aboutsummaryrefslogtreecommitdiff
path: root/sound/pci/hda
diff options
context:
space:
mode:
Diffstat (limited to 'sound/pci/hda')
-rw-r--r--sound/pci/hda/Kconfig225
-rw-r--r--sound/pci/hda/Makefile61
-rw-r--r--sound/pci/hda/ca0132_regs.h409
-rw-r--r--sound/pci/hda/hda_auto_parser.c939
-rw-r--r--sound/pci/hda/hda_auto_parser.h118
-rw-r--r--sound/pci/hda/hda_beep.c184
-rw-r--r--sound/pci/hda/hda_beep.h21
-rw-r--r--sound/pci/hda/hda_codec.c3334
-rw-r--r--sound/pci/hda/hda_codec.h797
-rw-r--r--sound/pci/hda/hda_controller.c2035
-rw-r--r--sound/pci/hda/hda_controller.h53
-rw-r--r--sound/pci/hda/hda_eld.c413
-rw-r--r--sound/pci/hda/hda_generic.c5765
-rw-r--r--sound/pci/hda/hda_generic.h342
-rw-r--r--sound/pci/hda/hda_hwdep.c702
-rw-r--r--sound/pci/hda/hda_i915.c130
-rw-r--r--sound/pci/hda/hda_i915.h37
-rw-r--r--sound/pci/hda/hda_intel.c3153
-rw-r--r--sound/pci/hda/hda_intel_trace.h62
-rw-r--r--sound/pci/hda/hda_jack.c546
-rw-r--r--sound/pci/hda/hda_jack.h105
-rw-r--r--sound/pci/hda/hda_local.h430
-rw-r--r--sound/pci/hda/hda_priv.h465
-rw-r--r--sound/pci/hda/hda_proc.c172
-rw-r--r--sound/pci/hda/hda_sysfs.c780
-rw-r--r--sound/pci/hda/hda_tegra.c588
-rw-r--r--sound/pci/hda/hda_trace.h143
-rw-r--r--sound/pci/hda/patch_analog.c5094
-rw-r--r--sound/pci/hda/patch_ca0110.c495
-rw-r--r--sound/pci/hda/patch_ca0132.c4721
-rw-r--r--sound/pci/hda/patch_cirrus.c1845
-rw-r--r--sound/pci/hda/patch_cmedia.c229
-rw-r--r--sound/pci/hda/patch_conexant.c2257
-rw-r--r--sound/pci/hda/patch_hdmi.c2704
-rw-r--r--sound/pci/hda/patch_realtek.c23009
-rw-r--r--sound/pci/hda/patch_si3054.c17
-rw-r--r--sound/pci/hda/patch_sigmatel.c8386
-rw-r--r--sound/pci/hda/patch_via.c6221
-rw-r--r--sound/pci/hda/thinkpad_helper.c102
39 files changed, 34946 insertions, 42143 deletions
diff --git a/sound/pci/hda/Kconfig b/sound/pci/hda/Kconfig
index 0ea5cc60ac7..ebf4c2fb99d 100644
--- a/sound/pci/hda/Kconfig
+++ b/sound/pci/hda/Kconfig
@@ -1,7 +1,15 @@
-menuconfig SND_HDA_INTEL
- tristate "Intel HD Audio"
+menu "HD-Audio"
+
+config SND_HDA
+ tristate
select SND_PCM
select SND_VMASTER
+ select SND_KCTL_JACK
+
+config SND_HDA_INTEL
+ tristate "HD Audio PCI"
+ depends on SND_PCI
+ select SND_HDA
help
Say Y here to include support for Intel "High Definition
Audio" (Azalia) and its compatible devices.
@@ -12,7 +20,38 @@ menuconfig SND_HDA_INTEL
To compile this driver as a module, choose M here: the module
will be called snd-hda-intel.
-if SND_HDA_INTEL
+config SND_HDA_TEGRA
+ tristate "NVIDIA Tegra HD Audio"
+ depends on ARCH_TEGRA
+ select SND_HDA
+ help
+ Say Y here to support the HDA controller present in NVIDIA
+ Tegra SoCs
+
+ This options enables support for the HD Audio controller
+ present in some NVIDIA Tegra SoCs, used to communicate audio
+ to the HDMI output.
+
+ To compile this driver as a module, choose M here: the module
+ will be called snd-hda-tegra.
+
+if SND_HDA
+
+config SND_HDA_DSP_LOADER
+ bool
+
+config SND_HDA_PREALLOC_SIZE
+ int "Pre-allocated buffer size for HD-audio driver"
+ range 0 32768
+ default 64
+ help
+ Specifies the default pre-allocated buffer-size in kB for the
+ HD-audio driver. A larger buffer (e.g. 2048) is preferred
+ for systems using PulseAudio. The default 64 is chosen just
+ for compatibility reasons.
+
+ Note that the pre-allocation size can be changed dynamically
+ via a proc file (/proc/asound/card*/pcm*/sub*/prealloc), too.
config SND_HDA_HWDEP
bool "Build hwdep interface for HD-audio driver"
@@ -23,8 +62,7 @@ config SND_HDA_HWDEP
with codecs for debugging purposes.
config SND_HDA_RECONFIG
- bool "Allow dynamic codec reconfiguration (EXPERIMENTAL)"
- depends on SND_HDA_HWDEP && EXPERIMENTAL
+ bool "Allow dynamic codec reconfiguration"
help
Say Y here to enable the HD-audio codec re-configuration feature.
This adds the sysfs interfaces to allow user to clear the whole
@@ -33,21 +71,20 @@ config SND_HDA_RECONFIG
config SND_HDA_INPUT_BEEP
bool "Support digital beep via input layer"
- depends on INPUT=y || INPUT=SND_HDA_INTEL
+ depends on INPUT=y || INPUT=SND_HDA
help
Say Y here to build a digital beep interface for HD-audio
driver. This interface is used to generate digital beeps.
config SND_HDA_INPUT_BEEP_MODE
- int "Digital beep registration mode (0=off, 1=on, 2=mute sw on/off)"
+ int "Digital beep registration mode (0=off, 1=on)"
depends on SND_HDA_INPUT_BEEP=y
default "1"
- range 0 2
+ range 0 1
help
Set 0 to disable the digital beep interface for HD-audio by default.
Set 1 to always enable the digital beep interface for HD-audio by
- default. Set 2 to control the beep device registration to input
- layer using a "Beep Switch" in mixer applications.
+ default.
config SND_HDA_INPUT_JACK
bool "Support jack plugging notification via input layer"
@@ -59,9 +96,7 @@ config SND_HDA_INPUT_JACK
config SND_HDA_PATCH_LOADER
bool "Support initialization patch loading for HD-audio"
- depends on EXPERIMENTAL
select FW_LOADER
- select SND_HDA_HWDEP
select SND_HDA_RECONFIG
help
Say Y here to allow the HD-audio driver to load a pseudo
@@ -69,152 +104,148 @@ config SND_HDA_PATCH_LOADER
start up. The "patch" file can be specified via patch module
option, such as patch=hda-init.
- This option turns on hwdep and reconfig features automatically.
-
config SND_HDA_CODEC_REALTEK
- bool "Build Realtek HD-audio codec support"
- default y
+ tristate "Build Realtek HD-audio codec support"
+ select SND_HDA_GENERIC
help
- Say Y here to include Realtek HD-audio codec support in
+ Say Y or M here to include Realtek HD-audio codec support in
snd-hda-intel driver, such as ALC880.
- When the HD-audio driver is built as a module, the codec
- support code is also built as another module,
- snd-hda-codec-realtek.
- This module is automatically loaded at probing.
+comment "Set to Y if you want auto-loading the codec driver"
+ depends on SND_HDA=y && SND_HDA_CODEC_REALTEK=m
config SND_HDA_CODEC_ANALOG
- bool "Build Analog Device HD-audio codec support"
- default y
+ tristate "Build Analog Device HD-audio codec support"
+ select SND_HDA_GENERIC
help
- Say Y here to include Analog Device HD-audio codec support in
+ Say Y or M here to include Analog Device HD-audio codec support in
snd-hda-intel driver, such as AD1986A.
- When the HD-audio driver is built as a module, the codec
- support code is also built as another module,
- snd-hda-codec-analog.
- This module is automatically loaded at probing.
+comment "Set to Y if you want auto-loading the codec driver"
+ depends on SND_HDA=y && SND_HDA_CODEC_ANALOG=m
config SND_HDA_CODEC_SIGMATEL
- bool "Build IDT/Sigmatel HD-audio codec support"
- default y
+ tristate "Build IDT/Sigmatel HD-audio codec support"
+ select SND_HDA_GENERIC
help
- Say Y here to include IDT (Sigmatel) HD-audio codec support in
+ Say Y or M here to include IDT (Sigmatel) HD-audio codec support in
snd-hda-intel driver, such as STAC9200.
- When the HD-audio driver is built as a module, the codec
- support code is also built as another module,
- snd-hda-codec-idt.
- This module is automatically loaded at probing.
+comment "Set to Y if you want auto-loading the codec driver"
+ depends on SND_HDA=y && SND_HDA_CODEC_SIGMATEL=m
config SND_HDA_CODEC_VIA
- bool "Build VIA HD-audio codec support"
- default y
+ tristate "Build VIA HD-audio codec support"
+ select SND_HDA_GENERIC
help
- Say Y here to include VIA HD-audio codec support in
+ Say Y or M here to include VIA HD-audio codec support in
snd-hda-intel driver, such as VT1708.
- When the HD-audio driver is built as a module, the codec
- support code is also built as another module,
- snd-hda-codec-via.
- This module is automatically loaded at probing.
+comment "Set to Y if you want auto-loading the codec driver"
+ depends on SND_HDA=y && SND_HDA_CODEC_VIA=m
config SND_HDA_CODEC_HDMI
- bool "Build HDMI/DisplayPort HD-audio codec support"
- select SND_DYNAMIC_MINORS
- default y
+ tristate "Build HDMI/DisplayPort HD-audio codec support"
help
- Say Y here to include HDMI and DisplayPort HD-audio codec
+ Say Y or M here to include HDMI and DisplayPort HD-audio codec
support in snd-hda-intel driver. This includes all AMD/ATI,
Intel and Nvidia HDMI/DisplayPort codecs.
- When the HD-audio driver is built as a module, the codec
- support code is also built as another module,
- snd-hda-codec-hdmi.
- This module is automatically loaded at probing.
+comment "Set to Y if you want auto-loading the codec driver"
+ depends on SND_HDA=y && SND_HDA_CODEC_HDMI=m
-config SND_HDA_CODEC_CIRRUS
- bool "Build Cirrus Logic codec support"
- depends on SND_HDA_INTEL
+config SND_HDA_I915
+ bool
default y
+ depends on DRM_I915
+
+config SND_HDA_CODEC_CIRRUS
+ tristate "Build Cirrus Logic codec support"
+ select SND_HDA_GENERIC
help
- Say Y here to include Cirrus Logic codec support in
+ Say Y or M here to include Cirrus Logic codec support in
snd-hda-intel driver, such as CS4206.
- When the HD-audio driver is built as a module, the codec
- support code is also built as another module,
- snd-hda-codec-cirrus.
- This module is automatically loaded at probing.
+comment "Set to Y if you want auto-loading the codec driver"
+ depends on SND_HDA=y && SND_HDA_CODEC_CIRRUS=m
config SND_HDA_CODEC_CONEXANT
- bool "Build Conexant HD-audio codec support"
- default y
+ tristate "Build Conexant HD-audio codec support"
+ select SND_HDA_GENERIC
help
- Say Y here to include Conexant HD-audio codec support in
+ Say Y or M here to include Conexant HD-audio codec support in
snd-hda-intel driver, such as CX20549.
- When the HD-audio driver is built as a module, the codec
- support code is also built as another module,
- snd-hda-codec-conexant.
- This module is automatically loaded at probing.
+comment "Set to Y if you want auto-loading the codec driver"
+ depends on SND_HDA=y && SND_HDA_CODEC_CONEXANT=m
config SND_HDA_CODEC_CA0110
- bool "Build Creative CA0110-IBG codec support"
- depends on SND_HDA_INTEL
- default y
+ tristate "Build Creative CA0110-IBG codec support"
+ select SND_HDA_GENERIC
help
- Say Y here to include Creative CA0110-IBG codec support in
+ Say Y or M here to include Creative CA0110-IBG codec support in
snd-hda-intel driver, found on some Creative X-Fi cards.
- When the HD-audio driver is built as a module, the codec
- support code is also built as another module,
- snd-hda-codec-ca0110.
- This module is automatically loaded at probing.
+comment "Set to Y if you want auto-loading the codec driver"
+ depends on SND_HDA=y && SND_HDA_CODEC_CA0110=m
+
+config SND_HDA_CODEC_CA0132
+ tristate "Build Creative CA0132 codec support"
+ help
+ Say Y or M here to include Creative CA0132 codec support in
+ snd-hda-intel driver.
+
+comment "Set to Y if you want auto-loading the codec driver"
+ depends on SND_HDA=y && SND_HDA_CODEC_CA0132=m
+
+config SND_HDA_CODEC_CA0132_DSP
+ bool "Support new DSP code for CA0132 codec"
+ depends on SND_HDA_CODEC_CA0132
+ select SND_HDA_DSP_LOADER
+ select FW_LOADER
+ help
+ Say Y here to enable the DSP for Creative CA0132 for extended
+ features like equalizer or echo cancellation.
+
+ Note that this option requires the external firmware file
+ (ctefx.bin).
config SND_HDA_CODEC_CMEDIA
- bool "Build C-Media HD-audio codec support"
- default y
+ tristate "Build C-Media HD-audio codec support"
+ select SND_HDA_GENERIC
help
- Say Y here to include C-Media HD-audio codec support in
+ Say Y or M here to include C-Media HD-audio codec support in
snd-hda-intel driver, such as CMI9880.
- When the HD-audio driver is built as a module, the codec
- support code is also built as another module,
- snd-hda-codec-cmedia.
- This module is automatically loaded at probing.
+comment "Set to Y if you want auto-loading the codec driver"
+ depends on SND_HDA=y && SND_HDA_CODEC_CMEDIA=m
config SND_HDA_CODEC_SI3054
- bool "Build Silicon Labs 3054 HD-modem codec support"
- default y
+ tristate "Build Silicon Labs 3054 HD-modem codec support"
help
- Say Y here to include Silicon Labs 3054 HD-modem codec
+ Say Y or M here to include Silicon Labs 3054 HD-modem codec
(and compatibles) support in snd-hda-intel driver.
- When the HD-audio driver is built as a module, the codec
- support code is also built as another module,
- snd-hda-codec-si3054.
- This module is automatically loaded at probing.
+comment "Set to Y if you want auto-loading the codec driver"
+ depends on SND_HDA=y && SND_HDA_CODEC_SI3054=m
config SND_HDA_GENERIC
- bool "Enable generic HD-audio codec parser"
- default y
+ tristate "Enable generic HD-audio codec parser"
help
- Say Y here to enable the generic HD-audio codec parser
+ Say Y or M here to enable the generic HD-audio codec parser
in snd-hda-intel driver.
-config SND_HDA_POWER_SAVE
- bool "Aggressive power-saving on HD-audio"
- help
- Say Y here to enable more aggressive power-saving mode on
- HD-audio driver. The power-saving timeout can be configured
- via power_save option or over sysfs on-the-fly.
+comment "Set to Y if you want auto-loading the codec driver"
+ depends on SND_HDA=y && SND_HDA_GENERIC=m
config SND_HDA_POWER_SAVE_DEFAULT
int "Default time-out for HD-audio power-save mode"
- depends on SND_HDA_POWER_SAVE
+ depends on PM
default 0
help
The default time-out value in seconds for HD-audio automatic
power-save mode. 0 means to disable the power-save mode.
endif
+
+endmenu
diff --git a/sound/pci/hda/Makefile b/sound/pci/hda/Makefile
index 17ef3658f34..194f30935e7 100644
--- a/sound/pci/hda/Makefile
+++ b/sound/pci/hda/Makefile
@@ -1,11 +1,19 @@
snd-hda-intel-objs := hda_intel.o
+snd-hda-controller-objs := hda_controller.o
+snd-hda-tegra-objs := hda_tegra.o
+# for haswell power well
+snd-hda-intel-$(CONFIG_SND_HDA_I915) += hda_i915.o
-snd-hda-codec-y := hda_codec.o
-snd-hda-codec-$(CONFIG_SND_HDA_GENERIC) += hda_generic.o
+snd-hda-codec-y := hda_codec.o hda_jack.o hda_auto_parser.o hda_sysfs.o
snd-hda-codec-$(CONFIG_PROC_FS) += hda_proc.o
snd-hda-codec-$(CONFIG_SND_HDA_HWDEP) += hda_hwdep.o
snd-hda-codec-$(CONFIG_SND_HDA_INPUT_BEEP) += hda_beep.o
+# for trace-points
+CFLAGS_hda_codec.o := -I$(src)
+CFLAGS_hda_controller.o := -I$(src)
+
+snd-hda-codec-generic-objs := hda_generic.o
snd-hda-codec-realtek-objs := patch_realtek.o
snd-hda-codec-cmedia-objs := patch_cmedia.o
snd-hda-codec-analog-objs := patch_analog.o
@@ -13,46 +21,31 @@ snd-hda-codec-idt-objs := patch_sigmatel.o
snd-hda-codec-si3054-objs := patch_si3054.o
snd-hda-codec-cirrus-objs := patch_cirrus.o
snd-hda-codec-ca0110-objs := patch_ca0110.o
+snd-hda-codec-ca0132-objs := patch_ca0132.o
snd-hda-codec-conexant-objs := patch_conexant.o
snd-hda-codec-via-objs := patch_via.o
snd-hda-codec-hdmi-objs := patch_hdmi.o hda_eld.o
# common driver
-obj-$(CONFIG_SND_HDA_INTEL) := snd-hda-codec.o
+obj-$(CONFIG_SND_HDA) := snd-hda-codec.o
+obj-$(CONFIG_SND_HDA) += snd-hda-controller.o
-# codec drivers (note: CONFIG_SND_HDA_CODEC_XXX are booleans)
-ifdef CONFIG_SND_HDA_CODEC_REALTEK
-obj-$(CONFIG_SND_HDA_INTEL) += snd-hda-codec-realtek.o
-endif
-ifdef CONFIG_SND_HDA_CODEC_CMEDIA
-obj-$(CONFIG_SND_HDA_INTEL) += snd-hda-codec-cmedia.o
-endif
-ifdef CONFIG_SND_HDA_CODEC_ANALOG
-obj-$(CONFIG_SND_HDA_INTEL) += snd-hda-codec-analog.o
-endif
-ifdef CONFIG_SND_HDA_CODEC_SIGMATEL
-obj-$(CONFIG_SND_HDA_INTEL) += snd-hda-codec-idt.o
-endif
-ifdef CONFIG_SND_HDA_CODEC_SI3054
-obj-$(CONFIG_SND_HDA_INTEL) += snd-hda-codec-si3054.o
-endif
-ifdef CONFIG_SND_HDA_CODEC_CIRRUS
-obj-$(CONFIG_SND_HDA_INTEL) += snd-hda-codec-cirrus.o
-endif
-ifdef CONFIG_SND_HDA_CODEC_CA0110
-obj-$(CONFIG_SND_HDA_INTEL) += snd-hda-codec-ca0110.o
-endif
-ifdef CONFIG_SND_HDA_CODEC_CONEXANT
-obj-$(CONFIG_SND_HDA_INTEL) += snd-hda-codec-conexant.o
-endif
-ifdef CONFIG_SND_HDA_CODEC_VIA
-obj-$(CONFIG_SND_HDA_INTEL) += snd-hda-codec-via.o
-endif
-ifdef CONFIG_SND_HDA_CODEC_HDMI
-obj-$(CONFIG_SND_HDA_INTEL) += snd-hda-codec-hdmi.o
-endif
+# codec drivers
+obj-$(CONFIG_SND_HDA_GENERIC) += snd-hda-codec-generic.o
+obj-$(CONFIG_SND_HDA_CODEC_REALTEK) += snd-hda-codec-realtek.o
+obj-$(CONFIG_SND_HDA_CODEC_CMEDIA) += snd-hda-codec-cmedia.o
+obj-$(CONFIG_SND_HDA_CODEC_ANALOG) += snd-hda-codec-analog.o
+obj-$(CONFIG_SND_HDA_CODEC_SIGMATEL) += snd-hda-codec-idt.o
+obj-$(CONFIG_SND_HDA_CODEC_SI3054) += snd-hda-codec-si3054.o
+obj-$(CONFIG_SND_HDA_CODEC_CIRRUS) += snd-hda-codec-cirrus.o
+obj-$(CONFIG_SND_HDA_CODEC_CA0110) += snd-hda-codec-ca0110.o
+obj-$(CONFIG_SND_HDA_CODEC_CA0132) += snd-hda-codec-ca0132.o
+obj-$(CONFIG_SND_HDA_CODEC_CONEXANT) += snd-hda-codec-conexant.o
+obj-$(CONFIG_SND_HDA_CODEC_VIA) += snd-hda-codec-via.o
+obj-$(CONFIG_SND_HDA_CODEC_HDMI) += snd-hda-codec-hdmi.o
# this must be the last entry after codec drivers;
# otherwise the codec patches won't be hooked before the PCI probe
# when built in kernel
obj-$(CONFIG_SND_HDA_INTEL) += snd-hda-intel.o
+obj-$(CONFIG_SND_HDA_TEGRA) += snd-hda-tegra.o
diff --git a/sound/pci/hda/ca0132_regs.h b/sound/pci/hda/ca0132_regs.h
new file mode 100644
index 00000000000..07e760937d3
--- /dev/null
+++ b/sound/pci/hda/ca0132_regs.h
@@ -0,0 +1,409 @@
+/*
+ * HD audio interface patch for Creative CA0132 chip.
+ * CA0132 registers defines.
+ *
+ * Copyright (c) 2011, Creative Technology Ltd.
+ *
+ * This driver is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This driver is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#ifndef __CA0132_REGS_H
+#define __CA0312_REGS_H
+
+#define DSP_CHIP_OFFSET 0x100000
+#define DSP_DBGCNTL_MODULE_OFFSET 0xE30
+#define DSP_DBGCNTL_INST_OFFSET \
+ (DSP_CHIP_OFFSET + DSP_DBGCNTL_MODULE_OFFSET)
+
+#define DSP_DBGCNTL_EXEC_LOBIT 0x0
+#define DSP_DBGCNTL_EXEC_HIBIT 0x3
+#define DSP_DBGCNTL_EXEC_MASK 0xF
+
+#define DSP_DBGCNTL_SS_LOBIT 0x4
+#define DSP_DBGCNTL_SS_HIBIT 0x7
+#define DSP_DBGCNTL_SS_MASK 0xF0
+
+#define DSP_DBGCNTL_STATE_LOBIT 0xA
+#define DSP_DBGCNTL_STATE_HIBIT 0xD
+#define DSP_DBGCNTL_STATE_MASK 0x3C00
+
+#define XRAM_CHIP_OFFSET 0x0
+#define XRAM_XRAM_CHANNEL_COUNT 0xE000
+#define XRAM_XRAM_MODULE_OFFSET 0x0
+#define XRAM_XRAM_CHAN_INCR 4
+#define XRAM_XRAM_INST_OFFSET(_chan) \
+ (XRAM_CHIP_OFFSET + XRAM_XRAM_MODULE_OFFSET + \
+ (_chan * XRAM_XRAM_CHAN_INCR))
+
+#define YRAM_CHIP_OFFSET 0x40000
+#define YRAM_YRAM_CHANNEL_COUNT 0x8000
+#define YRAM_YRAM_MODULE_OFFSET 0x0
+#define YRAM_YRAM_CHAN_INCR 4
+#define YRAM_YRAM_INST_OFFSET(_chan) \
+ (YRAM_CHIP_OFFSET + YRAM_YRAM_MODULE_OFFSET + \
+ (_chan * YRAM_YRAM_CHAN_INCR))
+
+#define UC_CHIP_OFFSET 0x80000
+#define UC_UC_CHANNEL_COUNT 0x10000
+#define UC_UC_MODULE_OFFSET 0x0
+#define UC_UC_CHAN_INCR 4
+#define UC_UC_INST_OFFSET(_chan) \
+ (UC_CHIP_OFFSET + UC_UC_MODULE_OFFSET + \
+ (_chan * UC_UC_CHAN_INCR))
+
+#define AXRAM_CHIP_OFFSET 0x3C000
+#define AXRAM_AXRAM_CHANNEL_COUNT 0x1000
+#define AXRAM_AXRAM_MODULE_OFFSET 0x0
+#define AXRAM_AXRAM_CHAN_INCR 4
+#define AXRAM_AXRAM_INST_OFFSET(_chan) \
+ (AXRAM_CHIP_OFFSET + AXRAM_AXRAM_MODULE_OFFSET + \
+ (_chan * AXRAM_AXRAM_CHAN_INCR))
+
+#define AYRAM_CHIP_OFFSET 0x78000
+#define AYRAM_AYRAM_CHANNEL_COUNT 0x1000
+#define AYRAM_AYRAM_MODULE_OFFSET 0x0
+#define AYRAM_AYRAM_CHAN_INCR 4
+#define AYRAM_AYRAM_INST_OFFSET(_chan) \
+ (AYRAM_CHIP_OFFSET + AYRAM_AYRAM_MODULE_OFFSET + \
+ (_chan * AYRAM_AYRAM_CHAN_INCR))
+
+#define DSPDMAC_CHIP_OFFSET 0x110000
+#define DSPDMAC_DMA_CFG_CHANNEL_COUNT 12
+#define DSPDMAC_DMACFG_MODULE_OFFSET 0xF00
+#define DSPDMAC_DMACFG_CHAN_INCR 0x10
+#define DSPDMAC_DMACFG_INST_OFFSET(_chan) \
+ (DSPDMAC_CHIP_OFFSET + DSPDMAC_DMACFG_MODULE_OFFSET + \
+ (_chan * DSPDMAC_DMACFG_CHAN_INCR))
+
+#define DSPDMAC_DMACFG_DBADR_LOBIT 0x0
+#define DSPDMAC_DMACFG_DBADR_HIBIT 0x10
+#define DSPDMAC_DMACFG_DBADR_MASK 0x1FFFF
+#define DSPDMAC_DMACFG_LP_LOBIT 0x11
+#define DSPDMAC_DMACFG_LP_HIBIT 0x11
+#define DSPDMAC_DMACFG_LP_MASK 0x20000
+
+#define DSPDMAC_DMACFG_AINCR_LOBIT 0x12
+#define DSPDMAC_DMACFG_AINCR_HIBIT 0x12
+#define DSPDMAC_DMACFG_AINCR_MASK 0x40000
+
+#define DSPDMAC_DMACFG_DWR_LOBIT 0x13
+#define DSPDMAC_DMACFG_DWR_HIBIT 0x13
+#define DSPDMAC_DMACFG_DWR_MASK 0x80000
+
+#define DSPDMAC_DMACFG_AJUMP_LOBIT 0x14
+#define DSPDMAC_DMACFG_AJUMP_HIBIT 0x17
+#define DSPDMAC_DMACFG_AJUMP_MASK 0xF00000
+
+#define DSPDMAC_DMACFG_AMODE_LOBIT 0x18
+#define DSPDMAC_DMACFG_AMODE_HIBIT 0x19
+#define DSPDMAC_DMACFG_AMODE_MASK 0x3000000
+
+#define DSPDMAC_DMACFG_LK_LOBIT 0x1A
+#define DSPDMAC_DMACFG_LK_HIBIT 0x1A
+#define DSPDMAC_DMACFG_LK_MASK 0x4000000
+
+#define DSPDMAC_DMACFG_AICS_LOBIT 0x1B
+#define DSPDMAC_DMACFG_AICS_HIBIT 0x1F
+#define DSPDMAC_DMACFG_AICS_MASK 0xF8000000
+
+#define DSPDMAC_DMACFG_LP_SINGLE 0
+#define DSPDMAC_DMACFG_LP_LOOPING 1
+
+#define DSPDMAC_DMACFG_AINCR_XANDY 0
+#define DSPDMAC_DMACFG_AINCR_XORY 1
+
+#define DSPDMAC_DMACFG_DWR_DMA_RD 0
+#define DSPDMAC_DMACFG_DWR_DMA_WR 1
+
+#define DSPDMAC_DMACFG_AMODE_LINEAR 0
+#define DSPDMAC_DMACFG_AMODE_RSV1 1
+#define DSPDMAC_DMACFG_AMODE_WINTLV 2
+#define DSPDMAC_DMACFG_AMODE_GINTLV 3
+
+#define DSPDMAC_DSP_ADR_OFS_CHANNEL_COUNT 12
+#define DSPDMAC_DSPADROFS_MODULE_OFFSET 0xF04
+#define DSPDMAC_DSPADROFS_CHAN_INCR 0x10
+#define DSPDMAC_DSPADROFS_INST_OFFSET(_chan) \
+ (DSPDMAC_CHIP_OFFSET + DSPDMAC_DSPADROFS_MODULE_OFFSET + \
+ (_chan * DSPDMAC_DSPADROFS_CHAN_INCR))
+
+#define DSPDMAC_DSPADROFS_COFS_LOBIT 0x0
+#define DSPDMAC_DSPADROFS_COFS_HIBIT 0xF
+#define DSPDMAC_DSPADROFS_COFS_MASK 0xFFFF
+
+#define DSPDMAC_DSPADROFS_BOFS_LOBIT 0x10
+#define DSPDMAC_DSPADROFS_BOFS_HIBIT 0x1F
+#define DSPDMAC_DSPADROFS_BOFS_MASK 0xFFFF0000
+
+#define DSPDMAC_DSP_ADR_WOFS_CHANNEL_COUNT 12
+#define DSPDMAC_DSPADRWOFS_MODULE_OFFSET 0xF04
+#define DSPDMAC_DSPADRWOFS_CHAN_INCR 0x10
+
+#define DSPDMAC_DSPADRWOFS_INST_OFFSET(_chan) \
+ (DSPDMAC_CHIP_OFFSET + DSPDMAC_DSPADRWOFS_MODULE_OFFSET + \
+ (_chan * DSPDMAC_DSPADRWOFS_CHAN_INCR))
+
+#define DSPDMAC_DSPADRWOFS_WCOFS_LOBIT 0x0
+#define DSPDMAC_DSPADRWOFS_WCOFS_HIBIT 0xA
+#define DSPDMAC_DSPADRWOFS_WCOFS_MASK 0x7FF
+
+#define DSPDMAC_DSPADRWOFS_WCBFR_LOBIT 0xB
+#define DSPDMAC_DSPADRWOFS_WCBFR_HIBIT 0xF
+#define DSPDMAC_DSPADRWOFS_WCBFR_MASK 0xF800
+
+#define DSPDMAC_DSPADRWOFS_WBOFS_LOBIT 0x10
+#define DSPDMAC_DSPADRWOFS_WBOFS_HIBIT 0x1A
+#define DSPDMAC_DSPADRWOFS_WBOFS_MASK 0x7FF0000
+
+#define DSPDMAC_DSPADRWOFS_WBBFR_LOBIT 0x1B
+#define DSPDMAC_DSPADRWOFS_WBBFR_HIBIT 0x1F
+#define DSPDMAC_DSPADRWOFS_WBBFR_MASK 0xF8000000
+
+#define DSPDMAC_DSP_ADR_GOFS_CHANNEL_COUNT 12
+#define DSPDMAC_DSPADRGOFS_MODULE_OFFSET 0xF04
+#define DSPDMAC_DSPADRGOFS_CHAN_INCR 0x10
+#define DSPDMAC_DSPADRGOFS_INST_OFFSET(_chan) \
+ (DSPDMAC_CHIP_OFFSET + DSPDMAC_DSPADRGOFS_MODULE_OFFSET + \
+ (_chan * DSPDMAC_DSPADRGOFS_CHAN_INCR))
+
+#define DSPDMAC_DSPADRGOFS_GCOFS_LOBIT 0x0
+#define DSPDMAC_DSPADRGOFS_GCOFS_HIBIT 0x9
+#define DSPDMAC_DSPADRGOFS_GCOFS_MASK 0x3FF
+
+#define DSPDMAC_DSPADRGOFS_GCS_LOBIT 0xA
+#define DSPDMAC_DSPADRGOFS_GCS_HIBIT 0xC
+#define DSPDMAC_DSPADRGOFS_GCS_MASK 0x1C00
+
+#define DSPDMAC_DSPADRGOFS_GCBFR_LOBIT 0xD
+#define DSPDMAC_DSPADRGOFS_GCBFR_HIBIT 0xF
+#define DSPDMAC_DSPADRGOFS_GCBFR_MASK 0xE000
+
+#define DSPDMAC_DSPADRGOFS_GBOFS_LOBIT 0x10
+#define DSPDMAC_DSPADRGOFS_GBOFS_HIBIT 0x19
+#define DSPDMAC_DSPADRGOFS_GBOFS_MASK 0x3FF0000
+
+#define DSPDMAC_DSPADRGOFS_GBS_LOBIT 0x1A
+#define DSPDMAC_DSPADRGOFS_GBS_HIBIT 0x1C
+#define DSPDMAC_DSPADRGOFS_GBS_MASK 0x1C000000
+
+#define DSPDMAC_DSPADRGOFS_GBBFR_LOBIT 0x1D
+#define DSPDMAC_DSPADRGOFS_GBBFR_HIBIT 0x1F
+#define DSPDMAC_DSPADRGOFS_GBBFR_MASK 0xE0000000
+
+#define DSPDMAC_XFR_CNT_CHANNEL_COUNT 12
+#define DSPDMAC_XFRCNT_MODULE_OFFSET 0xF08
+#define DSPDMAC_XFRCNT_CHAN_INCR 0x10
+
+#define DSPDMAC_XFRCNT_INST_OFFSET(_chan) \
+ (DSPDMAC_CHIP_OFFSET + DSPDMAC_XFRCNT_MODULE_OFFSET + \
+ (_chan * DSPDMAC_XFRCNT_CHAN_INCR))
+
+#define DSPDMAC_XFRCNT_CCNT_LOBIT 0x0
+#define DSPDMAC_XFRCNT_CCNT_HIBIT 0xF
+#define DSPDMAC_XFRCNT_CCNT_MASK 0xFFFF
+
+#define DSPDMAC_XFRCNT_BCNT_LOBIT 0x10
+#define DSPDMAC_XFRCNT_BCNT_HIBIT 0x1F
+#define DSPDMAC_XFRCNT_BCNT_MASK 0xFFFF0000
+
+#define DSPDMAC_IRQ_CNT_CHANNEL_COUNT 12
+#define DSPDMAC_IRQCNT_MODULE_OFFSET 0xF0C
+#define DSPDMAC_IRQCNT_CHAN_INCR 0x10
+#define DSPDMAC_IRQCNT_INST_OFFSET(_chan) \
+ (DSPDMAC_CHIP_OFFSET + DSPDMAC_IRQCNT_MODULE_OFFSET + \
+ (_chan * DSPDMAC_IRQCNT_CHAN_INCR))
+
+#define DSPDMAC_IRQCNT_CICNT_LOBIT 0x0
+#define DSPDMAC_IRQCNT_CICNT_HIBIT 0xF
+#define DSPDMAC_IRQCNT_CICNT_MASK 0xFFFF
+
+#define DSPDMAC_IRQCNT_BICNT_LOBIT 0x10
+#define DSPDMAC_IRQCNT_BICNT_HIBIT 0x1F
+#define DSPDMAC_IRQCNT_BICNT_MASK 0xFFFF0000
+
+#define DSPDMAC_AUD_CHSEL_CHANNEL_COUNT 12
+#define DSPDMAC_AUDCHSEL_MODULE_OFFSET 0xFC0
+#define DSPDMAC_AUDCHSEL_CHAN_INCR 0x4
+#define DSPDMAC_AUDCHSEL_INST_OFFSET(_chan) \
+ (DSPDMAC_CHIP_OFFSET + DSPDMAC_AUDCHSEL_MODULE_OFFSET + \
+ (_chan * DSPDMAC_AUDCHSEL_CHAN_INCR))
+
+#define DSPDMAC_AUDCHSEL_ACS_LOBIT 0x0
+#define DSPDMAC_AUDCHSEL_ACS_HIBIT 0x1F
+#define DSPDMAC_AUDCHSEL_ACS_MASK 0xFFFFFFFF
+
+#define DSPDMAC_CHNLSTART_MODULE_OFFSET 0xFF0
+#define DSPDMAC_CHNLSTART_INST_OFFSET \
+ (DSPDMAC_CHIP_OFFSET + DSPDMAC_CHNLSTART_MODULE_OFFSET)
+
+#define DSPDMAC_CHNLSTART_EN_LOBIT 0x0
+#define DSPDMAC_CHNLSTART_EN_HIBIT 0xB
+#define DSPDMAC_CHNLSTART_EN_MASK 0xFFF
+
+#define DSPDMAC_CHNLSTART_VAI1_LOBIT 0xC
+#define DSPDMAC_CHNLSTART_VAI1_HIBIT 0xF
+#define DSPDMAC_CHNLSTART_VAI1_MASK 0xF000
+
+#define DSPDMAC_CHNLSTART_DIS_LOBIT 0x10
+#define DSPDMAC_CHNLSTART_DIS_HIBIT 0x1B
+#define DSPDMAC_CHNLSTART_DIS_MASK 0xFFF0000
+
+#define DSPDMAC_CHNLSTART_VAI2_LOBIT 0x1C
+#define DSPDMAC_CHNLSTART_VAI2_HIBIT 0x1F
+#define DSPDMAC_CHNLSTART_VAI2_MASK 0xF0000000
+
+#define DSPDMAC_CHNLSTATUS_MODULE_OFFSET 0xFF4
+#define DSPDMAC_CHNLSTATUS_INST_OFFSET \
+ (DSPDMAC_CHIP_OFFSET + DSPDMAC_CHNLSTATUS_MODULE_OFFSET)
+
+#define DSPDMAC_CHNLSTATUS_ISC_LOBIT 0x0
+#define DSPDMAC_CHNLSTATUS_ISC_HIBIT 0xB
+#define DSPDMAC_CHNLSTATUS_ISC_MASK 0xFFF
+
+#define DSPDMAC_CHNLSTATUS_AOO_LOBIT 0xC
+#define DSPDMAC_CHNLSTATUS_AOO_HIBIT 0xC
+#define DSPDMAC_CHNLSTATUS_AOO_MASK 0x1000
+
+#define DSPDMAC_CHNLSTATUS_AOU_LOBIT 0xD
+#define DSPDMAC_CHNLSTATUS_AOU_HIBIT 0xD
+#define DSPDMAC_CHNLSTATUS_AOU_MASK 0x2000
+
+#define DSPDMAC_CHNLSTATUS_AIO_LOBIT 0xE
+#define DSPDMAC_CHNLSTATUS_AIO_HIBIT 0xE
+#define DSPDMAC_CHNLSTATUS_AIO_MASK 0x4000
+
+#define DSPDMAC_CHNLSTATUS_AIU_LOBIT 0xF
+#define DSPDMAC_CHNLSTATUS_AIU_HIBIT 0xF
+#define DSPDMAC_CHNLSTATUS_AIU_MASK 0x8000
+
+#define DSPDMAC_CHNLSTATUS_IEN_LOBIT 0x10
+#define DSPDMAC_CHNLSTATUS_IEN_HIBIT 0x1B
+#define DSPDMAC_CHNLSTATUS_IEN_MASK 0xFFF0000
+
+#define DSPDMAC_CHNLSTATUS_VAI0_LOBIT 0x1C
+#define DSPDMAC_CHNLSTATUS_VAI0_HIBIT 0x1F
+#define DSPDMAC_CHNLSTATUS_VAI0_MASK 0xF0000000
+
+#define DSPDMAC_CHNLPROP_MODULE_OFFSET 0xFF8
+#define DSPDMAC_CHNLPROP_INST_OFFSET \
+ (DSPDMAC_CHIP_OFFSET + DSPDMAC_CHNLPROP_MODULE_OFFSET)
+
+#define DSPDMAC_CHNLPROP_DCON_LOBIT 0x0
+#define DSPDMAC_CHNLPROP_DCON_HIBIT 0xB
+#define DSPDMAC_CHNLPROP_DCON_MASK 0xFFF
+
+#define DSPDMAC_CHNLPROP_FFS_LOBIT 0xC
+#define DSPDMAC_CHNLPROP_FFS_HIBIT 0xC
+#define DSPDMAC_CHNLPROP_FFS_MASK 0x1000
+
+#define DSPDMAC_CHNLPROP_NAJ_LOBIT 0xD
+#define DSPDMAC_CHNLPROP_NAJ_HIBIT 0xD
+#define DSPDMAC_CHNLPROP_NAJ_MASK 0x2000
+
+#define DSPDMAC_CHNLPROP_ENH_LOBIT 0xE
+#define DSPDMAC_CHNLPROP_ENH_HIBIT 0xE
+#define DSPDMAC_CHNLPROP_ENH_MASK 0x4000
+
+#define DSPDMAC_CHNLPROP_MSPCE_LOBIT 0x10
+#define DSPDMAC_CHNLPROP_MSPCE_HIBIT 0x1B
+#define DSPDMAC_CHNLPROP_MSPCE_MASK 0xFFF0000
+
+#define DSPDMAC_CHNLPROP_AC_LOBIT 0x1C
+#define DSPDMAC_CHNLPROP_AC_HIBIT 0x1F
+#define DSPDMAC_CHNLPROP_AC_MASK 0xF0000000
+
+#define DSPDMAC_ACTIVE_MODULE_OFFSET 0xFFC
+#define DSPDMAC_ACTIVE_INST_OFFSET \
+ (DSPDMAC_CHIP_OFFSET + DSPDMAC_ACTIVE_MODULE_OFFSET)
+
+#define DSPDMAC_ACTIVE_AAR_LOBIT 0x0
+#define DSPDMAC_ACTIVE_AAR_HIBIT 0xB
+#define DSPDMAC_ACTIVE_AAR_MASK 0xFFF
+
+#define DSPDMAC_ACTIVE_WFR_LOBIT 0xC
+#define DSPDMAC_ACTIVE_WFR_HIBIT 0x17
+#define DSPDMAC_ACTIVE_WFR_MASK 0xFFF000
+
+#define DSP_AUX_MEM_BASE 0xE000
+#define INVALID_CHIP_ADDRESS (~0U)
+
+#define X_SIZE (XRAM_XRAM_CHANNEL_COUNT * XRAM_XRAM_CHAN_INCR)
+#define Y_SIZE (YRAM_YRAM_CHANNEL_COUNT * YRAM_YRAM_CHAN_INCR)
+#define AX_SIZE (AXRAM_AXRAM_CHANNEL_COUNT * AXRAM_AXRAM_CHAN_INCR)
+#define AY_SIZE (AYRAM_AYRAM_CHANNEL_COUNT * AYRAM_AYRAM_CHAN_INCR)
+#define UC_SIZE (UC_UC_CHANNEL_COUNT * UC_UC_CHAN_INCR)
+
+#define XEXT_SIZE (X_SIZE + AX_SIZE)
+#define YEXT_SIZE (Y_SIZE + AY_SIZE)
+
+#define U64K 0x10000UL
+
+#define X_END (XRAM_CHIP_OFFSET + X_SIZE)
+#define X_EXT (XRAM_CHIP_OFFSET + XEXT_SIZE)
+#define AX_END (XRAM_CHIP_OFFSET + U64K*4)
+
+#define Y_END (YRAM_CHIP_OFFSET + Y_SIZE)
+#define Y_EXT (YRAM_CHIP_OFFSET + YEXT_SIZE)
+#define AY_END (YRAM_CHIP_OFFSET + U64K*4)
+
+#define UC_END (UC_CHIP_OFFSET + UC_SIZE)
+
+#define X_RANGE_MAIN(a, s) \
+ (((a)+((s)-1)*XRAM_XRAM_CHAN_INCR < X_END))
+#define X_RANGE_AUX(a, s) \
+ (((a) >= X_END) && ((a)+((s)-1)*XRAM_XRAM_CHAN_INCR < AX_END))
+#define X_RANGE_EXT(a, s) \
+ (((a)+((s)-1)*XRAM_XRAM_CHAN_INCR < X_EXT))
+#define X_RANGE_ALL(a, s) \
+ (((a)+((s)-1)*XRAM_XRAM_CHAN_INCR < AX_END))
+
+#define Y_RANGE_MAIN(a, s) \
+ (((a) >= YRAM_CHIP_OFFSET) && \
+ ((a)+((s)-1)*YRAM_YRAM_CHAN_INCR < Y_END))
+#define Y_RANGE_AUX(a, s) \
+ (((a) >= Y_END) && \
+ ((a)+((s)-1)*YRAM_YRAM_CHAN_INCR < AY_END))
+#define Y_RANGE_EXT(a, s) \
+ (((a) >= YRAM_CHIP_OFFSET) && \
+ ((a)+((s)-1)*YRAM_YRAM_CHAN_INCR < Y_EXT))
+#define Y_RANGE_ALL(a, s) \
+ (((a) >= YRAM_CHIP_OFFSET) && \
+ ((a)+((s)-1)*YRAM_YRAM_CHAN_INCR < AY_END))
+
+#define UC_RANGE(a, s) \
+ (((a) >= UC_CHIP_OFFSET) && \
+ ((a)+((s)-1)*UC_UC_CHAN_INCR < UC_END))
+
+#define X_OFF(a) \
+ (((a) - XRAM_CHIP_OFFSET) / XRAM_XRAM_CHAN_INCR)
+#define AX_OFF(a) \
+ (((a) % (AXRAM_AXRAM_CHANNEL_COUNT * \
+ AXRAM_AXRAM_CHAN_INCR)) / AXRAM_AXRAM_CHAN_INCR)
+
+#define Y_OFF(a) \
+ (((a) - YRAM_CHIP_OFFSET) / YRAM_YRAM_CHAN_INCR)
+#define AY_OFF(a) \
+ (((a) % (AYRAM_AYRAM_CHANNEL_COUNT * \
+ AYRAM_AYRAM_CHAN_INCR)) / AYRAM_AYRAM_CHAN_INCR)
+
+#define UC_OFF(a) (((a) - UC_CHIP_OFFSET) / UC_UC_CHAN_INCR)
+
+#define X_EXT_MAIN_SIZE(a) (XRAM_XRAM_CHANNEL_COUNT - X_OFF(a))
+#define X_EXT_AUX_SIZE(a, s) ((s) - X_EXT_MAIN_SIZE(a))
+
+#define Y_EXT_MAIN_SIZE(a) (YRAM_YRAM_CHANNEL_COUNT - Y_OFF(a))
+#define Y_EXT_AUX_SIZE(a, s) ((s) - Y_EXT_MAIN_SIZE(a))
+
+#endif
diff --git a/sound/pci/hda/hda_auto_parser.c b/sound/pci/hda/hda_auto_parser.c
new file mode 100644
index 00000000000..dabe41975a9
--- /dev/null
+++ b/sound/pci/hda/hda_auto_parser.c
@@ -0,0 +1,939 @@
+/*
+ * BIOS auto-parser helper functions for HD-audio
+ *
+ * Copyright (c) 2012 Takashi Iwai <tiwai@suse.de>
+ *
+ * This driver is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ */
+
+#include <linux/slab.h>
+#include <linux/export.h>
+#include <linux/sort.h>
+#include <sound/core.h>
+#include "hda_codec.h"
+#include "hda_local.h"
+#include "hda_auto_parser.h"
+
+#define SFX "hda_codec: "
+
+/*
+ * Helper for automatic pin configuration
+ */
+
+static int is_in_nid_list(hda_nid_t nid, const hda_nid_t *list)
+{
+ for (; *list; list++)
+ if (*list == nid)
+ return 1;
+ return 0;
+}
+
+/* a pair of input pin and its sequence */
+struct auto_out_pin {
+ hda_nid_t pin;
+ short seq;
+};
+
+static int compare_seq(const void *ap, const void *bp)
+{
+ const struct auto_out_pin *a = ap;
+ const struct auto_out_pin *b = bp;
+ return (int)(a->seq - b->seq);
+}
+
+/*
+ * Sort an associated group of pins according to their sequence numbers.
+ * then store it to a pin array.
+ */
+static void sort_pins_by_sequence(hda_nid_t *pins, struct auto_out_pin *list,
+ int num_pins)
+{
+ int i;
+ sort(list, num_pins, sizeof(list[0]), compare_seq, NULL);
+ for (i = 0; i < num_pins; i++)
+ pins[i] = list[i].pin;
+}
+
+
+/* add the found input-pin to the cfg->inputs[] table */
+static void add_auto_cfg_input_pin(struct auto_pin_cfg *cfg, hda_nid_t nid,
+ int type)
+{
+ if (cfg->num_inputs < AUTO_CFG_MAX_INS) {
+ cfg->inputs[cfg->num_inputs].pin = nid;
+ cfg->inputs[cfg->num_inputs].type = type;
+ cfg->num_inputs++;
+ }
+}
+
+static int compare_input_type(const void *ap, const void *bp)
+{
+ const struct auto_pin_cfg_item *a = ap;
+ const struct auto_pin_cfg_item *b = bp;
+ return (int)(a->type - b->type);
+}
+
+/* Reorder the surround channels
+ * ALSA sequence is front/surr/clfe/side
+ * HDA sequence is:
+ * 4-ch: front/surr => OK as it is
+ * 6-ch: front/clfe/surr
+ * 8-ch: front/clfe/rear/side|fc
+ */
+static void reorder_outputs(unsigned int nums, hda_nid_t *pins)
+{
+ hda_nid_t nid;
+
+ switch (nums) {
+ case 3:
+ case 4:
+ nid = pins[1];
+ pins[1] = pins[2];
+ pins[2] = nid;
+ break;
+ }
+}
+
+/* check whether the given pin has a proper pin I/O capability bit */
+static bool check_pincap_validity(struct hda_codec *codec, hda_nid_t pin,
+ unsigned int dev)
+{
+ unsigned int pincap = snd_hda_query_pin_caps(codec, pin);
+
+ /* some old hardware don't return the proper pincaps */
+ if (!pincap)
+ return true;
+
+ switch (dev) {
+ case AC_JACK_LINE_OUT:
+ case AC_JACK_SPEAKER:
+ case AC_JACK_HP_OUT:
+ case AC_JACK_SPDIF_OUT:
+ case AC_JACK_DIG_OTHER_OUT:
+ return !!(pincap & AC_PINCAP_OUT);
+ default:
+ return !!(pincap & AC_PINCAP_IN);
+ }
+}
+
+static bool can_be_headset_mic(struct hda_codec *codec,
+ struct auto_pin_cfg_item *item,
+ int seq_number)
+{
+ int attr;
+ unsigned int def_conf;
+ if (item->type != AUTO_PIN_MIC)
+ return false;
+
+ if (item->is_headset_mic || item->is_headphone_mic)
+ return false; /* Already assigned */
+
+ def_conf = snd_hda_codec_get_pincfg(codec, item->pin);
+ attr = snd_hda_get_input_pin_attr(def_conf);
+ if (attr <= INPUT_PIN_ATTR_DOCK)
+ return false;
+
+ if (seq_number >= 0) {
+ int seq = get_defcfg_sequence(def_conf);
+ if (seq != seq_number)
+ return false;
+ }
+
+ return true;
+}
+
+/*
+ * Parse all pin widgets and store the useful pin nids to cfg
+ *
+ * The number of line-outs or any primary output is stored in line_outs,
+ * and the corresponding output pins are assigned to line_out_pins[],
+ * in the order of front, rear, CLFE, side, ...
+ *
+ * If more extra outputs (speaker and headphone) are found, the pins are
+ * assisnged to hp_pins[] and speaker_pins[], respectively. If no line-out jack
+ * is detected, one of speaker of HP pins is assigned as the primary
+ * output, i.e. to line_out_pins[0]. So, line_outs is always positive
+ * if any analog output exists.
+ *
+ * The analog input pins are assigned to inputs array.
+ * The digital input/output pins are assigned to dig_in_pin and dig_out_pin,
+ * respectively.
+ */
+int snd_hda_parse_pin_defcfg(struct hda_codec *codec,
+ struct auto_pin_cfg *cfg,
+ const hda_nid_t *ignore_nids,
+ unsigned int cond_flags)
+{
+ hda_nid_t nid, end_nid;
+ short seq, assoc_line_out;
+ struct auto_out_pin line_out[ARRAY_SIZE(cfg->line_out_pins)];
+ struct auto_out_pin speaker_out[ARRAY_SIZE(cfg->speaker_pins)];
+ struct auto_out_pin hp_out[ARRAY_SIZE(cfg->hp_pins)];
+ int i;
+
+ if (!snd_hda_get_int_hint(codec, "parser_flags", &i))
+ cond_flags = i;
+
+ memset(cfg, 0, sizeof(*cfg));
+
+ memset(line_out, 0, sizeof(line_out));
+ memset(speaker_out, 0, sizeof(speaker_out));
+ memset(hp_out, 0, sizeof(hp_out));
+ assoc_line_out = 0;
+
+ end_nid = codec->start_nid + codec->num_nodes;
+ for (nid = codec->start_nid; nid < end_nid; nid++) {
+ unsigned int wid_caps = get_wcaps(codec, nid);
+ unsigned int wid_type = get_wcaps_type(wid_caps);
+ unsigned int def_conf;
+ short assoc, loc, conn, dev;
+
+ /* read all default configuration for pin complex */
+ if (wid_type != AC_WID_PIN)
+ continue;
+ /* ignore the given nids (e.g. pc-beep returns error) */
+ if (ignore_nids && is_in_nid_list(nid, ignore_nids))
+ continue;
+
+ def_conf = snd_hda_codec_get_pincfg(codec, nid);
+ conn = get_defcfg_connect(def_conf);
+ if (conn == AC_JACK_PORT_NONE)
+ continue;
+ loc = get_defcfg_location(def_conf);
+ dev = get_defcfg_device(def_conf);
+
+ /* workaround for buggy BIOS setups */
+ if (dev == AC_JACK_LINE_OUT) {
+ if (conn == AC_JACK_PORT_FIXED ||
+ conn == AC_JACK_PORT_BOTH)
+ dev = AC_JACK_SPEAKER;
+ }
+
+ if (!check_pincap_validity(codec, nid, dev))
+ continue;
+
+ switch (dev) {
+ case AC_JACK_LINE_OUT:
+ seq = get_defcfg_sequence(def_conf);
+ assoc = get_defcfg_association(def_conf);
+
+ if (!(wid_caps & AC_WCAP_STEREO))
+ if (!cfg->mono_out_pin)
+ cfg->mono_out_pin = nid;
+ if (!assoc)
+ continue;
+ if (!assoc_line_out)
+ assoc_line_out = assoc;
+ else if (assoc_line_out != assoc) {
+ codec_info(codec,
+ "ignore pin 0x%x with mismatching assoc# 0x%x vs 0x%x\n",
+ nid, assoc, assoc_line_out);
+ continue;
+ }
+ if (cfg->line_outs >= ARRAY_SIZE(cfg->line_out_pins)) {
+ codec_info(codec,
+ "ignore pin 0x%x, too many assigned pins\n",
+ nid);
+ continue;
+ }
+ line_out[cfg->line_outs].pin = nid;
+ line_out[cfg->line_outs].seq = seq;
+ cfg->line_outs++;
+ break;
+ case AC_JACK_SPEAKER:
+ seq = get_defcfg_sequence(def_conf);
+ assoc = get_defcfg_association(def_conf);
+ if (cfg->speaker_outs >= ARRAY_SIZE(cfg->speaker_pins)) {
+ codec_info(codec,
+ "ignore pin 0x%x, too many assigned pins\n",
+ nid);
+ continue;
+ }
+ speaker_out[cfg->speaker_outs].pin = nid;
+ speaker_out[cfg->speaker_outs].seq = (assoc << 4) | seq;
+ cfg->speaker_outs++;
+ break;
+ case AC_JACK_HP_OUT:
+ seq = get_defcfg_sequence(def_conf);
+ assoc = get_defcfg_association(def_conf);
+ if (cfg->hp_outs >= ARRAY_SIZE(cfg->hp_pins)) {
+ codec_info(codec,
+ "ignore pin 0x%x, too many assigned pins\n",
+ nid);
+ continue;
+ }
+ hp_out[cfg->hp_outs].pin = nid;
+ hp_out[cfg->hp_outs].seq = (assoc << 4) | seq;
+ cfg->hp_outs++;
+ break;
+ case AC_JACK_MIC_IN:
+ add_auto_cfg_input_pin(cfg, nid, AUTO_PIN_MIC);
+ break;
+ case AC_JACK_LINE_IN:
+ add_auto_cfg_input_pin(cfg, nid, AUTO_PIN_LINE_IN);
+ break;
+ case AC_JACK_CD:
+ add_auto_cfg_input_pin(cfg, nid, AUTO_PIN_CD);
+ break;
+ case AC_JACK_AUX:
+ add_auto_cfg_input_pin(cfg, nid, AUTO_PIN_AUX);
+ break;
+ case AC_JACK_SPDIF_OUT:
+ case AC_JACK_DIG_OTHER_OUT:
+ if (cfg->dig_outs >= ARRAY_SIZE(cfg->dig_out_pins)) {
+ codec_info(codec,
+ "ignore pin 0x%x, too many assigned pins\n",
+ nid);
+ continue;
+ }
+ cfg->dig_out_pins[cfg->dig_outs] = nid;
+ cfg->dig_out_type[cfg->dig_outs] =
+ (loc == AC_JACK_LOC_HDMI) ?
+ HDA_PCM_TYPE_HDMI : HDA_PCM_TYPE_SPDIF;
+ cfg->dig_outs++;
+ break;
+ case AC_JACK_SPDIF_IN:
+ case AC_JACK_DIG_OTHER_IN:
+ cfg->dig_in_pin = nid;
+ if (loc == AC_JACK_LOC_HDMI)
+ cfg->dig_in_type = HDA_PCM_TYPE_HDMI;
+ else
+ cfg->dig_in_type = HDA_PCM_TYPE_SPDIF;
+ break;
+ }
+ }
+
+ /* Find a pin that could be a headset or headphone mic */
+ if (cond_flags & HDA_PINCFG_HEADSET_MIC || cond_flags & HDA_PINCFG_HEADPHONE_MIC) {
+ bool hsmic = !!(cond_flags & HDA_PINCFG_HEADSET_MIC);
+ bool hpmic = !!(cond_flags & HDA_PINCFG_HEADPHONE_MIC);
+ for (i = 0; (hsmic || hpmic) && (i < cfg->num_inputs); i++)
+ if (hsmic && can_be_headset_mic(codec, &cfg->inputs[i], 0xc)) {
+ cfg->inputs[i].is_headset_mic = 1;
+ hsmic = false;
+ } else if (hpmic && can_be_headset_mic(codec, &cfg->inputs[i], 0xd)) {
+ cfg->inputs[i].is_headphone_mic = 1;
+ hpmic = false;
+ }
+
+ /* If we didn't find our sequence number mark, fall back to any sequence number */
+ for (i = 0; (hsmic || hpmic) && (i < cfg->num_inputs); i++) {
+ if (!can_be_headset_mic(codec, &cfg->inputs[i], -1))
+ continue;
+ if (hsmic) {
+ cfg->inputs[i].is_headset_mic = 1;
+ hsmic = false;
+ } else if (hpmic) {
+ cfg->inputs[i].is_headphone_mic = 1;
+ hpmic = false;
+ }
+ }
+
+ if (hsmic)
+ codec_dbg(codec, "Told to look for a headset mic, but didn't find any.\n");
+ if (hpmic)
+ codec_dbg(codec, "Told to look for a headphone mic, but didn't find any.\n");
+ }
+
+ /* FIX-UP:
+ * If no line-out is defined but multiple HPs are found,
+ * some of them might be the real line-outs.
+ */
+ if (!cfg->line_outs && cfg->hp_outs > 1 &&
+ !(cond_flags & HDA_PINCFG_NO_HP_FIXUP)) {
+ int i = 0;
+ while (i < cfg->hp_outs) {
+ /* The real HPs should have the sequence 0x0f */
+ if ((hp_out[i].seq & 0x0f) == 0x0f) {
+ i++;
+ continue;
+ }
+ /* Move it to the line-out table */
+ line_out[cfg->line_outs++] = hp_out[i];
+ cfg->hp_outs--;
+ memmove(hp_out + i, hp_out + i + 1,
+ sizeof(hp_out[0]) * (cfg->hp_outs - i));
+ }
+ memset(hp_out + cfg->hp_outs, 0,
+ sizeof(hp_out[0]) * (AUTO_CFG_MAX_OUTS - cfg->hp_outs));
+ if (!cfg->hp_outs)
+ cfg->line_out_type = AUTO_PIN_HP_OUT;
+
+ }
+
+ /* sort by sequence */
+ sort_pins_by_sequence(cfg->line_out_pins, line_out, cfg->line_outs);
+ sort_pins_by_sequence(cfg->speaker_pins, speaker_out,
+ cfg->speaker_outs);
+ sort_pins_by_sequence(cfg->hp_pins, hp_out, cfg->hp_outs);
+
+ /*
+ * FIX-UP: if no line-outs are detected, try to use speaker or HP pin
+ * as a primary output
+ */
+ if (!cfg->line_outs &&
+ !(cond_flags & HDA_PINCFG_NO_LO_FIXUP)) {
+ if (cfg->speaker_outs) {
+ cfg->line_outs = cfg->speaker_outs;
+ memcpy(cfg->line_out_pins, cfg->speaker_pins,
+ sizeof(cfg->speaker_pins));
+ cfg->speaker_outs = 0;
+ memset(cfg->speaker_pins, 0, sizeof(cfg->speaker_pins));
+ cfg->line_out_type = AUTO_PIN_SPEAKER_OUT;
+ } else if (cfg->hp_outs) {
+ cfg->line_outs = cfg->hp_outs;
+ memcpy(cfg->line_out_pins, cfg->hp_pins,
+ sizeof(cfg->hp_pins));
+ cfg->hp_outs = 0;
+ memset(cfg->hp_pins, 0, sizeof(cfg->hp_pins));
+ cfg->line_out_type = AUTO_PIN_HP_OUT;
+ }
+ }
+
+ reorder_outputs(cfg->line_outs, cfg->line_out_pins);
+ reorder_outputs(cfg->hp_outs, cfg->hp_pins);
+ reorder_outputs(cfg->speaker_outs, cfg->speaker_pins);
+
+ /* sort inputs in the order of AUTO_PIN_* type */
+ sort(cfg->inputs, cfg->num_inputs, sizeof(cfg->inputs[0]),
+ compare_input_type, NULL);
+
+ /*
+ * debug prints of the parsed results
+ */
+ codec_info(codec, "autoconfig: line_outs=%d (0x%x/0x%x/0x%x/0x%x/0x%x) type:%s\n",
+ cfg->line_outs, cfg->line_out_pins[0], cfg->line_out_pins[1],
+ cfg->line_out_pins[2], cfg->line_out_pins[3],
+ cfg->line_out_pins[4],
+ cfg->line_out_type == AUTO_PIN_HP_OUT ? "hp" :
+ (cfg->line_out_type == AUTO_PIN_SPEAKER_OUT ?
+ "speaker" : "line"));
+ codec_info(codec, " speaker_outs=%d (0x%x/0x%x/0x%x/0x%x/0x%x)\n",
+ cfg->speaker_outs, cfg->speaker_pins[0],
+ cfg->speaker_pins[1], cfg->speaker_pins[2],
+ cfg->speaker_pins[3], cfg->speaker_pins[4]);
+ codec_info(codec, " hp_outs=%d (0x%x/0x%x/0x%x/0x%x/0x%x)\n",
+ cfg->hp_outs, cfg->hp_pins[0],
+ cfg->hp_pins[1], cfg->hp_pins[2],
+ cfg->hp_pins[3], cfg->hp_pins[4]);
+ codec_info(codec, " mono: mono_out=0x%x\n", cfg->mono_out_pin);
+ if (cfg->dig_outs)
+ codec_info(codec, " dig-out=0x%x/0x%x\n",
+ cfg->dig_out_pins[0], cfg->dig_out_pins[1]);
+ codec_info(codec, " inputs:\n");
+ for (i = 0; i < cfg->num_inputs; i++) {
+ codec_info(codec, " %s=0x%x\n",
+ hda_get_autocfg_input_label(codec, cfg, i),
+ cfg->inputs[i].pin);
+ }
+ if (cfg->dig_in_pin)
+ codec_info(codec, " dig-in=0x%x\n", cfg->dig_in_pin);
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(snd_hda_parse_pin_defcfg);
+
+int snd_hda_get_input_pin_attr(unsigned int def_conf)
+{
+ unsigned int loc = get_defcfg_location(def_conf);
+ unsigned int conn = get_defcfg_connect(def_conf);
+ if (conn == AC_JACK_PORT_NONE)
+ return INPUT_PIN_ATTR_UNUSED;
+ /* Windows may claim the internal mic to be BOTH, too */
+ if (conn == AC_JACK_PORT_FIXED || conn == AC_JACK_PORT_BOTH)
+ return INPUT_PIN_ATTR_INT;
+ if ((loc & 0x30) == AC_JACK_LOC_INTERNAL)
+ return INPUT_PIN_ATTR_INT;
+ if ((loc & 0x30) == AC_JACK_LOC_SEPARATE)
+ return INPUT_PIN_ATTR_DOCK;
+ if (loc == AC_JACK_LOC_REAR)
+ return INPUT_PIN_ATTR_REAR;
+ if (loc == AC_JACK_LOC_FRONT)
+ return INPUT_PIN_ATTR_FRONT;
+ return INPUT_PIN_ATTR_NORMAL;
+}
+EXPORT_SYMBOL_GPL(snd_hda_get_input_pin_attr);
+
+/**
+ * hda_get_input_pin_label - Give a label for the given input pin
+ *
+ * When check_location is true, the function checks the pin location
+ * for mic and line-in pins, and set an appropriate prefix like "Front",
+ * "Rear", "Internal".
+ */
+
+static const char *hda_get_input_pin_label(struct hda_codec *codec,
+ const struct auto_pin_cfg_item *item,
+ hda_nid_t pin, bool check_location)
+{
+ unsigned int def_conf;
+ static const char * const mic_names[] = {
+ "Internal Mic", "Dock Mic", "Mic", "Rear Mic", "Front Mic"
+ };
+ int attr;
+
+ def_conf = snd_hda_codec_get_pincfg(codec, pin);
+
+ switch (get_defcfg_device(def_conf)) {
+ case AC_JACK_MIC_IN:
+ if (item && item->is_headset_mic)
+ return "Headset Mic";
+ if (item && item->is_headphone_mic)
+ return "Headphone Mic";
+ if (!check_location)
+ return "Mic";
+ attr = snd_hda_get_input_pin_attr(def_conf);
+ if (!attr)
+ return "None";
+ return mic_names[attr - 1];
+ case AC_JACK_LINE_IN:
+ if (!check_location)
+ return "Line";
+ attr = snd_hda_get_input_pin_attr(def_conf);
+ if (!attr)
+ return "None";
+ if (attr == INPUT_PIN_ATTR_DOCK)
+ return "Dock Line";
+ return "Line";
+ case AC_JACK_AUX:
+ return "Aux";
+ case AC_JACK_CD:
+ return "CD";
+ case AC_JACK_SPDIF_IN:
+ return "SPDIF In";
+ case AC_JACK_DIG_OTHER_IN:
+ return "Digital In";
+ case AC_JACK_HP_OUT:
+ return "Headphone Mic";
+ default:
+ return "Misc";
+ }
+}
+
+/* Check whether the location prefix needs to be added to the label.
+ * If all mic-jacks are in the same location (e.g. rear panel), we don't
+ * have to put "Front" prefix to each label. In such a case, returns false.
+ */
+static int check_mic_location_need(struct hda_codec *codec,
+ const struct auto_pin_cfg *cfg,
+ int input)
+{
+ unsigned int defc;
+ int i, attr, attr2;
+
+ defc = snd_hda_codec_get_pincfg(codec, cfg->inputs[input].pin);
+ attr = snd_hda_get_input_pin_attr(defc);
+ /* for internal or docking mics, we need locations */
+ if (attr <= INPUT_PIN_ATTR_NORMAL)
+ return 1;
+
+ attr = 0;
+ for (i = 0; i < cfg->num_inputs; i++) {
+ defc = snd_hda_codec_get_pincfg(codec, cfg->inputs[i].pin);
+ attr2 = snd_hda_get_input_pin_attr(defc);
+ if (attr2 >= INPUT_PIN_ATTR_NORMAL) {
+ if (attr && attr != attr2)
+ return 1; /* different locations found */
+ attr = attr2;
+ }
+ }
+ return 0;
+}
+
+/**
+ * hda_get_autocfg_input_label - Get a label for the given input
+ *
+ * Get a label for the given input pin defined by the autocfg item.
+ * Unlike hda_get_input_pin_label(), this function checks all inputs
+ * defined in autocfg and avoids the redundant mic/line prefix as much as
+ * possible.
+ */
+const char *hda_get_autocfg_input_label(struct hda_codec *codec,
+ const struct auto_pin_cfg *cfg,
+ int input)
+{
+ int type = cfg->inputs[input].type;
+ int has_multiple_pins = 0;
+
+ if ((input > 0 && cfg->inputs[input - 1].type == type) ||
+ (input < cfg->num_inputs - 1 && cfg->inputs[input + 1].type == type))
+ has_multiple_pins = 1;
+ if (has_multiple_pins && type == AUTO_PIN_MIC)
+ has_multiple_pins &= check_mic_location_need(codec, cfg, input);
+ return hda_get_input_pin_label(codec, &cfg->inputs[input],
+ cfg->inputs[input].pin,
+ has_multiple_pins);
+}
+EXPORT_SYMBOL_GPL(hda_get_autocfg_input_label);
+
+/* return the position of NID in the list, or -1 if not found */
+static int find_idx_in_nid_list(hda_nid_t nid, const hda_nid_t *list, int nums)
+{
+ int i;
+ for (i = 0; i < nums; i++)
+ if (list[i] == nid)
+ return i;
+ return -1;
+}
+
+/* get a unique suffix or an index number */
+static const char *check_output_sfx(hda_nid_t nid, const hda_nid_t *pins,
+ int num_pins, int *indexp)
+{
+ static const char * const channel_sfx[] = {
+ " Front", " Surround", " CLFE", " Side"
+ };
+ int i;
+
+ i = find_idx_in_nid_list(nid, pins, num_pins);
+ if (i < 0)
+ return NULL;
+ if (num_pins == 1)
+ return "";
+ if (num_pins > ARRAY_SIZE(channel_sfx)) {
+ if (indexp)
+ *indexp = i;
+ return "";
+ }
+ return channel_sfx[i];
+}
+
+static const char *check_output_pfx(struct hda_codec *codec, hda_nid_t nid)
+{
+ unsigned int def_conf = snd_hda_codec_get_pincfg(codec, nid);
+ int attr = snd_hda_get_input_pin_attr(def_conf);
+
+ /* check the location */
+ switch (attr) {
+ case INPUT_PIN_ATTR_DOCK:
+ return "Dock ";
+ case INPUT_PIN_ATTR_FRONT:
+ return "Front ";
+ }
+ return "";
+}
+
+static int get_hp_label_index(struct hda_codec *codec, hda_nid_t nid,
+ const hda_nid_t *pins, int num_pins)
+{
+ int i, j, idx = 0;
+
+ const char *pfx = check_output_pfx(codec, nid);
+
+ i = find_idx_in_nid_list(nid, pins, num_pins);
+ if (i < 0)
+ return -1;
+ for (j = 0; j < i; j++)
+ if (pfx == check_output_pfx(codec, pins[j]))
+ idx++;
+
+ return idx;
+}
+
+static int fill_audio_out_name(struct hda_codec *codec, hda_nid_t nid,
+ const struct auto_pin_cfg *cfg,
+ const char *name, char *label, int maxlen,
+ int *indexp)
+{
+ unsigned int def_conf = snd_hda_codec_get_pincfg(codec, nid);
+ int attr = snd_hda_get_input_pin_attr(def_conf);
+ const char *pfx, *sfx = "";
+
+ /* handle as a speaker if it's a fixed line-out */
+ if (!strcmp(name, "Line Out") && attr == INPUT_PIN_ATTR_INT)
+ name = "Speaker";
+ pfx = check_output_pfx(codec, nid);
+
+ if (cfg) {
+ /* try to give a unique suffix if needed */
+ sfx = check_output_sfx(nid, cfg->line_out_pins, cfg->line_outs,
+ indexp);
+ if (!sfx)
+ sfx = check_output_sfx(nid, cfg->speaker_pins, cfg->speaker_outs,
+ indexp);
+ if (!sfx) {
+ /* don't add channel suffix for Headphone controls */
+ int idx = get_hp_label_index(codec, nid, cfg->hp_pins,
+ cfg->hp_outs);
+ if (idx >= 0 && indexp)
+ *indexp = idx;
+ sfx = "";
+ }
+ }
+ snprintf(label, maxlen, "%s%s%s", pfx, name, sfx);
+ return 1;
+}
+
+#define is_hdmi_cfg(conf) \
+ (get_defcfg_location(conf) == AC_JACK_LOC_HDMI)
+
+/**
+ * snd_hda_get_pin_label - Get a label for the given I/O pin
+ *
+ * Get a label for the given pin. This function works for both input and
+ * output pins. When @cfg is given as non-NULL, the function tries to get
+ * an optimized label using hda_get_autocfg_input_label().
+ *
+ * This function tries to give a unique label string for the pin as much as
+ * possible. For example, when the multiple line-outs are present, it adds
+ * the channel suffix like "Front", "Surround", etc (only when @cfg is given).
+ * If no unique name with a suffix is available and @indexp is non-NULL, the
+ * index number is stored in the pointer.
+ */
+int snd_hda_get_pin_label(struct hda_codec *codec, hda_nid_t nid,
+ const struct auto_pin_cfg *cfg,
+ char *label, int maxlen, int *indexp)
+{
+ unsigned int def_conf = snd_hda_codec_get_pincfg(codec, nid);
+ const char *name = NULL;
+ int i;
+ bool hdmi;
+
+ if (indexp)
+ *indexp = 0;
+ if (get_defcfg_connect(def_conf) == AC_JACK_PORT_NONE)
+ return 0;
+
+ switch (get_defcfg_device(def_conf)) {
+ case AC_JACK_LINE_OUT:
+ return fill_audio_out_name(codec, nid, cfg, "Line Out",
+ label, maxlen, indexp);
+ case AC_JACK_SPEAKER:
+ return fill_audio_out_name(codec, nid, cfg, "Speaker",
+ label, maxlen, indexp);
+ case AC_JACK_HP_OUT:
+ return fill_audio_out_name(codec, nid, cfg, "Headphone",
+ label, maxlen, indexp);
+ case AC_JACK_SPDIF_OUT:
+ case AC_JACK_DIG_OTHER_OUT:
+ hdmi = is_hdmi_cfg(def_conf);
+ name = hdmi ? "HDMI" : "SPDIF";
+ if (cfg && indexp)
+ for (i = 0; i < cfg->dig_outs; i++) {
+ hda_nid_t pin = cfg->dig_out_pins[i];
+ unsigned int c;
+ if (pin == nid)
+ break;
+ c = snd_hda_codec_get_pincfg(codec, pin);
+ if (hdmi == is_hdmi_cfg(c))
+ (*indexp)++;
+ }
+ break;
+ default:
+ if (cfg) {
+ for (i = 0; i < cfg->num_inputs; i++) {
+ if (cfg->inputs[i].pin != nid)
+ continue;
+ name = hda_get_autocfg_input_label(codec, cfg, i);
+ if (name)
+ break;
+ }
+ }
+ if (!name)
+ name = hda_get_input_pin_label(codec, NULL, nid, true);
+ break;
+ }
+ if (!name)
+ return 0;
+ strlcpy(label, name, maxlen);
+ return 1;
+}
+EXPORT_SYMBOL_GPL(snd_hda_get_pin_label);
+
+int snd_hda_add_verbs(struct hda_codec *codec,
+ const struct hda_verb *list)
+{
+ const struct hda_verb **v;
+ v = snd_array_new(&codec->verbs);
+ if (!v)
+ return -ENOMEM;
+ *v = list;
+ return 0;
+}
+EXPORT_SYMBOL_GPL(snd_hda_add_verbs);
+
+void snd_hda_apply_verbs(struct hda_codec *codec)
+{
+ int i;
+ for (i = 0; i < codec->verbs.used; i++) {
+ struct hda_verb **v = snd_array_elem(&codec->verbs, i);
+ snd_hda_sequence_write(codec, *v);
+ }
+}
+EXPORT_SYMBOL_GPL(snd_hda_apply_verbs);
+
+void snd_hda_apply_pincfgs(struct hda_codec *codec,
+ const struct hda_pintbl *cfg)
+{
+ for (; cfg->nid; cfg++)
+ snd_hda_codec_set_pincfg(codec, cfg->nid, cfg->val);
+}
+EXPORT_SYMBOL_GPL(snd_hda_apply_pincfgs);
+
+static void set_pin_targets(struct hda_codec *codec,
+ const struct hda_pintbl *cfg)
+{
+ for (; cfg->nid; cfg++)
+ snd_hda_set_pin_ctl_cache(codec, cfg->nid, cfg->val);
+}
+
+static void apply_fixup(struct hda_codec *codec, int id, int action, int depth)
+{
+ const char *modelname = codec->fixup_name;
+
+ while (id >= 0) {
+ const struct hda_fixup *fix = codec->fixup_list + id;
+
+ if (fix->chained_before)
+ apply_fixup(codec, fix->chain_id, action, depth + 1);
+
+ switch (fix->type) {
+ case HDA_FIXUP_PINS:
+ if (action != HDA_FIXUP_ACT_PRE_PROBE || !fix->v.pins)
+ break;
+ codec_dbg(codec, "%s: Apply pincfg for %s\n",
+ codec->chip_name, modelname);
+ snd_hda_apply_pincfgs(codec, fix->v.pins);
+ break;
+ case HDA_FIXUP_VERBS:
+ if (action != HDA_FIXUP_ACT_PROBE || !fix->v.verbs)
+ break;
+ codec_dbg(codec, "%s: Apply fix-verbs for %s\n",
+ codec->chip_name, modelname);
+ snd_hda_add_verbs(codec, fix->v.verbs);
+ break;
+ case HDA_FIXUP_FUNC:
+ if (!fix->v.func)
+ break;
+ codec_dbg(codec, "%s: Apply fix-func for %s\n",
+ codec->chip_name, modelname);
+ fix->v.func(codec, fix, action);
+ break;
+ case HDA_FIXUP_PINCTLS:
+ if (action != HDA_FIXUP_ACT_PROBE || !fix->v.pins)
+ break;
+ codec_dbg(codec, "%s: Apply pinctl for %s\n",
+ codec->chip_name, modelname);
+ set_pin_targets(codec, fix->v.pins);
+ break;
+ default:
+ codec_err(codec, "%s: Invalid fixup type %d\n",
+ codec->chip_name, fix->type);
+ break;
+ }
+ if (!fix->chained || fix->chained_before)
+ break;
+ if (++depth > 10)
+ break;
+ id = fix->chain_id;
+ }
+}
+
+void snd_hda_apply_fixup(struct hda_codec *codec, int action)
+{
+ if (codec->fixup_list)
+ apply_fixup(codec, codec->fixup_id, action, 0);
+}
+EXPORT_SYMBOL_GPL(snd_hda_apply_fixup);
+
+static bool pin_config_match(struct hda_codec *codec,
+ const struct hda_pintbl *pins)
+{
+ for (; pins->nid; pins++) {
+ u32 def_conf = snd_hda_codec_get_pincfg(codec, pins->nid);
+ if (pins->val != def_conf)
+ return false;
+ }
+ return true;
+}
+
+void snd_hda_pick_pin_fixup(struct hda_codec *codec,
+ const struct snd_hda_pin_quirk *pin_quirk,
+ const struct hda_fixup *fixlist)
+{
+ const struct snd_hda_pin_quirk *pq;
+
+ if (codec->fixup_forced)
+ return;
+
+ for (pq = pin_quirk; pq->subvendor; pq++) {
+ if ((codec->subsystem_id & 0xffff0000) != (pq->subvendor << 16))
+ continue;
+ if (codec->vendor_id != pq->codec)
+ continue;
+ if (pin_config_match(codec, pq->pins)) {
+ codec->fixup_id = pq->value;
+#ifdef CONFIG_SND_DEBUG_VERBOSE
+ codec->fixup_name = pq->name;
+#endif
+ codec->fixup_list = fixlist;
+ return;
+ }
+ }
+}
+EXPORT_SYMBOL_GPL(snd_hda_pick_pin_fixup);
+
+void snd_hda_pick_fixup(struct hda_codec *codec,
+ const struct hda_model_fixup *models,
+ const struct snd_pci_quirk *quirk,
+ const struct hda_fixup *fixlist)
+{
+ const struct snd_pci_quirk *q;
+ int id = -1;
+ const char *name = NULL;
+
+ /* when model=nofixup is given, don't pick up any fixups */
+ if (codec->modelname && !strcmp(codec->modelname, "nofixup")) {
+ codec->fixup_list = NULL;
+ codec->fixup_id = -1;
+ codec->fixup_forced = 1;
+ return;
+ }
+
+ if (codec->modelname && models) {
+ while (models->name) {
+ if (!strcmp(codec->modelname, models->name)) {
+ codec->fixup_id = models->id;
+ codec->fixup_name = models->name;
+ codec->fixup_list = fixlist;
+ codec->fixup_forced = 1;
+ return;
+ }
+ models++;
+ }
+ }
+ if (id < 0 && quirk) {
+ q = snd_pci_quirk_lookup(codec->bus->pci, quirk);
+ if (q) {
+ id = q->value;
+#ifdef CONFIG_SND_DEBUG_VERBOSE
+ name = q->name;
+#endif
+ }
+ }
+ if (id < 0 && quirk) {
+ for (q = quirk; q->subvendor || q->subdevice; q++) {
+ unsigned int vendorid =
+ q->subdevice | (q->subvendor << 16);
+ unsigned int mask = 0xffff0000 | q->subdevice_mask;
+ if ((codec->subsystem_id & mask) == (vendorid & mask)) {
+ id = q->value;
+#ifdef CONFIG_SND_DEBUG_VERBOSE
+ name = q->name;
+#endif
+ break;
+ }
+ }
+ }
+
+ codec->fixup_forced = 0;
+ codec->fixup_id = id;
+ if (id >= 0) {
+ codec->fixup_list = fixlist;
+ codec->fixup_name = name;
+ }
+}
+EXPORT_SYMBOL_GPL(snd_hda_pick_fixup);
diff --git a/sound/pci/hda/hda_auto_parser.h b/sound/pci/hda/hda_auto_parser.h
new file mode 100644
index 00000000000..e941f604f5e
--- /dev/null
+++ b/sound/pci/hda/hda_auto_parser.h
@@ -0,0 +1,118 @@
+/*
+ * BIOS auto-parser helper functions for HD-audio
+ *
+ * Copyright (c) 2012 Takashi Iwai <tiwai@suse.de>
+ *
+ * This driver is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ */
+
+#ifndef __SOUND_HDA_AUTO_PARSER_H
+#define __SOUND_HDA_AUTO_PARSER_H
+
+/*
+ * Helper for automatic pin configuration
+ */
+
+enum {
+ AUTO_PIN_MIC,
+ AUTO_PIN_LINE_IN,
+ AUTO_PIN_CD,
+ AUTO_PIN_AUX,
+ AUTO_PIN_LAST
+};
+
+enum {
+ AUTO_PIN_LINE_OUT,
+ AUTO_PIN_SPEAKER_OUT,
+ AUTO_PIN_HP_OUT
+};
+
+#define AUTO_CFG_MAX_OUTS HDA_MAX_OUTS
+#define AUTO_CFG_MAX_INS 8
+
+struct auto_pin_cfg_item {
+ hda_nid_t pin;
+ int type;
+ unsigned int is_headset_mic:1;
+ unsigned int is_headphone_mic:1; /* Mic-only in headphone jack */
+};
+
+struct auto_pin_cfg;
+const char *hda_get_autocfg_input_label(struct hda_codec *codec,
+ const struct auto_pin_cfg *cfg,
+ int input);
+int snd_hda_get_pin_label(struct hda_codec *codec, hda_nid_t nid,
+ const struct auto_pin_cfg *cfg,
+ char *label, int maxlen, int *indexp);
+
+enum {
+ INPUT_PIN_ATTR_UNUSED, /* pin not connected */
+ INPUT_PIN_ATTR_INT, /* internal mic/line-in */
+ INPUT_PIN_ATTR_DOCK, /* docking mic/line-in */
+ INPUT_PIN_ATTR_NORMAL, /* mic/line-in jack */
+ INPUT_PIN_ATTR_REAR, /* mic/line-in jack in rear */
+ INPUT_PIN_ATTR_FRONT, /* mic/line-in jack in front */
+ INPUT_PIN_ATTR_LAST = INPUT_PIN_ATTR_FRONT,
+};
+
+int snd_hda_get_input_pin_attr(unsigned int def_conf);
+
+struct auto_pin_cfg {
+ int line_outs;
+ /* sorted in the order of Front/Surr/CLFE/Side */
+ hda_nid_t line_out_pins[AUTO_CFG_MAX_OUTS];
+ int speaker_outs;
+ hda_nid_t speaker_pins[AUTO_CFG_MAX_OUTS];
+ int hp_outs;
+ int line_out_type; /* AUTO_PIN_XXX_OUT */
+ hda_nid_t hp_pins[AUTO_CFG_MAX_OUTS];
+ int num_inputs;
+ struct auto_pin_cfg_item inputs[AUTO_CFG_MAX_INS];
+ int dig_outs;
+ hda_nid_t dig_out_pins[2];
+ hda_nid_t dig_in_pin;
+ hda_nid_t mono_out_pin;
+ int dig_out_type[2]; /* HDA_PCM_TYPE_XXX */
+ int dig_in_type; /* HDA_PCM_TYPE_XXX */
+};
+
+/* bit-flags for snd_hda_parse_pin_def_config() behavior */
+#define HDA_PINCFG_NO_HP_FIXUP (1 << 0) /* no HP-split */
+#define HDA_PINCFG_NO_LO_FIXUP (1 << 1) /* don't take other outs as LO */
+#define HDA_PINCFG_HEADSET_MIC (1 << 2) /* Try to find headset mic; mark seq number as 0xc to trigger */
+#define HDA_PINCFG_HEADPHONE_MIC (1 << 3) /* Try to find headphone mic; mark seq number as 0xd to trigger */
+
+int snd_hda_parse_pin_defcfg(struct hda_codec *codec,
+ struct auto_pin_cfg *cfg,
+ const hda_nid_t *ignore_nids,
+ unsigned int cond_flags);
+
+/* older function */
+#define snd_hda_parse_pin_def_config(codec, cfg, ignore) \
+ snd_hda_parse_pin_defcfg(codec, cfg, ignore, 0)
+
+static inline int auto_cfg_hp_outs(const struct auto_pin_cfg *cfg)
+{
+ return (cfg->line_out_type == AUTO_PIN_HP_OUT) ?
+ cfg->line_outs : cfg->hp_outs;
+}
+static inline const hda_nid_t *auto_cfg_hp_pins(const struct auto_pin_cfg *cfg)
+{
+ return (cfg->line_out_type == AUTO_PIN_HP_OUT) ?
+ cfg->line_out_pins : cfg->hp_pins;
+}
+static inline int auto_cfg_speaker_outs(const struct auto_pin_cfg *cfg)
+{
+ return (cfg->line_out_type == AUTO_PIN_SPEAKER_OUT) ?
+ cfg->line_outs : cfg->speaker_outs;
+}
+static inline const hda_nid_t *auto_cfg_speaker_pins(const struct auto_pin_cfg *cfg)
+{
+ return (cfg->line_out_type == AUTO_PIN_SPEAKER_OUT) ?
+ cfg->line_out_pins : cfg->speaker_pins;
+}
+
+#endif /* __SOUND_HDA_AUTO_PARSER_H */
diff --git a/sound/pci/hda/hda_beep.c b/sound/pci/hda/hda_beep.c
index 29714c818b5..8c6c50afc0b 100644
--- a/sound/pci/hda/hda_beep.c
+++ b/sound/pci/hda/hda_beep.c
@@ -20,9 +20,9 @@
*/
#include <linux/input.h>
-#include <linux/pci.h>
#include <linux/slab.h>
#include <linux/workqueue.h>
+#include <linux/export.h>
#include <sound/core.h>
#include "hda_beep.h"
#include "hda_local.h"
@@ -38,13 +38,23 @@ static void snd_hda_generate_beep(struct work_struct *work)
struct hda_beep *beep =
container_of(work, struct hda_beep, beep_work);
struct hda_codec *codec = beep->codec;
+ int tone;
if (!beep->enabled)
return;
+ tone = beep->tone;
+ if (tone && !beep->playing) {
+ snd_hda_power_up(codec);
+ beep->playing = 1;
+ }
/* generate tone */
snd_hda_codec_write(codec, beep->nid, 0,
- AC_VERB_SET_BEEP_CONTROL, beep->tone);
+ AC_VERB_SET_BEEP_CONTROL, tone);
+ if (!tone && beep->playing) {
+ beep->playing = 0;
+ snd_hda_power_down(codec);
+ }
}
/* (non-standard) Linear beep tone calculation for IDT/STAC codecs
@@ -99,6 +109,7 @@ static int snd_hda_beep_event(struct input_dev *dev, unsigned int type,
case SND_BELL:
if (hz)
hz = 1000;
+ /* fallthru */
case SND_TONE:
if (beep->linear_tone)
beep->tone = beep_linear_tone(beep, hz);
@@ -114,27 +125,36 @@ static int snd_hda_beep_event(struct input_dev *dev, unsigned int type,
return 0;
}
+static void turn_off_beep(struct hda_beep *beep)
+{
+ cancel_work_sync(&beep->beep_work);
+ if (beep->playing) {
+ /* turn off beep */
+ snd_hda_codec_write(beep->codec, beep->nid, 0,
+ AC_VERB_SET_BEEP_CONTROL, 0);
+ beep->playing = 0;
+ snd_hda_power_down(beep->codec);
+ }
+}
+
static void snd_hda_do_detach(struct hda_beep *beep)
{
- input_unregister_device(beep->dev);
+ if (beep->registered)
+ input_unregister_device(beep->dev);
+ else
+ input_free_device(beep->dev);
beep->dev = NULL;
- cancel_work_sync(&beep->beep_work);
- /* turn off beep for sure */
- snd_hda_codec_write(beep->codec, beep->nid, 0,
- AC_VERB_SET_BEEP_CONTROL, 0);
+ turn_off_beep(beep);
}
static int snd_hda_do_attach(struct hda_beep *beep)
{
struct input_dev *input_dev;
struct hda_codec *codec = beep->codec;
- int err;
input_dev = input_allocate_device();
- if (!input_dev) {
- printk(KERN_INFO "hda_beep: unable to allocate input device\n");
+ if (!input_dev)
return -ENOMEM;
- }
/* setup digital beep device */
input_dev->name = "HDA Digital PCBeep";
@@ -148,72 +168,33 @@ static int snd_hda_do_attach(struct hda_beep *beep)
input_dev->evbit[0] = BIT_MASK(EV_SND);
input_dev->sndbit[0] = BIT_MASK(SND_BELL) | BIT_MASK(SND_TONE);
input_dev->event = snd_hda_beep_event;
- input_dev->dev.parent = &codec->bus->pci->dev;
+ input_dev->dev.parent = &codec->dev;
input_set_drvdata(input_dev, beep);
- err = input_register_device(input_dev);
- if (err < 0) {
- input_free_device(input_dev);
- printk(KERN_INFO "hda_beep: unable to register input device\n");
- return err;
- }
beep->dev = input_dev;
return 0;
}
-static void snd_hda_do_register(struct work_struct *work)
-{
- struct hda_beep *beep =
- container_of(work, struct hda_beep, register_work);
-
- mutex_lock(&beep->mutex);
- if (beep->enabled && !beep->dev)
- snd_hda_do_attach(beep);
- mutex_unlock(&beep->mutex);
-}
-
-static void snd_hda_do_unregister(struct work_struct *work)
-{
- struct hda_beep *beep =
- container_of(work, struct hda_beep, unregister_work.work);
-
- mutex_lock(&beep->mutex);
- if (!beep->enabled && beep->dev)
- snd_hda_do_detach(beep);
- mutex_unlock(&beep->mutex);
-}
-
int snd_hda_enable_beep_device(struct hda_codec *codec, int enable)
{
struct hda_beep *beep = codec->beep;
- enable = !!enable;
- if (beep == NULL)
+ if (!beep)
return 0;
+ enable = !!enable;
if (beep->enabled != enable) {
beep->enabled = enable;
- if (!enable) {
- /* turn off beep */
- snd_hda_codec_write(beep->codec, beep->nid, 0,
- AC_VERB_SET_BEEP_CONTROL, 0);
- }
- if (beep->mode == HDA_BEEP_MODE_SWREG) {
- if (enable) {
- cancel_delayed_work(&beep->unregister_work);
- schedule_work(&beep->register_work);
- } else {
- schedule_delayed_work(&beep->unregister_work,
- HZ);
- }
- }
+ if (!enable)
+ turn_off_beep(beep);
return 1;
}
return 0;
}
-EXPORT_SYMBOL_HDA(snd_hda_enable_beep_device);
+EXPORT_SYMBOL_GPL(snd_hda_enable_beep_device);
int snd_hda_attach_beep_device(struct hda_codec *codec, int nid)
{
struct hda_beep *beep;
+ int err;
if (!snd_hda_get_bool_hint(codec, "beep"))
return 0; /* disabled explicitly by hints */
@@ -226,42 +207,101 @@ int snd_hda_attach_beep_device(struct hda_codec *codec, int nid)
snprintf(beep->phys, sizeof(beep->phys),
"card%d/codec#%d/beep0", codec->bus->card->number, codec->addr);
/* enable linear scale */
- snd_hda_codec_write(codec, nid, 0,
+ snd_hda_codec_write_cache(codec, nid, 0,
AC_VERB_SET_DIGI_CONVERT_2, 0x01);
beep->nid = nid;
beep->codec = codec;
- beep->mode = codec->beep_mode;
codec->beep = beep;
- INIT_WORK(&beep->register_work, &snd_hda_do_register);
- INIT_DELAYED_WORK(&beep->unregister_work, &snd_hda_do_unregister);
INIT_WORK(&beep->beep_work, &snd_hda_generate_beep);
mutex_init(&beep->mutex);
- if (beep->mode == HDA_BEEP_MODE_ON) {
- int err = snd_hda_do_attach(beep);
- if (err < 0) {
- kfree(beep);
- codec->beep = NULL;
- return err;
- }
+ err = snd_hda_do_attach(beep);
+ if (err < 0) {
+ kfree(beep);
+ codec->beep = NULL;
+ return err;
}
return 0;
}
-EXPORT_SYMBOL_HDA(snd_hda_attach_beep_device);
+EXPORT_SYMBOL_GPL(snd_hda_attach_beep_device);
void snd_hda_detach_beep_device(struct hda_codec *codec)
{
struct hda_beep *beep = codec->beep;
if (beep) {
- cancel_work_sync(&beep->register_work);
- cancel_delayed_work(&beep->unregister_work);
if (beep->dev)
snd_hda_do_detach(beep);
codec->beep = NULL;
kfree(beep);
}
}
-EXPORT_SYMBOL_HDA(snd_hda_detach_beep_device);
+EXPORT_SYMBOL_GPL(snd_hda_detach_beep_device);
+
+int snd_hda_register_beep_device(struct hda_codec *codec)
+{
+ struct hda_beep *beep = codec->beep;
+ int err;
+
+ if (!beep || !beep->dev)
+ return 0;
+
+ err = input_register_device(beep->dev);
+ if (err < 0) {
+ codec_err(codec, "hda_beep: unable to register input device\n");
+ input_free_device(beep->dev);
+ codec->beep = NULL;
+ kfree(beep);
+ return err;
+ }
+ beep->registered = true;
+ return 0;
+}
+EXPORT_SYMBOL_GPL(snd_hda_register_beep_device);
+
+static bool ctl_has_mute(struct snd_kcontrol *kcontrol)
+{
+ struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
+ return query_amp_caps(codec, get_amp_nid(kcontrol),
+ get_amp_direction(kcontrol)) & AC_AMPCAP_MUTE;
+}
+
+/* get/put callbacks for beep mute mixer switches */
+int snd_hda_mixer_amp_switch_get_beep(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
+ struct hda_beep *beep = codec->beep;
+ if (beep && (!beep->enabled || !ctl_has_mute(kcontrol))) {
+ ucontrol->value.integer.value[0] =
+ ucontrol->value.integer.value[1] = beep->enabled;
+ return 0;
+ }
+ return snd_hda_mixer_amp_switch_get(kcontrol, ucontrol);
+}
+EXPORT_SYMBOL_GPL(snd_hda_mixer_amp_switch_get_beep);
+
+int snd_hda_mixer_amp_switch_put_beep(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
+ struct hda_beep *beep = codec->beep;
+ if (beep) {
+ u8 chs = get_amp_channels(kcontrol);
+ int enable = 0;
+ long *valp = ucontrol->value.integer.value;
+ if (chs & 1) {
+ enable |= *valp;
+ valp++;
+ }
+ if (chs & 2)
+ enable |= *valp;
+ snd_hda_enable_beep_device(codec, enable);
+ }
+ if (!ctl_has_mute(kcontrol))
+ return 0;
+ return snd_hda_mixer_amp_switch_put(kcontrol, ucontrol);
+}
+EXPORT_SYMBOL_GPL(snd_hda_mixer_amp_switch_put_beep);
diff --git a/sound/pci/hda/hda_beep.h b/sound/pci/hda/hda_beep.h
index f1de1bac042..a63b5e07733 100644
--- a/sound/pci/hda/hda_beep.h
+++ b/sound/pci/hda/hda_beep.h
@@ -26,21 +26,18 @@
#define HDA_BEEP_MODE_OFF 0
#define HDA_BEEP_MODE_ON 1
-#define HDA_BEEP_MODE_SWREG 2
/* beep information */
struct hda_beep {
struct input_dev *dev;
struct hda_codec *codec;
- unsigned int mode;
char phys[32];
int tone;
hda_nid_t nid;
+ unsigned int registered:1;
unsigned int enabled:1;
- unsigned int request_enable:1;
unsigned int linear_tone:1; /* linear tone for IDT/STAC codec */
- struct work_struct register_work; /* registration work */
- struct delayed_work unregister_work; /* unregistration work */
+ unsigned int playing:1;
struct work_struct beep_work; /* scheduled task for beep event */
struct mutex mutex;
};
@@ -49,8 +46,18 @@ struct hda_beep {
int snd_hda_enable_beep_device(struct hda_codec *codec, int enable);
int snd_hda_attach_beep_device(struct hda_codec *codec, int nid);
void snd_hda_detach_beep_device(struct hda_codec *codec);
+int snd_hda_register_beep_device(struct hda_codec *codec);
#else
-#define snd_hda_attach_beep_device(...) 0
-#define snd_hda_detach_beep_device(...)
+static inline int snd_hda_attach_beep_device(struct hda_codec *codec, int nid)
+{
+ return 0;
+}
+static inline void snd_hda_detach_beep_device(struct hda_codec *codec)
+{
+}
+static inline int snd_hda_register_beep_device(struct hda_codec *codec)
+{
+ return 0;
+}
#endif
#endif
diff --git a/sound/pci/hda/hda_codec.c b/sound/pci/hda/hda_codec.c
index 430f41db604..4c20277a683 100644
--- a/sound/pci/hda/hda_codec.c
+++ b/sound/pci/hda/hda_codec.c
@@ -19,11 +19,13 @@
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
+#include <linux/mm.h>
#include <linux/init.h>
#include <linux/delay.h>
#include <linux/slab.h>
-#include <linux/pci.h>
#include <linux/mutex.h>
+#include <linux/module.h>
+#include <linux/async.h>
#include <sound/core.h>
#include "hda_codec.h"
#include <sound/asoundef.h>
@@ -32,8 +34,12 @@
#include <sound/jack.h>
#include "hda_local.h"
#include "hda_beep.h"
+#include "hda_jack.h"
#include <sound/hda_hwdep.h>
+#define CREATE_TRACE_POINTS
+#include "hda_trace.h"
+
/*
* vendor / preset table
*/
@@ -61,6 +67,7 @@ static struct hda_vendor_id hda_vendor_ids[] = {
{ 0x17e8, "Chrontel" },
{ 0x1854, "LG" },
{ 0x1aec, "Wolfson Microelectronics" },
+ { 0x1af4, "QEMU" },
{ 0x434d, "C-Media" },
{ 0x8086, "Intel" },
{ 0x8384, "SigmaTel" },
@@ -77,7 +84,7 @@ int snd_hda_add_codec_preset(struct hda_codec_preset_list *preset)
mutex_unlock(&preset_mutex);
return 0;
}
-EXPORT_SYMBOL_HDA(snd_hda_add_codec_preset);
+EXPORT_SYMBOL_GPL(snd_hda_add_codec_preset);
int snd_hda_delete_codec_preset(struct hda_codec_preset_list *preset)
{
@@ -86,13 +93,31 @@ int snd_hda_delete_codec_preset(struct hda_codec_preset_list *preset)
mutex_unlock(&preset_mutex);
return 0;
}
-EXPORT_SYMBOL_HDA(snd_hda_delete_codec_preset);
+EXPORT_SYMBOL_GPL(snd_hda_delete_codec_preset);
-#ifdef CONFIG_SND_HDA_POWER_SAVE
+#ifdef CONFIG_PM
+#define codec_in_pm(codec) ((codec)->in_pm)
static void hda_power_work(struct work_struct *work);
static void hda_keep_power_on(struct hda_codec *codec);
+#define hda_codec_is_power_on(codec) ((codec)->power_on)
+
+static void hda_call_pm_notify(struct hda_codec *codec, bool power_up)
+{
+ struct hda_bus *bus = codec->bus;
+
+ if ((power_up && codec->pm_up_notified) ||
+ (!power_up && !codec->pm_up_notified))
+ return;
+ if (bus->ops.pm_notify)
+ bus->ops.pm_notify(bus, power_up);
+ codec->pm_up_notified = power_up;
+}
+
#else
+#define codec_in_pm(codec) 0
static inline void hda_keep_power_on(struct hda_codec *codec) {}
+#define hda_codec_is_power_on(codec) 1
+#define hda_call_pm_notify(codec, state) {}
#endif
/**
@@ -127,7 +152,7 @@ const char *snd_hda_get_jack_location(u32 cfg)
}
return "UNKNOWN";
}
-EXPORT_SYMBOL_HDA(snd_hda_get_jack_location);
+EXPORT_SYMBOL_GPL(snd_hda_get_jack_location);
/**
* snd_hda_get_jack_connectivity - Give a connectivity string of the jack
@@ -142,7 +167,7 @@ const char *snd_hda_get_jack_connectivity(u32 cfg)
return jack_locations[(cfg >> (AC_DEFCFG_LOCATION_SHIFT + 4)) & 3];
}
-EXPORT_SYMBOL_HDA(snd_hda_get_jack_connectivity);
+EXPORT_SYMBOL_GPL(snd_hda_get_jack_connectivity);
/**
* snd_hda_get_jack_type - Give a type string of the jack
@@ -157,32 +182,31 @@ const char *snd_hda_get_jack_type(u32 cfg)
"Line Out", "Speaker", "HP Out", "CD",
"SPDIF Out", "Digital Out", "Modem Line", "Modem Hand",
"Line In", "Aux", "Mic", "Telephony",
- "SPDIF In", "Digitial In", "Reserved", "Other"
+ "SPDIF In", "Digital In", "Reserved", "Other"
};
return jack_types[(cfg & AC_DEFCFG_DEVICE)
>> AC_DEFCFG_DEVICE_SHIFT];
}
-EXPORT_SYMBOL_HDA(snd_hda_get_jack_type);
+EXPORT_SYMBOL_GPL(snd_hda_get_jack_type);
/*
* Compose a 32bit command word to be sent to the HD-audio controller
*/
static inline unsigned int
-make_codec_cmd(struct hda_codec *codec, hda_nid_t nid, int direct,
+make_codec_cmd(struct hda_codec *codec, hda_nid_t nid, int flags,
unsigned int verb, unsigned int parm)
{
u32 val;
- if ((codec->addr & ~0xf) || (direct & ~1) || (nid & ~0x7f) ||
+ if ((codec->addr & ~0xf) || (nid & ~0x7f) ||
(verb & ~0xfff) || (parm & ~0xffff)) {
- printk(KERN_ERR "hda-codec: out of range cmd %x:%x:%x:%x:%x\n",
- codec->addr, direct, nid, verb, parm);
+ codec_err(codec, "hda-codec: out of range cmd %x:%x:%x:%x\n",
+ codec->addr, nid, verb, parm);
return ~0;
}
val = (u32)codec->addr << 28;
- val |= (u32)direct << 27;
val |= (u32)nid << 20;
val |= verb << 8;
val |= parm;
@@ -193,7 +217,7 @@ make_codec_cmd(struct hda_codec *codec, hda_nid_t nid, int direct,
* Send and receive a verb
*/
static int codec_exec_verb(struct hda_codec *codec, unsigned int cmd,
- unsigned int *res)
+ int flags, unsigned int *res)
{
struct hda_bus *bus = codec->bus;
int err;
@@ -206,21 +230,34 @@ static int codec_exec_verb(struct hda_codec *codec, unsigned int cmd,
again:
snd_hda_power_up(codec);
mutex_lock(&bus->cmd_mutex);
- err = bus->ops.command(bus, cmd);
- if (!err && res)
+ if (flags & HDA_RW_NO_RESPONSE_FALLBACK)
+ bus->no_response_fallback = 1;
+ for (;;) {
+ trace_hda_send_cmd(codec, cmd);
+ err = bus->ops.command(bus, cmd);
+ if (err != -EAGAIN)
+ break;
+ /* process pending verbs */
+ bus->ops.get_response(bus, codec->addr);
+ }
+ if (!err && res) {
*res = bus->ops.get_response(bus, codec->addr);
+ trace_hda_get_response(codec, *res);
+ }
+ bus->no_response_fallback = 0;
mutex_unlock(&bus->cmd_mutex);
snd_hda_power_down(codec);
- if (res && *res == -1 && bus->rirb_error) {
+ if (!codec_in_pm(codec) && res && *res == -1 && bus->rirb_error) {
if (bus->response_reset) {
- snd_printd("hda_codec: resetting BUS due to "
- "fatal communication error\n");
+ codec_dbg(codec,
+ "resetting BUS due to fatal communication error\n");
+ trace_hda_bus_reset(bus);
bus->ops.bus_reset(bus);
}
goto again;
}
/* clear reset-flag when the communication gets recovered */
- if (!err)
+ if (!err || codec_in_pm(codec))
bus->response_reset = 0;
return err;
}
@@ -229,7 +266,7 @@ static int codec_exec_verb(struct hda_codec *codec, unsigned int cmd,
* snd_hda_codec_read - send a command and get the response
* @codec: the HDA codec
* @nid: NID to send the command
- * @direct: direct flag
+ * @flags: optional bit flags
* @verb: the verb to send
* @parm: the parameter for the verb
*
@@ -238,21 +275,22 @@ static int codec_exec_verb(struct hda_codec *codec, unsigned int cmd,
* Returns the obtained response value, or -1 for an error.
*/
unsigned int snd_hda_codec_read(struct hda_codec *codec, hda_nid_t nid,
- int direct,
+ int flags,
unsigned int verb, unsigned int parm)
{
- unsigned cmd = make_codec_cmd(codec, nid, direct, verb, parm);
+ unsigned cmd = make_codec_cmd(codec, nid, flags, verb, parm);
unsigned int res;
- codec_exec_verb(codec, cmd, &res);
+ if (codec_exec_verb(codec, cmd, flags, &res))
+ return -1;
return res;
}
-EXPORT_SYMBOL_HDA(snd_hda_codec_read);
+EXPORT_SYMBOL_GPL(snd_hda_codec_read);
/**
* snd_hda_codec_write - send a single command without waiting for response
* @codec: the HDA codec
* @nid: NID to send the command
- * @direct: direct flag
+ * @flags: optional bit flags
* @verb: the verb to send
* @parm: the parameter for the verb
*
@@ -260,15 +298,15 @@ EXPORT_SYMBOL_HDA(snd_hda_codec_read);
*
* Returns 0 if successful, or a negative error code.
*/
-int snd_hda_codec_write(struct hda_codec *codec, hda_nid_t nid, int direct,
- unsigned int verb, unsigned int parm)
+int snd_hda_codec_write(struct hda_codec *codec, hda_nid_t nid, int flags,
+ unsigned int verb, unsigned int parm)
{
- unsigned int cmd = make_codec_cmd(codec, nid, direct, verb, parm);
+ unsigned int cmd = make_codec_cmd(codec, nid, flags, verb, parm);
unsigned int res;
- return codec_exec_verb(codec, cmd,
+ return codec_exec_verb(codec, cmd, flags,
codec->bus->sync_write ? &res : NULL);
}
-EXPORT_SYMBOL_HDA(snd_hda_codec_write);
+EXPORT_SYMBOL_GPL(snd_hda_codec_write);
/**
* snd_hda_sequence_write - sequence writes
@@ -283,7 +321,7 @@ void snd_hda_sequence_write(struct hda_codec *codec, const struct hda_verb *seq)
for (; seq->nid; seq++)
snd_hda_codec_write(codec, seq->nid, 0, seq->verb, seq->param);
}
-EXPORT_SYMBOL_HDA(snd_hda_sequence_write);
+EXPORT_SYMBOL_GPL(snd_hda_sequence_write);
/**
* snd_hda_get_sub_nodes - get the range of sub nodes
@@ -305,13 +343,123 @@ int snd_hda_get_sub_nodes(struct hda_codec *codec, hda_nid_t nid,
*start_id = (parm >> 16) & 0x7fff;
return (int)(parm & 0x7fff);
}
-EXPORT_SYMBOL_HDA(snd_hda_get_sub_nodes);
+EXPORT_SYMBOL_GPL(snd_hda_get_sub_nodes);
+
+/* connection list element */
+struct hda_conn_list {
+ struct list_head list;
+ int len;
+ hda_nid_t nid;
+ hda_nid_t conns[0];
+};
+
+/* look up the cached results */
+static struct hda_conn_list *
+lookup_conn_list(struct hda_codec *codec, hda_nid_t nid)
+{
+ struct hda_conn_list *p;
+ list_for_each_entry(p, &codec->conn_list, list) {
+ if (p->nid == nid)
+ return p;
+ }
+ return NULL;
+}
+
+static int add_conn_list(struct hda_codec *codec, hda_nid_t nid, int len,
+ const hda_nid_t *list)
+{
+ struct hda_conn_list *p;
+
+ p = kmalloc(sizeof(*p) + len * sizeof(hda_nid_t), GFP_KERNEL);
+ if (!p)
+ return -ENOMEM;
+ p->len = len;
+ p->nid = nid;
+ memcpy(p->conns, list, len * sizeof(hda_nid_t));
+ list_add(&p->list, &codec->conn_list);
+ return 0;
+}
+
+static void remove_conn_list(struct hda_codec *codec)
+{
+ while (!list_empty(&codec->conn_list)) {
+ struct hda_conn_list *p;
+ p = list_first_entry(&codec->conn_list, typeof(*p), list);
+ list_del(&p->list);
+ kfree(p);
+ }
+}
+
+/* read the connection and add to the cache */
+static int read_and_add_raw_conns(struct hda_codec *codec, hda_nid_t nid)
+{
+ hda_nid_t list[32];
+ hda_nid_t *result = list;
+ int len;
+
+ len = snd_hda_get_raw_connections(codec, nid, list, ARRAY_SIZE(list));
+ if (len == -ENOSPC) {
+ len = snd_hda_get_num_raw_conns(codec, nid);
+ result = kmalloc(sizeof(hda_nid_t) * len, GFP_KERNEL);
+ if (!result)
+ return -ENOMEM;
+ len = snd_hda_get_raw_connections(codec, nid, result, len);
+ }
+ if (len >= 0)
+ len = snd_hda_override_conn_list(codec, nid, len, result);
+ if (result != list)
+ kfree(result);
+ return len;
+}
/**
- * snd_hda_get_connections - get connection list
+ * snd_hda_get_conn_list - get connection list
* @codec: the HDA codec
* @nid: NID to parse
- * @conn_list: connection list array
+ * @len: number of connection list entries
+ * @listp: the pointer to store NID list
+ *
+ * Parses the connection list of the given widget and stores the pointer
+ * to the list of NIDs.
+ *
+ * Returns the number of connections, or a negative error code.
+ *
+ * Note that the returned pointer isn't protected against the list
+ * modification. If snd_hda_override_conn_list() might be called
+ * concurrently, protect with a mutex appropriately.
+ */
+int snd_hda_get_conn_list(struct hda_codec *codec, hda_nid_t nid,
+ const hda_nid_t **listp)
+{
+ bool added = false;
+
+ for (;;) {
+ int err;
+ const struct hda_conn_list *p;
+
+ /* if the connection-list is already cached, read it */
+ p = lookup_conn_list(codec, nid);
+ if (p) {
+ if (listp)
+ *listp = p->conns;
+ return p->len;
+ }
+ if (snd_BUG_ON(added))
+ return -EINVAL;
+
+ err = read_and_add_raw_conns(codec, nid);
+ if (err < 0)
+ return err;
+ added = true;
+ }
+}
+EXPORT_SYMBOL_GPL(snd_hda_get_conn_list);
+
+/**
+ * snd_hda_get_connections - copy connection list
+ * @codec: the HDA codec
+ * @nid: NID to parse
+ * @conn_list: connection list array; when NULL, checks only the size
* @max_conns: max. number of connections to store
*
* Parses the connection list of the given widget and stores the list
@@ -322,24 +470,67 @@ EXPORT_SYMBOL_HDA(snd_hda_get_sub_nodes);
int snd_hda_get_connections(struct hda_codec *codec, hda_nid_t nid,
hda_nid_t *conn_list, int max_conns)
{
+ const hda_nid_t *list;
+ int len = snd_hda_get_conn_list(codec, nid, &list);
+
+ if (len > 0 && conn_list) {
+ if (len > max_conns) {
+ codec_err(codec, "Too many connections %d for NID 0x%x\n",
+ len, nid);
+ return -EINVAL;
+ }
+ memcpy(conn_list, list, len * sizeof(hda_nid_t));
+ }
+
+ return len;
+}
+EXPORT_SYMBOL_GPL(snd_hda_get_connections);
+
+/* return CONNLIST_LEN parameter of the given widget */
+static unsigned int get_num_conns(struct hda_codec *codec, hda_nid_t nid)
+{
+ unsigned int wcaps = get_wcaps(codec, nid);
+ unsigned int parm;
+
+ if (!(wcaps & AC_WCAP_CONN_LIST) &&
+ get_wcaps_type(wcaps) != AC_WID_VOL_KNB)
+ return 0;
+
+ parm = snd_hda_param_read(codec, nid, AC_PAR_CONNLIST_LEN);
+ if (parm == -1)
+ parm = 0;
+ return parm;
+}
+
+int snd_hda_get_num_raw_conns(struct hda_codec *codec, hda_nid_t nid)
+{
+ return snd_hda_get_raw_connections(codec, nid, NULL, 0);
+}
+
+/**
+ * snd_hda_get_raw_connections - copy connection list without cache
+ * @codec: the HDA codec
+ * @nid: NID to parse
+ * @conn_list: connection list array
+ * @max_conns: max. number of connections to store
+ *
+ * Like snd_hda_get_connections(), copy the connection list but without
+ * checking through the connection-list cache.
+ * Currently called only from hda_proc.c, so not exported.
+ */
+int snd_hda_get_raw_connections(struct hda_codec *codec, hda_nid_t nid,
+ hda_nid_t *conn_list, int max_conns)
+{
unsigned int parm;
int i, conn_len, conns;
unsigned int shift, num_elems, mask;
- unsigned int wcaps;
hda_nid_t prev_nid;
+ int null_count = 0;
- if (snd_BUG_ON(!conn_list || max_conns <= 0))
- return -EINVAL;
-
- wcaps = get_wcaps(codec, nid);
- if (!(wcaps & AC_WCAP_CONN_LIST) &&
- get_wcaps_type(wcaps) != AC_WID_VOL_KNB) {
- snd_printk(KERN_WARNING "hda_codec: "
- "connection list not available for 0x%x\n", nid);
- return -EINVAL;
- }
+ parm = get_num_conns(codec, nid);
+ if (!parm)
+ return 0;
- parm = snd_hda_param_read(codec, nid, AC_PAR_CONNLIST_LEN);
if (parm & AC_CLIST_LONG) {
/* long form */
shift = 16;
@@ -361,7 +552,8 @@ int snd_hda_get_connections(struct hda_codec *codec, hda_nid_t nid,
AC_VERB_GET_CONNECT_LIST, 0);
if (parm == -1 && codec->bus->rirb_error)
return -EIO;
- conn_list[0] = parm & mask;
+ if (conn_list)
+ conn_list[0] = parm & mask;
return 1;
}
@@ -380,9 +572,9 @@ int snd_hda_get_connections(struct hda_codec *codec, hda_nid_t nid,
}
range_val = !!(parm & (1 << (shift-1))); /* ranges */
val = parm & mask;
- if (val == 0) {
- snd_printk(KERN_WARNING "hda_codec: "
- "invalid CONNECT_LIST verb %x[%i]:%x\n",
+ if (val == 0 && null_count++) { /* no second chance */
+ codec_dbg(codec,
+ "invalid CONNECT_LIST verb %x[%i]:%x\n",
nid, i, parm);
return 0;
}
@@ -390,35 +582,155 @@ int snd_hda_get_connections(struct hda_codec *codec, hda_nid_t nid,
if (range_val) {
/* ranges between the previous and this one */
if (!prev_nid || prev_nid >= val) {
- snd_printk(KERN_WARNING "hda_codec: "
+ codec_warn(codec,
"invalid dep_range_val %x:%x\n",
prev_nid, val);
continue;
}
for (n = prev_nid + 1; n <= val; n++) {
- if (conns >= max_conns) {
- snd_printk(KERN_ERR "hda_codec: "
- "Too many connections %d for NID 0x%x\n",
- conns, nid);
- return -EINVAL;
+ if (conn_list) {
+ if (conns >= max_conns)
+ return -ENOSPC;
+ conn_list[conns] = n;
}
- conn_list[conns++] = n;
+ conns++;
}
} else {
- if (conns >= max_conns) {
- snd_printk(KERN_ERR "hda_codec: "
- "Too many connections %d for NID 0x%x\n",
- conns, nid);
- return -EINVAL;
+ if (conn_list) {
+ if (conns >= max_conns)
+ return -ENOSPC;
+ conn_list[conns] = val;
}
- conn_list[conns++] = val;
+ conns++;
}
prev_nid = val;
}
return conns;
}
-EXPORT_SYMBOL_HDA(snd_hda_get_connections);
+/**
+ * snd_hda_override_conn_list - add/modify the connection-list to cache
+ * @codec: the HDA codec
+ * @nid: NID to parse
+ * @len: number of connection list entries
+ * @list: the list of connection entries
+ *
+ * Add or modify the given connection-list to the cache. If the corresponding
+ * cache already exists, invalidate it and append a new one.
+ *
+ * Returns zero or a negative error code.
+ */
+int snd_hda_override_conn_list(struct hda_codec *codec, hda_nid_t nid, int len,
+ const hda_nid_t *list)
+{
+ struct hda_conn_list *p;
+
+ p = lookup_conn_list(codec, nid);
+ if (p) {
+ list_del(&p->list);
+ kfree(p);
+ }
+
+ return add_conn_list(codec, nid, len, list);
+}
+EXPORT_SYMBOL_GPL(snd_hda_override_conn_list);
+
+/**
+ * snd_hda_get_conn_index - get the connection index of the given NID
+ * @codec: the HDA codec
+ * @mux: NID containing the list
+ * @nid: NID to select
+ * @recursive: 1 when searching NID recursively, otherwise 0
+ *
+ * Parses the connection list of the widget @mux and checks whether the
+ * widget @nid is present. If it is, return the connection index.
+ * Otherwise it returns -1.
+ */
+int snd_hda_get_conn_index(struct hda_codec *codec, hda_nid_t mux,
+ hda_nid_t nid, int recursive)
+{
+ const hda_nid_t *conn;
+ int i, nums;
+
+ nums = snd_hda_get_conn_list(codec, mux, &conn);
+ for (i = 0; i < nums; i++)
+ if (conn[i] == nid)
+ return i;
+ if (!recursive)
+ return -1;
+ if (recursive > 10) {
+ codec_dbg(codec, "too deep connection for 0x%x\n", nid);
+ return -1;
+ }
+ recursive++;
+ for (i = 0; i < nums; i++) {
+ unsigned int type = get_wcaps_type(get_wcaps(codec, conn[i]));
+ if (type == AC_WID_PIN || type == AC_WID_AUD_OUT)
+ continue;
+ if (snd_hda_get_conn_index(codec, conn[i], nid, recursive) >= 0)
+ return i;
+ }
+ return -1;
+}
+EXPORT_SYMBOL_GPL(snd_hda_get_conn_index);
+
+
+/* return DEVLIST_LEN parameter of the given widget */
+static unsigned int get_num_devices(struct hda_codec *codec, hda_nid_t nid)
+{
+ unsigned int wcaps = get_wcaps(codec, nid);
+ unsigned int parm;
+
+ if (!codec->dp_mst || !(wcaps & AC_WCAP_DIGITAL) ||
+ get_wcaps_type(wcaps) != AC_WID_PIN)
+ return 0;
+
+ parm = snd_hda_param_read(codec, nid, AC_PAR_DEVLIST_LEN);
+ if (parm == -1 && codec->bus->rirb_error)
+ parm = 0;
+ return parm & AC_DEV_LIST_LEN_MASK;
+}
+
+/**
+ * snd_hda_get_devices - copy device list without cache
+ * @codec: the HDA codec
+ * @nid: NID of the pin to parse
+ * @dev_list: device list array
+ * @max_devices: max. number of devices to store
+ *
+ * Copy the device list. This info is dynamic and so not cached.
+ * Currently called only from hda_proc.c, so not exported.
+ */
+int snd_hda_get_devices(struct hda_codec *codec, hda_nid_t nid,
+ u8 *dev_list, int max_devices)
+{
+ unsigned int parm;
+ int i, dev_len, devices;
+
+ parm = get_num_devices(codec, nid);
+ if (!parm) /* not multi-stream capable */
+ return 0;
+
+ dev_len = parm + 1;
+ dev_len = dev_len < max_devices ? dev_len : max_devices;
+
+ devices = 0;
+ while (devices < dev_len) {
+ parm = snd_hda_codec_read(codec, nid, 0,
+ AC_VERB_GET_DEVICE_LIST, devices);
+ if (parm == -1 && codec->bus->rirb_error)
+ break;
+
+ for (i = 0; i < 8; i++) {
+ dev_list[devices] = (u8)parm;
+ parm >>= 4;
+ devices++;
+ if (devices >= dev_len)
+ break;
+ }
+ }
+ return devices;
+}
/**
* snd_hda_queue_unsol_event - add an unsolicited event to queue
@@ -437,6 +749,10 @@ int snd_hda_queue_unsol_event(struct hda_bus *bus, u32 res, u32 res_ex)
struct hda_bus_unsolicited *unsol;
unsigned int wp;
+ if (!bus || !bus->workq)
+ return 0;
+
+ trace_hda_unsol_event(bus, res, res_ex);
unsol = bus->unsol;
if (!unsol)
return 0;
@@ -452,7 +768,7 @@ int snd_hda_queue_unsol_event(struct hda_bus *bus, u32 res, u32 res_ex)
return 0;
}
-EXPORT_SYMBOL_HDA(snd_hda_queue_unsol_event);
+EXPORT_SYMBOL_GPL(snd_hda_queue_unsol_event);
/*
* process queued unsolicited events
@@ -491,8 +807,7 @@ static int init_unsol_queue(struct hda_bus *bus)
unsol = kzalloc(sizeof(*unsol), GFP_KERNEL);
if (!unsol) {
- snd_printk(KERN_ERR "hda_codec: "
- "can't allocate unsolicited queue\n");
+ dev_err(bus->card->dev, "can't allocate unsolicited queue\n");
return -ENOMEM;
}
INIT_WORK(&unsol->work, process_unsol_events);
@@ -504,50 +819,36 @@ static int init_unsol_queue(struct hda_bus *bus)
/*
* destructor
*/
-static void snd_hda_codec_free(struct hda_codec *codec);
-
-static int snd_hda_bus_free(struct hda_bus *bus)
+static void snd_hda_bus_free(struct hda_bus *bus)
{
- struct hda_codec *codec, *n;
-
if (!bus)
- return 0;
+ return;
+
+ WARN_ON(!list_empty(&bus->codec_list));
if (bus->workq)
flush_workqueue(bus->workq);
if (bus->unsol)
kfree(bus->unsol);
- list_for_each_entry_safe(codec, n, &bus->codec_list, list) {
- snd_hda_codec_free(codec);
- }
if (bus->ops.private_free)
bus->ops.private_free(bus);
if (bus->workq)
destroy_workqueue(bus->workq);
+
kfree(bus);
- return 0;
}
static int snd_hda_bus_dev_free(struct snd_device *device)
{
- struct hda_bus *bus = device->device_data;
- bus->shutdown = 1;
- return snd_hda_bus_free(bus);
+ snd_hda_bus_free(device->device_data);
+ return 0;
}
-#ifdef CONFIG_SND_HDA_HWDEP
-static int snd_hda_bus_dev_register(struct snd_device *device)
+static int snd_hda_bus_dev_disconnect(struct snd_device *device)
{
struct hda_bus *bus = device->device_data;
- struct hda_codec *codec;
- list_for_each_entry(codec, &bus->codec_list, list) {
- snd_hda_hwdep_add_sysfs(codec);
- snd_hda_hwdep_add_power_sysfs(codec);
- }
+ bus->shutdown = 1;
return 0;
}
-#else
-#define snd_hda_bus_dev_register NULL
-#endif
/**
* snd_hda_bus_new - create a HDA bus
@@ -557,14 +858,14 @@ static int snd_hda_bus_dev_register(struct snd_device *device)
*
* Returns 0 if successful, or a negative error code.
*/
-int /*__devinit*/ snd_hda_bus_new(struct snd_card *card,
+int snd_hda_bus_new(struct snd_card *card,
const struct hda_bus_template *temp,
struct hda_bus **busp)
{
struct hda_bus *bus;
int err;
static struct snd_device_ops dev_ops = {
- .dev_register = snd_hda_bus_dev_register,
+ .dev_disconnect = snd_hda_bus_dev_disconnect,
.dev_free = snd_hda_bus_dev_free,
};
@@ -578,7 +879,7 @@ int /*__devinit*/ snd_hda_bus_new(struct snd_card *card,
bus = kzalloc(sizeof(*bus), GFP_KERNEL);
if (bus == NULL) {
- snd_printk(KERN_ERR "can't allocate struct hda_bus\n");
+ dev_err(card->dev, "can't allocate struct hda_bus\n");
return -ENOMEM;
}
@@ -597,7 +898,7 @@ int /*__devinit*/ snd_hda_bus_new(struct snd_card *card,
"hd-audio%d", card->number);
bus->workq = create_singlethread_workqueue(bus->workq_name);
if (!bus->workq) {
- snd_printk(KERN_ERR "cannot create workqueue %s\n",
+ dev_err(card->dev, "cannot create workqueue %s\n",
bus->workq_name);
kfree(bus);
return -ENOMEM;
@@ -612,9 +913,9 @@ int /*__devinit*/ snd_hda_bus_new(struct snd_card *card,
*busp = bus;
return 0;
}
-EXPORT_SYMBOL_HDA(snd_hda_bus_new);
+EXPORT_SYMBOL_GPL(snd_hda_bus_new);
-#ifdef CONFIG_SND_HDA_GENERIC
+#if IS_ENABLED(CONFIG_SND_HDA_GENERIC)
#define is_generic_config(codec) \
(codec->modelname && !strcmp(codec->modelname, "generic"))
#else
@@ -635,16 +936,13 @@ find_codec_preset(struct hda_codec *codec)
{
struct hda_codec_preset_list *tbl;
const struct hda_codec_preset *preset;
- int mod_requested = 0;
-
- if (is_generic_config(codec))
- return NULL; /* use the generic parser */
+ unsigned int mod_requested = 0;
again:
mutex_lock(&preset_mutex);
list_for_each_entry(tbl, &hda_preset_tables, list) {
if (!try_module_get(tbl->owner)) {
- snd_printk(KERN_ERR "hda_codec: cannot module_get\n");
+ codec_err(codec, "cannot module_get\n");
continue;
}
for (preset = tbl->preset; preset->id; preset++) {
@@ -727,7 +1025,7 @@ static int get_codec_name(struct hda_codec *codec)
/*
* look for an AFG and MFG nodes
*/
-static void /*__devinit*/ setup_fg_nodes(struct hda_codec *codec)
+static void setup_fg_nodes(struct hda_codec *codec)
{
int i, total_nodes, function_id;
hda_nid_t nid;
@@ -812,19 +1110,6 @@ static struct hda_pincfg *look_up_pincfg(struct hda_codec *codec,
return NULL;
}
-/* write a config value for the given NID */
-static void set_pincfg(struct hda_codec *codec, hda_nid_t nid,
- unsigned int cfg)
-{
- int i;
- for (i = 0; i < 4; i++) {
- snd_hda_codec_write(codec, nid, 0,
- AC_VERB_SET_CONFIG_DEFAULT_BYTES_0 + i,
- cfg & 0xff);
- cfg >>= 8;
- }
-}
-
/* set the current pin config value for the given NID.
* the value is cached, and read via snd_hda_codec_get_pincfg()
*/
@@ -832,12 +1117,16 @@ int snd_hda_add_pincfg(struct hda_codec *codec, struct snd_array *list,
hda_nid_t nid, unsigned int cfg)
{
struct hda_pincfg *pin;
- unsigned int oldcfg;
+ /* the check below may be invalid when pins are added by a fixup
+ * dynamically (e.g. via snd_hda_codec_update_widgets()), so disabled
+ * for now
+ */
+ /*
if (get_wcaps_type(get_wcaps(codec, nid)) != AC_WID_PIN)
return -EINVAL;
+ */
- oldcfg = snd_hda_codec_get_pincfg(codec, nid);
pin = look_up_pincfg(codec, list, nid);
if (!pin) {
pin = snd_array_new(list);
@@ -846,13 +1135,6 @@ int snd_hda_add_pincfg(struct hda_codec *codec, struct snd_array *list,
pin->nid = nid;
}
pin->cfg = cfg;
-
- /* change only when needed; e.g. if the pincfg is already present
- * in user_pins[], don't write it
- */
- cfg = snd_hda_codec_get_pincfg(codec, nid);
- if (oldcfg != cfg)
- set_pincfg(codec, nid, cfg);
return 0;
}
@@ -871,7 +1153,7 @@ int snd_hda_codec_set_pincfg(struct hda_codec *codec,
{
return snd_hda_add_pincfg(codec, &codec->driver_pins, nid, cfg);
}
-EXPORT_SYMBOL_HDA(snd_hda_codec_set_pincfg);
+EXPORT_SYMBOL_GPL(snd_hda_codec_set_pincfg);
/**
* snd_hda_codec_get_pincfg - Obtain a pin-default configuration
@@ -886,10 +1168,17 @@ unsigned int snd_hda_codec_get_pincfg(struct hda_codec *codec, hda_nid_t nid)
{
struct hda_pincfg *pin;
-#ifdef CONFIG_SND_HDA_HWDEP
- pin = look_up_pincfg(codec, &codec->user_pins, nid);
- if (pin)
- return pin->cfg;
+#ifdef CONFIG_SND_HDA_RECONFIG
+ {
+ unsigned int cfg = 0;
+ mutex_lock(&codec->user_mutex);
+ pin = look_up_pincfg(codec, &codec->user_pins, nid);
+ if (pin)
+ cfg = pin->cfg;
+ mutex_unlock(&codec->user_mutex);
+ if (cfg)
+ return cfg;
+ }
#endif
pin = look_up_pincfg(codec, &codec->driver_pins, nid);
if (pin)
@@ -899,18 +1188,33 @@ unsigned int snd_hda_codec_get_pincfg(struct hda_codec *codec, hda_nid_t nid)
return pin->cfg;
return 0;
}
-EXPORT_SYMBOL_HDA(snd_hda_codec_get_pincfg);
+EXPORT_SYMBOL_GPL(snd_hda_codec_get_pincfg);
-/* restore all current pin configs */
-static void restore_pincfgs(struct hda_codec *codec)
+/* remember the current pinctl target value */
+int snd_hda_codec_set_pin_target(struct hda_codec *codec, hda_nid_t nid,
+ unsigned int val)
{
- int i;
- for (i = 0; i < codec->init_pins.used; i++) {
- struct hda_pincfg *pin = snd_array_elem(&codec->init_pins, i);
- set_pincfg(codec, pin->nid,
- snd_hda_codec_get_pincfg(codec, pin->nid));
- }
+ struct hda_pincfg *pin;
+
+ pin = look_up_pincfg(codec, &codec->init_pins, nid);
+ if (!pin)
+ return -EINVAL;
+ pin->target = val;
+ return 0;
}
+EXPORT_SYMBOL_GPL(snd_hda_codec_set_pin_target);
+
+/* return the current pinctl target value */
+int snd_hda_codec_get_pin_target(struct hda_codec *codec, hda_nid_t nid)
+{
+ struct hda_pincfg *pin;
+
+ pin = look_up_pincfg(codec, &codec->init_pins, nid);
+ if (!pin)
+ return 0;
+ return pin->target;
+}
+EXPORT_SYMBOL_GPL(snd_hda_codec_get_pin_target);
/**
* snd_hda_shutup_pins - Shut up all pins
@@ -935,8 +1239,9 @@ void snd_hda_shutup_pins(struct hda_codec *codec)
}
codec->pins_shutup = 1;
}
-EXPORT_SYMBOL_HDA(snd_hda_shutup_pins);
+EXPORT_SYMBOL_GPL(snd_hda_shutup_pins);
+#ifdef CONFIG_PM
/* Restore the pin controls cleared previously via snd_hda_shutup_pins() */
static void restore_shutup_pins(struct hda_codec *codec)
{
@@ -953,22 +1258,34 @@ static void restore_shutup_pins(struct hda_codec *codec)
}
codec->pins_shutup = 0;
}
+#endif
+
+static void hda_jackpoll_work(struct work_struct *work)
+{
+ struct hda_codec *codec =
+ container_of(work, struct hda_codec, jackpoll_work.work);
+
+ snd_hda_jack_set_dirty_all(codec);
+ snd_hda_jack_poll_all(codec);
+
+ if (!codec->jackpoll_interval)
+ return;
+
+ queue_delayed_work(codec->bus->workq, &codec->jackpoll_work,
+ codec->jackpoll_interval);
+}
static void init_hda_cache(struct hda_cache_rec *cache,
unsigned int record_size);
static void free_hda_cache(struct hda_cache_rec *cache);
-/* restore the initial pin cfgs and release all pincfg lists */
-static void restore_init_pincfgs(struct hda_codec *codec)
+/* release all pincfg lists */
+static void free_init_pincfgs(struct hda_codec *codec)
{
- /* first free driver_pins and user_pins, then call restore_pincfg
- * so that only the values in init_pins are restored
- */
snd_array_free(&codec->driver_pins);
-#ifdef CONFIG_SND_HDA_HWDEP
+#ifdef CONFIG_SND_HDA_RECONFIG
snd_array_free(&codec->user_pins);
#endif
- restore_pincfgs(codec);
snd_array_free(&codec->init_pins);
}
@@ -1003,23 +1320,45 @@ get_hda_cvt_setup(struct hda_codec *codec, hda_nid_t nid)
}
/*
+ * Dynamic symbol binding for the codec parsers
+ */
+
+#define load_parser(codec, sym) \
+ ((codec)->parser = (int (*)(struct hda_codec *))symbol_request(sym))
+
+static void unload_parser(struct hda_codec *codec)
+{
+ if (codec->parser)
+ symbol_put_addr(codec->parser);
+ codec->parser = NULL;
+}
+
+/*
* codec destructor
*/
static void snd_hda_codec_free(struct hda_codec *codec)
{
if (!codec)
return;
- restore_init_pincfgs(codec);
-#ifdef CONFIG_SND_HDA_POWER_SAVE
+ cancel_delayed_work_sync(&codec->jackpoll_work);
+ snd_hda_jack_tbl_clear(codec);
+ free_init_pincfgs(codec);
+#ifdef CONFIG_PM
cancel_delayed_work(&codec->power_work);
flush_workqueue(codec->bus->workq);
#endif
list_del(&codec->list);
snd_array_free(&codec->mixers);
snd_array_free(&codec->nids);
+ snd_array_free(&codec->cvt_setups);
+ snd_array_free(&codec->spdif_out);
+ remove_conn_list(codec);
codec->bus->caddr_tbl[codec->addr] = NULL;
if (codec->patch_ops.free)
codec->patch_ops.free(codec);
+ hda_call_pm_notify(codec, false); /* cancel leftover refcounts */
+ snd_hda_sysfs_clear(codec);
+ unload_parser(codec);
module_put(codec->owner);
free_hda_cache(&codec->amp_cache);
free_hda_cache(&codec->cmd_cache);
@@ -1027,12 +1366,48 @@ static void snd_hda_codec_free(struct hda_codec *codec)
kfree(codec->chip_name);
kfree(codec->modelname);
kfree(codec->wcaps);
- kfree(codec);
+ codec->bus->num_codecs--;
+ put_device(&codec->dev);
}
-static void hda_set_power_state(struct hda_codec *codec, hda_nid_t fg,
+static bool snd_hda_codec_get_supported_ps(struct hda_codec *codec,
+ hda_nid_t fg, unsigned int power_state);
+
+static unsigned int hda_set_power_state(struct hda_codec *codec,
unsigned int power_state);
+static int snd_hda_codec_dev_register(struct snd_device *device)
+{
+ struct hda_codec *codec = device->device_data;
+ int err = device_add(&codec->dev);
+
+ if (err < 0)
+ return err;
+ snd_hda_register_beep_device(codec);
+ return 0;
+}
+
+static int snd_hda_codec_dev_disconnect(struct snd_device *device)
+{
+ struct hda_codec *codec = device->device_data;
+
+ snd_hda_detach_beep_device(codec);
+ device_del(&codec->dev);
+ return 0;
+}
+
+static int snd_hda_codec_dev_free(struct snd_device *device)
+{
+ snd_hda_codec_free(device->device_data);
+ return 0;
+}
+
+/* just free the container */
+static void snd_hda_codec_dev_release(struct device *dev)
+{
+ kfree(container_of(dev, struct hda_codec, dev));
+}
+
/**
* snd_hda_codec_new - create a HDA codec
* @bus: the bus to assign
@@ -1041,13 +1416,19 @@ static void hda_set_power_state(struct hda_codec *codec, hda_nid_t fg,
*
* Returns 0 if successful, or a negative error code.
*/
-int /*__devinit*/ snd_hda_codec_new(struct hda_bus *bus,
+int snd_hda_codec_new(struct hda_bus *bus,
unsigned int codec_addr,
struct hda_codec **codecp)
{
struct hda_codec *codec;
char component[31];
+ hda_nid_t fg;
int err;
+ static struct snd_device_ops dev_ops = {
+ .dev_register = snd_hda_codec_dev_register,
+ .dev_disconnect = snd_hda_codec_dev_disconnect,
+ .dev_free = snd_hda_codec_dev_free,
+ };
if (snd_BUG_ON(!bus))
return -EINVAL;
@@ -1055,21 +1436,32 @@ int /*__devinit*/ snd_hda_codec_new(struct hda_bus *bus,
return -EINVAL;
if (bus->caddr_tbl[codec_addr]) {
- snd_printk(KERN_ERR "hda_codec: "
- "address 0x%x is already occupied\n", codec_addr);
+ dev_err(bus->card->dev,
+ "address 0x%x is already occupied\n",
+ codec_addr);
return -EBUSY;
}
codec = kzalloc(sizeof(*codec), GFP_KERNEL);
if (codec == NULL) {
- snd_printk(KERN_ERR "can't allocate struct hda_codec\n");
+ dev_err(bus->card->dev, "can't allocate struct hda_codec\n");
return -ENOMEM;
}
+ device_initialize(&codec->dev);
+ codec->dev.parent = &bus->card->card_dev;
+ codec->dev.class = sound_class;
+ codec->dev.release = snd_hda_codec_dev_release;
+ codec->dev.groups = snd_hda_dev_attr_groups;
+ dev_set_name(&codec->dev, "hdaudioC%dD%d", bus->card->number,
+ codec_addr);
+ dev_set_drvdata(&codec->dev, codec); /* for sysfs */
+
codec->bus = bus;
codec->addr = codec_addr;
mutex_init(&codec->spdif_mutex);
mutex_init(&codec->control_mutex);
+ mutex_init(&codec->hash_mutex);
init_hda_cache(&codec->amp_cache, sizeof(struct hda_amp_info));
init_hda_cache(&codec->cmd_cache, sizeof(struct hda_cache_head));
snd_array_init(&codec->mixers, sizeof(struct hda_nid_item), 32);
@@ -1077,15 +1469,16 @@ int /*__devinit*/ snd_hda_codec_new(struct hda_bus *bus,
snd_array_init(&codec->init_pins, sizeof(struct hda_pincfg), 16);
snd_array_init(&codec->driver_pins, sizeof(struct hda_pincfg), 16);
snd_array_init(&codec->cvt_setups, sizeof(struct hda_cvt_setup), 8);
- if (codec->bus->modelname) {
- codec->modelname = kstrdup(codec->bus->modelname, GFP_KERNEL);
- if (!codec->modelname) {
- snd_hda_codec_free(codec);
- return -ENODEV;
- }
- }
+ snd_array_init(&codec->spdif_out, sizeof(struct hda_spdif_out), 16);
+ snd_array_init(&codec->jacktbl, sizeof(struct hda_jack_tbl), 16);
+ snd_array_init(&codec->verbs, sizeof(struct hda_verb *), 8);
+ INIT_LIST_HEAD(&codec->conn_list);
+
+ INIT_DELAYED_WORK(&codec->jackpoll_work, hda_jackpoll_work);
+ codec->depop_delay = -1;
-#ifdef CONFIG_SND_HDA_POWER_SAVE
+#ifdef CONFIG_PM
+ spin_lock_init(&codec->power_lock);
INIT_DELAYED_WORK(&codec->power_work, hda_power_work);
/* snd_hda_codec_new() marks the codec as power-up, and leave it as is.
* the caller has to power down appropriatley after initialization
@@ -1094,7 +1487,19 @@ int /*__devinit*/ snd_hda_codec_new(struct hda_bus *bus,
hda_keep_power_on(codec);
#endif
+ snd_hda_sysfs_init(codec);
+
+ if (codec->bus->modelname) {
+ codec->modelname = kstrdup(codec->bus->modelname, GFP_KERNEL);
+ if (!codec->modelname) {
+ err = -ENODEV;
+ goto error;
+ }
+ }
+
list_add_tail(&codec->list, &bus->codec_list);
+ bus->num_codecs++;
+
bus->caddr_tbl[codec_addr] = codec;
codec->vendor_id = snd_hda_param_read(codec, AC_NODE_ROOT,
@@ -1112,14 +1517,15 @@ int /*__devinit*/ snd_hda_codec_new(struct hda_bus *bus,
setup_fg_nodes(codec);
if (!codec->afg && !codec->mfg) {
- snd_printdd("hda_codec: no AFG or MFG node found\n");
+ dev_err(bus->card->dev, "no AFG or MFG node found\n");
err = -ENODEV;
goto error;
}
- err = read_widget_caps(codec, codec->afg ? codec->afg : codec->mfg);
+ fg = codec->afg ? codec->afg : codec->mfg;
+ err = read_widget_caps(codec, fg);
if (err < 0) {
- snd_printk(KERN_ERR "hda_codec: cannot malloc\n");
+ dev_err(bus->card->dev, "cannot malloc\n");
goto error;
}
err = read_pin_defaults(codec);
@@ -1127,16 +1533,25 @@ int /*__devinit*/ snd_hda_codec_new(struct hda_bus *bus,
goto error;
if (!codec->subsystem_id) {
- hda_nid_t nid = codec->afg ? codec->afg : codec->mfg;
codec->subsystem_id =
- snd_hda_codec_read(codec, nid, 0,
+ snd_hda_codec_read(codec, fg, 0,
AC_VERB_GET_SUBSYSTEM_ID, 0);
}
+#ifdef CONFIG_PM
+ codec->d3_stop_clk = snd_hda_codec_get_supported_ps(codec, fg,
+ AC_PWRST_CLKSTOP);
+#endif
+ codec->epss = snd_hda_codec_get_supported_ps(codec, fg,
+ AC_PWRST_EPSS);
+#ifdef CONFIG_PM
+ if (!codec->d3_stop_clk || !codec->epss)
+ bus->power_keep_link_on = 1;
+#endif
+
+
/* power-up all before initialization */
- hda_set_power_state(codec,
- codec->afg ? codec->afg : codec->mfg,
- AC_PWRST_D0);
+ hda_set_power_state(codec, AC_PWRST_D0);
snd_hda_codec_proc_new(codec);
@@ -1146,6 +1561,10 @@ int /*__devinit*/ snd_hda_codec_new(struct hda_bus *bus,
codec->subsystem_id, codec->revision_id);
snd_component_add(codec->bus->card, component);
+ err = snd_device_new(bus->card, SNDRV_DEV_CODEC, codec, &dev_ops);
+ if (err < 0)
+ goto error;
+
if (codecp)
*codecp = codec;
return 0;
@@ -1154,7 +1573,56 @@ int /*__devinit*/ snd_hda_codec_new(struct hda_bus *bus,
snd_hda_codec_free(codec);
return err;
}
-EXPORT_SYMBOL_HDA(snd_hda_codec_new);
+EXPORT_SYMBOL_GPL(snd_hda_codec_new);
+
+int snd_hda_codec_update_widgets(struct hda_codec *codec)
+{
+ hda_nid_t fg;
+ int err;
+
+ /* Assume the function group node does not change,
+ * only the widget nodes may change.
+ */
+ kfree(codec->wcaps);
+ fg = codec->afg ? codec->afg : codec->mfg;
+ err = read_widget_caps(codec, fg);
+ if (err < 0) {
+ codec_err(codec, "cannot malloc\n");
+ return err;
+ }
+
+ snd_array_free(&codec->init_pins);
+ err = read_pin_defaults(codec);
+
+ return err;
+}
+EXPORT_SYMBOL_GPL(snd_hda_codec_update_widgets);
+
+
+#if IS_ENABLED(CONFIG_SND_HDA_CODEC_HDMI)
+/* if all audio out widgets are digital, let's assume the codec as a HDMI/DP */
+static bool is_likely_hdmi_codec(struct hda_codec *codec)
+{
+ hda_nid_t nid = codec->start_nid;
+ int i;
+
+ for (i = 0; i < codec->num_nodes; i++, nid++) {
+ unsigned int wcaps = get_wcaps(codec, nid);
+ switch (get_wcaps_type(wcaps)) {
+ case AC_WID_AUD_IN:
+ return false; /* HDMI parser supports only HDMI out */
+ case AC_WID_AUD_OUT:
+ if (!(wcaps & AC_WCAP_DIGITAL))
+ return false;
+ break;
+ }
+ }
+ return true;
+}
+#else
+/* no HDMI codec parser support */
+#define is_likely_hdmi_codec(codec) false
+#endif /* CONFIG_SND_HDA_CODEC_HDMI */
/**
* snd_hda_codec_configure - (Re-)configure the HD-audio codec
@@ -1167,6 +1635,7 @@ EXPORT_SYMBOL_HDA(snd_hda_codec_new);
*/
int snd_hda_codec_configure(struct hda_codec *codec)
{
+ int (*patch)(struct hda_codec *) = NULL;
int err;
codec->preset = find_codec_preset(codec);
@@ -1176,60 +1645,58 @@ int snd_hda_codec_configure(struct hda_codec *codec)
return err;
}
- if (is_generic_config(codec)) {
- err = snd_hda_parse_generic_codec(codec);
- goto patched;
- }
- if (codec->preset && codec->preset->patch) {
- err = codec->preset->patch(codec);
- goto patched;
+ if (!is_generic_config(codec) && codec->preset)
+ patch = codec->preset->patch;
+ if (!patch) {
+ unload_parser(codec); /* to be sure */
+ if (is_likely_hdmi_codec(codec)) {
+#if IS_MODULE(CONFIG_SND_HDA_CODEC_HDMI)
+ patch = load_parser(codec, snd_hda_parse_hdmi_codec);
+#elif IS_BUILTIN(CONFIG_SND_HDA_CODEC_HDMI)
+ patch = snd_hda_parse_hdmi_codec;
+#endif
+ }
+ if (!patch) {
+#if IS_MODULE(CONFIG_SND_HDA_GENERIC)
+ patch = load_parser(codec, snd_hda_parse_generic_codec);
+#elif IS_BUILTIN(CONFIG_SND_HDA_GENERIC)
+ patch = snd_hda_parse_generic_codec;
+#endif
+ }
+ if (!patch) {
+ codec_err(codec, "No codec parser is available\n");
+ return -ENODEV;
+ }
}
- /* call the default parser */
- err = snd_hda_parse_generic_codec(codec);
- if (err < 0)
- printk(KERN_ERR "hda-codec: No codec parser is available\n");
+ err = patch(codec);
+ if (err < 0) {
+ unload_parser(codec);
+ return err;
+ }
- patched:
- if (!err && codec->patch_ops.unsol_event)
+ if (codec->patch_ops.unsol_event) {
err = init_unsol_queue(codec->bus);
+ if (err < 0)
+ return err;
+ }
+
/* audio codec should override the mixer name */
- if (!err && (codec->afg || !*codec->bus->card->mixername))
+ if (codec->afg || !*codec->bus->card->mixername)
snprintf(codec->bus->card->mixername,
sizeof(codec->bus->card->mixername),
"%s %s", codec->vendor_name, codec->chip_name);
- return err;
+ return 0;
}
-EXPORT_SYMBOL_HDA(snd_hda_codec_configure);
+EXPORT_SYMBOL_GPL(snd_hda_codec_configure);
-/**
- * snd_hda_codec_setup_stream - set up the codec for streaming
- * @codec: the CODEC to set up
- * @nid: the NID to set up
- * @stream_tag: stream tag to pass, it's between 0x1 and 0xf.
- * @channel_id: channel id to pass, zero based.
- * @format: stream format.
- */
-void snd_hda_codec_setup_stream(struct hda_codec *codec, hda_nid_t nid,
- u32 stream_tag,
- int channel_id, int format)
+/* update the stream-id if changed */
+static void update_pcm_stream_id(struct hda_codec *codec,
+ struct hda_cvt_setup *p, hda_nid_t nid,
+ u32 stream_tag, int channel_id)
{
- struct hda_codec *c;
- struct hda_cvt_setup *p;
unsigned int oldval, newval;
- int type;
- int i;
- if (!nid)
- return;
-
- snd_printdd("hda_codec_setup_stream: "
- "NID=0x%x, stream=0x%x, channel=%d, format=0x%x\n",
- nid, stream_tag, channel_id, format);
- p = get_hda_cvt_setup(codec, nid);
- if (!p)
- return;
- /* update the stream-id if changed */
if (p->stream_tag != stream_tag || p->channel_id != channel_id) {
oldval = snd_hda_codec_read(codec, nid, 0, AC_VERB_GET_CONV, 0);
newval = (stream_tag << 4) | channel_id;
@@ -1240,7 +1707,14 @@ void snd_hda_codec_setup_stream(struct hda_codec *codec, hda_nid_t nid,
p->stream_tag = stream_tag;
p->channel_id = channel_id;
}
- /* update the format-id if changed */
+}
+
+/* update the format-id if changed */
+static void update_pcm_format(struct hda_codec *codec, struct hda_cvt_setup *p,
+ hda_nid_t nid, int format)
+{
+ unsigned int oldval;
+
if (p->format_id != format) {
oldval = snd_hda_codec_read(codec, nid, 0,
AC_VERB_GET_STREAM_FORMAT, 0);
@@ -1252,6 +1726,41 @@ void snd_hda_codec_setup_stream(struct hda_codec *codec, hda_nid_t nid,
}
p->format_id = format;
}
+}
+
+/**
+ * snd_hda_codec_setup_stream - set up the codec for streaming
+ * @codec: the CODEC to set up
+ * @nid: the NID to set up
+ * @stream_tag: stream tag to pass, it's between 0x1 and 0xf.
+ * @channel_id: channel id to pass, zero based.
+ * @format: stream format.
+ */
+void snd_hda_codec_setup_stream(struct hda_codec *codec, hda_nid_t nid,
+ u32 stream_tag,
+ int channel_id, int format)
+{
+ struct hda_codec *c;
+ struct hda_cvt_setup *p;
+ int type;
+ int i;
+
+ if (!nid)
+ return;
+
+ codec_dbg(codec,
+ "hda_codec_setup_stream: NID=0x%x, stream=0x%x, channel=%d, format=0x%x\n",
+ nid, stream_tag, channel_id, format);
+ p = get_hda_cvt_setup(codec, nid);
+ if (!p)
+ return;
+
+ if (codec->pcm_format_first)
+ update_pcm_format(codec, p, nid, format);
+ update_pcm_stream_id(codec, p, nid, stream_tag, channel_id);
+ if (!codec->pcm_format_first)
+ update_pcm_format(codec, p, nid, format);
+
p->active = 1;
p->dirty = 0;
@@ -1261,12 +1770,12 @@ void snd_hda_codec_setup_stream(struct hda_codec *codec, hda_nid_t nid,
for (i = 0; i < c->cvt_setups.used; i++) {
p = snd_array_elem(&c->cvt_setups, i);
if (!p->active && p->stream_tag == stream_tag &&
- get_wcaps_type(get_wcaps(codec, p->nid)) == type)
+ get_wcaps_type(get_wcaps(c, p->nid)) == type)
p->dirty = 1;
}
}
}
-EXPORT_SYMBOL_HDA(snd_hda_codec_setup_stream);
+EXPORT_SYMBOL_GPL(snd_hda_codec_setup_stream);
static void really_cleanup_stream(struct hda_codec *codec,
struct hda_cvt_setup *q);
@@ -1288,7 +1797,7 @@ void __snd_hda_codec_cleanup_stream(struct hda_codec *codec, hda_nid_t nid,
if (codec->no_sticky_stream)
do_now = 1;
- snd_printdd("hda_codec_cleanup_stream: NID=0x%x\n", nid);
+ codec_dbg(codec, "hda_codec_cleanup_stream: NID=0x%x\n", nid);
p = get_hda_cvt_setup(codec, nid);
if (p) {
/* here we just clear the active flag when do_now isn't set;
@@ -1301,14 +1810,17 @@ void __snd_hda_codec_cleanup_stream(struct hda_codec *codec, hda_nid_t nid,
p->active = 0;
}
}
-EXPORT_SYMBOL_HDA(__snd_hda_codec_cleanup_stream);
+EXPORT_SYMBOL_GPL(__snd_hda_codec_cleanup_stream);
static void really_cleanup_stream(struct hda_codec *codec,
struct hda_cvt_setup *q)
{
hda_nid_t nid = q->nid;
- snd_hda_codec_write(codec, nid, 0, AC_VERB_SET_CHANNEL_STREAMID, 0);
- snd_hda_codec_write(codec, nid, 0, AC_VERB_SET_STREAM_FORMAT, 0);
+ if (q->stream_tag || q->channel_id)
+ snd_hda_codec_write(codec, nid, 0, AC_VERB_SET_CHANNEL_STREAMID, 0);
+ if (q->format_id)
+ snd_hda_codec_write(codec, nid, 0, AC_VERB_SET_STREAM_FORMAT, 0
+);
memset(q, 0, sizeof(*q));
q->nid = nid;
}
@@ -1329,6 +1841,7 @@ static void purify_inactive_streams(struct hda_codec *codec)
}
}
+#ifdef CONFIG_PM
/* clean up all streams; called from suspend */
static void hda_cleanup_all_streams(struct hda_codec *codec)
{
@@ -1340,6 +1853,7 @@ static void hda_cleanup_all_streams(struct hda_codec *codec)
really_cleanup_stream(codec, p);
}
}
+#endif
/*
* amp access functions
@@ -1354,7 +1868,7 @@ static void hda_cleanup_all_streams(struct hda_codec *codec)
#define INFO_AMP_VOL(ch) (1 << (1 + (ch)))
/* initialize the hash table */
-static void /*__devinit*/ init_hda_cache(struct hda_cache_rec *cache,
+static void init_hda_cache(struct hda_cache_rec *cache,
unsigned int record_size)
{
memset(cache, 0, sizeof(*cache));
@@ -1397,6 +1911,7 @@ static struct hda_cache_head *get_alloc_hash(struct hda_cache_rec *cache,
cur = snd_array_index(&cache->buf, info);
info->key = key;
info->val = 0;
+ info->dirty = 0;
idx = key % (u16)ARRAY_SIZE(cache->hash);
info->next = cache->hash[idx];
cache->hash[idx] = cur;
@@ -1411,6 +1926,60 @@ get_alloc_amp_hash(struct hda_codec *codec, u32 key)
return (struct hda_amp_info *)get_alloc_hash(&codec->amp_cache, key);
}
+/* overwrite the value with the key in the caps hash */
+static int write_caps_hash(struct hda_codec *codec, u32 key, unsigned int val)
+{
+ struct hda_amp_info *info;
+
+ mutex_lock(&codec->hash_mutex);
+ info = get_alloc_amp_hash(codec, key);
+ if (!info) {
+ mutex_unlock(&codec->hash_mutex);
+ return -EINVAL;
+ }
+ info->amp_caps = val;
+ info->head.val |= INFO_AMP_CAPS;
+ mutex_unlock(&codec->hash_mutex);
+ return 0;
+}
+
+/* query the value from the caps hash; if not found, fetch the current
+ * value from the given function and store in the hash
+ */
+static unsigned int
+query_caps_hash(struct hda_codec *codec, hda_nid_t nid, int dir, u32 key,
+ unsigned int (*func)(struct hda_codec *, hda_nid_t, int))
+{
+ struct hda_amp_info *info;
+ unsigned int val;
+
+ mutex_lock(&codec->hash_mutex);
+ info = get_alloc_amp_hash(codec, key);
+ if (!info) {
+ mutex_unlock(&codec->hash_mutex);
+ return 0;
+ }
+ if (!(info->head.val & INFO_AMP_CAPS)) {
+ mutex_unlock(&codec->hash_mutex); /* for reentrance */
+ val = func(codec, nid, dir);
+ write_caps_hash(codec, key, val);
+ } else {
+ val = info->amp_caps;
+ mutex_unlock(&codec->hash_mutex);
+ }
+ return val;
+}
+
+static unsigned int read_amp_cap(struct hda_codec *codec, hda_nid_t nid,
+ int direction)
+{
+ if (!(get_wcaps(codec, nid) & AC_WCAP_AMP_OVRD))
+ nid = codec->afg;
+ return snd_hda_param_read(codec, nid,
+ direction == HDA_OUTPUT ?
+ AC_PAR_AMP_OUT_CAP : AC_PAR_AMP_IN_CAP);
+}
+
/**
* query_amp_caps - query AMP capabilities
* @codec: the HD-auio codec
@@ -1425,24 +1994,11 @@ get_alloc_amp_hash(struct hda_codec *codec, u32 key)
*/
u32 query_amp_caps(struct hda_codec *codec, hda_nid_t nid, int direction)
{
- struct hda_amp_info *info;
-
- info = get_alloc_amp_hash(codec, HDA_HASH_KEY(nid, direction, 0));
- if (!info)
- return 0;
- if (!(info->head.val & INFO_AMP_CAPS)) {
- if (!(get_wcaps(codec, nid) & AC_WCAP_AMP_OVRD))
- nid = codec->afg;
- info->amp_caps = snd_hda_param_read(codec, nid,
- direction == HDA_OUTPUT ?
- AC_PAR_AMP_OUT_CAP :
- AC_PAR_AMP_IN_CAP);
- if (info->amp_caps)
- info->head.val |= INFO_AMP_CAPS;
- }
- return info->amp_caps;
+ return query_caps_hash(codec, nid, direction,
+ HDA_HASH_KEY(nid, direction, 0),
+ read_amp_cap);
}
-EXPORT_SYMBOL_HDA(query_amp_caps);
+EXPORT_SYMBOL_GPL(query_amp_caps);
/**
* snd_hda_override_amp_caps - Override the AMP capabilities
@@ -1460,34 +2016,12 @@ EXPORT_SYMBOL_HDA(query_amp_caps);
int snd_hda_override_amp_caps(struct hda_codec *codec, hda_nid_t nid, int dir,
unsigned int caps)
{
- struct hda_amp_info *info;
-
- info = get_alloc_amp_hash(codec, HDA_HASH_KEY(nid, dir, 0));
- if (!info)
- return -EINVAL;
- info->amp_caps = caps;
- info->head.val |= INFO_AMP_CAPS;
- return 0;
+ return write_caps_hash(codec, HDA_HASH_KEY(nid, dir, 0), caps);
}
-EXPORT_SYMBOL_HDA(snd_hda_override_amp_caps);
+EXPORT_SYMBOL_GPL(snd_hda_override_amp_caps);
-static unsigned int
-query_caps_hash(struct hda_codec *codec, hda_nid_t nid, u32 key,
- unsigned int (*func)(struct hda_codec *, hda_nid_t))
-{
- struct hda_amp_info *info;
-
- info = get_alloc_amp_hash(codec, key);
- if (!info)
- return 0;
- if (!info->head.val) {
- info->head.val |= INFO_AMP_CAPS;
- info->amp_caps = func(codec, nid);
- }
- return info->amp_caps;
-}
-
-static unsigned int read_pin_cap(struct hda_codec *codec, hda_nid_t nid)
+static unsigned int read_pin_cap(struct hda_codec *codec, hda_nid_t nid,
+ int dir)
{
return snd_hda_param_read(codec, nid, AC_PAR_PIN_CAP);
}
@@ -1505,75 +2039,68 @@ static unsigned int read_pin_cap(struct hda_codec *codec, hda_nid_t nid)
*/
u32 snd_hda_query_pin_caps(struct hda_codec *codec, hda_nid_t nid)
{
- return query_caps_hash(codec, nid, HDA_HASH_PINCAP_KEY(nid),
+ return query_caps_hash(codec, nid, 0, HDA_HASH_PINCAP_KEY(nid),
read_pin_cap);
}
-EXPORT_SYMBOL_HDA(snd_hda_query_pin_caps);
+EXPORT_SYMBOL_GPL(snd_hda_query_pin_caps);
/**
- * snd_hda_pin_sense - execute pin sense measurement
- * @codec: the CODEC to sense
- * @nid: the pin NID to sense
+ * snd_hda_override_pin_caps - Override the pin capabilities
+ * @codec: the CODEC
+ * @nid: the NID to override
+ * @caps: the capability bits to set
*
- * Execute necessary pin sense measurement and return its Presence Detect,
- * Impedance, ELD Valid etc. status bits.
- */
-u32 snd_hda_pin_sense(struct hda_codec *codec, hda_nid_t nid)
-{
- u32 pincap;
-
- if (!codec->no_trigger_sense) {
- pincap = snd_hda_query_pin_caps(codec, nid);
- if (pincap & AC_PINCAP_TRIG_REQ) /* need trigger? */
- snd_hda_codec_read(codec, nid, 0,
- AC_VERB_SET_PIN_SENSE, 0);
- }
- return snd_hda_codec_read(codec, nid, 0,
- AC_VERB_GET_PIN_SENSE, 0);
-}
-EXPORT_SYMBOL_HDA(snd_hda_pin_sense);
-
-/**
- * snd_hda_jack_detect - query pin Presence Detect status
- * @codec: the CODEC to sense
- * @nid: the pin NID to sense
+ * Override the cached PIN capabilitiy bits value by the given one.
*
- * Query and return the pin's Presence Detect status.
+ * Returns zero if successful or a negative error code.
*/
-int snd_hda_jack_detect(struct hda_codec *codec, hda_nid_t nid)
+int snd_hda_override_pin_caps(struct hda_codec *codec, hda_nid_t nid,
+ unsigned int caps)
{
- u32 sense = snd_hda_pin_sense(codec, nid);
- return !!(sense & AC_PINSENSE_PRESENCE);
+ return write_caps_hash(codec, HDA_HASH_PINCAP_KEY(nid), caps);
}
-EXPORT_SYMBOL_HDA(snd_hda_jack_detect);
+EXPORT_SYMBOL_GPL(snd_hda_override_pin_caps);
-/*
- * read the current volume to info
- * if the cache exists, read the cache value.
+/* read or sync the hash value with the current value;
+ * call within hash_mutex
*/
-static unsigned int get_vol_mute(struct hda_codec *codec,
- struct hda_amp_info *info, hda_nid_t nid,
- int ch, int direction, int index)
+static struct hda_amp_info *
+update_amp_hash(struct hda_codec *codec, hda_nid_t nid, int ch,
+ int direction, int index, bool init_only)
{
- u32 val, parm;
-
- if (info->head.val & INFO_AMP_VOL(ch))
- return info->vol[ch];
+ struct hda_amp_info *info;
+ unsigned int parm, val = 0;
+ bool val_read = false;
- parm = ch ? AC_AMP_GET_RIGHT : AC_AMP_GET_LEFT;
- parm |= direction == HDA_OUTPUT ? AC_AMP_GET_OUTPUT : AC_AMP_GET_INPUT;
- parm |= index;
- val = snd_hda_codec_read(codec, nid, 0,
+ retry:
+ info = get_alloc_amp_hash(codec, HDA_HASH_KEY(nid, direction, index));
+ if (!info)
+ return NULL;
+ if (!(info->head.val & INFO_AMP_VOL(ch))) {
+ if (!val_read) {
+ mutex_unlock(&codec->hash_mutex);
+ parm = ch ? AC_AMP_GET_RIGHT : AC_AMP_GET_LEFT;
+ parm |= direction == HDA_OUTPUT ?
+ AC_AMP_GET_OUTPUT : AC_AMP_GET_INPUT;
+ parm |= index;
+ val = snd_hda_codec_read(codec, nid, 0,
AC_VERB_GET_AMP_GAIN_MUTE, parm);
- info->vol[ch] = val & 0xff;
- info->head.val |= INFO_AMP_VOL(ch);
- return info->vol[ch];
+ val &= 0xff;
+ val_read = true;
+ mutex_lock(&codec->hash_mutex);
+ goto retry;
+ }
+ info->vol[ch] = val;
+ info->head.val |= INFO_AMP_VOL(ch);
+ } else if (init_only)
+ return NULL;
+ return info;
}
/*
- * write the current volume in info to the h/w and update the cache
+ * write the current volume in info to the h/w
*/
-static void put_vol_mute(struct hda_codec *codec, struct hda_amp_info *info,
+static void put_vol_mute(struct hda_codec *codec, unsigned int amp_caps,
hda_nid_t nid, int ch, int direction, int index,
int val)
{
@@ -1582,9 +2109,12 @@ static void put_vol_mute(struct hda_codec *codec, struct hda_amp_info *info,
parm = ch ? AC_AMP_SET_RIGHT : AC_AMP_SET_LEFT;
parm |= direction == HDA_OUTPUT ? AC_AMP_SET_OUTPUT : AC_AMP_SET_INPUT;
parm |= index << AC_AMP_SET_INDEX_SHIFT;
- parm |= val;
+ if ((val & HDA_AMP_MUTE) && !(amp_caps & AC_AMPCAP_MUTE) &&
+ (amp_caps & AC_AMPCAP_MIN_MUTE))
+ ; /* set the zero value as a fake mute */
+ else
+ parm |= val;
snd_hda_codec_write(codec, nid, 0, AC_VERB_SET_AMP_GAIN_MUTE, parm);
- info->vol[ch] = val;
}
/**
@@ -1601,12 +2131,48 @@ int snd_hda_codec_amp_read(struct hda_codec *codec, hda_nid_t nid, int ch,
int direction, int index)
{
struct hda_amp_info *info;
- info = get_alloc_amp_hash(codec, HDA_HASH_KEY(nid, direction, index));
- if (!info)
+ unsigned int val = 0;
+
+ mutex_lock(&codec->hash_mutex);
+ info = update_amp_hash(codec, nid, ch, direction, index, false);
+ if (info)
+ val = info->vol[ch];
+ mutex_unlock(&codec->hash_mutex);
+ return val;
+}
+EXPORT_SYMBOL_GPL(snd_hda_codec_amp_read);
+
+static int codec_amp_update(struct hda_codec *codec, hda_nid_t nid, int ch,
+ int direction, int idx, int mask, int val,
+ bool init_only)
+{
+ struct hda_amp_info *info;
+ unsigned int caps;
+ unsigned int cache_only;
+
+ if (snd_BUG_ON(mask & ~0xff))
+ mask &= 0xff;
+ val &= mask;
+
+ mutex_lock(&codec->hash_mutex);
+ info = update_amp_hash(codec, nid, ch, direction, idx, init_only);
+ if (!info) {
+ mutex_unlock(&codec->hash_mutex);
return 0;
- return get_vol_mute(codec, info, nid, ch, direction, index);
+ }
+ val |= info->vol[ch] & ~mask;
+ if (info->vol[ch] == val) {
+ mutex_unlock(&codec->hash_mutex);
+ return 0;
+ }
+ info->vol[ch] = val;
+ cache_only = info->head.dirty = codec->cached_write;
+ caps = info->amp_caps;
+ mutex_unlock(&codec->hash_mutex);
+ if (!cache_only)
+ put_vol_mute(codec, caps, nid, ch, direction, idx, val);
+ return 1;
}
-EXPORT_SYMBOL_HDA(snd_hda_codec_amp_read);
/**
* snd_hda_codec_amp_update - update the AMP value
@@ -1624,21 +2190,9 @@ EXPORT_SYMBOL_HDA(snd_hda_codec_amp_read);
int snd_hda_codec_amp_update(struct hda_codec *codec, hda_nid_t nid, int ch,
int direction, int idx, int mask, int val)
{
- struct hda_amp_info *info;
-
- info = get_alloc_amp_hash(codec, HDA_HASH_KEY(nid, direction, idx));
- if (!info)
- return 0;
- if (snd_BUG_ON(mask & ~0xff))
- mask &= 0xff;
- val &= mask;
- val |= get_vol_mute(codec, info, nid, ch, direction, idx) & ~mask;
- if (info->vol[ch] == val)
- return 0;
- put_vol_mute(codec, info, nid, ch, direction, idx, val);
- return 1;
+ return codec_amp_update(codec, nid, ch, direction, idx, mask, val, false);
}
-EXPORT_SYMBOL_HDA(snd_hda_codec_amp_update);
+EXPORT_SYMBOL_GPL(snd_hda_codec_amp_update);
/**
* snd_hda_codec_amp_stereo - update the AMP stereo values
@@ -1664,9 +2218,33 @@ int snd_hda_codec_amp_stereo(struct hda_codec *codec, hda_nid_t nid,
idx, mask, val);
return ret;
}
-EXPORT_SYMBOL_HDA(snd_hda_codec_amp_stereo);
+EXPORT_SYMBOL_GPL(snd_hda_codec_amp_stereo);
+
+/* Works like snd_hda_codec_amp_update() but it writes the value only at
+ * the first access. If the amp was already initialized / updated beforehand,
+ * this does nothing.
+ */
+int snd_hda_codec_amp_init(struct hda_codec *codec, hda_nid_t nid, int ch,
+ int dir, int idx, int mask, int val)
+{
+ return codec_amp_update(codec, nid, ch, dir, idx, mask, val, true);
+}
+EXPORT_SYMBOL_GPL(snd_hda_codec_amp_init);
+
+int snd_hda_codec_amp_init_stereo(struct hda_codec *codec, hda_nid_t nid,
+ int dir, int idx, int mask, int val)
+{
+ int ch, ret = 0;
+
+ if (snd_BUG_ON(mask & ~0xff))
+ mask &= 0xff;
+ for (ch = 0; ch < 2; ch++)
+ ret |= snd_hda_codec_amp_init(codec, nid, ch, dir,
+ idx, mask, val);
+ return ret;
+}
+EXPORT_SYMBOL_GPL(snd_hda_codec_amp_init_stereo);
-#ifdef SND_HDA_NEEDS_RESUME
/**
* snd_hda_codec_resume_amp - Resume all AMP commands from the cache
* @codec: HD-audio codec
@@ -1675,28 +2253,40 @@ EXPORT_SYMBOL_HDA(snd_hda_codec_amp_stereo);
*/
void snd_hda_codec_resume_amp(struct hda_codec *codec)
{
- struct hda_amp_info *buffer = codec->amp_cache.buf.list;
int i;
- for (i = 0; i < codec->amp_cache.buf.used; i++, buffer++) {
- u32 key = buffer->head.key;
+ mutex_lock(&codec->hash_mutex);
+ codec->cached_write = 0;
+ for (i = 0; i < codec->amp_cache.buf.used; i++) {
+ struct hda_amp_info *buffer;
+ u32 key;
hda_nid_t nid;
unsigned int idx, dir, ch;
+ struct hda_amp_info info;
+
+ buffer = snd_array_elem(&codec->amp_cache.buf, i);
+ if (!buffer->head.dirty)
+ continue;
+ buffer->head.dirty = 0;
+ info = *buffer;
+ key = info.head.key;
if (!key)
continue;
nid = key & 0xff;
idx = (key >> 16) & 0xff;
dir = (key >> 24) & 0xff;
for (ch = 0; ch < 2; ch++) {
- if (!(buffer->head.val & INFO_AMP_VOL(ch)))
+ if (!(info.head.val & INFO_AMP_VOL(ch)))
continue;
- put_vol_mute(codec, buffer, nid, ch, dir, idx,
- buffer->vol[ch]);
+ mutex_unlock(&codec->hash_mutex);
+ put_vol_mute(codec, info.amp_caps, nid, ch, dir, idx,
+ info.vol[ch]);
+ mutex_lock(&codec->hash_mutex);
}
}
+ mutex_unlock(&codec->hash_mutex);
}
-EXPORT_SYMBOL_HDA(snd_hda_codec_resume_amp);
-#endif /* SND_HDA_NEEDS_RESUME */
+EXPORT_SYMBOL_GPL(snd_hda_codec_resume_amp);
static u32 get_amp_max_value(struct hda_codec *codec, hda_nid_t nid, int dir,
unsigned int ofs)
@@ -1729,14 +2319,14 @@ int snd_hda_mixer_amp_volume_info(struct snd_kcontrol *kcontrol,
uinfo->value.integer.min = 0;
uinfo->value.integer.max = get_amp_max_value(codec, nid, dir, ofs);
if (!uinfo->value.integer.max) {
- printk(KERN_WARNING "hda_codec: "
- "num_steps = 0 for NID=0x%x (ctl = %s)\n", nid,
- kcontrol->id.name);
+ codec_warn(codec,
+ "num_steps = 0 for NID=0x%x (ctl = %s)\n",
+ nid, kcontrol->id.name);
return -EINVAL;
}
return 0;
}
-EXPORT_SYMBOL_HDA(snd_hda_mixer_amp_volume_info);
+EXPORT_SYMBOL_GPL(snd_hda_mixer_amp_volume_info);
static inline unsigned int
@@ -1793,7 +2383,7 @@ int snd_hda_mixer_amp_volume_get(struct snd_kcontrol *kcontrol,
*valp = read_amp_value(codec, nid, 1, dir, idx, ofs);
return 0;
}
-EXPORT_SYMBOL_HDA(snd_hda_mixer_amp_volume_get);
+EXPORT_SYMBOL_GPL(snd_hda_mixer_amp_volume_get);
/**
* snd_hda_mixer_amp_volume_put - Put callback for a standard AMP mixer volume
@@ -1823,7 +2413,7 @@ int snd_hda_mixer_amp_volume_put(struct snd_kcontrol *kcontrol,
snd_hda_power_down(codec);
return change;
}
-EXPORT_SYMBOL_HDA(snd_hda_mixer_amp_volume_put);
+EXPORT_SYMBOL_GPL(snd_hda_mixer_amp_volume_put);
/**
* snd_hda_mixer_amp_volume_put - TLV callback for a standard AMP mixer volume
@@ -1849,7 +2439,7 @@ int snd_hda_mixer_amp_tlv(struct snd_kcontrol *kcontrol, int op_flag,
val1 = -((caps & AC_AMPCAP_OFFSET) >> AC_AMPCAP_OFFSET_SHIFT);
val1 += ofs;
val1 = ((int)val1) * ((int)val2);
- if (min_mute)
+ if (min_mute || (caps & AC_AMPCAP_MIN_MUTE))
val2 |= TLV_DB_SCALE_MUTE;
if (put_user(SNDRV_CTL_TLVT_DB_SCALE, _tlv))
return -EFAULT;
@@ -1861,7 +2451,7 @@ int snd_hda_mixer_amp_tlv(struct snd_kcontrol *kcontrol, int op_flag,
return -EFAULT;
return 0;
}
-EXPORT_SYMBOL_HDA(snd_hda_mixer_amp_tlv);
+EXPORT_SYMBOL_GPL(snd_hda_mixer_amp_tlv);
/**
* snd_hda_set_vmaster_tlv - Set TLV for a virtual master control
@@ -1889,16 +2479,16 @@ void snd_hda_set_vmaster_tlv(struct hda_codec *codec, hda_nid_t nid, int dir,
tlv[2] = -nums * step;
tlv[3] = step;
}
-EXPORT_SYMBOL_HDA(snd_hda_set_vmaster_tlv);
+EXPORT_SYMBOL_GPL(snd_hda_set_vmaster_tlv);
/* find a mixer control element with the given name */
static struct snd_kcontrol *
-_snd_hda_find_mixer_ctl(struct hda_codec *codec,
- const char *name, int idx)
+find_mixer_ctl(struct hda_codec *codec, const char *name, int dev, int idx)
{
struct snd_ctl_elem_id id;
memset(&id, 0, sizeof(id));
id.iface = SNDRV_CTL_ELEM_IFACE_MIXER;
+ id.device = dev;
id.index = idx;
if (snd_BUG_ON(strlen(name) >= sizeof(id.name)))
return NULL;
@@ -1916,15 +2506,17 @@ _snd_hda_find_mixer_ctl(struct hda_codec *codec,
struct snd_kcontrol *snd_hda_find_mixer_ctl(struct hda_codec *codec,
const char *name)
{
- return _snd_hda_find_mixer_ctl(codec, name, 0);
+ return find_mixer_ctl(codec, name, 0, 0);
}
-EXPORT_SYMBOL_HDA(snd_hda_find_mixer_ctl);
+EXPORT_SYMBOL_GPL(snd_hda_find_mixer_ctl);
-static int find_empty_mixer_ctl_idx(struct hda_codec *codec, const char *name)
+static int find_empty_mixer_ctl_idx(struct hda_codec *codec, const char *name,
+ int start_idx)
{
- int idx;
- for (idx = 0; idx < 16; idx++) { /* 16 ctlrs should be large enough */
- if (!_snd_hda_find_mixer_ctl(codec, name, idx))
+ int i, idx;
+ /* 16 ctlrs should be large enough */
+ for (i = 0, idx = start_idx; i < 16; i++, idx++) {
+ if (!find_mixer_ctl(codec, name, 0, idx))
return idx;
}
return -EBUSY;
@@ -1976,7 +2568,7 @@ int snd_hda_ctl_add(struct hda_codec *codec, hda_nid_t nid,
item->flags = flags;
return 0;
}
-EXPORT_SYMBOL_HDA(snd_hda_ctl_add);
+EXPORT_SYMBOL_GPL(snd_hda_ctl_add);
/**
* snd_hda_add_nid - Assign a NID to a control element
@@ -2003,11 +2595,11 @@ int snd_hda_add_nid(struct hda_codec *codec, struct snd_kcontrol *kctl,
item->nid = nid;
return 0;
}
- printk(KERN_ERR "hda-codec: no NID for mapping control %s:%d:%d\n",
- kctl->id.name, kctl->id.index, index);
+ codec_err(codec, "no NID for mapping control %s:%d:%d\n",
+ kctl->id.name, kctl->id.index, index);
return -EINVAL;
}
-EXPORT_SYMBOL_HDA(snd_hda_add_nid);
+EXPORT_SYMBOL_GPL(snd_hda_add_nid);
/**
* snd_hda_ctls_clear - Clear all controls assigned to the given codec
@@ -2026,24 +2618,50 @@ void snd_hda_ctls_clear(struct hda_codec *codec)
/* pseudo device locking
* toggle card->shutdown to allow/disallow the device access (as a hack)
*/
-static int hda_lock_devices(struct snd_card *card)
+int snd_hda_lock_devices(struct hda_bus *bus)
{
+ struct snd_card *card = bus->card;
+ struct hda_codec *codec;
+
spin_lock(&card->files_lock);
- if (card->shutdown) {
- spin_unlock(&card->files_lock);
- return -EINVAL;
- }
+ if (card->shutdown)
+ goto err_unlock;
card->shutdown = 1;
+ if (!list_empty(&card->ctl_files))
+ goto err_clear;
+
+ list_for_each_entry(codec, &bus->codec_list, list) {
+ int pcm;
+ for (pcm = 0; pcm < codec->num_pcms; pcm++) {
+ struct hda_pcm *cpcm = &codec->pcm_info[pcm];
+ if (!cpcm->pcm)
+ continue;
+ if (cpcm->pcm->streams[0].substream_opened ||
+ cpcm->pcm->streams[1].substream_opened)
+ goto err_clear;
+ }
+ }
spin_unlock(&card->files_lock);
return 0;
+
+ err_clear:
+ card->shutdown = 0;
+ err_unlock:
+ spin_unlock(&card->files_lock);
+ return -EINVAL;
}
+EXPORT_SYMBOL_GPL(snd_hda_lock_devices);
-static void hda_unlock_devices(struct snd_card *card)
+void snd_hda_unlock_devices(struct hda_bus *bus)
{
+ struct snd_card *card = bus->card;
+
+ card = bus->card;
spin_lock(&card->files_lock);
card->shutdown = 0;
spin_unlock(&card->files_lock);
}
+EXPORT_SYMBOL_GPL(snd_hda_unlock_devices);
/**
* snd_hda_codec_reset - Clear all objects assigned to the codec
@@ -2057,44 +2675,33 @@ static void hda_unlock_devices(struct snd_card *card)
*/
int snd_hda_codec_reset(struct hda_codec *codec)
{
- struct snd_card *card = codec->bus->card;
- int i, pcm;
+ struct hda_bus *bus = codec->bus;
+ struct snd_card *card = bus->card;
+ int i;
- if (hda_lock_devices(card) < 0)
- return -EBUSY;
- /* check whether the codec isn't used by any mixer or PCM streams */
- if (!list_empty(&card->ctl_files)) {
- hda_unlock_devices(card);
+ if (snd_hda_lock_devices(bus) < 0)
return -EBUSY;
- }
- for (pcm = 0; pcm < codec->num_pcms; pcm++) {
- struct hda_pcm *cpcm = &codec->pcm_info[pcm];
- if (!cpcm->pcm)
- continue;
- if (cpcm->pcm->streams[0].substream_opened ||
- cpcm->pcm->streams[1].substream_opened) {
- hda_unlock_devices(card);
- return -EBUSY;
- }
- }
/* OK, let it free */
-
-#ifdef CONFIG_SND_HDA_POWER_SAVE
- cancel_delayed_work(&codec->power_work);
- flush_workqueue(codec->bus->workq);
+ cancel_delayed_work_sync(&codec->jackpoll_work);
+#ifdef CONFIG_PM
+ cancel_delayed_work_sync(&codec->power_work);
+ flush_workqueue(bus->workq);
#endif
snd_hda_ctls_clear(codec);
- /* relase PCMs */
+ /* release PCMs */
for (i = 0; i < codec->num_pcms; i++) {
if (codec->pcm_info[i].pcm) {
snd_device_free(card, codec->pcm_info[i].pcm);
clear_bit(codec->pcm_info[i].device,
- codec->bus->pcm_dev_bits);
+ bus->pcm_dev_bits);
}
}
+ snd_hda_detach_beep_device(codec);
if (codec->patch_ops.free)
codec->patch_ops.free(codec);
+ memset(&codec->patch_ops, 0, sizeof(codec->patch_ops));
+ snd_hda_jack_tbl_clear(codec);
codec->proc_widget_hook = NULL;
codec->spec = NULL;
free_hda_cache(&codec->amp_cache);
@@ -2103,27 +2710,132 @@ int snd_hda_codec_reset(struct hda_codec *codec)
init_hda_cache(&codec->cmd_cache, sizeof(struct hda_cache_head));
/* free only driver_pins so that init_pins + user_pins are restored */
snd_array_free(&codec->driver_pins);
- restore_pincfgs(codec);
+ snd_array_free(&codec->cvt_setups);
+ snd_array_free(&codec->spdif_out);
+ snd_array_free(&codec->verbs);
codec->num_pcms = 0;
codec->pcm_info = NULL;
codec->preset = NULL;
- memset(&codec->patch_ops, 0, sizeof(codec->patch_ops));
codec->slave_dig_outs = NULL;
codec->spdif_status_reset = 0;
+ unload_parser(codec);
module_put(codec->owner);
codec->owner = NULL;
/* allow device access again */
- hda_unlock_devices(card);
+ snd_hda_unlock_devices(bus);
return 0;
}
+typedef int (*map_slave_func_t)(void *, struct snd_kcontrol *);
+
+/* apply the function to all matching slave ctls in the mixer list */
+static int map_slaves(struct hda_codec *codec, const char * const *slaves,
+ const char *suffix, map_slave_func_t func, void *data)
+{
+ struct hda_nid_item *items;
+ const char * const *s;
+ int i, err;
+
+ items = codec->mixers.list;
+ for (i = 0; i < codec->mixers.used; i++) {
+ struct snd_kcontrol *sctl = items[i].kctl;
+ if (!sctl || sctl->id.iface != SNDRV_CTL_ELEM_IFACE_MIXER)
+ continue;
+ for (s = slaves; *s; s++) {
+ char tmpname[sizeof(sctl->id.name)];
+ const char *name = *s;
+ if (suffix) {
+ snprintf(tmpname, sizeof(tmpname), "%s %s",
+ name, suffix);
+ name = tmpname;
+ }
+ if (!strcmp(sctl->id.name, name)) {
+ err = func(data, sctl);
+ if (err)
+ return err;
+ break;
+ }
+ }
+ }
+ return 0;
+}
+
+static int check_slave_present(void *data, struct snd_kcontrol *sctl)
+{
+ return 1;
+}
+
+/* guess the value corresponding to 0dB */
+static int get_kctl_0dB_offset(struct snd_kcontrol *kctl, int *step_to_check)
+{
+ int _tlv[4];
+ const int *tlv = NULL;
+ int val = -1;
+
+ if (kctl->vd[0].access & SNDRV_CTL_ELEM_ACCESS_TLV_CALLBACK) {
+ /* FIXME: set_fs() hack for obtaining user-space TLV data */
+ mm_segment_t fs = get_fs();
+ set_fs(get_ds());
+ if (!kctl->tlv.c(kctl, 0, sizeof(_tlv), _tlv))
+ tlv = _tlv;
+ set_fs(fs);
+ } else if (kctl->vd[0].access & SNDRV_CTL_ELEM_ACCESS_TLV_READ)
+ tlv = kctl->tlv.p;
+ if (tlv && tlv[0] == SNDRV_CTL_TLVT_DB_SCALE) {
+ int step = tlv[3];
+ step &= ~TLV_DB_SCALE_MUTE;
+ if (!step)
+ return -1;
+ if (*step_to_check && *step_to_check != step) {
+ snd_printk(KERN_ERR "hda_codec: Mismatching dB step for vmaster slave (%d!=%d)\n",
+- *step_to_check, step);
+ return -1;
+ }
+ *step_to_check = step;
+ val = -tlv[2] / step;
+ }
+ return val;
+}
+
+/* call kctl->put with the given value(s) */
+static int put_kctl_with_value(struct snd_kcontrol *kctl, int val)
+{
+ struct snd_ctl_elem_value *ucontrol;
+ ucontrol = kzalloc(sizeof(*ucontrol), GFP_KERNEL);
+ if (!ucontrol)
+ return -ENOMEM;
+ ucontrol->value.integer.value[0] = val;
+ ucontrol->value.integer.value[1] = val;
+ kctl->put(kctl, ucontrol);
+ kfree(ucontrol);
+ return 0;
+}
+
+/* initialize the slave volume with 0dB */
+static int init_slave_0dB(void *data, struct snd_kcontrol *slave)
+{
+ int offset = get_kctl_0dB_offset(slave, data);
+ if (offset > 0)
+ put_kctl_with_value(slave, offset);
+ return 0;
+}
+
+/* unmute the slave */
+static int init_slave_unmute(void *data, struct snd_kcontrol *slave)
+{
+ return put_kctl_with_value(slave, 1);
+}
+
/**
* snd_hda_add_vmaster - create a virtual master control and add slaves
* @codec: HD-audio codec
* @name: vmaster control name
* @tlv: TLV data (optional)
* @slaves: slave control names (optional)
+ * @suffix: suffix string to each slave name (optional)
+ * @init_slave_vol: initialize slaves to unmute/0dB
+ * @ctl_ret: store the vmaster kcontrol in return
*
* Create a virtual master control with the given name. The TLV data
* must be either NULL or a valid data.
@@ -2134,17 +2846,20 @@ int snd_hda_codec_reset(struct hda_codec *codec)
*
* This function returns zero if successful or a negative error code.
*/
-int snd_hda_add_vmaster(struct hda_codec *codec, char *name,
- unsigned int *tlv, const char * const *slaves)
+int __snd_hda_add_vmaster(struct hda_codec *codec, char *name,
+ unsigned int *tlv, const char * const *slaves,
+ const char *suffix, bool init_slave_vol,
+ struct snd_kcontrol **ctl_ret)
{
struct snd_kcontrol *kctl;
- const char * const *s;
int err;
- for (s = slaves; *s && !snd_hda_find_mixer_ctl(codec, *s); s++)
- ;
- if (!*s) {
- snd_printdd("No slave found for %s\n", name);
+ if (ctl_ret)
+ *ctl_ret = NULL;
+
+ err = map_slaves(codec, slaves, suffix, check_slave_present, NULL);
+ if (err != 1) {
+ codec_dbg(codec, "No slave found for %s\n", name);
return 0;
}
kctl = snd_ctl_make_virtual_master(name, tlv);
@@ -2154,26 +2869,126 @@ int snd_hda_add_vmaster(struct hda_codec *codec, char *name,
if (err < 0)
return err;
- for (s = slaves; *s; s++) {
- struct snd_kcontrol *sctl;
- int i = 0;
- for (;;) {
- sctl = _snd_hda_find_mixer_ctl(codec, *s, i);
- if (!sctl) {
- if (!i)
- snd_printdd("Cannot find slave %s, "
- "skipped\n", *s);
- break;
- }
- err = snd_ctl_add_slave(kctl, sctl);
- if (err < 0)
- return err;
- i++;
- }
+ err = map_slaves(codec, slaves, suffix,
+ (map_slave_func_t)snd_ctl_add_slave, kctl);
+ if (err < 0)
+ return err;
+
+ /* init with master mute & zero volume */
+ put_kctl_with_value(kctl, 0);
+ if (init_slave_vol) {
+ int step = 0;
+ map_slaves(codec, slaves, suffix,
+ tlv ? init_slave_0dB : init_slave_unmute, &step);
}
+
+ if (ctl_ret)
+ *ctl_ret = kctl;
+ return 0;
+}
+EXPORT_SYMBOL_GPL(__snd_hda_add_vmaster);
+
+/*
+ * mute-LED control using vmaster
+ */
+static int vmaster_mute_mode_info(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_info *uinfo)
+{
+ static const char * const texts[] = {
+ "On", "Off", "Follow Master"
+ };
+ unsigned int index;
+
+ uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED;
+ uinfo->count = 1;
+ uinfo->value.enumerated.items = 3;
+ index = uinfo->value.enumerated.item;
+ if (index >= 3)
+ index = 2;
+ strcpy(uinfo->value.enumerated.name, texts[index]);
return 0;
}
-EXPORT_SYMBOL_HDA(snd_hda_add_vmaster);
+
+static int vmaster_mute_mode_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct hda_vmaster_mute_hook *hook = snd_kcontrol_chip(kcontrol);
+ ucontrol->value.enumerated.item[0] = hook->mute_mode;
+ return 0;
+}
+
+static int vmaster_mute_mode_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct hda_vmaster_mute_hook *hook = snd_kcontrol_chip(kcontrol);
+ unsigned int old_mode = hook->mute_mode;
+
+ hook->mute_mode = ucontrol->value.enumerated.item[0];
+ if (hook->mute_mode > HDA_VMUTE_FOLLOW_MASTER)
+ hook->mute_mode = HDA_VMUTE_FOLLOW_MASTER;
+ if (old_mode == hook->mute_mode)
+ return 0;
+ snd_hda_sync_vmaster_hook(hook);
+ return 1;
+}
+
+static struct snd_kcontrol_new vmaster_mute_mode = {
+ .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+ .name = "Mute-LED Mode",
+ .info = vmaster_mute_mode_info,
+ .get = vmaster_mute_mode_get,
+ .put = vmaster_mute_mode_put,
+};
+
+/*
+ * Add a mute-LED hook with the given vmaster switch kctl
+ * "Mute-LED Mode" control is automatically created and associated with
+ * the given hook.
+ */
+int snd_hda_add_vmaster_hook(struct hda_codec *codec,
+ struct hda_vmaster_mute_hook *hook,
+ bool expose_enum_ctl)
+{
+ struct snd_kcontrol *kctl;
+
+ if (!hook->hook || !hook->sw_kctl)
+ return 0;
+ snd_ctl_add_vmaster_hook(hook->sw_kctl, hook->hook, codec);
+ hook->codec = codec;
+ hook->mute_mode = HDA_VMUTE_FOLLOW_MASTER;
+ if (!expose_enum_ctl)
+ return 0;
+ kctl = snd_ctl_new1(&vmaster_mute_mode, hook);
+ if (!kctl)
+ return -ENOMEM;
+ return snd_hda_ctl_add(codec, 0, kctl);
+}
+EXPORT_SYMBOL_GPL(snd_hda_add_vmaster_hook);
+
+/*
+ * Call the hook with the current value for synchronization
+ * Should be called in init callback
+ */
+void snd_hda_sync_vmaster_hook(struct hda_vmaster_mute_hook *hook)
+{
+ if (!hook->hook || !hook->codec)
+ return;
+ /* don't call vmaster hook in the destructor since it might have
+ * been already destroyed
+ */
+ if (hook->codec->bus->shutdown)
+ return;
+ switch (hook->mute_mode) {
+ case HDA_VMUTE_FOLLOW_MASTER:
+ snd_ctl_sync_vmaster_hook(hook->sw_kctl);
+ break;
+ default:
+ hook->hook(hook->codec, hook->mute_mode);
+ break;
+ }
+}
+EXPORT_SYMBOL_GPL(snd_hda_sync_vmaster_hook);
+
/**
* snd_hda_mixer_amp_switch_info - Info callback for a standard AMP mixer switch
@@ -2192,7 +3007,7 @@ int snd_hda_mixer_amp_switch_info(struct snd_kcontrol *kcontrol,
uinfo->value.integer.max = 1;
return 0;
}
-EXPORT_SYMBOL_HDA(snd_hda_mixer_amp_switch_info);
+EXPORT_SYMBOL_GPL(snd_hda_mixer_amp_switch_info);
/**
* snd_hda_mixer_amp_switch_get - Get callback for a standard AMP mixer switch
@@ -2218,7 +3033,7 @@ int snd_hda_mixer_amp_switch_get(struct snd_kcontrol *kcontrol,
HDA_AMP_MUTE) ? 0 : 1;
return 0;
}
-EXPORT_SYMBOL_HDA(snd_hda_mixer_amp_switch_get);
+EXPORT_SYMBOL_GPL(snd_hda_mixer_amp_switch_get);
/**
* snd_hda_mixer_amp_switch_put - Put callback for a standard AMP mixer switch
@@ -2252,26 +3067,7 @@ int snd_hda_mixer_amp_switch_put(struct snd_kcontrol *kcontrol,
snd_hda_power_down(codec);
return change;
}
-EXPORT_SYMBOL_HDA(snd_hda_mixer_amp_switch_put);
-
-#ifdef CONFIG_SND_HDA_INPUT_BEEP
-/**
- * snd_hda_mixer_amp_switch_put_beep - Put callback for a beep AMP switch
- *
- * This function calls snd_hda_enable_beep_device(), which behaves differently
- * depending on beep_mode option.
- */
-int snd_hda_mixer_amp_switch_put_beep(struct snd_kcontrol *kcontrol,
- struct snd_ctl_elem_value *ucontrol)
-{
- struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
- long *valp = ucontrol->value.integer.value;
-
- snd_hda_enable_beep_device(codec, *valp);
- return snd_hda_mixer_amp_switch_put(kcontrol, ucontrol);
-}
-EXPORT_SYMBOL_HDA(snd_hda_mixer_amp_switch_put_beep);
-#endif /* CONFIG_SND_HDA_INPUT_BEEP */
+EXPORT_SYMBOL_GPL(snd_hda_mixer_amp_switch_put);
/*
* bound volume controls
@@ -2303,7 +3099,7 @@ int snd_hda_mixer_bind_switch_get(struct snd_kcontrol *kcontrol,
mutex_unlock(&codec->control_mutex);
return err;
}
-EXPORT_SYMBOL_HDA(snd_hda_mixer_bind_switch_get);
+EXPORT_SYMBOL_GPL(snd_hda_mixer_bind_switch_get);
/**
* snd_hda_mixer_bind_switch_put - Put callback for a bound volume control
@@ -2333,7 +3129,7 @@ int snd_hda_mixer_bind_switch_put(struct snd_kcontrol *kcontrol,
mutex_unlock(&codec->control_mutex);
return err < 0 ? err : change;
}
-EXPORT_SYMBOL_HDA(snd_hda_mixer_bind_switch_put);
+EXPORT_SYMBOL_GPL(snd_hda_mixer_bind_switch_put);
/**
* snd_hda_mixer_bind_ctls_info - Info callback for a generic bound control
@@ -2356,7 +3152,7 @@ int snd_hda_mixer_bind_ctls_info(struct snd_kcontrol *kcontrol,
mutex_unlock(&codec->control_mutex);
return err;
}
-EXPORT_SYMBOL_HDA(snd_hda_mixer_bind_ctls_info);
+EXPORT_SYMBOL_GPL(snd_hda_mixer_bind_ctls_info);
/**
* snd_hda_mixer_bind_ctls_get - Get callback for a generic bound control
@@ -2379,7 +3175,7 @@ int snd_hda_mixer_bind_ctls_get(struct snd_kcontrol *kcontrol,
mutex_unlock(&codec->control_mutex);
return err;
}
-EXPORT_SYMBOL_HDA(snd_hda_mixer_bind_ctls_get);
+EXPORT_SYMBOL_GPL(snd_hda_mixer_bind_ctls_get);
/**
* snd_hda_mixer_bind_ctls_put - Put callback for a generic bound control
@@ -2408,7 +3204,7 @@ int snd_hda_mixer_bind_ctls_put(struct snd_kcontrol *kcontrol,
mutex_unlock(&codec->control_mutex);
return err < 0 ? err : change;
}
-EXPORT_SYMBOL_HDA(snd_hda_mixer_bind_ctls_put);
+EXPORT_SYMBOL_GPL(snd_hda_mixer_bind_ctls_put);
/**
* snd_hda_mixer_bind_tlv - TLV callback for a generic bound control
@@ -2431,7 +3227,7 @@ int snd_hda_mixer_bind_tlv(struct snd_kcontrol *kcontrol, int op_flag,
mutex_unlock(&codec->control_mutex);
return err;
}
-EXPORT_SYMBOL_HDA(snd_hda_mixer_bind_tlv);
+EXPORT_SYMBOL_GPL(snd_hda_mixer_bind_tlv);
struct hda_ctl_ops snd_hda_bind_vol = {
.info = snd_hda_mixer_amp_volume_info,
@@ -2439,7 +3235,7 @@ struct hda_ctl_ops snd_hda_bind_vol = {
.put = snd_hda_mixer_amp_volume_put,
.tlv = snd_hda_mixer_amp_tlv
};
-EXPORT_SYMBOL_HDA(snd_hda_bind_vol);
+EXPORT_SYMBOL_GPL(snd_hda_bind_vol);
struct hda_ctl_ops snd_hda_bind_sw = {
.info = snd_hda_mixer_amp_switch_info,
@@ -2447,7 +3243,7 @@ struct hda_ctl_ops snd_hda_bind_sw = {
.put = snd_hda_mixer_amp_switch_put,
.tlv = snd_hda_mixer_amp_tlv
};
-EXPORT_SYMBOL_HDA(snd_hda_bind_sw);
+EXPORT_SYMBOL_GPL(snd_hda_bind_sw);
/*
* SPDIF out controls
@@ -2486,11 +3282,16 @@ static int snd_hda_spdif_default_get(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
+ int idx = kcontrol->private_value;
+ struct hda_spdif_out *spdif;
- ucontrol->value.iec958.status[0] = codec->spdif_status & 0xff;
- ucontrol->value.iec958.status[1] = (codec->spdif_status >> 8) & 0xff;
- ucontrol->value.iec958.status[2] = (codec->spdif_status >> 16) & 0xff;
- ucontrol->value.iec958.status[3] = (codec->spdif_status >> 24) & 0xff;
+ mutex_lock(&codec->spdif_mutex);
+ spdif = snd_array_elem(&codec->spdif_out, idx);
+ ucontrol->value.iec958.status[0] = spdif->status & 0xff;
+ ucontrol->value.iec958.status[1] = (spdif->status >> 8) & 0xff;
+ ucontrol->value.iec958.status[2] = (spdif->status >> 16) & 0xff;
+ ucontrol->value.iec958.status[3] = (spdif->status >> 24) & 0xff;
+ mutex_unlock(&codec->spdif_mutex);
return 0;
}
@@ -2534,7 +3335,7 @@ static unsigned int convert_to_spdif_status(unsigned short val)
if (val & AC_DIG1_PROFESSIONAL)
sbits |= IEC958_AES0_PROFESSIONAL;
if (sbits & IEC958_AES0_PROFESSIONAL) {
- if (sbits & AC_DIG1_EMPHASIS)
+ if (val & AC_DIG1_EMPHASIS)
sbits |= IEC958_AES0_PRO_EMPHASIS_5015;
} else {
if (val & AC_DIG1_EMPHASIS)
@@ -2552,7 +3353,7 @@ static unsigned int convert_to_spdif_status(unsigned short val)
static void set_dig_out(struct hda_codec *codec, hda_nid_t nid,
int verb, int val)
{
- hda_nid_t *d;
+ const hda_nid_t *d;
snd_hda_codec_write_cache(codec, nid, 0, verb, val);
d = codec->slave_dig_outs;
@@ -2575,23 +3376,25 @@ static int snd_hda_spdif_default_put(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
- hda_nid_t nid = kcontrol->private_value;
+ int idx = kcontrol->private_value;
+ struct hda_spdif_out *spdif;
+ hda_nid_t nid;
unsigned short val;
int change;
mutex_lock(&codec->spdif_mutex);
- codec->spdif_status = ucontrol->value.iec958.status[0] |
+ spdif = snd_array_elem(&codec->spdif_out, idx);
+ nid = spdif->nid;
+ spdif->status = ucontrol->value.iec958.status[0] |
((unsigned int)ucontrol->value.iec958.status[1] << 8) |
((unsigned int)ucontrol->value.iec958.status[2] << 16) |
((unsigned int)ucontrol->value.iec958.status[3] << 24);
- val = convert_from_spdif_status(codec->spdif_status);
- val |= codec->spdif_ctls & 1;
- change = codec->spdif_ctls != val;
- codec->spdif_ctls = val;
-
- if (change)
+ val = convert_from_spdif_status(spdif->status);
+ val |= spdif->ctls & 1;
+ change = spdif->ctls != val;
+ spdif->ctls = val;
+ if (change && nid != (u16)-1)
set_dig_out_convert(codec, nid, val & 0xff, (val >> 8) & 0xff);
-
mutex_unlock(&codec->spdif_mutex);
return change;
}
@@ -2602,33 +3405,47 @@ static int snd_hda_spdif_out_switch_get(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
+ int idx = kcontrol->private_value;
+ struct hda_spdif_out *spdif;
- ucontrol->value.integer.value[0] = codec->spdif_ctls & AC_DIG1_ENABLE;
+ mutex_lock(&codec->spdif_mutex);
+ spdif = snd_array_elem(&codec->spdif_out, idx);
+ ucontrol->value.integer.value[0] = spdif->ctls & AC_DIG1_ENABLE;
+ mutex_unlock(&codec->spdif_mutex);
return 0;
}
+static inline void set_spdif_ctls(struct hda_codec *codec, hda_nid_t nid,
+ int dig1, int dig2)
+{
+ set_dig_out_convert(codec, nid, dig1, dig2);
+ /* unmute amp switch (if any) */
+ if ((get_wcaps(codec, nid) & AC_WCAP_OUT_AMP) &&
+ (dig1 & AC_DIG1_ENABLE))
+ snd_hda_codec_amp_stereo(codec, nid, HDA_OUTPUT, 0,
+ HDA_AMP_MUTE, 0);
+}
+
static int snd_hda_spdif_out_switch_put(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
- hda_nid_t nid = kcontrol->private_value;
+ int idx = kcontrol->private_value;
+ struct hda_spdif_out *spdif;
+ hda_nid_t nid;
unsigned short val;
int change;
mutex_lock(&codec->spdif_mutex);
- val = codec->spdif_ctls & ~AC_DIG1_ENABLE;
+ spdif = snd_array_elem(&codec->spdif_out, idx);
+ nid = spdif->nid;
+ val = spdif->ctls & ~AC_DIG1_ENABLE;
if (ucontrol->value.integer.value[0])
val |= AC_DIG1_ENABLE;
- change = codec->spdif_ctls != val;
- if (change) {
- codec->spdif_ctls = val;
- set_dig_out_convert(codec, nid, val & 0xff, -1);
- /* unmute amp switch (if any) */
- if ((get_wcaps(codec, nid) & AC_WCAP_OUT_AMP) &&
- (val & AC_DIG1_ENABLE))
- snd_hda_codec_amp_stereo(codec, nid, HDA_OUTPUT, 0,
- HDA_AMP_MUTE, 0);
- }
+ change = spdif->ctls != val;
+ spdif->ctls = val;
+ if (change && nid != (u16)-1)
+ set_spdif_ctls(codec, nid, val & 0xff, -1);
mutex_unlock(&codec->spdif_mutex);
return change;
}
@@ -2666,44 +3483,115 @@ static struct snd_kcontrol_new dig_mixes[] = {
};
/**
- * snd_hda_create_spdif_out_ctls - create Output SPDIF-related controls
+ * snd_hda_create_dig_out_ctls - create Output SPDIF-related controls
* @codec: the HDA codec
- * @nid: audio out widget NID
- *
- * Creates controls related with the SPDIF output.
- * Called from each patch supporting the SPDIF out.
+ * @associated_nid: NID that new ctls associated with
+ * @cvt_nid: converter NID
+ * @type: HDA_PCM_TYPE_*
+ * Creates controls related with the digital output.
+ * Called from each patch supporting the digital out.
*
* Returns 0 if successful, or a negative error code.
*/
-int snd_hda_create_spdif_out_ctls(struct hda_codec *codec, hda_nid_t nid)
+int snd_hda_create_dig_out_ctls(struct hda_codec *codec,
+ hda_nid_t associated_nid,
+ hda_nid_t cvt_nid,
+ int type)
{
int err;
struct snd_kcontrol *kctl;
struct snd_kcontrol_new *dig_mix;
- int idx;
+ int idx = 0;
+ const int spdif_index = 16;
+ struct hda_spdif_out *spdif;
+ struct hda_bus *bus = codec->bus;
+
+ if (bus->primary_dig_out_type == HDA_PCM_TYPE_HDMI &&
+ type == HDA_PCM_TYPE_SPDIF) {
+ idx = spdif_index;
+ } else if (bus->primary_dig_out_type == HDA_PCM_TYPE_SPDIF &&
+ type == HDA_PCM_TYPE_HDMI) {
+ /* suppose a single SPDIF device */
+ for (dig_mix = dig_mixes; dig_mix->name; dig_mix++) {
+ kctl = find_mixer_ctl(codec, dig_mix->name, 0, 0);
+ if (!kctl)
+ break;
+ kctl->id.index = spdif_index;
+ }
+ bus->primary_dig_out_type = HDA_PCM_TYPE_HDMI;
+ }
+ if (!bus->primary_dig_out_type)
+ bus->primary_dig_out_type = type;
- idx = find_empty_mixer_ctl_idx(codec, "IEC958 Playback Switch");
+ idx = find_empty_mixer_ctl_idx(codec, "IEC958 Playback Switch", idx);
if (idx < 0) {
- printk(KERN_ERR "hda_codec: too many IEC958 outputs\n");
+ codec_err(codec, "too many IEC958 outputs\n");
return -EBUSY;
}
+ spdif = snd_array_new(&codec->spdif_out);
+ if (!spdif)
+ return -ENOMEM;
for (dig_mix = dig_mixes; dig_mix->name; dig_mix++) {
kctl = snd_ctl_new1(dig_mix, codec);
if (!kctl)
return -ENOMEM;
kctl->id.index = idx;
- kctl->private_value = nid;
- err = snd_hda_ctl_add(codec, nid, kctl);
+ kctl->private_value = codec->spdif_out.used - 1;
+ err = snd_hda_ctl_add(codec, associated_nid, kctl);
if (err < 0)
return err;
}
- codec->spdif_ctls =
- snd_hda_codec_read(codec, nid, 0,
- AC_VERB_GET_DIGI_CONVERT_1, 0);
- codec->spdif_status = convert_to_spdif_status(codec->spdif_ctls);
+ spdif->nid = cvt_nid;
+ spdif->ctls = snd_hda_codec_read(codec, cvt_nid, 0,
+ AC_VERB_GET_DIGI_CONVERT_1, 0);
+ spdif->status = convert_to_spdif_status(spdif->ctls);
return 0;
}
-EXPORT_SYMBOL_HDA(snd_hda_create_spdif_out_ctls);
+EXPORT_SYMBOL_GPL(snd_hda_create_dig_out_ctls);
+
+/* get the hda_spdif_out entry from the given NID
+ * call within spdif_mutex lock
+ */
+struct hda_spdif_out *snd_hda_spdif_out_of_nid(struct hda_codec *codec,
+ hda_nid_t nid)
+{
+ int i;
+ for (i = 0; i < codec->spdif_out.used; i++) {
+ struct hda_spdif_out *spdif =
+ snd_array_elem(&codec->spdif_out, i);
+ if (spdif->nid == nid)
+ return spdif;
+ }
+ return NULL;
+}
+EXPORT_SYMBOL_GPL(snd_hda_spdif_out_of_nid);
+
+void snd_hda_spdif_ctls_unassign(struct hda_codec *codec, int idx)
+{
+ struct hda_spdif_out *spdif;
+
+ mutex_lock(&codec->spdif_mutex);
+ spdif = snd_array_elem(&codec->spdif_out, idx);
+ spdif->nid = (u16)-1;
+ mutex_unlock(&codec->spdif_mutex);
+}
+EXPORT_SYMBOL_GPL(snd_hda_spdif_ctls_unassign);
+
+void snd_hda_spdif_ctls_assign(struct hda_codec *codec, int idx, hda_nid_t nid)
+{
+ struct hda_spdif_out *spdif;
+ unsigned short val;
+
+ mutex_lock(&codec->spdif_mutex);
+ spdif = snd_array_elem(&codec->spdif_out, idx);
+ if (spdif->nid != nid) {
+ spdif->nid = nid;
+ val = spdif->ctls;
+ set_spdif_ctls(codec, nid, val & 0xff, (val >> 8) & 0xff);
+ }
+ mutex_unlock(&codec->spdif_mutex);
+}
+EXPORT_SYMBOL_GPL(snd_hda_spdif_ctls_assign);
/*
* SPDIF sharing with analog output
@@ -2740,13 +3628,18 @@ static struct snd_kcontrol_new spdif_share_sw = {
int snd_hda_create_spdif_share_sw(struct hda_codec *codec,
struct hda_multi_out *mout)
{
+ struct snd_kcontrol *kctl;
+
if (!mout->dig_out_nid)
return 0;
+
+ kctl = snd_ctl_new1(&spdif_share_sw, mout);
+ if (!kctl)
+ return -ENOMEM;
/* ATTENTION: here mout is passed as private_data, instead of codec */
- return snd_hda_ctl_add(codec, mout->dig_out_nid,
- snd_ctl_new1(&spdif_share_sw, mout));
+ return snd_hda_ctl_add(codec, mout->dig_out_nid, kctl);
}
-EXPORT_SYMBOL_HDA(snd_hda_create_spdif_share_sw);
+EXPORT_SYMBOL_GPL(snd_hda_create_spdif_share_sw);
/*
* SPDIF input
@@ -2834,9 +3727,9 @@ int snd_hda_create_spdif_in_ctls(struct hda_codec *codec, hda_nid_t nid)
struct snd_kcontrol_new *dig_mix;
int idx;
- idx = find_empty_mixer_ctl_idx(codec, "IEC958 Capture Switch");
+ idx = find_empty_mixer_ctl_idx(codec, "IEC958 Capture Switch", 0);
if (idx < 0) {
- printk(KERN_ERR "hda_codec: too many IEC958 inputs\n");
+ codec_err(codec, "too many IEC958 inputs\n");
return -EBUSY;
}
for (dig_mix = dig_in_ctls; dig_mix->name; dig_mix++) {
@@ -2854,14 +3747,13 @@ int snd_hda_create_spdif_in_ctls(struct hda_codec *codec, hda_nid_t nid)
AC_DIG1_ENABLE;
return 0;
}
-EXPORT_SYMBOL_HDA(snd_hda_create_spdif_in_ctls);
+EXPORT_SYMBOL_GPL(snd_hda_create_spdif_in_ctls);
-#ifdef SND_HDA_NEEDS_RESUME
/*
* command cache
*/
-/* build a 32bit cache key with the widget id and the command parameter */
+/* build a 31bit cache key with the widget id and the command parameter */
#define build_cmd_cache_key(nid, verb) ((verb << 8) | nid)
#define get_cmd_cache_nid(key) ((key) & 0xff)
#define get_cmd_cache_cmd(key) (((key) >> 8) & 0xffff)
@@ -2870,7 +3762,7 @@ EXPORT_SYMBOL_HDA(snd_hda_create_spdif_in_ctls);
* snd_hda_codec_write_cache - send a single command with caching
* @codec: the HDA codec
* @nid: NID to send the command
- * @direct: direct flag
+ * @flags: optional bit flags
* @verb: the verb to send
* @parm: the parameter for the verb
*
@@ -2879,32 +3771,40 @@ EXPORT_SYMBOL_HDA(snd_hda_create_spdif_in_ctls);
* Returns 0 if successful, or a negative error code.
*/
int snd_hda_codec_write_cache(struct hda_codec *codec, hda_nid_t nid,
- int direct, unsigned int verb, unsigned int parm)
+ int flags, unsigned int verb, unsigned int parm)
{
- int err = snd_hda_codec_write(codec, nid, direct, verb, parm);
+ int err;
struct hda_cache_head *c;
u32 key;
+ unsigned int cache_only;
+
+ cache_only = codec->cached_write;
+ if (!cache_only) {
+ err = snd_hda_codec_write(codec, nid, flags, verb, parm);
+ if (err < 0)
+ return err;
+ }
- if (err < 0)
- return err;
/* parm may contain the verb stuff for get/set amp */
verb = verb | (parm >> 8);
parm &= 0xff;
key = build_cmd_cache_key(nid, verb);
mutex_lock(&codec->bus->cmd_mutex);
c = get_alloc_hash(&codec->cmd_cache, key);
- if (c)
+ if (c) {
c->val = parm;
+ c->dirty = cache_only;
+ }
mutex_unlock(&codec->bus->cmd_mutex);
return 0;
}
-EXPORT_SYMBOL_HDA(snd_hda_codec_write_cache);
+EXPORT_SYMBOL_GPL(snd_hda_codec_write_cache);
/**
* snd_hda_codec_update_cache - check cache and write the cmd only when needed
* @codec: the HDA codec
* @nid: NID to send the command
- * @direct: direct flag
+ * @flags: optional bit flags
* @verb: the verb to send
* @parm: the parameter for the verb
*
@@ -2915,7 +3815,7 @@ EXPORT_SYMBOL_HDA(snd_hda_codec_write_cache);
* Returns 0 if successful, or a negative error code.
*/
int snd_hda_codec_update_cache(struct hda_codec *codec, hda_nid_t nid,
- int direct, unsigned int verb, unsigned int parm)
+ int flags, unsigned int verb, unsigned int parm)
{
struct hda_cache_head *c;
u32 key;
@@ -2931,9 +3831,9 @@ int snd_hda_codec_update_cache(struct hda_codec *codec, hda_nid_t nid,
return 0;
}
mutex_unlock(&codec->bus->cmd_mutex);
- return snd_hda_codec_write_cache(codec, nid, direct, verb, parm);
+ return snd_hda_codec_write_cache(codec, nid, flags, verb, parm);
}
-EXPORT_SYMBOL_HDA(snd_hda_codec_update_cache);
+EXPORT_SYMBOL_GPL(snd_hda_codec_update_cache);
/**
* snd_hda_codec_resume_cache - Resume the all commands from the cache
@@ -2943,18 +3843,29 @@ EXPORT_SYMBOL_HDA(snd_hda_codec_update_cache);
*/
void snd_hda_codec_resume_cache(struct hda_codec *codec)
{
- struct hda_cache_head *buffer = codec->cmd_cache.buf.list;
int i;
- for (i = 0; i < codec->cmd_cache.buf.used; i++, buffer++) {
- u32 key = buffer->key;
+ mutex_lock(&codec->hash_mutex);
+ codec->cached_write = 0;
+ for (i = 0; i < codec->cmd_cache.buf.used; i++) {
+ struct hda_cache_head *buffer;
+ u32 key;
+
+ buffer = snd_array_elem(&codec->cmd_cache.buf, i);
+ key = buffer->key;
if (!key)
continue;
+ if (!buffer->dirty)
+ continue;
+ buffer->dirty = 0;
+ mutex_unlock(&codec->hash_mutex);
snd_hda_codec_write(codec, get_cmd_cache_nid(key), 0,
get_cmd_cache_cmd(key), buffer->val);
+ mutex_lock(&codec->hash_mutex);
}
+ mutex_unlock(&codec->hash_mutex);
}
-EXPORT_SYMBOL_HDA(snd_hda_codec_resume_cache);
+EXPORT_SYMBOL_GPL(snd_hda_codec_resume_cache);
/**
* snd_hda_sequence_write_cache - sequence writes with caching
@@ -2972,72 +3883,172 @@ void snd_hda_sequence_write_cache(struct hda_codec *codec,
snd_hda_codec_write_cache(codec, seq->nid, 0, seq->verb,
seq->param);
}
-EXPORT_SYMBOL_HDA(snd_hda_sequence_write_cache);
-#endif /* SND_HDA_NEEDS_RESUME */
+EXPORT_SYMBOL_GPL(snd_hda_sequence_write_cache);
+
+/**
+ * snd_hda_codec_flush_cache - Execute all pending (cached) amps / verbs
+ * @codec: HD-audio codec
+ */
+void snd_hda_codec_flush_cache(struct hda_codec *codec)
+{
+ snd_hda_codec_resume_amp(codec);
+ snd_hda_codec_resume_cache(codec);
+}
+EXPORT_SYMBOL_GPL(snd_hda_codec_flush_cache);
+
+void snd_hda_codec_set_power_to_all(struct hda_codec *codec, hda_nid_t fg,
+ unsigned int power_state)
+{
+ hda_nid_t nid = codec->start_nid;
+ int i;
+
+ for (i = 0; i < codec->num_nodes; i++, nid++) {
+ unsigned int wcaps = get_wcaps(codec, nid);
+ unsigned int state = power_state;
+ if (!(wcaps & AC_WCAP_POWER))
+ continue;
+ if (codec->power_filter) {
+ state = codec->power_filter(codec, nid, power_state);
+ if (state != power_state && power_state == AC_PWRST_D3)
+ continue;
+ }
+ snd_hda_codec_write(codec, nid, 0, AC_VERB_SET_POWER_STATE,
+ state);
+ }
+}
+EXPORT_SYMBOL_GPL(snd_hda_codec_set_power_to_all);
/*
- * set power state of the codec
+ * supported power states check
*/
-static void hda_set_power_state(struct hda_codec *codec, hda_nid_t fg,
+static bool snd_hda_codec_get_supported_ps(struct hda_codec *codec, hda_nid_t fg,
unsigned int power_state)
{
- hda_nid_t nid;
- int i;
+ int sup = snd_hda_param_read(codec, fg, AC_PAR_POWER_STATE);
+
+ if (sup == -1)
+ return false;
+ if (sup & power_state)
+ return true;
+ else
+ return false;
+}
+
+/*
+ * wait until the state is reached, returns the current state
+ */
+static unsigned int hda_sync_power_state(struct hda_codec *codec,
+ hda_nid_t fg,
+ unsigned int power_state)
+{
+ unsigned long end_time = jiffies + msecs_to_jiffies(500);
+ unsigned int state, actual_state;
+
+ for (;;) {
+ state = snd_hda_codec_read(codec, fg, 0,
+ AC_VERB_GET_POWER_STATE, 0);
+ if (state & AC_PWRST_ERROR)
+ break;
+ actual_state = (state >> 4) & 0x0f;
+ if (actual_state == power_state)
+ break;
+ if (time_after_eq(jiffies, end_time))
+ break;
+ /* wait until the codec reachs to the target state */
+ msleep(1);
+ }
+ return state;
+}
+
+/* don't power down the widget if it controls eapd and EAPD_BTLENABLE is set */
+unsigned int snd_hda_codec_eapd_power_filter(struct hda_codec *codec,
+ hda_nid_t nid,
+ unsigned int power_state)
+{
+ if (nid == codec->afg || nid == codec->mfg)
+ return power_state;
+ if (power_state == AC_PWRST_D3 &&
+ get_wcaps_type(get_wcaps(codec, nid)) == AC_WID_PIN &&
+ (snd_hda_query_pin_caps(codec, nid) & AC_PINCAP_EAPD)) {
+ int eapd = snd_hda_codec_read(codec, nid, 0,
+ AC_VERB_GET_EAPD_BTLENABLE, 0);
+ if (eapd & 0x02)
+ return AC_PWRST_D0;
+ }
+ return power_state;
+}
+EXPORT_SYMBOL_GPL(snd_hda_codec_eapd_power_filter);
+
+/*
+ * set power state of the codec, and return the power state
+ */
+static unsigned int hda_set_power_state(struct hda_codec *codec,
+ unsigned int power_state)
+{
+ hda_nid_t fg = codec->afg ? codec->afg : codec->mfg;
+ int count;
+ unsigned int state;
+ int flags = 0;
/* this delay seems necessary to avoid click noise at power-down */
- if (power_state == AC_PWRST_D3)
- msleep(100);
- snd_hda_codec_read(codec, fg, 0, AC_VERB_SET_POWER_STATE,
- power_state);
- /* partial workaround for "azx_get_response timeout" */
- if (power_state == AC_PWRST_D0 &&
- (codec->vendor_id & 0xffff0000) == 0x14f10000)
- msleep(10);
+ if (power_state == AC_PWRST_D3) {
+ if (codec->depop_delay < 0)
+ msleep(codec->epss ? 10 : 100);
+ else if (codec->depop_delay > 0)
+ msleep(codec->depop_delay);
+ flags = HDA_RW_NO_RESPONSE_FALLBACK;
+ }
+
+ /* repeat power states setting at most 10 times*/
+ for (count = 0; count < 10; count++) {
+ if (codec->patch_ops.set_power_state)
+ codec->patch_ops.set_power_state(codec, fg,
+ power_state);
+ else {
+ state = power_state;
+ if (codec->power_filter)
+ state = codec->power_filter(codec, fg, state);
+ if (state == power_state || power_state != AC_PWRST_D3)
+ snd_hda_codec_read(codec, fg, flags,
+ AC_VERB_SET_POWER_STATE,
+ state);
+ snd_hda_codec_set_power_to_all(codec, fg, power_state);
+ }
+ state = hda_sync_power_state(codec, fg, power_state);
+ if (!(state & AC_PWRST_ERROR))
+ break;
+ }
+
+ return state;
+}
+
+/* sync power states of all widgets;
+ * this is called at the end of codec parsing
+ */
+static void sync_power_up_states(struct hda_codec *codec)
+{
+ hda_nid_t nid = codec->start_nid;
+ int i;
+
+ /* don't care if no filter is used */
+ if (!codec->power_filter)
+ return;
- nid = codec->start_nid;
for (i = 0; i < codec->num_nodes; i++, nid++) {
unsigned int wcaps = get_wcaps(codec, nid);
- if (wcaps & AC_WCAP_POWER) {
- unsigned int wid_type = get_wcaps_type(wcaps);
- if (power_state == AC_PWRST_D3 &&
- wid_type == AC_WID_PIN) {
- unsigned int pincap;
- /*
- * don't power down the widget if it controls
- * eapd and EAPD_BTLENABLE is set.
- */
- pincap = snd_hda_query_pin_caps(codec, nid);
- if (pincap & AC_PINCAP_EAPD) {
- int eapd = snd_hda_codec_read(codec,
- nid, 0,
- AC_VERB_GET_EAPD_BTLENABLE, 0);
- eapd &= 0x02;
- if (eapd)
- continue;
- }
- }
+ unsigned int target;
+ if (!(wcaps & AC_WCAP_POWER))
+ continue;
+ target = codec->power_filter(codec, nid, AC_PWRST_D0);
+ if (target == AC_PWRST_D0)
+ continue;
+ if (!snd_hda_check_power_state(codec, nid, target))
snd_hda_codec_write(codec, nid, 0,
- AC_VERB_SET_POWER_STATE,
- power_state);
- }
- }
-
- if (power_state == AC_PWRST_D0) {
- unsigned long end_time;
- int state;
- /* wait until the codec reachs to D0 */
- end_time = jiffies + msecs_to_jiffies(500);
- do {
- state = snd_hda_codec_read(codec, fg, 0,
- AC_VERB_GET_POWER_STATE, 0);
- if (state == power_state)
- break;
- msleep(1);
- } while (time_after_eq(end_time, jiffies));
+ AC_VERB_SET_POWER_STATE, target);
}
}
-#ifdef CONFIG_SND_HDA_HWDEP
+#ifdef CONFIG_SND_HDA_RECONFIG
/* execute additional init verbs */
static void hda_exec_init_verbs(struct hda_codec *codec)
{
@@ -3048,25 +4059,49 @@ static void hda_exec_init_verbs(struct hda_codec *codec)
static inline void hda_exec_init_verbs(struct hda_codec *codec) {}
#endif
-#ifdef SND_HDA_NEEDS_RESUME
+#ifdef CONFIG_PM
/*
* call suspend and power-down; used both from PM and power-save
+ * this function returns the power state in the end
*/
-static void hda_call_codec_suspend(struct hda_codec *codec)
+static unsigned int hda_call_codec_suspend(struct hda_codec *codec, bool in_wq)
{
+ unsigned int state;
+
+ codec->in_pm = 1;
+
if (codec->patch_ops.suspend)
- codec->patch_ops.suspend(codec, PMSG_SUSPEND);
+ codec->patch_ops.suspend(codec);
hda_cleanup_all_streams(codec);
- hda_set_power_state(codec,
- codec->afg ? codec->afg : codec->mfg,
- AC_PWRST_D3);
-#ifdef CONFIG_SND_HDA_POWER_SAVE
+ state = hda_set_power_state(codec, AC_PWRST_D3);
+ /* Cancel delayed work if we aren't currently running from it. */
+ if (!in_wq)
+ cancel_delayed_work_sync(&codec->power_work);
+ spin_lock(&codec->power_lock);
snd_hda_update_power_acct(codec);
- cancel_delayed_work(&codec->power_work);
+ trace_hda_power_down(codec);
codec->power_on = 0;
codec->power_transition = 0;
codec->power_jiffies = jiffies;
-#endif
+ spin_unlock(&codec->power_lock);
+ codec->in_pm = 0;
+ return state;
+}
+
+/* mark all entries of cmd and amp caches dirty */
+static void hda_mark_cmd_cache_dirty(struct hda_codec *codec)
+{
+ int i;
+ for (i = 0; i < codec->cmd_cache.buf.used; i++) {
+ struct hda_cache_head *cmd;
+ cmd = snd_array_elem(&codec->cmd_cache.buf, i);
+ cmd->dirty = 1;
+ }
+ for (i = 0; i < codec->amp_cache.buf.used; i++) {
+ struct hda_amp_info *amp;
+ amp = snd_array_elem(&codec->amp_cache.buf, i);
+ amp->head.dirty = 1;
+ }
}
/*
@@ -3074,12 +4109,18 @@ static void hda_call_codec_suspend(struct hda_codec *codec)
*/
static void hda_call_codec_resume(struct hda_codec *codec)
{
- hda_set_power_state(codec,
- codec->afg ? codec->afg : codec->mfg,
- AC_PWRST_D0);
- restore_pincfgs(codec); /* restore all current pin configs */
+ codec->in_pm = 1;
+
+ hda_mark_cmd_cache_dirty(codec);
+
+ /* set as if powered on for avoiding re-entering the resume
+ * in the resume / power-save sequence
+ */
+ hda_keep_power_on(codec);
+ hda_set_power_state(codec, AC_PWRST_D0);
restore_shutup_pins(codec);
hda_exec_init_verbs(codec);
+ snd_hda_jack_set_dirty_all(codec);
if (codec->patch_ops.resume)
codec->patch_ops.resume(codec);
else {
@@ -3088,8 +4129,16 @@ static void hda_call_codec_resume(struct hda_codec *codec)
snd_hda_codec_resume_amp(codec);
snd_hda_codec_resume_cache(codec);
}
+
+ if (codec->jackpoll_interval)
+ hda_jackpoll_work(&codec->jackpoll_work.work);
+ else
+ snd_hda_jack_report_sync(codec);
+
+ codec->in_pm = 0;
+ snd_hda_power_down(codec); /* flag down before returning */
}
-#endif /* SND_HDA_NEEDS_RESUME */
+#endif /* CONFIG_PM */
/**
@@ -3100,26 +4149,71 @@ static void hda_call_codec_resume(struct hda_codec *codec)
*
* Returns 0 if successful, otherwise a negative error code.
*/
-int /*__devinit*/ snd_hda_build_controls(struct hda_bus *bus)
+int snd_hda_build_controls(struct hda_bus *bus)
{
struct hda_codec *codec;
list_for_each_entry(codec, &bus->codec_list, list) {
int err = snd_hda_codec_build_controls(codec);
if (err < 0) {
- printk(KERN_ERR "hda_codec: cannot build controls "
- "for #%d (error %d)\n", codec->addr, err);
+ codec_err(codec,
+ "cannot build controls for #%d (error %d)\n",
+ codec->addr, err);
err = snd_hda_codec_reset(codec);
if (err < 0) {
- printk(KERN_ERR
- "hda_codec: cannot revert codec\n");
+ codec_err(codec,
+ "cannot revert codec\n");
return err;
}
}
}
return 0;
}
-EXPORT_SYMBOL_HDA(snd_hda_build_controls);
+EXPORT_SYMBOL_GPL(snd_hda_build_controls);
+
+/*
+ * add standard channel maps if not specified
+ */
+static int add_std_chmaps(struct hda_codec *codec)
+{
+ int i, str, err;
+
+ for (i = 0; i < codec->num_pcms; i++) {
+ for (str = 0; str < 2; str++) {
+ struct snd_pcm *pcm = codec->pcm_info[i].pcm;
+ struct hda_pcm_stream *hinfo =
+ &codec->pcm_info[i].stream[str];
+ struct snd_pcm_chmap *chmap;
+ const struct snd_pcm_chmap_elem *elem;
+
+ if (codec->pcm_info[i].own_chmap)
+ continue;
+ if (!pcm || !hinfo->substreams)
+ continue;
+ elem = hinfo->chmap ? hinfo->chmap : snd_pcm_std_chmaps;
+ err = snd_pcm_add_chmap_ctls(pcm, str, elem,
+ hinfo->channels_max,
+ 0, &chmap);
+ if (err < 0)
+ return err;
+ chmap->channel_mask = SND_PCM_CHMAP_MASK_2468;
+ }
+ }
+ return 0;
+}
+
+/* default channel maps for 2.1 speakers;
+ * since HD-audio supports only stereo, odd number channels are omitted
+ */
+const struct snd_pcm_chmap_elem snd_pcm_2_1_chmaps[] = {
+ { .channels = 2,
+ .map = { SNDRV_CHMAP_FL, SNDRV_CHMAP_FR } },
+ { .channels = 4,
+ .map = { SNDRV_CHMAP_FL, SNDRV_CHMAP_FR,
+ SNDRV_CHMAP_LFE, SNDRV_CHMAP_LFE } },
+ { }
+};
+EXPORT_SYMBOL_GPL(snd_pcm_2_1_chmaps);
int snd_hda_codec_build_controls(struct hda_codec *codec)
{
@@ -3132,6 +4226,17 @@ int snd_hda_codec_build_controls(struct hda_codec *codec)
err = codec->patch_ops.build_controls(codec);
if (err < 0)
return err;
+
+ /* we create chmaps here instead of build_pcms */
+ err = add_std_chmaps(codec);
+ if (err < 0)
+ return err;
+
+ if (codec->jackpoll_interval)
+ hda_jackpoll_work(&codec->jackpoll_work.work);
+ else
+ snd_hda_jack_report_sync(codec); /* call at the last init point */
+ sync_power_up_states(codec);
return 0;
}
@@ -3228,7 +4333,7 @@ unsigned int snd_hda_calc_stream_format(unsigned int rate,
break;
default:
snd_printdd("invalid format width %d\n",
- snd_pcm_format_width(format));
+ snd_pcm_format_width(format));
return 0;
}
@@ -3237,9 +4342,10 @@ unsigned int snd_hda_calc_stream_format(unsigned int rate,
return val;
}
-EXPORT_SYMBOL_HDA(snd_hda_calc_stream_format);
+EXPORT_SYMBOL_GPL(snd_hda_calc_stream_format);
-static unsigned int get_pcm_param(struct hda_codec *codec, hda_nid_t nid)
+static unsigned int get_pcm_param(struct hda_codec *codec, hda_nid_t nid,
+ int dir)
{
unsigned int val = 0;
if (nid != codec->afg &&
@@ -3254,11 +4360,12 @@ static unsigned int get_pcm_param(struct hda_codec *codec, hda_nid_t nid)
static unsigned int query_pcm_param(struct hda_codec *codec, hda_nid_t nid)
{
- return query_caps_hash(codec, nid, HDA_HASH_PARPCM_KEY(nid),
+ return query_caps_hash(codec, nid, 0, HDA_HASH_PARPCM_KEY(nid),
get_pcm_param);
}
-static unsigned int get_stream_param(struct hda_codec *codec, hda_nid_t nid)
+static unsigned int get_stream_param(struct hda_codec *codec, hda_nid_t nid,
+ int dir)
{
unsigned int streams = snd_hda_param_read(codec, nid, AC_PAR_STREAM);
if (!streams || streams == -1)
@@ -3270,7 +4377,7 @@ static unsigned int get_stream_param(struct hda_codec *codec, hda_nid_t nid)
static unsigned int query_stream_param(struct hda_codec *codec, hda_nid_t nid)
{
- return query_caps_hash(codec, nid, HDA_HASH_PARSTR_KEY(nid),
+ return query_caps_hash(codec, nid, 0, HDA_HASH_PARSTR_KEY(nid),
get_stream_param);
}
@@ -3287,7 +4394,7 @@ static unsigned int query_stream_param(struct hda_codec *codec, hda_nid_t nid)
*
* Returns 0 if successful, otherwise a negative error code.
*/
-static int snd_hda_query_supported_pcm(struct hda_codec *codec, hda_nid_t nid,
+int snd_hda_query_supported_pcm(struct hda_codec *codec, hda_nid_t nid,
u32 *ratesp, u64 *formatsp, unsigned int *bpsp)
{
unsigned int i, val, wcaps;
@@ -3302,10 +4409,10 @@ static int snd_hda_query_supported_pcm(struct hda_codec *codec, hda_nid_t nid,
rates |= rate_bits[i].alsa_bits;
}
if (rates == 0) {
- snd_printk(KERN_ERR "hda_codec: rates == 0 "
- "(nid=0x%x, val=0x%x, ovrd=%i)\n",
- nid, val,
- (wcaps & AC_WCAP_FORMAT_OVRD) ? 1 : 0);
+ codec_err(codec,
+ "rates == 0 (nid=0x%x, val=0x%x, ovrd=%i)\n",
+ nid, val,
+ (wcaps & AC_WCAP_FORMAT_OVRD) ? 1 : 0);
return -EIO;
}
*ratesp = rates;
@@ -3349,11 +4456,13 @@ static int snd_hda_query_supported_pcm(struct hda_codec *codec, hda_nid_t nid,
bps = 20;
}
}
+#if 0 /* FIXME: CS4206 doesn't work, which is the only codec supporting float */
if (streams & AC_SUPFMT_FLOAT32) {
formats |= SNDRV_PCM_FMTBIT_FLOAT_LE;
if (!bps)
bps = 32;
}
+#endif
if (streams == AC_SUPFMT_AC3) {
/* should be exclusive */
/* temporary hack: we have still no proper support
@@ -3363,12 +4472,11 @@ static int snd_hda_query_supported_pcm(struct hda_codec *codec, hda_nid_t nid,
bps = 8;
}
if (formats == 0) {
- snd_printk(KERN_ERR "hda_codec: formats == 0 "
- "(nid=0x%x, val=0x%x, ovrd=%i, "
- "streams=0x%x)\n",
- nid, val,
- (wcaps & AC_WCAP_FORMAT_OVRD) ? 1 : 0,
- streams);
+ codec_err(codec,
+ "formats == 0 (nid=0x%x, val=0x%x, ovrd=%i, streams=0x%x)\n",
+ nid, val,
+ (wcaps & AC_WCAP_FORMAT_OVRD) ? 1 : 0,
+ streams);
return -EIO;
}
if (formatsp)
@@ -3379,6 +4487,7 @@ static int snd_hda_query_supported_pcm(struct hda_codec *codec, hda_nid_t nid,
return 0;
}
+EXPORT_SYMBOL_GPL(snd_hda_query_supported_pcm);
/**
* snd_hda_is_supported_format - Check the validity of the format
@@ -3445,7 +4554,7 @@ int snd_hda_is_supported_format(struct hda_codec *codec, hda_nid_t nid,
return 1;
}
-EXPORT_SYMBOL_HDA(snd_hda_is_supported_format);
+EXPORT_SYMBOL_GPL(snd_hda_is_supported_format);
/*
* PCM stuff
@@ -3523,7 +4632,7 @@ int snd_hda_codec_prepare(struct hda_codec *codec,
mutex_unlock(&codec->bus->prepare_mutex);
return ret;
}
-EXPORT_SYMBOL_HDA(snd_hda_codec_prepare);
+EXPORT_SYMBOL_GPL(snd_hda_codec_prepare);
void snd_hda_codec_cleanup(struct hda_codec *codec,
struct hda_pcm_stream *hinfo,
@@ -3533,7 +4642,7 @@ void snd_hda_codec_cleanup(struct hda_codec *codec,
hinfo->ops.cleanup(hinfo, codec, substream);
mutex_unlock(&codec->bus->prepare_mutex);
}
-EXPORT_SYMBOL_HDA(snd_hda_codec_cleanup);
+EXPORT_SYMBOL_GPL(snd_hda_codec_cleanup);
/* global */
const char *snd_hda_pcm_type_name[HDA_PCM_NTYPES] = {
@@ -3542,12 +4651,13 @@ const char *snd_hda_pcm_type_name[HDA_PCM_NTYPES] = {
/*
* get the empty PCM device number to assign
- *
- * note the max device number is limited by HDA_MAX_PCMS, currently 10
*/
-static int get_empty_pcm_device(struct hda_bus *bus, int type)
+static int get_empty_pcm_device(struct hda_bus *bus, unsigned int type)
{
/* audio device indices; not linear to keep compatibility */
+ /* assigned to static slots up to dev#10; if more needed, assign
+ * the later slot dynamically (when CONFIG_SND_DYNAMIC_MINORS=y)
+ */
static int audio_idx[HDA_PCM_NTYPES][5] = {
[HDA_PCM_TYPE_AUDIO] = { 0, 2, 4, 5, -1 },
[HDA_PCM_TYPE_SPDIF] = { 1, -1 },
@@ -3557,16 +4667,33 @@ static int get_empty_pcm_device(struct hda_bus *bus, int type)
int i;
if (type >= HDA_PCM_NTYPES) {
- snd_printk(KERN_WARNING "Invalid PCM type %d\n", type);
+ dev_err(bus->card->dev, "Invalid PCM type %d\n", type);
return -EINVAL;
}
- for (i = 0; audio_idx[type][i] >= 0 ; i++)
+ for (i = 0; audio_idx[type][i] >= 0; i++) {
+#ifndef CONFIG_SND_DYNAMIC_MINORS
+ if (audio_idx[type][i] >= 8)
+ break;
+#endif
if (!test_and_set_bit(audio_idx[type][i], bus->pcm_dev_bits))
return audio_idx[type][i];
+ }
+
+#ifdef CONFIG_SND_DYNAMIC_MINORS
+ /* non-fixed slots starting from 10 */
+ for (i = 10; i < 32; i++) {
+ if (!test_and_set_bit(i, bus->pcm_dev_bits))
+ return i;
+ }
+#endif
- snd_printk(KERN_WARNING "Too many %s devices\n",
+ dev_warn(bus->card->dev, "Too many %s devices\n",
snd_hda_pcm_type_name[type]);
+#ifndef CONFIG_SND_DYNAMIC_MINORS
+ dev_warn(bus->card->dev,
+ "Consider building the kernel with CONFIG_SND_DYNAMIC_MINORS=y\n");
+#endif
return -EAGAIN;
}
@@ -3603,12 +4730,13 @@ int snd_hda_codec_build_pcms(struct hda_codec *codec)
return 0;
err = codec->patch_ops.build_pcms(codec);
if (err < 0) {
- printk(KERN_ERR "hda_codec: cannot build PCMs"
- "for #%d (error %d)\n", codec->addr, err);
+ codec_err(codec,
+ "cannot build PCMs for #%d (error %d)\n",
+ codec->addr, err);
err = snd_hda_codec_reset(codec);
if (err < 0) {
- printk(KERN_ERR
- "hda_codec: cannot revert codec\n");
+ codec_err(codec,
+ "cannot revert codec\n");
return err;
}
}
@@ -3627,9 +4755,9 @@ int snd_hda_codec_build_pcms(struct hda_codec *codec)
cpcm->device = dev;
err = snd_hda_attach_pcm(codec, cpcm);
if (err < 0) {
- printk(KERN_ERR "hda_codec: cannot attach "
- "PCM stream %d for codec #%d\n",
- dev, codec->addr);
+ codec_err(codec,
+ "cannot attach PCM stream %d for codec #%d\n",
+ dev, codec->addr);
continue; /* no fatal error */
}
}
@@ -3663,7 +4791,7 @@ int snd_hda_codec_build_pcms(struct hda_codec *codec)
*
* This function returns 0 if successful, or a negative error code.
*/
-int __devinit snd_hda_build_pcms(struct hda_bus *bus)
+int snd_hda_build_pcms(struct hda_bus *bus)
{
struct hda_codec *codec;
@@ -3674,7 +4802,7 @@ int __devinit snd_hda_build_pcms(struct hda_bus *bus)
}
return 0;
}
-EXPORT_SYMBOL_HDA(snd_hda_build_pcms);
+EXPORT_SYMBOL_GPL(snd_hda_build_pcms);
/**
* snd_hda_check_board_config - compare the current codec with the config table
@@ -3698,8 +4826,8 @@ int snd_hda_check_board_config(struct hda_codec *codec,
for (i = 0; i < num_configs; i++) {
if (models[i] &&
!strcmp(codec->modelname, models[i])) {
- snd_printd(KERN_INFO "hda_codec: model '%s' is "
- "selected\n", models[i]);
+ codec_info(codec, "model '%s' is selected\n",
+ models[i]);
return i;
}
}
@@ -3721,16 +4849,15 @@ int snd_hda_check_board_config(struct hda_codec *codec,
sprintf(tmp, "#%d", tbl->value);
model = tmp;
}
- snd_printdd(KERN_INFO "hda_codec: model '%s' is selected "
- "for config %x:%x (%s)\n",
- model, tbl->subvendor, tbl->subdevice,
- (tbl->name ? tbl->name : "Unknown device"));
+ codec_info(codec, "model '%s' is selected for config %x:%x (%s)\n",
+ model, tbl->subvendor, tbl->subdevice,
+ (tbl->name ? tbl->name : "Unknown device"));
#endif
return tbl->value;
}
return -1;
}
-EXPORT_SYMBOL_HDA(snd_hda_check_board_config);
+EXPORT_SYMBOL_GPL(snd_hda_check_board_config);
/**
* snd_hda_check_board_codec_sid_config - compare the current codec
@@ -3761,9 +4888,9 @@ int snd_hda_check_board_codec_sid_config(struct hda_codec *codec,
/* Search for codec ID */
for (q = tbl; q->subvendor; q++) {
- unsigned long vendorid = (q->subdevice) | (q->subvendor << 16);
-
- if (vendorid == codec->subsystem_id)
+ unsigned int mask = 0xffff0000 | q->subdevice_mask;
+ unsigned int id = (q->subdevice | (q->subvendor << 16)) & mask;
+ if ((codec->subsystem_id & mask) == id)
break;
}
@@ -3782,16 +4909,15 @@ int snd_hda_check_board_codec_sid_config(struct hda_codec *codec,
sprintf(tmp, "#%d", tbl->value);
model = tmp;
}
- snd_printdd(KERN_INFO "hda_codec: model '%s' is selected "
- "for config %x:%x (%s)\n",
- model, tbl->subvendor, tbl->subdevice,
- (tbl->name ? tbl->name : "Unknown device"));
+ codec_info(codec, "model '%s' is selected for config %x:%x (%s)\n",
+ model, tbl->subvendor, tbl->subdevice,
+ (tbl->name ? tbl->name : "Unknown device"));
#endif
return tbl->value;
}
return -1;
}
-EXPORT_SYMBOL_HDA(snd_hda_check_board_codec_sid_config);
+EXPORT_SYMBOL_GPL(snd_hda_check_board_codec_sid_config);
/**
* snd_hda_add_new_ctls - create controls from the array
@@ -3803,7 +4929,8 @@ EXPORT_SYMBOL_HDA(snd_hda_check_board_codec_sid_config);
*
* Returns 0 if successful, or a negative error code.
*/
-int snd_hda_add_new_ctls(struct hda_codec *codec, struct snd_kcontrol_new *knew)
+int snd_hda_add_new_ctls(struct hda_codec *codec,
+ const struct snd_kcontrol_new *knew)
{
int err;
@@ -3831,7 +4958,7 @@ int snd_hda_add_new_ctls(struct hda_codec *codec, struct snd_kcontrol_new *knew)
addr = codec->addr;
else if (!idx && !knew->index) {
idx = find_empty_mixer_ctl_idx(codec,
- knew->name);
+ knew->name, 0);
if (idx <= 0)
return err;
} else
@@ -3840,33 +4967,41 @@ int snd_hda_add_new_ctls(struct hda_codec *codec, struct snd_kcontrol_new *knew)
}
return 0;
}
-EXPORT_SYMBOL_HDA(snd_hda_add_new_ctls);
-
-#ifdef CONFIG_SND_HDA_POWER_SAVE
-static void hda_set_power_state(struct hda_codec *codec, hda_nid_t fg,
- unsigned int power_state);
+EXPORT_SYMBOL_GPL(snd_hda_add_new_ctls);
+#ifdef CONFIG_PM
static void hda_power_work(struct work_struct *work)
{
struct hda_codec *codec =
container_of(work, struct hda_codec, power_work.work);
struct hda_bus *bus = codec->bus;
+ unsigned int state;
+ spin_lock(&codec->power_lock);
+ if (codec->power_transition > 0) { /* during power-up sequence? */
+ spin_unlock(&codec->power_lock);
+ return;
+ }
if (!codec->power_on || codec->power_count) {
codec->power_transition = 0;
+ spin_unlock(&codec->power_lock);
return;
}
+ spin_unlock(&codec->power_lock);
- hda_call_codec_suspend(codec);
- if (bus->ops.pm_notify)
- bus->ops.pm_notify(bus);
+ state = hda_call_codec_suspend(codec, true);
+ if (!bus->power_keep_link_on && (state & AC_PWRST_CLK_STOP_OK))
+ hda_call_pm_notify(codec, false);
}
static void hda_keep_power_on(struct hda_codec *codec)
{
+ spin_lock(&codec->power_lock);
codec->power_count++;
codec->power_on = 1;
codec->power_jiffies = jiffies;
+ spin_unlock(&codec->power_lock);
+ hda_call_pm_notify(codec, true);
}
/* update the power on/off account with the current jiffies */
@@ -3880,54 +5015,81 @@ void snd_hda_update_power_acct(struct hda_codec *codec)
codec->power_jiffies += delta;
}
-/**
- * snd_hda_power_up - Power-up the codec
- * @codec: HD-audio codec
- *
- * Increment the power-up counter and power up the hardware really when
- * not turned on yet.
- */
-void snd_hda_power_up(struct hda_codec *codec)
+/* Transition to powered up, if wait_power_down then wait for a pending
+ * transition to D3 to complete. A pending D3 transition is indicated
+ * with power_transition == -1. */
+/* call this with codec->power_lock held! */
+static void __snd_hda_power_up(struct hda_codec *codec, bool wait_power_down)
{
- struct hda_bus *bus = codec->bus;
+ /* Return if power_on or transitioning to power_on, unless currently
+ * powering down. */
+ if ((codec->power_on || codec->power_transition > 0) &&
+ !(wait_power_down && codec->power_transition < 0))
+ return;
+ spin_unlock(&codec->power_lock);
- codec->power_count++;
- if (codec->power_on || codec->power_transition)
+ cancel_delayed_work_sync(&codec->power_work);
+
+ spin_lock(&codec->power_lock);
+ /* If the power down delayed work was cancelled above before starting,
+ * then there is no need to go through power up here.
+ */
+ if (codec->power_on) {
+ if (codec->power_transition < 0)
+ codec->power_transition = 0;
return;
+ }
+ trace_hda_power_up(codec);
snd_hda_update_power_acct(codec);
codec->power_on = 1;
codec->power_jiffies = jiffies;
- if (bus->ops.pm_notify)
- bus->ops.pm_notify(bus);
+ codec->power_transition = 1; /* avoid reentrance */
+ spin_unlock(&codec->power_lock);
+
hda_call_codec_resume(codec);
- cancel_delayed_work(&codec->power_work);
+
+ spin_lock(&codec->power_lock);
codec->power_transition = 0;
}
-EXPORT_SYMBOL_HDA(snd_hda_power_up);
#define power_save(codec) \
((codec)->bus->power_save ? *(codec)->bus->power_save : 0)
-/**
- * snd_hda_power_down - Power-down the codec
- * @codec: HD-audio codec
- *
- * Decrement the power-up counter and schedules the power-off work if
- * the counter rearches to zero.
- */
-void snd_hda_power_down(struct hda_codec *codec)
+/* Transition to powered down */
+static void __snd_hda_power_down(struct hda_codec *codec)
{
- --codec->power_count;
if (!codec->power_on || codec->power_count || codec->power_transition)
return;
+
if (power_save(codec)) {
- codec->power_transition = 1; /* avoid reentrance */
+ codec->power_transition = -1; /* avoid reentrance */
queue_delayed_work(codec->bus->workq, &codec->power_work,
msecs_to_jiffies(power_save(codec) * 1000));
}
}
-EXPORT_SYMBOL_HDA(snd_hda_power_down);
+
+/**
+ * snd_hda_power_save - Power-up/down/sync the codec
+ * @codec: HD-audio codec
+ * @delta: the counter delta to change
+ *
+ * Change the power-up counter via @delta, and power up or down the hardware
+ * appropriately. For the power-down, queue to the delayed action.
+ * Passing zero to @delta means to synchronize the power state.
+ */
+void snd_hda_power_save(struct hda_codec *codec, int delta, bool d3wait)
+{
+ spin_lock(&codec->power_lock);
+ codec->power_count += delta;
+ trace_hda_power_count(codec);
+ if (delta > 0)
+ __snd_hda_power_up(codec, d3wait);
+ else
+ __snd_hda_power_down(codec);
+ spin_unlock(&codec->power_lock);
+}
+EXPORT_SYMBOL_GPL(snd_hda_power_save);
/**
* snd_hda_check_amp_list_power - Check the amp list and update the power
@@ -3946,7 +5108,7 @@ int snd_hda_check_amp_list_power(struct hda_codec *codec,
struct hda_loopback_check *check,
hda_nid_t nid)
{
- struct hda_amp_list *p;
+ const struct hda_amp_list *p;
int ch, v;
if (!check->amplist)
@@ -3977,7 +5139,7 @@ int snd_hda_check_amp_list_power(struct hda_codec *codec,
}
return 0;
}
-EXPORT_SYMBOL_HDA(snd_hda_check_amp_list_power);
+EXPORT_SYMBOL_GPL(snd_hda_check_amp_list_power);
#endif
/*
@@ -4001,7 +5163,7 @@ int snd_hda_ch_mode_info(struct hda_codec *codec,
chmode[uinfo->value.enumerated.item].channels);
return 0;
}
-EXPORT_SYMBOL_HDA(snd_hda_ch_mode_info);
+EXPORT_SYMBOL_GPL(snd_hda_ch_mode_info);
/**
* snd_hda_ch_mode_get - Get callback helper for the channel mode enum
@@ -4022,7 +5184,7 @@ int snd_hda_ch_mode_get(struct hda_codec *codec,
}
return 0;
}
-EXPORT_SYMBOL_HDA(snd_hda_ch_mode_get);
+EXPORT_SYMBOL_GPL(snd_hda_ch_mode_get);
/**
* snd_hda_ch_mode_put - Put callback helper for the channel mode enum
@@ -4046,7 +5208,7 @@ int snd_hda_ch_mode_put(struct hda_codec *codec,
snd_hda_sequence_write_cache(codec, chmode[mode].sequence);
return 1;
}
-EXPORT_SYMBOL_HDA(snd_hda_ch_mode_put);
+EXPORT_SYMBOL_GPL(snd_hda_ch_mode_put);
/*
* input MUX helper
@@ -4071,7 +5233,7 @@ int snd_hda_input_mux_info(const struct hda_input_mux *imux,
strcpy(uinfo->value.enumerated.name, imux->items[index].label);
return 0;
}
-EXPORT_SYMBOL_HDA(snd_hda_input_mux_info);
+EXPORT_SYMBOL_GPL(snd_hda_input_mux_info);
/**
* snd_hda_input_mux_info_put - Put callback helper for the input-mux enum
@@ -4096,10 +5258,38 @@ int snd_hda_input_mux_put(struct hda_codec *codec,
*cur_val = idx;
return 1;
}
-EXPORT_SYMBOL_HDA(snd_hda_input_mux_put);
+EXPORT_SYMBOL_GPL(snd_hda_input_mux_put);
/*
+ * process kcontrol info callback of a simple string enum array
+ * when @num_items is 0 or @texts is NULL, assume a boolean enum array
+ */
+int snd_hda_enum_helper_info(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_info *uinfo,
+ int num_items, const char * const *texts)
+{
+ static const char * const texts_default[] = {
+ "Disabled", "Enabled"
+ };
+
+ if (!texts || !num_items) {
+ num_items = 2;
+ texts = texts_default;
+ }
+
+ uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED;
+ uinfo->count = 1;
+ uinfo->value.enumerated.items = num_items;
+ if (uinfo->value.enumerated.item >= uinfo->value.enumerated.items)
+ uinfo->value.enumerated.item = uinfo->value.enumerated.items - 1;
+ strcpy(uinfo->value.enumerated.name,
+ texts[uinfo->value.enumerated.item]);
+ return 0;
+}
+EXPORT_SYMBOL_GPL(snd_hda_enum_helper_info);
+
+/*
* Multi-channel / digital-out PCM helper functions
*/
@@ -4107,29 +5297,41 @@ EXPORT_SYMBOL_HDA(snd_hda_input_mux_put);
static void setup_dig_out_stream(struct hda_codec *codec, hda_nid_t nid,
unsigned int stream_tag, unsigned int format)
{
- /* turn off SPDIF once; otherwise the IEC958 bits won't be updated */
- if (codec->spdif_status_reset && (codec->spdif_ctls & AC_DIG1_ENABLE))
+ struct hda_spdif_out *spdif;
+ unsigned int curr_fmt;
+ bool reset;
+
+ spdif = snd_hda_spdif_out_of_nid(codec, nid);
+ curr_fmt = snd_hda_codec_read(codec, nid, 0,
+ AC_VERB_GET_STREAM_FORMAT, 0);
+ reset = codec->spdif_status_reset &&
+ (spdif->ctls & AC_DIG1_ENABLE) &&
+ curr_fmt != format;
+
+ /* turn off SPDIF if needed; otherwise the IEC958 bits won't be
+ updated */
+ if (reset)
set_dig_out_convert(codec, nid,
- codec->spdif_ctls & ~AC_DIG1_ENABLE & 0xff,
+ spdif->ctls & ~AC_DIG1_ENABLE & 0xff,
-1);
snd_hda_codec_setup_stream(codec, nid, stream_tag, 0, format);
if (codec->slave_dig_outs) {
- hda_nid_t *d;
+ const hda_nid_t *d;
for (d = codec->slave_dig_outs; *d; d++)
snd_hda_codec_setup_stream(codec, *d, stream_tag, 0,
format);
}
/* turn on again (if needed) */
- if (codec->spdif_status_reset && (codec->spdif_ctls & AC_DIG1_ENABLE))
+ if (reset)
set_dig_out_convert(codec, nid,
- codec->spdif_ctls & 0xff, -1);
+ spdif->ctls & 0xff, -1);
}
static void cleanup_dig_out_stream(struct hda_codec *codec, hda_nid_t nid)
{
snd_hda_codec_cleanup_stream(codec, nid);
if (codec->slave_dig_outs) {
- hda_nid_t *d;
+ const hda_nid_t *d;
for (d = codec->slave_dig_outs; *d; d++)
snd_hda_codec_cleanup_stream(codec, *d);
}
@@ -4146,15 +5348,12 @@ void snd_hda_bus_reboot_notify(struct hda_bus *bus)
if (!bus)
return;
list_for_each_entry(codec, &bus->codec_list, list) {
-#ifdef CONFIG_SND_HDA_POWER_SAVE
- if (!codec->power_on)
- continue;
-#endif
- if (codec->patch_ops.reboot_notify)
+ if (hda_codec_is_power_on(codec) &&
+ codec->patch_ops.reboot_notify)
codec->patch_ops.reboot_notify(codec);
}
}
-EXPORT_SYMBOL_HDA(snd_hda_bus_reboot_notify);
+EXPORT_SYMBOL_GPL(snd_hda_bus_reboot_notify);
/**
* snd_hda_multi_out_dig_open - open the digital out in the exclusive mode
@@ -4170,7 +5369,7 @@ int snd_hda_multi_out_dig_open(struct hda_codec *codec,
mutex_unlock(&codec->spdif_mutex);
return 0;
}
-EXPORT_SYMBOL_HDA(snd_hda_multi_out_dig_open);
+EXPORT_SYMBOL_GPL(snd_hda_multi_out_dig_open);
/**
* snd_hda_multi_out_dig_prepare - prepare the digital out stream
@@ -4186,7 +5385,7 @@ int snd_hda_multi_out_dig_prepare(struct hda_codec *codec,
mutex_unlock(&codec->spdif_mutex);
return 0;
}
-EXPORT_SYMBOL_HDA(snd_hda_multi_out_dig_prepare);
+EXPORT_SYMBOL_GPL(snd_hda_multi_out_dig_prepare);
/**
* snd_hda_multi_out_dig_cleanup - clean-up the digital out stream
@@ -4199,7 +5398,7 @@ int snd_hda_multi_out_dig_cleanup(struct hda_codec *codec,
mutex_unlock(&codec->spdif_mutex);
return 0;
}
-EXPORT_SYMBOL_HDA(snd_hda_multi_out_dig_cleanup);
+EXPORT_SYMBOL_GPL(snd_hda_multi_out_dig_cleanup);
/**
* snd_hda_multi_out_dig_close - release the digital out stream
@@ -4212,7 +5411,7 @@ int snd_hda_multi_out_dig_close(struct hda_codec *codec,
mutex_unlock(&codec->spdif_mutex);
return 0;
}
-EXPORT_SYMBOL_HDA(snd_hda_multi_out_dig_close);
+EXPORT_SYMBOL_GPL(snd_hda_multi_out_dig_close);
/**
* snd_hda_multi_out_analog_open - open analog outputs
@@ -4262,7 +5461,7 @@ int snd_hda_multi_out_analog_open(struct hda_codec *codec,
return snd_pcm_hw_constraint_step(substream->runtime, 0,
SNDRV_PCM_HW_PARAM_CHANNELS, 2);
}
-EXPORT_SYMBOL_HDA(snd_hda_multi_out_analog_open);
+EXPORT_SYMBOL_GPL(snd_hda_multi_out_analog_open);
/**
* snd_hda_multi_out_analog_prepare - Preapre the analog outputs.
@@ -4276,17 +5475,19 @@ int snd_hda_multi_out_analog_prepare(struct hda_codec *codec,
unsigned int format,
struct snd_pcm_substream *substream)
{
- hda_nid_t *nids = mout->dac_nids;
+ const hda_nid_t *nids = mout->dac_nids;
int chs = substream->runtime->channels;
+ struct hda_spdif_out *spdif;
int i;
mutex_lock(&codec->spdif_mutex);
+ spdif = snd_hda_spdif_out_of_nid(codec, mout->dig_out_nid);
if (mout->dig_out_nid && mout->share_spdif &&
mout->dig_out_used != HDA_DIG_EXCLUSIVE) {
if (chs == 2 &&
snd_hda_is_supported_format(codec, mout->dig_out_nid,
format) &&
- !(codec->spdif_status & IEC958_AES0_NONAUDIO)) {
+ !(spdif->status & IEC958_AES0_NONAUDIO)) {
mout->dig_out_used = HDA_DIG_ANALOG_DUP;
setup_dig_out_stream(codec, mout->dig_out_nid,
stream_tag, format);
@@ -4306,10 +5507,10 @@ int snd_hda_multi_out_analog_prepare(struct hda_codec *codec,
snd_hda_codec_setup_stream(codec, mout->hp_nid, stream_tag,
0, format);
/* extra outputs copied from front */
- for (i = 0; i < ARRAY_SIZE(mout->extra_out_nid); i++)
- if (!mout->no_share_stream && mout->extra_out_nid[i])
+ for (i = 0; i < ARRAY_SIZE(mout->hp_out_nid); i++)
+ if (!mout->no_share_stream && mout->hp_out_nid[i])
snd_hda_codec_setup_stream(codec,
- mout->extra_out_nid[i],
+ mout->hp_out_nid[i],
stream_tag, 0, format);
/* surrounds */
@@ -4321,9 +5522,23 @@ int snd_hda_multi_out_analog_prepare(struct hda_codec *codec,
snd_hda_codec_setup_stream(codec, nids[i], stream_tag,
0, format);
}
+
+ /* extra surrounds */
+ for (i = 0; i < ARRAY_SIZE(mout->extra_out_nid); i++) {
+ int ch = 0;
+ if (!mout->extra_out_nid[i])
+ break;
+ if (chs >= (i + 1) * 2)
+ ch = i * 2;
+ else if (!mout->no_share_stream)
+ break;
+ snd_hda_codec_setup_stream(codec, mout->extra_out_nid[i],
+ stream_tag, ch, format);
+ }
+
return 0;
}
-EXPORT_SYMBOL_HDA(snd_hda_multi_out_analog_prepare);
+EXPORT_SYMBOL_GPL(snd_hda_multi_out_analog_prepare);
/**
* snd_hda_multi_out_analog_cleanup - clean up the setting for analog out
@@ -4331,13 +5546,17 @@ EXPORT_SYMBOL_HDA(snd_hda_multi_out_analog_prepare);
int snd_hda_multi_out_analog_cleanup(struct hda_codec *codec,
struct hda_multi_out *mout)
{
- hda_nid_t *nids = mout->dac_nids;
+ const hda_nid_t *nids = mout->dac_nids;
int i;
for (i = 0; i < mout->num_dacs; i++)
snd_hda_codec_cleanup_stream(codec, nids[i]);
if (mout->hp_nid)
snd_hda_codec_cleanup_stream(codec, mout->hp_nid);
+ for (i = 0; i < ARRAY_SIZE(mout->hp_out_nid); i++)
+ if (mout->hp_out_nid[i])
+ snd_hda_codec_cleanup_stream(codec,
+ mout->hp_out_nid[i]);
for (i = 0; i < ARRAY_SIZE(mout->extra_out_nid); i++)
if (mout->extra_out_nid[i])
snd_hda_codec_cleanup_stream(codec,
@@ -4350,439 +5569,99 @@ int snd_hda_multi_out_analog_cleanup(struct hda_codec *codec,
mutex_unlock(&codec->spdif_mutex);
return 0;
}
-EXPORT_SYMBOL_HDA(snd_hda_multi_out_analog_cleanup);
-
-/*
- * Helper for automatic pin configuration
- */
-
-static int is_in_nid_list(hda_nid_t nid, hda_nid_t *list)
-{
- for (; *list; list++)
- if (*list == nid)
- return 1;
- return 0;
-}
-
-
-/*
- * Sort an associated group of pins according to their sequence numbers.
- */
-static void sort_pins_by_sequence(hda_nid_t *pins, short *sequences,
- int num_pins)
-{
- int i, j;
- short seq;
- hda_nid_t nid;
-
- for (i = 0; i < num_pins; i++) {
- for (j = i + 1; j < num_pins; j++) {
- if (sequences[i] > sequences[j]) {
- seq = sequences[i];
- sequences[i] = sequences[j];
- sequences[j] = seq;
- nid = pins[i];
- pins[i] = pins[j];
- pins[j] = nid;
- }
- }
- }
-}
-
-
-/* add the found input-pin to the cfg->inputs[] table */
-static void add_auto_cfg_input_pin(struct auto_pin_cfg *cfg, hda_nid_t nid,
- int type)
-{
- if (cfg->num_inputs < AUTO_CFG_MAX_INS) {
- cfg->inputs[cfg->num_inputs].pin = nid;
- cfg->inputs[cfg->num_inputs].type = type;
- cfg->num_inputs++;
- }
-}
-
-/* sort inputs in the order of AUTO_PIN_* type */
-static void sort_autocfg_input_pins(struct auto_pin_cfg *cfg)
-{
- int i, j;
-
- for (i = 0; i < cfg->num_inputs; i++) {
- for (j = i + 1; j < cfg->num_inputs; j++) {
- if (cfg->inputs[i].type > cfg->inputs[j].type) {
- struct auto_pin_cfg_item tmp;
- tmp = cfg->inputs[i];
- cfg->inputs[i] = cfg->inputs[j];
- cfg->inputs[j] = tmp;
- }
- }
- }
-}
-
-/*
- * Parse all pin widgets and store the useful pin nids to cfg
- *
- * The number of line-outs or any primary output is stored in line_outs,
- * and the corresponding output pins are assigned to line_out_pins[],
- * in the order of front, rear, CLFE, side, ...
- *
- * If more extra outputs (speaker and headphone) are found, the pins are
- * assisnged to hp_pins[] and speaker_pins[], respectively. If no line-out jack
- * is detected, one of speaker of HP pins is assigned as the primary
- * output, i.e. to line_out_pins[0]. So, line_outs is always positive
- * if any analog output exists.
- *
- * The analog input pins are assigned to inputs array.
- * The digital input/output pins are assigned to dig_in_pin and dig_out_pin,
- * respectively.
- */
-int snd_hda_parse_pin_def_config(struct hda_codec *codec,
- struct auto_pin_cfg *cfg,
- hda_nid_t *ignore_nids)
-{
- hda_nid_t nid, end_nid;
- short seq, assoc_line_out, assoc_speaker;
- short sequences_line_out[ARRAY_SIZE(cfg->line_out_pins)];
- short sequences_speaker[ARRAY_SIZE(cfg->speaker_pins)];
- short sequences_hp[ARRAY_SIZE(cfg->hp_pins)];
- int i;
-
- memset(cfg, 0, sizeof(*cfg));
-
- memset(sequences_line_out, 0, sizeof(sequences_line_out));
- memset(sequences_speaker, 0, sizeof(sequences_speaker));
- memset(sequences_hp, 0, sizeof(sequences_hp));
- assoc_line_out = assoc_speaker = 0;
-
- end_nid = codec->start_nid + codec->num_nodes;
- for (nid = codec->start_nid; nid < end_nid; nid++) {
- unsigned int wid_caps = get_wcaps(codec, nid);
- unsigned int wid_type = get_wcaps_type(wid_caps);
- unsigned int def_conf;
- short assoc, loc;
-
- /* read all default configuration for pin complex */
- if (wid_type != AC_WID_PIN)
- continue;
- /* ignore the given nids (e.g. pc-beep returns error) */
- if (ignore_nids && is_in_nid_list(nid, ignore_nids))
- continue;
-
- def_conf = snd_hda_codec_get_pincfg(codec, nid);
- if (get_defcfg_connect(def_conf) == AC_JACK_PORT_NONE)
- continue;
- loc = get_defcfg_location(def_conf);
- switch (get_defcfg_device(def_conf)) {
- case AC_JACK_LINE_OUT:
- seq = get_defcfg_sequence(def_conf);
- assoc = get_defcfg_association(def_conf);
-
- if (!(wid_caps & AC_WCAP_STEREO))
- if (!cfg->mono_out_pin)
- cfg->mono_out_pin = nid;
- if (!assoc)
- continue;
- if (!assoc_line_out)
- assoc_line_out = assoc;
- else if (assoc_line_out != assoc)
- continue;
- if (cfg->line_outs >= ARRAY_SIZE(cfg->line_out_pins))
- continue;
- cfg->line_out_pins[cfg->line_outs] = nid;
- sequences_line_out[cfg->line_outs] = seq;
- cfg->line_outs++;
- break;
- case AC_JACK_SPEAKER:
- seq = get_defcfg_sequence(def_conf);
- assoc = get_defcfg_association(def_conf);
- if (!assoc)
- continue;
- if (!assoc_speaker)
- assoc_speaker = assoc;
- else if (assoc_speaker != assoc)
- continue;
- if (cfg->speaker_outs >= ARRAY_SIZE(cfg->speaker_pins))
- continue;
- cfg->speaker_pins[cfg->speaker_outs] = nid;
- sequences_speaker[cfg->speaker_outs] = seq;
- cfg->speaker_outs++;
- break;
- case AC_JACK_HP_OUT:
- seq = get_defcfg_sequence(def_conf);
- assoc = get_defcfg_association(def_conf);
- if (cfg->hp_outs >= ARRAY_SIZE(cfg->hp_pins))
- continue;
- cfg->hp_pins[cfg->hp_outs] = nid;
- sequences_hp[cfg->hp_outs] = (assoc << 4) | seq;
- cfg->hp_outs++;
- break;
- case AC_JACK_MIC_IN:
- add_auto_cfg_input_pin(cfg, nid, AUTO_PIN_MIC);
- break;
- case AC_JACK_LINE_IN:
- add_auto_cfg_input_pin(cfg, nid, AUTO_PIN_LINE_IN);
- break;
- case AC_JACK_CD:
- add_auto_cfg_input_pin(cfg, nid, AUTO_PIN_CD);
- break;
- case AC_JACK_AUX:
- add_auto_cfg_input_pin(cfg, nid, AUTO_PIN_AUX);
- break;
- case AC_JACK_SPDIF_OUT:
- case AC_JACK_DIG_OTHER_OUT:
- if (cfg->dig_outs >= ARRAY_SIZE(cfg->dig_out_pins))
- continue;
- cfg->dig_out_pins[cfg->dig_outs] = nid;
- cfg->dig_out_type[cfg->dig_outs] =
- (loc == AC_JACK_LOC_HDMI) ?
- HDA_PCM_TYPE_HDMI : HDA_PCM_TYPE_SPDIF;
- cfg->dig_outs++;
- break;
- case AC_JACK_SPDIF_IN:
- case AC_JACK_DIG_OTHER_IN:
- cfg->dig_in_pin = nid;
- if (loc == AC_JACK_LOC_HDMI)
- cfg->dig_in_type = HDA_PCM_TYPE_HDMI;
- else
- cfg->dig_in_type = HDA_PCM_TYPE_SPDIF;
- break;
- }
- }
-
- /* FIX-UP:
- * If no line-out is defined but multiple HPs are found,
- * some of them might be the real line-outs.
- */
- if (!cfg->line_outs && cfg->hp_outs > 1) {
- int i = 0;
- while (i < cfg->hp_outs) {
- /* The real HPs should have the sequence 0x0f */
- if ((sequences_hp[i] & 0x0f) == 0x0f) {
- i++;
- continue;
- }
- /* Move it to the line-out table */
- cfg->line_out_pins[cfg->line_outs] = cfg->hp_pins[i];
- sequences_line_out[cfg->line_outs] = sequences_hp[i];
- cfg->line_outs++;
- cfg->hp_outs--;
- memmove(cfg->hp_pins + i, cfg->hp_pins + i + 1,
- sizeof(cfg->hp_pins[0]) * (cfg->hp_outs - i));
- memmove(sequences_hp + i, sequences_hp + i + 1,
- sizeof(sequences_hp[0]) * (cfg->hp_outs - i));
- }
- memset(cfg->hp_pins + cfg->hp_outs, 0,
- sizeof(hda_nid_t) * (AUTO_CFG_MAX_OUTS - cfg->hp_outs));
- if (!cfg->hp_outs)
- cfg->line_out_type = AUTO_PIN_HP_OUT;
-
- }
-
- /* sort by sequence */
- sort_pins_by_sequence(cfg->line_out_pins, sequences_line_out,
- cfg->line_outs);
- sort_pins_by_sequence(cfg->speaker_pins, sequences_speaker,
- cfg->speaker_outs);
- sort_pins_by_sequence(cfg->hp_pins, sequences_hp,
- cfg->hp_outs);
-
- /*
- * FIX-UP: if no line-outs are detected, try to use speaker or HP pin
- * as a primary output
- */
- if (!cfg->line_outs) {
- if (cfg->speaker_outs) {
- cfg->line_outs = cfg->speaker_outs;
- memcpy(cfg->line_out_pins, cfg->speaker_pins,
- sizeof(cfg->speaker_pins));
- cfg->speaker_outs = 0;
- memset(cfg->speaker_pins, 0, sizeof(cfg->speaker_pins));
- cfg->line_out_type = AUTO_PIN_SPEAKER_OUT;
- } else if (cfg->hp_outs) {
- cfg->line_outs = cfg->hp_outs;
- memcpy(cfg->line_out_pins, cfg->hp_pins,
- sizeof(cfg->hp_pins));
- cfg->hp_outs = 0;
- memset(cfg->hp_pins, 0, sizeof(cfg->hp_pins));
- cfg->line_out_type = AUTO_PIN_HP_OUT;
- }
- }
-
- /* Reorder the surround channels
- * ALSA sequence is front/surr/clfe/side
- * HDA sequence is:
- * 4-ch: front/surr => OK as it is
- * 6-ch: front/clfe/surr
- * 8-ch: front/clfe/rear/side|fc
- */
- switch (cfg->line_outs) {
- case 3:
- case 4:
- nid = cfg->line_out_pins[1];
- cfg->line_out_pins[1] = cfg->line_out_pins[2];
- cfg->line_out_pins[2] = nid;
- break;
- }
-
- sort_autocfg_input_pins(cfg);
-
- /*
- * debug prints of the parsed results
- */
- snd_printd("autoconfig: line_outs=%d (0x%x/0x%x/0x%x/0x%x/0x%x)\n",
- cfg->line_outs, cfg->line_out_pins[0], cfg->line_out_pins[1],
- cfg->line_out_pins[2], cfg->line_out_pins[3],
- cfg->line_out_pins[4]);
- snd_printd(" speaker_outs=%d (0x%x/0x%x/0x%x/0x%x/0x%x)\n",
- cfg->speaker_outs, cfg->speaker_pins[0],
- cfg->speaker_pins[1], cfg->speaker_pins[2],
- cfg->speaker_pins[3], cfg->speaker_pins[4]);
- snd_printd(" hp_outs=%d (0x%x/0x%x/0x%x/0x%x/0x%x)\n",
- cfg->hp_outs, cfg->hp_pins[0],
- cfg->hp_pins[1], cfg->hp_pins[2],
- cfg->hp_pins[3], cfg->hp_pins[4]);
- snd_printd(" mono: mono_out=0x%x\n", cfg->mono_out_pin);
- if (cfg->dig_outs)
- snd_printd(" dig-out=0x%x/0x%x\n",
- cfg->dig_out_pins[0], cfg->dig_out_pins[1]);
- snd_printd(" inputs:");
- for (i = 0; i < cfg->num_inputs; i++) {
- snd_printdd(" %s=0x%x",
- hda_get_autocfg_input_label(codec, cfg, i),
- cfg->inputs[i].pin);
- }
- snd_printd("\n");
- if (cfg->dig_in_pin)
- snd_printd(" dig-in=0x%x\n", cfg->dig_in_pin);
-
- return 0;
-}
-EXPORT_SYMBOL_HDA(snd_hda_parse_pin_def_config);
-
-int snd_hda_get_input_pin_attr(unsigned int def_conf)
-{
- unsigned int loc = get_defcfg_location(def_conf);
- unsigned int conn = get_defcfg_connect(def_conf);
- if (conn == AC_JACK_PORT_NONE)
- return INPUT_PIN_ATTR_UNUSED;
- /* Windows may claim the internal mic to be BOTH, too */
- if (conn == AC_JACK_PORT_FIXED || conn == AC_JACK_PORT_BOTH)
- return INPUT_PIN_ATTR_INT;
- if ((loc & 0x30) == AC_JACK_LOC_INTERNAL)
- return INPUT_PIN_ATTR_INT;
- if ((loc & 0x30) == AC_JACK_LOC_SEPARATE)
- return INPUT_PIN_ATTR_DOCK;
- if (loc == AC_JACK_LOC_REAR)
- return INPUT_PIN_ATTR_REAR;
- if (loc == AC_JACK_LOC_FRONT)
- return INPUT_PIN_ATTR_FRONT;
- return INPUT_PIN_ATTR_NORMAL;
-}
-EXPORT_SYMBOL_HDA(snd_hda_get_input_pin_attr);
+EXPORT_SYMBOL_GPL(snd_hda_multi_out_analog_cleanup);
/**
- * hda_get_input_pin_label - Give a label for the given input pin
+ * snd_hda_get_default_vref - Get the default (mic) VREF pin bits
*
- * When check_location is true, the function checks the pin location
- * for mic and line-in pins, and set an appropriate prefix like "Front",
- * "Rear", "Internal".
- */
-
-const char *hda_get_input_pin_label(struct hda_codec *codec, hda_nid_t pin,
- int check_location)
-{
- unsigned int def_conf;
- static const char * const mic_names[] = {
- "Internal Mic", "Dock Mic", "Mic", "Front Mic", "Rear Mic",
+ * Guess the suitable VREF pin bits to be set as the pin-control value.
+ * Note: the function doesn't set the AC_PINCTL_IN_EN bit.
+ */
+unsigned int snd_hda_get_default_vref(struct hda_codec *codec, hda_nid_t pin)
+{
+ unsigned int pincap;
+ unsigned int oldval;
+ oldval = snd_hda_codec_read(codec, pin, 0,
+ AC_VERB_GET_PIN_WIDGET_CONTROL, 0);
+ pincap = snd_hda_query_pin_caps(codec, pin);
+ pincap = (pincap & AC_PINCAP_VREF) >> AC_PINCAP_VREF_SHIFT;
+ /* Exception: if the default pin setup is vref50, we give it priority */
+ if ((pincap & AC_PINCAP_VREF_80) && oldval != PIN_VREF50)
+ return AC_PINCTL_VREF_80;
+ else if (pincap & AC_PINCAP_VREF_50)
+ return AC_PINCTL_VREF_50;
+ else if (pincap & AC_PINCAP_VREF_100)
+ return AC_PINCTL_VREF_100;
+ else if (pincap & AC_PINCAP_VREF_GRD)
+ return AC_PINCTL_VREF_GRD;
+ return AC_PINCTL_VREF_HIZ;
+}
+EXPORT_SYMBOL_GPL(snd_hda_get_default_vref);
+
+/* correct the pin ctl value for matching with the pin cap */
+unsigned int snd_hda_correct_pin_ctl(struct hda_codec *codec,
+ hda_nid_t pin, unsigned int val)
+{
+ static unsigned int cap_lists[][2] = {
+ { AC_PINCTL_VREF_100, AC_PINCAP_VREF_100 },
+ { AC_PINCTL_VREF_80, AC_PINCAP_VREF_80 },
+ { AC_PINCTL_VREF_50, AC_PINCAP_VREF_50 },
+ { AC_PINCTL_VREF_GRD, AC_PINCAP_VREF_GRD },
};
- int attr;
-
- def_conf = snd_hda_codec_get_pincfg(codec, pin);
-
- switch (get_defcfg_device(def_conf)) {
- case AC_JACK_MIC_IN:
- if (!check_location)
- return "Mic";
- attr = snd_hda_get_input_pin_attr(def_conf);
- if (!attr)
- return "None";
- return mic_names[attr - 1];
- case AC_JACK_LINE_IN:
- if (!check_location)
- return "Line";
- attr = snd_hda_get_input_pin_attr(def_conf);
- if (!attr)
- return "None";
- if (attr == INPUT_PIN_ATTR_DOCK)
- return "Dock Line";
- return "Line";
- case AC_JACK_AUX:
- return "Aux";
- case AC_JACK_CD:
- return "CD";
- case AC_JACK_SPDIF_IN:
- return "SPDIF In";
- case AC_JACK_DIG_OTHER_IN:
- return "Digital In";
- default:
- return "Misc";
- }
-}
-EXPORT_SYMBOL_HDA(hda_get_input_pin_label);
-
-/* Check whether the location prefix needs to be added to the label.
- * If all mic-jacks are in the same location (e.g. rear panel), we don't
- * have to put "Front" prefix to each label. In such a case, returns false.
- */
-static int check_mic_location_need(struct hda_codec *codec,
- const struct auto_pin_cfg *cfg,
- int input)
-{
- unsigned int defc;
- int i, attr, attr2;
+ unsigned int cap;
- defc = snd_hda_codec_get_pincfg(codec, cfg->inputs[input].pin);
- attr = snd_hda_get_input_pin_attr(defc);
- /* for internal or docking mics, we need locations */
- if (attr <= INPUT_PIN_ATTR_NORMAL)
- return 1;
-
- attr = 0;
- for (i = 0; i < cfg->num_inputs; i++) {
- defc = snd_hda_codec_get_pincfg(codec, cfg->inputs[i].pin);
- attr2 = snd_hda_get_input_pin_attr(defc);
- if (attr2 >= INPUT_PIN_ATTR_NORMAL) {
- if (attr && attr != attr2)
- return 1; /* different locations found */
- attr = attr2;
+ if (!val)
+ return 0;
+ cap = snd_hda_query_pin_caps(codec, pin);
+ if (!cap)
+ return val; /* don't know what to do... */
+
+ if (val & AC_PINCTL_OUT_EN) {
+ if (!(cap & AC_PINCAP_OUT))
+ val &= ~(AC_PINCTL_OUT_EN | AC_PINCTL_HP_EN);
+ else if ((val & AC_PINCTL_HP_EN) && !(cap & AC_PINCAP_HP_DRV))
+ val &= ~AC_PINCTL_HP_EN;
+ }
+
+ if (val & AC_PINCTL_IN_EN) {
+ if (!(cap & AC_PINCAP_IN))
+ val &= ~(AC_PINCTL_IN_EN | AC_PINCTL_VREFEN);
+ else {
+ unsigned int vcap, vref;
+ int i;
+ vcap = (cap & AC_PINCAP_VREF) >> AC_PINCAP_VREF_SHIFT;
+ vref = val & AC_PINCTL_VREFEN;
+ for (i = 0; i < ARRAY_SIZE(cap_lists); i++) {
+ if (vref == cap_lists[i][0] &&
+ !(vcap & cap_lists[i][1])) {
+ if (i == ARRAY_SIZE(cap_lists) - 1)
+ vref = AC_PINCTL_VREF_HIZ;
+ else
+ vref = cap_lists[i + 1][0];
+ }
+ }
+ val &= ~AC_PINCTL_VREFEN;
+ val |= vref;
}
}
- return 0;
+
+ return val;
}
+EXPORT_SYMBOL_GPL(snd_hda_correct_pin_ctl);
-/**
- * hda_get_autocfg_input_label - Get a label for the given input
- *
- * Get a label for the given input pin defined by the autocfg item.
- * Unlike hda_get_input_pin_label(), this function checks all inputs
- * defined in autocfg and avoids the redundant mic/line prefix as much as
- * possible.
- */
-const char *hda_get_autocfg_input_label(struct hda_codec *codec,
- const struct auto_pin_cfg *cfg,
- int input)
+int _snd_hda_set_pin_ctl(struct hda_codec *codec, hda_nid_t pin,
+ unsigned int val, bool cached)
{
- int type = cfg->inputs[input].type;
- int has_multiple_pins = 0;
-
- if ((input > 0 && cfg->inputs[input - 1].type == type) ||
- (input < cfg->num_inputs - 1 && cfg->inputs[input + 1].type == type))
- has_multiple_pins = 1;
- if (has_multiple_pins && type == AUTO_PIN_MIC)
- has_multiple_pins &= check_mic_location_need(codec, cfg, input);
- return hda_get_input_pin_label(codec, cfg->inputs[input].pin,
- has_multiple_pins);
+ val = snd_hda_correct_pin_ctl(codec, pin, val);
+ snd_hda_codec_set_pin_target(codec, pin, val);
+ if (cached)
+ return snd_hda_codec_update_cache(codec, pin, 0,
+ AC_VERB_SET_PIN_WIDGET_CONTROL, val);
+ else
+ return snd_hda_codec_write(codec, pin, 0,
+ AC_VERB_SET_PIN_WIDGET_CONTROL, val);
}
-EXPORT_SYMBOL_HDA(hda_get_autocfg_input_label);
+EXPORT_SYMBOL_GPL(_snd_hda_set_pin_ctl);
/**
* snd_hda_add_imux_item - Add an item to input_mux
@@ -4816,7 +5695,7 @@ int snd_hda_add_imux_item(struct hda_input_mux *imux, const char *label,
imux->num_items++;
return 0;
}
-EXPORT_SYMBOL_HDA(snd_hda_add_imux_item);
+EXPORT_SYMBOL_GPL(snd_hda_add_imux_item);
#ifdef CONFIG_PM
@@ -4824,6 +5703,17 @@ EXPORT_SYMBOL_HDA(snd_hda_add_imux_item);
* power management
*/
+
+static void hda_async_suspend(void *data, async_cookie_t cookie)
+{
+ hda_call_codec_suspend(data, false);
+}
+
+static void hda_async_resume(void *data, async_cookie_t cookie)
+{
+ hda_call_codec_resume(data);
+}
+
/**
* snd_hda_suspend - suspend the codecs
* @bus: the HDA bus
@@ -4833,38 +5723,50 @@ EXPORT_SYMBOL_HDA(snd_hda_add_imux_item);
int snd_hda_suspend(struct hda_bus *bus)
{
struct hda_codec *codec;
+ ASYNC_DOMAIN_EXCLUSIVE(domain);
list_for_each_entry(codec, &bus->codec_list, list) {
-#ifdef CONFIG_SND_HDA_POWER_SAVE
- if (!codec->power_on)
- continue;
-#endif
- hda_call_codec_suspend(codec);
+ cancel_delayed_work_sync(&codec->jackpoll_work);
+ if (hda_codec_is_power_on(codec)) {
+ if (bus->num_codecs > 1)
+ async_schedule_domain(hda_async_suspend, codec,
+ &domain);
+ else
+ hda_call_codec_suspend(codec, false);
+ }
}
+
+ if (bus->num_codecs > 1)
+ async_synchronize_full_domain(&domain);
+
return 0;
}
-EXPORT_SYMBOL_HDA(snd_hda_suspend);
+EXPORT_SYMBOL_GPL(snd_hda_suspend);
/**
* snd_hda_resume - resume the codecs
* @bus: the HDA bus
*
* Returns 0 if successful.
- *
- * This function is defined only when POWER_SAVE isn't set.
- * In the power-save mode, the codec is resumed dynamically.
*/
int snd_hda_resume(struct hda_bus *bus)
{
struct hda_codec *codec;
+ ASYNC_DOMAIN_EXCLUSIVE(domain);
list_for_each_entry(codec, &bus->codec_list, list) {
- if (snd_hda_codec_needs_resume(codec))
+ if (bus->num_codecs > 1)
+ async_schedule_domain(hda_async_resume, codec, &domain);
+ else
hda_call_codec_resume(codec);
}
+
+ if (bus->num_codecs > 1)
+ async_synchronize_full_domain(&domain);
+
return 0;
}
-EXPORT_SYMBOL_HDA(snd_hda_resume);
+EXPORT_SYMBOL_GPL(snd_hda_resume);
#endif /* CONFIG_PM */
/*
@@ -4882,25 +5784,23 @@ EXPORT_SYMBOL_HDA(snd_hda_resume);
*/
void *snd_array_new(struct snd_array *array)
{
+ if (snd_BUG_ON(!array->elem_size))
+ return NULL;
if (array->used >= array->alloced) {
int num = array->alloced + array->alloc_align;
+ int size = (num + 1) * array->elem_size;
void *nlist;
if (snd_BUG_ON(num >= 4096))
return NULL;
- nlist = kcalloc(num + 1, array->elem_size, GFP_KERNEL);
+ nlist = krealloc(array->list, size, GFP_KERNEL | __GFP_ZERO);
if (!nlist)
return NULL;
- if (array->list) {
- memcpy(nlist, array->list,
- array->elem_size * array->alloced);
- kfree(array->list);
- }
array->list = nlist;
array->alloced = num;
}
return snd_array_elem(array, array->used++);
}
-EXPORT_SYMBOL_HDA(snd_array_new);
+EXPORT_SYMBOL_GPL(snd_array_new);
/**
* snd_array_free - free the given array elements
@@ -4913,31 +5813,7 @@ void snd_array_free(struct snd_array *array)
array->alloced = 0;
array->list = NULL;
}
-EXPORT_SYMBOL_HDA(snd_array_free);
-
-/**
- * snd_print_pcm_rates - Print the supported PCM rates to the string buffer
- * @pcm: PCM caps bits
- * @buf: the string buffer to write
- * @buflen: the max buffer length
- *
- * used by hda_proc.c and hda_eld.c
- */
-void snd_print_pcm_rates(int pcm, char *buf, int buflen)
-{
- static unsigned int rates[] = {
- 8000, 11025, 16000, 22050, 32000, 44100, 48000, 88200,
- 96000, 176400, 192000, 384000
- };
- int i, j;
-
- for (i = 0, j = 0; i < ARRAY_SIZE(rates); i++)
- if (pcm & (1 << i))
- j += snprintf(buf + j, buflen - j, " %d", rates[i]);
-
- buf[j] = '\0'; /* necessary when j == 0 */
-}
-EXPORT_SYMBOL_HDA(snd_print_pcm_rates);
+EXPORT_SYMBOL_GPL(snd_array_free);
/**
* snd_print_pcm_bits - Print the supported PCM fmt bits to the string buffer
@@ -4958,111 +5834,7 @@ void snd_print_pcm_bits(int pcm, char *buf, int buflen)
buf[j] = '\0'; /* necessary when j == 0 */
}
-EXPORT_SYMBOL_HDA(snd_print_pcm_bits);
-
-#ifdef CONFIG_SND_HDA_INPUT_JACK
-/*
- * Input-jack notification support
- */
-struct hda_jack_item {
- hda_nid_t nid;
- int type;
- struct snd_jack *jack;
-};
-
-static const char *get_jack_default_name(struct hda_codec *codec, hda_nid_t nid,
- int type)
-{
- switch (type) {
- case SND_JACK_HEADPHONE:
- return "Headphone";
- case SND_JACK_MICROPHONE:
- return "Mic";
- case SND_JACK_LINEOUT:
- return "Line-out";
- case SND_JACK_HEADSET:
- return "Headset";
- default:
- return "Misc";
- }
-}
-
-static void hda_free_jack_priv(struct snd_jack *jack)
-{
- struct hda_jack_item *jacks = jack->private_data;
- jacks->nid = 0;
- jacks->jack = NULL;
-}
-
-int snd_hda_input_jack_add(struct hda_codec *codec, hda_nid_t nid, int type,
- const char *name)
-{
- struct hda_jack_item *jack;
- int err;
-
- snd_array_init(&codec->jacks, sizeof(*jack), 32);
- jack = snd_array_new(&codec->jacks);
- if (!jack)
- return -ENOMEM;
-
- jack->nid = nid;
- jack->type = type;
- if (!name)
- name = get_jack_default_name(codec, nid, type);
- err = snd_jack_new(codec->bus->card, name, type, &jack->jack);
- if (err < 0) {
- jack->nid = 0;
- return err;
- }
- jack->jack->private_data = jack;
- jack->jack->private_free = hda_free_jack_priv;
- return 0;
-}
-EXPORT_SYMBOL_HDA(snd_hda_input_jack_add);
-
-void snd_hda_input_jack_report(struct hda_codec *codec, hda_nid_t nid)
-{
- struct hda_jack_item *jacks = codec->jacks.list;
- int i;
-
- if (!jacks)
- return;
-
- for (i = 0; i < codec->jacks.used; i++, jacks++) {
- unsigned int pin_ctl;
- unsigned int present;
- int type;
-
- if (jacks->nid != nid)
- continue;
- present = snd_hda_jack_detect(codec, nid);
- type = jacks->type;
- if (type == (SND_JACK_HEADPHONE | SND_JACK_LINEOUT)) {
- pin_ctl = snd_hda_codec_read(codec, nid, 0,
- AC_VERB_GET_PIN_WIDGET_CONTROL, 0);
- type = (pin_ctl & AC_PINCTL_HP_EN) ?
- SND_JACK_HEADPHONE : SND_JACK_LINEOUT;
- }
- snd_jack_report(jacks->jack, present ? type : 0);
- }
-}
-EXPORT_SYMBOL_HDA(snd_hda_input_jack_report);
-
-/* free jack instances manually when clearing/reconfiguring */
-void snd_hda_input_jack_free(struct hda_codec *codec)
-{
- if (!codec->bus->shutdown && codec->jacks.list) {
- struct hda_jack_item *jacks = codec->jacks.list;
- int i;
- for (i = 0; i < codec->jacks.used; i++, jacks++) {
- if (jacks->jack)
- snd_device_free(codec->bus->card, jacks->jack);
- }
- }
- snd_array_free(&codec->jacks);
-}
-EXPORT_SYMBOL_HDA(snd_hda_input_jack_free);
-#endif /* CONFIG_SND_HDA_INPUT_JACK */
+EXPORT_SYMBOL_GPL(snd_print_pcm_bits);
MODULE_DESCRIPTION("HDA codec core");
MODULE_LICENSE("GPL");
diff --git a/sound/pci/hda/hda_codec.h b/sound/pci/hda/hda_codec.h
index e46d5420a9f..5825aa17d8e 100644
--- a/sound/pci/hda/hda_codec.h
+++ b/sound/pci/hda/hda_codec.h
@@ -25,534 +25,7 @@
#include <sound/control.h>
#include <sound/pcm.h>
#include <sound/hwdep.h>
-
-#if defined(CONFIG_PM) || defined(CONFIG_SND_HDA_POWER_SAVE)
-#define SND_HDA_NEEDS_RESUME /* resume control code is required */
-#endif
-
-/*
- * nodes
- */
-#define AC_NODE_ROOT 0x00
-
-/*
- * function group types
- */
-enum {
- AC_GRP_AUDIO_FUNCTION = 0x01,
- AC_GRP_MODEM_FUNCTION = 0x02,
-};
-
-/*
- * widget types
- */
-enum {
- AC_WID_AUD_OUT, /* Audio Out */
- AC_WID_AUD_IN, /* Audio In */
- AC_WID_AUD_MIX, /* Audio Mixer */
- AC_WID_AUD_SEL, /* Audio Selector */
- AC_WID_PIN, /* Pin Complex */
- AC_WID_POWER, /* Power */
- AC_WID_VOL_KNB, /* Volume Knob */
- AC_WID_BEEP, /* Beep Generator */
- AC_WID_VENDOR = 0x0f /* Vendor specific */
-};
-
-/*
- * GET verbs
- */
-#define AC_VERB_GET_STREAM_FORMAT 0x0a00
-#define AC_VERB_GET_AMP_GAIN_MUTE 0x0b00
-#define AC_VERB_GET_PROC_COEF 0x0c00
-#define AC_VERB_GET_COEF_INDEX 0x0d00
-#define AC_VERB_PARAMETERS 0x0f00
-#define AC_VERB_GET_CONNECT_SEL 0x0f01
-#define AC_VERB_GET_CONNECT_LIST 0x0f02
-#define AC_VERB_GET_PROC_STATE 0x0f03
-#define AC_VERB_GET_SDI_SELECT 0x0f04
-#define AC_VERB_GET_POWER_STATE 0x0f05
-#define AC_VERB_GET_CONV 0x0f06
-#define AC_VERB_GET_PIN_WIDGET_CONTROL 0x0f07
-#define AC_VERB_GET_UNSOLICITED_RESPONSE 0x0f08
-#define AC_VERB_GET_PIN_SENSE 0x0f09
-#define AC_VERB_GET_BEEP_CONTROL 0x0f0a
-#define AC_VERB_GET_EAPD_BTLENABLE 0x0f0c
-#define AC_VERB_GET_DIGI_CONVERT_1 0x0f0d
-#define AC_VERB_GET_DIGI_CONVERT_2 0x0f0e /* unused */
-#define AC_VERB_GET_VOLUME_KNOB_CONTROL 0x0f0f
-/* f10-f1a: GPIO */
-#define AC_VERB_GET_GPIO_DATA 0x0f15
-#define AC_VERB_GET_GPIO_MASK 0x0f16
-#define AC_VERB_GET_GPIO_DIRECTION 0x0f17
-#define AC_VERB_GET_GPIO_WAKE_MASK 0x0f18
-#define AC_VERB_GET_GPIO_UNSOLICITED_RSP_MASK 0x0f19
-#define AC_VERB_GET_GPIO_STICKY_MASK 0x0f1a
-#define AC_VERB_GET_CONFIG_DEFAULT 0x0f1c
-/* f20: AFG/MFG */
-#define AC_VERB_GET_SUBSYSTEM_ID 0x0f20
-#define AC_VERB_GET_CVT_CHAN_COUNT 0x0f2d
-#define AC_VERB_GET_HDMI_DIP_SIZE 0x0f2e
-#define AC_VERB_GET_HDMI_ELDD 0x0f2f
-#define AC_VERB_GET_HDMI_DIP_INDEX 0x0f30
-#define AC_VERB_GET_HDMI_DIP_DATA 0x0f31
-#define AC_VERB_GET_HDMI_DIP_XMIT 0x0f32
-#define AC_VERB_GET_HDMI_CP_CTRL 0x0f33
-#define AC_VERB_GET_HDMI_CHAN_SLOT 0x0f34
-
-/*
- * SET verbs
- */
-#define AC_VERB_SET_STREAM_FORMAT 0x200
-#define AC_VERB_SET_AMP_GAIN_MUTE 0x300
-#define AC_VERB_SET_PROC_COEF 0x400
-#define AC_VERB_SET_COEF_INDEX 0x500
-#define AC_VERB_SET_CONNECT_SEL 0x701
-#define AC_VERB_SET_PROC_STATE 0x703
-#define AC_VERB_SET_SDI_SELECT 0x704
-#define AC_VERB_SET_POWER_STATE 0x705
-#define AC_VERB_SET_CHANNEL_STREAMID 0x706
-#define AC_VERB_SET_PIN_WIDGET_CONTROL 0x707
-#define AC_VERB_SET_UNSOLICITED_ENABLE 0x708
-#define AC_VERB_SET_PIN_SENSE 0x709
-#define AC_VERB_SET_BEEP_CONTROL 0x70a
-#define AC_VERB_SET_EAPD_BTLENABLE 0x70c
-#define AC_VERB_SET_DIGI_CONVERT_1 0x70d
-#define AC_VERB_SET_DIGI_CONVERT_2 0x70e
-#define AC_VERB_SET_VOLUME_KNOB_CONTROL 0x70f
-#define AC_VERB_SET_GPIO_DATA 0x715
-#define AC_VERB_SET_GPIO_MASK 0x716
-#define AC_VERB_SET_GPIO_DIRECTION 0x717
-#define AC_VERB_SET_GPIO_WAKE_MASK 0x718
-#define AC_VERB_SET_GPIO_UNSOLICITED_RSP_MASK 0x719
-#define AC_VERB_SET_GPIO_STICKY_MASK 0x71a
-#define AC_VERB_SET_CONFIG_DEFAULT_BYTES_0 0x71c
-#define AC_VERB_SET_CONFIG_DEFAULT_BYTES_1 0x71d
-#define AC_VERB_SET_CONFIG_DEFAULT_BYTES_2 0x71e
-#define AC_VERB_SET_CONFIG_DEFAULT_BYTES_3 0x71f
-#define AC_VERB_SET_EAPD 0x788
-#define AC_VERB_SET_CODEC_RESET 0x7ff
-#define AC_VERB_SET_CVT_CHAN_COUNT 0x72d
-#define AC_VERB_SET_HDMI_DIP_INDEX 0x730
-#define AC_VERB_SET_HDMI_DIP_DATA 0x731
-#define AC_VERB_SET_HDMI_DIP_XMIT 0x732
-#define AC_VERB_SET_HDMI_CP_CTRL 0x733
-#define AC_VERB_SET_HDMI_CHAN_SLOT 0x734
-
-/*
- * Parameter IDs
- */
-#define AC_PAR_VENDOR_ID 0x00
-#define AC_PAR_SUBSYSTEM_ID 0x01
-#define AC_PAR_REV_ID 0x02
-#define AC_PAR_NODE_COUNT 0x04
-#define AC_PAR_FUNCTION_TYPE 0x05
-#define AC_PAR_AUDIO_FG_CAP 0x08
-#define AC_PAR_AUDIO_WIDGET_CAP 0x09
-#define AC_PAR_PCM 0x0a
-#define AC_PAR_STREAM 0x0b
-#define AC_PAR_PIN_CAP 0x0c
-#define AC_PAR_AMP_IN_CAP 0x0d
-#define AC_PAR_CONNLIST_LEN 0x0e
-#define AC_PAR_POWER_STATE 0x0f
-#define AC_PAR_PROC_CAP 0x10
-#define AC_PAR_GPIO_CAP 0x11
-#define AC_PAR_AMP_OUT_CAP 0x12
-#define AC_PAR_VOL_KNB_CAP 0x13
-#define AC_PAR_HDMI_LPCM_CAP 0x20
-
-/*
- * AC_VERB_PARAMETERS results (32bit)
- */
-
-/* Function Group Type */
-#define AC_FGT_TYPE (0xff<<0)
-#define AC_FGT_TYPE_SHIFT 0
-#define AC_FGT_UNSOL_CAP (1<<8)
-
-/* Audio Function Group Capabilities */
-#define AC_AFG_OUT_DELAY (0xf<<0)
-#define AC_AFG_IN_DELAY (0xf<<8)
-#define AC_AFG_BEEP_GEN (1<<16)
-
-/* Audio Widget Capabilities */
-#define AC_WCAP_STEREO (1<<0) /* stereo I/O */
-#define AC_WCAP_IN_AMP (1<<1) /* AMP-in present */
-#define AC_WCAP_OUT_AMP (1<<2) /* AMP-out present */
-#define AC_WCAP_AMP_OVRD (1<<3) /* AMP-parameter override */
-#define AC_WCAP_FORMAT_OVRD (1<<4) /* format override */
-#define AC_WCAP_STRIPE (1<<5) /* stripe */
-#define AC_WCAP_PROC_WID (1<<6) /* Proc Widget */
-#define AC_WCAP_UNSOL_CAP (1<<7) /* Unsol capable */
-#define AC_WCAP_CONN_LIST (1<<8) /* connection list */
-#define AC_WCAP_DIGITAL (1<<9) /* digital I/O */
-#define AC_WCAP_POWER (1<<10) /* power control */
-#define AC_WCAP_LR_SWAP (1<<11) /* L/R swap */
-#define AC_WCAP_CP_CAPS (1<<12) /* content protection */
-#define AC_WCAP_CHAN_CNT_EXT (7<<13) /* channel count ext */
-#define AC_WCAP_DELAY (0xf<<16)
-#define AC_WCAP_DELAY_SHIFT 16
-#define AC_WCAP_TYPE (0xf<<20)
-#define AC_WCAP_TYPE_SHIFT 20
-
-/* supported PCM rates and bits */
-#define AC_SUPPCM_RATES (0xfff << 0)
-#define AC_SUPPCM_BITS_8 (1<<16)
-#define AC_SUPPCM_BITS_16 (1<<17)
-#define AC_SUPPCM_BITS_20 (1<<18)
-#define AC_SUPPCM_BITS_24 (1<<19)
-#define AC_SUPPCM_BITS_32 (1<<20)
-
-/* supported PCM stream format */
-#define AC_SUPFMT_PCM (1<<0)
-#define AC_SUPFMT_FLOAT32 (1<<1)
-#define AC_SUPFMT_AC3 (1<<2)
-
-/* GP I/O count */
-#define AC_GPIO_IO_COUNT (0xff<<0)
-#define AC_GPIO_O_COUNT (0xff<<8)
-#define AC_GPIO_O_COUNT_SHIFT 8
-#define AC_GPIO_I_COUNT (0xff<<16)
-#define AC_GPIO_I_COUNT_SHIFT 16
-#define AC_GPIO_UNSOLICITED (1<<30)
-#define AC_GPIO_WAKE (1<<31)
-
-/* Converter stream, channel */
-#define AC_CONV_CHANNEL (0xf<<0)
-#define AC_CONV_STREAM (0xf<<4)
-#define AC_CONV_STREAM_SHIFT 4
-
-/* Input converter SDI select */
-#define AC_SDI_SELECT (0xf<<0)
-
-/* stream format id */
-#define AC_FMT_CHAN_SHIFT 0
-#define AC_FMT_CHAN_MASK (0x0f << 0)
-#define AC_FMT_BITS_SHIFT 4
-#define AC_FMT_BITS_MASK (7 << 4)
-#define AC_FMT_BITS_8 (0 << 4)
-#define AC_FMT_BITS_16 (1 << 4)
-#define AC_FMT_BITS_20 (2 << 4)
-#define AC_FMT_BITS_24 (3 << 4)
-#define AC_FMT_BITS_32 (4 << 4)
-#define AC_FMT_DIV_SHIFT 8
-#define AC_FMT_DIV_MASK (7 << 8)
-#define AC_FMT_MULT_SHIFT 11
-#define AC_FMT_MULT_MASK (7 << 11)
-#define AC_FMT_BASE_SHIFT 14
-#define AC_FMT_BASE_48K (0 << 14)
-#define AC_FMT_BASE_44K (1 << 14)
-#define AC_FMT_TYPE_SHIFT 15
-#define AC_FMT_TYPE_PCM (0 << 15)
-#define AC_FMT_TYPE_NON_PCM (1 << 15)
-
-/* Unsolicited response control */
-#define AC_UNSOL_TAG (0x3f<<0)
-#define AC_UNSOL_ENABLED (1<<7)
-#define AC_USRSP_EN AC_UNSOL_ENABLED
-
-/* Unsolicited responses */
-#define AC_UNSOL_RES_TAG (0x3f<<26)
-#define AC_UNSOL_RES_TAG_SHIFT 26
-#define AC_UNSOL_RES_SUBTAG (0x1f<<21)
-#define AC_UNSOL_RES_SUBTAG_SHIFT 21
-#define AC_UNSOL_RES_ELDV (1<<1) /* ELD Data valid (for HDMI) */
-#define AC_UNSOL_RES_PD (1<<0) /* pinsense detect */
-#define AC_UNSOL_RES_CP_STATE (1<<1) /* content protection */
-#define AC_UNSOL_RES_CP_READY (1<<0) /* content protection */
-
-/* Pin widget capabilies */
-#define AC_PINCAP_IMP_SENSE (1<<0) /* impedance sense capable */
-#define AC_PINCAP_TRIG_REQ (1<<1) /* trigger required */
-#define AC_PINCAP_PRES_DETECT (1<<2) /* presence detect capable */
-#define AC_PINCAP_HP_DRV (1<<3) /* headphone drive capable */
-#define AC_PINCAP_OUT (1<<4) /* output capable */
-#define AC_PINCAP_IN (1<<5) /* input capable */
-#define AC_PINCAP_BALANCE (1<<6) /* balanced I/O capable */
-/* Note: This LR_SWAP pincap is defined in the Realtek ALC883 specification,
- * but is marked reserved in the Intel HDA specification.
- */
-#define AC_PINCAP_LR_SWAP (1<<7) /* L/R swap */
-/* Note: The same bit as LR_SWAP is newly defined as HDMI capability
- * in HD-audio specification
- */
-#define AC_PINCAP_HDMI (1<<7) /* HDMI pin */
-#define AC_PINCAP_DP (1<<24) /* DisplayPort pin, can
- * coexist with AC_PINCAP_HDMI
- */
-#define AC_PINCAP_VREF (0x37<<8)
-#define AC_PINCAP_VREF_SHIFT 8
-#define AC_PINCAP_EAPD (1<<16) /* EAPD capable */
-#define AC_PINCAP_HBR (1<<27) /* High Bit Rate */
-/* Vref status (used in pin cap) */
-#define AC_PINCAP_VREF_HIZ (1<<0) /* Hi-Z */
-#define AC_PINCAP_VREF_50 (1<<1) /* 50% */
-#define AC_PINCAP_VREF_GRD (1<<2) /* ground */
-#define AC_PINCAP_VREF_80 (1<<4) /* 80% */
-#define AC_PINCAP_VREF_100 (1<<5) /* 100% */
-
-/* Amplifier capabilities */
-#define AC_AMPCAP_OFFSET (0x7f<<0) /* 0dB offset */
-#define AC_AMPCAP_OFFSET_SHIFT 0
-#define AC_AMPCAP_NUM_STEPS (0x7f<<8) /* number of steps */
-#define AC_AMPCAP_NUM_STEPS_SHIFT 8
-#define AC_AMPCAP_STEP_SIZE (0x7f<<16) /* step size 0-32dB
- * in 0.25dB
- */
-#define AC_AMPCAP_STEP_SIZE_SHIFT 16
-#define AC_AMPCAP_MUTE (1<<31) /* mute capable */
-#define AC_AMPCAP_MUTE_SHIFT 31
-
-/* Connection list */
-#define AC_CLIST_LENGTH (0x7f<<0)
-#define AC_CLIST_LONG (1<<7)
-
-/* Supported power status */
-#define AC_PWRST_D0SUP (1<<0)
-#define AC_PWRST_D1SUP (1<<1)
-#define AC_PWRST_D2SUP (1<<2)
-#define AC_PWRST_D3SUP (1<<3)
-#define AC_PWRST_D3COLDSUP (1<<4)
-#define AC_PWRST_S3D3COLDSUP (1<<29)
-#define AC_PWRST_CLKSTOP (1<<30)
-#define AC_PWRST_EPSS (1U<<31)
-
-/* Power state values */
-#define AC_PWRST_SETTING (0xf<<0)
-#define AC_PWRST_ACTUAL (0xf<<4)
-#define AC_PWRST_ACTUAL_SHIFT 4
-#define AC_PWRST_D0 0x00
-#define AC_PWRST_D1 0x01
-#define AC_PWRST_D2 0x02
-#define AC_PWRST_D3 0x03
-
-/* Processing capabilies */
-#define AC_PCAP_BENIGN (1<<0)
-#define AC_PCAP_NUM_COEF (0xff<<8)
-#define AC_PCAP_NUM_COEF_SHIFT 8
-
-/* Volume knobs capabilities */
-#define AC_KNBCAP_NUM_STEPS (0x7f<<0)
-#define AC_KNBCAP_DELTA (1<<7)
-
-/* HDMI LPCM capabilities */
-#define AC_LPCMCAP_48K_CP_CHNS (0x0f<<0) /* max channels w/ CP-on */
-#define AC_LPCMCAP_48K_NO_CHNS (0x0f<<4) /* max channels w/o CP-on */
-#define AC_LPCMCAP_48K_20BIT (1<<8) /* 20b bitrate supported */
-#define AC_LPCMCAP_48K_24BIT (1<<9) /* 24b bitrate supported */
-#define AC_LPCMCAP_96K_CP_CHNS (0x0f<<10) /* max channels w/ CP-on */
-#define AC_LPCMCAP_96K_NO_CHNS (0x0f<<14) /* max channels w/o CP-on */
-#define AC_LPCMCAP_96K_20BIT (1<<18) /* 20b bitrate supported */
-#define AC_LPCMCAP_96K_24BIT (1<<19) /* 24b bitrate supported */
-#define AC_LPCMCAP_192K_CP_CHNS (0x0f<<20) /* max channels w/ CP-on */
-#define AC_LPCMCAP_192K_NO_CHNS (0x0f<<24) /* max channels w/o CP-on */
-#define AC_LPCMCAP_192K_20BIT (1<<28) /* 20b bitrate supported */
-#define AC_LPCMCAP_192K_24BIT (1<<29) /* 24b bitrate supported */
-#define AC_LPCMCAP_44K (1<<30) /* 44.1kHz support */
-#define AC_LPCMCAP_44K_MS (1<<31) /* 44.1kHz-multiplies support */
-
-/*
- * Control Parameters
- */
-
-/* Amp gain/mute */
-#define AC_AMP_MUTE (1<<7)
-#define AC_AMP_GAIN (0x7f)
-#define AC_AMP_GET_INDEX (0xf<<0)
-
-#define AC_AMP_GET_LEFT (1<<13)
-#define AC_AMP_GET_RIGHT (0<<13)
-#define AC_AMP_GET_OUTPUT (1<<15)
-#define AC_AMP_GET_INPUT (0<<15)
-
-#define AC_AMP_SET_INDEX (0xf<<8)
-#define AC_AMP_SET_INDEX_SHIFT 8
-#define AC_AMP_SET_RIGHT (1<<12)
-#define AC_AMP_SET_LEFT (1<<13)
-#define AC_AMP_SET_INPUT (1<<14)
-#define AC_AMP_SET_OUTPUT (1<<15)
-
-/* DIGITAL1 bits */
-#define AC_DIG1_ENABLE (1<<0)
-#define AC_DIG1_V (1<<1)
-#define AC_DIG1_VCFG (1<<2)
-#define AC_DIG1_EMPHASIS (1<<3)
-#define AC_DIG1_COPYRIGHT (1<<4)
-#define AC_DIG1_NONAUDIO (1<<5)
-#define AC_DIG1_PROFESSIONAL (1<<6)
-#define AC_DIG1_LEVEL (1<<7)
-
-/* DIGITAL2 bits */
-#define AC_DIG2_CC (0x7f<<0)
-
-/* Pin widget control - 8bit */
-#define AC_PINCTL_EPT (0x3<<0)
-#define AC_PINCTL_EPT_NATIVE 0
-#define AC_PINCTL_EPT_HBR 3
-#define AC_PINCTL_VREFEN (0x7<<0)
-#define AC_PINCTL_VREF_HIZ 0 /* Hi-Z */
-#define AC_PINCTL_VREF_50 1 /* 50% */
-#define AC_PINCTL_VREF_GRD 2 /* ground */
-#define AC_PINCTL_VREF_80 4 /* 80% */
-#define AC_PINCTL_VREF_100 5 /* 100% */
-#define AC_PINCTL_IN_EN (1<<5)
-#define AC_PINCTL_OUT_EN (1<<6)
-#define AC_PINCTL_HP_EN (1<<7)
-
-/* Pin sense - 32bit */
-#define AC_PINSENSE_IMPEDANCE_MASK (0x7fffffff)
-#define AC_PINSENSE_PRESENCE (1<<31)
-#define AC_PINSENSE_ELDV (1<<30) /* ELD valid (HDMI) */
-
-/* EAPD/BTL enable - 32bit */
-#define AC_EAPDBTL_BALANCED (1<<0)
-#define AC_EAPDBTL_EAPD (1<<1)
-#define AC_EAPDBTL_LR_SWAP (1<<2)
-
-/* HDMI ELD data */
-#define AC_ELDD_ELD_VALID (1<<31)
-#define AC_ELDD_ELD_DATA 0xff
-
-/* HDMI DIP size */
-#define AC_DIPSIZE_ELD_BUF (1<<3) /* ELD buf size of packet size */
-#define AC_DIPSIZE_PACK_IDX (0x07<<0) /* packet index */
-
-/* HDMI DIP index */
-#define AC_DIPIDX_PACK_IDX (0x07<<5) /* packet idnex */
-#define AC_DIPIDX_BYTE_IDX (0x1f<<0) /* byte index */
-
-/* HDMI DIP xmit (transmit) control */
-#define AC_DIPXMIT_MASK (0x3<<6)
-#define AC_DIPXMIT_DISABLE (0x0<<6) /* disable xmit */
-#define AC_DIPXMIT_ONCE (0x2<<6) /* xmit once then disable */
-#define AC_DIPXMIT_BEST (0x3<<6) /* best effort */
-
-/* HDMI content protection (CP) control */
-#define AC_CPCTRL_CES (1<<9) /* current encryption state */
-#define AC_CPCTRL_READY (1<<8) /* ready bit */
-#define AC_CPCTRL_SUBTAG (0x1f<<3) /* subtag for unsol-resp */
-#define AC_CPCTRL_STATE (3<<0) /* current CP request state */
-
-/* Converter channel <-> HDMI slot mapping */
-#define AC_CVTMAP_HDMI_SLOT (0xf<<0) /* HDMI slot number */
-#define AC_CVTMAP_CHAN (0xf<<4) /* converter channel number */
-
-/* configuration default - 32bit */
-#define AC_DEFCFG_SEQUENCE (0xf<<0)
-#define AC_DEFCFG_DEF_ASSOC (0xf<<4)
-#define AC_DEFCFG_ASSOC_SHIFT 4
-#define AC_DEFCFG_MISC (0xf<<8)
-#define AC_DEFCFG_MISC_SHIFT 8
-#define AC_DEFCFG_MISC_NO_PRESENCE (1<<0)
-#define AC_DEFCFG_COLOR (0xf<<12)
-#define AC_DEFCFG_COLOR_SHIFT 12
-#define AC_DEFCFG_CONN_TYPE (0xf<<16)
-#define AC_DEFCFG_CONN_TYPE_SHIFT 16
-#define AC_DEFCFG_DEVICE (0xf<<20)
-#define AC_DEFCFG_DEVICE_SHIFT 20
-#define AC_DEFCFG_LOCATION (0x3f<<24)
-#define AC_DEFCFG_LOCATION_SHIFT 24
-#define AC_DEFCFG_PORT_CONN (0x3<<30)
-#define AC_DEFCFG_PORT_CONN_SHIFT 30
-
-/* device device types (0x0-0xf) */
-enum {
- AC_JACK_LINE_OUT,
- AC_JACK_SPEAKER,
- AC_JACK_HP_OUT,
- AC_JACK_CD,
- AC_JACK_SPDIF_OUT,
- AC_JACK_DIG_OTHER_OUT,
- AC_JACK_MODEM_LINE_SIDE,
- AC_JACK_MODEM_HAND_SIDE,
- AC_JACK_LINE_IN,
- AC_JACK_AUX,
- AC_JACK_MIC_IN,
- AC_JACK_TELEPHONY,
- AC_JACK_SPDIF_IN,
- AC_JACK_DIG_OTHER_IN,
- AC_JACK_OTHER = 0xf,
-};
-
-/* jack connection types (0x0-0xf) */
-enum {
- AC_JACK_CONN_UNKNOWN,
- AC_JACK_CONN_1_8,
- AC_JACK_CONN_1_4,
- AC_JACK_CONN_ATAPI,
- AC_JACK_CONN_RCA,
- AC_JACK_CONN_OPTICAL,
- AC_JACK_CONN_OTHER_DIGITAL,
- AC_JACK_CONN_OTHER_ANALOG,
- AC_JACK_CONN_DIN,
- AC_JACK_CONN_XLR,
- AC_JACK_CONN_RJ11,
- AC_JACK_CONN_COMB,
- AC_JACK_CONN_OTHER = 0xf,
-};
-
-/* jack colors (0x0-0xf) */
-enum {
- AC_JACK_COLOR_UNKNOWN,
- AC_JACK_COLOR_BLACK,
- AC_JACK_COLOR_GREY,
- AC_JACK_COLOR_BLUE,
- AC_JACK_COLOR_GREEN,
- AC_JACK_COLOR_RED,
- AC_JACK_COLOR_ORANGE,
- AC_JACK_COLOR_YELLOW,
- AC_JACK_COLOR_PURPLE,
- AC_JACK_COLOR_PINK,
- AC_JACK_COLOR_WHITE = 0xe,
- AC_JACK_COLOR_OTHER,
-};
-
-/* Jack location (0x0-0x3f) */
-/* common case */
-enum {
- AC_JACK_LOC_NONE,
- AC_JACK_LOC_REAR,
- AC_JACK_LOC_FRONT,
- AC_JACK_LOC_LEFT,
- AC_JACK_LOC_RIGHT,
- AC_JACK_LOC_TOP,
- AC_JACK_LOC_BOTTOM,
-};
-/* bits 4-5 */
-enum {
- AC_JACK_LOC_EXTERNAL = 0x00,
- AC_JACK_LOC_INTERNAL = 0x10,
- AC_JACK_LOC_SEPARATE = 0x20,
- AC_JACK_LOC_OTHER = 0x30,
-};
-enum {
- /* external on primary chasis */
- AC_JACK_LOC_REAR_PANEL = 0x07,
- AC_JACK_LOC_DRIVE_BAY,
- /* internal */
- AC_JACK_LOC_RISER = 0x17,
- AC_JACK_LOC_HDMI,
- AC_JACK_LOC_ATAPI,
- /* others */
- AC_JACK_LOC_MOBILE_IN = 0x37,
- AC_JACK_LOC_MOBILE_OUT,
-};
-
-/* Port connectivity (0-3) */
-enum {
- AC_JACK_PORT_COMPLEX,
- AC_JACK_PORT_NONE,
- AC_JACK_PORT_FIXED,
- AC_JACK_PORT_BOTH,
-};
-
-/* max. connections to a widget */
-#define HDA_MAX_CONNECTIONS 32
-
-/* max. codec address */
-#define HDA_MAX_CODEC_ADDRESS 0x0f
-
-/* max number of PCM devics per card */
-#define HDA_MAX_PCMS 10
+#include <sound/hda_verbs.h>
/*
* generic arrays
@@ -611,9 +84,20 @@ struct hda_bus_ops {
struct hda_pcm *pcm);
/* reset bus for retry verb */
void (*bus_reset)(struct hda_bus *bus);
-#ifdef CONFIG_SND_HDA_POWER_SAVE
+#ifdef CONFIG_PM
/* notify power-up/down from codec to controller */
- void (*pm_notify)(struct hda_bus *bus);
+ void (*pm_notify)(struct hda_bus *bus, bool power_up);
+#endif
+#ifdef CONFIG_SND_HDA_DSP_LOADER
+ /* prepare DSP transfer */
+ int (*load_dsp_prepare)(struct hda_bus *bus, unsigned int format,
+ unsigned int byte_size,
+ struct snd_dma_buffer *bufp);
+ /* start/stop DSP transfer */
+ void (*load_dsp_trigger)(struct hda_bus *bus, bool start);
+ /* clean up DSP transfer */
+ void (*load_dsp_cleanup)(struct hda_bus *bus,
+ struct snd_dma_buffer *dmab);
#endif
};
@@ -644,6 +128,7 @@ struct hda_bus {
/* codec linked list */
struct list_head codec_list;
+ unsigned int num_codecs;
/* link caddr -> codec */
struct hda_codec *caddr_tbl[HDA_MAX_CODEC_ADDRESS + 1];
@@ -668,6 +153,9 @@ struct hda_bus {
unsigned int response_reset:1; /* controller was reset */
unsigned int in_reset:1; /* during reset operation */
unsigned int power_keep_link_on:1; /* don't power off HDA link */
+ unsigned int no_response_fallback:1; /* don't fallback at RIRB error */
+
+ int primary_dig_out_type; /* primary digital out PCM type */
};
/*
@@ -704,11 +192,11 @@ struct hda_codec_ops {
int (*init)(struct hda_codec *codec);
void (*free)(struct hda_codec *codec);
void (*unsol_event)(struct hda_codec *codec, unsigned int res);
-#ifdef SND_HDA_NEEDS_RESUME
- int (*suspend)(struct hda_codec *codec, pm_message_t state);
+ void (*set_power_state)(struct hda_codec *codec, hda_nid_t fg,
+ unsigned int power_state);
+#ifdef CONFIG_PM
+ int (*suspend)(struct hda_codec *codec);
int (*resume)(struct hda_codec *codec);
-#endif
-#ifdef CONFIG_SND_HDA_POWER_SAVE
int (*check_power_status)(struct hda_codec *codec, hda_nid_t nid);
#endif
void (*reboot_notify)(struct hda_codec *codec);
@@ -716,9 +204,10 @@ struct hda_codec_ops {
/* record for amp information cache */
struct hda_cache_head {
- u32 key; /* hash key */
+ u32 key:31; /* hash key */
+ u32 dirty:1;
u16 val; /* assigned value */
- u16 next; /* next link; -1 = terminal */
+ u16 next;
};
struct hda_amp_info {
@@ -743,6 +232,9 @@ struct hda_pcm_ops {
struct snd_pcm_substream *substream);
int (*cleanup)(struct hda_pcm_stream *info, struct hda_codec *codec,
struct snd_pcm_substream *substream);
+ unsigned int (*get_delay)(struct hda_pcm_stream *info,
+ struct hda_codec *codec,
+ struct snd_pcm_substream *substream);
};
/* PCM information for each substream */
@@ -754,6 +246,7 @@ struct hda_pcm_stream {
u32 rates; /* supported rates */
u64 formats; /* supported formats (SNDRV_PCM_FMTBIT_) */
unsigned int maxbps; /* supported max. bit per sample */
+ const struct snd_pcm_chmap_elem *chmap; /* chmap to override */
struct hda_pcm_ops ops;
};
@@ -773,10 +266,12 @@ struct hda_pcm {
unsigned int pcm_type; /* HDA_PCM_TYPE_XXX */
int device; /* device number to assign */
struct snd_pcm *pcm; /* assigned PCM instance */
+ bool own_chmap; /* codec driver provides own channel maps */
};
/* codec information */
struct hda_codec {
+ struct device dev;
struct hda_bus *bus;
unsigned int addr; /* codec addr*/
struct list_head list; /* list point */
@@ -796,6 +291,7 @@ struct hda_codec {
/* detected preset */
const struct hda_codec_preset *preset;
struct module *owner;
+ int (*parser)(struct hda_codec *codec);
const char *vendor_name; /* codec vendor name */
const char *chip_name; /* codec chip name */
const char *modelname; /* model name for preset */
@@ -825,23 +321,29 @@ struct hda_codec {
struct hda_cache_rec amp_cache; /* cache for amp access */
struct hda_cache_rec cmd_cache; /* cache for other commands */
+ struct list_head conn_list; /* linked-list of connection-list */
+
struct mutex spdif_mutex;
struct mutex control_mutex;
- unsigned int spdif_status; /* IEC958 status bits */
- unsigned short spdif_ctls; /* SPDIF control bits */
+ struct mutex hash_mutex;
+ struct snd_array spdif_out;
unsigned int spdif_in_enable; /* SPDIF input enable? */
- hda_nid_t *slave_dig_outs; /* optional digital out slave widgets */
+ const hda_nid_t *slave_dig_outs; /* optional digital out slave widgets */
struct snd_array init_pins; /* initial (BIOS) pin configurations */
struct snd_array driver_pins; /* pin configs set by codec parser */
struct snd_array cvt_setups; /* audio convert setups */
-#ifdef CONFIG_SND_HDA_HWDEP
- struct snd_hwdep *hwdep; /* assigned hwdep device */
+ struct mutex user_mutex;
+#ifdef CONFIG_SND_HDA_RECONFIG
struct snd_array init_verbs; /* additional init verbs */
struct snd_array hints; /* additional hints */
struct snd_array user_pins; /* default pin configs to override */
#endif
+#ifdef CONFIG_SND_HDA_HWDEP
+ struct snd_hwdep *hwdep; /* assigned hwdep device */
+#endif
+
/* misc flags */
unsigned int spdif_status_reset :1; /* needs to toggle SPDIF for each
* status change
@@ -850,27 +352,62 @@ struct hda_codec {
unsigned int pin_amp_workaround:1; /* pin out-amp takes index
* (e.g. Conexant codecs)
*/
+ unsigned int single_adc_amp:1; /* adc in-amp takes no index
+ * (e.g. CX20549 codec)
+ */
unsigned int no_sticky_stream:1; /* no sticky-PCM stream assignment */
unsigned int pins_shutup:1; /* pins are shut up */
unsigned int no_trigger_sense:1; /* don't trigger at pin-sensing */
-#ifdef CONFIG_SND_HDA_POWER_SAVE
+ unsigned int no_jack_detect:1; /* Machine has no jack-detection */
+ unsigned int inv_eapd:1; /* broken h/w: inverted EAPD control */
+ unsigned int inv_jack_detect:1; /* broken h/w: inverted detection bit */
+ unsigned int pcm_format_first:1; /* PCM format must be set first */
+ unsigned int epss:1; /* supporting EPSS? */
+ unsigned int cached_write:1; /* write only to caches */
+ unsigned int dp_mst:1; /* support DP1.2 Multi-stream transport */
+ unsigned int dump_coef:1; /* dump processing coefs in codec proc file */
+#ifdef CONFIG_PM
unsigned int power_on :1; /* current (global) power-state */
- unsigned int power_transition :1; /* power-state in transition */
+ unsigned int d3_stop_clk:1; /* support D3 operation without BCLK */
+ unsigned int pm_up_notified:1; /* PM notified to controller */
+ unsigned int in_pm:1; /* suspend/resume being performed */
+ int power_transition; /* power-state in transition */
int power_count; /* current (global) power refcount */
struct delayed_work power_work; /* delayed task for powerdown */
unsigned long power_on_acct;
unsigned long power_off_acct;
unsigned long power_jiffies;
+ spinlock_t power_lock;
#endif
+ /* filter the requested power state per nid */
+ unsigned int (*power_filter)(struct hda_codec *codec, hda_nid_t nid,
+ unsigned int power_state);
+
/* codec-specific additional proc output */
void (*proc_widget_hook)(struct snd_info_buffer *buffer,
struct hda_codec *codec, hda_nid_t nid);
+ /* jack detection */
+ struct snd_array jacktbl;
+ unsigned long jackpoll_interval; /* In jiffies. Zero means no poll, rely on unsol events */
+ struct delayed_work jackpoll_work;
+
#ifdef CONFIG_SND_HDA_INPUT_JACK
/* jack detection */
struct snd_array jacks;
#endif
+
+ int depop_delay; /* depop delay in ms, -1 for default delay time */
+
+ /* fix-up list */
+ int fixup_id;
+ unsigned int fixup_forced:1; /* fixup explicitly set by user */
+ const struct hda_fixup *fixup_list;
+ const char *fixup_name;
+
+ /* additional init verbs */
+ struct snd_array verbs;
};
/* direction */
@@ -878,6 +415,8 @@ enum {
HDA_INPUT, HDA_OUTPUT
};
+/* snd_hda_codec_read/write optional flags */
+#define HDA_RW_NO_RESPONSE_FALLBACK (1 << 0)
/*
* constructors
@@ -887,14 +426,15 @@ int snd_hda_bus_new(struct snd_card *card, const struct hda_bus_template *temp,
int snd_hda_codec_new(struct hda_bus *bus, unsigned int codec_addr,
struct hda_codec **codecp);
int snd_hda_codec_configure(struct hda_codec *codec);
+int snd_hda_codec_update_widgets(struct hda_codec *codec);
/*
* low level functions
*/
unsigned int snd_hda_codec_read(struct hda_codec *codec, hda_nid_t nid,
- int direct,
+ int flags,
unsigned int verb, unsigned int parm);
-int snd_hda_codec_write(struct hda_codec *codec, hda_nid_t nid, int direct,
+int snd_hda_codec_write(struct hda_codec *codec, hda_nid_t nid, int flags,
unsigned int verb, unsigned int parm);
#define snd_hda_param_read(codec, nid, param) \
snd_hda_codec_read(codec, nid, 0, AC_VERB_PARAMETERS, param)
@@ -902,6 +442,24 @@ int snd_hda_get_sub_nodes(struct hda_codec *codec, hda_nid_t nid,
hda_nid_t *start_id);
int snd_hda_get_connections(struct hda_codec *codec, hda_nid_t nid,
hda_nid_t *conn_list, int max_conns);
+static inline int
+snd_hda_get_num_conns(struct hda_codec *codec, hda_nid_t nid)
+{
+ return snd_hda_get_connections(codec, nid, NULL, 0);
+}
+int snd_hda_get_num_raw_conns(struct hda_codec *codec, hda_nid_t nid);
+int snd_hda_get_raw_connections(struct hda_codec *codec, hda_nid_t nid,
+ hda_nid_t *conn_list, int max_conns);
+int snd_hda_get_conn_list(struct hda_codec *codec, hda_nid_t nid,
+ const hda_nid_t **listp);
+int snd_hda_override_conn_list(struct hda_codec *codec, hda_nid_t nid, int nums,
+ const hda_nid_t *list);
+int snd_hda_get_conn_index(struct hda_codec *codec, hda_nid_t mux,
+ hda_nid_t nid, int recursive);
+int snd_hda_get_devices(struct hda_codec *codec, hda_nid_t nid,
+ u8 *dev_list, int max_devices);
+int snd_hda_query_supported_pcm(struct hda_codec *codec, hda_nid_t nid,
+ u32 *ratesp, u64 *formatsp, unsigned int *bpsp);
struct hda_verb {
hda_nid_t nid;
@@ -916,25 +474,21 @@ void snd_hda_sequence_write(struct hda_codec *codec,
int snd_hda_queue_unsol_event(struct hda_bus *bus, u32 res, u32 res_ex);
/* cached write */
-#ifdef SND_HDA_NEEDS_RESUME
int snd_hda_codec_write_cache(struct hda_codec *codec, hda_nid_t nid,
- int direct, unsigned int verb, unsigned int parm);
+ int flags, unsigned int verb, unsigned int parm);
void snd_hda_sequence_write_cache(struct hda_codec *codec,
const struct hda_verb *seq);
int snd_hda_codec_update_cache(struct hda_codec *codec, hda_nid_t nid,
- int direct, unsigned int verb, unsigned int parm);
+ int flags, unsigned int verb, unsigned int parm);
void snd_hda_codec_resume_cache(struct hda_codec *codec);
-#else
-#define snd_hda_codec_write_cache snd_hda_codec_write
-#define snd_hda_codec_update_cache snd_hda_codec_write
-#define snd_hda_sequence_write_cache snd_hda_sequence_write
-#endif
+/* both for cmd & amp caches */
+void snd_hda_codec_flush_cache(struct hda_codec *codec);
/* the struct for codec->pin_configs */
struct hda_pincfg {
hda_nid_t nid;
- unsigned char ctrl; /* current pin control value */
- unsigned char pad; /* reserved */
+ unsigned char ctrl; /* original pin control value */
+ unsigned char target; /* target pin control value */
unsigned int cfg; /* default configuration */
};
@@ -945,6 +499,17 @@ int snd_hda_add_pincfg(struct hda_codec *codec, struct snd_array *list,
hda_nid_t nid, unsigned int cfg); /* for hwdep */
void snd_hda_shutup_pins(struct hda_codec *codec);
+/* SPDIF controls */
+struct hda_spdif_out {
+ hda_nid_t nid; /* Converter nid values relate to */
+ unsigned int status; /* IEC958 status bits */
+ unsigned short ctls; /* SPDIF control bits */
+};
+struct hda_spdif_out *snd_hda_spdif_out_of_nid(struct hda_codec *codec,
+ hda_nid_t nid);
+void snd_hda_spdif_ctls_unassign(struct hda_codec *codec, int idx);
+void snd_hda_spdif_ctls_assign(struct hda_codec *codec, int idx, hda_nid_t nid);
+
/*
* Mixer
*/
@@ -981,11 +546,18 @@ unsigned int snd_hda_calc_stream_format(unsigned int rate,
int snd_hda_is_supported_format(struct hda_codec *codec, hda_nid_t nid,
unsigned int format);
+extern const struct snd_pcm_chmap_elem snd_pcm_2_1_chmaps[];
+
/*
* Misc
*/
void snd_hda_get_codec_name(struct hda_codec *codec, char *name, int namelen);
void snd_hda_bus_reboot_notify(struct hda_bus *bus);
+void snd_hda_codec_set_power_to_all(struct hda_codec *codec, hda_nid_t fg,
+ unsigned int power_state);
+
+int snd_hda_lock_devices(struct hda_bus *bus);
+void snd_hda_unlock_devices(struct hda_bus *bus);
/*
* power management
@@ -995,17 +567,15 @@ int snd_hda_suspend(struct hda_bus *bus);
int snd_hda_resume(struct hda_bus *bus);
#endif
-#ifdef CONFIG_SND_HDA_POWER_SAVE
static inline
int hda_call_check_power_status(struct hda_codec *codec, hda_nid_t nid)
{
+#ifdef CONFIG_PM
if (codec->patch_ops.check_power_status)
return codec->patch_ops.check_power_status(codec, nid);
+#endif
return 0;
}
-#else
-#define hda_call_check_power_status(codec, nid) 0
-#endif
/*
* get widget information
@@ -1017,37 +587,106 @@ const char *snd_hda_get_jack_location(u32 cfg);
/*
* power saving
*/
-#ifdef CONFIG_SND_HDA_POWER_SAVE
-void snd_hda_power_up(struct hda_codec *codec);
-void snd_hda_power_down(struct hda_codec *codec);
-#define snd_hda_codec_needs_resume(codec) codec->power_count
+#ifdef CONFIG_PM
+void snd_hda_power_save(struct hda_codec *codec, int delta, bool d3wait);
void snd_hda_update_power_acct(struct hda_codec *codec);
#else
-static inline void snd_hda_power_up(struct hda_codec *codec) {}
-static inline void snd_hda_power_down(struct hda_codec *codec) {}
-#define snd_hda_codec_needs_resume(codec) 1
+static inline void snd_hda_power_save(struct hda_codec *codec, int delta,
+ bool d3wait) {}
#endif
+/**
+ * snd_hda_power_up - Power-up the codec
+ * @codec: HD-audio codec
+ *
+ * Increment the power-up counter and power up the hardware really when
+ * not turned on yet.
+ */
+static inline void snd_hda_power_up(struct hda_codec *codec)
+{
+ snd_hda_power_save(codec, 1, false);
+}
+
+/**
+ * snd_hda_power_up_d3wait - Power-up the codec after waiting for any pending
+ * D3 transition to complete. This differs from snd_hda_power_up() when
+ * power_transition == -1. snd_hda_power_up sees this case as a nop,
+ * snd_hda_power_up_d3wait waits for the D3 transition to complete then powers
+ * back up.
+ * @codec: HD-audio codec
+ *
+ * Cancel any power down operation hapenning on the work queue, then power up.
+ */
+static inline void snd_hda_power_up_d3wait(struct hda_codec *codec)
+{
+ snd_hda_power_save(codec, 1, true);
+}
+
+/**
+ * snd_hda_power_down - Power-down the codec
+ * @codec: HD-audio codec
+ *
+ * Decrement the power-up counter and schedules the power-off work if
+ * the counter rearches to zero.
+ */
+static inline void snd_hda_power_down(struct hda_codec *codec)
+{
+ snd_hda_power_save(codec, -1, false);
+}
+
+/**
+ * snd_hda_power_sync - Synchronize the power-save status
+ * @codec: HD-audio codec
+ *
+ * Synchronize the actual power state with the power account;
+ * called when power_save parameter is changed
+ */
+static inline void snd_hda_power_sync(struct hda_codec *codec)
+{
+ snd_hda_power_save(codec, 0, false);
+}
+
#ifdef CONFIG_SND_HDA_PATCH_LOADER
/*
* patch firmware
*/
-int snd_hda_load_patch(struct hda_bus *bus, const char *patch);
+int snd_hda_load_patch(struct hda_bus *bus, size_t size, const void *buf);
#endif
-/*
- * Codec modularization
- */
-
-/* Export symbols only for communication with codec drivers;
- * When built in kernel, all HD-audio drivers are supposed to be statically
- * linked to the kernel. Thus, the symbols don't have to (or shouldn't) be
- * exported unless it's built as a module.
- */
-#ifdef MODULE
-#define EXPORT_SYMBOL_HDA(sym) EXPORT_SYMBOL_GPL(sym)
+#ifdef CONFIG_SND_HDA_DSP_LOADER
+static inline int
+snd_hda_codec_load_dsp_prepare(struct hda_codec *codec, unsigned int format,
+ unsigned int size,
+ struct snd_dma_buffer *bufp)
+{
+ return codec->bus->ops.load_dsp_prepare(codec->bus, format, size, bufp);
+}
+static inline void
+snd_hda_codec_load_dsp_trigger(struct hda_codec *codec, bool start)
+{
+ return codec->bus->ops.load_dsp_trigger(codec->bus, start);
+}
+static inline void
+snd_hda_codec_load_dsp_cleanup(struct hda_codec *codec,
+ struct snd_dma_buffer *dmab)
+{
+ return codec->bus->ops.load_dsp_cleanup(codec->bus, dmab);
+}
#else
-#define EXPORT_SYMBOL_HDA(sym)
+static inline int
+snd_hda_codec_load_dsp_prepare(struct hda_codec *codec, unsigned int format,
+ unsigned int size,
+ struct snd_dma_buffer *bufp)
+{
+ return -ENOSYS;
+}
+static inline void
+snd_hda_codec_load_dsp_trigger(struct hda_codec *codec, bool start) {}
+static inline void
+snd_hda_codec_load_dsp_cleanup(struct hda_codec *codec,
+ struct snd_dma_buffer *dmab) {}
#endif
+#define EXPORT_SYMBOL_HDA(sym) EXPORT_SYMBOL_GPL(sym)
+
#endif /* __SOUND_HDA_CODEC_H */
diff --git a/sound/pci/hda/hda_controller.c b/sound/pci/hda/hda_controller.c
new file mode 100644
index 00000000000..6df04d91c93
--- /dev/null
+++ b/sound/pci/hda/hda_controller.c
@@ -0,0 +1,2035 @@
+/*
+ *
+ * Implementation of primary alsa driver code base for Intel HD Audio.
+ *
+ * Copyright(c) 2004 Intel Corporation. All rights reserved.
+ *
+ * Copyright (c) 2004 Takashi Iwai <tiwai@suse.de>
+ * PeiSen Hou <pshou@realtek.com.tw>
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the Free
+ * Software Foundation; either version 2 of the License, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ *
+ */
+
+#include <linux/clocksource.h>
+#include <linux/delay.h>
+#include <linux/interrupt.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/pm_runtime.h>
+#include <linux/slab.h>
+#include <sound/core.h>
+#include <sound/initval.h>
+#include "hda_priv.h"
+#include "hda_controller.h"
+
+#define CREATE_TRACE_POINTS
+#include "hda_intel_trace.h"
+
+/* DSP lock helpers */
+#ifdef CONFIG_SND_HDA_DSP_LOADER
+#define dsp_lock_init(dev) mutex_init(&(dev)->dsp_mutex)
+#define dsp_lock(dev) mutex_lock(&(dev)->dsp_mutex)
+#define dsp_unlock(dev) mutex_unlock(&(dev)->dsp_mutex)
+#define dsp_is_locked(dev) ((dev)->locked)
+#else
+#define dsp_lock_init(dev) do {} while (0)
+#define dsp_lock(dev) do {} while (0)
+#define dsp_unlock(dev) do {} while (0)
+#define dsp_is_locked(dev) 0
+#endif
+
+/*
+ * AZX stream operations.
+ */
+
+/* start a stream */
+static void azx_stream_start(struct azx *chip, struct azx_dev *azx_dev)
+{
+ /*
+ * Before stream start, initialize parameter
+ */
+ azx_dev->insufficient = 1;
+
+ /* enable SIE */
+ azx_writel(chip, INTCTL,
+ azx_readl(chip, INTCTL) | (1 << azx_dev->index));
+ /* set DMA start and interrupt mask */
+ azx_sd_writeb(chip, azx_dev, SD_CTL,
+ azx_sd_readb(chip, azx_dev, SD_CTL) |
+ SD_CTL_DMA_START | SD_INT_MASK);
+}
+
+/* stop DMA */
+static void azx_stream_clear(struct azx *chip, struct azx_dev *azx_dev)
+{
+ azx_sd_writeb(chip, azx_dev, SD_CTL,
+ azx_sd_readb(chip, azx_dev, SD_CTL) &
+ ~(SD_CTL_DMA_START | SD_INT_MASK));
+ azx_sd_writeb(chip, azx_dev, SD_STS, SD_INT_MASK); /* to be sure */
+}
+
+/* stop a stream */
+void azx_stream_stop(struct azx *chip, struct azx_dev *azx_dev)
+{
+ azx_stream_clear(chip, azx_dev);
+ /* disable SIE */
+ azx_writel(chip, INTCTL,
+ azx_readl(chip, INTCTL) & ~(1 << azx_dev->index));
+}
+EXPORT_SYMBOL_GPL(azx_stream_stop);
+
+/* reset stream */
+static void azx_stream_reset(struct azx *chip, struct azx_dev *azx_dev)
+{
+ unsigned char val;
+ int timeout;
+
+ azx_stream_clear(chip, azx_dev);
+
+ azx_sd_writeb(chip, azx_dev, SD_CTL,
+ azx_sd_readb(chip, azx_dev, SD_CTL) |
+ SD_CTL_STREAM_RESET);
+ udelay(3);
+ timeout = 300;
+ while (!((val = azx_sd_readb(chip, azx_dev, SD_CTL)) &
+ SD_CTL_STREAM_RESET) && --timeout)
+ ;
+ val &= ~SD_CTL_STREAM_RESET;
+ azx_sd_writeb(chip, azx_dev, SD_CTL, val);
+ udelay(3);
+
+ timeout = 300;
+ /* waiting for hardware to report that the stream is out of reset */
+ while (((val = azx_sd_readb(chip, azx_dev, SD_CTL)) &
+ SD_CTL_STREAM_RESET) && --timeout)
+ ;
+
+ /* reset first position - may not be synced with hw at this time */
+ *azx_dev->posbuf = 0;
+}
+
+/*
+ * set up the SD for streaming
+ */
+static int azx_setup_controller(struct azx *chip, struct azx_dev *azx_dev)
+{
+ unsigned int val;
+ /* make sure the run bit is zero for SD */
+ azx_stream_clear(chip, azx_dev);
+ /* program the stream_tag */
+ val = azx_sd_readl(chip, azx_dev, SD_CTL);
+ val = (val & ~SD_CTL_STREAM_TAG_MASK) |
+ (azx_dev->stream_tag << SD_CTL_STREAM_TAG_SHIFT);
+ if (!azx_snoop(chip))
+ val |= SD_CTL_TRAFFIC_PRIO;
+ azx_sd_writel(chip, azx_dev, SD_CTL, val);
+
+ /* program the length of samples in cyclic buffer */
+ azx_sd_writel(chip, azx_dev, SD_CBL, azx_dev->bufsize);
+
+ /* program the stream format */
+ /* this value needs to be the same as the one programmed */
+ azx_sd_writew(chip, azx_dev, SD_FORMAT, azx_dev->format_val);
+
+ /* program the stream LVI (last valid index) of the BDL */
+ azx_sd_writew(chip, azx_dev, SD_LVI, azx_dev->frags - 1);
+
+ /* program the BDL address */
+ /* lower BDL address */
+ azx_sd_writel(chip, azx_dev, SD_BDLPL, (u32)azx_dev->bdl.addr);
+ /* upper BDL address */
+ azx_sd_writel(chip, azx_dev, SD_BDLPU,
+ upper_32_bits(azx_dev->bdl.addr));
+
+ /* enable the position buffer */
+ if (chip->position_fix[0] != POS_FIX_LPIB ||
+ chip->position_fix[1] != POS_FIX_LPIB) {
+ if (!(azx_readl(chip, DPLBASE) & ICH6_DPLBASE_ENABLE))
+ azx_writel(chip, DPLBASE,
+ (u32)chip->posbuf.addr | ICH6_DPLBASE_ENABLE);
+ }
+
+ /* set the interrupt enable bits in the descriptor control register */
+ azx_sd_writel(chip, azx_dev, SD_CTL,
+ azx_sd_readl(chip, azx_dev, SD_CTL) | SD_INT_MASK);
+
+ return 0;
+}
+
+/* assign a stream for the PCM */
+static inline struct azx_dev *
+azx_assign_device(struct azx *chip, struct snd_pcm_substream *substream)
+{
+ int dev, i, nums;
+ struct azx_dev *res = NULL;
+ /* make a non-zero unique key for the substream */
+ int key = (substream->pcm->device << 16) | (substream->number << 2) |
+ (substream->stream + 1);
+
+ if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
+ dev = chip->playback_index_offset;
+ nums = chip->playback_streams;
+ } else {
+ dev = chip->capture_index_offset;
+ nums = chip->capture_streams;
+ }
+ for (i = 0; i < nums; i++, dev++) {
+ struct azx_dev *azx_dev = &chip->azx_dev[dev];
+ dsp_lock(azx_dev);
+ if (!azx_dev->opened && !dsp_is_locked(azx_dev)) {
+ if (azx_dev->assigned_key == key) {
+ azx_dev->opened = 1;
+ azx_dev->assigned_key = key;
+ dsp_unlock(azx_dev);
+ return azx_dev;
+ }
+ if (!res ||
+ (chip->driver_caps & AZX_DCAPS_REVERSE_ASSIGN))
+ res = azx_dev;
+ }
+ dsp_unlock(azx_dev);
+ }
+ if (res) {
+ dsp_lock(res);
+ res->opened = 1;
+ res->assigned_key = key;
+ dsp_unlock(res);
+ }
+ return res;
+}
+
+/* release the assigned stream */
+static inline void azx_release_device(struct azx_dev *azx_dev)
+{
+ azx_dev->opened = 0;
+}
+
+static cycle_t azx_cc_read(const struct cyclecounter *cc)
+{
+ struct azx_dev *azx_dev = container_of(cc, struct azx_dev, azx_cc);
+ struct snd_pcm_substream *substream = azx_dev->substream;
+ struct azx_pcm *apcm = snd_pcm_substream_chip(substream);
+ struct azx *chip = apcm->chip;
+
+ return azx_readl(chip, WALLCLK);
+}
+
+static void azx_timecounter_init(struct snd_pcm_substream *substream,
+ bool force, cycle_t last)
+{
+ struct azx_dev *azx_dev = get_azx_dev(substream);
+ struct timecounter *tc = &azx_dev->azx_tc;
+ struct cyclecounter *cc = &azx_dev->azx_cc;
+ u64 nsec;
+
+ cc->read = azx_cc_read;
+ cc->mask = CLOCKSOURCE_MASK(32);
+
+ /*
+ * Converting from 24 MHz to ns means applying a 125/3 factor.
+ * To avoid any saturation issues in intermediate operations,
+ * the 125 factor is applied first. The division is applied
+ * last after reading the timecounter value.
+ * Applying the 1/3 factor as part of the multiplication
+ * requires at least 20 bits for a decent precision, however
+ * overflows occur after about 4 hours or less, not a option.
+ */
+
+ cc->mult = 125; /* saturation after 195 years */
+ cc->shift = 0;
+
+ nsec = 0; /* audio time is elapsed time since trigger */
+ timecounter_init(tc, cc, nsec);
+ if (force)
+ /*
+ * force timecounter to use predefined value,
+ * used for synchronized starts
+ */
+ tc->cycle_last = last;
+}
+
+static u64 azx_adjust_codec_delay(struct snd_pcm_substream *substream,
+ u64 nsec)
+{
+ struct azx_pcm *apcm = snd_pcm_substream_chip(substream);
+ struct hda_pcm_stream *hinfo = apcm->hinfo[substream->stream];
+ u64 codec_frames, codec_nsecs;
+
+ if (!hinfo->ops.get_delay)
+ return nsec;
+
+ codec_frames = hinfo->ops.get_delay(hinfo, apcm->codec, substream);
+ codec_nsecs = div_u64(codec_frames * 1000000000LL,
+ substream->runtime->rate);
+
+ if (substream->stream == SNDRV_PCM_STREAM_CAPTURE)
+ return nsec + codec_nsecs;
+
+ return (nsec > codec_nsecs) ? nsec - codec_nsecs : 0;
+}
+
+/*
+ * set up a BDL entry
+ */
+static int setup_bdle(struct azx *chip,
+ struct snd_dma_buffer *dmab,
+ struct azx_dev *azx_dev, u32 **bdlp,
+ int ofs, int size, int with_ioc)
+{
+ u32 *bdl = *bdlp;
+
+ while (size > 0) {
+ dma_addr_t addr;
+ int chunk;
+
+ if (azx_dev->frags >= AZX_MAX_BDL_ENTRIES)
+ return -EINVAL;
+
+ addr = snd_sgbuf_get_addr(dmab, ofs);
+ /* program the address field of the BDL entry */
+ bdl[0] = cpu_to_le32((u32)addr);
+ bdl[1] = cpu_to_le32(upper_32_bits(addr));
+ /* program the size field of the BDL entry */
+ chunk = snd_sgbuf_get_chunk_size(dmab, ofs, size);
+ /* one BDLE cannot cross 4K boundary on CTHDA chips */
+ if (chip->driver_caps & AZX_DCAPS_4K_BDLE_BOUNDARY) {
+ u32 remain = 0x1000 - (ofs & 0xfff);
+ if (chunk > remain)
+ chunk = remain;
+ }
+ bdl[2] = cpu_to_le32(chunk);
+ /* program the IOC to enable interrupt
+ * only when the whole fragment is processed
+ */
+ size -= chunk;
+ bdl[3] = (size || !with_ioc) ? 0 : cpu_to_le32(0x01);
+ bdl += 4;
+ azx_dev->frags++;
+ ofs += chunk;
+ }
+ *bdlp = bdl;
+ return ofs;
+}
+
+/*
+ * set up BDL entries
+ */
+static int azx_setup_periods(struct azx *chip,
+ struct snd_pcm_substream *substream,
+ struct azx_dev *azx_dev)
+{
+ u32 *bdl;
+ int i, ofs, periods, period_bytes;
+ int pos_adj = 0;
+
+ /* reset BDL address */
+ azx_sd_writel(chip, azx_dev, SD_BDLPL, 0);
+ azx_sd_writel(chip, azx_dev, SD_BDLPU, 0);
+
+ period_bytes = azx_dev->period_bytes;
+ periods = azx_dev->bufsize / period_bytes;
+
+ /* program the initial BDL entries */
+ bdl = (u32 *)azx_dev->bdl.area;
+ ofs = 0;
+ azx_dev->frags = 0;
+
+ if (chip->bdl_pos_adj)
+ pos_adj = chip->bdl_pos_adj[chip->dev_index];
+ if (!azx_dev->no_period_wakeup && pos_adj > 0) {
+ struct snd_pcm_runtime *runtime = substream->runtime;
+ int pos_align = pos_adj;
+ pos_adj = (pos_adj * runtime->rate + 47999) / 48000;
+ if (!pos_adj)
+ pos_adj = pos_align;
+ else
+ pos_adj = ((pos_adj + pos_align - 1) / pos_align) *
+ pos_align;
+ pos_adj = frames_to_bytes(runtime, pos_adj);
+ if (pos_adj >= period_bytes) {
+ dev_warn(chip->card->dev,"Too big adjustment %d\n",
+ pos_adj);
+ pos_adj = 0;
+ } else {
+ ofs = setup_bdle(chip, snd_pcm_get_dma_buf(substream),
+ azx_dev,
+ &bdl, ofs, pos_adj, true);
+ if (ofs < 0)
+ goto error;
+ }
+ } else
+ pos_adj = 0;
+
+ for (i = 0; i < periods; i++) {
+ if (i == periods - 1 && pos_adj)
+ ofs = setup_bdle(chip, snd_pcm_get_dma_buf(substream),
+ azx_dev, &bdl, ofs,
+ period_bytes - pos_adj, 0);
+ else
+ ofs = setup_bdle(chip, snd_pcm_get_dma_buf(substream),
+ azx_dev, &bdl, ofs,
+ period_bytes,
+ !azx_dev->no_period_wakeup);
+ if (ofs < 0)
+ goto error;
+ }
+ return 0;
+
+ error:
+ dev_err(chip->card->dev, "Too many BDL entries: buffer=%d, period=%d\n",
+ azx_dev->bufsize, period_bytes);
+ return -EINVAL;
+}
+
+/*
+ * PCM ops
+ */
+
+static int azx_pcm_close(struct snd_pcm_substream *substream)
+{
+ struct azx_pcm *apcm = snd_pcm_substream_chip(substream);
+ struct hda_pcm_stream *hinfo = apcm->hinfo[substream->stream];
+ struct azx *chip = apcm->chip;
+ struct azx_dev *azx_dev = get_azx_dev(substream);
+ unsigned long flags;
+
+ mutex_lock(&chip->open_mutex);
+ spin_lock_irqsave(&chip->reg_lock, flags);
+ azx_dev->substream = NULL;
+ azx_dev->running = 0;
+ spin_unlock_irqrestore(&chip->reg_lock, flags);
+ azx_release_device(azx_dev);
+ hinfo->ops.close(hinfo, apcm->codec, substream);
+ snd_hda_power_down(apcm->codec);
+ mutex_unlock(&chip->open_mutex);
+ return 0;
+}
+
+static int azx_pcm_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *hw_params)
+{
+ struct azx_pcm *apcm = snd_pcm_substream_chip(substream);
+ struct azx *chip = apcm->chip;
+ int ret;
+
+ dsp_lock(get_azx_dev(substream));
+ if (dsp_is_locked(get_azx_dev(substream))) {
+ ret = -EBUSY;
+ goto unlock;
+ }
+
+ ret = chip->ops->substream_alloc_pages(chip, substream,
+ params_buffer_bytes(hw_params));
+unlock:
+ dsp_unlock(get_azx_dev(substream));
+ return ret;
+}
+
+static int azx_pcm_hw_free(struct snd_pcm_substream *substream)
+{
+ struct azx_pcm *apcm = snd_pcm_substream_chip(substream);
+ struct azx_dev *azx_dev = get_azx_dev(substream);
+ struct azx *chip = apcm->chip;
+ struct hda_pcm_stream *hinfo = apcm->hinfo[substream->stream];
+ int err;
+
+ /* reset BDL address */
+ dsp_lock(azx_dev);
+ if (!dsp_is_locked(azx_dev)) {
+ azx_sd_writel(chip, azx_dev, SD_BDLPL, 0);
+ azx_sd_writel(chip, azx_dev, SD_BDLPU, 0);
+ azx_sd_writel(chip, azx_dev, SD_CTL, 0);
+ azx_dev->bufsize = 0;
+ azx_dev->period_bytes = 0;
+ azx_dev->format_val = 0;
+ }
+
+ snd_hda_codec_cleanup(apcm->codec, hinfo, substream);
+
+ err = chip->ops->substream_free_pages(chip, substream);
+ azx_dev->prepared = 0;
+ dsp_unlock(azx_dev);
+ return err;
+}
+
+static int azx_pcm_prepare(struct snd_pcm_substream *substream)
+{
+ struct azx_pcm *apcm = snd_pcm_substream_chip(substream);
+ struct azx *chip = apcm->chip;
+ struct azx_dev *azx_dev = get_azx_dev(substream);
+ struct hda_pcm_stream *hinfo = apcm->hinfo[substream->stream];
+ struct snd_pcm_runtime *runtime = substream->runtime;
+ unsigned int bufsize, period_bytes, format_val, stream_tag;
+ int err;
+ struct hda_spdif_out *spdif =
+ snd_hda_spdif_out_of_nid(apcm->codec, hinfo->nid);
+ unsigned short ctls = spdif ? spdif->ctls : 0;
+
+ dsp_lock(azx_dev);
+ if (dsp_is_locked(azx_dev)) {
+ err = -EBUSY;
+ goto unlock;
+ }
+
+ azx_stream_reset(chip, azx_dev);
+ format_val = snd_hda_calc_stream_format(runtime->rate,
+ runtime->channels,
+ runtime->format,
+ hinfo->maxbps,
+ ctls);
+ if (!format_val) {
+ dev_err(chip->card->dev,
+ "invalid format_val, rate=%d, ch=%d, format=%d\n",
+ runtime->rate, runtime->channels, runtime->format);
+ err = -EINVAL;
+ goto unlock;
+ }
+
+ bufsize = snd_pcm_lib_buffer_bytes(substream);
+ period_bytes = snd_pcm_lib_period_bytes(substream);
+
+ dev_dbg(chip->card->dev, "azx_pcm_prepare: bufsize=0x%x, format=0x%x\n",
+ bufsize, format_val);
+
+ if (bufsize != azx_dev->bufsize ||
+ period_bytes != azx_dev->period_bytes ||
+ format_val != azx_dev->format_val ||
+ runtime->no_period_wakeup != azx_dev->no_period_wakeup) {
+ azx_dev->bufsize = bufsize;
+ azx_dev->period_bytes = period_bytes;
+ azx_dev->format_val = format_val;
+ azx_dev->no_period_wakeup = runtime->no_period_wakeup;
+ err = azx_setup_periods(chip, substream, azx_dev);
+ if (err < 0)
+ goto unlock;
+ }
+
+ /* when LPIB delay correction gives a small negative value,
+ * we ignore it; currently set the threshold statically to
+ * 64 frames
+ */
+ if (runtime->period_size > 64)
+ azx_dev->delay_negative_threshold = -frames_to_bytes(runtime, 64);
+ else
+ azx_dev->delay_negative_threshold = 0;
+
+ /* wallclk has 24Mhz clock source */
+ azx_dev->period_wallclk = (((runtime->period_size * 24000) /
+ runtime->rate) * 1000);
+ azx_setup_controller(chip, azx_dev);
+ if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
+ azx_dev->fifo_size =
+ azx_sd_readw(chip, azx_dev, SD_FIFOSIZE) + 1;
+ else
+ azx_dev->fifo_size = 0;
+
+ stream_tag = azx_dev->stream_tag;
+ /* CA-IBG chips need the playback stream starting from 1 */
+ if ((chip->driver_caps & AZX_DCAPS_CTX_WORKAROUND) &&
+ stream_tag > chip->capture_streams)
+ stream_tag -= chip->capture_streams;
+ err = snd_hda_codec_prepare(apcm->codec, hinfo, stream_tag,
+ azx_dev->format_val, substream);
+
+ unlock:
+ if (!err)
+ azx_dev->prepared = 1;
+ dsp_unlock(azx_dev);
+ return err;
+}
+
+static int azx_pcm_trigger(struct snd_pcm_substream *substream, int cmd)
+{
+ struct azx_pcm *apcm = snd_pcm_substream_chip(substream);
+ struct azx *chip = apcm->chip;
+ struct azx_dev *azx_dev;
+ struct snd_pcm_substream *s;
+ int rstart = 0, start, nsync = 0, sbits = 0;
+ int nwait, timeout;
+
+ azx_dev = get_azx_dev(substream);
+ trace_azx_pcm_trigger(chip, azx_dev, cmd);
+
+ if (dsp_is_locked(azx_dev) || !azx_dev->prepared)
+ return -EPIPE;
+
+ switch (cmd) {
+ case SNDRV_PCM_TRIGGER_START:
+ rstart = 1;
+ case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
+ case SNDRV_PCM_TRIGGER_RESUME:
+ start = 1;
+ break;
+ case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
+ case SNDRV_PCM_TRIGGER_SUSPEND:
+ case SNDRV_PCM_TRIGGER_STOP:
+ start = 0;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ snd_pcm_group_for_each_entry(s, substream) {
+ if (s->pcm->card != substream->pcm->card)
+ continue;
+ azx_dev = get_azx_dev(s);
+ sbits |= 1 << azx_dev->index;
+ nsync++;
+ snd_pcm_trigger_done(s, substream);
+ }
+
+ spin_lock(&chip->reg_lock);
+
+ /* first, set SYNC bits of corresponding streams */
+ if (chip->driver_caps & AZX_DCAPS_OLD_SSYNC)
+ azx_writel(chip, OLD_SSYNC,
+ azx_readl(chip, OLD_SSYNC) | sbits);
+ else
+ azx_writel(chip, SSYNC, azx_readl(chip, SSYNC) | sbits);
+
+ snd_pcm_group_for_each_entry(s, substream) {
+ if (s->pcm->card != substream->pcm->card)
+ continue;
+ azx_dev = get_azx_dev(s);
+ if (start) {
+ azx_dev->start_wallclk = azx_readl(chip, WALLCLK);
+ if (!rstart)
+ azx_dev->start_wallclk -=
+ azx_dev->period_wallclk;
+ azx_stream_start(chip, azx_dev);
+ } else {
+ azx_stream_stop(chip, azx_dev);
+ }
+ azx_dev->running = start;
+ }
+ spin_unlock(&chip->reg_lock);
+ if (start) {
+ /* wait until all FIFOs get ready */
+ for (timeout = 5000; timeout; timeout--) {
+ nwait = 0;
+ snd_pcm_group_for_each_entry(s, substream) {
+ if (s->pcm->card != substream->pcm->card)
+ continue;
+ azx_dev = get_azx_dev(s);
+ if (!(azx_sd_readb(chip, azx_dev, SD_STS) &
+ SD_STS_FIFO_READY))
+ nwait++;
+ }
+ if (!nwait)
+ break;
+ cpu_relax();
+ }
+ } else {
+ /* wait until all RUN bits are cleared */
+ for (timeout = 5000; timeout; timeout--) {
+ nwait = 0;
+ snd_pcm_group_for_each_entry(s, substream) {
+ if (s->pcm->card != substream->pcm->card)
+ continue;
+ azx_dev = get_azx_dev(s);
+ if (azx_sd_readb(chip, azx_dev, SD_CTL) &
+ SD_CTL_DMA_START)
+ nwait++;
+ }
+ if (!nwait)
+ break;
+ cpu_relax();
+ }
+ }
+ spin_lock(&chip->reg_lock);
+ /* reset SYNC bits */
+ if (chip->driver_caps & AZX_DCAPS_OLD_SSYNC)
+ azx_writel(chip, OLD_SSYNC,
+ azx_readl(chip, OLD_SSYNC) & ~sbits);
+ else
+ azx_writel(chip, SSYNC, azx_readl(chip, SSYNC) & ~sbits);
+ if (start) {
+ azx_timecounter_init(substream, 0, 0);
+ if (nsync > 1) {
+ cycle_t cycle_last;
+
+ /* same start cycle for master and group */
+ azx_dev = get_azx_dev(substream);
+ cycle_last = azx_dev->azx_tc.cycle_last;
+
+ snd_pcm_group_for_each_entry(s, substream) {
+ if (s->pcm->card != substream->pcm->card)
+ continue;
+ azx_timecounter_init(s, 1, cycle_last);
+ }
+ }
+ }
+ spin_unlock(&chip->reg_lock);
+ return 0;
+}
+
+/* get the current DMA position with correction on VIA chips */
+static unsigned int azx_via_get_position(struct azx *chip,
+ struct azx_dev *azx_dev)
+{
+ unsigned int link_pos, mini_pos, bound_pos;
+ unsigned int mod_link_pos, mod_dma_pos, mod_mini_pos;
+ unsigned int fifo_size;
+
+ link_pos = azx_sd_readl(chip, azx_dev, SD_LPIB);
+ if (azx_dev->substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
+ /* Playback, no problem using link position */
+ return link_pos;
+ }
+
+ /* Capture */
+ /* For new chipset,
+ * use mod to get the DMA position just like old chipset
+ */
+ mod_dma_pos = le32_to_cpu(*azx_dev->posbuf);
+ mod_dma_pos %= azx_dev->period_bytes;
+
+ /* azx_dev->fifo_size can't get FIFO size of in stream.
+ * Get from base address + offset.
+ */
+ fifo_size = readw(chip->remap_addr + VIA_IN_STREAM0_FIFO_SIZE_OFFSET);
+
+ if (azx_dev->insufficient) {
+ /* Link position never gather than FIFO size */
+ if (link_pos <= fifo_size)
+ return 0;
+
+ azx_dev->insufficient = 0;
+ }
+
+ if (link_pos <= fifo_size)
+ mini_pos = azx_dev->bufsize + link_pos - fifo_size;
+ else
+ mini_pos = link_pos - fifo_size;
+
+ /* Find nearest previous boudary */
+ mod_mini_pos = mini_pos % azx_dev->period_bytes;
+ mod_link_pos = link_pos % azx_dev->period_bytes;
+ if (mod_link_pos >= fifo_size)
+ bound_pos = link_pos - mod_link_pos;
+ else if (mod_dma_pos >= mod_mini_pos)
+ bound_pos = mini_pos - mod_mini_pos;
+ else {
+ bound_pos = mini_pos - mod_mini_pos + azx_dev->period_bytes;
+ if (bound_pos >= azx_dev->bufsize)
+ bound_pos = 0;
+ }
+
+ /* Calculate real DMA position we want */
+ return bound_pos + mod_dma_pos;
+}
+
+unsigned int azx_get_position(struct azx *chip,
+ struct azx_dev *azx_dev,
+ bool with_check)
+{
+ struct snd_pcm_substream *substream = azx_dev->substream;
+ struct azx_pcm *apcm = snd_pcm_substream_chip(substream);
+ unsigned int pos;
+ int stream = substream->stream;
+ struct hda_pcm_stream *hinfo = apcm->hinfo[stream];
+ int delay = 0;
+
+ switch (chip->position_fix[stream]) {
+ case POS_FIX_LPIB:
+ /* read LPIB */
+ pos = azx_sd_readl(chip, azx_dev, SD_LPIB);
+ break;
+ case POS_FIX_VIACOMBO:
+ pos = azx_via_get_position(chip, azx_dev);
+ break;
+ default:
+ /* use the position buffer */
+ pos = le32_to_cpu(*azx_dev->posbuf);
+ if (with_check && chip->position_fix[stream] == POS_FIX_AUTO) {
+ if (!pos || pos == (u32)-1) {
+ dev_info(chip->card->dev,
+ "Invalid position buffer, using LPIB read method instead.\n");
+ chip->position_fix[stream] = POS_FIX_LPIB;
+ pos = azx_sd_readl(chip, azx_dev, SD_LPIB);
+ } else
+ chip->position_fix[stream] = POS_FIX_POSBUF;
+ }
+ break;
+ }
+
+ if (pos >= azx_dev->bufsize)
+ pos = 0;
+
+ /* calculate runtime delay from LPIB */
+ if (substream->runtime &&
+ chip->position_fix[stream] == POS_FIX_POSBUF &&
+ (chip->driver_caps & AZX_DCAPS_COUNT_LPIB_DELAY)) {
+ unsigned int lpib_pos = azx_sd_readl(chip, azx_dev, SD_LPIB);
+ if (stream == SNDRV_PCM_STREAM_PLAYBACK)
+ delay = pos - lpib_pos;
+ else
+ delay = lpib_pos - pos;
+ if (delay < 0) {
+ if (delay >= azx_dev->delay_negative_threshold)
+ delay = 0;
+ else
+ delay += azx_dev->bufsize;
+ }
+ if (delay >= azx_dev->period_bytes) {
+ dev_info(chip->card->dev,
+ "Unstable LPIB (%d >= %d); disabling LPIB delay counting\n",
+ delay, azx_dev->period_bytes);
+ delay = 0;
+ chip->driver_caps &= ~AZX_DCAPS_COUNT_LPIB_DELAY;
+ }
+ delay = bytes_to_frames(substream->runtime, delay);
+ }
+
+ if (substream->runtime) {
+ if (hinfo->ops.get_delay)
+ delay += hinfo->ops.get_delay(hinfo, apcm->codec,
+ substream);
+ substream->runtime->delay = delay;
+ }
+
+ trace_azx_get_position(chip, azx_dev, pos, delay);
+ return pos;
+}
+EXPORT_SYMBOL_GPL(azx_get_position);
+
+static snd_pcm_uframes_t azx_pcm_pointer(struct snd_pcm_substream *substream)
+{
+ struct azx_pcm *apcm = snd_pcm_substream_chip(substream);
+ struct azx *chip = apcm->chip;
+ struct azx_dev *azx_dev = get_azx_dev(substream);
+ return bytes_to_frames(substream->runtime,
+ azx_get_position(chip, azx_dev, false));
+}
+
+static int azx_get_wallclock_tstamp(struct snd_pcm_substream *substream,
+ struct timespec *ts)
+{
+ struct azx_dev *azx_dev = get_azx_dev(substream);
+ u64 nsec;
+
+ nsec = timecounter_read(&azx_dev->azx_tc);
+ nsec = div_u64(nsec, 3); /* can be optimized */
+ nsec = azx_adjust_codec_delay(substream, nsec);
+
+ *ts = ns_to_timespec(nsec);
+
+ return 0;
+}
+
+static struct snd_pcm_hardware azx_pcm_hw = {
+ .info = (SNDRV_PCM_INFO_MMAP |
+ SNDRV_PCM_INFO_INTERLEAVED |
+ SNDRV_PCM_INFO_BLOCK_TRANSFER |
+ SNDRV_PCM_INFO_MMAP_VALID |
+ /* No full-resume yet implemented */
+ /* SNDRV_PCM_INFO_RESUME |*/
+ SNDRV_PCM_INFO_PAUSE |
+ SNDRV_PCM_INFO_SYNC_START |
+ SNDRV_PCM_INFO_HAS_WALL_CLOCK |
+ SNDRV_PCM_INFO_NO_PERIOD_WAKEUP),
+ .formats = SNDRV_PCM_FMTBIT_S16_LE,
+ .rates = SNDRV_PCM_RATE_48000,
+ .rate_min = 48000,
+ .rate_max = 48000,
+ .channels_min = 2,
+ .channels_max = 2,
+ .buffer_bytes_max = AZX_MAX_BUF_SIZE,
+ .period_bytes_min = 128,
+ .period_bytes_max = AZX_MAX_BUF_SIZE / 2,
+ .periods_min = 2,
+ .periods_max = AZX_MAX_FRAG,
+ .fifo_size = 0,
+};
+
+static int azx_pcm_open(struct snd_pcm_substream *substream)
+{
+ struct azx_pcm *apcm = snd_pcm_substream_chip(substream);
+ struct hda_pcm_stream *hinfo = apcm->hinfo[substream->stream];
+ struct azx *chip = apcm->chip;
+ struct azx_dev *azx_dev;
+ struct snd_pcm_runtime *runtime = substream->runtime;
+ unsigned long flags;
+ int err;
+ int buff_step;
+
+ mutex_lock(&chip->open_mutex);
+ azx_dev = azx_assign_device(chip, substream);
+ if (azx_dev == NULL) {
+ mutex_unlock(&chip->open_mutex);
+ return -EBUSY;
+ }
+ runtime->hw = azx_pcm_hw;
+ runtime->hw.channels_min = hinfo->channels_min;
+ runtime->hw.channels_max = hinfo->channels_max;
+ runtime->hw.formats = hinfo->formats;
+ runtime->hw.rates = hinfo->rates;
+ snd_pcm_limit_hw_rates(runtime);
+ snd_pcm_hw_constraint_integer(runtime, SNDRV_PCM_HW_PARAM_PERIODS);
+
+ /* avoid wrap-around with wall-clock */
+ snd_pcm_hw_constraint_minmax(runtime, SNDRV_PCM_HW_PARAM_BUFFER_TIME,
+ 20,
+ 178000000);
+
+ if (chip->align_buffer_size)
+ /* constrain buffer sizes to be multiple of 128
+ bytes. This is more efficient in terms of memory
+ access but isn't required by the HDA spec and
+ prevents users from specifying exact period/buffer
+ sizes. For example for 44.1kHz, a period size set
+ to 20ms will be rounded to 19.59ms. */
+ buff_step = 128;
+ else
+ /* Don't enforce steps on buffer sizes, still need to
+ be multiple of 4 bytes (HDA spec). Tested on Intel
+ HDA controllers, may not work on all devices where
+ option needs to be disabled */
+ buff_step = 4;
+
+ snd_pcm_hw_constraint_step(runtime, 0, SNDRV_PCM_HW_PARAM_BUFFER_BYTES,
+ buff_step);
+ snd_pcm_hw_constraint_step(runtime, 0, SNDRV_PCM_HW_PARAM_PERIOD_BYTES,
+ buff_step);
+ snd_hda_power_up_d3wait(apcm->codec);
+ err = hinfo->ops.open(hinfo, apcm->codec, substream);
+ if (err < 0) {
+ azx_release_device(azx_dev);
+ snd_hda_power_down(apcm->codec);
+ mutex_unlock(&chip->open_mutex);
+ return err;
+ }
+ snd_pcm_limit_hw_rates(runtime);
+ /* sanity check */
+ if (snd_BUG_ON(!runtime->hw.channels_min) ||
+ snd_BUG_ON(!runtime->hw.channels_max) ||
+ snd_BUG_ON(!runtime->hw.formats) ||
+ snd_BUG_ON(!runtime->hw.rates)) {
+ azx_release_device(azx_dev);
+ hinfo->ops.close(hinfo, apcm->codec, substream);
+ snd_hda_power_down(apcm->codec);
+ mutex_unlock(&chip->open_mutex);
+ return -EINVAL;
+ }
+
+ /* disable WALLCLOCK timestamps for capture streams
+ until we figure out how to handle digital inputs */
+ if (substream->stream == SNDRV_PCM_STREAM_CAPTURE)
+ runtime->hw.info &= ~SNDRV_PCM_INFO_HAS_WALL_CLOCK;
+
+ spin_lock_irqsave(&chip->reg_lock, flags);
+ azx_dev->substream = substream;
+ azx_dev->running = 0;
+ spin_unlock_irqrestore(&chip->reg_lock, flags);
+
+ runtime->private_data = azx_dev;
+ snd_pcm_set_sync(substream);
+ mutex_unlock(&chip->open_mutex);
+ return 0;
+}
+
+static int azx_pcm_mmap(struct snd_pcm_substream *substream,
+ struct vm_area_struct *area)
+{
+ struct azx_pcm *apcm = snd_pcm_substream_chip(substream);
+ struct azx *chip = apcm->chip;
+ if (chip->ops->pcm_mmap_prepare)
+ chip->ops->pcm_mmap_prepare(substream, area);
+ return snd_pcm_lib_default_mmap(substream, area);
+}
+
+static struct snd_pcm_ops azx_pcm_ops = {
+ .open = azx_pcm_open,
+ .close = azx_pcm_close,
+ .ioctl = snd_pcm_lib_ioctl,
+ .hw_params = azx_pcm_hw_params,
+ .hw_free = azx_pcm_hw_free,
+ .prepare = azx_pcm_prepare,
+ .trigger = azx_pcm_trigger,
+ .pointer = azx_pcm_pointer,
+ .wall_clock = azx_get_wallclock_tstamp,
+ .mmap = azx_pcm_mmap,
+ .page = snd_pcm_sgbuf_ops_page,
+};
+
+static void azx_pcm_free(struct snd_pcm *pcm)
+{
+ struct azx_pcm *apcm = pcm->private_data;
+ if (apcm) {
+ list_del(&apcm->list);
+ kfree(apcm);
+ }
+}
+
+#define MAX_PREALLOC_SIZE (32 * 1024 * 1024)
+
+static int azx_attach_pcm_stream(struct hda_bus *bus, struct hda_codec *codec,
+ struct hda_pcm *cpcm)
+{
+ struct azx *chip = bus->private_data;
+ struct snd_pcm *pcm;
+ struct azx_pcm *apcm;
+ int pcm_dev = cpcm->device;
+ unsigned int size;
+ int s, err;
+
+ list_for_each_entry(apcm, &chip->pcm_list, list) {
+ if (apcm->pcm->device == pcm_dev) {
+ dev_err(chip->card->dev, "PCM %d already exists\n",
+ pcm_dev);
+ return -EBUSY;
+ }
+ }
+ err = snd_pcm_new(chip->card, cpcm->name, pcm_dev,
+ cpcm->stream[SNDRV_PCM_STREAM_PLAYBACK].substreams,
+ cpcm->stream[SNDRV_PCM_STREAM_CAPTURE].substreams,
+ &pcm);
+ if (err < 0)
+ return err;
+ strlcpy(pcm->name, cpcm->name, sizeof(pcm->name));
+ apcm = kzalloc(sizeof(*apcm), GFP_KERNEL);
+ if (apcm == NULL)
+ return -ENOMEM;
+ apcm->chip = chip;
+ apcm->pcm = pcm;
+ apcm->codec = codec;
+ pcm->private_data = apcm;
+ pcm->private_free = azx_pcm_free;
+ if (cpcm->pcm_type == HDA_PCM_TYPE_MODEM)
+ pcm->dev_class = SNDRV_PCM_CLASS_MODEM;
+ list_add_tail(&apcm->list, &chip->pcm_list);
+ cpcm->pcm = pcm;
+ for (s = 0; s < 2; s++) {
+ apcm->hinfo[s] = &cpcm->stream[s];
+ if (cpcm->stream[s].substreams)
+ snd_pcm_set_ops(pcm, s, &azx_pcm_ops);
+ }
+ /* buffer pre-allocation */
+ size = CONFIG_SND_HDA_PREALLOC_SIZE * 1024;
+ if (size > MAX_PREALLOC_SIZE)
+ size = MAX_PREALLOC_SIZE;
+ snd_pcm_lib_preallocate_pages_for_all(pcm, SNDRV_DMA_TYPE_DEV_SG,
+ chip->card->dev,
+ size, MAX_PREALLOC_SIZE);
+ /* link to codec */
+ pcm->dev = &codec->dev;
+ return 0;
+}
+
+/*
+ * CORB / RIRB interface
+ */
+static int azx_alloc_cmd_io(struct azx *chip)
+{
+ int err;
+
+ /* single page (at least 4096 bytes) must suffice for both ringbuffes */
+ err = chip->ops->dma_alloc_pages(chip, SNDRV_DMA_TYPE_DEV,
+ PAGE_SIZE, &chip->rb);
+ if (err < 0)
+ dev_err(chip->card->dev, "cannot allocate CORB/RIRB\n");
+ return err;
+}
+EXPORT_SYMBOL_GPL(azx_alloc_cmd_io);
+
+static void azx_init_cmd_io(struct azx *chip)
+{
+ int timeout;
+
+ spin_lock_irq(&chip->reg_lock);
+ /* CORB set up */
+ chip->corb.addr = chip->rb.addr;
+ chip->corb.buf = (u32 *)chip->rb.area;
+ azx_writel(chip, CORBLBASE, (u32)chip->corb.addr);
+ azx_writel(chip, CORBUBASE, upper_32_bits(chip->corb.addr));
+
+ /* set the corb size to 256 entries (ULI requires explicitly) */
+ azx_writeb(chip, CORBSIZE, 0x02);
+ /* set the corb write pointer to 0 */
+ azx_writew(chip, CORBWP, 0);
+
+ /* reset the corb hw read pointer */
+ azx_writew(chip, CORBRP, ICH6_CORBRP_RST);
+ if (!(chip->driver_caps & AZX_DCAPS_CORBRP_SELF_CLEAR)) {
+ for (timeout = 1000; timeout > 0; timeout--) {
+ if ((azx_readw(chip, CORBRP) & ICH6_CORBRP_RST) == ICH6_CORBRP_RST)
+ break;
+ udelay(1);
+ }
+ if (timeout <= 0)
+ dev_err(chip->card->dev, "CORB reset timeout#1, CORBRP = %d\n",
+ azx_readw(chip, CORBRP));
+
+ azx_writew(chip, CORBRP, 0);
+ for (timeout = 1000; timeout > 0; timeout--) {
+ if (azx_readw(chip, CORBRP) == 0)
+ break;
+ udelay(1);
+ }
+ if (timeout <= 0)
+ dev_err(chip->card->dev, "CORB reset timeout#2, CORBRP = %d\n",
+ azx_readw(chip, CORBRP));
+ }
+
+ /* enable corb dma */
+ azx_writeb(chip, CORBCTL, ICH6_CORBCTL_RUN);
+
+ /* RIRB set up */
+ chip->rirb.addr = chip->rb.addr + 2048;
+ chip->rirb.buf = (u32 *)(chip->rb.area + 2048);
+ chip->rirb.wp = chip->rirb.rp = 0;
+ memset(chip->rirb.cmds, 0, sizeof(chip->rirb.cmds));
+ azx_writel(chip, RIRBLBASE, (u32)chip->rirb.addr);
+ azx_writel(chip, RIRBUBASE, upper_32_bits(chip->rirb.addr));
+
+ /* set the rirb size to 256 entries (ULI requires explicitly) */
+ azx_writeb(chip, RIRBSIZE, 0x02);
+ /* reset the rirb hw write pointer */
+ azx_writew(chip, RIRBWP, ICH6_RIRBWP_RST);
+ /* set N=1, get RIRB response interrupt for new entry */
+ if (chip->driver_caps & AZX_DCAPS_CTX_WORKAROUND)
+ azx_writew(chip, RINTCNT, 0xc0);
+ else
+ azx_writew(chip, RINTCNT, 1);
+ /* enable rirb dma and response irq */
+ azx_writeb(chip, RIRBCTL, ICH6_RBCTL_DMA_EN | ICH6_RBCTL_IRQ_EN);
+ spin_unlock_irq(&chip->reg_lock);
+}
+EXPORT_SYMBOL_GPL(azx_init_cmd_io);
+
+static void azx_free_cmd_io(struct azx *chip)
+{
+ spin_lock_irq(&chip->reg_lock);
+ /* disable ringbuffer DMAs */
+ azx_writeb(chip, RIRBCTL, 0);
+ azx_writeb(chip, CORBCTL, 0);
+ spin_unlock_irq(&chip->reg_lock);
+}
+EXPORT_SYMBOL_GPL(azx_free_cmd_io);
+
+static unsigned int azx_command_addr(u32 cmd)
+{
+ unsigned int addr = cmd >> 28;
+
+ if (addr >= AZX_MAX_CODECS) {
+ snd_BUG();
+ addr = 0;
+ }
+
+ return addr;
+}
+
+/* send a command */
+static int azx_corb_send_cmd(struct hda_bus *bus, u32 val)
+{
+ struct azx *chip = bus->private_data;
+ unsigned int addr = azx_command_addr(val);
+ unsigned int wp, rp;
+
+ spin_lock_irq(&chip->reg_lock);
+
+ /* add command to corb */
+ wp = azx_readw(chip, CORBWP);
+ if (wp == 0xffff) {
+ /* something wrong, controller likely turned to D3 */
+ spin_unlock_irq(&chip->reg_lock);
+ return -EIO;
+ }
+ wp++;
+ wp %= ICH6_MAX_CORB_ENTRIES;
+
+ rp = azx_readw(chip, CORBRP);
+ if (wp == rp) {
+ /* oops, it's full */
+ spin_unlock_irq(&chip->reg_lock);
+ return -EAGAIN;
+ }
+
+ chip->rirb.cmds[addr]++;
+ chip->corb.buf[wp] = cpu_to_le32(val);
+ azx_writew(chip, CORBWP, wp);
+
+ spin_unlock_irq(&chip->reg_lock);
+
+ return 0;
+}
+
+#define ICH6_RIRB_EX_UNSOL_EV (1<<4)
+
+/* retrieve RIRB entry - called from interrupt handler */
+static void azx_update_rirb(struct azx *chip)
+{
+ unsigned int rp, wp;
+ unsigned int addr;
+ u32 res, res_ex;
+
+ wp = azx_readw(chip, RIRBWP);
+ if (wp == 0xffff) {
+ /* something wrong, controller likely turned to D3 */
+ return;
+ }
+
+ if (wp == chip->rirb.wp)
+ return;
+ chip->rirb.wp = wp;
+
+ while (chip->rirb.rp != wp) {
+ chip->rirb.rp++;
+ chip->rirb.rp %= ICH6_MAX_RIRB_ENTRIES;
+
+ rp = chip->rirb.rp << 1; /* an RIRB entry is 8-bytes */
+ res_ex = le32_to_cpu(chip->rirb.buf[rp + 1]);
+ res = le32_to_cpu(chip->rirb.buf[rp]);
+ addr = res_ex & 0xf;
+ if ((addr >= AZX_MAX_CODECS) || !(chip->codec_mask & (1 << addr))) {
+ dev_err(chip->card->dev, "spurious response %#x:%#x, rp = %d, wp = %d",
+ res, res_ex,
+ chip->rirb.rp, wp);
+ snd_BUG();
+ }
+ else if (res_ex & ICH6_RIRB_EX_UNSOL_EV)
+ snd_hda_queue_unsol_event(chip->bus, res, res_ex);
+ else if (chip->rirb.cmds[addr]) {
+ chip->rirb.res[addr] = res;
+ smp_wmb();
+ chip->rirb.cmds[addr]--;
+ } else if (printk_ratelimit()) {
+ dev_err(chip->card->dev, "spurious response %#x:%#x, last cmd=%#08x\n",
+ res, res_ex,
+ chip->last_cmd[addr]);
+ }
+ }
+}
+
+/* receive a response */
+static unsigned int azx_rirb_get_response(struct hda_bus *bus,
+ unsigned int addr)
+{
+ struct azx *chip = bus->private_data;
+ unsigned long timeout;
+ unsigned long loopcounter;
+ int do_poll = 0;
+
+ again:
+ timeout = jiffies + msecs_to_jiffies(1000);
+
+ for (loopcounter = 0;; loopcounter++) {
+ if (chip->polling_mode || do_poll) {
+ spin_lock_irq(&chip->reg_lock);
+ azx_update_rirb(chip);
+ spin_unlock_irq(&chip->reg_lock);
+ }
+ if (!chip->rirb.cmds[addr]) {
+ smp_rmb();
+ bus->rirb_error = 0;
+
+ if (!do_poll)
+ chip->poll_count = 0;
+ return chip->rirb.res[addr]; /* the last value */
+ }
+ if (time_after(jiffies, timeout))
+ break;
+ if (bus->needs_damn_long_delay || loopcounter > 3000)
+ msleep(2); /* temporary workaround */
+ else {
+ udelay(10);
+ cond_resched();
+ }
+ }
+
+ if (!bus->no_response_fallback)
+ return -1;
+
+ if (!chip->polling_mode && chip->poll_count < 2) {
+ dev_dbg(chip->card->dev,
+ "azx_get_response timeout, polling the codec once: last cmd=0x%08x\n",
+ chip->last_cmd[addr]);
+ do_poll = 1;
+ chip->poll_count++;
+ goto again;
+ }
+
+
+ if (!chip->polling_mode) {
+ dev_warn(chip->card->dev,
+ "azx_get_response timeout, switching to polling mode: last cmd=0x%08x\n",
+ chip->last_cmd[addr]);
+ chip->polling_mode = 1;
+ goto again;
+ }
+
+ if (chip->msi) {
+ dev_warn(chip->card->dev,
+ "No response from codec, disabling MSI: last cmd=0x%08x\n",
+ chip->last_cmd[addr]);
+ if (chip->ops->disable_msi_reset_irq(chip) &&
+ chip->ops->disable_msi_reset_irq(chip) < 0) {
+ bus->rirb_error = 1;
+ return -1;
+ }
+ goto again;
+ }
+
+ if (chip->probing) {
+ /* If this critical timeout happens during the codec probing
+ * phase, this is likely an access to a non-existing codec
+ * slot. Better to return an error and reset the system.
+ */
+ return -1;
+ }
+
+ /* a fatal communication error; need either to reset or to fallback
+ * to the single_cmd mode
+ */
+ bus->rirb_error = 1;
+ if (bus->allow_bus_reset && !bus->response_reset && !bus->in_reset) {
+ bus->response_reset = 1;
+ return -1; /* give a chance to retry */
+ }
+
+ dev_err(chip->card->dev,
+ "azx_get_response timeout, switching to single_cmd mode: last cmd=0x%08x\n",
+ chip->last_cmd[addr]);
+ chip->single_cmd = 1;
+ bus->response_reset = 0;
+ /* release CORB/RIRB */
+ azx_free_cmd_io(chip);
+ /* disable unsolicited responses */
+ azx_writel(chip, GCTL, azx_readl(chip, GCTL) & ~ICH6_GCTL_UNSOL);
+ return -1;
+}
+
+/*
+ * Use the single immediate command instead of CORB/RIRB for simplicity
+ *
+ * Note: according to Intel, this is not preferred use. The command was
+ * intended for the BIOS only, and may get confused with unsolicited
+ * responses. So, we shouldn't use it for normal operation from the
+ * driver.
+ * I left the codes, however, for debugging/testing purposes.
+ */
+
+/* receive a response */
+static int azx_single_wait_for_response(struct azx *chip, unsigned int addr)
+{
+ int timeout = 50;
+
+ while (timeout--) {
+ /* check IRV busy bit */
+ if (azx_readw(chip, IRS) & ICH6_IRS_VALID) {
+ /* reuse rirb.res as the response return value */
+ chip->rirb.res[addr] = azx_readl(chip, IR);
+ return 0;
+ }
+ udelay(1);
+ }
+ if (printk_ratelimit())
+ dev_dbg(chip->card->dev, "get_response timeout: IRS=0x%x\n",
+ azx_readw(chip, IRS));
+ chip->rirb.res[addr] = -1;
+ return -EIO;
+}
+
+/* send a command */
+static int azx_single_send_cmd(struct hda_bus *bus, u32 val)
+{
+ struct azx *chip = bus->private_data;
+ unsigned int addr = azx_command_addr(val);
+ int timeout = 50;
+
+ bus->rirb_error = 0;
+ while (timeout--) {
+ /* check ICB busy bit */
+ if (!((azx_readw(chip, IRS) & ICH6_IRS_BUSY))) {
+ /* Clear IRV valid bit */
+ azx_writew(chip, IRS, azx_readw(chip, IRS) |
+ ICH6_IRS_VALID);
+ azx_writel(chip, IC, val);
+ azx_writew(chip, IRS, azx_readw(chip, IRS) |
+ ICH6_IRS_BUSY);
+ return azx_single_wait_for_response(chip, addr);
+ }
+ udelay(1);
+ }
+ if (printk_ratelimit())
+ dev_dbg(chip->card->dev,
+ "send_cmd timeout: IRS=0x%x, val=0x%x\n",
+ azx_readw(chip, IRS), val);
+ return -EIO;
+}
+
+/* receive a response */
+static unsigned int azx_single_get_response(struct hda_bus *bus,
+ unsigned int addr)
+{
+ struct azx *chip = bus->private_data;
+ return chip->rirb.res[addr];
+}
+
+/*
+ * The below are the main callbacks from hda_codec.
+ *
+ * They are just the skeleton to call sub-callbacks according to the
+ * current setting of chip->single_cmd.
+ */
+
+/* send a command */
+static int azx_send_cmd(struct hda_bus *bus, unsigned int val)
+{
+ struct azx *chip = bus->private_data;
+
+ if (chip->disabled)
+ return 0;
+ chip->last_cmd[azx_command_addr(val)] = val;
+ if (chip->single_cmd)
+ return azx_single_send_cmd(bus, val);
+ else
+ return azx_corb_send_cmd(bus, val);
+}
+EXPORT_SYMBOL_GPL(azx_send_cmd);
+
+/* get a response */
+static unsigned int azx_get_response(struct hda_bus *bus,
+ unsigned int addr)
+{
+ struct azx *chip = bus->private_data;
+ if (chip->disabled)
+ return 0;
+ if (chip->single_cmd)
+ return azx_single_get_response(bus, addr);
+ else
+ return azx_rirb_get_response(bus, addr);
+}
+EXPORT_SYMBOL_GPL(azx_get_response);
+
+#ifdef CONFIG_SND_HDA_DSP_LOADER
+/*
+ * DSP loading code (e.g. for CA0132)
+ */
+
+/* use the first stream for loading DSP */
+static struct azx_dev *
+azx_get_dsp_loader_dev(struct azx *chip)
+{
+ return &chip->azx_dev[chip->playback_index_offset];
+}
+
+static int azx_load_dsp_prepare(struct hda_bus *bus, unsigned int format,
+ unsigned int byte_size,
+ struct snd_dma_buffer *bufp)
+{
+ u32 *bdl;
+ struct azx *chip = bus->private_data;
+ struct azx_dev *azx_dev;
+ int err;
+
+ azx_dev = azx_get_dsp_loader_dev(chip);
+
+ dsp_lock(azx_dev);
+ spin_lock_irq(&chip->reg_lock);
+ if (azx_dev->running || azx_dev->locked) {
+ spin_unlock_irq(&chip->reg_lock);
+ err = -EBUSY;
+ goto unlock;
+ }
+ azx_dev->prepared = 0;
+ chip->saved_azx_dev = *azx_dev;
+ azx_dev->locked = 1;
+ spin_unlock_irq(&chip->reg_lock);
+
+ err = chip->ops->dma_alloc_pages(chip, SNDRV_DMA_TYPE_DEV_SG,
+ byte_size, bufp);
+ if (err < 0)
+ goto err_alloc;
+
+ azx_dev->bufsize = byte_size;
+ azx_dev->period_bytes = byte_size;
+ azx_dev->format_val = format;
+
+ azx_stream_reset(chip, azx_dev);
+
+ /* reset BDL address */
+ azx_sd_writel(chip, azx_dev, SD_BDLPL, 0);
+ azx_sd_writel(chip, azx_dev, SD_BDLPU, 0);
+
+ azx_dev->frags = 0;
+ bdl = (u32 *)azx_dev->bdl.area;
+ err = setup_bdle(chip, bufp, azx_dev, &bdl, 0, byte_size, 0);
+ if (err < 0)
+ goto error;
+
+ azx_setup_controller(chip, azx_dev);
+ dsp_unlock(azx_dev);
+ return azx_dev->stream_tag;
+
+ error:
+ chip->ops->dma_free_pages(chip, bufp);
+ err_alloc:
+ spin_lock_irq(&chip->reg_lock);
+ if (azx_dev->opened)
+ *azx_dev = chip->saved_azx_dev;
+ azx_dev->locked = 0;
+ spin_unlock_irq(&chip->reg_lock);
+ unlock:
+ dsp_unlock(azx_dev);
+ return err;
+}
+
+static void azx_load_dsp_trigger(struct hda_bus *bus, bool start)
+{
+ struct azx *chip = bus->private_data;
+ struct azx_dev *azx_dev = azx_get_dsp_loader_dev(chip);
+
+ if (start)
+ azx_stream_start(chip, azx_dev);
+ else
+ azx_stream_stop(chip, azx_dev);
+ azx_dev->running = start;
+}
+
+static void azx_load_dsp_cleanup(struct hda_bus *bus,
+ struct snd_dma_buffer *dmab)
+{
+ struct azx *chip = bus->private_data;
+ struct azx_dev *azx_dev = azx_get_dsp_loader_dev(chip);
+
+ if (!dmab->area || !azx_dev->locked)
+ return;
+
+ dsp_lock(azx_dev);
+ /* reset BDL address */
+ azx_sd_writel(chip, azx_dev, SD_BDLPL, 0);
+ azx_sd_writel(chip, azx_dev, SD_BDLPU, 0);
+ azx_sd_writel(chip, azx_dev, SD_CTL, 0);
+ azx_dev->bufsize = 0;
+ azx_dev->period_bytes = 0;
+ azx_dev->format_val = 0;
+
+ chip->ops->dma_free_pages(chip, dmab);
+ dmab->area = NULL;
+
+ spin_lock_irq(&chip->reg_lock);
+ if (azx_dev->opened)
+ *azx_dev = chip->saved_azx_dev;
+ azx_dev->locked = 0;
+ spin_unlock_irq(&chip->reg_lock);
+ dsp_unlock(azx_dev);
+}
+#endif /* CONFIG_SND_HDA_DSP_LOADER */
+
+int azx_alloc_stream_pages(struct azx *chip)
+{
+ int i, err;
+ struct snd_card *card = chip->card;
+
+ for (i = 0; i < chip->num_streams; i++) {
+ dsp_lock_init(&chip->azx_dev[i]);
+ /* allocate memory for the BDL for each stream */
+ err = chip->ops->dma_alloc_pages(chip, SNDRV_DMA_TYPE_DEV,
+ BDL_SIZE,
+ &chip->azx_dev[i].bdl);
+ if (err < 0) {
+ dev_err(card->dev, "cannot allocate BDL\n");
+ return -ENOMEM;
+ }
+ }
+ /* allocate memory for the position buffer */
+ err = chip->ops->dma_alloc_pages(chip, SNDRV_DMA_TYPE_DEV,
+ chip->num_streams * 8, &chip->posbuf);
+ if (err < 0) {
+ dev_err(card->dev, "cannot allocate posbuf\n");
+ return -ENOMEM;
+ }
+
+ /* allocate CORB/RIRB */
+ err = azx_alloc_cmd_io(chip);
+ if (err < 0)
+ return err;
+ return 0;
+}
+EXPORT_SYMBOL_GPL(azx_alloc_stream_pages);
+
+void azx_free_stream_pages(struct azx *chip)
+{
+ int i;
+ if (chip->azx_dev) {
+ for (i = 0; i < chip->num_streams; i++)
+ if (chip->azx_dev[i].bdl.area)
+ chip->ops->dma_free_pages(
+ chip, &chip->azx_dev[i].bdl);
+ }
+ if (chip->rb.area)
+ chip->ops->dma_free_pages(chip, &chip->rb);
+ if (chip->posbuf.area)
+ chip->ops->dma_free_pages(chip, &chip->posbuf);
+}
+EXPORT_SYMBOL_GPL(azx_free_stream_pages);
+
+/*
+ * Lowlevel interface
+ */
+
+/* enter link reset */
+void azx_enter_link_reset(struct azx *chip)
+{
+ unsigned long timeout;
+
+ /* reset controller */
+ azx_writel(chip, GCTL, azx_readl(chip, GCTL) & ~ICH6_GCTL_RESET);
+
+ timeout = jiffies + msecs_to_jiffies(100);
+ while ((azx_readb(chip, GCTL) & ICH6_GCTL_RESET) &&
+ time_before(jiffies, timeout))
+ usleep_range(500, 1000);
+}
+EXPORT_SYMBOL_GPL(azx_enter_link_reset);
+
+/* exit link reset */
+static void azx_exit_link_reset(struct azx *chip)
+{
+ unsigned long timeout;
+
+ azx_writeb(chip, GCTL, azx_readb(chip, GCTL) | ICH6_GCTL_RESET);
+
+ timeout = jiffies + msecs_to_jiffies(100);
+ while (!azx_readb(chip, GCTL) &&
+ time_before(jiffies, timeout))
+ usleep_range(500, 1000);
+}
+
+/* reset codec link */
+static int azx_reset(struct azx *chip, bool full_reset)
+{
+ if (!full_reset)
+ goto __skip;
+
+ /* clear STATESTS */
+ azx_writew(chip, STATESTS, STATESTS_INT_MASK);
+
+ /* reset controller */
+ azx_enter_link_reset(chip);
+
+ /* delay for >= 100us for codec PLL to settle per spec
+ * Rev 0.9 section 5.5.1
+ */
+ usleep_range(500, 1000);
+
+ /* Bring controller out of reset */
+ azx_exit_link_reset(chip);
+
+ /* Brent Chartrand said to wait >= 540us for codecs to initialize */
+ usleep_range(1000, 1200);
+
+ __skip:
+ /* check to see if controller is ready */
+ if (!azx_readb(chip, GCTL)) {
+ dev_dbg(chip->card->dev, "azx_reset: controller not ready!\n");
+ return -EBUSY;
+ }
+
+ /* Accept unsolicited responses */
+ if (!chip->single_cmd)
+ azx_writel(chip, GCTL, azx_readl(chip, GCTL) |
+ ICH6_GCTL_UNSOL);
+
+ /* detect codecs */
+ if (!chip->codec_mask) {
+ chip->codec_mask = azx_readw(chip, STATESTS);
+ dev_dbg(chip->card->dev, "codec_mask = 0x%x\n",
+ chip->codec_mask);
+ }
+
+ return 0;
+}
+
+/* enable interrupts */
+static void azx_int_enable(struct azx *chip)
+{
+ /* enable controller CIE and GIE */
+ azx_writel(chip, INTCTL, azx_readl(chip, INTCTL) |
+ ICH6_INT_CTRL_EN | ICH6_INT_GLOBAL_EN);
+}
+
+/* disable interrupts */
+static void azx_int_disable(struct azx *chip)
+{
+ int i;
+
+ /* disable interrupts in stream descriptor */
+ for (i = 0; i < chip->num_streams; i++) {
+ struct azx_dev *azx_dev = &chip->azx_dev[i];
+ azx_sd_writeb(chip, azx_dev, SD_CTL,
+ azx_sd_readb(chip, azx_dev, SD_CTL) &
+ ~SD_INT_MASK);
+ }
+
+ /* disable SIE for all streams */
+ azx_writeb(chip, INTCTL, 0);
+
+ /* disable controller CIE and GIE */
+ azx_writel(chip, INTCTL, azx_readl(chip, INTCTL) &
+ ~(ICH6_INT_CTRL_EN | ICH6_INT_GLOBAL_EN));
+}
+
+/* clear interrupts */
+static void azx_int_clear(struct azx *chip)
+{
+ int i;
+
+ /* clear stream status */
+ for (i = 0; i < chip->num_streams; i++) {
+ struct azx_dev *azx_dev = &chip->azx_dev[i];
+ azx_sd_writeb(chip, azx_dev, SD_STS, SD_INT_MASK);
+ }
+
+ /* clear STATESTS */
+ azx_writew(chip, STATESTS, STATESTS_INT_MASK);
+
+ /* clear rirb status */
+ azx_writeb(chip, RIRBSTS, RIRB_INT_MASK);
+
+ /* clear int status */
+ azx_writel(chip, INTSTS, ICH6_INT_CTRL_EN | ICH6_INT_ALL_STREAM);
+}
+
+/*
+ * reset and start the controller registers
+ */
+void azx_init_chip(struct azx *chip, bool full_reset)
+{
+ if (chip->initialized)
+ return;
+
+ /* reset controller */
+ azx_reset(chip, full_reset);
+
+ /* initialize interrupts */
+ azx_int_clear(chip);
+ azx_int_enable(chip);
+
+ /* initialize the codec command I/O */
+ if (!chip->single_cmd)
+ azx_init_cmd_io(chip);
+
+ /* program the position buffer */
+ azx_writel(chip, DPLBASE, (u32)chip->posbuf.addr);
+ azx_writel(chip, DPUBASE, upper_32_bits(chip->posbuf.addr));
+
+ chip->initialized = 1;
+}
+EXPORT_SYMBOL_GPL(azx_init_chip);
+
+void azx_stop_chip(struct azx *chip)
+{
+ if (!chip->initialized)
+ return;
+
+ /* disable interrupts */
+ azx_int_disable(chip);
+ azx_int_clear(chip);
+
+ /* disable CORB/RIRB */
+ azx_free_cmd_io(chip);
+
+ /* disable position buffer */
+ azx_writel(chip, DPLBASE, 0);
+ azx_writel(chip, DPUBASE, 0);
+
+ chip->initialized = 0;
+}
+EXPORT_SYMBOL_GPL(azx_stop_chip);
+
+/*
+ * interrupt handler
+ */
+irqreturn_t azx_interrupt(int irq, void *dev_id)
+{
+ struct azx *chip = dev_id;
+ struct azx_dev *azx_dev;
+ u32 status;
+ u8 sd_status;
+ int i;
+
+#ifdef CONFIG_PM_RUNTIME
+ if (chip->driver_caps & AZX_DCAPS_PM_RUNTIME)
+ if (!pm_runtime_active(chip->card->dev))
+ return IRQ_NONE;
+#endif
+
+ spin_lock(&chip->reg_lock);
+
+ if (chip->disabled) {
+ spin_unlock(&chip->reg_lock);
+ return IRQ_NONE;
+ }
+
+ status = azx_readl(chip, INTSTS);
+ if (status == 0 || status == 0xffffffff) {
+ spin_unlock(&chip->reg_lock);
+ return IRQ_NONE;
+ }
+
+ for (i = 0; i < chip->num_streams; i++) {
+ azx_dev = &chip->azx_dev[i];
+ if (status & azx_dev->sd_int_sta_mask) {
+ sd_status = azx_sd_readb(chip, azx_dev, SD_STS);
+ azx_sd_writeb(chip, azx_dev, SD_STS, SD_INT_MASK);
+ if (!azx_dev->substream || !azx_dev->running ||
+ !(sd_status & SD_INT_COMPLETE))
+ continue;
+ /* check whether this IRQ is really acceptable */
+ if (!chip->ops->position_check ||
+ chip->ops->position_check(chip, azx_dev)) {
+ spin_unlock(&chip->reg_lock);
+ snd_pcm_period_elapsed(azx_dev->substream);
+ spin_lock(&chip->reg_lock);
+ }
+ }
+ }
+
+ /* clear rirb int */
+ status = azx_readb(chip, RIRBSTS);
+ if (status & RIRB_INT_MASK) {
+ if (status & RIRB_INT_RESPONSE) {
+ if (chip->driver_caps & AZX_DCAPS_RIRB_PRE_DELAY)
+ udelay(80);
+ azx_update_rirb(chip);
+ }
+ azx_writeb(chip, RIRBSTS, RIRB_INT_MASK);
+ }
+
+ spin_unlock(&chip->reg_lock);
+
+ return IRQ_HANDLED;
+}
+EXPORT_SYMBOL_GPL(azx_interrupt);
+
+/*
+ * Codec initerface
+ */
+
+/*
+ * Probe the given codec address
+ */
+static int probe_codec(struct azx *chip, int addr)
+{
+ unsigned int cmd = (addr << 28) | (AC_NODE_ROOT << 20) |
+ (AC_VERB_PARAMETERS << 8) | AC_PAR_VENDOR_ID;
+ unsigned int res;
+
+ mutex_lock(&chip->bus->cmd_mutex);
+ chip->probing = 1;
+ azx_send_cmd(chip->bus, cmd);
+ res = azx_get_response(chip->bus, addr);
+ chip->probing = 0;
+ mutex_unlock(&chip->bus->cmd_mutex);
+ if (res == -1)
+ return -EIO;
+ dev_dbg(chip->card->dev, "codec #%d probed OK\n", addr);
+ return 0;
+}
+
+static void azx_bus_reset(struct hda_bus *bus)
+{
+ struct azx *chip = bus->private_data;
+
+ bus->in_reset = 1;
+ azx_stop_chip(chip);
+ azx_init_chip(chip, true);
+#ifdef CONFIG_PM
+ if (chip->initialized) {
+ struct azx_pcm *p;
+ list_for_each_entry(p, &chip->pcm_list, list)
+ snd_pcm_suspend_all(p->pcm);
+ snd_hda_suspend(chip->bus);
+ snd_hda_resume(chip->bus);
+ }
+#endif
+ bus->in_reset = 0;
+}
+
+#ifdef CONFIG_PM
+/* power-up/down the controller */
+static void azx_power_notify(struct hda_bus *bus, bool power_up)
+{
+ struct azx *chip = bus->private_data;
+
+ if (!(chip->driver_caps & AZX_DCAPS_PM_RUNTIME))
+ return;
+
+ if (power_up)
+ pm_runtime_get_sync(chip->card->dev);
+ else
+ pm_runtime_put_sync(chip->card->dev);
+}
+#endif
+
+static int get_jackpoll_interval(struct azx *chip)
+{
+ int i;
+ unsigned int j;
+
+ if (!chip->jackpoll_ms)
+ return 0;
+
+ i = chip->jackpoll_ms[chip->dev_index];
+ if (i == 0)
+ return 0;
+ if (i < 50 || i > 60000)
+ j = 0;
+ else
+ j = msecs_to_jiffies(i);
+ if (j == 0)
+ dev_warn(chip->card->dev,
+ "jackpoll_ms value out of range: %d\n", i);
+ return j;
+}
+
+/* Codec initialization */
+int azx_codec_create(struct azx *chip, const char *model,
+ unsigned int max_slots,
+ int *power_save_to)
+{
+ struct hda_bus_template bus_temp;
+ int c, codecs, err;
+
+ memset(&bus_temp, 0, sizeof(bus_temp));
+ bus_temp.private_data = chip;
+ bus_temp.modelname = model;
+ bus_temp.pci = chip->pci;
+ bus_temp.ops.command = azx_send_cmd;
+ bus_temp.ops.get_response = azx_get_response;
+ bus_temp.ops.attach_pcm = azx_attach_pcm_stream;
+ bus_temp.ops.bus_reset = azx_bus_reset;
+#ifdef CONFIG_PM
+ bus_temp.power_save = power_save_to;
+ bus_temp.ops.pm_notify = azx_power_notify;
+#endif
+#ifdef CONFIG_SND_HDA_DSP_LOADER
+ bus_temp.ops.load_dsp_prepare = azx_load_dsp_prepare;
+ bus_temp.ops.load_dsp_trigger = azx_load_dsp_trigger;
+ bus_temp.ops.load_dsp_cleanup = azx_load_dsp_cleanup;
+#endif
+
+ err = snd_hda_bus_new(chip->card, &bus_temp, &chip->bus);
+ if (err < 0)
+ return err;
+
+ if (chip->driver_caps & AZX_DCAPS_RIRB_DELAY) {
+ dev_dbg(chip->card->dev, "Enable delay in RIRB handling\n");
+ chip->bus->needs_damn_long_delay = 1;
+ }
+
+ codecs = 0;
+ if (!max_slots)
+ max_slots = AZX_DEFAULT_CODECS;
+
+ /* First try to probe all given codec slots */
+ for (c = 0; c < max_slots; c++) {
+ if ((chip->codec_mask & (1 << c)) & chip->codec_probe_mask) {
+ if (probe_codec(chip, c) < 0) {
+ /* Some BIOSen give you wrong codec addresses
+ * that don't exist
+ */
+ dev_warn(chip->card->dev,
+ "Codec #%d probe error; disabling it...\n", c);
+ chip->codec_mask &= ~(1 << c);
+ /* More badly, accessing to a non-existing
+ * codec often screws up the controller chip,
+ * and disturbs the further communications.
+ * Thus if an error occurs during probing,
+ * better to reset the controller chip to
+ * get back to the sanity state.
+ */
+ azx_stop_chip(chip);
+ azx_init_chip(chip, true);
+ }
+ }
+ }
+
+ /* AMD chipsets often cause the communication stalls upon certain
+ * sequence like the pin-detection. It seems that forcing the synced
+ * access works around the stall. Grrr...
+ */
+ if (chip->driver_caps & AZX_DCAPS_SYNC_WRITE) {
+ dev_dbg(chip->card->dev, "Enable sync_write for stable communication\n");
+ chip->bus->sync_write = 1;
+ chip->bus->allow_bus_reset = 1;
+ }
+
+ /* Then create codec instances */
+ for (c = 0; c < max_slots; c++) {
+ if ((chip->codec_mask & (1 << c)) & chip->codec_probe_mask) {
+ struct hda_codec *codec;
+ err = snd_hda_codec_new(chip->bus, c, &codec);
+ if (err < 0)
+ continue;
+ codec->jackpoll_interval = get_jackpoll_interval(chip);
+ codec->beep_mode = chip->beep_mode;
+ codecs++;
+ }
+ }
+ if (!codecs) {
+ dev_err(chip->card->dev, "no codecs initialized\n");
+ return -ENXIO;
+ }
+ return 0;
+}
+EXPORT_SYMBOL_GPL(azx_codec_create);
+
+/* configure each codec instance */
+int azx_codec_configure(struct azx *chip)
+{
+ struct hda_codec *codec;
+ list_for_each_entry(codec, &chip->bus->codec_list, list) {
+ snd_hda_codec_configure(codec);
+ }
+ return 0;
+}
+EXPORT_SYMBOL_GPL(azx_codec_configure);
+
+/* mixer creation - all stuff is implemented in hda module */
+int azx_mixer_create(struct azx *chip)
+{
+ return snd_hda_build_controls(chip->bus);
+}
+EXPORT_SYMBOL_GPL(azx_mixer_create);
+
+
+/* initialize SD streams */
+int azx_init_stream(struct azx *chip)
+{
+ int i;
+
+ /* initialize each stream (aka device)
+ * assign the starting bdl address to each stream (device)
+ * and initialize
+ */
+ for (i = 0; i < chip->num_streams; i++) {
+ struct azx_dev *azx_dev = &chip->azx_dev[i];
+ azx_dev->posbuf = (u32 __iomem *)(chip->posbuf.area + i * 8);
+ /* offset: SDI0=0x80, SDI1=0xa0, ... SDO3=0x160 */
+ azx_dev->sd_addr = chip->remap_addr + (0x20 * i + 0x80);
+ /* int mask: SDI0=0x01, SDI1=0x02, ... SDO3=0x80 */
+ azx_dev->sd_int_sta_mask = 1 << i;
+ /* stream tag: must be non-zero and unique */
+ azx_dev->index = i;
+ azx_dev->stream_tag = i + 1;
+ }
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(azx_init_stream);
+
+MODULE_LICENSE("GPL");
+MODULE_DESCRIPTION("Common HDA driver funcitons");
diff --git a/sound/pci/hda/hda_controller.h b/sound/pci/hda/hda_controller.h
new file mode 100644
index 00000000000..baf0e77330a
--- /dev/null
+++ b/sound/pci/hda/hda_controller.h
@@ -0,0 +1,53 @@
+/*
+ * Common functionality for the alsa driver code base for HD Audio.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the Free
+ * Software Foundation; either version 2 of the License, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ */
+
+#ifndef __SOUND_HDA_CONTROLLER_H
+#define __SOUND_HDA_CONTROLLER_H
+
+#include <sound/core.h>
+#include <sound/initval.h>
+#include "hda_codec.h"
+#include "hda_priv.h"
+
+/* PCM setup */
+static inline struct azx_dev *get_azx_dev(struct snd_pcm_substream *substream)
+{
+ return substream->runtime->private_data;
+}
+unsigned int azx_get_position(struct azx *chip,
+ struct azx_dev *azx_dev,
+ bool with_check);
+
+/* Stream control. */
+void azx_stream_stop(struct azx *chip, struct azx_dev *azx_dev);
+
+/* Allocation functions. */
+int azx_alloc_stream_pages(struct azx *chip);
+void azx_free_stream_pages(struct azx *chip);
+
+/* Low level azx interface */
+void azx_init_chip(struct azx *chip, bool full_reset);
+void azx_stop_chip(struct azx *chip);
+void azx_enter_link_reset(struct azx *chip);
+irqreturn_t azx_interrupt(int irq, void *dev_id);
+
+/* Codec interface */
+int azx_codec_create(struct azx *chip, const char *model,
+ unsigned int max_slots,
+ int *power_save_to);
+int azx_codec_configure(struct azx *chip);
+int azx_mixer_create(struct azx *chip);
+int azx_init_stream(struct azx *chip);
+
+#endif /* __SOUND_HDA_CONTROLLER_H */
diff --git a/sound/pci/hda/hda_eld.c b/sound/pci/hda/hda_eld.c
index 74b0560289c..46690a7f48f 100644
--- a/sound/pci/hda/hda_eld.c
+++ b/sound/pci/hda/hda_eld.c
@@ -2,6 +2,7 @@
* Generic routines and proc interface for ELD(EDID Like Data) information
*
* Copyright(c) 2008 Intel Corporation.
+ * Copyright (c) 2013 Anssi Hannula <anssi.hannula@iki.fi>
*
* Authors:
* Wu Fengguang <wfg@linux.intel.com>
@@ -144,25 +145,17 @@ static int cea_sampling_frequencies[8] = {
SNDRV_PCM_RATE_192000, /* 7: 192000Hz */
};
-static unsigned char hdmi_get_eld_byte(struct hda_codec *codec, hda_nid_t nid,
+static unsigned int hdmi_get_eld_data(struct hda_codec *codec, hda_nid_t nid,
int byte_index)
{
unsigned int val;
val = snd_hda_codec_read(codec, nid, 0,
AC_VERB_GET_HDMI_ELDD, byte_index);
-
#ifdef BE_PARANOID
- printk(KERN_INFO "HDMI: ELD data byte %d: 0x%x\n", byte_index, val);
+ codec_info(codec, "HDMI: ELD data byte %d: 0x%x\n", byte_index, val);
#endif
-
- if ((val & AC_ELDD_ELD_VALID) == 0) {
- snd_printd(KERN_INFO "HDMI: invalid ELD data byte %d\n",
- byte_index);
- val = 0;
- }
-
- return val & AC_ELDD_ELD_DATA;
+ return val;
}
#define GRAB_BITS(buf, byte, lowbit, bits) \
@@ -254,8 +247,8 @@ static void hdmi_update_short_audio_desc(struct cea_sad *a,
/*
* Be careful, ELD buf could be totally rubbish!
*/
-static int hdmi_update_eld(struct hdmi_eld *e,
- const unsigned char *buf, int size)
+int snd_hdmi_parse_eld(struct parsed_hdmi_eld *e,
+ const unsigned char *buf, int size)
{
int mnl;
int i;
@@ -268,7 +261,6 @@ static int hdmi_update_eld(struct hdmi_eld *e,
goto out_fail;
}
- e->eld_size = size;
e->baseline_len = GRAB_BITS(buf, 2, 0, 8);
mnl = GRAB_BITS(buf, 4, 0, 5);
e->cea_edid_ver = GRAB_BITS(buf, 4, 5, 3);
@@ -294,7 +286,7 @@ static int hdmi_update_eld(struct hdmi_eld *e,
snd_printd(KERN_INFO "HDMI: out of range MNL %d\n", mnl);
goto out_fail;
} else
- strlcpy(e->monitor_name, buf + ELD_FIXED_BYTES, mnl);
+ strlcpy(e->monitor_name, buf + ELD_FIXED_BYTES, mnl + 1);
for (i = 0; i < e->sad_count; i++) {
if (ELD_FIXED_BYTES + mnl + 3 * (i + 1) > size) {
@@ -305,71 +297,103 @@ static int hdmi_update_eld(struct hdmi_eld *e,
buf + ELD_FIXED_BYTES + mnl + 3 * i);
}
+ /*
+ * HDMI sink's ELD info cannot always be retrieved for now, e.g.
+ * in console or for audio devices. Assume the highest speakers
+ * configuration, to _not_ prohibit multi-channel audio playback.
+ */
+ if (!e->spk_alloc)
+ e->spk_alloc = 0xffff;
+
return 0;
out_fail:
- e->eld_ver = 0;
return -EINVAL;
}
-static int hdmi_eld_valid(struct hda_codec *codec, hda_nid_t nid)
-{
- int eldv;
- int present;
-
- present = snd_hda_pin_sense(codec, nid);
- eldv = (present & AC_PINSENSE_ELDV);
- present = (present & AC_PINSENSE_PRESENCE);
-
-#ifdef CONFIG_SND_DEBUG_VERBOSE
- printk(KERN_INFO "HDMI: sink_present = %d, eld_valid = %d\n",
- !!present, !!eldv);
-#endif
-
- return eldv && present;
-}
-
int snd_hdmi_get_eld_size(struct hda_codec *codec, hda_nid_t nid)
{
return snd_hda_codec_read(codec, nid, 0, AC_VERB_GET_HDMI_DIP_SIZE,
AC_DIPSIZE_ELD_BUF);
}
-int snd_hdmi_get_eld(struct hdmi_eld *eld,
- struct hda_codec *codec, hda_nid_t nid)
+int snd_hdmi_get_eld(struct hda_codec *codec, hda_nid_t nid,
+ unsigned char *buf, int *eld_size)
{
int i;
- int ret;
+ int ret = 0;
int size;
- unsigned char *buf;
- if (!hdmi_eld_valid(codec, nid))
- return -ENOENT;
+ /*
+ * ELD size is initialized to zero in caller function. If no errors and
+ * ELD is valid, actual eld_size is assigned.
+ */
size = snd_hdmi_get_eld_size(codec, nid);
if (size == 0) {
/* wfg: workaround for ASUS P5E-VM HDMI board */
- snd_printd(KERN_INFO "HDMI: ELD buf size is 0, force 128\n");
+ codec_info(codec, "HDMI: ELD buf size is 0, force 128\n");
size = 128;
}
- if (size < ELD_FIXED_BYTES || size > PAGE_SIZE) {
- snd_printd(KERN_INFO "HDMI: invalid ELD buf size %d\n", size);
+ if (size < ELD_FIXED_BYTES || size > ELD_MAX_SIZE) {
+ codec_info(codec, "HDMI: invalid ELD buf size %d\n", size);
return -ERANGE;
}
- buf = kmalloc(size, GFP_KERNEL);
- if (!buf)
- return -ENOMEM;
+ /* set ELD buffer */
+ for (i = 0; i < size; i++) {
+ unsigned int val = hdmi_get_eld_data(codec, nid, i);
+ /*
+ * Graphics driver might be writing to ELD buffer right now.
+ * Just abort. The caller will repoll after a while.
+ */
+ if (!(val & AC_ELDD_ELD_VALID)) {
+ codec_info(codec, "HDMI: invalid ELD data byte %d\n", i);
+ ret = -EINVAL;
+ goto error;
+ }
+ val &= AC_ELDD_ELD_DATA;
+ /*
+ * The first byte cannot be zero. This can happen on some DVI
+ * connections. Some Intel chips may also need some 250ms delay
+ * to return non-zero ELD data, even when the graphics driver
+ * correctly writes ELD content before setting ELD_valid bit.
+ */
+ if (!val && !i) {
+ codec_dbg(codec, "HDMI: 0 ELD data\n");
+ ret = -EINVAL;
+ goto error;
+ }
+ buf[i] = val;
+ }
- for (i = 0; i < size; i++)
- buf[i] = hdmi_get_eld_byte(codec, nid, i);
+ *eld_size = size;
+error:
+ return ret;
+}
- ret = hdmi_update_eld(eld, buf, size);
+/**
+ * SNDRV_PCM_RATE_* and AC_PAR_PCM values don't match, print correct rates with
+ * hdmi-specific routine.
+ */
+static void hdmi_print_pcm_rates(int pcm, char *buf, int buflen)
+{
+ static unsigned int alsa_rates[] = {
+ 5512, 8000, 11025, 16000, 22050, 32000, 44100, 48000, 64000,
+ 88200, 96000, 176400, 192000, 384000
+ };
+ int i, j;
- kfree(buf);
- return ret;
+ for (i = 0, j = 0; i < ARRAY_SIZE(alsa_rates); i++)
+ if (pcm & (1 << i))
+ j += snprintf(buf + j, buflen - j, " %d",
+ alsa_rates[i]);
+
+ buf[j] = '\0'; /* necessary when j == 0 */
}
+#define SND_PRINT_RATES_ADVISED_BUFSIZE 80
+
static void hdmi_show_short_audio_desc(struct cea_sad *a)
{
char buf[SND_PRINT_RATES_ADVISED_BUFSIZE];
@@ -378,7 +402,7 @@ static void hdmi_show_short_audio_desc(struct cea_sad *a)
if (!a->format)
return;
- snd_print_pcm_rates(a->rates, buf, sizeof(buf));
+ hdmi_print_pcm_rates(a->rates, buf, sizeof(buf));
if (a->format == AUDIO_CODING_TYPE_LPCM)
snd_print_pcm_bits(a->sample_bits, buf2 + 8, sizeof(buf2) - 8);
@@ -388,7 +412,7 @@ static void hdmi_show_short_audio_desc(struct cea_sad *a)
else
buf2[0] = '\0';
- printk(KERN_INFO "HDMI: supports coding type %s:"
+ _snd_printd(SND_PR_VERBOSE, "HDMI: supports coding type %s:"
" channels = %d, rates =%s%s\n",
cea_audio_coding_type_names[a->format],
a->channels,
@@ -408,18 +432,18 @@ void snd_print_channel_allocation(int spk_alloc, char *buf, int buflen)
buf[j] = '\0'; /* necessary when j == 0 */
}
-void snd_hdmi_show_eld(struct hdmi_eld *e)
+void snd_hdmi_show_eld(struct parsed_hdmi_eld *e)
{
int i;
- printk(KERN_INFO "HDMI: detected monitor %s at connection type %s\n",
+ _snd_printd(SND_PR_VERBOSE, "HDMI: detected monitor %s at connection type %s\n",
e->monitor_name,
eld_connection_type_names[e->conn_type]);
if (e->spk_alloc) {
char buf[SND_PRINT_CHANNEL_ALLOCATION_ADVISED_BUFSIZE];
snd_print_channel_allocation(e->spk_alloc, buf, sizeof(buf));
- printk(KERN_INFO "HDMI: available speakers:%s\n", buf);
+ _snd_printd(SND_PR_VERBOSE, "HDMI: available speakers:%s\n", buf);
}
for (i = 0; i < e->sad_count; i++)
@@ -437,7 +461,7 @@ static void hdmi_print_sad_info(int i, struct cea_sad *a,
i, a->format, cea_audio_coding_type_names[a->format]);
snd_iprintf(buffer, "sad%d_channels\t\t%d\n", i, a->channels);
- snd_print_pcm_rates(a->rates, buf, sizeof(buf));
+ hdmi_print_pcm_rates(a->rates, buf, sizeof(buf));
snd_iprintf(buffer, "sad%d_rates\t\t[0x%x]%s\n", i, a->rates, buf);
if (a->format == AUDIO_CODING_TYPE_LPCM) {
@@ -454,13 +478,13 @@ static void hdmi_print_sad_info(int i, struct cea_sad *a,
snd_iprintf(buffer, "sad%d_profile\t\t%d\n", i, a->profile);
}
-static void hdmi_print_eld_info(struct snd_info_entry *entry,
- struct snd_info_buffer *buffer)
+void snd_hdmi_print_eld_info(struct hdmi_eld *eld,
+ struct snd_info_buffer *buffer)
{
- struct hdmi_eld *e = entry->private_data;
+ struct parsed_hdmi_eld *e = &eld->info;
char buf[SND_PRINT_CHANNEL_ALLOCATION_ADVISED_BUFSIZE];
int i;
- static char *eld_versoin_names[32] = {
+ static char *eld_version_names[32] = {
"reserved",
"reserved",
"CEA-861D or below",
@@ -475,13 +499,15 @@ static void hdmi_print_eld_info(struct snd_info_entry *entry,
[4 ... 7] = "reserved"
};
- snd_iprintf(buffer, "monitor_present\t\t%d\n", e->monitor_present);
- snd_iprintf(buffer, "eld_valid\t\t%d\n", e->eld_valid);
+ snd_iprintf(buffer, "monitor_present\t\t%d\n", eld->monitor_present);
+ snd_iprintf(buffer, "eld_valid\t\t%d\n", eld->eld_valid);
+ if (!eld->eld_valid)
+ return;
snd_iprintf(buffer, "monitor_name\t\t%s\n", e->monitor_name);
snd_iprintf(buffer, "connection_type\t\t%s\n",
eld_connection_type_names[e->conn_type]);
snd_iprintf(buffer, "eld_version\t\t[0x%x] %s\n", e->eld_ver,
- eld_versoin_names[e->eld_ver]);
+ eld_version_names[e->eld_ver]);
snd_iprintf(buffer, "edid_version\t\t[0x%x] %s\n", e->cea_edid_ver,
cea_edid_version_names[e->cea_edid_ver]);
snd_iprintf(buffer, "manufacture_id\t\t0x%x\n", e->manufacture_id);
@@ -500,10 +526,10 @@ static void hdmi_print_eld_info(struct snd_info_entry *entry,
hdmi_print_sad_info(i, e->sad + i, buffer);
}
-static void hdmi_write_eld_info(struct snd_info_entry *entry,
- struct snd_info_buffer *buffer)
+void snd_hdmi_write_eld_info(struct hdmi_eld *eld,
+ struct snd_info_buffer *buffer)
{
- struct hdmi_eld *e = entry->private_data;
+ struct parsed_hdmi_eld *e = &eld->info;
char line[64];
char name[64];
char *sname;
@@ -519,9 +545,9 @@ static void hdmi_write_eld_info(struct snd_info_entry *entry,
* eld_version edid_version
*/
if (!strcmp(name, "monitor_present"))
- e->monitor_present = val;
+ eld->monitor_present = val;
else if (!strcmp(name, "eld_valid"))
- e->eld_valid = val;
+ eld->eld_valid = val;
else if (!strcmp(name, "connection_type"))
e->conn_type = val;
else if (!strcmp(name, "port_id"))
@@ -562,76 +588,219 @@ static void hdmi_write_eld_info(struct snd_info_entry *entry,
}
}
}
-
-
-int snd_hda_eld_proc_new(struct hda_codec *codec, struct hdmi_eld *eld,
- int index)
-{
- char name[32];
- struct snd_info_entry *entry;
- int err;
-
- snprintf(name, sizeof(name), "eld#%d.%d", codec->addr, index);
- err = snd_card_proc_new(codec->bus->card, name, &entry);
- if (err < 0)
- return err;
-
- snd_info_set_text_ops(entry, eld, hdmi_print_eld_info);
- entry->c.text.write = hdmi_write_eld_info;
- entry->mode |= S_IWUSR;
- eld->proc_entry = entry;
-
- return 0;
-}
-
-void snd_hda_eld_proc_free(struct hda_codec *codec, struct hdmi_eld *eld)
-{
- if (!codec->bus->shutdown && eld->proc_entry) {
- snd_device_free(codec->bus->card, eld->proc_entry);
- eld->proc_entry = NULL;
- }
-}
-
#endif /* CONFIG_PROC_FS */
/* update PCM info based on ELD */
-void hdmi_eld_update_pcm_info(struct hdmi_eld *eld, struct hda_pcm_stream *pcm,
- struct hda_pcm_stream *codec_pars)
+void snd_hdmi_eld_update_pcm_info(struct parsed_hdmi_eld *e,
+ struct hda_pcm_stream *hinfo)
{
+ u32 rates;
+ u64 formats;
+ unsigned int maxbps;
+ unsigned int channels_max;
int i;
/* assume basic audio support (the basic audio flag is not in ELD;
* however, all audio capable sinks are required to support basic
* audio) */
- pcm->rates = SNDRV_PCM_RATE_32000 | SNDRV_PCM_RATE_44100 | SNDRV_PCM_RATE_48000;
- pcm->formats = SNDRV_PCM_FMTBIT_S16_LE;
- pcm->maxbps = 16;
- pcm->channels_max = 2;
- for (i = 0; i < eld->sad_count; i++) {
- struct cea_sad *a = &eld->sad[i];
- pcm->rates |= a->rates;
- if (a->channels > pcm->channels_max)
- pcm->channels_max = a->channels;
+ rates = SNDRV_PCM_RATE_32000 | SNDRV_PCM_RATE_44100 |
+ SNDRV_PCM_RATE_48000;
+ formats = SNDRV_PCM_FMTBIT_S16_LE;
+ maxbps = 16;
+ channels_max = 2;
+ for (i = 0; i < e->sad_count; i++) {
+ struct cea_sad *a = &e->sad[i];
+ rates |= a->rates;
+ if (a->channels > channels_max)
+ channels_max = a->channels;
if (a->format == AUDIO_CODING_TYPE_LPCM) {
if (a->sample_bits & AC_SUPPCM_BITS_20) {
- pcm->formats |= SNDRV_PCM_FMTBIT_S32_LE;
- if (pcm->maxbps < 20)
- pcm->maxbps = 20;
+ formats |= SNDRV_PCM_FMTBIT_S32_LE;
+ if (maxbps < 20)
+ maxbps = 20;
}
if (a->sample_bits & AC_SUPPCM_BITS_24) {
- pcm->formats |= SNDRV_PCM_FMTBIT_S32_LE;
- if (pcm->maxbps < 24)
- pcm->maxbps = 24;
+ formats |= SNDRV_PCM_FMTBIT_S32_LE;
+ if (maxbps < 24)
+ maxbps = 24;
}
}
}
- if (!codec_pars)
- return;
-
/* restrict the parameters by the values the codec provides */
- pcm->rates &= codec_pars->rates;
- pcm->formats &= codec_pars->formats;
- pcm->channels_max = min(pcm->channels_max, codec_pars->channels_max);
- pcm->maxbps = min(pcm->maxbps, codec_pars->maxbps);
+ hinfo->rates &= rates;
+ hinfo->formats &= formats;
+ hinfo->maxbps = min(hinfo->maxbps, maxbps);
+ hinfo->channels_max = min(hinfo->channels_max, channels_max);
+}
+
+
+/* ATI/AMD specific stuff (ELD emulation) */
+
+#define ATI_VERB_SET_AUDIO_DESCRIPTOR 0x776
+#define ATI_VERB_SET_SINK_INFO_INDEX 0x780
+#define ATI_VERB_GET_SPEAKER_ALLOCATION 0xf70
+#define ATI_VERB_GET_AUDIO_DESCRIPTOR 0xf76
+#define ATI_VERB_GET_AUDIO_VIDEO_DELAY 0xf7b
+#define ATI_VERB_GET_SINK_INFO_INDEX 0xf80
+#define ATI_VERB_GET_SINK_INFO_DATA 0xf81
+
+#define ATI_SPKALLOC_SPKALLOC 0x007f
+#define ATI_SPKALLOC_TYPE_HDMI 0x0100
+#define ATI_SPKALLOC_TYPE_DISPLAYPORT 0x0200
+
+/* first three bytes are just standard SAD */
+#define ATI_AUDIODESC_CHANNELS 0x00000007
+#define ATI_AUDIODESC_RATES 0x0000ff00
+#define ATI_AUDIODESC_LPCM_STEREO_RATES 0xff000000
+
+/* in standard HDMI VSDB format */
+#define ATI_DELAY_VIDEO_LATENCY 0x000000ff
+#define ATI_DELAY_AUDIO_LATENCY 0x0000ff00
+
+enum ati_sink_info_idx {
+ ATI_INFO_IDX_MANUFACTURER_ID = 0,
+ ATI_INFO_IDX_PRODUCT_ID = 1,
+ ATI_INFO_IDX_SINK_DESC_LEN = 2,
+ ATI_INFO_IDX_PORT_ID_LOW = 3,
+ ATI_INFO_IDX_PORT_ID_HIGH = 4,
+ ATI_INFO_IDX_SINK_DESC_FIRST = 5,
+ ATI_INFO_IDX_SINK_DESC_LAST = 22, /* max len 18 bytes */
+};
+
+int snd_hdmi_get_eld_ati(struct hda_codec *codec, hda_nid_t nid,
+ unsigned char *buf, int *eld_size, bool rev3_or_later)
+{
+ int spkalloc, ati_sad, aud_synch;
+ int sink_desc_len = 0;
+ int pos, i;
+
+ /* ATI/AMD does not have ELD, emulate it */
+
+ spkalloc = snd_hda_codec_read(codec, nid, 0, ATI_VERB_GET_SPEAKER_ALLOCATION, 0);
+
+ if (spkalloc <= 0) {
+ codec_info(codec, "HDMI ATI/AMD: no speaker allocation for ELD\n");
+ return -EINVAL;
+ }
+
+ memset(buf, 0, ELD_FIXED_BYTES + ELD_MAX_MNL + ELD_MAX_SAD * 3);
+
+ /* version */
+ buf[0] = ELD_VER_CEA_861D << 3;
+
+ /* speaker allocation from EDID */
+ buf[7] = spkalloc & ATI_SPKALLOC_SPKALLOC;
+
+ /* is DisplayPort? */
+ if (spkalloc & ATI_SPKALLOC_TYPE_DISPLAYPORT)
+ buf[5] |= 0x04;
+
+ pos = ELD_FIXED_BYTES;
+
+ if (rev3_or_later) {
+ int sink_info;
+
+ snd_hda_codec_write(codec, nid, 0, ATI_VERB_SET_SINK_INFO_INDEX, ATI_INFO_IDX_PORT_ID_LOW);
+ sink_info = snd_hda_codec_read(codec, nid, 0, ATI_VERB_GET_SINK_INFO_DATA, 0);
+ put_unaligned_le32(sink_info, buf + 8);
+
+ snd_hda_codec_write(codec, nid, 0, ATI_VERB_SET_SINK_INFO_INDEX, ATI_INFO_IDX_PORT_ID_HIGH);
+ sink_info = snd_hda_codec_read(codec, nid, 0, ATI_VERB_GET_SINK_INFO_DATA, 0);
+ put_unaligned_le32(sink_info, buf + 12);
+
+ snd_hda_codec_write(codec, nid, 0, ATI_VERB_SET_SINK_INFO_INDEX, ATI_INFO_IDX_MANUFACTURER_ID);
+ sink_info = snd_hda_codec_read(codec, nid, 0, ATI_VERB_GET_SINK_INFO_DATA, 0);
+ put_unaligned_le16(sink_info, buf + 16);
+
+ snd_hda_codec_write(codec, nid, 0, ATI_VERB_SET_SINK_INFO_INDEX, ATI_INFO_IDX_PRODUCT_ID);
+ sink_info = snd_hda_codec_read(codec, nid, 0, ATI_VERB_GET_SINK_INFO_DATA, 0);
+ put_unaligned_le16(sink_info, buf + 18);
+
+ snd_hda_codec_write(codec, nid, 0, ATI_VERB_SET_SINK_INFO_INDEX, ATI_INFO_IDX_SINK_DESC_LEN);
+ sink_desc_len = snd_hda_codec_read(codec, nid, 0, ATI_VERB_GET_SINK_INFO_DATA, 0);
+
+ if (sink_desc_len > ELD_MAX_MNL) {
+ codec_info(codec, "HDMI ATI/AMD: Truncating HDMI sink description with length %d\n",
+ sink_desc_len);
+ sink_desc_len = ELD_MAX_MNL;
+ }
+
+ buf[4] |= sink_desc_len;
+
+ for (i = 0; i < sink_desc_len; i++) {
+ snd_hda_codec_write(codec, nid, 0, ATI_VERB_SET_SINK_INFO_INDEX, ATI_INFO_IDX_SINK_DESC_FIRST + i);
+ buf[pos++] = snd_hda_codec_read(codec, nid, 0, ATI_VERB_GET_SINK_INFO_DATA, 0);
+ }
+ }
+
+ for (i = AUDIO_CODING_TYPE_LPCM; i <= AUDIO_CODING_TYPE_WMAPRO; i++) {
+ if (i == AUDIO_CODING_TYPE_SACD || i == AUDIO_CODING_TYPE_DST)
+ continue; /* not handled by ATI/AMD */
+
+ snd_hda_codec_write(codec, nid, 0, ATI_VERB_SET_AUDIO_DESCRIPTOR, i << 3);
+ ati_sad = snd_hda_codec_read(codec, nid, 0, ATI_VERB_GET_AUDIO_DESCRIPTOR, 0);
+
+ if (ati_sad <= 0)
+ continue;
+
+ if (ati_sad & ATI_AUDIODESC_RATES) {
+ /* format is supported, copy SAD as-is */
+ buf[pos++] = (ati_sad & 0x0000ff) >> 0;
+ buf[pos++] = (ati_sad & 0x00ff00) >> 8;
+ buf[pos++] = (ati_sad & 0xff0000) >> 16;
+ }
+
+ if (i == AUDIO_CODING_TYPE_LPCM
+ && (ati_sad & ATI_AUDIODESC_LPCM_STEREO_RATES)
+ && (ati_sad & ATI_AUDIODESC_LPCM_STEREO_RATES) >> 16 != (ati_sad & ATI_AUDIODESC_RATES)) {
+ /* for PCM there is a separate stereo rate mask */
+ buf[pos++] = ((ati_sad & 0x000000ff) & ~ATI_AUDIODESC_CHANNELS) | 0x1;
+ /* rates from the extra byte */
+ buf[pos++] = (ati_sad & 0xff000000) >> 24;
+ buf[pos++] = (ati_sad & 0x00ff0000) >> 16;
+ }
+ }
+
+ if (pos == ELD_FIXED_BYTES + sink_desc_len) {
+ codec_info(codec, "HDMI ATI/AMD: no audio descriptors for ELD\n");
+ return -EINVAL;
+ }
+
+ /*
+ * HDMI VSDB latency format:
+ * separately for both audio and video:
+ * 0 field not valid or unknown latency
+ * [1..251] msecs = (x-1)*2 (max 500ms with x = 251 = 0xfb)
+ * 255 audio/video not supported
+ *
+ * HDA latency format:
+ * single value indicating video latency relative to audio:
+ * 0 unknown or 0ms
+ * [1..250] msecs = x*2 (max 500ms with x = 250 = 0xfa)
+ * [251..255] reserved
+ */
+ aud_synch = snd_hda_codec_read(codec, nid, 0, ATI_VERB_GET_AUDIO_VIDEO_DELAY, 0);
+ if ((aud_synch & ATI_DELAY_VIDEO_LATENCY) && (aud_synch & ATI_DELAY_AUDIO_LATENCY)) {
+ int video_latency_hdmi = (aud_synch & ATI_DELAY_VIDEO_LATENCY);
+ int audio_latency_hdmi = (aud_synch & ATI_DELAY_AUDIO_LATENCY) >> 8;
+
+ if (video_latency_hdmi <= 0xfb && audio_latency_hdmi <= 0xfb &&
+ video_latency_hdmi > audio_latency_hdmi)
+ buf[6] = video_latency_hdmi - audio_latency_hdmi;
+ /* else unknown/invalid or 0ms or video ahead of audio, so use zero */
+ }
+
+ /* SAD count */
+ buf[5] |= ((pos - ELD_FIXED_BYTES - sink_desc_len) / 3) << 4;
+
+ /* Baseline ELD block length is 4-byte aligned */
+ pos = round_up(pos, 4);
+
+ /* Baseline ELD length (4-byte header is not counted in) */
+ buf[2] = (pos - 4) / 4;
+
+ *eld_size = pos;
+
+ return 0;
}
diff --git a/sound/pci/hda/hda_generic.c b/sound/pci/hda/hda_generic.c
index a63c54d9d76..589e47c5aeb 100644
--- a/sound/pci/hda/hda_generic.c
+++ b/sound/pci/hda/hda_generic.c
@@ -22,836 +22,3577 @@
#include <linux/init.h>
#include <linux/slab.h>
+#include <linux/export.h>
+#include <linux/sort.h>
+#include <linux/delay.h>
+#include <linux/ctype.h>
+#include <linux/string.h>
+#include <linux/bitops.h>
+#include <linux/module.h>
#include <sound/core.h>
+#include <sound/jack.h>
#include "hda_codec.h"
#include "hda_local.h"
+#include "hda_auto_parser.h"
+#include "hda_jack.h"
+#include "hda_beep.h"
+#include "hda_generic.h"
-/* widget node for parsing */
-struct hda_gnode {
- hda_nid_t nid; /* NID of this widget */
- unsigned short nconns; /* number of input connections */
- hda_nid_t *conn_list;
- hda_nid_t slist[2]; /* temporay list */
- unsigned int wid_caps; /* widget capabilities */
- unsigned char type; /* widget type */
- unsigned char pin_ctl; /* pin controls */
- unsigned char checked; /* the flag indicates that the node is already parsed */
- unsigned int pin_caps; /* pin widget capabilities */
- unsigned int def_cfg; /* default configuration */
- unsigned int amp_out_caps; /* AMP out capabilities */
- unsigned int amp_in_caps; /* AMP in capabilities */
- struct list_head list;
-};
-/* patch-specific record */
+/* initialize hda_gen_spec struct */
+int snd_hda_gen_spec_init(struct hda_gen_spec *spec)
+{
+ snd_array_init(&spec->kctls, sizeof(struct snd_kcontrol_new), 32);
+ snd_array_init(&spec->paths, sizeof(struct nid_path), 8);
+ snd_array_init(&spec->loopback_list, sizeof(struct hda_amp_list), 8);
+ mutex_init(&spec->pcm_mutex);
+ return 0;
+}
+EXPORT_SYMBOL_GPL(snd_hda_gen_spec_init);
-#define MAX_PCM_VOLS 2
-struct pcm_vol {
- struct hda_gnode *node; /* Node for PCM volume */
- unsigned int index; /* connection of PCM volume */
-};
+struct snd_kcontrol_new *
+snd_hda_gen_add_kctl(struct hda_gen_spec *spec, const char *name,
+ const struct snd_kcontrol_new *temp)
+{
+ struct snd_kcontrol_new *knew = snd_array_new(&spec->kctls);
+ if (!knew)
+ return NULL;
+ *knew = *temp;
+ if (name)
+ knew->name = kstrdup(name, GFP_KERNEL);
+ else if (knew->name)
+ knew->name = kstrdup(knew->name, GFP_KERNEL);
+ if (!knew->name)
+ return NULL;
+ return knew;
+}
+EXPORT_SYMBOL_GPL(snd_hda_gen_add_kctl);
-struct hda_gspec {
- struct hda_gnode *dac_node[2]; /* DAC node */
- struct hda_gnode *out_pin_node[2]; /* Output pin (Line-Out) node */
- struct pcm_vol pcm_vol[MAX_PCM_VOLS]; /* PCM volumes */
- unsigned int pcm_vol_nodes; /* number of PCM volumes */
+static void free_kctls(struct hda_gen_spec *spec)
+{
+ if (spec->kctls.list) {
+ struct snd_kcontrol_new *kctl = spec->kctls.list;
+ int i;
+ for (i = 0; i < spec->kctls.used; i++)
+ kfree(kctl[i].name);
+ }
+ snd_array_free(&spec->kctls);
+}
- struct hda_gnode *adc_node; /* ADC node */
- struct hda_gnode *cap_vol_node; /* Node for capture volume */
- unsigned int cur_cap_src; /* current capture source */
- struct hda_input_mux input_mux;
+static void snd_hda_gen_spec_free(struct hda_gen_spec *spec)
+{
+ if (!spec)
+ return;
+ free_kctls(spec);
+ snd_array_free(&spec->paths);
+ snd_array_free(&spec->loopback_list);
+}
- unsigned int def_amp_in_caps;
- unsigned int def_amp_out_caps;
+/*
+ * store user hints
+ */
+static void parse_user_hints(struct hda_codec *codec)
+{
+ struct hda_gen_spec *spec = codec->spec;
+ int val;
- struct hda_pcm pcm_rec; /* PCM information */
+ val = snd_hda_get_bool_hint(codec, "jack_detect");
+ if (val >= 0)
+ codec->no_jack_detect = !val;
+ val = snd_hda_get_bool_hint(codec, "inv_jack_detect");
+ if (val >= 0)
+ codec->inv_jack_detect = !!val;
+ val = snd_hda_get_bool_hint(codec, "trigger_sense");
+ if (val >= 0)
+ codec->no_trigger_sense = !val;
+ val = snd_hda_get_bool_hint(codec, "inv_eapd");
+ if (val >= 0)
+ codec->inv_eapd = !!val;
+ val = snd_hda_get_bool_hint(codec, "pcm_format_first");
+ if (val >= 0)
+ codec->pcm_format_first = !!val;
+ val = snd_hda_get_bool_hint(codec, "sticky_stream");
+ if (val >= 0)
+ codec->no_sticky_stream = !val;
+ val = snd_hda_get_bool_hint(codec, "spdif_status_reset");
+ if (val >= 0)
+ codec->spdif_status_reset = !!val;
+ val = snd_hda_get_bool_hint(codec, "pin_amp_workaround");
+ if (val >= 0)
+ codec->pin_amp_workaround = !!val;
+ val = snd_hda_get_bool_hint(codec, "single_adc_amp");
+ if (val >= 0)
+ codec->single_adc_amp = !!val;
- struct list_head nid_list; /* list of widgets */
+ val = snd_hda_get_bool_hint(codec, "auto_mute");
+ if (val >= 0)
+ spec->suppress_auto_mute = !val;
+ val = snd_hda_get_bool_hint(codec, "auto_mic");
+ if (val >= 0)
+ spec->suppress_auto_mic = !val;
+ val = snd_hda_get_bool_hint(codec, "line_in_auto_switch");
+ if (val >= 0)
+ spec->line_in_auto_switch = !!val;
+ val = snd_hda_get_bool_hint(codec, "auto_mute_via_amp");
+ if (val >= 0)
+ spec->auto_mute_via_amp = !!val;
+ val = snd_hda_get_bool_hint(codec, "need_dac_fix");
+ if (val >= 0)
+ spec->need_dac_fix = !!val;
+ val = snd_hda_get_bool_hint(codec, "primary_hp");
+ if (val >= 0)
+ spec->no_primary_hp = !val;
+ val = snd_hda_get_bool_hint(codec, "multi_io");
+ if (val >= 0)
+ spec->no_multi_io = !val;
+ val = snd_hda_get_bool_hint(codec, "multi_cap_vol");
+ if (val >= 0)
+ spec->multi_cap_vol = !!val;
+ val = snd_hda_get_bool_hint(codec, "inv_dmic_split");
+ if (val >= 0)
+ spec->inv_dmic_split = !!val;
+ val = snd_hda_get_bool_hint(codec, "indep_hp");
+ if (val >= 0)
+ spec->indep_hp = !!val;
+ val = snd_hda_get_bool_hint(codec, "add_stereo_mix_input");
+ if (val >= 0)
+ spec->add_stereo_mix_input = !!val;
+ /* the following two are just for compatibility */
+ val = snd_hda_get_bool_hint(codec, "add_out_jack_modes");
+ if (val >= 0)
+ spec->add_jack_modes = !!val;
+ val = snd_hda_get_bool_hint(codec, "add_in_jack_modes");
+ if (val >= 0)
+ spec->add_jack_modes = !!val;
+ val = snd_hda_get_bool_hint(codec, "add_jack_modes");
+ if (val >= 0)
+ spec->add_jack_modes = !!val;
+ val = snd_hda_get_bool_hint(codec, "power_down_unused");
+ if (val >= 0)
+ spec->power_down_unused = !!val;
+ val = snd_hda_get_bool_hint(codec, "add_hp_mic");
+ if (val >= 0)
+ spec->hp_mic = !!val;
+ val = snd_hda_get_bool_hint(codec, "hp_mic_detect");
+ if (val >= 0)
+ spec->suppress_hp_mic_detect = !val;
-#ifdef CONFIG_SND_HDA_POWER_SAVE
-#define MAX_LOOPBACK_AMPS 7
- struct hda_loopback_check loopback;
- int num_loopbacks;
- struct hda_amp_list loopback_list[MAX_LOOPBACK_AMPS + 1];
-#endif
-};
+ if (!snd_hda_get_int_hint(codec, "mixer_nid", &val))
+ spec->mixer_nid = val;
+}
/*
- * retrieve the default device type from the default config value
+ * pin control value accesses
*/
-#define defcfg_type(node) (((node)->def_cfg & AC_DEFCFG_DEVICE) >> \
- AC_DEFCFG_DEVICE_SHIFT)
-#define defcfg_location(node) (((node)->def_cfg & AC_DEFCFG_LOCATION) >> \
- AC_DEFCFG_LOCATION_SHIFT)
-#define defcfg_port_conn(node) (((node)->def_cfg & AC_DEFCFG_PORT_CONN) >> \
- AC_DEFCFG_PORT_CONN_SHIFT)
-/*
- * destructor
- */
-static void snd_hda_generic_free(struct hda_codec *codec)
+#define update_pin_ctl(codec, pin, val) \
+ snd_hda_codec_update_cache(codec, pin, 0, \
+ AC_VERB_SET_PIN_WIDGET_CONTROL, val)
+
+/* restore the pinctl based on the cached value */
+static inline void restore_pin_ctl(struct hda_codec *codec, hda_nid_t pin)
{
- struct hda_gspec *spec = codec->spec;
- struct hda_gnode *node, *n;
+ update_pin_ctl(codec, pin, snd_hda_codec_get_pin_target(codec, pin));
+}
- if (! spec)
+/* set the pinctl target value and write it if requested */
+static void set_pin_target(struct hda_codec *codec, hda_nid_t pin,
+ unsigned int val, bool do_write)
+{
+ if (!pin)
return;
- /* free all widgets */
- list_for_each_entry_safe(node, n, &spec->nid_list, list) {
- if (node->conn_list != node->slist)
- kfree(node->conn_list);
- kfree(node);
- }
- kfree(spec);
+ val = snd_hda_correct_pin_ctl(codec, pin, val);
+ snd_hda_codec_set_pin_target(codec, pin, val);
+ if (do_write)
+ update_pin_ctl(codec, pin, val);
}
+/* set pinctl target values for all given pins */
+static void set_pin_targets(struct hda_codec *codec, int num_pins,
+ hda_nid_t *pins, unsigned int val)
+{
+ int i;
+ for (i = 0; i < num_pins; i++)
+ set_pin_target(codec, pins[i], val, false);
+}
/*
- * add a new widget node and read its attributes
+ * parsing paths
*/
-static int add_new_node(struct hda_codec *codec, struct hda_gspec *spec, hda_nid_t nid)
+
+/* return the position of NID in the list, or -1 if not found */
+static int find_idx_in_nid_list(hda_nid_t nid, const hda_nid_t *list, int nums)
{
- struct hda_gnode *node;
- int nconns;
- hda_nid_t conn_list[HDA_MAX_CONNECTIONS];
+ int i;
+ for (i = 0; i < nums; i++)
+ if (list[i] == nid)
+ return i;
+ return -1;
+}
- node = kzalloc(sizeof(*node), GFP_KERNEL);
- if (node == NULL)
- return -ENOMEM;
- node->nid = nid;
- node->wid_caps = get_wcaps(codec, nid);
- node->type = get_wcaps_type(node->wid_caps);
- if (node->wid_caps & AC_WCAP_CONN_LIST) {
- nconns = snd_hda_get_connections(codec, nid, conn_list,
- HDA_MAX_CONNECTIONS);
- if (nconns < 0) {
- kfree(node);
- return nconns;
- }
- } else {
- nconns = 0;
- }
- if (nconns <= ARRAY_SIZE(node->slist))
- node->conn_list = node->slist;
- else {
- node->conn_list = kmalloc(sizeof(hda_nid_t) * nconns,
- GFP_KERNEL);
- if (! node->conn_list) {
- snd_printk(KERN_ERR "hda-generic: cannot malloc\n");
- kfree(node);
- return -ENOMEM;
+/* return true if the given NID is contained in the path */
+static bool is_nid_contained(struct nid_path *path, hda_nid_t nid)
+{
+ return find_idx_in_nid_list(nid, path->path, path->depth) >= 0;
+}
+
+static struct nid_path *get_nid_path(struct hda_codec *codec,
+ hda_nid_t from_nid, hda_nid_t to_nid,
+ int anchor_nid)
+{
+ struct hda_gen_spec *spec = codec->spec;
+ int i;
+
+ for (i = 0; i < spec->paths.used; i++) {
+ struct nid_path *path = snd_array_elem(&spec->paths, i);
+ if (path->depth <= 0)
+ continue;
+ if ((!from_nid || path->path[0] == from_nid) &&
+ (!to_nid || path->path[path->depth - 1] == to_nid)) {
+ if (!anchor_nid ||
+ (anchor_nid > 0 && is_nid_contained(path, anchor_nid)) ||
+ (anchor_nid < 0 && !is_nid_contained(path, anchor_nid)))
+ return path;
}
}
- memcpy(node->conn_list, conn_list, nconns * sizeof(hda_nid_t));
- node->nconns = nconns;
+ return NULL;
+}
+
+/* get the path between the given NIDs;
+ * passing 0 to either @pin or @dac behaves as a wildcard
+ */
+struct nid_path *snd_hda_get_nid_path(struct hda_codec *codec,
+ hda_nid_t from_nid, hda_nid_t to_nid)
+{
+ return get_nid_path(codec, from_nid, to_nid, 0);
+}
+EXPORT_SYMBOL_GPL(snd_hda_get_nid_path);
+
+/* get the index number corresponding to the path instance;
+ * the index starts from 1, for easier checking the invalid value
+ */
+int snd_hda_get_path_idx(struct hda_codec *codec, struct nid_path *path)
+{
+ struct hda_gen_spec *spec = codec->spec;
+ struct nid_path *array = spec->paths.list;
+ ssize_t idx;
+
+ if (!spec->paths.used)
+ return 0;
+ idx = path - array;
+ if (idx < 0 || idx >= spec->paths.used)
+ return 0;
+ return idx + 1;
+}
+EXPORT_SYMBOL_GPL(snd_hda_get_path_idx);
- if (node->type == AC_WID_PIN) {
- node->pin_caps = snd_hda_query_pin_caps(codec, node->nid);
- node->pin_ctl = snd_hda_codec_read(codec, node->nid, 0, AC_VERB_GET_PIN_WIDGET_CONTROL, 0);
- node->def_cfg = snd_hda_codec_get_pincfg(codec, node->nid);
+/* get the path instance corresponding to the given index number */
+struct nid_path *snd_hda_get_path_from_idx(struct hda_codec *codec, int idx)
+{
+ struct hda_gen_spec *spec = codec->spec;
+
+ if (idx <= 0 || idx > spec->paths.used)
+ return NULL;
+ return snd_array_elem(&spec->paths, idx - 1);
+}
+EXPORT_SYMBOL_GPL(snd_hda_get_path_from_idx);
+
+/* check whether the given DAC is already found in any existing paths */
+static bool is_dac_already_used(struct hda_codec *codec, hda_nid_t nid)
+{
+ struct hda_gen_spec *spec = codec->spec;
+ int i;
+
+ for (i = 0; i < spec->paths.used; i++) {
+ struct nid_path *path = snd_array_elem(&spec->paths, i);
+ if (path->path[0] == nid)
+ return true;
}
+ return false;
+}
+
+/* check whether the given two widgets can be connected */
+static bool is_reachable_path(struct hda_codec *codec,
+ hda_nid_t from_nid, hda_nid_t to_nid)
+{
+ if (!from_nid || !to_nid)
+ return false;
+ return snd_hda_get_conn_index(codec, to_nid, from_nid, true) >= 0;
+}
+
+/* nid, dir and idx */
+#define AMP_VAL_COMPARE_MASK (0xffff | (1U << 18) | (0x0f << 19))
+
+/* check whether the given ctl is already assigned in any path elements */
+static bool is_ctl_used(struct hda_codec *codec, unsigned int val, int type)
+{
+ struct hda_gen_spec *spec = codec->spec;
+ int i;
- if (node->wid_caps & AC_WCAP_OUT_AMP) {
- if (node->wid_caps & AC_WCAP_AMP_OVRD)
- node->amp_out_caps = snd_hda_param_read(codec, node->nid, AC_PAR_AMP_OUT_CAP);
- if (! node->amp_out_caps)
- node->amp_out_caps = spec->def_amp_out_caps;
+ val &= AMP_VAL_COMPARE_MASK;
+ for (i = 0; i < spec->paths.used; i++) {
+ struct nid_path *path = snd_array_elem(&spec->paths, i);
+ if ((path->ctls[type] & AMP_VAL_COMPARE_MASK) == val)
+ return true;
}
- if (node->wid_caps & AC_WCAP_IN_AMP) {
- if (node->wid_caps & AC_WCAP_AMP_OVRD)
- node->amp_in_caps = snd_hda_param_read(codec, node->nid, AC_PAR_AMP_IN_CAP);
- if (! node->amp_in_caps)
- node->amp_in_caps = spec->def_amp_in_caps;
+ return false;
+}
+
+/* check whether a control with the given (nid, dir, idx) was assigned */
+static bool is_ctl_associated(struct hda_codec *codec, hda_nid_t nid,
+ int dir, int idx, int type)
+{
+ unsigned int val = HDA_COMPOSE_AMP_VAL(nid, 3, idx, dir);
+ return is_ctl_used(codec, val, type);
+}
+
+static void print_nid_path(struct hda_codec *codec,
+ const char *pfx, struct nid_path *path)
+{
+ char buf[40];
+ int i;
+
+
+ buf[0] = 0;
+ for (i = 0; i < path->depth; i++) {
+ char tmp[4];
+ sprintf(tmp, ":%02x", path->path[i]);
+ strlcat(buf, tmp, sizeof(buf));
}
- list_add_tail(&node->list, &spec->nid_list);
- return 0;
+ codec_dbg(codec, "%s path: depth=%d %s\n", pfx, path->depth, buf);
}
+/* called recursively */
+static bool __parse_nid_path(struct hda_codec *codec,
+ hda_nid_t from_nid, hda_nid_t to_nid,
+ int anchor_nid, struct nid_path *path,
+ int depth)
+{
+ const hda_nid_t *conn;
+ int i, nums;
+
+ if (to_nid == anchor_nid)
+ anchor_nid = 0; /* anchor passed */
+ else if (to_nid == (hda_nid_t)(-anchor_nid))
+ return false; /* hit the exclusive nid */
+
+ nums = snd_hda_get_conn_list(codec, to_nid, &conn);
+ for (i = 0; i < nums; i++) {
+ if (conn[i] != from_nid) {
+ /* special case: when from_nid is 0,
+ * try to find an empty DAC
+ */
+ if (from_nid ||
+ get_wcaps_type(get_wcaps(codec, conn[i])) != AC_WID_AUD_OUT ||
+ is_dac_already_used(codec, conn[i]))
+ continue;
+ }
+ /* anchor is not requested or already passed? */
+ if (anchor_nid <= 0)
+ goto found;
+ }
+ if (depth >= MAX_NID_PATH_DEPTH)
+ return false;
+ for (i = 0; i < nums; i++) {
+ unsigned int type;
+ type = get_wcaps_type(get_wcaps(codec, conn[i]));
+ if (type == AC_WID_AUD_OUT || type == AC_WID_AUD_IN ||
+ type == AC_WID_PIN)
+ continue;
+ if (__parse_nid_path(codec, from_nid, conn[i],
+ anchor_nid, path, depth + 1))
+ goto found;
+ }
+ return false;
+
+ found:
+ path->path[path->depth] = conn[i];
+ path->idx[path->depth + 1] = i;
+ if (nums > 1 && get_wcaps_type(get_wcaps(codec, to_nid)) != AC_WID_AUD_MIX)
+ path->multi[path->depth + 1] = 1;
+ path->depth++;
+ return true;
+}
+
+/* parse the widget path from the given nid to the target nid;
+ * when @from_nid is 0, try to find an empty DAC;
+ * when @anchor_nid is set to a positive value, only paths through the widget
+ * with the given value are evaluated.
+ * when @anchor_nid is set to a negative value, paths through the widget
+ * with the negative of given value are excluded, only other paths are chosen.
+ * when @anchor_nid is zero, no special handling about path selection.
+ */
+bool snd_hda_parse_nid_path(struct hda_codec *codec, hda_nid_t from_nid,
+ hda_nid_t to_nid, int anchor_nid,
+ struct nid_path *path)
+{
+ if (__parse_nid_path(codec, from_nid, to_nid, anchor_nid, path, 1)) {
+ path->path[path->depth] = to_nid;
+ path->depth++;
+ return true;
+ }
+ return false;
+}
+EXPORT_SYMBOL_GPL(snd_hda_parse_nid_path);
+
/*
- * build the AFG subtree
+ * parse the path between the given NIDs and add to the path list.
+ * if no valid path is found, return NULL
*/
-static int build_afg_tree(struct hda_codec *codec)
+struct nid_path *
+snd_hda_add_new_path(struct hda_codec *codec, hda_nid_t from_nid,
+ hda_nid_t to_nid, int anchor_nid)
{
- struct hda_gspec *spec = codec->spec;
- int i, nodes, err;
- hda_nid_t nid;
+ struct hda_gen_spec *spec = codec->spec;
+ struct nid_path *path;
- if (snd_BUG_ON(!spec))
- return -EINVAL;
+ if (from_nid && to_nid && !is_reachable_path(codec, from_nid, to_nid))
+ return NULL;
- spec->def_amp_out_caps = snd_hda_param_read(codec, codec->afg, AC_PAR_AMP_OUT_CAP);
- spec->def_amp_in_caps = snd_hda_param_read(codec, codec->afg, AC_PAR_AMP_IN_CAP);
+ /* check whether the path has been already added */
+ path = get_nid_path(codec, from_nid, to_nid, anchor_nid);
+ if (path)
+ return path;
- nodes = snd_hda_get_sub_nodes(codec, codec->afg, &nid);
- if (! nid || nodes < 0) {
- printk(KERN_ERR "Invalid AFG subtree\n");
- return -EINVAL;
- }
+ path = snd_array_new(&spec->paths);
+ if (!path)
+ return NULL;
+ memset(path, 0, sizeof(*path));
+ if (snd_hda_parse_nid_path(codec, from_nid, to_nid, anchor_nid, path))
+ return path;
+ /* push back */
+ spec->paths.used--;
+ return NULL;
+}
+EXPORT_SYMBOL_GPL(snd_hda_add_new_path);
- /* parse all nodes belonging to the AFG */
- for (i = 0; i < nodes; i++, nid++) {
- if ((err = add_new_node(codec, spec, nid)) < 0)
- return err;
+/* clear the given path as invalid so that it won't be picked up later */
+static void invalidate_nid_path(struct hda_codec *codec, int idx)
+{
+ struct nid_path *path = snd_hda_get_path_from_idx(codec, idx);
+ if (!path)
+ return;
+ memset(path, 0, sizeof(*path));
+}
+
+/* return a DAC if paired to the given pin by codec driver */
+static hda_nid_t get_preferred_dac(struct hda_codec *codec, hda_nid_t pin)
+{
+ struct hda_gen_spec *spec = codec->spec;
+ const hda_nid_t *list = spec->preferred_dacs;
+
+ if (!list)
+ return 0;
+ for (; *list; list += 2)
+ if (*list == pin)
+ return list[1];
+ return 0;
+}
+
+/* look for an empty DAC slot */
+static hda_nid_t look_for_dac(struct hda_codec *codec, hda_nid_t pin,
+ bool is_digital)
+{
+ struct hda_gen_spec *spec = codec->spec;
+ bool cap_digital;
+ int i;
+
+ for (i = 0; i < spec->num_all_dacs; i++) {
+ hda_nid_t nid = spec->all_dacs[i];
+ if (!nid || is_dac_already_used(codec, nid))
+ continue;
+ cap_digital = !!(get_wcaps(codec, nid) & AC_WCAP_DIGITAL);
+ if (is_digital != cap_digital)
+ continue;
+ if (is_reachable_path(codec, nid, pin))
+ return nid;
}
+ return 0;
+}
+
+/* replace the channels in the composed amp value with the given number */
+static unsigned int amp_val_replace_channels(unsigned int val, unsigned int chs)
+{
+ val &= ~(0x3U << 16);
+ val |= chs << 16;
+ return val;
+}
+
+/* check whether the widget has the given amp capability for the direction */
+static bool check_amp_caps(struct hda_codec *codec, hda_nid_t nid,
+ int dir, unsigned int bits)
+{
+ if (!nid)
+ return false;
+ if (get_wcaps(codec, nid) & (1 << (dir + 1)))
+ if (query_amp_caps(codec, nid, dir) & bits)
+ return true;
+ return false;
+}
+
+static bool same_amp_caps(struct hda_codec *codec, hda_nid_t nid1,
+ hda_nid_t nid2, int dir)
+{
+ if (!(get_wcaps(codec, nid1) & (1 << (dir + 1))))
+ return !(get_wcaps(codec, nid2) & (1 << (dir + 1)));
+ return (query_amp_caps(codec, nid1, dir) ==
+ query_amp_caps(codec, nid2, dir));
+}
+#define nid_has_mute(codec, nid, dir) \
+ check_amp_caps(codec, nid, dir, (AC_AMPCAP_MUTE | AC_AMPCAP_MIN_MUTE))
+#define nid_has_volume(codec, nid, dir) \
+ check_amp_caps(codec, nid, dir, AC_AMPCAP_NUM_STEPS)
+
+/* look for a widget suitable for assigning a mute switch in the path */
+static hda_nid_t look_for_out_mute_nid(struct hda_codec *codec,
+ struct nid_path *path)
+{
+ int i;
+
+ for (i = path->depth - 1; i >= 0; i--) {
+ if (nid_has_mute(codec, path->path[i], HDA_OUTPUT))
+ return path->path[i];
+ if (i != path->depth - 1 && i != 0 &&
+ nid_has_mute(codec, path->path[i], HDA_INPUT))
+ return path->path[i];
+ }
return 0;
}
+/* look for a widget suitable for assigning a volume ctl in the path */
+static hda_nid_t look_for_out_vol_nid(struct hda_codec *codec,
+ struct nid_path *path)
+{
+ struct hda_gen_spec *spec = codec->spec;
+ int i;
+
+ for (i = path->depth - 1; i >= 0; i--) {
+ hda_nid_t nid = path->path[i];
+ if ((spec->out_vol_mask >> nid) & 1)
+ continue;
+ if (nid_has_volume(codec, nid, HDA_OUTPUT))
+ return nid;
+ }
+ return 0;
+}
/*
- * look for the node record for the given NID
+ * path activation / deactivation
*/
-/* FIXME: should avoid the braindead linear search */
-static struct hda_gnode *hda_get_node(struct hda_gspec *spec, hda_nid_t nid)
+
+/* can have the amp-in capability? */
+static bool has_amp_in(struct hda_codec *codec, struct nid_path *path, int idx)
+{
+ hda_nid_t nid = path->path[idx];
+ unsigned int caps = get_wcaps(codec, nid);
+ unsigned int type = get_wcaps_type(caps);
+
+ if (!(caps & AC_WCAP_IN_AMP))
+ return false;
+ if (type == AC_WID_PIN && idx > 0) /* only for input pins */
+ return false;
+ return true;
+}
+
+/* can have the amp-out capability? */
+static bool has_amp_out(struct hda_codec *codec, struct nid_path *path, int idx)
+{
+ hda_nid_t nid = path->path[idx];
+ unsigned int caps = get_wcaps(codec, nid);
+ unsigned int type = get_wcaps_type(caps);
+
+ if (!(caps & AC_WCAP_OUT_AMP))
+ return false;
+ if (type == AC_WID_PIN && !idx) /* only for output pins */
+ return false;
+ return true;
+}
+
+/* check whether the given (nid,dir,idx) is active */
+static bool is_active_nid(struct hda_codec *codec, hda_nid_t nid,
+ unsigned int dir, unsigned int idx)
{
- struct hda_gnode *node;
+ struct hda_gen_spec *spec = codec->spec;
+ int i, n;
- list_for_each_entry(node, &spec->nid_list, list) {
- if (node->nid == nid)
- return node;
+ for (n = 0; n < spec->paths.used; n++) {
+ struct nid_path *path = snd_array_elem(&spec->paths, n);
+ if (!path->active)
+ continue;
+ for (i = 0; i < path->depth; i++) {
+ if (path->path[i] == nid) {
+ if (dir == HDA_OUTPUT || path->idx[i] == idx)
+ return true;
+ break;
+ }
+ }
}
- return NULL;
+ return false;
}
-/*
- * unmute (and set max vol) the output amplifier
+/* check whether the NID is referred by any active paths */
+#define is_active_nid_for_any(codec, nid) \
+ is_active_nid(codec, nid, HDA_OUTPUT, 0)
+
+/* get the default amp value for the target state */
+static int get_amp_val_to_activate(struct hda_codec *codec, hda_nid_t nid,
+ int dir, unsigned int caps, bool enable)
+{
+ unsigned int val = 0;
+
+ if (caps & AC_AMPCAP_NUM_STEPS) {
+ /* set to 0dB */
+ if (enable)
+ val = (caps & AC_AMPCAP_OFFSET) >> AC_AMPCAP_OFFSET_SHIFT;
+ }
+ if (caps & (AC_AMPCAP_MUTE | AC_AMPCAP_MIN_MUTE)) {
+ if (!enable)
+ val |= HDA_AMP_MUTE;
+ }
+ return val;
+}
+
+/* initialize the amp value (only at the first time) */
+static void init_amp(struct hda_codec *codec, hda_nid_t nid, int dir, int idx)
+{
+ unsigned int caps = query_amp_caps(codec, nid, dir);
+ int val = get_amp_val_to_activate(codec, nid, dir, caps, false);
+ snd_hda_codec_amp_init_stereo(codec, nid, dir, idx, 0xff, val);
+}
+
+/* calculate amp value mask we can modify;
+ * if the given amp is controlled by mixers, don't touch it
*/
-static int unmute_output(struct hda_codec *codec, struct hda_gnode *node)
+static unsigned int get_amp_mask_to_modify(struct hda_codec *codec,
+ hda_nid_t nid, int dir, int idx,
+ unsigned int caps)
{
- unsigned int val, ofs;
- snd_printdd("UNMUTE OUT: NID=0x%x\n", node->nid);
- val = (node->amp_out_caps & AC_AMPCAP_NUM_STEPS) >> AC_AMPCAP_NUM_STEPS_SHIFT;
- ofs = (node->amp_out_caps & AC_AMPCAP_OFFSET) >> AC_AMPCAP_OFFSET_SHIFT;
- if (val >= ofs)
- val -= ofs;
- snd_hda_codec_amp_stereo(codec, node->nid, HDA_OUTPUT, 0, 0xff, val);
- return 0;
+ unsigned int mask = 0xff;
+
+ if (caps & (AC_AMPCAP_MUTE | AC_AMPCAP_MIN_MUTE)) {
+ if (is_ctl_associated(codec, nid, dir, idx, NID_PATH_MUTE_CTL))
+ mask &= ~0x80;
+ }
+ if (caps & AC_AMPCAP_NUM_STEPS) {
+ if (is_ctl_associated(codec, nid, dir, idx, NID_PATH_VOL_CTL) ||
+ is_ctl_associated(codec, nid, dir, idx, NID_PATH_BOOST_CTL))
+ mask &= ~0x7f;
+ }
+ return mask;
}
-/*
- * unmute (and set max vol) the input amplifier
+static void activate_amp(struct hda_codec *codec, hda_nid_t nid, int dir,
+ int idx, int idx_to_check, bool enable)
+{
+ unsigned int caps;
+ unsigned int mask, val;
+
+ if (!enable && is_active_nid(codec, nid, dir, idx_to_check))
+ return;
+
+ caps = query_amp_caps(codec, nid, dir);
+ val = get_amp_val_to_activate(codec, nid, dir, caps, enable);
+ mask = get_amp_mask_to_modify(codec, nid, dir, idx_to_check, caps);
+ if (!mask)
+ return;
+
+ val &= mask;
+ snd_hda_codec_amp_stereo(codec, nid, dir, idx, mask, val);
+}
+
+static void activate_amp_out(struct hda_codec *codec, struct nid_path *path,
+ int i, bool enable)
+{
+ hda_nid_t nid = path->path[i];
+ init_amp(codec, nid, HDA_OUTPUT, 0);
+ activate_amp(codec, nid, HDA_OUTPUT, 0, 0, enable);
+}
+
+static void activate_amp_in(struct hda_codec *codec, struct nid_path *path,
+ int i, bool enable, bool add_aamix)
+{
+ struct hda_gen_spec *spec = codec->spec;
+ const hda_nid_t *conn;
+ int n, nums, idx;
+ int type;
+ hda_nid_t nid = path->path[i];
+
+ nums = snd_hda_get_conn_list(codec, nid, &conn);
+ type = get_wcaps_type(get_wcaps(codec, nid));
+ if (type == AC_WID_PIN ||
+ (type == AC_WID_AUD_IN && codec->single_adc_amp)) {
+ nums = 1;
+ idx = 0;
+ } else
+ idx = path->idx[i];
+
+ for (n = 0; n < nums; n++)
+ init_amp(codec, nid, HDA_INPUT, n);
+
+ /* here is a little bit tricky in comparison with activate_amp_out();
+ * when aa-mixer is available, we need to enable the path as well
+ */
+ for (n = 0; n < nums; n++) {
+ if (n != idx && (!add_aamix || conn[n] != spec->mixer_merge_nid))
+ continue;
+ activate_amp(codec, nid, HDA_INPUT, n, idx, enable);
+ }
+}
+
+/* activate or deactivate the given path
+ * if @add_aamix is set, enable the input from aa-mix NID as well (if any)
*/
-static int unmute_input(struct hda_codec *codec, struct hda_gnode *node, unsigned int index)
+void snd_hda_activate_path(struct hda_codec *codec, struct nid_path *path,
+ bool enable, bool add_aamix)
{
- unsigned int val, ofs;
- snd_printdd("UNMUTE IN: NID=0x%x IDX=0x%x\n", node->nid, index);
- val = (node->amp_in_caps & AC_AMPCAP_NUM_STEPS) >> AC_AMPCAP_NUM_STEPS_SHIFT;
- ofs = (node->amp_in_caps & AC_AMPCAP_OFFSET) >> AC_AMPCAP_OFFSET_SHIFT;
- if (val >= ofs)
- val -= ofs;
- snd_hda_codec_amp_stereo(codec, node->nid, HDA_INPUT, index, 0xff, val);
- return 0;
+ struct hda_gen_spec *spec = codec->spec;
+ int i;
+
+ if (!enable)
+ path->active = false;
+
+ for (i = path->depth - 1; i >= 0; i--) {
+ hda_nid_t nid = path->path[i];
+ if (enable && spec->power_down_unused) {
+ /* make sure the widget is powered up */
+ if (!snd_hda_check_power_state(codec, nid, AC_PWRST_D0))
+ snd_hda_codec_write(codec, nid, 0,
+ AC_VERB_SET_POWER_STATE,
+ AC_PWRST_D0);
+ }
+ if (enable && path->multi[i])
+ snd_hda_codec_update_cache(codec, nid, 0,
+ AC_VERB_SET_CONNECT_SEL,
+ path->idx[i]);
+ if (has_amp_in(codec, path, i))
+ activate_amp_in(codec, path, i, enable, add_aamix);
+ if (has_amp_out(codec, path, i))
+ activate_amp_out(codec, path, i, enable);
+ }
+
+ if (enable)
+ path->active = true;
+}
+EXPORT_SYMBOL_GPL(snd_hda_activate_path);
+
+/* if the given path is inactive, put widgets into D3 (only if suitable) */
+static void path_power_down_sync(struct hda_codec *codec, struct nid_path *path)
+{
+ struct hda_gen_spec *spec = codec->spec;
+ bool changed = false;
+ int i;
+
+ if (!spec->power_down_unused || path->active)
+ return;
+
+ for (i = 0; i < path->depth; i++) {
+ hda_nid_t nid = path->path[i];
+ if (!snd_hda_check_power_state(codec, nid, AC_PWRST_D3) &&
+ !is_active_nid_for_any(codec, nid)) {
+ snd_hda_codec_write(codec, nid, 0,
+ AC_VERB_SET_POWER_STATE,
+ AC_PWRST_D3);
+ changed = true;
+ }
+ }
+
+ if (changed) {
+ msleep(10);
+ snd_hda_codec_read(codec, path->path[0], 0,
+ AC_VERB_GET_POWER_STATE, 0);
+ }
+}
+
+/* turn on/off EAPD on the given pin */
+static void set_pin_eapd(struct hda_codec *codec, hda_nid_t pin, bool enable)
+{
+ struct hda_gen_spec *spec = codec->spec;
+ if (spec->own_eapd_ctl ||
+ !(snd_hda_query_pin_caps(codec, pin) & AC_PINCAP_EAPD))
+ return;
+ if (spec->keep_eapd_on && !enable)
+ return;
+ if (codec->inv_eapd)
+ enable = !enable;
+ snd_hda_codec_update_cache(codec, pin, 0,
+ AC_VERB_SET_EAPD_BTLENABLE,
+ enable ? 0x02 : 0x00);
+}
+
+/* re-initialize the path specified by the given path index */
+static void resume_path_from_idx(struct hda_codec *codec, int path_idx)
+{
+ struct nid_path *path = snd_hda_get_path_from_idx(codec, path_idx);
+ if (path)
+ snd_hda_activate_path(codec, path, path->active, false);
}
+
/*
- * select the input connection of the given node.
+ * Helper functions for creating mixer ctl elements
*/
-static int select_input_connection(struct hda_codec *codec, struct hda_gnode *node,
- unsigned int index)
+
+static int hda_gen_mixer_mute_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol);
+static int hda_gen_bind_mute_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol);
+
+enum {
+ HDA_CTL_WIDGET_VOL,
+ HDA_CTL_WIDGET_MUTE,
+ HDA_CTL_BIND_MUTE,
+};
+static const struct snd_kcontrol_new control_templates[] = {
+ HDA_CODEC_VOLUME(NULL, 0, 0, 0),
+ /* only the put callback is replaced for handling the special mute */
+ {
+ .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+ .subdevice = HDA_SUBDEV_AMP_FLAG,
+ .info = snd_hda_mixer_amp_switch_info,
+ .get = snd_hda_mixer_amp_switch_get,
+ .put = hda_gen_mixer_mute_put, /* replaced */
+ .private_value = HDA_COMPOSE_AMP_VAL(0, 3, 0, 0),
+ },
+ {
+ .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+ .info = snd_hda_mixer_amp_switch_info,
+ .get = snd_hda_mixer_bind_switch_get,
+ .put = hda_gen_bind_mute_put, /* replaced */
+ .private_value = HDA_COMPOSE_AMP_VAL(0, 3, 0, 0),
+ },
+};
+
+/* add dynamic controls from template */
+static struct snd_kcontrol_new *
+add_control(struct hda_gen_spec *spec, int type, const char *name,
+ int cidx, unsigned long val)
{
- snd_printdd("CONNECT: NID=0x%x IDX=0x%x\n", node->nid, index);
- return snd_hda_codec_write_cache(codec, node->nid, 0,
- AC_VERB_SET_CONNECT_SEL, index);
+ struct snd_kcontrol_new *knew;
+
+ knew = snd_hda_gen_add_kctl(spec, name, &control_templates[type]);
+ if (!knew)
+ return NULL;
+ knew->index = cidx;
+ if (get_amp_nid_(val))
+ knew->subdevice = HDA_SUBDEV_AMP_FLAG;
+ knew->private_value = val;
+ return knew;
}
-/*
- * clear checked flag of each node in the node list
+static int add_control_with_pfx(struct hda_gen_spec *spec, int type,
+ const char *pfx, const char *dir,
+ const char *sfx, int cidx, unsigned long val)
+{
+ char name[SNDRV_CTL_ELEM_ID_NAME_MAXLEN];
+ snprintf(name, sizeof(name), "%s %s %s", pfx, dir, sfx);
+ if (!add_control(spec, type, name, cidx, val))
+ return -ENOMEM;
+ return 0;
+}
+
+#define add_pb_vol_ctrl(spec, type, pfx, val) \
+ add_control_with_pfx(spec, type, pfx, "Playback", "Volume", 0, val)
+#define add_pb_sw_ctrl(spec, type, pfx, val) \
+ add_control_with_pfx(spec, type, pfx, "Playback", "Switch", 0, val)
+#define __add_pb_vol_ctrl(spec, type, pfx, cidx, val) \
+ add_control_with_pfx(spec, type, pfx, "Playback", "Volume", cidx, val)
+#define __add_pb_sw_ctrl(spec, type, pfx, cidx, val) \
+ add_control_with_pfx(spec, type, pfx, "Playback", "Switch", cidx, val)
+
+static int add_vol_ctl(struct hda_codec *codec, const char *pfx, int cidx,
+ unsigned int chs, struct nid_path *path)
+{
+ unsigned int val;
+ if (!path)
+ return 0;
+ val = path->ctls[NID_PATH_VOL_CTL];
+ if (!val)
+ return 0;
+ val = amp_val_replace_channels(val, chs);
+ return __add_pb_vol_ctrl(codec->spec, HDA_CTL_WIDGET_VOL, pfx, cidx, val);
+}
+
+/* return the channel bits suitable for the given path->ctls[] */
+static int get_default_ch_nums(struct hda_codec *codec, struct nid_path *path,
+ int type)
+{
+ int chs = 1; /* mono (left only) */
+ if (path) {
+ hda_nid_t nid = get_amp_nid_(path->ctls[type]);
+ if (nid && (get_wcaps(codec, nid) & AC_WCAP_STEREO))
+ chs = 3; /* stereo */
+ }
+ return chs;
+}
+
+static int add_stereo_vol(struct hda_codec *codec, const char *pfx, int cidx,
+ struct nid_path *path)
+{
+ int chs = get_default_ch_nums(codec, path, NID_PATH_VOL_CTL);
+ return add_vol_ctl(codec, pfx, cidx, chs, path);
+}
+
+/* create a mute-switch for the given mixer widget;
+ * if it has multiple sources (e.g. DAC and loopback), create a bind-mute
*/
-static void clear_check_flags(struct hda_gspec *spec)
+static int add_sw_ctl(struct hda_codec *codec, const char *pfx, int cidx,
+ unsigned int chs, struct nid_path *path)
+{
+ unsigned int val;
+ int type = HDA_CTL_WIDGET_MUTE;
+
+ if (!path)
+ return 0;
+ val = path->ctls[NID_PATH_MUTE_CTL];
+ if (!val)
+ return 0;
+ val = amp_val_replace_channels(val, chs);
+ if (get_amp_direction_(val) == HDA_INPUT) {
+ hda_nid_t nid = get_amp_nid_(val);
+ int nums = snd_hda_get_num_conns(codec, nid);
+ if (nums > 1) {
+ type = HDA_CTL_BIND_MUTE;
+ val |= nums << 19;
+ }
+ }
+ return __add_pb_sw_ctrl(codec->spec, type, pfx, cidx, val);
+}
+
+static int add_stereo_sw(struct hda_codec *codec, const char *pfx,
+ int cidx, struct nid_path *path)
+{
+ int chs = get_default_ch_nums(codec, path, NID_PATH_MUTE_CTL);
+ return add_sw_ctl(codec, pfx, cidx, chs, path);
+}
+
+/* playback mute control with the software mute bit check */
+static void sync_auto_mute_bits(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
{
- struct hda_gnode *node;
+ struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
+ struct hda_gen_spec *spec = codec->spec;
- list_for_each_entry(node, &spec->nid_list, list) {
- node->checked = 0;
+ if (spec->auto_mute_via_amp) {
+ hda_nid_t nid = get_amp_nid(kcontrol);
+ bool enabled = !((spec->mute_bits >> nid) & 1);
+ ucontrol->value.integer.value[0] &= enabled;
+ ucontrol->value.integer.value[1] &= enabled;
}
}
+static int hda_gen_mixer_mute_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ sync_auto_mute_bits(kcontrol, ucontrol);
+ return snd_hda_mixer_amp_switch_put(kcontrol, ucontrol);
+}
+
+static int hda_gen_bind_mute_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ sync_auto_mute_bits(kcontrol, ucontrol);
+ return snd_hda_mixer_bind_switch_put(kcontrol, ucontrol);
+}
+
+/* any ctl assigned to the path with the given index? */
+static bool path_has_mixer(struct hda_codec *codec, int path_idx, int ctl_type)
+{
+ struct nid_path *path = snd_hda_get_path_from_idx(codec, path_idx);
+ return path && path->ctls[ctl_type];
+}
+
+static const char * const channel_name[4] = {
+ "Front", "Surround", "CLFE", "Side"
+};
+
+/* give some appropriate ctl name prefix for the given line out channel */
+static const char *get_line_out_pfx(struct hda_codec *codec, int ch,
+ int *index, int ctl_type)
+{
+ struct hda_gen_spec *spec = codec->spec;
+ struct auto_pin_cfg *cfg = &spec->autocfg;
+
+ *index = 0;
+ if (cfg->line_outs == 1 && !spec->multi_ios &&
+ !cfg->hp_outs && !cfg->speaker_outs)
+ return spec->vmaster_mute.hook ? "PCM" : "Master";
+
+ /* if there is really a single DAC used in the whole output paths,
+ * use it master (or "PCM" if a vmaster hook is present)
+ */
+ if (spec->multiout.num_dacs == 1 && !spec->mixer_nid &&
+ !spec->multiout.hp_out_nid[0] && !spec->multiout.extra_out_nid[0])
+ return spec->vmaster_mute.hook ? "PCM" : "Master";
+
+ /* multi-io channels */
+ if (ch >= cfg->line_outs)
+ return channel_name[ch];
+
+ switch (cfg->line_out_type) {
+ case AUTO_PIN_SPEAKER_OUT:
+ /* if the primary channel vol/mute is shared with HP volume,
+ * don't name it as Speaker
+ */
+ if (!ch && cfg->hp_outs &&
+ !path_has_mixer(codec, spec->hp_paths[0], ctl_type))
+ break;
+ if (cfg->line_outs == 1)
+ return "Speaker";
+ if (cfg->line_outs == 2)
+ return ch ? "Bass Speaker" : "Speaker";
+ break;
+ case AUTO_PIN_HP_OUT:
+ /* if the primary channel vol/mute is shared with spk volume,
+ * don't name it as Headphone
+ */
+ if (!ch && cfg->speaker_outs &&
+ !path_has_mixer(codec, spec->speaker_paths[0], ctl_type))
+ break;
+ /* for multi-io case, only the primary out */
+ if (ch && spec->multi_ios)
+ break;
+ *index = ch;
+ return "Headphone";
+ }
+
+ /* for a single channel output, we don't have to name the channel */
+ if (cfg->line_outs == 1 && !spec->multi_ios)
+ return "PCM";
+
+ if (ch >= ARRAY_SIZE(channel_name)) {
+ snd_BUG();
+ return "PCM";
+ }
+
+ return channel_name[ch];
+}
+
/*
- * parse the output path recursively until reach to an audio output widget
+ * Parse output paths
+ */
+
+/* badness definition */
+enum {
+ /* No primary DAC is found for the main output */
+ BAD_NO_PRIMARY_DAC = 0x10000,
+ /* No DAC is found for the extra output */
+ BAD_NO_DAC = 0x4000,
+ /* No possible multi-ios */
+ BAD_MULTI_IO = 0x120,
+ /* No individual DAC for extra output */
+ BAD_NO_EXTRA_DAC = 0x102,
+ /* No individual DAC for extra surrounds */
+ BAD_NO_EXTRA_SURR_DAC = 0x101,
+ /* Primary DAC shared with main surrounds */
+ BAD_SHARED_SURROUND = 0x100,
+ /* No independent HP possible */
+ BAD_NO_INDEP_HP = 0x10,
+ /* Primary DAC shared with main CLFE */
+ BAD_SHARED_CLFE = 0x10,
+ /* Primary DAC shared with extra surrounds */
+ BAD_SHARED_EXTRA_SURROUND = 0x10,
+ /* Volume widget is shared */
+ BAD_SHARED_VOL = 0x10,
+};
+
+/* look for widgets in the given path which are appropriate for
+ * volume and mute controls, and assign the values to ctls[].
*
- * returns 0 if not found, 1 if found, or a negative error code.
+ * When no appropriate widget is found in the path, the badness value
+ * is incremented depending on the situation. The function returns the
+ * total badness for both volume and mute controls.
*/
-static int parse_output_path(struct hda_codec *codec, struct hda_gspec *spec,
- struct hda_gnode *node, int dac_idx)
+static int assign_out_path_ctls(struct hda_codec *codec, struct nid_path *path)
{
- int i, err;
- struct hda_gnode *child;
+ hda_nid_t nid;
+ unsigned int val;
+ int badness = 0;
+
+ if (!path)
+ return BAD_SHARED_VOL * 2;
+
+ if (path->ctls[NID_PATH_VOL_CTL] ||
+ path->ctls[NID_PATH_MUTE_CTL])
+ return 0; /* already evaluated */
- if (node->checked)
+ nid = look_for_out_vol_nid(codec, path);
+ if (nid) {
+ val = HDA_COMPOSE_AMP_VAL(nid, 3, 0, HDA_OUTPUT);
+ if (is_ctl_used(codec, val, NID_PATH_VOL_CTL))
+ badness += BAD_SHARED_VOL;
+ else
+ path->ctls[NID_PATH_VOL_CTL] = val;
+ } else
+ badness += BAD_SHARED_VOL;
+ nid = look_for_out_mute_nid(codec, path);
+ if (nid) {
+ unsigned int wid_type = get_wcaps_type(get_wcaps(codec, nid));
+ if (wid_type == AC_WID_PIN || wid_type == AC_WID_AUD_OUT ||
+ nid_has_mute(codec, nid, HDA_OUTPUT))
+ val = HDA_COMPOSE_AMP_VAL(nid, 3, 0, HDA_OUTPUT);
+ else
+ val = HDA_COMPOSE_AMP_VAL(nid, 3, 0, HDA_INPUT);
+ if (is_ctl_used(codec, val, NID_PATH_MUTE_CTL))
+ badness += BAD_SHARED_VOL;
+ else
+ path->ctls[NID_PATH_MUTE_CTL] = val;
+ } else
+ badness += BAD_SHARED_VOL;
+ return badness;
+}
+
+const struct badness_table hda_main_out_badness = {
+ .no_primary_dac = BAD_NO_PRIMARY_DAC,
+ .no_dac = BAD_NO_DAC,
+ .shared_primary = BAD_NO_PRIMARY_DAC,
+ .shared_surr = BAD_SHARED_SURROUND,
+ .shared_clfe = BAD_SHARED_CLFE,
+ .shared_surr_main = BAD_SHARED_SURROUND,
+};
+EXPORT_SYMBOL_GPL(hda_main_out_badness);
+
+const struct badness_table hda_extra_out_badness = {
+ .no_primary_dac = BAD_NO_DAC,
+ .no_dac = BAD_NO_DAC,
+ .shared_primary = BAD_NO_EXTRA_DAC,
+ .shared_surr = BAD_SHARED_EXTRA_SURROUND,
+ .shared_clfe = BAD_SHARED_EXTRA_SURROUND,
+ .shared_surr_main = BAD_NO_EXTRA_SURR_DAC,
+};
+EXPORT_SYMBOL_GPL(hda_extra_out_badness);
+
+/* get the DAC of the primary output corresponding to the given array index */
+static hda_nid_t get_primary_out(struct hda_codec *codec, int idx)
+{
+ struct hda_gen_spec *spec = codec->spec;
+ struct auto_pin_cfg *cfg = &spec->autocfg;
+
+ if (cfg->line_outs > idx)
+ return spec->private_dac_nids[idx];
+ idx -= cfg->line_outs;
+ if (spec->multi_ios > idx)
+ return spec->multi_io[idx].dac;
+ return 0;
+}
+
+/* return the DAC if it's reachable, otherwise zero */
+static inline hda_nid_t try_dac(struct hda_codec *codec,
+ hda_nid_t dac, hda_nid_t pin)
+{
+ return is_reachable_path(codec, dac, pin) ? dac : 0;
+}
+
+/* try to assign DACs to pins and return the resultant badness */
+static int try_assign_dacs(struct hda_codec *codec, int num_outs,
+ const hda_nid_t *pins, hda_nid_t *dacs,
+ int *path_idx,
+ const struct badness_table *bad)
+{
+ struct hda_gen_spec *spec = codec->spec;
+ int i, j;
+ int badness = 0;
+ hda_nid_t dac;
+
+ if (!num_outs)
return 0;
- node->checked = 1;
- if (node->type == AC_WID_AUD_OUT) {
- if (node->wid_caps & AC_WCAP_DIGITAL) {
- snd_printdd("Skip Digital OUT node %x\n", node->nid);
- return 0;
+ for (i = 0; i < num_outs; i++) {
+ struct nid_path *path;
+ hda_nid_t pin = pins[i];
+
+ path = snd_hda_get_path_from_idx(codec, path_idx[i]);
+ if (path) {
+ badness += assign_out_path_ctls(codec, path);
+ continue;
}
- snd_printdd("AUD_OUT found %x\n", node->nid);
- if (spec->dac_node[dac_idx]) {
- /* already DAC node is assigned, just unmute & connect */
- return node == spec->dac_node[dac_idx];
+
+ dacs[i] = get_preferred_dac(codec, pin);
+ if (dacs[i]) {
+ if (is_dac_already_used(codec, dacs[i]))
+ badness += bad->shared_primary;
+ }
+
+ if (!dacs[i])
+ dacs[i] = look_for_dac(codec, pin, false);
+ if (!dacs[i] && !i) {
+ /* try to steal the DAC of surrounds for the front */
+ for (j = 1; j < num_outs; j++) {
+ if (is_reachable_path(codec, dacs[j], pin)) {
+ dacs[0] = dacs[j];
+ dacs[j] = 0;
+ invalidate_nid_path(codec, path_idx[j]);
+ path_idx[j] = 0;
+ break;
+ }
+ }
}
- spec->dac_node[dac_idx] = node;
- if ((node->wid_caps & AC_WCAP_OUT_AMP) &&
- spec->pcm_vol_nodes < MAX_PCM_VOLS) {
- spec->pcm_vol[spec->pcm_vol_nodes].node = node;
- spec->pcm_vol[spec->pcm_vol_nodes].index = 0;
- spec->pcm_vol_nodes++;
+ dac = dacs[i];
+ if (!dac) {
+ if (num_outs > 2)
+ dac = try_dac(codec, get_primary_out(codec, i), pin);
+ if (!dac)
+ dac = try_dac(codec, dacs[0], pin);
+ if (!dac)
+ dac = try_dac(codec, get_primary_out(codec, i), pin);
+ if (dac) {
+ if (!i)
+ badness += bad->shared_primary;
+ else if (i == 1)
+ badness += bad->shared_surr;
+ else
+ badness += bad->shared_clfe;
+ } else if (is_reachable_path(codec, spec->private_dac_nids[0], pin)) {
+ dac = spec->private_dac_nids[0];
+ badness += bad->shared_surr_main;
+ } else if (!i)
+ badness += bad->no_primary_dac;
+ else
+ badness += bad->no_dac;
+ }
+ if (!dac)
+ continue;
+ path = snd_hda_add_new_path(codec, dac, pin, -spec->mixer_nid);
+ if (!path && !i && spec->mixer_nid) {
+ /* try with aamix */
+ path = snd_hda_add_new_path(codec, dac, pin, 0);
+ }
+ if (!path) {
+ dac = dacs[i] = 0;
+ badness += bad->no_dac;
+ } else {
+ /* print_nid_path(codec, "output", path); */
+ path->active = true;
+ path_idx[i] = snd_hda_get_path_idx(codec, path);
+ badness += assign_out_path_ctls(codec, path);
}
- return 1; /* found */
}
- for (i = 0; i < node->nconns; i++) {
- child = hda_get_node(spec, node->conn_list[i]);
- if (! child)
+ return badness;
+}
+
+/* return NID if the given pin has only a single connection to a certain DAC */
+static hda_nid_t get_dac_if_single(struct hda_codec *codec, hda_nid_t pin)
+{
+ struct hda_gen_spec *spec = codec->spec;
+ int i;
+ hda_nid_t nid_found = 0;
+
+ for (i = 0; i < spec->num_all_dacs; i++) {
+ hda_nid_t nid = spec->all_dacs[i];
+ if (!nid || is_dac_already_used(codec, nid))
continue;
- err = parse_output_path(codec, spec, child, dac_idx);
- if (err < 0)
- return err;
- else if (err > 0) {
- /* found one,
- * select the path, unmute both input and output
- */
- if (node->nconns > 1)
- select_input_connection(codec, node, i);
- unmute_input(codec, node, i);
- unmute_output(codec, node);
- if (spec->dac_node[dac_idx] &&
- spec->pcm_vol_nodes < MAX_PCM_VOLS &&
- !(spec->dac_node[dac_idx]->wid_caps &
- AC_WCAP_OUT_AMP)) {
- if ((node->wid_caps & AC_WCAP_IN_AMP) ||
- (node->wid_caps & AC_WCAP_OUT_AMP)) {
- int n = spec->pcm_vol_nodes;
- spec->pcm_vol[n].node = node;
- spec->pcm_vol[n].index = i;
- spec->pcm_vol_nodes++;
- }
- }
- return 1;
+ if (is_reachable_path(codec, nid, pin)) {
+ if (nid_found)
+ return 0;
+ nid_found = nid;
}
}
- return 0;
+ return nid_found;
+}
+
+/* check whether the given pin can be a multi-io pin */
+static bool can_be_multiio_pin(struct hda_codec *codec,
+ unsigned int location, hda_nid_t nid)
+{
+ unsigned int defcfg, caps;
+
+ defcfg = snd_hda_codec_get_pincfg(codec, nid);
+ if (get_defcfg_connect(defcfg) != AC_JACK_PORT_COMPLEX)
+ return false;
+ if (location && get_defcfg_location(defcfg) != location)
+ return false;
+ caps = snd_hda_query_pin_caps(codec, nid);
+ if (!(caps & AC_PINCAP_OUT))
+ return false;
+ return true;
+}
+
+/* count the number of input pins that are capable to be multi-io */
+static int count_multiio_pins(struct hda_codec *codec, hda_nid_t reference_pin)
+{
+ struct hda_gen_spec *spec = codec->spec;
+ struct auto_pin_cfg *cfg = &spec->autocfg;
+ unsigned int defcfg = snd_hda_codec_get_pincfg(codec, reference_pin);
+ unsigned int location = get_defcfg_location(defcfg);
+ int type, i;
+ int num_pins = 0;
+
+ for (type = AUTO_PIN_LINE_IN; type >= AUTO_PIN_MIC; type--) {
+ for (i = 0; i < cfg->num_inputs; i++) {
+ if (cfg->inputs[i].type != type)
+ continue;
+ if (can_be_multiio_pin(codec, location,
+ cfg->inputs[i].pin))
+ num_pins++;
+ }
+ }
+ return num_pins;
}
/*
- * Look for the output PIN widget with the given jack type
- * and parse the output path to that PIN.
+ * multi-io helper
*
- * Returns the PIN node when the path to DAC is established.
+ * When hardwired is set, try to fill ony hardwired pins, and returns
+ * zero if any pins are filled, non-zero if nothing found.
+ * When hardwired is off, try to fill possible input pins, and returns
+ * the badness value.
*/
-static struct hda_gnode *parse_output_jack(struct hda_codec *codec,
- struct hda_gspec *spec,
- int jack_type)
+static int fill_multi_ios(struct hda_codec *codec,
+ hda_nid_t reference_pin,
+ bool hardwired)
{
- struct hda_gnode *node;
- int err;
+ struct hda_gen_spec *spec = codec->spec;
+ struct auto_pin_cfg *cfg = &spec->autocfg;
+ int type, i, j, num_pins, old_pins;
+ unsigned int defcfg = snd_hda_codec_get_pincfg(codec, reference_pin);
+ unsigned int location = get_defcfg_location(defcfg);
+ int badness = 0;
+ struct nid_path *path;
- list_for_each_entry(node, &spec->nid_list, list) {
- if (node->type != AC_WID_PIN)
- continue;
- /* output capable? */
- if (! (node->pin_caps & AC_PINCAP_OUT))
- continue;
- if (defcfg_port_conn(node) == AC_JACK_PORT_NONE)
- continue; /* unconnected */
- if (jack_type >= 0) {
- if (jack_type != defcfg_type(node))
+ old_pins = spec->multi_ios;
+ if (old_pins >= 2)
+ goto end_fill;
+
+ num_pins = count_multiio_pins(codec, reference_pin);
+ if (num_pins < 2)
+ goto end_fill;
+
+ for (type = AUTO_PIN_LINE_IN; type >= AUTO_PIN_MIC; type--) {
+ for (i = 0; i < cfg->num_inputs; i++) {
+ hda_nid_t nid = cfg->inputs[i].pin;
+ hda_nid_t dac = 0;
+
+ if (cfg->inputs[i].type != type)
continue;
- if (node->wid_caps & AC_WCAP_DIGITAL)
- continue; /* skip SPDIF */
- } else {
- /* output as default? */
- if (! (node->pin_ctl & AC_PINCTL_OUT_EN))
+ if (!can_be_multiio_pin(codec, location, nid))
continue;
+ for (j = 0; j < spec->multi_ios; j++) {
+ if (nid == spec->multi_io[j].pin)
+ break;
+ }
+ if (j < spec->multi_ios)
+ continue;
+
+ if (hardwired)
+ dac = get_dac_if_single(codec, nid);
+ else if (!dac)
+ dac = look_for_dac(codec, nid, false);
+ if (!dac) {
+ badness++;
+ continue;
+ }
+ path = snd_hda_add_new_path(codec, dac, nid,
+ -spec->mixer_nid);
+ if (!path) {
+ badness++;
+ continue;
+ }
+ /* print_nid_path(codec, "multiio", path); */
+ spec->multi_io[spec->multi_ios].pin = nid;
+ spec->multi_io[spec->multi_ios].dac = dac;
+ spec->out_paths[cfg->line_outs + spec->multi_ios] =
+ snd_hda_get_path_idx(codec, path);
+ spec->multi_ios++;
+ if (spec->multi_ios >= 2)
+ break;
+ }
+ }
+ end_fill:
+ if (badness)
+ badness = BAD_MULTI_IO;
+ if (old_pins == spec->multi_ios) {
+ if (hardwired)
+ return 1; /* nothing found */
+ else
+ return badness; /* no badness if nothing found */
+ }
+ if (!hardwired && spec->multi_ios < 2) {
+ /* cancel newly assigned paths */
+ spec->paths.used -= spec->multi_ios - old_pins;
+ spec->multi_ios = old_pins;
+ return badness;
+ }
+
+ /* assign volume and mute controls */
+ for (i = old_pins; i < spec->multi_ios; i++) {
+ path = snd_hda_get_path_from_idx(codec, spec->out_paths[cfg->line_outs + i]);
+ badness += assign_out_path_ctls(codec, path);
+ }
+
+ return badness;
+}
+
+/* map DACs for all pins in the list if they are single connections */
+static bool map_singles(struct hda_codec *codec, int outs,
+ const hda_nid_t *pins, hda_nid_t *dacs, int *path_idx)
+{
+ struct hda_gen_spec *spec = codec->spec;
+ int i;
+ bool found = false;
+ for (i = 0; i < outs; i++) {
+ struct nid_path *path;
+ hda_nid_t dac;
+ if (dacs[i])
+ continue;
+ dac = get_dac_if_single(codec, pins[i]);
+ if (!dac)
+ continue;
+ path = snd_hda_add_new_path(codec, dac, pins[i],
+ -spec->mixer_nid);
+ if (!path && !i && spec->mixer_nid)
+ path = snd_hda_add_new_path(codec, dac, pins[i], 0);
+ if (path) {
+ dacs[i] = dac;
+ found = true;
+ /* print_nid_path(codec, "output", path); */
+ path->active = true;
+ path_idx[i] = snd_hda_get_path_idx(codec, path);
}
- clear_check_flags(spec);
- err = parse_output_path(codec, spec, node, 0);
+ }
+ return found;
+}
+
+/* create a new path including aamix if available, and return its index */
+static int check_aamix_out_path(struct hda_codec *codec, int path_idx)
+{
+ struct hda_gen_spec *spec = codec->spec;
+ struct nid_path *path;
+ hda_nid_t path_dac, dac, pin;
+
+ path = snd_hda_get_path_from_idx(codec, path_idx);
+ if (!path || !path->depth ||
+ is_nid_contained(path, spec->mixer_nid))
+ return 0;
+ path_dac = path->path[0];
+ dac = spec->private_dac_nids[0];
+ pin = path->path[path->depth - 1];
+ path = snd_hda_add_new_path(codec, dac, pin, spec->mixer_nid);
+ if (!path) {
+ if (dac != path_dac)
+ dac = path_dac;
+ else if (spec->multiout.hp_out_nid[0])
+ dac = spec->multiout.hp_out_nid[0];
+ else if (spec->multiout.extra_out_nid[0])
+ dac = spec->multiout.extra_out_nid[0];
+ else
+ dac = 0;
+ if (dac)
+ path = snd_hda_add_new_path(codec, dac, pin,
+ spec->mixer_nid);
+ }
+ if (!path)
+ return 0;
+ /* print_nid_path(codec, "output-aamix", path); */
+ path->active = false; /* unused as default */
+ return snd_hda_get_path_idx(codec, path);
+}
+
+/* check whether the independent HP is available with the current config */
+static bool indep_hp_possible(struct hda_codec *codec)
+{
+ struct hda_gen_spec *spec = codec->spec;
+ struct auto_pin_cfg *cfg = &spec->autocfg;
+ struct nid_path *path;
+ int i, idx;
+
+ if (cfg->line_out_type == AUTO_PIN_HP_OUT)
+ idx = spec->out_paths[0];
+ else
+ idx = spec->hp_paths[0];
+ path = snd_hda_get_path_from_idx(codec, idx);
+ if (!path)
+ return false;
+
+ /* assume no path conflicts unless aamix is involved */
+ if (!spec->mixer_nid || !is_nid_contained(path, spec->mixer_nid))
+ return true;
+
+ /* check whether output paths contain aamix */
+ for (i = 0; i < cfg->line_outs; i++) {
+ if (spec->out_paths[i] == idx)
+ break;
+ path = snd_hda_get_path_from_idx(codec, spec->out_paths[i]);
+ if (path && is_nid_contained(path, spec->mixer_nid))
+ return false;
+ }
+ for (i = 0; i < cfg->speaker_outs; i++) {
+ path = snd_hda_get_path_from_idx(codec, spec->speaker_paths[i]);
+ if (path && is_nid_contained(path, spec->mixer_nid))
+ return false;
+ }
+
+ return true;
+}
+
+/* fill the empty entries in the dac array for speaker/hp with the
+ * shared dac pointed by the paths
+ */
+static void refill_shared_dacs(struct hda_codec *codec, int num_outs,
+ hda_nid_t *dacs, int *path_idx)
+{
+ struct nid_path *path;
+ int i;
+
+ for (i = 0; i < num_outs; i++) {
+ if (dacs[i])
+ continue;
+ path = snd_hda_get_path_from_idx(codec, path_idx[i]);
+ if (!path)
+ continue;
+ dacs[i] = path->path[0];
+ }
+}
+
+/* fill in the dac_nids table from the parsed pin configuration */
+static int fill_and_eval_dacs(struct hda_codec *codec,
+ bool fill_hardwired,
+ bool fill_mio_first)
+{
+ struct hda_gen_spec *spec = codec->spec;
+ struct auto_pin_cfg *cfg = &spec->autocfg;
+ int i, err, badness;
+
+ /* set num_dacs once to full for look_for_dac() */
+ spec->multiout.num_dacs = cfg->line_outs;
+ spec->multiout.dac_nids = spec->private_dac_nids;
+ memset(spec->private_dac_nids, 0, sizeof(spec->private_dac_nids));
+ memset(spec->multiout.hp_out_nid, 0, sizeof(spec->multiout.hp_out_nid));
+ memset(spec->multiout.extra_out_nid, 0, sizeof(spec->multiout.extra_out_nid));
+ spec->multi_ios = 0;
+ snd_array_free(&spec->paths);
+
+ /* clear path indices */
+ memset(spec->out_paths, 0, sizeof(spec->out_paths));
+ memset(spec->hp_paths, 0, sizeof(spec->hp_paths));
+ memset(spec->speaker_paths, 0, sizeof(spec->speaker_paths));
+ memset(spec->aamix_out_paths, 0, sizeof(spec->aamix_out_paths));
+ memset(spec->digout_paths, 0, sizeof(spec->digout_paths));
+ memset(spec->input_paths, 0, sizeof(spec->input_paths));
+ memset(spec->loopback_paths, 0, sizeof(spec->loopback_paths));
+ memset(&spec->digin_path, 0, sizeof(spec->digin_path));
+
+ badness = 0;
+
+ /* fill hard-wired DACs first */
+ if (fill_hardwired) {
+ bool mapped;
+ do {
+ mapped = map_singles(codec, cfg->line_outs,
+ cfg->line_out_pins,
+ spec->private_dac_nids,
+ spec->out_paths);
+ mapped |= map_singles(codec, cfg->hp_outs,
+ cfg->hp_pins,
+ spec->multiout.hp_out_nid,
+ spec->hp_paths);
+ mapped |= map_singles(codec, cfg->speaker_outs,
+ cfg->speaker_pins,
+ spec->multiout.extra_out_nid,
+ spec->speaker_paths);
+ if (!spec->no_multi_io &&
+ fill_mio_first && cfg->line_outs == 1 &&
+ cfg->line_out_type != AUTO_PIN_SPEAKER_OUT) {
+ err = fill_multi_ios(codec, cfg->line_out_pins[0], true);
+ if (!err)
+ mapped = true;
+ }
+ } while (mapped);
+ }
+
+ badness += try_assign_dacs(codec, cfg->line_outs, cfg->line_out_pins,
+ spec->private_dac_nids, spec->out_paths,
+ spec->main_out_badness);
+
+ if (!spec->no_multi_io && fill_mio_first &&
+ cfg->line_outs == 1 && cfg->line_out_type != AUTO_PIN_SPEAKER_OUT) {
+ /* try to fill multi-io first */
+ err = fill_multi_ios(codec, cfg->line_out_pins[0], false);
if (err < 0)
- return NULL;
- if (! err && spec->out_pin_node[0]) {
- err = parse_output_path(codec, spec, node, 1);
+ return err;
+ /* we don't count badness at this stage yet */
+ }
+
+ if (cfg->line_out_type != AUTO_PIN_HP_OUT) {
+ err = try_assign_dacs(codec, cfg->hp_outs, cfg->hp_pins,
+ spec->multiout.hp_out_nid,
+ spec->hp_paths,
+ spec->extra_out_badness);
+ if (err < 0)
+ return err;
+ badness += err;
+ }
+ if (cfg->line_out_type != AUTO_PIN_SPEAKER_OUT) {
+ err = try_assign_dacs(codec, cfg->speaker_outs,
+ cfg->speaker_pins,
+ spec->multiout.extra_out_nid,
+ spec->speaker_paths,
+ spec->extra_out_badness);
+ if (err < 0)
+ return err;
+ badness += err;
+ }
+ if (!spec->no_multi_io &&
+ cfg->line_outs == 1 && cfg->line_out_type != AUTO_PIN_SPEAKER_OUT) {
+ err = fill_multi_ios(codec, cfg->line_out_pins[0], false);
+ if (err < 0)
+ return err;
+ badness += err;
+ }
+
+ if (spec->mixer_nid) {
+ spec->aamix_out_paths[0] =
+ check_aamix_out_path(codec, spec->out_paths[0]);
+ if (cfg->line_out_type != AUTO_PIN_HP_OUT)
+ spec->aamix_out_paths[1] =
+ check_aamix_out_path(codec, spec->hp_paths[0]);
+ if (cfg->line_out_type != AUTO_PIN_SPEAKER_OUT)
+ spec->aamix_out_paths[2] =
+ check_aamix_out_path(codec, spec->speaker_paths[0]);
+ }
+
+ if (!spec->no_multi_io &&
+ cfg->hp_outs && cfg->line_out_type == AUTO_PIN_SPEAKER_OUT)
+ if (count_multiio_pins(codec, cfg->hp_pins[0]) >= 2)
+ spec->multi_ios = 1; /* give badness */
+
+ /* re-count num_dacs and squash invalid entries */
+ spec->multiout.num_dacs = 0;
+ for (i = 0; i < cfg->line_outs; i++) {
+ if (spec->private_dac_nids[i])
+ spec->multiout.num_dacs++;
+ else {
+ memmove(spec->private_dac_nids + i,
+ spec->private_dac_nids + i + 1,
+ sizeof(hda_nid_t) * (cfg->line_outs - i - 1));
+ spec->private_dac_nids[cfg->line_outs - 1] = 0;
+ }
+ }
+
+ spec->ext_channel_count = spec->min_channel_count =
+ spec->multiout.num_dacs * 2;
+
+ if (spec->multi_ios == 2) {
+ for (i = 0; i < 2; i++)
+ spec->private_dac_nids[spec->multiout.num_dacs++] =
+ spec->multi_io[i].dac;
+ } else if (spec->multi_ios) {
+ spec->multi_ios = 0;
+ badness += BAD_MULTI_IO;
+ }
+
+ if (spec->indep_hp && !indep_hp_possible(codec))
+ badness += BAD_NO_INDEP_HP;
+
+ /* re-fill the shared DAC for speaker / headphone */
+ if (cfg->line_out_type != AUTO_PIN_HP_OUT)
+ refill_shared_dacs(codec, cfg->hp_outs,
+ spec->multiout.hp_out_nid,
+ spec->hp_paths);
+ if (cfg->line_out_type != AUTO_PIN_SPEAKER_OUT)
+ refill_shared_dacs(codec, cfg->speaker_outs,
+ spec->multiout.extra_out_nid,
+ spec->speaker_paths);
+
+ return badness;
+}
+
+#define DEBUG_BADNESS
+
+#ifdef DEBUG_BADNESS
+#define debug_badness(fmt, args...) codec_dbg(codec, fmt, ##args)
+#else
+#define debug_badness(...)
+#endif
+
+#ifdef DEBUG_BADNESS
+static inline void print_nid_path_idx(struct hda_codec *codec,
+ const char *pfx, int idx)
+{
+ struct nid_path *path;
+
+ path = snd_hda_get_path_from_idx(codec, idx);
+ if (path)
+ print_nid_path(codec, pfx, path);
+}
+
+static void debug_show_configs(struct hda_codec *codec,
+ struct auto_pin_cfg *cfg)
+{
+ struct hda_gen_spec *spec = codec->spec;
+ static const char * const lo_type[3] = { "LO", "SP", "HP" };
+ int i;
+
+ debug_badness("multi_outs = %x/%x/%x/%x : %x/%x/%x/%x (type %s)\n",
+ cfg->line_out_pins[0], cfg->line_out_pins[1],
+ cfg->line_out_pins[2], cfg->line_out_pins[3],
+ spec->multiout.dac_nids[0],
+ spec->multiout.dac_nids[1],
+ spec->multiout.dac_nids[2],
+ spec->multiout.dac_nids[3],
+ lo_type[cfg->line_out_type]);
+ for (i = 0; i < cfg->line_outs; i++)
+ print_nid_path_idx(codec, " out", spec->out_paths[i]);
+ if (spec->multi_ios > 0)
+ debug_badness("multi_ios(%d) = %x/%x : %x/%x\n",
+ spec->multi_ios,
+ spec->multi_io[0].pin, spec->multi_io[1].pin,
+ spec->multi_io[0].dac, spec->multi_io[1].dac);
+ for (i = 0; i < spec->multi_ios; i++)
+ print_nid_path_idx(codec, " mio",
+ spec->out_paths[cfg->line_outs + i]);
+ if (cfg->hp_outs)
+ debug_badness("hp_outs = %x/%x/%x/%x : %x/%x/%x/%x\n",
+ cfg->hp_pins[0], cfg->hp_pins[1],
+ cfg->hp_pins[2], cfg->hp_pins[3],
+ spec->multiout.hp_out_nid[0],
+ spec->multiout.hp_out_nid[1],
+ spec->multiout.hp_out_nid[2],
+ spec->multiout.hp_out_nid[3]);
+ for (i = 0; i < cfg->hp_outs; i++)
+ print_nid_path_idx(codec, " hp ", spec->hp_paths[i]);
+ if (cfg->speaker_outs)
+ debug_badness("spk_outs = %x/%x/%x/%x : %x/%x/%x/%x\n",
+ cfg->speaker_pins[0], cfg->speaker_pins[1],
+ cfg->speaker_pins[2], cfg->speaker_pins[3],
+ spec->multiout.extra_out_nid[0],
+ spec->multiout.extra_out_nid[1],
+ spec->multiout.extra_out_nid[2],
+ spec->multiout.extra_out_nid[3]);
+ for (i = 0; i < cfg->speaker_outs; i++)
+ print_nid_path_idx(codec, " spk", spec->speaker_paths[i]);
+ for (i = 0; i < 3; i++)
+ print_nid_path_idx(codec, " mix", spec->aamix_out_paths[i]);
+}
+#else
+#define debug_show_configs(codec, cfg) /* NOP */
+#endif
+
+/* find all available DACs of the codec */
+static void fill_all_dac_nids(struct hda_codec *codec)
+{
+ struct hda_gen_spec *spec = codec->spec;
+ int i;
+ hda_nid_t nid = codec->start_nid;
+
+ spec->num_all_dacs = 0;
+ memset(spec->all_dacs, 0, sizeof(spec->all_dacs));
+ for (i = 0; i < codec->num_nodes; i++, nid++) {
+ if (get_wcaps_type(get_wcaps(codec, nid)) != AC_WID_AUD_OUT)
+ continue;
+ if (spec->num_all_dacs >= ARRAY_SIZE(spec->all_dacs)) {
+ codec_err(codec, "Too many DACs!\n");
+ break;
+ }
+ spec->all_dacs[spec->num_all_dacs++] = nid;
+ }
+}
+
+static int parse_output_paths(struct hda_codec *codec)
+{
+ struct hda_gen_spec *spec = codec->spec;
+ struct auto_pin_cfg *cfg = &spec->autocfg;
+ struct auto_pin_cfg *best_cfg;
+ unsigned int val;
+ int best_badness = INT_MAX;
+ int badness;
+ bool fill_hardwired = true, fill_mio_first = true;
+ bool best_wired = true, best_mio = true;
+ bool hp_spk_swapped = false;
+
+ best_cfg = kmalloc(sizeof(*best_cfg), GFP_KERNEL);
+ if (!best_cfg)
+ return -ENOMEM;
+ *best_cfg = *cfg;
+
+ for (;;) {
+ badness = fill_and_eval_dacs(codec, fill_hardwired,
+ fill_mio_first);
+ if (badness < 0) {
+ kfree(best_cfg);
+ return badness;
+ }
+ debug_badness("==> lo_type=%d, wired=%d, mio=%d, badness=0x%x\n",
+ cfg->line_out_type, fill_hardwired, fill_mio_first,
+ badness);
+ debug_show_configs(codec, cfg);
+ if (badness < best_badness) {
+ best_badness = badness;
+ *best_cfg = *cfg;
+ best_wired = fill_hardwired;
+ best_mio = fill_mio_first;
+ }
+ if (!badness)
+ break;
+ fill_mio_first = !fill_mio_first;
+ if (!fill_mio_first)
+ continue;
+ fill_hardwired = !fill_hardwired;
+ if (!fill_hardwired)
+ continue;
+ if (hp_spk_swapped)
+ break;
+ hp_spk_swapped = true;
+ if (cfg->speaker_outs > 0 &&
+ cfg->line_out_type == AUTO_PIN_HP_OUT) {
+ cfg->hp_outs = cfg->line_outs;
+ memcpy(cfg->hp_pins, cfg->line_out_pins,
+ sizeof(cfg->hp_pins));
+ cfg->line_outs = cfg->speaker_outs;
+ memcpy(cfg->line_out_pins, cfg->speaker_pins,
+ sizeof(cfg->speaker_pins));
+ cfg->speaker_outs = 0;
+ memset(cfg->speaker_pins, 0, sizeof(cfg->speaker_pins));
+ cfg->line_out_type = AUTO_PIN_SPEAKER_OUT;
+ fill_hardwired = true;
+ continue;
+ }
+ if (cfg->hp_outs > 0 &&
+ cfg->line_out_type == AUTO_PIN_SPEAKER_OUT) {
+ cfg->speaker_outs = cfg->line_outs;
+ memcpy(cfg->speaker_pins, cfg->line_out_pins,
+ sizeof(cfg->speaker_pins));
+ cfg->line_outs = cfg->hp_outs;
+ memcpy(cfg->line_out_pins, cfg->hp_pins,
+ sizeof(cfg->hp_pins));
+ cfg->hp_outs = 0;
+ memset(cfg->hp_pins, 0, sizeof(cfg->hp_pins));
+ cfg->line_out_type = AUTO_PIN_HP_OUT;
+ fill_hardwired = true;
+ continue;
+ }
+ break;
+ }
+
+ if (badness) {
+ debug_badness("==> restoring best_cfg\n");
+ *cfg = *best_cfg;
+ fill_and_eval_dacs(codec, best_wired, best_mio);
+ }
+ debug_badness("==> Best config: lo_type=%d, wired=%d, mio=%d\n",
+ cfg->line_out_type, best_wired, best_mio);
+ debug_show_configs(codec, cfg);
+
+ if (cfg->line_out_pins[0]) {
+ struct nid_path *path;
+ path = snd_hda_get_path_from_idx(codec, spec->out_paths[0]);
+ if (path)
+ spec->vmaster_nid = look_for_out_vol_nid(codec, path);
+ if (spec->vmaster_nid)
+ snd_hda_set_vmaster_tlv(codec, spec->vmaster_nid,
+ HDA_OUTPUT, spec->vmaster_tlv);
+ }
+
+ /* set initial pinctl targets */
+ if (spec->prefer_hp_amp || cfg->line_out_type == AUTO_PIN_HP_OUT)
+ val = PIN_HP;
+ else
+ val = PIN_OUT;
+ set_pin_targets(codec, cfg->line_outs, cfg->line_out_pins, val);
+ if (cfg->line_out_type != AUTO_PIN_HP_OUT)
+ set_pin_targets(codec, cfg->hp_outs, cfg->hp_pins, PIN_HP);
+ if (cfg->line_out_type != AUTO_PIN_SPEAKER_OUT) {
+ val = spec->prefer_hp_amp ? PIN_HP : PIN_OUT;
+ set_pin_targets(codec, cfg->speaker_outs,
+ cfg->speaker_pins, val);
+ }
+
+ /* clear indep_hp flag if not available */
+ if (spec->indep_hp && !indep_hp_possible(codec))
+ spec->indep_hp = 0;
+
+ kfree(best_cfg);
+ return 0;
+}
+
+/* add playback controls from the parsed DAC table */
+static int create_multi_out_ctls(struct hda_codec *codec,
+ const struct auto_pin_cfg *cfg)
+{
+ struct hda_gen_spec *spec = codec->spec;
+ int i, err, noutputs;
+
+ noutputs = cfg->line_outs;
+ if (spec->multi_ios > 0 && cfg->line_outs < 3)
+ noutputs += spec->multi_ios;
+
+ for (i = 0; i < noutputs; i++) {
+ const char *name;
+ int index;
+ struct nid_path *path;
+
+ path = snd_hda_get_path_from_idx(codec, spec->out_paths[i]);
+ if (!path)
+ continue;
+
+ name = get_line_out_pfx(codec, i, &index, NID_PATH_VOL_CTL);
+ if (!name || !strcmp(name, "CLFE")) {
+ /* Center/LFE */
+ err = add_vol_ctl(codec, "Center", 0, 1, path);
+ if (err < 0)
+ return err;
+ err = add_vol_ctl(codec, "LFE", 0, 2, path);
if (err < 0)
- return NULL;
+ return err;
+ } else {
+ err = add_stereo_vol(codec, name, index, path);
+ if (err < 0)
+ return err;
}
- if (err > 0) {
- /* unmute the PIN output */
- unmute_output(codec, node);
- /* set PIN-Out enable */
- snd_hda_codec_write_cache(codec, node->nid, 0,
- AC_VERB_SET_PIN_WIDGET_CONTROL,
- AC_PINCTL_OUT_EN |
- ((node->pin_caps & AC_PINCAP_HP_DRV) ?
- AC_PINCTL_HP_EN : 0));
- return node;
+
+ name = get_line_out_pfx(codec, i, &index, NID_PATH_MUTE_CTL);
+ if (!name || !strcmp(name, "CLFE")) {
+ err = add_sw_ctl(codec, "Center", 0, 1, path);
+ if (err < 0)
+ return err;
+ err = add_sw_ctl(codec, "LFE", 0, 2, path);
+ if (err < 0)
+ return err;
+ } else {
+ err = add_stereo_sw(codec, name, index, path);
+ if (err < 0)
+ return err;
}
}
- return NULL;
+ return 0;
}
+static int create_extra_out(struct hda_codec *codec, int path_idx,
+ const char *pfx, int cidx)
+{
+ struct nid_path *path;
+ int err;
+
+ path = snd_hda_get_path_from_idx(codec, path_idx);
+ if (!path)
+ return 0;
+ err = add_stereo_vol(codec, pfx, cidx, path);
+ if (err < 0)
+ return err;
+ err = add_stereo_sw(codec, pfx, cidx, path);
+ if (err < 0)
+ return err;
+ return 0;
+}
+
+/* add playback controls for speaker and HP outputs */
+static int create_extra_outs(struct hda_codec *codec, int num_pins,
+ const int *paths, const char *pfx)
+{
+ int i;
+
+ for (i = 0; i < num_pins; i++) {
+ const char *name;
+ char tmp[SNDRV_CTL_ELEM_ID_NAME_MAXLEN];
+ int err, idx = 0;
+
+ if (num_pins == 2 && i == 1 && !strcmp(pfx, "Speaker"))
+ name = "Bass Speaker";
+ else if (num_pins >= 3) {
+ snprintf(tmp, sizeof(tmp), "%s %s",
+ pfx, channel_name[i]);
+ name = tmp;
+ } else {
+ name = pfx;
+ idx = i;
+ }
+ err = create_extra_out(codec, paths[i], name, idx);
+ if (err < 0)
+ return err;
+ }
+ return 0;
+}
+
+static int create_hp_out_ctls(struct hda_codec *codec)
+{
+ struct hda_gen_spec *spec = codec->spec;
+ return create_extra_outs(codec, spec->autocfg.hp_outs,
+ spec->hp_paths,
+ "Headphone");
+}
+
+static int create_speaker_out_ctls(struct hda_codec *codec)
+{
+ struct hda_gen_spec *spec = codec->spec;
+ return create_extra_outs(codec, spec->autocfg.speaker_outs,
+ spec->speaker_paths,
+ "Speaker");
+}
/*
- * parse outputs
+ * independent HP controls
*/
-static int parse_output(struct hda_codec *codec)
+
+static void call_hp_automute(struct hda_codec *codec, struct hda_jack_tbl *jack);
+static int indep_hp_info(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_info *uinfo)
{
- struct hda_gspec *spec = codec->spec;
- struct hda_gnode *node;
+ return snd_hda_enum_bool_helper_info(kcontrol, uinfo);
+}
- /*
- * Look for the output PIN widget
- */
- /* first, look for the line-out pin */
- node = parse_output_jack(codec, spec, AC_JACK_LINE_OUT);
- if (node) /* found, remember the PIN node */
- spec->out_pin_node[0] = node;
- else {
- /* if no line-out is found, try speaker out */
- node = parse_output_jack(codec, spec, AC_JACK_SPEAKER);
- if (node)
- spec->out_pin_node[0] = node;
- }
- /* look for the HP-out pin */
- node = parse_output_jack(codec, spec, AC_JACK_HP_OUT);
- if (node) {
- if (! spec->out_pin_node[0])
- spec->out_pin_node[0] = node;
+static int indep_hp_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
+ struct hda_gen_spec *spec = codec->spec;
+ ucontrol->value.enumerated.item[0] = spec->indep_hp_enabled;
+ return 0;
+}
+
+static void update_aamix_paths(struct hda_codec *codec, bool do_mix,
+ int nomix_path_idx, int mix_path_idx,
+ int out_type);
+
+static int indep_hp_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
+ struct hda_gen_spec *spec = codec->spec;
+ unsigned int select = ucontrol->value.enumerated.item[0];
+ int ret = 0;
+
+ mutex_lock(&spec->pcm_mutex);
+ if (spec->active_streams) {
+ ret = -EBUSY;
+ goto unlock;
+ }
+
+ if (spec->indep_hp_enabled != select) {
+ hda_nid_t *dacp;
+ if (spec->autocfg.line_out_type == AUTO_PIN_HP_OUT)
+ dacp = &spec->private_dac_nids[0];
else
- spec->out_pin_node[1] = node;
+ dacp = &spec->multiout.hp_out_nid[0];
+
+ /* update HP aamix paths in case it conflicts with indep HP */
+ if (spec->have_aamix_ctl) {
+ if (spec->autocfg.line_out_type == AUTO_PIN_HP_OUT)
+ update_aamix_paths(codec, spec->aamix_mode,
+ spec->out_paths[0],
+ spec->aamix_out_paths[0],
+ spec->autocfg.line_out_type);
+ else
+ update_aamix_paths(codec, spec->aamix_mode,
+ spec->hp_paths[0],
+ spec->aamix_out_paths[1],
+ AUTO_PIN_HP_OUT);
+ }
+
+ spec->indep_hp_enabled = select;
+ if (spec->indep_hp_enabled)
+ *dacp = 0;
+ else
+ *dacp = spec->alt_dac_nid;
+
+ call_hp_automute(codec, NULL);
+ ret = 1;
}
+ unlock:
+ mutex_unlock(&spec->pcm_mutex);
+ return ret;
+}
- if (! spec->out_pin_node[0]) {
- /* no line-out or HP pins found,
- * then choose for the first output pin
- */
- spec->out_pin_node[0] = parse_output_jack(codec, spec, -1);
- if (! spec->out_pin_node[0])
- snd_printd("hda_generic: no proper output path found\n");
+static const struct snd_kcontrol_new indep_hp_ctl = {
+ .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+ .name = "Independent HP",
+ .info = indep_hp_info,
+ .get = indep_hp_get,
+ .put = indep_hp_put,
+};
+
+
+static int create_indep_hp_ctls(struct hda_codec *codec)
+{
+ struct hda_gen_spec *spec = codec->spec;
+ hda_nid_t dac;
+
+ if (!spec->indep_hp)
+ return 0;
+ if (spec->autocfg.line_out_type == AUTO_PIN_HP_OUT)
+ dac = spec->multiout.dac_nids[0];
+ else
+ dac = spec->multiout.hp_out_nid[0];
+ if (!dac) {
+ spec->indep_hp = 0;
+ return 0;
}
+ spec->indep_hp_enabled = false;
+ spec->alt_dac_nid = dac;
+ if (!snd_hda_gen_add_kctl(spec, NULL, &indep_hp_ctl))
+ return -ENOMEM;
return 0;
}
/*
- * input MUX
+ * channel mode enum control
*/
-/* control callbacks */
-static int capture_source_info(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *uinfo)
+static int ch_mode_info(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_info *uinfo)
{
struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
- struct hda_gspec *spec = codec->spec;
- return snd_hda_input_mux_info(&spec->input_mux, uinfo);
+ struct hda_gen_spec *spec = codec->spec;
+ int chs;
+
+ uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED;
+ uinfo->count = 1;
+ uinfo->value.enumerated.items = spec->multi_ios + 1;
+ if (uinfo->value.enumerated.item > spec->multi_ios)
+ uinfo->value.enumerated.item = spec->multi_ios;
+ chs = uinfo->value.enumerated.item * 2 + spec->min_channel_count;
+ sprintf(uinfo->value.enumerated.name, "%dch", chs);
+ return 0;
}
-static int capture_source_get(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
+static int ch_mode_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
{
struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
- struct hda_gspec *spec = codec->spec;
+ struct hda_gen_spec *spec = codec->spec;
+ ucontrol->value.enumerated.item[0] =
+ (spec->ext_channel_count - spec->min_channel_count) / 2;
+ return 0;
+}
+
+static inline struct nid_path *
+get_multiio_path(struct hda_codec *codec, int idx)
+{
+ struct hda_gen_spec *spec = codec->spec;
+ return snd_hda_get_path_from_idx(codec,
+ spec->out_paths[spec->autocfg.line_outs + idx]);
+}
+
+static void update_automute_all(struct hda_codec *codec);
+
+/* Default value to be passed as aamix argument for snd_hda_activate_path();
+ * used for output paths
+ */
+static bool aamix_default(struct hda_gen_spec *spec)
+{
+ return !spec->have_aamix_ctl || spec->aamix_mode;
+}
+
+static int set_multi_io(struct hda_codec *codec, int idx, bool output)
+{
+ struct hda_gen_spec *spec = codec->spec;
+ hda_nid_t nid = spec->multi_io[idx].pin;
+ struct nid_path *path;
+
+ path = get_multiio_path(codec, idx);
+ if (!path)
+ return -EINVAL;
+
+ if (path->active == output)
+ return 0;
+
+ if (output) {
+ set_pin_target(codec, nid, PIN_OUT, true);
+ snd_hda_activate_path(codec, path, true, aamix_default(spec));
+ set_pin_eapd(codec, nid, true);
+ } else {
+ set_pin_eapd(codec, nid, false);
+ snd_hda_activate_path(codec, path, false, aamix_default(spec));
+ set_pin_target(codec, nid, spec->multi_io[idx].ctl_in, true);
+ path_power_down_sync(codec, path);
+ }
+
+ /* update jack retasking in case it modifies any of them */
+ update_automute_all(codec);
- ucontrol->value.enumerated.item[0] = spec->cur_cap_src;
return 0;
}
-static int capture_source_put(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
+static int ch_mode_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
{
struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
- struct hda_gspec *spec = codec->spec;
- return snd_hda_input_mux_put(codec, &spec->input_mux, ucontrol,
- spec->adc_node->nid, &spec->cur_cap_src);
+ struct hda_gen_spec *spec = codec->spec;
+ int i, ch;
+
+ ch = ucontrol->value.enumerated.item[0];
+ if (ch < 0 || ch > spec->multi_ios)
+ return -EINVAL;
+ if (ch == (spec->ext_channel_count - spec->min_channel_count) / 2)
+ return 0;
+ spec->ext_channel_count = ch * 2 + spec->min_channel_count;
+ for (i = 0; i < spec->multi_ios; i++)
+ set_multi_io(codec, i, i < ch);
+ spec->multiout.max_channels = max(spec->ext_channel_count,
+ spec->const_channel_count);
+ if (spec->need_dac_fix)
+ spec->multiout.num_dacs = spec->multiout.max_channels / 2;
+ return 1;
+}
+
+static const struct snd_kcontrol_new channel_mode_enum = {
+ .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+ .name = "Channel Mode",
+ .info = ch_mode_info,
+ .get = ch_mode_get,
+ .put = ch_mode_put,
+};
+
+static int create_multi_channel_mode(struct hda_codec *codec)
+{
+ struct hda_gen_spec *spec = codec->spec;
+
+ if (spec->multi_ios > 0) {
+ if (!snd_hda_gen_add_kctl(spec, NULL, &channel_mode_enum))
+ return -ENOMEM;
+ }
+ return 0;
}
/*
- * return the string name of the given input PIN widget
+ * aamix loopback enable/disable switch
*/
-static const char *get_input_type(struct hda_gnode *node, unsigned int *pinctl)
-{
- unsigned int location = defcfg_location(node);
- switch (defcfg_type(node)) {
- case AC_JACK_LINE_IN:
- if ((location & 0x0f) == AC_JACK_LOC_FRONT)
- return "Front Line";
- return "Line";
- case AC_JACK_CD:
-#if 0
- if (pinctl)
- *pinctl |= AC_PINCTL_VREF_GRD;
-#endif
- return "CD";
- case AC_JACK_AUX:
- if ((location & 0x0f) == AC_JACK_LOC_FRONT)
- return "Front Aux";
- return "Aux";
- case AC_JACK_MIC_IN:
- if (pinctl &&
- (node->pin_caps &
- (AC_PINCAP_VREF_80 << AC_PINCAP_VREF_SHIFT)))
- *pinctl |= AC_PINCTL_VREF_80;
- if ((location & 0x0f) == AC_JACK_LOC_FRONT)
- return "Front Mic";
- return "Mic";
- case AC_JACK_SPDIF_IN:
- return "SPDIF";
- case AC_JACK_DIG_OTHER_IN:
- return "Digital";
+
+#define loopback_mixing_info indep_hp_info
+
+static int loopback_mixing_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
+ struct hda_gen_spec *spec = codec->spec;
+ ucontrol->value.enumerated.item[0] = spec->aamix_mode;
+ return 0;
+}
+
+static void update_aamix_paths(struct hda_codec *codec, bool do_mix,
+ int nomix_path_idx, int mix_path_idx,
+ int out_type)
+{
+ struct hda_gen_spec *spec = codec->spec;
+ struct nid_path *nomix_path, *mix_path;
+
+ nomix_path = snd_hda_get_path_from_idx(codec, nomix_path_idx);
+ mix_path = snd_hda_get_path_from_idx(codec, mix_path_idx);
+ if (!nomix_path || !mix_path)
+ return;
+
+ /* if HP aamix path is driven from a different DAC and the
+ * independent HP mode is ON, can't turn on aamix path
+ */
+ if (out_type == AUTO_PIN_HP_OUT && spec->indep_hp_enabled &&
+ mix_path->path[0] != spec->alt_dac_nid)
+ do_mix = false;
+
+ if (do_mix) {
+ snd_hda_activate_path(codec, nomix_path, false, true);
+ snd_hda_activate_path(codec, mix_path, true, true);
+ path_power_down_sync(codec, nomix_path);
+ } else {
+ snd_hda_activate_path(codec, mix_path, false, false);
+ snd_hda_activate_path(codec, nomix_path, true, false);
+ path_power_down_sync(codec, mix_path);
}
- return NULL;
+}
+
+static int loopback_mixing_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
+ struct hda_gen_spec *spec = codec->spec;
+ unsigned int val = ucontrol->value.enumerated.item[0];
+
+ if (val == spec->aamix_mode)
+ return 0;
+ spec->aamix_mode = val;
+ update_aamix_paths(codec, val, spec->out_paths[0],
+ spec->aamix_out_paths[0],
+ spec->autocfg.line_out_type);
+ update_aamix_paths(codec, val, spec->hp_paths[0],
+ spec->aamix_out_paths[1],
+ AUTO_PIN_HP_OUT);
+ update_aamix_paths(codec, val, spec->speaker_paths[0],
+ spec->aamix_out_paths[2],
+ AUTO_PIN_SPEAKER_OUT);
+ return 1;
+}
+
+static const struct snd_kcontrol_new loopback_mixing_enum = {
+ .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+ .name = "Loopback Mixing",
+ .info = loopback_mixing_info,
+ .get = loopback_mixing_get,
+ .put = loopback_mixing_put,
+};
+
+static int create_loopback_mixing_ctl(struct hda_codec *codec)
+{
+ struct hda_gen_spec *spec = codec->spec;
+
+ if (!spec->mixer_nid)
+ return 0;
+ if (!(spec->aamix_out_paths[0] || spec->aamix_out_paths[1] ||
+ spec->aamix_out_paths[2]))
+ return 0;
+ if (!snd_hda_gen_add_kctl(spec, NULL, &loopback_mixing_enum))
+ return -ENOMEM;
+ spec->have_aamix_ctl = 1;
+ return 0;
}
/*
- * parse the nodes recursively until reach to the input PIN
- *
- * returns 0 if not found, 1 if found, or a negative error code.
+ * shared headphone/mic handling
*/
-static int parse_adc_sub_nodes(struct hda_codec *codec, struct hda_gspec *spec,
- struct hda_gnode *node, int idx)
+
+static void call_update_outputs(struct hda_codec *codec);
+
+/* for shared I/O, change the pin-control accordingly */
+static void update_hp_mic(struct hda_codec *codec, int adc_mux, bool force)
{
- int i, err;
- unsigned int pinctl;
- const char *type;
+ struct hda_gen_spec *spec = codec->spec;
+ bool as_mic;
+ unsigned int val;
+ hda_nid_t pin;
- if (node->checked)
- return 0;
+ pin = spec->hp_mic_pin;
+ as_mic = spec->cur_mux[adc_mux] == spec->hp_mic_mux_idx;
- node->checked = 1;
- if (node->type != AC_WID_PIN) {
- for (i = 0; i < node->nconns; i++) {
- struct hda_gnode *child;
- child = hda_get_node(spec, node->conn_list[i]);
- if (! child)
- continue;
- err = parse_adc_sub_nodes(codec, spec, child, idx);
- if (err < 0)
- return err;
- if (err > 0) {
- /* found one,
- * select the path, unmute both input and output
- */
- if (node->nconns > 1)
- select_input_connection(codec, node, i);
- unmute_input(codec, node, i);
- unmute_output(codec, node);
- return err;
- }
+ if (!force) {
+ val = snd_hda_codec_get_pin_target(codec, pin);
+ if (as_mic) {
+ if (val & PIN_IN)
+ return;
+ } else {
+ if (val & PIN_OUT)
+ return;
+ }
+ }
+
+ val = snd_hda_get_default_vref(codec, pin);
+ /* if the HP pin doesn't support VREF and the codec driver gives an
+ * alternative pin, set up the VREF on that pin instead
+ */
+ if (val == AC_PINCTL_VREF_HIZ && spec->shared_mic_vref_pin) {
+ const hda_nid_t vref_pin = spec->shared_mic_vref_pin;
+ unsigned int vref_val = snd_hda_get_default_vref(codec, vref_pin);
+ if (vref_val != AC_PINCTL_VREF_HIZ)
+ snd_hda_set_pin_ctl_cache(codec, vref_pin,
+ PIN_IN | (as_mic ? vref_val : 0));
+ }
+
+ if (!spec->hp_mic_jack_modes) {
+ if (as_mic)
+ val |= PIN_IN;
+ else
+ val = PIN_HP;
+ set_pin_target(codec, pin, val, true);
+ call_hp_automute(codec, NULL);
+ }
+}
+
+/* create a shared input with the headphone out */
+static int create_hp_mic(struct hda_codec *codec)
+{
+ struct hda_gen_spec *spec = codec->spec;
+ struct auto_pin_cfg *cfg = &spec->autocfg;
+ unsigned int defcfg;
+ hda_nid_t nid;
+
+ if (!spec->hp_mic) {
+ if (spec->suppress_hp_mic_detect)
+ return 0;
+ /* automatic detection: only if no input or a single internal
+ * input pin is found, try to detect the shared hp/mic
+ */
+ if (cfg->num_inputs > 1)
+ return 0;
+ else if (cfg->num_inputs == 1) {
+ defcfg = snd_hda_codec_get_pincfg(codec, cfg->inputs[0].pin);
+ if (snd_hda_get_input_pin_attr(defcfg) != INPUT_PIN_ATTR_INT)
+ return 0;
}
- return 0;
}
- /* input capable? */
- if (! (node->pin_caps & AC_PINCAP_IN))
+ spec->hp_mic = 0; /* clear once */
+ if (cfg->num_inputs >= AUTO_CFG_MAX_INS)
return 0;
- if (defcfg_port_conn(node) == AC_JACK_PORT_NONE)
- return 0; /* unconnected */
+ nid = 0;
+ if (cfg->line_out_type == AUTO_PIN_HP_OUT && cfg->line_outs > 0)
+ nid = cfg->line_out_pins[0];
+ else if (cfg->hp_outs > 0)
+ nid = cfg->hp_pins[0];
+ if (!nid)
+ return 0;
- if (node->wid_caps & AC_WCAP_DIGITAL)
- return 0; /* skip SPDIF */
+ if (!(snd_hda_query_pin_caps(codec, nid) & AC_PINCAP_IN))
+ return 0; /* no input */
- if (spec->input_mux.num_items >= HDA_MAX_NUM_INPUTS) {
- snd_printk(KERN_ERR "hda_generic: Too many items for capture\n");
- return -EINVAL;
+ cfg->inputs[cfg->num_inputs].pin = nid;
+ cfg->inputs[cfg->num_inputs].type = AUTO_PIN_MIC;
+ cfg->inputs[cfg->num_inputs].is_headphone_mic = 1;
+ cfg->num_inputs++;
+ spec->hp_mic = 1;
+ spec->hp_mic_pin = nid;
+ /* we can't handle auto-mic together with HP-mic */
+ spec->suppress_auto_mic = 1;
+ codec_dbg(codec, "Enable shared I/O jack on NID 0x%x\n", nid);
+ return 0;
+}
+
+/*
+ * output jack mode
+ */
+
+static int create_hp_mic_jack_mode(struct hda_codec *codec, hda_nid_t pin);
+
+static const char * const out_jack_texts[] = {
+ "Line Out", "Headphone Out",
+};
+
+static int out_jack_mode_info(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_info *uinfo)
+{
+ return snd_hda_enum_helper_info(kcontrol, uinfo, 2, out_jack_texts);
+}
+
+static int out_jack_mode_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
+ hda_nid_t nid = kcontrol->private_value;
+ if (snd_hda_codec_get_pin_target(codec, nid) == PIN_HP)
+ ucontrol->value.enumerated.item[0] = 1;
+ else
+ ucontrol->value.enumerated.item[0] = 0;
+ return 0;
+}
+
+static int out_jack_mode_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
+ hda_nid_t nid = kcontrol->private_value;
+ unsigned int val;
+
+ val = ucontrol->value.enumerated.item[0] ? PIN_HP : PIN_OUT;
+ if (snd_hda_codec_get_pin_target(codec, nid) == val)
+ return 0;
+ snd_hda_set_pin_ctl_cache(codec, nid, val);
+ return 1;
+}
+
+static const struct snd_kcontrol_new out_jack_mode_enum = {
+ .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+ .info = out_jack_mode_info,
+ .get = out_jack_mode_get,
+ .put = out_jack_mode_put,
+};
+
+static bool find_kctl_name(struct hda_codec *codec, const char *name, int idx)
+{
+ struct hda_gen_spec *spec = codec->spec;
+ int i;
+
+ for (i = 0; i < spec->kctls.used; i++) {
+ struct snd_kcontrol_new *kctl = snd_array_elem(&spec->kctls, i);
+ if (!strcmp(kctl->name, name) && kctl->index == idx)
+ return true;
}
+ return false;
+}
- pinctl = AC_PINCTL_IN_EN;
- /* create a proper capture source label */
- type = get_input_type(node, &pinctl);
- if (! type) {
- /* input as default? */
- if (! (node->pin_ctl & AC_PINCTL_IN_EN))
- return 0;
- type = "Input";
+static void get_jack_mode_name(struct hda_codec *codec, hda_nid_t pin,
+ char *name, size_t name_len)
+{
+ struct hda_gen_spec *spec = codec->spec;
+ int idx = 0;
+
+ snd_hda_get_pin_label(codec, pin, &spec->autocfg, name, name_len, &idx);
+ strlcat(name, " Jack Mode", name_len);
+
+ for (; find_kctl_name(codec, name, idx); idx++)
+ ;
+}
+
+static int get_out_jack_num_items(struct hda_codec *codec, hda_nid_t pin)
+{
+ struct hda_gen_spec *spec = codec->spec;
+ if (spec->add_jack_modes) {
+ unsigned int pincap = snd_hda_query_pin_caps(codec, pin);
+ if ((pincap & AC_PINCAP_OUT) && (pincap & AC_PINCAP_HP_DRV))
+ return 2;
}
- snd_hda_add_imux_item(&spec->input_mux, type, idx, NULL);
+ return 1;
+}
+
+static int create_out_jack_modes(struct hda_codec *codec, int num_pins,
+ hda_nid_t *pins)
+{
+ struct hda_gen_spec *spec = codec->spec;
+ int i;
- /* unmute the PIN external input */
- unmute_input(codec, node, 0); /* index = 0? */
- /* set PIN-In enable */
- snd_hda_codec_write_cache(codec, node->nid, 0,
- AC_VERB_SET_PIN_WIDGET_CONTROL, pinctl);
+ for (i = 0; i < num_pins; i++) {
+ hda_nid_t pin = pins[i];
+ if (pin == spec->hp_mic_pin)
+ continue;
+ if (get_out_jack_num_items(codec, pin) > 1) {
+ struct snd_kcontrol_new *knew;
+ char name[SNDRV_CTL_ELEM_ID_NAME_MAXLEN];
+ get_jack_mode_name(codec, pin, name, sizeof(name));
+ knew = snd_hda_gen_add_kctl(spec, name,
+ &out_jack_mode_enum);
+ if (!knew)
+ return -ENOMEM;
+ knew->private_value = pin;
+ }
+ }
- return 1; /* found */
+ return 0;
}
/*
- * parse input
+ * input jack mode
*/
-static int parse_input_path(struct hda_codec *codec, struct hda_gnode *adc_node)
+
+/* from AC_PINCTL_VREF_HIZ to AC_PINCTL_VREF_100 */
+#define NUM_VREFS 6
+
+static const char * const vref_texts[NUM_VREFS] = {
+ "Line In", "Mic 50pc Bias", "Mic 0V Bias",
+ "", "Mic 80pc Bias", "Mic 100pc Bias"
+};
+
+static unsigned int get_vref_caps(struct hda_codec *codec, hda_nid_t pin)
{
- struct hda_gspec *spec = codec->spec;
- struct hda_gnode *node;
- int i, err;
+ unsigned int pincap;
- snd_printdd("AUD_IN = %x\n", adc_node->nid);
- clear_check_flags(spec);
+ pincap = snd_hda_query_pin_caps(codec, pin);
+ pincap = (pincap & AC_PINCAP_VREF) >> AC_PINCAP_VREF_SHIFT;
+ /* filter out unusual vrefs */
+ pincap &= ~(AC_PINCAP_VREF_GRD | AC_PINCAP_VREF_100);
+ return pincap;
+}
- // awk added - fixed no recording due to muted widget
- unmute_input(codec, adc_node, 0);
-
- /*
- * check each connection of the ADC
- * if it reaches to a proper input PIN, add the path as the
- * input path.
- */
- /* first, check the direct connections to PIN widgets */
- for (i = 0; i < adc_node->nconns; i++) {
- node = hda_get_node(spec, adc_node->conn_list[i]);
- if (node && node->type == AC_WID_PIN) {
- err = parse_adc_sub_nodes(codec, spec, node, i);
- if (err < 0)
- return err;
+/* convert from the enum item index to the vref ctl index (0=HIZ, 1=50%...) */
+static int get_vref_idx(unsigned int vref_caps, unsigned int item_idx)
+{
+ unsigned int i, n = 0;
+
+ for (i = 0; i < NUM_VREFS; i++) {
+ if (vref_caps & (1 << i)) {
+ if (n == item_idx)
+ return i;
+ n++;
}
}
- /* ... then check the rests, more complicated connections */
- for (i = 0; i < adc_node->nconns; i++) {
- node = hda_get_node(spec, adc_node->conn_list[i]);
- if (node && node->type != AC_WID_PIN) {
- err = parse_adc_sub_nodes(codec, spec, node, i);
- if (err < 0)
- return err;
- }
+ return 0;
+}
+
+/* convert back from the vref ctl index to the enum item index */
+static int cvt_from_vref_idx(unsigned int vref_caps, unsigned int idx)
+{
+ unsigned int i, n = 0;
+
+ for (i = 0; i < NUM_VREFS; i++) {
+ if (i == idx)
+ return n;
+ if (vref_caps & (1 << i))
+ n++;
}
+ return 0;
+}
- if (! spec->input_mux.num_items)
- return 0; /* no input path found... */
+static int in_jack_mode_info(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_info *uinfo)
+{
+ struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
+ hda_nid_t nid = kcontrol->private_value;
+ unsigned int vref_caps = get_vref_caps(codec, nid);
- snd_printdd("[Capture Source] NID=0x%x, #SRC=%d\n", adc_node->nid, spec->input_mux.num_items);
- for (i = 0; i < spec->input_mux.num_items; i++)
- snd_printdd(" [%s] IDX=0x%x\n", spec->input_mux.items[i].label,
- spec->input_mux.items[i].index);
+ snd_hda_enum_helper_info(kcontrol, uinfo, hweight32(vref_caps),
+ vref_texts);
+ /* set the right text */
+ strcpy(uinfo->value.enumerated.name,
+ vref_texts[get_vref_idx(vref_caps, uinfo->value.enumerated.item)]);
+ return 0;
+}
+
+static int in_jack_mode_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
+ hda_nid_t nid = kcontrol->private_value;
+ unsigned int vref_caps = get_vref_caps(codec, nid);
+ unsigned int idx;
+
+ idx = snd_hda_codec_get_pin_target(codec, nid) & AC_PINCTL_VREFEN;
+ ucontrol->value.enumerated.item[0] = cvt_from_vref_idx(vref_caps, idx);
+ return 0;
+}
- spec->adc_node = adc_node;
+static int in_jack_mode_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
+ hda_nid_t nid = kcontrol->private_value;
+ unsigned int vref_caps = get_vref_caps(codec, nid);
+ unsigned int val, idx;
+
+ val = snd_hda_codec_get_pin_target(codec, nid);
+ idx = cvt_from_vref_idx(vref_caps, val & AC_PINCTL_VREFEN);
+ if (idx == ucontrol->value.enumerated.item[0])
+ return 0;
+
+ val &= ~AC_PINCTL_VREFEN;
+ val |= get_vref_idx(vref_caps, ucontrol->value.enumerated.item[0]);
+ snd_hda_set_pin_ctl_cache(codec, nid, val);
return 1;
}
+static const struct snd_kcontrol_new in_jack_mode_enum = {
+ .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+ .info = in_jack_mode_info,
+ .get = in_jack_mode_get,
+ .put = in_jack_mode_put,
+};
+
+static int get_in_jack_num_items(struct hda_codec *codec, hda_nid_t pin)
+{
+ struct hda_gen_spec *spec = codec->spec;
+ int nitems = 0;
+ if (spec->add_jack_modes)
+ nitems = hweight32(get_vref_caps(codec, pin));
+ return nitems ? nitems : 1;
+}
+
+static int create_in_jack_mode(struct hda_codec *codec, hda_nid_t pin)
+{
+ struct hda_gen_spec *spec = codec->spec;
+ struct snd_kcontrol_new *knew;
+ char name[SNDRV_CTL_ELEM_ID_NAME_MAXLEN];
+ unsigned int defcfg;
+
+ if (pin == spec->hp_mic_pin)
+ return 0; /* already done in create_out_jack_mode() */
+
+ /* no jack mode for fixed pins */
+ defcfg = snd_hda_codec_get_pincfg(codec, pin);
+ if (snd_hda_get_input_pin_attr(defcfg) == INPUT_PIN_ATTR_INT)
+ return 0;
+
+ /* no multiple vref caps? */
+ if (get_in_jack_num_items(codec, pin) <= 1)
+ return 0;
+
+ get_jack_mode_name(codec, pin, name, sizeof(name));
+ knew = snd_hda_gen_add_kctl(spec, name, &in_jack_mode_enum);
+ if (!knew)
+ return -ENOMEM;
+ knew->private_value = pin;
+ return 0;
+}
+
/*
- * parse input
+ * HP/mic shared jack mode
*/
-static int parse_input(struct hda_codec *codec)
+static int hp_mic_jack_mode_info(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_info *uinfo)
{
- struct hda_gspec *spec = codec->spec;
- struct hda_gnode *node;
- int err;
+ struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
+ hda_nid_t nid = kcontrol->private_value;
+ int out_jacks = get_out_jack_num_items(codec, nid);
+ int in_jacks = get_in_jack_num_items(codec, nid);
+ const char *text = NULL;
+ int idx;
- /*
- * At first we look for an audio input widget.
- * If it reaches to certain input PINs, we take it as the
- * input path.
- */
- list_for_each_entry(node, &spec->nid_list, list) {
- if (node->wid_caps & AC_WCAP_DIGITAL)
- continue; /* skip SPDIF */
- if (node->type == AC_WID_AUD_IN) {
- err = parse_input_path(codec, node);
- if (err < 0)
- return err;
- else if (err > 0)
- return 0;
+ uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED;
+ uinfo->count = 1;
+ uinfo->value.enumerated.items = out_jacks + in_jacks;
+ if (uinfo->value.enumerated.item >= uinfo->value.enumerated.items)
+ uinfo->value.enumerated.item = uinfo->value.enumerated.items - 1;
+ idx = uinfo->value.enumerated.item;
+ if (idx < out_jacks) {
+ if (out_jacks > 1)
+ text = out_jack_texts[idx];
+ else
+ text = "Headphone Out";
+ } else {
+ idx -= out_jacks;
+ if (in_jacks > 1) {
+ unsigned int vref_caps = get_vref_caps(codec, nid);
+ text = vref_texts[get_vref_idx(vref_caps, idx)];
+ } else
+ text = "Mic In";
+ }
+
+ strcpy(uinfo->value.enumerated.name, text);
+ return 0;
+}
+
+static int get_cur_hp_mic_jack_mode(struct hda_codec *codec, hda_nid_t nid)
+{
+ int out_jacks = get_out_jack_num_items(codec, nid);
+ int in_jacks = get_in_jack_num_items(codec, nid);
+ unsigned int val = snd_hda_codec_get_pin_target(codec, nid);
+ int idx = 0;
+
+ if (val & PIN_OUT) {
+ if (out_jacks > 1 && val == PIN_HP)
+ idx = 1;
+ } else if (val & PIN_IN) {
+ idx = out_jacks;
+ if (in_jacks > 1) {
+ unsigned int vref_caps = get_vref_caps(codec, nid);
+ val &= AC_PINCTL_VREFEN;
+ idx += cvt_from_vref_idx(vref_caps, val);
}
}
- snd_printd("hda_generic: no proper input path found\n");
+ return idx;
+}
+
+static int hp_mic_jack_mode_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
+ hda_nid_t nid = kcontrol->private_value;
+ ucontrol->value.enumerated.item[0] =
+ get_cur_hp_mic_jack_mode(codec, nid);
return 0;
}
-#ifdef CONFIG_SND_HDA_POWER_SAVE
-static void add_input_loopback(struct hda_codec *codec, hda_nid_t nid,
- int dir, int idx)
+static int hp_mic_jack_mode_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
{
- struct hda_gspec *spec = codec->spec;
- struct hda_amp_list *p;
+ struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
+ hda_nid_t nid = kcontrol->private_value;
+ int out_jacks = get_out_jack_num_items(codec, nid);
+ int in_jacks = get_in_jack_num_items(codec, nid);
+ unsigned int val, oldval, idx;
- if (spec->num_loopbacks >= MAX_LOOPBACK_AMPS) {
- snd_printk(KERN_ERR "hda_generic: Too many loopback ctls\n");
- return;
+ oldval = get_cur_hp_mic_jack_mode(codec, nid);
+ idx = ucontrol->value.enumerated.item[0];
+ if (oldval == idx)
+ return 0;
+
+ if (idx < out_jacks) {
+ if (out_jacks > 1)
+ val = idx ? PIN_HP : PIN_OUT;
+ else
+ val = PIN_HP;
+ } else {
+ idx -= out_jacks;
+ if (in_jacks > 1) {
+ unsigned int vref_caps = get_vref_caps(codec, nid);
+ val = snd_hda_codec_get_pin_target(codec, nid);
+ val &= ~(AC_PINCTL_VREFEN | PIN_HP);
+ val |= get_vref_idx(vref_caps, idx) | PIN_IN;
+ } else
+ val = snd_hda_get_default_vref(codec, nid) | PIN_IN;
}
- p = &spec->loopback_list[spec->num_loopbacks++];
- p->nid = nid;
- p->dir = dir;
- p->idx = idx;
- spec->loopback.amplist = spec->loopback_list;
+ snd_hda_set_pin_ctl_cache(codec, nid, val);
+ call_hp_automute(codec, NULL);
+
+ return 1;
+}
+
+static const struct snd_kcontrol_new hp_mic_jack_mode_enum = {
+ .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+ .info = hp_mic_jack_mode_info,
+ .get = hp_mic_jack_mode_get,
+ .put = hp_mic_jack_mode_put,
+};
+
+static int create_hp_mic_jack_mode(struct hda_codec *codec, hda_nid_t pin)
+{
+ struct hda_gen_spec *spec = codec->spec;
+ struct snd_kcontrol_new *knew;
+
+ knew = snd_hda_gen_add_kctl(spec, "Headphone Mic Jack Mode",
+ &hp_mic_jack_mode_enum);
+ if (!knew)
+ return -ENOMEM;
+ knew->private_value = pin;
+ spec->hp_mic_jack_modes = 1;
+ return 0;
}
-#else
-#define add_input_loopback(codec,nid,dir,idx)
-#endif
/*
- * create mixer controls if possible
+ * Parse input paths
*/
-static int create_mixer(struct hda_codec *codec, struct hda_gnode *node,
- unsigned int index, const char *type,
- const char *dir_sfx, int is_loopback)
+
+/* add the powersave loopback-list entry */
+static int add_loopback_list(struct hda_gen_spec *spec, hda_nid_t mix, int idx)
{
- char name[32];
- int err;
- int created = 0;
- struct snd_kcontrol_new knew;
+ struct hda_amp_list *list;
- if (type)
- sprintf(name, "%s %s Switch", type, dir_sfx);
- else
- sprintf(name, "%s Switch", dir_sfx);
- if ((node->wid_caps & AC_WCAP_IN_AMP) &&
- (node->amp_in_caps & AC_AMPCAP_MUTE)) {
- knew = (struct snd_kcontrol_new)HDA_CODEC_MUTE(name, node->nid, index, HDA_INPUT);
- if (is_loopback)
- add_input_loopback(codec, node->nid, HDA_INPUT, index);
- snd_printdd("[%s] NID=0x%x, DIR=IN, IDX=0x%x\n", name, node->nid, index);
- err = snd_hda_ctl_add(codec, node->nid,
- snd_ctl_new1(&knew, codec));
+ list = snd_array_new(&spec->loopback_list);
+ if (!list)
+ return -ENOMEM;
+ list->nid = mix;
+ list->dir = HDA_INPUT;
+ list->idx = idx;
+ spec->loopback.amplist = spec->loopback_list.list;
+ return 0;
+}
+
+/* return true if either a volume or a mute amp is found for the given
+ * aamix path; the amp has to be either in the mixer node or its direct leaf
+ */
+static bool look_for_mix_leaf_ctls(struct hda_codec *codec, hda_nid_t mix_nid,
+ hda_nid_t pin, unsigned int *mix_val,
+ unsigned int *mute_val)
+{
+ int idx, num_conns;
+ const hda_nid_t *list;
+ hda_nid_t nid;
+
+ idx = snd_hda_get_conn_index(codec, mix_nid, pin, true);
+ if (idx < 0)
+ return false;
+
+ *mix_val = *mute_val = 0;
+ if (nid_has_volume(codec, mix_nid, HDA_INPUT))
+ *mix_val = HDA_COMPOSE_AMP_VAL(mix_nid, 3, idx, HDA_INPUT);
+ if (nid_has_mute(codec, mix_nid, HDA_INPUT))
+ *mute_val = HDA_COMPOSE_AMP_VAL(mix_nid, 3, idx, HDA_INPUT);
+ if (*mix_val && *mute_val)
+ return true;
+
+ /* check leaf node */
+ num_conns = snd_hda_get_conn_list(codec, mix_nid, &list);
+ if (num_conns < idx)
+ return false;
+ nid = list[idx];
+ if (!*mix_val && nid_has_volume(codec, nid, HDA_OUTPUT) &&
+ !is_ctl_associated(codec, nid, HDA_OUTPUT, 0, NID_PATH_VOL_CTL))
+ *mix_val = HDA_COMPOSE_AMP_VAL(nid, 3, 0, HDA_OUTPUT);
+ if (!*mute_val && nid_has_mute(codec, nid, HDA_OUTPUT) &&
+ !is_ctl_associated(codec, nid, HDA_OUTPUT, 0, NID_PATH_MUTE_CTL))
+ *mute_val = HDA_COMPOSE_AMP_VAL(nid, 3, 0, HDA_OUTPUT);
+
+ return *mix_val || *mute_val;
+}
+
+/* create input playback/capture controls for the given pin */
+static int new_analog_input(struct hda_codec *codec, int input_idx,
+ hda_nid_t pin, const char *ctlname, int ctlidx,
+ hda_nid_t mix_nid)
+{
+ struct hda_gen_spec *spec = codec->spec;
+ struct nid_path *path;
+ unsigned int mix_val, mute_val;
+ int err, idx;
+
+ if (!look_for_mix_leaf_ctls(codec, mix_nid, pin, &mix_val, &mute_val))
+ return 0;
+
+ path = snd_hda_add_new_path(codec, pin, mix_nid, 0);
+ if (!path)
+ return -EINVAL;
+ print_nid_path(codec, "loopback", path);
+ spec->loopback_paths[input_idx] = snd_hda_get_path_idx(codec, path);
+
+ idx = path->idx[path->depth - 1];
+ if (mix_val) {
+ err = __add_pb_vol_ctrl(spec, HDA_CTL_WIDGET_VOL, ctlname, ctlidx, mix_val);
if (err < 0)
return err;
- created = 1;
- } else if ((node->wid_caps & AC_WCAP_OUT_AMP) &&
- (node->amp_out_caps & AC_AMPCAP_MUTE)) {
- knew = (struct snd_kcontrol_new)HDA_CODEC_MUTE(name, node->nid, 0, HDA_OUTPUT);
- if (is_loopback)
- add_input_loopback(codec, node->nid, HDA_OUTPUT, 0);
- snd_printdd("[%s] NID=0x%x, DIR=OUT\n", name, node->nid);
- err = snd_hda_ctl_add(codec, node->nid,
- snd_ctl_new1(&knew, codec));
+ path->ctls[NID_PATH_VOL_CTL] = mix_val;
+ }
+
+ if (mute_val) {
+ err = __add_pb_sw_ctrl(spec, HDA_CTL_WIDGET_MUTE, ctlname, ctlidx, mute_val);
if (err < 0)
return err;
- created = 1;
+ path->ctls[NID_PATH_MUTE_CTL] = mute_val;
}
- if (type)
- sprintf(name, "%s %s Volume", type, dir_sfx);
- else
- sprintf(name, "%s Volume", dir_sfx);
- if ((node->wid_caps & AC_WCAP_IN_AMP) &&
- (node->amp_in_caps & AC_AMPCAP_NUM_STEPS)) {
- knew = (struct snd_kcontrol_new)HDA_CODEC_VOLUME(name, node->nid, index, HDA_INPUT);
- snd_printdd("[%s] NID=0x%x, DIR=IN, IDX=0x%x\n", name, node->nid, index);
- err = snd_hda_ctl_add(codec, node->nid,
- snd_ctl_new1(&knew, codec));
+ path->active = true;
+ err = add_loopback_list(spec, mix_nid, idx);
+ if (err < 0)
+ return err;
+
+ if (spec->mixer_nid != spec->mixer_merge_nid &&
+ !spec->loopback_merge_path) {
+ path = snd_hda_add_new_path(codec, spec->mixer_nid,
+ spec->mixer_merge_nid, 0);
+ if (path) {
+ print_nid_path(codec, "loopback-merge", path);
+ path->active = true;
+ spec->loopback_merge_path =
+ snd_hda_get_path_idx(codec, path);
+ }
+ }
+
+ return 0;
+}
+
+static int is_input_pin(struct hda_codec *codec, hda_nid_t nid)
+{
+ unsigned int pincap = snd_hda_query_pin_caps(codec, nid);
+ return (pincap & AC_PINCAP_IN) != 0;
+}
+
+/* Parse the codec tree and retrieve ADCs */
+static int fill_adc_nids(struct hda_codec *codec)
+{
+ struct hda_gen_spec *spec = codec->spec;
+ hda_nid_t nid;
+ hda_nid_t *adc_nids = spec->adc_nids;
+ int max_nums = ARRAY_SIZE(spec->adc_nids);
+ int i, nums = 0;
+
+ nid = codec->start_nid;
+ for (i = 0; i < codec->num_nodes; i++, nid++) {
+ unsigned int caps = get_wcaps(codec, nid);
+ int type = get_wcaps_type(caps);
+
+ if (type != AC_WID_AUD_IN || (caps & AC_WCAP_DIGITAL))
+ continue;
+ adc_nids[nums] = nid;
+ if (++nums >= max_nums)
+ break;
+ }
+ spec->num_adc_nids = nums;
+
+ /* copy the detected ADCs to all_adcs[] */
+ spec->num_all_adcs = nums;
+ memcpy(spec->all_adcs, spec->adc_nids, nums * sizeof(hda_nid_t));
+
+ return nums;
+}
+
+/* filter out invalid adc_nids that don't give all active input pins;
+ * if needed, check whether dynamic ADC-switching is available
+ */
+static int check_dyn_adc_switch(struct hda_codec *codec)
+{
+ struct hda_gen_spec *spec = codec->spec;
+ struct hda_input_mux *imux = &spec->input_mux;
+ unsigned int ok_bits;
+ int i, n, nums;
+
+ nums = 0;
+ ok_bits = 0;
+ for (n = 0; n < spec->num_adc_nids; n++) {
+ for (i = 0; i < imux->num_items; i++) {
+ if (!spec->input_paths[i][n])
+ break;
+ }
+ if (i >= imux->num_items) {
+ ok_bits |= (1 << n);
+ nums++;
+ }
+ }
+
+ if (!ok_bits) {
+ /* check whether ADC-switch is possible */
+ for (i = 0; i < imux->num_items; i++) {
+ for (n = 0; n < spec->num_adc_nids; n++) {
+ if (spec->input_paths[i][n]) {
+ spec->dyn_adc_idx[i] = n;
+ break;
+ }
+ }
+ }
+
+ codec_dbg(codec, "enabling ADC switching\n");
+ spec->dyn_adc_switch = 1;
+ } else if (nums != spec->num_adc_nids) {
+ /* shrink the invalid adcs and input paths */
+ nums = 0;
+ for (n = 0; n < spec->num_adc_nids; n++) {
+ if (!(ok_bits & (1 << n)))
+ continue;
+ if (n != nums) {
+ spec->adc_nids[nums] = spec->adc_nids[n];
+ for (i = 0; i < imux->num_items; i++) {
+ invalidate_nid_path(codec,
+ spec->input_paths[i][nums]);
+ spec->input_paths[i][nums] =
+ spec->input_paths[i][n];
+ }
+ }
+ nums++;
+ }
+ spec->num_adc_nids = nums;
+ }
+
+ if (imux->num_items == 1 ||
+ (imux->num_items == 2 && spec->hp_mic)) {
+ codec_dbg(codec, "reducing to a single ADC\n");
+ spec->num_adc_nids = 1; /* reduce to a single ADC */
+ }
+
+ /* single index for individual volumes ctls */
+ if (!spec->dyn_adc_switch && spec->multi_cap_vol)
+ spec->num_adc_nids = 1;
+
+ return 0;
+}
+
+/* parse capture source paths from the given pin and create imux items */
+static int parse_capture_source(struct hda_codec *codec, hda_nid_t pin,
+ int cfg_idx, int num_adcs,
+ const char *label, int anchor)
+{
+ struct hda_gen_spec *spec = codec->spec;
+ struct hda_input_mux *imux = &spec->input_mux;
+ int imux_idx = imux->num_items;
+ bool imux_added = false;
+ int c;
+
+ for (c = 0; c < num_adcs; c++) {
+ struct nid_path *path;
+ hda_nid_t adc = spec->adc_nids[c];
+
+ if (!is_reachable_path(codec, pin, adc))
+ continue;
+ path = snd_hda_add_new_path(codec, pin, adc, anchor);
+ if (!path)
+ continue;
+ print_nid_path(codec, "input", path);
+ spec->input_paths[imux_idx][c] =
+ snd_hda_get_path_idx(codec, path);
+
+ if (!imux_added) {
+ if (spec->hp_mic_pin == pin)
+ spec->hp_mic_mux_idx = imux->num_items;
+ spec->imux_pins[imux->num_items] = pin;
+ snd_hda_add_imux_item(imux, label, cfg_idx, NULL);
+ imux_added = true;
+ if (spec->dyn_adc_switch)
+ spec->dyn_adc_idx[imux_idx] = c;
+ }
+ }
+
+ return 0;
+}
+
+/*
+ * create playback/capture controls for input pins
+ */
+
+/* fill the label for each input at first */
+static int fill_input_pin_labels(struct hda_codec *codec)
+{
+ struct hda_gen_spec *spec = codec->spec;
+ const struct auto_pin_cfg *cfg = &spec->autocfg;
+ int i;
+
+ for (i = 0; i < cfg->num_inputs; i++) {
+ hda_nid_t pin = cfg->inputs[i].pin;
+ const char *label;
+ int j, idx;
+
+ if (!is_input_pin(codec, pin))
+ continue;
+
+ label = hda_get_autocfg_input_label(codec, cfg, i);
+ idx = 0;
+ for (j = i - 1; j >= 0; j--) {
+ if (spec->input_labels[j] &&
+ !strcmp(spec->input_labels[j], label)) {
+ idx = spec->input_label_idxs[j] + 1;
+ break;
+ }
+ }
+
+ spec->input_labels[i] = label;
+ spec->input_label_idxs[i] = idx;
+ }
+
+ return 0;
+}
+
+#define CFG_IDX_MIX 99 /* a dummy cfg->input idx for stereo mix */
+
+static int create_input_ctls(struct hda_codec *codec)
+{
+ struct hda_gen_spec *spec = codec->spec;
+ const struct auto_pin_cfg *cfg = &spec->autocfg;
+ hda_nid_t mixer = spec->mixer_nid;
+ int num_adcs;
+ int i, err;
+ unsigned int val;
+
+ num_adcs = fill_adc_nids(codec);
+ if (num_adcs < 0)
+ return 0;
+
+ err = fill_input_pin_labels(codec);
+ if (err < 0)
+ return err;
+
+ for (i = 0; i < cfg->num_inputs; i++) {
+ hda_nid_t pin;
+
+ pin = cfg->inputs[i].pin;
+ if (!is_input_pin(codec, pin))
+ continue;
+
+ val = PIN_IN;
+ if (cfg->inputs[i].type == AUTO_PIN_MIC)
+ val |= snd_hda_get_default_vref(codec, pin);
+ if (pin != spec->hp_mic_pin)
+ set_pin_target(codec, pin, val, false);
+
+ if (mixer) {
+ if (is_reachable_path(codec, pin, mixer)) {
+ err = new_analog_input(codec, i, pin,
+ spec->input_labels[i],
+ spec->input_label_idxs[i],
+ mixer);
+ if (err < 0)
+ return err;
+ }
+ }
+
+ err = parse_capture_source(codec, pin, i, num_adcs,
+ spec->input_labels[i], -mixer);
if (err < 0)
return err;
- created = 1;
- } else if ((node->wid_caps & AC_WCAP_OUT_AMP) &&
- (node->amp_out_caps & AC_AMPCAP_NUM_STEPS)) {
- knew = (struct snd_kcontrol_new)HDA_CODEC_VOLUME(name, node->nid, 0, HDA_OUTPUT);
- snd_printdd("[%s] NID=0x%x, DIR=OUT\n", name, node->nid);
- err = snd_hda_ctl_add(codec, node->nid,
- snd_ctl_new1(&knew, codec));
+
+ if (spec->add_jack_modes) {
+ err = create_in_jack_mode(codec, pin);
+ if (err < 0)
+ return err;
+ }
+ }
+
+ /* add stereo mix when explicitly enabled via hint */
+ if (mixer && spec->add_stereo_mix_input &&
+ snd_hda_get_bool_hint(codec, "add_stereo_mix_input") > 0) {
+ err = parse_capture_source(codec, mixer, CFG_IDX_MIX, num_adcs,
+ "Stereo Mix", 0);
if (err < 0)
return err;
- created = 1;
}
- return created;
+ return 0;
}
+
/*
- * check whether the controls with the given name and direction suffix already exist
+ * input source mux
*/
-static int check_existing_control(struct hda_codec *codec, const char *type, const char *dir)
+
+/* get the input path specified by the given adc and imux indices */
+static struct nid_path *get_input_path(struct hda_codec *codec, int adc_idx, int imux_idx)
{
- struct snd_ctl_elem_id id;
- memset(&id, 0, sizeof(id));
- sprintf(id.name, "%s %s Volume", type, dir);
- id.iface = SNDRV_CTL_ELEM_IFACE_MIXER;
- if (snd_ctl_find_id(codec->bus->card, &id))
- return 1;
- sprintf(id.name, "%s %s Switch", type, dir);
- id.iface = SNDRV_CTL_ELEM_IFACE_MIXER;
- if (snd_ctl_find_id(codec->bus->card, &id))
- return 1;
+ struct hda_gen_spec *spec = codec->spec;
+ if (imux_idx < 0 || imux_idx >= HDA_MAX_NUM_INPUTS) {
+ snd_BUG();
+ return NULL;
+ }
+ if (spec->dyn_adc_switch)
+ adc_idx = spec->dyn_adc_idx[imux_idx];
+ if (adc_idx < 0 || adc_idx >= AUTO_CFG_MAX_INS) {
+ snd_BUG();
+ return NULL;
+ }
+ return snd_hda_get_path_from_idx(codec, spec->input_paths[imux_idx][adc_idx]);
+}
+
+static int mux_select(struct hda_codec *codec, unsigned int adc_idx,
+ unsigned int idx);
+
+static int mux_enum_info(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_info *uinfo)
+{
+ struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
+ struct hda_gen_spec *spec = codec->spec;
+ return snd_hda_input_mux_info(&spec->input_mux, uinfo);
+}
+
+static int mux_enum_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
+ struct hda_gen_spec *spec = codec->spec;
+ /* the ctls are created at once with multiple counts */
+ unsigned int adc_idx = snd_ctl_get_ioffidx(kcontrol, &ucontrol->id);
+
+ ucontrol->value.enumerated.item[0] = spec->cur_mux[adc_idx];
return 0;
}
+static int mux_enum_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
+ unsigned int adc_idx = snd_ctl_get_ioffidx(kcontrol, &ucontrol->id);
+ return mux_select(codec, adc_idx,
+ ucontrol->value.enumerated.item[0]);
+}
+
+static const struct snd_kcontrol_new cap_src_temp = {
+ .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+ .name = "Input Source",
+ .info = mux_enum_info,
+ .get = mux_enum_get,
+ .put = mux_enum_put,
+};
+
/*
- * build output mixer controls
+ * capture volume and capture switch ctls
*/
-static int create_output_mixers(struct hda_codec *codec,
- const char * const *names)
+
+typedef int (*put_call_t)(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol);
+
+/* call the given amp update function for all amps in the imux list at once */
+static int cap_put_caller(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol,
+ put_call_t func, int type)
{
- struct hda_gspec *spec = codec->spec;
- int i, err;
+ struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
+ struct hda_gen_spec *spec = codec->spec;
+ const struct hda_input_mux *imux;
+ struct nid_path *path;
+ int i, adc_idx, err = 0;
- for (i = 0; i < spec->pcm_vol_nodes; i++) {
- err = create_mixer(codec, spec->pcm_vol[i].node,
- spec->pcm_vol[i].index,
- names[i], "Playback", 0);
+ imux = &spec->input_mux;
+ adc_idx = kcontrol->id.index;
+ mutex_lock(&codec->control_mutex);
+ /* we use the cache-only update at first since multiple input paths
+ * may shared the same amp; by updating only caches, the redundant
+ * writes to hardware can be reduced.
+ */
+ codec->cached_write = 1;
+ for (i = 0; i < imux->num_items; i++) {
+ path = get_input_path(codec, adc_idx, i);
+ if (!path || !path->ctls[type])
+ continue;
+ kcontrol->private_value = path->ctls[type];
+ err = func(kcontrol, ucontrol);
if (err < 0)
- return err;
+ goto error;
}
- return 0;
+ error:
+ codec->cached_write = 0;
+ mutex_unlock(&codec->control_mutex);
+ snd_hda_codec_flush_cache(codec); /* flush the updates */
+ if (err >= 0 && spec->cap_sync_hook)
+ spec->cap_sync_hook(codec, kcontrol, ucontrol);
+ return err;
}
-static int build_output_controls(struct hda_codec *codec)
+/* capture volume ctl callbacks */
+#define cap_vol_info snd_hda_mixer_amp_volume_info
+#define cap_vol_get snd_hda_mixer_amp_volume_get
+#define cap_vol_tlv snd_hda_mixer_amp_tlv
+
+static int cap_vol_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
{
- struct hda_gspec *spec = codec->spec;
- static const char * const types_speaker[] = { "Speaker", "Headphone" };
- static const char * const types_line[] = { "Front", "Headphone" };
+ return cap_put_caller(kcontrol, ucontrol,
+ snd_hda_mixer_amp_volume_put,
+ NID_PATH_VOL_CTL);
+}
- switch (spec->pcm_vol_nodes) {
- case 1:
- return create_mixer(codec, spec->pcm_vol[0].node,
- spec->pcm_vol[0].index,
- "Master", "Playback", 0);
- case 2:
- if (defcfg_type(spec->out_pin_node[0]) == AC_JACK_SPEAKER)
- return create_output_mixers(codec, types_speaker);
- else
- return create_output_mixers(codec, types_line);
+static const struct snd_kcontrol_new cap_vol_temp = {
+ .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+ .name = "Capture Volume",
+ .access = (SNDRV_CTL_ELEM_ACCESS_READWRITE |
+ SNDRV_CTL_ELEM_ACCESS_TLV_READ |
+ SNDRV_CTL_ELEM_ACCESS_TLV_CALLBACK),
+ .info = cap_vol_info,
+ .get = cap_vol_get,
+ .put = cap_vol_put,
+ .tlv = { .c = cap_vol_tlv },
+};
+
+/* capture switch ctl callbacks */
+#define cap_sw_info snd_ctl_boolean_stereo_info
+#define cap_sw_get snd_hda_mixer_amp_switch_get
+
+static int cap_sw_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ return cap_put_caller(kcontrol, ucontrol,
+ snd_hda_mixer_amp_switch_put,
+ NID_PATH_MUTE_CTL);
+}
+
+static const struct snd_kcontrol_new cap_sw_temp = {
+ .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+ .name = "Capture Switch",
+ .info = cap_sw_info,
+ .get = cap_sw_get,
+ .put = cap_sw_put,
+};
+
+static int parse_capvol_in_path(struct hda_codec *codec, struct nid_path *path)
+{
+ hda_nid_t nid;
+ int i, depth;
+
+ path->ctls[NID_PATH_VOL_CTL] = path->ctls[NID_PATH_MUTE_CTL] = 0;
+ for (depth = 0; depth < 3; depth++) {
+ if (depth >= path->depth)
+ return -EINVAL;
+ i = path->depth - depth - 1;
+ nid = path->path[i];
+ if (!path->ctls[NID_PATH_VOL_CTL]) {
+ if (nid_has_volume(codec, nid, HDA_OUTPUT))
+ path->ctls[NID_PATH_VOL_CTL] =
+ HDA_COMPOSE_AMP_VAL(nid, 3, 0, HDA_OUTPUT);
+ else if (nid_has_volume(codec, nid, HDA_INPUT)) {
+ int idx = path->idx[i];
+ if (!depth && codec->single_adc_amp)
+ idx = 0;
+ path->ctls[NID_PATH_VOL_CTL] =
+ HDA_COMPOSE_AMP_VAL(nid, 3, idx, HDA_INPUT);
+ }
+ }
+ if (!path->ctls[NID_PATH_MUTE_CTL]) {
+ if (nid_has_mute(codec, nid, HDA_OUTPUT))
+ path->ctls[NID_PATH_MUTE_CTL] =
+ HDA_COMPOSE_AMP_VAL(nid, 3, 0, HDA_OUTPUT);
+ else if (nid_has_mute(codec, nid, HDA_INPUT)) {
+ int idx = path->idx[i];
+ if (!depth && codec->single_adc_amp)
+ idx = 0;
+ path->ctls[NID_PATH_MUTE_CTL] =
+ HDA_COMPOSE_AMP_VAL(nid, 3, idx, HDA_INPUT);
+ }
+ }
}
return 0;
}
-/* create capture volume/switch */
-static int build_input_controls(struct hda_codec *codec)
+static bool is_inv_dmic_pin(struct hda_codec *codec, hda_nid_t nid)
{
- struct hda_gspec *spec = codec->spec;
- struct hda_gnode *adc_node = spec->adc_node;
- int i, err;
- static struct snd_kcontrol_new cap_sel = {
- .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
- .name = "Capture Source",
- .info = capture_source_info,
- .get = capture_source_get,
- .put = capture_source_put,
- };
+ struct hda_gen_spec *spec = codec->spec;
+ struct auto_pin_cfg *cfg = &spec->autocfg;
+ unsigned int val;
+ int i;
- if (! adc_node || ! spec->input_mux.num_items)
- return 0; /* not found */
+ if (!spec->inv_dmic_split)
+ return false;
+ for (i = 0; i < cfg->num_inputs; i++) {
+ if (cfg->inputs[i].pin != nid)
+ continue;
+ if (cfg->inputs[i].type != AUTO_PIN_MIC)
+ return false;
+ val = snd_hda_codec_get_pincfg(codec, nid);
+ return snd_hda_get_input_pin_attr(val) == INPUT_PIN_ATTR_INT;
+ }
+ return false;
+}
+
+/* capture switch put callback for a single control with hook call */
+static int cap_single_sw_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
+ struct hda_gen_spec *spec = codec->spec;
+ int ret;
- spec->cur_cap_src = 0;
- select_input_connection(codec, adc_node,
- spec->input_mux.items[0].index);
+ ret = snd_hda_mixer_amp_switch_put(kcontrol, ucontrol);
+ if (ret < 0)
+ return ret;
- /* create capture volume and switch controls if the ADC has an amp */
- /* do we have only a single item? */
- if (spec->input_mux.num_items == 1) {
- err = create_mixer(codec, adc_node,
- spec->input_mux.items[0].index,
- NULL, "Capture", 0);
- if (err < 0)
- return err;
+ if (spec->cap_sync_hook)
+ spec->cap_sync_hook(codec, kcontrol, ucontrol);
+
+ return ret;
+}
+
+static int add_single_cap_ctl(struct hda_codec *codec, const char *label,
+ int idx, bool is_switch, unsigned int ctl,
+ bool inv_dmic)
+{
+ struct hda_gen_spec *spec = codec->spec;
+ char tmpname[SNDRV_CTL_ELEM_ID_NAME_MAXLEN];
+ int type = is_switch ? HDA_CTL_WIDGET_MUTE : HDA_CTL_WIDGET_VOL;
+ const char *sfx = is_switch ? "Switch" : "Volume";
+ unsigned int chs = inv_dmic ? 1 : 3;
+ struct snd_kcontrol_new *knew;
+
+ if (!ctl)
return 0;
- }
- /* create input MUX if multiple sources are available */
- err = snd_hda_ctl_add(codec, spec->adc_node->nid,
- snd_ctl_new1(&cap_sel, codec));
+ if (label)
+ snprintf(tmpname, sizeof(tmpname),
+ "%s Capture %s", label, sfx);
+ else
+ snprintf(tmpname, sizeof(tmpname),
+ "Capture %s", sfx);
+ knew = add_control(spec, type, tmpname, idx,
+ amp_val_replace_channels(ctl, chs));
+ if (!knew)
+ return -ENOMEM;
+ if (is_switch)
+ knew->put = cap_single_sw_put;
+ if (!inv_dmic)
+ return 0;
+
+ /* Make independent right kcontrol */
+ if (label)
+ snprintf(tmpname, sizeof(tmpname),
+ "Inverted %s Capture %s", label, sfx);
+ else
+ snprintf(tmpname, sizeof(tmpname),
+ "Inverted Capture %s", sfx);
+ knew = add_control(spec, type, tmpname, idx,
+ amp_val_replace_channels(ctl, 2));
+ if (!knew)
+ return -ENOMEM;
+ if (is_switch)
+ knew->put = cap_single_sw_put;
+ return 0;
+}
+
+/* create single (and simple) capture volume and switch controls */
+static int create_single_cap_vol_ctl(struct hda_codec *codec, int idx,
+ unsigned int vol_ctl, unsigned int sw_ctl,
+ bool inv_dmic)
+{
+ int err;
+ err = add_single_cap_ctl(codec, NULL, idx, false, vol_ctl, inv_dmic);
+ if (err < 0)
+ return err;
+ err = add_single_cap_ctl(codec, NULL, idx, true, sw_ctl, inv_dmic);
if (err < 0)
return err;
+ return 0;
+}
+
+/* create bound capture volume and switch controls */
+static int create_bind_cap_vol_ctl(struct hda_codec *codec, int idx,
+ unsigned int vol_ctl, unsigned int sw_ctl)
+{
+ struct hda_gen_spec *spec = codec->spec;
+ struct snd_kcontrol_new *knew;
+
+ if (vol_ctl) {
+ knew = snd_hda_gen_add_kctl(spec, NULL, &cap_vol_temp);
+ if (!knew)
+ return -ENOMEM;
+ knew->index = idx;
+ knew->private_value = vol_ctl;
+ knew->subdevice = HDA_SUBDEV_AMP_FLAG;
+ }
+ if (sw_ctl) {
+ knew = snd_hda_gen_add_kctl(spec, NULL, &cap_sw_temp);
+ if (!knew)
+ return -ENOMEM;
+ knew->index = idx;
+ knew->private_value = sw_ctl;
+ knew->subdevice = HDA_SUBDEV_AMP_FLAG;
+ }
+ return 0;
+}
+
+/* return the vol ctl when used first in the imux list */
+static unsigned int get_first_cap_ctl(struct hda_codec *codec, int idx, int type)
+{
+ struct nid_path *path;
+ unsigned int ctl;
+ int i;
- /* no volume control? */
- if (! (adc_node->wid_caps & AC_WCAP_IN_AMP) ||
- ! (adc_node->amp_in_caps & AC_AMPCAP_NUM_STEPS))
+ path = get_input_path(codec, 0, idx);
+ if (!path)
return 0;
+ ctl = path->ctls[type];
+ if (!ctl)
+ return 0;
+ for (i = 0; i < idx - 1; i++) {
+ path = get_input_path(codec, 0, i);
+ if (path && path->ctls[type] == ctl)
+ return 0;
+ }
+ return ctl;
+}
+
+/* create individual capture volume and switch controls per input */
+static int create_multi_cap_vol_ctl(struct hda_codec *codec)
+{
+ struct hda_gen_spec *spec = codec->spec;
+ struct hda_input_mux *imux = &spec->input_mux;
+ int i, err, type;
+
+ for (i = 0; i < imux->num_items; i++) {
+ bool inv_dmic;
+ int idx;
+
+ idx = imux->items[i].index;
+ if (idx >= spec->autocfg.num_inputs)
+ continue;
+ inv_dmic = is_inv_dmic_pin(codec, spec->imux_pins[i]);
+
+ for (type = 0; type < 2; type++) {
+ err = add_single_cap_ctl(codec,
+ spec->input_labels[idx],
+ spec->input_label_idxs[idx],
+ type,
+ get_first_cap_ctl(codec, i, type),
+ inv_dmic);
+ if (err < 0)
+ return err;
+ }
+ }
+ return 0;
+}
+
+static int create_capture_mixers(struct hda_codec *codec)
+{
+ struct hda_gen_spec *spec = codec->spec;
+ struct hda_input_mux *imux = &spec->input_mux;
+ int i, n, nums, err;
+
+ if (spec->dyn_adc_switch)
+ nums = 1;
+ else
+ nums = spec->num_adc_nids;
+
+ if (!spec->auto_mic && imux->num_items > 1) {
+ struct snd_kcontrol_new *knew;
+ const char *name;
+ name = nums > 1 ? "Input Source" : "Capture Source";
+ knew = snd_hda_gen_add_kctl(spec, name, &cap_src_temp);
+ if (!knew)
+ return -ENOMEM;
+ knew->count = nums;
+ }
+
+ for (n = 0; n < nums; n++) {
+ bool multi = false;
+ bool multi_cap_vol = spec->multi_cap_vol;
+ bool inv_dmic = false;
+ int vol, sw;
+
+ vol = sw = 0;
+ for (i = 0; i < imux->num_items; i++) {
+ struct nid_path *path;
+ path = get_input_path(codec, n, i);
+ if (!path)
+ continue;
+ parse_capvol_in_path(codec, path);
+ if (!vol)
+ vol = path->ctls[NID_PATH_VOL_CTL];
+ else if (vol != path->ctls[NID_PATH_VOL_CTL]) {
+ multi = true;
+ if (!same_amp_caps(codec, vol,
+ path->ctls[NID_PATH_VOL_CTL], HDA_INPUT))
+ multi_cap_vol = true;
+ }
+ if (!sw)
+ sw = path->ctls[NID_PATH_MUTE_CTL];
+ else if (sw != path->ctls[NID_PATH_MUTE_CTL]) {
+ multi = true;
+ if (!same_amp_caps(codec, sw,
+ path->ctls[NID_PATH_MUTE_CTL], HDA_INPUT))
+ multi_cap_vol = true;
+ }
+ if (is_inv_dmic_pin(codec, spec->imux_pins[i]))
+ inv_dmic = true;
+ }
- for (i = 0; i < spec->input_mux.num_items; i++) {
- struct snd_kcontrol_new knew;
- char name[32];
- sprintf(name, "%s Capture Volume",
- spec->input_mux.items[i].label);
- knew = (struct snd_kcontrol_new)
- HDA_CODEC_VOLUME(name, adc_node->nid,
- spec->input_mux.items[i].index,
- HDA_INPUT);
- err = snd_hda_ctl_add(codec, adc_node->nid,
- snd_ctl_new1(&knew, codec));
+ if (!multi)
+ err = create_single_cap_vol_ctl(codec, n, vol, sw,
+ inv_dmic);
+ else if (!multi_cap_vol && !inv_dmic)
+ err = create_bind_cap_vol_ctl(codec, n, vol, sw);
+ else
+ err = create_multi_cap_vol_ctl(codec);
if (err < 0)
return err;
}
@@ -859,226 +3600,1818 @@ static int build_input_controls(struct hda_codec *codec)
return 0;
}
+/*
+ * add mic boosts if needed
+ */
+
+/* check whether the given amp is feasible as a boost volume */
+static bool check_boost_vol(struct hda_codec *codec, hda_nid_t nid,
+ int dir, int idx)
+{
+ unsigned int step;
+
+ if (!nid_has_volume(codec, nid, dir) ||
+ is_ctl_associated(codec, nid, dir, idx, NID_PATH_VOL_CTL) ||
+ is_ctl_associated(codec, nid, dir, idx, NID_PATH_BOOST_CTL))
+ return false;
+
+ step = (query_amp_caps(codec, nid, dir) & AC_AMPCAP_STEP_SIZE)
+ >> AC_AMPCAP_STEP_SIZE_SHIFT;
+ if (step < 0x20)
+ return false;
+ return true;
+}
+
+/* look for a boost amp in a widget close to the pin */
+static unsigned int look_for_boost_amp(struct hda_codec *codec,
+ struct nid_path *path)
+{
+ unsigned int val = 0;
+ hda_nid_t nid;
+ int depth;
+
+ for (depth = 0; depth < 3; depth++) {
+ if (depth >= path->depth - 1)
+ break;
+ nid = path->path[depth];
+ if (depth && check_boost_vol(codec, nid, HDA_OUTPUT, 0)) {
+ val = HDA_COMPOSE_AMP_VAL(nid, 3, 0, HDA_OUTPUT);
+ break;
+ } else if (check_boost_vol(codec, nid, HDA_INPUT,
+ path->idx[depth])) {
+ val = HDA_COMPOSE_AMP_VAL(nid, 3, path->idx[depth],
+ HDA_INPUT);
+ break;
+ }
+ }
+
+ return val;
+}
+
+static int parse_mic_boost(struct hda_codec *codec)
+{
+ struct hda_gen_spec *spec = codec->spec;
+ struct auto_pin_cfg *cfg = &spec->autocfg;
+ struct hda_input_mux *imux = &spec->input_mux;
+ int i;
+
+ if (!spec->num_adc_nids)
+ return 0;
+
+ for (i = 0; i < imux->num_items; i++) {
+ struct nid_path *path;
+ unsigned int val;
+ int idx;
+ char boost_label[SNDRV_CTL_ELEM_ID_NAME_MAXLEN];
+
+ idx = imux->items[i].index;
+ if (idx >= imux->num_items)
+ continue;
+
+ /* check only line-in and mic pins */
+ if (cfg->inputs[idx].type > AUTO_PIN_LINE_IN)
+ continue;
+
+ path = get_input_path(codec, 0, i);
+ if (!path)
+ continue;
+
+ val = look_for_boost_amp(codec, path);
+ if (!val)
+ continue;
+
+ /* create a boost control */
+ snprintf(boost_label, sizeof(boost_label),
+ "%s Boost Volume", spec->input_labels[idx]);
+ if (!add_control(spec, HDA_CTL_WIDGET_VOL, boost_label,
+ spec->input_label_idxs[idx], val))
+ return -ENOMEM;
+
+ path->ctls[NID_PATH_BOOST_CTL] = val;
+ }
+ return 0;
+}
/*
- * parse the nodes recursively until reach to the output PIN.
- *
- * returns 0 - if not found,
- * 1 - if found, but no mixer is created
- * 2 - if found and mixer was already created, (just skip)
- * a negative error code
+ * parse digital I/Os and set up NIDs in BIOS auto-parse mode
*/
-static int parse_loopback_path(struct hda_codec *codec, struct hda_gspec *spec,
- struct hda_gnode *node, struct hda_gnode *dest_node,
- const char *type)
+static void parse_digital(struct hda_codec *codec)
{
+ struct hda_gen_spec *spec = codec->spec;
+ struct nid_path *path;
+ int i, nums;
+ hda_nid_t dig_nid, pin;
+
+ /* support multiple SPDIFs; the secondary is set up as a slave */
+ nums = 0;
+ for (i = 0; i < spec->autocfg.dig_outs; i++) {
+ pin = spec->autocfg.dig_out_pins[i];
+ dig_nid = look_for_dac(codec, pin, true);
+ if (!dig_nid)
+ continue;
+ path = snd_hda_add_new_path(codec, dig_nid, pin, 0);
+ if (!path)
+ continue;
+ print_nid_path(codec, "digout", path);
+ path->active = true;
+ spec->digout_paths[i] = snd_hda_get_path_idx(codec, path);
+ set_pin_target(codec, pin, PIN_OUT, false);
+ if (!nums) {
+ spec->multiout.dig_out_nid = dig_nid;
+ spec->dig_out_type = spec->autocfg.dig_out_type[0];
+ } else {
+ spec->multiout.slave_dig_outs = spec->slave_dig_outs;
+ if (nums >= ARRAY_SIZE(spec->slave_dig_outs) - 1)
+ break;
+ spec->slave_dig_outs[nums - 1] = dig_nid;
+ }
+ nums++;
+ }
+
+ if (spec->autocfg.dig_in_pin) {
+ pin = spec->autocfg.dig_in_pin;
+ dig_nid = codec->start_nid;
+ for (i = 0; i < codec->num_nodes; i++, dig_nid++) {
+ unsigned int wcaps = get_wcaps(codec, dig_nid);
+ if (get_wcaps_type(wcaps) != AC_WID_AUD_IN)
+ continue;
+ if (!(wcaps & AC_WCAP_DIGITAL))
+ continue;
+ path = snd_hda_add_new_path(codec, pin, dig_nid, 0);
+ if (path) {
+ print_nid_path(codec, "digin", path);
+ path->active = true;
+ spec->dig_in_nid = dig_nid;
+ spec->digin_path = snd_hda_get_path_idx(codec, path);
+ set_pin_target(codec, pin, PIN_IN, false);
+ break;
+ }
+ }
+ }
+}
+
+
+/*
+ * input MUX handling
+ */
+
+static bool dyn_adc_pcm_resetup(struct hda_codec *codec, int cur);
+
+/* select the given imux item; either unmute exclusively or select the route */
+static int mux_select(struct hda_codec *codec, unsigned int adc_idx,
+ unsigned int idx)
+{
+ struct hda_gen_spec *spec = codec->spec;
+ const struct hda_input_mux *imux;
+ struct nid_path *old_path, *path;
+
+ imux = &spec->input_mux;
+ if (!imux->num_items)
+ return 0;
+
+ if (idx >= imux->num_items)
+ idx = imux->num_items - 1;
+ if (spec->cur_mux[adc_idx] == idx)
+ return 0;
+
+ old_path = get_input_path(codec, adc_idx, spec->cur_mux[adc_idx]);
+ if (!old_path)
+ return 0;
+ if (old_path->active)
+ snd_hda_activate_path(codec, old_path, false, false);
+
+ spec->cur_mux[adc_idx] = idx;
+
+ if (spec->hp_mic)
+ update_hp_mic(codec, adc_idx, false);
+
+ if (spec->dyn_adc_switch)
+ dyn_adc_pcm_resetup(codec, idx);
+
+ path = get_input_path(codec, adc_idx, idx);
+ if (!path)
+ return 0;
+ if (path->active)
+ return 0;
+ snd_hda_activate_path(codec, path, true, false);
+ if (spec->cap_sync_hook)
+ spec->cap_sync_hook(codec, NULL, NULL);
+ path_power_down_sync(codec, old_path);
+ return 1;
+}
+
+
+/*
+ * Jack detections for HP auto-mute and mic-switch
+ */
+
+/* check each pin in the given array; returns true if any of them is plugged */
+static bool detect_jacks(struct hda_codec *codec, int num_pins, hda_nid_t *pins)
+{
+ int i;
+ bool present = false;
+
+ for (i = 0; i < num_pins; i++) {
+ hda_nid_t nid = pins[i];
+ if (!nid)
+ break;
+ /* don't detect pins retasked as inputs */
+ if (snd_hda_codec_get_pin_target(codec, nid) & AC_PINCTL_IN_EN)
+ continue;
+ if (snd_hda_jack_detect_state(codec, nid) == HDA_JACK_PRESENT)
+ present = true;
+ }
+ return present;
+}
+
+/* standard HP/line-out auto-mute helper */
+static void do_automute(struct hda_codec *codec, int num_pins, hda_nid_t *pins,
+ int *paths, bool mute)
+{
+ struct hda_gen_spec *spec = codec->spec;
+ int i;
+
+ for (i = 0; i < num_pins; i++) {
+ hda_nid_t nid = pins[i];
+ unsigned int val, oldval;
+ if (!nid)
+ break;
+
+ if (spec->auto_mute_via_amp) {
+ struct nid_path *path;
+ hda_nid_t mute_nid;
+
+ path = snd_hda_get_path_from_idx(codec, paths[i]);
+ if (!path)
+ continue;
+ mute_nid = get_amp_nid_(path->ctls[NID_PATH_MUTE_CTL]);
+ if (!mute_nid)
+ continue;
+ if (mute)
+ spec->mute_bits |= (1ULL << mute_nid);
+ else
+ spec->mute_bits &= ~(1ULL << mute_nid);
+ set_pin_eapd(codec, nid, !mute);
+ continue;
+ }
+
+ oldval = snd_hda_codec_get_pin_target(codec, nid);
+ if (oldval & PIN_IN)
+ continue; /* no mute for inputs */
+ /* don't reset VREF value in case it's controlling
+ * the amp (see alc861_fixup_asus_amp_vref_0f())
+ */
+ if (spec->keep_vref_in_automute)
+ val = oldval & ~PIN_HP;
+ else
+ val = 0;
+ if (!mute)
+ val |= oldval;
+ /* here we call update_pin_ctl() so that the pinctl is changed
+ * without changing the pinctl target value;
+ * the original target value will be still referred at the
+ * init / resume again
+ */
+ update_pin_ctl(codec, nid, val);
+ set_pin_eapd(codec, nid, !mute);
+ }
+}
+
+/* Toggle outputs muting */
+void snd_hda_gen_update_outputs(struct hda_codec *codec)
+{
+ struct hda_gen_spec *spec = codec->spec;
+ int *paths;
+ int on;
+
+ /* Control HP pins/amps depending on master_mute state;
+ * in general, HP pins/amps control should be enabled in all cases,
+ * but currently set only for master_mute, just to be safe
+ */
+ if (spec->autocfg.line_out_type == AUTO_PIN_HP_OUT)
+ paths = spec->out_paths;
+ else
+ paths = spec->hp_paths;
+ do_automute(codec, ARRAY_SIZE(spec->autocfg.hp_pins),
+ spec->autocfg.hp_pins, paths, spec->master_mute);
+
+ if (!spec->automute_speaker)
+ on = 0;
+ else
+ on = spec->hp_jack_present | spec->line_jack_present;
+ on |= spec->master_mute;
+ spec->speaker_muted = on;
+ if (spec->autocfg.line_out_type == AUTO_PIN_SPEAKER_OUT)
+ paths = spec->out_paths;
+ else
+ paths = spec->speaker_paths;
+ do_automute(codec, ARRAY_SIZE(spec->autocfg.speaker_pins),
+ spec->autocfg.speaker_pins, paths, on);
+
+ /* toggle line-out mutes if needed, too */
+ /* if LO is a copy of either HP or Speaker, don't need to handle it */
+ if (spec->autocfg.line_out_pins[0] == spec->autocfg.hp_pins[0] ||
+ spec->autocfg.line_out_pins[0] == spec->autocfg.speaker_pins[0])
+ return;
+ if (!spec->automute_lo)
+ on = 0;
+ else
+ on = spec->hp_jack_present;
+ on |= spec->master_mute;
+ spec->line_out_muted = on;
+ paths = spec->out_paths;
+ do_automute(codec, ARRAY_SIZE(spec->autocfg.line_out_pins),
+ spec->autocfg.line_out_pins, paths, on);
+}
+EXPORT_SYMBOL_GPL(snd_hda_gen_update_outputs);
+
+static void call_update_outputs(struct hda_codec *codec)
+{
+ struct hda_gen_spec *spec = codec->spec;
+ if (spec->automute_hook)
+ spec->automute_hook(codec);
+ else
+ snd_hda_gen_update_outputs(codec);
+
+ /* sync the whole vmaster slaves to reflect the new auto-mute status */
+ if (spec->auto_mute_via_amp && !codec->bus->shutdown)
+ snd_ctl_sync_vmaster(spec->vmaster_mute.sw_kctl, false);
+}
+
+/* standard HP-automute helper */
+void snd_hda_gen_hp_automute(struct hda_codec *codec, struct hda_jack_tbl *jack)
+{
+ struct hda_gen_spec *spec = codec->spec;
+ hda_nid_t *pins = spec->autocfg.hp_pins;
+ int num_pins = ARRAY_SIZE(spec->autocfg.hp_pins);
+
+ /* No detection for the first HP jack during indep-HP mode */
+ if (spec->indep_hp_enabled) {
+ pins++;
+ num_pins--;
+ }
+
+ spec->hp_jack_present = detect_jacks(codec, num_pins, pins);
+ if (!spec->detect_hp || (!spec->automute_speaker && !spec->automute_lo))
+ return;
+ call_update_outputs(codec);
+}
+EXPORT_SYMBOL_GPL(snd_hda_gen_hp_automute);
+
+/* standard line-out-automute helper */
+void snd_hda_gen_line_automute(struct hda_codec *codec, struct hda_jack_tbl *jack)
+{
+ struct hda_gen_spec *spec = codec->spec;
+
+ if (spec->autocfg.line_out_type == AUTO_PIN_SPEAKER_OUT)
+ return;
+ /* check LO jack only when it's different from HP */
+ if (spec->autocfg.line_out_pins[0] == spec->autocfg.hp_pins[0])
+ return;
+
+ spec->line_jack_present =
+ detect_jacks(codec, ARRAY_SIZE(spec->autocfg.line_out_pins),
+ spec->autocfg.line_out_pins);
+ if (!spec->automute_speaker || !spec->detect_lo)
+ return;
+ call_update_outputs(codec);
+}
+EXPORT_SYMBOL_GPL(snd_hda_gen_line_automute);
+
+/* standard mic auto-switch helper */
+void snd_hda_gen_mic_autoswitch(struct hda_codec *codec, struct hda_jack_tbl *jack)
+{
+ struct hda_gen_spec *spec = codec->spec;
+ int i;
+
+ if (!spec->auto_mic)
+ return;
+
+ for (i = spec->am_num_entries - 1; i > 0; i--) {
+ hda_nid_t pin = spec->am_entry[i].pin;
+ /* don't detect pins retasked as outputs */
+ if (snd_hda_codec_get_pin_target(codec, pin) & AC_PINCTL_OUT_EN)
+ continue;
+ if (snd_hda_jack_detect_state(codec, pin) == HDA_JACK_PRESENT) {
+ mux_select(codec, 0, spec->am_entry[i].idx);
+ return;
+ }
+ }
+ mux_select(codec, 0, spec->am_entry[0].idx);
+}
+EXPORT_SYMBOL_GPL(snd_hda_gen_mic_autoswitch);
+
+/* call appropriate hooks */
+static void call_hp_automute(struct hda_codec *codec, struct hda_jack_tbl *jack)
+{
+ struct hda_gen_spec *spec = codec->spec;
+ if (spec->hp_automute_hook)
+ spec->hp_automute_hook(codec, jack);
+ else
+ snd_hda_gen_hp_automute(codec, jack);
+}
+
+static void call_line_automute(struct hda_codec *codec,
+ struct hda_jack_tbl *jack)
+{
+ struct hda_gen_spec *spec = codec->spec;
+ if (spec->line_automute_hook)
+ spec->line_automute_hook(codec, jack);
+ else
+ snd_hda_gen_line_automute(codec, jack);
+}
+
+static void call_mic_autoswitch(struct hda_codec *codec,
+ struct hda_jack_tbl *jack)
+{
+ struct hda_gen_spec *spec = codec->spec;
+ if (spec->mic_autoswitch_hook)
+ spec->mic_autoswitch_hook(codec, jack);
+ else
+ snd_hda_gen_mic_autoswitch(codec, jack);
+}
+
+/* update jack retasking */
+static void update_automute_all(struct hda_codec *codec)
+{
+ call_hp_automute(codec, NULL);
+ call_line_automute(codec, NULL);
+ call_mic_autoswitch(codec, NULL);
+}
+
+/*
+ * Auto-Mute mode mixer enum support
+ */
+static int automute_mode_info(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_info *uinfo)
+{
+ struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
+ struct hda_gen_spec *spec = codec->spec;
+ static const char * const texts3[] = {
+ "Disabled", "Speaker Only", "Line Out+Speaker"
+ };
+
+ if (spec->automute_speaker_possible && spec->automute_lo_possible)
+ return snd_hda_enum_helper_info(kcontrol, uinfo, 3, texts3);
+ return snd_hda_enum_bool_helper_info(kcontrol, uinfo);
+}
+
+static int automute_mode_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
+ struct hda_gen_spec *spec = codec->spec;
+ unsigned int val = 0;
+ if (spec->automute_speaker)
+ val++;
+ if (spec->automute_lo)
+ val++;
+
+ ucontrol->value.enumerated.item[0] = val;
+ return 0;
+}
+
+static int automute_mode_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
+ struct hda_gen_spec *spec = codec->spec;
+
+ switch (ucontrol->value.enumerated.item[0]) {
+ case 0:
+ if (!spec->automute_speaker && !spec->automute_lo)
+ return 0;
+ spec->automute_speaker = 0;
+ spec->automute_lo = 0;
+ break;
+ case 1:
+ if (spec->automute_speaker_possible) {
+ if (!spec->automute_lo && spec->automute_speaker)
+ return 0;
+ spec->automute_speaker = 1;
+ spec->automute_lo = 0;
+ } else if (spec->automute_lo_possible) {
+ if (spec->automute_lo)
+ return 0;
+ spec->automute_lo = 1;
+ } else
+ return -EINVAL;
+ break;
+ case 2:
+ if (!spec->automute_lo_possible || !spec->automute_speaker_possible)
+ return -EINVAL;
+ if (spec->automute_speaker && spec->automute_lo)
+ return 0;
+ spec->automute_speaker = 1;
+ spec->automute_lo = 1;
+ break;
+ default:
+ return -EINVAL;
+ }
+ call_update_outputs(codec);
+ return 1;
+}
+
+static const struct snd_kcontrol_new automute_mode_enum = {
+ .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+ .name = "Auto-Mute Mode",
+ .info = automute_mode_info,
+ .get = automute_mode_get,
+ .put = automute_mode_put,
+};
+
+static int add_automute_mode_enum(struct hda_codec *codec)
+{
+ struct hda_gen_spec *spec = codec->spec;
+
+ if (!snd_hda_gen_add_kctl(spec, NULL, &automute_mode_enum))
+ return -ENOMEM;
+ return 0;
+}
+
+/*
+ * Check the availability of HP/line-out auto-mute;
+ * Set up appropriately if really supported
+ */
+static int check_auto_mute_availability(struct hda_codec *codec)
+{
+ struct hda_gen_spec *spec = codec->spec;
+ struct auto_pin_cfg *cfg = &spec->autocfg;
+ int present = 0;
int i, err;
- if (node->checked)
+ if (spec->suppress_auto_mute)
+ return 0;
+
+ if (cfg->hp_pins[0])
+ present++;
+ if (cfg->line_out_pins[0])
+ present++;
+ if (cfg->speaker_pins[0])
+ present++;
+ if (present < 2) /* need two different output types */
return 0;
- node->checked = 1;
- if (node == dest_node) {
- /* loopback connection found */
- return 1;
+ if (!cfg->speaker_pins[0] &&
+ cfg->line_out_type == AUTO_PIN_SPEAKER_OUT) {
+ memcpy(cfg->speaker_pins, cfg->line_out_pins,
+ sizeof(cfg->speaker_pins));
+ cfg->speaker_outs = cfg->line_outs;
+ }
+
+ if (!cfg->hp_pins[0] &&
+ cfg->line_out_type == AUTO_PIN_HP_OUT) {
+ memcpy(cfg->hp_pins, cfg->line_out_pins,
+ sizeof(cfg->hp_pins));
+ cfg->hp_outs = cfg->line_outs;
}
- for (i = 0; i < node->nconns; i++) {
- struct hda_gnode *child = hda_get_node(spec, node->conn_list[i]);
- if (! child)
+ for (i = 0; i < cfg->hp_outs; i++) {
+ hda_nid_t nid = cfg->hp_pins[i];
+ if (!is_jack_detectable(codec, nid))
continue;
- err = parse_loopback_path(codec, spec, child, dest_node, type);
- if (err < 0)
- return err;
- else if (err >= 1) {
- if (err == 1) {
- err = create_mixer(codec, node, i, type,
- "Playback", 1);
- if (err < 0)
- return err;
- if (err > 0)
- return 2; /* ok, created */
- /* not created, maybe in the lower path */
- err = 1;
+ codec_dbg(codec, "Enable HP auto-muting on NID 0x%x\n", nid);
+ snd_hda_jack_detect_enable_callback(codec, nid, HDA_GEN_HP_EVENT,
+ call_hp_automute);
+ spec->detect_hp = 1;
+ }
+
+ if (cfg->line_out_type == AUTO_PIN_LINE_OUT && cfg->line_outs) {
+ if (cfg->speaker_outs)
+ for (i = 0; i < cfg->line_outs; i++) {
+ hda_nid_t nid = cfg->line_out_pins[i];
+ if (!is_jack_detectable(codec, nid))
+ continue;
+ codec_dbg(codec, "Enable Line-Out auto-muting on NID 0x%x\n", nid);
+ snd_hda_jack_detect_enable_callback(codec, nid,
+ HDA_GEN_FRONT_EVENT,
+ call_line_automute);
+ spec->detect_lo = 1;
}
- /* connect and unmute */
- if (node->nconns > 1)
- select_input_connection(codec, node, i);
- unmute_input(codec, node, i);
- unmute_output(codec, node);
+ spec->automute_lo_possible = spec->detect_hp;
+ }
+
+ spec->automute_speaker_possible = cfg->speaker_outs &&
+ (spec->detect_hp || spec->detect_lo);
+
+ spec->automute_lo = spec->automute_lo_possible;
+ spec->automute_speaker = spec->automute_speaker_possible;
+
+ if (spec->automute_speaker_possible || spec->automute_lo_possible) {
+ /* create a control for automute mode */
+ err = add_automute_mode_enum(codec);
+ if (err < 0)
return err;
- }
}
return 0;
}
+/* check whether all auto-mic pins are valid; setup indices if OK */
+static bool auto_mic_check_imux(struct hda_codec *codec)
+{
+ struct hda_gen_spec *spec = codec->spec;
+ const struct hda_input_mux *imux;
+ int i;
+
+ imux = &spec->input_mux;
+ for (i = 0; i < spec->am_num_entries; i++) {
+ spec->am_entry[i].idx =
+ find_idx_in_nid_list(spec->am_entry[i].pin,
+ spec->imux_pins, imux->num_items);
+ if (spec->am_entry[i].idx < 0)
+ return false; /* no corresponding imux */
+ }
+
+ /* we don't need the jack detection for the first pin */
+ for (i = 1; i < spec->am_num_entries; i++)
+ snd_hda_jack_detect_enable_callback(codec,
+ spec->am_entry[i].pin,
+ HDA_GEN_MIC_EVENT,
+ call_mic_autoswitch);
+ return true;
+}
+
+static int compare_attr(const void *ap, const void *bp)
+{
+ const struct automic_entry *a = ap;
+ const struct automic_entry *b = bp;
+ return (int)(a->attr - b->attr);
+}
+
/*
- * parse the tree and build the loopback controls
+ * Check the availability of auto-mic switch;
+ * Set up if really supported
*/
-static int build_loopback_controls(struct hda_codec *codec)
+static int check_auto_mic_availability(struct hda_codec *codec)
{
- struct hda_gspec *spec = codec->spec;
- struct hda_gnode *node;
- int err;
- const char *type;
+ struct hda_gen_spec *spec = codec->spec;
+ struct auto_pin_cfg *cfg = &spec->autocfg;
+ unsigned int types;
+ int i, num_pins;
- if (! spec->out_pin_node[0])
+ if (spec->suppress_auto_mic)
return 0;
- list_for_each_entry(node, &spec->nid_list, list) {
- if (node->type != AC_WID_PIN)
- continue;
- /* input capable? */
- if (! (node->pin_caps & AC_PINCAP_IN))
+ types = 0;
+ num_pins = 0;
+ for (i = 0; i < cfg->num_inputs; i++) {
+ hda_nid_t nid = cfg->inputs[i].pin;
+ unsigned int attr;
+ attr = snd_hda_codec_get_pincfg(codec, nid);
+ attr = snd_hda_get_input_pin_attr(attr);
+ if (types & (1 << attr))
+ return 0; /* already occupied */
+ switch (attr) {
+ case INPUT_PIN_ATTR_INT:
+ if (cfg->inputs[i].type != AUTO_PIN_MIC)
+ return 0; /* invalid type */
+ break;
+ case INPUT_PIN_ATTR_UNUSED:
+ return 0; /* invalid entry */
+ default:
+ if (cfg->inputs[i].type > AUTO_PIN_LINE_IN)
+ return 0; /* invalid type */
+ if (!spec->line_in_auto_switch &&
+ cfg->inputs[i].type != AUTO_PIN_MIC)
+ return 0; /* only mic is allowed */
+ if (!is_jack_detectable(codec, nid))
+ return 0; /* no unsol support */
+ break;
+ }
+ if (num_pins >= MAX_AUTO_MIC_PINS)
return 0;
- type = get_input_type(node, NULL);
- if (type) {
- if (check_existing_control(codec, type, "Playback"))
- continue;
- clear_check_flags(spec);
- err = parse_loopback_path(codec, spec,
- spec->out_pin_node[0],
- node, type);
+ types |= (1 << attr);
+ spec->am_entry[num_pins].pin = nid;
+ spec->am_entry[num_pins].attr = attr;
+ num_pins++;
+ }
+
+ if (num_pins < 2)
+ return 0;
+
+ spec->am_num_entries = num_pins;
+ /* sort the am_entry in the order of attr so that the pin with a
+ * higher attr will be selected when the jack is plugged.
+ */
+ sort(spec->am_entry, num_pins, sizeof(spec->am_entry[0]),
+ compare_attr, NULL);
+
+ if (!auto_mic_check_imux(codec))
+ return 0;
+
+ spec->auto_mic = 1;
+ spec->num_adc_nids = 1;
+ spec->cur_mux[0] = spec->am_entry[0].idx;
+ codec_dbg(codec, "Enable auto-mic switch on NID 0x%x/0x%x/0x%x\n",
+ spec->am_entry[0].pin,
+ spec->am_entry[1].pin,
+ spec->am_entry[2].pin);
+
+ return 0;
+}
+
+/* power_filter hook; make inactive widgets into power down */
+unsigned int snd_hda_gen_path_power_filter(struct hda_codec *codec,
+ hda_nid_t nid,
+ unsigned int power_state)
+{
+ if (power_state != AC_PWRST_D0 || nid == codec->afg)
+ return power_state;
+ if (get_wcaps_type(get_wcaps(codec, nid)) >= AC_WID_POWER)
+ return power_state;
+ if (is_active_nid_for_any(codec, nid))
+ return power_state;
+ return AC_PWRST_D3;
+}
+EXPORT_SYMBOL_GPL(snd_hda_gen_path_power_filter);
+
+/* mute all aamix inputs initially; parse up to the first leaves */
+static void mute_all_mixer_nid(struct hda_codec *codec, hda_nid_t mix)
+{
+ int i, nums;
+ const hda_nid_t *conn;
+ bool has_amp;
+
+ nums = snd_hda_get_conn_list(codec, mix, &conn);
+ has_amp = nid_has_mute(codec, mix, HDA_INPUT);
+ for (i = 0; i < nums; i++) {
+ if (has_amp)
+ snd_hda_codec_amp_stereo(codec, mix,
+ HDA_INPUT, i,
+ 0xff, HDA_AMP_MUTE);
+ else if (nid_has_volume(codec, conn[i], HDA_OUTPUT))
+ snd_hda_codec_amp_stereo(codec, conn[i],
+ HDA_OUTPUT, 0,
+ 0xff, HDA_AMP_MUTE);
+ }
+}
+
+/*
+ * Parse the given BIOS configuration and set up the hda_gen_spec
+ *
+ * return 1 if successful, 0 if the proper config is not found,
+ * or a negative error code
+ */
+int snd_hda_gen_parse_auto_config(struct hda_codec *codec,
+ struct auto_pin_cfg *cfg)
+{
+ struct hda_gen_spec *spec = codec->spec;
+ int err;
+
+ parse_user_hints(codec);
+
+ if (spec->mixer_nid && !spec->mixer_merge_nid)
+ spec->mixer_merge_nid = spec->mixer_nid;
+
+ if (cfg != &spec->autocfg) {
+ spec->autocfg = *cfg;
+ cfg = &spec->autocfg;
+ }
+
+ if (!spec->main_out_badness)
+ spec->main_out_badness = &hda_main_out_badness;
+ if (!spec->extra_out_badness)
+ spec->extra_out_badness = &hda_extra_out_badness;
+
+ fill_all_dac_nids(codec);
+
+ if (!cfg->line_outs) {
+ if (cfg->dig_outs || cfg->dig_in_pin) {
+ spec->multiout.max_channels = 2;
+ spec->no_analog = 1;
+ goto dig_only;
+ }
+ if (!cfg->num_inputs && !cfg->dig_in_pin)
+ return 0; /* can't find valid BIOS pin config */
+ }
+
+ if (!spec->no_primary_hp &&
+ cfg->line_out_type == AUTO_PIN_SPEAKER_OUT &&
+ cfg->line_outs <= cfg->hp_outs) {
+ /* use HP as primary out */
+ cfg->speaker_outs = cfg->line_outs;
+ memcpy(cfg->speaker_pins, cfg->line_out_pins,
+ sizeof(cfg->speaker_pins));
+ cfg->line_outs = cfg->hp_outs;
+ memcpy(cfg->line_out_pins, cfg->hp_pins, sizeof(cfg->hp_pins));
+ cfg->hp_outs = 0;
+ memset(cfg->hp_pins, 0, sizeof(cfg->hp_pins));
+ cfg->line_out_type = AUTO_PIN_HP_OUT;
+ }
+
+ err = parse_output_paths(codec);
+ if (err < 0)
+ return err;
+ err = create_multi_channel_mode(codec);
+ if (err < 0)
+ return err;
+ err = create_multi_out_ctls(codec, cfg);
+ if (err < 0)
+ return err;
+ err = create_hp_out_ctls(codec);
+ if (err < 0)
+ return err;
+ err = create_speaker_out_ctls(codec);
+ if (err < 0)
+ return err;
+ err = create_indep_hp_ctls(codec);
+ if (err < 0)
+ return err;
+ err = create_loopback_mixing_ctl(codec);
+ if (err < 0)
+ return err;
+ err = create_hp_mic(codec);
+ if (err < 0)
+ return err;
+ err = create_input_ctls(codec);
+ if (err < 0)
+ return err;
+
+ spec->const_channel_count = spec->ext_channel_count;
+ /* check the multiple speaker and headphone pins */
+ if (cfg->line_out_type != AUTO_PIN_SPEAKER_OUT)
+ spec->const_channel_count = max(spec->const_channel_count,
+ cfg->speaker_outs * 2);
+ if (cfg->line_out_type != AUTO_PIN_HP_OUT)
+ spec->const_channel_count = max(spec->const_channel_count,
+ cfg->hp_outs * 2);
+ spec->multiout.max_channels = max(spec->ext_channel_count,
+ spec->const_channel_count);
+
+ err = check_auto_mute_availability(codec);
+ if (err < 0)
+ return err;
+
+ err = check_dyn_adc_switch(codec);
+ if (err < 0)
+ return err;
+
+ err = check_auto_mic_availability(codec);
+ if (err < 0)
+ return err;
+
+ /* add stereo mix if available and not enabled yet */
+ if (!spec->auto_mic && spec->mixer_nid &&
+ spec->add_stereo_mix_input &&
+ spec->input_mux.num_items > 1 &&
+ snd_hda_get_bool_hint(codec, "add_stereo_mix_input") < 0) {
+ err = parse_capture_source(codec, spec->mixer_nid,
+ CFG_IDX_MIX, spec->num_all_adcs,
+ "Stereo Mix", 0);
+ if (err < 0)
+ return err;
+ }
+
+
+ err = create_capture_mixers(codec);
+ if (err < 0)
+ return err;
+
+ err = parse_mic_boost(codec);
+ if (err < 0)
+ return err;
+
+ /* create "Headphone Mic Jack Mode" if no input selection is
+ * available (or user specifies add_jack_modes hint)
+ */
+ if (spec->hp_mic_pin &&
+ (spec->auto_mic || spec->input_mux.num_items == 1 ||
+ spec->add_jack_modes)) {
+ err = create_hp_mic_jack_mode(codec, spec->hp_mic_pin);
+ if (err < 0)
+ return err;
+ }
+
+ if (spec->add_jack_modes) {
+ if (cfg->line_out_type != AUTO_PIN_SPEAKER_OUT) {
+ err = create_out_jack_modes(codec, cfg->line_outs,
+ cfg->line_out_pins);
+ if (err < 0)
+ return err;
+ }
+ if (cfg->line_out_type != AUTO_PIN_HP_OUT) {
+ err = create_out_jack_modes(codec, cfg->hp_outs,
+ cfg->hp_pins);
if (err < 0)
return err;
- if (! err)
- continue;
}
}
- return 0;
+
+ /* mute all aamix input initially */
+ if (spec->mixer_nid)
+ mute_all_mixer_nid(codec, spec->mixer_nid);
+
+ dig_only:
+ parse_digital(codec);
+
+ if (spec->power_down_unused)
+ codec->power_filter = snd_hda_gen_path_power_filter;
+
+ if (!spec->no_analog && spec->beep_nid) {
+ err = snd_hda_attach_beep_device(codec, spec->beep_nid);
+ if (err < 0)
+ return err;
+ }
+
+ return 1;
}
+EXPORT_SYMBOL_GPL(snd_hda_gen_parse_auto_config);
+
/*
- * build mixer controls
+ * Build control elements
*/
-static int build_generic_controls(struct hda_codec *codec)
+
+/* slave controls for virtual master */
+static const char * const slave_pfxs[] = {
+ "Front", "Surround", "Center", "LFE", "Side",
+ "Headphone", "Speaker", "Mono", "Line Out",
+ "CLFE", "Bass Speaker", "PCM",
+ "Speaker Front", "Speaker Surround", "Speaker CLFE", "Speaker Side",
+ "Headphone Front", "Headphone Surround", "Headphone CLFE",
+ "Headphone Side",
+ NULL,
+};
+
+int snd_hda_gen_build_controls(struct hda_codec *codec)
{
+ struct hda_gen_spec *spec = codec->spec;
int err;
- if ((err = build_input_controls(codec)) < 0 ||
- (err = build_output_controls(codec)) < 0 ||
- (err = build_loopback_controls(codec)) < 0)
+ if (spec->kctls.used) {
+ err = snd_hda_add_new_ctls(codec, spec->kctls.list);
+ if (err < 0)
+ return err;
+ }
+
+ if (spec->multiout.dig_out_nid) {
+ err = snd_hda_create_dig_out_ctls(codec,
+ spec->multiout.dig_out_nid,
+ spec->multiout.dig_out_nid,
+ spec->pcm_rec[1].pcm_type);
+ if (err < 0)
+ return err;
+ if (!spec->no_analog) {
+ err = snd_hda_create_spdif_share_sw(codec,
+ &spec->multiout);
+ if (err < 0)
+ return err;
+ spec->multiout.share_spdif = 1;
+ }
+ }
+ if (spec->dig_in_nid) {
+ err = snd_hda_create_spdif_in_ctls(codec, spec->dig_in_nid);
+ if (err < 0)
+ return err;
+ }
+
+ /* if we have no master control, let's create it */
+ if (!spec->no_analog &&
+ !snd_hda_find_mixer_ctl(codec, "Master Playback Volume")) {
+ err = snd_hda_add_vmaster(codec, "Master Playback Volume",
+ spec->vmaster_tlv, slave_pfxs,
+ "Playback Volume");
+ if (err < 0)
+ return err;
+ }
+ if (!spec->no_analog &&
+ !snd_hda_find_mixer_ctl(codec, "Master Playback Switch")) {
+ err = __snd_hda_add_vmaster(codec, "Master Playback Switch",
+ NULL, slave_pfxs,
+ "Playback Switch",
+ true, &spec->vmaster_mute.sw_kctl);
+ if (err < 0)
+ return err;
+ if (spec->vmaster_mute.hook) {
+ snd_hda_add_vmaster_hook(codec, &spec->vmaster_mute,
+ spec->vmaster_mute_enum);
+ snd_hda_sync_vmaster_hook(&spec->vmaster_mute);
+ }
+ }
+
+ free_kctls(spec); /* no longer needed */
+
+ err = snd_hda_jack_add_kctls(codec, &spec->autocfg);
+ if (err < 0)
return err;
return 0;
}
+EXPORT_SYMBOL_GPL(snd_hda_gen_build_controls);
+
/*
- * PCM
+ * PCM definitions
*/
-static struct hda_pcm_stream generic_pcm_playback = {
- .substreams = 1,
- .channels_min = 2,
- .channels_max = 2,
-};
-static int generic_pcm2_prepare(struct hda_pcm_stream *hinfo,
+static void call_pcm_playback_hook(struct hda_pcm_stream *hinfo,
+ struct hda_codec *codec,
+ struct snd_pcm_substream *substream,
+ int action)
+{
+ struct hda_gen_spec *spec = codec->spec;
+ if (spec->pcm_playback_hook)
+ spec->pcm_playback_hook(hinfo, codec, substream, action);
+}
+
+static void call_pcm_capture_hook(struct hda_pcm_stream *hinfo,
+ struct hda_codec *codec,
+ struct snd_pcm_substream *substream,
+ int action)
+{
+ struct hda_gen_spec *spec = codec->spec;
+ if (spec->pcm_capture_hook)
+ spec->pcm_capture_hook(hinfo, codec, substream, action);
+}
+
+/*
+ * Analog playback callbacks
+ */
+static int playback_pcm_open(struct hda_pcm_stream *hinfo,
+ struct hda_codec *codec,
+ struct snd_pcm_substream *substream)
+{
+ struct hda_gen_spec *spec = codec->spec;
+ int err;
+
+ mutex_lock(&spec->pcm_mutex);
+ err = snd_hda_multi_out_analog_open(codec,
+ &spec->multiout, substream,
+ hinfo);
+ if (!err) {
+ spec->active_streams |= 1 << STREAM_MULTI_OUT;
+ call_pcm_playback_hook(hinfo, codec, substream,
+ HDA_GEN_PCM_ACT_OPEN);
+ }
+ mutex_unlock(&spec->pcm_mutex);
+ return err;
+}
+
+static int playback_pcm_prepare(struct hda_pcm_stream *hinfo,
struct hda_codec *codec,
unsigned int stream_tag,
unsigned int format,
struct snd_pcm_substream *substream)
{
- struct hda_gspec *spec = codec->spec;
+ struct hda_gen_spec *spec = codec->spec;
+ int err;
- snd_hda_codec_setup_stream(codec, hinfo->nid, stream_tag, 0, format);
- snd_hda_codec_setup_stream(codec, spec->dac_node[1]->nid,
- stream_tag, 0, format);
- return 0;
+ err = snd_hda_multi_out_analog_prepare(codec, &spec->multiout,
+ stream_tag, format, substream);
+ if (!err)
+ call_pcm_playback_hook(hinfo, codec, substream,
+ HDA_GEN_PCM_ACT_PREPARE);
+ return err;
}
-static int generic_pcm2_cleanup(struct hda_pcm_stream *hinfo,
+static int playback_pcm_cleanup(struct hda_pcm_stream *hinfo,
struct hda_codec *codec,
struct snd_pcm_substream *substream)
{
- struct hda_gspec *spec = codec->spec;
+ struct hda_gen_spec *spec = codec->spec;
+ int err;
+
+ err = snd_hda_multi_out_analog_cleanup(codec, &spec->multiout);
+ if (!err)
+ call_pcm_playback_hook(hinfo, codec, substream,
+ HDA_GEN_PCM_ACT_CLEANUP);
+ return err;
+}
+
+static int playback_pcm_close(struct hda_pcm_stream *hinfo,
+ struct hda_codec *codec,
+ struct snd_pcm_substream *substream)
+{
+ struct hda_gen_spec *spec = codec->spec;
+ mutex_lock(&spec->pcm_mutex);
+ spec->active_streams &= ~(1 << STREAM_MULTI_OUT);
+ call_pcm_playback_hook(hinfo, codec, substream,
+ HDA_GEN_PCM_ACT_CLOSE);
+ mutex_unlock(&spec->pcm_mutex);
+ return 0;
+}
+
+static int capture_pcm_open(struct hda_pcm_stream *hinfo,
+ struct hda_codec *codec,
+ struct snd_pcm_substream *substream)
+{
+ call_pcm_capture_hook(hinfo, codec, substream, HDA_GEN_PCM_ACT_OPEN);
+ return 0;
+}
+static int capture_pcm_prepare(struct hda_pcm_stream *hinfo,
+ struct hda_codec *codec,
+ unsigned int stream_tag,
+ unsigned int format,
+ struct snd_pcm_substream *substream)
+{
+ snd_hda_codec_setup_stream(codec, hinfo->nid, stream_tag, 0, format);
+ call_pcm_capture_hook(hinfo, codec, substream,
+ HDA_GEN_PCM_ACT_PREPARE);
+ return 0;
+}
+
+static int capture_pcm_cleanup(struct hda_pcm_stream *hinfo,
+ struct hda_codec *codec,
+ struct snd_pcm_substream *substream)
+{
snd_hda_codec_cleanup_stream(codec, hinfo->nid);
- snd_hda_codec_cleanup_stream(codec, spec->dac_node[1]->nid);
+ call_pcm_capture_hook(hinfo, codec, substream,
+ HDA_GEN_PCM_ACT_CLEANUP);
return 0;
}
-static int build_generic_pcms(struct hda_codec *codec)
+static int capture_pcm_close(struct hda_pcm_stream *hinfo,
+ struct hda_codec *codec,
+ struct snd_pcm_substream *substream)
{
- struct hda_gspec *spec = codec->spec;
- struct hda_pcm *info = &spec->pcm_rec;
+ call_pcm_capture_hook(hinfo, codec, substream, HDA_GEN_PCM_ACT_CLOSE);
+ return 0;
+}
- if (! spec->dac_node[0] && ! spec->adc_node) {
- snd_printd("hda_generic: no PCM found\n");
- return 0;
+static int alt_playback_pcm_open(struct hda_pcm_stream *hinfo,
+ struct hda_codec *codec,
+ struct snd_pcm_substream *substream)
+{
+ struct hda_gen_spec *spec = codec->spec;
+ int err = 0;
+
+ mutex_lock(&spec->pcm_mutex);
+ if (!spec->indep_hp_enabled)
+ err = -EBUSY;
+ else
+ spec->active_streams |= 1 << STREAM_INDEP_HP;
+ call_pcm_playback_hook(hinfo, codec, substream,
+ HDA_GEN_PCM_ACT_OPEN);
+ mutex_unlock(&spec->pcm_mutex);
+ return err;
+}
+
+static int alt_playback_pcm_close(struct hda_pcm_stream *hinfo,
+ struct hda_codec *codec,
+ struct snd_pcm_substream *substream)
+{
+ struct hda_gen_spec *spec = codec->spec;
+ mutex_lock(&spec->pcm_mutex);
+ spec->active_streams &= ~(1 << STREAM_INDEP_HP);
+ call_pcm_playback_hook(hinfo, codec, substream,
+ HDA_GEN_PCM_ACT_CLOSE);
+ mutex_unlock(&spec->pcm_mutex);
+ return 0;
+}
+
+static int alt_playback_pcm_prepare(struct hda_pcm_stream *hinfo,
+ struct hda_codec *codec,
+ unsigned int stream_tag,
+ unsigned int format,
+ struct snd_pcm_substream *substream)
+{
+ snd_hda_codec_setup_stream(codec, hinfo->nid, stream_tag, 0, format);
+ call_pcm_playback_hook(hinfo, codec, substream,
+ HDA_GEN_PCM_ACT_PREPARE);
+ return 0;
+}
+
+static int alt_playback_pcm_cleanup(struct hda_pcm_stream *hinfo,
+ struct hda_codec *codec,
+ struct snd_pcm_substream *substream)
+{
+ snd_hda_codec_cleanup_stream(codec, hinfo->nid);
+ call_pcm_playback_hook(hinfo, codec, substream,
+ HDA_GEN_PCM_ACT_CLEANUP);
+ return 0;
+}
+
+/*
+ * Digital out
+ */
+static int dig_playback_pcm_open(struct hda_pcm_stream *hinfo,
+ struct hda_codec *codec,
+ struct snd_pcm_substream *substream)
+{
+ struct hda_gen_spec *spec = codec->spec;
+ return snd_hda_multi_out_dig_open(codec, &spec->multiout);
+}
+
+static int dig_playback_pcm_prepare(struct hda_pcm_stream *hinfo,
+ struct hda_codec *codec,
+ unsigned int stream_tag,
+ unsigned int format,
+ struct snd_pcm_substream *substream)
+{
+ struct hda_gen_spec *spec = codec->spec;
+ return snd_hda_multi_out_dig_prepare(codec, &spec->multiout,
+ stream_tag, format, substream);
+}
+
+static int dig_playback_pcm_cleanup(struct hda_pcm_stream *hinfo,
+ struct hda_codec *codec,
+ struct snd_pcm_substream *substream)
+{
+ struct hda_gen_spec *spec = codec->spec;
+ return snd_hda_multi_out_dig_cleanup(codec, &spec->multiout);
+}
+
+static int dig_playback_pcm_close(struct hda_pcm_stream *hinfo,
+ struct hda_codec *codec,
+ struct snd_pcm_substream *substream)
+{
+ struct hda_gen_spec *spec = codec->spec;
+ return snd_hda_multi_out_dig_close(codec, &spec->multiout);
+}
+
+/*
+ * Analog capture
+ */
+#define alt_capture_pcm_open capture_pcm_open
+#define alt_capture_pcm_close capture_pcm_close
+
+static int alt_capture_pcm_prepare(struct hda_pcm_stream *hinfo,
+ struct hda_codec *codec,
+ unsigned int stream_tag,
+ unsigned int format,
+ struct snd_pcm_substream *substream)
+{
+ struct hda_gen_spec *spec = codec->spec;
+
+ snd_hda_codec_setup_stream(codec, spec->adc_nids[substream->number + 1],
+ stream_tag, 0, format);
+ call_pcm_capture_hook(hinfo, codec, substream,
+ HDA_GEN_PCM_ACT_PREPARE);
+ return 0;
+}
+
+static int alt_capture_pcm_cleanup(struct hda_pcm_stream *hinfo,
+ struct hda_codec *codec,
+ struct snd_pcm_substream *substream)
+{
+ struct hda_gen_spec *spec = codec->spec;
+
+ snd_hda_codec_cleanup_stream(codec,
+ spec->adc_nids[substream->number + 1]);
+ call_pcm_capture_hook(hinfo, codec, substream,
+ HDA_GEN_PCM_ACT_CLEANUP);
+ return 0;
+}
+
+/*
+ */
+static const struct hda_pcm_stream pcm_analog_playback = {
+ .substreams = 1,
+ .channels_min = 2,
+ .channels_max = 8,
+ /* NID is set in build_pcms */
+ .ops = {
+ .open = playback_pcm_open,
+ .close = playback_pcm_close,
+ .prepare = playback_pcm_prepare,
+ .cleanup = playback_pcm_cleanup
+ },
+};
+
+static const struct hda_pcm_stream pcm_analog_capture = {
+ .substreams = 1,
+ .channels_min = 2,
+ .channels_max = 2,
+ /* NID is set in build_pcms */
+ .ops = {
+ .open = capture_pcm_open,
+ .close = capture_pcm_close,
+ .prepare = capture_pcm_prepare,
+ .cleanup = capture_pcm_cleanup
+ },
+};
+
+static const struct hda_pcm_stream pcm_analog_alt_playback = {
+ .substreams = 1,
+ .channels_min = 2,
+ .channels_max = 2,
+ /* NID is set in build_pcms */
+ .ops = {
+ .open = alt_playback_pcm_open,
+ .close = alt_playback_pcm_close,
+ .prepare = alt_playback_pcm_prepare,
+ .cleanup = alt_playback_pcm_cleanup
+ },
+};
+
+static const struct hda_pcm_stream pcm_analog_alt_capture = {
+ .substreams = 2, /* can be overridden */
+ .channels_min = 2,
+ .channels_max = 2,
+ /* NID is set in build_pcms */
+ .ops = {
+ .open = alt_capture_pcm_open,
+ .close = alt_capture_pcm_close,
+ .prepare = alt_capture_pcm_prepare,
+ .cleanup = alt_capture_pcm_cleanup
+ },
+};
+
+static const struct hda_pcm_stream pcm_digital_playback = {
+ .substreams = 1,
+ .channels_min = 2,
+ .channels_max = 2,
+ /* NID is set in build_pcms */
+ .ops = {
+ .open = dig_playback_pcm_open,
+ .close = dig_playback_pcm_close,
+ .prepare = dig_playback_pcm_prepare,
+ .cleanup = dig_playback_pcm_cleanup
+ },
+};
+
+static const struct hda_pcm_stream pcm_digital_capture = {
+ .substreams = 1,
+ .channels_min = 2,
+ .channels_max = 2,
+ /* NID is set in build_pcms */
+};
+
+/* Used by build_pcms to flag that a PCM has no playback stream */
+static const struct hda_pcm_stream pcm_null_stream = {
+ .substreams = 0,
+ .channels_min = 0,
+ .channels_max = 0,
+};
+
+/*
+ * dynamic changing ADC PCM streams
+ */
+static bool dyn_adc_pcm_resetup(struct hda_codec *codec, int cur)
+{
+ struct hda_gen_spec *spec = codec->spec;
+ hda_nid_t new_adc = spec->adc_nids[spec->dyn_adc_idx[cur]];
+
+ if (spec->cur_adc && spec->cur_adc != new_adc) {
+ /* stream is running, let's swap the current ADC */
+ __snd_hda_codec_cleanup_stream(codec, spec->cur_adc, 1);
+ spec->cur_adc = new_adc;
+ snd_hda_codec_setup_stream(codec, new_adc,
+ spec->cur_adc_stream_tag, 0,
+ spec->cur_adc_format);
+ return true;
}
+ return false;
+}
+
+/* analog capture with dynamic dual-adc changes */
+static int dyn_adc_capture_pcm_prepare(struct hda_pcm_stream *hinfo,
+ struct hda_codec *codec,
+ unsigned int stream_tag,
+ unsigned int format,
+ struct snd_pcm_substream *substream)
+{
+ struct hda_gen_spec *spec = codec->spec;
+ spec->cur_adc = spec->adc_nids[spec->dyn_adc_idx[spec->cur_mux[0]]];
+ spec->cur_adc_stream_tag = stream_tag;
+ spec->cur_adc_format = format;
+ snd_hda_codec_setup_stream(codec, spec->cur_adc, stream_tag, 0, format);
+ return 0;
+}
+
+static int dyn_adc_capture_pcm_cleanup(struct hda_pcm_stream *hinfo,
+ struct hda_codec *codec,
+ struct snd_pcm_substream *substream)
+{
+ struct hda_gen_spec *spec = codec->spec;
+ snd_hda_codec_cleanup_stream(codec, spec->cur_adc);
+ spec->cur_adc = 0;
+ return 0;
+}
+
+static const struct hda_pcm_stream dyn_adc_pcm_analog_capture = {
+ .substreams = 1,
+ .channels_min = 2,
+ .channels_max = 2,
+ .nid = 0, /* fill later */
+ .ops = {
+ .prepare = dyn_adc_capture_pcm_prepare,
+ .cleanup = dyn_adc_capture_pcm_cleanup
+ },
+};
+
+static void fill_pcm_stream_name(char *str, size_t len, const char *sfx,
+ const char *chip_name)
+{
+ char *p;
+
+ if (*str)
+ return;
+ strlcpy(str, chip_name, len);
+
+ /* drop non-alnum chars after a space */
+ for (p = strchr(str, ' '); p; p = strchr(p + 1, ' ')) {
+ if (!isalnum(p[1])) {
+ *p = 0;
+ break;
+ }
+ }
+ strlcat(str, sfx, len);
+}
+
+/* build PCM streams based on the parsed results */
+int snd_hda_gen_build_pcms(struct hda_codec *codec)
+{
+ struct hda_gen_spec *spec = codec->spec;
+ struct hda_pcm *info = spec->pcm_rec;
+ const struct hda_pcm_stream *p;
+ bool have_multi_adcs;
codec->num_pcms = 1;
codec->pcm_info = info;
- info->name = "HDA Generic";
- if (spec->dac_node[0]) {
- info->stream[0] = generic_pcm_playback;
- info->stream[0].nid = spec->dac_node[0]->nid;
- if (spec->dac_node[1]) {
- info->stream[0].ops.prepare = generic_pcm2_prepare;
- info->stream[0].ops.cleanup = generic_pcm2_cleanup;
+ if (spec->no_analog)
+ goto skip_analog;
+
+ fill_pcm_stream_name(spec->stream_name_analog,
+ sizeof(spec->stream_name_analog),
+ " Analog", codec->chip_name);
+ info->name = spec->stream_name_analog;
+
+ if (spec->multiout.num_dacs > 0) {
+ p = spec->stream_analog_playback;
+ if (!p)
+ p = &pcm_analog_playback;
+ info->stream[SNDRV_PCM_STREAM_PLAYBACK] = *p;
+ info->stream[SNDRV_PCM_STREAM_PLAYBACK].nid = spec->multiout.dac_nids[0];
+ info->stream[SNDRV_PCM_STREAM_PLAYBACK].channels_max =
+ spec->multiout.max_channels;
+ if (spec->autocfg.line_out_type == AUTO_PIN_SPEAKER_OUT &&
+ spec->autocfg.line_outs == 2)
+ info->stream[SNDRV_PCM_STREAM_PLAYBACK].chmap =
+ snd_pcm_2_1_chmaps;
+ }
+ if (spec->num_adc_nids) {
+ p = spec->stream_analog_capture;
+ if (!p) {
+ if (spec->dyn_adc_switch)
+ p = &dyn_adc_pcm_analog_capture;
+ else
+ p = &pcm_analog_capture;
+ }
+ info->stream[SNDRV_PCM_STREAM_CAPTURE] = *p;
+ info->stream[SNDRV_PCM_STREAM_CAPTURE].nid = spec->adc_nids[0];
+ }
+
+ skip_analog:
+ /* SPDIF for stream index #1 */
+ if (spec->multiout.dig_out_nid || spec->dig_in_nid) {
+ fill_pcm_stream_name(spec->stream_name_digital,
+ sizeof(spec->stream_name_digital),
+ " Digital", codec->chip_name);
+ codec->num_pcms = 2;
+ codec->slave_dig_outs = spec->multiout.slave_dig_outs;
+ info = spec->pcm_rec + 1;
+ info->name = spec->stream_name_digital;
+ if (spec->dig_out_type)
+ info->pcm_type = spec->dig_out_type;
+ else
+ info->pcm_type = HDA_PCM_TYPE_SPDIF;
+ if (spec->multiout.dig_out_nid) {
+ p = spec->stream_digital_playback;
+ if (!p)
+ p = &pcm_digital_playback;
+ info->stream[SNDRV_PCM_STREAM_PLAYBACK] = *p;
+ info->stream[SNDRV_PCM_STREAM_PLAYBACK].nid = spec->multiout.dig_out_nid;
+ }
+ if (spec->dig_in_nid) {
+ p = spec->stream_digital_capture;
+ if (!p)
+ p = &pcm_digital_capture;
+ info->stream[SNDRV_PCM_STREAM_CAPTURE] = *p;
+ info->stream[SNDRV_PCM_STREAM_CAPTURE].nid = spec->dig_in_nid;
+ }
+ }
+
+ if (spec->no_analog)
+ return 0;
+
+ /* If the use of more than one ADC is requested for the current
+ * model, configure a second analog capture-only PCM.
+ */
+ have_multi_adcs = (spec->num_adc_nids > 1) &&
+ !spec->dyn_adc_switch && !spec->auto_mic;
+ /* Additional Analaog capture for index #2 */
+ if (spec->alt_dac_nid || have_multi_adcs) {
+ fill_pcm_stream_name(spec->stream_name_alt_analog,
+ sizeof(spec->stream_name_alt_analog),
+ " Alt Analog", codec->chip_name);
+ codec->num_pcms = 3;
+ info = spec->pcm_rec + 2;
+ info->name = spec->stream_name_alt_analog;
+ if (spec->alt_dac_nid) {
+ p = spec->stream_analog_alt_playback;
+ if (!p)
+ p = &pcm_analog_alt_playback;
+ info->stream[SNDRV_PCM_STREAM_PLAYBACK] = *p;
+ info->stream[SNDRV_PCM_STREAM_PLAYBACK].nid =
+ spec->alt_dac_nid;
+ } else {
+ info->stream[SNDRV_PCM_STREAM_PLAYBACK] =
+ pcm_null_stream;
+ info->stream[SNDRV_PCM_STREAM_PLAYBACK].nid = 0;
+ }
+ if (have_multi_adcs) {
+ p = spec->stream_analog_alt_capture;
+ if (!p)
+ p = &pcm_analog_alt_capture;
+ info->stream[SNDRV_PCM_STREAM_CAPTURE] = *p;
+ info->stream[SNDRV_PCM_STREAM_CAPTURE].nid =
+ spec->adc_nids[1];
+ info->stream[SNDRV_PCM_STREAM_CAPTURE].substreams =
+ spec->num_adc_nids - 1;
+ } else {
+ info->stream[SNDRV_PCM_STREAM_CAPTURE] =
+ pcm_null_stream;
+ info->stream[SNDRV_PCM_STREAM_CAPTURE].nid = 0;
+ }
+ }
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(snd_hda_gen_build_pcms);
+
+
+/*
+ * Standard auto-parser initializations
+ */
+
+/* configure the given path as a proper output */
+static void set_output_and_unmute(struct hda_codec *codec, int path_idx)
+{
+ struct nid_path *path;
+ hda_nid_t pin;
+
+ path = snd_hda_get_path_from_idx(codec, path_idx);
+ if (!path || !path->depth)
+ return;
+ pin = path->path[path->depth - 1];
+ restore_pin_ctl(codec, pin);
+ snd_hda_activate_path(codec, path, path->active,
+ aamix_default(codec->spec));
+ set_pin_eapd(codec, pin, path->active);
+}
+
+/* initialize primary output paths */
+static void init_multi_out(struct hda_codec *codec)
+{
+ struct hda_gen_spec *spec = codec->spec;
+ int i;
+
+ for (i = 0; i < spec->autocfg.line_outs; i++)
+ set_output_and_unmute(codec, spec->out_paths[i]);
+}
+
+
+static void __init_extra_out(struct hda_codec *codec, int num_outs, int *paths)
+{
+ int i;
+
+ for (i = 0; i < num_outs; i++)
+ set_output_and_unmute(codec, paths[i]);
+}
+
+/* initialize hp and speaker paths */
+static void init_extra_out(struct hda_codec *codec)
+{
+ struct hda_gen_spec *spec = codec->spec;
+
+ if (spec->autocfg.line_out_type != AUTO_PIN_HP_OUT)
+ __init_extra_out(codec, spec->autocfg.hp_outs, spec->hp_paths);
+ if (spec->autocfg.line_out_type != AUTO_PIN_SPEAKER_OUT)
+ __init_extra_out(codec, spec->autocfg.speaker_outs,
+ spec->speaker_paths);
+}
+
+/* initialize multi-io paths */
+static void init_multi_io(struct hda_codec *codec)
+{
+ struct hda_gen_spec *spec = codec->spec;
+ int i;
+
+ for (i = 0; i < spec->multi_ios; i++) {
+ hda_nid_t pin = spec->multi_io[i].pin;
+ struct nid_path *path;
+ path = get_multiio_path(codec, i);
+ if (!path)
+ continue;
+ if (!spec->multi_io[i].ctl_in)
+ spec->multi_io[i].ctl_in =
+ snd_hda_codec_get_pin_target(codec, pin);
+ snd_hda_activate_path(codec, path, path->active,
+ aamix_default(spec));
+ }
+}
+
+static void init_aamix_paths(struct hda_codec *codec)
+{
+ struct hda_gen_spec *spec = codec->spec;
+
+ if (!spec->have_aamix_ctl)
+ return;
+ update_aamix_paths(codec, spec->aamix_mode, spec->out_paths[0],
+ spec->aamix_out_paths[0],
+ spec->autocfg.line_out_type);
+ update_aamix_paths(codec, spec->aamix_mode, spec->hp_paths[0],
+ spec->aamix_out_paths[1],
+ AUTO_PIN_HP_OUT);
+ update_aamix_paths(codec, spec->aamix_mode, spec->speaker_paths[0],
+ spec->aamix_out_paths[2],
+ AUTO_PIN_SPEAKER_OUT);
+}
+
+/* set up input pins and loopback paths */
+static void init_analog_input(struct hda_codec *codec)
+{
+ struct hda_gen_spec *spec = codec->spec;
+ struct auto_pin_cfg *cfg = &spec->autocfg;
+ int i;
+
+ for (i = 0; i < cfg->num_inputs; i++) {
+ hda_nid_t nid = cfg->inputs[i].pin;
+ if (is_input_pin(codec, nid))
+ restore_pin_ctl(codec, nid);
+
+ /* init loopback inputs */
+ if (spec->mixer_nid) {
+ resume_path_from_idx(codec, spec->loopback_paths[i]);
+ resume_path_from_idx(codec, spec->loopback_merge_path);
+ }
+ }
+}
+
+/* initialize ADC paths */
+static void init_input_src(struct hda_codec *codec)
+{
+ struct hda_gen_spec *spec = codec->spec;
+ struct hda_input_mux *imux = &spec->input_mux;
+ struct nid_path *path;
+ int i, c, nums;
+
+ if (spec->dyn_adc_switch)
+ nums = 1;
+ else
+ nums = spec->num_adc_nids;
+
+ for (c = 0; c < nums; c++) {
+ for (i = 0; i < imux->num_items; i++) {
+ path = get_input_path(codec, c, i);
+ if (path) {
+ bool active = path->active;
+ if (i == spec->cur_mux[c])
+ active = true;
+ snd_hda_activate_path(codec, path, active, false);
+ }
}
+ if (spec->hp_mic)
+ update_hp_mic(codec, c, true);
+ }
+
+ if (spec->cap_sync_hook)
+ spec->cap_sync_hook(codec, NULL, NULL);
+}
+
+/* set right pin controls for digital I/O */
+static void init_digital(struct hda_codec *codec)
+{
+ struct hda_gen_spec *spec = codec->spec;
+ int i;
+ hda_nid_t pin;
+
+ for (i = 0; i < spec->autocfg.dig_outs; i++)
+ set_output_and_unmute(codec, spec->digout_paths[i]);
+ pin = spec->autocfg.dig_in_pin;
+ if (pin) {
+ restore_pin_ctl(codec, pin);
+ resume_path_from_idx(codec, spec->digin_path);
}
- if (spec->adc_node) {
- info->stream[1] = generic_pcm_playback;
- info->stream[1].nid = spec->adc_node->nid;
+}
+
+/* clear unsol-event tags on unused pins; Conexant codecs seem to leave
+ * invalid unsol tags by some reason
+ */
+static void clear_unsol_on_unused_pins(struct hda_codec *codec)
+{
+ int i;
+
+ for (i = 0; i < codec->init_pins.used; i++) {
+ struct hda_pincfg *pin = snd_array_elem(&codec->init_pins, i);
+ hda_nid_t nid = pin->nid;
+ if (is_jack_detectable(codec, nid) &&
+ !snd_hda_jack_tbl_get(codec, nid))
+ snd_hda_codec_update_cache(codec, nid, 0,
+ AC_VERB_SET_UNSOLICITED_ENABLE, 0);
}
+}
+
+/*
+ * initialize the generic spec;
+ * this can be put as patch_ops.init function
+ */
+int snd_hda_gen_init(struct hda_codec *codec)
+{
+ struct hda_gen_spec *spec = codec->spec;
+
+ if (spec->init_hook)
+ spec->init_hook(codec);
+
+ snd_hda_apply_verbs(codec);
+
+ codec->cached_write = 1;
+ init_multi_out(codec);
+ init_extra_out(codec);
+ init_multi_io(codec);
+ init_aamix_paths(codec);
+ init_analog_input(codec);
+ init_input_src(codec);
+ init_digital(codec);
+
+ clear_unsol_on_unused_pins(codec);
+
+ /* call init functions of standard auto-mute helpers */
+ update_automute_all(codec);
+
+ snd_hda_codec_flush_cache(codec);
+
+ if (spec->vmaster_mute.sw_kctl && spec->vmaster_mute.hook)
+ snd_hda_sync_vmaster_hook(&spec->vmaster_mute);
+
+ hda_call_check_power_status(codec, 0x01);
return 0;
}
+EXPORT_SYMBOL_GPL(snd_hda_gen_init);
-#ifdef CONFIG_SND_HDA_POWER_SAVE
-static int generic_check_power_status(struct hda_codec *codec, hda_nid_t nid)
+/*
+ * free the generic spec;
+ * this can be put as patch_ops.free function
+ */
+void snd_hda_gen_free(struct hda_codec *codec)
{
- struct hda_gspec *spec = codec->spec;
+ snd_hda_apply_fixup(codec, HDA_FIXUP_ACT_FREE);
+ snd_hda_gen_spec_free(codec->spec);
+ kfree(codec->spec);
+ codec->spec = NULL;
+}
+EXPORT_SYMBOL_GPL(snd_hda_gen_free);
+
+#ifdef CONFIG_PM
+/*
+ * check the loopback power save state;
+ * this can be put as patch_ops.check_power_status function
+ */
+int snd_hda_gen_check_power_status(struct hda_codec *codec, hda_nid_t nid)
+{
+ struct hda_gen_spec *spec = codec->spec;
return snd_hda_check_amp_list_power(codec, &spec->loopback, nid);
}
+EXPORT_SYMBOL_GPL(snd_hda_gen_check_power_status);
#endif
/*
+ * the generic codec support
*/
-static struct hda_codec_ops generic_patch_ops = {
- .build_controls = build_generic_controls,
- .build_pcms = build_generic_pcms,
- .free = snd_hda_generic_free,
-#ifdef CONFIG_SND_HDA_POWER_SAVE
- .check_power_status = generic_check_power_status,
+
+static const struct hda_codec_ops generic_patch_ops = {
+ .build_controls = snd_hda_gen_build_controls,
+ .build_pcms = snd_hda_gen_build_pcms,
+ .init = snd_hda_gen_init,
+ .free = snd_hda_gen_free,
+ .unsol_event = snd_hda_jack_unsol_event,
+#ifdef CONFIG_PM
+ .check_power_status = snd_hda_gen_check_power_status,
#endif
};
-/*
- * the generic parser
- */
int snd_hda_parse_generic_codec(struct hda_codec *codec)
{
- struct hda_gspec *spec;
+ struct hda_gen_spec *spec;
int err;
- if(!codec->afg)
- return 0;
-
spec = kzalloc(sizeof(*spec), GFP_KERNEL);
- if (spec == NULL) {
- printk(KERN_ERR "hda_generic: can't allocate spec\n");
+ if (!spec)
return -ENOMEM;
- }
+ snd_hda_gen_spec_init(spec);
codec->spec = spec;
- INIT_LIST_HEAD(&spec->nid_list);
- if ((err = build_afg_tree(codec)) < 0)
- goto error;
+ err = snd_hda_parse_pin_defcfg(codec, &spec->autocfg, NULL, 0);
+ if (err < 0)
+ return err;
- if ((err = parse_input(codec)) < 0 ||
- (err = parse_output(codec)) < 0)
+ err = snd_hda_gen_parse_auto_config(codec, &spec->autocfg);
+ if (err < 0)
goto error;
codec->patch_ops = generic_patch_ops;
-
return 0;
- error:
- snd_hda_generic_free(codec);
+error:
+ snd_hda_gen_free(codec);
return err;
}
-EXPORT_SYMBOL(snd_hda_parse_generic_codec);
+EXPORT_SYMBOL_GPL(snd_hda_parse_generic_codec);
+
+MODULE_LICENSE("GPL");
+MODULE_DESCRIPTION("Generic HD-audio codec parser");
diff --git a/sound/pci/hda/hda_generic.h b/sound/pci/hda/hda_generic.h
new file mode 100644
index 00000000000..bb2dea74398
--- /dev/null
+++ b/sound/pci/hda/hda_generic.h
@@ -0,0 +1,342 @@
+/*
+ * Generic BIOS auto-parser helper functions for HD-audio
+ *
+ * Copyright (c) 2012 Takashi Iwai <tiwai@suse.de>
+ *
+ * This driver is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ */
+
+#ifndef __SOUND_HDA_GENERIC_H
+#define __SOUND_HDA_GENERIC_H
+
+/* unsol event tags */
+enum {
+ HDA_GEN_HP_EVENT = 1, HDA_GEN_FRONT_EVENT, HDA_GEN_MIC_EVENT,
+ HDA_GEN_LAST_EVENT = HDA_GEN_MIC_EVENT
+};
+
+/* table entry for multi-io paths */
+struct hda_multi_io {
+ hda_nid_t pin; /* multi-io widget pin NID */
+ hda_nid_t dac; /* DAC to be connected */
+ unsigned int ctl_in; /* cached input-pin control value */
+};
+
+/* Widget connection path
+ *
+ * For output, stored in the order of DAC -> ... -> pin,
+ * for input, pin -> ... -> ADC.
+ *
+ * idx[i] contains the source index number to select on of the widget path[i];
+ * e.g. idx[1] is the index of the DAC (path[0]) selected by path[1] widget
+ * multi[] indicates whether it's a selector widget with multi-connectors
+ * (i.e. the connection selection is mandatory)
+ * vol_ctl and mute_ctl contains the NIDs for the assigned mixers
+ */
+
+#define MAX_NID_PATH_DEPTH 10
+
+enum {
+ NID_PATH_VOL_CTL,
+ NID_PATH_MUTE_CTL,
+ NID_PATH_BOOST_CTL,
+ NID_PATH_NUM_CTLS
+};
+
+struct nid_path {
+ int depth;
+ hda_nid_t path[MAX_NID_PATH_DEPTH];
+ unsigned char idx[MAX_NID_PATH_DEPTH];
+ unsigned char multi[MAX_NID_PATH_DEPTH];
+ unsigned int ctls[NID_PATH_NUM_CTLS]; /* NID_PATH_XXX_CTL */
+ bool active;
+};
+
+/* mic/line-in auto switching entry */
+
+#define MAX_AUTO_MIC_PINS 3
+
+struct automic_entry {
+ hda_nid_t pin; /* pin */
+ int idx; /* imux index, -1 = invalid */
+ unsigned int attr; /* pin attribute (INPUT_PIN_ATTR_*) */
+};
+
+/* active stream id */
+enum { STREAM_MULTI_OUT, STREAM_INDEP_HP };
+
+/* PCM hook action */
+enum {
+ HDA_GEN_PCM_ACT_OPEN,
+ HDA_GEN_PCM_ACT_PREPARE,
+ HDA_GEN_PCM_ACT_CLEANUP,
+ HDA_GEN_PCM_ACT_CLOSE,
+};
+
+/* DAC assignment badness table */
+struct badness_table {
+ int no_primary_dac; /* no primary DAC */
+ int no_dac; /* no secondary DACs */
+ int shared_primary; /* primary DAC is shared with main output */
+ int shared_surr; /* secondary DAC shared with main or primary */
+ int shared_clfe; /* third DAC shared with main or primary */
+ int shared_surr_main; /* secondary DAC sahred with main/DAC0 */
+};
+
+extern const struct badness_table hda_main_out_badness;
+extern const struct badness_table hda_extra_out_badness;
+
+struct hda_gen_spec {
+ char stream_name_analog[32]; /* analog PCM stream */
+ const struct hda_pcm_stream *stream_analog_playback;
+ const struct hda_pcm_stream *stream_analog_capture;
+
+ char stream_name_alt_analog[32]; /* alternative analog PCM stream */
+ const struct hda_pcm_stream *stream_analog_alt_playback;
+ const struct hda_pcm_stream *stream_analog_alt_capture;
+
+ char stream_name_digital[32]; /* digital PCM stream */
+ const struct hda_pcm_stream *stream_digital_playback;
+ const struct hda_pcm_stream *stream_digital_capture;
+
+ /* PCM */
+ unsigned int active_streams;
+ struct mutex pcm_mutex;
+
+ /* playback */
+ struct hda_multi_out multiout; /* playback set-up
+ * max_channels, dacs must be set
+ * dig_out_nid and hp_nid are optional
+ */
+ hda_nid_t alt_dac_nid;
+ hda_nid_t slave_dig_outs[3]; /* optional - for auto-parsing */
+ int dig_out_type;
+
+ /* capture */
+ unsigned int num_adc_nids;
+ hda_nid_t adc_nids[AUTO_CFG_MAX_INS];
+ hda_nid_t dig_in_nid; /* digital-in NID; optional */
+ hda_nid_t mixer_nid; /* analog-mixer NID */
+ hda_nid_t mixer_merge_nid; /* aamix merge-point NID (optional) */
+ const char *input_labels[HDA_MAX_NUM_INPUTS];
+ int input_label_idxs[HDA_MAX_NUM_INPUTS];
+
+ /* capture setup for dynamic dual-adc switch */
+ hda_nid_t cur_adc;
+ unsigned int cur_adc_stream_tag;
+ unsigned int cur_adc_format;
+
+ /* capture source */
+ struct hda_input_mux input_mux;
+ unsigned int cur_mux[3];
+
+ /* channel model */
+ /* min_channel_count contains the minimum channel count for primary
+ * outputs. When multi_ios is set, the channels can be configured
+ * between min_channel_count and (min_channel_count + multi_ios * 2).
+ *
+ * ext_channel_count contains the current channel count of the primary
+ * out. This varies in the range above.
+ *
+ * Meanwhile, const_channel_count is the channel count for all outputs
+ * including headphone and speakers. It's a constant value, and the
+ * PCM is set up as max(ext_channel_count, const_channel_count).
+ */
+ int min_channel_count; /* min. channel count for primary out */
+ int ext_channel_count; /* current channel count for primary */
+ int const_channel_count; /* channel count for all */
+
+ /* PCM information */
+ struct hda_pcm pcm_rec[3]; /* used in build_pcms() */
+
+ /* dynamic controls, init_verbs and input_mux */
+ struct auto_pin_cfg autocfg;
+ struct snd_array kctls;
+ hda_nid_t private_dac_nids[AUTO_CFG_MAX_OUTS];
+ hda_nid_t imux_pins[HDA_MAX_NUM_INPUTS];
+ unsigned int dyn_adc_idx[HDA_MAX_NUM_INPUTS];
+ /* shared hp/mic */
+ hda_nid_t shared_mic_vref_pin;
+ hda_nid_t hp_mic_pin;
+ int hp_mic_mux_idx;
+
+ /* DAC/ADC lists */
+ int num_all_dacs;
+ hda_nid_t all_dacs[16];
+ int num_all_adcs;
+ hda_nid_t all_adcs[AUTO_CFG_MAX_INS];
+
+ /* path list */
+ struct snd_array paths;
+
+ /* path indices */
+ int out_paths[AUTO_CFG_MAX_OUTS];
+ int hp_paths[AUTO_CFG_MAX_OUTS];
+ int speaker_paths[AUTO_CFG_MAX_OUTS];
+ int aamix_out_paths[3];
+ int digout_paths[AUTO_CFG_MAX_OUTS];
+ int input_paths[HDA_MAX_NUM_INPUTS][AUTO_CFG_MAX_INS];
+ int loopback_paths[HDA_MAX_NUM_INPUTS];
+ int loopback_merge_path;
+ int digin_path;
+
+ /* auto-mic stuff */
+ int am_num_entries;
+ struct automic_entry am_entry[MAX_AUTO_MIC_PINS];
+
+ /* for pin sensing */
+ /* current status; set in hda_geneic.c */
+ unsigned int hp_jack_present:1;
+ unsigned int line_jack_present:1;
+ unsigned int speaker_muted:1; /* current status of speaker mute */
+ unsigned int line_out_muted:1; /* current status of LO mute */
+
+ /* internal states of automute / autoswitch behavior */
+ unsigned int auto_mic:1;
+ unsigned int automute_speaker:1; /* automute speaker outputs */
+ unsigned int automute_lo:1; /* automute LO outputs */
+
+ /* capabilities detected by parser */
+ unsigned int detect_hp:1; /* Headphone detection enabled */
+ unsigned int detect_lo:1; /* Line-out detection enabled */
+ unsigned int automute_speaker_possible:1; /* there are speakers and either LO or HP */
+ unsigned int automute_lo_possible:1; /* there are line outs and HP */
+
+ /* additional parameters set by codec drivers */
+ unsigned int master_mute:1; /* master mute over all */
+ unsigned int keep_vref_in_automute:1; /* Don't clear VREF in automute */
+ unsigned int line_in_auto_switch:1; /* allow line-in auto switch */
+ unsigned int auto_mute_via_amp:1; /* auto-mute via amp instead of pinctl */
+
+ /* parser behavior flags; set before snd_hda_gen_parse_auto_config() */
+ unsigned int suppress_auto_mute:1; /* suppress input jack auto mute */
+ unsigned int suppress_auto_mic:1; /* suppress input jack auto switch */
+
+ /* other parse behavior flags */
+ unsigned int need_dac_fix:1; /* need to limit DACs for multi channels */
+ unsigned int hp_mic:1; /* Allow HP as a mic-in */
+ unsigned int suppress_hp_mic_detect:1; /* Don't detect HP/mic */
+ unsigned int no_primary_hp:1; /* Don't prefer HP pins to speaker pins */
+ unsigned int no_multi_io:1; /* Don't try multi I/O config */
+ unsigned int multi_cap_vol:1; /* allow multiple capture xxx volumes */
+ unsigned int inv_dmic_split:1; /* inverted dmic w/a for conexant */
+ unsigned int own_eapd_ctl:1; /* set EAPD by own function */
+ unsigned int keep_eapd_on:1; /* don't turn off EAPD automatically */
+ unsigned int vmaster_mute_enum:1; /* add vmaster mute mode enum */
+ unsigned int indep_hp:1; /* independent HP supported */
+ unsigned int prefer_hp_amp:1; /* enable HP amp for speaker if any */
+ unsigned int add_stereo_mix_input:1; /* add aamix as a capture src */
+ unsigned int add_jack_modes:1; /* add i/o jack mode enum ctls */
+ unsigned int power_down_unused:1; /* power down unused widgets */
+
+ /* other internal flags */
+ unsigned int no_analog:1; /* digital I/O only */
+ unsigned int dyn_adc_switch:1; /* switch ADCs (for ALC275) */
+ unsigned int indep_hp_enabled:1; /* independent HP enabled */
+ unsigned int have_aamix_ctl:1;
+ unsigned int hp_mic_jack_modes:1;
+
+ /* additional mute flags (only effective with auto_mute_via_amp=1) */
+ u64 mute_bits;
+
+ /* bitmask for skipping volume controls */
+ u64 out_vol_mask;
+
+ /* badness tables for output path evaluations */
+ const struct badness_table *main_out_badness;
+ const struct badness_table *extra_out_badness;
+
+ /* preferred pin/DAC pairs; an array of paired NIDs */
+ const hda_nid_t *preferred_dacs;
+
+ /* loopback mixing mode */
+ bool aamix_mode;
+
+ /* digital beep */
+ hda_nid_t beep_nid;
+
+ /* for virtual master */
+ hda_nid_t vmaster_nid;
+ unsigned int vmaster_tlv[4];
+ struct hda_vmaster_mute_hook vmaster_mute;
+
+ struct hda_loopback_check loopback;
+ struct snd_array loopback_list;
+
+ /* multi-io */
+ int multi_ios;
+ struct hda_multi_io multi_io[4];
+
+ /* hooks */
+ void (*init_hook)(struct hda_codec *codec);
+ void (*automute_hook)(struct hda_codec *codec);
+ void (*cap_sync_hook)(struct hda_codec *codec,
+ struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol);
+
+ /* PCM hooks */
+ void (*pcm_playback_hook)(struct hda_pcm_stream *hinfo,
+ struct hda_codec *codec,
+ struct snd_pcm_substream *substream,
+ int action);
+ void (*pcm_capture_hook)(struct hda_pcm_stream *hinfo,
+ struct hda_codec *codec,
+ struct snd_pcm_substream *substream,
+ int action);
+
+ /* automute / autoswitch hooks */
+ void (*hp_automute_hook)(struct hda_codec *codec,
+ struct hda_jack_tbl *tbl);
+ void (*line_automute_hook)(struct hda_codec *codec,
+ struct hda_jack_tbl *tbl);
+ void (*mic_autoswitch_hook)(struct hda_codec *codec,
+ struct hda_jack_tbl *tbl);
+};
+
+int snd_hda_gen_spec_init(struct hda_gen_spec *spec);
+
+int snd_hda_gen_init(struct hda_codec *codec);
+void snd_hda_gen_free(struct hda_codec *codec);
+
+struct nid_path *snd_hda_get_nid_path(struct hda_codec *codec,
+ hda_nid_t from_nid, hda_nid_t to_nid);
+int snd_hda_get_path_idx(struct hda_codec *codec, struct nid_path *path);
+struct nid_path *snd_hda_get_path_from_idx(struct hda_codec *codec, int idx);
+bool snd_hda_parse_nid_path(struct hda_codec *codec, hda_nid_t from_nid,
+ hda_nid_t to_nid, int anchor_nid,
+ struct nid_path *path);
+struct nid_path *
+snd_hda_add_new_path(struct hda_codec *codec, hda_nid_t from_nid,
+ hda_nid_t to_nid, int anchor_nid);
+void snd_hda_activate_path(struct hda_codec *codec, struct nid_path *path,
+ bool enable, bool add_aamix);
+
+struct snd_kcontrol_new *
+snd_hda_gen_add_kctl(struct hda_gen_spec *spec, const char *name,
+ const struct snd_kcontrol_new *temp);
+
+int snd_hda_gen_parse_auto_config(struct hda_codec *codec,
+ struct auto_pin_cfg *cfg);
+int snd_hda_gen_build_controls(struct hda_codec *codec);
+int snd_hda_gen_build_pcms(struct hda_codec *codec);
+
+/* standard jack event callbacks */
+void snd_hda_gen_hp_automute(struct hda_codec *codec,
+ struct hda_jack_tbl *jack);
+void snd_hda_gen_line_automute(struct hda_codec *codec,
+ struct hda_jack_tbl *jack);
+void snd_hda_gen_mic_autoswitch(struct hda_codec *codec,
+ struct hda_jack_tbl *jack);
+void snd_hda_gen_update_outputs(struct hda_codec *codec);
+
+#ifdef CONFIG_PM
+int snd_hda_gen_check_power_status(struct hda_codec *codec, hda_nid_t nid);
+#endif
+unsigned int snd_hda_gen_path_power_filter(struct hda_codec *codec,
+ hda_nid_t nid,
+ unsigned int power_state);
+
+#endif /* __SOUND_HDA_GENERIC_H */
diff --git a/sound/pci/hda/hda_hwdep.c b/sound/pci/hda/hda_hwdep.c
index bf3ced51e0f..014a7849e8f 100644
--- a/sound/pci/hda/hda_hwdep.c
+++ b/sound/pci/hda/hda_hwdep.c
@@ -20,24 +20,13 @@
#include <linux/init.h>
#include <linux/slab.h>
-#include <linux/pci.h>
#include <linux/compat.h>
-#include <linux/mutex.h>
-#include <linux/ctype.h>
-#include <linux/string.h>
-#include <linux/firmware.h>
#include <sound/core.h>
#include "hda_codec.h"
#include "hda_local.h"
#include <sound/hda_hwdep.h>
#include <sound/minors.h>
-/* hint string pair */
-struct hda_hint {
- const char *key;
- const char *val; /* contained in the same alloc as key */
-};
-
/*
* write/read an out-of-bound verb
*/
@@ -105,27 +94,7 @@ static int hda_hwdep_open(struct snd_hwdep *hw, struct file *file)
return 0;
}
-static void clear_hwdep_elements(struct hda_codec *codec)
-{
- int i;
-
- /* clear init verbs */
- snd_array_free(&codec->init_verbs);
- /* clear hints */
- for (i = 0; i < codec->hints.used; i++) {
- struct hda_hint *hint = snd_array_elem(&codec->hints, i);
- kfree(hint->key); /* we don't need to free hint->val */
- }
- snd_array_free(&codec->hints);
- snd_array_free(&codec->user_pins);
-}
-
-static void hwdep_free(struct snd_hwdep *hwdep)
-{
- clear_hwdep_elements(hwdep->private_data);
-}
-
-int /*__devinit*/ snd_hda_create_hwdep(struct hda_codec *codec)
+int snd_hda_create_hwdep(struct hda_codec *codec)
{
char hwname[16];
struct snd_hwdep *hwdep;
@@ -139,8 +108,8 @@ int /*__devinit*/ snd_hda_create_hwdep(struct hda_codec *codec)
sprintf(hwdep->name, "HDA Codec %d", codec->addr);
hwdep->iface = SNDRV_HWDEP_IFACE_HDA;
hwdep->private_data = codec;
- hwdep->private_free = hwdep_free;
hwdep->exclusive = 1;
+ hwdep->groups = snd_hda_dev_attr_groups;
hwdep->ops.open = hda_hwdep_open;
hwdep->ops.ioctl = hda_hwdep_ioctl;
@@ -148,671 +117,8 @@ int /*__devinit*/ snd_hda_create_hwdep(struct hda_codec *codec)
hwdep->ops.ioctl_compat = hda_hwdep_ioctl_compat;
#endif
- snd_array_init(&codec->init_verbs, sizeof(struct hda_verb), 32);
- snd_array_init(&codec->hints, sizeof(struct hda_hint), 32);
- snd_array_init(&codec->user_pins, sizeof(struct hda_pincfg), 16);
-
- return 0;
-}
-
-#ifdef CONFIG_SND_HDA_POWER_SAVE
-static ssize_t power_on_acct_show(struct device *dev,
- struct device_attribute *attr,
- char *buf)
-{
- struct snd_hwdep *hwdep = dev_get_drvdata(dev);
- struct hda_codec *codec = hwdep->private_data;
- snd_hda_update_power_acct(codec);
- return sprintf(buf, "%u\n", jiffies_to_msecs(codec->power_on_acct));
-}
+ /* link to codec */
+ hwdep->dev = &codec->dev;
-static ssize_t power_off_acct_show(struct device *dev,
- struct device_attribute *attr,
- char *buf)
-{
- struct snd_hwdep *hwdep = dev_get_drvdata(dev);
- struct hda_codec *codec = hwdep->private_data;
- snd_hda_update_power_acct(codec);
- return sprintf(buf, "%u\n", jiffies_to_msecs(codec->power_off_acct));
-}
-
-static struct device_attribute power_attrs[] = {
- __ATTR_RO(power_on_acct),
- __ATTR_RO(power_off_acct),
-};
-
-int snd_hda_hwdep_add_power_sysfs(struct hda_codec *codec)
-{
- struct snd_hwdep *hwdep = codec->hwdep;
- int i;
-
- for (i = 0; i < ARRAY_SIZE(power_attrs); i++)
- snd_add_device_sysfs_file(SNDRV_DEVICE_TYPE_HWDEP, hwdep->card,
- hwdep->device, &power_attrs[i]);
- return 0;
-}
-#endif /* CONFIG_SND_HDA_POWER_SAVE */
-
-#ifdef CONFIG_SND_HDA_RECONFIG
-
-/*
- * sysfs interface
- */
-
-static int clear_codec(struct hda_codec *codec)
-{
- int err;
-
- err = snd_hda_codec_reset(codec);
- if (err < 0) {
- snd_printk(KERN_ERR "The codec is being used, can't free.\n");
- return err;
- }
- clear_hwdep_elements(codec);
- return 0;
-}
-
-static int reconfig_codec(struct hda_codec *codec)
-{
- int err;
-
- snd_hda_power_up(codec);
- snd_printk(KERN_INFO "hda-codec: reconfiguring\n");
- err = snd_hda_codec_reset(codec);
- if (err < 0) {
- snd_printk(KERN_ERR
- "The codec is being used, can't reconfigure.\n");
- goto error;
- }
- err = snd_hda_codec_configure(codec);
- if (err < 0)
- goto error;
- /* rebuild PCMs */
- err = snd_hda_codec_build_pcms(codec);
- if (err < 0)
- goto error;
- /* rebuild mixers */
- err = snd_hda_codec_build_controls(codec);
- if (err < 0)
- goto error;
- err = snd_card_register(codec->bus->card);
- error:
- snd_hda_power_down(codec);
- return err;
-}
-
-/*
- * allocate a string at most len chars, and remove the trailing EOL
- */
-static char *kstrndup_noeol(const char *src, size_t len)
-{
- char *s = kstrndup(src, len, GFP_KERNEL);
- char *p;
- if (!s)
- return NULL;
- p = strchr(s, '\n');
- if (p)
- *p = 0;
- return s;
-}
-
-#define CODEC_INFO_SHOW(type) \
-static ssize_t type##_show(struct device *dev, \
- struct device_attribute *attr, \
- char *buf) \
-{ \
- struct snd_hwdep *hwdep = dev_get_drvdata(dev); \
- struct hda_codec *codec = hwdep->private_data; \
- return sprintf(buf, "0x%x\n", codec->type); \
-}
-
-#define CODEC_INFO_STR_SHOW(type) \
-static ssize_t type##_show(struct device *dev, \
- struct device_attribute *attr, \
- char *buf) \
-{ \
- struct snd_hwdep *hwdep = dev_get_drvdata(dev); \
- struct hda_codec *codec = hwdep->private_data; \
- return sprintf(buf, "%s\n", \
- codec->type ? codec->type : ""); \
-}
-
-CODEC_INFO_SHOW(vendor_id);
-CODEC_INFO_SHOW(subsystem_id);
-CODEC_INFO_SHOW(revision_id);
-CODEC_INFO_SHOW(afg);
-CODEC_INFO_SHOW(mfg);
-CODEC_INFO_STR_SHOW(vendor_name);
-CODEC_INFO_STR_SHOW(chip_name);
-CODEC_INFO_STR_SHOW(modelname);
-
-#define CODEC_INFO_STORE(type) \
-static ssize_t type##_store(struct device *dev, \
- struct device_attribute *attr, \
- const char *buf, size_t count) \
-{ \
- struct snd_hwdep *hwdep = dev_get_drvdata(dev); \
- struct hda_codec *codec = hwdep->private_data; \
- unsigned long val; \
- int err = strict_strtoul(buf, 0, &val); \
- if (err < 0) \
- return err; \
- codec->type = val; \
- return count; \
-}
-
-#define CODEC_INFO_STR_STORE(type) \
-static ssize_t type##_store(struct device *dev, \
- struct device_attribute *attr, \
- const char *buf, size_t count) \
-{ \
- struct snd_hwdep *hwdep = dev_get_drvdata(dev); \
- struct hda_codec *codec = hwdep->private_data; \
- char *s = kstrndup_noeol(buf, 64); \
- if (!s) \
- return -ENOMEM; \
- kfree(codec->type); \
- codec->type = s; \
- return count; \
-}
-
-CODEC_INFO_STORE(vendor_id);
-CODEC_INFO_STORE(subsystem_id);
-CODEC_INFO_STORE(revision_id);
-CODEC_INFO_STR_STORE(vendor_name);
-CODEC_INFO_STR_STORE(chip_name);
-CODEC_INFO_STR_STORE(modelname);
-
-#define CODEC_ACTION_STORE(type) \
-static ssize_t type##_store(struct device *dev, \
- struct device_attribute *attr, \
- const char *buf, size_t count) \
-{ \
- struct snd_hwdep *hwdep = dev_get_drvdata(dev); \
- struct hda_codec *codec = hwdep->private_data; \
- int err = 0; \
- if (*buf) \
- err = type##_codec(codec); \
- return err < 0 ? err : count; \
-}
-
-CODEC_ACTION_STORE(reconfig);
-CODEC_ACTION_STORE(clear);
-
-static ssize_t init_verbs_show(struct device *dev,
- struct device_attribute *attr,
- char *buf)
-{
- struct snd_hwdep *hwdep = dev_get_drvdata(dev);
- struct hda_codec *codec = hwdep->private_data;
- int i, len = 0;
- for (i = 0; i < codec->init_verbs.used; i++) {
- struct hda_verb *v = snd_array_elem(&codec->init_verbs, i);
- len += snprintf(buf + len, PAGE_SIZE - len,
- "0x%02x 0x%03x 0x%04x\n",
- v->nid, v->verb, v->param);
- }
- return len;
-}
-
-static int parse_init_verbs(struct hda_codec *codec, const char *buf)
-{
- struct hda_verb *v;
- int nid, verb, param;
-
- if (sscanf(buf, "%i %i %i", &nid, &verb, &param) != 3)
- return -EINVAL;
- if (!nid || !verb)
- return -EINVAL;
- v = snd_array_new(&codec->init_verbs);
- if (!v)
- return -ENOMEM;
- v->nid = nid;
- v->verb = verb;
- v->param = param;
- return 0;
-}
-
-static ssize_t init_verbs_store(struct device *dev,
- struct device_attribute *attr,
- const char *buf, size_t count)
-{
- struct snd_hwdep *hwdep = dev_get_drvdata(dev);
- struct hda_codec *codec = hwdep->private_data;
- int err = parse_init_verbs(codec, buf);
- if (err < 0)
- return err;
- return count;
-}
-
-static ssize_t hints_show(struct device *dev,
- struct device_attribute *attr,
- char *buf)
-{
- struct snd_hwdep *hwdep = dev_get_drvdata(dev);
- struct hda_codec *codec = hwdep->private_data;
- int i, len = 0;
- for (i = 0; i < codec->hints.used; i++) {
- struct hda_hint *hint = snd_array_elem(&codec->hints, i);
- len += snprintf(buf + len, PAGE_SIZE - len,
- "%s = %s\n", hint->key, hint->val);
- }
- return len;
-}
-
-static struct hda_hint *get_hint(struct hda_codec *codec, const char *key)
-{
- int i;
-
- for (i = 0; i < codec->hints.used; i++) {
- struct hda_hint *hint = snd_array_elem(&codec->hints, i);
- if (!strcmp(hint->key, key))
- return hint;
- }
- return NULL;
-}
-
-static void remove_trail_spaces(char *str)
-{
- char *p;
- if (!*str)
- return;
- p = str + strlen(str) - 1;
- for (; isspace(*p); p--) {
- *p = 0;
- if (p == str)
- return;
- }
-}
-
-#define MAX_HINTS 1024
-
-static int parse_hints(struct hda_codec *codec, const char *buf)
-{
- char *key, *val;
- struct hda_hint *hint;
-
- buf = skip_spaces(buf);
- if (!*buf || *buf == '#' || *buf == '\n')
- return 0;
- if (*buf == '=')
- return -EINVAL;
- key = kstrndup_noeol(buf, 1024);
- if (!key)
- return -ENOMEM;
- /* extract key and val */
- val = strchr(key, '=');
- if (!val) {
- kfree(key);
- return -EINVAL;
- }
- *val++ = 0;
- val = skip_spaces(val);
- remove_trail_spaces(key);
- remove_trail_spaces(val);
- hint = get_hint(codec, key);
- if (hint) {
- /* replace */
- kfree(hint->key);
- hint->key = key;
- hint->val = val;
- return 0;
- }
- /* allocate a new hint entry */
- if (codec->hints.used >= MAX_HINTS)
- hint = NULL;
- else
- hint = snd_array_new(&codec->hints);
- if (!hint) {
- kfree(key);
- return -ENOMEM;
- }
- hint->key = key;
- hint->val = val;
- return 0;
-}
-
-static ssize_t hints_store(struct device *dev,
- struct device_attribute *attr,
- const char *buf, size_t count)
-{
- struct snd_hwdep *hwdep = dev_get_drvdata(dev);
- struct hda_codec *codec = hwdep->private_data;
- int err = parse_hints(codec, buf);
- if (err < 0)
- return err;
- return count;
-}
-
-static ssize_t pin_configs_show(struct hda_codec *codec,
- struct snd_array *list,
- char *buf)
-{
- int i, len = 0;
- for (i = 0; i < list->used; i++) {
- struct hda_pincfg *pin = snd_array_elem(list, i);
- len += sprintf(buf + len, "0x%02x 0x%08x\n",
- pin->nid, pin->cfg);
- }
- return len;
-}
-
-static ssize_t init_pin_configs_show(struct device *dev,
- struct device_attribute *attr,
- char *buf)
-{
- struct snd_hwdep *hwdep = dev_get_drvdata(dev);
- struct hda_codec *codec = hwdep->private_data;
- return pin_configs_show(codec, &codec->init_pins, buf);
-}
-
-static ssize_t user_pin_configs_show(struct device *dev,
- struct device_attribute *attr,
- char *buf)
-{
- struct snd_hwdep *hwdep = dev_get_drvdata(dev);
- struct hda_codec *codec = hwdep->private_data;
- return pin_configs_show(codec, &codec->user_pins, buf);
-}
-
-static ssize_t driver_pin_configs_show(struct device *dev,
- struct device_attribute *attr,
- char *buf)
-{
- struct snd_hwdep *hwdep = dev_get_drvdata(dev);
- struct hda_codec *codec = hwdep->private_data;
- return pin_configs_show(codec, &codec->driver_pins, buf);
-}
-
-#define MAX_PIN_CONFIGS 32
-
-static int parse_user_pin_configs(struct hda_codec *codec, const char *buf)
-{
- int nid, cfg;
-
- if (sscanf(buf, "%i %i", &nid, &cfg) != 2)
- return -EINVAL;
- if (!nid)
- return -EINVAL;
- return snd_hda_add_pincfg(codec, &codec->user_pins, nid, cfg);
-}
-
-static ssize_t user_pin_configs_store(struct device *dev,
- struct device_attribute *attr,
- const char *buf, size_t count)
-{
- struct snd_hwdep *hwdep = dev_get_drvdata(dev);
- struct hda_codec *codec = hwdep->private_data;
- int err = parse_user_pin_configs(codec, buf);
- if (err < 0)
- return err;
- return count;
-}
-
-#define CODEC_ATTR_RW(type) \
- __ATTR(type, 0644, type##_show, type##_store)
-#define CODEC_ATTR_RO(type) \
- __ATTR_RO(type)
-#define CODEC_ATTR_WO(type) \
- __ATTR(type, 0200, NULL, type##_store)
-
-static struct device_attribute codec_attrs[] = {
- CODEC_ATTR_RW(vendor_id),
- CODEC_ATTR_RW(subsystem_id),
- CODEC_ATTR_RW(revision_id),
- CODEC_ATTR_RO(afg),
- CODEC_ATTR_RO(mfg),
- CODEC_ATTR_RW(vendor_name),
- CODEC_ATTR_RW(chip_name),
- CODEC_ATTR_RW(modelname),
- CODEC_ATTR_RW(init_verbs),
- CODEC_ATTR_RW(hints),
- CODEC_ATTR_RO(init_pin_configs),
- CODEC_ATTR_RW(user_pin_configs),
- CODEC_ATTR_RO(driver_pin_configs),
- CODEC_ATTR_WO(reconfig),
- CODEC_ATTR_WO(clear),
-};
-
-/*
- * create sysfs files on hwdep directory
- */
-int snd_hda_hwdep_add_sysfs(struct hda_codec *codec)
-{
- struct snd_hwdep *hwdep = codec->hwdep;
- int i;
-
- for (i = 0; i < ARRAY_SIZE(codec_attrs); i++)
- snd_add_device_sysfs_file(SNDRV_DEVICE_TYPE_HWDEP, hwdep->card,
- hwdep->device, &codec_attrs[i]);
- return 0;
-}
-
-/*
- * Look for hint string
- */
-const char *snd_hda_get_hint(struct hda_codec *codec, const char *key)
-{
- struct hda_hint *hint = get_hint(codec, key);
- return hint ? hint->val : NULL;
-}
-EXPORT_SYMBOL_HDA(snd_hda_get_hint);
-
-int snd_hda_get_bool_hint(struct hda_codec *codec, const char *key)
-{
- const char *p = snd_hda_get_hint(codec, key);
- if (!p || !*p)
- return -ENOENT;
- switch (toupper(*p)) {
- case 'T': /* true */
- case 'Y': /* yes */
- case '1':
- return 1;
- }
- return 0;
-}
-EXPORT_SYMBOL_HDA(snd_hda_get_bool_hint);
-
-#endif /* CONFIG_SND_HDA_RECONFIG */
-
-#ifdef CONFIG_SND_HDA_PATCH_LOADER
-
-/* parser mode */
-enum {
- LINE_MODE_NONE,
- LINE_MODE_CODEC,
- LINE_MODE_MODEL,
- LINE_MODE_PINCFG,
- LINE_MODE_VERB,
- LINE_MODE_HINT,
- LINE_MODE_VENDOR_ID,
- LINE_MODE_SUBSYSTEM_ID,
- LINE_MODE_REVISION_ID,
- LINE_MODE_CHIP_NAME,
- NUM_LINE_MODES,
-};
-
-static inline int strmatch(const char *a, const char *b)
-{
- return strnicmp(a, b, strlen(b)) == 0;
-}
-
-/* parse the contents after the line "[codec]"
- * accept only the line with three numbers, and assign the current codec
- */
-static void parse_codec_mode(char *buf, struct hda_bus *bus,
- struct hda_codec **codecp)
-{
- unsigned int vendorid, subid, caddr;
- struct hda_codec *codec;
-
- *codecp = NULL;
- if (sscanf(buf, "%i %i %i", &vendorid, &subid, &caddr) == 3) {
- list_for_each_entry(codec, &bus->codec_list, list) {
- if (codec->vendor_id == vendorid &&
- codec->subsystem_id == subid &&
- codec->addr == caddr) {
- *codecp = codec;
- break;
- }
- }
- }
-}
-
-/* parse the contents after the other command tags, [pincfg], [verb],
- * [vendor_id], [subsystem_id], [revision_id], [chip_name], [hint] and [model]
- * just pass to the sysfs helper (only when any codec was specified)
- */
-static void parse_pincfg_mode(char *buf, struct hda_bus *bus,
- struct hda_codec **codecp)
-{
- parse_user_pin_configs(*codecp, buf);
-}
-
-static void parse_verb_mode(char *buf, struct hda_bus *bus,
- struct hda_codec **codecp)
-{
- parse_init_verbs(*codecp, buf);
-}
-
-static void parse_hint_mode(char *buf, struct hda_bus *bus,
- struct hda_codec **codecp)
-{
- parse_hints(*codecp, buf);
-}
-
-static void parse_model_mode(char *buf, struct hda_bus *bus,
- struct hda_codec **codecp)
-{
- kfree((*codecp)->modelname);
- (*codecp)->modelname = kstrdup(buf, GFP_KERNEL);
-}
-
-static void parse_chip_name_mode(char *buf, struct hda_bus *bus,
- struct hda_codec **codecp)
-{
- kfree((*codecp)->chip_name);
- (*codecp)->chip_name = kstrdup(buf, GFP_KERNEL);
-}
-
-#define DEFINE_PARSE_ID_MODE(name) \
-static void parse_##name##_mode(char *buf, struct hda_bus *bus, \
- struct hda_codec **codecp) \
-{ \
- unsigned long val; \
- if (!strict_strtoul(buf, 0, &val)) \
- (*codecp)->name = val; \
-}
-
-DEFINE_PARSE_ID_MODE(vendor_id);
-DEFINE_PARSE_ID_MODE(subsystem_id);
-DEFINE_PARSE_ID_MODE(revision_id);
-
-
-struct hda_patch_item {
- const char *tag;
- void (*parser)(char *buf, struct hda_bus *bus, struct hda_codec **retc);
- int need_codec;
-};
-
-static struct hda_patch_item patch_items[NUM_LINE_MODES] = {
- [LINE_MODE_CODEC] = { "[codec]", parse_codec_mode, 0 },
- [LINE_MODE_MODEL] = { "[model]", parse_model_mode, 1 },
- [LINE_MODE_VERB] = { "[verb]", parse_verb_mode, 1 },
- [LINE_MODE_PINCFG] = { "[pincfg]", parse_pincfg_mode, 1 },
- [LINE_MODE_HINT] = { "[hint]", parse_hint_mode, 1 },
- [LINE_MODE_VENDOR_ID] = { "[vendor_id]", parse_vendor_id_mode, 1 },
- [LINE_MODE_SUBSYSTEM_ID] = { "[subsystem_id]", parse_subsystem_id_mode, 1 },
- [LINE_MODE_REVISION_ID] = { "[revision_id]", parse_revision_id_mode, 1 },
- [LINE_MODE_CHIP_NAME] = { "[chip_name]", parse_chip_name_mode, 1 },
-};
-
-/* check the line starting with '[' -- change the parser mode accodingly */
-static int parse_line_mode(char *buf, struct hda_bus *bus)
-{
- int i;
- for (i = 0; i < ARRAY_SIZE(patch_items); i++) {
- if (!patch_items[i].tag)
- continue;
- if (strmatch(buf, patch_items[i].tag))
- return i;
- }
- return LINE_MODE_NONE;
-}
-
-/* copy one line from the buffer in fw, and update the fields in fw
- * return zero if it reaches to the end of the buffer, or non-zero
- * if successfully copied a line
- *
- * the spaces at the beginning and the end of the line are stripped
- */
-static int get_line_from_fw(char *buf, int size, struct firmware *fw)
-{
- int len;
- const char *p = fw->data;
- while (isspace(*p) && fw->size) {
- p++;
- fw->size--;
- }
- if (!fw->size)
- return 0;
- if (size < fw->size)
- size = fw->size;
-
- for (len = 0; len < fw->size; len++) {
- if (!*p)
- break;
- if (*p == '\n') {
- p++;
- len++;
- break;
- }
- if (len < size)
- *buf++ = *p++;
- }
- *buf = 0;
- fw->size -= len;
- fw->data = p;
- remove_trail_spaces(buf);
- return 1;
-}
-
-/*
- * load a "patch" firmware file and parse it
- */
-int snd_hda_load_patch(struct hda_bus *bus, const char *patch)
-{
- int err;
- const struct firmware *fw;
- struct firmware tmp;
- char buf[128];
- struct hda_codec *codec;
- int line_mode;
- struct device *dev = bus->card->dev;
-
- if (snd_BUG_ON(!dev))
- return -ENODEV;
- err = request_firmware(&fw, patch, dev);
- if (err < 0) {
- printk(KERN_ERR "hda-codec: Cannot load the patch '%s'\n",
- patch);
- return err;
- }
-
- tmp = *fw;
- line_mode = LINE_MODE_NONE;
- codec = NULL;
- while (get_line_from_fw(buf, sizeof(buf) - 1, &tmp)) {
- if (!*buf || *buf == '#' || *buf == '\n')
- continue;
- if (*buf == '[')
- line_mode = parse_line_mode(buf, bus);
- else if (patch_items[line_mode].parser &&
- (codec || !patch_items[line_mode].need_codec))
- patch_items[line_mode].parser(buf, bus, &codec);
- }
- release_firmware(fw);
return 0;
}
-EXPORT_SYMBOL_HDA(snd_hda_load_patch);
-#endif /* CONFIG_SND_HDA_PATCH_LOADER */
diff --git a/sound/pci/hda/hda_i915.c b/sound/pci/hda/hda_i915.c
new file mode 100644
index 00000000000..8b4940ba33d
--- /dev/null
+++ b/sound/pci/hda/hda_i915.c
@@ -0,0 +1,130 @@
+/*
+ * hda_i915.c - routines for Haswell HDA controller power well support
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the Free
+ * Software Foundation; either version 2 of the License, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
+ * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#include <linux/init.h>
+#include <linux/module.h>
+#include <sound/core.h>
+#include <drm/i915_powerwell.h>
+#include "hda_priv.h"
+#include "hda_i915.h"
+
+/* Intel HSW/BDW display HDA controller Extended Mode registers.
+ * EM4 (M value) and EM5 (N Value) are used to convert CDClk (Core Display
+ * Clock) to 24MHz BCLK: BCLK = CDCLK * M / N
+ * The values will be lost when the display power well is disabled.
+ */
+#define ICH6_REG_EM4 0x100c
+#define ICH6_REG_EM5 0x1010
+
+static int (*get_power)(void);
+static int (*put_power)(void);
+static int (*get_cdclk)(void);
+
+int hda_display_power(bool enable)
+{
+ if (!get_power || !put_power)
+ return -ENODEV;
+
+ pr_debug("HDA display power %s \n",
+ enable ? "Enable" : "Disable");
+ if (enable)
+ return get_power();
+ else
+ return put_power();
+}
+
+void haswell_set_bclk(struct azx *chip)
+{
+ int cdclk_freq;
+ unsigned int bclk_m, bclk_n;
+
+ if (!get_cdclk)
+ return;
+
+ cdclk_freq = get_cdclk();
+ switch (cdclk_freq) {
+ case 337500:
+ bclk_m = 16;
+ bclk_n = 225;
+ break;
+
+ case 450000:
+ default: /* default CDCLK 450MHz */
+ bclk_m = 4;
+ bclk_n = 75;
+ break;
+
+ case 540000:
+ bclk_m = 4;
+ bclk_n = 90;
+ break;
+
+ case 675000:
+ bclk_m = 8;
+ bclk_n = 225;
+ break;
+ }
+
+ azx_writew(chip, EM4, bclk_m);
+ azx_writew(chip, EM5, bclk_n);
+}
+
+
+int hda_i915_init(void)
+{
+ int err = 0;
+
+ get_power = symbol_request(i915_request_power_well);
+ if (!get_power) {
+ pr_warn("hda-i915: get_power symbol get fail\n");
+ return -ENODEV;
+ }
+
+ put_power = symbol_request(i915_release_power_well);
+ if (!put_power) {
+ symbol_put(i915_request_power_well);
+ get_power = NULL;
+ return -ENODEV;
+ }
+
+ get_cdclk = symbol_request(i915_get_cdclk_freq);
+ if (!get_cdclk) /* may have abnormal BCLK and audio playback rate */
+ pr_warn("hda-i915: get_cdclk symbol get fail\n");
+
+ pr_debug("HDA driver get symbol successfully from i915 module\n");
+
+ return err;
+}
+
+int hda_i915_exit(void)
+{
+ if (get_power) {
+ symbol_put(i915_request_power_well);
+ get_power = NULL;
+ }
+ if (put_power) {
+ symbol_put(i915_release_power_well);
+ put_power = NULL;
+ }
+ if (get_cdclk) {
+ symbol_put(i915_get_cdclk_freq);
+ get_cdclk = NULL;
+ }
+
+ return 0;
+}
diff --git a/sound/pci/hda/hda_i915.h b/sound/pci/hda/hda_i915.h
new file mode 100644
index 00000000000..e6072c62758
--- /dev/null
+++ b/sound/pci/hda/hda_i915.h
@@ -0,0 +1,37 @@
+/*
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the Free
+ * Software Foundation; either version 2 of the License, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program; if not, write to the Free Software Foundation, Inc., 59
+ * Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+#ifndef __SOUND_HDA_I915_H
+#define __SOUND_HDA_I915_H
+
+#ifdef CONFIG_SND_HDA_I915
+int hda_display_power(bool enable);
+void haswell_set_bclk(struct azx *chip);
+int hda_i915_init(void);
+int hda_i915_exit(void);
+#else
+static inline int hda_display_power(bool enable) { return 0; }
+static inline void haswell_set_bclk(struct azx *chip) { return; }
+static inline int hda_i915_init(void)
+{
+ return -ENODEV;
+}
+static inline int hda_i915_exit(void)
+{
+ return 0;
+}
+#endif
+
+#endif
diff --git a/sound/pci/hda/hda_intel.c b/sound/pci/hda/hda_intel.c
index 70a9d32f0e9..83cd19017cf 100644
--- a/sound/pci/hda/hda_intel.c
+++ b/sound/pci/hda/hda_intel.c
@@ -34,7 +34,6 @@
*
*/
-#include <asm/io.h>
#include <linux/delay.h>
#include <linux/interrupt.h>
#include <linux/kernel.h>
@@ -46,26 +45,44 @@
#include <linux/pci.h>
#include <linux/mutex.h>
#include <linux/reboot.h>
+#include <linux/io.h>
+#include <linux/pm_runtime.h>
+#include <linux/clocksource.h>
+#include <linux/time.h>
+#include <linux/completion.h>
+
+#ifdef CONFIG_X86
+/* for snoop control */
+#include <asm/pgtable.h>
+#include <asm/cacheflush.h>
+#endif
#include <sound/core.h>
#include <sound/initval.h>
+#include <linux/vgaarb.h>
+#include <linux/vga_switcheroo.h>
+#include <linux/firmware.h>
#include "hda_codec.h"
+#include "hda_controller.h"
+#include "hda_priv.h"
+#include "hda_i915.h"
static int index[SNDRV_CARDS] = SNDRV_DEFAULT_IDX;
static char *id[SNDRV_CARDS] = SNDRV_DEFAULT_STR;
-static int enable[SNDRV_CARDS] = SNDRV_DEFAULT_ENABLE_PNP;
+static bool enable[SNDRV_CARDS] = SNDRV_DEFAULT_ENABLE_PNP;
static char *model[SNDRV_CARDS];
-static int position_fix[SNDRV_CARDS];
+static int position_fix[SNDRV_CARDS] = {[0 ... (SNDRV_CARDS-1)] = -1};
static int bdl_pos_adj[SNDRV_CARDS] = {[0 ... (SNDRV_CARDS-1)] = -1};
static int probe_mask[SNDRV_CARDS] = {[0 ... (SNDRV_CARDS-1)] = -1};
static int probe_only[SNDRV_CARDS];
-static int single_cmd;
+static int jackpoll_ms[SNDRV_CARDS];
+static bool single_cmd;
static int enable_msi = -1;
#ifdef CONFIG_SND_HDA_PATCH_LOADER
static char *patch[SNDRV_CARDS];
#endif
#ifdef CONFIG_SND_HDA_INPUT_BEEP
-static int beep_mode[SNDRV_CARDS] = {[0 ... (SNDRV_CARDS-1)] =
+static bool beep_mode[SNDRV_CARDS] = {[0 ... (SNDRV_CARDS-1)] =
CONFIG_SND_HDA_INPUT_BEEP_MODE};
#endif
@@ -79,31 +96,41 @@ module_param_array(model, charp, NULL, 0444);
MODULE_PARM_DESC(model, "Use the given board model.");
module_param_array(position_fix, int, NULL, 0444);
MODULE_PARM_DESC(position_fix, "DMA pointer read method."
- "(0 = auto, 1 = LPIB, 2 = POSBUF, 3 = VIACOMBO).");
+ "(-1 = system default, 0 = auto, 1 = LPIB, 2 = POSBUF, 3 = VIACOMBO, 4 = COMBO).");
module_param_array(bdl_pos_adj, int, NULL, 0644);
MODULE_PARM_DESC(bdl_pos_adj, "BDL position adjustment offset.");
module_param_array(probe_mask, int, NULL, 0444);
MODULE_PARM_DESC(probe_mask, "Bitmask to probe codecs (default = -1).");
module_param_array(probe_only, int, NULL, 0444);
MODULE_PARM_DESC(probe_only, "Only probing and no codec initialization.");
+module_param_array(jackpoll_ms, int, NULL, 0444);
+MODULE_PARM_DESC(jackpoll_ms, "Ms between polling for jack events (default = 0, using unsol events only)");
module_param(single_cmd, bool, 0444);
MODULE_PARM_DESC(single_cmd, "Use single command to communicate with codecs "
"(for debugging only).");
-module_param(enable_msi, int, 0444);
+module_param(enable_msi, bint, 0444);
MODULE_PARM_DESC(enable_msi, "Enable Message Signaled Interrupt (MSI)");
#ifdef CONFIG_SND_HDA_PATCH_LOADER
module_param_array(patch, charp, NULL, 0444);
MODULE_PARM_DESC(patch, "Patch file for Intel HD audio interface.");
#endif
#ifdef CONFIG_SND_HDA_INPUT_BEEP
-module_param_array(beep_mode, int, NULL, 0444);
+module_param_array(beep_mode, bool, NULL, 0444);
MODULE_PARM_DESC(beep_mode, "Select HDA Beep registration mode "
- "(0=off, 1=on, 2=mute switch on/off) (default=1).");
+ "(0=off, 1=on) (default=1).");
#endif
-#ifdef CONFIG_SND_HDA_POWER_SAVE
+#ifdef CONFIG_PM
+static int param_set_xint(const char *val, const struct kernel_param *kp);
+static struct kernel_param_ops param_ops_xint = {
+ .set = param_set_xint,
+ .get = param_get_int,
+};
+#define param_check_xint param_check_int
+
static int power_save = CONFIG_SND_HDA_POWER_SAVE_DEFAULT;
-module_param(power_save, int, 0644);
+static int *power_save_addr = &power_save;
+module_param(power_save, xint, 0644);
MODULE_PARM_DESC(power_save, "Automatic power-saving timeout "
"(in second, 0 = disable).");
@@ -111,11 +138,27 @@ MODULE_PARM_DESC(power_save, "Automatic power-saving timeout "
* this may give more power-saving, but will take longer time to
* wake up.
*/
-static int power_save_controller = 1;
+static bool power_save_controller = 1;
module_param(power_save_controller, bool, 0644);
MODULE_PARM_DESC(power_save_controller, "Reset controller in power save mode.");
+#else
+static int *power_save_addr;
+#endif /* CONFIG_PM */
+
+static int align_buffer_size = -1;
+module_param(align_buffer_size, bint, 0644);
+MODULE_PARM_DESC(align_buffer_size,
+ "Force buffer and period sizes to be multiple of 128 bytes.");
+
+#ifdef CONFIG_X86
+static bool hda_snoop = true;
+module_param_named(snoop, hda_snoop, bool, 0444);
+MODULE_PARM_DESC(snoop, "Enable/disable snooping");
+#else
+#define hda_snoop true
#endif
+
MODULE_LICENSE("GPL");
MODULE_SUPPORTED_DEVICE("{{Intel, ICH6},"
"{Intel, ICH6M},"
@@ -126,6 +169,11 @@ MODULE_SUPPORTED_DEVICE("{{Intel, ICH6},"
"{Intel, ICH10},"
"{Intel, PCH},"
"{Intel, CPT},"
+ "{Intel, PPT},"
+ "{Intel, LPT},"
+ "{Intel, LPT_LP},"
+ "{Intel, WPT_LP},"
+ "{Intel, HPT},"
"{Intel, PBG},"
"{Intel, SCH},"
"{ATI, SB450},"
@@ -146,889 +194,159 @@ MODULE_SUPPORTED_DEVICE("{{Intel, ICH6},"
"{ULI, M5461}}");
MODULE_DESCRIPTION("Intel HDA driver");
-#ifdef CONFIG_SND_VERBOSE_PRINTK
-#define SFX /* nop */
-#else
-#define SFX "hda-intel: "
+#if defined(CONFIG_PM) && defined(CONFIG_VGA_SWITCHEROO)
+#if IS_ENABLED(CONFIG_SND_HDA_CODEC_HDMI)
+#define SUPPORT_VGA_SWITCHEROO
+#endif
#endif
-/*
- * registers
- */
-#define ICH6_REG_GCAP 0x00
-#define ICH6_GCAP_64OK (1 << 0) /* 64bit address support */
-#define ICH6_GCAP_NSDO (3 << 1) /* # of serial data out signals */
-#define ICH6_GCAP_BSS (31 << 3) /* # of bidirectional streams */
-#define ICH6_GCAP_ISS (15 << 8) /* # of input streams */
-#define ICH6_GCAP_OSS (15 << 12) /* # of output streams */
-#define ICH6_REG_VMIN 0x02
-#define ICH6_REG_VMAJ 0x03
-#define ICH6_REG_OUTPAY 0x04
-#define ICH6_REG_INPAY 0x06
-#define ICH6_REG_GCTL 0x08
-#define ICH6_GCTL_RESET (1 << 0) /* controller reset */
-#define ICH6_GCTL_FCNTRL (1 << 1) /* flush control */
-#define ICH6_GCTL_UNSOL (1 << 8) /* accept unsol. response enable */
-#define ICH6_REG_WAKEEN 0x0c
-#define ICH6_REG_STATESTS 0x0e
-#define ICH6_REG_GSTS 0x10
-#define ICH6_GSTS_FSTS (1 << 1) /* flush status */
-#define ICH6_REG_INTCTL 0x20
-#define ICH6_REG_INTSTS 0x24
-#define ICH6_REG_WALLCLK 0x30 /* 24Mhz source */
-#define ICH6_REG_SYNC 0x34
-#define ICH6_REG_CORBLBASE 0x40
-#define ICH6_REG_CORBUBASE 0x44
-#define ICH6_REG_CORBWP 0x48
-#define ICH6_REG_CORBRP 0x4a
-#define ICH6_CORBRP_RST (1 << 15) /* read pointer reset */
-#define ICH6_REG_CORBCTL 0x4c
-#define ICH6_CORBCTL_RUN (1 << 1) /* enable DMA */
-#define ICH6_CORBCTL_CMEIE (1 << 0) /* enable memory error irq */
-#define ICH6_REG_CORBSTS 0x4d
-#define ICH6_CORBSTS_CMEI (1 << 0) /* memory error indication */
-#define ICH6_REG_CORBSIZE 0x4e
-
-#define ICH6_REG_RIRBLBASE 0x50
-#define ICH6_REG_RIRBUBASE 0x54
-#define ICH6_REG_RIRBWP 0x58
-#define ICH6_RIRBWP_RST (1 << 15) /* write pointer reset */
-#define ICH6_REG_RINTCNT 0x5a
-#define ICH6_REG_RIRBCTL 0x5c
-#define ICH6_RBCTL_IRQ_EN (1 << 0) /* enable IRQ */
-#define ICH6_RBCTL_DMA_EN (1 << 1) /* enable DMA */
-#define ICH6_RBCTL_OVERRUN_EN (1 << 2) /* enable overrun irq */
-#define ICH6_REG_RIRBSTS 0x5d
-#define ICH6_RBSTS_IRQ (1 << 0) /* response irq */
-#define ICH6_RBSTS_OVERRUN (1 << 2) /* overrun irq */
-#define ICH6_REG_RIRBSIZE 0x5e
-
-#define ICH6_REG_IC 0x60
-#define ICH6_REG_IR 0x64
-#define ICH6_REG_IRS 0x68
-#define ICH6_IRS_VALID (1<<1)
-#define ICH6_IRS_BUSY (1<<0)
-
-#define ICH6_REG_DPLBASE 0x70
-#define ICH6_REG_DPUBASE 0x74
-#define ICH6_DPLBASE_ENABLE 0x1 /* Enable position buffer */
-
-/* SD offset: SDI0=0x80, SDI1=0xa0, ... SDO3=0x160 */
-enum { SDI0, SDI1, SDI2, SDI3, SDO0, SDO1, SDO2, SDO3 };
-
-/* stream register offsets from stream base */
-#define ICH6_REG_SD_CTL 0x00
-#define ICH6_REG_SD_STS 0x03
-#define ICH6_REG_SD_LPIB 0x04
-#define ICH6_REG_SD_CBL 0x08
-#define ICH6_REG_SD_LVI 0x0c
-#define ICH6_REG_SD_FIFOW 0x0e
-#define ICH6_REG_SD_FIFOSIZE 0x10
-#define ICH6_REG_SD_FORMAT 0x12
-#define ICH6_REG_SD_BDLPL 0x18
-#define ICH6_REG_SD_BDLPU 0x1c
-
-/* PCI space */
-#define ICH6_PCIREG_TCSEL 0x44
-
-/*
- * other constants
- */
-
-/* max number of SDs */
-/* ICH, ATI and VIA have 4 playback and 4 capture */
-#define ICH6_NUM_CAPTURE 4
-#define ICH6_NUM_PLAYBACK 4
-
-/* ULI has 6 playback and 5 capture */
-#define ULI_NUM_CAPTURE 5
-#define ULI_NUM_PLAYBACK 6
-
-/* ATI HDMI has 1 playback and 0 capture */
-#define ATIHDMI_NUM_CAPTURE 0
-#define ATIHDMI_NUM_PLAYBACK 1
-
-/* TERA has 4 playback and 3 capture */
-#define TERA_NUM_CAPTURE 3
-#define TERA_NUM_PLAYBACK 4
-
-/* this number is statically defined for simplicity */
-#define MAX_AZX_DEV 16
-
-/* max number of fragments - we may use more if allocating more pages for BDL */
-#define BDL_SIZE 4096
-#define AZX_MAX_BDL_ENTRIES (BDL_SIZE / 16)
-#define AZX_MAX_FRAG 32
-/* max buffer size - no h/w limit, you can increase as you like */
-#define AZX_MAX_BUF_SIZE (1024*1024*1024)
-
-/* RIRB int mask: overrun[2], response[0] */
-#define RIRB_INT_RESPONSE 0x01
-#define RIRB_INT_OVERRUN 0x04
-#define RIRB_INT_MASK 0x05
-
-/* STATESTS int mask: S3,SD2,SD1,SD0 */
-#define AZX_MAX_CODECS 8
-#define AZX_DEFAULT_CODECS 4
-#define STATESTS_INT_MASK ((1 << AZX_MAX_CODECS) - 1)
-
-/* SD_CTL bits */
-#define SD_CTL_STREAM_RESET 0x01 /* stream reset bit */
-#define SD_CTL_DMA_START 0x02 /* stream DMA start bit */
-#define SD_CTL_STRIPE (3 << 16) /* stripe control */
-#define SD_CTL_TRAFFIC_PRIO (1 << 18) /* traffic priority */
-#define SD_CTL_DIR (1 << 19) /* bi-directional stream */
-#define SD_CTL_STREAM_TAG_MASK (0xf << 20)
-#define SD_CTL_STREAM_TAG_SHIFT 20
-
-/* SD_CTL and SD_STS */
-#define SD_INT_DESC_ERR 0x10 /* descriptor error interrupt */
-#define SD_INT_FIFO_ERR 0x08 /* FIFO error interrupt */
-#define SD_INT_COMPLETE 0x04 /* completion interrupt */
-#define SD_INT_MASK (SD_INT_DESC_ERR|SD_INT_FIFO_ERR|\
- SD_INT_COMPLETE)
-
-/* SD_STS */
-#define SD_STS_FIFO_READY 0x20 /* FIFO ready */
-
-/* INTCTL and INTSTS */
-#define ICH6_INT_ALL_STREAM 0xff /* all stream interrupts */
-#define ICH6_INT_CTRL_EN 0x40000000 /* controller interrupt enable bit */
-#define ICH6_INT_GLOBAL_EN 0x80000000 /* global interrupt enable bit */
-
-/* below are so far hardcoded - should read registers in future */
-#define ICH6_MAX_CORB_ENTRIES 256
-#define ICH6_MAX_RIRB_ENTRIES 256
-
-/* position fix mode */
-enum {
- POS_FIX_AUTO,
- POS_FIX_LPIB,
- POS_FIX_POSBUF,
- POS_FIX_VIACOMBO,
-};
-
-/* Defines for ATI HD Audio support in SB450 south bridge */
-#define ATI_SB450_HDAUDIO_MISC_CNTR2_ADDR 0x42
-#define ATI_SB450_HDAUDIO_ENABLE_SNOOP 0x02
-
-/* Defines for Nvidia HDA support */
-#define NVIDIA_HDA_TRANSREG_ADDR 0x4e
-#define NVIDIA_HDA_ENABLE_COHBITS 0x0f
-#define NVIDIA_HDA_ISTRM_COH 0x4d
-#define NVIDIA_HDA_OSTRM_COH 0x4c
-#define NVIDIA_HDA_ENABLE_COHBIT 0x01
-
-/* Defines for Intel SCH HDA snoop control */
-#define INTEL_SCH_HDA_DEVC 0x78
-#define INTEL_SCH_HDA_DEVC_NOSNOOP (0x1<<11)
-
-/* Define IN stream 0 FIFO size offset in VIA controller */
-#define VIA_IN_STREAM0_FIFO_SIZE_OFFSET 0x90
-/* Define VIA HD Audio Device ID*/
-#define VIA_HDAC_DEVICE_ID 0x3288
-
-/* HD Audio class code */
-#define PCI_CLASS_MULTIMEDIA_HD_AUDIO 0x0403
/*
*/
-struct azx_dev {
- struct snd_dma_buffer bdl; /* BDL buffer */
- u32 *posbuf; /* position buffer pointer */
-
- unsigned int bufsize; /* size of the play buffer in bytes */
- unsigned int period_bytes; /* size of the period in bytes */
- unsigned int frags; /* number for period in the play buffer */
- unsigned int fifo_size; /* FIFO size */
- unsigned long start_wallclk; /* start + minimum wallclk */
- unsigned long period_wallclk; /* wallclk for period */
-
- void __iomem *sd_addr; /* stream descriptor pointer */
-
- u32 sd_int_sta_mask; /* stream int status mask */
-
- /* pcm support */
- struct snd_pcm_substream *substream; /* assigned substream,
- * set in PCM open
- */
- unsigned int format_val; /* format value to be set in the
- * controller and the codec
- */
- unsigned char stream_tag; /* assigned stream */
- unsigned char index; /* stream index */
- int device; /* last device number assigned to */
-
- unsigned int opened :1;
- unsigned int running :1;
- unsigned int irq_pending :1;
- /*
- * For VIA:
- * A flag to ensure DMA position is 0
- * when link position is not greater than FIFO size
- */
- unsigned int insufficient :1;
-};
-
-/* CORB/RIRB */
-struct azx_rb {
- u32 *buf; /* CORB/RIRB buffer
- * Each CORB entry is 4byte, RIRB is 8byte
- */
- dma_addr_t addr; /* physical address of CORB/RIRB buffer */
- /* for RIRB */
- unsigned short rp, wp; /* read/write pointers */
- int cmds[AZX_MAX_CODECS]; /* number of pending requests */
- u32 res[AZX_MAX_CODECS]; /* last read value */
-};
-
-struct azx {
- struct snd_card *card;
- struct pci_dev *pci;
- int dev_index;
-
- /* chip type specific */
- int driver_type;
- int playback_streams;
- int playback_index_offset;
- int capture_streams;
- int capture_index_offset;
- int num_streams;
-
- /* pci resources */
- unsigned long addr;
- void __iomem *remap_addr;
- int irq;
-
- /* locks */
- spinlock_t reg_lock;
- struct mutex open_mutex;
-
- /* streams (x num_streams) */
- struct azx_dev *azx_dev;
-
- /* PCM */
- struct snd_pcm *pcm[HDA_MAX_PCMS];
-
- /* HD codec */
- unsigned short codec_mask;
- int codec_probe_mask; /* copied from probe_mask option */
- struct hda_bus *bus;
- unsigned int beep_mode;
-
- /* CORB/RIRB */
- struct azx_rb corb;
- struct azx_rb rirb;
-
- /* CORB/RIRB and position buffers */
- struct snd_dma_buffer rb;
- struct snd_dma_buffer posbuf;
-
- /* flags */
- int position_fix[2]; /* for both playback/capture streams */
- int poll_count;
- unsigned int running :1;
- unsigned int initialized :1;
- unsigned int single_cmd :1;
- unsigned int polling_mode :1;
- unsigned int msi :1;
- unsigned int irq_pending_warned :1;
- unsigned int probing :1; /* codec probing phase */
-
- /* for debugging */
- unsigned int last_cmd[AZX_MAX_CODECS];
-
- /* for pending irqs */
- struct work_struct irq_pending_work;
-
- /* reboot notifier (for mysterious hangup problem at power-down) */
- struct notifier_block reboot_notifier;
-};
-
/* driver types */
enum {
AZX_DRIVER_ICH,
AZX_DRIVER_PCH,
AZX_DRIVER_SCH,
+ AZX_DRIVER_HDMI,
AZX_DRIVER_ATI,
AZX_DRIVER_ATIHDMI,
+ AZX_DRIVER_ATIHDMI_NS,
AZX_DRIVER_VIA,
AZX_DRIVER_SIS,
AZX_DRIVER_ULI,
AZX_DRIVER_NVIDIA,
AZX_DRIVER_TERA,
AZX_DRIVER_CTX,
+ AZX_DRIVER_CTHDA,
AZX_DRIVER_GENERIC,
AZX_NUM_DRIVERS, /* keep this as last entry */
};
-static char *driver_short_names[] __devinitdata = {
+/* quirks for Intel PCH */
+#define AZX_DCAPS_INTEL_PCH_NOPM \
+ (AZX_DCAPS_SCH_SNOOP | AZX_DCAPS_BUFSIZE | \
+ AZX_DCAPS_COUNT_LPIB_DELAY | AZX_DCAPS_REVERSE_ASSIGN)
+
+#define AZX_DCAPS_INTEL_PCH \
+ (AZX_DCAPS_INTEL_PCH_NOPM | AZX_DCAPS_PM_RUNTIME)
+
+#define AZX_DCAPS_INTEL_HASWELL \
+ (AZX_DCAPS_SCH_SNOOP | AZX_DCAPS_ALIGN_BUFSIZE | \
+ AZX_DCAPS_COUNT_LPIB_DELAY | AZX_DCAPS_PM_RUNTIME | \
+ AZX_DCAPS_I915_POWERWELL)
+
+/* Broadwell HDMI can't use position buffer reliably, force to use LPIB */
+#define AZX_DCAPS_INTEL_BROADWELL \
+ (AZX_DCAPS_SCH_SNOOP | AZX_DCAPS_ALIGN_BUFSIZE | \
+ AZX_DCAPS_POSFIX_LPIB | AZX_DCAPS_PM_RUNTIME | \
+ AZX_DCAPS_I915_POWERWELL)
+
+/* quirks for ATI SB / AMD Hudson */
+#define AZX_DCAPS_PRESET_ATI_SB \
+ (AZX_DCAPS_ATI_SNOOP | AZX_DCAPS_NO_TCSEL | \
+ AZX_DCAPS_SYNC_WRITE | AZX_DCAPS_POSFIX_LPIB)
+
+/* quirks for ATI/AMD HDMI */
+#define AZX_DCAPS_PRESET_ATI_HDMI \
+ (AZX_DCAPS_NO_TCSEL | AZX_DCAPS_SYNC_WRITE | AZX_DCAPS_POSFIX_LPIB)
+
+/* quirks for Nvidia */
+#define AZX_DCAPS_PRESET_NVIDIA \
+ (AZX_DCAPS_NVIDIA_SNOOP | AZX_DCAPS_RIRB_DELAY | AZX_DCAPS_NO_MSI |\
+ AZX_DCAPS_ALIGN_BUFSIZE | AZX_DCAPS_NO_64BIT |\
+ AZX_DCAPS_CORBRP_SELF_CLEAR)
+
+#define AZX_DCAPS_PRESET_CTHDA \
+ (AZX_DCAPS_NO_MSI | AZX_DCAPS_POSFIX_LPIB | AZX_DCAPS_4K_BDLE_BOUNDARY)
+
+/*
+ * VGA-switcher support
+ */
+#ifdef SUPPORT_VGA_SWITCHEROO
+#define use_vga_switcheroo(chip) ((chip)->use_vga_switcheroo)
+#else
+#define use_vga_switcheroo(chip) 0
+#endif
+
+static char *driver_short_names[] = {
[AZX_DRIVER_ICH] = "HDA Intel",
[AZX_DRIVER_PCH] = "HDA Intel PCH",
[AZX_DRIVER_SCH] = "HDA Intel MID",
+ [AZX_DRIVER_HDMI] = "HDA Intel HDMI",
[AZX_DRIVER_ATI] = "HDA ATI SB",
[AZX_DRIVER_ATIHDMI] = "HDA ATI HDMI",
+ [AZX_DRIVER_ATIHDMI_NS] = "HDA ATI HDMI",
[AZX_DRIVER_VIA] = "HDA VIA VT82xx",
[AZX_DRIVER_SIS] = "HDA SIS966",
[AZX_DRIVER_ULI] = "HDA ULI M5461",
[AZX_DRIVER_NVIDIA] = "HDA NVidia",
[AZX_DRIVER_TERA] = "HDA Teradici",
[AZX_DRIVER_CTX] = "HDA Creative",
+ [AZX_DRIVER_CTHDA] = "HDA Creative",
[AZX_DRIVER_GENERIC] = "HD-Audio Generic",
};
-/*
- * macros for easy use
- */
-#define azx_writel(chip,reg,value) \
- writel(value, (chip)->remap_addr + ICH6_REG_##reg)
-#define azx_readl(chip,reg) \
- readl((chip)->remap_addr + ICH6_REG_##reg)
-#define azx_writew(chip,reg,value) \
- writew(value, (chip)->remap_addr + ICH6_REG_##reg)
-#define azx_readw(chip,reg) \
- readw((chip)->remap_addr + ICH6_REG_##reg)
-#define azx_writeb(chip,reg,value) \
- writeb(value, (chip)->remap_addr + ICH6_REG_##reg)
-#define azx_readb(chip,reg) \
- readb((chip)->remap_addr + ICH6_REG_##reg)
-
-#define azx_sd_writel(dev,reg,value) \
- writel(value, (dev)->sd_addr + ICH6_REG_##reg)
-#define azx_sd_readl(dev,reg) \
- readl((dev)->sd_addr + ICH6_REG_##reg)
-#define azx_sd_writew(dev,reg,value) \
- writew(value, (dev)->sd_addr + ICH6_REG_##reg)
-#define azx_sd_readw(dev,reg) \
- readw((dev)->sd_addr + ICH6_REG_##reg)
-#define azx_sd_writeb(dev,reg,value) \
- writeb(value, (dev)->sd_addr + ICH6_REG_##reg)
-#define azx_sd_readb(dev,reg) \
- readb((dev)->sd_addr + ICH6_REG_##reg)
-
-/* for pcm support */
-#define get_azx_dev(substream) (substream->runtime->private_data)
-
-static int azx_acquire_irq(struct azx *chip, int do_disconnect);
-static int azx_send_cmd(struct hda_bus *bus, unsigned int val);
-/*
- * Interface for HD codec
- */
-
-/*
- * CORB / RIRB interface
- */
-static int azx_alloc_cmd_io(struct azx *chip)
-{
- int err;
-
- /* single page (at least 4096 bytes) must suffice for both ringbuffes */
- err = snd_dma_alloc_pages(SNDRV_DMA_TYPE_DEV,
- snd_dma_pci_data(chip->pci),
- PAGE_SIZE, &chip->rb);
- if (err < 0) {
- snd_printk(KERN_ERR SFX "cannot allocate CORB/RIRB\n");
- return err;
- }
- return 0;
-}
-
-static void azx_init_cmd_io(struct azx *chip)
-{
- spin_lock_irq(&chip->reg_lock);
- /* CORB set up */
- chip->corb.addr = chip->rb.addr;
- chip->corb.buf = (u32 *)chip->rb.area;
- azx_writel(chip, CORBLBASE, (u32)chip->corb.addr);
- azx_writel(chip, CORBUBASE, upper_32_bits(chip->corb.addr));
-
- /* set the corb size to 256 entries (ULI requires explicitly) */
- azx_writeb(chip, CORBSIZE, 0x02);
- /* set the corb write pointer to 0 */
- azx_writew(chip, CORBWP, 0);
- /* reset the corb hw read pointer */
- azx_writew(chip, CORBRP, ICH6_CORBRP_RST);
- /* enable corb dma */
- azx_writeb(chip, CORBCTL, ICH6_CORBCTL_RUN);
-
- /* RIRB set up */
- chip->rirb.addr = chip->rb.addr + 2048;
- chip->rirb.buf = (u32 *)(chip->rb.area + 2048);
- chip->rirb.wp = chip->rirb.rp = 0;
- memset(chip->rirb.cmds, 0, sizeof(chip->rirb.cmds));
- azx_writel(chip, RIRBLBASE, (u32)chip->rirb.addr);
- azx_writel(chip, RIRBUBASE, upper_32_bits(chip->rirb.addr));
-
- /* set the rirb size to 256 entries (ULI requires explicitly) */
- azx_writeb(chip, RIRBSIZE, 0x02);
- /* reset the rirb hw write pointer */
- azx_writew(chip, RIRBWP, ICH6_RIRBWP_RST);
- /* set N=1, get RIRB response interrupt for new entry */
- if (chip->driver_type == AZX_DRIVER_CTX)
- azx_writew(chip, RINTCNT, 0xc0);
- else
- azx_writew(chip, RINTCNT, 1);
- /* enable rirb dma and response irq */
- azx_writeb(chip, RIRBCTL, ICH6_RBCTL_DMA_EN | ICH6_RBCTL_IRQ_EN);
- spin_unlock_irq(&chip->reg_lock);
-}
-
-static void azx_free_cmd_io(struct azx *chip)
-{
- spin_lock_irq(&chip->reg_lock);
- /* disable ringbuffer DMAs */
- azx_writeb(chip, RIRBCTL, 0);
- azx_writeb(chip, CORBCTL, 0);
- spin_unlock_irq(&chip->reg_lock);
-}
-
-static unsigned int azx_command_addr(u32 cmd)
-{
- unsigned int addr = cmd >> 28;
-
- if (addr >= AZX_MAX_CODECS) {
- snd_BUG();
- addr = 0;
- }
-
- return addr;
-}
-
-static unsigned int azx_response_addr(u32 res)
-{
- unsigned int addr = res & 0xf;
-
- if (addr >= AZX_MAX_CODECS) {
- snd_BUG();
- addr = 0;
- }
-
- return addr;
-}
-
-/* send a command */
-static int azx_corb_send_cmd(struct hda_bus *bus, u32 val)
-{
- struct azx *chip = bus->private_data;
- unsigned int addr = azx_command_addr(val);
- unsigned int wp;
-
- spin_lock_irq(&chip->reg_lock);
-
- /* add command to corb */
- wp = azx_readb(chip, CORBWP);
- wp++;
- wp %= ICH6_MAX_CORB_ENTRIES;
-
- chip->rirb.cmds[addr]++;
- chip->corb.buf[wp] = cpu_to_le32(val);
- azx_writel(chip, CORBWP, wp);
-
- spin_unlock_irq(&chip->reg_lock);
-
- return 0;
-}
+struct hda_intel {
+ struct azx chip;
+};
-#define ICH6_RIRB_EX_UNSOL_EV (1<<4)
-/* retrieve RIRB entry - called from interrupt handler */
-static void azx_update_rirb(struct azx *chip)
+#ifdef CONFIG_X86
+static void __mark_pages_wc(struct azx *chip, struct snd_dma_buffer *dmab, bool on)
{
- unsigned int rp, wp;
- unsigned int addr;
- u32 res, res_ex;
+ int pages;
- wp = azx_readb(chip, RIRBWP);
- if (wp == chip->rirb.wp)
+ if (azx_snoop(chip))
+ return;
+ if (!dmab || !dmab->area || !dmab->bytes)
return;
- chip->rirb.wp = wp;
-
- while (chip->rirb.rp != wp) {
- chip->rirb.rp++;
- chip->rirb.rp %= ICH6_MAX_RIRB_ENTRIES;
-
- rp = chip->rirb.rp << 1; /* an RIRB entry is 8-bytes */
- res_ex = le32_to_cpu(chip->rirb.buf[rp + 1]);
- res = le32_to_cpu(chip->rirb.buf[rp]);
- addr = azx_response_addr(res_ex);
- if (res_ex & ICH6_RIRB_EX_UNSOL_EV)
- snd_hda_queue_unsol_event(chip->bus, res, res_ex);
- else if (chip->rirb.cmds[addr]) {
- chip->rirb.res[addr] = res;
- smp_wmb();
- chip->rirb.cmds[addr]--;
- } else
- snd_printk(KERN_ERR SFX "spurious response %#x:%#x, "
- "last cmd=%#08x\n",
- res, res_ex,
- chip->last_cmd[addr]);
- }
-}
-
-/* receive a response */
-static unsigned int azx_rirb_get_response(struct hda_bus *bus,
- unsigned int addr)
-{
- struct azx *chip = bus->private_data;
- unsigned long timeout;
- int do_poll = 0;
-
- again:
- timeout = jiffies + msecs_to_jiffies(1000);
- for (;;) {
- if (chip->polling_mode || do_poll) {
- spin_lock_irq(&chip->reg_lock);
- azx_update_rirb(chip);
- spin_unlock_irq(&chip->reg_lock);
- }
- if (!chip->rirb.cmds[addr]) {
- smp_rmb();
- bus->rirb_error = 0;
-
- if (!do_poll)
- chip->poll_count = 0;
- return chip->rirb.res[addr]; /* the last value */
- }
- if (time_after(jiffies, timeout))
- break;
- if (bus->needs_damn_long_delay)
- msleep(2); /* temporary workaround */
- else {
- udelay(10);
- cond_resched();
- }
- }
-
- if (!chip->polling_mode && chip->poll_count < 2) {
- snd_printdd(SFX "azx_get_response timeout, "
- "polling the codec once: last cmd=0x%08x\n",
- chip->last_cmd[addr]);
- do_poll = 1;
- chip->poll_count++;
- goto again;
- }
-
-
- if (!chip->polling_mode) {
- snd_printk(KERN_WARNING SFX "azx_get_response timeout, "
- "switching to polling mode: last cmd=0x%08x\n",
- chip->last_cmd[addr]);
- chip->polling_mode = 1;
- goto again;
- }
-
- if (chip->msi) {
- snd_printk(KERN_WARNING SFX "No response from codec, "
- "disabling MSI: last cmd=0x%08x\n",
- chip->last_cmd[addr]);
- free_irq(chip->irq, chip);
- chip->irq = -1;
- pci_disable_msi(chip->pci);
- chip->msi = 0;
- if (azx_acquire_irq(chip, 1) < 0) {
- bus->rirb_error = 1;
- return -1;
- }
- goto again;
- }
-
- if (chip->probing) {
- /* If this critical timeout happens during the codec probing
- * phase, this is likely an access to a non-existing codec
- * slot. Better to return an error and reset the system.
- */
- return -1;
- }
-
- /* a fatal communication error; need either to reset or to fallback
- * to the single_cmd mode
- */
- bus->rirb_error = 1;
- if (bus->allow_bus_reset && !bus->response_reset && !bus->in_reset) {
- bus->response_reset = 1;
- return -1; /* give a chance to retry */
- }
-
- snd_printk(KERN_ERR "hda_intel: azx_get_response timeout, "
- "switching to single_cmd mode: last cmd=0x%08x\n",
- chip->last_cmd[addr]);
- chip->single_cmd = 1;
- bus->response_reset = 0;
- /* release CORB/RIRB */
- azx_free_cmd_io(chip);
- /* disable unsolicited responses */
- azx_writel(chip, GCTL, azx_readl(chip, GCTL) & ~ICH6_GCTL_UNSOL);
- return -1;
-}
-
-/*
- * Use the single immediate command instead of CORB/RIRB for simplicity
- *
- * Note: according to Intel, this is not preferred use. The command was
- * intended for the BIOS only, and may get confused with unsolicited
- * responses. So, we shouldn't use it for normal operation from the
- * driver.
- * I left the codes, however, for debugging/testing purposes.
- */
-
-/* receive a response */
-static int azx_single_wait_for_response(struct azx *chip, unsigned int addr)
-{
- int timeout = 50;
-
- while (timeout--) {
- /* check IRV busy bit */
- if (azx_readw(chip, IRS) & ICH6_IRS_VALID) {
- /* reuse rirb.res as the response return value */
- chip->rirb.res[addr] = azx_readl(chip, IR);
- return 0;
- }
- udelay(1);
- }
- if (printk_ratelimit())
- snd_printd(SFX "get_response timeout: IRS=0x%x\n",
- azx_readw(chip, IRS));
- chip->rirb.res[addr] = -1;
- return -EIO;
-}
-/* send a command */
-static int azx_single_send_cmd(struct hda_bus *bus, u32 val)
-{
- struct azx *chip = bus->private_data;
- unsigned int addr = azx_command_addr(val);
- int timeout = 50;
-
- bus->rirb_error = 0;
- while (timeout--) {
- /* check ICB busy bit */
- if (!((azx_readw(chip, IRS) & ICH6_IRS_BUSY))) {
- /* Clear IRV valid bit */
- azx_writew(chip, IRS, azx_readw(chip, IRS) |
- ICH6_IRS_VALID);
- azx_writel(chip, IC, val);
- azx_writew(chip, IRS, azx_readw(chip, IRS) |
- ICH6_IRS_BUSY);
- return azx_single_wait_for_response(chip, addr);
- }
- udelay(1);
+#ifdef CONFIG_SND_DMA_SGBUF
+ if (dmab->dev.type == SNDRV_DMA_TYPE_DEV_SG) {
+ struct snd_sg_buf *sgbuf = dmab->private_data;
+ if (on)
+ set_pages_array_wc(sgbuf->page_table, sgbuf->pages);
+ else
+ set_pages_array_wb(sgbuf->page_table, sgbuf->pages);
+ return;
}
- if (printk_ratelimit())
- snd_printd(SFX "send_cmd timeout: IRS=0x%x, val=0x%x\n",
- azx_readw(chip, IRS), val);
- return -EIO;
-}
-
-/* receive a response */
-static unsigned int azx_single_get_response(struct hda_bus *bus,
- unsigned int addr)
-{
- struct azx *chip = bus->private_data;
- return chip->rirb.res[addr];
-}
-
-/*
- * The below are the main callbacks from hda_codec.
- *
- * They are just the skeleton to call sub-callbacks according to the
- * current setting of chip->single_cmd.
- */
-
-/* send a command */
-static int azx_send_cmd(struct hda_bus *bus, unsigned int val)
-{
- struct azx *chip = bus->private_data;
-
- chip->last_cmd[azx_command_addr(val)] = val;
- if (chip->single_cmd)
- return azx_single_send_cmd(bus, val);
- else
- return azx_corb_send_cmd(bus, val);
-}
-
-/* get a response */
-static unsigned int azx_get_response(struct hda_bus *bus,
- unsigned int addr)
-{
- struct azx *chip = bus->private_data;
- if (chip->single_cmd)
- return azx_single_get_response(bus, addr);
- else
- return azx_rirb_get_response(bus, addr);
-}
-
-#ifdef CONFIG_SND_HDA_POWER_SAVE
-static void azx_power_notify(struct hda_bus *bus);
#endif
-/* reset codec link */
-static int azx_reset(struct azx *chip, int full_reset)
-{
- int count;
-
- if (!full_reset)
- goto __skip;
-
- /* clear STATESTS */
- azx_writeb(chip, STATESTS, STATESTS_INT_MASK);
-
- /* reset controller */
- azx_writel(chip, GCTL, azx_readl(chip, GCTL) & ~ICH6_GCTL_RESET);
-
- count = 50;
- while (azx_readb(chip, GCTL) && --count)
- msleep(1);
-
- /* delay for >= 100us for codec PLL to settle per spec
- * Rev 0.9 section 5.5.1
- */
- msleep(1);
-
- /* Bring controller out of reset */
- azx_writeb(chip, GCTL, azx_readb(chip, GCTL) | ICH6_GCTL_RESET);
-
- count = 50;
- while (!azx_readb(chip, GCTL) && --count)
- msleep(1);
-
- /* Brent Chartrand said to wait >= 540us for codecs to initialize */
- msleep(1);
-
- __skip:
- /* check to see if controller is ready */
- if (!azx_readb(chip, GCTL)) {
- snd_printd(SFX "azx_reset: controller not ready!\n");
- return -EBUSY;
- }
-
- /* Accept unsolicited responses */
- if (!chip->single_cmd)
- azx_writel(chip, GCTL, azx_readl(chip, GCTL) |
- ICH6_GCTL_UNSOL);
-
- /* detect codecs */
- if (!chip->codec_mask) {
- chip->codec_mask = azx_readw(chip, STATESTS);
- snd_printdd(SFX "codec_mask = 0x%x\n", chip->codec_mask);
- }
-
- return 0;
-}
-
-
-/*
- * Lowlevel interface
- */
-
-/* enable interrupts */
-static void azx_int_enable(struct azx *chip)
-{
- /* enable controller CIE and GIE */
- azx_writel(chip, INTCTL, azx_readl(chip, INTCTL) |
- ICH6_INT_CTRL_EN | ICH6_INT_GLOBAL_EN);
+ pages = (dmab->bytes + PAGE_SIZE - 1) >> PAGE_SHIFT;
+ if (on)
+ set_memory_wc((unsigned long)dmab->area, pages);
+ else
+ set_memory_wb((unsigned long)dmab->area, pages);
}
-/* disable interrupts */
-static void azx_int_disable(struct azx *chip)
+static inline void mark_pages_wc(struct azx *chip, struct snd_dma_buffer *buf,
+ bool on)
{
- int i;
-
- /* disable interrupts in stream descriptor */
- for (i = 0; i < chip->num_streams; i++) {
- struct azx_dev *azx_dev = &chip->azx_dev[i];
- azx_sd_writeb(azx_dev, SD_CTL,
- azx_sd_readb(azx_dev, SD_CTL) & ~SD_INT_MASK);
- }
-
- /* disable SIE for all streams */
- azx_writeb(chip, INTCTL, 0);
-
- /* disable controller CIE and GIE */
- azx_writel(chip, INTCTL, azx_readl(chip, INTCTL) &
- ~(ICH6_INT_CTRL_EN | ICH6_INT_GLOBAL_EN));
+ __mark_pages_wc(chip, buf, on);
}
-
-/* clear interrupts */
-static void azx_int_clear(struct azx *chip)
+static inline void mark_runtime_wc(struct azx *chip, struct azx_dev *azx_dev,
+ struct snd_pcm_substream *substream, bool on)
{
- int i;
-
- /* clear stream status */
- for (i = 0; i < chip->num_streams; i++) {
- struct azx_dev *azx_dev = &chip->azx_dev[i];
- azx_sd_writeb(azx_dev, SD_STS, SD_INT_MASK);
+ if (azx_dev->wc_marked != on) {
+ __mark_pages_wc(chip, snd_pcm_get_dma_buf(substream), on);
+ azx_dev->wc_marked = on;
}
-
- /* clear STATESTS */
- azx_writeb(chip, STATESTS, STATESTS_INT_MASK);
-
- /* clear rirb status */
- azx_writeb(chip, RIRBSTS, RIRB_INT_MASK);
-
- /* clear int status */
- azx_writel(chip, INTSTS, ICH6_INT_CTRL_EN | ICH6_INT_ALL_STREAM);
}
-
-/* start a stream */
-static void azx_stream_start(struct azx *chip, struct azx_dev *azx_dev)
-{
- /*
- * Before stream start, initialize parameter
- */
- azx_dev->insufficient = 1;
-
- /* enable SIE */
- azx_writel(chip, INTCTL,
- azx_readl(chip, INTCTL) | (1 << azx_dev->index));
- /* set DMA start and interrupt mask */
- azx_sd_writeb(azx_dev, SD_CTL, azx_sd_readb(azx_dev, SD_CTL) |
- SD_CTL_DMA_START | SD_INT_MASK);
-}
-
-/* stop DMA */
-static void azx_stream_clear(struct azx *chip, struct azx_dev *azx_dev)
+#else
+/* NOP for other archs */
+static inline void mark_pages_wc(struct azx *chip, struct snd_dma_buffer *buf,
+ bool on)
{
- azx_sd_writeb(azx_dev, SD_CTL, azx_sd_readb(azx_dev, SD_CTL) &
- ~(SD_CTL_DMA_START | SD_INT_MASK));
- azx_sd_writeb(azx_dev, SD_STS, SD_INT_MASK); /* to be sure */
}
-
-/* stop a stream */
-static void azx_stream_stop(struct azx *chip, struct azx_dev *azx_dev)
+static inline void mark_runtime_wc(struct azx *chip, struct azx_dev *azx_dev,
+ struct snd_pcm_substream *substream, bool on)
{
- azx_stream_clear(chip, azx_dev);
- /* disable SIE */
- azx_writel(chip, INTCTL,
- azx_readl(chip, INTCTL) & ~(1 << azx_dev->index));
}
+#endif
-
-/*
- * reset and start the controller registers
- */
-static void azx_init_chip(struct azx *chip, int full_reset)
-{
- if (chip->initialized)
- return;
-
- /* reset controller */
- azx_reset(chip, full_reset);
-
- /* initialize interrupts */
- azx_int_clear(chip);
- azx_int_enable(chip);
-
- /* initialize the codec command I/O */
- if (!chip->single_cmd)
- azx_init_cmd_io(chip);
-
- /* program the position buffer */
- azx_writel(chip, DPLBASE, (u32)chip->posbuf.addr);
- azx_writel(chip, DPUBASE, upper_32_bits(chip->posbuf.addr));
-
- chip->initialized = 1;
-}
+static int azx_acquire_irq(struct azx *chip, int do_disconnect);
/*
* initialize the PCI registers
@@ -1047,27 +365,32 @@ static void update_pci_byte(struct pci_dev *pci, unsigned int reg,
static void azx_init_pci(struct azx *chip)
{
- unsigned short snoop;
-
/* Clear bits 0-2 of PCI register TCSEL (at offset 0x44)
* TCSEL == Traffic Class Select Register, which sets PCI express QOS
* Ensuring these bits are 0 clears playback static on some HD Audio
* codecs.
* The PCI register TCSEL is defined in the Intel manuals.
*/
- if (chip->driver_type != AZX_DRIVER_ATI &&
- chip->driver_type != AZX_DRIVER_ATIHDMI)
+ if (!(chip->driver_caps & AZX_DCAPS_NO_TCSEL)) {
+ dev_dbg(chip->card->dev, "Clearing TCSEL\n");
update_pci_byte(chip->pci, ICH6_PCIREG_TCSEL, 0x07, 0);
+ }
- switch (chip->driver_type) {
- case AZX_DRIVER_ATI:
- /* For ATI SB450 azalia HD audio, we need to enable snoop */
+ /* For ATI SB450/600/700/800/900 and AMD Hudson azalia HD audio,
+ * we need to enable snoop.
+ */
+ if (chip->driver_caps & AZX_DCAPS_ATI_SNOOP) {
+ dev_dbg(chip->card->dev, "Setting ATI snoop: %d\n",
+ azx_snoop(chip));
update_pci_byte(chip->pci,
- ATI_SB450_HDAUDIO_MISC_CNTR2_ADDR,
- 0x07, ATI_SB450_HDAUDIO_ENABLE_SNOOP);
- break;
- case AZX_DRIVER_NVIDIA:
- /* For NVIDIA HDA, enable snoop */
+ ATI_SB450_HDAUDIO_MISC_CNTR2_ADDR, 0x07,
+ azx_snoop(chip) ? ATI_SB450_HDAUDIO_ENABLE_SNOOP : 0);
+ }
+
+ /* For NVIDIA HDA, enable snoop */
+ if (chip->driver_caps & AZX_DCAPS_NVIDIA_SNOOP) {
+ dev_dbg(chip->card->dev, "Setting Nvidia snoop: %d\n",
+ azx_snoop(chip));
update_pci_byte(chip->pci,
NVIDIA_HDA_TRANSREG_ADDR,
0x0f, NVIDIA_HDA_ENABLE_COHBITS);
@@ -1077,826 +400,46 @@ static void azx_init_pci(struct azx *chip)
update_pci_byte(chip->pci,
NVIDIA_HDA_OSTRM_COH,
0x01, NVIDIA_HDA_ENABLE_COHBIT);
- break;
- case AZX_DRIVER_SCH:
- case AZX_DRIVER_PCH:
+ }
+
+ /* Enable SCH/PCH snoop if needed */
+ if (chip->driver_caps & AZX_DCAPS_SCH_SNOOP) {
+ unsigned short snoop;
pci_read_config_word(chip->pci, INTEL_SCH_HDA_DEVC, &snoop);
- if (snoop & INTEL_SCH_HDA_DEVC_NOSNOOP) {
- pci_write_config_word(chip->pci, INTEL_SCH_HDA_DEVC,
- snoop & (~INTEL_SCH_HDA_DEVC_NOSNOOP));
+ if ((!azx_snoop(chip) && !(snoop & INTEL_SCH_HDA_DEVC_NOSNOOP)) ||
+ (azx_snoop(chip) && (snoop & INTEL_SCH_HDA_DEVC_NOSNOOP))) {
+ snoop &= ~INTEL_SCH_HDA_DEVC_NOSNOOP;
+ if (!azx_snoop(chip))
+ snoop |= INTEL_SCH_HDA_DEVC_NOSNOOP;
+ pci_write_config_word(chip->pci, INTEL_SCH_HDA_DEVC, snoop);
pci_read_config_word(chip->pci,
INTEL_SCH_HDA_DEVC, &snoop);
- snd_printdd(SFX "HDA snoop disabled, enabling ... %s\n",
- (snoop & INTEL_SCH_HDA_DEVC_NOSNOOP)
- ? "Failed" : "OK");
}
- break;
-
+ dev_dbg(chip->card->dev, "SCH snoop: %s\n",
+ (snoop & INTEL_SCH_HDA_DEVC_NOSNOOP) ?
+ "Disabled" : "Enabled");
}
}
-
static int azx_position_ok(struct azx *chip, struct azx_dev *azx_dev);
-/*
- * interrupt handler
- */
-static irqreturn_t azx_interrupt(int irq, void *dev_id)
-{
- struct azx *chip = dev_id;
- struct azx_dev *azx_dev;
- u32 status;
- u8 sd_status;
- int i, ok;
-
- spin_lock(&chip->reg_lock);
-
- status = azx_readl(chip, INTSTS);
- if (status == 0) {
- spin_unlock(&chip->reg_lock);
- return IRQ_NONE;
- }
-
- for (i = 0; i < chip->num_streams; i++) {
- azx_dev = &chip->azx_dev[i];
- if (status & azx_dev->sd_int_sta_mask) {
- sd_status = azx_sd_readb(azx_dev, SD_STS);
- azx_sd_writeb(azx_dev, SD_STS, SD_INT_MASK);
- if (!azx_dev->substream || !azx_dev->running ||
- !(sd_status & SD_INT_COMPLETE))
- continue;
- /* check whether this IRQ is really acceptable */
- ok = azx_position_ok(chip, azx_dev);
- if (ok == 1) {
- azx_dev->irq_pending = 0;
- spin_unlock(&chip->reg_lock);
- snd_pcm_period_elapsed(azx_dev->substream);
- spin_lock(&chip->reg_lock);
- } else if (ok == 0 && chip->bus && chip->bus->workq) {
- /* bogus IRQ, process it later */
- azx_dev->irq_pending = 1;
- queue_work(chip->bus->workq,
- &chip->irq_pending_work);
- }
- }
- }
-
- /* clear rirb int */
- status = azx_readb(chip, RIRBSTS);
- if (status & RIRB_INT_MASK) {
- if (status & RIRB_INT_RESPONSE) {
- if (chip->driver_type == AZX_DRIVER_CTX)
- udelay(80);
- azx_update_rirb(chip);
- }
- azx_writeb(chip, RIRBSTS, RIRB_INT_MASK);
- }
-
-#if 0
- /* clear state status int */
- if (azx_readb(chip, STATESTS) & 0x04)
- azx_writeb(chip, STATESTS, 0x04);
-#endif
- spin_unlock(&chip->reg_lock);
-
- return IRQ_HANDLED;
-}
-
-
-/*
- * set up a BDL entry
- */
-static int setup_bdle(struct snd_pcm_substream *substream,
- struct azx_dev *azx_dev, u32 **bdlp,
- int ofs, int size, int with_ioc)
-{
- u32 *bdl = *bdlp;
-
- while (size > 0) {
- dma_addr_t addr;
- int chunk;
-
- if (azx_dev->frags >= AZX_MAX_BDL_ENTRIES)
- return -EINVAL;
-
- addr = snd_pcm_sgbuf_get_addr(substream, ofs);
- /* program the address field of the BDL entry */
- bdl[0] = cpu_to_le32((u32)addr);
- bdl[1] = cpu_to_le32(upper_32_bits(addr));
- /* program the size field of the BDL entry */
- chunk = snd_pcm_sgbuf_get_chunk_size(substream, ofs, size);
- bdl[2] = cpu_to_le32(chunk);
- /* program the IOC to enable interrupt
- * only when the whole fragment is processed
- */
- size -= chunk;
- bdl[3] = (size || !with_ioc) ? 0 : cpu_to_le32(0x01);
- bdl += 4;
- azx_dev->frags++;
- ofs += chunk;
- }
- *bdlp = bdl;
- return ofs;
-}
-
-/*
- * set up BDL entries
- */
-static int azx_setup_periods(struct azx *chip,
- struct snd_pcm_substream *substream,
- struct azx_dev *azx_dev)
-{
- u32 *bdl;
- int i, ofs, periods, period_bytes;
- int pos_adj;
-
- /* reset BDL address */
- azx_sd_writel(azx_dev, SD_BDLPL, 0);
- azx_sd_writel(azx_dev, SD_BDLPU, 0);
-
- period_bytes = azx_dev->period_bytes;
- periods = azx_dev->bufsize / period_bytes;
-
- /* program the initial BDL entries */
- bdl = (u32 *)azx_dev->bdl.area;
- ofs = 0;
- azx_dev->frags = 0;
- pos_adj = bdl_pos_adj[chip->dev_index];
- if (pos_adj > 0) {
- struct snd_pcm_runtime *runtime = substream->runtime;
- int pos_align = pos_adj;
- pos_adj = (pos_adj * runtime->rate + 47999) / 48000;
- if (!pos_adj)
- pos_adj = pos_align;
- else
- pos_adj = ((pos_adj + pos_align - 1) / pos_align) *
- pos_align;
- pos_adj = frames_to_bytes(runtime, pos_adj);
- if (pos_adj >= period_bytes) {
- snd_printk(KERN_WARNING SFX "Too big adjustment %d\n",
- bdl_pos_adj[chip->dev_index]);
- pos_adj = 0;
- } else {
- ofs = setup_bdle(substream, azx_dev,
- &bdl, ofs, pos_adj,
- !substream->runtime->no_period_wakeup);
- if (ofs < 0)
- goto error;
- }
- } else
- pos_adj = 0;
- for (i = 0; i < periods; i++) {
- if (i == periods - 1 && pos_adj)
- ofs = setup_bdle(substream, azx_dev, &bdl, ofs,
- period_bytes - pos_adj, 0);
- else
- ofs = setup_bdle(substream, azx_dev, &bdl, ofs,
- period_bytes,
- !substream->runtime->no_period_wakeup);
- if (ofs < 0)
- goto error;
- }
- return 0;
-
- error:
- snd_printk(KERN_ERR SFX "Too many BDL entries: buffer=%d, period=%d\n",
- azx_dev->bufsize, period_bytes);
- return -EINVAL;
-}
-
-/* reset stream */
-static void azx_stream_reset(struct azx *chip, struct azx_dev *azx_dev)
-{
- unsigned char val;
- int timeout;
-
- azx_stream_clear(chip, azx_dev);
-
- azx_sd_writeb(azx_dev, SD_CTL, azx_sd_readb(azx_dev, SD_CTL) |
- SD_CTL_STREAM_RESET);
- udelay(3);
- timeout = 300;
- while (!((val = azx_sd_readb(azx_dev, SD_CTL)) & SD_CTL_STREAM_RESET) &&
- --timeout)
- ;
- val &= ~SD_CTL_STREAM_RESET;
- azx_sd_writeb(azx_dev, SD_CTL, val);
- udelay(3);
-
- timeout = 300;
- /* waiting for hardware to report that the stream is out of reset */
- while (((val = azx_sd_readb(azx_dev, SD_CTL)) & SD_CTL_STREAM_RESET) &&
- --timeout)
- ;
-
- /* reset first position - may not be synced with hw at this time */
- *azx_dev->posbuf = 0;
-}
-
-/*
- * set up the SD for streaming
- */
-static int azx_setup_controller(struct azx *chip, struct azx_dev *azx_dev)
-{
- /* make sure the run bit is zero for SD */
- azx_stream_clear(chip, azx_dev);
- /* program the stream_tag */
- azx_sd_writel(azx_dev, SD_CTL,
- (azx_sd_readl(azx_dev, SD_CTL) & ~SD_CTL_STREAM_TAG_MASK)|
- (azx_dev->stream_tag << SD_CTL_STREAM_TAG_SHIFT));
-
- /* program the length of samples in cyclic buffer */
- azx_sd_writel(azx_dev, SD_CBL, azx_dev->bufsize);
-
- /* program the stream format */
- /* this value needs to be the same as the one programmed */
- azx_sd_writew(azx_dev, SD_FORMAT, azx_dev->format_val);
-
- /* program the stream LVI (last valid index) of the BDL */
- azx_sd_writew(azx_dev, SD_LVI, azx_dev->frags - 1);
-
- /* program the BDL address */
- /* lower BDL address */
- azx_sd_writel(azx_dev, SD_BDLPL, (u32)azx_dev->bdl.addr);
- /* upper BDL address */
- azx_sd_writel(azx_dev, SD_BDLPU, upper_32_bits(azx_dev->bdl.addr));
-
- /* enable the position buffer */
- if (chip->position_fix[0] != POS_FIX_LPIB ||
- chip->position_fix[1] != POS_FIX_LPIB) {
- if (!(azx_readl(chip, DPLBASE) & ICH6_DPLBASE_ENABLE))
- azx_writel(chip, DPLBASE,
- (u32)chip->posbuf.addr | ICH6_DPLBASE_ENABLE);
- }
-
- /* set the interrupt enable bits in the descriptor control register */
- azx_sd_writel(azx_dev, SD_CTL,
- azx_sd_readl(azx_dev, SD_CTL) | SD_INT_MASK);
-
- return 0;
-}
-
-/*
- * Probe the given codec address
- */
-static int probe_codec(struct azx *chip, int addr)
-{
- unsigned int cmd = (addr << 28) | (AC_NODE_ROOT << 20) |
- (AC_VERB_PARAMETERS << 8) | AC_PAR_VENDOR_ID;
- unsigned int res;
-
- mutex_lock(&chip->bus->cmd_mutex);
- chip->probing = 1;
- azx_send_cmd(chip->bus, cmd);
- res = azx_get_response(chip->bus, addr);
- chip->probing = 0;
- mutex_unlock(&chip->bus->cmd_mutex);
- if (res == -1)
- return -EIO;
- snd_printdd(SFX "codec #%d probed OK\n", addr);
- return 0;
-}
-
-static int azx_attach_pcm_stream(struct hda_bus *bus, struct hda_codec *codec,
- struct hda_pcm *cpcm);
-static void azx_stop_chip(struct azx *chip);
-
-static void azx_bus_reset(struct hda_bus *bus)
+/* called from IRQ */
+static int azx_position_check(struct azx *chip, struct azx_dev *azx_dev)
{
- struct azx *chip = bus->private_data;
-
- bus->in_reset = 1;
- azx_stop_chip(chip);
- azx_init_chip(chip, 1);
-#ifdef CONFIG_PM
- if (chip->initialized) {
- int i;
-
- for (i = 0; i < HDA_MAX_PCMS; i++)
- snd_pcm_suspend_all(chip->pcm[i]);
- snd_hda_suspend(chip->bus);
- snd_hda_resume(chip->bus);
- }
-#endif
- bus->in_reset = 0;
-}
-
-/*
- * Codec initialization
- */
-
-/* number of codec slots for each chipset: 0 = default slots (i.e. 4) */
-static unsigned int azx_max_codecs[AZX_NUM_DRIVERS] __devinitdata = {
- [AZX_DRIVER_NVIDIA] = 8,
- [AZX_DRIVER_TERA] = 1,
-};
-
-static int __devinit azx_codec_create(struct azx *chip, const char *model)
-{
- struct hda_bus_template bus_temp;
- int c, codecs, err;
- int max_slots;
-
- memset(&bus_temp, 0, sizeof(bus_temp));
- bus_temp.private_data = chip;
- bus_temp.modelname = model;
- bus_temp.pci = chip->pci;
- bus_temp.ops.command = azx_send_cmd;
- bus_temp.ops.get_response = azx_get_response;
- bus_temp.ops.attach_pcm = azx_attach_pcm_stream;
- bus_temp.ops.bus_reset = azx_bus_reset;
-#ifdef CONFIG_SND_HDA_POWER_SAVE
- bus_temp.power_save = &power_save;
- bus_temp.ops.pm_notify = azx_power_notify;
-#endif
-
- err = snd_hda_bus_new(chip->card, &bus_temp, &chip->bus);
- if (err < 0)
- return err;
-
- if (chip->driver_type == AZX_DRIVER_NVIDIA)
- chip->bus->needs_damn_long_delay = 1;
-
- codecs = 0;
- max_slots = azx_max_codecs[chip->driver_type];
- if (!max_slots)
- max_slots = AZX_DEFAULT_CODECS;
-
- /* First try to probe all given codec slots */
- for (c = 0; c < max_slots; c++) {
- if ((chip->codec_mask & (1 << c)) & chip->codec_probe_mask) {
- if (probe_codec(chip, c) < 0) {
- /* Some BIOSen give you wrong codec addresses
- * that don't exist
- */
- snd_printk(KERN_WARNING SFX
- "Codec #%d probe error; "
- "disabling it...\n", c);
- chip->codec_mask &= ~(1 << c);
- /* More badly, accessing to a non-existing
- * codec often screws up the controller chip,
- * and disturbs the further communications.
- * Thus if an error occurs during probing,
- * better to reset the controller chip to
- * get back to the sanity state.
- */
- azx_stop_chip(chip);
- azx_init_chip(chip, 1);
- }
- }
- }
-
- /* Then create codec instances */
- for (c = 0; c < max_slots; c++) {
- if ((chip->codec_mask & (1 << c)) & chip->codec_probe_mask) {
- struct hda_codec *codec;
- err = snd_hda_codec_new(chip->bus, c, &codec);
- if (err < 0)
- continue;
- codec->beep_mode = chip->beep_mode;
- codecs++;
- }
- }
- if (!codecs) {
- snd_printk(KERN_ERR SFX "no codecs initialized\n");
- return -ENXIO;
- }
- return 0;
-}
+ int ok;
-/* configure each codec instance */
-static int __devinit azx_codec_configure(struct azx *chip)
-{
- struct hda_codec *codec;
- list_for_each_entry(codec, &chip->bus->codec_list, list) {
- snd_hda_codec_configure(codec);
+ ok = azx_position_ok(chip, azx_dev);
+ if (ok == 1) {
+ azx_dev->irq_pending = 0;
+ return ok;
+ } else if (ok == 0 && chip->bus && chip->bus->workq) {
+ /* bogus IRQ, process it later */
+ azx_dev->irq_pending = 1;
+ queue_work(chip->bus->workq, &chip->irq_pending_work);
}
return 0;
}
-
-/*
- * PCM support
- */
-
-/* assign a stream for the PCM */
-static inline struct azx_dev *
-azx_assign_device(struct azx *chip, struct snd_pcm_substream *substream)
-{
- int dev, i, nums;
- struct azx_dev *res = NULL;
-
- if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
- dev = chip->playback_index_offset;
- nums = chip->playback_streams;
- } else {
- dev = chip->capture_index_offset;
- nums = chip->capture_streams;
- }
- for (i = 0; i < nums; i++, dev++)
- if (!chip->azx_dev[dev].opened) {
- res = &chip->azx_dev[dev];
- if (res->device == substream->pcm->device)
- break;
- }
- if (res) {
- res->opened = 1;
- res->device = substream->pcm->device;
- }
- return res;
-}
-
-/* release the assigned stream */
-static inline void azx_release_device(struct azx_dev *azx_dev)
-{
- azx_dev->opened = 0;
-}
-
-static struct snd_pcm_hardware azx_pcm_hw = {
- .info = (SNDRV_PCM_INFO_MMAP |
- SNDRV_PCM_INFO_INTERLEAVED |
- SNDRV_PCM_INFO_BLOCK_TRANSFER |
- SNDRV_PCM_INFO_MMAP_VALID |
- /* No full-resume yet implemented */
- /* SNDRV_PCM_INFO_RESUME |*/
- SNDRV_PCM_INFO_PAUSE |
- SNDRV_PCM_INFO_SYNC_START |
- SNDRV_PCM_INFO_NO_PERIOD_WAKEUP),
- .formats = SNDRV_PCM_FMTBIT_S16_LE,
- .rates = SNDRV_PCM_RATE_48000,
- .rate_min = 48000,
- .rate_max = 48000,
- .channels_min = 2,
- .channels_max = 2,
- .buffer_bytes_max = AZX_MAX_BUF_SIZE,
- .period_bytes_min = 128,
- .period_bytes_max = AZX_MAX_BUF_SIZE / 2,
- .periods_min = 2,
- .periods_max = AZX_MAX_FRAG,
- .fifo_size = 0,
-};
-
-struct azx_pcm {
- struct azx *chip;
- struct hda_codec *codec;
- struct hda_pcm_stream *hinfo[2];
-};
-
-static int azx_pcm_open(struct snd_pcm_substream *substream)
-{
- struct azx_pcm *apcm = snd_pcm_substream_chip(substream);
- struct hda_pcm_stream *hinfo = apcm->hinfo[substream->stream];
- struct azx *chip = apcm->chip;
- struct azx_dev *azx_dev;
- struct snd_pcm_runtime *runtime = substream->runtime;
- unsigned long flags;
- int err;
-
- mutex_lock(&chip->open_mutex);
- azx_dev = azx_assign_device(chip, substream);
- if (azx_dev == NULL) {
- mutex_unlock(&chip->open_mutex);
- return -EBUSY;
- }
- runtime->hw = azx_pcm_hw;
- runtime->hw.channels_min = hinfo->channels_min;
- runtime->hw.channels_max = hinfo->channels_max;
- runtime->hw.formats = hinfo->formats;
- runtime->hw.rates = hinfo->rates;
- snd_pcm_limit_hw_rates(runtime);
- snd_pcm_hw_constraint_integer(runtime, SNDRV_PCM_HW_PARAM_PERIODS);
- snd_pcm_hw_constraint_step(runtime, 0, SNDRV_PCM_HW_PARAM_BUFFER_BYTES,
- 128);
- snd_pcm_hw_constraint_step(runtime, 0, SNDRV_PCM_HW_PARAM_PERIOD_BYTES,
- 128);
- snd_hda_power_up(apcm->codec);
- err = hinfo->ops.open(hinfo, apcm->codec, substream);
- if (err < 0) {
- azx_release_device(azx_dev);
- snd_hda_power_down(apcm->codec);
- mutex_unlock(&chip->open_mutex);
- return err;
- }
- snd_pcm_limit_hw_rates(runtime);
- /* sanity check */
- if (snd_BUG_ON(!runtime->hw.channels_min) ||
- snd_BUG_ON(!runtime->hw.channels_max) ||
- snd_BUG_ON(!runtime->hw.formats) ||
- snd_BUG_ON(!runtime->hw.rates)) {
- azx_release_device(azx_dev);
- hinfo->ops.close(hinfo, apcm->codec, substream);
- snd_hda_power_down(apcm->codec);
- mutex_unlock(&chip->open_mutex);
- return -EINVAL;
- }
- spin_lock_irqsave(&chip->reg_lock, flags);
- azx_dev->substream = substream;
- azx_dev->running = 0;
- spin_unlock_irqrestore(&chip->reg_lock, flags);
-
- runtime->private_data = azx_dev;
- snd_pcm_set_sync(substream);
- mutex_unlock(&chip->open_mutex);
- return 0;
-}
-
-static int azx_pcm_close(struct snd_pcm_substream *substream)
-{
- struct azx_pcm *apcm = snd_pcm_substream_chip(substream);
- struct hda_pcm_stream *hinfo = apcm->hinfo[substream->stream];
- struct azx *chip = apcm->chip;
- struct azx_dev *azx_dev = get_azx_dev(substream);
- unsigned long flags;
-
- mutex_lock(&chip->open_mutex);
- spin_lock_irqsave(&chip->reg_lock, flags);
- azx_dev->substream = NULL;
- azx_dev->running = 0;
- spin_unlock_irqrestore(&chip->reg_lock, flags);
- azx_release_device(azx_dev);
- hinfo->ops.close(hinfo, apcm->codec, substream);
- snd_hda_power_down(apcm->codec);
- mutex_unlock(&chip->open_mutex);
- return 0;
-}
-
-static int azx_pcm_hw_params(struct snd_pcm_substream *substream,
- struct snd_pcm_hw_params *hw_params)
-{
- struct azx_dev *azx_dev = get_azx_dev(substream);
-
- azx_dev->bufsize = 0;
- azx_dev->period_bytes = 0;
- azx_dev->format_val = 0;
- return snd_pcm_lib_malloc_pages(substream,
- params_buffer_bytes(hw_params));
-}
-
-static int azx_pcm_hw_free(struct snd_pcm_substream *substream)
-{
- struct azx_pcm *apcm = snd_pcm_substream_chip(substream);
- struct azx_dev *azx_dev = get_azx_dev(substream);
- struct hda_pcm_stream *hinfo = apcm->hinfo[substream->stream];
-
- /* reset BDL address */
- azx_sd_writel(azx_dev, SD_BDLPL, 0);
- azx_sd_writel(azx_dev, SD_BDLPU, 0);
- azx_sd_writel(azx_dev, SD_CTL, 0);
- azx_dev->bufsize = 0;
- azx_dev->period_bytes = 0;
- azx_dev->format_val = 0;
-
- snd_hda_codec_cleanup(apcm->codec, hinfo, substream);
-
- return snd_pcm_lib_free_pages(substream);
-}
-
-static int azx_pcm_prepare(struct snd_pcm_substream *substream)
-{
- struct azx_pcm *apcm = snd_pcm_substream_chip(substream);
- struct azx *chip = apcm->chip;
- struct azx_dev *azx_dev = get_azx_dev(substream);
- struct hda_pcm_stream *hinfo = apcm->hinfo[substream->stream];
- struct snd_pcm_runtime *runtime = substream->runtime;
- unsigned int bufsize, period_bytes, format_val, stream_tag;
- int err;
-
- azx_stream_reset(chip, azx_dev);
- format_val = snd_hda_calc_stream_format(runtime->rate,
- runtime->channels,
- runtime->format,
- hinfo->maxbps,
- apcm->codec->spdif_ctls);
- if (!format_val) {
- snd_printk(KERN_ERR SFX
- "invalid format_val, rate=%d, ch=%d, format=%d\n",
- runtime->rate, runtime->channels, runtime->format);
- return -EINVAL;
- }
-
- bufsize = snd_pcm_lib_buffer_bytes(substream);
- period_bytes = snd_pcm_lib_period_bytes(substream);
-
- snd_printdd(SFX "azx_pcm_prepare: bufsize=0x%x, format=0x%x\n",
- bufsize, format_val);
-
- if (bufsize != azx_dev->bufsize ||
- period_bytes != azx_dev->period_bytes ||
- format_val != azx_dev->format_val) {
- azx_dev->bufsize = bufsize;
- azx_dev->period_bytes = period_bytes;
- azx_dev->format_val = format_val;
- err = azx_setup_periods(chip, substream, azx_dev);
- if (err < 0)
- return err;
- }
-
- /* wallclk has 24Mhz clock source */
- azx_dev->period_wallclk = (((runtime->period_size * 24000) /
- runtime->rate) * 1000);
- azx_setup_controller(chip, azx_dev);
- if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
- azx_dev->fifo_size = azx_sd_readw(azx_dev, SD_FIFOSIZE) + 1;
- else
- azx_dev->fifo_size = 0;
-
- stream_tag = azx_dev->stream_tag;
- /* CA-IBG chips need the playback stream starting from 1 */
- if (chip->driver_type == AZX_DRIVER_CTX &&
- stream_tag > chip->capture_streams)
- stream_tag -= chip->capture_streams;
- return snd_hda_codec_prepare(apcm->codec, hinfo, stream_tag,
- azx_dev->format_val, substream);
-}
-
-static int azx_pcm_trigger(struct snd_pcm_substream *substream, int cmd)
-{
- struct azx_pcm *apcm = snd_pcm_substream_chip(substream);
- struct azx *chip = apcm->chip;
- struct azx_dev *azx_dev;
- struct snd_pcm_substream *s;
- int rstart = 0, start, nsync = 0, sbits = 0;
- int nwait, timeout;
-
- switch (cmd) {
- case SNDRV_PCM_TRIGGER_START:
- rstart = 1;
- case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
- case SNDRV_PCM_TRIGGER_RESUME:
- start = 1;
- break;
- case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
- case SNDRV_PCM_TRIGGER_SUSPEND:
- case SNDRV_PCM_TRIGGER_STOP:
- start = 0;
- break;
- default:
- return -EINVAL;
- }
-
- snd_pcm_group_for_each_entry(s, substream) {
- if (s->pcm->card != substream->pcm->card)
- continue;
- azx_dev = get_azx_dev(s);
- sbits |= 1 << azx_dev->index;
- nsync++;
- snd_pcm_trigger_done(s, substream);
- }
-
- spin_lock(&chip->reg_lock);
- if (nsync > 1) {
- /* first, set SYNC bits of corresponding streams */
- azx_writel(chip, SYNC, azx_readl(chip, SYNC) | sbits);
- }
- snd_pcm_group_for_each_entry(s, substream) {
- if (s->pcm->card != substream->pcm->card)
- continue;
- azx_dev = get_azx_dev(s);
- if (start) {
- azx_dev->start_wallclk = azx_readl(chip, WALLCLK);
- if (!rstart)
- azx_dev->start_wallclk -=
- azx_dev->period_wallclk;
- azx_stream_start(chip, azx_dev);
- } else {
- azx_stream_stop(chip, azx_dev);
- }
- azx_dev->running = start;
- }
- spin_unlock(&chip->reg_lock);
- if (start) {
- if (nsync == 1)
- return 0;
- /* wait until all FIFOs get ready */
- for (timeout = 5000; timeout; timeout--) {
- nwait = 0;
- snd_pcm_group_for_each_entry(s, substream) {
- if (s->pcm->card != substream->pcm->card)
- continue;
- azx_dev = get_azx_dev(s);
- if (!(azx_sd_readb(azx_dev, SD_STS) &
- SD_STS_FIFO_READY))
- nwait++;
- }
- if (!nwait)
- break;
- cpu_relax();
- }
- } else {
- /* wait until all RUN bits are cleared */
- for (timeout = 5000; timeout; timeout--) {
- nwait = 0;
- snd_pcm_group_for_each_entry(s, substream) {
- if (s->pcm->card != substream->pcm->card)
- continue;
- azx_dev = get_azx_dev(s);
- if (azx_sd_readb(azx_dev, SD_CTL) &
- SD_CTL_DMA_START)
- nwait++;
- }
- if (!nwait)
- break;
- cpu_relax();
- }
- }
- if (nsync > 1) {
- spin_lock(&chip->reg_lock);
- /* reset SYNC bits */
- azx_writel(chip, SYNC, azx_readl(chip, SYNC) & ~sbits);
- spin_unlock(&chip->reg_lock);
- }
- return 0;
-}
-
-/* get the current DMA position with correction on VIA chips */
-static unsigned int azx_via_get_position(struct azx *chip,
- struct azx_dev *azx_dev)
-{
- unsigned int link_pos, mini_pos, bound_pos;
- unsigned int mod_link_pos, mod_dma_pos, mod_mini_pos;
- unsigned int fifo_size;
-
- link_pos = azx_sd_readl(azx_dev, SD_LPIB);
- if (azx_dev->index >= 4) {
- /* Playback, no problem using link position */
- return link_pos;
- }
-
- /* Capture */
- /* For new chipset,
- * use mod to get the DMA position just like old chipset
- */
- mod_dma_pos = le32_to_cpu(*azx_dev->posbuf);
- mod_dma_pos %= azx_dev->period_bytes;
-
- /* azx_dev->fifo_size can't get FIFO size of in stream.
- * Get from base address + offset.
- */
- fifo_size = readw(chip->remap_addr + VIA_IN_STREAM0_FIFO_SIZE_OFFSET);
-
- if (azx_dev->insufficient) {
- /* Link position never gather than FIFO size */
- if (link_pos <= fifo_size)
- return 0;
-
- azx_dev->insufficient = 0;
- }
-
- if (link_pos <= fifo_size)
- mini_pos = azx_dev->bufsize + link_pos - fifo_size;
- else
- mini_pos = link_pos - fifo_size;
-
- /* Find nearest previous boudary */
- mod_mini_pos = mini_pos % azx_dev->period_bytes;
- mod_link_pos = link_pos % azx_dev->period_bytes;
- if (mod_link_pos >= fifo_size)
- bound_pos = link_pos - mod_link_pos;
- else if (mod_dma_pos >= mod_mini_pos)
- bound_pos = mini_pos - mod_mini_pos;
- else {
- bound_pos = mini_pos - mod_mini_pos + azx_dev->period_bytes;
- if (bound_pos >= azx_dev->bufsize)
- bound_pos = 0;
- }
-
- /* Calculate real DMA position we want */
- return bound_pos + mod_dma_pos;
-}
-
-static unsigned int azx_get_position(struct azx *chip,
- struct azx_dev *azx_dev)
-{
- unsigned int pos;
- int stream = azx_dev->substream->stream;
-
- switch (chip->position_fix[stream]) {
- case POS_FIX_LPIB:
- /* read LPIB */
- pos = azx_sd_readl(azx_dev, SD_LPIB);
- break;
- case POS_FIX_VIACOMBO:
- pos = azx_via_get_position(chip, azx_dev);
- break;
- default:
- /* use the position buffer */
- pos = le32_to_cpu(*azx_dev->posbuf);
- }
-
- if (pos >= azx_dev->bufsize)
- pos = 0;
- return pos;
-}
-
-static snd_pcm_uframes_t azx_pcm_pointer(struct snd_pcm_substream *substream)
-{
- struct azx_pcm *apcm = snd_pcm_substream_chip(substream);
- struct azx *chip = apcm->chip;
- struct azx_dev *azx_dev = get_azx_dev(substream);
- return bytes_to_frames(substream->runtime,
- azx_get_position(chip, azx_dev));
-}
-
/*
* Check whether the current DMA position is acceptable for updating
* periods. Returns non-zero if it's OK.
@@ -1910,24 +453,12 @@ static int azx_position_ok(struct azx *chip, struct azx_dev *azx_dev)
{
u32 wallclk;
unsigned int pos;
- int stream;
wallclk = azx_readl(chip, WALLCLK) - azx_dev->start_wallclk;
if (wallclk < (azx_dev->period_wallclk * 2) / 3)
return -1; /* bogus (too early) interrupt */
- stream = azx_dev->substream->stream;
- pos = azx_get_position(chip, azx_dev);
- if (chip->position_fix[stream] == POS_FIX_AUTO) {
- if (!pos) {
- printk(KERN_WARNING
- "hda-intel: Invalid position buffer, "
- "using LPIB read method instead.\n");
- chip->position_fix[stream] = POS_FIX_LPIB;
- pos = azx_get_position(chip, azx_dev);
- } else
- chip->position_fix[stream] = POS_FIX_POSBUF;
- }
+ pos = azx_get_position(chip, azx_dev, true);
if (WARN_ONCE(!azx_dev->period_bytes,
"hda-intel: zero azx_dev->period_bytes"))
@@ -1935,7 +466,7 @@ static int azx_position_ok(struct azx *chip, struct azx_dev *azx_dev)
if (wallclk < (azx_dev->period_wallclk * 5) / 4 &&
pos % azx_dev->period_bytes > azx_dev->period_bytes / 2)
/* NG - it's below the first next period boundary */
- return bdl_pos_adj[chip->dev_index] ? 0 : -1;
+ return chip->bdl_pos_adj[chip->dev_index] ? 0 : -1;
azx_dev->start_wallclk += wallclk;
return 1; /* OK, it's fine */
}
@@ -1949,10 +480,9 @@ static void azx_irq_pending_work(struct work_struct *work)
int i, pending, ok;
if (!chip->irq_pending_warned) {
- printk(KERN_WARNING
- "hda-intel: IRQ timing workaround is activated "
- "for card #%d. Suggest a bigger bdl_pos_adj.\n",
- chip->card->number);
+ dev_info(chip->card->dev,
+ "IRQ timing workaround is activated for card #%d. Suggest a bigger bdl_pos_adj.\n",
+ chip->card->number);
chip->irq_pending_warned = 1;
}
@@ -1994,118 +524,14 @@ static void azx_clear_irq_pending(struct azx *chip)
spin_unlock_irq(&chip->reg_lock);
}
-static struct snd_pcm_ops azx_pcm_ops = {
- .open = azx_pcm_open,
- .close = azx_pcm_close,
- .ioctl = snd_pcm_lib_ioctl,
- .hw_params = azx_pcm_hw_params,
- .hw_free = azx_pcm_hw_free,
- .prepare = azx_pcm_prepare,
- .trigger = azx_pcm_trigger,
- .pointer = azx_pcm_pointer,
- .page = snd_pcm_sgbuf_ops_page,
-};
-
-static void azx_pcm_free(struct snd_pcm *pcm)
-{
- struct azx_pcm *apcm = pcm->private_data;
- if (apcm) {
- apcm->chip->pcm[pcm->device] = NULL;
- kfree(apcm);
- }
-}
-
-static int
-azx_attach_pcm_stream(struct hda_bus *bus, struct hda_codec *codec,
- struct hda_pcm *cpcm)
-{
- struct azx *chip = bus->private_data;
- struct snd_pcm *pcm;
- struct azx_pcm *apcm;
- int pcm_dev = cpcm->device;
- int s, err;
-
- if (pcm_dev >= HDA_MAX_PCMS) {
- snd_printk(KERN_ERR SFX "Invalid PCM device number %d\n",
- pcm_dev);
- return -EINVAL;
- }
- if (chip->pcm[pcm_dev]) {
- snd_printk(KERN_ERR SFX "PCM %d already exists\n", pcm_dev);
- return -EBUSY;
- }
- err = snd_pcm_new(chip->card, cpcm->name, pcm_dev,
- cpcm->stream[SNDRV_PCM_STREAM_PLAYBACK].substreams,
- cpcm->stream[SNDRV_PCM_STREAM_CAPTURE].substreams,
- &pcm);
- if (err < 0)
- return err;
- strlcpy(pcm->name, cpcm->name, sizeof(pcm->name));
- apcm = kzalloc(sizeof(*apcm), GFP_KERNEL);
- if (apcm == NULL)
- return -ENOMEM;
- apcm->chip = chip;
- apcm->codec = codec;
- pcm->private_data = apcm;
- pcm->private_free = azx_pcm_free;
- if (cpcm->pcm_type == HDA_PCM_TYPE_MODEM)
- pcm->dev_class = SNDRV_PCM_CLASS_MODEM;
- chip->pcm[pcm_dev] = pcm;
- cpcm->pcm = pcm;
- for (s = 0; s < 2; s++) {
- apcm->hinfo[s] = &cpcm->stream[s];
- if (cpcm->stream[s].substreams)
- snd_pcm_set_ops(pcm, s, &azx_pcm_ops);
- }
- /* buffer pre-allocation */
- snd_pcm_lib_preallocate_pages_for_all(pcm, SNDRV_DMA_TYPE_DEV_SG,
- snd_dma_pci_data(chip->pci),
- 1024 * 64, 32 * 1024 * 1024);
- return 0;
-}
-
-/*
- * mixer creation - all stuff is implemented in hda module
- */
-static int __devinit azx_mixer_create(struct azx *chip)
-{
- return snd_hda_build_controls(chip->bus);
-}
-
-
-/*
- * initialize SD streams
- */
-static int __devinit azx_init_stream(struct azx *chip)
-{
- int i;
-
- /* initialize each stream (aka device)
- * assign the starting bdl address to each stream (device)
- * and initialize
- */
- for (i = 0; i < chip->num_streams; i++) {
- struct azx_dev *azx_dev = &chip->azx_dev[i];
- azx_dev->posbuf = (u32 __iomem *)(chip->posbuf.area + i * 8);
- /* offset: SDI0=0x80, SDI1=0xa0, ... SDO3=0x160 */
- azx_dev->sd_addr = chip->remap_addr + (0x20 * i + 0x80);
- /* int mask: SDI0=0x01, SDI1=0x02, ... SDO3=0x80 */
- azx_dev->sd_int_sta_mask = 1 << i;
- /* stream tag: must be non-zero and unique */
- azx_dev->index = i;
- azx_dev->stream_tag = i + 1;
- }
-
- return 0;
-}
-
static int azx_acquire_irq(struct azx *chip, int do_disconnect)
{
if (request_irq(chip->pci->irq, azx_interrupt,
chip->msi ? 0 : IRQF_SHARED,
- "hda_intel", chip)) {
- printk(KERN_ERR "hda-intel: unable to grab IRQ %d, "
- "disabling device\n", chip->pci->irq);
+ KBUILD_MODNAME, chip)) {
+ dev_err(chip->card->dev,
+ "unable to grab IRQ %d, disabling device\n",
+ chip->pci->irq);
if (do_disconnect)
snd_card_disconnect(chip->card);
return -1;
@@ -2115,99 +541,105 @@ static int azx_acquire_irq(struct azx *chip, int do_disconnect)
return 0;
}
+#ifdef CONFIG_PM
+static DEFINE_MUTEX(card_list_lock);
+static LIST_HEAD(card_list);
-static void azx_stop_chip(struct azx *chip)
+static void azx_add_card_list(struct azx *chip)
{
- if (!chip->initialized)
- return;
-
- /* disable interrupts */
- azx_int_disable(chip);
- azx_int_clear(chip);
-
- /* disable CORB/RIRB */
- azx_free_cmd_io(chip);
-
- /* disable position buffer */
- azx_writel(chip, DPLBASE, 0);
- azx_writel(chip, DPUBASE, 0);
+ mutex_lock(&card_list_lock);
+ list_add(&chip->list, &card_list);
+ mutex_unlock(&card_list_lock);
+}
- chip->initialized = 0;
+static void azx_del_card_list(struct azx *chip)
+{
+ mutex_lock(&card_list_lock);
+ list_del_init(&chip->list);
+ mutex_unlock(&card_list_lock);
}
-#ifdef CONFIG_SND_HDA_POWER_SAVE
-/* power-up/down the controller */
-static void azx_power_notify(struct hda_bus *bus)
+/* trigger power-save check at writing parameter */
+static int param_set_xint(const char *val, const struct kernel_param *kp)
{
- struct azx *chip = bus->private_data;
+ struct azx *chip;
struct hda_codec *c;
- int power_on = 0;
+ int prev = power_save;
+ int ret = param_set_int(val, kp);
- list_for_each_entry(c, &bus->codec_list, list) {
- if (c->power_on) {
- power_on = 1;
- break;
- }
+ if (ret || prev == power_save)
+ return ret;
+
+ mutex_lock(&card_list_lock);
+ list_for_each_entry(chip, &card_list, list) {
+ if (!chip->bus || chip->disabled)
+ continue;
+ list_for_each_entry(c, &chip->bus->codec_list, list)
+ snd_hda_power_sync(c);
}
- if (power_on)
- azx_init_chip(chip, 1);
- else if (chip->running && power_save_controller &&
- !bus->power_keep_link_on)
- azx_stop_chip(chip);
+ mutex_unlock(&card_list_lock);
+ return 0;
}
-#endif /* CONFIG_SND_HDA_POWER_SAVE */
+#else
+#define azx_add_card_list(chip) /* NOP */
+#define azx_del_card_list(chip) /* NOP */
+#endif /* CONFIG_PM */
-#ifdef CONFIG_PM
+#if defined(CONFIG_PM_SLEEP) || defined(SUPPORT_VGA_SWITCHEROO)
/*
* power management
*/
-
-static int snd_hda_codecs_inuse(struct hda_bus *bus)
+static int azx_suspend(struct device *dev)
{
- struct hda_codec *codec;
-
- list_for_each_entry(codec, &bus->codec_list, list) {
- if (snd_hda_codec_needs_resume(codec))
- return 1;
- }
- return 0;
-}
-
-static int azx_suspend(struct pci_dev *pci, pm_message_t state)
-{
- struct snd_card *card = pci_get_drvdata(pci);
+ struct pci_dev *pci = to_pci_dev(dev);
+ struct snd_card *card = dev_get_drvdata(dev);
struct azx *chip = card->private_data;
- int i;
+ struct azx_pcm *p;
+
+ if (chip->disabled || chip->init_failed)
+ return 0;
snd_power_change_state(card, SNDRV_CTL_POWER_D3hot);
azx_clear_irq_pending(chip);
- for (i = 0; i < HDA_MAX_PCMS; i++)
- snd_pcm_suspend_all(chip->pcm[i]);
+ list_for_each_entry(p, &chip->pcm_list, list)
+ snd_pcm_suspend_all(p->pcm);
if (chip->initialized)
snd_hda_suspend(chip->bus);
azx_stop_chip(chip);
+ azx_enter_link_reset(chip);
if (chip->irq >= 0) {
free_irq(chip->irq, chip);
chip->irq = -1;
}
+
if (chip->msi)
pci_disable_msi(chip->pci);
pci_disable_device(pci);
pci_save_state(pci);
- pci_set_power_state(pci, pci_choose_state(pci, state));
+ pci_set_power_state(pci, PCI_D3hot);
+ if (chip->driver_caps & AZX_DCAPS_I915_POWERWELL)
+ hda_display_power(false);
return 0;
}
-static int azx_resume(struct pci_dev *pci)
+static int azx_resume(struct device *dev)
{
- struct snd_card *card = pci_get_drvdata(pci);
+ struct pci_dev *pci = to_pci_dev(dev);
+ struct snd_card *card = dev_get_drvdata(dev);
struct azx *chip = card->private_data;
+ if (chip->disabled || chip->init_failed)
+ return 0;
+
+ if (chip->driver_caps & AZX_DCAPS_I915_POWERWELL) {
+ hda_display_power(true);
+ haswell_set_bclk(chip);
+ }
pci_set_power_state(pci, PCI_D0);
pci_restore_state(pci);
if (pci_enable_device(pci) < 0) {
- printk(KERN_ERR "hda-intel: pci_enable_device failed, "
- "disabling device\n");
+ dev_err(chip->card->dev,
+ "pci_enable_device failed, disabling device\n");
snd_card_disconnect(card);
return -EIO;
}
@@ -2219,13 +651,105 @@ static int azx_resume(struct pci_dev *pci)
return -EIO;
azx_init_pci(chip);
- if (snd_hda_codecs_inuse(chip->bus))
- azx_init_chip(chip, 1);
+ azx_init_chip(chip, true);
snd_hda_resume(chip->bus);
snd_power_change_state(card, SNDRV_CTL_POWER_D0);
return 0;
}
+#endif /* CONFIG_PM_SLEEP || SUPPORT_VGA_SWITCHEROO */
+
+#ifdef CONFIG_PM_RUNTIME
+static int azx_runtime_suspend(struct device *dev)
+{
+ struct snd_card *card = dev_get_drvdata(dev);
+ struct azx *chip = card->private_data;
+
+ if (chip->disabled || chip->init_failed)
+ return 0;
+
+ if (!(chip->driver_caps & AZX_DCAPS_PM_RUNTIME))
+ return 0;
+
+ /* enable controller wake up event */
+ azx_writew(chip, WAKEEN, azx_readw(chip, WAKEEN) |
+ STATESTS_INT_MASK);
+
+ azx_stop_chip(chip);
+ azx_enter_link_reset(chip);
+ azx_clear_irq_pending(chip);
+ if (chip->driver_caps & AZX_DCAPS_I915_POWERWELL)
+ hda_display_power(false);
+
+ return 0;
+}
+
+static int azx_runtime_resume(struct device *dev)
+{
+ struct snd_card *card = dev_get_drvdata(dev);
+ struct azx *chip = card->private_data;
+ struct hda_bus *bus;
+ struct hda_codec *codec;
+ int status;
+
+ if (chip->disabled || chip->init_failed)
+ return 0;
+
+ if (!(chip->driver_caps & AZX_DCAPS_PM_RUNTIME))
+ return 0;
+
+ if (chip->driver_caps & AZX_DCAPS_I915_POWERWELL) {
+ hda_display_power(true);
+ haswell_set_bclk(chip);
+ }
+
+ /* Read STATESTS before controller reset */
+ status = azx_readw(chip, STATESTS);
+
+ azx_init_pci(chip);
+ azx_init_chip(chip, true);
+
+ bus = chip->bus;
+ if (status && bus) {
+ list_for_each_entry(codec, &bus->codec_list, list)
+ if (status & (1 << codec->addr))
+ queue_delayed_work(codec->bus->workq,
+ &codec->jackpoll_work, codec->jackpoll_interval);
+ }
+
+ /* disable controller Wake Up event*/
+ azx_writew(chip, WAKEEN, azx_readw(chip, WAKEEN) &
+ ~STATESTS_INT_MASK);
+
+ return 0;
+}
+
+static int azx_runtime_idle(struct device *dev)
+{
+ struct snd_card *card = dev_get_drvdata(dev);
+ struct azx *chip = card->private_data;
+
+ if (chip->disabled || chip->init_failed)
+ return 0;
+
+ if (!power_save_controller ||
+ !(chip->driver_caps & AZX_DCAPS_PM_RUNTIME))
+ return -EBUSY;
+
+ return 0;
+}
+
+#endif /* CONFIG_PM_RUNTIME */
+
+#ifdef CONFIG_PM
+static const struct dev_pm_ops azx_pm = {
+ SET_SYSTEM_SLEEP_PM_OPS(azx_suspend, azx_resume)
+ SET_RUNTIME_PM_OPS(azx_runtime_suspend, azx_runtime_resume, azx_runtime_idle)
+};
+
+#define AZX_PM_OPS &azx_pm
+#else
+#define AZX_PM_OPS NULL
#endif /* CONFIG_PM */
@@ -2252,15 +776,146 @@ static void azx_notifier_unregister(struct azx *chip)
unregister_reboot_notifier(&chip->reboot_notifier);
}
+static int azx_probe_continue(struct azx *chip);
+
+#ifdef SUPPORT_VGA_SWITCHEROO
+static struct pci_dev *get_bound_vga(struct pci_dev *pci);
+
+static void azx_vs_set_state(struct pci_dev *pci,
+ enum vga_switcheroo_state state)
+{
+ struct snd_card *card = pci_get_drvdata(pci);
+ struct azx *chip = card->private_data;
+ bool disabled;
+
+ wait_for_completion(&chip->probe_wait);
+ if (chip->init_failed)
+ return;
+
+ disabled = (state == VGA_SWITCHEROO_OFF);
+ if (chip->disabled == disabled)
+ return;
+
+ if (!chip->bus) {
+ chip->disabled = disabled;
+ if (!disabled) {
+ dev_info(chip->card->dev,
+ "Start delayed initialization\n");
+ if (azx_probe_continue(chip) < 0) {
+ dev_err(chip->card->dev, "initialization error\n");
+ chip->init_failed = true;
+ }
+ }
+ } else {
+ dev_info(chip->card->dev, "%s via VGA-switcheroo\n",
+ disabled ? "Disabling" : "Enabling");
+ if (disabled) {
+ pm_runtime_put_sync_suspend(card->dev);
+ azx_suspend(card->dev);
+ /* when we get suspended by vga switcheroo we end up in D3cold,
+ * however we have no ACPI handle, so pci/acpi can't put us there,
+ * put ourselves there */
+ pci->current_state = PCI_D3cold;
+ chip->disabled = true;
+ if (snd_hda_lock_devices(chip->bus))
+ dev_warn(chip->card->dev,
+ "Cannot lock devices!\n");
+ } else {
+ snd_hda_unlock_devices(chip->bus);
+ pm_runtime_get_noresume(card->dev);
+ chip->disabled = false;
+ azx_resume(card->dev);
+ }
+ }
+}
+
+static bool azx_vs_can_switch(struct pci_dev *pci)
+{
+ struct snd_card *card = pci_get_drvdata(pci);
+ struct azx *chip = card->private_data;
+
+ wait_for_completion(&chip->probe_wait);
+ if (chip->init_failed)
+ return false;
+ if (chip->disabled || !chip->bus)
+ return true;
+ if (snd_hda_lock_devices(chip->bus))
+ return false;
+ snd_hda_unlock_devices(chip->bus);
+ return true;
+}
+
+static void init_vga_switcheroo(struct azx *chip)
+{
+ struct pci_dev *p = get_bound_vga(chip->pci);
+ if (p) {
+ dev_info(chip->card->dev,
+ "Handle VGA-switcheroo audio client\n");
+ chip->use_vga_switcheroo = 1;
+ pci_dev_put(p);
+ }
+}
+
+static const struct vga_switcheroo_client_ops azx_vs_ops = {
+ .set_gpu_state = azx_vs_set_state,
+ .can_switch = azx_vs_can_switch,
+};
+
+static int register_vga_switcheroo(struct azx *chip)
+{
+ int err;
+
+ if (!chip->use_vga_switcheroo)
+ return 0;
+ /* FIXME: currently only handling DIS controller
+ * is there any machine with two switchable HDMI audio controllers?
+ */
+ err = vga_switcheroo_register_audio_client(chip->pci, &azx_vs_ops,
+ VGA_SWITCHEROO_DIS,
+ chip->bus != NULL);
+ if (err < 0)
+ return err;
+ chip->vga_switcheroo_registered = 1;
+
+ /* register as an optimus hdmi audio power domain */
+ vga_switcheroo_init_domain_pm_optimus_hdmi_audio(chip->card->dev,
+ &chip->hdmi_pm_domain);
+ return 0;
+}
+#else
+#define init_vga_switcheroo(chip) /* NOP */
+#define register_vga_switcheroo(chip) 0
+#define check_hdmi_disabled(pci) false
+#endif /* SUPPORT_VGA_SWITCHER */
+
/*
* destructor
*/
static int azx_free(struct azx *chip)
{
+ struct pci_dev *pci = chip->pci;
+ struct hda_intel *hda = container_of(chip, struct hda_intel, chip);
+
int i;
+ if ((chip->driver_caps & AZX_DCAPS_PM_RUNTIME)
+ && chip->running)
+ pm_runtime_get_noresume(&pci->dev);
+
+ azx_del_card_list(chip);
+
azx_notifier_unregister(chip);
+ chip->init_failed = 1; /* to be sure */
+ complete_all(&chip->probe_wait);
+
+ if (use_vga_switcheroo(chip)) {
+ if (chip->disabled && chip->bus)
+ snd_hda_unlock_devices(chip->bus);
+ if (chip->vga_switcheroo_registered)
+ vga_switcheroo_unregister_client(chip->pci);
+ }
+
if (chip->initialized) {
azx_clear_irq_pending(chip);
for (i = 0; i < chip->num_streams; i++)
@@ -2275,19 +930,20 @@ static int azx_free(struct azx *chip)
if (chip->remap_addr)
iounmap(chip->remap_addr);
- if (chip->azx_dev) {
- for (i = 0; i < chip->num_streams; i++)
- if (chip->azx_dev[i].bdl.area)
- snd_dma_free_pages(&chip->azx_dev[i].bdl);
- }
- if (chip->rb.area)
- snd_dma_free_pages(&chip->rb);
- if (chip->posbuf.area)
- snd_dma_free_pages(&chip->posbuf);
- pci_release_regions(chip->pci);
+ azx_free_stream_pages(chip);
+ if (chip->region_requested)
+ pci_release_regions(chip->pci);
pci_disable_device(chip->pci);
kfree(chip->azx_dev);
- kfree(chip);
+#ifdef CONFIG_SND_HDA_PATCH_LOADER
+ if (chip->fw)
+ release_firmware(chip->fw);
+#endif
+ if (chip->driver_caps & AZX_DCAPS_I915_POWERWELL) {
+ hda_display_power(false);
+ hda_i915_exit();
+ }
+ kfree(hda);
return 0;
}
@@ -2297,70 +953,105 @@ static int azx_dev_free(struct snd_device *device)
return azx_free(device->device_data);
}
+#ifdef SUPPORT_VGA_SWITCHEROO
+/*
+ * Check of disabled HDMI controller by vga-switcheroo
+ */
+static struct pci_dev *get_bound_vga(struct pci_dev *pci)
+{
+ struct pci_dev *p;
+
+ /* check only discrete GPU */
+ switch (pci->vendor) {
+ case PCI_VENDOR_ID_ATI:
+ case PCI_VENDOR_ID_AMD:
+ case PCI_VENDOR_ID_NVIDIA:
+ if (pci->devfn == 1) {
+ p = pci_get_domain_bus_and_slot(pci_domain_nr(pci->bus),
+ pci->bus->number, 0);
+ if (p) {
+ if ((p->class >> 8) == PCI_CLASS_DISPLAY_VGA)
+ return p;
+ pci_dev_put(p);
+ }
+ }
+ break;
+ }
+ return NULL;
+}
+
+static bool check_hdmi_disabled(struct pci_dev *pci)
+{
+ bool vga_inactive = false;
+ struct pci_dev *p = get_bound_vga(pci);
+
+ if (p) {
+ if (vga_switcheroo_get_client_state(p) == VGA_SWITCHEROO_OFF)
+ vga_inactive = true;
+ pci_dev_put(p);
+ }
+ return vga_inactive;
+}
+#endif /* SUPPORT_VGA_SWITCHEROO */
+
/*
* white/black-listing for position_fix
*/
-static struct snd_pci_quirk position_fix_list[] __devinitdata = {
- SND_PCI_QUIRK(0x1025, 0x009f, "Acer Aspire 5110", POS_FIX_LPIB),
- SND_PCI_QUIRK(0x1025, 0x026f, "Acer Aspire 5538", POS_FIX_LPIB),
+static struct snd_pci_quirk position_fix_list[] = {
SND_PCI_QUIRK(0x1028, 0x01cc, "Dell D820", POS_FIX_LPIB),
SND_PCI_QUIRK(0x1028, 0x01de, "Dell Precision 390", POS_FIX_LPIB),
- SND_PCI_QUIRK(0x1028, 0x01f6, "Dell Latitude 131L", POS_FIX_LPIB),
- SND_PCI_QUIRK(0x1028, 0x0470, "Dell Inspiron 1120", POS_FIX_LPIB),
SND_PCI_QUIRK(0x103c, 0x306d, "HP dv3", POS_FIX_LPIB),
SND_PCI_QUIRK(0x1043, 0x813d, "ASUS P5AD2", POS_FIX_LPIB),
SND_PCI_QUIRK(0x1043, 0x81b3, "ASUS", POS_FIX_LPIB),
SND_PCI_QUIRK(0x1043, 0x81e7, "ASUS M2V", POS_FIX_LPIB),
- SND_PCI_QUIRK(0x1043, 0x8410, "ASUS", POS_FIX_LPIB),
SND_PCI_QUIRK(0x104d, 0x9069, "Sony VPCS11V9E", POS_FIX_LPIB),
- SND_PCI_QUIRK(0x1106, 0x3288, "ASUS M2V-MX SE", POS_FIX_LPIB),
- SND_PCI_QUIRK(0x1179, 0xff10, "Toshiba A100-259", POS_FIX_LPIB),
+ SND_PCI_QUIRK(0x10de, 0xcb89, "Macbook Pro 7,1", POS_FIX_LPIB),
SND_PCI_QUIRK(0x1297, 0x3166, "Shuttle", POS_FIX_LPIB),
SND_PCI_QUIRK(0x1458, 0xa022, "ga-ma770-ud3", POS_FIX_LPIB),
SND_PCI_QUIRK(0x1462, 0x1002, "MSI Wind U115", POS_FIX_LPIB),
- SND_PCI_QUIRK(0x1565, 0x820f, "Biostar Microtech", POS_FIX_LPIB),
SND_PCI_QUIRK(0x1565, 0x8218, "Biostar Microtech", POS_FIX_LPIB),
SND_PCI_QUIRK(0x1849, 0x0888, "775Dual-VSTA", POS_FIX_LPIB),
SND_PCI_QUIRK(0x8086, 0x2503, "DG965OT AAD63733-203", POS_FIX_LPIB),
- SND_PCI_QUIRK(0x8086, 0xd601, "eMachines T5212", POS_FIX_LPIB),
{}
};
-static int __devinit check_position_fix(struct azx *chip, int fix)
+static int check_position_fix(struct azx *chip, int fix)
{
const struct snd_pci_quirk *q;
switch (fix) {
+ case POS_FIX_AUTO:
case POS_FIX_LPIB:
case POS_FIX_POSBUF:
case POS_FIX_VIACOMBO:
+ case POS_FIX_COMBO:
return fix;
}
q = snd_pci_quirk_lookup(chip->pci, position_fix_list);
if (q) {
- printk(KERN_INFO
- "hda_intel: position_fix set to %d "
- "for device %04x:%04x\n",
- q->value, q->subvendor, q->subdevice);
+ dev_info(chip->card->dev,
+ "position_fix set to %d for device %04x:%04x\n",
+ q->value, q->subvendor, q->subdevice);
return q->value;
}
/* Check VIA/ATI HD Audio Controller exist */
- switch (chip->driver_type) {
- case AZX_DRIVER_VIA:
- case AZX_DRIVER_ATI:
- /* Use link position directly, avoid any transfer problem. */
+ if (chip->driver_caps & AZX_DCAPS_POSFIX_VIA) {
+ dev_dbg(chip->card->dev, "Using VIACOMBO position fix\n");
return POS_FIX_VIACOMBO;
}
-
+ if (chip->driver_caps & AZX_DCAPS_POSFIX_LPIB) {
+ dev_dbg(chip->card->dev, "Using LPIB position fix\n");
+ return POS_FIX_LPIB;
+ }
return POS_FIX_AUTO;
}
/*
* black-lists for probe_mask
*/
-static struct snd_pci_quirk probe_mask_list[] __devinitdata = {
+static struct snd_pci_quirk probe_mask_list[] = {
/* Thinkpad often breaks the controller communication when accessing
* to the non-working (or non-existing) modem codec slot.
*/
@@ -2374,12 +1065,14 @@ static struct snd_pci_quirk probe_mask_list[] __devinitdata = {
/* forced codec slots */
SND_PCI_QUIRK(0x1043, 0x1262, "ASUS W5Fm", 0x103),
SND_PCI_QUIRK(0x1046, 0x1262, "ASUS W5F", 0x103),
+ /* WinFast VP200 H (Teradici) user reported broken communication */
+ SND_PCI_QUIRK(0x3a21, 0x040d, "WinFast VP200 H", 0x101),
{}
};
#define AZX_FORCE_CODEC_MASK 0x100
-static void __devinit check_probe_mask(struct azx *chip, int dev)
+static void check_probe_mask(struct azx *chip, int dev)
{
const struct snd_pci_quirk *q;
@@ -2387,10 +1080,9 @@ static void __devinit check_probe_mask(struct azx *chip, int dev)
if (chip->codec_probe_mask == -1) {
q = snd_pci_quirk_lookup(chip->pci, probe_mask_list);
if (q) {
- printk(KERN_INFO
- "hda_intel: probe_mask set to 0x%x "
- "for device %04x:%04x\n",
- q->value, q->subvendor, q->subdevice);
+ dev_info(chip->card->dev,
+ "probe_mask set to 0x%x for device %04x:%04x\n",
+ q->value, q->subvendor, q->subdevice);
chip->codec_probe_mask = q->value;
}
}
@@ -2399,24 +1091,29 @@ static void __devinit check_probe_mask(struct azx *chip, int dev)
if (chip->codec_probe_mask != -1 &&
(chip->codec_probe_mask & AZX_FORCE_CODEC_MASK)) {
chip->codec_mask = chip->codec_probe_mask & 0xff;
- printk(KERN_INFO "hda_intel: codec_mask forced to 0x%x\n",
- chip->codec_mask);
+ dev_info(chip->card->dev, "codec_mask forced to 0x%x\n",
+ chip->codec_mask);
}
}
/*
* white/black-list for enable_msi
*/
-static struct snd_pci_quirk msi_black_list[] __devinitdata = {
+static struct snd_pci_quirk msi_black_list[] = {
+ SND_PCI_QUIRK(0x103c, 0x2191, "HP", 0), /* AMD Hudson */
+ SND_PCI_QUIRK(0x103c, 0x2192, "HP", 0), /* AMD Hudson */
+ SND_PCI_QUIRK(0x103c, 0x21f7, "HP", 0), /* AMD Hudson */
+ SND_PCI_QUIRK(0x103c, 0x21fa, "HP", 0), /* AMD Hudson */
SND_PCI_QUIRK(0x1043, 0x81f2, "ASUS", 0), /* Athlon64 X2 + nvidia */
SND_PCI_QUIRK(0x1043, 0x81f6, "ASUS", 0), /* nvidia */
SND_PCI_QUIRK(0x1043, 0x822d, "ASUS", 0), /* Athlon64 X2 + nvidia MCP55 */
+ SND_PCI_QUIRK(0x1179, 0xfb44, "Toshiba Satellite C870", 0), /* AMD Hudson */
SND_PCI_QUIRK(0x1849, 0x0888, "ASRock", 0), /* Athlon64 X2 + nvidia */
SND_PCI_QUIRK(0xa0a0, 0x0575, "Aopen MZ915-M", 0), /* ICH6 */
{}
};
-static void __devinit check_msi(struct azx *chip)
+static void check_msi(struct azx *chip)
{
const struct snd_pci_quirk *q;
@@ -2427,34 +1124,72 @@ static void __devinit check_msi(struct azx *chip)
chip->msi = 1; /* enable MSI as default */
q = snd_pci_quirk_lookup(chip->pci, msi_black_list);
if (q) {
- printk(KERN_INFO
- "hda_intel: msi for device %04x:%04x set to %d\n",
- q->subvendor, q->subdevice, q->value);
+ dev_info(chip->card->dev,
+ "msi for device %04x:%04x set to %d\n",
+ q->subvendor, q->subdevice, q->value);
chip->msi = q->value;
return;
}
/* NVidia chipsets seem to cause troubles with MSI */
- if (chip->driver_type == AZX_DRIVER_NVIDIA) {
- printk(KERN_INFO "hda_intel: Disable MSI for Nvidia chipset\n");
+ if (chip->driver_caps & AZX_DCAPS_NO_MSI) {
+ dev_info(chip->card->dev, "Disabling MSI\n");
chip->msi = 0;
}
}
+/* check the snoop mode availability */
+static void azx_check_snoop_available(struct azx *chip)
+{
+ bool snoop = chip->snoop;
+
+ switch (chip->driver_type) {
+ case AZX_DRIVER_VIA:
+ /* force to non-snoop mode for a new VIA controller
+ * when BIOS is set
+ */
+ if (snoop) {
+ u8 val;
+ pci_read_config_byte(chip->pci, 0x42, &val);
+ if (!(val & 0x80) && chip->pci->revision == 0x30)
+ snoop = false;
+ }
+ break;
+ case AZX_DRIVER_ATIHDMI_NS:
+ /* new ATI HDMI requires non-snoop */
+ snoop = false;
+ break;
+ case AZX_DRIVER_CTHDA:
+ snoop = false;
+ break;
+ }
+
+ if (snoop != chip->snoop) {
+ dev_info(chip->card->dev, "Force to %s mode\n",
+ snoop ? "snoop" : "non-snoop");
+ chip->snoop = snoop;
+ }
+}
+
+static void azx_probe_work(struct work_struct *work)
+{
+ azx_probe_continue(container_of(work, struct azx, probe_work));
+}
/*
* constructor
*/
-static int __devinit azx_create(struct snd_card *card, struct pci_dev *pci,
- int dev, int driver_type,
- struct azx **rchip)
+static int azx_create(struct snd_card *card, struct pci_dev *pci,
+ int dev, unsigned int driver_caps,
+ const struct hda_controller_ops *hda_ops,
+ struct azx **rchip)
{
- struct azx *chip;
- int i, err;
- unsigned short gcap;
static struct snd_device_ops ops = {
.dev_free = azx_dev_free,
};
+ struct hda_intel *hda;
+ struct azx *chip;
+ int err;
*rchip = NULL;
@@ -2462,28 +1197,44 @@ static int __devinit azx_create(struct snd_card *card, struct pci_dev *pci,
if (err < 0)
return err;
- chip = kzalloc(sizeof(*chip), GFP_KERNEL);
- if (!chip) {
- snd_printk(KERN_ERR SFX "cannot allocate chip\n");
+ hda = kzalloc(sizeof(*hda), GFP_KERNEL);
+ if (!hda) {
+ dev_err(card->dev, "Cannot allocate hda\n");
pci_disable_device(pci);
return -ENOMEM;
}
+ chip = &hda->chip;
spin_lock_init(&chip->reg_lock);
mutex_init(&chip->open_mutex);
chip->card = card;
chip->pci = pci;
+ chip->ops = hda_ops;
chip->irq = -1;
- chip->driver_type = driver_type;
+ chip->driver_caps = driver_caps;
+ chip->driver_type = driver_caps & 0xff;
check_msi(chip);
chip->dev_index = dev;
+ chip->jackpoll_ms = jackpoll_ms;
INIT_WORK(&chip->irq_pending_work, azx_irq_pending_work);
+ INIT_LIST_HEAD(&chip->pcm_list);
+ INIT_LIST_HEAD(&chip->list);
+ init_vga_switcheroo(chip);
+ init_completion(&chip->probe_wait);
chip->position_fix[0] = chip->position_fix[1] =
check_position_fix(chip, position_fix[dev]);
+ /* combo mode uses LPIB for playback */
+ if (chip->position_fix[0] == POS_FIX_COMBO) {
+ chip->position_fix[0] = POS_FIX_LPIB;
+ chip->position_fix[1] = POS_FIX_AUTO;
+ }
+
check_probe_mask(chip, dev);
chip->single_cmd = single_cmd;
+ chip->snoop = hda_snoop;
+ azx_check_snoop_available(chip);
if (bdl_pos_adj[dev] < 0) {
switch (chip->driver_type) {
@@ -2496,6 +1247,30 @@ static int __devinit azx_create(struct snd_card *card, struct pci_dev *pci,
break;
}
}
+ chip->bdl_pos_adj = bdl_pos_adj;
+
+ err = snd_device_new(card, SNDRV_DEV_LOWLEVEL, chip, &ops);
+ if (err < 0) {
+ dev_err(card->dev, "Error creating device [card]!\n");
+ azx_free(chip);
+ return err;
+ }
+
+ /* continue probing in work context as may trigger request module */
+ INIT_WORK(&chip->probe_work, azx_probe_work);
+
+ *rchip = chip;
+
+ return 0;
+}
+
+static int azx_first_init(struct azx *chip)
+{
+ int dev = chip->dev_index;
+ struct pci_dev *pci = chip->pci;
+ struct snd_card *card = chip->card;
+ int err;
+ unsigned short gcap;
#if BITS_PER_LONG != 64
/* Fix up base address on ULI M5461 */
@@ -2508,38 +1283,32 @@ static int __devinit azx_create(struct snd_card *card, struct pci_dev *pci,
#endif
err = pci_request_regions(pci, "ICH HD audio");
- if (err < 0) {
- kfree(chip);
- pci_disable_device(pci);
+ if (err < 0)
return err;
- }
+ chip->region_requested = 1;
chip->addr = pci_resource_start(pci, 0);
chip->remap_addr = pci_ioremap_bar(pci, 0);
if (chip->remap_addr == NULL) {
- snd_printk(KERN_ERR SFX "ioremap error\n");
- err = -ENXIO;
- goto errout;
+ dev_err(card->dev, "ioremap error\n");
+ return -ENXIO;
}
if (chip->msi)
if (pci_enable_msi(pci) < 0)
chip->msi = 0;
- if (azx_acquire_irq(chip, 0) < 0) {
- err = -EBUSY;
- goto errout;
- }
+ if (azx_acquire_irq(chip, 0) < 0)
+ return -EBUSY;
pci_set_master(pci);
synchronize_irq(chip->irq);
gcap = azx_readw(chip, GCAP);
- snd_printdd(SFX "chipset global capabilities = 0x%x\n", gcap);
+ dev_dbg(card->dev, "chipset global capabilities = 0x%x\n", gcap);
/* disable SB600 64bit support for safety */
- if ((chip->driver_type == AZX_DRIVER_ATI) ||
- (chip->driver_type == AZX_DRIVER_ATIHDMI)) {
+ if (chip->pci->vendor == PCI_VENDOR_ID_ATI) {
struct pci_dev *p_smbus;
p_smbus = pci_get_device(PCI_VENDOR_ID_ATI,
PCI_DEVICE_ID_ATI_SBX00_SMBUS,
@@ -2551,10 +1320,23 @@ static int __devinit azx_create(struct snd_card *card, struct pci_dev *pci,
}
}
- /* disable 64bit DMA address for Teradici */
- /* it does not work with device 6549:1200 subsys e4a2:040b */
- if (chip->driver_type == AZX_DRIVER_TERA)
+ /* disable 64bit DMA address on some devices */
+ if (chip->driver_caps & AZX_DCAPS_NO_64BIT) {
+ dev_dbg(card->dev, "Disabling 64bit DMA\n");
gcap &= ~ICH6_GCAP_64OK;
+ }
+
+ /* disable buffer size rounding to 128-byte multiples if supported */
+ if (align_buffer_size >= 0)
+ chip->align_buffer_size = !!align_buffer_size;
+ else {
+ if (chip->driver_caps & AZX_DCAPS_BUFSIZE)
+ chip->align_buffer_size = 0;
+ else if (chip->driver_caps & AZX_DCAPS_ALIGN_BUFSIZE)
+ chip->align_buffer_size = 1;
+ else
+ chip->align_buffer_size = 1;
+ }
/* allow 64bit DMA address if supported by H/W */
if ((gcap & ICH6_GCAP_64OK) && !pci_set_dma_mask(pci, DMA_BIT_MASK(64)))
@@ -2578,6 +1360,7 @@ static int __devinit azx_create(struct snd_card *card, struct pci_dev *pci,
chip->capture_streams = ULI_NUM_CAPTURE;
break;
case AZX_DRIVER_ATIHDMI:
+ case AZX_DRIVER_ATIHDMI_NS:
chip->playback_streams = ATIHDMI_NUM_PLAYBACK;
chip->capture_streams = ATIHDMI_NUM_CAPTURE;
break;
@@ -2594,51 +1377,29 @@ static int __devinit azx_create(struct snd_card *card, struct pci_dev *pci,
chip->azx_dev = kcalloc(chip->num_streams, sizeof(*chip->azx_dev),
GFP_KERNEL);
if (!chip->azx_dev) {
- snd_printk(KERN_ERR SFX "cannot malloc azx_dev\n");
- goto errout;
+ dev_err(card->dev, "cannot malloc azx_dev\n");
+ return -ENOMEM;
}
- for (i = 0; i < chip->num_streams; i++) {
- /* allocate memory for the BDL for each stream */
- err = snd_dma_alloc_pages(SNDRV_DMA_TYPE_DEV,
- snd_dma_pci_data(chip->pci),
- BDL_SIZE, &chip->azx_dev[i].bdl);
- if (err < 0) {
- snd_printk(KERN_ERR SFX "cannot allocate BDL\n");
- goto errout;
- }
- }
- /* allocate memory for the position buffer */
- err = snd_dma_alloc_pages(SNDRV_DMA_TYPE_DEV,
- snd_dma_pci_data(chip->pci),
- chip->num_streams * 8, &chip->posbuf);
- if (err < 0) {
- snd_printk(KERN_ERR SFX "cannot allocate posbuf\n");
- goto errout;
- }
- /* allocate CORB/RIRB */
- err = azx_alloc_cmd_io(chip);
+ err = azx_alloc_stream_pages(chip);
if (err < 0)
- goto errout;
+ return err;
/* initialize streams */
azx_init_stream(chip);
/* initialize chip */
azx_init_pci(chip);
+
+ if (chip->driver_caps & AZX_DCAPS_I915_POWERWELL)
+ haswell_set_bclk(chip);
+
azx_init_chip(chip, (probe_only[dev] & 2) == 0);
/* codec detection */
if (!chip->codec_mask) {
- snd_printk(KERN_ERR SFX "no codecs found!\n");
- err = -ENODEV;
- goto errout;
- }
-
- err = snd_device_new(card, SNDRV_DEV_LOWLEVEL, chip, &ops);
- if (err <0) {
- snd_printk(KERN_ERR SFX "Error creating device [card]!\n");
- goto errout;
+ dev_err(card->dev, "no codecs found!\n");
+ return -ENODEV;
}
strcpy(card->driver, "HDA-Intel");
@@ -2648,17 +1409,12 @@ static int __devinit azx_create(struct snd_card *card, struct pci_dev *pci,
"%s at 0x%lx irq %i",
card->shortname, chip->addr, chip->irq);
- *rchip = chip;
return 0;
-
- errout:
- azx_free(chip);
- return err;
}
static void power_down_all_codecs(struct azx *chip)
{
-#ifdef CONFIG_SND_HDA_POWER_SAVE
+#ifdef CONFIG_PM
/* The codecs were powered up in snd_hda_codec_new().
* Now all initialization done, so turn them down if possible
*/
@@ -2669,12 +1425,166 @@ static void power_down_all_codecs(struct azx *chip)
#endif
}
-static int __devinit azx_probe(struct pci_dev *pci,
- const struct pci_device_id *pci_id)
+#ifdef CONFIG_SND_HDA_PATCH_LOADER
+/* callback from request_firmware_nowait() */
+static void azx_firmware_cb(const struct firmware *fw, void *context)
+{
+ struct snd_card *card = context;
+ struct azx *chip = card->private_data;
+ struct pci_dev *pci = chip->pci;
+
+ if (!fw) {
+ dev_err(card->dev, "Cannot load firmware, aborting\n");
+ goto error;
+ }
+
+ chip->fw = fw;
+ if (!chip->disabled) {
+ /* continue probing */
+ if (azx_probe_continue(chip))
+ goto error;
+ }
+ return; /* OK */
+
+ error:
+ snd_card_free(card);
+ pci_set_drvdata(pci, NULL);
+}
+#endif
+
+/*
+ * HDA controller ops.
+ */
+
+/* PCI register access. */
+static void pci_azx_writel(u32 value, u32 __iomem *addr)
+{
+ writel(value, addr);
+}
+
+static u32 pci_azx_readl(u32 __iomem *addr)
+{
+ return readl(addr);
+}
+
+static void pci_azx_writew(u16 value, u16 __iomem *addr)
+{
+ writew(value, addr);
+}
+
+static u16 pci_azx_readw(u16 __iomem *addr)
+{
+ return readw(addr);
+}
+
+static void pci_azx_writeb(u8 value, u8 __iomem *addr)
+{
+ writeb(value, addr);
+}
+
+static u8 pci_azx_readb(u8 __iomem *addr)
+{
+ return readb(addr);
+}
+
+static int disable_msi_reset_irq(struct azx *chip)
+{
+ int err;
+
+ free_irq(chip->irq, chip);
+ chip->irq = -1;
+ pci_disable_msi(chip->pci);
+ chip->msi = 0;
+ err = azx_acquire_irq(chip, 1);
+ if (err < 0)
+ return err;
+
+ return 0;
+}
+
+/* DMA page allocation helpers. */
+static int dma_alloc_pages(struct azx *chip,
+ int type,
+ size_t size,
+ struct snd_dma_buffer *buf)
+{
+ int err;
+
+ err = snd_dma_alloc_pages(type,
+ chip->card->dev,
+ size, buf);
+ if (err < 0)
+ return err;
+ mark_pages_wc(chip, buf, true);
+ return 0;
+}
+
+static void dma_free_pages(struct azx *chip, struct snd_dma_buffer *buf)
+{
+ mark_pages_wc(chip, buf, false);
+ snd_dma_free_pages(buf);
+}
+
+static int substream_alloc_pages(struct azx *chip,
+ struct snd_pcm_substream *substream,
+ size_t size)
+{
+ struct azx_dev *azx_dev = get_azx_dev(substream);
+ int ret;
+
+ mark_runtime_wc(chip, azx_dev, substream, false);
+ azx_dev->bufsize = 0;
+ azx_dev->period_bytes = 0;
+ azx_dev->format_val = 0;
+ ret = snd_pcm_lib_malloc_pages(substream, size);
+ if (ret < 0)
+ return ret;
+ mark_runtime_wc(chip, azx_dev, substream, true);
+ return 0;
+}
+
+static int substream_free_pages(struct azx *chip,
+ struct snd_pcm_substream *substream)
+{
+ struct azx_dev *azx_dev = get_azx_dev(substream);
+ mark_runtime_wc(chip, azx_dev, substream, false);
+ return snd_pcm_lib_free_pages(substream);
+}
+
+static void pcm_mmap_prepare(struct snd_pcm_substream *substream,
+ struct vm_area_struct *area)
+{
+#ifdef CONFIG_X86
+ struct azx_pcm *apcm = snd_pcm_substream_chip(substream);
+ struct azx *chip = apcm->chip;
+ if (!azx_snoop(chip))
+ area->vm_page_prot = pgprot_writecombine(area->vm_page_prot);
+#endif
+}
+
+static const struct hda_controller_ops pci_hda_ops = {
+ .reg_writel = pci_azx_writel,
+ .reg_readl = pci_azx_readl,
+ .reg_writew = pci_azx_writew,
+ .reg_readw = pci_azx_readw,
+ .reg_writeb = pci_azx_writeb,
+ .reg_readb = pci_azx_readb,
+ .disable_msi_reset_irq = disable_msi_reset_irq,
+ .dma_alloc_pages = dma_alloc_pages,
+ .dma_free_pages = dma_free_pages,
+ .substream_alloc_pages = substream_alloc_pages,
+ .substream_free_pages = substream_free_pages,
+ .pcm_mmap_prepare = pcm_mmap_prepare,
+ .position_check = azx_position_check,
+};
+
+static int azx_probe(struct pci_dev *pci,
+ const struct pci_device_id *pci_id)
{
static int dev;
struct snd_card *card;
struct azx *chip;
+ bool schedule_probe;
int err;
if (dev >= SNDRV_CARDS)
@@ -2684,35 +1594,121 @@ static int __devinit azx_probe(struct pci_dev *pci,
return -ENOENT;
}
- err = snd_card_create(index[dev], id[dev], THIS_MODULE, 0, &card);
+ err = snd_card_new(&pci->dev, index[dev], id[dev], THIS_MODULE,
+ 0, &card);
if (err < 0) {
- snd_printk(KERN_ERR SFX "Error creating card!\n");
+ dev_err(&pci->dev, "Error creating card!\n");
return err;
}
- /* set this here since it's referred in snd_hda_load_patch() */
- snd_card_set_dev(card, &pci->dev);
-
- err = azx_create(card, pci, dev, pci_id->driver_data, &chip);
+ err = azx_create(card, pci, dev, pci_id->driver_data,
+ &pci_hda_ops, &chip);
if (err < 0)
goto out_free;
card->private_data = chip;
+ pci_set_drvdata(pci, card);
+
+ err = register_vga_switcheroo(chip);
+ if (err < 0) {
+ dev_err(card->dev, "Error registering VGA-switcheroo client\n");
+ goto out_free;
+ }
+
+ if (check_hdmi_disabled(pci)) {
+ dev_info(card->dev, "VGA controller is disabled\n");
+ dev_info(card->dev, "Delaying initialization\n");
+ chip->disabled = true;
+ }
+
+ schedule_probe = !chip->disabled;
+
+#ifdef CONFIG_SND_HDA_PATCH_LOADER
+ if (patch[dev] && *patch[dev]) {
+ dev_info(card->dev, "Applying patch firmware '%s'\n",
+ patch[dev]);
+ err = request_firmware_nowait(THIS_MODULE, true, patch[dev],
+ &pci->dev, GFP_KERNEL, card,
+ azx_firmware_cb);
+ if (err < 0)
+ goto out_free;
+ schedule_probe = false; /* continued in azx_firmware_cb() */
+ }
+#endif /* CONFIG_SND_HDA_PATCH_LOADER */
+
+#ifndef CONFIG_SND_HDA_I915
+ if (chip->driver_caps & AZX_DCAPS_I915_POWERWELL)
+ dev_err(card->dev, "Haswell must build in CONFIG_SND_HDA_I915\n");
+#endif
+
+ if (schedule_probe)
+ schedule_work(&chip->probe_work);
+
+ dev++;
+ if (chip->disabled)
+ complete_all(&chip->probe_wait);
+ return 0;
+
+out_free:
+ snd_card_free(card);
+ return err;
+}
+
+/* number of codec slots for each chipset: 0 = default slots (i.e. 4) */
+static unsigned int azx_max_codecs[AZX_NUM_DRIVERS] = {
+ [AZX_DRIVER_NVIDIA] = 8,
+ [AZX_DRIVER_TERA] = 1,
+};
+
+static int azx_probe_continue(struct azx *chip)
+{
+ struct pci_dev *pci = chip->pci;
+ int dev = chip->dev_index;
+ int err;
+
+ /* Request power well for Haswell HDA controller and codec */
+ if (chip->driver_caps & AZX_DCAPS_I915_POWERWELL) {
+#ifdef CONFIG_SND_HDA_I915
+ err = hda_i915_init();
+ if (err < 0) {
+ dev_err(chip->card->dev,
+ "Error request power-well from i915\n");
+ goto out_free;
+ }
+ err = hda_display_power(true);
+ if (err < 0) {
+ dev_err(chip->card->dev,
+ "Cannot turn on display power on i915\n");
+ goto out_free;
+ }
+#endif
+ }
+
+ err = azx_first_init(chip);
+ if (err < 0)
+ goto out_free;
+
#ifdef CONFIG_SND_HDA_INPUT_BEEP
chip->beep_mode = beep_mode[dev];
#endif
/* create codec instances */
- err = azx_codec_create(chip, model[dev]);
+ err = azx_codec_create(chip, model[dev],
+ azx_max_codecs[chip->driver_type],
+ power_save_addr);
+
if (err < 0)
goto out_free;
#ifdef CONFIG_SND_HDA_PATCH_LOADER
- if (patch[dev] && *patch[dev]) {
- snd_printk(KERN_ERR SFX "Applying patch firmware '%s'\n",
- patch[dev]);
- err = snd_hda_load_patch(chip->bus, patch[dev]);
+ if (chip->fw) {
+ err = snd_hda_load_patch(chip->bus, chip->fw->size,
+ chip->fw->data);
if (err < 0)
goto out_free;
+#ifndef CONFIG_PM
+ release_firmware(chip->fw); /* no longer needed */
+ chip->fw = NULL;
+#endif
}
#endif
if ((probe_only[dev] & 1) == 0) {
@@ -2731,61 +1727,183 @@ static int __devinit azx_probe(struct pci_dev *pci,
if (err < 0)
goto out_free;
- err = snd_card_register(card);
+ err = snd_card_register(chip->card);
if (err < 0)
goto out_free;
- pci_set_drvdata(pci, card);
chip->running = 1;
power_down_all_codecs(chip);
azx_notifier_register(chip);
+ azx_add_card_list(chip);
+ if ((chip->driver_caps & AZX_DCAPS_PM_RUNTIME) || chip->use_vga_switcheroo)
+ pm_runtime_put_noidle(&pci->dev);
- dev++;
- return err;
out_free:
- snd_card_free(card);
+ if (err < 0)
+ chip->init_failed = 1;
+ complete_all(&chip->probe_wait);
return err;
}
-static void __devexit azx_remove(struct pci_dev *pci)
+static void azx_remove(struct pci_dev *pci)
{
- snd_card_free(pci_get_drvdata(pci));
- pci_set_drvdata(pci, NULL);
+ struct snd_card *card = pci_get_drvdata(pci);
+
+ if (card)
+ snd_card_free(card);
}
/* PCI IDs */
-static DEFINE_PCI_DEVICE_TABLE(azx_ids) = {
+static const struct pci_device_id azx_ids[] = {
/* CPT */
- { PCI_DEVICE(0x8086, 0x1c20), .driver_data = AZX_DRIVER_PCH },
+ { PCI_DEVICE(0x8086, 0x1c20),
+ .driver_data = AZX_DRIVER_PCH | AZX_DCAPS_INTEL_PCH_NOPM },
/* PBG */
- { PCI_DEVICE(0x8086, 0x1d20), .driver_data = AZX_DRIVER_PCH },
- /* SCH */
- { PCI_DEVICE(0x8086, 0x811b), .driver_data = AZX_DRIVER_SCH },
+ { PCI_DEVICE(0x8086, 0x1d20),
+ .driver_data = AZX_DRIVER_PCH | AZX_DCAPS_INTEL_PCH_NOPM },
+ /* Panther Point */
+ { PCI_DEVICE(0x8086, 0x1e20),
+ .driver_data = AZX_DRIVER_PCH | AZX_DCAPS_INTEL_PCH },
+ /* Lynx Point */
+ { PCI_DEVICE(0x8086, 0x8c20),
+ .driver_data = AZX_DRIVER_PCH | AZX_DCAPS_INTEL_PCH },
+ /* 9 Series */
+ { PCI_DEVICE(0x8086, 0x8ca0),
+ .driver_data = AZX_DRIVER_PCH | AZX_DCAPS_INTEL_PCH },
+ /* Wellsburg */
+ { PCI_DEVICE(0x8086, 0x8d20),
+ .driver_data = AZX_DRIVER_PCH | AZX_DCAPS_INTEL_PCH },
+ { PCI_DEVICE(0x8086, 0x8d21),
+ .driver_data = AZX_DRIVER_PCH | AZX_DCAPS_INTEL_PCH },
+ /* Lynx Point-LP */
+ { PCI_DEVICE(0x8086, 0x9c20),
+ .driver_data = AZX_DRIVER_PCH | AZX_DCAPS_INTEL_PCH },
+ /* Lynx Point-LP */
+ { PCI_DEVICE(0x8086, 0x9c21),
+ .driver_data = AZX_DRIVER_PCH | AZX_DCAPS_INTEL_PCH },
+ /* Wildcat Point-LP */
+ { PCI_DEVICE(0x8086, 0x9ca0),
+ .driver_data = AZX_DRIVER_PCH | AZX_DCAPS_INTEL_PCH },
+ /* Haswell */
+ { PCI_DEVICE(0x8086, 0x0a0c),
+ .driver_data = AZX_DRIVER_HDMI | AZX_DCAPS_INTEL_HASWELL },
+ { PCI_DEVICE(0x8086, 0x0c0c),
+ .driver_data = AZX_DRIVER_HDMI | AZX_DCAPS_INTEL_HASWELL },
+ { PCI_DEVICE(0x8086, 0x0d0c),
+ .driver_data = AZX_DRIVER_HDMI | AZX_DCAPS_INTEL_HASWELL },
+ /* Broadwell */
+ { PCI_DEVICE(0x8086, 0x160c),
+ .driver_data = AZX_DRIVER_HDMI | AZX_DCAPS_INTEL_BROADWELL },
+ /* 5 Series/3400 */
+ { PCI_DEVICE(0x8086, 0x3b56),
+ .driver_data = AZX_DRIVER_SCH | AZX_DCAPS_INTEL_PCH_NOPM },
+ /* Poulsbo */
+ { PCI_DEVICE(0x8086, 0x811b),
+ .driver_data = AZX_DRIVER_SCH | AZX_DCAPS_INTEL_PCH_NOPM },
+ /* Oaktrail */
+ { PCI_DEVICE(0x8086, 0x080a),
+ .driver_data = AZX_DRIVER_SCH | AZX_DCAPS_INTEL_PCH_NOPM },
+ /* BayTrail */
+ { PCI_DEVICE(0x8086, 0x0f04),
+ .driver_data = AZX_DRIVER_PCH | AZX_DCAPS_INTEL_PCH_NOPM },
+ /* ICH */
+ { PCI_DEVICE(0x8086, 0x2668),
+ .driver_data = AZX_DRIVER_ICH | AZX_DCAPS_OLD_SSYNC |
+ AZX_DCAPS_BUFSIZE }, /* ICH6 */
+ { PCI_DEVICE(0x8086, 0x27d8),
+ .driver_data = AZX_DRIVER_ICH | AZX_DCAPS_OLD_SSYNC |
+ AZX_DCAPS_BUFSIZE }, /* ICH7 */
+ { PCI_DEVICE(0x8086, 0x269a),
+ .driver_data = AZX_DRIVER_ICH | AZX_DCAPS_OLD_SSYNC |
+ AZX_DCAPS_BUFSIZE }, /* ESB2 */
+ { PCI_DEVICE(0x8086, 0x284b),
+ .driver_data = AZX_DRIVER_ICH | AZX_DCAPS_OLD_SSYNC |
+ AZX_DCAPS_BUFSIZE }, /* ICH8 */
+ { PCI_DEVICE(0x8086, 0x293e),
+ .driver_data = AZX_DRIVER_ICH | AZX_DCAPS_OLD_SSYNC |
+ AZX_DCAPS_BUFSIZE }, /* ICH9 */
+ { PCI_DEVICE(0x8086, 0x293f),
+ .driver_data = AZX_DRIVER_ICH | AZX_DCAPS_OLD_SSYNC |
+ AZX_DCAPS_BUFSIZE }, /* ICH9 */
+ { PCI_DEVICE(0x8086, 0x3a3e),
+ .driver_data = AZX_DRIVER_ICH | AZX_DCAPS_OLD_SSYNC |
+ AZX_DCAPS_BUFSIZE }, /* ICH10 */
+ { PCI_DEVICE(0x8086, 0x3a6e),
+ .driver_data = AZX_DRIVER_ICH | AZX_DCAPS_OLD_SSYNC |
+ AZX_DCAPS_BUFSIZE }, /* ICH10 */
/* Generic Intel */
{ PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_ANY_ID),
.class = PCI_CLASS_MULTIMEDIA_HD_AUDIO << 8,
.class_mask = 0xffffff,
- .driver_data = AZX_DRIVER_ICH },
- /* ATI SB 450/600 */
- { PCI_DEVICE(0x1002, 0x437b), .driver_data = AZX_DRIVER_ATI },
- { PCI_DEVICE(0x1002, 0x4383), .driver_data = AZX_DRIVER_ATI },
+ .driver_data = AZX_DRIVER_ICH | AZX_DCAPS_BUFSIZE },
+ /* ATI SB 450/600/700/800/900 */
+ { PCI_DEVICE(0x1002, 0x437b),
+ .driver_data = AZX_DRIVER_ATI | AZX_DCAPS_PRESET_ATI_SB },
+ { PCI_DEVICE(0x1002, 0x4383),
+ .driver_data = AZX_DRIVER_ATI | AZX_DCAPS_PRESET_ATI_SB },
+ /* AMD Hudson */
+ { PCI_DEVICE(0x1022, 0x780d),
+ .driver_data = AZX_DRIVER_GENERIC | AZX_DCAPS_PRESET_ATI_SB },
/* ATI HDMI */
- { PCI_DEVICE(0x1002, 0x793b), .driver_data = AZX_DRIVER_ATIHDMI },
- { PCI_DEVICE(0x1002, 0x7919), .driver_data = AZX_DRIVER_ATIHDMI },
- { PCI_DEVICE(0x1002, 0x960f), .driver_data = AZX_DRIVER_ATIHDMI },
- { PCI_DEVICE(0x1002, 0x970f), .driver_data = AZX_DRIVER_ATIHDMI },
- { PCI_DEVICE(0x1002, 0xaa00), .driver_data = AZX_DRIVER_ATIHDMI },
- { PCI_DEVICE(0x1002, 0xaa08), .driver_data = AZX_DRIVER_ATIHDMI },
- { PCI_DEVICE(0x1002, 0xaa10), .driver_data = AZX_DRIVER_ATIHDMI },
- { PCI_DEVICE(0x1002, 0xaa18), .driver_data = AZX_DRIVER_ATIHDMI },
- { PCI_DEVICE(0x1002, 0xaa20), .driver_data = AZX_DRIVER_ATIHDMI },
- { PCI_DEVICE(0x1002, 0xaa28), .driver_data = AZX_DRIVER_ATIHDMI },
- { PCI_DEVICE(0x1002, 0xaa30), .driver_data = AZX_DRIVER_ATIHDMI },
- { PCI_DEVICE(0x1002, 0xaa38), .driver_data = AZX_DRIVER_ATIHDMI },
- { PCI_DEVICE(0x1002, 0xaa40), .driver_data = AZX_DRIVER_ATIHDMI },
- { PCI_DEVICE(0x1002, 0xaa48), .driver_data = AZX_DRIVER_ATIHDMI },
+ { PCI_DEVICE(0x1002, 0x793b),
+ .driver_data = AZX_DRIVER_ATIHDMI | AZX_DCAPS_PRESET_ATI_HDMI },
+ { PCI_DEVICE(0x1002, 0x7919),
+ .driver_data = AZX_DRIVER_ATIHDMI | AZX_DCAPS_PRESET_ATI_HDMI },
+ { PCI_DEVICE(0x1002, 0x960f),
+ .driver_data = AZX_DRIVER_ATIHDMI | AZX_DCAPS_PRESET_ATI_HDMI },
+ { PCI_DEVICE(0x1002, 0x970f),
+ .driver_data = AZX_DRIVER_ATIHDMI | AZX_DCAPS_PRESET_ATI_HDMI },
+ { PCI_DEVICE(0x1002, 0xaa00),
+ .driver_data = AZX_DRIVER_ATIHDMI | AZX_DCAPS_PRESET_ATI_HDMI },
+ { PCI_DEVICE(0x1002, 0xaa08),
+ .driver_data = AZX_DRIVER_ATIHDMI | AZX_DCAPS_PRESET_ATI_HDMI },
+ { PCI_DEVICE(0x1002, 0xaa10),
+ .driver_data = AZX_DRIVER_ATIHDMI | AZX_DCAPS_PRESET_ATI_HDMI },
+ { PCI_DEVICE(0x1002, 0xaa18),
+ .driver_data = AZX_DRIVER_ATIHDMI | AZX_DCAPS_PRESET_ATI_HDMI },
+ { PCI_DEVICE(0x1002, 0xaa20),
+ .driver_data = AZX_DRIVER_ATIHDMI | AZX_DCAPS_PRESET_ATI_HDMI },
+ { PCI_DEVICE(0x1002, 0xaa28),
+ .driver_data = AZX_DRIVER_ATIHDMI | AZX_DCAPS_PRESET_ATI_HDMI },
+ { PCI_DEVICE(0x1002, 0xaa30),
+ .driver_data = AZX_DRIVER_ATIHDMI | AZX_DCAPS_PRESET_ATI_HDMI },
+ { PCI_DEVICE(0x1002, 0xaa38),
+ .driver_data = AZX_DRIVER_ATIHDMI | AZX_DCAPS_PRESET_ATI_HDMI },
+ { PCI_DEVICE(0x1002, 0xaa40),
+ .driver_data = AZX_DRIVER_ATIHDMI | AZX_DCAPS_PRESET_ATI_HDMI },
+ { PCI_DEVICE(0x1002, 0xaa48),
+ .driver_data = AZX_DRIVER_ATIHDMI | AZX_DCAPS_PRESET_ATI_HDMI },
+ { PCI_DEVICE(0x1002, 0xaa50),
+ .driver_data = AZX_DRIVER_ATIHDMI | AZX_DCAPS_PRESET_ATI_HDMI },
+ { PCI_DEVICE(0x1002, 0xaa58),
+ .driver_data = AZX_DRIVER_ATIHDMI | AZX_DCAPS_PRESET_ATI_HDMI },
+ { PCI_DEVICE(0x1002, 0xaa60),
+ .driver_data = AZX_DRIVER_ATIHDMI | AZX_DCAPS_PRESET_ATI_HDMI },
+ { PCI_DEVICE(0x1002, 0xaa68),
+ .driver_data = AZX_DRIVER_ATIHDMI | AZX_DCAPS_PRESET_ATI_HDMI },
+ { PCI_DEVICE(0x1002, 0xaa80),
+ .driver_data = AZX_DRIVER_ATIHDMI | AZX_DCAPS_PRESET_ATI_HDMI },
+ { PCI_DEVICE(0x1002, 0xaa88),
+ .driver_data = AZX_DRIVER_ATIHDMI | AZX_DCAPS_PRESET_ATI_HDMI },
+ { PCI_DEVICE(0x1002, 0xaa90),
+ .driver_data = AZX_DRIVER_ATIHDMI | AZX_DCAPS_PRESET_ATI_HDMI },
+ { PCI_DEVICE(0x1002, 0xaa98),
+ .driver_data = AZX_DRIVER_ATIHDMI | AZX_DCAPS_PRESET_ATI_HDMI },
+ { PCI_DEVICE(0x1002, 0x9902),
+ .driver_data = AZX_DRIVER_ATIHDMI_NS | AZX_DCAPS_PRESET_ATI_HDMI },
+ { PCI_DEVICE(0x1002, 0xaaa0),
+ .driver_data = AZX_DRIVER_ATIHDMI_NS | AZX_DCAPS_PRESET_ATI_HDMI },
+ { PCI_DEVICE(0x1002, 0xaaa8),
+ .driver_data = AZX_DRIVER_ATIHDMI_NS | AZX_DCAPS_PRESET_ATI_HDMI },
+ { PCI_DEVICE(0x1002, 0xaab0),
+ .driver_data = AZX_DRIVER_ATIHDMI_NS | AZX_DCAPS_PRESET_ATI_HDMI },
/* VIA VT8251/VT8237A */
- { PCI_DEVICE(0x1106, 0x3288), .driver_data = AZX_DRIVER_VIA },
+ { PCI_DEVICE(0x1106, 0x3288),
+ .driver_data = AZX_DRIVER_VIA | AZX_DCAPS_POSFIX_VIA },
+ /* VIA GFX VT7122/VX900 */
+ { PCI_DEVICE(0x1106, 0x9170), .driver_data = AZX_DRIVER_GENERIC },
+ /* VIA GFX VT6122/VX11 */
+ { PCI_DEVICE(0x1106, 0x9140), .driver_data = AZX_DRIVER_GENERIC },
/* SIS966 */
{ PCI_DEVICE(0x1039, 0x7502), .driver_data = AZX_DRIVER_SIS },
/* ULI M5461 */
@@ -2794,11 +1912,19 @@ static DEFINE_PCI_DEVICE_TABLE(azx_ids) = {
{ PCI_DEVICE(PCI_VENDOR_ID_NVIDIA, PCI_ANY_ID),
.class = PCI_CLASS_MULTIMEDIA_HD_AUDIO << 8,
.class_mask = 0xffffff,
- .driver_data = AZX_DRIVER_NVIDIA },
+ .driver_data = AZX_DRIVER_NVIDIA | AZX_DCAPS_PRESET_NVIDIA },
/* Teradici */
- { PCI_DEVICE(0x6549, 0x1200), .driver_data = AZX_DRIVER_TERA },
+ { PCI_DEVICE(0x6549, 0x1200),
+ .driver_data = AZX_DRIVER_TERA | AZX_DCAPS_NO_64BIT },
+ { PCI_DEVICE(0x6549, 0x2200),
+ .driver_data = AZX_DRIVER_TERA | AZX_DCAPS_NO_64BIT },
/* Creative X-Fi (CA0110-IBG) */
-#if !defined(CONFIG_SND_CTXFI) && !defined(CONFIG_SND_CTXFI_MODULE)
+ /* CTHDA chips */
+ { PCI_DEVICE(0x1102, 0x0010),
+ .driver_data = AZX_DRIVER_CTHDA | AZX_DCAPS_PRESET_CTHDA },
+ { PCI_DEVICE(0x1102, 0x0012),
+ .driver_data = AZX_DRIVER_CTHDA | AZX_DCAPS_PRESET_CTHDA },
+#if !IS_ENABLED(CONFIG_SND_CTXFI)
/* the following entry conflicts with snd-ctxfi driver,
* as ctxfi driver mutates from HD-audio to native mode with
* a special command sequence.
@@ -2806,10 +1932,13 @@ static DEFINE_PCI_DEVICE_TABLE(azx_ids) = {
{ PCI_DEVICE(PCI_VENDOR_ID_CREATIVE, PCI_ANY_ID),
.class = PCI_CLASS_MULTIMEDIA_HD_AUDIO << 8,
.class_mask = 0xffffff,
- .driver_data = AZX_DRIVER_CTX },
+ .driver_data = AZX_DRIVER_CTX | AZX_DCAPS_CTX_WORKAROUND |
+ AZX_DCAPS_RIRB_PRE_DELAY | AZX_DCAPS_POSFIX_LPIB },
#else
/* this entry seems still valid -- i.e. without emu20kx chip */
- { PCI_DEVICE(0x1102, 0x0009), .driver_data = AZX_DRIVER_CTX },
+ { PCI_DEVICE(0x1102, 0x0009),
+ .driver_data = AZX_DRIVER_CTX | AZX_DCAPS_CTX_WORKAROUND |
+ AZX_DCAPS_RIRB_PRE_DELAY | AZX_DCAPS_POSFIX_LPIB },
#endif
/* Vortex86MX */
{ PCI_DEVICE(0x17f3, 0x3010), .driver_data = AZX_DRIVER_GENERIC },
@@ -2819,36 +1948,24 @@ static DEFINE_PCI_DEVICE_TABLE(azx_ids) = {
{ PCI_DEVICE(PCI_VENDOR_ID_ATI, PCI_ANY_ID),
.class = PCI_CLASS_MULTIMEDIA_HD_AUDIO << 8,
.class_mask = 0xffffff,
- .driver_data = AZX_DRIVER_GENERIC },
+ .driver_data = AZX_DRIVER_GENERIC | AZX_DCAPS_PRESET_ATI_HDMI },
{ PCI_DEVICE(PCI_VENDOR_ID_AMD, PCI_ANY_ID),
.class = PCI_CLASS_MULTIMEDIA_HD_AUDIO << 8,
.class_mask = 0xffffff,
- .driver_data = AZX_DRIVER_GENERIC },
+ .driver_data = AZX_DRIVER_GENERIC | AZX_DCAPS_PRESET_ATI_HDMI },
{ 0, }
};
MODULE_DEVICE_TABLE(pci, azx_ids);
/* pci_driver definition */
-static struct pci_driver driver = {
- .name = "HDA Intel",
+static struct pci_driver azx_driver = {
+ .name = KBUILD_MODNAME,
.id_table = azx_ids,
.probe = azx_probe,
- .remove = __devexit_p(azx_remove),
-#ifdef CONFIG_PM
- .suspend = azx_suspend,
- .resume = azx_resume,
-#endif
+ .remove = azx_remove,
+ .driver = {
+ .pm = AZX_PM_OPS,
+ },
};
-static int __init alsa_card_azx_init(void)
-{
- return pci_register_driver(&driver);
-}
-
-static void __exit alsa_card_azx_exit(void)
-{
- pci_unregister_driver(&driver);
-}
-
-module_init(alsa_card_azx_init)
-module_exit(alsa_card_azx_exit)
+module_pci_driver(azx_driver);
diff --git a/sound/pci/hda/hda_intel_trace.h b/sound/pci/hda/hda_intel_trace.h
new file mode 100644
index 00000000000..7b5e4c2cf9d
--- /dev/null
+++ b/sound/pci/hda/hda_intel_trace.h
@@ -0,0 +1,62 @@
+#undef TRACE_SYSTEM
+#define TRACE_SYSTEM hda_intel
+#define TRACE_INCLUDE_FILE hda_intel_trace
+
+#if !defined(_TRACE_HDA_INTEL_H) || defined(TRACE_HEADER_MULTI_READ)
+#define _TRACE_HDA_INTEL_H
+
+#include <linux/tracepoint.h>
+
+struct azx;
+struct azx_dev;
+
+TRACE_EVENT(azx_pcm_trigger,
+
+ TP_PROTO(struct azx *chip, struct azx_dev *dev, int cmd),
+
+ TP_ARGS(chip, dev, cmd),
+
+ TP_STRUCT__entry(
+ __field( int, card )
+ __field( int, idx )
+ __field( int, cmd )
+ ),
+
+ TP_fast_assign(
+ __entry->card = (chip)->card->number;
+ __entry->idx = (dev)->index;
+ __entry->cmd = cmd;
+ ),
+
+ TP_printk("[%d:%d] cmd=%d", __entry->card, __entry->idx, __entry->cmd)
+);
+
+TRACE_EVENT(azx_get_position,
+
+ TP_PROTO(struct azx *chip, struct azx_dev *dev, unsigned int pos, unsigned int delay),
+
+ TP_ARGS(chip, dev, pos, delay),
+
+ TP_STRUCT__entry(
+ __field( int, card )
+ __field( int, idx )
+ __field( unsigned int, pos )
+ __field( unsigned int, delay )
+ ),
+
+ TP_fast_assign(
+ __entry->card = (chip)->card->number;
+ __entry->idx = (dev)->index;
+ __entry->pos = pos;
+ __entry->delay = delay;
+ ),
+
+ TP_printk("[%d:%d] pos=%u, delay=%u", __entry->card, __entry->idx, __entry->pos, __entry->delay)
+);
+
+#endif /* _TRACE_HDA_INTEL_H */
+
+/* This part must be outside protection */
+#undef TRACE_INCLUDE_PATH
+#define TRACE_INCLUDE_PATH .
+#include <trace/define_trace.h>
diff --git a/sound/pci/hda/hda_jack.c b/sound/pci/hda/hda_jack.c
new file mode 100644
index 00000000000..9746d73cec5
--- /dev/null
+++ b/sound/pci/hda/hda_jack.c
@@ -0,0 +1,546 @@
+/*
+ * Jack-detection handling for HD-audio
+ *
+ * Copyright (c) 2011 Takashi Iwai <tiwai@suse.de>
+ *
+ * This driver is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ */
+
+#include <linux/init.h>
+#include <linux/slab.h>
+#include <linux/export.h>
+#include <sound/core.h>
+#include <sound/control.h>
+#include <sound/jack.h>
+#include "hda_codec.h"
+#include "hda_local.h"
+#include "hda_auto_parser.h"
+#include "hda_jack.h"
+
+bool is_jack_detectable(struct hda_codec *codec, hda_nid_t nid)
+{
+ if (codec->no_jack_detect)
+ return false;
+ if (!(snd_hda_query_pin_caps(codec, nid) & AC_PINCAP_PRES_DETECT))
+ return false;
+ if (get_defcfg_misc(snd_hda_codec_get_pincfg(codec, nid)) &
+ AC_DEFCFG_MISC_NO_PRESENCE)
+ return false;
+ if (!(get_wcaps(codec, nid) & AC_WCAP_UNSOL_CAP) &&
+ !codec->jackpoll_interval)
+ return false;
+ return true;
+}
+EXPORT_SYMBOL_GPL(is_jack_detectable);
+
+/* execute pin sense measurement */
+static u32 read_pin_sense(struct hda_codec *codec, hda_nid_t nid)
+{
+ u32 pincap;
+ u32 val;
+
+ if (!codec->no_trigger_sense) {
+ pincap = snd_hda_query_pin_caps(codec, nid);
+ if (pincap & AC_PINCAP_TRIG_REQ) /* need trigger? */
+ snd_hda_codec_read(codec, nid, 0,
+ AC_VERB_SET_PIN_SENSE, 0);
+ }
+ val = snd_hda_codec_read(codec, nid, 0,
+ AC_VERB_GET_PIN_SENSE, 0);
+ if (codec->inv_jack_detect)
+ val ^= AC_PINSENSE_PRESENCE;
+ return val;
+}
+
+/**
+ * snd_hda_jack_tbl_get - query the jack-table entry for the given NID
+ */
+struct hda_jack_tbl *
+snd_hda_jack_tbl_get(struct hda_codec *codec, hda_nid_t nid)
+{
+ struct hda_jack_tbl *jack = codec->jacktbl.list;
+ int i;
+
+ if (!nid || !jack)
+ return NULL;
+ for (i = 0; i < codec->jacktbl.used; i++, jack++)
+ if (jack->nid == nid)
+ return jack;
+ return NULL;
+}
+EXPORT_SYMBOL_GPL(snd_hda_jack_tbl_get);
+
+/**
+ * snd_hda_jack_tbl_get_from_tag - query the jack-table entry for the given tag
+ */
+struct hda_jack_tbl *
+snd_hda_jack_tbl_get_from_tag(struct hda_codec *codec, unsigned char tag)
+{
+ struct hda_jack_tbl *jack = codec->jacktbl.list;
+ int i;
+
+ if (!tag || !jack)
+ return NULL;
+ for (i = 0; i < codec->jacktbl.used; i++, jack++)
+ if (jack->tag == tag)
+ return jack;
+ return NULL;
+}
+EXPORT_SYMBOL_GPL(snd_hda_jack_tbl_get_from_tag);
+
+/**
+ * snd_hda_jack_tbl_new - create a jack-table entry for the given NID
+ */
+struct hda_jack_tbl *
+snd_hda_jack_tbl_new(struct hda_codec *codec, hda_nid_t nid)
+{
+ struct hda_jack_tbl *jack = snd_hda_jack_tbl_get(codec, nid);
+ if (jack)
+ return jack;
+ jack = snd_array_new(&codec->jacktbl);
+ if (!jack)
+ return NULL;
+ jack->nid = nid;
+ jack->jack_dirty = 1;
+ jack->tag = codec->jacktbl.used;
+ return jack;
+}
+EXPORT_SYMBOL_GPL(snd_hda_jack_tbl_new);
+
+void snd_hda_jack_tbl_clear(struct hda_codec *codec)
+{
+#ifdef CONFIG_SND_HDA_INPUT_JACK
+ /* free jack instances manually when clearing/reconfiguring */
+ if (!codec->bus->shutdown && codec->jacktbl.list) {
+ struct hda_jack_tbl *jack = codec->jacktbl.list;
+ int i;
+ for (i = 0; i < codec->jacktbl.used; i++, jack++) {
+ if (jack->jack)
+ snd_device_free(codec->bus->card, jack->jack);
+ }
+ }
+#endif
+ snd_array_free(&codec->jacktbl);
+}
+
+#define get_jack_plug_state(sense) !!(sense & AC_PINSENSE_PRESENCE)
+
+/* update the cached value and notification flag if needed */
+static void jack_detect_update(struct hda_codec *codec,
+ struct hda_jack_tbl *jack)
+{
+ if (!jack->jack_dirty)
+ return;
+
+ if (jack->phantom_jack)
+ jack->pin_sense = AC_PINSENSE_PRESENCE;
+ else
+ jack->pin_sense = read_pin_sense(codec, jack->nid);
+
+ /* A gating jack indicates the jack is invalid if gating is unplugged */
+ if (jack->gating_jack && !snd_hda_jack_detect(codec, jack->gating_jack))
+ jack->pin_sense &= ~AC_PINSENSE_PRESENCE;
+
+ jack->jack_dirty = 0;
+
+ /* If a jack is gated by this one update it. */
+ if (jack->gated_jack) {
+ struct hda_jack_tbl *gated =
+ snd_hda_jack_tbl_get(codec, jack->gated_jack);
+ if (gated) {
+ gated->jack_dirty = 1;
+ jack_detect_update(codec, gated);
+ }
+ }
+}
+
+/**
+ * snd_hda_set_dirty_all - Mark all the cached as dirty
+ *
+ * This function sets the dirty flag to all entries of jack table.
+ * It's called from the resume path in hda_codec.c.
+ */
+void snd_hda_jack_set_dirty_all(struct hda_codec *codec)
+{
+ struct hda_jack_tbl *jack = codec->jacktbl.list;
+ int i;
+
+ for (i = 0; i < codec->jacktbl.used; i++, jack++)
+ if (jack->nid)
+ jack->jack_dirty = 1;
+}
+EXPORT_SYMBOL_GPL(snd_hda_jack_set_dirty_all);
+
+/**
+ * snd_hda_pin_sense - execute pin sense measurement
+ * @codec: the CODEC to sense
+ * @nid: the pin NID to sense
+ *
+ * Execute necessary pin sense measurement and return its Presence Detect,
+ * Impedance, ELD Valid etc. status bits.
+ */
+u32 snd_hda_pin_sense(struct hda_codec *codec, hda_nid_t nid)
+{
+ struct hda_jack_tbl *jack = snd_hda_jack_tbl_get(codec, nid);
+ if (jack) {
+ jack_detect_update(codec, jack);
+ return jack->pin_sense;
+ }
+ return read_pin_sense(codec, nid);
+}
+EXPORT_SYMBOL_GPL(snd_hda_pin_sense);
+
+/**
+ * snd_hda_jack_detect_state - query pin Presence Detect status
+ * @codec: the CODEC to sense
+ * @nid: the pin NID to sense
+ *
+ * Query and return the pin's Presence Detect status, as either
+ * HDA_JACK_NOT_PRESENT, HDA_JACK_PRESENT or HDA_JACK_PHANTOM.
+ */
+int snd_hda_jack_detect_state(struct hda_codec *codec, hda_nid_t nid)
+{
+ struct hda_jack_tbl *jack = snd_hda_jack_tbl_get(codec, nid);
+ if (jack && jack->phantom_jack)
+ return HDA_JACK_PHANTOM;
+ else if (snd_hda_pin_sense(codec, nid) & AC_PINSENSE_PRESENCE)
+ return HDA_JACK_PRESENT;
+ else
+ return HDA_JACK_NOT_PRESENT;
+}
+EXPORT_SYMBOL_GPL(snd_hda_jack_detect_state);
+
+/**
+ * snd_hda_jack_detect_enable - enable the jack-detection
+ */
+int snd_hda_jack_detect_enable_callback(struct hda_codec *codec, hda_nid_t nid,
+ unsigned char action,
+ hda_jack_callback cb)
+{
+ struct hda_jack_tbl *jack = snd_hda_jack_tbl_new(codec, nid);
+ if (!jack)
+ return -ENOMEM;
+ if (jack->jack_detect)
+ return 0; /* already registered */
+ jack->jack_detect = 1;
+ if (action)
+ jack->action = action;
+ if (cb)
+ jack->callback = cb;
+ if (codec->jackpoll_interval > 0)
+ return 0; /* No unsol if we're polling instead */
+ return snd_hda_codec_write_cache(codec, nid, 0,
+ AC_VERB_SET_UNSOLICITED_ENABLE,
+ AC_USRSP_EN | jack->tag);
+}
+EXPORT_SYMBOL_GPL(snd_hda_jack_detect_enable_callback);
+
+int snd_hda_jack_detect_enable(struct hda_codec *codec, hda_nid_t nid,
+ unsigned char action)
+{
+ return snd_hda_jack_detect_enable_callback(codec, nid, action, NULL);
+}
+EXPORT_SYMBOL_GPL(snd_hda_jack_detect_enable);
+
+/**
+ * snd_hda_jack_set_gating_jack - Set gating jack.
+ *
+ * Indicates the gated jack is only valid when the gating jack is plugged.
+ */
+int snd_hda_jack_set_gating_jack(struct hda_codec *codec, hda_nid_t gated_nid,
+ hda_nid_t gating_nid)
+{
+ struct hda_jack_tbl *gated = snd_hda_jack_tbl_new(codec, gated_nid);
+ struct hda_jack_tbl *gating = snd_hda_jack_tbl_new(codec, gating_nid);
+
+ if (!gated || !gating)
+ return -EINVAL;
+
+ gated->gating_jack = gating_nid;
+ gating->gated_jack = gated_nid;
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(snd_hda_jack_set_gating_jack);
+
+/**
+ * snd_hda_jack_report_sync - sync the states of all jacks and report if changed
+ */
+void snd_hda_jack_report_sync(struct hda_codec *codec)
+{
+ struct hda_jack_tbl *jack;
+ int i, state;
+
+ /* update all jacks at first */
+ jack = codec->jacktbl.list;
+ for (i = 0; i < codec->jacktbl.used; i++, jack++)
+ if (jack->nid)
+ jack_detect_update(codec, jack);
+
+ /* report the updated jacks; it's done after updating all jacks
+ * to make sure that all gating jacks properly have been set
+ */
+ jack = codec->jacktbl.list;
+ for (i = 0; i < codec->jacktbl.used; i++, jack++)
+ if (jack->nid) {
+ if (!jack->kctl || jack->block_report)
+ continue;
+ state = get_jack_plug_state(jack->pin_sense);
+ snd_kctl_jack_report(codec->bus->card, jack->kctl, state);
+#ifdef CONFIG_SND_HDA_INPUT_JACK
+ if (jack->jack)
+ snd_jack_report(jack->jack,
+ state ? jack->type : 0);
+#endif
+ }
+}
+EXPORT_SYMBOL_GPL(snd_hda_jack_report_sync);
+
+#ifdef CONFIG_SND_HDA_INPUT_JACK
+/* guess the jack type from the pin-config */
+static int get_input_jack_type(struct hda_codec *codec, hda_nid_t nid)
+{
+ unsigned int def_conf = snd_hda_codec_get_pincfg(codec, nid);
+ switch (get_defcfg_device(def_conf)) {
+ case AC_JACK_LINE_OUT:
+ case AC_JACK_SPEAKER:
+ return SND_JACK_LINEOUT;
+ case AC_JACK_HP_OUT:
+ return SND_JACK_HEADPHONE;
+ case AC_JACK_SPDIF_OUT:
+ case AC_JACK_DIG_OTHER_OUT:
+ return SND_JACK_AVOUT;
+ case AC_JACK_MIC_IN:
+ return SND_JACK_MICROPHONE;
+ default:
+ return SND_JACK_LINEIN;
+ }
+}
+
+static void hda_free_jack_priv(struct snd_jack *jack)
+{
+ struct hda_jack_tbl *jacks = jack->private_data;
+ jacks->nid = 0;
+ jacks->jack = NULL;
+}
+#endif
+
+/**
+ * snd_hda_jack_add_kctl - Add a kctl for the given pin
+ *
+ * This assigns a jack-detection kctl to the given pin. The kcontrol
+ * will have the given name and index.
+ */
+static int __snd_hda_jack_add_kctl(struct hda_codec *codec, hda_nid_t nid,
+ const char *name, int idx, bool phantom_jack)
+{
+ struct hda_jack_tbl *jack;
+ struct snd_kcontrol *kctl;
+ int err, state;
+
+ jack = snd_hda_jack_tbl_new(codec, nid);
+ if (!jack)
+ return 0;
+ if (jack->kctl)
+ return 0; /* already created */
+ kctl = snd_kctl_jack_new(name, idx, codec);
+ if (!kctl)
+ return -ENOMEM;
+ err = snd_hda_ctl_add(codec, nid, kctl);
+ if (err < 0)
+ return err;
+ jack->kctl = kctl;
+ jack->phantom_jack = !!phantom_jack;
+
+ state = snd_hda_jack_detect(codec, nid);
+ snd_kctl_jack_report(codec->bus->card, kctl, state);
+#ifdef CONFIG_SND_HDA_INPUT_JACK
+ if (!phantom_jack) {
+ jack->type = get_input_jack_type(codec, nid);
+ err = snd_jack_new(codec->bus->card, name, jack->type,
+ &jack->jack);
+ if (err < 0)
+ return err;
+ jack->jack->private_data = jack;
+ jack->jack->private_free = hda_free_jack_priv;
+ snd_jack_report(jack->jack, state ? jack->type : 0);
+ }
+#endif
+ return 0;
+}
+
+int snd_hda_jack_add_kctl(struct hda_codec *codec, hda_nid_t nid,
+ const char *name, int idx)
+{
+ return __snd_hda_jack_add_kctl(codec, nid, name, idx, false);
+}
+EXPORT_SYMBOL_GPL(snd_hda_jack_add_kctl);
+
+/* get the unique index number for the given kctl name */
+static int get_unique_index(struct hda_codec *codec, const char *name, int idx)
+{
+ struct hda_jack_tbl *jack;
+ int i, len = strlen(name);
+ again:
+ jack = codec->jacktbl.list;
+ for (i = 0; i < codec->jacktbl.used; i++, jack++) {
+ /* jack->kctl.id contains "XXX Jack" name string with index */
+ if (jack->kctl &&
+ !strncmp(name, jack->kctl->id.name, len) &&
+ !strcmp(" Jack", jack->kctl->id.name + len) &&
+ jack->kctl->id.index == idx) {
+ idx++;
+ goto again;
+ }
+ }
+ return idx;
+}
+
+static int add_jack_kctl(struct hda_codec *codec, hda_nid_t nid,
+ const struct auto_pin_cfg *cfg,
+ const char *base_name)
+{
+ unsigned int def_conf, conn;
+ char name[SNDRV_CTL_ELEM_ID_NAME_MAXLEN];
+ int idx, err;
+ bool phantom_jack;
+
+ if (!nid)
+ return 0;
+ def_conf = snd_hda_codec_get_pincfg(codec, nid);
+ conn = get_defcfg_connect(def_conf);
+ if (conn == AC_JACK_PORT_NONE)
+ return 0;
+ phantom_jack = (conn != AC_JACK_PORT_COMPLEX) ||
+ !is_jack_detectable(codec, nid);
+
+ if (base_name) {
+ strlcpy(name, base_name, sizeof(name));
+ idx = 0;
+ } else
+ snd_hda_get_pin_label(codec, nid, cfg, name, sizeof(name), &idx);
+ if (phantom_jack)
+ /* Example final name: "Internal Mic Phantom Jack" */
+ strncat(name, " Phantom", sizeof(name) - strlen(name) - 1);
+ idx = get_unique_index(codec, name, idx);
+ err = __snd_hda_jack_add_kctl(codec, nid, name, idx, phantom_jack);
+ if (err < 0)
+ return err;
+
+ if (!phantom_jack)
+ return snd_hda_jack_detect_enable(codec, nid, 0);
+ return 0;
+}
+
+/**
+ * snd_hda_jack_add_kctls - Add kctls for all pins included in the given pincfg
+ */
+int snd_hda_jack_add_kctls(struct hda_codec *codec,
+ const struct auto_pin_cfg *cfg)
+{
+ const hda_nid_t *p;
+ int i, err;
+
+ for (i = 0; i < cfg->num_inputs; i++) {
+ /* If we have headphone mics; make sure they get the right name
+ before grabbed by output pins */
+ if (cfg->inputs[i].is_headphone_mic) {
+ if (auto_cfg_hp_outs(cfg) == 1)
+ err = add_jack_kctl(codec, auto_cfg_hp_pins(cfg)[0],
+ cfg, "Headphone Mic");
+ else
+ err = add_jack_kctl(codec, cfg->inputs[i].pin,
+ cfg, "Headphone Mic");
+ } else
+ err = add_jack_kctl(codec, cfg->inputs[i].pin, cfg,
+ NULL);
+ if (err < 0)
+ return err;
+ }
+
+ for (i = 0, p = cfg->line_out_pins; i < cfg->line_outs; i++, p++) {
+ err = add_jack_kctl(codec, *p, cfg, NULL);
+ if (err < 0)
+ return err;
+ }
+ for (i = 0, p = cfg->hp_pins; i < cfg->hp_outs; i++, p++) {
+ if (*p == *cfg->line_out_pins) /* might be duplicated */
+ break;
+ err = add_jack_kctl(codec, *p, cfg, NULL);
+ if (err < 0)
+ return err;
+ }
+ for (i = 0, p = cfg->speaker_pins; i < cfg->speaker_outs; i++, p++) {
+ if (*p == *cfg->line_out_pins) /* might be duplicated */
+ break;
+ err = add_jack_kctl(codec, *p, cfg, NULL);
+ if (err < 0)
+ return err;
+ }
+ for (i = 0, p = cfg->dig_out_pins; i < cfg->dig_outs; i++, p++) {
+ err = add_jack_kctl(codec, *p, cfg, NULL);
+ if (err < 0)
+ return err;
+ }
+ err = add_jack_kctl(codec, cfg->dig_in_pin, cfg, NULL);
+ if (err < 0)
+ return err;
+ err = add_jack_kctl(codec, cfg->mono_out_pin, cfg, NULL);
+ if (err < 0)
+ return err;
+ return 0;
+}
+EXPORT_SYMBOL_GPL(snd_hda_jack_add_kctls);
+
+static void call_jack_callback(struct hda_codec *codec,
+ struct hda_jack_tbl *jack)
+{
+ if (jack->callback)
+ jack->callback(codec, jack);
+ if (jack->gated_jack) {
+ struct hda_jack_tbl *gated =
+ snd_hda_jack_tbl_get(codec, jack->gated_jack);
+ if (gated && gated->callback)
+ gated->callback(codec, gated);
+ }
+}
+
+void snd_hda_jack_unsol_event(struct hda_codec *codec, unsigned int res)
+{
+ struct hda_jack_tbl *event;
+ int tag = (res >> AC_UNSOL_RES_TAG_SHIFT) & 0x7f;
+
+ event = snd_hda_jack_tbl_get_from_tag(codec, tag);
+ if (!event)
+ return;
+ event->jack_dirty = 1;
+
+ call_jack_callback(codec, event);
+ snd_hda_jack_report_sync(codec);
+}
+EXPORT_SYMBOL_GPL(snd_hda_jack_unsol_event);
+
+void snd_hda_jack_poll_all(struct hda_codec *codec)
+{
+ struct hda_jack_tbl *jack = codec->jacktbl.list;
+ int i, changes = 0;
+
+ for (i = 0; i < codec->jacktbl.used; i++, jack++) {
+ unsigned int old_sense;
+ if (!jack->nid || !jack->jack_dirty || jack->phantom_jack)
+ continue;
+ old_sense = get_jack_plug_state(jack->pin_sense);
+ jack_detect_update(codec, jack);
+ if (old_sense == get_jack_plug_state(jack->pin_sense))
+ continue;
+ changes = 1;
+ call_jack_callback(codec, jack);
+ }
+ if (changes)
+ snd_hda_jack_report_sync(codec);
+}
+EXPORT_SYMBOL_GPL(snd_hda_jack_poll_all);
+
diff --git a/sound/pci/hda/hda_jack.h b/sound/pci/hda/hda_jack.h
new file mode 100644
index 00000000000..46e1ea83ce3
--- /dev/null
+++ b/sound/pci/hda/hda_jack.h
@@ -0,0 +1,105 @@
+/*
+ * Jack-detection handling for HD-audio
+ *
+ * Copyright (c) 2011 Takashi Iwai <tiwai@suse.de>
+ *
+ * This driver is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ */
+
+#ifndef __SOUND_HDA_JACK_H
+#define __SOUND_HDA_JACK_H
+
+struct auto_pin_cfg;
+struct hda_jack_tbl;
+
+typedef void (*hda_jack_callback) (struct hda_codec *, struct hda_jack_tbl *);
+
+struct hda_jack_tbl {
+ hda_nid_t nid;
+ unsigned char action; /* event action (0 = none) */
+ unsigned char tag; /* unsol event tag */
+ unsigned int private_data; /* arbitrary data */
+ hda_jack_callback callback;
+ /* jack-detection stuff */
+ unsigned int pin_sense; /* cached pin-sense value */
+ unsigned int jack_detect:1; /* capable of jack-detection? */
+ unsigned int jack_dirty:1; /* needs to update? */
+ unsigned int phantom_jack:1; /* a fixed, always present port? */
+ unsigned int block_report:1; /* in a transitional state - do not report to userspace */
+ hda_nid_t gating_jack; /* valid when gating jack plugged */
+ hda_nid_t gated_jack; /* gated is dependent on this jack */
+ struct snd_kcontrol *kctl; /* assigned kctl for jack-detection */
+#ifdef CONFIG_SND_HDA_INPUT_JACK
+ int type;
+ struct snd_jack *jack;
+#endif
+};
+
+struct hda_jack_tbl *
+snd_hda_jack_tbl_get(struct hda_codec *codec, hda_nid_t nid);
+struct hda_jack_tbl *
+snd_hda_jack_tbl_get_from_tag(struct hda_codec *codec, unsigned char tag);
+
+struct hda_jack_tbl *
+snd_hda_jack_tbl_new(struct hda_codec *codec, hda_nid_t nid);
+void snd_hda_jack_tbl_clear(struct hda_codec *codec);
+
+/**
+ * snd_hda_jack_get_action - get jack-tbl entry for the tag
+ *
+ * Call this from the unsol event handler to get the assigned action for the
+ * event. This will mark the dirty flag for the later reporting, too.
+ */
+static inline unsigned char
+snd_hda_jack_get_action(struct hda_codec *codec, unsigned int tag)
+{
+ struct hda_jack_tbl *jack = snd_hda_jack_tbl_get_from_tag(codec, tag);
+ if (jack) {
+ jack->jack_dirty = 1;
+ return jack->action;
+ }
+ return 0;
+}
+
+void snd_hda_jack_set_dirty_all(struct hda_codec *codec);
+
+int snd_hda_jack_detect_enable(struct hda_codec *codec, hda_nid_t nid,
+ unsigned char action);
+int snd_hda_jack_detect_enable_callback(struct hda_codec *codec, hda_nid_t nid,
+ unsigned char action,
+ hda_jack_callback cb);
+
+int snd_hda_jack_set_gating_jack(struct hda_codec *codec, hda_nid_t gated_nid,
+ hda_nid_t gating_nid);
+
+u32 snd_hda_pin_sense(struct hda_codec *codec, hda_nid_t nid);
+
+/* the jack state returned from snd_hda_jack_detect_state() */
+enum {
+ HDA_JACK_NOT_PRESENT, HDA_JACK_PRESENT, HDA_JACK_PHANTOM,
+};
+
+int snd_hda_jack_detect_state(struct hda_codec *codec, hda_nid_t nid);
+
+static inline bool snd_hda_jack_detect(struct hda_codec *codec, hda_nid_t nid)
+{
+ return snd_hda_jack_detect_state(codec, nid) != HDA_JACK_NOT_PRESENT;
+}
+
+bool is_jack_detectable(struct hda_codec *codec, hda_nid_t nid);
+
+int snd_hda_jack_add_kctl(struct hda_codec *codec, hda_nid_t nid,
+ const char *name, int idx);
+int snd_hda_jack_add_kctls(struct hda_codec *codec,
+ const struct auto_pin_cfg *cfg);
+
+void snd_hda_jack_report_sync(struct hda_codec *codec);
+
+void snd_hda_jack_unsol_event(struct hda_codec *codec, unsigned int res);
+
+void snd_hda_jack_poll_all(struct hda_codec *codec);
+
+#endif /* __SOUND_HDA_JACK_H */
diff --git a/sound/pci/hda/hda_local.h b/sound/pci/hda/hda_local.h
index ff5e2ac2239..4e2d4863daa 100644
--- a/sound/pci/hda/hda_local.h
+++ b/sound/pci/hda/hda_local.h
@@ -89,7 +89,7 @@
{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, .index = xcidx, \
.subdevice = HDA_SUBDEV_AMP_FLAG, \
.info = snd_hda_mixer_amp_switch_info, \
- .get = snd_hda_mixer_amp_switch_get, \
+ .get = snd_hda_mixer_amp_switch_get_beep, \
.put = snd_hda_mixer_amp_switch_put_beep, \
.private_value = HDA_COMPOSE_AMP_VAL(nid, channel, xindex, direction) }
#else
@@ -121,6 +121,8 @@ int snd_hda_mixer_amp_switch_get(struct snd_kcontrol *kcontrol,
int snd_hda_mixer_amp_switch_put(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol);
#ifdef CONFIG_SND_HDA_INPUT_BEEP
+int snd_hda_mixer_amp_switch_get_beep(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol);
int snd_hda_mixer_amp_switch_put_beep(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol);
#endif
@@ -131,18 +133,46 @@ int snd_hda_codec_amp_update(struct hda_codec *codec, hda_nid_t nid, int ch,
int direction, int idx, int mask, int val);
int snd_hda_codec_amp_stereo(struct hda_codec *codec, hda_nid_t nid,
int dir, int idx, int mask, int val);
-#ifdef SND_HDA_NEEDS_RESUME
+int snd_hda_codec_amp_init(struct hda_codec *codec, hda_nid_t nid, int ch,
+ int direction, int idx, int mask, int val);
+int snd_hda_codec_amp_init_stereo(struct hda_codec *codec, hda_nid_t nid,
+ int dir, int idx, int mask, int val);
void snd_hda_codec_resume_amp(struct hda_codec *codec);
-#endif
void snd_hda_set_vmaster_tlv(struct hda_codec *codec, hda_nid_t nid, int dir,
unsigned int *tlv);
struct snd_kcontrol *snd_hda_find_mixer_ctl(struct hda_codec *codec,
const char *name);
-int snd_hda_add_vmaster(struct hda_codec *codec, char *name,
- unsigned int *tlv, const char * const *slaves);
+int __snd_hda_add_vmaster(struct hda_codec *codec, char *name,
+ unsigned int *tlv, const char * const *slaves,
+ const char *suffix, bool init_slave_vol,
+ struct snd_kcontrol **ctl_ret);
+#define snd_hda_add_vmaster(codec, name, tlv, slaves, suffix) \
+ __snd_hda_add_vmaster(codec, name, tlv, slaves, suffix, true, NULL)
int snd_hda_codec_reset(struct hda_codec *codec);
+enum {
+ HDA_VMUTE_OFF,
+ HDA_VMUTE_ON,
+ HDA_VMUTE_FOLLOW_MASTER,
+};
+
+struct hda_vmaster_mute_hook {
+ /* below two fields must be filled by the caller of
+ * snd_hda_add_vmaster_hook() beforehand
+ */
+ struct snd_kcontrol *sw_kctl;
+ void (*hook)(void *, int);
+ /* below are initialized automatically */
+ unsigned int mute_mode; /* HDA_VMUTE_XXX */
+ struct hda_codec *codec;
+};
+
+int snd_hda_add_vmaster_hook(struct hda_codec *codec,
+ struct hda_vmaster_mute_hook *hook,
+ bool expose_enum_ctl);
+void snd_hda_sync_vmaster_hook(struct hda_vmaster_mute_hook *hook);
+
/* amp value bits */
#define HDA_AMP_MUTE 0x80
#define HDA_AMP_UNMUTE 0x00
@@ -212,7 +242,11 @@ int snd_hda_mixer_bind_tlv(struct snd_kcontrol *kcontrol, int op_flag,
/*
* SPDIF I/O
*/
-int snd_hda_create_spdif_out_ctls(struct hda_codec *codec, hda_nid_t nid);
+int snd_hda_create_dig_out_ctls(struct hda_codec *codec,
+ hda_nid_t associated_nid,
+ hda_nid_t cvt_nid, int type);
+#define snd_hda_create_spdif_out_ctls(codec, anid, cnid) \
+ snd_hda_create_dig_out_ctls(codec, anid, cnid, HDA_PCM_TYPE_SPDIF)
int snd_hda_create_spdif_in_ctls(struct hda_codec *codec, hda_nid_t nid);
/*
@@ -234,6 +268,8 @@ int snd_hda_input_mux_put(struct hda_codec *codec,
const struct hda_input_mux *imux,
struct snd_ctl_elem_value *ucontrol, hda_nid_t nid,
unsigned int *cur_val);
+int snd_hda_add_imux_item(struct hda_input_mux *imux, const char *label,
+ int index, int *type_index_ret);
/*
* Channel mode helper
@@ -265,13 +301,16 @@ int snd_hda_ch_mode_put(struct hda_codec *codec,
enum { HDA_FRONT, HDA_REAR, HDA_CLFE, HDA_SIDE }; /* index for dac_nidx */
enum { HDA_DIG_NONE, HDA_DIG_EXCLUSIVE, HDA_DIG_ANALOG_DUP }; /* dig_out_used */
+#define HDA_MAX_OUTS 5
+
struct hda_multi_out {
int num_dacs; /* # of DACs, must be more than 1 */
- hda_nid_t *dac_nids; /* DAC list */
+ const hda_nid_t *dac_nids; /* DAC list */
hda_nid_t hp_nid; /* optional DAC for HP, 0 when not exists */
- hda_nid_t extra_out_nid[3]; /* optional DACs, 0 when not exists */
+ hda_nid_t hp_out_nid[HDA_MAX_OUTS]; /* DACs for multiple HPs */
+ hda_nid_t extra_out_nid[HDA_MAX_OUTS]; /* other (e.g. speaker) DACs */
hda_nid_t dig_out_nid; /* digital out audio widget */
- hda_nid_t *slave_dig_outs;
+ const hda_nid_t *slave_dig_outs;
int max_channels; /* currently supported analog channels */
int dig_out_used; /* current usage of digital out (HDA_DIG_XXX) */
int no_share_stream; /* don't share a stream with multiple pins */
@@ -313,14 +352,8 @@ int snd_hda_multi_out_analog_cleanup(struct hda_codec *codec,
/*
* generic codec parser
*/
-#ifdef CONFIG_SND_HDA_GENERIC
int snd_hda_parse_generic_codec(struct hda_codec *codec);
-#else
-static inline int snd_hda_parse_generic_codec(struct hda_codec *codec)
-{
- return -ENODEV;
-}
-#endif
+int snd_hda_parse_hdmi_codec(struct hda_codec *codec);
/*
* generic proc interface
@@ -331,9 +364,6 @@ int snd_hda_codec_proc_new(struct hda_codec *codec);
static inline int snd_hda_codec_proc_new(struct hda_codec *codec) { return 0; }
#endif
-#define SND_PRINT_RATES_ADVISED_BUFSIZE 80
-void snd_print_pcm_rates(int pcm, char *buf, int buflen);
-
#define SND_PRINT_BITS_ADVISED_BUFSIZE 16
void snd_print_pcm_bits(int pcm, char *buf, int buflen);
@@ -347,89 +377,116 @@ int snd_hda_check_board_codec_sid_config(struct hda_codec *codec,
int num_configs, const char * const *models,
const struct snd_pci_quirk *tbl);
int snd_hda_add_new_ctls(struct hda_codec *codec,
- struct snd_kcontrol_new *knew);
+ const struct snd_kcontrol_new *knew);
/*
- * unsolicited event handler
+ * Fix-up pin default configurations and add default verbs
*/
-#define HDA_UNSOL_QUEUE_SIZE 64
+struct hda_pintbl {
+ hda_nid_t nid;
+ u32 val;
+};
-struct hda_bus_unsolicited {
- /* ring buffer */
- u32 queue[HDA_UNSOL_QUEUE_SIZE * 2];
- unsigned int rp, wp;
+struct hda_model_fixup {
+ const int id;
+ const char *name;
+};
- /* workqueue */
- struct work_struct work;
- struct hda_bus *bus;
+struct hda_fixup {
+ int type;
+ bool chained:1; /* call the chained fixup(s) after this */
+ bool chained_before:1; /* call the chained fixup(s) before this */
+ int chain_id;
+ union {
+ const struct hda_pintbl *pins;
+ const struct hda_verb *verbs;
+ void (*func)(struct hda_codec *codec,
+ const struct hda_fixup *fix,
+ int action);
+ } v;
};
-/*
- * Helper for automatic pin configuration
- */
+struct snd_hda_pin_quirk {
+ unsigned int codec; /* Codec vendor/device ID */
+ unsigned short subvendor; /* PCI subvendor ID */
+ const struct hda_pintbl *pins; /* list of matching pins */
+#ifdef CONFIG_SND_DEBUG_VERBOSE
+ const char *name;
+#endif
+ int value; /* quirk value */
+};
+
+#ifdef CONFIG_SND_DEBUG_VERBOSE
+#define SND_HDA_PIN_QUIRK(_codec, _subvendor, _name, _value, _pins...) \
+ { .codec = _codec,\
+ .subvendor = _subvendor,\
+ .name = _name,\
+ .value = _value,\
+ .pins = (const struct hda_pintbl[]) { _pins } \
+ }
+#else
+
+#define SND_HDA_PIN_QUIRK(_codec, _subvendor, _name, _value, _pins...) \
+ { .codec = _codec,\
+ .subvendor = _subvendor,\
+ .value = _value,\
+ .pins = (const struct hda_pintbl[]) { _pins } \
+ }
+
+#endif
+
+
+/* fixup types */
enum {
- AUTO_PIN_MIC,
- AUTO_PIN_LINE_IN,
- AUTO_PIN_CD,
- AUTO_PIN_AUX,
- AUTO_PIN_LAST
+ HDA_FIXUP_INVALID,
+ HDA_FIXUP_PINS,
+ HDA_FIXUP_VERBS,
+ HDA_FIXUP_FUNC,
+ HDA_FIXUP_PINCTLS,
};
+/* fixup action definitions */
enum {
- AUTO_PIN_LINE_OUT,
- AUTO_PIN_SPEAKER_OUT,
- AUTO_PIN_HP_OUT
+ HDA_FIXUP_ACT_PRE_PROBE,
+ HDA_FIXUP_ACT_PROBE,
+ HDA_FIXUP_ACT_INIT,
+ HDA_FIXUP_ACT_BUILD,
+ HDA_FIXUP_ACT_FREE,
};
-#define AUTO_CFG_MAX_OUTS 5
-#define AUTO_CFG_MAX_INS 8
+int snd_hda_add_verbs(struct hda_codec *codec, const struct hda_verb *list);
+void snd_hda_apply_verbs(struct hda_codec *codec);
+void snd_hda_apply_pincfgs(struct hda_codec *codec,
+ const struct hda_pintbl *cfg);
+void snd_hda_apply_fixup(struct hda_codec *codec, int action);
+void snd_hda_pick_fixup(struct hda_codec *codec,
+ const struct hda_model_fixup *models,
+ const struct snd_pci_quirk *quirk,
+ const struct hda_fixup *fixlist);
+void snd_hda_pick_pin_fixup(struct hda_codec *codec,
+ const struct snd_hda_pin_quirk *pin_quirk,
+ const struct hda_fixup *fixlist);
-struct auto_pin_cfg_item {
- hda_nid_t pin;
- int type;
-};
-struct auto_pin_cfg;
-const char *hda_get_input_pin_label(struct hda_codec *codec, hda_nid_t pin,
- int check_location);
-const char *hda_get_autocfg_input_label(struct hda_codec *codec,
- const struct auto_pin_cfg *cfg,
- int input);
-int snd_hda_add_imux_item(struct hda_input_mux *imux, const char *label,
- int index, int *type_index_ret);
+/*
+ * unsolicited event handler
+ */
-enum {
- INPUT_PIN_ATTR_UNUSED, /* pin not connected */
- INPUT_PIN_ATTR_INT, /* internal mic/line-in */
- INPUT_PIN_ATTR_DOCK, /* docking mic/line-in */
- INPUT_PIN_ATTR_NORMAL, /* mic/line-in jack */
- INPUT_PIN_ATTR_FRONT, /* mic/line-in jack in front */
- INPUT_PIN_ATTR_REAR, /* mic/line-in jack in rear */
-};
+#define HDA_UNSOL_QUEUE_SIZE 64
-int snd_hda_get_input_pin_attr(unsigned int def_conf);
-
-struct auto_pin_cfg {
- int line_outs;
- /* sorted in the order of Front/Surr/CLFE/Side */
- hda_nid_t line_out_pins[AUTO_CFG_MAX_OUTS];
- int speaker_outs;
- hda_nid_t speaker_pins[AUTO_CFG_MAX_OUTS];
- int hp_outs;
- int line_out_type; /* AUTO_PIN_XXX_OUT */
- hda_nid_t hp_pins[AUTO_CFG_MAX_OUTS];
- int num_inputs;
- struct auto_pin_cfg_item inputs[AUTO_CFG_MAX_INS];
- int dig_outs;
- hda_nid_t dig_out_pins[2];
- hda_nid_t dig_in_pin;
- hda_nid_t mono_out_pin;
- int dig_out_type[2]; /* HDA_PCM_TYPE_XXX */
- int dig_in_type; /* HDA_PCM_TYPE_XXX */
+struct hda_bus_unsolicited {
+ /* ring buffer */
+ u32 queue[HDA_UNSOL_QUEUE_SIZE * 2];
+ unsigned int rp, wp;
+
+ /* workqueue */
+ struct work_struct work;
+ struct hda_bus *bus;
};
+/* helper macros to retrieve pin default-config values */
#define get_defcfg_connect(cfg) \
((cfg & AC_DEFCFG_PORT_CONN) >> AC_DEFCFG_PORT_CONN_SHIFT)
#define get_defcfg_association(cfg) \
@@ -440,10 +497,8 @@ struct auto_pin_cfg {
(cfg & AC_DEFCFG_SEQUENCE)
#define get_defcfg_device(cfg) \
((cfg & AC_DEFCFG_DEVICE) >> AC_DEFCFG_DEVICE_SHIFT)
-
-int snd_hda_parse_pin_def_config(struct hda_codec *codec,
- struct auto_pin_cfg *cfg,
- hda_nid_t *ignore_nids);
+#define get_defcfg_misc(cfg) \
+ ((cfg & AC_DEFCFG_MISC) >> AC_DEFCFG_MISC_SHIFT)
/* amp values */
#define AMP_IN_MUTE(idx) (0x7080 | ((idx)<<8))
@@ -462,6 +517,52 @@ int snd_hda_parse_pin_def_config(struct hda_codec *codec,
#define PIN_HP (AC_PINCTL_OUT_EN | AC_PINCTL_HP_EN)
#define PIN_HP_AMP (AC_PINCTL_HP_EN)
+unsigned int snd_hda_get_default_vref(struct hda_codec *codec, hda_nid_t pin);
+unsigned int snd_hda_correct_pin_ctl(struct hda_codec *codec,
+ hda_nid_t pin, unsigned int val);
+int _snd_hda_set_pin_ctl(struct hda_codec *codec, hda_nid_t pin,
+ unsigned int val, bool cached);
+
+/**
+ * _snd_hda_set_pin_ctl - Set a pin-control value safely
+ * @codec: the codec instance
+ * @pin: the pin NID to set the control
+ * @val: the pin-control value (AC_PINCTL_* bits)
+ *
+ * This function sets the pin-control value to the given pin, but
+ * filters out the invalid pin-control bits when the pin has no such
+ * capabilities. For example, when PIN_HP is passed but the pin has no
+ * HP-drive capability, the HP bit is omitted.
+ *
+ * The function doesn't check the input VREF capability bits, though.
+ * Use snd_hda_get_default_vref() to guess the right value.
+ * Also, this function is only for analog pins, not for HDMI pins.
+ */
+static inline int
+snd_hda_set_pin_ctl(struct hda_codec *codec, hda_nid_t pin, unsigned int val)
+{
+ return _snd_hda_set_pin_ctl(codec, pin, val, false);
+}
+
+/**
+ * snd_hda_set_pin_ctl_cache - Set a pin-control value safely
+ * @codec: the codec instance
+ * @pin: the pin NID to set the control
+ * @val: the pin-control value (AC_PINCTL_* bits)
+ *
+ * Just like snd_hda_set_pin_ctl() but write to cache as well.
+ */
+static inline int
+snd_hda_set_pin_ctl_cache(struct hda_codec *codec, hda_nid_t pin,
+ unsigned int val)
+{
+ return _snd_hda_set_pin_ctl(codec, pin, val, true);
+}
+
+int snd_hda_codec_get_pin_target(struct hda_codec *codec, hda_nid_t nid);
+int snd_hda_codec_set_pin_target(struct hda_codec *codec, hda_nid_t nid,
+ unsigned int val);
+
/*
* get widget capabilities
*/
@@ -474,7 +575,12 @@ static inline u32 get_wcaps(struct hda_codec *codec, hda_nid_t nid)
}
/* get the widget type from widget capability bits */
-#define get_wcaps_type(wcaps) (((wcaps) & AC_WCAP_TYPE) >> AC_WCAP_TYPE_SHIFT)
+static inline int get_wcaps_type(unsigned int wcaps)
+{
+ if (!wcaps)
+ return -1; /* invalid type */
+ return (wcaps & AC_WCAP_TYPE) >> AC_WCAP_TYPE_SHIFT;
+}
static inline unsigned int get_wcaps_channels(u32 wcaps)
{
@@ -486,12 +592,20 @@ static inline unsigned int get_wcaps_channels(u32 wcaps)
return chans;
}
+static inline void snd_hda_override_wcaps(struct hda_codec *codec,
+ hda_nid_t nid, u32 val)
+{
+ if (nid >= codec->start_nid &&
+ nid < codec->start_nid + codec->num_nodes)
+ codec->wcaps[nid - codec->start_nid] = val;
+}
+
u32 query_amp_caps(struct hda_codec *codec, hda_nid_t nid, int direction);
int snd_hda_override_amp_caps(struct hda_codec *codec, hda_nid_t nid, int dir,
unsigned int caps);
u32 snd_hda_query_pin_caps(struct hda_codec *codec, hda_nid_t nid);
-u32 snd_hda_pin_sense(struct hda_codec *codec, hda_nid_t nid);
-int snd_hda_jack_detect(struct hda_codec *codec, hda_nid_t nid);
+int snd_hda_override_pin_caps(struct hda_codec *codec, hda_nid_t nid,
+ unsigned int caps);
/* flags for hda_nid_item */
#define HDA_NID_ITEM_AMP (1<<0)
@@ -518,27 +632,15 @@ int snd_hda_create_hwdep(struct hda_codec *codec);
static inline int snd_hda_create_hwdep(struct hda_codec *codec) { return 0; }
#endif
-#if defined(CONFIG_SND_HDA_POWER_SAVE) && defined(CONFIG_SND_HDA_HWDEP)
-int snd_hda_hwdep_add_power_sysfs(struct hda_codec *codec);
-#else
-static inline int snd_hda_hwdep_add_power_sysfs(struct hda_codec *codec)
-{
- return 0;
-}
-#endif
+void snd_hda_sysfs_init(struct hda_codec *codec);
+void snd_hda_sysfs_clear(struct hda_codec *codec);
-#ifdef CONFIG_SND_HDA_RECONFIG
-int snd_hda_hwdep_add_sysfs(struct hda_codec *codec);
-#else
-static inline int snd_hda_hwdep_add_sysfs(struct hda_codec *codec)
-{
- return 0;
-}
-#endif
+extern const struct attribute_group *snd_hda_dev_attr_groups[];
#ifdef CONFIG_SND_HDA_RECONFIG
const char *snd_hda_get_hint(struct hda_codec *codec, const char *key);
int snd_hda_get_bool_hint(struct hda_codec *codec, const char *key);
+int snd_hda_get_int_hint(struct hda_codec *codec, const char *key, int *valp);
#else
static inline
const char *snd_hda_get_hint(struct hda_codec *codec, const char *key)
@@ -551,13 +653,18 @@ int snd_hda_get_bool_hint(struct hda_codec *codec, const char *key)
{
return -ENOENT;
}
+
+static inline
+int snd_hda_get_int_hint(struct hda_codec *codec, const char *key, int *valp)
+{
+ return -ENOENT;
+}
#endif
/*
* power-management
*/
-#ifdef CONFIG_SND_HDA_POWER_SAVE
void snd_hda_schedule_power_save(struct hda_codec *codec);
struct hda_amp_list {
@@ -567,14 +674,30 @@ struct hda_amp_list {
};
struct hda_loopback_check {
- struct hda_amp_list *amplist;
+ const struct hda_amp_list *amplist;
int power_on;
};
int snd_hda_check_amp_list_power(struct hda_codec *codec,
struct hda_loopback_check *check,
hda_nid_t nid);
-#endif /* CONFIG_SND_HDA_POWER_SAVE */
+
+/* check whether the actual power state matches with the target state */
+static inline bool
+snd_hda_check_power_state(struct hda_codec *codec, hda_nid_t nid,
+ unsigned int target_state)
+{
+ unsigned int state = snd_hda_codec_read(codec, nid, 0,
+ AC_VERB_GET_POWER_STATE, 0);
+ if (state & AC_PWRST_ERROR)
+ return true;
+ state = (state >> 4) & 0x0f;
+ return (state == target_state);
+}
+
+unsigned int snd_hda_codec_eapd_power_filter(struct hda_codec *codec,
+ hda_nid_t nid,
+ unsigned int power_state);
/*
* AMP control callbacks
@@ -583,12 +706,23 @@ int snd_hda_check_amp_list_power(struct hda_codec *codec,
#define get_amp_nid_(pv) ((pv) & 0xffff)
#define get_amp_nid(kc) get_amp_nid_((kc)->private_value)
#define get_amp_channels(kc) (((kc)->private_value >> 16) & 0x3)
-#define get_amp_direction(kc) (((kc)->private_value >> 18) & 0x1)
-#define get_amp_index(kc) (((kc)->private_value >> 19) & 0xf)
+#define get_amp_direction_(pv) (((pv) >> 18) & 0x1)
+#define get_amp_direction(kc) get_amp_direction_((kc)->private_value)
+#define get_amp_index_(pv) (((pv) >> 19) & 0xf)
+#define get_amp_index(kc) get_amp_index_((kc)->private_value)
#define get_amp_offset(kc) (((kc)->private_value >> 23) & 0x3f)
#define get_amp_min_mute(kc) (((kc)->private_value >> 29) & 0x1)
/*
+ * enum control helper
+ */
+int snd_hda_enum_helper_info(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_info *uinfo,
+ int num_entries, const char * const *texts);
+#define snd_hda_enum_bool_helper_info(kcontrol, uinfo) \
+ snd_hda_enum_helper_info(kcontrol, uinfo, 0, NULL)
+
+/*
* CEA Short Audio Descriptor data
*/
struct cea_sad {
@@ -601,16 +735,17 @@ struct cea_sad {
};
#define ELD_FIXED_BYTES 20
+#define ELD_MAX_SIZE 256
#define ELD_MAX_MNL 16
#define ELD_MAX_SAD 16
/*
* ELD: EDID Like Data
*/
-struct hdmi_eld {
- bool monitor_present;
- bool eld_valid;
- int eld_size;
+struct parsed_hdmi_eld {
+ /*
+ * all fields will be cleared before updating ELD
+ */
int baseline_len;
int eld_ver;
int cea_edid_ver;
@@ -625,59 +760,44 @@ struct hdmi_eld {
int spk_alloc;
int sad_count;
struct cea_sad sad[ELD_MAX_SAD];
-#ifdef CONFIG_PROC_FS
- struct snd_info_entry *proc_entry;
-#endif
+};
+
+struct hdmi_eld {
+ bool monitor_present;
+ bool eld_valid;
+ int eld_size;
+ char eld_buffer[ELD_MAX_SIZE];
+ struct parsed_hdmi_eld info;
};
int snd_hdmi_get_eld_size(struct hda_codec *codec, hda_nid_t nid);
-int snd_hdmi_get_eld(struct hdmi_eld *, struct hda_codec *, hda_nid_t);
-void snd_hdmi_show_eld(struct hdmi_eld *eld);
-void hdmi_eld_update_pcm_info(struct hdmi_eld *eld, struct hda_pcm_stream *pcm,
- struct hda_pcm_stream *codec_pars);
+int snd_hdmi_get_eld(struct hda_codec *codec, hda_nid_t nid,
+ unsigned char *buf, int *eld_size);
+int snd_hdmi_parse_eld(struct parsed_hdmi_eld *e,
+ const unsigned char *buf, int size);
+void snd_hdmi_show_eld(struct parsed_hdmi_eld *e);
+void snd_hdmi_eld_update_pcm_info(struct parsed_hdmi_eld *e,
+ struct hda_pcm_stream *hinfo);
+
+int snd_hdmi_get_eld_ati(struct hda_codec *codec, hda_nid_t nid,
+ unsigned char *buf, int *eld_size,
+ bool rev3_or_later);
#ifdef CONFIG_PROC_FS
-int snd_hda_eld_proc_new(struct hda_codec *codec, struct hdmi_eld *eld,
- int index);
-void snd_hda_eld_proc_free(struct hda_codec *codec, struct hdmi_eld *eld);
-#else
-static inline int snd_hda_eld_proc_new(struct hda_codec *codec,
- struct hdmi_eld *eld,
- int index)
-{
- return 0;
-}
-static inline void snd_hda_eld_proc_free(struct hda_codec *codec,
- struct hdmi_eld *eld)
-{
-}
+void snd_hdmi_print_eld_info(struct hdmi_eld *eld,
+ struct snd_info_buffer *buffer);
+void snd_hdmi_write_eld_info(struct hdmi_eld *eld,
+ struct snd_info_buffer *buffer);
#endif
#define SND_PRINT_CHANNEL_ALLOCATION_ADVISED_BUFSIZE 80
void snd_print_channel_allocation(int spk_alloc, char *buf, int buflen);
/*
- * Input-jack notification support
*/
-#ifdef CONFIG_SND_HDA_INPUT_JACK
-int snd_hda_input_jack_add(struct hda_codec *codec, hda_nid_t nid, int type,
- const char *name);
-void snd_hda_input_jack_report(struct hda_codec *codec, hda_nid_t nid);
-void snd_hda_input_jack_free(struct hda_codec *codec);
-#else /* CONFIG_SND_HDA_INPUT_JACK */
-static inline int snd_hda_input_jack_add(struct hda_codec *codec,
- hda_nid_t nid, int type,
- const char *name)
-{
- return 0;
-}
-static inline void snd_hda_input_jack_report(struct hda_codec *codec,
- hda_nid_t nid)
-{
-}
-static inline void snd_hda_input_jack_free(struct hda_codec *codec)
-{
-}
-#endif /* CONFIG_SND_HDA_INPUT_JACK */
+#define codec_err(codec, fmt, args...) dev_err(&(codec)->dev, fmt, ##args)
+#define codec_warn(codec, fmt, args...) dev_warn(&(codec)->dev, fmt, ##args)
+#define codec_info(codec, fmt, args...) dev_info(&(codec)->dev, fmt, ##args)
+#define codec_dbg(codec, fmt, args...) dev_dbg(&(codec)->dev, fmt, ##args)
#endif /* __SOUND_HDA_LOCAL_H */
diff --git a/sound/pci/hda/hda_priv.h b/sound/pci/hda/hda_priv.h
new file mode 100644
index 00000000000..e9d1a5762a5
--- /dev/null
+++ b/sound/pci/hda/hda_priv.h
@@ -0,0 +1,465 @@
+/*
+ * Common defines for the alsa driver code base for HD Audio.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the Free
+ * Software Foundation; either version 2 of the License, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ */
+
+#ifndef __SOUND_HDA_PRIV_H
+#define __SOUND_HDA_PRIV_H
+
+#include <linux/clocksource.h>
+#include <sound/core.h>
+#include <sound/pcm.h>
+
+/*
+ * registers
+ */
+#define ICH6_REG_GCAP 0x00
+#define ICH6_GCAP_64OK (1 << 0) /* 64bit address support */
+#define ICH6_GCAP_NSDO (3 << 1) /* # of serial data out signals */
+#define ICH6_GCAP_BSS (31 << 3) /* # of bidirectional streams */
+#define ICH6_GCAP_ISS (15 << 8) /* # of input streams */
+#define ICH6_GCAP_OSS (15 << 12) /* # of output streams */
+#define ICH6_REG_VMIN 0x02
+#define ICH6_REG_VMAJ 0x03
+#define ICH6_REG_OUTPAY 0x04
+#define ICH6_REG_INPAY 0x06
+#define ICH6_REG_GCTL 0x08
+#define ICH6_GCTL_RESET (1 << 0) /* controller reset */
+#define ICH6_GCTL_FCNTRL (1 << 1) /* flush control */
+#define ICH6_GCTL_UNSOL (1 << 8) /* accept unsol. response enable */
+#define ICH6_REG_WAKEEN 0x0c
+#define ICH6_REG_STATESTS 0x0e
+#define ICH6_REG_GSTS 0x10
+#define ICH6_GSTS_FSTS (1 << 1) /* flush status */
+#define ICH6_REG_INTCTL 0x20
+#define ICH6_REG_INTSTS 0x24
+#define ICH6_REG_WALLCLK 0x30 /* 24Mhz source */
+#define ICH6_REG_OLD_SSYNC 0x34 /* SSYNC for old ICH */
+#define ICH6_REG_SSYNC 0x38
+#define ICH6_REG_CORBLBASE 0x40
+#define ICH6_REG_CORBUBASE 0x44
+#define ICH6_REG_CORBWP 0x48
+#define ICH6_REG_CORBRP 0x4a
+#define ICH6_CORBRP_RST (1 << 15) /* read pointer reset */
+#define ICH6_REG_CORBCTL 0x4c
+#define ICH6_CORBCTL_RUN (1 << 1) /* enable DMA */
+#define ICH6_CORBCTL_CMEIE (1 << 0) /* enable memory error irq */
+#define ICH6_REG_CORBSTS 0x4d
+#define ICH6_CORBSTS_CMEI (1 << 0) /* memory error indication */
+#define ICH6_REG_CORBSIZE 0x4e
+
+#define ICH6_REG_RIRBLBASE 0x50
+#define ICH6_REG_RIRBUBASE 0x54
+#define ICH6_REG_RIRBWP 0x58
+#define ICH6_RIRBWP_RST (1 << 15) /* write pointer reset */
+#define ICH6_REG_RINTCNT 0x5a
+#define ICH6_REG_RIRBCTL 0x5c
+#define ICH6_RBCTL_IRQ_EN (1 << 0) /* enable IRQ */
+#define ICH6_RBCTL_DMA_EN (1 << 1) /* enable DMA */
+#define ICH6_RBCTL_OVERRUN_EN (1 << 2) /* enable overrun irq */
+#define ICH6_REG_RIRBSTS 0x5d
+#define ICH6_RBSTS_IRQ (1 << 0) /* response irq */
+#define ICH6_RBSTS_OVERRUN (1 << 2) /* overrun irq */
+#define ICH6_REG_RIRBSIZE 0x5e
+
+#define ICH6_REG_IC 0x60
+#define ICH6_REG_IR 0x64
+#define ICH6_REG_IRS 0x68
+#define ICH6_IRS_VALID (1<<1)
+#define ICH6_IRS_BUSY (1<<0)
+
+#define ICH6_REG_DPLBASE 0x70
+#define ICH6_REG_DPUBASE 0x74
+#define ICH6_DPLBASE_ENABLE 0x1 /* Enable position buffer */
+
+/* SD offset: SDI0=0x80, SDI1=0xa0, ... SDO3=0x160 */
+enum { SDI0, SDI1, SDI2, SDI3, SDO0, SDO1, SDO2, SDO3 };
+
+/* stream register offsets from stream base */
+#define ICH6_REG_SD_CTL 0x00
+#define ICH6_REG_SD_STS 0x03
+#define ICH6_REG_SD_LPIB 0x04
+#define ICH6_REG_SD_CBL 0x08
+#define ICH6_REG_SD_LVI 0x0c
+#define ICH6_REG_SD_FIFOW 0x0e
+#define ICH6_REG_SD_FIFOSIZE 0x10
+#define ICH6_REG_SD_FORMAT 0x12
+#define ICH6_REG_SD_BDLPL 0x18
+#define ICH6_REG_SD_BDLPU 0x1c
+
+/* PCI space */
+#define ICH6_PCIREG_TCSEL 0x44
+
+/*
+ * other constants
+ */
+
+/* max number of SDs */
+/* ICH, ATI and VIA have 4 playback and 4 capture */
+#define ICH6_NUM_CAPTURE 4
+#define ICH6_NUM_PLAYBACK 4
+
+/* ULI has 6 playback and 5 capture */
+#define ULI_NUM_CAPTURE 5
+#define ULI_NUM_PLAYBACK 6
+
+/* ATI HDMI may have up to 8 playbacks and 0 capture */
+#define ATIHDMI_NUM_CAPTURE 0
+#define ATIHDMI_NUM_PLAYBACK 8
+
+/* TERA has 4 playback and 3 capture */
+#define TERA_NUM_CAPTURE 3
+#define TERA_NUM_PLAYBACK 4
+
+/* this number is statically defined for simplicity */
+#define MAX_AZX_DEV 16
+
+/* max number of fragments - we may use more if allocating more pages for BDL */
+#define BDL_SIZE 4096
+#define AZX_MAX_BDL_ENTRIES (BDL_SIZE / 16)
+#define AZX_MAX_FRAG 32
+/* max buffer size - no h/w limit, you can increase as you like */
+#define AZX_MAX_BUF_SIZE (1024*1024*1024)
+
+/* RIRB int mask: overrun[2], response[0] */
+#define RIRB_INT_RESPONSE 0x01
+#define RIRB_INT_OVERRUN 0x04
+#define RIRB_INT_MASK 0x05
+
+/* STATESTS int mask: S3,SD2,SD1,SD0 */
+#define AZX_MAX_CODECS 8
+#define AZX_DEFAULT_CODECS 4
+#define STATESTS_INT_MASK ((1 << AZX_MAX_CODECS) - 1)
+
+/* SD_CTL bits */
+#define SD_CTL_STREAM_RESET 0x01 /* stream reset bit */
+#define SD_CTL_DMA_START 0x02 /* stream DMA start bit */
+#define SD_CTL_STRIPE (3 << 16) /* stripe control */
+#define SD_CTL_TRAFFIC_PRIO (1 << 18) /* traffic priority */
+#define SD_CTL_DIR (1 << 19) /* bi-directional stream */
+#define SD_CTL_STREAM_TAG_MASK (0xf << 20)
+#define SD_CTL_STREAM_TAG_SHIFT 20
+
+/* SD_CTL and SD_STS */
+#define SD_INT_DESC_ERR 0x10 /* descriptor error interrupt */
+#define SD_INT_FIFO_ERR 0x08 /* FIFO error interrupt */
+#define SD_INT_COMPLETE 0x04 /* completion interrupt */
+#define SD_INT_MASK (SD_INT_DESC_ERR|SD_INT_FIFO_ERR|\
+ SD_INT_COMPLETE)
+
+/* SD_STS */
+#define SD_STS_FIFO_READY 0x20 /* FIFO ready */
+
+/* INTCTL and INTSTS */
+#define ICH6_INT_ALL_STREAM 0xff /* all stream interrupts */
+#define ICH6_INT_CTRL_EN 0x40000000 /* controller interrupt enable bit */
+#define ICH6_INT_GLOBAL_EN 0x80000000 /* global interrupt enable bit */
+
+/* below are so far hardcoded - should read registers in future */
+#define ICH6_MAX_CORB_ENTRIES 256
+#define ICH6_MAX_RIRB_ENTRIES 256
+
+/* driver quirks (capabilities) */
+/* bits 0-7 are used for indicating driver type */
+#define AZX_DCAPS_NO_TCSEL (1 << 8) /* No Intel TCSEL bit */
+#define AZX_DCAPS_NO_MSI (1 << 9) /* No MSI support */
+#define AZX_DCAPS_ATI_SNOOP (1 << 10) /* ATI snoop enable */
+#define AZX_DCAPS_NVIDIA_SNOOP (1 << 11) /* Nvidia snoop enable */
+#define AZX_DCAPS_SCH_SNOOP (1 << 12) /* SCH/PCH snoop enable */
+#define AZX_DCAPS_RIRB_DELAY (1 << 13) /* Long delay in read loop */
+#define AZX_DCAPS_RIRB_PRE_DELAY (1 << 14) /* Put a delay before read */
+#define AZX_DCAPS_CTX_WORKAROUND (1 << 15) /* X-Fi workaround */
+#define AZX_DCAPS_POSFIX_LPIB (1 << 16) /* Use LPIB as default */
+#define AZX_DCAPS_POSFIX_VIA (1 << 17) /* Use VIACOMBO as default */
+#define AZX_DCAPS_NO_64BIT (1 << 18) /* No 64bit address */
+#define AZX_DCAPS_SYNC_WRITE (1 << 19) /* sync each cmd write */
+#define AZX_DCAPS_OLD_SSYNC (1 << 20) /* Old SSYNC reg for ICH */
+#define AZX_DCAPS_BUFSIZE (1 << 21) /* no buffer size alignment */
+#define AZX_DCAPS_ALIGN_BUFSIZE (1 << 22) /* buffer size alignment */
+#define AZX_DCAPS_4K_BDLE_BOUNDARY (1 << 23) /* BDLE in 4k boundary */
+#define AZX_DCAPS_REVERSE_ASSIGN (1 << 24) /* Assign devices in reverse order */
+#define AZX_DCAPS_COUNT_LPIB_DELAY (1 << 25) /* Take LPIB as delay */
+#define AZX_DCAPS_PM_RUNTIME (1 << 26) /* runtime PM support */
+#define AZX_DCAPS_I915_POWERWELL (1 << 27) /* HSW i915 powerwell support */
+#define AZX_DCAPS_CORBRP_SELF_CLEAR (1 << 28) /* CORBRP clears itself after reset */
+
+/* position fix mode */
+enum {
+ POS_FIX_AUTO,
+ POS_FIX_LPIB,
+ POS_FIX_POSBUF,
+ POS_FIX_VIACOMBO,
+ POS_FIX_COMBO,
+};
+
+/* Defines for ATI HD Audio support in SB450 south bridge */
+#define ATI_SB450_HDAUDIO_MISC_CNTR2_ADDR 0x42
+#define ATI_SB450_HDAUDIO_ENABLE_SNOOP 0x02
+
+/* Defines for Nvidia HDA support */
+#define NVIDIA_HDA_TRANSREG_ADDR 0x4e
+#define NVIDIA_HDA_ENABLE_COHBITS 0x0f
+#define NVIDIA_HDA_ISTRM_COH 0x4d
+#define NVIDIA_HDA_OSTRM_COH 0x4c
+#define NVIDIA_HDA_ENABLE_COHBIT 0x01
+
+/* Defines for Intel SCH HDA snoop control */
+#define INTEL_SCH_HDA_DEVC 0x78
+#define INTEL_SCH_HDA_DEVC_NOSNOOP (0x1<<11)
+
+/* Define IN stream 0 FIFO size offset in VIA controller */
+#define VIA_IN_STREAM0_FIFO_SIZE_OFFSET 0x90
+/* Define VIA HD Audio Device ID*/
+#define VIA_HDAC_DEVICE_ID 0x3288
+
+/* HD Audio class code */
+#define PCI_CLASS_MULTIMEDIA_HD_AUDIO 0x0403
+
+struct azx_dev {
+ struct snd_dma_buffer bdl; /* BDL buffer */
+ u32 *posbuf; /* position buffer pointer */
+
+ unsigned int bufsize; /* size of the play buffer in bytes */
+ unsigned int period_bytes; /* size of the period in bytes */
+ unsigned int frags; /* number for period in the play buffer */
+ unsigned int fifo_size; /* FIFO size */
+ unsigned long start_wallclk; /* start + minimum wallclk */
+ unsigned long period_wallclk; /* wallclk for period */
+
+ void __iomem *sd_addr; /* stream descriptor pointer */
+
+ u32 sd_int_sta_mask; /* stream int status mask */
+
+ /* pcm support */
+ struct snd_pcm_substream *substream; /* assigned substream,
+ * set in PCM open
+ */
+ unsigned int format_val; /* format value to be set in the
+ * controller and the codec
+ */
+ unsigned char stream_tag; /* assigned stream */
+ unsigned char index; /* stream index */
+ int assigned_key; /* last device# key assigned to */
+
+ unsigned int opened:1;
+ unsigned int running:1;
+ unsigned int irq_pending:1;
+ unsigned int prepared:1;
+ unsigned int locked:1;
+ /*
+ * For VIA:
+ * A flag to ensure DMA position is 0
+ * when link position is not greater than FIFO size
+ */
+ unsigned int insufficient:1;
+ unsigned int wc_marked:1;
+ unsigned int no_period_wakeup:1;
+
+ struct timecounter azx_tc;
+ struct cyclecounter azx_cc;
+
+ int delay_negative_threshold;
+
+#ifdef CONFIG_SND_HDA_DSP_LOADER
+ /* Allows dsp load to have sole access to the playback stream. */
+ struct mutex dsp_mutex;
+#endif
+};
+
+/* CORB/RIRB */
+struct azx_rb {
+ u32 *buf; /* CORB/RIRB buffer
+ * Each CORB entry is 4byte, RIRB is 8byte
+ */
+ dma_addr_t addr; /* physical address of CORB/RIRB buffer */
+ /* for RIRB */
+ unsigned short rp, wp; /* read/write pointers */
+ int cmds[AZX_MAX_CODECS]; /* number of pending requests */
+ u32 res[AZX_MAX_CODECS]; /* last read value */
+};
+
+struct azx;
+
+/* Functions to read/write to hda registers. */
+struct hda_controller_ops {
+ /* Register Access */
+ void (*reg_writel)(u32 value, u32 __iomem *addr);
+ u32 (*reg_readl)(u32 __iomem *addr);
+ void (*reg_writew)(u16 value, u16 __iomem *addr);
+ u16 (*reg_readw)(u16 __iomem *addr);
+ void (*reg_writeb)(u8 value, u8 __iomem *addr);
+ u8 (*reg_readb)(u8 __iomem *addr);
+ /* Disable msi if supported, PCI only */
+ int (*disable_msi_reset_irq)(struct azx *);
+ /* Allocation ops */
+ int (*dma_alloc_pages)(struct azx *chip,
+ int type,
+ size_t size,
+ struct snd_dma_buffer *buf);
+ void (*dma_free_pages)(struct azx *chip, struct snd_dma_buffer *buf);
+ int (*substream_alloc_pages)(struct azx *chip,
+ struct snd_pcm_substream *substream,
+ size_t size);
+ int (*substream_free_pages)(struct azx *chip,
+ struct snd_pcm_substream *substream);
+ void (*pcm_mmap_prepare)(struct snd_pcm_substream *substream,
+ struct vm_area_struct *area);
+ /* Check if current position is acceptable */
+ int (*position_check)(struct azx *chip, struct azx_dev *azx_dev);
+};
+
+struct azx_pcm {
+ struct azx *chip;
+ struct snd_pcm *pcm;
+ struct hda_codec *codec;
+ struct hda_pcm_stream *hinfo[2];
+ struct list_head list;
+};
+
+struct azx {
+ struct snd_card *card;
+ struct pci_dev *pci;
+ int dev_index;
+
+ /* chip type specific */
+ int driver_type;
+ unsigned int driver_caps;
+ int playback_streams;
+ int playback_index_offset;
+ int capture_streams;
+ int capture_index_offset;
+ int num_streams;
+ const int *jackpoll_ms; /* per-card jack poll interval */
+
+ /* Register interaction. */
+ const struct hda_controller_ops *ops;
+
+ /* pci resources */
+ unsigned long addr;
+ void __iomem *remap_addr;
+ int irq;
+
+ /* locks */
+ spinlock_t reg_lock;
+ struct mutex open_mutex; /* Prevents concurrent open/close operations */
+ struct completion probe_wait;
+
+ /* streams (x num_streams) */
+ struct azx_dev *azx_dev;
+
+ /* PCM */
+ struct list_head pcm_list; /* azx_pcm list */
+
+ /* HD codec */
+ unsigned short codec_mask;
+ int codec_probe_mask; /* copied from probe_mask option */
+ struct hda_bus *bus;
+ unsigned int beep_mode;
+
+ /* CORB/RIRB */
+ struct azx_rb corb;
+ struct azx_rb rirb;
+
+ /* CORB/RIRB and position buffers */
+ struct snd_dma_buffer rb;
+ struct snd_dma_buffer posbuf;
+
+#ifdef CONFIG_SND_HDA_PATCH_LOADER
+ const struct firmware *fw;
+#endif
+
+ /* flags */
+ int position_fix[2]; /* for both playback/capture streams */
+ const int *bdl_pos_adj;
+ int poll_count;
+ unsigned int running:1;
+ unsigned int initialized:1;
+ unsigned int single_cmd:1;
+ unsigned int polling_mode:1;
+ unsigned int msi:1;
+ unsigned int irq_pending_warned:1;
+ unsigned int probing:1; /* codec probing phase */
+ unsigned int snoop:1;
+ unsigned int align_buffer_size:1;
+ unsigned int region_requested:1;
+
+ /* VGA-switcheroo setup */
+ unsigned int use_vga_switcheroo:1;
+ unsigned int vga_switcheroo_registered:1;
+ unsigned int init_failed:1; /* delayed init failed */
+ unsigned int disabled:1; /* disabled by VGA-switcher */
+
+ /* for debugging */
+ unsigned int last_cmd[AZX_MAX_CODECS];
+
+ /* for pending irqs */
+ struct work_struct irq_pending_work;
+
+ struct work_struct probe_work;
+
+ /* reboot notifier (for mysterious hangup problem at power-down) */
+ struct notifier_block reboot_notifier;
+
+ /* card list (for power_save trigger) */
+ struct list_head list;
+
+#ifdef CONFIG_SND_HDA_DSP_LOADER
+ struct azx_dev saved_azx_dev;
+#endif
+
+ /* secondary power domain for hdmi audio under vga device */
+ struct dev_pm_domain hdmi_pm_domain;
+};
+
+#ifdef CONFIG_SND_VERBOSE_PRINTK
+#define SFX /* nop */
+#else
+#define SFX "hda-intel "
+#endif
+
+#ifdef CONFIG_X86
+#define azx_snoop(chip) ((chip)->snoop)
+#else
+#define azx_snoop(chip) true
+#endif
+
+/*
+ * macros for easy use
+ */
+
+#define azx_writel(chip, reg, value) \
+ ((chip)->ops->reg_writel(value, (chip)->remap_addr + ICH6_REG_##reg))
+#define azx_readl(chip, reg) \
+ ((chip)->ops->reg_readl((chip)->remap_addr + ICH6_REG_##reg))
+#define azx_writew(chip, reg, value) \
+ ((chip)->ops->reg_writew(value, (chip)->remap_addr + ICH6_REG_##reg))
+#define azx_readw(chip, reg) \
+ ((chip)->ops->reg_readw((chip)->remap_addr + ICH6_REG_##reg))
+#define azx_writeb(chip, reg, value) \
+ ((chip)->ops->reg_writeb(value, (chip)->remap_addr + ICH6_REG_##reg))
+#define azx_readb(chip, reg) \
+ ((chip)->ops->reg_readb((chip)->remap_addr + ICH6_REG_##reg))
+
+#define azx_sd_writel(chip, dev, reg, value) \
+ ((chip)->ops->reg_writel(value, (dev)->sd_addr + ICH6_REG_##reg))
+#define azx_sd_readl(chip, dev, reg) \
+ ((chip)->ops->reg_readl((dev)->sd_addr + ICH6_REG_##reg))
+#define azx_sd_writew(chip, dev, reg, value) \
+ ((chip)->ops->reg_writew(value, (dev)->sd_addr + ICH6_REG_##reg))
+#define azx_sd_readw(chip, dev, reg) \
+ ((chip)->ops->reg_readw((dev)->sd_addr + ICH6_REG_##reg))
+#define azx_sd_writeb(chip, dev, reg, value) \
+ ((chip)->ops->reg_writeb(value, (dev)->sd_addr + ICH6_REG_##reg))
+#define azx_sd_readb(chip, dev, reg) \
+ ((chip)->ops->reg_readb((dev)->sd_addr + ICH6_REG_##reg))
+
+#endif /* __SOUND_HDA_PRIV_H */
diff --git a/sound/pci/hda/hda_proc.c b/sound/pci/hda/hda_proc.c
index bfe74c2fb07..ce5a6da8341 100644
--- a/sound/pci/hda/hda_proc.c
+++ b/sound/pci/hda/hda_proc.c
@@ -22,10 +22,16 @@
*/
#include <linux/init.h>
+#include <linux/slab.h>
#include <sound/core.h>
+#include <linux/module.h>
#include "hda_codec.h"
#include "hda_local.h"
+static int dump_coef = -1;
+module_param(dump_coef, int, 0644);
+MODULE_PARM_DESC(dump_coef, "Dump processing coefficients in codec proc file (-1=auto, 0=disable, 1=enable)");
+
static char *bits_names(unsigned int bits, char *names[], int size)
{
int i, n;
@@ -54,6 +60,8 @@ static const char *get_wid_type_name(unsigned int wid_value)
[AC_WID_BEEP] = "Beep Generator Widget",
[AC_WID_VENDOR] = "Vendor Defined Widget",
};
+ if (wid_value == -1)
+ return "UNKNOWN Widget";
wid_value &= 0xf;
if (names[wid_value])
return names[wid_value];
@@ -136,28 +144,35 @@ static void print_amp_vals(struct snd_info_buffer *buffer,
dir = dir == HDA_OUTPUT ? AC_AMP_GET_OUTPUT : AC_AMP_GET_INPUT;
for (i = 0; i < indices; i++) {
snd_iprintf(buffer, " [");
+ val = snd_hda_codec_read(codec, nid, 0,
+ AC_VERB_GET_AMP_GAIN_MUTE,
+ AC_AMP_GET_LEFT | dir | i);
+ snd_iprintf(buffer, "0x%02x", val);
if (stereo) {
val = snd_hda_codec_read(codec, nid, 0,
AC_VERB_GET_AMP_GAIN_MUTE,
- AC_AMP_GET_LEFT | dir | i);
- snd_iprintf(buffer, "0x%02x ", val);
+ AC_AMP_GET_RIGHT | dir | i);
+ snd_iprintf(buffer, " 0x%02x", val);
}
- val = snd_hda_codec_read(codec, nid, 0,
- AC_VERB_GET_AMP_GAIN_MUTE,
- AC_AMP_GET_RIGHT | dir | i);
- snd_iprintf(buffer, "0x%02x]", val);
+ snd_iprintf(buffer, "]");
}
snd_iprintf(buffer, "\n");
}
static void print_pcm_rates(struct snd_info_buffer *buffer, unsigned int pcm)
{
- char buf[SND_PRINT_RATES_ADVISED_BUFSIZE];
+ static unsigned int rates[] = {
+ 8000, 11025, 16000, 22050, 32000, 44100, 48000, 88200,
+ 96000, 176400, 192000, 384000
+ };
+ int i;
pcm &= AC_SUPPCM_RATES;
snd_iprintf(buffer, " rates [0x%x]:", pcm);
- snd_print_pcm_rates(pcm, buf, sizeof(buf));
- snd_iprintf(buffer, "%s\n", buf);
+ for (i = 0; i < ARRAY_SIZE(rates); i++)
+ if (pcm & (1 << i))
+ snd_iprintf(buffer, " %d", rates[i]);
+ snd_iprintf(buffer, "\n");
}
static void print_pcm_bits(struct snd_info_buffer *buffer, unsigned int pcm)
@@ -394,6 +409,9 @@ static void print_digital_conv(struct snd_info_buffer *buffer,
{
unsigned int digi1 = snd_hda_codec_read(codec, nid, 0,
AC_VERB_GET_DIGI_CONVERT_1, 0);
+ unsigned char digi2 = digi1 >> 8;
+ unsigned char digi3 = digi1 >> 16;
+
snd_iprintf(buffer, " Digital:");
if (digi1 & AC_DIG1_ENABLE)
snd_iprintf(buffer, " Enabled");
@@ -404,24 +422,28 @@ static void print_digital_conv(struct snd_info_buffer *buffer,
if (digi1 & AC_DIG1_EMPHASIS)
snd_iprintf(buffer, " Preemphasis");
if (digi1 & AC_DIG1_COPYRIGHT)
- snd_iprintf(buffer, " Copyright");
+ snd_iprintf(buffer, " Non-Copyright");
if (digi1 & AC_DIG1_NONAUDIO)
snd_iprintf(buffer, " Non-Audio");
if (digi1 & AC_DIG1_PROFESSIONAL)
snd_iprintf(buffer, " Pro");
if (digi1 & AC_DIG1_LEVEL)
snd_iprintf(buffer, " GenLevel");
+ if (digi3 & AC_DIG3_KAE)
+ snd_iprintf(buffer, " KAE");
snd_iprintf(buffer, "\n");
snd_iprintf(buffer, " Digital category: 0x%x\n",
- (digi1 >> 8) & AC_DIG2_CC);
+ digi2 & AC_DIG2_CC);
+ snd_iprintf(buffer, " IEC Coding Type: 0x%x\n",
+ digi3 & AC_DIG3_ICT);
}
static const char *get_pwr_state(u32 state)
{
- static const char * const buf[4] = {
- "D0", "D1", "D2", "D3"
+ static const char * const buf[] = {
+ "D0", "D1", "D2", "D3", "D3cold"
};
- if (state < 4)
+ if (state < ARRAY_SIZE(buf))
return buf[state];
return "UNKNOWN";
}
@@ -443,14 +465,21 @@ static void print_power_state(struct snd_info_buffer *buffer,
int sup = snd_hda_param_read(codec, nid, AC_PAR_POWER_STATE);
int pwr = snd_hda_codec_read(codec, nid, 0,
AC_VERB_GET_POWER_STATE, 0);
- if (sup)
+ if (sup != -1)
snd_iprintf(buffer, " Power states: %s\n",
bits_names(sup, names, ARRAY_SIZE(names)));
- snd_iprintf(buffer, " Power: setting=%s, actual=%s\n",
+ snd_iprintf(buffer, " Power: setting=%s, actual=%s",
get_pwr_state(pwr & AC_PWRST_SETTING),
get_pwr_state((pwr & AC_PWRST_ACTUAL) >>
AC_PWRST_ACTUAL_SHIFT));
+ if (pwr & AC_PWRST_ERROR)
+ snd_iprintf(buffer, ", Error");
+ if (pwr & AC_PWRST_CLK_STOP_OK)
+ snd_iprintf(buffer, ", Clock-stop-OK");
+ if (pwr & AC_PWRST_SETTING_RESET)
+ snd_iprintf(buffer, ", Setting-reset");
+ snd_iprintf(buffer, "\n");
}
static void print_unsol_cap(struct snd_info_buffer *buffer,
@@ -464,14 +493,39 @@ static void print_unsol_cap(struct snd_info_buffer *buffer,
(unsol & AC_UNSOL_ENABLED) ? 1 : 0);
}
+static inline bool can_dump_coef(struct hda_codec *codec)
+{
+ switch (dump_coef) {
+ case 0: return false;
+ case 1: return true;
+ default: return codec->dump_coef;
+ }
+}
+
static void print_proc_caps(struct snd_info_buffer *buffer,
struct hda_codec *codec, hda_nid_t nid)
{
+ unsigned int i, ncoeff, oldindex;
unsigned int proc_caps = snd_hda_param_read(codec, nid,
AC_PAR_PROC_CAP);
+ ncoeff = (proc_caps & AC_PCAP_NUM_COEF) >> AC_PCAP_NUM_COEF_SHIFT;
snd_iprintf(buffer, " Processing caps: benign=%d, ncoeff=%d\n",
- proc_caps & AC_PCAP_BENIGN,
- (proc_caps & AC_PCAP_NUM_COEF) >> AC_PCAP_NUM_COEF_SHIFT);
+ proc_caps & AC_PCAP_BENIGN, ncoeff);
+
+ if (!can_dump_coef(codec))
+ return;
+
+ /* Note: This is racy - another process could run in parallel and change
+ the coef index too. */
+ oldindex = snd_hda_codec_read(codec, nid, 0, AC_VERB_GET_COEF_INDEX, 0);
+ for (i = 0; i < ncoeff; i++) {
+ unsigned int val;
+ snd_hda_codec_write(codec, nid, 0, AC_VERB_SET_COEF_INDEX, i);
+ val = snd_hda_codec_read(codec, nid, 0, AC_VERB_GET_PROC_COEF,
+ 0);
+ snd_iprintf(buffer, " Coeff 0x%02x: 0x%04x\n", i, val);
+ }
+ snd_hda_codec_write(codec, nid, 0, AC_VERB_SET_COEF_INDEX, oldindex);
}
static void print_conn_list(struct snd_info_buffer *buffer,
@@ -480,6 +534,8 @@ static void print_conn_list(struct snd_info_buffer *buffer,
int conn_len)
{
int c, curr = -1;
+ const hda_nid_t *list;
+ int cache_len;
if (conn_len > 1 &&
wid_type != AC_WID_AUD_MIX &&
@@ -497,6 +553,19 @@ static void print_conn_list(struct snd_info_buffer *buffer,
}
snd_iprintf(buffer, "\n");
}
+
+ /* Get Cache connections info */
+ cache_len = snd_hda_get_conn_list(codec, nid, &list);
+ if (cache_len != conn_len
+ || memcmp(list, conn, conn_len)) {
+ snd_iprintf(buffer, " In-driver Connection: %d\n", cache_len);
+ if (cache_len > 0) {
+ snd_iprintf(buffer, " ");
+ for (c = 0; c < cache_len; c++)
+ snd_iprintf(buffer, " 0x%02x", list[c]);
+ snd_iprintf(buffer, "\n");
+ }
+ }
}
static void print_gpio(struct snd_info_buffer *buffer,
@@ -543,6 +612,36 @@ static void print_gpio(struct snd_info_buffer *buffer,
print_nid_array(buffer, codec, nid, &codec->nids);
}
+static void print_device_list(struct snd_info_buffer *buffer,
+ struct hda_codec *codec, hda_nid_t nid)
+{
+ int i, curr = -1;
+ u8 dev_list[AC_MAX_DEV_LIST_LEN];
+ int devlist_len;
+
+ devlist_len = snd_hda_get_devices(codec, nid, dev_list,
+ AC_MAX_DEV_LIST_LEN);
+ snd_iprintf(buffer, " Devices: %d\n", devlist_len);
+ if (devlist_len <= 0)
+ return;
+
+ curr = snd_hda_codec_read(codec, nid, 0,
+ AC_VERB_GET_DEVICE_SEL, 0);
+
+ for (i = 0; i < devlist_len; i++) {
+ if (i == curr)
+ snd_iprintf(buffer, " *");
+ else
+ snd_iprintf(buffer, " ");
+
+ snd_iprintf(buffer,
+ "Dev %02d: PD = %d, ELDV = %d, IA = %d\n", i,
+ !!(dev_list[i] & AC_DE_PD),
+ !!(dev_list[i] & AC_DE_ELDV),
+ !!(dev_list[i] & AC_DE_IA));
+ }
+}
+
static void print_codec_info(struct snd_info_entry *entry,
struct snd_info_buffer *buffer)
{
@@ -581,6 +680,8 @@ static void print_codec_info(struct snd_info_entry *entry,
print_amp_caps(buffer, codec, codec->afg, HDA_INPUT);
snd_iprintf(buffer, "Default Amp-Out caps: ");
print_amp_caps(buffer, codec, codec->afg, HDA_OUTPUT);
+ snd_iprintf(buffer, "State of AFG node 0x%02x:\n", codec->afg);
+ print_power_state(buffer, codec, codec->afg);
nodes = snd_hda_get_sub_nodes(codec, codec->afg, &nid);
if (! nid || nodes < 0) {
@@ -598,7 +699,7 @@ static void print_codec_info(struct snd_info_entry *entry,
snd_hda_param_read(codec, nid,
AC_PAR_AUDIO_WIDGET_CAP);
unsigned int wid_type = get_wcaps_type(wid_caps);
- hda_nid_t conn[HDA_MAX_CONNECTIONS];
+ hda_nid_t *conn = NULL;
int conn_len = 0;
snd_iprintf(buffer, "Node 0x%02x [%s] wcaps 0x%x:", nid,
@@ -635,17 +736,33 @@ static void print_codec_info(struct snd_info_entry *entry,
if (wid_type == AC_WID_VOL_KNB)
wid_caps |= AC_WCAP_CONN_LIST;
- if (wid_caps & AC_WCAP_CONN_LIST)
- conn_len = snd_hda_get_connections(codec, nid, conn,
- HDA_MAX_CONNECTIONS);
+ if (wid_caps & AC_WCAP_CONN_LIST) {
+ conn_len = snd_hda_get_num_raw_conns(codec, nid);
+ if (conn_len > 0) {
+ conn = kmalloc(sizeof(hda_nid_t) * conn_len,
+ GFP_KERNEL);
+ if (!conn)
+ return;
+ if (snd_hda_get_raw_connections(codec, nid, conn,
+ conn_len) < 0)
+ conn_len = 0;
+ }
+ }
if (wid_caps & AC_WCAP_IN_AMP) {
snd_iprintf(buffer, " Amp-In caps: ");
print_amp_caps(buffer, codec, nid, HDA_INPUT);
snd_iprintf(buffer, " Amp-In vals: ");
- print_amp_vals(buffer, codec, nid, HDA_INPUT,
- wid_caps & AC_WCAP_STEREO,
- wid_type == AC_WID_PIN ? 1 : conn_len);
+ if (wid_type == AC_WID_PIN ||
+ (codec->single_adc_amp &&
+ wid_type == AC_WID_AUD_IN))
+ print_amp_vals(buffer, codec, nid, HDA_INPUT,
+ wid_caps & AC_WCAP_STEREO,
+ 1);
+ else
+ print_amp_vals(buffer, codec, nid, HDA_INPUT,
+ wid_caps & AC_WCAP_STEREO,
+ conn_len);
}
if (wid_caps & AC_WCAP_OUT_AMP) {
snd_iprintf(buffer, " Amp-Out caps: ");
@@ -694,6 +811,9 @@ static void print_codec_info(struct snd_info_entry *entry,
(wid_caps & AC_WCAP_DELAY) >>
AC_WCAP_DELAY_SHIFT);
+ if (wid_type == AC_WID_PIN && codec->dp_mst)
+ print_device_list(buffer, codec, nid);
+
if (wid_caps & AC_WCAP_CONN_LIST)
print_conn_list(buffer, codec, nid, wid_type,
conn, conn_len);
@@ -703,6 +823,8 @@ static void print_codec_info(struct snd_info_entry *entry,
if (codec->proc_widget_hook)
codec->proc_widget_hook(buffer, codec, nid);
+
+ kfree(conn);
}
snd_hda_power_down(codec);
}
diff --git a/sound/pci/hda/hda_sysfs.c b/sound/pci/hda/hda_sysfs.c
new file mode 100644
index 00000000000..e2079090ca6
--- /dev/null
+++ b/sound/pci/hda/hda_sysfs.c
@@ -0,0 +1,780 @@
+/*
+ * sysfs interface for HD-audio codec
+ *
+ * Copyright (c) 2014 Takashi Iwai <tiwai@suse.de>
+ *
+ * split from hda_hwdep.c
+ */
+
+#include <linux/init.h>
+#include <linux/slab.h>
+#include <linux/compat.h>
+#include <linux/mutex.h>
+#include <linux/ctype.h>
+#include <linux/string.h>
+#include <linux/export.h>
+#include <sound/core.h>
+#include "hda_codec.h"
+#include "hda_local.h"
+#include <sound/hda_hwdep.h>
+#include <sound/minors.h>
+
+/* hint string pair */
+struct hda_hint {
+ const char *key;
+ const char *val; /* contained in the same alloc as key */
+};
+
+#ifdef CONFIG_PM
+static ssize_t power_on_acct_show(struct device *dev,
+ struct device_attribute *attr,
+ char *buf)
+{
+ struct hda_codec *codec = dev_get_drvdata(dev);
+ snd_hda_update_power_acct(codec);
+ return sprintf(buf, "%u\n", jiffies_to_msecs(codec->power_on_acct));
+}
+
+static ssize_t power_off_acct_show(struct device *dev,
+ struct device_attribute *attr,
+ char *buf)
+{
+ struct hda_codec *codec = dev_get_drvdata(dev);
+ snd_hda_update_power_acct(codec);
+ return sprintf(buf, "%u\n", jiffies_to_msecs(codec->power_off_acct));
+}
+
+static DEVICE_ATTR_RO(power_on_acct);
+static DEVICE_ATTR_RO(power_off_acct);
+#endif /* CONFIG_PM */
+
+#define CODEC_INFO_SHOW(type) \
+static ssize_t type##_show(struct device *dev, \
+ struct device_attribute *attr, \
+ char *buf) \
+{ \
+ struct hda_codec *codec = dev_get_drvdata(dev); \
+ return sprintf(buf, "0x%x\n", codec->type); \
+}
+
+#define CODEC_INFO_STR_SHOW(type) \
+static ssize_t type##_show(struct device *dev, \
+ struct device_attribute *attr, \
+ char *buf) \
+{ \
+ struct hda_codec *codec = dev_get_drvdata(dev); \
+ return sprintf(buf, "%s\n", \
+ codec->type ? codec->type : ""); \
+}
+
+CODEC_INFO_SHOW(vendor_id);
+CODEC_INFO_SHOW(subsystem_id);
+CODEC_INFO_SHOW(revision_id);
+CODEC_INFO_SHOW(afg);
+CODEC_INFO_SHOW(mfg);
+CODEC_INFO_STR_SHOW(vendor_name);
+CODEC_INFO_STR_SHOW(chip_name);
+CODEC_INFO_STR_SHOW(modelname);
+
+static ssize_t pin_configs_show(struct hda_codec *codec,
+ struct snd_array *list,
+ char *buf)
+{
+ int i, len = 0;
+ mutex_lock(&codec->user_mutex);
+ for (i = 0; i < list->used; i++) {
+ struct hda_pincfg *pin = snd_array_elem(list, i);
+ len += sprintf(buf + len, "0x%02x 0x%08x\n",
+ pin->nid, pin->cfg);
+ }
+ mutex_unlock(&codec->user_mutex);
+ return len;
+}
+
+static ssize_t init_pin_configs_show(struct device *dev,
+ struct device_attribute *attr,
+ char *buf)
+{
+ struct hda_codec *codec = dev_get_drvdata(dev);
+ return pin_configs_show(codec, &codec->init_pins, buf);
+}
+
+static ssize_t driver_pin_configs_show(struct device *dev,
+ struct device_attribute *attr,
+ char *buf)
+{
+ struct hda_codec *codec = dev_get_drvdata(dev);
+ return pin_configs_show(codec, &codec->driver_pins, buf);
+}
+
+#ifdef CONFIG_SND_HDA_RECONFIG
+
+/*
+ * sysfs interface
+ */
+
+static int clear_codec(struct hda_codec *codec)
+{
+ int err;
+
+ err = snd_hda_codec_reset(codec);
+ if (err < 0) {
+ codec_err(codec, "The codec is being used, can't free.\n");
+ return err;
+ }
+ snd_hda_sysfs_clear(codec);
+ return 0;
+}
+
+static int reconfig_codec(struct hda_codec *codec)
+{
+ int err;
+
+ snd_hda_power_up(codec);
+ codec_info(codec, "hda-codec: reconfiguring\n");
+ err = snd_hda_codec_reset(codec);
+ if (err < 0) {
+ codec_err(codec,
+ "The codec is being used, can't reconfigure.\n");
+ goto error;
+ }
+ err = snd_hda_codec_configure(codec);
+ if (err < 0)
+ goto error;
+ /* rebuild PCMs */
+ err = snd_hda_codec_build_pcms(codec);
+ if (err < 0)
+ goto error;
+ /* rebuild mixers */
+ err = snd_hda_codec_build_controls(codec);
+ if (err < 0)
+ goto error;
+ err = snd_card_register(codec->bus->card);
+ error:
+ snd_hda_power_down(codec);
+ return err;
+}
+
+/*
+ * allocate a string at most len chars, and remove the trailing EOL
+ */
+static char *kstrndup_noeol(const char *src, size_t len)
+{
+ char *s = kstrndup(src, len, GFP_KERNEL);
+ char *p;
+ if (!s)
+ return NULL;
+ p = strchr(s, '\n');
+ if (p)
+ *p = 0;
+ return s;
+}
+
+#define CODEC_INFO_STORE(type) \
+static ssize_t type##_store(struct device *dev, \
+ struct device_attribute *attr, \
+ const char *buf, size_t count) \
+{ \
+ struct hda_codec *codec = dev_get_drvdata(dev); \
+ unsigned long val; \
+ int err = kstrtoul(buf, 0, &val); \
+ if (err < 0) \
+ return err; \
+ codec->type = val; \
+ return count; \
+}
+
+#define CODEC_INFO_STR_STORE(type) \
+static ssize_t type##_store(struct device *dev, \
+ struct device_attribute *attr, \
+ const char *buf, size_t count) \
+{ \
+ struct hda_codec *codec = dev_get_drvdata(dev); \
+ char *s = kstrndup_noeol(buf, 64); \
+ if (!s) \
+ return -ENOMEM; \
+ kfree(codec->type); \
+ codec->type = s; \
+ return count; \
+}
+
+CODEC_INFO_STORE(vendor_id);
+CODEC_INFO_STORE(subsystem_id);
+CODEC_INFO_STORE(revision_id);
+CODEC_INFO_STR_STORE(vendor_name);
+CODEC_INFO_STR_STORE(chip_name);
+CODEC_INFO_STR_STORE(modelname);
+
+#define CODEC_ACTION_STORE(type) \
+static ssize_t type##_store(struct device *dev, \
+ struct device_attribute *attr, \
+ const char *buf, size_t count) \
+{ \
+ struct hda_codec *codec = dev_get_drvdata(dev); \
+ int err = 0; \
+ if (*buf) \
+ err = type##_codec(codec); \
+ return err < 0 ? err : count; \
+}
+
+CODEC_ACTION_STORE(reconfig);
+CODEC_ACTION_STORE(clear);
+
+static ssize_t init_verbs_show(struct device *dev,
+ struct device_attribute *attr,
+ char *buf)
+{
+ struct hda_codec *codec = dev_get_drvdata(dev);
+ int i, len = 0;
+ mutex_lock(&codec->user_mutex);
+ for (i = 0; i < codec->init_verbs.used; i++) {
+ struct hda_verb *v = snd_array_elem(&codec->init_verbs, i);
+ len += snprintf(buf + len, PAGE_SIZE - len,
+ "0x%02x 0x%03x 0x%04x\n",
+ v->nid, v->verb, v->param);
+ }
+ mutex_unlock(&codec->user_mutex);
+ return len;
+}
+
+static int parse_init_verbs(struct hda_codec *codec, const char *buf)
+{
+ struct hda_verb *v;
+ int nid, verb, param;
+
+ if (sscanf(buf, "%i %i %i", &nid, &verb, &param) != 3)
+ return -EINVAL;
+ if (!nid || !verb)
+ return -EINVAL;
+ mutex_lock(&codec->user_mutex);
+ v = snd_array_new(&codec->init_verbs);
+ if (!v) {
+ mutex_unlock(&codec->user_mutex);
+ return -ENOMEM;
+ }
+ v->nid = nid;
+ v->verb = verb;
+ v->param = param;
+ mutex_unlock(&codec->user_mutex);
+ return 0;
+}
+
+static ssize_t init_verbs_store(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ struct hda_codec *codec = dev_get_drvdata(dev);
+ int err = parse_init_verbs(codec, buf);
+ if (err < 0)
+ return err;
+ return count;
+}
+
+static ssize_t hints_show(struct device *dev,
+ struct device_attribute *attr,
+ char *buf)
+{
+ struct hda_codec *codec = dev_get_drvdata(dev);
+ int i, len = 0;
+ mutex_lock(&codec->user_mutex);
+ for (i = 0; i < codec->hints.used; i++) {
+ struct hda_hint *hint = snd_array_elem(&codec->hints, i);
+ len += snprintf(buf + len, PAGE_SIZE - len,
+ "%s = %s\n", hint->key, hint->val);
+ }
+ mutex_unlock(&codec->user_mutex);
+ return len;
+}
+
+static struct hda_hint *get_hint(struct hda_codec *codec, const char *key)
+{
+ int i;
+
+ for (i = 0; i < codec->hints.used; i++) {
+ struct hda_hint *hint = snd_array_elem(&codec->hints, i);
+ if (!strcmp(hint->key, key))
+ return hint;
+ }
+ return NULL;
+}
+
+static void remove_trail_spaces(char *str)
+{
+ char *p;
+ if (!*str)
+ return;
+ p = str + strlen(str) - 1;
+ for (; isspace(*p); p--) {
+ *p = 0;
+ if (p == str)
+ return;
+ }
+}
+
+#define MAX_HINTS 1024
+
+static int parse_hints(struct hda_codec *codec, const char *buf)
+{
+ char *key, *val;
+ struct hda_hint *hint;
+ int err = 0;
+
+ buf = skip_spaces(buf);
+ if (!*buf || *buf == '#' || *buf == '\n')
+ return 0;
+ if (*buf == '=')
+ return -EINVAL;
+ key = kstrndup_noeol(buf, 1024);
+ if (!key)
+ return -ENOMEM;
+ /* extract key and val */
+ val = strchr(key, '=');
+ if (!val) {
+ kfree(key);
+ return -EINVAL;
+ }
+ *val++ = 0;
+ val = skip_spaces(val);
+ remove_trail_spaces(key);
+ remove_trail_spaces(val);
+ mutex_lock(&codec->user_mutex);
+ hint = get_hint(codec, key);
+ if (hint) {
+ /* replace */
+ kfree(hint->key);
+ hint->key = key;
+ hint->val = val;
+ goto unlock;
+ }
+ /* allocate a new hint entry */
+ if (codec->hints.used >= MAX_HINTS)
+ hint = NULL;
+ else
+ hint = snd_array_new(&codec->hints);
+ if (hint) {
+ hint->key = key;
+ hint->val = val;
+ } else {
+ err = -ENOMEM;
+ }
+ unlock:
+ mutex_unlock(&codec->user_mutex);
+ if (err)
+ kfree(key);
+ return err;
+}
+
+static ssize_t hints_store(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ struct hda_codec *codec = dev_get_drvdata(dev);
+ int err = parse_hints(codec, buf);
+ if (err < 0)
+ return err;
+ return count;
+}
+
+static ssize_t user_pin_configs_show(struct device *dev,
+ struct device_attribute *attr,
+ char *buf)
+{
+ struct hda_codec *codec = dev_get_drvdata(dev);
+ return pin_configs_show(codec, &codec->user_pins, buf);
+}
+
+#define MAX_PIN_CONFIGS 32
+
+static int parse_user_pin_configs(struct hda_codec *codec, const char *buf)
+{
+ int nid, cfg, err;
+
+ if (sscanf(buf, "%i %i", &nid, &cfg) != 2)
+ return -EINVAL;
+ if (!nid)
+ return -EINVAL;
+ mutex_lock(&codec->user_mutex);
+ err = snd_hda_add_pincfg(codec, &codec->user_pins, nid, cfg);
+ mutex_unlock(&codec->user_mutex);
+ return err;
+}
+
+static ssize_t user_pin_configs_store(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ struct hda_codec *codec = dev_get_drvdata(dev);
+ int err = parse_user_pin_configs(codec, buf);
+ if (err < 0)
+ return err;
+ return count;
+}
+
+/* sysfs attributes exposed only when CONFIG_SND_HDA_RECONFIG=y */
+static DEVICE_ATTR_RW(init_verbs);
+static DEVICE_ATTR_RW(hints);
+static DEVICE_ATTR_RW(user_pin_configs);
+static DEVICE_ATTR_WO(reconfig);
+static DEVICE_ATTR_WO(clear);
+
+/*
+ * Look for hint string
+ */
+const char *snd_hda_get_hint(struct hda_codec *codec, const char *key)
+{
+ struct hda_hint *hint = get_hint(codec, key);
+ return hint ? hint->val : NULL;
+}
+EXPORT_SYMBOL_GPL(snd_hda_get_hint);
+
+int snd_hda_get_bool_hint(struct hda_codec *codec, const char *key)
+{
+ const char *p;
+ int ret;
+
+ mutex_lock(&codec->user_mutex);
+ p = snd_hda_get_hint(codec, key);
+ if (!p || !*p)
+ ret = -ENOENT;
+ else {
+ switch (toupper(*p)) {
+ case 'T': /* true */
+ case 'Y': /* yes */
+ case '1':
+ ret = 1;
+ break;
+ default:
+ ret = 0;
+ break;
+ }
+ }
+ mutex_unlock(&codec->user_mutex);
+ return ret;
+}
+EXPORT_SYMBOL_GPL(snd_hda_get_bool_hint);
+
+int snd_hda_get_int_hint(struct hda_codec *codec, const char *key, int *valp)
+{
+ const char *p;
+ unsigned long val;
+ int ret;
+
+ mutex_lock(&codec->user_mutex);
+ p = snd_hda_get_hint(codec, key);
+ if (!p)
+ ret = -ENOENT;
+ else if (kstrtoul(p, 0, &val))
+ ret = -EINVAL;
+ else {
+ *valp = val;
+ ret = 0;
+ }
+ mutex_unlock(&codec->user_mutex);
+ return ret;
+}
+EXPORT_SYMBOL_GPL(snd_hda_get_int_hint);
+#endif /* CONFIG_SND_HDA_RECONFIG */
+
+/*
+ * common sysfs attributes
+ */
+#ifdef CONFIG_SND_HDA_RECONFIG
+#define RECONFIG_DEVICE_ATTR(name) DEVICE_ATTR_RW(name)
+#else
+#define RECONFIG_DEVICE_ATTR(name) DEVICE_ATTR_RO(name)
+#endif
+static RECONFIG_DEVICE_ATTR(vendor_id);
+static RECONFIG_DEVICE_ATTR(subsystem_id);
+static RECONFIG_DEVICE_ATTR(revision_id);
+static DEVICE_ATTR_RO(afg);
+static DEVICE_ATTR_RO(mfg);
+static RECONFIG_DEVICE_ATTR(vendor_name);
+static RECONFIG_DEVICE_ATTR(chip_name);
+static RECONFIG_DEVICE_ATTR(modelname);
+static DEVICE_ATTR_RO(init_pin_configs);
+static DEVICE_ATTR_RO(driver_pin_configs);
+
+
+#ifdef CONFIG_SND_HDA_PATCH_LOADER
+
+/* parser mode */
+enum {
+ LINE_MODE_NONE,
+ LINE_MODE_CODEC,
+ LINE_MODE_MODEL,
+ LINE_MODE_PINCFG,
+ LINE_MODE_VERB,
+ LINE_MODE_HINT,
+ LINE_MODE_VENDOR_ID,
+ LINE_MODE_SUBSYSTEM_ID,
+ LINE_MODE_REVISION_ID,
+ LINE_MODE_CHIP_NAME,
+ NUM_LINE_MODES,
+};
+
+static inline int strmatch(const char *a, const char *b)
+{
+ return strnicmp(a, b, strlen(b)) == 0;
+}
+
+/* parse the contents after the line "[codec]"
+ * accept only the line with three numbers, and assign the current codec
+ */
+static void parse_codec_mode(char *buf, struct hda_bus *bus,
+ struct hda_codec **codecp)
+{
+ int vendorid, subid, caddr;
+ struct hda_codec *codec;
+
+ *codecp = NULL;
+ if (sscanf(buf, "%i %i %i", &vendorid, &subid, &caddr) == 3) {
+ list_for_each_entry(codec, &bus->codec_list, list) {
+ if ((vendorid <= 0 || codec->vendor_id == vendorid) &&
+ (subid <= 0 || codec->subsystem_id == subid) &&
+ codec->addr == caddr) {
+ *codecp = codec;
+ break;
+ }
+ }
+ }
+}
+
+/* parse the contents after the other command tags, [pincfg], [verb],
+ * [vendor_id], [subsystem_id], [revision_id], [chip_name], [hint] and [model]
+ * just pass to the sysfs helper (only when any codec was specified)
+ */
+static void parse_pincfg_mode(char *buf, struct hda_bus *bus,
+ struct hda_codec **codecp)
+{
+ parse_user_pin_configs(*codecp, buf);
+}
+
+static void parse_verb_mode(char *buf, struct hda_bus *bus,
+ struct hda_codec **codecp)
+{
+ parse_init_verbs(*codecp, buf);
+}
+
+static void parse_hint_mode(char *buf, struct hda_bus *bus,
+ struct hda_codec **codecp)
+{
+ parse_hints(*codecp, buf);
+}
+
+static void parse_model_mode(char *buf, struct hda_bus *bus,
+ struct hda_codec **codecp)
+{
+ kfree((*codecp)->modelname);
+ (*codecp)->modelname = kstrdup(buf, GFP_KERNEL);
+}
+
+static void parse_chip_name_mode(char *buf, struct hda_bus *bus,
+ struct hda_codec **codecp)
+{
+ kfree((*codecp)->chip_name);
+ (*codecp)->chip_name = kstrdup(buf, GFP_KERNEL);
+}
+
+#define DEFINE_PARSE_ID_MODE(name) \
+static void parse_##name##_mode(char *buf, struct hda_bus *bus, \
+ struct hda_codec **codecp) \
+{ \
+ unsigned long val; \
+ if (!kstrtoul(buf, 0, &val)) \
+ (*codecp)->name = val; \
+}
+
+DEFINE_PARSE_ID_MODE(vendor_id);
+DEFINE_PARSE_ID_MODE(subsystem_id);
+DEFINE_PARSE_ID_MODE(revision_id);
+
+
+struct hda_patch_item {
+ const char *tag;
+ const char *alias;
+ void (*parser)(char *buf, struct hda_bus *bus, struct hda_codec **retc);
+};
+
+static struct hda_patch_item patch_items[NUM_LINE_MODES] = {
+ [LINE_MODE_CODEC] = {
+ .tag = "[codec]",
+ .parser = parse_codec_mode,
+ },
+ [LINE_MODE_MODEL] = {
+ .tag = "[model]",
+ .parser = parse_model_mode,
+ },
+ [LINE_MODE_VERB] = {
+ .tag = "[verb]",
+ .alias = "[init_verbs]",
+ .parser = parse_verb_mode,
+ },
+ [LINE_MODE_PINCFG] = {
+ .tag = "[pincfg]",
+ .alias = "[user_pin_configs]",
+ .parser = parse_pincfg_mode,
+ },
+ [LINE_MODE_HINT] = {
+ .tag = "[hint]",
+ .alias = "[hints]",
+ .parser = parse_hint_mode
+ },
+ [LINE_MODE_VENDOR_ID] = {
+ .tag = "[vendor_id]",
+ .parser = parse_vendor_id_mode,
+ },
+ [LINE_MODE_SUBSYSTEM_ID] = {
+ .tag = "[subsystem_id]",
+ .parser = parse_subsystem_id_mode,
+ },
+ [LINE_MODE_REVISION_ID] = {
+ .tag = "[revision_id]",
+ .parser = parse_revision_id_mode,
+ },
+ [LINE_MODE_CHIP_NAME] = {
+ .tag = "[chip_name]",
+ .parser = parse_chip_name_mode,
+ },
+};
+
+/* check the line starting with '[' -- change the parser mode accodingly */
+static int parse_line_mode(char *buf, struct hda_bus *bus)
+{
+ int i;
+ for (i = 0; i < ARRAY_SIZE(patch_items); i++) {
+ if (!patch_items[i].tag)
+ continue;
+ if (strmatch(buf, patch_items[i].tag))
+ return i;
+ if (patch_items[i].alias && strmatch(buf, patch_items[i].alias))
+ return i;
+ }
+ return LINE_MODE_NONE;
+}
+
+/* copy one line from the buffer in fw, and update the fields in fw
+ * return zero if it reaches to the end of the buffer, or non-zero
+ * if successfully copied a line
+ *
+ * the spaces at the beginning and the end of the line are stripped
+ */
+static int get_line_from_fw(char *buf, int size, size_t *fw_size_p,
+ const void **fw_data_p)
+{
+ int len;
+ size_t fw_size = *fw_size_p;
+ const char *p = *fw_data_p;
+
+ while (isspace(*p) && fw_size) {
+ p++;
+ fw_size--;
+ }
+ if (!fw_size)
+ return 0;
+
+ for (len = 0; len < fw_size; len++) {
+ if (!*p)
+ break;
+ if (*p == '\n') {
+ p++;
+ len++;
+ break;
+ }
+ if (len < size)
+ *buf++ = *p++;
+ }
+ *buf = 0;
+ *fw_size_p = fw_size - len;
+ *fw_data_p = p;
+ remove_trail_spaces(buf);
+ return 1;
+}
+
+/*
+ * load a "patch" firmware file and parse it
+ */
+int snd_hda_load_patch(struct hda_bus *bus, size_t fw_size, const void *fw_buf)
+{
+ char buf[128];
+ struct hda_codec *codec;
+ int line_mode;
+
+ line_mode = LINE_MODE_NONE;
+ codec = NULL;
+ while (get_line_from_fw(buf, sizeof(buf) - 1, &fw_size, &fw_buf)) {
+ if (!*buf || *buf == '#' || *buf == '\n')
+ continue;
+ if (*buf == '[')
+ line_mode = parse_line_mode(buf, bus);
+ else if (patch_items[line_mode].parser &&
+ (codec || line_mode <= LINE_MODE_CODEC))
+ patch_items[line_mode].parser(buf, bus, &codec);
+ }
+ return 0;
+}
+EXPORT_SYMBOL_GPL(snd_hda_load_patch);
+#endif /* CONFIG_SND_HDA_PATCH_LOADER */
+
+/*
+ * sysfs entries
+ */
+static struct attribute *hda_dev_attrs[] = {
+ &dev_attr_vendor_id.attr,
+ &dev_attr_subsystem_id.attr,
+ &dev_attr_revision_id.attr,
+ &dev_attr_afg.attr,
+ &dev_attr_mfg.attr,
+ &dev_attr_vendor_name.attr,
+ &dev_attr_chip_name.attr,
+ &dev_attr_modelname.attr,
+ &dev_attr_init_pin_configs.attr,
+ &dev_attr_driver_pin_configs.attr,
+#ifdef CONFIG_PM
+ &dev_attr_power_on_acct.attr,
+ &dev_attr_power_off_acct.attr,
+#endif
+#ifdef CONFIG_SND_HDA_RECONFIG
+ &dev_attr_init_verbs.attr,
+ &dev_attr_hints.attr,
+ &dev_attr_user_pin_configs.attr,
+ &dev_attr_reconfig.attr,
+ &dev_attr_clear.attr,
+#endif
+ NULL
+};
+
+static struct attribute_group hda_dev_attr_group = {
+ .attrs = hda_dev_attrs,
+};
+
+const struct attribute_group *snd_hda_dev_attr_groups[] = {
+ &hda_dev_attr_group,
+ NULL
+};
+
+void snd_hda_sysfs_init(struct hda_codec *codec)
+{
+ mutex_init(&codec->user_mutex);
+#ifdef CONFIG_SND_HDA_RECONFIG
+ snd_array_init(&codec->init_verbs, sizeof(struct hda_verb), 32);
+ snd_array_init(&codec->hints, sizeof(struct hda_hint), 32);
+ snd_array_init(&codec->user_pins, sizeof(struct hda_pincfg), 16);
+#endif
+}
+
+void snd_hda_sysfs_clear(struct hda_codec *codec)
+{
+#ifdef CONFIG_SND_HDA_RECONFIG
+ int i;
+
+ /* clear init verbs */
+ snd_array_free(&codec->init_verbs);
+ /* clear hints */
+ for (i = 0; i < codec->hints.used; i++) {
+ struct hda_hint *hint = snd_array_elem(&codec->hints, i);
+ kfree(hint->key); /* we don't need to free hint->val */
+ }
+ snd_array_free(&codec->hints);
+ snd_array_free(&codec->user_pins);
+#endif
+}
diff --git a/sound/pci/hda/hda_tegra.c b/sound/pci/hda/hda_tegra.c
new file mode 100644
index 00000000000..358414da641
--- /dev/null
+++ b/sound/pci/hda/hda_tegra.c
@@ -0,0 +1,588 @@
+/*
+ *
+ * Implementation of primary ALSA driver code base for NVIDIA Tegra HDA.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#include <linux/clk.h>
+#include <linux/clocksource.h>
+#include <linux/completion.h>
+#include <linux/delay.h>
+#include <linux/dma-mapping.h>
+#include <linux/init.h>
+#include <linux/interrupt.h>
+#include <linux/io.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/mutex.h>
+#include <linux/of_device.h>
+#include <linux/reboot.h>
+#include <linux/slab.h>
+#include <linux/time.h>
+
+#include <sound/core.h>
+#include <sound/initval.h>
+
+#include "hda_codec.h"
+#include "hda_controller.h"
+#include "hda_priv.h"
+
+/* Defines for Nvidia Tegra HDA support */
+#define HDA_BAR0 0x8000
+
+#define HDA_CFG_CMD 0x1004
+#define HDA_CFG_BAR0 0x1010
+
+#define HDA_ENABLE_IO_SPACE (1 << 0)
+#define HDA_ENABLE_MEM_SPACE (1 << 1)
+#define HDA_ENABLE_BUS_MASTER (1 << 2)
+#define HDA_ENABLE_SERR (1 << 8)
+#define HDA_DISABLE_INTR (1 << 10)
+#define HDA_BAR0_INIT_PROGRAM 0xFFFFFFFF
+#define HDA_BAR0_FINAL_PROGRAM (1 << 14)
+
+/* IPFS */
+#define HDA_IPFS_CONFIG 0x180
+#define HDA_IPFS_EN_FPCI 0x1
+
+#define HDA_IPFS_FPCI_BAR0 0x80
+#define HDA_FPCI_BAR0_START 0x40
+
+#define HDA_IPFS_INTR_MASK 0x188
+#define HDA_IPFS_EN_INTR (1 << 16)
+
+/* max number of SDs */
+#define NUM_CAPTURE_SD 1
+#define NUM_PLAYBACK_SD 1
+
+struct hda_tegra {
+ struct azx chip;
+ struct device *dev;
+ struct clk *hda_clk;
+ struct clk *hda2codec_2x_clk;
+ struct clk *hda2hdmi_clk;
+ void __iomem *regs;
+};
+
+#ifdef CONFIG_PM
+static int power_save = CONFIG_SND_HDA_POWER_SAVE_DEFAULT;
+module_param(power_save, bint, 0644);
+MODULE_PARM_DESC(power_save,
+ "Automatic power-saving timeout (in seconds, 0 = disable).");
+#else
+static int power_save = 0;
+#endif
+
+/*
+ * DMA page allocation ops.
+ */
+static int dma_alloc_pages(struct azx *chip, int type, size_t size,
+ struct snd_dma_buffer *buf)
+{
+ return snd_dma_alloc_pages(type, chip->card->dev, size, buf);
+}
+
+static void dma_free_pages(struct azx *chip, struct snd_dma_buffer *buf)
+{
+ snd_dma_free_pages(buf);
+}
+
+static int substream_alloc_pages(struct azx *chip,
+ struct snd_pcm_substream *substream,
+ size_t size)
+{
+ struct azx_dev *azx_dev = get_azx_dev(substream);
+
+ azx_dev->bufsize = 0;
+ azx_dev->period_bytes = 0;
+ azx_dev->format_val = 0;
+ return snd_pcm_lib_malloc_pages(substream, size);
+}
+
+static int substream_free_pages(struct azx *chip,
+ struct snd_pcm_substream *substream)
+{
+ return snd_pcm_lib_free_pages(substream);
+}
+
+/*
+ * Register access ops. Tegra HDA register access is DWORD only.
+ */
+static void hda_tegra_writel(u32 value, u32 *addr)
+{
+ writel(value, addr);
+}
+
+static u32 hda_tegra_readl(u32 *addr)
+{
+ return readl(addr);
+}
+
+static void hda_tegra_writew(u16 value, u16 *addr)
+{
+ unsigned int shift = ((unsigned long)(addr) & 0x3) << 3;
+ void *dword_addr = (void *)((unsigned long)(addr) & ~0x3);
+ u32 v;
+
+ v = readl(dword_addr);
+ v &= ~(0xffff << shift);
+ v |= value << shift;
+ writel(v, dword_addr);
+}
+
+static u16 hda_tegra_readw(u16 *addr)
+{
+ unsigned int shift = ((unsigned long)(addr) & 0x3) << 3;
+ void *dword_addr = (void *)((unsigned long)(addr) & ~0x3);
+ u32 v;
+
+ v = readl(dword_addr);
+ return (v >> shift) & 0xffff;
+}
+
+static void hda_tegra_writeb(u8 value, u8 *addr)
+{
+ unsigned int shift = ((unsigned long)(addr) & 0x3) << 3;
+ void *dword_addr = (void *)((unsigned long)(addr) & ~0x3);
+ u32 v;
+
+ v = readl(dword_addr);
+ v &= ~(0xff << shift);
+ v |= value << shift;
+ writel(v, dword_addr);
+}
+
+static u8 hda_tegra_readb(u8 *addr)
+{
+ unsigned int shift = ((unsigned long)(addr) & 0x3) << 3;
+ void *dword_addr = (void *)((unsigned long)(addr) & ~0x3);
+ u32 v;
+
+ v = readl(dword_addr);
+ return (v >> shift) & 0xff;
+}
+
+static const struct hda_controller_ops hda_tegra_ops = {
+ .reg_writel = hda_tegra_writel,
+ .reg_readl = hda_tegra_readl,
+ .reg_writew = hda_tegra_writew,
+ .reg_readw = hda_tegra_readw,
+ .reg_writeb = hda_tegra_writeb,
+ .reg_readb = hda_tegra_readb,
+ .dma_alloc_pages = dma_alloc_pages,
+ .dma_free_pages = dma_free_pages,
+ .substream_alloc_pages = substream_alloc_pages,
+ .substream_free_pages = substream_free_pages,
+};
+
+static void hda_tegra_init(struct hda_tegra *hda)
+{
+ u32 v;
+
+ /* Enable PCI access */
+ v = readl(hda->regs + HDA_IPFS_CONFIG);
+ v |= HDA_IPFS_EN_FPCI;
+ writel(v, hda->regs + HDA_IPFS_CONFIG);
+
+ /* Enable MEM/IO space and bus master */
+ v = readl(hda->regs + HDA_CFG_CMD);
+ v &= ~HDA_DISABLE_INTR;
+ v |= HDA_ENABLE_MEM_SPACE | HDA_ENABLE_IO_SPACE |
+ HDA_ENABLE_BUS_MASTER | HDA_ENABLE_SERR;
+ writel(v, hda->regs + HDA_CFG_CMD);
+
+ writel(HDA_BAR0_INIT_PROGRAM, hda->regs + HDA_CFG_BAR0);
+ writel(HDA_BAR0_FINAL_PROGRAM, hda->regs + HDA_CFG_BAR0);
+ writel(HDA_FPCI_BAR0_START, hda->regs + HDA_IPFS_FPCI_BAR0);
+
+ v = readl(hda->regs + HDA_IPFS_INTR_MASK);
+ v |= HDA_IPFS_EN_INTR;
+ writel(v, hda->regs + HDA_IPFS_INTR_MASK);
+}
+
+static int hda_tegra_enable_clocks(struct hda_tegra *data)
+{
+ int rc;
+
+ rc = clk_prepare_enable(data->hda_clk);
+ if (rc)
+ return rc;
+ rc = clk_prepare_enable(data->hda2codec_2x_clk);
+ if (rc)
+ goto disable_hda;
+ rc = clk_prepare_enable(data->hda2hdmi_clk);
+ if (rc)
+ goto disable_codec_2x;
+
+ return 0;
+
+disable_codec_2x:
+ clk_disable_unprepare(data->hda2codec_2x_clk);
+disable_hda:
+ clk_disable_unprepare(data->hda_clk);
+ return rc;
+}
+
+#ifdef CONFIG_PM_SLEEP
+static void hda_tegra_disable_clocks(struct hda_tegra *data)
+{
+ clk_disable_unprepare(data->hda2hdmi_clk);
+ clk_disable_unprepare(data->hda2codec_2x_clk);
+ clk_disable_unprepare(data->hda_clk);
+}
+
+/*
+ * power management
+ */
+static int hda_tegra_suspend(struct device *dev)
+{
+ struct snd_card *card = dev_get_drvdata(dev);
+ struct azx *chip = card->private_data;
+ struct azx_pcm *p;
+ struct hda_tegra *hda = container_of(chip, struct hda_tegra, chip);
+
+ snd_power_change_state(card, SNDRV_CTL_POWER_D3hot);
+ list_for_each_entry(p, &chip->pcm_list, list)
+ snd_pcm_suspend_all(p->pcm);
+ if (chip->initialized)
+ snd_hda_suspend(chip->bus);
+
+ azx_stop_chip(chip);
+ azx_enter_link_reset(chip);
+ hda_tegra_disable_clocks(hda);
+
+ return 0;
+}
+
+static int hda_tegra_resume(struct device *dev)
+{
+ struct snd_card *card = dev_get_drvdata(dev);
+ struct azx *chip = card->private_data;
+ struct hda_tegra *hda = container_of(chip, struct hda_tegra, chip);
+ int status;
+
+ hda_tegra_enable_clocks(hda);
+
+ /* Read STATESTS before controller reset */
+ status = azx_readw(chip, STATESTS);
+
+ hda_tegra_init(hda);
+
+ azx_init_chip(chip, 1);
+
+ snd_hda_resume(chip->bus);
+ snd_power_change_state(card, SNDRV_CTL_POWER_D0);
+
+ return 0;
+}
+#endif /* CONFIG_PM_SLEEP */
+
+static const struct dev_pm_ops hda_tegra_pm = {
+ SET_SYSTEM_SLEEP_PM_OPS(hda_tegra_suspend, hda_tegra_resume)
+};
+
+/*
+ * reboot notifier for hang-up problem at power-down
+ */
+static int hda_tegra_halt(struct notifier_block *nb, unsigned long event,
+ void *buf)
+{
+ struct azx *chip = container_of(nb, struct azx, reboot_notifier);
+ snd_hda_bus_reboot_notify(chip->bus);
+ azx_stop_chip(chip);
+ return NOTIFY_OK;
+}
+
+static void hda_tegra_notifier_register(struct azx *chip)
+{
+ chip->reboot_notifier.notifier_call = hda_tegra_halt;
+ register_reboot_notifier(&chip->reboot_notifier);
+}
+
+static void hda_tegra_notifier_unregister(struct azx *chip)
+{
+ if (chip->reboot_notifier.notifier_call)
+ unregister_reboot_notifier(&chip->reboot_notifier);
+}
+
+/*
+ * destructor
+ */
+static int hda_tegra_dev_free(struct snd_device *device)
+{
+ int i;
+ struct azx *chip = device->device_data;
+
+ hda_tegra_notifier_unregister(chip);
+
+ if (chip->initialized) {
+ for (i = 0; i < chip->num_streams; i++)
+ azx_stream_stop(chip, &chip->azx_dev[i]);
+ azx_stop_chip(chip);
+ }
+
+ azx_free_stream_pages(chip);
+
+ return 0;
+}
+
+static int hda_tegra_init_chip(struct azx *chip, struct platform_device *pdev)
+{
+ struct hda_tegra *hda = container_of(chip, struct hda_tegra, chip);
+ struct device *dev = hda->dev;
+ struct resource *res;
+ int err;
+
+ hda->hda_clk = devm_clk_get(dev, "hda");
+ if (IS_ERR(hda->hda_clk))
+ return PTR_ERR(hda->hda_clk);
+ hda->hda2codec_2x_clk = devm_clk_get(dev, "hda2codec_2x");
+ if (IS_ERR(hda->hda2codec_2x_clk))
+ return PTR_ERR(hda->hda2codec_2x_clk);
+ hda->hda2hdmi_clk = devm_clk_get(dev, "hda2hdmi");
+ if (IS_ERR(hda->hda2hdmi_clk))
+ return PTR_ERR(hda->hda2hdmi_clk);
+
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ hda->regs = devm_ioremap_resource(dev, res);
+ if (IS_ERR(chip->remap_addr))
+ return PTR_ERR(chip->remap_addr);
+
+ chip->remap_addr = hda->regs + HDA_BAR0;
+ chip->addr = res->start + HDA_BAR0;
+
+ err = hda_tegra_enable_clocks(hda);
+ if (err)
+ return err;
+
+ hda_tegra_init(hda);
+
+ return 0;
+}
+
+/*
+ * The codecs were powered up in snd_hda_codec_new().
+ * Now all initialization done, so turn them down if possible
+ */
+static void power_down_all_codecs(struct azx *chip)
+{
+ struct hda_codec *codec;
+ list_for_each_entry(codec, &chip->bus->codec_list, list)
+ snd_hda_power_down(codec);
+}
+
+static int hda_tegra_first_init(struct azx *chip, struct platform_device *pdev)
+{
+ struct snd_card *card = chip->card;
+ int err;
+ unsigned short gcap;
+ int irq_id = platform_get_irq(pdev, 0);
+
+ err = hda_tegra_init_chip(chip, pdev);
+ if (err)
+ return err;
+
+ err = devm_request_irq(chip->card->dev, irq_id, azx_interrupt,
+ IRQF_SHARED, KBUILD_MODNAME, chip);
+ if (err) {
+ dev_err(chip->card->dev,
+ "unable to request IRQ %d, disabling device\n",
+ irq_id);
+ return err;
+ }
+ chip->irq = irq_id;
+
+ synchronize_irq(chip->irq);
+
+ gcap = azx_readw(chip, GCAP);
+ dev_dbg(card->dev, "chipset global capabilities = 0x%x\n", gcap);
+
+ /* read number of streams from GCAP register instead of using
+ * hardcoded value
+ */
+ chip->capture_streams = (gcap >> 8) & 0x0f;
+ chip->playback_streams = (gcap >> 12) & 0x0f;
+ if (!chip->playback_streams && !chip->capture_streams) {
+ /* gcap didn't give any info, switching to old method */
+ chip->playback_streams = NUM_PLAYBACK_SD;
+ chip->capture_streams = NUM_CAPTURE_SD;
+ }
+ chip->capture_index_offset = 0;
+ chip->playback_index_offset = chip->capture_streams;
+ chip->num_streams = chip->playback_streams + chip->capture_streams;
+ chip->azx_dev = devm_kcalloc(card->dev, chip->num_streams,
+ sizeof(*chip->azx_dev), GFP_KERNEL);
+ if (!chip->azx_dev)
+ return -ENOMEM;
+
+ err = azx_alloc_stream_pages(chip);
+ if (err < 0)
+ return err;
+
+ /* initialize streams */
+ azx_init_stream(chip);
+
+ /* initialize chip */
+ azx_init_chip(chip, 1);
+
+ /* codec detection */
+ if (!chip->codec_mask) {
+ dev_err(card->dev, "no codecs found!\n");
+ return -ENODEV;
+ }
+
+ strcpy(card->driver, "tegra-hda");
+ strcpy(card->shortname, "tegra-hda");
+ snprintf(card->longname, sizeof(card->longname),
+ "%s at 0x%lx irq %i",
+ card->shortname, chip->addr, chip->irq);
+
+ return 0;
+}
+
+/*
+ * constructor
+ */
+static int hda_tegra_create(struct snd_card *card,
+ unsigned int driver_caps,
+ const struct hda_controller_ops *hda_ops,
+ struct hda_tegra *hda)
+{
+ static struct snd_device_ops ops = {
+ .dev_free = hda_tegra_dev_free,
+ };
+ struct azx *chip;
+ int err;
+
+ chip = &hda->chip;
+
+ spin_lock_init(&chip->reg_lock);
+ mutex_init(&chip->open_mutex);
+ chip->card = card;
+ chip->ops = hda_ops;
+ chip->irq = -1;
+ chip->driver_caps = driver_caps;
+ chip->driver_type = driver_caps & 0xff;
+ chip->dev_index = 0;
+ INIT_LIST_HEAD(&chip->pcm_list);
+ INIT_LIST_HEAD(&chip->list);
+
+ chip->position_fix[0] = POS_FIX_AUTO;
+ chip->position_fix[1] = POS_FIX_AUTO;
+ chip->codec_probe_mask = -1;
+
+ chip->single_cmd = false;
+ chip->snoop = true;
+
+ err = snd_device_new(card, SNDRV_DEV_LOWLEVEL, chip, &ops);
+ if (err < 0) {
+ dev_err(card->dev, "Error creating device\n");
+ return err;
+ }
+
+ return 0;
+}
+
+static const struct of_device_id hda_tegra_match[] = {
+ { .compatible = "nvidia,tegra30-hda" },
+ {},
+};
+MODULE_DEVICE_TABLE(of, hda_tegra_match);
+
+static int hda_tegra_probe(struct platform_device *pdev)
+{
+ struct snd_card *card;
+ struct azx *chip;
+ struct hda_tegra *hda;
+ int err;
+ const unsigned int driver_flags = AZX_DCAPS_RIRB_DELAY;
+
+ hda = devm_kzalloc(&pdev->dev, sizeof(*hda), GFP_KERNEL);
+ if (!hda)
+ return -ENOMEM;
+ hda->dev = &pdev->dev;
+ chip = &hda->chip;
+
+ err = snd_card_new(&pdev->dev, SNDRV_DEFAULT_IDX1, SNDRV_DEFAULT_STR1,
+ THIS_MODULE, 0, &card);
+ if (err < 0) {
+ dev_err(&pdev->dev, "Error creating card!\n");
+ return err;
+ }
+
+ err = hda_tegra_create(card, driver_flags, &hda_tegra_ops, hda);
+ if (err < 0)
+ goto out_free;
+ card->private_data = chip;
+
+ dev_set_drvdata(&pdev->dev, card);
+
+ err = hda_tegra_first_init(chip, pdev);
+ if (err < 0)
+ goto out_free;
+
+ /* create codec instances */
+ err = azx_codec_create(chip, NULL, 0, &power_save);
+ if (err < 0)
+ goto out_free;
+
+ err = azx_codec_configure(chip);
+ if (err < 0)
+ goto out_free;
+
+ /* create PCM streams */
+ err = snd_hda_build_pcms(chip->bus);
+ if (err < 0)
+ goto out_free;
+
+ /* create mixer controls */
+ err = azx_mixer_create(chip);
+ if (err < 0)
+ goto out_free;
+
+ err = snd_card_register(chip->card);
+ if (err < 0)
+ goto out_free;
+
+ chip->running = 1;
+ power_down_all_codecs(chip);
+ hda_tegra_notifier_register(chip);
+
+ return 0;
+
+out_free:
+ snd_card_free(card);
+ return err;
+}
+
+static int hda_tegra_remove(struct platform_device *pdev)
+{
+ return snd_card_free(dev_get_drvdata(&pdev->dev));
+}
+
+static struct platform_driver tegra_platform_hda = {
+ .driver = {
+ .name = "tegra-hda",
+ .pm = &hda_tegra_pm,
+ .of_match_table = hda_tegra_match,
+ },
+ .probe = hda_tegra_probe,
+ .remove = hda_tegra_remove,
+};
+module_platform_driver(tegra_platform_hda);
+
+MODULE_DESCRIPTION("Tegra HDA bus driver");
+MODULE_LICENSE("GPL v2");
diff --git a/sound/pci/hda/hda_trace.h b/sound/pci/hda/hda_trace.h
new file mode 100644
index 00000000000..3a1c63161eb
--- /dev/null
+++ b/sound/pci/hda/hda_trace.h
@@ -0,0 +1,143 @@
+#undef TRACE_SYSTEM
+#define TRACE_SYSTEM hda
+#define TRACE_INCLUDE_FILE hda_trace
+
+#if !defined(_TRACE_HDA_H) || defined(TRACE_HEADER_MULTI_READ)
+#define _TRACE_HDA_H
+
+#include <linux/tracepoint.h>
+
+struct hda_bus;
+struct hda_codec;
+
+DECLARE_EVENT_CLASS(hda_cmd,
+
+ TP_PROTO(struct hda_codec *codec, unsigned int val),
+
+ TP_ARGS(codec, val),
+
+ TP_STRUCT__entry(
+ __field( unsigned int, card )
+ __field( unsigned int, addr )
+ __field( unsigned int, val )
+ ),
+
+ TP_fast_assign(
+ __entry->card = (codec)->bus->card->number;
+ __entry->addr = (codec)->addr;
+ __entry->val = (val);
+ ),
+
+ TP_printk("[%d:%d] val=%x", __entry->card, __entry->addr, __entry->val)
+);
+
+DEFINE_EVENT(hda_cmd, hda_send_cmd,
+ TP_PROTO(struct hda_codec *codec, unsigned int val),
+ TP_ARGS(codec, val)
+);
+
+DEFINE_EVENT(hda_cmd, hda_get_response,
+ TP_PROTO(struct hda_codec *codec, unsigned int val),
+ TP_ARGS(codec, val)
+);
+
+TRACE_EVENT(hda_bus_reset,
+
+ TP_PROTO(struct hda_bus *bus),
+
+ TP_ARGS(bus),
+
+ TP_STRUCT__entry(
+ __field( unsigned int, card )
+ ),
+
+ TP_fast_assign(
+ __entry->card = (bus)->card->number;
+ ),
+
+ TP_printk("[%d]", __entry->card)
+);
+
+#ifdef CONFIG_PM
+DECLARE_EVENT_CLASS(hda_power,
+
+ TP_PROTO(struct hda_codec *codec),
+
+ TP_ARGS(codec),
+
+ TP_STRUCT__entry(
+ __field( unsigned int, card )
+ __field( unsigned int, addr )
+ ),
+
+ TP_fast_assign(
+ __entry->card = (codec)->bus->card->number;
+ __entry->addr = (codec)->addr;
+ ),
+
+ TP_printk("[%d:%d]", __entry->card, __entry->addr)
+);
+
+DEFINE_EVENT(hda_power, hda_power_down,
+ TP_PROTO(struct hda_codec *codec),
+ TP_ARGS(codec)
+);
+
+DEFINE_EVENT(hda_power, hda_power_up,
+ TP_PROTO(struct hda_codec *codec),
+ TP_ARGS(codec)
+);
+
+TRACE_EVENT(hda_power_count,
+ TP_PROTO(struct hda_codec *codec),
+ TP_ARGS(codec),
+ TP_STRUCT__entry(
+ __field( unsigned int, card )
+ __field( unsigned int, addr )
+ __field( int, power_count )
+ __field( int, power_on )
+ __field( int, power_transition )
+ ),
+
+ TP_fast_assign(
+ __entry->card = (codec)->bus->card->number;
+ __entry->addr = (codec)->addr;
+ __entry->power_count = (codec)->power_count;
+ __entry->power_on = (codec)->power_on;
+ __entry->power_transition = (codec)->power_transition;
+ ),
+
+ TP_printk("[%d:%d] power_count=%d, power_on=%d, power_transition=%d",
+ __entry->card, __entry->addr, __entry->power_count,
+ __entry->power_on, __entry->power_transition)
+);
+#endif /* CONFIG_PM */
+
+TRACE_EVENT(hda_unsol_event,
+
+ TP_PROTO(struct hda_bus *bus, u32 res, u32 res_ex),
+
+ TP_ARGS(bus, res, res_ex),
+
+ TP_STRUCT__entry(
+ __field( unsigned int, card )
+ __field( u32, res )
+ __field( u32, res_ex )
+ ),
+
+ TP_fast_assign(
+ __entry->card = (bus)->card->number;
+ __entry->res = res;
+ __entry->res_ex = res_ex;
+ ),
+
+ TP_printk("[%d] res=%x, res_ex=%x", __entry->card,
+ __entry->res, __entry->res_ex)
+);
+
+#endif /* _TRACE_HDA_H */
+
+/* This part must be outside protection */
+#undef TRACE_INCLUDE_PATH
+#define TRACE_INCLUDE_PATH .
+#include <trace/define_trace.h>
diff --git a/sound/pci/hda/patch_analog.c b/sound/pci/hda/patch_analog.c
index 2942d2a9ea1..06275f8807a 100644
--- a/sound/pci/hda/patch_analog.c
+++ b/sound/pci/hda/patch_analog.c
@@ -20,501 +20,80 @@
*/
#include <linux/init.h>
-#include <linux/delay.h>
#include <linux/slab.h>
-#include <linux/pci.h>
+#include <linux/module.h>
#include <sound/core.h>
#include "hda_codec.h"
#include "hda_local.h"
+#include "hda_auto_parser.h"
#include "hda_beep.h"
+#include "hda_jack.h"
+#include "hda_generic.h"
-struct ad198x_spec {
- struct snd_kcontrol_new *mixers[6];
- int num_mixers;
- unsigned int beep_amp; /* beep amp value, set via set_beep_amp() */
- const struct hda_verb *init_verbs[6]; /* initialization verbs
- * don't forget NULL termination!
- */
- unsigned int num_init_verbs;
-
- /* playback */
- struct hda_multi_out multiout; /* playback set-up
- * max_channels, dacs must be set
- * dig_out_nid and hp_nid are optional
- */
- unsigned int cur_eapd;
- unsigned int need_dac_fix;
-
- hda_nid_t *alt_dac_nid;
- struct hda_pcm_stream *stream_analog_alt_playback;
-
- /* capture */
- unsigned int num_adc_nids;
- hda_nid_t *adc_nids;
- hda_nid_t dig_in_nid; /* digital-in NID; optional */
-
- /* capture source */
- const struct hda_input_mux *input_mux;
- hda_nid_t *capsrc_nids;
- unsigned int cur_mux[3];
-
- /* channel model */
- const struct hda_channel_mode *channel_mode;
- int num_channel_mode;
-
- /* PCM information */
- struct hda_pcm pcm_rec[3]; /* used in alc_build_pcms() */
-
- unsigned int spdif_route;
-
- /* dynamic controls, init_verbs and input_mux */
- struct auto_pin_cfg autocfg;
- struct snd_array kctls;
- struct hda_input_mux private_imux;
- hda_nid_t private_dac_nids[AUTO_CFG_MAX_OUTS];
-
- unsigned int jack_present: 1;
- unsigned int inv_jack_detect: 1;/* inverted jack-detection */
- unsigned int inv_eapd: 1; /* inverted EAPD implementation */
- unsigned int analog_beep: 1; /* analog beep input present */
-
-#ifdef CONFIG_SND_HDA_POWER_SAVE
- struct hda_loopback_check loopback;
-#endif
- /* for virtual master */
- hda_nid_t vmaster_nid;
- const char * const *slave_vols;
- const char * const *slave_sws;
-};
-
-/*
- * input MUX handling (common part)
- */
-static int ad198x_mux_enum_info(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *uinfo)
-{
- struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
- struct ad198x_spec *spec = codec->spec;
- return snd_hda_input_mux_info(spec->input_mux, uinfo);
-}
-
-static int ad198x_mux_enum_get(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
-{
- struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
- struct ad198x_spec *spec = codec->spec;
- unsigned int adc_idx = snd_ctl_get_ioffidx(kcontrol, &ucontrol->id);
-
- ucontrol->value.enumerated.item[0] = spec->cur_mux[adc_idx];
- return 0;
-}
-
-static int ad198x_mux_enum_put(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
-{
- struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
- struct ad198x_spec *spec = codec->spec;
- unsigned int adc_idx = snd_ctl_get_ioffidx(kcontrol, &ucontrol->id);
-
- return snd_hda_input_mux_put(codec, spec->input_mux, ucontrol,
- spec->capsrc_nids[adc_idx],
- &spec->cur_mux[adc_idx]);
-}
-
-/*
- * initialization (common callbacks)
- */
-static int ad198x_init(struct hda_codec *codec)
-{
- struct ad198x_spec *spec = codec->spec;
- int i;
-
- for (i = 0; i < spec->num_init_verbs; i++)
- snd_hda_sequence_write(codec, spec->init_verbs[i]);
- return 0;
-}
+struct ad198x_spec {
+ struct hda_gen_spec gen;
-static const char * const ad_slave_vols[] = {
- "Front Playback Volume",
- "Surround Playback Volume",
- "Center Playback Volume",
- "LFE Playback Volume",
- "Side Playback Volume",
- "Headphone Playback Volume",
- "Mono Playback Volume",
- "Speaker Playback Volume",
- "IEC958 Playback Volume",
- NULL
-};
+ /* for auto parser */
+ int smux_paths[4];
+ unsigned int cur_smux;
+ hda_nid_t eapd_nid;
-static const char * const ad_slave_sws[] = {
- "Front Playback Switch",
- "Surround Playback Switch",
- "Center Playback Switch",
- "LFE Playback Switch",
- "Side Playback Switch",
- "Headphone Playback Switch",
- "Mono Playback Switch",
- "Speaker Playback Switch",
- "IEC958 Playback Switch",
- NULL
-};
-
-static const char * const ad1988_6stack_fp_slave_vols[] = {
- "Front Playback Volume",
- "Surround Playback Volume",
- "Center Playback Volume",
- "LFE Playback Volume",
- "Side Playback Volume",
- "IEC958 Playback Volume",
- NULL
+ unsigned int beep_amp; /* beep amp value, set via set_beep_amp() */
};
-static const char * const ad1988_6stack_fp_slave_sws[] = {
- "Front Playback Switch",
- "Surround Playback Switch",
- "Center Playback Switch",
- "LFE Playback Switch",
- "Side Playback Switch",
- "IEC958 Playback Switch",
- NULL
-};
-static void ad198x_free_kctls(struct hda_codec *codec);
#ifdef CONFIG_SND_HDA_INPUT_BEEP
/* additional beep mixers; the actual parameters are overwritten at build */
-static struct snd_kcontrol_new ad_beep_mixer[] = {
+static const struct snd_kcontrol_new ad_beep_mixer[] = {
HDA_CODEC_VOLUME("Beep Playback Volume", 0, 0, HDA_OUTPUT),
HDA_CODEC_MUTE_BEEP("Beep Playback Switch", 0, 0, HDA_OUTPUT),
{ } /* end */
};
-static struct snd_kcontrol_new ad_beep2_mixer[] = {
- HDA_CODEC_VOLUME("Digital Beep Playback Volume", 0, 0, HDA_OUTPUT),
- HDA_CODEC_MUTE_BEEP("Digital Beep Playback Switch", 0, 0, HDA_OUTPUT),
- { } /* end */
-};
-
#define set_beep_amp(spec, nid, idx, dir) \
((spec)->beep_amp = HDA_COMPOSE_AMP_VAL(nid, 1, idx, dir)) /* mono */
#else
#define set_beep_amp(spec, nid, idx, dir) /* NOP */
#endif
-static int ad198x_build_controls(struct hda_codec *codec)
+#ifdef CONFIG_SND_HDA_INPUT_BEEP
+static int create_beep_ctls(struct hda_codec *codec)
{
struct ad198x_spec *spec = codec->spec;
- struct snd_kcontrol *kctl;
- unsigned int i;
- int err;
-
- for (i = 0; i < spec->num_mixers; i++) {
- err = snd_hda_add_new_ctls(codec, spec->mixers[i]);
- if (err < 0)
- return err;
- }
- if (spec->multiout.dig_out_nid) {
- err = snd_hda_create_spdif_out_ctls(codec, spec->multiout.dig_out_nid);
- if (err < 0)
- return err;
- err = snd_hda_create_spdif_share_sw(codec,
- &spec->multiout);
- if (err < 0)
- return err;
- spec->multiout.share_spdif = 1;
- }
- if (spec->dig_in_nid) {
- err = snd_hda_create_spdif_in_ctls(codec, spec->dig_in_nid);
- if (err < 0)
- return err;
- }
-
- /* create beep controls if needed */
-#ifdef CONFIG_SND_HDA_INPUT_BEEP
- if (spec->beep_amp) {
- struct snd_kcontrol_new *knew;
- knew = spec->analog_beep ? ad_beep2_mixer : ad_beep_mixer;
- for ( ; knew->name; knew++) {
- struct snd_kcontrol *kctl;
- kctl = snd_ctl_new1(knew, codec);
- if (!kctl)
- return -ENOMEM;
- kctl->private_value = spec->beep_amp;
- err = snd_hda_ctl_add(codec, 0, kctl);
- if (err < 0)
- return err;
- }
- }
-#endif
+ const struct snd_kcontrol_new *knew;
- /* if we have no master control, let's create it */
- if (!snd_hda_find_mixer_ctl(codec, "Master Playback Volume")) {
- unsigned int vmaster_tlv[4];
- snd_hda_set_vmaster_tlv(codec, spec->vmaster_nid,
- HDA_OUTPUT, vmaster_tlv);
- err = snd_hda_add_vmaster(codec, "Master Playback Volume",
- vmaster_tlv,
- (spec->slave_vols ?
- spec->slave_vols : ad_slave_vols));
- if (err < 0)
- return err;
- }
- if (!snd_hda_find_mixer_ctl(codec, "Master Playback Switch")) {
- err = snd_hda_add_vmaster(codec, "Master Playback Switch",
- NULL,
- (spec->slave_sws ?
- spec->slave_sws : ad_slave_sws));
- if (err < 0)
- return err;
- }
-
- ad198x_free_kctls(codec); /* no longer needed */
-
- /* assign Capture Source enums to NID */
- kctl = snd_hda_find_mixer_ctl(codec, "Capture Source");
- if (!kctl)
- kctl = snd_hda_find_mixer_ctl(codec, "Input Source");
- for (i = 0; kctl && i < kctl->count; i++) {
- err = snd_hda_add_nid(codec, kctl, i, spec->capsrc_nids[i]);
- if (err < 0)
- return err;
- }
+ if (!spec->beep_amp)
+ return 0;
- /* assign IEC958 enums to NID */
- kctl = snd_hda_find_mixer_ctl(codec,
- SNDRV_CTL_NAME_IEC958("",PLAYBACK,NONE) "Source");
- if (kctl) {
- err = snd_hda_add_nid(codec, kctl, 0,
- spec->multiout.dig_out_nid);
+ for (knew = ad_beep_mixer ; knew->name; knew++) {
+ int err;
+ struct snd_kcontrol *kctl;
+ kctl = snd_ctl_new1(knew, codec);
+ if (!kctl)
+ return -ENOMEM;
+ kctl->private_value = spec->beep_amp;
+ err = snd_hda_ctl_add(codec, 0, kctl);
if (err < 0)
return err;
}
-
return 0;
}
-
-#ifdef CONFIG_SND_HDA_POWER_SAVE
-static int ad198x_check_power_status(struct hda_codec *codec, hda_nid_t nid)
-{
- struct ad198x_spec *spec = codec->spec;
- return snd_hda_check_amp_list_power(codec, &spec->loopback, nid);
-}
+#else
+#define create_beep_ctls(codec) 0
#endif
-/*
- * Analog playback callbacks
- */
-static int ad198x_playback_pcm_open(struct hda_pcm_stream *hinfo,
- struct hda_codec *codec,
- struct snd_pcm_substream *substream)
-{
- struct ad198x_spec *spec = codec->spec;
- return snd_hda_multi_out_analog_open(codec, &spec->multiout, substream,
- hinfo);
-}
-
-static int ad198x_playback_pcm_prepare(struct hda_pcm_stream *hinfo,
- struct hda_codec *codec,
- unsigned int stream_tag,
- unsigned int format,
- struct snd_pcm_substream *substream)
-{
- struct ad198x_spec *spec = codec->spec;
- return snd_hda_multi_out_analog_prepare(codec, &spec->multiout, stream_tag,
- format, substream);
-}
-
-static int ad198x_playback_pcm_cleanup(struct hda_pcm_stream *hinfo,
- struct hda_codec *codec,
- struct snd_pcm_substream *substream)
-{
- struct ad198x_spec *spec = codec->spec;
- return snd_hda_multi_out_analog_cleanup(codec, &spec->multiout);
-}
-
-static struct hda_pcm_stream ad198x_pcm_analog_alt_playback = {
- .substreams = 1,
- .channels_min = 2,
- .channels_max = 2,
- /* NID is set in ad198x_build_pcms */
-};
-
-/*
- * Digital out
- */
-static int ad198x_dig_playback_pcm_open(struct hda_pcm_stream *hinfo,
- struct hda_codec *codec,
- struct snd_pcm_substream *substream)
-{
- struct ad198x_spec *spec = codec->spec;
- return snd_hda_multi_out_dig_open(codec, &spec->multiout);
-}
-
-static int ad198x_dig_playback_pcm_close(struct hda_pcm_stream *hinfo,
- struct hda_codec *codec,
- struct snd_pcm_substream *substream)
-{
- struct ad198x_spec *spec = codec->spec;
- return snd_hda_multi_out_dig_close(codec, &spec->multiout);
-}
-
-static int ad198x_dig_playback_pcm_prepare(struct hda_pcm_stream *hinfo,
- struct hda_codec *codec,
- unsigned int stream_tag,
- unsigned int format,
- struct snd_pcm_substream *substream)
-{
- struct ad198x_spec *spec = codec->spec;
- return snd_hda_multi_out_dig_prepare(codec, &spec->multiout, stream_tag,
- format, substream);
-}
-
-static int ad198x_dig_playback_pcm_cleanup(struct hda_pcm_stream *hinfo,
- struct hda_codec *codec,
- struct snd_pcm_substream *substream)
-{
- struct ad198x_spec *spec = codec->spec;
- return snd_hda_multi_out_dig_cleanup(codec, &spec->multiout);
-}
-
-/*
- * Analog capture
- */
-static int ad198x_capture_pcm_prepare(struct hda_pcm_stream *hinfo,
- struct hda_codec *codec,
- unsigned int stream_tag,
- unsigned int format,
- struct snd_pcm_substream *substream)
-{
- struct ad198x_spec *spec = codec->spec;
- snd_hda_codec_setup_stream(codec, spec->adc_nids[substream->number],
- stream_tag, 0, format);
- return 0;
-}
-
-static int ad198x_capture_pcm_cleanup(struct hda_pcm_stream *hinfo,
- struct hda_codec *codec,
- struct snd_pcm_substream *substream)
-{
- struct ad198x_spec *spec = codec->spec;
- snd_hda_codec_cleanup_stream(codec, spec->adc_nids[substream->number]);
- return 0;
-}
-
-
-/*
- */
-static struct hda_pcm_stream ad198x_pcm_analog_playback = {
- .substreams = 1,
- .channels_min = 2,
- .channels_max = 6, /* changed later */
- .nid = 0, /* fill later */
- .ops = {
- .open = ad198x_playback_pcm_open,
- .prepare = ad198x_playback_pcm_prepare,
- .cleanup = ad198x_playback_pcm_cleanup
- },
-};
-
-static struct hda_pcm_stream ad198x_pcm_analog_capture = {
- .substreams = 1,
- .channels_min = 2,
- .channels_max = 2,
- .nid = 0, /* fill later */
- .ops = {
- .prepare = ad198x_capture_pcm_prepare,
- .cleanup = ad198x_capture_pcm_cleanup
- },
-};
-
-static struct hda_pcm_stream ad198x_pcm_digital_playback = {
- .substreams = 1,
- .channels_min = 2,
- .channels_max = 2,
- .nid = 0, /* fill later */
- .ops = {
- .open = ad198x_dig_playback_pcm_open,
- .close = ad198x_dig_playback_pcm_close,
- .prepare = ad198x_dig_playback_pcm_prepare,
- .cleanup = ad198x_dig_playback_pcm_cleanup
- },
-};
-
-static struct hda_pcm_stream ad198x_pcm_digital_capture = {
- .substreams = 1,
- .channels_min = 2,
- .channels_max = 2,
- /* NID is set in alc_build_pcms */
-};
-
-static int ad198x_build_pcms(struct hda_codec *codec)
-{
- struct ad198x_spec *spec = codec->spec;
- struct hda_pcm *info = spec->pcm_rec;
-
- codec->num_pcms = 1;
- codec->pcm_info = info;
-
- info->name = "AD198x Analog";
- info->stream[SNDRV_PCM_STREAM_PLAYBACK] = ad198x_pcm_analog_playback;
- info->stream[SNDRV_PCM_STREAM_PLAYBACK].channels_max = spec->multiout.max_channels;
- info->stream[SNDRV_PCM_STREAM_PLAYBACK].nid = spec->multiout.dac_nids[0];
- info->stream[SNDRV_PCM_STREAM_CAPTURE] = ad198x_pcm_analog_capture;
- info->stream[SNDRV_PCM_STREAM_CAPTURE].substreams = spec->num_adc_nids;
- info->stream[SNDRV_PCM_STREAM_CAPTURE].nid = spec->adc_nids[0];
-
- if (spec->multiout.dig_out_nid) {
- info++;
- codec->num_pcms++;
- info->name = "AD198x Digital";
- info->pcm_type = HDA_PCM_TYPE_SPDIF;
- info->stream[SNDRV_PCM_STREAM_PLAYBACK] = ad198x_pcm_digital_playback;
- info->stream[SNDRV_PCM_STREAM_PLAYBACK].nid = spec->multiout.dig_out_nid;
- if (spec->dig_in_nid) {
- info->stream[SNDRV_PCM_STREAM_CAPTURE] = ad198x_pcm_digital_capture;
- info->stream[SNDRV_PCM_STREAM_CAPTURE].nid = spec->dig_in_nid;
- }
- }
-
- if (spec->alt_dac_nid && spec->stream_analog_alt_playback) {
- codec->num_pcms++;
- info = spec->pcm_rec + 2;
- info->name = "AD198x Headphone";
- info->pcm_type = HDA_PCM_TYPE_AUDIO;
- info->stream[SNDRV_PCM_STREAM_PLAYBACK] =
- *spec->stream_analog_alt_playback;
- info->stream[SNDRV_PCM_STREAM_PLAYBACK].nid =
- spec->alt_dac_nid[0];
- }
-
- return 0;
-}
-
-static inline void ad198x_shutup(struct hda_codec *codec)
-{
- snd_hda_shutup_pins(codec);
-}
-
-static void ad198x_free_kctls(struct hda_codec *codec)
-{
- struct ad198x_spec *spec = codec->spec;
-
- if (spec->kctls.list) {
- struct snd_kcontrol_new *kctl = spec->kctls.list;
- int i;
- for (i = 0; i < spec->kctls.used; i++)
- kfree(kctl[i].name);
- }
- snd_array_free(&spec->kctls);
-}
static void ad198x_power_eapd_write(struct hda_codec *codec, hda_nid_t front,
hda_nid_t hp)
{
- struct ad198x_spec *spec = codec->spec;
- snd_hda_codec_write(codec, front, 0, AC_VERB_SET_EAPD_BTLENABLE,
- !spec->inv_eapd ? 0x00 : 0x02);
- snd_hda_codec_write(codec, hp, 0, AC_VERB_SET_EAPD_BTLENABLE,
- !spec->inv_eapd ? 0x00 : 0x02);
+ if (snd_hda_query_pin_caps(codec, front) & AC_PINCAP_EAPD)
+ snd_hda_codec_write(codec, front, 0, AC_VERB_SET_EAPD_BTLENABLE,
+ !codec->inv_eapd ? 0x00 : 0x02);
+ if (snd_hda_query_pin_caps(codec, hp) & AC_PINCAP_EAPD)
+ snd_hda_codec_write(codec, hp, 0, AC_VERB_SET_EAPD_BTLENABLE,
+ !codec->inv_eapd ? 0x00 : 0x02);
}
static void ad198x_power_eapd(struct hda_codec *codec)
@@ -529,6 +108,10 @@ static void ad198x_power_eapd(struct hda_codec *codec)
case 0x11d4184a:
case 0x11d4194a:
case 0x11d4194b:
+ case 0x11d41988:
+ case 0x11d4198b:
+ case 0x11d4989a:
+ case 0x11d4989b:
ad198x_power_eapd_write(codec, 0x12, 0x11);
break;
case 0x11d41981:
@@ -538,975 +121,404 @@ static void ad198x_power_eapd(struct hda_codec *codec)
case 0x11d41986:
ad198x_power_eapd_write(codec, 0x1b, 0x1a);
break;
- case 0x11d41988:
- case 0x11d4198b:
- case 0x11d4989a:
- case 0x11d4989b:
- ad198x_power_eapd_write(codec, 0x29, 0x22);
- break;
}
}
-static void ad198x_free(struct hda_codec *codec)
+static void ad198x_shutup(struct hda_codec *codec)
{
- struct ad198x_spec *spec = codec->spec;
-
- if (!spec)
- return;
-
- ad198x_shutup(codec);
- ad198x_free_kctls(codec);
- kfree(spec);
- snd_hda_detach_beep_device(codec);
+ snd_hda_shutup_pins(codec);
+ ad198x_power_eapd(codec);
}
-#ifdef SND_HDA_NEEDS_RESUME
-static int ad198x_suspend(struct hda_codec *codec, pm_message_t state)
+#ifdef CONFIG_PM
+static int ad198x_suspend(struct hda_codec *codec)
{
ad198x_shutup(codec);
- ad198x_power_eapd(codec);
return 0;
}
#endif
-static struct hda_codec_ops ad198x_patch_ops = {
- .build_controls = ad198x_build_controls,
- .build_pcms = ad198x_build_pcms,
- .init = ad198x_init,
- .free = ad198x_free,
-#ifdef CONFIG_SND_HDA_POWER_SAVE
- .check_power_status = ad198x_check_power_status,
-#endif
-#ifdef SND_HDA_NEEDS_RESUME
- .suspend = ad198x_suspend,
-#endif
- .reboot_notify = ad198x_shutup,
-};
-
-
-/*
- * EAPD control
- * the private value = nid
- */
-#define ad198x_eapd_info snd_ctl_boolean_mono_info
-
-static int ad198x_eapd_get(struct snd_kcontrol *kcontrol,
- struct snd_ctl_elem_value *ucontrol)
+/* follow EAPD via vmaster hook */
+static void ad_vmaster_eapd_hook(void *private_data, int enabled)
{
- struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
+ struct hda_codec *codec = private_data;
struct ad198x_spec *spec = codec->spec;
- if (spec->inv_eapd)
- ucontrol->value.integer.value[0] = ! spec->cur_eapd;
- else
- ucontrol->value.integer.value[0] = spec->cur_eapd;
- return 0;
-}
-static int ad198x_eapd_put(struct snd_kcontrol *kcontrol,
- struct snd_ctl_elem_value *ucontrol)
-{
- struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
- struct ad198x_spec *spec = codec->spec;
- hda_nid_t nid = kcontrol->private_value & 0xff;
- unsigned int eapd;
- eapd = !!ucontrol->value.integer.value[0];
- if (spec->inv_eapd)
- eapd = !eapd;
- if (eapd == spec->cur_eapd)
- return 0;
- spec->cur_eapd = eapd;
- snd_hda_codec_write_cache(codec, nid,
- 0, AC_VERB_SET_EAPD_BTLENABLE,
- eapd ? 0x02 : 0x00);
- return 1;
+ if (!spec->eapd_nid)
+ return;
+ if (codec->inv_eapd)
+ enabled = !enabled;
+ snd_hda_codec_update_cache(codec, spec->eapd_nid, 0,
+ AC_VERB_SET_EAPD_BTLENABLE,
+ enabled ? 0x02 : 0x00);
}
-static int ad198x_ch_mode_info(struct snd_kcontrol *kcontrol,
- struct snd_ctl_elem_info *uinfo);
-static int ad198x_ch_mode_get(struct snd_kcontrol *kcontrol,
- struct snd_ctl_elem_value *ucontrol);
-static int ad198x_ch_mode_put(struct snd_kcontrol *kcontrol,
- struct snd_ctl_elem_value *ucontrol);
-
-
/*
- * AD1986A specific
- */
-
-#define AD1986A_SPDIF_OUT 0x02
-#define AD1986A_FRONT_DAC 0x03
-#define AD1986A_SURR_DAC 0x04
-#define AD1986A_CLFE_DAC 0x05
-#define AD1986A_ADC 0x06
-
-static hda_nid_t ad1986a_dac_nids[3] = {
- AD1986A_FRONT_DAC, AD1986A_SURR_DAC, AD1986A_CLFE_DAC
-};
-static hda_nid_t ad1986a_adc_nids[1] = { AD1986A_ADC };
-static hda_nid_t ad1986a_capsrc_nids[1] = { 0x12 };
-
-static struct hda_input_mux ad1986a_capture_source = {
- .num_items = 7,
- .items = {
- { "Mic", 0x0 },
- { "CD", 0x1 },
- { "Aux", 0x3 },
- { "Line", 0x4 },
- { "Mix", 0x5 },
- { "Mono", 0x6 },
- { "Phone", 0x7 },
- },
-};
-
-
-static struct hda_bind_ctls ad1986a_bind_pcm_vol = {
- .ops = &snd_hda_bind_vol,
- .values = {
- HDA_COMPOSE_AMP_VAL(AD1986A_FRONT_DAC, 3, 0, HDA_OUTPUT),
- HDA_COMPOSE_AMP_VAL(AD1986A_SURR_DAC, 3, 0, HDA_OUTPUT),
- HDA_COMPOSE_AMP_VAL(AD1986A_CLFE_DAC, 3, 0, HDA_OUTPUT),
- 0
- },
-};
-
-static struct hda_bind_ctls ad1986a_bind_pcm_sw = {
- .ops = &snd_hda_bind_sw,
- .values = {
- HDA_COMPOSE_AMP_VAL(AD1986A_FRONT_DAC, 3, 0, HDA_OUTPUT),
- HDA_COMPOSE_AMP_VAL(AD1986A_SURR_DAC, 3, 0, HDA_OUTPUT),
- HDA_COMPOSE_AMP_VAL(AD1986A_CLFE_DAC, 3, 0, HDA_OUTPUT),
- 0
- },
-};
-
-/*
- * mixers
+ * Automatic parse of I/O pins from the BIOS configuration
*/
-static struct snd_kcontrol_new ad1986a_mixers[] = {
- /*
- * bind volumes/mutes of 3 DACs as a single PCM control for simplicity
- */
- HDA_BIND_VOL("PCM Playback Volume", &ad1986a_bind_pcm_vol),
- HDA_BIND_SW("PCM Playback Switch", &ad1986a_bind_pcm_sw),
- HDA_CODEC_VOLUME("Front Playback Volume", 0x1b, 0x0, HDA_OUTPUT),
- HDA_CODEC_MUTE("Front Playback Switch", 0x1b, 0x0, HDA_OUTPUT),
- HDA_CODEC_VOLUME("Surround Playback Volume", 0x1c, 0x0, HDA_OUTPUT),
- HDA_CODEC_MUTE("Surround Playback Switch", 0x1c, 0x0, HDA_OUTPUT),
- HDA_CODEC_VOLUME_MONO("Center Playback Volume", 0x1d, 1, 0x0, HDA_OUTPUT),
- HDA_CODEC_VOLUME_MONO("LFE Playback Volume", 0x1d, 2, 0x0, HDA_OUTPUT),
- HDA_CODEC_MUTE_MONO("Center Playback Switch", 0x1d, 1, 0x0, HDA_OUTPUT),
- HDA_CODEC_MUTE_MONO("LFE Playback Switch", 0x1d, 2, 0x0, HDA_OUTPUT),
- HDA_CODEC_VOLUME("Headphone Playback Volume", 0x1a, 0x0, HDA_OUTPUT),
- HDA_CODEC_MUTE("Headphone Playback Switch", 0x1a, 0x0, HDA_OUTPUT),
- HDA_CODEC_VOLUME("CD Playback Volume", 0x15, 0x0, HDA_OUTPUT),
- HDA_CODEC_MUTE("CD Playback Switch", 0x15, 0x0, HDA_OUTPUT),
- HDA_CODEC_VOLUME("Line Playback Volume", 0x17, 0x0, HDA_OUTPUT),
- HDA_CODEC_MUTE("Line Playback Switch", 0x17, 0x0, HDA_OUTPUT),
- HDA_CODEC_VOLUME("Aux Playback Volume", 0x16, 0x0, HDA_OUTPUT),
- HDA_CODEC_MUTE("Aux Playback Switch", 0x16, 0x0, HDA_OUTPUT),
- HDA_CODEC_VOLUME("Mic Playback Volume", 0x13, 0x0, HDA_OUTPUT),
- HDA_CODEC_MUTE("Mic Playback Switch", 0x13, 0x0, HDA_OUTPUT),
- HDA_CODEC_VOLUME("Mic Boost Volume", 0x0f, 0x0, HDA_OUTPUT),
- HDA_CODEC_VOLUME("Mono Playback Volume", 0x1e, 0x0, HDA_OUTPUT),
- HDA_CODEC_MUTE("Mono Playback Switch", 0x1e, 0x0, HDA_OUTPUT),
- HDA_CODEC_VOLUME("Capture Volume", 0x12, 0x0, HDA_OUTPUT),
- HDA_CODEC_MUTE("Capture Switch", 0x12, 0x0, HDA_OUTPUT),
- {
- .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
- .name = "Capture Source",
- .info = ad198x_mux_enum_info,
- .get = ad198x_mux_enum_get,
- .put = ad198x_mux_enum_put,
- },
- HDA_CODEC_MUTE("Stereo Downmix Switch", 0x09, 0x0, HDA_OUTPUT),
- { } /* end */
-};
-/* additional mixers for 3stack mode */
-static struct snd_kcontrol_new ad1986a_3st_mixers[] = {
- {
- .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
- .name = "Channel Mode",
- .info = ad198x_ch_mode_info,
- .get = ad198x_ch_mode_get,
- .put = ad198x_ch_mode_put,
- },
- { } /* end */
-};
-
-/* laptop model - 2ch only */
-static hda_nid_t ad1986a_laptop_dac_nids[1] = { AD1986A_FRONT_DAC };
-
-/* master controls both pins 0x1a and 0x1b */
-static struct hda_bind_ctls ad1986a_laptop_master_vol = {
- .ops = &snd_hda_bind_vol,
- .values = {
- HDA_COMPOSE_AMP_VAL(0x1a, 3, 0, HDA_OUTPUT),
- HDA_COMPOSE_AMP_VAL(0x1b, 3, 0, HDA_OUTPUT),
- 0,
- },
-};
+static int ad198x_auto_build_controls(struct hda_codec *codec)
+{
+ int err;
-static struct hda_bind_ctls ad1986a_laptop_master_sw = {
- .ops = &snd_hda_bind_sw,
- .values = {
- HDA_COMPOSE_AMP_VAL(0x1a, 3, 0, HDA_OUTPUT),
- HDA_COMPOSE_AMP_VAL(0x1b, 3, 0, HDA_OUTPUT),
- 0,
- },
-};
+ err = snd_hda_gen_build_controls(codec);
+ if (err < 0)
+ return err;
+ err = create_beep_ctls(codec);
+ if (err < 0)
+ return err;
+ return 0;
+}
-static struct snd_kcontrol_new ad1986a_laptop_mixers[] = {
- HDA_CODEC_VOLUME("PCM Playback Volume", 0x03, 0x0, HDA_OUTPUT),
- HDA_CODEC_MUTE("PCM Playback Switch", 0x03, 0x0, HDA_OUTPUT),
- HDA_BIND_VOL("Master Playback Volume", &ad1986a_laptop_master_vol),
- HDA_BIND_SW("Master Playback Switch", &ad1986a_laptop_master_sw),
- HDA_CODEC_VOLUME("CD Playback Volume", 0x15, 0x0, HDA_OUTPUT),
- HDA_CODEC_MUTE("CD Playback Switch", 0x15, 0x0, HDA_OUTPUT),
- HDA_CODEC_VOLUME("Line Playback Volume", 0x17, 0x0, HDA_OUTPUT),
- HDA_CODEC_MUTE("Line Playback Switch", 0x17, 0x0, HDA_OUTPUT),
- HDA_CODEC_VOLUME("Aux Playback Volume", 0x16, 0x0, HDA_OUTPUT),
- HDA_CODEC_MUTE("Aux Playback Switch", 0x16, 0x0, HDA_OUTPUT),
- HDA_CODEC_VOLUME("Mic Playback Volume", 0x13, 0x0, HDA_OUTPUT),
- HDA_CODEC_MUTE("Mic Playback Switch", 0x13, 0x0, HDA_OUTPUT),
- HDA_CODEC_VOLUME("Mic Boost Volume", 0x0f, 0x0, HDA_OUTPUT),
- /*
- HDA_CODEC_VOLUME("Mono Playback Volume", 0x1e, 0x0, HDA_OUTPUT),
- HDA_CODEC_MUTE("Mono Playback Switch", 0x1e, 0x0, HDA_OUTPUT), */
- HDA_CODEC_VOLUME("Capture Volume", 0x12, 0x0, HDA_OUTPUT),
- HDA_CODEC_MUTE("Capture Switch", 0x12, 0x0, HDA_OUTPUT),
- {
- .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
- .name = "Capture Source",
- .info = ad198x_mux_enum_info,
- .get = ad198x_mux_enum_get,
- .put = ad198x_mux_enum_put,
- },
- { } /* end */
+static const struct hda_codec_ops ad198x_auto_patch_ops = {
+ .build_controls = ad198x_auto_build_controls,
+ .build_pcms = snd_hda_gen_build_pcms,
+ .init = snd_hda_gen_init,
+ .free = snd_hda_gen_free,
+ .unsol_event = snd_hda_jack_unsol_event,
+#ifdef CONFIG_PM
+ .check_power_status = snd_hda_gen_check_power_status,
+ .suspend = ad198x_suspend,
+#endif
+ .reboot_notify = ad198x_shutup,
};
-/* laptop-eapd model - 2ch only */
-static struct hda_input_mux ad1986a_laptop_eapd_capture_source = {
- .num_items = 3,
- .items = {
- { "Mic", 0x0 },
- { "Internal Mic", 0x4 },
- { "Mix", 0x5 },
- },
-};
+static int ad198x_parse_auto_config(struct hda_codec *codec, bool indep_hp)
+{
+ struct ad198x_spec *spec = codec->spec;
+ struct auto_pin_cfg *cfg = &spec->gen.autocfg;
+ int err;
-static struct hda_input_mux ad1986a_automic_capture_source = {
- .num_items = 2,
- .items = {
- { "Mic", 0x0 },
- { "Mix", 0x5 },
- },
-};
+ codec->spdif_status_reset = 1;
+ codec->no_trigger_sense = 1;
+ codec->no_sticky_stream = 1;
-static struct snd_kcontrol_new ad1986a_laptop_master_mixers[] = {
- HDA_BIND_VOL("Master Playback Volume", &ad1986a_laptop_master_vol),
- HDA_BIND_SW("Master Playback Switch", &ad1986a_laptop_master_sw),
- { } /* end */
-};
+ spec->gen.indep_hp = indep_hp;
+ spec->gen.add_stereo_mix_input = 1;
-static struct snd_kcontrol_new ad1986a_laptop_eapd_mixers[] = {
- HDA_CODEC_VOLUME("PCM Playback Volume", 0x03, 0x0, HDA_OUTPUT),
- HDA_CODEC_MUTE("PCM Playback Switch", 0x03, 0x0, HDA_OUTPUT),
- HDA_CODEC_VOLUME("Mic Playback Volume", 0x13, 0x0, HDA_OUTPUT),
- HDA_CODEC_MUTE("Mic Playback Switch", 0x13, 0x0, HDA_OUTPUT),
- HDA_CODEC_VOLUME("Mic Boost Volume", 0x0f, 0x0, HDA_OUTPUT),
- HDA_CODEC_VOLUME("Capture Volume", 0x12, 0x0, HDA_OUTPUT),
- HDA_CODEC_MUTE("Capture Switch", 0x12, 0x0, HDA_OUTPUT),
- {
- .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
- .name = "Capture Source",
- .info = ad198x_mux_enum_info,
- .get = ad198x_mux_enum_get,
- .put = ad198x_mux_enum_put,
- },
- {
- .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
- .name = "External Amplifier",
- .subdevice = HDA_SUBDEV_NID_FLAG | 0x1b,
- .info = ad198x_eapd_info,
- .get = ad198x_eapd_get,
- .put = ad198x_eapd_put,
- .private_value = 0x1b, /* port-D */
- },
- { } /* end */
-};
+ err = snd_hda_parse_pin_defcfg(codec, cfg, NULL, 0);
+ if (err < 0)
+ return err;
+ err = snd_hda_gen_parse_auto_config(codec, cfg);
+ if (err < 0)
+ return err;
-static struct snd_kcontrol_new ad1986a_laptop_intmic_mixers[] = {
- HDA_CODEC_VOLUME("Internal Mic Playback Volume", 0x17, 0, HDA_OUTPUT),
- HDA_CODEC_MUTE("Internal Mic Playback Switch", 0x17, 0, HDA_OUTPUT),
- { } /* end */
-};
+ codec->patch_ops = ad198x_auto_patch_ops;
-/* re-connect the mic boost input according to the jack sensing */
-static void ad1986a_automic(struct hda_codec *codec)
-{
- unsigned int present;
- present = snd_hda_jack_detect(codec, 0x1f);
- /* 0 = 0x1f, 2 = 0x1d, 4 = mixed */
- snd_hda_codec_write(codec, 0x0f, 0, AC_VERB_SET_CONNECT_SEL,
- present ? 0 : 2);
+ return 0;
}
-#define AD1986A_MIC_EVENT 0x36
+/*
+ * AD1986A specific
+ */
-static void ad1986a_automic_unsol_event(struct hda_codec *codec,
- unsigned int res)
+static int alloc_ad_spec(struct hda_codec *codec)
{
- if ((res >> 26) != AD1986A_MIC_EVENT)
- return;
- ad1986a_automic(codec);
-}
+ struct ad198x_spec *spec;
-static int ad1986a_automic_init(struct hda_codec *codec)
-{
- ad198x_init(codec);
- ad1986a_automic(codec);
+ spec = kzalloc(sizeof(*spec), GFP_KERNEL);
+ if (!spec)
+ return -ENOMEM;
+ codec->spec = spec;
+ snd_hda_gen_spec_init(&spec->gen);
return 0;
}
-/* laptop-automute - 2ch only */
+/*
+ * AD1986A fixup codes
+ */
-static void ad1986a_update_hp(struct hda_codec *codec)
+/* Lenovo N100 seems to report the reversed bit for HP jack-sensing */
+static void ad_fixup_inv_jack_detect(struct hda_codec *codec,
+ const struct hda_fixup *fix, int action)
{
struct ad198x_spec *spec = codec->spec;
- unsigned int mute;
- if (spec->jack_present)
- mute = HDA_AMP_MUTE; /* mute internal speaker */
- else
- /* unmute internal speaker if necessary */
- mute = snd_hda_codec_amp_read(codec, 0x1a, 0, HDA_OUTPUT, 0);
- snd_hda_codec_amp_stereo(codec, 0x1b, HDA_OUTPUT, 0,
- HDA_AMP_MUTE, mute);
+ if (action == HDA_FIXUP_ACT_PRE_PROBE) {
+ codec->inv_jack_detect = 1;
+ spec->gen.keep_eapd_on = 1;
+ spec->gen.vmaster_mute.hook = ad_vmaster_eapd_hook;
+ spec->eapd_nid = 0x1b;
+ }
}
-static void ad1986a_hp_automute(struct hda_codec *codec)
+/* Toshiba Satellite L40 implements EAPD in a standard way unlike others */
+static void ad1986a_fixup_eapd(struct hda_codec *codec,
+ const struct hda_fixup *fix, int action)
{
struct ad198x_spec *spec = codec->spec;
- spec->jack_present = snd_hda_jack_detect(codec, 0x1a);
- if (spec->inv_jack_detect)
- spec->jack_present = !spec->jack_present;
- ad1986a_update_hp(codec);
-}
-
-#define AD1986A_HP_EVENT 0x37
-
-static void ad1986a_hp_unsol_event(struct hda_codec *codec, unsigned int res)
-{
- if ((res >> 26) != AD1986A_HP_EVENT)
- return;
- ad1986a_hp_automute(codec);
-}
-
-static int ad1986a_hp_init(struct hda_codec *codec)
-{
- ad198x_init(codec);
- ad1986a_hp_automute(codec);
- return 0;
-}
-
-/* bind hp and internal speaker mute (with plug check) */
-static int ad1986a_hp_master_sw_put(struct snd_kcontrol *kcontrol,
- struct snd_ctl_elem_value *ucontrol)
-{
- struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
- long *valp = ucontrol->value.integer.value;
- int change;
-
- change = snd_hda_codec_amp_update(codec, 0x1a, 0, HDA_OUTPUT, 0,
- HDA_AMP_MUTE,
- valp[0] ? 0 : HDA_AMP_MUTE);
- change |= snd_hda_codec_amp_update(codec, 0x1a, 1, HDA_OUTPUT, 0,
- HDA_AMP_MUTE,
- valp[1] ? 0 : HDA_AMP_MUTE);
- if (change)
- ad1986a_update_hp(codec);
- return change;
-}
-
-static struct snd_kcontrol_new ad1986a_automute_master_mixers[] = {
- HDA_BIND_VOL("Master Playback Volume", &ad1986a_laptop_master_vol),
- {
- .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
- .name = "Master Playback Switch",
- .subdevice = HDA_SUBDEV_AMP_FLAG,
- .info = snd_hda_mixer_amp_switch_info,
- .get = snd_hda_mixer_amp_switch_get,
- .put = ad1986a_hp_master_sw_put,
- .private_value = HDA_COMPOSE_AMP_VAL(0x1a, 3, 0, HDA_OUTPUT),
- },
- { } /* end */
-};
-
-
-/*
- * initialization verbs
- */
-static struct hda_verb ad1986a_init_verbs[] = {
- /* Front, Surround, CLFE DAC; mute as default */
- {0x03, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080},
- {0x04, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080},
- {0x05, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080},
- /* Downmix - off */
- {0x09, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080},
- /* HP, Line-Out, Surround, CLFE selectors */
- {0x0a, AC_VERB_SET_CONNECT_SEL, 0x0},
- {0x0b, AC_VERB_SET_CONNECT_SEL, 0x0},
- {0x0c, AC_VERB_SET_CONNECT_SEL, 0x0},
- {0x0d, AC_VERB_SET_CONNECT_SEL, 0x0},
- /* Mono selector */
- {0x0e, AC_VERB_SET_CONNECT_SEL, 0x0},
- /* Mic selector: Mic 1/2 pin */
- {0x0f, AC_VERB_SET_CONNECT_SEL, 0x0},
- /* Line-in selector: Line-in */
- {0x10, AC_VERB_SET_CONNECT_SEL, 0x0},
- /* Mic 1/2 swap */
- {0x11, AC_VERB_SET_CONNECT_SEL, 0x0},
- /* Record selector: mic */
- {0x12, AC_VERB_SET_CONNECT_SEL, 0x0},
- /* Mic, Phone, CD, Aux, Line-In amp; mute as default */
- {0x13, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080},
- {0x14, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080},
- {0x15, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080},
- {0x16, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080},
- {0x17, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080},
- /* PC beep */
- {0x18, AC_VERB_SET_CONNECT_SEL, 0x0},
- /* HP, Line-Out, Surround, CLFE, Mono pins; mute as default */
- {0x1a, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080},
- {0x1b, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080},
- {0x1c, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080},
- {0x1d, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080},
- {0x1e, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080},
- /* HP Pin */
- {0x1a, AC_VERB_SET_PIN_WIDGET_CONTROL, 0xc0 },
- /* Front, Surround, CLFE Pins */
- {0x1b, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x40 },
- {0x1c, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x40 },
- {0x1d, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x40 },
- /* Mono Pin */
- {0x1e, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x40 },
- /* Mic Pin */
- {0x1f, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x24 },
- /* Line, Aux, CD, Beep-In Pin */
- {0x20, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x20 },
- {0x21, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x20 },
- {0x22, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x20 },
- {0x23, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x20 },
- {0x24, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x20 },
- { } /* end */
-};
-
-static struct hda_verb ad1986a_ch2_init[] = {
- /* Surround out -> Line In */
- { 0x1c, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN },
- /* Line-in selectors */
- { 0x10, AC_VERB_SET_CONNECT_SEL, 0x1 },
- /* CLFE -> Mic in */
- { 0x1d, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80 },
- /* Mic selector, mix C/LFE (backmic) and Mic (frontmic) */
- { 0x0f, AC_VERB_SET_CONNECT_SEL, 0x4 },
- { } /* end */
-};
-
-static struct hda_verb ad1986a_ch4_init[] = {
- /* Surround out -> Surround */
- { 0x1c, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT },
- { 0x10, AC_VERB_SET_CONNECT_SEL, 0x0 },
- /* CLFE -> Mic in */
- { 0x1d, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80 },
- { 0x0f, AC_VERB_SET_CONNECT_SEL, 0x4 },
- { } /* end */
-};
-
-static struct hda_verb ad1986a_ch6_init[] = {
- /* Surround out -> Surround out */
- { 0x1c, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT },
- { 0x10, AC_VERB_SET_CONNECT_SEL, 0x0 },
- /* CLFE -> CLFE */
- { 0x1d, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT },
- { 0x0f, AC_VERB_SET_CONNECT_SEL, 0x0 },
- { } /* end */
-};
-
-static struct hda_channel_mode ad1986a_modes[3] = {
- { 2, ad1986a_ch2_init },
- { 4, ad1986a_ch4_init },
- { 6, ad1986a_ch6_init },
-};
-
-/* eapd initialization */
-static struct hda_verb ad1986a_eapd_init_verbs[] = {
- {0x1b, AC_VERB_SET_EAPD_BTLENABLE, 0x00 },
- {}
-};
-
-static struct hda_verb ad1986a_automic_verbs[] = {
- {0x1d, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80},
- {0x1f, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80},
- /*{0x20, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80},*/
- {0x0f, AC_VERB_SET_CONNECT_SEL, 0x0},
- {0x1f, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | AD1986A_MIC_EVENT},
- {}
-};
-
-/* Ultra initialization */
-static struct hda_verb ad1986a_ultra_init[] = {
- /* eapd initialization */
- { 0x1b, AC_VERB_SET_EAPD_BTLENABLE, 0x00 },
- /* CLFE -> Mic in */
- { 0x0f, AC_VERB_SET_CONNECT_SEL, 0x2 },
- { 0x1d, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x24 },
- { 0x1d, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080 },
- { } /* end */
-};
-
-/* pin sensing on HP jack */
-static struct hda_verb ad1986a_hp_init_verbs[] = {
- {0x1a, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | AD1986A_HP_EVENT},
- {}
-};
-
-static void ad1986a_samsung_p50_unsol_event(struct hda_codec *codec,
- unsigned int res)
-{
- switch (res >> 26) {
- case AD1986A_HP_EVENT:
- ad1986a_hp_automute(codec);
- break;
- case AD1986A_MIC_EVENT:
- ad1986a_automic(codec);
- break;
+ if (action == HDA_FIXUP_ACT_PRE_PROBE) {
+ codec->inv_eapd = 0;
+ spec->gen.keep_eapd_on = 1;
+ spec->eapd_nid = 0x1b;
}
}
-static int ad1986a_samsung_p50_init(struct hda_codec *codec)
-{
- ad198x_init(codec);
- ad1986a_hp_automute(codec);
- ad1986a_automic(codec);
- return 0;
-}
-
-
-/* models */
enum {
- AD1986A_6STACK,
- AD1986A_3STACK,
- AD1986A_LAPTOP,
- AD1986A_LAPTOP_EAPD,
- AD1986A_LAPTOP_AUTOMUTE,
- AD1986A_ULTRA,
- AD1986A_SAMSUNG,
- AD1986A_SAMSUNG_P50,
- AD1986A_MODELS
-};
-
-static const char * const ad1986a_models[AD1986A_MODELS] = {
- [AD1986A_6STACK] = "6stack",
- [AD1986A_3STACK] = "3stack",
- [AD1986A_LAPTOP] = "laptop",
- [AD1986A_LAPTOP_EAPD] = "laptop-eapd",
- [AD1986A_LAPTOP_AUTOMUTE] = "laptop-automute",
- [AD1986A_ULTRA] = "ultra",
- [AD1986A_SAMSUNG] = "samsung",
- [AD1986A_SAMSUNG_P50] = "samsung-p50",
+ AD1986A_FIXUP_INV_JACK_DETECT,
+ AD1986A_FIXUP_ULTRA,
+ AD1986A_FIXUP_SAMSUNG,
+ AD1986A_FIXUP_3STACK,
+ AD1986A_FIXUP_LAPTOP,
+ AD1986A_FIXUP_LAPTOP_IMIC,
+ AD1986A_FIXUP_EAPD,
+};
+
+static const struct hda_fixup ad1986a_fixups[] = {
+ [AD1986A_FIXUP_INV_JACK_DETECT] = {
+ .type = HDA_FIXUP_FUNC,
+ .v.func = ad_fixup_inv_jack_detect,
+ },
+ [AD1986A_FIXUP_ULTRA] = {
+ .type = HDA_FIXUP_PINS,
+ .v.pins = (const struct hda_pintbl[]) {
+ { 0x1b, 0x90170110 }, /* speaker */
+ { 0x1d, 0x90a7013e }, /* int mic */
+ {}
+ },
+ },
+ [AD1986A_FIXUP_SAMSUNG] = {
+ .type = HDA_FIXUP_PINS,
+ .v.pins = (const struct hda_pintbl[]) {
+ { 0x1b, 0x90170110 }, /* speaker */
+ { 0x1d, 0x90a7013e }, /* int mic */
+ { 0x20, 0x411111f0 }, /* N/A */
+ { 0x24, 0x411111f0 }, /* N/A */
+ {}
+ },
+ },
+ [AD1986A_FIXUP_3STACK] = {
+ .type = HDA_FIXUP_PINS,
+ .v.pins = (const struct hda_pintbl[]) {
+ { 0x1a, 0x02214021 }, /* headphone */
+ { 0x1b, 0x01014011 }, /* front */
+ { 0x1c, 0x01813030 }, /* line-in */
+ { 0x1d, 0x01a19020 }, /* rear mic */
+ { 0x1e, 0x411111f0 }, /* N/A */
+ { 0x1f, 0x02a190f0 }, /* mic */
+ { 0x20, 0x411111f0 }, /* N/A */
+ {}
+ },
+ },
+ [AD1986A_FIXUP_LAPTOP] = {
+ .type = HDA_FIXUP_PINS,
+ .v.pins = (const struct hda_pintbl[]) {
+ { 0x1a, 0x02214021 }, /* headphone */
+ { 0x1b, 0x90170110 }, /* speaker */
+ { 0x1c, 0x411111f0 }, /* N/A */
+ { 0x1d, 0x411111f0 }, /* N/A */
+ { 0x1e, 0x411111f0 }, /* N/A */
+ { 0x1f, 0x02a191f0 }, /* mic */
+ { 0x20, 0x411111f0 }, /* N/A */
+ {}
+ },
+ },
+ [AD1986A_FIXUP_LAPTOP_IMIC] = {
+ .type = HDA_FIXUP_PINS,
+ .v.pins = (const struct hda_pintbl[]) {
+ { 0x1d, 0x90a7013e }, /* int mic */
+ {}
+ },
+ .chained_before = 1,
+ .chain_id = AD1986A_FIXUP_LAPTOP,
+ },
+ [AD1986A_FIXUP_EAPD] = {
+ .type = HDA_FIXUP_FUNC,
+ .v.func = ad1986a_fixup_eapd,
+ },
};
-static struct snd_pci_quirk ad1986a_cfg_tbl[] = {
- SND_PCI_QUIRK(0x103c, 0x30af, "HP B2800", AD1986A_LAPTOP_EAPD),
- SND_PCI_QUIRK(0x1043, 0x1153, "ASUS M9", AD1986A_LAPTOP_EAPD),
- SND_PCI_QUIRK(0x1043, 0x11f7, "ASUS U5A", AD1986A_LAPTOP_EAPD),
- SND_PCI_QUIRK(0x1043, 0x1213, "ASUS A6J", AD1986A_LAPTOP_EAPD),
- SND_PCI_QUIRK(0x1043, 0x1263, "ASUS U5F", AD1986A_LAPTOP_EAPD),
- SND_PCI_QUIRK(0x1043, 0x1297, "ASUS Z62F", AD1986A_LAPTOP_EAPD),
- SND_PCI_QUIRK(0x1043, 0x12b3, "ASUS V1j", AD1986A_LAPTOP_EAPD),
- SND_PCI_QUIRK(0x1043, 0x1302, "ASUS W3j", AD1986A_LAPTOP_EAPD),
- SND_PCI_QUIRK(0x1043, 0x1443, "ASUS VX1", AD1986A_LAPTOP),
- SND_PCI_QUIRK(0x1043, 0x1447, "ASUS A8J", AD1986A_3STACK),
- SND_PCI_QUIRK(0x1043, 0x817f, "ASUS P5", AD1986A_3STACK),
- SND_PCI_QUIRK(0x1043, 0x818f, "ASUS P5", AD1986A_LAPTOP),
- SND_PCI_QUIRK(0x1043, 0x81b3, "ASUS P5", AD1986A_3STACK),
- SND_PCI_QUIRK(0x1043, 0x81cb, "ASUS M2N", AD1986A_3STACK),
- SND_PCI_QUIRK(0x1043, 0x8234, "ASUS M2N", AD1986A_3STACK),
- SND_PCI_QUIRK(0x10de, 0xcb84, "ASUS A8N-VM", AD1986A_3STACK),
- SND_PCI_QUIRK(0x1179, 0xff40, "Toshiba Satellite L40-10Q", AD1986A_3STACK),
- SND_PCI_QUIRK(0x144d, 0xb03c, "Samsung R55", AD1986A_3STACK),
- SND_PCI_QUIRK(0x144d, 0xc01e, "FSC V2060", AD1986A_LAPTOP),
- SND_PCI_QUIRK(0x144d, 0xc024, "Samsung P50", AD1986A_SAMSUNG_P50),
- SND_PCI_QUIRK(0x144d, 0xc027, "Samsung Q1", AD1986A_ULTRA),
- SND_PCI_QUIRK_MASK(0x144d, 0xff00, 0xc000, "Samsung", AD1986A_SAMSUNG),
- SND_PCI_QUIRK(0x144d, 0xc504, "Samsung Q35", AD1986A_3STACK),
- SND_PCI_QUIRK(0x17aa, 0x1011, "Lenovo M55", AD1986A_LAPTOP),
- SND_PCI_QUIRK(0x17aa, 0x1017, "Lenovo A60", AD1986A_3STACK),
- SND_PCI_QUIRK(0x17aa, 0x2066, "Lenovo N100", AD1986A_LAPTOP_AUTOMUTE),
- SND_PCI_QUIRK(0x17c0, 0x2017, "Samsung M50", AD1986A_LAPTOP),
+static const struct snd_pci_quirk ad1986a_fixup_tbl[] = {
+ SND_PCI_QUIRK(0x103c, 0x30af, "HP B2800", AD1986A_FIXUP_LAPTOP_IMIC),
+ SND_PCI_QUIRK(0x1043, 0x1447, "ASUS A8JN", AD1986A_FIXUP_EAPD),
+ SND_PCI_QUIRK_MASK(0x1043, 0xff00, 0x8100, "ASUS P5", AD1986A_FIXUP_3STACK),
+ SND_PCI_QUIRK_MASK(0x1043, 0xff00, 0x8200, "ASUS M2", AD1986A_FIXUP_3STACK),
+ SND_PCI_QUIRK(0x10de, 0xcb84, "ASUS A8N-VM", AD1986A_FIXUP_3STACK),
+ SND_PCI_QUIRK(0x1179, 0xff40, "Toshiba Satellite L40", AD1986A_FIXUP_EAPD),
+ SND_PCI_QUIRK(0x144d, 0xc01e, "FSC V2060", AD1986A_FIXUP_LAPTOP),
+ SND_PCI_QUIRK_MASK(0x144d, 0xff00, 0xc000, "Samsung", AD1986A_FIXUP_SAMSUNG),
+ SND_PCI_QUIRK(0x144d, 0xc027, "Samsung Q1", AD1986A_FIXUP_ULTRA),
+ SND_PCI_QUIRK(0x17aa, 0x2066, "Lenovo N100", AD1986A_FIXUP_INV_JACK_DETECT),
+ SND_PCI_QUIRK(0x17aa, 0x1011, "Lenovo M55", AD1986A_FIXUP_3STACK),
+ SND_PCI_QUIRK(0x17aa, 0x1017, "Lenovo A60", AD1986A_FIXUP_3STACK),
{}
};
-#ifdef CONFIG_SND_HDA_POWER_SAVE
-static struct hda_amp_list ad1986a_loopbacks[] = {
- { 0x13, HDA_OUTPUT, 0 }, /* Mic */
- { 0x14, HDA_OUTPUT, 0 }, /* Phone */
- { 0x15, HDA_OUTPUT, 0 }, /* CD */
- { 0x16, HDA_OUTPUT, 0 }, /* Aux */
- { 0x17, HDA_OUTPUT, 0 }, /* Line */
- { } /* end */
+static const struct hda_model_fixup ad1986a_fixup_models[] = {
+ { .id = AD1986A_FIXUP_3STACK, .name = "3stack" },
+ { .id = AD1986A_FIXUP_LAPTOP, .name = "laptop" },
+ { .id = AD1986A_FIXUP_LAPTOP_IMIC, .name = "laptop-imic" },
+ { .id = AD1986A_FIXUP_LAPTOP_IMIC, .name = "laptop-eapd" }, /* alias */
+ {}
};
-#endif
-
-static int is_jack_available(struct hda_codec *codec, hda_nid_t nid)
-{
- unsigned int conf = snd_hda_codec_get_pincfg(codec, nid);
- return get_defcfg_connect(conf) != AC_JACK_PORT_NONE;
-}
+/*
+ */
static int patch_ad1986a(struct hda_codec *codec)
{
+ int err;
struct ad198x_spec *spec;
- int err, board_config;
+ static hda_nid_t preferred_pairs[] = {
+ 0x1a, 0x03,
+ 0x1b, 0x03,
+ 0x1c, 0x04,
+ 0x1d, 0x05,
+ 0x1e, 0x03,
+ 0
+ };
- spec = kzalloc(sizeof(*spec), GFP_KERNEL);
- if (spec == NULL)
- return -ENOMEM;
+ err = alloc_ad_spec(codec);
+ if (err < 0)
+ return err;
+ spec = codec->spec;
- codec->spec = spec;
+ /* AD1986A has the inverted EAPD implementation */
+ codec->inv_eapd = 1;
- err = snd_hda_attach_beep_device(codec, 0x19);
- if (err < 0) {
- ad198x_free(codec);
- return err;
- }
+ spec->gen.mixer_nid = 0x07;
+ spec->gen.beep_nid = 0x19;
set_beep_amp(spec, 0x18, 0, HDA_OUTPUT);
- spec->multiout.max_channels = 6;
- spec->multiout.num_dacs = ARRAY_SIZE(ad1986a_dac_nids);
- spec->multiout.dac_nids = ad1986a_dac_nids;
- spec->multiout.dig_out_nid = AD1986A_SPDIF_OUT;
- spec->num_adc_nids = 1;
- spec->adc_nids = ad1986a_adc_nids;
- spec->capsrc_nids = ad1986a_capsrc_nids;
- spec->input_mux = &ad1986a_capture_source;
- spec->num_mixers = 1;
- spec->mixers[0] = ad1986a_mixers;
- spec->num_init_verbs = 1;
- spec->init_verbs[0] = ad1986a_init_verbs;
-#ifdef CONFIG_SND_HDA_POWER_SAVE
- spec->loopback.amplist = ad1986a_loopbacks;
-#endif
- spec->vmaster_nid = 0x1b;
- spec->inv_eapd = 1; /* AD1986A has the inverted EAPD implementation */
-
- codec->patch_ops = ad198x_patch_ops;
-
- /* override some parameters */
- board_config = snd_hda_check_board_config(codec, AD1986A_MODELS,
- ad1986a_models,
- ad1986a_cfg_tbl);
- switch (board_config) {
- case AD1986A_3STACK:
- spec->num_mixers = 2;
- spec->mixers[1] = ad1986a_3st_mixers;
- spec->num_init_verbs = 2;
- spec->init_verbs[1] = ad1986a_ch2_init;
- spec->channel_mode = ad1986a_modes;
- spec->num_channel_mode = ARRAY_SIZE(ad1986a_modes);
- spec->need_dac_fix = 1;
- spec->multiout.max_channels = 2;
- spec->multiout.num_dacs = 1;
- break;
- case AD1986A_LAPTOP:
- spec->mixers[0] = ad1986a_laptop_mixers;
- spec->multiout.max_channels = 2;
- spec->multiout.num_dacs = 1;
- spec->multiout.dac_nids = ad1986a_laptop_dac_nids;
- break;
- case AD1986A_LAPTOP_EAPD:
- spec->num_mixers = 3;
- spec->mixers[0] = ad1986a_laptop_master_mixers;
- spec->mixers[1] = ad1986a_laptop_eapd_mixers;
- spec->mixers[2] = ad1986a_laptop_intmic_mixers;
- spec->num_init_verbs = 2;
- spec->init_verbs[1] = ad1986a_eapd_init_verbs;
- spec->multiout.max_channels = 2;
- spec->multiout.num_dacs = 1;
- spec->multiout.dac_nids = ad1986a_laptop_dac_nids;
- if (!is_jack_available(codec, 0x25))
- spec->multiout.dig_out_nid = 0;
- spec->input_mux = &ad1986a_laptop_eapd_capture_source;
- break;
- case AD1986A_SAMSUNG:
- spec->num_mixers = 2;
- spec->mixers[0] = ad1986a_laptop_master_mixers;
- spec->mixers[1] = ad1986a_laptop_eapd_mixers;
- spec->num_init_verbs = 3;
- spec->init_verbs[1] = ad1986a_eapd_init_verbs;
- spec->init_verbs[2] = ad1986a_automic_verbs;
- spec->multiout.max_channels = 2;
- spec->multiout.num_dacs = 1;
- spec->multiout.dac_nids = ad1986a_laptop_dac_nids;
- if (!is_jack_available(codec, 0x25))
- spec->multiout.dig_out_nid = 0;
- spec->input_mux = &ad1986a_automic_capture_source;
- codec->patch_ops.unsol_event = ad1986a_automic_unsol_event;
- codec->patch_ops.init = ad1986a_automic_init;
- break;
- case AD1986A_SAMSUNG_P50:
- spec->num_mixers = 2;
- spec->mixers[0] = ad1986a_automute_master_mixers;
- spec->mixers[1] = ad1986a_laptop_eapd_mixers;
- spec->num_init_verbs = 4;
- spec->init_verbs[1] = ad1986a_eapd_init_verbs;
- spec->init_verbs[2] = ad1986a_automic_verbs;
- spec->init_verbs[3] = ad1986a_hp_init_verbs;
- spec->multiout.max_channels = 2;
- spec->multiout.num_dacs = 1;
- spec->multiout.dac_nids = ad1986a_laptop_dac_nids;
- if (!is_jack_available(codec, 0x25))
- spec->multiout.dig_out_nid = 0;
- spec->input_mux = &ad1986a_automic_capture_source;
- codec->patch_ops.unsol_event = ad1986a_samsung_p50_unsol_event;
- codec->patch_ops.init = ad1986a_samsung_p50_init;
- break;
- case AD1986A_LAPTOP_AUTOMUTE:
- spec->num_mixers = 3;
- spec->mixers[0] = ad1986a_automute_master_mixers;
- spec->mixers[1] = ad1986a_laptop_eapd_mixers;
- spec->mixers[2] = ad1986a_laptop_intmic_mixers;
- spec->num_init_verbs = 3;
- spec->init_verbs[1] = ad1986a_eapd_init_verbs;
- spec->init_verbs[2] = ad1986a_hp_init_verbs;
- spec->multiout.max_channels = 2;
- spec->multiout.num_dacs = 1;
- spec->multiout.dac_nids = ad1986a_laptop_dac_nids;
- if (!is_jack_available(codec, 0x25))
- spec->multiout.dig_out_nid = 0;
- spec->input_mux = &ad1986a_laptop_eapd_capture_source;
- codec->patch_ops.unsol_event = ad1986a_hp_unsol_event;
- codec->patch_ops.init = ad1986a_hp_init;
- /* Lenovo N100 seems to report the reversed bit
- * for HP jack-sensing
- */
- spec->inv_jack_detect = 1;
- break;
- case AD1986A_ULTRA:
- spec->mixers[0] = ad1986a_laptop_eapd_mixers;
- spec->num_init_verbs = 2;
- spec->init_verbs[1] = ad1986a_ultra_init;
- spec->multiout.max_channels = 2;
- spec->multiout.num_dacs = 1;
- spec->multiout.dac_nids = ad1986a_laptop_dac_nids;
- spec->multiout.dig_out_nid = 0;
- break;
- }
-
/* AD1986A has a hardware problem that it can't share a stream
* with multiple output pins. The copy of front to surrounds
* causes noisy or silent outputs at a certain timing, e.g.
* changing the volume.
* So, let's disable the shared stream.
*/
- spec->multiout.no_share_stream = 1;
+ spec->gen.multiout.no_share_stream = 1;
+ /* give fixed DAC/pin pairs */
+ spec->gen.preferred_dacs = preferred_pairs;
- codec->no_trigger_sense = 1;
- codec->no_sticky_stream = 1;
+ /* AD1986A can't manage the dynamic pin on/off smoothly */
+ spec->gen.auto_mute_via_amp = 1;
+
+ snd_hda_pick_fixup(codec, ad1986a_fixup_models, ad1986a_fixup_tbl,
+ ad1986a_fixups);
+ snd_hda_apply_fixup(codec, HDA_FIXUP_ACT_PRE_PROBE);
+
+ err = ad198x_parse_auto_config(codec, false);
+ if (err < 0) {
+ snd_hda_gen_free(codec);
+ return err;
+ }
+
+ snd_hda_apply_fixup(codec, HDA_FIXUP_ACT_PROBE);
return 0;
}
+
/*
* AD1983 specific
*/
-#define AD1983_SPDIF_OUT 0x02
-#define AD1983_DAC 0x03
-#define AD1983_ADC 0x04
-
-static hda_nid_t ad1983_dac_nids[1] = { AD1983_DAC };
-static hda_nid_t ad1983_adc_nids[1] = { AD1983_ADC };
-static hda_nid_t ad1983_capsrc_nids[1] = { 0x15 };
-
-static struct hda_input_mux ad1983_capture_source = {
- .num_items = 4,
- .items = {
- { "Mic", 0x0 },
- { "Line", 0x1 },
- { "Mix", 0x2 },
- { "Mix Mono", 0x3 },
- },
-};
-
/*
- * SPDIF playback route
+ * SPDIF mux control for AD1983 auto-parser
*/
-static int ad1983_spdif_route_info(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *uinfo)
+static int ad1983_auto_smux_enum_info(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_info *uinfo)
{
- static char *texts[] = { "PCM", "ADC" };
-
- uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED;
- uinfo->count = 1;
- uinfo->value.enumerated.items = 2;
- if (uinfo->value.enumerated.item > 1)
- uinfo->value.enumerated.item = 1;
- strcpy(uinfo->value.enumerated.name, texts[uinfo->value.enumerated.item]);
- return 0;
+ struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
+ struct ad198x_spec *spec = codec->spec;
+ static const char * const texts2[] = { "PCM", "ADC" };
+ static const char * const texts3[] = { "PCM", "ADC1", "ADC2" };
+ hda_nid_t dig_out = spec->gen.multiout.dig_out_nid;
+ int num_conns = snd_hda_get_num_conns(codec, dig_out);
+
+ if (num_conns == 2)
+ return snd_hda_enum_helper_info(kcontrol, uinfo, 2, texts2);
+ else if (num_conns == 3)
+ return snd_hda_enum_helper_info(kcontrol, uinfo, 3, texts3);
+ else
+ return -EINVAL;
}
-static int ad1983_spdif_route_get(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
+static int ad1983_auto_smux_enum_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
{
struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
struct ad198x_spec *spec = codec->spec;
- ucontrol->value.enumerated.item[0] = spec->spdif_route;
+ ucontrol->value.enumerated.item[0] = spec->cur_smux;
return 0;
}
-static int ad1983_spdif_route_put(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
+static int ad1983_auto_smux_enum_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
{
struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
struct ad198x_spec *spec = codec->spec;
+ unsigned int val = ucontrol->value.enumerated.item[0];
+ hda_nid_t dig_out = spec->gen.multiout.dig_out_nid;
+ int num_conns = snd_hda_get_num_conns(codec, dig_out);
- if (ucontrol->value.enumerated.item[0] > 1)
+ if (val >= num_conns)
return -EINVAL;
- if (spec->spdif_route != ucontrol->value.enumerated.item[0]) {
- spec->spdif_route = ucontrol->value.enumerated.item[0];
- snd_hda_codec_write_cache(codec, spec->multiout.dig_out_nid, 0,
- AC_VERB_SET_CONNECT_SEL,
- spec->spdif_route);
- return 1;
- }
- return 0;
+ if (spec->cur_smux == val)
+ return 0;
+ spec->cur_smux = val;
+ snd_hda_codec_write_cache(codec, dig_out, 0,
+ AC_VERB_SET_CONNECT_SEL, val);
+ return 1;
}
-static struct snd_kcontrol_new ad1983_mixers[] = {
- HDA_CODEC_VOLUME("Front Playback Volume", 0x05, 0x0, HDA_OUTPUT),
- HDA_CODEC_MUTE("Front Playback Switch", 0x05, 0x0, HDA_OUTPUT),
- HDA_CODEC_VOLUME("Headphone Playback Volume", 0x06, 0x0, HDA_OUTPUT),
- HDA_CODEC_MUTE("Headphone Playback Switch", 0x06, 0x0, HDA_OUTPUT),
- HDA_CODEC_VOLUME_MONO("Mono Playback Volume", 0x07, 1, 0x0, HDA_OUTPUT),
- HDA_CODEC_MUTE_MONO("Mono Playback Switch", 0x07, 1, 0x0, HDA_OUTPUT),
- HDA_CODEC_VOLUME("PCM Playback Volume", 0x11, 0x0, HDA_OUTPUT),
- HDA_CODEC_MUTE("PCM Playback Switch", 0x11, 0x0, HDA_OUTPUT),
- HDA_CODEC_VOLUME("Mic Playback Volume", 0x12, 0x0, HDA_OUTPUT),
- HDA_CODEC_MUTE("Mic Playback Switch", 0x12, 0x0, HDA_OUTPUT),
- HDA_CODEC_VOLUME("Line Playback Volume", 0x13, 0x0, HDA_OUTPUT),
- HDA_CODEC_MUTE("Line Playback Switch", 0x13, 0x0, HDA_OUTPUT),
- HDA_CODEC_VOLUME("Mic Boost Volume", 0x0c, 0x0, HDA_OUTPUT),
- HDA_CODEC_VOLUME("Capture Volume", 0x15, 0x0, HDA_OUTPUT),
- HDA_CODEC_MUTE("Capture Switch", 0x15, 0x0, HDA_OUTPUT),
- {
- .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
- .name = "Capture Source",
- .info = ad198x_mux_enum_info,
- .get = ad198x_mux_enum_get,
- .put = ad198x_mux_enum_put,
- },
- {
- .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
- .name = SNDRV_CTL_NAME_IEC958("",PLAYBACK,NONE) "Source",
- .info = ad1983_spdif_route_info,
- .get = ad1983_spdif_route_get,
- .put = ad1983_spdif_route_put,
- },
- { } /* end */
+static struct snd_kcontrol_new ad1983_auto_smux_mixer = {
+ .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+ .name = "IEC958 Playback Source",
+ .info = ad1983_auto_smux_enum_info,
+ .get = ad1983_auto_smux_enum_get,
+ .put = ad1983_auto_smux_enum_put,
};
-static struct hda_verb ad1983_init_verbs[] = {
- /* Front, HP, Mono; mute as default */
- {0x05, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080},
- {0x06, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080},
- {0x07, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080},
- /* Beep, PCM, Mic, Line-In: mute */
- {0x10, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080},
- {0x11, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080},
- {0x12, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080},
- {0x13, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080},
- /* Front, HP selectors; from Mix */
- {0x05, AC_VERB_SET_CONNECT_SEL, 0x01},
- {0x06, AC_VERB_SET_CONNECT_SEL, 0x01},
- /* Mono selector; from Mix */
- {0x0b, AC_VERB_SET_CONNECT_SEL, 0x03},
- /* Mic selector; Mic */
- {0x0c, AC_VERB_SET_CONNECT_SEL, 0x0},
- /* Line-in selector: Line-in */
- {0x0d, AC_VERB_SET_CONNECT_SEL, 0x0},
- /* Mic boost: 0dB */
- {0x0c, AC_VERB_SET_AMP_GAIN_MUTE, 0xb000},
- /* Record selector: mic */
- {0x15, AC_VERB_SET_CONNECT_SEL, 0x0},
- {0x15, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080},
- /* SPDIF route: PCM */
- {0x02, AC_VERB_SET_CONNECT_SEL, 0x0},
- /* Front Pin */
- {0x05, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x40 },
- /* HP Pin */
- {0x06, AC_VERB_SET_PIN_WIDGET_CONTROL, 0xc0 },
- /* Mono Pin */
- {0x07, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x40 },
- /* Mic Pin */
- {0x08, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x24 },
- /* Line Pin */
- {0x09, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x20 },
- { } /* end */
-};
+static int ad1983_add_spdif_mux_ctl(struct hda_codec *codec)
+{
+ struct ad198x_spec *spec = codec->spec;
+ hda_nid_t dig_out = spec->gen.multiout.dig_out_nid;
+ int num_conns;
-#ifdef CONFIG_SND_HDA_POWER_SAVE
-static struct hda_amp_list ad1983_loopbacks[] = {
- { 0x12, HDA_OUTPUT, 0 }, /* Mic */
- { 0x13, HDA_OUTPUT, 0 }, /* Line */
- { } /* end */
-};
-#endif
+ if (!dig_out)
+ return 0;
+ num_conns = snd_hda_get_num_conns(codec, dig_out);
+ if (num_conns != 2 && num_conns != 3)
+ return 0;
+ if (!snd_hda_gen_add_kctl(&spec->gen, NULL, &ad1983_auto_smux_mixer))
+ return -ENOMEM;
+ return 0;
+}
static int patch_ad1983(struct hda_codec *codec)
{
struct ad198x_spec *spec;
+ static hda_nid_t conn_0c[] = { 0x08 };
+ static hda_nid_t conn_0d[] = { 0x09 };
int err;
- spec = kzalloc(sizeof(*spec), GFP_KERNEL);
- if (spec == NULL)
- return -ENOMEM;
-
- codec->spec = spec;
-
- err = snd_hda_attach_beep_device(codec, 0x10);
- if (err < 0) {
- ad198x_free(codec);
+ err = alloc_ad_spec(codec);
+ if (err < 0)
return err;
- }
- set_beep_amp(spec, 0x10, 0, HDA_OUTPUT);
-
- spec->multiout.max_channels = 2;
- spec->multiout.num_dacs = ARRAY_SIZE(ad1983_dac_nids);
- spec->multiout.dac_nids = ad1983_dac_nids;
- spec->multiout.dig_out_nid = AD1983_SPDIF_OUT;
- spec->num_adc_nids = 1;
- spec->adc_nids = ad1983_adc_nids;
- spec->capsrc_nids = ad1983_capsrc_nids;
- spec->input_mux = &ad1983_capture_source;
- spec->num_mixers = 1;
- spec->mixers[0] = ad1983_mixers;
- spec->num_init_verbs = 1;
- spec->init_verbs[0] = ad1983_init_verbs;
- spec->spdif_route = 0;
-#ifdef CONFIG_SND_HDA_POWER_SAVE
- spec->loopback.amplist = ad1983_loopbacks;
-#endif
- spec->vmaster_nid = 0x05;
+ spec = codec->spec;
- codec->patch_ops = ad198x_patch_ops;
+ spec->gen.mixer_nid = 0x0e;
+ spec->gen.beep_nid = 0x10;
+ set_beep_amp(spec, 0x10, 0, HDA_OUTPUT);
- codec->no_trigger_sense = 1;
- codec->no_sticky_stream = 1;
+ /* limit the loopback routes not to confuse the parser */
+ snd_hda_override_conn_list(codec, 0x0c, ARRAY_SIZE(conn_0c), conn_0c);
+ snd_hda_override_conn_list(codec, 0x0d, ARRAY_SIZE(conn_0d), conn_0d);
+ err = ad198x_parse_auto_config(codec, false);
+ if (err < 0)
+ goto error;
+ err = ad1983_add_spdif_mux_ctl(codec);
+ if (err < 0)
+ goto error;
return 0;
+
+ error:
+ snd_hda_gen_free(codec);
+ return err;
}
@@ -1514,454 +526,89 @@ static int patch_ad1983(struct hda_codec *codec)
* AD1981 HD specific
*/
-#define AD1981_SPDIF_OUT 0x02
-#define AD1981_DAC 0x03
-#define AD1981_ADC 0x04
-
-static hda_nid_t ad1981_dac_nids[1] = { AD1981_DAC };
-static hda_nid_t ad1981_adc_nids[1] = { AD1981_ADC };
-static hda_nid_t ad1981_capsrc_nids[1] = { 0x15 };
-
-/* 0x0c, 0x09, 0x0e, 0x0f, 0x19, 0x05, 0x18, 0x17 */
-static struct hda_input_mux ad1981_capture_source = {
- .num_items = 7,
- .items = {
- { "Front Mic", 0x0 },
- { "Line", 0x1 },
- { "Mix", 0x2 },
- { "Mix Mono", 0x3 },
- { "CD", 0x4 },
- { "Mic", 0x6 },
- { "Aux", 0x7 },
- },
-};
-
-static struct snd_kcontrol_new ad1981_mixers[] = {
- HDA_CODEC_VOLUME("Front Playback Volume", 0x05, 0x0, HDA_OUTPUT),
- HDA_CODEC_MUTE("Front Playback Switch", 0x05, 0x0, HDA_OUTPUT),
- HDA_CODEC_VOLUME("Headphone Playback Volume", 0x06, 0x0, HDA_OUTPUT),
- HDA_CODEC_MUTE("Headphone Playback Switch", 0x06, 0x0, HDA_OUTPUT),
- HDA_CODEC_VOLUME_MONO("Mono Playback Volume", 0x07, 1, 0x0, HDA_OUTPUT),
- HDA_CODEC_MUTE_MONO("Mono Playback Switch", 0x07, 1, 0x0, HDA_OUTPUT),
- HDA_CODEC_VOLUME("PCM Playback Volume", 0x11, 0x0, HDA_OUTPUT),
- HDA_CODEC_MUTE("PCM Playback Switch", 0x11, 0x0, HDA_OUTPUT),
- HDA_CODEC_VOLUME("Front Mic Playback Volume", 0x12, 0x0, HDA_OUTPUT),
- HDA_CODEC_MUTE("Front Mic Playback Switch", 0x12, 0x0, HDA_OUTPUT),
- HDA_CODEC_VOLUME("Line Playback Volume", 0x13, 0x0, HDA_OUTPUT),
- HDA_CODEC_MUTE("Line Playback Switch", 0x13, 0x0, HDA_OUTPUT),
- HDA_CODEC_VOLUME("Aux Playback Volume", 0x1b, 0x0, HDA_OUTPUT),
- HDA_CODEC_MUTE("Aux Playback Switch", 0x1b, 0x0, HDA_OUTPUT),
- HDA_CODEC_VOLUME("Mic Playback Volume", 0x1c, 0x0, HDA_OUTPUT),
- HDA_CODEC_MUTE("Mic Playback Switch", 0x1c, 0x0, HDA_OUTPUT),
- HDA_CODEC_VOLUME("CD Playback Volume", 0x1d, 0x0, HDA_OUTPUT),
- HDA_CODEC_MUTE("CD Playback Switch", 0x1d, 0x0, HDA_OUTPUT),
- HDA_CODEC_VOLUME("Front Mic Boost Volume", 0x08, 0x0, HDA_INPUT),
- HDA_CODEC_VOLUME("Mic Boost Volume", 0x18, 0x0, HDA_INPUT),
- HDA_CODEC_VOLUME("Capture Volume", 0x15, 0x0, HDA_OUTPUT),
- HDA_CODEC_MUTE("Capture Switch", 0x15, 0x0, HDA_OUTPUT),
- {
- .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
- .name = "Capture Source",
- .info = ad198x_mux_enum_info,
- .get = ad198x_mux_enum_get,
- .put = ad198x_mux_enum_put,
- },
- /* identical with AD1983 */
- {
- .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
- .name = SNDRV_CTL_NAME_IEC958("",PLAYBACK,NONE) "Source",
- .info = ad1983_spdif_route_info,
- .get = ad1983_spdif_route_get,
- .put = ad1983_spdif_route_put,
- },
- { } /* end */
-};
-
-static struct hda_verb ad1981_init_verbs[] = {
- /* Front, HP, Mono; mute as default */
- {0x05, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080},
- {0x06, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080},
- {0x07, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080},
- /* Beep, PCM, Front Mic, Line, Rear Mic, Aux, CD-In: mute */
- {0x0d, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080},
- {0x11, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080},
- {0x12, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080},
- {0x13, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080},
- {0x1b, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080},
- {0x1c, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080},
- {0x1d, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080},
- /* Front, HP selectors; from Mix */
- {0x05, AC_VERB_SET_CONNECT_SEL, 0x01},
- {0x06, AC_VERB_SET_CONNECT_SEL, 0x01},
- /* Mono selector; from Mix */
- {0x0b, AC_VERB_SET_CONNECT_SEL, 0x03},
- /* Mic Mixer; select Front Mic */
- {0x1e, AC_VERB_SET_AMP_GAIN_MUTE, 0xb000},
- {0x1f, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080},
- /* Mic boost: 0dB */
- {0x08, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
- {0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
- /* Record selector: Front mic */
- {0x15, AC_VERB_SET_CONNECT_SEL, 0x0},
- {0x15, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080},
- /* SPDIF route: PCM */
- {0x02, AC_VERB_SET_CONNECT_SEL, 0x0},
- /* Front Pin */
- {0x05, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x40 },
- /* HP Pin */
- {0x06, AC_VERB_SET_PIN_WIDGET_CONTROL, 0xc0 },
- /* Mono Pin */
- {0x07, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x40 },
- /* Front & Rear Mic Pins */
- {0x08, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x24 },
- {0x18, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x24 },
- /* Line Pin */
- {0x09, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x20 },
- /* Digital Beep */
- {0x0d, AC_VERB_SET_CONNECT_SEL, 0x00},
- /* Line-Out as Input: disabled */
- {0x1a, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080},
- { } /* end */
-};
-
-#ifdef CONFIG_SND_HDA_POWER_SAVE
-static struct hda_amp_list ad1981_loopbacks[] = {
- { 0x12, HDA_OUTPUT, 0 }, /* Front Mic */
- { 0x13, HDA_OUTPUT, 0 }, /* Line */
- { 0x1b, HDA_OUTPUT, 0 }, /* Aux */
- { 0x1c, HDA_OUTPUT, 0 }, /* Mic */
- { 0x1d, HDA_OUTPUT, 0 }, /* CD */
- { } /* end */
-};
-#endif
-
-/*
- * Patch for HP nx6320
- *
- * nx6320 uses EAPD in the reverse way - EAPD-on means the internal
- * speaker output enabled _and_ mute-LED off.
- */
-
-#define AD1981_HP_EVENT 0x37
-#define AD1981_MIC_EVENT 0x38
-
-static struct hda_verb ad1981_hp_init_verbs[] = {
- {0x05, AC_VERB_SET_EAPD_BTLENABLE, 0x00 }, /* default off */
- /* pin sensing on HP and Mic jacks */
- {0x06, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | AD1981_HP_EVENT},
- {0x08, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | AD1981_MIC_EVENT},
- {}
-};
-
-/* turn on/off EAPD (+ mute HP) as a master switch */
-static int ad1981_hp_master_sw_put(struct snd_kcontrol *kcontrol,
- struct snd_ctl_elem_value *ucontrol)
+static void ad1981_fixup_hp_eapd(struct hda_codec *codec,
+ const struct hda_fixup *fix, int action)
{
- struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
struct ad198x_spec *spec = codec->spec;
- if (! ad198x_eapd_put(kcontrol, ucontrol))
- return 0;
- /* change speaker pin appropriately */
- snd_hda_codec_write(codec, 0x05, 0,
- AC_VERB_SET_PIN_WIDGET_CONTROL,
- spec->cur_eapd ? PIN_OUT : 0);
- /* toggle HP mute appropriately */
- snd_hda_codec_amp_stereo(codec, 0x06, HDA_OUTPUT, 0,
- HDA_AMP_MUTE,
- spec->cur_eapd ? 0 : HDA_AMP_MUTE);
- return 1;
-}
-
-/* bind volumes of both NID 0x05 and 0x06 */
-static struct hda_bind_ctls ad1981_hp_bind_master_vol = {
- .ops = &snd_hda_bind_vol,
- .values = {
- HDA_COMPOSE_AMP_VAL(0x05, 3, 0, HDA_OUTPUT),
- HDA_COMPOSE_AMP_VAL(0x06, 3, 0, HDA_OUTPUT),
- 0
- },
-};
-
-/* mute internal speaker if HP is plugged */
-static void ad1981_hp_automute(struct hda_codec *codec)
-{
- unsigned int present;
-
- present = snd_hda_jack_detect(codec, 0x06);
- snd_hda_codec_amp_stereo(codec, 0x05, HDA_OUTPUT, 0,
- HDA_AMP_MUTE, present ? HDA_AMP_MUTE : 0);
-}
-
-/* toggle input of built-in and mic jack appropriately */
-static void ad1981_hp_automic(struct hda_codec *codec)
-{
- static struct hda_verb mic_jack_on[] = {
- {0x1f, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080},
- {0x1e, AC_VERB_SET_AMP_GAIN_MUTE, 0xb000},
- {}
- };
- static struct hda_verb mic_jack_off[] = {
- {0x1e, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080},
- {0x1f, AC_VERB_SET_AMP_GAIN_MUTE, 0xb000},
- {}
- };
- unsigned int present;
-
- present = snd_hda_jack_detect(codec, 0x08);
- if (present)
- snd_hda_sequence_write(codec, mic_jack_on);
- else
- snd_hda_sequence_write(codec, mic_jack_off);
-}
-
-/* unsolicited event for HP jack sensing */
-static void ad1981_hp_unsol_event(struct hda_codec *codec,
- unsigned int res)
-{
- res >>= 26;
- switch (res) {
- case AD1981_HP_EVENT:
- ad1981_hp_automute(codec);
- break;
- case AD1981_MIC_EVENT:
- ad1981_hp_automic(codec);
- break;
+ if (action == HDA_FIXUP_ACT_PRE_PROBE) {
+ spec->gen.vmaster_mute.hook = ad_vmaster_eapd_hook;
+ spec->eapd_nid = 0x05;
}
}
-static struct hda_input_mux ad1981_hp_capture_source = {
- .num_items = 3,
- .items = {
- { "Mic", 0x0 },
- { "Docking-Station", 0x1 },
- { "Mix", 0x2 },
- },
-};
-
-static struct snd_kcontrol_new ad1981_hp_mixers[] = {
- HDA_BIND_VOL("Master Playback Volume", &ad1981_hp_bind_master_vol),
- {
- .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
- .subdevice = HDA_SUBDEV_NID_FLAG | 0x05,
- .name = "Master Playback Switch",
- .info = ad198x_eapd_info,
- .get = ad198x_eapd_get,
- .put = ad1981_hp_master_sw_put,
- .private_value = 0x05,
- },
- HDA_CODEC_VOLUME("PCM Playback Volume", 0x11, 0x0, HDA_OUTPUT),
- HDA_CODEC_MUTE("PCM Playback Switch", 0x11, 0x0, HDA_OUTPUT),
-#if 0
- /* FIXME: analog mic/line loopback doesn't work with my tests...
- * (although recording is OK)
- */
- HDA_CODEC_VOLUME("Mic Playback Volume", 0x12, 0x0, HDA_OUTPUT),
- HDA_CODEC_MUTE("Mic Playback Switch", 0x12, 0x0, HDA_OUTPUT),
- HDA_CODEC_VOLUME("Docking-Station Playback Volume", 0x13, 0x0, HDA_OUTPUT),
- HDA_CODEC_MUTE("Docking-Station Playback Switch", 0x13, 0x0, HDA_OUTPUT),
- HDA_CODEC_VOLUME("Internal Mic Playback Volume", 0x1c, 0x0, HDA_OUTPUT),
- HDA_CODEC_MUTE("Internal Mic Playback Switch", 0x1c, 0x0, HDA_OUTPUT),
- /* FIXME: does this laptop have analog CD connection? */
- HDA_CODEC_VOLUME("CD Playback Volume", 0x1d, 0x0, HDA_OUTPUT),
- HDA_CODEC_MUTE("CD Playback Switch", 0x1d, 0x0, HDA_OUTPUT),
-#endif
- HDA_CODEC_VOLUME("Mic Boost Volume", 0x08, 0x0, HDA_INPUT),
- HDA_CODEC_VOLUME("Internal Mic Boost Volume", 0x18, 0x0, HDA_INPUT),
- HDA_CODEC_VOLUME("Capture Volume", 0x15, 0x0, HDA_OUTPUT),
- HDA_CODEC_MUTE("Capture Switch", 0x15, 0x0, HDA_OUTPUT),
- {
- .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
- .name = "Capture Source",
- .info = ad198x_mux_enum_info,
- .get = ad198x_mux_enum_get,
- .put = ad198x_mux_enum_put,
- },
- { } /* end */
-};
-
-/* initialize jack-sensing, too */
-static int ad1981_hp_init(struct hda_codec *codec)
+/* set the upper-limit for mixer amp to 0dB for avoiding the possible
+ * damage by overloading
+ */
+static void ad1981_fixup_amp_override(struct hda_codec *codec,
+ const struct hda_fixup *fix, int action)
{
- ad198x_init(codec);
- ad1981_hp_automute(codec);
- ad1981_hp_automic(codec);
- return 0;
+ if (action == HDA_FIXUP_ACT_PRE_PROBE)
+ snd_hda_override_amp_caps(codec, 0x11, HDA_INPUT,
+ (0x17 << AC_AMPCAP_OFFSET_SHIFT) |
+ (0x17 << AC_AMPCAP_NUM_STEPS_SHIFT) |
+ (0x05 << AC_AMPCAP_STEP_SIZE_SHIFT) |
+ (1 << AC_AMPCAP_MUTE_SHIFT));
}
-/* configuration for Toshiba Laptops */
-static struct hda_verb ad1981_toshiba_init_verbs[] = {
- {0x05, AC_VERB_SET_EAPD_BTLENABLE, 0x01 }, /* default on */
- /* pin sensing on HP and Mic jacks */
- {0x06, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | AD1981_HP_EVENT},
- {0x08, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | AD1981_MIC_EVENT},
- {}
-};
-
-static struct snd_kcontrol_new ad1981_toshiba_mixers[] = {
- HDA_CODEC_VOLUME("Amp Volume", 0x1a, 0x0, HDA_OUTPUT),
- HDA_CODEC_MUTE("Amp Switch", 0x1a, 0x0, HDA_OUTPUT),
- { }
+enum {
+ AD1981_FIXUP_AMP_OVERRIDE,
+ AD1981_FIXUP_HP_EAPD,
};
-/* configuration for Lenovo Thinkpad T60 */
-static struct snd_kcontrol_new ad1981_thinkpad_mixers[] = {
- HDA_CODEC_VOLUME("Master Playback Volume", 0x05, 0x0, HDA_OUTPUT),
- HDA_CODEC_MUTE("Master Playback Switch", 0x05, 0x0, HDA_OUTPUT),
- HDA_CODEC_VOLUME("PCM Playback Volume", 0x11, 0x0, HDA_OUTPUT),
- HDA_CODEC_MUTE("PCM Playback Switch", 0x11, 0x0, HDA_OUTPUT),
- HDA_CODEC_VOLUME("Mic Playback Volume", 0x12, 0x0, HDA_OUTPUT),
- HDA_CODEC_MUTE("Mic Playback Switch", 0x12, 0x0, HDA_OUTPUT),
- HDA_CODEC_VOLUME("CD Playback Volume", 0x1d, 0x0, HDA_OUTPUT),
- HDA_CODEC_MUTE("CD Playback Switch", 0x1d, 0x0, HDA_OUTPUT),
- HDA_CODEC_VOLUME("Mic Boost Volume", 0x08, 0x0, HDA_INPUT),
- HDA_CODEC_VOLUME("Capture Volume", 0x15, 0x0, HDA_OUTPUT),
- HDA_CODEC_MUTE("Capture Switch", 0x15, 0x0, HDA_OUTPUT),
- {
- .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
- .name = "Capture Source",
- .info = ad198x_mux_enum_info,
- .get = ad198x_mux_enum_get,
- .put = ad198x_mux_enum_put,
+static const struct hda_fixup ad1981_fixups[] = {
+ [AD1981_FIXUP_AMP_OVERRIDE] = {
+ .type = HDA_FIXUP_FUNC,
+ .v.func = ad1981_fixup_amp_override,
},
- /* identical with AD1983 */
- {
- .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
- .name = SNDRV_CTL_NAME_IEC958("",PLAYBACK,NONE) "Source",
- .info = ad1983_spdif_route_info,
- .get = ad1983_spdif_route_get,
- .put = ad1983_spdif_route_put,
+ [AD1981_FIXUP_HP_EAPD] = {
+ .type = HDA_FIXUP_FUNC,
+ .v.func = ad1981_fixup_hp_eapd,
+ .chained = true,
+ .chain_id = AD1981_FIXUP_AMP_OVERRIDE,
},
- { } /* end */
-};
-
-static struct hda_input_mux ad1981_thinkpad_capture_source = {
- .num_items = 3,
- .items = {
- { "Mic", 0x0 },
- { "Mix", 0x2 },
- { "CD", 0x4 },
- },
-};
-
-/* models */
-enum {
- AD1981_BASIC,
- AD1981_HP,
- AD1981_THINKPAD,
- AD1981_TOSHIBA,
- AD1981_MODELS
};
-static const char * const ad1981_models[AD1981_MODELS] = {
- [AD1981_HP] = "hp",
- [AD1981_THINKPAD] = "thinkpad",
- [AD1981_BASIC] = "basic",
- [AD1981_TOSHIBA] = "toshiba"
-};
-
-static struct snd_pci_quirk ad1981_cfg_tbl[] = {
- SND_PCI_QUIRK(0x1014, 0x0597, "Lenovo Z60", AD1981_THINKPAD),
- SND_PCI_QUIRK(0x1014, 0x05b7, "Lenovo Z60m", AD1981_THINKPAD),
- /* All HP models */
- SND_PCI_QUIRK_VENDOR(0x103c, "HP nx", AD1981_HP),
- SND_PCI_QUIRK(0x1179, 0x0001, "Toshiba U205", AD1981_TOSHIBA),
- /* Lenovo Thinkpad T60/X60/Z6xx */
- SND_PCI_QUIRK_VENDOR(0x17aa, "Lenovo Thinkpad", AD1981_THINKPAD),
+static const struct snd_pci_quirk ad1981_fixup_tbl[] = {
+ SND_PCI_QUIRK_VENDOR(0x1014, "Lenovo", AD1981_FIXUP_AMP_OVERRIDE),
+ SND_PCI_QUIRK_VENDOR(0x103c, "HP", AD1981_FIXUP_HP_EAPD),
+ SND_PCI_QUIRK_VENDOR(0x17aa, "Lenovo", AD1981_FIXUP_AMP_OVERRIDE),
/* HP nx6320 (reversed SSID, H/W bug) */
- SND_PCI_QUIRK(0x30b0, 0x103c, "HP nx6320", AD1981_HP),
+ SND_PCI_QUIRK(0x30b0, 0x103c, "HP nx6320", AD1981_FIXUP_HP_EAPD),
{}
};
static int patch_ad1981(struct hda_codec *codec)
{
struct ad198x_spec *spec;
- int err, board_config;
+ int err;
- spec = kzalloc(sizeof(*spec), GFP_KERNEL);
- if (spec == NULL)
+ err = alloc_ad_spec(codec);
+ if (err < 0)
return -ENOMEM;
+ spec = codec->spec;
- codec->spec = spec;
-
- err = snd_hda_attach_beep_device(codec, 0x10);
- if (err < 0) {
- ad198x_free(codec);
- return err;
- }
+ spec->gen.mixer_nid = 0x0e;
+ spec->gen.beep_nid = 0x10;
set_beep_amp(spec, 0x0d, 0, HDA_OUTPUT);
- spec->multiout.max_channels = 2;
- spec->multiout.num_dacs = ARRAY_SIZE(ad1981_dac_nids);
- spec->multiout.dac_nids = ad1981_dac_nids;
- spec->multiout.dig_out_nid = AD1981_SPDIF_OUT;
- spec->num_adc_nids = 1;
- spec->adc_nids = ad1981_adc_nids;
- spec->capsrc_nids = ad1981_capsrc_nids;
- spec->input_mux = &ad1981_capture_source;
- spec->num_mixers = 1;
- spec->mixers[0] = ad1981_mixers;
- spec->num_init_verbs = 1;
- spec->init_verbs[0] = ad1981_init_verbs;
- spec->spdif_route = 0;
-#ifdef CONFIG_SND_HDA_POWER_SAVE
- spec->loopback.amplist = ad1981_loopbacks;
-#endif
- spec->vmaster_nid = 0x05;
-
- codec->patch_ops = ad198x_patch_ops;
-
- /* override some parameters */
- board_config = snd_hda_check_board_config(codec, AD1981_MODELS,
- ad1981_models,
- ad1981_cfg_tbl);
- switch (board_config) {
- case AD1981_HP:
- spec->mixers[0] = ad1981_hp_mixers;
- spec->num_init_verbs = 2;
- spec->init_verbs[1] = ad1981_hp_init_verbs;
- spec->multiout.dig_out_nid = 0;
- spec->input_mux = &ad1981_hp_capture_source;
-
- codec->patch_ops.init = ad1981_hp_init;
- codec->patch_ops.unsol_event = ad1981_hp_unsol_event;
- /* set the upper-limit for mixer amp to 0dB for avoiding the
- * possible damage by overloading
- */
- snd_hda_override_amp_caps(codec, 0x11, HDA_INPUT,
- (0x17 << AC_AMPCAP_OFFSET_SHIFT) |
- (0x17 << AC_AMPCAP_NUM_STEPS_SHIFT) |
- (0x05 << AC_AMPCAP_STEP_SIZE_SHIFT) |
- (1 << AC_AMPCAP_MUTE_SHIFT));
- break;
- case AD1981_THINKPAD:
- spec->mixers[0] = ad1981_thinkpad_mixers;
- spec->input_mux = &ad1981_thinkpad_capture_source;
- /* set the upper-limit for mixer amp to 0dB for avoiding the
- * possible damage by overloading
- */
- snd_hda_override_amp_caps(codec, 0x11, HDA_INPUT,
- (0x17 << AC_AMPCAP_OFFSET_SHIFT) |
- (0x17 << AC_AMPCAP_NUM_STEPS_SHIFT) |
- (0x05 << AC_AMPCAP_STEP_SIZE_SHIFT) |
- (1 << AC_AMPCAP_MUTE_SHIFT));
- break;
- case AD1981_TOSHIBA:
- spec->mixers[0] = ad1981_hp_mixers;
- spec->mixers[1] = ad1981_toshiba_mixers;
- spec->num_init_verbs = 2;
- spec->init_verbs[1] = ad1981_toshiba_init_verbs;
- spec->multiout.dig_out_nid = 0;
- spec->input_mux = &ad1981_hp_capture_source;
- codec->patch_ops.init = ad1981_hp_init;
- codec->patch_ops.unsol_event = ad1981_hp_unsol_event;
- break;
- }
+ snd_hda_pick_fixup(codec, NULL, ad1981_fixup_tbl, ad1981_fixups);
+ snd_hda_apply_fixup(codec, HDA_FIXUP_ACT_PRE_PROBE);
- codec->no_trigger_sense = 1;
- codec->no_sticky_stream = 1;
+ err = ad198x_parse_auto_config(codec, false);
+ if (err < 0)
+ goto error;
+ err = ad1983_add_spdif_mux_ctl(codec);
+ if (err < 0)
+ goto error;
+
+ snd_hda_apply_fixup(codec, HDA_FIXUP_ACT_PROBE);
return 0;
+
+ error:
+ snd_hda_gen_free(codec);
+ return err;
}
@@ -2050,90 +697,7 @@ static int patch_ad1981(struct hda_codec *codec)
* E/F quad mic array
*/
-
-/* models */
-enum {
- AD1988_6STACK,
- AD1988_6STACK_DIG,
- AD1988_6STACK_DIG_FP,
- AD1988_3STACK,
- AD1988_3STACK_DIG,
- AD1988_LAPTOP,
- AD1988_LAPTOP_DIG,
- AD1988_AUTO,
- AD1988_MODEL_LAST,
-};
-
-/* reivision id to check workarounds */
-#define AD1988A_REV2 0x100200
-
-#define is_rev2(codec) \
- ((codec)->vendor_id == 0x11d41988 && \
- (codec)->revision_id == AD1988A_REV2)
-
-/*
- * mixers
- */
-
-static hda_nid_t ad1988_6stack_dac_nids[4] = {
- 0x04, 0x06, 0x05, 0x0a
-};
-
-static hda_nid_t ad1988_3stack_dac_nids[3] = {
- 0x04, 0x05, 0x0a
-};
-
-/* for AD1988A revision-2, DAC2-4 are swapped */
-static hda_nid_t ad1988_6stack_dac_nids_rev2[4] = {
- 0x04, 0x05, 0x0a, 0x06
-};
-
-static hda_nid_t ad1988_alt_dac_nid[1] = {
- 0x03
-};
-
-static hda_nid_t ad1988_3stack_dac_nids_rev2[3] = {
- 0x04, 0x0a, 0x06
-};
-
-static hda_nid_t ad1988_adc_nids[3] = {
- 0x08, 0x09, 0x0f
-};
-
-static hda_nid_t ad1988_capsrc_nids[3] = {
- 0x0c, 0x0d, 0x0e
-};
-
-#define AD1988_SPDIF_OUT 0x02
-#define AD1988_SPDIF_OUT_HDMI 0x0b
-#define AD1988_SPDIF_IN 0x07
-
-static hda_nid_t ad1989b_slave_dig_outs[] = {
- AD1988_SPDIF_OUT, AD1988_SPDIF_OUT_HDMI, 0
-};
-
-static struct hda_input_mux ad1988_6stack_capture_source = {
- .num_items = 5,
- .items = {
- { "Front Mic", 0x1 }, /* port-B */
- { "Line", 0x2 }, /* port-C */
- { "Mic", 0x4 }, /* port-E */
- { "CD", 0x5 },
- { "Mix", 0x9 },
- },
-};
-
-static struct hda_input_mux ad1988_laptop_capture_source = {
- .num_items = 3,
- .items = {
- { "Mic/Line", 0x1 }, /* port-B */
- { "CD", 0x5 },
- { "Mix", 0x9 },
- },
-};
-
-/*
- */
+#ifdef ENABLE_AD_STATIC_QUIRKS
static int ad198x_ch_mode_info(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_info *uinfo)
{
@@ -2164,1163 +728,213 @@ static int ad198x_ch_mode_put(struct snd_kcontrol *kcontrol,
spec->multiout.num_dacs = spec->multiout.max_channels / 2;
return err;
}
+#endif /* ENABLE_AD_STATIC_QUIRKS */
-/* 6-stack mode */
-static struct snd_kcontrol_new ad1988_6stack_mixers1[] = {
- HDA_CODEC_VOLUME("Front Playback Volume", 0x04, 0x0, HDA_OUTPUT),
- HDA_CODEC_VOLUME("Surround Playback Volume", 0x06, 0x0, HDA_OUTPUT),
- HDA_CODEC_VOLUME_MONO("Center Playback Volume", 0x05, 1, 0x0, HDA_OUTPUT),
- HDA_CODEC_VOLUME_MONO("LFE Playback Volume", 0x05, 2, 0x0, HDA_OUTPUT),
- HDA_CODEC_VOLUME("Side Playback Volume", 0x0a, 0x0, HDA_OUTPUT),
- { } /* end */
-};
-
-static struct snd_kcontrol_new ad1988_6stack_mixers1_rev2[] = {
- HDA_CODEC_VOLUME("Front Playback Volume", 0x04, 0x0, HDA_OUTPUT),
- HDA_CODEC_VOLUME("Surround Playback Volume", 0x05, 0x0, HDA_OUTPUT),
- HDA_CODEC_VOLUME_MONO("Center Playback Volume", 0x0a, 1, 0x0, HDA_OUTPUT),
- HDA_CODEC_VOLUME_MONO("LFE Playback Volume", 0x0a, 2, 0x0, HDA_OUTPUT),
- HDA_CODEC_VOLUME("Side Playback Volume", 0x06, 0x0, HDA_OUTPUT),
- { } /* end */
-};
-
-static struct snd_kcontrol_new ad1988_6stack_mixers2[] = {
- HDA_BIND_MUTE("Front Playback Switch", 0x29, 2, HDA_INPUT),
- HDA_BIND_MUTE("Surround Playback Switch", 0x2a, 2, HDA_INPUT),
- HDA_BIND_MUTE_MONO("Center Playback Switch", 0x27, 1, 2, HDA_INPUT),
- HDA_BIND_MUTE_MONO("LFE Playback Switch", 0x27, 2, 2, HDA_INPUT),
- HDA_BIND_MUTE("Side Playback Switch", 0x28, 2, HDA_INPUT),
- HDA_BIND_MUTE("Headphone Playback Switch", 0x22, 2, HDA_INPUT),
- HDA_BIND_MUTE("Mono Playback Switch", 0x1e, 2, HDA_INPUT),
-
- HDA_CODEC_VOLUME("CD Playback Volume", 0x20, 0x6, HDA_INPUT),
- HDA_CODEC_MUTE("CD Playback Switch", 0x20, 0x6, HDA_INPUT),
- HDA_CODEC_VOLUME("Front Mic Playback Volume", 0x20, 0x0, HDA_INPUT),
- HDA_CODEC_MUTE("Front Mic Playback Switch", 0x20, 0x0, HDA_INPUT),
- HDA_CODEC_VOLUME("Line Playback Volume", 0x20, 0x1, HDA_INPUT),
- HDA_CODEC_MUTE("Line Playback Switch", 0x20, 0x1, HDA_INPUT),
- HDA_CODEC_VOLUME("Mic Playback Volume", 0x20, 0x4, HDA_INPUT),
- HDA_CODEC_MUTE("Mic Playback Switch", 0x20, 0x4, HDA_INPUT),
-
- HDA_CODEC_VOLUME("Analog Mix Playback Volume", 0x21, 0x0, HDA_OUTPUT),
- HDA_CODEC_MUTE("Analog Mix Playback Switch", 0x21, 0x0, HDA_OUTPUT),
-
- HDA_CODEC_VOLUME("Front Mic Boost Volume", 0x39, 0x0, HDA_OUTPUT),
- HDA_CODEC_VOLUME("Mic Boost Volume", 0x3c, 0x0, HDA_OUTPUT),
-
- { } /* end */
-};
-
-static struct snd_kcontrol_new ad1988_6stack_fp_mixers[] = {
- HDA_CODEC_VOLUME("Headphone Playback Volume", 0x03, 0x0, HDA_OUTPUT),
-
- { } /* end */
-};
-
-/* 3-stack mode */
-static struct snd_kcontrol_new ad1988_3stack_mixers1[] = {
- HDA_CODEC_VOLUME("Front Playback Volume", 0x04, 0x0, HDA_OUTPUT),
- HDA_CODEC_VOLUME("Surround Playback Volume", 0x0a, 0x0, HDA_OUTPUT),
- HDA_CODEC_VOLUME_MONO("Center Playback Volume", 0x05, 1, 0x0, HDA_OUTPUT),
- HDA_CODEC_VOLUME_MONO("LFE Playback Volume", 0x05, 2, 0x0, HDA_OUTPUT),
- { } /* end */
-};
-
-static struct snd_kcontrol_new ad1988_3stack_mixers1_rev2[] = {
- HDA_CODEC_VOLUME("Front Playback Volume", 0x04, 0x0, HDA_OUTPUT),
- HDA_CODEC_VOLUME("Surround Playback Volume", 0x0a, 0x0, HDA_OUTPUT),
- HDA_CODEC_VOLUME_MONO("Center Playback Volume", 0x06, 1, 0x0, HDA_OUTPUT),
- HDA_CODEC_VOLUME_MONO("LFE Playback Volume", 0x06, 2, 0x0, HDA_OUTPUT),
- { } /* end */
-};
-
-static struct snd_kcontrol_new ad1988_3stack_mixers2[] = {
- HDA_BIND_MUTE("Front Playback Switch", 0x29, 2, HDA_INPUT),
- HDA_BIND_MUTE("Surround Playback Switch", 0x2c, 2, HDA_INPUT),
- HDA_BIND_MUTE_MONO("Center Playback Switch", 0x26, 1, 2, HDA_INPUT),
- HDA_BIND_MUTE_MONO("LFE Playback Switch", 0x26, 2, 2, HDA_INPUT),
- HDA_BIND_MUTE("Headphone Playback Switch", 0x22, 2, HDA_INPUT),
- HDA_BIND_MUTE("Mono Playback Switch", 0x1e, 2, HDA_INPUT),
-
- HDA_CODEC_VOLUME("CD Playback Volume", 0x20, 0x6, HDA_INPUT),
- HDA_CODEC_MUTE("CD Playback Switch", 0x20, 0x6, HDA_INPUT),
- HDA_CODEC_VOLUME("Front Mic Playback Volume", 0x20, 0x0, HDA_INPUT),
- HDA_CODEC_MUTE("Front Mic Playback Switch", 0x20, 0x0, HDA_INPUT),
- HDA_CODEC_VOLUME("Line Playback Volume", 0x20, 0x1, HDA_INPUT),
- HDA_CODEC_MUTE("Line Playback Switch", 0x20, 0x1, HDA_INPUT),
- HDA_CODEC_VOLUME("Mic Playback Volume", 0x20, 0x4, HDA_INPUT),
- HDA_CODEC_MUTE("Mic Playback Switch", 0x20, 0x4, HDA_INPUT),
-
- HDA_CODEC_VOLUME("Analog Mix Playback Volume", 0x21, 0x0, HDA_OUTPUT),
- HDA_CODEC_MUTE("Analog Mix Playback Switch", 0x21, 0x0, HDA_OUTPUT),
-
- HDA_CODEC_VOLUME("Front Mic Boost Volume", 0x39, 0x0, HDA_OUTPUT),
- HDA_CODEC_VOLUME("Mic Boost Volume", 0x3c, 0x0, HDA_OUTPUT),
- {
- .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
- .name = "Channel Mode",
- .info = ad198x_ch_mode_info,
- .get = ad198x_ch_mode_get,
- .put = ad198x_ch_mode_put,
- },
-
- { } /* end */
-};
-
-/* laptop mode */
-static struct snd_kcontrol_new ad1988_laptop_mixers[] = {
- HDA_CODEC_VOLUME("PCM Playback Volume", 0x04, 0x0, HDA_OUTPUT),
- HDA_CODEC_MUTE("PCM Playback Switch", 0x29, 0x0, HDA_INPUT),
- HDA_BIND_MUTE("Mono Playback Switch", 0x1e, 2, HDA_INPUT),
-
- HDA_CODEC_VOLUME("CD Playback Volume", 0x20, 0x6, HDA_INPUT),
- HDA_CODEC_MUTE("CD Playback Switch", 0x20, 0x6, HDA_INPUT),
- HDA_CODEC_VOLUME("Mic Playback Volume", 0x20, 0x0, HDA_INPUT),
- HDA_CODEC_MUTE("Mic Playback Switch", 0x20, 0x0, HDA_INPUT),
- HDA_CODEC_VOLUME("Line Playback Volume", 0x20, 0x1, HDA_INPUT),
- HDA_CODEC_MUTE("Line Playback Switch", 0x20, 0x1, HDA_INPUT),
-
- HDA_CODEC_VOLUME("Analog Mix Playback Volume", 0x21, 0x0, HDA_OUTPUT),
- HDA_CODEC_MUTE("Analog Mix Playback Switch", 0x21, 0x0, HDA_OUTPUT),
-
- HDA_CODEC_VOLUME("Mic Boost Volume", 0x39, 0x0, HDA_OUTPUT),
-
- {
- .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
- .name = "External Amplifier",
- .subdevice = HDA_SUBDEV_NID_FLAG | 0x12,
- .info = ad198x_eapd_info,
- .get = ad198x_eapd_get,
- .put = ad198x_eapd_put,
- .private_value = 0x12, /* port-D */
- },
-
- { } /* end */
-};
-
-/* capture */
-static struct snd_kcontrol_new ad1988_capture_mixers[] = {
- HDA_CODEC_VOLUME("Capture Volume", 0x0c, 0x0, HDA_OUTPUT),
- HDA_CODEC_MUTE("Capture Switch", 0x0c, 0x0, HDA_OUTPUT),
- HDA_CODEC_VOLUME_IDX("Capture Volume", 1, 0x0d, 0x0, HDA_OUTPUT),
- HDA_CODEC_MUTE_IDX("Capture Switch", 1, 0x0d, 0x0, HDA_OUTPUT),
- HDA_CODEC_VOLUME_IDX("Capture Volume", 2, 0x0e, 0x0, HDA_OUTPUT),
- HDA_CODEC_MUTE_IDX("Capture Switch", 2, 0x0e, 0x0, HDA_OUTPUT),
- {
- .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
- /* The multiple "Capture Source" controls confuse alsamixer
- * So call somewhat different..
- */
- /* .name = "Capture Source", */
- .name = "Input Source",
- .count = 3,
- .info = ad198x_mux_enum_info,
- .get = ad198x_mux_enum_get,
- .put = ad198x_mux_enum_put,
- },
- { } /* end */
-};
-
-static int ad1988_spdif_playback_source_info(struct snd_kcontrol *kcontrol,
- struct snd_ctl_elem_info *uinfo)
+static int ad1988_auto_smux_enum_info(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_info *uinfo)
{
- static char *texts[] = {
- "PCM", "ADC1", "ADC2", "ADC3"
+ struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
+ static const char * const texts[] = {
+ "PCM", "ADC1", "ADC2", "ADC3",
};
- uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED;
- uinfo->count = 1;
- uinfo->value.enumerated.items = 4;
- if (uinfo->value.enumerated.item >= 4)
- uinfo->value.enumerated.item = 3;
- strcpy(uinfo->value.enumerated.name, texts[uinfo->value.enumerated.item]);
- return 0;
+ int num_conns = snd_hda_get_num_conns(codec, 0x0b) + 1;
+ if (num_conns > 4)
+ num_conns = 4;
+ return snd_hda_enum_helper_info(kcontrol, uinfo, num_conns, texts);
}
-static int ad1988_spdif_playback_source_get(struct snd_kcontrol *kcontrol,
- struct snd_ctl_elem_value *ucontrol)
+static int ad1988_auto_smux_enum_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
{
struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
- unsigned int sel;
-
- sel = snd_hda_codec_read(codec, 0x1d, 0, AC_VERB_GET_AMP_GAIN_MUTE,
- AC_AMP_GET_INPUT);
- if (!(sel & 0x80))
- ucontrol->value.enumerated.item[0] = 0;
- else {
- sel = snd_hda_codec_read(codec, 0x0b, 0,
- AC_VERB_GET_CONNECT_SEL, 0);
- if (sel < 3)
- sel++;
- else
- sel = 0;
- ucontrol->value.enumerated.item[0] = sel;
- }
+ struct ad198x_spec *spec = codec->spec;
+
+ ucontrol->value.enumerated.item[0] = spec->cur_smux;
return 0;
}
-static int ad1988_spdif_playback_source_put(struct snd_kcontrol *kcontrol,
- struct snd_ctl_elem_value *ucontrol)
+static int ad1988_auto_smux_enum_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
{
struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
- unsigned int val, sel;
- int change;
+ struct ad198x_spec *spec = codec->spec;
+ unsigned int val = ucontrol->value.enumerated.item[0];
+ struct nid_path *path;
+ int num_conns = snd_hda_get_num_conns(codec, 0x0b) + 1;
- val = ucontrol->value.enumerated.item[0];
- if (val > 3)
+ if (val >= num_conns)
return -EINVAL;
- if (!val) {
- sel = snd_hda_codec_read(codec, 0x1d, 0,
- AC_VERB_GET_AMP_GAIN_MUTE,
- AC_AMP_GET_INPUT);
- change = sel & 0x80;
- if (change) {
- snd_hda_codec_write_cache(codec, 0x1d, 0,
- AC_VERB_SET_AMP_GAIN_MUTE,
- AMP_IN_UNMUTE(0));
- snd_hda_codec_write_cache(codec, 0x1d, 0,
- AC_VERB_SET_AMP_GAIN_MUTE,
- AMP_IN_MUTE(1));
- }
- } else {
- sel = snd_hda_codec_read(codec, 0x1d, 0,
- AC_VERB_GET_AMP_GAIN_MUTE,
- AC_AMP_GET_INPUT | 0x01);
- change = sel & 0x80;
- if (change) {
- snd_hda_codec_write_cache(codec, 0x1d, 0,
- AC_VERB_SET_AMP_GAIN_MUTE,
- AMP_IN_MUTE(0));
- snd_hda_codec_write_cache(codec, 0x1d, 0,
- AC_VERB_SET_AMP_GAIN_MUTE,
- AMP_IN_UNMUTE(1));
- }
- sel = snd_hda_codec_read(codec, 0x0b, 0,
- AC_VERB_GET_CONNECT_SEL, 0) + 1;
- change |= sel != val;
- if (change)
- snd_hda_codec_write_cache(codec, 0x0b, 0,
- AC_VERB_SET_CONNECT_SEL,
- val - 1);
- }
- return change;
-}
-
-static struct snd_kcontrol_new ad1988_spdif_out_mixers[] = {
- HDA_CODEC_VOLUME("IEC958 Playback Volume", 0x1b, 0x0, HDA_OUTPUT),
- {
- .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
- .name = "IEC958 Playback Source",
- .subdevice = HDA_SUBDEV_NID_FLAG | 0x1b,
- .info = ad1988_spdif_playback_source_info,
- .get = ad1988_spdif_playback_source_get,
- .put = ad1988_spdif_playback_source_put,
- },
- { } /* end */
-};
-
-static struct snd_kcontrol_new ad1988_spdif_in_mixers[] = {
- HDA_CODEC_VOLUME("IEC958 Capture Volume", 0x1c, 0x0, HDA_INPUT),
- { } /* end */
-};
-
-static struct snd_kcontrol_new ad1989_spdif_out_mixers[] = {
- HDA_CODEC_VOLUME("IEC958 Playback Volume", 0x1b, 0x0, HDA_OUTPUT),
- HDA_CODEC_VOLUME("HDMI Playback Volume", 0x1d, 0x0, HDA_OUTPUT),
- { } /* end */
-};
-
-/*
- * initialization verbs
- */
-
-/*
- * for 6-stack (+dig)
- */
-static struct hda_verb ad1988_6stack_init_verbs[] = {
- /* Front, Surround, CLFE, side DAC; unmute as default */
- {0x04, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
- {0x06, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
- {0x05, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
- {0x0a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
- /* Port-A front headphon path */
- {0x37, AC_VERB_SET_CONNECT_SEL, 0x01}, /* DAC1:04h */
- {0x22, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
- {0x22, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
- {0x11, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
- {0x11, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP},
- /* Port-D line-out path */
- {0x29, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
- {0x29, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
- {0x12, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
- {0x12, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
- /* Port-F surround path */
- {0x2a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
- {0x2a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
- {0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
- {0x16, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
- /* Port-G CLFE path */
- {0x27, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
- {0x27, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
- {0x24, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
- {0x24, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
- /* Port-H side path */
- {0x28, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
- {0x28, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
- {0x25, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
- {0x25, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
- /* Mono out path */
- {0x36, AC_VERB_SET_CONNECT_SEL, 0x1}, /* DAC1:04h */
- {0x1e, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
- {0x1e, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
- {0x13, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
- {0x13, AC_VERB_SET_AMP_GAIN_MUTE, 0xb01f}, /* unmute, 0dB */
- /* Port-B front mic-in path */
- {0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
- {0x14, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80},
- {0x39, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
- /* Port-C line-in path */
- {0x15, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
- {0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN},
- {0x3a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
- {0x33, AC_VERB_SET_CONNECT_SEL, 0x0},
- /* Port-E mic-in path */
- {0x17, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
- {0x17, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80},
- {0x3c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
- {0x34, AC_VERB_SET_CONNECT_SEL, 0x0},
- /* Analog CD Input */
- {0x18, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN},
- /* Analog Mix output amp */
- {0x21, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE | 0x1f}, /* 0dB */
-
- { }
-};
-
-static struct hda_verb ad1988_6stack_fp_init_verbs[] = {
- /* Headphone; unmute as default */
- {0x03, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
- /* Port-A front headphon path */
- {0x37, AC_VERB_SET_CONNECT_SEL, 0x00}, /* DAC0:03h */
- {0x22, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
- {0x22, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
- {0x11, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
- {0x11, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP},
-
- { }
-};
-
-static struct hda_verb ad1988_capture_init_verbs[] = {
- /* mute analog mix */
- {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
- {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
- {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(2)},
- {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(3)},
- {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(4)},
- {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(5)},
- {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(6)},
- {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(7)},
- /* select ADCs - front-mic */
- {0x0c, AC_VERB_SET_CONNECT_SEL, 0x1},
- {0x0d, AC_VERB_SET_CONNECT_SEL, 0x1},
- {0x0e, AC_VERB_SET_CONNECT_SEL, 0x1},
-
- { }
-};
-
-static struct hda_verb ad1988_spdif_init_verbs[] = {
- /* SPDIF out sel */
- {0x02, AC_VERB_SET_CONNECT_SEL, 0x0}, /* PCM */
- {0x0b, AC_VERB_SET_CONNECT_SEL, 0x0}, /* ADC1 */
- {0x1d, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
- {0x1d, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
- /* SPDIF out pin */
- {0x1b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE | 0x27}, /* 0dB */
-
- { }
-};
-
-static struct hda_verb ad1988_spdif_in_init_verbs[] = {
- /* unmute SPDIF input pin */
- {0x1c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
- { }
-};
-
-/* AD1989 has no ADC -> SPDIF route */
-static struct hda_verb ad1989_spdif_init_verbs[] = {
- /* SPDIF-1 out pin */
- {0x1b, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT },
- {0x1b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE | 0x27}, /* 0dB */
- /* SPDIF-2/HDMI out pin */
- {0x1d, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT },
- {0x1d, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE | 0x27}, /* 0dB */
- { }
-};
-
-/*
- * verbs for 3stack (+dig)
- */
-static struct hda_verb ad1988_3stack_ch2_init[] = {
- /* set port-C to line-in */
- { 0x15, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE },
- { 0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN },
- /* set port-E to mic-in */
- { 0x17, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE },
- { 0x17, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80 },
- { } /* end */
-};
-
-static struct hda_verb ad1988_3stack_ch6_init[] = {
- /* set port-C to surround out */
- { 0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT },
- { 0x15, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE },
- /* set port-E to CLFE out */
- { 0x17, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT },
- { 0x17, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE },
- { } /* end */
-};
-
-static struct hda_channel_mode ad1988_3stack_modes[2] = {
- { 2, ad1988_3stack_ch2_init },
- { 6, ad1988_3stack_ch6_init },
-};
-
-static struct hda_verb ad1988_3stack_init_verbs[] = {
- /* Front, Surround, CLFE, side DAC; unmute as default */
- {0x04, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
- {0x06, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
- {0x05, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
- {0x0a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
- /* Port-A front headphon path */
- {0x37, AC_VERB_SET_CONNECT_SEL, 0x01}, /* DAC1:04h */
- {0x22, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
- {0x22, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
- {0x11, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
- {0x11, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP},
- /* Port-D line-out path */
- {0x29, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
- {0x29, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
- {0x12, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
- {0x12, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
- /* Mono out path */
- {0x36, AC_VERB_SET_CONNECT_SEL, 0x1}, /* DAC1:04h */
- {0x1e, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
- {0x1e, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
- {0x13, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
- {0x13, AC_VERB_SET_AMP_GAIN_MUTE, 0xb01f}, /* unmute, 0dB */
- /* Port-B front mic-in path */
- {0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
- {0x14, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80},
- {0x39, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
- /* Port-C line-in/surround path - 6ch mode as default */
- {0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
- {0x15, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
- {0x3a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
- {0x31, AC_VERB_SET_CONNECT_SEL, 0x0}, /* output sel: DAC 0x05 */
- {0x33, AC_VERB_SET_CONNECT_SEL, 0x0},
- /* Port-E mic-in/CLFE path - 6ch mode as default */
- {0x17, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
- {0x17, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
- {0x3c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
- {0x32, AC_VERB_SET_CONNECT_SEL, 0x1}, /* output sel: DAC 0x0a */
- {0x34, AC_VERB_SET_CONNECT_SEL, 0x0},
- /* mute analog mix */
- {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
- {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
- {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(2)},
- {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(3)},
- {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(4)},
- {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(5)},
- {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(6)},
- {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(7)},
- /* select ADCs - front-mic */
- {0x0c, AC_VERB_SET_CONNECT_SEL, 0x1},
- {0x0d, AC_VERB_SET_CONNECT_SEL, 0x1},
- {0x0e, AC_VERB_SET_CONNECT_SEL, 0x1},
- /* Analog Mix output amp */
- {0x21, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE | 0x1f}, /* 0dB */
- { }
-};
-
-/*
- * verbs for laptop mode (+dig)
- */
-static struct hda_verb ad1988_laptop_hp_on[] = {
- /* unmute port-A and mute port-D */
- { 0x11, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE },
- { 0x12, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE },
- { } /* end */
-};
-static struct hda_verb ad1988_laptop_hp_off[] = {
- /* mute port-A and unmute port-D */
- { 0x11, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE },
- { 0x12, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE },
- { } /* end */
-};
-
-#define AD1988_HP_EVENT 0x01
-
-static struct hda_verb ad1988_laptop_init_verbs[] = {
- /* Front, Surround, CLFE, side DAC; unmute as default */
- {0x04, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
- {0x06, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
- {0x05, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
- {0x0a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
- /* Port-A front headphon path */
- {0x37, AC_VERB_SET_CONNECT_SEL, 0x01}, /* DAC1:04h */
- {0x22, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
- {0x22, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
- {0x11, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
- {0x11, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP},
- /* unsolicited event for pin-sense */
- {0x11, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | AD1988_HP_EVENT },
- /* Port-D line-out path + EAPD */
- {0x29, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
- {0x29, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
- {0x12, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
- {0x12, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
- {0x12, AC_VERB_SET_EAPD_BTLENABLE, 0x00}, /* EAPD-off */
- /* Mono out path */
- {0x36, AC_VERB_SET_CONNECT_SEL, 0x1}, /* DAC1:04h */
- {0x1e, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
- {0x1e, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
- {0x13, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
- {0x13, AC_VERB_SET_AMP_GAIN_MUTE, 0xb01f}, /* unmute, 0dB */
- /* Port-B mic-in path */
- {0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
- {0x14, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80},
- {0x39, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
- /* Port-C docking station - try to output */
- {0x15, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
- {0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
- {0x3a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
- {0x33, AC_VERB_SET_CONNECT_SEL, 0x0},
- /* mute analog mix */
- {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
- {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
- {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(2)},
- {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(3)},
- {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(4)},
- {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(5)},
- {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(6)},
- {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(7)},
- /* select ADCs - mic */
- {0x0c, AC_VERB_SET_CONNECT_SEL, 0x1},
- {0x0d, AC_VERB_SET_CONNECT_SEL, 0x1},
- {0x0e, AC_VERB_SET_CONNECT_SEL, 0x1},
- /* Analog Mix output amp */
- {0x21, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE | 0x1f}, /* 0dB */
- { }
-};
-
-static void ad1988_laptop_unsol_event(struct hda_codec *codec, unsigned int res)
-{
- if ((res >> 26) != AD1988_HP_EVENT)
- return;
- if (snd_hda_jack_detect(codec, 0x11))
- snd_hda_sequence_write(codec, ad1988_laptop_hp_on);
- else
- snd_hda_sequence_write(codec, ad1988_laptop_hp_off);
-}
-
-#ifdef CONFIG_SND_HDA_POWER_SAVE
-static struct hda_amp_list ad1988_loopbacks[] = {
- { 0x20, HDA_INPUT, 0 }, /* Front Mic */
- { 0x20, HDA_INPUT, 1 }, /* Line */
- { 0x20, HDA_INPUT, 4 }, /* Mic */
- { 0x20, HDA_INPUT, 6 }, /* CD */
- { } /* end */
-};
-#endif
-
-/*
- * Automatic parse of I/O pins from the BIOS configuration
- */
-
-enum {
- AD_CTL_WIDGET_VOL,
- AD_CTL_WIDGET_MUTE,
- AD_CTL_BIND_MUTE,
-};
-static struct snd_kcontrol_new ad1988_control_templates[] = {
- HDA_CODEC_VOLUME(NULL, 0, 0, 0),
- HDA_CODEC_MUTE(NULL, 0, 0, 0),
- HDA_BIND_MUTE(NULL, 0, 0, 0),
-};
-
-/* add dynamic controls */
-static int add_control(struct ad198x_spec *spec, int type, const char *name,
- unsigned long val)
-{
- struct snd_kcontrol_new *knew;
-
- snd_array_init(&spec->kctls, sizeof(*knew), 32);
- knew = snd_array_new(&spec->kctls);
- if (!knew)
- return -ENOMEM;
- *knew = ad1988_control_templates[type];
- knew->name = kstrdup(name, GFP_KERNEL);
- if (! knew->name)
- return -ENOMEM;
- if (get_amp_nid_(val))
- knew->subdevice = HDA_SUBDEV_AMP_FLAG;
- knew->private_value = val;
- return 0;
-}
-
-#define AD1988_PIN_CD_NID 0x18
-#define AD1988_PIN_BEEP_NID 0x10
-
-static hda_nid_t ad1988_mixer_nids[8] = {
- /* A B C D E F G H */
- 0x22, 0x2b, 0x2c, 0x29, 0x26, 0x2a, 0x27, 0x28
-};
+ if (spec->cur_smux == val)
+ return 0;
-static inline hda_nid_t ad1988_idx_to_dac(struct hda_codec *codec, int idx)
-{
- static hda_nid_t idx_to_dac[8] = {
- /* A B C D E F G H */
- 0x04, 0x06, 0x05, 0x04, 0x0a, 0x06, 0x05, 0x0a
- };
- static hda_nid_t idx_to_dac_rev2[8] = {
- /* A B C D E F G H */
- 0x04, 0x05, 0x0a, 0x04, 0x06, 0x05, 0x0a, 0x06
- };
- if (is_rev2(codec))
- return idx_to_dac_rev2[idx];
- else
- return idx_to_dac[idx];
+ mutex_lock(&codec->control_mutex);
+ codec->cached_write = 1;
+ path = snd_hda_get_path_from_idx(codec,
+ spec->smux_paths[spec->cur_smux]);
+ if (path)
+ snd_hda_activate_path(codec, path, false, true);
+ path = snd_hda_get_path_from_idx(codec, spec->smux_paths[val]);
+ if (path)
+ snd_hda_activate_path(codec, path, true, true);
+ spec->cur_smux = val;
+ codec->cached_write = 0;
+ mutex_unlock(&codec->control_mutex);
+ snd_hda_codec_flush_cache(codec); /* flush the updates */
+ return 1;
}
-static hda_nid_t ad1988_boost_nids[8] = {
- 0x38, 0x39, 0x3a, 0x3d, 0x3c, 0x3b, 0, 0
+static struct snd_kcontrol_new ad1988_auto_smux_mixer = {
+ .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+ .name = "IEC958 Playback Source",
+ .info = ad1988_auto_smux_enum_info,
+ .get = ad1988_auto_smux_enum_get,
+ .put = ad1988_auto_smux_enum_put,
};
-static int ad1988_pin_idx(hda_nid_t nid)
-{
- static hda_nid_t ad1988_io_pins[8] = {
- 0x11, 0x14, 0x15, 0x12, 0x17, 0x16, 0x24, 0x25
- };
- int i;
- for (i = 0; i < ARRAY_SIZE(ad1988_io_pins); i++)
- if (ad1988_io_pins[i] == nid)
- return i;
- return 0; /* should be -1 */
-}
-
-static int ad1988_pin_to_loopback_idx(hda_nid_t nid)
-{
- static int loopback_idx[8] = {
- 2, 0, 1, 3, 4, 5, 1, 4
- };
- switch (nid) {
- case AD1988_PIN_CD_NID:
- return 6;
- default:
- return loopback_idx[ad1988_pin_idx(nid)];
- }
-}
-
-static int ad1988_pin_to_adc_idx(hda_nid_t nid)
-{
- static int adc_idx[8] = {
- 0, 1, 2, 8, 4, 3, 6, 7
- };
- switch (nid) {
- case AD1988_PIN_CD_NID:
- return 5;
- default:
- return adc_idx[ad1988_pin_idx(nid)];
- }
-}
-
-/* fill in the dac_nids table from the parsed pin configuration */
-static int ad1988_auto_fill_dac_nids(struct hda_codec *codec,
- const struct auto_pin_cfg *cfg)
+static int ad1988_auto_init(struct hda_codec *codec)
{
struct ad198x_spec *spec = codec->spec;
- int i, idx;
-
- spec->multiout.dac_nids = spec->private_dac_nids;
-
- /* check the pins hardwired to audio widget */
- for (i = 0; i < cfg->line_outs; i++) {
- idx = ad1988_pin_idx(cfg->line_out_pins[i]);
- spec->multiout.dac_nids[i] = ad1988_idx_to_dac(codec, idx);
- }
- spec->multiout.num_dacs = cfg->line_outs;
- return 0;
-}
-
-/* add playback controls from the parsed DAC table */
-static int ad1988_auto_create_multi_out_ctls(struct ad198x_spec *spec,
- const struct auto_pin_cfg *cfg)
-{
- char name[32];
- static const char * const chname[4] = {
- "Front", "Surround", NULL /*CLFE*/, "Side"
- };
- hda_nid_t nid;
int i, err;
- for (i = 0; i < cfg->line_outs; i++) {
- hda_nid_t dac = spec->multiout.dac_nids[i];
- if (! dac)
- continue;
- nid = ad1988_mixer_nids[ad1988_pin_idx(cfg->line_out_pins[i])];
- if (i == 2) {
- /* Center/LFE */
- err = add_control(spec, AD_CTL_WIDGET_VOL,
- "Center Playback Volume",
- HDA_COMPOSE_AMP_VAL(dac, 1, 0, HDA_OUTPUT));
- if (err < 0)
- return err;
- err = add_control(spec, AD_CTL_WIDGET_VOL,
- "LFE Playback Volume",
- HDA_COMPOSE_AMP_VAL(dac, 2, 0, HDA_OUTPUT));
- if (err < 0)
- return err;
- err = add_control(spec, AD_CTL_BIND_MUTE,
- "Center Playback Switch",
- HDA_COMPOSE_AMP_VAL(nid, 1, 2, HDA_INPUT));
- if (err < 0)
- return err;
- err = add_control(spec, AD_CTL_BIND_MUTE,
- "LFE Playback Switch",
- HDA_COMPOSE_AMP_VAL(nid, 2, 2, HDA_INPUT));
- if (err < 0)
- return err;
- } else {
- sprintf(name, "%s Playback Volume", chname[i]);
- err = add_control(spec, AD_CTL_WIDGET_VOL, name,
- HDA_COMPOSE_AMP_VAL(dac, 3, 0, HDA_OUTPUT));
- if (err < 0)
- return err;
- sprintf(name, "%s Playback Switch", chname[i]);
- err = add_control(spec, AD_CTL_BIND_MUTE, name,
- HDA_COMPOSE_AMP_VAL(nid, 3, 2, HDA_INPUT));
- if (err < 0)
- return err;
- }
- }
- return 0;
-}
-
-/* add playback controls for speaker and HP outputs */
-static int ad1988_auto_create_extra_out(struct hda_codec *codec, hda_nid_t pin,
- const char *pfx)
-{
- struct ad198x_spec *spec = codec->spec;
- hda_nid_t nid;
- int i, idx, err;
- char name[32];
-
- if (! pin)
+ err = snd_hda_gen_init(codec);
+ if (err < 0)
+ return err;
+ if (!spec->gen.autocfg.dig_outs)
return 0;
- idx = ad1988_pin_idx(pin);
- nid = ad1988_idx_to_dac(codec, idx);
- /* check whether the corresponding DAC was already taken */
- for (i = 0; i < spec->autocfg.line_outs; i++) {
- hda_nid_t pin = spec->autocfg.line_out_pins[i];
- hda_nid_t dac = ad1988_idx_to_dac(codec, ad1988_pin_idx(pin));
- if (dac == nid)
- break;
- }
- if (i >= spec->autocfg.line_outs) {
- /* specify the DAC as the extra output */
- if (!spec->multiout.hp_nid)
- spec->multiout.hp_nid = nid;
- else
- spec->multiout.extra_out_nid[0] = nid;
- /* control HP volume/switch on the output mixer amp */
- sprintf(name, "%s Playback Volume", pfx);
- err = add_control(spec, AD_CTL_WIDGET_VOL, name,
- HDA_COMPOSE_AMP_VAL(nid, 3, 0, HDA_OUTPUT));
- if (err < 0)
- return err;
+ for (i = 0; i < 4; i++) {
+ struct nid_path *path;
+ path = snd_hda_get_path_from_idx(codec, spec->smux_paths[i]);
+ if (path)
+ snd_hda_activate_path(codec, path, path->active, false);
}
- nid = ad1988_mixer_nids[idx];
- sprintf(name, "%s Playback Switch", pfx);
- if ((err = add_control(spec, AD_CTL_BIND_MUTE, name,
- HDA_COMPOSE_AMP_VAL(nid, 3, 2, HDA_INPUT))) < 0)
- return err;
- return 0;
-}
-
-/* create input playback/capture controls for the given pin */
-static int new_analog_input(struct ad198x_spec *spec, hda_nid_t pin,
- const char *ctlname, int ctlidx, int boost)
-{
- char name[32];
- int err, idx;
- sprintf(name, "%s Playback Volume", ctlname);
- idx = ad1988_pin_to_loopback_idx(pin);
- if ((err = add_control(spec, AD_CTL_WIDGET_VOL, name,
- HDA_COMPOSE_AMP_VAL(0x20, 3, idx, HDA_INPUT))) < 0)
- return err;
- sprintf(name, "%s Playback Switch", ctlname);
- if ((err = add_control(spec, AD_CTL_WIDGET_MUTE, name,
- HDA_COMPOSE_AMP_VAL(0x20, 3, idx, HDA_INPUT))) < 0)
- return err;
- if (boost) {
- hda_nid_t bnid;
- idx = ad1988_pin_idx(pin);
- bnid = ad1988_boost_nids[idx];
- if (bnid) {
- sprintf(name, "%s Boost Volume", ctlname);
- return add_control(spec, AD_CTL_WIDGET_VOL, name,
- HDA_COMPOSE_AMP_VAL(bnid, 3, idx, HDA_OUTPUT));
-
- }
- }
return 0;
}
-/* create playback/capture controls for input pins */
-static int ad1988_auto_create_analog_input_ctls(struct hda_codec *codec,
- const struct auto_pin_cfg *cfg)
+static int ad1988_add_spdif_mux_ctl(struct hda_codec *codec)
{
struct ad198x_spec *spec = codec->spec;
- struct hda_input_mux *imux = &spec->private_imux;
- int i, err, type, type_idx;
-
- for (i = 0; i < cfg->num_inputs; i++) {
- const char *label;
- type = cfg->inputs[i].type;
- label = hda_get_autocfg_input_label(codec, cfg, i);
- snd_hda_add_imux_item(imux, label,
- ad1988_pin_to_adc_idx(cfg->inputs[i].pin),
- &type_idx);
- err = new_analog_input(spec, cfg->inputs[i].pin,
- label, type_idx,
- type == AUTO_PIN_MIC);
- if (err < 0)
- return err;
- }
- snd_hda_add_imux_item(imux, "Mix", 9, NULL);
-
- if ((err = add_control(spec, AD_CTL_WIDGET_VOL,
- "Analog Mix Playback Volume",
- HDA_COMPOSE_AMP_VAL(0x21, 3, 0x0, HDA_OUTPUT))) < 0)
- return err;
- if ((err = add_control(spec, AD_CTL_WIDGET_MUTE,
- "Analog Mix Playback Switch",
- HDA_COMPOSE_AMP_VAL(0x21, 3, 0x0, HDA_OUTPUT))) < 0)
- return err;
-
- return 0;
-}
+ int i, num_conns;
+ /* we create four static faked paths, since AD codecs have odd
+ * widget connections regarding the SPDIF out source
+ */
+ static struct nid_path fake_paths[4] = {
+ {
+ .depth = 3,
+ .path = { 0x02, 0x1d, 0x1b },
+ .idx = { 0, 0, 0 },
+ .multi = { 0, 0, 0 },
+ },
+ {
+ .depth = 4,
+ .path = { 0x08, 0x0b, 0x1d, 0x1b },
+ .idx = { 0, 0, 1, 0 },
+ .multi = { 0, 1, 0, 0 },
+ },
+ {
+ .depth = 4,
+ .path = { 0x09, 0x0b, 0x1d, 0x1b },
+ .idx = { 0, 1, 1, 0 },
+ .multi = { 0, 1, 0, 0 },
+ },
+ {
+ .depth = 4,
+ .path = { 0x0f, 0x0b, 0x1d, 0x1b },
+ .idx = { 0, 2, 1, 0 },
+ .multi = { 0, 1, 0, 0 },
+ },
+ };
-static void ad1988_auto_set_output_and_unmute(struct hda_codec *codec,
- hda_nid_t nid, int pin_type,
- int dac_idx)
-{
- /* set as output */
- snd_hda_codec_write(codec, nid, 0, AC_VERB_SET_PIN_WIDGET_CONTROL, pin_type);
- snd_hda_codec_write(codec, nid, 0, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE);
- switch (nid) {
- case 0x11: /* port-A - DAC 04 */
- snd_hda_codec_write(codec, 0x37, 0, AC_VERB_SET_CONNECT_SEL, 0x01);
- break;
- case 0x14: /* port-B - DAC 06 */
- snd_hda_codec_write(codec, 0x30, 0, AC_VERB_SET_CONNECT_SEL, 0x02);
- break;
- case 0x15: /* port-C - DAC 05 */
- snd_hda_codec_write(codec, 0x31, 0, AC_VERB_SET_CONNECT_SEL, 0x00);
- break;
- case 0x17: /* port-E - DAC 0a */
- snd_hda_codec_write(codec, 0x32, 0, AC_VERB_SET_CONNECT_SEL, 0x01);
- break;
- case 0x13: /* mono - DAC 04 */
- snd_hda_codec_write(codec, 0x36, 0, AC_VERB_SET_CONNECT_SEL, 0x01);
- break;
- }
-}
+ /* SPDIF source mux appears to be present only on AD1988A */
+ if (!spec->gen.autocfg.dig_outs ||
+ get_wcaps_type(get_wcaps(codec, 0x1d)) != AC_WID_AUD_MIX)
+ return 0;
-static void ad1988_auto_init_multi_out(struct hda_codec *codec)
-{
- struct ad198x_spec *spec = codec->spec;
- int i;
+ num_conns = snd_hda_get_num_conns(codec, 0x0b) + 1;
+ if (num_conns != 3 && num_conns != 4)
+ return 0;
- for (i = 0; i < spec->autocfg.line_outs; i++) {
- hda_nid_t nid = spec->autocfg.line_out_pins[i];
- ad1988_auto_set_output_and_unmute(codec, nid, PIN_OUT, i);
+ for (i = 0; i < num_conns; i++) {
+ struct nid_path *path = snd_array_new(&spec->gen.paths);
+ if (!path)
+ return -ENOMEM;
+ *path = fake_paths[i];
+ if (!i)
+ path->active = 1;
+ spec->smux_paths[i] = snd_hda_get_path_idx(codec, path);
}
-}
-static void ad1988_auto_init_extra_out(struct hda_codec *codec)
-{
- struct ad198x_spec *spec = codec->spec;
- hda_nid_t pin;
-
- pin = spec->autocfg.speaker_pins[0];
- if (pin) /* connect to front */
- ad1988_auto_set_output_and_unmute(codec, pin, PIN_OUT, 0);
- pin = spec->autocfg.hp_pins[0];
- if (pin) /* connect to front */
- ad1988_auto_set_output_and_unmute(codec, pin, PIN_HP, 0);
-}
-
-static void ad1988_auto_init_analog_input(struct hda_codec *codec)
-{
- struct ad198x_spec *spec = codec->spec;
- const struct auto_pin_cfg *cfg = &spec->autocfg;
- int i, idx;
-
- for (i = 0; i < cfg->num_inputs; i++) {
- hda_nid_t nid = cfg->inputs[i].pin;
- switch (nid) {
- case 0x15: /* port-C */
- snd_hda_codec_write(codec, 0x33, 0, AC_VERB_SET_CONNECT_SEL, 0x0);
- break;
- case 0x17: /* port-E */
- snd_hda_codec_write(codec, 0x34, 0, AC_VERB_SET_CONNECT_SEL, 0x0);
- break;
- }
- snd_hda_codec_write(codec, nid, 0, AC_VERB_SET_PIN_WIDGET_CONTROL,
- i == AUTO_PIN_MIC ? PIN_VREF80 : PIN_IN);
- if (nid != AD1988_PIN_CD_NID)
- snd_hda_codec_write(codec, nid, 0, AC_VERB_SET_AMP_GAIN_MUTE,
- AMP_OUT_MUTE);
- idx = ad1988_pin_idx(nid);
- if (ad1988_boost_nids[idx])
- snd_hda_codec_write(codec, ad1988_boost_nids[idx], 0,
- AC_VERB_SET_AMP_GAIN_MUTE,
- AMP_OUT_ZERO);
- }
-}
-
-/* parse the BIOS configuration and set up the alc_spec */
-/* return 1 if successful, 0 if the proper config is not found, or a negative error code */
-static int ad1988_parse_auto_config(struct hda_codec *codec)
-{
- struct ad198x_spec *spec = codec->spec;
- int err;
-
- if ((err = snd_hda_parse_pin_def_config(codec, &spec->autocfg, NULL)) < 0)
- return err;
- if ((err = ad1988_auto_fill_dac_nids(codec, &spec->autocfg)) < 0)
- return err;
- if (! spec->autocfg.line_outs)
- return 0; /* can't find valid BIOS pin config */
- if ((err = ad1988_auto_create_multi_out_ctls(spec, &spec->autocfg)) < 0 ||
- (err = ad1988_auto_create_extra_out(codec,
- spec->autocfg.speaker_pins[0],
- "Speaker")) < 0 ||
- (err = ad1988_auto_create_extra_out(codec, spec->autocfg.hp_pins[0],
- "Headphone")) < 0 ||
- (err = ad1988_auto_create_analog_input_ctls(codec, &spec->autocfg)) < 0)
- return err;
-
- spec->multiout.max_channels = spec->multiout.num_dacs * 2;
-
- if (spec->autocfg.dig_outs)
- spec->multiout.dig_out_nid = AD1988_SPDIF_OUT;
- if (spec->autocfg.dig_in_pin)
- spec->dig_in_nid = AD1988_SPDIF_IN;
-
- if (spec->kctls.list)
- spec->mixers[spec->num_mixers++] = spec->kctls.list;
-
- spec->init_verbs[spec->num_init_verbs++] = ad1988_6stack_init_verbs;
-
- spec->input_mux = &spec->private_imux;
+ if (!snd_hda_gen_add_kctl(&spec->gen, NULL, &ad1988_auto_smux_mixer))
+ return -ENOMEM;
- return 1;
-}
+ codec->patch_ops.init = ad1988_auto_init;
-/* init callback for auto-configuration model -- overriding the default init */
-static int ad1988_auto_init(struct hda_codec *codec)
-{
- ad198x_init(codec);
- ad1988_auto_init_multi_out(codec);
- ad1988_auto_init_extra_out(codec);
- ad1988_auto_init_analog_input(codec);
return 0;
}
/*
*/
-static const char * const ad1988_models[AD1988_MODEL_LAST] = {
- [AD1988_6STACK] = "6stack",
- [AD1988_6STACK_DIG] = "6stack-dig",
- [AD1988_6STACK_DIG_FP] = "6stack-dig-fp",
- [AD1988_3STACK] = "3stack",
- [AD1988_3STACK_DIG] = "3stack-dig",
- [AD1988_LAPTOP] = "laptop",
- [AD1988_LAPTOP_DIG] = "laptop-dig",
- [AD1988_AUTO] = "auto",
+enum {
+ AD1988_FIXUP_6STACK_DIG,
+};
+
+static const struct hda_fixup ad1988_fixups[] = {
+ [AD1988_FIXUP_6STACK_DIG] = {
+ .type = HDA_FIXUP_PINS,
+ .v.pins = (const struct hda_pintbl[]) {
+ { 0x11, 0x02214130 }, /* front-hp */
+ { 0x12, 0x01014010 }, /* line-out */
+ { 0x14, 0x02a19122 }, /* front-mic */
+ { 0x15, 0x01813021 }, /* line-in */
+ { 0x16, 0x01011012 }, /* line-out */
+ { 0x17, 0x01a19020 }, /* mic */
+ { 0x1b, 0x0145f1f0 }, /* SPDIF */
+ { 0x24, 0x01016011 }, /* line-out */
+ { 0x25, 0x01012013 }, /* line-out */
+ { }
+ }
+ },
};
-static struct snd_pci_quirk ad1988_cfg_tbl[] = {
- SND_PCI_QUIRK(0x1043, 0x81ec, "Asus P5B-DLX", AD1988_6STACK_DIG),
- SND_PCI_QUIRK(0x1043, 0x81f6, "Asus M2N-SLI", AD1988_6STACK_DIG),
- SND_PCI_QUIRK(0x1043, 0x8277, "Asus P5K-E/WIFI-AP", AD1988_6STACK_DIG),
- SND_PCI_QUIRK(0x1043, 0x8311, "Asus P5Q-Premium/Pro", AD1988_6STACK_DIG),
+static const struct hda_model_fixup ad1988_fixup_models[] = {
+ { .id = AD1988_FIXUP_6STACK_DIG, .name = "6stack-dig" },
{}
};
static int patch_ad1988(struct hda_codec *codec)
{
struct ad198x_spec *spec;
- int err, board_config;
-
- spec = kzalloc(sizeof(*spec), GFP_KERNEL);
- if (spec == NULL)
- return -ENOMEM;
-
- codec->spec = spec;
-
- if (is_rev2(codec))
- snd_printk(KERN_INFO "patch_analog: AD1988A rev.2 is detected, enable workarounds\n");
-
- board_config = snd_hda_check_board_config(codec, AD1988_MODEL_LAST,
- ad1988_models, ad1988_cfg_tbl);
- if (board_config < 0) {
- printk(KERN_INFO "hda_codec: %s: BIOS auto-probing.\n",
- codec->chip_name);
- board_config = AD1988_AUTO;
- }
-
- if (board_config == AD1988_AUTO) {
- /* automatic parse from the BIOS config */
- err = ad1988_parse_auto_config(codec);
- if (err < 0) {
- ad198x_free(codec);
- return err;
- } else if (! err) {
- printk(KERN_INFO "hda_codec: Cannot set up configuration from BIOS. Using 6-stack mode...\n");
- board_config = AD1988_6STACK;
- }
- }
+ int err;
- err = snd_hda_attach_beep_device(codec, 0x10);
- if (err < 0) {
- ad198x_free(codec);
+ err = alloc_ad_spec(codec);
+ if (err < 0)
return err;
- }
- set_beep_amp(spec, 0x10, 0, HDA_OUTPUT);
+ spec = codec->spec;
- switch (board_config) {
- case AD1988_6STACK:
- case AD1988_6STACK_DIG:
- case AD1988_6STACK_DIG_FP:
- spec->multiout.max_channels = 8;
- spec->multiout.num_dacs = 4;
- if (is_rev2(codec))
- spec->multiout.dac_nids = ad1988_6stack_dac_nids_rev2;
- else
- spec->multiout.dac_nids = ad1988_6stack_dac_nids;
- spec->input_mux = &ad1988_6stack_capture_source;
- spec->num_mixers = 2;
- if (is_rev2(codec))
- spec->mixers[0] = ad1988_6stack_mixers1_rev2;
- else
- spec->mixers[0] = ad1988_6stack_mixers1;
- spec->mixers[1] = ad1988_6stack_mixers2;
- spec->num_init_verbs = 1;
- spec->init_verbs[0] = ad1988_6stack_init_verbs;
- if (board_config == AD1988_6STACK_DIG_FP) {
- spec->num_mixers++;
- spec->mixers[2] = ad1988_6stack_fp_mixers;
- spec->num_init_verbs++;
- spec->init_verbs[1] = ad1988_6stack_fp_init_verbs;
- spec->slave_vols = ad1988_6stack_fp_slave_vols;
- spec->slave_sws = ad1988_6stack_fp_slave_sws;
- spec->alt_dac_nid = ad1988_alt_dac_nid;
- spec->stream_analog_alt_playback =
- &ad198x_pcm_analog_alt_playback;
- }
- if ((board_config == AD1988_6STACK_DIG) ||
- (board_config == AD1988_6STACK_DIG_FP)) {
- spec->multiout.dig_out_nid = AD1988_SPDIF_OUT;
- spec->dig_in_nid = AD1988_SPDIF_IN;
- }
- break;
- case AD1988_3STACK:
- case AD1988_3STACK_DIG:
- spec->multiout.max_channels = 6;
- spec->multiout.num_dacs = 3;
- if (is_rev2(codec))
- spec->multiout.dac_nids = ad1988_3stack_dac_nids_rev2;
- else
- spec->multiout.dac_nids = ad1988_3stack_dac_nids;
- spec->input_mux = &ad1988_6stack_capture_source;
- spec->channel_mode = ad1988_3stack_modes;
- spec->num_channel_mode = ARRAY_SIZE(ad1988_3stack_modes);
- spec->num_mixers = 2;
- if (is_rev2(codec))
- spec->mixers[0] = ad1988_3stack_mixers1_rev2;
- else
- spec->mixers[0] = ad1988_3stack_mixers1;
- spec->mixers[1] = ad1988_3stack_mixers2;
- spec->num_init_verbs = 1;
- spec->init_verbs[0] = ad1988_3stack_init_verbs;
- if (board_config == AD1988_3STACK_DIG)
- spec->multiout.dig_out_nid = AD1988_SPDIF_OUT;
- break;
- case AD1988_LAPTOP:
- case AD1988_LAPTOP_DIG:
- spec->multiout.max_channels = 2;
- spec->multiout.num_dacs = 1;
- spec->multiout.dac_nids = ad1988_3stack_dac_nids;
- spec->input_mux = &ad1988_laptop_capture_source;
- spec->num_mixers = 1;
- spec->mixers[0] = ad1988_laptop_mixers;
- spec->inv_eapd = 1; /* inverted EAPD */
- spec->num_init_verbs = 1;
- spec->init_verbs[0] = ad1988_laptop_init_verbs;
- if (board_config == AD1988_LAPTOP_DIG)
- spec->multiout.dig_out_nid = AD1988_SPDIF_OUT;
- break;
- }
+ spec->gen.mixer_nid = 0x20;
+ spec->gen.mixer_merge_nid = 0x21;
+ spec->gen.beep_nid = 0x10;
+ set_beep_amp(spec, 0x10, 0, HDA_OUTPUT);
- spec->num_adc_nids = ARRAY_SIZE(ad1988_adc_nids);
- spec->adc_nids = ad1988_adc_nids;
- spec->capsrc_nids = ad1988_capsrc_nids;
- spec->mixers[spec->num_mixers++] = ad1988_capture_mixers;
- spec->init_verbs[spec->num_init_verbs++] = ad1988_capture_init_verbs;
- if (spec->multiout.dig_out_nid) {
- if (codec->vendor_id >= 0x11d4989a) {
- spec->mixers[spec->num_mixers++] =
- ad1989_spdif_out_mixers;
- spec->init_verbs[spec->num_init_verbs++] =
- ad1989_spdif_init_verbs;
- codec->slave_dig_outs = ad1989b_slave_dig_outs;
- } else {
- spec->mixers[spec->num_mixers++] =
- ad1988_spdif_out_mixers;
- spec->init_verbs[spec->num_init_verbs++] =
- ad1988_spdif_init_verbs;
- }
- }
- if (spec->dig_in_nid && codec->vendor_id < 0x11d4989a) {
- spec->mixers[spec->num_mixers++] = ad1988_spdif_in_mixers;
- spec->init_verbs[spec->num_init_verbs++] =
- ad1988_spdif_in_init_verbs;
- }
+ snd_hda_pick_fixup(codec, ad1988_fixup_models, NULL, ad1988_fixups);
+ snd_hda_apply_fixup(codec, HDA_FIXUP_ACT_PRE_PROBE);
- codec->patch_ops = ad198x_patch_ops;
- switch (board_config) {
- case AD1988_AUTO:
- codec->patch_ops.init = ad1988_auto_init;
- break;
- case AD1988_LAPTOP:
- case AD1988_LAPTOP_DIG:
- codec->patch_ops.unsol_event = ad1988_laptop_unsol_event;
- break;
- }
-#ifdef CONFIG_SND_HDA_POWER_SAVE
- spec->loopback.amplist = ad1988_loopbacks;
-#endif
- spec->vmaster_nid = 0x04;
+ err = ad198x_parse_auto_config(codec, true);
+ if (err < 0)
+ goto error;
+ err = ad1988_add_spdif_mux_ctl(codec);
+ if (err < 0)
+ goto error;
- codec->no_trigger_sense = 1;
- codec->no_sticky_stream = 1;
+ snd_hda_apply_fixup(codec, HDA_FIXUP_ACT_PROBE);
return 0;
+
+ error:
+ snd_hda_gen_free(codec);
+ return err;
}
@@ -3336,440 +950,6 @@ static int patch_ad1988(struct hda_codec *codec)
*
* AD1984 = AD1884 + two digital mic-ins
*
- * FIXME:
- * For simplicity, we share the single DAC for both HP and line-outs
- * right now. The inidividual playbacks could be easily implemented,
- * but no build-up framework is given, so far.
- */
-
-static hda_nid_t ad1884_dac_nids[1] = {
- 0x04,
-};
-
-static hda_nid_t ad1884_adc_nids[2] = {
- 0x08, 0x09,
-};
-
-static hda_nid_t ad1884_capsrc_nids[2] = {
- 0x0c, 0x0d,
-};
-
-#define AD1884_SPDIF_OUT 0x02
-
-static struct hda_input_mux ad1884_capture_source = {
- .num_items = 4,
- .items = {
- { "Front Mic", 0x0 },
- { "Mic", 0x1 },
- { "CD", 0x2 },
- { "Mix", 0x3 },
- },
-};
-
-static struct snd_kcontrol_new ad1884_base_mixers[] = {
- HDA_CODEC_VOLUME("PCM Playback Volume", 0x04, 0x0, HDA_OUTPUT),
- /* HDA_CODEC_VOLUME_IDX("PCM Playback Volume", 1, 0x03, 0x0, HDA_OUTPUT), */
- HDA_CODEC_MUTE("Headphone Playback Switch", 0x11, 0x0, HDA_OUTPUT),
- HDA_CODEC_MUTE("Front Playback Switch", 0x12, 0x0, HDA_OUTPUT),
- HDA_CODEC_VOLUME_MONO("Mono Playback Volume", 0x13, 1, 0x0, HDA_OUTPUT),
- HDA_CODEC_MUTE_MONO("Mono Playback Switch", 0x13, 1, 0x0, HDA_OUTPUT),
- HDA_CODEC_VOLUME("Front Mic Playback Volume", 0x20, 0x00, HDA_INPUT),
- HDA_CODEC_MUTE("Front Mic Playback Switch", 0x20, 0x00, HDA_INPUT),
- HDA_CODEC_VOLUME("Mic Playback Volume", 0x20, 0x01, HDA_INPUT),
- HDA_CODEC_MUTE("Mic Playback Switch", 0x20, 0x01, HDA_INPUT),
- HDA_CODEC_VOLUME("CD Playback Volume", 0x20, 0x02, HDA_INPUT),
- HDA_CODEC_MUTE("CD Playback Switch", 0x20, 0x02, HDA_INPUT),
- HDA_CODEC_VOLUME("Mic Boost Volume", 0x15, 0x0, HDA_INPUT),
- HDA_CODEC_VOLUME("Front Mic Boost Volume", 0x14, 0x0, HDA_INPUT),
- HDA_CODEC_VOLUME("Capture Volume", 0x0c, 0x0, HDA_OUTPUT),
- HDA_CODEC_MUTE("Capture Switch", 0x0c, 0x0, HDA_OUTPUT),
- HDA_CODEC_VOLUME_IDX("Capture Volume", 1, 0x0d, 0x0, HDA_OUTPUT),
- HDA_CODEC_MUTE_IDX("Capture Switch", 1, 0x0d, 0x0, HDA_OUTPUT),
- {
- .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
- /* The multiple "Capture Source" controls confuse alsamixer
- * So call somewhat different..
- */
- /* .name = "Capture Source", */
- .name = "Input Source",
- .count = 2,
- .info = ad198x_mux_enum_info,
- .get = ad198x_mux_enum_get,
- .put = ad198x_mux_enum_put,
- },
- /* SPDIF controls */
- HDA_CODEC_VOLUME("IEC958 Playback Volume", 0x1b, 0x0, HDA_OUTPUT),
- {
- .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
- .name = SNDRV_CTL_NAME_IEC958("",PLAYBACK,NONE) "Source",
- /* identical with ad1983 */
- .info = ad1983_spdif_route_info,
- .get = ad1983_spdif_route_get,
- .put = ad1983_spdif_route_put,
- },
- { } /* end */
-};
-
-static struct snd_kcontrol_new ad1984_dmic_mixers[] = {
- HDA_CODEC_VOLUME("Digital Mic Capture Volume", 0x05, 0x0, HDA_INPUT),
- HDA_CODEC_MUTE("Digital Mic Capture Switch", 0x05, 0x0, HDA_INPUT),
- HDA_CODEC_VOLUME_IDX("Digital Mic Capture Volume", 1, 0x06, 0x0,
- HDA_INPUT),
- HDA_CODEC_MUTE_IDX("Digital Mic Capture Switch", 1, 0x06, 0x0,
- HDA_INPUT),
- { } /* end */
-};
-
-/*
- * initialization verbs
- */
-static struct hda_verb ad1884_init_verbs[] = {
- /* DACs; mute as default */
- {0x03, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
- {0x04, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
- /* Port-A (HP) mixer */
- {0x07, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
- {0x07, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
- /* Port-A pin */
- {0x11, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP},
- {0x11, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
- /* HP selector - select DAC2 */
- {0x22, AC_VERB_SET_CONNECT_SEL, 0x1},
- /* Port-D (Line-out) mixer */
- {0x0a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
- {0x0a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
- /* Port-D pin */
- {0x12, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP},
- {0x12, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
- /* Mono-out mixer */
- {0x1e, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
- {0x1e, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
- /* Mono-out pin */
- {0x13, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP},
- {0x13, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
- /* Mono selector */
- {0x0e, AC_VERB_SET_CONNECT_SEL, 0x1},
- /* Port-B (front mic) pin */
- {0x14, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80},
- {0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
- /* Port-C (rear mic) pin */
- {0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80},
- {0x15, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
- /* Analog mixer; mute as default */
- {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
- {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
- {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(2)},
- {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(3)},
- /* Analog Mix output amp */
- {0x21, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE | 0x1f}, /* 0dB */
- /* SPDIF output selector */
- {0x02, AC_VERB_SET_CONNECT_SEL, 0x0}, /* PCM */
- {0x1b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE | 0x27}, /* 0dB */
- { } /* end */
-};
-
-#ifdef CONFIG_SND_HDA_POWER_SAVE
-static struct hda_amp_list ad1884_loopbacks[] = {
- { 0x20, HDA_INPUT, 0 }, /* Front Mic */
- { 0x20, HDA_INPUT, 1 }, /* Mic */
- { 0x20, HDA_INPUT, 2 }, /* CD */
- { 0x20, HDA_INPUT, 4 }, /* Docking */
- { } /* end */
-};
-#endif
-
-static const char * const ad1884_slave_vols[] = {
- "PCM Playback Volume",
- "Mic Playback Volume",
- "Mono Playback Volume",
- "Front Mic Playback Volume",
- "Mic Playback Volume",
- "CD Playback Volume",
- "Internal Mic Playback Volume",
- "Docking Mic Playback Volume",
- /* "Beep Playback Volume", */
- "IEC958 Playback Volume",
- NULL
-};
-
-static int patch_ad1884(struct hda_codec *codec)
-{
- struct ad198x_spec *spec;
- int err;
-
- spec = kzalloc(sizeof(*spec), GFP_KERNEL);
- if (spec == NULL)
- return -ENOMEM;
-
- codec->spec = spec;
-
- err = snd_hda_attach_beep_device(codec, 0x10);
- if (err < 0) {
- ad198x_free(codec);
- return err;
- }
- set_beep_amp(spec, 0x10, 0, HDA_OUTPUT);
-
- spec->multiout.max_channels = 2;
- spec->multiout.num_dacs = ARRAY_SIZE(ad1884_dac_nids);
- spec->multiout.dac_nids = ad1884_dac_nids;
- spec->multiout.dig_out_nid = AD1884_SPDIF_OUT;
- spec->num_adc_nids = ARRAY_SIZE(ad1884_adc_nids);
- spec->adc_nids = ad1884_adc_nids;
- spec->capsrc_nids = ad1884_capsrc_nids;
- spec->input_mux = &ad1884_capture_source;
- spec->num_mixers = 1;
- spec->mixers[0] = ad1884_base_mixers;
- spec->num_init_verbs = 1;
- spec->init_verbs[0] = ad1884_init_verbs;
- spec->spdif_route = 0;
-#ifdef CONFIG_SND_HDA_POWER_SAVE
- spec->loopback.amplist = ad1884_loopbacks;
-#endif
- spec->vmaster_nid = 0x04;
- /* we need to cover all playback volumes */
- spec->slave_vols = ad1884_slave_vols;
-
- codec->patch_ops = ad198x_patch_ops;
-
- codec->no_trigger_sense = 1;
- codec->no_sticky_stream = 1;
-
- return 0;
-}
-
-/*
- * Lenovo Thinkpad T61/X61
- */
-static struct hda_input_mux ad1984_thinkpad_capture_source = {
- .num_items = 4,
- .items = {
- { "Mic", 0x0 },
- { "Internal Mic", 0x1 },
- { "Mix", 0x3 },
- { "Docking-Station", 0x4 },
- },
-};
-
-
-/*
- * Dell Precision T3400
- */
-static struct hda_input_mux ad1984_dell_desktop_capture_source = {
- .num_items = 3,
- .items = {
- { "Front Mic", 0x0 },
- { "Line-In", 0x1 },
- { "Mix", 0x3 },
- },
-};
-
-
-static struct snd_kcontrol_new ad1984_thinkpad_mixers[] = {
- HDA_CODEC_VOLUME("PCM Playback Volume", 0x04, 0x0, HDA_OUTPUT),
- /* HDA_CODEC_VOLUME_IDX("PCM Playback Volume", 1, 0x03, 0x0, HDA_OUTPUT), */
- HDA_CODEC_MUTE("Headphone Playback Switch", 0x11, 0x0, HDA_OUTPUT),
- HDA_CODEC_MUTE("Speaker Playback Switch", 0x12, 0x0, HDA_OUTPUT),
- HDA_CODEC_VOLUME("Mic Playback Volume", 0x20, 0x00, HDA_INPUT),
- HDA_CODEC_MUTE("Mic Playback Switch", 0x20, 0x00, HDA_INPUT),
- HDA_CODEC_VOLUME("Internal Mic Playback Volume", 0x20, 0x01, HDA_INPUT),
- HDA_CODEC_MUTE("Internal Mic Playback Switch", 0x20, 0x01, HDA_INPUT),
- HDA_CODEC_VOLUME("Beep Playback Volume", 0x20, 0x03, HDA_INPUT),
- HDA_CODEC_MUTE("Beep Playback Switch", 0x20, 0x03, HDA_INPUT),
- HDA_CODEC_VOLUME("Docking Mic Playback Volume", 0x20, 0x04, HDA_INPUT),
- HDA_CODEC_MUTE("Docking Mic Playback Switch", 0x20, 0x04, HDA_INPUT),
- HDA_CODEC_VOLUME("Mic Boost Volume", 0x14, 0x0, HDA_INPUT),
- HDA_CODEC_VOLUME("Internal Mic Boost Volume", 0x15, 0x0, HDA_INPUT),
- HDA_CODEC_VOLUME("Dock Mic Boost Volume", 0x25, 0x0, HDA_OUTPUT),
- HDA_CODEC_VOLUME("Capture Volume", 0x0c, 0x0, HDA_OUTPUT),
- HDA_CODEC_MUTE("Capture Switch", 0x0c, 0x0, HDA_OUTPUT),
- HDA_CODEC_VOLUME_IDX("Capture Volume", 1, 0x0d, 0x0, HDA_OUTPUT),
- HDA_CODEC_MUTE_IDX("Capture Switch", 1, 0x0d, 0x0, HDA_OUTPUT),
- {
- .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
- /* The multiple "Capture Source" controls confuse alsamixer
- * So call somewhat different..
- */
- /* .name = "Capture Source", */
- .name = "Input Source",
- .count = 2,
- .info = ad198x_mux_enum_info,
- .get = ad198x_mux_enum_get,
- .put = ad198x_mux_enum_put,
- },
- /* SPDIF controls */
- HDA_CODEC_VOLUME("IEC958 Playback Volume", 0x1b, 0x0, HDA_OUTPUT),
- {
- .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
- .name = SNDRV_CTL_NAME_IEC958("",PLAYBACK,NONE) "Source",
- /* identical with ad1983 */
- .info = ad1983_spdif_route_info,
- .get = ad1983_spdif_route_get,
- .put = ad1983_spdif_route_put,
- },
- { } /* end */
-};
-
-/* additional verbs */
-static struct hda_verb ad1984_thinkpad_init_verbs[] = {
- /* Port-E (docking station mic) pin */
- {0x1c, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80},
- {0x1c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
- /* docking mic boost */
- {0x25, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
- /* Analog PC Beeper - allow firmware/ACPI beeps */
- {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(3) | 0x1a},
- /* Analog mixer - docking mic; mute as default */
- {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(4)},
- /* enable EAPD bit */
- {0x12, AC_VERB_SET_EAPD_BTLENABLE, 0x02},
- { } /* end */
-};
-
-/*
- * Dell Precision T3400
- */
-static struct snd_kcontrol_new ad1984_dell_desktop_mixers[] = {
- HDA_CODEC_VOLUME("PCM Playback Volume", 0x04, 0x0, HDA_OUTPUT),
- HDA_CODEC_MUTE("Headphone Playback Switch", 0x11, 0x0, HDA_OUTPUT),
- HDA_CODEC_MUTE("Speaker Playback Switch", 0x12, 0x0, HDA_OUTPUT),
- HDA_CODEC_VOLUME_MONO("Mono Playback Volume", 0x13, 1, 0x0, HDA_OUTPUT),
- HDA_CODEC_MUTE_MONO("Mono Playback Switch", 0x13, 1, 0x0, HDA_OUTPUT),
- HDA_CODEC_VOLUME("Front Mic Playback Volume", 0x20, 0x00, HDA_INPUT),
- HDA_CODEC_MUTE("Front Mic Playback Switch", 0x20, 0x00, HDA_INPUT),
- HDA_CODEC_VOLUME("Line-In Playback Volume", 0x20, 0x01, HDA_INPUT),
- HDA_CODEC_MUTE("Line-In Playback Switch", 0x20, 0x01, HDA_INPUT),
- HDA_CODEC_VOLUME("Line-In Boost Volume", 0x15, 0x0, HDA_INPUT),
- HDA_CODEC_VOLUME("Front Mic Boost Volume", 0x14, 0x0, HDA_INPUT),
- HDA_CODEC_VOLUME("Capture Volume", 0x0c, 0x0, HDA_OUTPUT),
- HDA_CODEC_MUTE("Capture Switch", 0x0c, 0x0, HDA_OUTPUT),
- HDA_CODEC_VOLUME_IDX("Capture Volume", 1, 0x0d, 0x0, HDA_OUTPUT),
- HDA_CODEC_MUTE_IDX("Capture Switch", 1, 0x0d, 0x0, HDA_OUTPUT),
- {
- .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
- /* The multiple "Capture Source" controls confuse alsamixer
- * So call somewhat different..
- */
- /* .name = "Capture Source", */
- .name = "Input Source",
- .count = 2,
- .info = ad198x_mux_enum_info,
- .get = ad198x_mux_enum_get,
- .put = ad198x_mux_enum_put,
- },
- { } /* end */
-};
-
-/* Digial MIC ADC NID 0x05 + 0x06 */
-static int ad1984_pcm_dmic_prepare(struct hda_pcm_stream *hinfo,
- struct hda_codec *codec,
- unsigned int stream_tag,
- unsigned int format,
- struct snd_pcm_substream *substream)
-{
- snd_hda_codec_setup_stream(codec, 0x05 + substream->number,
- stream_tag, 0, format);
- return 0;
-}
-
-static int ad1984_pcm_dmic_cleanup(struct hda_pcm_stream *hinfo,
- struct hda_codec *codec,
- struct snd_pcm_substream *substream)
-{
- snd_hda_codec_cleanup_stream(codec, 0x05 + substream->number);
- return 0;
-}
-
-static struct hda_pcm_stream ad1984_pcm_dmic_capture = {
- .substreams = 2,
- .channels_min = 2,
- .channels_max = 2,
- .nid = 0x05,
- .ops = {
- .prepare = ad1984_pcm_dmic_prepare,
- .cleanup = ad1984_pcm_dmic_cleanup
- },
-};
-
-static int ad1984_build_pcms(struct hda_codec *codec)
-{
- struct ad198x_spec *spec = codec->spec;
- struct hda_pcm *info;
- int err;
-
- err = ad198x_build_pcms(codec);
- if (err < 0)
- return err;
-
- info = spec->pcm_rec + codec->num_pcms;
- codec->num_pcms++;
- info->name = "AD1984 Digital Mic";
- info->stream[SNDRV_PCM_STREAM_CAPTURE] = ad1984_pcm_dmic_capture;
- return 0;
-}
-
-/* models */
-enum {
- AD1984_BASIC,
- AD1984_THINKPAD,
- AD1984_DELL_DESKTOP,
- AD1984_MODELS
-};
-
-static const char * const ad1984_models[AD1984_MODELS] = {
- [AD1984_BASIC] = "basic",
- [AD1984_THINKPAD] = "thinkpad",
- [AD1984_DELL_DESKTOP] = "dell_desktop",
-};
-
-static struct snd_pci_quirk ad1984_cfg_tbl[] = {
- /* Lenovo Thinkpad T61/X61 */
- SND_PCI_QUIRK_VENDOR(0x17aa, "Lenovo Thinkpad", AD1984_THINKPAD),
- SND_PCI_QUIRK(0x1028, 0x0214, "Dell T3400", AD1984_DELL_DESKTOP),
- SND_PCI_QUIRK(0x1028, 0x0233, "Dell Latitude E6400", AD1984_DELL_DESKTOP),
- {}
-};
-
-static int patch_ad1984(struct hda_codec *codec)
-{
- struct ad198x_spec *spec;
- int board_config, err;
-
- err = patch_ad1884(codec);
- if (err < 0)
- return err;
- spec = codec->spec;
- board_config = snd_hda_check_board_config(codec, AD1984_MODELS,
- ad1984_models, ad1984_cfg_tbl);
- switch (board_config) {
- case AD1984_BASIC:
- /* additional digital mics */
- spec->mixers[spec->num_mixers++] = ad1984_dmic_mixers;
- codec->patch_ops.build_pcms = ad1984_build_pcms;
- break;
- case AD1984_THINKPAD:
- if (codec->subsystem_id == 0x17aa20fb) {
- /* Thinpad X300 does not have the ability to do SPDIF,
- or attach to docking station to use SPDIF */
- spec->multiout.dig_out_nid = 0;
- } else
- spec->multiout.dig_out_nid = AD1884_SPDIF_OUT;
- spec->input_mux = &ad1984_thinkpad_capture_source;
- spec->mixers[0] = ad1984_thinkpad_mixers;
- spec->init_verbs[spec->num_init_verbs++] = ad1984_thinkpad_init_verbs;
- spec->analog_beep = 1;
- break;
- case AD1984_DELL_DESKTOP:
- spec->multiout.dig_out_nid = 0;
- spec->input_mux = &ad1984_dell_desktop_capture_source;
- spec->mixers[0] = ad1984_dell_desktop_mixers;
- break;
- }
- return 0;
-}
-
-
-/*
* AD1883 / AD1884A / AD1984A / AD1984B
*
* port-B (0x14) - front mic-in
@@ -3782,825 +962,161 @@ static int patch_ad1984(struct hda_codec *codec)
* AD1984A = AD1884A + digital-mic
* AD1883 = equivalent with AD1984A
* AD1984B = AD1984A + extra SPDIF-out
- *
- * FIXME:
- * We share the single DAC for both HP and line-outs (see AD1884/1984).
- */
-
-static hda_nid_t ad1884a_dac_nids[1] = {
- 0x03,
-};
-
-#define ad1884a_adc_nids ad1884_adc_nids
-#define ad1884a_capsrc_nids ad1884_capsrc_nids
-
-#define AD1884A_SPDIF_OUT 0x02
-
-static struct hda_input_mux ad1884a_capture_source = {
- .num_items = 5,
- .items = {
- { "Front Mic", 0x0 },
- { "Mic", 0x4 },
- { "Line", 0x1 },
- { "CD", 0x2 },
- { "Mix", 0x3 },
- },
-};
-
-static struct snd_kcontrol_new ad1884a_base_mixers[] = {
- HDA_CODEC_VOLUME("Master Playback Volume", 0x21, 0x0, HDA_OUTPUT),
- HDA_CODEC_MUTE("Master Playback Switch", 0x21, 0x0, HDA_OUTPUT),
- HDA_CODEC_MUTE("Headphone Playback Switch", 0x11, 0x0, HDA_OUTPUT),
- HDA_CODEC_MUTE("Front Playback Switch", 0x12, 0x0, HDA_OUTPUT),
- HDA_CODEC_VOLUME_MONO("Mono Playback Volume", 0x13, 1, 0x0, HDA_OUTPUT),
- HDA_CODEC_MUTE_MONO("Mono Playback Switch", 0x13, 1, 0x0, HDA_OUTPUT),
- HDA_CODEC_VOLUME("PCM Playback Volume", 0x20, 0x5, HDA_INPUT),
- HDA_CODEC_MUTE("PCM Playback Switch", 0x20, 0x5, HDA_INPUT),
- HDA_CODEC_VOLUME("Front Mic Playback Volume", 0x20, 0x00, HDA_INPUT),
- HDA_CODEC_MUTE("Front Mic Playback Switch", 0x20, 0x00, HDA_INPUT),
- HDA_CODEC_VOLUME("Line Playback Volume", 0x20, 0x01, HDA_INPUT),
- HDA_CODEC_MUTE("Line Playback Switch", 0x20, 0x01, HDA_INPUT),
- HDA_CODEC_VOLUME("Mic Playback Volume", 0x20, 0x04, HDA_INPUT),
- HDA_CODEC_MUTE("Mic Playback Switch", 0x20, 0x04, HDA_INPUT),
- HDA_CODEC_VOLUME("CD Playback Volume", 0x20, 0x02, HDA_INPUT),
- HDA_CODEC_MUTE("CD Playback Switch", 0x20, 0x02, HDA_INPUT),
- HDA_CODEC_VOLUME("Front Mic Boost Volume", 0x14, 0x0, HDA_INPUT),
- HDA_CODEC_VOLUME("Line Boost Volume", 0x15, 0x0, HDA_INPUT),
- HDA_CODEC_VOLUME("Mic Boost Volume", 0x25, 0x0, HDA_OUTPUT),
- HDA_CODEC_VOLUME("Capture Volume", 0x0c, 0x0, HDA_OUTPUT),
- HDA_CODEC_MUTE("Capture Switch", 0x0c, 0x0, HDA_OUTPUT),
- HDA_CODEC_VOLUME_IDX("Capture Volume", 1, 0x0d, 0x0, HDA_OUTPUT),
- HDA_CODEC_MUTE_IDX("Capture Switch", 1, 0x0d, 0x0, HDA_OUTPUT),
- {
- .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
- /* The multiple "Capture Source" controls confuse alsamixer
- * So call somewhat different..
- */
- /* .name = "Capture Source", */
- .name = "Input Source",
- .count = 2,
- .info = ad198x_mux_enum_info,
- .get = ad198x_mux_enum_get,
- .put = ad198x_mux_enum_put,
- },
- /* SPDIF controls */
- HDA_CODEC_VOLUME("IEC958 Playback Volume", 0x1b, 0x0, HDA_OUTPUT),
- {
- .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
- .name = SNDRV_CTL_NAME_IEC958("",PLAYBACK,NONE) "Source",
- /* identical with ad1983 */
- .info = ad1983_spdif_route_info,
- .get = ad1983_spdif_route_get,
- .put = ad1983_spdif_route_put,
- },
- { } /* end */
-};
-
-/*
- * initialization verbs
*/
-static struct hda_verb ad1884a_init_verbs[] = {
- /* DACs; unmute as default */
- {0x03, AC_VERB_SET_AMP_GAIN_MUTE, 0x27}, /* 0dB */
- {0x04, AC_VERB_SET_AMP_GAIN_MUTE, 0x27}, /* 0dB */
- /* Port-A (HP) mixer - route only from analog mixer */
- {0x07, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
- {0x07, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
- /* Port-A pin */
- {0x11, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP},
- {0x11, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
- /* Port-D (Line-out) mixer - route only from analog mixer */
- {0x0a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
- {0x0a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
- /* Port-D pin */
- {0x12, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP},
- {0x12, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
- /* Mono-out mixer - route only from analog mixer */
- {0x1e, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
- {0x1e, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
- /* Mono-out pin */
- {0x13, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP},
- {0x13, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
- /* Port-B (front mic) pin */
- {0x14, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80},
- {0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
- /* Port-C (rear line-in) pin */
- {0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN},
- {0x15, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
- /* Port-E (rear mic) pin */
- {0x1c, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80},
- {0x1c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
- {0x25, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO}, /* no boost */
- /* Port-F (CD) pin */
- {0x16, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN},
- {0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
- /* Analog mixer; mute as default */
- {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
- {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
- {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(2)},
- {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(3)},
- {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(4)}, /* aux */
- {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(5)},
- /* Analog Mix output amp */
- {0x21, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
- /* capture sources */
- {0x0c, AC_VERB_SET_CONNECT_SEL, 0x0},
- {0x0c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
- {0x0d, AC_VERB_SET_CONNECT_SEL, 0x0},
- {0x0d, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
- /* SPDIF output amp */
- {0x1b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE | 0x27}, /* 0dB */
- { } /* end */
-};
-
-#ifdef CONFIG_SND_HDA_POWER_SAVE
-static struct hda_amp_list ad1884a_loopbacks[] = {
- { 0x20, HDA_INPUT, 0 }, /* Front Mic */
- { 0x20, HDA_INPUT, 1 }, /* Mic */
- { 0x20, HDA_INPUT, 2 }, /* CD */
- { 0x20, HDA_INPUT, 4 }, /* Docking */
- { } /* end */
-};
-#endif
-/*
- * Laptop model
- *
- * Port A: Headphone jack
- * Port B: MIC jack
- * Port C: Internal MIC
- * Port D: Dock Line Out (if enabled)
- * Port E: Dock Line In (if enabled)
- * Port F: Internal speakers
+/* set the upper-limit for mixer amp to 0dB for avoiding the possible
+ * damage by overloading
*/
-
-static int ad1884a_mobile_master_sw_put(struct snd_kcontrol *kcontrol,
- struct snd_ctl_elem_value *ucontrol)
+static void ad1884_fixup_amp_override(struct hda_codec *codec,
+ const struct hda_fixup *fix, int action)
{
- struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
- int ret = snd_hda_mixer_amp_switch_put(kcontrol, ucontrol);
- int mute = (!ucontrol->value.integer.value[0] &&
- !ucontrol->value.integer.value[1]);
- /* toggle GPIO1 according to the mute state */
- snd_hda_codec_write_cache(codec, 0x01, 0, AC_VERB_SET_GPIO_DATA,
- mute ? 0x02 : 0x0);
- return ret;
+ if (action == HDA_FIXUP_ACT_PRE_PROBE)
+ snd_hda_override_amp_caps(codec, 0x20, HDA_INPUT,
+ (0x17 << AC_AMPCAP_OFFSET_SHIFT) |
+ (0x17 << AC_AMPCAP_NUM_STEPS_SHIFT) |
+ (0x05 << AC_AMPCAP_STEP_SIZE_SHIFT) |
+ (1 << AC_AMPCAP_MUTE_SHIFT));
}
-static struct snd_kcontrol_new ad1884a_laptop_mixers[] = {
- HDA_CODEC_VOLUME("Master Playback Volume", 0x21, 0x0, HDA_OUTPUT),
- {
- .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
- .name = "Master Playback Switch",
- .subdevice = HDA_SUBDEV_AMP_FLAG,
- .info = snd_hda_mixer_amp_switch_info,
- .get = snd_hda_mixer_amp_switch_get,
- .put = ad1884a_mobile_master_sw_put,
- .private_value = HDA_COMPOSE_AMP_VAL(0x21, 3, 0, HDA_OUTPUT),
- },
- HDA_CODEC_MUTE("Dock Playback Switch", 0x12, 0x0, HDA_OUTPUT),
- HDA_CODEC_VOLUME("PCM Playback Volume", 0x20, 0x5, HDA_INPUT),
- HDA_CODEC_MUTE("PCM Playback Switch", 0x20, 0x5, HDA_INPUT),
- HDA_CODEC_VOLUME("Mic Playback Volume", 0x20, 0x00, HDA_INPUT),
- HDA_CODEC_MUTE("Mic Playback Switch", 0x20, 0x00, HDA_INPUT),
- HDA_CODEC_VOLUME("Internal Mic Playback Volume", 0x20, 0x01, HDA_INPUT),
- HDA_CODEC_MUTE("Internal Mic Playback Switch", 0x20, 0x01, HDA_INPUT),
- HDA_CODEC_VOLUME("Dock Mic Playback Volume", 0x20, 0x04, HDA_INPUT),
- HDA_CODEC_MUTE("Dock Mic Playback Switch", 0x20, 0x04, HDA_INPUT),
- HDA_CODEC_VOLUME("Mic Boost Volume", 0x14, 0x0, HDA_INPUT),
- HDA_CODEC_VOLUME("Internal Mic Boost Volume", 0x15, 0x0, HDA_INPUT),
- HDA_CODEC_VOLUME("Dock Mic Boost Volume", 0x25, 0x0, HDA_OUTPUT),
- HDA_CODEC_VOLUME("Capture Volume", 0x0c, 0x0, HDA_OUTPUT),
- HDA_CODEC_MUTE("Capture Switch", 0x0c, 0x0, HDA_OUTPUT),
- { } /* end */
-};
-
-static struct snd_kcontrol_new ad1884a_mobile_mixers[] = {
- HDA_CODEC_VOLUME("Master Playback Volume", 0x21, 0x0, HDA_OUTPUT),
- /*HDA_CODEC_MUTE("Master Playback Switch", 0x21, 0x0, HDA_OUTPUT),*/
- {
- .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
- .name = "Master Playback Switch",
- .subdevice = HDA_SUBDEV_AMP_FLAG,
- .info = snd_hda_mixer_amp_switch_info,
- .get = snd_hda_mixer_amp_switch_get,
- .put = ad1884a_mobile_master_sw_put,
- .private_value = HDA_COMPOSE_AMP_VAL(0x21, 3, 0, HDA_OUTPUT),
- },
- HDA_CODEC_VOLUME("PCM Playback Volume", 0x20, 0x5, HDA_INPUT),
- HDA_CODEC_MUTE("PCM Playback Switch", 0x20, 0x5, HDA_INPUT),
- HDA_CODEC_VOLUME("Mic Capture Volume", 0x14, 0x0, HDA_INPUT),
- HDA_CODEC_VOLUME("Internal Mic Capture Volume", 0x15, 0x0, HDA_INPUT),
- HDA_CODEC_VOLUME("Capture Volume", 0x0c, 0x0, HDA_OUTPUT),
- HDA_CODEC_MUTE("Capture Switch", 0x0c, 0x0, HDA_OUTPUT),
- { } /* end */
-};
-
-/* mute internal speaker if HP is plugged */
-static void ad1884a_hp_automute(struct hda_codec *codec)
+/* toggle GPIO1 according to the mute state */
+static void ad1884_vmaster_hp_gpio_hook(void *private_data, int enabled)
{
- unsigned int present;
+ struct hda_codec *codec = private_data;
+ struct ad198x_spec *spec = codec->spec;
- present = snd_hda_jack_detect(codec, 0x11);
- snd_hda_codec_amp_stereo(codec, 0x16, HDA_OUTPUT, 0,
- HDA_AMP_MUTE, present ? HDA_AMP_MUTE : 0);
- snd_hda_codec_write(codec, 0x16, 0, AC_VERB_SET_EAPD_BTLENABLE,
- present ? 0x00 : 0x02);
+ if (spec->eapd_nid)
+ ad_vmaster_eapd_hook(private_data, enabled);
+ snd_hda_codec_update_cache(codec, 0x01, 0,
+ AC_VERB_SET_GPIO_DATA,
+ enabled ? 0x00 : 0x02);
}
-/* switch to external mic if plugged */
-static void ad1884a_hp_automic(struct hda_codec *codec)
+static void ad1884_fixup_hp_eapd(struct hda_codec *codec,
+ const struct hda_fixup *fix, int action)
{
- unsigned int present;
-
- present = snd_hda_jack_detect(codec, 0x14);
- snd_hda_codec_write(codec, 0x0c, 0, AC_VERB_SET_CONNECT_SEL,
- present ? 0 : 1);
-}
-
-#define AD1884A_HP_EVENT 0x37
-#define AD1884A_MIC_EVENT 0x36
+ struct ad198x_spec *spec = codec->spec;
+ static const struct hda_verb gpio_init_verbs[] = {
+ {0x01, AC_VERB_SET_GPIO_MASK, 0x02},
+ {0x01, AC_VERB_SET_GPIO_DIRECTION, 0x02},
+ {0x01, AC_VERB_SET_GPIO_DATA, 0x02},
+ {},
+ };
-/* unsolicited event for HP jack sensing */
-static void ad1884a_hp_unsol_event(struct hda_codec *codec, unsigned int res)
-{
- switch (res >> 26) {
- case AD1884A_HP_EVENT:
- ad1884a_hp_automute(codec);
+ switch (action) {
+ case HDA_FIXUP_ACT_PRE_PROBE:
+ spec->gen.vmaster_mute.hook = ad1884_vmaster_hp_gpio_hook;
+ spec->gen.own_eapd_ctl = 1;
+ snd_hda_sequence_write_cache(codec, gpio_init_verbs);
break;
- case AD1884A_MIC_EVENT:
- ad1884a_hp_automic(codec);
+ case HDA_FIXUP_ACT_PROBE:
+ if (spec->gen.autocfg.line_out_type == AUTO_PIN_SPEAKER_OUT)
+ spec->eapd_nid = spec->gen.autocfg.line_out_pins[0];
+ else
+ spec->eapd_nid = spec->gen.autocfg.speaker_pins[0];
break;
}
}
-/* initialize jack-sensing, too */
-static int ad1884a_hp_init(struct hda_codec *codec)
-{
- ad198x_init(codec);
- ad1884a_hp_automute(codec);
- ad1884a_hp_automic(codec);
- return 0;
-}
-
-/* mute internal speaker if HP or docking HP is plugged */
-static void ad1884a_laptop_automute(struct hda_codec *codec)
+static void ad1884_fixup_thinkpad(struct hda_codec *codec,
+ const struct hda_fixup *fix, int action)
{
- unsigned int present;
-
- present = snd_hda_jack_detect(codec, 0x11);
- if (!present)
- present = snd_hda_jack_detect(codec, 0x12);
- snd_hda_codec_amp_stereo(codec, 0x16, HDA_OUTPUT, 0,
- HDA_AMP_MUTE, present ? HDA_AMP_MUTE : 0);
- snd_hda_codec_write(codec, 0x16, 0, AC_VERB_SET_EAPD_BTLENABLE,
- present ? 0x00 : 0x02);
-}
-
-/* switch to external mic if plugged */
-static void ad1884a_laptop_automic(struct hda_codec *codec)
-{
- unsigned int idx;
-
- if (snd_hda_jack_detect(codec, 0x14))
- idx = 0;
- else if (snd_hda_jack_detect(codec, 0x1c))
- idx = 4;
- else
- idx = 1;
- snd_hda_codec_write(codec, 0x0c, 0, AC_VERB_SET_CONNECT_SEL, idx);
-}
+ struct ad198x_spec *spec = codec->spec;
-/* unsolicited event for HP jack sensing */
-static void ad1884a_laptop_unsol_event(struct hda_codec *codec,
- unsigned int res)
-{
- switch (res >> 26) {
- case AD1884A_HP_EVENT:
- ad1884a_laptop_automute(codec);
- break;
- case AD1884A_MIC_EVENT:
- ad1884a_laptop_automic(codec);
- break;
+ if (action == HDA_FIXUP_ACT_PRE_PROBE) {
+ spec->gen.keep_eapd_on = 1;
+ spec->gen.vmaster_mute.hook = ad_vmaster_eapd_hook;
+ spec->eapd_nid = 0x12;
+ /* Analog PC Beeper - allow firmware/ACPI beeps */
+ spec->beep_amp = HDA_COMPOSE_AMP_VAL(0x20, 3, 3, HDA_INPUT);
+ spec->gen.beep_nid = 0; /* no digital beep */
}
}
-/* initialize jack-sensing, too */
-static int ad1884a_laptop_init(struct hda_codec *codec)
-{
- ad198x_init(codec);
- ad1884a_laptop_automute(codec);
- ad1884a_laptop_automic(codec);
- return 0;
-}
-
-/* additional verbs for laptop model */
-static struct hda_verb ad1884a_laptop_verbs[] = {
- /* Port-A (HP) pin - always unmuted */
- {0x11, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
- /* Port-F (int speaker) mixer - route only from analog mixer */
- {0x0b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
- {0x0b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
- /* Port-F (int speaker) pin */
- {0x16, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
- {0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
- /* required for compaq 6530s/6531s speaker output */
- {0x1c, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
- /* Port-C pin - internal mic-in */
- {0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80},
- {0x14, AC_VERB_SET_AMP_GAIN_MUTE, 0x7002}, /* raise mic as default */
- {0x15, AC_VERB_SET_AMP_GAIN_MUTE, 0x7002}, /* raise mic as default */
- /* Port-D (docking line-out) pin - default unmuted */
- {0x12, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
- /* analog mix */
- {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(4)},
- /* unsolicited event for pin-sense */
- {0x11, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | AD1884A_HP_EVENT},
- {0x12, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | AD1884A_HP_EVENT},
- {0x14, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | AD1884A_MIC_EVENT},
- {0x1c, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | AD1884A_MIC_EVENT},
- /* allow to touch GPIO1 (for mute control) */
- {0x01, AC_VERB_SET_GPIO_MASK, 0x02},
- {0x01, AC_VERB_SET_GPIO_DIRECTION, 0x02},
- {0x01, AC_VERB_SET_GPIO_DATA, 0x02}, /* first muted */
- { } /* end */
-};
-
-static struct hda_verb ad1884a_mobile_verbs[] = {
- /* DACs; unmute as default */
- {0x03, AC_VERB_SET_AMP_GAIN_MUTE, 0x27}, /* 0dB */
- {0x04, AC_VERB_SET_AMP_GAIN_MUTE, 0x27}, /* 0dB */
- /* Port-A (HP) mixer - route only from analog mixer */
- {0x07, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
- {0x07, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
- /* Port-A pin */
- {0x11, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP},
- /* Port-A (HP) pin - always unmuted */
- {0x11, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
- /* Port-B (mic jack) pin */
- {0x14, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80},
- {0x14, AC_VERB_SET_AMP_GAIN_MUTE, 0x7002}, /* raise mic as default */
- /* Port-C (int mic) pin */
- {0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80},
- {0x15, AC_VERB_SET_AMP_GAIN_MUTE, 0x7002}, /* raise mic as default */
- /* Port-F (int speaker) mixer - route only from analog mixer */
- {0x0b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
- {0x0b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
- /* Port-F pin */
- {0x16, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP},
- {0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
- /* Analog mixer; mute as default */
- {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
- {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
- {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(2)},
- {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(3)},
- {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(4)},
- {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(5)},
- /* Analog Mix output amp */
- {0x21, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
- /* capture sources */
- /* {0x0c, AC_VERB_SET_CONNECT_SEL, 0x0}, */ /* set via unsol */
- {0x0c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
- {0x0d, AC_VERB_SET_CONNECT_SEL, 0x0},
- {0x0d, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
- /* unsolicited event for pin-sense */
- {0x11, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | AD1884A_HP_EVENT},
- {0x14, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | AD1884A_MIC_EVENT},
- /* allow to touch GPIO1 (for mute control) */
- {0x01, AC_VERB_SET_GPIO_MASK, 0x02},
- {0x01, AC_VERB_SET_GPIO_DIRECTION, 0x02},
- {0x01, AC_VERB_SET_GPIO_DATA, 0x02}, /* first muted */
- { } /* end */
-};
-
-/*
- * Thinkpad X300
- * 0x11 - HP
- * 0x12 - speaker
- * 0x14 - mic-in
- * 0x17 - built-in mic
- */
-
-static struct hda_verb ad1984a_thinkpad_verbs[] = {
- /* HP unmute */
- {0x11, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
- /* analog mix */
- {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(4)},
- /* turn on EAPD */
- {0x12, AC_VERB_SET_EAPD_BTLENABLE, 0x02},
- /* unsolicited event for pin-sense */
- {0x11, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | AD1884A_HP_EVENT},
- /* internal mic - dmic */
- {0x17, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN},
- /* set magic COEFs for dmic */
+/* set magic COEFs for dmic */
+static const struct hda_verb ad1884_dmic_init_verbs[] = {
{0x01, AC_VERB_SET_COEF_INDEX, 0x13f7},
{0x01, AC_VERB_SET_PROC_COEF, 0x08},
- { } /* end */
+ {}
};
-static struct snd_kcontrol_new ad1984a_thinkpad_mixers[] = {
- HDA_CODEC_VOLUME("Master Playback Volume", 0x21, 0x0, HDA_OUTPUT),
- HDA_CODEC_MUTE("Master Playback Switch", 0x21, 0x0, HDA_OUTPUT),
- HDA_CODEC_VOLUME("PCM Playback Volume", 0x20, 0x5, HDA_INPUT),
- HDA_CODEC_MUTE("PCM Playback Switch", 0x20, 0x5, HDA_INPUT),
- HDA_CODEC_VOLUME("Mic Playback Volume", 0x20, 0x00, HDA_INPUT),
- HDA_CODEC_MUTE("Mic Playback Switch", 0x20, 0x00, HDA_INPUT),
- HDA_CODEC_VOLUME("Mic Boost Volume", 0x14, 0x0, HDA_INPUT),
- HDA_CODEC_VOLUME("Internal Mic Boost Volume", 0x17, 0x0, HDA_INPUT),
- HDA_CODEC_VOLUME("Capture Volume", 0x0c, 0x0, HDA_OUTPUT),
- HDA_CODEC_MUTE("Capture Switch", 0x0c, 0x0, HDA_OUTPUT),
- {
- .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
- .name = "Capture Source",
- .info = ad198x_mux_enum_info,
- .get = ad198x_mux_enum_get,
- .put = ad198x_mux_enum_put,
- },
- { } /* end */
+enum {
+ AD1884_FIXUP_AMP_OVERRIDE,
+ AD1884_FIXUP_HP_EAPD,
+ AD1884_FIXUP_DMIC_COEF,
+ AD1884_FIXUP_THINKPAD,
+ AD1884_FIXUP_HP_TOUCHSMART,
};
-static struct hda_input_mux ad1984a_thinkpad_capture_source = {
- .num_items = 3,
- .items = {
- { "Mic", 0x0 },
- { "Internal Mic", 0x5 },
- { "Mix", 0x3 },
+static const struct hda_fixup ad1884_fixups[] = {
+ [AD1884_FIXUP_AMP_OVERRIDE] = {
+ .type = HDA_FIXUP_FUNC,
+ .v.func = ad1884_fixup_amp_override,
},
-};
-
-/* mute internal speaker if HP is plugged */
-static void ad1984a_thinkpad_automute(struct hda_codec *codec)
-{
- unsigned int present;
-
- present = snd_hda_jack_detect(codec, 0x11);
- snd_hda_codec_amp_stereo(codec, 0x12, HDA_OUTPUT, 0,
- HDA_AMP_MUTE, present ? HDA_AMP_MUTE : 0);
-}
-
-/* unsolicited event for HP jack sensing */
-static void ad1984a_thinkpad_unsol_event(struct hda_codec *codec,
- unsigned int res)
-{
- if ((res >> 26) != AD1884A_HP_EVENT)
- return;
- ad1984a_thinkpad_automute(codec);
-}
-
-/* initialize jack-sensing, too */
-static int ad1984a_thinkpad_init(struct hda_codec *codec)
-{
- ad198x_init(codec);
- ad1984a_thinkpad_automute(codec);
- return 0;
-}
-
-/*
- * Precision R5500
- * 0x12 - HP/line-out
- * 0x13 - speaker (mono)
- * 0x15 - mic-in
- */
-
-static struct hda_verb ad1984a_precision_verbs[] = {
- /* Unmute main output path */
- {0x03, AC_VERB_SET_AMP_GAIN_MUTE, 0x27}, /* 0dB */
- {0x21, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE + 0x1f}, /* 0dB */
- {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(5) + 0x17}, /* 0dB */
- /* Analog mixer; mute as default */
- {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
- {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
- {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(2)},
- {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(3)},
- {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(4)},
- /* Select mic as input */
- {0x0c, AC_VERB_SET_CONNECT_SEL, 0x1},
- {0x0c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE + 0x27}, /* 0dB */
- /* Configure as mic */
- {0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80},
- {0x15, AC_VERB_SET_AMP_GAIN_MUTE, 0x7002}, /* raise mic as default */
- /* HP unmute */
- {0x12, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
- /* turn on EAPD */
- {0x13, AC_VERB_SET_EAPD_BTLENABLE, 0x02},
- /* unsolicited event for pin-sense */
- {0x12, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | AD1884A_HP_EVENT},
- { } /* end */
-};
-
-static struct snd_kcontrol_new ad1984a_precision_mixers[] = {
- HDA_CODEC_VOLUME("Master Playback Volume", 0x21, 0x0, HDA_OUTPUT),
- HDA_CODEC_MUTE("Master Playback Switch", 0x21, 0x0, HDA_OUTPUT),
- HDA_CODEC_VOLUME("PCM Playback Volume", 0x20, 0x5, HDA_INPUT),
- HDA_CODEC_MUTE("PCM Playback Switch", 0x20, 0x5, HDA_INPUT),
- HDA_CODEC_VOLUME("Mic Playback Volume", 0x20, 0x01, HDA_INPUT),
- HDA_CODEC_MUTE("Mic Playback Switch", 0x20, 0x01, HDA_INPUT),
- HDA_CODEC_VOLUME("Mic Boost Volume", 0x15, 0x0, HDA_INPUT),
- HDA_CODEC_MUTE("Front Playback Switch", 0x12, 0x0, HDA_OUTPUT),
- HDA_CODEC_VOLUME("Speaker Playback Volume", 0x13, 0x0, HDA_OUTPUT),
- HDA_CODEC_VOLUME("Capture Volume", 0x0c, 0x0, HDA_OUTPUT),
- HDA_CODEC_MUTE("Capture Switch", 0x0c, 0x0, HDA_OUTPUT),
- { } /* end */
-};
-
-
-/* mute internal speaker if HP is plugged */
-static void ad1984a_precision_automute(struct hda_codec *codec)
-{
- unsigned int present;
-
- present = snd_hda_jack_detect(codec, 0x12);
- snd_hda_codec_amp_stereo(codec, 0x13, HDA_OUTPUT, 0,
- HDA_AMP_MUTE, present ? HDA_AMP_MUTE : 0);
-}
-
-
-/* unsolicited event for HP jack sensing */
-static void ad1984a_precision_unsol_event(struct hda_codec *codec,
- unsigned int res)
-{
- if ((res >> 26) != AD1884A_HP_EVENT)
- return;
- ad1984a_precision_automute(codec);
-}
-
-/* initialize jack-sensing, too */
-static int ad1984a_precision_init(struct hda_codec *codec)
-{
- ad198x_init(codec);
- ad1984a_precision_automute(codec);
- return 0;
-}
-
-
-/*
- * HP Touchsmart
- * port-A (0x11) - front hp-out
- * port-B (0x14) - unused
- * port-C (0x15) - unused
- * port-D (0x12) - rear line out
- * port-E (0x1c) - front mic-in
- * port-F (0x16) - Internal speakers
- * digital-mic (0x17) - Internal mic
- */
-
-static struct hda_verb ad1984a_touchsmart_verbs[] = {
- /* DACs; unmute as default */
- {0x03, AC_VERB_SET_AMP_GAIN_MUTE, 0x27}, /* 0dB */
- {0x04, AC_VERB_SET_AMP_GAIN_MUTE, 0x27}, /* 0dB */
- /* Port-A (HP) mixer - route only from analog mixer */
- {0x07, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
- {0x07, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
- /* Port-A pin */
- {0x11, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP},
- /* Port-A (HP) pin - always unmuted */
- {0x11, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
- /* Port-E (int speaker) mixer - route only from analog mixer */
- {0x25, AC_VERB_SET_AMP_GAIN_MUTE, 0x03},
- /* Port-E pin */
- {0x1c, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN},
- {0x1c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
- {0x1c, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80},
- /* Port-F (int speaker) mixer - route only from analog mixer */
- {0x0b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
- {0x0b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
- /* Port-F pin */
- {0x16, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP},
- {0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
- /* Analog mixer; mute as default */
- {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
- {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
- {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(2)},
- {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(3)},
- {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(4)},
- {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(5)},
- /* Analog Mix output amp */
- {0x21, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
- /* capture sources */
- /* {0x0c, AC_VERB_SET_CONNECT_SEL, 0x0}, */ /* set via unsol */
- {0x0c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
- {0x0d, AC_VERB_SET_CONNECT_SEL, 0x0},
- {0x0d, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
- /* unsolicited event for pin-sense */
- {0x11, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | AD1884A_HP_EVENT},
- {0x1c, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | AD1884A_MIC_EVENT},
- /* allow to touch GPIO1 (for mute control) */
- {0x01, AC_VERB_SET_GPIO_MASK, 0x02},
- {0x01, AC_VERB_SET_GPIO_DIRECTION, 0x02},
- {0x01, AC_VERB_SET_GPIO_DATA, 0x02}, /* first muted */
- /* internal mic - dmic */
- {0x17, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN},
- /* set magic COEFs for dmic */
- {0x01, AC_VERB_SET_COEF_INDEX, 0x13f7},
- {0x01, AC_VERB_SET_PROC_COEF, 0x08},
- { } /* end */
-};
-
-static struct snd_kcontrol_new ad1984a_touchsmart_mixers[] = {
- HDA_CODEC_VOLUME("Master Playback Volume", 0x21, 0x0, HDA_OUTPUT),
-/* HDA_CODEC_MUTE("Master Playback Switch", 0x21, 0x0, HDA_OUTPUT),*/
- {
- .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
- .subdevice = HDA_SUBDEV_AMP_FLAG,
- .name = "Master Playback Switch",
- .info = snd_hda_mixer_amp_switch_info,
- .get = snd_hda_mixer_amp_switch_get,
- .put = ad1884a_mobile_master_sw_put,
- .private_value = HDA_COMPOSE_AMP_VAL(0x21, 3, 0, HDA_OUTPUT),
+ [AD1884_FIXUP_HP_EAPD] = {
+ .type = HDA_FIXUP_FUNC,
+ .v.func = ad1884_fixup_hp_eapd,
+ .chained = true,
+ .chain_id = AD1884_FIXUP_AMP_OVERRIDE,
+ },
+ [AD1884_FIXUP_DMIC_COEF] = {
+ .type = HDA_FIXUP_VERBS,
+ .v.verbs = ad1884_dmic_init_verbs,
+ },
+ [AD1884_FIXUP_THINKPAD] = {
+ .type = HDA_FIXUP_FUNC,
+ .v.func = ad1884_fixup_thinkpad,
+ .chained = true,
+ .chain_id = AD1884_FIXUP_DMIC_COEF,
+ },
+ [AD1884_FIXUP_HP_TOUCHSMART] = {
+ .type = HDA_FIXUP_VERBS,
+ .v.verbs = ad1884_dmic_init_verbs,
+ .chained = true,
+ .chain_id = AD1884_FIXUP_HP_EAPD,
},
- HDA_CODEC_VOLUME("PCM Playback Volume", 0x20, 0x5, HDA_INPUT),
- HDA_CODEC_MUTE("PCM Playback Switch", 0x20, 0x5, HDA_INPUT),
- HDA_CODEC_VOLUME("Capture Volume", 0x0c, 0x0, HDA_OUTPUT),
- HDA_CODEC_MUTE("Capture Switch", 0x0c, 0x0, HDA_OUTPUT),
- HDA_CODEC_VOLUME("Mic Boost Volume", 0x25, 0x0, HDA_OUTPUT),
- HDA_CODEC_VOLUME("Internal Mic Boost Volume", 0x17, 0x0, HDA_INPUT),
- { } /* end */
-};
-
-/* switch to external mic if plugged */
-static void ad1984a_touchsmart_automic(struct hda_codec *codec)
-{
- if (snd_hda_jack_detect(codec, 0x1c))
- snd_hda_codec_write(codec, 0x0c, 0,
- AC_VERB_SET_CONNECT_SEL, 0x4);
- else
- snd_hda_codec_write(codec, 0x0c, 0,
- AC_VERB_SET_CONNECT_SEL, 0x5);
-}
-
-
-/* unsolicited event for HP jack sensing */
-static void ad1984a_touchsmart_unsol_event(struct hda_codec *codec,
- unsigned int res)
-{
- switch (res >> 26) {
- case AD1884A_HP_EVENT:
- ad1884a_hp_automute(codec);
- break;
- case AD1884A_MIC_EVENT:
- ad1984a_touchsmart_automic(codec);
- break;
- }
-}
-
-/* initialize jack-sensing, too */
-static int ad1984a_touchsmart_init(struct hda_codec *codec)
-{
- ad198x_init(codec);
- ad1884a_hp_automute(codec);
- ad1984a_touchsmart_automic(codec);
- return 0;
-}
-
-
-/*
- */
-
-enum {
- AD1884A_DESKTOP,
- AD1884A_LAPTOP,
- AD1884A_MOBILE,
- AD1884A_THINKPAD,
- AD1984A_TOUCHSMART,
- AD1984A_PRECISION,
- AD1884A_MODELS
-};
-
-static const char * const ad1884a_models[AD1884A_MODELS] = {
- [AD1884A_DESKTOP] = "desktop",
- [AD1884A_LAPTOP] = "laptop",
- [AD1884A_MOBILE] = "mobile",
- [AD1884A_THINKPAD] = "thinkpad",
- [AD1984A_TOUCHSMART] = "touchsmart",
- [AD1984A_PRECISION] = "precision",
};
-static struct snd_pci_quirk ad1884a_cfg_tbl[] = {
- SND_PCI_QUIRK(0x1028, 0x04ac, "Precision R5500", AD1984A_PRECISION),
- SND_PCI_QUIRK(0x103c, 0x3030, "HP", AD1884A_MOBILE),
- SND_PCI_QUIRK(0x103c, 0x3037, "HP 2230s", AD1884A_LAPTOP),
- SND_PCI_QUIRK(0x103c, 0x3056, "HP", AD1884A_MOBILE),
- SND_PCI_QUIRK_MASK(0x103c, 0xfff0, 0x3070, "HP", AD1884A_MOBILE),
- SND_PCI_QUIRK_MASK(0x103c, 0xfff0, 0x30d0, "HP laptop", AD1884A_LAPTOP),
- SND_PCI_QUIRK_MASK(0x103c, 0xfff0, 0x30e0, "HP laptop", AD1884A_LAPTOP),
- SND_PCI_QUIRK_MASK(0x103c, 0xff00, 0x3600, "HP laptop", AD1884A_LAPTOP),
- SND_PCI_QUIRK_MASK(0x103c, 0xfff0, 0x7010, "HP laptop", AD1884A_MOBILE),
- SND_PCI_QUIRK(0x17aa, 0x20ac, "Thinkpad X300", AD1884A_THINKPAD),
- SND_PCI_QUIRK(0x103c, 0x2a82, "Touchsmart", AD1984A_TOUCHSMART),
+static const struct snd_pci_quirk ad1884_fixup_tbl[] = {
+ SND_PCI_QUIRK(0x103c, 0x2a82, "HP Touchsmart", AD1884_FIXUP_HP_TOUCHSMART),
+ SND_PCI_QUIRK_VENDOR(0x103c, "HP", AD1884_FIXUP_HP_EAPD),
+ SND_PCI_QUIRK_VENDOR(0x17aa, "Lenovo Thinkpad", AD1884_FIXUP_THINKPAD),
{}
};
-static int patch_ad1884a(struct hda_codec *codec)
+
+static int patch_ad1884(struct hda_codec *codec)
{
struct ad198x_spec *spec;
- int err, board_config;
-
- spec = kzalloc(sizeof(*spec), GFP_KERNEL);
- if (spec == NULL)
- return -ENOMEM;
-
- codec->spec = spec;
+ int err;
- err = snd_hda_attach_beep_device(codec, 0x10);
- if (err < 0) {
- ad198x_free(codec);
+ err = alloc_ad_spec(codec);
+ if (err < 0)
return err;
- }
+ spec = codec->spec;
+
+ spec->gen.mixer_nid = 0x20;
+ spec->gen.mixer_merge_nid = 0x21;
+ spec->gen.beep_nid = 0x10;
set_beep_amp(spec, 0x10, 0, HDA_OUTPUT);
- spec->multiout.max_channels = 2;
- spec->multiout.num_dacs = ARRAY_SIZE(ad1884a_dac_nids);
- spec->multiout.dac_nids = ad1884a_dac_nids;
- spec->multiout.dig_out_nid = AD1884A_SPDIF_OUT;
- spec->num_adc_nids = ARRAY_SIZE(ad1884a_adc_nids);
- spec->adc_nids = ad1884a_adc_nids;
- spec->capsrc_nids = ad1884a_capsrc_nids;
- spec->input_mux = &ad1884a_capture_source;
- spec->num_mixers = 1;
- spec->mixers[0] = ad1884a_base_mixers;
- spec->num_init_verbs = 1;
- spec->init_verbs[0] = ad1884a_init_verbs;
- spec->spdif_route = 0;
-#ifdef CONFIG_SND_HDA_POWER_SAVE
- spec->loopback.amplist = ad1884a_loopbacks;
-#endif
- codec->patch_ops = ad198x_patch_ops;
-
- /* override some parameters */
- board_config = snd_hda_check_board_config(codec, AD1884A_MODELS,
- ad1884a_models,
- ad1884a_cfg_tbl);
- switch (board_config) {
- case AD1884A_LAPTOP:
- spec->mixers[0] = ad1884a_laptop_mixers;
- spec->init_verbs[spec->num_init_verbs++] = ad1884a_laptop_verbs;
- spec->multiout.dig_out_nid = 0;
- codec->patch_ops.unsol_event = ad1884a_laptop_unsol_event;
- codec->patch_ops.init = ad1884a_laptop_init;
- /* set the upper-limit for mixer amp to 0dB for avoiding the
- * possible damage by overloading
- */
- snd_hda_override_amp_caps(codec, 0x20, HDA_INPUT,
- (0x17 << AC_AMPCAP_OFFSET_SHIFT) |
- (0x17 << AC_AMPCAP_NUM_STEPS_SHIFT) |
- (0x05 << AC_AMPCAP_STEP_SIZE_SHIFT) |
- (1 << AC_AMPCAP_MUTE_SHIFT));
- break;
- case AD1884A_MOBILE:
- spec->mixers[0] = ad1884a_mobile_mixers;
- spec->init_verbs[0] = ad1884a_mobile_verbs;
- spec->multiout.dig_out_nid = 0;
- codec->patch_ops.unsol_event = ad1884a_hp_unsol_event;
- codec->patch_ops.init = ad1884a_hp_init;
- /* set the upper-limit for mixer amp to 0dB for avoiding the
- * possible damage by overloading
- */
- snd_hda_override_amp_caps(codec, 0x20, HDA_INPUT,
- (0x17 << AC_AMPCAP_OFFSET_SHIFT) |
- (0x17 << AC_AMPCAP_NUM_STEPS_SHIFT) |
- (0x05 << AC_AMPCAP_STEP_SIZE_SHIFT) |
- (1 << AC_AMPCAP_MUTE_SHIFT));
- break;
- case AD1884A_THINKPAD:
- spec->mixers[0] = ad1984a_thinkpad_mixers;
- spec->init_verbs[spec->num_init_verbs++] =
- ad1984a_thinkpad_verbs;
- spec->multiout.dig_out_nid = 0;
- spec->input_mux = &ad1984a_thinkpad_capture_source;
- codec->patch_ops.unsol_event = ad1984a_thinkpad_unsol_event;
- codec->patch_ops.init = ad1984a_thinkpad_init;
- break;
- case AD1984A_PRECISION:
- spec->mixers[0] = ad1984a_precision_mixers;
- spec->init_verbs[spec->num_init_verbs++] =
- ad1984a_precision_verbs;
- spec->multiout.dig_out_nid = 0;
- codec->patch_ops.unsol_event = ad1984a_precision_unsol_event;
- codec->patch_ops.init = ad1984a_precision_init;
- break;
- case AD1984A_TOUCHSMART:
- spec->mixers[0] = ad1984a_touchsmart_mixers;
- spec->init_verbs[0] = ad1984a_touchsmart_verbs;
- spec->multiout.dig_out_nid = 0;
- codec->patch_ops.unsol_event = ad1984a_touchsmart_unsol_event;
- codec->patch_ops.init = ad1984a_touchsmart_init;
- /* set the upper-limit for mixer amp to 0dB for avoiding the
- * possible damage by overloading
- */
- snd_hda_override_amp_caps(codec, 0x20, HDA_INPUT,
- (0x17 << AC_AMPCAP_OFFSET_SHIFT) |
- (0x17 << AC_AMPCAP_NUM_STEPS_SHIFT) |
- (0x05 << AC_AMPCAP_STEP_SIZE_SHIFT) |
- (1 << AC_AMPCAP_MUTE_SHIFT));
- break;
- }
+ snd_hda_pick_fixup(codec, NULL, ad1884_fixup_tbl, ad1884_fixups);
+ snd_hda_apply_fixup(codec, HDA_FIXUP_ACT_PRE_PROBE);
- codec->no_trigger_sense = 1;
- codec->no_sticky_stream = 1;
+ err = ad198x_parse_auto_config(codec, true);
+ if (err < 0)
+ goto error;
+ err = ad1983_add_spdif_mux_ctl(codec);
+ if (err < 0)
+ goto error;
+
+ snd_hda_apply_fixup(codec, HDA_FIXUP_ACT_PROBE);
return 0;
-}
+ error:
+ snd_hda_gen_free(codec);
+ return err;
+}
/*
* AD1882 / AD1882A
@@ -4614,347 +1130,47 @@ static int patch_ad1884a(struct hda_codec *codec)
* port-G - rear clfe-out (6stack)
*/
-static hda_nid_t ad1882_dac_nids[3] = {
- 0x04, 0x03, 0x05
-};
-
-static hda_nid_t ad1882_adc_nids[2] = {
- 0x08, 0x09,
-};
-
-static hda_nid_t ad1882_capsrc_nids[2] = {
- 0x0c, 0x0d,
-};
-
-#define AD1882_SPDIF_OUT 0x02
-
-/* list: 0x11, 0x39, 0x3a, 0x18, 0x3c, 0x3b, 0x12, 0x20 */
-static struct hda_input_mux ad1882_capture_source = {
- .num_items = 5,
- .items = {
- { "Front Mic", 0x1 },
- { "Mic", 0x4 },
- { "Line", 0x2 },
- { "CD", 0x3 },
- { "Mix", 0x7 },
- },
-};
-
-/* list: 0x11, 0x39, 0x3a, 0x3c, 0x18, 0x1f, 0x12, 0x20 */
-static struct hda_input_mux ad1882a_capture_source = {
- .num_items = 5,
- .items = {
- { "Front Mic", 0x1 },
- { "Mic", 0x4},
- { "Line", 0x2 },
- { "Digital Mic", 0x06 },
- { "Mix", 0x7 },
- },
-};
-
-static struct snd_kcontrol_new ad1882_base_mixers[] = {
- HDA_CODEC_VOLUME("Front Playback Volume", 0x04, 0x0, HDA_OUTPUT),
- HDA_CODEC_VOLUME("Surround Playback Volume", 0x03, 0x0, HDA_OUTPUT),
- HDA_CODEC_VOLUME_MONO("Center Playback Volume", 0x05, 1, 0x0, HDA_OUTPUT),
- HDA_CODEC_VOLUME_MONO("LFE Playback Volume", 0x05, 2, 0x0, HDA_OUTPUT),
- HDA_CODEC_MUTE("Headphone Playback Switch", 0x11, 0x0, HDA_OUTPUT),
- HDA_CODEC_MUTE("Front Playback Switch", 0x12, 0x0, HDA_OUTPUT),
- HDA_CODEC_VOLUME_MONO("Mono Playback Volume", 0x13, 1, 0x0, HDA_OUTPUT),
- HDA_CODEC_MUTE_MONO("Mono Playback Switch", 0x13, 1, 0x0, HDA_OUTPUT),
-
- HDA_CODEC_VOLUME("Mic Boost Volume", 0x3c, 0x0, HDA_OUTPUT),
- HDA_CODEC_VOLUME("Front Mic Boost Volume", 0x39, 0x0, HDA_OUTPUT),
- HDA_CODEC_VOLUME("Line-In Boost Volume", 0x3a, 0x0, HDA_OUTPUT),
- HDA_CODEC_VOLUME("Capture Volume", 0x0c, 0x0, HDA_OUTPUT),
- HDA_CODEC_MUTE("Capture Switch", 0x0c, 0x0, HDA_OUTPUT),
- HDA_CODEC_VOLUME_IDX("Capture Volume", 1, 0x0d, 0x0, HDA_OUTPUT),
- HDA_CODEC_MUTE_IDX("Capture Switch", 1, 0x0d, 0x0, HDA_OUTPUT),
- {
- .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
- /* The multiple "Capture Source" controls confuse alsamixer
- * So call somewhat different..
- */
- /* .name = "Capture Source", */
- .name = "Input Source",
- .count = 2,
- .info = ad198x_mux_enum_info,
- .get = ad198x_mux_enum_get,
- .put = ad198x_mux_enum_put,
- },
- /* SPDIF controls */
- HDA_CODEC_VOLUME("IEC958 Playback Volume", 0x1b, 0x0, HDA_OUTPUT),
- {
- .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
- .name = SNDRV_CTL_NAME_IEC958("",PLAYBACK,NONE) "Source",
- /* identical with ad1983 */
- .info = ad1983_spdif_route_info,
- .get = ad1983_spdif_route_get,
- .put = ad1983_spdif_route_put,
- },
- { } /* end */
-};
-
-static struct snd_kcontrol_new ad1882_loopback_mixers[] = {
- HDA_CODEC_VOLUME("Front Mic Playback Volume", 0x20, 0x00, HDA_INPUT),
- HDA_CODEC_MUTE("Front Mic Playback Switch", 0x20, 0x00, HDA_INPUT),
- HDA_CODEC_VOLUME("Mic Playback Volume", 0x20, 0x01, HDA_INPUT),
- HDA_CODEC_MUTE("Mic Playback Switch", 0x20, 0x01, HDA_INPUT),
- HDA_CODEC_VOLUME("Line Playback Volume", 0x20, 0x04, HDA_INPUT),
- HDA_CODEC_MUTE("Line Playback Switch", 0x20, 0x04, HDA_INPUT),
- HDA_CODEC_VOLUME("CD Playback Volume", 0x20, 0x06, HDA_INPUT),
- HDA_CODEC_MUTE("CD Playback Switch", 0x20, 0x06, HDA_INPUT),
- { } /* end */
-};
-
-static struct snd_kcontrol_new ad1882a_loopback_mixers[] = {
- HDA_CODEC_VOLUME("Front Mic Playback Volume", 0x20, 0x00, HDA_INPUT),
- HDA_CODEC_MUTE("Front Mic Playback Switch", 0x20, 0x00, HDA_INPUT),
- HDA_CODEC_VOLUME("Mic Playback Volume", 0x20, 0x04, HDA_INPUT),
- HDA_CODEC_MUTE("Mic Playback Switch", 0x20, 0x04, HDA_INPUT),
- HDA_CODEC_VOLUME("Line Playback Volume", 0x20, 0x01, HDA_INPUT),
- HDA_CODEC_MUTE("Line Playback Switch", 0x20, 0x01, HDA_INPUT),
- HDA_CODEC_VOLUME("CD Playback Volume", 0x20, 0x06, HDA_INPUT),
- HDA_CODEC_MUTE("CD Playback Switch", 0x20, 0x06, HDA_INPUT),
- HDA_CODEC_VOLUME("Digital Mic Boost Volume", 0x1f, 0x0, HDA_INPUT),
- { } /* end */
-};
-
-static struct snd_kcontrol_new ad1882_3stack_mixers[] = {
- HDA_CODEC_MUTE("Surround Playback Switch", 0x15, 0x0, HDA_OUTPUT),
- HDA_CODEC_MUTE_MONO("Center Playback Switch", 0x17, 1, 0x0, HDA_OUTPUT),
- HDA_CODEC_MUTE_MONO("LFE Playback Switch", 0x17, 2, 0x0, HDA_OUTPUT),
- {
- .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
- .name = "Channel Mode",
- .info = ad198x_ch_mode_info,
- .get = ad198x_ch_mode_get,
- .put = ad198x_ch_mode_put,
- },
- { } /* end */
-};
-
-static struct snd_kcontrol_new ad1882_6stack_mixers[] = {
- HDA_CODEC_MUTE("Surround Playback Switch", 0x16, 0x0, HDA_OUTPUT),
- HDA_CODEC_MUTE_MONO("Center Playback Switch", 0x24, 1, 0x0, HDA_OUTPUT),
- HDA_CODEC_MUTE_MONO("LFE Playback Switch", 0x24, 2, 0x0, HDA_OUTPUT),
- { } /* end */
-};
-
-static struct hda_verb ad1882_ch2_init[] = {
- {0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN},
- {0x2c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
- {0x2c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
- {0x17, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80},
- {0x26, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
- {0x26, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
- { } /* end */
-};
-
-static struct hda_verb ad1882_ch4_init[] = {
- {0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
- {0x2c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
- {0x2c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
- {0x17, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80},
- {0x26, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
- {0x26, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
- { } /* end */
-};
-
-static struct hda_verb ad1882_ch6_init[] = {
- {0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
- {0x2c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
- {0x2c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
- {0x17, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
- {0x26, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
- {0x26, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
- { } /* end */
-};
-
-static struct hda_channel_mode ad1882_modes[3] = {
- { 2, ad1882_ch2_init },
- { 4, ad1882_ch4_init },
- { 6, ad1882_ch6_init },
-};
-
-/*
- * initialization verbs
- */
-static struct hda_verb ad1882_init_verbs[] = {
- /* DACs; mute as default */
- {0x03, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
- {0x04, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
- {0x05, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
- /* Port-A (HP) mixer */
- {0x22, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
- {0x22, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
- /* Port-A pin */
- {0x11, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP},
- {0x11, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
- /* HP selector - select DAC2 */
- {0x37, AC_VERB_SET_CONNECT_SEL, 0x1},
- /* Port-D (Line-out) mixer */
- {0x29, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
- {0x29, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
- /* Port-D pin */
- {0x12, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP},
- {0x12, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
- /* Mono-out mixer */
- {0x1e, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
- {0x1e, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
- /* Mono-out pin */
- {0x13, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP},
- {0x13, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
- /* Port-B (front mic) pin */
- {0x14, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80},
- {0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
- {0x39, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO}, /* boost */
- /* Port-C (line-in) pin */
- {0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN},
- {0x15, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
- {0x3a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO}, /* boost */
- /* Port-C mixer - mute as input */
- {0x2c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
- {0x2c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
- /* Port-E (mic-in) pin */
- {0x17, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80},
- {0x17, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
- {0x3c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO}, /* boost */
- /* Port-E mixer - mute as input */
- {0x26, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
- {0x26, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
- /* Port-F (surround) */
- {0x16, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
- {0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
- /* Port-G (CLFE) */
- {0x24, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
- {0x24, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
- /* Analog mixer; mute as default */
- /* list: 0x39, 0x3a, 0x11, 0x12, 0x3c, 0x3b, 0x18, 0x1a */
- {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
- {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
- {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(2)},
- {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(3)},
- {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(4)},
- {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(5)},
- {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(6)},
- {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(7)},
- /* Analog Mix output amp */
- {0x21, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE | 0x1f}, /* 0dB */
- /* SPDIF output selector */
- {0x02, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE | 0x27}, /* 0dB */
- {0x02, AC_VERB_SET_CONNECT_SEL, 0x0}, /* PCM */
- {0x1b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE | 0x27}, /* 0dB */
- { } /* end */
-};
-
-#ifdef CONFIG_SND_HDA_POWER_SAVE
-static struct hda_amp_list ad1882_loopbacks[] = {
- { 0x20, HDA_INPUT, 0 }, /* Front Mic */
- { 0x20, HDA_INPUT, 1 }, /* Mic */
- { 0x20, HDA_INPUT, 4 }, /* Line */
- { 0x20, HDA_INPUT, 6 }, /* CD */
- { } /* end */
-};
-#endif
-
-/* models */
-enum {
- AD1882_3STACK,
- AD1882_6STACK,
- AD1882_MODELS
-};
-
-static const char * const ad1882_models[AD1986A_MODELS] = {
- [AD1882_3STACK] = "3stack",
- [AD1882_6STACK] = "6stack",
-};
-
-
static int patch_ad1882(struct hda_codec *codec)
{
struct ad198x_spec *spec;
- int err, board_config;
-
- spec = kzalloc(sizeof(*spec), GFP_KERNEL);
- if (spec == NULL)
- return -ENOMEM;
-
- codec->spec = spec;
+ int err;
- err = snd_hda_attach_beep_device(codec, 0x10);
- if (err < 0) {
- ad198x_free(codec);
+ err = alloc_ad_spec(codec);
+ if (err < 0)
return err;
- }
- set_beep_amp(spec, 0x10, 0, HDA_OUTPUT);
-
- spec->multiout.max_channels = 6;
- spec->multiout.num_dacs = 3;
- spec->multiout.dac_nids = ad1882_dac_nids;
- spec->multiout.dig_out_nid = AD1882_SPDIF_OUT;
- spec->num_adc_nids = ARRAY_SIZE(ad1882_adc_nids);
- spec->adc_nids = ad1882_adc_nids;
- spec->capsrc_nids = ad1882_capsrc_nids;
- if (codec->vendor_id == 0x11d41882)
- spec->input_mux = &ad1882_capture_source;
- else
- spec->input_mux = &ad1882a_capture_source;
- spec->num_mixers = 2;
- spec->mixers[0] = ad1882_base_mixers;
- if (codec->vendor_id == 0x11d41882)
- spec->mixers[1] = ad1882_loopback_mixers;
- else
- spec->mixers[1] = ad1882a_loopback_mixers;
- spec->num_init_verbs = 1;
- spec->init_verbs[0] = ad1882_init_verbs;
- spec->spdif_route = 0;
-#ifdef CONFIG_SND_HDA_POWER_SAVE
- spec->loopback.amplist = ad1882_loopbacks;
-#endif
- spec->vmaster_nid = 0x04;
-
- codec->patch_ops = ad198x_patch_ops;
-
- /* override some parameters */
- board_config = snd_hda_check_board_config(codec, AD1882_MODELS,
- ad1882_models, NULL);
- switch (board_config) {
- default:
- case AD1882_3STACK:
- spec->num_mixers = 3;
- spec->mixers[2] = ad1882_3stack_mixers;
- spec->channel_mode = ad1882_modes;
- spec->num_channel_mode = ARRAY_SIZE(ad1882_modes);
- spec->need_dac_fix = 1;
- spec->multiout.max_channels = 2;
- spec->multiout.num_dacs = 1;
- break;
- case AD1882_6STACK:
- spec->num_mixers = 3;
- spec->mixers[2] = ad1882_6stack_mixers;
- break;
- }
-
- codec->no_trigger_sense = 1;
- codec->no_sticky_stream = 1;
+ spec = codec->spec;
+ spec->gen.mixer_nid = 0x20;
+ spec->gen.mixer_merge_nid = 0x21;
+ spec->gen.beep_nid = 0x10;
+ set_beep_amp(spec, 0x10, 0, HDA_OUTPUT);
+ err = ad198x_parse_auto_config(codec, true);
+ if (err < 0)
+ goto error;
+ err = ad1988_add_spdif_mux_ctl(codec);
+ if (err < 0)
+ goto error;
return 0;
+
+ error:
+ snd_hda_gen_free(codec);
+ return err;
}
/*
* patch entries
*/
-static struct hda_codec_preset snd_hda_preset_analog[] = {
- { .id = 0x11d4184a, .name = "AD1884A", .patch = patch_ad1884a },
+static const struct hda_codec_preset snd_hda_preset_analog[] = {
+ { .id = 0x11d4184a, .name = "AD1884A", .patch = patch_ad1884 },
{ .id = 0x11d41882, .name = "AD1882", .patch = patch_ad1882 },
- { .id = 0x11d41883, .name = "AD1883", .patch = patch_ad1884a },
+ { .id = 0x11d41883, .name = "AD1883", .patch = patch_ad1884 },
{ .id = 0x11d41884, .name = "AD1884", .patch = patch_ad1884 },
- { .id = 0x11d4194a, .name = "AD1984A", .patch = patch_ad1884a },
- { .id = 0x11d4194b, .name = "AD1984B", .patch = patch_ad1884a },
+ { .id = 0x11d4194a, .name = "AD1984A", .patch = patch_ad1884 },
+ { .id = 0x11d4194b, .name = "AD1984B", .patch = patch_ad1884 },
{ .id = 0x11d41981, .name = "AD1981", .patch = patch_ad1981 },
{ .id = 0x11d41983, .name = "AD1983", .patch = patch_ad1983 },
- { .id = 0x11d41984, .name = "AD1984", .patch = patch_ad1984 },
+ { .id = 0x11d41984, .name = "AD1984", .patch = patch_ad1884 },
{ .id = 0x11d41986, .name = "AD1986A", .patch = patch_ad1986a },
{ .id = 0x11d41988, .name = "AD1988", .patch = patch_ad1988 },
{ .id = 0x11d4198b, .name = "AD1988B", .patch = patch_ad1988 },
diff --git a/sound/pci/hda/patch_ca0110.c b/sound/pci/hda/patch_ca0110.c
index 46c8bf48c31..5e65999e0d8 100644
--- a/sound/pci/hda/patch_ca0110.c
+++ b/sound/pci/hda/patch_ca0110.c
@@ -19,506 +19,52 @@
*/
#include <linux/init.h>
-#include <linux/delay.h>
#include <linux/slab.h>
-#include <linux/pci.h>
+#include <linux/module.h>
#include <sound/core.h>
#include "hda_codec.h"
#include "hda_local.h"
+#include "hda_auto_parser.h"
+#include "hda_jack.h"
+#include "hda_generic.h"
-/*
- */
-
-struct ca0110_spec {
- struct auto_pin_cfg autocfg;
- struct hda_multi_out multiout;
- hda_nid_t out_pins[AUTO_CFG_MAX_OUTS];
- hda_nid_t dacs[AUTO_CFG_MAX_OUTS];
- hda_nid_t hp_dac;
- hda_nid_t input_pins[AUTO_PIN_LAST];
- hda_nid_t adcs[AUTO_PIN_LAST];
- hda_nid_t dig_out;
- hda_nid_t dig_in;
- unsigned int num_inputs;
- const char *input_labels[AUTO_PIN_LAST];
- struct hda_pcm pcm_rec[2]; /* PCM information */
-};
-
-/*
- * PCM callbacks
- */
-static int ca0110_playback_pcm_open(struct hda_pcm_stream *hinfo,
- struct hda_codec *codec,
- struct snd_pcm_substream *substream)
-{
- struct ca0110_spec *spec = codec->spec;
- return snd_hda_multi_out_analog_open(codec, &spec->multiout, substream,
- hinfo);
-}
-
-static int ca0110_playback_pcm_prepare(struct hda_pcm_stream *hinfo,
- struct hda_codec *codec,
- unsigned int stream_tag,
- unsigned int format,
- struct snd_pcm_substream *substream)
-{
- struct ca0110_spec *spec = codec->spec;
- return snd_hda_multi_out_analog_prepare(codec, &spec->multiout,
- stream_tag, format, substream);
-}
-
-static int ca0110_playback_pcm_cleanup(struct hda_pcm_stream *hinfo,
- struct hda_codec *codec,
- struct snd_pcm_substream *substream)
-{
- struct ca0110_spec *spec = codec->spec;
- return snd_hda_multi_out_analog_cleanup(codec, &spec->multiout);
-}
-
-/*
- * Digital out
- */
-static int ca0110_dig_playback_pcm_open(struct hda_pcm_stream *hinfo,
- struct hda_codec *codec,
- struct snd_pcm_substream *substream)
-{
- struct ca0110_spec *spec = codec->spec;
- return snd_hda_multi_out_dig_open(codec, &spec->multiout);
-}
-
-static int ca0110_dig_playback_pcm_close(struct hda_pcm_stream *hinfo,
- struct hda_codec *codec,
- struct snd_pcm_substream *substream)
-{
- struct ca0110_spec *spec = codec->spec;
- return snd_hda_multi_out_dig_close(codec, &spec->multiout);
-}
-
-static int ca0110_dig_playback_pcm_prepare(struct hda_pcm_stream *hinfo,
- struct hda_codec *codec,
- unsigned int stream_tag,
- unsigned int format,
- struct snd_pcm_substream *substream)
-{
- struct ca0110_spec *spec = codec->spec;
- return snd_hda_multi_out_dig_prepare(codec, &spec->multiout, stream_tag,
- format, substream);
-}
-
-/*
- * Analog capture
- */
-static int ca0110_capture_pcm_prepare(struct hda_pcm_stream *hinfo,
- struct hda_codec *codec,
- unsigned int stream_tag,
- unsigned int format,
- struct snd_pcm_substream *substream)
-{
- struct ca0110_spec *spec = codec->spec;
-
- snd_hda_codec_setup_stream(codec, spec->adcs[substream->number],
- stream_tag, 0, format);
- return 0;
-}
-
-static int ca0110_capture_pcm_cleanup(struct hda_pcm_stream *hinfo,
- struct hda_codec *codec,
- struct snd_pcm_substream *substream)
-{
- struct ca0110_spec *spec = codec->spec;
-
- snd_hda_codec_cleanup_stream(codec, spec->adcs[substream->number]);
- return 0;
-}
-
-/*
- */
-
-static char *dirstr[2] = { "Playback", "Capture" };
-
-static int _add_switch(struct hda_codec *codec, hda_nid_t nid, const char *pfx,
- int chan, int dir)
-{
- char namestr[44];
- int type = dir ? HDA_INPUT : HDA_OUTPUT;
- struct snd_kcontrol_new knew =
- HDA_CODEC_MUTE_MONO(namestr, nid, chan, 0, type);
- sprintf(namestr, "%s %s Switch", pfx, dirstr[dir]);
- return snd_hda_ctl_add(codec, nid, snd_ctl_new1(&knew, codec));
-}
-
-static int _add_volume(struct hda_codec *codec, hda_nid_t nid, const char *pfx,
- int chan, int dir)
-{
- char namestr[44];
- int type = dir ? HDA_INPUT : HDA_OUTPUT;
- struct snd_kcontrol_new knew =
- HDA_CODEC_VOLUME_MONO(namestr, nid, chan, 0, type);
- sprintf(namestr, "%s %s Volume", pfx, dirstr[dir]);
- return snd_hda_ctl_add(codec, nid, snd_ctl_new1(&knew, codec));
-}
-
-#define add_out_switch(codec, nid, pfx) _add_switch(codec, nid, pfx, 3, 0)
-#define add_out_volume(codec, nid, pfx) _add_volume(codec, nid, pfx, 3, 0)
-#define add_in_switch(codec, nid, pfx) _add_switch(codec, nid, pfx, 3, 1)
-#define add_in_volume(codec, nid, pfx) _add_volume(codec, nid, pfx, 3, 1)
-#define add_mono_switch(codec, nid, pfx, chan) \
- _add_switch(codec, nid, pfx, chan, 0)
-#define add_mono_volume(codec, nid, pfx, chan) \
- _add_volume(codec, nid, pfx, chan, 0)
-
-static int ca0110_build_controls(struct hda_codec *codec)
-{
- struct ca0110_spec *spec = codec->spec;
- struct auto_pin_cfg *cfg = &spec->autocfg;
- static char *prefix[AUTO_CFG_MAX_OUTS] = {
- "Front", "Surround", NULL, "Side", "Multi"
- };
- hda_nid_t mutenid;
- int i, err;
-
- for (i = 0; i < spec->multiout.num_dacs; i++) {
- if (get_wcaps(codec, spec->out_pins[i]) & AC_WCAP_OUT_AMP)
- mutenid = spec->out_pins[i];
- else
- mutenid = spec->multiout.dac_nids[i];
- if (!prefix[i]) {
- err = add_mono_switch(codec, mutenid,
- "Center", 1);
- if (err < 0)
- return err;
- err = add_mono_switch(codec, mutenid,
- "LFE", 1);
- if (err < 0)
- return err;
- err = add_mono_volume(codec, spec->multiout.dac_nids[i],
- "Center", 1);
- if (err < 0)
- return err;
- err = add_mono_volume(codec, spec->multiout.dac_nids[i],
- "LFE", 1);
- if (err < 0)
- return err;
- } else {
- err = add_out_switch(codec, mutenid,
- prefix[i]);
- if (err < 0)
- return err;
- err = add_out_volume(codec, spec->multiout.dac_nids[i],
- prefix[i]);
- if (err < 0)
- return err;
- }
- }
- if (cfg->hp_outs) {
- if (get_wcaps(codec, cfg->hp_pins[0]) & AC_WCAP_OUT_AMP)
- mutenid = cfg->hp_pins[0];
- else
- mutenid = spec->multiout.dac_nids[i];
-
- err = add_out_switch(codec, mutenid, "Headphone");
- if (err < 0)
- return err;
- if (spec->hp_dac) {
- err = add_out_volume(codec, spec->hp_dac, "Headphone");
- if (err < 0)
- return err;
- }
- }
- for (i = 0; i < spec->num_inputs; i++) {
- const char *label = spec->input_labels[i];
- if (get_wcaps(codec, spec->input_pins[i]) & AC_WCAP_IN_AMP)
- mutenid = spec->input_pins[i];
- else
- mutenid = spec->adcs[i];
- err = add_in_switch(codec, mutenid, label);
- if (err < 0)
- return err;
- err = add_in_volume(codec, spec->adcs[i], label);
- if (err < 0)
- return err;
- }
-
- if (spec->dig_out) {
- err = snd_hda_create_spdif_out_ctls(codec, spec->dig_out);
- if (err < 0)
- return err;
- err = snd_hda_create_spdif_share_sw(codec, &spec->multiout);
- if (err < 0)
- return err;
- spec->multiout.share_spdif = 1;
- }
- if (spec->dig_in) {
- err = snd_hda_create_spdif_in_ctls(codec, spec->dig_in);
- if (err < 0)
- return err;
- err = add_in_volume(codec, spec->dig_in, "IEC958");
- }
- return 0;
-}
-
-/*
- */
-static struct hda_pcm_stream ca0110_pcm_analog_playback = {
- .substreams = 1,
- .channels_min = 2,
- .channels_max = 8,
- .ops = {
- .open = ca0110_playback_pcm_open,
- .prepare = ca0110_playback_pcm_prepare,
- .cleanup = ca0110_playback_pcm_cleanup
- },
-};
-
-static struct hda_pcm_stream ca0110_pcm_analog_capture = {
- .substreams = 1,
- .channels_min = 2,
- .channels_max = 2,
- .ops = {
- .prepare = ca0110_capture_pcm_prepare,
- .cleanup = ca0110_capture_pcm_cleanup
- },
-};
-static struct hda_pcm_stream ca0110_pcm_digital_playback = {
- .substreams = 1,
- .channels_min = 2,
- .channels_max = 2,
- .ops = {
- .open = ca0110_dig_playback_pcm_open,
- .close = ca0110_dig_playback_pcm_close,
- .prepare = ca0110_dig_playback_pcm_prepare
- },
+static const struct hda_codec_ops ca0110_patch_ops = {
+ .build_controls = snd_hda_gen_build_controls,
+ .build_pcms = snd_hda_gen_build_pcms,
+ .init = snd_hda_gen_init,
+ .free = snd_hda_gen_free,
+ .unsol_event = snd_hda_jack_unsol_event,
};
-static struct hda_pcm_stream ca0110_pcm_digital_capture = {
- .substreams = 1,
- .channels_min = 2,
- .channels_max = 2,
-};
-
-static int ca0110_build_pcms(struct hda_codec *codec)
-{
- struct ca0110_spec *spec = codec->spec;
- struct hda_pcm *info = spec->pcm_rec;
-
- codec->pcm_info = info;
- codec->num_pcms = 0;
-
- info->name = "CA0110 Analog";
- info->stream[SNDRV_PCM_STREAM_PLAYBACK] = ca0110_pcm_analog_playback;
- info->stream[SNDRV_PCM_STREAM_PLAYBACK].nid = spec->dacs[0];
- info->stream[SNDRV_PCM_STREAM_PLAYBACK].channels_max =
- spec->multiout.max_channels;
- info->stream[SNDRV_PCM_STREAM_CAPTURE] = ca0110_pcm_analog_capture;
- info->stream[SNDRV_PCM_STREAM_CAPTURE].substreams = spec->num_inputs;
- info->stream[SNDRV_PCM_STREAM_CAPTURE].nid = spec->adcs[0];
- codec->num_pcms++;
-
- if (!spec->dig_out && !spec->dig_in)
- return 0;
-
- info++;
- info->name = "CA0110 Digital";
- info->pcm_type = HDA_PCM_TYPE_SPDIF;
- if (spec->dig_out) {
- info->stream[SNDRV_PCM_STREAM_PLAYBACK] =
- ca0110_pcm_digital_playback;
- info->stream[SNDRV_PCM_STREAM_PLAYBACK].nid = spec->dig_out;
- }
- if (spec->dig_in) {
- info->stream[SNDRV_PCM_STREAM_CAPTURE] =
- ca0110_pcm_digital_capture;
- info->stream[SNDRV_PCM_STREAM_CAPTURE].nid = spec->dig_in;
- }
- codec->num_pcms++;
-
- return 0;
-}
-
-static void init_output(struct hda_codec *codec, hda_nid_t pin, hda_nid_t dac)
-{
- if (pin) {
- snd_hda_codec_write(codec, pin, 0,
- AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP);
- if (get_wcaps(codec, pin) & AC_WCAP_OUT_AMP)
- snd_hda_codec_write(codec, pin, 0,
- AC_VERB_SET_AMP_GAIN_MUTE,
- AMP_OUT_UNMUTE);
- }
- if (dac)
- snd_hda_codec_write(codec, dac, 0,
- AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO);
-}
-
-static void init_input(struct hda_codec *codec, hda_nid_t pin, hda_nid_t adc)
-{
- if (pin) {
- snd_hda_codec_write(codec, pin, 0,
- AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80);
- if (get_wcaps(codec, pin) & AC_WCAP_IN_AMP)
- snd_hda_codec_write(codec, pin, 0,
- AC_VERB_SET_AMP_GAIN_MUTE,
- AMP_IN_UNMUTE(0));
- }
- if (adc)
- snd_hda_codec_write(codec, adc, 0, AC_VERB_SET_AMP_GAIN_MUTE,
- AMP_IN_UNMUTE(0));
-}
-
-static int ca0110_init(struct hda_codec *codec)
-{
- struct ca0110_spec *spec = codec->spec;
- struct auto_pin_cfg *cfg = &spec->autocfg;
- int i;
-
- for (i = 0; i < spec->multiout.num_dacs; i++)
- init_output(codec, spec->out_pins[i],
- spec->multiout.dac_nids[i]);
- init_output(codec, cfg->hp_pins[0], spec->hp_dac);
- init_output(codec, cfg->dig_out_pins[0], spec->dig_out);
-
- for (i = 0; i < spec->num_inputs; i++)
- init_input(codec, spec->input_pins[i], spec->adcs[i]);
- init_input(codec, cfg->dig_in_pin, spec->dig_in);
- return 0;
-}
-
-static void ca0110_free(struct hda_codec *codec)
-{
- kfree(codec->spec);
-}
-
-static struct hda_codec_ops ca0110_patch_ops = {
- .build_controls = ca0110_build_controls,
- .build_pcms = ca0110_build_pcms,
- .init = ca0110_init,
- .free = ca0110_free,
-};
-
-
-static void parse_line_outs(struct hda_codec *codec)
-{
- struct ca0110_spec *spec = codec->spec;
- struct auto_pin_cfg *cfg = &spec->autocfg;
- int i, n;
- unsigned int def_conf;
- hda_nid_t nid;
-
- n = 0;
- for (i = 0; i < cfg->line_outs; i++) {
- nid = cfg->line_out_pins[i];
- def_conf = snd_hda_codec_get_pincfg(codec, nid);
- if (!def_conf)
- continue; /* invalid pin */
- if (snd_hda_get_connections(codec, nid, &spec->dacs[i], 1) != 1)
- continue;
- spec->out_pins[n++] = nid;
- }
- spec->multiout.dac_nids = spec->dacs;
- spec->multiout.num_dacs = n;
- spec->multiout.max_channels = n * 2;
-}
-
-static void parse_hp_out(struct hda_codec *codec)
-{
- struct ca0110_spec *spec = codec->spec;
- struct auto_pin_cfg *cfg = &spec->autocfg;
- int i;
- unsigned int def_conf;
- hda_nid_t nid, dac;
-
- if (!cfg->hp_outs)
- return;
- nid = cfg->hp_pins[0];
- def_conf = snd_hda_codec_get_pincfg(codec, nid);
- if (!def_conf) {
- cfg->hp_outs = 0;
- return;
- }
- if (snd_hda_get_connections(codec, nid, &dac, 1) != 1)
- return;
-
- for (i = 0; i < cfg->line_outs; i++)
- if (dac == spec->dacs[i])
- break;
- if (i >= cfg->line_outs) {
- spec->hp_dac = dac;
- spec->multiout.hp_nid = dac;
- }
-}
-
-static void parse_input(struct hda_codec *codec)
-{
- struct ca0110_spec *spec = codec->spec;
- struct auto_pin_cfg *cfg = &spec->autocfg;
- hda_nid_t nid, pin;
- int n, i, j;
-
- n = 0;
- nid = codec->start_nid;
- for (i = 0; i < codec->num_nodes; i++, nid++) {
- unsigned int wcaps = get_wcaps(codec, nid);
- unsigned int type = get_wcaps_type(wcaps);
- if (type != AC_WID_AUD_IN)
- continue;
- if (snd_hda_get_connections(codec, nid, &pin, 1) != 1)
- continue;
- if (pin == cfg->dig_in_pin) {
- spec->dig_in = nid;
- continue;
- }
- for (j = 0; j < cfg->num_inputs; j++)
- if (cfg->inputs[j].pin == pin)
- break;
- if (j >= cfg->num_inputs)
- continue;
- spec->input_pins[n] = pin;
- spec->input_labels[n] = hda_get_input_pin_label(codec, pin, 1);
- spec->adcs[n] = nid;
- n++;
- }
- spec->num_inputs = n;
-}
-
-static void parse_digital(struct hda_codec *codec)
-{
- struct ca0110_spec *spec = codec->spec;
- struct auto_pin_cfg *cfg = &spec->autocfg;
-
- if (cfg->dig_outs &&
- snd_hda_get_connections(codec, cfg->dig_out_pins[0],
- &spec->dig_out, 1) == 1)
- spec->multiout.dig_out_nid = spec->dig_out;
-}
-
static int ca0110_parse_auto_config(struct hda_codec *codec)
{
- struct ca0110_spec *spec = codec->spec;
+ struct hda_gen_spec *spec = codec->spec;
int err;
- err = snd_hda_parse_pin_def_config(codec, &spec->autocfg, NULL);
+ err = snd_hda_parse_pin_defcfg(codec, &spec->autocfg, NULL, 0);
+ if (err < 0)
+ return err;
+ err = snd_hda_gen_parse_auto_config(codec, &spec->autocfg);
if (err < 0)
return err;
- parse_line_outs(codec);
- parse_hp_out(codec);
- parse_digital(codec);
- parse_input(codec);
return 0;
}
static int patch_ca0110(struct hda_codec *codec)
{
- struct ca0110_spec *spec;
+ struct hda_gen_spec *spec;
int err;
spec = kzalloc(sizeof(*spec), GFP_KERNEL);
if (!spec)
return -ENOMEM;
+ snd_hda_gen_spec_init(spec);
codec->spec = spec;
+ spec->multi_cap_vol = 1;
codec->bus->needs_damn_long_delay = 1;
err = ca0110_parse_auto_config(codec);
@@ -530,8 +76,7 @@ static int patch_ca0110(struct hda_codec *codec)
return 0;
error:
- kfree(codec->spec);
- codec->spec = NULL;
+ snd_hda_gen_free(codec);
return err;
}
@@ -539,7 +84,7 @@ static int patch_ca0110(struct hda_codec *codec)
/*
* patch entries
*/
-static struct hda_codec_preset snd_hda_preset_ca0110[] = {
+static const struct hda_codec_preset snd_hda_preset_ca0110[] = {
{ .id = 0x1102000a, .name = "CA0110-IBG", .patch = patch_ca0110 },
{ .id = 0x1102000b, .name = "CA0110-IBG", .patch = patch_ca0110 },
{ .id = 0x1102000d, .name = "SB0880 X-Fi", .patch = patch_ca0110 },
diff --git a/sound/pci/hda/patch_ca0132.c b/sound/pci/hda/patch_ca0132.c
new file mode 100644
index 00000000000..092f2bd030b
--- /dev/null
+++ b/sound/pci/hda/patch_ca0132.c
@@ -0,0 +1,4721 @@
+/*
+ * HD audio interface patch for Creative CA0132 chip
+ *
+ * Copyright (c) 2011, Creative Technology Ltd.
+ *
+ * Based on patch_ca0110.c
+ * Copyright (c) 2008 Takashi Iwai <tiwai@suse.de>
+ *
+ * This driver is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This driver is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include <linux/init.h>
+#include <linux/delay.h>
+#include <linux/slab.h>
+#include <linux/mutex.h>
+#include <linux/module.h>
+#include <linux/firmware.h>
+#include <sound/core.h>
+#include "hda_codec.h"
+#include "hda_local.h"
+#include "hda_auto_parser.h"
+#include "hda_jack.h"
+
+#include "ca0132_regs.h"
+
+/* Enable this to see controls for tuning purpose. */
+/*#define ENABLE_TUNING_CONTROLS*/
+
+#define FLOAT_ZERO 0x00000000
+#define FLOAT_ONE 0x3f800000
+#define FLOAT_TWO 0x40000000
+#define FLOAT_MINUS_5 0xc0a00000
+
+#define UNSOL_TAG_HP 0x10
+#define UNSOL_TAG_AMIC1 0x12
+#define UNSOL_TAG_DSP 0x16
+
+#define DSP_DMA_WRITE_BUFLEN_INIT (1UL<<18)
+#define DSP_DMA_WRITE_BUFLEN_OVLY (1UL<<15)
+
+#define DMA_TRANSFER_FRAME_SIZE_NWORDS 8
+#define DMA_TRANSFER_MAX_FRAME_SIZE_NWORDS 32
+#define DMA_OVERLAY_FRAME_SIZE_NWORDS 2
+
+#define MASTERCONTROL 0x80
+#define MASTERCONTROL_ALLOC_DMA_CHAN 10
+#define MASTERCONTROL_QUERY_SPEAKER_EQ_ADDRESS 60
+
+#define WIDGET_CHIP_CTRL 0x15
+#define WIDGET_DSP_CTRL 0x16
+
+#define MEM_CONNID_MICIN1 3
+#define MEM_CONNID_MICIN2 5
+#define MEM_CONNID_MICOUT1 12
+#define MEM_CONNID_MICOUT2 14
+#define MEM_CONNID_WUH 10
+#define MEM_CONNID_DSP 16
+#define MEM_CONNID_DMIC 100
+
+#define SCP_SET 0
+#define SCP_GET 1
+
+#define EFX_FILE "ctefx.bin"
+
+#ifdef CONFIG_SND_HDA_CODEC_CA0132_DSP
+MODULE_FIRMWARE(EFX_FILE);
+#endif
+
+static char *dirstr[2] = { "Playback", "Capture" };
+
+enum {
+ SPEAKER_OUT,
+ HEADPHONE_OUT
+};
+
+enum {
+ DIGITAL_MIC,
+ LINE_MIC_IN
+};
+
+enum {
+#define VNODE_START_NID 0x80
+ VNID_SPK = VNODE_START_NID, /* Speaker vnid */
+ VNID_MIC,
+ VNID_HP_SEL,
+ VNID_AMIC1_SEL,
+ VNID_HP_ASEL,
+ VNID_AMIC1_ASEL,
+ VNODE_END_NID,
+#define VNODES_COUNT (VNODE_END_NID - VNODE_START_NID)
+
+#define EFFECT_START_NID 0x90
+#define OUT_EFFECT_START_NID EFFECT_START_NID
+ SURROUND = OUT_EFFECT_START_NID,
+ CRYSTALIZER,
+ DIALOG_PLUS,
+ SMART_VOLUME,
+ X_BASS,
+ EQUALIZER,
+ OUT_EFFECT_END_NID,
+#define OUT_EFFECTS_COUNT (OUT_EFFECT_END_NID - OUT_EFFECT_START_NID)
+
+#define IN_EFFECT_START_NID OUT_EFFECT_END_NID
+ ECHO_CANCELLATION = IN_EFFECT_START_NID,
+ VOICE_FOCUS,
+ MIC_SVM,
+ NOISE_REDUCTION,
+ IN_EFFECT_END_NID,
+#define IN_EFFECTS_COUNT (IN_EFFECT_END_NID - IN_EFFECT_START_NID)
+
+ VOICEFX = IN_EFFECT_END_NID,
+ PLAY_ENHANCEMENT,
+ CRYSTAL_VOICE,
+ EFFECT_END_NID
+#define EFFECTS_COUNT (EFFECT_END_NID - EFFECT_START_NID)
+};
+
+/* Effects values size*/
+#define EFFECT_VALS_MAX_COUNT 12
+
+/* Latency introduced by DSP blocks in milliseconds. */
+#define DSP_CAPTURE_INIT_LATENCY 0
+#define DSP_CRYSTAL_VOICE_LATENCY 124
+#define DSP_PLAYBACK_INIT_LATENCY 13
+#define DSP_PLAY_ENHANCEMENT_LATENCY 30
+#define DSP_SPEAKER_OUT_LATENCY 7
+
+struct ct_effect {
+ char name[SNDRV_CTL_ELEM_ID_NAME_MAXLEN];
+ hda_nid_t nid;
+ int mid; /*effect module ID*/
+ int reqs[EFFECT_VALS_MAX_COUNT]; /*effect module request*/
+ int direct; /* 0:output; 1:input*/
+ int params; /* number of default non-on/off params */
+ /*effect default values, 1st is on/off. */
+ unsigned int def_vals[EFFECT_VALS_MAX_COUNT];
+};
+
+#define EFX_DIR_OUT 0
+#define EFX_DIR_IN 1
+
+static struct ct_effect ca0132_effects[EFFECTS_COUNT] = {
+ { .name = "Surround",
+ .nid = SURROUND,
+ .mid = 0x96,
+ .reqs = {0, 1},
+ .direct = EFX_DIR_OUT,
+ .params = 1,
+ .def_vals = {0x3F800000, 0x3F2B851F}
+ },
+ { .name = "Crystalizer",
+ .nid = CRYSTALIZER,
+ .mid = 0x96,
+ .reqs = {7, 8},
+ .direct = EFX_DIR_OUT,
+ .params = 1,
+ .def_vals = {0x3F800000, 0x3F266666}
+ },
+ { .name = "Dialog Plus",
+ .nid = DIALOG_PLUS,
+ .mid = 0x96,
+ .reqs = {2, 3},
+ .direct = EFX_DIR_OUT,
+ .params = 1,
+ .def_vals = {0x00000000, 0x3F000000}
+ },
+ { .name = "Smart Volume",
+ .nid = SMART_VOLUME,
+ .mid = 0x96,
+ .reqs = {4, 5, 6},
+ .direct = EFX_DIR_OUT,
+ .params = 2,
+ .def_vals = {0x3F800000, 0x3F3D70A4, 0x00000000}
+ },
+ { .name = "X-Bass",
+ .nid = X_BASS,
+ .mid = 0x96,
+ .reqs = {24, 23, 25},
+ .direct = EFX_DIR_OUT,
+ .params = 2,
+ .def_vals = {0x3F800000, 0x42A00000, 0x3F000000}
+ },
+ { .name = "Equalizer",
+ .nid = EQUALIZER,
+ .mid = 0x96,
+ .reqs = {9, 10, 11, 12, 13, 14,
+ 15, 16, 17, 18, 19, 20},
+ .direct = EFX_DIR_OUT,
+ .params = 11,
+ .def_vals = {0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000}
+ },
+ { .name = "Echo Cancellation",
+ .nid = ECHO_CANCELLATION,
+ .mid = 0x95,
+ .reqs = {0, 1, 2, 3},
+ .direct = EFX_DIR_IN,
+ .params = 3,
+ .def_vals = {0x00000000, 0x3F3A9692, 0x00000000, 0x00000000}
+ },
+ { .name = "Voice Focus",
+ .nid = VOICE_FOCUS,
+ .mid = 0x95,
+ .reqs = {6, 7, 8, 9},
+ .direct = EFX_DIR_IN,
+ .params = 3,
+ .def_vals = {0x3F800000, 0x3D7DF3B6, 0x41F00000, 0x41F00000}
+ },
+ { .name = "Mic SVM",
+ .nid = MIC_SVM,
+ .mid = 0x95,
+ .reqs = {44, 45},
+ .direct = EFX_DIR_IN,
+ .params = 1,
+ .def_vals = {0x00000000, 0x3F3D70A4}
+ },
+ { .name = "Noise Reduction",
+ .nid = NOISE_REDUCTION,
+ .mid = 0x95,
+ .reqs = {4, 5},
+ .direct = EFX_DIR_IN,
+ .params = 1,
+ .def_vals = {0x3F800000, 0x3F000000}
+ },
+ { .name = "VoiceFX",
+ .nid = VOICEFX,
+ .mid = 0x95,
+ .reqs = {10, 11, 12, 13, 14, 15, 16, 17, 18},
+ .direct = EFX_DIR_IN,
+ .params = 8,
+ .def_vals = {0x00000000, 0x43C80000, 0x44AF0000, 0x44FA0000,
+ 0x3F800000, 0x3F800000, 0x3F800000, 0x00000000,
+ 0x00000000}
+ }
+};
+
+/* Tuning controls */
+#ifdef ENABLE_TUNING_CONTROLS
+
+enum {
+#define TUNING_CTL_START_NID 0xC0
+ WEDGE_ANGLE = TUNING_CTL_START_NID,
+ SVM_LEVEL,
+ EQUALIZER_BAND_0,
+ EQUALIZER_BAND_1,
+ EQUALIZER_BAND_2,
+ EQUALIZER_BAND_3,
+ EQUALIZER_BAND_4,
+ EQUALIZER_BAND_5,
+ EQUALIZER_BAND_6,
+ EQUALIZER_BAND_7,
+ EQUALIZER_BAND_8,
+ EQUALIZER_BAND_9,
+ TUNING_CTL_END_NID
+#define TUNING_CTLS_COUNT (TUNING_CTL_END_NID - TUNING_CTL_START_NID)
+};
+
+struct ct_tuning_ctl {
+ char name[SNDRV_CTL_ELEM_ID_NAME_MAXLEN];
+ hda_nid_t parent_nid;
+ hda_nid_t nid;
+ int mid; /*effect module ID*/
+ int req; /*effect module request*/
+ int direct; /* 0:output; 1:input*/
+ unsigned int def_val;/*effect default values*/
+};
+
+static struct ct_tuning_ctl ca0132_tuning_ctls[] = {
+ { .name = "Wedge Angle",
+ .parent_nid = VOICE_FOCUS,
+ .nid = WEDGE_ANGLE,
+ .mid = 0x95,
+ .req = 8,
+ .direct = EFX_DIR_IN,
+ .def_val = 0x41F00000
+ },
+ { .name = "SVM Level",
+ .parent_nid = MIC_SVM,
+ .nid = SVM_LEVEL,
+ .mid = 0x95,
+ .req = 45,
+ .direct = EFX_DIR_IN,
+ .def_val = 0x3F3D70A4
+ },
+ { .name = "EQ Band0",
+ .parent_nid = EQUALIZER,
+ .nid = EQUALIZER_BAND_0,
+ .mid = 0x96,
+ .req = 11,
+ .direct = EFX_DIR_OUT,
+ .def_val = 0x00000000
+ },
+ { .name = "EQ Band1",
+ .parent_nid = EQUALIZER,
+ .nid = EQUALIZER_BAND_1,
+ .mid = 0x96,
+ .req = 12,
+ .direct = EFX_DIR_OUT,
+ .def_val = 0x00000000
+ },
+ { .name = "EQ Band2",
+ .parent_nid = EQUALIZER,
+ .nid = EQUALIZER_BAND_2,
+ .mid = 0x96,
+ .req = 13,
+ .direct = EFX_DIR_OUT,
+ .def_val = 0x00000000
+ },
+ { .name = "EQ Band3",
+ .parent_nid = EQUALIZER,
+ .nid = EQUALIZER_BAND_3,
+ .mid = 0x96,
+ .req = 14,
+ .direct = EFX_DIR_OUT,
+ .def_val = 0x00000000
+ },
+ { .name = "EQ Band4",
+ .parent_nid = EQUALIZER,
+ .nid = EQUALIZER_BAND_4,
+ .mid = 0x96,
+ .req = 15,
+ .direct = EFX_DIR_OUT,
+ .def_val = 0x00000000
+ },
+ { .name = "EQ Band5",
+ .parent_nid = EQUALIZER,
+ .nid = EQUALIZER_BAND_5,
+ .mid = 0x96,
+ .req = 16,
+ .direct = EFX_DIR_OUT,
+ .def_val = 0x00000000
+ },
+ { .name = "EQ Band6",
+ .parent_nid = EQUALIZER,
+ .nid = EQUALIZER_BAND_6,
+ .mid = 0x96,
+ .req = 17,
+ .direct = EFX_DIR_OUT,
+ .def_val = 0x00000000
+ },
+ { .name = "EQ Band7",
+ .parent_nid = EQUALIZER,
+ .nid = EQUALIZER_BAND_7,
+ .mid = 0x96,
+ .req = 18,
+ .direct = EFX_DIR_OUT,
+ .def_val = 0x00000000
+ },
+ { .name = "EQ Band8",
+ .parent_nid = EQUALIZER,
+ .nid = EQUALIZER_BAND_8,
+ .mid = 0x96,
+ .req = 19,
+ .direct = EFX_DIR_OUT,
+ .def_val = 0x00000000
+ },
+ { .name = "EQ Band9",
+ .parent_nid = EQUALIZER,
+ .nid = EQUALIZER_BAND_9,
+ .mid = 0x96,
+ .req = 20,
+ .direct = EFX_DIR_OUT,
+ .def_val = 0x00000000
+ }
+};
+#endif
+
+/* Voice FX Presets */
+#define VOICEFX_MAX_PARAM_COUNT 9
+
+struct ct_voicefx {
+ char *name;
+ hda_nid_t nid;
+ int mid;
+ int reqs[VOICEFX_MAX_PARAM_COUNT]; /*effect module request*/
+};
+
+struct ct_voicefx_preset {
+ char *name; /*preset name*/
+ unsigned int vals[VOICEFX_MAX_PARAM_COUNT];
+};
+
+static struct ct_voicefx ca0132_voicefx = {
+ .name = "VoiceFX Capture Switch",
+ .nid = VOICEFX,
+ .mid = 0x95,
+ .reqs = {10, 11, 12, 13, 14, 15, 16, 17, 18}
+};
+
+static struct ct_voicefx_preset ca0132_voicefx_presets[] = {
+ { .name = "Neutral",
+ .vals = { 0x00000000, 0x43C80000, 0x44AF0000,
+ 0x44FA0000, 0x3F800000, 0x3F800000,
+ 0x3F800000, 0x00000000, 0x00000000 }
+ },
+ { .name = "Female2Male",
+ .vals = { 0x3F800000, 0x43C80000, 0x44AF0000,
+ 0x44FA0000, 0x3F19999A, 0x3F866666,
+ 0x3F800000, 0x00000000, 0x00000000 }
+ },
+ { .name = "Male2Female",
+ .vals = { 0x3F800000, 0x43C80000, 0x44AF0000,
+ 0x450AC000, 0x4017AE14, 0x3F6B851F,
+ 0x3F800000, 0x00000000, 0x00000000 }
+ },
+ { .name = "ScrappyKid",
+ .vals = { 0x3F800000, 0x43C80000, 0x44AF0000,
+ 0x44FA0000, 0x40400000, 0x3F28F5C3,
+ 0x3F800000, 0x00000000, 0x00000000 }
+ },
+ { .name = "Elderly",
+ .vals = { 0x3F800000, 0x44324000, 0x44BB8000,
+ 0x44E10000, 0x3FB33333, 0x3FB9999A,
+ 0x3F800000, 0x3E3A2E43, 0x00000000 }
+ },
+ { .name = "Orc",
+ .vals = { 0x3F800000, 0x43EA0000, 0x44A52000,
+ 0x45098000, 0x3F266666, 0x3FC00000,
+ 0x3F800000, 0x00000000, 0x00000000 }
+ },
+ { .name = "Elf",
+ .vals = { 0x3F800000, 0x43C70000, 0x44AE6000,
+ 0x45193000, 0x3F8E147B, 0x3F75C28F,
+ 0x3F800000, 0x00000000, 0x00000000 }
+ },
+ { .name = "Dwarf",
+ .vals = { 0x3F800000, 0x43930000, 0x44BEE000,
+ 0x45007000, 0x3F451EB8, 0x3F7851EC,
+ 0x3F800000, 0x00000000, 0x00000000 }
+ },
+ { .name = "AlienBrute",
+ .vals = { 0x3F800000, 0x43BFC5AC, 0x44B28FDF,
+ 0x451F6000, 0x3F266666, 0x3FA7D945,
+ 0x3F800000, 0x3CF5C28F, 0x00000000 }
+ },
+ { .name = "Robot",
+ .vals = { 0x3F800000, 0x43C80000, 0x44AF0000,
+ 0x44FA0000, 0x3FB2718B, 0x3F800000,
+ 0xBC07010E, 0x00000000, 0x00000000 }
+ },
+ { .name = "Marine",
+ .vals = { 0x3F800000, 0x43C20000, 0x44906000,
+ 0x44E70000, 0x3F4CCCCD, 0x3F8A3D71,
+ 0x3F0A3D71, 0x00000000, 0x00000000 }
+ },
+ { .name = "Emo",
+ .vals = { 0x3F800000, 0x43C80000, 0x44AF0000,
+ 0x44FA0000, 0x3F800000, 0x3F800000,
+ 0x3E4CCCCD, 0x00000000, 0x00000000 }
+ },
+ { .name = "DeepVoice",
+ .vals = { 0x3F800000, 0x43A9C5AC, 0x44AA4FDF,
+ 0x44FFC000, 0x3EDBB56F, 0x3F99C4CA,
+ 0x3F800000, 0x00000000, 0x00000000 }
+ },
+ { .name = "Munchkin",
+ .vals = { 0x3F800000, 0x43C80000, 0x44AF0000,
+ 0x44FA0000, 0x3F800000, 0x3F1A043C,
+ 0x3F800000, 0x00000000, 0x00000000 }
+ }
+};
+
+enum hda_cmd_vendor_io {
+ /* for DspIO node */
+ VENDOR_DSPIO_SCP_WRITE_DATA_LOW = 0x000,
+ VENDOR_DSPIO_SCP_WRITE_DATA_HIGH = 0x100,
+
+ VENDOR_DSPIO_STATUS = 0xF01,
+ VENDOR_DSPIO_SCP_POST_READ_DATA = 0x702,
+ VENDOR_DSPIO_SCP_READ_DATA = 0xF02,
+ VENDOR_DSPIO_DSP_INIT = 0x703,
+ VENDOR_DSPIO_SCP_POST_COUNT_QUERY = 0x704,
+ VENDOR_DSPIO_SCP_READ_COUNT = 0xF04,
+
+ /* for ChipIO node */
+ VENDOR_CHIPIO_ADDRESS_LOW = 0x000,
+ VENDOR_CHIPIO_ADDRESS_HIGH = 0x100,
+ VENDOR_CHIPIO_STREAM_FORMAT = 0x200,
+ VENDOR_CHIPIO_DATA_LOW = 0x300,
+ VENDOR_CHIPIO_DATA_HIGH = 0x400,
+
+ VENDOR_CHIPIO_GET_PARAMETER = 0xF00,
+ VENDOR_CHIPIO_STATUS = 0xF01,
+ VENDOR_CHIPIO_HIC_POST_READ = 0x702,
+ VENDOR_CHIPIO_HIC_READ_DATA = 0xF03,
+
+ VENDOR_CHIPIO_8051_DATA_WRITE = 0x707,
+ VENDOR_CHIPIO_8051_DATA_READ = 0xF07,
+
+ VENDOR_CHIPIO_CT_EXTENSIONS_ENABLE = 0x70A,
+ VENDOR_CHIPIO_CT_EXTENSIONS_GET = 0xF0A,
+
+ VENDOR_CHIPIO_PLL_PMU_WRITE = 0x70C,
+ VENDOR_CHIPIO_PLL_PMU_READ = 0xF0C,
+ VENDOR_CHIPIO_8051_ADDRESS_LOW = 0x70D,
+ VENDOR_CHIPIO_8051_ADDRESS_HIGH = 0x70E,
+ VENDOR_CHIPIO_FLAG_SET = 0x70F,
+ VENDOR_CHIPIO_FLAGS_GET = 0xF0F,
+ VENDOR_CHIPIO_PARAM_SET = 0x710,
+ VENDOR_CHIPIO_PARAM_GET = 0xF10,
+
+ VENDOR_CHIPIO_PORT_ALLOC_CONFIG_SET = 0x711,
+ VENDOR_CHIPIO_PORT_ALLOC_SET = 0x712,
+ VENDOR_CHIPIO_PORT_ALLOC_GET = 0xF12,
+ VENDOR_CHIPIO_PORT_FREE_SET = 0x713,
+
+ VENDOR_CHIPIO_PARAM_EX_ID_GET = 0xF17,
+ VENDOR_CHIPIO_PARAM_EX_ID_SET = 0x717,
+ VENDOR_CHIPIO_PARAM_EX_VALUE_GET = 0xF18,
+ VENDOR_CHIPIO_PARAM_EX_VALUE_SET = 0x718,
+
+ VENDOR_CHIPIO_DMIC_CTL_SET = 0x788,
+ VENDOR_CHIPIO_DMIC_CTL_GET = 0xF88,
+ VENDOR_CHIPIO_DMIC_PIN_SET = 0x789,
+ VENDOR_CHIPIO_DMIC_PIN_GET = 0xF89,
+ VENDOR_CHIPIO_DMIC_MCLK_SET = 0x78A,
+ VENDOR_CHIPIO_DMIC_MCLK_GET = 0xF8A,
+
+ VENDOR_CHIPIO_EAPD_SEL_SET = 0x78D
+};
+
+/*
+ * Control flag IDs
+ */
+enum control_flag_id {
+ /* Connection manager stream setup is bypassed/enabled */
+ CONTROL_FLAG_C_MGR = 0,
+ /* DSP DMA is bypassed/enabled */
+ CONTROL_FLAG_DMA = 1,
+ /* 8051 'idle' mode is disabled/enabled */
+ CONTROL_FLAG_IDLE_ENABLE = 2,
+ /* Tracker for the SPDIF-in path is bypassed/enabled */
+ CONTROL_FLAG_TRACKER = 3,
+ /* DigitalOut to Spdif2Out connection is disabled/enabled */
+ CONTROL_FLAG_SPDIF2OUT = 4,
+ /* Digital Microphone is disabled/enabled */
+ CONTROL_FLAG_DMIC = 5,
+ /* ADC_B rate is 48 kHz/96 kHz */
+ CONTROL_FLAG_ADC_B_96KHZ = 6,
+ /* ADC_C rate is 48 kHz/96 kHz */
+ CONTROL_FLAG_ADC_C_96KHZ = 7,
+ /* DAC rate is 48 kHz/96 kHz (affects all DACs) */
+ CONTROL_FLAG_DAC_96KHZ = 8,
+ /* DSP rate is 48 kHz/96 kHz */
+ CONTROL_FLAG_DSP_96KHZ = 9,
+ /* SRC clock is 98 MHz/196 MHz (196 MHz forces rate to 96 KHz) */
+ CONTROL_FLAG_SRC_CLOCK_196MHZ = 10,
+ /* SRC rate is 48 kHz/96 kHz (48 kHz disabled when clock is 196 MHz) */
+ CONTROL_FLAG_SRC_RATE_96KHZ = 11,
+ /* Decode Loop (DSP->SRC->DSP) is disabled/enabled */
+ CONTROL_FLAG_DECODE_LOOP = 12,
+ /* De-emphasis filter on DAC-1 disabled/enabled */
+ CONTROL_FLAG_DAC1_DEEMPHASIS = 13,
+ /* De-emphasis filter on DAC-2 disabled/enabled */
+ CONTROL_FLAG_DAC2_DEEMPHASIS = 14,
+ /* De-emphasis filter on DAC-3 disabled/enabled */
+ CONTROL_FLAG_DAC3_DEEMPHASIS = 15,
+ /* High-pass filter on ADC_B disabled/enabled */
+ CONTROL_FLAG_ADC_B_HIGH_PASS = 16,
+ /* High-pass filter on ADC_C disabled/enabled */
+ CONTROL_FLAG_ADC_C_HIGH_PASS = 17,
+ /* Common mode on Port_A disabled/enabled */
+ CONTROL_FLAG_PORT_A_COMMON_MODE = 18,
+ /* Common mode on Port_D disabled/enabled */
+ CONTROL_FLAG_PORT_D_COMMON_MODE = 19,
+ /* Impedance for ramp generator on Port_A 16 Ohm/10K Ohm */
+ CONTROL_FLAG_PORT_A_10KOHM_LOAD = 20,
+ /* Impedance for ramp generator on Port_D, 16 Ohm/10K Ohm */
+ CONTROL_FLAG_PORT_D_10KOHM_LOAD = 21,
+ /* ASI rate is 48kHz/96kHz */
+ CONTROL_FLAG_ASI_96KHZ = 22,
+ /* DAC power settings able to control attached ports no/yes */
+ CONTROL_FLAG_DACS_CONTROL_PORTS = 23,
+ /* Clock Stop OK reporting is disabled/enabled */
+ CONTROL_FLAG_CONTROL_STOP_OK_ENABLE = 24,
+ /* Number of control flags */
+ CONTROL_FLAGS_MAX = (CONTROL_FLAG_CONTROL_STOP_OK_ENABLE+1)
+};
+
+/*
+ * Control parameter IDs
+ */
+enum control_param_id {
+ /* 0: None, 1: Mic1In*/
+ CONTROL_PARAM_VIP_SOURCE = 1,
+ /* 0: force HDA, 1: allow DSP if HDA Spdif1Out stream is idle */
+ CONTROL_PARAM_SPDIF1_SOURCE = 2,
+ /* Port A output stage gain setting to use when 16 Ohm output
+ * impedance is selected*/
+ CONTROL_PARAM_PORTA_160OHM_GAIN = 8,
+ /* Port D output stage gain setting to use when 16 Ohm output
+ * impedance is selected*/
+ CONTROL_PARAM_PORTD_160OHM_GAIN = 10,
+
+ /* Stream Control */
+
+ /* Select stream with the given ID */
+ CONTROL_PARAM_STREAM_ID = 24,
+ /* Source connection point for the selected stream */
+ CONTROL_PARAM_STREAM_SOURCE_CONN_POINT = 25,
+ /* Destination connection point for the selected stream */
+ CONTROL_PARAM_STREAM_DEST_CONN_POINT = 26,
+ /* Number of audio channels in the selected stream */
+ CONTROL_PARAM_STREAMS_CHANNELS = 27,
+ /*Enable control for the selected stream */
+ CONTROL_PARAM_STREAM_CONTROL = 28,
+
+ /* Connection Point Control */
+
+ /* Select connection point with the given ID */
+ CONTROL_PARAM_CONN_POINT_ID = 29,
+ /* Connection point sample rate */
+ CONTROL_PARAM_CONN_POINT_SAMPLE_RATE = 30,
+
+ /* Node Control */
+
+ /* Select HDA node with the given ID */
+ CONTROL_PARAM_NODE_ID = 31
+};
+
+/*
+ * Dsp Io Status codes
+ */
+enum hda_vendor_status_dspio {
+ /* Success */
+ VENDOR_STATUS_DSPIO_OK = 0x00,
+ /* Busy, unable to accept new command, the host must retry */
+ VENDOR_STATUS_DSPIO_BUSY = 0x01,
+ /* SCP command queue is full */
+ VENDOR_STATUS_DSPIO_SCP_COMMAND_QUEUE_FULL = 0x02,
+ /* SCP response queue is empty */
+ VENDOR_STATUS_DSPIO_SCP_RESPONSE_QUEUE_EMPTY = 0x03
+};
+
+/*
+ * Chip Io Status codes
+ */
+enum hda_vendor_status_chipio {
+ /* Success */
+ VENDOR_STATUS_CHIPIO_OK = 0x00,
+ /* Busy, unable to accept new command, the host must retry */
+ VENDOR_STATUS_CHIPIO_BUSY = 0x01
+};
+
+/*
+ * CA0132 sample rate
+ */
+enum ca0132_sample_rate {
+ SR_6_000 = 0x00,
+ SR_8_000 = 0x01,
+ SR_9_600 = 0x02,
+ SR_11_025 = 0x03,
+ SR_16_000 = 0x04,
+ SR_22_050 = 0x05,
+ SR_24_000 = 0x06,
+ SR_32_000 = 0x07,
+ SR_44_100 = 0x08,
+ SR_48_000 = 0x09,
+ SR_88_200 = 0x0A,
+ SR_96_000 = 0x0B,
+ SR_144_000 = 0x0C,
+ SR_176_400 = 0x0D,
+ SR_192_000 = 0x0E,
+ SR_384_000 = 0x0F,
+
+ SR_COUNT = 0x10,
+
+ SR_RATE_UNKNOWN = 0x1F
+};
+
+enum dsp_download_state {
+ DSP_DOWNLOAD_FAILED = -1,
+ DSP_DOWNLOAD_INIT = 0,
+ DSP_DOWNLOADING = 1,
+ DSP_DOWNLOADED = 2
+};
+
+/* retrieve parameters from hda format */
+#define get_hdafmt_chs(fmt) (fmt & 0xf)
+#define get_hdafmt_bits(fmt) ((fmt >> 4) & 0x7)
+#define get_hdafmt_rate(fmt) ((fmt >> 8) & 0x7f)
+#define get_hdafmt_type(fmt) ((fmt >> 15) & 0x1)
+
+/*
+ * CA0132 specific
+ */
+
+struct ca0132_spec {
+ struct snd_kcontrol_new *mixers[5];
+ unsigned int num_mixers;
+ const struct hda_verb *base_init_verbs;
+ const struct hda_verb *base_exit_verbs;
+ const struct hda_verb *init_verbs[5];
+ unsigned int num_init_verbs; /* exclude base init verbs */
+ struct auto_pin_cfg autocfg;
+
+ /* Nodes configurations */
+ struct hda_multi_out multiout;
+ hda_nid_t out_pins[AUTO_CFG_MAX_OUTS];
+ hda_nid_t dacs[AUTO_CFG_MAX_OUTS];
+ unsigned int num_outputs;
+ hda_nid_t input_pins[AUTO_PIN_LAST];
+ hda_nid_t adcs[AUTO_PIN_LAST];
+ hda_nid_t dig_out;
+ hda_nid_t dig_in;
+ unsigned int num_inputs;
+ hda_nid_t shared_mic_nid;
+ hda_nid_t shared_out_nid;
+ struct hda_pcm pcm_rec[5]; /* PCM information */
+
+ /* chip access */
+ struct mutex chipio_mutex; /* chip access mutex */
+ u32 curr_chip_addx;
+
+ /* DSP download related */
+ enum dsp_download_state dsp_state;
+ unsigned int dsp_stream_id;
+ unsigned int wait_scp;
+ unsigned int wait_scp_header;
+ unsigned int wait_num_data;
+ unsigned int scp_resp_header;
+ unsigned int scp_resp_data[4];
+ unsigned int scp_resp_count;
+
+ /* mixer and effects related */
+ unsigned char dmic_ctl;
+ int cur_out_type;
+ int cur_mic_type;
+ long vnode_lvol[VNODES_COUNT];
+ long vnode_rvol[VNODES_COUNT];
+ long vnode_lswitch[VNODES_COUNT];
+ long vnode_rswitch[VNODES_COUNT];
+ long effects_switch[EFFECTS_COUNT];
+ long voicefx_val;
+ long cur_mic_boost;
+
+ struct hda_codec *codec;
+ struct delayed_work unsol_hp_work;
+
+#ifdef ENABLE_TUNING_CONTROLS
+ long cur_ctl_vals[TUNING_CTLS_COUNT];
+#endif
+};
+
+/*
+ * CA0132 codec access
+ */
+static unsigned int codec_send_command(struct hda_codec *codec, hda_nid_t nid,
+ unsigned int verb, unsigned int parm, unsigned int *res)
+{
+ unsigned int response;
+ response = snd_hda_codec_read(codec, nid, 0, verb, parm);
+ *res = response;
+
+ return ((response == -1) ? -1 : 0);
+}
+
+static int codec_set_converter_format(struct hda_codec *codec, hda_nid_t nid,
+ unsigned short converter_format, unsigned int *res)
+{
+ return codec_send_command(codec, nid, VENDOR_CHIPIO_STREAM_FORMAT,
+ converter_format & 0xffff, res);
+}
+
+static int codec_set_converter_stream_channel(struct hda_codec *codec,
+ hda_nid_t nid, unsigned char stream,
+ unsigned char channel, unsigned int *res)
+{
+ unsigned char converter_stream_channel = 0;
+
+ converter_stream_channel = (stream << 4) | (channel & 0x0f);
+ return codec_send_command(codec, nid, AC_VERB_SET_CHANNEL_STREAMID,
+ converter_stream_channel, res);
+}
+
+/* Chip access helper function */
+static int chipio_send(struct hda_codec *codec,
+ unsigned int reg,
+ unsigned int data)
+{
+ unsigned int res;
+ unsigned long timeout = jiffies + msecs_to_jiffies(1000);
+
+ /* send bits of data specified by reg */
+ do {
+ res = snd_hda_codec_read(codec, WIDGET_CHIP_CTRL, 0,
+ reg, data);
+ if (res == VENDOR_STATUS_CHIPIO_OK)
+ return 0;
+ msleep(20);
+ } while (time_before(jiffies, timeout));
+
+ return -EIO;
+}
+
+/*
+ * Write chip address through the vendor widget -- NOT protected by the Mutex!
+ */
+static int chipio_write_address(struct hda_codec *codec,
+ unsigned int chip_addx)
+{
+ struct ca0132_spec *spec = codec->spec;
+ int res;
+
+ if (spec->curr_chip_addx == chip_addx)
+ return 0;
+
+ /* send low 16 bits of the address */
+ res = chipio_send(codec, VENDOR_CHIPIO_ADDRESS_LOW,
+ chip_addx & 0xffff);
+
+ if (res != -EIO) {
+ /* send high 16 bits of the address */
+ res = chipio_send(codec, VENDOR_CHIPIO_ADDRESS_HIGH,
+ chip_addx >> 16);
+ }
+
+ spec->curr_chip_addx = (res < 0) ? ~0UL : chip_addx;
+
+ return res;
+}
+
+/*
+ * Write data through the vendor widget -- NOT protected by the Mutex!
+ */
+static int chipio_write_data(struct hda_codec *codec, unsigned int data)
+{
+ struct ca0132_spec *spec = codec->spec;
+ int res;
+
+ /* send low 16 bits of the data */
+ res = chipio_send(codec, VENDOR_CHIPIO_DATA_LOW, data & 0xffff);
+
+ if (res != -EIO) {
+ /* send high 16 bits of the data */
+ res = chipio_send(codec, VENDOR_CHIPIO_DATA_HIGH,
+ data >> 16);
+ }
+
+ /*If no error encountered, automatically increment the address
+ as per chip behaviour*/
+ spec->curr_chip_addx = (res != -EIO) ?
+ (spec->curr_chip_addx + 4) : ~0UL;
+ return res;
+}
+
+/*
+ * Write multiple data through the vendor widget -- NOT protected by the Mutex!
+ */
+static int chipio_write_data_multiple(struct hda_codec *codec,
+ const u32 *data,
+ unsigned int count)
+{
+ int status = 0;
+
+ if (data == NULL) {
+ codec_dbg(codec, "chipio_write_data null ptr\n");
+ return -EINVAL;
+ }
+
+ while ((count-- != 0) && (status == 0))
+ status = chipio_write_data(codec, *data++);
+
+ return status;
+}
+
+
+/*
+ * Read data through the vendor widget -- NOT protected by the Mutex!
+ */
+static int chipio_read_data(struct hda_codec *codec, unsigned int *data)
+{
+ struct ca0132_spec *spec = codec->spec;
+ int res;
+
+ /* post read */
+ res = chipio_send(codec, VENDOR_CHIPIO_HIC_POST_READ, 0);
+
+ if (res != -EIO) {
+ /* read status */
+ res = chipio_send(codec, VENDOR_CHIPIO_STATUS, 0);
+ }
+
+ if (res != -EIO) {
+ /* read data */
+ *data = snd_hda_codec_read(codec, WIDGET_CHIP_CTRL, 0,
+ VENDOR_CHIPIO_HIC_READ_DATA,
+ 0);
+ }
+
+ /*If no error encountered, automatically increment the address
+ as per chip behaviour*/
+ spec->curr_chip_addx = (res != -EIO) ?
+ (spec->curr_chip_addx + 4) : ~0UL;
+ return res;
+}
+
+/*
+ * Write given value to the given address through the chip I/O widget.
+ * protected by the Mutex
+ */
+static int chipio_write(struct hda_codec *codec,
+ unsigned int chip_addx, const unsigned int data)
+{
+ struct ca0132_spec *spec = codec->spec;
+ int err;
+
+ mutex_lock(&spec->chipio_mutex);
+
+ /* write the address, and if successful proceed to write data */
+ err = chipio_write_address(codec, chip_addx);
+ if (err < 0)
+ goto exit;
+
+ err = chipio_write_data(codec, data);
+ if (err < 0)
+ goto exit;
+
+exit:
+ mutex_unlock(&spec->chipio_mutex);
+ return err;
+}
+
+/*
+ * Write multiple values to the given address through the chip I/O widget.
+ * protected by the Mutex
+ */
+static int chipio_write_multiple(struct hda_codec *codec,
+ u32 chip_addx,
+ const u32 *data,
+ unsigned int count)
+{
+ struct ca0132_spec *spec = codec->spec;
+ int status;
+
+ mutex_lock(&spec->chipio_mutex);
+ status = chipio_write_address(codec, chip_addx);
+ if (status < 0)
+ goto error;
+
+ status = chipio_write_data_multiple(codec, data, count);
+error:
+ mutex_unlock(&spec->chipio_mutex);
+
+ return status;
+}
+
+/*
+ * Read the given address through the chip I/O widget
+ * protected by the Mutex
+ */
+static int chipio_read(struct hda_codec *codec,
+ unsigned int chip_addx, unsigned int *data)
+{
+ struct ca0132_spec *spec = codec->spec;
+ int err;
+
+ mutex_lock(&spec->chipio_mutex);
+
+ /* write the address, and if successful proceed to write data */
+ err = chipio_write_address(codec, chip_addx);
+ if (err < 0)
+ goto exit;
+
+ err = chipio_read_data(codec, data);
+ if (err < 0)
+ goto exit;
+
+exit:
+ mutex_unlock(&spec->chipio_mutex);
+ return err;
+}
+
+/*
+ * Set chip control flags through the chip I/O widget.
+ */
+static void chipio_set_control_flag(struct hda_codec *codec,
+ enum control_flag_id flag_id,
+ bool flag_state)
+{
+ unsigned int val;
+ unsigned int flag_bit;
+
+ flag_bit = (flag_state ? 1 : 0);
+ val = (flag_bit << 7) | (flag_id);
+ snd_hda_codec_write(codec, WIDGET_CHIP_CTRL, 0,
+ VENDOR_CHIPIO_FLAG_SET, val);
+}
+
+/*
+ * Set chip parameters through the chip I/O widget.
+ */
+static void chipio_set_control_param(struct hda_codec *codec,
+ enum control_param_id param_id, int param_val)
+{
+ struct ca0132_spec *spec = codec->spec;
+ int val;
+
+ if ((param_id < 32) && (param_val < 8)) {
+ val = (param_val << 5) | (param_id);
+ snd_hda_codec_write(codec, WIDGET_CHIP_CTRL, 0,
+ VENDOR_CHIPIO_PARAM_SET, val);
+ } else {
+ mutex_lock(&spec->chipio_mutex);
+ if (chipio_send(codec, VENDOR_CHIPIO_STATUS, 0) == 0) {
+ snd_hda_codec_write(codec, WIDGET_CHIP_CTRL, 0,
+ VENDOR_CHIPIO_PARAM_EX_ID_SET,
+ param_id);
+ snd_hda_codec_write(codec, WIDGET_CHIP_CTRL, 0,
+ VENDOR_CHIPIO_PARAM_EX_VALUE_SET,
+ param_val);
+ }
+ mutex_unlock(&spec->chipio_mutex);
+ }
+}
+
+/*
+ * Set sampling rate of the connection point.
+ */
+static void chipio_set_conn_rate(struct hda_codec *codec,
+ int connid, enum ca0132_sample_rate rate)
+{
+ chipio_set_control_param(codec, CONTROL_PARAM_CONN_POINT_ID, connid);
+ chipio_set_control_param(codec, CONTROL_PARAM_CONN_POINT_SAMPLE_RATE,
+ rate);
+}
+
+/*
+ * Enable clocks.
+ */
+static void chipio_enable_clocks(struct hda_codec *codec)
+{
+ struct ca0132_spec *spec = codec->spec;
+
+ mutex_lock(&spec->chipio_mutex);
+ snd_hda_codec_write(codec, WIDGET_CHIP_CTRL, 0,
+ VENDOR_CHIPIO_8051_ADDRESS_LOW, 0);
+ snd_hda_codec_write(codec, WIDGET_CHIP_CTRL, 0,
+ VENDOR_CHIPIO_PLL_PMU_WRITE, 0xff);
+ snd_hda_codec_write(codec, WIDGET_CHIP_CTRL, 0,
+ VENDOR_CHIPIO_8051_ADDRESS_LOW, 5);
+ snd_hda_codec_write(codec, WIDGET_CHIP_CTRL, 0,
+ VENDOR_CHIPIO_PLL_PMU_WRITE, 0x0b);
+ snd_hda_codec_write(codec, WIDGET_CHIP_CTRL, 0,
+ VENDOR_CHIPIO_8051_ADDRESS_LOW, 6);
+ snd_hda_codec_write(codec, WIDGET_CHIP_CTRL, 0,
+ VENDOR_CHIPIO_PLL_PMU_WRITE, 0xff);
+ mutex_unlock(&spec->chipio_mutex);
+}
+
+/*
+ * CA0132 DSP IO stuffs
+ */
+static int dspio_send(struct hda_codec *codec, unsigned int reg,
+ unsigned int data)
+{
+ int res;
+ unsigned long timeout = jiffies + msecs_to_jiffies(1000);
+
+ /* send bits of data specified by reg to dsp */
+ do {
+ res = snd_hda_codec_read(codec, WIDGET_DSP_CTRL, 0, reg, data);
+ if ((res >= 0) && (res != VENDOR_STATUS_DSPIO_BUSY))
+ return res;
+ msleep(20);
+ } while (time_before(jiffies, timeout));
+
+ return -EIO;
+}
+
+/*
+ * Wait for DSP to be ready for commands
+ */
+static void dspio_write_wait(struct hda_codec *codec)
+{
+ int status;
+ unsigned long timeout = jiffies + msecs_to_jiffies(1000);
+
+ do {
+ status = snd_hda_codec_read(codec, WIDGET_DSP_CTRL, 0,
+ VENDOR_DSPIO_STATUS, 0);
+ if ((status == VENDOR_STATUS_DSPIO_OK) ||
+ (status == VENDOR_STATUS_DSPIO_SCP_RESPONSE_QUEUE_EMPTY))
+ break;
+ msleep(1);
+ } while (time_before(jiffies, timeout));
+}
+
+/*
+ * Write SCP data to DSP
+ */
+static int dspio_write(struct hda_codec *codec, unsigned int scp_data)
+{
+ struct ca0132_spec *spec = codec->spec;
+ int status;
+
+ dspio_write_wait(codec);
+
+ mutex_lock(&spec->chipio_mutex);
+ status = dspio_send(codec, VENDOR_DSPIO_SCP_WRITE_DATA_LOW,
+ scp_data & 0xffff);
+ if (status < 0)
+ goto error;
+
+ status = dspio_send(codec, VENDOR_DSPIO_SCP_WRITE_DATA_HIGH,
+ scp_data >> 16);
+ if (status < 0)
+ goto error;
+
+ /* OK, now check if the write itself has executed*/
+ status = snd_hda_codec_read(codec, WIDGET_DSP_CTRL, 0,
+ VENDOR_DSPIO_STATUS, 0);
+error:
+ mutex_unlock(&spec->chipio_mutex);
+
+ return (status == VENDOR_STATUS_DSPIO_SCP_COMMAND_QUEUE_FULL) ?
+ -EIO : 0;
+}
+
+/*
+ * Write multiple SCP data to DSP
+ */
+static int dspio_write_multiple(struct hda_codec *codec,
+ unsigned int *buffer, unsigned int size)
+{
+ int status = 0;
+ unsigned int count;
+
+ if ((buffer == NULL))
+ return -EINVAL;
+
+ count = 0;
+ while (count < size) {
+ status = dspio_write(codec, *buffer++);
+ if (status != 0)
+ break;
+ count++;
+ }
+
+ return status;
+}
+
+static int dspio_read(struct hda_codec *codec, unsigned int *data)
+{
+ int status;
+
+ status = dspio_send(codec, VENDOR_DSPIO_SCP_POST_READ_DATA, 0);
+ if (status == -EIO)
+ return status;
+
+ status = dspio_send(codec, VENDOR_DSPIO_STATUS, 0);
+ if (status == -EIO ||
+ status == VENDOR_STATUS_DSPIO_SCP_RESPONSE_QUEUE_EMPTY)
+ return -EIO;
+
+ *data = snd_hda_codec_read(codec, WIDGET_DSP_CTRL, 0,
+ VENDOR_DSPIO_SCP_READ_DATA, 0);
+
+ return 0;
+}
+
+static int dspio_read_multiple(struct hda_codec *codec, unsigned int *buffer,
+ unsigned int *buf_size, unsigned int size_count)
+{
+ int status = 0;
+ unsigned int size = *buf_size;
+ unsigned int count;
+ unsigned int skip_count;
+ unsigned int dummy;
+
+ if ((buffer == NULL))
+ return -1;
+
+ count = 0;
+ while (count < size && count < size_count) {
+ status = dspio_read(codec, buffer++);
+ if (status != 0)
+ break;
+ count++;
+ }
+
+ skip_count = count;
+ if (status == 0) {
+ while (skip_count < size) {
+ status = dspio_read(codec, &dummy);
+ if (status != 0)
+ break;
+ skip_count++;
+ }
+ }
+ *buf_size = count;
+
+ return status;
+}
+
+/*
+ * Construct the SCP header using corresponding fields
+ */
+static inline unsigned int
+make_scp_header(unsigned int target_id, unsigned int source_id,
+ unsigned int get_flag, unsigned int req,
+ unsigned int device_flag, unsigned int resp_flag,
+ unsigned int error_flag, unsigned int data_size)
+{
+ unsigned int header = 0;
+
+ header = (data_size & 0x1f) << 27;
+ header |= (error_flag & 0x01) << 26;
+ header |= (resp_flag & 0x01) << 25;
+ header |= (device_flag & 0x01) << 24;
+ header |= (req & 0x7f) << 17;
+ header |= (get_flag & 0x01) << 16;
+ header |= (source_id & 0xff) << 8;
+ header |= target_id & 0xff;
+
+ return header;
+}
+
+/*
+ * Extract corresponding fields from SCP header
+ */
+static inline void
+extract_scp_header(unsigned int header,
+ unsigned int *target_id, unsigned int *source_id,
+ unsigned int *get_flag, unsigned int *req,
+ unsigned int *device_flag, unsigned int *resp_flag,
+ unsigned int *error_flag, unsigned int *data_size)
+{
+ if (data_size)
+ *data_size = (header >> 27) & 0x1f;
+ if (error_flag)
+ *error_flag = (header >> 26) & 0x01;
+ if (resp_flag)
+ *resp_flag = (header >> 25) & 0x01;
+ if (device_flag)
+ *device_flag = (header >> 24) & 0x01;
+ if (req)
+ *req = (header >> 17) & 0x7f;
+ if (get_flag)
+ *get_flag = (header >> 16) & 0x01;
+ if (source_id)
+ *source_id = (header >> 8) & 0xff;
+ if (target_id)
+ *target_id = header & 0xff;
+}
+
+#define SCP_MAX_DATA_WORDS (16)
+
+/* Structure to contain any SCP message */
+struct scp_msg {
+ unsigned int hdr;
+ unsigned int data[SCP_MAX_DATA_WORDS];
+};
+
+static void dspio_clear_response_queue(struct hda_codec *codec)
+{
+ unsigned int dummy = 0;
+ int status = -1;
+
+ /* clear all from the response queue */
+ do {
+ status = dspio_read(codec, &dummy);
+ } while (status == 0);
+}
+
+static int dspio_get_response_data(struct hda_codec *codec)
+{
+ struct ca0132_spec *spec = codec->spec;
+ unsigned int data = 0;
+ unsigned int count;
+
+ if (dspio_read(codec, &data) < 0)
+ return -EIO;
+
+ if ((data & 0x00ffffff) == spec->wait_scp_header) {
+ spec->scp_resp_header = data;
+ spec->scp_resp_count = data >> 27;
+ count = spec->wait_num_data;
+ dspio_read_multiple(codec, spec->scp_resp_data,
+ &spec->scp_resp_count, count);
+ return 0;
+ }
+
+ return -EIO;
+}
+
+/*
+ * Send SCP message to DSP
+ */
+static int dspio_send_scp_message(struct hda_codec *codec,
+ unsigned char *send_buf,
+ unsigned int send_buf_size,
+ unsigned char *return_buf,
+ unsigned int return_buf_size,
+ unsigned int *bytes_returned)
+{
+ struct ca0132_spec *spec = codec->spec;
+ int status = -1;
+ unsigned int scp_send_size = 0;
+ unsigned int total_size;
+ bool waiting_for_resp = false;
+ unsigned int header;
+ struct scp_msg *ret_msg;
+ unsigned int resp_src_id, resp_target_id;
+ unsigned int data_size, src_id, target_id, get_flag, device_flag;
+
+ if (bytes_returned)
+ *bytes_returned = 0;
+
+ /* get scp header from buffer */
+ header = *((unsigned int *)send_buf);
+ extract_scp_header(header, &target_id, &src_id, &get_flag, NULL,
+ &device_flag, NULL, NULL, &data_size);
+ scp_send_size = data_size + 1;
+ total_size = (scp_send_size * 4);
+
+ if (send_buf_size < total_size)
+ return -EINVAL;
+
+ if (get_flag || device_flag) {
+ if (!return_buf || return_buf_size < 4 || !bytes_returned)
+ return -EINVAL;
+
+ spec->wait_scp_header = *((unsigned int *)send_buf);
+
+ /* swap source id with target id */
+ resp_target_id = src_id;
+ resp_src_id = target_id;
+ spec->wait_scp_header &= 0xffff0000;
+ spec->wait_scp_header |= (resp_src_id << 8) | (resp_target_id);
+ spec->wait_num_data = return_buf_size/sizeof(unsigned int) - 1;
+ spec->wait_scp = 1;
+ waiting_for_resp = true;
+ }
+
+ status = dspio_write_multiple(codec, (unsigned int *)send_buf,
+ scp_send_size);
+ if (status < 0) {
+ spec->wait_scp = 0;
+ return status;
+ }
+
+ if (waiting_for_resp) {
+ unsigned long timeout = jiffies + msecs_to_jiffies(1000);
+ memset(return_buf, 0, return_buf_size);
+ do {
+ msleep(20);
+ } while (spec->wait_scp && time_before(jiffies, timeout));
+ waiting_for_resp = false;
+ if (!spec->wait_scp) {
+ ret_msg = (struct scp_msg *)return_buf;
+ memcpy(&ret_msg->hdr, &spec->scp_resp_header, 4);
+ memcpy(&ret_msg->data, spec->scp_resp_data,
+ spec->wait_num_data);
+ *bytes_returned = (spec->scp_resp_count + 1) * 4;
+ status = 0;
+ } else {
+ status = -EIO;
+ }
+ spec->wait_scp = 0;
+ }
+
+ return status;
+}
+
+/**
+ * Prepare and send the SCP message to DSP
+ * @codec: the HDA codec
+ * @mod_id: ID of the DSP module to send the command
+ * @req: ID of request to send to the DSP module
+ * @dir: SET or GET
+ * @data: pointer to the data to send with the request, request specific
+ * @len: length of the data, in bytes
+ * @reply: point to the buffer to hold data returned for a reply
+ * @reply_len: length of the reply buffer returned from GET
+ *
+ * Returns zero or a negative error code.
+ */
+static int dspio_scp(struct hda_codec *codec,
+ int mod_id, int req, int dir, void *data, unsigned int len,
+ void *reply, unsigned int *reply_len)
+{
+ int status = 0;
+ struct scp_msg scp_send, scp_reply;
+ unsigned int ret_bytes, send_size, ret_size;
+ unsigned int send_get_flag, reply_resp_flag, reply_error_flag;
+ unsigned int reply_data_size;
+
+ memset(&scp_send, 0, sizeof(scp_send));
+ memset(&scp_reply, 0, sizeof(scp_reply));
+
+ if ((len != 0 && data == NULL) || (len > SCP_MAX_DATA_WORDS))
+ return -EINVAL;
+
+ if (dir == SCP_GET && reply == NULL) {
+ codec_dbg(codec, "dspio_scp get but has no buffer\n");
+ return -EINVAL;
+ }
+
+ if (reply != NULL && (reply_len == NULL || (*reply_len == 0))) {
+ codec_dbg(codec, "dspio_scp bad resp buf len parms\n");
+ return -EINVAL;
+ }
+
+ scp_send.hdr = make_scp_header(mod_id, 0x20, (dir == SCP_GET), req,
+ 0, 0, 0, len/sizeof(unsigned int));
+ if (data != NULL && len > 0) {
+ len = min((unsigned int)(sizeof(scp_send.data)), len);
+ memcpy(scp_send.data, data, len);
+ }
+
+ ret_bytes = 0;
+ send_size = sizeof(unsigned int) + len;
+ status = dspio_send_scp_message(codec, (unsigned char *)&scp_send,
+ send_size, (unsigned char *)&scp_reply,
+ sizeof(scp_reply), &ret_bytes);
+
+ if (status < 0) {
+ codec_dbg(codec, "dspio_scp: send scp msg failed\n");
+ return status;
+ }
+
+ /* extract send and reply headers members */
+ extract_scp_header(scp_send.hdr, NULL, NULL, &send_get_flag,
+ NULL, NULL, NULL, NULL, NULL);
+ extract_scp_header(scp_reply.hdr, NULL, NULL, NULL, NULL, NULL,
+ &reply_resp_flag, &reply_error_flag,
+ &reply_data_size);
+
+ if (!send_get_flag)
+ return 0;
+
+ if (reply_resp_flag && !reply_error_flag) {
+ ret_size = (ret_bytes - sizeof(scp_reply.hdr))
+ / sizeof(unsigned int);
+
+ if (*reply_len < ret_size*sizeof(unsigned int)) {
+ codec_dbg(codec, "reply too long for buf\n");
+ return -EINVAL;
+ } else if (ret_size != reply_data_size) {
+ codec_dbg(codec, "RetLen and HdrLen .NE.\n");
+ return -EINVAL;
+ } else {
+ *reply_len = ret_size*sizeof(unsigned int);
+ memcpy(reply, scp_reply.data, *reply_len);
+ }
+ } else {
+ codec_dbg(codec, "reply ill-formed or errflag set\n");
+ return -EIO;
+ }
+
+ return status;
+}
+
+/*
+ * Set DSP parameters
+ */
+static int dspio_set_param(struct hda_codec *codec, int mod_id,
+ int req, void *data, unsigned int len)
+{
+ return dspio_scp(codec, mod_id, req, SCP_SET, data, len, NULL, NULL);
+}
+
+static int dspio_set_uint_param(struct hda_codec *codec, int mod_id,
+ int req, unsigned int data)
+{
+ return dspio_set_param(codec, mod_id, req, &data, sizeof(unsigned int));
+}
+
+/*
+ * Allocate a DSP DMA channel via an SCP message
+ */
+static int dspio_alloc_dma_chan(struct hda_codec *codec, unsigned int *dma_chan)
+{
+ int status = 0;
+ unsigned int size = sizeof(dma_chan);
+
+ codec_dbg(codec, " dspio_alloc_dma_chan() -- begin\n");
+ status = dspio_scp(codec, MASTERCONTROL, MASTERCONTROL_ALLOC_DMA_CHAN,
+ SCP_GET, NULL, 0, dma_chan, &size);
+
+ if (status < 0) {
+ codec_dbg(codec, "dspio_alloc_dma_chan: SCP Failed\n");
+ return status;
+ }
+
+ if ((*dma_chan + 1) == 0) {
+ codec_dbg(codec, "no free dma channels to allocate\n");
+ return -EBUSY;
+ }
+
+ codec_dbg(codec, "dspio_alloc_dma_chan: chan=%d\n", *dma_chan);
+ codec_dbg(codec, " dspio_alloc_dma_chan() -- complete\n");
+
+ return status;
+}
+
+/*
+ * Free a DSP DMA via an SCP message
+ */
+static int dspio_free_dma_chan(struct hda_codec *codec, unsigned int dma_chan)
+{
+ int status = 0;
+ unsigned int dummy = 0;
+
+ codec_dbg(codec, " dspio_free_dma_chan() -- begin\n");
+ codec_dbg(codec, "dspio_free_dma_chan: chan=%d\n", dma_chan);
+
+ status = dspio_scp(codec, MASTERCONTROL, MASTERCONTROL_ALLOC_DMA_CHAN,
+ SCP_SET, &dma_chan, sizeof(dma_chan), NULL, &dummy);
+
+ if (status < 0) {
+ codec_dbg(codec, "dspio_free_dma_chan: SCP Failed\n");
+ return status;
+ }
+
+ codec_dbg(codec, " dspio_free_dma_chan() -- complete\n");
+
+ return status;
+}
+
+/*
+ * (Re)start the DSP
+ */
+static int dsp_set_run_state(struct hda_codec *codec)
+{
+ unsigned int dbg_ctrl_reg;
+ unsigned int halt_state;
+ int err;
+
+ err = chipio_read(codec, DSP_DBGCNTL_INST_OFFSET, &dbg_ctrl_reg);
+ if (err < 0)
+ return err;
+
+ halt_state = (dbg_ctrl_reg & DSP_DBGCNTL_STATE_MASK) >>
+ DSP_DBGCNTL_STATE_LOBIT;
+
+ if (halt_state != 0) {
+ dbg_ctrl_reg &= ~((halt_state << DSP_DBGCNTL_SS_LOBIT) &
+ DSP_DBGCNTL_SS_MASK);
+ err = chipio_write(codec, DSP_DBGCNTL_INST_OFFSET,
+ dbg_ctrl_reg);
+ if (err < 0)
+ return err;
+
+ dbg_ctrl_reg |= (halt_state << DSP_DBGCNTL_EXEC_LOBIT) &
+ DSP_DBGCNTL_EXEC_MASK;
+ err = chipio_write(codec, DSP_DBGCNTL_INST_OFFSET,
+ dbg_ctrl_reg);
+ if (err < 0)
+ return err;
+ }
+
+ return 0;
+}
+
+/*
+ * Reset the DSP
+ */
+static int dsp_reset(struct hda_codec *codec)
+{
+ unsigned int res;
+ int retry = 20;
+
+ codec_dbg(codec, "dsp_reset\n");
+ do {
+ res = dspio_send(codec, VENDOR_DSPIO_DSP_INIT, 0);
+ retry--;
+ } while (res == -EIO && retry);
+
+ if (!retry) {
+ codec_dbg(codec, "dsp_reset timeout\n");
+ return -EIO;
+ }
+
+ return 0;
+}
+
+/*
+ * Convert chip address to DSP address
+ */
+static unsigned int dsp_chip_to_dsp_addx(unsigned int chip_addx,
+ bool *code, bool *yram)
+{
+ *code = *yram = false;
+
+ if (UC_RANGE(chip_addx, 1)) {
+ *code = true;
+ return UC_OFF(chip_addx);
+ } else if (X_RANGE_ALL(chip_addx, 1)) {
+ return X_OFF(chip_addx);
+ } else if (Y_RANGE_ALL(chip_addx, 1)) {
+ *yram = true;
+ return Y_OFF(chip_addx);
+ }
+
+ return INVALID_CHIP_ADDRESS;
+}
+
+/*
+ * Check if the DSP DMA is active
+ */
+static bool dsp_is_dma_active(struct hda_codec *codec, unsigned int dma_chan)
+{
+ unsigned int dma_chnlstart_reg;
+
+ chipio_read(codec, DSPDMAC_CHNLSTART_INST_OFFSET, &dma_chnlstart_reg);
+
+ return ((dma_chnlstart_reg & (1 <<
+ (DSPDMAC_CHNLSTART_EN_LOBIT + dma_chan))) != 0);
+}
+
+static int dsp_dma_setup_common(struct hda_codec *codec,
+ unsigned int chip_addx,
+ unsigned int dma_chan,
+ unsigned int port_map_mask,
+ bool ovly)
+{
+ int status = 0;
+ unsigned int chnl_prop;
+ unsigned int dsp_addx;
+ unsigned int active;
+ bool code, yram;
+
+ codec_dbg(codec, "-- dsp_dma_setup_common() -- Begin ---------\n");
+
+ if (dma_chan >= DSPDMAC_DMA_CFG_CHANNEL_COUNT) {
+ codec_dbg(codec, "dma chan num invalid\n");
+ return -EINVAL;
+ }
+
+ if (dsp_is_dma_active(codec, dma_chan)) {
+ codec_dbg(codec, "dma already active\n");
+ return -EBUSY;
+ }
+
+ dsp_addx = dsp_chip_to_dsp_addx(chip_addx, &code, &yram);
+
+ if (dsp_addx == INVALID_CHIP_ADDRESS) {
+ codec_dbg(codec, "invalid chip addr\n");
+ return -ENXIO;
+ }
+
+ chnl_prop = DSPDMAC_CHNLPROP_AC_MASK;
+ active = 0;
+
+ codec_dbg(codec, " dsp_dma_setup_common() start reg pgm\n");
+
+ if (ovly) {
+ status = chipio_read(codec, DSPDMAC_CHNLPROP_INST_OFFSET,
+ &chnl_prop);
+
+ if (status < 0) {
+ codec_dbg(codec, "read CHNLPROP Reg fail\n");
+ return status;
+ }
+ codec_dbg(codec, "dsp_dma_setup_common() Read CHNLPROP\n");
+ }
+
+ if (!code)
+ chnl_prop &= ~(1 << (DSPDMAC_CHNLPROP_MSPCE_LOBIT + dma_chan));
+ else
+ chnl_prop |= (1 << (DSPDMAC_CHNLPROP_MSPCE_LOBIT + dma_chan));
+
+ chnl_prop &= ~(1 << (DSPDMAC_CHNLPROP_DCON_LOBIT + dma_chan));
+
+ status = chipio_write(codec, DSPDMAC_CHNLPROP_INST_OFFSET, chnl_prop);
+ if (status < 0) {
+ codec_dbg(codec, "write CHNLPROP Reg fail\n");
+ return status;
+ }
+ codec_dbg(codec, " dsp_dma_setup_common() Write CHNLPROP\n");
+
+ if (ovly) {
+ status = chipio_read(codec, DSPDMAC_ACTIVE_INST_OFFSET,
+ &active);
+
+ if (status < 0) {
+ codec_dbg(codec, "read ACTIVE Reg fail\n");
+ return status;
+ }
+ codec_dbg(codec, "dsp_dma_setup_common() Read ACTIVE\n");
+ }
+
+ active &= (~(1 << (DSPDMAC_ACTIVE_AAR_LOBIT + dma_chan))) &
+ DSPDMAC_ACTIVE_AAR_MASK;
+
+ status = chipio_write(codec, DSPDMAC_ACTIVE_INST_OFFSET, active);
+ if (status < 0) {
+ codec_dbg(codec, "write ACTIVE Reg fail\n");
+ return status;
+ }
+
+ codec_dbg(codec, " dsp_dma_setup_common() Write ACTIVE\n");
+
+ status = chipio_write(codec, DSPDMAC_AUDCHSEL_INST_OFFSET(dma_chan),
+ port_map_mask);
+ if (status < 0) {
+ codec_dbg(codec, "write AUDCHSEL Reg fail\n");
+ return status;
+ }
+ codec_dbg(codec, " dsp_dma_setup_common() Write AUDCHSEL\n");
+
+ status = chipio_write(codec, DSPDMAC_IRQCNT_INST_OFFSET(dma_chan),
+ DSPDMAC_IRQCNT_BICNT_MASK | DSPDMAC_IRQCNT_CICNT_MASK);
+ if (status < 0) {
+ codec_dbg(codec, "write IRQCNT Reg fail\n");
+ return status;
+ }
+ codec_dbg(codec, " dsp_dma_setup_common() Write IRQCNT\n");
+
+ codec_dbg(codec,
+ "ChipA=0x%x,DspA=0x%x,dmaCh=%u, "
+ "CHSEL=0x%x,CHPROP=0x%x,Active=0x%x\n",
+ chip_addx, dsp_addx, dma_chan,
+ port_map_mask, chnl_prop, active);
+
+ codec_dbg(codec, "-- dsp_dma_setup_common() -- Complete ------\n");
+
+ return 0;
+}
+
+/*
+ * Setup the DSP DMA per-transfer-specific registers
+ */
+static int dsp_dma_setup(struct hda_codec *codec,
+ unsigned int chip_addx,
+ unsigned int count,
+ unsigned int dma_chan)
+{
+ int status = 0;
+ bool code, yram;
+ unsigned int dsp_addx;
+ unsigned int addr_field;
+ unsigned int incr_field;
+ unsigned int base_cnt;
+ unsigned int cur_cnt;
+ unsigned int dma_cfg = 0;
+ unsigned int adr_ofs = 0;
+ unsigned int xfr_cnt = 0;
+ const unsigned int max_dma_count = 1 << (DSPDMAC_XFRCNT_BCNT_HIBIT -
+ DSPDMAC_XFRCNT_BCNT_LOBIT + 1);
+
+ codec_dbg(codec, "-- dsp_dma_setup() -- Begin ---------\n");
+
+ if (count > max_dma_count) {
+ codec_dbg(codec, "count too big\n");
+ return -EINVAL;
+ }
+
+ dsp_addx = dsp_chip_to_dsp_addx(chip_addx, &code, &yram);
+ if (dsp_addx == INVALID_CHIP_ADDRESS) {
+ codec_dbg(codec, "invalid chip addr\n");
+ return -ENXIO;
+ }
+
+ codec_dbg(codec, " dsp_dma_setup() start reg pgm\n");
+
+ addr_field = dsp_addx << DSPDMAC_DMACFG_DBADR_LOBIT;
+ incr_field = 0;
+
+ if (!code) {
+ addr_field <<= 1;
+ if (yram)
+ addr_field |= (1 << DSPDMAC_DMACFG_DBADR_LOBIT);
+
+ incr_field = (1 << DSPDMAC_DMACFG_AINCR_LOBIT);
+ }
+
+ dma_cfg = addr_field + incr_field;
+ status = chipio_write(codec, DSPDMAC_DMACFG_INST_OFFSET(dma_chan),
+ dma_cfg);
+ if (status < 0) {
+ codec_dbg(codec, "write DMACFG Reg fail\n");
+ return status;
+ }
+ codec_dbg(codec, " dsp_dma_setup() Write DMACFG\n");
+
+ adr_ofs = (count - 1) << (DSPDMAC_DSPADROFS_BOFS_LOBIT +
+ (code ? 0 : 1));
+
+ status = chipio_write(codec, DSPDMAC_DSPADROFS_INST_OFFSET(dma_chan),
+ adr_ofs);
+ if (status < 0) {
+ codec_dbg(codec, "write DSPADROFS Reg fail\n");
+ return status;
+ }
+ codec_dbg(codec, " dsp_dma_setup() Write DSPADROFS\n");
+
+ base_cnt = (count - 1) << DSPDMAC_XFRCNT_BCNT_LOBIT;
+
+ cur_cnt = (count - 1) << DSPDMAC_XFRCNT_CCNT_LOBIT;
+
+ xfr_cnt = base_cnt | cur_cnt;
+
+ status = chipio_write(codec,
+ DSPDMAC_XFRCNT_INST_OFFSET(dma_chan), xfr_cnt);
+ if (status < 0) {
+ codec_dbg(codec, "write XFRCNT Reg fail\n");
+ return status;
+ }
+ codec_dbg(codec, " dsp_dma_setup() Write XFRCNT\n");
+
+ codec_dbg(codec,
+ "ChipA=0x%x, cnt=0x%x, DMACFG=0x%x, "
+ "ADROFS=0x%x, XFRCNT=0x%x\n",
+ chip_addx, count, dma_cfg, adr_ofs, xfr_cnt);
+
+ codec_dbg(codec, "-- dsp_dma_setup() -- Complete ---------\n");
+
+ return 0;
+}
+
+/*
+ * Start the DSP DMA
+ */
+static int dsp_dma_start(struct hda_codec *codec,
+ unsigned int dma_chan, bool ovly)
+{
+ unsigned int reg = 0;
+ int status = 0;
+
+ codec_dbg(codec, "-- dsp_dma_start() -- Begin ---------\n");
+
+ if (ovly) {
+ status = chipio_read(codec,
+ DSPDMAC_CHNLSTART_INST_OFFSET, &reg);
+
+ if (status < 0) {
+ codec_dbg(codec, "read CHNLSTART reg fail\n");
+ return status;
+ }
+ codec_dbg(codec, "-- dsp_dma_start() Read CHNLSTART\n");
+
+ reg &= ~(DSPDMAC_CHNLSTART_EN_MASK |
+ DSPDMAC_CHNLSTART_DIS_MASK);
+ }
+
+ status = chipio_write(codec, DSPDMAC_CHNLSTART_INST_OFFSET,
+ reg | (1 << (dma_chan + DSPDMAC_CHNLSTART_EN_LOBIT)));
+ if (status < 0) {
+ codec_dbg(codec, "write CHNLSTART reg fail\n");
+ return status;
+ }
+ codec_dbg(codec, "-- dsp_dma_start() -- Complete ---------\n");
+
+ return status;
+}
+
+/*
+ * Stop the DSP DMA
+ */
+static int dsp_dma_stop(struct hda_codec *codec,
+ unsigned int dma_chan, bool ovly)
+{
+ unsigned int reg = 0;
+ int status = 0;
+
+ codec_dbg(codec, "-- dsp_dma_stop() -- Begin ---------\n");
+
+ if (ovly) {
+ status = chipio_read(codec,
+ DSPDMAC_CHNLSTART_INST_OFFSET, &reg);
+
+ if (status < 0) {
+ codec_dbg(codec, "read CHNLSTART reg fail\n");
+ return status;
+ }
+ codec_dbg(codec, "-- dsp_dma_stop() Read CHNLSTART\n");
+ reg &= ~(DSPDMAC_CHNLSTART_EN_MASK |
+ DSPDMAC_CHNLSTART_DIS_MASK);
+ }
+
+ status = chipio_write(codec, DSPDMAC_CHNLSTART_INST_OFFSET,
+ reg | (1 << (dma_chan + DSPDMAC_CHNLSTART_DIS_LOBIT)));
+ if (status < 0) {
+ codec_dbg(codec, "write CHNLSTART reg fail\n");
+ return status;
+ }
+ codec_dbg(codec, "-- dsp_dma_stop() -- Complete ---------\n");
+
+ return status;
+}
+
+/**
+ * Allocate router ports
+ *
+ * @codec: the HDA codec
+ * @num_chans: number of channels in the stream
+ * @ports_per_channel: number of ports per channel
+ * @start_device: start device
+ * @port_map: pointer to the port list to hold the allocated ports
+ *
+ * Returns zero or a negative error code.
+ */
+static int dsp_allocate_router_ports(struct hda_codec *codec,
+ unsigned int num_chans,
+ unsigned int ports_per_channel,
+ unsigned int start_device,
+ unsigned int *port_map)
+{
+ int status = 0;
+ int res;
+ u8 val;
+
+ status = chipio_send(codec, VENDOR_CHIPIO_STATUS, 0);
+ if (status < 0)
+ return status;
+
+ val = start_device << 6;
+ val |= (ports_per_channel - 1) << 4;
+ val |= num_chans - 1;
+
+ snd_hda_codec_write(codec, WIDGET_CHIP_CTRL, 0,
+ VENDOR_CHIPIO_PORT_ALLOC_CONFIG_SET,
+ val);
+
+ snd_hda_codec_write(codec, WIDGET_CHIP_CTRL, 0,
+ VENDOR_CHIPIO_PORT_ALLOC_SET,
+ MEM_CONNID_DSP);
+
+ status = chipio_send(codec, VENDOR_CHIPIO_STATUS, 0);
+ if (status < 0)
+ return status;
+
+ res = snd_hda_codec_read(codec, WIDGET_CHIP_CTRL, 0,
+ VENDOR_CHIPIO_PORT_ALLOC_GET, 0);
+
+ *port_map = res;
+
+ return (res < 0) ? res : 0;
+}
+
+/*
+ * Free router ports
+ */
+static int dsp_free_router_ports(struct hda_codec *codec)
+{
+ int status = 0;
+
+ status = chipio_send(codec, VENDOR_CHIPIO_STATUS, 0);
+ if (status < 0)
+ return status;
+
+ snd_hda_codec_write(codec, WIDGET_CHIP_CTRL, 0,
+ VENDOR_CHIPIO_PORT_FREE_SET,
+ MEM_CONNID_DSP);
+
+ status = chipio_send(codec, VENDOR_CHIPIO_STATUS, 0);
+
+ return status;
+}
+
+/*
+ * Allocate DSP ports for the download stream
+ */
+static int dsp_allocate_ports(struct hda_codec *codec,
+ unsigned int num_chans,
+ unsigned int rate_multi, unsigned int *port_map)
+{
+ int status;
+
+ codec_dbg(codec, " dsp_allocate_ports() -- begin\n");
+
+ if ((rate_multi != 1) && (rate_multi != 2) && (rate_multi != 4)) {
+ codec_dbg(codec, "bad rate multiple\n");
+ return -EINVAL;
+ }
+
+ status = dsp_allocate_router_ports(codec, num_chans,
+ rate_multi, 0, port_map);
+
+ codec_dbg(codec, " dsp_allocate_ports() -- complete\n");
+
+ return status;
+}
+
+static int dsp_allocate_ports_format(struct hda_codec *codec,
+ const unsigned short fmt,
+ unsigned int *port_map)
+{
+ int status;
+ unsigned int num_chans;
+
+ unsigned int sample_rate_div = ((get_hdafmt_rate(fmt) >> 0) & 3) + 1;
+ unsigned int sample_rate_mul = ((get_hdafmt_rate(fmt) >> 3) & 3) + 1;
+ unsigned int rate_multi = sample_rate_mul / sample_rate_div;
+
+ if ((rate_multi != 1) && (rate_multi != 2) && (rate_multi != 4)) {
+ codec_dbg(codec, "bad rate multiple\n");
+ return -EINVAL;
+ }
+
+ num_chans = get_hdafmt_chs(fmt) + 1;
+
+ status = dsp_allocate_ports(codec, num_chans, rate_multi, port_map);
+
+ return status;
+}
+
+/*
+ * free DSP ports
+ */
+static int dsp_free_ports(struct hda_codec *codec)
+{
+ int status;
+
+ codec_dbg(codec, " dsp_free_ports() -- begin\n");
+
+ status = dsp_free_router_ports(codec);
+ if (status < 0) {
+ codec_dbg(codec, "free router ports fail\n");
+ return status;
+ }
+ codec_dbg(codec, " dsp_free_ports() -- complete\n");
+
+ return status;
+}
+
+/*
+ * HDA DMA engine stuffs for DSP code download
+ */
+struct dma_engine {
+ struct hda_codec *codec;
+ unsigned short m_converter_format;
+ struct snd_dma_buffer *dmab;
+ unsigned int buf_size;
+};
+
+
+enum dma_state {
+ DMA_STATE_STOP = 0,
+ DMA_STATE_RUN = 1
+};
+
+static int dma_convert_to_hda_format(
+ unsigned int sample_rate,
+ unsigned short channels,
+ unsigned short *hda_format)
+{
+ unsigned int format_val;
+
+ format_val = snd_hda_calc_stream_format(
+ sample_rate,
+ channels,
+ SNDRV_PCM_FORMAT_S32_LE,
+ 32, 0);
+
+ if (hda_format)
+ *hda_format = (unsigned short)format_val;
+
+ return 0;
+}
+
+/*
+ * Reset DMA for DSP download
+ */
+static int dma_reset(struct dma_engine *dma)
+{
+ struct hda_codec *codec = dma->codec;
+ struct ca0132_spec *spec = codec->spec;
+ int status;
+
+ if (dma->dmab->area)
+ snd_hda_codec_load_dsp_cleanup(codec, dma->dmab);
+
+ status = snd_hda_codec_load_dsp_prepare(codec,
+ dma->m_converter_format,
+ dma->buf_size,
+ dma->dmab);
+ if (status < 0)
+ return status;
+ spec->dsp_stream_id = status;
+ return 0;
+}
+
+static int dma_set_state(struct dma_engine *dma, enum dma_state state)
+{
+ bool cmd;
+
+ switch (state) {
+ case DMA_STATE_STOP:
+ cmd = false;
+ break;
+ case DMA_STATE_RUN:
+ cmd = true;
+ break;
+ default:
+ return 0;
+ }
+
+ snd_hda_codec_load_dsp_trigger(dma->codec, cmd);
+ return 0;
+}
+
+static unsigned int dma_get_buffer_size(struct dma_engine *dma)
+{
+ return dma->dmab->bytes;
+}
+
+static unsigned char *dma_get_buffer_addr(struct dma_engine *dma)
+{
+ return dma->dmab->area;
+}
+
+static int dma_xfer(struct dma_engine *dma,
+ const unsigned int *data,
+ unsigned int count)
+{
+ memcpy(dma->dmab->area, data, count);
+ return 0;
+}
+
+static void dma_get_converter_format(
+ struct dma_engine *dma,
+ unsigned short *format)
+{
+ if (format)
+ *format = dma->m_converter_format;
+}
+
+static unsigned int dma_get_stream_id(struct dma_engine *dma)
+{
+ struct ca0132_spec *spec = dma->codec->spec;
+
+ return spec->dsp_stream_id;
+}
+
+struct dsp_image_seg {
+ u32 magic;
+ u32 chip_addr;
+ u32 count;
+ u32 data[0];
+};
+
+static const u32 g_magic_value = 0x4c46584d;
+static const u32 g_chip_addr_magic_value = 0xFFFFFF01;
+
+static bool is_valid(const struct dsp_image_seg *p)
+{
+ return p->magic == g_magic_value;
+}
+
+static bool is_hci_prog_list_seg(const struct dsp_image_seg *p)
+{
+ return g_chip_addr_magic_value == p->chip_addr;
+}
+
+static bool is_last(const struct dsp_image_seg *p)
+{
+ return p->count == 0;
+}
+
+static size_t dsp_sizeof(const struct dsp_image_seg *p)
+{
+ return sizeof(*p) + p->count*sizeof(u32);
+}
+
+static const struct dsp_image_seg *get_next_seg_ptr(
+ const struct dsp_image_seg *p)
+{
+ return (struct dsp_image_seg *)((unsigned char *)(p) + dsp_sizeof(p));
+}
+
+/*
+ * CA0132 chip DSP transfer stuffs. For DSP download.
+ */
+#define INVALID_DMA_CHANNEL (~0U)
+
+/*
+ * Program a list of address/data pairs via the ChipIO widget.
+ * The segment data is in the format of successive pairs of words.
+ * These are repeated as indicated by the segment's count field.
+ */
+static int dspxfr_hci_write(struct hda_codec *codec,
+ const struct dsp_image_seg *fls)
+{
+ int status;
+ const u32 *data;
+ unsigned int count;
+
+ if (fls == NULL || fls->chip_addr != g_chip_addr_magic_value) {
+ codec_dbg(codec, "hci_write invalid params\n");
+ return -EINVAL;
+ }
+
+ count = fls->count;
+ data = (u32 *)(fls->data);
+ while (count >= 2) {
+ status = chipio_write(codec, data[0], data[1]);
+ if (status < 0) {
+ codec_dbg(codec, "hci_write chipio failed\n");
+ return status;
+ }
+ count -= 2;
+ data += 2;
+ }
+ return 0;
+}
+
+/**
+ * Write a block of data into DSP code or data RAM using pre-allocated
+ * DMA engine.
+ *
+ * @codec: the HDA codec
+ * @fls: pointer to a fast load image
+ * @reloc: Relocation address for loading single-segment overlays, or 0 for
+ * no relocation
+ * @dma_engine: pointer to DMA engine to be used for DSP download
+ * @dma_chan: The number of DMA channels used for DSP download
+ * @port_map_mask: port mapping
+ * @ovly: TRUE if overlay format is required
+ *
+ * Returns zero or a negative error code.
+ */
+static int dspxfr_one_seg(struct hda_codec *codec,
+ const struct dsp_image_seg *fls,
+ unsigned int reloc,
+ struct dma_engine *dma_engine,
+ unsigned int dma_chan,
+ unsigned int port_map_mask,
+ bool ovly)
+{
+ int status = 0;
+ bool comm_dma_setup_done = false;
+ const unsigned int *data;
+ unsigned int chip_addx;
+ unsigned int words_to_write;
+ unsigned int buffer_size_words;
+ unsigned char *buffer_addx;
+ unsigned short hda_format;
+ unsigned int sample_rate_div;
+ unsigned int sample_rate_mul;
+ unsigned int num_chans;
+ unsigned int hda_frame_size_words;
+ unsigned int remainder_words;
+ const u32 *data_remainder;
+ u32 chip_addx_remainder;
+ unsigned int run_size_words;
+ const struct dsp_image_seg *hci_write = NULL;
+ unsigned long timeout;
+ bool dma_active;
+
+ if (fls == NULL)
+ return -EINVAL;
+ if (is_hci_prog_list_seg(fls)) {
+ hci_write = fls;
+ fls = get_next_seg_ptr(fls);
+ }
+
+ if (hci_write && (!fls || is_last(fls))) {
+ codec_dbg(codec, "hci_write\n");
+ return dspxfr_hci_write(codec, hci_write);
+ }
+
+ if (fls == NULL || dma_engine == NULL || port_map_mask == 0) {
+ codec_dbg(codec, "Invalid Params\n");
+ return -EINVAL;
+ }
+
+ data = fls->data;
+ chip_addx = fls->chip_addr,
+ words_to_write = fls->count;
+
+ if (!words_to_write)
+ return hci_write ? dspxfr_hci_write(codec, hci_write) : 0;
+ if (reloc)
+ chip_addx = (chip_addx & (0xFFFF0000 << 2)) + (reloc << 2);
+
+ if (!UC_RANGE(chip_addx, words_to_write) &&
+ !X_RANGE_ALL(chip_addx, words_to_write) &&
+ !Y_RANGE_ALL(chip_addx, words_to_write)) {
+ codec_dbg(codec, "Invalid chip_addx Params\n");
+ return -EINVAL;
+ }
+
+ buffer_size_words = (unsigned int)dma_get_buffer_size(dma_engine) /
+ sizeof(u32);
+
+ buffer_addx = dma_get_buffer_addr(dma_engine);
+
+ if (buffer_addx == NULL) {
+ codec_dbg(codec, "dma_engine buffer NULL\n");
+ return -EINVAL;
+ }
+
+ dma_get_converter_format(dma_engine, &hda_format);
+ sample_rate_div = ((get_hdafmt_rate(hda_format) >> 0) & 3) + 1;
+ sample_rate_mul = ((get_hdafmt_rate(hda_format) >> 3) & 3) + 1;
+ num_chans = get_hdafmt_chs(hda_format) + 1;
+
+ hda_frame_size_words = ((sample_rate_div == 0) ? 0 :
+ (num_chans * sample_rate_mul / sample_rate_div));
+
+ if (hda_frame_size_words == 0) {
+ codec_dbg(codec, "frmsz zero\n");
+ return -EINVAL;
+ }
+
+ buffer_size_words = min(buffer_size_words,
+ (unsigned int)(UC_RANGE(chip_addx, 1) ?
+ 65536 : 32768));
+ buffer_size_words -= buffer_size_words % hda_frame_size_words;
+ codec_dbg(codec,
+ "chpadr=0x%08x frmsz=%u nchan=%u "
+ "rate_mul=%u div=%u bufsz=%u\n",
+ chip_addx, hda_frame_size_words, num_chans,
+ sample_rate_mul, sample_rate_div, buffer_size_words);
+
+ if (buffer_size_words < hda_frame_size_words) {
+ codec_dbg(codec, "dspxfr_one_seg:failed\n");
+ return -EINVAL;
+ }
+
+ remainder_words = words_to_write % hda_frame_size_words;
+ data_remainder = data;
+ chip_addx_remainder = chip_addx;
+
+ data += remainder_words;
+ chip_addx += remainder_words*sizeof(u32);
+ words_to_write -= remainder_words;
+
+ while (words_to_write != 0) {
+ run_size_words = min(buffer_size_words, words_to_write);
+ codec_dbg(codec, "dspxfr (seg loop)cnt=%u rs=%u remainder=%u\n",
+ words_to_write, run_size_words, remainder_words);
+ dma_xfer(dma_engine, data, run_size_words*sizeof(u32));
+ if (!comm_dma_setup_done) {
+ status = dsp_dma_stop(codec, dma_chan, ovly);
+ if (status < 0)
+ return status;
+ status = dsp_dma_setup_common(codec, chip_addx,
+ dma_chan, port_map_mask, ovly);
+ if (status < 0)
+ return status;
+ comm_dma_setup_done = true;
+ }
+
+ status = dsp_dma_setup(codec, chip_addx,
+ run_size_words, dma_chan);
+ if (status < 0)
+ return status;
+ status = dsp_dma_start(codec, dma_chan, ovly);
+ if (status < 0)
+ return status;
+ if (!dsp_is_dma_active(codec, dma_chan)) {
+ codec_dbg(codec, "dspxfr:DMA did not start\n");
+ return -EIO;
+ }
+ status = dma_set_state(dma_engine, DMA_STATE_RUN);
+ if (status < 0)
+ return status;
+ if (remainder_words != 0) {
+ status = chipio_write_multiple(codec,
+ chip_addx_remainder,
+ data_remainder,
+ remainder_words);
+ if (status < 0)
+ return status;
+ remainder_words = 0;
+ }
+ if (hci_write) {
+ status = dspxfr_hci_write(codec, hci_write);
+ if (status < 0)
+ return status;
+ hci_write = NULL;
+ }
+
+ timeout = jiffies + msecs_to_jiffies(2000);
+ do {
+ dma_active = dsp_is_dma_active(codec, dma_chan);
+ if (!dma_active)
+ break;
+ msleep(20);
+ } while (time_before(jiffies, timeout));
+ if (dma_active)
+ break;
+
+ codec_dbg(codec, "+++++ DMA complete\n");
+ dma_set_state(dma_engine, DMA_STATE_STOP);
+ status = dma_reset(dma_engine);
+
+ if (status < 0)
+ return status;
+
+ data += run_size_words;
+ chip_addx += run_size_words*sizeof(u32);
+ words_to_write -= run_size_words;
+ }
+
+ if (remainder_words != 0) {
+ status = chipio_write_multiple(codec, chip_addx_remainder,
+ data_remainder, remainder_words);
+ }
+
+ return status;
+}
+
+/**
+ * Write the entire DSP image of a DSP code/data overlay to DSP memories
+ *
+ * @codec: the HDA codec
+ * @fls_data: pointer to a fast load image
+ * @reloc: Relocation address for loading single-segment overlays, or 0 for
+ * no relocation
+ * @sample_rate: sampling rate of the stream used for DSP download
+ * @number_channels: channels of the stream used for DSP download
+ * @ovly: TRUE if overlay format is required
+ *
+ * Returns zero or a negative error code.
+ */
+static int dspxfr_image(struct hda_codec *codec,
+ const struct dsp_image_seg *fls_data,
+ unsigned int reloc,
+ unsigned int sample_rate,
+ unsigned short channels,
+ bool ovly)
+{
+ struct ca0132_spec *spec = codec->spec;
+ int status;
+ unsigned short hda_format = 0;
+ unsigned int response;
+ unsigned char stream_id = 0;
+ struct dma_engine *dma_engine;
+ unsigned int dma_chan;
+ unsigned int port_map_mask;
+
+ if (fls_data == NULL)
+ return -EINVAL;
+
+ dma_engine = kzalloc(sizeof(*dma_engine), GFP_KERNEL);
+ if (!dma_engine)
+ return -ENOMEM;
+
+ dma_engine->dmab = kzalloc(sizeof(*dma_engine->dmab), GFP_KERNEL);
+ if (!dma_engine->dmab) {
+ kfree(dma_engine);
+ return -ENOMEM;
+ }
+
+ dma_engine->codec = codec;
+ dma_convert_to_hda_format(sample_rate, channels, &hda_format);
+ dma_engine->m_converter_format = hda_format;
+ dma_engine->buf_size = (ovly ? DSP_DMA_WRITE_BUFLEN_OVLY :
+ DSP_DMA_WRITE_BUFLEN_INIT) * 2;
+
+ dma_chan = ovly ? INVALID_DMA_CHANNEL : 0;
+
+ status = codec_set_converter_format(codec, WIDGET_CHIP_CTRL,
+ hda_format, &response);
+
+ if (status < 0) {
+ codec_dbg(codec, "set converter format fail\n");
+ goto exit;
+ }
+
+ status = snd_hda_codec_load_dsp_prepare(codec,
+ dma_engine->m_converter_format,
+ dma_engine->buf_size,
+ dma_engine->dmab);
+ if (status < 0)
+ goto exit;
+ spec->dsp_stream_id = status;
+
+ if (ovly) {
+ status = dspio_alloc_dma_chan(codec, &dma_chan);
+ if (status < 0) {
+ codec_dbg(codec, "alloc dmachan fail\n");
+ dma_chan = INVALID_DMA_CHANNEL;
+ goto exit;
+ }
+ }
+
+ port_map_mask = 0;
+ status = dsp_allocate_ports_format(codec, hda_format,
+ &port_map_mask);
+ if (status < 0) {
+ codec_dbg(codec, "alloc ports fail\n");
+ goto exit;
+ }
+
+ stream_id = dma_get_stream_id(dma_engine);
+ status = codec_set_converter_stream_channel(codec,
+ WIDGET_CHIP_CTRL, stream_id, 0, &response);
+ if (status < 0) {
+ codec_dbg(codec, "set stream chan fail\n");
+ goto exit;
+ }
+
+ while ((fls_data != NULL) && !is_last(fls_data)) {
+ if (!is_valid(fls_data)) {
+ codec_dbg(codec, "FLS check fail\n");
+ status = -EINVAL;
+ goto exit;
+ }
+ status = dspxfr_one_seg(codec, fls_data, reloc,
+ dma_engine, dma_chan,
+ port_map_mask, ovly);
+ if (status < 0)
+ break;
+
+ if (is_hci_prog_list_seg(fls_data))
+ fls_data = get_next_seg_ptr(fls_data);
+
+ if ((fls_data != NULL) && !is_last(fls_data))
+ fls_data = get_next_seg_ptr(fls_data);
+ }
+
+ if (port_map_mask != 0)
+ status = dsp_free_ports(codec);
+
+ if (status < 0)
+ goto exit;
+
+ status = codec_set_converter_stream_channel(codec,
+ WIDGET_CHIP_CTRL, 0, 0, &response);
+
+exit:
+ if (ovly && (dma_chan != INVALID_DMA_CHANNEL))
+ dspio_free_dma_chan(codec, dma_chan);
+
+ if (dma_engine->dmab->area)
+ snd_hda_codec_load_dsp_cleanup(codec, dma_engine->dmab);
+ kfree(dma_engine->dmab);
+ kfree(dma_engine);
+
+ return status;
+}
+
+/*
+ * CA0132 DSP download stuffs.
+ */
+static void dspload_post_setup(struct hda_codec *codec)
+{
+ codec_dbg(codec, "---- dspload_post_setup ------\n");
+
+ /*set DSP speaker to 2.0 configuration*/
+ chipio_write(codec, XRAM_XRAM_INST_OFFSET(0x18), 0x08080080);
+ chipio_write(codec, XRAM_XRAM_INST_OFFSET(0x19), 0x3f800000);
+
+ /*update write pointer*/
+ chipio_write(codec, XRAM_XRAM_INST_OFFSET(0x29), 0x00000002);
+}
+
+/**
+ * Download DSP from a DSP Image Fast Load structure. This structure is a
+ * linear, non-constant sized element array of structures, each of which
+ * contain the count of the data to be loaded, the data itself, and the
+ * corresponding starting chip address of the starting data location.
+ *
+ * @codec: the HDA codec
+ * @fls: pointer to a fast load image
+ * @ovly: TRUE if overlay format is required
+ * @reloc: Relocation address for loading single-segment overlays, or 0 for
+ * no relocation
+ * @autostart: TRUE if DSP starts after loading; ignored if ovly is TRUE
+ * @router_chans: number of audio router channels to be allocated (0 means use
+ * internal defaults; max is 32)
+ *
+ * Returns zero or a negative error code.
+ */
+static int dspload_image(struct hda_codec *codec,
+ const struct dsp_image_seg *fls,
+ bool ovly,
+ unsigned int reloc,
+ bool autostart,
+ int router_chans)
+{
+ int status = 0;
+ unsigned int sample_rate;
+ unsigned short channels;
+
+ codec_dbg(codec, "---- dspload_image begin ------\n");
+ if (router_chans == 0) {
+ if (!ovly)
+ router_chans = DMA_TRANSFER_FRAME_SIZE_NWORDS;
+ else
+ router_chans = DMA_OVERLAY_FRAME_SIZE_NWORDS;
+ }
+
+ sample_rate = 48000;
+ channels = (unsigned short)router_chans;
+
+ while (channels > 16) {
+ sample_rate *= 2;
+ channels /= 2;
+ }
+
+ do {
+ codec_dbg(codec, "Ready to program DMA\n");
+ if (!ovly)
+ status = dsp_reset(codec);
+
+ if (status < 0)
+ break;
+
+ codec_dbg(codec, "dsp_reset() complete\n");
+ status = dspxfr_image(codec, fls, reloc, sample_rate, channels,
+ ovly);
+
+ if (status < 0)
+ break;
+
+ codec_dbg(codec, "dspxfr_image() complete\n");
+ if (autostart && !ovly) {
+ dspload_post_setup(codec);
+ status = dsp_set_run_state(codec);
+ }
+
+ codec_dbg(codec, "LOAD FINISHED\n");
+ } while (0);
+
+ return status;
+}
+
+#ifdef CONFIG_SND_HDA_CODEC_CA0132_DSP
+static bool dspload_is_loaded(struct hda_codec *codec)
+{
+ unsigned int data = 0;
+ int status = 0;
+
+ status = chipio_read(codec, 0x40004, &data);
+ if ((status < 0) || (data != 1))
+ return false;
+
+ return true;
+}
+#else
+#define dspload_is_loaded(codec) false
+#endif
+
+static bool dspload_wait_loaded(struct hda_codec *codec)
+{
+ unsigned long timeout = jiffies + msecs_to_jiffies(2000);
+
+ do {
+ if (dspload_is_loaded(codec)) {
+ pr_info("ca0132 DOWNLOAD OK :-) DSP IS RUNNING.\n");
+ return true;
+ }
+ msleep(20);
+ } while (time_before(jiffies, timeout));
+
+ pr_err("ca0132 DOWNLOAD FAILED!!! DSP IS NOT RUNNING.\n");
+ return false;
+}
+
+/*
+ * PCM callbacks
+ */
+static int ca0132_playback_pcm_prepare(struct hda_pcm_stream *hinfo,
+ struct hda_codec *codec,
+ unsigned int stream_tag,
+ unsigned int format,
+ struct snd_pcm_substream *substream)
+{
+ struct ca0132_spec *spec = codec->spec;
+
+ snd_hda_codec_setup_stream(codec, spec->dacs[0], stream_tag, 0, format);
+
+ return 0;
+}
+
+static int ca0132_playback_pcm_cleanup(struct hda_pcm_stream *hinfo,
+ struct hda_codec *codec,
+ struct snd_pcm_substream *substream)
+{
+ struct ca0132_spec *spec = codec->spec;
+
+ if (spec->dsp_state == DSP_DOWNLOADING)
+ return 0;
+
+ /*If Playback effects are on, allow stream some time to flush
+ *effects tail*/
+ if (spec->effects_switch[PLAY_ENHANCEMENT - EFFECT_START_NID])
+ msleep(50);
+
+ snd_hda_codec_cleanup_stream(codec, spec->dacs[0]);
+
+ return 0;
+}
+
+static unsigned int ca0132_playback_pcm_delay(struct hda_pcm_stream *info,
+ struct hda_codec *codec,
+ struct snd_pcm_substream *substream)
+{
+ struct ca0132_spec *spec = codec->spec;
+ unsigned int latency = DSP_PLAYBACK_INIT_LATENCY;
+ struct snd_pcm_runtime *runtime = substream->runtime;
+
+ if (spec->dsp_state != DSP_DOWNLOADED)
+ return 0;
+
+ /* Add latency if playback enhancement and either effect is enabled. */
+ if (spec->effects_switch[PLAY_ENHANCEMENT - EFFECT_START_NID]) {
+ if ((spec->effects_switch[SURROUND - EFFECT_START_NID]) ||
+ (spec->effects_switch[DIALOG_PLUS - EFFECT_START_NID]))
+ latency += DSP_PLAY_ENHANCEMENT_LATENCY;
+ }
+
+ /* Applying Speaker EQ adds latency as well. */
+ if (spec->cur_out_type == SPEAKER_OUT)
+ latency += DSP_SPEAKER_OUT_LATENCY;
+
+ return (latency * runtime->rate) / 1000;
+}
+
+/*
+ * Digital out
+ */
+static int ca0132_dig_playback_pcm_open(struct hda_pcm_stream *hinfo,
+ struct hda_codec *codec,
+ struct snd_pcm_substream *substream)
+{
+ struct ca0132_spec *spec = codec->spec;
+ return snd_hda_multi_out_dig_open(codec, &spec->multiout);
+}
+
+static int ca0132_dig_playback_pcm_prepare(struct hda_pcm_stream *hinfo,
+ struct hda_codec *codec,
+ unsigned int stream_tag,
+ unsigned int format,
+ struct snd_pcm_substream *substream)
+{
+ struct ca0132_spec *spec = codec->spec;
+ return snd_hda_multi_out_dig_prepare(codec, &spec->multiout,
+ stream_tag, format, substream);
+}
+
+static int ca0132_dig_playback_pcm_cleanup(struct hda_pcm_stream *hinfo,
+ struct hda_codec *codec,
+ struct snd_pcm_substream *substream)
+{
+ struct ca0132_spec *spec = codec->spec;
+ return snd_hda_multi_out_dig_cleanup(codec, &spec->multiout);
+}
+
+static int ca0132_dig_playback_pcm_close(struct hda_pcm_stream *hinfo,
+ struct hda_codec *codec,
+ struct snd_pcm_substream *substream)
+{
+ struct ca0132_spec *spec = codec->spec;
+ return snd_hda_multi_out_dig_close(codec, &spec->multiout);
+}
+
+/*
+ * Analog capture
+ */
+static int ca0132_capture_pcm_prepare(struct hda_pcm_stream *hinfo,
+ struct hda_codec *codec,
+ unsigned int stream_tag,
+ unsigned int format,
+ struct snd_pcm_substream *substream)
+{
+ snd_hda_codec_setup_stream(codec, hinfo->nid,
+ stream_tag, 0, format);
+
+ return 0;
+}
+
+static int ca0132_capture_pcm_cleanup(struct hda_pcm_stream *hinfo,
+ struct hda_codec *codec,
+ struct snd_pcm_substream *substream)
+{
+ struct ca0132_spec *spec = codec->spec;
+
+ if (spec->dsp_state == DSP_DOWNLOADING)
+ return 0;
+
+ snd_hda_codec_cleanup_stream(codec, hinfo->nid);
+ return 0;
+}
+
+static unsigned int ca0132_capture_pcm_delay(struct hda_pcm_stream *info,
+ struct hda_codec *codec,
+ struct snd_pcm_substream *substream)
+{
+ struct ca0132_spec *spec = codec->spec;
+ unsigned int latency = DSP_CAPTURE_INIT_LATENCY;
+ struct snd_pcm_runtime *runtime = substream->runtime;
+
+ if (spec->dsp_state != DSP_DOWNLOADED)
+ return 0;
+
+ if (spec->effects_switch[CRYSTAL_VOICE - EFFECT_START_NID])
+ latency += DSP_CRYSTAL_VOICE_LATENCY;
+
+ return (latency * runtime->rate) / 1000;
+}
+
+/*
+ * Controls stuffs.
+ */
+
+/*
+ * Mixer controls helpers.
+ */
+#define CA0132_CODEC_VOL_MONO(xname, nid, channel, dir) \
+ { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, \
+ .name = xname, \
+ .subdevice = HDA_SUBDEV_AMP_FLAG, \
+ .access = SNDRV_CTL_ELEM_ACCESS_READWRITE | \
+ SNDRV_CTL_ELEM_ACCESS_TLV_READ | \
+ SNDRV_CTL_ELEM_ACCESS_TLV_CALLBACK, \
+ .info = ca0132_volume_info, \
+ .get = ca0132_volume_get, \
+ .put = ca0132_volume_put, \
+ .tlv = { .c = ca0132_volume_tlv }, \
+ .private_value = HDA_COMPOSE_AMP_VAL(nid, channel, 0, dir) }
+
+#define CA0132_CODEC_MUTE_MONO(xname, nid, channel, dir) \
+ { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, \
+ .name = xname, \
+ .subdevice = HDA_SUBDEV_AMP_FLAG, \
+ .info = snd_hda_mixer_amp_switch_info, \
+ .get = ca0132_switch_get, \
+ .put = ca0132_switch_put, \
+ .private_value = HDA_COMPOSE_AMP_VAL(nid, channel, 0, dir) }
+
+/* stereo */
+#define CA0132_CODEC_VOL(xname, nid, dir) \
+ CA0132_CODEC_VOL_MONO(xname, nid, 3, dir)
+#define CA0132_CODEC_MUTE(xname, nid, dir) \
+ CA0132_CODEC_MUTE_MONO(xname, nid, 3, dir)
+
+/* The followings are for tuning of products */
+#ifdef ENABLE_TUNING_CONTROLS
+
+static unsigned int voice_focus_vals_lookup[] = {
+0x41A00000, 0x41A80000, 0x41B00000, 0x41B80000, 0x41C00000, 0x41C80000,
+0x41D00000, 0x41D80000, 0x41E00000, 0x41E80000, 0x41F00000, 0x41F80000,
+0x42000000, 0x42040000, 0x42080000, 0x420C0000, 0x42100000, 0x42140000,
+0x42180000, 0x421C0000, 0x42200000, 0x42240000, 0x42280000, 0x422C0000,
+0x42300000, 0x42340000, 0x42380000, 0x423C0000, 0x42400000, 0x42440000,
+0x42480000, 0x424C0000, 0x42500000, 0x42540000, 0x42580000, 0x425C0000,
+0x42600000, 0x42640000, 0x42680000, 0x426C0000, 0x42700000, 0x42740000,
+0x42780000, 0x427C0000, 0x42800000, 0x42820000, 0x42840000, 0x42860000,
+0x42880000, 0x428A0000, 0x428C0000, 0x428E0000, 0x42900000, 0x42920000,
+0x42940000, 0x42960000, 0x42980000, 0x429A0000, 0x429C0000, 0x429E0000,
+0x42A00000, 0x42A20000, 0x42A40000, 0x42A60000, 0x42A80000, 0x42AA0000,
+0x42AC0000, 0x42AE0000, 0x42B00000, 0x42B20000, 0x42B40000, 0x42B60000,
+0x42B80000, 0x42BA0000, 0x42BC0000, 0x42BE0000, 0x42C00000, 0x42C20000,
+0x42C40000, 0x42C60000, 0x42C80000, 0x42CA0000, 0x42CC0000, 0x42CE0000,
+0x42D00000, 0x42D20000, 0x42D40000, 0x42D60000, 0x42D80000, 0x42DA0000,
+0x42DC0000, 0x42DE0000, 0x42E00000, 0x42E20000, 0x42E40000, 0x42E60000,
+0x42E80000, 0x42EA0000, 0x42EC0000, 0x42EE0000, 0x42F00000, 0x42F20000,
+0x42F40000, 0x42F60000, 0x42F80000, 0x42FA0000, 0x42FC0000, 0x42FE0000,
+0x43000000, 0x43010000, 0x43020000, 0x43030000, 0x43040000, 0x43050000,
+0x43060000, 0x43070000, 0x43080000, 0x43090000, 0x430A0000, 0x430B0000,
+0x430C0000, 0x430D0000, 0x430E0000, 0x430F0000, 0x43100000, 0x43110000,
+0x43120000, 0x43130000, 0x43140000, 0x43150000, 0x43160000, 0x43170000,
+0x43180000, 0x43190000, 0x431A0000, 0x431B0000, 0x431C0000, 0x431D0000,
+0x431E0000, 0x431F0000, 0x43200000, 0x43210000, 0x43220000, 0x43230000,
+0x43240000, 0x43250000, 0x43260000, 0x43270000, 0x43280000, 0x43290000,
+0x432A0000, 0x432B0000, 0x432C0000, 0x432D0000, 0x432E0000, 0x432F0000,
+0x43300000, 0x43310000, 0x43320000, 0x43330000, 0x43340000
+};
+
+static unsigned int mic_svm_vals_lookup[] = {
+0x00000000, 0x3C23D70A, 0x3CA3D70A, 0x3CF5C28F, 0x3D23D70A, 0x3D4CCCCD,
+0x3D75C28F, 0x3D8F5C29, 0x3DA3D70A, 0x3DB851EC, 0x3DCCCCCD, 0x3DE147AE,
+0x3DF5C28F, 0x3E051EB8, 0x3E0F5C29, 0x3E19999A, 0x3E23D70A, 0x3E2E147B,
+0x3E3851EC, 0x3E428F5C, 0x3E4CCCCD, 0x3E570A3D, 0x3E6147AE, 0x3E6B851F,
+0x3E75C28F, 0x3E800000, 0x3E851EB8, 0x3E8A3D71, 0x3E8F5C29, 0x3E947AE1,
+0x3E99999A, 0x3E9EB852, 0x3EA3D70A, 0x3EA8F5C3, 0x3EAE147B, 0x3EB33333,
+0x3EB851EC, 0x3EBD70A4, 0x3EC28F5C, 0x3EC7AE14, 0x3ECCCCCD, 0x3ED1EB85,
+0x3ED70A3D, 0x3EDC28F6, 0x3EE147AE, 0x3EE66666, 0x3EEB851F, 0x3EF0A3D7,
+0x3EF5C28F, 0x3EFAE148, 0x3F000000, 0x3F028F5C, 0x3F051EB8, 0x3F07AE14,
+0x3F0A3D71, 0x3F0CCCCD, 0x3F0F5C29, 0x3F11EB85, 0x3F147AE1, 0x3F170A3D,
+0x3F19999A, 0x3F1C28F6, 0x3F1EB852, 0x3F2147AE, 0x3F23D70A, 0x3F266666,
+0x3F28F5C3, 0x3F2B851F, 0x3F2E147B, 0x3F30A3D7, 0x3F333333, 0x3F35C28F,
+0x3F3851EC, 0x3F3AE148, 0x3F3D70A4, 0x3F400000, 0x3F428F5C, 0x3F451EB8,
+0x3F47AE14, 0x3F4A3D71, 0x3F4CCCCD, 0x3F4F5C29, 0x3F51EB85, 0x3F547AE1,
+0x3F570A3D, 0x3F59999A, 0x3F5C28F6, 0x3F5EB852, 0x3F6147AE, 0x3F63D70A,
+0x3F666666, 0x3F68F5C3, 0x3F6B851F, 0x3F6E147B, 0x3F70A3D7, 0x3F733333,
+0x3F75C28F, 0x3F7851EC, 0x3F7AE148, 0x3F7D70A4, 0x3F800000
+};
+
+static unsigned int equalizer_vals_lookup[] = {
+0xC1C00000, 0xC1B80000, 0xC1B00000, 0xC1A80000, 0xC1A00000, 0xC1980000,
+0xC1900000, 0xC1880000, 0xC1800000, 0xC1700000, 0xC1600000, 0xC1500000,
+0xC1400000, 0xC1300000, 0xC1200000, 0xC1100000, 0xC1000000, 0xC0E00000,
+0xC0C00000, 0xC0A00000, 0xC0800000, 0xC0400000, 0xC0000000, 0xBF800000,
+0x00000000, 0x3F800000, 0x40000000, 0x40400000, 0x40800000, 0x40A00000,
+0x40C00000, 0x40E00000, 0x41000000, 0x41100000, 0x41200000, 0x41300000,
+0x41400000, 0x41500000, 0x41600000, 0x41700000, 0x41800000, 0x41880000,
+0x41900000, 0x41980000, 0x41A00000, 0x41A80000, 0x41B00000, 0x41B80000,
+0x41C00000
+};
+
+static int tuning_ctl_set(struct hda_codec *codec, hda_nid_t nid,
+ unsigned int *lookup, int idx)
+{
+ int i = 0;
+
+ for (i = 0; i < TUNING_CTLS_COUNT; i++)
+ if (nid == ca0132_tuning_ctls[i].nid)
+ break;
+
+ snd_hda_power_up(codec);
+ dspio_set_param(codec, ca0132_tuning_ctls[i].mid,
+ ca0132_tuning_ctls[i].req,
+ &(lookup[idx]), sizeof(unsigned int));
+ snd_hda_power_down(codec);
+
+ return 1;
+}
+
+static int tuning_ctl_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
+ struct ca0132_spec *spec = codec->spec;
+ hda_nid_t nid = get_amp_nid(kcontrol);
+ long *valp = ucontrol->value.integer.value;
+ int idx = nid - TUNING_CTL_START_NID;
+
+ *valp = spec->cur_ctl_vals[idx];
+ return 0;
+}
+
+static int voice_focus_ctl_info(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_info *uinfo)
+{
+ int chs = get_amp_channels(kcontrol);
+ uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
+ uinfo->count = chs == 3 ? 2 : 1;
+ uinfo->value.integer.min = 20;
+ uinfo->value.integer.max = 180;
+ uinfo->value.integer.step = 1;
+
+ return 0;
+}
+
+static int voice_focus_ctl_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
+ struct ca0132_spec *spec = codec->spec;
+ hda_nid_t nid = get_amp_nid(kcontrol);
+ long *valp = ucontrol->value.integer.value;
+ int idx;
+
+ idx = nid - TUNING_CTL_START_NID;
+ /* any change? */
+ if (spec->cur_ctl_vals[idx] == *valp)
+ return 0;
+
+ spec->cur_ctl_vals[idx] = *valp;
+
+ idx = *valp - 20;
+ tuning_ctl_set(codec, nid, voice_focus_vals_lookup, idx);
+
+ return 1;
+}
+
+static int mic_svm_ctl_info(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_info *uinfo)
+{
+ int chs = get_amp_channels(kcontrol);
+ uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
+ uinfo->count = chs == 3 ? 2 : 1;
+ uinfo->value.integer.min = 0;
+ uinfo->value.integer.max = 100;
+ uinfo->value.integer.step = 1;
+
+ return 0;
+}
+
+static int mic_svm_ctl_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
+ struct ca0132_spec *spec = codec->spec;
+ hda_nid_t nid = get_amp_nid(kcontrol);
+ long *valp = ucontrol->value.integer.value;
+ int idx;
+
+ idx = nid - TUNING_CTL_START_NID;
+ /* any change? */
+ if (spec->cur_ctl_vals[idx] == *valp)
+ return 0;
+
+ spec->cur_ctl_vals[idx] = *valp;
+
+ idx = *valp;
+ tuning_ctl_set(codec, nid, mic_svm_vals_lookup, idx);
+
+ return 0;
+}
+
+static int equalizer_ctl_info(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_info *uinfo)
+{
+ int chs = get_amp_channels(kcontrol);
+ uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
+ uinfo->count = chs == 3 ? 2 : 1;
+ uinfo->value.integer.min = 0;
+ uinfo->value.integer.max = 48;
+ uinfo->value.integer.step = 1;
+
+ return 0;
+}
+
+static int equalizer_ctl_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
+ struct ca0132_spec *spec = codec->spec;
+ hda_nid_t nid = get_amp_nid(kcontrol);
+ long *valp = ucontrol->value.integer.value;
+ int idx;
+
+ idx = nid - TUNING_CTL_START_NID;
+ /* any change? */
+ if (spec->cur_ctl_vals[idx] == *valp)
+ return 0;
+
+ spec->cur_ctl_vals[idx] = *valp;
+
+ idx = *valp;
+ tuning_ctl_set(codec, nid, equalizer_vals_lookup, idx);
+
+ return 1;
+}
+
+static const DECLARE_TLV_DB_SCALE(voice_focus_db_scale, 2000, 100, 0);
+static const DECLARE_TLV_DB_SCALE(eq_db_scale, -2400, 100, 0);
+
+static int add_tuning_control(struct hda_codec *codec,
+ hda_nid_t pnid, hda_nid_t nid,
+ const char *name, int dir)
+{
+ char namestr[SNDRV_CTL_ELEM_ID_NAME_MAXLEN];
+ int type = dir ? HDA_INPUT : HDA_OUTPUT;
+ struct snd_kcontrol_new knew =
+ HDA_CODEC_VOLUME_MONO(namestr, nid, 1, 0, type);
+
+ knew.access = SNDRV_CTL_ELEM_ACCESS_READWRITE |
+ SNDRV_CTL_ELEM_ACCESS_TLV_READ;
+ knew.tlv.c = 0;
+ knew.tlv.p = 0;
+ switch (pnid) {
+ case VOICE_FOCUS:
+ knew.info = voice_focus_ctl_info;
+ knew.get = tuning_ctl_get;
+ knew.put = voice_focus_ctl_put;
+ knew.tlv.p = voice_focus_db_scale;
+ break;
+ case MIC_SVM:
+ knew.info = mic_svm_ctl_info;
+ knew.get = tuning_ctl_get;
+ knew.put = mic_svm_ctl_put;
+ break;
+ case EQUALIZER:
+ knew.info = equalizer_ctl_info;
+ knew.get = tuning_ctl_get;
+ knew.put = equalizer_ctl_put;
+ knew.tlv.p = eq_db_scale;
+ break;
+ default:
+ return 0;
+ }
+ knew.private_value =
+ HDA_COMPOSE_AMP_VAL(nid, 1, 0, type);
+ sprintf(namestr, "%s %s Volume", name, dirstr[dir]);
+ return snd_hda_ctl_add(codec, nid, snd_ctl_new1(&knew, codec));
+}
+
+static int add_tuning_ctls(struct hda_codec *codec)
+{
+ int i;
+ int err;
+
+ for (i = 0; i < TUNING_CTLS_COUNT; i++) {
+ err = add_tuning_control(codec,
+ ca0132_tuning_ctls[i].parent_nid,
+ ca0132_tuning_ctls[i].nid,
+ ca0132_tuning_ctls[i].name,
+ ca0132_tuning_ctls[i].direct);
+ if (err < 0)
+ return err;
+ }
+
+ return 0;
+}
+
+static void ca0132_init_tuning_defaults(struct hda_codec *codec)
+{
+ struct ca0132_spec *spec = codec->spec;
+ int i;
+
+ /* Wedge Angle defaults to 30. 10 below is 30 - 20. 20 is min. */
+ spec->cur_ctl_vals[WEDGE_ANGLE - TUNING_CTL_START_NID] = 10;
+ /* SVM level defaults to 0.74. */
+ spec->cur_ctl_vals[SVM_LEVEL - TUNING_CTL_START_NID] = 74;
+
+ /* EQ defaults to 0dB. */
+ for (i = 2; i < TUNING_CTLS_COUNT; i++)
+ spec->cur_ctl_vals[i] = 24;
+}
+#endif /*ENABLE_TUNING_CONTROLS*/
+
+/*
+ * Select the active output.
+ * If autodetect is enabled, output will be selected based on jack detection.
+ * If jack inserted, headphone will be selected, else built-in speakers
+ * If autodetect is disabled, output will be selected based on selection.
+ */
+static int ca0132_select_out(struct hda_codec *codec)
+{
+ struct ca0132_spec *spec = codec->spec;
+ unsigned int pin_ctl;
+ int jack_present;
+ int auto_jack;
+ unsigned int tmp;
+ int err;
+
+ codec_dbg(codec, "ca0132_select_out\n");
+
+ snd_hda_power_up(codec);
+
+ auto_jack = spec->vnode_lswitch[VNID_HP_ASEL - VNODE_START_NID];
+
+ if (auto_jack)
+ jack_present = snd_hda_jack_detect(codec, spec->out_pins[1]);
+ else
+ jack_present =
+ spec->vnode_lswitch[VNID_HP_SEL - VNODE_START_NID];
+
+ if (jack_present)
+ spec->cur_out_type = HEADPHONE_OUT;
+ else
+ spec->cur_out_type = SPEAKER_OUT;
+
+ if (spec->cur_out_type == SPEAKER_OUT) {
+ codec_dbg(codec, "ca0132_select_out speaker\n");
+ /*speaker out config*/
+ tmp = FLOAT_ONE;
+ err = dspio_set_uint_param(codec, 0x80, 0x04, tmp);
+ if (err < 0)
+ goto exit;
+ /*enable speaker EQ*/
+ tmp = FLOAT_ONE;
+ err = dspio_set_uint_param(codec, 0x8f, 0x00, tmp);
+ if (err < 0)
+ goto exit;
+
+ /* Setup EAPD */
+ snd_hda_codec_write(codec, spec->out_pins[1], 0,
+ VENDOR_CHIPIO_EAPD_SEL_SET, 0x02);
+ snd_hda_codec_write(codec, spec->out_pins[0], 0,
+ AC_VERB_SET_EAPD_BTLENABLE, 0x00);
+ snd_hda_codec_write(codec, spec->out_pins[0], 0,
+ VENDOR_CHIPIO_EAPD_SEL_SET, 0x00);
+ snd_hda_codec_write(codec, spec->out_pins[0], 0,
+ AC_VERB_SET_EAPD_BTLENABLE, 0x02);
+
+ /* disable headphone node */
+ pin_ctl = snd_hda_codec_read(codec, spec->out_pins[1], 0,
+ AC_VERB_GET_PIN_WIDGET_CONTROL, 0);
+ snd_hda_set_pin_ctl(codec, spec->out_pins[1],
+ pin_ctl & ~PIN_HP);
+ /* enable speaker node */
+ pin_ctl = snd_hda_codec_read(codec, spec->out_pins[0], 0,
+ AC_VERB_GET_PIN_WIDGET_CONTROL, 0);
+ snd_hda_set_pin_ctl(codec, spec->out_pins[0],
+ pin_ctl | PIN_OUT);
+ } else {
+ codec_dbg(codec, "ca0132_select_out hp\n");
+ /*headphone out config*/
+ tmp = FLOAT_ZERO;
+ err = dspio_set_uint_param(codec, 0x80, 0x04, tmp);
+ if (err < 0)
+ goto exit;
+ /*disable speaker EQ*/
+ tmp = FLOAT_ZERO;
+ err = dspio_set_uint_param(codec, 0x8f, 0x00, tmp);
+ if (err < 0)
+ goto exit;
+
+ /* Setup EAPD */
+ snd_hda_codec_write(codec, spec->out_pins[0], 0,
+ VENDOR_CHIPIO_EAPD_SEL_SET, 0x00);
+ snd_hda_codec_write(codec, spec->out_pins[0], 0,
+ AC_VERB_SET_EAPD_BTLENABLE, 0x00);
+ snd_hda_codec_write(codec, spec->out_pins[1], 0,
+ VENDOR_CHIPIO_EAPD_SEL_SET, 0x02);
+ snd_hda_codec_write(codec, spec->out_pins[0], 0,
+ AC_VERB_SET_EAPD_BTLENABLE, 0x02);
+
+ /* disable speaker*/
+ pin_ctl = snd_hda_codec_read(codec, spec->out_pins[0], 0,
+ AC_VERB_GET_PIN_WIDGET_CONTROL, 0);
+ snd_hda_set_pin_ctl(codec, spec->out_pins[0],
+ pin_ctl & ~PIN_HP);
+ /* enable headphone*/
+ pin_ctl = snd_hda_codec_read(codec, spec->out_pins[1], 0,
+ AC_VERB_GET_PIN_WIDGET_CONTROL, 0);
+ snd_hda_set_pin_ctl(codec, spec->out_pins[1],
+ pin_ctl | PIN_HP);
+ }
+
+exit:
+ snd_hda_power_down(codec);
+
+ return err < 0 ? err : 0;
+}
+
+static void ca0132_unsol_hp_delayed(struct work_struct *work)
+{
+ struct ca0132_spec *spec = container_of(
+ to_delayed_work(work), struct ca0132_spec, unsol_hp_work);
+ ca0132_select_out(spec->codec);
+ snd_hda_jack_report_sync(spec->codec);
+}
+
+static void ca0132_set_dmic(struct hda_codec *codec, int enable);
+static int ca0132_mic_boost_set(struct hda_codec *codec, long val);
+static int ca0132_effects_set(struct hda_codec *codec, hda_nid_t nid, long val);
+
+/*
+ * Select the active VIP source
+ */
+static int ca0132_set_vipsource(struct hda_codec *codec, int val)
+{
+ struct ca0132_spec *spec = codec->spec;
+ unsigned int tmp;
+
+ if (spec->dsp_state != DSP_DOWNLOADED)
+ return 0;
+
+ /* if CrystalVoice if off, vipsource should be 0 */
+ if (!spec->effects_switch[CRYSTAL_VOICE - EFFECT_START_NID] ||
+ (val == 0)) {
+ chipio_set_control_param(codec, CONTROL_PARAM_VIP_SOURCE, 0);
+ chipio_set_conn_rate(codec, MEM_CONNID_MICIN1, SR_96_000);
+ chipio_set_conn_rate(codec, MEM_CONNID_MICOUT1, SR_96_000);
+ if (spec->cur_mic_type == DIGITAL_MIC)
+ tmp = FLOAT_TWO;
+ else
+ tmp = FLOAT_ONE;
+ dspio_set_uint_param(codec, 0x80, 0x00, tmp);
+ tmp = FLOAT_ZERO;
+ dspio_set_uint_param(codec, 0x80, 0x05, tmp);
+ } else {
+ chipio_set_conn_rate(codec, MEM_CONNID_MICIN1, SR_16_000);
+ chipio_set_conn_rate(codec, MEM_CONNID_MICOUT1, SR_16_000);
+ if (spec->cur_mic_type == DIGITAL_MIC)
+ tmp = FLOAT_TWO;
+ else
+ tmp = FLOAT_ONE;
+ dspio_set_uint_param(codec, 0x80, 0x00, tmp);
+ tmp = FLOAT_ONE;
+ dspio_set_uint_param(codec, 0x80, 0x05, tmp);
+ msleep(20);
+ chipio_set_control_param(codec, CONTROL_PARAM_VIP_SOURCE, val);
+ }
+
+ return 1;
+}
+
+/*
+ * Select the active microphone.
+ * If autodetect is enabled, mic will be selected based on jack detection.
+ * If jack inserted, ext.mic will be selected, else built-in mic
+ * If autodetect is disabled, mic will be selected based on selection.
+ */
+static int ca0132_select_mic(struct hda_codec *codec)
+{
+ struct ca0132_spec *spec = codec->spec;
+ int jack_present;
+ int auto_jack;
+
+ codec_dbg(codec, "ca0132_select_mic\n");
+
+ snd_hda_power_up(codec);
+
+ auto_jack = spec->vnode_lswitch[VNID_AMIC1_ASEL - VNODE_START_NID];
+
+ if (auto_jack)
+ jack_present = snd_hda_jack_detect(codec, spec->input_pins[0]);
+ else
+ jack_present =
+ spec->vnode_lswitch[VNID_AMIC1_SEL - VNODE_START_NID];
+
+ if (jack_present)
+ spec->cur_mic_type = LINE_MIC_IN;
+ else
+ spec->cur_mic_type = DIGITAL_MIC;
+
+ if (spec->cur_mic_type == DIGITAL_MIC) {
+ /* enable digital Mic */
+ chipio_set_conn_rate(codec, MEM_CONNID_DMIC, SR_32_000);
+ ca0132_set_dmic(codec, 1);
+ ca0132_mic_boost_set(codec, 0);
+ /* set voice focus */
+ ca0132_effects_set(codec, VOICE_FOCUS,
+ spec->effects_switch
+ [VOICE_FOCUS - EFFECT_START_NID]);
+ } else {
+ /* disable digital Mic */
+ chipio_set_conn_rate(codec, MEM_CONNID_DMIC, SR_96_000);
+ ca0132_set_dmic(codec, 0);
+ ca0132_mic_boost_set(codec, spec->cur_mic_boost);
+ /* disable voice focus */
+ ca0132_effects_set(codec, VOICE_FOCUS, 0);
+ }
+
+ snd_hda_power_down(codec);
+
+ return 0;
+}
+
+/*
+ * Check if VNODE settings take effect immediately.
+ */
+static bool ca0132_is_vnode_effective(struct hda_codec *codec,
+ hda_nid_t vnid,
+ hda_nid_t *shared_nid)
+{
+ struct ca0132_spec *spec = codec->spec;
+ hda_nid_t nid;
+
+ switch (vnid) {
+ case VNID_SPK:
+ nid = spec->shared_out_nid;
+ break;
+ case VNID_MIC:
+ nid = spec->shared_mic_nid;
+ break;
+ default:
+ return false;
+ }
+
+ if (shared_nid)
+ *shared_nid = nid;
+
+ return true;
+}
+
+/*
+* The following functions are control change helpers.
+* They return 0 if no changed. Return 1 if changed.
+*/
+static int ca0132_voicefx_set(struct hda_codec *codec, int enable)
+{
+ struct ca0132_spec *spec = codec->spec;
+ unsigned int tmp;
+
+ /* based on CrystalVoice state to enable VoiceFX. */
+ if (enable) {
+ tmp = spec->effects_switch[CRYSTAL_VOICE - EFFECT_START_NID] ?
+ FLOAT_ONE : FLOAT_ZERO;
+ } else {
+ tmp = FLOAT_ZERO;
+ }
+
+ dspio_set_uint_param(codec, ca0132_voicefx.mid,
+ ca0132_voicefx.reqs[0], tmp);
+
+ return 1;
+}
+
+/*
+ * Set the effects parameters
+ */
+static int ca0132_effects_set(struct hda_codec *codec, hda_nid_t nid, long val)
+{
+ struct ca0132_spec *spec = codec->spec;
+ unsigned int on;
+ int num_fx = OUT_EFFECTS_COUNT + IN_EFFECTS_COUNT;
+ int err = 0;
+ int idx = nid - EFFECT_START_NID;
+
+ if ((idx < 0) || (idx >= num_fx))
+ return 0; /* no changed */
+
+ /* for out effect, qualify with PE */
+ if ((nid >= OUT_EFFECT_START_NID) && (nid < OUT_EFFECT_END_NID)) {
+ /* if PE if off, turn off out effects. */
+ if (!spec->effects_switch[PLAY_ENHANCEMENT - EFFECT_START_NID])
+ val = 0;
+ }
+
+ /* for in effect, qualify with CrystalVoice */
+ if ((nid >= IN_EFFECT_START_NID) && (nid < IN_EFFECT_END_NID)) {
+ /* if CrystalVoice if off, turn off in effects. */
+ if (!spec->effects_switch[CRYSTAL_VOICE - EFFECT_START_NID])
+ val = 0;
+
+ /* Voice Focus applies to 2-ch Mic, Digital Mic */
+ if ((nid == VOICE_FOCUS) && (spec->cur_mic_type != DIGITAL_MIC))
+ val = 0;
+ }
+
+ codec_dbg(codec, "ca0132_effect_set: nid=0x%x, val=%ld\n",
+ nid, val);
+
+ on = (val == 0) ? FLOAT_ZERO : FLOAT_ONE;
+ err = dspio_set_uint_param(codec, ca0132_effects[idx].mid,
+ ca0132_effects[idx].reqs[0], on);
+
+ if (err < 0)
+ return 0; /* no changed */
+
+ return 1;
+}
+
+/*
+ * Turn on/off Playback Enhancements
+ */
+static int ca0132_pe_switch_set(struct hda_codec *codec)
+{
+ struct ca0132_spec *spec = codec->spec;
+ hda_nid_t nid;
+ int i, ret = 0;
+
+ codec_dbg(codec, "ca0132_pe_switch_set: val=%ld\n",
+ spec->effects_switch[PLAY_ENHANCEMENT - EFFECT_START_NID]);
+
+ i = OUT_EFFECT_START_NID - EFFECT_START_NID;
+ nid = OUT_EFFECT_START_NID;
+ /* PE affects all out effects */
+ for (; nid < OUT_EFFECT_END_NID; nid++, i++)
+ ret |= ca0132_effects_set(codec, nid, spec->effects_switch[i]);
+
+ return ret;
+}
+
+/* Check if Mic1 is streaming, if so, stop streaming */
+static int stop_mic1(struct hda_codec *codec)
+{
+ struct ca0132_spec *spec = codec->spec;
+ unsigned int oldval = snd_hda_codec_read(codec, spec->adcs[0], 0,
+ AC_VERB_GET_CONV, 0);
+ if (oldval != 0)
+ snd_hda_codec_write(codec, spec->adcs[0], 0,
+ AC_VERB_SET_CHANNEL_STREAMID,
+ 0);
+ return oldval;
+}
+
+/* Resume Mic1 streaming if it was stopped. */
+static void resume_mic1(struct hda_codec *codec, unsigned int oldval)
+{
+ struct ca0132_spec *spec = codec->spec;
+ /* Restore the previous stream and channel */
+ if (oldval != 0)
+ snd_hda_codec_write(codec, spec->adcs[0], 0,
+ AC_VERB_SET_CHANNEL_STREAMID,
+ oldval);
+}
+
+/*
+ * Turn on/off CrystalVoice
+ */
+static int ca0132_cvoice_switch_set(struct hda_codec *codec)
+{
+ struct ca0132_spec *spec = codec->spec;
+ hda_nid_t nid;
+ int i, ret = 0;
+ unsigned int oldval;
+
+ codec_dbg(codec, "ca0132_cvoice_switch_set: val=%ld\n",
+ spec->effects_switch[CRYSTAL_VOICE - EFFECT_START_NID]);
+
+ i = IN_EFFECT_START_NID - EFFECT_START_NID;
+ nid = IN_EFFECT_START_NID;
+ /* CrystalVoice affects all in effects */
+ for (; nid < IN_EFFECT_END_NID; nid++, i++)
+ ret |= ca0132_effects_set(codec, nid, spec->effects_switch[i]);
+
+ /* including VoiceFX */
+ ret |= ca0132_voicefx_set(codec, (spec->voicefx_val ? 1 : 0));
+
+ /* set correct vipsource */
+ oldval = stop_mic1(codec);
+ ret |= ca0132_set_vipsource(codec, 1);
+ resume_mic1(codec, oldval);
+ return ret;
+}
+
+static int ca0132_mic_boost_set(struct hda_codec *codec, long val)
+{
+ struct ca0132_spec *spec = codec->spec;
+ int ret = 0;
+
+ if (val) /* on */
+ ret = snd_hda_codec_amp_update(codec, spec->input_pins[0], 0,
+ HDA_INPUT, 0, HDA_AMP_VOLMASK, 3);
+ else /* off */
+ ret = snd_hda_codec_amp_update(codec, spec->input_pins[0], 0,
+ HDA_INPUT, 0, HDA_AMP_VOLMASK, 0);
+
+ return ret;
+}
+
+static int ca0132_vnode_switch_set(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
+ hda_nid_t nid = get_amp_nid(kcontrol);
+ hda_nid_t shared_nid = 0;
+ bool effective;
+ int ret = 0;
+ struct ca0132_spec *spec = codec->spec;
+ int auto_jack;
+
+ if (nid == VNID_HP_SEL) {
+ auto_jack =
+ spec->vnode_lswitch[VNID_HP_ASEL - VNODE_START_NID];
+ if (!auto_jack)
+ ca0132_select_out(codec);
+ return 1;
+ }
+
+ if (nid == VNID_AMIC1_SEL) {
+ auto_jack =
+ spec->vnode_lswitch[VNID_AMIC1_ASEL - VNODE_START_NID];
+ if (!auto_jack)
+ ca0132_select_mic(codec);
+ return 1;
+ }
+
+ if (nid == VNID_HP_ASEL) {
+ ca0132_select_out(codec);
+ return 1;
+ }
+
+ if (nid == VNID_AMIC1_ASEL) {
+ ca0132_select_mic(codec);
+ return 1;
+ }
+
+ /* if effective conditions, then update hw immediately. */
+ effective = ca0132_is_vnode_effective(codec, nid, &shared_nid);
+ if (effective) {
+ int dir = get_amp_direction(kcontrol);
+ int ch = get_amp_channels(kcontrol);
+ unsigned long pval;
+
+ mutex_lock(&codec->control_mutex);
+ pval = kcontrol->private_value;
+ kcontrol->private_value = HDA_COMPOSE_AMP_VAL(shared_nid, ch,
+ 0, dir);
+ ret = snd_hda_mixer_amp_switch_put(kcontrol, ucontrol);
+ kcontrol->private_value = pval;
+ mutex_unlock(&codec->control_mutex);
+ }
+
+ return ret;
+}
+/* End of control change helpers. */
+
+static int ca0132_voicefx_info(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_info *uinfo)
+{
+ unsigned int items = sizeof(ca0132_voicefx_presets)
+ / sizeof(struct ct_voicefx_preset);
+
+ uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED;
+ uinfo->count = 1;
+ uinfo->value.enumerated.items = items;
+ if (uinfo->value.enumerated.item >= items)
+ uinfo->value.enumerated.item = items - 1;
+ strcpy(uinfo->value.enumerated.name,
+ ca0132_voicefx_presets[uinfo->value.enumerated.item].name);
+ return 0;
+}
+
+static int ca0132_voicefx_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
+ struct ca0132_spec *spec = codec->spec;
+
+ ucontrol->value.enumerated.item[0] = spec->voicefx_val;
+ return 0;
+}
+
+static int ca0132_voicefx_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
+ struct ca0132_spec *spec = codec->spec;
+ int i, err = 0;
+ int sel = ucontrol->value.enumerated.item[0];
+ unsigned int items = sizeof(ca0132_voicefx_presets)
+ / sizeof(struct ct_voicefx_preset);
+
+ if (sel >= items)
+ return 0;
+
+ codec_dbg(codec, "ca0132_voicefx_put: sel=%d, preset=%s\n",
+ sel, ca0132_voicefx_presets[sel].name);
+
+ /*
+ * Idx 0 is default.
+ * Default needs to qualify with CrystalVoice state.
+ */
+ for (i = 0; i < VOICEFX_MAX_PARAM_COUNT; i++) {
+ err = dspio_set_uint_param(codec, ca0132_voicefx.mid,
+ ca0132_voicefx.reqs[i],
+ ca0132_voicefx_presets[sel].vals[i]);
+ if (err < 0)
+ break;
+ }
+
+ if (err >= 0) {
+ spec->voicefx_val = sel;
+ /* enable voice fx */
+ ca0132_voicefx_set(codec, (sel ? 1 : 0));
+ }
+
+ return 1;
+}
+
+static int ca0132_switch_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
+ struct ca0132_spec *spec = codec->spec;
+ hda_nid_t nid = get_amp_nid(kcontrol);
+ int ch = get_amp_channels(kcontrol);
+ long *valp = ucontrol->value.integer.value;
+
+ /* vnode */
+ if ((nid >= VNODE_START_NID) && (nid < VNODE_END_NID)) {
+ if (ch & 1) {
+ *valp = spec->vnode_lswitch[nid - VNODE_START_NID];
+ valp++;
+ }
+ if (ch & 2) {
+ *valp = spec->vnode_rswitch[nid - VNODE_START_NID];
+ valp++;
+ }
+ return 0;
+ }
+
+ /* effects, include PE and CrystalVoice */
+ if ((nid >= EFFECT_START_NID) && (nid < EFFECT_END_NID)) {
+ *valp = spec->effects_switch[nid - EFFECT_START_NID];
+ return 0;
+ }
+
+ /* mic boost */
+ if (nid == spec->input_pins[0]) {
+ *valp = spec->cur_mic_boost;
+ return 0;
+ }
+
+ return 0;
+}
+
+static int ca0132_switch_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
+ struct ca0132_spec *spec = codec->spec;
+ hda_nid_t nid = get_amp_nid(kcontrol);
+ int ch = get_amp_channels(kcontrol);
+ long *valp = ucontrol->value.integer.value;
+ int changed = 1;
+
+ codec_dbg(codec, "ca0132_switch_put: nid=0x%x, val=%ld\n",
+ nid, *valp);
+
+ snd_hda_power_up(codec);
+ /* vnode */
+ if ((nid >= VNODE_START_NID) && (nid < VNODE_END_NID)) {
+ if (ch & 1) {
+ spec->vnode_lswitch[nid - VNODE_START_NID] = *valp;
+ valp++;
+ }
+ if (ch & 2) {
+ spec->vnode_rswitch[nid - VNODE_START_NID] = *valp;
+ valp++;
+ }
+ changed = ca0132_vnode_switch_set(kcontrol, ucontrol);
+ goto exit;
+ }
+
+ /* PE */
+ if (nid == PLAY_ENHANCEMENT) {
+ spec->effects_switch[nid - EFFECT_START_NID] = *valp;
+ changed = ca0132_pe_switch_set(codec);
+ goto exit;
+ }
+
+ /* CrystalVoice */
+ if (nid == CRYSTAL_VOICE) {
+ spec->effects_switch[nid - EFFECT_START_NID] = *valp;
+ changed = ca0132_cvoice_switch_set(codec);
+ goto exit;
+ }
+
+ /* out and in effects */
+ if (((nid >= OUT_EFFECT_START_NID) && (nid < OUT_EFFECT_END_NID)) ||
+ ((nid >= IN_EFFECT_START_NID) && (nid < IN_EFFECT_END_NID))) {
+ spec->effects_switch[nid - EFFECT_START_NID] = *valp;
+ changed = ca0132_effects_set(codec, nid, *valp);
+ goto exit;
+ }
+
+ /* mic boost */
+ if (nid == spec->input_pins[0]) {
+ spec->cur_mic_boost = *valp;
+
+ /* Mic boost does not apply to Digital Mic */
+ if (spec->cur_mic_type != DIGITAL_MIC)
+ changed = ca0132_mic_boost_set(codec, *valp);
+ goto exit;
+ }
+
+exit:
+ snd_hda_power_down(codec);
+ return changed;
+}
+
+/*
+ * Volume related
+ */
+static int ca0132_volume_info(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_info *uinfo)
+{
+ struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
+ struct ca0132_spec *spec = codec->spec;
+ hda_nid_t nid = get_amp_nid(kcontrol);
+ int ch = get_amp_channels(kcontrol);
+ int dir = get_amp_direction(kcontrol);
+ unsigned long pval;
+ int err;
+
+ switch (nid) {
+ case VNID_SPK:
+ /* follow shared_out info */
+ nid = spec->shared_out_nid;
+ mutex_lock(&codec->control_mutex);
+ pval = kcontrol->private_value;
+ kcontrol->private_value = HDA_COMPOSE_AMP_VAL(nid, ch, 0, dir);
+ err = snd_hda_mixer_amp_volume_info(kcontrol, uinfo);
+ kcontrol->private_value = pval;
+ mutex_unlock(&codec->control_mutex);
+ break;
+ case VNID_MIC:
+ /* follow shared_mic info */
+ nid = spec->shared_mic_nid;
+ mutex_lock(&codec->control_mutex);
+ pval = kcontrol->private_value;
+ kcontrol->private_value = HDA_COMPOSE_AMP_VAL(nid, ch, 0, dir);
+ err = snd_hda_mixer_amp_volume_info(kcontrol, uinfo);
+ kcontrol->private_value = pval;
+ mutex_unlock(&codec->control_mutex);
+ break;
+ default:
+ err = snd_hda_mixer_amp_volume_info(kcontrol, uinfo);
+ }
+ return err;
+}
+
+static int ca0132_volume_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
+ struct ca0132_spec *spec = codec->spec;
+ hda_nid_t nid = get_amp_nid(kcontrol);
+ int ch = get_amp_channels(kcontrol);
+ long *valp = ucontrol->value.integer.value;
+
+ /* store the left and right volume */
+ if (ch & 1) {
+ *valp = spec->vnode_lvol[nid - VNODE_START_NID];
+ valp++;
+ }
+ if (ch & 2) {
+ *valp = spec->vnode_rvol[nid - VNODE_START_NID];
+ valp++;
+ }
+ return 0;
+}
+
+static int ca0132_volume_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
+ struct ca0132_spec *spec = codec->spec;
+ hda_nid_t nid = get_amp_nid(kcontrol);
+ int ch = get_amp_channels(kcontrol);
+ long *valp = ucontrol->value.integer.value;
+ hda_nid_t shared_nid = 0;
+ bool effective;
+ int changed = 1;
+
+ /* store the left and right volume */
+ if (ch & 1) {
+ spec->vnode_lvol[nid - VNODE_START_NID] = *valp;
+ valp++;
+ }
+ if (ch & 2) {
+ spec->vnode_rvol[nid - VNODE_START_NID] = *valp;
+ valp++;
+ }
+
+ /* if effective conditions, then update hw immediately. */
+ effective = ca0132_is_vnode_effective(codec, nid, &shared_nid);
+ if (effective) {
+ int dir = get_amp_direction(kcontrol);
+ unsigned long pval;
+
+ snd_hda_power_up(codec);
+ mutex_lock(&codec->control_mutex);
+ pval = kcontrol->private_value;
+ kcontrol->private_value = HDA_COMPOSE_AMP_VAL(shared_nid, ch,
+ 0, dir);
+ changed = snd_hda_mixer_amp_volume_put(kcontrol, ucontrol);
+ kcontrol->private_value = pval;
+ mutex_unlock(&codec->control_mutex);
+ snd_hda_power_down(codec);
+ }
+
+ return changed;
+}
+
+static int ca0132_volume_tlv(struct snd_kcontrol *kcontrol, int op_flag,
+ unsigned int size, unsigned int __user *tlv)
+{
+ struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
+ struct ca0132_spec *spec = codec->spec;
+ hda_nid_t nid = get_amp_nid(kcontrol);
+ int ch = get_amp_channels(kcontrol);
+ int dir = get_amp_direction(kcontrol);
+ unsigned long pval;
+ int err;
+
+ switch (nid) {
+ case VNID_SPK:
+ /* follow shared_out tlv */
+ nid = spec->shared_out_nid;
+ mutex_lock(&codec->control_mutex);
+ pval = kcontrol->private_value;
+ kcontrol->private_value = HDA_COMPOSE_AMP_VAL(nid, ch, 0, dir);
+ err = snd_hda_mixer_amp_tlv(kcontrol, op_flag, size, tlv);
+ kcontrol->private_value = pval;
+ mutex_unlock(&codec->control_mutex);
+ break;
+ case VNID_MIC:
+ /* follow shared_mic tlv */
+ nid = spec->shared_mic_nid;
+ mutex_lock(&codec->control_mutex);
+ pval = kcontrol->private_value;
+ kcontrol->private_value = HDA_COMPOSE_AMP_VAL(nid, ch, 0, dir);
+ err = snd_hda_mixer_amp_tlv(kcontrol, op_flag, size, tlv);
+ kcontrol->private_value = pval;
+ mutex_unlock(&codec->control_mutex);
+ break;
+ default:
+ err = snd_hda_mixer_amp_tlv(kcontrol, op_flag, size, tlv);
+ }
+ return err;
+}
+
+static int add_fx_switch(struct hda_codec *codec, hda_nid_t nid,
+ const char *pfx, int dir)
+{
+ char namestr[SNDRV_CTL_ELEM_ID_NAME_MAXLEN];
+ int type = dir ? HDA_INPUT : HDA_OUTPUT;
+ struct snd_kcontrol_new knew =
+ CA0132_CODEC_MUTE_MONO(namestr, nid, 1, type);
+ sprintf(namestr, "%s %s Switch", pfx, dirstr[dir]);
+ return snd_hda_ctl_add(codec, nid, snd_ctl_new1(&knew, codec));
+}
+
+static int add_voicefx(struct hda_codec *codec)
+{
+ struct snd_kcontrol_new knew =
+ HDA_CODEC_MUTE_MONO(ca0132_voicefx.name,
+ VOICEFX, 1, 0, HDA_INPUT);
+ knew.info = ca0132_voicefx_info;
+ knew.get = ca0132_voicefx_get;
+ knew.put = ca0132_voicefx_put;
+ return snd_hda_ctl_add(codec, VOICEFX, snd_ctl_new1(&knew, codec));
+}
+
+/*
+ * When changing Node IDs for Mixer Controls below, make sure to update
+ * Node IDs in ca0132_config() as well.
+ */
+static struct snd_kcontrol_new ca0132_mixer[] = {
+ CA0132_CODEC_VOL("Master Playback Volume", VNID_SPK, HDA_OUTPUT),
+ CA0132_CODEC_MUTE("Master Playback Switch", VNID_SPK, HDA_OUTPUT),
+ CA0132_CODEC_VOL("Capture Volume", VNID_MIC, HDA_INPUT),
+ CA0132_CODEC_MUTE("Capture Switch", VNID_MIC, HDA_INPUT),
+ HDA_CODEC_VOLUME("Analog-Mic2 Capture Volume", 0x08, 0, HDA_INPUT),
+ HDA_CODEC_MUTE("Analog-Mic2 Capture Switch", 0x08, 0, HDA_INPUT),
+ HDA_CODEC_VOLUME("What U Hear Capture Volume", 0x0a, 0, HDA_INPUT),
+ HDA_CODEC_MUTE("What U Hear Capture Switch", 0x0a, 0, HDA_INPUT),
+ CA0132_CODEC_MUTE_MONO("Mic1-Boost (30dB) Capture Switch",
+ 0x12, 1, HDA_INPUT),
+ CA0132_CODEC_MUTE_MONO("HP/Speaker Playback Switch",
+ VNID_HP_SEL, 1, HDA_OUTPUT),
+ CA0132_CODEC_MUTE_MONO("AMic1/DMic Capture Switch",
+ VNID_AMIC1_SEL, 1, HDA_INPUT),
+ CA0132_CODEC_MUTE_MONO("HP/Speaker Auto Detect Playback Switch",
+ VNID_HP_ASEL, 1, HDA_OUTPUT),
+ CA0132_CODEC_MUTE_MONO("AMic1/DMic Auto Detect Capture Switch",
+ VNID_AMIC1_ASEL, 1, HDA_INPUT),
+ { } /* end */
+};
+
+static int ca0132_build_controls(struct hda_codec *codec)
+{
+ struct ca0132_spec *spec = codec->spec;
+ int i, num_fx;
+ int err = 0;
+
+ /* Add Mixer controls */
+ for (i = 0; i < spec->num_mixers; i++) {
+ err = snd_hda_add_new_ctls(codec, spec->mixers[i]);
+ if (err < 0)
+ return err;
+ }
+
+ /* Add in and out effects controls.
+ * VoiceFX, PE and CrystalVoice are added separately.
+ */
+ num_fx = OUT_EFFECTS_COUNT + IN_EFFECTS_COUNT;
+ for (i = 0; i < num_fx; i++) {
+ err = add_fx_switch(codec, ca0132_effects[i].nid,
+ ca0132_effects[i].name,
+ ca0132_effects[i].direct);
+ if (err < 0)
+ return err;
+ }
+
+ err = add_fx_switch(codec, PLAY_ENHANCEMENT, "PlayEnhancement", 0);
+ if (err < 0)
+ return err;
+
+ err = add_fx_switch(codec, CRYSTAL_VOICE, "CrystalVoice", 1);
+ if (err < 0)
+ return err;
+
+ add_voicefx(codec);
+
+#ifdef ENABLE_TUNING_CONTROLS
+ add_tuning_ctls(codec);
+#endif
+
+ err = snd_hda_jack_add_kctls(codec, &spec->autocfg);
+ if (err < 0)
+ return err;
+
+ if (spec->dig_out) {
+ err = snd_hda_create_spdif_out_ctls(codec, spec->dig_out,
+ spec->dig_out);
+ if (err < 0)
+ return err;
+ err = snd_hda_create_spdif_share_sw(codec, &spec->multiout);
+ if (err < 0)
+ return err;
+ /* spec->multiout.share_spdif = 1; */
+ }
+
+ if (spec->dig_in) {
+ err = snd_hda_create_spdif_in_ctls(codec, spec->dig_in);
+ if (err < 0)
+ return err;
+ }
+ return 0;
+}
+
+/*
+ * PCM
+ */
+static struct hda_pcm_stream ca0132_pcm_analog_playback = {
+ .substreams = 1,
+ .channels_min = 2,
+ .channels_max = 6,
+ .ops = {
+ .prepare = ca0132_playback_pcm_prepare,
+ .cleanup = ca0132_playback_pcm_cleanup,
+ .get_delay = ca0132_playback_pcm_delay,
+ },
+};
+
+static struct hda_pcm_stream ca0132_pcm_analog_capture = {
+ .substreams = 1,
+ .channels_min = 2,
+ .channels_max = 2,
+ .ops = {
+ .prepare = ca0132_capture_pcm_prepare,
+ .cleanup = ca0132_capture_pcm_cleanup,
+ .get_delay = ca0132_capture_pcm_delay,
+ },
+};
+
+static struct hda_pcm_stream ca0132_pcm_digital_playback = {
+ .substreams = 1,
+ .channels_min = 2,
+ .channels_max = 2,
+ .ops = {
+ .open = ca0132_dig_playback_pcm_open,
+ .close = ca0132_dig_playback_pcm_close,
+ .prepare = ca0132_dig_playback_pcm_prepare,
+ .cleanup = ca0132_dig_playback_pcm_cleanup
+ },
+};
+
+static struct hda_pcm_stream ca0132_pcm_digital_capture = {
+ .substreams = 1,
+ .channels_min = 2,
+ .channels_max = 2,
+};
+
+static int ca0132_build_pcms(struct hda_codec *codec)
+{
+ struct ca0132_spec *spec = codec->spec;
+ struct hda_pcm *info = spec->pcm_rec;
+
+ codec->pcm_info = info;
+ codec->num_pcms = 0;
+
+ info->name = "CA0132 Analog";
+ info->stream[SNDRV_PCM_STREAM_PLAYBACK] = ca0132_pcm_analog_playback;
+ info->stream[SNDRV_PCM_STREAM_PLAYBACK].nid = spec->dacs[0];
+ info->stream[SNDRV_PCM_STREAM_PLAYBACK].channels_max =
+ spec->multiout.max_channels;
+ info->stream[SNDRV_PCM_STREAM_CAPTURE] = ca0132_pcm_analog_capture;
+ info->stream[SNDRV_PCM_STREAM_CAPTURE].substreams = 1;
+ info->stream[SNDRV_PCM_STREAM_CAPTURE].nid = spec->adcs[0];
+ codec->num_pcms++;
+
+ info++;
+ info->name = "CA0132 Analog Mic-In2";
+ info->stream[SNDRV_PCM_STREAM_CAPTURE] = ca0132_pcm_analog_capture;
+ info->stream[SNDRV_PCM_STREAM_CAPTURE].substreams = 1;
+ info->stream[SNDRV_PCM_STREAM_CAPTURE].nid = spec->adcs[1];
+ codec->num_pcms++;
+
+ info++;
+ info->name = "CA0132 What U Hear";
+ info->stream[SNDRV_PCM_STREAM_CAPTURE] = ca0132_pcm_analog_capture;
+ info->stream[SNDRV_PCM_STREAM_CAPTURE].substreams = 1;
+ info->stream[SNDRV_PCM_STREAM_CAPTURE].nid = spec->adcs[2];
+ codec->num_pcms++;
+
+ if (!spec->dig_out && !spec->dig_in)
+ return 0;
+
+ info++;
+ info->name = "CA0132 Digital";
+ info->pcm_type = HDA_PCM_TYPE_SPDIF;
+ if (spec->dig_out) {
+ info->stream[SNDRV_PCM_STREAM_PLAYBACK] =
+ ca0132_pcm_digital_playback;
+ info->stream[SNDRV_PCM_STREAM_PLAYBACK].nid = spec->dig_out;
+ }
+ if (spec->dig_in) {
+ info->stream[SNDRV_PCM_STREAM_CAPTURE] =
+ ca0132_pcm_digital_capture;
+ info->stream[SNDRV_PCM_STREAM_CAPTURE].nid = spec->dig_in;
+ }
+ codec->num_pcms++;
+
+ return 0;
+}
+
+static void init_output(struct hda_codec *codec, hda_nid_t pin, hda_nid_t dac)
+{
+ if (pin) {
+ snd_hda_set_pin_ctl(codec, pin, PIN_HP);
+ if (get_wcaps(codec, pin) & AC_WCAP_OUT_AMP)
+ snd_hda_codec_write(codec, pin, 0,
+ AC_VERB_SET_AMP_GAIN_MUTE,
+ AMP_OUT_UNMUTE);
+ }
+ if (dac && (get_wcaps(codec, dac) & AC_WCAP_OUT_AMP))
+ snd_hda_codec_write(codec, dac, 0,
+ AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO);
+}
+
+static void init_input(struct hda_codec *codec, hda_nid_t pin, hda_nid_t adc)
+{
+ if (pin) {
+ snd_hda_set_pin_ctl(codec, pin, PIN_VREF80);
+ if (get_wcaps(codec, pin) & AC_WCAP_IN_AMP)
+ snd_hda_codec_write(codec, pin, 0,
+ AC_VERB_SET_AMP_GAIN_MUTE,
+ AMP_IN_UNMUTE(0));
+ }
+ if (adc && (get_wcaps(codec, adc) & AC_WCAP_IN_AMP)) {
+ snd_hda_codec_write(codec, adc, 0, AC_VERB_SET_AMP_GAIN_MUTE,
+ AMP_IN_UNMUTE(0));
+
+ /* init to 0 dB and unmute. */
+ snd_hda_codec_amp_stereo(codec, adc, HDA_INPUT, 0,
+ HDA_AMP_VOLMASK, 0x5a);
+ snd_hda_codec_amp_stereo(codec, adc, HDA_INPUT, 0,
+ HDA_AMP_MUTE, 0);
+ }
+}
+
+static void ca0132_init_unsol(struct hda_codec *codec)
+{
+ snd_hda_jack_detect_enable(codec, UNSOL_TAG_HP, UNSOL_TAG_HP);
+ snd_hda_jack_detect_enable(codec, UNSOL_TAG_AMIC1, UNSOL_TAG_AMIC1);
+}
+
+static void refresh_amp_caps(struct hda_codec *codec, hda_nid_t nid, int dir)
+{
+ unsigned int caps;
+
+ caps = snd_hda_param_read(codec, nid, dir == HDA_OUTPUT ?
+ AC_PAR_AMP_OUT_CAP : AC_PAR_AMP_IN_CAP);
+ snd_hda_override_amp_caps(codec, nid, dir, caps);
+}
+
+/*
+ * Switch between Digital built-in mic and analog mic.
+ */
+static void ca0132_set_dmic(struct hda_codec *codec, int enable)
+{
+ struct ca0132_spec *spec = codec->spec;
+ unsigned int tmp;
+ u8 val;
+ unsigned int oldval;
+
+ codec_dbg(codec, "ca0132_set_dmic: enable=%d\n", enable);
+
+ oldval = stop_mic1(codec);
+ ca0132_set_vipsource(codec, 0);
+ if (enable) {
+ /* set DMic input as 2-ch */
+ tmp = FLOAT_TWO;
+ dspio_set_uint_param(codec, 0x80, 0x00, tmp);
+
+ val = spec->dmic_ctl;
+ val |= 0x80;
+ snd_hda_codec_write(codec, spec->input_pins[0], 0,
+ VENDOR_CHIPIO_DMIC_CTL_SET, val);
+
+ if (!(spec->dmic_ctl & 0x20))
+ chipio_set_control_flag(codec, CONTROL_FLAG_DMIC, 1);
+ } else {
+ /* set AMic input as mono */
+ tmp = FLOAT_ONE;
+ dspio_set_uint_param(codec, 0x80, 0x00, tmp);
+
+ val = spec->dmic_ctl;
+ /* clear bit7 and bit5 to disable dmic */
+ val &= 0x5f;
+ snd_hda_codec_write(codec, spec->input_pins[0], 0,
+ VENDOR_CHIPIO_DMIC_CTL_SET, val);
+
+ if (!(spec->dmic_ctl & 0x20))
+ chipio_set_control_flag(codec, CONTROL_FLAG_DMIC, 0);
+ }
+ ca0132_set_vipsource(codec, 1);
+ resume_mic1(codec, oldval);
+}
+
+/*
+ * Initialization for Digital Mic.
+ */
+static void ca0132_init_dmic(struct hda_codec *codec)
+{
+ struct ca0132_spec *spec = codec->spec;
+ u8 val;
+
+ /* Setup Digital Mic here, but don't enable.
+ * Enable based on jack detect.
+ */
+
+ /* MCLK uses MPIO1, set to enable.
+ * Bit 2-0: MPIO select
+ * Bit 3: set to disable
+ * Bit 7-4: reserved
+ */
+ val = 0x01;
+ snd_hda_codec_write(codec, spec->input_pins[0], 0,
+ VENDOR_CHIPIO_DMIC_MCLK_SET, val);
+
+ /* Data1 uses MPIO3. Data2 not use
+ * Bit 2-0: Data1 MPIO select
+ * Bit 3: set disable Data1
+ * Bit 6-4: Data2 MPIO select
+ * Bit 7: set disable Data2
+ */
+ val = 0x83;
+ snd_hda_codec_write(codec, spec->input_pins[0], 0,
+ VENDOR_CHIPIO_DMIC_PIN_SET, val);
+
+ /* Use Ch-0 and Ch-1. Rate is 48K, mode 1. Disable DMic first.
+ * Bit 3-0: Channel mask
+ * Bit 4: set for 48KHz, clear for 32KHz
+ * Bit 5: mode
+ * Bit 6: set to select Data2, clear for Data1
+ * Bit 7: set to enable DMic, clear for AMic
+ */
+ val = 0x23;
+ /* keep a copy of dmic ctl val for enable/disable dmic purpuse */
+ spec->dmic_ctl = val;
+ snd_hda_codec_write(codec, spec->input_pins[0], 0,
+ VENDOR_CHIPIO_DMIC_CTL_SET, val);
+}
+
+/*
+ * Initialization for Analog Mic 2
+ */
+static void ca0132_init_analog_mic2(struct hda_codec *codec)
+{
+ struct ca0132_spec *spec = codec->spec;
+
+ mutex_lock(&spec->chipio_mutex);
+ snd_hda_codec_write(codec, WIDGET_CHIP_CTRL, 0,
+ VENDOR_CHIPIO_8051_ADDRESS_LOW, 0x20);
+ snd_hda_codec_write(codec, WIDGET_CHIP_CTRL, 0,
+ VENDOR_CHIPIO_8051_ADDRESS_HIGH, 0x19);
+ snd_hda_codec_write(codec, WIDGET_CHIP_CTRL, 0,
+ VENDOR_CHIPIO_8051_DATA_WRITE, 0x00);
+ snd_hda_codec_write(codec, WIDGET_CHIP_CTRL, 0,
+ VENDOR_CHIPIO_8051_ADDRESS_LOW, 0x2D);
+ snd_hda_codec_write(codec, WIDGET_CHIP_CTRL, 0,
+ VENDOR_CHIPIO_8051_ADDRESS_HIGH, 0x19);
+ snd_hda_codec_write(codec, WIDGET_CHIP_CTRL, 0,
+ VENDOR_CHIPIO_8051_DATA_WRITE, 0x00);
+ mutex_unlock(&spec->chipio_mutex);
+}
+
+static void ca0132_refresh_widget_caps(struct hda_codec *codec)
+{
+ struct ca0132_spec *spec = codec->spec;
+ int i;
+ hda_nid_t nid;
+
+ codec_dbg(codec, "ca0132_refresh_widget_caps.\n");
+ nid = codec->start_nid;
+ for (i = 0; i < codec->num_nodes; i++, nid++)
+ codec->wcaps[i] = snd_hda_param_read(codec, nid,
+ AC_PAR_AUDIO_WIDGET_CAP);
+
+ for (i = 0; i < spec->multiout.num_dacs; i++)
+ refresh_amp_caps(codec, spec->dacs[i], HDA_OUTPUT);
+
+ for (i = 0; i < spec->num_outputs; i++)
+ refresh_amp_caps(codec, spec->out_pins[i], HDA_OUTPUT);
+
+ for (i = 0; i < spec->num_inputs; i++) {
+ refresh_amp_caps(codec, spec->adcs[i], HDA_INPUT);
+ refresh_amp_caps(codec, spec->input_pins[i], HDA_INPUT);
+ }
+}
+
+/*
+ * Setup default parameters for DSP
+ */
+static void ca0132_setup_defaults(struct hda_codec *codec)
+{
+ struct ca0132_spec *spec = codec->spec;
+ unsigned int tmp;
+ int num_fx;
+ int idx, i;
+
+ if (spec->dsp_state != DSP_DOWNLOADED)
+ return;
+
+ /* out, in effects + voicefx */
+ num_fx = OUT_EFFECTS_COUNT + IN_EFFECTS_COUNT + 1;
+ for (idx = 0; idx < num_fx; idx++) {
+ for (i = 0; i <= ca0132_effects[idx].params; i++) {
+ dspio_set_uint_param(codec, ca0132_effects[idx].mid,
+ ca0132_effects[idx].reqs[i],
+ ca0132_effects[idx].def_vals[i]);
+ }
+ }
+
+ /*remove DSP headroom*/
+ tmp = FLOAT_ZERO;
+ dspio_set_uint_param(codec, 0x96, 0x3C, tmp);
+
+ /*set speaker EQ bypass attenuation*/
+ dspio_set_uint_param(codec, 0x8f, 0x01, tmp);
+
+ /* set AMic1 and AMic2 as mono mic */
+ tmp = FLOAT_ONE;
+ dspio_set_uint_param(codec, 0x80, 0x00, tmp);
+ dspio_set_uint_param(codec, 0x80, 0x01, tmp);
+
+ /* set AMic1 as CrystalVoice input */
+ tmp = FLOAT_ONE;
+ dspio_set_uint_param(codec, 0x80, 0x05, tmp);
+
+ /* set WUH source */
+ tmp = FLOAT_TWO;
+ dspio_set_uint_param(codec, 0x31, 0x00, tmp);
+}
+
+/*
+ * Initialization of flags in chip
+ */
+static void ca0132_init_flags(struct hda_codec *codec)
+{
+ chipio_set_control_flag(codec, CONTROL_FLAG_IDLE_ENABLE, 0);
+ chipio_set_control_flag(codec, CONTROL_FLAG_PORT_A_COMMON_MODE, 0);
+ chipio_set_control_flag(codec, CONTROL_FLAG_PORT_D_COMMON_MODE, 0);
+ chipio_set_control_flag(codec, CONTROL_FLAG_PORT_A_10KOHM_LOAD, 0);
+ chipio_set_control_flag(codec, CONTROL_FLAG_PORT_D_10KOHM_LOAD, 0);
+ chipio_set_control_flag(codec, CONTROL_FLAG_ADC_C_HIGH_PASS, 1);
+}
+
+/*
+ * Initialization of parameters in chip
+ */
+static void ca0132_init_params(struct hda_codec *codec)
+{
+ chipio_set_control_param(codec, CONTROL_PARAM_PORTA_160OHM_GAIN, 6);
+ chipio_set_control_param(codec, CONTROL_PARAM_PORTD_160OHM_GAIN, 6);
+}
+
+static void ca0132_set_dsp_msr(struct hda_codec *codec, bool is96k)
+{
+ chipio_set_control_flag(codec, CONTROL_FLAG_DSP_96KHZ, is96k);
+ chipio_set_control_flag(codec, CONTROL_FLAG_DAC_96KHZ, is96k);
+ chipio_set_control_flag(codec, CONTROL_FLAG_SRC_RATE_96KHZ, is96k);
+ chipio_set_control_flag(codec, CONTROL_FLAG_SRC_CLOCK_196MHZ, is96k);
+ chipio_set_control_flag(codec, CONTROL_FLAG_ADC_B_96KHZ, is96k);
+ chipio_set_control_flag(codec, CONTROL_FLAG_ADC_C_96KHZ, is96k);
+
+ chipio_set_conn_rate(codec, MEM_CONNID_MICIN1, SR_96_000);
+ chipio_set_conn_rate(codec, MEM_CONNID_MICOUT1, SR_96_000);
+ chipio_set_conn_rate(codec, MEM_CONNID_WUH, SR_48_000);
+}
+
+static bool ca0132_download_dsp_images(struct hda_codec *codec)
+{
+ bool dsp_loaded = false;
+ const struct dsp_image_seg *dsp_os_image;
+ const struct firmware *fw_entry;
+
+ if (request_firmware(&fw_entry, EFX_FILE, codec->bus->card->dev) != 0)
+ return false;
+
+ dsp_os_image = (struct dsp_image_seg *)(fw_entry->data);
+ if (dspload_image(codec, dsp_os_image, 0, 0, true, 0)) {
+ pr_err("ca0132 dspload_image failed.\n");
+ goto exit_download;
+ }
+
+ dsp_loaded = dspload_wait_loaded(codec);
+
+exit_download:
+ release_firmware(fw_entry);
+
+ return dsp_loaded;
+}
+
+static void ca0132_download_dsp(struct hda_codec *codec)
+{
+ struct ca0132_spec *spec = codec->spec;
+
+#ifndef CONFIG_SND_HDA_CODEC_CA0132_DSP
+ return; /* NOP */
+#endif
+
+ chipio_enable_clocks(codec);
+ spec->dsp_state = DSP_DOWNLOADING;
+ if (!ca0132_download_dsp_images(codec))
+ spec->dsp_state = DSP_DOWNLOAD_FAILED;
+ else
+ spec->dsp_state = DSP_DOWNLOADED;
+
+ if (spec->dsp_state == DSP_DOWNLOADED)
+ ca0132_set_dsp_msr(codec, true);
+}
+
+static void ca0132_process_dsp_response(struct hda_codec *codec)
+{
+ struct ca0132_spec *spec = codec->spec;
+
+ codec_dbg(codec, "ca0132_process_dsp_response\n");
+ if (spec->wait_scp) {
+ if (dspio_get_response_data(codec) >= 0)
+ spec->wait_scp = 0;
+ }
+
+ dspio_clear_response_queue(codec);
+}
+
+static void ca0132_unsol_event(struct hda_codec *codec, unsigned int res)
+{
+ struct ca0132_spec *spec = codec->spec;
+
+ if (((res >> AC_UNSOL_RES_TAG_SHIFT) & 0x3f) == UNSOL_TAG_DSP) {
+ ca0132_process_dsp_response(codec);
+ } else {
+ res = snd_hda_jack_get_action(codec,
+ (res >> AC_UNSOL_RES_TAG_SHIFT) & 0x3f);
+
+ codec_dbg(codec, "snd_hda_jack_get_action: 0x%x\n", res);
+
+ switch (res) {
+ case UNSOL_TAG_HP:
+ /* Delay enabling the HP amp, to let the mic-detection
+ * state machine run.
+ */
+ cancel_delayed_work_sync(&spec->unsol_hp_work);
+ queue_delayed_work(codec->bus->workq,
+ &spec->unsol_hp_work,
+ msecs_to_jiffies(500));
+ break;
+ case UNSOL_TAG_AMIC1:
+ ca0132_select_mic(codec);
+ snd_hda_jack_report_sync(codec);
+ break;
+ default:
+ break;
+ }
+ }
+}
+
+/*
+ * Verbs tables.
+ */
+
+/* Sends before DSP download. */
+static struct hda_verb ca0132_base_init_verbs[] = {
+ /*enable ct extension*/
+ {0x15, VENDOR_CHIPIO_CT_EXTENSIONS_ENABLE, 0x1},
+ /*enable DSP node unsol, needed for DSP download*/
+ {0x16, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | UNSOL_TAG_DSP},
+ {}
+};
+
+/* Send at exit. */
+static struct hda_verb ca0132_base_exit_verbs[] = {
+ /*set afg to D3*/
+ {0x01, AC_VERB_SET_POWER_STATE, 0x03},
+ /*disable ct extension*/
+ {0x15, VENDOR_CHIPIO_CT_EXTENSIONS_ENABLE, 0},
+ {}
+};
+
+/* Other verbs tables. Sends after DSP download. */
+static struct hda_verb ca0132_init_verbs0[] = {
+ /* chip init verbs */
+ {0x15, 0x70D, 0xF0},
+ {0x15, 0x70E, 0xFE},
+ {0x15, 0x707, 0x75},
+ {0x15, 0x707, 0xD3},
+ {0x15, 0x707, 0x09},
+ {0x15, 0x707, 0x53},
+ {0x15, 0x707, 0xD4},
+ {0x15, 0x707, 0xEF},
+ {0x15, 0x707, 0x75},
+ {0x15, 0x707, 0xD3},
+ {0x15, 0x707, 0x09},
+ {0x15, 0x707, 0x02},
+ {0x15, 0x707, 0x37},
+ {0x15, 0x707, 0x78},
+ {0x15, 0x53C, 0xCE},
+ {0x15, 0x575, 0xC9},
+ {0x15, 0x53D, 0xCE},
+ {0x15, 0x5B7, 0xC9},
+ {0x15, 0x70D, 0xE8},
+ {0x15, 0x70E, 0xFE},
+ {0x15, 0x707, 0x02},
+ {0x15, 0x707, 0x68},
+ {0x15, 0x707, 0x62},
+ {0x15, 0x53A, 0xCE},
+ {0x15, 0x546, 0xC9},
+ {0x15, 0x53B, 0xCE},
+ {0x15, 0x5E8, 0xC9},
+ {0x15, 0x717, 0x0D},
+ {0x15, 0x718, 0x20},
+ {}
+};
+
+static struct hda_verb ca0132_init_verbs1[] = {
+ {0x10, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | UNSOL_TAG_HP},
+ {0x12, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | UNSOL_TAG_AMIC1},
+ /* config EAPD */
+ {0x0b, 0x78D, 0x00},
+ /*{0x0b, AC_VERB_SET_EAPD_BTLENABLE, 0x02},*/
+ /*{0x10, 0x78D, 0x02},*/
+ /*{0x10, AC_VERB_SET_EAPD_BTLENABLE, 0x02},*/
+ {}
+};
+
+static void ca0132_init_chip(struct hda_codec *codec)
+{
+ struct ca0132_spec *spec = codec->spec;
+ int num_fx;
+ int i;
+ unsigned int on;
+
+ mutex_init(&spec->chipio_mutex);
+
+ spec->cur_out_type = SPEAKER_OUT;
+ spec->cur_mic_type = DIGITAL_MIC;
+ spec->cur_mic_boost = 0;
+
+ for (i = 0; i < VNODES_COUNT; i++) {
+ spec->vnode_lvol[i] = 0x5a;
+ spec->vnode_rvol[i] = 0x5a;
+ spec->vnode_lswitch[i] = 0;
+ spec->vnode_rswitch[i] = 0;
+ }
+
+ /*
+ * Default states for effects are in ca0132_effects[].
+ */
+ num_fx = OUT_EFFECTS_COUNT + IN_EFFECTS_COUNT;
+ for (i = 0; i < num_fx; i++) {
+ on = (unsigned int)ca0132_effects[i].reqs[0];
+ spec->effects_switch[i] = on ? 1 : 0;
+ }
+
+ spec->voicefx_val = 0;
+ spec->effects_switch[PLAY_ENHANCEMENT - EFFECT_START_NID] = 1;
+ spec->effects_switch[CRYSTAL_VOICE - EFFECT_START_NID] = 0;
+
+#ifdef ENABLE_TUNING_CONTROLS
+ ca0132_init_tuning_defaults(codec);
+#endif
+}
+
+static void ca0132_exit_chip(struct hda_codec *codec)
+{
+ /* put any chip cleanup stuffs here. */
+
+ if (dspload_is_loaded(codec))
+ dsp_reset(codec);
+}
+
+static int ca0132_init(struct hda_codec *codec)
+{
+ struct ca0132_spec *spec = codec->spec;
+ struct auto_pin_cfg *cfg = &spec->autocfg;
+ int i;
+
+ spec->dsp_state = DSP_DOWNLOAD_INIT;
+ spec->curr_chip_addx = INVALID_CHIP_ADDRESS;
+
+ snd_hda_power_up(codec);
+
+ ca0132_init_params(codec);
+ ca0132_init_flags(codec);
+ snd_hda_sequence_write(codec, spec->base_init_verbs);
+ ca0132_download_dsp(codec);
+ ca0132_refresh_widget_caps(codec);
+ ca0132_setup_defaults(codec);
+ ca0132_init_analog_mic2(codec);
+ ca0132_init_dmic(codec);
+
+ for (i = 0; i < spec->num_outputs; i++)
+ init_output(codec, spec->out_pins[i], spec->dacs[0]);
+
+ init_output(codec, cfg->dig_out_pins[0], spec->dig_out);
+
+ for (i = 0; i < spec->num_inputs; i++)
+ init_input(codec, spec->input_pins[i], spec->adcs[i]);
+
+ init_input(codec, cfg->dig_in_pin, spec->dig_in);
+
+ for (i = 0; i < spec->num_init_verbs; i++)
+ snd_hda_sequence_write(codec, spec->init_verbs[i]);
+
+ ca0132_init_unsol(codec);
+
+ ca0132_select_out(codec);
+ ca0132_select_mic(codec);
+
+ snd_hda_jack_report_sync(codec);
+
+ snd_hda_power_down(codec);
+
+ return 0;
+}
+
+static void ca0132_free(struct hda_codec *codec)
+{
+ struct ca0132_spec *spec = codec->spec;
+
+ cancel_delayed_work_sync(&spec->unsol_hp_work);
+ snd_hda_power_up(codec);
+ snd_hda_sequence_write(codec, spec->base_exit_verbs);
+ ca0132_exit_chip(codec);
+ snd_hda_power_down(codec);
+ kfree(codec->spec);
+}
+
+static struct hda_codec_ops ca0132_patch_ops = {
+ .build_controls = ca0132_build_controls,
+ .build_pcms = ca0132_build_pcms,
+ .init = ca0132_init,
+ .free = ca0132_free,
+ .unsol_event = ca0132_unsol_event,
+};
+
+static void ca0132_config(struct hda_codec *codec)
+{
+ struct ca0132_spec *spec = codec->spec;
+ struct auto_pin_cfg *cfg = &spec->autocfg;
+
+ spec->dacs[0] = 0x2;
+ spec->dacs[1] = 0x3;
+ spec->dacs[2] = 0x4;
+
+ spec->multiout.dac_nids = spec->dacs;
+ spec->multiout.num_dacs = 3;
+ spec->multiout.max_channels = 2;
+
+ spec->num_outputs = 2;
+ spec->out_pins[0] = 0x0b; /* speaker out */
+ spec->out_pins[1] = 0x10; /* headphone out */
+ spec->shared_out_nid = 0x2;
+
+ spec->num_inputs = 3;
+ spec->adcs[0] = 0x7; /* digital mic / analog mic1 */
+ spec->adcs[1] = 0x8; /* analog mic2 */
+ spec->adcs[2] = 0xa; /* what u hear */
+ spec->shared_mic_nid = 0x7;
+
+ spec->input_pins[0] = 0x12;
+ spec->input_pins[1] = 0x11;
+ spec->input_pins[2] = 0x13;
+
+ /* SPDIF I/O */
+ spec->dig_out = 0x05;
+ spec->multiout.dig_out_nid = spec->dig_out;
+ cfg->dig_out_pins[0] = 0x0c;
+ cfg->dig_outs = 1;
+ cfg->dig_out_type[0] = HDA_PCM_TYPE_SPDIF;
+ spec->dig_in = 0x09;
+ cfg->dig_in_pin = 0x0e;
+ cfg->dig_in_type = HDA_PCM_TYPE_SPDIF;
+}
+
+static int patch_ca0132(struct hda_codec *codec)
+{
+ struct ca0132_spec *spec;
+ int err;
+
+ codec_dbg(codec, "patch_ca0132\n");
+
+ spec = kzalloc(sizeof(*spec), GFP_KERNEL);
+ if (!spec)
+ return -ENOMEM;
+ codec->spec = spec;
+ spec->codec = codec;
+
+ spec->num_mixers = 1;
+ spec->mixers[0] = ca0132_mixer;
+
+ spec->base_init_verbs = ca0132_base_init_verbs;
+ spec->base_exit_verbs = ca0132_base_exit_verbs;
+ spec->init_verbs[0] = ca0132_init_verbs0;
+ spec->init_verbs[1] = ca0132_init_verbs1;
+ spec->num_init_verbs = 2;
+
+ INIT_DELAYED_WORK(&spec->unsol_hp_work, ca0132_unsol_hp_delayed);
+
+ ca0132_init_chip(codec);
+
+ ca0132_config(codec);
+
+ err = snd_hda_parse_pin_def_config(codec, &spec->autocfg, NULL);
+ if (err < 0)
+ return err;
+
+ codec->patch_ops = ca0132_patch_ops;
+ codec->pcm_format_first = 1;
+ codec->no_sticky_stream = 1;
+
+ return 0;
+}
+
+/*
+ * patch entries
+ */
+static struct hda_codec_preset snd_hda_preset_ca0132[] = {
+ { .id = 0x11020011, .name = "CA0132", .patch = patch_ca0132 },
+ {} /* terminator */
+};
+
+MODULE_ALIAS("snd-hda-codec-id:11020011");
+
+MODULE_LICENSE("GPL");
+MODULE_DESCRIPTION("Creative Sound Core3D codec");
+
+static struct hda_codec_preset_list ca0132_list = {
+ .preset = snd_hda_preset_ca0132,
+ .owner = THIS_MODULE,
+};
+
+static int __init patch_ca0132_init(void)
+{
+ return snd_hda_add_codec_preset(&ca0132_list);
+}
+
+static void __exit patch_ca0132_exit(void)
+{
+ snd_hda_delete_codec_preset(&ca0132_list);
+}
+
+module_init(patch_ca0132_init)
+module_exit(patch_ca0132_exit)
diff --git a/sound/pci/hda/patch_cirrus.c b/sound/pci/hda/patch_cirrus.c
index 067982f4f18..387f0b55188 100644
--- a/sound/pci/hda/patch_cirrus.c
+++ b/sound/pci/hda/patch_cirrus.c
@@ -19,65 +19,68 @@
*/
#include <linux/init.h>
-#include <linux/delay.h>
#include <linux/slab.h>
-#include <linux/pci.h>
+#include <linux/module.h>
#include <sound/core.h>
+#include <sound/tlv.h>
#include "hda_codec.h"
#include "hda_local.h"
+#include "hda_auto_parser.h"
+#include "hda_jack.h"
+#include "hda_generic.h"
/*
*/
struct cs_spec {
- int board_config;
- struct auto_pin_cfg autocfg;
- struct hda_multi_out multiout;
- struct snd_kcontrol *vmaster_sw;
- struct snd_kcontrol *vmaster_vol;
-
- hda_nid_t dac_nid[AUTO_CFG_MAX_OUTS];
- hda_nid_t slave_dig_outs[2];
-
- unsigned int input_idx[AUTO_PIN_LAST];
- unsigned int capsrc_idx[AUTO_PIN_LAST];
- hda_nid_t adc_nid[AUTO_PIN_LAST];
- unsigned int adc_idx[AUTO_PIN_LAST];
- unsigned int num_inputs;
- unsigned int cur_input;
- unsigned int automic_idx;
- hda_nid_t cur_adc;
- unsigned int cur_adc_stream_tag;
- unsigned int cur_adc_format;
- hda_nid_t dig_in;
-
- struct hda_bind_ctls *capture_bind[2];
+ struct hda_gen_spec gen;
unsigned int gpio_mask;
unsigned int gpio_dir;
unsigned int gpio_data;
-
- struct hda_pcm pcm_rec[2]; /* PCM information */
-
- unsigned int hp_detect:1;
- unsigned int mic_detect:1;
+ unsigned int gpio_eapd_hp; /* EAPD GPIO bit for headphones */
+ unsigned int gpio_eapd_speaker; /* EAPD GPIO bit for speakers */
+
+ /* CS421x */
+ unsigned int spdif_detect:1;
+ unsigned int spdif_present:1;
+ unsigned int sense_b:1;
+ hda_nid_t vendor_nid;
+
+ /* for MBP SPDIF control */
+ int (*spdif_sw_put)(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol);
};
-/* available models */
+/* available models with CS420x */
enum {
CS420X_MBP53,
CS420X_MBP55,
CS420X_IMAC27,
+ CS420X_GPIO_13,
+ CS420X_GPIO_23,
+ CS420X_MBP101,
+ CS420X_MBP81,
+ CS420X_MBA42,
CS420X_AUTO,
- CS420X_MODELS
+ /* aliases */
+ CS420X_IMAC27_122 = CS420X_GPIO_23,
+ CS420X_APPLE = CS420X_GPIO_13,
+};
+
+/* CS421x boards */
+enum {
+ CS421X_CDB4210,
+ CS421X_SENSE_B,
+ CS421X_STUMPY,
};
/* Vendor-specific processing widget */
#define CS420X_VENDOR_NID 0x11
#define CS_DIG_OUT1_PIN_NID 0x10
#define CS_DIG_OUT2_PIN_NID 0x15
-#define CS_DMIC1_PIN_NID 0x12
-#define CS_DMIC2_PIN_NID 0x0e
+#define CS_DMIC1_PIN_NID 0x0e
+#define CS_DMIC2_PIN_NID 0x12
/* coef indices */
#define IDX_SPDIF_STAT 0x0000
@@ -111,966 +114,926 @@ enum {
/* 0x0009 - 0x0014 -> 12 test regs */
/* 0x0015 - visibility reg */
+/* Cirrus Logic CS4208 */
+#define CS4208_VENDOR_NID 0x24
+
+/*
+ * Cirrus Logic CS4210
+ *
+ * 1 DAC => HP(sense) / Speakers,
+ * 1 ADC <= LineIn(sense) / MicIn / DMicIn,
+ * 1 SPDIF OUT => SPDIF Trasmitter(sense)
+*/
+#define CS4210_DAC_NID 0x02
+#define CS4210_ADC_NID 0x03
+#define CS4210_VENDOR_NID 0x0B
+#define CS421X_DMIC_PIN_NID 0x09 /* Port E */
+#define CS421X_SPDIF_PIN_NID 0x0A /* Port H */
+
+#define CS421X_IDX_DEV_CFG 0x01
+#define CS421X_IDX_ADC_CFG 0x02
+#define CS421X_IDX_DAC_CFG 0x03
+#define CS421X_IDX_SPK_CTL 0x04
+
+#define SPDIF_EVENT 0x04
+
+/* Cirrus Logic CS4213 is like CS4210 but does not have SPDIF input/output */
+#define CS4213_VENDOR_NID 0x09
+
static inline int cs_vendor_coef_get(struct hda_codec *codec, unsigned int idx)
{
- snd_hda_codec_write(codec, CS420X_VENDOR_NID, 0,
+ struct cs_spec *spec = codec->spec;
+ snd_hda_codec_write(codec, spec->vendor_nid, 0,
AC_VERB_SET_COEF_INDEX, idx);
- return snd_hda_codec_read(codec, CS420X_VENDOR_NID, 0,
+ return snd_hda_codec_read(codec, spec->vendor_nid, 0,
AC_VERB_GET_PROC_COEF, 0);
}
static inline void cs_vendor_coef_set(struct hda_codec *codec, unsigned int idx,
unsigned int coef)
{
- snd_hda_codec_write(codec, CS420X_VENDOR_NID, 0,
+ struct cs_spec *spec = codec->spec;
+ snd_hda_codec_write(codec, spec->vendor_nid, 0,
AC_VERB_SET_COEF_INDEX, idx);
- snd_hda_codec_write(codec, CS420X_VENDOR_NID, 0,
+ snd_hda_codec_write(codec, spec->vendor_nid, 0,
AC_VERB_SET_PROC_COEF, coef);
}
-
-#define HP_EVENT 1
-#define MIC_EVENT 2
-
/*
- * PCM callbacks
+ * auto-mute and auto-mic switching
+ * CS421x auto-output redirecting
+ * HP/SPK/SPDIF
*/
-static int cs_playback_pcm_open(struct hda_pcm_stream *hinfo,
- struct hda_codec *codec,
- struct snd_pcm_substream *substream)
-{
- struct cs_spec *spec = codec->spec;
- return snd_hda_multi_out_analog_open(codec, &spec->multiout, substream,
- hinfo);
-}
-static int cs_playback_pcm_prepare(struct hda_pcm_stream *hinfo,
- struct hda_codec *codec,
- unsigned int stream_tag,
- unsigned int format,
- struct snd_pcm_substream *substream)
+static void cs_automute(struct hda_codec *codec)
{
struct cs_spec *spec = codec->spec;
- return snd_hda_multi_out_analog_prepare(codec, &spec->multiout,
- stream_tag, format, substream);
-}
-static int cs_playback_pcm_cleanup(struct hda_pcm_stream *hinfo,
- struct hda_codec *codec,
- struct snd_pcm_substream *substream)
-{
- struct cs_spec *spec = codec->spec;
- return snd_hda_multi_out_analog_cleanup(codec, &spec->multiout);
-}
+ /* mute HPs if spdif jack (SENSE_B) is present */
+ spec->gen.master_mute = !!(spec->spdif_present && spec->sense_b);
-/*
- * Digital out
- */
-static int cs_dig_playback_pcm_open(struct hda_pcm_stream *hinfo,
- struct hda_codec *codec,
- struct snd_pcm_substream *substream)
-{
- struct cs_spec *spec = codec->spec;
- return snd_hda_multi_out_dig_open(codec, &spec->multiout);
-}
+ snd_hda_gen_update_outputs(codec);
-static int cs_dig_playback_pcm_close(struct hda_pcm_stream *hinfo,
- struct hda_codec *codec,
- struct snd_pcm_substream *substream)
-{
- struct cs_spec *spec = codec->spec;
- return snd_hda_multi_out_dig_close(codec, &spec->multiout);
+ if (spec->gpio_eapd_hp || spec->gpio_eapd_speaker) {
+ spec->gpio_data = spec->gen.hp_jack_present ?
+ spec->gpio_eapd_hp : spec->gpio_eapd_speaker;
+ snd_hda_codec_write(codec, 0x01, 0,
+ AC_VERB_SET_GPIO_DATA, spec->gpio_data);
+ }
}
-static int cs_dig_playback_pcm_prepare(struct hda_pcm_stream *hinfo,
- struct hda_codec *codec,
- unsigned int stream_tag,
- unsigned int format,
- struct snd_pcm_substream *substream)
+static bool is_active_pin(struct hda_codec *codec, hda_nid_t nid)
{
- struct cs_spec *spec = codec->spec;
- return snd_hda_multi_out_dig_prepare(codec, &spec->multiout, stream_tag,
- format, substream);
+ unsigned int val;
+ val = snd_hda_codec_get_pincfg(codec, nid);
+ return (get_defcfg_connect(val) != AC_JACK_PORT_NONE);
}
-static int cs_dig_playback_pcm_cleanup(struct hda_pcm_stream *hinfo,
- struct hda_codec *codec,
- struct snd_pcm_substream *substream)
+static void init_input_coef(struct hda_codec *codec)
{
struct cs_spec *spec = codec->spec;
- return snd_hda_multi_out_dig_cleanup(codec, &spec->multiout);
-}
+ unsigned int coef;
-/*
- * Analog capture
- */
-static int cs_capture_pcm_prepare(struct hda_pcm_stream *hinfo,
- struct hda_codec *codec,
- unsigned int stream_tag,
- unsigned int format,
- struct snd_pcm_substream *substream)
-{
- struct cs_spec *spec = codec->spec;
- spec->cur_adc = spec->adc_nid[spec->cur_input];
- spec->cur_adc_stream_tag = stream_tag;
- spec->cur_adc_format = format;
- snd_hda_codec_setup_stream(codec, spec->cur_adc, stream_tag, 0, format);
- return 0;
+ /* CS420x has multiple ADC, CS421x has single ADC */
+ if (spec->vendor_nid == CS420X_VENDOR_NID) {
+ coef = cs_vendor_coef_get(codec, IDX_BEEP_CFG);
+ if (is_active_pin(codec, CS_DMIC2_PIN_NID))
+ coef |= 1 << 4; /* DMIC2 2 chan on, GPIO1 off */
+ if (is_active_pin(codec, CS_DMIC1_PIN_NID))
+ coef |= 1 << 3; /* DMIC1 2 chan on, GPIO0 off
+ * No effect if SPDIF_OUT2 is
+ * selected in IDX_SPDIF_CTL.
+ */
+
+ cs_vendor_coef_set(codec, IDX_BEEP_CFG, coef);
+ }
}
-static int cs_capture_pcm_cleanup(struct hda_pcm_stream *hinfo,
- struct hda_codec *codec,
- struct snd_pcm_substream *substream)
-{
- struct cs_spec *spec = codec->spec;
- snd_hda_codec_cleanup_stream(codec, spec->cur_adc);
- spec->cur_adc = 0;
- return 0;
-}
+static const struct hda_verb cs_coef_init_verbs[] = {
+ {0x11, AC_VERB_SET_PROC_STATE, 1},
+ {0x11, AC_VERB_SET_COEF_INDEX, IDX_DAC_CFG},
+ {0x11, AC_VERB_SET_PROC_COEF,
+ (0x002a /* DAC1/2/3 SZCMode Soft Ramp */
+ | 0x0040 /* Mute DACs on FIFO error */
+ | 0x1000 /* Enable DACs High Pass Filter */
+ | 0x0400 /* Disable Coefficient Auto increment */
+ )},
+ /* ADC1/2 - Digital and Analog Soft Ramp */
+ {0x11, AC_VERB_SET_COEF_INDEX, IDX_ADC_CFG},
+ {0x11, AC_VERB_SET_PROC_COEF, 0x000a},
+ /* Beep */
+ {0x11, AC_VERB_SET_COEF_INDEX, IDX_BEEP_CFG},
+ {0x11, AC_VERB_SET_PROC_COEF, 0x0007}, /* Enable Beep thru DAC1/2/3 */
-/*
- */
-static struct hda_pcm_stream cs_pcm_analog_playback = {
- .substreams = 1,
- .channels_min = 2,
- .channels_max = 2,
- .ops = {
- .open = cs_playback_pcm_open,
- .prepare = cs_playback_pcm_prepare,
- .cleanup = cs_playback_pcm_cleanup
- },
+ {} /* terminator */
};
-static struct hda_pcm_stream cs_pcm_analog_capture = {
- .substreams = 1,
- .channels_min = 2,
- .channels_max = 2,
- .ops = {
- .prepare = cs_capture_pcm_prepare,
- .cleanup = cs_capture_pcm_cleanup
- },
+static const struct hda_verb cs4208_coef_init_verbs[] = {
+ {0x01, AC_VERB_SET_POWER_STATE, 0x00}, /* AFG: D0 */
+ {0x24, AC_VERB_SET_PROC_STATE, 0x01}, /* VPW: processing on */
+ {0x24, AC_VERB_SET_COEF_INDEX, 0x0033},
+ {0x24, AC_VERB_SET_PROC_COEF, 0x0001}, /* A1 ICS */
+ {0x24, AC_VERB_SET_COEF_INDEX, 0x0034},
+ {0x24, AC_VERB_SET_PROC_COEF, 0x1C01}, /* A1 Enable, A Thresh = 300mV */
+ {} /* terminator */
};
-static struct hda_pcm_stream cs_pcm_digital_playback = {
- .substreams = 1,
- .channels_min = 2,
- .channels_max = 2,
- .ops = {
- .open = cs_dig_playback_pcm_open,
- .close = cs_dig_playback_pcm_close,
- .prepare = cs_dig_playback_pcm_prepare,
- .cleanup = cs_dig_playback_pcm_cleanup
- },
-};
+/* Errata: CS4207 rev C0/C1/C2 Silicon
+ *
+ * http://www.cirrus.com/en/pubs/errata/ER880C3.pdf
+ *
+ * 6. At high temperature (TA > +85°C), the digital supply current (IVD)
+ * may be excessive (up to an additional 200 μA), which is most easily
+ * observed while the part is being held in reset (RESET# active low).
+ *
+ * Root Cause: At initial powerup of the device, the logic that drives
+ * the clock and write enable to the S/PDIF SRC RAMs is not properly
+ * initialized.
+ * Certain random patterns will cause a steady leakage current in those
+ * RAM cells. The issue will resolve once the SRCs are used (turned on).
+ *
+ * Workaround: The following verb sequence briefly turns on the S/PDIF SRC
+ * blocks, which will alleviate the issue.
+ */
-static struct hda_pcm_stream cs_pcm_digital_capture = {
- .substreams = 1,
- .channels_min = 2,
- .channels_max = 2,
-};
+static const struct hda_verb cs_errata_init_verbs[] = {
+ {0x01, AC_VERB_SET_POWER_STATE, 0x00}, /* AFG: D0 */
+ {0x11, AC_VERB_SET_PROC_STATE, 0x01}, /* VPW: processing on */
-static int cs_build_pcms(struct hda_codec *codec)
-{
- struct cs_spec *spec = codec->spec;
- struct hda_pcm *info = spec->pcm_rec;
-
- codec->pcm_info = info;
- codec->num_pcms = 0;
-
- info->name = "Cirrus Analog";
- info->stream[SNDRV_PCM_STREAM_PLAYBACK] = cs_pcm_analog_playback;
- info->stream[SNDRV_PCM_STREAM_PLAYBACK].nid = spec->dac_nid[0];
- info->stream[SNDRV_PCM_STREAM_PLAYBACK].channels_max =
- spec->multiout.max_channels;
- info->stream[SNDRV_PCM_STREAM_CAPTURE] = cs_pcm_analog_capture;
- info->stream[SNDRV_PCM_STREAM_CAPTURE].nid =
- spec->adc_nid[spec->cur_input];
- codec->num_pcms++;
-
- if (!spec->multiout.dig_out_nid && !spec->dig_in)
- return 0;
+ {0x11, AC_VERB_SET_COEF_INDEX, 0x0008},
+ {0x11, AC_VERB_SET_PROC_COEF, 0x9999},
+ {0x11, AC_VERB_SET_COEF_INDEX, 0x0017},
+ {0x11, AC_VERB_SET_PROC_COEF, 0xa412},
+ {0x11, AC_VERB_SET_COEF_INDEX, 0x0001},
+ {0x11, AC_VERB_SET_PROC_COEF, 0x0009},
- info++;
- info->name = "Cirrus Digital";
- info->pcm_type = spec->autocfg.dig_out_type[0];
- if (!info->pcm_type)
- info->pcm_type = HDA_PCM_TYPE_SPDIF;
- if (spec->multiout.dig_out_nid) {
- info->stream[SNDRV_PCM_STREAM_PLAYBACK] =
- cs_pcm_digital_playback;
- info->stream[SNDRV_PCM_STREAM_PLAYBACK].nid =
- spec->multiout.dig_out_nid;
- }
- if (spec->dig_in) {
- info->stream[SNDRV_PCM_STREAM_CAPTURE] =
- cs_pcm_digital_capture;
- info->stream[SNDRV_PCM_STREAM_CAPTURE].nid = spec->dig_in;
- }
- codec->num_pcms++;
+ {0x07, AC_VERB_SET_POWER_STATE, 0x00}, /* S/PDIF Rx: D0 */
+ {0x08, AC_VERB_SET_POWER_STATE, 0x00}, /* S/PDIF Tx: D0 */
- return 0;
-}
+ {0x11, AC_VERB_SET_COEF_INDEX, 0x0017},
+ {0x11, AC_VERB_SET_PROC_COEF, 0x2412},
+ {0x11, AC_VERB_SET_COEF_INDEX, 0x0008},
+ {0x11, AC_VERB_SET_PROC_COEF, 0x0000},
+ {0x11, AC_VERB_SET_COEF_INDEX, 0x0001},
+ {0x11, AC_VERB_SET_PROC_COEF, 0x0008},
+ {0x11, AC_VERB_SET_PROC_STATE, 0x00},
-/*
- * parse codec topology
- */
+#if 0 /* Don't to set to D3 as we are in power-up sequence */
+ {0x07, AC_VERB_SET_POWER_STATE, 0x03}, /* S/PDIF Rx: D3 */
+ {0x08, AC_VERB_SET_POWER_STATE, 0x03}, /* S/PDIF Tx: D3 */
+ /*{0x01, AC_VERB_SET_POWER_STATE, 0x03},*/ /* AFG: D3 This is already handled */
+#endif
+
+ {} /* terminator */
+};
-static hda_nid_t get_dac(struct hda_codec *codec, hda_nid_t pin)
+/* SPDIF setup */
+static void init_digital_coef(struct hda_codec *codec)
{
- hda_nid_t dac;
- if (!pin)
- return 0;
- if (snd_hda_get_connections(codec, pin, &dac, 1) != 1)
- return 0;
- return dac;
+ unsigned int coef;
+
+ coef = 0x0002; /* SRC_MUTE soft-mute on SPDIF (if no lock) */
+ coef |= 0x0008; /* Replace with mute on error */
+ if (is_active_pin(codec, CS_DIG_OUT2_PIN_NID))
+ coef |= 0x4000; /* RX to TX1 or TX2 Loopthru / SPDIF2
+ * SPDIF_OUT2 is shared with GPIO1 and
+ * DMIC_SDA2.
+ */
+ cs_vendor_coef_set(codec, IDX_SPDIF_CTL, coef);
}
-static int is_ext_mic(struct hda_codec *codec, unsigned int idx)
+static int cs_init(struct hda_codec *codec)
{
struct cs_spec *spec = codec->spec;
- struct auto_pin_cfg *cfg = &spec->autocfg;
- hda_nid_t pin = cfg->inputs[idx].pin;
- unsigned int val = snd_hda_query_pin_caps(codec, pin);
- if (!(val & AC_PINCAP_PRES_DETECT))
- return 0;
- val = snd_hda_codec_get_pincfg(codec, pin);
- return (snd_hda_get_input_pin_attr(val) != INPUT_PIN_ATTR_INT);
-}
-static hda_nid_t get_adc(struct hda_codec *codec, hda_nid_t pin,
- unsigned int *idxp)
-{
- int i;
- hda_nid_t nid;
-
- nid = codec->start_nid;
- for (i = 0; i < codec->num_nodes; i++, nid++) {
- hda_nid_t pins[2];
- unsigned int type;
- int j, nums;
- type = (get_wcaps(codec, nid) & AC_WCAP_TYPE)
- >> AC_WCAP_TYPE_SHIFT;
- if (type != AC_WID_AUD_IN)
- continue;
- nums = snd_hda_get_connections(codec, nid, pins,
- ARRAY_SIZE(pins));
- if (nums <= 0)
- continue;
- for (j = 0; j < nums; j++) {
- if (pins[j] == pin) {
- *idxp = j;
- return nid;
- }
- }
+ if (spec->vendor_nid == CS420X_VENDOR_NID) {
+ /* init_verb sequence for C0/C1/C2 errata*/
+ snd_hda_sequence_write(codec, cs_errata_init_verbs);
+ snd_hda_sequence_write(codec, cs_coef_init_verbs);
+ } else if (spec->vendor_nid == CS4208_VENDOR_NID) {
+ snd_hda_sequence_write(codec, cs4208_coef_init_verbs);
}
- return 0;
-}
-static int is_active_pin(struct hda_codec *codec, hda_nid_t nid)
-{
- unsigned int val;
- val = snd_hda_codec_get_pincfg(codec, nid);
- return (get_defcfg_connect(val) != AC_JACK_PORT_NONE);
-}
+ snd_hda_gen_init(codec);
-static int parse_output(struct hda_codec *codec)
-{
- struct cs_spec *spec = codec->spec;
- struct auto_pin_cfg *cfg = &spec->autocfg;
- int i, extra_nids;
- hda_nid_t dac;
-
- for (i = 0; i < cfg->line_outs; i++) {
- dac = get_dac(codec, cfg->line_out_pins[i]);
- if (!dac)
- break;
- spec->dac_nid[i] = dac;
- }
- spec->multiout.num_dacs = i;
- spec->multiout.dac_nids = spec->dac_nid;
- spec->multiout.max_channels = i * 2;
-
- /* add HP and speakers */
- extra_nids = 0;
- for (i = 0; i < cfg->hp_outs; i++) {
- dac = get_dac(codec, cfg->hp_pins[i]);
- if (!dac)
- break;
- if (!i)
- spec->multiout.hp_nid = dac;
- else
- spec->multiout.extra_out_nid[extra_nids++] = dac;
- }
- for (i = 0; i < cfg->speaker_outs; i++) {
- dac = get_dac(codec, cfg->speaker_pins[i]);
- if (!dac)
- break;
- spec->multiout.extra_out_nid[extra_nids++] = dac;
+ if (spec->gpio_mask) {
+ snd_hda_codec_write(codec, 0x01, 0, AC_VERB_SET_GPIO_MASK,
+ spec->gpio_mask);
+ snd_hda_codec_write(codec, 0x01, 0, AC_VERB_SET_GPIO_DIRECTION,
+ spec->gpio_dir);
+ snd_hda_codec_write(codec, 0x01, 0, AC_VERB_SET_GPIO_DATA,
+ spec->gpio_data);
}
- if (cfg->line_out_type == AUTO_PIN_SPEAKER_OUT) {
- cfg->speaker_outs = cfg->line_outs;
- memcpy(cfg->speaker_pins, cfg->line_out_pins,
- sizeof(cfg->speaker_pins));
- cfg->line_outs = 0;
+ if (spec->vendor_nid == CS420X_VENDOR_NID) {
+ init_input_coef(codec);
+ init_digital_coef(codec);
}
return 0;
}
-static int parse_input(struct hda_codec *codec)
+static int cs_build_controls(struct hda_codec *codec)
{
- struct cs_spec *spec = codec->spec;
- struct auto_pin_cfg *cfg = &spec->autocfg;
- int i;
-
- for (i = 0; i < cfg->num_inputs; i++) {
- hda_nid_t pin = cfg->inputs[i].pin;
- spec->input_idx[spec->num_inputs] = i;
- spec->capsrc_idx[i] = spec->num_inputs++;
- spec->cur_input = i;
- spec->adc_nid[i] = get_adc(codec, pin, &spec->adc_idx[i]);
- }
- if (!spec->num_inputs)
- return 0;
+ int err;
- /* check whether the automatic mic switch is available */
- if (spec->num_inputs == 2 &&
- cfg->inputs[0].type == AUTO_PIN_MIC &&
- cfg->inputs[1].type == AUTO_PIN_MIC) {
- if (is_ext_mic(codec, cfg->inputs[0].pin)) {
- if (!is_ext_mic(codec, cfg->inputs[1].pin)) {
- spec->mic_detect = 1;
- spec->automic_idx = 0;
- }
- } else {
- if (is_ext_mic(codec, cfg->inputs[1].pin)) {
- spec->mic_detect = 1;
- spec->automic_idx = 1;
- }
- }
- }
+ err = snd_hda_gen_build_controls(codec);
+ if (err < 0)
+ return err;
+ snd_hda_apply_fixup(codec, HDA_FIXUP_ACT_BUILD);
return 0;
}
+#define cs_free snd_hda_gen_free
+
+static const struct hda_codec_ops cs_patch_ops = {
+ .build_controls = cs_build_controls,
+ .build_pcms = snd_hda_gen_build_pcms,
+ .init = cs_init,
+ .free = cs_free,
+ .unsol_event = snd_hda_jack_unsol_event,
+};
-static int parse_digital_output(struct hda_codec *codec)
+static int cs_parse_auto_config(struct hda_codec *codec)
{
struct cs_spec *spec = codec->spec;
- struct auto_pin_cfg *cfg = &spec->autocfg;
- hda_nid_t nid;
+ int err;
- if (!cfg->dig_outs)
- return 0;
- if (snd_hda_get_connections(codec, cfg->dig_out_pins[0], &nid, 1) < 1)
- return 0;
- spec->multiout.dig_out_nid = nid;
- spec->multiout.share_spdif = 1;
- if (cfg->dig_outs > 1 &&
- snd_hda_get_connections(codec, cfg->dig_out_pins[1], &nid, 1) > 0) {
- spec->slave_dig_outs[0] = nid;
- codec->slave_dig_outs = spec->slave_dig_outs;
- }
- return 0;
-}
+ err = snd_hda_parse_pin_defcfg(codec, &spec->gen.autocfg, NULL, 0);
+ if (err < 0)
+ return err;
-static int parse_digital_input(struct hda_codec *codec)
-{
- struct cs_spec *spec = codec->spec;
- struct auto_pin_cfg *cfg = &spec->autocfg;
- int idx;
+ err = snd_hda_gen_parse_auto_config(codec, &spec->gen.autocfg);
+ if (err < 0)
+ return err;
- if (cfg->dig_in_pin)
- spec->dig_in = get_adc(codec, cfg->dig_in_pin, &idx);
return 0;
}
-/*
- * create mixer controls
- */
+static const struct hda_model_fixup cs420x_models[] = {
+ { .id = CS420X_MBP53, .name = "mbp53" },
+ { .id = CS420X_MBP55, .name = "mbp55" },
+ { .id = CS420X_IMAC27, .name = "imac27" },
+ { .id = CS420X_IMAC27_122, .name = "imac27_122" },
+ { .id = CS420X_APPLE, .name = "apple" },
+ { .id = CS420X_MBP101, .name = "mbp101" },
+ { .id = CS420X_MBP81, .name = "mbp81" },
+ { .id = CS420X_MBA42, .name = "mba42" },
+ {}
+};
-static const char * const dir_sfx[2] = { "Playback", "Capture" };
+static const struct snd_pci_quirk cs420x_fixup_tbl[] = {
+ SND_PCI_QUIRK(0x10de, 0x0ac0, "MacBookPro 5,3", CS420X_MBP53),
+ SND_PCI_QUIRK(0x10de, 0x0d94, "MacBookAir 3,1(2)", CS420X_MBP55),
+ SND_PCI_QUIRK(0x10de, 0xcb79, "MacBookPro 5,5", CS420X_MBP55),
+ SND_PCI_QUIRK(0x10de, 0xcb89, "MacBookPro 7,1", CS420X_MBP55),
+ /* this conflicts with too many other models */
+ /*SND_PCI_QUIRK(0x8086, 0x7270, "IMac 27 Inch", CS420X_IMAC27),*/
+
+ /* codec SSID */
+ SND_PCI_QUIRK(0x106b, 0x1c00, "MacBookPro 8,1", CS420X_MBP81),
+ SND_PCI_QUIRK(0x106b, 0x2000, "iMac 12,2", CS420X_IMAC27_122),
+ SND_PCI_QUIRK(0x106b, 0x2800, "MacBookPro 10,1", CS420X_MBP101),
+ SND_PCI_QUIRK(0x106b, 0x5b00, "MacBookAir 4,2", CS420X_MBA42),
+ SND_PCI_QUIRK_VENDOR(0x106b, "Apple", CS420X_APPLE),
+ {} /* terminator */
+};
-static int add_mute(struct hda_codec *codec, const char *name, int index,
- unsigned int pval, int dir, struct snd_kcontrol **kctlp)
-{
- char tmp[44];
- struct snd_kcontrol_new knew =
- HDA_CODEC_MUTE_IDX(tmp, index, 0, 0, HDA_OUTPUT);
- knew.private_value = pval;
- snprintf(tmp, sizeof(tmp), "%s %s Switch", name, dir_sfx[dir]);
- *kctlp = snd_ctl_new1(&knew, codec);
- (*kctlp)->id.subdevice = HDA_SUBDEV_AMP_FLAG;
- return snd_hda_ctl_add(codec, 0, *kctlp);
-}
+static const struct hda_pintbl mbp53_pincfgs[] = {
+ { 0x09, 0x012b4050 },
+ { 0x0a, 0x90100141 },
+ { 0x0b, 0x90100140 },
+ { 0x0c, 0x018b3020 },
+ { 0x0d, 0x90a00110 },
+ { 0x0e, 0x400000f0 },
+ { 0x0f, 0x01cbe030 },
+ { 0x10, 0x014be060 },
+ { 0x12, 0x400000f0 },
+ { 0x15, 0x400000f0 },
+ {} /* terminator */
+};
-static int add_volume(struct hda_codec *codec, const char *name,
- int index, unsigned int pval, int dir,
- struct snd_kcontrol **kctlp)
+static const struct hda_pintbl mbp55_pincfgs[] = {
+ { 0x09, 0x012b4030 },
+ { 0x0a, 0x90100121 },
+ { 0x0b, 0x90100120 },
+ { 0x0c, 0x400000f0 },
+ { 0x0d, 0x90a00110 },
+ { 0x0e, 0x400000f0 },
+ { 0x0f, 0x400000f0 },
+ { 0x10, 0x014be040 },
+ { 0x12, 0x400000f0 },
+ { 0x15, 0x400000f0 },
+ {} /* terminator */
+};
+
+static const struct hda_pintbl imac27_pincfgs[] = {
+ { 0x09, 0x012b4050 },
+ { 0x0a, 0x90100140 },
+ { 0x0b, 0x90100142 },
+ { 0x0c, 0x018b3020 },
+ { 0x0d, 0x90a00110 },
+ { 0x0e, 0x400000f0 },
+ { 0x0f, 0x01cbe030 },
+ { 0x10, 0x014be060 },
+ { 0x12, 0x01ab9070 },
+ { 0x15, 0x400000f0 },
+ {} /* terminator */
+};
+
+static const struct hda_pintbl mbp101_pincfgs[] = {
+ { 0x0d, 0x40ab90f0 },
+ { 0x0e, 0x90a600f0 },
+ { 0x12, 0x50a600f0 },
+ {} /* terminator */
+};
+
+static const struct hda_pintbl mba42_pincfgs[] = {
+ { 0x09, 0x012b4030 }, /* HP */
+ { 0x0a, 0x400000f0 },
+ { 0x0b, 0x90100120 }, /* speaker */
+ { 0x0c, 0x400000f0 },
+ { 0x0d, 0x90a00110 }, /* mic */
+ { 0x0e, 0x400000f0 },
+ { 0x0f, 0x400000f0 },
+ { 0x10, 0x400000f0 },
+ { 0x12, 0x400000f0 },
+ { 0x15, 0x400000f0 },
+ {} /* terminator */
+};
+
+static const struct hda_pintbl mba6_pincfgs[] = {
+ { 0x10, 0x032120f0 }, /* HP */
+ { 0x11, 0x500000f0 },
+ { 0x12, 0x90100010 }, /* Speaker */
+ { 0x13, 0x500000f0 },
+ { 0x14, 0x500000f0 },
+ { 0x15, 0x770000f0 },
+ { 0x16, 0x770000f0 },
+ { 0x17, 0x430000f0 },
+ { 0x18, 0x43ab9030 }, /* Mic */
+ { 0x19, 0x770000f0 },
+ { 0x1a, 0x770000f0 },
+ { 0x1b, 0x770000f0 },
+ { 0x1c, 0x90a00090 },
+ { 0x1d, 0x500000f0 },
+ { 0x1e, 0x500000f0 },
+ { 0x1f, 0x500000f0 },
+ { 0x20, 0x500000f0 },
+ { 0x21, 0x430000f0 },
+ { 0x22, 0x430000f0 },
+ {} /* terminator */
+};
+
+static void cs420x_fixup_gpio_13(struct hda_codec *codec,
+ const struct hda_fixup *fix, int action)
{
- char tmp[32];
- struct snd_kcontrol_new knew =
- HDA_CODEC_VOLUME_IDX(tmp, index, 0, 0, HDA_OUTPUT);
- knew.private_value = pval;
- snprintf(tmp, sizeof(tmp), "%s %s Volume", name, dir_sfx[dir]);
- *kctlp = snd_ctl_new1(&knew, codec);
- (*kctlp)->id.subdevice = HDA_SUBDEV_AMP_FLAG;
- return snd_hda_ctl_add(codec, 0, *kctlp);
+ if (action == HDA_FIXUP_ACT_PRE_PROBE) {
+ struct cs_spec *spec = codec->spec;
+ spec->gpio_eapd_hp = 2; /* GPIO1 = headphones */
+ spec->gpio_eapd_speaker = 8; /* GPIO3 = speakers */
+ spec->gpio_mask = spec->gpio_dir =
+ spec->gpio_eapd_hp | spec->gpio_eapd_speaker;
+ }
}
-static void fix_volume_caps(struct hda_codec *codec, hda_nid_t dac)
+static void cs420x_fixup_gpio_23(struct hda_codec *codec,
+ const struct hda_fixup *fix, int action)
{
- unsigned int caps;
-
- /* set the upper-limit for mixer amp to 0dB */
- caps = query_amp_caps(codec, dac, HDA_OUTPUT);
- caps &= ~(0x7f << AC_AMPCAP_NUM_STEPS_SHIFT);
- caps |= ((caps >> AC_AMPCAP_OFFSET_SHIFT) & 0x7f)
- << AC_AMPCAP_NUM_STEPS_SHIFT;
- snd_hda_override_amp_caps(codec, dac, HDA_OUTPUT, caps);
+ if (action == HDA_FIXUP_ACT_PRE_PROBE) {
+ struct cs_spec *spec = codec->spec;
+ spec->gpio_eapd_hp = 4; /* GPIO2 = headphones */
+ spec->gpio_eapd_speaker = 8; /* GPIO3 = speakers */
+ spec->gpio_mask = spec->gpio_dir =
+ spec->gpio_eapd_hp | spec->gpio_eapd_speaker;
+ }
}
-static int add_vmaster(struct hda_codec *codec, hda_nid_t dac)
+static const struct hda_fixup cs420x_fixups[] = {
+ [CS420X_MBP53] = {
+ .type = HDA_FIXUP_PINS,
+ .v.pins = mbp53_pincfgs,
+ .chained = true,
+ .chain_id = CS420X_APPLE,
+ },
+ [CS420X_MBP55] = {
+ .type = HDA_FIXUP_PINS,
+ .v.pins = mbp55_pincfgs,
+ .chained = true,
+ .chain_id = CS420X_GPIO_13,
+ },
+ [CS420X_IMAC27] = {
+ .type = HDA_FIXUP_PINS,
+ .v.pins = imac27_pincfgs,
+ .chained = true,
+ .chain_id = CS420X_GPIO_13,
+ },
+ [CS420X_GPIO_13] = {
+ .type = HDA_FIXUP_FUNC,
+ .v.func = cs420x_fixup_gpio_13,
+ },
+ [CS420X_GPIO_23] = {
+ .type = HDA_FIXUP_FUNC,
+ .v.func = cs420x_fixup_gpio_23,
+ },
+ [CS420X_MBP101] = {
+ .type = HDA_FIXUP_PINS,
+ .v.pins = mbp101_pincfgs,
+ .chained = true,
+ .chain_id = CS420X_GPIO_13,
+ },
+ [CS420X_MBP81] = {
+ .type = HDA_FIXUP_VERBS,
+ .v.verbs = (const struct hda_verb[]) {
+ /* internal mic ADC2: right only, single ended */
+ {0x11, AC_VERB_SET_COEF_INDEX, IDX_ADC_CFG},
+ {0x11, AC_VERB_SET_PROC_COEF, 0x102a},
+ {}
+ },
+ .chained = true,
+ .chain_id = CS420X_GPIO_13,
+ },
+ [CS420X_MBA42] = {
+ .type = HDA_FIXUP_PINS,
+ .v.pins = mba42_pincfgs,
+ .chained = true,
+ .chain_id = CS420X_GPIO_13,
+ },
+};
+
+static struct cs_spec *cs_alloc_spec(struct hda_codec *codec, int vendor_nid)
{
- struct cs_spec *spec = codec->spec;
- unsigned int tlv[4];
- int err;
+ struct cs_spec *spec;
- spec->vmaster_sw =
- snd_ctl_make_virtual_master("Master Playback Switch", NULL);
- err = snd_hda_ctl_add(codec, dac, spec->vmaster_sw);
- if (err < 0)
- return err;
+ spec = kzalloc(sizeof(*spec), GFP_KERNEL);
+ if (!spec)
+ return NULL;
+ codec->spec = spec;
+ spec->vendor_nid = vendor_nid;
+ snd_hda_gen_spec_init(&spec->gen);
- snd_hda_set_vmaster_tlv(codec, dac, HDA_OUTPUT, tlv);
- spec->vmaster_vol =
- snd_ctl_make_virtual_master("Master Playback Volume", tlv);
- err = snd_hda_ctl_add(codec, dac, spec->vmaster_vol);
- if (err < 0)
- return err;
- return 0;
+ return spec;
}
-static int add_output(struct hda_codec *codec, hda_nid_t dac, int idx,
- int num_ctls, int type)
+static int patch_cs420x(struct hda_codec *codec)
{
- struct cs_spec *spec = codec->spec;
- const char *name;
- int err, index;
- struct snd_kcontrol *kctl;
- static char *speakers[] = {
- "Front Speaker", "Surround Speaker", "Bass Speaker"
- };
- static char *line_outs[] = {
- "Front Line-Out", "Surround Line-Out", "Bass Line-Out"
- };
+ struct cs_spec *spec;
+ int err;
- fix_volume_caps(codec, dac);
- if (!spec->vmaster_sw) {
- err = add_vmaster(codec, dac);
- if (err < 0)
- return err;
- }
+ spec = cs_alloc_spec(codec, CS420X_VENDOR_NID);
+ if (!spec)
+ return -ENOMEM;
- index = 0;
- switch (type) {
- case AUTO_PIN_HP_OUT:
- name = "Headphone";
- index = idx;
- break;
- case AUTO_PIN_SPEAKER_OUT:
- if (num_ctls > 1)
- name = speakers[idx];
- else
- name = "Speaker";
- break;
- default:
- if (num_ctls > 1)
- name = line_outs[idx];
- else
- name = "Line-Out";
- break;
- }
+ spec->gen.automute_hook = cs_automute;
- err = add_mute(codec, name, index,
- HDA_COMPOSE_AMP_VAL(dac, 3, 0, HDA_OUTPUT), 0, &kctl);
- if (err < 0)
- return err;
- err = snd_ctl_add_slave(spec->vmaster_sw, kctl);
- if (err < 0)
- return err;
+ snd_hda_pick_fixup(codec, cs420x_models, cs420x_fixup_tbl,
+ cs420x_fixups);
+ snd_hda_apply_fixup(codec, HDA_FIXUP_ACT_PRE_PROBE);
- err = add_volume(codec, name, index,
- HDA_COMPOSE_AMP_VAL(dac, 3, 0, HDA_OUTPUT), 0, &kctl);
- if (err < 0)
- return err;
- err = snd_ctl_add_slave(spec->vmaster_vol, kctl);
+ err = cs_parse_auto_config(codec);
if (err < 0)
- return err;
+ goto error;
- return 0;
-}
+ codec->patch_ops = cs_patch_ops;
-static int build_output(struct hda_codec *codec)
-{
- struct cs_spec *spec = codec->spec;
- struct auto_pin_cfg *cfg = &spec->autocfg;
- int i, err;
+ snd_hda_apply_fixup(codec, HDA_FIXUP_ACT_PROBE);
- for (i = 0; i < cfg->line_outs; i++) {
- err = add_output(codec, get_dac(codec, cfg->line_out_pins[i]),
- i, cfg->line_outs, cfg->line_out_type);
- if (err < 0)
- return err;
- }
- for (i = 0; i < cfg->hp_outs; i++) {
- err = add_output(codec, get_dac(codec, cfg->hp_pins[i]),
- i, cfg->hp_outs, AUTO_PIN_HP_OUT);
- if (err < 0)
- return err;
- }
- for (i = 0; i < cfg->speaker_outs; i++) {
- err = add_output(codec, get_dac(codec, cfg->speaker_pins[i]),
- i, cfg->speaker_outs, AUTO_PIN_SPEAKER_OUT);
- if (err < 0)
- return err;
- }
return 0;
+
+ error:
+ cs_free(codec);
+ return err;
}
/*
+ * CS4208 support:
+ * Its layout is no longer compatible with CS4206/CS4207
*/
+enum {
+ CS4208_MAC_AUTO,
+ CS4208_MBA6,
+ CS4208_MBP11,
+ CS4208_GPIO0,
+};
+
+static const struct hda_model_fixup cs4208_models[] = {
+ { .id = CS4208_GPIO0, .name = "gpio0" },
+ { .id = CS4208_MBA6, .name = "mba6" },
+ { .id = CS4208_MBP11, .name = "mbp11" },
+ {}
+};
+
+static const struct snd_pci_quirk cs4208_fixup_tbl[] = {
+ SND_PCI_QUIRK_VENDOR(0x106b, "Apple", CS4208_MAC_AUTO),
+ {} /* terminator */
+};
-static struct snd_kcontrol_new cs_capture_ctls[] = {
- HDA_BIND_SW("Capture Switch", 0),
- HDA_BIND_VOL("Capture Volume", 0),
+/* codec SSID matching */
+static const struct snd_pci_quirk cs4208_mac_fixup_tbl[] = {
+ SND_PCI_QUIRK(0x106b, 0x5e00, "MacBookPro 11,2", CS4208_MBP11),
+ SND_PCI_QUIRK(0x106b, 0x7100, "MacBookAir 6,1", CS4208_MBA6),
+ SND_PCI_QUIRK(0x106b, 0x7200, "MacBookAir 6,2", CS4208_MBA6),
+ {} /* terminator */
};
-static int change_cur_input(struct hda_codec *codec, unsigned int idx,
- int force)
+static void cs4208_fixup_gpio0(struct hda_codec *codec,
+ const struct hda_fixup *fix, int action)
{
- struct cs_spec *spec = codec->spec;
-
- if (spec->cur_input == idx && !force)
- return 0;
- if (spec->cur_adc && spec->cur_adc != spec->adc_nid[idx]) {
- /* stream is running, let's swap the current ADC */
- __snd_hda_codec_cleanup_stream(codec, spec->cur_adc, 1);
- spec->cur_adc = spec->adc_nid[idx];
- snd_hda_codec_setup_stream(codec, spec->cur_adc,
- spec->cur_adc_stream_tag, 0,
- spec->cur_adc_format);
+ if (action == HDA_FIXUP_ACT_PRE_PROBE) {
+ struct cs_spec *spec = codec->spec;
+ spec->gpio_eapd_hp = 0;
+ spec->gpio_eapd_speaker = 1;
+ spec->gpio_mask = spec->gpio_dir =
+ spec->gpio_eapd_hp | spec->gpio_eapd_speaker;
}
- snd_hda_codec_write(codec, spec->cur_adc, 0,
- AC_VERB_SET_CONNECT_SEL,
- spec->adc_idx[idx]);
- spec->cur_input = idx;
- return 1;
}
-static int cs_capture_source_info(struct snd_kcontrol *kcontrol,
- struct snd_ctl_elem_info *uinfo)
-{
- struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
- struct cs_spec *spec = codec->spec;
- struct auto_pin_cfg *cfg = &spec->autocfg;
- unsigned int idx;
+static const struct hda_fixup cs4208_fixups[];
- uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED;
- uinfo->count = 1;
- uinfo->value.enumerated.items = spec->num_inputs;
- if (uinfo->value.enumerated.item >= spec->num_inputs)
- uinfo->value.enumerated.item = spec->num_inputs - 1;
- idx = spec->input_idx[uinfo->value.enumerated.item];
- strcpy(uinfo->value.enumerated.name,
- hda_get_input_pin_label(codec, cfg->inputs[idx].pin, 1));
- return 0;
+/* remap the fixup from codec SSID and apply it */
+static void cs4208_fixup_mac(struct hda_codec *codec,
+ const struct hda_fixup *fix, int action)
+{
+ if (action != HDA_FIXUP_ACT_PRE_PROBE)
+ return;
+ snd_hda_pick_fixup(codec, NULL, cs4208_mac_fixup_tbl, cs4208_fixups);
+ if (codec->fixup_id < 0 || codec->fixup_id == CS4208_MAC_AUTO)
+ codec->fixup_id = CS4208_GPIO0; /* default fixup */
+ snd_hda_apply_fixup(codec, action);
}
-static int cs_capture_source_get(struct snd_kcontrol *kcontrol,
- struct snd_ctl_elem_value *ucontrol)
+static int cs4208_spdif_sw_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
{
struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
struct cs_spec *spec = codec->spec;
- ucontrol->value.enumerated.item[0] = spec->capsrc_idx[spec->cur_input];
- return 0;
+ hda_nid_t pin = spec->gen.autocfg.dig_out_pins[0];
+ int pinctl = ucontrol->value.integer.value[0] ? PIN_OUT : 0;
+
+ snd_hda_set_pin_ctl_cache(codec, pin, pinctl);
+ return spec->spdif_sw_put(kcontrol, ucontrol);
}
-static int cs_capture_source_put(struct snd_kcontrol *kcontrol,
- struct snd_ctl_elem_value *ucontrol)
+/* hook the SPDIF switch */
+static void cs4208_fixup_spdif_switch(struct hda_codec *codec,
+ const struct hda_fixup *fix, int action)
{
- struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
- struct cs_spec *spec = codec->spec;
- unsigned int idx = ucontrol->value.enumerated.item[0];
+ if (action == HDA_FIXUP_ACT_BUILD) {
+ struct cs_spec *spec = codec->spec;
+ struct snd_kcontrol *kctl;
- if (idx >= spec->num_inputs)
- return -EINVAL;
- idx = spec->input_idx[idx];
- return change_cur_input(codec, idx, 0);
+ if (!spec->gen.autocfg.dig_out_pins[0])
+ return;
+ kctl = snd_hda_find_mixer_ctl(codec, "IEC958 Playback Switch");
+ if (!kctl)
+ return;
+ spec->spdif_sw_put = kctl->put;
+ kctl->put = cs4208_spdif_sw_put;
+ }
}
-static struct snd_kcontrol_new cs_capture_source = {
- .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
- .name = "Capture Source",
- .access = SNDRV_CTL_ELEM_ACCESS_READWRITE,
- .info = cs_capture_source_info,
- .get = cs_capture_source_get,
- .put = cs_capture_source_put,
+static const struct hda_fixup cs4208_fixups[] = {
+ [CS4208_MBA6] = {
+ .type = HDA_FIXUP_PINS,
+ .v.pins = mba6_pincfgs,
+ .chained = true,
+ .chain_id = CS4208_GPIO0,
+ },
+ [CS4208_MBP11] = {
+ .type = HDA_FIXUP_FUNC,
+ .v.func = cs4208_fixup_spdif_switch,
+ .chained = true,
+ .chain_id = CS4208_GPIO0,
+ },
+ [CS4208_GPIO0] = {
+ .type = HDA_FIXUP_FUNC,
+ .v.func = cs4208_fixup_gpio0,
+ },
+ [CS4208_MAC_AUTO] = {
+ .type = HDA_FIXUP_FUNC,
+ .v.func = cs4208_fixup_mac,
+ },
};
-static struct hda_bind_ctls *make_bind_capture(struct hda_codec *codec,
- struct hda_ctl_ops *ops)
+/* correct the 0dB offset of input pins */
+static void cs4208_fix_amp_caps(struct hda_codec *codec, hda_nid_t adc)
{
- struct cs_spec *spec = codec->spec;
- struct hda_bind_ctls *bind;
- int i, n;
+ unsigned int caps;
- bind = kzalloc(sizeof(*bind) + sizeof(long) * (spec->num_inputs + 1),
- GFP_KERNEL);
- if (!bind)
- return NULL;
- bind->ops = ops;
- n = 0;
- for (i = 0; i < AUTO_PIN_LAST; i++) {
- if (!spec->adc_nid[i])
- continue;
- bind->values[n++] =
- HDA_COMPOSE_AMP_VAL(spec->adc_nid[i], 3,
- spec->adc_idx[i], HDA_INPUT);
- }
- return bind;
+ caps = query_amp_caps(codec, adc, HDA_INPUT);
+ caps &= ~(AC_AMPCAP_OFFSET);
+ caps |= 0x02;
+ snd_hda_override_amp_caps(codec, adc, HDA_INPUT, caps);
}
-/* add a (input-boost) volume control to the given input pin */
-static int add_input_volume_control(struct hda_codec *codec,
- struct auto_pin_cfg *cfg,
- int item)
+static int patch_cs4208(struct hda_codec *codec)
{
- hda_nid_t pin = cfg->inputs[item].pin;
- u32 caps;
- const char *label;
- struct snd_kcontrol *kctl;
-
- if (!(get_wcaps(codec, pin) & AC_WCAP_IN_AMP))
- return 0;
- caps = query_amp_caps(codec, pin, HDA_INPUT);
- caps = (caps & AC_AMPCAP_NUM_STEPS) >> AC_AMPCAP_NUM_STEPS_SHIFT;
- if (caps <= 1)
- return 0;
- label = hda_get_autocfg_input_label(codec, cfg, item);
- return add_volume(codec, label, 0,
- HDA_COMPOSE_AMP_VAL(pin, 3, 0, HDA_INPUT), 1, &kctl);
-}
+ struct cs_spec *spec;
+ int err;
-static int build_input(struct hda_codec *codec)
-{
- struct cs_spec *spec = codec->spec;
- int i, err;
+ spec = cs_alloc_spec(codec, CS4208_VENDOR_NID);
+ if (!spec)
+ return -ENOMEM;
- if (!spec->num_inputs)
- return 0;
+ spec->gen.automute_hook = cs_automute;
+ /* exclude NID 0x10 (HP) from output volumes due to different steps */
+ spec->gen.out_vol_mask = 1ULL << 0x10;
- /* make bind-capture */
- spec->capture_bind[0] = make_bind_capture(codec, &snd_hda_bind_sw);
- spec->capture_bind[1] = make_bind_capture(codec, &snd_hda_bind_vol);
- for (i = 0; i < 2; i++) {
- struct snd_kcontrol *kctl;
- int n;
- if (!spec->capture_bind[i])
- return -ENOMEM;
- kctl = snd_ctl_new1(&cs_capture_ctls[i], codec);
- if (!kctl)
- return -ENOMEM;
- kctl->private_value = (long)spec->capture_bind[i];
- err = snd_hda_ctl_add(codec, 0, kctl);
- if (err < 0)
- return err;
- for (n = 0; n < AUTO_PIN_LAST; n++) {
- if (!spec->adc_nid[n])
- continue;
- err = snd_hda_add_nid(codec, kctl, 0, spec->adc_nid[n]);
- if (err < 0)
- return err;
- }
- }
-
- if (spec->num_inputs > 1 && !spec->mic_detect) {
- err = snd_hda_ctl_add(codec, 0,
- snd_ctl_new1(&cs_capture_source, codec));
- if (err < 0)
- return err;
- }
+ snd_hda_pick_fixup(codec, cs4208_models, cs4208_fixup_tbl,
+ cs4208_fixups);
+ snd_hda_apply_fixup(codec, HDA_FIXUP_ACT_PRE_PROBE);
- for (i = 0; i < spec->num_inputs; i++) {
- err = add_input_volume_control(codec, &spec->autocfg, i);
- if (err < 0)
- return err;
- }
+ snd_hda_override_wcaps(codec, 0x18,
+ get_wcaps(codec, 0x18) | AC_WCAP_STEREO);
+ cs4208_fix_amp_caps(codec, 0x18);
+ cs4208_fix_amp_caps(codec, 0x1b);
+ cs4208_fix_amp_caps(codec, 0x1c);
+
+ err = cs_parse_auto_config(codec);
+ if (err < 0)
+ goto error;
+
+ codec->patch_ops = cs_patch_ops;
+
+ snd_hda_apply_fixup(codec, HDA_FIXUP_ACT_PROBE);
return 0;
+
+ error:
+ cs_free(codec);
+ return err;
}
/*
- */
+ * Cirrus Logic CS4210
+ *
+ * 1 DAC => HP(sense) / Speakers,
+ * 1 ADC <= LineIn(sense) / MicIn / DMicIn,
+ * 1 SPDIF OUT => SPDIF Trasmitter(sense)
+*/
+
+/* CS4210 board names */
+static const struct hda_model_fixup cs421x_models[] = {
+ { .id = CS421X_CDB4210, .name = "cdb4210" },
+ { .id = CS421X_STUMPY, .name = "stumpy" },
+ {}
+};
-static int build_digital_output(struct hda_codec *codec)
-{
- struct cs_spec *spec = codec->spec;
- int err;
+static const struct snd_pci_quirk cs421x_fixup_tbl[] = {
+ /* Test Intel board + CDB2410 */
+ SND_PCI_QUIRK(0x8086, 0x5001, "DP45SG/CDB4210", CS421X_CDB4210),
+ {} /* terminator */
+};
- if (!spec->multiout.dig_out_nid)
- return 0;
+/* CS4210 board pinconfigs */
+/* Default CS4210 (CDB4210)*/
+static const struct hda_pintbl cdb4210_pincfgs[] = {
+ { 0x05, 0x0321401f },
+ { 0x06, 0x90170010 },
+ { 0x07, 0x03813031 },
+ { 0x08, 0xb7a70037 },
+ { 0x09, 0xb7a6003e },
+ { 0x0a, 0x034510f0 },
+ {} /* terminator */
+};
- err = snd_hda_create_spdif_out_ctls(codec, spec->multiout.dig_out_nid);
- if (err < 0)
- return err;
- err = snd_hda_create_spdif_share_sw(codec, &spec->multiout);
- if (err < 0)
- return err;
- return 0;
-}
+/* Stumpy ChromeBox */
+static const struct hda_pintbl stumpy_pincfgs[] = {
+ { 0x05, 0x022120f0 },
+ { 0x06, 0x901700f0 },
+ { 0x07, 0x02a120f0 },
+ { 0x08, 0x77a70037 },
+ { 0x09, 0x77a6003e },
+ { 0x0a, 0x434510f0 },
+ {} /* terminator */
+};
-static int build_digital_input(struct hda_codec *codec)
+/* Setup GPIO/SENSE for each board (if used) */
+static void cs421x_fixup_sense_b(struct hda_codec *codec,
+ const struct hda_fixup *fix, int action)
{
struct cs_spec *spec = codec->spec;
- if (spec->dig_in)
- return snd_hda_create_spdif_in_ctls(codec, spec->dig_in);
- return 0;
+ if (action == HDA_FIXUP_ACT_PRE_PROBE)
+ spec->sense_b = 1;
}
-/*
- * auto-mute and auto-mic switching
+static const struct hda_fixup cs421x_fixups[] = {
+ [CS421X_CDB4210] = {
+ .type = HDA_FIXUP_PINS,
+ .v.pins = cdb4210_pincfgs,
+ .chained = true,
+ .chain_id = CS421X_SENSE_B,
+ },
+ [CS421X_SENSE_B] = {
+ .type = HDA_FIXUP_FUNC,
+ .v.func = cs421x_fixup_sense_b,
+ },
+ [CS421X_STUMPY] = {
+ .type = HDA_FIXUP_PINS,
+ .v.pins = stumpy_pincfgs,
+ },
+};
+
+static const struct hda_verb cs421x_coef_init_verbs[] = {
+ {0x0B, AC_VERB_SET_PROC_STATE, 1},
+ {0x0B, AC_VERB_SET_COEF_INDEX, CS421X_IDX_DEV_CFG},
+ /*
+ Disable Coefficient Index Auto-Increment(DAI)=1,
+ PDREF=0
+ */
+ {0x0B, AC_VERB_SET_PROC_COEF, 0x0001 },
+
+ {0x0B, AC_VERB_SET_COEF_INDEX, CS421X_IDX_ADC_CFG},
+ /* ADC SZCMode = Digital Soft Ramp */
+ {0x0B, AC_VERB_SET_PROC_COEF, 0x0002 },
+
+ {0x0B, AC_VERB_SET_COEF_INDEX, CS421X_IDX_DAC_CFG},
+ {0x0B, AC_VERB_SET_PROC_COEF,
+ (0x0002 /* DAC SZCMode = Digital Soft Ramp */
+ | 0x0004 /* Mute DAC on FIFO error */
+ | 0x0008 /* Enable DAC High Pass Filter */
+ )},
+ {} /* terminator */
+};
+
+/* Errata: CS4210 rev A1 Silicon
+ *
+ * http://www.cirrus.com/en/pubs/errata/
+ *
+ * Description:
+ * 1. Performance degredation is present in the ADC.
+ * 2. Speaker output is not completely muted upon HP detect.
+ * 3. Noise is present when clipping occurs on the amplified
+ * speaker outputs.
+ *
+ * Workaround:
+ * The following verb sequence written to the registers during
+ * initialization will correct the issues listed above.
*/
-static void cs_automute(struct hda_codec *codec)
-{
- struct cs_spec *spec = codec->spec;
- struct auto_pin_cfg *cfg = &spec->autocfg;
- unsigned int caps, hp_present;
- hda_nid_t nid;
- int i;
+static const struct hda_verb cs421x_coef_init_verbs_A1_silicon_fixes[] = {
+ {0x0B, AC_VERB_SET_PROC_STATE, 0x01}, /* VPW: processing on */
- hp_present = 0;
- for (i = 0; i < cfg->hp_outs; i++) {
- nid = cfg->hp_pins[i];
- caps = snd_hda_query_pin_caps(codec, nid);
- if (!(caps & AC_PINCAP_PRES_DETECT))
- continue;
- hp_present = snd_hda_jack_detect(codec, nid);
- if (hp_present)
- break;
- }
- for (i = 0; i < cfg->speaker_outs; i++) {
- nid = cfg->speaker_pins[i];
- snd_hda_codec_write(codec, nid, 0,
- AC_VERB_SET_PIN_WIDGET_CONTROL,
- hp_present ? 0 : PIN_OUT);
- }
- if (spec->board_config == CS420X_MBP53 ||
- spec->board_config == CS420X_MBP55 ||
- spec->board_config == CS420X_IMAC27) {
- unsigned int gpio = hp_present ? 0x02 : 0x08;
- snd_hda_codec_write(codec, 0x01, 0,
- AC_VERB_SET_GPIO_DATA, gpio);
- }
-}
+ {0x0B, AC_VERB_SET_COEF_INDEX, 0x0006},
+ {0x0B, AC_VERB_SET_PROC_COEF, 0x9999}, /* Test mode: on */
+
+ {0x0B, AC_VERB_SET_COEF_INDEX, 0x000A},
+ {0x0B, AC_VERB_SET_PROC_COEF, 0x14CB}, /* Chop double */
+
+ {0x0B, AC_VERB_SET_COEF_INDEX, 0x0011},
+ {0x0B, AC_VERB_SET_PROC_COEF, 0xA2D0}, /* Increase ADC current */
-static void cs_automic(struct hda_codec *codec)
+ {0x0B, AC_VERB_SET_COEF_INDEX, 0x001A},
+ {0x0B, AC_VERB_SET_PROC_COEF, 0x02A9}, /* Mute speaker */
+
+ {0x0B, AC_VERB_SET_COEF_INDEX, 0x001B},
+ {0x0B, AC_VERB_SET_PROC_COEF, 0X1006}, /* Remove noise */
+
+ {} /* terminator */
+};
+
+/* Speaker Amp Gain is controlled by the vendor widget's coef 4 */
+static const DECLARE_TLV_DB_SCALE(cs421x_speaker_boost_db_scale, 900, 300, 0);
+
+static int cs421x_boost_vol_info(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_info *uinfo)
{
- struct cs_spec *spec = codec->spec;
- struct auto_pin_cfg *cfg = &spec->autocfg;
- hda_nid_t nid;
- unsigned int present;
-
- nid = cfg->inputs[spec->automic_idx].pin;
- present = snd_hda_jack_detect(codec, nid);
- if (present)
- change_cur_input(codec, spec->automic_idx, 0);
- else
- change_cur_input(codec, !spec->automic_idx, 0);
+ uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
+ uinfo->count = 1;
+ uinfo->value.integer.min = 0;
+ uinfo->value.integer.max = 3;
+ return 0;
}
-/*
- */
+static int cs421x_boost_vol_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
-static void init_output(struct hda_codec *codec)
+ ucontrol->value.integer.value[0] =
+ cs_vendor_coef_get(codec, CS421X_IDX_SPK_CTL) & 0x0003;
+ return 0;
+}
+
+static int cs421x_boost_vol_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
{
- struct cs_spec *spec = codec->spec;
- struct auto_pin_cfg *cfg = &spec->autocfg;
- int i;
+ struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
- /* mute first */
- for (i = 0; i < spec->multiout.num_dacs; i++)
- snd_hda_codec_write(codec, spec->multiout.dac_nids[i], 0,
- AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE);
- if (spec->multiout.hp_nid)
- snd_hda_codec_write(codec, spec->multiout.hp_nid, 0,
- AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE);
- for (i = 0; i < ARRAY_SIZE(spec->multiout.extra_out_nid); i++) {
- if (!spec->multiout.extra_out_nid[i])
- break;
- snd_hda_codec_write(codec, spec->multiout.extra_out_nid[i], 0,
- AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE);
- }
+ unsigned int vol = ucontrol->value.integer.value[0];
+ unsigned int coef =
+ cs_vendor_coef_get(codec, CS421X_IDX_SPK_CTL);
+ unsigned int original_coef = coef;
- /* set appropriate pin controls */
- for (i = 0; i < cfg->line_outs; i++)
- snd_hda_codec_write(codec, cfg->line_out_pins[i], 0,
- AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT);
- for (i = 0; i < cfg->hp_outs; i++) {
- hda_nid_t nid = cfg->hp_pins[i];
- snd_hda_codec_write(codec, nid, 0,
- AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP);
- if (!cfg->speaker_outs)
- continue;
- if (get_wcaps(codec, nid) & AC_WCAP_UNSOL_CAP) {
- snd_hda_codec_write(codec, nid, 0,
- AC_VERB_SET_UNSOLICITED_ENABLE,
- AC_USRSP_EN | HP_EVENT);
- spec->hp_detect = 1;
- }
+ coef &= ~0x0003;
+ coef |= (vol & 0x0003);
+ if (original_coef == coef)
+ return 0;
+ else {
+ cs_vendor_coef_set(codec, CS421X_IDX_SPK_CTL, coef);
+ return 1;
}
- for (i = 0; i < cfg->speaker_outs; i++)
- snd_hda_codec_write(codec, cfg->speaker_pins[i], 0,
- AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT);
- if (spec->hp_detect)
- cs_automute(codec);
}
-static void init_input(struct hda_codec *codec)
+static const struct snd_kcontrol_new cs421x_speaker_boost_ctl = {
+
+ .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+ .access = (SNDRV_CTL_ELEM_ACCESS_READWRITE |
+ SNDRV_CTL_ELEM_ACCESS_TLV_READ),
+ .name = "Speaker Boost Playback Volume",
+ .info = cs421x_boost_vol_info,
+ .get = cs421x_boost_vol_get,
+ .put = cs421x_boost_vol_put,
+ .tlv = { .p = cs421x_speaker_boost_db_scale },
+};
+
+static void cs4210_pinmux_init(struct hda_codec *codec)
{
struct cs_spec *spec = codec->spec;
- struct auto_pin_cfg *cfg = &spec->autocfg;
- unsigned int coef;
- int i;
+ unsigned int def_conf, coef;
- for (i = 0; i < cfg->num_inputs; i++) {
- unsigned int ctl;
- hda_nid_t pin = cfg->inputs[i].pin;
- if (!spec->adc_nid[i])
- continue;
- /* set appropriate pin control and mute first */
- ctl = PIN_IN;
- if (cfg->inputs[i].type == AUTO_PIN_MIC) {
- unsigned int caps = snd_hda_query_pin_caps(codec, pin);
- caps >>= AC_PINCAP_VREF_SHIFT;
- if (caps & AC_PINCAP_VREF_80)
- ctl = PIN_VREF80;
- }
- snd_hda_codec_write(codec, pin, 0,
- AC_VERB_SET_PIN_WIDGET_CONTROL, ctl);
- snd_hda_codec_write(codec, spec->adc_nid[i], 0,
- AC_VERB_SET_AMP_GAIN_MUTE,
- AMP_IN_MUTE(spec->adc_idx[i]));
- if (spec->mic_detect && spec->automic_idx == i)
- snd_hda_codec_write(codec, pin, 0,
- AC_VERB_SET_UNSOLICITED_ENABLE,
- AC_USRSP_EN | MIC_EVENT);
- }
- change_cur_input(codec, spec->cur_input, 1);
- if (spec->mic_detect)
- cs_automic(codec);
-
- coef = 0x000a; /* ADC1/2 - Digital and Analog Soft Ramp */
- if (is_active_pin(codec, CS_DMIC2_PIN_NID))
- coef |= 0x0500; /* DMIC2 enable 2 channels, disable GPIO1 */
- if (is_active_pin(codec, CS_DMIC1_PIN_NID))
- coef |= 0x1800; /* DMIC1 enable 2 channels, disable GPIO0
- * No effect if SPDIF_OUT2 is selected in
- * IDX_SPDIF_CTL.
- */
- cs_vendor_coef_set(codec, IDX_ADC_CFG, coef);
-}
+ /* GPIO, DMIC_SCL, DMIC_SDA and SENSE_B are multiplexed */
+ coef = cs_vendor_coef_get(codec, CS421X_IDX_DEV_CFG);
-static struct hda_verb cs_coef_init_verbs[] = {
- {0x11, AC_VERB_SET_PROC_STATE, 1},
- {0x11, AC_VERB_SET_COEF_INDEX, IDX_DAC_CFG},
- {0x11, AC_VERB_SET_PROC_COEF,
- (0x002a /* DAC1/2/3 SZCMode Soft Ramp */
- | 0x0040 /* Mute DACs on FIFO error */
- | 0x1000 /* Enable DACs High Pass Filter */
- | 0x0400 /* Disable Coefficient Auto increment */
- )},
- /* Beep */
- {0x11, AC_VERB_SET_COEF_INDEX, IDX_DAC_CFG},
- {0x11, AC_VERB_SET_PROC_COEF, 0x0007}, /* Enable Beep thru DAC1/2/3 */
+ if (spec->gpio_mask)
+ coef |= 0x0008; /* B1,B2 are GPIOs */
+ else
+ coef &= ~0x0008;
- {} /* terminator */
-};
+ if (spec->sense_b)
+ coef |= 0x0010; /* B2 is SENSE_B, not inverted */
+ else
+ coef &= ~0x0010;
-/* Errata: CS4207 rev C0/C1/C2 Silicon
- *
- * http://www.cirrus.com/en/pubs/errata/ER880C3.pdf
- *
- * 6. At high temperature (TA > +85°C), the digital supply current (IVD)
- * may be excessive (up to an additional 200 μA), which is most easily
- * observed while the part is being held in reset (RESET# active low).
- *
- * Root Cause: At initial powerup of the device, the logic that drives
- * the clock and write enable to the S/PDIF SRC RAMs is not properly
- * initialized.
- * Certain random patterns will cause a steady leakage current in those
- * RAM cells. The issue will resolve once the SRCs are used (turned on).
- *
- * Workaround: The following verb sequence briefly turns on the S/PDIF SRC
- * blocks, which will alleviate the issue.
- */
+ cs_vendor_coef_set(codec, CS421X_IDX_DEV_CFG, coef);
-static struct hda_verb cs_errata_init_verbs[] = {
- {0x01, AC_VERB_SET_POWER_STATE, 0x00}, /* AFG: D0 */
- {0x11, AC_VERB_SET_PROC_STATE, 0x01}, /* VPW: processing on */
+ if ((spec->gpio_mask || spec->sense_b) &&
+ is_active_pin(codec, CS421X_DMIC_PIN_NID)) {
- {0x11, AC_VERB_SET_COEF_INDEX, 0x0008},
- {0x11, AC_VERB_SET_PROC_COEF, 0x9999},
- {0x11, AC_VERB_SET_COEF_INDEX, 0x0017},
- {0x11, AC_VERB_SET_PROC_COEF, 0xa412},
- {0x11, AC_VERB_SET_COEF_INDEX, 0x0001},
- {0x11, AC_VERB_SET_PROC_COEF, 0x0009},
+ /*
+ GPIO or SENSE_B forced - disconnect the DMIC pin.
+ */
+ def_conf = snd_hda_codec_get_pincfg(codec, CS421X_DMIC_PIN_NID);
+ def_conf &= ~AC_DEFCFG_PORT_CONN;
+ def_conf |= (AC_JACK_PORT_NONE << AC_DEFCFG_PORT_CONN_SHIFT);
+ snd_hda_codec_set_pincfg(codec, CS421X_DMIC_PIN_NID, def_conf);
+ }
+}
- {0x07, AC_VERB_SET_POWER_STATE, 0x00}, /* S/PDIF Rx: D0 */
- {0x08, AC_VERB_SET_POWER_STATE, 0x00}, /* S/PDIF Tx: D0 */
+static void cs4210_spdif_automute(struct hda_codec *codec,
+ struct hda_jack_tbl *tbl)
+{
+ struct cs_spec *spec = codec->spec;
+ bool spdif_present = false;
+ hda_nid_t spdif_pin = spec->gen.autocfg.dig_out_pins[0];
- {0x11, AC_VERB_SET_COEF_INDEX, 0x0017},
- {0x11, AC_VERB_SET_PROC_COEF, 0x2412},
- {0x11, AC_VERB_SET_COEF_INDEX, 0x0008},
- {0x11, AC_VERB_SET_PROC_COEF, 0x0000},
- {0x11, AC_VERB_SET_COEF_INDEX, 0x0001},
- {0x11, AC_VERB_SET_PROC_COEF, 0x0008},
- {0x11, AC_VERB_SET_PROC_STATE, 0x00},
+ /* detect on spdif is specific to CS4210 */
+ if (!spec->spdif_detect ||
+ spec->vendor_nid != CS4210_VENDOR_NID)
+ return;
-#if 0 /* Don't to set to D3 as we are in power-up sequence */
- {0x07, AC_VERB_SET_POWER_STATE, 0x03}, /* S/PDIF Rx: D3 */
- {0x08, AC_VERB_SET_POWER_STATE, 0x03}, /* S/PDIF Tx: D3 */
- /*{0x01, AC_VERB_SET_POWER_STATE, 0x03},*/ /* AFG: D3 This is already handled */
-#endif
+ spdif_present = snd_hda_jack_detect(codec, spdif_pin);
+ if (spdif_present == spec->spdif_present)
+ return;
- {} /* terminator */
-};
+ spec->spdif_present = spdif_present;
+ /* SPDIF TX on/off */
+ if (spdif_present)
+ snd_hda_set_pin_ctl(codec, spdif_pin,
+ spdif_present ? PIN_OUT : 0);
-/* SPDIF setup */
-static void init_digital(struct hda_codec *codec)
+ cs_automute(codec);
+}
+
+static void parse_cs421x_digital(struct hda_codec *codec)
{
- unsigned int coef;
+ struct cs_spec *spec = codec->spec;
+ struct auto_pin_cfg *cfg = &spec->gen.autocfg;
+ int i;
- coef = 0x0002; /* SRC_MUTE soft-mute on SPDIF (if no lock) */
- coef |= 0x0008; /* Replace with mute on error */
- if (is_active_pin(codec, CS_DIG_OUT2_PIN_NID))
- coef |= 0x4000; /* RX to TX1 or TX2 Loopthru / SPDIF2
- * SPDIF_OUT2 is shared with GPIO1 and
- * DMIC_SDA2.
- */
- cs_vendor_coef_set(codec, IDX_SPDIF_CTL, coef);
+ for (i = 0; i < cfg->dig_outs; i++) {
+ hda_nid_t nid = cfg->dig_out_pins[i];
+ if (get_wcaps(codec, nid) & AC_WCAP_UNSOL_CAP) {
+ spec->spdif_detect = 1;
+ snd_hda_jack_detect_enable_callback(codec, nid,
+ SPDIF_EVENT,
+ cs4210_spdif_automute);
+ }
+ }
}
-static int cs_init(struct hda_codec *codec)
+static int cs421x_init(struct hda_codec *codec)
{
struct cs_spec *spec = codec->spec;
- /* init_verb sequence for C0/C1/C2 errata*/
- snd_hda_sequence_write(codec, cs_errata_init_verbs);
+ if (spec->vendor_nid == CS4210_VENDOR_NID) {
+ snd_hda_sequence_write(codec, cs421x_coef_init_verbs);
+ snd_hda_sequence_write(codec, cs421x_coef_init_verbs_A1_silicon_fixes);
+ cs4210_pinmux_init(codec);
+ }
- snd_hda_sequence_write(codec, cs_coef_init_verbs);
+ snd_hda_gen_init(codec);
if (spec->gpio_mask) {
snd_hda_codec_write(codec, 0x01, 0, AC_VERB_SET_GPIO_MASK,
@@ -1081,201 +1044,157 @@ static int cs_init(struct hda_codec *codec)
spec->gpio_data);
}
- init_output(codec);
- init_input(codec);
- init_digital(codec);
+ init_input_coef(codec);
+
+ cs4210_spdif_automute(codec, NULL);
+
return 0;
}
-static int cs_build_controls(struct hda_codec *codec)
+static int cs421x_build_controls(struct hda_codec *codec)
{
+ struct cs_spec *spec = codec->spec;
int err;
- err = build_output(codec);
- if (err < 0)
- return err;
- err = build_input(codec);
+ err = snd_hda_gen_build_controls(codec);
if (err < 0)
return err;
- err = build_digital_output(codec);
- if (err < 0)
- return err;
- err = build_digital_input(codec);
- if (err < 0)
- return err;
- return cs_init(codec);
-}
-static void cs_free(struct hda_codec *codec)
-{
- struct cs_spec *spec = codec->spec;
- kfree(spec->capture_bind[0]);
- kfree(spec->capture_bind[1]);
- kfree(codec->spec);
+ if (spec->gen.autocfg.speaker_outs &&
+ spec->vendor_nid == CS4210_VENDOR_NID) {
+ err = snd_hda_ctl_add(codec, 0,
+ snd_ctl_new1(&cs421x_speaker_boost_ctl, codec));
+ if (err < 0)
+ return err;
+ }
+ return 0;
}
-static void cs_unsol_event(struct hda_codec *codec, unsigned int res)
+static void fix_volume_caps(struct hda_codec *codec, hda_nid_t dac)
{
- switch ((res >> 26) & 0x7f) {
- case HP_EVENT:
- cs_automute(codec);
- break;
- case MIC_EVENT:
- cs_automic(codec);
- break;
- }
-}
+ unsigned int caps;
-static struct hda_codec_ops cs_patch_ops = {
- .build_controls = cs_build_controls,
- .build_pcms = cs_build_pcms,
- .init = cs_init,
- .free = cs_free,
- .unsol_event = cs_unsol_event,
-};
+ /* set the upper-limit for mixer amp to 0dB */
+ caps = query_amp_caps(codec, dac, HDA_OUTPUT);
+ caps &= ~(0x7f << AC_AMPCAP_NUM_STEPS_SHIFT);
+ caps |= ((caps >> AC_AMPCAP_OFFSET_SHIFT) & 0x7f)
+ << AC_AMPCAP_NUM_STEPS_SHIFT;
+ snd_hda_override_amp_caps(codec, dac, HDA_OUTPUT, caps);
+}
-static int cs_parse_auto_config(struct hda_codec *codec)
+static int cs421x_parse_auto_config(struct hda_codec *codec)
{
struct cs_spec *spec = codec->spec;
+ hda_nid_t dac = CS4210_DAC_NID;
int err;
- err = snd_hda_parse_pin_def_config(codec, &spec->autocfg, NULL);
- if (err < 0)
- return err;
+ fix_volume_caps(codec, dac);
- err = parse_output(codec);
+ err = snd_hda_parse_pin_defcfg(codec, &spec->gen.autocfg, NULL, 0);
if (err < 0)
return err;
- err = parse_input(codec);
- if (err < 0)
- return err;
- err = parse_digital_output(codec);
- if (err < 0)
- return err;
- err = parse_digital_input(codec);
+
+ err = snd_hda_gen_parse_auto_config(codec, &spec->gen.autocfg);
if (err < 0)
return err;
+
+ parse_cs421x_digital(codec);
return 0;
}
-static const char * const cs420x_models[CS420X_MODELS] = {
- [CS420X_MBP53] = "mbp53",
- [CS420X_MBP55] = "mbp55",
- [CS420X_IMAC27] = "imac27",
- [CS420X_AUTO] = "auto",
-};
+#ifdef CONFIG_PM
+/*
+ Manage PDREF, when transitioning to D3hot
+ (DAC,ADC) -> D3, PDREF=1, AFG->D3
+*/
+static int cs421x_suspend(struct hda_codec *codec)
+{
+ struct cs_spec *spec = codec->spec;
+ unsigned int coef;
+ snd_hda_shutup_pins(codec);
-static struct snd_pci_quirk cs420x_cfg_tbl[] = {
- SND_PCI_QUIRK(0x10de, 0x0ac0, "MacBookPro 5,3", CS420X_MBP53),
- SND_PCI_QUIRK(0x10de, 0x0d94, "MacBookAir 3,1(2)", CS420X_MBP55),
- SND_PCI_QUIRK(0x10de, 0xcb79, "MacBookPro 5,5", CS420X_MBP55),
- SND_PCI_QUIRK(0x10de, 0xcb89, "MacBookPro 7,1", CS420X_MBP55),
- SND_PCI_QUIRK(0x8086, 0x7270, "IMac 27 Inch", CS420X_IMAC27),
- {} /* terminator */
-};
+ snd_hda_codec_write(codec, CS4210_DAC_NID, 0,
+ AC_VERB_SET_POWER_STATE, AC_PWRST_D3);
+ snd_hda_codec_write(codec, CS4210_ADC_NID, 0,
+ AC_VERB_SET_POWER_STATE, AC_PWRST_D3);
-struct cs_pincfg {
- hda_nid_t nid;
- u32 val;
-};
+ if (spec->vendor_nid == CS4210_VENDOR_NID) {
+ coef = cs_vendor_coef_get(codec, CS421X_IDX_DEV_CFG);
+ coef |= 0x0004; /* PDREF */
+ cs_vendor_coef_set(codec, CS421X_IDX_DEV_CFG, coef);
+ }
-static struct cs_pincfg mbp53_pincfgs[] = {
- { 0x09, 0x012b4050 },
- { 0x0a, 0x90100141 },
- { 0x0b, 0x90100140 },
- { 0x0c, 0x018b3020 },
- { 0x0d, 0x90a00110 },
- { 0x0e, 0x400000f0 },
- { 0x0f, 0x01cbe030 },
- { 0x10, 0x014be060 },
- { 0x12, 0x400000f0 },
- { 0x15, 0x400000f0 },
- {} /* terminator */
-};
+ return 0;
+}
+#endif
-static struct cs_pincfg mbp55_pincfgs[] = {
- { 0x09, 0x012b4030 },
- { 0x0a, 0x90100121 },
- { 0x0b, 0x90100120 },
- { 0x0c, 0x400000f0 },
- { 0x0d, 0x90a00110 },
- { 0x0e, 0x400000f0 },
- { 0x0f, 0x400000f0 },
- { 0x10, 0x014be040 },
- { 0x12, 0x400000f0 },
- { 0x15, 0x400000f0 },
- {} /* terminator */
+static const struct hda_codec_ops cs421x_patch_ops = {
+ .build_controls = cs421x_build_controls,
+ .build_pcms = snd_hda_gen_build_pcms,
+ .init = cs421x_init,
+ .free = cs_free,
+ .unsol_event = snd_hda_jack_unsol_event,
+#ifdef CONFIG_PM
+ .suspend = cs421x_suspend,
+#endif
};
-static struct cs_pincfg imac27_pincfgs[] = {
- { 0x09, 0x012b4050 },
- { 0x0a, 0x90100140 },
- { 0x0b, 0x90100142 },
- { 0x0c, 0x018b3020 },
- { 0x0d, 0x90a00110 },
- { 0x0e, 0x400000f0 },
- { 0x0f, 0x01cbe030 },
- { 0x10, 0x014be060 },
- { 0x12, 0x01ab9070 },
- { 0x15, 0x400000f0 },
- {} /* terminator */
-};
+static int patch_cs4210(struct hda_codec *codec)
+{
+ struct cs_spec *spec;
+ int err;
-static struct cs_pincfg *cs_pincfgs[CS420X_MODELS] = {
- [CS420X_MBP53] = mbp53_pincfgs,
- [CS420X_MBP55] = mbp55_pincfgs,
- [CS420X_IMAC27] = imac27_pincfgs,
-};
+ spec = cs_alloc_spec(codec, CS4210_VENDOR_NID);
+ if (!spec)
+ return -ENOMEM;
-static void fix_pincfg(struct hda_codec *codec, int model)
-{
- const struct cs_pincfg *cfg = cs_pincfgs[model];
- if (!cfg)
- return;
- for (; cfg->nid; cfg++)
- snd_hda_codec_set_pincfg(codec, cfg->nid, cfg->val);
-}
+ spec->gen.automute_hook = cs_automute;
+ snd_hda_pick_fixup(codec, cs421x_models, cs421x_fixup_tbl,
+ cs421x_fixups);
+ snd_hda_apply_fixup(codec, HDA_FIXUP_ACT_PRE_PROBE);
-static int patch_cs420x(struct hda_codec *codec)
+ /*
+ Update the GPIO/DMIC/SENSE_B pinmux before the configuration
+ is auto-parsed. If GPIO or SENSE_B is forced, DMIC input
+ is disabled.
+ */
+ cs4210_pinmux_init(codec);
+
+ err = cs421x_parse_auto_config(codec);
+ if (err < 0)
+ goto error;
+
+ codec->patch_ops = cs421x_patch_ops;
+
+ snd_hda_apply_fixup(codec, HDA_FIXUP_ACT_PROBE);
+
+ return 0;
+
+ error:
+ cs_free(codec);
+ return err;
+}
+
+static int patch_cs4213(struct hda_codec *codec)
{
struct cs_spec *spec;
int err;
- spec = kzalloc(sizeof(*spec), GFP_KERNEL);
+ spec = cs_alloc_spec(codec, CS4213_VENDOR_NID);
if (!spec)
return -ENOMEM;
- codec->spec = spec;
- spec->board_config =
- snd_hda_check_board_config(codec, CS420X_MODELS,
- cs420x_models, cs420x_cfg_tbl);
- if (spec->board_config >= 0)
- fix_pincfg(codec, spec->board_config);
-
- switch (spec->board_config) {
- case CS420X_IMAC27:
- case CS420X_MBP53:
- case CS420X_MBP55:
- /* GPIO1 = headphones */
- /* GPIO3 = speakers */
- spec->gpio_mask = 0x0a;
- spec->gpio_dir = 0x0a;
- break;
- }
-
- err = cs_parse_auto_config(codec);
+ err = cs421x_parse_auto_config(codec);
if (err < 0)
goto error;
- codec->patch_ops = cs_patch_ops;
-
+ codec->patch_ops = cs421x_patch_ops;
return 0;
error:
- kfree(codec->spec);
- codec->spec = NULL;
+ cs_free(codec);
return err;
}
@@ -1283,14 +1202,20 @@ static int patch_cs420x(struct hda_codec *codec)
/*
* patch entries
*/
-static struct hda_codec_preset snd_hda_preset_cirrus[] = {
+static const struct hda_codec_preset snd_hda_preset_cirrus[] = {
{ .id = 0x10134206, .name = "CS4206", .patch = patch_cs420x },
{ .id = 0x10134207, .name = "CS4207", .patch = patch_cs420x },
+ { .id = 0x10134208, .name = "CS4208", .patch = patch_cs4208 },
+ { .id = 0x10134210, .name = "CS4210", .patch = patch_cs4210 },
+ { .id = 0x10134213, .name = "CS4213", .patch = patch_cs4213 },
{} /* terminator */
};
MODULE_ALIAS("snd-hda-codec-id:10134206");
MODULE_ALIAS("snd-hda-codec-id:10134207");
+MODULE_ALIAS("snd-hda-codec-id:10134208");
+MODULE_ALIAS("snd-hda-codec-id:10134210");
+MODULE_ALIAS("snd-hda-codec-id:10134213");
MODULE_LICENSE("GPL");
MODULE_DESCRIPTION("Cirrus Logic HD-audio codec");
diff --git a/sound/pci/hda/patch_cmedia.c b/sound/pci/hda/patch_cmedia.c
index 1f8bbcd0f80..061ea5965dd 100644
--- a/sound/pci/hda/patch_cmedia.c
+++ b/sound/pci/hda/patch_cmedia.c
@@ -22,12 +22,18 @@
*/
#include <linux/init.h>
-#include <linux/delay.h>
#include <linux/slab.h>
-#include <linux/pci.h>
+#include <linux/module.h>
#include <sound/core.h>
#include "hda_codec.h"
#include "hda_local.h"
+#include "hda_auto_parser.h"
+#include "hda_jack.h"
+#include "hda_generic.h"
+
+#undef ENABLE_CMI_STATIC_QUIRKS
+
+#ifdef ENABLE_CMI_STATIC_QUIRKS
#define NUM_PINS 11
@@ -41,8 +47,14 @@ enum {
CMI_AUTO, /* let driver guess it */
CMI_MODELS
};
+#endif /* ENABLE_CMI_STATIC_QUIRKS */
struct cmi_spec {
+ struct hda_gen_spec gen;
+
+#ifdef ENABLE_CMI_STATIC_QUIRKS
+ /* below are only for static models */
+
int board_config;
unsigned int no_line_in: 1; /* no line-in (5-jack) */
unsigned int front_panel: 1; /* has front-panel 2-jack */
@@ -53,7 +65,7 @@ struct cmi_spec {
int num_dacs;
/* capture */
- hda_nid_t *adc_nids;
+ const hda_nid_t *adc_nids;
hda_nid_t dig_in_nid;
/* capture source */
@@ -73,8 +85,10 @@ struct cmi_spec {
/* multichannel pins */
struct hda_verb multi_init[9]; /* 2 verbs for each pin + terminator */
+#endif /* ENABLE_CMI_STATIC_QUIRKS */
};
+#ifdef ENABLE_CMI_STATIC_QUIRKS
/*
* input MUX
*/
@@ -110,7 +124,7 @@ static int cmi_mux_enum_put(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_v
*/
/* 3-stack / 2 channel */
-static struct hda_verb cmi9880_ch2_init[] = {
+static const struct hda_verb cmi9880_ch2_init[] = {
/* set line-in PIN for input */
{ 0x0c, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN },
/* set mic PIN for input, also enable vref */
@@ -121,7 +135,7 @@ static struct hda_verb cmi9880_ch2_init[] = {
};
/* 3-stack / 6 channel */
-static struct hda_verb cmi9880_ch6_init[] = {
+static const struct hda_verb cmi9880_ch6_init[] = {
/* set line-in PIN for output */
{ 0x0c, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT },
/* set mic PIN for output */
@@ -132,7 +146,7 @@ static struct hda_verb cmi9880_ch6_init[] = {
};
/* 3-stack+front / 8 channel */
-static struct hda_verb cmi9880_ch8_init[] = {
+static const struct hda_verb cmi9880_ch8_init[] = {
/* set line-in PIN for output */
{ 0x0c, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT },
/* set mic PIN for output */
@@ -142,7 +156,7 @@ static struct hda_verb cmi9880_ch8_init[] = {
{}
};
-static struct hda_channel_mode cmi9880_channel_modes[3] = {
+static const struct hda_channel_mode cmi9880_channel_modes[3] = {
{ 2, cmi9880_ch2_init },
{ 6, cmi9880_ch6_init },
{ 8, cmi9880_ch8_init },
@@ -174,7 +188,7 @@ static int cmi_ch_mode_put(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_va
/*
*/
-static struct snd_kcontrol_new cmi9880_basic_mixer[] = {
+static const struct snd_kcontrol_new cmi9880_basic_mixer[] = {
/* CMI9880 has no playback volumes! */
HDA_CODEC_MUTE("PCM Playback Switch", 0x03, 0x0, HDA_OUTPUT), /* front */
HDA_CODEC_MUTE("Surround Playback Switch", 0x04, 0x0, HDA_OUTPUT),
@@ -205,7 +219,7 @@ static struct snd_kcontrol_new cmi9880_basic_mixer[] = {
/*
* shared I/O pins
*/
-static struct snd_kcontrol_new cmi9880_ch_mode_mixer[] = {
+static const struct snd_kcontrol_new cmi9880_ch_mode_mixer[] = {
{
.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
.name = "Channel Mode",
@@ -219,7 +233,7 @@ static struct snd_kcontrol_new cmi9880_ch_mode_mixer[] = {
/* AUD-in selections:
* 0x0b 0x0c 0x0d 0x0e 0x0f 0x10 0x11 0x1f 0x20
*/
-static struct hda_input_mux cmi9880_basic_mux = {
+static const struct hda_input_mux cmi9880_basic_mux = {
.num_items = 4,
.items = {
{ "Front Mic", 0x5 },
@@ -229,7 +243,7 @@ static struct hda_input_mux cmi9880_basic_mux = {
}
};
-static struct hda_input_mux cmi9880_no_line_mux = {
+static const struct hda_input_mux cmi9880_no_line_mux = {
.num_items = 3,
.items = {
{ "Front Mic", 0x5 },
@@ -239,11 +253,11 @@ static struct hda_input_mux cmi9880_no_line_mux = {
};
/* front, rear, clfe, rear_surr */
-static hda_nid_t cmi9880_dac_nids[4] = {
+static const hda_nid_t cmi9880_dac_nids[4] = {
0x03, 0x04, 0x05, 0x06
};
/* ADC0, ADC1 */
-static hda_nid_t cmi9880_adc_nids[2] = {
+static const hda_nid_t cmi9880_adc_nids[2] = {
0x08, 0x09
};
@@ -252,7 +266,7 @@ static hda_nid_t cmi9880_adc_nids[2] = {
/*
*/
-static struct hda_verb cmi9880_basic_init[] = {
+static const struct hda_verb cmi9880_basic_init[] = {
/* port-D for line out (rear panel) */
{ 0x0b, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP },
/* port-E for HP out (front panel) */
@@ -281,7 +295,7 @@ static struct hda_verb cmi9880_basic_init[] = {
{} /* terminator */
};
-static struct hda_verb cmi9880_allout_init[] = {
+static const struct hda_verb cmi9880_allout_init[] = {
/* port-D for line out (rear panel) */
{ 0x0b, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP },
/* port-E for HP out (front panel) */
@@ -327,7 +341,9 @@ static int cmi9880_build_controls(struct hda_codec *codec)
return err;
}
if (spec->multiout.dig_out_nid) {
- err = snd_hda_create_spdif_out_ctls(codec, spec->multiout.dig_out_nid);
+ err = snd_hda_create_spdif_out_ctls(codec,
+ spec->multiout.dig_out_nid,
+ spec->multiout.dig_out_nid);
if (err < 0)
return err;
err = snd_hda_create_spdif_share_sw(codec,
@@ -352,80 +368,6 @@ static int cmi9880_build_controls(struct hda_codec *codec)
return 0;
}
-/* fill in the multi_dac_nids table, which will decide
- which audio widget to use for each channel */
-static int cmi9880_fill_multi_dac_nids(struct hda_codec *codec, const struct auto_pin_cfg *cfg)
-{
- struct cmi_spec *spec = codec->spec;
- hda_nid_t nid;
- int assigned[4];
- int i, j;
-
- /* clear the table, only one c-media dac assumed here */
- memset(spec->dac_nids, 0, sizeof(spec->dac_nids));
- memset(assigned, 0, sizeof(assigned));
- /* check the pins we found */
- for (i = 0; i < cfg->line_outs; i++) {
- nid = cfg->line_out_pins[i];
- /* nid 0x0b~0x0e is hardwired to audio widget 0x3~0x6 */
- if (nid >= 0x0b && nid <= 0x0e) {
- spec->dac_nids[i] = (nid - 0x0b) + 0x03;
- assigned[nid - 0x0b] = 1;
- }
- }
- /* left pin can be connect to any audio widget */
- for (i = 0; i < cfg->line_outs; i++) {
- nid = cfg->line_out_pins[i];
- if (nid <= 0x0e)
- continue;
- /* search for an empty channel */
- for (j = 0; j < cfg->line_outs; j++) {
- if (! assigned[j]) {
- spec->dac_nids[i] = j + 0x03;
- assigned[j] = 1;
- break;
- }
- }
- }
- spec->num_dacs = cfg->line_outs;
- return 0;
-}
-
-/* create multi_init table, which is used for multichannel initialization */
-static int cmi9880_fill_multi_init(struct hda_codec *codec, const struct auto_pin_cfg *cfg)
-{
- struct cmi_spec *spec = codec->spec;
- hda_nid_t nid;
- int i, j, k, len;
-
- /* clear the table, only one c-media dac assumed here */
- memset(spec->multi_init, 0, sizeof(spec->multi_init));
- for (j = 0, i = 0; i < cfg->line_outs; i++) {
- hda_nid_t conn[4];
- nid = cfg->line_out_pins[i];
- /* set as output */
- spec->multi_init[j].nid = nid;
- spec->multi_init[j].verb = AC_VERB_SET_PIN_WIDGET_CONTROL;
- spec->multi_init[j].param = PIN_OUT;
- j++;
- if (nid > 0x0e) {
- /* set connection */
- spec->multi_init[j].nid = nid;
- spec->multi_init[j].verb = AC_VERB_SET_CONNECT_SEL;
- spec->multi_init[j].param = 0;
- /* find the index in connect list */
- len = snd_hda_get_connections(codec, nid, conn, 4);
- for (k = 0; k < len; k++)
- if (conn[k] == spec->dac_nids[i]) {
- spec->multi_init[j].param = k;
- break;
- }
- j++;
- }
- }
- return 0;
-}
-
static int cmi9880_init(struct hda_codec *codec)
{
struct cmi_spec *spec = codec->spec;
@@ -528,7 +470,7 @@ static int cmi9880_capture_pcm_cleanup(struct hda_pcm_stream *hinfo,
/*
*/
-static struct hda_pcm_stream cmi9880_pcm_analog_playback = {
+static const struct hda_pcm_stream cmi9880_pcm_analog_playback = {
.substreams = 1,
.channels_min = 2,
.channels_max = 8,
@@ -540,7 +482,7 @@ static struct hda_pcm_stream cmi9880_pcm_analog_playback = {
},
};
-static struct hda_pcm_stream cmi9880_pcm_analog_capture = {
+static const struct hda_pcm_stream cmi9880_pcm_analog_capture = {
.substreams = 2,
.channels_min = 2,
.channels_max = 2,
@@ -551,7 +493,7 @@ static struct hda_pcm_stream cmi9880_pcm_analog_capture = {
},
};
-static struct hda_pcm_stream cmi9880_pcm_digital_playback = {
+static const struct hda_pcm_stream cmi9880_pcm_digital_playback = {
.substreams = 1,
.channels_min = 2,
.channels_max = 2,
@@ -563,7 +505,7 @@ static struct hda_pcm_stream cmi9880_pcm_digital_playback = {
},
};
-static struct hda_pcm_stream cmi9880_pcm_digital_capture = {
+static const struct hda_pcm_stream cmi9880_pcm_digital_capture = {
.substreams = 1,
.channels_min = 2,
.channels_max = 2,
@@ -617,19 +559,55 @@ static const char * const cmi9880_models[CMI_MODELS] = {
[CMI_AUTO] = "auto",
};
-static struct snd_pci_quirk cmi9880_cfg_tbl[] = {
+static const struct snd_pci_quirk cmi9880_cfg_tbl[] = {
SND_PCI_QUIRK(0x1043, 0x813d, "ASUS P5AD2", CMI_FULL_DIG),
SND_PCI_QUIRK(0x1854, 0x002b, "LG LS75", CMI_MINIMAL),
SND_PCI_QUIRK(0x1854, 0x0032, "LG", CMI_FULL_DIG),
{} /* terminator */
};
-static struct hda_codec_ops cmi9880_patch_ops = {
+static const struct hda_codec_ops cmi9880_patch_ops = {
.build_controls = cmi9880_build_controls,
.build_pcms = cmi9880_build_pcms,
.init = cmi9880_init,
.free = cmi9880_free,
};
+#endif /* ENABLE_CMI_STATIC_QUIRKS */
+
+/*
+ * stuff for auto-parser
+ */
+static const struct hda_codec_ops cmi_auto_patch_ops = {
+ .build_controls = snd_hda_gen_build_controls,
+ .build_pcms = snd_hda_gen_build_pcms,
+ .init = snd_hda_gen_init,
+ .free = snd_hda_gen_free,
+ .unsol_event = snd_hda_jack_unsol_event,
+};
+
+static int cmi_parse_auto_config(struct hda_codec *codec)
+{
+ struct cmi_spec *spec = codec->spec;
+ struct auto_pin_cfg *cfg = &spec->gen.autocfg;
+ int err;
+
+ snd_hda_gen_spec_init(&spec->gen);
+
+ err = snd_hda_parse_pin_defcfg(codec, cfg, NULL, 0);
+ if (err < 0)
+ goto error;
+ err = snd_hda_gen_parse_auto_config(codec, cfg);
+ if (err < 0)
+ goto error;
+
+ codec->patch_ops = cmi_auto_patch_ops;
+ return 0;
+
+ error:
+ snd_hda_gen_free(codec);
+ return err;
+}
+
static int patch_cmi9880(struct hda_codec *codec)
{
@@ -640,15 +618,19 @@ static int patch_cmi9880(struct hda_codec *codec)
return -ENOMEM;
codec->spec = spec;
+#ifdef ENABLE_CMI_STATIC_QUIRKS
spec->board_config = snd_hda_check_board_config(codec, CMI_MODELS,
cmi9880_models,
cmi9880_cfg_tbl);
if (spec->board_config < 0) {
- snd_printdd(KERN_INFO "hda_codec: %s: BIOS auto-probing.\n",
+ codec_dbg(codec, "%s: BIOS auto-probing.\n",
codec->chip_name);
spec->board_config = CMI_AUTO; /* try everything */
}
+ if (spec->board_config == CMI_AUTO)
+ return cmi_parse_auto_config(codec);
+
/* copy default DAC NIDs */
memcpy(spec->dac_nids, cmi9880_dac_nids, sizeof(spec->dac_nids));
spec->num_dacs = 4;
@@ -677,59 +659,13 @@ static int patch_cmi9880(struct hda_codec *codec)
}
break;
case CMI_ALLOUT:
+ default:
spec->front_panel = 1;
spec->multiout.max_channels = 8;
spec->no_line_in = 1;
spec->input_mux = &cmi9880_no_line_mux;
spec->multiout.dig_out_nid = CMI_DIG_OUT_NID;
break;
- case CMI_AUTO:
- {
- unsigned int port_e, port_f, port_g, port_h;
- unsigned int port_spdifi, port_spdifo;
- struct auto_pin_cfg cfg;
-
- /* collect pin default configuration */
- port_e = snd_hda_codec_get_pincfg(codec, 0x0f);
- port_f = snd_hda_codec_get_pincfg(codec, 0x10);
- spec->front_panel = 1;
- if (get_defcfg_connect(port_e) == AC_JACK_PORT_NONE ||
- get_defcfg_connect(port_f) == AC_JACK_PORT_NONE) {
- port_g = snd_hda_codec_get_pincfg(codec, 0x1f);
- port_h = snd_hda_codec_get_pincfg(codec, 0x20);
- spec->channel_modes = cmi9880_channel_modes;
- /* no front panel */
- if (get_defcfg_connect(port_g) == AC_JACK_PORT_NONE ||
- get_defcfg_connect(port_h) == AC_JACK_PORT_NONE) {
- /* no optional rear panel */
- spec->board_config = CMI_MINIMAL;
- spec->front_panel = 0;
- spec->num_channel_modes = 2;
- } else {
- spec->board_config = CMI_MIN_FP;
- spec->num_channel_modes = 3;
- }
- spec->input_mux = &cmi9880_basic_mux;
- spec->multiout.max_channels = cmi9880_channel_modes[0].channels;
- } else {
- spec->input_mux = &cmi9880_basic_mux;
- port_spdifi = snd_hda_codec_get_pincfg(codec, 0x13);
- port_spdifo = snd_hda_codec_get_pincfg(codec, 0x12);
- if (get_defcfg_connect(port_spdifo) != AC_JACK_PORT_NONE)
- spec->multiout.dig_out_nid = CMI_DIG_OUT_NID;
- if (get_defcfg_connect(port_spdifi) != AC_JACK_PORT_NONE)
- spec->dig_in_nid = CMI_DIG_IN_NID;
- spec->multiout.max_channels = 8;
- }
- snd_hda_parse_pin_def_config(codec, &cfg, NULL);
- if (cfg.line_outs) {
- spec->multiout.max_channels = cfg.line_outs * 2;
- cmi9880_fill_multi_dac_nids(codec, &cfg);
- cmi9880_fill_multi_init(codec, &cfg);
- } else
- snd_printd("patch_cmedia: cannot detect association in defcfg\n");
- break;
- }
}
spec->multiout.num_dacs = spec->num_dacs;
@@ -740,12 +676,15 @@ static int patch_cmi9880(struct hda_codec *codec)
codec->patch_ops = cmi9880_patch_ops;
return 0;
+#else
+ return cmi_parse_auto_config(codec);
+#endif
}
/*
* patch entries
*/
-static struct hda_codec_preset snd_hda_preset_cmedia[] = {
+static const struct hda_codec_preset snd_hda_preset_cmedia[] = {
{ .id = 0x13f69880, .name = "CMI9880", .patch = patch_cmi9880 },
{ .id = 0x434d4980, .name = "CMI9880", .patch = patch_cmi9880 },
{} /* terminator */
diff --git a/sound/pci/hda/patch_conexant.c b/sound/pci/hda/patch_conexant.c
index ad97d937d3a..1dc7e974f3b 100644
--- a/sound/pci/hda/patch_conexant.c
+++ b/sound/pci/hda/patch_conexant.c
@@ -23,13 +23,18 @@
#include <linux/init.h>
#include <linux/delay.h>
#include <linux/slab.h>
-#include <linux/pci.h>
+#include <linux/module.h>
#include <sound/core.h>
#include <sound/jack.h>
#include "hda_codec.h"
#include "hda_local.h"
+#include "hda_auto_parser.h"
#include "hda_beep.h"
+#include "hda_jack.h"
+#include "hda_generic.h"
+
+#undef ENABLE_CXT_STATIC_QUIRKS
#define CXT_PIN_DIR_IN 0x00
#define CXT_PIN_DIR_OUT 0x01
@@ -39,6 +44,7 @@
#define CONEXANT_HP_EVENT 0x37
#define CONEXANT_MIC_EVENT 0x38
+#define CONEXANT_LINE_EVENT 0x39
/* Conexant 5051 specific */
@@ -49,15 +55,26 @@
#define AUTO_MIC_PORTB (1 << 1)
#define AUTO_MIC_PORTC (1 << 2)
-struct pin_dac_pair {
- hda_nid_t pin;
- hda_nid_t dac;
- int type;
-};
-
struct conexant_spec {
+ struct hda_gen_spec gen;
+
+ unsigned int beep_amp;
+
+ /* extra EAPD pins */
+ unsigned int num_eapds;
+ hda_nid_t eapds[4];
+ bool dynamic_eapd;
- struct snd_kcontrol_new *mixers[5];
+ unsigned int parse_flags; /* flag for snd_hda_parse_pin_defcfg() */
+
+ /* OPLC XO specific */
+ bool recording;
+ bool dc_enable;
+ unsigned int dc_input_bias; /* offset into olpc_xo_dc_bias */
+ struct nid_path *dc_mode_path;
+
+#ifdef ENABLE_CXT_STATIC_QUIRKS
+ const struct snd_kcontrol_new *mixers[5];
int num_mixers;
hda_nid_t vmaster_nid;
@@ -74,14 +91,12 @@ struct conexant_spec {
*/
unsigned int cur_eapd;
unsigned int hp_present;
+ unsigned int line_present;
unsigned int auto_mic;
- int auto_mic_ext; /* autocfg.inputs[] index for ext mic */
- unsigned int need_dac_fix;
- hda_nid_t slave_dig_outs[2];
/* capture */
unsigned int num_adc_nids;
- hda_nid_t *adc_nids;
+ const hda_nid_t *adc_nids;
hda_nid_t dig_in_nid; /* digital-in NID; optional */
unsigned int cur_adc_idx;
@@ -89,9 +104,11 @@ struct conexant_spec {
unsigned int cur_adc_stream_tag;
unsigned int cur_adc_format;
+ const struct hda_pcm_stream *capture_stream;
+
/* capture source */
const struct hda_input_mux *input_mux;
- hda_nid_t *capsrc_nids;
+ const hda_nid_t *capsrc_nids;
unsigned int cur_mux[3];
/* channel model */
@@ -103,15 +120,7 @@ struct conexant_spec {
unsigned int spdif_route;
- /* dynamic controls, init_verbs and input_mux */
- struct auto_pin_cfg autocfg;
- struct hda_input_mux private_imux;
- hda_nid_t private_dac_nids[AUTO_CFG_MAX_OUTS];
- struct pin_dac_pair dac_info[8];
- int dac_info_filled;
-
unsigned int port_d_mode;
- unsigned int auto_mute:1; /* used in auto-parser */
unsigned int dell_automute:1;
unsigned int dell_vostro:1;
unsigned int ideapad:1;
@@ -119,24 +128,53 @@ struct conexant_spec {
unsigned int hp_laptop:1;
unsigned int asus:1;
- unsigned int ext_mic_present;
- unsigned int recording;
- void (*capture_prepare)(struct hda_codec *codec);
- void (*capture_cleanup)(struct hda_codec *codec);
-
- /* OLPC XO-1.5 supports DC input mode (e.g. for use with analog sensors)
- * through the microphone jack.
- * When the user enables this through a mixer switch, both internal and
- * external microphones are disabled. Gain is fixed at 0dB. In this mode,
- * we also allow the bias to be configured through a separate mixer
- * control. */
- unsigned int dc_enable;
- unsigned int dc_input_bias; /* offset into cxt5066_olpc_dc_bias */
unsigned int mic_boost; /* offset into cxt5066_analog_mic_boost */
+#endif /* ENABLE_CXT_STATIC_QUIRKS */
+};
- unsigned int beep_amp;
+
+#ifdef CONFIG_SND_HDA_INPUT_BEEP
+static inline void set_beep_amp(struct conexant_spec *spec, hda_nid_t nid,
+ int idx, int dir)
+{
+ spec->gen.beep_nid = nid;
+ spec->beep_amp = HDA_COMPOSE_AMP_VAL(nid, 1, idx, dir);
+}
+/* additional beep mixers; the actual parameters are overwritten at build */
+static const struct snd_kcontrol_new cxt_beep_mixer[] = {
+ HDA_CODEC_VOLUME_MONO("Beep Playback Volume", 0, 1, 0, HDA_OUTPUT),
+ HDA_CODEC_MUTE_BEEP_MONO("Beep Playback Switch", 0, 1, 0, HDA_OUTPUT),
+ { } /* end */
};
+/* create beep controls if needed */
+static int add_beep_ctls(struct hda_codec *codec)
+{
+ struct conexant_spec *spec = codec->spec;
+ int err;
+
+ if (spec->beep_amp) {
+ const struct snd_kcontrol_new *knew;
+ for (knew = cxt_beep_mixer; knew->name; knew++) {
+ struct snd_kcontrol *kctl;
+ kctl = snd_ctl_new1(knew, codec);
+ if (!kctl)
+ return -ENOMEM;
+ kctl->private_value = spec->beep_amp;
+ err = snd_hda_ctl_add(codec, 0, kctl);
+ if (err < 0)
+ return err;
+ }
+ }
+ return 0;
+}
+#else
+#define set_beep_amp(spec, nid, idx, dir) /* NOP */
+#define add_beep_ctls(codec) 0
+#endif
+
+
+#ifdef ENABLE_CXT_STATIC_QUIRKS
static int conexant_playback_pcm_open(struct hda_pcm_stream *hinfo,
struct hda_codec *codec,
struct snd_pcm_substream *substream)
@@ -207,8 +245,6 @@ static int conexant_capture_pcm_prepare(struct hda_pcm_stream *hinfo,
struct snd_pcm_substream *substream)
{
struct conexant_spec *spec = codec->spec;
- if (spec->capture_prepare)
- spec->capture_prepare(codec);
snd_hda_codec_setup_stream(codec, spec->adc_nids[substream->number],
stream_tag, 0, format);
return 0;
@@ -220,14 +256,12 @@ static int conexant_capture_pcm_cleanup(struct hda_pcm_stream *hinfo,
{
struct conexant_spec *spec = codec->spec;
snd_hda_codec_cleanup_stream(codec, spec->adc_nids[substream->number]);
- if (spec->capture_cleanup)
- spec->capture_cleanup(codec);
return 0;
}
-static struct hda_pcm_stream conexant_pcm_analog_playback = {
+static const struct hda_pcm_stream conexant_pcm_analog_playback = {
.substreams = 1,
.channels_min = 2,
.channels_max = 2,
@@ -239,7 +273,7 @@ static struct hda_pcm_stream conexant_pcm_analog_playback = {
},
};
-static struct hda_pcm_stream conexant_pcm_analog_capture = {
+static const struct hda_pcm_stream conexant_pcm_analog_capture = {
.substreams = 1,
.channels_min = 2,
.channels_max = 2,
@@ -251,7 +285,7 @@ static struct hda_pcm_stream conexant_pcm_analog_capture = {
};
-static struct hda_pcm_stream conexant_pcm_digital_playback = {
+static const struct hda_pcm_stream conexant_pcm_digital_playback = {
.substreams = 1,
.channels_min = 2,
.channels_max = 2,
@@ -263,7 +297,7 @@ static struct hda_pcm_stream conexant_pcm_digital_playback = {
},
};
-static struct hda_pcm_stream conexant_pcm_digital_capture = {
+static const struct hda_pcm_stream conexant_pcm_digital_capture = {
.substreams = 1,
.channels_min = 2,
.channels_max = 2,
@@ -294,7 +328,7 @@ static int cx5051_capture_pcm_cleanup(struct hda_pcm_stream *hinfo,
return 0;
}
-static struct hda_pcm_stream cx5051_pcm_analog_capture = {
+static const struct hda_pcm_stream cx5051_pcm_analog_capture = {
.substreams = 1,
.channels_min = 2,
.channels_max = 2,
@@ -319,13 +353,19 @@ static int conexant_build_pcms(struct hda_codec *codec)
spec->multiout.max_channels;
info->stream[SNDRV_PCM_STREAM_PLAYBACK].nid =
spec->multiout.dac_nids[0];
- if (codec->vendor_id == 0x14f15051)
- info->stream[SNDRV_PCM_STREAM_CAPTURE] =
- cx5051_pcm_analog_capture;
- else
- info->stream[SNDRV_PCM_STREAM_CAPTURE] =
- conexant_pcm_analog_capture;
- info->stream[SNDRV_PCM_STREAM_CAPTURE].substreams = spec->num_adc_nids;
+ if (spec->capture_stream)
+ info->stream[SNDRV_PCM_STREAM_CAPTURE] = *spec->capture_stream;
+ else {
+ if (codec->vendor_id == 0x14f15051)
+ info->stream[SNDRV_PCM_STREAM_CAPTURE] =
+ cx5051_pcm_analog_capture;
+ else {
+ info->stream[SNDRV_PCM_STREAM_CAPTURE] =
+ conexant_pcm_analog_capture;
+ info->stream[SNDRV_PCM_STREAM_CAPTURE].substreams =
+ spec->num_adc_nids;
+ }
+ }
info->stream[SNDRV_PCM_STREAM_CAPTURE].nid = spec->adc_nids[0];
if (spec->multiout.dig_out_nid) {
@@ -343,8 +383,6 @@ static int conexant_build_pcms(struct hda_codec *codec)
info->stream[SNDRV_PCM_STREAM_CAPTURE].nid =
spec->dig_in_nid;
}
- if (spec->slave_dig_outs[0])
- codec->slave_dig_outs = spec->slave_dig_outs;
}
return 0;
@@ -382,38 +420,17 @@ static int conexant_mux_enum_put(struct snd_kcontrol *kcontrol,
&spec->cur_mux[adc_idx]);
}
-static int conexant_init_jacks(struct hda_codec *codec)
+static void conexant_set_power(struct hda_codec *codec, hda_nid_t fg,
+ unsigned int power_state)
{
-#ifdef CONFIG_SND_HDA_INPUT_JACK
- struct conexant_spec *spec = codec->spec;
- int i;
-
- for (i = 0; i < spec->num_init_verbs; i++) {
- const struct hda_verb *hv;
-
- hv = spec->init_verbs[i];
- while (hv->nid) {
- int err = 0;
- switch (hv->param ^ AC_USRSP_EN) {
- case CONEXANT_HP_EVENT:
- err = snd_hda_input_jack_add(codec, hv->nid,
- SND_JACK_HEADPHONE, NULL);
- snd_hda_input_jack_report(codec, hv->nid);
- break;
- case CXT5051_PORTC_EVENT:
- case CONEXANT_MIC_EVENT:
- err = snd_hda_input_jack_add(codec, hv->nid,
- SND_JACK_MICROPHONE, NULL);
- snd_hda_input_jack_report(codec, hv->nid);
- break;
- }
- if (err < 0)
- return err;
- ++hv;
- }
- }
-#endif /* CONFIG_SND_HDA_INPUT_JACK */
- return 0;
+ if (power_state == AC_PWRST_D3)
+ msleep(100);
+ snd_hda_codec_read(codec, fg, 0, AC_VERB_SET_POWER_STATE,
+ power_state);
+ /* partial workaround for "azx_get_response timeout" */
+ if (power_state == AC_PWRST_D0)
+ msleep(10);
+ snd_hda_codec_set_power_to_all(codec, fg, power_state);
}
static int conexant_init(struct hda_codec *codec)
@@ -428,12 +445,10 @@ static int conexant_init(struct hda_codec *codec)
static void conexant_free(struct hda_codec *codec)
{
- snd_hda_input_jack_free(codec);
- snd_hda_detach_beep_device(codec);
kfree(codec->spec);
}
-static struct snd_kcontrol_new cxt_capture_mixers[] = {
+static const struct snd_kcontrol_new cxt_capture_mixers[] = {
{
.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
.name = "Capture Source",
@@ -444,24 +459,8 @@ static struct snd_kcontrol_new cxt_capture_mixers[] = {
{}
};
-#ifdef CONFIG_SND_HDA_INPUT_BEEP
-/* additional beep mixers; the actual parameters are overwritten at build */
-static struct snd_kcontrol_new cxt_beep_mixer[] = {
- HDA_CODEC_VOLUME_MONO("Beep Playback Volume", 0, 1, 0, HDA_OUTPUT),
- HDA_CODEC_MUTE_BEEP_MONO("Beep Playback Switch", 0, 1, 0, HDA_OUTPUT),
- { } /* end */
-};
-#endif
-
-static const char * const slave_vols[] = {
- "Headphone Playback Volume",
- "Speaker Playback Volume",
- NULL
-};
-
-static const char * const slave_sws[] = {
- "Headphone Playback Switch",
- "Speaker Playback Switch",
+static const char * const slave_pfxs[] = {
+ "Headphone", "Speaker", "Bass Speaker", "Front", "Surround", "CLFE",
NULL
};
@@ -478,6 +477,7 @@ static int conexant_build_controls(struct hda_codec *codec)
}
if (spec->multiout.dig_out_nid) {
err = snd_hda_create_spdif_out_ctls(codec,
+ spec->multiout.dig_out_nid,
spec->multiout.dig_out_nid);
if (err < 0)
return err;
@@ -500,14 +500,16 @@ static int conexant_build_controls(struct hda_codec *codec)
snd_hda_set_vmaster_tlv(codec, spec->vmaster_nid,
HDA_OUTPUT, vmaster_tlv);
err = snd_hda_add_vmaster(codec, "Master Playback Volume",
- vmaster_tlv, slave_vols);
+ vmaster_tlv, slave_pfxs,
+ "Playback Volume");
if (err < 0)
return err;
}
if (spec->vmaster_nid &&
!snd_hda_find_mixer_ctl(codec, "Master Playback Switch")) {
err = snd_hda_add_vmaster(codec, "Master Playback Switch",
- NULL, slave_sws);
+ NULL, slave_pfxs,
+ "Playback Switch");
if (err < 0)
return err;
}
@@ -518,52 +520,22 @@ static int conexant_build_controls(struct hda_codec *codec)
return err;
}
-#ifdef CONFIG_SND_HDA_INPUT_BEEP
- /* create beep controls if needed */
- if (spec->beep_amp) {
- struct snd_kcontrol_new *knew;
- for (knew = cxt_beep_mixer; knew->name; knew++) {
- struct snd_kcontrol *kctl;
- kctl = snd_ctl_new1(knew, codec);
- if (!kctl)
- return -ENOMEM;
- kctl->private_value = spec->beep_amp;
- err = snd_hda_ctl_add(codec, 0, kctl);
- if (err < 0)
- return err;
- }
- }
-#endif
-
- return 0;
-}
+ err = add_beep_ctls(codec);
+ if (err < 0)
+ return err;
-#ifdef CONFIG_SND_HDA_POWER_SAVE
-static int conexant_suspend(struct hda_codec *codec, pm_message_t state)
-{
- snd_hda_shutup_pins(codec);
return 0;
}
-#endif
-static struct hda_codec_ops conexant_patch_ops = {
+static const struct hda_codec_ops conexant_patch_ops = {
.build_controls = conexant_build_controls,
.build_pcms = conexant_build_pcms,
.init = conexant_init,
.free = conexant_free,
-#ifdef CONFIG_SND_HDA_POWER_SAVE
- .suspend = conexant_suspend,
-#endif
- .reboot_notify = snd_hda_shutup_pins,
+ .set_power_state = conexant_set_power,
};
-#ifdef CONFIG_SND_HDA_INPUT_BEEP
-#define set_beep_amp(spec, nid, idx, dir) \
- ((spec)->beep_amp = HDA_COMPOSE_AMP_VAL(nid, 1, idx, dir))
-#else
-#define set_beep_amp(spec, nid, idx, dir) /* NOP */
-#endif
-
+static int patch_conexant_auto(struct hda_codec *codec);
/*
* EAPD control
* the private value = nid | (invert << 8)
@@ -646,8 +618,6 @@ static int conexant_ch_mode_put(struct snd_kcontrol *kcontrol,
int err = snd_hda_ch_mode_put(codec, ucontrol, spec->channel_mode,
spec->num_channel_mode,
&spec->multiout.max_channels);
- if (err >= 0 && spec->need_dac_fix)
- spec->multiout.num_dacs = spec->multiout.max_channels / 2;
return err;
}
@@ -662,39 +632,30 @@ static int conexant_ch_mode_put(struct snd_kcontrol *kcontrol,
/* Conexant 5045 specific */
-static hda_nid_t cxt5045_dac_nids[1] = { 0x19 };
-static hda_nid_t cxt5045_adc_nids[1] = { 0x1a };
-static hda_nid_t cxt5045_capsrc_nids[1] = { 0x1a };
+static const hda_nid_t cxt5045_dac_nids[1] = { 0x19 };
+static const hda_nid_t cxt5045_adc_nids[1] = { 0x1a };
+static const hda_nid_t cxt5045_capsrc_nids[1] = { 0x1a };
#define CXT5045_SPDIF_OUT 0x18
-static struct hda_channel_mode cxt5045_modes[1] = {
+static const struct hda_channel_mode cxt5045_modes[1] = {
{ 2, NULL },
};
-static struct hda_input_mux cxt5045_capture_source = {
+static const struct hda_input_mux cxt5045_capture_source = {
.num_items = 2,
.items = {
- { "IntMic", 0x1 },
- { "ExtMic", 0x2 },
- }
-};
-
-static struct hda_input_mux cxt5045_capture_source_benq = {
- .num_items = 5,
- .items = {
- { "IntMic", 0x1 },
- { "ExtMic", 0x2 },
- { "LineIn", 0x3 },
- { "CD", 0x4 },
- { "Mixer", 0x0 },
+ { "Internal Mic", 0x1 },
+ { "Mic", 0x2 },
}
};
-static struct hda_input_mux cxt5045_capture_source_hp530 = {
- .num_items = 2,
+static const struct hda_input_mux cxt5045_capture_source_benq = {
+ .num_items = 4,
.items = {
- { "ExtMic", 0x1 },
- { "IntMic", 0x2 },
+ { "Internal Mic", 0x1 },
+ { "Mic", 0x2 },
+ { "Line", 0x3 },
+ { "Mixer", 0x0 },
}
};
@@ -723,7 +684,7 @@ static int cxt5045_hp_master_sw_put(struct snd_kcontrol *kcontrol,
}
/* bind volumes of both NID 0x10 and 0x11 */
-static struct hda_bind_ctls cxt5045_hp_bind_master_vol = {
+static const struct hda_bind_ctls cxt5045_hp_bind_master_vol = {
.ops = &snd_hda_bind_vol,
.values = {
HDA_COMPOSE_AMP_VAL(0x10, 3, 0, HDA_OUTPUT),
@@ -735,12 +696,12 @@ static struct hda_bind_ctls cxt5045_hp_bind_master_vol = {
/* toggle input of built-in and mic jack appropriately */
static void cxt5045_hp_automic(struct hda_codec *codec)
{
- static struct hda_verb mic_jack_on[] = {
+ static const struct hda_verb mic_jack_on[] = {
{0x14, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080},
{0x12, AC_VERB_SET_AMP_GAIN_MUTE, 0xb000},
{}
};
- static struct hda_verb mic_jack_off[] = {
+ static const struct hda_verb mic_jack_off[] = {
{0x12, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080},
{0x14, AC_VERB_SET_AMP_GAIN_MUTE, 0xb000},
{}
@@ -784,11 +745,9 @@ static void cxt5045_hp_unsol_event(struct hda_codec *codec,
}
}
-static struct snd_kcontrol_new cxt5045_mixers[] = {
- HDA_CODEC_VOLUME("Internal Mic Capture Volume", 0x1a, 0x01, HDA_INPUT),
- HDA_CODEC_MUTE("Internal Mic Capture Switch", 0x1a, 0x01, HDA_INPUT),
- HDA_CODEC_VOLUME("Mic Capture Volume", 0x1a, 0x02, HDA_INPUT),
- HDA_CODEC_MUTE("Mic Capture Switch", 0x1a, 0x02, HDA_INPUT),
+static const struct snd_kcontrol_new cxt5045_mixers[] = {
+ HDA_CODEC_VOLUME("Capture Volume", 0x1a, 0x00, HDA_INPUT),
+ HDA_CODEC_MUTE("Capture Switch", 0x1a, 0x0, HDA_INPUT),
HDA_CODEC_VOLUME("PCM Playback Volume", 0x17, 0x0, HDA_INPUT),
HDA_CODEC_MUTE("PCM Playback Switch", 0x17, 0x0, HDA_INPUT),
HDA_CODEC_VOLUME("Internal Mic Playback Volume", 0x17, 0x1, HDA_INPUT),
@@ -808,48 +767,14 @@ static struct snd_kcontrol_new cxt5045_mixers[] = {
{}
};
-static struct snd_kcontrol_new cxt5045_benq_mixers[] = {
- HDA_CODEC_VOLUME("CD Capture Volume", 0x1a, 0x04, HDA_INPUT),
- HDA_CODEC_MUTE("CD Capture Switch", 0x1a, 0x04, HDA_INPUT),
- HDA_CODEC_VOLUME("CD Playback Volume", 0x17, 0x4, HDA_INPUT),
- HDA_CODEC_MUTE("CD Playback Switch", 0x17, 0x4, HDA_INPUT),
-
- HDA_CODEC_VOLUME("Line In Capture Volume", 0x1a, 0x03, HDA_INPUT),
- HDA_CODEC_MUTE("Line In Capture Switch", 0x1a, 0x03, HDA_INPUT),
- HDA_CODEC_VOLUME("Line In Playback Volume", 0x17, 0x3, HDA_INPUT),
- HDA_CODEC_MUTE("Line In Playback Switch", 0x17, 0x3, HDA_INPUT),
-
- HDA_CODEC_VOLUME("Mixer Capture Volume", 0x1a, 0x0, HDA_INPUT),
- HDA_CODEC_MUTE("Mixer Capture Switch", 0x1a, 0x0, HDA_INPUT),
-
- {}
-};
-
-static struct snd_kcontrol_new cxt5045_mixers_hp530[] = {
- HDA_CODEC_VOLUME("Internal Mic Capture Volume", 0x1a, 0x02, HDA_INPUT),
- HDA_CODEC_MUTE("Internal Mic Capture Switch", 0x1a, 0x02, HDA_INPUT),
- HDA_CODEC_VOLUME("Mic Capture Volume", 0x1a, 0x01, HDA_INPUT),
- HDA_CODEC_MUTE("Mic Capture Switch", 0x1a, 0x01, HDA_INPUT),
- HDA_CODEC_VOLUME("PCM Playback Volume", 0x17, 0x0, HDA_INPUT),
- HDA_CODEC_MUTE("PCM Playback Switch", 0x17, 0x0, HDA_INPUT),
- HDA_CODEC_VOLUME("Internal Mic Playback Volume", 0x17, 0x2, HDA_INPUT),
- HDA_CODEC_MUTE("Internal Mic Playback Switch", 0x17, 0x2, HDA_INPUT),
- HDA_CODEC_VOLUME("Mic Playback Volume", 0x17, 0x1, HDA_INPUT),
- HDA_CODEC_MUTE("Mic Playback Switch", 0x17, 0x1, HDA_INPUT),
- HDA_BIND_VOL("Master Playback Volume", &cxt5045_hp_bind_master_vol),
- {
- .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
- .name = "Master Playback Switch",
- .info = cxt_eapd_info,
- .get = cxt_eapd_get,
- .put = cxt5045_hp_master_sw_put,
- .private_value = 0x10,
- },
+static const struct snd_kcontrol_new cxt5045_benq_mixers[] = {
+ HDA_CODEC_VOLUME("Line Playback Volume", 0x17, 0x3, HDA_INPUT),
+ HDA_CODEC_MUTE("Line Playback Switch", 0x17, 0x3, HDA_INPUT),
{}
};
-static struct hda_verb cxt5045_init_verbs[] = {
+static const struct hda_verb cxt5045_init_verbs[] = {
/* Line in, Mic */
{0x12, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN|AC_PINCTL_VREF_80 },
{0x14, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN|AC_PINCTL_VREF_80 },
@@ -875,7 +800,7 @@ static struct hda_verb cxt5045_init_verbs[] = {
{ } /* end */
};
-static struct hda_verb cxt5045_benq_init_verbs[] = {
+static const struct hda_verb cxt5045_benq_init_verbs[] = {
/* Internal Mic, Mic */
{0x12, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN|AC_PINCTL_VREF_80 },
{0x14, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN|AC_PINCTL_VREF_80 },
@@ -901,13 +826,13 @@ static struct hda_verb cxt5045_benq_init_verbs[] = {
{ } /* end */
};
-static struct hda_verb cxt5045_hp_sense_init_verbs[] = {
+static const struct hda_verb cxt5045_hp_sense_init_verbs[] = {
/* pin sensing on HP jack */
{0x11, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | CONEXANT_HP_EVENT},
{ } /* end */
};
-static struct hda_verb cxt5045_mic_sense_init_verbs[] = {
+static const struct hda_verb cxt5045_mic_sense_init_verbs[] = {
/* pin sensing on HP jack */
{0x12, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | CONEXANT_MIC_EVENT},
{ } /* end */
@@ -917,7 +842,7 @@ static struct hda_verb cxt5045_mic_sense_init_verbs[] = {
/* Test configuration for debugging, modelled after the ALC260 test
* configuration.
*/
-static struct hda_input_mux cxt5045_test_capture_source = {
+static const struct hda_input_mux cxt5045_test_capture_source = {
.num_items = 5,
.items = {
{ "MIXER", 0x0 },
@@ -928,15 +853,15 @@ static struct hda_input_mux cxt5045_test_capture_source = {
},
};
-static struct snd_kcontrol_new cxt5045_test_mixer[] = {
+static const struct snd_kcontrol_new cxt5045_test_mixer[] = {
/* Output controls */
HDA_CODEC_VOLUME("Speaker Playback Volume", 0x10, 0x0, HDA_OUTPUT),
HDA_CODEC_MUTE("Speaker Playback Switch", 0x10, 0x0, HDA_OUTPUT),
- HDA_CODEC_VOLUME("Node 11 Playback Volume", 0x11, 0x0, HDA_OUTPUT),
- HDA_CODEC_MUTE("Node 11 Playback Switch", 0x11, 0x0, HDA_OUTPUT),
- HDA_CODEC_VOLUME("Node 12 Playback Volume", 0x12, 0x0, HDA_OUTPUT),
- HDA_CODEC_MUTE("Node 12 Playback Switch", 0x12, 0x0, HDA_OUTPUT),
+ HDA_CODEC_VOLUME("HP-OUT Playback Volume", 0x11, 0x0, HDA_OUTPUT),
+ HDA_CODEC_MUTE("HP-OUT Playback Switch", 0x11, 0x0, HDA_OUTPUT),
+ HDA_CODEC_VOLUME("LINE1 Playback Volume", 0x12, 0x0, HDA_OUTPUT),
+ HDA_CODEC_MUTE("LINE1 Playback Switch", 0x12, 0x0, HDA_OUTPUT),
/* Modes for retasking pin widgets */
CXT_PIN_MODE("HP-OUT pin mode", 0x11, CXT_PIN_DIR_INOUT),
@@ -947,16 +872,16 @@ static struct snd_kcontrol_new cxt5045_test_mixer[] = {
/* Loopback mixer controls */
- HDA_CODEC_VOLUME("Mixer-1 Volume", 0x17, 0x0, HDA_INPUT),
- HDA_CODEC_MUTE("Mixer-1 Switch", 0x17, 0x0, HDA_INPUT),
- HDA_CODEC_VOLUME("Mixer-2 Volume", 0x17, 0x1, HDA_INPUT),
- HDA_CODEC_MUTE("Mixer-2 Switch", 0x17, 0x1, HDA_INPUT),
- HDA_CODEC_VOLUME("Mixer-3 Volume", 0x17, 0x2, HDA_INPUT),
- HDA_CODEC_MUTE("Mixer-3 Switch", 0x17, 0x2, HDA_INPUT),
- HDA_CODEC_VOLUME("Mixer-4 Volume", 0x17, 0x3, HDA_INPUT),
- HDA_CODEC_MUTE("Mixer-4 Switch", 0x17, 0x3, HDA_INPUT),
- HDA_CODEC_VOLUME("Mixer-5 Volume", 0x17, 0x4, HDA_INPUT),
- HDA_CODEC_MUTE("Mixer-5 Switch", 0x17, 0x4, HDA_INPUT),
+ HDA_CODEC_VOLUME("PCM Volume", 0x17, 0x0, HDA_INPUT),
+ HDA_CODEC_MUTE("PCM Switch", 0x17, 0x0, HDA_INPUT),
+ HDA_CODEC_VOLUME("MIC1 pin Volume", 0x17, 0x1, HDA_INPUT),
+ HDA_CODEC_MUTE("MIC1 pin Switch", 0x17, 0x1, HDA_INPUT),
+ HDA_CODEC_VOLUME("LINE1 pin Volume", 0x17, 0x2, HDA_INPUT),
+ HDA_CODEC_MUTE("LINE1 pin Switch", 0x17, 0x2, HDA_INPUT),
+ HDA_CODEC_VOLUME("HP-OUT pin Volume", 0x17, 0x3, HDA_INPUT),
+ HDA_CODEC_MUTE("HP-OUT pin Switch", 0x17, 0x3, HDA_INPUT),
+ HDA_CODEC_VOLUME("CD pin Volume", 0x17, 0x4, HDA_INPUT),
+ HDA_CODEC_MUTE("CD pin Switch", 0x17, 0x4, HDA_INPUT),
{
.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
.name = "Input Source",
@@ -965,20 +890,12 @@ static struct snd_kcontrol_new cxt5045_test_mixer[] = {
.put = conexant_mux_enum_put,
},
/* Audio input controls */
- HDA_CODEC_VOLUME("Input-1 Volume", 0x1a, 0x0, HDA_INPUT),
- HDA_CODEC_MUTE("Input-1 Switch", 0x1a, 0x0, HDA_INPUT),
- HDA_CODEC_VOLUME("Input-2 Volume", 0x1a, 0x1, HDA_INPUT),
- HDA_CODEC_MUTE("Input-2 Switch", 0x1a, 0x1, HDA_INPUT),
- HDA_CODEC_VOLUME("Input-3 Volume", 0x1a, 0x2, HDA_INPUT),
- HDA_CODEC_MUTE("Input-3 Switch", 0x1a, 0x2, HDA_INPUT),
- HDA_CODEC_VOLUME("Input-4 Volume", 0x1a, 0x3, HDA_INPUT),
- HDA_CODEC_MUTE("Input-4 Switch", 0x1a, 0x3, HDA_INPUT),
- HDA_CODEC_VOLUME("Input-5 Volume", 0x1a, 0x4, HDA_INPUT),
- HDA_CODEC_MUTE("Input-5 Switch", 0x1a, 0x4, HDA_INPUT),
+ HDA_CODEC_VOLUME("Capture Volume", 0x1a, 0x0, HDA_INPUT),
+ HDA_CODEC_MUTE("Capture Switch", 0x1a, 0x0, HDA_INPUT),
{ } /* end */
};
-static struct hda_verb cxt5045_test_init_verbs[] = {
+static const struct hda_verb cxt5045_test_init_verbs[] = {
/* Set connections */
{ 0x10, AC_VERB_SET_CONNECT_SEL, 0x0 },
{ 0x11, AC_VERB_SET_CONNECT_SEL, 0x0 },
@@ -996,10 +913,6 @@ static struct hda_verb cxt5045_test_init_verbs[] = {
{0x13, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
{0x18, AC_VERB_SET_DIGI_CONVERT_1, 0},
- /* Start with output sum widgets muted and their output gains at min */
- {0x17, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
- {0x17, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
-
/* Unmute retasking pin widget output buffers since the default
* state appears to be output. As the pin mode is changed by the
* user the pin mode control will take care of enabling the pin's
@@ -1014,11 +927,11 @@ static struct hda_verb cxt5045_test_init_verbs[] = {
/* Set ADC connection select to match default mixer setting (mic1
* pin)
*/
- {0x1a, AC_VERB_SET_CONNECT_SEL, 0x00},
- {0x17, AC_VERB_SET_CONNECT_SEL, 0x00},
+ {0x1a, AC_VERB_SET_CONNECT_SEL, 0x01},
+ {0x17, AC_VERB_SET_CONNECT_SEL, 0x01},
/* Mute all inputs to mixer widget (even unconnected ones) */
- {0x17, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)}, /* Mixer pin */
+ {0x17, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)}, /* Mixer */
{0x17, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)}, /* Mic1 pin */
{0x17, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(2)}, /* Line pin */
{0x17, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(3)}, /* HP pin */
@@ -1043,10 +956,10 @@ enum {
CXT5045_LAPTOP_MICSENSE,
CXT5045_LAPTOP_HPMICSENSE,
CXT5045_BENQ,
- CXT5045_LAPTOP_HP530,
#ifdef CONFIG_SND_DEBUG
CXT5045_TEST,
#endif
+ CXT5045_AUTO,
CXT5045_MODELS
};
@@ -1055,17 +968,13 @@ static const char * const cxt5045_models[CXT5045_MODELS] = {
[CXT5045_LAPTOP_MICSENSE] = "laptop-micsense",
[CXT5045_LAPTOP_HPMICSENSE] = "laptop-hpmicsense",
[CXT5045_BENQ] = "benq",
- [CXT5045_LAPTOP_HP530] = "laptop-hp530",
#ifdef CONFIG_SND_DEBUG
[CXT5045_TEST] = "test",
#endif
+ [CXT5045_AUTO] = "auto",
};
-static struct snd_pci_quirk cxt5045_cfg_tbl[] = {
- SND_PCI_QUIRK(0x103c, 0x30d5, "HP 530", CXT5045_LAPTOP_HP530),
- SND_PCI_QUIRK_MASK(0x103c, 0xff00, 0x3000, "HP DV Series",
- CXT5045_LAPTOP_HPSENSE),
- SND_PCI_QUIRK(0x1179, 0xff31, "Toshiba P105", CXT5045_LAPTOP_MICSENSE),
+static const struct snd_pci_quirk cxt5045_cfg_tbl[] = {
SND_PCI_QUIRK(0x152d, 0x0753, "Benq R55E", CXT5045_BENQ),
SND_PCI_QUIRK(0x1734, 0x10ad, "Fujitsu Si1520", CXT5045_LAPTOP_MICSENSE),
SND_PCI_QUIRK(0x1734, 0x10cb, "Fujitsu Si3515", CXT5045_LAPTOP_HPMICSENSE),
@@ -1085,11 +994,19 @@ static int patch_cxt5045(struct hda_codec *codec)
struct conexant_spec *spec;
int board_config;
+ board_config = snd_hda_check_board_config(codec, CXT5045_MODELS,
+ cxt5045_models,
+ cxt5045_cfg_tbl);
+ if (board_config < 0)
+ board_config = CXT5045_AUTO; /* model=auto as default */
+ if (board_config == CXT5045_AUTO)
+ return patch_conexant_auto(codec);
+
spec = kzalloc(sizeof(*spec), GFP_KERNEL);
if (!spec)
return -ENOMEM;
codec->spec = spec;
- codec->pin_amp_workaround = 1;
+ codec->single_adc_amp = 1;
spec->multiout.max_channels = 2;
spec->multiout.num_dacs = ARRAY_SIZE(cxt5045_dac_nids);
@@ -1111,9 +1028,6 @@ static int patch_cxt5045(struct hda_codec *codec)
codec->patch_ops = conexant_patch_ops;
- board_config = snd_hda_check_board_config(codec, CXT5045_MODELS,
- cxt5045_models,
- cxt5045_cfg_tbl);
switch (board_config) {
case CXT5045_LAPTOP_HPSENSE:
codec->patch_ops.unsol_event = cxt5045_hp_unsol_event;
@@ -1151,14 +1065,6 @@ static int patch_cxt5045(struct hda_codec *codec)
spec->num_mixers = 2;
codec->patch_ops.init = cxt5045_init;
break;
- case CXT5045_LAPTOP_HP530:
- codec->patch_ops.unsol_event = cxt5045_hp_unsol_event;
- spec->input_mux = &cxt5045_capture_source_hp530;
- spec->num_init_verbs = 2;
- spec->init_verbs[1] = cxt5045_hp_sense_init_verbs;
- spec->mixers[0] = cxt5045_mixers_hp530;
- codec->patch_ops.init = cxt5045_init;
- break;
#ifdef CONFIG_SND_DEBUG
case CXT5045_TEST:
spec->input_mux = &cxt5045_test_capture_source;
@@ -1187,7 +1093,7 @@ static int patch_cxt5045(struct hda_codec *codec)
}
if (spec->beep_amp)
- snd_hda_attach_beep_device(codec, spec->beep_amp);
+ snd_hda_attach_beep_device(codec, get_amp_nid_(spec->beep_amp));
return 0;
}
@@ -1196,15 +1102,15 @@ static int patch_cxt5045(struct hda_codec *codec)
/* Conexant 5047 specific */
#define CXT5047_SPDIF_OUT 0x11
-static hda_nid_t cxt5047_dac_nids[1] = { 0x10 }; /* 0x1c */
-static hda_nid_t cxt5047_adc_nids[1] = { 0x12 };
-static hda_nid_t cxt5047_capsrc_nids[1] = { 0x1a };
+static const hda_nid_t cxt5047_dac_nids[1] = { 0x10 }; /* 0x1c */
+static const hda_nid_t cxt5047_adc_nids[1] = { 0x12 };
+static const hda_nid_t cxt5047_capsrc_nids[1] = { 0x1a };
-static struct hda_channel_mode cxt5047_modes[1] = {
+static const struct hda_channel_mode cxt5047_modes[1] = {
{ 2, NULL },
};
-static struct hda_input_mux cxt5047_toshiba_capture_source = {
+static const struct hda_input_mux cxt5047_toshiba_capture_source = {
.num_items = 2,
.items = {
{ "ExtMic", 0x2 },
@@ -1256,12 +1162,12 @@ static void cxt5047_hp_automute(struct hda_codec *codec)
/* toggle input of built-in and mic jack appropriately */
static void cxt5047_hp_automic(struct hda_codec *codec)
{
- static struct hda_verb mic_jack_on[] = {
+ static const struct hda_verb mic_jack_on[] = {
{0x15, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
{0x17, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
{}
};
- static struct hda_verb mic_jack_off[] = {
+ static const struct hda_verb mic_jack_off[] = {
{0x17, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
{0x15, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
{}
@@ -1289,7 +1195,7 @@ static void cxt5047_hp_unsol_event(struct hda_codec *codec,
}
}
-static struct snd_kcontrol_new cxt5047_base_mixers[] = {
+static const struct snd_kcontrol_new cxt5047_base_mixers[] = {
HDA_CODEC_VOLUME("Mic Playback Volume", 0x19, 0x02, HDA_INPUT),
HDA_CODEC_MUTE("Mic Playback Switch", 0x19, 0x02, HDA_INPUT),
HDA_CODEC_VOLUME("Mic Boost Volume", 0x1a, 0x0, HDA_OUTPUT),
@@ -1309,19 +1215,19 @@ static struct snd_kcontrol_new cxt5047_base_mixers[] = {
{}
};
-static struct snd_kcontrol_new cxt5047_hp_spk_mixers[] = {
+static const struct snd_kcontrol_new cxt5047_hp_spk_mixers[] = {
/* See the note in cxt5047_hp_master_sw_put */
HDA_CODEC_VOLUME("Speaker Playback Volume", 0x1d, 0x01, HDA_OUTPUT),
HDA_CODEC_VOLUME("Headphone Playback Volume", 0x13, 0x00, HDA_OUTPUT),
{}
};
-static struct snd_kcontrol_new cxt5047_hp_only_mixers[] = {
+static const struct snd_kcontrol_new cxt5047_hp_only_mixers[] = {
HDA_CODEC_VOLUME("Master Playback Volume", 0x13, 0x00, HDA_OUTPUT),
{ } /* end */
};
-static struct hda_verb cxt5047_init_verbs[] = {
+static const struct hda_verb cxt5047_init_verbs[] = {
/* Line in, Mic, Built-in Mic */
{0x14, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN },
{0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN|AC_PINCTL_VREF_50 },
@@ -1348,7 +1254,7 @@ static struct hda_verb cxt5047_init_verbs[] = {
};
/* configuration for Toshiba Laptops */
-static struct hda_verb cxt5047_toshiba_init_verbs[] = {
+static const struct hda_verb cxt5047_toshiba_init_verbs[] = {
{0x13, AC_VERB_SET_EAPD_BTLENABLE, 0x0}, /* default off */
{}
};
@@ -1357,7 +1263,7 @@ static struct hda_verb cxt5047_toshiba_init_verbs[] = {
* configuration.
*/
#ifdef CONFIG_SND_DEBUG
-static struct hda_input_mux cxt5047_test_capture_source = {
+static const struct hda_input_mux cxt5047_test_capture_source = {
.num_items = 4,
.items = {
{ "LINE1 pin", 0x0 },
@@ -1367,7 +1273,7 @@ static struct hda_input_mux cxt5047_test_capture_source = {
},
};
-static struct snd_kcontrol_new cxt5047_test_mixer[] = {
+static const struct snd_kcontrol_new cxt5047_test_mixer[] = {
/* Output only controls */
HDA_CODEC_VOLUME("OutAmp-1 Volume", 0x10, 0x0, HDA_OUTPUT),
@@ -1420,7 +1326,7 @@ static struct snd_kcontrol_new cxt5047_test_mixer[] = {
{ } /* end */
};
-static struct hda_verb cxt5047_test_init_verbs[] = {
+static const struct hda_verb cxt5047_test_init_verbs[] = {
/* Enable retasking pins as output, initially without power amp */
{0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
{0x14, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
@@ -1492,6 +1398,7 @@ enum {
#ifdef CONFIG_SND_DEBUG
CXT5047_TEST,
#endif
+ CXT5047_AUTO,
CXT5047_MODELS
};
@@ -1502,9 +1409,10 @@ static const char * const cxt5047_models[CXT5047_MODELS] = {
#ifdef CONFIG_SND_DEBUG
[CXT5047_TEST] = "test",
#endif
+ [CXT5047_AUTO] = "auto",
};
-static struct snd_pci_quirk cxt5047_cfg_tbl[] = {
+static const struct snd_pci_quirk cxt5047_cfg_tbl[] = {
SND_PCI_QUIRK(0x103c, 0x30a5, "HP DV5200T/DV8000T", CXT5047_LAPTOP_HP),
SND_PCI_QUIRK_MASK(0x103c, 0xff00, 0x3000, "HP DV Series",
CXT5047_LAPTOP),
@@ -1517,6 +1425,14 @@ static int patch_cxt5047(struct hda_codec *codec)
struct conexant_spec *spec;
int board_config;
+ board_config = snd_hda_check_board_config(codec, CXT5047_MODELS,
+ cxt5047_models,
+ cxt5047_cfg_tbl);
+ if (board_config < 0)
+ board_config = CXT5047_AUTO; /* model=auto as default */
+ if (board_config == CXT5047_AUTO)
+ return patch_conexant_auto(codec);
+
spec = kzalloc(sizeof(*spec), GFP_KERNEL);
if (!spec)
return -ENOMEM;
@@ -1540,9 +1456,6 @@ static int patch_cxt5047(struct hda_codec *codec)
codec->patch_ops = conexant_patch_ops;
- board_config = snd_hda_check_board_config(codec, CXT5047_MODELS,
- cxt5047_models,
- cxt5047_cfg_tbl);
switch (board_config) {
case CXT5047_LAPTOP:
spec->num_mixers = 2;
@@ -1591,10 +1504,10 @@ static int patch_cxt5047(struct hda_codec *codec)
}
/* Conexant 5051 specific */
-static hda_nid_t cxt5051_dac_nids[1] = { 0x10 };
-static hda_nid_t cxt5051_adc_nids[2] = { 0x14, 0x15 };
+static const hda_nid_t cxt5051_dac_nids[1] = { 0x10 };
+static const hda_nid_t cxt5051_adc_nids[2] = { 0x14, 0x15 };
-static struct hda_channel_mode cxt5051_modes[1] = {
+static const struct hda_channel_mode cxt5051_modes[1] = {
{ 2, NULL },
};
@@ -1604,17 +1517,13 @@ static void cxt5051_update_speaker(struct hda_codec *codec)
unsigned int pinctl;
/* headphone pin */
pinctl = (spec->hp_present && spec->cur_eapd) ? PIN_HP : 0;
- snd_hda_codec_write(codec, 0x16, 0, AC_VERB_SET_PIN_WIDGET_CONTROL,
- pinctl);
+ snd_hda_set_pin_ctl(codec, 0x16, pinctl);
/* speaker pin */
pinctl = (!spec->hp_present && spec->cur_eapd) ? PIN_OUT : 0;
- snd_hda_codec_write(codec, 0x1a, 0, AC_VERB_SET_PIN_WIDGET_CONTROL,
- pinctl);
- /* on ideapad there is an aditional speaker (subwoofer) to mute */
+ snd_hda_set_pin_ctl(codec, 0x1a, pinctl);
+ /* on ideapad there is an additional speaker (subwoofer) to mute */
if (spec->ideapad)
- snd_hda_codec_write(codec, 0x1b, 0,
- AC_VERB_SET_PIN_WIDGET_CONTROL,
- pinctl);
+ snd_hda_set_pin_ctl(codec, 0x1b, pinctl);
}
/* turn on/off EAPD (+ mute HP) as a master switch */
@@ -1681,7 +1590,6 @@ static void cxt5051_hp_automute(struct hda_codec *codec)
static void cxt5051_hp_unsol_event(struct hda_codec *codec,
unsigned int res)
{
- int nid = (res & AC_UNSOL_RES_SUBTAG) >> 20;
switch (res >> 26) {
case CONEXANT_HP_EVENT:
cxt5051_hp_automute(codec);
@@ -1693,10 +1601,9 @@ static void cxt5051_hp_unsol_event(struct hda_codec *codec,
cxt5051_portc_automic(codec);
break;
}
- snd_hda_input_jack_report(codec, nid);
}
-static struct snd_kcontrol_new cxt5051_playback_mixers[] = {
+static const struct snd_kcontrol_new cxt5051_playback_mixers[] = {
HDA_CODEC_VOLUME("Master Playback Volume", 0x10, 0x00, HDA_OUTPUT),
{
.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
@@ -1709,17 +1616,17 @@ static struct snd_kcontrol_new cxt5051_playback_mixers[] = {
{}
};
-static struct snd_kcontrol_new cxt5051_capture_mixers[] = {
+static const struct snd_kcontrol_new cxt5051_capture_mixers[] = {
HDA_CODEC_VOLUME("Internal Mic Volume", 0x14, 0x00, HDA_INPUT),
HDA_CODEC_MUTE("Internal Mic Switch", 0x14, 0x00, HDA_INPUT),
HDA_CODEC_VOLUME("Mic Volume", 0x14, 0x01, HDA_INPUT),
HDA_CODEC_MUTE("Mic Switch", 0x14, 0x01, HDA_INPUT),
- HDA_CODEC_VOLUME("Docking Mic Volume", 0x15, 0x00, HDA_INPUT),
- HDA_CODEC_MUTE("Docking Mic Switch", 0x15, 0x00, HDA_INPUT),
+ HDA_CODEC_VOLUME("Dock Mic Volume", 0x15, 0x00, HDA_INPUT),
+ HDA_CODEC_MUTE("Dock Mic Switch", 0x15, 0x00, HDA_INPUT),
{}
};
-static struct snd_kcontrol_new cxt5051_hp_mixers[] = {
+static const struct snd_kcontrol_new cxt5051_hp_mixers[] = {
HDA_CODEC_VOLUME("Internal Mic Volume", 0x14, 0x00, HDA_INPUT),
HDA_CODEC_MUTE("Internal Mic Switch", 0x14, 0x00, HDA_INPUT),
HDA_CODEC_VOLUME("Mic Volume", 0x15, 0x00, HDA_INPUT),
@@ -1727,19 +1634,19 @@ static struct snd_kcontrol_new cxt5051_hp_mixers[] = {
{}
};
-static struct snd_kcontrol_new cxt5051_hp_dv6736_mixers[] = {
+static const struct snd_kcontrol_new cxt5051_hp_dv6736_mixers[] = {
HDA_CODEC_VOLUME("Capture Volume", 0x14, 0x00, HDA_INPUT),
HDA_CODEC_MUTE("Capture Switch", 0x14, 0x00, HDA_INPUT),
{}
};
-static struct snd_kcontrol_new cxt5051_f700_mixers[] = {
+static const struct snd_kcontrol_new cxt5051_f700_mixers[] = {
HDA_CODEC_VOLUME("Capture Volume", 0x14, 0x01, HDA_INPUT),
HDA_CODEC_MUTE("Capture Switch", 0x14, 0x01, HDA_INPUT),
{}
};
-static struct snd_kcontrol_new cxt5051_toshiba_mixers[] = {
+static const struct snd_kcontrol_new cxt5051_toshiba_mixers[] = {
HDA_CODEC_VOLUME("Internal Mic Volume", 0x14, 0x00, HDA_INPUT),
HDA_CODEC_MUTE("Internal Mic Switch", 0x14, 0x00, HDA_INPUT),
HDA_CODEC_VOLUME("Mic Volume", 0x14, 0x01, HDA_INPUT),
@@ -1747,7 +1654,7 @@ static struct snd_kcontrol_new cxt5051_toshiba_mixers[] = {
{}
};
-static struct hda_verb cxt5051_init_verbs[] = {
+static const struct hda_verb cxt5051_init_verbs[] = {
/* Line in, Mic */
{0x17, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0) | 0x03},
{0x17, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80},
@@ -1776,7 +1683,7 @@ static struct hda_verb cxt5051_init_verbs[] = {
{ } /* end */
};
-static struct hda_verb cxt5051_hp_dv6736_init_verbs[] = {
+static const struct hda_verb cxt5051_hp_dv6736_init_verbs[] = {
/* Line in, Mic */
{0x17, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0) | 0x03},
{0x17, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80},
@@ -1801,40 +1708,7 @@ static struct hda_verb cxt5051_hp_dv6736_init_verbs[] = {
{ } /* end */
};
-static struct hda_verb cxt5051_lenovo_x200_init_verbs[] = {
- /* Line in, Mic */
- {0x17, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0) | 0x03},
- {0x17, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80},
- {0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0) | 0x03},
- {0x18, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80},
- {0x1d, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN},
- {0x1d, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0) | 0x03},
- /* SPK */
- {0x1a, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
- {0x1a, AC_VERB_SET_CONNECT_SEL, 0x00},
- /* HP, Amp */
- {0x16, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP},
- {0x16, AC_VERB_SET_CONNECT_SEL, 0x00},
- /* Docking HP */
- {0x19, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP},
- {0x19, AC_VERB_SET_CONNECT_SEL, 0x00},
- /* DAC1 */
- {0x10, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
- /* Record selector: Internal mic */
- {0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0) | 0x44},
- {0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1) | 0x44},
- {0x15, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0) | 0x44},
- /* SPDIF route: PCM */
- {0x1c, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT}, /* needed for W500 Advanced Mini Dock 250410 */
- {0x1c, AC_VERB_SET_CONNECT_SEL, 0x0},
- /* EAPD */
- {0x1a, AC_VERB_SET_EAPD_BTLENABLE, 0x2}, /* default on */
- {0x16, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN|CONEXANT_HP_EVENT},
- {0x19, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN|CONEXANT_HP_EVENT},
- { } /* end */
-};
-
-static struct hda_verb cxt5051_f700_init_verbs[] = {
+static const struct hda_verb cxt5051_f700_init_verbs[] = {
/* Line in, Mic */
{0x17, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0) | 0x03},
{0x17, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80},
@@ -1865,11 +1739,9 @@ static void cxt5051_init_mic_port(struct hda_codec *codec, hda_nid_t nid,
snd_hda_codec_write(codec, nid, 0,
AC_VERB_SET_UNSOLICITED_ENABLE,
AC_USRSP_EN | event);
- snd_hda_input_jack_add(codec, nid, SND_JACK_MICROPHONE, NULL);
- snd_hda_input_jack_report(codec, nid);
}
-static struct hda_verb cxt5051_ideapad_init_verbs[] = {
+static const struct hda_verb cxt5051_ideapad_init_verbs[] = {
/* Subwoofer */
{0x1b, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
{0x1b, AC_VERB_SET_CONNECT_SEL, 0x00},
@@ -1882,7 +1754,6 @@ static int cxt5051_init(struct hda_codec *codec)
struct conexant_spec *spec = codec->spec;
conexant_init(codec);
- conexant_init_jacks(codec);
if (spec->auto_mic & AUTO_MIC_PORTB)
cxt5051_init_mic_port(codec, 0x17, CXT5051_PORTB_EVENT);
@@ -1902,10 +1773,10 @@ enum {
CXT5051_LAPTOP, /* Laptops w/ EAPD support */
CXT5051_HP, /* no docking */
CXT5051_HP_DV6736, /* HP without mic switch */
- CXT5051_LENOVO_X200, /* Lenovo X200 laptop, also used for Advanced Mini Dock 250410 */
CXT5051_F700, /* HP Compaq Presario F700 */
CXT5051_TOSHIBA, /* Toshiba M300 & co */
CXT5051_IDEAPAD, /* Lenovo IdeaPad Y430 */
+ CXT5051_AUTO, /* auto-parser */
CXT5051_MODELS
};
@@ -1913,13 +1784,13 @@ static const char *const cxt5051_models[CXT5051_MODELS] = {
[CXT5051_LAPTOP] = "laptop",
[CXT5051_HP] = "hp",
[CXT5051_HP_DV6736] = "hp-dv6736",
- [CXT5051_LENOVO_X200] = "lenovo-x200",
[CXT5051_F700] = "hp-700",
[CXT5051_TOSHIBA] = "toshiba",
[CXT5051_IDEAPAD] = "ideapad",
+ [CXT5051_AUTO] = "auto",
};
-static struct snd_pci_quirk cxt5051_cfg_tbl[] = {
+static const struct snd_pci_quirk cxt5051_cfg_tbl[] = {
SND_PCI_QUIRK(0x103c, 0x30cf, "HP DV6736", CXT5051_HP_DV6736),
SND_PCI_QUIRK(0x103c, 0x360b, "Compaq Presario CQ60", CXT5051_HP),
SND_PCI_QUIRK(0x103c, 0x30ea, "Compaq Presario F700", CXT5051_F700),
@@ -1927,7 +1798,6 @@ static struct snd_pci_quirk cxt5051_cfg_tbl[] = {
SND_PCI_QUIRK(0x14f1, 0x0101, "Conexant Reference board",
CXT5051_LAPTOP),
SND_PCI_QUIRK(0x14f1, 0x5051, "HP Spartan 1.1", CXT5051_HP),
- SND_PCI_QUIRK(0x17aa, 0x20f2, "Lenovo X200", CXT5051_LENOVO_X200),
SND_PCI_QUIRK(0x17aa, 0x3a0d, "Lenovo IdeaPad", CXT5051_IDEAPAD),
{}
};
@@ -1937,6 +1807,14 @@ static int patch_cxt5051(struct hda_codec *codec)
struct conexant_spec *spec;
int board_config;
+ board_config = snd_hda_check_board_config(codec, CXT5051_MODELS,
+ cxt5051_models,
+ cxt5051_cfg_tbl);
+ if (board_config < 0)
+ board_config = CXT5051_AUTO; /* model=auto as default */
+ if (board_config == CXT5051_AUTO)
+ return patch_conexant_auto(codec);
+
spec = kzalloc(sizeof(*spec), GFP_KERNEL);
if (!spec)
return -ENOMEM;
@@ -1967,9 +1845,6 @@ static int patch_cxt5051(struct hda_codec *codec)
codec->patch_ops.unsol_event = cxt5051_hp_unsol_event;
- board_config = snd_hda_check_board_config(codec, CXT5051_MODELS,
- cxt5051_models,
- cxt5051_cfg_tbl);
spec->auto_mic = AUTO_MIC_PORTB | AUTO_MIC_PORTC;
switch (board_config) {
case CXT5051_HP:
@@ -1980,13 +1855,6 @@ static int patch_cxt5051(struct hda_codec *codec)
spec->mixers[0] = cxt5051_hp_dv6736_mixers;
spec->auto_mic = 0;
break;
- case CXT5051_LENOVO_X200:
- spec->init_verbs[0] = cxt5051_lenovo_x200_init_verbs;
- /* Thinkpad X301 does not have S/PDIF wired and no ability
- to use a docking station. */
- if (codec->subsystem_id == 0x17aa211f)
- spec->multiout.dig_out_nid = 0;
- break;
case CXT5051_F700:
spec->init_verbs[0] = cxt5051_f700_init_verbs;
spec->mixers[0] = cxt5051_f700_mixers;
@@ -2004,24 +1872,19 @@ static int patch_cxt5051(struct hda_codec *codec)
}
if (spec->beep_amp)
- snd_hda_attach_beep_device(codec, spec->beep_amp);
+ snd_hda_attach_beep_device(codec, get_amp_nid_(spec->beep_amp));
return 0;
}
/* Conexant 5066 specific */
-static hda_nid_t cxt5066_dac_nids[1] = { 0x10 };
-static hda_nid_t cxt5066_adc_nids[3] = { 0x14, 0x15, 0x16 };
-static hda_nid_t cxt5066_capsrc_nids[1] = { 0x17 };
-static hda_nid_t cxt5066_digout_pin_nids[2] = { 0x20, 0x22 };
+static const hda_nid_t cxt5066_dac_nids[1] = { 0x10 };
+static const hda_nid_t cxt5066_adc_nids[3] = { 0x14, 0x15, 0x16 };
+static const hda_nid_t cxt5066_capsrc_nids[1] = { 0x17 };
+static const hda_nid_t cxt5066_digout_pin_nids[2] = { 0x20, 0x22 };
-/* OLPC's microphone port is DC coupled for use with external sensors,
- * therefore we use a 50% mic bias in order to center the input signal with
- * the DC input range of the codec. */
-#define CXT5066_OLPC_EXT_MIC_BIAS PIN_VREF50
-
-static struct hda_channel_mode cxt5066_modes[1] = {
+static const struct hda_channel_mode cxt5066_modes[1] = {
{ 2, NULL },
};
@@ -2035,13 +1898,13 @@ static void cxt5066_update_speaker(struct hda_codec *codec)
struct conexant_spec *spec = codec->spec;
unsigned int pinctl;
- snd_printdd("CXT5066: update speaker, hp_present=%d, cur_eapd=%d\n",
+ codec_dbg(codec,
+ "CXT5066: update speaker, hp_present=%d, cur_eapd=%d\n",
spec->hp_present, spec->cur_eapd);
/* Port A (HP) */
pinctl = (hp_port_a_present(spec) && spec->cur_eapd) ? PIN_HP : 0;
- snd_hda_codec_write(codec, 0x19, 0, AC_VERB_SET_PIN_WIDGET_CONTROL,
- pinctl);
+ snd_hda_set_pin_ctl(codec, 0x19, pinctl);
/* Port D (HP/LO) */
pinctl = spec->cur_eapd ? spec->port_d_mode : 0;
@@ -2054,13 +1917,11 @@ static void cxt5066_update_speaker(struct hda_codec *codec)
if (!hp_port_d_present(spec))
pinctl = 0;
}
- snd_hda_codec_write(codec, 0x1c, 0, AC_VERB_SET_PIN_WIDGET_CONTROL,
- pinctl);
+ snd_hda_set_pin_ctl(codec, 0x1c, pinctl);
/* CLASS_D AMP */
pinctl = (!spec->hp_present && spec->cur_eapd) ? PIN_OUT : 0;
- snd_hda_codec_write(codec, 0x1f, 0, AC_VERB_SET_PIN_WIDGET_CONTROL,
- pinctl);
+ snd_hda_set_pin_ctl(codec, 0x1f, pinctl);
}
/* turn on/off EAPD (+ mute HP) as a master switch */
@@ -2076,89 +1937,6 @@ static int cxt5066_hp_master_sw_put(struct snd_kcontrol *kcontrol,
return 1;
}
-static const struct hda_input_mux cxt5066_olpc_dc_bias = {
- .num_items = 3,
- .items = {
- { "Off", PIN_IN },
- { "50%", PIN_VREF50 },
- { "80%", PIN_VREF80 },
- },
-};
-
-static int cxt5066_set_olpc_dc_bias(struct hda_codec *codec)
-{
- struct conexant_spec *spec = codec->spec;
- /* Even though port F is the DC input, the bias is controlled on port B.
- * we also leave that port as an active input (but unselected) in DC mode
- * just in case that is necessary to make the bias setting take effect. */
- return snd_hda_codec_write_cache(codec, 0x1a, 0,
- AC_VERB_SET_PIN_WIDGET_CONTROL,
- cxt5066_olpc_dc_bias.items[spec->dc_input_bias].index);
-}
-
-/* OLPC defers mic widget control until when capture is started because the
- * microphone LED comes on as soon as these settings are put in place. if we
- * did this before recording, it would give the false indication that recording
- * is happening when it is not. */
-static void cxt5066_olpc_select_mic(struct hda_codec *codec)
-{
- struct conexant_spec *spec = codec->spec;
- if (!spec->recording)
- return;
-
- if (spec->dc_enable) {
- /* in DC mode we ignore presence detection and just use the jack
- * through our special DC port */
- const struct hda_verb enable_dc_mode[] = {
- /* disble internal mic, port C */
- {0x1b, AC_VERB_SET_PIN_WIDGET_CONTROL, 0},
-
- /* enable DC capture, port F */
- {0x1e, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN},
- {},
- };
-
- snd_hda_sequence_write(codec, enable_dc_mode);
- /* port B input disabled (and bias set) through the following call */
- cxt5066_set_olpc_dc_bias(codec);
- return;
- }
-
- /* disable DC (port F) */
- snd_hda_codec_write(codec, 0x1e, 0, AC_VERB_SET_PIN_WIDGET_CONTROL, 0);
-
- /* external mic, port B */
- snd_hda_codec_write(codec, 0x1a, 0, AC_VERB_SET_PIN_WIDGET_CONTROL,
- spec->ext_mic_present ? CXT5066_OLPC_EXT_MIC_BIAS : 0);
-
- /* internal mic, port C */
- snd_hda_codec_write(codec, 0x1b, 0, AC_VERB_SET_PIN_WIDGET_CONTROL,
- spec->ext_mic_present ? 0 : PIN_VREF80);
-}
-
-/* toggle input of built-in and mic jack appropriately */
-static void cxt5066_olpc_automic(struct hda_codec *codec)
-{
- struct conexant_spec *spec = codec->spec;
- unsigned int present;
-
- if (spec->dc_enable) /* don't do presence detection in DC mode */
- return;
-
- present = snd_hda_codec_read(codec, 0x1a, 0,
- AC_VERB_GET_PIN_SENSE, 0) & 0x80000000;
- if (present)
- snd_printdd("CXT5066: external microphone detected\n");
- else
- snd_printdd("CXT5066: external microphone absent\n");
-
- snd_hda_codec_write(codec, 0x17, 0, AC_VERB_SET_CONNECT_SEL,
- present ? 0 : 1);
- spec->ext_mic_present = !!present;
-
- cxt5066_olpc_select_mic(codec);
-}
-
/* toggle input of built-in digital mic and mic jack appropriately */
static void cxt5066_vostro_automic(struct hda_codec *codec)
{
@@ -2176,7 +1954,7 @@ static void cxt5066_vostro_automic(struct hda_codec *codec)
{0x23, AC_VERB_SET_PIN_WIDGET_CONTROL, 0},
{}
};
- static struct hda_verb ext_mic_absent[] = {
+ static const struct hda_verb ext_mic_absent[] = {
/* enable internal mic, port C */
{0x23, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN},
@@ -2190,10 +1968,10 @@ static void cxt5066_vostro_automic(struct hda_codec *codec)
present = snd_hda_jack_detect(codec, 0x1a);
if (present) {
- snd_printdd("CXT5066: external microphone detected\n");
+ codec_dbg(codec, "CXT5066: external microphone detected\n");
snd_hda_sequence_write(codec, ext_mic_present);
} else {
- snd_printdd("CXT5066: external microphone absent\n");
+ codec_dbg(codec, "CXT5066: external microphone absent\n");
snd_hda_sequence_write(codec, ext_mic_absent);
}
}
@@ -2209,7 +1987,7 @@ static void cxt5066_ideapad_automic(struct hda_codec *codec)
{0x23, AC_VERB_SET_PIN_WIDGET_CONTROL, 0},
{}
};
- static struct hda_verb ext_mic_absent[] = {
+ static const struct hda_verb ext_mic_absent[] = {
{0x14, AC_VERB_SET_CONNECT_SEL, 2},
{0x23, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN},
{0x1b, AC_VERB_SET_PIN_WIDGET_CONTROL, 0},
@@ -2218,10 +1996,10 @@ static void cxt5066_ideapad_automic(struct hda_codec *codec)
present = snd_hda_jack_detect(codec, 0x1b);
if (present) {
- snd_printdd("CXT5066: external microphone detected\n");
+ codec_dbg(codec, "CXT5066: external microphone detected\n");
snd_hda_sequence_write(codec, ext_mic_present);
} else {
- snd_printdd("CXT5066: external microphone absent\n");
+ codec_dbg(codec, "CXT5066: external microphone absent\n");
snd_hda_sequence_write(codec, ext_mic_absent);
}
}
@@ -2233,7 +2011,7 @@ static void cxt5066_asus_automic(struct hda_codec *codec)
unsigned int present;
present = snd_hda_jack_detect(codec, 0x1b);
- snd_printdd("CXT5066: external microphone present=%d\n", present);
+ codec_dbg(codec, "CXT5066: external microphone present=%d\n", present);
snd_hda_codec_write(codec, 0x17, 0, AC_VERB_SET_CONNECT_SEL,
present ? 1 : 0);
}
@@ -2245,7 +2023,7 @@ static void cxt5066_hp_laptop_automic(struct hda_codec *codec)
unsigned int present;
present = snd_hda_jack_detect(codec, 0x1b);
- snd_printdd("CXT5066: external microphone present=%d\n", present);
+ codec_dbg(codec, "CXT5066: external microphone present=%d\n", present);
snd_hda_codec_write(codec, 0x17, 0, AC_VERB_SET_CONNECT_SEL,
present ? 1 : 3);
}
@@ -2257,7 +2035,7 @@ static void cxt5066_thinkpad_automic(struct hda_codec *codec)
{
unsigned int ext_present, dock_present;
- static struct hda_verb ext_mic_present[] = {
+ static const struct hda_verb ext_mic_present[] = {
{0x14, AC_VERB_SET_CONNECT_SEL, 0},
{0x17, AC_VERB_SET_CONNECT_SEL, 1},
{0x1b, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80},
@@ -2265,7 +2043,7 @@ static void cxt5066_thinkpad_automic(struct hda_codec *codec)
{0x1a, AC_VERB_SET_PIN_WIDGET_CONTROL, 0},
{}
};
- static struct hda_verb dock_mic_present[] = {
+ static const struct hda_verb dock_mic_present[] = {
{0x14, AC_VERB_SET_CONNECT_SEL, 0},
{0x17, AC_VERB_SET_CONNECT_SEL, 0},
{0x1a, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80},
@@ -2273,7 +2051,7 @@ static void cxt5066_thinkpad_automic(struct hda_codec *codec)
{0x1b, AC_VERB_SET_PIN_WIDGET_CONTROL, 0},
{}
};
- static struct hda_verb ext_mic_absent[] = {
+ static const struct hda_verb ext_mic_absent[] = {
{0x14, AC_VERB_SET_CONNECT_SEL, 2},
{0x23, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN},
{0x1b, AC_VERB_SET_PIN_WIDGET_CONTROL, 0},
@@ -2284,13 +2062,13 @@ static void cxt5066_thinkpad_automic(struct hda_codec *codec)
ext_present = snd_hda_jack_detect(codec, 0x1b);
dock_present = snd_hda_jack_detect(codec, 0x1a);
if (ext_present) {
- snd_printdd("CXT5066: external microphone detected\n");
+ codec_dbg(codec, "CXT5066: external microphone detected\n");
snd_hda_sequence_write(codec, ext_mic_present);
} else if (dock_present) {
- snd_printdd("CXT5066: dock microphone detected\n");
+ codec_dbg(codec, "CXT5066: dock microphone detected\n");
snd_hda_sequence_write(codec, dock_mic_present);
} else {
- snd_printdd("CXT5066: external microphone absent\n");
+ codec_dbg(codec, "CXT5066: external microphone absent\n");
snd_hda_sequence_write(codec, ext_mic_absent);
}
}
@@ -2309,7 +2087,7 @@ static void cxt5066_hp_automute(struct hda_codec *codec)
spec->hp_present = portA ? HP_PRESENT_PORT_A : 0;
spec->hp_present |= portD ? HP_PRESENT_PORT_D : 0;
- snd_printdd("CXT5066: hp automute portA=%x portD=%x present=%d\n",
+ codec_dbg(codec, "CXT5066: hp automute portA=%x portD=%x present=%d\n",
portA, portD, spec->hp_present);
cxt5066_update_speaker(codec);
}
@@ -2332,26 +2110,9 @@ static void cxt5066_automic(struct hda_codec *codec)
}
/* unsolicited event for jack sensing */
-static void cxt5066_olpc_unsol_event(struct hda_codec *codec, unsigned int res)
-{
- struct conexant_spec *spec = codec->spec;
- snd_printdd("CXT5066: unsol event %x (%x)\n", res, res >> 26);
- switch (res >> 26) {
- case CONEXANT_HP_EVENT:
- cxt5066_hp_automute(codec);
- break;
- case CONEXANT_MIC_EVENT:
- /* ignore mic events in DC mode; we're always using the jack */
- if (!spec->dc_enable)
- cxt5066_olpc_automic(codec);
- break;
- }
-}
-
-/* unsolicited event for jack sensing */
static void cxt5066_unsol_event(struct hda_codec *codec, unsigned int res)
{
- snd_printdd("CXT5066: unsol event %x (%x)\n", res, res >> 26);
+ codec_dbg(codec, "CXT5066: unsol event %x (%x)\n", res, res >> 26);
switch (res >> 26) {
case CONEXANT_HP_EVENT:
cxt5066_hp_automute(codec);
@@ -2418,126 +2179,12 @@ static int cxt5066_mic_boost_mux_enum_put(struct snd_kcontrol *kcontrol,
idx = imux->num_items - 1;
spec->mic_boost = idx;
- if (!spec->dc_enable)
- cxt5066_set_mic_boost(codec);
- return 1;
-}
-
-static void cxt5066_enable_dc(struct hda_codec *codec)
-{
- const struct hda_verb enable_dc_mode[] = {
- /* disable gain */
- {0x17, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
-
- /* switch to DC input */
- {0x17, AC_VERB_SET_CONNECT_SEL, 3},
- {}
- };
-
- /* configure as input source */
- snd_hda_sequence_write(codec, enable_dc_mode);
- cxt5066_olpc_select_mic(codec); /* also sets configured bias */
-}
-
-static void cxt5066_disable_dc(struct hda_codec *codec)
-{
- /* reconfigure input source */
cxt5066_set_mic_boost(codec);
- /* automic also selects the right mic if we're recording */
- cxt5066_olpc_automic(codec);
-}
-
-static int cxt5066_olpc_dc_get(struct snd_kcontrol *kcontrol,
- struct snd_ctl_elem_value *ucontrol)
-{
- struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
- struct conexant_spec *spec = codec->spec;
- ucontrol->value.integer.value[0] = spec->dc_enable;
- return 0;
-}
-
-static int cxt5066_olpc_dc_put(struct snd_kcontrol *kcontrol,
- struct snd_ctl_elem_value *ucontrol)
-{
- struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
- struct conexant_spec *spec = codec->spec;
- int dc_enable = !!ucontrol->value.integer.value[0];
-
- if (dc_enable == spec->dc_enable)
- return 0;
-
- spec->dc_enable = dc_enable;
- if (dc_enable)
- cxt5066_enable_dc(codec);
- else
- cxt5066_disable_dc(codec);
-
- return 1;
-}
-
-static int cxt5066_olpc_dc_bias_enum_info(struct snd_kcontrol *kcontrol,
- struct snd_ctl_elem_info *uinfo)
-{
- return snd_hda_input_mux_info(&cxt5066_olpc_dc_bias, uinfo);
-}
-
-static int cxt5066_olpc_dc_bias_enum_get(struct snd_kcontrol *kcontrol,
- struct snd_ctl_elem_value *ucontrol)
-{
- struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
- struct conexant_spec *spec = codec->spec;
- ucontrol->value.enumerated.item[0] = spec->dc_input_bias;
- return 0;
-}
-
-static int cxt5066_olpc_dc_bias_enum_put(struct snd_kcontrol *kcontrol,
- struct snd_ctl_elem_value *ucontrol)
-{
- struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
- struct conexant_spec *spec = codec->spec;
- const struct hda_input_mux *imux = &cxt5066_analog_mic_boost;
- unsigned int idx;
-
- idx = ucontrol->value.enumerated.item[0];
- if (idx >= imux->num_items)
- idx = imux->num_items - 1;
-
- spec->dc_input_bias = idx;
- if (spec->dc_enable)
- cxt5066_set_olpc_dc_bias(codec);
return 1;
}
-static void cxt5066_olpc_capture_prepare(struct hda_codec *codec)
-{
- struct conexant_spec *spec = codec->spec;
- /* mark as recording and configure the microphone widget so that the
- * recording LED comes on. */
- spec->recording = 1;
- cxt5066_olpc_select_mic(codec);
-}
-
-static void cxt5066_olpc_capture_cleanup(struct hda_codec *codec)
-{
- struct conexant_spec *spec = codec->spec;
- const struct hda_verb disable_mics[] = {
- /* disable external mic, port B */
- {0x1a, AC_VERB_SET_PIN_WIDGET_CONTROL, 0},
-
- /* disble internal mic, port C */
- {0x1b, AC_VERB_SET_PIN_WIDGET_CONTROL, 0},
-
- /* disable DC capture, port F */
- {0x1e, AC_VERB_SET_PIN_WIDGET_CONTROL, 0},
- {},
- };
-
- snd_hda_sequence_write(codec, disable_mics);
- spec->recording = 0;
-}
-
static void conexant_check_dig_outs(struct hda_codec *codec,
- hda_nid_t *dig_pins,
+ const hda_nid_t *dig_pins,
int num_pins)
{
struct conexant_spec *spec = codec->spec;
@@ -2550,14 +2197,10 @@ static void conexant_check_dig_outs(struct hda_codec *codec,
continue;
if (snd_hda_get_connections(codec, *dig_pins, nid_loc, 1) != 1)
continue;
- if (spec->slave_dig_outs[0])
- nid_loc++;
- else
- nid_loc = spec->slave_dig_outs;
}
}
-static struct hda_input_mux cxt5066_capture_source = {
+static const struct hda_input_mux cxt5066_capture_source = {
.num_items = 4,
.items = {
{ "Mic B", 0 },
@@ -2567,7 +2210,7 @@ static struct hda_input_mux cxt5066_capture_source = {
},
};
-static struct hda_bind_ctls cxt5066_bind_capture_vol_others = {
+static const struct hda_bind_ctls cxt5066_bind_capture_vol_others = {
.ops = &snd_hda_bind_vol,
.values = {
HDA_COMPOSE_AMP_VAL(0x14, 3, 0, HDA_INPUT),
@@ -2576,7 +2219,7 @@ static struct hda_bind_ctls cxt5066_bind_capture_vol_others = {
},
};
-static struct hda_bind_ctls cxt5066_bind_capture_sw_others = {
+static const struct hda_bind_ctls cxt5066_bind_capture_sw_others = {
.ops = &snd_hda_bind_sw,
.values = {
HDA_COMPOSE_AMP_VAL(0x14, 3, 0, HDA_INPUT),
@@ -2585,49 +2228,12 @@ static struct hda_bind_ctls cxt5066_bind_capture_sw_others = {
},
};
-static struct snd_kcontrol_new cxt5066_mixer_master[] = {
+static const struct snd_kcontrol_new cxt5066_mixer_master[] = {
HDA_CODEC_VOLUME("Master Playback Volume", 0x10, 0x00, HDA_OUTPUT),
{}
};
-static struct snd_kcontrol_new cxt5066_mixer_master_olpc[] = {
- {
- .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
- .name = "Master Playback Volume",
- .access = SNDRV_CTL_ELEM_ACCESS_READWRITE |
- SNDRV_CTL_ELEM_ACCESS_TLV_READ |
- SNDRV_CTL_ELEM_ACCESS_TLV_CALLBACK,
- .subdevice = HDA_SUBDEV_AMP_FLAG,
- .info = snd_hda_mixer_amp_volume_info,
- .get = snd_hda_mixer_amp_volume_get,
- .put = snd_hda_mixer_amp_volume_put,
- .tlv = { .c = snd_hda_mixer_amp_tlv },
- /* offset by 28 volume steps to limit minimum gain to -46dB */
- .private_value =
- HDA_COMPOSE_AMP_VAL_OFS(0x10, 3, 0, HDA_OUTPUT, 28),
- },
- {}
-};
-
-static struct snd_kcontrol_new cxt5066_mixer_olpc_dc[] = {
- {
- .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
- .name = "DC Mode Enable Switch",
- .info = snd_ctl_boolean_mono_info,
- .get = cxt5066_olpc_dc_get,
- .put = cxt5066_olpc_dc_put,
- },
- {
- .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
- .name = "DC Input Bias Enum",
- .info = cxt5066_olpc_dc_bias_enum_info,
- .get = cxt5066_olpc_dc_bias_enum_get,
- .put = cxt5066_olpc_dc_bias_enum_put,
- },
- {}
-};
-
-static struct snd_kcontrol_new cxt5066_mixers[] = {
+static const struct snd_kcontrol_new cxt5066_mixers[] = {
{
.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
.name = "Master Playback Switch",
@@ -2650,7 +2256,7 @@ static struct snd_kcontrol_new cxt5066_mixers[] = {
{}
};
-static struct snd_kcontrol_new cxt5066_vostro_mixers[] = {
+static const struct snd_kcontrol_new cxt5066_vostro_mixers[] = {
{
.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
.name = "Internal Mic Boost Capture Enum",
@@ -2662,7 +2268,7 @@ static struct snd_kcontrol_new cxt5066_vostro_mixers[] = {
{}
};
-static struct hda_verb cxt5066_init_verbs[] = {
+static const struct hda_verb cxt5066_init_verbs[] = {
{0x1a, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80}, /* Port B */
{0x1b, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80}, /* Port C */
{0x1e, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN}, /* Port F */
@@ -2717,68 +2323,7 @@ static struct hda_verb cxt5066_init_verbs[] = {
{ } /* end */
};
-static struct hda_verb cxt5066_init_verbs_olpc[] = {
- /* Port A: headphones */
- {0x19, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP},
- {0x19, AC_VERB_SET_CONNECT_SEL, 0x00}, /* DAC1 */
-
- /* Port B: external microphone */
- {0x1a, AC_VERB_SET_PIN_WIDGET_CONTROL, 0},
-
- /* Port C: internal microphone */
- {0x1b, AC_VERB_SET_PIN_WIDGET_CONTROL, 0},
-
- /* Port D: unused */
- {0x1c, AC_VERB_SET_PIN_WIDGET_CONTROL, 0},
-
- /* Port E: unused, but has primary EAPD */
- {0x1d, AC_VERB_SET_PIN_WIDGET_CONTROL, 0},
- {0x1d, AC_VERB_SET_EAPD_BTLENABLE, 0x2}, /* default on */
-
- /* Port F: external DC input through microphone port */
- {0x1e, AC_VERB_SET_PIN_WIDGET_CONTROL, 0},
-
- /* Port G: internal speakers */
- {0x1f, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
- {0x1f, AC_VERB_SET_CONNECT_SEL, 0x00}, /* DAC1 */
-
- /* DAC1 */
- {0x10, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
-
- /* DAC2: unused */
- {0x11, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
-
- {0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0) | 0x50},
- {0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
- {0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(2)},
- {0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(3)},
- {0x15, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
- {0x15, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
- {0x15, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(2)},
- {0x15, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(3)},
- {0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
- {0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
- {0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(2)},
- {0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(3)},
-
- /* Disable digital microphone port */
- {0x23, AC_VERB_SET_PIN_WIDGET_CONTROL, 0},
-
- /* Audio input selectors */
- {0x17, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE | 0x3},
- {0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE },
-
- /* Disable SPDIF */
- {0x20, AC_VERB_SET_PIN_WIDGET_CONTROL, 0},
- {0x22, AC_VERB_SET_PIN_WIDGET_CONTROL, 0},
-
- /* enable unsolicited events for Port A and B */
- {0x19, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | CONEXANT_HP_EVENT},
- {0x1a, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | CONEXANT_MIC_EVENT},
- { } /* end */
-};
-
-static struct hda_verb cxt5066_init_verbs_vostro[] = {
+static const struct hda_verb cxt5066_init_verbs_vostro[] = {
/* Port A: headphones */
{0x19, AC_VERB_SET_PIN_WIDGET_CONTROL, 0},
{0x19, AC_VERB_SET_CONNECT_SEL, 0x00}, /* DAC1 */
@@ -2839,7 +2384,7 @@ static struct hda_verb cxt5066_init_verbs_vostro[] = {
{ } /* end */
};
-static struct hda_verb cxt5066_init_verbs_ideapad[] = {
+static const struct hda_verb cxt5066_init_verbs_ideapad[] = {
{0x1a, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80}, /* Port B */
{0x1b, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80}, /* Port C */
{0x1e, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN}, /* Port F */
@@ -2889,7 +2434,7 @@ static struct hda_verb cxt5066_init_verbs_ideapad[] = {
{ } /* end */
};
-static struct hda_verb cxt5066_init_verbs_thinkpad[] = {
+static const struct hda_verb cxt5066_init_verbs_thinkpad[] = {
{0x1e, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN}, /* Port F */
{0x1d, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN}, /* Port E */
@@ -2947,13 +2492,13 @@ static struct hda_verb cxt5066_init_verbs_thinkpad[] = {
{ } /* end */
};
-static struct hda_verb cxt5066_init_verbs_portd_lo[] = {
+static const struct hda_verb cxt5066_init_verbs_portd_lo[] = {
{0x1c, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
{ } /* end */
};
-static struct hda_verb cxt5066_init_verbs_hp_laptop[] = {
+static const struct hda_verb cxt5066_init_verbs_hp_laptop[] = {
{0x14, AC_VERB_SET_CONNECT_SEL, 0x0},
{0x19, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | CONEXANT_HP_EVENT},
{0x1b, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | CONEXANT_MIC_EVENT},
@@ -2963,7 +2508,7 @@ static struct hda_verb cxt5066_init_verbs_hp_laptop[] = {
/* initialize jack-sensing, too */
static int cxt5066_init(struct hda_codec *codec)
{
- snd_printdd("CXT5066: init\n");
+ codec_dbg(codec, "CXT5066: init\n");
conexant_init(codec);
if (codec->patch_ops.unsol_event) {
cxt5066_hp_automute(codec);
@@ -2973,71 +2518,48 @@ static int cxt5066_init(struct hda_codec *codec)
return 0;
}
-static int cxt5066_olpc_init(struct hda_codec *codec)
-{
- struct conexant_spec *spec = codec->spec;
- snd_printdd("CXT5066: init\n");
- conexant_init(codec);
- cxt5066_hp_automute(codec);
- if (!spec->dc_enable) {
- cxt5066_set_mic_boost(codec);
- cxt5066_olpc_automic(codec);
- } else {
- cxt5066_enable_dc(codec);
- }
- return 0;
-}
-
enum {
CXT5066_LAPTOP, /* Laptops w/ EAPD support */
CXT5066_DELL_LAPTOP, /* Dell Laptop */
- CXT5066_OLPC_XO_1_5, /* OLPC XO 1.5 */
CXT5066_DELL_VOSTRO, /* Dell Vostro 1015i */
CXT5066_IDEAPAD, /* Lenovo IdeaPad U150 */
CXT5066_THINKPAD, /* Lenovo ThinkPad T410s, others? */
CXT5066_ASUS, /* Asus K52JU, Lenovo G560 - Int mic at 0x1a and Ext mic at 0x1b */
CXT5066_HP_LAPTOP, /* HP Laptop */
+ CXT5066_AUTO, /* BIOS auto-parser */
CXT5066_MODELS
};
static const char * const cxt5066_models[CXT5066_MODELS] = {
[CXT5066_LAPTOP] = "laptop",
[CXT5066_DELL_LAPTOP] = "dell-laptop",
- [CXT5066_OLPC_XO_1_5] = "olpc-xo-1_5",
[CXT5066_DELL_VOSTRO] = "dell-vostro",
[CXT5066_IDEAPAD] = "ideapad",
[CXT5066_THINKPAD] = "thinkpad",
[CXT5066_ASUS] = "asus",
[CXT5066_HP_LAPTOP] = "hp-laptop",
+ [CXT5066_AUTO] = "auto",
};
-static struct snd_pci_quirk cxt5066_cfg_tbl[] = {
+static const struct snd_pci_quirk cxt5066_cfg_tbl[] = {
SND_PCI_QUIRK_MASK(0x1025, 0xff00, 0x0400, "Acer", CXT5066_IDEAPAD),
SND_PCI_QUIRK(0x1028, 0x02d8, "Dell Vostro", CXT5066_DELL_VOSTRO),
SND_PCI_QUIRK(0x1028, 0x02f5, "Dell Vostro 320", CXT5066_IDEAPAD),
SND_PCI_QUIRK(0x1028, 0x0401, "Dell Vostro 1014", CXT5066_DELL_VOSTRO),
- SND_PCI_QUIRK(0x1028, 0x0402, "Dell Vostro", CXT5066_DELL_VOSTRO),
SND_PCI_QUIRK(0x1028, 0x0408, "Dell Inspiron One 19T", CXT5066_IDEAPAD),
SND_PCI_QUIRK(0x1028, 0x050f, "Dell Inspiron", CXT5066_IDEAPAD),
- SND_PCI_QUIRK(0x1028, 0x0510, "Dell Vostro", CXT5066_IDEAPAD),
SND_PCI_QUIRK(0x103c, 0x360b, "HP G60", CXT5066_HP_LAPTOP),
SND_PCI_QUIRK(0x1043, 0x13f3, "Asus A52J", CXT5066_ASUS),
SND_PCI_QUIRK(0x1043, 0x1643, "Asus K52JU", CXT5066_ASUS),
SND_PCI_QUIRK(0x1043, 0x1993, "Asus U50F", CXT5066_ASUS),
SND_PCI_QUIRK(0x1179, 0xff1e, "Toshiba Satellite C650D", CXT5066_IDEAPAD),
- SND_PCI_QUIRK(0x1179, 0xff50, "Toshiba Satellite P500-PSPGSC-01800T", CXT5066_OLPC_XO_1_5),
- SND_PCI_QUIRK(0x1179, 0xffe0, "Toshiba Satellite Pro T130-15F", CXT5066_OLPC_XO_1_5),
SND_PCI_QUIRK(0x14f1, 0x0101, "Conexant Reference board",
CXT5066_LAPTOP),
- SND_PCI_QUIRK(0x152d, 0x0833, "OLPC XO-1.5", CXT5066_OLPC_XO_1_5),
SND_PCI_QUIRK(0x17aa, 0x20f2, "Lenovo T400s", CXT5066_THINKPAD),
SND_PCI_QUIRK(0x17aa, 0x21c5, "Thinkpad Edge 13", CXT5066_THINKPAD),
SND_PCI_QUIRK(0x17aa, 0x21c6, "Thinkpad Edge 13", CXT5066_ASUS),
- SND_PCI_QUIRK(0x17aa, 0x215e, "Lenovo Thinkpad", CXT5066_THINKPAD),
- SND_PCI_QUIRK(0x17aa, 0x21da, "Lenovo X220", CXT5066_THINKPAD),
- SND_PCI_QUIRK(0x17aa, 0x21db, "Lenovo X220-tablet", CXT5066_THINKPAD),
+ SND_PCI_QUIRK(0x17aa, 0x3a0d, "Lenovo U350", CXT5066_ASUS),
SND_PCI_QUIRK(0x17aa, 0x38af, "Lenovo G560", CXT5066_ASUS),
- SND_PCI_QUIRK_VENDOR(0x17aa, "Lenovo", CXT5066_IDEAPAD), /* Fallback for Lenovos without dock mic */
{}
};
@@ -3046,6 +2568,13 @@ static int patch_cxt5066(struct hda_codec *codec)
struct conexant_spec *spec;
int board_config;
+ board_config = snd_hda_check_board_config(codec, CXT5066_MODELS,
+ cxt5066_models, cxt5066_cfg_tbl);
+ if (board_config < 0)
+ board_config = CXT5066_AUTO; /* model=auto as default */
+ if (board_config == CXT5066_AUTO)
+ return patch_conexant_auto(codec);
+
spec = kzalloc(sizeof(*spec), GFP_KERNEL);
if (!spec)
return -ENOMEM;
@@ -3076,8 +2605,6 @@ static int patch_cxt5066(struct hda_codec *codec)
set_beep_amp(spec, 0x13, 0, HDA_OUTPUT);
- board_config = snd_hda_check_board_config(codec, CXT5066_MODELS,
- cxt5066_models, cxt5066_cfg_tbl);
switch (board_config) {
default:
case CXT5066_LAPTOP:
@@ -3113,32 +2640,11 @@ static int patch_cxt5066(struct hda_codec *codec)
spec->mic_boost = 3; /* default 30dB gain */
break;
- case CXT5066_OLPC_XO_1_5:
- codec->patch_ops.init = cxt5066_olpc_init;
- codec->patch_ops.unsol_event = cxt5066_olpc_unsol_event;
- spec->init_verbs[0] = cxt5066_init_verbs_olpc;
- spec->mixers[spec->num_mixers++] = cxt5066_mixer_master_olpc;
- spec->mixers[spec->num_mixers++] = cxt5066_mixer_olpc_dc;
- spec->mixers[spec->num_mixers++] = cxt5066_mixers;
- spec->port_d_mode = 0;
- spec->mic_boost = 3; /* default 30dB gain */
-
- /* no S/PDIF out */
- spec->multiout.dig_out_nid = 0;
-
- /* input source automatically selected */
- spec->input_mux = NULL;
-
- /* our capture hooks which allow us to turn on the microphone LED
- * at the right time */
- spec->capture_prepare = cxt5066_olpc_capture_prepare;
- spec->capture_cleanup = cxt5066_olpc_capture_cleanup;
- break;
case CXT5066_DELL_VOSTRO:
codec->patch_ops.init = cxt5066_init;
codec->patch_ops.unsol_event = cxt5066_unsol_event;
spec->init_verbs[0] = cxt5066_init_verbs_vostro;
- spec->mixers[spec->num_mixers++] = cxt5066_mixer_master_olpc;
+ spec->mixers[spec->num_mixers++] = cxt5066_mixer_master;
spec->mixers[spec->num_mixers++] = cxt5066_mixers;
spec->mixers[spec->num_mixers++] = cxt5066_vostro_mixers;
spec->port_d_mode = 0;
@@ -3186,651 +2692,812 @@ static int patch_cxt5066(struct hda_codec *codec)
}
if (spec->beep_amp)
- snd_hda_attach_beep_device(codec, spec->beep_amp);
+ snd_hda_attach_beep_device(codec, get_amp_nid_(spec->beep_amp));
return 0;
}
+#endif /* ENABLE_CXT_STATIC_QUIRKS */
+
+
/*
* Automatic parser for CX20641 & co
*/
-static hda_nid_t cx_auto_adc_nids[] = { 0x14 };
-
-/* get the connection index of @nid in the widget @mux */
-static int get_connection_index(struct hda_codec *codec, hda_nid_t mux,
- hda_nid_t nid)
-{
- hda_nid_t conn[HDA_MAX_NUM_INPUTS];
- int i, nums;
-
- nums = snd_hda_get_connections(codec, mux, conn, ARRAY_SIZE(conn));
- for (i = 0; i < nums; i++)
- if (conn[i] == nid)
- return i;
- return -1;
-}
-
-/* get an unassigned DAC from the given list.
- * Return the nid if found and reduce the DAC list, or return zero if
- * not found
- */
-static hda_nid_t get_unassigned_dac(struct hda_codec *codec, hda_nid_t pin,
- hda_nid_t *dacs, int *num_dacs)
+#ifdef CONFIG_SND_HDA_INPUT_BEEP
+static void cx_auto_parse_beep(struct hda_codec *codec)
{
- int i, nums = *num_dacs;
- hda_nid_t ret = 0;
+ struct conexant_spec *spec = codec->spec;
+ hda_nid_t nid, end_nid;
- for (i = 0; i < nums; i++) {
- if (get_connection_index(codec, pin, dacs[i]) >= 0) {
- ret = dacs[i];
+ end_nid = codec->start_nid + codec->num_nodes;
+ for (nid = codec->start_nid; nid < end_nid; nid++)
+ if (get_wcaps_type(get_wcaps(codec, nid)) == AC_WID_BEEP) {
+ set_beep_amp(spec, nid, 0, HDA_OUTPUT);
break;
}
- }
- if (!ret)
- return 0;
- if (--nums > 0)
- memmove(dacs, dacs + 1, nums * sizeof(hda_nid_t));
- *num_dacs = nums;
- return ret;
}
+#else
+#define cx_auto_parse_beep(codec)
+#endif
-#define MAX_AUTO_DACS 5
-
-/* fill analog DAC list from the widget tree */
-static int fill_cx_auto_dacs(struct hda_codec *codec, hda_nid_t *dacs)
+/* parse EAPDs */
+static void cx_auto_parse_eapd(struct hda_codec *codec)
{
+ struct conexant_spec *spec = codec->spec;
hda_nid_t nid, end_nid;
- int nums = 0;
end_nid = codec->start_nid + codec->num_nodes;
for (nid = codec->start_nid; nid < end_nid; nid++) {
- unsigned int wcaps = get_wcaps(codec, nid);
- unsigned int type = get_wcaps_type(wcaps);
- if (type == AC_WID_AUD_OUT && !(wcaps & AC_WCAP_DIGITAL)) {
- dacs[nums++] = nid;
- if (nums >= MAX_AUTO_DACS)
- break;
- }
+ if (get_wcaps_type(get_wcaps(codec, nid)) != AC_WID_PIN)
+ continue;
+ if (!(snd_hda_query_pin_caps(codec, nid) & AC_PINCAP_EAPD))
+ continue;
+ spec->eapds[spec->num_eapds++] = nid;
+ if (spec->num_eapds >= ARRAY_SIZE(spec->eapds))
+ break;
}
- return nums;
+
+ /* NOTE: below is a wild guess; if we have more than two EAPDs,
+ * it's a new chip, where EAPDs are supposed to be associated to
+ * pins, and we can control EAPD per pin.
+ * OTOH, if only one or two EAPDs are found, it's an old chip,
+ * thus it might control over all pins.
+ */
+ if (spec->num_eapds > 2)
+ spec->dynamic_eapd = 1;
}
-/* fill pin_dac_pair list from the pin and dac list */
-static int fill_dacs_for_pins(struct hda_codec *codec, hda_nid_t *pins,
- int num_pins, hda_nid_t *dacs, int *rest,
- struct pin_dac_pair *filled, int type)
+static void cx_auto_turn_eapd(struct hda_codec *codec, int num_pins,
+ hda_nid_t *pins, bool on)
{
- int i, nums;
-
- nums = 0;
+ int i;
for (i = 0; i < num_pins; i++) {
- filled[nums].pin = pins[i];
- filled[nums].type = type;
- filled[nums].dac = get_unassigned_dac(codec, pins[i], dacs, rest);
- nums++;
+ if (snd_hda_query_pin_caps(codec, pins[i]) & AC_PINCAP_EAPD)
+ snd_hda_codec_write(codec, pins[i], 0,
+ AC_VERB_SET_EAPD_BTLENABLE,
+ on ? 0x02 : 0);
}
- return nums;
}
-/* parse analog output paths */
-static void cx_auto_parse_output(struct hda_codec *codec)
+/* turn on/off EAPD according to Master switch */
+static void cx_auto_vmaster_hook(void *private_data, int enabled)
{
+ struct hda_codec *codec = private_data;
struct conexant_spec *spec = codec->spec;
- struct auto_pin_cfg *cfg = &spec->autocfg;
- hda_nid_t dacs[MAX_AUTO_DACS];
- int i, j, nums, rest;
-
- rest = fill_cx_auto_dacs(codec, dacs);
- /* parse all analog output pins */
- nums = fill_dacs_for_pins(codec, cfg->line_out_pins, cfg->line_outs,
- dacs, &rest, spec->dac_info,
- AUTO_PIN_LINE_OUT);
- nums += fill_dacs_for_pins(codec, cfg->hp_pins, cfg->hp_outs,
- dacs, &rest, spec->dac_info + nums,
- AUTO_PIN_HP_OUT);
- nums += fill_dacs_for_pins(codec, cfg->speaker_pins, cfg->speaker_outs,
- dacs, &rest, spec->dac_info + nums,
- AUTO_PIN_SPEAKER_OUT);
- spec->dac_info_filled = nums;
- /* fill multiout struct */
- for (i = 0; i < nums; i++) {
- hda_nid_t dac = spec->dac_info[i].dac;
- if (!dac)
- continue;
- switch (spec->dac_info[i].type) {
- case AUTO_PIN_LINE_OUT:
- spec->private_dac_nids[spec->multiout.num_dacs] = dac;
- spec->multiout.num_dacs++;
- break;
- case AUTO_PIN_HP_OUT:
- case AUTO_PIN_SPEAKER_OUT:
- if (!spec->multiout.hp_nid) {
- spec->multiout.hp_nid = dac;
- break;
- }
- for (j = 0; j < ARRAY_SIZE(spec->multiout.extra_out_nid); j++)
- if (!spec->multiout.extra_out_nid[j]) {
- spec->multiout.extra_out_nid[j] = dac;
- break;
- }
- break;
- }
- }
- spec->multiout.dac_nids = spec->private_dac_nids;
- spec->multiout.max_channels = spec->multiout.num_dacs * 2;
- if (cfg->hp_outs > 0)
- spec->auto_mute = 1;
- spec->vmaster_nid = spec->private_dac_nids[0];
+ cx_auto_turn_eapd(codec, spec->num_eapds, spec->eapds, enabled);
}
-/* auto-mute/unmute speaker and line outs according to headphone jack */
-static void cx_auto_hp_automute(struct hda_codec *codec)
+static int cx_auto_build_controls(struct hda_codec *codec)
{
- struct conexant_spec *spec = codec->spec;
- struct auto_pin_cfg *cfg = &spec->autocfg;
- int i, present;
+ int err;
- if (!spec->auto_mute)
- return;
- present = 0;
- for (i = 0; i < cfg->hp_outs; i++) {
- if (snd_hda_jack_detect(codec, cfg->hp_pins[i])) {
- present = 1;
- break;
- }
- }
- for (i = 0; i < cfg->line_outs; i++) {
- snd_hda_codec_write(codec, cfg->line_out_pins[i], 0,
- AC_VERB_SET_PIN_WIDGET_CONTROL,
- present ? 0 : PIN_OUT);
- }
- for (i = 0; !present && i < cfg->line_outs; i++)
- if (snd_hda_jack_detect(codec, cfg->line_out_pins[i]))
- present = 1;
- for (i = 0; i < cfg->speaker_outs; i++) {
- snd_hda_codec_write(codec, cfg->speaker_pins[i], 0,
- AC_VERB_SET_PIN_WIDGET_CONTROL,
- present ? 0 : PIN_OUT);
- }
+ err = snd_hda_gen_build_controls(codec);
+ if (err < 0)
+ return err;
+
+ err = add_beep_ctls(codec);
+ if (err < 0)
+ return err;
+
+ return 0;
}
-/* automatic switch internal and external mic */
-static void cx_auto_automic(struct hda_codec *codec)
+static int cx_auto_init(struct hda_codec *codec)
{
struct conexant_spec *spec = codec->spec;
- struct auto_pin_cfg *cfg = &spec->autocfg;
- struct hda_input_mux *imux = &spec->private_imux;
- int ext_idx = spec->auto_mic_ext;
+ snd_hda_gen_init(codec);
+ if (!spec->dynamic_eapd)
+ cx_auto_turn_eapd(codec, spec->num_eapds, spec->eapds, true);
- if (!spec->auto_mic)
- return;
- if (snd_hda_jack_detect(codec, cfg->inputs[ext_idx].pin)) {
- snd_hda_codec_write(codec, spec->adc_nids[0], 0,
- AC_VERB_SET_CONNECT_SEL,
- imux->items[ext_idx].index);
- } else {
- snd_hda_codec_write(codec, spec->adc_nids[0], 0,
- AC_VERB_SET_CONNECT_SEL,
- imux->items[!ext_idx].index);
- }
-}
+ snd_hda_apply_fixup(codec, HDA_FIXUP_ACT_INIT);
-static void cx_auto_unsol_event(struct hda_codec *codec, unsigned int res)
-{
- int nid = (res & AC_UNSOL_RES_SUBTAG) >> 20;
- switch (res >> 26) {
- case CONEXANT_HP_EVENT:
- cx_auto_hp_automute(codec);
- snd_hda_input_jack_report(codec, nid);
- break;
- case CONEXANT_MIC_EVENT:
- cx_auto_automic(codec);
- snd_hda_input_jack_report(codec, nid);
- break;
- }
+ return 0;
}
-/* return true if it's an internal-mic pin */
-static int is_int_mic(struct hda_codec *codec, hda_nid_t pin)
-{
- unsigned int def_conf = snd_hda_codec_get_pincfg(codec, pin);
- return get_defcfg_device(def_conf) == AC_JACK_MIC_IN &&
- snd_hda_get_input_pin_attr(def_conf) == INPUT_PIN_ATTR_INT;
-}
+#define cx_auto_free snd_hda_gen_free
-/* return true if it's an external-mic pin */
-static int is_ext_mic(struct hda_codec *codec, hda_nid_t pin)
-{
- unsigned int def_conf = snd_hda_codec_get_pincfg(codec, pin);
- return get_defcfg_device(def_conf) == AC_JACK_MIC_IN &&
- snd_hda_get_input_pin_attr(def_conf) >= INPUT_PIN_ATTR_NORMAL &&
- (snd_hda_query_pin_caps(codec, pin) & AC_PINCAP_PRES_DETECT);
-}
+static const struct hda_codec_ops cx_auto_patch_ops = {
+ .build_controls = cx_auto_build_controls,
+ .build_pcms = snd_hda_gen_build_pcms,
+ .init = cx_auto_init,
+ .free = cx_auto_free,
+ .unsol_event = snd_hda_jack_unsol_event,
+#ifdef CONFIG_PM
+ .check_power_status = snd_hda_gen_check_power_status,
+#endif
+};
-/* check whether the pin config is suitable for auto-mic switching;
- * auto-mic is enabled only when one int-mic and one-ext mic exist
+/*
+ * pin fix-up
*/
-static void cx_auto_check_auto_mic(struct hda_codec *codec)
+enum {
+ CXT_PINCFG_LENOVO_X200,
+ CXT_PINCFG_LENOVO_TP410,
+ CXT_PINCFG_LEMOTE_A1004,
+ CXT_PINCFG_LEMOTE_A1205,
+ CXT_FIXUP_STEREO_DMIC,
+ CXT_FIXUP_INC_MIC_BOOST,
+ CXT_FIXUP_HEADPHONE_MIC_PIN,
+ CXT_FIXUP_HEADPHONE_MIC,
+ CXT_FIXUP_GPIO1,
+ CXT_FIXUP_THINKPAD_ACPI,
+ CXT_FIXUP_OLPC_XO,
+ CXT_FIXUP_CAP_MIX_AMP,
+ CXT_FIXUP_TOSHIBA_P105,
+ CXT_FIXUP_HP_530,
+ CXT_FIXUP_CAP_MIX_AMP_5047,
+};
+
+/* for hda_fixup_thinkpad_acpi() */
+#include "thinkpad_helper.c"
+
+static void cxt_fixup_stereo_dmic(struct hda_codec *codec,
+ const struct hda_fixup *fix, int action)
{
struct conexant_spec *spec = codec->spec;
- struct auto_pin_cfg *cfg = &spec->autocfg;
+ spec->gen.inv_dmic_split = 1;
+}
- if (is_ext_mic(codec, cfg->inputs[0].pin) &&
- is_int_mic(codec, cfg->inputs[1].pin)) {
- spec->auto_mic = 1;
- spec->auto_mic_ext = 1;
- return;
- }
- if (is_int_mic(codec, cfg->inputs[1].pin) &&
- is_ext_mic(codec, cfg->inputs[0].pin)) {
- spec->auto_mic = 1;
- spec->auto_mic_ext = 0;
+static void cxt5066_increase_mic_boost(struct hda_codec *codec,
+ const struct hda_fixup *fix, int action)
+{
+ if (action != HDA_FIXUP_ACT_PRE_PROBE)
return;
- }
+
+ snd_hda_override_amp_caps(codec, 0x17, HDA_OUTPUT,
+ (0x3 << AC_AMPCAP_OFFSET_SHIFT) |
+ (0x4 << AC_AMPCAP_NUM_STEPS_SHIFT) |
+ (0x27 << AC_AMPCAP_STEP_SIZE_SHIFT) |
+ (0 << AC_AMPCAP_MUTE_SHIFT));
}
-static void cx_auto_parse_input(struct hda_codec *codec)
+static void cxt_update_headset_mode(struct hda_codec *codec)
{
- struct conexant_spec *spec = codec->spec;
- struct auto_pin_cfg *cfg = &spec->autocfg;
- struct hda_input_mux *imux;
+ /* The verbs used in this function were tested on a Conexant CX20751/2 codec. */
int i;
+ bool mic_mode = false;
+ struct conexant_spec *spec = codec->spec;
+ struct auto_pin_cfg *cfg = &spec->gen.autocfg;
+
+ hda_nid_t mux_pin = spec->gen.imux_pins[spec->gen.cur_mux[0]];
- imux = &spec->private_imux;
- for (i = 0; i < cfg->num_inputs; i++) {
- int idx = get_connection_index(codec, spec->adc_nids[0],
- cfg->inputs[i].pin);
- if (idx >= 0) {
- const char *label;
- label = hda_get_autocfg_input_label(codec, cfg, i);
- snd_hda_add_imux_item(imux, label, idx, NULL);
+ for (i = 0; i < cfg->num_inputs; i++)
+ if (cfg->inputs[i].pin == mux_pin) {
+ mic_mode = !!cfg->inputs[i].is_headphone_mic;
+ break;
}
+
+ if (mic_mode) {
+ snd_hda_codec_write_cache(codec, 0x1c, 0, 0x410, 0x7c); /* enable merged mode for analog int-mic */
+ spec->gen.hp_jack_present = false;
+ } else {
+ snd_hda_codec_write_cache(codec, 0x1c, 0, 0x410, 0x54); /* disable merged mode for analog int-mic */
+ spec->gen.hp_jack_present = snd_hda_jack_detect(codec, spec->gen.autocfg.hp_pins[0]);
}
- if (imux->num_items == 2 && cfg->num_inputs == 2)
- cx_auto_check_auto_mic(codec);
- if (imux->num_items > 1 && !spec->auto_mic)
- spec->input_mux = imux;
+
+ snd_hda_gen_update_outputs(codec);
}
-/* get digital-input audio widget corresponding to the given pin */
-static hda_nid_t cx_auto_get_dig_in(struct hda_codec *codec, hda_nid_t pin)
+static void cxt_update_headset_mode_hook(struct hda_codec *codec,
+ struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
{
- hda_nid_t nid, end_nid;
-
- end_nid = codec->start_nid + codec->num_nodes;
- for (nid = codec->start_nid; nid < end_nid; nid++) {
- unsigned int wcaps = get_wcaps(codec, nid);
- unsigned int type = get_wcaps_type(wcaps);
- if (type == AC_WID_AUD_IN && (wcaps & AC_WCAP_DIGITAL)) {
- if (get_connection_index(codec, nid, pin) >= 0)
- return nid;
- }
- }
- return 0;
+ cxt_update_headset_mode(codec);
}
-static void cx_auto_parse_digital(struct hda_codec *codec)
+static void cxt_fixup_headphone_mic(struct hda_codec *codec,
+ const struct hda_fixup *fix, int action)
{
struct conexant_spec *spec = codec->spec;
- struct auto_pin_cfg *cfg = &spec->autocfg;
- hda_nid_t nid;
- if (cfg->dig_outs &&
- snd_hda_get_connections(codec, cfg->dig_out_pins[0], &nid, 1) == 1)
- spec->multiout.dig_out_nid = nid;
- if (cfg->dig_in_pin)
- spec->dig_in_nid = cx_auto_get_dig_in(codec, cfg->dig_in_pin);
+ switch (action) {
+ case HDA_FIXUP_ACT_PRE_PROBE:
+ spec->parse_flags |= HDA_PINCFG_HEADPHONE_MIC;
+ break;
+ case HDA_FIXUP_ACT_PROBE:
+ spec->gen.cap_sync_hook = cxt_update_headset_mode_hook;
+ spec->gen.automute_hook = cxt_update_headset_mode;
+ break;
+ case HDA_FIXUP_ACT_INIT:
+ cxt_update_headset_mode(codec);
+ break;
+ }
}
-#ifdef CONFIG_SND_HDA_INPUT_BEEP
-static void cx_auto_parse_beep(struct hda_codec *codec)
+/* OPLC XO 1.5 fixup */
+
+/* OLPC XO-1.5 supports DC input mode (e.g. for use with analog sensors)
+ * through the microphone jack.
+ * When the user enables this through a mixer switch, both internal and
+ * external microphones are disabled. Gain is fixed at 0dB. In this mode,
+ * we also allow the bias to be configured through a separate mixer
+ * control. */
+
+#define update_mic_pin(codec, nid, val) \
+ snd_hda_codec_update_cache(codec, nid, 0, \
+ AC_VERB_SET_PIN_WIDGET_CONTROL, val)
+
+static const struct hda_input_mux olpc_xo_dc_bias = {
+ .num_items = 3,
+ .items = {
+ { "Off", PIN_IN },
+ { "50%", PIN_VREF50 },
+ { "80%", PIN_VREF80 },
+ },
+};
+
+static void olpc_xo_update_mic_boost(struct hda_codec *codec)
{
struct conexant_spec *spec = codec->spec;
- hda_nid_t nid, end_nid;
+ int ch, val;
- end_nid = codec->start_nid + codec->num_nodes;
- for (nid = codec->start_nid; nid < end_nid; nid++)
- if (get_wcaps_type(get_wcaps(codec, nid)) == AC_WID_BEEP) {
- set_beep_amp(spec, nid, 0, HDA_OUTPUT);
- break;
- }
+ for (ch = 0; ch < 2; ch++) {
+ val = AC_AMP_SET_OUTPUT |
+ (ch ? AC_AMP_SET_RIGHT : AC_AMP_SET_LEFT);
+ if (!spec->dc_enable)
+ val |= snd_hda_codec_amp_read(codec, 0x17, ch, HDA_OUTPUT, 0);
+ snd_hda_codec_write(codec, 0x17, 0,
+ AC_VERB_SET_AMP_GAIN_MUTE, val);
+ }
}
-#else
-#define cx_auto_parse_beep(codec)
-#endif
-static int cx_auto_parse_auto_config(struct hda_codec *codec)
+static void olpc_xo_update_mic_pins(struct hda_codec *codec)
{
struct conexant_spec *spec = codec->spec;
- int err;
-
- err = snd_hda_parse_pin_def_config(codec, &spec->autocfg, NULL);
- if (err < 0)
- return err;
+ int cur_input, val;
+ struct nid_path *path;
- cx_auto_parse_output(codec);
- cx_auto_parse_input(codec);
- cx_auto_parse_digital(codec);
- cx_auto_parse_beep(codec);
- return 0;
-}
+ cur_input = spec->gen.input_paths[0][spec->gen.cur_mux[0]];
-static void cx_auto_turn_on_eapd(struct hda_codec *codec, int num_pins,
- hda_nid_t *pins)
-{
- int i;
- for (i = 0; i < num_pins; i++) {
- if (snd_hda_query_pin_caps(codec, pins[i]) & AC_PINCAP_EAPD)
- snd_hda_codec_write(codec, pins[i], 0,
- AC_VERB_SET_EAPD_BTLENABLE, 0x02);
+ /* Set up mic pins for port-B, C and F dynamically as the recording
+ * LED is turned on/off by these pin controls
+ */
+ if (!spec->dc_enable) {
+ /* disable DC bias path and pin for port F */
+ update_mic_pin(codec, 0x1e, 0);
+ snd_hda_activate_path(codec, spec->dc_mode_path, false, false);
+
+ /* update port B (ext mic) and C (int mic) */
+ /* OLPC defers mic widget control until when capture is
+ * started because the microphone LED comes on as soon as
+ * these settings are put in place. if we did this before
+ * recording, it would give the false indication that
+ * recording is happening when it is not.
+ */
+ update_mic_pin(codec, 0x1a, spec->recording ?
+ snd_hda_codec_get_pin_target(codec, 0x1a) : 0);
+ update_mic_pin(codec, 0x1b, spec->recording ?
+ snd_hda_codec_get_pin_target(codec, 0x1b) : 0);
+ /* enable normal mic path */
+ path = snd_hda_get_path_from_idx(codec, cur_input);
+ if (path)
+ snd_hda_activate_path(codec, path, true, false);
+ } else {
+ /* disable normal mic path */
+ path = snd_hda_get_path_from_idx(codec, cur_input);
+ if (path)
+ snd_hda_activate_path(codec, path, false, false);
+
+ /* Even though port F is the DC input, the bias is controlled
+ * on port B. We also leave that port as an active input (but
+ * unselected) in DC mode just in case that is necessary to
+ * make the bias setting take effect.
+ */
+ if (spec->recording)
+ val = olpc_xo_dc_bias.items[spec->dc_input_bias].index;
+ else
+ val = 0;
+ update_mic_pin(codec, 0x1a, val);
+ update_mic_pin(codec, 0x1b, 0);
+ /* enable DC bias path and pin */
+ update_mic_pin(codec, 0x1e, spec->recording ? PIN_IN : 0);
+ snd_hda_activate_path(codec, spec->dc_mode_path, true, false);
}
}
-static void select_connection(struct hda_codec *codec, hda_nid_t pin,
- hda_nid_t src)
+/* mic_autoswitch hook */
+static void olpc_xo_automic(struct hda_codec *codec, struct hda_jack_tbl *jack)
{
- int idx = get_connection_index(codec, pin, src);
- if (idx >= 0)
- snd_hda_codec_write(codec, pin, 0,
- AC_VERB_SET_CONNECT_SEL, idx);
+ struct conexant_spec *spec = codec->spec;
+ int saved_cached_write = codec->cached_write;
+
+ codec->cached_write = 1;
+ /* in DC mode, we don't handle automic */
+ if (!spec->dc_enable)
+ snd_hda_gen_mic_autoswitch(codec, jack);
+ olpc_xo_update_mic_pins(codec);
+ snd_hda_codec_flush_cache(codec);
+ codec->cached_write = saved_cached_write;
+ if (spec->dc_enable)
+ olpc_xo_update_mic_boost(codec);
}
-static void cx_auto_init_output(struct hda_codec *codec)
+/* pcm_capture hook */
+static void olpc_xo_capture_hook(struct hda_pcm_stream *hinfo,
+ struct hda_codec *codec,
+ struct snd_pcm_substream *substream,
+ int action)
{
struct conexant_spec *spec = codec->spec;
- struct auto_pin_cfg *cfg = &spec->autocfg;
- hda_nid_t nid;
- int i;
- for (i = 0; i < spec->multiout.num_dacs; i++)
- snd_hda_codec_write(codec, spec->multiout.dac_nids[i], 0,
- AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE);
-
- for (i = 0; i < cfg->hp_outs; i++)
- snd_hda_codec_write(codec, cfg->hp_pins[i], 0,
- AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP);
- if (spec->auto_mute) {
- for (i = 0; i < cfg->hp_outs; i++) {
- snd_hda_codec_write(codec, cfg->hp_pins[i], 0,
- AC_VERB_SET_UNSOLICITED_ENABLE,
- AC_USRSP_EN | CONEXANT_HP_EVENT);
- }
- cx_auto_hp_automute(codec);
- } else {
- for (i = 0; i < cfg->line_outs; i++)
- snd_hda_codec_write(codec, cfg->line_out_pins[i], 0,
- AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT);
- for (i = 0; i < cfg->speaker_outs; i++)
- snd_hda_codec_write(codec, cfg->speaker_pins[i], 0,
- AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT);
- }
-
- for (i = 0; i < spec->dac_info_filled; i++) {
- nid = spec->dac_info[i].dac;
- if (!nid)
- nid = spec->multiout.dac_nids[0];
- select_connection(codec, spec->dac_info[i].pin, nid);
+ /* toggle spec->recording flag and update mic pins accordingly
+ * for turning on/off LED
+ */
+ switch (action) {
+ case HDA_GEN_PCM_ACT_PREPARE:
+ spec->recording = 1;
+ olpc_xo_update_mic_pins(codec);
+ break;
+ case HDA_GEN_PCM_ACT_CLEANUP:
+ spec->recording = 0;
+ olpc_xo_update_mic_pins(codec);
+ break;
}
-
- /* turn on EAPD */
- cx_auto_turn_on_eapd(codec, cfg->line_outs, cfg->line_out_pins);
- cx_auto_turn_on_eapd(codec, cfg->hp_outs, cfg->hp_pins);
- cx_auto_turn_on_eapd(codec, cfg->speaker_outs, cfg->speaker_pins);
}
-static void cx_auto_init_input(struct hda_codec *codec)
+static int olpc_xo_dc_mode_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
{
+ struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
struct conexant_spec *spec = codec->spec;
- struct auto_pin_cfg *cfg = &spec->autocfg;
- int i;
-
- for (i = 0; i < spec->num_adc_nids; i++)
- snd_hda_codec_write(codec, spec->adc_nids[i], 0,
- AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0));
-
- for (i = 0; i < cfg->num_inputs; i++) {
- unsigned int type;
- if (cfg->inputs[i].type == AUTO_PIN_MIC)
- type = PIN_VREF80;
- else
- type = PIN_IN;
- snd_hda_codec_write(codec, cfg->inputs[i].pin, 0,
- AC_VERB_SET_PIN_WIDGET_CONTROL, type);
- }
-
- if (spec->auto_mic) {
- int ext_idx = spec->auto_mic_ext;
- snd_hda_codec_write(codec, cfg->inputs[ext_idx].pin, 0,
- AC_VERB_SET_UNSOLICITED_ENABLE,
- AC_USRSP_EN | CONEXANT_MIC_EVENT);
- cx_auto_automic(codec);
- } else {
- for (i = 0; i < spec->num_adc_nids; i++) {
- snd_hda_codec_write(codec, spec->adc_nids[i], 0,
- AC_VERB_SET_CONNECT_SEL,
- spec->private_imux.items[0].index);
- }
- }
+ ucontrol->value.integer.value[0] = spec->dc_enable;
+ return 0;
}
-static void cx_auto_init_digital(struct hda_codec *codec)
+static int olpc_xo_dc_mode_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
{
+ struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
struct conexant_spec *spec = codec->spec;
- struct auto_pin_cfg *cfg = &spec->autocfg;
+ int dc_enable = !!ucontrol->value.integer.value[0];
+
+ if (dc_enable == spec->dc_enable)
+ return 0;
- if (spec->multiout.dig_out_nid)
- snd_hda_codec_write(codec, cfg->dig_out_pins[0], 0,
- AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT);
- if (spec->dig_in_nid)
- snd_hda_codec_write(codec, cfg->dig_in_pin, 0,
- AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN);
+ spec->dc_enable = dc_enable;
+ olpc_xo_update_mic_pins(codec);
+ olpc_xo_update_mic_boost(codec);
+ return 1;
}
-static int cx_auto_init(struct hda_codec *codec)
+static int olpc_xo_dc_bias_enum_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
{
- /*snd_hda_sequence_write(codec, cx_auto_init_verbs);*/
- cx_auto_init_output(codec);
- cx_auto_init_input(codec);
- cx_auto_init_digital(codec);
+ struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
+ struct conexant_spec *spec = codec->spec;
+ ucontrol->value.enumerated.item[0] = spec->dc_input_bias;
return 0;
}
-static int cx_auto_add_volume_idx(struct hda_codec *codec, const char *basename,
- const char *dir, int cidx,
- hda_nid_t nid, int hda_dir, int amp_idx)
+static int olpc_xo_dc_bias_enum_info(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_info *uinfo)
{
- static char name[32];
- static struct snd_kcontrol_new knew[] = {
- HDA_CODEC_VOLUME(name, 0, 0, 0),
- HDA_CODEC_MUTE(name, 0, 0, 0),
- };
- static char *sfx[2] = { "Volume", "Switch" };
- int i, err;
-
- for (i = 0; i < 2; i++) {
- struct snd_kcontrol *kctl;
- knew[i].private_value = HDA_COMPOSE_AMP_VAL(nid, 3, amp_idx,
- hda_dir);
- knew[i].subdevice = HDA_SUBDEV_AMP_FLAG;
- knew[i].index = cidx;
- snprintf(name, sizeof(name), "%s%s %s", basename, dir, sfx[i]);
- kctl = snd_ctl_new1(&knew[i], codec);
- if (!kctl)
- return -ENOMEM;
- err = snd_hda_ctl_add(codec, nid, kctl);
- if (err < 0)
- return err;
- if (!(query_amp_caps(codec, nid, hda_dir) & AC_AMPCAP_MUTE))
- break;
- }
- return 0;
+ return snd_hda_input_mux_info(&olpc_xo_dc_bias, uinfo);
}
-#define cx_auto_add_volume(codec, str, dir, cidx, nid, hda_dir) \
- cx_auto_add_volume_idx(codec, str, dir, cidx, nid, hda_dir, 0)
+static int olpc_xo_dc_bias_enum_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
+ struct conexant_spec *spec = codec->spec;
+ const struct hda_input_mux *imux = &olpc_xo_dc_bias;
+ unsigned int idx;
+
+ idx = ucontrol->value.enumerated.item[0];
+ if (idx >= imux->num_items)
+ idx = imux->num_items - 1;
+ if (spec->dc_input_bias == idx)
+ return 0;
+
+ spec->dc_input_bias = idx;
+ if (spec->dc_enable)
+ olpc_xo_update_mic_pins(codec);
+ return 1;
+}
-#define cx_auto_add_pb_volume(codec, nid, str, idx) \
- cx_auto_add_volume(codec, str, " Playback", idx, nid, HDA_OUTPUT)
+static const struct snd_kcontrol_new olpc_xo_mixers[] = {
+ {
+ .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+ .name = "DC Mode Enable Switch",
+ .info = snd_ctl_boolean_mono_info,
+ .get = olpc_xo_dc_mode_get,
+ .put = olpc_xo_dc_mode_put,
+ },
+ {
+ .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+ .name = "DC Input Bias Enum",
+ .info = olpc_xo_dc_bias_enum_info,
+ .get = olpc_xo_dc_bias_enum_get,
+ .put = olpc_xo_dc_bias_enum_put,
+ },
+ {}
+};
-static int cx_auto_build_output_controls(struct hda_codec *codec)
+/* overriding mic boost put callback; update mic boost volume only when
+ * DC mode is disabled
+ */
+static int olpc_xo_mic_boost_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
{
+ struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
struct conexant_spec *spec = codec->spec;
- int i, err;
- int num_line = 0, num_hp = 0, num_spk = 0;
- static const char * const texts[3] = { "Front", "Surround", "CLFE" };
-
- if (spec->dac_info_filled == 1)
- return cx_auto_add_pb_volume(codec, spec->dac_info[0].dac,
- "Master", 0);
- for (i = 0; i < spec->dac_info_filled; i++) {
- const char *label;
- int idx, type;
- if (!spec->dac_info[i].dac)
- continue;
- type = spec->dac_info[i].type;
- if (type == AUTO_PIN_LINE_OUT)
- type = spec->autocfg.line_out_type;
- switch (type) {
- case AUTO_PIN_LINE_OUT:
- default:
- label = texts[num_line++];
- idx = 0;
- break;
- case AUTO_PIN_HP_OUT:
- label = "Headphone";
- idx = num_hp++;
- break;
- case AUTO_PIN_SPEAKER_OUT:
- label = "Speaker";
- idx = num_spk++;
- break;
- }
- err = cx_auto_add_pb_volume(codec, spec->dac_info[i].dac,
- label, idx);
- if (err < 0)
- return err;
- }
- return 0;
+ int ret = snd_hda_mixer_amp_volume_put(kcontrol, ucontrol);
+ if (ret > 0 && spec->dc_enable)
+ olpc_xo_update_mic_boost(codec);
+ return ret;
}
-static int cx_auto_build_input_controls(struct hda_codec *codec)
+static void cxt_fixup_olpc_xo(struct hda_codec *codec,
+ const struct hda_fixup *fix, int action)
{
struct conexant_spec *spec = codec->spec;
- struct auto_pin_cfg *cfg = &spec->autocfg;
- static const char *prev_label;
- int i, err, cidx, conn_len;
- hda_nid_t conn[HDA_MAX_CONNECTIONS];
-
- int multi_adc_volume = 0; /* If the ADC nid has several input volumes */
- int adc_nid = spec->adc_nids[0];
-
- conn_len = snd_hda_get_connections(codec, adc_nid, conn,
- HDA_MAX_CONNECTIONS);
- if (conn_len < 0)
- return conn_len;
-
- multi_adc_volume = cfg->num_inputs > 1 && conn_len > 1;
- if (!multi_adc_volume) {
- err = cx_auto_add_volume(codec, "Capture", "", 0, adc_nid,
- HDA_INPUT);
- if (err < 0)
- return err;
- }
+ int i;
- prev_label = NULL;
- cidx = 0;
- for (i = 0; i < cfg->num_inputs; i++) {
- hda_nid_t nid = cfg->inputs[i].pin;
- const char *label;
- int j;
- int pin_amp = get_wcaps(codec, nid) & AC_WCAP_IN_AMP;
- if (!pin_amp && !multi_adc_volume)
- continue;
+ if (action != HDA_FIXUP_ACT_PROBE)
+ return;
- label = hda_get_autocfg_input_label(codec, cfg, i);
- if (label == prev_label)
- cidx++;
- else
- cidx = 0;
- prev_label = label;
+ spec->gen.mic_autoswitch_hook = olpc_xo_automic;
+ spec->gen.pcm_capture_hook = olpc_xo_capture_hook;
+ spec->dc_mode_path = snd_hda_add_new_path(codec, 0x1e, 0x14, 0);
- if (pin_amp) {
- err = cx_auto_add_volume(codec, label, " Boost", cidx,
- nid, HDA_INPUT);
- if (err < 0)
- return err;
- }
+ snd_hda_add_new_ctls(codec, olpc_xo_mixers);
- if (!multi_adc_volume)
- continue;
- for (j = 0; j < conn_len; j++) {
- if (conn[j] == nid) {
- err = cx_auto_add_volume_idx(codec, label,
- " Capture", cidx, adc_nid, HDA_INPUT, j);
- if (err < 0)
- return err;
- break;
- }
+ /* OLPC's microphone port is DC coupled for use with external sensors,
+ * therefore we use a 50% mic bias in order to center the input signal
+ * with the DC input range of the codec.
+ */
+ snd_hda_codec_set_pin_target(codec, 0x1a, PIN_VREF50);
+
+ /* override mic boost control */
+ for (i = 0; i < spec->gen.kctls.used; i++) {
+ struct snd_kcontrol_new *kctl =
+ snd_array_elem(&spec->gen.kctls, i);
+ if (!strcmp(kctl->name, "Mic Boost Volume")) {
+ kctl->put = olpc_xo_mic_boost_put;
+ break;
}
}
- return 0;
}
-static int cx_auto_build_controls(struct hda_codec *codec)
+/*
+ * Fix max input level on mixer widget to 0dB
+ * (originally it has 0x2b steps with 0dB offset 0x14)
+ */
+static void cxt_fixup_cap_mix_amp(struct hda_codec *codec,
+ const struct hda_fixup *fix, int action)
{
- int err;
-
- err = cx_auto_build_output_controls(codec);
- if (err < 0)
- return err;
- err = cx_auto_build_input_controls(codec);
- if (err < 0)
- return err;
- return conexant_build_controls(codec);
+ snd_hda_override_amp_caps(codec, 0x17, HDA_INPUT,
+ (0x14 << AC_AMPCAP_OFFSET_SHIFT) |
+ (0x14 << AC_AMPCAP_NUM_STEPS_SHIFT) |
+ (0x05 << AC_AMPCAP_STEP_SIZE_SHIFT) |
+ (1 << AC_AMPCAP_MUTE_SHIFT));
}
-static struct hda_codec_ops cx_auto_patch_ops = {
- .build_controls = cx_auto_build_controls,
- .build_pcms = conexant_build_pcms,
- .init = cx_auto_init,
- .free = conexant_free,
- .unsol_event = cx_auto_unsol_event,
-#ifdef CONFIG_SND_HDA_POWER_SAVE
- .suspend = conexant_suspend,
-#endif
- .reboot_notify = snd_hda_shutup_pins,
+/*
+ * Fix max input level on mixer widget to 0dB
+ * (originally it has 0x1e steps with 0 dB offset 0x17)
+ */
+static void cxt_fixup_cap_mix_amp_5047(struct hda_codec *codec,
+ const struct hda_fixup *fix, int action)
+{
+ snd_hda_override_amp_caps(codec, 0x10, HDA_INPUT,
+ (0x17 << AC_AMPCAP_OFFSET_SHIFT) |
+ (0x17 << AC_AMPCAP_NUM_STEPS_SHIFT) |
+ (0x05 << AC_AMPCAP_STEP_SIZE_SHIFT) |
+ (1 << AC_AMPCAP_MUTE_SHIFT));
+}
+
+/* ThinkPad X200 & co with cxt5051 */
+static const struct hda_pintbl cxt_pincfg_lenovo_x200[] = {
+ { 0x16, 0x042140ff }, /* HP (seq# overridden) */
+ { 0x17, 0x21a11000 }, /* dock-mic */
+ { 0x19, 0x2121103f }, /* dock-HP */
+ { 0x1c, 0x21440100 }, /* dock SPDIF out */
+ {}
};
+/* ThinkPad 410/420/510/520, X201 & co with cxt5066 */
+static const struct hda_pintbl cxt_pincfg_lenovo_tp410[] = {
+ { 0x19, 0x042110ff }, /* HP (seq# overridden) */
+ { 0x1a, 0x21a190f0 }, /* dock-mic */
+ { 0x1c, 0x212140ff }, /* dock-HP */
+ {}
+};
+
+/* Lemote A1004/A1205 with cxt5066 */
+static const struct hda_pintbl cxt_pincfg_lemote[] = {
+ { 0x1a, 0x90a10020 }, /* Internal mic */
+ { 0x1b, 0x03a11020 }, /* External mic */
+ { 0x1d, 0x400101f0 }, /* Not used */
+ { 0x1e, 0x40a701f0 }, /* Not used */
+ { 0x20, 0x404501f0 }, /* Not used */
+ { 0x22, 0x404401f0 }, /* Not used */
+ { 0x23, 0x40a701f0 }, /* Not used */
+ {}
+};
+
+static const struct hda_fixup cxt_fixups[] = {
+ [CXT_PINCFG_LENOVO_X200] = {
+ .type = HDA_FIXUP_PINS,
+ .v.pins = cxt_pincfg_lenovo_x200,
+ },
+ [CXT_PINCFG_LENOVO_TP410] = {
+ .type = HDA_FIXUP_PINS,
+ .v.pins = cxt_pincfg_lenovo_tp410,
+ .chained = true,
+ .chain_id = CXT_FIXUP_THINKPAD_ACPI,
+ },
+ [CXT_PINCFG_LEMOTE_A1004] = {
+ .type = HDA_FIXUP_PINS,
+ .chained = true,
+ .chain_id = CXT_FIXUP_INC_MIC_BOOST,
+ .v.pins = cxt_pincfg_lemote,
+ },
+ [CXT_PINCFG_LEMOTE_A1205] = {
+ .type = HDA_FIXUP_PINS,
+ .v.pins = cxt_pincfg_lemote,
+ },
+ [CXT_FIXUP_STEREO_DMIC] = {
+ .type = HDA_FIXUP_FUNC,
+ .v.func = cxt_fixup_stereo_dmic,
+ },
+ [CXT_FIXUP_INC_MIC_BOOST] = {
+ .type = HDA_FIXUP_FUNC,
+ .v.func = cxt5066_increase_mic_boost,
+ },
+ [CXT_FIXUP_HEADPHONE_MIC_PIN] = {
+ .type = HDA_FIXUP_PINS,
+ .chained = true,
+ .chain_id = CXT_FIXUP_HEADPHONE_MIC,
+ .v.pins = (const struct hda_pintbl[]) {
+ { 0x18, 0x03a1913d }, /* use as headphone mic, without its own jack detect */
+ { }
+ }
+ },
+ [CXT_FIXUP_HEADPHONE_MIC] = {
+ .type = HDA_FIXUP_FUNC,
+ .v.func = cxt_fixup_headphone_mic,
+ },
+ [CXT_FIXUP_GPIO1] = {
+ .type = HDA_FIXUP_VERBS,
+ .v.verbs = (const struct hda_verb[]) {
+ { 0x01, AC_VERB_SET_GPIO_MASK, 0x01 },
+ { 0x01, AC_VERB_SET_GPIO_DIRECTION, 0x01 },
+ { 0x01, AC_VERB_SET_GPIO_DATA, 0x01 },
+ { }
+ },
+ },
+ [CXT_FIXUP_THINKPAD_ACPI] = {
+ .type = HDA_FIXUP_FUNC,
+ .v.func = hda_fixup_thinkpad_acpi,
+ },
+ [CXT_FIXUP_OLPC_XO] = {
+ .type = HDA_FIXUP_FUNC,
+ .v.func = cxt_fixup_olpc_xo,
+ },
+ [CXT_FIXUP_CAP_MIX_AMP] = {
+ .type = HDA_FIXUP_FUNC,
+ .v.func = cxt_fixup_cap_mix_amp,
+ },
+ [CXT_FIXUP_TOSHIBA_P105] = {
+ .type = HDA_FIXUP_PINS,
+ .v.pins = (const struct hda_pintbl[]) {
+ { 0x10, 0x961701f0 }, /* speaker/hp */
+ { 0x12, 0x02a1901e }, /* ext mic */
+ { 0x14, 0x95a70110 }, /* int mic */
+ {}
+ },
+ },
+ [CXT_FIXUP_HP_530] = {
+ .type = HDA_FIXUP_PINS,
+ .v.pins = (const struct hda_pintbl[]) {
+ { 0x12, 0x90a60160 }, /* int mic */
+ {}
+ },
+ .chained = true,
+ .chain_id = CXT_FIXUP_CAP_MIX_AMP,
+ },
+ [CXT_FIXUP_CAP_MIX_AMP_5047] = {
+ .type = HDA_FIXUP_FUNC,
+ .v.func = cxt_fixup_cap_mix_amp_5047,
+ },
+};
+
+static const struct snd_pci_quirk cxt5045_fixups[] = {
+ SND_PCI_QUIRK(0x103c, 0x30d5, "HP 530", CXT_FIXUP_HP_530),
+ SND_PCI_QUIRK(0x1179, 0xff31, "Toshiba P105", CXT_FIXUP_TOSHIBA_P105),
+ /* HP, Packard Bell, Fujitsu-Siemens & Lenovo laptops have
+ * really bad sound over 0dB on NID 0x17.
+ */
+ SND_PCI_QUIRK_VENDOR(0x103c, "HP", CXT_FIXUP_CAP_MIX_AMP),
+ SND_PCI_QUIRK_VENDOR(0x1631, "Packard Bell", CXT_FIXUP_CAP_MIX_AMP),
+ SND_PCI_QUIRK_VENDOR(0x1734, "Fujitsu", CXT_FIXUP_CAP_MIX_AMP),
+ SND_PCI_QUIRK_VENDOR(0x17aa, "Lenovo", CXT_FIXUP_CAP_MIX_AMP),
+ {}
+};
+
+static const struct hda_model_fixup cxt5045_fixup_models[] = {
+ { .id = CXT_FIXUP_CAP_MIX_AMP, .name = "cap-mix-amp" },
+ { .id = CXT_FIXUP_TOSHIBA_P105, .name = "toshiba-p105" },
+ { .id = CXT_FIXUP_HP_530, .name = "hp-530" },
+ {}
+};
+
+static const struct snd_pci_quirk cxt5047_fixups[] = {
+ /* HP laptops have really bad sound over 0 dB on NID 0x10.
+ */
+ SND_PCI_QUIRK_VENDOR(0x103c, "HP", CXT_FIXUP_CAP_MIX_AMP_5047),
+ {}
+};
+
+static const struct hda_model_fixup cxt5047_fixup_models[] = {
+ { .id = CXT_FIXUP_CAP_MIX_AMP_5047, .name = "cap-mix-amp" },
+ {}
+};
+
+static const struct snd_pci_quirk cxt5051_fixups[] = {
+ SND_PCI_QUIRK(0x17aa, 0x20f2, "Lenovo X200", CXT_PINCFG_LENOVO_X200),
+ {}
+};
+
+static const struct hda_model_fixup cxt5051_fixup_models[] = {
+ { .id = CXT_PINCFG_LENOVO_X200, .name = "lenovo-x200" },
+ {}
+};
+
+static const struct snd_pci_quirk cxt5066_fixups[] = {
+ SND_PCI_QUIRK(0x1025, 0x0543, "Acer Aspire One 522", CXT_FIXUP_STEREO_DMIC),
+ SND_PCI_QUIRK(0x1025, 0x054c, "Acer Aspire 3830TG", CXT_FIXUP_GPIO1),
+ SND_PCI_QUIRK(0x1043, 0x138d, "Asus", CXT_FIXUP_HEADPHONE_MIC_PIN),
+ SND_PCI_QUIRK(0x152d, 0x0833, "OLPC XO-1.5", CXT_FIXUP_OLPC_XO),
+ SND_PCI_QUIRK(0x17aa, 0x20f2, "Lenovo T400", CXT_PINCFG_LENOVO_TP410),
+ SND_PCI_QUIRK(0x17aa, 0x215e, "Lenovo T410", CXT_PINCFG_LENOVO_TP410),
+ SND_PCI_QUIRK(0x17aa, 0x215f, "Lenovo T510", CXT_PINCFG_LENOVO_TP410),
+ SND_PCI_QUIRK(0x17aa, 0x21ce, "Lenovo T420", CXT_PINCFG_LENOVO_TP410),
+ SND_PCI_QUIRK(0x17aa, 0x21cf, "Lenovo T520", CXT_PINCFG_LENOVO_TP410),
+ SND_PCI_QUIRK(0x17aa, 0x21da, "Lenovo X220", CXT_PINCFG_LENOVO_TP410),
+ SND_PCI_QUIRK(0x17aa, 0x21db, "Lenovo X220-tablet", CXT_PINCFG_LENOVO_TP410),
+ SND_PCI_QUIRK(0x17aa, 0x3975, "Lenovo U300s", CXT_FIXUP_STEREO_DMIC),
+ SND_PCI_QUIRK(0x17aa, 0x3977, "Lenovo IdeaPad U310", CXT_FIXUP_STEREO_DMIC),
+ SND_PCI_QUIRK(0x17aa, 0x397b, "Lenovo S205", CXT_FIXUP_STEREO_DMIC),
+ SND_PCI_QUIRK_VENDOR(0x17aa, "Thinkpad", CXT_FIXUP_THINKPAD_ACPI),
+ SND_PCI_QUIRK(0x1c06, 0x2011, "Lemote A1004", CXT_PINCFG_LEMOTE_A1004),
+ SND_PCI_QUIRK(0x1c06, 0x2012, "Lemote A1205", CXT_PINCFG_LEMOTE_A1205),
+ {}
+};
+
+static const struct hda_model_fixup cxt5066_fixup_models[] = {
+ { .id = CXT_FIXUP_STEREO_DMIC, .name = "stereo-dmic" },
+ { .id = CXT_FIXUP_GPIO1, .name = "gpio1" },
+ { .id = CXT_FIXUP_HEADPHONE_MIC_PIN, .name = "headphone-mic-pin" },
+ { .id = CXT_PINCFG_LENOVO_TP410, .name = "tp410" },
+ { .id = CXT_FIXUP_THINKPAD_ACPI, .name = "thinkpad" },
+ { .id = CXT_PINCFG_LEMOTE_A1004, .name = "lemote-a1004" },
+ { .id = CXT_FIXUP_OLPC_XO, .name = "olpc-xo" },
+ {}
+};
+
+/* add "fake" mute amp-caps to DACs on cx5051 so that mixer mute switches
+ * can be created (bko#42825)
+ */
+static void add_cx5051_fake_mutes(struct hda_codec *codec)
+{
+ static hda_nid_t out_nids[] = {
+ 0x10, 0x11, 0
+ };
+ hda_nid_t *p;
+
+ for (p = out_nids; *p; p++)
+ snd_hda_override_amp_caps(codec, *p, HDA_OUTPUT,
+ AC_AMPCAP_MIN_MUTE |
+ query_amp_caps(codec, *p, HDA_OUTPUT));
+}
+
static int patch_conexant_auto(struct hda_codec *codec)
{
struct conexant_spec *spec;
int err;
+ codec_info(codec, "%s: BIOS auto-probing.\n", codec->chip_name);
+
spec = kzalloc(sizeof(*spec), GFP_KERNEL);
if (!spec)
return -ENOMEM;
+ snd_hda_gen_spec_init(&spec->gen);
codec->spec = spec;
- spec->adc_nids = cx_auto_adc_nids;
- spec->num_adc_nids = ARRAY_SIZE(cx_auto_adc_nids);
- spec->capsrc_nids = spec->adc_nids;
- err = cx_auto_parse_auto_config(codec);
- if (err < 0) {
- kfree(codec->spec);
- codec->spec = NULL;
- return err;
+
+ cx_auto_parse_beep(codec);
+ cx_auto_parse_eapd(codec);
+ spec->gen.own_eapd_ctl = 1;
+ if (spec->dynamic_eapd)
+ spec->gen.vmaster_mute.hook = cx_auto_vmaster_hook;
+
+ switch (codec->vendor_id) {
+ case 0x14f15045:
+ codec->single_adc_amp = 1;
+ spec->gen.mixer_nid = 0x17;
+ spec->gen.add_stereo_mix_input = 1;
+ snd_hda_pick_fixup(codec, cxt5045_fixup_models,
+ cxt5045_fixups, cxt_fixups);
+ break;
+ case 0x14f15047:
+ codec->pin_amp_workaround = 1;
+ spec->gen.mixer_nid = 0x19;
+ spec->gen.add_stereo_mix_input = 1;
+ snd_hda_pick_fixup(codec, cxt5047_fixup_models,
+ cxt5047_fixups, cxt_fixups);
+ break;
+ case 0x14f15051:
+ add_cx5051_fake_mutes(codec);
+ codec->pin_amp_workaround = 1;
+ snd_hda_pick_fixup(codec, cxt5051_fixup_models,
+ cxt5051_fixups, cxt_fixups);
+ break;
+ default:
+ codec->pin_amp_workaround = 1;
+ snd_hda_pick_fixup(codec, cxt5066_fixup_models,
+ cxt5066_fixups, cxt_fixups);
+ break;
+ }
+
+ /* Show mute-led control only on HP laptops
+ * This is a sort of white-list: on HP laptops, EAPD corresponds
+ * only to the mute-LED without actualy amp function. Meanwhile,
+ * others may use EAPD really as an amp switch, so it might be
+ * not good to expose it blindly.
+ */
+ switch (codec->subsystem_id >> 16) {
+ case 0x103c:
+ spec->gen.vmaster_mute_enum = 1;
+ break;
}
+
+ snd_hda_apply_fixup(codec, HDA_FIXUP_ACT_PRE_PROBE);
+
+ err = snd_hda_parse_pin_defcfg(codec, &spec->gen.autocfg, NULL,
+ spec->parse_flags);
+ if (err < 0)
+ goto error;
+
+ err = snd_hda_gen_parse_auto_config(codec, &spec->gen.autocfg);
+ if (err < 0)
+ goto error;
+
codec->patch_ops = cx_auto_patch_ops;
- if (spec->beep_amp)
- snd_hda_attach_beep_device(codec, spec->beep_amp);
+
+ /* Some laptops with Conexant chips show stalls in S3 resume,
+ * which falls into the single-cmd mode.
+ * Better to make reset, then.
+ */
+ if (!codec->bus->sync_write) {
+ codec_info(codec,
+ "Enable sync_write for stable communication\n");
+ codec->bus->sync_write = 1;
+ codec->bus->allow_bus_reset = 1;
+ }
+
+ snd_hda_apply_fixup(codec, HDA_FIXUP_ACT_PROBE);
+
return 0;
+
+ error:
+ cx_auto_free(codec);
+ return err;
}
+#ifndef ENABLE_CXT_STATIC_QUIRKS
+#define patch_cxt5045 patch_conexant_auto
+#define patch_cxt5047 patch_conexant_auto
+#define patch_cxt5051 patch_conexant_auto
+#define patch_cxt5066 patch_conexant_auto
+#endif
+
/*
*/
-static struct hda_codec_preset snd_hda_preset_conexant[] = {
+static const struct hda_codec_preset snd_hda_preset_conexant[] = {
{ .id = 0x14f15045, .name = "CX20549 (Venice)",
.patch = patch_cxt5045 },
{ .id = 0x14f15047, .name = "CX20551 (Waikiki)",
@@ -3845,6 +3512,8 @@ static struct hda_codec_preset snd_hda_preset_conexant[] = {
.patch = patch_cxt5066 },
{ .id = 0x14f15069, .name = "CX20585",
.patch = patch_cxt5066 },
+ { .id = 0x14f1506c, .name = "CX20588",
+ .patch = patch_cxt5066 },
{ .id = 0x14f1506e, .name = "CX20590",
.patch = patch_cxt5066 },
{ .id = 0x14f15097, .name = "CX20631",
@@ -3863,6 +3532,20 @@ static struct hda_codec_preset snd_hda_preset_conexant[] = {
.patch = patch_conexant_auto },
{ .id = 0x14f150b9, .name = "CX20665",
.patch = patch_conexant_auto },
+ { .id = 0x14f1510f, .name = "CX20751/2",
+ .patch = patch_conexant_auto },
+ { .id = 0x14f15110, .name = "CX20751/2",
+ .patch = patch_conexant_auto },
+ { .id = 0x14f15111, .name = "CX20753/4",
+ .patch = patch_conexant_auto },
+ { .id = 0x14f15113, .name = "CX20755",
+ .patch = patch_conexant_auto },
+ { .id = 0x14f15114, .name = "CX20756",
+ .patch = patch_conexant_auto },
+ { .id = 0x14f15115, .name = "CX20757",
+ .patch = patch_conexant_auto },
+ { .id = 0x14f151d7, .name = "CX20952",
+ .patch = patch_conexant_auto },
{} /* terminator */
};
@@ -3873,6 +3556,7 @@ MODULE_ALIAS("snd-hda-codec-id:14f15066");
MODULE_ALIAS("snd-hda-codec-id:14f15067");
MODULE_ALIAS("snd-hda-codec-id:14f15068");
MODULE_ALIAS("snd-hda-codec-id:14f15069");
+MODULE_ALIAS("snd-hda-codec-id:14f1506c");
MODULE_ALIAS("snd-hda-codec-id:14f1506e");
MODULE_ALIAS("snd-hda-codec-id:14f15097");
MODULE_ALIAS("snd-hda-codec-id:14f15098");
@@ -3882,6 +3566,13 @@ MODULE_ALIAS("snd-hda-codec-id:14f150ab");
MODULE_ALIAS("snd-hda-codec-id:14f150ac");
MODULE_ALIAS("snd-hda-codec-id:14f150b8");
MODULE_ALIAS("snd-hda-codec-id:14f150b9");
+MODULE_ALIAS("snd-hda-codec-id:14f1510f");
+MODULE_ALIAS("snd-hda-codec-id:14f15110");
+MODULE_ALIAS("snd-hda-codec-id:14f15111");
+MODULE_ALIAS("snd-hda-codec-id:14f15113");
+MODULE_ALIAS("snd-hda-codec-id:14f15114");
+MODULE_ALIAS("snd-hda-codec-id:14f15115");
+MODULE_ALIAS("snd-hda-codec-id:14f151d7");
MODULE_LICENSE("GPL");
MODULE_DESCRIPTION("Conexant HD-audio codec");
diff --git a/sound/pci/hda/patch_hdmi.c b/sound/pci/hda/patch_hdmi.c
index 715615a88a8..ba4ca52072f 100644
--- a/sound/pci/hda/patch_hdmi.c
+++ b/sound/pci/hda/patch_hdmi.c
@@ -6,6 +6,7 @@
* Copyright (c) 2006 ATI Technologies Inc.
* Copyright (c) 2008 NVIDIA Corp. All rights reserved.
* Copyright (c) 2008 Wei Ni <wni@nvidia.com>
+ * Copyright (c) 2013 Anssi Hannula <anssi.hannula@iki.fi>
*
* Authors:
* Wu Fengguang <wfg@linux.intel.com>
@@ -31,56 +32,115 @@
#include <linux/init.h>
#include <linux/delay.h>
#include <linux/slab.h>
-#include <linux/moduleparam.h>
+#include <linux/module.h>
#include <sound/core.h>
+#include <sound/jack.h>
+#include <sound/asoundef.h>
+#include <sound/tlv.h>
#include "hda_codec.h"
#include "hda_local.h"
+#include "hda_jack.h"
static bool static_hdmi_pcm;
module_param(static_hdmi_pcm, bool, 0644);
MODULE_PARM_DESC(static_hdmi_pcm, "Don't restrict PCM parameters per ELD info");
-/*
- * The HDMI/DisplayPort configuration can be highly dynamic. A graphics device
- * could support two independent pipes, each of them can be connected to one or
- * more ports (DVI, HDMI or DisplayPort).
- *
- * The HDA correspondence of pipes/ports are converter/pin nodes.
- */
-#define MAX_HDMI_CVTS 3
-#define MAX_HDMI_PINS 3
+#define is_haswell(codec) ((codec)->vendor_id == 0x80862807)
+#define is_broadwell(codec) ((codec)->vendor_id == 0x80862808)
+#define is_haswell_plus(codec) (is_haswell(codec) || is_broadwell(codec))
+
+#define is_valleyview(codec) ((codec)->vendor_id == 0x80862882)
+
+struct hdmi_spec_per_cvt {
+ hda_nid_t cvt_nid;
+ int assigned;
+ unsigned int channels_min;
+ unsigned int channels_max;
+ u32 rates;
+ u64 formats;
+ unsigned int maxbps;
+};
+
+/* max. connections to a widget */
+#define HDA_MAX_CONNECTIONS 32
+
+struct hdmi_spec_per_pin {
+ hda_nid_t pin_nid;
+ int num_mux_nids;
+ hda_nid_t mux_nids[HDA_MAX_CONNECTIONS];
+ int mux_idx;
+ hda_nid_t cvt_nid;
+
+ struct hda_codec *codec;
+ struct hdmi_eld sink_eld;
+ struct mutex lock;
+ struct delayed_work work;
+ struct snd_kcontrol *eld_ctl;
+ int repoll_count;
+ bool setup; /* the stream has been set up by prepare callback */
+ int channels; /* current number of channels */
+ bool non_pcm;
+ bool chmap_set; /* channel-map override by ALSA API? */
+ unsigned char chmap[8]; /* ALSA API channel-map */
+ char pcm_name[8]; /* filled in build_pcm callbacks */
+#ifdef CONFIG_PROC_FS
+ struct snd_info_entry *proc_entry;
+#endif
+};
+
+struct cea_channel_speaker_allocation;
+
+/* operations used by generic code that can be overridden by patches */
+struct hdmi_ops {
+ int (*pin_get_eld)(struct hda_codec *codec, hda_nid_t pin_nid,
+ unsigned char *buf, int *eld_size);
+
+ /* get and set channel assigned to each HDMI ASP (audio sample packet) slot */
+ int (*pin_get_slot_channel)(struct hda_codec *codec, hda_nid_t pin_nid,
+ int asp_slot);
+ int (*pin_set_slot_channel)(struct hda_codec *codec, hda_nid_t pin_nid,
+ int asp_slot, int channel);
+
+ void (*pin_setup_infoframe)(struct hda_codec *codec, hda_nid_t pin_nid,
+ int ca, int active_channels, int conn_type);
+
+ /* enable/disable HBR (HD passthrough) */
+ int (*pin_hbr_setup)(struct hda_codec *codec, hda_nid_t pin_nid, bool hbr);
+
+ int (*setup_stream)(struct hda_codec *codec, hda_nid_t cvt_nid,
+ hda_nid_t pin_nid, u32 stream_tag, int format);
+
+ /* Helpers for producing the channel map TLVs. These can be overridden
+ * for devices that have non-standard mapping requirements. */
+ int (*chmap_cea_alloc_validate_get_type)(struct cea_channel_speaker_allocation *cap,
+ int channels);
+ void (*cea_alloc_to_tlv_chmap)(struct cea_channel_speaker_allocation *cap,
+ unsigned int *chmap, int channels);
+
+ /* check that the user-given chmap is supported */
+ int (*chmap_validate)(int ca, int channels, unsigned char *chmap);
+};
struct hdmi_spec {
int num_cvts;
- int num_pins;
- hda_nid_t cvt[MAX_HDMI_CVTS+1]; /* audio sources */
- hda_nid_t pin[MAX_HDMI_PINS+1]; /* audio sinks */
+ struct snd_array cvts; /* struct hdmi_spec_per_cvt */
+ hda_nid_t cvt_nids[4]; /* only for haswell fix */
- /*
- * source connection for each pin
- */
- hda_nid_t pin_cvt[MAX_HDMI_PINS+1];
+ int num_pins;
+ struct snd_array pins; /* struct hdmi_spec_per_pin */
+ struct snd_array pcm_rec; /* struct hda_pcm */
+ unsigned int channels_max; /* max over all cvts */
- /*
- * HDMI sink attached to each pin
- */
- struct hdmi_eld sink_eld[MAX_HDMI_PINS];
+ struct hdmi_eld temp_eld;
+ struct hdmi_ops ops;
- /*
- * export one pcm per pipe
- */
- struct hda_pcm pcm_rec[MAX_HDMI_CVTS];
- struct hda_pcm_stream codec_pcm_pars[MAX_HDMI_CVTS];
+ bool dyn_pin_out;
/*
- * ati/nvhdmi specific
+ * Non-generic VIA/NVIDIA specific
*/
struct hda_multi_out multiout;
- struct hda_pcm_stream *pcm_playback;
-
- /* misc flags */
- /* PD bit indicates only the update, not the current state */
- unsigned int old_pin_detect:1;
+ struct hda_pcm_stream pcm_playback;
};
@@ -287,23 +347,132 @@ static struct cea_channel_speaker_allocation channel_allocations[] = {
* HDMI routines
*/
-static int hda_node_index(hda_nid_t *nids, hda_nid_t nid)
+#define get_pin(spec, idx) \
+ ((struct hdmi_spec_per_pin *)snd_array_elem(&spec->pins, idx))
+#define get_cvt(spec, idx) \
+ ((struct hdmi_spec_per_cvt *)snd_array_elem(&spec->cvts, idx))
+#define get_pcm_rec(spec, idx) \
+ ((struct hda_pcm *)snd_array_elem(&spec->pcm_rec, idx))
+
+static int pin_nid_to_pin_index(struct hda_codec *codec, hda_nid_t pin_nid)
{
- int i;
+ struct hdmi_spec *spec = codec->spec;
+ int pin_idx;
- for (i = 0; nids[i]; i++)
- if (nids[i] == nid)
- return i;
+ for (pin_idx = 0; pin_idx < spec->num_pins; pin_idx++)
+ if (get_pin(spec, pin_idx)->pin_nid == pin_nid)
+ return pin_idx;
- snd_printk(KERN_WARNING "HDMI: nid %d not registered\n", nid);
+ codec_warn(codec, "HDMI: pin nid %d not registered\n", pin_nid);
return -EINVAL;
}
-static void hdmi_get_show_eld(struct hda_codec *codec, hda_nid_t pin_nid,
- struct hdmi_eld *eld)
+static int hinfo_to_pin_index(struct hda_codec *codec,
+ struct hda_pcm_stream *hinfo)
{
- if (!snd_hdmi_get_eld(eld, codec, pin_nid))
- snd_hdmi_show_eld(eld);
+ struct hdmi_spec *spec = codec->spec;
+ int pin_idx;
+
+ for (pin_idx = 0; pin_idx < spec->num_pins; pin_idx++)
+ if (get_pcm_rec(spec, pin_idx)->stream == hinfo)
+ return pin_idx;
+
+ codec_warn(codec, "HDMI: hinfo %p not registered\n", hinfo);
+ return -EINVAL;
+}
+
+static int cvt_nid_to_cvt_index(struct hda_codec *codec, hda_nid_t cvt_nid)
+{
+ struct hdmi_spec *spec = codec->spec;
+ int cvt_idx;
+
+ for (cvt_idx = 0; cvt_idx < spec->num_cvts; cvt_idx++)
+ if (get_cvt(spec, cvt_idx)->cvt_nid == cvt_nid)
+ return cvt_idx;
+
+ codec_warn(codec, "HDMI: cvt nid %d not registered\n", cvt_nid);
+ return -EINVAL;
+}
+
+static int hdmi_eld_ctl_info(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_info *uinfo)
+{
+ struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
+ struct hdmi_spec *spec = codec->spec;
+ struct hdmi_spec_per_pin *per_pin;
+ struct hdmi_eld *eld;
+ int pin_idx;
+
+ uinfo->type = SNDRV_CTL_ELEM_TYPE_BYTES;
+
+ pin_idx = kcontrol->private_value;
+ per_pin = get_pin(spec, pin_idx);
+ eld = &per_pin->sink_eld;
+
+ mutex_lock(&per_pin->lock);
+ uinfo->count = eld->eld_valid ? eld->eld_size : 0;
+ mutex_unlock(&per_pin->lock);
+
+ return 0;
+}
+
+static int hdmi_eld_ctl_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
+ struct hdmi_spec *spec = codec->spec;
+ struct hdmi_spec_per_pin *per_pin;
+ struct hdmi_eld *eld;
+ int pin_idx;
+
+ pin_idx = kcontrol->private_value;
+ per_pin = get_pin(spec, pin_idx);
+ eld = &per_pin->sink_eld;
+
+ mutex_lock(&per_pin->lock);
+ if (eld->eld_size > ARRAY_SIZE(ucontrol->value.bytes.data)) {
+ mutex_unlock(&per_pin->lock);
+ snd_BUG();
+ return -EINVAL;
+ }
+
+ memset(ucontrol->value.bytes.data, 0,
+ ARRAY_SIZE(ucontrol->value.bytes.data));
+ if (eld->eld_valid)
+ memcpy(ucontrol->value.bytes.data, eld->eld_buffer,
+ eld->eld_size);
+ mutex_unlock(&per_pin->lock);
+
+ return 0;
+}
+
+static struct snd_kcontrol_new eld_bytes_ctl = {
+ .access = SNDRV_CTL_ELEM_ACCESS_READ | SNDRV_CTL_ELEM_ACCESS_VOLATILE,
+ .iface = SNDRV_CTL_ELEM_IFACE_PCM,
+ .name = "ELD",
+ .info = hdmi_eld_ctl_info,
+ .get = hdmi_eld_ctl_get,
+};
+
+static int hdmi_create_eld_ctl(struct hda_codec *codec, int pin_idx,
+ int device)
+{
+ struct snd_kcontrol *kctl;
+ struct hdmi_spec *spec = codec->spec;
+ int err;
+
+ kctl = snd_ctl_new1(&eld_bytes_ctl, codec);
+ if (!kctl)
+ return -ENOMEM;
+ kctl->private_value = pin_idx;
+ kctl->id.device = device;
+
+ err = snd_hda_ctl_add(codec, get_pin(spec, pin_idx)->pin_nid, kctl);
+ if (err < 0)
+ return err;
+
+ get_pin(spec, pin_idx)->eld_ctl = kctl;
+ return 0;
}
#ifdef BE_PARANOID
@@ -336,31 +505,105 @@ static void hdmi_write_dip_byte(struct hda_codec *codec, hda_nid_t pin_nid,
snd_hda_codec_write(codec, pin_nid, 0, AC_VERB_SET_HDMI_DIP_DATA, val);
}
-static void hdmi_enable_output(struct hda_codec *codec, hda_nid_t pin_nid)
+static void hdmi_init_pin(struct hda_codec *codec, hda_nid_t pin_nid)
{
+ struct hdmi_spec *spec = codec->spec;
+ int pin_out;
+
/* Unmute */
if (get_wcaps(codec, pin_nid) & AC_WCAP_OUT_AMP)
snd_hda_codec_write(codec, pin_nid, 0,
AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE);
- /* Enable pin out */
+
+ if (spec->dyn_pin_out)
+ /* Disable pin out until stream is active */
+ pin_out = 0;
+ else
+ /* Enable pin out: some machines with GM965 gets broken output
+ * when the pin is disabled or changed while using with HDMI
+ */
+ pin_out = PIN_OUT;
+
snd_hda_codec_write(codec, pin_nid, 0,
- AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT);
+ AC_VERB_SET_PIN_WIDGET_CONTROL, pin_out);
}
-static int hdmi_get_channel_count(struct hda_codec *codec, hda_nid_t nid)
+static int hdmi_get_channel_count(struct hda_codec *codec, hda_nid_t cvt_nid)
{
- return 1 + snd_hda_codec_read(codec, nid, 0,
+ return 1 + snd_hda_codec_read(codec, cvt_nid, 0,
AC_VERB_GET_CVT_CHAN_COUNT, 0);
}
static void hdmi_set_channel_count(struct hda_codec *codec,
- hda_nid_t nid, int chs)
+ hda_nid_t cvt_nid, int chs)
{
- if (chs != hdmi_get_channel_count(codec, nid))
- snd_hda_codec_write(codec, nid, 0,
+ if (chs != hdmi_get_channel_count(codec, cvt_nid))
+ snd_hda_codec_write(codec, cvt_nid, 0,
AC_VERB_SET_CVT_CHAN_COUNT, chs - 1);
}
+/*
+ * ELD proc files
+ */
+
+#ifdef CONFIG_PROC_FS
+static void print_eld_info(struct snd_info_entry *entry,
+ struct snd_info_buffer *buffer)
+{
+ struct hdmi_spec_per_pin *per_pin = entry->private_data;
+
+ mutex_lock(&per_pin->lock);
+ snd_hdmi_print_eld_info(&per_pin->sink_eld, buffer);
+ mutex_unlock(&per_pin->lock);
+}
+
+static void write_eld_info(struct snd_info_entry *entry,
+ struct snd_info_buffer *buffer)
+{
+ struct hdmi_spec_per_pin *per_pin = entry->private_data;
+
+ mutex_lock(&per_pin->lock);
+ snd_hdmi_write_eld_info(&per_pin->sink_eld, buffer);
+ mutex_unlock(&per_pin->lock);
+}
+
+static int eld_proc_new(struct hdmi_spec_per_pin *per_pin, int index)
+{
+ char name[32];
+ struct hda_codec *codec = per_pin->codec;
+ struct snd_info_entry *entry;
+ int err;
+
+ snprintf(name, sizeof(name), "eld#%d.%d", codec->addr, index);
+ err = snd_card_proc_new(codec->bus->card, name, &entry);
+ if (err < 0)
+ return err;
+
+ snd_info_set_text_ops(entry, per_pin, print_eld_info);
+ entry->c.text.write = write_eld_info;
+ entry->mode |= S_IWUSR;
+ per_pin->proc_entry = entry;
+
+ return 0;
+}
+
+static void eld_proc_free(struct hdmi_spec_per_pin *per_pin)
+{
+ if (!per_pin->codec->bus->shutdown && per_pin->proc_entry) {
+ snd_device_free(per_pin->codec->bus->card, per_pin->proc_entry);
+ per_pin->proc_entry = NULL;
+ }
+}
+#else
+static inline int eld_proc_new(struct hdmi_spec_per_pin *per_pin,
+ int index)
+{
+ return 0;
+}
+static inline void eld_proc_free(struct hdmi_spec_per_pin *per_pin)
+{
+}
+#endif
/*
* Channel mapping routines
@@ -386,6 +629,17 @@ static void init_channel_allocations(void)
}
}
+static int get_channel_allocation_order(int ca)
+{
+ int i;
+
+ for (i = 0; i < ARRAY_SIZE(channel_allocations); i++) {
+ if (channel_allocations[i].ca_index == ca)
+ break;
+ }
+ return i;
+}
+
/*
* The transformation takes two steps:
*
@@ -394,11 +648,8 @@ static void init_channel_allocations(void)
*
* TODO: it could select the wrong CA from multiple candidates.
*/
-static int hdmi_channel_allocation(struct hda_codec *codec, hda_nid_t nid,
- int channels)
+static int hdmi_channel_allocation(struct hdmi_eld *eld, int channels)
{
- struct hdmi_spec *spec = codec->spec;
- struct hdmi_eld *eld;
int i;
int ca = 0;
int spk_mask = 0;
@@ -410,19 +661,6 @@ static int hdmi_channel_allocation(struct hda_codec *codec, hda_nid_t nid,
if (channels <= 2)
return 0;
- i = hda_node_index(spec->pin_cvt, nid);
- if (i < 0)
- return 0;
- eld = &spec->sink_eld[i];
-
- /*
- * HDMI sink's ELD info cannot always be retrieved for now, e.g.
- * in console or for audio devices. Assume the highest speakers
- * configuration, to _not_ prohibit multi-channel audio playback.
- */
- if (!eld->spk_alloc)
- eld->spk_alloc = 0xffff;
-
/*
* expand ELD's speaker allocation mask
*
@@ -430,7 +668,7 @@ static int hdmi_channel_allocation(struct hda_codec *codec, hda_nid_t nid,
* expand ELD's notions to match the ones used by Audio InfoFrame.
*/
for (i = 0; i < ARRAY_SIZE(eld_speaker_allocation_bits); i++) {
- if (eld->spk_alloc & (1 << i))
+ if (eld->info.spk_alloc & (1 << i))
spk_mask |= eld_speaker_allocation_bits[i];
}
@@ -444,7 +682,18 @@ static int hdmi_channel_allocation(struct hda_codec *codec, hda_nid_t nid,
}
}
- snd_print_channel_allocation(eld->spk_alloc, buf, sizeof(buf));
+ if (!ca) {
+ /* if there was no match, select the regular ALSA channel
+ * allocation with the matching number of channels */
+ for (i = 0; i < ARRAY_SIZE(channel_allocations); i++) {
+ if (channels == channel_allocations[i].channels) {
+ ca = channel_allocations[i].ca_index;
+ break;
+ }
+ }
+ }
+
+ snd_print_channel_allocation(eld->info.spk_alloc, buf, sizeof(buf));
snd_printdd("HDMI: select CA 0x%x for %d-channel allocation: %s\n",
ca, channels, buf);
@@ -455,47 +704,238 @@ static void hdmi_debug_channel_mapping(struct hda_codec *codec,
hda_nid_t pin_nid)
{
#ifdef CONFIG_SND_DEBUG_VERBOSE
+ struct hdmi_spec *spec = codec->spec;
int i;
- int slot;
+ int channel;
for (i = 0; i < 8; i++) {
- slot = snd_hda_codec_read(codec, pin_nid, 0,
- AC_VERB_GET_HDMI_CHAN_SLOT, i);
- printk(KERN_DEBUG "HDMI: ASP channel %d => slot %d\n",
- slot >> 4, slot & 0xf);
+ channel = spec->ops.pin_get_slot_channel(codec, pin_nid, i);
+ codec_dbg(codec, "HDMI: ASP channel %d => slot %d\n",
+ channel, i);
}
#endif
}
-
-static void hdmi_setup_channel_mapping(struct hda_codec *codec,
+static void hdmi_std_setup_channel_mapping(struct hda_codec *codec,
hda_nid_t pin_nid,
+ bool non_pcm,
int ca)
{
+ struct hdmi_spec *spec = codec->spec;
+ struct cea_channel_speaker_allocation *ch_alloc;
int i;
int err;
+ int order;
+ int non_pcm_mapping[8];
+
+ order = get_channel_allocation_order(ca);
+ ch_alloc = &channel_allocations[order];
if (hdmi_channel_mapping[ca][1] == 0) {
- for (i = 0; i < channel_allocations[ca].channels; i++)
- hdmi_channel_mapping[ca][i] = i | (i << 4);
+ int hdmi_slot = 0;
+ /* fill actual channel mappings in ALSA channel (i) order */
+ for (i = 0; i < ch_alloc->channels; i++) {
+ while (!ch_alloc->speakers[7 - hdmi_slot] && !WARN_ON(hdmi_slot >= 8))
+ hdmi_slot++; /* skip zero slots */
+
+ hdmi_channel_mapping[ca][i] = (i << 4) | hdmi_slot++;
+ }
+ /* fill the rest of the slots with ALSA channel 0xf */
+ for (hdmi_slot = 0; hdmi_slot < 8; hdmi_slot++)
+ if (!ch_alloc->speakers[7 - hdmi_slot])
+ hdmi_channel_mapping[ca][i++] = (0xf << 4) | hdmi_slot;
+ }
+
+ if (non_pcm) {
+ for (i = 0; i < ch_alloc->channels; i++)
+ non_pcm_mapping[i] = (i << 4) | i;
for (; i < 8; i++)
- hdmi_channel_mapping[ca][i] = 0xf | (i << 4);
+ non_pcm_mapping[i] = (0xf << 4) | i;
}
for (i = 0; i < 8; i++) {
- err = snd_hda_codec_write(codec, pin_nid, 0,
- AC_VERB_SET_HDMI_CHAN_SLOT,
- hdmi_channel_mapping[ca][i]);
+ int slotsetup = non_pcm ? non_pcm_mapping[i] : hdmi_channel_mapping[ca][i];
+ int hdmi_slot = slotsetup & 0x0f;
+ int channel = (slotsetup & 0xf0) >> 4;
+ err = spec->ops.pin_set_slot_channel(codec, pin_nid, hdmi_slot, channel);
if (err) {
- snd_printdd(KERN_NOTICE
- "HDMI: channel mapping failed\n");
+ codec_dbg(codec, "HDMI: channel mapping failed\n");
break;
}
}
+}
+
+struct channel_map_table {
+ unsigned char map; /* ALSA API channel map position */
+ int spk_mask; /* speaker position bit mask */
+};
+
+static struct channel_map_table map_tables[] = {
+ { SNDRV_CHMAP_FL, FL },
+ { SNDRV_CHMAP_FR, FR },
+ { SNDRV_CHMAP_RL, RL },
+ { SNDRV_CHMAP_RR, RR },
+ { SNDRV_CHMAP_LFE, LFE },
+ { SNDRV_CHMAP_FC, FC },
+ { SNDRV_CHMAP_RLC, RLC },
+ { SNDRV_CHMAP_RRC, RRC },
+ { SNDRV_CHMAP_RC, RC },
+ { SNDRV_CHMAP_FLC, FLC },
+ { SNDRV_CHMAP_FRC, FRC },
+ { SNDRV_CHMAP_TFL, FLH },
+ { SNDRV_CHMAP_TFR, FRH },
+ { SNDRV_CHMAP_FLW, FLW },
+ { SNDRV_CHMAP_FRW, FRW },
+ { SNDRV_CHMAP_TC, TC },
+ { SNDRV_CHMAP_TFC, FCH },
+ {} /* terminator */
+};
+
+/* from ALSA API channel position to speaker bit mask */
+static int to_spk_mask(unsigned char c)
+{
+ struct channel_map_table *t = map_tables;
+ for (; t->map; t++) {
+ if (t->map == c)
+ return t->spk_mask;
+ }
+ return 0;
+}
+
+/* from ALSA API channel position to CEA slot */
+static int to_cea_slot(int ordered_ca, unsigned char pos)
+{
+ int mask = to_spk_mask(pos);
+ int i;
+
+ if (mask) {
+ for (i = 0; i < 8; i++) {
+ if (channel_allocations[ordered_ca].speakers[7 - i] == mask)
+ return i;
+ }
+ }
+
+ return -1;
+}
+
+/* from speaker bit mask to ALSA API channel position */
+static int spk_to_chmap(int spk)
+{
+ struct channel_map_table *t = map_tables;
+ for (; t->map; t++) {
+ if (t->spk_mask == spk)
+ return t->map;
+ }
+ return 0;
+}
+
+/* from CEA slot to ALSA API channel position */
+static int from_cea_slot(int ordered_ca, unsigned char slot)
+{
+ int mask = channel_allocations[ordered_ca].speakers[7 - slot];
+
+ return spk_to_chmap(mask);
+}
+
+/* get the CA index corresponding to the given ALSA API channel map */
+static int hdmi_manual_channel_allocation(int chs, unsigned char *map)
+{
+ int i, spks = 0, spk_mask = 0;
+
+ for (i = 0; i < chs; i++) {
+ int mask = to_spk_mask(map[i]);
+ if (mask) {
+ spk_mask |= mask;
+ spks++;
+ }
+ }
+
+ for (i = 0; i < ARRAY_SIZE(channel_allocations); i++) {
+ if ((chs == channel_allocations[i].channels ||
+ spks == channel_allocations[i].channels) &&
+ (spk_mask & channel_allocations[i].spk_mask) ==
+ channel_allocations[i].spk_mask)
+ return channel_allocations[i].ca_index;
+ }
+ return -1;
+}
+
+/* set up the channel slots for the given ALSA API channel map */
+static int hdmi_manual_setup_channel_mapping(struct hda_codec *codec,
+ hda_nid_t pin_nid,
+ int chs, unsigned char *map,
+ int ca)
+{
+ struct hdmi_spec *spec = codec->spec;
+ int ordered_ca = get_channel_allocation_order(ca);
+ int alsa_pos, hdmi_slot;
+ int assignments[8] = {[0 ... 7] = 0xf};
+
+ for (alsa_pos = 0; alsa_pos < chs; alsa_pos++) {
+
+ hdmi_slot = to_cea_slot(ordered_ca, map[alsa_pos]);
+
+ if (hdmi_slot < 0)
+ continue; /* unassigned channel */
+
+ assignments[hdmi_slot] = alsa_pos;
+ }
+
+ for (hdmi_slot = 0; hdmi_slot < 8; hdmi_slot++) {
+ int err;
+
+ err = spec->ops.pin_set_slot_channel(codec, pin_nid, hdmi_slot,
+ assignments[hdmi_slot]);
+ if (err)
+ return -EINVAL;
+ }
+ return 0;
+}
+
+/* store ALSA API channel map from the current default map */
+static void hdmi_setup_fake_chmap(unsigned char *map, int ca)
+{
+ int i;
+ int ordered_ca = get_channel_allocation_order(ca);
+ for (i = 0; i < 8; i++) {
+ if (i < channel_allocations[ordered_ca].channels)
+ map[i] = from_cea_slot(ordered_ca, hdmi_channel_mapping[ca][i] & 0x0f);
+ else
+ map[i] = 0;
+ }
+}
+
+static void hdmi_setup_channel_mapping(struct hda_codec *codec,
+ hda_nid_t pin_nid, bool non_pcm, int ca,
+ int channels, unsigned char *map,
+ bool chmap_set)
+{
+ if (!non_pcm && chmap_set) {
+ hdmi_manual_setup_channel_mapping(codec, pin_nid,
+ channels, map, ca);
+ } else {
+ hdmi_std_setup_channel_mapping(codec, pin_nid, non_pcm, ca);
+ hdmi_setup_fake_chmap(map, ca);
+ }
hdmi_debug_channel_mapping(codec, pin_nid);
}
+static int hdmi_pin_set_slot_channel(struct hda_codec *codec, hda_nid_t pin_nid,
+ int asp_slot, int channel)
+{
+ return snd_hda_codec_write(codec, pin_nid, 0,
+ AC_VERB_SET_HDMI_CHAN_SLOT,
+ (channel << 4) | asp_slot);
+}
+
+static int hdmi_pin_get_slot_channel(struct hda_codec *codec, hda_nid_t pin_nid,
+ int asp_slot)
+{
+ return (snd_hda_codec_read(codec, pin_nid, 0,
+ AC_VERB_GET_HDMI_CHAN_SLOT,
+ asp_slot) & 0xf0) >> 4;
+}
/*
* Audio InfoFrame routines
@@ -530,12 +970,12 @@ static void hdmi_debug_dip_size(struct hda_codec *codec, hda_nid_t pin_nid)
int size;
size = snd_hdmi_get_eld_size(codec, pin_nid);
- printk(KERN_DEBUG "HDMI: ELD buf size is %d\n", size);
+ codec_dbg(codec, "HDMI: ELD buf size is %d\n", size);
for (i = 0; i < 8; i++) {
size = snd_hda_codec_read(codec, pin_nid, 0,
AC_VERB_GET_HDMI_DIP_SIZE, i);
- printk(KERN_DEBUG "HDMI: DIP GP[%d] buf size is %d\n", i, size);
+ codec_dbg(codec, "HDMI: DIP GP[%d] buf size is %d\n", i, size);
}
#endif
}
@@ -557,12 +997,12 @@ static void hdmi_clear_dip_buffers(struct hda_codec *codec, hda_nid_t pin_nid)
hdmi_write_dip_byte(codec, pin_nid, 0x0);
hdmi_get_dip_index(codec, pin_nid, &pi, &bi);
if (pi != i)
- snd_printd(KERN_INFO "dip index %d: %d != %d\n",
+ codec_dbg(codec, "dip index %d: %d != %d\n",
bi, pi, i);
if (bi == 0) /* byte index wrapped around */
break;
}
- snd_printd(KERN_INFO
+ codec_dbg(codec,
"HDMI: DIP GP[%d] buf reported size=%d, written=%d\n",
i, size, j);
}
@@ -618,108 +1058,136 @@ static bool hdmi_infoframe_uptodate(struct hda_codec *codec, hda_nid_t pin_nid,
return true;
}
-static void hdmi_setup_audio_infoframe(struct hda_codec *codec, hda_nid_t nid,
- struct snd_pcm_substream *substream)
+static void hdmi_pin_setup_infoframe(struct hda_codec *codec,
+ hda_nid_t pin_nid,
+ int ca, int active_channels,
+ int conn_type)
{
- struct hdmi_spec *spec = codec->spec;
- hda_nid_t pin_nid;
- int channels = substream->runtime->channels;
- int ca;
- int i;
union audio_infoframe ai;
- ca = hdmi_channel_allocation(codec, nid, channels);
-
- for (i = 0; i < spec->num_pins; i++) {
- if (spec->pin_cvt[i] != nid)
- continue;
- if (!spec->sink_eld[i].monitor_present)
- continue;
-
- pin_nid = spec->pin[i];
-
- memset(&ai, 0, sizeof(ai));
- if (spec->sink_eld[i].conn_type == 0) { /* HDMI */
- struct hdmi_audio_infoframe *hdmi_ai = &ai.hdmi;
-
- hdmi_ai->type = 0x84;
- hdmi_ai->ver = 0x01;
- hdmi_ai->len = 0x0a;
- hdmi_ai->CC02_CT47 = channels - 1;
- hdmi_ai->CA = ca;
- hdmi_checksum_audio_infoframe(hdmi_ai);
- } else if (spec->sink_eld[i].conn_type == 1) { /* DisplayPort */
- struct dp_audio_infoframe *dp_ai = &ai.dp;
-
- dp_ai->type = 0x84;
- dp_ai->len = 0x1b;
- dp_ai->ver = 0x11 << 2;
- dp_ai->CC02_CT47 = channels - 1;
- dp_ai->CA = ca;
- } else {
- snd_printd("HDMI: unknown connection type at pin %d\n",
- pin_nid);
- continue;
- }
+ memset(&ai, 0, sizeof(ai));
+ if (conn_type == 0) { /* HDMI */
+ struct hdmi_audio_infoframe *hdmi_ai = &ai.hdmi;
+
+ hdmi_ai->type = 0x84;
+ hdmi_ai->ver = 0x01;
+ hdmi_ai->len = 0x0a;
+ hdmi_ai->CC02_CT47 = active_channels - 1;
+ hdmi_ai->CA = ca;
+ hdmi_checksum_audio_infoframe(hdmi_ai);
+ } else if (conn_type == 1) { /* DisplayPort */
+ struct dp_audio_infoframe *dp_ai = &ai.dp;
+
+ dp_ai->type = 0x84;
+ dp_ai->len = 0x1b;
+ dp_ai->ver = 0x11 << 2;
+ dp_ai->CC02_CT47 = active_channels - 1;
+ dp_ai->CA = ca;
+ } else {
+ codec_dbg(codec, "HDMI: unknown connection type at pin %d\n",
+ pin_nid);
+ return;
+ }
- /*
- * sizeof(ai) is used instead of sizeof(*hdmi_ai) or
- * sizeof(*dp_ai) to avoid partial match/update problems when
- * the user switches between HDMI/DP monitors.
- */
- if (!hdmi_infoframe_uptodate(codec, pin_nid, ai.bytes,
- sizeof(ai))) {
- snd_printdd("hdmi_setup_audio_infoframe: "
- "cvt=%d pin=%d channels=%d\n",
- nid, pin_nid,
- channels);
- hdmi_setup_channel_mapping(codec, pin_nid, ca);
- hdmi_stop_infoframe_trans(codec, pin_nid);
- hdmi_fill_audio_infoframe(codec, pin_nid,
- ai.bytes, sizeof(ai));
- hdmi_start_infoframe_trans(codec, pin_nid);
- }
+ /*
+ * sizeof(ai) is used instead of sizeof(*hdmi_ai) or
+ * sizeof(*dp_ai) to avoid partial match/update problems when
+ * the user switches between HDMI/DP monitors.
+ */
+ if (!hdmi_infoframe_uptodate(codec, pin_nid, ai.bytes,
+ sizeof(ai))) {
+ codec_dbg(codec,
+ "hdmi_pin_setup_infoframe: pin=%d channels=%d ca=0x%02x\n",
+ pin_nid,
+ active_channels, ca);
+ hdmi_stop_infoframe_trans(codec, pin_nid);
+ hdmi_fill_audio_infoframe(codec, pin_nid,
+ ai.bytes, sizeof(ai));
+ hdmi_start_infoframe_trans(codec, pin_nid);
}
}
+static void hdmi_setup_audio_infoframe(struct hda_codec *codec,
+ struct hdmi_spec_per_pin *per_pin,
+ bool non_pcm)
+{
+ struct hdmi_spec *spec = codec->spec;
+ hda_nid_t pin_nid = per_pin->pin_nid;
+ int channels = per_pin->channels;
+ int active_channels;
+ struct hdmi_eld *eld;
+ int ca, ordered_ca;
+
+ if (!channels)
+ return;
+
+ if (is_haswell_plus(codec))
+ snd_hda_codec_write(codec, pin_nid, 0,
+ AC_VERB_SET_AMP_GAIN_MUTE,
+ AMP_OUT_UNMUTE);
+
+ eld = &per_pin->sink_eld;
+
+ if (!non_pcm && per_pin->chmap_set)
+ ca = hdmi_manual_channel_allocation(channels, per_pin->chmap);
+ else
+ ca = hdmi_channel_allocation(eld, channels);
+ if (ca < 0)
+ ca = 0;
+
+ ordered_ca = get_channel_allocation_order(ca);
+ active_channels = channel_allocations[ordered_ca].channels;
+
+ hdmi_set_channel_count(codec, per_pin->cvt_nid, active_channels);
+
+ /*
+ * always configure channel mapping, it may have been changed by the
+ * user in the meantime
+ */
+ hdmi_setup_channel_mapping(codec, pin_nid, non_pcm, ca,
+ channels, per_pin->chmap,
+ per_pin->chmap_set);
+
+ spec->ops.pin_setup_infoframe(codec, pin_nid, ca, active_channels,
+ eld->info.conn_type);
+
+ per_pin->non_pcm = non_pcm;
+}
/*
* Unsolicited events
*/
-static void hdmi_present_sense(struct hda_codec *codec, hda_nid_t pin_nid,
- struct hdmi_eld *eld);
+static bool hdmi_present_sense(struct hdmi_spec_per_pin *per_pin, int repoll);
-static void hdmi_intrinsic_event(struct hda_codec *codec, unsigned int res)
+static void jack_callback(struct hda_codec *codec, struct hda_jack_tbl *jack)
{
struct hdmi_spec *spec = codec->spec;
- int tag = res >> AC_UNSOL_RES_TAG_SHIFT;
- int pind = !!(res & AC_UNSOL_RES_PD);
- int eldv = !!(res & AC_UNSOL_RES_ELDV);
- int index;
+ int pin_idx = pin_nid_to_pin_index(codec, jack->nid);
+ if (pin_idx < 0)
+ return;
- printk(KERN_INFO
- "HDMI hot plug event: Pin=%d Presence_Detect=%d ELD_Valid=%d\n",
- tag, pind, eldv);
+ if (hdmi_present_sense(get_pin(spec, pin_idx), 1))
+ snd_hda_jack_report_sync(codec);
+}
- index = hda_node_index(spec->pin, tag);
- if (index < 0)
- return;
+static void hdmi_intrinsic_event(struct hda_codec *codec, unsigned int res)
+{
+ int tag = res >> AC_UNSOL_RES_TAG_SHIFT;
+ struct hda_jack_tbl *jack;
+ int dev_entry = (res & AC_UNSOL_RES_DE) >> AC_UNSOL_RES_DE_SHIFT;
- if (spec->old_pin_detect) {
- if (pind)
- hdmi_present_sense(codec, tag, &spec->sink_eld[index]);
- pind = spec->sink_eld[index].monitor_present;
- }
+ jack = snd_hda_jack_tbl_get_from_tag(codec, tag);
+ if (!jack)
+ return;
+ jack->jack_dirty = 1;
- spec->sink_eld[index].monitor_present = pind;
- spec->sink_eld[index].eld_valid = eldv;
+ codec_dbg(codec,
+ "HDMI hot plug event: Codec=%d Pin=%d Device=%d Inactive=%d Presence_Detect=%d ELD_Valid=%d\n",
+ codec->addr, jack->nid, dev_entry, !!(res & AC_UNSOL_RES_IA),
+ !!(res & AC_UNSOL_RES_PD), !!(res & AC_UNSOL_RES_ELDV));
- if (pind && eldv) {
- hdmi_get_show_eld(codec, spec->pin[index],
- &spec->sink_eld[index]);
- /* TODO: do real things about ELD */
- }
+ jack_callback(codec, jack);
}
static void hdmi_non_intrinsic_event(struct hda_codec *codec, unsigned int res)
@@ -729,8 +1197,9 @@ static void hdmi_non_intrinsic_event(struct hda_codec *codec, unsigned int res)
int cp_state = !!(res & AC_UNSOL_RES_CP_STATE);
int cp_ready = !!(res & AC_UNSOL_RES_CP_READY);
- printk(KERN_INFO
- "HDMI CP event: PIN=%d SUBTAG=0x%x CP_STATE=%d CP_READY=%d\n",
+ codec_info(codec,
+ "HDMI CP event: CODEC=%d TAG=%d SUBTAG=0x%x CP_STATE=%d CP_READY=%d\n",
+ codec->addr,
tag,
subtag,
cp_state,
@@ -746,12 +1215,11 @@ static void hdmi_non_intrinsic_event(struct hda_codec *codec, unsigned int res)
static void hdmi_unsol_event(struct hda_codec *codec, unsigned int res)
{
- struct hdmi_spec *spec = codec->spec;
int tag = res >> AC_UNSOL_RES_TAG_SHIFT;
int subtag = (res & AC_UNSOL_RES_SUBTAG) >> AC_UNSOL_RES_SUBTAG_SHIFT;
- if (hda_node_index(spec->pin, tag) < 0) {
- snd_printd(KERN_INFO "Unexpected HDMI event tag 0x%x\n", tag);
+ if (!snd_hda_jack_tbl_get_from_tag(codec, tag)) {
+ codec_dbg(codec, "Unexpected HDMI event tag 0x%x\n", tag);
return;
}
@@ -761,6 +1229,27 @@ static void hdmi_unsol_event(struct hda_codec *codec, unsigned int res)
hdmi_non_intrinsic_event(codec, res);
}
+static void haswell_verify_D0(struct hda_codec *codec,
+ hda_nid_t cvt_nid, hda_nid_t nid)
+{
+ int pwr;
+
+ /* For Haswell, the converter 1/2 may keep in D3 state after bootup,
+ * thus pins could only choose converter 0 for use. Make sure the
+ * converters are in correct power state */
+ if (!snd_hda_check_power_state(codec, cvt_nid, AC_PWRST_D0))
+ snd_hda_codec_write(codec, cvt_nid, 0, AC_VERB_SET_POWER_STATE, AC_PWRST_D0);
+
+ if (!snd_hda_check_power_state(codec, nid, AC_PWRST_D0)) {
+ snd_hda_codec_write(codec, nid, 0, AC_VERB_SET_POWER_STATE,
+ AC_PWRST_D0);
+ msleep(40);
+ pwr = snd_hda_codec_read(codec, nid, 0, AC_VERB_GET_POWER_STATE, 0);
+ pwr = (pwr & AC_PWRST_ACTUAL) >> AC_PWRST_ACTUAL_SHIFT;
+ codec_dbg(codec, "Haswell HDMI audio: Power for pin 0x%x is now D%d\n", nid, pwr);
+ }
+}
+
/*
* Callbacks
*/
@@ -769,50 +1258,169 @@ static void hdmi_unsol_event(struct hda_codec *codec, unsigned int res)
#define is_hbr_format(format) \
((format & AC_FMT_TYPE_NON_PCM) && (format & AC_FMT_CHAN_MASK) == 7)
-static int hdmi_setup_stream(struct hda_codec *codec, hda_nid_t nid,
- u32 stream_tag, int format)
+static int hdmi_pin_hbr_setup(struct hda_codec *codec, hda_nid_t pin_nid,
+ bool hbr)
{
- struct hdmi_spec *spec = codec->spec;
- int pinctl;
- int new_pinctl = 0;
- int i;
-
- for (i = 0; i < spec->num_pins; i++) {
- if (spec->pin_cvt[i] != nid)
- continue;
- if (!(snd_hda_query_pin_caps(codec, spec->pin[i]) & AC_PINCAP_HBR))
- continue;
+ int pinctl, new_pinctl;
- pinctl = snd_hda_codec_read(codec, spec->pin[i], 0,
+ if (snd_hda_query_pin_caps(codec, pin_nid) & AC_PINCAP_HBR) {
+ pinctl = snd_hda_codec_read(codec, pin_nid, 0,
AC_VERB_GET_PIN_WIDGET_CONTROL, 0);
+ if (pinctl < 0)
+ return hbr ? -EINVAL : 0;
+
new_pinctl = pinctl & ~AC_PINCTL_EPT;
- if (is_hbr_format(format))
+ if (hbr)
new_pinctl |= AC_PINCTL_EPT_HBR;
else
new_pinctl |= AC_PINCTL_EPT_NATIVE;
- snd_printdd("hdmi_setup_stream: "
- "NID=0x%x, %spinctl=0x%x\n",
- spec->pin[i],
+ codec_dbg(codec,
+ "hdmi_pin_hbr_setup: NID=0x%x, %spinctl=0x%x\n",
+ pin_nid,
pinctl == new_pinctl ? "" : "new-",
new_pinctl);
if (pinctl != new_pinctl)
- snd_hda_codec_write(codec, spec->pin[i], 0,
+ snd_hda_codec_write(codec, pin_nid, 0,
AC_VERB_SET_PIN_WIDGET_CONTROL,
new_pinctl);
+ } else if (hbr)
+ return -EINVAL;
+
+ return 0;
+}
+
+static int hdmi_setup_stream(struct hda_codec *codec, hda_nid_t cvt_nid,
+ hda_nid_t pin_nid, u32 stream_tag, int format)
+{
+ struct hdmi_spec *spec = codec->spec;
+ int err;
+
+ if (is_haswell_plus(codec))
+ haswell_verify_D0(codec, cvt_nid, pin_nid);
+
+ err = spec->ops.pin_hbr_setup(codec, pin_nid, is_hbr_format(format));
+
+ if (err) {
+ codec_dbg(codec, "hdmi_setup_stream: HBR is not supported\n");
+ return err;
}
- if (is_hbr_format(format) && !new_pinctl) {
- snd_printdd("hdmi_setup_stream: HBR is not supported\n");
- return -EINVAL;
+ snd_hda_codec_setup_stream(codec, cvt_nid, stream_tag, 0, format);
+ return 0;
+}
+
+static int hdmi_choose_cvt(struct hda_codec *codec,
+ int pin_idx, int *cvt_id, int *mux_id)
+{
+ struct hdmi_spec *spec = codec->spec;
+ struct hdmi_spec_per_pin *per_pin;
+ struct hdmi_spec_per_cvt *per_cvt = NULL;
+ int cvt_idx, mux_idx = 0;
+
+ per_pin = get_pin(spec, pin_idx);
+
+ /* Dynamically assign converter to stream */
+ for (cvt_idx = 0; cvt_idx < spec->num_cvts; cvt_idx++) {
+ per_cvt = get_cvt(spec, cvt_idx);
+
+ /* Must not already be assigned */
+ if (per_cvt->assigned)
+ continue;
+ /* Must be in pin's mux's list of converters */
+ for (mux_idx = 0; mux_idx < per_pin->num_mux_nids; mux_idx++)
+ if (per_pin->mux_nids[mux_idx] == per_cvt->cvt_nid)
+ break;
+ /* Not in mux list */
+ if (mux_idx == per_pin->num_mux_nids)
+ continue;
+ break;
}
- snd_hda_codec_setup_stream(codec, nid, stream_tag, 0, format);
+ /* No free converters */
+ if (cvt_idx == spec->num_cvts)
+ return -ENODEV;
+
+ per_pin->mux_idx = mux_idx;
+
+ if (cvt_id)
+ *cvt_id = cvt_idx;
+ if (mux_id)
+ *mux_id = mux_idx;
+
return 0;
}
+/* Assure the pin select the right convetor */
+static void intel_verify_pin_cvt_connect(struct hda_codec *codec,
+ struct hdmi_spec_per_pin *per_pin)
+{
+ hda_nid_t pin_nid = per_pin->pin_nid;
+ int mux_idx, curr;
+
+ mux_idx = per_pin->mux_idx;
+ curr = snd_hda_codec_read(codec, pin_nid, 0,
+ AC_VERB_GET_CONNECT_SEL, 0);
+ if (curr != mux_idx)
+ snd_hda_codec_write_cache(codec, pin_nid, 0,
+ AC_VERB_SET_CONNECT_SEL,
+ mux_idx);
+}
+
+/* Intel HDMI workaround to fix audio routing issue:
+ * For some Intel display codecs, pins share the same connection list.
+ * So a conveter can be selected by multiple pins and playback on any of these
+ * pins will generate sound on the external display, because audio flows from
+ * the same converter to the display pipeline. Also muting one pin may make
+ * other pins have no sound output.
+ * So this function assures that an assigned converter for a pin is not selected
+ * by any other pins.
+ */
+static void intel_not_share_assigned_cvt(struct hda_codec *codec,
+ hda_nid_t pin_nid, int mux_idx)
+{
+ struct hdmi_spec *spec = codec->spec;
+ hda_nid_t nid, end_nid;
+ int cvt_idx, curr;
+ struct hdmi_spec_per_cvt *per_cvt;
+
+ /* configure all pins, including "no physical connection" ones */
+ end_nid = codec->start_nid + codec->num_nodes;
+ for (nid = codec->start_nid; nid < end_nid; nid++) {
+ unsigned int wid_caps = get_wcaps(codec, nid);
+ unsigned int wid_type = get_wcaps_type(wid_caps);
+
+ if (wid_type != AC_WID_PIN)
+ continue;
+
+ if (nid == pin_nid)
+ continue;
+
+ curr = snd_hda_codec_read(codec, nid, 0,
+ AC_VERB_GET_CONNECT_SEL, 0);
+ if (curr != mux_idx)
+ continue;
+
+ /* choose an unassigned converter. The conveters in the
+ * connection list are in the same order as in the codec.
+ */
+ for (cvt_idx = 0; cvt_idx < spec->num_cvts; cvt_idx++) {
+ per_cvt = get_cvt(spec, cvt_idx);
+ if (!per_cvt->assigned) {
+ codec_dbg(codec,
+ "choose cvt %d for pin nid %d\n",
+ cvt_idx, nid);
+ snd_hda_codec_write_cache(codec, nid, 0,
+ AC_VERB_SET_CONNECT_SEL,
+ cvt_idx);
+ break;
+ }
+ }
+ }
+}
+
/*
* HDA PCM callbacks
*/
@@ -821,37 +1429,60 @@ static int hdmi_pcm_open(struct hda_pcm_stream *hinfo,
struct snd_pcm_substream *substream)
{
struct hdmi_spec *spec = codec->spec;
- struct hdmi_eld *eld;
- struct hda_pcm_stream *codec_pars;
struct snd_pcm_runtime *runtime = substream->runtime;
- unsigned int idx;
+ int pin_idx, cvt_idx, mux_idx = 0;
+ struct hdmi_spec_per_pin *per_pin;
+ struct hdmi_eld *eld;
+ struct hdmi_spec_per_cvt *per_cvt = NULL;
+ int err;
- for (idx = 0; idx < spec->num_cvts; idx++)
- if (hinfo->nid == spec->cvt[idx])
- break;
- if (snd_BUG_ON(idx >= spec->num_cvts) ||
- snd_BUG_ON(idx >= spec->num_pins))
+ /* Validate hinfo */
+ pin_idx = hinfo_to_pin_index(codec, hinfo);
+ if (snd_BUG_ON(pin_idx < 0))
return -EINVAL;
+ per_pin = get_pin(spec, pin_idx);
+ eld = &per_pin->sink_eld;
+
+ err = hdmi_choose_cvt(codec, pin_idx, &cvt_idx, &mux_idx);
+ if (err < 0)
+ return err;
+
+ per_cvt = get_cvt(spec, cvt_idx);
+ /* Claim converter */
+ per_cvt->assigned = 1;
+ per_pin->cvt_nid = per_cvt->cvt_nid;
+ hinfo->nid = per_cvt->cvt_nid;
+
+ snd_hda_codec_write_cache(codec, per_pin->pin_nid, 0,
+ AC_VERB_SET_CONNECT_SEL,
+ mux_idx);
- /* save the PCM info the codec provides */
- codec_pars = &spec->codec_pcm_pars[idx];
- if (!codec_pars->rates)
- *codec_pars = *hinfo;
+ /* configure unused pins to choose other converters */
+ if (is_haswell_plus(codec) || is_valleyview(codec))
+ intel_not_share_assigned_cvt(codec, per_pin->pin_nid, mux_idx);
- eld = &spec->sink_eld[idx];
- if (!static_hdmi_pcm && eld->eld_valid && eld->sad_count > 0) {
- hdmi_eld_update_pcm_info(eld, hinfo, codec_pars);
+ snd_hda_spdif_ctls_assign(codec, pin_idx, per_cvt->cvt_nid);
+
+ /* Initially set the converter's capabilities */
+ hinfo->channels_min = per_cvt->channels_min;
+ hinfo->channels_max = per_cvt->channels_max;
+ hinfo->rates = per_cvt->rates;
+ hinfo->formats = per_cvt->formats;
+ hinfo->maxbps = per_cvt->maxbps;
+
+ /* Restrict capabilities by ELD if this isn't disabled */
+ if (!static_hdmi_pcm && eld->eld_valid) {
+ snd_hdmi_eld_update_pcm_info(&eld->info, hinfo);
if (hinfo->channels_min > hinfo->channels_max ||
- !hinfo->rates || !hinfo->formats)
+ !hinfo->rates || !hinfo->formats) {
+ per_cvt->assigned = 0;
+ hinfo->nid = 0;
+ snd_hda_spdif_ctls_unassign(codec, pin_idx);
return -ENODEV;
- } else {
- /* fallback to the codec default */
- hinfo->channels_max = codec_pars->channels_max;
- hinfo->rates = codec_pars->rates;
- hinfo->formats = codec_pars->formats;
- hinfo->maxbps = codec_pars->maxbps;
+ }
}
- /* store the updated parameters */
+
+ /* Store the updated parameters */
runtime->hw.channels_min = hinfo->channels_min;
runtime->hw.channels_max = hinfo->channels_max;
runtime->hw.formats = hinfo->formats;
@@ -865,88 +1496,216 @@ static int hdmi_pcm_open(struct hda_pcm_stream *hinfo,
/*
* HDA/HDMI auto parsing
*/
-static int hdmi_read_pin_conn(struct hda_codec *codec, hda_nid_t pin_nid)
+static int hdmi_read_pin_conn(struct hda_codec *codec, int pin_idx)
{
struct hdmi_spec *spec = codec->spec;
- hda_nid_t conn_list[HDA_MAX_CONNECTIONS];
- int conn_len, curr;
- int index;
+ struct hdmi_spec_per_pin *per_pin = get_pin(spec, pin_idx);
+ hda_nid_t pin_nid = per_pin->pin_nid;
if (!(get_wcaps(codec, pin_nid) & AC_WCAP_CONN_LIST)) {
- snd_printk(KERN_WARNING
- "HDMI: pin %d wcaps %#x "
- "does not support connection list\n",
+ codec_warn(codec,
+ "HDMI: pin %d wcaps %#x does not support connection list\n",
pin_nid, get_wcaps(codec, pin_nid));
return -EINVAL;
}
- conn_len = snd_hda_get_connections(codec, pin_nid, conn_list,
- HDA_MAX_CONNECTIONS);
- if (conn_len > 1)
- curr = snd_hda_codec_read(codec, pin_nid, 0,
- AC_VERB_GET_CONNECT_SEL, 0);
+ per_pin->num_mux_nids = snd_hda_get_connections(codec, pin_nid,
+ per_pin->mux_nids,
+ HDA_MAX_CONNECTIONS);
+
+ return 0;
+}
+
+static bool hdmi_present_sense(struct hdmi_spec_per_pin *per_pin, int repoll)
+{
+ struct hda_jack_tbl *jack;
+ struct hda_codec *codec = per_pin->codec;
+ struct hdmi_spec *spec = codec->spec;
+ struct hdmi_eld *eld = &spec->temp_eld;
+ struct hdmi_eld *pin_eld = &per_pin->sink_eld;
+ hda_nid_t pin_nid = per_pin->pin_nid;
+ /*
+ * Always execute a GetPinSense verb here, even when called from
+ * hdmi_intrinsic_event; for some NVIDIA HW, the unsolicited
+ * response's PD bit is not the real PD value, but indicates that
+ * the real PD value changed. An older version of the HD-audio
+ * specification worked this way. Hence, we just ignore the data in
+ * the unsolicited response to avoid custom WARs.
+ */
+ int present;
+ bool update_eld = false;
+ bool eld_changed = false;
+ bool ret;
+
+ snd_hda_power_up(codec);
+ present = snd_hda_pin_sense(codec, pin_nid);
+
+ mutex_lock(&per_pin->lock);
+ pin_eld->monitor_present = !!(present & AC_PINSENSE_PRESENCE);
+ if (pin_eld->monitor_present)
+ eld->eld_valid = !!(present & AC_PINSENSE_ELDV);
else
- curr = 0;
+ eld->eld_valid = false;
+
+ codec_dbg(codec,
+ "HDMI status: Codec=%d Pin=%d Presence_Detect=%d ELD_Valid=%d\n",
+ codec->addr, pin_nid, pin_eld->monitor_present, eld->eld_valid);
+
+ if (eld->eld_valid) {
+ if (spec->ops.pin_get_eld(codec, pin_nid, eld->eld_buffer,
+ &eld->eld_size) < 0)
+ eld->eld_valid = false;
+ else {
+ memset(&eld->info, 0, sizeof(struct parsed_hdmi_eld));
+ if (snd_hdmi_parse_eld(&eld->info, eld->eld_buffer,
+ eld->eld_size) < 0)
+ eld->eld_valid = false;
+ }
- index = hda_node_index(spec->pin, pin_nid);
- if (index < 0)
- return -EINVAL;
+ if (eld->eld_valid) {
+ snd_hdmi_show_eld(&eld->info);
+ update_eld = true;
+ }
+ else if (repoll) {
+ queue_delayed_work(codec->bus->workq,
+ &per_pin->work,
+ msecs_to_jiffies(300));
+ goto unlock;
+ }
+ }
+
+ if (pin_eld->eld_valid && !eld->eld_valid) {
+ update_eld = true;
+ eld_changed = true;
+ }
+ if (update_eld) {
+ bool old_eld_valid = pin_eld->eld_valid;
+ pin_eld->eld_valid = eld->eld_valid;
+ eld_changed = pin_eld->eld_size != eld->eld_size ||
+ memcmp(pin_eld->eld_buffer, eld->eld_buffer,
+ eld->eld_size) != 0;
+ if (eld_changed)
+ memcpy(pin_eld->eld_buffer, eld->eld_buffer,
+ eld->eld_size);
+ pin_eld->eld_size = eld->eld_size;
+ pin_eld->info = eld->info;
- spec->pin_cvt[index] = conn_list[curr];
+ /*
+ * Re-setup pin and infoframe. This is needed e.g. when
+ * - sink is first plugged-in (infoframe is not set up if !monitor_present)
+ * - transcoder can change during stream playback on Haswell
+ * and this can make HW reset converter selection on a pin.
+ */
+ if (eld->eld_valid && !old_eld_valid && per_pin->setup) {
+ if (is_haswell_plus(codec) || is_valleyview(codec)) {
+ intel_verify_pin_cvt_connect(codec, per_pin);
+ intel_not_share_assigned_cvt(codec, pin_nid,
+ per_pin->mux_idx);
+ }
- return 0;
+ hdmi_setup_audio_infoframe(codec, per_pin,
+ per_pin->non_pcm);
+ }
+ }
+
+ if (eld_changed)
+ snd_ctl_notify(codec->bus->card,
+ SNDRV_CTL_EVENT_MASK_VALUE | SNDRV_CTL_EVENT_MASK_INFO,
+ &per_pin->eld_ctl->id);
+ unlock:
+ ret = !repoll || !pin_eld->monitor_present || pin_eld->eld_valid;
+
+ jack = snd_hda_jack_tbl_get(codec, pin_nid);
+ if (jack)
+ jack->block_report = !ret;
+
+ mutex_unlock(&per_pin->lock);
+ snd_hda_power_down(codec);
+ return ret;
}
-static void hdmi_present_sense(struct hda_codec *codec, hda_nid_t pin_nid,
- struct hdmi_eld *eld)
+static void hdmi_repoll_eld(struct work_struct *work)
{
- int present = snd_hda_pin_sense(codec, pin_nid);
+ struct hdmi_spec_per_pin *per_pin =
+ container_of(to_delayed_work(work), struct hdmi_spec_per_pin, work);
- eld->monitor_present = !!(present & AC_PINSENSE_PRESENCE);
- eld->eld_valid = !!(present & AC_PINSENSE_ELDV);
+ if (per_pin->repoll_count++ > 6)
+ per_pin->repoll_count = 0;
- if (present & AC_PINSENSE_ELDV)
- hdmi_get_show_eld(codec, pin_nid, eld);
+ if (hdmi_present_sense(per_pin, per_pin->repoll_count))
+ snd_hda_jack_report_sync(per_pin->codec);
}
+static void intel_haswell_fixup_connect_list(struct hda_codec *codec,
+ hda_nid_t nid);
+
static int hdmi_add_pin(struct hda_codec *codec, hda_nid_t pin_nid)
{
struct hdmi_spec *spec = codec->spec;
+ unsigned int caps, config;
+ int pin_idx;
+ struct hdmi_spec_per_pin *per_pin;
+ int err;
- if (spec->num_pins >= MAX_HDMI_PINS) {
- snd_printk(KERN_WARNING
- "HDMI: no space for pin %d\n", pin_nid);
- return -E2BIG;
- }
+ caps = snd_hda_query_pin_caps(codec, pin_nid);
+ if (!(caps & (AC_PINCAP_HDMI | AC_PINCAP_DP)))
+ return 0;
- hdmi_present_sense(codec, pin_nid, &spec->sink_eld[spec->num_pins]);
+ config = snd_hda_codec_get_pincfg(codec, pin_nid);
+ if (get_defcfg_connect(config) == AC_JACK_PORT_NONE)
+ return 0;
+
+ if (is_haswell_plus(codec))
+ intel_haswell_fixup_connect_list(codec, pin_nid);
+
+ pin_idx = spec->num_pins;
+ per_pin = snd_array_new(&spec->pins);
+ if (!per_pin)
+ return -ENOMEM;
+
+ per_pin->pin_nid = pin_nid;
+ per_pin->non_pcm = false;
+
+ err = hdmi_read_pin_conn(codec, pin_idx);
+ if (err < 0)
+ return err;
- spec->pin[spec->num_pins] = pin_nid;
spec->num_pins++;
- return hdmi_read_pin_conn(codec, pin_nid);
+ return 0;
}
-static int hdmi_add_cvt(struct hda_codec *codec, hda_nid_t nid)
+static int hdmi_add_cvt(struct hda_codec *codec, hda_nid_t cvt_nid)
{
- int i, found_pin = 0;
struct hdmi_spec *spec = codec->spec;
+ struct hdmi_spec_per_cvt *per_cvt;
+ unsigned int chans;
+ int err;
- for (i = 0; i < spec->num_pins; i++)
- if (nid == spec->pin_cvt[i]) {
- found_pin = 1;
- break;
- }
+ chans = get_wcaps(codec, cvt_nid);
+ chans = get_wcaps_channels(chans);
- if (!found_pin) {
- snd_printdd("HDMI: Skipping node %d (no connection)\n", nid);
- return -EINVAL;
+ per_cvt = snd_array_new(&spec->cvts);
+ if (!per_cvt)
+ return -ENOMEM;
+
+ per_cvt->cvt_nid = cvt_nid;
+ per_cvt->channels_min = 2;
+ if (chans <= 16) {
+ per_cvt->channels_max = chans;
+ if (chans > spec->channels_max)
+ spec->channels_max = chans;
}
- if (snd_BUG_ON(spec->num_cvts >= MAX_HDMI_CVTS))
- return -E2BIG;
+ err = snd_hda_query_supported_pcm(codec, cvt_nid,
+ &per_cvt->rates,
+ &per_cvt->formats,
+ &per_cvt->maxbps);
+ if (err < 0)
+ return err;
- spec->cvt[spec->num_cvts] = nid;
+ if (spec->num_cvts < ARRAY_SIZE(spec->cvt_nids))
+ spec->cvt_nids[spec->num_cvts] = cvt_nid;
spec->num_cvts++;
return 0;
@@ -956,21 +1715,18 @@ static int hdmi_parse_codec(struct hda_codec *codec)
{
hda_nid_t nid;
int i, nodes;
- int num_tmp_cvts = 0;
- hda_nid_t tmp_cvt[MAX_HDMI_CVTS];
nodes = snd_hda_get_sub_nodes(codec, codec->afg, &nid);
if (!nid || nodes < 0) {
- snd_printk(KERN_WARNING "HDMI: failed to get afg sub nodes\n");
+ codec_warn(codec, "HDMI: failed to get afg sub nodes\n");
return -EINVAL;
}
for (i = 0; i < nodes; i++, nid++) {
unsigned int caps;
unsigned int type;
- unsigned int config;
- caps = snd_hda_param_read(codec, nid, AC_PAR_AUDIO_WIDGET_CAP);
+ caps = get_wcaps(codec, nid);
type = get_wcaps_type(caps);
if (!(caps & AC_WCAP_DIGITAL))
@@ -978,53 +1734,31 @@ static int hdmi_parse_codec(struct hda_codec *codec)
switch (type) {
case AC_WID_AUD_OUT:
- if (num_tmp_cvts >= MAX_HDMI_CVTS) {
- snd_printk(KERN_WARNING
- "HDMI: no space for converter %d\n", nid);
- continue;
- }
- tmp_cvt[num_tmp_cvts] = nid;
- num_tmp_cvts++;
+ hdmi_add_cvt(codec, nid);
break;
case AC_WID_PIN:
- caps = snd_hda_param_read(codec, nid, AC_PAR_PIN_CAP);
- if (!(caps & (AC_PINCAP_HDMI | AC_PINCAP_DP)))
- continue;
-
- config = snd_hda_codec_read(codec, nid, 0,
- AC_VERB_GET_CONFIG_DEFAULT, 0);
- if (get_defcfg_connect(config) == AC_JACK_PORT_NONE)
- continue;
-
hdmi_add_pin(codec, nid);
break;
}
}
- for (i = 0; i < num_tmp_cvts; i++)
- hdmi_add_cvt(codec, tmp_cvt[i]);
-
- /*
- * G45/IbexPeak don't support EPSS: the unsolicited pin hot plug event
- * can be lost and presence sense verb will become inaccurate if the
- * HDA link is powered off at hot plug or hw initialization time.
- */
-#ifdef CONFIG_SND_HDA_POWER_SAVE
- if (!(snd_hda_param_read(codec, codec->afg, AC_PAR_POWER_STATE) &
- AC_PWRST_EPSS))
- codec->bus->power_keep_link_on = 1;
-#endif
-
return 0;
}
/*
*/
-static char *generic_hdmi_pcm_names[MAX_HDMI_CVTS] = {
- "HDMI 0",
- "HDMI 1",
- "HDMI 2",
-};
+static bool check_non_pcm_per_cvt(struct hda_codec *codec, hda_nid_t cvt_nid)
+{
+ struct hda_spdif_out *spdif;
+ bool non_pcm;
+
+ mutex_lock(&codec->spdif_mutex);
+ spdif = snd_hda_spdif_out_of_nid(codec, cvt_nid);
+ non_pcm = !!(spdif->status & IEC958_AES0_NONAUDIO);
+ mutex_unlock(&codec->spdif_mutex);
+ return non_pcm;
+}
+
/*
* HDMI callbacks
@@ -1036,121 +1770,581 @@ static int generic_hdmi_playback_pcm_prepare(struct hda_pcm_stream *hinfo,
unsigned int format,
struct snd_pcm_substream *substream)
{
- hdmi_set_channel_count(codec, hinfo->nid,
- substream->runtime->channels);
+ hda_nid_t cvt_nid = hinfo->nid;
+ struct hdmi_spec *spec = codec->spec;
+ int pin_idx = hinfo_to_pin_index(codec, hinfo);
+ struct hdmi_spec_per_pin *per_pin = get_pin(spec, pin_idx);
+ hda_nid_t pin_nid = per_pin->pin_nid;
+ bool non_pcm;
+ int pinctl;
+
+ if (is_haswell_plus(codec) || is_valleyview(codec)) {
+ /* Verify pin:cvt selections to avoid silent audio after S3.
+ * After S3, the audio driver restores pin:cvt selections
+ * but this can happen before gfx is ready and such selection
+ * is overlooked by HW. Thus multiple pins can share a same
+ * default convertor and mute control will affect each other,
+ * which can cause a resumed audio playback become silent
+ * after S3.
+ */
+ intel_verify_pin_cvt_connect(codec, per_pin);
+ intel_not_share_assigned_cvt(codec, pin_nid, per_pin->mux_idx);
+ }
+
+ non_pcm = check_non_pcm_per_cvt(codec, cvt_nid);
+ mutex_lock(&per_pin->lock);
+ per_pin->channels = substream->runtime->channels;
+ per_pin->setup = true;
+
+ hdmi_setup_audio_infoframe(codec, per_pin, non_pcm);
+ mutex_unlock(&per_pin->lock);
+
+ if (spec->dyn_pin_out) {
+ pinctl = snd_hda_codec_read(codec, pin_nid, 0,
+ AC_VERB_GET_PIN_WIDGET_CONTROL, 0);
+ snd_hda_codec_write(codec, pin_nid, 0,
+ AC_VERB_SET_PIN_WIDGET_CONTROL,
+ pinctl | PIN_OUT);
+ }
+
+ return spec->ops.setup_stream(codec, cvt_nid, pin_nid, stream_tag, format);
+}
+
+static int generic_hdmi_playback_pcm_cleanup(struct hda_pcm_stream *hinfo,
+ struct hda_codec *codec,
+ struct snd_pcm_substream *substream)
+{
+ snd_hda_codec_cleanup_stream(codec, hinfo->nid);
+ return 0;
+}
- hdmi_setup_audio_infoframe(codec, hinfo->nid, substream);
+static int hdmi_pcm_close(struct hda_pcm_stream *hinfo,
+ struct hda_codec *codec,
+ struct snd_pcm_substream *substream)
+{
+ struct hdmi_spec *spec = codec->spec;
+ int cvt_idx, pin_idx;
+ struct hdmi_spec_per_cvt *per_cvt;
+ struct hdmi_spec_per_pin *per_pin;
+ int pinctl;
+
+ if (hinfo->nid) {
+ cvt_idx = cvt_nid_to_cvt_index(codec, hinfo->nid);
+ if (snd_BUG_ON(cvt_idx < 0))
+ return -EINVAL;
+ per_cvt = get_cvt(spec, cvt_idx);
+
+ snd_BUG_ON(!per_cvt->assigned);
+ per_cvt->assigned = 0;
+ hinfo->nid = 0;
+
+ pin_idx = hinfo_to_pin_index(codec, hinfo);
+ if (snd_BUG_ON(pin_idx < 0))
+ return -EINVAL;
+ per_pin = get_pin(spec, pin_idx);
+
+ if (spec->dyn_pin_out) {
+ pinctl = snd_hda_codec_read(codec, per_pin->pin_nid, 0,
+ AC_VERB_GET_PIN_WIDGET_CONTROL, 0);
+ snd_hda_codec_write(codec, per_pin->pin_nid, 0,
+ AC_VERB_SET_PIN_WIDGET_CONTROL,
+ pinctl & ~PIN_OUT);
+ }
- return hdmi_setup_stream(codec, hinfo->nid, stream_tag, format);
+ snd_hda_spdif_ctls_unassign(codec, pin_idx);
+
+ mutex_lock(&per_pin->lock);
+ per_pin->chmap_set = false;
+ memset(per_pin->chmap, 0, sizeof(per_pin->chmap));
+
+ per_pin->setup = false;
+ per_pin->channels = 0;
+ mutex_unlock(&per_pin->lock);
+ }
+
+ return 0;
}
-static struct hda_pcm_stream generic_hdmi_pcm_playback = {
- .substreams = 1,
- .channels_min = 2,
- .ops = {
- .open = hdmi_pcm_open,
- .prepare = generic_hdmi_playback_pcm_prepare,
- },
+static const struct hda_pcm_ops generic_ops = {
+ .open = hdmi_pcm_open,
+ .close = hdmi_pcm_close,
+ .prepare = generic_hdmi_playback_pcm_prepare,
+ .cleanup = generic_hdmi_playback_pcm_cleanup,
};
-static int generic_hdmi_build_pcms(struct hda_codec *codec)
+/*
+ * ALSA API channel-map control callbacks
+ */
+static int hdmi_chmap_ctl_info(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_info *uinfo)
+{
+ struct snd_pcm_chmap *info = snd_kcontrol_chip(kcontrol);
+ struct hda_codec *codec = info->private_data;
+ struct hdmi_spec *spec = codec->spec;
+ uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
+ uinfo->count = spec->channels_max;
+ uinfo->value.integer.min = 0;
+ uinfo->value.integer.max = SNDRV_CHMAP_LAST;
+ return 0;
+}
+
+static int hdmi_chmap_cea_alloc_validate_get_type(struct cea_channel_speaker_allocation *cap,
+ int channels)
+{
+ /* If the speaker allocation matches the channel count, it is OK.*/
+ if (cap->channels != channels)
+ return -1;
+
+ /* all channels are remappable freely */
+ return SNDRV_CTL_TLVT_CHMAP_VAR;
+}
+
+static void hdmi_cea_alloc_to_tlv_chmap(struct cea_channel_speaker_allocation *cap,
+ unsigned int *chmap, int channels)
+{
+ int count = 0;
+ int c;
+
+ for (c = 7; c >= 0; c--) {
+ int spk = cap->speakers[c];
+ if (!spk)
+ continue;
+
+ chmap[count++] = spk_to_chmap(spk);
+ }
+
+ WARN_ON(count != channels);
+}
+
+static int hdmi_chmap_ctl_tlv(struct snd_kcontrol *kcontrol, int op_flag,
+ unsigned int size, unsigned int __user *tlv)
{
+ struct snd_pcm_chmap *info = snd_kcontrol_chip(kcontrol);
+ struct hda_codec *codec = info->private_data;
struct hdmi_spec *spec = codec->spec;
- struct hda_pcm *info = spec->pcm_rec;
+ unsigned int __user *dst;
+ int chs, count = 0;
+
+ if (size < 8)
+ return -ENOMEM;
+ if (put_user(SNDRV_CTL_TLVT_CONTAINER, tlv))
+ return -EFAULT;
+ size -= 8;
+ dst = tlv + 2;
+ for (chs = 2; chs <= spec->channels_max; chs++) {
+ int i;
+ struct cea_channel_speaker_allocation *cap;
+ cap = channel_allocations;
+ for (i = 0; i < ARRAY_SIZE(channel_allocations); i++, cap++) {
+ int chs_bytes = chs * 4;
+ int type = spec->ops.chmap_cea_alloc_validate_get_type(cap, chs);
+ unsigned int tlv_chmap[8];
+
+ if (type < 0)
+ continue;
+ if (size < 8)
+ return -ENOMEM;
+ if (put_user(type, dst) ||
+ put_user(chs_bytes, dst + 1))
+ return -EFAULT;
+ dst += 2;
+ size -= 8;
+ count += 8;
+ if (size < chs_bytes)
+ return -ENOMEM;
+ size -= chs_bytes;
+ count += chs_bytes;
+ spec->ops.cea_alloc_to_tlv_chmap(cap, tlv_chmap, chs);
+ if (copy_to_user(dst, tlv_chmap, chs_bytes))
+ return -EFAULT;
+ dst += chs;
+ }
+ }
+ if (put_user(count, tlv + 1))
+ return -EFAULT;
+ return 0;
+}
+
+static int hdmi_chmap_ctl_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_pcm_chmap *info = snd_kcontrol_chip(kcontrol);
+ struct hda_codec *codec = info->private_data;
+ struct hdmi_spec *spec = codec->spec;
+ int pin_idx = kcontrol->private_value;
+ struct hdmi_spec_per_pin *per_pin = get_pin(spec, pin_idx);
int i;
- codec->num_pcms = spec->num_cvts;
- codec->pcm_info = info;
+ for (i = 0; i < ARRAY_SIZE(per_pin->chmap); i++)
+ ucontrol->value.integer.value[i] = per_pin->chmap[i];
+ return 0;
+}
- for (i = 0; i < codec->num_pcms; i++, info++) {
- unsigned int chans;
- struct hda_pcm_stream *pstr;
+static int hdmi_chmap_ctl_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_pcm_chmap *info = snd_kcontrol_chip(kcontrol);
+ struct hda_codec *codec = info->private_data;
+ struct hdmi_spec *spec = codec->spec;
+ int pin_idx = kcontrol->private_value;
+ struct hdmi_spec_per_pin *per_pin = get_pin(spec, pin_idx);
+ unsigned int ctl_idx;
+ struct snd_pcm_substream *substream;
+ unsigned char chmap[8];
+ int i, err, ca, prepared = 0;
+
+ ctl_idx = snd_ctl_get_ioffidx(kcontrol, &ucontrol->id);
+ substream = snd_pcm_chmap_substream(info, ctl_idx);
+ if (!substream || !substream->runtime)
+ return 0; /* just for avoiding error from alsactl restore */
+ switch (substream->runtime->status->state) {
+ case SNDRV_PCM_STATE_OPEN:
+ case SNDRV_PCM_STATE_SETUP:
+ break;
+ case SNDRV_PCM_STATE_PREPARED:
+ prepared = 1;
+ break;
+ default:
+ return -EBUSY;
+ }
+ memset(chmap, 0, sizeof(chmap));
+ for (i = 0; i < ARRAY_SIZE(chmap); i++)
+ chmap[i] = ucontrol->value.integer.value[i];
+ if (!memcmp(chmap, per_pin->chmap, sizeof(chmap)))
+ return 0;
+ ca = hdmi_manual_channel_allocation(ARRAY_SIZE(chmap), chmap);
+ if (ca < 0)
+ return -EINVAL;
+ if (spec->ops.chmap_validate) {
+ err = spec->ops.chmap_validate(ca, ARRAY_SIZE(chmap), chmap);
+ if (err)
+ return err;
+ }
+ mutex_lock(&per_pin->lock);
+ per_pin->chmap_set = true;
+ memcpy(per_pin->chmap, chmap, sizeof(chmap));
+ if (prepared)
+ hdmi_setup_audio_infoframe(codec, per_pin, per_pin->non_pcm);
+ mutex_unlock(&per_pin->lock);
- chans = get_wcaps(codec, spec->cvt[i]);
- chans = get_wcaps_channels(chans);
+ return 0;
+}
- info->name = generic_hdmi_pcm_names[i];
+static int generic_hdmi_build_pcms(struct hda_codec *codec)
+{
+ struct hdmi_spec *spec = codec->spec;
+ int pin_idx;
+
+ for (pin_idx = 0; pin_idx < spec->num_pins; pin_idx++) {
+ struct hda_pcm *info;
+ struct hda_pcm_stream *pstr;
+ struct hdmi_spec_per_pin *per_pin;
+
+ per_pin = get_pin(spec, pin_idx);
+ sprintf(per_pin->pcm_name, "HDMI %d", pin_idx);
+ info = snd_array_new(&spec->pcm_rec);
+ if (!info)
+ return -ENOMEM;
+ info->name = per_pin->pcm_name;
info->pcm_type = HDA_PCM_TYPE_HDMI;
+ info->own_chmap = true;
+
pstr = &info->stream[SNDRV_PCM_STREAM_PLAYBACK];
- if (spec->pcm_playback)
- *pstr = *spec->pcm_playback;
- else
- *pstr = generic_hdmi_pcm_playback;
- pstr->nid = spec->cvt[i];
- if (pstr->channels_max <= 2 && chans && chans <= 16)
- pstr->channels_max = chans;
+ pstr->substreams = 1;
+ pstr->ops = generic_ops;
+ /* other pstr fields are set in open */
}
+ codec->num_pcms = spec->num_pins;
+ codec->pcm_info = spec->pcm_rec.list;
+
return 0;
}
+static int generic_hdmi_build_jack(struct hda_codec *codec, int pin_idx)
+{
+ char hdmi_str[32] = "HDMI/DP";
+ struct hdmi_spec *spec = codec->spec;
+ struct hdmi_spec_per_pin *per_pin = get_pin(spec, pin_idx);
+ int pcmdev = get_pcm_rec(spec, pin_idx)->device;
+
+ if (pcmdev > 0)
+ sprintf(hdmi_str + strlen(hdmi_str), ",pcm=%d", pcmdev);
+ if (!is_jack_detectable(codec, per_pin->pin_nid))
+ strncat(hdmi_str, " Phantom",
+ sizeof(hdmi_str) - strlen(hdmi_str) - 1);
+
+ return snd_hda_jack_add_kctl(codec, per_pin->pin_nid, hdmi_str, 0);
+}
+
static int generic_hdmi_build_controls(struct hda_codec *codec)
{
struct hdmi_spec *spec = codec->spec;
int err;
- int i;
+ int pin_idx;
+
+ for (pin_idx = 0; pin_idx < spec->num_pins; pin_idx++) {
+ struct hdmi_spec_per_pin *per_pin = get_pin(spec, pin_idx);
+
+ err = generic_hdmi_build_jack(codec, pin_idx);
+ if (err < 0)
+ return err;
+
+ err = snd_hda_create_dig_out_ctls(codec,
+ per_pin->pin_nid,
+ per_pin->mux_nids[0],
+ HDA_PCM_TYPE_HDMI);
+ if (err < 0)
+ return err;
+ snd_hda_spdif_ctls_unassign(codec, pin_idx);
+
+ /* add control for ELD Bytes */
+ err = hdmi_create_eld_ctl(codec, pin_idx,
+ get_pcm_rec(spec, pin_idx)->device);
- for (i = 0; i < codec->num_pcms; i++) {
- err = snd_hda_create_spdif_out_ctls(codec, spec->cvt[i]);
if (err < 0)
return err;
+
+ hdmi_present_sense(per_pin, 0);
+ }
+
+ /* add channel maps */
+ for (pin_idx = 0; pin_idx < spec->num_pins; pin_idx++) {
+ struct snd_pcm_chmap *chmap;
+ struct snd_kcontrol *kctl;
+ int i;
+
+ if (!codec->pcm_info[pin_idx].pcm)
+ break;
+ err = snd_pcm_add_chmap_ctls(codec->pcm_info[pin_idx].pcm,
+ SNDRV_PCM_STREAM_PLAYBACK,
+ NULL, 0, pin_idx, &chmap);
+ if (err < 0)
+ return err;
+ /* override handlers */
+ chmap->private_data = codec;
+ kctl = chmap->kctl;
+ for (i = 0; i < kctl->count; i++)
+ kctl->vd[i].access |= SNDRV_CTL_ELEM_ACCESS_WRITE;
+ kctl->info = hdmi_chmap_ctl_info;
+ kctl->get = hdmi_chmap_ctl_get;
+ kctl->put = hdmi_chmap_ctl_put;
+ kctl->tlv.c = hdmi_chmap_ctl_tlv;
}
return 0;
}
+static int generic_hdmi_init_per_pins(struct hda_codec *codec)
+{
+ struct hdmi_spec *spec = codec->spec;
+ int pin_idx;
+
+ for (pin_idx = 0; pin_idx < spec->num_pins; pin_idx++) {
+ struct hdmi_spec_per_pin *per_pin = get_pin(spec, pin_idx);
+
+ per_pin->codec = codec;
+ mutex_init(&per_pin->lock);
+ INIT_DELAYED_WORK(&per_pin->work, hdmi_repoll_eld);
+ eld_proc_new(per_pin, pin_idx);
+ }
+ return 0;
+}
+
static int generic_hdmi_init(struct hda_codec *codec)
{
struct hdmi_spec *spec = codec->spec;
- int i;
+ int pin_idx;
+
+ for (pin_idx = 0; pin_idx < spec->num_pins; pin_idx++) {
+ struct hdmi_spec_per_pin *per_pin = get_pin(spec, pin_idx);
+ hda_nid_t pin_nid = per_pin->pin_nid;
- for (i = 0; spec->pin[i]; i++) {
- hdmi_enable_output(codec, spec->pin[i]);
- snd_hda_codec_write(codec, spec->pin[i], 0,
- AC_VERB_SET_UNSOLICITED_ENABLE,
- AC_USRSP_EN | spec->pin[i]);
+ hdmi_init_pin(codec, pin_nid);
+ snd_hda_jack_detect_enable_callback(codec, pin_nid, pin_nid,
+ codec->jackpoll_interval > 0 ? jack_callback : NULL);
}
return 0;
}
+static void hdmi_array_init(struct hdmi_spec *spec, int nums)
+{
+ snd_array_init(&spec->pins, sizeof(struct hdmi_spec_per_pin), nums);
+ snd_array_init(&spec->cvts, sizeof(struct hdmi_spec_per_cvt), nums);
+ snd_array_init(&spec->pcm_rec, sizeof(struct hda_pcm), nums);
+}
+
+static void hdmi_array_free(struct hdmi_spec *spec)
+{
+ snd_array_free(&spec->pins);
+ snd_array_free(&spec->cvts);
+ snd_array_free(&spec->pcm_rec);
+}
+
static void generic_hdmi_free(struct hda_codec *codec)
{
struct hdmi_spec *spec = codec->spec;
- int i;
+ int pin_idx;
- for (i = 0; i < spec->num_pins; i++)
- snd_hda_eld_proc_free(codec, &spec->sink_eld[i]);
+ for (pin_idx = 0; pin_idx < spec->num_pins; pin_idx++) {
+ struct hdmi_spec_per_pin *per_pin = get_pin(spec, pin_idx);
+ cancel_delayed_work(&per_pin->work);
+ eld_proc_free(per_pin);
+ }
+
+ flush_workqueue(codec->bus->workq);
+ hdmi_array_free(spec);
kfree(spec);
}
-static struct hda_codec_ops generic_hdmi_patch_ops = {
+#ifdef CONFIG_PM
+static int generic_hdmi_resume(struct hda_codec *codec)
+{
+ struct hdmi_spec *spec = codec->spec;
+ int pin_idx;
+
+ codec->patch_ops.init(codec);
+ snd_hda_codec_resume_amp(codec);
+ snd_hda_codec_resume_cache(codec);
+
+ for (pin_idx = 0; pin_idx < spec->num_pins; pin_idx++) {
+ struct hdmi_spec_per_pin *per_pin = get_pin(spec, pin_idx);
+ hdmi_present_sense(per_pin, 1);
+ }
+ return 0;
+}
+#endif
+
+static const struct hda_codec_ops generic_hdmi_patch_ops = {
.init = generic_hdmi_init,
.free = generic_hdmi_free,
.build_pcms = generic_hdmi_build_pcms,
.build_controls = generic_hdmi_build_controls,
.unsol_event = hdmi_unsol_event,
+#ifdef CONFIG_PM
+ .resume = generic_hdmi_resume,
+#endif
+};
+
+static const struct hdmi_ops generic_standard_hdmi_ops = {
+ .pin_get_eld = snd_hdmi_get_eld,
+ .pin_get_slot_channel = hdmi_pin_get_slot_channel,
+ .pin_set_slot_channel = hdmi_pin_set_slot_channel,
+ .pin_setup_infoframe = hdmi_pin_setup_infoframe,
+ .pin_hbr_setup = hdmi_pin_hbr_setup,
+ .setup_stream = hdmi_setup_stream,
+ .chmap_cea_alloc_validate_get_type = hdmi_chmap_cea_alloc_validate_get_type,
+ .cea_alloc_to_tlv_chmap = hdmi_cea_alloc_to_tlv_chmap,
};
+
+static void intel_haswell_fixup_connect_list(struct hda_codec *codec,
+ hda_nid_t nid)
+{
+ struct hdmi_spec *spec = codec->spec;
+ hda_nid_t conns[4];
+ int nconns;
+
+ nconns = snd_hda_get_connections(codec, nid, conns, ARRAY_SIZE(conns));
+ if (nconns == spec->num_cvts &&
+ !memcmp(conns, spec->cvt_nids, spec->num_cvts * sizeof(hda_nid_t)))
+ return;
+
+ /* override pins connection list */
+ codec_dbg(codec, "hdmi: haswell: override pin connection 0x%x\n", nid);
+ snd_hda_override_conn_list(codec, nid, spec->num_cvts, spec->cvt_nids);
+}
+
+#define INTEL_VENDOR_NID 0x08
+#define INTEL_GET_VENDOR_VERB 0xf81
+#define INTEL_SET_VENDOR_VERB 0x781
+#define INTEL_EN_DP12 0x02 /* enable DP 1.2 features */
+#define INTEL_EN_ALL_PIN_CVTS 0x01 /* enable 2nd & 3rd pins and convertors */
+
+static void intel_haswell_enable_all_pins(struct hda_codec *codec,
+ bool update_tree)
+{
+ unsigned int vendor_param;
+
+ vendor_param = snd_hda_codec_read(codec, INTEL_VENDOR_NID, 0,
+ INTEL_GET_VENDOR_VERB, 0);
+ if (vendor_param == -1 || vendor_param & INTEL_EN_ALL_PIN_CVTS)
+ return;
+
+ vendor_param |= INTEL_EN_ALL_PIN_CVTS;
+ vendor_param = snd_hda_codec_read(codec, INTEL_VENDOR_NID, 0,
+ INTEL_SET_VENDOR_VERB, vendor_param);
+ if (vendor_param == -1)
+ return;
+
+ if (update_tree)
+ snd_hda_codec_update_widgets(codec);
+}
+
+static void intel_haswell_fixup_enable_dp12(struct hda_codec *codec)
+{
+ unsigned int vendor_param;
+
+ vendor_param = snd_hda_codec_read(codec, INTEL_VENDOR_NID, 0,
+ INTEL_GET_VENDOR_VERB, 0);
+ if (vendor_param == -1 || vendor_param & INTEL_EN_DP12)
+ return;
+
+ /* enable DP1.2 mode */
+ vendor_param |= INTEL_EN_DP12;
+ snd_hda_codec_write_cache(codec, INTEL_VENDOR_NID, 0,
+ INTEL_SET_VENDOR_VERB, vendor_param);
+}
+
+/* Haswell needs to re-issue the vendor-specific verbs before turning to D0.
+ * Otherwise you may get severe h/w communication errors.
+ */
+static void haswell_set_power_state(struct hda_codec *codec, hda_nid_t fg,
+ unsigned int power_state)
+{
+ if (power_state == AC_PWRST_D0) {
+ intel_haswell_enable_all_pins(codec, false);
+ intel_haswell_fixup_enable_dp12(codec);
+ }
+
+ snd_hda_codec_read(codec, fg, 0, AC_VERB_SET_POWER_STATE, power_state);
+ snd_hda_codec_set_power_to_all(codec, fg, power_state);
+}
+
static int patch_generic_hdmi(struct hda_codec *codec)
{
struct hdmi_spec *spec;
- int i;
spec = kzalloc(sizeof(*spec), GFP_KERNEL);
if (spec == NULL)
return -ENOMEM;
+ spec->ops = generic_standard_hdmi_ops;
codec->spec = spec;
+ hdmi_array_init(spec, 4);
+
+ if (is_haswell_plus(codec)) {
+ intel_haswell_enable_all_pins(codec, true);
+ intel_haswell_fixup_enable_dp12(codec);
+ }
+
+ if (is_haswell(codec) || is_valleyview(codec)) {
+ codec->depop_delay = 0;
+ }
+
if (hdmi_parse_codec(codec) < 0) {
codec->spec = NULL;
kfree(spec);
return -EINVAL;
}
codec->patch_ops = generic_hdmi_patch_ops;
+ if (is_haswell_plus(codec)) {
+ codec->patch_ops.set_power_state = haswell_set_power_state;
+ codec->dp_mst = true;
+ }
- for (i = 0; i < spec->num_pins; i++)
- snd_hda_eld_proc_new(codec, &spec->sink_eld[i], i);
+ generic_hdmi_init_per_pins(codec);
init_channel_allocations();
@@ -1158,6 +2352,92 @@ static int patch_generic_hdmi(struct hda_codec *codec)
}
/*
+ * Shared non-generic implementations
+ */
+
+static int simple_playback_build_pcms(struct hda_codec *codec)
+{
+ struct hdmi_spec *spec = codec->spec;
+ struct hda_pcm *info;
+ unsigned int chans;
+ struct hda_pcm_stream *pstr;
+ struct hdmi_spec_per_cvt *per_cvt;
+
+ per_cvt = get_cvt(spec, 0);
+ chans = get_wcaps(codec, per_cvt->cvt_nid);
+ chans = get_wcaps_channels(chans);
+
+ info = snd_array_new(&spec->pcm_rec);
+ if (!info)
+ return -ENOMEM;
+ info->name = get_pin(spec, 0)->pcm_name;
+ sprintf(info->name, "HDMI 0");
+ info->pcm_type = HDA_PCM_TYPE_HDMI;
+ pstr = &info->stream[SNDRV_PCM_STREAM_PLAYBACK];
+ *pstr = spec->pcm_playback;
+ pstr->nid = per_cvt->cvt_nid;
+ if (pstr->channels_max <= 2 && chans && chans <= 16)
+ pstr->channels_max = chans;
+
+ codec->num_pcms = 1;
+ codec->pcm_info = info;
+
+ return 0;
+}
+
+/* unsolicited event for jack sensing */
+static void simple_hdmi_unsol_event(struct hda_codec *codec,
+ unsigned int res)
+{
+ snd_hda_jack_set_dirty_all(codec);
+ snd_hda_jack_report_sync(codec);
+}
+
+/* generic_hdmi_build_jack can be used for simple_hdmi, too,
+ * as long as spec->pins[] is set correctly
+ */
+#define simple_hdmi_build_jack generic_hdmi_build_jack
+
+static int simple_playback_build_controls(struct hda_codec *codec)
+{
+ struct hdmi_spec *spec = codec->spec;
+ struct hdmi_spec_per_cvt *per_cvt;
+ int err;
+
+ per_cvt = get_cvt(spec, 0);
+ err = snd_hda_create_dig_out_ctls(codec, per_cvt->cvt_nid,
+ per_cvt->cvt_nid,
+ HDA_PCM_TYPE_HDMI);
+ if (err < 0)
+ return err;
+ return simple_hdmi_build_jack(codec, 0);
+}
+
+static int simple_playback_init(struct hda_codec *codec)
+{
+ struct hdmi_spec *spec = codec->spec;
+ struct hdmi_spec_per_pin *per_pin = get_pin(spec, 0);
+ hda_nid_t pin = per_pin->pin_nid;
+
+ snd_hda_codec_write(codec, pin, 0,
+ AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT);
+ /* some codecs require to unmute the pin */
+ if (get_wcaps(codec, pin) & AC_WCAP_OUT_AMP)
+ snd_hda_codec_write(codec, pin, 0, AC_VERB_SET_AMP_GAIN_MUTE,
+ AMP_OUT_UNMUTE);
+ snd_hda_jack_detect_enable(codec, pin, pin);
+ return 0;
+}
+
+static void simple_playback_free(struct hda_codec *codec)
+{
+ struct hdmi_spec *spec = codec->spec;
+
+ hdmi_array_free(spec);
+ kfree(spec);
+}
+
+/*
* Nvidia specific implementations
*/
@@ -1169,12 +2449,20 @@ static int patch_generic_hdmi(struct hda_codec *codec)
#define nvhdmi_master_con_nid_7x 0x04
#define nvhdmi_master_pin_nid_7x 0x05
-static hda_nid_t nvhdmi_con_nids_7x[4] = {
+static const hda_nid_t nvhdmi_con_nids_7x[4] = {
/*front, rear, clfe, rear_surr */
0x6, 0x8, 0xa, 0xc,
};
-static struct hda_verb nvhdmi_basic_init_7x[] = {
+static const struct hda_verb nvhdmi_basic_init_7x_2ch[] = {
+ /* set audio protect on */
+ { 0x1, Nv_VERB_SET_Audio_Protection_On, 0x1},
+ /* enable digital output on pin widget */
+ { 0x5, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT | 0x5 },
+ {} /* terminator */
+};
+
+static const struct hda_verb nvhdmi_basic_init_7x_8ch[] = {
/* set audio protect on */
{ 0x1, Nv_VERB_SET_Audio_Protection_On, 0x1},
/* enable digital output on pin widget */
@@ -1202,9 +2490,15 @@ static struct hda_verb nvhdmi_basic_init_7x[] = {
(SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S32_LE)
#endif
-static int nvhdmi_7x_init(struct hda_codec *codec)
+static int nvhdmi_7x_init_2ch(struct hda_codec *codec)
{
- snd_hda_sequence_write(codec, nvhdmi_basic_init_7x);
+ snd_hda_sequence_write(codec, nvhdmi_basic_init_7x_2ch);
+ return 0;
+}
+
+static int nvhdmi_7x_init_8ch(struct hda_codec *codec)
+{
+ snd_hda_sequence_write(codec, nvhdmi_basic_init_7x_8ch);
return 0;
}
@@ -1280,6 +2574,59 @@ static int simple_playback_pcm_prepare(struct hda_pcm_stream *hinfo,
stream_tag, format, substream);
}
+static const struct hda_pcm_stream simple_pcm_playback = {
+ .substreams = 1,
+ .channels_min = 2,
+ .channels_max = 2,
+ .ops = {
+ .open = simple_playback_pcm_open,
+ .close = simple_playback_pcm_close,
+ .prepare = simple_playback_pcm_prepare
+ },
+};
+
+static const struct hda_codec_ops simple_hdmi_patch_ops = {
+ .build_controls = simple_playback_build_controls,
+ .build_pcms = simple_playback_build_pcms,
+ .init = simple_playback_init,
+ .free = simple_playback_free,
+ .unsol_event = simple_hdmi_unsol_event,
+};
+
+static int patch_simple_hdmi(struct hda_codec *codec,
+ hda_nid_t cvt_nid, hda_nid_t pin_nid)
+{
+ struct hdmi_spec *spec;
+ struct hdmi_spec_per_cvt *per_cvt;
+ struct hdmi_spec_per_pin *per_pin;
+
+ spec = kzalloc(sizeof(*spec), GFP_KERNEL);
+ if (!spec)
+ return -ENOMEM;
+
+ codec->spec = spec;
+ hdmi_array_init(spec, 1);
+
+ spec->multiout.num_dacs = 0; /* no analog */
+ spec->multiout.max_channels = 2;
+ spec->multiout.dig_out_nid = cvt_nid;
+ spec->num_cvts = 1;
+ spec->num_pins = 1;
+ per_pin = snd_array_new(&spec->pins);
+ per_cvt = snd_array_new(&spec->cvts);
+ if (!per_pin || !per_cvt) {
+ simple_playback_free(codec);
+ return -ENOMEM;
+ }
+ per_cvt->cvt_nid = cvt_nid;
+ per_pin->pin_nid = pin_nid;
+ spec->pcm_playback = simple_pcm_playback;
+
+ codec->patch_ops = simple_hdmi_patch_ops;
+
+ return 0;
+}
+
static void nvhdmi_8ch_7x_set_info_frame_parameters(struct hda_codec *codec,
int channels)
{
@@ -1345,23 +2692,27 @@ static int nvhdmi_8ch_7x_pcm_prepare(struct hda_pcm_stream *hinfo,
struct snd_pcm_substream *substream)
{
int chs;
- unsigned int dataDCC1, dataDCC2, channel_id;
+ unsigned int dataDCC2, channel_id;
int i;
+ struct hdmi_spec *spec = codec->spec;
+ struct hda_spdif_out *spdif;
+ struct hdmi_spec_per_cvt *per_cvt;
mutex_lock(&codec->spdif_mutex);
+ per_cvt = get_cvt(spec, 0);
+ spdif = snd_hda_spdif_out_of_nid(codec, per_cvt->cvt_nid);
chs = substream->runtime->channels;
- dataDCC1 = AC_DIG1_ENABLE | AC_DIG1_COPYRIGHT;
dataDCC2 = 0x2;
/* turn off SPDIF once; otherwise the IEC958 bits won't be updated */
- if (codec->spdif_status_reset && (codec->spdif_ctls & AC_DIG1_ENABLE))
+ if (codec->spdif_status_reset && (spdif->ctls & AC_DIG1_ENABLE))
snd_hda_codec_write(codec,
nvhdmi_master_con_nid_7x,
0,
AC_VERB_SET_DIGI_CONVERT_1,
- codec->spdif_ctls & ~AC_DIG1_ENABLE & 0xff);
+ spdif->ctls & ~AC_DIG1_ENABLE & 0xff);
/* set the stream id */
snd_hda_codec_write(codec, nvhdmi_master_con_nid_7x, 0,
@@ -1373,12 +2724,12 @@ static int nvhdmi_8ch_7x_pcm_prepare(struct hda_pcm_stream *hinfo,
/* turn on again (if needed) */
/* enable and set the channel status audio/data flag */
- if (codec->spdif_status_reset && (codec->spdif_ctls & AC_DIG1_ENABLE)) {
+ if (codec->spdif_status_reset && (spdif->ctls & AC_DIG1_ENABLE)) {
snd_hda_codec_write(codec,
nvhdmi_master_con_nid_7x,
0,
AC_VERB_SET_DIGI_CONVERT_1,
- codec->spdif_ctls & 0xff);
+ spdif->ctls & 0xff);
snd_hda_codec_write(codec,
nvhdmi_master_con_nid_7x,
0,
@@ -1395,12 +2746,12 @@ static int nvhdmi_8ch_7x_pcm_prepare(struct hda_pcm_stream *hinfo,
*otherwise the IEC958 bits won't be updated
*/
if (codec->spdif_status_reset &&
- (codec->spdif_ctls & AC_DIG1_ENABLE))
+ (spdif->ctls & AC_DIG1_ENABLE))
snd_hda_codec_write(codec,
nvhdmi_con_nids_7x[i],
0,
AC_VERB_SET_DIGI_CONVERT_1,
- codec->spdif_ctls & ~AC_DIG1_ENABLE & 0xff);
+ spdif->ctls & ~AC_DIG1_ENABLE & 0xff);
/* set the stream id */
snd_hda_codec_write(codec,
nvhdmi_con_nids_7x[i],
@@ -1416,12 +2767,12 @@ static int nvhdmi_8ch_7x_pcm_prepare(struct hda_pcm_stream *hinfo,
/* turn on again (if needed) */
/* enable and set the channel status audio/data flag */
if (codec->spdif_status_reset &&
- (codec->spdif_ctls & AC_DIG1_ENABLE)) {
+ (spdif->ctls & AC_DIG1_ENABLE)) {
snd_hda_codec_write(codec,
nvhdmi_con_nids_7x[i],
0,
AC_VERB_SET_DIGI_CONVERT_1,
- codec->spdif_ctls & 0xff);
+ spdif->ctls & 0xff);
snd_hda_codec_write(codec,
nvhdmi_con_nids_7x[i],
0,
@@ -1435,7 +2786,7 @@ static int nvhdmi_8ch_7x_pcm_prepare(struct hda_pcm_stream *hinfo,
return 0;
}
-static struct hda_pcm_stream nvhdmi_pcm_playback_8ch_7x = {
+static const struct hda_pcm_stream nvhdmi_pcm_playback_8ch_7x = {
.substreams = 1,
.channels_min = 2,
.channels_max = 8,
@@ -1450,67 +2801,62 @@ static struct hda_pcm_stream nvhdmi_pcm_playback_8ch_7x = {
},
};
-static struct hda_pcm_stream nvhdmi_pcm_playback_2ch = {
- .substreams = 1,
- .channels_min = 2,
- .channels_max = 2,
- .nid = nvhdmi_master_con_nid_7x,
- .rates = SUPPORTED_RATES,
- .maxbps = SUPPORTED_MAXBPS,
- .formats = SUPPORTED_FORMATS,
- .ops = {
- .open = simple_playback_pcm_open,
- .close = simple_playback_pcm_close,
- .prepare = simple_playback_pcm_prepare
- },
-};
-
-static struct hda_codec_ops nvhdmi_patch_ops_8ch_7x = {
- .build_controls = generic_hdmi_build_controls,
- .build_pcms = generic_hdmi_build_pcms,
- .init = nvhdmi_7x_init,
- .free = generic_hdmi_free,
-};
-
-static struct hda_codec_ops nvhdmi_patch_ops_2ch = {
- .build_controls = generic_hdmi_build_controls,
- .build_pcms = generic_hdmi_build_pcms,
- .init = nvhdmi_7x_init,
- .free = generic_hdmi_free,
-};
-
-static int patch_nvhdmi_8ch_89(struct hda_codec *codec)
+static int patch_nvhdmi_2ch(struct hda_codec *codec)
{
struct hdmi_spec *spec;
- int err = patch_generic_hdmi(codec);
-
+ int err = patch_simple_hdmi(codec, nvhdmi_master_con_nid_7x,
+ nvhdmi_master_pin_nid_7x);
if (err < 0)
return err;
+
+ codec->patch_ops.init = nvhdmi_7x_init_2ch;
+ /* override the PCM rates, etc, as the codec doesn't give full list */
spec = codec->spec;
- spec->old_pin_detect = 1;
+ spec->pcm_playback.rates = SUPPORTED_RATES;
+ spec->pcm_playback.maxbps = SUPPORTED_MAXBPS;
+ spec->pcm_playback.formats = SUPPORTED_FORMATS;
return 0;
}
-static int patch_nvhdmi_2ch(struct hda_codec *codec)
+static int nvhdmi_7x_8ch_build_pcms(struct hda_codec *codec)
{
- struct hdmi_spec *spec;
-
- spec = kzalloc(sizeof(*spec), GFP_KERNEL);
- if (spec == NULL)
- return -ENOMEM;
-
- codec->spec = spec;
+ struct hdmi_spec *spec = codec->spec;
+ int err = simple_playback_build_pcms(codec);
+ if (!err) {
+ struct hda_pcm *info = get_pcm_rec(spec, 0);
+ info->own_chmap = true;
+ }
+ return err;
+}
- spec->multiout.num_dacs = 0; /* no analog */
- spec->multiout.max_channels = 2;
- spec->multiout.dig_out_nid = nvhdmi_master_con_nid_7x;
- spec->old_pin_detect = 1;
- spec->num_cvts = 1;
- spec->cvt[0] = nvhdmi_master_con_nid_7x;
- spec->pcm_playback = &nvhdmi_pcm_playback_2ch;
+static int nvhdmi_7x_8ch_build_controls(struct hda_codec *codec)
+{
+ struct hdmi_spec *spec = codec->spec;
+ struct hda_pcm *info;
+ struct snd_pcm_chmap *chmap;
+ int err;
- codec->patch_ops = nvhdmi_patch_ops_2ch;
+ err = simple_playback_build_controls(codec);
+ if (err < 0)
+ return err;
+ /* add channel maps */
+ info = get_pcm_rec(spec, 0);
+ err = snd_pcm_add_chmap_ctls(info->pcm,
+ SNDRV_PCM_STREAM_PLAYBACK,
+ snd_pcm_alt_chmaps, 8, 0, &chmap);
+ if (err < 0)
+ return err;
+ switch (codec->preset->id) {
+ case 0x10de0002:
+ case 0x10de0003:
+ case 0x10de0005:
+ case 0x10de0006:
+ chmap->channel_mask = (1U << 2) | (1U << 8);
+ break;
+ case 0x10de0007:
+ chmap->channel_mask = (1U << 2) | (1U << 6) | (1U << 8);
+ }
return 0;
}
@@ -1518,13 +2864,14 @@ static int patch_nvhdmi_8ch_7x(struct hda_codec *codec)
{
struct hdmi_spec *spec;
int err = patch_nvhdmi_2ch(codec);
-
if (err < 0)
return err;
spec = codec->spec;
spec->multiout.max_channels = 8;
- spec->pcm_playback = &nvhdmi_pcm_playback_8ch_7x;
- codec->patch_ops = nvhdmi_patch_ops_8ch_7x;
+ spec->pcm_playback = nvhdmi_pcm_playback_8ch_7x;
+ codec->patch_ops.init = nvhdmi_7x_init_8ch;
+ codec->patch_ops.build_pcms = nvhdmi_7x_8ch_build_pcms;
+ codec->patch_ops.build_controls = nvhdmi_7x_8ch_build_controls;
/* Initialize the audio infoframe channel mask and checksum to something
* valid */
@@ -1534,111 +2881,428 @@ static int patch_nvhdmi_8ch_7x(struct hda_codec *codec)
}
/*
- * ATI-specific implementations
- *
- * FIXME: we may omit the whole this and use the generic code once after
- * it's confirmed to work.
+ * NVIDIA codecs ignore ASP mapping for 2ch - confirmed on:
+ * - 0x10de0015
+ * - 0x10de0040
*/
+static int nvhdmi_chmap_cea_alloc_validate_get_type(struct cea_channel_speaker_allocation *cap,
+ int channels)
+{
+ if (cap->ca_index == 0x00 && channels == 2)
+ return SNDRV_CTL_TLVT_CHMAP_FIXED;
-#define ATIHDMI_CVT_NID 0x02 /* audio converter */
-#define ATIHDMI_PIN_NID 0x03 /* HDMI output pin */
+ return hdmi_chmap_cea_alloc_validate_get_type(cap, channels);
+}
-static int atihdmi_playback_pcm_prepare(struct hda_pcm_stream *hinfo,
- struct hda_codec *codec,
- unsigned int stream_tag,
- unsigned int format,
- struct snd_pcm_substream *substream)
+static int nvhdmi_chmap_validate(int ca, int chs, unsigned char *map)
{
- struct hdmi_spec *spec = codec->spec;
- int chans = substream->runtime->channels;
- int i, err;
+ if (ca == 0x00 && (map[0] != SNDRV_CHMAP_FL || map[1] != SNDRV_CHMAP_FR))
+ return -EINVAL;
- err = simple_playback_pcm_prepare(hinfo, codec, stream_tag, format,
- substream);
- if (err < 0)
+ return 0;
+}
+
+static int patch_nvhdmi(struct hda_codec *codec)
+{
+ struct hdmi_spec *spec;
+ int err;
+
+ err = patch_generic_hdmi(codec);
+ if (err)
return err;
- snd_hda_codec_write(codec, spec->cvt[0], 0, AC_VERB_SET_CVT_CHAN_COUNT,
- chans - 1);
- /* FIXME: XXX */
- for (i = 0; i < chans; i++) {
- snd_hda_codec_write(codec, spec->cvt[0], 0,
- AC_VERB_SET_HDMI_CHAN_SLOT,
- (i << 4) | i);
+
+ spec = codec->spec;
+ spec->dyn_pin_out = true;
+
+ spec->ops.chmap_cea_alloc_validate_get_type =
+ nvhdmi_chmap_cea_alloc_validate_get_type;
+ spec->ops.chmap_validate = nvhdmi_chmap_validate;
+
+ return 0;
+}
+
+/*
+ * ATI/AMD-specific implementations
+ */
+
+#define is_amdhdmi_rev3_or_later(codec) \
+ ((codec)->vendor_id == 0x1002aa01 && ((codec)->revision_id & 0xff00) >= 0x0300)
+#define has_amd_full_remap_support(codec) is_amdhdmi_rev3_or_later(codec)
+
+/* ATI/AMD specific HDA pin verbs, see the AMD HDA Verbs specification */
+#define ATI_VERB_SET_CHANNEL_ALLOCATION 0x771
+#define ATI_VERB_SET_DOWNMIX_INFO 0x772
+#define ATI_VERB_SET_MULTICHANNEL_01 0x777
+#define ATI_VERB_SET_MULTICHANNEL_23 0x778
+#define ATI_VERB_SET_MULTICHANNEL_45 0x779
+#define ATI_VERB_SET_MULTICHANNEL_67 0x77a
+#define ATI_VERB_SET_HBR_CONTROL 0x77c
+#define ATI_VERB_SET_MULTICHANNEL_1 0x785
+#define ATI_VERB_SET_MULTICHANNEL_3 0x786
+#define ATI_VERB_SET_MULTICHANNEL_5 0x787
+#define ATI_VERB_SET_MULTICHANNEL_7 0x788
+#define ATI_VERB_SET_MULTICHANNEL_MODE 0x789
+#define ATI_VERB_GET_CHANNEL_ALLOCATION 0xf71
+#define ATI_VERB_GET_DOWNMIX_INFO 0xf72
+#define ATI_VERB_GET_MULTICHANNEL_01 0xf77
+#define ATI_VERB_GET_MULTICHANNEL_23 0xf78
+#define ATI_VERB_GET_MULTICHANNEL_45 0xf79
+#define ATI_VERB_GET_MULTICHANNEL_67 0xf7a
+#define ATI_VERB_GET_HBR_CONTROL 0xf7c
+#define ATI_VERB_GET_MULTICHANNEL_1 0xf85
+#define ATI_VERB_GET_MULTICHANNEL_3 0xf86
+#define ATI_VERB_GET_MULTICHANNEL_5 0xf87
+#define ATI_VERB_GET_MULTICHANNEL_7 0xf88
+#define ATI_VERB_GET_MULTICHANNEL_MODE 0xf89
+
+/* AMD specific HDA cvt verbs */
+#define ATI_VERB_SET_RAMP_RATE 0x770
+#define ATI_VERB_GET_RAMP_RATE 0xf70
+
+#define ATI_OUT_ENABLE 0x1
+
+#define ATI_MULTICHANNEL_MODE_PAIRED 0
+#define ATI_MULTICHANNEL_MODE_SINGLE 1
+
+#define ATI_HBR_CAPABLE 0x01
+#define ATI_HBR_ENABLE 0x10
+
+static int atihdmi_pin_get_eld(struct hda_codec *codec, hda_nid_t nid,
+ unsigned char *buf, int *eld_size)
+{
+ /* call hda_eld.c ATI/AMD-specific function */
+ return snd_hdmi_get_eld_ati(codec, nid, buf, eld_size,
+ is_amdhdmi_rev3_or_later(codec));
+}
+
+static void atihdmi_pin_setup_infoframe(struct hda_codec *codec, hda_nid_t pin_nid, int ca,
+ int active_channels, int conn_type)
+{
+ snd_hda_codec_write(codec, pin_nid, 0, ATI_VERB_SET_CHANNEL_ALLOCATION, ca);
+}
+
+static int atihdmi_paired_swap_fc_lfe(int pos)
+{
+ /*
+ * ATI/AMD have automatic FC/LFE swap built-in
+ * when in pairwise mapping mode.
+ */
+
+ switch (pos) {
+ /* see channel_allocations[].speakers[] */
+ case 2: return 3;
+ case 3: return 2;
+ default: break;
+ }
+
+ return pos;
+}
+
+static int atihdmi_paired_chmap_validate(int ca, int chs, unsigned char *map)
+{
+ struct cea_channel_speaker_allocation *cap;
+ int i, j;
+
+ /* check that only channel pairs need to be remapped on old pre-rev3 ATI/AMD */
+
+ cap = &channel_allocations[get_channel_allocation_order(ca)];
+ for (i = 0; i < chs; ++i) {
+ int mask = to_spk_mask(map[i]);
+ bool ok = false;
+ bool companion_ok = false;
+
+ if (!mask)
+ continue;
+
+ for (j = 0 + i % 2; j < 8; j += 2) {
+ int chan_idx = 7 - atihdmi_paired_swap_fc_lfe(j);
+ if (cap->speakers[chan_idx] == mask) {
+ /* channel is in a supported position */
+ ok = true;
+
+ if (i % 2 == 0 && i + 1 < chs) {
+ /* even channel, check the odd companion */
+ int comp_chan_idx = 7 - atihdmi_paired_swap_fc_lfe(j + 1);
+ int comp_mask_req = to_spk_mask(map[i+1]);
+ int comp_mask_act = cap->speakers[comp_chan_idx];
+
+ if (comp_mask_req == comp_mask_act)
+ companion_ok = true;
+ else
+ return -EINVAL;
+ }
+ break;
+ }
+ }
+
+ if (!ok)
+ return -EINVAL;
+
+ if (companion_ok)
+ i++; /* companion channel already checked */
}
+
return 0;
}
-static struct hda_pcm_stream atihdmi_pcm_digital_playback = {
- .substreams = 1,
- .channels_min = 2,
- .channels_max = 2,
- .nid = ATIHDMI_CVT_NID,
- .ops = {
- .open = simple_playback_pcm_open,
- .close = simple_playback_pcm_close,
- .prepare = atihdmi_playback_pcm_prepare
- },
-};
+static int atihdmi_pin_set_slot_channel(struct hda_codec *codec, hda_nid_t pin_nid,
+ int hdmi_slot, int stream_channel)
+{
+ int verb;
+ int ati_channel_setup = 0;
-static struct hda_verb atihdmi_basic_init[] = {
- /* enable digital output on pin widget */
- { 0x03, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT },
- {} /* terminator */
-};
+ if (hdmi_slot > 7)
+ return -EINVAL;
-static int atihdmi_init(struct hda_codec *codec)
+ if (!has_amd_full_remap_support(codec)) {
+ hdmi_slot = atihdmi_paired_swap_fc_lfe(hdmi_slot);
+
+ /* In case this is an odd slot but without stream channel, do not
+ * disable the slot since the corresponding even slot could have a
+ * channel. In case neither have a channel, the slot pair will be
+ * disabled when this function is called for the even slot. */
+ if (hdmi_slot % 2 != 0 && stream_channel == 0xf)
+ return 0;
+
+ hdmi_slot -= hdmi_slot % 2;
+
+ if (stream_channel != 0xf)
+ stream_channel -= stream_channel % 2;
+ }
+
+ verb = ATI_VERB_SET_MULTICHANNEL_01 + hdmi_slot/2 + (hdmi_slot % 2) * 0x00e;
+
+ /* ati_channel_setup format: [7..4] = stream_channel_id, [1] = mute, [0] = enable */
+
+ if (stream_channel != 0xf)
+ ati_channel_setup = (stream_channel << 4) | ATI_OUT_ENABLE;
+
+ return snd_hda_codec_write(codec, pin_nid, 0, verb, ati_channel_setup);
+}
+
+static int atihdmi_pin_get_slot_channel(struct hda_codec *codec, hda_nid_t pin_nid,
+ int asp_slot)
{
- struct hdmi_spec *spec = codec->spec;
+ bool was_odd = false;
+ int ati_asp_slot = asp_slot;
+ int verb;
+ int ati_channel_setup;
+
+ if (asp_slot > 7)
+ return -EINVAL;
+
+ if (!has_amd_full_remap_support(codec)) {
+ ati_asp_slot = atihdmi_paired_swap_fc_lfe(asp_slot);
+ if (ati_asp_slot % 2 != 0) {
+ ati_asp_slot -= 1;
+ was_odd = true;
+ }
+ }
+
+ verb = ATI_VERB_GET_MULTICHANNEL_01 + ati_asp_slot/2 + (ati_asp_slot % 2) * 0x00e;
+
+ ati_channel_setup = snd_hda_codec_read(codec, pin_nid, 0, verb, 0);
+
+ if (!(ati_channel_setup & ATI_OUT_ENABLE))
+ return 0xf;
+
+ return ((ati_channel_setup & 0xf0) >> 4) + !!was_odd;
+}
+
+static int atihdmi_paired_chmap_cea_alloc_validate_get_type(struct cea_channel_speaker_allocation *cap,
+ int channels)
+{
+ int c;
+
+ /*
+ * Pre-rev3 ATI/AMD codecs operate in a paired channel mode, so
+ * we need to take that into account (a single channel may take 2
+ * channel slots if we need to carry a silent channel next to it).
+ * On Rev3+ AMD codecs this function is not used.
+ */
+ int chanpairs = 0;
+
+ /* We only produce even-numbered channel count TLVs */
+ if ((channels % 2) != 0)
+ return -1;
+
+ for (c = 0; c < 7; c += 2) {
+ if (cap->speakers[c] || cap->speakers[c+1])
+ chanpairs++;
+ }
+
+ if (chanpairs * 2 != channels)
+ return -1;
+
+ return SNDRV_CTL_TLVT_CHMAP_PAIRED;
+}
+
+static void atihdmi_paired_cea_alloc_to_tlv_chmap(struct cea_channel_speaker_allocation *cap,
+ unsigned int *chmap, int channels)
+{
+ /* produce paired maps for pre-rev3 ATI/AMD codecs */
+ int count = 0;
+ int c;
+
+ for (c = 7; c >= 0; c--) {
+ int chan = 7 - atihdmi_paired_swap_fc_lfe(7 - c);
+ int spk = cap->speakers[chan];
+ if (!spk) {
+ /* add N/A channel if the companion channel is occupied */
+ if (cap->speakers[chan + (chan % 2 ? -1 : 1)])
+ chmap[count++] = SNDRV_CHMAP_NA;
+
+ continue;
+ }
+
+ chmap[count++] = spk_to_chmap(spk);
+ }
+
+ WARN_ON(count != channels);
+}
+
+static int atihdmi_pin_hbr_setup(struct hda_codec *codec, hda_nid_t pin_nid,
+ bool hbr)
+{
+ int hbr_ctl, hbr_ctl_new;
+
+ hbr_ctl = snd_hda_codec_read(codec, pin_nid, 0, ATI_VERB_GET_HBR_CONTROL, 0);
+ if (hbr_ctl >= 0 && (hbr_ctl & ATI_HBR_CAPABLE)) {
+ if (hbr)
+ hbr_ctl_new = hbr_ctl | ATI_HBR_ENABLE;
+ else
+ hbr_ctl_new = hbr_ctl & ~ATI_HBR_ENABLE;
+
+ codec_dbg(codec,
+ "atihdmi_pin_hbr_setup: NID=0x%x, %shbr-ctl=0x%x\n",
+ pin_nid,
+ hbr_ctl == hbr_ctl_new ? "" : "new-",
+ hbr_ctl_new);
+
+ if (hbr_ctl != hbr_ctl_new)
+ snd_hda_codec_write(codec, pin_nid, 0,
+ ATI_VERB_SET_HBR_CONTROL,
+ hbr_ctl_new);
+
+ } else if (hbr)
+ return -EINVAL;
- snd_hda_sequence_write(codec, atihdmi_basic_init);
- /* SI codec requires to unmute the pin */
- if (get_wcaps(codec, spec->pin[0]) & AC_WCAP_OUT_AMP)
- snd_hda_codec_write(codec, spec->pin[0], 0,
- AC_VERB_SET_AMP_GAIN_MUTE,
- AMP_OUT_UNMUTE);
return 0;
}
-static struct hda_codec_ops atihdmi_patch_ops = {
- .build_controls = generic_hdmi_build_controls,
- .build_pcms = generic_hdmi_build_pcms,
- .init = atihdmi_init,
- .free = generic_hdmi_free,
-};
+static int atihdmi_setup_stream(struct hda_codec *codec, hda_nid_t cvt_nid,
+ hda_nid_t pin_nid, u32 stream_tag, int format)
+{
+
+ if (is_amdhdmi_rev3_or_later(codec)) {
+ int ramp_rate = 180; /* default as per AMD spec */
+ /* disable ramp-up/down for non-pcm as per AMD spec */
+ if (format & AC_FMT_TYPE_NON_PCM)
+ ramp_rate = 0;
+
+ snd_hda_codec_write(codec, cvt_nid, 0, ATI_VERB_SET_RAMP_RATE, ramp_rate);
+ }
+
+ return hdmi_setup_stream(codec, cvt_nid, pin_nid, stream_tag, format);
+}
+
+
+static int atihdmi_init(struct hda_codec *codec)
+{
+ struct hdmi_spec *spec = codec->spec;
+ int pin_idx, err;
+
+ err = generic_hdmi_init(codec);
+
+ if (err)
+ return err;
+ for (pin_idx = 0; pin_idx < spec->num_pins; pin_idx++) {
+ struct hdmi_spec_per_pin *per_pin = get_pin(spec, pin_idx);
+
+ /* make sure downmix information in infoframe is zero */
+ snd_hda_codec_write(codec, per_pin->pin_nid, 0, ATI_VERB_SET_DOWNMIX_INFO, 0);
+
+ /* enable channel-wise remap mode if supported */
+ if (has_amd_full_remap_support(codec))
+ snd_hda_codec_write(codec, per_pin->pin_nid, 0,
+ ATI_VERB_SET_MULTICHANNEL_MODE,
+ ATI_MULTICHANNEL_MODE_SINGLE);
+ }
+
+ return 0;
+}
static int patch_atihdmi(struct hda_codec *codec)
{
struct hdmi_spec *spec;
+ struct hdmi_spec_per_cvt *per_cvt;
+ int err, cvt_idx;
- spec = kzalloc(sizeof(*spec), GFP_KERNEL);
- if (spec == NULL)
- return -ENOMEM;
+ err = patch_generic_hdmi(codec);
- codec->spec = spec;
+ if (err)
+ return err;
- spec->multiout.num_dacs = 0; /* no analog */
- spec->multiout.max_channels = 2;
- spec->multiout.dig_out_nid = ATIHDMI_CVT_NID;
- spec->num_cvts = 1;
- spec->cvt[0] = ATIHDMI_CVT_NID;
- spec->pin[0] = ATIHDMI_PIN_NID;
- spec->pcm_playback = &atihdmi_pcm_digital_playback;
+ codec->patch_ops.init = atihdmi_init;
- codec->patch_ops = atihdmi_patch_ops;
+ spec = codec->spec;
+
+ spec->ops.pin_get_eld = atihdmi_pin_get_eld;
+ spec->ops.pin_get_slot_channel = atihdmi_pin_get_slot_channel;
+ spec->ops.pin_set_slot_channel = atihdmi_pin_set_slot_channel;
+ spec->ops.pin_setup_infoframe = atihdmi_pin_setup_infoframe;
+ spec->ops.pin_hbr_setup = atihdmi_pin_hbr_setup;
+ spec->ops.setup_stream = atihdmi_setup_stream;
+
+ if (!has_amd_full_remap_support(codec)) {
+ /* override to ATI/AMD-specific versions with pairwise mapping */
+ spec->ops.chmap_cea_alloc_validate_get_type =
+ atihdmi_paired_chmap_cea_alloc_validate_get_type;
+ spec->ops.cea_alloc_to_tlv_chmap = atihdmi_paired_cea_alloc_to_tlv_chmap;
+ spec->ops.chmap_validate = atihdmi_paired_chmap_validate;
+ }
+
+ /* ATI/AMD converters do not advertise all of their capabilities */
+ for (cvt_idx = 0; cvt_idx < spec->num_cvts; cvt_idx++) {
+ per_cvt = get_cvt(spec, cvt_idx);
+ per_cvt->channels_max = max(per_cvt->channels_max, 8u);
+ per_cvt->rates |= SUPPORTED_RATES;
+ per_cvt->formats |= SUPPORTED_FORMATS;
+ per_cvt->maxbps = max(per_cvt->maxbps, 24u);
+ }
+
+ spec->channels_max = max(spec->channels_max, 8u);
return 0;
}
+/* VIA HDMI Implementation */
+#define VIAHDMI_CVT_NID 0x02 /* audio converter1 */
+#define VIAHDMI_PIN_NID 0x03 /* HDMI output pin1 */
+
+static int patch_via_hdmi(struct hda_codec *codec)
+{
+ return patch_simple_hdmi(codec, VIAHDMI_CVT_NID, VIAHDMI_PIN_NID);
+}
+
+/*
+ * called from hda_codec.c for generic HDMI support
+ */
+int snd_hda_parse_hdmi_codec(struct hda_codec *codec)
+{
+ return patch_generic_hdmi(codec);
+}
+EXPORT_SYMBOL_GPL(snd_hda_parse_hdmi_codec);
/*
* patch entries
*/
-static struct hda_codec_preset snd_hda_preset_hdmi[] = {
+static const struct hda_codec_preset snd_hda_preset_hdmi[] = {
{ .id = 0x1002793c, .name = "RS600 HDMI", .patch = patch_atihdmi },
{ .id = 0x10027919, .name = "RS600 HDMI", .patch = patch_atihdmi },
{ .id = 0x1002791a, .name = "RS690/780 HDMI", .patch = patch_atihdmi },
-{ .id = 0x1002aa01, .name = "R6xx HDMI", .patch = patch_generic_hdmi },
+{ .id = 0x1002aa01, .name = "R6xx HDMI", .patch = patch_atihdmi },
{ .id = 0x10951390, .name = "SiI1390 HDMI", .patch = patch_generic_hdmi },
{ .id = 0x10951392, .name = "SiI1392 HDMI", .patch = patch_generic_hdmi },
{ .id = 0x17e80047, .name = "Chrontel HDMI", .patch = patch_generic_hdmi },
@@ -1647,36 +3311,50 @@ static struct hda_codec_preset snd_hda_preset_hdmi[] = {
{ .id = 0x10de0005, .name = "MCP77/78 HDMI", .patch = patch_nvhdmi_8ch_7x },
{ .id = 0x10de0006, .name = "MCP77/78 HDMI", .patch = patch_nvhdmi_8ch_7x },
{ .id = 0x10de0007, .name = "MCP79/7A HDMI", .patch = patch_nvhdmi_8ch_7x },
-{ .id = 0x10de000a, .name = "GPU 0a HDMI/DP", .patch = patch_nvhdmi_8ch_89 },
-{ .id = 0x10de000b, .name = "GPU 0b HDMI/DP", .patch = patch_nvhdmi_8ch_89 },
-{ .id = 0x10de000c, .name = "MCP89 HDMI", .patch = patch_nvhdmi_8ch_89 },
-{ .id = 0x10de000d, .name = "GPU 0d HDMI/DP", .patch = patch_nvhdmi_8ch_89 },
-{ .id = 0x10de0010, .name = "GPU 10 HDMI/DP", .patch = patch_nvhdmi_8ch_89 },
-{ .id = 0x10de0011, .name = "GPU 11 HDMI/DP", .patch = patch_nvhdmi_8ch_89 },
-{ .id = 0x10de0012, .name = "GPU 12 HDMI/DP", .patch = patch_nvhdmi_8ch_89 },
-{ .id = 0x10de0013, .name = "GPU 13 HDMI/DP", .patch = patch_nvhdmi_8ch_89 },
-{ .id = 0x10de0014, .name = "GPU 14 HDMI/DP", .patch = patch_nvhdmi_8ch_89 },
-{ .id = 0x10de0015, .name = "GPU 15 HDMI/DP", .patch = patch_nvhdmi_8ch_89 },
-{ .id = 0x10de0016, .name = "GPU 16 HDMI/DP", .patch = patch_nvhdmi_8ch_89 },
+{ .id = 0x10de000a, .name = "GPU 0a HDMI/DP", .patch = patch_nvhdmi },
+{ .id = 0x10de000b, .name = "GPU 0b HDMI/DP", .patch = patch_nvhdmi },
+{ .id = 0x10de000c, .name = "MCP89 HDMI", .patch = patch_nvhdmi },
+{ .id = 0x10de000d, .name = "GPU 0d HDMI/DP", .patch = patch_nvhdmi },
+{ .id = 0x10de0010, .name = "GPU 10 HDMI/DP", .patch = patch_nvhdmi },
+{ .id = 0x10de0011, .name = "GPU 11 HDMI/DP", .patch = patch_nvhdmi },
+{ .id = 0x10de0012, .name = "GPU 12 HDMI/DP", .patch = patch_nvhdmi },
+{ .id = 0x10de0013, .name = "GPU 13 HDMI/DP", .patch = patch_nvhdmi },
+{ .id = 0x10de0014, .name = "GPU 14 HDMI/DP", .patch = patch_nvhdmi },
+{ .id = 0x10de0015, .name = "GPU 15 HDMI/DP", .patch = patch_nvhdmi },
+{ .id = 0x10de0016, .name = "GPU 16 HDMI/DP", .patch = patch_nvhdmi },
/* 17 is known to be absent */
-{ .id = 0x10de0018, .name = "GPU 18 HDMI/DP", .patch = patch_nvhdmi_8ch_89 },
-{ .id = 0x10de0019, .name = "GPU 19 HDMI/DP", .patch = patch_nvhdmi_8ch_89 },
-{ .id = 0x10de001a, .name = "GPU 1a HDMI/DP", .patch = patch_nvhdmi_8ch_89 },
-{ .id = 0x10de001b, .name = "GPU 1b HDMI/DP", .patch = patch_nvhdmi_8ch_89 },
-{ .id = 0x10de001c, .name = "GPU 1c HDMI/DP", .patch = patch_nvhdmi_8ch_89 },
-{ .id = 0x10de0040, .name = "GPU 40 HDMI/DP", .patch = patch_nvhdmi_8ch_89 },
-{ .id = 0x10de0041, .name = "GPU 41 HDMI/DP", .patch = patch_nvhdmi_8ch_89 },
-{ .id = 0x10de0042, .name = "GPU 42 HDMI/DP", .patch = patch_nvhdmi_8ch_89 },
-{ .id = 0x10de0043, .name = "GPU 43 HDMI/DP", .patch = patch_nvhdmi_8ch_89 },
-{ .id = 0x10de0044, .name = "GPU 44 HDMI/DP", .patch = patch_nvhdmi_8ch_89 },
+{ .id = 0x10de0018, .name = "GPU 18 HDMI/DP", .patch = patch_nvhdmi },
+{ .id = 0x10de0019, .name = "GPU 19 HDMI/DP", .patch = patch_nvhdmi },
+{ .id = 0x10de001a, .name = "GPU 1a HDMI/DP", .patch = patch_nvhdmi },
+{ .id = 0x10de001b, .name = "GPU 1b HDMI/DP", .patch = patch_nvhdmi },
+{ .id = 0x10de001c, .name = "GPU 1c HDMI/DP", .patch = patch_nvhdmi },
+{ .id = 0x10de0028, .name = "Tegra12x HDMI", .patch = patch_nvhdmi },
+{ .id = 0x10de0040, .name = "GPU 40 HDMI/DP", .patch = patch_nvhdmi },
+{ .id = 0x10de0041, .name = "GPU 41 HDMI/DP", .patch = patch_nvhdmi },
+{ .id = 0x10de0042, .name = "GPU 42 HDMI/DP", .patch = patch_nvhdmi },
+{ .id = 0x10de0043, .name = "GPU 43 HDMI/DP", .patch = patch_nvhdmi },
+{ .id = 0x10de0044, .name = "GPU 44 HDMI/DP", .patch = patch_nvhdmi },
+{ .id = 0x10de0051, .name = "GPU 51 HDMI/DP", .patch = patch_nvhdmi },
+{ .id = 0x10de0060, .name = "GPU 60 HDMI/DP", .patch = patch_nvhdmi },
{ .id = 0x10de0067, .name = "MCP67 HDMI", .patch = patch_nvhdmi_2ch },
+{ .id = 0x10de0070, .name = "GPU 70 HDMI/DP", .patch = patch_nvhdmi },
+{ .id = 0x10de0071, .name = "GPU 71 HDMI/DP", .patch = patch_nvhdmi },
{ .id = 0x10de8001, .name = "MCP73 HDMI", .patch = patch_nvhdmi_2ch },
+{ .id = 0x11069f80, .name = "VX900 HDMI/DP", .patch = patch_via_hdmi },
+{ .id = 0x11069f81, .name = "VX900 HDMI/DP", .patch = patch_via_hdmi },
+{ .id = 0x11069f84, .name = "VX11 HDMI/DP", .patch = patch_generic_hdmi },
+{ .id = 0x11069f85, .name = "VX11 HDMI/DP", .patch = patch_generic_hdmi },
{ .id = 0x80860054, .name = "IbexPeak HDMI", .patch = patch_generic_hdmi },
{ .id = 0x80862801, .name = "Bearlake HDMI", .patch = patch_generic_hdmi },
{ .id = 0x80862802, .name = "Cantiga HDMI", .patch = patch_generic_hdmi },
{ .id = 0x80862803, .name = "Eaglelake HDMI", .patch = patch_generic_hdmi },
{ .id = 0x80862804, .name = "IbexPeak HDMI", .patch = patch_generic_hdmi },
{ .id = 0x80862805, .name = "CougarPoint HDMI", .patch = patch_generic_hdmi },
+{ .id = 0x80862806, .name = "PantherPoint HDMI", .patch = patch_generic_hdmi },
+{ .id = 0x80862807, .name = "Haswell HDMI", .patch = patch_generic_hdmi },
+{ .id = 0x80862808, .name = "Broadwell HDMI", .patch = patch_generic_hdmi },
+{ .id = 0x80862880, .name = "CedarTrail HDMI", .patch = patch_generic_hdmi },
+{ .id = 0x80862882, .name = "Valleyview2 HDMI", .patch = patch_generic_hdmi },
{ .id = 0x808629fb, .name = "Crestline HDMI", .patch = patch_generic_hdmi },
{} /* terminator */
};
@@ -1708,13 +3386,22 @@ MODULE_ALIAS("snd-hda-codec-id:10de0019");
MODULE_ALIAS("snd-hda-codec-id:10de001a");
MODULE_ALIAS("snd-hda-codec-id:10de001b");
MODULE_ALIAS("snd-hda-codec-id:10de001c");
+MODULE_ALIAS("snd-hda-codec-id:10de0028");
MODULE_ALIAS("snd-hda-codec-id:10de0040");
MODULE_ALIAS("snd-hda-codec-id:10de0041");
MODULE_ALIAS("snd-hda-codec-id:10de0042");
MODULE_ALIAS("snd-hda-codec-id:10de0043");
MODULE_ALIAS("snd-hda-codec-id:10de0044");
+MODULE_ALIAS("snd-hda-codec-id:10de0051");
+MODULE_ALIAS("snd-hda-codec-id:10de0060");
MODULE_ALIAS("snd-hda-codec-id:10de0067");
+MODULE_ALIAS("snd-hda-codec-id:10de0070");
+MODULE_ALIAS("snd-hda-codec-id:10de0071");
MODULE_ALIAS("snd-hda-codec-id:10de8001");
+MODULE_ALIAS("snd-hda-codec-id:11069f80");
+MODULE_ALIAS("snd-hda-codec-id:11069f81");
+MODULE_ALIAS("snd-hda-codec-id:11069f84");
+MODULE_ALIAS("snd-hda-codec-id:11069f85");
MODULE_ALIAS("snd-hda-codec-id:17e80047");
MODULE_ALIAS("snd-hda-codec-id:80860054");
MODULE_ALIAS("snd-hda-codec-id:80862801");
@@ -1722,6 +3409,11 @@ MODULE_ALIAS("snd-hda-codec-id:80862802");
MODULE_ALIAS("snd-hda-codec-id:80862803");
MODULE_ALIAS("snd-hda-codec-id:80862804");
MODULE_ALIAS("snd-hda-codec-id:80862805");
+MODULE_ALIAS("snd-hda-codec-id:80862806");
+MODULE_ALIAS("snd-hda-codec-id:80862807");
+MODULE_ALIAS("snd-hda-codec-id:80862808");
+MODULE_ALIAS("snd-hda-codec-id:80862880");
+MODULE_ALIAS("snd-hda-codec-id:80862882");
MODULE_ALIAS("snd-hda-codec-id:808629fb");
MODULE_LICENSE("GPL");
diff --git a/sound/pci/hda/patch_realtek.c b/sound/pci/hda/patch_realtek.c
index 52928d9a72d..b60824e9040 100644
--- a/sound/pci/hda/patch_realtek.c
+++ b/sound/pci/hda/patch_realtek.c
@@ -1,12 +1,12 @@
/*
* Universal Interface for Intel High Definition Audio Codec
*
- * HD audio interface patch for ALC 260/880/882 codecs
+ * HD audio interface patch for Realtek ALC codecs
*
* Copyright (c) 2004 Kailang Yang <kailang@realtek.com.tw>
* PeiSen Hou <pshou@realtek.com.tw>
* Takashi Iwai <tiwai@suse.de>
- * Jonathan Woithe <jwoithe@physics.adelaide.edu.au>
+ * Jonathan Woithe <jwoithe@just42.net>
*
* This driver is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@@ -27,242 +27,21 @@
#include <linux/delay.h>
#include <linux/slab.h>
#include <linux/pci.h>
+#include <linux/dmi.h>
+#include <linux/module.h>
#include <sound/core.h>
#include <sound/jack.h>
#include "hda_codec.h"
#include "hda_local.h"
-#include "hda_beep.h"
+#include "hda_auto_parser.h"
+#include "hda_jack.h"
+#include "hda_generic.h"
-#define ALC880_FRONT_EVENT 0x01
-#define ALC880_DCVOL_EVENT 0x02
-#define ALC880_HP_EVENT 0x04
-#define ALC880_MIC_EVENT 0x08
+/* keep halting ALC5505 DSP, for power saving */
+#define HALT_REALTEK_ALC5505
-/* ALC880 board config type */
-enum {
- ALC880_3ST,
- ALC880_3ST_DIG,
- ALC880_5ST,
- ALC880_5ST_DIG,
- ALC880_W810,
- ALC880_Z71V,
- ALC880_6ST,
- ALC880_6ST_DIG,
- ALC880_F1734,
- ALC880_ASUS,
- ALC880_ASUS_DIG,
- ALC880_ASUS_W1V,
- ALC880_ASUS_DIG2,
- ALC880_FUJITSU,
- ALC880_UNIWILL_DIG,
- ALC880_UNIWILL,
- ALC880_UNIWILL_P53,
- ALC880_CLEVO,
- ALC880_TCL_S700,
- ALC880_LG,
- ALC880_LG_LW,
- ALC880_MEDION_RIM,
-#ifdef CONFIG_SND_DEBUG
- ALC880_TEST,
-#endif
- ALC880_AUTO,
- ALC880_MODEL_LAST /* last tag */
-};
-
-/* ALC260 models */
-enum {
- ALC260_BASIC,
- ALC260_HP,
- ALC260_HP_DC7600,
- ALC260_HP_3013,
- ALC260_FUJITSU_S702X,
- ALC260_ACER,
- ALC260_WILL,
- ALC260_REPLACER_672V,
- ALC260_FAVORIT100,
-#ifdef CONFIG_SND_DEBUG
- ALC260_TEST,
-#endif
- ALC260_AUTO,
- ALC260_MODEL_LAST /* last tag */
-};
-
-/* ALC262 models */
-enum {
- ALC262_BASIC,
- ALC262_HIPPO,
- ALC262_HIPPO_1,
- ALC262_FUJITSU,
- ALC262_HP_BPC,
- ALC262_HP_BPC_D7000_WL,
- ALC262_HP_BPC_D7000_WF,
- ALC262_HP_TC_T5735,
- ALC262_HP_RP5700,
- ALC262_BENQ_ED8,
- ALC262_SONY_ASSAMD,
- ALC262_BENQ_T31,
- ALC262_ULTRA,
- ALC262_LENOVO_3000,
- ALC262_NEC,
- ALC262_TOSHIBA_S06,
- ALC262_TOSHIBA_RX1,
- ALC262_TYAN,
- ALC262_AUTO,
- ALC262_MODEL_LAST /* last tag */
-};
-
-/* ALC268 models */
-enum {
- ALC267_QUANTA_IL1,
- ALC268_3ST,
- ALC268_TOSHIBA,
- ALC268_ACER,
- ALC268_ACER_DMIC,
- ALC268_ACER_ASPIRE_ONE,
- ALC268_DELL,
- ALC268_ZEPTO,
-#ifdef CONFIG_SND_DEBUG
- ALC268_TEST,
-#endif
- ALC268_AUTO,
- ALC268_MODEL_LAST /* last tag */
-};
-
-/* ALC269 models */
-enum {
- ALC269_BASIC,
- ALC269_QUANTA_FL1,
- ALC269_AMIC,
- ALC269_DMIC,
- ALC269VB_AMIC,
- ALC269VB_DMIC,
- ALC269_FUJITSU,
- ALC269_LIFEBOOK,
- ALC271_ACER,
- ALC269_AUTO,
- ALC269_MODEL_LAST /* last tag */
-};
-
-/* ALC861 models */
-enum {
- ALC861_3ST,
- ALC660_3ST,
- ALC861_3ST_DIG,
- ALC861_6ST_DIG,
- ALC861_UNIWILL_M31,
- ALC861_TOSHIBA,
- ALC861_ASUS,
- ALC861_ASUS_LAPTOP,
- ALC861_AUTO,
- ALC861_MODEL_LAST,
-};
-
-/* ALC861-VD models */
-enum {
- ALC660VD_3ST,
- ALC660VD_3ST_DIG,
- ALC660VD_ASUS_V1S,
- ALC861VD_3ST,
- ALC861VD_3ST_DIG,
- ALC861VD_6ST_DIG,
- ALC861VD_LENOVO,
- ALC861VD_DALLAS,
- ALC861VD_HP,
- ALC861VD_AUTO,
- ALC861VD_MODEL_LAST,
-};
-
-/* ALC662 models */
-enum {
- ALC662_3ST_2ch_DIG,
- ALC662_3ST_6ch_DIG,
- ALC662_3ST_6ch,
- ALC662_5ST_DIG,
- ALC662_LENOVO_101E,
- ALC662_ASUS_EEEPC_P701,
- ALC662_ASUS_EEEPC_EP20,
- ALC663_ASUS_M51VA,
- ALC663_ASUS_G71V,
- ALC663_ASUS_H13,
- ALC663_ASUS_G50V,
- ALC662_ECS,
- ALC663_ASUS_MODE1,
- ALC662_ASUS_MODE2,
- ALC663_ASUS_MODE3,
- ALC663_ASUS_MODE4,
- ALC663_ASUS_MODE5,
- ALC663_ASUS_MODE6,
- ALC663_ASUS_MODE7,
- ALC663_ASUS_MODE8,
- ALC272_DELL,
- ALC272_DELL_ZM1,
- ALC272_SAMSUNG_NC10,
- ALC662_AUTO,
- ALC662_MODEL_LAST,
-};
-
-/* ALC882 models */
-enum {
- ALC882_3ST_DIG,
- ALC882_6ST_DIG,
- ALC882_ARIMA,
- ALC882_W2JC,
- ALC882_TARGA,
- ALC882_ASUS_A7J,
- ALC882_ASUS_A7M,
- ALC885_MACPRO,
- ALC885_MBA21,
- ALC885_MBP3,
- ALC885_MB5,
- ALC885_MACMINI3,
- ALC885_IMAC24,
- ALC885_IMAC91,
- ALC883_3ST_2ch_DIG,
- ALC883_3ST_6ch_DIG,
- ALC883_3ST_6ch,
- ALC883_6ST_DIG,
- ALC883_TARGA_DIG,
- ALC883_TARGA_2ch_DIG,
- ALC883_TARGA_8ch_DIG,
- ALC883_ACER,
- ALC883_ACER_ASPIRE,
- ALC888_ACER_ASPIRE_4930G,
- ALC888_ACER_ASPIRE_6530G,
- ALC888_ACER_ASPIRE_8930G,
- ALC888_ACER_ASPIRE_7730G,
- ALC883_MEDION,
- ALC883_MEDION_WIM2160,
- ALC883_LAPTOP_EAPD,
- ALC883_LENOVO_101E_2ch,
- ALC883_LENOVO_NB0763,
- ALC888_LENOVO_MS7195_DIG,
- ALC888_LENOVO_SKY,
- ALC883_HAIER_W66,
- ALC888_3ST_HP,
- ALC888_6ST_DELL,
- ALC883_MITAC,
- ALC883_CLEVO_M540R,
- ALC883_CLEVO_M720,
- ALC883_FUJITSU_PI2515,
- ALC888_FUJITSU_XA3530,
- ALC883_3ST_6ch_INTEL,
- ALC889A_INTEL,
- ALC889_INTEL,
- ALC888_ASUS_M90V,
- ALC888_ASUS_EEE1601,
- ALC889A_MB31,
- ALC1200_ASUS_P5Q,
- ALC883_SONY_VAIO_TT,
- ALC882_AUTO,
- ALC882_MODEL_LAST,
-};
-
-/* ALC680 models */
-enum {
- ALC680_BASE,
- ALC680_AUTO,
- ALC680_MODEL_LAST,
-};
+/* unsol event tags */
+#define ALC_DCVOL_EVENT 0x08
/* for GPIO Poll */
#define GPIO_MASK 0x03
@@ -276,13 +55,19 @@ enum {
ALC_INIT_GPIO3,
};
-struct alc_mic_route {
- hda_nid_t pin;
- unsigned char mux_idx;
- unsigned char amix_idx;
+enum {
+ ALC_HEADSET_MODE_UNKNOWN,
+ ALC_HEADSET_MODE_UNPLUGGED,
+ ALC_HEADSET_MODE_HEADSET,
+ ALC_HEADSET_MODE_MIC,
+ ALC_HEADSET_MODE_HEADPHONE,
};
-#define MUX_IDX_UNDEF ((unsigned char)-1)
+enum {
+ ALC_HEADSET_TYPE_UNKNOWN,
+ ALC_HEADSET_TYPE_CTIA,
+ ALC_HEADSET_TYPE_OMTP,
+};
struct alc_customize_define {
unsigned int sku_cfg;
@@ -297,695 +82,82 @@ struct alc_customize_define {
unsigned int fixup:1; /* Means that this sku is set by driver, not read from hw */
};
-struct alc_fixup;
-
struct alc_spec {
+ struct hda_gen_spec gen; /* must be at head */
+
/* codec parameterization */
- struct snd_kcontrol_new *mixers[5]; /* mixer arrays */
+ const struct snd_kcontrol_new *mixers[5]; /* mixer arrays */
unsigned int num_mixers;
- struct snd_kcontrol_new *cap_mixer; /* capture mixer */
unsigned int beep_amp; /* beep amp value, set via set_beep_amp() */
- const struct hda_verb *init_verbs[10]; /* initialization verbs
- * don't forget NULL
- * termination!
- */
- unsigned int num_init_verbs;
-
- char stream_name_analog[32]; /* analog PCM stream */
- struct hda_pcm_stream *stream_analog_playback;
- struct hda_pcm_stream *stream_analog_capture;
- struct hda_pcm_stream *stream_analog_alt_playback;
- struct hda_pcm_stream *stream_analog_alt_capture;
-
- char stream_name_digital[32]; /* digital PCM stream */
- struct hda_pcm_stream *stream_digital_playback;
- struct hda_pcm_stream *stream_digital_capture;
-
- /* playback */
- struct hda_multi_out multiout; /* playback set-up
- * max_channels, dacs must be set
- * dig_out_nid and hp_nid are optional
- */
- hda_nid_t alt_dac_nid;
- hda_nid_t slave_dig_outs[3]; /* optional - for auto-parsing */
- int dig_out_type;
-
- /* capture */
- unsigned int num_adc_nids;
- hda_nid_t *adc_nids;
- hda_nid_t *capsrc_nids;
- hda_nid_t dig_in_nid; /* digital-in NID; optional */
-
- /* capture setup for dynamic dual-adc switch */
- unsigned int cur_adc_idx;
- hda_nid_t cur_adc;
- unsigned int cur_adc_stream_tag;
- unsigned int cur_adc_format;
-
- /* capture source */
- unsigned int num_mux_defs;
- const struct hda_input_mux *input_mux;
- unsigned int cur_mux[3];
- struct alc_mic_route ext_mic;
- struct alc_mic_route int_mic;
-
- /* channel model */
- const struct hda_channel_mode *channel_mode;
- int num_channel_mode;
- int need_dac_fix;
- int const_channel_count;
- int ext_channel_count;
-
- /* PCM information */
- struct hda_pcm pcm_rec[3]; /* used in alc_build_pcms() */
-
- /* dynamic controls, init_verbs and input_mux */
- struct auto_pin_cfg autocfg;
struct alc_customize_define cdefine;
- struct snd_array kctls;
- struct hda_input_mux private_imux[3];
- hda_nid_t private_dac_nids[AUTO_CFG_MAX_OUTS];
- hda_nid_t private_adc_nids[AUTO_CFG_MAX_OUTS];
- hda_nid_t private_capsrc_nids[AUTO_CFG_MAX_OUTS];
+ unsigned int parse_flags; /* flag for snd_hda_parse_pin_defcfg() */
+
+ /* inverted dmic fix */
+ unsigned int inv_dmic_fixup:1; /* has inverted digital-mic workaround */
+ unsigned int inv_dmic_muted:1; /* R-ch of inv d-mic is muted? */
+ hda_nid_t inv_dmic_pin;
+
+ /* mute LED for HP laptops, see alc269_fixup_mic_mute_hook() */
+ int mute_led_polarity;
+ hda_nid_t mute_led_nid;
+
+ unsigned int gpio_led; /* used for alc269_fixup_hp_gpio_led() */
+
+ hda_nid_t headset_mic_pin;
+ hda_nid_t headphone_mic_pin;
+ int current_headset_mode;
+ int current_headset_type;
/* hooks */
void (*init_hook)(struct hda_codec *codec);
- void (*unsol_event)(struct hda_codec *codec, unsigned int res);
-#ifdef CONFIG_SND_HDA_POWER_SAVE
+#ifdef CONFIG_PM
void (*power_hook)(struct hda_codec *codec);
#endif
+ void (*shutup)(struct hda_codec *codec);
- /* for pin sensing */
- unsigned int sense_updated: 1;
- unsigned int jack_present: 1;
- unsigned int master_sw: 1;
- unsigned int auto_mic:1;
-
- /* other flags */
- unsigned int no_analog :1; /* digital I/O only */
- unsigned int dual_adc_switch:1; /* switch ADCs (for ALC275) */
- unsigned int single_input_src:1;
int init_amp;
int codec_variant; /* flag for other variants */
-
- /* for virtual master */
- hda_nid_t vmaster_nid;
-#ifdef CONFIG_SND_HDA_POWER_SAVE
- struct hda_loopback_check loopback;
-#endif
+ unsigned int has_alc5505_dsp:1;
+ unsigned int no_depop_delay:1;
/* for PLL fix */
hda_nid_t pll_nid;
unsigned int pll_coef_idx, pll_coef_bit;
-
- /* fix-up list */
- int fixup_id;
- const struct alc_fixup *fixup_list;
- const char *fixup_name;
+ unsigned int coef0;
};
/*
- * configuration template - to be copied to the spec instance
- */
-struct alc_config_preset {
- struct snd_kcontrol_new *mixers[5]; /* should be identical size
- * with spec
- */
- struct snd_kcontrol_new *cap_mixer; /* capture mixer */
- const struct hda_verb *init_verbs[5];
- unsigned int num_dacs;
- hda_nid_t *dac_nids;
- hda_nid_t dig_out_nid; /* optional */
- hda_nid_t hp_nid; /* optional */
- hda_nid_t *slave_dig_outs;
- unsigned int num_adc_nids;
- hda_nid_t *adc_nids;
- hda_nid_t *capsrc_nids;
- hda_nid_t dig_in_nid;
- unsigned int num_channel_mode;
- const struct hda_channel_mode *channel_mode;
- int need_dac_fix;
- int const_channel_count;
- unsigned int num_mux_defs;
- const struct hda_input_mux *input_mux;
- void (*unsol_event)(struct hda_codec *, unsigned int);
- void (*setup)(struct hda_codec *);
- void (*init_hook)(struct hda_codec *);
-#ifdef CONFIG_SND_HDA_POWER_SAVE
- struct hda_amp_list *loopbacks;
- void (*power_hook)(struct hda_codec *codec);
-#endif
-};
-
-
-/*
- * input MUX handling
- */
-static int alc_mux_enum_info(struct snd_kcontrol *kcontrol,
- struct snd_ctl_elem_info *uinfo)
-{
- struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
- struct alc_spec *spec = codec->spec;
- unsigned int mux_idx = snd_ctl_get_ioffidx(kcontrol, &uinfo->id);
- if (mux_idx >= spec->num_mux_defs)
- mux_idx = 0;
- if (!spec->input_mux[mux_idx].num_items && mux_idx > 0)
- mux_idx = 0;
- return snd_hda_input_mux_info(&spec->input_mux[mux_idx], uinfo);
-}
-
-static int alc_mux_enum_get(struct snd_kcontrol *kcontrol,
- struct snd_ctl_elem_value *ucontrol)
-{
- struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
- struct alc_spec *spec = codec->spec;
- unsigned int adc_idx = snd_ctl_get_ioffidx(kcontrol, &ucontrol->id);
-
- ucontrol->value.enumerated.item[0] = spec->cur_mux[adc_idx];
- return 0;
-}
-
-static int alc_mux_enum_put(struct snd_kcontrol *kcontrol,
- struct snd_ctl_elem_value *ucontrol)
-{
- struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
- struct alc_spec *spec = codec->spec;
- const struct hda_input_mux *imux;
- unsigned int adc_idx = snd_ctl_get_ioffidx(kcontrol, &ucontrol->id);
- unsigned int mux_idx;
- hda_nid_t nid = spec->capsrc_nids ?
- spec->capsrc_nids[adc_idx] : spec->adc_nids[adc_idx];
- unsigned int type;
-
- mux_idx = adc_idx >= spec->num_mux_defs ? 0 : adc_idx;
- imux = &spec->input_mux[mux_idx];
- if (!imux->num_items && mux_idx > 0)
- imux = &spec->input_mux[0];
-
- type = get_wcaps_type(get_wcaps(codec, nid));
- if (type == AC_WID_AUD_MIX) {
- /* Matrix-mixer style (e.g. ALC882) */
- unsigned int *cur_val = &spec->cur_mux[adc_idx];
- unsigned int i, idx;
-
- idx = ucontrol->value.enumerated.item[0];
- if (idx >= imux->num_items)
- idx = imux->num_items - 1;
- if (*cur_val == idx)
- return 0;
- for (i = 0; i < imux->num_items; i++) {
- unsigned int v = (i == idx) ? 0 : HDA_AMP_MUTE;
- snd_hda_codec_amp_stereo(codec, nid, HDA_INPUT,
- imux->items[i].index,
- HDA_AMP_MUTE, v);
- }
- *cur_val = idx;
- return 1;
- } else {
- /* MUX style (e.g. ALC880) */
- return snd_hda_input_mux_put(codec, imux, ucontrol, nid,
- &spec->cur_mux[adc_idx]);
- }
-}
-
-/*
- * channel mode setting
- */
-static int alc_ch_mode_info(struct snd_kcontrol *kcontrol,
- struct snd_ctl_elem_info *uinfo)
-{
- struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
- struct alc_spec *spec = codec->spec;
- return snd_hda_ch_mode_info(codec, uinfo, spec->channel_mode,
- spec->num_channel_mode);
-}
-
-static int alc_ch_mode_get(struct snd_kcontrol *kcontrol,
- struct snd_ctl_elem_value *ucontrol)
-{
- struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
- struct alc_spec *spec = codec->spec;
- return snd_hda_ch_mode_get(codec, ucontrol, spec->channel_mode,
- spec->num_channel_mode,
- spec->ext_channel_count);
-}
-
-static int alc_ch_mode_put(struct snd_kcontrol *kcontrol,
- struct snd_ctl_elem_value *ucontrol)
-{
- struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
- struct alc_spec *spec = codec->spec;
- int err = snd_hda_ch_mode_put(codec, ucontrol, spec->channel_mode,
- spec->num_channel_mode,
- &spec->ext_channel_count);
- if (err >= 0 && !spec->const_channel_count) {
- spec->multiout.max_channels = spec->ext_channel_count;
- if (spec->need_dac_fix)
- spec->multiout.num_dacs = spec->multiout.max_channels / 2;
- }
- return err;
-}
-
-/*
- * Control the mode of pin widget settings via the mixer. "pc" is used
- * instead of "%" to avoid consequences of accidentally treating the % as
- * being part of a format specifier. Maximum allowed length of a value is
- * 63 characters plus NULL terminator.
- *
- * Note: some retasking pin complexes seem to ignore requests for input
- * states other than HiZ (eg: PIN_VREFxx) and revert to HiZ if any of these
- * are requested. Therefore order this list so that this behaviour will not
- * cause problems when mixer clients move through the enum sequentially.
- * NIDs 0x0f and 0x10 have been observed to have this behaviour as of
- * March 2006.
- */
-static char *alc_pin_mode_names[] = {
- "Mic 50pc bias", "Mic 80pc bias",
- "Line in", "Line out", "Headphone out",
-};
-static unsigned char alc_pin_mode_values[] = {
- PIN_VREF50, PIN_VREF80, PIN_IN, PIN_OUT, PIN_HP,
-};
-/* The control can present all 5 options, or it can limit the options based
- * in the pin being assumed to be exclusively an input or an output pin. In
- * addition, "input" pins may or may not process the mic bias option
- * depending on actual widget capability (NIDs 0x0f and 0x10 don't seem to
- * accept requests for bias as of chip versions up to March 2006) and/or
- * wiring in the computer.
- */
-#define ALC_PIN_DIR_IN 0x00
-#define ALC_PIN_DIR_OUT 0x01
-#define ALC_PIN_DIR_INOUT 0x02
-#define ALC_PIN_DIR_IN_NOMICBIAS 0x03
-#define ALC_PIN_DIR_INOUT_NOMICBIAS 0x04
-
-/* Info about the pin modes supported by the different pin direction modes.
- * For each direction the minimum and maximum values are given.
- */
-static signed char alc_pin_mode_dir_info[5][2] = {
- { 0, 2 }, /* ALC_PIN_DIR_IN */
- { 3, 4 }, /* ALC_PIN_DIR_OUT */
- { 0, 4 }, /* ALC_PIN_DIR_INOUT */
- { 2, 2 }, /* ALC_PIN_DIR_IN_NOMICBIAS */
- { 2, 4 }, /* ALC_PIN_DIR_INOUT_NOMICBIAS */
-};
-#define alc_pin_mode_min(_dir) (alc_pin_mode_dir_info[_dir][0])
-#define alc_pin_mode_max(_dir) (alc_pin_mode_dir_info[_dir][1])
-#define alc_pin_mode_n_items(_dir) \
- (alc_pin_mode_max(_dir)-alc_pin_mode_min(_dir)+1)
-
-static int alc_pin_mode_info(struct snd_kcontrol *kcontrol,
- struct snd_ctl_elem_info *uinfo)
-{
- unsigned int item_num = uinfo->value.enumerated.item;
- unsigned char dir = (kcontrol->private_value >> 16) & 0xff;
-
- uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED;
- uinfo->count = 1;
- uinfo->value.enumerated.items = alc_pin_mode_n_items(dir);
-
- if (item_num<alc_pin_mode_min(dir) || item_num>alc_pin_mode_max(dir))
- item_num = alc_pin_mode_min(dir);
- strcpy(uinfo->value.enumerated.name, alc_pin_mode_names[item_num]);
- return 0;
-}
-
-static int alc_pin_mode_get(struct snd_kcontrol *kcontrol,
- struct snd_ctl_elem_value *ucontrol)
-{
- unsigned int i;
- struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
- hda_nid_t nid = kcontrol->private_value & 0xffff;
- unsigned char dir = (kcontrol->private_value >> 16) & 0xff;
- long *valp = ucontrol->value.integer.value;
- unsigned int pinctl = snd_hda_codec_read(codec, nid, 0,
- AC_VERB_GET_PIN_WIDGET_CONTROL,
- 0x00);
-
- /* Find enumerated value for current pinctl setting */
- i = alc_pin_mode_min(dir);
- while (i <= alc_pin_mode_max(dir) && alc_pin_mode_values[i] != pinctl)
- i++;
- *valp = i <= alc_pin_mode_max(dir) ? i: alc_pin_mode_min(dir);
- return 0;
-}
-
-static int alc_pin_mode_put(struct snd_kcontrol *kcontrol,
- struct snd_ctl_elem_value *ucontrol)
-{
- signed int change;
- struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
- hda_nid_t nid = kcontrol->private_value & 0xffff;
- unsigned char dir = (kcontrol->private_value >> 16) & 0xff;
- long val = *ucontrol->value.integer.value;
- unsigned int pinctl = snd_hda_codec_read(codec, nid, 0,
- AC_VERB_GET_PIN_WIDGET_CONTROL,
- 0x00);
-
- if (val < alc_pin_mode_min(dir) || val > alc_pin_mode_max(dir))
- val = alc_pin_mode_min(dir);
-
- change = pinctl != alc_pin_mode_values[val];
- if (change) {
- /* Set pin mode to that requested */
- snd_hda_codec_write_cache(codec, nid, 0,
- AC_VERB_SET_PIN_WIDGET_CONTROL,
- alc_pin_mode_values[val]);
-
- /* Also enable the retasking pin's input/output as required
- * for the requested pin mode. Enum values of 2 or less are
- * input modes.
- *
- * Dynamically switching the input/output buffers probably
- * reduces noise slightly (particularly on input) so we'll
- * do it. However, having both input and output buffers
- * enabled simultaneously doesn't seem to be problematic if
- * this turns out to be necessary in the future.
- */
- if (val <= 2) {
- snd_hda_codec_amp_stereo(codec, nid, HDA_OUTPUT, 0,
- HDA_AMP_MUTE, HDA_AMP_MUTE);
- snd_hda_codec_amp_stereo(codec, nid, HDA_INPUT, 0,
- HDA_AMP_MUTE, 0);
- } else {
- snd_hda_codec_amp_stereo(codec, nid, HDA_INPUT, 0,
- HDA_AMP_MUTE, HDA_AMP_MUTE);
- snd_hda_codec_amp_stereo(codec, nid, HDA_OUTPUT, 0,
- HDA_AMP_MUTE, 0);
- }
- }
- return change;
-}
-
-#define ALC_PIN_MODE(xname, nid, dir) \
- { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, .index = 0, \
- .subdevice = HDA_SUBDEV_NID_FLAG | nid, \
- .info = alc_pin_mode_info, \
- .get = alc_pin_mode_get, \
- .put = alc_pin_mode_put, \
- .private_value = nid | (dir<<16) }
-
-/* A switch control for ALC260 GPIO pins. Multiple GPIOs can be ganged
- * together using a mask with more than one bit set. This control is
- * currently used only by the ALC260 test model. At this stage they are not
- * needed for any "production" models.
- */
-#ifdef CONFIG_SND_DEBUG
-#define alc_gpio_data_info snd_ctl_boolean_mono_info
-
-static int alc_gpio_data_get(struct snd_kcontrol *kcontrol,
- struct snd_ctl_elem_value *ucontrol)
-{
- struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
- hda_nid_t nid = kcontrol->private_value & 0xffff;
- unsigned char mask = (kcontrol->private_value >> 16) & 0xff;
- long *valp = ucontrol->value.integer.value;
- unsigned int val = snd_hda_codec_read(codec, nid, 0,
- AC_VERB_GET_GPIO_DATA, 0x00);
-
- *valp = (val & mask) != 0;
- return 0;
-}
-static int alc_gpio_data_put(struct snd_kcontrol *kcontrol,
- struct snd_ctl_elem_value *ucontrol)
-{
- signed int change;
- struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
- hda_nid_t nid = kcontrol->private_value & 0xffff;
- unsigned char mask = (kcontrol->private_value >> 16) & 0xff;
- long val = *ucontrol->value.integer.value;
- unsigned int gpio_data = snd_hda_codec_read(codec, nid, 0,
- AC_VERB_GET_GPIO_DATA,
- 0x00);
-
- /* Set/unset the masked GPIO bit(s) as needed */
- change = (val == 0 ? 0 : mask) != (gpio_data & mask);
- if (val == 0)
- gpio_data &= ~mask;
- else
- gpio_data |= mask;
- snd_hda_codec_write_cache(codec, nid, 0,
- AC_VERB_SET_GPIO_DATA, gpio_data);
-
- return change;
-}
-#define ALC_GPIO_DATA_SWITCH(xname, nid, mask) \
- { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, .index = 0, \
- .subdevice = HDA_SUBDEV_NID_FLAG | nid, \
- .info = alc_gpio_data_info, \
- .get = alc_gpio_data_get, \
- .put = alc_gpio_data_put, \
- .private_value = nid | (mask<<16) }
-#endif /* CONFIG_SND_DEBUG */
-
-/* A switch control to allow the enabling of the digital IO pins on the
- * ALC260. This is incredibly simplistic; the intention of this control is
- * to provide something in the test model allowing digital outputs to be
- * identified if present. If models are found which can utilise these
- * outputs a more complete mixer control can be devised for those models if
- * necessary.
- */
-#ifdef CONFIG_SND_DEBUG
-#define alc_spdif_ctrl_info snd_ctl_boolean_mono_info
-
-static int alc_spdif_ctrl_get(struct snd_kcontrol *kcontrol,
- struct snd_ctl_elem_value *ucontrol)
-{
- struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
- hda_nid_t nid = kcontrol->private_value & 0xffff;
- unsigned char mask = (kcontrol->private_value >> 16) & 0xff;
- long *valp = ucontrol->value.integer.value;
- unsigned int val = snd_hda_codec_read(codec, nid, 0,
- AC_VERB_GET_DIGI_CONVERT_1, 0x00);
-
- *valp = (val & mask) != 0;
- return 0;
-}
-static int alc_spdif_ctrl_put(struct snd_kcontrol *kcontrol,
- struct snd_ctl_elem_value *ucontrol)
-{
- signed int change;
- struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
- hda_nid_t nid = kcontrol->private_value & 0xffff;
- unsigned char mask = (kcontrol->private_value >> 16) & 0xff;
- long val = *ucontrol->value.integer.value;
- unsigned int ctrl_data = snd_hda_codec_read(codec, nid, 0,
- AC_VERB_GET_DIGI_CONVERT_1,
- 0x00);
-
- /* Set/unset the masked control bit(s) as needed */
- change = (val == 0 ? 0 : mask) != (ctrl_data & mask);
- if (val==0)
- ctrl_data &= ~mask;
- else
- ctrl_data |= mask;
- snd_hda_codec_write_cache(codec, nid, 0, AC_VERB_SET_DIGI_CONVERT_1,
- ctrl_data);
-
- return change;
-}
-#define ALC_SPDIF_CTRL_SWITCH(xname, nid, mask) \
- { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, .index = 0, \
- .subdevice = HDA_SUBDEV_NID_FLAG | nid, \
- .info = alc_spdif_ctrl_info, \
- .get = alc_spdif_ctrl_get, \
- .put = alc_spdif_ctrl_put, \
- .private_value = nid | (mask<<16) }
-#endif /* CONFIG_SND_DEBUG */
-
-/* A switch control to allow the enabling EAPD digital outputs on the ALC26x.
- * Again, this is only used in the ALC26x test models to help identify when
- * the EAPD line must be asserted for features to work.
- */
-#ifdef CONFIG_SND_DEBUG
-#define alc_eapd_ctrl_info snd_ctl_boolean_mono_info
-
-static int alc_eapd_ctrl_get(struct snd_kcontrol *kcontrol,
- struct snd_ctl_elem_value *ucontrol)
-{
- struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
- hda_nid_t nid = kcontrol->private_value & 0xffff;
- unsigned char mask = (kcontrol->private_value >> 16) & 0xff;
- long *valp = ucontrol->value.integer.value;
- unsigned int val = snd_hda_codec_read(codec, nid, 0,
- AC_VERB_GET_EAPD_BTLENABLE, 0x00);
-
- *valp = (val & mask) != 0;
- return 0;
-}
-
-static int alc_eapd_ctrl_put(struct snd_kcontrol *kcontrol,
- struct snd_ctl_elem_value *ucontrol)
-{
- int change;
- struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
- hda_nid_t nid = kcontrol->private_value & 0xffff;
- unsigned char mask = (kcontrol->private_value >> 16) & 0xff;
- long val = *ucontrol->value.integer.value;
- unsigned int ctrl_data = snd_hda_codec_read(codec, nid, 0,
- AC_VERB_GET_EAPD_BTLENABLE,
- 0x00);
-
- /* Set/unset the masked control bit(s) as needed */
- change = (!val ? 0 : mask) != (ctrl_data & mask);
- if (!val)
- ctrl_data &= ~mask;
- else
- ctrl_data |= mask;
- snd_hda_codec_write_cache(codec, nid, 0, AC_VERB_SET_EAPD_BTLENABLE,
- ctrl_data);
-
- return change;
-}
-
-#define ALC_EAPD_CTRL_SWITCH(xname, nid, mask) \
- { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, .index = 0, \
- .subdevice = HDA_SUBDEV_NID_FLAG | nid, \
- .info = alc_eapd_ctrl_info, \
- .get = alc_eapd_ctrl_get, \
- .put = alc_eapd_ctrl_put, \
- .private_value = nid | (mask<<16) }
-#endif /* CONFIG_SND_DEBUG */
-
-/*
- * set up the input pin config (depending on the given auto-pin type)
- */
-static void alc_set_input_pin(struct hda_codec *codec, hda_nid_t nid,
- int auto_pin_type)
-{
- unsigned int val = PIN_IN;
-
- if (auto_pin_type == AUTO_PIN_MIC) {
- unsigned int pincap;
- unsigned int oldval;
- oldval = snd_hda_codec_read(codec, nid, 0,
- AC_VERB_GET_PIN_WIDGET_CONTROL, 0);
- pincap = snd_hda_query_pin_caps(codec, nid);
- pincap = (pincap & AC_PINCAP_VREF) >> AC_PINCAP_VREF_SHIFT;
- /* if the default pin setup is vref50, we give it priority */
- if ((pincap & AC_PINCAP_VREF_80) && oldval != PIN_VREF50)
- val = PIN_VREF80;
- else if (pincap & AC_PINCAP_VREF_50)
- val = PIN_VREF50;
- else if (pincap & AC_PINCAP_VREF_100)
- val = PIN_VREF100;
- else if (pincap & AC_PINCAP_VREF_GRD)
- val = PIN_VREFGRD;
- }
- snd_hda_codec_write(codec, nid, 0, AC_VERB_SET_PIN_WIDGET_CONTROL, val);
-}
-
-static void alc_fixup_autocfg_pin_nums(struct hda_codec *codec)
-{
- struct alc_spec *spec = codec->spec;
- struct auto_pin_cfg *cfg = &spec->autocfg;
-
- if (!cfg->line_outs) {
- while (cfg->line_outs < AUTO_CFG_MAX_OUTS &&
- cfg->line_out_pins[cfg->line_outs])
- cfg->line_outs++;
- }
- if (!cfg->speaker_outs) {
- while (cfg->speaker_outs < AUTO_CFG_MAX_OUTS &&
- cfg->speaker_pins[cfg->speaker_outs])
- cfg->speaker_outs++;
- }
- if (!cfg->hp_outs) {
- while (cfg->hp_outs < AUTO_CFG_MAX_OUTS &&
- cfg->hp_pins[cfg->hp_outs])
- cfg->hp_outs++;
- }
-}
-
-/*
+ * Append the given mixer and verb elements for the later use
+ * The mixer array is referred in build_controls(), and init_verbs are
+ * called in init().
*/
-static void add_mixer(struct alc_spec *spec, struct snd_kcontrol_new *mix)
+static void add_mixer(struct alc_spec *spec, const struct snd_kcontrol_new *mix)
{
if (snd_BUG_ON(spec->num_mixers >= ARRAY_SIZE(spec->mixers)))
return;
spec->mixers[spec->num_mixers++] = mix;
}
-static void add_verb(struct alc_spec *spec, const struct hda_verb *verb)
-{
- if (snd_BUG_ON(spec->num_init_verbs >= ARRAY_SIZE(spec->init_verbs)))
- return;
- spec->init_verbs[spec->num_init_verbs++] = verb;
-}
-
/*
- * set up from the preset table
+ * GPIO setup tables, used in initialization
*/
-static void setup_preset(struct hda_codec *codec,
- const struct alc_config_preset *preset)
-{
- struct alc_spec *spec = codec->spec;
- int i;
-
- for (i = 0; i < ARRAY_SIZE(preset->mixers) && preset->mixers[i]; i++)
- add_mixer(spec, preset->mixers[i]);
- spec->cap_mixer = preset->cap_mixer;
- for (i = 0; i < ARRAY_SIZE(preset->init_verbs) && preset->init_verbs[i];
- i++)
- add_verb(spec, preset->init_verbs[i]);
-
- spec->channel_mode = preset->channel_mode;
- spec->num_channel_mode = preset->num_channel_mode;
- spec->need_dac_fix = preset->need_dac_fix;
- spec->const_channel_count = preset->const_channel_count;
-
- if (preset->const_channel_count)
- spec->multiout.max_channels = preset->const_channel_count;
- else
- spec->multiout.max_channels = spec->channel_mode[0].channels;
- spec->ext_channel_count = spec->channel_mode[0].channels;
-
- spec->multiout.num_dacs = preset->num_dacs;
- spec->multiout.dac_nids = preset->dac_nids;
- spec->multiout.dig_out_nid = preset->dig_out_nid;
- spec->multiout.slave_dig_outs = preset->slave_dig_outs;
- spec->multiout.hp_nid = preset->hp_nid;
-
- spec->num_mux_defs = preset->num_mux_defs;
- if (!spec->num_mux_defs)
- spec->num_mux_defs = 1;
- spec->input_mux = preset->input_mux;
-
- spec->num_adc_nids = preset->num_adc_nids;
- spec->adc_nids = preset->adc_nids;
- spec->capsrc_nids = preset->capsrc_nids;
- spec->dig_in_nid = preset->dig_in_nid;
-
- spec->unsol_event = preset->unsol_event;
- spec->init_hook = preset->init_hook;
-#ifdef CONFIG_SND_HDA_POWER_SAVE
- spec->power_hook = preset->power_hook;
- spec->loopback.amplist = preset->loopbacks;
-#endif
-
- if (preset->setup)
- preset->setup(codec);
-
- alc_fixup_autocfg_pin_nums(codec);
-}
-
/* Enable GPIO mask and set output */
-static struct hda_verb alc_gpio1_init_verbs[] = {
+static const struct hda_verb alc_gpio1_init_verbs[] = {
{0x01, AC_VERB_SET_GPIO_MASK, 0x01},
{0x01, AC_VERB_SET_GPIO_DIRECTION, 0x01},
{0x01, AC_VERB_SET_GPIO_DATA, 0x01},
{ }
};
-static struct hda_verb alc_gpio2_init_verbs[] = {
+static const struct hda_verb alc_gpio2_init_verbs[] = {
{0x01, AC_VERB_SET_GPIO_MASK, 0x02},
{0x01, AC_VERB_SET_GPIO_DIRECTION, 0x02},
{0x01, AC_VERB_SET_GPIO_DATA, 0x02},
{ }
};
-static struct hda_verb alc_gpio3_init_verbs[] = {
+static const struct hda_verb alc_gpio3_init_verbs[] = {
{0x01, AC_VERB_SET_GPIO_MASK, 0x03},
{0x01, AC_VERB_SET_GPIO_DIRECTION, 0x03},
{0x01, AC_VERB_SET_GPIO_DATA, 0x03},
@@ -1024,178 +196,33 @@ static void alc_fix_pll_init(struct hda_codec *codec, hda_nid_t nid,
alc_fix_pll(codec);
}
-static int alc_init_jacks(struct hda_codec *codec)
-{
-#ifdef CONFIG_SND_HDA_INPUT_JACK
- struct alc_spec *spec = codec->spec;
- int err;
- unsigned int hp_nid = spec->autocfg.hp_pins[0];
- unsigned int mic_nid = spec->ext_mic.pin;
-
- if (hp_nid) {
- err = snd_hda_input_jack_add(codec, hp_nid,
- SND_JACK_HEADPHONE, NULL);
- if (err < 0)
- return err;
- snd_hda_input_jack_report(codec, hp_nid);
- }
-
- if (mic_nid) {
- err = snd_hda_input_jack_add(codec, mic_nid,
- SND_JACK_MICROPHONE, NULL);
- if (err < 0)
- return err;
- snd_hda_input_jack_report(codec, mic_nid);
- }
-#endif /* CONFIG_SND_HDA_INPUT_JACK */
- return 0;
-}
-
-static void alc_automute_speaker(struct hda_codec *codec, int pinctl)
-{
- struct alc_spec *spec = codec->spec;
- unsigned int mute;
- hda_nid_t nid;
- int i;
-
- spec->jack_present = 0;
- for (i = 0; i < ARRAY_SIZE(spec->autocfg.hp_pins); i++) {
- nid = spec->autocfg.hp_pins[i];
- if (!nid)
- break;
- snd_hda_input_jack_report(codec, nid);
- spec->jack_present |= snd_hda_jack_detect(codec, nid);
- }
-
- mute = spec->jack_present ? HDA_AMP_MUTE : 0;
- /* Toggle internal speakers muting */
- for (i = 0; i < ARRAY_SIZE(spec->autocfg.speaker_pins); i++) {
- nid = spec->autocfg.speaker_pins[i];
- if (!nid)
- break;
- if (pinctl) {
- snd_hda_codec_write(codec, nid, 0,
- AC_VERB_SET_PIN_WIDGET_CONTROL,
- spec->jack_present ? 0 : PIN_OUT);
- } else {
- snd_hda_codec_amp_stereo(codec, nid, HDA_OUTPUT, 0,
- HDA_AMP_MUTE, mute);
- }
- }
-}
-
-static void alc_automute_pin(struct hda_codec *codec)
-{
- alc_automute_speaker(codec, 1);
-}
-
-static int get_connection_index(struct hda_codec *codec, hda_nid_t mux,
- hda_nid_t nid)
-{
- hda_nid_t conn[HDA_MAX_NUM_INPUTS];
- int i, nums;
-
- nums = snd_hda_get_connections(codec, mux, conn, ARRAY_SIZE(conn));
- for (i = 0; i < nums; i++)
- if (conn[i] == nid)
- return i;
- return -1;
-}
-
-/* switch the current ADC according to the jack state */
-static void alc_dual_mic_adc_auto_switch(struct hda_codec *codec)
+/* update the master volume per volume-knob's unsol event */
+static void alc_update_knob_master(struct hda_codec *codec, struct hda_jack_tbl *jack)
{
- struct alc_spec *spec = codec->spec;
- unsigned int present;
- hda_nid_t new_adc;
-
- present = snd_hda_jack_detect(codec, spec->ext_mic.pin);
- if (present)
- spec->cur_adc_idx = 1;
- else
- spec->cur_adc_idx = 0;
- new_adc = spec->adc_nids[spec->cur_adc_idx];
- if (spec->cur_adc && spec->cur_adc != new_adc) {
- /* stream is running, let's swap the current ADC */
- __snd_hda_codec_cleanup_stream(codec, spec->cur_adc, 1);
- spec->cur_adc = new_adc;
- snd_hda_codec_setup_stream(codec, new_adc,
- spec->cur_adc_stream_tag, 0,
- spec->cur_adc_format);
- }
-}
-
-static void alc_mic_automute(struct hda_codec *codec)
-{
- struct alc_spec *spec = codec->spec;
- struct alc_mic_route *dead, *alive;
- unsigned int present, type;
- hda_nid_t cap_nid;
+ unsigned int val;
+ struct snd_kcontrol *kctl;
+ struct snd_ctl_elem_value *uctl;
- if (!spec->auto_mic)
- return;
- if (!spec->int_mic.pin || !spec->ext_mic.pin)
+ kctl = snd_hda_find_mixer_ctl(codec, "Master Playback Volume");
+ if (!kctl)
return;
- if (snd_BUG_ON(!spec->adc_nids))
+ uctl = kzalloc(sizeof(*uctl), GFP_KERNEL);
+ if (!uctl)
return;
-
- if (spec->dual_adc_switch) {
- alc_dual_mic_adc_auto_switch(codec);
- return;
- }
-
- cap_nid = spec->capsrc_nids ? spec->capsrc_nids[0] : spec->adc_nids[0];
-
- present = snd_hda_jack_detect(codec, spec->ext_mic.pin);
- if (present) {
- alive = &spec->ext_mic;
- dead = &spec->int_mic;
- } else {
- alive = &spec->int_mic;
- dead = &spec->ext_mic;
- }
-
- type = get_wcaps_type(get_wcaps(codec, cap_nid));
- if (type == AC_WID_AUD_MIX) {
- /* Matrix-mixer style (e.g. ALC882) */
- snd_hda_codec_amp_stereo(codec, cap_nid, HDA_INPUT,
- alive->mux_idx,
- HDA_AMP_MUTE, 0);
- snd_hda_codec_amp_stereo(codec, cap_nid, HDA_INPUT,
- dead->mux_idx,
- HDA_AMP_MUTE, HDA_AMP_MUTE);
- } else {
- /* MUX style (e.g. ALC880) */
- snd_hda_codec_write_cache(codec, cap_nid, 0,
- AC_VERB_SET_CONNECT_SEL,
- alive->mux_idx);
- }
- snd_hda_input_jack_report(codec, spec->ext_mic.pin);
-
- /* FIXME: analog mixer */
-}
-
-/* unsolicited event for HP jack sensing */
-static void alc_sku_unsol_event(struct hda_codec *codec, unsigned int res)
-{
- if (codec->vendor_id == 0x10ec0880)
- res >>= 28;
- else
- res >>= 26;
- switch (res) {
- case ALC880_HP_EVENT:
- alc_automute_pin(codec);
- break;
- case ALC880_MIC_EVENT:
- alc_mic_automute(codec);
- break;
- }
+ val = snd_hda_codec_read(codec, jack->nid, 0,
+ AC_VERB_GET_VOLUME_KNOB_CONTROL, 0);
+ val &= HDA_AMP_VOLMASK;
+ uctl->value.integer.value[0] = val;
+ uctl->value.integer.value[1] = val;
+ kctl->put(kctl, uctl);
+ kfree(uctl);
}
-static void alc_inithook(struct hda_codec *codec)
+static void alc880_unsol_event(struct hda_codec *codec, unsigned int res)
{
- alc_automute_pin(codec);
- alc_mic_automute(codec);
+ /* For some reason, the res given from ALC880 is broken.
+ Here we adjust it properly. */
+ snd_hda_jack_unsol_event(codec, res >> 2);
}
/* additional initialization for ALC888 variants */
@@ -1216,6 +243,7 @@ static void alc888_coef_init(struct hda_codec *codec)
AC_VERB_SET_PROC_COEF, 0x3030);
}
+/* additional initialization for ALC889 variants */
static void alc889_coef_init(struct hda_codec *codec)
{
unsigned int tmp;
@@ -1236,10 +264,37 @@ static void set_eapd(struct hda_codec *codec, hda_nid_t nid, int on)
on ? 2 : 0);
}
+/* turn on/off EAPD controls of the codec */
+static void alc_auto_setup_eapd(struct hda_codec *codec, bool on)
+{
+ /* We currently only handle front, HP */
+ static hda_nid_t pins[] = {
+ 0x0f, 0x10, 0x14, 0x15, 0
+ };
+ hda_nid_t *p;
+ for (p = pins; *p; p++)
+ set_eapd(codec, *p, on);
+}
+
+/* generic shutup callback;
+ * just turning off EPAD and a little pause for avoiding pop-noise
+ */
+static void alc_eapd_shutup(struct hda_codec *codec)
+{
+ struct alc_spec *spec = codec->spec;
+
+ alc_auto_setup_eapd(codec, false);
+ if (!spec->no_depop_delay)
+ msleep(200);
+ snd_hda_shutup_pins(codec);
+}
+
+/* generic EAPD initialization */
static void alc_auto_init_amp(struct hda_codec *codec, int type)
{
unsigned int tmp;
+ alc_auto_setup_eapd(codec, true);
switch (type) {
case ALC_INIT_GPIO1:
snd_hda_sequence_write(codec, alc_gpio1_init_verbs);
@@ -1253,27 +308,6 @@ static void alc_auto_init_amp(struct hda_codec *codec, int type)
case ALC_INIT_DEFAULT:
switch (codec->vendor_id) {
case 0x10ec0260:
- set_eapd(codec, 0x0f, 1);
- set_eapd(codec, 0x10, 1);
- break;
- case 0x10ec0262:
- case 0x10ec0267:
- case 0x10ec0268:
- case 0x10ec0269:
- case 0x10ec0270:
- case 0x10ec0272:
- case 0x10ec0660:
- case 0x10ec0662:
- case 0x10ec0663:
- case 0x10ec0665:
- case 0x10ec0862:
- case 0x10ec0889:
- set_eapd(codec, 0x14, 1);
- set_eapd(codec, 0x15, 1);
- break;
- }
- switch (codec->vendor_id) {
- case 0x10ec0260:
snd_hda_codec_write(codec, 0x1a, 0,
AC_VERB_SET_COEF_INDEX, 7);
tmp = snd_hda_codec_read(codec, 0x1a, 0,
@@ -1315,95 +349,37 @@ static void alc_auto_init_amp(struct hda_codec *codec, int type)
}
}
-static void alc_init_auto_hp(struct hda_codec *codec)
-{
- struct alc_spec *spec = codec->spec;
- struct auto_pin_cfg *cfg = &spec->autocfg;
- int i;
- if (!cfg->hp_pins[0]) {
- if (cfg->line_out_type != AUTO_PIN_HP_OUT)
- return;
- }
-
- if (!cfg->speaker_pins[0]) {
- if (cfg->line_out_type != AUTO_PIN_SPEAKER_OUT)
- return;
- memcpy(cfg->speaker_pins, cfg->line_out_pins,
- sizeof(cfg->speaker_pins));
- cfg->speaker_outs = cfg->line_outs;
- }
+/*
+ * Realtek SSID verification
+ */
- if (!cfg->hp_pins[0]) {
- memcpy(cfg->hp_pins, cfg->line_out_pins,
- sizeof(cfg->hp_pins));
- cfg->hp_outs = cfg->line_outs;
- }
+/* Could be any non-zero and even value. When used as fixup, tells
+ * the driver to ignore any present sku defines.
+ */
+#define ALC_FIXUP_SKU_IGNORE (2)
- for (i = 0; i < cfg->hp_outs; i++) {
- snd_printdd("realtek: Enable HP auto-muting on NID 0x%x\n",
- cfg->hp_pins[i]);
- snd_hda_codec_write_cache(codec, cfg->hp_pins[i], 0,
- AC_VERB_SET_UNSOLICITED_ENABLE,
- AC_USRSP_EN | ALC880_HP_EVENT);
+static void alc_fixup_sku_ignore(struct hda_codec *codec,
+ const struct hda_fixup *fix, int action)
+{
+ struct alc_spec *spec = codec->spec;
+ if (action == HDA_FIXUP_ACT_PRE_PROBE) {
+ spec->cdefine.fixup = 1;
+ spec->cdefine.sku_cfg = ALC_FIXUP_SKU_IGNORE;
}
- spec->unsol_event = alc_sku_unsol_event;
}
-static void alc_init_auto_mic(struct hda_codec *codec)
+static void alc_fixup_no_depop_delay(struct hda_codec *codec,
+ const struct hda_fixup *fix, int action)
{
struct alc_spec *spec = codec->spec;
- struct auto_pin_cfg *cfg = &spec->autocfg;
- hda_nid_t fixed, ext;
- int i;
-
- /* there must be only two mic inputs exclusively */
- for (i = 0; i < cfg->num_inputs; i++)
- if (cfg->inputs[i].type >= AUTO_PIN_LINE_IN)
- return;
- fixed = ext = 0;
- for (i = 0; i < cfg->num_inputs; i++) {
- hda_nid_t nid = cfg->inputs[i].pin;
- unsigned int defcfg;
- defcfg = snd_hda_codec_get_pincfg(codec, nid);
- switch (snd_hda_get_input_pin_attr(defcfg)) {
- case INPUT_PIN_ATTR_INT:
- if (fixed)
- return; /* already occupied */
- fixed = nid;
- break;
- case INPUT_PIN_ATTR_UNUSED:
- return; /* invalid entry */
- default:
- if (ext)
- return; /* already occupied */
- ext = nid;
- break;
- }
+ if (action == HDA_FIXUP_ACT_PROBE) {
+ spec->no_depop_delay = 1;
+ codec->depop_delay = 0;
}
- if (!ext || !fixed)
- return;
- if (!(get_wcaps(codec, ext) & AC_WCAP_UNSOL_CAP))
- return; /* no unsol support */
- snd_printdd("realtek: Enable auto-mic switch on NID 0x%x/0x%x\n",
- ext, fixed);
- spec->ext_mic.pin = ext;
- spec->int_mic.pin = fixed;
- spec->ext_mic.mux_idx = MUX_IDX_UNDEF; /* set later */
- spec->int_mic.mux_idx = MUX_IDX_UNDEF; /* set later */
- spec->auto_mic = 1;
- snd_hda_codec_write_cache(codec, spec->ext_mic.pin, 0,
- AC_VERB_SET_UNSOLICITED_ENABLE,
- AC_USRSP_EN | ALC880_MIC_EVENT);
- spec->unsol_event = alc_sku_unsol_event;
}
-/* Could be any non-zero and even value. When used as fixup, tells
- * the driver to ignore any present sku defines.
- */
-#define ALC_FIXUP_SKU_IGNORE (2)
-
static int alc_auto_parse_customize_define(struct hda_codec *codec)
{
unsigned int ass, tmp, i;
@@ -1419,6 +395,8 @@ static int alc_auto_parse_customize_define(struct hda_codec *codec)
goto do_sku;
}
+ if (!codec->bus->pci)
+ return -1;
ass = codec->subsystem_id & 0xffff;
if (ass != codec->bus->pci->subsystem_device && (ass & 1))
goto do_sku;
@@ -1429,8 +407,8 @@ static int alc_auto_parse_customize_define(struct hda_codec *codec)
ass = snd_hda_codec_get_pincfg(codec, nid);
if (!(ass & 1)) {
- printk(KERN_INFO "hda_codec: %s: SKU not ready 0x%08x\n",
- codec->chip_name, ass);
+ codec_info(codec, "%s: SKU not ready 0x%08x\n",
+ codec->chip_name, ass);
return -1;
}
@@ -1454,21 +432,36 @@ do_sku:
spec->cdefine.swap = (ass & 0x2) >> 1;
spec->cdefine.override = ass & 0x1;
- snd_printd("SKU: Nid=0x%x sku_cfg=0x%08x\n",
+ codec_dbg(codec, "SKU: Nid=0x%x sku_cfg=0x%08x\n",
nid, spec->cdefine.sku_cfg);
- snd_printd("SKU: port_connectivity=0x%x\n",
+ codec_dbg(codec, "SKU: port_connectivity=0x%x\n",
spec->cdefine.port_connectivity);
- snd_printd("SKU: enable_pcbeep=0x%x\n", spec->cdefine.enable_pcbeep);
- snd_printd("SKU: check_sum=0x%08x\n", spec->cdefine.check_sum);
- snd_printd("SKU: customization=0x%08x\n", spec->cdefine.customization);
- snd_printd("SKU: external_amp=0x%x\n", spec->cdefine.external_amp);
- snd_printd("SKU: platform_type=0x%x\n", spec->cdefine.platform_type);
- snd_printd("SKU: swap=0x%x\n", spec->cdefine.swap);
- snd_printd("SKU: override=0x%x\n", spec->cdefine.override);
+ codec_dbg(codec, "SKU: enable_pcbeep=0x%x\n", spec->cdefine.enable_pcbeep);
+ codec_dbg(codec, "SKU: check_sum=0x%08x\n", spec->cdefine.check_sum);
+ codec_dbg(codec, "SKU: customization=0x%08x\n", spec->cdefine.customization);
+ codec_dbg(codec, "SKU: external_amp=0x%x\n", spec->cdefine.external_amp);
+ codec_dbg(codec, "SKU: platform_type=0x%x\n", spec->cdefine.platform_type);
+ codec_dbg(codec, "SKU: swap=0x%x\n", spec->cdefine.swap);
+ codec_dbg(codec, "SKU: override=0x%x\n", spec->cdefine.override);
return 0;
}
+/* return the position of NID in the list, or -1 if not found */
+static int find_idx_in_nid_list(hda_nid_t nid, const hda_nid_t *list, int nums)
+{
+ int i;
+ for (i = 0; i < nums; i++)
+ if (list[i] == nid)
+ return i;
+ return -1;
+}
+/* return true if the given NID is found in the list */
+static bool found_in_nid_list(hda_nid_t nid, const hda_nid_t *list, int nums)
+{
+ return find_idx_in_nid_list(nid, list, nums) >= 0;
+}
+
/* check subsystem ID and set up device-specific initialization;
* return 1 if initialized, 0 if invalid SSID
*/
@@ -1478,9 +471,7 @@ do_sku:
* 7 ~ 0 : Assembly ID
* port-A --> pin 39/41, port-E --> pin 14/15, port-D --> pin 35/36
*/
-static int alc_subsystem_id(struct hda_codec *codec,
- hda_nid_t porta, hda_nid_t porte,
- hda_nid_t portd, hda_nid_t porti)
+static int alc_subsystem_id(struct hda_codec *codec, const hda_nid_t *ports)
{
unsigned int ass, tmp, i;
unsigned nid;
@@ -1494,7 +485,8 @@ static int alc_subsystem_id(struct hda_codec *codec,
}
ass = codec->subsystem_id & 0xffff;
- if ((ass != codec->bus->pci->subsystem_device) && (ass & 1))
+ if (codec->bus->pci &&
+ ass != codec->bus->pci->subsystem_device && (ass & 1))
goto do_sku;
/* invalid SSID, check the special NID pin defcfg instead */
@@ -1510,8 +502,8 @@ static int alc_subsystem_id(struct hda_codec *codec,
if (codec->vendor_id == 0x10ec0260)
nid = 0x17;
ass = snd_hda_codec_get_pincfg(codec, nid);
- snd_printd("realtek: No valid SSID, "
- "checking pincfg 0x%08x for NID 0x%x\n",
+ codec_dbg(codec,
+ "realtek: No valid SSID, checking pincfg 0x%08x for NID 0x%x\n",
ass, nid);
if (!(ass & 1))
return 0;
@@ -1527,7 +519,7 @@ static int alc_subsystem_id(struct hda_codec *codec,
if (((ass >> 16) & 0xf) != tmp)
return 0;
do_sku:
- snd_printd("realtek: Enabling init ASM_ID=0x%04x CODEC_ID=%08x\n",
+ codec_dbg(codec, "realtek: Enabling init ASM_ID=0x%04x CODEC_ID=%08x\n",
ass & 0xffff, codec->vendor_id);
/*
* 0 : override
@@ -1565,1356 +557,223 @@ do_sku:
* 15 : 1 --> enable the function "Mute internal speaker
* when the external headphone out jack is plugged"
*/
- if (!spec->autocfg.hp_pins[0]) {
+ if (!spec->gen.autocfg.hp_pins[0] &&
+ !(spec->gen.autocfg.line_out_pins[0] &&
+ spec->gen.autocfg.line_out_type == AUTO_PIN_HP_OUT)) {
hda_nid_t nid;
tmp = (ass >> 11) & 0x3; /* HP to chassis */
- if (tmp == 0)
- nid = porta;
- else if (tmp == 1)
- nid = porte;
- else if (tmp == 2)
- nid = portd;
- else if (tmp == 3)
- nid = porti;
- else
+ nid = ports[tmp];
+ if (found_in_nid_list(nid, spec->gen.autocfg.line_out_pins,
+ spec->gen.autocfg.line_outs))
return 1;
- for (i = 0; i < spec->autocfg.line_outs; i++)
- if (spec->autocfg.line_out_pins[i] == nid)
- return 1;
- spec->autocfg.hp_pins[0] = nid;
+ spec->gen.autocfg.hp_pins[0] = nid;
}
-
- alc_init_auto_hp(codec);
- alc_init_auto_mic(codec);
return 1;
}
-static void alc_ssid_check(struct hda_codec *codec,
- hda_nid_t porta, hda_nid_t porte,
- hda_nid_t portd, hda_nid_t porti)
+/* Check the validity of ALC subsystem-id
+ * ports contains an array of 4 pin NIDs for port-A, E, D and I */
+static void alc_ssid_check(struct hda_codec *codec, const hda_nid_t *ports)
{
- if (!alc_subsystem_id(codec, porta, porte, portd, porti)) {
+ if (!alc_subsystem_id(codec, ports)) {
struct alc_spec *spec = codec->spec;
- snd_printd("realtek: "
- "Enable default setup for auto mode as fallback\n");
+ codec_dbg(codec,
+ "realtek: Enable default setup for auto mode as fallback\n");
spec->init_amp = ALC_INIT_DEFAULT;
- alc_init_auto_hp(codec);
- alc_init_auto_mic(codec);
}
}
/*
- * Fix-up pin default configurations and add default verbs
+ * COEF access helper functions
*/
-struct alc_pincfg {
- hda_nid_t nid;
- u32 val;
-};
-
-struct alc_model_fixup {
- const int id;
- const char *name;
-};
-
-struct alc_fixup {
- int type;
- bool chained;
- int chain_id;
- union {
- unsigned int sku;
- const struct alc_pincfg *pins;
- const struct hda_verb *verbs;
- void (*func)(struct hda_codec *codec,
- const struct alc_fixup *fix,
- int action);
- } v;
-};
-
-enum {
- ALC_FIXUP_INVALID,
- ALC_FIXUP_SKU,
- ALC_FIXUP_PINS,
- ALC_FIXUP_VERBS,
- ALC_FIXUP_FUNC,
-};
-
-enum {
- ALC_FIXUP_ACT_PRE_PROBE,
- ALC_FIXUP_ACT_PROBE,
- ALC_FIXUP_ACT_INIT,
-};
-
-static void alc_apply_fixup(struct hda_codec *codec, int action)
-{
- struct alc_spec *spec = codec->spec;
- int id = spec->fixup_id;
-#ifdef CONFIG_SND_DEBUG_VERBOSE
- const char *modelname = spec->fixup_name;
-#endif
- int depth = 0;
-
- if (!spec->fixup_list)
- return;
-
- while (id >= 0) {
- const struct alc_fixup *fix = spec->fixup_list + id;
- const struct alc_pincfg *cfg;
-
- switch (fix->type) {
- case ALC_FIXUP_SKU:
- if (action != ALC_FIXUP_ACT_PRE_PROBE || !fix->v.sku)
- break;;
- snd_printdd(KERN_INFO "hda_codec: %s: "
- "Apply sku override for %s\n",
- codec->chip_name, modelname);
- spec->cdefine.sku_cfg = fix->v.sku;
- spec->cdefine.fixup = 1;
- break;
- case ALC_FIXUP_PINS:
- cfg = fix->v.pins;
- if (action != ALC_FIXUP_ACT_PRE_PROBE || !cfg)
- break;
- snd_printdd(KERN_INFO "hda_codec: %s: "
- "Apply pincfg for %s\n",
- codec->chip_name, modelname);
- for (; cfg->nid; cfg++)
- snd_hda_codec_set_pincfg(codec, cfg->nid,
- cfg->val);
- break;
- case ALC_FIXUP_VERBS:
- if (action != ALC_FIXUP_ACT_PROBE || !fix->v.verbs)
- break;
- snd_printdd(KERN_INFO "hda_codec: %s: "
- "Apply fix-verbs for %s\n",
- codec->chip_name, modelname);
- add_verb(codec->spec, fix->v.verbs);
- break;
- case ALC_FIXUP_FUNC:
- if (!fix->v.func)
- break;
- snd_printdd(KERN_INFO "hda_codec: %s: "
- "Apply fix-func for %s\n",
- codec->chip_name, modelname);
- fix->v.func(codec, fix, action);
- break;
- default:
- snd_printk(KERN_ERR "hda_codec: %s: "
- "Invalid fixup type %d\n",
- codec->chip_name, fix->type);
- break;
- }
- if (!fix[id].chained)
- break;
- if (++depth > 10)
- break;
- id = fix[id].chain_id;
- }
-}
-
-static void alc_pick_fixup(struct hda_codec *codec,
- const struct alc_model_fixup *models,
- const struct snd_pci_quirk *quirk,
- const struct alc_fixup *fixlist)
-{
- struct alc_spec *spec = codec->spec;
- int id = -1;
- const char *name = NULL;
-
- if (codec->modelname && models) {
- while (models->name) {
- if (!strcmp(codec->modelname, models->name)) {
- id = models->id;
- name = models->name;
- break;
- }
- models++;
- }
- }
- if (id < 0) {
- quirk = snd_pci_quirk_lookup(codec->bus->pci, quirk);
- if (quirk) {
- id = quirk->value;
-#ifdef CONFIG_SND_DEBUG_VERBOSE
- name = quirk->name;
-#endif
- }
- }
-
- spec->fixup_id = id;
- if (id >= 0) {
- spec->fixup_list = fixlist;
- spec->fixup_name = name;
- }
-}
-
-static int alc_read_coef_idx(struct hda_codec *codec,
- unsigned int coef_idx)
+static int alc_read_coefex_idx(struct hda_codec *codec,
+ hda_nid_t nid,
+ unsigned int coef_idx)
{
unsigned int val;
- snd_hda_codec_write(codec, 0x20, 0, AC_VERB_SET_COEF_INDEX,
+ snd_hda_codec_write(codec, nid, 0, AC_VERB_SET_COEF_INDEX,
coef_idx);
- val = snd_hda_codec_read(codec, 0x20, 0,
+ val = snd_hda_codec_read(codec, nid, 0,
AC_VERB_GET_PROC_COEF, 0);
return val;
}
-static void alc_write_coef_idx(struct hda_codec *codec, unsigned int coef_idx,
+#define alc_read_coef_idx(codec, coef_idx) \
+ alc_read_coefex_idx(codec, 0x20, coef_idx)
+
+static void alc_write_coefex_idx(struct hda_codec *codec, hda_nid_t nid,
+ unsigned int coef_idx,
unsigned int coef_val)
{
- snd_hda_codec_write(codec, 0x20, 0, AC_VERB_SET_COEF_INDEX,
+ snd_hda_codec_write(codec, nid, 0, AC_VERB_SET_COEF_INDEX,
coef_idx);
- snd_hda_codec_write(codec, 0x20, 0, AC_VERB_SET_PROC_COEF,
+ snd_hda_codec_write(codec, nid, 0, AC_VERB_SET_PROC_COEF,
coef_val);
}
-/* set right pin controls for digital I/O */
-static void alc_auto_init_digital(struct hda_codec *codec)
-{
- struct alc_spec *spec = codec->spec;
- int i;
- hda_nid_t pin;
-
- for (i = 0; i < spec->autocfg.dig_outs; i++) {
- pin = spec->autocfg.dig_out_pins[i];
- if (pin) {
- snd_hda_codec_write(codec, pin, 0,
- AC_VERB_SET_PIN_WIDGET_CONTROL,
- PIN_OUT);
- }
- }
- pin = spec->autocfg.dig_in_pin;
- if (pin)
- snd_hda_codec_write(codec, pin, 0,
- AC_VERB_SET_PIN_WIDGET_CONTROL,
- PIN_IN);
-}
+#define alc_write_coef_idx(codec, coef_idx, coef_val) \
+ alc_write_coefex_idx(codec, 0x20, coef_idx, coef_val)
-/* parse digital I/Os and set up NIDs in BIOS auto-parse mode */
-static void alc_auto_parse_digital(struct hda_codec *codec)
+/* a special bypass for COEF 0; read the cached value at the second time */
+static unsigned int alc_get_coef0(struct hda_codec *codec)
{
struct alc_spec *spec = codec->spec;
- int i, err;
- hda_nid_t dig_nid;
-
- /* support multiple SPDIFs; the secondary is set up as a slave */
- for (i = 0; i < spec->autocfg.dig_outs; i++) {
- err = snd_hda_get_connections(codec,
- spec->autocfg.dig_out_pins[i],
- &dig_nid, 1);
- if (err < 0)
- continue;
- if (!i) {
- spec->multiout.dig_out_nid = dig_nid;
- spec->dig_out_type = spec->autocfg.dig_out_type[0];
- } else {
- spec->multiout.slave_dig_outs = spec->slave_dig_outs;
- if (i >= ARRAY_SIZE(spec->slave_dig_outs) - 1)
- break;
- spec->slave_dig_outs[i - 1] = dig_nid;
- }
- }
-
- if (spec->autocfg.dig_in_pin) {
- dig_nid = codec->start_nid;
- for (i = 0; i < codec->num_nodes; i++, dig_nid++) {
- unsigned int wcaps = get_wcaps(codec, dig_nid);
- if (get_wcaps_type(wcaps) != AC_WID_AUD_IN)
- continue;
- if (!(wcaps & AC_WCAP_DIGITAL))
- continue;
- if (!(wcaps & AC_WCAP_CONN_LIST))
- continue;
- err = get_connection_index(codec, dig_nid,
- spec->autocfg.dig_in_pin);
- if (err >= 0) {
- spec->dig_in_nid = dig_nid;
- break;
- }
- }
- }
+ if (!spec->coef0)
+ spec->coef0 = alc_read_coef_idx(codec, 0);
+ return spec->coef0;
}
/*
- * ALC888
- */
-
-/*
- * 2ch mode
- */
-static struct hda_verb alc888_4ST_ch2_intel_init[] = {
-/* Mic-in jack as mic in */
- { 0x18, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80 },
- { 0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE },
-/* Line-in jack as Line in */
- { 0x1a, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN },
- { 0x1a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE },
-/* Line-Out as Front */
- { 0x17, AC_VERB_SET_CONNECT_SEL, 0x00},
- { } /* end */
-};
-
-/*
- * 4ch mode
- */
-static struct hda_verb alc888_4ST_ch4_intel_init[] = {
-/* Mic-in jack as mic in */
- { 0x18, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80 },
- { 0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE },
-/* Line-in jack as Surround */
- { 0x1a, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT },
- { 0x1a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE },
-/* Line-Out as Front */
- { 0x17, AC_VERB_SET_CONNECT_SEL, 0x00},
- { } /* end */
-};
-
-/*
- * 6ch mode
- */
-static struct hda_verb alc888_4ST_ch6_intel_init[] = {
-/* Mic-in jack as CLFE */
- { 0x18, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT },
- { 0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE },
-/* Line-in jack as Surround */
- { 0x1a, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT },
- { 0x1a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE },
-/* Line-Out as CLFE (workaround because Mic-in is not loud enough) */
- { 0x17, AC_VERB_SET_CONNECT_SEL, 0x03},
- { } /* end */
-};
-
-/*
- * 8ch mode
- */
-static struct hda_verb alc888_4ST_ch8_intel_init[] = {
-/* Mic-in jack as CLFE */
- { 0x18, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT },
- { 0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE },
-/* Line-in jack as Surround */
- { 0x1a, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT },
- { 0x1a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE },
-/* Line-Out as Side */
- { 0x17, AC_VERB_SET_CONNECT_SEL, 0x03},
- { } /* end */
-};
-
-static struct hda_channel_mode alc888_4ST_8ch_intel_modes[4] = {
- { 2, alc888_4ST_ch2_intel_init },
- { 4, alc888_4ST_ch4_intel_init },
- { 6, alc888_4ST_ch6_intel_init },
- { 8, alc888_4ST_ch8_intel_init },
-};
-
-/*
- * ALC888 Fujitsu Siemens Amillo xa3530
*/
-static struct hda_verb alc888_fujitsu_xa3530_verbs[] = {
-/* Front Mic: set to PIN_IN (empty by default) */
- {0x12, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN},
-/* Connect Internal HP to Front */
- {0x14, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
- {0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
- {0x14, AC_VERB_SET_CONNECT_SEL, 0x00},
-/* Connect Bass HP to Front */
- {0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
- {0x15, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
- {0x15, AC_VERB_SET_CONNECT_SEL, 0x00},
-/* Connect Line-Out side jack (SPDIF) to Side */
- {0x17, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
- {0x17, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
- {0x17, AC_VERB_SET_CONNECT_SEL, 0x03},
-/* Connect Mic jack to CLFE */
- {0x18, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
- {0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
- {0x18, AC_VERB_SET_CONNECT_SEL, 0x02},
-/* Connect Line-in jack to Surround */
- {0x1a, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
- {0x1a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
- {0x1a, AC_VERB_SET_CONNECT_SEL, 0x01},
-/* Connect HP out jack to Front */
- {0x1b, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
- {0x1b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
- {0x1b, AC_VERB_SET_CONNECT_SEL, 0x00},
-/* Enable unsolicited event for HP jack and Line-out jack */
- {0x1b, AC_VERB_SET_UNSOLICITED_ENABLE, ALC880_HP_EVENT | AC_USRSP_EN},
- {0x17, AC_VERB_SET_UNSOLICITED_ENABLE, ALC880_HP_EVENT | AC_USRSP_EN},
- {}
-};
-
-static void alc_automute_amp(struct hda_codec *codec)
+static hda_nid_t get_adc_nid(struct hda_codec *codec, int adc_idx, int imux_idx)
{
- alc_automute_speaker(codec, 0);
+ struct hda_gen_spec *spec = codec->spec;
+ if (spec->dyn_adc_switch)
+ adc_idx = spec->dyn_adc_idx[imux_idx];
+ return spec->adc_nids[adc_idx];
}
-static void alc_automute_amp_unsol_event(struct hda_codec *codec,
- unsigned int res)
-{
- if (codec->vendor_id == 0x10ec0880)
- res >>= 28;
- else
- res >>= 26;
- if (res == ALC880_HP_EVENT)
- alc_automute_amp(codec);
-}
-
-static void alc889_automute_setup(struct hda_codec *codec)
+static void alc_inv_dmic_sync_adc(struct hda_codec *codec, int adc_idx)
{
struct alc_spec *spec = codec->spec;
+ struct hda_input_mux *imux = &spec->gen.input_mux;
+ struct nid_path *path;
+ hda_nid_t nid;
+ int i, dir, parm;
+ unsigned int val;
- spec->autocfg.hp_pins[0] = 0x15;
- spec->autocfg.speaker_pins[0] = 0x14;
- spec->autocfg.speaker_pins[1] = 0x16;
- spec->autocfg.speaker_pins[2] = 0x17;
- spec->autocfg.speaker_pins[3] = 0x19;
- spec->autocfg.speaker_pins[4] = 0x1a;
-}
+ for (i = 0; i < imux->num_items; i++) {
+ if (spec->gen.imux_pins[i] == spec->inv_dmic_pin)
+ break;
+ }
+ if (i >= imux->num_items)
+ return;
-static void alc889_intel_init_hook(struct hda_codec *codec)
-{
- alc889_coef_init(codec);
- alc_automute_amp(codec);
-}
+ path = snd_hda_get_nid_path(codec, spec->inv_dmic_pin,
+ get_adc_nid(codec, adc_idx, i));
+ val = path->ctls[NID_PATH_MUTE_CTL];
+ if (!val)
+ return;
+ nid = get_amp_nid_(val);
+ dir = get_amp_direction_(val);
+ parm = AC_AMP_SET_RIGHT |
+ (dir == HDA_OUTPUT ? AC_AMP_SET_OUTPUT : AC_AMP_SET_INPUT);
-static void alc888_fujitsu_xa3530_setup(struct hda_codec *codec)
-{
- struct alc_spec *spec = codec->spec;
+ /* flush all cached amps at first */
+ snd_hda_codec_flush_cache(codec);
- spec->autocfg.hp_pins[0] = 0x17; /* line-out */
- spec->autocfg.hp_pins[1] = 0x1b; /* hp */
- spec->autocfg.speaker_pins[0] = 0x14; /* speaker */
- spec->autocfg.speaker_pins[1] = 0x15; /* bass */
+ /* we care only right channel */
+ val = snd_hda_codec_amp_read(codec, nid, 1, dir, 0);
+ if (val & 0x80) /* if already muted, we don't need to touch */
+ return;
+ val |= 0x80;
+ snd_hda_codec_write(codec, nid, 0, AC_VERB_SET_AMP_GAIN_MUTE,
+ parm | val);
}
/*
- * ALC888 Acer Aspire 4930G model
- */
-
-static struct hda_verb alc888_acer_aspire_4930g_verbs[] = {
-/* Front Mic: set to PIN_IN (empty by default) */
- {0x12, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN},
-/* Unselect Front Mic by default in input mixer 3 */
- {0x22, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0xb)},
-/* Enable unsolicited event for HP jack */
- {0x15, AC_VERB_SET_UNSOLICITED_ENABLE, ALC880_HP_EVENT | AC_USRSP_EN},
-/* Connect Internal HP to front */
- {0x14, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
- {0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
- {0x14, AC_VERB_SET_CONNECT_SEL, 0x00},
-/* Connect HP out to front */
- {0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
- {0x15, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
- {0x15, AC_VERB_SET_CONNECT_SEL, 0x00},
- {0x15, AC_VERB_SET_EAPD_BTLENABLE, 2},
- { }
-};
-
-/*
- * ALC888 Acer Aspire 6530G model
- */
-
-static struct hda_verb alc888_acer_aspire_6530g_verbs[] = {
-/* Route to built-in subwoofer as well as speakers */
- {0x0c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
- {0x0c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
- {0x0f, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
- {0x0f, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
-/* Bias voltage on for external mic port */
- {0x18, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN | PIN_VREF80},
-/* Front Mic: set to PIN_IN (empty by default) */
- {0x12, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN},
-/* Unselect Front Mic by default in input mixer 3 */
- {0x22, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0xb)},
-/* Enable unsolicited event for HP jack */
- {0x15, AC_VERB_SET_UNSOLICITED_ENABLE, ALC880_HP_EVENT | AC_USRSP_EN},
-/* Enable speaker output */
- {0x14, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
- {0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
- {0x14, AC_VERB_SET_EAPD_BTLENABLE, 2},
-/* Enable headphone output */
- {0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT | PIN_HP},
- {0x15, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
- {0x15, AC_VERB_SET_CONNECT_SEL, 0x00},
- {0x15, AC_VERB_SET_EAPD_BTLENABLE, 2},
- { }
-};
-
-/*
- *ALC888 Acer Aspire 7730G model
- */
-
-static struct hda_verb alc888_acer_aspire_7730G_verbs[] = {
-/* Bias voltage on for external mic port */
- {0x18, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN | PIN_VREF80},
-/* Front Mic: set to PIN_IN (empty by default) */
- {0x12, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN},
-/* Unselect Front Mic by default in input mixer 3 */
- {0x22, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0xb)},
-/* Enable unsolicited event for HP jack */
- {0x15, AC_VERB_SET_UNSOLICITED_ENABLE, ALC880_HP_EVENT | AC_USRSP_EN},
-/* Enable speaker output */
- {0x14, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
- {0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
- {0x14, AC_VERB_SET_EAPD_BTLENABLE, 2},
-/* Enable headphone output */
- {0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT | PIN_HP},
- {0x15, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
- {0x15, AC_VERB_SET_CONNECT_SEL, 0x00},
- {0x15, AC_VERB_SET_EAPD_BTLENABLE, 2},
-/*Enable internal subwoofer */
- {0x17, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
- {0x17, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
- {0x17, AC_VERB_SET_CONNECT_SEL, 0x02},
- {0x17, AC_VERB_SET_EAPD_BTLENABLE, 2},
- { }
-};
-
-/*
- * ALC889 Acer Aspire 8930G model
- */
-
-static struct hda_verb alc889_acer_aspire_8930g_verbs[] = {
-/* Front Mic: set to PIN_IN (empty by default) */
- {0x12, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN},
-/* Unselect Front Mic by default in input mixer 3 */
- {0x22, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0xb)},
-/* Enable unsolicited event for HP jack */
- {0x15, AC_VERB_SET_UNSOLICITED_ENABLE, ALC880_HP_EVENT | AC_USRSP_EN},
-/* Connect Internal Front to Front */
- {0x14, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
- {0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
- {0x14, AC_VERB_SET_CONNECT_SEL, 0x00},
-/* Connect Internal Rear to Rear */
- {0x1b, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
- {0x1b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
- {0x1b, AC_VERB_SET_CONNECT_SEL, 0x01},
-/* Connect Internal CLFE to CLFE */
- {0x16, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
- {0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
- {0x16, AC_VERB_SET_CONNECT_SEL, 0x02},
-/* Connect HP out to Front */
- {0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT | PIN_HP},
- {0x15, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
- {0x15, AC_VERB_SET_CONNECT_SEL, 0x00},
-/* Enable all DACs */
-/* DAC DISABLE/MUTE 1? */
-/* setting bits 1-5 disables DAC nids 0x02-0x06 apparently. Init=0x38 */
- {0x20, AC_VERB_SET_COEF_INDEX, 0x03},
- {0x20, AC_VERB_SET_PROC_COEF, 0x0000},
-/* DAC DISABLE/MUTE 2? */
-/* some bit here disables the other DACs. Init=0x4900 */
- {0x20, AC_VERB_SET_COEF_INDEX, 0x08},
- {0x20, AC_VERB_SET_PROC_COEF, 0x0000},
-/* DMIC fix
- * This laptop has a stereo digital microphone. The mics are only 1cm apart
- * which makes the stereo useless. However, either the mic or the ALC889
- * makes the signal become a difference/sum signal instead of standard
- * stereo, which is annoying. So instead we flip this bit which makes the
- * codec replicate the sum signal to both channels, turning it into a
- * normal mono mic.
+ * Inverted digital-mic handling
+ *
+ * First off, it's a bit tricky. The "Inverted Internal Mic Capture Switch"
+ * gives the additional mute only to the right channel of the digital mic
+ * capture stream. This is a workaround for avoiding the almost silence
+ * by summing the stereo stream from some (known to be ForteMedia)
+ * digital mic unit.
+ *
+ * The logic is to call alc_inv_dmic_sync() after each action (possibly)
+ * modifying ADC amp. When the mute flag is set, it mutes the R-channel
+ * without caching so that the cache can still keep the original value.
+ * The cached value is then restored when the flag is set off or any other
+ * than d-mic is used as the current input source.
*/
-/* DMIC_CONTROL? Init value = 0x0001 */
- {0x20, AC_VERB_SET_COEF_INDEX, 0x0b},
- {0x20, AC_VERB_SET_PROC_COEF, 0x0003},
- { }
-};
-
-static struct hda_input_mux alc888_2_capture_sources[2] = {
- /* Front mic only available on one ADC */
- {
- .num_items = 4,
- .items = {
- { "Mic", 0x0 },
- { "Line", 0x2 },
- { "CD", 0x4 },
- { "Front Mic", 0xb },
- },
- },
- {
- .num_items = 3,
- .items = {
- { "Mic", 0x0 },
- { "Line", 0x2 },
- { "CD", 0x4 },
- },
- }
-};
-
-static struct hda_input_mux alc888_acer_aspire_6530_sources[2] = {
- /* Interal mic only available on one ADC */
- {
- .num_items = 5,
- .items = {
- { "Mic", 0x0 },
- { "Line In", 0x2 },
- { "CD", 0x4 },
- { "Input Mix", 0xa },
- { "Internal Mic", 0xb },
- },
- },
- {
- .num_items = 4,
- .items = {
- { "Mic", 0x0 },
- { "Line In", 0x2 },
- { "CD", 0x4 },
- { "Input Mix", 0xa },
- },
- }
-};
-
-static struct hda_input_mux alc889_capture_sources[3] = {
- /* Digital mic only available on first "ADC" */
- {
- .num_items = 5,
- .items = {
- { "Mic", 0x0 },
- { "Line", 0x2 },
- { "CD", 0x4 },
- { "Front Mic", 0xb },
- { "Input Mix", 0xa },
- },
- },
- {
- .num_items = 4,
- .items = {
- { "Mic", 0x0 },
- { "Line", 0x2 },
- { "CD", 0x4 },
- { "Input Mix", 0xa },
- },
- },
- {
- .num_items = 4,
- .items = {
- { "Mic", 0x0 },
- { "Line", 0x2 },
- { "CD", 0x4 },
- { "Input Mix", 0xa },
- },
- }
-};
-
-static struct snd_kcontrol_new alc888_base_mixer[] = {
- HDA_CODEC_VOLUME("Front Playback Volume", 0x0c, 0x0, HDA_OUTPUT),
- HDA_BIND_MUTE("Front Playback Switch", 0x0c, 2, HDA_INPUT),
- HDA_CODEC_VOLUME("Surround Playback Volume", 0x0d, 0x0, HDA_OUTPUT),
- HDA_BIND_MUTE("Surround Playback Switch", 0x0d, 2, HDA_INPUT),
- HDA_CODEC_VOLUME_MONO("Center Playback Volume", 0x0e, 1, 0x0,
- HDA_OUTPUT),
- HDA_CODEC_VOLUME_MONO("LFE Playback Volume", 0x0e, 2, 0x0, HDA_OUTPUT),
- HDA_BIND_MUTE_MONO("Center Playback Switch", 0x0e, 1, 2, HDA_INPUT),
- HDA_BIND_MUTE_MONO("LFE Playback Switch", 0x0e, 2, 2, HDA_INPUT),
- HDA_CODEC_VOLUME("Side Playback Volume", 0x0f, 0x0, HDA_OUTPUT),
- HDA_BIND_MUTE("Side Playback Switch", 0x0f, 2, HDA_INPUT),
- HDA_CODEC_VOLUME("CD Playback Volume", 0x0b, 0x04, HDA_INPUT),
- HDA_CODEC_MUTE("CD Playback Switch", 0x0b, 0x04, HDA_INPUT),
- HDA_CODEC_VOLUME("Line Playback Volume", 0x0b, 0x02, HDA_INPUT),
- HDA_CODEC_MUTE("Line Playback Switch", 0x0b, 0x02, HDA_INPUT),
- HDA_CODEC_VOLUME("Mic Playback Volume", 0x0b, 0x0, HDA_INPUT),
- HDA_CODEC_VOLUME("Mic Boost Volume", 0x18, 0, HDA_INPUT),
- HDA_CODEC_MUTE("Mic Playback Switch", 0x0b, 0x0, HDA_INPUT),
- { } /* end */
-};
-
-static struct snd_kcontrol_new alc888_acer_aspire_4930g_mixer[] = {
- HDA_CODEC_VOLUME("Front Playback Volume", 0x0c, 0x0, HDA_OUTPUT),
- HDA_BIND_MUTE("Front Playback Switch", 0x0c, 2, HDA_INPUT),
- HDA_CODEC_VOLUME("Surround Playback Volume", 0x0d, 0x0, HDA_OUTPUT),
- HDA_BIND_MUTE("Surround Playback Switch", 0x0d, 2, HDA_INPUT),
- HDA_CODEC_VOLUME_MONO("Center Playback Volume", 0x0e, 1, 0x0,
- HDA_OUTPUT),
- HDA_BIND_MUTE_MONO("Center Playback Switch", 0x0e, 1, 2, HDA_INPUT),
- HDA_CODEC_VOLUME_MONO("LFE Playback Volume", 0x0e, 2, 0x0, HDA_OUTPUT),
- HDA_BIND_MUTE_MONO("LFE Playback Switch", 0x0e, 2, 2, HDA_INPUT),
- HDA_CODEC_VOLUME_MONO("Internal LFE Playback Volume", 0x0f, 1, 0x0, HDA_OUTPUT),
- HDA_BIND_MUTE_MONO("Internal LFE Playback Switch", 0x0f, 1, 2, HDA_INPUT),
- HDA_CODEC_VOLUME("CD Playback Volume", 0x0b, 0x04, HDA_INPUT),
- HDA_CODEC_MUTE("CD Playback Switch", 0x0b, 0x04, HDA_INPUT),
- HDA_CODEC_VOLUME("Line Playback Volume", 0x0b, 0x02, HDA_INPUT),
- HDA_CODEC_MUTE("Line Playback Switch", 0x0b, 0x02, HDA_INPUT),
- HDA_CODEC_VOLUME("Mic Playback Volume", 0x0b, 0x0, HDA_INPUT),
- HDA_CODEC_VOLUME("Mic Boost Volume", 0x18, 0, HDA_INPUT),
- HDA_CODEC_MUTE("Mic Playback Switch", 0x0b, 0x0, HDA_INPUT),
- { } /* end */
-};
-
-static struct snd_kcontrol_new alc889_acer_aspire_8930g_mixer[] = {
- HDA_CODEC_VOLUME("Front Playback Volume", 0x0c, 0x0, HDA_OUTPUT),
- HDA_BIND_MUTE("Front Playback Switch", 0x0c, 2, HDA_INPUT),
- HDA_CODEC_VOLUME("Surround Playback Volume", 0x0d, 0x0, HDA_OUTPUT),
- HDA_BIND_MUTE("Surround Playback Switch", 0x0d, 2, HDA_INPUT),
- HDA_CODEC_VOLUME_MONO("Center Playback Volume", 0x0e, 1, 0x0,
- HDA_OUTPUT),
- HDA_CODEC_VOLUME_MONO("LFE Playback Volume", 0x0e, 2, 0x0, HDA_OUTPUT),
- HDA_BIND_MUTE_MONO("Center Playback Switch", 0x0e, 1, 2, HDA_INPUT),
- HDA_BIND_MUTE_MONO("LFE Playback Switch", 0x0e, 2, 2, HDA_INPUT),
- HDA_CODEC_VOLUME("Line Playback Volume", 0x0b, 0x02, HDA_INPUT),
- HDA_CODEC_MUTE("Line Playback Switch", 0x0b, 0x02, HDA_INPUT),
- HDA_CODEC_VOLUME("Mic Playback Volume", 0x0b, 0x0, HDA_INPUT),
- HDA_CODEC_VOLUME("Mic Boost Volume", 0x18, 0, HDA_INPUT),
- HDA_CODEC_MUTE("Mic Playback Switch", 0x0b, 0x0, HDA_INPUT),
- { } /* end */
-};
-
-
-static void alc888_acer_aspire_4930g_setup(struct hda_codec *codec)
-{
- struct alc_spec *spec = codec->spec;
-
- spec->autocfg.hp_pins[0] = 0x15;
- spec->autocfg.speaker_pins[0] = 0x14;
- spec->autocfg.speaker_pins[1] = 0x16;
- spec->autocfg.speaker_pins[2] = 0x17;
-}
-
-static void alc888_acer_aspire_6530g_setup(struct hda_codec *codec)
-{
- struct alc_spec *spec = codec->spec;
-
- spec->autocfg.hp_pins[0] = 0x15;
- spec->autocfg.speaker_pins[0] = 0x14;
- spec->autocfg.speaker_pins[1] = 0x16;
- spec->autocfg.speaker_pins[2] = 0x17;
-}
-
-static void alc888_acer_aspire_7730g_setup(struct hda_codec *codec)
+static void alc_inv_dmic_sync(struct hda_codec *codec, bool force)
{
struct alc_spec *spec = codec->spec;
+ int src, nums;
- spec->autocfg.hp_pins[0] = 0x15;
- spec->autocfg.speaker_pins[0] = 0x14;
- spec->autocfg.speaker_pins[1] = 0x16;
- spec->autocfg.speaker_pins[2] = 0x17;
+ if (!spec->inv_dmic_fixup)
+ return;
+ if (!spec->inv_dmic_muted && !force)
+ return;
+ nums = spec->gen.dyn_adc_switch ? 1 : spec->gen.num_adc_nids;
+ for (src = 0; src < nums; src++) {
+ bool dmic_fixup = false;
+
+ if (spec->inv_dmic_muted &&
+ spec->gen.imux_pins[spec->gen.cur_mux[src]] == spec->inv_dmic_pin)
+ dmic_fixup = true;
+ if (!dmic_fixup && !force)
+ continue;
+ alc_inv_dmic_sync_adc(codec, src);
+ }
}
-static void alc889_acer_aspire_8930g_setup(struct hda_codec *codec)
+static void alc_inv_dmic_hook(struct hda_codec *codec,
+ struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
{
- struct alc_spec *spec = codec->spec;
-
- spec->autocfg.hp_pins[0] = 0x15;
- spec->autocfg.speaker_pins[0] = 0x14;
- spec->autocfg.speaker_pins[1] = 0x16;
- spec->autocfg.speaker_pins[2] = 0x1b;
+ alc_inv_dmic_sync(codec, false);
}
-/*
- * ALC880 3-stack model
- *
- * DAC: Front = 0x02 (0x0c), Surr = 0x05 (0x0f), CLFE = 0x04 (0x0e)
- * Pin assignment: Front = 0x14, Line-In/Surr = 0x1a, Mic/CLFE = 0x18,
- * F-Mic = 0x1b, HP = 0x19
- */
-
-static hda_nid_t alc880_dac_nids[4] = {
- /* front, rear, clfe, rear_surr */
- 0x02, 0x05, 0x04, 0x03
-};
-
-static hda_nid_t alc880_adc_nids[3] = {
- /* ADC0-2 */
- 0x07, 0x08, 0x09,
-};
-
-/* The datasheet says the node 0x07 is connected from inputs,
- * but it shows zero connection in the real implementation on some devices.
- * Note: this is a 915GAV bug, fixed on 915GLV
- */
-static hda_nid_t alc880_adc_nids_alt[2] = {
- /* ADC1-2 */
- 0x08, 0x09,
-};
-
-#define ALC880_DIGOUT_NID 0x06
-#define ALC880_DIGIN_NID 0x0a
-
-static struct hda_input_mux alc880_capture_source = {
- .num_items = 4,
- .items = {
- { "Mic", 0x0 },
- { "Front Mic", 0x3 },
- { "Line", 0x2 },
- { "CD", 0x4 },
- },
-};
-
-/* channel source setting (2/6 channel selection for 3-stack) */
-/* 2ch mode */
-static struct hda_verb alc880_threestack_ch2_init[] = {
- /* set line-in to input, mute it */
- { 0x1a, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN },
- { 0x1a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE },
- /* set mic-in to input vref 80%, mute it */
- { 0x18, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80 },
- { 0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE },
- { } /* end */
-};
-
-/* 6ch mode */
-static struct hda_verb alc880_threestack_ch6_init[] = {
- /* set line-in to output, unmute it */
- { 0x1a, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT },
- { 0x1a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE },
- /* set mic-in to output, unmute it */
- { 0x18, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT },
- { 0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE },
- { } /* end */
-};
-
-static struct hda_channel_mode alc880_threestack_modes[2] = {
- { 2, alc880_threestack_ch2_init },
- { 6, alc880_threestack_ch6_init },
-};
-
-static struct snd_kcontrol_new alc880_three_stack_mixer[] = {
- HDA_CODEC_VOLUME("Front Playback Volume", 0x0c, 0x0, HDA_OUTPUT),
- HDA_BIND_MUTE("Front Playback Switch", 0x0c, 2, HDA_INPUT),
- HDA_CODEC_VOLUME("Surround Playback Volume", 0x0f, 0x0, HDA_OUTPUT),
- HDA_BIND_MUTE("Surround Playback Switch", 0x0f, 2, HDA_INPUT),
- HDA_CODEC_VOLUME_MONO("Center Playback Volume", 0x0e, 1, 0x0, HDA_OUTPUT),
- HDA_CODEC_VOLUME_MONO("LFE Playback Volume", 0x0e, 2, 0x0, HDA_OUTPUT),
- HDA_BIND_MUTE_MONO("Center Playback Switch", 0x0e, 1, 2, HDA_INPUT),
- HDA_BIND_MUTE_MONO("LFE Playback Switch", 0x0e, 2, 2, HDA_INPUT),
- HDA_CODEC_VOLUME("CD Playback Volume", 0x0b, 0x04, HDA_INPUT),
- HDA_CODEC_MUTE("CD Playback Switch", 0x0b, 0x04, HDA_INPUT),
- HDA_CODEC_VOLUME("Line Playback Volume", 0x0b, 0x02, HDA_INPUT),
- HDA_CODEC_MUTE("Line Playback Switch", 0x0b, 0x02, HDA_INPUT),
- HDA_CODEC_VOLUME("Mic Playback Volume", 0x0b, 0x0, HDA_INPUT),
- HDA_CODEC_MUTE("Mic Playback Switch", 0x0b, 0x0, HDA_INPUT),
- HDA_CODEC_VOLUME("Front Mic Playback Volume", 0x0b, 0x3, HDA_INPUT),
- HDA_CODEC_MUTE("Front Mic Playback Switch", 0x0b, 0x3, HDA_INPUT),
- HDA_CODEC_MUTE("Headphone Playback Switch", 0x19, 0x0, HDA_OUTPUT),
- {
- .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
- .name = "Channel Mode",
- .info = alc_ch_mode_info,
- .get = alc_ch_mode_get,
- .put = alc_ch_mode_put,
- },
- { } /* end */
-};
-
-/* capture mixer elements */
-static int alc_cap_vol_info(struct snd_kcontrol *kcontrol,
- struct snd_ctl_elem_info *uinfo)
+static int alc_inv_dmic_sw_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
{
struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
struct alc_spec *spec = codec->spec;
- int err;
- mutex_lock(&codec->control_mutex);
- kcontrol->private_value = HDA_COMPOSE_AMP_VAL(spec->adc_nids[0], 3, 0,
- HDA_INPUT);
- err = snd_hda_mixer_amp_volume_info(kcontrol, uinfo);
- mutex_unlock(&codec->control_mutex);
- return err;
+ ucontrol->value.integer.value[0] = !spec->inv_dmic_muted;
+ return 0;
}
-static int alc_cap_vol_tlv(struct snd_kcontrol *kcontrol, int op_flag,
- unsigned int size, unsigned int __user *tlv)
+static int alc_inv_dmic_sw_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
{
struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
struct alc_spec *spec = codec->spec;
- int err;
+ unsigned int val = !ucontrol->value.integer.value[0];
- mutex_lock(&codec->control_mutex);
- kcontrol->private_value = HDA_COMPOSE_AMP_VAL(spec->adc_nids[0], 3, 0,
- HDA_INPUT);
- err = snd_hda_mixer_amp_tlv(kcontrol, op_flag, size, tlv);
- mutex_unlock(&codec->control_mutex);
- return err;
+ if (val == spec->inv_dmic_muted)
+ return 0;
+ spec->inv_dmic_muted = val;
+ alc_inv_dmic_sync(codec, true);
+ return 0;
}
-typedef int (*getput_call_t)(struct snd_kcontrol *kcontrol,
- struct snd_ctl_elem_value *ucontrol);
+static const struct snd_kcontrol_new alc_inv_dmic_sw = {
+ .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+ .name = "Inverted Internal Mic Capture Switch",
+ .info = snd_ctl_boolean_mono_info,
+ .get = alc_inv_dmic_sw_get,
+ .put = alc_inv_dmic_sw_put,
+};
-static int alc_cap_getput_caller(struct snd_kcontrol *kcontrol,
- struct snd_ctl_elem_value *ucontrol,
- getput_call_t func)
+static int alc_add_inv_dmic_mixer(struct hda_codec *codec, hda_nid_t nid)
{
- struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
struct alc_spec *spec = codec->spec;
- unsigned int adc_idx = snd_ctl_get_ioffidx(kcontrol, &ucontrol->id);
- int err;
-
- mutex_lock(&codec->control_mutex);
- kcontrol->private_value = HDA_COMPOSE_AMP_VAL(spec->adc_nids[adc_idx],
- 3, 0, HDA_INPUT);
- err = func(kcontrol, ucontrol);
- mutex_unlock(&codec->control_mutex);
- return err;
-}
-
-static int alc_cap_vol_get(struct snd_kcontrol *kcontrol,
- struct snd_ctl_elem_value *ucontrol)
-{
- return alc_cap_getput_caller(kcontrol, ucontrol,
- snd_hda_mixer_amp_volume_get);
-}
-
-static int alc_cap_vol_put(struct snd_kcontrol *kcontrol,
- struct snd_ctl_elem_value *ucontrol)
-{
- return alc_cap_getput_caller(kcontrol, ucontrol,
- snd_hda_mixer_amp_volume_put);
-}
-/* capture mixer elements */
-#define alc_cap_sw_info snd_ctl_boolean_stereo_info
-
-static int alc_cap_sw_get(struct snd_kcontrol *kcontrol,
- struct snd_ctl_elem_value *ucontrol)
-{
- return alc_cap_getput_caller(kcontrol, ucontrol,
- snd_hda_mixer_amp_switch_get);
+ if (!snd_hda_gen_add_kctl(&spec->gen, NULL, &alc_inv_dmic_sw))
+ return -ENOMEM;
+ spec->inv_dmic_fixup = 1;
+ spec->inv_dmic_muted = 0;
+ spec->inv_dmic_pin = nid;
+ spec->gen.cap_sync_hook = alc_inv_dmic_hook;
+ return 0;
}
-static int alc_cap_sw_put(struct snd_kcontrol *kcontrol,
- struct snd_ctl_elem_value *ucontrol)
+/* typically the digital mic is put at node 0x12 */
+static void alc_fixup_inv_dmic_0x12(struct hda_codec *codec,
+ const struct hda_fixup *fix, int action)
{
- return alc_cap_getput_caller(kcontrol, ucontrol,
- snd_hda_mixer_amp_switch_put);
-}
-
-#define _DEFINE_CAPMIX(num) \
- { \
- .iface = SNDRV_CTL_ELEM_IFACE_MIXER, \
- .name = "Capture Switch", \
- .access = SNDRV_CTL_ELEM_ACCESS_READWRITE, \
- .count = num, \
- .info = alc_cap_sw_info, \
- .get = alc_cap_sw_get, \
- .put = alc_cap_sw_put, \
- }, \
- { \
- .iface = SNDRV_CTL_ELEM_IFACE_MIXER, \
- .name = "Capture Volume", \
- .access = (SNDRV_CTL_ELEM_ACCESS_READWRITE | \
- SNDRV_CTL_ELEM_ACCESS_TLV_READ | \
- SNDRV_CTL_ELEM_ACCESS_TLV_CALLBACK), \
- .count = num, \
- .info = alc_cap_vol_info, \
- .get = alc_cap_vol_get, \
- .put = alc_cap_vol_put, \
- .tlv = { .c = alc_cap_vol_tlv }, \
- }
-
-#define _DEFINE_CAPSRC(num) \
- { \
- .iface = SNDRV_CTL_ELEM_IFACE_MIXER, \
- /* .name = "Capture Source", */ \
- .name = "Input Source", \
- .count = num, \
- .info = alc_mux_enum_info, \
- .get = alc_mux_enum_get, \
- .put = alc_mux_enum_put, \
- }
-
-#define DEFINE_CAPMIX(num) \
-static struct snd_kcontrol_new alc_capture_mixer ## num[] = { \
- _DEFINE_CAPMIX(num), \
- _DEFINE_CAPSRC(num), \
- { } /* end */ \
-}
-
-#define DEFINE_CAPMIX_NOSRC(num) \
-static struct snd_kcontrol_new alc_capture_mixer_nosrc ## num[] = { \
- _DEFINE_CAPMIX(num), \
- { } /* end */ \
+ if (action == HDA_FIXUP_ACT_PROBE)
+ alc_add_inv_dmic_mixer(codec, 0x12);
}
-/* up to three ADCs */
-DEFINE_CAPMIX(1);
-DEFINE_CAPMIX(2);
-DEFINE_CAPMIX(3);
-DEFINE_CAPMIX_NOSRC(1);
-DEFINE_CAPMIX_NOSRC(2);
-DEFINE_CAPMIX_NOSRC(3);
-
-/*
- * ALC880 5-stack model
- *
- * DAC: Front = 0x02 (0x0c), Surr = 0x05 (0x0f), CLFE = 0x04 (0x0d),
- * Side = 0x02 (0xd)
- * Pin assignment: Front = 0x14, Surr = 0x17, CLFE = 0x16
- * Line-In/Side = 0x1a, Mic = 0x18, F-Mic = 0x1b, HP = 0x19
- */
-
-/* additional mixers to alc880_three_stack_mixer */
-static struct snd_kcontrol_new alc880_five_stack_mixer[] = {
- HDA_CODEC_VOLUME("Side Playback Volume", 0x0d, 0x0, HDA_OUTPUT),
- HDA_BIND_MUTE("Side Playback Switch", 0x0d, 2, HDA_INPUT),
- { } /* end */
-};
-
-/* channel source setting (6/8 channel selection for 5-stack) */
-/* 6ch mode */
-static struct hda_verb alc880_fivestack_ch6_init[] = {
- /* set line-in to input, mute it */
- { 0x1a, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN },
- { 0x1a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE },
- { } /* end */
-};
-
-/* 8ch mode */
-static struct hda_verb alc880_fivestack_ch8_init[] = {
- /* set line-in to output, unmute it */
- { 0x1a, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT },
- { 0x1a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE },
- { } /* end */
-};
-
-static struct hda_channel_mode alc880_fivestack_modes[2] = {
- { 6, alc880_fivestack_ch6_init },
- { 8, alc880_fivestack_ch8_init },
-};
-
-
-/*
- * ALC880 6-stack model
- *
- * DAC: Front = 0x02 (0x0c), Surr = 0x03 (0x0d), CLFE = 0x04 (0x0e),
- * Side = 0x05 (0x0f)
- * Pin assignment: Front = 0x14, Surr = 0x15, CLFE = 0x16, Side = 0x17,
- * Mic = 0x18, F-Mic = 0x19, Line = 0x1a, HP = 0x1b
- */
-
-static hda_nid_t alc880_6st_dac_nids[4] = {
- /* front, rear, clfe, rear_surr */
- 0x02, 0x03, 0x04, 0x05
-};
-
-static struct hda_input_mux alc880_6stack_capture_source = {
- .num_items = 4,
- .items = {
- { "Mic", 0x0 },
- { "Front Mic", 0x1 },
- { "Line", 0x2 },
- { "CD", 0x4 },
- },
-};
-
-/* fixed 8-channels */
-static struct hda_channel_mode alc880_sixstack_modes[1] = {
- { 8, NULL },
-};
-
-static struct snd_kcontrol_new alc880_six_stack_mixer[] = {
- HDA_CODEC_VOLUME("Front Playback Volume", 0x0c, 0x0, HDA_OUTPUT),
- HDA_BIND_MUTE("Front Playback Switch", 0x0c, 2, HDA_INPUT),
- HDA_CODEC_VOLUME("Surround Playback Volume", 0x0d, 0x0, HDA_OUTPUT),
- HDA_BIND_MUTE("Surround Playback Switch", 0x0d, 2, HDA_INPUT),
- HDA_CODEC_VOLUME_MONO("Center Playback Volume", 0x0e, 1, 0x0, HDA_OUTPUT),
- HDA_CODEC_VOLUME_MONO("LFE Playback Volume", 0x0e, 2, 0x0, HDA_OUTPUT),
- HDA_BIND_MUTE_MONO("Center Playback Switch", 0x0e, 1, 2, HDA_INPUT),
- HDA_BIND_MUTE_MONO("LFE Playback Switch", 0x0e, 2, 2, HDA_INPUT),
- HDA_CODEC_VOLUME("Side Playback Volume", 0x0f, 0x0, HDA_OUTPUT),
- HDA_BIND_MUTE("Side Playback Switch", 0x0f, 2, HDA_INPUT),
- HDA_CODEC_VOLUME("CD Playback Volume", 0x0b, 0x04, HDA_INPUT),
- HDA_CODEC_MUTE("CD Playback Switch", 0x0b, 0x04, HDA_INPUT),
- HDA_CODEC_VOLUME("Line Playback Volume", 0x0b, 0x02, HDA_INPUT),
- HDA_CODEC_MUTE("Line Playback Switch", 0x0b, 0x02, HDA_INPUT),
- HDA_CODEC_VOLUME("Mic Playback Volume", 0x0b, 0x0, HDA_INPUT),
- HDA_CODEC_MUTE("Mic Playback Switch", 0x0b, 0x0, HDA_INPUT),
- HDA_CODEC_VOLUME("Front Mic Playback Volume", 0x0b, 0x1, HDA_INPUT),
- HDA_CODEC_MUTE("Front Mic Playback Switch", 0x0b, 0x1, HDA_INPUT),
- {
- .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
- .name = "Channel Mode",
- .info = alc_ch_mode_info,
- .get = alc_ch_mode_get,
- .put = alc_ch_mode_put,
- },
- { } /* end */
-};
-
-
-/*
- * ALC880 W810 model
- *
- * W810 has rear IO for:
- * Front (DAC 02)
- * Surround (DAC 03)
- * Center/LFE (DAC 04)
- * Digital out (06)
- *
- * The system also has a pair of internal speakers, and a headphone jack.
- * These are both connected to Line2 on the codec, hence to DAC 02.
- *
- * There is a variable resistor to control the speaker or headphone
- * volume. This is a hardware-only device without a software API.
- *
- * Plugging headphones in will disable the internal speakers. This is
- * implemented in hardware, not via the driver using jack sense. In
- * a similar fashion, plugging into the rear socket marked "front" will
- * disable both the speakers and headphones.
- *
- * For input, there's a microphone jack, and an "audio in" jack.
- * These may not do anything useful with this driver yet, because I
- * haven't setup any initialization verbs for these yet...
- */
-
-static hda_nid_t alc880_w810_dac_nids[3] = {
- /* front, rear/surround, clfe */
- 0x02, 0x03, 0x04
-};
-
-/* fixed 6 channels */
-static struct hda_channel_mode alc880_w810_modes[1] = {
- { 6, NULL }
-};
-
-/* Pin assignment: Front = 0x14, Surr = 0x15, CLFE = 0x16, HP = 0x1b */
-static struct snd_kcontrol_new alc880_w810_base_mixer[] = {
- HDA_CODEC_VOLUME("Front Playback Volume", 0x0c, 0x0, HDA_OUTPUT),
- HDA_BIND_MUTE("Front Playback Switch", 0x0c, 2, HDA_INPUT),
- HDA_CODEC_VOLUME("Surround Playback Volume", 0x0d, 0x0, HDA_OUTPUT),
- HDA_BIND_MUTE("Surround Playback Switch", 0x0d, 2, HDA_INPUT),
- HDA_CODEC_VOLUME_MONO("Center Playback Volume", 0x0e, 1, 0x0, HDA_OUTPUT),
- HDA_CODEC_VOLUME_MONO("LFE Playback Volume", 0x0e, 2, 0x0, HDA_OUTPUT),
- HDA_BIND_MUTE_MONO("Center Playback Switch", 0x0e, 1, 2, HDA_INPUT),
- HDA_BIND_MUTE_MONO("LFE Playback Switch", 0x0e, 2, 2, HDA_INPUT),
- HDA_CODEC_MUTE("Headphone Playback Switch", 0x1b, 0x0, HDA_OUTPUT),
- { } /* end */
-};
-
-
-/*
- * Z710V model
- *
- * DAC: Front = 0x02 (0x0c), HP = 0x03 (0x0d)
- * Pin assignment: Front = 0x14, HP = 0x15, Mic = 0x18, Mic2 = 0x19(?),
- * Line = 0x1a
- */
-
-static hda_nid_t alc880_z71v_dac_nids[1] = {
- 0x02
-};
-#define ALC880_Z71V_HP_DAC 0x03
-
-/* fixed 2 channels */
-static struct hda_channel_mode alc880_2_jack_modes[1] = {
- { 2, NULL }
-};
-
-static struct snd_kcontrol_new alc880_z71v_mixer[] = {
- HDA_CODEC_VOLUME("Front Playback Volume", 0x0c, 0x0, HDA_OUTPUT),
- HDA_BIND_MUTE("Front Playback Switch", 0x0c, 2, HDA_INPUT),
- HDA_CODEC_VOLUME("Headphone Playback Volume", 0x0d, 0x0, HDA_OUTPUT),
- HDA_BIND_MUTE("Headphone Playback Switch", 0x0d, 2, HDA_INPUT),
- HDA_CODEC_VOLUME("CD Playback Volume", 0x0b, 0x04, HDA_INPUT),
- HDA_CODEC_MUTE("CD Playback Switch", 0x0b, 0x04, HDA_INPUT),
- HDA_CODEC_VOLUME("Mic Playback Volume", 0x0b, 0x0, HDA_INPUT),
- HDA_CODEC_MUTE("Mic Playback Switch", 0x0b, 0x0, HDA_INPUT),
- { } /* end */
-};
-
-
-/*
- * ALC880 F1734 model
- *
- * DAC: HP = 0x02 (0x0c), Front = 0x03 (0x0d)
- * Pin assignment: HP = 0x14, Front = 0x15, Mic = 0x18
- */
-
-static hda_nid_t alc880_f1734_dac_nids[1] = {
- 0x03
-};
-#define ALC880_F1734_HP_DAC 0x02
-
-static struct snd_kcontrol_new alc880_f1734_mixer[] = {
- HDA_CODEC_VOLUME("Headphone Playback Volume", 0x0c, 0x0, HDA_OUTPUT),
- HDA_BIND_MUTE("Headphone Playback Switch", 0x0c, 2, HDA_INPUT),
- HDA_CODEC_VOLUME("Speaker Playback Volume", 0x0d, 0x0, HDA_OUTPUT),
- HDA_BIND_MUTE("Speaker Playback Switch", 0x0d, 2, HDA_INPUT),
- HDA_CODEC_VOLUME("CD Playback Volume", 0x0b, 0x04, HDA_INPUT),
- HDA_CODEC_MUTE("CD Playback Switch", 0x0b, 0x04, HDA_INPUT),
- HDA_CODEC_VOLUME("Mic Playback Volume", 0x0b, 0x1, HDA_INPUT),
- HDA_CODEC_MUTE("Mic Playback Switch", 0x0b, 0x1, HDA_INPUT),
- { } /* end */
-};
-
-static struct hda_input_mux alc880_f1734_capture_source = {
- .num_items = 2,
- .items = {
- { "Mic", 0x1 },
- { "CD", 0x4 },
- },
-};
-
-
-/*
- * ALC880 ASUS model
- *
- * DAC: HP/Front = 0x02 (0x0c), Surr = 0x03 (0x0d), CLFE = 0x04 (0x0e)
- * Pin assignment: HP/Front = 0x14, Surr = 0x15, CLFE = 0x16,
- * Mic = 0x18, Line = 0x1a
- */
-
-#define alc880_asus_dac_nids alc880_w810_dac_nids /* identical with w810 */
-#define alc880_asus_modes alc880_threestack_modes /* 2/6 channel mode */
-
-static struct snd_kcontrol_new alc880_asus_mixer[] = {
- HDA_CODEC_VOLUME("Front Playback Volume", 0x0c, 0x0, HDA_OUTPUT),
- HDA_BIND_MUTE("Front Playback Switch", 0x0c, 2, HDA_INPUT),
- HDA_CODEC_VOLUME("Surround Playback Volume", 0x0d, 0x0, HDA_OUTPUT),
- HDA_BIND_MUTE("Surround Playback Switch", 0x0d, 2, HDA_INPUT),
- HDA_CODEC_VOLUME_MONO("Center Playback Volume", 0x0e, 1, 0x0, HDA_OUTPUT),
- HDA_CODEC_VOLUME_MONO("LFE Playback Volume", 0x0e, 2, 0x0, HDA_OUTPUT),
- HDA_BIND_MUTE_MONO("Center Playback Switch", 0x0e, 1, 2, HDA_INPUT),
- HDA_BIND_MUTE_MONO("LFE Playback Switch", 0x0e, 2, 2, HDA_INPUT),
- HDA_CODEC_VOLUME("CD Playback Volume", 0x0b, 0x04, HDA_INPUT),
- HDA_CODEC_MUTE("CD Playback Switch", 0x0b, 0x04, HDA_INPUT),
- HDA_CODEC_VOLUME("Line Playback Volume", 0x0b, 0x02, HDA_INPUT),
- HDA_CODEC_MUTE("Line Playback Switch", 0x0b, 0x02, HDA_INPUT),
- HDA_CODEC_VOLUME("Mic Playback Volume", 0x0b, 0x0, HDA_INPUT),
- HDA_CODEC_MUTE("Mic Playback Switch", 0x0b, 0x0, HDA_INPUT),
- {
- .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
- .name = "Channel Mode",
- .info = alc_ch_mode_info,
- .get = alc_ch_mode_get,
- .put = alc_ch_mode_put,
- },
- { } /* end */
-};
-
-/*
- * ALC880 ASUS W1V model
- *
- * DAC: HP/Front = 0x02 (0x0c), Surr = 0x03 (0x0d), CLFE = 0x04 (0x0e)
- * Pin assignment: HP/Front = 0x14, Surr = 0x15, CLFE = 0x16,
- * Mic = 0x18, Line = 0x1a, Line2 = 0x1b
- */
-
-/* additional mixers to alc880_asus_mixer */
-static struct snd_kcontrol_new alc880_asus_w1v_mixer[] = {
- HDA_CODEC_VOLUME("Line2 Playback Volume", 0x0b, 0x03, HDA_INPUT),
- HDA_CODEC_MUTE("Line2 Playback Switch", 0x0b, 0x03, HDA_INPUT),
- { } /* end */
-};
-
-/* TCL S700 */
-static struct snd_kcontrol_new alc880_tcl_s700_mixer[] = {
- HDA_CODEC_VOLUME("Front Playback Volume", 0x0c, 0x0, HDA_OUTPUT),
- HDA_CODEC_MUTE("Front Playback Switch", 0x1b, 0x0, HDA_OUTPUT),
- HDA_CODEC_MUTE("Headphone Playback Switch", 0x14, 0x0, HDA_OUTPUT),
- HDA_CODEC_VOLUME("CD Playback Volume", 0x0B, 0x04, HDA_INPUT),
- HDA_CODEC_MUTE("CD Playback Switch", 0x0B, 0x04, HDA_INPUT),
- HDA_CODEC_VOLUME("Mic Playback Volume", 0x0B, 0x0, HDA_INPUT),
- HDA_CODEC_MUTE("Mic Playback Switch", 0x0B, 0x0, HDA_INPUT),
- HDA_CODEC_VOLUME("Capture Volume", 0x08, 0x0, HDA_INPUT),
- HDA_CODEC_MUTE("Capture Switch", 0x08, 0x0, HDA_INPUT),
- { } /* end */
-};
-
-/* Uniwill */
-static struct snd_kcontrol_new alc880_uniwill_mixer[] = {
- HDA_CODEC_VOLUME("Headphone Playback Volume", 0x0c, 0x0, HDA_OUTPUT),
- HDA_BIND_MUTE("Headphone Playback Switch", 0x0c, 2, HDA_INPUT),
- HDA_CODEC_VOLUME("Speaker Playback Volume", 0x0d, 0x0, HDA_OUTPUT),
- HDA_BIND_MUTE("Speaker Playback Switch", 0x0d, 2, HDA_INPUT),
- HDA_CODEC_VOLUME_MONO("Center Playback Volume", 0x0e, 1, 0x0, HDA_OUTPUT),
- HDA_CODEC_VOLUME_MONO("LFE Playback Volume", 0x0e, 2, 0x0, HDA_OUTPUT),
- HDA_BIND_MUTE_MONO("Center Playback Switch", 0x0e, 1, 2, HDA_INPUT),
- HDA_BIND_MUTE_MONO("LFE Playback Switch", 0x0e, 2, 2, HDA_INPUT),
- HDA_CODEC_VOLUME("CD Playback Volume", 0x0b, 0x04, HDA_INPUT),
- HDA_CODEC_MUTE("CD Playback Switch", 0x0b, 0x04, HDA_INPUT),
- HDA_CODEC_VOLUME("Line Playback Volume", 0x0b, 0x02, HDA_INPUT),
- HDA_CODEC_MUTE("Line Playback Switch", 0x0b, 0x02, HDA_INPUT),
- HDA_CODEC_VOLUME("Mic Playback Volume", 0x0b, 0x0, HDA_INPUT),
- HDA_CODEC_MUTE("Mic Playback Switch", 0x0b, 0x0, HDA_INPUT),
- HDA_CODEC_VOLUME("Front Mic Playback Volume", 0x0b, 0x1, HDA_INPUT),
- HDA_CODEC_MUTE("Front Mic Playback Switch", 0x0b, 0x1, HDA_INPUT),
- {
- .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
- .name = "Channel Mode",
- .info = alc_ch_mode_info,
- .get = alc_ch_mode_get,
- .put = alc_ch_mode_put,
- },
- { } /* end */
-};
-
-static struct snd_kcontrol_new alc880_fujitsu_mixer[] = {
- HDA_CODEC_VOLUME("Headphone Playback Volume", 0x0c, 0x0, HDA_OUTPUT),
- HDA_BIND_MUTE("Headphone Playback Switch", 0x0c, 2, HDA_INPUT),
- HDA_CODEC_VOLUME("Speaker Playback Volume", 0x0d, 0x0, HDA_OUTPUT),
- HDA_BIND_MUTE("Speaker Playback Switch", 0x0d, 2, HDA_INPUT),
- HDA_CODEC_VOLUME("CD Playback Volume", 0x0b, 0x04, HDA_INPUT),
- HDA_CODEC_MUTE("CD Playback Switch", 0x0b, 0x04, HDA_INPUT),
- HDA_CODEC_VOLUME("Mic Playback Volume", 0x0b, 0x0, HDA_INPUT),
- HDA_CODEC_MUTE("Mic Playback Switch", 0x0b, 0x0, HDA_INPUT),
- HDA_CODEC_VOLUME("Internal Mic Playback Volume", 0x0b, 0x1, HDA_INPUT),
- HDA_CODEC_MUTE("Internal Mic Playback Switch", 0x0b, 0x1, HDA_INPUT),
- { } /* end */
-};
-
-static struct snd_kcontrol_new alc880_uniwill_p53_mixer[] = {
- HDA_CODEC_VOLUME("Headphone Playback Volume", 0x0c, 0x0, HDA_OUTPUT),
- HDA_BIND_MUTE("Headphone Playback Switch", 0x0c, 2, HDA_INPUT),
- HDA_CODEC_VOLUME("Speaker Playback Volume", 0x0d, 0x0, HDA_OUTPUT),
- HDA_BIND_MUTE("Speaker Playback Switch", 0x0d, 2, HDA_INPUT),
- HDA_CODEC_VOLUME("Mic Playback Volume", 0x0b, 0x0, HDA_INPUT),
- HDA_CODEC_MUTE("Mic Playback Switch", 0x0b, 0x0, HDA_INPUT),
- { } /* end */
-};
-
-/*
- * virtual master controls
- */
-
-/*
- * slave controls for virtual master
- */
-static const char * const alc_slave_vols[] = {
- "Front Playback Volume",
- "Surround Playback Volume",
- "Center Playback Volume",
- "LFE Playback Volume",
- "Side Playback Volume",
- "Headphone Playback Volume",
- "Speaker Playback Volume",
- "Mono Playback Volume",
- "Line-Out Playback Volume",
- "PCM Playback Volume",
- NULL,
-};
-
-static const char * const alc_slave_sws[] = {
- "Front Playback Switch",
- "Surround Playback Switch",
- "Center Playback Switch",
- "LFE Playback Switch",
- "Side Playback Switch",
- "Headphone Playback Switch",
- "Speaker Playback Switch",
- "Mono Playback Switch",
- "IEC958 Playback Switch",
- "Line-Out Playback Switch",
- "PCM Playback Switch",
- NULL,
-};
-
-/*
- * build control elements
- */
-
-#define NID_MAPPING (-1)
-
-#define SUBDEV_SPEAKER_ (0 << 6)
-#define SUBDEV_HP_ (1 << 6)
-#define SUBDEV_LINE_ (2 << 6)
-#define SUBDEV_SPEAKER(x) (SUBDEV_SPEAKER_ | ((x) & 0x3f))
-#define SUBDEV_HP(x) (SUBDEV_HP_ | ((x) & 0x3f))
-#define SUBDEV_LINE(x) (SUBDEV_LINE_ | ((x) & 0x3f))
-
-static void alc_free_kctls(struct hda_codec *codec);
#ifdef CONFIG_SND_HDA_INPUT_BEEP
/* additional beep mixers; the actual parameters are overwritten at build */
-static struct snd_kcontrol_new alc_beep_mixer[] = {
+static const struct snd_kcontrol_new alc_beep_mixer[] = {
HDA_CODEC_VOLUME("Beep Playback Volume", 0, 0, HDA_INPUT),
HDA_CODEC_MUTE_BEEP("Beep Playback Switch", 0, 0, HDA_INPUT),
{ } /* end */
@@ -2924,45 +783,22 @@ static struct snd_kcontrol_new alc_beep_mixer[] = {
static int alc_build_controls(struct hda_codec *codec)
{
struct alc_spec *spec = codec->spec;
- struct snd_kcontrol *kctl = NULL;
- struct snd_kcontrol_new *knew;
- int i, j, err;
- unsigned int u;
- hda_nid_t nid;
+ int i, err;
+
+ err = snd_hda_gen_build_controls(codec);
+ if (err < 0)
+ return err;
for (i = 0; i < spec->num_mixers; i++) {
err = snd_hda_add_new_ctls(codec, spec->mixers[i]);
if (err < 0)
return err;
}
- if (spec->cap_mixer) {
- err = snd_hda_add_new_ctls(codec, spec->cap_mixer);
- if (err < 0)
- return err;
- }
- if (spec->multiout.dig_out_nid) {
- err = snd_hda_create_spdif_out_ctls(codec,
- spec->multiout.dig_out_nid);
- if (err < 0)
- return err;
- if (!spec->no_analog) {
- err = snd_hda_create_spdif_share_sw(codec,
- &spec->multiout);
- if (err < 0)
- return err;
- spec->multiout.share_spdif = 1;
- }
- }
- if (spec->dig_in_nid) {
- err = snd_hda_create_spdif_in_ctls(codec, spec->dig_in_nid);
- if (err < 0)
- return err;
- }
#ifdef CONFIG_SND_HDA_INPUT_BEEP
/* create beep controls if needed */
if (spec->beep_amp) {
- struct snd_kcontrol_new *knew;
+ const struct snd_kcontrol_new *knew;
for (knew = alc_beep_mixer; knew->name; knew++) {
struct snd_kcontrol *kctl;
kctl = snd_ctl_new1(knew, codec);
@@ -2976,1281 +812,51 @@ static int alc_build_controls(struct hda_codec *codec)
}
#endif
- /* if we have no master control, let's create it */
- if (!spec->no_analog &&
- !snd_hda_find_mixer_ctl(codec, "Master Playback Volume")) {
- unsigned int vmaster_tlv[4];
- snd_hda_set_vmaster_tlv(codec, spec->vmaster_nid,
- HDA_OUTPUT, vmaster_tlv);
- err = snd_hda_add_vmaster(codec, "Master Playback Volume",
- vmaster_tlv, alc_slave_vols);
- if (err < 0)
- return err;
- }
- if (!spec->no_analog &&
- !snd_hda_find_mixer_ctl(codec, "Master Playback Switch")) {
- err = snd_hda_add_vmaster(codec, "Master Playback Switch",
- NULL, alc_slave_sws);
- if (err < 0)
- return err;
- }
-
- /* assign Capture Source enums to NID */
- if (spec->capsrc_nids || spec->adc_nids) {
- kctl = snd_hda_find_mixer_ctl(codec, "Capture Source");
- if (!kctl)
- kctl = snd_hda_find_mixer_ctl(codec, "Input Source");
- for (i = 0; kctl && i < kctl->count; i++) {
- hda_nid_t *nids = spec->capsrc_nids;
- if (!nids)
- nids = spec->adc_nids;
- err = snd_hda_add_nid(codec, kctl, i, nids[i]);
- if (err < 0)
- return err;
- }
- }
- if (spec->cap_mixer) {
- const char *kname = kctl ? kctl->id.name : NULL;
- for (knew = spec->cap_mixer; knew->name; knew++) {
- if (kname && strcmp(knew->name, kname) == 0)
- continue;
- kctl = snd_hda_find_mixer_ctl(codec, knew->name);
- for (i = 0; kctl && i < kctl->count; i++) {
- err = snd_hda_add_nid(codec, kctl, i,
- spec->adc_nids[i]);
- if (err < 0)
- return err;
- }
- }
- }
-
- /* other nid->control mapping */
- for (i = 0; i < spec->num_mixers; i++) {
- for (knew = spec->mixers[i]; knew->name; knew++) {
- if (knew->iface != NID_MAPPING)
- continue;
- kctl = snd_hda_find_mixer_ctl(codec, knew->name);
- if (kctl == NULL)
- continue;
- u = knew->subdevice;
- for (j = 0; j < 4; j++, u >>= 8) {
- nid = u & 0x3f;
- if (nid == 0)
- continue;
- switch (u & 0xc0) {
- case SUBDEV_SPEAKER_:
- nid = spec->autocfg.speaker_pins[nid];
- break;
- case SUBDEV_LINE_:
- nid = spec->autocfg.line_out_pins[nid];
- break;
- case SUBDEV_HP_:
- nid = spec->autocfg.hp_pins[nid];
- break;
- default:
- continue;
- }
- err = snd_hda_add_nid(codec, kctl, 0, nid);
- if (err < 0)
- return err;
- }
- u = knew->private_value;
- for (j = 0; j < 4; j++, u >>= 8) {
- nid = u & 0xff;
- if (nid == 0)
- continue;
- err = snd_hda_add_nid(codec, kctl, 0, nid);
- if (err < 0)
- return err;
- }
- }
- }
-
- alc_free_kctls(codec); /* no longer needed */
-
+ snd_hda_apply_fixup(codec, HDA_FIXUP_ACT_BUILD);
return 0;
}
/*
- * initialize the codec volumes, etc
- */
-
-/*
- * generic initialization of ADC, input mixers and output mixers
- */
-static struct hda_verb alc880_volume_init_verbs[] = {
- /*
- * Unmute ADC0-2 and set the default input to mic-in
- */
- {0x07, AC_VERB_SET_CONNECT_SEL, 0x00},
- {0x07, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
- {0x08, AC_VERB_SET_CONNECT_SEL, 0x00},
- {0x08, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
- {0x09, AC_VERB_SET_CONNECT_SEL, 0x00},
- {0x09, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
-
- /* Unmute input amps (CD, Line In, Mic 1 & Mic 2) of the analog-loopback
- * mixer widget
- * Note: PASD motherboards uses the Line In 2 as the input for front
- * panel mic (mic 2)
- */
- /* Amp Indices: Mic1 = 0, Mic2 = 1, Line1 = 2, Line2 = 3, CD = 4 */
- {0x0b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
- {0x0b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
- {0x0b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(2)},
- {0x0b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(3)},
- {0x0b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(4)},
- {0x0b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(6)},
- {0x0b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(7)},
-
- /*
- * Set up output mixers (0x0c - 0x0f)
- */
- /* set vol=0 to output mixers */
- {0x0c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
- {0x0d, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
- {0x0e, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
- {0x0f, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
- /* set up input amps for analog loopback */
- /* Amp Indices: DAC = 0, mixer = 1 */
- {0x0c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
- {0x0c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
- {0x0d, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
- {0x0d, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
- {0x0e, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
- {0x0e, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
- {0x0f, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
- {0x0f, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
-
- { }
-};
-
-/*
- * 3-stack pin configuration:
- * front = 0x14, mic/clfe = 0x18, HP = 0x19, line/surr = 0x1a, f-mic = 0x1b
- */
-static struct hda_verb alc880_pin_3stack_init_verbs[] = {
- /*
- * preset connection lists of input pins
- * 0 = front, 1 = rear_surr, 2 = CLFE, 3 = surround
- */
- {0x10, AC_VERB_SET_CONNECT_SEL, 0x02}, /* mic/clfe */
- {0x11, AC_VERB_SET_CONNECT_SEL, 0x00}, /* HP */
- {0x12, AC_VERB_SET_CONNECT_SEL, 0x03}, /* line/surround */
-
- /*
- * Set pin mode and muting
- */
- /* set front pin widgets 0x14 for output */
- {0x14, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
- {0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
- /* Mic1 (rear panel) pin widget for input and vref at 80% */
- {0x18, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80},
- {0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
- /* Mic2 (as headphone out) for HP output */
- {0x19, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP},
- {0x19, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
- /* Line In pin widget for input */
- {0x1a, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN},
- {0x1a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
- /* Line2 (as front mic) pin widget for input and vref at 80% */
- {0x1b, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80},
- {0x1b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
- /* CD pin widget for input */
- {0x1c, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN},
-
- { }
-};
-
-/*
- * 5-stack pin configuration:
- * front = 0x14, surround = 0x17, clfe = 0x16, mic = 0x18, HP = 0x19,
- * line-in/side = 0x1a, f-mic = 0x1b
- */
-static struct hda_verb alc880_pin_5stack_init_verbs[] = {
- /*
- * preset connection lists of input pins
- * 0 = front, 1 = rear_surr, 2 = CLFE, 3 = surround
- */
- {0x11, AC_VERB_SET_CONNECT_SEL, 0x00}, /* HP */
- {0x12, AC_VERB_SET_CONNECT_SEL, 0x01}, /* line/side */
-
- /*
- * Set pin mode and muting
- */
- /* set pin widgets 0x14-0x17 for output */
- {0x14, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
- {0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
- {0x16, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
- {0x17, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
- /* unmute pins for output (no gain on this amp) */
- {0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
- {0x15, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
- {0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
- {0x17, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
-
- /* Mic1 (rear panel) pin widget for input and vref at 80% */
- {0x18, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80},
- {0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
- /* Mic2 (as headphone out) for HP output */
- {0x19, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP},
- {0x19, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
- /* Line In pin widget for input */
- {0x1a, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN},
- {0x1a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
- /* Line2 (as front mic) pin widget for input and vref at 80% */
- {0x1b, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80},
- {0x1b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
- /* CD pin widget for input */
- {0x1c, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN},
-
- { }
-};
-
-/*
- * W810 pin configuration:
- * front = 0x14, surround = 0x15, clfe = 0x16, HP = 0x1b
- */
-static struct hda_verb alc880_pin_w810_init_verbs[] = {
- /* hphone/speaker input selector: front DAC */
- {0x13, AC_VERB_SET_CONNECT_SEL, 0x0},
-
- {0x14, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
- {0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
- {0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
- {0x15, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
- {0x16, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
- {0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
-
- {0x1b, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP},
- {0x1b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
-
- { }
-};
-
-/*
- * Z71V pin configuration:
- * Speaker-out = 0x14, HP = 0x15, Mic = 0x18, Line-in = 0x1a, Mic2 = 0x1b (?)
- */
-static struct hda_verb alc880_pin_z71v_init_verbs[] = {
- {0x14, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
- {0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
- {0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP},
- {0x15, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
-
- {0x18, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80},
- {0x1a, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN},
- {0x1b, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80},
- {0x1c, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN},
-
- { }
-};
-
-/*
- * 6-stack pin configuration:
- * front = 0x14, surr = 0x15, clfe = 0x16, side = 0x17, mic = 0x18,
- * f-mic = 0x19, line = 0x1a, HP = 0x1b
- */
-static struct hda_verb alc880_pin_6stack_init_verbs[] = {
- {0x13, AC_VERB_SET_CONNECT_SEL, 0x00}, /* HP */
-
- {0x14, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
- {0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
- {0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
- {0x15, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
- {0x16, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
- {0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
- {0x17, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
- {0x17, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
-
- {0x18, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80},
- {0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
- {0x19, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80},
- {0x19, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
- {0x1a, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN},
- {0x1a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
- {0x1b, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP},
- {0x1b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
- {0x1c, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN},
-
- { }
-};
-
-/*
- * Uniwill pin configuration:
- * HP = 0x14, InternalSpeaker = 0x15, mic = 0x18, internal mic = 0x19,
- * line = 0x1a
- */
-static struct hda_verb alc880_uniwill_init_verbs[] = {
- {0x13, AC_VERB_SET_CONNECT_SEL, 0x00}, /* HP */
-
- {0x14, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP},
- {0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
- {0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP},
- {0x15, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
- {0x16, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP},
- {0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
- {0x17, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
- {0x17, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
- {0x0c, AC_VERB_SET_AMP_GAIN_MUTE, (0x7000 | (0x00 << 8))},
- {0x0c, AC_VERB_SET_AMP_GAIN_MUTE, (0x7000 | (0x01 << 8))},
- {0x0d, AC_VERB_SET_AMP_GAIN_MUTE, (0x7000 | (0x00 << 8))},
- {0x0d, AC_VERB_SET_AMP_GAIN_MUTE, (0x7000 | (0x01 << 8))},
- {0x0e, AC_VERB_SET_AMP_GAIN_MUTE, (0x7000 | (0x00 << 8))},
- {0x0e, AC_VERB_SET_AMP_GAIN_MUTE, (0x7000 | (0x01 << 8))},
-
- {0x18, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80},
- {0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
- {0x19, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80},
- {0x19, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
- {0x1a, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN},
- {0x1a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
- /* {0x1b, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP}, */
- /* {0x1b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE}, */
- {0x1c, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN},
-
- {0x14, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | ALC880_HP_EVENT},
- {0x18, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | ALC880_MIC_EVENT},
-
- { }
-};
-
-/*
-* Uniwill P53
-* HP = 0x14, InternalSpeaker = 0x15, mic = 0x19,
- */
-static struct hda_verb alc880_uniwill_p53_init_verbs[] = {
- {0x13, AC_VERB_SET_CONNECT_SEL, 0x00}, /* HP */
-
- {0x14, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP},
- {0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
- {0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP},
- {0x15, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
- {0x16, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP},
- {0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
- {0x0c, AC_VERB_SET_AMP_GAIN_MUTE, (0x7000 | (0x00 << 8))},
- {0x0c, AC_VERB_SET_AMP_GAIN_MUTE, (0x7000 | (0x01 << 8))},
- {0x0d, AC_VERB_SET_AMP_GAIN_MUTE, (0x7000 | (0x00 << 8))},
- {0x0d, AC_VERB_SET_AMP_GAIN_MUTE, (0x7000 | (0x01 << 8))},
- {0x0e, AC_VERB_SET_AMP_GAIN_MUTE, (0x7000 | (0x00 << 8))},
- {0x0e, AC_VERB_SET_AMP_GAIN_MUTE, (0x7000 | (0x01 << 8))},
-
- {0x18, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80},
- {0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
- {0x19, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80},
- {0x19, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
- {0x1a, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN},
- {0x1a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
-
- {0x14, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | ALC880_HP_EVENT},
- {0x21, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | ALC880_DCVOL_EVENT},
-
- { }
-};
-
-static struct hda_verb alc880_beep_init_verbs[] = {
- { 0x0b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(5) },
- { }
-};
-
-/* auto-toggle front mic */
-static void alc88x_simple_mic_automute(struct hda_codec *codec)
-{
- unsigned int present;
- unsigned char bits;
-
- present = snd_hda_jack_detect(codec, 0x18);
- bits = present ? HDA_AMP_MUTE : 0;
- snd_hda_codec_amp_stereo(codec, 0x0b, HDA_INPUT, 1, HDA_AMP_MUTE, bits);
-}
-
-static void alc880_uniwill_setup(struct hda_codec *codec)
-{
- struct alc_spec *spec = codec->spec;
-
- spec->autocfg.hp_pins[0] = 0x14;
- spec->autocfg.speaker_pins[0] = 0x15;
- spec->autocfg.speaker_pins[0] = 0x16;
-}
-
-static void alc880_uniwill_init_hook(struct hda_codec *codec)
-{
- alc_automute_amp(codec);
- alc88x_simple_mic_automute(codec);
-}
-
-static void alc880_uniwill_unsol_event(struct hda_codec *codec,
- unsigned int res)
-{
- /* Looks like the unsol event is incompatible with the standard
- * definition. 4bit tag is placed at 28 bit!
- */
- switch (res >> 28) {
- case ALC880_MIC_EVENT:
- alc88x_simple_mic_automute(codec);
- break;
- default:
- alc_automute_amp_unsol_event(codec, res);
- break;
- }
-}
-
-static void alc880_uniwill_p53_setup(struct hda_codec *codec)
-{
- struct alc_spec *spec = codec->spec;
-
- spec->autocfg.hp_pins[0] = 0x14;
- spec->autocfg.speaker_pins[0] = 0x15;
-}
-
-static void alc880_uniwill_p53_dcvol_automute(struct hda_codec *codec)
-{
- unsigned int present;
-
- present = snd_hda_codec_read(codec, 0x21, 0,
- AC_VERB_GET_VOLUME_KNOB_CONTROL, 0);
- present &= HDA_AMP_VOLMASK;
- snd_hda_codec_amp_stereo(codec, 0x0c, HDA_OUTPUT, 0,
- HDA_AMP_VOLMASK, present);
- snd_hda_codec_amp_stereo(codec, 0x0d, HDA_OUTPUT, 0,
- HDA_AMP_VOLMASK, present);
-}
-
-static void alc880_uniwill_p53_unsol_event(struct hda_codec *codec,
- unsigned int res)
-{
- /* Looks like the unsol event is incompatible with the standard
- * definition. 4bit tag is placed at 28 bit!
- */
- if ((res >> 28) == ALC880_DCVOL_EVENT)
- alc880_uniwill_p53_dcvol_automute(codec);
- else
- alc_automute_amp_unsol_event(codec, res);
-}
-
-/*
- * F1734 pin configuration:
- * HP = 0x14, speaker-out = 0x15, mic = 0x18
- */
-static struct hda_verb alc880_pin_f1734_init_verbs[] = {
- {0x07, AC_VERB_SET_CONNECT_SEL, 0x01},
- {0x10, AC_VERB_SET_CONNECT_SEL, 0x02},
- {0x11, AC_VERB_SET_CONNECT_SEL, 0x00},
- {0x12, AC_VERB_SET_CONNECT_SEL, 0x01},
- {0x13, AC_VERB_SET_CONNECT_SEL, 0x00},
-
- {0x14, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP},
- {0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
- {0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
- {0x15, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
-
- {0x18, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80},
- {0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
- {0x19, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF50},
- {0x19, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
- {0x1a, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
- {0x1a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
- {0x1b, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
- {0x1b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
- {0x1c, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN},
-
- {0x14, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN|ALC880_HP_EVENT},
- {0x21, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN|ALC880_DCVOL_EVENT},
-
- { }
-};
-
-/*
- * ASUS pin configuration:
- * HP/front = 0x14, surr = 0x15, clfe = 0x16, mic = 0x18, line = 0x1a
- */
-static struct hda_verb alc880_pin_asus_init_verbs[] = {
- {0x10, AC_VERB_SET_CONNECT_SEL, 0x02},
- {0x11, AC_VERB_SET_CONNECT_SEL, 0x00},
- {0x12, AC_VERB_SET_CONNECT_SEL, 0x01},
- {0x13, AC_VERB_SET_CONNECT_SEL, 0x00},
-
- {0x14, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP},
- {0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
- {0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
- {0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
- {0x16, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
- {0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
- {0x17, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
- {0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
-
- {0x18, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80},
- {0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
- {0x19, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80},
- {0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
- {0x1a, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN},
- {0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
- {0x1b, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
- {0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
- {0x1c, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN},
-
- { }
-};
-
-/* Enable GPIO mask and set output */
-#define alc880_gpio1_init_verbs alc_gpio1_init_verbs
-#define alc880_gpio2_init_verbs alc_gpio2_init_verbs
-#define alc880_gpio3_init_verbs alc_gpio3_init_verbs
-
-/* Clevo m520g init */
-static struct hda_verb alc880_pin_clevo_init_verbs[] = {
- /* headphone output */
- {0x11, AC_VERB_SET_CONNECT_SEL, 0x01},
- /* line-out */
- {0x14, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
- {0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
- /* Line-in */
- {0x1a, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN},
- {0x1a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
- /* CD */
- {0x1c, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN},
- {0x1c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
- /* Mic1 (rear panel) */
- {0x18, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80},
- {0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
- /* Mic2 (front panel) */
- {0x1b, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80},
- {0x1b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
- /* headphone */
- {0x19, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP},
- {0x19, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
- /* change to EAPD mode */
- {0x20, AC_VERB_SET_COEF_INDEX, 0x07},
- {0x20, AC_VERB_SET_PROC_COEF, 0x3060},
-
- { }
-};
-
-static struct hda_verb alc880_pin_tcl_S700_init_verbs[] = {
- /* change to EAPD mode */
- {0x20, AC_VERB_SET_COEF_INDEX, 0x07},
- {0x20, AC_VERB_SET_PROC_COEF, 0x3060},
-
- /* Headphone output */
- {0x14, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP},
- /* Front output*/
- {0x1b, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
- {0x1b, AC_VERB_SET_CONNECT_SEL, 0x00},
-
- /* Line In pin widget for input */
- {0x1a, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN},
- /* CD pin widget for input */
- {0x1c, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN},
- /* Mic1 (rear panel) pin widget for input and vref at 80% */
- {0x18, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80},
-
- /* change to EAPD mode */
- {0x20, AC_VERB_SET_COEF_INDEX, 0x07},
- {0x20, AC_VERB_SET_PROC_COEF, 0x3070},
-
- { }
-};
-
-/*
- * LG m1 express dual
- *
- * Pin assignment:
- * Rear Line-In/Out (blue): 0x14
- * Build-in Mic-In: 0x15
- * Speaker-out: 0x17
- * HP-Out (green): 0x1b
- * Mic-In/Out (red): 0x19
- * SPDIF-Out: 0x1e
- */
-
-/* To make 5.1 output working (green=Front, blue=Surr, red=CLFE) */
-static hda_nid_t alc880_lg_dac_nids[3] = {
- 0x05, 0x02, 0x03
-};
-
-/* seems analog CD is not working */
-static struct hda_input_mux alc880_lg_capture_source = {
- .num_items = 3,
- .items = {
- { "Mic", 0x1 },
- { "Line", 0x5 },
- { "Internal Mic", 0x6 },
- },
-};
-
-/* 2,4,6 channel modes */
-static struct hda_verb alc880_lg_ch2_init[] = {
- /* set line-in and mic-in to input */
- { 0x14, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN },
- { 0x19, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80 },
- { }
-};
-
-static struct hda_verb alc880_lg_ch4_init[] = {
- /* set line-in to out and mic-in to input */
- { 0x14, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP },
- { 0x19, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80 },
- { }
-};
-
-static struct hda_verb alc880_lg_ch6_init[] = {
- /* set line-in and mic-in to output */
- { 0x14, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP },
- { 0x19, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP },
- { }
-};
-
-static struct hda_channel_mode alc880_lg_ch_modes[3] = {
- { 2, alc880_lg_ch2_init },
- { 4, alc880_lg_ch4_init },
- { 6, alc880_lg_ch6_init },
-};
-
-static struct snd_kcontrol_new alc880_lg_mixer[] = {
- HDA_CODEC_VOLUME("Front Playback Volume", 0x0f, 0x0, HDA_OUTPUT),
- HDA_BIND_MUTE("Front Playback Switch", 0x0f, 2, HDA_INPUT),
- HDA_CODEC_VOLUME("Surround Playback Volume", 0x0c, 0x0, HDA_OUTPUT),
- HDA_BIND_MUTE("Surround Playback Switch", 0x0c, 2, HDA_INPUT),
- HDA_CODEC_VOLUME_MONO("Center Playback Volume", 0x0d, 1, 0x0, HDA_OUTPUT),
- HDA_CODEC_VOLUME_MONO("LFE Playback Volume", 0x0d, 2, 0x0, HDA_OUTPUT),
- HDA_BIND_MUTE_MONO("Center Playback Switch", 0x0d, 1, 2, HDA_INPUT),
- HDA_BIND_MUTE_MONO("LFE Playback Switch", 0x0d, 2, 2, HDA_INPUT),
- HDA_CODEC_VOLUME("Mic Playback Volume", 0x0b, 0x1, HDA_INPUT),
- HDA_CODEC_MUTE("Mic Playback Switch", 0x0b, 0x1, HDA_INPUT),
- HDA_CODEC_VOLUME("Line Playback Volume", 0x0b, 0x06, HDA_INPUT),
- HDA_CODEC_MUTE("Line Playback Switch", 0x0b, 0x06, HDA_INPUT),
- HDA_CODEC_VOLUME("Internal Mic Playback Volume", 0x0b, 0x07, HDA_INPUT),
- HDA_CODEC_MUTE("Internal Mic Playback Switch", 0x0b, 0x07, HDA_INPUT),
- {
- .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
- .name = "Channel Mode",
- .info = alc_ch_mode_info,
- .get = alc_ch_mode_get,
- .put = alc_ch_mode_put,
- },
- { } /* end */
-};
-
-static struct hda_verb alc880_lg_init_verbs[] = {
- /* set capture source to mic-in */
- {0x07, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
- {0x08, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
- {0x09, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
- /* mute all amp mixer inputs */
- {0x0b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(5)},
- {0x0b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(6)},
- {0x0b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(7)},
- /* line-in to input */
- {0x14, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN},
- {0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
- /* built-in mic */
- {0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80},
- {0x15, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
- /* speaker-out */
- {0x17, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP},
- {0x17, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
- /* mic-in to input */
- {0x11, AC_VERB_SET_CONNECT_SEL, 0x01},
- {0x19, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80},
- {0x19, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
- /* HP-out */
- {0x13, AC_VERB_SET_CONNECT_SEL, 0x03},
- {0x1b, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP},
- {0x1b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
- /* jack sense */
- {0x1b, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | ALC880_HP_EVENT},
- { }
-};
-
-/* toggle speaker-output according to the hp-jack state */
-static void alc880_lg_setup(struct hda_codec *codec)
-{
- struct alc_spec *spec = codec->spec;
-
- spec->autocfg.hp_pins[0] = 0x1b;
- spec->autocfg.speaker_pins[0] = 0x17;
-}
-
-/*
- * LG LW20
- *
- * Pin assignment:
- * Speaker-out: 0x14
- * Mic-In: 0x18
- * Built-in Mic-In: 0x19
- * Line-In: 0x1b
- * HP-Out: 0x1a
- * SPDIF-Out: 0x1e
- */
-
-static struct hda_input_mux alc880_lg_lw_capture_source = {
- .num_items = 3,
- .items = {
- { "Mic", 0x0 },
- { "Internal Mic", 0x1 },
- { "Line In", 0x2 },
- },
-};
-
-#define alc880_lg_lw_modes alc880_threestack_modes
-
-static struct snd_kcontrol_new alc880_lg_lw_mixer[] = {
- HDA_CODEC_VOLUME("Front Playback Volume", 0x0c, 0x0, HDA_OUTPUT),
- HDA_BIND_MUTE("Front Playback Switch", 0x0c, 2, HDA_INPUT),
- HDA_CODEC_VOLUME("Surround Playback Volume", 0x0f, 0x0, HDA_OUTPUT),
- HDA_BIND_MUTE("Surround Playback Switch", 0x0f, 2, HDA_INPUT),
- HDA_CODEC_VOLUME_MONO("Center Playback Volume", 0x0e, 1, 0x0, HDA_OUTPUT),
- HDA_CODEC_VOLUME_MONO("LFE Playback Volume", 0x0e, 2, 0x0, HDA_OUTPUT),
- HDA_BIND_MUTE_MONO("Center Playback Switch", 0x0e, 1, 2, HDA_INPUT),
- HDA_BIND_MUTE_MONO("LFE Playback Switch", 0x0e, 2, 2, HDA_INPUT),
- HDA_CODEC_VOLUME("Line Playback Volume", 0x0b, 0x02, HDA_INPUT),
- HDA_CODEC_MUTE("Line Playback Switch", 0x0b, 0x02, HDA_INPUT),
- HDA_CODEC_VOLUME("Mic Playback Volume", 0x0b, 0x0, HDA_INPUT),
- HDA_CODEC_MUTE("Mic Playback Switch", 0x0b, 0x0, HDA_INPUT),
- HDA_CODEC_VOLUME("Internal Mic Playback Volume", 0x0b, 0x01, HDA_INPUT),
- HDA_CODEC_MUTE("Internal Mic Playback Switch", 0x0b, 0x01, HDA_INPUT),
- {
- .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
- .name = "Channel Mode",
- .info = alc_ch_mode_info,
- .get = alc_ch_mode_get,
- .put = alc_ch_mode_put,
- },
- { } /* end */
-};
-
-static struct hda_verb alc880_lg_lw_init_verbs[] = {
- {0x13, AC_VERB_SET_CONNECT_SEL, 0x00}, /* HP */
- {0x10, AC_VERB_SET_CONNECT_SEL, 0x02}, /* mic/clfe */
- {0x12, AC_VERB_SET_CONNECT_SEL, 0x03}, /* line/surround */
-
- /* set capture source to mic-in */
- {0x07, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
- {0x08, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
- {0x09, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
- {0x0b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(7)},
- /* speaker-out */
- {0x14, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP},
- {0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
- /* HP-out */
- {0x1b, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP},
- {0x1b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
- /* mic-in to input */
- {0x18, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80},
- {0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
- /* built-in mic */
- {0x19, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80},
- {0x19, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
- /* jack sense */
- {0x1b, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | ALC880_HP_EVENT},
- { }
-};
-
-/* toggle speaker-output according to the hp-jack state */
-static void alc880_lg_lw_setup(struct hda_codec *codec)
-{
- struct alc_spec *spec = codec->spec;
-
- spec->autocfg.hp_pins[0] = 0x1b;
- spec->autocfg.speaker_pins[0] = 0x14;
-}
-
-static struct snd_kcontrol_new alc880_medion_rim_mixer[] = {
- HDA_CODEC_VOLUME("Master Playback Volume", 0x0c, 0x0, HDA_OUTPUT),
- HDA_BIND_MUTE("Master Playback Switch", 0x0c, 2, HDA_INPUT),
- HDA_CODEC_VOLUME("Mic Playback Volume", 0x0b, 0x0, HDA_INPUT),
- HDA_CODEC_MUTE("Mic Playback Switch", 0x0b, 0x0, HDA_INPUT),
- HDA_CODEC_VOLUME("Internal Mic Playback Volume", 0x0b, 0x1, HDA_INPUT),
- HDA_CODEC_MUTE("Internal Playback Switch", 0x0b, 0x1, HDA_INPUT),
- { } /* end */
-};
-
-static struct hda_input_mux alc880_medion_rim_capture_source = {
- .num_items = 2,
- .items = {
- { "Mic", 0x0 },
- { "Internal Mic", 0x1 },
- },
-};
-
-static struct hda_verb alc880_medion_rim_init_verbs[] = {
- {0x13, AC_VERB_SET_CONNECT_SEL, 0x00}, /* HP */
-
- {0x14, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP},
- {0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
-
- /* Mic1 (rear panel) pin widget for input and vref at 80% */
- {0x18, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80},
- {0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
- /* Mic2 (as headphone out) for HP output */
- {0x19, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP},
- {0x19, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
- /* Internal Speaker */
- {0x1b, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
- {0x1b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
-
- {0x20, AC_VERB_SET_COEF_INDEX, 0x07},
- {0x20, AC_VERB_SET_PROC_COEF, 0x3060},
-
- {0x14, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | ALC880_HP_EVENT},
- { }
-};
-
-/* toggle speaker-output according to the hp-jack state */
-static void alc880_medion_rim_automute(struct hda_codec *codec)
-{
- struct alc_spec *spec = codec->spec;
- alc_automute_amp(codec);
- /* toggle EAPD */
- if (spec->jack_present)
- snd_hda_codec_write(codec, 0x01, 0, AC_VERB_SET_GPIO_DATA, 0);
- else
- snd_hda_codec_write(codec, 0x01, 0, AC_VERB_SET_GPIO_DATA, 2);
-}
-
-static void alc880_medion_rim_unsol_event(struct hda_codec *codec,
- unsigned int res)
-{
- /* Looks like the unsol event is incompatible with the standard
- * definition. 4bit tag is placed at 28 bit!
- */
- if ((res >> 28) == ALC880_HP_EVENT)
- alc880_medion_rim_automute(codec);
-}
-
-static void alc880_medion_rim_setup(struct hda_codec *codec)
-{
- struct alc_spec *spec = codec->spec;
-
- spec->autocfg.hp_pins[0] = 0x14;
- spec->autocfg.speaker_pins[0] = 0x1b;
-}
-
-#ifdef CONFIG_SND_HDA_POWER_SAVE
-static struct hda_amp_list alc880_loopbacks[] = {
- { 0x0b, HDA_INPUT, 0 },
- { 0x0b, HDA_INPUT, 1 },
- { 0x0b, HDA_INPUT, 2 },
- { 0x0b, HDA_INPUT, 3 },
- { 0x0b, HDA_INPUT, 4 },
- { } /* end */
-};
-
-static struct hda_amp_list alc880_lg_loopbacks[] = {
- { 0x0b, HDA_INPUT, 1 },
- { 0x0b, HDA_INPUT, 6 },
- { 0x0b, HDA_INPUT, 7 },
- { } /* end */
-};
-#endif
-
-/*
* Common callbacks
*/
-static void alc_init_special_input_src(struct hda_codec *codec);
-
static int alc_init(struct hda_codec *codec)
{
struct alc_spec *spec = codec->spec;
- unsigned int i;
-
- alc_fix_pll(codec);
- alc_auto_init_amp(codec, spec->init_amp);
-
- for (i = 0; i < spec->num_init_verbs; i++)
- snd_hda_sequence_write(codec, spec->init_verbs[i]);
- alc_init_special_input_src(codec);
if (spec->init_hook)
spec->init_hook(codec);
- alc_apply_fixup(codec, ALC_FIXUP_ACT_INIT);
-
- hda_call_check_power_status(codec, 0x01);
- return 0;
-}
-
-static void alc_unsol_event(struct hda_codec *codec, unsigned int res)
-{
- struct alc_spec *spec = codec->spec;
-
- if (spec->unsol_event)
- spec->unsol_event(codec, res);
-}
-
-#ifdef CONFIG_SND_HDA_POWER_SAVE
-static int alc_check_power_status(struct hda_codec *codec, hda_nid_t nid)
-{
- struct alc_spec *spec = codec->spec;
- return snd_hda_check_amp_list_power(codec, &spec->loopback, nid);
-}
-#endif
-
-/*
- * Analog playback callbacks
- */
-static int alc880_playback_pcm_open(struct hda_pcm_stream *hinfo,
- struct hda_codec *codec,
- struct snd_pcm_substream *substream)
-{
- struct alc_spec *spec = codec->spec;
- return snd_hda_multi_out_analog_open(codec, &spec->multiout, substream,
- hinfo);
-}
-
-static int alc880_playback_pcm_prepare(struct hda_pcm_stream *hinfo,
- struct hda_codec *codec,
- unsigned int stream_tag,
- unsigned int format,
- struct snd_pcm_substream *substream)
-{
- struct alc_spec *spec = codec->spec;
- return snd_hda_multi_out_analog_prepare(codec, &spec->multiout,
- stream_tag, format, substream);
-}
-
-static int alc880_playback_pcm_cleanup(struct hda_pcm_stream *hinfo,
- struct hda_codec *codec,
- struct snd_pcm_substream *substream)
-{
- struct alc_spec *spec = codec->spec;
- return snd_hda_multi_out_analog_cleanup(codec, &spec->multiout);
-}
-
-/*
- * Digital out
- */
-static int alc880_dig_playback_pcm_open(struct hda_pcm_stream *hinfo,
- struct hda_codec *codec,
- struct snd_pcm_substream *substream)
-{
- struct alc_spec *spec = codec->spec;
- return snd_hda_multi_out_dig_open(codec, &spec->multiout);
-}
-
-static int alc880_dig_playback_pcm_prepare(struct hda_pcm_stream *hinfo,
- struct hda_codec *codec,
- unsigned int stream_tag,
- unsigned int format,
- struct snd_pcm_substream *substream)
-{
- struct alc_spec *spec = codec->spec;
- return snd_hda_multi_out_dig_prepare(codec, &spec->multiout,
- stream_tag, format, substream);
-}
-
-static int alc880_dig_playback_pcm_cleanup(struct hda_pcm_stream *hinfo,
- struct hda_codec *codec,
- struct snd_pcm_substream *substream)
-{
- struct alc_spec *spec = codec->spec;
- return snd_hda_multi_out_dig_cleanup(codec, &spec->multiout);
-}
-
-static int alc880_dig_playback_pcm_close(struct hda_pcm_stream *hinfo,
- struct hda_codec *codec,
- struct snd_pcm_substream *substream)
-{
- struct alc_spec *spec = codec->spec;
- return snd_hda_multi_out_dig_close(codec, &spec->multiout);
-}
-
-/*
- * Analog capture
- */
-static int alc880_alt_capture_pcm_prepare(struct hda_pcm_stream *hinfo,
- struct hda_codec *codec,
- unsigned int stream_tag,
- unsigned int format,
- struct snd_pcm_substream *substream)
-{
- struct alc_spec *spec = codec->spec;
-
- snd_hda_codec_setup_stream(codec, spec->adc_nids[substream->number + 1],
- stream_tag, 0, format);
- return 0;
-}
-
-static int alc880_alt_capture_pcm_cleanup(struct hda_pcm_stream *hinfo,
- struct hda_codec *codec,
- struct snd_pcm_substream *substream)
-{
- struct alc_spec *spec = codec->spec;
-
- snd_hda_codec_cleanup_stream(codec,
- spec->adc_nids[substream->number + 1]);
- return 0;
-}
-
-/* analog capture with dynamic dual-adc changes */
-static int dualmic_capture_pcm_prepare(struct hda_pcm_stream *hinfo,
- struct hda_codec *codec,
- unsigned int stream_tag,
- unsigned int format,
- struct snd_pcm_substream *substream)
-{
- struct alc_spec *spec = codec->spec;
- spec->cur_adc = spec->adc_nids[spec->cur_adc_idx];
- spec->cur_adc_stream_tag = stream_tag;
- spec->cur_adc_format = format;
- snd_hda_codec_setup_stream(codec, spec->cur_adc, stream_tag, 0, format);
- return 0;
-}
-
-static int dualmic_capture_pcm_cleanup(struct hda_pcm_stream *hinfo,
- struct hda_codec *codec,
- struct snd_pcm_substream *substream)
-{
- struct alc_spec *spec = codec->spec;
- snd_hda_codec_cleanup_stream(codec, spec->cur_adc);
- spec->cur_adc = 0;
- return 0;
-}
-
-static struct hda_pcm_stream dualmic_pcm_analog_capture = {
- .substreams = 1,
- .channels_min = 2,
- .channels_max = 2,
- .nid = 0, /* fill later */
- .ops = {
- .prepare = dualmic_capture_pcm_prepare,
- .cleanup = dualmic_capture_pcm_cleanup
- },
-};
-
-/*
- */
-static struct hda_pcm_stream alc880_pcm_analog_playback = {
- .substreams = 1,
- .channels_min = 2,
- .channels_max = 8,
- /* NID is set in alc_build_pcms */
- .ops = {
- .open = alc880_playback_pcm_open,
- .prepare = alc880_playback_pcm_prepare,
- .cleanup = alc880_playback_pcm_cleanup
- },
-};
-
-static struct hda_pcm_stream alc880_pcm_analog_capture = {
- .substreams = 1,
- .channels_min = 2,
- .channels_max = 2,
- /* NID is set in alc_build_pcms */
-};
-
-static struct hda_pcm_stream alc880_pcm_analog_alt_playback = {
- .substreams = 1,
- .channels_min = 2,
- .channels_max = 2,
- /* NID is set in alc_build_pcms */
-};
-
-static struct hda_pcm_stream alc880_pcm_analog_alt_capture = {
- .substreams = 2, /* can be overridden */
- .channels_min = 2,
- .channels_max = 2,
- /* NID is set in alc_build_pcms */
- .ops = {
- .prepare = alc880_alt_capture_pcm_prepare,
- .cleanup = alc880_alt_capture_pcm_cleanup
- },
-};
-
-static struct hda_pcm_stream alc880_pcm_digital_playback = {
- .substreams = 1,
- .channels_min = 2,
- .channels_max = 2,
- /* NID is set in alc_build_pcms */
- .ops = {
- .open = alc880_dig_playback_pcm_open,
- .close = alc880_dig_playback_pcm_close,
- .prepare = alc880_dig_playback_pcm_prepare,
- .cleanup = alc880_dig_playback_pcm_cleanup
- },
-};
-
-static struct hda_pcm_stream alc880_pcm_digital_capture = {
- .substreams = 1,
- .channels_min = 2,
- .channels_max = 2,
- /* NID is set in alc_build_pcms */
-};
-
-/* Used by alc_build_pcms to flag that a PCM has no playback stream */
-static struct hda_pcm_stream alc_pcm_null_stream = {
- .substreams = 0,
- .channels_min = 0,
- .channels_max = 0,
-};
-
-static int alc_build_pcms(struct hda_codec *codec)
-{
- struct alc_spec *spec = codec->spec;
- struct hda_pcm *info = spec->pcm_rec;
- int i;
-
- codec->num_pcms = 1;
- codec->pcm_info = info;
-
- if (spec->no_analog)
- goto skip_analog;
-
- snprintf(spec->stream_name_analog, sizeof(spec->stream_name_analog),
- "%s Analog", codec->chip_name);
- info->name = spec->stream_name_analog;
-
- if (spec->stream_analog_playback) {
- if (snd_BUG_ON(!spec->multiout.dac_nids))
- return -EINVAL;
- info->stream[SNDRV_PCM_STREAM_PLAYBACK] = *(spec->stream_analog_playback);
- info->stream[SNDRV_PCM_STREAM_PLAYBACK].nid = spec->multiout.dac_nids[0];
- }
- if (spec->stream_analog_capture) {
- if (snd_BUG_ON(!spec->adc_nids))
- return -EINVAL;
- info->stream[SNDRV_PCM_STREAM_CAPTURE] = *(spec->stream_analog_capture);
- info->stream[SNDRV_PCM_STREAM_CAPTURE].nid = spec->adc_nids[0];
- }
-
- if (spec->channel_mode) {
- info->stream[SNDRV_PCM_STREAM_PLAYBACK].channels_max = 0;
- for (i = 0; i < spec->num_channel_mode; i++) {
- if (spec->channel_mode[i].channels > info->stream[SNDRV_PCM_STREAM_PLAYBACK].channels_max) {
- info->stream[SNDRV_PCM_STREAM_PLAYBACK].channels_max = spec->channel_mode[i].channels;
- }
- }
- }
-
- skip_analog:
- /* SPDIF for stream index #1 */
- if (spec->multiout.dig_out_nid || spec->dig_in_nid) {
- snprintf(spec->stream_name_digital,
- sizeof(spec->stream_name_digital),
- "%s Digital", codec->chip_name);
- codec->num_pcms = 2;
- codec->slave_dig_outs = spec->multiout.slave_dig_outs;
- info = spec->pcm_rec + 1;
- info->name = spec->stream_name_digital;
- if (spec->dig_out_type)
- info->pcm_type = spec->dig_out_type;
- else
- info->pcm_type = HDA_PCM_TYPE_SPDIF;
- if (spec->multiout.dig_out_nid &&
- spec->stream_digital_playback) {
- info->stream[SNDRV_PCM_STREAM_PLAYBACK] = *(spec->stream_digital_playback);
- info->stream[SNDRV_PCM_STREAM_PLAYBACK].nid = spec->multiout.dig_out_nid;
- }
- if (spec->dig_in_nid &&
- spec->stream_digital_capture) {
- info->stream[SNDRV_PCM_STREAM_CAPTURE] = *(spec->stream_digital_capture);
- info->stream[SNDRV_PCM_STREAM_CAPTURE].nid = spec->dig_in_nid;
- }
- /* FIXME: do we need this for all Realtek codec models? */
- codec->spdif_status_reset = 1;
- }
+ alc_fix_pll(codec);
+ alc_auto_init_amp(codec, spec->init_amp);
- if (spec->no_analog)
- return 0;
+ snd_hda_gen_init(codec);
- /* If the use of more than one ADC is requested for the current
- * model, configure a second analog capture-only PCM.
- */
- /* Additional Analaog capture for index #2 */
- if ((spec->alt_dac_nid && spec->stream_analog_alt_playback) ||
- (spec->num_adc_nids > 1 && spec->stream_analog_alt_capture)) {
- codec->num_pcms = 3;
- info = spec->pcm_rec + 2;
- info->name = spec->stream_name_analog;
- if (spec->alt_dac_nid) {
- info->stream[SNDRV_PCM_STREAM_PLAYBACK] =
- *spec->stream_analog_alt_playback;
- info->stream[SNDRV_PCM_STREAM_PLAYBACK].nid =
- spec->alt_dac_nid;
- } else {
- info->stream[SNDRV_PCM_STREAM_PLAYBACK] =
- alc_pcm_null_stream;
- info->stream[SNDRV_PCM_STREAM_PLAYBACK].nid = 0;
- }
- if (spec->num_adc_nids > 1) {
- info->stream[SNDRV_PCM_STREAM_CAPTURE] =
- *spec->stream_analog_alt_capture;
- info->stream[SNDRV_PCM_STREAM_CAPTURE].nid =
- spec->adc_nids[1];
- info->stream[SNDRV_PCM_STREAM_CAPTURE].substreams =
- spec->num_adc_nids - 1;
- } else {
- info->stream[SNDRV_PCM_STREAM_CAPTURE] =
- alc_pcm_null_stream;
- info->stream[SNDRV_PCM_STREAM_CAPTURE].nid = 0;
- }
- }
+ snd_hda_apply_fixup(codec, HDA_FIXUP_ACT_INIT);
return 0;
}
static inline void alc_shutup(struct hda_codec *codec)
{
- snd_hda_shutup_pins(codec);
-}
-
-static void alc_free_kctls(struct hda_codec *codec)
-{
struct alc_spec *spec = codec->spec;
- if (spec->kctls.list) {
- struct snd_kcontrol_new *kctl = spec->kctls.list;
- int i;
- for (i = 0; i < spec->kctls.used; i++)
- kfree(kctl[i].name);
- }
- snd_array_free(&spec->kctls);
+ if (spec && spec->shutup)
+ spec->shutup(codec);
+ else
+ snd_hda_shutup_pins(codec);
}
-static void alc_free(struct hda_codec *codec)
-{
- struct alc_spec *spec = codec->spec;
-
- if (!spec)
- return;
+#define alc_free snd_hda_gen_free
- alc_shutup(codec);
- snd_hda_input_jack_free(codec);
- alc_free_kctls(codec);
- kfree(spec);
- snd_hda_detach_beep_device(codec);
-}
-
-#ifdef CONFIG_SND_HDA_POWER_SAVE
+#ifdef CONFIG_PM
static void alc_power_eapd(struct hda_codec *codec)
{
- /* We currently only handle front, HP */
- switch (codec->vendor_id) {
- case 0x10ec0260:
- set_eapd(codec, 0x0f, 0);
- set_eapd(codec, 0x10, 0);
- break;
- case 0x10ec0262:
- case 0x10ec0267:
- case 0x10ec0268:
- case 0x10ec0269:
- case 0x10ec0270:
- case 0x10ec0272:
- case 0x10ec0660:
- case 0x10ec0662:
- case 0x10ec0663:
- case 0x10ec0665:
- case 0x10ec0862:
- case 0x10ec0889:
- set_eapd(codec, 0x14, 0);
- set_eapd(codec, 0x15, 0);
- break;
- }
+ alc_auto_setup_eapd(codec, false);
}
-static int alc_suspend(struct hda_codec *codec, pm_message_t state)
+static int alc_suspend(struct hda_codec *codec)
{
struct alc_spec *spec = codec->spec;
alc_shutup(codec);
@@ -4260,12 +866,17 @@ static int alc_suspend(struct hda_codec *codec, pm_message_t state)
}
#endif
-#ifdef SND_HDA_NEEDS_RESUME
+#ifdef CONFIG_PM
static int alc_resume(struct hda_codec *codec)
{
+ struct alc_spec *spec = codec->spec;
+
+ if (!spec->no_depop_delay)
+ msleep(150); /* to avoid pop noise */
codec->patch_ops.init(codec);
snd_hda_codec_resume_amp(codec);
snd_hda_codec_resume_cache(codec);
+ alc_inv_dmic_sync(codec, true);
hda_call_check_power_status(codec, 0x01);
return 0;
}
@@ -4273,22 +884,21 @@ static int alc_resume(struct hda_codec *codec)
/*
*/
-static struct hda_codec_ops alc_patch_ops = {
+static const struct hda_codec_ops alc_patch_ops = {
.build_controls = alc_build_controls,
- .build_pcms = alc_build_pcms,
+ .build_pcms = snd_hda_gen_build_pcms,
.init = alc_init,
.free = alc_free,
- .unsol_event = alc_unsol_event,
-#ifdef SND_HDA_NEEDS_RESUME
+ .unsol_event = snd_hda_jack_unsol_event,
+#ifdef CONFIG_PM
.resume = alc_resume,
-#endif
-#ifdef CONFIG_SND_HDA_POWER_SAVE
.suspend = alc_suspend,
- .check_power_status = alc_check_power_status,
+ .check_power_status = snd_hda_gen_check_power_status,
#endif
.reboot_notify = alc_shutup,
};
+
/* replace the codec chip_name with the given string */
static int alc_codec_rename(struct hda_codec *codec, const char *name)
{
@@ -4302,1349 +912,112 @@ static int alc_codec_rename(struct hda_codec *codec, const char *name)
}
/*
- * Test configuration for debugging
- *
- * Almost all inputs/outputs are enabled. I/O pins can be configured via
- * enum controls.
+ * Rename codecs appropriately from COEF value or subvendor id
*/
-#ifdef CONFIG_SND_DEBUG
-static hda_nid_t alc880_test_dac_nids[4] = {
- 0x02, 0x03, 0x04, 0x05
-};
-
-static struct hda_input_mux alc880_test_capture_source = {
- .num_items = 7,
- .items = {
- { "In-1", 0x0 },
- { "In-2", 0x1 },
- { "In-3", 0x2 },
- { "In-4", 0x3 },
- { "CD", 0x4 },
- { "Front", 0x5 },
- { "Surround", 0x6 },
- },
-};
-
-static struct hda_channel_mode alc880_test_modes[4] = {
- { 2, NULL },
- { 4, NULL },
- { 6, NULL },
- { 8, NULL },
-};
-
-static int alc_test_pin_ctl_info(struct snd_kcontrol *kcontrol,
- struct snd_ctl_elem_info *uinfo)
-{
- static char *texts[] = {
- "N/A", "Line Out", "HP Out",
- "In Hi-Z", "In 50%", "In Grd", "In 80%", "In 100%"
- };
- uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED;
- uinfo->count = 1;
- uinfo->value.enumerated.items = 8;
- if (uinfo->value.enumerated.item >= 8)
- uinfo->value.enumerated.item = 7;
- strcpy(uinfo->value.enumerated.name, texts[uinfo->value.enumerated.item]);
- return 0;
-}
-
-static int alc_test_pin_ctl_get(struct snd_kcontrol *kcontrol,
- struct snd_ctl_elem_value *ucontrol)
-{
- struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
- hda_nid_t nid = (hda_nid_t)kcontrol->private_value;
- unsigned int pin_ctl, item = 0;
-
- pin_ctl = snd_hda_codec_read(codec, nid, 0,
- AC_VERB_GET_PIN_WIDGET_CONTROL, 0);
- if (pin_ctl & AC_PINCTL_OUT_EN) {
- if (pin_ctl & AC_PINCTL_HP_EN)
- item = 2;
- else
- item = 1;
- } else if (pin_ctl & AC_PINCTL_IN_EN) {
- switch (pin_ctl & AC_PINCTL_VREFEN) {
- case AC_PINCTL_VREF_HIZ: item = 3; break;
- case AC_PINCTL_VREF_50: item = 4; break;
- case AC_PINCTL_VREF_GRD: item = 5; break;
- case AC_PINCTL_VREF_80: item = 6; break;
- case AC_PINCTL_VREF_100: item = 7; break;
- }
- }
- ucontrol->value.enumerated.item[0] = item;
- return 0;
-}
-
-static int alc_test_pin_ctl_put(struct snd_kcontrol *kcontrol,
- struct snd_ctl_elem_value *ucontrol)
-{
- struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
- hda_nid_t nid = (hda_nid_t)kcontrol->private_value;
- static unsigned int ctls[] = {
- 0, AC_PINCTL_OUT_EN, AC_PINCTL_OUT_EN | AC_PINCTL_HP_EN,
- AC_PINCTL_IN_EN | AC_PINCTL_VREF_HIZ,
- AC_PINCTL_IN_EN | AC_PINCTL_VREF_50,
- AC_PINCTL_IN_EN | AC_PINCTL_VREF_GRD,
- AC_PINCTL_IN_EN | AC_PINCTL_VREF_80,
- AC_PINCTL_IN_EN | AC_PINCTL_VREF_100,
- };
- unsigned int old_ctl, new_ctl;
-
- old_ctl = snd_hda_codec_read(codec, nid, 0,
- AC_VERB_GET_PIN_WIDGET_CONTROL, 0);
- new_ctl = ctls[ucontrol->value.enumerated.item[0]];
- if (old_ctl != new_ctl) {
- int val;
- snd_hda_codec_write_cache(codec, nid, 0,
- AC_VERB_SET_PIN_WIDGET_CONTROL,
- new_ctl);
- val = ucontrol->value.enumerated.item[0] >= 3 ?
- HDA_AMP_MUTE : 0;
- snd_hda_codec_amp_stereo(codec, nid, HDA_OUTPUT, 0,
- HDA_AMP_MUTE, val);
- return 1;
- }
- return 0;
-}
-
-static int alc_test_pin_src_info(struct snd_kcontrol *kcontrol,
- struct snd_ctl_elem_info *uinfo)
-{
- static char *texts[] = {
- "Front", "Surround", "CLFE", "Side"
- };
- uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED;
- uinfo->count = 1;
- uinfo->value.enumerated.items = 4;
- if (uinfo->value.enumerated.item >= 4)
- uinfo->value.enumerated.item = 3;
- strcpy(uinfo->value.enumerated.name, texts[uinfo->value.enumerated.item]);
- return 0;
-}
-
-static int alc_test_pin_src_get(struct snd_kcontrol *kcontrol,
- struct snd_ctl_elem_value *ucontrol)
-{
- struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
- hda_nid_t nid = (hda_nid_t)kcontrol->private_value;
- unsigned int sel;
-
- sel = snd_hda_codec_read(codec, nid, 0, AC_VERB_GET_CONNECT_SEL, 0);
- ucontrol->value.enumerated.item[0] = sel & 3;
- return 0;
-}
-
-static int alc_test_pin_src_put(struct snd_kcontrol *kcontrol,
- struct snd_ctl_elem_value *ucontrol)
-{
- struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
- hda_nid_t nid = (hda_nid_t)kcontrol->private_value;
- unsigned int sel;
-
- sel = snd_hda_codec_read(codec, nid, 0, AC_VERB_GET_CONNECT_SEL, 0) & 3;
- if (ucontrol->value.enumerated.item[0] != sel) {
- sel = ucontrol->value.enumerated.item[0] & 3;
- snd_hda_codec_write_cache(codec, nid, 0,
- AC_VERB_SET_CONNECT_SEL, sel);
- return 1;
- }
- return 0;
-}
-
-#define PIN_CTL_TEST(xname,nid) { \
- .iface = SNDRV_CTL_ELEM_IFACE_MIXER, \
- .name = xname, \
- .subdevice = HDA_SUBDEV_NID_FLAG | nid, \
- .info = alc_test_pin_ctl_info, \
- .get = alc_test_pin_ctl_get, \
- .put = alc_test_pin_ctl_put, \
- .private_value = nid \
- }
-
-#define PIN_SRC_TEST(xname,nid) { \
- .iface = SNDRV_CTL_ELEM_IFACE_MIXER, \
- .name = xname, \
- .subdevice = HDA_SUBDEV_NID_FLAG | nid, \
- .info = alc_test_pin_src_info, \
- .get = alc_test_pin_src_get, \
- .put = alc_test_pin_src_put, \
- .private_value = nid \
- }
-
-static struct snd_kcontrol_new alc880_test_mixer[] = {
- HDA_CODEC_VOLUME("Front Playback Volume", 0x0c, 0x0, HDA_OUTPUT),
- HDA_CODEC_VOLUME("Surround Playback Volume", 0x0d, 0x0, HDA_OUTPUT),
- HDA_CODEC_VOLUME("CLFE Playback Volume", 0x0e, 0x0, HDA_OUTPUT),
- HDA_CODEC_VOLUME("Side Playback Volume", 0x0f, 0x0, HDA_OUTPUT),
- HDA_BIND_MUTE("Front Playback Switch", 0x0c, 2, HDA_INPUT),
- HDA_BIND_MUTE("Surround Playback Switch", 0x0d, 2, HDA_INPUT),
- HDA_BIND_MUTE("CLFE Playback Switch", 0x0e, 2, HDA_INPUT),
- HDA_BIND_MUTE("Side Playback Switch", 0x0f, 2, HDA_INPUT),
- PIN_CTL_TEST("Front Pin Mode", 0x14),
- PIN_CTL_TEST("Surround Pin Mode", 0x15),
- PIN_CTL_TEST("CLFE Pin Mode", 0x16),
- PIN_CTL_TEST("Side Pin Mode", 0x17),
- PIN_CTL_TEST("In-1 Pin Mode", 0x18),
- PIN_CTL_TEST("In-2 Pin Mode", 0x19),
- PIN_CTL_TEST("In-3 Pin Mode", 0x1a),
- PIN_CTL_TEST("In-4 Pin Mode", 0x1b),
- PIN_SRC_TEST("In-1 Pin Source", 0x18),
- PIN_SRC_TEST("In-2 Pin Source", 0x19),
- PIN_SRC_TEST("In-3 Pin Source", 0x1a),
- PIN_SRC_TEST("In-4 Pin Source", 0x1b),
- HDA_CODEC_VOLUME("In-1 Playback Volume", 0x0b, 0x0, HDA_INPUT),
- HDA_CODEC_MUTE("In-1 Playback Switch", 0x0b, 0x0, HDA_INPUT),
- HDA_CODEC_VOLUME("In-2 Playback Volume", 0x0b, 0x1, HDA_INPUT),
- HDA_CODEC_MUTE("In-2 Playback Switch", 0x0b, 0x1, HDA_INPUT),
- HDA_CODEC_VOLUME("In-3 Playback Volume", 0x0b, 0x2, HDA_INPUT),
- HDA_CODEC_MUTE("In-3 Playback Switch", 0x0b, 0x2, HDA_INPUT),
- HDA_CODEC_VOLUME("In-4 Playback Volume", 0x0b, 0x3, HDA_INPUT),
- HDA_CODEC_MUTE("In-4 Playback Switch", 0x0b, 0x3, HDA_INPUT),
- HDA_CODEC_VOLUME("CD Playback Volume", 0x0b, 0x4, HDA_INPUT),
- HDA_CODEC_MUTE("CD Playback Switch", 0x0b, 0x4, HDA_INPUT),
- {
- .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
- .name = "Channel Mode",
- .info = alc_ch_mode_info,
- .get = alc_ch_mode_get,
- .put = alc_ch_mode_put,
- },
- { } /* end */
-};
-
-static struct hda_verb alc880_test_init_verbs[] = {
- /* Unmute inputs of 0x0c - 0x0f */
- {0x0c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
- {0x0c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
- {0x0d, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
- {0x0d, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
- {0x0e, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
- {0x0e, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
- {0x0f, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
- {0x0f, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
- /* Vol output for 0x0c-0x0f */
- {0x0c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
- {0x0d, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
- {0x0e, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
- {0x0f, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
- /* Set output pins 0x14-0x17 */
- {0x14, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
- {0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
- {0x16, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
- {0x17, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
- /* Unmute output pins 0x14-0x17 */
- {0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
- {0x15, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
- {0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
- {0x17, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
- /* Set input pins 0x18-0x1c */
- {0x18, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80},
- {0x19, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80},
- {0x1a, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN},
- {0x1b, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN},
- {0x1c, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN},
- /* Mute input pins 0x18-0x1b */
- {0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
- {0x19, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
- {0x1a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
- {0x1b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
- /* ADC set up */
- {0x07, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
- {0x07, AC_VERB_SET_CONNECT_SEL, 0x00},
- {0x08, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
- {0x08, AC_VERB_SET_CONNECT_SEL, 0x00},
- {0x09, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
- {0x09, AC_VERB_SET_CONNECT_SEL, 0x00},
- /* Analog input/passthru */
- {0x0b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
- {0x0b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
- {0x0b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(2)},
- {0x0b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(3)},
- {0x0b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(4)},
- { }
-};
-#endif
-
-/*
- */
-
-static const char * const alc880_models[ALC880_MODEL_LAST] = {
- [ALC880_3ST] = "3stack",
- [ALC880_TCL_S700] = "tcl",
- [ALC880_3ST_DIG] = "3stack-digout",
- [ALC880_CLEVO] = "clevo",
- [ALC880_5ST] = "5stack",
- [ALC880_5ST_DIG] = "5stack-digout",
- [ALC880_W810] = "w810",
- [ALC880_Z71V] = "z71v",
- [ALC880_6ST] = "6stack",
- [ALC880_6ST_DIG] = "6stack-digout",
- [ALC880_ASUS] = "asus",
- [ALC880_ASUS_W1V] = "asus-w1v",
- [ALC880_ASUS_DIG] = "asus-dig",
- [ALC880_ASUS_DIG2] = "asus-dig2",
- [ALC880_UNIWILL_DIG] = "uniwill",
- [ALC880_UNIWILL_P53] = "uniwill-p53",
- [ALC880_FUJITSU] = "fujitsu",
- [ALC880_F1734] = "F1734",
- [ALC880_LG] = "lg",
- [ALC880_LG_LW] = "lg-lw",
- [ALC880_MEDION_RIM] = "medion",
-#ifdef CONFIG_SND_DEBUG
- [ALC880_TEST] = "test",
-#endif
- [ALC880_AUTO] = "auto",
-};
-
-static struct snd_pci_quirk alc880_cfg_tbl[] = {
- SND_PCI_QUIRK(0x1019, 0x0f69, "Coeus G610P", ALC880_W810),
- SND_PCI_QUIRK(0x1019, 0xa880, "ECS", ALC880_5ST_DIG),
- SND_PCI_QUIRK(0x1019, 0xa884, "Acer APFV", ALC880_6ST),
- SND_PCI_QUIRK(0x1025, 0x0070, "ULI", ALC880_3ST_DIG),
- SND_PCI_QUIRK(0x1025, 0x0077, "ULI", ALC880_6ST_DIG),
- SND_PCI_QUIRK(0x1025, 0x0078, "ULI", ALC880_6ST_DIG),
- SND_PCI_QUIRK(0x1025, 0x0087, "ULI", ALC880_6ST_DIG),
- SND_PCI_QUIRK(0x1025, 0xe309, "ULI", ALC880_3ST_DIG),
- SND_PCI_QUIRK(0x1025, 0xe310, "ULI", ALC880_3ST),
- SND_PCI_QUIRK(0x1039, 0x1234, NULL, ALC880_6ST_DIG),
- SND_PCI_QUIRK(0x103c, 0x2a09, "HP", ALC880_5ST),
- SND_PCI_QUIRK(0x1043, 0x10b3, "ASUS W1V", ALC880_ASUS_W1V),
- SND_PCI_QUIRK(0x1043, 0x10c2, "ASUS W6A", ALC880_ASUS_DIG),
- SND_PCI_QUIRK(0x1043, 0x10c3, "ASUS Wxx", ALC880_ASUS_DIG),
- SND_PCI_QUIRK(0x1043, 0x1113, "ASUS", ALC880_ASUS_DIG),
- SND_PCI_QUIRK(0x1043, 0x1123, "ASUS", ALC880_ASUS_DIG),
- SND_PCI_QUIRK(0x1043, 0x1173, "ASUS", ALC880_ASUS_DIG),
- SND_PCI_QUIRK(0x1043, 0x1964, "ASUS Z71V", ALC880_Z71V),
- /* SND_PCI_QUIRK(0x1043, 0x1964, "ASUS", ALC880_ASUS_DIG), */
- SND_PCI_QUIRK(0x1043, 0x1973, "ASUS", ALC880_ASUS_DIG),
- SND_PCI_QUIRK(0x1043, 0x19b3, "ASUS", ALC880_ASUS_DIG),
- SND_PCI_QUIRK(0x1043, 0x814e, "ASUS P5GD1 w/SPDIF", ALC880_6ST_DIG),
- SND_PCI_QUIRK(0x1043, 0x8181, "ASUS P4GPL", ALC880_ASUS_DIG),
- SND_PCI_QUIRK(0x1043, 0x8196, "ASUS P5GD1", ALC880_6ST),
- SND_PCI_QUIRK(0x1043, 0x81b4, "ASUS", ALC880_6ST),
- SND_PCI_QUIRK_VENDOR(0x1043, "ASUS", ALC880_ASUS), /* default ASUS */
- SND_PCI_QUIRK(0x104d, 0x81a0, "Sony", ALC880_3ST),
- SND_PCI_QUIRK(0x104d, 0x81d6, "Sony", ALC880_3ST),
- SND_PCI_QUIRK(0x107b, 0x3032, "Gateway", ALC880_5ST),
- SND_PCI_QUIRK(0x107b, 0x3033, "Gateway", ALC880_5ST),
- SND_PCI_QUIRK(0x107b, 0x4039, "Gateway", ALC880_5ST),
- SND_PCI_QUIRK(0x1297, 0xc790, "Shuttle ST20G5", ALC880_6ST_DIG),
- SND_PCI_QUIRK(0x1458, 0xa102, "Gigabyte K8", ALC880_6ST_DIG),
- SND_PCI_QUIRK(0x1462, 0x1150, "MSI", ALC880_6ST_DIG),
- SND_PCI_QUIRK(0x1509, 0x925d, "FIC P4M", ALC880_6ST_DIG),
- SND_PCI_QUIRK(0x1558, 0x0520, "Clevo m520G", ALC880_CLEVO),
- SND_PCI_QUIRK(0x1558, 0x0660, "Clevo m655n", ALC880_CLEVO),
- SND_PCI_QUIRK(0x1558, 0x5401, "ASUS", ALC880_ASUS_DIG2),
- SND_PCI_QUIRK(0x1565, 0x8202, "Biostar", ALC880_5ST_DIG),
- SND_PCI_QUIRK(0x1584, 0x9050, "Uniwill", ALC880_UNIWILL_DIG),
- SND_PCI_QUIRK(0x1584, 0x9054, "Uniwill", ALC880_F1734),
- SND_PCI_QUIRK(0x1584, 0x9070, "Uniwill", ALC880_UNIWILL),
- SND_PCI_QUIRK(0x1584, 0x9077, "Uniwill P53", ALC880_UNIWILL_P53),
- SND_PCI_QUIRK(0x161f, 0x203d, "W810", ALC880_W810),
- SND_PCI_QUIRK(0x161f, 0x205d, "Medion Rim 2150", ALC880_MEDION_RIM),
- SND_PCI_QUIRK(0x1695, 0x400d, "EPoX", ALC880_5ST_DIG),
- SND_PCI_QUIRK(0x1695, 0x4012, "EPox EP-5LDA", ALC880_5ST_DIG),
- SND_PCI_QUIRK(0x1734, 0x107c, "FSC F1734", ALC880_F1734),
- SND_PCI_QUIRK(0x1734, 0x1094, "FSC Amilo M1451G", ALC880_FUJITSU),
- SND_PCI_QUIRK(0x1734, 0x10ac, "FSC AMILO Xi 1526", ALC880_F1734),
- SND_PCI_QUIRK(0x1734, 0x10b0, "Fujitsu", ALC880_FUJITSU),
- SND_PCI_QUIRK(0x1854, 0x0018, "LG LW20", ALC880_LG_LW),
- SND_PCI_QUIRK(0x1854, 0x003b, "LG", ALC880_LG),
- SND_PCI_QUIRK(0x1854, 0x005f, "LG P1 Express", ALC880_LG),
- SND_PCI_QUIRK(0x1854, 0x0068, "LG w1", ALC880_LG),
- SND_PCI_QUIRK(0x1854, 0x0077, "LG LW25", ALC880_LG_LW),
- SND_PCI_QUIRK(0x19db, 0x4188, "TCL S700", ALC880_TCL_S700),
- SND_PCI_QUIRK(0x2668, 0x8086, NULL, ALC880_6ST_DIG), /* broken BIOS */
- SND_PCI_QUIRK(0x8086, 0x2668, NULL, ALC880_6ST_DIG),
- SND_PCI_QUIRK(0x8086, 0xa100, "Intel mobo", ALC880_5ST_DIG),
- SND_PCI_QUIRK(0x8086, 0xd400, "Intel mobo", ALC880_5ST_DIG),
- SND_PCI_QUIRK(0x8086, 0xd401, "Intel mobo", ALC880_5ST_DIG),
- SND_PCI_QUIRK(0x8086, 0xd402, "Intel mobo", ALC880_3ST_DIG),
- SND_PCI_QUIRK(0x8086, 0xe224, "Intel mobo", ALC880_5ST_DIG),
- SND_PCI_QUIRK(0x8086, 0xe305, "Intel mobo", ALC880_3ST_DIG),
- SND_PCI_QUIRK(0x8086, 0xe308, "Intel mobo", ALC880_3ST_DIG),
- SND_PCI_QUIRK(0x8086, 0xe400, "Intel mobo", ALC880_5ST_DIG),
- SND_PCI_QUIRK(0x8086, 0xe401, "Intel mobo", ALC880_5ST_DIG),
- SND_PCI_QUIRK(0x8086, 0xe402, "Intel mobo", ALC880_5ST_DIG),
- /* default Intel */
- SND_PCI_QUIRK_VENDOR(0x8086, "Intel mobo", ALC880_3ST),
- SND_PCI_QUIRK(0xa0a0, 0x0560, "AOpen i915GMm-HFS", ALC880_5ST_DIG),
- SND_PCI_QUIRK(0xe803, 0x1019, NULL, ALC880_6ST_DIG),
- {}
-};
-
-/*
- * ALC880 codec presets
- */
-static struct alc_config_preset alc880_presets[] = {
- [ALC880_3ST] = {
- .mixers = { alc880_three_stack_mixer },
- .init_verbs = { alc880_volume_init_verbs,
- alc880_pin_3stack_init_verbs },
- .num_dacs = ARRAY_SIZE(alc880_dac_nids),
- .dac_nids = alc880_dac_nids,
- .num_channel_mode = ARRAY_SIZE(alc880_threestack_modes),
- .channel_mode = alc880_threestack_modes,
- .need_dac_fix = 1,
- .input_mux = &alc880_capture_source,
- },
- [ALC880_3ST_DIG] = {
- .mixers = { alc880_three_stack_mixer },
- .init_verbs = { alc880_volume_init_verbs,
- alc880_pin_3stack_init_verbs },
- .num_dacs = ARRAY_SIZE(alc880_dac_nids),
- .dac_nids = alc880_dac_nids,
- .dig_out_nid = ALC880_DIGOUT_NID,
- .num_channel_mode = ARRAY_SIZE(alc880_threestack_modes),
- .channel_mode = alc880_threestack_modes,
- .need_dac_fix = 1,
- .input_mux = &alc880_capture_source,
- },
- [ALC880_TCL_S700] = {
- .mixers = { alc880_tcl_s700_mixer },
- .init_verbs = { alc880_volume_init_verbs,
- alc880_pin_tcl_S700_init_verbs,
- alc880_gpio2_init_verbs },
- .num_dacs = ARRAY_SIZE(alc880_dac_nids),
- .dac_nids = alc880_dac_nids,
- .adc_nids = alc880_adc_nids_alt, /* FIXME: correct? */
- .num_adc_nids = 1, /* single ADC */
- .hp_nid = 0x03,
- .num_channel_mode = ARRAY_SIZE(alc880_2_jack_modes),
- .channel_mode = alc880_2_jack_modes,
- .input_mux = &alc880_capture_source,
- },
- [ALC880_5ST] = {
- .mixers = { alc880_three_stack_mixer,
- alc880_five_stack_mixer},
- .init_verbs = { alc880_volume_init_verbs,
- alc880_pin_5stack_init_verbs },
- .num_dacs = ARRAY_SIZE(alc880_dac_nids),
- .dac_nids = alc880_dac_nids,
- .num_channel_mode = ARRAY_SIZE(alc880_fivestack_modes),
- .channel_mode = alc880_fivestack_modes,
- .input_mux = &alc880_capture_source,
- },
- [ALC880_5ST_DIG] = {
- .mixers = { alc880_three_stack_mixer,
- alc880_five_stack_mixer },
- .init_verbs = { alc880_volume_init_verbs,
- alc880_pin_5stack_init_verbs },
- .num_dacs = ARRAY_SIZE(alc880_dac_nids),
- .dac_nids = alc880_dac_nids,
- .dig_out_nid = ALC880_DIGOUT_NID,
- .num_channel_mode = ARRAY_SIZE(alc880_fivestack_modes),
- .channel_mode = alc880_fivestack_modes,
- .input_mux = &alc880_capture_source,
- },
- [ALC880_6ST] = {
- .mixers = { alc880_six_stack_mixer },
- .init_verbs = { alc880_volume_init_verbs,
- alc880_pin_6stack_init_verbs },
- .num_dacs = ARRAY_SIZE(alc880_6st_dac_nids),
- .dac_nids = alc880_6st_dac_nids,
- .num_channel_mode = ARRAY_SIZE(alc880_sixstack_modes),
- .channel_mode = alc880_sixstack_modes,
- .input_mux = &alc880_6stack_capture_source,
- },
- [ALC880_6ST_DIG] = {
- .mixers = { alc880_six_stack_mixer },
- .init_verbs = { alc880_volume_init_verbs,
- alc880_pin_6stack_init_verbs },
- .num_dacs = ARRAY_SIZE(alc880_6st_dac_nids),
- .dac_nids = alc880_6st_dac_nids,
- .dig_out_nid = ALC880_DIGOUT_NID,
- .num_channel_mode = ARRAY_SIZE(alc880_sixstack_modes),
- .channel_mode = alc880_sixstack_modes,
- .input_mux = &alc880_6stack_capture_source,
- },
- [ALC880_W810] = {
- .mixers = { alc880_w810_base_mixer },
- .init_verbs = { alc880_volume_init_verbs,
- alc880_pin_w810_init_verbs,
- alc880_gpio2_init_verbs },
- .num_dacs = ARRAY_SIZE(alc880_w810_dac_nids),
- .dac_nids = alc880_w810_dac_nids,
- .dig_out_nid = ALC880_DIGOUT_NID,
- .num_channel_mode = ARRAY_SIZE(alc880_w810_modes),
- .channel_mode = alc880_w810_modes,
- .input_mux = &alc880_capture_source,
- },
- [ALC880_Z71V] = {
- .mixers = { alc880_z71v_mixer },
- .init_verbs = { alc880_volume_init_verbs,
- alc880_pin_z71v_init_verbs },
- .num_dacs = ARRAY_SIZE(alc880_z71v_dac_nids),
- .dac_nids = alc880_z71v_dac_nids,
- .dig_out_nid = ALC880_DIGOUT_NID,
- .hp_nid = 0x03,
- .num_channel_mode = ARRAY_SIZE(alc880_2_jack_modes),
- .channel_mode = alc880_2_jack_modes,
- .input_mux = &alc880_capture_source,
- },
- [ALC880_F1734] = {
- .mixers = { alc880_f1734_mixer },
- .init_verbs = { alc880_volume_init_verbs,
- alc880_pin_f1734_init_verbs },
- .num_dacs = ARRAY_SIZE(alc880_f1734_dac_nids),
- .dac_nids = alc880_f1734_dac_nids,
- .hp_nid = 0x02,
- .num_channel_mode = ARRAY_SIZE(alc880_2_jack_modes),
- .channel_mode = alc880_2_jack_modes,
- .input_mux = &alc880_f1734_capture_source,
- .unsol_event = alc880_uniwill_p53_unsol_event,
- .setup = alc880_uniwill_p53_setup,
- .init_hook = alc_automute_amp,
- },
- [ALC880_ASUS] = {
- .mixers = { alc880_asus_mixer },
- .init_verbs = { alc880_volume_init_verbs,
- alc880_pin_asus_init_verbs,
- alc880_gpio1_init_verbs },
- .num_dacs = ARRAY_SIZE(alc880_asus_dac_nids),
- .dac_nids = alc880_asus_dac_nids,
- .num_channel_mode = ARRAY_SIZE(alc880_asus_modes),
- .channel_mode = alc880_asus_modes,
- .need_dac_fix = 1,
- .input_mux = &alc880_capture_source,
- },
- [ALC880_ASUS_DIG] = {
- .mixers = { alc880_asus_mixer },
- .init_verbs = { alc880_volume_init_verbs,
- alc880_pin_asus_init_verbs,
- alc880_gpio1_init_verbs },
- .num_dacs = ARRAY_SIZE(alc880_asus_dac_nids),
- .dac_nids = alc880_asus_dac_nids,
- .dig_out_nid = ALC880_DIGOUT_NID,
- .num_channel_mode = ARRAY_SIZE(alc880_asus_modes),
- .channel_mode = alc880_asus_modes,
- .need_dac_fix = 1,
- .input_mux = &alc880_capture_source,
- },
- [ALC880_ASUS_DIG2] = {
- .mixers = { alc880_asus_mixer },
- .init_verbs = { alc880_volume_init_verbs,
- alc880_pin_asus_init_verbs,
- alc880_gpio2_init_verbs }, /* use GPIO2 */
- .num_dacs = ARRAY_SIZE(alc880_asus_dac_nids),
- .dac_nids = alc880_asus_dac_nids,
- .dig_out_nid = ALC880_DIGOUT_NID,
- .num_channel_mode = ARRAY_SIZE(alc880_asus_modes),
- .channel_mode = alc880_asus_modes,
- .need_dac_fix = 1,
- .input_mux = &alc880_capture_source,
- },
- [ALC880_ASUS_W1V] = {
- .mixers = { alc880_asus_mixer, alc880_asus_w1v_mixer },
- .init_verbs = { alc880_volume_init_verbs,
- alc880_pin_asus_init_verbs,
- alc880_gpio1_init_verbs },
- .num_dacs = ARRAY_SIZE(alc880_asus_dac_nids),
- .dac_nids = alc880_asus_dac_nids,
- .dig_out_nid = ALC880_DIGOUT_NID,
- .num_channel_mode = ARRAY_SIZE(alc880_asus_modes),
- .channel_mode = alc880_asus_modes,
- .need_dac_fix = 1,
- .input_mux = &alc880_capture_source,
- },
- [ALC880_UNIWILL_DIG] = {
- .mixers = { alc880_asus_mixer },
- .init_verbs = { alc880_volume_init_verbs,
- alc880_pin_asus_init_verbs },
- .num_dacs = ARRAY_SIZE(alc880_asus_dac_nids),
- .dac_nids = alc880_asus_dac_nids,
- .dig_out_nid = ALC880_DIGOUT_NID,
- .num_channel_mode = ARRAY_SIZE(alc880_asus_modes),
- .channel_mode = alc880_asus_modes,
- .need_dac_fix = 1,
- .input_mux = &alc880_capture_source,
- },
- [ALC880_UNIWILL] = {
- .mixers = { alc880_uniwill_mixer },
- .init_verbs = { alc880_volume_init_verbs,
- alc880_uniwill_init_verbs },
- .num_dacs = ARRAY_SIZE(alc880_asus_dac_nids),
- .dac_nids = alc880_asus_dac_nids,
- .dig_out_nid = ALC880_DIGOUT_NID,
- .num_channel_mode = ARRAY_SIZE(alc880_threestack_modes),
- .channel_mode = alc880_threestack_modes,
- .need_dac_fix = 1,
- .input_mux = &alc880_capture_source,
- .unsol_event = alc880_uniwill_unsol_event,
- .setup = alc880_uniwill_setup,
- .init_hook = alc880_uniwill_init_hook,
- },
- [ALC880_UNIWILL_P53] = {
- .mixers = { alc880_uniwill_p53_mixer },
- .init_verbs = { alc880_volume_init_verbs,
- alc880_uniwill_p53_init_verbs },
- .num_dacs = ARRAY_SIZE(alc880_asus_dac_nids),
- .dac_nids = alc880_asus_dac_nids,
- .num_channel_mode = ARRAY_SIZE(alc880_w810_modes),
- .channel_mode = alc880_threestack_modes,
- .input_mux = &alc880_capture_source,
- .unsol_event = alc880_uniwill_p53_unsol_event,
- .setup = alc880_uniwill_p53_setup,
- .init_hook = alc_automute_amp,
- },
- [ALC880_FUJITSU] = {
- .mixers = { alc880_fujitsu_mixer },
- .init_verbs = { alc880_volume_init_verbs,
- alc880_uniwill_p53_init_verbs,
- alc880_beep_init_verbs },
- .num_dacs = ARRAY_SIZE(alc880_dac_nids),
- .dac_nids = alc880_dac_nids,
- .dig_out_nid = ALC880_DIGOUT_NID,
- .num_channel_mode = ARRAY_SIZE(alc880_2_jack_modes),
- .channel_mode = alc880_2_jack_modes,
- .input_mux = &alc880_capture_source,
- .unsol_event = alc880_uniwill_p53_unsol_event,
- .setup = alc880_uniwill_p53_setup,
- .init_hook = alc_automute_amp,
- },
- [ALC880_CLEVO] = {
- .mixers = { alc880_three_stack_mixer },
- .init_verbs = { alc880_volume_init_verbs,
- alc880_pin_clevo_init_verbs },
- .num_dacs = ARRAY_SIZE(alc880_dac_nids),
- .dac_nids = alc880_dac_nids,
- .hp_nid = 0x03,
- .num_channel_mode = ARRAY_SIZE(alc880_threestack_modes),
- .channel_mode = alc880_threestack_modes,
- .need_dac_fix = 1,
- .input_mux = &alc880_capture_source,
- },
- [ALC880_LG] = {
- .mixers = { alc880_lg_mixer },
- .init_verbs = { alc880_volume_init_verbs,
- alc880_lg_init_verbs },
- .num_dacs = ARRAY_SIZE(alc880_lg_dac_nids),
- .dac_nids = alc880_lg_dac_nids,
- .dig_out_nid = ALC880_DIGOUT_NID,
- .num_channel_mode = ARRAY_SIZE(alc880_lg_ch_modes),
- .channel_mode = alc880_lg_ch_modes,
- .need_dac_fix = 1,
- .input_mux = &alc880_lg_capture_source,
- .unsol_event = alc_automute_amp_unsol_event,
- .setup = alc880_lg_setup,
- .init_hook = alc_automute_amp,
-#ifdef CONFIG_SND_HDA_POWER_SAVE
- .loopbacks = alc880_lg_loopbacks,
-#endif
- },
- [ALC880_LG_LW] = {
- .mixers = { alc880_lg_lw_mixer },
- .init_verbs = { alc880_volume_init_verbs,
- alc880_lg_lw_init_verbs },
- .num_dacs = ARRAY_SIZE(alc880_dac_nids),
- .dac_nids = alc880_dac_nids,
- .dig_out_nid = ALC880_DIGOUT_NID,
- .num_channel_mode = ARRAY_SIZE(alc880_lg_lw_modes),
- .channel_mode = alc880_lg_lw_modes,
- .input_mux = &alc880_lg_lw_capture_source,
- .unsol_event = alc_automute_amp_unsol_event,
- .setup = alc880_lg_lw_setup,
- .init_hook = alc_automute_amp,
- },
- [ALC880_MEDION_RIM] = {
- .mixers = { alc880_medion_rim_mixer },
- .init_verbs = { alc880_volume_init_verbs,
- alc880_medion_rim_init_verbs,
- alc_gpio2_init_verbs },
- .num_dacs = ARRAY_SIZE(alc880_dac_nids),
- .dac_nids = alc880_dac_nids,
- .dig_out_nid = ALC880_DIGOUT_NID,
- .num_channel_mode = ARRAY_SIZE(alc880_2_jack_modes),
- .channel_mode = alc880_2_jack_modes,
- .input_mux = &alc880_medion_rim_capture_source,
- .unsol_event = alc880_medion_rim_unsol_event,
- .setup = alc880_medion_rim_setup,
- .init_hook = alc880_medion_rim_automute,
- },
-#ifdef CONFIG_SND_DEBUG
- [ALC880_TEST] = {
- .mixers = { alc880_test_mixer },
- .init_verbs = { alc880_test_init_verbs },
- .num_dacs = ARRAY_SIZE(alc880_test_dac_nids),
- .dac_nids = alc880_test_dac_nids,
- .dig_out_nid = ALC880_DIGOUT_NID,
- .num_channel_mode = ARRAY_SIZE(alc880_test_modes),
- .channel_mode = alc880_test_modes,
- .input_mux = &alc880_test_capture_source,
- },
-#endif
+struct alc_codec_rename_table {
+ unsigned int vendor_id;
+ unsigned short coef_mask;
+ unsigned short coef_bits;
+ const char *name;
};
-/*
- * Automatic parse of I/O pins from the BIOS configuration
- */
-
-enum {
- ALC_CTL_WIDGET_VOL,
- ALC_CTL_WIDGET_MUTE,
- ALC_CTL_BIND_MUTE,
-};
-static struct snd_kcontrol_new alc880_control_templates[] = {
- HDA_CODEC_VOLUME(NULL, 0, 0, 0),
- HDA_CODEC_MUTE(NULL, 0, 0, 0),
- HDA_BIND_MUTE(NULL, 0, 0, 0),
+struct alc_codec_rename_pci_table {
+ unsigned int codec_vendor_id;
+ unsigned short pci_subvendor;
+ unsigned short pci_subdevice;
+ const char *name;
};
-/* add dynamic controls */
-static int add_control(struct alc_spec *spec, int type, const char *name,
- int cidx, unsigned long val)
-{
- struct snd_kcontrol_new *knew;
-
- snd_array_init(&spec->kctls, sizeof(*knew), 32);
- knew = snd_array_new(&spec->kctls);
- if (!knew)
- return -ENOMEM;
- *knew = alc880_control_templates[type];
- knew->name = kstrdup(name, GFP_KERNEL);
- if (!knew->name)
- return -ENOMEM;
- knew->index = cidx;
- if (get_amp_nid_(val))
- knew->subdevice = HDA_SUBDEV_AMP_FLAG;
- knew->private_value = val;
- return 0;
-}
-
-static int add_control_with_pfx(struct alc_spec *spec, int type,
- const char *pfx, const char *dir,
- const char *sfx, int cidx, unsigned long val)
-{
- char name[32];
- snprintf(name, sizeof(name), "%s %s %s", pfx, dir, sfx);
- return add_control(spec, type, name, cidx, val);
-}
-
-#define add_pb_vol_ctrl(spec, type, pfx, val) \
- add_control_with_pfx(spec, type, pfx, "Playback", "Volume", 0, val)
-#define add_pb_sw_ctrl(spec, type, pfx, val) \
- add_control_with_pfx(spec, type, pfx, "Playback", "Switch", 0, val)
-#define __add_pb_vol_ctrl(spec, type, pfx, cidx, val) \
- add_control_with_pfx(spec, type, pfx, "Playback", "Volume", cidx, val)
-#define __add_pb_sw_ctrl(spec, type, pfx, cidx, val) \
- add_control_with_pfx(spec, type, pfx, "Playback", "Switch", cidx, val)
-
-#define alc880_is_fixed_pin(nid) ((nid) >= 0x14 && (nid) <= 0x17)
-#define alc880_fixed_pin_idx(nid) ((nid) - 0x14)
-#define alc880_is_multi_pin(nid) ((nid) >= 0x18)
-#define alc880_multi_pin_idx(nid) ((nid) - 0x18)
-#define alc880_idx_to_dac(nid) ((nid) + 0x02)
-#define alc880_dac_to_idx(nid) ((nid) - 0x02)
-#define alc880_idx_to_mixer(nid) ((nid) + 0x0c)
-#define alc880_idx_to_selector(nid) ((nid) + 0x10)
-#define ALC880_PIN_CD_NID 0x1c
-
-/* fill in the dac_nids table from the parsed pin configuration */
-static int alc880_auto_fill_dac_nids(struct alc_spec *spec,
- const struct auto_pin_cfg *cfg)
-{
- hda_nid_t nid;
- int assigned[4];
- int i, j;
-
- memset(assigned, 0, sizeof(assigned));
- spec->multiout.dac_nids = spec->private_dac_nids;
-
- /* check the pins hardwired to audio widget */
- for (i = 0; i < cfg->line_outs; i++) {
- nid = cfg->line_out_pins[i];
- if (alc880_is_fixed_pin(nid)) {
- int idx = alc880_fixed_pin_idx(nid);
- spec->multiout.dac_nids[i] = alc880_idx_to_dac(idx);
- assigned[idx] = 1;
- }
- }
- /* left pins can be connect to any audio widget */
- for (i = 0; i < cfg->line_outs; i++) {
- nid = cfg->line_out_pins[i];
- if (alc880_is_fixed_pin(nid))
+static struct alc_codec_rename_table rename_tbl[] = {
+ { 0x10ec0221, 0xf00f, 0x1003, "ALC231" },
+ { 0x10ec0269, 0xfff0, 0x3010, "ALC277" },
+ { 0x10ec0269, 0xf0f0, 0x2010, "ALC259" },
+ { 0x10ec0269, 0xf0f0, 0x3010, "ALC258" },
+ { 0x10ec0269, 0x00f0, 0x0010, "ALC269VB" },
+ { 0x10ec0269, 0xffff, 0xa023, "ALC259" },
+ { 0x10ec0269, 0xffff, 0x6023, "ALC281X" },
+ { 0x10ec0269, 0x00f0, 0x0020, "ALC269VC" },
+ { 0x10ec0269, 0x00f0, 0x0030, "ALC269VD" },
+ { 0x10ec0662, 0xffff, 0x4020, "ALC656" },
+ { 0x10ec0887, 0x00f0, 0x0030, "ALC887-VD" },
+ { 0x10ec0888, 0x00f0, 0x0030, "ALC888-VD" },
+ { 0x10ec0888, 0xf0f0, 0x3020, "ALC886" },
+ { 0x10ec0899, 0x2000, 0x2000, "ALC899" },
+ { 0x10ec0892, 0xffff, 0x8020, "ALC661" },
+ { 0x10ec0892, 0xffff, 0x8011, "ALC661" },
+ { 0x10ec0892, 0xffff, 0x4011, "ALC656" },
+ { } /* terminator */
+};
+
+static struct alc_codec_rename_pci_table rename_pci_tbl[] = {
+ { 0x10ec0280, 0x1028, 0, "ALC3220" },
+ { 0x10ec0282, 0x1028, 0, "ALC3221" },
+ { 0x10ec0283, 0x1028, 0, "ALC3223" },
+ { 0x10ec0288, 0x1028, 0, "ALC3263" },
+ { 0x10ec0292, 0x1028, 0, "ALC3226" },
+ { 0x10ec0293, 0x1028, 0, "ALC3235" },
+ { 0x10ec0255, 0x1028, 0, "ALC3234" },
+ { 0x10ec0668, 0x1028, 0, "ALC3661" },
+ { 0x10ec0275, 0x1028, 0, "ALC3260" },
+ { 0x10ec0899, 0x1028, 0, "ALC3861" },
+ { 0x10ec0670, 0x1025, 0, "ALC669X" },
+ { 0x10ec0676, 0x1025, 0, "ALC679X" },
+ { 0x10ec0282, 0x1043, 0, "ALC3229" },
+ { 0x10ec0233, 0x1043, 0, "ALC3236" },
+ { 0x10ec0280, 0x103c, 0, "ALC3228" },
+ { 0x10ec0282, 0x103c, 0, "ALC3227" },
+ { 0x10ec0286, 0x103c, 0, "ALC3242" },
+ { 0x10ec0290, 0x103c, 0, "ALC3241" },
+ { 0x10ec0668, 0x103c, 0, "ALC3662" },
+ { 0x10ec0283, 0x17aa, 0, "ALC3239" },
+ { 0x10ec0292, 0x17aa, 0, "ALC3232" },
+ { } /* terminator */
+};
+
+static int alc_codec_rename_from_preset(struct hda_codec *codec)
+{
+ const struct alc_codec_rename_table *p;
+ const struct alc_codec_rename_pci_table *q;
+
+ for (p = rename_tbl; p->vendor_id; p++) {
+ if (p->vendor_id != codec->vendor_id)
continue;
- /* search for an empty channel */
- for (j = 0; j < cfg->line_outs; j++) {
- if (!assigned[j]) {
- spec->multiout.dac_nids[i] =
- alc880_idx_to_dac(j);
- assigned[j] = 1;
- break;
- }
- }
+ if ((alc_get_coef0(codec) & p->coef_mask) == p->coef_bits)
+ return alc_codec_rename(codec, p->name);
}
- spec->multiout.num_dacs = cfg->line_outs;
- return 0;
-}
-
-static const char *alc_get_line_out_pfx(const struct auto_pin_cfg *cfg,
- bool can_be_master)
-{
- if (!cfg->hp_outs && !cfg->speaker_outs && can_be_master)
- return "Master";
- switch (cfg->line_out_type) {
- case AUTO_PIN_SPEAKER_OUT:
- if (cfg->line_outs == 1)
- return "Speaker";
- break;
- case AUTO_PIN_HP_OUT:
- return "Headphone";
- default:
- if (cfg->line_outs == 1)
- return "PCM";
- break;
- }
- return NULL;
-}
-
-/* add playback controls from the parsed DAC table */
-static int alc880_auto_create_multi_out_ctls(struct alc_spec *spec,
- const struct auto_pin_cfg *cfg)
-{
- static const char * const chname[4] = {
- "Front", "Surround", NULL /*CLFE*/, "Side"
- };
- const char *pfx = alc_get_line_out_pfx(cfg, false);
- hda_nid_t nid;
- int i, err;
-
- for (i = 0; i < cfg->line_outs; i++) {
- if (!spec->multiout.dac_nids[i])
- continue;
- nid = alc880_idx_to_mixer(alc880_dac_to_idx(spec->multiout.dac_nids[i]));
- if (!pfx && i == 2) {
- /* Center/LFE */
- err = add_pb_vol_ctrl(spec, ALC_CTL_WIDGET_VOL,
- "Center",
- HDA_COMPOSE_AMP_VAL(nid, 1, 0,
- HDA_OUTPUT));
- if (err < 0)
- return err;
- err = add_pb_vol_ctrl(spec, ALC_CTL_WIDGET_VOL,
- "LFE",
- HDA_COMPOSE_AMP_VAL(nid, 2, 0,
- HDA_OUTPUT));
- if (err < 0)
- return err;
- err = add_pb_sw_ctrl(spec, ALC_CTL_BIND_MUTE,
- "Center",
- HDA_COMPOSE_AMP_VAL(nid, 1, 2,
- HDA_INPUT));
- if (err < 0)
- return err;
- err = add_pb_sw_ctrl(spec, ALC_CTL_BIND_MUTE,
- "LFE",
- HDA_COMPOSE_AMP_VAL(nid, 2, 2,
- HDA_INPUT));
- if (err < 0)
- return err;
- } else {
- const char *name = pfx;
- int index = i;
- if (!name) {
- name = chname[i];
- index = 0;
- }
- err = __add_pb_vol_ctrl(spec, ALC_CTL_WIDGET_VOL,
- name, index,
- HDA_COMPOSE_AMP_VAL(nid, 3, 0,
- HDA_OUTPUT));
- if (err < 0)
- return err;
- err = __add_pb_sw_ctrl(spec, ALC_CTL_BIND_MUTE,
- name, index,
- HDA_COMPOSE_AMP_VAL(nid, 3, 2,
- HDA_INPUT));
- if (err < 0)
- return err;
- }
- }
- return 0;
-}
-
-/* add playback controls for speaker and HP outputs */
-static int alc880_auto_create_extra_out(struct alc_spec *spec, hda_nid_t pin,
- const char *pfx)
-{
- hda_nid_t nid;
- int err;
-
- if (!pin)
+ if (!codec->bus->pci)
return 0;
-
- if (alc880_is_fixed_pin(pin)) {
- nid = alc880_idx_to_dac(alc880_fixed_pin_idx(pin));
- /* specify the DAC as the extra output */
- if (!spec->multiout.hp_nid)
- spec->multiout.hp_nid = nid;
- else
- spec->multiout.extra_out_nid[0] = nid;
- /* control HP volume/switch on the output mixer amp */
- nid = alc880_idx_to_mixer(alc880_fixed_pin_idx(pin));
- err = add_pb_vol_ctrl(spec, ALC_CTL_WIDGET_VOL, pfx,
- HDA_COMPOSE_AMP_VAL(nid, 3, 0, HDA_OUTPUT));
- if (err < 0)
- return err;
- err = add_pb_sw_ctrl(spec, ALC_CTL_BIND_MUTE, pfx,
- HDA_COMPOSE_AMP_VAL(nid, 3, 2, HDA_INPUT));
- if (err < 0)
- return err;
- } else if (alc880_is_multi_pin(pin)) {
- /* set manual connection */
- /* we have only a switch on HP-out PIN */
- err = add_pb_sw_ctrl(spec, ALC_CTL_WIDGET_MUTE, pfx,
- HDA_COMPOSE_AMP_VAL(pin, 3, 0, HDA_OUTPUT));
- if (err < 0)
- return err;
- }
- return 0;
-}
-
-/* create input playback/capture controls for the given pin */
-static int new_analog_input(struct alc_spec *spec, hda_nid_t pin,
- const char *ctlname, int ctlidx,
- int idx, hda_nid_t mix_nid)
-{
- int err;
-
- err = __add_pb_vol_ctrl(spec, ALC_CTL_WIDGET_VOL, ctlname, ctlidx,
- HDA_COMPOSE_AMP_VAL(mix_nid, 3, idx, HDA_INPUT));
- if (err < 0)
- return err;
- err = __add_pb_sw_ctrl(spec, ALC_CTL_WIDGET_MUTE, ctlname, ctlidx,
- HDA_COMPOSE_AMP_VAL(mix_nid, 3, idx, HDA_INPUT));
- if (err < 0)
- return err;
- return 0;
-}
-
-static int alc_is_input_pin(struct hda_codec *codec, hda_nid_t nid)
-{
- unsigned int pincap = snd_hda_query_pin_caps(codec, nid);
- return (pincap & AC_PINCAP_IN) != 0;
-}
-
-/* create playback/capture controls for input pins */
-static int alc_auto_create_input_ctls(struct hda_codec *codec,
- const struct auto_pin_cfg *cfg,
- hda_nid_t mixer,
- hda_nid_t cap1, hda_nid_t cap2)
-{
- struct alc_spec *spec = codec->spec;
- struct hda_input_mux *imux = &spec->private_imux[0];
- int i, err, idx, type_idx = 0;
- const char *prev_label = NULL;
-
- for (i = 0; i < cfg->num_inputs; i++) {
- hda_nid_t pin;
- const char *label;
-
- pin = cfg->inputs[i].pin;
- if (!alc_is_input_pin(codec, pin))
+ for (q = rename_pci_tbl; q->codec_vendor_id; q++) {
+ if (q->codec_vendor_id != codec->vendor_id)
continue;
-
- label = hda_get_autocfg_input_label(codec, cfg, i);
- if (prev_label && !strcmp(label, prev_label))
- type_idx++;
- else
- type_idx = 0;
- prev_label = label;
-
- if (mixer) {
- idx = get_connection_index(codec, mixer, pin);
- if (idx >= 0) {
- err = new_analog_input(spec, pin,
- label, type_idx,
- idx, mixer);
- if (err < 0)
- return err;
- }
- }
-
- if (!cap1)
+ if (q->pci_subvendor != codec->bus->pci->subsystem_vendor)
continue;
- idx = get_connection_index(codec, cap1, pin);
- if (idx < 0 && cap2)
- idx = get_connection_index(codec, cap2, pin);
- if (idx >= 0)
- snd_hda_add_imux_item(imux, label, idx, NULL);
- }
- return 0;
-}
-
-static int alc880_auto_create_input_ctls(struct hda_codec *codec,
- const struct auto_pin_cfg *cfg)
-{
- return alc_auto_create_input_ctls(codec, cfg, 0x0b, 0x08, 0x09);
-}
-
-static void alc_set_pin_output(struct hda_codec *codec, hda_nid_t nid,
- unsigned int pin_type)
-{
- snd_hda_codec_write(codec, nid, 0, AC_VERB_SET_PIN_WIDGET_CONTROL,
- pin_type);
- /* unmute pin */
- snd_hda_codec_write(codec, nid, 0, AC_VERB_SET_AMP_GAIN_MUTE,
- AMP_OUT_UNMUTE);
-}
-
-static void alc880_auto_set_output_and_unmute(struct hda_codec *codec,
- hda_nid_t nid, int pin_type,
- int dac_idx)
-{
- alc_set_pin_output(codec, nid, pin_type);
- /* need the manual connection? */
- if (alc880_is_multi_pin(nid)) {
- struct alc_spec *spec = codec->spec;
- int idx = alc880_multi_pin_idx(nid);
- snd_hda_codec_write(codec, alc880_idx_to_selector(idx), 0,
- AC_VERB_SET_CONNECT_SEL,
- alc880_dac_to_idx(spec->multiout.dac_nids[dac_idx]));
- }
-}
-
-static int get_pin_type(int line_out_type)
-{
- if (line_out_type == AUTO_PIN_HP_OUT)
- return PIN_HP;
- else
- return PIN_OUT;
-}
-
-static void alc880_auto_init_multi_out(struct hda_codec *codec)
-{
- struct alc_spec *spec = codec->spec;
- int i;
-
- for (i = 0; i < spec->autocfg.line_outs; i++) {
- hda_nid_t nid = spec->autocfg.line_out_pins[i];
- int pin_type = get_pin_type(spec->autocfg.line_out_type);
- alc880_auto_set_output_and_unmute(codec, nid, pin_type, i);
- }
-}
-
-static void alc880_auto_init_extra_out(struct hda_codec *codec)
-{
- struct alc_spec *spec = codec->spec;
- hda_nid_t pin;
-
- pin = spec->autocfg.speaker_pins[0];
- if (pin) /* connect to front */
- alc880_auto_set_output_and_unmute(codec, pin, PIN_OUT, 0);
- pin = spec->autocfg.hp_pins[0];
- if (pin) /* connect to front */
- alc880_auto_set_output_and_unmute(codec, pin, PIN_HP, 0);
-}
-
-static void alc880_auto_init_analog_input(struct hda_codec *codec)
-{
- struct alc_spec *spec = codec->spec;
- struct auto_pin_cfg *cfg = &spec->autocfg;
- int i;
-
- for (i = 0; i < cfg->num_inputs; i++) {
- hda_nid_t nid = cfg->inputs[i].pin;
- if (alc_is_input_pin(codec, nid)) {
- alc_set_input_pin(codec, nid, cfg->inputs[i].type);
- if (nid != ALC880_PIN_CD_NID &&
- (get_wcaps(codec, nid) & AC_WCAP_OUT_AMP))
- snd_hda_codec_write(codec, nid, 0,
- AC_VERB_SET_AMP_GAIN_MUTE,
- AMP_OUT_MUTE);
- }
+ if (!q->pci_subdevice ||
+ q->pci_subdevice == codec->bus->pci->subsystem_device)
+ return alc_codec_rename(codec, q->name);
}
-}
-
-static void alc880_auto_init_input_src(struct hda_codec *codec)
-{
- struct alc_spec *spec = codec->spec;
- int c;
-
- for (c = 0; c < spec->num_adc_nids; c++) {
- unsigned int mux_idx;
- const struct hda_input_mux *imux;
- mux_idx = c >= spec->num_mux_defs ? 0 : c;
- imux = &spec->input_mux[mux_idx];
- if (!imux->num_items && mux_idx > 0)
- imux = &spec->input_mux[0];
- if (imux)
- snd_hda_codec_write(codec, spec->adc_nids[c], 0,
- AC_VERB_SET_CONNECT_SEL,
- imux->items[0].index);
- }
-}
-
-/* parse the BIOS configuration and set up the alc_spec */
-/* return 1 if successful, 0 if the proper config is not found,
- * or a negative error code
- */
-static int alc880_parse_auto_config(struct hda_codec *codec)
-{
- struct alc_spec *spec = codec->spec;
- int err;
- static hda_nid_t alc880_ignore[] = { 0x1d, 0 };
-
- err = snd_hda_parse_pin_def_config(codec, &spec->autocfg,
- alc880_ignore);
- if (err < 0)
- return err;
- if (!spec->autocfg.line_outs)
- return 0; /* can't find valid BIOS pin config */
-
- err = alc880_auto_fill_dac_nids(spec, &spec->autocfg);
- if (err < 0)
- return err;
- err = alc880_auto_create_multi_out_ctls(spec, &spec->autocfg);
- if (err < 0)
- return err;
- err = alc880_auto_create_extra_out(spec,
- spec->autocfg.speaker_pins[0],
- "Speaker");
- if (err < 0)
- return err;
- err = alc880_auto_create_extra_out(spec, spec->autocfg.hp_pins[0],
- "Headphone");
- if (err < 0)
- return err;
- err = alc880_auto_create_input_ctls(codec, &spec->autocfg);
- if (err < 0)
- return err;
-
- spec->multiout.max_channels = spec->multiout.num_dacs * 2;
-
- alc_auto_parse_digital(codec);
-
- if (spec->kctls.list)
- add_mixer(spec, spec->kctls.list);
-
- add_verb(spec, alc880_volume_init_verbs);
- spec->num_mux_defs = 1;
- spec->input_mux = &spec->private_imux[0];
-
- alc_ssid_check(codec, 0x15, 0x1b, 0x14, 0);
-
- return 1;
+ return 0;
}
-/* additional initialization for auto-configuration model */
-static void alc880_auto_init(struct hda_codec *codec)
-{
- struct alc_spec *spec = codec->spec;
- alc880_auto_init_multi_out(codec);
- alc880_auto_init_extra_out(codec);
- alc880_auto_init_analog_input(codec);
- alc880_auto_init_input_src(codec);
- alc_auto_init_digital(codec);
- if (spec->unsol_event)
- alc_inithook(codec);
-}
-/* check the ADC/MUX contains all input pins; some ADC/MUX contains only
- * one of two digital mic pins, e.g. on ALC272
+/*
+ * Digital-beep handlers
*/
-static void fixup_automic_adc(struct hda_codec *codec)
-{
- struct alc_spec *spec = codec->spec;
- int i;
-
- for (i = 0; i < spec->num_adc_nids; i++) {
- hda_nid_t cap = spec->capsrc_nids ?
- spec->capsrc_nids[i] : spec->adc_nids[i];
- int iidx, eidx;
-
- iidx = get_connection_index(codec, cap, spec->int_mic.pin);
- if (iidx < 0)
- continue;
- eidx = get_connection_index(codec, cap, spec->ext_mic.pin);
- if (eidx < 0)
- continue;
- spec->int_mic.mux_idx = iidx;
- spec->ext_mic.mux_idx = eidx;
- if (spec->capsrc_nids)
- spec->capsrc_nids += i;
- spec->adc_nids += i;
- spec->num_adc_nids = 1;
- return;
- }
- snd_printd(KERN_INFO "hda_codec: %s: "
- "No ADC/MUX containing both 0x%x and 0x%x pins\n",
- codec->chip_name, spec->int_mic.pin, spec->ext_mic.pin);
- spec->auto_mic = 0; /* disable auto-mic to be sure */
-}
-
-/* select or unmute the given capsrc route */
-static void select_or_unmute_capsrc(struct hda_codec *codec, hda_nid_t cap,
- int idx)
-{
- if (get_wcaps_type(get_wcaps(codec, cap)) == AC_WID_AUD_MIX) {
- snd_hda_codec_amp_stereo(codec, cap, HDA_INPUT, idx,
- HDA_AMP_MUTE, 0);
- } else {
- snd_hda_codec_write_cache(codec, cap, 0,
- AC_VERB_SET_CONNECT_SEL, idx);
- }
-}
-
-/* set the default connection to that pin */
-static int init_capsrc_for_pin(struct hda_codec *codec, hda_nid_t pin)
-{
- struct alc_spec *spec = codec->spec;
- int i;
-
- for (i = 0; i < spec->num_adc_nids; i++) {
- hda_nid_t cap = spec->capsrc_nids ?
- spec->capsrc_nids[i] : spec->adc_nids[i];
- int idx;
-
- idx = get_connection_index(codec, cap, pin);
- if (idx < 0)
- continue;
- select_or_unmute_capsrc(codec, cap, idx);
- return i; /* return the found index */
- }
- return -1; /* not found */
-}
-
-/* choose the ADC/MUX containing the input pin and initialize the setup */
-static void fixup_single_adc(struct hda_codec *codec)
-{
- struct alc_spec *spec = codec->spec;
- struct auto_pin_cfg *cfg = &spec->autocfg;
- int i;
-
- /* search for the input pin; there must be only one */
- if (cfg->num_inputs != 1)
- return;
- i = init_capsrc_for_pin(codec, cfg->inputs[0].pin);
- if (i >= 0) {
- /* use only this ADC */
- if (spec->capsrc_nids)
- spec->capsrc_nids += i;
- spec->adc_nids += i;
- spec->num_adc_nids = 1;
- spec->single_input_src = 1;
- }
-}
-
-/* initialize dual adcs */
-static void fixup_dual_adc_switch(struct hda_codec *codec)
-{
- struct alc_spec *spec = codec->spec;
- init_capsrc_for_pin(codec, spec->ext_mic.pin);
- init_capsrc_for_pin(codec, spec->int_mic.pin);
-}
-
-/* initialize some special cases for input sources */
-static void alc_init_special_input_src(struct hda_codec *codec)
-{
- struct alc_spec *spec = codec->spec;
- if (spec->dual_adc_switch)
- fixup_dual_adc_switch(codec);
- else if (spec->single_input_src)
- init_capsrc_for_pin(codec, spec->autocfg.inputs[0].pin);
-}
-
-static void set_capture_mixer(struct hda_codec *codec)
-{
- struct alc_spec *spec = codec->spec;
- static struct snd_kcontrol_new *caps[2][3] = {
- { alc_capture_mixer_nosrc1,
- alc_capture_mixer_nosrc2,
- alc_capture_mixer_nosrc3 },
- { alc_capture_mixer1,
- alc_capture_mixer2,
- alc_capture_mixer3 },
- };
- if (spec->num_adc_nids > 0 && spec->num_adc_nids <= 3) {
- int mux = 0;
- int num_adcs = spec->num_adc_nids;
- if (spec->dual_adc_switch)
- num_adcs = 1;
- else if (spec->auto_mic)
- fixup_automic_adc(codec);
- else if (spec->input_mux) {
- if (spec->input_mux->num_items > 1)
- mux = 1;
- else if (spec->input_mux->num_items == 1)
- fixup_single_adc(codec);
- }
- spec->cap_mixer = caps[mux][num_adcs - 1];
- }
-}
-
-/* fill adc_nids (and capsrc_nids) containing all active input pins */
-static void fillup_priv_adc_nids(struct hda_codec *codec, hda_nid_t *nids,
- int num_nids)
-{
- struct alc_spec *spec = codec->spec;
- struct auto_pin_cfg *cfg = &spec->autocfg;
- int n;
- hda_nid_t fallback_adc = 0, fallback_cap = 0;
-
- for (n = 0; n < num_nids; n++) {
- hda_nid_t adc, cap;
- hda_nid_t conn[HDA_MAX_NUM_INPUTS];
- int nconns, i, j;
-
- adc = nids[n];
- if (get_wcaps_type(get_wcaps(codec, adc)) != AC_WID_AUD_IN)
- continue;
- cap = adc;
- nconns = snd_hda_get_connections(codec, cap, conn,
- ARRAY_SIZE(conn));
- if (nconns == 1) {
- cap = conn[0];
- nconns = snd_hda_get_connections(codec, cap, conn,
- ARRAY_SIZE(conn));
- }
- if (nconns <= 0)
- continue;
- if (!fallback_adc) {
- fallback_adc = adc;
- fallback_cap = cap;
- }
- for (i = 0; i < cfg->num_inputs; i++) {
- hda_nid_t nid = cfg->inputs[i].pin;
- for (j = 0; j < nconns; j++) {
- if (conn[j] == nid)
- break;
- }
- if (j >= nconns)
- break;
- }
- if (i >= cfg->num_inputs) {
- int num_adcs = spec->num_adc_nids;
- spec->private_adc_nids[num_adcs] = adc;
- spec->private_capsrc_nids[num_adcs] = cap;
- spec->num_adc_nids++;
- spec->adc_nids = spec->private_adc_nids;
- if (adc != cap)
- spec->capsrc_nids = spec->private_capsrc_nids;
- }
- }
- if (!spec->num_adc_nids) {
- printk(KERN_WARNING "hda_codec: %s: no valid ADC found;"
- " using fallback 0x%x\n",
- codec->chip_name, fallback_adc);
- spec->private_adc_nids[0] = fallback_adc;
- spec->adc_nids = spec->private_adc_nids;
- if (fallback_adc != fallback_cap) {
- spec->private_capsrc_nids[0] = fallback_cap;
- spec->capsrc_nids = spec->private_adc_nids;
- }
- }
-}
-
#ifdef CONFIG_SND_HDA_INPUT_BEEP
#define set_beep_amp(spec, nid, idx, dir) \
((spec)->beep_amp = HDA_COMPOSE_AMP_VAL(nid, 3, idx, dir))
-static struct snd_pci_quirk beep_white_list[] = {
+static const struct snd_pci_quirk beep_white_list[] = {
+ SND_PCI_QUIRK(0x1043, 0x103c, "ASUS", 1),
+ SND_PCI_QUIRK(0x1043, 0x115d, "ASUS", 1),
SND_PCI_QUIRK(0x1043, 0x829f, "ASUS", 1),
+ SND_PCI_QUIRK(0x1043, 0x8376, "EeePC", 1),
SND_PCI_QUIRK(0x1043, 0x83ce, "EeePC", 1),
+ SND_PCI_QUIRK(0x1043, 0x831a, "EeePC", 1),
+ SND_PCI_QUIRK(0x1043, 0x834a, "EeePC", 1),
+ SND_PCI_QUIRK(0x1458, 0xa002, "GA-MA790X", 1),
SND_PCI_QUIRK(0x8086, 0xd613, "Intel", 1),
{}
};
@@ -5663,1732 +1036,765 @@ static inline int has_cdefine_beep(struct hda_codec *codec)
#define has_cdefine_beep(codec) 0
#endif
-/*
- * OK, here we have finally the patch for ALC880
+/* parse the BIOS configuration and set up the alc_spec */
+/* return 1 if successful, 0 if the proper config is not found,
+ * or a negative error code
*/
-
-static int patch_alc880(struct hda_codec *codec)
+static int alc_parse_auto_config(struct hda_codec *codec,
+ const hda_nid_t *ignore_nids,
+ const hda_nid_t *ssid_nids)
{
- struct alc_spec *spec;
- int board_config;
+ struct alc_spec *spec = codec->spec;
+ struct auto_pin_cfg *cfg = &spec->gen.autocfg;
int err;
- spec = kzalloc(sizeof(*spec), GFP_KERNEL);
- if (spec == NULL)
- return -ENOMEM;
+ err = snd_hda_parse_pin_defcfg(codec, cfg, ignore_nids,
+ spec->parse_flags);
+ if (err < 0)
+ return err;
- codec->spec = spec;
+ if (ssid_nids)
+ alc_ssid_check(codec, ssid_nids);
- board_config = snd_hda_check_board_config(codec, ALC880_MODEL_LAST,
- alc880_models,
- alc880_cfg_tbl);
- if (board_config < 0) {
- printk(KERN_INFO "hda_codec: %s: BIOS auto-probing.\n",
- codec->chip_name);
- board_config = ALC880_AUTO;
- }
+ err = snd_hda_gen_parse_auto_config(codec, cfg);
+ if (err < 0)
+ return err;
- if (board_config == ALC880_AUTO) {
- /* automatic parse from the BIOS config */
- err = alc880_parse_auto_config(codec);
- if (err < 0) {
- alc_free(codec);
- return err;
- } else if (!err) {
- printk(KERN_INFO
- "hda_codec: Cannot set up configuration "
- "from BIOS. Using 3-stack mode...\n");
- board_config = ALC880_3ST;
- }
- }
+ return 1;
+}
+
+/* common preparation job for alc_spec */
+static int alc_alloc_spec(struct hda_codec *codec, hda_nid_t mixer_nid)
+{
+ struct alc_spec *spec = kzalloc(sizeof(*spec), GFP_KERNEL);
+ int err;
- err = snd_hda_attach_beep_device(codec, 0x1);
+ if (!spec)
+ return -ENOMEM;
+ codec->spec = spec;
+ snd_hda_gen_spec_init(&spec->gen);
+ spec->gen.mixer_nid = mixer_nid;
+ spec->gen.own_eapd_ctl = 1;
+ codec->single_adc_amp = 1;
+ /* FIXME: do we need this for all Realtek codec models? */
+ codec->spdif_status_reset = 1;
+
+ err = alc_codec_rename_from_preset(codec);
if (err < 0) {
- alc_free(codec);
+ kfree(spec);
return err;
}
-
- if (board_config != ALC880_AUTO)
- setup_preset(codec, &alc880_presets[board_config]);
-
- spec->stream_analog_playback = &alc880_pcm_analog_playback;
- spec->stream_analog_capture = &alc880_pcm_analog_capture;
- spec->stream_analog_alt_capture = &alc880_pcm_analog_alt_capture;
-
- spec->stream_digital_playback = &alc880_pcm_digital_playback;
- spec->stream_digital_capture = &alc880_pcm_digital_capture;
-
- if (!spec->adc_nids && spec->input_mux) {
- /* check whether NID 0x07 is valid */
- unsigned int wcap = get_wcaps(codec, alc880_adc_nids[0]);
- /* get type */
- wcap = get_wcaps_type(wcap);
- if (wcap != AC_WID_AUD_IN) {
- spec->adc_nids = alc880_adc_nids_alt;
- spec->num_adc_nids = ARRAY_SIZE(alc880_adc_nids_alt);
- } else {
- spec->adc_nids = alc880_adc_nids;
- spec->num_adc_nids = ARRAY_SIZE(alc880_adc_nids);
- }
- }
- set_capture_mixer(codec);
- set_beep_amp(spec, 0x0b, 0x05, HDA_INPUT);
-
- spec->vmaster_nid = 0x0c;
-
- codec->patch_ops = alc_patch_ops;
- if (board_config == ALC880_AUTO)
- spec->init_hook = alc880_auto_init;
-#ifdef CONFIG_SND_HDA_POWER_SAVE
- if (!spec->loopback.amplist)
- spec->loopback.amplist = alc880_loopbacks;
-#endif
-
return 0;
}
+static int alc880_parse_auto_config(struct hda_codec *codec)
+{
+ static const hda_nid_t alc880_ignore[] = { 0x1d, 0 };
+ static const hda_nid_t alc880_ssids[] = { 0x15, 0x1b, 0x14, 0 };
+ return alc_parse_auto_config(codec, alc880_ignore, alc880_ssids);
+}
/*
- * ALC260 support
+ * ALC880 fix-ups
*/
-
-static hda_nid_t alc260_dac_nids[1] = {
- /* front */
- 0x02,
-};
-
-static hda_nid_t alc260_adc_nids[1] = {
- /* ADC0 */
- 0x04,
-};
-
-static hda_nid_t alc260_adc_nids_alt[1] = {
- /* ADC1 */
- 0x05,
-};
-
-/* NIDs used when simultaneous access to both ADCs makes sense. Note that
- * alc260_capture_mixer assumes ADC0 (nid 0x04) is the first ADC.
- */
-static hda_nid_t alc260_dual_adc_nids[2] = {
- /* ADC0, ADC1 */
- 0x04, 0x05
-};
-
-#define ALC260_DIGOUT_NID 0x03
-#define ALC260_DIGIN_NID 0x06
-
-static struct hda_input_mux alc260_capture_source = {
- .num_items = 4,
- .items = {
- { "Mic", 0x0 },
- { "Front Mic", 0x1 },
- { "Line", 0x2 },
- { "CD", 0x4 },
+enum {
+ ALC880_FIXUP_GPIO1,
+ ALC880_FIXUP_GPIO2,
+ ALC880_FIXUP_MEDION_RIM,
+ ALC880_FIXUP_LG,
+ ALC880_FIXUP_LG_LW25,
+ ALC880_FIXUP_W810,
+ ALC880_FIXUP_EAPD_COEF,
+ ALC880_FIXUP_TCL_S700,
+ ALC880_FIXUP_VOL_KNOB,
+ ALC880_FIXUP_FUJITSU,
+ ALC880_FIXUP_F1734,
+ ALC880_FIXUP_UNIWILL,
+ ALC880_FIXUP_UNIWILL_DIG,
+ ALC880_FIXUP_Z71V,
+ ALC880_FIXUP_ASUS_W5A,
+ ALC880_FIXUP_3ST_BASE,
+ ALC880_FIXUP_3ST,
+ ALC880_FIXUP_3ST_DIG,
+ ALC880_FIXUP_5ST_BASE,
+ ALC880_FIXUP_5ST,
+ ALC880_FIXUP_5ST_DIG,
+ ALC880_FIXUP_6ST_BASE,
+ ALC880_FIXUP_6ST,
+ ALC880_FIXUP_6ST_DIG,
+ ALC880_FIXUP_6ST_AUTOMUTE,
+};
+
+/* enable the volume-knob widget support on NID 0x21 */
+static void alc880_fixup_vol_knob(struct hda_codec *codec,
+ const struct hda_fixup *fix, int action)
+{
+ if (action == HDA_FIXUP_ACT_PROBE)
+ snd_hda_jack_detect_enable_callback(codec, 0x21, ALC_DCVOL_EVENT, alc_update_knob_master);
+}
+
+static const struct hda_fixup alc880_fixups[] = {
+ [ALC880_FIXUP_GPIO1] = {
+ .type = HDA_FIXUP_VERBS,
+ .v.verbs = alc_gpio1_init_verbs,
+ },
+ [ALC880_FIXUP_GPIO2] = {
+ .type = HDA_FIXUP_VERBS,
+ .v.verbs = alc_gpio2_init_verbs,
+ },
+ [ALC880_FIXUP_MEDION_RIM] = {
+ .type = HDA_FIXUP_VERBS,
+ .v.verbs = (const struct hda_verb[]) {
+ { 0x20, AC_VERB_SET_COEF_INDEX, 0x07 },
+ { 0x20, AC_VERB_SET_PROC_COEF, 0x3060 },
+ { }
+ },
+ .chained = true,
+ .chain_id = ALC880_FIXUP_GPIO2,
+ },
+ [ALC880_FIXUP_LG] = {
+ .type = HDA_FIXUP_PINS,
+ .v.pins = (const struct hda_pintbl[]) {
+ /* disable bogus unused pins */
+ { 0x16, 0x411111f0 },
+ { 0x18, 0x411111f0 },
+ { 0x1a, 0x411111f0 },
+ { }
+ }
},
-};
-
-/* On Fujitsu S702x laptops capture only makes sense from Mic/LineIn jack,
- * headphone jack and the internal CD lines since these are the only pins at
- * which audio can appear. For flexibility, also allow the option of
- * recording the mixer output on the second ADC (ADC0 doesn't have a
- * connection to the mixer output).
- */
-static struct hda_input_mux alc260_fujitsu_capture_sources[2] = {
- {
- .num_items = 3,
- .items = {
- { "Mic/Line", 0x0 },
- { "CD", 0x4 },
- { "Headphone", 0x2 },
+ [ALC880_FIXUP_LG_LW25] = {
+ .type = HDA_FIXUP_PINS,
+ .v.pins = (const struct hda_pintbl[]) {
+ { 0x1a, 0x0181344f }, /* line-in */
+ { 0x1b, 0x0321403f }, /* headphone */
+ { }
+ }
+ },
+ [ALC880_FIXUP_W810] = {
+ .type = HDA_FIXUP_PINS,
+ .v.pins = (const struct hda_pintbl[]) {
+ /* disable bogus unused pins */
+ { 0x17, 0x411111f0 },
+ { }
},
+ .chained = true,
+ .chain_id = ALC880_FIXUP_GPIO2,
},
- {
- .num_items = 4,
- .items = {
- { "Mic/Line", 0x0 },
- { "CD", 0x4 },
- { "Headphone", 0x2 },
- { "Mixer", 0x5 },
+ [ALC880_FIXUP_EAPD_COEF] = {
+ .type = HDA_FIXUP_VERBS,
+ .v.verbs = (const struct hda_verb[]) {
+ /* change to EAPD mode */
+ { 0x20, AC_VERB_SET_COEF_INDEX, 0x07 },
+ { 0x20, AC_VERB_SET_PROC_COEF, 0x3060 },
+ {}
},
},
-
-};
-
-/* Acer TravelMate(/Extensa/Aspire) notebooks have similar configuration to
- * the Fujitsu S702x, but jacks are marked differently.
- */
-static struct hda_input_mux alc260_acer_capture_sources[2] = {
- {
- .num_items = 4,
- .items = {
- { "Mic", 0x0 },
- { "Line", 0x2 },
- { "CD", 0x4 },
- { "Headphone", 0x5 },
+ [ALC880_FIXUP_TCL_S700] = {
+ .type = HDA_FIXUP_VERBS,
+ .v.verbs = (const struct hda_verb[]) {
+ /* change to EAPD mode */
+ { 0x20, AC_VERB_SET_COEF_INDEX, 0x07 },
+ { 0x20, AC_VERB_SET_PROC_COEF, 0x3070 },
+ {}
+ },
+ .chained = true,
+ .chain_id = ALC880_FIXUP_GPIO2,
+ },
+ [ALC880_FIXUP_VOL_KNOB] = {
+ .type = HDA_FIXUP_FUNC,
+ .v.func = alc880_fixup_vol_knob,
+ },
+ [ALC880_FIXUP_FUJITSU] = {
+ /* override all pins as BIOS on old Amilo is broken */
+ .type = HDA_FIXUP_PINS,
+ .v.pins = (const struct hda_pintbl[]) {
+ { 0x14, 0x0121411f }, /* HP */
+ { 0x15, 0x99030120 }, /* speaker */
+ { 0x16, 0x99030130 }, /* bass speaker */
+ { 0x17, 0x411111f0 }, /* N/A */
+ { 0x18, 0x411111f0 }, /* N/A */
+ { 0x19, 0x01a19950 }, /* mic-in */
+ { 0x1a, 0x411111f0 }, /* N/A */
+ { 0x1b, 0x411111f0 }, /* N/A */
+ { 0x1c, 0x411111f0 }, /* N/A */
+ { 0x1d, 0x411111f0 }, /* N/A */
+ { 0x1e, 0x01454140 }, /* SPDIF out */
+ { }
+ },
+ .chained = true,
+ .chain_id = ALC880_FIXUP_VOL_KNOB,
+ },
+ [ALC880_FIXUP_F1734] = {
+ /* almost compatible with FUJITSU, but no bass and SPDIF */
+ .type = HDA_FIXUP_PINS,
+ .v.pins = (const struct hda_pintbl[]) {
+ { 0x14, 0x0121411f }, /* HP */
+ { 0x15, 0x99030120 }, /* speaker */
+ { 0x16, 0x411111f0 }, /* N/A */
+ { 0x17, 0x411111f0 }, /* N/A */
+ { 0x18, 0x411111f0 }, /* N/A */
+ { 0x19, 0x01a19950 }, /* mic-in */
+ { 0x1a, 0x411111f0 }, /* N/A */
+ { 0x1b, 0x411111f0 }, /* N/A */
+ { 0x1c, 0x411111f0 }, /* N/A */
+ { 0x1d, 0x411111f0 }, /* N/A */
+ { 0x1e, 0x411111f0 }, /* N/A */
+ { }
+ },
+ .chained = true,
+ .chain_id = ALC880_FIXUP_VOL_KNOB,
+ },
+ [ALC880_FIXUP_UNIWILL] = {
+ /* need to fix HP and speaker pins to be parsed correctly */
+ .type = HDA_FIXUP_PINS,
+ .v.pins = (const struct hda_pintbl[]) {
+ { 0x14, 0x0121411f }, /* HP */
+ { 0x15, 0x99030120 }, /* speaker */
+ { 0x16, 0x99030130 }, /* bass speaker */
+ { }
},
},
- {
- .num_items = 5,
- .items = {
- { "Mic", 0x0 },
- { "Line", 0x2 },
- { "CD", 0x4 },
- { "Headphone", 0x6 },
- { "Mixer", 0x5 },
+ [ALC880_FIXUP_UNIWILL_DIG] = {
+ .type = HDA_FIXUP_PINS,
+ .v.pins = (const struct hda_pintbl[]) {
+ /* disable bogus unused pins */
+ { 0x17, 0x411111f0 },
+ { 0x19, 0x411111f0 },
+ { 0x1b, 0x411111f0 },
+ { 0x1f, 0x411111f0 },
+ { }
+ }
+ },
+ [ALC880_FIXUP_Z71V] = {
+ .type = HDA_FIXUP_PINS,
+ .v.pins = (const struct hda_pintbl[]) {
+ /* set up the whole pins as BIOS is utterly broken */
+ { 0x14, 0x99030120 }, /* speaker */
+ { 0x15, 0x0121411f }, /* HP */
+ { 0x16, 0x411111f0 }, /* N/A */
+ { 0x17, 0x411111f0 }, /* N/A */
+ { 0x18, 0x01a19950 }, /* mic-in */
+ { 0x19, 0x411111f0 }, /* N/A */
+ { 0x1a, 0x01813031 }, /* line-in */
+ { 0x1b, 0x411111f0 }, /* N/A */
+ { 0x1c, 0x411111f0 }, /* N/A */
+ { 0x1d, 0x411111f0 }, /* N/A */
+ { 0x1e, 0x0144111e }, /* SPDIF */
+ { }
+ }
+ },
+ [ALC880_FIXUP_ASUS_W5A] = {
+ .type = HDA_FIXUP_PINS,
+ .v.pins = (const struct hda_pintbl[]) {
+ /* set up the whole pins as BIOS is utterly broken */
+ { 0x14, 0x0121411f }, /* HP */
+ { 0x15, 0x411111f0 }, /* N/A */
+ { 0x16, 0x411111f0 }, /* N/A */
+ { 0x17, 0x411111f0 }, /* N/A */
+ { 0x18, 0x90a60160 }, /* mic */
+ { 0x19, 0x411111f0 }, /* N/A */
+ { 0x1a, 0x411111f0 }, /* N/A */
+ { 0x1b, 0x411111f0 }, /* N/A */
+ { 0x1c, 0x411111f0 }, /* N/A */
+ { 0x1d, 0x411111f0 }, /* N/A */
+ { 0x1e, 0xb743111e }, /* SPDIF out */
+ { }
},
+ .chained = true,
+ .chain_id = ALC880_FIXUP_GPIO1,
+ },
+ [ALC880_FIXUP_3ST_BASE] = {
+ .type = HDA_FIXUP_PINS,
+ .v.pins = (const struct hda_pintbl[]) {
+ { 0x14, 0x01014010 }, /* line-out */
+ { 0x15, 0x411111f0 }, /* N/A */
+ { 0x16, 0x411111f0 }, /* N/A */
+ { 0x17, 0x411111f0 }, /* N/A */
+ { 0x18, 0x01a19c30 }, /* mic-in */
+ { 0x19, 0x0121411f }, /* HP */
+ { 0x1a, 0x01813031 }, /* line-in */
+ { 0x1b, 0x02a19c40 }, /* front-mic */
+ { 0x1c, 0x411111f0 }, /* N/A */
+ { 0x1d, 0x411111f0 }, /* N/A */
+ /* 0x1e is filled in below */
+ { 0x1f, 0x411111f0 }, /* N/A */
+ { }
+ }
},
-};
-
-/* Maxdata Favorit 100XS */
-static struct hda_input_mux alc260_favorit100_capture_sources[2] = {
- {
- .num_items = 2,
- .items = {
- { "Line/Mic", 0x0 },
- { "CD", 0x4 },
+ [ALC880_FIXUP_3ST] = {
+ .type = HDA_FIXUP_PINS,
+ .v.pins = (const struct hda_pintbl[]) {
+ { 0x1e, 0x411111f0 }, /* N/A */
+ { }
},
+ .chained = true,
+ .chain_id = ALC880_FIXUP_3ST_BASE,
},
- {
- .num_items = 3,
- .items = {
- { "Line/Mic", 0x0 },
- { "CD", 0x4 },
- { "Mixer", 0x5 },
+ [ALC880_FIXUP_3ST_DIG] = {
+ .type = HDA_FIXUP_PINS,
+ .v.pins = (const struct hda_pintbl[]) {
+ { 0x1e, 0x0144111e }, /* SPDIF */
+ { }
},
+ .chained = true,
+ .chain_id = ALC880_FIXUP_3ST_BASE,
+ },
+ [ALC880_FIXUP_5ST_BASE] = {
+ .type = HDA_FIXUP_PINS,
+ .v.pins = (const struct hda_pintbl[]) {
+ { 0x14, 0x01014010 }, /* front */
+ { 0x15, 0x411111f0 }, /* N/A */
+ { 0x16, 0x01011411 }, /* CLFE */
+ { 0x17, 0x01016412 }, /* surr */
+ { 0x18, 0x01a19c30 }, /* mic-in */
+ { 0x19, 0x0121411f }, /* HP */
+ { 0x1a, 0x01813031 }, /* line-in */
+ { 0x1b, 0x02a19c40 }, /* front-mic */
+ { 0x1c, 0x411111f0 }, /* N/A */
+ { 0x1d, 0x411111f0 }, /* N/A */
+ /* 0x1e is filled in below */
+ { 0x1f, 0x411111f0 }, /* N/A */
+ { }
+ }
},
-};
-
-/*
- * This is just place-holder, so there's something for alc_build_pcms to look
- * at when it calculates the maximum number of channels. ALC260 has no mixer
- * element which allows changing the channel mode, so the verb list is
- * never used.
- */
-static struct hda_channel_mode alc260_modes[1] = {
- { 2, NULL },
-};
-
-
-/* Mixer combinations
- *
- * basic: base_output + input + pc_beep + capture
- * HP: base_output + input + capture_alt
- * HP_3013: hp_3013 + input + capture
- * fujitsu: fujitsu + capture
- * acer: acer + capture
- */
-
-static struct snd_kcontrol_new alc260_base_output_mixer[] = {
- HDA_CODEC_VOLUME("Front Playback Volume", 0x08, 0x0, HDA_OUTPUT),
- HDA_BIND_MUTE("Front Playback Switch", 0x08, 2, HDA_INPUT),
- HDA_CODEC_VOLUME("Headphone Playback Volume", 0x09, 0x0, HDA_OUTPUT),
- HDA_BIND_MUTE("Headphone Playback Switch", 0x09, 2, HDA_INPUT),
- HDA_CODEC_VOLUME_MONO("Mono Playback Volume", 0x0a, 1, 0x0, HDA_OUTPUT),
- HDA_BIND_MUTE_MONO("Mono Playback Switch", 0x0a, 1, 2, HDA_INPUT),
- { } /* end */
-};
-
-static struct snd_kcontrol_new alc260_input_mixer[] = {
- HDA_CODEC_VOLUME("CD Playback Volume", 0x07, 0x04, HDA_INPUT),
- HDA_CODEC_MUTE("CD Playback Switch", 0x07, 0x04, HDA_INPUT),
- HDA_CODEC_VOLUME("Line Playback Volume", 0x07, 0x02, HDA_INPUT),
- HDA_CODEC_MUTE("Line Playback Switch", 0x07, 0x02, HDA_INPUT),
- HDA_CODEC_VOLUME("Mic Playback Volume", 0x07, 0x0, HDA_INPUT),
- HDA_CODEC_MUTE("Mic Playback Switch", 0x07, 0x0, HDA_INPUT),
- HDA_CODEC_VOLUME("Front Mic Playback Volume", 0x07, 0x01, HDA_INPUT),
- HDA_CODEC_MUTE("Front Mic Playback Switch", 0x07, 0x01, HDA_INPUT),
- { } /* end */
-};
-
-/* update HP, line and mono out pins according to the master switch */
-static void alc260_hp_master_update(struct hda_codec *codec,
- hda_nid_t hp, hda_nid_t line,
- hda_nid_t mono)
-{
- struct alc_spec *spec = codec->spec;
- unsigned int val = spec->master_sw ? PIN_HP : 0;
- /* change HP and line-out pins */
- snd_hda_codec_write(codec, hp, 0, AC_VERB_SET_PIN_WIDGET_CONTROL,
- val);
- snd_hda_codec_write(codec, line, 0, AC_VERB_SET_PIN_WIDGET_CONTROL,
- val);
- /* mono (speaker) depending on the HP jack sense */
- val = (val && !spec->jack_present) ? PIN_OUT : 0;
- snd_hda_codec_write(codec, mono, 0, AC_VERB_SET_PIN_WIDGET_CONTROL,
- val);
-}
-
-static int alc260_hp_master_sw_get(struct snd_kcontrol *kcontrol,
- struct snd_ctl_elem_value *ucontrol)
-{
- struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
- struct alc_spec *spec = codec->spec;
- *ucontrol->value.integer.value = spec->master_sw;
- return 0;
-}
-
-static int alc260_hp_master_sw_put(struct snd_kcontrol *kcontrol,
- struct snd_ctl_elem_value *ucontrol)
-{
- struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
- struct alc_spec *spec = codec->spec;
- int val = !!*ucontrol->value.integer.value;
- hda_nid_t hp, line, mono;
-
- if (val == spec->master_sw)
- return 0;
- spec->master_sw = val;
- hp = (kcontrol->private_value >> 16) & 0xff;
- line = (kcontrol->private_value >> 8) & 0xff;
- mono = kcontrol->private_value & 0xff;
- alc260_hp_master_update(codec, hp, line, mono);
- return 1;
-}
-
-static struct snd_kcontrol_new alc260_hp_output_mixer[] = {
- {
- .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
- .name = "Master Playback Switch",
- .subdevice = HDA_SUBDEV_NID_FLAG | 0x11,
- .info = snd_ctl_boolean_mono_info,
- .get = alc260_hp_master_sw_get,
- .put = alc260_hp_master_sw_put,
- .private_value = (0x0f << 16) | (0x10 << 8) | 0x11
+ [ALC880_FIXUP_5ST] = {
+ .type = HDA_FIXUP_PINS,
+ .v.pins = (const struct hda_pintbl[]) {
+ { 0x1e, 0x411111f0 }, /* N/A */
+ { }
+ },
+ .chained = true,
+ .chain_id = ALC880_FIXUP_5ST_BASE,
},
- HDA_CODEC_VOLUME("Front Playback Volume", 0x08, 0x0, HDA_OUTPUT),
- HDA_BIND_MUTE("Front Playback Switch", 0x08, 2, HDA_INPUT),
- HDA_CODEC_VOLUME("Headphone Playback Volume", 0x09, 0x0, HDA_OUTPUT),
- HDA_BIND_MUTE("Headphone Playback Switch", 0x09, 2, HDA_INPUT),
- HDA_CODEC_VOLUME_MONO("Speaker Playback Volume", 0x0a, 1, 0x0,
- HDA_OUTPUT),
- HDA_BIND_MUTE_MONO("Speaker Playback Switch", 0x0a, 1, 2, HDA_INPUT),
- { } /* end */
-};
-
-static struct hda_verb alc260_hp_unsol_verbs[] = {
- {0x10, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | ALC880_HP_EVENT},
- {},
-};
-
-static void alc260_hp_automute(struct hda_codec *codec)
-{
- struct alc_spec *spec = codec->spec;
-
- spec->jack_present = snd_hda_jack_detect(codec, 0x10);
- alc260_hp_master_update(codec, 0x0f, 0x10, 0x11);
-}
-
-static void alc260_hp_unsol_event(struct hda_codec *codec, unsigned int res)
-{
- if ((res >> 26) == ALC880_HP_EVENT)
- alc260_hp_automute(codec);
-}
-
-static struct snd_kcontrol_new alc260_hp_3013_mixer[] = {
- {
- .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
- .name = "Master Playback Switch",
- .subdevice = HDA_SUBDEV_NID_FLAG | 0x11,
- .info = snd_ctl_boolean_mono_info,
- .get = alc260_hp_master_sw_get,
- .put = alc260_hp_master_sw_put,
- .private_value = (0x15 << 16) | (0x10 << 8) | 0x11
+ [ALC880_FIXUP_5ST_DIG] = {
+ .type = HDA_FIXUP_PINS,
+ .v.pins = (const struct hda_pintbl[]) {
+ { 0x1e, 0x0144111e }, /* SPDIF */
+ { }
+ },
+ .chained = true,
+ .chain_id = ALC880_FIXUP_5ST_BASE,
+ },
+ [ALC880_FIXUP_6ST_BASE] = {
+ .type = HDA_FIXUP_PINS,
+ .v.pins = (const struct hda_pintbl[]) {
+ { 0x14, 0x01014010 }, /* front */
+ { 0x15, 0x01016412 }, /* surr */
+ { 0x16, 0x01011411 }, /* CLFE */
+ { 0x17, 0x01012414 }, /* side */
+ { 0x18, 0x01a19c30 }, /* mic-in */
+ { 0x19, 0x02a19c40 }, /* front-mic */
+ { 0x1a, 0x01813031 }, /* line-in */
+ { 0x1b, 0x0121411f }, /* HP */
+ { 0x1c, 0x411111f0 }, /* N/A */
+ { 0x1d, 0x411111f0 }, /* N/A */
+ /* 0x1e is filled in below */
+ { 0x1f, 0x411111f0 }, /* N/A */
+ { }
+ }
},
- HDA_CODEC_VOLUME("Front Playback Volume", 0x09, 0x0, HDA_OUTPUT),
- HDA_CODEC_MUTE("Front Playback Switch", 0x10, 0x0, HDA_OUTPUT),
- HDA_CODEC_VOLUME("Aux-In Playback Volume", 0x07, 0x06, HDA_INPUT),
- HDA_CODEC_MUTE("Aux-In Playback Switch", 0x07, 0x06, HDA_INPUT),
- HDA_CODEC_VOLUME("Headphone Playback Volume", 0x08, 0x0, HDA_OUTPUT),
- HDA_CODEC_MUTE("Headphone Playback Switch", 0x15, 0x0, HDA_OUTPUT),
- HDA_CODEC_VOLUME_MONO("Speaker Playback Volume", 0x0a, 1, 0x0, HDA_OUTPUT),
- HDA_CODEC_MUTE_MONO("Speaker Playback Switch", 0x11, 1, 0x0, HDA_OUTPUT),
- { } /* end */
-};
-
-static struct hda_bind_ctls alc260_dc7600_bind_master_vol = {
- .ops = &snd_hda_bind_vol,
- .values = {
- HDA_COMPOSE_AMP_VAL(0x08, 3, 0, HDA_OUTPUT),
- HDA_COMPOSE_AMP_VAL(0x09, 3, 0, HDA_OUTPUT),
- HDA_COMPOSE_AMP_VAL(0x0a, 3, 0, HDA_OUTPUT),
- 0
+ [ALC880_FIXUP_6ST] = {
+ .type = HDA_FIXUP_PINS,
+ .v.pins = (const struct hda_pintbl[]) {
+ { 0x1e, 0x411111f0 }, /* N/A */
+ { }
+ },
+ .chained = true,
+ .chain_id = ALC880_FIXUP_6ST_BASE,
},
-};
-
-static struct hda_bind_ctls alc260_dc7600_bind_switch = {
- .ops = &snd_hda_bind_sw,
- .values = {
- HDA_COMPOSE_AMP_VAL(0x11, 3, 0, HDA_OUTPUT),
- HDA_COMPOSE_AMP_VAL(0x15, 3, 0, HDA_OUTPUT),
- 0
+ [ALC880_FIXUP_6ST_DIG] = {
+ .type = HDA_FIXUP_PINS,
+ .v.pins = (const struct hda_pintbl[]) {
+ { 0x1e, 0x0144111e }, /* SPDIF */
+ { }
+ },
+ .chained = true,
+ .chain_id = ALC880_FIXUP_6ST_BASE,
},
-};
-
-static struct snd_kcontrol_new alc260_hp_dc7600_mixer[] = {
- HDA_BIND_VOL("Master Playback Volume", &alc260_dc7600_bind_master_vol),
- HDA_BIND_SW("LineOut Playback Switch", &alc260_dc7600_bind_switch),
- HDA_CODEC_MUTE("Speaker Playback Switch", 0x0f, 0x0, HDA_OUTPUT),
- HDA_CODEC_MUTE("Headphone Playback Switch", 0x10, 0x0, HDA_OUTPUT),
- { } /* end */
-};
-
-static struct hda_verb alc260_hp_3013_unsol_verbs[] = {
- {0x15, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | ALC880_HP_EVENT},
- {},
-};
-
-static void alc260_hp_3013_automute(struct hda_codec *codec)
-{
- struct alc_spec *spec = codec->spec;
-
- spec->jack_present = snd_hda_jack_detect(codec, 0x15);
- alc260_hp_master_update(codec, 0x15, 0x10, 0x11);
-}
-
-static void alc260_hp_3013_unsol_event(struct hda_codec *codec,
- unsigned int res)
-{
- if ((res >> 26) == ALC880_HP_EVENT)
- alc260_hp_3013_automute(codec);
-}
-
-static void alc260_hp_3012_automute(struct hda_codec *codec)
-{
- unsigned int bits = snd_hda_jack_detect(codec, 0x10) ? 0 : PIN_OUT;
-
- snd_hda_codec_write(codec, 0x0f, 0, AC_VERB_SET_PIN_WIDGET_CONTROL,
- bits);
- snd_hda_codec_write(codec, 0x11, 0, AC_VERB_SET_PIN_WIDGET_CONTROL,
- bits);
- snd_hda_codec_write(codec, 0x15, 0, AC_VERB_SET_PIN_WIDGET_CONTROL,
- bits);
-}
-
-static void alc260_hp_3012_unsol_event(struct hda_codec *codec,
- unsigned int res)
-{
- if ((res >> 26) == ALC880_HP_EVENT)
- alc260_hp_3012_automute(codec);
-}
-
-/* Fujitsu S702x series laptops. ALC260 pin usage: Mic/Line jack = 0x12,
- * HP jack = 0x14, CD audio = 0x16, internal speaker = 0x10.
- */
-static struct snd_kcontrol_new alc260_fujitsu_mixer[] = {
- HDA_CODEC_VOLUME("Headphone Playback Volume", 0x08, 0x0, HDA_OUTPUT),
- HDA_BIND_MUTE("Headphone Playback Switch", 0x08, 2, HDA_INPUT),
- ALC_PIN_MODE("Headphone Jack Mode", 0x14, ALC_PIN_DIR_INOUT),
- HDA_CODEC_VOLUME("CD Playback Volume", 0x07, 0x04, HDA_INPUT),
- HDA_CODEC_MUTE("CD Playback Switch", 0x07, 0x04, HDA_INPUT),
- HDA_CODEC_VOLUME("Mic/Line Playback Volume", 0x07, 0x0, HDA_INPUT),
- HDA_CODEC_MUTE("Mic/Line Playback Switch", 0x07, 0x0, HDA_INPUT),
- ALC_PIN_MODE("Mic/Line Jack Mode", 0x12, ALC_PIN_DIR_IN),
- HDA_CODEC_VOLUME("Speaker Playback Volume", 0x09, 0x0, HDA_OUTPUT),
- HDA_BIND_MUTE("Speaker Playback Switch", 0x09, 2, HDA_INPUT),
- { } /* end */
-};
-
-/* Mixer for Acer TravelMate(/Extensa/Aspire) notebooks. Note that current
- * versions of the ALC260 don't act on requests to enable mic bias from NID
- * 0x0f (used to drive the headphone jack in these laptops). The ALC260
- * datasheet doesn't mention this restriction. At this stage it's not clear
- * whether this behaviour is intentional or is a hardware bug in chip
- * revisions available in early 2006. Therefore for now allow the
- * "Headphone Jack Mode" control to span all choices, but if it turns out
- * that the lack of mic bias for this NID is intentional we could change the
- * mode from ALC_PIN_DIR_INOUT to ALC_PIN_DIR_INOUT_NOMICBIAS.
- *
- * In addition, Acer TravelMate(/Extensa/Aspire) notebooks in early 2006
- * don't appear to make the mic bias available from the "line" jack, even
- * though the NID used for this jack (0x14) can supply it. The theory is
- * that perhaps Acer have included blocking capacitors between the ALC260
- * and the output jack. If this turns out to be the case for all such
- * models the "Line Jack Mode" mode could be changed from ALC_PIN_DIR_INOUT
- * to ALC_PIN_DIR_INOUT_NOMICBIAS.
- *
- * The C20x Tablet series have a mono internal speaker which is controlled
- * via the chip's Mono sum widget and pin complex, so include the necessary
- * controls for such models. On models without a "mono speaker" the control
- * won't do anything.
- */
-static struct snd_kcontrol_new alc260_acer_mixer[] = {
- HDA_CODEC_VOLUME("Master Playback Volume", 0x08, 0x0, HDA_OUTPUT),
- HDA_BIND_MUTE("Master Playback Switch", 0x08, 2, HDA_INPUT),
- ALC_PIN_MODE("Headphone Jack Mode", 0x0f, ALC_PIN_DIR_INOUT),
- HDA_CODEC_VOLUME_MONO("Speaker Playback Volume", 0x0a, 1, 0x0,
- HDA_OUTPUT),
- HDA_BIND_MUTE_MONO("Speaker Playback Switch", 0x0a, 1, 2,
- HDA_INPUT),
- HDA_CODEC_VOLUME("CD Playback Volume", 0x07, 0x04, HDA_INPUT),
- HDA_CODEC_MUTE("CD Playback Switch", 0x07, 0x04, HDA_INPUT),
- HDA_CODEC_VOLUME("Mic Playback Volume", 0x07, 0x0, HDA_INPUT),
- HDA_CODEC_MUTE("Mic Playback Switch", 0x07, 0x0, HDA_INPUT),
- ALC_PIN_MODE("Mic Jack Mode", 0x12, ALC_PIN_DIR_IN),
- HDA_CODEC_VOLUME("Line Playback Volume", 0x07, 0x02, HDA_INPUT),
- HDA_CODEC_MUTE("Line Playback Switch", 0x07, 0x02, HDA_INPUT),
- ALC_PIN_MODE("Line Jack Mode", 0x14, ALC_PIN_DIR_INOUT),
- { } /* end */
-};
-
-/* Maxdata Favorit 100XS: one output and one input (0x12) jack
- */
-static struct snd_kcontrol_new alc260_favorit100_mixer[] = {
- HDA_CODEC_VOLUME("Master Playback Volume", 0x08, 0x0, HDA_OUTPUT),
- HDA_BIND_MUTE("Master Playback Switch", 0x08, 2, HDA_INPUT),
- ALC_PIN_MODE("Output Jack Mode", 0x0f, ALC_PIN_DIR_INOUT),
- HDA_CODEC_VOLUME("Line/Mic Playback Volume", 0x07, 0x0, HDA_INPUT),
- HDA_CODEC_MUTE("Line/Mic Playback Switch", 0x07, 0x0, HDA_INPUT),
- ALC_PIN_MODE("Line/Mic Jack Mode", 0x12, ALC_PIN_DIR_IN),
- { } /* end */
-};
-
-/* Packard bell V7900 ALC260 pin usage: HP = 0x0f, Mic jack = 0x12,
- * Line In jack = 0x14, CD audio = 0x16, pc beep = 0x17.
- */
-static struct snd_kcontrol_new alc260_will_mixer[] = {
- HDA_CODEC_VOLUME("Master Playback Volume", 0x08, 0x0, HDA_OUTPUT),
- HDA_BIND_MUTE("Master Playback Switch", 0x08, 0x2, HDA_INPUT),
- HDA_CODEC_VOLUME("Mic Playback Volume", 0x07, 0x0, HDA_INPUT),
- HDA_CODEC_MUTE("Mic Playback Switch", 0x07, 0x0, HDA_INPUT),
- ALC_PIN_MODE("Mic Jack Mode", 0x12, ALC_PIN_DIR_IN),
- HDA_CODEC_VOLUME("Line Playback Volume", 0x07, 0x02, HDA_INPUT),
- HDA_CODEC_MUTE("Line Playback Switch", 0x07, 0x02, HDA_INPUT),
- ALC_PIN_MODE("Line Jack Mode", 0x14, ALC_PIN_DIR_INOUT),
- HDA_CODEC_VOLUME("CD Playback Volume", 0x07, 0x04, HDA_INPUT),
- HDA_CODEC_MUTE("CD Playback Switch", 0x07, 0x04, HDA_INPUT),
- { } /* end */
-};
-
-/* Replacer 672V ALC260 pin usage: Mic jack = 0x12,
- * Line In jack = 0x14, ATAPI Mic = 0x13, speaker = 0x0f.
- */
-static struct snd_kcontrol_new alc260_replacer_672v_mixer[] = {
- HDA_CODEC_VOLUME("Master Playback Volume", 0x08, 0x0, HDA_OUTPUT),
- HDA_BIND_MUTE("Master Playback Switch", 0x08, 0x2, HDA_INPUT),
- HDA_CODEC_VOLUME("Mic Playback Volume", 0x07, 0x0, HDA_INPUT),
- HDA_CODEC_MUTE("Mic Playback Switch", 0x07, 0x0, HDA_INPUT),
- ALC_PIN_MODE("Mic Jack Mode", 0x12, ALC_PIN_DIR_IN),
- HDA_CODEC_VOLUME("ATAPI Mic Playback Volume", 0x07, 0x1, HDA_INPUT),
- HDA_CODEC_MUTE("ATATI Mic Playback Switch", 0x07, 0x1, HDA_INPUT),
- HDA_CODEC_VOLUME("Line Playback Volume", 0x07, 0x02, HDA_INPUT),
- HDA_CODEC_MUTE("Line Playback Switch", 0x07, 0x02, HDA_INPUT),
- ALC_PIN_MODE("Line Jack Mode", 0x14, ALC_PIN_DIR_INOUT),
- { } /* end */
-};
-
-/*
- * initialization verbs
- */
-static struct hda_verb alc260_init_verbs[] = {
- /* Line In pin widget for input */
- {0x14, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN},
- /* CD pin widget for input */
- {0x16, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN},
- /* Mic1 (rear panel) pin widget for input and vref at 80% */
- {0x12, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80},
- /* Mic2 (front panel) pin widget for input and vref at 80% */
- {0x13, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80},
- /* LINE-2 is used for line-out in rear */
- {0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
- /* select line-out */
- {0x0e, AC_VERB_SET_CONNECT_SEL, 0x00},
- /* LINE-OUT pin */
- {0x0f, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
- /* enable HP */
- {0x10, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP},
- /* enable Mono */
- {0x11, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
- /* mute capture amp left and right */
- {0x04, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
- /* set connection select to line in (default select for this ADC) */
- {0x04, AC_VERB_SET_CONNECT_SEL, 0x02},
- /* mute capture amp left and right */
- {0x05, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
- /* set connection select to line in (default select for this ADC) */
- {0x05, AC_VERB_SET_CONNECT_SEL, 0x02},
- /* set vol=0 Line-Out mixer amp left and right */
- {0x08, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
- /* unmute pin widget amp left and right (no gain on this amp) */
- {0x0f, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
- /* set vol=0 HP mixer amp left and right */
- {0x09, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
- /* unmute pin widget amp left and right (no gain on this amp) */
- {0x10, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
- /* set vol=0 Mono mixer amp left and right */
- {0x0a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
- /* unmute pin widget amp left and right (no gain on this amp) */
- {0x11, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
- /* unmute LINE-2 out pin */
- {0x15, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
- /* Amp Indexes: CD = 0x04, Line In 1 = 0x02, Mic 1 = 0x00 &
- * Line In 2 = 0x03
- */
- /* mute analog inputs */
- {0x07, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
- {0x07, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
- {0x07, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(2)},
- {0x07, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(3)},
- {0x07, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(4)},
- /* Amp Indexes: DAC = 0x01 & mixer = 0x00 */
- /* mute Front out path */
- {0x08, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
- {0x08, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
- /* mute Headphone out path */
- {0x09, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
- {0x09, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
- /* mute Mono out path */
- {0x0a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
- {0x0a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
- { }
-};
-
-#if 0 /* should be identical with alc260_init_verbs? */
-static struct hda_verb alc260_hp_init_verbs[] = {
- /* Headphone and output */
- {0x10, AC_VERB_SET_PIN_WIDGET_CONTROL, 0xc0},
- /* mono output */
- {0x11, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x40},
- /* Mic1 (rear panel) pin widget for input and vref at 80% */
- {0x12, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x24},
- /* Mic2 (front panel) pin widget for input and vref at 80% */
- {0x13, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x24},
- /* Line In pin widget for input */
- {0x14, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x20},
- /* Line-2 pin widget for output */
- {0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x40},
- /* CD pin widget for input */
- {0x16, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x20},
- /* unmute amp left and right */
- {0x04, AC_VERB_SET_AMP_GAIN_MUTE, 0x7000},
- /* set connection select to line in (default select for this ADC) */
- {0x04, AC_VERB_SET_CONNECT_SEL, 0x02},
- /* unmute Line-Out mixer amp left and right (volume = 0) */
- {0x08, AC_VERB_SET_AMP_GAIN_MUTE, 0xb000},
- /* mute pin widget amp left and right (no gain on this amp) */
- {0x15, AC_VERB_SET_AMP_GAIN_MUTE, 0x0000},
- /* unmute HP mixer amp left and right (volume = 0) */
- {0x09, AC_VERB_SET_AMP_GAIN_MUTE, 0xb000},
- /* mute pin widget amp left and right (no gain on this amp) */
- {0x10, AC_VERB_SET_AMP_GAIN_MUTE, 0x0000},
- /* Amp Indexes: CD = 0x04, Line In 1 = 0x02, Mic 1 = 0x00 &
- * Line In 2 = 0x03
- */
- /* mute analog inputs */
- {0x07, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
- {0x07, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
- {0x07, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(2)},
- {0x07, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(3)},
- {0x07, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(4)},
- /* Amp Indexes: DAC = 0x01 & mixer = 0x00 */
- /* Unmute Front out path */
- {0x08, AC_VERB_SET_AMP_GAIN_MUTE, (0x7000 | (0x00 << 8))},
- {0x08, AC_VERB_SET_AMP_GAIN_MUTE, (0x7000 | (0x01 << 8))},
- /* Unmute Headphone out path */
- {0x09, AC_VERB_SET_AMP_GAIN_MUTE, (0x7000 | (0x00 << 8))},
- {0x09, AC_VERB_SET_AMP_GAIN_MUTE, (0x7000 | (0x01 << 8))},
- /* Unmute Mono out path */
- {0x0a, AC_VERB_SET_AMP_GAIN_MUTE, (0x7000 | (0x00 << 8))},
- {0x0a, AC_VERB_SET_AMP_GAIN_MUTE, (0x7000 | (0x01 << 8))},
- { }
-};
-#endif
-
-static struct hda_verb alc260_hp_3013_init_verbs[] = {
- /* Line out and output */
- {0x10, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x40},
- /* mono output */
- {0x11, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x40},
- /* Mic1 (rear panel) pin widget for input and vref at 80% */
- {0x12, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x24},
- /* Mic2 (front panel) pin widget for input and vref at 80% */
- {0x13, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x24},
- /* Line In pin widget for input */
- {0x14, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x20},
- /* Headphone pin widget for output */
- {0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, 0xc0},
- /* CD pin widget for input */
- {0x16, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x20},
- /* unmute amp left and right */
- {0x04, AC_VERB_SET_AMP_GAIN_MUTE, 0x7000},
- /* set connection select to line in (default select for this ADC) */
- {0x04, AC_VERB_SET_CONNECT_SEL, 0x02},
- /* unmute Line-Out mixer amp left and right (volume = 0) */
- {0x08, AC_VERB_SET_AMP_GAIN_MUTE, 0xb000},
- /* mute pin widget amp left and right (no gain on this amp) */
- {0x15, AC_VERB_SET_AMP_GAIN_MUTE, 0x0000},
- /* unmute HP mixer amp left and right (volume = 0) */
- {0x09, AC_VERB_SET_AMP_GAIN_MUTE, 0xb000},
- /* mute pin widget amp left and right (no gain on this amp) */
- {0x10, AC_VERB_SET_AMP_GAIN_MUTE, 0x0000},
- /* Amp Indexes: CD = 0x04, Line In 1 = 0x02, Mic 1 = 0x00 &
- * Line In 2 = 0x03
- */
- /* mute analog inputs */
- {0x07, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
- {0x07, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
- {0x07, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(2)},
- {0x07, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(3)},
- {0x07, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(4)},
- /* Amp Indexes: DAC = 0x01 & mixer = 0x00 */
- /* Unmute Front out path */
- {0x08, AC_VERB_SET_AMP_GAIN_MUTE, (0x7000 | (0x00 << 8))},
- {0x08, AC_VERB_SET_AMP_GAIN_MUTE, (0x7000 | (0x01 << 8))},
- /* Unmute Headphone out path */
- {0x09, AC_VERB_SET_AMP_GAIN_MUTE, (0x7000 | (0x00 << 8))},
- {0x09, AC_VERB_SET_AMP_GAIN_MUTE, (0x7000 | (0x01 << 8))},
- /* Unmute Mono out path */
- {0x0a, AC_VERB_SET_AMP_GAIN_MUTE, (0x7000 | (0x00 << 8))},
- {0x0a, AC_VERB_SET_AMP_GAIN_MUTE, (0x7000 | (0x01 << 8))},
- { }
-};
-
-/* Initialisation sequence for ALC260 as configured in Fujitsu S702x
- * laptops. ALC260 pin usage: Mic/Line jack = 0x12, HP jack = 0x14, CD
- * audio = 0x16, internal speaker = 0x10.
- */
-static struct hda_verb alc260_fujitsu_init_verbs[] = {
- /* Disable all GPIOs */
- {0x01, AC_VERB_SET_GPIO_MASK, 0},
- /* Internal speaker is connected to headphone pin */
- {0x10, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP},
- /* Headphone/Line-out jack connects to Line1 pin; make it an output */
- {0x14, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
- /* Mic/Line-in jack is connected to mic1 pin, so make it an input */
- {0x12, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN},
- /* Ensure all other unused pins are disabled and muted. */
- {0x0f, AC_VERB_SET_PIN_WIDGET_CONTROL, 0},
- {0x0f, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
- {0x11, AC_VERB_SET_PIN_WIDGET_CONTROL, 0},
- {0x11, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
- {0x13, AC_VERB_SET_PIN_WIDGET_CONTROL, 0},
- {0x13, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
- {0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, 0},
- {0x15, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
-
- /* Disable digital (SPDIF) pins */
- {0x03, AC_VERB_SET_DIGI_CONVERT_1, 0},
- {0x06, AC_VERB_SET_DIGI_CONVERT_1, 0},
-
- /* Ensure Line1 pin widget takes its input from the OUT1 sum bus
- * when acting as an output.
- */
- {0x0d, AC_VERB_SET_CONNECT_SEL, 0},
-
- /* Start with output sum widgets muted and their output gains at min */
- {0x08, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
- {0x08, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
- {0x08, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
- {0x09, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
- {0x09, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
- {0x09, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
- {0x0a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
- {0x0a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
- {0x0a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
-
- /* Unmute HP pin widget amp left and right (no equiv mixer ctrl) */
- {0x10, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
- /* Unmute Line1 pin widget output buffer since it starts as an output.
- * If the pin mode is changed by the user the pin mode control will
- * take care of enabling the pin's input/output buffers as needed.
- * Therefore there's no need to enable the input buffer at this
- * stage.
- */
- {0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
- /* Unmute input buffer of pin widget used for Line-in (no equiv
- * mixer ctrl)
- */
- {0x12, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
-
- /* Mute capture amp left and right */
- {0x04, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
- /* Set ADC connection select to match default mixer setting - line
- * in (on mic1 pin)
- */
- {0x04, AC_VERB_SET_CONNECT_SEL, 0x00},
-
- /* Do the same for the second ADC: mute capture input amp and
- * set ADC connection to line in (on mic1 pin)
- */
- {0x05, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
- {0x05, AC_VERB_SET_CONNECT_SEL, 0x00},
-
- /* Mute all inputs to mixer widget (even unconnected ones) */
- {0x07, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)}, /* mic1 pin */
- {0x07, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)}, /* mic2 pin */
- {0x07, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(2)}, /* line1 pin */
- {0x07, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(3)}, /* line2 pin */
- {0x07, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(4)}, /* CD pin */
- {0x07, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(5)}, /* Beep-gen pin */
- {0x07, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(6)}, /* Line-out pin */
- {0x07, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(7)}, /* HP-pin pin */
-
- { }
-};
-
-/* Initialisation sequence for ALC260 as configured in Acer TravelMate and
- * similar laptops (adapted from Fujitsu init verbs).
- */
-static struct hda_verb alc260_acer_init_verbs[] = {
- /* On TravelMate laptops, GPIO 0 enables the internal speaker and
- * the headphone jack. Turn this on and rely on the standard mute
- * methods whenever the user wants to turn these outputs off.
- */
- {0x01, AC_VERB_SET_GPIO_MASK, 0x01},
- {0x01, AC_VERB_SET_GPIO_DIRECTION, 0x01},
- {0x01, AC_VERB_SET_GPIO_DATA, 0x01},
- /* Internal speaker/Headphone jack is connected to Line-out pin */
- {0x0f, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP},
- /* Internal microphone/Mic jack is connected to Mic1 pin */
- {0x12, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF50},
- /* Line In jack is connected to Line1 pin */
- {0x14, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN},
- /* Some Acers (eg: C20x Tablets) use Mono pin for internal speaker */
- {0x11, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP},
- /* Ensure all other unused pins are disabled and muted. */
- {0x10, AC_VERB_SET_PIN_WIDGET_CONTROL, 0},
- {0x10, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
- {0x13, AC_VERB_SET_PIN_WIDGET_CONTROL, 0},
- {0x13, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
- {0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, 0},
- {0x15, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
- /* Disable digital (SPDIF) pins */
- {0x03, AC_VERB_SET_DIGI_CONVERT_1, 0},
- {0x06, AC_VERB_SET_DIGI_CONVERT_1, 0},
-
- /* Ensure Mic1 and Line1 pin widgets take input from the OUT1 sum
- * bus when acting as outputs.
- */
- {0x0b, AC_VERB_SET_CONNECT_SEL, 0},
- {0x0d, AC_VERB_SET_CONNECT_SEL, 0},
-
- /* Start with output sum widgets muted and their output gains at min */
- {0x08, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
- {0x08, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
- {0x08, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
- {0x09, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
- {0x09, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
- {0x09, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
- {0x0a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
- {0x0a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
- {0x0a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
-
- /* Unmute Line-out pin widget amp left and right
- * (no equiv mixer ctrl)
- */
- {0x0f, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
- /* Unmute mono pin widget amp output (no equiv mixer ctrl) */
- {0x11, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
- /* Unmute Mic1 and Line1 pin widget input buffers since they start as
- * inputs. If the pin mode is changed by the user the pin mode control
- * will take care of enabling the pin's input/output buffers as needed.
- * Therefore there's no need to enable the input buffer at this
- * stage.
- */
- {0x12, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
- {0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
-
- /* Mute capture amp left and right */
- {0x04, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
- /* Set ADC connection select to match default mixer setting - mic
- * (on mic1 pin)
- */
- {0x04, AC_VERB_SET_CONNECT_SEL, 0x00},
-
- /* Do similar with the second ADC: mute capture input amp and
- * set ADC connection to mic to match ALSA's default state.
- */
- {0x05, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
- {0x05, AC_VERB_SET_CONNECT_SEL, 0x00},
-
- /* Mute all inputs to mixer widget (even unconnected ones) */
- {0x07, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)}, /* mic1 pin */
- {0x07, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)}, /* mic2 pin */
- {0x07, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(2)}, /* line1 pin */
- {0x07, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(3)}, /* line2 pin */
- {0x07, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(4)}, /* CD pin */
- {0x07, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(5)}, /* Beep-gen pin */
- {0x07, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(6)}, /* Line-out pin */
- {0x07, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(7)}, /* HP-pin pin */
-
- { }
-};
-
-/* Initialisation sequence for Maxdata Favorit 100XS
- * (adapted from Acer init verbs).
- */
-static struct hda_verb alc260_favorit100_init_verbs[] = {
- /* GPIO 0 enables the output jack.
- * Turn this on and rely on the standard mute
- * methods whenever the user wants to turn these outputs off.
- */
- {0x01, AC_VERB_SET_GPIO_MASK, 0x01},
- {0x01, AC_VERB_SET_GPIO_DIRECTION, 0x01},
- {0x01, AC_VERB_SET_GPIO_DATA, 0x01},
- /* Line/Mic input jack is connected to Mic1 pin */
- {0x12, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF50},
- /* Ensure all other unused pins are disabled and muted. */
- {0x10, AC_VERB_SET_PIN_WIDGET_CONTROL, 0},
- {0x10, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
- {0x11, AC_VERB_SET_PIN_WIDGET_CONTROL, 0},
- {0x11, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
- {0x13, AC_VERB_SET_PIN_WIDGET_CONTROL, 0},
- {0x13, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
- {0x14, AC_VERB_SET_PIN_WIDGET_CONTROL, 0},
- {0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
- {0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, 0},
- {0x15, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
- /* Disable digital (SPDIF) pins */
- {0x03, AC_VERB_SET_DIGI_CONVERT_1, 0},
- {0x06, AC_VERB_SET_DIGI_CONVERT_1, 0},
-
- /* Ensure Mic1 and Line1 pin widgets take input from the OUT1 sum
- * bus when acting as outputs.
- */
- {0x0b, AC_VERB_SET_CONNECT_SEL, 0},
- {0x0d, AC_VERB_SET_CONNECT_SEL, 0},
-
- /* Start with output sum widgets muted and their output gains at min */
- {0x08, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
- {0x08, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
- {0x08, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
- {0x09, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
- {0x09, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
- {0x09, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
- {0x0a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
- {0x0a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
- {0x0a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
-
- /* Unmute Line-out pin widget amp left and right
- * (no equiv mixer ctrl)
- */
- {0x0f, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
- /* Unmute Mic1 and Line1 pin widget input buffers since they start as
- * inputs. If the pin mode is changed by the user the pin mode control
- * will take care of enabling the pin's input/output buffers as needed.
- * Therefore there's no need to enable the input buffer at this
- * stage.
- */
- {0x12, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
-
- /* Mute capture amp left and right */
- {0x04, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
- /* Set ADC connection select to match default mixer setting - mic
- * (on mic1 pin)
- */
- {0x04, AC_VERB_SET_CONNECT_SEL, 0x00},
-
- /* Do similar with the second ADC: mute capture input amp and
- * set ADC connection to mic to match ALSA's default state.
+ [ALC880_FIXUP_6ST_AUTOMUTE] = {
+ .type = HDA_FIXUP_PINS,
+ .v.pins = (const struct hda_pintbl[]) {
+ { 0x1b, 0x0121401f }, /* HP with jack detect */
+ { }
+ },
+ .chained_before = true,
+ .chain_id = ALC880_FIXUP_6ST_BASE,
+ },
+};
+
+static const struct snd_pci_quirk alc880_fixup_tbl[] = {
+ SND_PCI_QUIRK(0x1019, 0x0f69, "Coeus G610P", ALC880_FIXUP_W810),
+ SND_PCI_QUIRK(0x1043, 0x10c3, "ASUS W5A", ALC880_FIXUP_ASUS_W5A),
+ SND_PCI_QUIRK(0x1043, 0x1964, "ASUS Z71V", ALC880_FIXUP_Z71V),
+ SND_PCI_QUIRK_VENDOR(0x1043, "ASUS", ALC880_FIXUP_GPIO1),
+ SND_PCI_QUIRK(0x147b, 0x1045, "ABit AA8XE", ALC880_FIXUP_6ST_AUTOMUTE),
+ SND_PCI_QUIRK(0x1558, 0x5401, "Clevo GPIO2", ALC880_FIXUP_GPIO2),
+ SND_PCI_QUIRK_VENDOR(0x1558, "Clevo", ALC880_FIXUP_EAPD_COEF),
+ SND_PCI_QUIRK(0x1584, 0x9050, "Uniwill", ALC880_FIXUP_UNIWILL_DIG),
+ SND_PCI_QUIRK(0x1584, 0x9054, "Uniwill", ALC880_FIXUP_F1734),
+ SND_PCI_QUIRK(0x1584, 0x9070, "Uniwill", ALC880_FIXUP_UNIWILL),
+ SND_PCI_QUIRK(0x1584, 0x9077, "Uniwill P53", ALC880_FIXUP_VOL_KNOB),
+ SND_PCI_QUIRK(0x161f, 0x203d, "W810", ALC880_FIXUP_W810),
+ SND_PCI_QUIRK(0x161f, 0x205d, "Medion Rim 2150", ALC880_FIXUP_MEDION_RIM),
+ SND_PCI_QUIRK(0x1631, 0xe011, "PB 13201056", ALC880_FIXUP_6ST_AUTOMUTE),
+ SND_PCI_QUIRK(0x1734, 0x107c, "FSC F1734", ALC880_FIXUP_F1734),
+ SND_PCI_QUIRK(0x1734, 0x1094, "FSC Amilo M1451G", ALC880_FIXUP_FUJITSU),
+ SND_PCI_QUIRK(0x1734, 0x10ac, "FSC AMILO Xi 1526", ALC880_FIXUP_F1734),
+ SND_PCI_QUIRK(0x1734, 0x10b0, "FSC Amilo Pi1556", ALC880_FIXUP_FUJITSU),
+ SND_PCI_QUIRK(0x1854, 0x003b, "LG", ALC880_FIXUP_LG),
+ SND_PCI_QUIRK(0x1854, 0x005f, "LG P1 Express", ALC880_FIXUP_LG),
+ SND_PCI_QUIRK(0x1854, 0x0068, "LG w1", ALC880_FIXUP_LG),
+ SND_PCI_QUIRK(0x1854, 0x0077, "LG LW25", ALC880_FIXUP_LG_LW25),
+ SND_PCI_QUIRK(0x19db, 0x4188, "TCL S700", ALC880_FIXUP_TCL_S700),
+
+ /* Below is the copied entries from alc880_quirks.c.
+ * It's not quite sure whether BIOS sets the correct pin-config table
+ * on these machines, thus they are kept to be compatible with
+ * the old static quirks. Once when it's confirmed to work without
+ * these overrides, it'd be better to remove.
*/
- {0x05, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
- {0x05, AC_VERB_SET_CONNECT_SEL, 0x00},
-
- /* Mute all inputs to mixer widget (even unconnected ones) */
- {0x07, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)}, /* mic1 pin */
- {0x07, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)}, /* mic2 pin */
- {0x07, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(2)}, /* line1 pin */
- {0x07, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(3)}, /* line2 pin */
- {0x07, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(4)}, /* CD pin */
- {0x07, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(5)}, /* Beep-gen pin */
- {0x07, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(6)}, /* Line-out pin */
- {0x07, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(7)}, /* HP-pin pin */
-
- { }
-};
-
-static struct hda_verb alc260_will_verbs[] = {
- {0x0f, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP},
- {0x0b, AC_VERB_SET_CONNECT_SEL, 0x00},
- {0x0d, AC_VERB_SET_CONNECT_SEL, 0x00},
- {0x0f, AC_VERB_SET_EAPD_BTLENABLE, 0x02},
- {0x1a, AC_VERB_SET_COEF_INDEX, 0x07},
- {0x1a, AC_VERB_SET_PROC_COEF, 0x3040},
- {}
-};
-
-static struct hda_verb alc260_replacer_672v_verbs[] = {
- {0x0f, AC_VERB_SET_EAPD_BTLENABLE, 0x02},
- {0x1a, AC_VERB_SET_COEF_INDEX, 0x07},
- {0x1a, AC_VERB_SET_PROC_COEF, 0x3050},
-
- {0x01, AC_VERB_SET_GPIO_MASK, 0x01},
- {0x01, AC_VERB_SET_GPIO_DIRECTION, 0x01},
- {0x01, AC_VERB_SET_GPIO_DATA, 0x00},
-
- {0x0f, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | ALC880_HP_EVENT},
+ SND_PCI_QUIRK(0x1019, 0xa880, "ECS", ALC880_FIXUP_5ST_DIG),
+ SND_PCI_QUIRK(0x1019, 0xa884, "Acer APFV", ALC880_FIXUP_6ST),
+ SND_PCI_QUIRK(0x1025, 0x0070, "ULI", ALC880_FIXUP_3ST_DIG),
+ SND_PCI_QUIRK(0x1025, 0x0077, "ULI", ALC880_FIXUP_6ST_DIG),
+ SND_PCI_QUIRK(0x1025, 0x0078, "ULI", ALC880_FIXUP_6ST_DIG),
+ SND_PCI_QUIRK(0x1025, 0x0087, "ULI", ALC880_FIXUP_6ST_DIG),
+ SND_PCI_QUIRK(0x1025, 0xe309, "ULI", ALC880_FIXUP_3ST_DIG),
+ SND_PCI_QUIRK(0x1025, 0xe310, "ULI", ALC880_FIXUP_3ST),
+ SND_PCI_QUIRK(0x1039, 0x1234, NULL, ALC880_FIXUP_6ST_DIG),
+ SND_PCI_QUIRK(0x104d, 0x81a0, "Sony", ALC880_FIXUP_3ST),
+ SND_PCI_QUIRK(0x104d, 0x81d6, "Sony", ALC880_FIXUP_3ST),
+ SND_PCI_QUIRK(0x107b, 0x3032, "Gateway", ALC880_FIXUP_5ST),
+ SND_PCI_QUIRK(0x107b, 0x3033, "Gateway", ALC880_FIXUP_5ST),
+ SND_PCI_QUIRK(0x107b, 0x4039, "Gateway", ALC880_FIXUP_5ST),
+ SND_PCI_QUIRK(0x1297, 0xc790, "Shuttle ST20G5", ALC880_FIXUP_6ST_DIG),
+ SND_PCI_QUIRK(0x1458, 0xa102, "Gigabyte K8", ALC880_FIXUP_6ST_DIG),
+ SND_PCI_QUIRK(0x1462, 0x1150, "MSI", ALC880_FIXUP_6ST_DIG),
+ SND_PCI_QUIRK(0x1509, 0x925d, "FIC P4M", ALC880_FIXUP_6ST_DIG),
+ SND_PCI_QUIRK(0x1565, 0x8202, "Biostar", ALC880_FIXUP_5ST_DIG),
+ SND_PCI_QUIRK(0x1695, 0x400d, "EPoX", ALC880_FIXUP_5ST_DIG),
+ SND_PCI_QUIRK(0x1695, 0x4012, "EPox EP-5LDA", ALC880_FIXUP_5ST_DIG),
+ SND_PCI_QUIRK(0x2668, 0x8086, NULL, ALC880_FIXUP_6ST_DIG), /* broken BIOS */
+ SND_PCI_QUIRK(0x8086, 0x2668, NULL, ALC880_FIXUP_6ST_DIG),
+ SND_PCI_QUIRK(0x8086, 0xa100, "Intel mobo", ALC880_FIXUP_5ST_DIG),
+ SND_PCI_QUIRK(0x8086, 0xd400, "Intel mobo", ALC880_FIXUP_5ST_DIG),
+ SND_PCI_QUIRK(0x8086, 0xd401, "Intel mobo", ALC880_FIXUP_5ST_DIG),
+ SND_PCI_QUIRK(0x8086, 0xd402, "Intel mobo", ALC880_FIXUP_3ST_DIG),
+ SND_PCI_QUIRK(0x8086, 0xe224, "Intel mobo", ALC880_FIXUP_5ST_DIG),
+ SND_PCI_QUIRK(0x8086, 0xe305, "Intel mobo", ALC880_FIXUP_3ST_DIG),
+ SND_PCI_QUIRK(0x8086, 0xe308, "Intel mobo", ALC880_FIXUP_3ST_DIG),
+ SND_PCI_QUIRK(0x8086, 0xe400, "Intel mobo", ALC880_FIXUP_5ST_DIG),
+ SND_PCI_QUIRK(0x8086, 0xe401, "Intel mobo", ALC880_FIXUP_5ST_DIG),
+ SND_PCI_QUIRK(0x8086, 0xe402, "Intel mobo", ALC880_FIXUP_5ST_DIG),
+ /* default Intel */
+ SND_PCI_QUIRK_VENDOR(0x8086, "Intel mobo", ALC880_FIXUP_3ST),
+ SND_PCI_QUIRK(0xa0a0, 0x0560, "AOpen i915GMm-HFS", ALC880_FIXUP_5ST_DIG),
+ SND_PCI_QUIRK(0xe803, 0x1019, NULL, ALC880_FIXUP_6ST_DIG),
{}
};
-/* toggle speaker-output according to the hp-jack state */
-static void alc260_replacer_672v_automute(struct hda_codec *codec)
-{
- unsigned int present;
-
- /* speaker --> GPIO Data 0, hp or spdif --> GPIO data 1 */
- present = snd_hda_jack_detect(codec, 0x0f);
- if (present) {
- snd_hda_codec_write_cache(codec, 0x01, 0,
- AC_VERB_SET_GPIO_DATA, 1);
- snd_hda_codec_write_cache(codec, 0x0f, 0,
- AC_VERB_SET_PIN_WIDGET_CONTROL,
- PIN_HP);
- } else {
- snd_hda_codec_write_cache(codec, 0x01, 0,
- AC_VERB_SET_GPIO_DATA, 0);
- snd_hda_codec_write_cache(codec, 0x0f, 0,
- AC_VERB_SET_PIN_WIDGET_CONTROL,
- PIN_OUT);
- }
-}
-
-static void alc260_replacer_672v_unsol_event(struct hda_codec *codec,
- unsigned int res)
-{
- if ((res >> 26) == ALC880_HP_EVENT)
- alc260_replacer_672v_automute(codec);
-}
-
-static struct hda_verb alc260_hp_dc7600_verbs[] = {
- {0x05, AC_VERB_SET_CONNECT_SEL, 0x01},
- {0x15, AC_VERB_SET_CONNECT_SEL, 0x01},
- {0x0f, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
- {0x10, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP},
- {0x11, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
- {0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
- {0x13, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80},
- {0x10, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | ALC880_HP_EVENT},
- {0x11, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | ALC880_HP_EVENT},
- {0x15, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | ALC880_HP_EVENT},
+static const struct hda_model_fixup alc880_fixup_models[] = {
+ {.id = ALC880_FIXUP_3ST, .name = "3stack"},
+ {.id = ALC880_FIXUP_3ST_DIG, .name = "3stack-digout"},
+ {.id = ALC880_FIXUP_5ST, .name = "5stack"},
+ {.id = ALC880_FIXUP_5ST_DIG, .name = "5stack-digout"},
+ {.id = ALC880_FIXUP_6ST, .name = "6stack"},
+ {.id = ALC880_FIXUP_6ST_DIG, .name = "6stack-digout"},
+ {.id = ALC880_FIXUP_6ST_AUTOMUTE, .name = "6stack-automute"},
{}
};
-/* Test configuration for debugging, modelled after the ALC880 test
- * configuration.
- */
-#ifdef CONFIG_SND_DEBUG
-static hda_nid_t alc260_test_dac_nids[1] = {
- 0x02,
-};
-static hda_nid_t alc260_test_adc_nids[2] = {
- 0x04, 0x05,
-};
-/* For testing the ALC260, each input MUX needs its own definition since
- * the signal assignments are different. This assumes that the first ADC
- * is NID 0x04.
- */
-static struct hda_input_mux alc260_test_capture_sources[2] = {
- {
- .num_items = 7,
- .items = {
- { "MIC1 pin", 0x0 },
- { "MIC2 pin", 0x1 },
- { "LINE1 pin", 0x2 },
- { "LINE2 pin", 0x3 },
- { "CD pin", 0x4 },
- { "LINE-OUT pin", 0x5 },
- { "HP-OUT pin", 0x6 },
- },
- },
- {
- .num_items = 8,
- .items = {
- { "MIC1 pin", 0x0 },
- { "MIC2 pin", 0x1 },
- { "LINE1 pin", 0x2 },
- { "LINE2 pin", 0x3 },
- { "CD pin", 0x4 },
- { "Mixer", 0x5 },
- { "LINE-OUT pin", 0x6 },
- { "HP-OUT pin", 0x7 },
- },
- },
-};
-static struct snd_kcontrol_new alc260_test_mixer[] = {
- /* Output driver widgets */
- HDA_CODEC_VOLUME_MONO("Mono Playback Volume", 0x0a, 1, 0x0, HDA_OUTPUT),
- HDA_BIND_MUTE_MONO("Mono Playback Switch", 0x0a, 1, 2, HDA_INPUT),
- HDA_CODEC_VOLUME("LOUT2 Playback Volume", 0x09, 0x0, HDA_OUTPUT),
- HDA_BIND_MUTE("LOUT2 Playback Switch", 0x09, 2, HDA_INPUT),
- HDA_CODEC_VOLUME("LOUT1 Playback Volume", 0x08, 0x0, HDA_OUTPUT),
- HDA_BIND_MUTE("LOUT1 Playback Switch", 0x08, 2, HDA_INPUT),
-
- /* Modes for retasking pin widgets
- * Note: the ALC260 doesn't seem to act on requests to enable mic
- * bias from NIDs 0x0f and 0x10. The ALC260 datasheet doesn't
- * mention this restriction. At this stage it's not clear whether
- * this behaviour is intentional or is a hardware bug in chip
- * revisions available at least up until early 2006. Therefore for
- * now allow the "HP-OUT" and "LINE-OUT" Mode controls to span all
- * choices, but if it turns out that the lack of mic bias for these
- * NIDs is intentional we could change their modes from
- * ALC_PIN_DIR_INOUT to ALC_PIN_DIR_INOUT_NOMICBIAS.
- */
- ALC_PIN_MODE("HP-OUT pin mode", 0x10, ALC_PIN_DIR_INOUT),
- ALC_PIN_MODE("LINE-OUT pin mode", 0x0f, ALC_PIN_DIR_INOUT),
- ALC_PIN_MODE("LINE2 pin mode", 0x15, ALC_PIN_DIR_INOUT),
- ALC_PIN_MODE("LINE1 pin mode", 0x14, ALC_PIN_DIR_INOUT),
- ALC_PIN_MODE("MIC2 pin mode", 0x13, ALC_PIN_DIR_INOUT),
- ALC_PIN_MODE("MIC1 pin mode", 0x12, ALC_PIN_DIR_INOUT),
-
- /* Loopback mixer controls */
- HDA_CODEC_VOLUME("MIC1 Playback Volume", 0x07, 0x00, HDA_INPUT),
- HDA_CODEC_MUTE("MIC1 Playback Switch", 0x07, 0x00, HDA_INPUT),
- HDA_CODEC_VOLUME("MIC2 Playback Volume", 0x07, 0x01, HDA_INPUT),
- HDA_CODEC_MUTE("MIC2 Playback Switch", 0x07, 0x01, HDA_INPUT),
- HDA_CODEC_VOLUME("LINE1 Playback Volume", 0x07, 0x02, HDA_INPUT),
- HDA_CODEC_MUTE("LINE1 Playback Switch", 0x07, 0x02, HDA_INPUT),
- HDA_CODEC_VOLUME("LINE2 Playback Volume", 0x07, 0x03, HDA_INPUT),
- HDA_CODEC_MUTE("LINE2 Playback Switch", 0x07, 0x03, HDA_INPUT),
- HDA_CODEC_VOLUME("CD Playback Volume", 0x07, 0x04, HDA_INPUT),
- HDA_CODEC_MUTE("CD Playback Switch", 0x07, 0x04, HDA_INPUT),
- HDA_CODEC_VOLUME("LINE-OUT loopback Playback Volume", 0x07, 0x06, HDA_INPUT),
- HDA_CODEC_MUTE("LINE-OUT loopback Playback Switch", 0x07, 0x06, HDA_INPUT),
- HDA_CODEC_VOLUME("HP-OUT loopback Playback Volume", 0x07, 0x7, HDA_INPUT),
- HDA_CODEC_MUTE("HP-OUT loopback Playback Switch", 0x07, 0x7, HDA_INPUT),
-
- /* Controls for GPIO pins, assuming they are configured as outputs */
- ALC_GPIO_DATA_SWITCH("GPIO pin 0", 0x01, 0x01),
- ALC_GPIO_DATA_SWITCH("GPIO pin 1", 0x01, 0x02),
- ALC_GPIO_DATA_SWITCH("GPIO pin 2", 0x01, 0x04),
- ALC_GPIO_DATA_SWITCH("GPIO pin 3", 0x01, 0x08),
-
- /* Switches to allow the digital IO pins to be enabled. The datasheet
- * is ambigious as to which NID is which; testing on laptops which
- * make this output available should provide clarification.
- */
- ALC_SPDIF_CTRL_SWITCH("SPDIF Playback Switch", 0x03, 0x01),
- ALC_SPDIF_CTRL_SWITCH("SPDIF Capture Switch", 0x06, 0x01),
-
- /* A switch allowing EAPD to be enabled. Some laptops seem to use
- * this output to turn on an external amplifier.
- */
- ALC_EAPD_CTRL_SWITCH("LINE-OUT EAPD Enable Switch", 0x0f, 0x02),
- ALC_EAPD_CTRL_SWITCH("HP-OUT EAPD Enable Switch", 0x10, 0x02),
-
- { } /* end */
-};
-static struct hda_verb alc260_test_init_verbs[] = {
- /* Enable all GPIOs as outputs with an initial value of 0 */
- {0x01, AC_VERB_SET_GPIO_DIRECTION, 0x0f},
- {0x01, AC_VERB_SET_GPIO_DATA, 0x00},
- {0x01, AC_VERB_SET_GPIO_MASK, 0x0f},
-
- /* Enable retasking pins as output, initially without power amp */
- {0x10, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
- {0x0f, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
- {0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
- {0x14, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
- {0x13, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
- {0x12, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
-
- /* Disable digital (SPDIF) pins initially, but users can enable
- * them via a mixer switch. In the case of SPDIF-out, this initverb
- * payload also sets the generation to 0, output to be in "consumer"
- * PCM format, copyright asserted, no pre-emphasis and no validity
- * control.
- */
- {0x03, AC_VERB_SET_DIGI_CONVERT_1, 0},
- {0x06, AC_VERB_SET_DIGI_CONVERT_1, 0},
-
- /* Ensure mic1, mic2, line1 and line2 pin widgets take input from the
- * OUT1 sum bus when acting as an output.
- */
- {0x0b, AC_VERB_SET_CONNECT_SEL, 0},
- {0x0c, AC_VERB_SET_CONNECT_SEL, 0},
- {0x0d, AC_VERB_SET_CONNECT_SEL, 0},
- {0x0e, AC_VERB_SET_CONNECT_SEL, 0},
-
- /* Start with output sum widgets muted and their output gains at min */
- {0x08, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
- {0x08, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
- {0x08, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
- {0x09, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
- {0x09, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
- {0x09, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
- {0x0a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
- {0x0a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
- {0x0a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
-
- /* Unmute retasking pin widget output buffers since the default
- * state appears to be output. As the pin mode is changed by the
- * user the pin mode control will take care of enabling the pin's
- * input/output buffers as needed.
- */
- {0x10, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
- {0x0f, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
- {0x15, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
- {0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
- {0x13, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
- {0x12, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
- /* Also unmute the mono-out pin widget */
- {0x11, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
-
- /* Mute capture amp left and right */
- {0x04, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
- /* Set ADC connection select to match default mixer setting (mic1
- * pin)
- */
- {0x04, AC_VERB_SET_CONNECT_SEL, 0x00},
-
- /* Do the same for the second ADC: mute capture input amp and
- * set ADC connection to mic1 pin
- */
- {0x05, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
- {0x05, AC_VERB_SET_CONNECT_SEL, 0x00},
-
- /* Mute all inputs to mixer widget (even unconnected ones) */
- {0x07, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)}, /* mic1 pin */
- {0x07, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)}, /* mic2 pin */
- {0x07, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(2)}, /* line1 pin */
- {0x07, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(3)}, /* line2 pin */
- {0x07, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(4)}, /* CD pin */
- {0x07, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(5)}, /* Beep-gen pin */
- {0x07, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(6)}, /* Line-out pin */
- {0x07, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(7)}, /* HP-pin pin */
-
- { }
-};
-#endif
-
-#define alc260_pcm_analog_playback alc880_pcm_analog_alt_playback
-#define alc260_pcm_analog_capture alc880_pcm_analog_capture
-
-#define alc260_pcm_digital_playback alc880_pcm_digital_playback
-#define alc260_pcm_digital_capture alc880_pcm_digital_capture
/*
- * for BIOS auto-configuration
+ * OK, here we have finally the patch for ALC880
*/
-
-static int alc260_add_playback_controls(struct alc_spec *spec, hda_nid_t nid,
- const char *pfx, int *vol_bits)
+static int patch_alc880(struct hda_codec *codec)
{
- hda_nid_t nid_vol;
- unsigned long vol_val, sw_val;
+ struct alc_spec *spec;
int err;
- if (nid >= 0x0f && nid < 0x11) {
- nid_vol = nid - 0x7;
- vol_val = HDA_COMPOSE_AMP_VAL(nid_vol, 3, 0, HDA_OUTPUT);
- sw_val = HDA_COMPOSE_AMP_VAL(nid, 3, 0, HDA_OUTPUT);
- } else if (nid == 0x11) {
- nid_vol = nid - 0x7;
- vol_val = HDA_COMPOSE_AMP_VAL(nid_vol, 2, 0, HDA_OUTPUT);
- sw_val = HDA_COMPOSE_AMP_VAL(nid, 2, 0, HDA_OUTPUT);
- } else if (nid >= 0x12 && nid <= 0x15) {
- nid_vol = 0x08;
- vol_val = HDA_COMPOSE_AMP_VAL(nid_vol, 3, 0, HDA_OUTPUT);
- sw_val = HDA_COMPOSE_AMP_VAL(nid, 3, 0, HDA_OUTPUT);
- } else
- return 0; /* N/A */
-
- if (!(*vol_bits & (1 << nid_vol))) {
- /* first control for the volume widget */
- err = add_pb_vol_ctrl(spec, ALC_CTL_WIDGET_VOL, pfx, vol_val);
- if (err < 0)
- return err;
- *vol_bits |= (1 << nid_vol);
- }
- err = add_pb_sw_ctrl(spec, ALC_CTL_WIDGET_MUTE, pfx, sw_val);
+ err = alc_alloc_spec(codec, 0x0b);
if (err < 0)
return err;
- return 1;
-}
-/* add playback controls from the parsed DAC table */
-static int alc260_auto_create_multi_out_ctls(struct alc_spec *spec,
- const struct auto_pin_cfg *cfg)
-{
- hda_nid_t nid;
- int err;
- int vols = 0;
-
- spec->multiout.num_dacs = 1;
- spec->multiout.dac_nids = spec->private_dac_nids;
- spec->multiout.dac_nids[0] = 0x02;
-
- nid = cfg->line_out_pins[0];
- if (nid) {
- const char *pfx;
- if (!cfg->speaker_pins[0] && !cfg->hp_pins[0])
- pfx = "Master";
- else if (cfg->line_out_type == AUTO_PIN_SPEAKER_OUT)
- pfx = "Speaker";
- else
- pfx = "Front";
- err = alc260_add_playback_controls(spec, nid, pfx, &vols);
- if (err < 0)
- return err;
- }
+ spec = codec->spec;
+ spec->gen.need_dac_fix = 1;
+ spec->gen.beep_nid = 0x01;
- nid = cfg->speaker_pins[0];
- if (nid) {
- err = alc260_add_playback_controls(spec, nid, "Speaker", &vols);
- if (err < 0)
- return err;
- }
+ snd_hda_pick_fixup(codec, alc880_fixup_models, alc880_fixup_tbl,
+ alc880_fixups);
+ snd_hda_apply_fixup(codec, HDA_FIXUP_ACT_PRE_PROBE);
- nid = cfg->hp_pins[0];
- if (nid) {
- err = alc260_add_playback_controls(spec, nid, "Headphone",
- &vols);
- if (err < 0)
- return err;
- }
- return 0;
-}
+ /* automatic parse from the BIOS config */
+ err = alc880_parse_auto_config(codec);
+ if (err < 0)
+ goto error;
-/* create playback/capture controls for input pins */
-static int alc260_auto_create_input_ctls(struct hda_codec *codec,
- const struct auto_pin_cfg *cfg)
-{
- return alc_auto_create_input_ctls(codec, cfg, 0x07, 0x04, 0x05);
-}
+ if (!spec->gen.no_analog)
+ set_beep_amp(spec, 0x0b, 0x05, HDA_INPUT);
-static void alc260_auto_set_output_and_unmute(struct hda_codec *codec,
- hda_nid_t nid, int pin_type,
- int sel_idx)
-{
- alc_set_pin_output(codec, nid, pin_type);
- /* need the manual connection? */
- if (nid >= 0x12) {
- int idx = nid - 0x12;
- snd_hda_codec_write(codec, idx + 0x0b, 0,
- AC_VERB_SET_CONNECT_SEL, sel_idx);
- }
-}
+ codec->patch_ops = alc_patch_ops;
+ codec->patch_ops.unsol_event = alc880_unsol_event;
-static void alc260_auto_init_multi_out(struct hda_codec *codec)
-{
- struct alc_spec *spec = codec->spec;
- hda_nid_t nid;
- nid = spec->autocfg.line_out_pins[0];
- if (nid) {
- int pin_type = get_pin_type(spec->autocfg.line_out_type);
- alc260_auto_set_output_and_unmute(codec, nid, pin_type, 0);
- }
+ snd_hda_apply_fixup(codec, HDA_FIXUP_ACT_PROBE);
- nid = spec->autocfg.speaker_pins[0];
- if (nid)
- alc260_auto_set_output_and_unmute(codec, nid, PIN_OUT, 0);
+ return 0;
- nid = spec->autocfg.hp_pins[0];
- if (nid)
- alc260_auto_set_output_and_unmute(codec, nid, PIN_HP, 0);
+ error:
+ alc_free(codec);
+ return err;
}
-#define ALC260_PIN_CD_NID 0x16
-static void alc260_auto_init_analog_input(struct hda_codec *codec)
-{
- struct alc_spec *spec = codec->spec;
- struct auto_pin_cfg *cfg = &spec->autocfg;
- int i;
- for (i = 0; i < cfg->num_inputs; i++) {
- hda_nid_t nid = cfg->inputs[i].pin;
- if (nid >= 0x12) {
- alc_set_input_pin(codec, nid, cfg->inputs[i].type);
- if (nid != ALC260_PIN_CD_NID &&
- (get_wcaps(codec, nid) & AC_WCAP_OUT_AMP))
- snd_hda_codec_write(codec, nid, 0,
- AC_VERB_SET_AMP_GAIN_MUTE,
- AMP_OUT_MUTE);
- }
- }
+/*
+ * ALC260 support
+ */
+static int alc260_parse_auto_config(struct hda_codec *codec)
+{
+ static const hda_nid_t alc260_ignore[] = { 0x17, 0 };
+ static const hda_nid_t alc260_ssids[] = { 0x10, 0x15, 0x0f, 0 };
+ return alc_parse_auto_config(codec, alc260_ignore, alc260_ssids);
}
-#define alc260_auto_init_input_src alc880_auto_init_input_src
-
/*
- * generic initialization of ADC, input mixers and output mixers
+ * Pin config fixes
*/
-static struct hda_verb alc260_volume_init_verbs[] = {
- /*
- * Unmute ADC0-1 and set the default input to mic-in
- */
- {0x04, AC_VERB_SET_CONNECT_SEL, 0x00},
- {0x04, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
- {0x05, AC_VERB_SET_CONNECT_SEL, 0x00},
- {0x05, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
-
- /* Unmute input amps (CD, Line In, Mic 1 & Mic 2) of the analog-loopback
- * mixer widget
- * Note: PASD motherboards uses the Line In 2 as the input for
- * front panel mic (mic 2)
- */
- /* Amp Indices: Mic1 = 0, Mic2 = 1, Line1 = 2, Line2 = 3, CD = 4 */
- /* mute analog inputs */
- {0x07, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
- {0x07, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
- {0x07, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(2)},
- {0x07, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(3)},
- {0x07, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(4)},
-
- /*
- * Set up output mixers (0x08 - 0x0a)
- */
- /* set vol=0 to output mixers */
- {0x08, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
- {0x09, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
- {0x0a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
- /* set up input amps for analog loopback */
- /* Amp Indices: DAC = 0, mixer = 1 */
- {0x08, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
- {0x08, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
- {0x09, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
- {0x09, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
- {0x0a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
- {0x0a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
-
- { }
+enum {
+ ALC260_FIXUP_HP_DC5750,
+ ALC260_FIXUP_HP_PIN_0F,
+ ALC260_FIXUP_COEF,
+ ALC260_FIXUP_GPIO1,
+ ALC260_FIXUP_GPIO1_TOGGLE,
+ ALC260_FIXUP_REPLACER,
+ ALC260_FIXUP_HP_B1900,
+ ALC260_FIXUP_KN1,
+ ALC260_FIXUP_FSC_S7020,
+ ALC260_FIXUP_FSC_S7020_JWSE,
+ ALC260_FIXUP_VAIO_PINS,
};
-static int alc260_parse_auto_config(struct hda_codec *codec)
+static void alc260_gpio1_automute(struct hda_codec *codec)
{
struct alc_spec *spec = codec->spec;
- int err;
- static hda_nid_t alc260_ignore[] = { 0x17, 0 };
-
- err = snd_hda_parse_pin_def_config(codec, &spec->autocfg,
- alc260_ignore);
- if (err < 0)
- return err;
- err = alc260_auto_create_multi_out_ctls(spec, &spec->autocfg);
- if (err < 0)
- return err;
- if (!spec->kctls.list)
- return 0; /* can't find valid BIOS pin config */
- err = alc260_auto_create_input_ctls(codec, &spec->autocfg);
- if (err < 0)
- return err;
-
- spec->multiout.max_channels = 2;
-
- if (spec->autocfg.dig_outs)
- spec->multiout.dig_out_nid = ALC260_DIGOUT_NID;
- if (spec->kctls.list)
- add_mixer(spec, spec->kctls.list);
-
- add_verb(spec, alc260_volume_init_verbs);
-
- spec->num_mux_defs = 1;
- spec->input_mux = &spec->private_imux[0];
+ snd_hda_codec_write(codec, 0x01, 0, AC_VERB_SET_GPIO_DATA,
+ spec->gen.hp_jack_present);
+}
- alc_ssid_check(codec, 0x10, 0x15, 0x0f, 0);
+static void alc260_fixup_gpio1_toggle(struct hda_codec *codec,
+ const struct hda_fixup *fix, int action)
+{
+ struct alc_spec *spec = codec->spec;
+ if (action == HDA_FIXUP_ACT_PROBE) {
+ /* although the machine has only one output pin, we need to
+ * toggle GPIO1 according to the jack state
+ */
+ spec->gen.automute_hook = alc260_gpio1_automute;
+ spec->gen.detect_hp = 1;
+ spec->gen.automute_speaker = 1;
+ spec->gen.autocfg.hp_pins[0] = 0x0f; /* copy it for automute */
+ snd_hda_jack_detect_enable_callback(codec, 0x0f, HDA_GEN_HP_EVENT,
+ snd_hda_gen_hp_automute);
+ snd_hda_add_verbs(codec, alc_gpio1_init_verbs);
+ }
+}
+
+static void alc260_fixup_kn1(struct hda_codec *codec,
+ const struct hda_fixup *fix, int action)
+{
+ struct alc_spec *spec = codec->spec;
+ static const struct hda_pintbl pincfgs[] = {
+ { 0x0f, 0x02214000 }, /* HP/speaker */
+ { 0x12, 0x90a60160 }, /* int mic */
+ { 0x13, 0x02a19000 }, /* ext mic */
+ { 0x18, 0x01446000 }, /* SPDIF out */
+ /* disable bogus I/O pins */
+ { 0x10, 0x411111f0 },
+ { 0x11, 0x411111f0 },
+ { 0x14, 0x411111f0 },
+ { 0x15, 0x411111f0 },
+ { 0x16, 0x411111f0 },
+ { 0x17, 0x411111f0 },
+ { 0x19, 0x411111f0 },
+ { }
+ };
- return 1;
+ switch (action) {
+ case HDA_FIXUP_ACT_PRE_PROBE:
+ snd_hda_apply_pincfgs(codec, pincfgs);
+ break;
+ case HDA_FIXUP_ACT_PROBE:
+ spec->init_amp = ALC_INIT_NONE;
+ break;
+ }
}
-/* additional initialization for auto-configuration model */
-static void alc260_auto_init(struct hda_codec *codec)
+static void alc260_fixup_fsc_s7020(struct hda_codec *codec,
+ const struct hda_fixup *fix, int action)
{
struct alc_spec *spec = codec->spec;
- alc260_auto_init_multi_out(codec);
- alc260_auto_init_analog_input(codec);
- alc260_auto_init_input_src(codec);
- alc_auto_init_digital(codec);
- if (spec->unsol_event)
- alc_inithook(codec);
+ if (action == HDA_FIXUP_ACT_PROBE)
+ spec->init_amp = ALC_INIT_NONE;
}
-#ifdef CONFIG_SND_HDA_POWER_SAVE
-static struct hda_amp_list alc260_loopbacks[] = {
- { 0x07, HDA_INPUT, 0 },
- { 0x07, HDA_INPUT, 1 },
- { 0x07, HDA_INPUT, 2 },
- { 0x07, HDA_INPUT, 3 },
- { 0x07, HDA_INPUT, 4 },
- { } /* end */
-};
-#endif
-
-/*
- * Pin config fixes
- */
-enum {
- PINFIX_HP_DC5750,
-};
+static void alc260_fixup_fsc_s7020_jwse(struct hda_codec *codec,
+ const struct hda_fixup *fix, int action)
+{
+ struct alc_spec *spec = codec->spec;
+ if (action == HDA_FIXUP_ACT_PRE_PROBE) {
+ spec->gen.add_jack_modes = 1;
+ spec->gen.hp_mic = 1;
+ }
+}
-static const struct alc_fixup alc260_fixups[] = {
- [PINFIX_HP_DC5750] = {
- .type = ALC_FIXUP_PINS,
- .v.pins = (const struct alc_pincfg[]) {
+static const struct hda_fixup alc260_fixups[] = {
+ [ALC260_FIXUP_HP_DC5750] = {
+ .type = HDA_FIXUP_PINS,
+ .v.pins = (const struct hda_pintbl[]) {
{ 0x11, 0x90130110 }, /* speaker */
{ }
}
},
-};
-
-static struct snd_pci_quirk alc260_fixup_tbl[] = {
- SND_PCI_QUIRK(0x103c, 0x280a, "HP dc5750", PINFIX_HP_DC5750),
- {}
-};
-
-/*
- * ALC260 configurations
- */
-static const char * const alc260_models[ALC260_MODEL_LAST] = {
- [ALC260_BASIC] = "basic",
- [ALC260_HP] = "hp",
- [ALC260_HP_3013] = "hp-3013",
- [ALC260_HP_DC7600] = "hp-dc7600",
- [ALC260_FUJITSU_S702X] = "fujitsu",
- [ALC260_ACER] = "acer",
- [ALC260_WILL] = "will",
- [ALC260_REPLACER_672V] = "replacer",
- [ALC260_FAVORIT100] = "favorit100",
-#ifdef CONFIG_SND_DEBUG
- [ALC260_TEST] = "test",
-#endif
- [ALC260_AUTO] = "auto",
-};
-
-static struct snd_pci_quirk alc260_cfg_tbl[] = {
- SND_PCI_QUIRK(0x1025, 0x007b, "Acer C20x", ALC260_ACER),
- SND_PCI_QUIRK(0x1025, 0x007f, "Acer", ALC260_WILL),
- SND_PCI_QUIRK(0x1025, 0x008f, "Acer", ALC260_ACER),
- SND_PCI_QUIRK(0x1509, 0x4540, "Favorit 100XS", ALC260_FAVORIT100),
- SND_PCI_QUIRK(0x103c, 0x2808, "HP d5700", ALC260_HP_3013),
- SND_PCI_QUIRK(0x103c, 0x280a, "HP d5750", ALC260_AUTO), /* no quirk */
- SND_PCI_QUIRK(0x103c, 0x3010, "HP", ALC260_HP_3013),
- SND_PCI_QUIRK(0x103c, 0x3011, "HP", ALC260_HP_3013),
- SND_PCI_QUIRK(0x103c, 0x3012, "HP", ALC260_HP_DC7600),
- SND_PCI_QUIRK(0x103c, 0x3013, "HP", ALC260_HP_3013),
- SND_PCI_QUIRK(0x103c, 0x3014, "HP", ALC260_HP),
- SND_PCI_QUIRK(0x103c, 0x3015, "HP", ALC260_HP),
- SND_PCI_QUIRK(0x103c, 0x3016, "HP", ALC260_HP),
- SND_PCI_QUIRK(0x104d, 0x81bb, "Sony VAIO", ALC260_BASIC),
- SND_PCI_QUIRK(0x104d, 0x81cc, "Sony VAIO", ALC260_BASIC),
- SND_PCI_QUIRK(0x104d, 0x81cd, "Sony VAIO", ALC260_BASIC),
- SND_PCI_QUIRK(0x10cf, 0x1326, "Fujitsu S702X", ALC260_FUJITSU_S702X),
- SND_PCI_QUIRK(0x152d, 0x0729, "CTL U553W", ALC260_BASIC),
- SND_PCI_QUIRK(0x161f, 0x2057, "Replacer 672V", ALC260_REPLACER_672V),
- SND_PCI_QUIRK(0x1631, 0xc017, "PB V7900", ALC260_WILL),
- {}
-};
-
-static struct alc_config_preset alc260_presets[] = {
- [ALC260_BASIC] = {
- .mixers = { alc260_base_output_mixer,
- alc260_input_mixer },
- .init_verbs = { alc260_init_verbs },
- .num_dacs = ARRAY_SIZE(alc260_dac_nids),
- .dac_nids = alc260_dac_nids,
- .num_adc_nids = ARRAY_SIZE(alc260_dual_adc_nids),
- .adc_nids = alc260_dual_adc_nids,
- .num_channel_mode = ARRAY_SIZE(alc260_modes),
- .channel_mode = alc260_modes,
- .input_mux = &alc260_capture_source,
- },
- [ALC260_HP] = {
- .mixers = { alc260_hp_output_mixer,
- alc260_input_mixer },
- .init_verbs = { alc260_init_verbs,
- alc260_hp_unsol_verbs },
- .num_dacs = ARRAY_SIZE(alc260_dac_nids),
- .dac_nids = alc260_dac_nids,
- .num_adc_nids = ARRAY_SIZE(alc260_adc_nids_alt),
- .adc_nids = alc260_adc_nids_alt,
- .num_channel_mode = ARRAY_SIZE(alc260_modes),
- .channel_mode = alc260_modes,
- .input_mux = &alc260_capture_source,
- .unsol_event = alc260_hp_unsol_event,
- .init_hook = alc260_hp_automute,
+ [ALC260_FIXUP_HP_PIN_0F] = {
+ .type = HDA_FIXUP_PINS,
+ .v.pins = (const struct hda_pintbl[]) {
+ { 0x0f, 0x01214000 }, /* HP */
+ { }
+ }
},
- [ALC260_HP_DC7600] = {
- .mixers = { alc260_hp_dc7600_mixer,
- alc260_input_mixer },
- .init_verbs = { alc260_init_verbs,
- alc260_hp_dc7600_verbs },
- .num_dacs = ARRAY_SIZE(alc260_dac_nids),
- .dac_nids = alc260_dac_nids,
- .num_adc_nids = ARRAY_SIZE(alc260_adc_nids_alt),
- .adc_nids = alc260_adc_nids_alt,
- .num_channel_mode = ARRAY_SIZE(alc260_modes),
- .channel_mode = alc260_modes,
- .input_mux = &alc260_capture_source,
- .unsol_event = alc260_hp_3012_unsol_event,
- .init_hook = alc260_hp_3012_automute,
+ [ALC260_FIXUP_COEF] = {
+ .type = HDA_FIXUP_VERBS,
+ .v.verbs = (const struct hda_verb[]) {
+ { 0x1a, AC_VERB_SET_COEF_INDEX, 0x07 },
+ { 0x1a, AC_VERB_SET_PROC_COEF, 0x3040 },
+ { }
+ },
},
- [ALC260_HP_3013] = {
- .mixers = { alc260_hp_3013_mixer,
- alc260_input_mixer },
- .init_verbs = { alc260_hp_3013_init_verbs,
- alc260_hp_3013_unsol_verbs },
- .num_dacs = ARRAY_SIZE(alc260_dac_nids),
- .dac_nids = alc260_dac_nids,
- .num_adc_nids = ARRAY_SIZE(alc260_adc_nids_alt),
- .adc_nids = alc260_adc_nids_alt,
- .num_channel_mode = ARRAY_SIZE(alc260_modes),
- .channel_mode = alc260_modes,
- .input_mux = &alc260_capture_source,
- .unsol_event = alc260_hp_3013_unsol_event,
- .init_hook = alc260_hp_3013_automute,
+ [ALC260_FIXUP_GPIO1] = {
+ .type = HDA_FIXUP_VERBS,
+ .v.verbs = alc_gpio1_init_verbs,
},
- [ALC260_FUJITSU_S702X] = {
- .mixers = { alc260_fujitsu_mixer },
- .init_verbs = { alc260_fujitsu_init_verbs },
- .num_dacs = ARRAY_SIZE(alc260_dac_nids),
- .dac_nids = alc260_dac_nids,
- .num_adc_nids = ARRAY_SIZE(alc260_dual_adc_nids),
- .adc_nids = alc260_dual_adc_nids,
- .num_channel_mode = ARRAY_SIZE(alc260_modes),
- .channel_mode = alc260_modes,
- .num_mux_defs = ARRAY_SIZE(alc260_fujitsu_capture_sources),
- .input_mux = alc260_fujitsu_capture_sources,
+ [ALC260_FIXUP_GPIO1_TOGGLE] = {
+ .type = HDA_FIXUP_FUNC,
+ .v.func = alc260_fixup_gpio1_toggle,
+ .chained = true,
+ .chain_id = ALC260_FIXUP_HP_PIN_0F,
},
- [ALC260_ACER] = {
- .mixers = { alc260_acer_mixer },
- .init_verbs = { alc260_acer_init_verbs },
- .num_dacs = ARRAY_SIZE(alc260_dac_nids),
- .dac_nids = alc260_dac_nids,
- .num_adc_nids = ARRAY_SIZE(alc260_dual_adc_nids),
- .adc_nids = alc260_dual_adc_nids,
- .num_channel_mode = ARRAY_SIZE(alc260_modes),
- .channel_mode = alc260_modes,
- .num_mux_defs = ARRAY_SIZE(alc260_acer_capture_sources),
- .input_mux = alc260_acer_capture_sources,
+ [ALC260_FIXUP_REPLACER] = {
+ .type = HDA_FIXUP_VERBS,
+ .v.verbs = (const struct hda_verb[]) {
+ { 0x1a, AC_VERB_SET_COEF_INDEX, 0x07 },
+ { 0x1a, AC_VERB_SET_PROC_COEF, 0x3050 },
+ { }
+ },
+ .chained = true,
+ .chain_id = ALC260_FIXUP_GPIO1_TOGGLE,
},
- [ALC260_FAVORIT100] = {
- .mixers = { alc260_favorit100_mixer },
- .init_verbs = { alc260_favorit100_init_verbs },
- .num_dacs = ARRAY_SIZE(alc260_dac_nids),
- .dac_nids = alc260_dac_nids,
- .num_adc_nids = ARRAY_SIZE(alc260_dual_adc_nids),
- .adc_nids = alc260_dual_adc_nids,
- .num_channel_mode = ARRAY_SIZE(alc260_modes),
- .channel_mode = alc260_modes,
- .num_mux_defs = ARRAY_SIZE(alc260_favorit100_capture_sources),
- .input_mux = alc260_favorit100_capture_sources,
+ [ALC260_FIXUP_HP_B1900] = {
+ .type = HDA_FIXUP_FUNC,
+ .v.func = alc260_fixup_gpio1_toggle,
+ .chained = true,
+ .chain_id = ALC260_FIXUP_COEF,
},
- [ALC260_WILL] = {
- .mixers = { alc260_will_mixer },
- .init_verbs = { alc260_init_verbs, alc260_will_verbs },
- .num_dacs = ARRAY_SIZE(alc260_dac_nids),
- .dac_nids = alc260_dac_nids,
- .num_adc_nids = ARRAY_SIZE(alc260_adc_nids),
- .adc_nids = alc260_adc_nids,
- .dig_out_nid = ALC260_DIGOUT_NID,
- .num_channel_mode = ARRAY_SIZE(alc260_modes),
- .channel_mode = alc260_modes,
- .input_mux = &alc260_capture_source,
+ [ALC260_FIXUP_KN1] = {
+ .type = HDA_FIXUP_FUNC,
+ .v.func = alc260_fixup_kn1,
},
- [ALC260_REPLACER_672V] = {
- .mixers = { alc260_replacer_672v_mixer },
- .init_verbs = { alc260_init_verbs, alc260_replacer_672v_verbs },
- .num_dacs = ARRAY_SIZE(alc260_dac_nids),
- .dac_nids = alc260_dac_nids,
- .num_adc_nids = ARRAY_SIZE(alc260_adc_nids),
- .adc_nids = alc260_adc_nids,
- .dig_out_nid = ALC260_DIGOUT_NID,
- .num_channel_mode = ARRAY_SIZE(alc260_modes),
- .channel_mode = alc260_modes,
- .input_mux = &alc260_capture_source,
- .unsol_event = alc260_replacer_672v_unsol_event,
- .init_hook = alc260_replacer_672v_automute,
+ [ALC260_FIXUP_FSC_S7020] = {
+ .type = HDA_FIXUP_FUNC,
+ .v.func = alc260_fixup_fsc_s7020,
},
-#ifdef CONFIG_SND_DEBUG
- [ALC260_TEST] = {
- .mixers = { alc260_test_mixer },
- .init_verbs = { alc260_test_init_verbs },
- .num_dacs = ARRAY_SIZE(alc260_test_dac_nids),
- .dac_nids = alc260_test_dac_nids,
- .num_adc_nids = ARRAY_SIZE(alc260_test_adc_nids),
- .adc_nids = alc260_test_adc_nids,
- .num_channel_mode = ARRAY_SIZE(alc260_modes),
- .channel_mode = alc260_modes,
- .num_mux_defs = ARRAY_SIZE(alc260_test_capture_sources),
- .input_mux = alc260_test_capture_sources,
+ [ALC260_FIXUP_FSC_S7020_JWSE] = {
+ .type = HDA_FIXUP_FUNC,
+ .v.func = alc260_fixup_fsc_s7020_jwse,
+ .chained = true,
+ .chain_id = ALC260_FIXUP_FSC_S7020,
+ },
+ [ALC260_FIXUP_VAIO_PINS] = {
+ .type = HDA_FIXUP_PINS,
+ .v.pins = (const struct hda_pintbl[]) {
+ /* Pin configs are missing completely on some VAIOs */
+ { 0x0f, 0x01211020 },
+ { 0x10, 0x0001003f },
+ { 0x11, 0x411111f0 },
+ { 0x12, 0x01a15930 },
+ { 0x13, 0x411111f0 },
+ { 0x14, 0x411111f0 },
+ { 0x15, 0x411111f0 },
+ { 0x16, 0x411111f0 },
+ { 0x17, 0x411111f0 },
+ { 0x18, 0x411111f0 },
+ { 0x19, 0x411111f0 },
+ { }
+ }
},
-#endif
};
+static const struct snd_pci_quirk alc260_fixup_tbl[] = {
+ SND_PCI_QUIRK(0x1025, 0x007b, "Acer C20x", ALC260_FIXUP_GPIO1),
+ SND_PCI_QUIRK(0x1025, 0x007f, "Acer Aspire 9500", ALC260_FIXUP_COEF),
+ SND_PCI_QUIRK(0x1025, 0x008f, "Acer", ALC260_FIXUP_GPIO1),
+ SND_PCI_QUIRK(0x103c, 0x280a, "HP dc5750", ALC260_FIXUP_HP_DC5750),
+ SND_PCI_QUIRK(0x103c, 0x30ba, "HP Presario B1900", ALC260_FIXUP_HP_B1900),
+ SND_PCI_QUIRK(0x104d, 0x81bb, "Sony VAIO", ALC260_FIXUP_VAIO_PINS),
+ SND_PCI_QUIRK(0x104d, 0x81e2, "Sony VAIO TX", ALC260_FIXUP_HP_PIN_0F),
+ SND_PCI_QUIRK(0x10cf, 0x1326, "FSC LifeBook S7020", ALC260_FIXUP_FSC_S7020),
+ SND_PCI_QUIRK(0x1509, 0x4540, "Favorit 100XS", ALC260_FIXUP_GPIO1),
+ SND_PCI_QUIRK(0x152d, 0x0729, "Quanta KN1", ALC260_FIXUP_KN1),
+ SND_PCI_QUIRK(0x161f, 0x2057, "Replacer 672V", ALC260_FIXUP_REPLACER),
+ SND_PCI_QUIRK(0x1631, 0xc017, "PB V7900", ALC260_FIXUP_COEF),
+ {}
+};
+
+static const struct hda_model_fixup alc260_fixup_models[] = {
+ {.id = ALC260_FIXUP_GPIO1, .name = "gpio1"},
+ {.id = ALC260_FIXUP_COEF, .name = "coef"},
+ {.id = ALC260_FIXUP_FSC_S7020, .name = "fujitsu"},
+ {.id = ALC260_FIXUP_FSC_S7020_JWSE, .name = "fujitsu-jwse"},
+ {}
+};
+
+/*
+ */
static int patch_alc260(struct hda_codec *codec)
{
struct alc_spec *spec;
- int err, board_config;
-
- spec = kzalloc(sizeof(*spec), GFP_KERNEL);
- if (spec == NULL)
- return -ENOMEM;
-
- codec->spec = spec;
-
- board_config = snd_hda_check_board_config(codec, ALC260_MODEL_LAST,
- alc260_models,
- alc260_cfg_tbl);
- if (board_config < 0) {
- snd_printd(KERN_INFO "hda_codec: %s: BIOS auto-probing.\n",
- codec->chip_name);
- board_config = ALC260_AUTO;
- }
-
- if (board_config == ALC260_AUTO) {
- alc_pick_fixup(codec, NULL, alc260_fixup_tbl, alc260_fixups);
- alc_apply_fixup(codec, ALC_FIXUP_ACT_PRE_PROBE);
- }
-
- if (board_config == ALC260_AUTO) {
- /* automatic parse from the BIOS config */
- err = alc260_parse_auto_config(codec);
- if (err < 0) {
- alc_free(codec);
- return err;
- } else if (!err) {
- printk(KERN_INFO
- "hda_codec: Cannot set up configuration "
- "from BIOS. Using base mode...\n");
- board_config = ALC260_BASIC;
- }
- }
+ int err;
- err = snd_hda_attach_beep_device(codec, 0x1);
- if (err < 0) {
- alc_free(codec);
+ err = alc_alloc_spec(codec, 0x07);
+ if (err < 0)
return err;
- }
- if (board_config != ALC260_AUTO)
- setup_preset(codec, &alc260_presets[board_config]);
-
- spec->stream_analog_playback = &alc260_pcm_analog_playback;
- spec->stream_analog_capture = &alc260_pcm_analog_capture;
- spec->stream_analog_alt_capture = &alc260_pcm_analog_capture;
-
- spec->stream_digital_playback = &alc260_pcm_digital_playback;
- spec->stream_digital_capture = &alc260_pcm_digital_capture;
-
- if (!spec->adc_nids && spec->input_mux) {
- /* check whether NID 0x04 is valid */
- unsigned int wcap = get_wcaps(codec, 0x04);
- wcap = get_wcaps_type(wcap);
- /* get type */
- if (wcap != AC_WID_AUD_IN || spec->input_mux->num_items == 1) {
- spec->adc_nids = alc260_adc_nids_alt;
- spec->num_adc_nids = ARRAY_SIZE(alc260_adc_nids_alt);
- } else {
- spec->adc_nids = alc260_adc_nids;
- spec->num_adc_nids = ARRAY_SIZE(alc260_adc_nids);
- }
- }
- set_capture_mixer(codec);
- set_beep_amp(spec, 0x07, 0x05, HDA_INPUT);
+ spec = codec->spec;
+ /* as quite a few machines require HP amp for speaker outputs,
+ * it's easier to enable it unconditionally; even if it's unneeded,
+ * it's almost harmless.
+ */
+ spec->gen.prefer_hp_amp = 1;
+ spec->gen.beep_nid = 0x01;
+
+ snd_hda_pick_fixup(codec, alc260_fixup_models, alc260_fixup_tbl,
+ alc260_fixups);
+ snd_hda_apply_fixup(codec, HDA_FIXUP_ACT_PRE_PROBE);
- alc_apply_fixup(codec, ALC_FIXUP_ACT_PROBE);
+ /* automatic parse from the BIOS config */
+ err = alc260_parse_auto_config(codec);
+ if (err < 0)
+ goto error;
- spec->vmaster_nid = 0x08;
+ if (!spec->gen.no_analog)
+ set_beep_amp(spec, 0x07, 0x05, HDA_INPUT);
codec->patch_ops = alc_patch_ops;
- if (board_config == ALC260_AUTO)
- spec->init_hook = alc260_auto_init;
-#ifdef CONFIG_SND_HDA_POWER_SAVE
- if (!spec->loopback.amplist)
- spec->loopback.amplist = alc260_loopbacks;
-#endif
+ spec->shutup = alc_eapd_shutup;
+
+ snd_hda_apply_fixup(codec, HDA_FIXUP_ACT_PROBE);
return 0;
+
+ error:
+ alc_free(codec);
+ return err;
}
@@ -7403,1309 +1809,53 @@ static int patch_alc260(struct hda_codec *codec)
* In addition, an independent DAC for the multi-playback (not used in this
* driver yet).
*/
-#define ALC882_DIGOUT_NID 0x06
-#define ALC882_DIGIN_NID 0x0a
-#define ALC883_DIGOUT_NID ALC882_DIGOUT_NID
-#define ALC883_DIGIN_NID ALC882_DIGIN_NID
-#define ALC1200_DIGOUT_NID 0x10
-
-
-static struct hda_channel_mode alc882_ch_modes[1] = {
- { 8, NULL }
-};
-
-/* DACs */
-static hda_nid_t alc882_dac_nids[4] = {
- /* front, rear, clfe, rear_surr */
- 0x02, 0x03, 0x04, 0x05
-};
-#define alc883_dac_nids alc882_dac_nids
-
-/* ADCs */
-#define alc882_adc_nids alc880_adc_nids
-#define alc882_adc_nids_alt alc880_adc_nids_alt
-#define alc883_adc_nids alc882_adc_nids_alt
-static hda_nid_t alc883_adc_nids_alt[1] = { 0x08 };
-static hda_nid_t alc883_adc_nids_rev[2] = { 0x09, 0x08 };
-#define alc889_adc_nids alc880_adc_nids
-
-static hda_nid_t alc882_capsrc_nids[3] = { 0x24, 0x23, 0x22 };
-static hda_nid_t alc882_capsrc_nids_alt[2] = { 0x23, 0x22 };
-#define alc883_capsrc_nids alc882_capsrc_nids_alt
-static hda_nid_t alc883_capsrc_nids_rev[2] = { 0x22, 0x23 };
-#define alc889_capsrc_nids alc882_capsrc_nids
-
-/* input MUX */
-/* FIXME: should be a matrix-type input source selection */
-
-static struct hda_input_mux alc882_capture_source = {
- .num_items = 4,
- .items = {
- { "Mic", 0x0 },
- { "Front Mic", 0x1 },
- { "Line", 0x2 },
- { "CD", 0x4 },
- },
-};
-
-#define alc883_capture_source alc882_capture_source
-
-static struct hda_input_mux alc889_capture_source = {
- .num_items = 3,
- .items = {
- { "Front Mic", 0x0 },
- { "Mic", 0x3 },
- { "Line", 0x2 },
- },
-};
-
-static struct hda_input_mux mb5_capture_source = {
- .num_items = 3,
- .items = {
- { "Mic", 0x1 },
- { "Line", 0x7 },
- { "CD", 0x4 },
- },
-};
-
-static struct hda_input_mux macmini3_capture_source = {
- .num_items = 2,
- .items = {
- { "Line", 0x2 },
- { "CD", 0x4 },
- },
-};
-
-static struct hda_input_mux alc883_3stack_6ch_intel = {
- .num_items = 4,
- .items = {
- { "Mic", 0x1 },
- { "Front Mic", 0x0 },
- { "Line", 0x2 },
- { "CD", 0x4 },
- },
-};
-
-static struct hda_input_mux alc883_lenovo_101e_capture_source = {
- .num_items = 2,
- .items = {
- { "Mic", 0x1 },
- { "Line", 0x2 },
- },
-};
-
-static struct hda_input_mux alc883_lenovo_nb0763_capture_source = {
- .num_items = 4,
- .items = {
- { "Mic", 0x0 },
- { "Internal Mic", 0x1 },
- { "Line", 0x2 },
- { "CD", 0x4 },
- },
-};
-
-static struct hda_input_mux alc883_fujitsu_pi2515_capture_source = {
- .num_items = 2,
- .items = {
- { "Mic", 0x0 },
- { "Internal Mic", 0x1 },
- },
-};
-
-static struct hda_input_mux alc883_lenovo_sky_capture_source = {
- .num_items = 3,
- .items = {
- { "Mic", 0x0 },
- { "Front Mic", 0x1 },
- { "Line", 0x4 },
- },
-};
-
-static struct hda_input_mux alc883_asus_eee1601_capture_source = {
- .num_items = 2,
- .items = {
- { "Mic", 0x0 },
- { "Line", 0x2 },
- },
-};
-
-static struct hda_input_mux alc889A_mb31_capture_source = {
- .num_items = 2,
- .items = {
- { "Mic", 0x0 },
- /* Front Mic (0x01) unused */
- { "Line", 0x2 },
- /* Line 2 (0x03) unused */
- /* CD (0x04) unused? */
- },
-};
-
-static struct hda_input_mux alc889A_imac91_capture_source = {
- .num_items = 2,
- .items = {
- { "Mic", 0x01 },
- { "Line", 0x2 }, /* Not sure! */
- },
-};
-
-/*
- * 2ch mode
- */
-static struct hda_channel_mode alc883_3ST_2ch_modes[1] = {
- { 2, NULL }
-};
-
-/*
- * 2ch mode
- */
-static struct hda_verb alc882_3ST_ch2_init[] = {
- { 0x18, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80 },
- { 0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE },
- { 0x1a, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN },
- { 0x1a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE },
- { } /* end */
-};
-
-/*
- * 4ch mode
- */
-static struct hda_verb alc882_3ST_ch4_init[] = {
- { 0x18, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80 },
- { 0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE },
- { 0x1a, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT },
- { 0x1a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE },
- { 0x1a, AC_VERB_SET_CONNECT_SEL, 0x01 },
- { } /* end */
-};
-
-/*
- * 6ch mode
- */
-static struct hda_verb alc882_3ST_ch6_init[] = {
- { 0x18, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT },
- { 0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE },
- { 0x18, AC_VERB_SET_CONNECT_SEL, 0x02 },
- { 0x1a, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT },
- { 0x1a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE },
- { 0x1a, AC_VERB_SET_CONNECT_SEL, 0x01 },
- { } /* end */
-};
-
-static struct hda_channel_mode alc882_3ST_6ch_modes[3] = {
- { 2, alc882_3ST_ch2_init },
- { 4, alc882_3ST_ch4_init },
- { 6, alc882_3ST_ch6_init },
-};
-
-#define alc883_3ST_6ch_modes alc882_3ST_6ch_modes
-
-/*
- * 2ch mode
- */
-static struct hda_verb alc883_3ST_ch2_clevo_init[] = {
- { 0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP },
- { 0x18, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80 },
- { 0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE },
- { 0x1a, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN },
- { 0x1a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE },
- { } /* end */
-};
-
-/*
- * 4ch mode
- */
-static struct hda_verb alc883_3ST_ch4_clevo_init[] = {
- { 0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT },
- { 0x18, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80 },
- { 0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE },
- { 0x1a, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT },
- { 0x1a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE },
- { 0x1a, AC_VERB_SET_CONNECT_SEL, 0x01 },
- { } /* end */
-};
-
-/*
- * 6ch mode
- */
-static struct hda_verb alc883_3ST_ch6_clevo_init[] = {
- { 0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT },
- { 0x18, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT },
- { 0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE },
- { 0x18, AC_VERB_SET_CONNECT_SEL, 0x02 },
- { 0x1a, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT },
- { 0x1a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE },
- { 0x1a, AC_VERB_SET_CONNECT_SEL, 0x01 },
- { } /* end */
-};
-
-static struct hda_channel_mode alc883_3ST_6ch_clevo_modes[3] = {
- { 2, alc883_3ST_ch2_clevo_init },
- { 4, alc883_3ST_ch4_clevo_init },
- { 6, alc883_3ST_ch6_clevo_init },
-};
-
/*
- * 6ch mode
- */
-static struct hda_verb alc882_sixstack_ch6_init[] = {
- { 0x17, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x00 },
- { 0x16, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT },
- { 0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT },
- { 0x14, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT },
- { } /* end */
-};
-
-/*
- * 8ch mode
- */
-static struct hda_verb alc882_sixstack_ch8_init[] = {
- { 0x17, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT },
- { 0x16, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT },
- { 0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT },
- { 0x14, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT },
- { } /* end */
-};
-
-static struct hda_channel_mode alc882_sixstack_modes[2] = {
- { 6, alc882_sixstack_ch6_init },
- { 8, alc882_sixstack_ch8_init },
-};
-
-
-/* Macbook Air 2,1 */
-
-static struct hda_channel_mode alc885_mba21_ch_modes[1] = {
- { 2, NULL },
-};
-
-/*
- * macbook pro ALC885 can switch LineIn to LineOut without losing Mic
- */
-
-/*
- * 2ch mode
- */
-static struct hda_verb alc885_mbp_ch2_init[] = {
- { 0x1a, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN },
- { 0x0d, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
- { 0x0d, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
- { } /* end */
-};
-
-/*
- * 4ch mode
- */
-static struct hda_verb alc885_mbp_ch4_init[] = {
- { 0x1a, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT },
- { 0x1a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
- { 0x1a, AC_VERB_SET_CONNECT_SEL, 0x01 },
- { 0x0d, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
- { 0x0d, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
- { } /* end */
-};
-
-static struct hda_channel_mode alc885_mbp_4ch_modes[2] = {
- { 2, alc885_mbp_ch2_init },
- { 4, alc885_mbp_ch4_init },
-};
-
-/*
- * 2ch
- * Speakers/Woofer/HP = Front
- * LineIn = Input
- */
-static struct hda_verb alc885_mb5_ch2_init[] = {
- {0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN},
- {0x15, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
- { } /* end */
-};
-
-/*
- * 6ch mode
- * Speakers/HP = Front
- * Woofer = LFE
- * LineIn = Surround
- */
-static struct hda_verb alc885_mb5_ch6_init[] = {
- {0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
- {0x15, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
- {0x15, AC_VERB_SET_CONNECT_SEL, 0x01},
- { } /* end */
-};
-
-static struct hda_channel_mode alc885_mb5_6ch_modes[2] = {
- { 2, alc885_mb5_ch2_init },
- { 6, alc885_mb5_ch6_init },
-};
-
-#define alc885_macmini3_6ch_modes alc885_mb5_6ch_modes
-
-/*
- * 2ch mode
- */
-static struct hda_verb alc883_4ST_ch2_init[] = {
- { 0x17, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT },
- { 0x17, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE },
- { 0x18, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80 },
- { 0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE },
- { 0x1a, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN },
- { 0x1a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE },
- { } /* end */
-};
-
-/*
- * 4ch mode
- */
-static struct hda_verb alc883_4ST_ch4_init[] = {
- { 0x17, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT },
- { 0x17, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE },
- { 0x18, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80 },
- { 0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE },
- { 0x1a, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT },
- { 0x1a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE },
- { 0x1a, AC_VERB_SET_CONNECT_SEL, 0x01 },
- { } /* end */
-};
-
-/*
- * 6ch mode
- */
-static struct hda_verb alc883_4ST_ch6_init[] = {
- { 0x17, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT },
- { 0x17, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE },
- { 0x18, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT },
- { 0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE },
- { 0x18, AC_VERB_SET_CONNECT_SEL, 0x02 },
- { 0x1a, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT },
- { 0x1a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE },
- { 0x1a, AC_VERB_SET_CONNECT_SEL, 0x01 },
- { } /* end */
-};
-
-/*
- * 8ch mode
- */
-static struct hda_verb alc883_4ST_ch8_init[] = {
- { 0x17, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT },
- { 0x17, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE },
- { 0x17, AC_VERB_SET_CONNECT_SEL, 0x03 },
- { 0x18, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT },
- { 0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE },
- { 0x18, AC_VERB_SET_CONNECT_SEL, 0x02 },
- { 0x1a, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT },
- { 0x1a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE },
- { 0x1a, AC_VERB_SET_CONNECT_SEL, 0x01 },
- { } /* end */
-};
-
-static struct hda_channel_mode alc883_4ST_8ch_modes[4] = {
- { 2, alc883_4ST_ch2_init },
- { 4, alc883_4ST_ch4_init },
- { 6, alc883_4ST_ch6_init },
- { 8, alc883_4ST_ch8_init },
-};
-
-
-/*
- * 2ch mode
- */
-static struct hda_verb alc883_3ST_ch2_intel_init[] = {
- { 0x19, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80 },
- { 0x19, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE },
- { 0x1a, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN },
- { 0x1a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE },
- { } /* end */
-};
-
-/*
- * 4ch mode
- */
-static struct hda_verb alc883_3ST_ch4_intel_init[] = {
- { 0x19, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80 },
- { 0x19, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE },
- { 0x1a, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT },
- { 0x1a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE },
- { 0x1a, AC_VERB_SET_CONNECT_SEL, 0x01 },
- { } /* end */
-};
-
-/*
- * 6ch mode
- */
-static struct hda_verb alc883_3ST_ch6_intel_init[] = {
- { 0x19, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT },
- { 0x19, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE },
- { 0x19, AC_VERB_SET_CONNECT_SEL, 0x02 },
- { 0x1a, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT },
- { 0x1a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE },
- { 0x1a, AC_VERB_SET_CONNECT_SEL, 0x01 },
- { } /* end */
-};
-
-static struct hda_channel_mode alc883_3ST_6ch_intel_modes[3] = {
- { 2, alc883_3ST_ch2_intel_init },
- { 4, alc883_3ST_ch4_intel_init },
- { 6, alc883_3ST_ch6_intel_init },
-};
-
-/*
- * 2ch mode
- */
-static struct hda_verb alc889_ch2_intel_init[] = {
- { 0x14, AC_VERB_SET_CONNECT_SEL, 0x00 },
- { 0x19, AC_VERB_SET_CONNECT_SEL, 0x00 },
- { 0x16, AC_VERB_SET_CONNECT_SEL, 0x00 },
- { 0x17, AC_VERB_SET_CONNECT_SEL, 0x00 },
- { 0x1a, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN },
- { 0x1a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE },
- { } /* end */
-};
-
-/*
- * 6ch mode
- */
-static struct hda_verb alc889_ch6_intel_init[] = {
- { 0x14, AC_VERB_SET_CONNECT_SEL, 0x00 },
- { 0x19, AC_VERB_SET_CONNECT_SEL, 0x01 },
- { 0x16, AC_VERB_SET_CONNECT_SEL, 0x02 },
- { 0x17, AC_VERB_SET_CONNECT_SEL, 0x03 },
- { 0x1a, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN },
- { 0x1a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE },
- { } /* end */
-};
-
-/*
- * 8ch mode
- */
-static struct hda_verb alc889_ch8_intel_init[] = {
- { 0x14, AC_VERB_SET_CONNECT_SEL, 0x00 },
- { 0x19, AC_VERB_SET_CONNECT_SEL, 0x01 },
- { 0x16, AC_VERB_SET_CONNECT_SEL, 0x02 },
- { 0x17, AC_VERB_SET_CONNECT_SEL, 0x03 },
- { 0x1a, AC_VERB_SET_CONNECT_SEL, 0x03 },
- { 0x1a, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT },
- { 0x1a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE },
- { } /* end */
-};
-
-static struct hda_channel_mode alc889_8ch_intel_modes[3] = {
- { 2, alc889_ch2_intel_init },
- { 6, alc889_ch6_intel_init },
- { 8, alc889_ch8_intel_init },
-};
-
-/*
- * 6ch mode
- */
-static struct hda_verb alc883_sixstack_ch6_init[] = {
- { 0x17, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x00 },
- { 0x16, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT },
- { 0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT },
- { 0x14, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT },
- { } /* end */
-};
-
-/*
- * 8ch mode
- */
-static struct hda_verb alc883_sixstack_ch8_init[] = {
- { 0x17, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT },
- { 0x16, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT },
- { 0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT },
- { 0x14, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT },
- { } /* end */
-};
-
-static struct hda_channel_mode alc883_sixstack_modes[2] = {
- { 6, alc883_sixstack_ch6_init },
- { 8, alc883_sixstack_ch8_init },
-};
-
-
-/* Pin assignment: Front=0x14, Rear=0x15, CLFE=0x16, Side=0x17
- * Mic=0x18, Front Mic=0x19, Line-In=0x1a, HP=0x1b
- */
-static struct snd_kcontrol_new alc882_base_mixer[] = {
- HDA_CODEC_VOLUME("Front Playback Volume", 0x0c, 0x0, HDA_OUTPUT),
- HDA_BIND_MUTE("Front Playback Switch", 0x0c, 2, HDA_INPUT),
- HDA_CODEC_VOLUME("Surround Playback Volume", 0x0d, 0x0, HDA_OUTPUT),
- HDA_BIND_MUTE("Surround Playback Switch", 0x0d, 2, HDA_INPUT),
- HDA_CODEC_VOLUME_MONO("Center Playback Volume", 0x0e, 1, 0x0, HDA_OUTPUT),
- HDA_CODEC_VOLUME_MONO("LFE Playback Volume", 0x0e, 2, 0x0, HDA_OUTPUT),
- HDA_BIND_MUTE_MONO("Center Playback Switch", 0x0e, 1, 2, HDA_INPUT),
- HDA_BIND_MUTE_MONO("LFE Playback Switch", 0x0e, 2, 2, HDA_INPUT),
- HDA_CODEC_VOLUME("Side Playback Volume", 0x0f, 0x0, HDA_OUTPUT),
- HDA_BIND_MUTE("Side Playback Switch", 0x0f, 2, HDA_INPUT),
- HDA_CODEC_MUTE("Headphone Playback Switch", 0x1b, 0x0, HDA_OUTPUT),
- HDA_CODEC_VOLUME("CD Playback Volume", 0x0b, 0x04, HDA_INPUT),
- HDA_CODEC_MUTE("CD Playback Switch", 0x0b, 0x04, HDA_INPUT),
- HDA_CODEC_VOLUME("Line Playback Volume", 0x0b, 0x02, HDA_INPUT),
- HDA_CODEC_MUTE("Line Playback Switch", 0x0b, 0x02, HDA_INPUT),
- HDA_CODEC_VOLUME("Mic Playback Volume", 0x0b, 0x0, HDA_INPUT),
- HDA_CODEC_VOLUME("Mic Boost Volume", 0x18, 0, HDA_INPUT),
- HDA_CODEC_MUTE("Mic Playback Switch", 0x0b, 0x0, HDA_INPUT),
- HDA_CODEC_VOLUME("Front Mic Playback Volume", 0x0b, 0x1, HDA_INPUT),
- HDA_CODEC_VOLUME("Front Mic Boost Volume", 0x19, 0, HDA_INPUT),
- HDA_CODEC_MUTE("Front Mic Playback Switch", 0x0b, 0x1, HDA_INPUT),
- { } /* end */
-};
-
-/* Macbook Air 2,1 same control for HP and internal Speaker */
-
-static struct snd_kcontrol_new alc885_mba21_mixer[] = {
- HDA_CODEC_VOLUME("Speaker Playback Volume", 0x0c, 0x00, HDA_OUTPUT),
- HDA_BIND_MUTE("Speaker Playback Switch", 0x0c, 0x02, HDA_OUTPUT),
- { }
-};
-
-
-static struct snd_kcontrol_new alc885_mbp3_mixer[] = {
- HDA_CODEC_VOLUME("Speaker Playback Volume", 0x0c, 0x00, HDA_OUTPUT),
- HDA_BIND_MUTE ("Speaker Playback Switch", 0x0c, 0x02, HDA_INPUT),
- HDA_CODEC_VOLUME("Headphone Playback Volume", 0x0e, 0x00, HDA_OUTPUT),
- HDA_BIND_MUTE ("Headphone Playback Switch", 0x0e, 0x02, HDA_INPUT),
- HDA_CODEC_VOLUME("Surround Playback Volume", 0x0d, 0x00, HDA_OUTPUT),
- HDA_CODEC_VOLUME("Line Playback Volume", 0x0b, 0x02, HDA_INPUT),
- HDA_CODEC_MUTE ("Line Playback Switch", 0x0b, 0x02, HDA_INPUT),
- HDA_CODEC_VOLUME("Mic Playback Volume", 0x0b, 0x00, HDA_INPUT),
- HDA_CODEC_MUTE ("Mic Playback Switch", 0x0b, 0x00, HDA_INPUT),
- HDA_CODEC_VOLUME("Line Boost Volume", 0x1a, 0x00, HDA_INPUT),
- HDA_CODEC_VOLUME("Mic Boost Volume", 0x18, 0x00, HDA_INPUT),
- { } /* end */
-};
-
-static struct snd_kcontrol_new alc885_mb5_mixer[] = {
- HDA_CODEC_VOLUME("Front Playback Volume", 0x0c, 0x00, HDA_OUTPUT),
- HDA_BIND_MUTE ("Front Playback Switch", 0x0c, 0x02, HDA_INPUT),
- HDA_CODEC_VOLUME("Surround Playback Volume", 0x0d, 0x00, HDA_OUTPUT),
- HDA_BIND_MUTE ("Surround Playback Switch", 0x0d, 0x02, HDA_INPUT),
- HDA_CODEC_VOLUME("LFE Playback Volume", 0x0e, 0x00, HDA_OUTPUT),
- HDA_BIND_MUTE ("LFE Playback Switch", 0x0e, 0x02, HDA_INPUT),
- HDA_CODEC_VOLUME("Headphone Playback Volume", 0x0f, 0x00, HDA_OUTPUT),
- HDA_BIND_MUTE ("Headphone Playback Switch", 0x0f, 0x02, HDA_INPUT),
- HDA_CODEC_VOLUME("Line Playback Volume", 0x0b, 0x07, HDA_INPUT),
- HDA_CODEC_MUTE ("Line Playback Switch", 0x0b, 0x07, HDA_INPUT),
- HDA_CODEC_VOLUME("Mic Playback Volume", 0x0b, 0x01, HDA_INPUT),
- HDA_CODEC_MUTE ("Mic Playback Switch", 0x0b, 0x01, HDA_INPUT),
- HDA_CODEC_VOLUME("Line Boost Volume", 0x15, 0x00, HDA_INPUT),
- HDA_CODEC_VOLUME("Mic Boost Volume", 0x19, 0x00, HDA_INPUT),
- { } /* end */
-};
-
-static struct snd_kcontrol_new alc885_macmini3_mixer[] = {
- HDA_CODEC_VOLUME("Front Playback Volume", 0x0c, 0x00, HDA_OUTPUT),
- HDA_BIND_MUTE ("Front Playback Switch", 0x0c, 0x02, HDA_INPUT),
- HDA_CODEC_VOLUME("Surround Playback Volume", 0x0d, 0x00, HDA_OUTPUT),
- HDA_BIND_MUTE ("Surround Playback Switch", 0x0d, 0x02, HDA_INPUT),
- HDA_CODEC_VOLUME("LFE Playback Volume", 0x0e, 0x00, HDA_OUTPUT),
- HDA_BIND_MUTE ("LFE Playback Switch", 0x0e, 0x02, HDA_INPUT),
- HDA_CODEC_VOLUME("Headphone Playback Volume", 0x0f, 0x00, HDA_OUTPUT),
- HDA_BIND_MUTE ("Headphone Playback Switch", 0x0f, 0x02, HDA_INPUT),
- HDA_CODEC_VOLUME("Line Playback Volume", 0x0b, 0x07, HDA_INPUT),
- HDA_CODEC_MUTE ("Line Playback Switch", 0x0b, 0x07, HDA_INPUT),
- HDA_CODEC_VOLUME("Line Boost Volume", 0x15, 0x00, HDA_INPUT),
- { } /* end */
-};
-
-static struct snd_kcontrol_new alc885_imac91_mixer[] = {
- HDA_CODEC_VOLUME("Speaker Playback Volume", 0x0c, 0x00, HDA_OUTPUT),
- HDA_BIND_MUTE("Speaker Playback Switch", 0x0c, 0x02, HDA_INPUT),
- { } /* end */
-};
-
-
-static struct snd_kcontrol_new alc882_w2jc_mixer[] = {
- HDA_CODEC_VOLUME("Front Playback Volume", 0x0c, 0x0, HDA_OUTPUT),
- HDA_BIND_MUTE("Front Playback Switch", 0x0c, 2, HDA_INPUT),
- HDA_CODEC_VOLUME("CD Playback Volume", 0x0b, 0x04, HDA_INPUT),
- HDA_CODEC_MUTE("CD Playback Switch", 0x0b, 0x04, HDA_INPUT),
- HDA_CODEC_VOLUME("Line Playback Volume", 0x0b, 0x02, HDA_INPUT),
- HDA_CODEC_MUTE("Line Playback Switch", 0x0b, 0x02, HDA_INPUT),
- HDA_CODEC_VOLUME("Mic Playback Volume", 0x0b, 0x0, HDA_INPUT),
- HDA_CODEC_VOLUME("Mic Boost Volume", 0x18, 0, HDA_INPUT),
- HDA_CODEC_MUTE("Mic Playback Switch", 0x0b, 0x0, HDA_INPUT),
- { } /* end */
-};
-
-static struct snd_kcontrol_new alc882_targa_mixer[] = {
- HDA_CODEC_VOLUME("Front Playback Volume", 0x0c, 0x0, HDA_OUTPUT),
- HDA_BIND_MUTE("Front Playback Switch", 0x0c, 2, HDA_INPUT),
- HDA_CODEC_MUTE("Headphone Playback Switch", 0x1b, 0x0, HDA_OUTPUT),
- HDA_CODEC_VOLUME("CD Playback Volume", 0x0b, 0x04, HDA_INPUT),
- HDA_CODEC_MUTE("CD Playback Switch", 0x0b, 0x04, HDA_INPUT),
- HDA_CODEC_VOLUME("Line Playback Volume", 0x0b, 0x02, HDA_INPUT),
- HDA_CODEC_MUTE("Line Playback Switch", 0x0b, 0x02, HDA_INPUT),
- HDA_CODEC_VOLUME("Mic Playback Volume", 0x0b, 0x0, HDA_INPUT),
- HDA_CODEC_MUTE("Mic Playback Switch", 0x0b, 0x0, HDA_INPUT),
- HDA_CODEC_VOLUME("Mic Boost Volume", 0x18, 0, HDA_INPUT),
- HDA_CODEC_VOLUME("Front Mic Playback Volume", 0x0b, 0x1, HDA_INPUT),
- HDA_CODEC_MUTE("Front Mic Playback Switch", 0x0b, 0x1, HDA_INPUT),
- HDA_CODEC_VOLUME("Front Mic Boost Volume", 0x19, 0, HDA_INPUT),
- { } /* end */
-};
-
-/* Pin assignment: Front=0x14, HP = 0x15, Front = 0x16, ???
- * Front Mic=0x18, Line In = 0x1a, Line In = 0x1b, CD = 0x1c
+ * Pin config fixes
*/
-static struct snd_kcontrol_new alc882_asus_a7j_mixer[] = {
- HDA_CODEC_VOLUME("Front Playback Volume", 0x0c, 0x0, HDA_OUTPUT),
- HDA_CODEC_MUTE("Front Playback Switch", 0x14, 0x0, HDA_OUTPUT),
- HDA_CODEC_MUTE("Headphone Playback Switch", 0x15, 0x0, HDA_OUTPUT),
- HDA_CODEC_MUTE("Mobile Front Playback Switch", 0x16, 0x0, HDA_OUTPUT),
- HDA_CODEC_VOLUME("CD Playback Volume", 0x0b, 0x04, HDA_INPUT),
- HDA_CODEC_MUTE("CD Playback Switch", 0x0b, 0x04, HDA_INPUT),
- HDA_CODEC_VOLUME("Line Playback Volume", 0x0b, 0x02, HDA_INPUT),
- HDA_CODEC_MUTE("Line Playback Switch", 0x0b, 0x02, HDA_INPUT),
- HDA_CODEC_VOLUME("Mobile Line Playback Volume", 0x0b, 0x03, HDA_INPUT),
- HDA_CODEC_MUTE("Mobile Line Playback Switch", 0x0b, 0x03, HDA_INPUT),
- HDA_CODEC_VOLUME("Mic Playback Volume", 0x0b, 0x0, HDA_INPUT),
- HDA_CODEC_MUTE("Mic Playback Switch", 0x0b, 0x0, HDA_INPUT),
- HDA_CODEC_VOLUME("Mic Boost Volume", 0x18, 0, HDA_INPUT),
- { } /* end */
-};
-
-static struct snd_kcontrol_new alc882_asus_a7m_mixer[] = {
- HDA_CODEC_VOLUME("Front Playback Volume", 0x0c, 0x0, HDA_OUTPUT),
- HDA_BIND_MUTE("Front Playback Switch", 0x0c, 2, HDA_INPUT),
- HDA_CODEC_MUTE("Headphone Playback Switch", 0x15, 0x0, HDA_OUTPUT),
- HDA_CODEC_VOLUME("CD Playback Volume", 0x0b, 0x04, HDA_INPUT),
- HDA_CODEC_MUTE("CD Playback Switch", 0x0b, 0x04, HDA_INPUT),
- HDA_CODEC_VOLUME("Line Playback Volume", 0x0b, 0x02, HDA_INPUT),
- HDA_CODEC_MUTE("Line Playback Switch", 0x0b, 0x02, HDA_INPUT),
- HDA_CODEC_VOLUME("Mic Playback Volume", 0x0b, 0x0, HDA_INPUT),
- HDA_CODEC_VOLUME("Mic Boost Volume", 0x18, 0, HDA_INPUT),
- HDA_CODEC_MUTE("Mic Playback Switch", 0x0b, 0x0, HDA_INPUT),
- { } /* end */
-};
-
-static struct snd_kcontrol_new alc882_chmode_mixer[] = {
- {
- .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
- .name = "Channel Mode",
- .info = alc_ch_mode_info,
- .get = alc_ch_mode_get,
- .put = alc_ch_mode_put,
- },
- { } /* end */
-};
-
-static struct hda_verb alc882_base_init_verbs[] = {
- /* Front mixer: unmute input/output amp left and right (volume = 0) */
- {0x0c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
- {0x0c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
- /* Rear mixer */
- {0x0d, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
- {0x0d, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
- /* CLFE mixer */
- {0x0e, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
- {0x0e, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
- /* Side mixer */
- {0x0f, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
- {0x0f, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
-
- /* Front Pin: output 0 (0x0c) */
- {0x14, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
- {0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
- {0x14, AC_VERB_SET_CONNECT_SEL, 0x00},
- /* Rear Pin: output 1 (0x0d) */
- {0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
- {0x15, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
- {0x15, AC_VERB_SET_CONNECT_SEL, 0x01},
- /* CLFE Pin: output 2 (0x0e) */
- {0x16, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
- {0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
- {0x16, AC_VERB_SET_CONNECT_SEL, 0x02},
- /* Side Pin: output 3 (0x0f) */
- {0x17, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
- {0x17, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
- {0x17, AC_VERB_SET_CONNECT_SEL, 0x03},
- /* Mic (rear) pin: input vref at 80% */
- {0x18, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80},
- {0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
- /* Front Mic pin: input vref at 80% */
- {0x19, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80},
- {0x19, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
- /* Line In pin: input */
- {0x1a, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN},
- {0x1a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
- /* Line-2 In: Headphone output (output 0 - 0x0c) */
- {0x1b, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP},
- {0x1b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
- {0x1b, AC_VERB_SET_CONNECT_SEL, 0x00},
- /* CD pin widget for input */
- {0x1c, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN},
-
- /* FIXME: use matrix-type input source selection */
- /* Mixer elements: 0x18, 19, 1a, 1b, 1c, 1d, 14, 15, 16, 17, 0b */
- /* Input mixer2 */
- {0x23, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
- /* Input mixer3 */
- {0x22, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
- /* ADC2: mute amp left and right */
- {0x08, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
- {0x08, AC_VERB_SET_CONNECT_SEL, 0x00},
- /* ADC3: mute amp left and right */
- {0x09, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
- {0x09, AC_VERB_SET_CONNECT_SEL, 0x00},
-
- { }
-};
-
-static struct hda_verb alc882_adc1_init_verbs[] = {
- /* Input mixer1: unmute Mic, F-Mic, Line, CD inputs */
- {0x24, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
- {0x24, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(3)},
- {0x24, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(2)},
- {0x24, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(4)},
- /* ADC1: mute amp left and right */
- {0x07, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
- {0x07, AC_VERB_SET_CONNECT_SEL, 0x00},
- { }
-};
-
-static struct hda_verb alc882_eapd_verbs[] = {
- /* change to EAPD mode */
- {0x20, AC_VERB_SET_COEF_INDEX, 0x07},
- {0x20, AC_VERB_SET_PROC_COEF, 0x3060},
- { }
-};
-
-static struct hda_verb alc889_eapd_verbs[] = {
- {0x14, AC_VERB_SET_EAPD_BTLENABLE, 2},
- {0x15, AC_VERB_SET_EAPD_BTLENABLE, 2},
- { }
-};
-
-static struct hda_verb alc_hp15_unsol_verbs[] = {
- {0x15, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | ALC880_HP_EVENT},
- {0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP},
- {}
-};
-
-static struct hda_verb alc885_init_verbs[] = {
- /* Front mixer: unmute input/output amp left and right (volume = 0) */
- {0x0c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
- {0x0c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
- /* Rear mixer */
- {0x0d, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
- {0x0d, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
- /* CLFE mixer */
- {0x0e, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
- {0x0e, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
- /* Side mixer */
- {0x0f, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
- {0x0f, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
-
- /* Front HP Pin: output 0 (0x0c) */
- {0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP},
- {0x15, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
- {0x15, AC_VERB_SET_CONNECT_SEL, 0x00},
- /* Front Pin: output 0 (0x0c) */
- {0x14, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
- {0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
- {0x14, AC_VERB_SET_CONNECT_SEL, 0x00},
- /* Rear Pin: output 1 (0x0d) */
- {0x19, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
- {0x19, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
- {0x19, AC_VERB_SET_CONNECT_SEL, 0x01},
- /* CLFE Pin: output 2 (0x0e) */
- {0x16, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
- {0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
- {0x16, AC_VERB_SET_CONNECT_SEL, 0x02},
- /* Side Pin: output 3 (0x0f) */
- {0x17, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
- {0x17, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
- {0x17, AC_VERB_SET_CONNECT_SEL, 0x03},
- /* Mic (rear) pin: input vref at 80% */
- {0x1b, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80},
- {0x1b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
- /* Front Mic pin: input vref at 80% */
- {0x18, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80},
- {0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
- /* Line In pin: input */
- {0x1a, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN},
- {0x1a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
-
- /* Mixer elements: 0x18, , 0x1a, 0x1b */
- /* Input mixer1 */
- {0x24, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
- /* Input mixer2 */
- {0x23, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
- /* Input mixer3 */
- {0x22, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
- /* ADC2: mute amp left and right */
- {0x08, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
- /* ADC3: mute amp left and right */
- {0x09, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
-
- { }
-};
-
-static struct hda_verb alc885_init_input_verbs[] = {
- {0x24, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
- {0x24, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(2)},
- {0x24, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(3)},
- { }
-};
-
-
-/* Unmute Selector 24h and set the default input to front mic */
-static struct hda_verb alc889_init_input_verbs[] = {
- {0x24, AC_VERB_SET_CONNECT_SEL, 0x00},
- {0x24, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
- { }
-};
-
-
-#define alc883_init_verbs alc882_base_init_verbs
-
-/* Mac Pro test */
-static struct snd_kcontrol_new alc882_macpro_mixer[] = {
- HDA_CODEC_VOLUME("Front Playback Volume", 0x0c, 0x0, HDA_OUTPUT),
- HDA_BIND_MUTE("Front Playback Switch", 0x0c, 2, HDA_INPUT),
- HDA_CODEC_MUTE("Headphone Playback Switch", 0x18, 0x0, HDA_OUTPUT),
- HDA_CODEC_VOLUME("Line Playback Volume", 0x0b, 0x01, HDA_INPUT),
- HDA_CODEC_MUTE("Line Playback Switch", 0x0b, 0x01, HDA_INPUT),
- /* FIXME: this looks suspicious...
- HDA_CODEC_VOLUME("Beep Playback Volume", 0x0b, 0x02, HDA_INPUT),
- HDA_CODEC_MUTE("Beep Playback Switch", 0x0b, 0x02, HDA_INPUT),
- */
- { } /* end */
-};
-
-static struct hda_verb alc882_macpro_init_verbs[] = {
- /* Front mixer: unmute input/output amp left and right (volume = 0) */
- {0x0c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
- {0x0c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
- {0x0c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
- /* Front Pin: output 0 (0x0c) */
- {0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
- {0x15, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
- {0x15, AC_VERB_SET_CONNECT_SEL, 0x00},
- /* Front Mic pin: input vref at 80% */
- {0x19, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80},
- {0x19, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
- /* Speaker: output */
- {0x1a, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
- {0x1a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
- {0x1a, AC_VERB_SET_CONNECT_SEL, 0x04},
- /* Headphone output (output 0 - 0x0c) */
- {0x18, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP},
- {0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
- {0x18, AC_VERB_SET_CONNECT_SEL, 0x00},
-
- /* FIXME: use matrix-type input source selection */
- /* Mixer elements: 0x18, 19, 1a, 1b, 1c, 1d, 14, 15, 16, 17, 0b */
- /* Input mixer1: unmute Mic, F-Mic, Line, CD inputs */
- {0x24, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
- {0x24, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(3)},
- {0x24, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(2)},
- {0x24, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(4)},
- /* Input mixer2 */
- {0x23, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
- {0x23, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(3)},
- {0x23, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(2)},
- {0x23, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(4)},
- /* Input mixer3 */
- {0x22, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
- {0x22, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(3)},
- {0x22, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(2)},
- {0x22, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(4)},
- /* ADC1: mute amp left and right */
- {0x07, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
- {0x07, AC_VERB_SET_CONNECT_SEL, 0x00},
- /* ADC2: mute amp left and right */
- {0x08, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
- {0x08, AC_VERB_SET_CONNECT_SEL, 0x00},
- /* ADC3: mute amp left and right */
- {0x09, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
- {0x09, AC_VERB_SET_CONNECT_SEL, 0x00},
-
- { }
-};
-
-/* Macbook 5,1 */
-static struct hda_verb alc885_mb5_init_verbs[] = {
- /* DACs */
- {0x02, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
- {0x03, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
- {0x04, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
- {0x05, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
- /* Front mixer */
- {0x0c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
- {0x0c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
- {0x0c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
- /* Surround mixer */
- {0x0d, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
- {0x0d, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
- {0x0d, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
- /* LFE mixer */
- {0x0e, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
- {0x0e, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
- {0x0e, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
- /* HP mixer */
- {0x0f, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
- {0x0f, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
- {0x0f, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
- /* Front Pin (0x0c) */
- {0x18, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT | 0x01},
- {0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
- {0x18, AC_VERB_SET_CONNECT_SEL, 0x00},
- /* LFE Pin (0x0e) */
- {0x1a, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT | 0x01},
- {0x1a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
- {0x1a, AC_VERB_SET_CONNECT_SEL, 0x02},
- /* HP Pin (0x0f) */
- {0x14, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
- {0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
- {0x14, AC_VERB_SET_CONNECT_SEL, 0x03},
- {0x14, AC_VERB_SET_UNSOLICITED_ENABLE, ALC880_HP_EVENT | AC_USRSP_EN},
- /* Front Mic pin: input vref at 80% */
- {0x19, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80},
- {0x19, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
- /* Line In pin */
- {0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN},
- {0x15, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
-
- {0x24, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0x1)},
- {0x24, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0x7)},
- {0x24, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0x4)},
- { }
-};
-
-/* Macmini 3,1 */
-static struct hda_verb alc885_macmini3_init_verbs[] = {
- /* DACs */
- {0x02, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
- {0x03, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
- {0x04, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
- {0x05, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
- /* Front mixer */
- {0x0c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
- {0x0c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
- {0x0c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
- /* Surround mixer */
- {0x0d, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
- {0x0d, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
- {0x0d, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
- /* LFE mixer */
- {0x0e, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
- {0x0e, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
- {0x0e, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
- /* HP mixer */
- {0x0f, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
- {0x0f, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
- {0x0f, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
- /* Front Pin (0x0c) */
- {0x18, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT | 0x01},
- {0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
- {0x18, AC_VERB_SET_CONNECT_SEL, 0x00},
- /* LFE Pin (0x0e) */
- {0x1a, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT | 0x01},
- {0x1a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
- {0x1a, AC_VERB_SET_CONNECT_SEL, 0x02},
- /* HP Pin (0x0f) */
- {0x14, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
- {0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
- {0x14, AC_VERB_SET_CONNECT_SEL, 0x03},
- {0x14, AC_VERB_SET_UNSOLICITED_ENABLE, ALC880_HP_EVENT | AC_USRSP_EN},
- /* Line In pin */
- {0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN},
- {0x15, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
-
- {0x24, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
- {0x24, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(3)},
- {0x24, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(2)},
- {0x24, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(4)},
- { }
-};
-
-
-static struct hda_verb alc885_mba21_init_verbs[] = {
- /*Internal and HP Speaker Mixer*/
- {0x0c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
- {0x0c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
- {0x0c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
- /*Internal Speaker Pin (0x0c)*/
- {0x18, AC_VERB_SET_PIN_WIDGET_CONTROL, (PIN_OUT | AC_PINCTL_VREF_50) },
- {0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
- {0x18, AC_VERB_SET_CONNECT_SEL, 0x00},
- /* HP Pin: output 0 (0x0e) */
- {0x14, AC_VERB_SET_PIN_WIDGET_CONTROL, 0xc4},
- {0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
- {0x14, AC_VERB_SET_CONNECT_SEL, 0x00},
- {0x14, AC_VERB_SET_UNSOLICITED_ENABLE, (ALC880_HP_EVENT | AC_USRSP_EN)},
- /* Line in (is hp when jack connected)*/
- {0x19, AC_VERB_SET_PIN_WIDGET_CONTROL, AC_PINCTL_VREF_50},
- {0x19, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
-
- { }
- };
-
-
-/* Macbook Pro rev3 */
-static struct hda_verb alc885_mbp3_init_verbs[] = {
- /* Front mixer: unmute input/output amp left and right (volume = 0) */
- {0x0c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
- {0x0c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
- {0x0c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
- /* Rear mixer */
- {0x0d, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
- {0x0d, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
- {0x0d, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
- /* HP mixer */
- {0x0e, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
- {0x0e, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
- {0x0e, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
- /* Front Pin: output 0 (0x0c) */
- {0x14, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
- {0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
- {0x14, AC_VERB_SET_CONNECT_SEL, 0x00},
- /* HP Pin: output 0 (0x0e) */
- {0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, 0xc4},
- {0x15, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
- {0x15, AC_VERB_SET_CONNECT_SEL, 0x02},
- {0x15, AC_VERB_SET_UNSOLICITED_ENABLE, ALC880_HP_EVENT | AC_USRSP_EN},
- /* Mic (rear) pin: input vref at 80% */
- {0x18, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80},
- {0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
- /* Front Mic pin: input vref at 80% */
- {0x19, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80},
- {0x19, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
- /* Line In pin: use output 1 when in LineOut mode */
- {0x1a, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN},
- {0x1a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
- {0x1a, AC_VERB_SET_CONNECT_SEL, 0x01},
-
- /* FIXME: use matrix-type input source selection */
- /* Mixer elements: 0x18, 19, 1a, 1b, 1c, 1d, 14, 15, 16, 17, 0b */
- /* Input mixer1: unmute Mic, F-Mic, Line, CD inputs */
- {0x24, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
- {0x24, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(3)},
- {0x24, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(2)},
- {0x24, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(4)},
- /* Input mixer2 */
- {0x23, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
- {0x23, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(3)},
- {0x23, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(2)},
- {0x23, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(4)},
- /* Input mixer3 */
- {0x22, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
- {0x22, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(3)},
- {0x22, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(2)},
- {0x22, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(4)},
- /* ADC1: mute amp left and right */
- {0x07, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
- {0x07, AC_VERB_SET_CONNECT_SEL, 0x00},
- /* ADC2: mute amp left and right */
- {0x08, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
- {0x08, AC_VERB_SET_CONNECT_SEL, 0x00},
- /* ADC3: mute amp left and right */
- {0x09, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
- {0x09, AC_VERB_SET_CONNECT_SEL, 0x00},
-
- { }
-};
-
-/* iMac 9,1 */
-static struct hda_verb alc885_imac91_init_verbs[] = {
- /* Internal Speaker Pin (0x0c) */
- {0x18, AC_VERB_SET_PIN_WIDGET_CONTROL, (PIN_OUT | AC_PINCTL_VREF_50) },
- {0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
- {0x18, AC_VERB_SET_CONNECT_SEL, 0x00},
- {0x1a, AC_VERB_SET_PIN_WIDGET_CONTROL, (PIN_OUT | AC_PINCTL_VREF_50) },
- {0x1a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
- {0x1a, AC_VERB_SET_CONNECT_SEL, 0x00},
- /* HP Pin: Rear */
- {0x14, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP},
- {0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
- {0x14, AC_VERB_SET_CONNECT_SEL, 0x00},
- {0x14, AC_VERB_SET_UNSOLICITED_ENABLE, (ALC880_HP_EVENT | AC_USRSP_EN)},
- /* Line in Rear */
- {0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, AC_PINCTL_VREF_50},
- {0x15, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
- /* Front Mic pin: input vref at 80% */
- {0x19, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80},
- {0x19, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
- /* Rear mixer */
- {0x0d, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
- {0x0d, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
- {0x0d, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
- /* Line-Out mixer: unmute input/output amp left and right (volume = 0) */
- {0x0c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
- {0x0c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
- {0x0c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
- /* 0x24 [Audio Mixer] wcaps 0x20010b: Stereo Amp-In */
- {0x24, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
- {0x24, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(3)},
- {0x24, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(2)},
- {0x24, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(4)},
- /* 0x23 [Audio Mixer] wcaps 0x20010b: Stereo Amp-In */
- {0x23, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
- {0x23, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(3)},
- {0x23, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(2)},
- {0x23, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(4)},
- /* 0x22 [Audio Mixer] wcaps 0x20010b: Stereo Amp-In */
- {0x22, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
- {0x22, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(3)},
- {0x22, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(2)},
- {0x22, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(4)},
- /* 0x07 [Audio Input] wcaps 0x10011b: Stereo Amp-In */
- {0x07, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
- {0x07, AC_VERB_SET_CONNECT_SEL, 0x00},
- /* 0x08 [Audio Input] wcaps 0x10011b: Stereo Amp-In */
- {0x08, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
- {0x08, AC_VERB_SET_CONNECT_SEL, 0x00},
- /* 0x09 [Audio Input] wcaps 0x10011b: Stereo Amp-In */
- {0x09, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
- {0x09, AC_VERB_SET_CONNECT_SEL, 0x00},
- { }
-};
-
-/* iMac 24 mixer. */
-static struct snd_kcontrol_new alc885_imac24_mixer[] = {
- HDA_CODEC_VOLUME("Master Playback Volume", 0x0c, 0x00, HDA_OUTPUT),
- HDA_CODEC_MUTE("Master Playback Switch", 0x0c, 0x00, HDA_INPUT),
- { } /* end */
-};
-
-/* iMac 24 init verbs. */
-static struct hda_verb alc885_imac24_init_verbs[] = {
- /* Internal speakers: output 0 (0x0c) */
- {0x18, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
- {0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
- {0x18, AC_VERB_SET_CONNECT_SEL, 0x00},
- /* Internal speakers: output 0 (0x0c) */
- {0x1a, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
- {0x1a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
- {0x1a, AC_VERB_SET_CONNECT_SEL, 0x00},
- /* Headphone: output 0 (0x0c) */
- {0x14, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP},
- {0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
- {0x14, AC_VERB_SET_CONNECT_SEL, 0x00},
- {0x14, AC_VERB_SET_UNSOLICITED_ENABLE, ALC880_HP_EVENT | AC_USRSP_EN},
- /* Front Mic: input vref at 80% */
- {0x19, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80},
- {0x19, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
- { }
-};
-
-/* Toggle speaker-output according to the hp-jack state */
-static void alc885_imac24_setup(struct hda_codec *codec)
-{
- struct alc_spec *spec = codec->spec;
-
- spec->autocfg.hp_pins[0] = 0x14;
- spec->autocfg.speaker_pins[0] = 0x18;
- spec->autocfg.speaker_pins[1] = 0x1a;
-}
-
-#define alc885_mb5_setup alc885_imac24_setup
-#define alc885_macmini3_setup alc885_imac24_setup
-
-/* Macbook Air 2,1 */
-static void alc885_mba21_setup(struct hda_codec *codec)
-{
- struct alc_spec *spec = codec->spec;
-
- spec->autocfg.hp_pins[0] = 0x14;
- spec->autocfg.speaker_pins[0] = 0x18;
-}
-
-
-
-static void alc885_mbp3_setup(struct hda_codec *codec)
-{
- struct alc_spec *spec = codec->spec;
-
- spec->autocfg.hp_pins[0] = 0x15;
- spec->autocfg.speaker_pins[0] = 0x14;
-}
-
-static void alc885_imac91_setup(struct hda_codec *codec)
-{
- struct alc_spec *spec = codec->spec;
-
- spec->autocfg.hp_pins[0] = 0x14;
- spec->autocfg.speaker_pins[0] = 0x18;
- spec->autocfg.speaker_pins[1] = 0x1a;
+enum {
+ ALC882_FIXUP_ABIT_AW9D_MAX,
+ ALC882_FIXUP_LENOVO_Y530,
+ ALC882_FIXUP_PB_M5210,
+ ALC882_FIXUP_ACER_ASPIRE_7736,
+ ALC882_FIXUP_ASUS_W90V,
+ ALC889_FIXUP_CD,
+ ALC889_FIXUP_FRONT_HP_NO_PRESENCE,
+ ALC889_FIXUP_VAIO_TT,
+ ALC888_FIXUP_EEE1601,
+ ALC882_FIXUP_EAPD,
+ ALC883_FIXUP_EAPD,
+ ALC883_FIXUP_ACER_EAPD,
+ ALC882_FIXUP_GPIO1,
+ ALC882_FIXUP_GPIO2,
+ ALC882_FIXUP_GPIO3,
+ ALC889_FIXUP_COEF,
+ ALC882_FIXUP_ASUS_W2JC,
+ ALC882_FIXUP_ACER_ASPIRE_4930G,
+ ALC882_FIXUP_ACER_ASPIRE_8930G,
+ ALC882_FIXUP_ASPIRE_8930G_VERBS,
+ ALC885_FIXUP_MACPRO_GPIO,
+ ALC889_FIXUP_DAC_ROUTE,
+ ALC889_FIXUP_MBP_VREF,
+ ALC889_FIXUP_IMAC91_VREF,
+ ALC889_FIXUP_MBA11_VREF,
+ ALC889_FIXUP_MBA21_VREF,
+ ALC889_FIXUP_MP11_VREF,
+ ALC882_FIXUP_INV_DMIC,
+ ALC882_FIXUP_NO_PRIMARY_HP,
+ ALC887_FIXUP_ASUS_BASS,
+ ALC887_FIXUP_BASS_CHMAP,
+};
+
+static void alc889_fixup_coef(struct hda_codec *codec,
+ const struct hda_fixup *fix, int action)
+{
+ if (action != HDA_FIXUP_ACT_INIT)
+ return;
+ alc889_coef_init(codec);
}
-static struct hda_verb alc882_targa_verbs[] = {
- {0x0c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
- {0x0c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
-
- {0x14, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP},
- {0x1b, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
-
- {0x18, AC_VERB_SET_CONNECT_SEL, 0x02}, /* mic/clfe */
- {0x1a, AC_VERB_SET_CONNECT_SEL, 0x01}, /* line/surround */
- {0x1b, AC_VERB_SET_CONNECT_SEL, 0x00}, /* HP */
-
- {0x14, AC_VERB_SET_UNSOLICITED_ENABLE, ALC880_HP_EVENT | AC_USRSP_EN},
- { } /* end */
-};
-
/* toggle speaker-output according to the hp-jack state */
-static void alc882_targa_automute(struct hda_codec *codec)
-{
- struct alc_spec *spec = codec->spec;
- alc_automute_amp(codec);
- snd_hda_codec_write_cache(codec, 1, 0, AC_VERB_SET_GPIO_DATA,
- spec->jack_present ? 1 : 3);
-}
-
-static void alc882_targa_setup(struct hda_codec *codec)
-{
- struct alc_spec *spec = codec->spec;
-
- spec->autocfg.hp_pins[0] = 0x14;
- spec->autocfg.speaker_pins[0] = 0x1b;
-}
-
-static void alc882_targa_unsol_event(struct hda_codec *codec, unsigned int res)
-{
- if ((res >> 26) == ALC880_HP_EVENT)
- alc882_targa_automute(codec);
-}
-
-static struct hda_verb alc882_asus_a7j_verbs[] = {
- {0x0c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
- {0x0c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
-
- {0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP},
- {0x14, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
- {0x16, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
-
- {0x14, AC_VERB_SET_CONNECT_SEL, 0x00}, /* Front */
- {0x15, AC_VERB_SET_CONNECT_SEL, 0x00}, /* HP */
- {0x16, AC_VERB_SET_CONNECT_SEL, 0x00}, /* Front */
-
- {0x18, AC_VERB_SET_CONNECT_SEL, 0x02}, /* mic/clfe */
- {0x1a, AC_VERB_SET_CONNECT_SEL, 0x01}, /* line/surround */
- {0x1b, AC_VERB_SET_CONNECT_SEL, 0x00}, /* HP */
- { } /* end */
-};
-
-static struct hda_verb alc882_asus_a7m_verbs[] = {
- {0x0c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
- {0x0c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
-
- {0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP},
- {0x14, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
- {0x16, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
-
- {0x14, AC_VERB_SET_CONNECT_SEL, 0x00}, /* Front */
- {0x15, AC_VERB_SET_CONNECT_SEL, 0x00}, /* HP */
- {0x16, AC_VERB_SET_CONNECT_SEL, 0x00}, /* Front */
-
- {0x18, AC_VERB_SET_CONNECT_SEL, 0x02}, /* mic/clfe */
- {0x1a, AC_VERB_SET_CONNECT_SEL, 0x01}, /* line/surround */
- {0x1b, AC_VERB_SET_CONNECT_SEL, 0x00}, /* HP */
- { } /* end */
-};
-
static void alc882_gpio_mute(struct hda_codec *codec, int pin, int muted)
{
unsigned int gpiostate, gpiomask, gpiodir;
@@ -8739,2271 +1889,460 @@ static void alc882_gpio_mute(struct hda_codec *codec, int pin, int muted)
}
/* set up GPIO at initialization */
-static void alc885_macpro_init_hook(struct hda_codec *codec)
+static void alc885_fixup_macpro_gpio(struct hda_codec *codec,
+ const struct hda_fixup *fix, int action)
{
+ if (action != HDA_FIXUP_ACT_INIT)
+ return;
alc882_gpio_mute(codec, 0, 0);
alc882_gpio_mute(codec, 1, 0);
}
-/* set up GPIO and update auto-muting at initialization */
-static void alc885_imac24_init_hook(struct hda_codec *codec)
-{
- alc885_macpro_init_hook(codec);
- alc_automute_amp(codec);
-}
-
-/*
- * generic initialization of ADC, input mixers and output mixers
- */
-static struct hda_verb alc883_auto_init_verbs[] = {
- /*
- * Unmute ADC0-2 and set the default input to mic-in
- */
- {0x08, AC_VERB_SET_CONNECT_SEL, 0x00},
- {0x08, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
- {0x09, AC_VERB_SET_CONNECT_SEL, 0x00},
- {0x09, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
-
- /*
- * Set up output mixers (0x0c - 0x0f)
- */
- /* set vol=0 to output mixers */
- {0x0c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
- {0x0d, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
- {0x0e, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
- {0x0f, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
- /* set up input amps for analog loopback */
- /* Amp Indices: DAC = 0, mixer = 1 */
- {0x0c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
- {0x0c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
- {0x0d, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
- {0x0d, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
- {0x0e, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
- {0x0e, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
- {0x0f, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
- {0x0f, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
- {0x26, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
- {0x26, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
-
- /* FIXME: use matrix-type input source selection */
- /* Mixer elements: 0x18, 19, 1a, 1b, 1c, 1d, 14, 15, 16, 17, 0b */
- /* Input mixer2 */
- {0x23, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
- /* Input mixer3 */
- {0x22, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
- { }
-};
-
-/* 2ch mode (Speaker:front, Subwoofer:CLFE, Line:input, Headphones:front) */
-static struct hda_verb alc889A_mb31_ch2_init[] = {
- {0x15, AC_VERB_SET_CONNECT_SEL, 0x00}, /* HP as front */
- {0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE}, /* Subwoofer on */
- {0x1a, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN}, /* Line as input */
- {0x1a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE}, /* Line off */
- { } /* end */
-};
-
-/* 4ch mode (Speaker:front, Subwoofer:CLFE, Line:CLFE, Headphones:front) */
-static struct hda_verb alc889A_mb31_ch4_init[] = {
- {0x15, AC_VERB_SET_CONNECT_SEL, 0x00}, /* HP as front */
- {0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE}, /* Subwoofer on */
- {0x1a, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT}, /* Line as output */
- {0x1a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE}, /* Line on */
- { } /* end */
-};
-
-/* 5ch mode (Speaker:front, Subwoofer:CLFE, Line:input, Headphones:rear) */
-static struct hda_verb alc889A_mb31_ch5_init[] = {
- {0x15, AC_VERB_SET_CONNECT_SEL, 0x01}, /* HP as rear */
- {0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE}, /* Subwoofer on */
- {0x1a, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN}, /* Line as input */
- {0x1a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE}, /* Line off */
- { } /* end */
-};
-
-/* 6ch mode (Speaker:front, Subwoofer:off, Line:CLFE, Headphones:Rear) */
-static struct hda_verb alc889A_mb31_ch6_init[] = {
- {0x15, AC_VERB_SET_CONNECT_SEL, 0x01}, /* HP as front */
- {0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE}, /* Subwoofer off */
- {0x1a, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT}, /* Line as output */
- {0x1a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE}, /* Line on */
- { } /* end */
-};
-
-static struct hda_channel_mode alc889A_mb31_6ch_modes[4] = {
- { 2, alc889A_mb31_ch2_init },
- { 4, alc889A_mb31_ch4_init },
- { 5, alc889A_mb31_ch5_init },
- { 6, alc889A_mb31_ch6_init },
-};
-
-static struct hda_verb alc883_medion_eapd_verbs[] = {
- /* eanable EAPD on medion laptop */
- {0x20, AC_VERB_SET_COEF_INDEX, 0x07},
- {0x20, AC_VERB_SET_PROC_COEF, 0x3070},
- { }
-};
-
-#define alc883_base_mixer alc882_base_mixer
-
-static struct snd_kcontrol_new alc883_mitac_mixer[] = {
- HDA_CODEC_VOLUME("Front Playback Volume", 0x0c, 0x0, HDA_OUTPUT),
- HDA_BIND_MUTE("Front Playback Switch", 0x0c, 2, HDA_INPUT),
- HDA_CODEC_VOLUME_MONO("Center Playback Volume", 0x0e, 1, 0x0, HDA_OUTPUT),
- HDA_CODEC_VOLUME_MONO("LFE Playback Volume", 0x0e, 2, 0x0, HDA_OUTPUT),
- HDA_BIND_MUTE_MONO("Center Playback Switch", 0x0e, 1, 2, HDA_INPUT),
- HDA_BIND_MUTE_MONO("LFE Playback Switch", 0x0e, 2, 2, HDA_INPUT),
- HDA_CODEC_MUTE("Headphone Playback Switch", 0x15, 0x0, HDA_OUTPUT),
- HDA_CODEC_VOLUME("Mic Playback Volume", 0x0b, 0x0, HDA_INPUT),
- HDA_CODEC_VOLUME("Mic Boost Volume", 0x18, 0, HDA_INPUT),
- HDA_CODEC_MUTE("Mic Playback Switch", 0x0b, 0x0, HDA_INPUT),
- HDA_CODEC_VOLUME("Front Mic Playback Volume", 0x0b, 0x1, HDA_INPUT),
- HDA_CODEC_VOLUME("Front Mic Boost Volume", 0x19, 0, HDA_INPUT),
- HDA_CODEC_MUTE("Front Mic Playback Switch", 0x0b, 0x1, HDA_INPUT),
- { } /* end */
-};
-
-static struct snd_kcontrol_new alc883_clevo_m720_mixer[] = {
- HDA_CODEC_VOLUME("Headphone Playback Volume", 0x0c, 0x0, HDA_OUTPUT),
- HDA_BIND_MUTE("Headphone Playback Switch", 0x0c, 2, HDA_INPUT),
- HDA_CODEC_VOLUME("Speaker Playback Volume", 0x0d, 0x0, HDA_OUTPUT),
- HDA_BIND_MUTE("Speaker Playback Switch", 0x0d, 2, HDA_INPUT),
- HDA_CODEC_VOLUME("Mic Playback Volume", 0x0b, 0x0, HDA_INPUT),
- HDA_CODEC_VOLUME("Mic Boost Volume", 0x18, 0, HDA_INPUT),
- HDA_CODEC_MUTE("Mic Playback Switch", 0x0b, 0x0, HDA_INPUT),
- HDA_CODEC_VOLUME("Internal Mic Playback Volume", 0x0b, 0x1, HDA_INPUT),
- HDA_CODEC_VOLUME("Internal Mic Boost Volume", 0x19, 0, HDA_INPUT),
- HDA_CODEC_MUTE("Internal Mic Playback Switch", 0x0b, 0x1, HDA_INPUT),
- { } /* end */
-};
-
-static struct snd_kcontrol_new alc883_2ch_fujitsu_pi2515_mixer[] = {
- HDA_CODEC_VOLUME("Headphone Playback Volume", 0x0c, 0x0, HDA_OUTPUT),
- HDA_BIND_MUTE("Headphone Playback Switch", 0x0c, 2, HDA_INPUT),
- HDA_CODEC_VOLUME("Speaker Playback Volume", 0x0d, 0x0, HDA_OUTPUT),
- HDA_BIND_MUTE("Speaker Playback Switch", 0x0d, 2, HDA_INPUT),
- HDA_CODEC_VOLUME("Mic Playback Volume", 0x0b, 0x0, HDA_INPUT),
- HDA_CODEC_VOLUME("Mic Boost Volume", 0x18, 0, HDA_INPUT),
- HDA_CODEC_MUTE("Mic Playback Switch", 0x0b, 0x0, HDA_INPUT),
- HDA_CODEC_VOLUME("Internal Mic Playback Volume", 0x0b, 0x1, HDA_INPUT),
- HDA_CODEC_VOLUME("Internal Mic Boost Volume", 0x19, 0, HDA_INPUT),
- HDA_CODEC_MUTE("Internal Mic Playback Switch", 0x0b, 0x1, HDA_INPUT),
- { } /* end */
-};
-
-static struct snd_kcontrol_new alc883_3ST_2ch_mixer[] = {
- HDA_CODEC_VOLUME("Front Playback Volume", 0x0c, 0x0, HDA_OUTPUT),
- HDA_BIND_MUTE("Front Playback Switch", 0x0c, 2, HDA_INPUT),
- HDA_CODEC_MUTE("Headphone Playback Switch", 0x1b, 0x0, HDA_OUTPUT),
- HDA_CODEC_VOLUME("CD Playback Volume", 0x0b, 0x04, HDA_INPUT),
- HDA_CODEC_MUTE("CD Playback Switch", 0x0b, 0x04, HDA_INPUT),
- HDA_CODEC_VOLUME("Line Playback Volume", 0x0b, 0x02, HDA_INPUT),
- HDA_CODEC_MUTE("Line Playback Switch", 0x0b, 0x02, HDA_INPUT),
- HDA_CODEC_VOLUME("Mic Playback Volume", 0x0b, 0x0, HDA_INPUT),
- HDA_CODEC_VOLUME("Mic Boost Volume", 0x18, 0, HDA_INPUT),
- HDA_CODEC_MUTE("Mic Playback Switch", 0x0b, 0x0, HDA_INPUT),
- HDA_CODEC_VOLUME("Front Mic Playback Volume", 0x0b, 0x1, HDA_INPUT),
- HDA_CODEC_VOLUME("Front Mic Boost Volume", 0x19, 0, HDA_INPUT),
- HDA_CODEC_MUTE("Front Mic Playback Switch", 0x0b, 0x1, HDA_INPUT),
- { } /* end */
-};
-
-static struct snd_kcontrol_new alc883_3ST_6ch_mixer[] = {
- HDA_CODEC_VOLUME("Front Playback Volume", 0x0c, 0x0, HDA_OUTPUT),
- HDA_BIND_MUTE("Front Playback Switch", 0x0c, 2, HDA_INPUT),
- HDA_CODEC_VOLUME("Surround Playback Volume", 0x0d, 0x0, HDA_OUTPUT),
- HDA_BIND_MUTE("Surround Playback Switch", 0x0d, 2, HDA_INPUT),
- HDA_CODEC_VOLUME_MONO("Center Playback Volume", 0x0e, 1, 0x0, HDA_OUTPUT),
- HDA_CODEC_VOLUME_MONO("LFE Playback Volume", 0x0e, 2, 0x0, HDA_OUTPUT),
- HDA_BIND_MUTE_MONO("Center Playback Switch", 0x0e, 1, 2, HDA_INPUT),
- HDA_BIND_MUTE_MONO("LFE Playback Switch", 0x0e, 2, 2, HDA_INPUT),
- HDA_CODEC_MUTE("Headphone Playback Switch", 0x1b, 0x0, HDA_OUTPUT),
- HDA_CODEC_VOLUME("CD Playback Volume", 0x0b, 0x04, HDA_INPUT),
- HDA_CODEC_MUTE("CD Playback Switch", 0x0b, 0x04, HDA_INPUT),
- HDA_CODEC_VOLUME("Line Playback Volume", 0x0b, 0x02, HDA_INPUT),
- HDA_CODEC_MUTE("Line Playback Switch", 0x0b, 0x02, HDA_INPUT),
- HDA_CODEC_VOLUME("Mic Playback Volume", 0x0b, 0x0, HDA_INPUT),
- HDA_CODEC_VOLUME("Mic Boost Volume", 0x18, 0, HDA_INPUT),
- HDA_CODEC_MUTE("Mic Playback Switch", 0x0b, 0x0, HDA_INPUT),
- HDA_CODEC_VOLUME("Front Mic Playback Volume", 0x0b, 0x1, HDA_INPUT),
- HDA_CODEC_VOLUME("Front Mic Boost Volume", 0x19, 0, HDA_INPUT),
- HDA_CODEC_MUTE("Front Mic Playback Switch", 0x0b, 0x1, HDA_INPUT),
- { } /* end */
-};
-
-static struct snd_kcontrol_new alc883_3ST_6ch_intel_mixer[] = {
- HDA_CODEC_VOLUME("Front Playback Volume", 0x0c, 0x0, HDA_OUTPUT),
- HDA_BIND_MUTE("Front Playback Switch", 0x0c, 2, HDA_INPUT),
- HDA_CODEC_VOLUME("Surround Playback Volume", 0x0d, 0x0, HDA_OUTPUT),
- HDA_BIND_MUTE("Surround Playback Switch", 0x0d, 2, HDA_INPUT),
- HDA_CODEC_VOLUME_MONO("Center Playback Volume", 0x0e, 1, 0x0,
- HDA_OUTPUT),
- HDA_CODEC_VOLUME_MONO("LFE Playback Volume", 0x0e, 2, 0x0, HDA_OUTPUT),
- HDA_BIND_MUTE_MONO("Center Playback Switch", 0x0e, 1, 2, HDA_INPUT),
- HDA_BIND_MUTE_MONO("LFE Playback Switch", 0x0e, 2, 2, HDA_INPUT),
- HDA_CODEC_MUTE("Headphone Playback Switch", 0x15, 0x0, HDA_OUTPUT),
- HDA_CODEC_VOLUME("CD Playback Volume", 0x0b, 0x04, HDA_INPUT),
- HDA_CODEC_MUTE("CD Playback Switch", 0x0b, 0x04, HDA_INPUT),
- HDA_CODEC_VOLUME("Line Playback Volume", 0x0b, 0x02, HDA_INPUT),
- HDA_CODEC_MUTE("Line Playback Switch", 0x0b, 0x02, HDA_INPUT),
- HDA_CODEC_VOLUME("Mic Playback Volume", 0x0b, 0x1, HDA_INPUT),
- HDA_CODEC_VOLUME("Mic Boost Volume", 0x19, 0, HDA_INPUT),
- HDA_CODEC_MUTE("Mic Playback Switch", 0x0b, 0x1, HDA_INPUT),
- HDA_CODEC_VOLUME("Front Mic Playback Volume", 0x0b, 0x0, HDA_INPUT),
- HDA_CODEC_VOLUME("Front Mic Boost Volume", 0x18, 0, HDA_INPUT),
- HDA_CODEC_MUTE("Front Mic Playback Switch", 0x0b, 0x0, HDA_INPUT),
- { } /* end */
-};
-
-static struct snd_kcontrol_new alc885_8ch_intel_mixer[] = {
- HDA_CODEC_VOLUME("Front Playback Volume", 0x0c, 0x0, HDA_OUTPUT),
- HDA_BIND_MUTE("Front Playback Switch", 0x0c, 2, HDA_INPUT),
- HDA_CODEC_VOLUME("Surround Playback Volume", 0x0d, 0x0, HDA_OUTPUT),
- HDA_BIND_MUTE("Surround Playback Switch", 0x0d, 2, HDA_INPUT),
- HDA_CODEC_VOLUME_MONO("Center Playback Volume", 0x0e, 1, 0x0,
- HDA_OUTPUT),
- HDA_CODEC_VOLUME_MONO("LFE Playback Volume", 0x0e, 2, 0x0, HDA_OUTPUT),
- HDA_BIND_MUTE_MONO("Center Playback Switch", 0x0e, 1, 2, HDA_INPUT),
- HDA_BIND_MUTE_MONO("LFE Playback Switch", 0x0e, 2, 2, HDA_INPUT),
- HDA_CODEC_VOLUME("Speaker Playback Volume", 0x0f, 0x0, HDA_OUTPUT),
- HDA_BIND_MUTE("Speaker Playback Switch", 0x0f, 2, HDA_INPUT),
- HDA_CODEC_MUTE("Headphone Playback Switch", 0x15, 0x0, HDA_OUTPUT),
- HDA_CODEC_VOLUME("Line Playback Volume", 0x0b, 0x02, HDA_INPUT),
- HDA_CODEC_MUTE("Line Playback Switch", 0x0b, 0x02, HDA_INPUT),
- HDA_CODEC_VOLUME("Mic Playback Volume", 0x0b, 0x3, HDA_INPUT),
- HDA_CODEC_VOLUME("Mic Boost Volume", 0x1b, 0, HDA_INPUT),
- HDA_CODEC_MUTE("Mic Playback Switch", 0x0b, 0x3, HDA_INPUT),
- HDA_CODEC_VOLUME("Front Mic Playback Volume", 0x0b, 0x0, HDA_INPUT),
- HDA_CODEC_VOLUME("Front Mic Boost Volume", 0x18, 0, HDA_INPUT),
- HDA_CODEC_MUTE("Front Mic Playback Switch", 0x0b, 0x0, HDA_INPUT),
- { } /* end */
-};
-
-static struct snd_kcontrol_new alc883_fivestack_mixer[] = {
- HDA_CODEC_VOLUME("Front Playback Volume", 0x0c, 0x0, HDA_OUTPUT),
- HDA_BIND_MUTE("Front Playback Switch", 0x0c, 2, HDA_INPUT),
- HDA_CODEC_VOLUME("Surround Playback Volume", 0x0d, 0x0, HDA_OUTPUT),
- HDA_BIND_MUTE("Surround Playback Switch", 0x0d, 2, HDA_INPUT),
- HDA_CODEC_VOLUME_MONO("Center Playback Volume", 0x0e, 1, 0x0, HDA_OUTPUT),
- HDA_CODEC_VOLUME_MONO("LFE Playback Volume", 0x0e, 2, 0x0, HDA_OUTPUT),
- HDA_BIND_MUTE_MONO("Center Playback Switch", 0x0e, 1, 2, HDA_INPUT),
- HDA_BIND_MUTE_MONO("LFE Playback Switch", 0x0e, 2, 2, HDA_INPUT),
- HDA_CODEC_MUTE("Headphone Playback Switch", 0x1b, 0x0, HDA_OUTPUT),
- HDA_CODEC_VOLUME("CD Playback Volume", 0x0b, 0x04, HDA_INPUT),
- HDA_CODEC_MUTE("CD Playback Switch", 0x0b, 0x04, HDA_INPUT),
- HDA_CODEC_VOLUME("Line Playback Volume", 0x0b, 0x02, HDA_INPUT),
- HDA_CODEC_MUTE("Line Playback Switch", 0x0b, 0x02, HDA_INPUT),
- HDA_CODEC_VOLUME("Mic Playback Volume", 0x0b, 0x0, HDA_INPUT),
- HDA_CODEC_VOLUME("Mic Boost Volume", 0x18, 0, HDA_INPUT),
- HDA_CODEC_MUTE("Mic Playback Switch", 0x0b, 0x0, HDA_INPUT),
- HDA_CODEC_VOLUME("Front Mic Playback Volume", 0x0b, 0x1, HDA_INPUT),
- HDA_CODEC_VOLUME("Front Mic Boost Volume", 0x19, 0, HDA_INPUT),
- HDA_CODEC_MUTE("Front Mic Playback Switch", 0x0b, 0x1, HDA_INPUT),
- { } /* end */
-};
-
-static struct snd_kcontrol_new alc883_targa_mixer[] = {
- HDA_CODEC_VOLUME("Front Playback Volume", 0x0c, 0x0, HDA_OUTPUT),
- HDA_BIND_MUTE("Front Playback Switch", 0x0c, 2, HDA_INPUT),
- HDA_CODEC_MUTE("Headphone Playback Switch", 0x14, 0x0, HDA_OUTPUT),
- HDA_CODEC_MUTE("Speaker Playback Switch", 0x1b, 0x0, HDA_OUTPUT),
- HDA_CODEC_VOLUME("Surround Playback Volume", 0x0d, 0x0, HDA_OUTPUT),
- HDA_BIND_MUTE("Surround Playback Switch", 0x0d, 2, HDA_INPUT),
- HDA_CODEC_VOLUME_MONO("Center Playback Volume", 0x0e, 1, 0x0, HDA_OUTPUT),
- HDA_CODEC_VOLUME_MONO("LFE Playback Volume", 0x0e, 2, 0x0, HDA_OUTPUT),
- HDA_BIND_MUTE_MONO("Center Playback Switch", 0x0e, 1, 2, HDA_INPUT),
- HDA_BIND_MUTE_MONO("LFE Playback Switch", 0x0e, 2, 2, HDA_INPUT),
- HDA_CODEC_VOLUME("CD Playback Volume", 0x0b, 0x04, HDA_INPUT),
- HDA_CODEC_MUTE("CD Playback Switch", 0x0b, 0x04, HDA_INPUT),
- HDA_CODEC_VOLUME("Line Playback Volume", 0x0b, 0x02, HDA_INPUT),
- HDA_CODEC_MUTE("Line Playback Switch", 0x0b, 0x02, HDA_INPUT),
- HDA_CODEC_VOLUME("Mic Playback Volume", 0x0b, 0x0, HDA_INPUT),
- HDA_CODEC_VOLUME("Mic Boost Volume", 0x18, 0, HDA_INPUT),
- HDA_CODEC_MUTE("Mic Playback Switch", 0x0b, 0x0, HDA_INPUT),
- { } /* end */
-};
-
-static struct snd_kcontrol_new alc883_targa_2ch_mixer[] = {
- HDA_CODEC_VOLUME("Front Playback Volume", 0x0c, 0x0, HDA_OUTPUT),
- HDA_BIND_MUTE("Front Playback Switch", 0x0c, 2, HDA_INPUT),
- HDA_CODEC_MUTE("Headphone Playback Switch", 0x14, 0x0, HDA_OUTPUT),
- HDA_CODEC_MUTE("Speaker Playback Switch", 0x1b, 0x0, HDA_OUTPUT),
- HDA_CODEC_VOLUME("CD Playback Volume", 0x0b, 0x04, HDA_INPUT),
- HDA_CODEC_MUTE("CD Playback Switch", 0x0b, 0x04, HDA_INPUT),
- HDA_CODEC_VOLUME("Mic Playback Volume", 0x0b, 0x0, HDA_INPUT),
- HDA_CODEC_VOLUME("Mic Boost Volume", 0x18, 0, HDA_INPUT),
- HDA_CODEC_MUTE("Mic Playback Switch", 0x0b, 0x0, HDA_INPUT),
- HDA_CODEC_VOLUME("Internal Mic Playback Volume", 0x0b, 0x1, HDA_INPUT),
- HDA_CODEC_VOLUME("Internal Mic Boost Volume", 0x19, 0, HDA_INPUT),
- HDA_CODEC_MUTE("Internal Mic Playback Switch", 0x0b, 0x1, HDA_INPUT),
- { } /* end */
-};
-
-static struct snd_kcontrol_new alc883_targa_8ch_mixer[] = {
- HDA_CODEC_VOLUME("Side Playback Volume", 0x0f, 0x0, HDA_OUTPUT),
- HDA_BIND_MUTE("Side Playback Switch", 0x0f, 2, HDA_INPUT),
- HDA_CODEC_VOLUME("Internal Mic Playback Volume", 0x0b, 0x1, HDA_INPUT),
- HDA_CODEC_VOLUME("Internal Mic Boost Volume", 0x19, 0, HDA_INPUT),
- HDA_CODEC_MUTE("Internal Mic Playback Switch", 0x0b, 0x1, HDA_INPUT),
- { } /* end */
-};
-
-static struct snd_kcontrol_new alc883_lenovo_101e_2ch_mixer[] = {
- HDA_CODEC_VOLUME("Front Playback Volume", 0x0c, 0x0, HDA_OUTPUT),
- HDA_BIND_MUTE("Front Playback Switch", 0x0c, 2, HDA_INPUT),
- HDA_CODEC_VOLUME("Speaker Playback Volume", 0x0d, 0x0, HDA_OUTPUT),
- HDA_BIND_MUTE("Speaker Playback Switch", 0x0d, 2, HDA_INPUT),
- HDA_CODEC_MUTE("Headphone Playback Switch", 0x1b, 0x0, HDA_OUTPUT),
- HDA_CODEC_VOLUME("Mic Playback Volume", 0x0b, 0x1, HDA_INPUT),
- HDA_CODEC_VOLUME("Mic Boost Volume", 0x18, 0, HDA_INPUT),
- HDA_CODEC_MUTE("Mic Playback Switch", 0x0b, 0x1, HDA_INPUT),
- { } /* end */
-};
-
-static struct snd_kcontrol_new alc883_lenovo_nb0763_mixer[] = {
- HDA_CODEC_VOLUME("Speaker Playback Volume", 0x0c, 0x0, HDA_OUTPUT),
- HDA_BIND_MUTE("Speaker Playback Switch", 0x0c, 2, HDA_INPUT),
- HDA_CODEC_MUTE("Headphone Playback Switch", 0x14, 0x0, HDA_OUTPUT),
- HDA_CODEC_VOLUME("CD Playback Volume", 0x0b, 0x04, HDA_INPUT),
- HDA_CODEC_MUTE("CD Playback Switch", 0x0b, 0x04, HDA_INPUT),
- HDA_CODEC_VOLUME("Mic Playback Volume", 0x0b, 0x0, HDA_INPUT),
- HDA_CODEC_MUTE("Mic Playback Switch", 0x0b, 0x0, HDA_INPUT),
- HDA_CODEC_VOLUME("Internal Mic Playback Volume", 0x0b, 0x1, HDA_INPUT),
- HDA_CODEC_MUTE("Internal Mic Playback Switch", 0x0b, 0x1, HDA_INPUT),
- { } /* end */
-};
-
-static struct snd_kcontrol_new alc883_medion_wim2160_mixer[] = {
- HDA_CODEC_VOLUME("Front Playback Volume", 0x0c, 0x0, HDA_OUTPUT),
- HDA_BIND_MUTE("Front Playback Switch", 0x0c, 2, HDA_INPUT),
- HDA_CODEC_MUTE("Speaker Playback Switch", 0x15, 0x0, HDA_OUTPUT),
- HDA_CODEC_MUTE("Headphone Playback Switch", 0x1a, 0x0, HDA_OUTPUT),
- HDA_CODEC_VOLUME("Line Playback Volume", 0x08, 0x0, HDA_INPUT),
- HDA_CODEC_MUTE("Line Playback Switch", 0x08, 0x0, HDA_INPUT),
- { } /* end */
-};
-
-static struct hda_verb alc883_medion_wim2160_verbs[] = {
- /* Unmute front mixer */
- {0x0c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
- {0x0c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
-
- /* Set speaker pin to front mixer */
- {0x15, AC_VERB_SET_CONNECT_SEL, 0x00},
-
- /* Init headphone pin */
- {0x1a, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP},
- {0x1a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
- {0x1a, AC_VERB_SET_CONNECT_SEL, 0x00},
- {0x1a, AC_VERB_SET_UNSOLICITED_ENABLE, ALC880_HP_EVENT | AC_USRSP_EN},
-
- { } /* end */
-};
-
-/* toggle speaker-output according to the hp-jack state */
-static void alc883_medion_wim2160_setup(struct hda_codec *codec)
-{
- struct alc_spec *spec = codec->spec;
-
- spec->autocfg.hp_pins[0] = 0x1a;
- spec->autocfg.speaker_pins[0] = 0x15;
-}
-
-static struct snd_kcontrol_new alc883_acer_aspire_mixer[] = {
- HDA_CODEC_VOLUME("Front Playback Volume", 0x0c, 0x0, HDA_OUTPUT),
- HDA_BIND_MUTE("Front Playback Switch", 0x0c, 2, HDA_INPUT),
- HDA_CODEC_MUTE("Headphone Playback Switch", 0x14, 0x0, HDA_OUTPUT),
- HDA_CODEC_VOLUME("Line Playback Volume", 0x0b, 0x02, HDA_INPUT),
- HDA_CODEC_MUTE("Line Playback Switch", 0x0b, 0x02, HDA_INPUT),
- HDA_CODEC_VOLUME("Mic Playback Volume", 0x0b, 0x0, HDA_INPUT),
- HDA_CODEC_VOLUME("Mic Boost Volume", 0x18, 0, HDA_INPUT),
- HDA_CODEC_MUTE("Mic Playback Switch", 0x0b, 0x0, HDA_INPUT),
- { } /* end */
-};
-
-static struct snd_kcontrol_new alc888_acer_aspire_6530_mixer[] = {
- HDA_CODEC_VOLUME("Front Playback Volume", 0x0c, 0x0, HDA_OUTPUT),
- HDA_CODEC_VOLUME("LFE Playback Volume", 0x0f, 0x0, HDA_OUTPUT),
- HDA_CODEC_VOLUME("Line Playback Volume", 0x0b, 0x02, HDA_INPUT),
- HDA_CODEC_MUTE("Line Playback Switch", 0x0b, 0x02, HDA_INPUT),
- HDA_CODEC_VOLUME("CD Playback Volume", 0x0b, 0x04, HDA_INPUT),
- HDA_CODEC_MUTE("CD Playback Switch", 0x0b, 0x04, HDA_INPUT),
- HDA_CODEC_VOLUME("Mic Playback Volume", 0x0b, 0x0, HDA_INPUT),
- HDA_CODEC_VOLUME("Mic Boost Volume", 0x18, 0, HDA_INPUT),
- HDA_CODEC_MUTE("Mic Playback Switch", 0x0b, 0x0, HDA_INPUT),
- { } /* end */
-};
-
-static struct snd_kcontrol_new alc888_lenovo_sky_mixer[] = {
- HDA_CODEC_VOLUME("Front Playback Volume", 0x0c, 0x0, HDA_OUTPUT),
- HDA_BIND_MUTE("Front Playback Switch", 0x0c, 2, HDA_INPUT),
- HDA_CODEC_VOLUME("Surround Playback Volume", 0x0e, 0x0, HDA_OUTPUT),
- HDA_BIND_MUTE("Surround Playback Switch", 0x0e, 2, HDA_INPUT),
- HDA_CODEC_VOLUME_MONO("Center Playback Volume",
- 0x0d, 1, 0x0, HDA_OUTPUT),
- HDA_CODEC_VOLUME_MONO("LFE Playback Volume", 0x0d, 2, 0x0, HDA_OUTPUT),
- HDA_BIND_MUTE_MONO("Center Playback Switch", 0x0d, 1, 2, HDA_INPUT),
- HDA_BIND_MUTE_MONO("LFE Playback Switch", 0x0d, 2, 2, HDA_INPUT),
- HDA_CODEC_VOLUME("Side Playback Volume", 0x0f, 0x0, HDA_OUTPUT),
- HDA_BIND_MUTE("Side Playback Switch", 0x0f, 2, HDA_INPUT),
- HDA_CODEC_VOLUME("CD Playback Volume", 0x0b, 0x04, HDA_INPUT),
- HDA_CODEC_MUTE("CD Playback Switch", 0x0b, 0x04, HDA_INPUT),
- HDA_CODEC_VOLUME("Line Playback Volume", 0x0b, 0x02, HDA_INPUT),
- HDA_CODEC_MUTE("Line Playback Switch", 0x0b, 0x02, HDA_INPUT),
- HDA_CODEC_VOLUME("Mic Playback Volume", 0x0b, 0x0, HDA_INPUT),
- HDA_CODEC_VOLUME("Mic Boost Volume", 0x18, 0, HDA_INPUT),
- HDA_CODEC_MUTE("Mic Playback Switch", 0x0b, 0x0, HDA_INPUT),
- HDA_CODEC_VOLUME("Front Mic Playback Volume", 0x0b, 0x1, HDA_INPUT),
- HDA_CODEC_VOLUME("Front Mic Boost Volume", 0x19, 0, HDA_INPUT),
- HDA_CODEC_MUTE("Front Mic Playback Switch", 0x0b, 0x1, HDA_INPUT),
- { } /* end */
-};
-
-static struct snd_kcontrol_new alc889A_mb31_mixer[] = {
- /* Output mixers */
- HDA_CODEC_VOLUME("Front Playback Volume", 0x0c, 0x00, HDA_OUTPUT),
- HDA_BIND_MUTE("Front Playback Switch", 0x0c, 0x02, HDA_INPUT),
- HDA_CODEC_VOLUME("Surround Playback Volume", 0x0d, 0x00, HDA_OUTPUT),
- HDA_BIND_MUTE("Surround Playback Switch", 0x0d, 0x02, HDA_INPUT),
- HDA_CODEC_VOLUME_MONO("Center Playback Volume", 0x0e, 1, 0x00,
- HDA_OUTPUT),
- HDA_BIND_MUTE_MONO("Center Playback Switch", 0x0e, 1, 0x02, HDA_INPUT),
- HDA_CODEC_VOLUME_MONO("LFE Playback Volume", 0x0e, 2, 0x00, HDA_OUTPUT),
- HDA_BIND_MUTE_MONO("LFE Playback Switch", 0x0e, 2, 0x02, HDA_INPUT),
- /* Output switches */
- HDA_CODEC_MUTE("Enable Speaker", 0x14, 0x00, HDA_OUTPUT),
- HDA_CODEC_MUTE("Enable Headphones", 0x15, 0x00, HDA_OUTPUT),
- HDA_CODEC_MUTE_MONO("Enable LFE", 0x16, 2, 0x00, HDA_OUTPUT),
- /* Boost mixers */
- HDA_CODEC_VOLUME("Mic Boost Volume", 0x18, 0x00, HDA_INPUT),
- HDA_CODEC_VOLUME("Line Boost Volume", 0x1a, 0x00, HDA_INPUT),
- /* Input mixers */
- HDA_CODEC_VOLUME("Mic Playback Volume", 0x0b, 0x00, HDA_INPUT),
- HDA_CODEC_MUTE("Mic Playback Switch", 0x0b, 0x00, HDA_INPUT),
- HDA_CODEC_VOLUME("Line Playback Volume", 0x0b, 0x02, HDA_INPUT),
- HDA_CODEC_MUTE("Line Playback Switch", 0x0b, 0x02, HDA_INPUT),
- { } /* end */
-};
-
-static struct snd_kcontrol_new alc883_vaiott_mixer[] = {
- HDA_CODEC_VOLUME("Front Playback Volume", 0x0c, 0x0, HDA_OUTPUT),
- HDA_BIND_MUTE("Front Playback Switch", 0x0c, 2, HDA_INPUT),
- HDA_CODEC_MUTE("Headphone Playback Switch", 0x15, 0x0, HDA_OUTPUT),
- HDA_CODEC_VOLUME("Mic Playback Volume", 0x0b, 0x1, HDA_INPUT),
- HDA_CODEC_VOLUME("Mic Boost Volume", 0x19, 0, HDA_INPUT),
- HDA_CODEC_MUTE("Mic Playback Switch", 0x0b, 0x1, HDA_INPUT),
- { } /* end */
-};
-
-static struct hda_bind_ctls alc883_bind_cap_vol = {
- .ops = &snd_hda_bind_vol,
- .values = {
- HDA_COMPOSE_AMP_VAL(0x08, 3, 0, HDA_INPUT),
- HDA_COMPOSE_AMP_VAL(0x09, 3, 0, HDA_INPUT),
- 0
- },
-};
-
-static struct hda_bind_ctls alc883_bind_cap_switch = {
- .ops = &snd_hda_bind_sw,
- .values = {
- HDA_COMPOSE_AMP_VAL(0x08, 3, 0, HDA_INPUT),
- HDA_COMPOSE_AMP_VAL(0x09, 3, 0, HDA_INPUT),
- 0
- },
-};
-
-static struct snd_kcontrol_new alc883_asus_eee1601_mixer[] = {
- HDA_CODEC_VOLUME("Front Playback Volume", 0x0c, 0x0, HDA_OUTPUT),
- HDA_BIND_MUTE("Front Playback Switch", 0x0c, 2, HDA_INPUT),
- HDA_CODEC_MUTE("Headphone Playback Switch", 0x14, 0x0, HDA_OUTPUT),
- HDA_CODEC_VOLUME("Line Playback Volume", 0x0b, 0x02, HDA_INPUT),
- HDA_CODEC_MUTE("Line Playback Switch", 0x0b, 0x02, HDA_INPUT),
- HDA_CODEC_VOLUME("Mic Playback Volume", 0x0b, 0x0, HDA_INPUT),
- HDA_CODEC_VOLUME("Mic Boost Volume", 0x18, 0, HDA_INPUT),
- HDA_CODEC_MUTE("Mic Playback Switch", 0x0b, 0x0, HDA_INPUT),
- { } /* end */
-};
-
-static struct snd_kcontrol_new alc883_asus_eee1601_cap_mixer[] = {
- HDA_BIND_VOL("Capture Volume", &alc883_bind_cap_vol),
- HDA_BIND_SW("Capture Switch", &alc883_bind_cap_switch),
- {
- .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
- /* .name = "Capture Source", */
- .name = "Input Source",
- .count = 1,
- .info = alc_mux_enum_info,
- .get = alc_mux_enum_get,
- .put = alc_mux_enum_put,
- },
- { } /* end */
-};
-
-static struct snd_kcontrol_new alc883_chmode_mixer[] = {
- {
- .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
- .name = "Channel Mode",
- .info = alc_ch_mode_info,
- .get = alc_ch_mode_get,
- .put = alc_ch_mode_put,
- },
- { } /* end */
-};
-
-/* toggle speaker-output according to the hp-jack state */
-static void alc883_mitac_setup(struct hda_codec *codec)
-{
- struct alc_spec *spec = codec->spec;
-
- spec->autocfg.hp_pins[0] = 0x15;
- spec->autocfg.speaker_pins[0] = 0x14;
- spec->autocfg.speaker_pins[1] = 0x17;
-}
-
-static struct hda_verb alc883_mitac_verbs[] = {
- /* HP */
- {0x15, AC_VERB_SET_CONNECT_SEL, 0x00},
- {0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP},
- /* Subwoofer */
- {0x17, AC_VERB_SET_CONNECT_SEL, 0x02},
- {0x17, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
-
- /* enable unsolicited event */
- {0x15, AC_VERB_SET_UNSOLICITED_ENABLE, ALC880_HP_EVENT | AC_USRSP_EN},
- /* {0x18, AC_VERB_SET_UNSOLICITED_ENABLE, ALC880_MIC_EVENT | AC_USRSP_EN}, */
-
- { } /* end */
-};
-
-static struct hda_verb alc883_clevo_m540r_verbs[] = {
- /* HP */
- {0x15, AC_VERB_SET_CONNECT_SEL, 0x00},
- {0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP},
- /* Int speaker */
- /*{0x14, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP},*/
-
- /* enable unsolicited event */
- /*
- {0x15, AC_VERB_SET_UNSOLICITED_ENABLE, ALC880_HP_EVENT | AC_USRSP_EN},
- {0x18, AC_VERB_SET_UNSOLICITED_ENABLE, ALC880_MIC_EVENT | AC_USRSP_EN},
- */
-
- { } /* end */
-};
-
-static struct hda_verb alc883_clevo_m720_verbs[] = {
- /* HP */
- {0x15, AC_VERB_SET_CONNECT_SEL, 0x00},
- {0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP},
- /* Int speaker */
- {0x14, AC_VERB_SET_CONNECT_SEL, 0x01},
- {0x14, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
-
- /* enable unsolicited event */
- {0x15, AC_VERB_SET_UNSOLICITED_ENABLE, ALC880_HP_EVENT | AC_USRSP_EN},
- {0x18, AC_VERB_SET_UNSOLICITED_ENABLE, ALC880_MIC_EVENT | AC_USRSP_EN},
-
- { } /* end */
-};
-
-static struct hda_verb alc883_2ch_fujitsu_pi2515_verbs[] = {
- /* HP */
- {0x14, AC_VERB_SET_CONNECT_SEL, 0x00},
- {0x14, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP},
- /* Subwoofer */
- {0x15, AC_VERB_SET_CONNECT_SEL, 0x01},
- {0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
-
- /* enable unsolicited event */
- {0x14, AC_VERB_SET_UNSOLICITED_ENABLE, ALC880_HP_EVENT | AC_USRSP_EN},
-
- { } /* end */
-};
-
-static struct hda_verb alc883_targa_verbs[] = {
- {0x0c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
- {0x0c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
-
- {0x14, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP},
- {0x1b, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
-
-/* Connect Line-Out side jack (SPDIF) to Side */
- {0x17, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
- {0x17, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
- {0x17, AC_VERB_SET_CONNECT_SEL, 0x03},
-/* Connect Mic jack to CLFE */
- {0x18, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
- {0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
- {0x18, AC_VERB_SET_CONNECT_SEL, 0x02},
-/* Connect Line-in jack to Surround */
- {0x1a, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
- {0x1a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
- {0x1a, AC_VERB_SET_CONNECT_SEL, 0x01},
-/* Connect HP out jack to Front */
- {0x1b, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
- {0x1b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
- {0x1b, AC_VERB_SET_CONNECT_SEL, 0x00},
-
- {0x14, AC_VERB_SET_UNSOLICITED_ENABLE, ALC880_HP_EVENT | AC_USRSP_EN},
-
- { } /* end */
-};
-
-static struct hda_verb alc883_lenovo_101e_verbs[] = {
- {0x15, AC_VERB_SET_CONNECT_SEL, 0x00},
- {0x14, AC_VERB_SET_UNSOLICITED_ENABLE, ALC880_FRONT_EVENT|AC_USRSP_EN},
- {0x1b, AC_VERB_SET_UNSOLICITED_ENABLE, ALC880_HP_EVENT|AC_USRSP_EN},
- { } /* end */
-};
-
-static struct hda_verb alc883_lenovo_nb0763_verbs[] = {
- {0x15, AC_VERB_SET_CONNECT_SEL, 0x00},
- {0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP},
- {0x14, AC_VERB_SET_UNSOLICITED_ENABLE, ALC880_HP_EVENT | AC_USRSP_EN},
- {0x14, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP},
- { } /* end */
-};
-
-static struct hda_verb alc888_lenovo_ms7195_verbs[] = {
- {0x0c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
- {0x0c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
- {0x15, AC_VERB_SET_CONNECT_SEL, 0x00},
- {0x14, AC_VERB_SET_UNSOLICITED_ENABLE, ALC880_FRONT_EVENT | AC_USRSP_EN},
- {0x1b, AC_VERB_SET_UNSOLICITED_ENABLE, ALC880_HP_EVENT | AC_USRSP_EN},
- { } /* end */
-};
-
-static struct hda_verb alc883_haier_w66_verbs[] = {
- {0x0c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
- {0x0c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
-
- {0x14, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
-
- {0x1b, AC_VERB_SET_CONNECT_SEL, 0x00},
- {0x1b, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP},
- {0x1b, AC_VERB_SET_UNSOLICITED_ENABLE, ALC880_HP_EVENT | AC_USRSP_EN},
- {0x1b, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP},
- { } /* end */
-};
-
-static struct hda_verb alc888_lenovo_sky_verbs[] = {
- {0x0c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
- {0x0c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
- {0x0d, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
- {0x0e, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
- {0x0f, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
- {0x1a, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
- {0x1a, AC_VERB_SET_CONNECT_SEL, 0x00},
- {0x1b, AC_VERB_SET_UNSOLICITED_ENABLE, ALC880_HP_EVENT | AC_USRSP_EN},
- { } /* end */
-};
-
-static struct hda_verb alc888_6st_dell_verbs[] = {
- {0x1b, AC_VERB_SET_UNSOLICITED_ENABLE, ALC880_HP_EVENT | AC_USRSP_EN},
- { }
-};
-
-static struct hda_verb alc883_vaiott_verbs[] = {
- /* HP */
- {0x15, AC_VERB_SET_CONNECT_SEL, 0x00},
- {0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP},
-
- /* enable unsolicited event */
- {0x15, AC_VERB_SET_UNSOLICITED_ENABLE, ALC880_HP_EVENT | AC_USRSP_EN},
-
- { } /* end */
-};
-
-static void alc888_3st_hp_setup(struct hda_codec *codec)
-{
- struct alc_spec *spec = codec->spec;
-
- spec->autocfg.hp_pins[0] = 0x1b;
- spec->autocfg.speaker_pins[0] = 0x14;
- spec->autocfg.speaker_pins[1] = 0x16;
- spec->autocfg.speaker_pins[2] = 0x18;
-}
-
-static struct hda_verb alc888_3st_hp_verbs[] = {
- {0x14, AC_VERB_SET_CONNECT_SEL, 0x00}, /* Front: output 0 (0x0c) */
- {0x16, AC_VERB_SET_CONNECT_SEL, 0x01}, /* Rear : output 1 (0x0d) */
- {0x18, AC_VERB_SET_CONNECT_SEL, 0x02}, /* CLFE : output 2 (0x0e) */
- {0x1b, AC_VERB_SET_UNSOLICITED_ENABLE, ALC880_HP_EVENT | AC_USRSP_EN},
- { } /* end */
-};
-
-/*
- * 2ch mode
- */
-static struct hda_verb alc888_3st_hp_2ch_init[] = {
- { 0x18, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80 },
- { 0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE },
- { 0x16, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN },
- { 0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE },
- { } /* end */
-};
-
-/*
- * 4ch mode
+/* Fix the connection of some pins for ALC889:
+ * At least, Acer Aspire 5935 shows the connections to DAC3/4 don't
+ * work correctly (bko#42740)
*/
-static struct hda_verb alc888_3st_hp_4ch_init[] = {
- { 0x18, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80 },
- { 0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE },
- { 0x16, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT },
- { 0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE },
- { 0x16, AC_VERB_SET_CONNECT_SEL, 0x01 },
- { } /* end */
-};
-
-/*
- * 6ch mode
- */
-static struct hda_verb alc888_3st_hp_6ch_init[] = {
- { 0x18, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT },
- { 0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE },
- { 0x18, AC_VERB_SET_CONNECT_SEL, 0x02 },
- { 0x16, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT },
- { 0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE },
- { 0x16, AC_VERB_SET_CONNECT_SEL, 0x01 },
- { } /* end */
-};
-
-static struct hda_channel_mode alc888_3st_hp_modes[3] = {
- { 2, alc888_3st_hp_2ch_init },
- { 4, alc888_3st_hp_4ch_init },
- { 6, alc888_3st_hp_6ch_init },
-};
-
-/* toggle front-jack and RCA according to the hp-jack state */
-static void alc888_lenovo_ms7195_front_automute(struct hda_codec *codec)
-{
- unsigned int present = snd_hda_jack_detect(codec, 0x1b);
-
- snd_hda_codec_amp_stereo(codec, 0x14, HDA_OUTPUT, 0,
- HDA_AMP_MUTE, present ? HDA_AMP_MUTE : 0);
- snd_hda_codec_amp_stereo(codec, 0x15, HDA_OUTPUT, 0,
- HDA_AMP_MUTE, present ? HDA_AMP_MUTE : 0);
-}
-
-/* toggle RCA according to the front-jack state */
-static void alc888_lenovo_ms7195_rca_automute(struct hda_codec *codec)
+static void alc889_fixup_dac_route(struct hda_codec *codec,
+ const struct hda_fixup *fix, int action)
{
- unsigned int present = snd_hda_jack_detect(codec, 0x14);
-
- snd_hda_codec_amp_stereo(codec, 0x15, HDA_OUTPUT, 0,
- HDA_AMP_MUTE, present ? HDA_AMP_MUTE : 0);
-}
-
-static void alc883_lenovo_ms7195_unsol_event(struct hda_codec *codec,
- unsigned int res)
-{
- if ((res >> 26) == ALC880_HP_EVENT)
- alc888_lenovo_ms7195_front_automute(codec);
- if ((res >> 26) == ALC880_FRONT_EVENT)
- alc888_lenovo_ms7195_rca_automute(codec);
-}
-
-/* toggle speaker-output according to the hp-jack state */
-static void alc883_lenovo_nb0763_setup(struct hda_codec *codec)
-{
- struct alc_spec *spec = codec->spec;
-
- spec->autocfg.hp_pins[0] = 0x14;
- spec->autocfg.speaker_pins[0] = 0x15;
+ if (action == HDA_FIXUP_ACT_PRE_PROBE) {
+ /* fake the connections during parsing the tree */
+ hda_nid_t conn1[2] = { 0x0c, 0x0d };
+ hda_nid_t conn2[2] = { 0x0e, 0x0f };
+ snd_hda_override_conn_list(codec, 0x14, 2, conn1);
+ snd_hda_override_conn_list(codec, 0x15, 2, conn1);
+ snd_hda_override_conn_list(codec, 0x18, 2, conn2);
+ snd_hda_override_conn_list(codec, 0x1a, 2, conn2);
+ } else if (action == HDA_FIXUP_ACT_PROBE) {
+ /* restore the connections */
+ hda_nid_t conn[5] = { 0x0c, 0x0d, 0x0e, 0x0f, 0x26 };
+ snd_hda_override_conn_list(codec, 0x14, 5, conn);
+ snd_hda_override_conn_list(codec, 0x15, 5, conn);
+ snd_hda_override_conn_list(codec, 0x18, 5, conn);
+ snd_hda_override_conn_list(codec, 0x1a, 5, conn);
+ }
}
-/* toggle speaker-output according to the hp-jack state */
-#define alc883_targa_init_hook alc882_targa_init_hook
-#define alc883_targa_unsol_event alc882_targa_unsol_event
-
-static void alc883_clevo_m720_setup(struct hda_codec *codec)
+/* Set VREF on HP pin */
+static void alc889_fixup_mbp_vref(struct hda_codec *codec,
+ const struct hda_fixup *fix, int action)
{
struct alc_spec *spec = codec->spec;
+ static hda_nid_t nids[2] = { 0x14, 0x15 };
+ int i;
- spec->autocfg.hp_pins[0] = 0x15;
- spec->autocfg.speaker_pins[0] = 0x14;
-}
-
-static void alc883_clevo_m720_init_hook(struct hda_codec *codec)
-{
- alc_automute_amp(codec);
- alc88x_simple_mic_automute(codec);
-}
-
-static void alc883_clevo_m720_unsol_event(struct hda_codec *codec,
- unsigned int res)
-{
- switch (res >> 26) {
- case ALC880_MIC_EVENT:
- alc88x_simple_mic_automute(codec);
- break;
- default:
- alc_automute_amp_unsol_event(codec, res);
+ if (action != HDA_FIXUP_ACT_INIT)
+ return;
+ for (i = 0; i < ARRAY_SIZE(nids); i++) {
+ unsigned int val = snd_hda_codec_get_pincfg(codec, nids[i]);
+ if (get_defcfg_device(val) != AC_JACK_HP_OUT)
+ continue;
+ val = snd_hda_codec_get_pin_target(codec, nids[i]);
+ val |= AC_PINCTL_VREF_80;
+ snd_hda_set_pin_ctl(codec, nids[i], val);
+ spec->gen.keep_vref_in_automute = 1;
break;
}
}
-/* toggle speaker-output according to the hp-jack state */
-static void alc883_2ch_fujitsu_pi2515_setup(struct hda_codec *codec)
-{
- struct alc_spec *spec = codec->spec;
-
- spec->autocfg.hp_pins[0] = 0x14;
- spec->autocfg.speaker_pins[0] = 0x15;
-}
-
-static void alc883_haier_w66_setup(struct hda_codec *codec)
-{
- struct alc_spec *spec = codec->spec;
-
- spec->autocfg.hp_pins[0] = 0x1b;
- spec->autocfg.speaker_pins[0] = 0x14;
-}
-
-static void alc883_lenovo_101e_ispeaker_automute(struct hda_codec *codec)
-{
- int bits = snd_hda_jack_detect(codec, 0x14) ? HDA_AMP_MUTE : 0;
-
- snd_hda_codec_amp_stereo(codec, 0x15, HDA_OUTPUT, 0,
- HDA_AMP_MUTE, bits);
-}
-
-static void alc883_lenovo_101e_all_automute(struct hda_codec *codec)
-{
- int bits = snd_hda_jack_detect(codec, 0x1b) ? HDA_AMP_MUTE : 0;
-
- snd_hda_codec_amp_stereo(codec, 0x15, HDA_OUTPUT, 0,
- HDA_AMP_MUTE, bits);
- snd_hda_codec_amp_stereo(codec, 0x14, HDA_OUTPUT, 0,
- HDA_AMP_MUTE, bits);
-}
-
-static void alc883_lenovo_101e_unsol_event(struct hda_codec *codec,
- unsigned int res)
-{
- if ((res >> 26) == ALC880_HP_EVENT)
- alc883_lenovo_101e_all_automute(codec);
- if ((res >> 26) == ALC880_FRONT_EVENT)
- alc883_lenovo_101e_ispeaker_automute(codec);
-}
-
-/* toggle speaker-output according to the hp-jack state */
-static void alc883_acer_aspire_setup(struct hda_codec *codec)
-{
- struct alc_spec *spec = codec->spec;
-
- spec->autocfg.hp_pins[0] = 0x14;
- spec->autocfg.speaker_pins[0] = 0x15;
- spec->autocfg.speaker_pins[1] = 0x16;
-}
-
-static struct hda_verb alc883_acer_eapd_verbs[] = {
- /* HP Pin: output 0 (0x0c) */
- {0x14, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP},
- {0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
- {0x14, AC_VERB_SET_CONNECT_SEL, 0x00},
- /* Front Pin: output 0 (0x0c) */
- {0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
- {0x15, AC_VERB_SET_CONNECT_SEL, 0x00},
- {0x16, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
- {0x16, AC_VERB_SET_CONNECT_SEL, 0x00},
- /* eanable EAPD on medion laptop */
- {0x20, AC_VERB_SET_COEF_INDEX, 0x07},
- {0x20, AC_VERB_SET_PROC_COEF, 0x3050},
- /* enable unsolicited event */
- {0x14, AC_VERB_SET_UNSOLICITED_ENABLE, ALC880_HP_EVENT | AC_USRSP_EN},
- { }
-};
-
-static void alc888_6st_dell_setup(struct hda_codec *codec)
+static void alc889_fixup_mac_pins(struct hda_codec *codec,
+ const hda_nid_t *nids, int num_nids)
{
struct alc_spec *spec = codec->spec;
+ int i;
- spec->autocfg.hp_pins[0] = 0x1b;
- spec->autocfg.speaker_pins[0] = 0x14;
- spec->autocfg.speaker_pins[1] = 0x15;
- spec->autocfg.speaker_pins[2] = 0x16;
- spec->autocfg.speaker_pins[3] = 0x17;
+ for (i = 0; i < num_nids; i++) {
+ unsigned int val;
+ val = snd_hda_codec_get_pin_target(codec, nids[i]);
+ val |= AC_PINCTL_VREF_50;
+ snd_hda_set_pin_ctl(codec, nids[i], val);
+ }
+ spec->gen.keep_vref_in_automute = 1;
}
-static void alc888_lenovo_sky_setup(struct hda_codec *codec)
+/* Set VREF on speaker pins on imac91 */
+static void alc889_fixup_imac91_vref(struct hda_codec *codec,
+ const struct hda_fixup *fix, int action)
{
- struct alc_spec *spec = codec->spec;
+ static hda_nid_t nids[2] = { 0x18, 0x1a };
- spec->autocfg.hp_pins[0] = 0x1b;
- spec->autocfg.speaker_pins[0] = 0x14;
- spec->autocfg.speaker_pins[1] = 0x15;
- spec->autocfg.speaker_pins[2] = 0x16;
- spec->autocfg.speaker_pins[3] = 0x17;
- spec->autocfg.speaker_pins[4] = 0x1a;
+ if (action == HDA_FIXUP_ACT_INIT)
+ alc889_fixup_mac_pins(codec, nids, ARRAY_SIZE(nids));
}
-static void alc883_vaiott_setup(struct hda_codec *codec)
+/* Set VREF on speaker pins on mba11 */
+static void alc889_fixup_mba11_vref(struct hda_codec *codec,
+ const struct hda_fixup *fix, int action)
{
- struct alc_spec *spec = codec->spec;
+ static hda_nid_t nids[1] = { 0x18 };
- spec->autocfg.hp_pins[0] = 0x15;
- spec->autocfg.speaker_pins[0] = 0x14;
- spec->autocfg.speaker_pins[1] = 0x17;
+ if (action == HDA_FIXUP_ACT_INIT)
+ alc889_fixup_mac_pins(codec, nids, ARRAY_SIZE(nids));
}
-static struct hda_verb alc888_asus_m90v_verbs[] = {
- {0x22, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
- {0x23, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
- {0x23, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
- /* enable unsolicited event */
- {0x1b, AC_VERB_SET_UNSOLICITED_ENABLE, ALC880_HP_EVENT | AC_USRSP_EN},
- {0x18, AC_VERB_SET_UNSOLICITED_ENABLE, ALC880_MIC_EVENT | AC_USRSP_EN},
- { } /* end */
-};
-
-static void alc883_mode2_setup(struct hda_codec *codec)
+/* Set VREF on speaker pins on mba21 */
+static void alc889_fixup_mba21_vref(struct hda_codec *codec,
+ const struct hda_fixup *fix, int action)
{
- struct alc_spec *spec = codec->spec;
+ static hda_nid_t nids[2] = { 0x18, 0x19 };
- spec->autocfg.hp_pins[0] = 0x1b;
- spec->autocfg.speaker_pins[0] = 0x14;
- spec->autocfg.speaker_pins[1] = 0x15;
- spec->autocfg.speaker_pins[2] = 0x16;
- spec->ext_mic.pin = 0x18;
- spec->int_mic.pin = 0x19;
- spec->ext_mic.mux_idx = 0;
- spec->int_mic.mux_idx = 1;
- spec->auto_mic = 1;
+ if (action == HDA_FIXUP_ACT_INIT)
+ alc889_fixup_mac_pins(codec, nids, ARRAY_SIZE(nids));
}
-static struct hda_verb alc888_asus_eee1601_verbs[] = {
- {0x14, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP},
- {0x1b, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
- {0x22, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
- {0x23, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
- {0x23, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
- {0x20, AC_VERB_SET_COEF_INDEX, 0x0b},
- {0x20, AC_VERB_SET_PROC_COEF, 0x0838},
- /* enable unsolicited event */
- {0x14, AC_VERB_SET_UNSOLICITED_ENABLE, ALC880_HP_EVENT | AC_USRSP_EN},
- { } /* end */
-};
-
-static void alc883_eee1601_inithook(struct hda_codec *codec)
+/* Don't take HP output as primary
+ * Strangely, the speaker output doesn't work on Vaio Z and some Vaio
+ * all-in-one desktop PCs (for example VGC-LN51JGB) through DAC 0x05
+ */
+static void alc882_fixup_no_primary_hp(struct hda_codec *codec,
+ const struct hda_fixup *fix, int action)
{
struct alc_spec *spec = codec->spec;
-
- spec->autocfg.hp_pins[0] = 0x14;
- spec->autocfg.speaker_pins[0] = 0x1b;
- alc_automute_pin(codec);
-}
-
-static struct hda_verb alc889A_mb31_verbs[] = {
- /* Init rear pin (used as headphone output) */
- {0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, 0xc4}, /* Apple Headphones */
- {0x15, AC_VERB_SET_CONNECT_SEL, 0x00}, /* Connect to front */
- {0x15, AC_VERB_SET_UNSOLICITED_ENABLE, ALC880_HP_EVENT | AC_USRSP_EN},
- /* Init line pin (used as output in 4ch and 6ch mode) */
- {0x1a, AC_VERB_SET_CONNECT_SEL, 0x02}, /* Connect to CLFE */
- /* Init line 2 pin (used as headphone out by default) */
- {0x1b, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN}, /* Use as input */
- {0x1b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE}, /* Mute output */
- { } /* end */
-};
-
-/* Mute speakers according to the headphone jack state */
-static void alc889A_mb31_automute(struct hda_codec *codec)
-{
- unsigned int present;
-
- /* Mute only in 2ch or 4ch mode */
- if (snd_hda_codec_read(codec, 0x15, 0, AC_VERB_GET_CONNECT_SEL, 0)
- == 0x00) {
- present = snd_hda_jack_detect(codec, 0x15);
- snd_hda_codec_amp_stereo(codec, 0x14, HDA_OUTPUT, 0,
- HDA_AMP_MUTE, present ? HDA_AMP_MUTE : 0);
- snd_hda_codec_amp_stereo(codec, 0x16, HDA_OUTPUT, 0,
- HDA_AMP_MUTE, present ? HDA_AMP_MUTE : 0);
+ if (action == HDA_FIXUP_ACT_PRE_PROBE) {
+ spec->gen.no_primary_hp = 1;
+ spec->gen.no_multi_io = 1;
}
}
-static void alc889A_mb31_unsol_event(struct hda_codec *codec, unsigned int res)
-{
- if ((res >> 26) == ALC880_HP_EVENT)
- alc889A_mb31_automute(codec);
-}
-
-
-#ifdef CONFIG_SND_HDA_POWER_SAVE
-#define alc882_loopbacks alc880_loopbacks
-#endif
-
-/* pcm configuration: identical with ALC880 */
-#define alc882_pcm_analog_playback alc880_pcm_analog_playback
-#define alc882_pcm_analog_capture alc880_pcm_analog_capture
-#define alc882_pcm_digital_playback alc880_pcm_digital_playback
-#define alc882_pcm_digital_capture alc880_pcm_digital_capture
-
-static hda_nid_t alc883_slave_dig_outs[] = {
- ALC1200_DIGOUT_NID, 0,
-};
-
-static hda_nid_t alc1200_slave_dig_outs[] = {
- ALC883_DIGOUT_NID, 0,
-};
-
-/*
- * configuration and preset
- */
-static const char * const alc882_models[ALC882_MODEL_LAST] = {
- [ALC882_3ST_DIG] = "3stack-dig",
- [ALC882_6ST_DIG] = "6stack-dig",
- [ALC882_ARIMA] = "arima",
- [ALC882_W2JC] = "w2jc",
- [ALC882_TARGA] = "targa",
- [ALC882_ASUS_A7J] = "asus-a7j",
- [ALC882_ASUS_A7M] = "asus-a7m",
- [ALC885_MACPRO] = "macpro",
- [ALC885_MB5] = "mb5",
- [ALC885_MACMINI3] = "macmini3",
- [ALC885_MBA21] = "mba21",
- [ALC885_MBP3] = "mbp3",
- [ALC885_IMAC24] = "imac24",
- [ALC885_IMAC91] = "imac91",
- [ALC883_3ST_2ch_DIG] = "3stack-2ch-dig",
- [ALC883_3ST_6ch_DIG] = "3stack-6ch-dig",
- [ALC883_3ST_6ch] = "3stack-6ch",
- [ALC883_6ST_DIG] = "alc883-6stack-dig",
- [ALC883_TARGA_DIG] = "targa-dig",
- [ALC883_TARGA_2ch_DIG] = "targa-2ch-dig",
- [ALC883_TARGA_8ch_DIG] = "targa-8ch-dig",
- [ALC883_ACER] = "acer",
- [ALC883_ACER_ASPIRE] = "acer-aspire",
- [ALC888_ACER_ASPIRE_4930G] = "acer-aspire-4930g",
- [ALC888_ACER_ASPIRE_6530G] = "acer-aspire-6530g",
- [ALC888_ACER_ASPIRE_8930G] = "acer-aspire-8930g",
- [ALC888_ACER_ASPIRE_7730G] = "acer-aspire-7730g",
- [ALC883_MEDION] = "medion",
- [ALC883_MEDION_WIM2160] = "medion-wim2160",
- [ALC883_LAPTOP_EAPD] = "laptop-eapd",
- [ALC883_LENOVO_101E_2ch] = "lenovo-101e",
- [ALC883_LENOVO_NB0763] = "lenovo-nb0763",
- [ALC888_LENOVO_MS7195_DIG] = "lenovo-ms7195-dig",
- [ALC888_LENOVO_SKY] = "lenovo-sky",
- [ALC883_HAIER_W66] = "haier-w66",
- [ALC888_3ST_HP] = "3stack-hp",
- [ALC888_6ST_DELL] = "6stack-dell",
- [ALC883_MITAC] = "mitac",
- [ALC883_CLEVO_M540R] = "clevo-m540r",
- [ALC883_CLEVO_M720] = "clevo-m720",
- [ALC883_FUJITSU_PI2515] = "fujitsu-pi2515",
- [ALC888_FUJITSU_XA3530] = "fujitsu-xa3530",
- [ALC883_3ST_6ch_INTEL] = "3stack-6ch-intel",
- [ALC889A_INTEL] = "intel-alc889a",
- [ALC889_INTEL] = "intel-x58",
- [ALC1200_ASUS_P5Q] = "asus-p5q",
- [ALC889A_MB31] = "mb31",
- [ALC883_SONY_VAIO_TT] = "sony-vaio-tt",
- [ALC882_AUTO] = "auto",
-};
-
-static struct snd_pci_quirk alc882_cfg_tbl[] = {
- SND_PCI_QUIRK(0x1019, 0x6668, "ECS", ALC882_6ST_DIG),
+static void alc_fixup_bass_chmap(struct hda_codec *codec,
+ const struct hda_fixup *fix, int action);
- SND_PCI_QUIRK(0x1025, 0x006c, "Acer Aspire 9810", ALC883_ACER_ASPIRE),
- SND_PCI_QUIRK(0x1025, 0x0090, "Acer Aspire", ALC883_ACER_ASPIRE),
- SND_PCI_QUIRK(0x1025, 0x010a, "Acer Ferrari 5000", ALC883_ACER_ASPIRE),
- SND_PCI_QUIRK(0x1025, 0x0110, "Acer Aspire", ALC883_ACER_ASPIRE),
- SND_PCI_QUIRK(0x1025, 0x0112, "Acer Aspire 9303", ALC883_ACER_ASPIRE),
- SND_PCI_QUIRK(0x1025, 0x0121, "Acer Aspire 5920G", ALC883_ACER_ASPIRE),
- SND_PCI_QUIRK(0x1025, 0x013e, "Acer Aspire 4930G",
- ALC888_ACER_ASPIRE_4930G),
- SND_PCI_QUIRK(0x1025, 0x013f, "Acer Aspire 5930G",
- ALC888_ACER_ASPIRE_4930G),
- SND_PCI_QUIRK(0x1025, 0x0145, "Acer Aspire 8930G",
- ALC888_ACER_ASPIRE_8930G),
- SND_PCI_QUIRK(0x1025, 0x0146, "Acer Aspire 6935G",
- ALC888_ACER_ASPIRE_8930G),
- SND_PCI_QUIRK(0x1025, 0x0157, "Acer X3200", ALC882_AUTO),
- SND_PCI_QUIRK(0x1025, 0x0158, "Acer AX1700-U3700A", ALC882_AUTO),
- SND_PCI_QUIRK(0x1025, 0x015e, "Acer Aspire 6930G",
- ALC888_ACER_ASPIRE_6530G),
- SND_PCI_QUIRK(0x1025, 0x0166, "Acer Aspire 6530G",
- ALC888_ACER_ASPIRE_6530G),
- SND_PCI_QUIRK(0x1025, 0x0142, "Acer Aspire 7730G",
- ALC888_ACER_ASPIRE_7730G),
- /* default Acer -- disabled as it causes more problems.
- * model=auto should work fine now
- */
- /* SND_PCI_QUIRK_VENDOR(0x1025, "Acer laptop", ALC883_ACER), */
-
- SND_PCI_QUIRK(0x1028, 0x020d, "Dell Inspiron 530", ALC888_6ST_DELL),
-
- SND_PCI_QUIRK(0x103c, 0x2a3d, "HP Pavilion", ALC883_6ST_DIG),
- SND_PCI_QUIRK(0x103c, 0x2a4f, "HP Samba", ALC888_3ST_HP),
- SND_PCI_QUIRK(0x103c, 0x2a60, "HP Lucknow", ALC888_3ST_HP),
- SND_PCI_QUIRK(0x103c, 0x2a61, "HP Nettle", ALC883_6ST_DIG),
- SND_PCI_QUIRK(0x103c, 0x2a66, "HP Acacia", ALC888_3ST_HP),
- SND_PCI_QUIRK(0x103c, 0x2a72, "HP Educ.ar", ALC888_3ST_HP),
-
- SND_PCI_QUIRK(0x1043, 0x060d, "Asus A7J", ALC882_ASUS_A7J),
- SND_PCI_QUIRK(0x1043, 0x1243, "Asus A7J", ALC882_ASUS_A7J),
- SND_PCI_QUIRK(0x1043, 0x13c2, "Asus A7M", ALC882_ASUS_A7M),
- SND_PCI_QUIRK(0x1043, 0x1873, "Asus M90V", ALC888_ASUS_M90V),
- SND_PCI_QUIRK(0x1043, 0x1971, "Asus W2JC", ALC882_W2JC),
- SND_PCI_QUIRK(0x1043, 0x817f, "Asus P5LD2", ALC882_6ST_DIG),
- SND_PCI_QUIRK(0x1043, 0x81d8, "Asus P5WD", ALC882_6ST_DIG),
- SND_PCI_QUIRK(0x1043, 0x8249, "Asus M2A-VM HDMI", ALC883_3ST_6ch_DIG),
- SND_PCI_QUIRK(0x1043, 0x8284, "Asus Z37E", ALC883_6ST_DIG),
- SND_PCI_QUIRK(0x1043, 0x82fe, "Asus P5Q-EM HDMI", ALC1200_ASUS_P5Q),
- SND_PCI_QUIRK(0x1043, 0x835f, "Asus Eee 1601", ALC888_ASUS_EEE1601),
-
- SND_PCI_QUIRK(0x104d, 0x9047, "Sony Vaio TT", ALC883_SONY_VAIO_TT),
- SND_PCI_QUIRK(0x105b, 0x0ce8, "Foxconn P35AX-S", ALC883_6ST_DIG),
- SND_PCI_QUIRK(0x105b, 0x6668, "Foxconn", ALC882_6ST_DIG),
- SND_PCI_QUIRK(0x1071, 0x8227, "Mitac 82801H", ALC883_MITAC),
- SND_PCI_QUIRK(0x1071, 0x8253, "Mitac 8252d", ALC883_MITAC),
- SND_PCI_QUIRK(0x1071, 0x8258, "Evesham Voyaeger", ALC883_LAPTOP_EAPD),
- SND_PCI_QUIRK(0x10f1, 0x2350, "TYAN-S2350", ALC888_6ST_DELL),
- SND_PCI_QUIRK(0x108e, 0x534d, NULL, ALC883_3ST_6ch),
-
- SND_PCI_QUIRK(0x1462, 0x0349, "MSI", ALC883_TARGA_2ch_DIG),
- SND_PCI_QUIRK(0x1462, 0x040d, "MSI", ALC883_TARGA_2ch_DIG),
- SND_PCI_QUIRK(0x1462, 0x0579, "MSI", ALC883_TARGA_2ch_DIG),
- SND_PCI_QUIRK(0x1462, 0x28fb, "Targa T8", ALC882_TARGA), /* MSI-1049 T8 */
- SND_PCI_QUIRK(0x1462, 0x2fb3, "MSI", ALC882_AUTO),
- SND_PCI_QUIRK(0x1462, 0x6668, "MSI", ALC882_6ST_DIG),
- SND_PCI_QUIRK(0x1462, 0x3729, "MSI S420", ALC883_TARGA_DIG),
- SND_PCI_QUIRK(0x1462, 0x3783, "NEC S970", ALC883_TARGA_DIG),
- SND_PCI_QUIRK(0x1462, 0x3b7f, "MSI", ALC883_TARGA_2ch_DIG),
- SND_PCI_QUIRK(0x1462, 0x3ef9, "MSI", ALC883_TARGA_DIG),
- SND_PCI_QUIRK(0x1462, 0x3fc1, "MSI", ALC883_TARGA_DIG),
- SND_PCI_QUIRK(0x1462, 0x3fc3, "MSI", ALC883_TARGA_DIG),
- SND_PCI_QUIRK(0x1462, 0x3fcc, "MSI", ALC883_TARGA_DIG),
- SND_PCI_QUIRK(0x1462, 0x3fdf, "MSI", ALC883_TARGA_DIG),
- SND_PCI_QUIRK(0x1462, 0x42cd, "MSI", ALC883_TARGA_DIG),
- SND_PCI_QUIRK(0x1462, 0x4314, "MSI", ALC883_TARGA_DIG),
- SND_PCI_QUIRK(0x1462, 0x4319, "MSI", ALC883_TARGA_DIG),
- SND_PCI_QUIRK(0x1462, 0x4324, "MSI", ALC883_TARGA_DIG),
- SND_PCI_QUIRK(0x1462, 0x4570, "MSI Wind Top AE2220", ALC883_TARGA_DIG),
- SND_PCI_QUIRK(0x1462, 0x6510, "MSI GX620", ALC883_TARGA_8ch_DIG),
- SND_PCI_QUIRK(0x1462, 0x6668, "MSI", ALC883_6ST_DIG),
- SND_PCI_QUIRK(0x1462, 0x7187, "MSI", ALC883_6ST_DIG),
- SND_PCI_QUIRK(0x1462, 0x7250, "MSI", ALC883_6ST_DIG),
- SND_PCI_QUIRK(0x1462, 0x7260, "MSI 7260", ALC883_TARGA_DIG),
- SND_PCI_QUIRK(0x1462, 0x7267, "MSI", ALC883_3ST_6ch_DIG),
- SND_PCI_QUIRK(0x1462, 0x7280, "MSI", ALC883_6ST_DIG),
- SND_PCI_QUIRK(0x1462, 0x7327, "MSI", ALC883_6ST_DIG),
- SND_PCI_QUIRK(0x1462, 0x7350, "MSI", ALC883_6ST_DIG),
- SND_PCI_QUIRK(0x1462, 0x7437, "MSI NetOn AP1900", ALC883_TARGA_DIG),
- SND_PCI_QUIRK(0x1462, 0xa422, "MSI", ALC883_TARGA_2ch_DIG),
- SND_PCI_QUIRK(0x1462, 0xaa08, "MSI", ALC883_TARGA_2ch_DIG),
-
- SND_PCI_QUIRK(0x147b, 0x1083, "Abit IP35-PRO", ALC883_6ST_DIG),
- SND_PCI_QUIRK(0x1558, 0x0571, "Clevo laptop M570U", ALC883_3ST_6ch_DIG),
- SND_PCI_QUIRK(0x1558, 0x0721, "Clevo laptop M720R", ALC883_CLEVO_M720),
- SND_PCI_QUIRK(0x1558, 0x0722, "Clevo laptop M720SR", ALC883_CLEVO_M720),
- SND_PCI_QUIRK(0x1558, 0x5409, "Clevo laptop M540R", ALC883_CLEVO_M540R),
- SND_PCI_QUIRK_VENDOR(0x1558, "Clevo laptop", ALC883_LAPTOP_EAPD),
- SND_PCI_QUIRK(0x15d9, 0x8780, "Supermicro PDSBA", ALC883_3ST_6ch),
- /* SND_PCI_QUIRK(0x161f, 0x2054, "Arima W820", ALC882_ARIMA), */
- SND_PCI_QUIRK(0x161f, 0x2054, "Medion laptop", ALC883_MEDION),
- SND_PCI_QUIRK_MASK(0x1734, 0xfff0, 0x1100, "FSC AMILO Xi/Pi25xx",
- ALC883_FUJITSU_PI2515),
- SND_PCI_QUIRK_MASK(0x1734, 0xfff0, 0x1130, "Fujitsu AMILO Xa35xx",
- ALC888_FUJITSU_XA3530),
- SND_PCI_QUIRK(0x17aa, 0x101e, "Lenovo 101e", ALC883_LENOVO_101E_2ch),
- SND_PCI_QUIRK(0x17aa, 0x2085, "Lenovo NB0763", ALC883_LENOVO_NB0763),
- SND_PCI_QUIRK(0x17aa, 0x3bfc, "Lenovo NB0763", ALC883_LENOVO_NB0763),
- SND_PCI_QUIRK(0x17aa, 0x3bfd, "Lenovo NB0763", ALC883_LENOVO_NB0763),
- SND_PCI_QUIRK(0x17aa, 0x101d, "Lenovo Sky", ALC888_LENOVO_SKY),
- SND_PCI_QUIRK(0x17c0, 0x4085, "MEDION MD96630", ALC888_LENOVO_MS7195_DIG),
- SND_PCI_QUIRK(0x17f2, 0x5000, "Albatron KI690-AM2", ALC883_6ST_DIG),
- SND_PCI_QUIRK(0x1991, 0x5625, "Haier W66", ALC883_HAIER_W66),
-
- SND_PCI_QUIRK(0x8086, 0x0001, "DG33BUC", ALC883_3ST_6ch_INTEL),
- SND_PCI_QUIRK(0x8086, 0x0002, "DG33FBC", ALC883_3ST_6ch_INTEL),
- SND_PCI_QUIRK(0x8086, 0x2503, "82801H", ALC883_MITAC),
- SND_PCI_QUIRK(0x8086, 0x0022, "DX58SO", ALC889_INTEL),
- SND_PCI_QUIRK(0x8086, 0x0021, "Intel IbexPeak", ALC889A_INTEL),
- SND_PCI_QUIRK(0x8086, 0x3b56, "Intel IbexPeak", ALC889A_INTEL),
- SND_PCI_QUIRK(0x8086, 0xd601, "D102GGC", ALC882_6ST_DIG),
-
- {}
-};
-
-/* codec SSID table for Intel Mac */
-static struct snd_pci_quirk alc882_ssid_cfg_tbl[] = {
- SND_PCI_QUIRK(0x106b, 0x00a0, "MacBookPro 3,1", ALC885_MBP3),
- SND_PCI_QUIRK(0x106b, 0x00a1, "Macbook", ALC885_MBP3),
- SND_PCI_QUIRK(0x106b, 0x00a4, "MacbookPro 4,1", ALC885_MBP3),
- SND_PCI_QUIRK(0x106b, 0x0c00, "Mac Pro", ALC885_MACPRO),
- SND_PCI_QUIRK(0x106b, 0x1000, "iMac 24", ALC885_IMAC24),
- SND_PCI_QUIRK(0x106b, 0x2800, "AppleTV", ALC885_IMAC24),
- SND_PCI_QUIRK(0x106b, 0x2c00, "MacbookPro rev3", ALC885_MBP3),
- SND_PCI_QUIRK(0x106b, 0x3000, "iMac", ALC889A_MB31),
- SND_PCI_QUIRK(0x106b, 0x3200, "iMac 7,1 Aluminum", ALC882_ASUS_A7M),
- SND_PCI_QUIRK(0x106b, 0x3400, "MacBookAir 1,1", ALC885_MBP3),
- SND_PCI_QUIRK(0x106b, 0x3500, "MacBookAir 2,1", ALC885_MBA21),
- SND_PCI_QUIRK(0x106b, 0x3600, "Macbook 3,1", ALC889A_MB31),
- SND_PCI_QUIRK(0x106b, 0x3800, "MacbookPro 4,1", ALC885_MBP3),
- SND_PCI_QUIRK(0x106b, 0x3e00, "iMac 24 Aluminum", ALC885_IMAC24),
- SND_PCI_QUIRK(0x106b, 0x4900, "iMac 9,1 Aluminum", ALC885_IMAC91),
- SND_PCI_QUIRK(0x106b, 0x3f00, "Macbook 5,1", ALC885_MB5),
- SND_PCI_QUIRK(0x106b, 0x4a00, "Macbook 5,2", ALC885_MB5),
- /* FIXME: HP jack sense seems not working for MBP 5,1 or 5,2,
- * so apparently no perfect solution yet
- */
- SND_PCI_QUIRK(0x106b, 0x4000, "MacbookPro 5,1", ALC885_MB5),
- SND_PCI_QUIRK(0x106b, 0x4600, "MacbookPro 5,2", ALC885_MB5),
- SND_PCI_QUIRK(0x106b, 0x4100, "Macmini 3,1", ALC885_MACMINI3),
- {} /* terminator */
-};
-
-static struct alc_config_preset alc882_presets[] = {
- [ALC882_3ST_DIG] = {
- .mixers = { alc882_base_mixer },
- .init_verbs = { alc882_base_init_verbs,
- alc882_adc1_init_verbs },
- .num_dacs = ARRAY_SIZE(alc882_dac_nids),
- .dac_nids = alc882_dac_nids,
- .dig_out_nid = ALC882_DIGOUT_NID,
- .dig_in_nid = ALC882_DIGIN_NID,
- .num_channel_mode = ARRAY_SIZE(alc882_ch_modes),
- .channel_mode = alc882_ch_modes,
- .need_dac_fix = 1,
- .input_mux = &alc882_capture_source,
- },
- [ALC882_6ST_DIG] = {
- .mixers = { alc882_base_mixer, alc882_chmode_mixer },
- .init_verbs = { alc882_base_init_verbs,
- alc882_adc1_init_verbs },
- .num_dacs = ARRAY_SIZE(alc882_dac_nids),
- .dac_nids = alc882_dac_nids,
- .dig_out_nid = ALC882_DIGOUT_NID,
- .dig_in_nid = ALC882_DIGIN_NID,
- .num_channel_mode = ARRAY_SIZE(alc882_sixstack_modes),
- .channel_mode = alc882_sixstack_modes,
- .input_mux = &alc882_capture_source,
- },
- [ALC882_ARIMA] = {
- .mixers = { alc882_base_mixer, alc882_chmode_mixer },
- .init_verbs = { alc882_base_init_verbs, alc882_adc1_init_verbs,
- alc882_eapd_verbs },
- .num_dacs = ARRAY_SIZE(alc882_dac_nids),
- .dac_nids = alc882_dac_nids,
- .num_channel_mode = ARRAY_SIZE(alc882_sixstack_modes),
- .channel_mode = alc882_sixstack_modes,
- .input_mux = &alc882_capture_source,
- },
- [ALC882_W2JC] = {
- .mixers = { alc882_w2jc_mixer, alc882_chmode_mixer },
- .init_verbs = { alc882_base_init_verbs, alc882_adc1_init_verbs,
- alc882_eapd_verbs, alc880_gpio1_init_verbs },
- .num_dacs = ARRAY_SIZE(alc882_dac_nids),
- .dac_nids = alc882_dac_nids,
- .num_channel_mode = ARRAY_SIZE(alc880_threestack_modes),
- .channel_mode = alc880_threestack_modes,
- .need_dac_fix = 1,
- .input_mux = &alc882_capture_source,
- .dig_out_nid = ALC882_DIGOUT_NID,
- },
- [ALC885_MBA21] = {
- .mixers = { alc885_mba21_mixer },
- .init_verbs = { alc885_mba21_init_verbs, alc880_gpio1_init_verbs },
- .num_dacs = 2,
- .dac_nids = alc882_dac_nids,
- .channel_mode = alc885_mba21_ch_modes,
- .num_channel_mode = ARRAY_SIZE(alc885_mba21_ch_modes),
- .input_mux = &alc882_capture_source,
- .unsol_event = alc_automute_amp_unsol_event,
- .setup = alc885_mba21_setup,
- .init_hook = alc_automute_amp,
- },
- [ALC885_MBP3] = {
- .mixers = { alc885_mbp3_mixer, alc882_chmode_mixer },
- .init_verbs = { alc885_mbp3_init_verbs,
- alc880_gpio1_init_verbs },
- .num_dacs = 2,
- .dac_nids = alc882_dac_nids,
- .hp_nid = 0x04,
- .channel_mode = alc885_mbp_4ch_modes,
- .num_channel_mode = ARRAY_SIZE(alc885_mbp_4ch_modes),
- .input_mux = &alc882_capture_source,
- .dig_out_nid = ALC882_DIGOUT_NID,
- .dig_in_nid = ALC882_DIGIN_NID,
- .unsol_event = alc_automute_amp_unsol_event,
- .setup = alc885_mbp3_setup,
- .init_hook = alc_automute_amp,
- },
- [ALC885_MB5] = {
- .mixers = { alc885_mb5_mixer, alc882_chmode_mixer },
- .init_verbs = { alc885_mb5_init_verbs,
- alc880_gpio1_init_verbs },
- .num_dacs = ARRAY_SIZE(alc882_dac_nids),
- .dac_nids = alc882_dac_nids,
- .channel_mode = alc885_mb5_6ch_modes,
- .num_channel_mode = ARRAY_SIZE(alc885_mb5_6ch_modes),
- .input_mux = &mb5_capture_source,
- .dig_out_nid = ALC882_DIGOUT_NID,
- .dig_in_nid = ALC882_DIGIN_NID,
- .unsol_event = alc_automute_amp_unsol_event,
- .setup = alc885_mb5_setup,
- .init_hook = alc_automute_amp,
- },
- [ALC885_MACMINI3] = {
- .mixers = { alc885_macmini3_mixer, alc882_chmode_mixer },
- .init_verbs = { alc885_macmini3_init_verbs,
- alc880_gpio1_init_verbs },
- .num_dacs = ARRAY_SIZE(alc882_dac_nids),
- .dac_nids = alc882_dac_nids,
- .channel_mode = alc885_macmini3_6ch_modes,
- .num_channel_mode = ARRAY_SIZE(alc885_macmini3_6ch_modes),
- .input_mux = &macmini3_capture_source,
- .dig_out_nid = ALC882_DIGOUT_NID,
- .dig_in_nid = ALC882_DIGIN_NID,
- .unsol_event = alc_automute_amp_unsol_event,
- .setup = alc885_macmini3_setup,
- .init_hook = alc_automute_amp,
- },
- [ALC885_MACPRO] = {
- .mixers = { alc882_macpro_mixer },
- .init_verbs = { alc882_macpro_init_verbs },
- .num_dacs = ARRAY_SIZE(alc882_dac_nids),
- .dac_nids = alc882_dac_nids,
- .dig_out_nid = ALC882_DIGOUT_NID,
- .dig_in_nid = ALC882_DIGIN_NID,
- .num_channel_mode = ARRAY_SIZE(alc882_ch_modes),
- .channel_mode = alc882_ch_modes,
- .input_mux = &alc882_capture_source,
- .init_hook = alc885_macpro_init_hook,
- },
- [ALC885_IMAC24] = {
- .mixers = { alc885_imac24_mixer },
- .init_verbs = { alc885_imac24_init_verbs },
- .num_dacs = ARRAY_SIZE(alc882_dac_nids),
- .dac_nids = alc882_dac_nids,
- .dig_out_nid = ALC882_DIGOUT_NID,
- .dig_in_nid = ALC882_DIGIN_NID,
- .num_channel_mode = ARRAY_SIZE(alc882_ch_modes),
- .channel_mode = alc882_ch_modes,
- .input_mux = &alc882_capture_source,
- .unsol_event = alc_automute_amp_unsol_event,
- .setup = alc885_imac24_setup,
- .init_hook = alc885_imac24_init_hook,
- },
- [ALC885_IMAC91] = {
- .mixers = {alc885_imac91_mixer},
- .init_verbs = { alc885_imac91_init_verbs,
- alc880_gpio1_init_verbs },
- .num_dacs = ARRAY_SIZE(alc882_dac_nids),
- .dac_nids = alc882_dac_nids,
- .channel_mode = alc885_mba21_ch_modes,
- .num_channel_mode = ARRAY_SIZE(alc885_mba21_ch_modes),
- .input_mux = &alc889A_imac91_capture_source,
- .dig_out_nid = ALC882_DIGOUT_NID,
- .dig_in_nid = ALC882_DIGIN_NID,
- .unsol_event = alc_automute_amp_unsol_event,
- .setup = alc885_imac91_setup,
- .init_hook = alc_automute_amp,
- },
- [ALC882_TARGA] = {
- .mixers = { alc882_targa_mixer, alc882_chmode_mixer },
- .init_verbs = { alc882_base_init_verbs, alc882_adc1_init_verbs,
- alc880_gpio3_init_verbs, alc882_targa_verbs},
- .num_dacs = ARRAY_SIZE(alc882_dac_nids),
- .dac_nids = alc882_dac_nids,
- .dig_out_nid = ALC882_DIGOUT_NID,
- .num_adc_nids = ARRAY_SIZE(alc882_adc_nids),
- .adc_nids = alc882_adc_nids,
- .capsrc_nids = alc882_capsrc_nids,
- .num_channel_mode = ARRAY_SIZE(alc882_3ST_6ch_modes),
- .channel_mode = alc882_3ST_6ch_modes,
- .need_dac_fix = 1,
- .input_mux = &alc882_capture_source,
- .unsol_event = alc882_targa_unsol_event,
- .setup = alc882_targa_setup,
- .init_hook = alc882_targa_automute,
- },
- [ALC882_ASUS_A7J] = {
- .mixers = { alc882_asus_a7j_mixer, alc882_chmode_mixer },
- .init_verbs = { alc882_base_init_verbs, alc882_adc1_init_verbs,
- alc882_asus_a7j_verbs},
- .num_dacs = ARRAY_SIZE(alc882_dac_nids),
- .dac_nids = alc882_dac_nids,
- .dig_out_nid = ALC882_DIGOUT_NID,
- .num_adc_nids = ARRAY_SIZE(alc882_adc_nids),
- .adc_nids = alc882_adc_nids,
- .capsrc_nids = alc882_capsrc_nids,
- .num_channel_mode = ARRAY_SIZE(alc882_3ST_6ch_modes),
- .channel_mode = alc882_3ST_6ch_modes,
- .need_dac_fix = 1,
- .input_mux = &alc882_capture_source,
- },
- [ALC882_ASUS_A7M] = {
- .mixers = { alc882_asus_a7m_mixer, alc882_chmode_mixer },
- .init_verbs = { alc882_base_init_verbs, alc882_adc1_init_verbs,
- alc882_eapd_verbs, alc880_gpio1_init_verbs,
- alc882_asus_a7m_verbs },
- .num_dacs = ARRAY_SIZE(alc882_dac_nids),
- .dac_nids = alc882_dac_nids,
- .dig_out_nid = ALC882_DIGOUT_NID,
- .num_channel_mode = ARRAY_SIZE(alc880_threestack_modes),
- .channel_mode = alc880_threestack_modes,
- .need_dac_fix = 1,
- .input_mux = &alc882_capture_source,
- },
- [ALC883_3ST_2ch_DIG] = {
- .mixers = { alc883_3ST_2ch_mixer },
- .init_verbs = { alc883_init_verbs },
- .num_dacs = ARRAY_SIZE(alc883_dac_nids),
- .dac_nids = alc883_dac_nids,
- .dig_out_nid = ALC883_DIGOUT_NID,
- .dig_in_nid = ALC883_DIGIN_NID,
- .num_channel_mode = ARRAY_SIZE(alc883_3ST_2ch_modes),
- .channel_mode = alc883_3ST_2ch_modes,
- .input_mux = &alc883_capture_source,
- },
- [ALC883_3ST_6ch_DIG] = {
- .mixers = { alc883_3ST_6ch_mixer, alc883_chmode_mixer },
- .init_verbs = { alc883_init_verbs },
- .num_dacs = ARRAY_SIZE(alc883_dac_nids),
- .dac_nids = alc883_dac_nids,
- .dig_out_nid = ALC883_DIGOUT_NID,
- .dig_in_nid = ALC883_DIGIN_NID,
- .num_channel_mode = ARRAY_SIZE(alc883_3ST_6ch_modes),
- .channel_mode = alc883_3ST_6ch_modes,
- .need_dac_fix = 1,
- .input_mux = &alc883_capture_source,
- },
- [ALC883_3ST_6ch] = {
- .mixers = { alc883_3ST_6ch_mixer, alc883_chmode_mixer },
- .init_verbs = { alc883_init_verbs },
- .num_dacs = ARRAY_SIZE(alc883_dac_nids),
- .dac_nids = alc883_dac_nids,
- .num_channel_mode = ARRAY_SIZE(alc883_3ST_6ch_modes),
- .channel_mode = alc883_3ST_6ch_modes,
- .need_dac_fix = 1,
- .input_mux = &alc883_capture_source,
- },
- [ALC883_3ST_6ch_INTEL] = {
- .mixers = { alc883_3ST_6ch_intel_mixer, alc883_chmode_mixer },
- .init_verbs = { alc883_init_verbs },
- .num_dacs = ARRAY_SIZE(alc883_dac_nids),
- .dac_nids = alc883_dac_nids,
- .dig_out_nid = ALC883_DIGOUT_NID,
- .dig_in_nid = ALC883_DIGIN_NID,
- .slave_dig_outs = alc883_slave_dig_outs,
- .num_channel_mode = ARRAY_SIZE(alc883_3ST_6ch_intel_modes),
- .channel_mode = alc883_3ST_6ch_intel_modes,
- .need_dac_fix = 1,
- .input_mux = &alc883_3stack_6ch_intel,
- },
- [ALC889A_INTEL] = {
- .mixers = { alc885_8ch_intel_mixer, alc883_chmode_mixer },
- .init_verbs = { alc885_init_verbs, alc885_init_input_verbs,
- alc_hp15_unsol_verbs },
- .num_dacs = ARRAY_SIZE(alc883_dac_nids),
- .dac_nids = alc883_dac_nids,
- .num_adc_nids = ARRAY_SIZE(alc889_adc_nids),
- .adc_nids = alc889_adc_nids,
- .dig_out_nid = ALC883_DIGOUT_NID,
- .dig_in_nid = ALC883_DIGIN_NID,
- .slave_dig_outs = alc883_slave_dig_outs,
- .num_channel_mode = ARRAY_SIZE(alc889_8ch_intel_modes),
- .channel_mode = alc889_8ch_intel_modes,
- .capsrc_nids = alc889_capsrc_nids,
- .input_mux = &alc889_capture_source,
- .setup = alc889_automute_setup,
- .init_hook = alc_automute_amp,
- .unsol_event = alc_automute_amp_unsol_event,
- .need_dac_fix = 1,
- },
- [ALC889_INTEL] = {
- .mixers = { alc885_8ch_intel_mixer, alc883_chmode_mixer },
- .init_verbs = { alc885_init_verbs, alc889_init_input_verbs,
- alc889_eapd_verbs, alc_hp15_unsol_verbs},
- .num_dacs = ARRAY_SIZE(alc883_dac_nids),
- .dac_nids = alc883_dac_nids,
- .num_adc_nids = ARRAY_SIZE(alc889_adc_nids),
- .adc_nids = alc889_adc_nids,
- .dig_out_nid = ALC883_DIGOUT_NID,
- .dig_in_nid = ALC883_DIGIN_NID,
- .slave_dig_outs = alc883_slave_dig_outs,
- .num_channel_mode = ARRAY_SIZE(alc889_8ch_intel_modes),
- .channel_mode = alc889_8ch_intel_modes,
- .capsrc_nids = alc889_capsrc_nids,
- .input_mux = &alc889_capture_source,
- .setup = alc889_automute_setup,
- .init_hook = alc889_intel_init_hook,
- .unsol_event = alc_automute_amp_unsol_event,
- .need_dac_fix = 1,
+static const struct hda_fixup alc882_fixups[] = {
+ [ALC882_FIXUP_ABIT_AW9D_MAX] = {
+ .type = HDA_FIXUP_PINS,
+ .v.pins = (const struct hda_pintbl[]) {
+ { 0x15, 0x01080104 }, /* side */
+ { 0x16, 0x01011012 }, /* rear */
+ { 0x17, 0x01016011 }, /* clfe */
+ { }
+ }
},
- [ALC883_6ST_DIG] = {
- .mixers = { alc883_base_mixer, alc883_chmode_mixer },
- .init_verbs = { alc883_init_verbs },
- .num_dacs = ARRAY_SIZE(alc883_dac_nids),
- .dac_nids = alc883_dac_nids,
- .dig_out_nid = ALC883_DIGOUT_NID,
- .dig_in_nid = ALC883_DIGIN_NID,
- .num_channel_mode = ARRAY_SIZE(alc883_sixstack_modes),
- .channel_mode = alc883_sixstack_modes,
- .input_mux = &alc883_capture_source,
+ [ALC882_FIXUP_LENOVO_Y530] = {
+ .type = HDA_FIXUP_PINS,
+ .v.pins = (const struct hda_pintbl[]) {
+ { 0x15, 0x99130112 }, /* rear int speakers */
+ { 0x16, 0x99130111 }, /* subwoofer */
+ { }
+ }
},
- [ALC883_TARGA_DIG] = {
- .mixers = { alc883_targa_mixer, alc883_chmode_mixer },
- .init_verbs = { alc883_init_verbs, alc880_gpio3_init_verbs,
- alc883_targa_verbs},
- .num_dacs = ARRAY_SIZE(alc883_dac_nids),
- .dac_nids = alc883_dac_nids,
- .dig_out_nid = ALC883_DIGOUT_NID,
- .num_channel_mode = ARRAY_SIZE(alc883_3ST_6ch_modes),
- .channel_mode = alc883_3ST_6ch_modes,
- .need_dac_fix = 1,
- .input_mux = &alc883_capture_source,
- .unsol_event = alc883_targa_unsol_event,
- .setup = alc882_targa_setup,
- .init_hook = alc882_targa_automute,
+ [ALC882_FIXUP_PB_M5210] = {
+ .type = HDA_FIXUP_PINCTLS,
+ .v.pins = (const struct hda_pintbl[]) {
+ { 0x19, PIN_VREF50 },
+ {}
+ }
},
- [ALC883_TARGA_2ch_DIG] = {
- .mixers = { alc883_targa_2ch_mixer},
- .init_verbs = { alc883_init_verbs, alc880_gpio3_init_verbs,
- alc883_targa_verbs},
- .num_dacs = ARRAY_SIZE(alc883_dac_nids),
- .dac_nids = alc883_dac_nids,
- .adc_nids = alc883_adc_nids_alt,
- .num_adc_nids = ARRAY_SIZE(alc883_adc_nids_alt),
- .capsrc_nids = alc883_capsrc_nids,
- .dig_out_nid = ALC883_DIGOUT_NID,
- .num_channel_mode = ARRAY_SIZE(alc883_3ST_2ch_modes),
- .channel_mode = alc883_3ST_2ch_modes,
- .input_mux = &alc883_capture_source,
- .unsol_event = alc883_targa_unsol_event,
- .setup = alc882_targa_setup,
- .init_hook = alc882_targa_automute,
+ [ALC882_FIXUP_ACER_ASPIRE_7736] = {
+ .type = HDA_FIXUP_FUNC,
+ .v.func = alc_fixup_sku_ignore,
},
- [ALC883_TARGA_8ch_DIG] = {
- .mixers = { alc883_targa_mixer, alc883_targa_8ch_mixer,
- alc883_chmode_mixer },
- .init_verbs = { alc883_init_verbs, alc880_gpio3_init_verbs,
- alc883_targa_verbs },
- .num_dacs = ARRAY_SIZE(alc883_dac_nids),
- .dac_nids = alc883_dac_nids,
- .num_adc_nids = ARRAY_SIZE(alc883_adc_nids_rev),
- .adc_nids = alc883_adc_nids_rev,
- .capsrc_nids = alc883_capsrc_nids_rev,
- .dig_out_nid = ALC883_DIGOUT_NID,
- .dig_in_nid = ALC883_DIGIN_NID,
- .num_channel_mode = ARRAY_SIZE(alc883_4ST_8ch_modes),
- .channel_mode = alc883_4ST_8ch_modes,
- .need_dac_fix = 1,
- .input_mux = &alc883_capture_source,
- .unsol_event = alc883_targa_unsol_event,
- .setup = alc882_targa_setup,
- .init_hook = alc882_targa_automute,
+ [ALC882_FIXUP_ASUS_W90V] = {
+ .type = HDA_FIXUP_PINS,
+ .v.pins = (const struct hda_pintbl[]) {
+ { 0x16, 0x99130110 }, /* fix sequence for CLFE */
+ { }
+ }
},
- [ALC883_ACER] = {
- .mixers = { alc883_base_mixer },
- /* On TravelMate laptops, GPIO 0 enables the internal speaker
- * and the headphone jack. Turn this on and rely on the
- * standard mute methods whenever the user wants to turn
- * these outputs off.
- */
- .init_verbs = { alc883_init_verbs, alc880_gpio1_init_verbs },
- .num_dacs = ARRAY_SIZE(alc883_dac_nids),
- .dac_nids = alc883_dac_nids,
- .num_channel_mode = ARRAY_SIZE(alc883_3ST_2ch_modes),
- .channel_mode = alc883_3ST_2ch_modes,
- .input_mux = &alc883_capture_source,
+ [ALC889_FIXUP_CD] = {
+ .type = HDA_FIXUP_PINS,
+ .v.pins = (const struct hda_pintbl[]) {
+ { 0x1c, 0x993301f0 }, /* CD */
+ { }
+ }
},
- [ALC883_ACER_ASPIRE] = {
- .mixers = { alc883_acer_aspire_mixer },
- .init_verbs = { alc883_init_verbs, alc883_acer_eapd_verbs },
- .num_dacs = ARRAY_SIZE(alc883_dac_nids),
- .dac_nids = alc883_dac_nids,
- .dig_out_nid = ALC883_DIGOUT_NID,
- .num_channel_mode = ARRAY_SIZE(alc883_3ST_2ch_modes),
- .channel_mode = alc883_3ST_2ch_modes,
- .input_mux = &alc883_capture_source,
- .unsol_event = alc_automute_amp_unsol_event,
- .setup = alc883_acer_aspire_setup,
- .init_hook = alc_automute_amp,
+ [ALC889_FIXUP_FRONT_HP_NO_PRESENCE] = {
+ .type = HDA_FIXUP_PINS,
+ .v.pins = (const struct hda_pintbl[]) {
+ { 0x1b, 0x02214120 }, /* Front HP jack is flaky, disable jack detect */
+ { }
+ },
+ .chained = true,
+ .chain_id = ALC889_FIXUP_CD,
},
- [ALC888_ACER_ASPIRE_4930G] = {
- .mixers = { alc888_acer_aspire_4930g_mixer,
- alc883_chmode_mixer },
- .init_verbs = { alc883_init_verbs, alc880_gpio1_init_verbs,
- alc888_acer_aspire_4930g_verbs },
- .num_dacs = ARRAY_SIZE(alc883_dac_nids),
- .dac_nids = alc883_dac_nids,
- .num_adc_nids = ARRAY_SIZE(alc883_adc_nids_rev),
- .adc_nids = alc883_adc_nids_rev,
- .capsrc_nids = alc883_capsrc_nids_rev,
- .dig_out_nid = ALC883_DIGOUT_NID,
- .num_channel_mode = ARRAY_SIZE(alc883_3ST_6ch_modes),
- .channel_mode = alc883_3ST_6ch_modes,
- .need_dac_fix = 1,
- .const_channel_count = 6,
- .num_mux_defs =
- ARRAY_SIZE(alc888_2_capture_sources),
- .input_mux = alc888_2_capture_sources,
- .unsol_event = alc_automute_amp_unsol_event,
- .setup = alc888_acer_aspire_4930g_setup,
- .init_hook = alc_automute_amp,
+ [ALC889_FIXUP_VAIO_TT] = {
+ .type = HDA_FIXUP_PINS,
+ .v.pins = (const struct hda_pintbl[]) {
+ { 0x17, 0x90170111 }, /* hidden surround speaker */
+ { }
+ }
},
- [ALC888_ACER_ASPIRE_6530G] = {
- .mixers = { alc888_acer_aspire_6530_mixer },
- .init_verbs = { alc883_init_verbs, alc880_gpio1_init_verbs,
- alc888_acer_aspire_6530g_verbs },
- .num_dacs = ARRAY_SIZE(alc883_dac_nids),
- .dac_nids = alc883_dac_nids,
- .num_adc_nids = ARRAY_SIZE(alc883_adc_nids_rev),
- .adc_nids = alc883_adc_nids_rev,
- .capsrc_nids = alc883_capsrc_nids_rev,
- .dig_out_nid = ALC883_DIGOUT_NID,
- .num_channel_mode = ARRAY_SIZE(alc883_3ST_2ch_modes),
- .channel_mode = alc883_3ST_2ch_modes,
- .num_mux_defs =
- ARRAY_SIZE(alc888_2_capture_sources),
- .input_mux = alc888_acer_aspire_6530_sources,
- .unsol_event = alc_automute_amp_unsol_event,
- .setup = alc888_acer_aspire_6530g_setup,
- .init_hook = alc_automute_amp,
+ [ALC888_FIXUP_EEE1601] = {
+ .type = HDA_FIXUP_VERBS,
+ .v.verbs = (const struct hda_verb[]) {
+ { 0x20, AC_VERB_SET_COEF_INDEX, 0x0b },
+ { 0x20, AC_VERB_SET_PROC_COEF, 0x0838 },
+ { }
+ }
},
- [ALC888_ACER_ASPIRE_8930G] = {
- .mixers = { alc889_acer_aspire_8930g_mixer,
- alc883_chmode_mixer },
- .init_verbs = { alc883_init_verbs, alc880_gpio1_init_verbs,
- alc889_acer_aspire_8930g_verbs,
- alc889_eapd_verbs},
- .num_dacs = ARRAY_SIZE(alc883_dac_nids),
- .dac_nids = alc883_dac_nids,
- .num_adc_nids = ARRAY_SIZE(alc889_adc_nids),
- .adc_nids = alc889_adc_nids,
- .capsrc_nids = alc889_capsrc_nids,
- .dig_out_nid = ALC883_DIGOUT_NID,
- .num_channel_mode = ARRAY_SIZE(alc883_3ST_6ch_modes),
- .channel_mode = alc883_3ST_6ch_modes,
- .need_dac_fix = 1,
- .const_channel_count = 6,
- .num_mux_defs =
- ARRAY_SIZE(alc889_capture_sources),
- .input_mux = alc889_capture_sources,
- .unsol_event = alc_automute_amp_unsol_event,
- .setup = alc889_acer_aspire_8930g_setup,
- .init_hook = alc_automute_amp,
-#ifdef CONFIG_SND_HDA_POWER_SAVE
- .power_hook = alc_power_eapd,
-#endif
+ [ALC882_FIXUP_EAPD] = {
+ .type = HDA_FIXUP_VERBS,
+ .v.verbs = (const struct hda_verb[]) {
+ /* change to EAPD mode */
+ { 0x20, AC_VERB_SET_COEF_INDEX, 0x07 },
+ { 0x20, AC_VERB_SET_PROC_COEF, 0x3060 },
+ { }
+ }
},
- [ALC888_ACER_ASPIRE_7730G] = {
- .mixers = { alc883_3ST_6ch_mixer,
- alc883_chmode_mixer },
- .init_verbs = { alc883_init_verbs, alc880_gpio1_init_verbs,
- alc888_acer_aspire_7730G_verbs },
- .num_dacs = ARRAY_SIZE(alc883_dac_nids),
- .dac_nids = alc883_dac_nids,
- .num_adc_nids = ARRAY_SIZE(alc883_adc_nids_rev),
- .adc_nids = alc883_adc_nids_rev,
- .capsrc_nids = alc883_capsrc_nids_rev,
- .dig_out_nid = ALC883_DIGOUT_NID,
- .num_channel_mode = ARRAY_SIZE(alc883_3ST_6ch_modes),
- .channel_mode = alc883_3ST_6ch_modes,
- .need_dac_fix = 1,
- .const_channel_count = 6,
- .input_mux = &alc883_capture_source,
- .unsol_event = alc_automute_amp_unsol_event,
- .setup = alc888_acer_aspire_7730g_setup,
- .init_hook = alc_automute_amp,
+ [ALC883_FIXUP_EAPD] = {
+ .type = HDA_FIXUP_VERBS,
+ .v.verbs = (const struct hda_verb[]) {
+ /* change to EAPD mode */
+ { 0x20, AC_VERB_SET_COEF_INDEX, 0x07 },
+ { 0x20, AC_VERB_SET_PROC_COEF, 0x3070 },
+ { }
+ }
},
- [ALC883_MEDION] = {
- .mixers = { alc883_fivestack_mixer,
- alc883_chmode_mixer },
- .init_verbs = { alc883_init_verbs,
- alc883_medion_eapd_verbs },
- .num_dacs = ARRAY_SIZE(alc883_dac_nids),
- .dac_nids = alc883_dac_nids,
- .adc_nids = alc883_adc_nids_alt,
- .num_adc_nids = ARRAY_SIZE(alc883_adc_nids_alt),
- .capsrc_nids = alc883_capsrc_nids,
- .num_channel_mode = ARRAY_SIZE(alc883_sixstack_modes),
- .channel_mode = alc883_sixstack_modes,
- .input_mux = &alc883_capture_source,
+ [ALC883_FIXUP_ACER_EAPD] = {
+ .type = HDA_FIXUP_VERBS,
+ .v.verbs = (const struct hda_verb[]) {
+ /* eanable EAPD on Acer laptops */
+ { 0x20, AC_VERB_SET_COEF_INDEX, 0x07 },
+ { 0x20, AC_VERB_SET_PROC_COEF, 0x3050 },
+ { }
+ }
},
- [ALC883_MEDION_WIM2160] = {
- .mixers = { alc883_medion_wim2160_mixer },
- .init_verbs = { alc883_init_verbs, alc883_medion_wim2160_verbs },
- .num_dacs = ARRAY_SIZE(alc883_dac_nids),
- .dac_nids = alc883_dac_nids,
- .dig_out_nid = ALC883_DIGOUT_NID,
- .num_adc_nids = ARRAY_SIZE(alc883_adc_nids),
- .adc_nids = alc883_adc_nids,
- .num_channel_mode = ARRAY_SIZE(alc883_3ST_2ch_modes),
- .channel_mode = alc883_3ST_2ch_modes,
- .input_mux = &alc883_capture_source,
- .unsol_event = alc_automute_amp_unsol_event,
- .setup = alc883_medion_wim2160_setup,
- .init_hook = alc_automute_amp,
+ [ALC882_FIXUP_GPIO1] = {
+ .type = HDA_FIXUP_VERBS,
+ .v.verbs = alc_gpio1_init_verbs,
},
- [ALC883_LAPTOP_EAPD] = {
- .mixers = { alc883_base_mixer },
- .init_verbs = { alc883_init_verbs, alc882_eapd_verbs },
- .num_dacs = ARRAY_SIZE(alc883_dac_nids),
- .dac_nids = alc883_dac_nids,
- .num_channel_mode = ARRAY_SIZE(alc883_3ST_2ch_modes),
- .channel_mode = alc883_3ST_2ch_modes,
- .input_mux = &alc883_capture_source,
+ [ALC882_FIXUP_GPIO2] = {
+ .type = HDA_FIXUP_VERBS,
+ .v.verbs = alc_gpio2_init_verbs,
},
- [ALC883_CLEVO_M540R] = {
- .mixers = { alc883_3ST_6ch_mixer, alc883_chmode_mixer },
- .init_verbs = { alc883_init_verbs, alc883_clevo_m540r_verbs },
- .num_dacs = ARRAY_SIZE(alc883_dac_nids),
- .dac_nids = alc883_dac_nids,
- .dig_out_nid = ALC883_DIGOUT_NID,
- .dig_in_nid = ALC883_DIGIN_NID,
- .num_channel_mode = ARRAY_SIZE(alc883_3ST_6ch_clevo_modes),
- .channel_mode = alc883_3ST_6ch_clevo_modes,
- .need_dac_fix = 1,
- .input_mux = &alc883_capture_source,
- /* This machine has the hardware HP auto-muting, thus
- * we need no software mute via unsol event
- */
+ [ALC882_FIXUP_GPIO3] = {
+ .type = HDA_FIXUP_VERBS,
+ .v.verbs = alc_gpio3_init_verbs,
},
- [ALC883_CLEVO_M720] = {
- .mixers = { alc883_clevo_m720_mixer },
- .init_verbs = { alc883_init_verbs, alc883_clevo_m720_verbs },
- .num_dacs = ARRAY_SIZE(alc883_dac_nids),
- .dac_nids = alc883_dac_nids,
- .dig_out_nid = ALC883_DIGOUT_NID,
- .num_channel_mode = ARRAY_SIZE(alc883_3ST_2ch_modes),
- .channel_mode = alc883_3ST_2ch_modes,
- .input_mux = &alc883_capture_source,
- .unsol_event = alc883_clevo_m720_unsol_event,
- .setup = alc883_clevo_m720_setup,
- .init_hook = alc883_clevo_m720_init_hook,
+ [ALC882_FIXUP_ASUS_W2JC] = {
+ .type = HDA_FIXUP_VERBS,
+ .v.verbs = alc_gpio1_init_verbs,
+ .chained = true,
+ .chain_id = ALC882_FIXUP_EAPD,
},
- [ALC883_LENOVO_101E_2ch] = {
- .mixers = { alc883_lenovo_101e_2ch_mixer},
- .init_verbs = { alc883_init_verbs, alc883_lenovo_101e_verbs},
- .num_dacs = ARRAY_SIZE(alc883_dac_nids),
- .dac_nids = alc883_dac_nids,
- .adc_nids = alc883_adc_nids_alt,
- .num_adc_nids = ARRAY_SIZE(alc883_adc_nids_alt),
- .capsrc_nids = alc883_capsrc_nids,
- .num_channel_mode = ARRAY_SIZE(alc883_3ST_2ch_modes),
- .channel_mode = alc883_3ST_2ch_modes,
- .input_mux = &alc883_lenovo_101e_capture_source,
- .unsol_event = alc883_lenovo_101e_unsol_event,
- .init_hook = alc883_lenovo_101e_all_automute,
+ [ALC889_FIXUP_COEF] = {
+ .type = HDA_FIXUP_FUNC,
+ .v.func = alc889_fixup_coef,
},
- [ALC883_LENOVO_NB0763] = {
- .mixers = { alc883_lenovo_nb0763_mixer },
- .init_verbs = { alc883_init_verbs, alc883_lenovo_nb0763_verbs},
- .num_dacs = ARRAY_SIZE(alc883_dac_nids),
- .dac_nids = alc883_dac_nids,
- .num_channel_mode = ARRAY_SIZE(alc883_3ST_2ch_modes),
- .channel_mode = alc883_3ST_2ch_modes,
- .need_dac_fix = 1,
- .input_mux = &alc883_lenovo_nb0763_capture_source,
- .unsol_event = alc_automute_amp_unsol_event,
- .setup = alc883_lenovo_nb0763_setup,
- .init_hook = alc_automute_amp,
+ [ALC882_FIXUP_ACER_ASPIRE_4930G] = {
+ .type = HDA_FIXUP_PINS,
+ .v.pins = (const struct hda_pintbl[]) {
+ { 0x16, 0x99130111 }, /* CLFE speaker */
+ { 0x17, 0x99130112 }, /* surround speaker */
+ { }
+ },
+ .chained = true,
+ .chain_id = ALC882_FIXUP_GPIO1,
},
- [ALC888_LENOVO_MS7195_DIG] = {
- .mixers = { alc883_3ST_6ch_mixer, alc883_chmode_mixer },
- .init_verbs = { alc883_init_verbs, alc888_lenovo_ms7195_verbs},
- .num_dacs = ARRAY_SIZE(alc883_dac_nids),
- .dac_nids = alc883_dac_nids,
- .dig_out_nid = ALC883_DIGOUT_NID,
- .num_channel_mode = ARRAY_SIZE(alc883_3ST_6ch_modes),
- .channel_mode = alc883_3ST_6ch_modes,
- .need_dac_fix = 1,
- .input_mux = &alc883_capture_source,
- .unsol_event = alc883_lenovo_ms7195_unsol_event,
- .init_hook = alc888_lenovo_ms7195_front_automute,
+ [ALC882_FIXUP_ACER_ASPIRE_8930G] = {
+ .type = HDA_FIXUP_PINS,
+ .v.pins = (const struct hda_pintbl[]) {
+ { 0x16, 0x99130111 }, /* CLFE speaker */
+ { 0x1b, 0x99130112 }, /* surround speaker */
+ { }
+ },
+ .chained = true,
+ .chain_id = ALC882_FIXUP_ASPIRE_8930G_VERBS,
},
- [ALC883_HAIER_W66] = {
- .mixers = { alc883_targa_2ch_mixer},
- .init_verbs = { alc883_init_verbs, alc883_haier_w66_verbs},
- .num_dacs = ARRAY_SIZE(alc883_dac_nids),
- .dac_nids = alc883_dac_nids,
- .dig_out_nid = ALC883_DIGOUT_NID,
- .num_channel_mode = ARRAY_SIZE(alc883_3ST_2ch_modes),
- .channel_mode = alc883_3ST_2ch_modes,
- .input_mux = &alc883_capture_source,
- .unsol_event = alc_automute_amp_unsol_event,
- .setup = alc883_haier_w66_setup,
- .init_hook = alc_automute_amp,
+ [ALC882_FIXUP_ASPIRE_8930G_VERBS] = {
+ /* additional init verbs for Acer Aspire 8930G */
+ .type = HDA_FIXUP_VERBS,
+ .v.verbs = (const struct hda_verb[]) {
+ /* Enable all DACs */
+ /* DAC DISABLE/MUTE 1? */
+ /* setting bits 1-5 disables DAC nids 0x02-0x06
+ * apparently. Init=0x38 */
+ { 0x20, AC_VERB_SET_COEF_INDEX, 0x03 },
+ { 0x20, AC_VERB_SET_PROC_COEF, 0x0000 },
+ /* DAC DISABLE/MUTE 2? */
+ /* some bit here disables the other DACs.
+ * Init=0x4900 */
+ { 0x20, AC_VERB_SET_COEF_INDEX, 0x08 },
+ { 0x20, AC_VERB_SET_PROC_COEF, 0x0000 },
+ /* DMIC fix
+ * This laptop has a stereo digital microphone.
+ * The mics are only 1cm apart which makes the stereo
+ * useless. However, either the mic or the ALC889
+ * makes the signal become a difference/sum signal
+ * instead of standard stereo, which is annoying.
+ * So instead we flip this bit which makes the
+ * codec replicate the sum signal to both channels,
+ * turning it into a normal mono mic.
+ */
+ /* DMIC_CONTROL? Init value = 0x0001 */
+ { 0x20, AC_VERB_SET_COEF_INDEX, 0x0b },
+ { 0x20, AC_VERB_SET_PROC_COEF, 0x0003 },
+ { 0x20, AC_VERB_SET_COEF_INDEX, 0x07 },
+ { 0x20, AC_VERB_SET_PROC_COEF, 0x3050 },
+ { }
+ },
+ .chained = true,
+ .chain_id = ALC882_FIXUP_GPIO1,
},
- [ALC888_3ST_HP] = {
- .mixers = { alc883_3ST_6ch_mixer, alc883_chmode_mixer },
- .init_verbs = { alc883_init_verbs, alc888_3st_hp_verbs },
- .num_dacs = ARRAY_SIZE(alc883_dac_nids),
- .dac_nids = alc883_dac_nids,
- .num_channel_mode = ARRAY_SIZE(alc888_3st_hp_modes),
- .channel_mode = alc888_3st_hp_modes,
- .need_dac_fix = 1,
- .input_mux = &alc883_capture_source,
- .unsol_event = alc_automute_amp_unsol_event,
- .setup = alc888_3st_hp_setup,
- .init_hook = alc_automute_amp,
+ [ALC885_FIXUP_MACPRO_GPIO] = {
+ .type = HDA_FIXUP_FUNC,
+ .v.func = alc885_fixup_macpro_gpio,
},
- [ALC888_6ST_DELL] = {
- .mixers = { alc883_base_mixer, alc883_chmode_mixer },
- .init_verbs = { alc883_init_verbs, alc888_6st_dell_verbs },
- .num_dacs = ARRAY_SIZE(alc883_dac_nids),
- .dac_nids = alc883_dac_nids,
- .dig_out_nid = ALC883_DIGOUT_NID,
- .dig_in_nid = ALC883_DIGIN_NID,
- .num_channel_mode = ARRAY_SIZE(alc883_sixstack_modes),
- .channel_mode = alc883_sixstack_modes,
- .input_mux = &alc883_capture_source,
- .unsol_event = alc_automute_amp_unsol_event,
- .setup = alc888_6st_dell_setup,
- .init_hook = alc_automute_amp,
+ [ALC889_FIXUP_DAC_ROUTE] = {
+ .type = HDA_FIXUP_FUNC,
+ .v.func = alc889_fixup_dac_route,
},
- [ALC883_MITAC] = {
- .mixers = { alc883_mitac_mixer },
- .init_verbs = { alc883_init_verbs, alc883_mitac_verbs },
- .num_dacs = ARRAY_SIZE(alc883_dac_nids),
- .dac_nids = alc883_dac_nids,
- .num_channel_mode = ARRAY_SIZE(alc883_3ST_2ch_modes),
- .channel_mode = alc883_3ST_2ch_modes,
- .input_mux = &alc883_capture_source,
- .unsol_event = alc_automute_amp_unsol_event,
- .setup = alc883_mitac_setup,
- .init_hook = alc_automute_amp,
+ [ALC889_FIXUP_MBP_VREF] = {
+ .type = HDA_FIXUP_FUNC,
+ .v.func = alc889_fixup_mbp_vref,
+ .chained = true,
+ .chain_id = ALC882_FIXUP_GPIO1,
},
- [ALC883_FUJITSU_PI2515] = {
- .mixers = { alc883_2ch_fujitsu_pi2515_mixer },
- .init_verbs = { alc883_init_verbs,
- alc883_2ch_fujitsu_pi2515_verbs},
- .num_dacs = ARRAY_SIZE(alc883_dac_nids),
- .dac_nids = alc883_dac_nids,
- .dig_out_nid = ALC883_DIGOUT_NID,
- .num_channel_mode = ARRAY_SIZE(alc883_3ST_2ch_modes),
- .channel_mode = alc883_3ST_2ch_modes,
- .input_mux = &alc883_fujitsu_pi2515_capture_source,
- .unsol_event = alc_automute_amp_unsol_event,
- .setup = alc883_2ch_fujitsu_pi2515_setup,
- .init_hook = alc_automute_amp,
+ [ALC889_FIXUP_IMAC91_VREF] = {
+ .type = HDA_FIXUP_FUNC,
+ .v.func = alc889_fixup_imac91_vref,
+ .chained = true,
+ .chain_id = ALC882_FIXUP_GPIO1,
},
- [ALC888_FUJITSU_XA3530] = {
- .mixers = { alc888_base_mixer, alc883_chmode_mixer },
- .init_verbs = { alc883_init_verbs,
- alc888_fujitsu_xa3530_verbs },
- .num_dacs = ARRAY_SIZE(alc883_dac_nids),
- .dac_nids = alc883_dac_nids,
- .num_adc_nids = ARRAY_SIZE(alc883_adc_nids_rev),
- .adc_nids = alc883_adc_nids_rev,
- .capsrc_nids = alc883_capsrc_nids_rev,
- .dig_out_nid = ALC883_DIGOUT_NID,
- .num_channel_mode = ARRAY_SIZE(alc888_4ST_8ch_intel_modes),
- .channel_mode = alc888_4ST_8ch_intel_modes,
- .num_mux_defs =
- ARRAY_SIZE(alc888_2_capture_sources),
- .input_mux = alc888_2_capture_sources,
- .unsol_event = alc_automute_amp_unsol_event,
- .setup = alc888_fujitsu_xa3530_setup,
- .init_hook = alc_automute_amp,
+ [ALC889_FIXUP_MBA11_VREF] = {
+ .type = HDA_FIXUP_FUNC,
+ .v.func = alc889_fixup_mba11_vref,
+ .chained = true,
+ .chain_id = ALC889_FIXUP_MBP_VREF,
},
- [ALC888_LENOVO_SKY] = {
- .mixers = { alc888_lenovo_sky_mixer, alc883_chmode_mixer },
- .init_verbs = { alc883_init_verbs, alc888_lenovo_sky_verbs},
- .num_dacs = ARRAY_SIZE(alc883_dac_nids),
- .dac_nids = alc883_dac_nids,
- .dig_out_nid = ALC883_DIGOUT_NID,
- .num_channel_mode = ARRAY_SIZE(alc883_sixstack_modes),
- .channel_mode = alc883_sixstack_modes,
- .need_dac_fix = 1,
- .input_mux = &alc883_lenovo_sky_capture_source,
- .unsol_event = alc_automute_amp_unsol_event,
- .setup = alc888_lenovo_sky_setup,
- .init_hook = alc_automute_amp,
+ [ALC889_FIXUP_MBA21_VREF] = {
+ .type = HDA_FIXUP_FUNC,
+ .v.func = alc889_fixup_mba21_vref,
+ .chained = true,
+ .chain_id = ALC889_FIXUP_MBP_VREF,
},
- [ALC888_ASUS_M90V] = {
- .mixers = { alc883_3ST_6ch_mixer, alc883_chmode_mixer },
- .init_verbs = { alc883_init_verbs, alc888_asus_m90v_verbs },
- .num_dacs = ARRAY_SIZE(alc883_dac_nids),
- .dac_nids = alc883_dac_nids,
- .dig_out_nid = ALC883_DIGOUT_NID,
- .dig_in_nid = ALC883_DIGIN_NID,
- .num_channel_mode = ARRAY_SIZE(alc883_3ST_6ch_modes),
- .channel_mode = alc883_3ST_6ch_modes,
- .need_dac_fix = 1,
- .input_mux = &alc883_fujitsu_pi2515_capture_source,
- .unsol_event = alc_sku_unsol_event,
- .setup = alc883_mode2_setup,
- .init_hook = alc_inithook,
+ [ALC889_FIXUP_MP11_VREF] = {
+ .type = HDA_FIXUP_FUNC,
+ .v.func = alc889_fixup_mba11_vref,
+ .chained = true,
+ .chain_id = ALC885_FIXUP_MACPRO_GPIO,
},
- [ALC888_ASUS_EEE1601] = {
- .mixers = { alc883_asus_eee1601_mixer },
- .cap_mixer = alc883_asus_eee1601_cap_mixer,
- .init_verbs = { alc883_init_verbs, alc888_asus_eee1601_verbs },
- .num_dacs = ARRAY_SIZE(alc883_dac_nids),
- .dac_nids = alc883_dac_nids,
- .dig_out_nid = ALC883_DIGOUT_NID,
- .dig_in_nid = ALC883_DIGIN_NID,
- .num_channel_mode = ARRAY_SIZE(alc883_3ST_2ch_modes),
- .channel_mode = alc883_3ST_2ch_modes,
- .need_dac_fix = 1,
- .input_mux = &alc883_asus_eee1601_capture_source,
- .unsol_event = alc_sku_unsol_event,
- .init_hook = alc883_eee1601_inithook,
+ [ALC882_FIXUP_INV_DMIC] = {
+ .type = HDA_FIXUP_FUNC,
+ .v.func = alc_fixup_inv_dmic_0x12,
},
- [ALC1200_ASUS_P5Q] = {
- .mixers = { alc883_base_mixer, alc883_chmode_mixer },
- .init_verbs = { alc883_init_verbs },
- .num_dacs = ARRAY_SIZE(alc883_dac_nids),
- .dac_nids = alc883_dac_nids,
- .dig_out_nid = ALC1200_DIGOUT_NID,
- .dig_in_nid = ALC883_DIGIN_NID,
- .slave_dig_outs = alc1200_slave_dig_outs,
- .num_channel_mode = ARRAY_SIZE(alc883_sixstack_modes),
- .channel_mode = alc883_sixstack_modes,
- .input_mux = &alc883_capture_source,
+ [ALC882_FIXUP_NO_PRIMARY_HP] = {
+ .type = HDA_FIXUP_FUNC,
+ .v.func = alc882_fixup_no_primary_hp,
},
- [ALC889A_MB31] = {
- .mixers = { alc889A_mb31_mixer, alc883_chmode_mixer},
- .init_verbs = { alc883_init_verbs, alc889A_mb31_verbs,
- alc880_gpio1_init_verbs },
- .adc_nids = alc883_adc_nids,
- .num_adc_nids = ARRAY_SIZE(alc883_adc_nids),
- .capsrc_nids = alc883_capsrc_nids,
- .dac_nids = alc883_dac_nids,
- .num_dacs = ARRAY_SIZE(alc883_dac_nids),
- .channel_mode = alc889A_mb31_6ch_modes,
- .num_channel_mode = ARRAY_SIZE(alc889A_mb31_6ch_modes),
- .input_mux = &alc889A_mb31_capture_source,
- .dig_out_nid = ALC883_DIGOUT_NID,
- .unsol_event = alc889A_mb31_unsol_event,
- .init_hook = alc889A_mb31_automute,
+ [ALC887_FIXUP_ASUS_BASS] = {
+ .type = HDA_FIXUP_PINS,
+ .v.pins = (const struct hda_pintbl[]) {
+ {0x16, 0x99130130}, /* bass speaker */
+ {}
+ },
+ .chained = true,
+ .chain_id = ALC887_FIXUP_BASS_CHMAP,
},
- [ALC883_SONY_VAIO_TT] = {
- .mixers = { alc883_vaiott_mixer },
- .init_verbs = { alc883_init_verbs, alc883_vaiott_verbs },
- .num_dacs = ARRAY_SIZE(alc883_dac_nids),
- .dac_nids = alc883_dac_nids,
- .num_channel_mode = ARRAY_SIZE(alc883_3ST_2ch_modes),
- .channel_mode = alc883_3ST_2ch_modes,
- .input_mux = &alc883_capture_source,
- .unsol_event = alc_automute_amp_unsol_event,
- .setup = alc883_vaiott_setup,
- .init_hook = alc_automute_amp,
+ [ALC887_FIXUP_BASS_CHMAP] = {
+ .type = HDA_FIXUP_FUNC,
+ .v.func = alc_fixup_bass_chmap,
},
};
-
-/*
- * Pin config fixes
- */
-enum {
- PINFIX_ABIT_AW9D_MAX,
- PINFIX_LENOVO_Y530,
- PINFIX_PB_M5210,
- PINFIX_ACER_ASPIRE_7736,
- PINFIX_GIGABYTE_880GM,
-};
-
-static const struct alc_fixup alc882_fixups[] = {
- [PINFIX_ABIT_AW9D_MAX] = {
- .type = ALC_FIXUP_PINS,
- .v.pins = (const struct alc_pincfg[]) {
- { 0x15, 0x01080104 }, /* side */
- { 0x16, 0x01011012 }, /* rear */
- { 0x17, 0x01016011 }, /* clfe */
- { }
- }
- },
- [PINFIX_LENOVO_Y530] = {
- .type = ALC_FIXUP_PINS,
- .v.pins = (const struct alc_pincfg[]) {
- { 0x15, 0x99130112 }, /* rear int speakers */
- { 0x16, 0x99130111 }, /* subwoofer */
- { }
- }
- },
- [PINFIX_PB_M5210] = {
- .type = ALC_FIXUP_VERBS,
- .v.verbs = (const struct hda_verb[]) {
- { 0x19, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF50 },
- {}
- }
- },
- [PINFIX_ACER_ASPIRE_7736] = {
- .type = ALC_FIXUP_SKU,
- .v.sku = ALC_FIXUP_SKU_IGNORE,
- },
- [PINFIX_GIGABYTE_880GM] = {
- .type = ALC_FIXUP_PINS,
- .v.pins = (const struct alc_pincfg[]) {
- { 0x14, 0x1114410 }, /* set as speaker */
- { }
- }
- },
+static const struct snd_pci_quirk alc882_fixup_tbl[] = {
+ SND_PCI_QUIRK(0x1025, 0x006c, "Acer Aspire 9810", ALC883_FIXUP_ACER_EAPD),
+ SND_PCI_QUIRK(0x1025, 0x0090, "Acer Aspire", ALC883_FIXUP_ACER_EAPD),
+ SND_PCI_QUIRK(0x1025, 0x010a, "Acer Ferrari 5000", ALC883_FIXUP_ACER_EAPD),
+ SND_PCI_QUIRK(0x1025, 0x0110, "Acer Aspire", ALC883_FIXUP_ACER_EAPD),
+ SND_PCI_QUIRK(0x1025, 0x0112, "Acer Aspire 9303", ALC883_FIXUP_ACER_EAPD),
+ SND_PCI_QUIRK(0x1025, 0x0121, "Acer Aspire 5920G", ALC883_FIXUP_ACER_EAPD),
+ SND_PCI_QUIRK(0x1025, 0x013e, "Acer Aspire 4930G",
+ ALC882_FIXUP_ACER_ASPIRE_4930G),
+ SND_PCI_QUIRK(0x1025, 0x013f, "Acer Aspire 5930G",
+ ALC882_FIXUP_ACER_ASPIRE_4930G),
+ SND_PCI_QUIRK(0x1025, 0x0145, "Acer Aspire 8930G",
+ ALC882_FIXUP_ACER_ASPIRE_8930G),
+ SND_PCI_QUIRK(0x1025, 0x0146, "Acer Aspire 6935G",
+ ALC882_FIXUP_ACER_ASPIRE_8930G),
+ SND_PCI_QUIRK(0x1025, 0x015e, "Acer Aspire 6930G",
+ ALC882_FIXUP_ACER_ASPIRE_4930G),
+ SND_PCI_QUIRK(0x1025, 0x0166, "Acer Aspire 6530G",
+ ALC882_FIXUP_ACER_ASPIRE_4930G),
+ SND_PCI_QUIRK(0x1025, 0x0142, "Acer Aspire 7730G",
+ ALC882_FIXUP_ACER_ASPIRE_4930G),
+ SND_PCI_QUIRK(0x1025, 0x0155, "Packard-Bell M5120", ALC882_FIXUP_PB_M5210),
+ SND_PCI_QUIRK(0x1025, 0x021e, "Acer Aspire 5739G",
+ ALC882_FIXUP_ACER_ASPIRE_4930G),
+ SND_PCI_QUIRK(0x1025, 0x0259, "Acer Aspire 5935", ALC889_FIXUP_DAC_ROUTE),
+ SND_PCI_QUIRK(0x1025, 0x026b, "Acer Aspire 8940G", ALC882_FIXUP_ACER_ASPIRE_8930G),
+ SND_PCI_QUIRK(0x1025, 0x0296, "Acer Aspire 7736z", ALC882_FIXUP_ACER_ASPIRE_7736),
+ SND_PCI_QUIRK(0x1043, 0x13c2, "Asus A7M", ALC882_FIXUP_EAPD),
+ SND_PCI_QUIRK(0x1043, 0x1873, "ASUS W90V", ALC882_FIXUP_ASUS_W90V),
+ SND_PCI_QUIRK(0x1043, 0x1971, "Asus W2JC", ALC882_FIXUP_ASUS_W2JC),
+ SND_PCI_QUIRK(0x1043, 0x835f, "Asus Eee 1601", ALC888_FIXUP_EEE1601),
+ SND_PCI_QUIRK(0x1043, 0x84bc, "ASUS ET2700", ALC887_FIXUP_ASUS_BASS),
+ SND_PCI_QUIRK(0x104d, 0x9047, "Sony Vaio TT", ALC889_FIXUP_VAIO_TT),
+ SND_PCI_QUIRK(0x104d, 0x905a, "Sony Vaio Z", ALC882_FIXUP_NO_PRIMARY_HP),
+ SND_PCI_QUIRK(0x104d, 0x9043, "Sony Vaio VGC-LN51JGB", ALC882_FIXUP_NO_PRIMARY_HP),
+
+ /* All Apple entries are in codec SSIDs */
+ SND_PCI_QUIRK(0x106b, 0x00a0, "MacBookPro 3,1", ALC889_FIXUP_MBP_VREF),
+ SND_PCI_QUIRK(0x106b, 0x00a1, "Macbook", ALC889_FIXUP_MBP_VREF),
+ SND_PCI_QUIRK(0x106b, 0x00a4, "MacbookPro 4,1", ALC889_FIXUP_MBP_VREF),
+ SND_PCI_QUIRK(0x106b, 0x0c00, "Mac Pro", ALC889_FIXUP_MP11_VREF),
+ SND_PCI_QUIRK(0x106b, 0x1000, "iMac 24", ALC885_FIXUP_MACPRO_GPIO),
+ SND_PCI_QUIRK(0x106b, 0x2800, "AppleTV", ALC885_FIXUP_MACPRO_GPIO),
+ SND_PCI_QUIRK(0x106b, 0x2c00, "MacbookPro rev3", ALC889_FIXUP_MBP_VREF),
+ SND_PCI_QUIRK(0x106b, 0x3000, "iMac", ALC889_FIXUP_MBP_VREF),
+ SND_PCI_QUIRK(0x106b, 0x3200, "iMac 7,1 Aluminum", ALC882_FIXUP_EAPD),
+ SND_PCI_QUIRK(0x106b, 0x3400, "MacBookAir 1,1", ALC889_FIXUP_MBA11_VREF),
+ SND_PCI_QUIRK(0x106b, 0x3500, "MacBookAir 2,1", ALC889_FIXUP_MBA21_VREF),
+ SND_PCI_QUIRK(0x106b, 0x3600, "Macbook 3,1", ALC889_FIXUP_MBP_VREF),
+ SND_PCI_QUIRK(0x106b, 0x3800, "MacbookPro 4,1", ALC889_FIXUP_MBP_VREF),
+ SND_PCI_QUIRK(0x106b, 0x3e00, "iMac 24 Aluminum", ALC885_FIXUP_MACPRO_GPIO),
+ SND_PCI_QUIRK(0x106b, 0x3f00, "Macbook 5,1", ALC889_FIXUP_IMAC91_VREF),
+ SND_PCI_QUIRK(0x106b, 0x4000, "MacbookPro 5,1", ALC889_FIXUP_IMAC91_VREF),
+ SND_PCI_QUIRK(0x106b, 0x4100, "Macmini 3,1", ALC889_FIXUP_IMAC91_VREF),
+ SND_PCI_QUIRK(0x106b, 0x4200, "Mac Pro 5,1", ALC885_FIXUP_MACPRO_GPIO),
+ SND_PCI_QUIRK(0x106b, 0x4300, "iMac 9,1", ALC889_FIXUP_IMAC91_VREF),
+ SND_PCI_QUIRK(0x106b, 0x4600, "MacbookPro 5,2", ALC889_FIXUP_IMAC91_VREF),
+ SND_PCI_QUIRK(0x106b, 0x4900, "iMac 9,1 Aluminum", ALC889_FIXUP_IMAC91_VREF),
+ SND_PCI_QUIRK(0x106b, 0x4a00, "Macbook 5,2", ALC889_FIXUP_IMAC91_VREF),
+
+ SND_PCI_QUIRK(0x1071, 0x8258, "Evesham Voyaeger", ALC882_FIXUP_EAPD),
+ SND_PCI_QUIRK(0x1462, 0x7350, "MSI-7350", ALC889_FIXUP_CD),
+ SND_PCI_QUIRK_VENDOR(0x1462, "MSI", ALC882_FIXUP_GPIO3),
+ SND_PCI_QUIRK(0x1458, 0xa002, "Gigabyte EP45-DS3/Z87X-UD3H", ALC889_FIXUP_FRONT_HP_NO_PRESENCE),
+ SND_PCI_QUIRK(0x147b, 0x107a, "Abit AW9D-MAX", ALC882_FIXUP_ABIT_AW9D_MAX),
+ SND_PCI_QUIRK_VENDOR(0x1558, "Clevo laptop", ALC882_FIXUP_EAPD),
+ SND_PCI_QUIRK(0x161f, 0x2054, "Medion laptop", ALC883_FIXUP_EAPD),
+ SND_PCI_QUIRK(0x17aa, 0x3a0d, "Lenovo Y530", ALC882_FIXUP_LENOVO_Y530),
+ SND_PCI_QUIRK(0x8086, 0x0022, "DX58SO", ALC889_FIXUP_COEF),
+ {}
};
-static struct snd_pci_quirk alc882_fixup_tbl[] = {
- SND_PCI_QUIRK(0x1025, 0x0155, "Packard-Bell M5120", PINFIX_PB_M5210),
- SND_PCI_QUIRK(0x17aa, 0x3a0d, "Lenovo Y530", PINFIX_LENOVO_Y530),
- SND_PCI_QUIRK(0x147b, 0x107a, "Abit AW9D-MAX", PINFIX_ABIT_AW9D_MAX),
- SND_PCI_QUIRK(0x1025, 0x0296, "Acer Aspire 7736z", PINFIX_ACER_ASPIRE_7736),
- SND_PCI_QUIRK(0x1458, 0xa002, "Gigabyte", PINFIX_GIGABYTE_880GM),
+static const struct hda_model_fixup alc882_fixup_models[] = {
+ {.id = ALC882_FIXUP_ACER_ASPIRE_4930G, .name = "acer-aspire-4930g"},
+ {.id = ALC882_FIXUP_ACER_ASPIRE_8930G, .name = "acer-aspire-8930g"},
+ {.id = ALC883_FIXUP_ACER_EAPD, .name = "acer-aspire"},
+ {.id = ALC882_FIXUP_INV_DMIC, .name = "inv-dmic"},
+ {.id = ALC882_FIXUP_NO_PRIMARY_HP, .name = "no-primary-hp"},
{}
};
/*
* BIOS auto configuration
*/
-static int alc882_auto_create_input_ctls(struct hda_codec *codec,
- const struct auto_pin_cfg *cfg)
-{
- return alc_auto_create_input_ctls(codec, cfg, 0x0b, 0x23, 0x22);
-}
-
-static void alc882_auto_set_output_and_unmute(struct hda_codec *codec,
- hda_nid_t nid, int pin_type,
- hda_nid_t dac)
-{
- int idx;
-
- /* set as output */
- alc_set_pin_output(codec, nid, pin_type);
-
- if (dac == 0x25)
- idx = 4;
- else if (dac >= 0x02 && dac <= 0x05)
- idx = dac - 2;
- else
- return;
- snd_hda_codec_write(codec, nid, 0, AC_VERB_SET_CONNECT_SEL, idx);
-}
-
-static void alc882_auto_init_multi_out(struct hda_codec *codec)
-{
- struct alc_spec *spec = codec->spec;
- int i;
-
- for (i = 0; i <= HDA_SIDE; i++) {
- hda_nid_t nid = spec->autocfg.line_out_pins[i];
- int pin_type = get_pin_type(spec->autocfg.line_out_type);
- if (nid)
- alc882_auto_set_output_and_unmute(codec, nid, pin_type,
- spec->multiout.dac_nids[i]);
- }
-}
-
-static void alc882_auto_init_hp_out(struct hda_codec *codec)
-{
- struct alc_spec *spec = codec->spec;
- hda_nid_t pin, dac;
- int i;
-
- if (spec->autocfg.line_out_type != AUTO_PIN_HP_OUT) {
- for (i = 0; i < ARRAY_SIZE(spec->autocfg.hp_pins); i++) {
- pin = spec->autocfg.hp_pins[i];
- if (!pin)
- break;
- dac = spec->multiout.hp_nid;
- if (!dac)
- dac = spec->multiout.dac_nids[0]; /* to front */
- alc882_auto_set_output_and_unmute(codec, pin, PIN_HP, dac);
- }
- }
-
- if (spec->autocfg.line_out_type != AUTO_PIN_SPEAKER_OUT) {
- for (i = 0; i < ARRAY_SIZE(spec->autocfg.speaker_pins); i++) {
- pin = spec->autocfg.speaker_pins[i];
- if (!pin)
- break;
- dac = spec->multiout.extra_out_nid[0];
- if (!dac)
- dac = spec->multiout.dac_nids[0]; /* to front */
- alc882_auto_set_output_and_unmute(codec, pin, PIN_OUT, dac);
- }
- }
-}
-
-static void alc882_auto_init_analog_input(struct hda_codec *codec)
-{
- struct alc_spec *spec = codec->spec;
- struct auto_pin_cfg *cfg = &spec->autocfg;
- int i;
-
- for (i = 0; i < cfg->num_inputs; i++) {
- hda_nid_t nid = cfg->inputs[i].pin;
- alc_set_input_pin(codec, nid, cfg->inputs[i].type);
- if (get_wcaps(codec, nid) & AC_WCAP_OUT_AMP)
- snd_hda_codec_write(codec, nid, 0,
- AC_VERB_SET_AMP_GAIN_MUTE,
- AMP_OUT_MUTE);
- }
-}
-
-static void alc882_auto_init_input_src(struct hda_codec *codec)
-{
- struct alc_spec *spec = codec->spec;
- int c;
-
- for (c = 0; c < spec->num_adc_nids; c++) {
- hda_nid_t conn_list[HDA_MAX_NUM_INPUTS];
- hda_nid_t nid = spec->capsrc_nids[c];
- unsigned int mux_idx;
- const struct hda_input_mux *imux;
- int conns, mute, idx, item;
-
- conns = snd_hda_get_connections(codec, nid, conn_list,
- ARRAY_SIZE(conn_list));
- if (conns < 0)
- continue;
- mux_idx = c >= spec->num_mux_defs ? 0 : c;
- imux = &spec->input_mux[mux_idx];
- if (!imux->num_items && mux_idx > 0)
- imux = &spec->input_mux[0];
- for (idx = 0; idx < conns; idx++) {
- /* if the current connection is the selected one,
- * unmute it as default - otherwise mute it
- */
- mute = AMP_IN_MUTE(idx);
- for (item = 0; item < imux->num_items; item++) {
- if (imux->items[item].index == idx) {
- if (spec->cur_mux[c] == item)
- mute = AMP_IN_UNMUTE(idx);
- break;
- }
- }
- /* check if we have a selector or mixer
- * we could check for the widget type instead, but
- * just check for Amp-In presence (in case of mixer
- * without amp-in there is something wrong, this
- * function shouldn't be used or capsrc nid is wrong)
- */
- if (get_wcaps(codec, nid) & AC_WCAP_IN_AMP)
- snd_hda_codec_write(codec, nid, 0,
- AC_VERB_SET_AMP_GAIN_MUTE,
- mute);
- else if (mute != AMP_IN_MUTE(idx))
- snd_hda_codec_write(codec, nid, 0,
- AC_VERB_SET_CONNECT_SEL,
- idx);
- }
- }
-}
-
-/* add mic boosts if needed */
-static int alc_auto_add_mic_boost(struct hda_codec *codec)
-{
- struct alc_spec *spec = codec->spec;
- struct auto_pin_cfg *cfg = &spec->autocfg;
- int i, err;
- int type_idx = 0;
- hda_nid_t nid;
- const char *prev_label = NULL;
-
- for (i = 0; i < cfg->num_inputs; i++) {
- if (cfg->inputs[i].type > AUTO_PIN_MIC)
- break;
- nid = cfg->inputs[i].pin;
- if (get_wcaps(codec, nid) & AC_WCAP_IN_AMP) {
- const char *label;
- char boost_label[32];
-
- label = hda_get_autocfg_input_label(codec, cfg, i);
- if (prev_label && !strcmp(label, prev_label))
- type_idx++;
- else
- type_idx = 0;
- prev_label = label;
-
- snprintf(boost_label, sizeof(boost_label),
- "%s Boost Volume", label);
- err = add_control(spec, ALC_CTL_WIDGET_VOL,
- boost_label, type_idx,
- HDA_COMPOSE_AMP_VAL(nid, 3, 0, HDA_INPUT));
- if (err < 0)
- return err;
- }
- }
- return 0;
-}
-
/* almost identical with ALC880 parser... */
static int alc882_parse_auto_config(struct hda_codec *codec)
{
- struct alc_spec *spec = codec->spec;
- static hda_nid_t alc882_ignore[] = { 0x1d, 0 };
- int err;
-
- err = snd_hda_parse_pin_def_config(codec, &spec->autocfg,
- alc882_ignore);
- if (err < 0)
- return err;
- if (!spec->autocfg.line_outs)
- return 0; /* can't find valid BIOS pin config */
-
- err = alc880_auto_fill_dac_nids(spec, &spec->autocfg);
- if (err < 0)
- return err;
- err = alc880_auto_create_multi_out_ctls(spec, &spec->autocfg);
- if (err < 0)
- return err;
- err = alc880_auto_create_extra_out(spec, spec->autocfg.hp_pins[0],
- "Headphone");
- if (err < 0)
- return err;
- err = alc880_auto_create_extra_out(spec,
- spec->autocfg.speaker_pins[0],
- "Speaker");
- if (err < 0)
- return err;
- err = alc882_auto_create_input_ctls(codec, &spec->autocfg);
- if (err < 0)
- return err;
-
- spec->multiout.max_channels = spec->multiout.num_dacs * 2;
-
- alc_auto_parse_digital(codec);
-
- if (spec->kctls.list)
- add_mixer(spec, spec->kctls.list);
-
- add_verb(spec, alc883_auto_init_verbs);
- /* if ADC 0x07 is available, initialize it, too */
- if (get_wcaps_type(get_wcaps(codec, 0x07)) == AC_WID_AUD_IN)
- add_verb(spec, alc882_adc1_init_verbs);
-
- spec->num_mux_defs = 1;
- spec->input_mux = &spec->private_imux[0];
-
- alc_ssid_check(codec, 0x15, 0x1b, 0x14, 0);
-
- err = alc_auto_add_mic_boost(codec);
- if (err < 0)
- return err;
-
- return 1; /* config found */
-}
-
-/* additional initialization for auto-configuration model */
-static void alc882_auto_init(struct hda_codec *codec)
-{
- struct alc_spec *spec = codec->spec;
- alc882_auto_init_multi_out(codec);
- alc882_auto_init_hp_out(codec);
- alc882_auto_init_analog_input(codec);
- alc882_auto_init_input_src(codec);
- alc_auto_init_digital(codec);
- if (spec->unsol_event)
- alc_inithook(codec);
+ static const hda_nid_t alc882_ignore[] = { 0x1d, 0 };
+ static const hda_nid_t alc882_ssids[] = { 0x15, 0x1b, 0x14, 0 };
+ return alc_parse_auto_config(codec, alc882_ignore, alc882_ssids);
}
+/*
+ */
static int patch_alc882(struct hda_codec *codec)
{
struct alc_spec *spec;
- int err, board_config;
+ int err;
- spec = kzalloc(sizeof(*spec), GFP_KERNEL);
- if (spec == NULL)
- return -ENOMEM;
+ err = alc_alloc_spec(codec, 0x0b);
+ if (err < 0)
+ return err;
- codec->spec = spec;
+ spec = codec->spec;
switch (codec->vendor_id) {
case 0x10ec0882:
@@ -11015,3859 +2354,1883 @@ static int patch_alc882(struct hda_codec *codec)
break;
}
- board_config = snd_hda_check_board_config(codec, ALC882_MODEL_LAST,
- alc882_models,
- alc882_cfg_tbl);
-
- if (board_config < 0 || board_config >= ALC882_MODEL_LAST)
- board_config = snd_hda_check_board_codec_sid_config(codec,
- ALC882_MODEL_LAST, alc882_models, alc882_ssid_cfg_tbl);
-
- if (board_config < 0 || board_config >= ALC882_MODEL_LAST) {
- printk(KERN_INFO "hda_codec: %s: BIOS auto-probing.\n",
- codec->chip_name);
- board_config = ALC882_AUTO;
- }
-
- if (board_config == ALC882_AUTO) {
- alc_pick_fixup(codec, NULL, alc882_fixup_tbl, alc882_fixups);
- alc_apply_fixup(codec, ALC_FIXUP_ACT_PRE_PROBE);
- }
+ snd_hda_pick_fixup(codec, alc882_fixup_models, alc882_fixup_tbl,
+ alc882_fixups);
+ snd_hda_apply_fixup(codec, HDA_FIXUP_ACT_PRE_PROBE);
alc_auto_parse_customize_define(codec);
- if (board_config == ALC882_AUTO) {
- /* automatic parse from the BIOS config */
- err = alc882_parse_auto_config(codec);
- if (err < 0) {
- alc_free(codec);
- return err;
- } else if (!err) {
- printk(KERN_INFO
- "hda_codec: Cannot set up configuration "
- "from BIOS. Using base mode...\n");
- board_config = ALC882_3ST_DIG;
- }
- }
-
- if (has_cdefine_beep(codec)) {
- err = snd_hda_attach_beep_device(codec, 0x1);
- if (err < 0) {
- alc_free(codec);
- return err;
- }
- }
-
- if (board_config != ALC882_AUTO)
- setup_preset(codec, &alc882_presets[board_config]);
-
- spec->stream_analog_playback = &alc882_pcm_analog_playback;
- spec->stream_analog_capture = &alc882_pcm_analog_capture;
- /* FIXME: setup DAC5 */
- /*spec->stream_analog_alt_playback = &alc880_pcm_analog_alt_playback;*/
- spec->stream_analog_alt_capture = &alc880_pcm_analog_alt_capture;
-
- spec->stream_digital_playback = &alc882_pcm_digital_playback;
- spec->stream_digital_capture = &alc882_pcm_digital_capture;
-
- if (!spec->adc_nids && spec->input_mux) {
- int i, j;
- spec->num_adc_nids = 0;
- for (i = 0; i < ARRAY_SIZE(alc882_adc_nids); i++) {
- const struct hda_input_mux *imux = spec->input_mux;
- hda_nid_t cap;
- hda_nid_t items[16];
- hda_nid_t nid = alc882_adc_nids[i];
- unsigned int wcap = get_wcaps(codec, nid);
- /* get type */
- wcap = get_wcaps_type(wcap);
- if (wcap != AC_WID_AUD_IN)
- continue;
- spec->private_adc_nids[spec->num_adc_nids] = nid;
- err = snd_hda_get_connections(codec, nid, &cap, 1);
- if (err < 0)
- continue;
- err = snd_hda_get_connections(codec, cap, items,
- ARRAY_SIZE(items));
- if (err < 0)
- continue;
- for (j = 0; j < imux->num_items; j++)
- if (imux->items[j].index >= err)
- break;
- if (j < imux->num_items)
- continue;
- spec->private_capsrc_nids[spec->num_adc_nids] = cap;
- spec->num_adc_nids++;
- }
- spec->adc_nids = spec->private_adc_nids;
- spec->capsrc_nids = spec->private_capsrc_nids;
- }
-
- set_capture_mixer(codec);
-
if (has_cdefine_beep(codec))
- set_beep_amp(spec, 0x0b, 0x05, HDA_INPUT);
+ spec->gen.beep_nid = 0x01;
- alc_apply_fixup(codec, ALC_FIXUP_ACT_PROBE);
+ /* automatic parse from the BIOS config */
+ err = alc882_parse_auto_config(codec);
+ if (err < 0)
+ goto error;
- spec->vmaster_nid = 0x0c;
+ if (!spec->gen.no_analog && spec->gen.beep_nid)
+ set_beep_amp(spec, 0x0b, 0x05, HDA_INPUT);
codec->patch_ops = alc_patch_ops;
- if (board_config == ALC882_AUTO)
- spec->init_hook = alc882_auto_init;
- alc_init_jacks(codec);
-#ifdef CONFIG_SND_HDA_POWER_SAVE
- if (!spec->loopback.amplist)
- spec->loopback.amplist = alc882_loopbacks;
-#endif
+ snd_hda_apply_fixup(codec, HDA_FIXUP_ACT_PROBE);
return 0;
+
+ error:
+ alc_free(codec);
+ return err;
}
/*
* ALC262 support
*/
-
-#define ALC262_DIGOUT_NID ALC880_DIGOUT_NID
-#define ALC262_DIGIN_NID ALC880_DIGIN_NID
-
-#define alc262_dac_nids alc260_dac_nids
-#define alc262_adc_nids alc882_adc_nids
-#define alc262_adc_nids_alt alc882_adc_nids_alt
-#define alc262_capsrc_nids alc882_capsrc_nids
-#define alc262_capsrc_nids_alt alc882_capsrc_nids_alt
-
-#define alc262_modes alc260_modes
-#define alc262_capture_source alc882_capture_source
-
-static hda_nid_t alc262_dmic_adc_nids[1] = {
- /* ADC0 */
- 0x09
-};
-
-static hda_nid_t alc262_dmic_capsrc_nids[1] = { 0x22 };
-
-static struct snd_kcontrol_new alc262_base_mixer[] = {
- HDA_CODEC_VOLUME("Front Playback Volume", 0x0c, 0x0, HDA_OUTPUT),
- HDA_CODEC_MUTE("Front Playback Switch", 0x14, 0x0, HDA_OUTPUT),
- HDA_CODEC_VOLUME("CD Playback Volume", 0x0b, 0x04, HDA_INPUT),
- HDA_CODEC_MUTE("CD Playback Switch", 0x0b, 0x04, HDA_INPUT),
- HDA_CODEC_VOLUME("Line Playback Volume", 0x0b, 0x02, HDA_INPUT),
- HDA_CODEC_MUTE("Line Playback Switch", 0x0b, 0x02, HDA_INPUT),
- HDA_CODEC_VOLUME("Mic Playback Volume", 0x0b, 0x0, HDA_INPUT),
- HDA_CODEC_MUTE("Mic Playback Switch", 0x0b, 0x0, HDA_INPUT),
- HDA_CODEC_VOLUME("Mic Boost Volume", 0x18, 0, HDA_INPUT),
- HDA_CODEC_VOLUME("Front Mic Playback Volume", 0x0b, 0x01, HDA_INPUT),
- HDA_CODEC_MUTE("Front Mic Playback Switch", 0x0b, 0x01, HDA_INPUT),
- HDA_CODEC_VOLUME("Front Mic Boost Volume", 0x19, 0, HDA_INPUT),
- HDA_CODEC_VOLUME("Headphone Playback Volume", 0x0D, 0x0, HDA_OUTPUT),
- HDA_CODEC_MUTE("Headphone Playback Switch", 0x15, 0x0, HDA_OUTPUT),
- HDA_CODEC_VOLUME_MONO("Mono Playback Volume", 0x0e, 2, 0x0, HDA_OUTPUT),
- HDA_CODEC_MUTE_MONO("Mono Playback Switch", 0x16, 2, 0x0, HDA_OUTPUT),
- { } /* end */
-};
-
-/* update HP, line and mono-out pins according to the master switch */
-static void alc262_hp_master_update(struct hda_codec *codec)
-{
- struct alc_spec *spec = codec->spec;
- int val = spec->master_sw;
-
- /* HP & line-out */
- snd_hda_codec_write_cache(codec, 0x1b, 0,
- AC_VERB_SET_PIN_WIDGET_CONTROL,
- val ? PIN_HP : 0);
- snd_hda_codec_write_cache(codec, 0x15, 0,
- AC_VERB_SET_PIN_WIDGET_CONTROL,
- val ? PIN_HP : 0);
- /* mono (speaker) depending on the HP jack sense */
- val = val && !spec->jack_present;
- snd_hda_codec_write_cache(codec, 0x16, 0,
- AC_VERB_SET_PIN_WIDGET_CONTROL,
- val ? PIN_OUT : 0);
-}
-
-static void alc262_hp_bpc_automute(struct hda_codec *codec)
-{
- struct alc_spec *spec = codec->spec;
-
- spec->jack_present = snd_hda_jack_detect(codec, 0x1b);
- alc262_hp_master_update(codec);
-}
-
-static void alc262_hp_bpc_unsol_event(struct hda_codec *codec, unsigned int res)
-{
- if ((res >> 26) != ALC880_HP_EVENT)
- return;
- alc262_hp_bpc_automute(codec);
-}
-
-static void alc262_hp_wildwest_automute(struct hda_codec *codec)
-{
- struct alc_spec *spec = codec->spec;
-
- spec->jack_present = snd_hda_jack_detect(codec, 0x15);
- alc262_hp_master_update(codec);
-}
-
-static void alc262_hp_wildwest_unsol_event(struct hda_codec *codec,
- unsigned int res)
-{
- if ((res >> 26) != ALC880_HP_EVENT)
- return;
- alc262_hp_wildwest_automute(codec);
-}
-
-#define alc262_hp_master_sw_get alc260_hp_master_sw_get
-
-static int alc262_hp_master_sw_put(struct snd_kcontrol *kcontrol,
- struct snd_ctl_elem_value *ucontrol)
-{
- struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
- struct alc_spec *spec = codec->spec;
- int val = !!*ucontrol->value.integer.value;
-
- if (val == spec->master_sw)
- return 0;
- spec->master_sw = val;
- alc262_hp_master_update(codec);
- return 1;
-}
-
-#define ALC262_HP_MASTER_SWITCH \
- { \
- .iface = SNDRV_CTL_ELEM_IFACE_MIXER, \
- .name = "Master Playback Switch", \
- .info = snd_ctl_boolean_mono_info, \
- .get = alc262_hp_master_sw_get, \
- .put = alc262_hp_master_sw_put, \
- }, \
- { \
- .iface = NID_MAPPING, \
- .name = "Master Playback Switch", \
- .private_value = 0x15 | (0x16 << 8) | (0x1b << 16), \
- }
-
-
-static struct snd_kcontrol_new alc262_HP_BPC_mixer[] = {
- ALC262_HP_MASTER_SWITCH,
- HDA_CODEC_VOLUME("Front Playback Volume", 0x0c, 0x0, HDA_OUTPUT),
- HDA_CODEC_MUTE("Front Playback Switch", 0x15, 0x0, HDA_OUTPUT),
- HDA_CODEC_MUTE("Headphone Playback Switch", 0x1b, 0x0, HDA_OUTPUT),
- HDA_CODEC_VOLUME_MONO("Speaker Playback Volume", 0x0e, 2, 0x0,
- HDA_OUTPUT),
- HDA_CODEC_MUTE_MONO("Speaker Playback Switch", 0x16, 2, 0x0,
- HDA_OUTPUT),
- HDA_CODEC_VOLUME("Mic Playback Volume", 0x0b, 0x0, HDA_INPUT),
- HDA_CODEC_MUTE("Mic Playback Switch", 0x0b, 0x0, HDA_INPUT),
- HDA_CODEC_VOLUME("Mic Boost Volume", 0x18, 0, HDA_INPUT),
- HDA_CODEC_VOLUME("Front Mic Playback Volume", 0x0b, 0x01, HDA_INPUT),
- HDA_CODEC_MUTE("Front Mic Playback Switch", 0x0b, 0x01, HDA_INPUT),
- HDA_CODEC_VOLUME("Front Mic Boost Volume", 0x19, 0, HDA_INPUT),
- HDA_CODEC_VOLUME("Line Playback Volume", 0x0b, 0x02, HDA_INPUT),
- HDA_CODEC_MUTE("Line Playback Switch", 0x0b, 0x02, HDA_INPUT),
- HDA_CODEC_VOLUME("CD Playback Volume", 0x0b, 0x04, HDA_INPUT),
- HDA_CODEC_MUTE("CD Playback Switch", 0x0b, 0x04, HDA_INPUT),
- HDA_CODEC_VOLUME("AUX IN Playback Volume", 0x0b, 0x06, HDA_INPUT),
- HDA_CODEC_MUTE("AUX IN Playback Switch", 0x0b, 0x06, HDA_INPUT),
- { } /* end */
-};
-
-static struct snd_kcontrol_new alc262_HP_BPC_WildWest_mixer[] = {
- ALC262_HP_MASTER_SWITCH,
- HDA_CODEC_VOLUME("Front Playback Volume", 0x0c, 0x0, HDA_OUTPUT),
- HDA_CODEC_MUTE("Front Playback Switch", 0x1b, 0x0, HDA_OUTPUT),
- HDA_CODEC_VOLUME("Headphone Playback Volume", 0x0d, 0x0, HDA_OUTPUT),
- HDA_CODEC_MUTE("Headphone Playback Switch", 0x15, 0x0, HDA_OUTPUT),
- HDA_CODEC_VOLUME_MONO("Speaker Playback Volume", 0x0e, 2, 0x0,
- HDA_OUTPUT),
- HDA_CODEC_MUTE_MONO("Speaker Playback Switch", 0x16, 2, 0x0,
- HDA_OUTPUT),
- HDA_CODEC_VOLUME("Front Mic Playback Volume", 0x0b, 0x02, HDA_INPUT),
- HDA_CODEC_MUTE("Front Mic Playback Switch", 0x0b, 0x02, HDA_INPUT),
- HDA_CODEC_VOLUME("Front Mic Boost Volume", 0x1a, 0, HDA_INPUT),
- HDA_CODEC_VOLUME("Line Playback Volume", 0x0b, 0x01, HDA_INPUT),
- HDA_CODEC_MUTE("Line Playback Switch", 0x0b, 0x01, HDA_INPUT),
- HDA_CODEC_VOLUME("CD Playback Volume", 0x0b, 0x04, HDA_INPUT),
- HDA_CODEC_MUTE("CD Playback Switch", 0x0b, 0x04, HDA_INPUT),
- { } /* end */
-};
-
-static struct snd_kcontrol_new alc262_HP_BPC_WildWest_option_mixer[] = {
- HDA_CODEC_VOLUME("Rear Mic Playback Volume", 0x0b, 0x0, HDA_INPUT),
- HDA_CODEC_MUTE("Rear Mic Playback Switch", 0x0b, 0x0, HDA_INPUT),
- HDA_CODEC_VOLUME("Rear Mic Boost Volume", 0x18, 0, HDA_INPUT),
- { } /* end */
-};
-
-/* mute/unmute internal speaker according to the hp jack and mute state */
-static void alc262_hp_t5735_setup(struct hda_codec *codec)
+static int alc262_parse_auto_config(struct hda_codec *codec)
{
- struct alc_spec *spec = codec->spec;
-
- spec->autocfg.hp_pins[0] = 0x15;
- spec->autocfg.speaker_pins[0] = 0x14;
+ static const hda_nid_t alc262_ignore[] = { 0x1d, 0 };
+ static const hda_nid_t alc262_ssids[] = { 0x15, 0x1b, 0x14, 0 };
+ return alc_parse_auto_config(codec, alc262_ignore, alc262_ssids);
}
-static struct snd_kcontrol_new alc262_hp_t5735_mixer[] = {
- HDA_CODEC_VOLUME("Speaker Playback Volume", 0x0c, 0x0, HDA_OUTPUT),
- HDA_CODEC_MUTE("Speaker Playback Switch", 0x14, 0x0, HDA_OUTPUT),
- HDA_CODEC_VOLUME("Headphone Playback Volume", 0x0d, 0x0, HDA_OUTPUT),
- HDA_CODEC_MUTE("Headphone Playback Switch", 0x15, 0x0, HDA_OUTPUT),
- HDA_CODEC_VOLUME("Mic Playback Volume", 0x0b, 0x0, HDA_INPUT),
- HDA_CODEC_MUTE("Mic Playback Switch", 0x0b, 0x0, HDA_INPUT),
- HDA_CODEC_VOLUME("Mic Boost Volume", 0x18, 0, HDA_INPUT),
- { } /* end */
-};
-
-static struct hda_verb alc262_hp_t5735_verbs[] = {
- {0x14, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
- {0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP},
-
- {0x15, AC_VERB_SET_UNSOLICITED_ENABLE, ALC880_HP_EVENT | AC_USRSP_EN},
- { }
-};
-
-static struct snd_kcontrol_new alc262_hp_rp5700_mixer[] = {
- HDA_CODEC_VOLUME("Headphone Playback Volume", 0x0c, 0x0, HDA_OUTPUT),
- HDA_CODEC_MUTE("Headphone Playback Switch", 0x1b, 0x0, HDA_OUTPUT),
- HDA_CODEC_VOLUME("Speaker Playback Volume", 0x0e, 0x0, HDA_OUTPUT),
- HDA_CODEC_MUTE("Speaker Playback Switch", 0x16, 0x0, HDA_OUTPUT),
- HDA_CODEC_VOLUME("Line Playback Volume", 0x0b, 0x01, HDA_INPUT),
- HDA_CODEC_MUTE("Line Playback Switch", 0x0b, 0x01, HDA_INPUT),
- { } /* end */
+/*
+ * Pin config fixes
+ */
+enum {
+ ALC262_FIXUP_FSC_H270,
+ ALC262_FIXUP_FSC_S7110,
+ ALC262_FIXUP_HP_Z200,
+ ALC262_FIXUP_TYAN,
+ ALC262_FIXUP_LENOVO_3000,
+ ALC262_FIXUP_BENQ,
+ ALC262_FIXUP_BENQ_T31,
+ ALC262_FIXUP_INV_DMIC,
+ ALC262_FIXUP_INTEL_BAYLEYBAY,
+};
+
+static const struct hda_fixup alc262_fixups[] = {
+ [ALC262_FIXUP_FSC_H270] = {
+ .type = HDA_FIXUP_PINS,
+ .v.pins = (const struct hda_pintbl[]) {
+ { 0x14, 0x99130110 }, /* speaker */
+ { 0x15, 0x0221142f }, /* front HP */
+ { 0x1b, 0x0121141f }, /* rear HP */
+ { }
+ }
+ },
+ [ALC262_FIXUP_FSC_S7110] = {
+ .type = HDA_FIXUP_PINS,
+ .v.pins = (const struct hda_pintbl[]) {
+ { 0x15, 0x90170110 }, /* speaker */
+ { }
+ },
+ .chained = true,
+ .chain_id = ALC262_FIXUP_BENQ,
+ },
+ [ALC262_FIXUP_HP_Z200] = {
+ .type = HDA_FIXUP_PINS,
+ .v.pins = (const struct hda_pintbl[]) {
+ { 0x16, 0x99130120 }, /* internal speaker */
+ { }
+ }
+ },
+ [ALC262_FIXUP_TYAN] = {
+ .type = HDA_FIXUP_PINS,
+ .v.pins = (const struct hda_pintbl[]) {
+ { 0x14, 0x1993e1f0 }, /* int AUX */
+ { }
+ }
+ },
+ [ALC262_FIXUP_LENOVO_3000] = {
+ .type = HDA_FIXUP_PINCTLS,
+ .v.pins = (const struct hda_pintbl[]) {
+ { 0x19, PIN_VREF50 },
+ {}
+ },
+ .chained = true,
+ .chain_id = ALC262_FIXUP_BENQ,
+ },
+ [ALC262_FIXUP_BENQ] = {
+ .type = HDA_FIXUP_VERBS,
+ .v.verbs = (const struct hda_verb[]) {
+ { 0x20, AC_VERB_SET_COEF_INDEX, 0x07 },
+ { 0x20, AC_VERB_SET_PROC_COEF, 0x3070 },
+ {}
+ }
+ },
+ [ALC262_FIXUP_BENQ_T31] = {
+ .type = HDA_FIXUP_VERBS,
+ .v.verbs = (const struct hda_verb[]) {
+ { 0x20, AC_VERB_SET_COEF_INDEX, 0x07 },
+ { 0x20, AC_VERB_SET_PROC_COEF, 0x3050 },
+ {}
+ }
+ },
+ [ALC262_FIXUP_INV_DMIC] = {
+ .type = HDA_FIXUP_FUNC,
+ .v.func = alc_fixup_inv_dmic_0x12,
+ },
+ [ALC262_FIXUP_INTEL_BAYLEYBAY] = {
+ .type = HDA_FIXUP_FUNC,
+ .v.func = alc_fixup_no_depop_delay,
+ },
};
-static struct hda_verb alc262_hp_rp5700_verbs[] = {
- {0x1b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
- {0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
- {0x16, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
- {0x1b, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
- {0x19, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN},
- {0x1b, AC_VERB_SET_CONNECT_SEL, 0x00},
- {0x23, AC_VERB_SET_AMP_GAIN_MUTE, (0x7000 | (0x01 << 8))},
- {0x24, AC_VERB_SET_AMP_GAIN_MUTE, (0x7000 | (0x01 << 8))},
- {0x23, AC_VERB_SET_AMP_GAIN_MUTE, (0x7080 | (0x00 << 8))},
- {0x24, AC_VERB_SET_AMP_GAIN_MUTE, (0x7080 | (0x00 << 8))},
+static const struct snd_pci_quirk alc262_fixup_tbl[] = {
+ SND_PCI_QUIRK(0x103c, 0x170b, "HP Z200", ALC262_FIXUP_HP_Z200),
+ SND_PCI_QUIRK(0x10cf, 0x1397, "Fujitsu Lifebook S7110", ALC262_FIXUP_FSC_S7110),
+ SND_PCI_QUIRK(0x10cf, 0x142d, "Fujitsu Lifebook E8410", ALC262_FIXUP_BENQ),
+ SND_PCI_QUIRK(0x10f1, 0x2915, "Tyan Thunder n6650W", ALC262_FIXUP_TYAN),
+ SND_PCI_QUIRK(0x1734, 0x1147, "FSC Celsius H270", ALC262_FIXUP_FSC_H270),
+ SND_PCI_QUIRK(0x17aa, 0x384e, "Lenovo 3000", ALC262_FIXUP_LENOVO_3000),
+ SND_PCI_QUIRK(0x17ff, 0x0560, "Benq ED8", ALC262_FIXUP_BENQ),
+ SND_PCI_QUIRK(0x17ff, 0x058d, "Benq T31-16", ALC262_FIXUP_BENQ_T31),
+ SND_PCI_QUIRK(0x8086, 0x7270, "BayleyBay", ALC262_FIXUP_INTEL_BAYLEYBAY),
{}
};
-static struct hda_input_mux alc262_hp_rp5700_capture_source = {
- .num_items = 1,
- .items = {
- { "Line", 0x1 },
- },
+static const struct hda_model_fixup alc262_fixup_models[] = {
+ {.id = ALC262_FIXUP_INV_DMIC, .name = "inv-dmic"},
+ {}
};
-/* bind hp and internal speaker mute (with plug check) as master switch */
-static void alc262_hippo_master_update(struct hda_codec *codec)
+/*
+ */
+static int patch_alc262(struct hda_codec *codec)
{
- struct alc_spec *spec = codec->spec;
- hda_nid_t hp_nid = spec->autocfg.hp_pins[0];
- hda_nid_t line_nid = spec->autocfg.line_out_pins[0];
- hda_nid_t speaker_nid = spec->autocfg.speaker_pins[0];
- unsigned int mute;
-
- /* HP */
- mute = spec->master_sw ? 0 : HDA_AMP_MUTE;
- snd_hda_codec_amp_stereo(codec, hp_nid, HDA_OUTPUT, 0,
- HDA_AMP_MUTE, mute);
- /* mute internal speaker per jack sense */
- if (spec->jack_present)
- mute = HDA_AMP_MUTE;
- if (line_nid)
- snd_hda_codec_amp_stereo(codec, line_nid, HDA_OUTPUT, 0,
- HDA_AMP_MUTE, mute);
- if (speaker_nid && speaker_nid != line_nid)
- snd_hda_codec_amp_stereo(codec, speaker_nid, HDA_OUTPUT, 0,
- HDA_AMP_MUTE, mute);
-}
-
-#define alc262_hippo_master_sw_get alc262_hp_master_sw_get
+ struct alc_spec *spec;
+ int err;
-static int alc262_hippo_master_sw_put(struct snd_kcontrol *kcontrol,
- struct snd_ctl_elem_value *ucontrol)
-{
- struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
- struct alc_spec *spec = codec->spec;
- int val = !!*ucontrol->value.integer.value;
+ err = alc_alloc_spec(codec, 0x0b);
+ if (err < 0)
+ return err;
- if (val == spec->master_sw)
- return 0;
- spec->master_sw = val;
- alc262_hippo_master_update(codec);
- return 1;
-}
+ spec = codec->spec;
+ spec->gen.shared_mic_vref_pin = 0x18;
-#define ALC262_HIPPO_MASTER_SWITCH \
- { \
- .iface = SNDRV_CTL_ELEM_IFACE_MIXER, \
- .name = "Master Playback Switch", \
- .info = snd_ctl_boolean_mono_info, \
- .get = alc262_hippo_master_sw_get, \
- .put = alc262_hippo_master_sw_put, \
- }, \
- { \
- .iface = NID_MAPPING, \
- .name = "Master Playback Switch", \
- .subdevice = SUBDEV_HP(0) | (SUBDEV_LINE(0) << 8) | \
- (SUBDEV_SPEAKER(0) << 16), \
+#if 0
+ /* pshou 07/11/05 set a zero PCM sample to DAC when FIFO is
+ * under-run
+ */
+ {
+ int tmp;
+ snd_hda_codec_write(codec, 0x1a, 0, AC_VERB_SET_COEF_INDEX, 7);
+ tmp = snd_hda_codec_read(codec, 0x20, 0, AC_VERB_GET_PROC_COEF, 0);
+ snd_hda_codec_write(codec, 0x1a, 0, AC_VERB_SET_COEF_INDEX, 7);
+ snd_hda_codec_write(codec, 0x1a, 0, AC_VERB_SET_PROC_COEF, tmp | 0x80);
}
+#endif
+ alc_fix_pll_init(codec, 0x20, 0x0a, 10);
-static struct snd_kcontrol_new alc262_hippo_mixer[] = {
- ALC262_HIPPO_MASTER_SWITCH,
- HDA_CODEC_VOLUME("Speaker Playback Volume", 0x0c, 0x0, HDA_OUTPUT),
- HDA_CODEC_VOLUME("CD Playback Volume", 0x0b, 0x04, HDA_INPUT),
- HDA_CODEC_MUTE("CD Playback Switch", 0x0b, 0x04, HDA_INPUT),
- HDA_CODEC_VOLUME("Line Playback Volume", 0x0b, 0x02, HDA_INPUT),
- HDA_CODEC_MUTE("Line Playback Switch", 0x0b, 0x02, HDA_INPUT),
- HDA_CODEC_VOLUME("Mic Playback Volume", 0x0b, 0x0, HDA_INPUT),
- HDA_CODEC_MUTE("Mic Playback Switch", 0x0b, 0x0, HDA_INPUT),
- HDA_CODEC_VOLUME("Mic Boost Volume", 0x18, 0, HDA_INPUT),
- HDA_CODEC_VOLUME("Front Mic Playback Volume", 0x0b, 0x01, HDA_INPUT),
- HDA_CODEC_MUTE("Front Mic Playback Switch", 0x0b, 0x01, HDA_INPUT),
- HDA_CODEC_VOLUME("Front Mic Boost Volume", 0x19, 0, HDA_INPUT),
- HDA_CODEC_VOLUME("Headphone Playback Volume", 0x0d, 0x0, HDA_OUTPUT),
- { } /* end */
-};
-
-static struct snd_kcontrol_new alc262_hippo1_mixer[] = {
- HDA_CODEC_VOLUME("Master Playback Volume", 0x0c, 0x0, HDA_OUTPUT),
- ALC262_HIPPO_MASTER_SWITCH,
- HDA_CODEC_VOLUME("CD Playback Volume", 0x0b, 0x04, HDA_INPUT),
- HDA_CODEC_MUTE("CD Playback Switch", 0x0b, 0x04, HDA_INPUT),
- HDA_CODEC_VOLUME("Line Playback Volume", 0x0b, 0x02, HDA_INPUT),
- HDA_CODEC_MUTE("Line Playback Switch", 0x0b, 0x02, HDA_INPUT),
- HDA_CODEC_VOLUME("Mic Playback Volume", 0x0b, 0x0, HDA_INPUT),
- HDA_CODEC_MUTE("Mic Playback Switch", 0x0b, 0x0, HDA_INPUT),
- HDA_CODEC_VOLUME("Mic Boost Volume", 0x18, 0, HDA_INPUT),
- HDA_CODEC_VOLUME("Front Mic Playback Volume", 0x0b, 0x01, HDA_INPUT),
- HDA_CODEC_MUTE("Front Mic Playback Switch", 0x0b, 0x01, HDA_INPUT),
- HDA_CODEC_VOLUME("Front Mic Boost Volume", 0x19, 0, HDA_INPUT),
- { } /* end */
-};
-
-/* mute/unmute internal speaker according to the hp jack and mute state */
-static void alc262_hippo_automute(struct hda_codec *codec)
-{
- struct alc_spec *spec = codec->spec;
- hda_nid_t hp_nid = spec->autocfg.hp_pins[0];
-
- spec->jack_present = snd_hda_jack_detect(codec, hp_nid);
- alc262_hippo_master_update(codec);
-}
-
-static void alc262_hippo_unsol_event(struct hda_codec *codec, unsigned int res)
-{
- if ((res >> 26) != ALC880_HP_EVENT)
- return;
- alc262_hippo_automute(codec);
-}
-
-static void alc262_hippo_setup(struct hda_codec *codec)
-{
- struct alc_spec *spec = codec->spec;
-
- spec->autocfg.hp_pins[0] = 0x15;
- spec->autocfg.speaker_pins[0] = 0x14;
-}
-
-static void alc262_hippo1_setup(struct hda_codec *codec)
-{
- struct alc_spec *spec = codec->spec;
-
- spec->autocfg.hp_pins[0] = 0x1b;
- spec->autocfg.speaker_pins[0] = 0x14;
-}
-
+ snd_hda_pick_fixup(codec, alc262_fixup_models, alc262_fixup_tbl,
+ alc262_fixups);
+ snd_hda_apply_fixup(codec, HDA_FIXUP_ACT_PRE_PROBE);
-static struct snd_kcontrol_new alc262_sony_mixer[] = {
- HDA_CODEC_VOLUME("Master Playback Volume", 0x0c, 0x0, HDA_OUTPUT),
- ALC262_HIPPO_MASTER_SWITCH,
- HDA_CODEC_VOLUME("Mic Playback Volume", 0x0b, 0x0, HDA_INPUT),
- HDA_CODEC_MUTE("Mic Playback Switch", 0x0b, 0x0, HDA_INPUT),
- HDA_CODEC_VOLUME("ATAPI Mic Playback Volume", 0x0b, 0x01, HDA_INPUT),
- HDA_CODEC_MUTE("ATAPI Mic Playback Switch", 0x0b, 0x01, HDA_INPUT),
- { } /* end */
-};
+ alc_auto_parse_customize_define(codec);
-static struct snd_kcontrol_new alc262_benq_t31_mixer[] = {
- HDA_CODEC_VOLUME("Master Playback Volume", 0x0c, 0x0, HDA_OUTPUT),
- ALC262_HIPPO_MASTER_SWITCH,
- HDA_CODEC_MUTE("Headphone Playback Switch", 0x15, 0x0, HDA_OUTPUT),
- HDA_CODEC_VOLUME("Mic Playback Volume", 0x0b, 0x0, HDA_INPUT),
- HDA_CODEC_MUTE("Mic Playback Switch", 0x0b, 0x0, HDA_INPUT),
- HDA_CODEC_VOLUME("ATAPI Mic Playback Volume", 0x0b, 0x01, HDA_INPUT),
- HDA_CODEC_MUTE("ATAPI Mic Playback Switch", 0x0b, 0x01, HDA_INPUT),
- { } /* end */
-};
+ if (has_cdefine_beep(codec))
+ spec->gen.beep_nid = 0x01;
-static struct snd_kcontrol_new alc262_tyan_mixer[] = {
- HDA_CODEC_VOLUME("Master Playback Volume", 0x0c, 0x0, HDA_OUTPUT),
- HDA_BIND_MUTE("Master Playback Switch", 0x0c, 2, HDA_INPUT),
- HDA_CODEC_VOLUME("Aux Playback Volume", 0x0b, 0x06, HDA_INPUT),
- HDA_CODEC_MUTE("Aux Playback Switch", 0x0b, 0x06, HDA_INPUT),
- HDA_CODEC_VOLUME("Line Playback Volume", 0x0b, 0x02, HDA_INPUT),
- HDA_CODEC_MUTE("Line Playback Switch", 0x0b, 0x02, HDA_INPUT),
- HDA_CODEC_VOLUME("Mic Playback Volume", 0x0b, 0x0, HDA_INPUT),
- HDA_CODEC_MUTE("Mic Playback Switch", 0x0b, 0x0, HDA_INPUT),
- HDA_CODEC_VOLUME("Mic Boost Volume", 0x18, 0, HDA_INPUT),
- HDA_CODEC_VOLUME("Front Mic Playback Volume", 0x0b, 0x01, HDA_INPUT),
- HDA_CODEC_MUTE("Front Mic Playback Switch", 0x0b, 0x01, HDA_INPUT),
- HDA_CODEC_VOLUME("Front Mic Boost Volume", 0x19, 0, HDA_INPUT),
- { } /* end */
-};
+ /* automatic parse from the BIOS config */
+ err = alc262_parse_auto_config(codec);
+ if (err < 0)
+ goto error;
-static struct hda_verb alc262_tyan_verbs[] = {
- /* Headphone automute */
- {0x1b, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | ALC880_HP_EVENT},
- {0x1b, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP},
- {0x15, AC_VERB_SET_CONNECT_SEL, 0x00},
+ if (!spec->gen.no_analog && spec->gen.beep_nid)
+ set_beep_amp(spec, 0x0b, 0x05, HDA_INPUT);
- /* P11 AUX_IN, white 4-pin connector */
- {0x14, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN},
- {0x14, AC_VERB_SET_CONFIG_DEFAULT_BYTES_1, 0xe1},
- {0x14, AC_VERB_SET_CONFIG_DEFAULT_BYTES_2, 0x93},
- {0x14, AC_VERB_SET_CONFIG_DEFAULT_BYTES_3, 0x19},
+ codec->patch_ops = alc_patch_ops;
+ spec->shutup = alc_eapd_shutup;
- {}
-};
+ snd_hda_apply_fixup(codec, HDA_FIXUP_ACT_PROBE);
-/* unsolicited event for HP jack sensing */
-static void alc262_tyan_setup(struct hda_codec *codec)
-{
- struct alc_spec *spec = codec->spec;
+ return 0;
- spec->autocfg.hp_pins[0] = 0x1b;
- spec->autocfg.speaker_pins[0] = 0x15;
+ error:
+ alc_free(codec);
+ return err;
}
-
-#define alc262_capture_mixer alc882_capture_mixer
-#define alc262_capture_alt_mixer alc882_capture_alt_mixer
-
/*
- * generic initialization of ADC, input mixers and output mixers
+ * ALC268
*/
-static struct hda_verb alc262_init_verbs[] = {
- /*
- * Unmute ADC0-2 and set the default input to mic-in
- */
- {0x07, AC_VERB_SET_CONNECT_SEL, 0x00},
- {0x07, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
- {0x08, AC_VERB_SET_CONNECT_SEL, 0x00},
- {0x08, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
- {0x09, AC_VERB_SET_CONNECT_SEL, 0x00},
- {0x09, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
-
- /* Mute input amps (CD, Line In, Mic 1 & Mic 2) of the analog-loopback
- * mixer widget
- * Note: PASD motherboards uses the Line In 2 as the input for
- * front panel mic (mic 2)
- */
- /* Amp Indices: Mic1 = 0, Mic2 = 1, Line1 = 2, Line2 = 3, CD = 4 */
- {0x0b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
- {0x0b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
- {0x0b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(2)},
- {0x0b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(3)},
- {0x0b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(4)},
-
- /*
- * Set up output mixers (0x0c - 0x0e)
- */
- /* set vol=0 to output mixers */
- {0x0c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
- {0x0d, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
- {0x0e, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
- /* set up input amps for analog loopback */
- /* Amp Indices: DAC = 0, mixer = 1 */
- {0x0c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
- {0x0c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
- {0x0d, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
- {0x0d, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
- {0x0e, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
- {0x0e, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
-
- {0x14, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x40},
- {0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, 0xc0},
- {0x16, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x40},
- {0x18, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x24},
- {0x1a, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x20},
- {0x1c, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x20},
-
- {0x14, AC_VERB_SET_AMP_GAIN_MUTE, 0x0000},
- {0x15, AC_VERB_SET_AMP_GAIN_MUTE, 0x0000},
- {0x16, AC_VERB_SET_AMP_GAIN_MUTE, 0x0000},
- {0x18, AC_VERB_SET_AMP_GAIN_MUTE, 0x0000},
- {0x1a, AC_VERB_SET_AMP_GAIN_MUTE, 0x0000},
-
- {0x14, AC_VERB_SET_CONNECT_SEL, 0x00},
- {0x15, AC_VERB_SET_CONNECT_SEL, 0x01},
-
- /* FIXME: use matrix-type input source selection */
- /* Mixer elements: 0x18, 19, 1a, 1b, 1c, 1d, 14, 15, 16, 17, 0b */
- /* Input mixer1: unmute Mic, F-Mic, Line, CD inputs */
- {0x24, AC_VERB_SET_AMP_GAIN_MUTE, (0x7000 | (0x00 << 8))},
- {0x24, AC_VERB_SET_AMP_GAIN_MUTE, (0x7080 | (0x03 << 8))},
- {0x24, AC_VERB_SET_AMP_GAIN_MUTE, (0x7080 | (0x02 << 8))},
- {0x24, AC_VERB_SET_AMP_GAIN_MUTE, (0x7080 | (0x04 << 8))},
- /* Input mixer2 */
- {0x23, AC_VERB_SET_AMP_GAIN_MUTE, (0x7000 | (0x00 << 8))},
- {0x23, AC_VERB_SET_AMP_GAIN_MUTE, (0x7080 | (0x03 << 8))},
- {0x23, AC_VERB_SET_AMP_GAIN_MUTE, (0x7080 | (0x02 << 8))},
- {0x23, AC_VERB_SET_AMP_GAIN_MUTE, (0x7080 | (0x04 << 8))},
- /* Input mixer3 */
- {0x22, AC_VERB_SET_AMP_GAIN_MUTE, (0x7000 | (0x00 << 8))},
- {0x22, AC_VERB_SET_AMP_GAIN_MUTE, (0x7080 | (0x03 << 8))},
- {0x22, AC_VERB_SET_AMP_GAIN_MUTE, (0x7080 | (0x02 << 8))},
- {0x22, AC_VERB_SET_AMP_GAIN_MUTE, (0x7080 | (0x04 << 8))},
+/* bind Beep switches of both NID 0x0f and 0x10 */
+static const struct hda_bind_ctls alc268_bind_beep_sw = {
+ .ops = &snd_hda_bind_sw,
+ .values = {
+ HDA_COMPOSE_AMP_VAL(0x0f, 3, 1, HDA_INPUT),
+ HDA_COMPOSE_AMP_VAL(0x10, 3, 1, HDA_INPUT),
+ 0
+ },
+};
+static const struct snd_kcontrol_new alc268_beep_mixer[] = {
+ HDA_CODEC_VOLUME("Beep Playback Volume", 0x1d, 0x0, HDA_INPUT),
+ HDA_BIND_SW("Beep Playback Switch", &alc268_bind_beep_sw),
{ }
};
-static struct hda_verb alc262_eapd_verbs[] = {
- {0x14, AC_VERB_SET_EAPD_BTLENABLE, 2},
- {0x15, AC_VERB_SET_EAPD_BTLENABLE, 2},
+/* set PCBEEP vol = 0, mute connections */
+static const struct hda_verb alc268_beep_init_verbs[] = {
+ {0x1d, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
+ {0x0f, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
+ {0x10, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
{ }
};
-static struct hda_verb alc262_hippo1_unsol_verbs[] = {
- {0x1b, AC_VERB_SET_PIN_WIDGET_CONTROL, 0xc0},
- {0x1b, AC_VERB_SET_CONNECT_SEL, 0x00},
- {0x1b, AC_VERB_SET_AMP_GAIN_MUTE, 0x0000},
-
- {0x1b, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | ALC880_HP_EVENT},
- {0x1b, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP},
- {}
+enum {
+ ALC268_FIXUP_INV_DMIC,
+ ALC268_FIXUP_HP_EAPD,
+ ALC268_FIXUP_SPDIF,
};
-static struct hda_verb alc262_sony_unsol_verbs[] = {
- {0x14, AC_VERB_SET_PIN_WIDGET_CONTROL, 0xc0},
- {0x15, AC_VERB_SET_CONNECT_SEL, 0x00},
- {0x19, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x24}, // Front Mic
-
- {0x15, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | ALC880_HP_EVENT},
- {0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP},
- {}
+static const struct hda_fixup alc268_fixups[] = {
+ [ALC268_FIXUP_INV_DMIC] = {
+ .type = HDA_FIXUP_FUNC,
+ .v.func = alc_fixup_inv_dmic_0x12,
+ },
+ [ALC268_FIXUP_HP_EAPD] = {
+ .type = HDA_FIXUP_VERBS,
+ .v.verbs = (const struct hda_verb[]) {
+ {0x15, AC_VERB_SET_EAPD_BTLENABLE, 0},
+ {}
+ }
+ },
+ [ALC268_FIXUP_SPDIF] = {
+ .type = HDA_FIXUP_PINS,
+ .v.pins = (const struct hda_pintbl[]) {
+ { 0x1e, 0x014b1180 }, /* enable SPDIF out */
+ {}
+ }
+ },
};
-static struct snd_kcontrol_new alc262_toshiba_s06_mixer[] = {
- HDA_CODEC_VOLUME("Speaker Playback Volume", 0x0c, 0x0, HDA_OUTPUT),
- HDA_CODEC_MUTE("Speaker Playback Switch", 0x14, 0x0, HDA_OUTPUT),
- HDA_CODEC_MUTE("Headphone Playback Switch", 0x15, 0x0, HDA_OUTPUT),
- HDA_CODEC_VOLUME("Mic Playback Volume", 0x0b, 0x0, HDA_INPUT),
- HDA_CODEC_MUTE("Mic Playback Switch", 0x0b, 0x0, HDA_INPUT),
- { } /* end */
+static const struct hda_model_fixup alc268_fixup_models[] = {
+ {.id = ALC268_FIXUP_INV_DMIC, .name = "inv-dmic"},
+ {.id = ALC268_FIXUP_HP_EAPD, .name = "hp-eapd"},
+ {}
};
-static struct hda_verb alc262_toshiba_s06_verbs[] = {
- {0x12, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN},
- {0x14, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
- {0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP},
- {0x15, AC_VERB_SET_CONNECT_SEL, 0x00},
- {0x22, AC_VERB_SET_CONNECT_SEL, 0x09},
- {0x18, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x24},
- {0x18, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | ALC880_MIC_EVENT},
- {0x15, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | ALC880_HP_EVENT},
+static const struct snd_pci_quirk alc268_fixup_tbl[] = {
+ SND_PCI_QUIRK(0x1025, 0x0139, "Acer TravelMate 6293", ALC268_FIXUP_SPDIF),
+ SND_PCI_QUIRK(0x1025, 0x015b, "Acer AOA 150 (ZG5)", ALC268_FIXUP_INV_DMIC),
+ /* below is codec SSID since multiple Toshiba laptops have the
+ * same PCI SSID 1179:ff00
+ */
+ SND_PCI_QUIRK(0x1179, 0xff06, "Toshiba P200", ALC268_FIXUP_HP_EAPD),
{}
};
-static void alc262_toshiba_s06_setup(struct hda_codec *codec)
-{
- struct alc_spec *spec = codec->spec;
-
- spec->autocfg.hp_pins[0] = 0x15;
- spec->autocfg.speaker_pins[0] = 0x14;
- spec->ext_mic.pin = 0x18;
- spec->ext_mic.mux_idx = 0;
- spec->int_mic.pin = 0x12;
- spec->int_mic.mux_idx = 9;
- spec->auto_mic = 1;
-}
-
/*
- * nec model
- * 0x15 = headphone
- * 0x16 = internal speaker
- * 0x18 = external mic
+ * BIOS auto configuration
*/
-
-static struct snd_kcontrol_new alc262_nec_mixer[] = {
- HDA_CODEC_VOLUME_MONO("Speaker Playback Volume", 0x0e, 1, 0x0, HDA_OUTPUT),
- HDA_CODEC_MUTE_MONO("Speaker Playback Switch", 0x16, 0, 0x0, HDA_OUTPUT),
-
- HDA_CODEC_VOLUME("Mic Playback Volume", 0x0b, 0x0, HDA_INPUT),
- HDA_CODEC_MUTE("Mic Playback Switch", 0x0b, 0x0, HDA_INPUT),
- HDA_CODEC_VOLUME("Mic Boost Volume", 0x18, 0, HDA_INPUT),
-
- HDA_CODEC_VOLUME("Headphone Playback Volume", 0x0d, 0x0, HDA_OUTPUT),
- HDA_CODEC_MUTE("Headphone Playback Switch", 0x15, 0x0, HDA_OUTPUT),
- { } /* end */
-};
-
-static struct hda_verb alc262_nec_verbs[] = {
- /* Unmute Speaker */
- {0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
-
- /* Headphone */
- {0x15, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | ALC880_HP_EVENT},
- {0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
-
- /* External mic to headphone */
- {0x0d, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
- /* External mic to speaker */
- {0x0e, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
- {}
-};
+static int alc268_parse_auto_config(struct hda_codec *codec)
+{
+ static const hda_nid_t alc268_ssids[] = { 0x15, 0x1b, 0x14, 0 };
+ return alc_parse_auto_config(codec, NULL, alc268_ssids);
+}
/*
- * fujitsu model
- * 0x14 = headphone/spdif-out, 0x15 = internal speaker,
- * 0x1b = port replicator headphone out
*/
+static int patch_alc268(struct hda_codec *codec)
+{
+ struct alc_spec *spec;
+ int err;
-#define ALC_HP_EVENT 0x37
-
-static struct hda_verb alc262_fujitsu_unsol_verbs[] = {
- {0x14, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | ALC_HP_EVENT},
- {0x14, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP},
- {0x1b, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | ALC_HP_EVENT},
- {0x1b, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP},
- {}
-};
-
-static struct hda_verb alc262_lenovo_3000_unsol_verbs[] = {
- {0x1b, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | ALC_HP_EVENT},
- {0x1b, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP},
- {}
-};
-
-static struct hda_verb alc262_lenovo_3000_init_verbs[] = {
- /* Front Mic pin: input vref at 50% */
- {0x19, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF50},
- {0x19, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
- {}
-};
-
-static struct hda_input_mux alc262_fujitsu_capture_source = {
- .num_items = 3,
- .items = {
- { "Mic", 0x0 },
- { "Internal Mic", 0x1 },
- { "CD", 0x4 },
- },
-};
+ /* ALC268 has no aa-loopback mixer */
+ err = alc_alloc_spec(codec, 0);
+ if (err < 0)
+ return err;
-static struct hda_input_mux alc262_HP_capture_source = {
- .num_items = 5,
- .items = {
- { "Mic", 0x0 },
- { "Front Mic", 0x1 },
- { "Line", 0x2 },
- { "CD", 0x4 },
- { "AUX IN", 0x6 },
- },
-};
+ spec = codec->spec;
+ spec->gen.beep_nid = 0x01;
-static struct hda_input_mux alc262_HP_D7000_capture_source = {
- .num_items = 4,
- .items = {
- { "Mic", 0x0 },
- { "Front Mic", 0x2 },
- { "Line", 0x1 },
- { "CD", 0x4 },
- },
-};
+ snd_hda_pick_fixup(codec, alc268_fixup_models, alc268_fixup_tbl, alc268_fixups);
+ snd_hda_apply_fixup(codec, HDA_FIXUP_ACT_PRE_PROBE);
-/* mute/unmute internal speaker according to the hp jacks and mute state */
-static void alc262_fujitsu_automute(struct hda_codec *codec, int force)
-{
- struct alc_spec *spec = codec->spec;
- unsigned int mute;
+ /* automatic parse from the BIOS config */
+ err = alc268_parse_auto_config(codec);
+ if (err < 0)
+ goto error;
- if (force || !spec->sense_updated) {
- spec->jack_present = snd_hda_jack_detect(codec, 0x14) ||
- snd_hda_jack_detect(codec, 0x1b);
- spec->sense_updated = 1;
+ if (err > 0 && !spec->gen.no_analog &&
+ spec->gen.autocfg.speaker_pins[0] != 0x1d) {
+ add_mixer(spec, alc268_beep_mixer);
+ snd_hda_add_verbs(codec, alc268_beep_init_verbs);
+ if (!query_amp_caps(codec, 0x1d, HDA_INPUT))
+ /* override the amp caps for beep generator */
+ snd_hda_override_amp_caps(codec, 0x1d, HDA_INPUT,
+ (0x0c << AC_AMPCAP_OFFSET_SHIFT) |
+ (0x0c << AC_AMPCAP_NUM_STEPS_SHIFT) |
+ (0x07 << AC_AMPCAP_STEP_SIZE_SHIFT) |
+ (0 << AC_AMPCAP_MUTE_SHIFT));
}
- /* unmute internal speaker only if both HPs are unplugged and
- * master switch is on
- */
- if (spec->jack_present)
- mute = HDA_AMP_MUTE;
- else
- mute = snd_hda_codec_amp_read(codec, 0x14, 0, HDA_OUTPUT, 0);
- snd_hda_codec_amp_stereo(codec, 0x15, HDA_OUTPUT, 0,
- HDA_AMP_MUTE, mute);
-}
-/* unsolicited event for HP jack sensing */
-static void alc262_fujitsu_unsol_event(struct hda_codec *codec,
- unsigned int res)
-{
- if ((res >> 26) != ALC_HP_EVENT)
- return;
- alc262_fujitsu_automute(codec, 1);
-}
+ codec->patch_ops = alc_patch_ops;
+ spec->shutup = alc_eapd_shutup;
-static void alc262_fujitsu_init_hook(struct hda_codec *codec)
-{
- alc262_fujitsu_automute(codec, 1);
-}
+ snd_hda_apply_fixup(codec, HDA_FIXUP_ACT_PROBE);
-/* bind volumes of both NID 0x0c and 0x0d */
-static struct hda_bind_ctls alc262_fujitsu_bind_master_vol = {
- .ops = &snd_hda_bind_vol,
- .values = {
- HDA_COMPOSE_AMP_VAL(0x0c, 3, 0, HDA_OUTPUT),
- HDA_COMPOSE_AMP_VAL(0x0d, 3, 0, HDA_OUTPUT),
- 0
- },
-};
-
-/* mute/unmute internal speaker according to the hp jack and mute state */
-static void alc262_lenovo_3000_automute(struct hda_codec *codec, int force)
-{
- struct alc_spec *spec = codec->spec;
- unsigned int mute;
+ return 0;
- if (force || !spec->sense_updated) {
- spec->jack_present = snd_hda_jack_detect(codec, 0x1b);
- spec->sense_updated = 1;
- }
- if (spec->jack_present) {
- /* mute internal speaker */
- snd_hda_codec_amp_stereo(codec, 0x14, HDA_OUTPUT, 0,
- HDA_AMP_MUTE, HDA_AMP_MUTE);
- snd_hda_codec_amp_stereo(codec, 0x16, HDA_OUTPUT, 0,
- HDA_AMP_MUTE, HDA_AMP_MUTE);
- } else {
- /* unmute internal speaker if necessary */
- mute = snd_hda_codec_amp_read(codec, 0x1b, 0, HDA_OUTPUT, 0);
- snd_hda_codec_amp_stereo(codec, 0x14, HDA_OUTPUT, 0,
- HDA_AMP_MUTE, mute);
- snd_hda_codec_amp_stereo(codec, 0x16, HDA_OUTPUT, 0,
- HDA_AMP_MUTE, mute);
- }
+ error:
+ alc_free(codec);
+ return err;
}
-/* unsolicited event for HP jack sensing */
-static void alc262_lenovo_3000_unsol_event(struct hda_codec *codec,
- unsigned int res)
-{
- if ((res >> 26) != ALC_HP_EVENT)
- return;
- alc262_lenovo_3000_automute(codec, 1);
-}
+/*
+ * ALC269
+ */
-static int amp_stereo_mute_update(struct hda_codec *codec, hda_nid_t nid,
- int dir, int idx, long *valp)
+static int playback_pcm_open(struct hda_pcm_stream *hinfo,
+ struct hda_codec *codec,
+ struct snd_pcm_substream *substream)
{
- int i, change = 0;
-
- for (i = 0; i < 2; i++, valp++)
- change |= snd_hda_codec_amp_update(codec, nid, i, dir, idx,
- HDA_AMP_MUTE,
- *valp ? 0 : HDA_AMP_MUTE);
- return change;
+ struct hda_gen_spec *spec = codec->spec;
+ return snd_hda_multi_out_analog_open(codec, &spec->multiout, substream,
+ hinfo);
}
-/* bind hp and internal speaker mute (with plug check) */
-static int alc262_fujitsu_master_sw_put(struct snd_kcontrol *kcontrol,
- struct snd_ctl_elem_value *ucontrol)
+static int playback_pcm_prepare(struct hda_pcm_stream *hinfo,
+ struct hda_codec *codec,
+ unsigned int stream_tag,
+ unsigned int format,
+ struct snd_pcm_substream *substream)
{
- struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
- long *valp = ucontrol->value.integer.value;
- int change;
-
- change = amp_stereo_mute_update(codec, 0x14, HDA_OUTPUT, 0, valp);
- change |= amp_stereo_mute_update(codec, 0x1b, HDA_OUTPUT, 0, valp);
- if (change)
- alc262_fujitsu_automute(codec, 0);
- return change;
+ struct hda_gen_spec *spec = codec->spec;
+ return snd_hda_multi_out_analog_prepare(codec, &spec->multiout,
+ stream_tag, format, substream);
}
-static struct snd_kcontrol_new alc262_fujitsu_mixer[] = {
- HDA_BIND_VOL("Master Playback Volume", &alc262_fujitsu_bind_master_vol),
- {
- .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
- .name = "Master Playback Switch",
- .subdevice = HDA_SUBDEV_AMP_FLAG,
- .info = snd_hda_mixer_amp_switch_info,
- .get = snd_hda_mixer_amp_switch_get,
- .put = alc262_fujitsu_master_sw_put,
- .private_value = HDA_COMPOSE_AMP_VAL(0x14, 3, 0, HDA_OUTPUT),
- },
- {
- .iface = NID_MAPPING,
- .name = "Master Playback Switch",
- .private_value = 0x1b,
- },
- HDA_CODEC_VOLUME("CD Playback Volume", 0x0b, 0x04, HDA_INPUT),
- HDA_CODEC_MUTE("CD Playback Switch", 0x0b, 0x04, HDA_INPUT),
- HDA_CODEC_VOLUME("Mic Boost Volume", 0x18, 0, HDA_INPUT),
- HDA_CODEC_VOLUME("Mic Playback Volume", 0x0b, 0x0, HDA_INPUT),
- HDA_CODEC_MUTE("Mic Playback Switch", 0x0b, 0x0, HDA_INPUT),
- HDA_CODEC_VOLUME("Internal Mic Boost Volume", 0x19, 0, HDA_INPUT),
- HDA_CODEC_VOLUME("Internal Mic Playback Volume", 0x0b, 0x1, HDA_INPUT),
- HDA_CODEC_MUTE("Internal Mic Playback Switch", 0x0b, 0x1, HDA_INPUT),
- { } /* end */
-};
-
-/* bind hp and internal speaker mute (with plug check) */
-static int alc262_lenovo_3000_master_sw_put(struct snd_kcontrol *kcontrol,
- struct snd_ctl_elem_value *ucontrol)
+static int playback_pcm_cleanup(struct hda_pcm_stream *hinfo,
+ struct hda_codec *codec,
+ struct snd_pcm_substream *substream)
{
- struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
- long *valp = ucontrol->value.integer.value;
- int change;
-
- change = amp_stereo_mute_update(codec, 0x1b, HDA_OUTPUT, 0, valp);
- if (change)
- alc262_lenovo_3000_automute(codec, 0);
- return change;
+ struct hda_gen_spec *spec = codec->spec;
+ return snd_hda_multi_out_analog_cleanup(codec, &spec->multiout);
}
-static struct snd_kcontrol_new alc262_lenovo_3000_mixer[] = {
- HDA_BIND_VOL("Master Playback Volume", &alc262_fujitsu_bind_master_vol),
- {
- .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
- .name = "Master Playback Switch",
- .subdevice = HDA_SUBDEV_AMP_FLAG,
- .info = snd_hda_mixer_amp_switch_info,
- .get = snd_hda_mixer_amp_switch_get,
- .put = alc262_lenovo_3000_master_sw_put,
- .private_value = HDA_COMPOSE_AMP_VAL(0x1b, 3, 0, HDA_OUTPUT),
+static const struct hda_pcm_stream alc269_44k_pcm_analog_playback = {
+ .substreams = 1,
+ .channels_min = 2,
+ .channels_max = 8,
+ .rates = SNDRV_PCM_RATE_44100, /* fixed rate */
+ /* NID is set in alc_build_pcms */
+ .ops = {
+ .open = playback_pcm_open,
+ .prepare = playback_pcm_prepare,
+ .cleanup = playback_pcm_cleanup
},
- HDA_CODEC_VOLUME("CD Playback Volume", 0x0b, 0x04, HDA_INPUT),
- HDA_CODEC_MUTE("CD Playback Switch", 0x0b, 0x04, HDA_INPUT),
- HDA_CODEC_VOLUME("Mic Boost Volume", 0x18, 0, HDA_INPUT),
- HDA_CODEC_VOLUME("Mic Playback Volume", 0x0b, 0x0, HDA_INPUT),
- HDA_CODEC_MUTE("Mic Playback Switch", 0x0b, 0x0, HDA_INPUT),
- HDA_CODEC_VOLUME("Internal Mic Boost Volume", 0x19, 0, HDA_INPUT),
- HDA_CODEC_VOLUME("Internal Mic Playback Volume", 0x0b, 0x1, HDA_INPUT),
- HDA_CODEC_MUTE("Internal Mic Playback Switch", 0x0b, 0x1, HDA_INPUT),
- { } /* end */
-};
-
-static struct snd_kcontrol_new alc262_toshiba_rx1_mixer[] = {
- HDA_BIND_VOL("Master Playback Volume", &alc262_fujitsu_bind_master_vol),
- ALC262_HIPPO_MASTER_SWITCH,
- HDA_CODEC_VOLUME("Mic Playback Volume", 0x0b, 0x0, HDA_INPUT),
- HDA_CODEC_MUTE("Mic Playback Switch", 0x0b, 0x0, HDA_INPUT),
- HDA_CODEC_VOLUME("Mic Boost Volume", 0x18, 0, HDA_INPUT),
- HDA_CODEC_VOLUME("Front Mic Playback Volume", 0x0b, 0x01, HDA_INPUT),
- HDA_CODEC_MUTE("Front Mic Playback Switch", 0x0b, 0x01, HDA_INPUT),
- HDA_CODEC_VOLUME("Front Mic Boost Volume", 0x19, 0, HDA_INPUT),
- { } /* end */
-};
-
-/* additional init verbs for Benq laptops */
-static struct hda_verb alc262_EAPD_verbs[] = {
- {0x20, AC_VERB_SET_COEF_INDEX, 0x07},
- {0x20, AC_VERB_SET_PROC_COEF, 0x3070},
- {}
-};
-
-static struct hda_verb alc262_benq_t31_EAPD_verbs[] = {
- {0x15, AC_VERB_SET_CONNECT_SEL, 0x00},
- {0x19, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x24},
-
- {0x20, AC_VERB_SET_COEF_INDEX, 0x07},
- {0x20, AC_VERB_SET_PROC_COEF, 0x3050},
- {}
};
-/* Samsung Q1 Ultra Vista model setup */
-static struct snd_kcontrol_new alc262_ultra_mixer[] = {
- HDA_CODEC_VOLUME("Master Playback Volume", 0x0c, 0x0, HDA_OUTPUT),
- HDA_BIND_MUTE("Master Playback Switch", 0x0c, 2, HDA_INPUT),
- HDA_CODEC_VOLUME("Mic Playback Volume", 0x0b, 0x01, HDA_INPUT),
- HDA_CODEC_MUTE("Mic Playback Switch", 0x0b, 0x01, HDA_INPUT),
- HDA_CODEC_VOLUME("Mic Boost Volume", 0x19, 0, HDA_INPUT),
- HDA_CODEC_VOLUME("Headphone Mic Boost Volume", 0x15, 0, HDA_INPUT),
- { } /* end */
+static const struct hda_pcm_stream alc269_44k_pcm_analog_capture = {
+ .substreams = 1,
+ .channels_min = 2,
+ .channels_max = 2,
+ .rates = SNDRV_PCM_RATE_44100, /* fixed rate */
+ /* NID is set in alc_build_pcms */
};
-static struct hda_verb alc262_ultra_verbs[] = {
- /* output mixer */
- {0x0c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
- {0x0c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
- {0x0c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
- /* speaker */
- {0x14, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
- {0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
- {0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
- {0x14, AC_VERB_SET_CONNECT_SEL, 0x00},
- /* HP */
- {0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP},
- {0x15, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
- {0x15, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
- {0x15, AC_VERB_SET_CONNECT_SEL, 0x00},
- {0x15, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | ALC880_HP_EVENT},
- /* internal mic */
- {0x19, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80},
- {0x0b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
- /* ADC, choose mic */
- {0x07, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
- {0x24, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
- {0x24, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
- {0x24, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(2)},
- {0x24, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(3)},
- {0x24, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(4)},
- {0x24, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(5)},
- {0x24, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(6)},
- {0x24, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(7)},
- {0x24, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(8)},
- {}
+/* different alc269-variants */
+enum {
+ ALC269_TYPE_ALC269VA,
+ ALC269_TYPE_ALC269VB,
+ ALC269_TYPE_ALC269VC,
+ ALC269_TYPE_ALC269VD,
+ ALC269_TYPE_ALC280,
+ ALC269_TYPE_ALC282,
+ ALC269_TYPE_ALC283,
+ ALC269_TYPE_ALC284,
+ ALC269_TYPE_ALC285,
+ ALC269_TYPE_ALC286,
+ ALC269_TYPE_ALC255,
};
-/* mute/unmute internal speaker according to the hp jack and mute state */
-static void alc262_ultra_automute(struct hda_codec *codec)
+/*
+ * BIOS auto configuration
+ */
+static int alc269_parse_auto_config(struct hda_codec *codec)
{
+ static const hda_nid_t alc269_ignore[] = { 0x1d, 0 };
+ static const hda_nid_t alc269_ssids[] = { 0, 0x1b, 0x14, 0x21 };
+ static const hda_nid_t alc269va_ssids[] = { 0x15, 0x1b, 0x14, 0 };
struct alc_spec *spec = codec->spec;
- unsigned int mute;
-
- mute = 0;
- /* auto-mute only when HP is used as HP */
- if (!spec->cur_mux[0]) {
- spec->jack_present = snd_hda_jack_detect(codec, 0x15);
- if (spec->jack_present)
- mute = HDA_AMP_MUTE;
+ const hda_nid_t *ssids;
+
+ switch (spec->codec_variant) {
+ case ALC269_TYPE_ALC269VA:
+ case ALC269_TYPE_ALC269VC:
+ case ALC269_TYPE_ALC280:
+ case ALC269_TYPE_ALC284:
+ case ALC269_TYPE_ALC285:
+ ssids = alc269va_ssids;
+ break;
+ case ALC269_TYPE_ALC269VB:
+ case ALC269_TYPE_ALC269VD:
+ case ALC269_TYPE_ALC282:
+ case ALC269_TYPE_ALC283:
+ case ALC269_TYPE_ALC286:
+ case ALC269_TYPE_ALC255:
+ ssids = alc269_ssids;
+ break;
+ default:
+ ssids = alc269_ssids;
+ break;
}
- /* mute/unmute internal speaker */
- snd_hda_codec_amp_stereo(codec, 0x14, HDA_OUTPUT, 0,
- HDA_AMP_MUTE, mute);
- /* mute/unmute HP */
- snd_hda_codec_amp_stereo(codec, 0x15, HDA_OUTPUT, 0,
- HDA_AMP_MUTE, mute ? 0 : HDA_AMP_MUTE);
+
+ return alc_parse_auto_config(codec, alc269_ignore, ssids);
}
-/* unsolicited event for HP jack sensing */
-static void alc262_ultra_unsol_event(struct hda_codec *codec,
- unsigned int res)
+static void alc269vb_toggle_power_output(struct hda_codec *codec, int power_up)
{
- if ((res >> 26) != ALC880_HP_EVENT)
- return;
- alc262_ultra_automute(codec);
+ int val = alc_read_coef_idx(codec, 0x04);
+ if (power_up)
+ val |= 1 << 11;
+ else
+ val &= ~(1 << 11);
+ alc_write_coef_idx(codec, 0x04, val);
}
-static struct hda_input_mux alc262_ultra_capture_source = {
- .num_items = 2,
- .items = {
- { "Mic", 0x1 },
- { "Headphone", 0x7 },
- },
-};
-
-static int alc262_ultra_mux_enum_put(struct snd_kcontrol *kcontrol,
- struct snd_ctl_elem_value *ucontrol)
+static void alc269_shutup(struct hda_codec *codec)
{
- struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
struct alc_spec *spec = codec->spec;
- int ret;
- ret = alc_mux_enum_put(kcontrol, ucontrol);
- if (!ret)
- return 0;
- /* reprogram the HP pin as mic or HP according to the input source */
- snd_hda_codec_write_cache(codec, 0x15, 0,
- AC_VERB_SET_PIN_WIDGET_CONTROL,
- spec->cur_mux[0] ? PIN_VREF80 : PIN_HP);
- alc262_ultra_automute(codec); /* mute/unmute HP */
- return ret;
+ if (spec->codec_variant == ALC269_TYPE_ALC269VB)
+ alc269vb_toggle_power_output(codec, 0);
+ if (spec->codec_variant == ALC269_TYPE_ALC269VB &&
+ (alc_get_coef0(codec) & 0x00ff) == 0x018) {
+ msleep(150);
+ }
+ snd_hda_shutup_pins(codec);
}
-static struct snd_kcontrol_new alc262_ultra_capture_mixer[] = {
- HDA_CODEC_VOLUME("Capture Volume", 0x07, 0x0, HDA_INPUT),
- HDA_CODEC_MUTE("Capture Switch", 0x07, 0x0, HDA_INPUT),
- {
- .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
- .name = "Capture Source",
- .info = alc_mux_enum_info,
- .get = alc_mux_enum_get,
- .put = alc262_ultra_mux_enum_put,
- },
- {
- .iface = NID_MAPPING,
- .name = "Capture Source",
- .private_value = 0x15,
- },
- { } /* end */
-};
-
-/* We use two mixers depending on the output pin; 0x16 is a mono output
- * and thus it's bound with a different mixer.
- * This function returns which mixer amp should be used.
- */
-static int alc262_check_volbit(hda_nid_t nid)
+static void alc282_restore_default_value(struct hda_codec *codec)
{
- if (!nid)
- return 0;
- else if (nid == 0x16)
- return 2;
- else
- return 1;
-}
+ int val;
-static int alc262_add_out_vol_ctl(struct alc_spec *spec, hda_nid_t nid,
- const char *pfx, int *vbits, int idx)
-{
- unsigned long val;
- int vbit;
+ /* Power Down Control */
+ alc_write_coef_idx(codec, 0x03, 0x0002);
+ /* FIFO and filter clock */
+ alc_write_coef_idx(codec, 0x05, 0x0700);
+ /* DMIC control */
+ alc_write_coef_idx(codec, 0x07, 0x0200);
+ /* Analog clock */
+ val = alc_read_coef_idx(codec, 0x06);
+ alc_write_coef_idx(codec, 0x06, (val & ~0x00f0) | 0x0);
+ /* JD */
+ val = alc_read_coef_idx(codec, 0x08);
+ alc_write_coef_idx(codec, 0x08, (val & ~0xfffc) | 0x0c2c);
+ /* JD offset1 */
+ alc_write_coef_idx(codec, 0x0a, 0xcccc);
+ /* JD offset2 */
+ alc_write_coef_idx(codec, 0x0b, 0xcccc);
+ /* LDO1/2/3, DAC/ADC */
+ alc_write_coef_idx(codec, 0x0e, 0x6e00);
+ /* JD */
+ val = alc_read_coef_idx(codec, 0x0f);
+ alc_write_coef_idx(codec, 0x0f, (val & ~0xf800) | 0x1000);
+ /* Capless */
+ val = alc_read_coef_idx(codec, 0x10);
+ alc_write_coef_idx(codec, 0x10, (val & ~0xfc00) | 0x0c00);
+ /* Class D test 4 */
+ alc_write_coef_idx(codec, 0x6f, 0x0);
+ /* IO power down directly */
+ val = alc_read_coef_idx(codec, 0x0c);
+ alc_write_coef_idx(codec, 0x0c, (val & ~0xfe00) | 0x0);
+ /* ANC */
+ alc_write_coef_idx(codec, 0x34, 0xa0c0);
+ /* AGC MUX */
+ val = alc_read_coef_idx(codec, 0x16);
+ alc_write_coef_idx(codec, 0x16, (val & ~0x0008) | 0x0);
+ /* DAC simple content protection */
+ val = alc_read_coef_idx(codec, 0x1d);
+ alc_write_coef_idx(codec, 0x1d, (val & ~0x00e0) | 0x0);
+ /* ADC simple content protection */
+ val = alc_read_coef_idx(codec, 0x1f);
+ alc_write_coef_idx(codec, 0x1f, (val & ~0x00e0) | 0x0);
+ /* DAC ADC Zero Detection */
+ alc_write_coef_idx(codec, 0x21, 0x8804);
+ /* PLL */
+ alc_write_coef_idx(codec, 0x63, 0x2902);
+ /* capless control 2 */
+ alc_write_coef_idx(codec, 0x68, 0xa080);
+ /* capless control 3 */
+ alc_write_coef_idx(codec, 0x69, 0x3400);
+ /* capless control 4 */
+ alc_write_coef_idx(codec, 0x6a, 0x2f3e);
+ /* capless control 5 */
+ alc_write_coef_idx(codec, 0x6b, 0x0);
+ /* class D test 2 */
+ val = alc_read_coef_idx(codec, 0x6d);
+ alc_write_coef_idx(codec, 0x6d, (val & ~0x0fff) | 0x0900);
+ /* class D test 3 */
+ alc_write_coef_idx(codec, 0x6e, 0x110a);
+ /* class D test 5 */
+ val = alc_read_coef_idx(codec, 0x70);
+ alc_write_coef_idx(codec, 0x70, (val & ~0x00f8) | 0x00d8);
+ /* class D test 6 */
+ alc_write_coef_idx(codec, 0x71, 0x0014);
+ /* classD OCP */
+ alc_write_coef_idx(codec, 0x72, 0xc2ba);
+ /* classD pure DC test */
+ val = alc_read_coef_idx(codec, 0x77);
+ alc_write_coef_idx(codec, 0x77, (val & ~0x0f80) | 0x0);
+ /* Class D amp control */
+ alc_write_coef_idx(codec, 0x6c, 0xfc06);
+}
+
+static void alc282_init(struct hda_codec *codec)
+{
+ struct alc_spec *spec = codec->spec;
+ hda_nid_t hp_pin = spec->gen.autocfg.hp_pins[0];
+ bool hp_pin_sense;
+ int coef78;
+
+ alc282_restore_default_value(codec);
+
+ if (!hp_pin)
+ return;
+ hp_pin_sense = snd_hda_jack_detect(codec, hp_pin);
+ coef78 = alc_read_coef_idx(codec, 0x78);
- vbit = alc262_check_volbit(nid);
- if (!vbit)
- return 0;
- if (*vbits & vbit) /* a volume control for this mixer already there */
- return 0;
- *vbits |= vbit;
- if (vbit == 2)
- val = HDA_COMPOSE_AMP_VAL(0x0e, 2, 0, HDA_OUTPUT);
- else
- val = HDA_COMPOSE_AMP_VAL(0x0c, 3, 0, HDA_OUTPUT);
- return __add_pb_vol_ctrl(spec, ALC_CTL_WIDGET_VOL, pfx, idx, val);
-}
+ /* Index 0x78 Direct Drive HP AMP LPM Control 1 */
+ /* Headphone capless set to high power mode */
+ alc_write_coef_idx(codec, 0x78, 0x9004);
-static int alc262_add_out_sw_ctl(struct alc_spec *spec, hda_nid_t nid,
- const char *pfx, int idx)
-{
- unsigned long val;
+ if (hp_pin_sense)
+ msleep(2);
- if (!nid)
- return 0;
- if (nid == 0x16)
- val = HDA_COMPOSE_AMP_VAL(nid, 2, 0, HDA_OUTPUT);
- else
- val = HDA_COMPOSE_AMP_VAL(nid, 3, 0, HDA_OUTPUT);
- return __add_pb_sw_ctrl(spec, ALC_CTL_WIDGET_MUTE, pfx, idx, val);
-}
+ snd_hda_codec_write(codec, hp_pin, 0,
+ AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE);
-/* add playback controls from the parsed DAC table */
-static int alc262_auto_create_multi_out_ctls(struct alc_spec *spec,
- const struct auto_pin_cfg *cfg)
-{
- const char *pfx;
- int vbits;
- int i, err;
+ if (hp_pin_sense)
+ msleep(85);
- spec->multiout.num_dacs = 1; /* only use one dac */
- spec->multiout.dac_nids = spec->private_dac_nids;
- spec->multiout.dac_nids[0] = 2;
+ snd_hda_codec_write(codec, hp_pin, 0,
+ AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT);
- pfx = alc_get_line_out_pfx(cfg, true);
- if (!pfx)
- pfx = "Front";
- for (i = 0; i < 2; i++) {
- err = alc262_add_out_sw_ctl(spec, cfg->line_out_pins[i], pfx, i);
- if (err < 0)
- return err;
- if (cfg->line_out_type != AUTO_PIN_SPEAKER_OUT) {
- err = alc262_add_out_sw_ctl(spec, cfg->speaker_pins[i],
- "Speaker", i);
- if (err < 0)
- return err;
- }
- if (cfg->line_out_type != AUTO_PIN_HP_OUT) {
- err = alc262_add_out_sw_ctl(spec, cfg->hp_pins[i],
- "Headphone", i);
- if (err < 0)
- return err;
- }
- }
+ if (hp_pin_sense)
+ msleep(100);
- vbits = alc262_check_volbit(cfg->line_out_pins[0]) |
- alc262_check_volbit(cfg->speaker_pins[0]) |
- alc262_check_volbit(cfg->hp_pins[0]);
- if (vbits == 1 || vbits == 2)
- pfx = "Master"; /* only one mixer is used */
- vbits = 0;
- for (i = 0; i < 2; i++) {
- err = alc262_add_out_vol_ctl(spec, cfg->line_out_pins[i], pfx,
- &vbits, i);
- if (err < 0)
- return err;
- if (cfg->line_out_type != AUTO_PIN_SPEAKER_OUT) {
- err = alc262_add_out_vol_ctl(spec, cfg->speaker_pins[i],
- "Speaker", &vbits, i);
- if (err < 0)
- return err;
- }
- if (cfg->line_out_type != AUTO_PIN_HP_OUT) {
- err = alc262_add_out_vol_ctl(spec, cfg->hp_pins[i],
- "Headphone", &vbits, i);
- if (err < 0)
- return err;
- }
- }
- return 0;
+ /* Headphone capless set to normal mode */
+ alc_write_coef_idx(codec, 0x78, coef78);
}
-#define alc262_auto_create_input_ctls \
- alc882_auto_create_input_ctls
+static void alc282_shutup(struct hda_codec *codec)
+{
+ struct alc_spec *spec = codec->spec;
+ hda_nid_t hp_pin = spec->gen.autocfg.hp_pins[0];
+ bool hp_pin_sense;
+ int coef78;
-/*
- * generic initialization of ADC, input mixers and output mixers
- */
-static struct hda_verb alc262_volume_init_verbs[] = {
- /*
- * Unmute ADC0-2 and set the default input to mic-in
- */
- {0x07, AC_VERB_SET_CONNECT_SEL, 0x00},
- {0x07, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
- {0x08, AC_VERB_SET_CONNECT_SEL, 0x00},
- {0x08, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
- {0x09, AC_VERB_SET_CONNECT_SEL, 0x00},
- {0x09, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
-
- /* Mute input amps (CD, Line In, Mic 1 & Mic 2) of the analog-loopback
- * mixer widget
- * Note: PASD motherboards uses the Line In 2 as the input for
- * front panel mic (mic 2)
- */
- /* Amp Indices: Mic1 = 0, Mic2 = 1, Line1 = 2, Line2 = 3, CD = 4 */
- {0x0b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
- {0x0b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
- {0x0b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(2)},
- {0x0b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(3)},
- {0x0b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(4)},
+ if (!hp_pin) {
+ alc269_shutup(codec);
+ return;
+ }
- /*
- * Set up output mixers (0x0c - 0x0f)
- */
- /* set vol=0 to output mixers */
- {0x0c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
- {0x0d, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
- {0x0e, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
-
- /* set up input amps for analog loopback */
- /* Amp Indices: DAC = 0, mixer = 1 */
- {0x0c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
- {0x0c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
- {0x0d, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
- {0x0d, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
- {0x0e, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
- {0x0e, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
-
- /* FIXME: use matrix-type input source selection */
- /* Mixer elements: 0x18, 19, 1a, 1b, 1c, 1d, 14, 15, 16, 17, 0b */
- /* Input mixer1: unmute Mic, F-Mic, Line, CD inputs */
- {0x24, AC_VERB_SET_AMP_GAIN_MUTE, (0x7000 | (0x00 << 8))},
- {0x24, AC_VERB_SET_AMP_GAIN_MUTE, (0x7080 | (0x03 << 8))},
- {0x24, AC_VERB_SET_AMP_GAIN_MUTE, (0x7080 | (0x02 << 8))},
- {0x24, AC_VERB_SET_AMP_GAIN_MUTE, (0x7080 | (0x04 << 8))},
- /* Input mixer2 */
- {0x23, AC_VERB_SET_AMP_GAIN_MUTE, (0x7000 | (0x00 << 8))},
- {0x23, AC_VERB_SET_AMP_GAIN_MUTE, (0x7080 | (0x03 << 8))},
- {0x23, AC_VERB_SET_AMP_GAIN_MUTE, (0x7080 | (0x02 << 8))},
- {0x23, AC_VERB_SET_AMP_GAIN_MUTE, (0x7080 | (0x04 << 8))},
- /* Input mixer3 */
- {0x22, AC_VERB_SET_AMP_GAIN_MUTE, (0x7000 | (0x00 << 8))},
- {0x22, AC_VERB_SET_AMP_GAIN_MUTE, (0x7080 | (0x03 << 8))},
- {0x22, AC_VERB_SET_AMP_GAIN_MUTE, (0x7080 | (0x02 << 8))},
- {0x22, AC_VERB_SET_AMP_GAIN_MUTE, (0x7080 | (0x04 << 8))},
+ hp_pin_sense = snd_hda_jack_detect(codec, hp_pin);
+ coef78 = alc_read_coef_idx(codec, 0x78);
+ alc_write_coef_idx(codec, 0x78, 0x9004);
- { }
-};
+ if (hp_pin_sense)
+ msleep(2);
-static struct hda_verb alc262_HP_BPC_init_verbs[] = {
- /*
- * Unmute ADC0-2 and set the default input to mic-in
- */
- {0x07, AC_VERB_SET_CONNECT_SEL, 0x00},
- {0x07, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
- {0x08, AC_VERB_SET_CONNECT_SEL, 0x00},
- {0x08, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
- {0x09, AC_VERB_SET_CONNECT_SEL, 0x00},
- {0x09, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
-
- /* Mute input amps (CD, Line In, Mic 1 & Mic 2) of the analog-loopback
- * mixer widget
- * Note: PASD motherboards uses the Line In 2 as the input for
- * front panel mic (mic 2)
- */
- /* Amp Indices: Mic1 = 0, Mic2 = 1, Line1 = 2, Line2 = 3, CD = 4 */
- {0x0b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
- {0x0b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
- {0x0b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(2)},
- {0x0b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(3)},
- {0x0b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(4)},
- {0x0b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(5)},
- {0x0b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(6)},
+ snd_hda_codec_write(codec, hp_pin, 0,
+ AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE);
- /*
- * Set up output mixers (0x0c - 0x0e)
- */
- /* set vol=0 to output mixers */
- {0x0c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
- {0x0d, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
- {0x0e, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
-
- /* set up input amps for analog loopback */
- /* Amp Indices: DAC = 0, mixer = 1 */
- {0x0c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
- {0x0c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
- {0x0d, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
- {0x0d, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
- {0x0e, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
- {0x0e, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
-
- {0x1b, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP},
- {0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT },
- {0x16, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT },
-
- {0x1b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE },
- {0x15, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE },
-
- {0x1b, AC_VERB_SET_CONNECT_SEL, 0x00},
- {0x15, AC_VERB_SET_CONNECT_SEL, 0x00},
-
- {0x14, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x20},
- {0x18, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x24},
- {0x19, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x24},
- {0x1a, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x20},
- {0x1c, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x20},
-
- {0x14, AC_VERB_SET_AMP_GAIN_MUTE, 0x7000 },
- {0x18, AC_VERB_SET_AMP_GAIN_MUTE, 0x7000 },
- {0x19, AC_VERB_SET_AMP_GAIN_MUTE, 0x7000 },
- {0x1a, AC_VERB_SET_AMP_GAIN_MUTE, 0x7000 },
- {0x1c, AC_VERB_SET_AMP_GAIN_MUTE, 0x7000 },
- {0x1d, AC_VERB_SET_AMP_GAIN_MUTE, 0x7000 },
-
-
- /* FIXME: use matrix-type input source selection */
- /* Mixer elements: 0x18, 19, 1a, 1b, 1c, 1d, 14, 15, 0b, 12 */
- /* Input mixer1: only unmute Mic */
- {0x24, AC_VERB_SET_AMP_GAIN_MUTE, (0x7000 | (0x00 << 8))},
- {0x24, AC_VERB_SET_AMP_GAIN_MUTE, (0x7080 | (0x01 << 8))},
- {0x24, AC_VERB_SET_AMP_GAIN_MUTE, (0x7080 | (0x02 << 8))},
- {0x24, AC_VERB_SET_AMP_GAIN_MUTE, (0x7080 | (0x03 << 8))},
- {0x24, AC_VERB_SET_AMP_GAIN_MUTE, (0x7080 | (0x04 << 8))},
- {0x24, AC_VERB_SET_AMP_GAIN_MUTE, (0x7080 | (0x05 << 8))},
- {0x24, AC_VERB_SET_AMP_GAIN_MUTE, (0x7080 | (0x06 << 8))},
- {0x24, AC_VERB_SET_AMP_GAIN_MUTE, (0x7080 | (0x07 << 8))},
- {0x24, AC_VERB_SET_AMP_GAIN_MUTE, (0x7080 | (0x08 << 8))},
- /* Input mixer2 */
- {0x23, AC_VERB_SET_AMP_GAIN_MUTE, (0x7000 | (0x00 << 8))},
- {0x23, AC_VERB_SET_AMP_GAIN_MUTE, (0x7080 | (0x01 << 8))},
- {0x23, AC_VERB_SET_AMP_GAIN_MUTE, (0x7080 | (0x02 << 8))},
- {0x23, AC_VERB_SET_AMP_GAIN_MUTE, (0x7080 | (0x03 << 8))},
- {0x23, AC_VERB_SET_AMP_GAIN_MUTE, (0x7080 | (0x04 << 8))},
- {0x23, AC_VERB_SET_AMP_GAIN_MUTE, (0x7080 | (0x05 << 8))},
- {0x23, AC_VERB_SET_AMP_GAIN_MUTE, (0x7080 | (0x06 << 8))},
- {0x23, AC_VERB_SET_AMP_GAIN_MUTE, (0x7080 | (0x07 << 8))},
- {0x23, AC_VERB_SET_AMP_GAIN_MUTE, (0x7080 | (0x08 << 8))},
- /* Input mixer3 */
- {0x22, AC_VERB_SET_AMP_GAIN_MUTE, (0x7000 | (0x00 << 8))},
- {0x22, AC_VERB_SET_AMP_GAIN_MUTE, (0x7080 | (0x01 << 8))},
- {0x22, AC_VERB_SET_AMP_GAIN_MUTE, (0x7080 | (0x02 << 8))},
- {0x22, AC_VERB_SET_AMP_GAIN_MUTE, (0x7080 | (0x03 << 8))},
- {0x22, AC_VERB_SET_AMP_GAIN_MUTE, (0x7080 | (0x04 << 8))},
- {0x22, AC_VERB_SET_AMP_GAIN_MUTE, (0x7080 | (0x05 << 8))},
- {0x22, AC_VERB_SET_AMP_GAIN_MUTE, (0x7080 | (0x06 << 8))},
- {0x22, AC_VERB_SET_AMP_GAIN_MUTE, (0x7080 | (0x07 << 8))},
- {0x22, AC_VERB_SET_AMP_GAIN_MUTE, (0x7080 | (0x08 << 8))},
-
- {0x1b, AC_VERB_SET_UNSOLICITED_ENABLE, ALC880_HP_EVENT | AC_USRSP_EN},
+ if (hp_pin_sense)
+ msleep(85);
- { }
-};
+ snd_hda_codec_write(codec, hp_pin, 0,
+ AC_VERB_SET_PIN_WIDGET_CONTROL, 0x0);
-static struct hda_verb alc262_HP_BPC_WildWest_init_verbs[] = {
- /*
- * Unmute ADC0-2 and set the default input to mic-in
- */
- {0x07, AC_VERB_SET_CONNECT_SEL, 0x00},
- {0x07, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
- {0x08, AC_VERB_SET_CONNECT_SEL, 0x00},
- {0x08, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
- {0x09, AC_VERB_SET_CONNECT_SEL, 0x00},
- {0x09, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
-
- /* Mute input amps (CD, Line In, Mic 1 & Mic 2) of the analog-loopback
- * mixer widget
- * Note: PASD motherboards uses the Line In 2 as the input for front
- * panel mic (mic 2)
- */
- /* Amp Indices: Mic1 = 0, Mic2 = 1, Line1 = 2, Line2 = 3, CD = 4 */
- {0x0b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
- {0x0b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
- {0x0b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(2)},
- {0x0b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(3)},
- {0x0b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(4)},
- {0x0b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(5)},
- {0x0b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(6)},
- {0x0b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(7)},
- /*
- * Set up output mixers (0x0c - 0x0e)
- */
- /* set vol=0 to output mixers */
- {0x0c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
- {0x0d, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
- {0x0e, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
-
- /* set up input amps for analog loopback */
- /* Amp Indices: DAC = 0, mixer = 1 */
- {0x0c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
- {0x0c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
- {0x0d, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
- {0x0d, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
- {0x0e, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
- {0x0e, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
-
-
- {0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP }, /* HP */
- {0x16, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT }, /* Mono */
- {0x18, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80 }, /* rear MIC */
- {0x19, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN }, /* Line in */
- {0x1a, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80 }, /* Front MIC */
- {0x1b, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT }, /* Line out */
- {0x1c, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN }, /* CD in */
-
- {0x1b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE },
- {0x15, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE },
-
- {0x1b, AC_VERB_SET_CONNECT_SEL, 0x00},
- {0x15, AC_VERB_SET_CONNECT_SEL, 0x01},
-
- /* {0x14, AC_VERB_SET_AMP_GAIN_MUTE, 0x7023 }, */
- {0x18, AC_VERB_SET_AMP_GAIN_MUTE, 0x7000 },
- {0x19, AC_VERB_SET_AMP_GAIN_MUTE, 0x7000 },
- {0x1a, AC_VERB_SET_AMP_GAIN_MUTE, 0x7023 },
- {0x1c, AC_VERB_SET_AMP_GAIN_MUTE, 0x7000 },
- {0x1d, AC_VERB_SET_AMP_GAIN_MUTE, 0x7000 },
-
- /* FIXME: use matrix-type input source selection */
- /* Mixer elements: 0x18, 19, 1a, 1b, 1c, 1d, 14, 15, 16, 17, 0b */
- /* Input mixer1: unmute Mic, F-Mic, Line, CD inputs */
- {0x24, AC_VERB_SET_AMP_GAIN_MUTE, (0x7000 | (0x00 << 8))}, /*rear MIC*/
- {0x24, AC_VERB_SET_AMP_GAIN_MUTE, (0x7000 | (0x01 << 8))}, /*Line in*/
- {0x24, AC_VERB_SET_AMP_GAIN_MUTE, (0x7000 | (0x02 << 8))}, /*F MIC*/
- {0x24, AC_VERB_SET_AMP_GAIN_MUTE, (0x7000 | (0x03 << 8))}, /*Front*/
- {0x24, AC_VERB_SET_AMP_GAIN_MUTE, (0x7000 | (0x04 << 8))}, /*CD*/
- /* {0x24, AC_VERB_SET_AMP_GAIN_MUTE, (0x7000 | (0x06 << 8))}, */
- {0x24, AC_VERB_SET_AMP_GAIN_MUTE, (0x7000 | (0x07 << 8))}, /*HP*/
- /* Input mixer2 */
- {0x23, AC_VERB_SET_AMP_GAIN_MUTE, (0x7000 | (0x00 << 8))},
- {0x23, AC_VERB_SET_AMP_GAIN_MUTE, (0x7000 | (0x01 << 8))},
- {0x23, AC_VERB_SET_AMP_GAIN_MUTE, (0x7000 | (0x02 << 8))},
- {0x23, AC_VERB_SET_AMP_GAIN_MUTE, (0x7000 | (0x03 << 8))},
- {0x23, AC_VERB_SET_AMP_GAIN_MUTE, (0x7000 | (0x04 << 8))},
- /* {0x23, AC_VERB_SET_AMP_GAIN_MUTE, (0x7000 | (0x06 << 8))}, */
- {0x23, AC_VERB_SET_AMP_GAIN_MUTE, (0x7000 | (0x07 << 8))},
- /* Input mixer3 */
- {0x22, AC_VERB_SET_AMP_GAIN_MUTE, (0x7000 | (0x00 << 8))},
- {0x22, AC_VERB_SET_AMP_GAIN_MUTE, (0x7000 | (0x01 << 8))},
- {0x22, AC_VERB_SET_AMP_GAIN_MUTE, (0x7000 | (0x02 << 8))},
- {0x22, AC_VERB_SET_AMP_GAIN_MUTE, (0x7000 | (0x03 << 8))},
- {0x22, AC_VERB_SET_AMP_GAIN_MUTE, (0x7000 | (0x04 << 8))},
- /* {0x22, AC_VERB_SET_AMP_GAIN_MUTE, (0x7000 | (0x06 << 8))}, */
- {0x22, AC_VERB_SET_AMP_GAIN_MUTE, (0x7000 | (0x07 << 8))},
-
- {0x15, AC_VERB_SET_UNSOLICITED_ENABLE, ALC880_HP_EVENT | AC_USRSP_EN},
+ if (hp_pin_sense)
+ msleep(100);
- { }
-};
+ alc_auto_setup_eapd(codec, false);
+ snd_hda_shutup_pins(codec);
+ alc_write_coef_idx(codec, 0x78, coef78);
+}
-static struct hda_verb alc262_toshiba_rx1_unsol_verbs[] = {
+static void alc283_restore_default_value(struct hda_codec *codec)
+{
+ int val;
- {0x14, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT }, /* Front Speaker */
- {0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE },
- {0x14, AC_VERB_SET_CONNECT_SEL, 0x01},
+ /* Power Down Control */
+ alc_write_coef_idx(codec, 0x03, 0x0002);
+ /* FIFO and filter clock */
+ alc_write_coef_idx(codec, 0x05, 0x0700);
+ /* DMIC control */
+ alc_write_coef_idx(codec, 0x07, 0x0200);
+ /* Analog clock */
+ val = alc_read_coef_idx(codec, 0x06);
+ alc_write_coef_idx(codec, 0x06, (val & ~0x00f0) | 0x0);
+ /* JD */
+ val = alc_read_coef_idx(codec, 0x08);
+ alc_write_coef_idx(codec, 0x08, (val & ~0xfffc) | 0x0c2c);
+ /* JD offset1 */
+ alc_write_coef_idx(codec, 0x0a, 0xcccc);
+ /* JD offset2 */
+ alc_write_coef_idx(codec, 0x0b, 0xcccc);
+ /* LDO1/2/3, DAC/ADC */
+ alc_write_coef_idx(codec, 0x0e, 0x6fc0);
+ /* JD */
+ val = alc_read_coef_idx(codec, 0x0f);
+ alc_write_coef_idx(codec, 0x0f, (val & ~0xf800) | 0x1000);
+ /* Capless */
+ val = alc_read_coef_idx(codec, 0x10);
+ alc_write_coef_idx(codec, 0x10, (val & ~0xfc00) | 0x0c00);
+ /* Class D test 4 */
+ alc_write_coef_idx(codec, 0x3a, 0x0);
+ /* IO power down directly */
+ val = alc_read_coef_idx(codec, 0x0c);
+ alc_write_coef_idx(codec, 0x0c, (val & ~0xfe00) | 0x0);
+ /* ANC */
+ alc_write_coef_idx(codec, 0x22, 0xa0c0);
+ /* AGC MUX */
+ val = alc_read_coefex_idx(codec, 0x53, 0x01);
+ alc_write_coefex_idx(codec, 0x53, 0x01, (val & ~0x000f) | 0x0008);
+ /* DAC simple content protection */
+ val = alc_read_coef_idx(codec, 0x1d);
+ alc_write_coef_idx(codec, 0x1d, (val & ~0x00e0) | 0x0);
+ /* ADC simple content protection */
+ val = alc_read_coef_idx(codec, 0x1f);
+ alc_write_coef_idx(codec, 0x1f, (val & ~0x00e0) | 0x0);
+ /* DAC ADC Zero Detection */
+ alc_write_coef_idx(codec, 0x21, 0x8804);
+ /* PLL */
+ alc_write_coef_idx(codec, 0x2e, 0x2902);
+ /* capless control 2 */
+ alc_write_coef_idx(codec, 0x33, 0xa080);
+ /* capless control 3 */
+ alc_write_coef_idx(codec, 0x34, 0x3400);
+ /* capless control 4 */
+ alc_write_coef_idx(codec, 0x35, 0x2f3e);
+ /* capless control 5 */
+ alc_write_coef_idx(codec, 0x36, 0x0);
+ /* class D test 2 */
+ val = alc_read_coef_idx(codec, 0x38);
+ alc_write_coef_idx(codec, 0x38, (val & ~0x0fff) | 0x0900);
+ /* class D test 3 */
+ alc_write_coef_idx(codec, 0x39, 0x110a);
+ /* class D test 5 */
+ val = alc_read_coef_idx(codec, 0x3b);
+ alc_write_coef_idx(codec, 0x3b, (val & ~0x00f8) | 0x00d8);
+ /* class D test 6 */
+ alc_write_coef_idx(codec, 0x3c, 0x0014);
+ /* classD OCP */
+ alc_write_coef_idx(codec, 0x3d, 0xc2ba);
+ /* classD pure DC test */
+ val = alc_read_coef_idx(codec, 0x42);
+ alc_write_coef_idx(codec, 0x42, (val & ~0x0f80) | 0x0);
+ /* test mode */
+ alc_write_coef_idx(codec, 0x49, 0x0);
+ /* Class D DC enable */
+ val = alc_read_coef_idx(codec, 0x40);
+ alc_write_coef_idx(codec, 0x40, (val & ~0xf800) | 0x9800);
+ /* DC offset */
+ val = alc_read_coef_idx(codec, 0x42);
+ alc_write_coef_idx(codec, 0x42, (val & ~0xf000) | 0x2000);
+ /* Class D amp control */
+ alc_write_coef_idx(codec, 0x37, 0xfc06);
+}
+
+static void alc283_init(struct hda_codec *codec)
+{
+ struct alc_spec *spec = codec->spec;
+ hda_nid_t hp_pin = spec->gen.autocfg.hp_pins[0];
+ bool hp_pin_sense;
+ int val;
- {0x18, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80 }, /* MIC jack */
- {0x19, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80 }, /* Front MIC */
- {0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0) },
- {0x19, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0) },
+ if (!spec->gen.autocfg.hp_outs) {
+ if (spec->gen.autocfg.line_out_type == AC_JACK_HP_OUT)
+ hp_pin = spec->gen.autocfg.line_out_pins[0];
+ }
- {0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP }, /* HP jack */
- {0x15, AC_VERB_SET_CONNECT_SEL, 0x00},
- {0x15, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | ALC880_HP_EVENT},
- {}
-};
+ alc283_restore_default_value(codec);
-/*
- * Pin config fixes
- */
-enum {
- PINFIX_FSC_H270,
-};
+ if (!hp_pin)
+ return;
+ hp_pin_sense = snd_hda_jack_detect(codec, hp_pin);
-static const struct alc_fixup alc262_fixups[] = {
- [PINFIX_FSC_H270] = {
- .type = ALC_FIXUP_PINS,
- .v.pins = (const struct alc_pincfg[]) {
- { 0x14, 0x99130110 }, /* speaker */
- { 0x15, 0x0221142f }, /* front HP */
- { 0x1b, 0x0121141f }, /* rear HP */
- { }
- }
- },
-};
+ /* Index 0x43 Direct Drive HP AMP LPM Control 1 */
+ /* Headphone capless set to high power mode */
+ alc_write_coef_idx(codec, 0x43, 0x9004);
-static struct snd_pci_quirk alc262_fixup_tbl[] = {
- SND_PCI_QUIRK(0x1734, 0x1147, "FSC Celsius H270", PINFIX_FSC_H270),
- {}
-};
+ snd_hda_codec_write(codec, hp_pin, 0,
+ AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE);
+ if (hp_pin_sense)
+ msleep(85);
-#ifdef CONFIG_SND_HDA_POWER_SAVE
-#define alc262_loopbacks alc880_loopbacks
-#endif
+ snd_hda_codec_write(codec, hp_pin, 0,
+ AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT);
-/* pcm configuration: identical with ALC880 */
-#define alc262_pcm_analog_playback alc880_pcm_analog_playback
-#define alc262_pcm_analog_capture alc880_pcm_analog_capture
-#define alc262_pcm_digital_playback alc880_pcm_digital_playback
-#define alc262_pcm_digital_capture alc880_pcm_digital_capture
+ if (hp_pin_sense)
+ msleep(85);
+ /* Index 0x46 Combo jack auto switch control 2 */
+ /* 3k pull low control for Headset jack. */
+ val = alc_read_coef_idx(codec, 0x46);
+ alc_write_coef_idx(codec, 0x46, val & ~(3 << 12));
+ /* Headphone capless set to normal mode */
+ alc_write_coef_idx(codec, 0x43, 0x9614);
+}
-/*
- * BIOS auto configuration
- */
-static int alc262_parse_auto_config(struct hda_codec *codec)
+static void alc283_shutup(struct hda_codec *codec)
{
struct alc_spec *spec = codec->spec;
- int err;
- static hda_nid_t alc262_ignore[] = { 0x1d, 0 };
+ hda_nid_t hp_pin = spec->gen.autocfg.hp_pins[0];
+ bool hp_pin_sense;
+ int val;
- err = snd_hda_parse_pin_def_config(codec, &spec->autocfg,
- alc262_ignore);
- if (err < 0)
- return err;
- if (!spec->autocfg.line_outs) {
- if (spec->autocfg.dig_outs || spec->autocfg.dig_in_pin) {
- spec->multiout.max_channels = 2;
- spec->no_analog = 1;
- goto dig_only;
- }
- return 0; /* can't find valid BIOS pin config */
+ if (!spec->gen.autocfg.hp_outs) {
+ if (spec->gen.autocfg.line_out_type == AC_JACK_HP_OUT)
+ hp_pin = spec->gen.autocfg.line_out_pins[0];
}
- err = alc262_auto_create_multi_out_ctls(spec, &spec->autocfg);
- if (err < 0)
- return err;
- err = alc262_auto_create_input_ctls(codec, &spec->autocfg);
- if (err < 0)
- return err;
-
- spec->multiout.max_channels = spec->multiout.num_dacs * 2;
- dig_only:
- alc_auto_parse_digital(codec);
+ if (!hp_pin) {
+ alc269_shutup(codec);
+ return;
+ }
- if (spec->kctls.list)
- add_mixer(spec, spec->kctls.list);
+ hp_pin_sense = snd_hda_jack_detect(codec, hp_pin);
- add_verb(spec, alc262_volume_init_verbs);
- spec->num_mux_defs = 1;
- spec->input_mux = &spec->private_imux[0];
+ alc_write_coef_idx(codec, 0x43, 0x9004);
- err = alc_auto_add_mic_boost(codec);
- if (err < 0)
- return err;
+ snd_hda_codec_write(codec, hp_pin, 0,
+ AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE);
- alc_ssid_check(codec, 0x15, 0x1b, 0x14, 0);
+ if (hp_pin_sense)
+ msleep(100);
- return 1;
-}
+ snd_hda_codec_write(codec, hp_pin, 0,
+ AC_VERB_SET_PIN_WIDGET_CONTROL, 0x0);
-#define alc262_auto_init_multi_out alc882_auto_init_multi_out
-#define alc262_auto_init_hp_out alc882_auto_init_hp_out
-#define alc262_auto_init_analog_input alc882_auto_init_analog_input
-#define alc262_auto_init_input_src alc882_auto_init_input_src
+ val = alc_read_coef_idx(codec, 0x46);
+ alc_write_coef_idx(codec, 0x46, val | (3 << 12));
+ if (hp_pin_sense)
+ msleep(100);
+ alc_auto_setup_eapd(codec, false);
+ snd_hda_shutup_pins(codec);
+ alc_write_coef_idx(codec, 0x43, 0x9614);
+}
-/* init callback for auto-configuration model -- overriding the default init */
-static void alc262_auto_init(struct hda_codec *codec)
+static void alc5505_coef_set(struct hda_codec *codec, unsigned int index_reg,
+ unsigned int val)
{
- struct alc_spec *spec = codec->spec;
- alc262_auto_init_multi_out(codec);
- alc262_auto_init_hp_out(codec);
- alc262_auto_init_analog_input(codec);
- alc262_auto_init_input_src(codec);
- alc_auto_init_digital(codec);
- if (spec->unsol_event)
- alc_inithook(codec);
+ snd_hda_codec_write(codec, 0x51, 0, AC_VERB_SET_COEF_INDEX, index_reg >> 1);
+ snd_hda_codec_write(codec, 0x51, 0, AC_VERB_SET_PROC_COEF, val & 0xffff); /* LSB */
+ snd_hda_codec_write(codec, 0x51, 0, AC_VERB_SET_PROC_COEF, val >> 16); /* MSB */
}
-/*
- * configuration and preset
- */
-static const char * const alc262_models[ALC262_MODEL_LAST] = {
- [ALC262_BASIC] = "basic",
- [ALC262_HIPPO] = "hippo",
- [ALC262_HIPPO_1] = "hippo_1",
- [ALC262_FUJITSU] = "fujitsu",
- [ALC262_HP_BPC] = "hp-bpc",
- [ALC262_HP_BPC_D7000_WL]= "hp-bpc-d7000",
- [ALC262_HP_TC_T5735] = "hp-tc-t5735",
- [ALC262_HP_RP5700] = "hp-rp5700",
- [ALC262_BENQ_ED8] = "benq",
- [ALC262_BENQ_T31] = "benq-t31",
- [ALC262_SONY_ASSAMD] = "sony-assamd",
- [ALC262_TOSHIBA_S06] = "toshiba-s06",
- [ALC262_TOSHIBA_RX1] = "toshiba-rx1",
- [ALC262_ULTRA] = "ultra",
- [ALC262_LENOVO_3000] = "lenovo-3000",
- [ALC262_NEC] = "nec",
- [ALC262_TYAN] = "tyan",
- [ALC262_AUTO] = "auto",
-};
+static int alc5505_coef_get(struct hda_codec *codec, unsigned int index_reg)
+{
+ unsigned int val;
-static struct snd_pci_quirk alc262_cfg_tbl[] = {
- SND_PCI_QUIRK(0x1002, 0x437b, "Hippo", ALC262_HIPPO),
- SND_PCI_QUIRK(0x1033, 0x8895, "NEC Versa S9100", ALC262_NEC),
- SND_PCI_QUIRK_MASK(0x103c, 0xff00, 0x1200, "HP xw series",
- ALC262_HP_BPC),
- SND_PCI_QUIRK_MASK(0x103c, 0xff00, 0x1300, "HP xw series",
- ALC262_HP_BPC),
- SND_PCI_QUIRK_MASK(0x103c, 0xff00, 0x1500, "HP z series",
- ALC262_HP_BPC),
- SND_PCI_QUIRK_MASK(0x103c, 0xff00, 0x1700, "HP xw series",
- ALC262_HP_BPC),
- SND_PCI_QUIRK(0x103c, 0x2800, "HP D7000", ALC262_HP_BPC_D7000_WL),
- SND_PCI_QUIRK(0x103c, 0x2801, "HP D7000", ALC262_HP_BPC_D7000_WF),
- SND_PCI_QUIRK(0x103c, 0x2802, "HP D7000", ALC262_HP_BPC_D7000_WL),
- SND_PCI_QUIRK(0x103c, 0x2803, "HP D7000", ALC262_HP_BPC_D7000_WF),
- SND_PCI_QUIRK(0x103c, 0x2804, "HP D7000", ALC262_HP_BPC_D7000_WL),
- SND_PCI_QUIRK(0x103c, 0x2805, "HP D7000", ALC262_HP_BPC_D7000_WF),
- SND_PCI_QUIRK(0x103c, 0x2806, "HP D7000", ALC262_HP_BPC_D7000_WL),
- SND_PCI_QUIRK(0x103c, 0x2807, "HP D7000", ALC262_HP_BPC_D7000_WF),
- SND_PCI_QUIRK(0x103c, 0x280c, "HP xw4400", ALC262_HP_BPC),
- SND_PCI_QUIRK(0x103c, 0x3014, "HP xw6400", ALC262_HP_BPC),
- SND_PCI_QUIRK(0x103c, 0x3015, "HP xw8400", ALC262_HP_BPC),
- SND_PCI_QUIRK(0x103c, 0x302f, "HP Thin Client T5735",
- ALC262_HP_TC_T5735),
- SND_PCI_QUIRK(0x103c, 0x2817, "HP RP5700", ALC262_HP_RP5700),
- SND_PCI_QUIRK(0x104d, 0x1f00, "Sony ASSAMD", ALC262_SONY_ASSAMD),
- SND_PCI_QUIRK(0x104d, 0x8203, "Sony UX-90", ALC262_HIPPO),
- SND_PCI_QUIRK(0x104d, 0x820f, "Sony ASSAMD", ALC262_SONY_ASSAMD),
- SND_PCI_QUIRK(0x104d, 0x9016, "Sony VAIO", ALC262_AUTO), /* dig-only */
- SND_PCI_QUIRK(0x104d, 0x9025, "Sony VAIO Z21MN", ALC262_TOSHIBA_S06),
- SND_PCI_QUIRK(0x104d, 0x9035, "Sony VAIO VGN-FW170J", ALC262_AUTO),
- SND_PCI_QUIRK(0x104d, 0x9047, "Sony VAIO Type G", ALC262_AUTO),
-#if 0 /* disable the quirk since model=auto works better in recent versions */
- SND_PCI_QUIRK_MASK(0x104d, 0xff00, 0x9000, "Sony VAIO",
- ALC262_SONY_ASSAMD),
-#endif
- SND_PCI_QUIRK(0x1179, 0x0001, "Toshiba dynabook SS RX1",
- ALC262_TOSHIBA_RX1),
- SND_PCI_QUIRK(0x1179, 0xff7b, "Toshiba S06", ALC262_TOSHIBA_S06),
- SND_PCI_QUIRK(0x10cf, 0x1397, "Fujitsu", ALC262_FUJITSU),
- SND_PCI_QUIRK(0x10cf, 0x142d, "Fujitsu Lifebook E8410", ALC262_FUJITSU),
- SND_PCI_QUIRK(0x10f1, 0x2915, "Tyan Thunder n6650W", ALC262_TYAN),
- SND_PCI_QUIRK_MASK(0x144d, 0xff00, 0xc032, "Samsung Q1",
- ALC262_ULTRA),
- SND_PCI_QUIRK(0x144d, 0xc510, "Samsung Q45", ALC262_HIPPO),
- SND_PCI_QUIRK(0x17aa, 0x384e, "Lenovo 3000 y410", ALC262_LENOVO_3000),
- SND_PCI_QUIRK(0x17ff, 0x0560, "Benq ED8", ALC262_BENQ_ED8),
- SND_PCI_QUIRK(0x17ff, 0x058d, "Benq T31-16", ALC262_BENQ_T31),
- SND_PCI_QUIRK(0x17ff, 0x058f, "Benq Hippo", ALC262_HIPPO_1),
- {}
-};
+ snd_hda_codec_write(codec, 0x51, 0, AC_VERB_SET_COEF_INDEX, index_reg >> 1);
+ val = snd_hda_codec_read(codec, 0x51, 0, AC_VERB_GET_PROC_COEF, 0)
+ & 0xffff;
+ val |= snd_hda_codec_read(codec, 0x51, 0, AC_VERB_GET_PROC_COEF, 0)
+ << 16;
+ return val;
+}
-static struct alc_config_preset alc262_presets[] = {
- [ALC262_BASIC] = {
- .mixers = { alc262_base_mixer },
- .init_verbs = { alc262_init_verbs },
- .num_dacs = ARRAY_SIZE(alc262_dac_nids),
- .dac_nids = alc262_dac_nids,
- .hp_nid = 0x03,
- .num_channel_mode = ARRAY_SIZE(alc262_modes),
- .channel_mode = alc262_modes,
- .input_mux = &alc262_capture_source,
- },
- [ALC262_HIPPO] = {
- .mixers = { alc262_hippo_mixer },
- .init_verbs = { alc262_init_verbs, alc_hp15_unsol_verbs},
- .num_dacs = ARRAY_SIZE(alc262_dac_nids),
- .dac_nids = alc262_dac_nids,
- .hp_nid = 0x03,
- .dig_out_nid = ALC262_DIGOUT_NID,
- .num_channel_mode = ARRAY_SIZE(alc262_modes),
- .channel_mode = alc262_modes,
- .input_mux = &alc262_capture_source,
- .unsol_event = alc262_hippo_unsol_event,
- .setup = alc262_hippo_setup,
- .init_hook = alc262_hippo_automute,
- },
- [ALC262_HIPPO_1] = {
- .mixers = { alc262_hippo1_mixer },
- .init_verbs = { alc262_init_verbs, alc262_hippo1_unsol_verbs},
- .num_dacs = ARRAY_SIZE(alc262_dac_nids),
- .dac_nids = alc262_dac_nids,
- .hp_nid = 0x02,
- .dig_out_nid = ALC262_DIGOUT_NID,
- .num_channel_mode = ARRAY_SIZE(alc262_modes),
- .channel_mode = alc262_modes,
- .input_mux = &alc262_capture_source,
- .unsol_event = alc262_hippo_unsol_event,
- .setup = alc262_hippo1_setup,
- .init_hook = alc262_hippo_automute,
- },
- [ALC262_FUJITSU] = {
- .mixers = { alc262_fujitsu_mixer },
- .init_verbs = { alc262_init_verbs, alc262_EAPD_verbs,
- alc262_fujitsu_unsol_verbs },
- .num_dacs = ARRAY_SIZE(alc262_dac_nids),
- .dac_nids = alc262_dac_nids,
- .hp_nid = 0x03,
- .dig_out_nid = ALC262_DIGOUT_NID,
- .num_channel_mode = ARRAY_SIZE(alc262_modes),
- .channel_mode = alc262_modes,
- .input_mux = &alc262_fujitsu_capture_source,
- .unsol_event = alc262_fujitsu_unsol_event,
- .init_hook = alc262_fujitsu_init_hook,
- },
- [ALC262_HP_BPC] = {
- .mixers = { alc262_HP_BPC_mixer },
- .init_verbs = { alc262_HP_BPC_init_verbs },
- .num_dacs = ARRAY_SIZE(alc262_dac_nids),
- .dac_nids = alc262_dac_nids,
- .hp_nid = 0x03,
- .num_channel_mode = ARRAY_SIZE(alc262_modes),
- .channel_mode = alc262_modes,
- .input_mux = &alc262_HP_capture_source,
- .unsol_event = alc262_hp_bpc_unsol_event,
- .init_hook = alc262_hp_bpc_automute,
- },
- [ALC262_HP_BPC_D7000_WF] = {
- .mixers = { alc262_HP_BPC_WildWest_mixer },
- .init_verbs = { alc262_HP_BPC_WildWest_init_verbs },
- .num_dacs = ARRAY_SIZE(alc262_dac_nids),
- .dac_nids = alc262_dac_nids,
- .hp_nid = 0x03,
- .num_channel_mode = ARRAY_SIZE(alc262_modes),
- .channel_mode = alc262_modes,
- .input_mux = &alc262_HP_D7000_capture_source,
- .unsol_event = alc262_hp_wildwest_unsol_event,
- .init_hook = alc262_hp_wildwest_automute,
- },
- [ALC262_HP_BPC_D7000_WL] = {
- .mixers = { alc262_HP_BPC_WildWest_mixer,
- alc262_HP_BPC_WildWest_option_mixer },
- .init_verbs = { alc262_HP_BPC_WildWest_init_verbs },
- .num_dacs = ARRAY_SIZE(alc262_dac_nids),
- .dac_nids = alc262_dac_nids,
- .hp_nid = 0x03,
- .num_channel_mode = ARRAY_SIZE(alc262_modes),
- .channel_mode = alc262_modes,
- .input_mux = &alc262_HP_D7000_capture_source,
- .unsol_event = alc262_hp_wildwest_unsol_event,
- .init_hook = alc262_hp_wildwest_automute,
- },
- [ALC262_HP_TC_T5735] = {
- .mixers = { alc262_hp_t5735_mixer },
- .init_verbs = { alc262_init_verbs, alc262_hp_t5735_verbs },
- .num_dacs = ARRAY_SIZE(alc262_dac_nids),
- .dac_nids = alc262_dac_nids,
- .hp_nid = 0x03,
- .num_channel_mode = ARRAY_SIZE(alc262_modes),
- .channel_mode = alc262_modes,
- .input_mux = &alc262_capture_source,
- .unsol_event = alc_sku_unsol_event,
- .setup = alc262_hp_t5735_setup,
- .init_hook = alc_inithook,
- },
- [ALC262_HP_RP5700] = {
- .mixers = { alc262_hp_rp5700_mixer },
- .init_verbs = { alc262_init_verbs, alc262_hp_rp5700_verbs },
- .num_dacs = ARRAY_SIZE(alc262_dac_nids),
- .dac_nids = alc262_dac_nids,
- .num_channel_mode = ARRAY_SIZE(alc262_modes),
- .channel_mode = alc262_modes,
- .input_mux = &alc262_hp_rp5700_capture_source,
- },
- [ALC262_BENQ_ED8] = {
- .mixers = { alc262_base_mixer },
- .init_verbs = { alc262_init_verbs, alc262_EAPD_verbs },
- .num_dacs = ARRAY_SIZE(alc262_dac_nids),
- .dac_nids = alc262_dac_nids,
- .hp_nid = 0x03,
- .num_channel_mode = ARRAY_SIZE(alc262_modes),
- .channel_mode = alc262_modes,
- .input_mux = &alc262_capture_source,
- },
- [ALC262_SONY_ASSAMD] = {
- .mixers = { alc262_sony_mixer },
- .init_verbs = { alc262_init_verbs, alc262_sony_unsol_verbs},
- .num_dacs = ARRAY_SIZE(alc262_dac_nids),
- .dac_nids = alc262_dac_nids,
- .hp_nid = 0x02,
- .num_channel_mode = ARRAY_SIZE(alc262_modes),
- .channel_mode = alc262_modes,
- .input_mux = &alc262_capture_source,
- .unsol_event = alc262_hippo_unsol_event,
- .setup = alc262_hippo_setup,
- .init_hook = alc262_hippo_automute,
- },
- [ALC262_BENQ_T31] = {
- .mixers = { alc262_benq_t31_mixer },
- .init_verbs = { alc262_init_verbs, alc262_benq_t31_EAPD_verbs,
- alc_hp15_unsol_verbs },
- .num_dacs = ARRAY_SIZE(alc262_dac_nids),
- .dac_nids = alc262_dac_nids,
- .hp_nid = 0x03,
- .num_channel_mode = ARRAY_SIZE(alc262_modes),
- .channel_mode = alc262_modes,
- .input_mux = &alc262_capture_source,
- .unsol_event = alc262_hippo_unsol_event,
- .setup = alc262_hippo_setup,
- .init_hook = alc262_hippo_automute,
- },
- [ALC262_ULTRA] = {
- .mixers = { alc262_ultra_mixer },
- .cap_mixer = alc262_ultra_capture_mixer,
- .init_verbs = { alc262_ultra_verbs },
- .num_dacs = ARRAY_SIZE(alc262_dac_nids),
- .dac_nids = alc262_dac_nids,
- .num_channel_mode = ARRAY_SIZE(alc262_modes),
- .channel_mode = alc262_modes,
- .input_mux = &alc262_ultra_capture_source,
- .adc_nids = alc262_adc_nids, /* ADC0 */
- .capsrc_nids = alc262_capsrc_nids,
- .num_adc_nids = 1, /* single ADC */
- .unsol_event = alc262_ultra_unsol_event,
- .init_hook = alc262_ultra_automute,
- },
- [ALC262_LENOVO_3000] = {
- .mixers = { alc262_lenovo_3000_mixer },
- .init_verbs = { alc262_init_verbs, alc262_EAPD_verbs,
- alc262_lenovo_3000_unsol_verbs,
- alc262_lenovo_3000_init_verbs },
- .num_dacs = ARRAY_SIZE(alc262_dac_nids),
- .dac_nids = alc262_dac_nids,
- .hp_nid = 0x03,
- .dig_out_nid = ALC262_DIGOUT_NID,
- .num_channel_mode = ARRAY_SIZE(alc262_modes),
- .channel_mode = alc262_modes,
- .input_mux = &alc262_fujitsu_capture_source,
- .unsol_event = alc262_lenovo_3000_unsol_event,
- },
- [ALC262_NEC] = {
- .mixers = { alc262_nec_mixer },
- .init_verbs = { alc262_nec_verbs },
- .num_dacs = ARRAY_SIZE(alc262_dac_nids),
- .dac_nids = alc262_dac_nids,
- .hp_nid = 0x03,
- .num_channel_mode = ARRAY_SIZE(alc262_modes),
- .channel_mode = alc262_modes,
- .input_mux = &alc262_capture_source,
- },
- [ALC262_TOSHIBA_S06] = {
- .mixers = { alc262_toshiba_s06_mixer },
- .init_verbs = { alc262_init_verbs, alc262_toshiba_s06_verbs,
- alc262_eapd_verbs },
- .num_dacs = ARRAY_SIZE(alc262_dac_nids),
- .capsrc_nids = alc262_dmic_capsrc_nids,
- .dac_nids = alc262_dac_nids,
- .adc_nids = alc262_dmic_adc_nids, /* ADC0 */
- .num_adc_nids = 1, /* single ADC */
- .dig_out_nid = ALC262_DIGOUT_NID,
- .num_channel_mode = ARRAY_SIZE(alc262_modes),
- .channel_mode = alc262_modes,
- .unsol_event = alc_sku_unsol_event,
- .setup = alc262_toshiba_s06_setup,
- .init_hook = alc_inithook,
- },
- [ALC262_TOSHIBA_RX1] = {
- .mixers = { alc262_toshiba_rx1_mixer },
- .init_verbs = { alc262_init_verbs, alc262_toshiba_rx1_unsol_verbs },
- .num_dacs = ARRAY_SIZE(alc262_dac_nids),
- .dac_nids = alc262_dac_nids,
- .hp_nid = 0x03,
- .num_channel_mode = ARRAY_SIZE(alc262_modes),
- .channel_mode = alc262_modes,
- .input_mux = &alc262_capture_source,
- .unsol_event = alc262_hippo_unsol_event,
- .setup = alc262_hippo_setup,
- .init_hook = alc262_hippo_automute,
- },
- [ALC262_TYAN] = {
- .mixers = { alc262_tyan_mixer },
- .init_verbs = { alc262_init_verbs, alc262_tyan_verbs},
- .num_dacs = ARRAY_SIZE(alc262_dac_nids),
- .dac_nids = alc262_dac_nids,
- .hp_nid = 0x02,
- .dig_out_nid = ALC262_DIGOUT_NID,
- .num_channel_mode = ARRAY_SIZE(alc262_modes),
- .channel_mode = alc262_modes,
- .input_mux = &alc262_capture_source,
- .unsol_event = alc_automute_amp_unsol_event,
- .setup = alc262_tyan_setup,
- .init_hook = alc_automute_amp,
- },
-};
+static void alc5505_dsp_halt(struct hda_codec *codec)
+{
+ unsigned int val;
-static int patch_alc262(struct hda_codec *codec)
+ alc5505_coef_set(codec, 0x3000, 0x000c); /* DSP CPU stop */
+ alc5505_coef_set(codec, 0x880c, 0x0008); /* DDR enter self refresh */
+ alc5505_coef_set(codec, 0x61c0, 0x11110080); /* Clock control for PLL and CPU */
+ alc5505_coef_set(codec, 0x6230, 0xfc0d4011); /* Disable Input OP */
+ alc5505_coef_set(codec, 0x61b4, 0x040a2b03); /* Stop PLL2 */
+ alc5505_coef_set(codec, 0x61b0, 0x00005b17); /* Stop PLL1 */
+ alc5505_coef_set(codec, 0x61b8, 0x04133303); /* Stop PLL3 */
+ val = alc5505_coef_get(codec, 0x6220);
+ alc5505_coef_set(codec, 0x6220, (val | 0x3000)); /* switch Ringbuffer clock to DBUS clock */
+}
+
+static void alc5505_dsp_back_from_halt(struct hda_codec *codec)
{
- struct alc_spec *spec;
- int board_config;
- int err;
+ alc5505_coef_set(codec, 0x61b8, 0x04133302);
+ alc5505_coef_set(codec, 0x61b0, 0x00005b16);
+ alc5505_coef_set(codec, 0x61b4, 0x040a2b02);
+ alc5505_coef_set(codec, 0x6230, 0xf80d4011);
+ alc5505_coef_set(codec, 0x6220, 0x2002010f);
+ alc5505_coef_set(codec, 0x880c, 0x00000004);
+}
- spec = kzalloc(sizeof(*spec), GFP_KERNEL);
- if (spec == NULL)
- return -ENOMEM;
+static void alc5505_dsp_init(struct hda_codec *codec)
+{
+ unsigned int val;
- codec->spec = spec;
-#if 0
- /* pshou 07/11/05 set a zero PCM sample to DAC when FIFO is
- * under-run
- */
- {
- int tmp;
- snd_hda_codec_write(codec, 0x1a, 0, AC_VERB_SET_COEF_INDEX, 7);
- tmp = snd_hda_codec_read(codec, 0x20, 0, AC_VERB_GET_PROC_COEF, 0);
- snd_hda_codec_write(codec, 0x1a, 0, AC_VERB_SET_COEF_INDEX, 7);
- snd_hda_codec_write(codec, 0x1a, 0, AC_VERB_SET_PROC_COEF, tmp | 0x80);
- }
+ alc5505_dsp_halt(codec);
+ alc5505_dsp_back_from_halt(codec);
+ alc5505_coef_set(codec, 0x61b0, 0x5b14); /* PLL1 control */
+ alc5505_coef_set(codec, 0x61b0, 0x5b16);
+ alc5505_coef_set(codec, 0x61b4, 0x04132b00); /* PLL2 control */
+ alc5505_coef_set(codec, 0x61b4, 0x04132b02);
+ alc5505_coef_set(codec, 0x61b8, 0x041f3300); /* PLL3 control*/
+ alc5505_coef_set(codec, 0x61b8, 0x041f3302);
+ snd_hda_codec_write(codec, 0x51, 0, AC_VERB_SET_CODEC_RESET, 0); /* Function reset */
+ alc5505_coef_set(codec, 0x61b8, 0x041b3302);
+ alc5505_coef_set(codec, 0x61b8, 0x04173302);
+ alc5505_coef_set(codec, 0x61b8, 0x04163302);
+ alc5505_coef_set(codec, 0x8800, 0x348b328b); /* DRAM control */
+ alc5505_coef_set(codec, 0x8808, 0x00020022); /* DRAM control */
+ alc5505_coef_set(codec, 0x8818, 0x00000400); /* DRAM control */
+
+ val = alc5505_coef_get(codec, 0x6200) >> 16; /* Read revision ID */
+ if (val <= 3)
+ alc5505_coef_set(codec, 0x6220, 0x2002010f); /* I/O PAD Configuration */
+ else
+ alc5505_coef_set(codec, 0x6220, 0x6002018f);
+
+ alc5505_coef_set(codec, 0x61ac, 0x055525f0); /**/
+ alc5505_coef_set(codec, 0x61c0, 0x12230080); /* Clock control */
+ alc5505_coef_set(codec, 0x61b4, 0x040e2b02); /* PLL2 control */
+ alc5505_coef_set(codec, 0x61bc, 0x010234f8); /* OSC Control */
+ alc5505_coef_set(codec, 0x880c, 0x00000004); /* DRAM Function control */
+ alc5505_coef_set(codec, 0x880c, 0x00000003);
+ alc5505_coef_set(codec, 0x880c, 0x00000010);
+
+#ifdef HALT_REALTEK_ALC5505
+ alc5505_dsp_halt(codec);
#endif
- alc_auto_parse_customize_define(codec);
-
- alc_fix_pll_init(codec, 0x20, 0x0a, 10);
+}
- board_config = snd_hda_check_board_config(codec, ALC262_MODEL_LAST,
- alc262_models,
- alc262_cfg_tbl);
+#ifdef HALT_REALTEK_ALC5505
+#define alc5505_dsp_suspend(codec) /* NOP */
+#define alc5505_dsp_resume(codec) /* NOP */
+#else
+#define alc5505_dsp_suspend(codec) alc5505_dsp_halt(codec)
+#define alc5505_dsp_resume(codec) alc5505_dsp_back_from_halt(codec)
+#endif
- if (board_config < 0) {
- printk(KERN_INFO "hda_codec: %s: BIOS auto-probing.\n",
- codec->chip_name);
- board_config = ALC262_AUTO;
- }
+#ifdef CONFIG_PM
+static int alc269_suspend(struct hda_codec *codec)
+{
+ struct alc_spec *spec = codec->spec;
- if (board_config == ALC262_AUTO) {
- alc_pick_fixup(codec, NULL, alc262_fixup_tbl, alc262_fixups);
- alc_apply_fixup(codec, ALC_FIXUP_ACT_PRE_PROBE);
- }
+ if (spec->has_alc5505_dsp)
+ alc5505_dsp_suspend(codec);
+ return alc_suspend(codec);
+}
- if (board_config == ALC262_AUTO) {
- /* automatic parse from the BIOS config */
- err = alc262_parse_auto_config(codec);
- if (err < 0) {
- alc_free(codec);
- return err;
- } else if (!err) {
- printk(KERN_INFO
- "hda_codec: Cannot set up configuration "
- "from BIOS. Using base mode...\n");
- board_config = ALC262_BASIC;
- }
- }
+static int alc269_resume(struct hda_codec *codec)
+{
+ struct alc_spec *spec = codec->spec;
- if (!spec->no_analog && has_cdefine_beep(codec)) {
- err = snd_hda_attach_beep_device(codec, 0x1);
- if (err < 0) {
- alc_free(codec);
- return err;
- }
+ if (spec->codec_variant == ALC269_TYPE_ALC269VB)
+ alc269vb_toggle_power_output(codec, 0);
+ if (spec->codec_variant == ALC269_TYPE_ALC269VB &&
+ (alc_get_coef0(codec) & 0x00ff) == 0x018) {
+ msleep(150);
}
- if (board_config != ALC262_AUTO)
- setup_preset(codec, &alc262_presets[board_config]);
-
- spec->stream_analog_playback = &alc262_pcm_analog_playback;
- spec->stream_analog_capture = &alc262_pcm_analog_capture;
-
- spec->stream_digital_playback = &alc262_pcm_digital_playback;
- spec->stream_digital_capture = &alc262_pcm_digital_capture;
+ codec->patch_ops.init(codec);
- if (!spec->adc_nids && spec->input_mux) {
- int i;
- /* check whether the digital-mic has to be supported */
- for (i = 0; i < spec->input_mux->num_items; i++) {
- if (spec->input_mux->items[i].index >= 9)
- break;
- }
- if (i < spec->input_mux->num_items) {
- /* use only ADC0 */
- spec->adc_nids = alc262_dmic_adc_nids;
- spec->num_adc_nids = 1;
- spec->capsrc_nids = alc262_dmic_capsrc_nids;
- } else {
- /* all analog inputs */
- /* check whether NID 0x07 is valid */
- unsigned int wcap = get_wcaps(codec, 0x07);
-
- /* get type */
- wcap = get_wcaps_type(wcap);
- if (wcap != AC_WID_AUD_IN) {
- spec->adc_nids = alc262_adc_nids_alt;
- spec->num_adc_nids =
- ARRAY_SIZE(alc262_adc_nids_alt);
- spec->capsrc_nids = alc262_capsrc_nids_alt;
- } else {
- spec->adc_nids = alc262_adc_nids;
- spec->num_adc_nids =
- ARRAY_SIZE(alc262_adc_nids);
- spec->capsrc_nids = alc262_capsrc_nids;
- }
- }
+ if (spec->codec_variant == ALC269_TYPE_ALC269VB)
+ alc269vb_toggle_power_output(codec, 1);
+ if (spec->codec_variant == ALC269_TYPE_ALC269VB &&
+ (alc_get_coef0(codec) & 0x00ff) == 0x017) {
+ msleep(200);
}
- if (!spec->cap_mixer && !spec->no_analog)
- set_capture_mixer(codec);
- if (!spec->no_analog && has_cdefine_beep(codec))
- set_beep_amp(spec, 0x0b, 0x05, HDA_INPUT);
-
- alc_apply_fixup(codec, ALC_FIXUP_ACT_PROBE);
- spec->vmaster_nid = 0x0c;
-
- codec->patch_ops = alc_patch_ops;
- if (board_config == ALC262_AUTO)
- spec->init_hook = alc262_auto_init;
-
- alc_init_jacks(codec);
-#ifdef CONFIG_SND_HDA_POWER_SAVE
- if (!spec->loopback.amplist)
- spec->loopback.amplist = alc262_loopbacks;
-#endif
+ snd_hda_codec_resume_amp(codec);
+ snd_hda_codec_resume_cache(codec);
+ alc_inv_dmic_sync(codec, true);
+ hda_call_check_power_status(codec, 0x01);
+ if (spec->has_alc5505_dsp)
+ alc5505_dsp_resume(codec);
return 0;
}
+#endif /* CONFIG_PM */
-/*
- * ALC268 channel source setting (2 channel)
- */
-#define ALC268_DIGOUT_NID ALC880_DIGOUT_NID
-#define alc268_modes alc260_modes
-
-static hda_nid_t alc268_dac_nids[2] = {
- /* front, hp */
- 0x02, 0x03
-};
-
-static hda_nid_t alc268_adc_nids[2] = {
- /* ADC0-1 */
- 0x08, 0x07
-};
-
-static hda_nid_t alc268_adc_nids_alt[1] = {
- /* ADC0 */
- 0x08
-};
-
-static hda_nid_t alc268_capsrc_nids[2] = { 0x23, 0x24 };
-
-static struct snd_kcontrol_new alc268_base_mixer[] = {
- /* output mixer control */
- HDA_CODEC_VOLUME("Front Playback Volume", 0x2, 0x0, HDA_OUTPUT),
- HDA_CODEC_MUTE("Front Playback Switch", 0x14, 0x0, HDA_OUTPUT),
- HDA_CODEC_VOLUME("Headphone Playback Volume", 0x3, 0x0, HDA_OUTPUT),
- HDA_CODEC_MUTE("Headphone Playback Switch", 0x15, 0x0, HDA_OUTPUT),
- HDA_CODEC_VOLUME("Mic Boost Volume", 0x18, 0, HDA_INPUT),
- HDA_CODEC_VOLUME("Front Mic Boost Volume", 0x19, 0, HDA_INPUT),
- HDA_CODEC_VOLUME("Line In Boost Volume", 0x1a, 0, HDA_INPUT),
- { }
-};
-
-static struct snd_kcontrol_new alc268_toshiba_mixer[] = {
- /* output mixer control */
- HDA_CODEC_VOLUME("Front Playback Volume", 0x2, 0x0, HDA_OUTPUT),
- HDA_CODEC_VOLUME("Headphone Playback Volume", 0x3, 0x0, HDA_OUTPUT),
- ALC262_HIPPO_MASTER_SWITCH,
- HDA_CODEC_VOLUME("Mic Boost Volume", 0x18, 0, HDA_INPUT),
- HDA_CODEC_VOLUME("Front Mic Boost Volume", 0x19, 0, HDA_INPUT),
- HDA_CODEC_VOLUME("Line In Boost Volume", 0x1a, 0, HDA_INPUT),
- { }
-};
-
-/* bind Beep switches of both NID 0x0f and 0x10 */
-static struct hda_bind_ctls alc268_bind_beep_sw = {
- .ops = &snd_hda_bind_sw,
- .values = {
- HDA_COMPOSE_AMP_VAL(0x0f, 3, 1, HDA_INPUT),
- HDA_COMPOSE_AMP_VAL(0x10, 3, 1, HDA_INPUT),
- 0
- },
-};
-
-static struct snd_kcontrol_new alc268_beep_mixer[] = {
- HDA_CODEC_VOLUME("Beep Playback Volume", 0x1d, 0x0, HDA_INPUT),
- HDA_BIND_SW("Beep Playback Switch", &alc268_bind_beep_sw),
- { }
-};
+static void alc269_fixup_pincfg_no_hp_to_lineout(struct hda_codec *codec,
+ const struct hda_fixup *fix, int action)
+{
+ struct alc_spec *spec = codec->spec;
-static struct hda_verb alc268_eapd_verbs[] = {
- {0x14, AC_VERB_SET_EAPD_BTLENABLE, 2},
- {0x15, AC_VERB_SET_EAPD_BTLENABLE, 2},
- { }
-};
+ if (action == HDA_FIXUP_ACT_PRE_PROBE)
+ spec->parse_flags = HDA_PINCFG_NO_HP_FIXUP;
+}
-/* Toshiba specific */
-static struct hda_verb alc268_toshiba_verbs[] = {
- {0x15, AC_VERB_SET_UNSOLICITED_ENABLE, ALC880_HP_EVENT | AC_USRSP_EN},
- { } /* end */
-};
+static void alc269_fixup_hweq(struct hda_codec *codec,
+ const struct hda_fixup *fix, int action)
+{
+ int coef;
-/* Acer specific */
-/* bind volumes of both NID 0x02 and 0x03 */
-static struct hda_bind_ctls alc268_acer_bind_master_vol = {
- .ops = &snd_hda_bind_vol,
- .values = {
- HDA_COMPOSE_AMP_VAL(0x02, 3, 0, HDA_OUTPUT),
- HDA_COMPOSE_AMP_VAL(0x03, 3, 0, HDA_OUTPUT),
- 0
- },
-};
+ if (action != HDA_FIXUP_ACT_INIT)
+ return;
+ coef = alc_read_coef_idx(codec, 0x1e);
+ alc_write_coef_idx(codec, 0x1e, coef | 0x80);
+}
-/* mute/unmute internal speaker according to the hp jack and mute state */
-static void alc268_acer_automute(struct hda_codec *codec, int force)
+static void alc269_fixup_headset_mic(struct hda_codec *codec,
+ const struct hda_fixup *fix, int action)
{
struct alc_spec *spec = codec->spec;
- unsigned int mute;
- if (force || !spec->sense_updated) {
- spec->jack_present = snd_hda_jack_detect(codec, 0x14);
- spec->sense_updated = 1;
- }
- if (spec->jack_present)
- mute = HDA_AMP_MUTE; /* mute internal speaker */
- else /* unmute internal speaker if necessary */
- mute = snd_hda_codec_amp_read(codec, 0x14, 0, HDA_OUTPUT, 0);
- snd_hda_codec_amp_stereo(codec, 0x15, HDA_OUTPUT, 0,
- HDA_AMP_MUTE, mute);
+ if (action == HDA_FIXUP_ACT_PRE_PROBE)
+ spec->parse_flags |= HDA_PINCFG_HEADSET_MIC;
}
-
-/* bind hp and internal speaker mute (with plug check) */
-static int alc268_acer_master_sw_put(struct snd_kcontrol *kcontrol,
- struct snd_ctl_elem_value *ucontrol)
+static void alc271_fixup_dmic(struct hda_codec *codec,
+ const struct hda_fixup *fix, int action)
{
- struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
- long *valp = ucontrol->value.integer.value;
- int change;
+ static const struct hda_verb verbs[] = {
+ {0x20, AC_VERB_SET_COEF_INDEX, 0x0d},
+ {0x20, AC_VERB_SET_PROC_COEF, 0x4000},
+ {}
+ };
+ unsigned int cfg;
- change = amp_stereo_mute_update(codec, 0x14, HDA_OUTPUT, 0, valp);
- if (change)
- alc268_acer_automute(codec, 0);
- return change;
+ if (strcmp(codec->chip_name, "ALC271X") &&
+ strcmp(codec->chip_name, "ALC269VB"))
+ return;
+ cfg = snd_hda_codec_get_pincfg(codec, 0x12);
+ if (get_defcfg_connect(cfg) == AC_JACK_PORT_FIXED)
+ snd_hda_sequence_write(codec, verbs);
}
-static struct snd_kcontrol_new alc268_acer_aspire_one_mixer[] = {
- /* output mixer control */
- HDA_BIND_VOL("Master Playback Volume", &alc268_acer_bind_master_vol),
- {
- .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
- .name = "Master Playback Switch",
- .subdevice = HDA_SUBDEV_AMP_FLAG,
- .info = snd_hda_mixer_amp_switch_info,
- .get = snd_hda_mixer_amp_switch_get,
- .put = alc268_acer_master_sw_put,
- .private_value = HDA_COMPOSE_AMP_VAL(0x14, 3, 0, HDA_OUTPUT),
- },
- HDA_CODEC_VOLUME("Mic Boost Capture Volume", 0x18, 0, HDA_INPUT),
- { }
-};
-
-static struct snd_kcontrol_new alc268_acer_mixer[] = {
- /* output mixer control */
- HDA_BIND_VOL("Master Playback Volume", &alc268_acer_bind_master_vol),
- {
- .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
- .name = "Master Playback Switch",
- .subdevice = HDA_SUBDEV_AMP_FLAG,
- .info = snd_hda_mixer_amp_switch_info,
- .get = snd_hda_mixer_amp_switch_get,
- .put = alc268_acer_master_sw_put,
- .private_value = HDA_COMPOSE_AMP_VAL(0x14, 3, 0, HDA_OUTPUT),
- },
- HDA_CODEC_VOLUME("Mic Boost Volume", 0x18, 0, HDA_INPUT),
- HDA_CODEC_VOLUME("Internal Mic Boost Volume", 0x19, 0, HDA_INPUT),
- HDA_CODEC_VOLUME("Line In Boost Volume", 0x1a, 0, HDA_INPUT),
- { }
-};
-
-static struct snd_kcontrol_new alc268_acer_dmic_mixer[] = {
- /* output mixer control */
- HDA_BIND_VOL("Master Playback Volume", &alc268_acer_bind_master_vol),
- {
- .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
- .name = "Master Playback Switch",
- .subdevice = HDA_SUBDEV_AMP_FLAG,
- .info = snd_hda_mixer_amp_switch_info,
- .get = snd_hda_mixer_amp_switch_get,
- .put = alc268_acer_master_sw_put,
- .private_value = HDA_COMPOSE_AMP_VAL(0x14, 3, 0, HDA_OUTPUT),
- },
- HDA_CODEC_VOLUME("Mic Boost Volume", 0x18, 0, HDA_INPUT),
- HDA_CODEC_VOLUME("Line In Boost Volume", 0x1a, 0, HDA_INPUT),
- { }
-};
-
-static struct hda_verb alc268_acer_aspire_one_verbs[] = {
- {0x12, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN},
- {0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP},
- {0x15, AC_VERB_SET_UNSOLICITED_ENABLE, ALC880_HP_EVENT | AC_USRSP_EN},
- {0x18, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | ALC880_MIC_EVENT},
- {0x23, AC_VERB_SET_CONNECT_SEL, 0x06},
- {0x23, AC_VERB_SET_AMP_GAIN_MUTE, 0xa017},
- { }
-};
+static void alc269_fixup_pcm_44k(struct hda_codec *codec,
+ const struct hda_fixup *fix, int action)
+{
+ struct alc_spec *spec = codec->spec;
-static struct hda_verb alc268_acer_verbs[] = {
- {0x12, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN}, /* internal dmic? */
- {0x13, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN},
- {0x14, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP},
- {0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
- {0x18, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80},
- {0x1a, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80},
- {0x14, AC_VERB_SET_UNSOLICITED_ENABLE, ALC880_HP_EVENT | AC_USRSP_EN},
- { }
-};
+ if (action != HDA_FIXUP_ACT_PROBE)
+ return;
-/* unsolicited event for HP jack sensing */
-#define alc268_toshiba_unsol_event alc262_hippo_unsol_event
-#define alc268_toshiba_setup alc262_hippo_setup
-#define alc268_toshiba_automute alc262_hippo_automute
+ /* Due to a hardware problem on Lenovo Ideadpad, we need to
+ * fix the sample rate of analog I/O to 44.1kHz
+ */
+ spec->gen.stream_analog_playback = &alc269_44k_pcm_analog_playback;
+ spec->gen.stream_analog_capture = &alc269_44k_pcm_analog_capture;
+}
-static void alc268_acer_unsol_event(struct hda_codec *codec,
- unsigned int res)
+static void alc269_fixup_stereo_dmic(struct hda_codec *codec,
+ const struct hda_fixup *fix, int action)
{
- if ((res >> 26) != ALC880_HP_EVENT)
+ int coef;
+
+ if (action != HDA_FIXUP_ACT_INIT)
return;
- alc268_acer_automute(codec, 1);
+ /* The digital-mic unit sends PDM (differential signal) instead of
+ * the standard PCM, thus you can't record a valid mono stream as is.
+ * Below is a workaround specific to ALC269 to control the dmic
+ * signal source as mono.
+ */
+ coef = alc_read_coef_idx(codec, 0x07);
+ alc_write_coef_idx(codec, 0x07, coef | 0x80);
}
-static void alc268_acer_init_hook(struct hda_codec *codec)
+static void alc269_quanta_automute(struct hda_codec *codec)
{
- alc268_acer_automute(codec, 1);
-}
+ snd_hda_gen_update_outputs(codec);
-/* toggle speaker-output according to the hp-jack state */
-static void alc268_aspire_one_speaker_automute(struct hda_codec *codec)
-{
- unsigned int present;
- unsigned char bits;
-
- present = snd_hda_jack_detect(codec, 0x15);
- bits = present ? HDA_AMP_MUTE : 0;
- snd_hda_codec_amp_stereo(codec, 0x0f, HDA_INPUT, 0,
- HDA_AMP_MUTE, bits);
- snd_hda_codec_amp_stereo(codec, 0x0f, HDA_INPUT, 1,
- HDA_AMP_MUTE, bits);
+ snd_hda_codec_write(codec, 0x20, 0,
+ AC_VERB_SET_COEF_INDEX, 0x0c);
+ snd_hda_codec_write(codec, 0x20, 0,
+ AC_VERB_SET_PROC_COEF, 0x680);
+
+ snd_hda_codec_write(codec, 0x20, 0,
+ AC_VERB_SET_COEF_INDEX, 0x0c);
+ snd_hda_codec_write(codec, 0x20, 0,
+ AC_VERB_SET_PROC_COEF, 0x480);
}
-static void alc268_acer_lc_unsol_event(struct hda_codec *codec,
- unsigned int res)
+static void alc269_fixup_quanta_mute(struct hda_codec *codec,
+ const struct hda_fixup *fix, int action)
{
- switch (res >> 26) {
- case ALC880_HP_EVENT:
- alc268_aspire_one_speaker_automute(codec);
- break;
- case ALC880_MIC_EVENT:
- alc_mic_automute(codec);
- break;
- }
+ struct alc_spec *spec = codec->spec;
+ if (action != HDA_FIXUP_ACT_PROBE)
+ return;
+ spec->gen.automute_hook = alc269_quanta_automute;
}
-static void alc268_acer_lc_setup(struct hda_codec *codec)
+static void alc269_x101_hp_automute_hook(struct hda_codec *codec,
+ struct hda_jack_tbl *jack)
{
struct alc_spec *spec = codec->spec;
- spec->ext_mic.pin = 0x18;
- spec->ext_mic.mux_idx = 0;
- spec->int_mic.pin = 0x12;
- spec->int_mic.mux_idx = 6;
- spec->auto_mic = 1;
+ int vref;
+ msleep(200);
+ snd_hda_gen_hp_automute(codec, jack);
+
+ vref = spec->gen.hp_jack_present ? PIN_VREF80 : 0;
+ msleep(100);
+ snd_hda_codec_write(codec, 0x18, 0, AC_VERB_SET_PIN_WIDGET_CONTROL,
+ vref);
+ msleep(500);
+ snd_hda_codec_write(codec, 0x18, 0, AC_VERB_SET_PIN_WIDGET_CONTROL,
+ vref);
}
-static void alc268_acer_lc_init_hook(struct hda_codec *codec)
+static void alc269_fixup_x101_headset_mic(struct hda_codec *codec,
+ const struct hda_fixup *fix, int action)
{
- alc268_aspire_one_speaker_automute(codec);
- alc_mic_automute(codec);
+ struct alc_spec *spec = codec->spec;
+ if (action == HDA_FIXUP_ACT_PRE_PROBE) {
+ spec->parse_flags |= HDA_PINCFG_HEADSET_MIC;
+ spec->gen.hp_automute_hook = alc269_x101_hp_automute_hook;
+ }
}
-static struct snd_kcontrol_new alc268_dell_mixer[] = {
- /* output mixer control */
- HDA_CODEC_VOLUME("Speaker Playback Volume", 0x02, 0x0, HDA_OUTPUT),
- HDA_CODEC_MUTE("Speaker Playback Switch", 0x14, 0x0, HDA_OUTPUT),
- HDA_CODEC_VOLUME("Headphone Playback Volume", 0x03, 0x0, HDA_OUTPUT),
- HDA_CODEC_MUTE("Headphone Playback Switch", 0x15, 0x0, HDA_OUTPUT),
- HDA_CODEC_VOLUME("Mic Boost Volume", 0x18, 0, HDA_INPUT),
- HDA_CODEC_VOLUME("Internal Mic Boost Volume", 0x19, 0, HDA_INPUT),
- { }
-};
-
-static struct hda_verb alc268_dell_verbs[] = {
- {0x14, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP},
- {0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP},
- {0x15, AC_VERB_SET_UNSOLICITED_ENABLE, ALC880_HP_EVENT | AC_USRSP_EN},
- {0x18, AC_VERB_SET_UNSOLICITED_ENABLE, ALC880_MIC_EVENT | AC_USRSP_EN},
- { }
-};
-/* mute/unmute internal speaker according to the hp jack and mute state */
-static void alc268_dell_setup(struct hda_codec *codec)
+/* update mute-LED according to the speaker mute state via mic VREF pin */
+static void alc269_fixup_mic_mute_hook(void *private_data, int enabled)
{
+ struct hda_codec *codec = private_data;
struct alc_spec *spec = codec->spec;
+ unsigned int pinval;
- spec->autocfg.hp_pins[0] = 0x15;
- spec->autocfg.speaker_pins[0] = 0x14;
- spec->ext_mic.pin = 0x18;
- spec->ext_mic.mux_idx = 0;
- spec->int_mic.pin = 0x19;
- spec->int_mic.mux_idx = 1;
- spec->auto_mic = 1;
+ if (spec->mute_led_polarity)
+ enabled = !enabled;
+ pinval = snd_hda_codec_get_pin_target(codec, spec->mute_led_nid);
+ pinval &= ~AC_PINCTL_VREFEN;
+ pinval |= enabled ? AC_PINCTL_VREF_HIZ : AC_PINCTL_VREF_80;
+ if (spec->mute_led_nid)
+ snd_hda_set_pin_ctl_cache(codec, spec->mute_led_nid, pinval);
}
-static struct snd_kcontrol_new alc267_quanta_il1_mixer[] = {
- HDA_CODEC_VOLUME("Speaker Playback Volume", 0x2, 0x0, HDA_OUTPUT),
- HDA_CODEC_MUTE("Speaker Playback Switch", 0x14, 0x0, HDA_OUTPUT),
- HDA_CODEC_VOLUME("Headphone Playback Volume", 0x3, 0x0, HDA_OUTPUT),
- HDA_CODEC_MUTE("Headphone Playback Switch", 0x15, 0x0, HDA_OUTPUT),
- HDA_CODEC_VOLUME("Mic Capture Volume", 0x23, 0x0, HDA_OUTPUT),
- HDA_BIND_MUTE("Mic Capture Switch", 0x23, 2, HDA_OUTPUT),
- HDA_CODEC_VOLUME("Mic Boost Volume", 0x18, 0, HDA_INPUT),
- HDA_CODEC_VOLUME("Internal Mic Boost Volume", 0x19, 0, HDA_INPUT),
- { }
-};
-
-static struct hda_verb alc267_quanta_il1_verbs[] = {
- {0x15, AC_VERB_SET_UNSOLICITED_ENABLE, ALC880_HP_EVENT | AC_USRSP_EN},
- {0x18, AC_VERB_SET_UNSOLICITED_ENABLE, ALC880_MIC_EVENT | AC_USRSP_EN},
- { }
-};
-
-static void alc267_quanta_il1_setup(struct hda_codec *codec)
+/* Make sure the led works even in runtime suspend */
+static unsigned int led_power_filter(struct hda_codec *codec,
+ hda_nid_t nid,
+ unsigned int power_state)
{
struct alc_spec *spec = codec->spec;
- spec->autocfg.hp_pins[0] = 0x15;
- spec->autocfg.speaker_pins[0] = 0x14;
- spec->ext_mic.pin = 0x18;
- spec->ext_mic.mux_idx = 0;
- spec->int_mic.pin = 0x19;
- spec->int_mic.mux_idx = 1;
- spec->auto_mic = 1;
-}
-/*
- * generic initialization of ADC, input mixers and output mixers
- */
-static struct hda_verb alc268_base_init_verbs[] = {
- /* Unmute DAC0-1 and set vol = 0 */
- {0x02, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
- {0x03, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
+ if (power_state != AC_PWRST_D3 || nid != spec->mute_led_nid)
+ return power_state;
- /*
- * Set up output mixers (0x0c - 0x0e)
- */
- /* set vol=0 to output mixers */
- {0x0e, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
- {0x0e, AC_VERB_SET_CONNECT_SEL, 0x00},
-
- {0x0f, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
- {0x10, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
-
- {0x14, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x40},
- {0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, 0xc0},
- {0x16, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x40},
- {0x18, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x24},
- {0x19, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x24},
- {0x1a, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x20},
- {0x1c, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x20},
- {0x1d, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x20},
-
- {0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
- {0x15, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
- {0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
- {0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
- {0x1a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
-
- /* set PCBEEP vol = 0, mute connections */
- {0x1d, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
- {0x0f, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
- {0x10, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
+ /* Set pin ctl again, it might have just been set to 0 */
+ snd_hda_set_pin_ctl(codec, nid,
+ snd_hda_codec_get_pin_target(codec, nid));
- /* Unmute Selector 23h,24h and set the default input to mic-in */
-
- {0x23, AC_VERB_SET_CONNECT_SEL, 0x00},
- {0x23, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
- {0x24, AC_VERB_SET_CONNECT_SEL, 0x00},
- {0x24, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
-
- { }
-};
-
-/*
- * generic initialization of ADC, input mixers and output mixers
- */
-static struct hda_verb alc268_volume_init_verbs[] = {
- /* set output DAC */
- {0x02, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
- {0x03, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
-
- {0x18, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x24},
- {0x19, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x24},
- {0x1a, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x20},
- {0x1c, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x20},
- {0x1d, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x20},
-
- {0x0e, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
- {0x0f, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
- {0x10, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
-
- {0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
- {0x1a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
-
- /* set PCBEEP vol = 0, mute connections */
- {0x1d, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
- {0x0f, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
- {0x10, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
-
- { }
-};
-
-static struct snd_kcontrol_new alc268_capture_nosrc_mixer[] = {
- HDA_CODEC_VOLUME("Capture Volume", 0x23, 0x0, HDA_OUTPUT),
- HDA_CODEC_MUTE("Capture Switch", 0x23, 0x0, HDA_OUTPUT),
- { } /* end */
-};
-
-static struct snd_kcontrol_new alc268_capture_alt_mixer[] = {
- HDA_CODEC_VOLUME("Capture Volume", 0x23, 0x0, HDA_OUTPUT),
- HDA_CODEC_MUTE("Capture Switch", 0x23, 0x0, HDA_OUTPUT),
- _DEFINE_CAPSRC(1),
- { } /* end */
-};
-
-static struct snd_kcontrol_new alc268_capture_mixer[] = {
- HDA_CODEC_VOLUME("Capture Volume", 0x23, 0x0, HDA_OUTPUT),
- HDA_CODEC_MUTE("Capture Switch", 0x23, 0x0, HDA_OUTPUT),
- HDA_CODEC_VOLUME_IDX("Capture Volume", 1, 0x24, 0x0, HDA_OUTPUT),
- HDA_CODEC_MUTE_IDX("Capture Switch", 1, 0x24, 0x0, HDA_OUTPUT),
- _DEFINE_CAPSRC(2),
- { } /* end */
-};
-
-static struct hda_input_mux alc268_capture_source = {
- .num_items = 4,
- .items = {
- { "Mic", 0x0 },
- { "Front Mic", 0x1 },
- { "Line", 0x2 },
- { "CD", 0x3 },
- },
-};
-
-static struct hda_input_mux alc268_acer_capture_source = {
- .num_items = 3,
- .items = {
- { "Mic", 0x0 },
- { "Internal Mic", 0x1 },
- { "Line", 0x2 },
- },
-};
-
-static struct hda_input_mux alc268_acer_dmic_capture_source = {
- .num_items = 3,
- .items = {
- { "Mic", 0x0 },
- { "Internal Mic", 0x6 },
- { "Line", 0x2 },
- },
-};
-
-#ifdef CONFIG_SND_DEBUG
-static struct snd_kcontrol_new alc268_test_mixer[] = {
- /* Volume widgets */
- HDA_CODEC_VOLUME("LOUT1 Playback Volume", 0x02, 0x0, HDA_OUTPUT),
- HDA_CODEC_VOLUME("LOUT2 Playback Volume", 0x03, 0x0, HDA_OUTPUT),
- HDA_BIND_MUTE_MONO("Mono sum Playback Switch", 0x0e, 1, 2, HDA_INPUT),
- HDA_BIND_MUTE("LINE-OUT sum Playback Switch", 0x0f, 2, HDA_INPUT),
- HDA_BIND_MUTE("HP-OUT sum Playback Switch", 0x10, 2, HDA_INPUT),
- HDA_BIND_MUTE("LINE-OUT Playback Switch", 0x14, 2, HDA_OUTPUT),
- HDA_BIND_MUTE("HP-OUT Playback Switch", 0x15, 2, HDA_OUTPUT),
- HDA_BIND_MUTE("Mono Playback Switch", 0x16, 2, HDA_OUTPUT),
- HDA_CODEC_VOLUME("MIC1 Capture Volume", 0x18, 0x0, HDA_INPUT),
- HDA_BIND_MUTE("MIC1 Capture Switch", 0x18, 2, HDA_OUTPUT),
- HDA_CODEC_VOLUME("MIC2 Capture Volume", 0x19, 0x0, HDA_INPUT),
- HDA_CODEC_VOLUME("LINE1 Capture Volume", 0x1a, 0x0, HDA_INPUT),
- HDA_BIND_MUTE("LINE1 Capture Switch", 0x1a, 2, HDA_OUTPUT),
- /* The below appears problematic on some hardwares */
- /*HDA_CODEC_VOLUME("PCBEEP Playback Volume", 0x1d, 0x0, HDA_INPUT),*/
- HDA_CODEC_VOLUME("PCM-IN1 Capture Volume", 0x23, 0x0, HDA_OUTPUT),
- HDA_BIND_MUTE("PCM-IN1 Capture Switch", 0x23, 2, HDA_OUTPUT),
- HDA_CODEC_VOLUME("PCM-IN2 Capture Volume", 0x24, 0x0, HDA_OUTPUT),
- HDA_BIND_MUTE("PCM-IN2 Capture Switch", 0x24, 2, HDA_OUTPUT),
-
- /* Modes for retasking pin widgets */
- ALC_PIN_MODE("LINE-OUT pin mode", 0x14, ALC_PIN_DIR_INOUT),
- ALC_PIN_MODE("HP-OUT pin mode", 0x15, ALC_PIN_DIR_INOUT),
- ALC_PIN_MODE("MIC1 pin mode", 0x18, ALC_PIN_DIR_INOUT),
- ALC_PIN_MODE("LINE1 pin mode", 0x1a, ALC_PIN_DIR_INOUT),
-
- /* Controls for GPIO pins, assuming they are configured as outputs */
- ALC_GPIO_DATA_SWITCH("GPIO pin 0", 0x01, 0x01),
- ALC_GPIO_DATA_SWITCH("GPIO pin 1", 0x01, 0x02),
- ALC_GPIO_DATA_SWITCH("GPIO pin 2", 0x01, 0x04),
- ALC_GPIO_DATA_SWITCH("GPIO pin 3", 0x01, 0x08),
-
- /* Switches to allow the digital SPDIF output pin to be enabled.
- * The ALC268 does not have an SPDIF input.
- */
- ALC_SPDIF_CTRL_SWITCH("SPDIF Playback Switch", 0x06, 0x01),
-
- /* A switch allowing EAPD to be enabled. Some laptops seem to use
- * this output to turn on an external amplifier.
- */
- ALC_EAPD_CTRL_SWITCH("LINE-OUT EAPD Enable Switch", 0x0f, 0x02),
- ALC_EAPD_CTRL_SWITCH("HP-OUT EAPD Enable Switch", 0x10, 0x02),
-
- { } /* end */
-};
-#endif
+ return AC_PWRST_D0;
+}
-/* create input playback/capture controls for the given pin */
-static int alc268_new_analog_output(struct alc_spec *spec, hda_nid_t nid,
- const char *ctlname, int idx)
+static void alc269_fixup_hp_mute_led(struct hda_codec *codec,
+ const struct hda_fixup *fix, int action)
{
- hda_nid_t dac;
- int err;
+ struct alc_spec *spec = codec->spec;
+ const struct dmi_device *dev = NULL;
- switch (nid) {
- case 0x14:
- case 0x16:
- dac = 0x02;
- break;
- case 0x15:
- case 0x1a: /* ALC259/269 only */
- case 0x1b: /* ALC259/269 only */
- case 0x21: /* ALC269vb has this pin, too */
- dac = 0x03;
+ if (action != HDA_FIXUP_ACT_PRE_PROBE)
+ return;
+
+ while ((dev = dmi_find_device(DMI_DEV_TYPE_OEM_STRING, NULL, dev))) {
+ int pol, pin;
+ if (sscanf(dev->name, "HP_Mute_LED_%d_%x", &pol, &pin) != 2)
+ continue;
+ if (pin < 0x0a || pin >= 0x10)
+ break;
+ spec->mute_led_polarity = pol;
+ spec->mute_led_nid = pin - 0x0a + 0x18;
+ spec->gen.vmaster_mute.hook = alc269_fixup_mic_mute_hook;
+ spec->gen.vmaster_mute_enum = 1;
+ codec->power_filter = led_power_filter;
+ codec_dbg(codec,
+ "Detected mute LED for %x:%d\n", spec->mute_led_nid,
+ spec->mute_led_polarity);
break;
- default:
- snd_printd(KERN_WARNING "hda_codec: "
- "ignoring pin 0x%x as unknown\n", nid);
- return 0;
- }
- if (spec->multiout.dac_nids[0] != dac &&
- spec->multiout.dac_nids[1] != dac) {
- err = add_pb_vol_ctrl(spec, ALC_CTL_WIDGET_VOL, ctlname,
- HDA_COMPOSE_AMP_VAL(dac, 3, idx,
- HDA_OUTPUT));
- if (err < 0)
- return err;
- spec->multiout.dac_nids[spec->multiout.num_dacs++] = dac;
}
-
- if (nid != 0x16)
- err = add_pb_sw_ctrl(spec, ALC_CTL_WIDGET_MUTE, ctlname,
- HDA_COMPOSE_AMP_VAL(nid, 3, idx, HDA_OUTPUT));
- else /* mono */
- err = add_pb_sw_ctrl(spec, ALC_CTL_WIDGET_MUTE, ctlname,
- HDA_COMPOSE_AMP_VAL(nid, 2, idx, HDA_OUTPUT));
- if (err < 0)
- return err;
- return 0;
}
-/* add playback controls from the parsed DAC table */
-static int alc268_auto_create_multi_out_ctls(struct alc_spec *spec,
- const struct auto_pin_cfg *cfg)
+static void alc269_fixup_hp_mute_led_mic1(struct hda_codec *codec,
+ const struct hda_fixup *fix, int action)
{
- hda_nid_t nid;
- int err;
-
- spec->multiout.dac_nids = spec->private_dac_nids;
-
- nid = cfg->line_out_pins[0];
- if (nid) {
- const char *name;
- if (cfg->line_out_type == AUTO_PIN_SPEAKER_OUT)
- name = "Speaker";
- else
- name = "Front";
- err = alc268_new_analog_output(spec, nid, name, 0);
- if (err < 0)
- return err;
- }
-
- nid = cfg->speaker_pins[0];
- if (nid == 0x1d) {
- err = add_pb_vol_ctrl(spec, ALC_CTL_WIDGET_VOL, "Speaker",
- HDA_COMPOSE_AMP_VAL(nid, 3, 0, HDA_INPUT));
- if (err < 0)
- return err;
- } else if (nid) {
- err = alc268_new_analog_output(spec, nid, "Speaker", 0);
- if (err < 0)
- return err;
- }
- nid = cfg->hp_pins[0];
- if (nid) {
- err = alc268_new_analog_output(spec, nid, "Headphone", 0);
- if (err < 0)
- return err;
- }
-
- nid = cfg->line_out_pins[1] | cfg->line_out_pins[2];
- if (nid == 0x16) {
- err = add_pb_sw_ctrl(spec, ALC_CTL_WIDGET_MUTE, "Mono",
- HDA_COMPOSE_AMP_VAL(nid, 2, 0, HDA_OUTPUT));
- if (err < 0)
- return err;
+ struct alc_spec *spec = codec->spec;
+ if (action == HDA_FIXUP_ACT_PRE_PROBE) {
+ spec->mute_led_polarity = 0;
+ spec->mute_led_nid = 0x18;
+ spec->gen.vmaster_mute.hook = alc269_fixup_mic_mute_hook;
+ spec->gen.vmaster_mute_enum = 1;
+ codec->power_filter = led_power_filter;
}
- return 0;
}
-/* create playback/capture controls for input pins */
-static int alc268_auto_create_input_ctls(struct hda_codec *codec,
- const struct auto_pin_cfg *cfg)
+static void alc269_fixup_hp_mute_led_mic2(struct hda_codec *codec,
+ const struct hda_fixup *fix, int action)
{
- return alc_auto_create_input_ctls(codec, cfg, 0, 0x23, 0x24);
+ struct alc_spec *spec = codec->spec;
+ if (action == HDA_FIXUP_ACT_PRE_PROBE) {
+ spec->mute_led_polarity = 0;
+ spec->mute_led_nid = 0x19;
+ spec->gen.vmaster_mute.hook = alc269_fixup_mic_mute_hook;
+ spec->gen.vmaster_mute_enum = 1;
+ codec->power_filter = led_power_filter;
+ }
}
-static void alc268_auto_set_output_and_unmute(struct hda_codec *codec,
- hda_nid_t nid, int pin_type)
+/* turn on/off mute LED per vmaster hook */
+static void alc269_fixup_hp_gpio_mute_hook(void *private_data, int enabled)
{
- int idx;
+ struct hda_codec *codec = private_data;
+ struct alc_spec *spec = codec->spec;
+ unsigned int oldval = spec->gpio_led;
- alc_set_pin_output(codec, nid, pin_type);
- if (nid == 0x14 || nid == 0x16)
- idx = 0;
+ if (enabled)
+ spec->gpio_led &= ~0x08;
else
- idx = 1;
- snd_hda_codec_write(codec, nid, 0, AC_VERB_SET_CONNECT_SEL, idx);
+ spec->gpio_led |= 0x08;
+ if (spec->gpio_led != oldval)
+ snd_hda_codec_write(codec, 0x01, 0, AC_VERB_SET_GPIO_DATA,
+ spec->gpio_led);
}
-static void alc268_auto_init_multi_out(struct hda_codec *codec)
+/* turn on/off mic-mute LED per capture hook */
+static void alc269_fixup_hp_gpio_mic_mute_hook(struct hda_codec *codec,
+ struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
{
struct alc_spec *spec = codec->spec;
- int i;
+ unsigned int oldval = spec->gpio_led;
- for (i = 0; i < spec->autocfg.line_outs; i++) {
- hda_nid_t nid = spec->autocfg.line_out_pins[i];
- int pin_type = get_pin_type(spec->autocfg.line_out_type);
- alc268_auto_set_output_and_unmute(codec, nid, pin_type);
- }
+ if (!ucontrol)
+ return;
+
+ if (ucontrol->value.integer.value[0] ||
+ ucontrol->value.integer.value[1])
+ spec->gpio_led &= ~0x10;
+ else
+ spec->gpio_led |= 0x10;
+ if (spec->gpio_led != oldval)
+ snd_hda_codec_write(codec, 0x01, 0, AC_VERB_SET_GPIO_DATA,
+ spec->gpio_led);
}
-static void alc268_auto_init_hp_out(struct hda_codec *codec)
+static void alc269_fixup_hp_gpio_led(struct hda_codec *codec,
+ const struct hda_fixup *fix, int action)
{
struct alc_spec *spec = codec->spec;
- hda_nid_t pin;
- int i;
+ static const struct hda_verb gpio_init[] = {
+ { 0x01, AC_VERB_SET_GPIO_MASK, 0x18 },
+ { 0x01, AC_VERB_SET_GPIO_DIRECTION, 0x18 },
+ {}
+ };
- for (i = 0; i < spec->autocfg.hp_outs; i++) {
- pin = spec->autocfg.hp_pins[i];
- alc268_auto_set_output_and_unmute(codec, pin, PIN_HP);
- }
- for (i = 0; i < spec->autocfg.speaker_outs; i++) {
- pin = spec->autocfg.speaker_pins[i];
- alc268_auto_set_output_and_unmute(codec, pin, PIN_OUT);
+ if (action == HDA_FIXUP_ACT_PRE_PROBE) {
+ spec->gen.vmaster_mute.hook = alc269_fixup_hp_gpio_mute_hook;
+ spec->gen.cap_sync_hook = alc269_fixup_hp_gpio_mic_mute_hook;
+ spec->gpio_led = 0;
+ snd_hda_add_verbs(codec, gpio_init);
}
- if (spec->autocfg.mono_out_pin)
- snd_hda_codec_write(codec, spec->autocfg.mono_out_pin, 0,
- AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT);
}
-static void alc268_auto_init_mono_speaker_out(struct hda_codec *codec)
+static void alc_headset_mode_unplugged(struct hda_codec *codec)
{
- struct alc_spec *spec = codec->spec;
- hda_nid_t speaker_nid = spec->autocfg.speaker_pins[0];
- hda_nid_t hp_nid = spec->autocfg.hp_pins[0];
- hda_nid_t line_nid = spec->autocfg.line_out_pins[0];
- unsigned int dac_vol1, dac_vol2;
-
- if (line_nid == 0x1d || speaker_nid == 0x1d) {
- snd_hda_codec_write(codec, speaker_nid, 0,
- AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT);
- /* mute mixer inputs from 0x1d */
- snd_hda_codec_write(codec, 0x0f, 0,
- AC_VERB_SET_AMP_GAIN_MUTE,
- AMP_IN_UNMUTE(1));
- snd_hda_codec_write(codec, 0x10, 0,
- AC_VERB_SET_AMP_GAIN_MUTE,
- AMP_IN_UNMUTE(1));
- } else {
- /* unmute mixer inputs from 0x1d */
- snd_hda_codec_write(codec, 0x0f, 0,
- AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1));
- snd_hda_codec_write(codec, 0x10, 0,
- AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1));
- }
+ int val;
- dac_vol1 = dac_vol2 = 0xb000 | 0x40; /* set max volume */
- if (line_nid == 0x14)
- dac_vol2 = AMP_OUT_ZERO;
- else if (line_nid == 0x15)
- dac_vol1 = AMP_OUT_ZERO;
- if (hp_nid == 0x14)
- dac_vol2 = AMP_OUT_ZERO;
- else if (hp_nid == 0x15)
- dac_vol1 = AMP_OUT_ZERO;
- if (line_nid != 0x16 || hp_nid != 0x16 ||
- spec->autocfg.line_out_pins[1] != 0x16 ||
- spec->autocfg.line_out_pins[2] != 0x16)
- dac_vol1 = dac_vol2 = AMP_OUT_ZERO;
-
- snd_hda_codec_write(codec, 0x02, 0,
- AC_VERB_SET_AMP_GAIN_MUTE, dac_vol1);
- snd_hda_codec_write(codec, 0x03, 0,
- AC_VERB_SET_AMP_GAIN_MUTE, dac_vol2);
+ switch (codec->vendor_id) {
+ case 0x10ec0255:
+ /* LDO and MISC control */
+ alc_write_coef_idx(codec, 0x1b, 0x0c0b);
+ /* UAJ function set to menual mode */
+ alc_write_coef_idx(codec, 0x45, 0xd089);
+ /* Direct Drive HP Amp control(Set to verb control)*/
+ val = alc_read_coefex_idx(codec, 0x57, 0x05);
+ alc_write_coefex_idx(codec, 0x57, 0x05, val & ~(1<<14));
+ /* Set MIC2 Vref gate with HP */
+ alc_write_coef_idx(codec, 0x06, 0x6104);
+ /* Direct Drive HP Amp control */
+ alc_write_coefex_idx(codec, 0x57, 0x03, 0x8aa6);
+ break;
+ case 0x10ec0233:
+ case 0x10ec0283:
+ alc_write_coef_idx(codec, 0x1b, 0x0c0b);
+ alc_write_coef_idx(codec, 0x45, 0xc429);
+ val = alc_read_coef_idx(codec, 0x35);
+ alc_write_coef_idx(codec, 0x35, val & 0xbfff);
+ alc_write_coef_idx(codec, 0x06, 0x2104);
+ alc_write_coef_idx(codec, 0x1a, 0x0001);
+ alc_write_coef_idx(codec, 0x26, 0x0004);
+ alc_write_coef_idx(codec, 0x32, 0x42a3);
+ break;
+ case 0x10ec0292:
+ alc_write_coef_idx(codec, 0x76, 0x000e);
+ alc_write_coef_idx(codec, 0x6c, 0x2400);
+ alc_write_coef_idx(codec, 0x18, 0x7308);
+ alc_write_coef_idx(codec, 0x6b, 0xc429);
+ break;
+ case 0x10ec0293:
+ /* SET Line1 JD to 0 */
+ val = alc_read_coef_idx(codec, 0x10);
+ alc_write_coef_idx(codec, 0x10, (val & ~(7<<8)) | 6<<8);
+ /* SET charge pump by verb */
+ val = alc_read_coefex_idx(codec, 0x57, 0x05);
+ alc_write_coefex_idx(codec, 0x57, 0x05, (val & ~(1<<15|1<<13)) | 0x0);
+ /* SET EN_OSW to 1 */
+ val = alc_read_coefex_idx(codec, 0x57, 0x03);
+ alc_write_coefex_idx(codec, 0x57, 0x03, (val & ~(1<<10)) | (1<<10) );
+ /* Combo JD gating with LINE1-VREFO */
+ val = alc_read_coef_idx(codec, 0x1a);
+ alc_write_coef_idx(codec, 0x1a, (val & ~(1<<3)) | (1<<3));
+ /* Set to TRS type */
+ alc_write_coef_idx(codec, 0x45, 0xc429);
+ /* Combo Jack auto detect */
+ val = alc_read_coef_idx(codec, 0x4a);
+ alc_write_coef_idx(codec, 0x4a, (val & 0xfff0) | 0x000e);
+ break;
+ case 0x10ec0668:
+ alc_write_coef_idx(codec, 0x15, 0x0d40);
+ alc_write_coef_idx(codec, 0xb7, 0x802b);
+ break;
+ }
+ codec_dbg(codec, "Headset jack set to unplugged mode.\n");
}
-/* pcm configuration: identical with ALC880 */
-#define alc268_pcm_analog_playback alc880_pcm_analog_playback
-#define alc268_pcm_analog_capture alc880_pcm_analog_capture
-#define alc268_pcm_analog_alt_capture alc880_pcm_analog_alt_capture
-#define alc268_pcm_digital_playback alc880_pcm_digital_playback
-/*
- * BIOS auto configuration
- */
-static int alc268_parse_auto_config(struct hda_codec *codec)
+static void alc_headset_mode_mic_in(struct hda_codec *codec, hda_nid_t hp_pin,
+ hda_nid_t mic_pin)
{
- struct alc_spec *spec = codec->spec;
- int err;
- static hda_nid_t alc268_ignore[] = { 0 };
+ int val;
- err = snd_hda_parse_pin_def_config(codec, &spec->autocfg,
- alc268_ignore);
- if (err < 0)
- return err;
- if (!spec->autocfg.line_outs) {
- if (spec->autocfg.dig_outs || spec->autocfg.dig_in_pin) {
- spec->multiout.max_channels = 2;
- spec->no_analog = 1;
- goto dig_only;
- }
- return 0; /* can't find valid BIOS pin config */
+ switch (codec->vendor_id) {
+ case 0x10ec0255:
+ alc_write_coef_idx(codec, 0x45, 0xc489);
+ snd_hda_set_pin_ctl_cache(codec, hp_pin, 0);
+ alc_write_coefex_idx(codec, 0x57, 0x03, 0x8aa6);
+ /* Set MIC2 Vref gate to normal */
+ alc_write_coef_idx(codec, 0x06, 0x6100);
+ snd_hda_set_pin_ctl_cache(codec, mic_pin, PIN_VREF50);
+ break;
+ case 0x10ec0233:
+ case 0x10ec0283:
+ alc_write_coef_idx(codec, 0x45, 0xc429);
+ snd_hda_set_pin_ctl_cache(codec, hp_pin, 0);
+ val = alc_read_coef_idx(codec, 0x35);
+ alc_write_coef_idx(codec, 0x35, val | 1<<14);
+ alc_write_coef_idx(codec, 0x06, 0x2100);
+ alc_write_coef_idx(codec, 0x1a, 0x0021);
+ alc_write_coef_idx(codec, 0x26, 0x008c);
+ snd_hda_set_pin_ctl_cache(codec, mic_pin, PIN_VREF50);
+ break;
+ case 0x10ec0292:
+ snd_hda_set_pin_ctl_cache(codec, hp_pin, 0);
+ alc_write_coef_idx(codec, 0x19, 0xa208);
+ alc_write_coef_idx(codec, 0x2e, 0xacf0);
+ break;
+ case 0x10ec0293:
+ /* Set to TRS mode */
+ alc_write_coef_idx(codec, 0x45, 0xc429);
+ snd_hda_set_pin_ctl_cache(codec, hp_pin, 0);
+ /* SET charge pump by verb */
+ val = alc_read_coefex_idx(codec, 0x57, 0x05);
+ alc_write_coefex_idx(codec, 0x57, 0x05, (val & ~(1<<15|1<<13)) | (1<<15|1<<13));
+ /* SET EN_OSW to 0 */
+ val = alc_read_coefex_idx(codec, 0x57, 0x03);
+ alc_write_coefex_idx(codec, 0x57, 0x03, (val & ~(1<<10)) | 0x0);
+ /* Combo JD gating without LINE1-VREFO */
+ val = alc_read_coef_idx(codec, 0x1a);
+ alc_write_coef_idx(codec, 0x1a, (val & ~(1<<3)) | 0x0);
+ snd_hda_set_pin_ctl_cache(codec, mic_pin, PIN_VREF50);
+ break;
+ case 0x10ec0668:
+ alc_write_coef_idx(codec, 0x11, 0x0001);
+ snd_hda_set_pin_ctl_cache(codec, hp_pin, 0);
+ alc_write_coef_idx(codec, 0xb7, 0x802b);
+ alc_write_coef_idx(codec, 0xb5, 0x1040);
+ val = alc_read_coef_idx(codec, 0xc3);
+ alc_write_coef_idx(codec, 0xc3, val | 1<<12);
+ snd_hda_set_pin_ctl_cache(codec, mic_pin, PIN_VREF50);
+ break;
}
- err = alc268_auto_create_multi_out_ctls(spec, &spec->autocfg);
- if (err < 0)
- return err;
- err = alc268_auto_create_input_ctls(codec, &spec->autocfg);
- if (err < 0)
- return err;
-
- spec->multiout.max_channels = 2;
+ codec_dbg(codec, "Headset jack set to mic-in mode.\n");
+}
- dig_only:
- /* digital only support output */
- alc_auto_parse_digital(codec);
- if (spec->kctls.list)
- add_mixer(spec, spec->kctls.list);
+static void alc_headset_mode_default(struct hda_codec *codec)
+{
+ int val;
- if (!spec->no_analog && spec->autocfg.speaker_pins[0] != 0x1d)
- add_mixer(spec, alc268_beep_mixer);
+ switch (codec->vendor_id) {
+ case 0x10ec0255:
+ alc_write_coef_idx(codec, 0x45, 0xc089);
+ alc_write_coef_idx(codec, 0x45, 0xc489);
+ alc_write_coefex_idx(codec, 0x57, 0x03, 0x8ea6);
+ alc_write_coef_idx(codec, 0x49, 0x0049);
+ break;
+ case 0x10ec0233:
+ case 0x10ec0283:
+ alc_write_coef_idx(codec, 0x06, 0x2100);
+ alc_write_coef_idx(codec, 0x32, 0x4ea3);
+ break;
+ case 0x10ec0292:
+ alc_write_coef_idx(codec, 0x76, 0x000e);
+ alc_write_coef_idx(codec, 0x6c, 0x2400);
+ alc_write_coef_idx(codec, 0x6b, 0xc429);
+ alc_write_coef_idx(codec, 0x18, 0x7308);
+ break;
+ case 0x10ec0293:
+ /* Combo Jack auto detect */
+ val = alc_read_coef_idx(codec, 0x4a);
+ alc_write_coef_idx(codec, 0x4a, (val & 0xfff0) | 0x000e);
+ /* Set to TRS type */
+ alc_write_coef_idx(codec, 0x45, 0xC429);
+ /* Combo JD gating without LINE1-VREFO */
+ val = alc_read_coef_idx(codec, 0x1a);
+ alc_write_coef_idx(codec, 0x1a, (val & ~(1<<3)) | 0x0);
+ break;
+ case 0x10ec0668:
+ alc_write_coef_idx(codec, 0x11, 0x0041);
+ alc_write_coef_idx(codec, 0x15, 0x0d40);
+ alc_write_coef_idx(codec, 0xb7, 0x802b);
+ break;
+ }
+ codec_dbg(codec, "Headset jack set to headphone (default) mode.\n");
+}
- add_verb(spec, alc268_volume_init_verbs);
- spec->num_mux_defs = 2;
- spec->input_mux = &spec->private_imux[0];
+/* Iphone type */
+static void alc_headset_mode_ctia(struct hda_codec *codec)
+{
+ int val;
- err = alc_auto_add_mic_boost(codec);
- if (err < 0)
- return err;
+ switch (codec->vendor_id) {
+ case 0x10ec0255:
+ /* Set to CTIA type */
+ alc_write_coef_idx(codec, 0x45, 0xd489);
+ alc_write_coef_idx(codec, 0x1b, 0x0c2b);
+ alc_write_coefex_idx(codec, 0x57, 0x03, 0x8ea6);
+ break;
+ case 0x10ec0233:
+ case 0x10ec0283:
+ alc_write_coef_idx(codec, 0x45, 0xd429);
+ alc_write_coef_idx(codec, 0x1b, 0x0c2b);
+ alc_write_coef_idx(codec, 0x32, 0x4ea3);
+ break;
+ case 0x10ec0292:
+ alc_write_coef_idx(codec, 0x6b, 0xd429);
+ alc_write_coef_idx(codec, 0x76, 0x0008);
+ alc_write_coef_idx(codec, 0x18, 0x7388);
+ break;
+ case 0x10ec0293:
+ /* Set to ctia type */
+ alc_write_coef_idx(codec, 0x45, 0xd429);
+ /* SET Line1 JD to 1 */
+ val = alc_read_coef_idx(codec, 0x10);
+ alc_write_coef_idx(codec, 0x10, (val & ~(7<<8)) | 7<<8);
+ break;
+ case 0x10ec0668:
+ alc_write_coef_idx(codec, 0x11, 0x0001);
+ alc_write_coef_idx(codec, 0x15, 0x0d60);
+ alc_write_coef_idx(codec, 0xc3, 0x0000);
+ break;
+ }
+ codec_dbg(codec, "Headset jack set to iPhone-style headset mode.\n");
+}
- alc_ssid_check(codec, 0x15, 0x1b, 0x14, 0);
+/* Nokia type */
+static void alc_headset_mode_omtp(struct hda_codec *codec)
+{
+ int val;
- return 1;
+ switch (codec->vendor_id) {
+ case 0x10ec0255:
+ /* Set to OMTP Type */
+ alc_write_coef_idx(codec, 0x45, 0xe489);
+ alc_write_coef_idx(codec, 0x1b, 0x0c2b);
+ alc_write_coefex_idx(codec, 0x57, 0x03, 0x8ea6);
+ break;
+ case 0x10ec0233:
+ case 0x10ec0283:
+ alc_write_coef_idx(codec, 0x45, 0xe429);
+ alc_write_coef_idx(codec, 0x1b, 0x0c2b);
+ alc_write_coef_idx(codec, 0x32, 0x4ea3);
+ break;
+ case 0x10ec0292:
+ alc_write_coef_idx(codec, 0x6b, 0xe429);
+ alc_write_coef_idx(codec, 0x76, 0x0008);
+ alc_write_coef_idx(codec, 0x18, 0x7388);
+ break;
+ case 0x10ec0293:
+ /* Set to omtp type */
+ alc_write_coef_idx(codec, 0x45, 0xe429);
+ /* SET Line1 JD to 1 */
+ val = alc_read_coef_idx(codec, 0x10);
+ alc_write_coef_idx(codec, 0x10, (val & ~(7<<8)) | 7<<8);
+ break;
+ case 0x10ec0668:
+ alc_write_coef_idx(codec, 0x11, 0x0001);
+ alc_write_coef_idx(codec, 0x15, 0x0d50);
+ alc_write_coef_idx(codec, 0xc3, 0x0000);
+ break;
+ }
+ codec_dbg(codec, "Headset jack set to Nokia-style headset mode.\n");
}
-#define alc268_auto_init_analog_input alc882_auto_init_analog_input
-#define alc268_auto_init_input_src alc882_auto_init_input_src
-
-/* init callback for auto-configuration model -- overriding the default init */
-static void alc268_auto_init(struct hda_codec *codec)
+static void alc_determine_headset_type(struct hda_codec *codec)
{
+ int val;
+ bool is_ctia = false;
struct alc_spec *spec = codec->spec;
- alc268_auto_init_multi_out(codec);
- alc268_auto_init_hp_out(codec);
- alc268_auto_init_mono_speaker_out(codec);
- alc268_auto_init_analog_input(codec);
- alc268_auto_init_input_src(codec);
- alc_auto_init_digital(codec);
- if (spec->unsol_event)
- alc_inithook(codec);
-}
-
-/*
- * configuration and preset
- */
-static const char * const alc268_models[ALC268_MODEL_LAST] = {
- [ALC267_QUANTA_IL1] = "quanta-il1",
- [ALC268_3ST] = "3stack",
- [ALC268_TOSHIBA] = "toshiba",
- [ALC268_ACER] = "acer",
- [ALC268_ACER_DMIC] = "acer-dmic",
- [ALC268_ACER_ASPIRE_ONE] = "acer-aspire",
- [ALC268_DELL] = "dell",
- [ALC268_ZEPTO] = "zepto",
-#ifdef CONFIG_SND_DEBUG
- [ALC268_TEST] = "test",
-#endif
- [ALC268_AUTO] = "auto",
-};
-static struct snd_pci_quirk alc268_cfg_tbl[] = {
- SND_PCI_QUIRK(0x1025, 0x011e, "Acer Aspire 5720z", ALC268_ACER),
- SND_PCI_QUIRK(0x1025, 0x0126, "Acer", ALC268_ACER),
- SND_PCI_QUIRK(0x1025, 0x012e, "Acer Aspire 5310", ALC268_ACER),
- SND_PCI_QUIRK(0x1025, 0x0130, "Acer Extensa 5210", ALC268_ACER),
- SND_PCI_QUIRK(0x1025, 0x0136, "Acer Aspire 5315", ALC268_ACER),
- SND_PCI_QUIRK(0x1025, 0x015b, "Acer Aspire One",
- ALC268_ACER_ASPIRE_ONE),
- SND_PCI_QUIRK(0x1028, 0x0253, "Dell OEM", ALC268_DELL),
- SND_PCI_QUIRK_MASK(0x1028, 0xfff0, 0x02b0,
- "Dell Inspiron Mini9/Vostro A90", ALC268_DELL),
- /* almost compatible with toshiba but with optional digital outs;
- * auto-probing seems working fine
- */
- SND_PCI_QUIRK_MASK(0x103c, 0xff00, 0x3000, "HP TX25xx series",
- ALC268_AUTO),
- SND_PCI_QUIRK(0x1043, 0x1205, "ASUS W7J", ALC268_3ST),
- SND_PCI_QUIRK(0x1170, 0x0040, "ZEPTO", ALC268_ZEPTO),
- SND_PCI_QUIRK(0x14c0, 0x0025, "COMPAL IFL90/JFL-92", ALC268_TOSHIBA),
- SND_PCI_QUIRK(0x152d, 0x0763, "Diverse (CPR2000)", ALC268_ACER),
- SND_PCI_QUIRK(0x152d, 0x0771, "Quanta IL1", ALC267_QUANTA_IL1),
- {}
-};
-
-/* Toshiba laptops have no unique PCI SSID but only codec SSID */
-static struct snd_pci_quirk alc268_ssid_cfg_tbl[] = {
- SND_PCI_QUIRK(0x1179, 0xff0a, "TOSHIBA X-200", ALC268_AUTO),
- SND_PCI_QUIRK(0x1179, 0xff0e, "TOSHIBA X-200 HDMI", ALC268_AUTO),
- SND_PCI_QUIRK_MASK(0x1179, 0xff00, 0xff00, "TOSHIBA A/Lx05",
- ALC268_TOSHIBA),
- {}
-};
+ switch (codec->vendor_id) {
+ case 0x10ec0255:
+ /* combo jack auto switch control(Check type)*/
+ alc_write_coef_idx(codec, 0x45, 0xd089);
+ /* combo jack auto switch control(Vref conteol) */
+ alc_write_coef_idx(codec, 0x49, 0x0149);
+ msleep(300);
+ val = alc_read_coef_idx(codec, 0x46);
+ is_ctia = (val & 0x0070) == 0x0070;
+ break;
+ case 0x10ec0233:
+ case 0x10ec0283:
+ alc_write_coef_idx(codec, 0x45, 0xd029);
+ msleep(300);
+ val = alc_read_coef_idx(codec, 0x46);
+ is_ctia = (val & 0x0070) == 0x0070;
+ break;
+ case 0x10ec0292:
+ alc_write_coef_idx(codec, 0x6b, 0xd429);
+ msleep(300);
+ val = alc_read_coef_idx(codec, 0x6c);
+ is_ctia = (val & 0x001c) == 0x001c;
+ break;
+ case 0x10ec0293:
+ /* Combo Jack auto detect */
+ val = alc_read_coef_idx(codec, 0x4a);
+ alc_write_coef_idx(codec, 0x4a, (val & 0xfff0) | 0x0008);
+ /* Set to ctia type */
+ alc_write_coef_idx(codec, 0x45, 0xD429);
+ msleep(300);
+ val = alc_read_coef_idx(codec, 0x46);
+ is_ctia = (val & 0x0070) == 0x0070;
+ break;
+ case 0x10ec0668:
+ alc_write_coef_idx(codec, 0x11, 0x0001);
+ alc_write_coef_idx(codec, 0xb7, 0x802b);
+ alc_write_coef_idx(codec, 0x15, 0x0d60);
+ alc_write_coef_idx(codec, 0xc3, 0x0c00);
+ msleep(300);
+ val = alc_read_coef_idx(codec, 0xbe);
+ is_ctia = (val & 0x1c02) == 0x1c02;
+ break;
+ }
-static struct alc_config_preset alc268_presets[] = {
- [ALC267_QUANTA_IL1] = {
- .mixers = { alc267_quanta_il1_mixer, alc268_beep_mixer,
- alc268_capture_nosrc_mixer },
- .init_verbs = { alc268_base_init_verbs, alc268_eapd_verbs,
- alc267_quanta_il1_verbs },
- .num_dacs = ARRAY_SIZE(alc268_dac_nids),
- .dac_nids = alc268_dac_nids,
- .num_adc_nids = ARRAY_SIZE(alc268_adc_nids_alt),
- .adc_nids = alc268_adc_nids_alt,
- .hp_nid = 0x03,
- .num_channel_mode = ARRAY_SIZE(alc268_modes),
- .channel_mode = alc268_modes,
- .unsol_event = alc_sku_unsol_event,
- .setup = alc267_quanta_il1_setup,
- .init_hook = alc_inithook,
- },
- [ALC268_3ST] = {
- .mixers = { alc268_base_mixer, alc268_capture_alt_mixer,
- alc268_beep_mixer },
- .init_verbs = { alc268_base_init_verbs },
- .num_dacs = ARRAY_SIZE(alc268_dac_nids),
- .dac_nids = alc268_dac_nids,
- .num_adc_nids = ARRAY_SIZE(alc268_adc_nids_alt),
- .adc_nids = alc268_adc_nids_alt,
- .capsrc_nids = alc268_capsrc_nids,
- .hp_nid = 0x03,
- .dig_out_nid = ALC268_DIGOUT_NID,
- .num_channel_mode = ARRAY_SIZE(alc268_modes),
- .channel_mode = alc268_modes,
- .input_mux = &alc268_capture_source,
- },
- [ALC268_TOSHIBA] = {
- .mixers = { alc268_toshiba_mixer, alc268_capture_alt_mixer,
- alc268_beep_mixer },
- .init_verbs = { alc268_base_init_verbs, alc268_eapd_verbs,
- alc268_toshiba_verbs },
- .num_dacs = ARRAY_SIZE(alc268_dac_nids),
- .dac_nids = alc268_dac_nids,
- .num_adc_nids = ARRAY_SIZE(alc268_adc_nids_alt),
- .adc_nids = alc268_adc_nids_alt,
- .capsrc_nids = alc268_capsrc_nids,
- .hp_nid = 0x03,
- .num_channel_mode = ARRAY_SIZE(alc268_modes),
- .channel_mode = alc268_modes,
- .input_mux = &alc268_capture_source,
- .unsol_event = alc268_toshiba_unsol_event,
- .setup = alc268_toshiba_setup,
- .init_hook = alc268_toshiba_automute,
- },
- [ALC268_ACER] = {
- .mixers = { alc268_acer_mixer, alc268_capture_alt_mixer,
- alc268_beep_mixer },
- .init_verbs = { alc268_base_init_verbs, alc268_eapd_verbs,
- alc268_acer_verbs },
- .num_dacs = ARRAY_SIZE(alc268_dac_nids),
- .dac_nids = alc268_dac_nids,
- .num_adc_nids = ARRAY_SIZE(alc268_adc_nids_alt),
- .adc_nids = alc268_adc_nids_alt,
- .capsrc_nids = alc268_capsrc_nids,
- .hp_nid = 0x02,
- .num_channel_mode = ARRAY_SIZE(alc268_modes),
- .channel_mode = alc268_modes,
- .input_mux = &alc268_acer_capture_source,
- .unsol_event = alc268_acer_unsol_event,
- .init_hook = alc268_acer_init_hook,
- },
- [ALC268_ACER_DMIC] = {
- .mixers = { alc268_acer_dmic_mixer, alc268_capture_alt_mixer,
- alc268_beep_mixer },
- .init_verbs = { alc268_base_init_verbs, alc268_eapd_verbs,
- alc268_acer_verbs },
- .num_dacs = ARRAY_SIZE(alc268_dac_nids),
- .dac_nids = alc268_dac_nids,
- .num_adc_nids = ARRAY_SIZE(alc268_adc_nids_alt),
- .adc_nids = alc268_adc_nids_alt,
- .capsrc_nids = alc268_capsrc_nids,
- .hp_nid = 0x02,
- .num_channel_mode = ARRAY_SIZE(alc268_modes),
- .channel_mode = alc268_modes,
- .input_mux = &alc268_acer_dmic_capture_source,
- .unsol_event = alc268_acer_unsol_event,
- .init_hook = alc268_acer_init_hook,
- },
- [ALC268_ACER_ASPIRE_ONE] = {
- .mixers = { alc268_acer_aspire_one_mixer,
- alc268_beep_mixer,
- alc268_capture_nosrc_mixer },
- .init_verbs = { alc268_base_init_verbs, alc268_eapd_verbs,
- alc268_acer_aspire_one_verbs },
- .num_dacs = ARRAY_SIZE(alc268_dac_nids),
- .dac_nids = alc268_dac_nids,
- .num_adc_nids = ARRAY_SIZE(alc268_adc_nids_alt),
- .adc_nids = alc268_adc_nids_alt,
- .capsrc_nids = alc268_capsrc_nids,
- .hp_nid = 0x03,
- .num_channel_mode = ARRAY_SIZE(alc268_modes),
- .channel_mode = alc268_modes,
- .unsol_event = alc268_acer_lc_unsol_event,
- .setup = alc268_acer_lc_setup,
- .init_hook = alc268_acer_lc_init_hook,
- },
- [ALC268_DELL] = {
- .mixers = { alc268_dell_mixer, alc268_beep_mixer,
- alc268_capture_nosrc_mixer },
- .init_verbs = { alc268_base_init_verbs, alc268_eapd_verbs,
- alc268_dell_verbs },
- .num_dacs = ARRAY_SIZE(alc268_dac_nids),
- .dac_nids = alc268_dac_nids,
- .num_adc_nids = ARRAY_SIZE(alc268_adc_nids_alt),
- .adc_nids = alc268_adc_nids_alt,
- .capsrc_nids = alc268_capsrc_nids,
- .hp_nid = 0x02,
- .num_channel_mode = ARRAY_SIZE(alc268_modes),
- .channel_mode = alc268_modes,
- .unsol_event = alc_sku_unsol_event,
- .setup = alc268_dell_setup,
- .init_hook = alc_inithook,
- },
- [ALC268_ZEPTO] = {
- .mixers = { alc268_base_mixer, alc268_capture_alt_mixer,
- alc268_beep_mixer },
- .init_verbs = { alc268_base_init_verbs, alc268_eapd_verbs,
- alc268_toshiba_verbs },
- .num_dacs = ARRAY_SIZE(alc268_dac_nids),
- .dac_nids = alc268_dac_nids,
- .num_adc_nids = ARRAY_SIZE(alc268_adc_nids_alt),
- .adc_nids = alc268_adc_nids_alt,
- .capsrc_nids = alc268_capsrc_nids,
- .hp_nid = 0x03,
- .dig_out_nid = ALC268_DIGOUT_NID,
- .num_channel_mode = ARRAY_SIZE(alc268_modes),
- .channel_mode = alc268_modes,
- .input_mux = &alc268_capture_source,
- .setup = alc268_toshiba_setup,
- .init_hook = alc268_toshiba_automute,
- },
-#ifdef CONFIG_SND_DEBUG
- [ALC268_TEST] = {
- .mixers = { alc268_test_mixer, alc268_capture_mixer },
- .init_verbs = { alc268_base_init_verbs, alc268_eapd_verbs,
- alc268_volume_init_verbs },
- .num_dacs = ARRAY_SIZE(alc268_dac_nids),
- .dac_nids = alc268_dac_nids,
- .num_adc_nids = ARRAY_SIZE(alc268_adc_nids_alt),
- .adc_nids = alc268_adc_nids_alt,
- .capsrc_nids = alc268_capsrc_nids,
- .hp_nid = 0x03,
- .dig_out_nid = ALC268_DIGOUT_NID,
- .num_channel_mode = ARRAY_SIZE(alc268_modes),
- .channel_mode = alc268_modes,
- .input_mux = &alc268_capture_source,
- },
-#endif
-};
+ codec_dbg(codec, "Headset jack detected iPhone-style headset: %s\n",
+ is_ctia ? "yes" : "no");
+ spec->current_headset_type = is_ctia ? ALC_HEADSET_TYPE_CTIA : ALC_HEADSET_TYPE_OMTP;
+}
-static int patch_alc268(struct hda_codec *codec)
+static void alc_update_headset_mode(struct hda_codec *codec)
{
- struct alc_spec *spec;
- int board_config;
- int i, has_beep, err;
-
- spec = kzalloc(sizeof(*spec), GFP_KERNEL);
- if (spec == NULL)
- return -ENOMEM;
-
- codec->spec = spec;
-
- board_config = snd_hda_check_board_config(codec, ALC268_MODEL_LAST,
- alc268_models,
- alc268_cfg_tbl);
-
- if (board_config < 0 || board_config >= ALC268_MODEL_LAST)
- board_config = snd_hda_check_board_codec_sid_config(codec,
- ALC268_MODEL_LAST, alc268_models, alc268_ssid_cfg_tbl);
-
- if (board_config < 0 || board_config >= ALC268_MODEL_LAST) {
- printk(KERN_INFO "hda_codec: %s: BIOS auto-probing.\n",
- codec->chip_name);
- board_config = ALC268_AUTO;
- }
-
- if (board_config == ALC268_AUTO) {
- /* automatic parse from the BIOS config */
- err = alc268_parse_auto_config(codec);
- if (err < 0) {
- alc_free(codec);
- return err;
- } else if (!err) {
- printk(KERN_INFO
- "hda_codec: Cannot set up configuration "
- "from BIOS. Using base mode...\n");
- board_config = ALC268_3ST;
- }
- }
+ struct alc_spec *spec = codec->spec;
- if (board_config != ALC268_AUTO)
- setup_preset(codec, &alc268_presets[board_config]);
+ hda_nid_t mux_pin = spec->gen.imux_pins[spec->gen.cur_mux[0]];
+ hda_nid_t hp_pin = spec->gen.autocfg.hp_pins[0];
- spec->stream_analog_playback = &alc268_pcm_analog_playback;
- spec->stream_analog_capture = &alc268_pcm_analog_capture;
- spec->stream_analog_alt_capture = &alc268_pcm_analog_alt_capture;
+ int new_headset_mode;
- spec->stream_digital_playback = &alc268_pcm_digital_playback;
+ if (!snd_hda_jack_detect(codec, hp_pin))
+ new_headset_mode = ALC_HEADSET_MODE_UNPLUGGED;
+ else if (mux_pin == spec->headset_mic_pin)
+ new_headset_mode = ALC_HEADSET_MODE_HEADSET;
+ else if (mux_pin == spec->headphone_mic_pin)
+ new_headset_mode = ALC_HEADSET_MODE_MIC;
+ else
+ new_headset_mode = ALC_HEADSET_MODE_HEADPHONE;
- has_beep = 0;
- for (i = 0; i < spec->num_mixers; i++) {
- if (spec->mixers[i] == alc268_beep_mixer) {
- has_beep = 1;
- break;
- }
+ if (new_headset_mode == spec->current_headset_mode) {
+ snd_hda_gen_update_outputs(codec);
+ return;
}
- if (has_beep) {
- err = snd_hda_attach_beep_device(codec, 0x1);
- if (err < 0) {
- alc_free(codec);
- return err;
- }
- if (!query_amp_caps(codec, 0x1d, HDA_INPUT))
- /* override the amp caps for beep generator */
- snd_hda_override_amp_caps(codec, 0x1d, HDA_INPUT,
- (0x0c << AC_AMPCAP_OFFSET_SHIFT) |
- (0x0c << AC_AMPCAP_NUM_STEPS_SHIFT) |
- (0x07 << AC_AMPCAP_STEP_SIZE_SHIFT) |
- (0 << AC_AMPCAP_MUTE_SHIFT));
+ switch (new_headset_mode) {
+ case ALC_HEADSET_MODE_UNPLUGGED:
+ alc_headset_mode_unplugged(codec);
+ spec->gen.hp_jack_present = false;
+ break;
+ case ALC_HEADSET_MODE_HEADSET:
+ if (spec->current_headset_type == ALC_HEADSET_TYPE_UNKNOWN)
+ alc_determine_headset_type(codec);
+ if (spec->current_headset_type == ALC_HEADSET_TYPE_CTIA)
+ alc_headset_mode_ctia(codec);
+ else if (spec->current_headset_type == ALC_HEADSET_TYPE_OMTP)
+ alc_headset_mode_omtp(codec);
+ spec->gen.hp_jack_present = true;
+ break;
+ case ALC_HEADSET_MODE_MIC:
+ alc_headset_mode_mic_in(codec, hp_pin, spec->headphone_mic_pin);
+ spec->gen.hp_jack_present = false;
+ break;
+ case ALC_HEADSET_MODE_HEADPHONE:
+ alc_headset_mode_default(codec);
+ spec->gen.hp_jack_present = true;
+ break;
}
-
- if (!spec->no_analog && !spec->adc_nids && spec->input_mux) {
- /* check whether NID 0x07 is valid */
- unsigned int wcap = get_wcaps(codec, 0x07);
-
- spec->capsrc_nids = alc268_capsrc_nids;
- /* get type */
- wcap = get_wcaps_type(wcap);
- if (spec->auto_mic ||
- wcap != AC_WID_AUD_IN || spec->input_mux->num_items == 1) {
- spec->adc_nids = alc268_adc_nids_alt;
- spec->num_adc_nids = ARRAY_SIZE(alc268_adc_nids_alt);
- if (spec->auto_mic)
- fixup_automic_adc(codec);
- if (spec->auto_mic || spec->input_mux->num_items == 1)
- add_mixer(spec, alc268_capture_nosrc_mixer);
- else
- add_mixer(spec, alc268_capture_alt_mixer);
- } else {
- spec->adc_nids = alc268_adc_nids;
- spec->num_adc_nids = ARRAY_SIZE(alc268_adc_nids);
- add_mixer(spec, alc268_capture_mixer);
- }
+ if (new_headset_mode != ALC_HEADSET_MODE_MIC) {
+ snd_hda_set_pin_ctl_cache(codec, hp_pin,
+ AC_PINCTL_OUT_EN | AC_PINCTL_HP_EN);
+ if (spec->headphone_mic_pin)
+ snd_hda_set_pin_ctl_cache(codec, spec->headphone_mic_pin,
+ PIN_VREFHIZ);
}
+ spec->current_headset_mode = new_headset_mode;
- spec->vmaster_nid = 0x02;
-
- codec->patch_ops = alc_patch_ops;
- if (board_config == ALC268_AUTO)
- spec->init_hook = alc268_auto_init;
-
- alc_init_jacks(codec);
-
- return 0;
+ snd_hda_gen_update_outputs(codec);
}
-/*
- * ALC269 channel source setting (2 channel)
- */
-#define ALC269_DIGOUT_NID ALC880_DIGOUT_NID
-
-#define alc269_dac_nids alc260_dac_nids
-
-static hda_nid_t alc269_adc_nids[1] = {
- /* ADC1 */
- 0x08,
-};
-
-static hda_nid_t alc269_capsrc_nids[1] = {
- 0x23,
-};
-
-static hda_nid_t alc269vb_adc_nids[1] = {
- /* ADC1 */
- 0x09,
-};
-
-static hda_nid_t alc269vb_capsrc_nids[1] = {
- 0x22,
-};
-
-static hda_nid_t alc269_adc_candidates[] = {
- 0x08, 0x09, 0x07, 0x11,
-};
-
-#define alc269_modes alc260_modes
-#define alc269_capture_source alc880_lg_lw_capture_source
-
-static struct snd_kcontrol_new alc269_base_mixer[] = {
- HDA_CODEC_VOLUME("Front Playback Volume", 0x02, 0x0, HDA_OUTPUT),
- HDA_CODEC_MUTE("Front Playback Switch", 0x14, 0x0, HDA_OUTPUT),
- HDA_CODEC_VOLUME("Line Playback Volume", 0x0b, 0x02, HDA_INPUT),
- HDA_CODEC_MUTE("Line Playback Switch", 0x0b, 0x02, HDA_INPUT),
- HDA_CODEC_VOLUME("Mic Playback Volume", 0x0b, 0x0, HDA_INPUT),
- HDA_CODEC_MUTE("Mic Playback Switch", 0x0b, 0x0, HDA_INPUT),
- HDA_CODEC_VOLUME("Mic Boost Volume", 0x18, 0, HDA_INPUT),
- HDA_CODEC_VOLUME("Front Mic Playback Volume", 0x0b, 0x01, HDA_INPUT),
- HDA_CODEC_MUTE("Front Mic Playback Switch", 0x0b, 0x01, HDA_INPUT),
- HDA_CODEC_VOLUME("Front Mic Boost Volume", 0x19, 0, HDA_INPUT),
- HDA_CODEC_MUTE("Headphone Playback Switch", 0x15, 0x0, HDA_OUTPUT),
- HDA_CODEC_MUTE_MONO("Mono Playback Switch", 0x16, 2, 0x0, HDA_OUTPUT),
- { } /* end */
-};
-
-static struct snd_kcontrol_new alc269_quanta_fl1_mixer[] = {
- /* output mixer control */
- HDA_BIND_VOL("Master Playback Volume", &alc268_acer_bind_master_vol),
- {
- .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
- .name = "Master Playback Switch",
- .subdevice = HDA_SUBDEV_AMP_FLAG,
- .info = snd_hda_mixer_amp_switch_info,
- .get = snd_hda_mixer_amp_switch_get,
- .put = alc268_acer_master_sw_put,
- .private_value = HDA_COMPOSE_AMP_VAL(0x14, 3, 0, HDA_OUTPUT),
- },
- HDA_CODEC_VOLUME("Mic Playback Volume", 0x0b, 0x0, HDA_INPUT),
- HDA_CODEC_MUTE("Mic Playback Switch", 0x0b, 0x0, HDA_INPUT),
- HDA_CODEC_VOLUME("Mic Boost Volume", 0x18, 0, HDA_INPUT),
- HDA_CODEC_VOLUME("Internal Mic Playback Volume", 0x0b, 0x01, HDA_INPUT),
- HDA_CODEC_MUTE("Internal Mic Playback Switch", 0x0b, 0x01, HDA_INPUT),
- HDA_CODEC_VOLUME("Internal Mic Boost Volume", 0x19, 0, HDA_INPUT),
- { }
-};
-
-static struct snd_kcontrol_new alc269_lifebook_mixer[] = {
- /* output mixer control */
- HDA_BIND_VOL("Master Playback Volume", &alc268_acer_bind_master_vol),
- {
- .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
- .name = "Master Playback Switch",
- .subdevice = HDA_SUBDEV_AMP_FLAG,
- .info = snd_hda_mixer_amp_switch_info,
- .get = snd_hda_mixer_amp_switch_get,
- .put = alc268_acer_master_sw_put,
- .private_value = HDA_COMPOSE_AMP_VAL(0x14, 3, 0, HDA_OUTPUT),
- },
- HDA_CODEC_VOLUME("Mic Playback Volume", 0x0b, 0x0, HDA_INPUT),
- HDA_CODEC_MUTE("Mic Playback Switch", 0x0b, 0x0, HDA_INPUT),
- HDA_CODEC_VOLUME("Mic Boost Volume", 0x18, 0, HDA_INPUT),
- HDA_CODEC_VOLUME("Internal Mic Playback Volume", 0x0b, 0x01, HDA_INPUT),
- HDA_CODEC_MUTE("Internal Mic Playback Switch", 0x0b, 0x01, HDA_INPUT),
- HDA_CODEC_VOLUME("Internal Mic Boost Volume", 0x19, 0, HDA_INPUT),
- HDA_CODEC_VOLUME("Dock Mic Playback Volume", 0x0b, 0x03, HDA_INPUT),
- HDA_CODEC_MUTE("Dock Mic Playback Switch", 0x0b, 0x03, HDA_INPUT),
- HDA_CODEC_VOLUME("Dock Mic Boost Volume", 0x1b, 0, HDA_INPUT),
- { }
-};
-
-static struct snd_kcontrol_new alc269_laptop_mixer[] = {
- HDA_CODEC_MUTE("Speaker Playback Switch", 0x14, 0x0, HDA_OUTPUT),
- HDA_CODEC_VOLUME("Speaker Playback Volume", 0x02, 0x0, HDA_OUTPUT),
- HDA_CODEC_MUTE("Headphone Playback Switch", 0x15, 0x0, HDA_OUTPUT),
- HDA_CODEC_VOLUME("Headphone Playback Volume", 0x03, 0x0, HDA_OUTPUT),
- { } /* end */
-};
-
-static struct snd_kcontrol_new alc269vb_laptop_mixer[] = {
- HDA_CODEC_MUTE("Speaker Playback Switch", 0x14, 0x0, HDA_OUTPUT),
- HDA_CODEC_VOLUME("Speaker Playback Volume", 0x02, 0x0, HDA_OUTPUT),
- HDA_CODEC_MUTE("Headphone Playback Switch", 0x21, 0x0, HDA_OUTPUT),
- HDA_CODEC_VOLUME("Headphone Playback Volume", 0x03, 0x0, HDA_OUTPUT),
- { } /* end */
-};
-
-static struct snd_kcontrol_new alc269_asus_mixer[] = {
- HDA_CODEC_VOLUME("Master Playback Volume", 0x02, 0x0, HDA_OUTPUT),
- HDA_CODEC_MUTE("Master Playback Switch", 0x0c, 0x0, HDA_INPUT),
- { } /* end */
-};
-
-/* capture mixer elements */
-static struct snd_kcontrol_new alc269_laptop_analog_capture_mixer[] = {
- HDA_CODEC_VOLUME("Capture Volume", 0x08, 0x0, HDA_INPUT),
- HDA_CODEC_MUTE("Capture Switch", 0x08, 0x0, HDA_INPUT),
- HDA_CODEC_VOLUME("Mic Boost Volume", 0x18, 0, HDA_INPUT),
- HDA_CODEC_VOLUME("Internal Mic Boost Volume", 0x19, 0, HDA_INPUT),
- { } /* end */
-};
-
-static struct snd_kcontrol_new alc269_laptop_digital_capture_mixer[] = {
- HDA_CODEC_VOLUME("Capture Volume", 0x08, 0x0, HDA_INPUT),
- HDA_CODEC_MUTE("Capture Switch", 0x08, 0x0, HDA_INPUT),
- HDA_CODEC_VOLUME("Mic Boost Volume", 0x18, 0, HDA_INPUT),
- { } /* end */
-};
-
-static struct snd_kcontrol_new alc269vb_laptop_analog_capture_mixer[] = {
- HDA_CODEC_VOLUME("Capture Volume", 0x09, 0x0, HDA_INPUT),
- HDA_CODEC_MUTE("Capture Switch", 0x09, 0x0, HDA_INPUT),
- HDA_CODEC_VOLUME("Mic Boost Volume", 0x18, 0, HDA_INPUT),
- HDA_CODEC_VOLUME("Internal Mic Boost Volume", 0x19, 0, HDA_INPUT),
- { } /* end */
-};
-
-static struct snd_kcontrol_new alc269vb_laptop_digital_capture_mixer[] = {
- HDA_CODEC_VOLUME("Capture Volume", 0x09, 0x0, HDA_INPUT),
- HDA_CODEC_MUTE("Capture Switch", 0x09, 0x0, HDA_INPUT),
- HDA_CODEC_VOLUME("Mic Boost Volume", 0x18, 0, HDA_INPUT),
- { } /* end */
-};
-
-/* FSC amilo */
-#define alc269_fujitsu_mixer alc269_laptop_mixer
-
-static struct hda_verb alc269_quanta_fl1_verbs[] = {
- {0x15, AC_VERB_SET_CONNECT_SEL, 0x01},
- {0x12, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN},
- {0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP},
- {0x15, AC_VERB_SET_UNSOLICITED_ENABLE, ALC880_HP_EVENT | AC_USRSP_EN},
- {0x18, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | ALC880_MIC_EVENT},
- {0x1d, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN},
- { }
-};
-
-static struct hda_verb alc269_lifebook_verbs[] = {
- {0x15, AC_VERB_SET_CONNECT_SEL, 0x01},
- {0x1a, AC_VERB_SET_CONNECT_SEL, 0x01},
- {0x12, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN},
- {0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP},
- {0x15, AC_VERB_SET_UNSOLICITED_ENABLE, ALC880_HP_EVENT | AC_USRSP_EN},
- {0x1a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
- {0x1a, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP},
- {0x1a, AC_VERB_SET_UNSOLICITED_ENABLE, ALC880_HP_EVENT | AC_USRSP_EN},
- {0x18, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | ALC880_MIC_EVENT},
- {0x1d, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN},
- { }
-};
-
-/* toggle speaker-output according to the hp-jack state */
-static void alc269_quanta_fl1_speaker_automute(struct hda_codec *codec)
+static void alc_update_headset_mode_hook(struct hda_codec *codec,
+ struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
{
- unsigned int present;
- unsigned char bits;
-
- present = snd_hda_jack_detect(codec, 0x15);
- bits = present ? HDA_AMP_MUTE : 0;
- snd_hda_codec_amp_stereo(codec, 0x0c, HDA_INPUT, 0,
- HDA_AMP_MUTE, bits);
- snd_hda_codec_amp_stereo(codec, 0x0c, HDA_INPUT, 1,
- HDA_AMP_MUTE, bits);
-
- snd_hda_codec_write(codec, 0x20, 0,
- AC_VERB_SET_COEF_INDEX, 0x0c);
- snd_hda_codec_write(codec, 0x20, 0,
- AC_VERB_SET_PROC_COEF, 0x680);
-
- snd_hda_codec_write(codec, 0x20, 0,
- AC_VERB_SET_COEF_INDEX, 0x0c);
- snd_hda_codec_write(codec, 0x20, 0,
- AC_VERB_SET_PROC_COEF, 0x480);
+ alc_update_headset_mode(codec);
}
-/* toggle speaker-output according to the hp-jacks state */
-static void alc269_lifebook_speaker_automute(struct hda_codec *codec)
+static void alc_update_headset_jack_cb(struct hda_codec *codec, struct hda_jack_tbl *jack)
{
- unsigned int present;
- unsigned char bits;
-
- /* Check laptop headphone socket */
- present = snd_hda_jack_detect(codec, 0x15);
-
- /* Check port replicator headphone socket */
- present |= snd_hda_jack_detect(codec, 0x1a);
+ struct alc_spec *spec = codec->spec;
+ spec->current_headset_type = ALC_HEADSET_TYPE_UNKNOWN;
+ snd_hda_gen_hp_automute(codec, jack);
+}
- bits = present ? HDA_AMP_MUTE : 0;
- snd_hda_codec_amp_stereo(codec, 0x0c, HDA_INPUT, 0,
- HDA_AMP_MUTE, bits);
- snd_hda_codec_amp_stereo(codec, 0x0c, HDA_INPUT, 1,
- HDA_AMP_MUTE, bits);
+static void alc_probe_headset_mode(struct hda_codec *codec)
+{
+ int i;
+ struct alc_spec *spec = codec->spec;
+ struct auto_pin_cfg *cfg = &spec->gen.autocfg;
- snd_hda_codec_write(codec, 0x20, 0,
- AC_VERB_SET_COEF_INDEX, 0x0c);
- snd_hda_codec_write(codec, 0x20, 0,
- AC_VERB_SET_PROC_COEF, 0x680);
+ /* Find mic pins */
+ for (i = 0; i < cfg->num_inputs; i++) {
+ if (cfg->inputs[i].is_headset_mic && !spec->headset_mic_pin)
+ spec->headset_mic_pin = cfg->inputs[i].pin;
+ if (cfg->inputs[i].is_headphone_mic && !spec->headphone_mic_pin)
+ spec->headphone_mic_pin = cfg->inputs[i].pin;
+ }
- snd_hda_codec_write(codec, 0x20, 0,
- AC_VERB_SET_COEF_INDEX, 0x0c);
- snd_hda_codec_write(codec, 0x20, 0,
- AC_VERB_SET_PROC_COEF, 0x480);
+ spec->gen.cap_sync_hook = alc_update_headset_mode_hook;
+ spec->gen.automute_hook = alc_update_headset_mode;
+ spec->gen.hp_automute_hook = alc_update_headset_jack_cb;
}
-static void alc269_lifebook_mic_autoswitch(struct hda_codec *codec)
+static void alc_fixup_headset_mode(struct hda_codec *codec,
+ const struct hda_fixup *fix, int action)
{
- unsigned int present_laptop;
- unsigned int present_dock;
-
- present_laptop = snd_hda_jack_detect(codec, 0x18);
- present_dock = snd_hda_jack_detect(codec, 0x1b);
-
- /* Laptop mic port overrides dock mic port, design decision */
- if (present_dock)
- snd_hda_codec_write(codec, 0x23, 0,
- AC_VERB_SET_CONNECT_SEL, 0x3);
- if (present_laptop)
- snd_hda_codec_write(codec, 0x23, 0,
- AC_VERB_SET_CONNECT_SEL, 0x0);
- if (!present_dock && !present_laptop)
- snd_hda_codec_write(codec, 0x23, 0,
- AC_VERB_SET_CONNECT_SEL, 0x1);
-}
+ struct alc_spec *spec = codec->spec;
-static void alc269_quanta_fl1_unsol_event(struct hda_codec *codec,
- unsigned int res)
-{
- switch (res >> 26) {
- case ALC880_HP_EVENT:
- alc269_quanta_fl1_speaker_automute(codec);
+ switch (action) {
+ case HDA_FIXUP_ACT_PRE_PROBE:
+ spec->parse_flags |= HDA_PINCFG_HEADSET_MIC | HDA_PINCFG_HEADPHONE_MIC;
+ break;
+ case HDA_FIXUP_ACT_PROBE:
+ alc_probe_headset_mode(codec);
break;
- case ALC880_MIC_EVENT:
- alc_mic_automute(codec);
+ case HDA_FIXUP_ACT_INIT:
+ spec->current_headset_mode = 0;
+ alc_update_headset_mode(codec);
break;
}
}
-static void alc269_lifebook_unsol_event(struct hda_codec *codec,
- unsigned int res)
+static void alc_fixup_headset_mode_no_hp_mic(struct hda_codec *codec,
+ const struct hda_fixup *fix, int action)
{
- if ((res >> 26) == ALC880_HP_EVENT)
- alc269_lifebook_speaker_automute(codec);
- if ((res >> 26) == ALC880_MIC_EVENT)
- alc269_lifebook_mic_autoswitch(codec);
+ if (action == HDA_FIXUP_ACT_PRE_PROBE) {
+ struct alc_spec *spec = codec->spec;
+ spec->parse_flags |= HDA_PINCFG_HEADSET_MIC;
+ }
+ else
+ alc_fixup_headset_mode(codec, fix, action);
}
-static void alc269_quanta_fl1_setup(struct hda_codec *codec)
+static void alc255_set_default_jack_type(struct hda_codec *codec)
{
- struct alc_spec *spec = codec->spec;
- spec->autocfg.hp_pins[0] = 0x15;
- spec->autocfg.speaker_pins[0] = 0x14;
- spec->ext_mic.pin = 0x18;
- spec->ext_mic.mux_idx = 0;
- spec->int_mic.pin = 0x19;
- spec->int_mic.mux_idx = 1;
- spec->auto_mic = 1;
+ /* Set to iphone type */
+ alc_write_coef_idx(codec, 0x1b, 0x880b);
+ alc_write_coef_idx(codec, 0x45, 0xd089);
+ alc_write_coef_idx(codec, 0x1b, 0x080b);
+ alc_write_coef_idx(codec, 0x46, 0x0004);
+ alc_write_coef_idx(codec, 0x1b, 0x0c0b);
+ msleep(30);
}
-static void alc269_quanta_fl1_init_hook(struct hda_codec *codec)
+static void alc_fixup_headset_mode_alc255(struct hda_codec *codec,
+ const struct hda_fixup *fix, int action)
{
- alc269_quanta_fl1_speaker_automute(codec);
- alc_mic_automute(codec);
+ if (action == HDA_FIXUP_ACT_PRE_PROBE) {
+ alc255_set_default_jack_type(codec);
+ }
+ alc_fixup_headset_mode(codec, fix, action);
}
-static void alc269_lifebook_init_hook(struct hda_codec *codec)
+static void alc_fixup_headset_mode_alc255_no_hp_mic(struct hda_codec *codec,
+ const struct hda_fixup *fix, int action)
{
- alc269_lifebook_speaker_automute(codec);
- alc269_lifebook_mic_autoswitch(codec);
-}
-
-static struct hda_verb alc269_laptop_dmic_init_verbs[] = {
- {0x15, AC_VERB_SET_CONNECT_SEL, 0x01},
- {0x23, AC_VERB_SET_CONNECT_SEL, 0x05},
- {0x02, AC_VERB_SET_AMP_GAIN_MUTE, 0xb026 },
- {0x08, AC_VERB_SET_AMP_GAIN_MUTE, (0x7019 | (0x00 << 8))},
- {0x12, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN},
- {0x18, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | ALC880_MIC_EVENT},
- {0x15, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | ALC880_HP_EVENT},
- {}
-};
-
-static struct hda_verb alc269_laptop_amic_init_verbs[] = {
- {0x15, AC_VERB_SET_CONNECT_SEL, 0x01},
- {0x23, AC_VERB_SET_CONNECT_SEL, 0x01},
- {0x02, AC_VERB_SET_AMP_GAIN_MUTE, 0xb026 },
- {0x08, AC_VERB_SET_AMP_GAIN_MUTE, (0x701b | (0x00 << 8))},
- {0x18, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | ALC880_MIC_EVENT},
- {0x15, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | ALC880_HP_EVENT},
- {}
-};
-
-static struct hda_verb alc269vb_laptop_dmic_init_verbs[] = {
- {0x21, AC_VERB_SET_CONNECT_SEL, 0x01},
- {0x22, AC_VERB_SET_CONNECT_SEL, 0x06},
- {0x02, AC_VERB_SET_AMP_GAIN_MUTE, 0xb026 },
- {0x09, AC_VERB_SET_AMP_GAIN_MUTE, (0x7019 | (0x00 << 8))},
- {0x12, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN},
- {0x18, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | ALC880_MIC_EVENT},
- {0x21, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | ALC880_HP_EVENT},
- {}
-};
-
-static struct hda_verb alc269vb_laptop_amic_init_verbs[] = {
- {0x21, AC_VERB_SET_CONNECT_SEL, 0x01},
- {0x22, AC_VERB_SET_CONNECT_SEL, 0x01},
- {0x02, AC_VERB_SET_AMP_GAIN_MUTE, 0xb026 },
- {0x09, AC_VERB_SET_AMP_GAIN_MUTE, (0x7019 | (0x00 << 8))},
- {0x12, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN},
- {0x18, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | ALC880_MIC_EVENT},
- {0x21, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | ALC880_HP_EVENT},
- {}
-};
-
-static struct hda_verb alc271_acer_dmic_verbs[] = {
- {0x20, AC_VERB_SET_COEF_INDEX, 0x0d},
- {0x20, AC_VERB_SET_PROC_COEF, 0x4000},
- {0x12, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN},
- {0x14, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
- {0x21, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP},
- {0x21, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
- {0x21, AC_VERB_SET_CONNECT_SEL, 0x00},
- {0x21, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | ALC880_HP_EVENT},
- {0x18, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | ALC880_MIC_EVENT},
- {0x22, AC_VERB_SET_CONNECT_SEL, 6},
- { }
-};
-
-/* toggle speaker-output according to the hp-jack state */
-static void alc269_speaker_automute(struct hda_codec *codec)
-{
- struct alc_spec *spec = codec->spec;
- unsigned int nid = spec->autocfg.hp_pins[0];
- unsigned int present;
- unsigned char bits;
-
- present = snd_hda_jack_detect(codec, nid);
- bits = present ? HDA_AMP_MUTE : 0;
- snd_hda_codec_amp_stereo(codec, 0x0c, HDA_INPUT, 0,
- HDA_AMP_MUTE, bits);
- snd_hda_codec_amp_stereo(codec, 0x0c, HDA_INPUT, 1,
- HDA_AMP_MUTE, bits);
- snd_hda_input_jack_report(codec, nid);
+ if (action == HDA_FIXUP_ACT_PRE_PROBE) {
+ struct alc_spec *spec = codec->spec;
+ spec->parse_flags |= HDA_PINCFG_HEADSET_MIC;
+ alc255_set_default_jack_type(codec);
+ }
+ else
+ alc_fixup_headset_mode(codec, fix, action);
}
-/* unsolicited event for HP jack sensing */
-static void alc269_laptop_unsol_event(struct hda_codec *codec,
- unsigned int res)
+static void alc_fixup_auto_mute_via_amp(struct hda_codec *codec,
+ const struct hda_fixup *fix, int action)
{
- switch (res >> 26) {
- case ALC880_HP_EVENT:
- alc269_speaker_automute(codec);
- break;
- case ALC880_MIC_EVENT:
- alc_mic_automute(codec);
- break;
+ if (action == HDA_FIXUP_ACT_PRE_PROBE) {
+ struct alc_spec *spec = codec->spec;
+ spec->gen.auto_mute_via_amp = 1;
}
}
-static void alc269_laptop_amic_setup(struct hda_codec *codec)
+static void alc_no_shutup(struct hda_codec *codec)
{
- struct alc_spec *spec = codec->spec;
- spec->autocfg.hp_pins[0] = 0x15;
- spec->autocfg.speaker_pins[0] = 0x14;
- spec->ext_mic.pin = 0x18;
- spec->ext_mic.mux_idx = 0;
- spec->int_mic.pin = 0x19;
- spec->int_mic.mux_idx = 1;
- spec->auto_mic = 1;
}
-static void alc269_laptop_dmic_setup(struct hda_codec *codec)
+static void alc_fixup_no_shutup(struct hda_codec *codec,
+ const struct hda_fixup *fix, int action)
{
- struct alc_spec *spec = codec->spec;
- spec->autocfg.hp_pins[0] = 0x15;
- spec->autocfg.speaker_pins[0] = 0x14;
- spec->ext_mic.pin = 0x18;
- spec->ext_mic.mux_idx = 0;
- spec->int_mic.pin = 0x12;
- spec->int_mic.mux_idx = 5;
- spec->auto_mic = 1;
+ if (action == HDA_FIXUP_ACT_PRE_PROBE) {
+ struct alc_spec *spec = codec->spec;
+ spec->shutup = alc_no_shutup;
+ }
}
-static void alc269vb_laptop_amic_setup(struct hda_codec *codec)
+static void alc_fixup_disable_aamix(struct hda_codec *codec,
+ const struct hda_fixup *fix, int action)
{
- struct alc_spec *spec = codec->spec;
- spec->autocfg.hp_pins[0] = 0x21;
- spec->autocfg.speaker_pins[0] = 0x14;
- spec->ext_mic.pin = 0x18;
- spec->ext_mic.mux_idx = 0;
- spec->int_mic.pin = 0x19;
- spec->int_mic.mux_idx = 1;
- spec->auto_mic = 1;
+ if (action == HDA_FIXUP_ACT_PRE_PROBE) {
+ struct alc_spec *spec = codec->spec;
+ /* Disable AA-loopback as it causes white noise */
+ spec->gen.mixer_nid = 0;
+ }
}
-static void alc269vb_laptop_dmic_setup(struct hda_codec *codec)
+static unsigned int alc_power_filter_xps13(struct hda_codec *codec,
+ hda_nid_t nid,
+ unsigned int power_state)
{
struct alc_spec *spec = codec->spec;
- spec->autocfg.hp_pins[0] = 0x21;
- spec->autocfg.speaker_pins[0] = 0x14;
- spec->ext_mic.pin = 0x18;
- spec->ext_mic.mux_idx = 0;
- spec->int_mic.pin = 0x12;
- spec->int_mic.mux_idx = 6;
- spec->auto_mic = 1;
-}
-static void alc269_laptop_inithook(struct hda_codec *codec)
-{
- alc269_speaker_automute(codec);
- alc_mic_automute(codec);
+ /* Avoid pop noises when headphones are plugged in */
+ if (spec->gen.hp_jack_present)
+ if (nid == codec->afg || nid == 0x02)
+ return AC_PWRST_D0;
+ return power_state;
}
-/*
- * generic initialization of ADC, input mixers and output mixers
- */
-static struct hda_verb alc269_init_verbs[] = {
- /*
- * Unmute ADC0 and set the default input to mic-in
- */
- {0x08, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
-
- /*
- * Set up output mixers (0x02 - 0x03)
- */
- /* set vol=0 to output mixers */
- {0x02, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
- {0x03, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
-
- /* set up input amps for analog loopback */
- /* Amp Indices: DAC = 0, mixer = 1 */
- {0x0c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
- {0x0c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
- {0x0d, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
- {0x0d, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
- {0x0e, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
- {0x0e, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
-
- {0x14, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
- {0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP},
- {0x16, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
- {0x18, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80},
- {0x19, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80},
- {0x1a, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN},
- {0x1b, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN},
-
- {0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
- {0x15, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
-
- /* FIXME: use Mux-type input source selection */
- /* Mixer elements: 0x18, 19, 1a, 1b, 1d, 0b */
- /* Input mixer1: unmute Mic, F-Mic, Line, CD inputs */
- {0x23, AC_VERB_SET_CONNECT_SEL, 0x00},
-
- /* set EAPD */
- {0x14, AC_VERB_SET_EAPD_BTLENABLE, 2},
- { }
-};
-
-static struct hda_verb alc269vb_init_verbs[] = {
- /*
- * Unmute ADC0 and set the default input to mic-in
- */
- {0x09, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
-
- /*
- * Set up output mixers (0x02 - 0x03)
- */
- /* set vol=0 to output mixers */
- {0x02, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
- {0x03, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
-
- /* set up input amps for analog loopback */
- /* Amp Indices: DAC = 0, mixer = 1 */
- {0x0c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
- {0x0c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
- {0x0d, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
- {0x0d, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
- {0x0f, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
- {0x0f, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
-
- {0x14, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
- {0x21, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP},
- {0x16, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
- {0x18, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80},
- {0x19, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80},
- {0x1a, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN},
- {0x1b, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN},
-
- {0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
- {0x21, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
-
- /* FIXME: use Mux-type input source selection */
- /* Mixer elements: 0x18, 19, 1a, 1b, 1d, 0b */
- /* Input mixer1: unmute Mic, F-Mic, Line, CD inputs */
- {0x22, AC_VERB_SET_CONNECT_SEL, 0x00},
-
- /* set EAPD */
- {0x14, AC_VERB_SET_EAPD_BTLENABLE, 2},
- { }
-};
-
-#define alc269_auto_create_multi_out_ctls \
- alc268_auto_create_multi_out_ctls
-#define alc269_auto_create_input_ctls \
- alc268_auto_create_input_ctls
-
-#ifdef CONFIG_SND_HDA_POWER_SAVE
-#define alc269_loopbacks alc880_loopbacks
-#endif
-
-/* pcm configuration: identical with ALC880 */
-#define alc269_pcm_analog_playback alc880_pcm_analog_playback
-#define alc269_pcm_analog_capture alc880_pcm_analog_capture
-#define alc269_pcm_digital_playback alc880_pcm_digital_playback
-#define alc269_pcm_digital_capture alc880_pcm_digital_capture
-
-static struct hda_pcm_stream alc269_44k_pcm_analog_playback = {
- .substreams = 1,
- .channels_min = 2,
- .channels_max = 8,
- .rates = SNDRV_PCM_RATE_44100, /* fixed rate */
- /* NID is set in alc_build_pcms */
- .ops = {
- .open = alc880_playback_pcm_open,
- .prepare = alc880_playback_pcm_prepare,
- .cleanup = alc880_playback_pcm_cleanup
- },
-};
-
-static struct hda_pcm_stream alc269_44k_pcm_analog_capture = {
- .substreams = 1,
- .channels_min = 2,
- .channels_max = 2,
- .rates = SNDRV_PCM_RATE_44100, /* fixed rate */
- /* NID is set in alc_build_pcms */
-};
-
-#ifdef CONFIG_SND_HDA_POWER_SAVE
-static int alc269_mic2_for_mute_led(struct hda_codec *codec)
+static void alc_fixup_dell_xps13(struct hda_codec *codec,
+ const struct hda_fixup *fix, int action)
{
- switch (codec->subsystem_id) {
- case 0x103c1586:
- return 1;
+ if (action == HDA_FIXUP_ACT_PROBE) {
+ struct alc_spec *spec = codec->spec;
+ spec->shutup = alc_no_shutup;
+ codec->power_filter = alc_power_filter_xps13;
}
- return 0;
}
-static int alc269_mic2_mute_check_ps(struct hda_codec *codec, hda_nid_t nid)
+static void alc_fixup_headset_mode_alc668(struct hda_codec *codec,
+ const struct hda_fixup *fix, int action)
{
- /* update mute-LED according to the speaker mute state */
- if (nid == 0x01 || nid == 0x14) {
- int pinval;
- if (snd_hda_codec_amp_read(codec, 0x14, 0, HDA_OUTPUT, 0) &
- HDA_AMP_MUTE)
- pinval = 0x24;
- else
- pinval = 0x20;
- /* mic2 vref pin is used for mute LED control */
- snd_hda_codec_update_cache(codec, 0x19, 0,
- AC_VERB_SET_PIN_WIDGET_CONTROL,
- pinval);
+ if (action == HDA_FIXUP_ACT_PRE_PROBE) {
+ int val;
+ alc_write_coef_idx(codec, 0xc4, 0x8000);
+ val = alc_read_coef_idx(codec, 0xc2);
+ alc_write_coef_idx(codec, 0xc2, val & 0xfe);
+ snd_hda_set_pin_ctl_cache(codec, 0x18, 0);
}
- return alc_check_power_status(codec, nid);
+ alc_fixup_headset_mode(codec, fix, action);
}
-#endif /* CONFIG_SND_HDA_POWER_SAVE */
-static int alc275_setup_dual_adc(struct hda_codec *codec)
+/* Returns the nid of the external mic input pin, or 0 if it cannot be found. */
+static int find_ext_mic_pin(struct hda_codec *codec)
{
struct alc_spec *spec = codec->spec;
+ struct auto_pin_cfg *cfg = &spec->gen.autocfg;
+ hda_nid_t nid;
+ unsigned int defcfg;
+ int i;
- if (codec->vendor_id != 0x10ec0275 || !spec->auto_mic)
- return 0;
- if ((spec->ext_mic.pin >= 0x18 && spec->int_mic.pin <= 0x13) ||
- (spec->ext_mic.pin <= 0x12 && spec->int_mic.pin >= 0x18)) {
- if (spec->ext_mic.pin <= 0x12) {
- spec->private_adc_nids[0] = 0x08;
- spec->private_adc_nids[1] = 0x11;
- spec->private_capsrc_nids[0] = 0x23;
- spec->private_capsrc_nids[1] = 0x22;
- } else {
- spec->private_adc_nids[0] = 0x11;
- spec->private_adc_nids[1] = 0x08;
- spec->private_capsrc_nids[0] = 0x22;
- spec->private_capsrc_nids[1] = 0x23;
- }
- spec->adc_nids = spec->private_adc_nids;
- spec->capsrc_nids = spec->private_capsrc_nids;
- spec->num_adc_nids = 2;
- spec->dual_adc_switch = 1;
- snd_printdd("realtek: enabling dual ADC switchg (%02x:%02x)\n",
- spec->adc_nids[0], spec->adc_nids[1]);
- return 1;
+ for (i = 0; i < cfg->num_inputs; i++) {
+ if (cfg->inputs[i].type != AUTO_PIN_MIC)
+ continue;
+ nid = cfg->inputs[i].pin;
+ defcfg = snd_hda_codec_get_pincfg(codec, nid);
+ if (snd_hda_get_input_pin_attr(defcfg) == INPUT_PIN_ATTR_INT)
+ continue;
+ return nid;
}
+
return 0;
}
-/* different alc269-variants */
-enum {
- ALC269_TYPE_NORMAL,
- ALC269_TYPE_ALC258,
- ALC269_TYPE_ALC259,
- ALC269_TYPE_ALC269VB,
- ALC269_TYPE_ALC270,
- ALC269_TYPE_ALC271X,
-};
-
-/*
- * BIOS auto configuration
- */
-static int alc269_parse_auto_config(struct hda_codec *codec)
+static void alc271_hp_gate_mic_jack(struct hda_codec *codec,
+ const struct hda_fixup *fix,
+ int action)
{
struct alc_spec *spec = codec->spec;
- int err;
- static hda_nid_t alc269_ignore[] = { 0x1d, 0 };
- err = snd_hda_parse_pin_def_config(codec, &spec->autocfg,
- alc269_ignore);
- if (err < 0)
- return err;
+ if (action == HDA_FIXUP_ACT_PROBE) {
+ int mic_pin = find_ext_mic_pin(codec);
+ int hp_pin = spec->gen.autocfg.hp_pins[0];
- err = alc269_auto_create_multi_out_ctls(spec, &spec->autocfg);
- if (err < 0)
- return err;
- if (spec->codec_variant == ALC269_TYPE_NORMAL)
- err = alc269_auto_create_input_ctls(codec, &spec->autocfg);
- else
- err = alc_auto_create_input_ctls(codec, &spec->autocfg, 0,
- 0x22, 0);
- if (err < 0)
- return err;
+ if (snd_BUG_ON(!mic_pin || !hp_pin))
+ return;
+ snd_hda_jack_set_gating_jack(codec, mic_pin, hp_pin);
+ }
+}
- spec->multiout.max_channels = spec->multiout.num_dacs * 2;
+static void alc269_fixup_limit_int_mic_boost(struct hda_codec *codec,
+ const struct hda_fixup *fix,
+ int action)
+{
+ struct alc_spec *spec = codec->spec;
+ struct auto_pin_cfg *cfg = &spec->gen.autocfg;
+ int i;
- alc_auto_parse_digital(codec);
+ /* The mic boosts on level 2 and 3 are too noisy
+ on the internal mic input.
+ Therefore limit the boost to 0 or 1. */
- if (spec->kctls.list)
- add_mixer(spec, spec->kctls.list);
+ if (action != HDA_FIXUP_ACT_PROBE)
+ return;
- if (spec->codec_variant != ALC269_TYPE_NORMAL) {
- add_verb(spec, alc269vb_init_verbs);
- alc_ssid_check(codec, 0, 0x1b, 0x14, 0x21);
- } else {
- add_verb(spec, alc269_init_verbs);
- alc_ssid_check(codec, 0x15, 0x1b, 0x14, 0);
+ for (i = 0; i < cfg->num_inputs; i++) {
+ hda_nid_t nid = cfg->inputs[i].pin;
+ unsigned int defcfg;
+ if (cfg->inputs[i].type != AUTO_PIN_MIC)
+ continue;
+ defcfg = snd_hda_codec_get_pincfg(codec, nid);
+ if (snd_hda_get_input_pin_attr(defcfg) != INPUT_PIN_ATTR_INT)
+ continue;
+
+ snd_hda_override_amp_caps(codec, nid, HDA_INPUT,
+ (0x00 << AC_AMPCAP_OFFSET_SHIFT) |
+ (0x01 << AC_AMPCAP_NUM_STEPS_SHIFT) |
+ (0x2f << AC_AMPCAP_STEP_SIZE_SHIFT) |
+ (0 << AC_AMPCAP_MUTE_SHIFT));
}
+}
- spec->num_mux_defs = 1;
- spec->input_mux = &spec->private_imux[0];
+static void alc283_hp_automute_hook(struct hda_codec *codec,
+ struct hda_jack_tbl *jack)
+{
+ struct alc_spec *spec = codec->spec;
+ int vref;
- if (!alc275_setup_dual_adc(codec))
- fillup_priv_adc_nids(codec, alc269_adc_candidates,
- sizeof(alc269_adc_candidates));
+ msleep(200);
+ snd_hda_gen_hp_automute(codec, jack);
- err = alc_auto_add_mic_boost(codec);
- if (err < 0)
- return err;
-
- if (!spec->cap_mixer && !spec->no_analog)
- set_capture_mixer(codec);
+ vref = spec->gen.hp_jack_present ? PIN_VREF80 : 0;
- return 1;
+ msleep(600);
+ snd_hda_codec_write(codec, 0x19, 0, AC_VERB_SET_PIN_WIDGET_CONTROL,
+ vref);
}
-#define alc269_auto_init_multi_out alc268_auto_init_multi_out
-#define alc269_auto_init_hp_out alc268_auto_init_hp_out
-#define alc269_auto_init_analog_input alc882_auto_init_analog_input
-#define alc269_auto_init_input_src alc882_auto_init_input_src
-
-
-/* init callback for auto-configuration model -- overriding the default init */
-static void alc269_auto_init(struct hda_codec *codec)
+static void alc283_fixup_chromebook(struct hda_codec *codec,
+ const struct hda_fixup *fix, int action)
{
struct alc_spec *spec = codec->spec;
- alc269_auto_init_multi_out(codec);
- alc269_auto_init_hp_out(codec);
- alc269_auto_init_analog_input(codec);
- if (!spec->dual_adc_switch)
- alc269_auto_init_input_src(codec);
- alc_auto_init_digital(codec);
- if (spec->unsol_event)
- alc_inithook(codec);
-}
+ int val;
-#ifdef SND_HDA_NEEDS_RESUME
-static void alc269_toggle_power_output(struct hda_codec *codec, int power_up)
-{
- int val = alc_read_coef_idx(codec, 0x04);
- if (power_up)
- val |= 1 << 11;
- else
- val &= ~(1 << 11);
- alc_write_coef_idx(codec, 0x04, val);
+ switch (action) {
+ case HDA_FIXUP_ACT_PRE_PROBE:
+ snd_hda_override_wcaps(codec, 0x03, 0);
+ /* Disable AA-loopback as it causes white noise */
+ spec->gen.mixer_nid = 0;
+ break;
+ case HDA_FIXUP_ACT_INIT:
+ /* MIC2-VREF control */
+ /* Set to manual mode */
+ val = alc_read_coef_idx(codec, 0x06);
+ alc_write_coef_idx(codec, 0x06, val & ~0x000c);
+ /* Enable Line1 input control by verb */
+ val = alc_read_coef_idx(codec, 0x1a);
+ alc_write_coef_idx(codec, 0x1a, val | (1 << 4));
+ break;
+ }
}
-#ifdef CONFIG_SND_HDA_POWER_SAVE
-static int alc269_suspend(struct hda_codec *codec, pm_message_t state)
+static void alc283_fixup_sense_combo_jack(struct hda_codec *codec,
+ const struct hda_fixup *fix, int action)
{
struct alc_spec *spec = codec->spec;
+ int val;
- if ((alc_read_coef_idx(codec, 0) & 0x00ff) == 0x017)
- alc269_toggle_power_output(codec, 0);
- if ((alc_read_coef_idx(codec, 0) & 0x00ff) == 0x018) {
- alc269_toggle_power_output(codec, 0);
- msleep(150);
+ switch (action) {
+ case HDA_FIXUP_ACT_PRE_PROBE:
+ spec->gen.hp_automute_hook = alc283_hp_automute_hook;
+ break;
+ case HDA_FIXUP_ACT_INIT:
+ /* MIC2-VREF control */
+ /* Set to manual mode */
+ val = alc_read_coef_idx(codec, 0x06);
+ alc_write_coef_idx(codec, 0x06, val & ~0x000c);
+ break;
}
-
- alc_shutup(codec);
- if (spec && spec->power_hook)
- spec->power_hook(codec);
- return 0;
}
-#endif /* CONFIG_SND_HDA_POWER_SAVE */
-static int alc269_resume(struct hda_codec *codec)
+/* mute tablet speaker pin (0x14) via dock plugging in addition */
+static void asus_tx300_automute(struct hda_codec *codec)
{
- if ((alc_read_coef_idx(codec, 0) & 0x00ff) == 0x018) {
- alc269_toggle_power_output(codec, 0);
- msleep(150);
- }
-
- codec->patch_ops.init(codec);
+ struct alc_spec *spec = codec->spec;
+ snd_hda_gen_update_outputs(codec);
+ if (snd_hda_jack_detect(codec, 0x1b))
+ spec->gen.mute_bits |= (1ULL << 0x14);
+}
- if ((alc_read_coef_idx(codec, 0) & 0x00ff) == 0x017) {
- alc269_toggle_power_output(codec, 1);
- msleep(200);
+static void alc282_fixup_asus_tx300(struct hda_codec *codec,
+ const struct hda_fixup *fix, int action)
+{
+ struct alc_spec *spec = codec->spec;
+ /* TX300 needs to set up GPIO2 for the speaker amp */
+ static const struct hda_verb gpio2_verbs[] = {
+ { 0x01, AC_VERB_SET_GPIO_MASK, 0x04 },
+ { 0x01, AC_VERB_SET_GPIO_DIRECTION, 0x04 },
+ { 0x01, AC_VERB_SET_GPIO_DATA, 0x04 },
+ {}
+ };
+ static const struct hda_pintbl dock_pins[] = {
+ { 0x1b, 0x21114000 }, /* dock speaker pin */
+ {}
+ };
+ struct snd_kcontrol *kctl;
+
+ switch (action) {
+ case HDA_FIXUP_ACT_PRE_PROBE:
+ snd_hda_add_verbs(codec, gpio2_verbs);
+ snd_hda_apply_pincfgs(codec, dock_pins);
+ spec->gen.auto_mute_via_amp = 1;
+ spec->gen.automute_hook = asus_tx300_automute;
+ snd_hda_jack_detect_enable_callback(codec, 0x1b,
+ HDA_GEN_HP_EVENT,
+ snd_hda_gen_hp_automute);
+ break;
+ case HDA_FIXUP_ACT_BUILD:
+ /* this is a bit tricky; give more sane names for the main
+ * (tablet) speaker and the dock speaker, respectively
+ */
+ kctl = snd_hda_find_mixer_ctl(codec, "Speaker Playback Switch");
+ if (kctl)
+ strcpy(kctl->id.name, "Dock Speaker Playback Switch");
+ kctl = snd_hda_find_mixer_ctl(codec, "Bass Speaker Playback Switch");
+ if (kctl)
+ strcpy(kctl->id.name, "Speaker Playback Switch");
+ break;
}
-
- if ((alc_read_coef_idx(codec, 0) & 0x00ff) == 0x018)
- alc269_toggle_power_output(codec, 1);
-
- snd_hda_codec_resume_amp(codec);
- snd_hda_codec_resume_cache(codec);
- hda_call_check_power_status(codec, 0x01);
- return 0;
}
-#endif /* SND_HDA_NEEDS_RESUME */
-static void alc269_fixup_hweq(struct hda_codec *codec,
- const struct alc_fixup *fix, int action)
+static void alc290_fixup_mono_speakers(struct hda_codec *codec,
+ const struct hda_fixup *fix, int action)
{
- int coef;
-
- if (action != ALC_FIXUP_ACT_INIT)
- return;
- coef = alc_read_coef_idx(codec, 0x1e);
- alc_write_coef_idx(codec, 0x1e, coef | 0x80);
+ if (action == HDA_FIXUP_ACT_PRE_PROBE) {
+ /* DAC node 0x03 is giving mono output. We therefore want to
+ make sure 0x14 (front speaker) and 0x15 (headphones) use the
+ stereo DAC, while leaving 0x17 (bass speaker) for node 0x03. */
+ hda_nid_t conn1[2] = { 0x0c };
+ snd_hda_override_conn_list(codec, 0x14, 1, conn1);
+ snd_hda_override_conn_list(codec, 0x15, 1, conn1);
+ }
}
+/* for hda_fixup_thinkpad_acpi() */
+#include "thinkpad_helper.c"
+
enum {
ALC269_FIXUP_SONY_VAIO,
ALC275_FIXUP_SONY_VAIO_GPIO2,
@@ -14876,18 +4239,71 @@ enum {
ALC269_FIXUP_ASUS_G73JW,
ALC269_FIXUP_LENOVO_EAPD,
ALC275_FIXUP_SONY_HWEQ,
-};
-
-static const struct alc_fixup alc269_fixups[] = {
+ ALC275_FIXUP_SONY_DISABLE_AAMIX,
+ ALC271_FIXUP_DMIC,
+ ALC269_FIXUP_PCM_44K,
+ ALC269_FIXUP_STEREO_DMIC,
+ ALC269_FIXUP_HEADSET_MIC,
+ ALC269_FIXUP_QUANTA_MUTE,
+ ALC269_FIXUP_LIFEBOOK,
+ ALC269_FIXUP_LIFEBOOK_EXTMIC,
+ ALC269_FIXUP_AMIC,
+ ALC269_FIXUP_DMIC,
+ ALC269VB_FIXUP_AMIC,
+ ALC269VB_FIXUP_DMIC,
+ ALC269_FIXUP_HP_MUTE_LED,
+ ALC269_FIXUP_HP_MUTE_LED_MIC1,
+ ALC269_FIXUP_HP_MUTE_LED_MIC2,
+ ALC269_FIXUP_HP_GPIO_LED,
+ ALC269_FIXUP_INV_DMIC,
+ ALC269_FIXUP_LENOVO_DOCK,
+ ALC269_FIXUP_NO_SHUTUP,
+ ALC286_FIXUP_SONY_MIC_NO_PRESENCE,
+ ALC269_FIXUP_PINCFG_NO_HP_TO_LINEOUT,
+ ALC269_FIXUP_DELL1_MIC_NO_PRESENCE,
+ ALC269_FIXUP_DELL2_MIC_NO_PRESENCE,
+ ALC269_FIXUP_DELL3_MIC_NO_PRESENCE,
+ ALC269_FIXUP_HEADSET_MODE,
+ ALC269_FIXUP_HEADSET_MODE_NO_HP_MIC,
+ ALC269_FIXUP_ASUS_X101_FUNC,
+ ALC269_FIXUP_ASUS_X101_VERB,
+ ALC269_FIXUP_ASUS_X101,
+ ALC271_FIXUP_AMIC_MIC2,
+ ALC271_FIXUP_HP_GATE_MIC_JACK,
+ ALC271_FIXUP_HP_GATE_MIC_JACK_E1_572,
+ ALC269_FIXUP_ACER_AC700,
+ ALC269_FIXUP_LIMIT_INT_MIC_BOOST,
+ ALC269VB_FIXUP_ASUS_ZENBOOK,
+ ALC269VB_FIXUP_ASUS_ZENBOOK_UX31A,
+ ALC269_FIXUP_LIMIT_INT_MIC_BOOST_MUTE_LED,
+ ALC269VB_FIXUP_ORDISSIMO_EVE2,
+ ALC283_FIXUP_CHROME_BOOK,
+ ALC283_FIXUP_SENSE_COMBO_JACK,
+ ALC282_FIXUP_ASUS_TX300,
+ ALC283_FIXUP_INT_MIC,
+ ALC290_FIXUP_MONO_SPEAKERS,
+ ALC290_FIXUP_MONO_SPEAKERS_HSJACK,
+ ALC290_FIXUP_SUBWOOFER,
+ ALC290_FIXUP_SUBWOOFER_HSJACK,
+ ALC269_FIXUP_THINKPAD_ACPI,
+ ALC255_FIXUP_DELL1_MIC_NO_PRESENCE,
+ ALC255_FIXUP_DELL2_MIC_NO_PRESENCE,
+ ALC255_FIXUP_HEADSET_MODE,
+ ALC255_FIXUP_HEADSET_MODE_NO_HP_MIC,
+ ALC293_FIXUP_DELL1_MIC_NO_PRESENCE,
+ ALC292_FIXUP_TPT440_DOCK,
+};
+
+static const struct hda_fixup alc269_fixups[] = {
[ALC269_FIXUP_SONY_VAIO] = {
- .type = ALC_FIXUP_VERBS,
- .v.verbs = (const struct hda_verb[]) {
- {0x19, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREFGRD},
+ .type = HDA_FIXUP_PINCTLS,
+ .v.pins = (const struct hda_pintbl[]) {
+ {0x19, PIN_VREFGRD},
{}
}
},
[ALC275_FIXUP_SONY_VAIO_GPIO2] = {
- .type = ALC_FIXUP_VERBS,
+ .type = HDA_FIXUP_VERBS,
.v.verbs = (const struct hda_verb[]) {
{0x01, AC_VERB_SET_GPIO_MASK, 0x04},
{0x01, AC_VERB_SET_GPIO_DIRECTION, 0x04},
@@ -14898,7 +4314,7 @@ static const struct alc_fixup alc269_fixups[] = {
.chain_id = ALC269_FIXUP_SONY_VAIO
},
[ALC269_FIXUP_DELL_M101Z] = {
- .type = ALC_FIXUP_VERBS,
+ .type = HDA_FIXUP_VERBS,
.v.verbs = (const struct hda_verb[]) {
/* Enables internal speaker */
{0x20, AC_VERB_SET_COEF_INDEX, 13},
@@ -14907,1544 +4323,1073 @@ static const struct alc_fixup alc269_fixups[] = {
}
},
[ALC269_FIXUP_SKU_IGNORE] = {
- .type = ALC_FIXUP_SKU,
- .v.sku = ALC_FIXUP_SKU_IGNORE,
+ .type = HDA_FIXUP_FUNC,
+ .v.func = alc_fixup_sku_ignore,
},
[ALC269_FIXUP_ASUS_G73JW] = {
- .type = ALC_FIXUP_PINS,
- .v.pins = (const struct alc_pincfg[]) {
+ .type = HDA_FIXUP_PINS,
+ .v.pins = (const struct hda_pintbl[]) {
{ 0x17, 0x99130111 }, /* subwoofer */
{ }
}
},
[ALC269_FIXUP_LENOVO_EAPD] = {
- .type = ALC_FIXUP_VERBS,
+ .type = HDA_FIXUP_VERBS,
.v.verbs = (const struct hda_verb[]) {
{0x14, AC_VERB_SET_EAPD_BTLENABLE, 0},
{}
}
},
[ALC275_FIXUP_SONY_HWEQ] = {
- .type = ALC_FIXUP_FUNC,
+ .type = HDA_FIXUP_FUNC,
.v.func = alc269_fixup_hweq,
.chained = true,
.chain_id = ALC275_FIXUP_SONY_VAIO_GPIO2
- }
+ },
+ [ALC275_FIXUP_SONY_DISABLE_AAMIX] = {
+ .type = HDA_FIXUP_FUNC,
+ .v.func = alc_fixup_disable_aamix,
+ .chained = true,
+ .chain_id = ALC269_FIXUP_SONY_VAIO
+ },
+ [ALC271_FIXUP_DMIC] = {
+ .type = HDA_FIXUP_FUNC,
+ .v.func = alc271_fixup_dmic,
+ },
+ [ALC269_FIXUP_PCM_44K] = {
+ .type = HDA_FIXUP_FUNC,
+ .v.func = alc269_fixup_pcm_44k,
+ .chained = true,
+ .chain_id = ALC269_FIXUP_QUANTA_MUTE
+ },
+ [ALC269_FIXUP_STEREO_DMIC] = {
+ .type = HDA_FIXUP_FUNC,
+ .v.func = alc269_fixup_stereo_dmic,
+ },
+ [ALC269_FIXUP_HEADSET_MIC] = {
+ .type = HDA_FIXUP_FUNC,
+ .v.func = alc269_fixup_headset_mic,
+ },
+ [ALC269_FIXUP_QUANTA_MUTE] = {
+ .type = HDA_FIXUP_FUNC,
+ .v.func = alc269_fixup_quanta_mute,
+ },
+ [ALC269_FIXUP_LIFEBOOK] = {
+ .type = HDA_FIXUP_PINS,
+ .v.pins = (const struct hda_pintbl[]) {
+ { 0x1a, 0x2101103f }, /* dock line-out */
+ { 0x1b, 0x23a11040 }, /* dock mic-in */
+ { }
+ },
+ .chained = true,
+ .chain_id = ALC269_FIXUP_QUANTA_MUTE
+ },
+ [ALC269_FIXUP_LIFEBOOK_EXTMIC] = {
+ .type = HDA_FIXUP_PINS,
+ .v.pins = (const struct hda_pintbl[]) {
+ { 0x19, 0x01a1903c }, /* headset mic, with jack detect */
+ { }
+ },
+ },
+ [ALC269_FIXUP_AMIC] = {
+ .type = HDA_FIXUP_PINS,
+ .v.pins = (const struct hda_pintbl[]) {
+ { 0x14, 0x99130110 }, /* speaker */
+ { 0x15, 0x0121401f }, /* HP out */
+ { 0x18, 0x01a19c20 }, /* mic */
+ { 0x19, 0x99a3092f }, /* int-mic */
+ { }
+ },
+ },
+ [ALC269_FIXUP_DMIC] = {
+ .type = HDA_FIXUP_PINS,
+ .v.pins = (const struct hda_pintbl[]) {
+ { 0x12, 0x99a3092f }, /* int-mic */
+ { 0x14, 0x99130110 }, /* speaker */
+ { 0x15, 0x0121401f }, /* HP out */
+ { 0x18, 0x01a19c20 }, /* mic */
+ { }
+ },
+ },
+ [ALC269VB_FIXUP_AMIC] = {
+ .type = HDA_FIXUP_PINS,
+ .v.pins = (const struct hda_pintbl[]) {
+ { 0x14, 0x99130110 }, /* speaker */
+ { 0x18, 0x01a19c20 }, /* mic */
+ { 0x19, 0x99a3092f }, /* int-mic */
+ { 0x21, 0x0121401f }, /* HP out */
+ { }
+ },
+ },
+ [ALC269VB_FIXUP_DMIC] = {
+ .type = HDA_FIXUP_PINS,
+ .v.pins = (const struct hda_pintbl[]) {
+ { 0x12, 0x99a3092f }, /* int-mic */
+ { 0x14, 0x99130110 }, /* speaker */
+ { 0x18, 0x01a19c20 }, /* mic */
+ { 0x21, 0x0121401f }, /* HP out */
+ { }
+ },
+ },
+ [ALC269_FIXUP_HP_MUTE_LED] = {
+ .type = HDA_FIXUP_FUNC,
+ .v.func = alc269_fixup_hp_mute_led,
+ },
+ [ALC269_FIXUP_HP_MUTE_LED_MIC1] = {
+ .type = HDA_FIXUP_FUNC,
+ .v.func = alc269_fixup_hp_mute_led_mic1,
+ },
+ [ALC269_FIXUP_HP_MUTE_LED_MIC2] = {
+ .type = HDA_FIXUP_FUNC,
+ .v.func = alc269_fixup_hp_mute_led_mic2,
+ },
+ [ALC269_FIXUP_HP_GPIO_LED] = {
+ .type = HDA_FIXUP_FUNC,
+ .v.func = alc269_fixup_hp_gpio_led,
+ },
+ [ALC269_FIXUP_INV_DMIC] = {
+ .type = HDA_FIXUP_FUNC,
+ .v.func = alc_fixup_inv_dmic_0x12,
+ },
+ [ALC269_FIXUP_NO_SHUTUP] = {
+ .type = HDA_FIXUP_FUNC,
+ .v.func = alc_fixup_no_shutup,
+ },
+ [ALC269_FIXUP_LENOVO_DOCK] = {
+ .type = HDA_FIXUP_PINS,
+ .v.pins = (const struct hda_pintbl[]) {
+ { 0x19, 0x23a11040 }, /* dock mic */
+ { 0x1b, 0x2121103f }, /* dock headphone */
+ { }
+ },
+ .chained = true,
+ .chain_id = ALC269_FIXUP_PINCFG_NO_HP_TO_LINEOUT
+ },
+ [ALC269_FIXUP_PINCFG_NO_HP_TO_LINEOUT] = {
+ .type = HDA_FIXUP_FUNC,
+ .v.func = alc269_fixup_pincfg_no_hp_to_lineout,
+ .chained = true,
+ .chain_id = ALC269_FIXUP_THINKPAD_ACPI,
+ },
+ [ALC269_FIXUP_DELL1_MIC_NO_PRESENCE] = {
+ .type = HDA_FIXUP_PINS,
+ .v.pins = (const struct hda_pintbl[]) {
+ { 0x19, 0x01a1913c }, /* use as headset mic, without its own jack detect */
+ { 0x1a, 0x01a1913d }, /* use as headphone mic, without its own jack detect */
+ { }
+ },
+ .chained = true,
+ .chain_id = ALC269_FIXUP_HEADSET_MODE
+ },
+ [ALC269_FIXUP_DELL2_MIC_NO_PRESENCE] = {
+ .type = HDA_FIXUP_PINS,
+ .v.pins = (const struct hda_pintbl[]) {
+ { 0x16, 0x21014020 }, /* dock line out */
+ { 0x19, 0x21a19030 }, /* dock mic */
+ { 0x1a, 0x01a1913c }, /* use as headset mic, without its own jack detect */
+ { }
+ },
+ .chained = true,
+ .chain_id = ALC269_FIXUP_HEADSET_MODE_NO_HP_MIC
+ },
+ [ALC269_FIXUP_DELL3_MIC_NO_PRESENCE] = {
+ .type = HDA_FIXUP_PINS,
+ .v.pins = (const struct hda_pintbl[]) {
+ { 0x1a, 0x01a1913c }, /* use as headset mic, without its own jack detect */
+ { }
+ },
+ .chained = true,
+ .chain_id = ALC269_FIXUP_HEADSET_MODE_NO_HP_MIC
+ },
+ [ALC269_FIXUP_HEADSET_MODE] = {
+ .type = HDA_FIXUP_FUNC,
+ .v.func = alc_fixup_headset_mode,
+ },
+ [ALC269_FIXUP_HEADSET_MODE_NO_HP_MIC] = {
+ .type = HDA_FIXUP_FUNC,
+ .v.func = alc_fixup_headset_mode_no_hp_mic,
+ },
+ [ALC286_FIXUP_SONY_MIC_NO_PRESENCE] = {
+ .type = HDA_FIXUP_PINS,
+ .v.pins = (const struct hda_pintbl[]) {
+ { 0x18, 0x01a1913c }, /* use as headset mic, without its own jack detect */
+ { }
+ },
+ .chained = true,
+ .chain_id = ALC269_FIXUP_HEADSET_MIC
+ },
+ [ALC269_FIXUP_ASUS_X101_FUNC] = {
+ .type = HDA_FIXUP_FUNC,
+ .v.func = alc269_fixup_x101_headset_mic,
+ },
+ [ALC269_FIXUP_ASUS_X101_VERB] = {
+ .type = HDA_FIXUP_VERBS,
+ .v.verbs = (const struct hda_verb[]) {
+ {0x18, AC_VERB_SET_PIN_WIDGET_CONTROL, 0},
+ {0x20, AC_VERB_SET_COEF_INDEX, 0x08},
+ {0x20, AC_VERB_SET_PROC_COEF, 0x0310},
+ { }
+ },
+ .chained = true,
+ .chain_id = ALC269_FIXUP_ASUS_X101_FUNC
+ },
+ [ALC269_FIXUP_ASUS_X101] = {
+ .type = HDA_FIXUP_PINS,
+ .v.pins = (const struct hda_pintbl[]) {
+ { 0x18, 0x04a1182c }, /* Headset mic */
+ { }
+ },
+ .chained = true,
+ .chain_id = ALC269_FIXUP_ASUS_X101_VERB
+ },
+ [ALC271_FIXUP_AMIC_MIC2] = {
+ .type = HDA_FIXUP_PINS,
+ .v.pins = (const struct hda_pintbl[]) {
+ { 0x14, 0x99130110 }, /* speaker */
+ { 0x19, 0x01a19c20 }, /* mic */
+ { 0x1b, 0x99a7012f }, /* int-mic */
+ { 0x21, 0x0121401f }, /* HP out */
+ { }
+ },
+ },
+ [ALC271_FIXUP_HP_GATE_MIC_JACK] = {
+ .type = HDA_FIXUP_FUNC,
+ .v.func = alc271_hp_gate_mic_jack,
+ .chained = true,
+ .chain_id = ALC271_FIXUP_AMIC_MIC2,
+ },
+ [ALC271_FIXUP_HP_GATE_MIC_JACK_E1_572] = {
+ .type = HDA_FIXUP_FUNC,
+ .v.func = alc269_fixup_limit_int_mic_boost,
+ .chained = true,
+ .chain_id = ALC271_FIXUP_HP_GATE_MIC_JACK,
+ },
+ [ALC269_FIXUP_ACER_AC700] = {
+ .type = HDA_FIXUP_PINS,
+ .v.pins = (const struct hda_pintbl[]) {
+ { 0x12, 0x99a3092f }, /* int-mic */
+ { 0x14, 0x99130110 }, /* speaker */
+ { 0x18, 0x03a11c20 }, /* mic */
+ { 0x1e, 0x0346101e }, /* SPDIF1 */
+ { 0x21, 0x0321101f }, /* HP out */
+ { }
+ },
+ .chained = true,
+ .chain_id = ALC271_FIXUP_DMIC,
+ },
+ [ALC269_FIXUP_LIMIT_INT_MIC_BOOST] = {
+ .type = HDA_FIXUP_FUNC,
+ .v.func = alc269_fixup_limit_int_mic_boost,
+ .chained = true,
+ .chain_id = ALC269_FIXUP_THINKPAD_ACPI,
+ },
+ [ALC269VB_FIXUP_ASUS_ZENBOOK] = {
+ .type = HDA_FIXUP_FUNC,
+ .v.func = alc269_fixup_limit_int_mic_boost,
+ .chained = true,
+ .chain_id = ALC269VB_FIXUP_DMIC,
+ },
+ [ALC269VB_FIXUP_ASUS_ZENBOOK_UX31A] = {
+ .type = HDA_FIXUP_VERBS,
+ .v.verbs = (const struct hda_verb[]) {
+ /* class-D output amp +5dB */
+ { 0x20, AC_VERB_SET_COEF_INDEX, 0x12 },
+ { 0x20, AC_VERB_SET_PROC_COEF, 0x2800 },
+ {}
+ },
+ .chained = true,
+ .chain_id = ALC269VB_FIXUP_ASUS_ZENBOOK,
+ },
+ [ALC269_FIXUP_LIMIT_INT_MIC_BOOST_MUTE_LED] = {
+ .type = HDA_FIXUP_FUNC,
+ .v.func = alc269_fixup_limit_int_mic_boost,
+ .chained = true,
+ .chain_id = ALC269_FIXUP_HP_MUTE_LED_MIC1,
+ },
+ [ALC269VB_FIXUP_ORDISSIMO_EVE2] = {
+ .type = HDA_FIXUP_PINS,
+ .v.pins = (const struct hda_pintbl[]) {
+ { 0x12, 0x99a3092f }, /* int-mic */
+ { 0x18, 0x03a11d20 }, /* mic */
+ { 0x19, 0x411111f0 }, /* Unused bogus pin */
+ { }
+ },
+ },
+ [ALC283_FIXUP_CHROME_BOOK] = {
+ .type = HDA_FIXUP_FUNC,
+ .v.func = alc283_fixup_chromebook,
+ },
+ [ALC283_FIXUP_SENSE_COMBO_JACK] = {
+ .type = HDA_FIXUP_FUNC,
+ .v.func = alc283_fixup_sense_combo_jack,
+ .chained = true,
+ .chain_id = ALC283_FIXUP_CHROME_BOOK,
+ },
+ [ALC282_FIXUP_ASUS_TX300] = {
+ .type = HDA_FIXUP_FUNC,
+ .v.func = alc282_fixup_asus_tx300,
+ },
+ [ALC283_FIXUP_INT_MIC] = {
+ .type = HDA_FIXUP_VERBS,
+ .v.verbs = (const struct hda_verb[]) {
+ {0x20, AC_VERB_SET_COEF_INDEX, 0x1a},
+ {0x20, AC_VERB_SET_PROC_COEF, 0x0011},
+ { }
+ },
+ .chained = true,
+ .chain_id = ALC269_FIXUP_LIMIT_INT_MIC_BOOST
+ },
+ [ALC290_FIXUP_SUBWOOFER_HSJACK] = {
+ .type = HDA_FIXUP_PINS,
+ .v.pins = (const struct hda_pintbl[]) {
+ { 0x17, 0x90170112 }, /* subwoofer */
+ { }
+ },
+ .chained = true,
+ .chain_id = ALC290_FIXUP_MONO_SPEAKERS_HSJACK,
+ },
+ [ALC290_FIXUP_SUBWOOFER] = {
+ .type = HDA_FIXUP_PINS,
+ .v.pins = (const struct hda_pintbl[]) {
+ { 0x17, 0x90170112 }, /* subwoofer */
+ { }
+ },
+ .chained = true,
+ .chain_id = ALC290_FIXUP_MONO_SPEAKERS,
+ },
+ [ALC290_FIXUP_MONO_SPEAKERS] = {
+ .type = HDA_FIXUP_FUNC,
+ .v.func = alc290_fixup_mono_speakers,
+ },
+ [ALC290_FIXUP_MONO_SPEAKERS_HSJACK] = {
+ .type = HDA_FIXUP_FUNC,
+ .v.func = alc290_fixup_mono_speakers,
+ .chained = true,
+ .chain_id = ALC269_FIXUP_DELL3_MIC_NO_PRESENCE,
+ },
+ [ALC269_FIXUP_THINKPAD_ACPI] = {
+ .type = HDA_FIXUP_FUNC,
+ .v.func = hda_fixup_thinkpad_acpi,
+ },
+ [ALC255_FIXUP_DELL1_MIC_NO_PRESENCE] = {
+ .type = HDA_FIXUP_PINS,
+ .v.pins = (const struct hda_pintbl[]) {
+ { 0x19, 0x01a1913c }, /* use as headset mic, without its own jack detect */
+ { 0x1a, 0x01a1913d }, /* use as headphone mic, without its own jack detect */
+ { }
+ },
+ .chained = true,
+ .chain_id = ALC255_FIXUP_HEADSET_MODE
+ },
+ [ALC255_FIXUP_DELL2_MIC_NO_PRESENCE] = {
+ .type = HDA_FIXUP_PINS,
+ .v.pins = (const struct hda_pintbl[]) {
+ { 0x19, 0x01a1913c }, /* use as headset mic, without its own jack detect */
+ { }
+ },
+ .chained = true,
+ .chain_id = ALC255_FIXUP_HEADSET_MODE_NO_HP_MIC
+ },
+ [ALC255_FIXUP_HEADSET_MODE] = {
+ .type = HDA_FIXUP_FUNC,
+ .v.func = alc_fixup_headset_mode_alc255,
+ },
+ [ALC255_FIXUP_HEADSET_MODE_NO_HP_MIC] = {
+ .type = HDA_FIXUP_FUNC,
+ .v.func = alc_fixup_headset_mode_alc255_no_hp_mic,
+ },
+ [ALC293_FIXUP_DELL1_MIC_NO_PRESENCE] = {
+ .type = HDA_FIXUP_PINS,
+ .v.pins = (const struct hda_pintbl[]) {
+ { 0x18, 0x01a1913d }, /* use as headphone mic, without its own jack detect */
+ { 0x1a, 0x01a1913c }, /* use as headset mic, without its own jack detect */
+ { }
+ },
+ .chained = true,
+ .chain_id = ALC269_FIXUP_HEADSET_MODE
+ },
+ [ALC292_FIXUP_TPT440_DOCK] = {
+ .type = HDA_FIXUP_PINS,
+ .v.pins = (const struct hda_pintbl[]) {
+ { 0x16, 0x21211010 }, /* dock headphone */
+ { 0x19, 0x21a11010 }, /* dock mic */
+ { }
+ },
+ .chained = true,
+ .chain_id = ALC269_FIXUP_LIMIT_INT_MIC_BOOST
+ },
};
-static struct snd_pci_quirk alc269_fixup_tbl[] = {
+static const struct snd_pci_quirk alc269_fixup_tbl[] = {
+ SND_PCI_QUIRK(0x1025, 0x0283, "Acer TravelMate 8371", ALC269_FIXUP_INV_DMIC),
+ SND_PCI_QUIRK(0x1025, 0x029b, "Acer 1810TZ", ALC269_FIXUP_INV_DMIC),
+ SND_PCI_QUIRK(0x1025, 0x0349, "Acer AOD260", ALC269_FIXUP_INV_DMIC),
+ SND_PCI_QUIRK(0x1025, 0x047c, "Acer AC700", ALC269_FIXUP_ACER_AC700),
+ SND_PCI_QUIRK(0x1025, 0x0740, "Acer AO725", ALC271_FIXUP_HP_GATE_MIC_JACK),
+ SND_PCI_QUIRK(0x1025, 0x0742, "Acer AO756", ALC271_FIXUP_HP_GATE_MIC_JACK),
+ SND_PCI_QUIRK_VENDOR(0x1025, "Acer Aspire", ALC271_FIXUP_DMIC),
+ SND_PCI_QUIRK(0x1025, 0x0775, "Acer Aspire E1-572", ALC271_FIXUP_HP_GATE_MIC_JACK_E1_572),
+ SND_PCI_QUIRK(0x1028, 0x0470, "Dell M101z", ALC269_FIXUP_DELL_M101Z),
+ SND_PCI_QUIRK(0x1028, 0x05bd, "Dell", ALC269_FIXUP_DELL2_MIC_NO_PRESENCE),
+ SND_PCI_QUIRK(0x1028, 0x05be, "Dell", ALC269_FIXUP_DELL2_MIC_NO_PRESENCE),
+ SND_PCI_QUIRK(0x1028, 0x05c4, "Dell", ALC269_FIXUP_DELL1_MIC_NO_PRESENCE),
+ SND_PCI_QUIRK(0x1028, 0x05c5, "Dell", ALC269_FIXUP_DELL1_MIC_NO_PRESENCE),
+ SND_PCI_QUIRK(0x1028, 0x05c6, "Dell", ALC269_FIXUP_DELL1_MIC_NO_PRESENCE),
+ SND_PCI_QUIRK(0x1028, 0x05c7, "Dell", ALC269_FIXUP_DELL1_MIC_NO_PRESENCE),
+ SND_PCI_QUIRK(0x1028, 0x05c8, "Dell", ALC269_FIXUP_DELL1_MIC_NO_PRESENCE),
+ SND_PCI_QUIRK(0x1028, 0x05c9, "Dell", ALC269_FIXUP_DELL1_MIC_NO_PRESENCE),
+ SND_PCI_QUIRK(0x1028, 0x05ca, "Dell", ALC269_FIXUP_DELL2_MIC_NO_PRESENCE),
+ SND_PCI_QUIRK(0x1028, 0x05cb, "Dell", ALC269_FIXUP_DELL2_MIC_NO_PRESENCE),
+ SND_PCI_QUIRK(0x1028, 0x05cc, "Dell X5 Precision", ALC269_FIXUP_DELL2_MIC_NO_PRESENCE),
+ SND_PCI_QUIRK(0x1028, 0x05cd, "Dell X5 Precision", ALC269_FIXUP_DELL2_MIC_NO_PRESENCE),
+ SND_PCI_QUIRK(0x1028, 0x05da, "Dell Vostro 5460", ALC290_FIXUP_SUBWOOFER),
+ SND_PCI_QUIRK(0x1028, 0x05de, "Dell", ALC269_FIXUP_DELL2_MIC_NO_PRESENCE),
+ SND_PCI_QUIRK(0x1028, 0x05e0, "Dell", ALC269_FIXUP_DELL2_MIC_NO_PRESENCE),
+ SND_PCI_QUIRK(0x1028, 0x05e9, "Dell", ALC269_FIXUP_DELL1_MIC_NO_PRESENCE),
+ SND_PCI_QUIRK(0x1028, 0x05ea, "Dell", ALC269_FIXUP_DELL1_MIC_NO_PRESENCE),
+ SND_PCI_QUIRK(0x1028, 0x05eb, "Dell", ALC269_FIXUP_DELL1_MIC_NO_PRESENCE),
+ SND_PCI_QUIRK(0x1028, 0x05ec, "Dell", ALC269_FIXUP_DELL1_MIC_NO_PRESENCE),
+ SND_PCI_QUIRK(0x1028, 0x05ed, "Dell", ALC269_FIXUP_DELL1_MIC_NO_PRESENCE),
+ SND_PCI_QUIRK(0x1028, 0x05ee, "Dell", ALC269_FIXUP_DELL1_MIC_NO_PRESENCE),
+ SND_PCI_QUIRK(0x1028, 0x05f3, "Dell", ALC269_FIXUP_DELL1_MIC_NO_PRESENCE),
+ SND_PCI_QUIRK(0x1028, 0x05f4, "Dell", ALC269_FIXUP_DELL1_MIC_NO_PRESENCE),
+ SND_PCI_QUIRK(0x1028, 0x05f5, "Dell", ALC269_FIXUP_DELL1_MIC_NO_PRESENCE),
+ SND_PCI_QUIRK(0x1028, 0x05f6, "Dell", ALC269_FIXUP_DELL1_MIC_NO_PRESENCE),
+ SND_PCI_QUIRK(0x1028, 0x05f8, "Dell", ALC269_FIXUP_DELL1_MIC_NO_PRESENCE),
+ SND_PCI_QUIRK(0x1028, 0x05f9, "Dell", ALC269_FIXUP_DELL1_MIC_NO_PRESENCE),
+ SND_PCI_QUIRK(0x1028, 0x05fb, "Dell", ALC269_FIXUP_DELL1_MIC_NO_PRESENCE),
+ SND_PCI_QUIRK(0x1028, 0x0606, "Dell", ALC269_FIXUP_DELL1_MIC_NO_PRESENCE),
+ SND_PCI_QUIRK(0x1028, 0x0608, "Dell", ALC269_FIXUP_DELL1_MIC_NO_PRESENCE),
+ SND_PCI_QUIRK(0x1028, 0x0609, "Dell", ALC269_FIXUP_DELL1_MIC_NO_PRESENCE),
+ SND_PCI_QUIRK(0x1028, 0x0613, "Dell", ALC269_FIXUP_DELL1_MIC_NO_PRESENCE),
+ SND_PCI_QUIRK(0x1028, 0x0614, "Dell Inspiron 3135", ALC269_FIXUP_DELL1_MIC_NO_PRESENCE),
+ SND_PCI_QUIRK(0x1028, 0x0615, "Dell Vostro 5470", ALC290_FIXUP_SUBWOOFER_HSJACK),
+ SND_PCI_QUIRK(0x1028, 0x0616, "Dell Vostro 5470", ALC290_FIXUP_SUBWOOFER_HSJACK),
+ SND_PCI_QUIRK(0x1028, 0x0638, "Dell Inspiron 5439", ALC290_FIXUP_MONO_SPEAKERS_HSJACK),
+ SND_PCI_QUIRK(0x1028, 0x063f, "Dell", ALC255_FIXUP_DELL1_MIC_NO_PRESENCE),
+ SND_PCI_QUIRK(0x1028, 0x064a, "Dell", ALC293_FIXUP_DELL1_MIC_NO_PRESENCE),
+ SND_PCI_QUIRK(0x1028, 0x064b, "Dell", ALC293_FIXUP_DELL1_MIC_NO_PRESENCE),
+ SND_PCI_QUIRK(0x1028, 0x0668, "Dell", ALC255_FIXUP_DELL2_MIC_NO_PRESENCE),
+ SND_PCI_QUIRK(0x1028, 0x0669, "Dell", ALC255_FIXUP_DELL2_MIC_NO_PRESENCE),
+ SND_PCI_QUIRK(0x1028, 0x0684, "Dell", ALC269_FIXUP_DELL2_MIC_NO_PRESENCE),
+ SND_PCI_QUIRK(0x1028, 0x15cc, "Dell X5 Precision", ALC269_FIXUP_DELL2_MIC_NO_PRESENCE),
+ SND_PCI_QUIRK(0x1028, 0x15cd, "Dell X5 Precision", ALC269_FIXUP_DELL2_MIC_NO_PRESENCE),
+ SND_PCI_QUIRK(0x1028, 0x164a, "Dell", ALC293_FIXUP_DELL1_MIC_NO_PRESENCE),
+ SND_PCI_QUIRK(0x1028, 0x164b, "Dell", ALC293_FIXUP_DELL1_MIC_NO_PRESENCE),
+ SND_PCI_QUIRK(0x103c, 0x1586, "HP", ALC269_FIXUP_HP_MUTE_LED_MIC2),
+ SND_PCI_QUIRK(0x103c, 0x18e6, "HP", ALC269_FIXUP_HP_GPIO_LED),
+ SND_PCI_QUIRK(0x103c, 0x1973, "HP Pavilion", ALC269_FIXUP_HP_MUTE_LED_MIC1),
+ SND_PCI_QUIRK(0x103c, 0x1983, "HP Pavilion", ALC269_FIXUP_HP_MUTE_LED_MIC1),
+ SND_PCI_QUIRK(0x103c, 0x218b, "HP", ALC269_FIXUP_LIMIT_INT_MIC_BOOST_MUTE_LED),
+ /* ALC282 */
+ SND_PCI_QUIRK(0x103c, 0x220d, "HP", ALC269_FIXUP_HP_MUTE_LED_MIC1),
+ SND_PCI_QUIRK(0x103c, 0x220e, "HP", ALC269_FIXUP_HP_MUTE_LED_MIC1),
+ SND_PCI_QUIRK(0x103c, 0x220f, "HP", ALC269_FIXUP_HP_MUTE_LED_MIC1),
+ SND_PCI_QUIRK(0x103c, 0x2210, "HP", ALC269_FIXUP_HP_MUTE_LED_MIC1),
+ SND_PCI_QUIRK(0x103c, 0x2211, "HP", ALC269_FIXUP_HP_MUTE_LED_MIC1),
+ SND_PCI_QUIRK(0x103c, 0x2212, "HP", ALC269_FIXUP_HP_MUTE_LED_MIC1),
+ SND_PCI_QUIRK(0x103c, 0x2213, "HP", ALC269_FIXUP_HP_MUTE_LED_MIC1),
+ SND_PCI_QUIRK(0x103c, 0x2214, "HP", ALC269_FIXUP_HP_MUTE_LED_MIC1),
+ SND_PCI_QUIRK(0x103c, 0x2266, "HP", ALC269_FIXUP_HP_MUTE_LED_MIC1),
+ SND_PCI_QUIRK(0x103c, 0x2267, "HP", ALC269_FIXUP_HP_MUTE_LED_MIC1),
+ SND_PCI_QUIRK(0x103c, 0x2268, "HP", ALC269_FIXUP_HP_MUTE_LED_MIC1),
+ SND_PCI_QUIRK(0x103c, 0x2269, "HP", ALC269_FIXUP_HP_MUTE_LED_MIC1),
+ SND_PCI_QUIRK(0x103c, 0x226a, "HP", ALC269_FIXUP_HP_MUTE_LED_MIC1),
+ SND_PCI_QUIRK(0x103c, 0x226b, "HP", ALC269_FIXUP_HP_MUTE_LED_MIC1),
+ SND_PCI_QUIRK(0x103c, 0x226c, "HP", ALC269_FIXUP_HP_MUTE_LED_MIC1),
+ SND_PCI_QUIRK(0x103c, 0x226d, "HP", ALC269_FIXUP_HP_MUTE_LED_MIC1),
+ SND_PCI_QUIRK(0x103c, 0x226e, "HP", ALC269_FIXUP_HP_MUTE_LED_MIC1),
+ SND_PCI_QUIRK(0x103c, 0x226f, "HP", ALC269_FIXUP_HP_MUTE_LED_MIC1),
+ SND_PCI_QUIRK(0x103c, 0x227a, "HP", ALC269_FIXUP_HP_MUTE_LED_MIC1),
+ SND_PCI_QUIRK(0x103c, 0x227b, "HP", ALC269_FIXUP_HP_MUTE_LED_MIC1),
+ SND_PCI_QUIRK(0x103c, 0x229e, "HP", ALC269_FIXUP_HP_MUTE_LED_MIC1),
+ SND_PCI_QUIRK(0x103c, 0x22a0, "HP", ALC269_FIXUP_HP_MUTE_LED_MIC1),
+ SND_PCI_QUIRK(0x103c, 0x22b2, "HP", ALC269_FIXUP_HP_MUTE_LED_MIC1),
+ SND_PCI_QUIRK(0x103c, 0x22b7, "HP", ALC269_FIXUP_HP_MUTE_LED_MIC1),
+ SND_PCI_QUIRK(0x103c, 0x22bf, "HP", ALC269_FIXUP_HP_MUTE_LED_MIC1),
+ SND_PCI_QUIRK(0x103c, 0x22c0, "HP", ALC269_FIXUP_HP_MUTE_LED_MIC1),
+ SND_PCI_QUIRK(0x103c, 0x22c1, "HP", ALC269_FIXUP_HP_MUTE_LED_MIC1),
+ SND_PCI_QUIRK(0x103c, 0x22c2, "HP", ALC269_FIXUP_HP_MUTE_LED_MIC1),
+ SND_PCI_QUIRK(0x103c, 0x22cd, "HP", ALC269_FIXUP_HP_MUTE_LED_MIC1),
+ SND_PCI_QUIRK(0x103c, 0x22ce, "HP", ALC269_FIXUP_HP_MUTE_LED_MIC1),
+ SND_PCI_QUIRK(0x103c, 0x22cf, "HP", ALC269_FIXUP_HP_MUTE_LED_MIC1),
+ SND_PCI_QUIRK(0x103c, 0x22d0, "HP", ALC269_FIXUP_HP_MUTE_LED_MIC1),
+ /* ALC290 */
+ SND_PCI_QUIRK(0x103c, 0x2260, "HP", ALC269_FIXUP_HP_MUTE_LED_MIC1),
+ SND_PCI_QUIRK(0x103c, 0x2261, "HP", ALC269_FIXUP_HP_MUTE_LED_MIC1),
+ SND_PCI_QUIRK(0x103c, 0x2262, "HP", ALC269_FIXUP_HP_MUTE_LED_MIC1),
+ SND_PCI_QUIRK(0x103c, 0x2263, "HP", ALC269_FIXUP_HP_MUTE_LED_MIC1),
+ SND_PCI_QUIRK(0x103c, 0x2264, "HP", ALC269_FIXUP_HP_MUTE_LED_MIC1),
+ SND_PCI_QUIRK(0x103c, 0x2265, "HP", ALC269_FIXUP_HP_MUTE_LED_MIC1),
+ SND_PCI_QUIRK(0x103c, 0x227d, "HP", ALC269_FIXUP_HP_MUTE_LED_MIC1),
+ SND_PCI_QUIRK(0x103c, 0x227e, "HP", ALC269_FIXUP_HP_MUTE_LED_MIC1),
+ SND_PCI_QUIRK(0x103c, 0x227f, "HP", ALC269_FIXUP_HP_MUTE_LED_MIC1),
+ SND_PCI_QUIRK(0x103c, 0x2280, "HP", ALC269_FIXUP_HP_MUTE_LED_MIC1),
+ SND_PCI_QUIRK(0x103c, 0x2281, "HP", ALC269_FIXUP_HP_MUTE_LED_MIC1),
+ SND_PCI_QUIRK(0x103c, 0x2282, "HP", ALC269_FIXUP_HP_MUTE_LED_MIC1),
+ SND_PCI_QUIRK(0x103c, 0x2289, "HP", ALC269_FIXUP_HP_MUTE_LED_MIC1),
+ SND_PCI_QUIRK(0x103c, 0x228a, "HP", ALC269_FIXUP_HP_MUTE_LED_MIC1),
+ SND_PCI_QUIRK(0x103c, 0x228b, "HP", ALC269_FIXUP_HP_MUTE_LED_MIC1),
+ SND_PCI_QUIRK(0x103c, 0x228c, "HP", ALC269_FIXUP_HP_MUTE_LED_MIC1),
+ SND_PCI_QUIRK(0x103c, 0x228d, "HP", ALC269_FIXUP_HP_MUTE_LED_MIC1),
+ SND_PCI_QUIRK(0x103c, 0x228e, "HP", ALC269_FIXUP_HP_MUTE_LED_MIC1),
+ SND_PCI_QUIRK(0x103c, 0x22c5, "HP", ALC269_FIXUP_HP_MUTE_LED_MIC1),
+ SND_PCI_QUIRK(0x103c, 0x22c6, "HP", ALC269_FIXUP_HP_MUTE_LED_MIC1),
+ SND_PCI_QUIRK(0x103c, 0x22c7, "HP", ALC269_FIXUP_HP_MUTE_LED_MIC1),
+ SND_PCI_QUIRK(0x103c, 0x22c8, "HP", ALC269_FIXUP_HP_MUTE_LED_MIC1),
+ SND_PCI_QUIRK(0x103c, 0x22c3, "HP", ALC269_FIXUP_HP_MUTE_LED_MIC1),
+ SND_PCI_QUIRK(0x103c, 0x22c4, "HP", ALC269_FIXUP_HP_MUTE_LED_MIC1),
+ SND_PCI_QUIRK(0x103c, 0x2334, "HP", ALC269_FIXUP_HP_MUTE_LED_MIC1),
+ SND_PCI_QUIRK(0x103c, 0x2335, "HP", ALC269_FIXUP_HP_MUTE_LED_MIC1),
+ SND_PCI_QUIRK(0x103c, 0x2336, "HP", ALC269_FIXUP_HP_MUTE_LED_MIC1),
+ SND_PCI_QUIRK(0x103c, 0x2337, "HP", ALC269_FIXUP_HP_MUTE_LED_MIC1),
+ SND_PCI_QUIRK_VENDOR(0x103c, "HP", ALC269_FIXUP_HP_MUTE_LED),
+ SND_PCI_QUIRK(0x1043, 0x103f, "ASUS TX300", ALC282_FIXUP_ASUS_TX300),
+ SND_PCI_QUIRK(0x1043, 0x106d, "Asus K53BE", ALC269_FIXUP_LIMIT_INT_MIC_BOOST),
+ SND_PCI_QUIRK(0x1043, 0x115d, "Asus 1015E", ALC269_FIXUP_LIMIT_INT_MIC_BOOST),
+ SND_PCI_QUIRK(0x1043, 0x1427, "Asus Zenbook UX31E", ALC269VB_FIXUP_ASUS_ZENBOOK),
+ SND_PCI_QUIRK(0x1043, 0x1517, "Asus Zenbook UX31A", ALC269VB_FIXUP_ASUS_ZENBOOK_UX31A),
+ SND_PCI_QUIRK(0x1043, 0x16e3, "ASUS UX50", ALC269_FIXUP_STEREO_DMIC),
+ SND_PCI_QUIRK(0x1043, 0x1a13, "Asus G73Jw", ALC269_FIXUP_ASUS_G73JW),
+ SND_PCI_QUIRK(0x1043, 0x1b13, "Asus U41SV", ALC269_FIXUP_INV_DMIC),
+ SND_PCI_QUIRK(0x1043, 0x1c23, "Asus X55U", ALC269_FIXUP_LIMIT_INT_MIC_BOOST),
+ SND_PCI_QUIRK(0x1043, 0x831a, "ASUS P901", ALC269_FIXUP_STEREO_DMIC),
+ SND_PCI_QUIRK(0x1043, 0x834a, "ASUS S101", ALC269_FIXUP_STEREO_DMIC),
+ SND_PCI_QUIRK(0x1043, 0x8398, "ASUS P1005", ALC269_FIXUP_STEREO_DMIC),
+ SND_PCI_QUIRK(0x1043, 0x83ce, "ASUS P1005", ALC269_FIXUP_STEREO_DMIC),
+ SND_PCI_QUIRK(0x1043, 0x8516, "ASUS X101CH", ALC269_FIXUP_ASUS_X101),
+ SND_PCI_QUIRK(0x104d, 0x90b5, "Sony VAIO Pro 11", ALC286_FIXUP_SONY_MIC_NO_PRESENCE),
+ SND_PCI_QUIRK(0x104d, 0x90b6, "Sony VAIO Pro 13", ALC286_FIXUP_SONY_MIC_NO_PRESENCE),
SND_PCI_QUIRK(0x104d, 0x9073, "Sony VAIO", ALC275_FIXUP_SONY_VAIO_GPIO2),
SND_PCI_QUIRK(0x104d, 0x907b, "Sony VAIO", ALC275_FIXUP_SONY_HWEQ),
SND_PCI_QUIRK(0x104d, 0x9084, "Sony VAIO", ALC275_FIXUP_SONY_HWEQ),
+ SND_PCI_QUIRK(0x104d, 0x9099, "Sony VAIO S13", ALC275_FIXUP_SONY_DISABLE_AAMIX),
SND_PCI_QUIRK_VENDOR(0x104d, "Sony VAIO", ALC269_FIXUP_SONY_VAIO),
- SND_PCI_QUIRK(0x1028, 0x0470, "Dell M101z", ALC269_FIXUP_DELL_M101Z),
+ SND_PCI_QUIRK(0x10cf, 0x1475, "Lifebook", ALC269_FIXUP_LIFEBOOK),
+ SND_PCI_QUIRK(0x10cf, 0x1845, "Lifebook U904", ALC269_FIXUP_LIFEBOOK_EXTMIC),
SND_PCI_QUIRK(0x17aa, 0x20f2, "Thinkpad SL410/510", ALC269_FIXUP_SKU_IGNORE),
SND_PCI_QUIRK(0x17aa, 0x215e, "Thinkpad L512", ALC269_FIXUP_SKU_IGNORE),
SND_PCI_QUIRK(0x17aa, 0x21b8, "Thinkpad Edge 14", ALC269_FIXUP_SKU_IGNORE),
SND_PCI_QUIRK(0x17aa, 0x21ca, "Thinkpad L412", ALC269_FIXUP_SKU_IGNORE),
SND_PCI_QUIRK(0x17aa, 0x21e9, "Thinkpad Edge 15", ALC269_FIXUP_SKU_IGNORE),
- SND_PCI_QUIRK(0x1043, 0x1a13, "Asus G73Jw", ALC269_FIXUP_ASUS_G73JW),
+ SND_PCI_QUIRK(0x17aa, 0x21f6, "Thinkpad T530", ALC269_FIXUP_LENOVO_DOCK),
+ SND_PCI_QUIRK(0x17aa, 0x21fa, "Thinkpad X230", ALC269_FIXUP_LENOVO_DOCK),
+ SND_PCI_QUIRK(0x17aa, 0x21f3, "Thinkpad T430", ALC269_FIXUP_LENOVO_DOCK),
+ SND_PCI_QUIRK(0x17aa, 0x21fb, "Thinkpad T430s", ALC269_FIXUP_LENOVO_DOCK),
+ SND_PCI_QUIRK(0x17aa, 0x2203, "Thinkpad X230 Tablet", ALC269_FIXUP_LENOVO_DOCK),
+ SND_PCI_QUIRK(0x17aa, 0x2208, "Thinkpad T431s", ALC269_FIXUP_LENOVO_DOCK),
+ SND_PCI_QUIRK(0x17aa, 0x220c, "Thinkpad T440s", ALC292_FIXUP_TPT440_DOCK),
+ SND_PCI_QUIRK(0x17aa, 0x220e, "Thinkpad T440p", ALC292_FIXUP_TPT440_DOCK),
+ SND_PCI_QUIRK(0x17aa, 0x2210, "Thinkpad T540p", ALC292_FIXUP_TPT440_DOCK),
+ SND_PCI_QUIRK(0x17aa, 0x2212, "Thinkpad", ALC269_FIXUP_LIMIT_INT_MIC_BOOST),
+ SND_PCI_QUIRK(0x17aa, 0x2214, "Thinkpad", ALC269_FIXUP_LIMIT_INT_MIC_BOOST),
+ SND_PCI_QUIRK(0x17aa, 0x2215, "Thinkpad", ALC269_FIXUP_LIMIT_INT_MIC_BOOST),
+ SND_PCI_QUIRK(0x17aa, 0x3978, "IdeaPad Y410P", ALC269_FIXUP_NO_SHUTUP),
+ SND_PCI_QUIRK(0x17aa, 0x5013, "Thinkpad", ALC269_FIXUP_LIMIT_INT_MIC_BOOST),
+ SND_PCI_QUIRK(0x17aa, 0x501a, "Thinkpad", ALC283_FIXUP_INT_MIC),
+ SND_PCI_QUIRK(0x17aa, 0x5026, "Thinkpad", ALC269_FIXUP_LIMIT_INT_MIC_BOOST),
+ SND_PCI_QUIRK(0x17aa, 0x5109, "Thinkpad", ALC269_FIXUP_LIMIT_INT_MIC_BOOST),
+ SND_PCI_QUIRK(0x17aa, 0x3bf8, "Quanta FL1", ALC269_FIXUP_PCM_44K),
SND_PCI_QUIRK(0x17aa, 0x9e54, "LENOVO NB", ALC269_FIXUP_LENOVO_EAPD),
- {}
-};
-
+ SND_PCI_QUIRK_VENDOR(0x17aa, "Thinkpad", ALC269_FIXUP_THINKPAD_ACPI),
+ SND_PCI_QUIRK(0x1b7d, 0xa831, "Ordissimo EVE2 ", ALC269VB_FIXUP_ORDISSIMO_EVE2), /* Also known as Malata PC-B1303 */
-/*
- * configuration and preset
- */
-static const char * const alc269_models[ALC269_MODEL_LAST] = {
- [ALC269_BASIC] = "basic",
- [ALC269_QUANTA_FL1] = "quanta",
- [ALC269_AMIC] = "laptop-amic",
- [ALC269_DMIC] = "laptop-dmic",
- [ALC269_FUJITSU] = "fujitsu",
- [ALC269_LIFEBOOK] = "lifebook",
- [ALC269_AUTO] = "auto",
+#if 0
+ /* Below is a quirk table taken from the old code.
+ * Basically the device should work as is without the fixup table.
+ * If BIOS doesn't give a proper info, enable the corresponding
+ * fixup entry.
+ */
+ SND_PCI_QUIRK(0x1043, 0x8330, "ASUS Eeepc P703 P900A",
+ ALC269_FIXUP_AMIC),
+ SND_PCI_QUIRK(0x1043, 0x1013, "ASUS N61Da", ALC269_FIXUP_AMIC),
+ SND_PCI_QUIRK(0x1043, 0x1143, "ASUS B53f", ALC269_FIXUP_AMIC),
+ SND_PCI_QUIRK(0x1043, 0x1133, "ASUS UJ20ft", ALC269_FIXUP_AMIC),
+ SND_PCI_QUIRK(0x1043, 0x1183, "ASUS K72DR", ALC269_FIXUP_AMIC),
+ SND_PCI_QUIRK(0x1043, 0x11b3, "ASUS K52DR", ALC269_FIXUP_AMIC),
+ SND_PCI_QUIRK(0x1043, 0x11e3, "ASUS U33Jc", ALC269_FIXUP_AMIC),
+ SND_PCI_QUIRK(0x1043, 0x1273, "ASUS UL80Jt", ALC269_FIXUP_AMIC),
+ SND_PCI_QUIRK(0x1043, 0x1283, "ASUS U53Jc", ALC269_FIXUP_AMIC),
+ SND_PCI_QUIRK(0x1043, 0x12b3, "ASUS N82JV", ALC269_FIXUP_AMIC),
+ SND_PCI_QUIRK(0x1043, 0x12d3, "ASUS N61Jv", ALC269_FIXUP_AMIC),
+ SND_PCI_QUIRK(0x1043, 0x13a3, "ASUS UL30Vt", ALC269_FIXUP_AMIC),
+ SND_PCI_QUIRK(0x1043, 0x1373, "ASUS G73JX", ALC269_FIXUP_AMIC),
+ SND_PCI_QUIRK(0x1043, 0x1383, "ASUS UJ30Jc", ALC269_FIXUP_AMIC),
+ SND_PCI_QUIRK(0x1043, 0x13d3, "ASUS N61JA", ALC269_FIXUP_AMIC),
+ SND_PCI_QUIRK(0x1043, 0x1413, "ASUS UL50", ALC269_FIXUP_AMIC),
+ SND_PCI_QUIRK(0x1043, 0x1443, "ASUS UL30", ALC269_FIXUP_AMIC),
+ SND_PCI_QUIRK(0x1043, 0x1453, "ASUS M60Jv", ALC269_FIXUP_AMIC),
+ SND_PCI_QUIRK(0x1043, 0x1483, "ASUS UL80", ALC269_FIXUP_AMIC),
+ SND_PCI_QUIRK(0x1043, 0x14f3, "ASUS F83Vf", ALC269_FIXUP_AMIC),
+ SND_PCI_QUIRK(0x1043, 0x14e3, "ASUS UL20", ALC269_FIXUP_AMIC),
+ SND_PCI_QUIRK(0x1043, 0x1513, "ASUS UX30", ALC269_FIXUP_AMIC),
+ SND_PCI_QUIRK(0x1043, 0x1593, "ASUS N51Vn", ALC269_FIXUP_AMIC),
+ SND_PCI_QUIRK(0x1043, 0x15a3, "ASUS N60Jv", ALC269_FIXUP_AMIC),
+ SND_PCI_QUIRK(0x1043, 0x15b3, "ASUS N60Dp", ALC269_FIXUP_AMIC),
+ SND_PCI_QUIRK(0x1043, 0x15c3, "ASUS N70De", ALC269_FIXUP_AMIC),
+ SND_PCI_QUIRK(0x1043, 0x15e3, "ASUS F83T", ALC269_FIXUP_AMIC),
+ SND_PCI_QUIRK(0x1043, 0x1643, "ASUS M60J", ALC269_FIXUP_AMIC),
+ SND_PCI_QUIRK(0x1043, 0x1653, "ASUS U50", ALC269_FIXUP_AMIC),
+ SND_PCI_QUIRK(0x1043, 0x1693, "ASUS F50N", ALC269_FIXUP_AMIC),
+ SND_PCI_QUIRK(0x1043, 0x16a3, "ASUS F5Q", ALC269_FIXUP_AMIC),
+ SND_PCI_QUIRK(0x1043, 0x1723, "ASUS P80", ALC269_FIXUP_AMIC),
+ SND_PCI_QUIRK(0x1043, 0x1743, "ASUS U80", ALC269_FIXUP_AMIC),
+ SND_PCI_QUIRK(0x1043, 0x1773, "ASUS U20A", ALC269_FIXUP_AMIC),
+ SND_PCI_QUIRK(0x1043, 0x1883, "ASUS F81Se", ALC269_FIXUP_AMIC),
+ SND_PCI_QUIRK(0x152d, 0x1778, "Quanta ON1", ALC269_FIXUP_DMIC),
+ SND_PCI_QUIRK(0x17aa, 0x3be9, "Quanta Wistron", ALC269_FIXUP_AMIC),
+ SND_PCI_QUIRK(0x17aa, 0x3bf8, "Quanta FL1", ALC269_FIXUP_AMIC),
+ SND_PCI_QUIRK(0x17ff, 0x059a, "Quanta EL3", ALC269_FIXUP_DMIC),
+ SND_PCI_QUIRK(0x17ff, 0x059b, "Quanta JR1", ALC269_FIXUP_DMIC),
+#endif
+ {}
};
-static struct snd_pci_quirk alc269_cfg_tbl[] = {
- SND_PCI_QUIRK(0x17aa, 0x3bf8, "Quanta FL1", ALC269_QUANTA_FL1),
- SND_PCI_QUIRK(0x1025, 0x047c, "ACER ZGA", ALC271_ACER),
- SND_PCI_QUIRK(0x1043, 0x8330, "ASUS Eeepc P703 P900A",
- ALC269_AMIC),
- SND_PCI_QUIRK(0x1043, 0x1013, "ASUS N61Da", ALC269VB_AMIC),
- SND_PCI_QUIRK(0x1043, 0x1113, "ASUS N63Jn", ALC269VB_AMIC),
- SND_PCI_QUIRK(0x1043, 0x1143, "ASUS B53f", ALC269VB_AMIC),
- SND_PCI_QUIRK(0x1043, 0x1133, "ASUS UJ20ft", ALC269_AMIC),
- SND_PCI_QUIRK(0x1043, 0x1183, "ASUS K72DR", ALC269VB_AMIC),
- SND_PCI_QUIRK(0x1043, 0x11b3, "ASUS K52DR", ALC269VB_AMIC),
- SND_PCI_QUIRK(0x1043, 0x11e3, "ASUS U33Jc", ALC269VB_AMIC),
- SND_PCI_QUIRK(0x1043, 0x1273, "ASUS UL80Jt", ALC269VB_AMIC),
- SND_PCI_QUIRK(0x1043, 0x1283, "ASUS U53Jc", ALC269_AMIC),
- SND_PCI_QUIRK(0x1043, 0x12b3, "ASUS N82JV", ALC269VB_AMIC),
- SND_PCI_QUIRK(0x1043, 0x12d3, "ASUS N61Jv", ALC269_AMIC),
- SND_PCI_QUIRK(0x1043, 0x13a3, "ASUS UL30Vt", ALC269_AMIC),
- SND_PCI_QUIRK(0x1043, 0x1373, "ASUS G73JX", ALC269_AMIC),
- SND_PCI_QUIRK(0x1043, 0x1383, "ASUS UJ30Jc", ALC269_AMIC),
- SND_PCI_QUIRK(0x1043, 0x13d3, "ASUS N61JA", ALC269_AMIC),
- SND_PCI_QUIRK(0x1043, 0x1413, "ASUS UL50", ALC269_AMIC),
- SND_PCI_QUIRK(0x1043, 0x1443, "ASUS UL30", ALC269_AMIC),
- SND_PCI_QUIRK(0x1043, 0x1453, "ASUS M60Jv", ALC269_AMIC),
- SND_PCI_QUIRK(0x1043, 0x1483, "ASUS UL80", ALC269_AMIC),
- SND_PCI_QUIRK(0x1043, 0x14f3, "ASUS F83Vf", ALC269_AMIC),
- SND_PCI_QUIRK(0x1043, 0x14e3, "ASUS UL20", ALC269_AMIC),
- SND_PCI_QUIRK(0x1043, 0x1513, "ASUS UX30", ALC269_AMIC),
- SND_PCI_QUIRK(0x1043, 0x1593, "ASUS N51Vn", ALC269_AMIC),
- SND_PCI_QUIRK(0x1043, 0x15a3, "ASUS N60Jv", ALC269_AMIC),
- SND_PCI_QUIRK(0x1043, 0x15b3, "ASUS N60Dp", ALC269_AMIC),
- SND_PCI_QUIRK(0x1043, 0x15c3, "ASUS N70De", ALC269_AMIC),
- SND_PCI_QUIRK(0x1043, 0x15e3, "ASUS F83T", ALC269_AMIC),
- SND_PCI_QUIRK(0x1043, 0x1643, "ASUS M60J", ALC269_AMIC),
- SND_PCI_QUIRK(0x1043, 0x1653, "ASUS U50", ALC269_AMIC),
- SND_PCI_QUIRK(0x1043, 0x1693, "ASUS F50N", ALC269_AMIC),
- SND_PCI_QUIRK(0x1043, 0x16a3, "ASUS F5Q", ALC269_AMIC),
- SND_PCI_QUIRK(0x1043, 0x16e3, "ASUS UX50", ALC269_DMIC),
- SND_PCI_QUIRK(0x1043, 0x1723, "ASUS P80", ALC269_AMIC),
- SND_PCI_QUIRK(0x1043, 0x1743, "ASUS U80", ALC269_AMIC),
- SND_PCI_QUIRK(0x1043, 0x1773, "ASUS U20A", ALC269_AMIC),
- SND_PCI_QUIRK(0x1043, 0x1883, "ASUS F81Se", ALC269_AMIC),
- SND_PCI_QUIRK(0x1043, 0x831a, "ASUS Eeepc P901",
- ALC269_DMIC),
- SND_PCI_QUIRK(0x1043, 0x834a, "ASUS Eeepc S101",
- ALC269_DMIC),
- SND_PCI_QUIRK(0x1043, 0x8398, "ASUS P1005HA", ALC269_DMIC),
- SND_PCI_QUIRK(0x1043, 0x83ce, "ASUS P1005HA", ALC269_DMIC),
- SND_PCI_QUIRK(0x104d, 0x9071, "Sony VAIO", ALC269_AUTO),
- SND_PCI_QUIRK(0x10cf, 0x1475, "Lifebook ICH9M-based", ALC269_LIFEBOOK),
- SND_PCI_QUIRK(0x152d, 0x1778, "Quanta ON1", ALC269_DMIC),
- SND_PCI_QUIRK(0x1734, 0x115d, "FSC Amilo", ALC269_FUJITSU),
- SND_PCI_QUIRK(0x17aa, 0x3be9, "Quanta Wistron", ALC269_AMIC),
- SND_PCI_QUIRK(0x17aa, 0x3bf8, "Quanta FL1", ALC269_AMIC),
- SND_PCI_QUIRK(0x17ff, 0x059a, "Quanta EL3", ALC269_DMIC),
- SND_PCI_QUIRK(0x17ff, 0x059b, "Quanta JR1", ALC269_DMIC),
+static const struct hda_model_fixup alc269_fixup_models[] = {
+ {.id = ALC269_FIXUP_AMIC, .name = "laptop-amic"},
+ {.id = ALC269_FIXUP_DMIC, .name = "laptop-dmic"},
+ {.id = ALC269_FIXUP_STEREO_DMIC, .name = "alc269-dmic"},
+ {.id = ALC271_FIXUP_DMIC, .name = "alc271-dmic"},
+ {.id = ALC269_FIXUP_INV_DMIC, .name = "inv-dmic"},
+ {.id = ALC269_FIXUP_HEADSET_MIC, .name = "headset-mic"},
+ {.id = ALC269_FIXUP_LENOVO_DOCK, .name = "lenovo-dock"},
+ {.id = ALC269_FIXUP_HP_GPIO_LED, .name = "hp-gpio-led"},
+ {.id = ALC269_FIXUP_DELL1_MIC_NO_PRESENCE, .name = "dell-headset-multi"},
+ {.id = ALC269_FIXUP_DELL2_MIC_NO_PRESENCE, .name = "dell-headset-dock"},
+ {.id = ALC283_FIXUP_CHROME_BOOK, .name = "alc283-dac-wcaps"},
+ {.id = ALC283_FIXUP_SENSE_COMBO_JACK, .name = "alc283-sense-combo"},
+ {.id = ALC292_FIXUP_TPT440_DOCK, .name = "tpt440-dock"},
{}
};
-static struct alc_config_preset alc269_presets[] = {
- [ALC269_BASIC] = {
- .mixers = { alc269_base_mixer },
- .init_verbs = { alc269_init_verbs },
- .num_dacs = ARRAY_SIZE(alc269_dac_nids),
- .dac_nids = alc269_dac_nids,
- .hp_nid = 0x03,
- .num_channel_mode = ARRAY_SIZE(alc269_modes),
- .channel_mode = alc269_modes,
- .input_mux = &alc269_capture_source,
- },
- [ALC269_QUANTA_FL1] = {
- .mixers = { alc269_quanta_fl1_mixer },
- .init_verbs = { alc269_init_verbs, alc269_quanta_fl1_verbs },
- .num_dacs = ARRAY_SIZE(alc269_dac_nids),
- .dac_nids = alc269_dac_nids,
- .hp_nid = 0x03,
- .num_channel_mode = ARRAY_SIZE(alc269_modes),
- .channel_mode = alc269_modes,
- .input_mux = &alc269_capture_source,
- .unsol_event = alc269_quanta_fl1_unsol_event,
- .setup = alc269_quanta_fl1_setup,
- .init_hook = alc269_quanta_fl1_init_hook,
- },
- [ALC269_AMIC] = {
- .mixers = { alc269_laptop_mixer },
- .cap_mixer = alc269_laptop_analog_capture_mixer,
- .init_verbs = { alc269_init_verbs,
- alc269_laptop_amic_init_verbs },
- .num_dacs = ARRAY_SIZE(alc269_dac_nids),
- .dac_nids = alc269_dac_nids,
- .hp_nid = 0x03,
- .num_channel_mode = ARRAY_SIZE(alc269_modes),
- .channel_mode = alc269_modes,
- .unsol_event = alc269_laptop_unsol_event,
- .setup = alc269_laptop_amic_setup,
- .init_hook = alc269_laptop_inithook,
- },
- [ALC269_DMIC] = {
- .mixers = { alc269_laptop_mixer },
- .cap_mixer = alc269_laptop_digital_capture_mixer,
- .init_verbs = { alc269_init_verbs,
- alc269_laptop_dmic_init_verbs },
- .num_dacs = ARRAY_SIZE(alc269_dac_nids),
- .dac_nids = alc269_dac_nids,
- .hp_nid = 0x03,
- .num_channel_mode = ARRAY_SIZE(alc269_modes),
- .channel_mode = alc269_modes,
- .unsol_event = alc269_laptop_unsol_event,
- .setup = alc269_laptop_dmic_setup,
- .init_hook = alc269_laptop_inithook,
- },
- [ALC269VB_AMIC] = {
- .mixers = { alc269vb_laptop_mixer },
- .cap_mixer = alc269vb_laptop_analog_capture_mixer,
- .init_verbs = { alc269vb_init_verbs,
- alc269vb_laptop_amic_init_verbs },
- .num_dacs = ARRAY_SIZE(alc269_dac_nids),
- .dac_nids = alc269_dac_nids,
- .hp_nid = 0x03,
- .num_channel_mode = ARRAY_SIZE(alc269_modes),
- .channel_mode = alc269_modes,
- .unsol_event = alc269_laptop_unsol_event,
- .setup = alc269vb_laptop_amic_setup,
- .init_hook = alc269_laptop_inithook,
- },
- [ALC269VB_DMIC] = {
- .mixers = { alc269vb_laptop_mixer },
- .cap_mixer = alc269vb_laptop_digital_capture_mixer,
- .init_verbs = { alc269vb_init_verbs,
- alc269vb_laptop_dmic_init_verbs },
- .num_dacs = ARRAY_SIZE(alc269_dac_nids),
- .dac_nids = alc269_dac_nids,
- .hp_nid = 0x03,
- .num_channel_mode = ARRAY_SIZE(alc269_modes),
- .channel_mode = alc269_modes,
- .unsol_event = alc269_laptop_unsol_event,
- .setup = alc269vb_laptop_dmic_setup,
- .init_hook = alc269_laptop_inithook,
- },
- [ALC269_FUJITSU] = {
- .mixers = { alc269_fujitsu_mixer },
- .cap_mixer = alc269_laptop_digital_capture_mixer,
- .init_verbs = { alc269_init_verbs,
- alc269_laptop_dmic_init_verbs },
- .num_dacs = ARRAY_SIZE(alc269_dac_nids),
- .dac_nids = alc269_dac_nids,
- .hp_nid = 0x03,
- .num_channel_mode = ARRAY_SIZE(alc269_modes),
- .channel_mode = alc269_modes,
- .unsol_event = alc269_laptop_unsol_event,
- .setup = alc269_laptop_dmic_setup,
- .init_hook = alc269_laptop_inithook,
- },
- [ALC269_LIFEBOOK] = {
- .mixers = { alc269_lifebook_mixer },
- .init_verbs = { alc269_init_verbs, alc269_lifebook_verbs },
- .num_dacs = ARRAY_SIZE(alc269_dac_nids),
- .dac_nids = alc269_dac_nids,
- .hp_nid = 0x03,
- .num_channel_mode = ARRAY_SIZE(alc269_modes),
- .channel_mode = alc269_modes,
- .input_mux = &alc269_capture_source,
- .unsol_event = alc269_lifebook_unsol_event,
- .init_hook = alc269_lifebook_init_hook,
- },
- [ALC271_ACER] = {
- .mixers = { alc269_asus_mixer },
- .cap_mixer = alc269vb_laptop_digital_capture_mixer,
- .init_verbs = { alc269_init_verbs, alc271_acer_dmic_verbs },
- .num_dacs = ARRAY_SIZE(alc269_dac_nids),
- .dac_nids = alc269_dac_nids,
- .adc_nids = alc262_dmic_adc_nids,
- .num_adc_nids = ARRAY_SIZE(alc262_dmic_adc_nids),
- .capsrc_nids = alc262_dmic_capsrc_nids,
- .num_channel_mode = ARRAY_SIZE(alc269_modes),
- .channel_mode = alc269_modes,
- .input_mux = &alc269_capture_source,
- .dig_out_nid = ALC880_DIGOUT_NID,
- .unsol_event = alc_sku_unsol_event,
- .setup = alc269vb_laptop_dmic_setup,
- .init_hook = alc_inithook,
- },
+static const struct snd_hda_pin_quirk alc269_pin_fixup_tbl[] = {
+ SND_HDA_PIN_QUIRK(0x10ec0255, 0x1028, "Dell", ALC255_FIXUP_DELL1_MIC_NO_PRESENCE,
+ {0x12, 0x90a60140},
+ {0x14, 0x90170110},
+ {0x17, 0x40000000},
+ {0x18, 0x411111f0},
+ {0x19, 0x411111f0},
+ {0x1a, 0x411111f0},
+ {0x1b, 0x411111f0},
+ {0x1d, 0x40700001},
+ {0x1e, 0x411111f0},
+ {0x21, 0x02211020}),
+ SND_HDA_PIN_QUIRK(0x10ec0255, 0x1028, "Dell", ALC255_FIXUP_DELL1_MIC_NO_PRESENCE,
+ {0x12, 0x90a60160},
+ {0x14, 0x90170120},
+ {0x17, 0x40000000},
+ {0x18, 0x411111f0},
+ {0x19, 0x411111f0},
+ {0x1a, 0x411111f0},
+ {0x1b, 0x411111f0},
+ {0x1d, 0x40700001},
+ {0x1e, 0x411111f0},
+ {0x21, 0x02211030}),
+ SND_HDA_PIN_QUIRK(0x10ec0255, 0x1028, "Dell", ALC255_FIXUP_DELL1_MIC_NO_PRESENCE,
+ {0x12, 0x90a60160},
+ {0x14, 0x90170120},
+ {0x17, 0x90170140},
+ {0x18, 0x40000000},
+ {0x19, 0x411111f0},
+ {0x1a, 0x411111f0},
+ {0x1b, 0x411111f0},
+ {0x1d, 0x41163b05},
+ {0x1e, 0x411111f0},
+ {0x21, 0x0321102f}),
+ SND_HDA_PIN_QUIRK(0x10ec0255, 0x1028, "Dell", ALC255_FIXUP_DELL1_MIC_NO_PRESENCE,
+ {0x12, 0x90a60160},
+ {0x14, 0x90170130},
+ {0x17, 0x40000000},
+ {0x18, 0x411111f0},
+ {0x19, 0x411111f0},
+ {0x1a, 0x411111f0},
+ {0x1b, 0x411111f0},
+ {0x1d, 0x40700001},
+ {0x1e, 0x411111f0},
+ {0x21, 0x02211040}),
+ SND_HDA_PIN_QUIRK(0x10ec0255, 0x1028, "Dell", ALC255_FIXUP_DELL1_MIC_NO_PRESENCE,
+ {0x12, 0x90a60160},
+ {0x14, 0x90170140},
+ {0x17, 0x40000000},
+ {0x18, 0x411111f0},
+ {0x19, 0x411111f0},
+ {0x1a, 0x411111f0},
+ {0x1b, 0x411111f0},
+ {0x1d, 0x40700001},
+ {0x1e, 0x411111f0},
+ {0x21, 0x02211050}),
+ SND_HDA_PIN_QUIRK(0x10ec0255, 0x1028, "Dell", ALC255_FIXUP_DELL1_MIC_NO_PRESENCE,
+ {0x12, 0x90a60170},
+ {0x14, 0x90170120},
+ {0x17, 0x40000000},
+ {0x18, 0x411111f0},
+ {0x19, 0x411111f0},
+ {0x1a, 0x411111f0},
+ {0x1b, 0x411111f0},
+ {0x1d, 0x40700001},
+ {0x1e, 0x411111f0},
+ {0x21, 0x02211030}),
+ SND_HDA_PIN_QUIRK(0x10ec0255, 0x1028, "Dell", ALC255_FIXUP_DELL1_MIC_NO_PRESENCE,
+ {0x12, 0x90a60170},
+ {0x14, 0x90170130},
+ {0x17, 0x40000000},
+ {0x18, 0x411111f0},
+ {0x19, 0x411111f0},
+ {0x1a, 0x411111f0},
+ {0x1b, 0x411111f0},
+ {0x1d, 0x40700001},
+ {0x1e, 0x411111f0},
+ {0x21, 0x02211040}),
+ SND_HDA_PIN_QUIRK(0x10ec0283, 0x1028, "Dell", ALC269_FIXUP_DELL1_MIC_NO_PRESENCE,
+ {0x12, 0x90a60130},
+ {0x14, 0x90170110},
+ {0x17, 0x40020008},
+ {0x18, 0x411111f0},
+ {0x19, 0x411111f0},
+ {0x1a, 0x411111f0},
+ {0x1b, 0x411111f0},
+ {0x1d, 0x40e00001},
+ {0x1e, 0x411111f0},
+ {0x21, 0x0321101f}),
+ SND_HDA_PIN_QUIRK(0x10ec0283, 0x1028, "Dell", ALC269_FIXUP_DELL1_MIC_NO_PRESENCE,
+ {0x12, 0x90a60160},
+ {0x14, 0x90170120},
+ {0x17, 0x40000000},
+ {0x18, 0x411111f0},
+ {0x19, 0x411111f0},
+ {0x1a, 0x411111f0},
+ {0x1b, 0x411111f0},
+ {0x1d, 0x40700001},
+ {0x1e, 0x411111f0},
+ {0x21, 0x02211030}),
+ SND_HDA_PIN_QUIRK(0x10ec0292, 0x1028, "Dell", ALC269_FIXUP_DELL3_MIC_NO_PRESENCE,
+ {0x12, 0x90a60140},
+ {0x13, 0x411111f0},
+ {0x14, 0x90170110},
+ {0x15, 0x0221401f},
+ {0x16, 0x411111f0},
+ {0x18, 0x411111f0},
+ {0x19, 0x411111f0},
+ {0x1a, 0x411111f0},
+ {0x1b, 0x411111f0},
+ {0x1d, 0x40700001},
+ {0x1e, 0x411111f0}),
+ SND_HDA_PIN_QUIRK(0x10ec0293, 0x1028, "Dell", ALC293_FIXUP_DELL1_MIC_NO_PRESENCE,
+ {0x12, 0x40000000},
+ {0x13, 0x90a60140},
+ {0x14, 0x90170110},
+ {0x15, 0x0221401f},
+ {0x16, 0x21014020},
+ {0x18, 0x411111f0},
+ {0x19, 0x21a19030},
+ {0x1a, 0x411111f0},
+ {0x1b, 0x411111f0},
+ {0x1d, 0x40700001},
+ {0x1e, 0x411111f0}),
+ SND_HDA_PIN_QUIRK(0x10ec0293, 0x1028, "Dell", ALC293_FIXUP_DELL1_MIC_NO_PRESENCE,
+ {0x12, 0x40000000},
+ {0x13, 0x90a60140},
+ {0x14, 0x90170110},
+ {0x15, 0x0221401f},
+ {0x16, 0x411111f0},
+ {0x18, 0x411111f0},
+ {0x19, 0x411111f0},
+ {0x1a, 0x411111f0},
+ {0x1b, 0x411111f0},
+ {0x1d, 0x40700001},
+ {0x1e, 0x411111f0}),
+ {}
};
-static int alc269_fill_coef(struct hda_codec *codec)
+static void alc269_fill_coef(struct hda_codec *codec)
{
+ struct alc_spec *spec = codec->spec;
int val;
- if ((alc_read_coef_idx(codec, 0) & 0x00ff) < 0x015) {
+ if (spec->codec_variant != ALC269_TYPE_ALC269VB)
+ return;
+
+ if ((alc_get_coef0(codec) & 0x00ff) < 0x015) {
alc_write_coef_idx(codec, 0xf, 0x960b);
alc_write_coef_idx(codec, 0xe, 0x8817);
}
- if ((alc_read_coef_idx(codec, 0) & 0x00ff) == 0x016) {
+ if ((alc_get_coef0(codec) & 0x00ff) == 0x016) {
alc_write_coef_idx(codec, 0xf, 0x960b);
alc_write_coef_idx(codec, 0xe, 0x8814);
}
- if ((alc_read_coef_idx(codec, 0) & 0x00ff) == 0x017) {
+ if ((alc_get_coef0(codec) & 0x00ff) == 0x017) {
val = alc_read_coef_idx(codec, 0x04);
/* Power up output pin */
alc_write_coef_idx(codec, 0x04, val | (1<<11));
}
- if ((alc_read_coef_idx(codec, 0) & 0x00ff) == 0x018) {
+ if ((alc_get_coef0(codec) & 0x00ff) == 0x018) {
val = alc_read_coef_idx(codec, 0xd);
if ((val & 0x0c00) >> 10 != 0x1) {
/* Capless ramp up clock control */
- alc_write_coef_idx(codec, 0xd, val | 1<<10);
+ alc_write_coef_idx(codec, 0xd, val | (1<<10));
}
val = alc_read_coef_idx(codec, 0x17);
if ((val & 0x01c0) >> 6 != 0x4) {
/* Class D power on reset */
- alc_write_coef_idx(codec, 0x17, val | 1<<7);
+ alc_write_coef_idx(codec, 0x17, val | (1<<7));
}
}
- return 0;
+
+ val = alc_read_coef_idx(codec, 0xd); /* Class D */
+ alc_write_coef_idx(codec, 0xd, val | (1<<14));
+
+ val = alc_read_coef_idx(codec, 0x4); /* HP */
+ alc_write_coef_idx(codec, 0x4, val | (1<<11));
}
+/*
+ */
static int patch_alc269(struct hda_codec *codec)
{
struct alc_spec *spec;
- int board_config, coef;
int err;
- spec = kzalloc(sizeof(*spec), GFP_KERNEL);
- if (spec == NULL)
- return -ENOMEM;
+ err = alc_alloc_spec(codec, 0x0b);
+ if (err < 0)
+ return err;
- codec->spec = spec;
+ spec = codec->spec;
+ spec->gen.shared_mic_vref_pin = 0x18;
+
+ snd_hda_pick_fixup(codec, alc269_fixup_models,
+ alc269_fixup_tbl, alc269_fixups);
+ snd_hda_pick_pin_fixup(codec, alc269_pin_fixup_tbl, alc269_fixups);
+ snd_hda_apply_fixup(codec, HDA_FIXUP_ACT_PRE_PROBE);
alc_auto_parse_customize_define(codec);
- if (codec->vendor_id == 0x10ec0269) {
- coef = alc_read_coef_idx(codec, 0);
- if ((coef & 0x00f0) == 0x0010) {
- if (codec->bus->pci->subsystem_vendor == 0x1025 &&
- spec->cdefine.platform_type == 1) {
- alc_codec_rename(codec, "ALC271X");
- spec->codec_variant = ALC269_TYPE_ALC271X;
- } else if ((coef & 0xf000) == 0x1000) {
- spec->codec_variant = ALC269_TYPE_ALC270;
- } else if ((coef & 0xf000) == 0x2000) {
- alc_codec_rename(codec, "ALC259");
- spec->codec_variant = ALC269_TYPE_ALC259;
- } else if ((coef & 0xf000) == 0x3000) {
- alc_codec_rename(codec, "ALC258");
- spec->codec_variant = ALC269_TYPE_ALC258;
- } else {
- alc_codec_rename(codec, "ALC269VB");
- spec->codec_variant = ALC269_TYPE_ALC269VB;
- }
- } else
+ if (has_cdefine_beep(codec))
+ spec->gen.beep_nid = 0x01;
+
+ switch (codec->vendor_id) {
+ case 0x10ec0269:
+ spec->codec_variant = ALC269_TYPE_ALC269VA;
+ switch (alc_get_coef0(codec) & 0x00f0) {
+ case 0x0010:
+ if (codec->bus->pci &&
+ codec->bus->pci->subsystem_vendor == 0x1025 &&
+ spec->cdefine.platform_type == 1)
+ err = alc_codec_rename(codec, "ALC271X");
+ spec->codec_variant = ALC269_TYPE_ALC269VB;
+ break;
+ case 0x0020:
+ if (codec->bus->pci &&
+ codec->bus->pci->subsystem_vendor == 0x17aa &&
+ codec->bus->pci->subsystem_device == 0x21f3)
+ err = alc_codec_rename(codec, "ALC3202");
+ spec->codec_variant = ALC269_TYPE_ALC269VC;
+ break;
+ case 0x0030:
+ spec->codec_variant = ALC269_TYPE_ALC269VD;
+ break;
+ default:
alc_fix_pll_init(codec, 0x20, 0x04, 15);
+ }
+ if (err < 0)
+ goto error;
+ spec->init_hook = alc269_fill_coef;
alc269_fill_coef(codec);
- }
-
- board_config = snd_hda_check_board_config(codec, ALC269_MODEL_LAST,
- alc269_models,
- alc269_cfg_tbl);
-
- if (board_config < 0) {
- printk(KERN_INFO "hda_codec: %s: BIOS auto-probing.\n",
- codec->chip_name);
- board_config = ALC269_AUTO;
- }
-
- if (board_config == ALC269_AUTO) {
- alc_pick_fixup(codec, NULL, alc269_fixup_tbl, alc269_fixups);
- alc_apply_fixup(codec, ALC_FIXUP_ACT_PRE_PROBE);
- }
+ break;
- if (board_config == ALC269_AUTO) {
- /* automatic parse from the BIOS config */
- err = alc269_parse_auto_config(codec);
- if (err < 0) {
- alc_free(codec);
- return err;
- } else if (!err) {
- printk(KERN_INFO
- "hda_codec: Cannot set up configuration "
- "from BIOS. Using base mode...\n");
- board_config = ALC269_BASIC;
- }
+ case 0x10ec0280:
+ case 0x10ec0290:
+ spec->codec_variant = ALC269_TYPE_ALC280;
+ break;
+ case 0x10ec0282:
+ spec->codec_variant = ALC269_TYPE_ALC282;
+ spec->shutup = alc282_shutup;
+ spec->init_hook = alc282_init;
+ break;
+ case 0x10ec0233:
+ case 0x10ec0283:
+ spec->codec_variant = ALC269_TYPE_ALC283;
+ spec->shutup = alc283_shutup;
+ spec->init_hook = alc283_init;
+ break;
+ case 0x10ec0284:
+ case 0x10ec0292:
+ spec->codec_variant = ALC269_TYPE_ALC284;
+ break;
+ case 0x10ec0285:
+ case 0x10ec0293:
+ spec->codec_variant = ALC269_TYPE_ALC285;
+ break;
+ case 0x10ec0286:
+ case 0x10ec0288:
+ spec->codec_variant = ALC269_TYPE_ALC286;
+ break;
+ case 0x10ec0255:
+ spec->codec_variant = ALC269_TYPE_ALC255;
+ break;
}
- if (has_cdefine_beep(codec)) {
- err = snd_hda_attach_beep_device(codec, 0x1);
- if (err < 0) {
- alc_free(codec);
- return err;
- }
+ if (snd_hda_codec_read(codec, 0x51, 0, AC_VERB_PARAMETERS, 0) == 0x10ec5505) {
+ spec->has_alc5505_dsp = 1;
+ spec->init_hook = alc5505_dsp_init;
}
- if (board_config != ALC269_AUTO)
- setup_preset(codec, &alc269_presets[board_config]);
-
- if (board_config == ALC269_QUANTA_FL1) {
- /* Due to a hardware problem on Lenovo Ideadpad, we need to
- * fix the sample rate of analog I/O to 44.1kHz
- */
- spec->stream_analog_playback = &alc269_44k_pcm_analog_playback;
- spec->stream_analog_capture = &alc269_44k_pcm_analog_capture;
- } else if (spec->dual_adc_switch) {
- spec->stream_analog_playback = &alc269_pcm_analog_playback;
- /* switch ADC dynamically */
- spec->stream_analog_capture = &dualmic_pcm_analog_capture;
- } else {
- spec->stream_analog_playback = &alc269_pcm_analog_playback;
- spec->stream_analog_capture = &alc269_pcm_analog_capture;
- }
- spec->stream_digital_playback = &alc269_pcm_digital_playback;
- spec->stream_digital_capture = &alc269_pcm_digital_capture;
-
- if (!spec->adc_nids) { /* wasn't filled automatically? use default */
- if (spec->codec_variant == ALC269_TYPE_NORMAL) {
- spec->adc_nids = alc269_adc_nids;
- spec->num_adc_nids = ARRAY_SIZE(alc269_adc_nids);
- spec->capsrc_nids = alc269_capsrc_nids;
- } else {
- spec->adc_nids = alc269vb_adc_nids;
- spec->num_adc_nids = ARRAY_SIZE(alc269vb_adc_nids);
- spec->capsrc_nids = alc269vb_capsrc_nids;
- }
- }
+ /* automatic parse from the BIOS config */
+ err = alc269_parse_auto_config(codec);
+ if (err < 0)
+ goto error;
- if (!spec->cap_mixer)
- set_capture_mixer(codec);
- if (has_cdefine_beep(codec))
+ if (!spec->gen.no_analog && spec->gen.beep_nid)
set_beep_amp(spec, 0x0b, 0x04, HDA_INPUT);
- alc_apply_fixup(codec, ALC_FIXUP_ACT_PROBE);
-
- spec->vmaster_nid = 0x02;
-
codec->patch_ops = alc_patch_ops;
-#ifdef CONFIG_SND_HDA_POWER_SAVE
+#ifdef CONFIG_PM
codec->patch_ops.suspend = alc269_suspend;
-#endif
-#ifdef SND_HDA_NEEDS_RESUME
codec->patch_ops.resume = alc269_resume;
#endif
- if (board_config == ALC269_AUTO)
- spec->init_hook = alc269_auto_init;
-
- alc_init_jacks(codec);
-#ifdef CONFIG_SND_HDA_POWER_SAVE
- if (!spec->loopback.amplist)
- spec->loopback.amplist = alc269_loopbacks;
- if (alc269_mic2_for_mute_led(codec))
- codec->patch_ops.check_power_status = alc269_mic2_mute_check_ps;
-#endif
-
- return 0;
-}
-
-/*
- * ALC861 channel source setting (2/6 channel selection for 3-stack)
- */
-
-/*
- * set the path ways for 2 channel output
- * need to set the codec line out and mic 1 pin widgets to inputs
- */
-static struct hda_verb alc861_threestack_ch2_init[] = {
- /* set pin widget 1Ah (line in) for input */
- { 0x0c, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x20 },
- /* set pin widget 18h (mic1/2) for input, for mic also enable
- * the vref
- */
- { 0x0d, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x24 },
-
- { 0x15, AC_VERB_SET_AMP_GAIN_MUTE, 0xb00c },
-#if 0
- { 0x15, AC_VERB_SET_AMP_GAIN_MUTE, (0x7000 | (0x01 << 8)) }, /*mic*/
- { 0x15, AC_VERB_SET_AMP_GAIN_MUTE, (0x7000 | (0x02 << 8)) }, /*line-in*/
-#endif
- { } /* end */
-};
-/*
- * 6ch mode
- * need to set the codec line out and mic 1 pin widgets to outputs
- */
-static struct hda_verb alc861_threestack_ch6_init[] = {
- /* set pin widget 1Ah (line in) for output (Back Surround)*/
- { 0x0c, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x40 },
- /* set pin widget 18h (mic1) for output (CLFE)*/
- { 0x0d, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x40 },
-
- { 0x0c, AC_VERB_SET_CONNECT_SEL, 0x00 },
- { 0x0d, AC_VERB_SET_CONNECT_SEL, 0x00 },
-
- { 0x15, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080 },
-#if 0
- { 0x15, AC_VERB_SET_AMP_GAIN_MUTE, (0x7080 | (0x01 << 8)) }, /*mic*/
- { 0x15, AC_VERB_SET_AMP_GAIN_MUTE, (0x7080 | (0x02 << 8)) }, /*line in*/
-#endif
- { } /* end */
-};
-
-static struct hda_channel_mode alc861_threestack_modes[2] = {
- { 2, alc861_threestack_ch2_init },
- { 6, alc861_threestack_ch6_init },
-};
-/* Set mic1 as input and unmute the mixer */
-static struct hda_verb alc861_uniwill_m31_ch2_init[] = {
- { 0x0d, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x24 },
- { 0x15, AC_VERB_SET_AMP_GAIN_MUTE, (0x7080 | (0x01 << 8)) }, /*mic*/
- { } /* end */
-};
-/* Set mic1 as output and mute mixer */
-static struct hda_verb alc861_uniwill_m31_ch4_init[] = {
- { 0x0d, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x40 },
- { 0x15, AC_VERB_SET_AMP_GAIN_MUTE, (0x7000 | (0x01 << 8)) }, /*mic*/
- { } /* end */
-};
-
-static struct hda_channel_mode alc861_uniwill_m31_modes[2] = {
- { 2, alc861_uniwill_m31_ch2_init },
- { 4, alc861_uniwill_m31_ch4_init },
-};
-
-/* Set mic1 and line-in as input and unmute the mixer */
-static struct hda_verb alc861_asus_ch2_init[] = {
- /* set pin widget 1Ah (line in) for input */
- { 0x0c, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x20 },
- /* set pin widget 18h (mic1/2) for input, for mic also enable
- * the vref
- */
- { 0x0d, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x24 },
-
- { 0x15, AC_VERB_SET_AMP_GAIN_MUTE, 0xb00c },
-#if 0
- { 0x15, AC_VERB_SET_AMP_GAIN_MUTE, (0x7000 | (0x01 << 8)) }, /*mic*/
- { 0x15, AC_VERB_SET_AMP_GAIN_MUTE, (0x7000 | (0x02 << 8)) }, /*line-in*/
-#endif
- { } /* end */
-};
-/* Set mic1 nad line-in as output and mute mixer */
-static struct hda_verb alc861_asus_ch6_init[] = {
- /* set pin widget 1Ah (line in) for output (Back Surround)*/
- { 0x0c, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x40 },
- /* { 0x0c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE }, */
- /* set pin widget 18h (mic1) for output (CLFE)*/
- { 0x0d, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x40 },
- /* { 0x0d, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE }, */
- { 0x0c, AC_VERB_SET_CONNECT_SEL, 0x00 },
- { 0x0d, AC_VERB_SET_CONNECT_SEL, 0x00 },
-
- { 0x15, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080 },
-#if 0
- { 0x15, AC_VERB_SET_AMP_GAIN_MUTE, (0x7080 | (0x01 << 8)) }, /*mic*/
- { 0x15, AC_VERB_SET_AMP_GAIN_MUTE, (0x7080 | (0x02 << 8)) }, /*line in*/
-#endif
- { } /* end */
-};
-
-static struct hda_channel_mode alc861_asus_modes[2] = {
- { 2, alc861_asus_ch2_init },
- { 6, alc861_asus_ch6_init },
-};
-
-/* patch-ALC861 */
-
-static struct snd_kcontrol_new alc861_base_mixer[] = {
- /* output mixer control */
- HDA_CODEC_MUTE("Front Playback Switch", 0x03, 0x0, HDA_OUTPUT),
- HDA_CODEC_MUTE("Surround Playback Switch", 0x06, 0x0, HDA_OUTPUT),
- HDA_CODEC_MUTE_MONO("Center Playback Switch", 0x05, 1, 0x0, HDA_OUTPUT),
- HDA_CODEC_MUTE_MONO("LFE Playback Switch", 0x05, 2, 0x0, HDA_OUTPUT),
- HDA_CODEC_MUTE("Side Playback Switch", 0x04, 0x0, HDA_OUTPUT),
-
- /*Input mixer control */
- /* HDA_CODEC_VOLUME("Input Playback Volume", 0x15, 0x0, HDA_OUTPUT),
- HDA_CODEC_MUTE("Input Playback Switch", 0x15, 0x0, HDA_OUTPUT), */
- HDA_CODEC_VOLUME("CD Playback Volume", 0x15, 0x0, HDA_INPUT),
- HDA_CODEC_MUTE("CD Playback Switch", 0x15, 0x0, HDA_INPUT),
- HDA_CODEC_VOLUME("Line Playback Volume", 0x15, 0x02, HDA_INPUT),
- HDA_CODEC_MUTE("Line Playback Switch", 0x15, 0x02, HDA_INPUT),
- HDA_CODEC_VOLUME("Mic Playback Volume", 0x15, 0x01, HDA_INPUT),
- HDA_CODEC_MUTE("Mic Playback Switch", 0x15, 0x01, HDA_INPUT),
- HDA_CODEC_MUTE("Front Mic Playback Switch", 0x10, 0x01, HDA_OUTPUT),
- HDA_CODEC_MUTE("Headphone Playback Switch", 0x1a, 0x03, HDA_INPUT),
-
- { } /* end */
-};
-
-static struct snd_kcontrol_new alc861_3ST_mixer[] = {
- /* output mixer control */
- HDA_CODEC_MUTE("Front Playback Switch", 0x03, 0x0, HDA_OUTPUT),
- HDA_CODEC_MUTE("Surround Playback Switch", 0x06, 0x0, HDA_OUTPUT),
- HDA_CODEC_MUTE_MONO("Center Playback Switch", 0x05, 1, 0x0, HDA_OUTPUT),
- HDA_CODEC_MUTE_MONO("LFE Playback Switch", 0x05, 2, 0x0, HDA_OUTPUT),
- /*HDA_CODEC_MUTE("Side Playback Switch", 0x04, 0x0, HDA_OUTPUT), */
-
- /* Input mixer control */
- /* HDA_CODEC_VOLUME("Input Playback Volume", 0x15, 0x0, HDA_OUTPUT),
- HDA_CODEC_MUTE("Input Playback Switch", 0x15, 0x0, HDA_OUTPUT), */
- HDA_CODEC_VOLUME("CD Playback Volume", 0x15, 0x0, HDA_INPUT),
- HDA_CODEC_MUTE("CD Playback Switch", 0x15, 0x0, HDA_INPUT),
- HDA_CODEC_VOLUME("Line Playback Volume", 0x15, 0x02, HDA_INPUT),
- HDA_CODEC_MUTE("Line Playback Switch", 0x15, 0x02, HDA_INPUT),
- HDA_CODEC_VOLUME("Mic Playback Volume", 0x15, 0x01, HDA_INPUT),
- HDA_CODEC_MUTE("Mic Playback Switch", 0x15, 0x01, HDA_INPUT),
- HDA_CODEC_MUTE("Front Mic Playback Switch", 0x10, 0x01, HDA_OUTPUT),
- HDA_CODEC_MUTE("Headphone Playback Switch", 0x1a, 0x03, HDA_INPUT),
-
- {
- .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
- .name = "Channel Mode",
- .info = alc_ch_mode_info,
- .get = alc_ch_mode_get,
- .put = alc_ch_mode_put,
- .private_value = ARRAY_SIZE(alc861_threestack_modes),
- },
- { } /* end */
-};
-
-static struct snd_kcontrol_new alc861_toshiba_mixer[] = {
- /* output mixer control */
- HDA_CODEC_MUTE("Master Playback Switch", 0x03, 0x0, HDA_OUTPUT),
- HDA_CODEC_VOLUME("Mic Playback Volume", 0x15, 0x01, HDA_INPUT),
- HDA_CODEC_MUTE("Mic Playback Switch", 0x15, 0x01, HDA_INPUT),
-
- { } /* end */
-};
-
-static struct snd_kcontrol_new alc861_uniwill_m31_mixer[] = {
- /* output mixer control */
- HDA_CODEC_MUTE("Front Playback Switch", 0x03, 0x0, HDA_OUTPUT),
- HDA_CODEC_MUTE("Surround Playback Switch", 0x06, 0x0, HDA_OUTPUT),
- HDA_CODEC_MUTE_MONO("Center Playback Switch", 0x05, 1, 0x0, HDA_OUTPUT),
- HDA_CODEC_MUTE_MONO("LFE Playback Switch", 0x05, 2, 0x0, HDA_OUTPUT),
- /*HDA_CODEC_MUTE("Side Playback Switch", 0x04, 0x0, HDA_OUTPUT), */
-
- /* Input mixer control */
- /* HDA_CODEC_VOLUME("Input Playback Volume", 0x15, 0x0, HDA_OUTPUT),
- HDA_CODEC_MUTE("Input Playback Switch", 0x15, 0x0, HDA_OUTPUT), */
- HDA_CODEC_VOLUME("CD Playback Volume", 0x15, 0x0, HDA_INPUT),
- HDA_CODEC_MUTE("CD Playback Switch", 0x15, 0x0, HDA_INPUT),
- HDA_CODEC_VOLUME("Line Playback Volume", 0x15, 0x02, HDA_INPUT),
- HDA_CODEC_MUTE("Line Playback Switch", 0x15, 0x02, HDA_INPUT),
- HDA_CODEC_VOLUME("Mic Playback Volume", 0x15, 0x01, HDA_INPUT),
- HDA_CODEC_MUTE("Mic Playback Switch", 0x15, 0x01, HDA_INPUT),
- HDA_CODEC_MUTE("Front Mic Playback Switch", 0x10, 0x01, HDA_OUTPUT),
- HDA_CODEC_MUTE("Headphone Playback Switch", 0x1a, 0x03, HDA_INPUT),
-
- {
- .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
- .name = "Channel Mode",
- .info = alc_ch_mode_info,
- .get = alc_ch_mode_get,
- .put = alc_ch_mode_put,
- .private_value = ARRAY_SIZE(alc861_uniwill_m31_modes),
- },
- { } /* end */
-};
-
-static struct snd_kcontrol_new alc861_asus_mixer[] = {
- /* output mixer control */
- HDA_CODEC_MUTE("Front Playback Switch", 0x03, 0x0, HDA_OUTPUT),
- HDA_CODEC_MUTE("Surround Playback Switch", 0x06, 0x0, HDA_OUTPUT),
- HDA_CODEC_MUTE_MONO("Center Playback Switch", 0x05, 1, 0x0, HDA_OUTPUT),
- HDA_CODEC_MUTE_MONO("LFE Playback Switch", 0x05, 2, 0x0, HDA_OUTPUT),
- HDA_CODEC_MUTE("Side Playback Switch", 0x04, 0x0, HDA_OUTPUT),
-
- /* Input mixer control */
- HDA_CODEC_VOLUME("Input Playback Volume", 0x15, 0x0, HDA_OUTPUT),
- HDA_CODEC_MUTE("Input Playback Switch", 0x15, 0x0, HDA_OUTPUT),
- HDA_CODEC_VOLUME("CD Playback Volume", 0x15, 0x0, HDA_INPUT),
- HDA_CODEC_MUTE("CD Playback Switch", 0x15, 0x0, HDA_INPUT),
- HDA_CODEC_VOLUME("Line Playback Volume", 0x15, 0x02, HDA_INPUT),
- HDA_CODEC_MUTE("Line Playback Switch", 0x15, 0x02, HDA_INPUT),
- HDA_CODEC_VOLUME("Mic Playback Volume", 0x15, 0x01, HDA_INPUT),
- HDA_CODEC_MUTE("Mic Playback Switch", 0x15, 0x01, HDA_INPUT),
- HDA_CODEC_MUTE("Front Mic Playback Switch", 0x10, 0x01, HDA_OUTPUT),
- HDA_CODEC_MUTE("Headphone Playback Switch", 0x1a, 0x03, HDA_OUTPUT),
-
- {
- .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
- .name = "Channel Mode",
- .info = alc_ch_mode_info,
- .get = alc_ch_mode_get,
- .put = alc_ch_mode_put,
- .private_value = ARRAY_SIZE(alc861_asus_modes),
- },
- { }
-};
+ if (!spec->shutup)
+ spec->shutup = alc269_shutup;
-/* additional mixer */
-static struct snd_kcontrol_new alc861_asus_laptop_mixer[] = {
- HDA_CODEC_VOLUME("CD Playback Volume", 0x15, 0x0, HDA_INPUT),
- HDA_CODEC_MUTE("CD Playback Switch", 0x15, 0x0, HDA_INPUT),
- { }
-};
+ snd_hda_apply_fixup(codec, HDA_FIXUP_ACT_PROBE);
-/*
- * generic initialization of ADC, input mixers and output mixers
- */
-static struct hda_verb alc861_base_init_verbs[] = {
- /*
- * Unmute ADC0 and set the default input to mic-in
- */
- /* port-A for surround (rear panel) */
- { 0x0e, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x40 },
- { 0x0e, AC_VERB_SET_CONNECT_SEL, 0x00 },
- /* port-B for mic-in (rear panel) with vref */
- { 0x0d, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x24 },
- /* port-C for line-in (rear panel) */
- { 0x0c, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x20 },
- /* port-D for Front */
- { 0x0b, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x40 },
- { 0x0b, AC_VERB_SET_CONNECT_SEL, 0x00 },
- /* port-E for HP out (front panel) */
- { 0x0f, AC_VERB_SET_PIN_WIDGET_CONTROL, 0xc0 },
- /* route front PCM to HP */
- { 0x0f, AC_VERB_SET_CONNECT_SEL, 0x00 },
- /* port-F for mic-in (front panel) with vref */
- { 0x10, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x24 },
- /* port-G for CLFE (rear panel) */
- { 0x1f, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x40 },
- { 0x1f, AC_VERB_SET_CONNECT_SEL, 0x00 },
- /* port-H for side (rear panel) */
- { 0x20, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x40 },
- { 0x20, AC_VERB_SET_CONNECT_SEL, 0x00 },
- /* CD-in */
- { 0x11, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x20 },
- /* route front mic to ADC1*/
- {0x08, AC_VERB_SET_CONNECT_SEL, 0x00},
- {0x08, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
-
- /* Unmute DAC0~3 & spdif out*/
- {0x03, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
- {0x04, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
- {0x05, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
- {0x06, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
- {0x07, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
-
- /* Unmute Mixer 14 (mic) 1c (Line in)*/
- {0x014, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
- {0x014, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
- {0x01c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
- {0x01c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
-
- /* Unmute Stereo Mixer 15 */
- {0x15, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
- {0x15, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
- {0x15, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(2)},
- {0x15, AC_VERB_SET_AMP_GAIN_MUTE, 0xb00c}, /* Output 0~12 step */
-
- {0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
- {0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
- {0x17, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
- {0x17, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
- {0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
- {0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
- {0x19, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
- {0x19, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
- /* hp used DAC 3 (Front) */
- {0x1a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(3)},
- {0x1a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(2)},
-
- { }
-};
-
-static struct hda_verb alc861_threestack_init_verbs[] = {
- /*
- * Unmute ADC0 and set the default input to mic-in
- */
- /* port-A for surround (rear panel) */
- { 0x0e, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x00 },
- /* port-B for mic-in (rear panel) with vref */
- { 0x0d, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x24 },
- /* port-C for line-in (rear panel) */
- { 0x0c, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x20 },
- /* port-D for Front */
- { 0x0b, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x40 },
- { 0x0b, AC_VERB_SET_CONNECT_SEL, 0x00 },
- /* port-E for HP out (front panel) */
- { 0x0f, AC_VERB_SET_PIN_WIDGET_CONTROL, 0xc0 },
- /* route front PCM to HP */
- { 0x0f, AC_VERB_SET_CONNECT_SEL, 0x00 },
- /* port-F for mic-in (front panel) with vref */
- { 0x10, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x24 },
- /* port-G for CLFE (rear panel) */
- { 0x1f, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x00 },
- /* port-H for side (rear panel) */
- { 0x20, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x00 },
- /* CD-in */
- { 0x11, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x20 },
- /* route front mic to ADC1*/
- {0x08, AC_VERB_SET_CONNECT_SEL, 0x00},
- {0x08, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
- /* Unmute DAC0~3 & spdif out*/
- {0x03, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
- {0x04, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
- {0x05, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
- {0x06, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
- {0x07, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
-
- /* Unmute Mixer 14 (mic) 1c (Line in)*/
- {0x014, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
- {0x014, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
- {0x01c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
- {0x01c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
-
- /* Unmute Stereo Mixer 15 */
- {0x15, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
- {0x15, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
- {0x15, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(2)},
- {0x15, AC_VERB_SET_AMP_GAIN_MUTE, 0xb00c}, /* Output 0~12 step */
-
- {0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
- {0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
- {0x17, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
- {0x17, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
- {0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
- {0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
- {0x19, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
- {0x19, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
- /* hp used DAC 3 (Front) */
- {0x1a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(3)},
- {0x1a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(2)},
- { }
-};
-
-static struct hda_verb alc861_uniwill_m31_init_verbs[] = {
- /*
- * Unmute ADC0 and set the default input to mic-in
- */
- /* port-A for surround (rear panel) */
- { 0x0e, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x00 },
- /* port-B for mic-in (rear panel) with vref */
- { 0x0d, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x24 },
- /* port-C for line-in (rear panel) */
- { 0x0c, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x20 },
- /* port-D for Front */
- { 0x0b, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x40 },
- { 0x0b, AC_VERB_SET_CONNECT_SEL, 0x00 },
- /* port-E for HP out (front panel) */
- /* this has to be set to VREF80 */
- { 0x0f, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x24 },
- /* route front PCM to HP */
- { 0x0f, AC_VERB_SET_CONNECT_SEL, 0x00 },
- /* port-F for mic-in (front panel) with vref */
- { 0x10, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x24 },
- /* port-G for CLFE (rear panel) */
- { 0x1f, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x00 },
- /* port-H for side (rear panel) */
- { 0x20, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x00 },
- /* CD-in */
- { 0x11, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x20 },
- /* route front mic to ADC1*/
- {0x08, AC_VERB_SET_CONNECT_SEL, 0x00},
- {0x08, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
- /* Unmute DAC0~3 & spdif out*/
- {0x03, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
- {0x04, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
- {0x05, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
- {0x06, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
- {0x07, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
-
- /* Unmute Mixer 14 (mic) 1c (Line in)*/
- {0x014, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
- {0x014, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
- {0x01c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
- {0x01c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
-
- /* Unmute Stereo Mixer 15 */
- {0x15, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
- {0x15, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
- {0x15, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(2)},
- {0x15, AC_VERB_SET_AMP_GAIN_MUTE, 0xb00c}, /* Output 0~12 step */
-
- {0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
- {0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
- {0x17, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
- {0x17, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
- {0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
- {0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
- {0x19, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
- {0x19, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
- /* hp used DAC 3 (Front) */
- {0x1a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(3)},
- {0x1a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(2)},
- { }
-};
-
-static struct hda_verb alc861_asus_init_verbs[] = {
- /*
- * Unmute ADC0 and set the default input to mic-in
- */
- /* port-A for surround (rear panel)
- * according to codec#0 this is the HP jack
- */
- { 0x0e, AC_VERB_SET_PIN_WIDGET_CONTROL, 0xc0 }, /* was 0x00 */
- /* route front PCM to HP */
- { 0x0e, AC_VERB_SET_CONNECT_SEL, 0x01 },
- /* port-B for mic-in (rear panel) with vref */
- { 0x0d, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x24 },
- /* port-C for line-in (rear panel) */
- { 0x0c, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x20 },
- /* port-D for Front */
- { 0x0b, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x40 },
- { 0x0b, AC_VERB_SET_CONNECT_SEL, 0x00 },
- /* port-E for HP out (front panel) */
- /* this has to be set to VREF80 */
- { 0x0f, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x24 },
- /* route front PCM to HP */
- { 0x0f, AC_VERB_SET_CONNECT_SEL, 0x00 },
- /* port-F for mic-in (front panel) with vref */
- { 0x10, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x24 },
- /* port-G for CLFE (rear panel) */
- { 0x1f, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x40 },
- /* port-H for side (rear panel) */
- { 0x20, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x40 },
- /* CD-in */
- { 0x11, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x20 },
- /* route front mic to ADC1*/
- {0x08, AC_VERB_SET_CONNECT_SEL, 0x00},
- {0x08, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
- /* Unmute DAC0~3 & spdif out*/
- {0x03, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
- {0x04, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
- {0x05, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
- {0x06, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
- {0x07, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
- /* Unmute Mixer 14 (mic) 1c (Line in)*/
- {0x014, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
- {0x014, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
- {0x01c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
- {0x01c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
-
- /* Unmute Stereo Mixer 15 */
- {0x15, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
- {0x15, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
- {0x15, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(2)},
- {0x15, AC_VERB_SET_AMP_GAIN_MUTE, 0xb00c}, /* Output 0~12 step */
-
- {0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
- {0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
- {0x17, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
- {0x17, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
- {0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
- {0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
- {0x19, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
- {0x19, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
- /* hp used DAC 3 (Front) */
- {0x1a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(3)},
- {0x1a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(2)},
- { }
-};
+ return 0;
-/* additional init verbs for ASUS laptops */
-static struct hda_verb alc861_asus_laptop_init_verbs[] = {
- { 0x0f, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x45 }, /* HP-out */
- { 0x15, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(2) }, /* mute line-in */
- { }
-};
+ error:
+ alc_free(codec);
+ return err;
+}
/*
- * generic initialization of ADC, input mixers and output mixers
+ * ALC861
*/
-static struct hda_verb alc861_auto_init_verbs[] = {
- /*
- * Unmute ADC0 and set the default input to mic-in
- */
- /* {0x08, AC_VERB_SET_CONNECT_SEL, 0x00}, */
- {0x08, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
-
- /* Unmute DAC0~3 & spdif out*/
- {0x03, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
- {0x04, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
- {0x05, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
- {0x06, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
- {0x07, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
-
- /* Unmute Mixer 14 (mic) 1c (Line in)*/
- {0x014, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
- {0x014, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
- {0x01c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
- {0x01c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
-
- /* Unmute Stereo Mixer 15 */
- {0x15, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
- {0x15, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
- {0x15, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(2)},
- {0x15, AC_VERB_SET_AMP_GAIN_MUTE, 0xb00c},
-
- {0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
- {0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
- {0x17, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
- {0x17, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
- {0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
- {0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
- {0x19, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
- {0x19, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
-
- {0x1a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
- {0x1a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
- {0x1a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(2)},
- {0x1a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(3)},
- {0x1b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
- {0x1b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
- {0x1b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(2)},
- {0x1b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(3)},
-
- {0x08, AC_VERB_SET_CONNECT_SEL, 0x00}, /* set Mic 1 */
-
- { }
-};
-
-static struct hda_verb alc861_toshiba_init_verbs[] = {
- {0x0f, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | ALC880_HP_EVENT},
- { }
-};
-
-/* toggle speaker-output according to the hp-jack state */
-static void alc861_toshiba_automute(struct hda_codec *codec)
-{
- unsigned int present = snd_hda_jack_detect(codec, 0x0f);
-
- snd_hda_codec_amp_stereo(codec, 0x16, HDA_INPUT, 0,
- HDA_AMP_MUTE, present ? HDA_AMP_MUTE : 0);
- snd_hda_codec_amp_stereo(codec, 0x1a, HDA_INPUT, 3,
- HDA_AMP_MUTE, present ? 0 : HDA_AMP_MUTE);
-}
-
-static void alc861_toshiba_unsol_event(struct hda_codec *codec,
- unsigned int res)
+static int alc861_parse_auto_config(struct hda_codec *codec)
{
- if ((res >> 26) == ALC880_HP_EVENT)
- alc861_toshiba_automute(codec);
+ static const hda_nid_t alc861_ignore[] = { 0x1d, 0 };
+ static const hda_nid_t alc861_ssids[] = { 0x0e, 0x0f, 0x0b, 0 };
+ return alc_parse_auto_config(codec, alc861_ignore, alc861_ssids);
}
-/* pcm configuration: identical with ALC880 */
-#define alc861_pcm_analog_playback alc880_pcm_analog_playback
-#define alc861_pcm_analog_capture alc880_pcm_analog_capture
-#define alc861_pcm_digital_playback alc880_pcm_digital_playback
-#define alc861_pcm_digital_capture alc880_pcm_digital_capture
-
-
-#define ALC861_DIGOUT_NID 0x07
-
-static struct hda_channel_mode alc861_8ch_modes[1] = {
- { 8, NULL }
-};
-
-static hda_nid_t alc861_dac_nids[4] = {
- /* front, surround, clfe, side */
- 0x03, 0x06, 0x05, 0x04
-};
-
-static hda_nid_t alc660_dac_nids[3] = {
- /* front, clfe, surround */
- 0x03, 0x05, 0x06
-};
-
-static hda_nid_t alc861_adc_nids[1] = {
- /* ADC0-2 */
- 0x08,
-};
-
-static struct hda_input_mux alc861_capture_source = {
- .num_items = 5,
- .items = {
- { "Mic", 0x0 },
- { "Front Mic", 0x3 },
- { "Line", 0x1 },
- { "CD", 0x4 },
- { "Mixer", 0x5 },
- },
+/* Pin config fixes */
+enum {
+ ALC861_FIXUP_FSC_AMILO_PI1505,
+ ALC861_FIXUP_AMP_VREF_0F,
+ ALC861_FIXUP_NO_JACK_DETECT,
+ ALC861_FIXUP_ASUS_A6RP,
+ ALC660_FIXUP_ASUS_W7J,
};
-static hda_nid_t alc861_look_for_dac(struct hda_codec *codec, hda_nid_t pin)
+/* On some laptops, VREF of pin 0x0f is abused for controlling the main amp */
+static void alc861_fixup_asus_amp_vref_0f(struct hda_codec *codec,
+ const struct hda_fixup *fix, int action)
{
struct alc_spec *spec = codec->spec;
- hda_nid_t mix, srcs[5];
- int i, j, num;
-
- if (snd_hda_get_connections(codec, pin, &mix, 1) != 1)
- return 0;
- num = snd_hda_get_connections(codec, mix, srcs, ARRAY_SIZE(srcs));
- if (num < 0)
- return 0;
- for (i = 0; i < num; i++) {
- unsigned int type;
- type = get_wcaps_type(get_wcaps(codec, srcs[i]));
- if (type != AC_WID_AUD_OUT)
- continue;
- for (j = 0; j < spec->multiout.num_dacs; j++)
- if (spec->multiout.dac_nids[j] == srcs[i])
- break;
- if (j >= spec->multiout.num_dacs)
- return srcs[i];
- }
- return 0;
-}
-
-/* fill in the dac_nids table from the parsed pin configuration */
-static int alc861_auto_fill_dac_nids(struct hda_codec *codec,
- const struct auto_pin_cfg *cfg)
-{
- struct alc_spec *spec = codec->spec;
- int i;
- hda_nid_t nid, dac;
-
- spec->multiout.dac_nids = spec->private_dac_nids;
- for (i = 0; i < cfg->line_outs; i++) {
- nid = cfg->line_out_pins[i];
- dac = alc861_look_for_dac(codec, nid);
- if (!dac)
- continue;
- spec->multiout.dac_nids[spec->multiout.num_dacs++] = dac;
- }
- return 0;
-}
-
-static int __alc861_create_out_sw(struct hda_codec *codec, const char *pfx,
- hda_nid_t nid, int idx, unsigned int chs)
-{
- return __add_pb_sw_ctrl(codec->spec, ALC_CTL_WIDGET_MUTE, pfx, idx,
- HDA_COMPOSE_AMP_VAL(nid, chs, 0, HDA_OUTPUT));
-}
-
-#define alc861_create_out_sw(codec, pfx, nid, chs) \
- __alc861_create_out_sw(codec, pfx, nid, 0, chs)
-
-/* add playback controls from the parsed DAC table */
-static int alc861_auto_create_multi_out_ctls(struct hda_codec *codec,
- const struct auto_pin_cfg *cfg)
-{
- struct alc_spec *spec = codec->spec;
- static const char * const chname[4] = {
- "Front", "Surround", NULL /*CLFE*/, "Side"
- };
- const char *pfx = alc_get_line_out_pfx(cfg, true);
- hda_nid_t nid;
- int i, err;
-
- for (i = 0; i < cfg->line_outs; i++) {
- nid = spec->multiout.dac_nids[i];
- if (!nid)
- continue;
- if (!pfx && i == 2) {
- /* Center/LFE */
- err = alc861_create_out_sw(codec, "Center", nid, 1);
- if (err < 0)
- return err;
- err = alc861_create_out_sw(codec, "LFE", nid, 2);
- if (err < 0)
- return err;
- } else {
- const char *name = pfx;
- int index = i;
- if (!name) {
- name = chname[i];
- index = 0;
- }
- err = __alc861_create_out_sw(codec, name, nid, index, 3);
- if (err < 0)
- return err;
- }
- }
- return 0;
-}
-
-static int alc861_auto_create_hp_ctls(struct hda_codec *codec, hda_nid_t pin)
-{
- struct alc_spec *spec = codec->spec;
- int err;
- hda_nid_t nid;
-
- if (!pin)
- return 0;
-
- if ((pin >= 0x0b && pin <= 0x10) || pin == 0x1f || pin == 0x20) {
- nid = alc861_look_for_dac(codec, pin);
- if (nid) {
- err = alc861_create_out_sw(codec, "Headphone", nid, 3);
- if (err < 0)
- return err;
- spec->multiout.hp_nid = nid;
- }
- }
- return 0;
-}
-
-/* create playback/capture controls for input pins */
-static int alc861_auto_create_input_ctls(struct hda_codec *codec,
- const struct auto_pin_cfg *cfg)
-{
- return alc_auto_create_input_ctls(codec, cfg, 0x15, 0x08, 0);
-}
+ unsigned int val;
-static void alc861_auto_set_output_and_unmute(struct hda_codec *codec,
- hda_nid_t nid,
- int pin_type, hda_nid_t dac)
-{
- hda_nid_t mix, srcs[5];
- int i, num;
-
- snd_hda_codec_write(codec, nid, 0, AC_VERB_SET_PIN_WIDGET_CONTROL,
- pin_type);
- snd_hda_codec_write(codec, dac, 0, AC_VERB_SET_AMP_GAIN_MUTE,
- AMP_OUT_UNMUTE);
- if (snd_hda_get_connections(codec, nid, &mix, 1) != 1)
- return;
- num = snd_hda_get_connections(codec, mix, srcs, ARRAY_SIZE(srcs));
- if (num < 0)
+ if (action != HDA_FIXUP_ACT_INIT)
return;
- for (i = 0; i < num; i++) {
- unsigned int mute;
- if (srcs[i] == dac || srcs[i] == 0x15)
- mute = AMP_IN_UNMUTE(i);
- else
- mute = AMP_IN_MUTE(i);
- snd_hda_codec_write(codec, mix, 0, AC_VERB_SET_AMP_GAIN_MUTE,
- mute);
- }
-}
-
-static void alc861_auto_init_multi_out(struct hda_codec *codec)
-{
- struct alc_spec *spec = codec->spec;
- int i;
-
- for (i = 0; i < spec->autocfg.line_outs; i++) {
- hda_nid_t nid = spec->autocfg.line_out_pins[i];
- int pin_type = get_pin_type(spec->autocfg.line_out_type);
- if (nid)
- alc861_auto_set_output_and_unmute(codec, nid, pin_type,
- spec->multiout.dac_nids[i]);
- }
-}
-
-static void alc861_auto_init_hp_out(struct hda_codec *codec)
-{
- struct alc_spec *spec = codec->spec;
-
- if (spec->autocfg.hp_outs)
- alc861_auto_set_output_and_unmute(codec,
- spec->autocfg.hp_pins[0],
- PIN_HP,
- spec->multiout.hp_nid);
- if (spec->autocfg.speaker_outs)
- alc861_auto_set_output_and_unmute(codec,
- spec->autocfg.speaker_pins[0],
- PIN_OUT,
- spec->multiout.dac_nids[0]);
-}
-
-static void alc861_auto_init_analog_input(struct hda_codec *codec)
-{
- struct alc_spec *spec = codec->spec;
- struct auto_pin_cfg *cfg = &spec->autocfg;
- int i;
-
- for (i = 0; i < cfg->num_inputs; i++) {
- hda_nid_t nid = cfg->inputs[i].pin;
- if (nid >= 0x0c && nid <= 0x11)
- alc_set_input_pin(codec, nid, cfg->inputs[i].type);
- }
-}
-
-/* parse the BIOS configuration and set up the alc_spec */
-/* return 1 if successful, 0 if the proper config is not found,
- * or a negative error code
- */
-static int alc861_parse_auto_config(struct hda_codec *codec)
-{
- struct alc_spec *spec = codec->spec;
- int err;
- static hda_nid_t alc861_ignore[] = { 0x1d, 0 };
-
- err = snd_hda_parse_pin_def_config(codec, &spec->autocfg,
- alc861_ignore);
- if (err < 0)
- return err;
- if (!spec->autocfg.line_outs)
- return 0; /* can't find valid BIOS pin config */
-
- err = alc861_auto_fill_dac_nids(codec, &spec->autocfg);
- if (err < 0)
- return err;
- err = alc861_auto_create_multi_out_ctls(codec, &spec->autocfg);
- if (err < 0)
- return err;
- err = alc861_auto_create_hp_ctls(codec, spec->autocfg.hp_pins[0]);
- if (err < 0)
- return err;
- err = alc861_auto_create_input_ctls(codec, &spec->autocfg);
- if (err < 0)
- return err;
-
- spec->multiout.max_channels = spec->multiout.num_dacs * 2;
-
- alc_auto_parse_digital(codec);
-
- if (spec->kctls.list)
- add_mixer(spec, spec->kctls.list);
-
- add_verb(spec, alc861_auto_init_verbs);
-
- spec->num_mux_defs = 1;
- spec->input_mux = &spec->private_imux[0];
-
- spec->adc_nids = alc861_adc_nids;
- spec->num_adc_nids = ARRAY_SIZE(alc861_adc_nids);
- set_capture_mixer(codec);
-
- alc_ssid_check(codec, 0x0e, 0x0f, 0x0b, 0);
-
- return 1;
+ val = snd_hda_codec_get_pin_target(codec, 0x0f);
+ if (!(val & (AC_PINCTL_IN_EN | AC_PINCTL_OUT_EN)))
+ val |= AC_PINCTL_IN_EN;
+ val |= AC_PINCTL_VREF_50;
+ snd_hda_set_pin_ctl(codec, 0x0f, val);
+ spec->gen.keep_vref_in_automute = 1;
}
-/* additional initialization for auto-configuration model */
-static void alc861_auto_init(struct hda_codec *codec)
+/* suppress the jack-detection */
+static void alc_fixup_no_jack_detect(struct hda_codec *codec,
+ const struct hda_fixup *fix, int action)
{
- struct alc_spec *spec = codec->spec;
- alc861_auto_init_multi_out(codec);
- alc861_auto_init_hp_out(codec);
- alc861_auto_init_analog_input(codec);
- alc_auto_init_digital(codec);
- if (spec->unsol_event)
- alc_inithook(codec);
+ if (action == HDA_FIXUP_ACT_PRE_PROBE)
+ codec->no_jack_detect = 1;
}
-#ifdef CONFIG_SND_HDA_POWER_SAVE
-static struct hda_amp_list alc861_loopbacks[] = {
- { 0x15, HDA_INPUT, 0 },
- { 0x15, HDA_INPUT, 1 },
- { 0x15, HDA_INPUT, 2 },
- { 0x15, HDA_INPUT, 3 },
- { } /* end */
-};
-#endif
-
-
-/*
- * configuration and preset
- */
-static const char * const alc861_models[ALC861_MODEL_LAST] = {
- [ALC861_3ST] = "3stack",
- [ALC660_3ST] = "3stack-660",
- [ALC861_3ST_DIG] = "3stack-dig",
- [ALC861_6ST_DIG] = "6stack-dig",
- [ALC861_UNIWILL_M31] = "uniwill-m31",
- [ALC861_TOSHIBA] = "toshiba",
- [ALC861_ASUS] = "asus",
- [ALC861_ASUS_LAPTOP] = "asus-laptop",
- [ALC861_AUTO] = "auto",
-};
-
-static struct snd_pci_quirk alc861_cfg_tbl[] = {
- SND_PCI_QUIRK(0x1043, 0x1205, "ASUS W7J", ALC861_3ST),
- SND_PCI_QUIRK(0x1043, 0x1335, "ASUS F2/3", ALC861_ASUS_LAPTOP),
- SND_PCI_QUIRK(0x1043, 0x1338, "ASUS F2/3", ALC861_ASUS_LAPTOP),
- SND_PCI_QUIRK(0x1043, 0x1393, "ASUS", ALC861_ASUS),
- SND_PCI_QUIRK(0x1043, 0x13d7, "ASUS A9rp", ALC861_ASUS_LAPTOP),
- SND_PCI_QUIRK(0x1043, 0x81cb, "ASUS P1-AH2", ALC861_3ST_DIG),
- SND_PCI_QUIRK(0x1179, 0xff00, "Toshiba", ALC861_TOSHIBA),
- /* FIXME: the entry below breaks Toshiba A100 (model=auto works!)
- * Any other models that need this preset?
- */
- /* SND_PCI_QUIRK(0x1179, 0xff10, "Toshiba", ALC861_TOSHIBA), */
- SND_PCI_QUIRK(0x1462, 0x7254, "HP dx2200 (MSI MS-7254)", ALC861_3ST),
- SND_PCI_QUIRK(0x1462, 0x7297, "HP dx2250 (MSI MS-7297)", ALC861_3ST),
- SND_PCI_QUIRK(0x1584, 0x2b01, "Uniwill X40AIx", ALC861_UNIWILL_M31),
- SND_PCI_QUIRK(0x1584, 0x9072, "Uniwill m31", ALC861_UNIWILL_M31),
- SND_PCI_QUIRK(0x1584, 0x9075, "Airis Praxis N1212", ALC861_ASUS_LAPTOP),
- /* FIXME: the below seems conflict */
- /* SND_PCI_QUIRK(0x1584, 0x9075, "Uniwill", ALC861_UNIWILL_M31), */
- SND_PCI_QUIRK(0x1849, 0x0660, "Asrock 939SLI32", ALC660_3ST),
- SND_PCI_QUIRK(0x8086, 0xd600, "Intel", ALC861_3ST),
- {}
-};
-
-static struct alc_config_preset alc861_presets[] = {
- [ALC861_3ST] = {
- .mixers = { alc861_3ST_mixer },
- .init_verbs = { alc861_threestack_init_verbs },
- .num_dacs = ARRAY_SIZE(alc861_dac_nids),
- .dac_nids = alc861_dac_nids,
- .num_channel_mode = ARRAY_SIZE(alc861_threestack_modes),
- .channel_mode = alc861_threestack_modes,
- .need_dac_fix = 1,
- .num_adc_nids = ARRAY_SIZE(alc861_adc_nids),
- .adc_nids = alc861_adc_nids,
- .input_mux = &alc861_capture_source,
- },
- [ALC861_3ST_DIG] = {
- .mixers = { alc861_base_mixer },
- .init_verbs = { alc861_threestack_init_verbs },
- .num_dacs = ARRAY_SIZE(alc861_dac_nids),
- .dac_nids = alc861_dac_nids,
- .dig_out_nid = ALC861_DIGOUT_NID,
- .num_channel_mode = ARRAY_SIZE(alc861_threestack_modes),
- .channel_mode = alc861_threestack_modes,
- .need_dac_fix = 1,
- .num_adc_nids = ARRAY_SIZE(alc861_adc_nids),
- .adc_nids = alc861_adc_nids,
- .input_mux = &alc861_capture_source,
- },
- [ALC861_6ST_DIG] = {
- .mixers = { alc861_base_mixer },
- .init_verbs = { alc861_base_init_verbs },
- .num_dacs = ARRAY_SIZE(alc861_dac_nids),
- .dac_nids = alc861_dac_nids,
- .dig_out_nid = ALC861_DIGOUT_NID,
- .num_channel_mode = ARRAY_SIZE(alc861_8ch_modes),
- .channel_mode = alc861_8ch_modes,
- .num_adc_nids = ARRAY_SIZE(alc861_adc_nids),
- .adc_nids = alc861_adc_nids,
- .input_mux = &alc861_capture_source,
- },
- [ALC660_3ST] = {
- .mixers = { alc861_3ST_mixer },
- .init_verbs = { alc861_threestack_init_verbs },
- .num_dacs = ARRAY_SIZE(alc660_dac_nids),
- .dac_nids = alc660_dac_nids,
- .num_channel_mode = ARRAY_SIZE(alc861_threestack_modes),
- .channel_mode = alc861_threestack_modes,
- .need_dac_fix = 1,
- .num_adc_nids = ARRAY_SIZE(alc861_adc_nids),
- .adc_nids = alc861_adc_nids,
- .input_mux = &alc861_capture_source,
- },
- [ALC861_UNIWILL_M31] = {
- .mixers = { alc861_uniwill_m31_mixer },
- .init_verbs = { alc861_uniwill_m31_init_verbs },
- .num_dacs = ARRAY_SIZE(alc861_dac_nids),
- .dac_nids = alc861_dac_nids,
- .dig_out_nid = ALC861_DIGOUT_NID,
- .num_channel_mode = ARRAY_SIZE(alc861_uniwill_m31_modes),
- .channel_mode = alc861_uniwill_m31_modes,
- .need_dac_fix = 1,
- .num_adc_nids = ARRAY_SIZE(alc861_adc_nids),
- .adc_nids = alc861_adc_nids,
- .input_mux = &alc861_capture_source,
- },
- [ALC861_TOSHIBA] = {
- .mixers = { alc861_toshiba_mixer },
- .init_verbs = { alc861_base_init_verbs,
- alc861_toshiba_init_verbs },
- .num_dacs = ARRAY_SIZE(alc861_dac_nids),
- .dac_nids = alc861_dac_nids,
- .num_channel_mode = ARRAY_SIZE(alc883_3ST_2ch_modes),
- .channel_mode = alc883_3ST_2ch_modes,
- .num_adc_nids = ARRAY_SIZE(alc861_adc_nids),
- .adc_nids = alc861_adc_nids,
- .input_mux = &alc861_capture_source,
- .unsol_event = alc861_toshiba_unsol_event,
- .init_hook = alc861_toshiba_automute,
- },
- [ALC861_ASUS] = {
- .mixers = { alc861_asus_mixer },
- .init_verbs = { alc861_asus_init_verbs },
- .num_dacs = ARRAY_SIZE(alc861_dac_nids),
- .dac_nids = alc861_dac_nids,
- .dig_out_nid = ALC861_DIGOUT_NID,
- .num_channel_mode = ARRAY_SIZE(alc861_asus_modes),
- .channel_mode = alc861_asus_modes,
- .need_dac_fix = 1,
- .hp_nid = 0x06,
- .num_adc_nids = ARRAY_SIZE(alc861_adc_nids),
- .adc_nids = alc861_adc_nids,
- .input_mux = &alc861_capture_source,
- },
- [ALC861_ASUS_LAPTOP] = {
- .mixers = { alc861_toshiba_mixer, alc861_asus_laptop_mixer },
- .init_verbs = { alc861_asus_init_verbs,
- alc861_asus_laptop_init_verbs },
- .num_dacs = ARRAY_SIZE(alc861_dac_nids),
- .dac_nids = alc861_dac_nids,
- .dig_out_nid = ALC861_DIGOUT_NID,
- .num_channel_mode = ARRAY_SIZE(alc883_3ST_2ch_modes),
- .channel_mode = alc883_3ST_2ch_modes,
- .need_dac_fix = 1,
- .num_adc_nids = ARRAY_SIZE(alc861_adc_nids),
- .adc_nids = alc861_adc_nids,
- .input_mux = &alc861_capture_source,
- },
-};
-
-/* Pin config fixes */
-enum {
- PINFIX_FSC_AMILO_PI1505,
-};
-
-static const struct alc_fixup alc861_fixups[] = {
- [PINFIX_FSC_AMILO_PI1505] = {
- .type = ALC_FIXUP_PINS,
- .v.pins = (const struct alc_pincfg[]) {
+static const struct hda_fixup alc861_fixups[] = {
+ [ALC861_FIXUP_FSC_AMILO_PI1505] = {
+ .type = HDA_FIXUP_PINS,
+ .v.pins = (const struct hda_pintbl[]) {
{ 0x0b, 0x0221101f }, /* HP */
{ 0x0f, 0x90170310 }, /* speaker */
{ }
}
},
+ [ALC861_FIXUP_AMP_VREF_0F] = {
+ .type = HDA_FIXUP_FUNC,
+ .v.func = alc861_fixup_asus_amp_vref_0f,
+ },
+ [ALC861_FIXUP_NO_JACK_DETECT] = {
+ .type = HDA_FIXUP_FUNC,
+ .v.func = alc_fixup_no_jack_detect,
+ },
+ [ALC861_FIXUP_ASUS_A6RP] = {
+ .type = HDA_FIXUP_FUNC,
+ .v.func = alc861_fixup_asus_amp_vref_0f,
+ .chained = true,
+ .chain_id = ALC861_FIXUP_NO_JACK_DETECT,
+ },
+ [ALC660_FIXUP_ASUS_W7J] = {
+ .type = HDA_FIXUP_VERBS,
+ .v.verbs = (const struct hda_verb[]) {
+ /* ASUS W7J needs a magic pin setup on unused NID 0x10
+ * for enabling outputs
+ */
+ {0x10, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x24},
+ { }
+ },
+ }
};
-static struct snd_pci_quirk alc861_fixup_tbl[] = {
- SND_PCI_QUIRK(0x1734, 0x10c7, "FSC Amilo Pi1505", PINFIX_FSC_AMILO_PI1505),
+static const struct snd_pci_quirk alc861_fixup_tbl[] = {
+ SND_PCI_QUIRK(0x1043, 0x1253, "ASUS W7J", ALC660_FIXUP_ASUS_W7J),
+ SND_PCI_QUIRK(0x1043, 0x1263, "ASUS Z35HL", ALC660_FIXUP_ASUS_W7J),
+ SND_PCI_QUIRK(0x1043, 0x1393, "ASUS A6Rp", ALC861_FIXUP_ASUS_A6RP),
+ SND_PCI_QUIRK_VENDOR(0x1043, "ASUS laptop", ALC861_FIXUP_AMP_VREF_0F),
+ SND_PCI_QUIRK(0x1462, 0x7254, "HP DX2200", ALC861_FIXUP_NO_JACK_DETECT),
+ SND_PCI_QUIRK(0x1584, 0x2b01, "Haier W18", ALC861_FIXUP_AMP_VREF_0F),
+ SND_PCI_QUIRK(0x1584, 0x0000, "Uniwill ECS M31EI", ALC861_FIXUP_AMP_VREF_0F),
+ SND_PCI_QUIRK(0x1734, 0x10c7, "FSC Amilo Pi1505", ALC861_FIXUP_FSC_AMILO_PI1505),
{}
};
+/*
+ */
static int patch_alc861(struct hda_codec *codec)
{
struct alc_spec *spec;
- int board_config;
int err;
- spec = kzalloc(sizeof(*spec), GFP_KERNEL);
- if (spec == NULL)
- return -ENOMEM;
-
- codec->spec = spec;
-
- board_config = snd_hda_check_board_config(codec, ALC861_MODEL_LAST,
- alc861_models,
- alc861_cfg_tbl);
-
- if (board_config < 0) {
- printk(KERN_INFO "hda_codec: %s: BIOS auto-probing.\n",
- codec->chip_name);
- board_config = ALC861_AUTO;
- }
-
- if (board_config == ALC861_AUTO) {
- alc_pick_fixup(codec, NULL, alc861_fixup_tbl, alc861_fixups);
- alc_apply_fixup(codec, ALC_FIXUP_ACT_PRE_PROBE);
- }
-
- if (board_config == ALC861_AUTO) {
- /* automatic parse from the BIOS config */
- err = alc861_parse_auto_config(codec);
- if (err < 0) {
- alc_free(codec);
- return err;
- } else if (!err) {
- printk(KERN_INFO
- "hda_codec: Cannot set up configuration "
- "from BIOS. Using base mode...\n");
- board_config = ALC861_3ST_DIG;
- }
- }
-
- err = snd_hda_attach_beep_device(codec, 0x23);
- if (err < 0) {
- alc_free(codec);
+ err = alc_alloc_spec(codec, 0x15);
+ if (err < 0)
return err;
- }
-
- if (board_config != ALC861_AUTO)
- setup_preset(codec, &alc861_presets[board_config]);
- spec->stream_analog_playback = &alc861_pcm_analog_playback;
- spec->stream_analog_capture = &alc861_pcm_analog_capture;
+ spec = codec->spec;
+ spec->gen.beep_nid = 0x23;
- spec->stream_digital_playback = &alc861_pcm_digital_playback;
- spec->stream_digital_capture = &alc861_pcm_digital_capture;
+ snd_hda_pick_fixup(codec, NULL, alc861_fixup_tbl, alc861_fixups);
+ snd_hda_apply_fixup(codec, HDA_FIXUP_ACT_PRE_PROBE);
- if (!spec->cap_mixer)
- set_capture_mixer(codec);
- set_beep_amp(spec, 0x23, 0, HDA_OUTPUT);
-
- spec->vmaster_nid = 0x03;
+ /* automatic parse from the BIOS config */
+ err = alc861_parse_auto_config(codec);
+ if (err < 0)
+ goto error;
- alc_apply_fixup(codec, ALC_FIXUP_ACT_PROBE);
+ if (!spec->gen.no_analog)
+ set_beep_amp(spec, 0x23, 0, HDA_OUTPUT);
codec->patch_ops = alc_patch_ops;
- if (board_config == ALC861_AUTO) {
- spec->init_hook = alc861_auto_init;
-#ifdef CONFIG_SND_HDA_POWER_SAVE
- spec->power_hook = alc_power_eapd;
-#endif
- }
-#ifdef CONFIG_SND_HDA_POWER_SAVE
- if (!spec->loopback.amplist)
- spec->loopback.amplist = alc861_loopbacks;
+#ifdef CONFIG_PM
+ spec->power_hook = alc_power_eapd;
#endif
+ snd_hda_apply_fixup(codec, HDA_FIXUP_ACT_PROBE);
+
return 0;
+
+ error:
+ alc_free(codec);
+ return err;
}
/*
@@ -16454,968 +5399,88 @@ static int patch_alc861(struct hda_codec *codec)
*
* In addition, an independent DAC
*/
-#define ALC861VD_DIGOUT_NID 0x06
-
-static hda_nid_t alc861vd_dac_nids[4] = {
- /* front, surr, clfe, side surr */
- 0x02, 0x03, 0x04, 0x05
-};
-
-/* dac_nids for ALC660vd are in a different order - according to
- * Realtek's driver.
- * This should probably result in a different mixer for 6stack models
- * of ALC660vd codecs, but for now there is only 3stack mixer
- * - and it is the same as in 861vd.
- * adc_nids in ALC660vd are (is) the same as in 861vd
- */
-static hda_nid_t alc660vd_dac_nids[3] = {
- /* front, rear, clfe, rear_surr */
- 0x02, 0x04, 0x03
-};
-
-static hda_nid_t alc861vd_adc_nids[1] = {
- /* ADC0 */
- 0x09,
-};
-
-static hda_nid_t alc861vd_capsrc_nids[1] = { 0x22 };
-
-/* input MUX */
-/* FIXME: should be a matrix-type input source selection */
-static struct hda_input_mux alc861vd_capture_source = {
- .num_items = 4,
- .items = {
- { "Mic", 0x0 },
- { "Front Mic", 0x1 },
- { "Line", 0x2 },
- { "CD", 0x4 },
- },
-};
-
-static struct hda_input_mux alc861vd_dallas_capture_source = {
- .num_items = 2,
- .items = {
- { "Mic", 0x0 },
- { "Internal Mic", 0x1 },
- },
-};
-
-static struct hda_input_mux alc861vd_hp_capture_source = {
- .num_items = 2,
- .items = {
- { "Front Mic", 0x0 },
- { "ATAPI Mic", 0x1 },
- },
-};
-
-/*
- * 2ch mode
- */
-static struct hda_channel_mode alc861vd_3stack_2ch_modes[1] = {
- { 2, NULL }
-};
-
-/*
- * 6ch mode
- */
-static struct hda_verb alc861vd_6stack_ch6_init[] = {
- { 0x17, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x00 },
- { 0x16, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT },
- { 0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT },
- { 0x14, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT },
- { } /* end */
-};
-
-/*
- * 8ch mode
- */
-static struct hda_verb alc861vd_6stack_ch8_init[] = {
- { 0x17, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT },
- { 0x16, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT },
- { 0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT },
- { 0x14, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT },
- { } /* end */
-};
-
-static struct hda_channel_mode alc861vd_6stack_modes[2] = {
- { 6, alc861vd_6stack_ch6_init },
- { 8, alc861vd_6stack_ch8_init },
-};
-
-static struct snd_kcontrol_new alc861vd_chmode_mixer[] = {
- {
- .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
- .name = "Channel Mode",
- .info = alc_ch_mode_info,
- .get = alc_ch_mode_get,
- .put = alc_ch_mode_put,
- },
- { } /* end */
-};
-
-/* Pin assignment: Front=0x14, Rear=0x15, CLFE=0x16, Side=0x17
- * Mic=0x18, Front Mic=0x19, Line-In=0x1a, HP=0x1b
- */
-static struct snd_kcontrol_new alc861vd_6st_mixer[] = {
- HDA_CODEC_VOLUME("Front Playback Volume", 0x02, 0x0, HDA_OUTPUT),
- HDA_BIND_MUTE("Front Playback Switch", 0x0c, 2, HDA_INPUT),
-
- HDA_CODEC_VOLUME("Surround Playback Volume", 0x03, 0x0, HDA_OUTPUT),
- HDA_BIND_MUTE("Surround Playback Switch", 0x0d, 2, HDA_INPUT),
-
- HDA_CODEC_VOLUME_MONO("Center Playback Volume", 0x04, 1, 0x0,
- HDA_OUTPUT),
- HDA_CODEC_VOLUME_MONO("LFE Playback Volume", 0x04, 2, 0x0,
- HDA_OUTPUT),
- HDA_BIND_MUTE_MONO("Center Playback Switch", 0x0e, 1, 2, HDA_INPUT),
- HDA_BIND_MUTE_MONO("LFE Playback Switch", 0x0e, 2, 2, HDA_INPUT),
-
- HDA_CODEC_VOLUME("Side Playback Volume", 0x05, 0x0, HDA_OUTPUT),
- HDA_BIND_MUTE("Side Playback Switch", 0x0f, 2, HDA_INPUT),
-
- HDA_CODEC_MUTE("Headphone Playback Switch", 0x1b, 0x0, HDA_OUTPUT),
-
- HDA_CODEC_VOLUME("Mic Boost Volume", 0x18, 0, HDA_INPUT),
- HDA_CODEC_VOLUME("Mic Playback Volume", 0x0b, 0x0, HDA_INPUT),
- HDA_CODEC_MUTE("Mic Playback Switch", 0x0b, 0x0, HDA_INPUT),
-
- HDA_CODEC_VOLUME("Front Mic Boost Volume", 0x19, 0, HDA_INPUT),
- HDA_CODEC_VOLUME("Front Mic Playback Volume", 0x0b, 0x1, HDA_INPUT),
- HDA_CODEC_MUTE("Front Mic Playback Switch", 0x0b, 0x1, HDA_INPUT),
-
- HDA_CODEC_VOLUME("Line Playback Volume", 0x0b, 0x02, HDA_INPUT),
- HDA_CODEC_MUTE("Line Playback Switch", 0x0b, 0x02, HDA_INPUT),
-
- HDA_CODEC_VOLUME("CD Playback Volume", 0x0b, 0x04, HDA_INPUT),
- HDA_CODEC_MUTE("CD Playback Switch", 0x0b, 0x04, HDA_INPUT),
-
- { } /* end */
-};
-
-static struct snd_kcontrol_new alc861vd_3st_mixer[] = {
- HDA_CODEC_VOLUME("Front Playback Volume", 0x02, 0x0, HDA_OUTPUT),
- HDA_BIND_MUTE("Front Playback Switch", 0x0c, 2, HDA_INPUT),
-
- HDA_CODEC_MUTE("Headphone Playback Switch", 0x1b, 0x0, HDA_OUTPUT),
-
- HDA_CODEC_VOLUME("Mic Boost Volume", 0x18, 0, HDA_INPUT),
- HDA_CODEC_VOLUME("Mic Playback Volume", 0x0b, 0x0, HDA_INPUT),
- HDA_CODEC_MUTE("Mic Playback Switch", 0x0b, 0x0, HDA_INPUT),
-
- HDA_CODEC_VOLUME("Front Mic Boost Volume", 0x19, 0, HDA_INPUT),
- HDA_CODEC_VOLUME("Front Mic Playback Volume", 0x0b, 0x1, HDA_INPUT),
- HDA_CODEC_MUTE("Front Mic Playback Switch", 0x0b, 0x1, HDA_INPUT),
-
- HDA_CODEC_VOLUME("Line Playback Volume", 0x0b, 0x02, HDA_INPUT),
- HDA_CODEC_MUTE("Line Playback Switch", 0x0b, 0x02, HDA_INPUT),
-
- HDA_CODEC_VOLUME("CD Playback Volume", 0x0b, 0x04, HDA_INPUT),
- HDA_CODEC_MUTE("CD Playback Switch", 0x0b, 0x04, HDA_INPUT),
-
- { } /* end */
-};
-
-static struct snd_kcontrol_new alc861vd_lenovo_mixer[] = {
- HDA_CODEC_VOLUME("Front Playback Volume", 0x02, 0x0, HDA_OUTPUT),
- /*HDA_BIND_MUTE("Front Playback Switch", 0x0c, 2, HDA_INPUT),*/
- HDA_CODEC_MUTE("Front Playback Switch", 0x14, 0x0, HDA_OUTPUT),
-
- HDA_CODEC_MUTE("Headphone Playback Switch", 0x1b, 0x0, HDA_OUTPUT),
-
- HDA_CODEC_VOLUME("Mic Boost Volume", 0x18, 0, HDA_INPUT),
- HDA_CODEC_VOLUME("Mic Playback Volume", 0x0b, 0x0, HDA_INPUT),
- HDA_CODEC_MUTE("Mic Playback Switch", 0x0b, 0x0, HDA_INPUT),
-
- HDA_CODEC_VOLUME("Front Mic Boost Volume", 0x19, 0, HDA_INPUT),
- HDA_CODEC_VOLUME("Front Mic Playback Volume", 0x0b, 0x1, HDA_INPUT),
- HDA_CODEC_MUTE("Front Mic Playback Switch", 0x0b, 0x1, HDA_INPUT),
-
- HDA_CODEC_VOLUME("CD Playback Volume", 0x0b, 0x04, HDA_INPUT),
- HDA_CODEC_MUTE("CD Playback Switch", 0x0b, 0x04, HDA_INPUT),
-
- { } /* end */
-};
-
-/* Pin assignment: Speaker=0x14, HP = 0x15,
- * Mic=0x18, Internal Mic = 0x19, CD = 0x1c, PC Beep = 0x1d
- */
-static struct snd_kcontrol_new alc861vd_dallas_mixer[] = {
- HDA_CODEC_VOLUME("Speaker Playback Volume", 0x02, 0x0, HDA_OUTPUT),
- HDA_BIND_MUTE("Speaker Playback Switch", 0x0c, 2, HDA_INPUT),
- HDA_CODEC_VOLUME("Headphone Playback Volume", 0x03, 0x0, HDA_OUTPUT),
- HDA_BIND_MUTE("Headphone Playback Switch", 0x0d, 2, HDA_INPUT),
- HDA_CODEC_VOLUME("Mic Boost Volume", 0x18, 0, HDA_INPUT),
- HDA_CODEC_VOLUME("Mic Playback Volume", 0x0b, 0x0, HDA_INPUT),
- HDA_CODEC_MUTE("Mic Playback Switch", 0x0b, 0x0, HDA_INPUT),
- HDA_CODEC_VOLUME("Internal Mic Boost Volume", 0x19, 0, HDA_INPUT),
- HDA_CODEC_VOLUME("Internal Mic Playback Volume", 0x0b, 0x1, HDA_INPUT),
- HDA_CODEC_MUTE("Internal Mic Playback Switch", 0x0b, 0x1, HDA_INPUT),
- { } /* end */
-};
-
-/* Pin assignment: Speaker=0x14, Line-out = 0x15,
- * Front Mic=0x18, ATAPI Mic = 0x19,
- */
-static struct snd_kcontrol_new alc861vd_hp_mixer[] = {
- HDA_CODEC_VOLUME("Front Playback Volume", 0x02, 0x0, HDA_OUTPUT),
- HDA_BIND_MUTE("Front Playback Switch", 0x0c, 2, HDA_INPUT),
- HDA_CODEC_VOLUME("Headphone Playback Volume", 0x03, 0x0, HDA_OUTPUT),
- HDA_BIND_MUTE("Headphone Playback Switch", 0x0d, 2, HDA_INPUT),
- HDA_CODEC_VOLUME("Front Mic Playback Volume", 0x0b, 0x0, HDA_INPUT),
- HDA_CODEC_MUTE("Front Mic Playback Switch", 0x0b, 0x0, HDA_INPUT),
- HDA_CODEC_VOLUME("ATAPI Mic Playback Volume", 0x0b, 0x1, HDA_INPUT),
- HDA_CODEC_MUTE("ATAPI Mic Playback Switch", 0x0b, 0x1, HDA_INPUT),
-
- { } /* end */
-};
-
-/*
- * generic initialization of ADC, input mixers and output mixers
- */
-static struct hda_verb alc861vd_volume_init_verbs[] = {
- /*
- * Unmute ADC0 and set the default input to mic-in
- */
- {0x09, AC_VERB_SET_CONNECT_SEL, 0x00},
- {0x09, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
-
- /* Unmute input amps (CD, Line In, Mic 1 & Mic 2) of
- * the analog-loopback mixer widget
- */
- /* Amp Indices: Mic1 = 0, Mic2 = 1, Line1 = 2, Line2 = 3, CD = 4 */
- {0x0b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
- {0x0b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
- {0x0b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(2)},
- {0x0b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(3)},
- {0x0b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(4)},
-
- /* Capture mixer: unmute Mic, F-Mic, Line, CD inputs */
- {0x22, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
- {0x22, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
- {0x22, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(2)},
- {0x22, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(4)},
-
- /*
- * Set up output mixers (0x02 - 0x05)
- */
- /* set vol=0 to output mixers */
- {0x02, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
- {0x03, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
- {0x04, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
- {0x05, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
-
- /* set up input amps for analog loopback */
- /* Amp Indices: DAC = 0, mixer = 1 */
- {0x0c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
- {0x0c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
- {0x0d, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
- {0x0d, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
- {0x0e, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
- {0x0e, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
- {0x0f, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
- {0x0f, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
-
- { }
-};
-
-/*
- * 3-stack pin configuration:
- * front = 0x14, mic/clfe = 0x18, HP = 0x19, line/surr = 0x1a, f-mic = 0x1b
- */
-static struct hda_verb alc861vd_3stack_init_verbs[] = {
- /*
- * Set pin mode and muting
- */
- /* set front pin widgets 0x14 for output */
- {0x14, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
- {0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
- {0x14, AC_VERB_SET_CONNECT_SEL, 0x00},
-
- /* Mic (rear) pin: input vref at 80% */
- {0x18, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80},
- {0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
- /* Front Mic pin: input vref at 80% */
- {0x19, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80},
- {0x19, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
- /* Line In pin: input */
- {0x1a, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN},
- {0x1a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
- /* Line-2 In: Headphone output (output 0 - 0x0c) */
- {0x1b, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP},
- {0x1b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
- {0x1b, AC_VERB_SET_CONNECT_SEL, 0x00},
- /* CD pin widget for input */
- {0x1c, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN},
-
- { }
-};
-
-/*
- * 6-stack pin configuration:
- */
-static struct hda_verb alc861vd_6stack_init_verbs[] = {
- /*
- * Set pin mode and muting
- */
- /* set front pin widgets 0x14 for output */
- {0x14, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
- {0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
- {0x14, AC_VERB_SET_CONNECT_SEL, 0x00},
-
- /* Rear Pin: output 1 (0x0d) */
- {0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
- {0x15, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
- {0x15, AC_VERB_SET_CONNECT_SEL, 0x01},
- /* CLFE Pin: output 2 (0x0e) */
- {0x16, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
- {0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
- {0x16, AC_VERB_SET_CONNECT_SEL, 0x02},
- /* Side Pin: output 3 (0x0f) */
- {0x17, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
- {0x17, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
- {0x17, AC_VERB_SET_CONNECT_SEL, 0x03},
-
- /* Mic (rear) pin: input vref at 80% */
- {0x18, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80},
- {0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
- /* Front Mic pin: input vref at 80% */
- {0x19, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80},
- {0x19, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
- /* Line In pin: input */
- {0x1a, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN},
- {0x1a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
- /* Line-2 In: Headphone output (output 0 - 0x0c) */
- {0x1b, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP},
- {0x1b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
- {0x1b, AC_VERB_SET_CONNECT_SEL, 0x00},
- /* CD pin widget for input */
- {0x1c, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN},
-
- { }
-};
-
-static struct hda_verb alc861vd_eapd_verbs[] = {
- {0x14, AC_VERB_SET_EAPD_BTLENABLE, 2},
- { }
-};
-
-static struct hda_verb alc660vd_eapd_verbs[] = {
- {0x14, AC_VERB_SET_EAPD_BTLENABLE, 2},
- {0x15, AC_VERB_SET_EAPD_BTLENABLE, 2},
- { }
-};
-
-static struct hda_verb alc861vd_lenovo_unsol_verbs[] = {
- {0x0c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
- {0x0c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
- {0x0b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(5)},
- {0x1b, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | ALC880_HP_EVENT},
- {0x18, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | ALC880_MIC_EVENT},
- {}
-};
-
-static void alc861vd_lenovo_setup(struct hda_codec *codec)
-{
- struct alc_spec *spec = codec->spec;
- spec->autocfg.hp_pins[0] = 0x1b;
- spec->autocfg.speaker_pins[0] = 0x14;
-}
-
-static void alc861vd_lenovo_init_hook(struct hda_codec *codec)
+static int alc861vd_parse_auto_config(struct hda_codec *codec)
{
- alc_automute_amp(codec);
- alc88x_simple_mic_automute(codec);
+ static const hda_nid_t alc861vd_ignore[] = { 0x1d, 0 };
+ static const hda_nid_t alc861vd_ssids[] = { 0x15, 0x1b, 0x14, 0 };
+ return alc_parse_auto_config(codec, alc861vd_ignore, alc861vd_ssids);
}
-static void alc861vd_lenovo_unsol_event(struct hda_codec *codec,
- unsigned int res)
-{
- switch (res >> 26) {
- case ALC880_MIC_EVENT:
- alc88x_simple_mic_automute(codec);
- break;
- default:
- alc_automute_amp_unsol_event(codec, res);
- break;
- }
-}
-
-static struct hda_verb alc861vd_dallas_verbs[] = {
- {0x02, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
- {0x03, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
- {0x04, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
- {0x05, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
-
- {0x0c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
- {0x0c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
- {0x0d, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
- {0x0d, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
- {0x0e, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
- {0x0e, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
- {0x0f, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
- {0x0f, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
-
- {0x14, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
- {0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
- {0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
- {0x15, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
- {0x16, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
- {0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
- {0x17, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
- {0x17, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
-
- {0x18, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF50},
- {0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
- {0x19, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF50},
- {0x19, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
- {0x1a, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN},
- {0x1b, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN},
- {0x1c, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN},
- {0x1d, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN},
-
- {0x22, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
- {0x22, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(3)},
- {0x22, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(2)},
- {0x22, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(4)},
-
- {0x09, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
- {0x09, AC_VERB_SET_CONNECT_SEL, 0x00},
- {0x15, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | ALC880_HP_EVENT},
-
- { } /* end */
-};
-
-/* toggle speaker-output according to the hp-jack state */
-static void alc861vd_dallas_setup(struct hda_codec *codec)
-{
- struct alc_spec *spec = codec->spec;
-
- spec->autocfg.hp_pins[0] = 0x15;
- spec->autocfg.speaker_pins[0] = 0x14;
-}
-
-#ifdef CONFIG_SND_HDA_POWER_SAVE
-#define alc861vd_loopbacks alc880_loopbacks
-#endif
-
-/* pcm configuration: identical with ALC880 */
-#define alc861vd_pcm_analog_playback alc880_pcm_analog_playback
-#define alc861vd_pcm_analog_capture alc880_pcm_analog_capture
-#define alc861vd_pcm_digital_playback alc880_pcm_digital_playback
-#define alc861vd_pcm_digital_capture alc880_pcm_digital_capture
-
-/*
- * configuration and preset
- */
-static const char * const alc861vd_models[ALC861VD_MODEL_LAST] = {
- [ALC660VD_3ST] = "3stack-660",
- [ALC660VD_3ST_DIG] = "3stack-660-digout",
- [ALC660VD_ASUS_V1S] = "asus-v1s",
- [ALC861VD_3ST] = "3stack",
- [ALC861VD_3ST_DIG] = "3stack-digout",
- [ALC861VD_6ST_DIG] = "6stack-digout",
- [ALC861VD_LENOVO] = "lenovo",
- [ALC861VD_DALLAS] = "dallas",
- [ALC861VD_HP] = "hp",
- [ALC861VD_AUTO] = "auto",
-};
-
-static struct snd_pci_quirk alc861vd_cfg_tbl[] = {
- SND_PCI_QUIRK(0x1019, 0xa88d, "Realtek ALC660 demo", ALC660VD_3ST),
- SND_PCI_QUIRK(0x103c, 0x30bf, "HP TX1000", ALC861VD_HP),
- SND_PCI_QUIRK(0x1043, 0x12e2, "Asus z35m", ALC660VD_3ST),
- /*SND_PCI_QUIRK(0x1043, 0x1339, "Asus G1", ALC660VD_3ST),*/ /* auto */
- SND_PCI_QUIRK(0x1043, 0x1633, "Asus V1Sn", ALC660VD_ASUS_V1S),
- SND_PCI_QUIRK(0x1043, 0x81e7, "ASUS", ALC660VD_3ST_DIG),
- SND_PCI_QUIRK(0x10de, 0x03f0, "Realtek ALC660 demo", ALC660VD_3ST),
- SND_PCI_QUIRK(0x1179, 0xff00, "Toshiba A135", ALC861VD_LENOVO),
- /*SND_PCI_QUIRK(0x1179, 0xff00, "DALLAS", ALC861VD_DALLAS),*/ /*lenovo*/
- SND_PCI_QUIRK(0x1179, 0xff01, "Toshiba A135", ALC861VD_LENOVO),
- SND_PCI_QUIRK(0x1179, 0xff03, "Toshiba P205", ALC861VD_LENOVO),
- SND_PCI_QUIRK(0x1179, 0xff31, "Toshiba L30-149", ALC861VD_DALLAS),
- SND_PCI_QUIRK(0x1565, 0x820d, "Biostar NF61S SE", ALC861VD_6ST_DIG),
- SND_PCI_QUIRK_VENDOR(0x17aa, "Lenovo", ALC861VD_LENOVO),
- SND_PCI_QUIRK(0x1849, 0x0862, "ASRock K8NF6G-VSTA", ALC861VD_6ST_DIG),
- {}
-};
-
-static struct alc_config_preset alc861vd_presets[] = {
- [ALC660VD_3ST] = {
- .mixers = { alc861vd_3st_mixer },
- .init_verbs = { alc861vd_volume_init_verbs,
- alc861vd_3stack_init_verbs },
- .num_dacs = ARRAY_SIZE(alc660vd_dac_nids),
- .dac_nids = alc660vd_dac_nids,
- .num_channel_mode = ARRAY_SIZE(alc861vd_3stack_2ch_modes),
- .channel_mode = alc861vd_3stack_2ch_modes,
- .input_mux = &alc861vd_capture_source,
- },
- [ALC660VD_3ST_DIG] = {
- .mixers = { alc861vd_3st_mixer },
- .init_verbs = { alc861vd_volume_init_verbs,
- alc861vd_3stack_init_verbs },
- .num_dacs = ARRAY_SIZE(alc660vd_dac_nids),
- .dac_nids = alc660vd_dac_nids,
- .dig_out_nid = ALC861VD_DIGOUT_NID,
- .num_channel_mode = ARRAY_SIZE(alc861vd_3stack_2ch_modes),
- .channel_mode = alc861vd_3stack_2ch_modes,
- .input_mux = &alc861vd_capture_source,
- },
- [ALC861VD_3ST] = {
- .mixers = { alc861vd_3st_mixer },
- .init_verbs = { alc861vd_volume_init_verbs,
- alc861vd_3stack_init_verbs },
- .num_dacs = ARRAY_SIZE(alc861vd_dac_nids),
- .dac_nids = alc861vd_dac_nids,
- .num_channel_mode = ARRAY_SIZE(alc861vd_3stack_2ch_modes),
- .channel_mode = alc861vd_3stack_2ch_modes,
- .input_mux = &alc861vd_capture_source,
- },
- [ALC861VD_3ST_DIG] = {
- .mixers = { alc861vd_3st_mixer },
- .init_verbs = { alc861vd_volume_init_verbs,
- alc861vd_3stack_init_verbs },
- .num_dacs = ARRAY_SIZE(alc861vd_dac_nids),
- .dac_nids = alc861vd_dac_nids,
- .dig_out_nid = ALC861VD_DIGOUT_NID,
- .num_channel_mode = ARRAY_SIZE(alc861vd_3stack_2ch_modes),
- .channel_mode = alc861vd_3stack_2ch_modes,
- .input_mux = &alc861vd_capture_source,
- },
- [ALC861VD_6ST_DIG] = {
- .mixers = { alc861vd_6st_mixer, alc861vd_chmode_mixer },
- .init_verbs = { alc861vd_volume_init_verbs,
- alc861vd_6stack_init_verbs },
- .num_dacs = ARRAY_SIZE(alc861vd_dac_nids),
- .dac_nids = alc861vd_dac_nids,
- .dig_out_nid = ALC861VD_DIGOUT_NID,
- .num_channel_mode = ARRAY_SIZE(alc861vd_6stack_modes),
- .channel_mode = alc861vd_6stack_modes,
- .input_mux = &alc861vd_capture_source,
- },
- [ALC861VD_LENOVO] = {
- .mixers = { alc861vd_lenovo_mixer },
- .init_verbs = { alc861vd_volume_init_verbs,
- alc861vd_3stack_init_verbs,
- alc861vd_eapd_verbs,
- alc861vd_lenovo_unsol_verbs },
- .num_dacs = ARRAY_SIZE(alc660vd_dac_nids),
- .dac_nids = alc660vd_dac_nids,
- .num_channel_mode = ARRAY_SIZE(alc861vd_3stack_2ch_modes),
- .channel_mode = alc861vd_3stack_2ch_modes,
- .input_mux = &alc861vd_capture_source,
- .unsol_event = alc861vd_lenovo_unsol_event,
- .setup = alc861vd_lenovo_setup,
- .init_hook = alc861vd_lenovo_init_hook,
- },
- [ALC861VD_DALLAS] = {
- .mixers = { alc861vd_dallas_mixer },
- .init_verbs = { alc861vd_dallas_verbs },
- .num_dacs = ARRAY_SIZE(alc861vd_dac_nids),
- .dac_nids = alc861vd_dac_nids,
- .num_channel_mode = ARRAY_SIZE(alc861vd_3stack_2ch_modes),
- .channel_mode = alc861vd_3stack_2ch_modes,
- .input_mux = &alc861vd_dallas_capture_source,
- .unsol_event = alc_automute_amp_unsol_event,
- .setup = alc861vd_dallas_setup,
- .init_hook = alc_automute_amp,
- },
- [ALC861VD_HP] = {
- .mixers = { alc861vd_hp_mixer },
- .init_verbs = { alc861vd_dallas_verbs, alc861vd_eapd_verbs },
- .num_dacs = ARRAY_SIZE(alc861vd_dac_nids),
- .dac_nids = alc861vd_dac_nids,
- .dig_out_nid = ALC861VD_DIGOUT_NID,
- .num_channel_mode = ARRAY_SIZE(alc861vd_3stack_2ch_modes),
- .channel_mode = alc861vd_3stack_2ch_modes,
- .input_mux = &alc861vd_hp_capture_source,
- .unsol_event = alc_automute_amp_unsol_event,
- .setup = alc861vd_dallas_setup,
- .init_hook = alc_automute_amp,
- },
- [ALC660VD_ASUS_V1S] = {
- .mixers = { alc861vd_lenovo_mixer },
- .init_verbs = { alc861vd_volume_init_verbs,
- alc861vd_3stack_init_verbs,
- alc861vd_eapd_verbs,
- alc861vd_lenovo_unsol_verbs },
- .num_dacs = ARRAY_SIZE(alc660vd_dac_nids),
- .dac_nids = alc660vd_dac_nids,
- .dig_out_nid = ALC861VD_DIGOUT_NID,
- .num_channel_mode = ARRAY_SIZE(alc861vd_3stack_2ch_modes),
- .channel_mode = alc861vd_3stack_2ch_modes,
- .input_mux = &alc861vd_capture_source,
- .unsol_event = alc861vd_lenovo_unsol_event,
- .setup = alc861vd_lenovo_setup,
- .init_hook = alc861vd_lenovo_init_hook,
- },
+enum {
+ ALC660VD_FIX_ASUS_GPIO1,
+ ALC861VD_FIX_DALLAS,
};
-/*
- * BIOS auto configuration
- */
-static int alc861vd_auto_create_input_ctls(struct hda_codec *codec,
- const struct auto_pin_cfg *cfg)
-{
- return alc_auto_create_input_ctls(codec, cfg, 0x0b, 0x22, 0);
-}
-
-
-static void alc861vd_auto_set_output_and_unmute(struct hda_codec *codec,
- hda_nid_t nid, int pin_type, int dac_idx)
-{
- alc_set_pin_output(codec, nid, pin_type);
-}
-
-static void alc861vd_auto_init_multi_out(struct hda_codec *codec)
+/* exclude VREF80 */
+static void alc861vd_fixup_dallas(struct hda_codec *codec,
+ const struct hda_fixup *fix, int action)
{
- struct alc_spec *spec = codec->spec;
- int i;
-
- for (i = 0; i <= HDA_SIDE; i++) {
- hda_nid_t nid = spec->autocfg.line_out_pins[i];
- int pin_type = get_pin_type(spec->autocfg.line_out_type);
- if (nid)
- alc861vd_auto_set_output_and_unmute(codec, nid,
- pin_type, i);
+ if (action == HDA_FIXUP_ACT_PRE_PROBE) {
+ snd_hda_override_pin_caps(codec, 0x18, 0x00000734);
+ snd_hda_override_pin_caps(codec, 0x19, 0x0000073c);
}
}
-
-static void alc861vd_auto_init_hp_out(struct hda_codec *codec)
-{
- struct alc_spec *spec = codec->spec;
- hda_nid_t pin;
-
- pin = spec->autocfg.hp_pins[0];
- if (pin) /* connect to front and use dac 0 */
- alc861vd_auto_set_output_and_unmute(codec, pin, PIN_HP, 0);
- pin = spec->autocfg.speaker_pins[0];
- if (pin)
- alc861vd_auto_set_output_and_unmute(codec, pin, PIN_OUT, 0);
-}
-
-#define ALC861VD_PIN_CD_NID ALC880_PIN_CD_NID
-
-static void alc861vd_auto_init_analog_input(struct hda_codec *codec)
-{
- struct alc_spec *spec = codec->spec;
- struct auto_pin_cfg *cfg = &spec->autocfg;
- int i;
-
- for (i = 0; i < cfg->num_inputs; i++) {
- hda_nid_t nid = cfg->inputs[i].pin;
- if (alc_is_input_pin(codec, nid)) {
- alc_set_input_pin(codec, nid, cfg->inputs[i].type);
- if (nid != ALC861VD_PIN_CD_NID &&
- (get_wcaps(codec, nid) & AC_WCAP_OUT_AMP))
- snd_hda_codec_write(codec, nid, 0,
- AC_VERB_SET_AMP_GAIN_MUTE,
- AMP_OUT_MUTE);
- }
- }
-}
-
-#define alc861vd_auto_init_input_src alc882_auto_init_input_src
-
-#define alc861vd_idx_to_mixer_vol(nid) ((nid) + 0x02)
-#define alc861vd_idx_to_mixer_switch(nid) ((nid) + 0x0c)
-
-/* add playback controls from the parsed DAC table */
-/* Based on ALC880 version. But ALC861VD has separate,
- * different NIDs for mute/unmute switch and volume control */
-static int alc861vd_auto_create_multi_out_ctls(struct alc_spec *spec,
- const struct auto_pin_cfg *cfg)
-{
- static const char * const chname[4] = {
- "Front", "Surround", "CLFE", "Side"
- };
- const char *pfx = alc_get_line_out_pfx(cfg, true);
- hda_nid_t nid_v, nid_s;
- int i, err;
-
- for (i = 0; i < cfg->line_outs; i++) {
- if (!spec->multiout.dac_nids[i])
- continue;
- nid_v = alc861vd_idx_to_mixer_vol(
- alc880_dac_to_idx(
- spec->multiout.dac_nids[i]));
- nid_s = alc861vd_idx_to_mixer_switch(
- alc880_dac_to_idx(
- spec->multiout.dac_nids[i]));
-
- if (!pfx && i == 2) {
- /* Center/LFE */
- err = add_pb_vol_ctrl(spec, ALC_CTL_WIDGET_VOL,
- "Center",
- HDA_COMPOSE_AMP_VAL(nid_v, 1, 0,
- HDA_OUTPUT));
- if (err < 0)
- return err;
- err = add_pb_vol_ctrl(spec, ALC_CTL_WIDGET_VOL,
- "LFE",
- HDA_COMPOSE_AMP_VAL(nid_v, 2, 0,
- HDA_OUTPUT));
- if (err < 0)
- return err;
- err = add_pb_sw_ctrl(spec, ALC_CTL_BIND_MUTE,
- "Center",
- HDA_COMPOSE_AMP_VAL(nid_s, 1, 2,
- HDA_INPUT));
- if (err < 0)
- return err;
- err = add_pb_sw_ctrl(spec, ALC_CTL_BIND_MUTE,
- "LFE",
- HDA_COMPOSE_AMP_VAL(nid_s, 2, 2,
- HDA_INPUT));
- if (err < 0)
- return err;
- } else {
- const char *name = pfx;
- int index = i;
- if (!name) {
- name = chname[i];
- index = 0;
- }
- err = __add_pb_vol_ctrl(spec, ALC_CTL_WIDGET_VOL,
- name, index,
- HDA_COMPOSE_AMP_VAL(nid_v, 3, 0,
- HDA_OUTPUT));
- if (err < 0)
- return err;
- err = __add_pb_sw_ctrl(spec, ALC_CTL_BIND_MUTE,
- name, index,
- HDA_COMPOSE_AMP_VAL(nid_s, 3, 2,
- HDA_INPUT));
- if (err < 0)
- return err;
- }
- }
- return 0;
-}
-
-/* add playback controls for speaker and HP outputs */
-/* Based on ALC880 version. But ALC861VD has separate,
- * different NIDs for mute/unmute switch and volume control */
-static int alc861vd_auto_create_extra_out(struct alc_spec *spec,
- hda_nid_t pin, const char *pfx)
-{
- hda_nid_t nid_v, nid_s;
- int err;
-
- if (!pin)
- return 0;
-
- if (alc880_is_fixed_pin(pin)) {
- nid_v = alc880_idx_to_dac(alc880_fixed_pin_idx(pin));
- /* specify the DAC as the extra output */
- if (!spec->multiout.hp_nid)
- spec->multiout.hp_nid = nid_v;
- else
- spec->multiout.extra_out_nid[0] = nid_v;
- /* control HP volume/switch on the output mixer amp */
- nid_v = alc861vd_idx_to_mixer_vol(
- alc880_fixed_pin_idx(pin));
- nid_s = alc861vd_idx_to_mixer_switch(
- alc880_fixed_pin_idx(pin));
-
- err = add_pb_vol_ctrl(spec, ALC_CTL_WIDGET_VOL, pfx,
- HDA_COMPOSE_AMP_VAL(nid_v, 3, 0, HDA_OUTPUT));
- if (err < 0)
- return err;
- err = add_pb_sw_ctrl(spec, ALC_CTL_BIND_MUTE, pfx,
- HDA_COMPOSE_AMP_VAL(nid_s, 3, 2, HDA_INPUT));
- if (err < 0)
- return err;
- } else if (alc880_is_multi_pin(pin)) {
- /* set manual connection */
- /* we have only a switch on HP-out PIN */
- err = add_pb_sw_ctrl(spec, ALC_CTL_WIDGET_MUTE, pfx,
- HDA_COMPOSE_AMP_VAL(pin, 3, 0, HDA_OUTPUT));
- if (err < 0)
- return err;
- }
- return 0;
-}
-
-/* parse the BIOS configuration and set up the alc_spec
- * return 1 if successful, 0 if the proper config is not found,
- * or a negative error code
- * Based on ALC880 version - had to change it to override
- * alc880_auto_create_extra_out and alc880_auto_create_multi_out_ctls */
-static int alc861vd_parse_auto_config(struct hda_codec *codec)
-{
- struct alc_spec *spec = codec->spec;
- int err;
- static hda_nid_t alc861vd_ignore[] = { 0x1d, 0 };
-
- err = snd_hda_parse_pin_def_config(codec, &spec->autocfg,
- alc861vd_ignore);
- if (err < 0)
- return err;
- if (!spec->autocfg.line_outs)
- return 0; /* can't find valid BIOS pin config */
-
- err = alc880_auto_fill_dac_nids(spec, &spec->autocfg);
- if (err < 0)
- return err;
- err = alc861vd_auto_create_multi_out_ctls(spec, &spec->autocfg);
- if (err < 0)
- return err;
- err = alc861vd_auto_create_extra_out(spec,
- spec->autocfg.speaker_pins[0],
- "Speaker");
- if (err < 0)
- return err;
- err = alc861vd_auto_create_extra_out(spec,
- spec->autocfg.hp_pins[0],
- "Headphone");
- if (err < 0)
- return err;
- err = alc861vd_auto_create_input_ctls(codec, &spec->autocfg);
- if (err < 0)
- return err;
-
- spec->multiout.max_channels = spec->multiout.num_dacs * 2;
-
- alc_auto_parse_digital(codec);
-
- if (spec->kctls.list)
- add_mixer(spec, spec->kctls.list);
-
- add_verb(spec, alc861vd_volume_init_verbs);
-
- spec->num_mux_defs = 1;
- spec->input_mux = &spec->private_imux[0];
-
- err = alc_auto_add_mic_boost(codec);
- if (err < 0)
- return err;
-
- alc_ssid_check(codec, 0x15, 0x1b, 0x14, 0);
-
- return 1;
-}
-
-/* additional initialization for auto-configuration model */
-static void alc861vd_auto_init(struct hda_codec *codec)
-{
- struct alc_spec *spec = codec->spec;
- alc861vd_auto_init_multi_out(codec);
- alc861vd_auto_init_hp_out(codec);
- alc861vd_auto_init_analog_input(codec);
- alc861vd_auto_init_input_src(codec);
- alc_auto_init_digital(codec);
- if (spec->unsol_event)
- alc_inithook(codec);
-}
-
-enum {
- ALC660VD_FIX_ASUS_GPIO1
-};
-
-/* reset GPIO1 */
-static const struct alc_fixup alc861vd_fixups[] = {
+static const struct hda_fixup alc861vd_fixups[] = {
[ALC660VD_FIX_ASUS_GPIO1] = {
- .type = ALC_FIXUP_VERBS,
+ .type = HDA_FIXUP_VERBS,
.v.verbs = (const struct hda_verb[]) {
+ /* reset GPIO1 */
{0x01, AC_VERB_SET_GPIO_MASK, 0x03},
{0x01, AC_VERB_SET_GPIO_DIRECTION, 0x01},
{0x01, AC_VERB_SET_GPIO_DATA, 0x01},
{ }
}
},
+ [ALC861VD_FIX_DALLAS] = {
+ .type = HDA_FIXUP_FUNC,
+ .v.func = alc861vd_fixup_dallas,
+ },
};
-static struct snd_pci_quirk alc861vd_fixup_tbl[] = {
+static const struct snd_pci_quirk alc861vd_fixup_tbl[] = {
+ SND_PCI_QUIRK(0x103c, 0x30bf, "HP TX1000", ALC861VD_FIX_DALLAS),
SND_PCI_QUIRK(0x1043, 0x1339, "ASUS A7-K", ALC660VD_FIX_ASUS_GPIO1),
+ SND_PCI_QUIRK(0x1179, 0xff31, "Toshiba L30-149", ALC861VD_FIX_DALLAS),
{}
};
+/*
+ */
static int patch_alc861vd(struct hda_codec *codec)
{
struct alc_spec *spec;
- int err, board_config;
-
- spec = kzalloc(sizeof(*spec), GFP_KERNEL);
- if (spec == NULL)
- return -ENOMEM;
-
- codec->spec = spec;
-
- board_config = snd_hda_check_board_config(codec, ALC861VD_MODEL_LAST,
- alc861vd_models,
- alc861vd_cfg_tbl);
-
- if (board_config < 0 || board_config >= ALC861VD_MODEL_LAST) {
- printk(KERN_INFO "hda_codec: %s: BIOS auto-probing.\n",
- codec->chip_name);
- board_config = ALC861VD_AUTO;
- }
-
- if (board_config == ALC861VD_AUTO) {
- alc_pick_fixup(codec, NULL, alc861vd_fixup_tbl, alc861vd_fixups);
- alc_apply_fixup(codec, ALC_FIXUP_ACT_PRE_PROBE);
- }
-
- if (board_config == ALC861VD_AUTO) {
- /* automatic parse from the BIOS config */
- err = alc861vd_parse_auto_config(codec);
- if (err < 0) {
- alc_free(codec);
- return err;
- } else if (!err) {
- printk(KERN_INFO
- "hda_codec: Cannot set up configuration "
- "from BIOS. Using base mode...\n");
- board_config = ALC861VD_3ST;
- }
- }
+ int err;
- err = snd_hda_attach_beep_device(codec, 0x23);
- if (err < 0) {
- alc_free(codec);
+ err = alc_alloc_spec(codec, 0x0b);
+ if (err < 0)
return err;
- }
-
- if (board_config != ALC861VD_AUTO)
- setup_preset(codec, &alc861vd_presets[board_config]);
- if (codec->vendor_id == 0x10ec0660) {
- /* always turn on EAPD */
- add_verb(spec, alc660vd_eapd_verbs);
- }
-
- spec->stream_analog_playback = &alc861vd_pcm_analog_playback;
- spec->stream_analog_capture = &alc861vd_pcm_analog_capture;
-
- spec->stream_digital_playback = &alc861vd_pcm_digital_playback;
- spec->stream_digital_capture = &alc861vd_pcm_digital_capture;
-
- if (!spec->adc_nids) {
- spec->adc_nids = alc861vd_adc_nids;
- spec->num_adc_nids = ARRAY_SIZE(alc861vd_adc_nids);
- }
- if (!spec->capsrc_nids)
- spec->capsrc_nids = alc861vd_capsrc_nids;
+ spec = codec->spec;
+ spec->gen.beep_nid = 0x23;
- set_capture_mixer(codec);
- set_beep_amp(spec, 0x0b, 0x05, HDA_INPUT);
+ snd_hda_pick_fixup(codec, NULL, alc861vd_fixup_tbl, alc861vd_fixups);
+ snd_hda_apply_fixup(codec, HDA_FIXUP_ACT_PRE_PROBE);
- spec->vmaster_nid = 0x02;
+ /* automatic parse from the BIOS config */
+ err = alc861vd_parse_auto_config(codec);
+ if (err < 0)
+ goto error;
- alc_apply_fixup(codec, ALC_FIXUP_ACT_PROBE);
+ if (!spec->gen.no_analog)
+ set_beep_amp(spec, 0x0b, 0x05, HDA_INPUT);
codec->patch_ops = alc_patch_ops;
- if (board_config == ALC861VD_AUTO)
- spec->init_hook = alc861vd_auto_init;
-#ifdef CONFIG_SND_HDA_POWER_SAVE
- if (!spec->loopback.amplist)
- spec->loopback.amplist = alc861vd_loopbacks;
-#endif
+ spec->shutup = alc_eapd_shutup;
+
+ snd_hda_apply_fixup(codec, HDA_FIXUP_ACT_PROBE);
return 0;
+
+ error:
+ alc_free(codec);
+ return err;
}
/*
@@ -17429,2167 +5494,599 @@ static int patch_alc861vd(struct hda_codec *codec)
* In addition, an independent DAC for the multi-playback (not used in this
* driver yet).
*/
-#define ALC662_DIGOUT_NID 0x06
-#define ALC662_DIGIN_NID 0x0a
-
-static hda_nid_t alc662_dac_nids[4] = {
- /* front, rear, clfe, rear_surr */
- 0x02, 0x03, 0x04
-};
-
-static hda_nid_t alc272_dac_nids[2] = {
- 0x02, 0x03
-};
-
-static hda_nid_t alc662_adc_nids[2] = {
- /* ADC1-2 */
- 0x09, 0x08
-};
-
-static hda_nid_t alc272_adc_nids[1] = {
- /* ADC1-2 */
- 0x08,
-};
-
-static hda_nid_t alc662_capsrc_nids[2] = { 0x22, 0x23 };
-static hda_nid_t alc272_capsrc_nids[1] = { 0x23 };
-
-
-/* input MUX */
-/* FIXME: should be a matrix-type input source selection */
-static struct hda_input_mux alc662_capture_source = {
- .num_items = 4,
- .items = {
- { "Mic", 0x0 },
- { "Front Mic", 0x1 },
- { "Line", 0x2 },
- { "CD", 0x4 },
- },
-};
-
-static struct hda_input_mux alc662_lenovo_101e_capture_source = {
- .num_items = 2,
- .items = {
- { "Mic", 0x1 },
- { "Line", 0x2 },
- },
-};
-
-static struct hda_input_mux alc663_capture_source = {
- .num_items = 3,
- .items = {
- { "Mic", 0x0 },
- { "Front Mic", 0x1 },
- { "Line", 0x2 },
- },
-};
-
-#if 0 /* set to 1 for testing other input sources below */
-static struct hda_input_mux alc272_nc10_capture_source = {
- .num_items = 16,
- .items = {
- { "Autoselect Mic", 0x0 },
- { "Internal Mic", 0x1 },
- { "In-0x02", 0x2 },
- { "In-0x03", 0x3 },
- { "In-0x04", 0x4 },
- { "In-0x05", 0x5 },
- { "In-0x06", 0x6 },
- { "In-0x07", 0x7 },
- { "In-0x08", 0x8 },
- { "In-0x09", 0x9 },
- { "In-0x0a", 0x0a },
- { "In-0x0b", 0x0b },
- { "In-0x0c", 0x0c },
- { "In-0x0d", 0x0d },
- { "In-0x0e", 0x0e },
- { "In-0x0f", 0x0f },
- },
-};
-#endif
-
-/*
- * 2ch mode
- */
-static struct hda_channel_mode alc662_3ST_2ch_modes[1] = {
- { 2, NULL }
-};
-
-/*
- * 2ch mode
- */
-static struct hda_verb alc662_3ST_ch2_init[] = {
- { 0x18, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80 },
- { 0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE },
- { 0x1a, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN },
- { 0x1a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE },
- { } /* end */
-};
-
-/*
- * 6ch mode
- */
-static struct hda_verb alc662_3ST_ch6_init[] = {
- { 0x18, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT },
- { 0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE },
- { 0x18, AC_VERB_SET_CONNECT_SEL, 0x02 },
- { 0x1a, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT },
- { 0x1a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE },
- { 0x1a, AC_VERB_SET_CONNECT_SEL, 0x01 },
- { } /* end */
-};
-
-static struct hda_channel_mode alc662_3ST_6ch_modes[2] = {
- { 2, alc662_3ST_ch2_init },
- { 6, alc662_3ST_ch6_init },
-};
-
-/*
- * 2ch mode
- */
-static struct hda_verb alc662_sixstack_ch6_init[] = {
- { 0x16, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x00 },
- { 0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x00 },
- { 0x14, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT },
- { } /* end */
-};
/*
- * 6ch mode
- */
-static struct hda_verb alc662_sixstack_ch8_init[] = {
- { 0x16, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT },
- { 0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT },
- { 0x14, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT },
- { } /* end */
-};
-
-static struct hda_channel_mode alc662_5stack_modes[2] = {
- { 2, alc662_sixstack_ch6_init },
- { 6, alc662_sixstack_ch8_init },
-};
-
-/* Pin assignment: Front=0x14, Rear=0x15, CLFE=0x16, Side=0x17
- * Mic=0x18, Front Mic=0x19, Line-In=0x1a, HP=0x1b
+ * BIOS auto configuration
*/
-static struct snd_kcontrol_new alc662_base_mixer[] = {
- /* output mixer control */
- HDA_CODEC_VOLUME("Front Playback Volume", 0x2, 0x0, HDA_OUTPUT),
- HDA_CODEC_MUTE("Front Playback Switch", 0x0c, 0x0, HDA_INPUT),
- HDA_CODEC_VOLUME("Surround Playback Volume", 0x3, 0x0, HDA_OUTPUT),
- HDA_CODEC_MUTE("Surround Playback Switch", 0x0d, 0x0, HDA_INPUT),
- HDA_CODEC_VOLUME_MONO("Center Playback Volume", 0x04, 1, 0x0, HDA_OUTPUT),
- HDA_CODEC_VOLUME_MONO("LFE Playback Volume", 0x04, 2, 0x0, HDA_OUTPUT),
- HDA_CODEC_MUTE_MONO("Center Playback Switch", 0x0e, 1, 0x0, HDA_INPUT),
- HDA_CODEC_MUTE_MONO("LFE Playback Switch", 0x0e, 2, 0x0, HDA_INPUT),
- HDA_CODEC_MUTE("Headphone Playback Switch", 0x1b, 0x0, HDA_OUTPUT),
-
- /*Input mixer control */
- HDA_CODEC_VOLUME("CD Playback Volume", 0xb, 0x4, HDA_INPUT),
- HDA_CODEC_MUTE("CD Playback Switch", 0xb, 0x4, HDA_INPUT),
- HDA_CODEC_VOLUME("Line Playback Volume", 0xb, 0x02, HDA_INPUT),
- HDA_CODEC_MUTE("Line Playback Switch", 0xb, 0x02, HDA_INPUT),
- HDA_CODEC_VOLUME("Mic Playback Volume", 0xb, 0x0, HDA_INPUT),
- HDA_CODEC_MUTE("Mic Playback Switch", 0xb, 0x0, HDA_INPUT),
- HDA_CODEC_VOLUME("Front Mic Playback Volume", 0xb, 0x01, HDA_INPUT),
- HDA_CODEC_MUTE("Front Mic Playback Switch", 0xb, 0x01, HDA_INPUT),
- { } /* end */
-};
-
-static struct snd_kcontrol_new alc662_3ST_2ch_mixer[] = {
- HDA_CODEC_VOLUME("Front Playback Volume", 0x02, 0x0, HDA_OUTPUT),
- HDA_CODEC_MUTE("Front Playback Switch", 0x0c, 0x0, HDA_INPUT),
- HDA_CODEC_MUTE("Headphone Playback Switch", 0x1b, 0x0, HDA_OUTPUT),
- HDA_CODEC_VOLUME("CD Playback Volume", 0x0b, 0x04, HDA_INPUT),
- HDA_CODEC_MUTE("CD Playback Switch", 0x0b, 0x04, HDA_INPUT),
- HDA_CODEC_VOLUME("Line Playback Volume", 0x0b, 0x02, HDA_INPUT),
- HDA_CODEC_MUTE("Line Playback Switch", 0x0b, 0x02, HDA_INPUT),
- HDA_CODEC_VOLUME("Mic Playback Volume", 0x0b, 0x0, HDA_INPUT),
- HDA_CODEC_MUTE("Mic Playback Switch", 0x0b, 0x0, HDA_INPUT),
- HDA_CODEC_VOLUME("Front Mic Playback Volume", 0x0b, 0x1, HDA_INPUT),
- HDA_CODEC_MUTE("Front Mic Playback Switch", 0x0b, 0x1, HDA_INPUT),
- { } /* end */
-};
-
-static struct snd_kcontrol_new alc662_3ST_6ch_mixer[] = {
- HDA_CODEC_VOLUME("Front Playback Volume", 0x02, 0x0, HDA_OUTPUT),
- HDA_CODEC_MUTE("Front Playback Switch", 0x0c, 0x0, HDA_INPUT),
- HDA_CODEC_VOLUME("Surround Playback Volume", 0x03, 0x0, HDA_OUTPUT),
- HDA_CODEC_MUTE("Surround Playback Switch", 0x0d, 0x0, HDA_INPUT),
- HDA_CODEC_VOLUME_MONO("Center Playback Volume", 0x04, 1, 0x0, HDA_OUTPUT),
- HDA_CODEC_VOLUME_MONO("LFE Playback Volume", 0x04, 2, 0x0, HDA_OUTPUT),
- HDA_CODEC_MUTE_MONO("Center Playback Switch", 0x0e, 1, 0x0, HDA_INPUT),
- HDA_CODEC_MUTE_MONO("LFE Playback Switch", 0x0e, 2, 0x0, HDA_INPUT),
- HDA_CODEC_MUTE("Headphone Playback Switch", 0x1b, 0x0, HDA_OUTPUT),
- HDA_CODEC_VOLUME("CD Playback Volume", 0x0b, 0x04, HDA_INPUT),
- HDA_CODEC_MUTE("CD Playback Switch", 0x0b, 0x04, HDA_INPUT),
- HDA_CODEC_VOLUME("Line Playback Volume", 0x0b, 0x02, HDA_INPUT),
- HDA_CODEC_MUTE("Line Playback Switch", 0x0b, 0x02, HDA_INPUT),
- HDA_CODEC_VOLUME("Mic Playback Volume", 0x0b, 0x0, HDA_INPUT),
- HDA_CODEC_MUTE("Mic Playback Switch", 0x0b, 0x0, HDA_INPUT),
- HDA_CODEC_VOLUME("Front Mic Playback Volume", 0x0b, 0x1, HDA_INPUT),
- HDA_CODEC_MUTE("Front Mic Playback Switch", 0x0b, 0x1, HDA_INPUT),
- { } /* end */
-};
-
-static struct snd_kcontrol_new alc662_lenovo_101e_mixer[] = {
- HDA_CODEC_VOLUME("Front Playback Volume", 0x02, 0x0, HDA_OUTPUT),
- HDA_BIND_MUTE("Front Playback Switch", 0x02, 2, HDA_INPUT),
- HDA_CODEC_VOLUME("Speaker Playback Volume", 0x03, 0x0, HDA_OUTPUT),
- HDA_BIND_MUTE("Speaker Playback Switch", 0x03, 2, HDA_INPUT),
- HDA_CODEC_MUTE("Headphone Playback Switch", 0x1b, 0x0, HDA_OUTPUT),
- HDA_CODEC_VOLUME("Line Playback Volume", 0x0b, 0x02, HDA_INPUT),
- HDA_CODEC_MUTE("Line Playback Switch", 0x0b, 0x02, HDA_INPUT),
- HDA_CODEC_VOLUME("Mic Playback Volume", 0x0b, 0x1, HDA_INPUT),
- HDA_CODEC_MUTE("Mic Playback Switch", 0x0b, 0x1, HDA_INPUT),
- { } /* end */
-};
-
-static struct snd_kcontrol_new alc662_eeepc_p701_mixer[] = {
- HDA_CODEC_VOLUME("Master Playback Volume", 0x02, 0x0, HDA_OUTPUT),
- ALC262_HIPPO_MASTER_SWITCH,
-
- HDA_CODEC_VOLUME("Mic Boost Volume", 0x18, 0, HDA_INPUT),
- HDA_CODEC_VOLUME("Mic Playback Volume", 0x0b, 0x0, HDA_INPUT),
- HDA_CODEC_MUTE("Mic Playback Switch", 0x0b, 0x0, HDA_INPUT),
-
- HDA_CODEC_VOLUME("Internal Mic Boost Volume", 0x19, 0, HDA_INPUT),
- HDA_CODEC_VOLUME("Internal Mic Playback Volume", 0x0b, 0x1, HDA_INPUT),
- HDA_CODEC_MUTE("Internal Mic Playback Switch", 0x0b, 0x1, HDA_INPUT),
- { } /* end */
-};
-
-static struct snd_kcontrol_new alc662_eeepc_ep20_mixer[] = {
- ALC262_HIPPO_MASTER_SWITCH,
- HDA_CODEC_VOLUME("Front Playback Volume", 0x02, 0x0, HDA_OUTPUT),
- HDA_CODEC_VOLUME("Surround Playback Volume", 0x03, 0x0, HDA_OUTPUT),
- HDA_CODEC_VOLUME_MONO("Center Playback Volume", 0x04, 1, 0x0, HDA_OUTPUT),
- HDA_CODEC_VOLUME_MONO("LFE Playback Volume", 0x04, 2, 0x0, HDA_OUTPUT),
- HDA_BIND_MUTE("MuteCtrl Playback Switch", 0x0c, 2, HDA_INPUT),
- HDA_CODEC_VOLUME("Line Playback Volume", 0x0b, 0x02, HDA_INPUT),
- HDA_CODEC_MUTE("Line Playback Switch", 0x0b, 0x02, HDA_INPUT),
- HDA_CODEC_VOLUME("Mic Playback Volume", 0x0b, 0x0, HDA_INPUT),
- HDA_CODEC_MUTE("Mic Playback Switch", 0x0b, 0x0, HDA_INPUT),
- { } /* end */
-};
-
-static struct hda_bind_ctls alc663_asus_bind_master_vol = {
- .ops = &snd_hda_bind_vol,
- .values = {
- HDA_COMPOSE_AMP_VAL(0x02, 3, 0, HDA_OUTPUT),
- HDA_COMPOSE_AMP_VAL(0x03, 3, 0, HDA_OUTPUT),
- 0
- },
-};
-
-static struct hda_bind_ctls alc663_asus_one_bind_switch = {
- .ops = &snd_hda_bind_sw,
- .values = {
- HDA_COMPOSE_AMP_VAL(0x14, 3, 0, HDA_OUTPUT),
- HDA_COMPOSE_AMP_VAL(0x21, 3, 0, HDA_OUTPUT),
- 0
- },
-};
-
-static struct snd_kcontrol_new alc663_m51va_mixer[] = {
- HDA_BIND_VOL("Master Playback Volume", &alc663_asus_bind_master_vol),
- HDA_BIND_SW("Master Playback Switch", &alc663_asus_one_bind_switch),
- HDA_CODEC_VOLUME("Mic Playback Volume", 0x0b, 0x0, HDA_INPUT),
- HDA_CODEC_MUTE("Mic Playback Switch", 0x0b, 0x0, HDA_INPUT),
- { } /* end */
-};
-
-static struct hda_bind_ctls alc663_asus_tree_bind_switch = {
- .ops = &snd_hda_bind_sw,
- .values = {
- HDA_COMPOSE_AMP_VAL(0x14, 3, 0, HDA_OUTPUT),
- HDA_COMPOSE_AMP_VAL(0x15, 3, 0, HDA_OUTPUT),
- HDA_COMPOSE_AMP_VAL(0x21, 3, 0, HDA_OUTPUT),
- 0
- },
-};
-
-static struct snd_kcontrol_new alc663_two_hp_m1_mixer[] = {
- HDA_BIND_VOL("Master Playback Volume", &alc663_asus_bind_master_vol),
- HDA_BIND_SW("Master Playback Switch", &alc663_asus_tree_bind_switch),
- HDA_CODEC_VOLUME("Mic Playback Volume", 0x0b, 0x0, HDA_INPUT),
- HDA_CODEC_MUTE("Mic Playback Switch", 0x0b, 0x0, HDA_INPUT),
- HDA_CODEC_VOLUME("F-Mic Playback Volume", 0x0b, 0x1, HDA_INPUT),
- HDA_CODEC_MUTE("F-Mic Playback Switch", 0x0b, 0x1, HDA_INPUT),
-
- { } /* end */
-};
-
-static struct hda_bind_ctls alc663_asus_four_bind_switch = {
- .ops = &snd_hda_bind_sw,
- .values = {
- HDA_COMPOSE_AMP_VAL(0x14, 3, 0, HDA_OUTPUT),
- HDA_COMPOSE_AMP_VAL(0x15, 3, 0, HDA_OUTPUT),
- HDA_COMPOSE_AMP_VAL(0x1b, 3, 0, HDA_OUTPUT),
- 0
- },
-};
-
-static struct snd_kcontrol_new alc663_two_hp_m2_mixer[] = {
- HDA_BIND_VOL("Master Playback Volume", &alc663_asus_bind_master_vol),
- HDA_BIND_SW("Master Playback Switch", &alc663_asus_four_bind_switch),
- HDA_CODEC_VOLUME("Mic Playback Volume", 0x0b, 0x0, HDA_INPUT),
- HDA_CODEC_MUTE("Mic Playback Switch", 0x0b, 0x0, HDA_INPUT),
- HDA_CODEC_VOLUME("F-Mic Playback Volume", 0x0b, 0x1, HDA_INPUT),
- HDA_CODEC_MUTE("F-Mic Playback Switch", 0x0b, 0x1, HDA_INPUT),
- { } /* end */
-};
-
-static struct snd_kcontrol_new alc662_1bjd_mixer[] = {
- HDA_CODEC_VOLUME("Speaker Playback Volume", 0x02, 0x0, HDA_OUTPUT),
- HDA_CODEC_MUTE("Speaker Playback Switch", 0x14, 0x0, HDA_OUTPUT),
- HDA_CODEC_MUTE("Headphone Playback Switch", 0x1b, 0x0, HDA_OUTPUT),
- HDA_CODEC_VOLUME("Mic Playback Volume", 0x0b, 0x0, HDA_INPUT),
- HDA_CODEC_MUTE("Mic Playback Switch", 0x0b, 0x0, HDA_INPUT),
- HDA_CODEC_VOLUME("F-Mic Playback Volume", 0x0b, 0x1, HDA_INPUT),
- HDA_CODEC_MUTE("F-Mic Playback Switch", 0x0b, 0x1, HDA_INPUT),
- { } /* end */
-};
-
-static struct hda_bind_ctls alc663_asus_two_bind_master_vol = {
- .ops = &snd_hda_bind_vol,
- .values = {
- HDA_COMPOSE_AMP_VAL(0x02, 3, 0, HDA_OUTPUT),
- HDA_COMPOSE_AMP_VAL(0x04, 3, 0, HDA_OUTPUT),
- 0
- },
-};
-
-static struct hda_bind_ctls alc663_asus_two_bind_switch = {
- .ops = &snd_hda_bind_sw,
- .values = {
- HDA_COMPOSE_AMP_VAL(0x14, 3, 0, HDA_OUTPUT),
- HDA_COMPOSE_AMP_VAL(0x16, 3, 0, HDA_OUTPUT),
- 0
- },
-};
-
-static struct snd_kcontrol_new alc663_asus_21jd_clfe_mixer[] = {
- HDA_BIND_VOL("Master Playback Volume",
- &alc663_asus_two_bind_master_vol),
- HDA_BIND_SW("Master Playback Switch", &alc663_asus_two_bind_switch),
- HDA_CODEC_VOLUME("Headphone Playback Volume", 0x03, 0x0, HDA_OUTPUT),
- HDA_CODEC_MUTE("Headphone Playback Switch", 0x21, 0x0, HDA_OUTPUT),
- HDA_CODEC_VOLUME("Mic Playback Volume", 0x0b, 0x0, HDA_INPUT),
- HDA_CODEC_MUTE("Mic Playback Switch", 0x0b, 0x0, HDA_INPUT),
- { } /* end */
-};
-
-static struct snd_kcontrol_new alc663_asus_15jd_clfe_mixer[] = {
- HDA_BIND_VOL("Master Playback Volume", &alc663_asus_bind_master_vol),
- HDA_BIND_SW("Master Playback Switch", &alc663_asus_two_bind_switch),
- HDA_CODEC_VOLUME("Headphone Playback Volume", 0x03, 0x0, HDA_OUTPUT),
- HDA_CODEC_MUTE("Headphone Playback Switch", 0x15, 0x0, HDA_OUTPUT),
- HDA_CODEC_VOLUME("Mic Playback Volume", 0x0b, 0x0, HDA_INPUT),
- HDA_CODEC_MUTE("Mic Playback Switch", 0x0b, 0x0, HDA_INPUT),
- { } /* end */
-};
-
-static struct snd_kcontrol_new alc663_g71v_mixer[] = {
- HDA_CODEC_VOLUME("Speaker Playback Volume", 0x02, 0x0, HDA_OUTPUT),
- HDA_CODEC_MUTE("Speaker Playback Switch", 0x14, 0x0, HDA_OUTPUT),
- HDA_CODEC_VOLUME("Front Playback Volume", 0x03, 0x0, HDA_OUTPUT),
- HDA_CODEC_MUTE("Front Playback Switch", 0x15, 0x0, HDA_OUTPUT),
- HDA_CODEC_MUTE("Headphone Playback Switch", 0x21, 0x0, HDA_OUTPUT),
-
- HDA_CODEC_VOLUME("Mic Playback Volume", 0x0b, 0x0, HDA_INPUT),
- HDA_CODEC_MUTE("Mic Playback Switch", 0x0b, 0x0, HDA_INPUT),
- HDA_CODEC_VOLUME("Internal Mic Playback Volume", 0x0b, 0x1, HDA_INPUT),
- HDA_CODEC_MUTE("Internal Mic Playback Switch", 0x0b, 0x1, HDA_INPUT),
- { } /* end */
-};
-
-static struct snd_kcontrol_new alc663_g50v_mixer[] = {
- HDA_CODEC_VOLUME("Speaker Playback Volume", 0x02, 0x0, HDA_OUTPUT),
- HDA_CODEC_MUTE("Speaker Playback Switch", 0x14, 0x0, HDA_OUTPUT),
- HDA_CODEC_MUTE("Headphone Playback Switch", 0x21, 0x0, HDA_OUTPUT),
-
- HDA_CODEC_VOLUME("Mic Playback Volume", 0x0b, 0x0, HDA_INPUT),
- HDA_CODEC_MUTE("Mic Playback Switch", 0x0b, 0x0, HDA_INPUT),
- HDA_CODEC_VOLUME("Internal Mic Playback Volume", 0x0b, 0x1, HDA_INPUT),
- HDA_CODEC_MUTE("Internal Mic Playback Switch", 0x0b, 0x1, HDA_INPUT),
- HDA_CODEC_VOLUME("Line Playback Volume", 0x0b, 0x02, HDA_INPUT),
- HDA_CODEC_MUTE("Line Playback Switch", 0x0b, 0x02, HDA_INPUT),
- { } /* end */
-};
-
-static struct hda_bind_ctls alc663_asus_mode7_8_all_bind_switch = {
- .ops = &snd_hda_bind_sw,
- .values = {
- HDA_COMPOSE_AMP_VAL(0x14, 3, 0, HDA_OUTPUT),
- HDA_COMPOSE_AMP_VAL(0x15, 3, 0, HDA_OUTPUT),
- HDA_COMPOSE_AMP_VAL(0x17, 3, 0, HDA_OUTPUT),
- HDA_COMPOSE_AMP_VAL(0x1b, 3, 0, HDA_OUTPUT),
- HDA_COMPOSE_AMP_VAL(0x21, 3, 0, HDA_OUTPUT),
- 0
- },
-};
-
-static struct hda_bind_ctls alc663_asus_mode7_8_sp_bind_switch = {
- .ops = &snd_hda_bind_sw,
- .values = {
- HDA_COMPOSE_AMP_VAL(0x14, 3, 0, HDA_OUTPUT),
- HDA_COMPOSE_AMP_VAL(0x17, 3, 0, HDA_OUTPUT),
- 0
- },
-};
-
-static struct snd_kcontrol_new alc663_mode7_mixer[] = {
- HDA_BIND_SW("Master Playback Switch", &alc663_asus_mode7_8_all_bind_switch),
- HDA_BIND_VOL("Speaker Playback Volume", &alc663_asus_bind_master_vol),
- HDA_BIND_SW("Speaker Playback Switch", &alc663_asus_mode7_8_sp_bind_switch),
- HDA_CODEC_MUTE("Headphone1 Playback Switch", 0x1b, 0x0, HDA_OUTPUT),
- HDA_CODEC_MUTE("Headphone2 Playback Switch", 0x21, 0x0, HDA_OUTPUT),
- HDA_CODEC_VOLUME("IntMic Playback Volume", 0x0b, 0x0, HDA_INPUT),
- HDA_CODEC_MUTE("IntMic Playback Switch", 0x0b, 0x0, HDA_INPUT),
- HDA_CODEC_VOLUME("Mic Playback Volume", 0x0b, 0x1, HDA_INPUT),
- HDA_CODEC_MUTE("Mic Playback Switch", 0x0b, 0x1, HDA_INPUT),
- { } /* end */
-};
-
-static struct snd_kcontrol_new alc663_mode8_mixer[] = {
- HDA_BIND_SW("Master Playback Switch", &alc663_asus_mode7_8_all_bind_switch),
- HDA_BIND_VOL("Speaker Playback Volume", &alc663_asus_bind_master_vol),
- HDA_BIND_SW("Speaker Playback Switch", &alc663_asus_mode7_8_sp_bind_switch),
- HDA_CODEC_MUTE("Headphone1 Playback Switch", 0x15, 0x0, HDA_OUTPUT),
- HDA_CODEC_MUTE("Headphone2 Playback Switch", 0x21, 0x0, HDA_OUTPUT),
- HDA_CODEC_VOLUME("Mic Playback Volume", 0x0b, 0x0, HDA_INPUT),
- HDA_CODEC_MUTE("Mic Playback Switch", 0x0b, 0x0, HDA_INPUT),
- { } /* end */
-};
-
-
-static struct snd_kcontrol_new alc662_chmode_mixer[] = {
- {
- .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
- .name = "Channel Mode",
- .info = alc_ch_mode_info,
- .get = alc_ch_mode_get,
- .put = alc_ch_mode_put,
- },
- { } /* end */
-};
-
-static struct hda_verb alc662_init_verbs[] = {
- /* ADC: mute amp left and right */
- {0x09, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
- {0x09, AC_VERB_SET_CONNECT_SEL, 0x00},
-
- {0x0c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
- {0x0c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
- {0x0d, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
- {0x0d, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
- {0x0e, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
- {0x0e, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
-
- /* Front Pin: output 0 (0x0c) */
- {0x14, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
- {0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
-
- /* Rear Pin: output 1 (0x0d) */
- {0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
- {0x15, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
-
- /* CLFE Pin: output 2 (0x0e) */
- {0x16, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
- {0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
-
- /* Mic (rear) pin: input vref at 80% */
- {0x18, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80},
- {0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
- /* Front Mic pin: input vref at 80% */
- {0x19, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80},
- {0x19, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
- /* Line In pin: input */
- {0x1a, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN},
- {0x1a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
- /* Line-2 In: Headphone output (output 0 - 0x0c) */
- {0x1b, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP},
- {0x1b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
- {0x1b, AC_VERB_SET_CONNECT_SEL, 0x00},
- /* CD pin widget for input */
- {0x1c, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN},
-
- /* FIXME: use matrix-type input source selection */
- /* Mixer elements: 0x18, 19, 1a, 1b, 1c, 1d, 14, 15, 16, 17, 0b */
- /* Input mixer */
- {0x22, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
- {0x23, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
-
- /* always trun on EAPD */
- {0x14, AC_VERB_SET_EAPD_BTLENABLE, 2},
- {0x15, AC_VERB_SET_EAPD_BTLENABLE, 2},
-
- { }
-};
-
-static struct hda_verb alc663_init_verbs[] = {
- {0x17, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
- {0x17, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
- {0x21, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
- {0x21, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
- {0x0f, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
- {0x0f, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
- { }
-};
-
-static struct hda_verb alc272_init_verbs[] = {
- {0x16, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN},
- {0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
- {0x17, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
- {0x17, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
- {0x21, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
- {0x21, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
- {0x0f, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
- {0x0f, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
- { }
-};
-
-static struct hda_verb alc662_sue_init_verbs[] = {
- {0x14, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN|ALC880_FRONT_EVENT},
- {0x1b, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN|ALC880_HP_EVENT},
- {}
-};
-
-static struct hda_verb alc662_eeepc_sue_init_verbs[] = {
- {0x18, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | ALC880_MIC_EVENT},
- {0x1b, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | ALC880_HP_EVENT},
- {}
-};
-
-/* Set Unsolicited Event*/
-static struct hda_verb alc662_eeepc_ep20_sue_init_verbs[] = {
- {0x1b, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
- {0x14, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | ALC880_HP_EVENT},
- {}
-};
-
-static struct hda_verb alc663_m51va_init_verbs[] = {
- {0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN},
- {0x16, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN},
- {0x21, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP},
- {0x21, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
- {0x21, AC_VERB_SET_CONNECT_SEL, 0x01}, /* Headphone */
- {0x22, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
- {0x22, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(9)},
- {0x18, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | ALC880_MIC_EVENT},
- {0x21, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | ALC880_HP_EVENT},
- {}
-};
-
-static struct hda_verb alc663_21jd_amic_init_verbs[] = {
- {0x21, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP},
- {0x21, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
- {0x21, AC_VERB_SET_CONNECT_SEL, 0x01}, /* Headphone */
- {0x22, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
- {0x22, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
- {0x18, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | ALC880_MIC_EVENT},
- {0x21, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | ALC880_HP_EVENT},
- {}
-};
-
-static struct hda_verb alc662_1bjd_amic_init_verbs[] = {
- {0x16, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN},
- {0x1b, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP},
- {0x1b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
- {0x1b, AC_VERB_SET_CONNECT_SEL, 0x00}, /* Headphone */
- {0x22, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
- {0x22, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
- {0x18, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | ALC880_MIC_EVENT},
- {0x1b, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | ALC880_HP_EVENT},
- {}
-};
-
-static struct hda_verb alc663_15jd_amic_init_verbs[] = {
- {0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP},
- {0x15, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
- {0x15, AC_VERB_SET_CONNECT_SEL, 0x01}, /* Headphone */
- {0x22, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
- {0x22, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
- {0x18, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | ALC880_MIC_EVENT},
- {0x15, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | ALC880_HP_EVENT},
- {}
-};
-
-static struct hda_verb alc663_two_hp_amic_m1_init_verbs[] = {
- {0x16, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN},
- {0x21, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP},
- {0x21, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
- {0x21, AC_VERB_SET_CONNECT_SEL, 0x0}, /* Headphone */
- {0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP},
- {0x15, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
- {0x15, AC_VERB_SET_CONNECT_SEL, 0x0}, /* Headphone */
- {0x22, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
- {0x22, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
- {0x18, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | ALC880_MIC_EVENT},
- {0x21, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | ALC880_HP_EVENT},
- {0x15, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | ALC880_HP_EVENT},
- {}
-};
-
-static struct hda_verb alc663_two_hp_amic_m2_init_verbs[] = {
- {0x16, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN},
- {0x1b, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP},
- {0x1b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
- {0x1b, AC_VERB_SET_CONNECT_SEL, 0x01}, /* Headphone */
- {0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP},
- {0x15, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
- {0x15, AC_VERB_SET_CONNECT_SEL, 0x01}, /* Headphone */
- {0x22, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
- {0x22, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
- {0x18, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | ALC880_MIC_EVENT},
- {0x1b, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | ALC880_HP_EVENT},
- {0x15, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | ALC880_HP_EVENT},
- {}
-};
-
-static struct hda_verb alc663_g71v_init_verbs[] = {
- {0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP},
- /* {0x15, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE}, */
- /* {0x15, AC_VERB_SET_CONNECT_SEL, 0x01}, */ /* Headphone */
-
- {0x21, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP},
- {0x21, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
- {0x21, AC_VERB_SET_CONNECT_SEL, 0x00}, /* Headphone */
-
- {0x15, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN|ALC880_FRONT_EVENT},
- {0x18, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN|ALC880_MIC_EVENT},
- {0x21, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN|ALC880_HP_EVENT},
- {}
-};
-
-static struct hda_verb alc663_g50v_init_verbs[] = {
- {0x21, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP},
- {0x21, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
- {0x21, AC_VERB_SET_CONNECT_SEL, 0x00}, /* Headphone */
-
- {0x18, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | ALC880_MIC_EVENT},
- {0x21, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | ALC880_HP_EVENT},
- {}
-};
-
-static struct hda_verb alc662_ecs_init_verbs[] = {
- {0x09, AC_VERB_SET_AMP_GAIN_MUTE, 0x701f},
- {0x22, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
- {0x18, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | ALC880_MIC_EVENT},
- {0x1b, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | ALC880_HP_EVENT},
- {}
-};
-
-static struct hda_verb alc272_dell_zm1_init_verbs[] = {
- {0x12, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN},
- {0x13, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN},
- {0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN},
- {0x16, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN},
- {0x21, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP},
- {0x21, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
- {0x21, AC_VERB_SET_CONNECT_SEL, 0x01}, /* Headphone */
- {0x22, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
- {0x22, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(9)},
- {0x18, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | ALC880_MIC_EVENT},
- {0x21, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | ALC880_HP_EVENT},
- {}
-};
-
-static struct hda_verb alc272_dell_init_verbs[] = {
- {0x12, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN},
- {0x13, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN},
- {0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN},
- {0x16, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN},
- {0x21, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP},
- {0x21, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
- {0x21, AC_VERB_SET_CONNECT_SEL, 0x01}, /* Headphone */
- {0x23, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
- {0x23, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(9)},
- {0x18, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | ALC880_MIC_EVENT},
- {0x21, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | ALC880_HP_EVENT},
- {}
-};
-
-static struct hda_verb alc663_mode7_init_verbs[] = {
- {0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN},
- {0x16, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN},
- {0x17, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
- {0x17, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
- {0x1b, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP},
- {0x1b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
- {0x1b, AC_VERB_SET_CONNECT_SEL, 0x01},
- {0x21, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP},
- {0x21, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
- {0x21, AC_VERB_SET_CONNECT_SEL, 0x01}, /* Headphone */
- {0x22, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
- {0x22, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(9)},
- {0x19, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | ALC880_MIC_EVENT},
- {0x1b, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | ALC880_HP_EVENT},
- {0x21, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | ALC880_HP_EVENT},
- {}
-};
-
-static struct hda_verb alc663_mode8_init_verbs[] = {
- {0x12, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN},
- {0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP},
- {0x15, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
- {0x15, AC_VERB_SET_CONNECT_SEL, 0x01},
- {0x16, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN},
- {0x17, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
- {0x17, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
- {0x1b, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN},
- {0x21, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP},
- {0x21, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
- {0x21, AC_VERB_SET_CONNECT_SEL, 0x01}, /* Headphone */
- {0x22, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
- {0x22, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(9)},
- {0x15, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | ALC880_HP_EVENT},
- {0x18, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | ALC880_MIC_EVENT},
- {0x21, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | ALC880_HP_EVENT},
- {}
-};
-
-static struct snd_kcontrol_new alc662_auto_capture_mixer[] = {
- HDA_CODEC_VOLUME("Capture Volume", 0x09, 0x0, HDA_INPUT),
- HDA_CODEC_MUTE("Capture Switch", 0x09, 0x0, HDA_INPUT),
- { } /* end */
-};
-
-static struct snd_kcontrol_new alc272_auto_capture_mixer[] = {
- HDA_CODEC_VOLUME("Capture Volume", 0x08, 0x0, HDA_INPUT),
- HDA_CODEC_MUTE("Capture Switch", 0x08, 0x0, HDA_INPUT),
- { } /* end */
-};
-
-static void alc662_lenovo_101e_ispeaker_automute(struct hda_codec *codec)
-{
- unsigned int present;
- unsigned char bits;
-
- present = snd_hda_jack_detect(codec, 0x14);
- bits = present ? HDA_AMP_MUTE : 0;
-
- snd_hda_codec_amp_stereo(codec, 0x15, HDA_OUTPUT, 0,
- HDA_AMP_MUTE, bits);
-}
-
-static void alc662_lenovo_101e_all_automute(struct hda_codec *codec)
-{
- unsigned int present;
- unsigned char bits;
-
- present = snd_hda_jack_detect(codec, 0x1b);
- bits = present ? HDA_AMP_MUTE : 0;
-
- snd_hda_codec_amp_stereo(codec, 0x15, HDA_OUTPUT, 0,
- HDA_AMP_MUTE, bits);
- snd_hda_codec_amp_stereo(codec, 0x14, HDA_OUTPUT, 0,
- HDA_AMP_MUTE, bits);
-}
-
-static void alc662_lenovo_101e_unsol_event(struct hda_codec *codec,
- unsigned int res)
+static int alc662_parse_auto_config(struct hda_codec *codec)
{
- if ((res >> 26) == ALC880_HP_EVENT)
- alc662_lenovo_101e_all_automute(codec);
- if ((res >> 26) == ALC880_FRONT_EVENT)
- alc662_lenovo_101e_ispeaker_automute(codec);
-}
+ static const hda_nid_t alc662_ignore[] = { 0x1d, 0 };
+ static const hda_nid_t alc663_ssids[] = { 0x15, 0x1b, 0x14, 0x21 };
+ static const hda_nid_t alc662_ssids[] = { 0x15, 0x1b, 0x14, 0 };
+ const hda_nid_t *ssids;
-/* unsolicited event for HP jack sensing */
-static void alc662_eeepc_unsol_event(struct hda_codec *codec,
- unsigned int res)
-{
- if ((res >> 26) == ALC880_MIC_EVENT)
- alc_mic_automute(codec);
+ if (codec->vendor_id == 0x10ec0272 || codec->vendor_id == 0x10ec0663 ||
+ codec->vendor_id == 0x10ec0665 || codec->vendor_id == 0x10ec0670 ||
+ codec->vendor_id == 0x10ec0671)
+ ssids = alc663_ssids;
else
- alc262_hippo_unsol_event(codec, res);
-}
-
-static void alc662_eeepc_setup(struct hda_codec *codec)
-{
- struct alc_spec *spec = codec->spec;
-
- alc262_hippo1_setup(codec);
- spec->ext_mic.pin = 0x18;
- spec->ext_mic.mux_idx = 0;
- spec->int_mic.pin = 0x19;
- spec->int_mic.mux_idx = 1;
- spec->auto_mic = 1;
-}
-
-static void alc662_eeepc_inithook(struct hda_codec *codec)
-{
- alc262_hippo_automute(codec);
- alc_mic_automute(codec);
-}
-
-static void alc662_eeepc_ep20_setup(struct hda_codec *codec)
-{
- struct alc_spec *spec = codec->spec;
-
- spec->autocfg.hp_pins[0] = 0x14;
- spec->autocfg.speaker_pins[0] = 0x1b;
-}
-
-#define alc662_eeepc_ep20_inithook alc262_hippo_master_update
-
-static void alc663_m51va_speaker_automute(struct hda_codec *codec)
-{
- unsigned int present;
- unsigned char bits;
-
- present = snd_hda_jack_detect(codec, 0x21);
- bits = present ? HDA_AMP_MUTE : 0;
- snd_hda_codec_amp_stereo(codec, 0x0c, HDA_INPUT, 0,
- HDA_AMP_MUTE, bits);
- snd_hda_codec_amp_stereo(codec, 0x0c, HDA_INPUT, 1,
- HDA_AMP_MUTE, bits);
-}
-
-static void alc663_21jd_two_speaker_automute(struct hda_codec *codec)
-{
- unsigned int present;
- unsigned char bits;
-
- present = snd_hda_jack_detect(codec, 0x21);
- bits = present ? HDA_AMP_MUTE : 0;
- snd_hda_codec_amp_stereo(codec, 0x0c, HDA_INPUT, 0,
- HDA_AMP_MUTE, bits);
- snd_hda_codec_amp_stereo(codec, 0x0c, HDA_INPUT, 1,
- HDA_AMP_MUTE, bits);
- snd_hda_codec_amp_stereo(codec, 0x0e, HDA_INPUT, 0,
- HDA_AMP_MUTE, bits);
- snd_hda_codec_amp_stereo(codec, 0x0e, HDA_INPUT, 1,
- HDA_AMP_MUTE, bits);
-}
-
-static void alc663_15jd_two_speaker_automute(struct hda_codec *codec)
-{
- unsigned int present;
- unsigned char bits;
-
- present = snd_hda_jack_detect(codec, 0x15);
- bits = present ? HDA_AMP_MUTE : 0;
- snd_hda_codec_amp_stereo(codec, 0x0c, HDA_INPUT, 0,
- HDA_AMP_MUTE, bits);
- snd_hda_codec_amp_stereo(codec, 0x0c, HDA_INPUT, 1,
- HDA_AMP_MUTE, bits);
- snd_hda_codec_amp_stereo(codec, 0x0e, HDA_INPUT, 0,
- HDA_AMP_MUTE, bits);
- snd_hda_codec_amp_stereo(codec, 0x0e, HDA_INPUT, 1,
- HDA_AMP_MUTE, bits);
-}
-
-static void alc662_f5z_speaker_automute(struct hda_codec *codec)
-{
- unsigned int present;
- unsigned char bits;
-
- present = snd_hda_jack_detect(codec, 0x1b);
- bits = present ? 0 : PIN_OUT;
- snd_hda_codec_write(codec, 0x14, 0,
- AC_VERB_SET_PIN_WIDGET_CONTROL, bits);
-}
-
-static void alc663_two_hp_m1_speaker_automute(struct hda_codec *codec)
-{
- unsigned int present1, present2;
-
- present1 = snd_hda_jack_detect(codec, 0x21);
- present2 = snd_hda_jack_detect(codec, 0x15);
-
- if (present1 || present2) {
- snd_hda_codec_write_cache(codec, 0x14, 0,
- AC_VERB_SET_PIN_WIDGET_CONTROL, 0);
- } else {
- snd_hda_codec_write_cache(codec, 0x14, 0,
- AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT);
- }
-}
-
-static void alc663_two_hp_m2_speaker_automute(struct hda_codec *codec)
-{
- unsigned int present1, present2;
-
- present1 = snd_hda_jack_detect(codec, 0x1b);
- present2 = snd_hda_jack_detect(codec, 0x15);
-
- if (present1 || present2) {
- snd_hda_codec_amp_stereo(codec, 0x0c, HDA_INPUT, 0,
- HDA_AMP_MUTE, HDA_AMP_MUTE);
- snd_hda_codec_amp_stereo(codec, 0x0c, HDA_INPUT, 1,
- HDA_AMP_MUTE, HDA_AMP_MUTE);
- } else {
- snd_hda_codec_amp_stereo(codec, 0x0c, HDA_INPUT, 0,
- HDA_AMP_MUTE, 0);
- snd_hda_codec_amp_stereo(codec, 0x0c, HDA_INPUT, 1,
- HDA_AMP_MUTE, 0);
- }
-}
-
-static void alc663_two_hp_m7_speaker_automute(struct hda_codec *codec)
-{
- unsigned int present1, present2;
-
- present1 = snd_hda_codec_read(codec, 0x1b, 0,
- AC_VERB_GET_PIN_SENSE, 0)
- & AC_PINSENSE_PRESENCE;
- present2 = snd_hda_codec_read(codec, 0x21, 0,
- AC_VERB_GET_PIN_SENSE, 0)
- & AC_PINSENSE_PRESENCE;
-
- if (present1 || present2) {
- snd_hda_codec_write_cache(codec, 0x14, 0,
- AC_VERB_SET_PIN_WIDGET_CONTROL, 0);
- snd_hda_codec_write_cache(codec, 0x17, 0,
- AC_VERB_SET_PIN_WIDGET_CONTROL, 0);
- } else {
- snd_hda_codec_write_cache(codec, 0x14, 0,
- AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT);
- snd_hda_codec_write_cache(codec, 0x17, 0,
- AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT);
- }
-}
-
-static void alc663_two_hp_m8_speaker_automute(struct hda_codec *codec)
-{
- unsigned int present1, present2;
-
- present1 = snd_hda_codec_read(codec, 0x21, 0,
- AC_VERB_GET_PIN_SENSE, 0)
- & AC_PINSENSE_PRESENCE;
- present2 = snd_hda_codec_read(codec, 0x15, 0,
- AC_VERB_GET_PIN_SENSE, 0)
- & AC_PINSENSE_PRESENCE;
-
- if (present1 || present2) {
- snd_hda_codec_write_cache(codec, 0x14, 0,
- AC_VERB_SET_PIN_WIDGET_CONTROL, 0);
- snd_hda_codec_write_cache(codec, 0x17, 0,
- AC_VERB_SET_PIN_WIDGET_CONTROL, 0);
- } else {
- snd_hda_codec_write_cache(codec, 0x14, 0,
- AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT);
- snd_hda_codec_write_cache(codec, 0x17, 0,
- AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT);
- }
-}
-
-static void alc663_m51va_unsol_event(struct hda_codec *codec,
- unsigned int res)
-{
- switch (res >> 26) {
- case ALC880_HP_EVENT:
- alc663_m51va_speaker_automute(codec);
- break;
- case ALC880_MIC_EVENT:
- alc_mic_automute(codec);
- break;
- }
-}
-
-static void alc663_m51va_setup(struct hda_codec *codec)
-{
- struct alc_spec *spec = codec->spec;
- spec->ext_mic.pin = 0x18;
- spec->ext_mic.mux_idx = 0;
- spec->int_mic.pin = 0x12;
- spec->int_mic.mux_idx = 9;
- spec->auto_mic = 1;
-}
-
-static void alc663_m51va_inithook(struct hda_codec *codec)
-{
- alc663_m51va_speaker_automute(codec);
- alc_mic_automute(codec);
-}
-
-/* ***************** Mode1 ******************************/
-#define alc663_mode1_unsol_event alc663_m51va_unsol_event
-
-static void alc663_mode1_setup(struct hda_codec *codec)
-{
- struct alc_spec *spec = codec->spec;
- spec->ext_mic.pin = 0x18;
- spec->ext_mic.mux_idx = 0;
- spec->int_mic.pin = 0x19;
- spec->int_mic.mux_idx = 1;
- spec->auto_mic = 1;
-}
-
-#define alc663_mode1_inithook alc663_m51va_inithook
-
-/* ***************** Mode2 ******************************/
-static void alc662_mode2_unsol_event(struct hda_codec *codec,
- unsigned int res)
-{
- switch (res >> 26) {
- case ALC880_HP_EVENT:
- alc662_f5z_speaker_automute(codec);
- break;
- case ALC880_MIC_EVENT:
- alc_mic_automute(codec);
- break;
- }
-}
-
-#define alc662_mode2_setup alc663_mode1_setup
-
-static void alc662_mode2_inithook(struct hda_codec *codec)
-{
- alc662_f5z_speaker_automute(codec);
- alc_mic_automute(codec);
-}
-/* ***************** Mode3 ******************************/
-static void alc663_mode3_unsol_event(struct hda_codec *codec,
- unsigned int res)
-{
- switch (res >> 26) {
- case ALC880_HP_EVENT:
- alc663_two_hp_m1_speaker_automute(codec);
- break;
- case ALC880_MIC_EVENT:
- alc_mic_automute(codec);
- break;
- }
-}
-
-#define alc663_mode3_setup alc663_mode1_setup
-
-static void alc663_mode3_inithook(struct hda_codec *codec)
-{
- alc663_two_hp_m1_speaker_automute(codec);
- alc_mic_automute(codec);
-}
-/* ***************** Mode4 ******************************/
-static void alc663_mode4_unsol_event(struct hda_codec *codec,
- unsigned int res)
-{
- switch (res >> 26) {
- case ALC880_HP_EVENT:
- alc663_21jd_two_speaker_automute(codec);
- break;
- case ALC880_MIC_EVENT:
- alc_mic_automute(codec);
- break;
- }
-}
-
-#define alc663_mode4_setup alc663_mode1_setup
-
-static void alc663_mode4_inithook(struct hda_codec *codec)
-{
- alc663_21jd_two_speaker_automute(codec);
- alc_mic_automute(codec);
-}
-/* ***************** Mode5 ******************************/
-static void alc663_mode5_unsol_event(struct hda_codec *codec,
- unsigned int res)
-{
- switch (res >> 26) {
- case ALC880_HP_EVENT:
- alc663_15jd_two_speaker_automute(codec);
- break;
- case ALC880_MIC_EVENT:
- alc_mic_automute(codec);
- break;
- }
-}
-
-#define alc663_mode5_setup alc663_mode1_setup
-
-static void alc663_mode5_inithook(struct hda_codec *codec)
-{
- alc663_15jd_two_speaker_automute(codec);
- alc_mic_automute(codec);
-}
-/* ***************** Mode6 ******************************/
-static void alc663_mode6_unsol_event(struct hda_codec *codec,
- unsigned int res)
-{
- switch (res >> 26) {
- case ALC880_HP_EVENT:
- alc663_two_hp_m2_speaker_automute(codec);
- break;
- case ALC880_MIC_EVENT:
- alc_mic_automute(codec);
- break;
- }
-}
-
-#define alc663_mode6_setup alc663_mode1_setup
-
-static void alc663_mode6_inithook(struct hda_codec *codec)
-{
- alc663_two_hp_m2_speaker_automute(codec);
- alc_mic_automute(codec);
-}
-
-/* ***************** Mode7 ******************************/
-static void alc663_mode7_unsol_event(struct hda_codec *codec,
- unsigned int res)
-{
- switch (res >> 26) {
- case ALC880_HP_EVENT:
- alc663_two_hp_m7_speaker_automute(codec);
- break;
- case ALC880_MIC_EVENT:
- alc_mic_automute(codec);
- break;
- }
-}
-
-#define alc663_mode7_setup alc663_mode1_setup
-
-static void alc663_mode7_inithook(struct hda_codec *codec)
-{
- alc663_two_hp_m7_speaker_automute(codec);
- alc_mic_automute(codec);
-}
-
-/* ***************** Mode8 ******************************/
-static void alc663_mode8_unsol_event(struct hda_codec *codec,
- unsigned int res)
-{
- switch (res >> 26) {
- case ALC880_HP_EVENT:
- alc663_two_hp_m8_speaker_automute(codec);
- break;
- case ALC880_MIC_EVENT:
- alc_mic_automute(codec);
- break;
- }
+ ssids = alc662_ssids;
+ return alc_parse_auto_config(codec, alc662_ignore, ssids);
}
-#define alc663_mode8_setup alc663_m51va_setup
-
-static void alc663_mode8_inithook(struct hda_codec *codec)
-{
- alc663_two_hp_m8_speaker_automute(codec);
- alc_mic_automute(codec);
-}
-
-static void alc663_g71v_hp_automute(struct hda_codec *codec)
-{
- unsigned int present;
- unsigned char bits;
-
- present = snd_hda_jack_detect(codec, 0x21);
- bits = present ? HDA_AMP_MUTE : 0;
- snd_hda_codec_amp_stereo(codec, 0x15, HDA_OUTPUT, 0,
- HDA_AMP_MUTE, bits);
- snd_hda_codec_amp_stereo(codec, 0x14, HDA_OUTPUT, 0,
- HDA_AMP_MUTE, bits);
-}
-
-static void alc663_g71v_front_automute(struct hda_codec *codec)
-{
- unsigned int present;
- unsigned char bits;
-
- present = snd_hda_jack_detect(codec, 0x15);
- bits = present ? HDA_AMP_MUTE : 0;
- snd_hda_codec_amp_stereo(codec, 0x14, HDA_OUTPUT, 0,
- HDA_AMP_MUTE, bits);
-}
-
-static void alc663_g71v_unsol_event(struct hda_codec *codec,
- unsigned int res)
-{
- switch (res >> 26) {
- case ALC880_HP_EVENT:
- alc663_g71v_hp_automute(codec);
- break;
- case ALC880_FRONT_EVENT:
- alc663_g71v_front_automute(codec);
- break;
- case ALC880_MIC_EVENT:
- alc_mic_automute(codec);
- break;
- }
-}
-
-#define alc663_g71v_setup alc663_m51va_setup
-
-static void alc663_g71v_inithook(struct hda_codec *codec)
-{
- alc663_g71v_front_automute(codec);
- alc663_g71v_hp_automute(codec);
- alc_mic_automute(codec);
-}
-
-static void alc663_g50v_unsol_event(struct hda_codec *codec,
- unsigned int res)
-{
- switch (res >> 26) {
- case ALC880_HP_EVENT:
- alc663_m51va_speaker_automute(codec);
- break;
- case ALC880_MIC_EVENT:
- alc_mic_automute(codec);
- break;
- }
-}
-
-#define alc663_g50v_setup alc663_m51va_setup
-
-static void alc663_g50v_inithook(struct hda_codec *codec)
+static void alc272_fixup_mario(struct hda_codec *codec,
+ const struct hda_fixup *fix, int action)
{
- alc663_m51va_speaker_automute(codec);
- alc_mic_automute(codec);
+ if (action != HDA_FIXUP_ACT_PRE_PROBE)
+ return;
+ if (snd_hda_override_amp_caps(codec, 0x2, HDA_OUTPUT,
+ (0x3b << AC_AMPCAP_OFFSET_SHIFT) |
+ (0x3b << AC_AMPCAP_NUM_STEPS_SHIFT) |
+ (0x03 << AC_AMPCAP_STEP_SIZE_SHIFT) |
+ (0 << AC_AMPCAP_MUTE_SHIFT)))
+ codec_warn(codec, "failed to override amp caps for NID 0x2\n");
}
-static struct snd_kcontrol_new alc662_ecs_mixer[] = {
- HDA_CODEC_VOLUME("Master Playback Volume", 0x02, 0x0, HDA_OUTPUT),
- ALC262_HIPPO_MASTER_SWITCH,
-
- HDA_CODEC_VOLUME("Mic/LineIn Boost Volume", 0x18, 0, HDA_INPUT),
- HDA_CODEC_VOLUME("Mic/LineIn Playback Volume", 0x0b, 0x0, HDA_INPUT),
- HDA_CODEC_MUTE("Mic/LineIn Playback Switch", 0x0b, 0x0, HDA_INPUT),
-
- HDA_CODEC_VOLUME("Internal Mic Boost Volume", 0x19, 0, HDA_INPUT),
- HDA_CODEC_VOLUME("Internal Mic Playback Volume", 0x0b, 0x1, HDA_INPUT),
- HDA_CODEC_MUTE("Internal Mic Playback Switch", 0x0b, 0x1, HDA_INPUT),
- { } /* end */
-};
-
-static struct snd_kcontrol_new alc272_nc10_mixer[] = {
- /* Master Playback automatically created from Speaker and Headphone */
- HDA_CODEC_VOLUME("Speaker Playback Volume", 0x02, 0x0, HDA_OUTPUT),
- HDA_CODEC_MUTE("Speaker Playback Switch", 0x14, 0x0, HDA_OUTPUT),
- HDA_CODEC_VOLUME("Headphone Playback Volume", 0x03, 0x0, HDA_OUTPUT),
- HDA_CODEC_MUTE("Headphone Playback Switch", 0x21, 0x0, HDA_OUTPUT),
-
- HDA_CODEC_VOLUME("Mic Playback Volume", 0x0b, 0x0, HDA_INPUT),
- HDA_CODEC_MUTE("Mic Playback Switch", 0x0b, 0x0, HDA_INPUT),
- HDA_CODEC_VOLUME("Mic Boost Volume", 0x18, 0, HDA_INPUT),
-
- HDA_CODEC_VOLUME("Internal Mic Playback Volume", 0x0b, 0x1, HDA_INPUT),
- HDA_CODEC_MUTE("Internal Mic Playback Switch", 0x0b, 0x1, HDA_INPUT),
- HDA_CODEC_VOLUME("Internal Mic Boost Volume", 0x19, 0, HDA_INPUT),
- { } /* end */
-};
-
-#ifdef CONFIG_SND_HDA_POWER_SAVE
-#define alc662_loopbacks alc880_loopbacks
-#endif
-
-
-/* pcm configuration: identical with ALC880 */
-#define alc662_pcm_analog_playback alc880_pcm_analog_playback
-#define alc662_pcm_analog_capture alc880_pcm_analog_capture
-#define alc662_pcm_digital_playback alc880_pcm_digital_playback
-#define alc662_pcm_digital_capture alc880_pcm_digital_capture
-
-/*
- * configuration and preset
- */
-static const char * const alc662_models[ALC662_MODEL_LAST] = {
- [ALC662_3ST_2ch_DIG] = "3stack-dig",
- [ALC662_3ST_6ch_DIG] = "3stack-6ch-dig",
- [ALC662_3ST_6ch] = "3stack-6ch",
- [ALC662_5ST_DIG] = "6stack-dig",
- [ALC662_LENOVO_101E] = "lenovo-101e",
- [ALC662_ASUS_EEEPC_P701] = "eeepc-p701",
- [ALC662_ASUS_EEEPC_EP20] = "eeepc-ep20",
- [ALC662_ECS] = "ecs",
- [ALC663_ASUS_M51VA] = "m51va",
- [ALC663_ASUS_G71V] = "g71v",
- [ALC663_ASUS_H13] = "h13",
- [ALC663_ASUS_G50V] = "g50v",
- [ALC663_ASUS_MODE1] = "asus-mode1",
- [ALC662_ASUS_MODE2] = "asus-mode2",
- [ALC663_ASUS_MODE3] = "asus-mode3",
- [ALC663_ASUS_MODE4] = "asus-mode4",
- [ALC663_ASUS_MODE5] = "asus-mode5",
- [ALC663_ASUS_MODE6] = "asus-mode6",
- [ALC663_ASUS_MODE7] = "asus-mode7",
- [ALC663_ASUS_MODE8] = "asus-mode8",
- [ALC272_DELL] = "dell",
- [ALC272_DELL_ZM1] = "dell-zm1",
- [ALC272_SAMSUNG_NC10] = "samsung-nc10",
- [ALC662_AUTO] = "auto",
-};
-
-static struct snd_pci_quirk alc662_cfg_tbl[] = {
- SND_PCI_QUIRK(0x1019, 0x9087, "ECS", ALC662_ECS),
- SND_PCI_QUIRK(0x1028, 0x02d6, "DELL", ALC272_DELL),
- SND_PCI_QUIRK(0x1028, 0x02f4, "DELL ZM1", ALC272_DELL_ZM1),
- SND_PCI_QUIRK(0x1043, 0x1000, "ASUS N50Vm", ALC663_ASUS_MODE1),
- SND_PCI_QUIRK(0x1043, 0x1092, "ASUS NB", ALC663_ASUS_MODE3),
- SND_PCI_QUIRK(0x1043, 0x1173, "ASUS K73Jn", ALC663_ASUS_MODE1),
- SND_PCI_QUIRK(0x1043, 0x11c3, "ASUS M70V", ALC663_ASUS_MODE3),
- SND_PCI_QUIRK(0x1043, 0x11d3, "ASUS NB", ALC663_ASUS_MODE1),
- SND_PCI_QUIRK(0x1043, 0x11f3, "ASUS NB", ALC662_ASUS_MODE2),
- SND_PCI_QUIRK(0x1043, 0x1203, "ASUS NB", ALC663_ASUS_MODE1),
- SND_PCI_QUIRK(0x1043, 0x1303, "ASUS G60J", ALC663_ASUS_MODE1),
- SND_PCI_QUIRK(0x1043, 0x1333, "ASUS G60Jx", ALC663_ASUS_MODE1),
- SND_PCI_QUIRK(0x1043, 0x1339, "ASUS NB", ALC662_ASUS_MODE2),
- SND_PCI_QUIRK(0x1043, 0x13e3, "ASUS N71JA", ALC663_ASUS_MODE7),
- SND_PCI_QUIRK(0x1043, 0x1463, "ASUS N71", ALC663_ASUS_MODE7),
- SND_PCI_QUIRK(0x1043, 0x14d3, "ASUS G72", ALC663_ASUS_MODE8),
- SND_PCI_QUIRK(0x1043, 0x1563, "ASUS N90", ALC663_ASUS_MODE3),
- SND_PCI_QUIRK(0x1043, 0x15d3, "ASUS N50SF F50SF", ALC663_ASUS_MODE1),
- SND_PCI_QUIRK(0x1043, 0x16c3, "ASUS NB", ALC662_ASUS_MODE2),
- SND_PCI_QUIRK(0x1043, 0x16f3, "ASUS K40C K50C", ALC662_ASUS_MODE2),
- SND_PCI_QUIRK(0x1043, 0x1733, "ASUS N81De", ALC663_ASUS_MODE1),
- SND_PCI_QUIRK(0x1043, 0x1753, "ASUS NB", ALC662_ASUS_MODE2),
- SND_PCI_QUIRK(0x1043, 0x1763, "ASUS NB", ALC663_ASUS_MODE6),
- SND_PCI_QUIRK(0x1043, 0x1765, "ASUS NB", ALC663_ASUS_MODE6),
- SND_PCI_QUIRK(0x1043, 0x1783, "ASUS NB", ALC662_ASUS_MODE2),
- SND_PCI_QUIRK(0x1043, 0x1793, "ASUS F50GX", ALC663_ASUS_MODE1),
- SND_PCI_QUIRK(0x1043, 0x17b3, "ASUS F70SL", ALC663_ASUS_MODE3),
- SND_PCI_QUIRK(0x1043, 0x17c3, "ASUS UX20", ALC663_ASUS_M51VA),
- SND_PCI_QUIRK(0x1043, 0x17f3, "ASUS X58LE", ALC662_ASUS_MODE2),
- SND_PCI_QUIRK(0x1043, 0x1813, "ASUS NB", ALC662_ASUS_MODE2),
- SND_PCI_QUIRK(0x1043, 0x1823, "ASUS NB", ALC663_ASUS_MODE5),
- SND_PCI_QUIRK(0x1043, 0x1833, "ASUS NB", ALC663_ASUS_MODE6),
- SND_PCI_QUIRK(0x1043, 0x1843, "ASUS NB", ALC662_ASUS_MODE2),
- SND_PCI_QUIRK(0x1043, 0x1853, "ASUS F50Z", ALC663_ASUS_MODE1),
- SND_PCI_QUIRK(0x1043, 0x1864, "ASUS NB", ALC662_ASUS_MODE2),
- SND_PCI_QUIRK(0x1043, 0x1876, "ASUS NB", ALC662_ASUS_MODE2),
- SND_PCI_QUIRK(0x1043, 0x1878, "ASUS M51VA", ALC663_ASUS_M51VA),
- /*SND_PCI_QUIRK(0x1043, 0x1878, "ASUS M50Vr", ALC663_ASUS_MODE1),*/
- SND_PCI_QUIRK(0x1043, 0x1893, "ASUS M50Vm", ALC663_ASUS_MODE3),
- SND_PCI_QUIRK(0x1043, 0x1894, "ASUS X55", ALC663_ASUS_MODE3),
- SND_PCI_QUIRK(0x1043, 0x18b3, "ASUS N80Vc", ALC663_ASUS_MODE1),
- SND_PCI_QUIRK(0x1043, 0x18c3, "ASUS VX5", ALC663_ASUS_MODE1),
- SND_PCI_QUIRK(0x1043, 0x18d3, "ASUS N81Te", ALC663_ASUS_MODE1),
- SND_PCI_QUIRK(0x1043, 0x18f3, "ASUS N505Tp", ALC663_ASUS_MODE1),
- SND_PCI_QUIRK(0x1043, 0x1903, "ASUS F5GL", ALC663_ASUS_MODE1),
- SND_PCI_QUIRK(0x1043, 0x1913, "ASUS NB", ALC662_ASUS_MODE2),
- SND_PCI_QUIRK(0x1043, 0x1933, "ASUS F80Q", ALC662_ASUS_MODE2),
- SND_PCI_QUIRK(0x1043, 0x1943, "ASUS Vx3V", ALC663_ASUS_MODE1),
- SND_PCI_QUIRK(0x1043, 0x1953, "ASUS NB", ALC663_ASUS_MODE1),
- SND_PCI_QUIRK(0x1043, 0x1963, "ASUS X71C", ALC663_ASUS_MODE3),
- SND_PCI_QUIRK(0x1043, 0x1983, "ASUS N5051A", ALC663_ASUS_MODE1),
- SND_PCI_QUIRK(0x1043, 0x1993, "ASUS N20", ALC663_ASUS_MODE1),
- SND_PCI_QUIRK(0x1043, 0x19a3, "ASUS G50V", ALC663_ASUS_G50V),
- /*SND_PCI_QUIRK(0x1043, 0x19a3, "ASUS NB", ALC663_ASUS_MODE1),*/
- SND_PCI_QUIRK(0x1043, 0x19b3, "ASUS F7Z", ALC663_ASUS_MODE1),
- SND_PCI_QUIRK(0x1043, 0x19c3, "ASUS F5Z/F6x", ALC662_ASUS_MODE2),
- SND_PCI_QUIRK(0x1043, 0x19d3, "ASUS NB", ALC663_ASUS_M51VA),
- SND_PCI_QUIRK(0x1043, 0x19e3, "ASUS NB", ALC663_ASUS_MODE1),
- SND_PCI_QUIRK(0x1043, 0x19f3, "ASUS NB", ALC663_ASUS_MODE4),
- SND_PCI_QUIRK(0x1043, 0x8290, "ASUS P5GC-MX", ALC662_3ST_6ch_DIG),
- SND_PCI_QUIRK(0x1043, 0x82a1, "ASUS Eeepc", ALC662_ASUS_EEEPC_P701),
- SND_PCI_QUIRK(0x1043, 0x82d1, "ASUS Eeepc EP20", ALC662_ASUS_EEEPC_EP20),
- SND_PCI_QUIRK(0x105b, 0x0cd6, "Foxconn", ALC662_ECS),
- SND_PCI_QUIRK(0x105b, 0x0d47, "Foxconn 45CMX/45GMX/45CMX-K",
- ALC662_3ST_6ch_DIG),
- SND_PCI_QUIRK(0x1179, 0xff6e, "Toshiba NB20x", ALC662_AUTO),
- SND_PCI_QUIRK(0x144d, 0xca00, "Samsung NC10", ALC272_SAMSUNG_NC10),
- SND_PCI_QUIRK(0x152d, 0x2304, "Quanta WH1", ALC663_ASUS_H13),
- SND_PCI_QUIRK(0x1565, 0x820f, "Biostar TA780G M2+", ALC662_3ST_6ch_DIG),
- SND_PCI_QUIRK(0x1631, 0xc10c, "PB RS65", ALC663_ASUS_M51VA),
- SND_PCI_QUIRK(0x17aa, 0x101e, "Lenovo", ALC662_LENOVO_101E),
- SND_PCI_QUIRK(0x1849, 0x3662, "ASROCK K10N78FullHD-hSLI R3.0",
- ALC662_3ST_6ch_DIG),
- SND_PCI_QUIRK_MASK(0x1854, 0xf000, 0x2000, "ASUS H13-200x",
- ALC663_ASUS_H13),
- SND_PCI_QUIRK(0x1991, 0x5628, "Ordissimo EVE", ALC662_LENOVO_101E),
- {}
-};
-
-static struct alc_config_preset alc662_presets[] = {
- [ALC662_3ST_2ch_DIG] = {
- .mixers = { alc662_3ST_2ch_mixer },
- .init_verbs = { alc662_init_verbs },
- .num_dacs = ARRAY_SIZE(alc662_dac_nids),
- .dac_nids = alc662_dac_nids,
- .dig_out_nid = ALC662_DIGOUT_NID,
- .dig_in_nid = ALC662_DIGIN_NID,
- .num_channel_mode = ARRAY_SIZE(alc662_3ST_2ch_modes),
- .channel_mode = alc662_3ST_2ch_modes,
- .input_mux = &alc662_capture_source,
- },
- [ALC662_3ST_6ch_DIG] = {
- .mixers = { alc662_3ST_6ch_mixer, alc662_chmode_mixer },
- .init_verbs = { alc662_init_verbs },
- .num_dacs = ARRAY_SIZE(alc662_dac_nids),
- .dac_nids = alc662_dac_nids,
- .dig_out_nid = ALC662_DIGOUT_NID,
- .dig_in_nid = ALC662_DIGIN_NID,
- .num_channel_mode = ARRAY_SIZE(alc662_3ST_6ch_modes),
- .channel_mode = alc662_3ST_6ch_modes,
- .need_dac_fix = 1,
- .input_mux = &alc662_capture_source,
- },
- [ALC662_3ST_6ch] = {
- .mixers = { alc662_3ST_6ch_mixer, alc662_chmode_mixer },
- .init_verbs = { alc662_init_verbs },
- .num_dacs = ARRAY_SIZE(alc662_dac_nids),
- .dac_nids = alc662_dac_nids,
- .num_channel_mode = ARRAY_SIZE(alc662_3ST_6ch_modes),
- .channel_mode = alc662_3ST_6ch_modes,
- .need_dac_fix = 1,
- .input_mux = &alc662_capture_source,
- },
- [ALC662_5ST_DIG] = {
- .mixers = { alc662_base_mixer, alc662_chmode_mixer },
- .init_verbs = { alc662_init_verbs },
- .num_dacs = ARRAY_SIZE(alc662_dac_nids),
- .dac_nids = alc662_dac_nids,
- .dig_out_nid = ALC662_DIGOUT_NID,
- .dig_in_nid = ALC662_DIGIN_NID,
- .num_channel_mode = ARRAY_SIZE(alc662_5stack_modes),
- .channel_mode = alc662_5stack_modes,
- .input_mux = &alc662_capture_source,
- },
- [ALC662_LENOVO_101E] = {
- .mixers = { alc662_lenovo_101e_mixer },
- .init_verbs = { alc662_init_verbs, alc662_sue_init_verbs },
- .num_dacs = ARRAY_SIZE(alc662_dac_nids),
- .dac_nids = alc662_dac_nids,
- .num_channel_mode = ARRAY_SIZE(alc662_3ST_2ch_modes),
- .channel_mode = alc662_3ST_2ch_modes,
- .input_mux = &alc662_lenovo_101e_capture_source,
- .unsol_event = alc662_lenovo_101e_unsol_event,
- .init_hook = alc662_lenovo_101e_all_automute,
- },
- [ALC662_ASUS_EEEPC_P701] = {
- .mixers = { alc662_eeepc_p701_mixer },
- .init_verbs = { alc662_init_verbs,
- alc662_eeepc_sue_init_verbs },
- .num_dacs = ARRAY_SIZE(alc662_dac_nids),
- .dac_nids = alc662_dac_nids,
- .num_channel_mode = ARRAY_SIZE(alc662_3ST_2ch_modes),
- .channel_mode = alc662_3ST_2ch_modes,
- .unsol_event = alc662_eeepc_unsol_event,
- .setup = alc662_eeepc_setup,
- .init_hook = alc662_eeepc_inithook,
- },
- [ALC662_ASUS_EEEPC_EP20] = {
- .mixers = { alc662_eeepc_ep20_mixer,
- alc662_chmode_mixer },
- .init_verbs = { alc662_init_verbs,
- alc662_eeepc_ep20_sue_init_verbs },
- .num_dacs = ARRAY_SIZE(alc662_dac_nids),
- .dac_nids = alc662_dac_nids,
- .num_channel_mode = ARRAY_SIZE(alc662_3ST_6ch_modes),
- .channel_mode = alc662_3ST_6ch_modes,
- .input_mux = &alc662_lenovo_101e_capture_source,
- .unsol_event = alc662_eeepc_unsol_event,
- .setup = alc662_eeepc_ep20_setup,
- .init_hook = alc662_eeepc_ep20_inithook,
- },
- [ALC662_ECS] = {
- .mixers = { alc662_ecs_mixer },
- .init_verbs = { alc662_init_verbs,
- alc662_ecs_init_verbs },
- .num_dacs = ARRAY_SIZE(alc662_dac_nids),
- .dac_nids = alc662_dac_nids,
- .num_channel_mode = ARRAY_SIZE(alc662_3ST_2ch_modes),
- .channel_mode = alc662_3ST_2ch_modes,
- .unsol_event = alc662_eeepc_unsol_event,
- .setup = alc662_eeepc_setup,
- .init_hook = alc662_eeepc_inithook,
- },
- [ALC663_ASUS_M51VA] = {
- .mixers = { alc663_m51va_mixer },
- .init_verbs = { alc662_init_verbs, alc663_m51va_init_verbs },
- .num_dacs = ARRAY_SIZE(alc662_dac_nids),
- .dac_nids = alc662_dac_nids,
- .dig_out_nid = ALC662_DIGOUT_NID,
- .num_channel_mode = ARRAY_SIZE(alc662_3ST_2ch_modes),
- .channel_mode = alc662_3ST_2ch_modes,
- .unsol_event = alc663_m51va_unsol_event,
- .setup = alc663_m51va_setup,
- .init_hook = alc663_m51va_inithook,
- },
- [ALC663_ASUS_G71V] = {
- .mixers = { alc663_g71v_mixer },
- .init_verbs = { alc662_init_verbs, alc663_g71v_init_verbs },
- .num_dacs = ARRAY_SIZE(alc662_dac_nids),
- .dac_nids = alc662_dac_nids,
- .dig_out_nid = ALC662_DIGOUT_NID,
- .num_channel_mode = ARRAY_SIZE(alc662_3ST_2ch_modes),
- .channel_mode = alc662_3ST_2ch_modes,
- .unsol_event = alc663_g71v_unsol_event,
- .setup = alc663_g71v_setup,
- .init_hook = alc663_g71v_inithook,
- },
- [ALC663_ASUS_H13] = {
- .mixers = { alc663_m51va_mixer },
- .init_verbs = { alc662_init_verbs, alc663_m51va_init_verbs },
- .num_dacs = ARRAY_SIZE(alc662_dac_nids),
- .dac_nids = alc662_dac_nids,
- .num_channel_mode = ARRAY_SIZE(alc662_3ST_2ch_modes),
- .channel_mode = alc662_3ST_2ch_modes,
- .unsol_event = alc663_m51va_unsol_event,
- .init_hook = alc663_m51va_inithook,
- },
- [ALC663_ASUS_G50V] = {
- .mixers = { alc663_g50v_mixer },
- .init_verbs = { alc662_init_verbs, alc663_g50v_init_verbs },
- .num_dacs = ARRAY_SIZE(alc662_dac_nids),
- .dac_nids = alc662_dac_nids,
- .dig_out_nid = ALC662_DIGOUT_NID,
- .num_channel_mode = ARRAY_SIZE(alc662_3ST_6ch_modes),
- .channel_mode = alc662_3ST_6ch_modes,
- .input_mux = &alc663_capture_source,
- .unsol_event = alc663_g50v_unsol_event,
- .setup = alc663_g50v_setup,
- .init_hook = alc663_g50v_inithook,
- },
- [ALC663_ASUS_MODE1] = {
- .mixers = { alc663_m51va_mixer },
- .cap_mixer = alc662_auto_capture_mixer,
- .init_verbs = { alc662_init_verbs,
- alc663_21jd_amic_init_verbs },
- .num_dacs = ARRAY_SIZE(alc662_dac_nids),
- .hp_nid = 0x03,
- .dac_nids = alc662_dac_nids,
- .dig_out_nid = ALC662_DIGOUT_NID,
- .num_channel_mode = ARRAY_SIZE(alc662_3ST_2ch_modes),
- .channel_mode = alc662_3ST_2ch_modes,
- .unsol_event = alc663_mode1_unsol_event,
- .setup = alc663_mode1_setup,
- .init_hook = alc663_mode1_inithook,
- },
- [ALC662_ASUS_MODE2] = {
- .mixers = { alc662_1bjd_mixer },
- .cap_mixer = alc662_auto_capture_mixer,
- .init_verbs = { alc662_init_verbs,
- alc662_1bjd_amic_init_verbs },
- .num_dacs = ARRAY_SIZE(alc662_dac_nids),
- .dac_nids = alc662_dac_nids,
- .dig_out_nid = ALC662_DIGOUT_NID,
- .num_channel_mode = ARRAY_SIZE(alc662_3ST_2ch_modes),
- .channel_mode = alc662_3ST_2ch_modes,
- .unsol_event = alc662_mode2_unsol_event,
- .setup = alc662_mode2_setup,
- .init_hook = alc662_mode2_inithook,
- },
- [ALC663_ASUS_MODE3] = {
- .mixers = { alc663_two_hp_m1_mixer },
- .cap_mixer = alc662_auto_capture_mixer,
- .init_verbs = { alc662_init_verbs,
- alc663_two_hp_amic_m1_init_verbs },
- .num_dacs = ARRAY_SIZE(alc662_dac_nids),
- .hp_nid = 0x03,
- .dac_nids = alc662_dac_nids,
- .dig_out_nid = ALC662_DIGOUT_NID,
- .num_channel_mode = ARRAY_SIZE(alc662_3ST_2ch_modes),
- .channel_mode = alc662_3ST_2ch_modes,
- .unsol_event = alc663_mode3_unsol_event,
- .setup = alc663_mode3_setup,
- .init_hook = alc663_mode3_inithook,
- },
- [ALC663_ASUS_MODE4] = {
- .mixers = { alc663_asus_21jd_clfe_mixer },
- .cap_mixer = alc662_auto_capture_mixer,
- .init_verbs = { alc662_init_verbs,
- alc663_21jd_amic_init_verbs},
- .num_dacs = ARRAY_SIZE(alc662_dac_nids),
- .hp_nid = 0x03,
- .dac_nids = alc662_dac_nids,
- .dig_out_nid = ALC662_DIGOUT_NID,
- .num_channel_mode = ARRAY_SIZE(alc662_3ST_2ch_modes),
- .channel_mode = alc662_3ST_2ch_modes,
- .unsol_event = alc663_mode4_unsol_event,
- .setup = alc663_mode4_setup,
- .init_hook = alc663_mode4_inithook,
- },
- [ALC663_ASUS_MODE5] = {
- .mixers = { alc663_asus_15jd_clfe_mixer },
- .cap_mixer = alc662_auto_capture_mixer,
- .init_verbs = { alc662_init_verbs,
- alc663_15jd_amic_init_verbs },
- .num_dacs = ARRAY_SIZE(alc662_dac_nids),
- .hp_nid = 0x03,
- .dac_nids = alc662_dac_nids,
- .dig_out_nid = ALC662_DIGOUT_NID,
- .num_channel_mode = ARRAY_SIZE(alc662_3ST_2ch_modes),
- .channel_mode = alc662_3ST_2ch_modes,
- .unsol_event = alc663_mode5_unsol_event,
- .setup = alc663_mode5_setup,
- .init_hook = alc663_mode5_inithook,
- },
- [ALC663_ASUS_MODE6] = {
- .mixers = { alc663_two_hp_m2_mixer },
- .cap_mixer = alc662_auto_capture_mixer,
- .init_verbs = { alc662_init_verbs,
- alc663_two_hp_amic_m2_init_verbs },
- .num_dacs = ARRAY_SIZE(alc662_dac_nids),
- .hp_nid = 0x03,
- .dac_nids = alc662_dac_nids,
- .dig_out_nid = ALC662_DIGOUT_NID,
- .num_channel_mode = ARRAY_SIZE(alc662_3ST_2ch_modes),
- .channel_mode = alc662_3ST_2ch_modes,
- .unsol_event = alc663_mode6_unsol_event,
- .setup = alc663_mode6_setup,
- .init_hook = alc663_mode6_inithook,
- },
- [ALC663_ASUS_MODE7] = {
- .mixers = { alc663_mode7_mixer },
- .cap_mixer = alc662_auto_capture_mixer,
- .init_verbs = { alc662_init_verbs,
- alc663_mode7_init_verbs },
- .num_dacs = ARRAY_SIZE(alc662_dac_nids),
- .hp_nid = 0x03,
- .dac_nids = alc662_dac_nids,
- .dig_out_nid = ALC662_DIGOUT_NID,
- .num_channel_mode = ARRAY_SIZE(alc662_3ST_2ch_modes),
- .channel_mode = alc662_3ST_2ch_modes,
- .unsol_event = alc663_mode7_unsol_event,
- .setup = alc663_mode7_setup,
- .init_hook = alc663_mode7_inithook,
- },
- [ALC663_ASUS_MODE8] = {
- .mixers = { alc663_mode8_mixer },
- .cap_mixer = alc662_auto_capture_mixer,
- .init_verbs = { alc662_init_verbs,
- alc663_mode8_init_verbs },
- .num_dacs = ARRAY_SIZE(alc662_dac_nids),
- .hp_nid = 0x03,
- .dac_nids = alc662_dac_nids,
- .dig_out_nid = ALC662_DIGOUT_NID,
- .num_channel_mode = ARRAY_SIZE(alc662_3ST_2ch_modes),
- .channel_mode = alc662_3ST_2ch_modes,
- .unsol_event = alc663_mode8_unsol_event,
- .setup = alc663_mode8_setup,
- .init_hook = alc663_mode8_inithook,
- },
- [ALC272_DELL] = {
- .mixers = { alc663_m51va_mixer },
- .cap_mixer = alc272_auto_capture_mixer,
- .init_verbs = { alc662_init_verbs, alc272_dell_init_verbs },
- .num_dacs = ARRAY_SIZE(alc272_dac_nids),
- .dac_nids = alc662_dac_nids,
- .num_channel_mode = ARRAY_SIZE(alc662_3ST_2ch_modes),
- .adc_nids = alc272_adc_nids,
- .num_adc_nids = ARRAY_SIZE(alc272_adc_nids),
- .capsrc_nids = alc272_capsrc_nids,
- .channel_mode = alc662_3ST_2ch_modes,
- .unsol_event = alc663_m51va_unsol_event,
- .setup = alc663_m51va_setup,
- .init_hook = alc663_m51va_inithook,
- },
- [ALC272_DELL_ZM1] = {
- .mixers = { alc663_m51va_mixer },
- .cap_mixer = alc662_auto_capture_mixer,
- .init_verbs = { alc662_init_verbs, alc272_dell_zm1_init_verbs },
- .num_dacs = ARRAY_SIZE(alc272_dac_nids),
- .dac_nids = alc662_dac_nids,
- .num_channel_mode = ARRAY_SIZE(alc662_3ST_2ch_modes),
- .adc_nids = alc662_adc_nids,
- .num_adc_nids = 1,
- .capsrc_nids = alc662_capsrc_nids,
- .channel_mode = alc662_3ST_2ch_modes,
- .unsol_event = alc663_m51va_unsol_event,
- .setup = alc663_m51va_setup,
- .init_hook = alc663_m51va_inithook,
- },
- [ALC272_SAMSUNG_NC10] = {
- .mixers = { alc272_nc10_mixer },
- .init_verbs = { alc662_init_verbs,
- alc663_21jd_amic_init_verbs },
- .num_dacs = ARRAY_SIZE(alc272_dac_nids),
- .dac_nids = alc272_dac_nids,
- .num_channel_mode = ARRAY_SIZE(alc662_3ST_2ch_modes),
- .channel_mode = alc662_3ST_2ch_modes,
- /*.input_mux = &alc272_nc10_capture_source,*/
- .unsol_event = alc663_mode4_unsol_event,
- .setup = alc663_mode4_setup,
- .init_hook = alc663_mode4_inithook,
- },
+static const struct snd_pcm_chmap_elem asus_pcm_2_1_chmaps[] = {
+ { .channels = 2,
+ .map = { SNDRV_CHMAP_FL, SNDRV_CHMAP_FR } },
+ { .channels = 4,
+ .map = { SNDRV_CHMAP_FL, SNDRV_CHMAP_FR,
+ SNDRV_CHMAP_NA, SNDRV_CHMAP_LFE } }, /* LFE only on right */
+ { }
};
-
-/*
- * BIOS auto configuration
- */
-
-/* convert from MIX nid to DAC */
-static inline hda_nid_t alc662_mix_to_dac(hda_nid_t nid)
-{
- if (nid == 0x0f)
- return 0x02;
- else if (nid >= 0x0c && nid <= 0x0e)
- return nid - 0x0c + 0x02;
- else if (nid == 0x26) /* ALC887-VD has this DAC too */
- return 0x25;
- else
- return 0;
-}
-
-/* get MIX nid connected to the given pin targeted to DAC */
-static hda_nid_t alc662_dac_to_mix(struct hda_codec *codec, hda_nid_t pin,
- hda_nid_t dac)
+/* override the 2.1 chmap */
+static void alc_fixup_bass_chmap(struct hda_codec *codec,
+ const struct hda_fixup *fix, int action)
{
- hda_nid_t mix[5];
- int i, num;
-
- num = snd_hda_get_connections(codec, pin, mix, ARRAY_SIZE(mix));
- for (i = 0; i < num; i++) {
- if (alc662_mix_to_dac(mix[i]) == dac)
- return mix[i];
+ if (action == HDA_FIXUP_ACT_BUILD) {
+ struct alc_spec *spec = codec->spec;
+ spec->gen.pcm_rec[0].stream[0].chmap = asus_pcm_2_1_chmaps;
}
- return 0;
}
-/* look for an empty DAC slot */
-static hda_nid_t alc662_look_for_dac(struct hda_codec *codec, hda_nid_t pin)
+/* turn on/off mute LED per vmaster hook */
+static void alc662_led_gpio1_mute_hook(void *private_data, int enabled)
{
+ struct hda_codec *codec = private_data;
struct alc_spec *spec = codec->spec;
- hda_nid_t srcs[5];
- int i, j, num;
+ unsigned int oldval = spec->gpio_led;
- num = snd_hda_get_connections(codec, pin, srcs, ARRAY_SIZE(srcs));
- if (num < 0)
- return 0;
- for (i = 0; i < num; i++) {
- hda_nid_t nid = alc662_mix_to_dac(srcs[i]);
- if (!nid)
- continue;
- for (j = 0; j < spec->multiout.num_dacs; j++)
- if (spec->multiout.dac_nids[j] == nid)
- break;
- if (j >= spec->multiout.num_dacs)
- return nid;
- }
- return 0;
+ if (enabled)
+ spec->gpio_led &= ~0x01;
+ else
+ spec->gpio_led |= 0x01;
+ if (spec->gpio_led != oldval)
+ snd_hda_codec_write(codec, 0x01, 0, AC_VERB_SET_GPIO_DATA,
+ spec->gpio_led);
}
-/* fill in the dac_nids table from the parsed pin configuration */
-static int alc662_auto_fill_dac_nids(struct hda_codec *codec,
- const struct auto_pin_cfg *cfg)
+/* avoid D3 for keeping GPIO up */
+static unsigned int gpio_led_power_filter(struct hda_codec *codec,
+ hda_nid_t nid,
+ unsigned int power_state)
{
struct alc_spec *spec = codec->spec;
- int i;
- hda_nid_t dac;
-
- spec->multiout.dac_nids = spec->private_dac_nids;
- for (i = 0; i < cfg->line_outs; i++) {
- dac = alc662_look_for_dac(codec, cfg->line_out_pins[i]);
- if (!dac)
- continue;
- spec->multiout.dac_nids[spec->multiout.num_dacs++] = dac;
- }
- return 0;
+ if (nid == codec->afg && power_state == AC_PWRST_D3 && spec->gpio_led)
+ return AC_PWRST_D0;
+ return power_state;
}
-static inline int __alc662_add_vol_ctl(struct alc_spec *spec, const char *pfx,
- hda_nid_t nid, int idx, unsigned int chs)
-{
- return __add_pb_vol_ctrl(spec, ALC_CTL_WIDGET_VOL, pfx, idx,
- HDA_COMPOSE_AMP_VAL(nid, chs, 0, HDA_OUTPUT));
-}
-
-static inline int __alc662_add_sw_ctl(struct alc_spec *spec, const char *pfx,
- hda_nid_t nid, int idx, unsigned int chs)
-{
- return __add_pb_sw_ctrl(spec, ALC_CTL_WIDGET_MUTE, pfx, idx,
- HDA_COMPOSE_AMP_VAL(nid, chs, 0, HDA_INPUT));
-}
-
-#define alc662_add_vol_ctl(spec, pfx, nid, chs) \
- __alc662_add_vol_ctl(spec, pfx, nid, 0, chs)
-#define alc662_add_sw_ctl(spec, pfx, nid, chs) \
- __alc662_add_sw_ctl(spec, pfx, nid, 0, chs)
-#define alc662_add_stereo_vol(spec, pfx, nid) \
- alc662_add_vol_ctl(spec, pfx, nid, 3)
-#define alc662_add_stereo_sw(spec, pfx, nid) \
- alc662_add_sw_ctl(spec, pfx, nid, 3)
-
-/* add playback controls from the parsed DAC table */
-static int alc662_auto_create_multi_out_ctls(struct hda_codec *codec,
- const struct auto_pin_cfg *cfg)
+static void alc662_fixup_led_gpio1(struct hda_codec *codec,
+ const struct hda_fixup *fix, int action)
{
struct alc_spec *spec = codec->spec;
- static const char * const chname[4] = {
- "Front", "Surround", NULL /*CLFE*/, "Side"
+ static const struct hda_verb gpio_init[] = {
+ { 0x01, AC_VERB_SET_GPIO_MASK, 0x01 },
+ { 0x01, AC_VERB_SET_GPIO_DIRECTION, 0x01 },
+ {}
};
- const char *pfx = alc_get_line_out_pfx(cfg, true);
- hda_nid_t nid, mix;
- int i, err;
- for (i = 0; i < cfg->line_outs; i++) {
- nid = spec->multiout.dac_nids[i];
- if (!nid)
- continue;
- mix = alc662_dac_to_mix(codec, cfg->line_out_pins[i], nid);
- if (!mix)
- continue;
- if (!pfx && i == 2) {
- /* Center/LFE */
- err = alc662_add_vol_ctl(spec, "Center", nid, 1);
- if (err < 0)
- return err;
- err = alc662_add_vol_ctl(spec, "LFE", nid, 2);
- if (err < 0)
- return err;
- err = alc662_add_sw_ctl(spec, "Center", mix, 1);
- if (err < 0)
- return err;
- err = alc662_add_sw_ctl(spec, "LFE", mix, 2);
- if (err < 0)
- return err;
- } else {
- const char *name = pfx;
- int index = i;
- if (!name) {
- name = chname[i];
- index = 0;
- }
- err = __alc662_add_vol_ctl(spec, name, nid, index, 3);
- if (err < 0)
- return err;
- err = __alc662_add_sw_ctl(spec, name, mix, index, 3);
- if (err < 0)
- return err;
- }
+ if (action == HDA_FIXUP_ACT_PRE_PROBE) {
+ spec->gen.vmaster_mute.hook = alc662_led_gpio1_mute_hook;
+ spec->gpio_led = 0;
+ snd_hda_add_verbs(codec, gpio_init);
+ codec->power_filter = gpio_led_power_filter;
}
- return 0;
-}
-
-/* add playback controls for speaker and HP outputs */
-/* return DAC nid if any new DAC is assigned */
-static int alc662_auto_create_extra_out(struct hda_codec *codec, hda_nid_t pin,
- const char *pfx)
-{
- struct alc_spec *spec = codec->spec;
- hda_nid_t nid, mix;
- int err;
-
- if (!pin)
- return 0;
- nid = alc662_look_for_dac(codec, pin);
- if (!nid) {
- /* the corresponding DAC is already occupied */
- if (!(get_wcaps(codec, pin) & AC_WCAP_OUT_AMP))
- return 0; /* no way */
- /* create a switch only */
- return add_pb_sw_ctrl(spec, ALC_CTL_WIDGET_MUTE, pfx,
- HDA_COMPOSE_AMP_VAL(pin, 3, 0, HDA_OUTPUT));
- }
-
- mix = alc662_dac_to_mix(codec, pin, nid);
- if (!mix)
- return 0;
- err = alc662_add_vol_ctl(spec, pfx, nid, 3);
- if (err < 0)
- return err;
- err = alc662_add_sw_ctl(spec, pfx, mix, 3);
- if (err < 0)
- return err;
- return nid;
-}
-
-/* create playback/capture controls for input pins */
-#define alc662_auto_create_input_ctls \
- alc882_auto_create_input_ctls
-
-static void alc662_auto_set_output_and_unmute(struct hda_codec *codec,
- hda_nid_t nid, int pin_type,
- hda_nid_t dac)
-{
- int i, num;
- hda_nid_t srcs[HDA_MAX_CONNECTIONS];
-
- alc_set_pin_output(codec, nid, pin_type);
- /* need the manual connection? */
- num = snd_hda_get_connections(codec, nid, srcs, ARRAY_SIZE(srcs));
- if (num <= 1)
- return;
- for (i = 0; i < num; i++) {
- if (alc662_mix_to_dac(srcs[i]) != dac)
- continue;
- snd_hda_codec_write(codec, nid, 0, AC_VERB_SET_CONNECT_SEL, i);
- return;
- }
-}
-
-static void alc662_auto_init_multi_out(struct hda_codec *codec)
-{
- struct alc_spec *spec = codec->spec;
- int pin_type = get_pin_type(spec->autocfg.line_out_type);
- int i;
-
- for (i = 0; i <= HDA_SIDE; i++) {
- hda_nid_t nid = spec->autocfg.line_out_pins[i];
- if (nid)
- alc662_auto_set_output_and_unmute(codec, nid, pin_type,
- spec->multiout.dac_nids[i]);
- }
-}
-
-static void alc662_auto_init_hp_out(struct hda_codec *codec)
-{
- struct alc_spec *spec = codec->spec;
- hda_nid_t pin;
-
- pin = spec->autocfg.hp_pins[0];
- if (pin)
- alc662_auto_set_output_and_unmute(codec, pin, PIN_HP,
- spec->multiout.hp_nid);
- pin = spec->autocfg.speaker_pins[0];
- if (pin)
- alc662_auto_set_output_and_unmute(codec, pin, PIN_OUT,
- spec->multiout.extra_out_nid[0]);
-}
-
-#define ALC662_PIN_CD_NID ALC880_PIN_CD_NID
-
-static void alc662_auto_init_analog_input(struct hda_codec *codec)
-{
- struct alc_spec *spec = codec->spec;
- struct auto_pin_cfg *cfg = &spec->autocfg;
- int i;
-
- for (i = 0; i < cfg->num_inputs; i++) {
- hda_nid_t nid = cfg->inputs[i].pin;
- if (alc_is_input_pin(codec, nid)) {
- alc_set_input_pin(codec, nid, cfg->inputs[i].type);
- if (nid != ALC662_PIN_CD_NID &&
- (get_wcaps(codec, nid) & AC_WCAP_OUT_AMP))
- snd_hda_codec_write(codec, nid, 0,
- AC_VERB_SET_AMP_GAIN_MUTE,
- AMP_OUT_MUTE);
- }
- }
-}
-
-#define alc662_auto_init_input_src alc882_auto_init_input_src
-
-static int alc662_parse_auto_config(struct hda_codec *codec)
-{
- struct alc_spec *spec = codec->spec;
- int err;
- static hda_nid_t alc662_ignore[] = { 0x1d, 0 };
-
- err = snd_hda_parse_pin_def_config(codec, &spec->autocfg,
- alc662_ignore);
- if (err < 0)
- return err;
- if (!spec->autocfg.line_outs)
- return 0; /* can't find valid BIOS pin config */
-
- err = alc662_auto_fill_dac_nids(codec, &spec->autocfg);
- if (err < 0)
- return err;
- err = alc662_auto_create_multi_out_ctls(codec, &spec->autocfg);
- if (err < 0)
- return err;
- err = alc662_auto_create_extra_out(codec,
- spec->autocfg.speaker_pins[0],
- "Speaker");
- if (err < 0)
- return err;
- if (err)
- spec->multiout.extra_out_nid[0] = err;
- err = alc662_auto_create_extra_out(codec, spec->autocfg.hp_pins[0],
- "Headphone");
- if (err < 0)
- return err;
- if (err)
- spec->multiout.hp_nid = err;
- err = alc662_auto_create_input_ctls(codec, &spec->autocfg);
- if (err < 0)
- return err;
-
- spec->multiout.max_channels = spec->multiout.num_dacs * 2;
-
- alc_auto_parse_digital(codec);
-
- if (spec->kctls.list)
- add_mixer(spec, spec->kctls.list);
-
- spec->num_mux_defs = 1;
- spec->input_mux = &spec->private_imux[0];
-
- add_verb(spec, alc662_init_verbs);
- if (codec->vendor_id == 0x10ec0272 || codec->vendor_id == 0x10ec0663 ||
- codec->vendor_id == 0x10ec0665 || codec->vendor_id == 0x10ec0670)
- add_verb(spec, alc663_init_verbs);
-
- if (codec->vendor_id == 0x10ec0272)
- add_verb(spec, alc272_init_verbs);
-
- err = alc_auto_add_mic_boost(codec);
- if (err < 0)
- return err;
-
- if (codec->vendor_id == 0x10ec0272 || codec->vendor_id == 0x10ec0663 ||
- codec->vendor_id == 0x10ec0665 || codec->vendor_id == 0x10ec0670)
- alc_ssid_check(codec, 0x15, 0x1b, 0x14, 0x21);
- else
- alc_ssid_check(codec, 0x15, 0x1b, 0x14, 0);
-
- return 1;
-}
-
-/* additional initialization for auto-configuration model */
-static void alc662_auto_init(struct hda_codec *codec)
-{
- struct alc_spec *spec = codec->spec;
- alc662_auto_init_multi_out(codec);
- alc662_auto_init_hp_out(codec);
- alc662_auto_init_analog_input(codec);
- alc662_auto_init_input_src(codec);
- alc_auto_init_digital(codec);
- if (spec->unsol_event)
- alc_inithook(codec);
-}
-
-static void alc272_fixup_mario(struct hda_codec *codec,
- const struct alc_fixup *fix, int action)
-{
- if (action != ALC_FIXUP_ACT_PROBE)
- return;
- if (snd_hda_override_amp_caps(codec, 0x2, HDA_OUTPUT,
- (0x3b << AC_AMPCAP_OFFSET_SHIFT) |
- (0x3b << AC_AMPCAP_NUM_STEPS_SHIFT) |
- (0x03 << AC_AMPCAP_STEP_SIZE_SHIFT) |
- (0 << AC_AMPCAP_MUTE_SHIFT)))
- printk(KERN_WARNING
- "hda_codec: failed to override amp caps for NID 0x2\n");
}
enum {
ALC662_FIXUP_ASPIRE,
+ ALC662_FIXUP_LED_GPIO1,
ALC662_FIXUP_IDEAPAD,
ALC272_FIXUP_MARIO,
ALC662_FIXUP_CZC_P10T,
- ALC662_FIXUP_GIGABYTE,
-};
-
-static const struct alc_fixup alc662_fixups[] = {
+ ALC662_FIXUP_SKU_IGNORE,
+ ALC662_FIXUP_HP_RP5800,
+ ALC662_FIXUP_ASUS_MODE1,
+ ALC662_FIXUP_ASUS_MODE2,
+ ALC662_FIXUP_ASUS_MODE3,
+ ALC662_FIXUP_ASUS_MODE4,
+ ALC662_FIXUP_ASUS_MODE5,
+ ALC662_FIXUP_ASUS_MODE6,
+ ALC662_FIXUP_ASUS_MODE7,
+ ALC662_FIXUP_ASUS_MODE8,
+ ALC662_FIXUP_NO_JACK_DETECT,
+ ALC662_FIXUP_ZOTAC_Z68,
+ ALC662_FIXUP_INV_DMIC,
+ ALC668_FIXUP_DELL_MIC_NO_PRESENCE,
+ ALC668_FIXUP_HEADSET_MODE,
+ ALC662_FIXUP_BASS_MODE4_CHMAP,
+ ALC662_FIXUP_BASS_16,
+ ALC662_FIXUP_BASS_1A,
+ ALC662_FIXUP_BASS_CHMAP,
+ ALC668_FIXUP_AUTO_MUTE,
+ ALC668_FIXUP_DELL_DISABLE_AAMIX,
+ ALC668_FIXUP_DELL_XPS13,
+};
+
+static const struct hda_fixup alc662_fixups[] = {
[ALC662_FIXUP_ASPIRE] = {
- .type = ALC_FIXUP_PINS,
- .v.pins = (const struct alc_pincfg[]) {
+ .type = HDA_FIXUP_PINS,
+ .v.pins = (const struct hda_pintbl[]) {
{ 0x15, 0x99130112 }, /* subwoofer */
{ }
}
},
+ [ALC662_FIXUP_LED_GPIO1] = {
+ .type = HDA_FIXUP_FUNC,
+ .v.func = alc662_fixup_led_gpio1,
+ },
[ALC662_FIXUP_IDEAPAD] = {
- .type = ALC_FIXUP_PINS,
- .v.pins = (const struct alc_pincfg[]) {
+ .type = HDA_FIXUP_PINS,
+ .v.pins = (const struct hda_pintbl[]) {
{ 0x17, 0x99130112 }, /* subwoofer */
{ }
- }
+ },
+ .chained = true,
+ .chain_id = ALC662_FIXUP_LED_GPIO1,
},
[ALC272_FIXUP_MARIO] = {
- .type = ALC_FIXUP_FUNC,
+ .type = HDA_FIXUP_FUNC,
.v.func = alc272_fixup_mario,
},
[ALC662_FIXUP_CZC_P10T] = {
- .type = ALC_FIXUP_VERBS,
+ .type = HDA_FIXUP_VERBS,
.v.verbs = (const struct hda_verb[]) {
{0x14, AC_VERB_SET_EAPD_BTLENABLE, 0},
{}
}
},
- [ALC662_FIXUP_GIGABYTE] = {
- .type = ALC_FIXUP_PINS,
- .v.pins = (const struct alc_pincfg[]) {
- { 0x14, 0x1114410 }, /* set as speaker */
+ [ALC662_FIXUP_SKU_IGNORE] = {
+ .type = HDA_FIXUP_FUNC,
+ .v.func = alc_fixup_sku_ignore,
+ },
+ [ALC662_FIXUP_HP_RP5800] = {
+ .type = HDA_FIXUP_PINS,
+ .v.pins = (const struct hda_pintbl[]) {
+ { 0x14, 0x0221201f }, /* HP out */
+ { }
+ },
+ .chained = true,
+ .chain_id = ALC662_FIXUP_SKU_IGNORE
+ },
+ [ALC662_FIXUP_ASUS_MODE1] = {
+ .type = HDA_FIXUP_PINS,
+ .v.pins = (const struct hda_pintbl[]) {
+ { 0x14, 0x99130110 }, /* speaker */
+ { 0x18, 0x01a19c20 }, /* mic */
+ { 0x19, 0x99a3092f }, /* int-mic */
+ { 0x21, 0x0121401f }, /* HP out */
+ { }
+ },
+ .chained = true,
+ .chain_id = ALC662_FIXUP_SKU_IGNORE
+ },
+ [ALC662_FIXUP_ASUS_MODE2] = {
+ .type = HDA_FIXUP_PINS,
+ .v.pins = (const struct hda_pintbl[]) {
+ { 0x14, 0x99130110 }, /* speaker */
+ { 0x18, 0x01a19820 }, /* mic */
+ { 0x19, 0x99a3092f }, /* int-mic */
+ { 0x1b, 0x0121401f }, /* HP out */
+ { }
+ },
+ .chained = true,
+ .chain_id = ALC662_FIXUP_SKU_IGNORE
+ },
+ [ALC662_FIXUP_ASUS_MODE3] = {
+ .type = HDA_FIXUP_PINS,
+ .v.pins = (const struct hda_pintbl[]) {
+ { 0x14, 0x99130110 }, /* speaker */
+ { 0x15, 0x0121441f }, /* HP */
+ { 0x18, 0x01a19840 }, /* mic */
+ { 0x19, 0x99a3094f }, /* int-mic */
+ { 0x21, 0x01211420 }, /* HP2 */
+ { }
+ },
+ .chained = true,
+ .chain_id = ALC662_FIXUP_SKU_IGNORE
+ },
+ [ALC662_FIXUP_ASUS_MODE4] = {
+ .type = HDA_FIXUP_PINS,
+ .v.pins = (const struct hda_pintbl[]) {
+ { 0x14, 0x99130110 }, /* speaker */
+ { 0x16, 0x99130111 }, /* speaker */
+ { 0x18, 0x01a19840 }, /* mic */
+ { 0x19, 0x99a3094f }, /* int-mic */
+ { 0x21, 0x0121441f }, /* HP */
+ { }
+ },
+ .chained = true,
+ .chain_id = ALC662_FIXUP_SKU_IGNORE
+ },
+ [ALC662_FIXUP_ASUS_MODE5] = {
+ .type = HDA_FIXUP_PINS,
+ .v.pins = (const struct hda_pintbl[]) {
+ { 0x14, 0x99130110 }, /* speaker */
+ { 0x15, 0x0121441f }, /* HP */
+ { 0x16, 0x99130111 }, /* speaker */
+ { 0x18, 0x01a19840 }, /* mic */
+ { 0x19, 0x99a3094f }, /* int-mic */
+ { }
+ },
+ .chained = true,
+ .chain_id = ALC662_FIXUP_SKU_IGNORE
+ },
+ [ALC662_FIXUP_ASUS_MODE6] = {
+ .type = HDA_FIXUP_PINS,
+ .v.pins = (const struct hda_pintbl[]) {
+ { 0x14, 0x99130110 }, /* speaker */
+ { 0x15, 0x01211420 }, /* HP2 */
+ { 0x18, 0x01a19840 }, /* mic */
+ { 0x19, 0x99a3094f }, /* int-mic */
+ { 0x1b, 0x0121441f }, /* HP */
+ { }
+ },
+ .chained = true,
+ .chain_id = ALC662_FIXUP_SKU_IGNORE
+ },
+ [ALC662_FIXUP_ASUS_MODE7] = {
+ .type = HDA_FIXUP_PINS,
+ .v.pins = (const struct hda_pintbl[]) {
+ { 0x14, 0x99130110 }, /* speaker */
+ { 0x17, 0x99130111 }, /* speaker */
+ { 0x18, 0x01a19840 }, /* mic */
+ { 0x19, 0x99a3094f }, /* int-mic */
+ { 0x1b, 0x01214020 }, /* HP */
+ { 0x21, 0x0121401f }, /* HP */
+ { }
+ },
+ .chained = true,
+ .chain_id = ALC662_FIXUP_SKU_IGNORE
+ },
+ [ALC662_FIXUP_ASUS_MODE8] = {
+ .type = HDA_FIXUP_PINS,
+ .v.pins = (const struct hda_pintbl[]) {
+ { 0x14, 0x99130110 }, /* speaker */
+ { 0x12, 0x99a30970 }, /* int-mic */
+ { 0x15, 0x01214020 }, /* HP */
+ { 0x17, 0x99130111 }, /* speaker */
+ { 0x18, 0x01a19840 }, /* mic */
+ { 0x21, 0x0121401f }, /* HP */
+ { }
+ },
+ .chained = true,
+ .chain_id = ALC662_FIXUP_SKU_IGNORE
+ },
+ [ALC662_FIXUP_NO_JACK_DETECT] = {
+ .type = HDA_FIXUP_FUNC,
+ .v.func = alc_fixup_no_jack_detect,
+ },
+ [ALC662_FIXUP_ZOTAC_Z68] = {
+ .type = HDA_FIXUP_PINS,
+ .v.pins = (const struct hda_pintbl[]) {
+ { 0x1b, 0x02214020 }, /* Front HP */
{ }
}
},
+ [ALC662_FIXUP_INV_DMIC] = {
+ .type = HDA_FIXUP_FUNC,
+ .v.func = alc_fixup_inv_dmic_0x12,
+ },
+ [ALC668_FIXUP_DELL_XPS13] = {
+ .type = HDA_FIXUP_FUNC,
+ .v.func = alc_fixup_dell_xps13,
+ .chained = true,
+ .chain_id = ALC668_FIXUP_DELL_DISABLE_AAMIX
+ },
+ [ALC668_FIXUP_DELL_DISABLE_AAMIX] = {
+ .type = HDA_FIXUP_FUNC,
+ .v.func = alc_fixup_disable_aamix,
+ .chained = true,
+ .chain_id = ALC668_FIXUP_DELL_MIC_NO_PRESENCE
+ },
+ [ALC668_FIXUP_AUTO_MUTE] = {
+ .type = HDA_FIXUP_FUNC,
+ .v.func = alc_fixup_auto_mute_via_amp,
+ .chained = true,
+ .chain_id = ALC668_FIXUP_DELL_MIC_NO_PRESENCE
+ },
+ [ALC668_FIXUP_DELL_MIC_NO_PRESENCE] = {
+ .type = HDA_FIXUP_PINS,
+ .v.pins = (const struct hda_pintbl[]) {
+ { 0x19, 0x03a1913d }, /* use as headphone mic, without its own jack detect */
+ { 0x1b, 0x03a1113c }, /* use as headset mic, without its own jack detect */
+ { }
+ },
+ .chained = true,
+ .chain_id = ALC668_FIXUP_HEADSET_MODE
+ },
+ [ALC668_FIXUP_HEADSET_MODE] = {
+ .type = HDA_FIXUP_FUNC,
+ .v.func = alc_fixup_headset_mode_alc668,
+ },
+ [ALC662_FIXUP_BASS_MODE4_CHMAP] = {
+ .type = HDA_FIXUP_FUNC,
+ .v.func = alc_fixup_bass_chmap,
+ .chained = true,
+ .chain_id = ALC662_FIXUP_ASUS_MODE4
+ },
+ [ALC662_FIXUP_BASS_16] = {
+ .type = HDA_FIXUP_PINS,
+ .v.pins = (const struct hda_pintbl[]) {
+ {0x16, 0x80106111}, /* bass speaker */
+ {}
+ },
+ .chained = true,
+ .chain_id = ALC662_FIXUP_BASS_CHMAP,
+ },
+ [ALC662_FIXUP_BASS_1A] = {
+ .type = HDA_FIXUP_PINS,
+ .v.pins = (const struct hda_pintbl[]) {
+ {0x1a, 0x80106111}, /* bass speaker */
+ {}
+ },
+ .chained = true,
+ .chain_id = ALC662_FIXUP_BASS_CHMAP,
+ },
+ [ALC662_FIXUP_BASS_CHMAP] = {
+ .type = HDA_FIXUP_FUNC,
+ .v.func = alc_fixup_bass_chmap,
+ },
};
-static struct snd_pci_quirk alc662_fixup_tbl[] = {
+static const struct snd_pci_quirk alc662_fixup_tbl[] = {
+ SND_PCI_QUIRK(0x1019, 0x9087, "ECS", ALC662_FIXUP_ASUS_MODE2),
+ SND_PCI_QUIRK(0x1025, 0x022f, "Acer Aspire One", ALC662_FIXUP_INV_DMIC),
SND_PCI_QUIRK(0x1025, 0x0308, "Acer Aspire 8942G", ALC662_FIXUP_ASPIRE),
+ SND_PCI_QUIRK(0x1025, 0x031c, "Gateway NV79", ALC662_FIXUP_SKU_IGNORE),
+ SND_PCI_QUIRK(0x1025, 0x0349, "eMachines eM250", ALC662_FIXUP_INV_DMIC),
+ SND_PCI_QUIRK(0x1025, 0x034a, "Gateway LT27", ALC662_FIXUP_INV_DMIC),
SND_PCI_QUIRK(0x1025, 0x038b, "Acer Aspire 8943G", ALC662_FIXUP_ASPIRE),
+ SND_PCI_QUIRK(0x1028, 0x05d8, "Dell", ALC668_FIXUP_DELL_MIC_NO_PRESENCE),
+ SND_PCI_QUIRK(0x1028, 0x05db, "Dell", ALC668_FIXUP_DELL_MIC_NO_PRESENCE),
+ SND_PCI_QUIRK(0x1028, 0x060a, "Dell XPS 13", ALC668_FIXUP_DELL_XPS13),
+ SND_PCI_QUIRK(0x1028, 0x0625, "Dell", ALC668_FIXUP_DELL_MIC_NO_PRESENCE),
+ SND_PCI_QUIRK(0x1028, 0x0626, "Dell", ALC668_FIXUP_DELL_MIC_NO_PRESENCE),
+ SND_PCI_QUIRK(0x1028, 0x0696, "Dell", ALC668_FIXUP_DELL_MIC_NO_PRESENCE),
+ SND_PCI_QUIRK(0x1028, 0x0698, "Dell", ALC668_FIXUP_DELL_MIC_NO_PRESENCE),
+ SND_PCI_QUIRK(0x103c, 0x1632, "HP RP5800", ALC662_FIXUP_HP_RP5800),
+ SND_PCI_QUIRK(0x1043, 0x11cd, "Asus N550", ALC662_FIXUP_BASS_1A),
+ SND_PCI_QUIRK(0x1043, 0x1477, "ASUS N56VZ", ALC662_FIXUP_BASS_MODE4_CHMAP),
+ SND_PCI_QUIRK(0x1043, 0x15a7, "ASUS UX51VZH", ALC662_FIXUP_BASS_16),
+ SND_PCI_QUIRK(0x1043, 0x1b73, "ASUS N55SF", ALC662_FIXUP_BASS_16),
+ SND_PCI_QUIRK(0x1043, 0x1bf3, "ASUS N76VZ", ALC662_FIXUP_BASS_MODE4_CHMAP),
+ SND_PCI_QUIRK(0x1043, 0x8469, "ASUS mobo", ALC662_FIXUP_NO_JACK_DETECT),
+ SND_PCI_QUIRK(0x105b, 0x0cd6, "Foxconn", ALC662_FIXUP_ASUS_MODE2),
SND_PCI_QUIRK(0x144d, 0xc051, "Samsung R720", ALC662_FIXUP_IDEAPAD),
- SND_PCI_QUIRK(0x1458, 0xa002, "Gigabyte", ALC662_FIXUP_GIGABYTE),
SND_PCI_QUIRK(0x17aa, 0x38af, "Lenovo Ideapad Y550P", ALC662_FIXUP_IDEAPAD),
SND_PCI_QUIRK(0x17aa, 0x3a0d, "Lenovo Ideapad Y550", ALC662_FIXUP_IDEAPAD),
+ SND_PCI_QUIRK(0x19da, 0xa130, "Zotac Z68", ALC662_FIXUP_ZOTAC_Z68),
SND_PCI_QUIRK(0x1b35, 0x2206, "CZC P10T", ALC662_FIXUP_CZC_P10T),
+
+#if 0
+ /* Below is a quirk table taken from the old code.
+ * Basically the device should work as is without the fixup table.
+ * If BIOS doesn't give a proper info, enable the corresponding
+ * fixup entry.
+ */
+ SND_PCI_QUIRK(0x1043, 0x1000, "ASUS N50Vm", ALC662_FIXUP_ASUS_MODE1),
+ SND_PCI_QUIRK(0x1043, 0x1092, "ASUS NB", ALC662_FIXUP_ASUS_MODE3),
+ SND_PCI_QUIRK(0x1043, 0x1173, "ASUS K73Jn", ALC662_FIXUP_ASUS_MODE1),
+ SND_PCI_QUIRK(0x1043, 0x11c3, "ASUS M70V", ALC662_FIXUP_ASUS_MODE3),
+ SND_PCI_QUIRK(0x1043, 0x11d3, "ASUS NB", ALC662_FIXUP_ASUS_MODE1),
+ SND_PCI_QUIRK(0x1043, 0x11f3, "ASUS NB", ALC662_FIXUP_ASUS_MODE2),
+ SND_PCI_QUIRK(0x1043, 0x1203, "ASUS NB", ALC662_FIXUP_ASUS_MODE1),
+ SND_PCI_QUIRK(0x1043, 0x1303, "ASUS G60J", ALC662_FIXUP_ASUS_MODE1),
+ SND_PCI_QUIRK(0x1043, 0x1333, "ASUS G60Jx", ALC662_FIXUP_ASUS_MODE1),
+ SND_PCI_QUIRK(0x1043, 0x1339, "ASUS NB", ALC662_FIXUP_ASUS_MODE2),
+ SND_PCI_QUIRK(0x1043, 0x13e3, "ASUS N71JA", ALC662_FIXUP_ASUS_MODE7),
+ SND_PCI_QUIRK(0x1043, 0x1463, "ASUS N71", ALC662_FIXUP_ASUS_MODE7),
+ SND_PCI_QUIRK(0x1043, 0x14d3, "ASUS G72", ALC662_FIXUP_ASUS_MODE8),
+ SND_PCI_QUIRK(0x1043, 0x1563, "ASUS N90", ALC662_FIXUP_ASUS_MODE3),
+ SND_PCI_QUIRK(0x1043, 0x15d3, "ASUS N50SF F50SF", ALC662_FIXUP_ASUS_MODE1),
+ SND_PCI_QUIRK(0x1043, 0x16c3, "ASUS NB", ALC662_FIXUP_ASUS_MODE2),
+ SND_PCI_QUIRK(0x1043, 0x16f3, "ASUS K40C K50C", ALC662_FIXUP_ASUS_MODE2),
+ SND_PCI_QUIRK(0x1043, 0x1733, "ASUS N81De", ALC662_FIXUP_ASUS_MODE1),
+ SND_PCI_QUIRK(0x1043, 0x1753, "ASUS NB", ALC662_FIXUP_ASUS_MODE2),
+ SND_PCI_QUIRK(0x1043, 0x1763, "ASUS NB", ALC662_FIXUP_ASUS_MODE6),
+ SND_PCI_QUIRK(0x1043, 0x1765, "ASUS NB", ALC662_FIXUP_ASUS_MODE6),
+ SND_PCI_QUIRK(0x1043, 0x1783, "ASUS NB", ALC662_FIXUP_ASUS_MODE2),
+ SND_PCI_QUIRK(0x1043, 0x1793, "ASUS F50GX", ALC662_FIXUP_ASUS_MODE1),
+ SND_PCI_QUIRK(0x1043, 0x17b3, "ASUS F70SL", ALC662_FIXUP_ASUS_MODE3),
+ SND_PCI_QUIRK(0x1043, 0x17f3, "ASUS X58LE", ALC662_FIXUP_ASUS_MODE2),
+ SND_PCI_QUIRK(0x1043, 0x1813, "ASUS NB", ALC662_FIXUP_ASUS_MODE2),
+ SND_PCI_QUIRK(0x1043, 0x1823, "ASUS NB", ALC662_FIXUP_ASUS_MODE5),
+ SND_PCI_QUIRK(0x1043, 0x1833, "ASUS NB", ALC662_FIXUP_ASUS_MODE6),
+ SND_PCI_QUIRK(0x1043, 0x1843, "ASUS NB", ALC662_FIXUP_ASUS_MODE2),
+ SND_PCI_QUIRK(0x1043, 0x1853, "ASUS F50Z", ALC662_FIXUP_ASUS_MODE1),
+ SND_PCI_QUIRK(0x1043, 0x1864, "ASUS NB", ALC662_FIXUP_ASUS_MODE2),
+ SND_PCI_QUIRK(0x1043, 0x1876, "ASUS NB", ALC662_FIXUP_ASUS_MODE2),
+ SND_PCI_QUIRK(0x1043, 0x1893, "ASUS M50Vm", ALC662_FIXUP_ASUS_MODE3),
+ SND_PCI_QUIRK(0x1043, 0x1894, "ASUS X55", ALC662_FIXUP_ASUS_MODE3),
+ SND_PCI_QUIRK(0x1043, 0x18b3, "ASUS N80Vc", ALC662_FIXUP_ASUS_MODE1),
+ SND_PCI_QUIRK(0x1043, 0x18c3, "ASUS VX5", ALC662_FIXUP_ASUS_MODE1),
+ SND_PCI_QUIRK(0x1043, 0x18d3, "ASUS N81Te", ALC662_FIXUP_ASUS_MODE1),
+ SND_PCI_QUIRK(0x1043, 0x18f3, "ASUS N505Tp", ALC662_FIXUP_ASUS_MODE1),
+ SND_PCI_QUIRK(0x1043, 0x1903, "ASUS F5GL", ALC662_FIXUP_ASUS_MODE1),
+ SND_PCI_QUIRK(0x1043, 0x1913, "ASUS NB", ALC662_FIXUP_ASUS_MODE2),
+ SND_PCI_QUIRK(0x1043, 0x1933, "ASUS F80Q", ALC662_FIXUP_ASUS_MODE2),
+ SND_PCI_QUIRK(0x1043, 0x1943, "ASUS Vx3V", ALC662_FIXUP_ASUS_MODE1),
+ SND_PCI_QUIRK(0x1043, 0x1953, "ASUS NB", ALC662_FIXUP_ASUS_MODE1),
+ SND_PCI_QUIRK(0x1043, 0x1963, "ASUS X71C", ALC662_FIXUP_ASUS_MODE3),
+ SND_PCI_QUIRK(0x1043, 0x1983, "ASUS N5051A", ALC662_FIXUP_ASUS_MODE1),
+ SND_PCI_QUIRK(0x1043, 0x1993, "ASUS N20", ALC662_FIXUP_ASUS_MODE1),
+ SND_PCI_QUIRK(0x1043, 0x19b3, "ASUS F7Z", ALC662_FIXUP_ASUS_MODE1),
+ SND_PCI_QUIRK(0x1043, 0x19c3, "ASUS F5Z/F6x", ALC662_FIXUP_ASUS_MODE2),
+ SND_PCI_QUIRK(0x1043, 0x19e3, "ASUS NB", ALC662_FIXUP_ASUS_MODE1),
+ SND_PCI_QUIRK(0x1043, 0x19f3, "ASUS NB", ALC662_FIXUP_ASUS_MODE4),
+#endif
{}
};
-static const struct alc_model_fixup alc662_fixup_models[] = {
+static const struct hda_model_fixup alc662_fixup_models[] = {
{.id = ALC272_FIXUP_MARIO, .name = "mario"},
+ {.id = ALC662_FIXUP_ASUS_MODE1, .name = "asus-mode1"},
+ {.id = ALC662_FIXUP_ASUS_MODE2, .name = "asus-mode2"},
+ {.id = ALC662_FIXUP_ASUS_MODE3, .name = "asus-mode3"},
+ {.id = ALC662_FIXUP_ASUS_MODE4, .name = "asus-mode4"},
+ {.id = ALC662_FIXUP_ASUS_MODE5, .name = "asus-mode5"},
+ {.id = ALC662_FIXUP_ASUS_MODE6, .name = "asus-mode6"},
+ {.id = ALC662_FIXUP_ASUS_MODE7, .name = "asus-mode7"},
+ {.id = ALC662_FIXUP_ASUS_MODE8, .name = "asus-mode8"},
+ {.id = ALC662_FIXUP_INV_DMIC, .name = "inv-dmic"},
+ {.id = ALC668_FIXUP_DELL_MIC_NO_PRESENCE, .name = "dell-headset-multi"},
{}
};
+static const struct snd_hda_pin_quirk alc662_pin_fixup_tbl[] = {
+ SND_HDA_PIN_QUIRK(0x10ec0668, 0x1028, "Dell", ALC668_FIXUP_AUTO_MUTE,
+ {0x12, 0x99a30130},
+ {0x14, 0x90170110},
+ {0x15, 0x0321101f},
+ {0x16, 0x03011020},
+ {0x18, 0x40000008},
+ {0x19, 0x411111f0},
+ {0x1a, 0x411111f0},
+ {0x1b, 0x411111f0},
+ {0x1d, 0x41000001},
+ {0x1e, 0x411111f0},
+ {0x1f, 0x411111f0}),
+ SND_HDA_PIN_QUIRK(0x10ec0668, 0x1028, "Dell", ALC668_FIXUP_AUTO_MUTE,
+ {0x12, 0x99a30140},
+ {0x14, 0x90170110},
+ {0x15, 0x0321101f},
+ {0x16, 0x03011020},
+ {0x18, 0x40000008},
+ {0x19, 0x411111f0},
+ {0x1a, 0x411111f0},
+ {0x1b, 0x411111f0},
+ {0x1d, 0x41000001},
+ {0x1e, 0x411111f0},
+ {0x1f, 0x411111f0}),
+ SND_HDA_PIN_QUIRK(0x10ec0668, 0x1028, "Dell", ALC668_FIXUP_AUTO_MUTE,
+ {0x12, 0x99a30150},
+ {0x14, 0x90170110},
+ {0x15, 0x0321101f},
+ {0x16, 0x03011020},
+ {0x18, 0x40000008},
+ {0x19, 0x411111f0},
+ {0x1a, 0x411111f0},
+ {0x1b, 0x411111f0},
+ {0x1d, 0x41000001},
+ {0x1e, 0x411111f0},
+ {0x1f, 0x411111f0}),
+ SND_HDA_PIN_QUIRK(0x10ec0668, 0x1028, "Dell", ALC668_FIXUP_AUTO_MUTE,
+ {0x12, 0x411111f0},
+ {0x14, 0x90170110},
+ {0x15, 0x0321101f},
+ {0x16, 0x03011020},
+ {0x18, 0x40000008},
+ {0x19, 0x411111f0},
+ {0x1a, 0x411111f0},
+ {0x1b, 0x411111f0},
+ {0x1d, 0x41000001},
+ {0x1e, 0x411111f0},
+ {0x1f, 0x411111f0}),
+ SND_HDA_PIN_QUIRK(0x10ec0668, 0x1028, "Dell XPS 15", ALC668_FIXUP_AUTO_MUTE,
+ {0x12, 0x90a60130},
+ {0x14, 0x90170110},
+ {0x15, 0x0321101f},
+ {0x16, 0x40000000},
+ {0x18, 0x411111f0},
+ {0x19, 0x411111f0},
+ {0x1a, 0x411111f0},
+ {0x1b, 0x411111f0},
+ {0x1d, 0x40d6832d},
+ {0x1e, 0x411111f0},
+ {0x1f, 0x411111f0}),
+ {}
+};
+
+static void alc662_fill_coef(struct hda_codec *codec)
+{
+ int val, coef;
+ coef = alc_get_coef0(codec);
+
+ switch (codec->vendor_id) {
+ case 0x10ec0662:
+ if ((coef & 0x00f0) == 0x0030) {
+ val = alc_read_coef_idx(codec, 0x4); /* EAPD Ctrl */
+ alc_write_coef_idx(codec, 0x4, val & ~(1<<10));
+ }
+ break;
+ case 0x10ec0272:
+ case 0x10ec0273:
+ case 0x10ec0663:
+ case 0x10ec0665:
+ case 0x10ec0670:
+ case 0x10ec0671:
+ case 0x10ec0672:
+ val = alc_read_coef_idx(codec, 0xd); /* EAPD Ctrl */
+ alc_write_coef_idx(codec, 0xd, val | (1<<14));
+ break;
+ }
+}
+
+/*
+ */
static int patch_alc662(struct hda_codec *codec)
{
struct alc_spec *spec;
- int err, board_config;
- int coef;
+ int err;
- spec = kzalloc(sizeof(*spec), GFP_KERNEL);
- if (!spec)
- return -ENOMEM;
+ err = alc_alloc_spec(codec, 0x0b);
+ if (err < 0)
+ return err;
- codec->spec = spec;
+ spec = codec->spec;
- alc_auto_parse_customize_define(codec);
+ /* handle multiple HPs as is */
+ spec->parse_flags = HDA_PINCFG_NO_HP_FIXUP;
alc_fix_pll_init(codec, 0x20, 0x04, 15);
- coef = alc_read_coef_idx(codec, 0);
- if (coef == 0x8020 || coef == 0x8011)
- alc_codec_rename(codec, "ALC661");
- else if (coef & (1 << 14) &&
- codec->bus->pci->subsystem_vendor == 0x1025 &&
- spec->cdefine.platform_type == 1)
- alc_codec_rename(codec, "ALC272X");
- else if (coef == 0x4011)
- alc_codec_rename(codec, "ALC656");
-
- board_config = snd_hda_check_board_config(codec, ALC662_MODEL_LAST,
- alc662_models,
- alc662_cfg_tbl);
- if (board_config < 0) {
- printk(KERN_INFO "hda_codec: %s: BIOS auto-probing.\n",
- codec->chip_name);
- board_config = ALC662_AUTO;
- }
+ spec->init_hook = alc662_fill_coef;
+ alc662_fill_coef(codec);
- if (board_config == ALC662_AUTO) {
- alc_pick_fixup(codec, alc662_fixup_models,
- alc662_fixup_tbl, alc662_fixups);
- alc_apply_fixup(codec, ALC_FIXUP_ACT_PRE_PROBE);
- /* automatic parse from the BIOS config */
- err = alc662_parse_auto_config(codec);
- if (err < 0) {
- alc_free(codec);
- return err;
- } else if (!err) {
- printk(KERN_INFO
- "hda_codec: Cannot set up configuration "
- "from BIOS. Using base mode...\n");
- board_config = ALC662_3ST_2ch_DIG;
- }
- }
+ snd_hda_pick_fixup(codec, alc662_fixup_models,
+ alc662_fixup_tbl, alc662_fixups);
+ snd_hda_pick_pin_fixup(codec, alc662_pin_fixup_tbl, alc662_fixups);
+ snd_hda_apply_fixup(codec, HDA_FIXUP_ACT_PRE_PROBE);
- if (has_cdefine_beep(codec)) {
- err = snd_hda_attach_beep_device(codec, 0x1);
- if (err < 0) {
- alc_free(codec);
- return err;
- }
- }
-
- if (board_config != ALC662_AUTO)
- setup_preset(codec, &alc662_presets[board_config]);
-
- spec->stream_analog_playback = &alc662_pcm_analog_playback;
- spec->stream_analog_capture = &alc662_pcm_analog_capture;
+ alc_auto_parse_customize_define(codec);
- spec->stream_digital_playback = &alc662_pcm_digital_playback;
- spec->stream_digital_capture = &alc662_pcm_digital_capture;
+ if (has_cdefine_beep(codec))
+ spec->gen.beep_nid = 0x01;
- if (!spec->adc_nids) {
- spec->adc_nids = alc662_adc_nids;
- spec->num_adc_nids = ARRAY_SIZE(alc662_adc_nids);
+ if ((alc_get_coef0(codec) & (1 << 14)) &&
+ codec->bus->pci && codec->bus->pci->subsystem_vendor == 0x1025 &&
+ spec->cdefine.platform_type == 1) {
+ err = alc_codec_rename(codec, "ALC272X");
+ if (err < 0)
+ goto error;
}
- if (!spec->capsrc_nids)
- spec->capsrc_nids = alc662_capsrc_nids;
- if (!spec->cap_mixer)
- set_capture_mixer(codec);
+ /* automatic parse from the BIOS config */
+ err = alc662_parse_auto_config(codec);
+ if (err < 0)
+ goto error;
- if (has_cdefine_beep(codec)) {
+ if (!spec->gen.no_analog && spec->gen.beep_nid) {
switch (codec->vendor_id) {
case 0x10ec0662:
set_beep_amp(spec, 0x0b, 0x05, HDA_INPUT);
@@ -19597,6 +6094,7 @@ static int patch_alc662(struct hda_codec *codec)
case 0x10ec0272:
case 0x10ec0663:
case 0x10ec0665:
+ case 0x10ec0668:
set_beep_amp(spec, 0x0b, 0x04, HDA_INPUT);
break;
case 0x10ec0273:
@@ -19604,484 +6102,47 @@ static int patch_alc662(struct hda_codec *codec)
break;
}
}
- spec->vmaster_nid = 0x02;
-
- alc_apply_fixup(codec, ALC_FIXUP_ACT_PROBE);
codec->patch_ops = alc_patch_ops;
- if (board_config == ALC662_AUTO)
- spec->init_hook = alc662_auto_init;
+ spec->shutup = alc_eapd_shutup;
- alc_init_jacks(codec);
-
-#ifdef CONFIG_SND_HDA_POWER_SAVE
- if (!spec->loopback.amplist)
- spec->loopback.amplist = alc662_loopbacks;
-#endif
+ snd_hda_apply_fixup(codec, HDA_FIXUP_ACT_PROBE);
return 0;
-}
-static int patch_alc888(struct hda_codec *codec)
-{
- if ((alc_read_coef_idx(codec, 0) & 0x00f0)==0x0030){
- kfree(codec->chip_name);
- if (codec->vendor_id == 0x10ec0887)
- codec->chip_name = kstrdup("ALC887-VD", GFP_KERNEL);
- else
- codec->chip_name = kstrdup("ALC888-VD", GFP_KERNEL);
- if (!codec->chip_name) {
- alc_free(codec);
- return -ENOMEM;
- }
- return patch_alc662(codec);
- }
- return patch_alc882(codec);
+ error:
+ alc_free(codec);
+ return err;
}
/*
* ALC680 support
*/
-#define ALC680_DIGIN_NID ALC880_DIGIN_NID
-#define ALC680_DIGOUT_NID ALC880_DIGOUT_NID
-#define alc680_modes alc260_modes
-static hda_nid_t alc680_dac_nids[3] = {
- /* Lout1, Lout2, hp */
- 0x02, 0x03, 0x04
-};
-
-static hda_nid_t alc680_adc_nids[3] = {
- /* ADC0-2 */
- /* DMIC, MIC, Line-in*/
- 0x07, 0x08, 0x09
-};
-
-/*
- * Analog capture ADC cgange
- */
-static void alc680_rec_autoswitch(struct hda_codec *codec)
-{
- struct alc_spec *spec = codec->spec;
- struct auto_pin_cfg *cfg = &spec->autocfg;
- int pin_found = 0;
- int type_found = AUTO_PIN_LAST;
- hda_nid_t nid;
- int i;
-
- for (i = 0; i < cfg->num_inputs; i++) {
- nid = cfg->inputs[i].pin;
- if (!(snd_hda_query_pin_caps(codec, nid) &
- AC_PINCAP_PRES_DETECT))
- continue;
- if (snd_hda_jack_detect(codec, nid)) {
- if (cfg->inputs[i].type < type_found) {
- type_found = cfg->inputs[i].type;
- pin_found = nid;
- }
- }
- }
-
- nid = 0x07;
- if (pin_found)
- snd_hda_get_connections(codec, pin_found, &nid, 1);
-
- if (nid != spec->cur_adc)
- __snd_hda_codec_cleanup_stream(codec, spec->cur_adc, 1);
- spec->cur_adc = nid;
- snd_hda_codec_setup_stream(codec, nid, spec->cur_adc_stream_tag, 0,
- spec->cur_adc_format);
-}
-
-static int alc680_capture_pcm_prepare(struct hda_pcm_stream *hinfo,
- struct hda_codec *codec,
- unsigned int stream_tag,
- unsigned int format,
- struct snd_pcm_substream *substream)
-{
- struct alc_spec *spec = codec->spec;
-
- spec->cur_adc = 0x07;
- spec->cur_adc_stream_tag = stream_tag;
- spec->cur_adc_format = format;
-
- alc680_rec_autoswitch(codec);
- return 0;
-}
-
-static int alc680_capture_pcm_cleanup(struct hda_pcm_stream *hinfo,
- struct hda_codec *codec,
- struct snd_pcm_substream *substream)
-{
- snd_hda_codec_cleanup_stream(codec, 0x07);
- snd_hda_codec_cleanup_stream(codec, 0x08);
- snd_hda_codec_cleanup_stream(codec, 0x09);
- return 0;
-}
-
-static struct hda_pcm_stream alc680_pcm_analog_auto_capture = {
- .substreams = 1, /* can be overridden */
- .channels_min = 2,
- .channels_max = 2,
- /* NID is set in alc_build_pcms */
- .ops = {
- .prepare = alc680_capture_pcm_prepare,
- .cleanup = alc680_capture_pcm_cleanup
- },
-};
-
-static struct snd_kcontrol_new alc680_base_mixer[] = {
- /* output mixer control */
- HDA_CODEC_VOLUME("Front Playback Volume", 0x2, 0x0, HDA_OUTPUT),
- HDA_CODEC_MUTE("Front Playback Switch", 0x14, 0x0, HDA_OUTPUT),
- HDA_CODEC_VOLUME("Headphone Playback Volume", 0x4, 0x0, HDA_OUTPUT),
- HDA_CODEC_MUTE("Headphone Playback Switch", 0x16, 0x0, HDA_OUTPUT),
- HDA_CODEC_VOLUME("Internal Mic Boost Volume", 0x12, 0, HDA_INPUT),
- HDA_CODEC_VOLUME("Mic Boost Volume", 0x18, 0, HDA_INPUT),
- HDA_CODEC_VOLUME("Line In Boost Volume", 0x19, 0, HDA_INPUT),
- { }
-};
-
-static struct hda_bind_ctls alc680_bind_cap_vol = {
- .ops = &snd_hda_bind_vol,
- .values = {
- HDA_COMPOSE_AMP_VAL(0x07, 3, 0, HDA_INPUT),
- HDA_COMPOSE_AMP_VAL(0x08, 3, 0, HDA_INPUT),
- HDA_COMPOSE_AMP_VAL(0x09, 3, 0, HDA_INPUT),
- 0
- },
-};
-
-static struct hda_bind_ctls alc680_bind_cap_switch = {
- .ops = &snd_hda_bind_sw,
- .values = {
- HDA_COMPOSE_AMP_VAL(0x07, 3, 0, HDA_INPUT),
- HDA_COMPOSE_AMP_VAL(0x08, 3, 0, HDA_INPUT),
- HDA_COMPOSE_AMP_VAL(0x09, 3, 0, HDA_INPUT),
- 0
- },
-};
-
-static struct snd_kcontrol_new alc680_master_capture_mixer[] = {
- HDA_BIND_VOL("Capture Volume", &alc680_bind_cap_vol),
- HDA_BIND_SW("Capture Switch", &alc680_bind_cap_switch),
- { } /* end */
-};
-
-/*
- * generic initialization of ADC, input mixers and output mixers
- */
-static struct hda_verb alc680_init_verbs[] = {
- {0x02, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
- {0x03, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
- {0x04, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
-
- {0x12, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN},
- {0x14, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
- {0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
- {0x16, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP},
- {0x18, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80},
- {0x19, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN},
-
- {0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
- {0x15, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
- {0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
- {0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
- {0x19, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
-
- {0x16, AC_VERB_SET_UNSOLICITED_ENABLE, ALC880_HP_EVENT | AC_USRSP_EN},
- {0x18, AC_VERB_SET_UNSOLICITED_ENABLE, ALC880_MIC_EVENT | AC_USRSP_EN},
- {0x19, AC_VERB_SET_UNSOLICITED_ENABLE, ALC880_MIC_EVENT | AC_USRSP_EN},
-
- { }
-};
-
-/* toggle speaker-output according to the hp-jack state */
-static void alc680_base_setup(struct hda_codec *codec)
-{
- struct alc_spec *spec = codec->spec;
-
- spec->autocfg.hp_pins[0] = 0x16;
- spec->autocfg.speaker_pins[0] = 0x14;
- spec->autocfg.speaker_pins[1] = 0x15;
- spec->autocfg.num_inputs = 2;
- spec->autocfg.inputs[0].pin = 0x18;
- spec->autocfg.inputs[0].type = AUTO_PIN_MIC;
- spec->autocfg.inputs[1].pin = 0x19;
- spec->autocfg.inputs[1].type = AUTO_PIN_LINE_IN;
-}
-
-static void alc680_unsol_event(struct hda_codec *codec,
- unsigned int res)
-{
- if ((res >> 26) == ALC880_HP_EVENT)
- alc_automute_amp(codec);
- if ((res >> 26) == ALC880_MIC_EVENT)
- alc680_rec_autoswitch(codec);
-}
-
-static void alc680_inithook(struct hda_codec *codec)
-{
- alc_automute_amp(codec);
- alc680_rec_autoswitch(codec);
-}
-
-/* create input playback/capture controls for the given pin */
-static int alc680_new_analog_output(struct alc_spec *spec, hda_nid_t nid,
- const char *ctlname, int idx)
-{
- hda_nid_t dac;
- int err;
-
- switch (nid) {
- case 0x14:
- dac = 0x02;
- break;
- case 0x15:
- dac = 0x03;
- break;
- case 0x16:
- dac = 0x04;
- break;
- default:
- return 0;
- }
- if (spec->multiout.dac_nids[0] != dac &&
- spec->multiout.dac_nids[1] != dac) {
- err = add_pb_vol_ctrl(spec, ALC_CTL_WIDGET_VOL, ctlname,
- HDA_COMPOSE_AMP_VAL(dac, 3, idx,
- HDA_OUTPUT));
- if (err < 0)
- return err;
-
- err = add_pb_sw_ctrl(spec, ALC_CTL_WIDGET_MUTE, ctlname,
- HDA_COMPOSE_AMP_VAL(nid, 3, idx, HDA_OUTPUT));
-
- if (err < 0)
- return err;
- spec->multiout.dac_nids[spec->multiout.num_dacs++] = dac;
- }
-
- return 0;
-}
-
-/* add playback controls from the parsed DAC table */
-static int alc680_auto_create_multi_out_ctls(struct alc_spec *spec,
- const struct auto_pin_cfg *cfg)
-{
- hda_nid_t nid;
- int err;
-
- spec->multiout.dac_nids = spec->private_dac_nids;
-
- nid = cfg->line_out_pins[0];
- if (nid) {
- const char *name;
- if (cfg->line_out_type == AUTO_PIN_SPEAKER_OUT)
- name = "Speaker";
- else
- name = "Front";
- err = alc680_new_analog_output(spec, nid, name, 0);
- if (err < 0)
- return err;
- }
-
- nid = cfg->speaker_pins[0];
- if (nid) {
- err = alc680_new_analog_output(spec, nid, "Speaker", 0);
- if (err < 0)
- return err;
- }
- nid = cfg->hp_pins[0];
- if (nid) {
- err = alc680_new_analog_output(spec, nid, "Headphone", 0);
- if (err < 0)
- return err;
- }
-
- return 0;
-}
-
-static void alc680_auto_set_output_and_unmute(struct hda_codec *codec,
- hda_nid_t nid, int pin_type)
-{
- alc_set_pin_output(codec, nid, pin_type);
-}
-
-static void alc680_auto_init_multi_out(struct hda_codec *codec)
-{
- struct alc_spec *spec = codec->spec;
- hda_nid_t nid = spec->autocfg.line_out_pins[0];
- if (nid) {
- int pin_type = get_pin_type(spec->autocfg.line_out_type);
- alc680_auto_set_output_and_unmute(codec, nid, pin_type);
- }
-}
-
-static void alc680_auto_init_hp_out(struct hda_codec *codec)
+static int alc680_parse_auto_config(struct hda_codec *codec)
{
- struct alc_spec *spec = codec->spec;
- hda_nid_t pin;
-
- pin = spec->autocfg.hp_pins[0];
- if (pin)
- alc680_auto_set_output_and_unmute(codec, pin, PIN_HP);
- pin = spec->autocfg.speaker_pins[0];
- if (pin)
- alc680_auto_set_output_and_unmute(codec, pin, PIN_OUT);
+ return alc_parse_auto_config(codec, NULL, NULL);
}
-/* pcm configuration: identical with ALC880 */
-#define alc680_pcm_analog_playback alc880_pcm_analog_playback
-#define alc680_pcm_analog_capture alc880_pcm_analog_capture
-#define alc680_pcm_analog_alt_capture alc880_pcm_analog_alt_capture
-#define alc680_pcm_digital_playback alc880_pcm_digital_playback
-#define alc680_pcm_digital_capture alc880_pcm_digital_capture
-
/*
- * BIOS auto configuration
*/
-static int alc680_parse_auto_config(struct hda_codec *codec)
+static int patch_alc680(struct hda_codec *codec)
{
- struct alc_spec *spec = codec->spec;
int err;
- static hda_nid_t alc680_ignore[] = { 0 };
- err = snd_hda_parse_pin_def_config(codec, &spec->autocfg,
- alc680_ignore);
+ /* ALC680 has no aa-loopback mixer */
+ err = alc_alloc_spec(codec, 0);
if (err < 0)
return err;
- if (!spec->autocfg.line_outs) {
- if (spec->autocfg.dig_outs || spec->autocfg.dig_in_pin) {
- spec->multiout.max_channels = 2;
- spec->no_analog = 1;
- goto dig_only;
- }
- return 0; /* can't find valid BIOS pin config */
- }
- err = alc680_auto_create_multi_out_ctls(spec, &spec->autocfg);
- if (err < 0)
- return err;
-
- spec->multiout.max_channels = 2;
-
- dig_only:
- /* digital only support output */
- alc_auto_parse_digital(codec);
- if (spec->kctls.list)
- add_mixer(spec, spec->kctls.list);
-
- add_verb(spec, alc680_init_verbs);
-
- err = alc_auto_add_mic_boost(codec);
- if (err < 0)
+ /* automatic parse from the BIOS config */
+ err = alc680_parse_auto_config(codec);
+ if (err < 0) {
+ alc_free(codec);
return err;
-
- return 1;
-}
-
-#define alc680_auto_init_analog_input alc882_auto_init_analog_input
-
-/* init callback for auto-configuration model -- overriding the default init */
-static void alc680_auto_init(struct hda_codec *codec)
-{
- struct alc_spec *spec = codec->spec;
- alc680_auto_init_multi_out(codec);
- alc680_auto_init_hp_out(codec);
- alc680_auto_init_analog_input(codec);
- alc_auto_init_digital(codec);
- if (spec->unsol_event)
- alc_inithook(codec);
-}
-
-/*
- * configuration and preset
- */
-static const char * const alc680_models[ALC680_MODEL_LAST] = {
- [ALC680_BASE] = "base",
- [ALC680_AUTO] = "auto",
-};
-
-static struct snd_pci_quirk alc680_cfg_tbl[] = {
- SND_PCI_QUIRK(0x1043, 0x12f3, "ASUS NX90", ALC680_BASE),
- {}
-};
-
-static struct alc_config_preset alc680_presets[] = {
- [ALC680_BASE] = {
- .mixers = { alc680_base_mixer },
- .cap_mixer = alc680_master_capture_mixer,
- .init_verbs = { alc680_init_verbs },
- .num_dacs = ARRAY_SIZE(alc680_dac_nids),
- .dac_nids = alc680_dac_nids,
- .dig_out_nid = ALC680_DIGOUT_NID,
- .num_channel_mode = ARRAY_SIZE(alc680_modes),
- .channel_mode = alc680_modes,
- .unsol_event = alc680_unsol_event,
- .setup = alc680_base_setup,
- .init_hook = alc680_inithook,
-
- },
-};
-
-static int patch_alc680(struct hda_codec *codec)
-{
- struct alc_spec *spec;
- int board_config;
- int err;
-
- spec = kzalloc(sizeof(*spec), GFP_KERNEL);
- if (spec == NULL)
- return -ENOMEM;
-
- codec->spec = spec;
-
- board_config = snd_hda_check_board_config(codec, ALC680_MODEL_LAST,
- alc680_models,
- alc680_cfg_tbl);
-
- if (board_config < 0 || board_config >= ALC680_MODEL_LAST) {
- printk(KERN_INFO "hda_codec: %s: BIOS auto-probing.\n",
- codec->chip_name);
- board_config = ALC680_AUTO;
}
- if (board_config == ALC680_AUTO) {
- /* automatic parse from the BIOS config */
- err = alc680_parse_auto_config(codec);
- if (err < 0) {
- alc_free(codec);
- return err;
- } else if (!err) {
- printk(KERN_INFO
- "hda_codec: Cannot set up configuration "
- "from BIOS. Using base mode...\n");
- board_config = ALC680_BASE;
- }
- }
-
- if (board_config != ALC680_AUTO)
- setup_preset(codec, &alc680_presets[board_config]);
-
- spec->stream_analog_playback = &alc680_pcm_analog_playback;
- spec->stream_analog_capture = &alc680_pcm_analog_auto_capture;
- spec->stream_digital_playback = &alc680_pcm_digital_playback;
- spec->stream_digital_capture = &alc680_pcm_digital_capture;
-
- if (!spec->adc_nids) {
- spec->adc_nids = alc680_adc_nids;
- spec->num_adc_nids = ARRAY_SIZE(alc680_adc_nids);
- }
-
- if (!spec->cap_mixer)
- set_capture_mixer(codec);
-
- spec->vmaster_nid = 0x02;
-
codec->patch_ops = alc_patch_ops;
- if (board_config == ALC680_AUTO)
- spec->init_hook = alc680_auto_init;
return 0;
}
@@ -20089,7 +6150,12 @@ static int patch_alc680(struct hda_codec *codec)
/*
* patch entries
*/
-static struct hda_codec_preset snd_hda_preset_realtek[] = {
+static const struct hda_codec_preset snd_hda_preset_realtek[] = {
+ { .id = 0x10ec0221, .name = "ALC221", .patch = patch_alc269 },
+ { .id = 0x10ec0231, .name = "ALC231", .patch = patch_alc269 },
+ { .id = 0x10ec0233, .name = "ALC233", .patch = patch_alc269 },
+ { .id = 0x10ec0235, .name = "ALC233", .patch = patch_alc269 },
+ { .id = 0x10ec0255, .name = "ALC255", .patch = patch_alc269 },
{ .id = 0x10ec0260, .name = "ALC260", .patch = patch_alc260 },
{ .id = 0x10ec0262, .name = "ALC262", .patch = patch_alc262 },
{ .id = 0x10ec0267, .name = "ALC267", .patch = patch_alc268 },
@@ -20098,6 +6164,17 @@ static struct hda_codec_preset snd_hda_preset_realtek[] = {
{ .id = 0x10ec0270, .name = "ALC270", .patch = patch_alc269 },
{ .id = 0x10ec0272, .name = "ALC272", .patch = patch_alc662 },
{ .id = 0x10ec0275, .name = "ALC275", .patch = patch_alc269 },
+ { .id = 0x10ec0276, .name = "ALC276", .patch = patch_alc269 },
+ { .id = 0x10ec0280, .name = "ALC280", .patch = patch_alc269 },
+ { .id = 0x10ec0282, .name = "ALC282", .patch = patch_alc269 },
+ { .id = 0x10ec0283, .name = "ALC283", .patch = patch_alc269 },
+ { .id = 0x10ec0284, .name = "ALC284", .patch = patch_alc269 },
+ { .id = 0x10ec0285, .name = "ALC285", .patch = patch_alc269 },
+ { .id = 0x10ec0286, .name = "ALC286", .patch = patch_alc269 },
+ { .id = 0x10ec0288, .name = "ALC288", .patch = patch_alc269 },
+ { .id = 0x10ec0290, .name = "ALC290", .patch = patch_alc269 },
+ { .id = 0x10ec0292, .name = "ALC292", .patch = patch_alc269 },
+ { .id = 0x10ec0293, .name = "ALC293", .patch = patch_alc269 },
{ .id = 0x10ec0861, .rev = 0x100340, .name = "ALC660",
.patch = patch_alc861 },
{ .id = 0x10ec0660, .name = "ALC660-VD", .patch = patch_alc861vd },
@@ -20107,10 +6184,16 @@ static struct hda_codec_preset snd_hda_preset_realtek[] = {
.patch = patch_alc882 },
{ .id = 0x10ec0662, .rev = 0x100101, .name = "ALC662 rev1",
.patch = patch_alc662 },
+ { .id = 0x10ec0662, .rev = 0x100300, .name = "ALC662 rev3",
+ .patch = patch_alc662 },
{ .id = 0x10ec0663, .name = "ALC663", .patch = patch_alc662 },
{ .id = 0x10ec0665, .name = "ALC665", .patch = patch_alc662 },
+ { .id = 0x10ec0667, .name = "ALC667", .patch = patch_alc662 },
+ { .id = 0x10ec0668, .name = "ALC668", .patch = patch_alc662 },
{ .id = 0x10ec0670, .name = "ALC670", .patch = patch_alc662 },
+ { .id = 0x10ec0671, .name = "ALC671", .patch = patch_alc662 },
{ .id = 0x10ec0680, .name = "ALC680", .patch = patch_alc680 },
+ { .id = 0x10ec0867, .name = "ALC891", .patch = patch_alc882 },
{ .id = 0x10ec0880, .name = "ALC880", .patch = patch_alc880 },
{ .id = 0x10ec0882, .name = "ALC882", .patch = patch_alc882 },
{ .id = 0x10ec0883, .name = "ALC883", .patch = patch_alc882 },
@@ -20119,12 +6202,14 @@ static struct hda_codec_preset snd_hda_preset_realtek[] = {
{ .id = 0x10ec0885, .rev = 0x100103, .name = "ALC889A",
.patch = patch_alc882 },
{ .id = 0x10ec0885, .name = "ALC885", .patch = patch_alc882 },
- { .id = 0x10ec0887, .name = "ALC887", .patch = patch_alc888 },
+ { .id = 0x10ec0887, .name = "ALC887", .patch = patch_alc882 },
{ .id = 0x10ec0888, .rev = 0x100101, .name = "ALC1200",
.patch = patch_alc882 },
- { .id = 0x10ec0888, .name = "ALC888", .patch = patch_alc888 },
+ { .id = 0x10ec0888, .name = "ALC888", .patch = patch_alc882 },
{ .id = 0x10ec0889, .name = "ALC889", .patch = patch_alc882 },
{ .id = 0x10ec0892, .name = "ALC892", .patch = patch_alc662 },
+ { .id = 0x10ec0899, .name = "ALC898", .patch = patch_alc882 },
+ { .id = 0x10ec0900, .name = "ALC1150", .patch = patch_alc882 },
{} /* terminator */
};
diff --git a/sound/pci/hda/patch_si3054.c b/sound/pci/hda/patch_si3054.c
index f419ee8d75f..3208ad69583 100644
--- a/sound/pci/hda/patch_si3054.c
+++ b/sound/pci/hda/patch_si3054.c
@@ -25,6 +25,7 @@
#include <linux/init.h>
#include <linux/delay.h>
#include <linux/slab.h>
+#include <linux/module.h>
#include <sound/core.h>
#include "hda_codec.h"
#include "hda_local.h"
@@ -130,7 +131,7 @@ static int si3054_switch_put(struct snd_kcontrol *kcontrol,
}
-static struct snd_kcontrol_new si3054_modem_mixer[] = {
+static const struct snd_kcontrol_new si3054_modem_mixer[] = {
SI3054_KCONTROL("Off-hook Switch", SI3054_GPIO_CONTROL, SI3054_GPIO_OH),
SI3054_KCONTROL("Caller ID Switch", SI3054_GPIO_CONTROL, SI3054_GPIO_CID),
{}
@@ -181,7 +182,7 @@ static int si3054_pcm_open(struct hda_pcm_stream *hinfo,
}
-static struct hda_pcm_stream si3054_pcm = {
+static const struct hda_pcm_stream si3054_pcm = {
.substreams = 1,
.channels_min = 1,
.channels_max = 1,
@@ -200,12 +201,13 @@ static int si3054_build_pcms(struct hda_codec *codec)
{
struct si3054_spec *spec = codec->spec;
struct hda_pcm *info = &spec->pcm;
- si3054_pcm.nid = codec->mfg;
codec->num_pcms = 1;
codec->pcm_info = info;
info->name = "Si3054 Modem";
info->stream[SNDRV_PCM_STREAM_PLAYBACK] = si3054_pcm;
info->stream[SNDRV_PCM_STREAM_CAPTURE] = si3054_pcm;
+ info->stream[SNDRV_PCM_STREAM_PLAYBACK].nid = codec->mfg;
+ info->stream[SNDRV_PCM_STREAM_CAPTURE].nid = codec->mfg;
info->pcm_type = HDA_PCM_TYPE_MODEM;
return 0;
}
@@ -234,7 +236,7 @@ static int si3054_init(struct hda_codec *codec)
} while ((val & SI3054_MEI_READY) != SI3054_MEI_READY && wait_count--);
if((val&SI3054_MEI_READY) != SI3054_MEI_READY) {
- snd_printk(KERN_ERR "si3054: cannot initialize. EXT MID = %04x\n", val);
+ codec_err(codec, "si3054: cannot initialize. EXT MID = %04x\n", val);
/* let's pray that this is no fatal error */
/* return -EACCES; */
}
@@ -245,7 +247,8 @@ static int si3054_init(struct hda_codec *codec)
SET_REG(codec, SI3054_LINE_CFG1,0x200);
if((GET_REG(codec,SI3054_LINE_STATUS) & (1<<6)) == 0) {
- snd_printd("Link Frame Detect(FDT) is not ready (line status: %04x)\n",
+ codec_dbg(codec,
+ "Link Frame Detect(FDT) is not ready (line status: %04x)\n",
GET_REG(codec,SI3054_LINE_STATUS));
}
@@ -263,7 +266,7 @@ static void si3054_free(struct hda_codec *codec)
/*
*/
-static struct hda_codec_ops si3054_patch_ops = {
+static const struct hda_codec_ops si3054_patch_ops = {
.build_controls = si3054_build_controls,
.build_pcms = si3054_build_pcms,
.init = si3054_init,
@@ -283,7 +286,7 @@ static int patch_si3054(struct hda_codec *codec)
/*
* patch entries
*/
-static struct hda_codec_preset snd_hda_preset_si3054[] = {
+static const struct hda_codec_preset snd_hda_preset_si3054[] = {
{ .id = 0x163c3055, .name = "Si3054", .patch = patch_si3054 },
{ .id = 0x163c3155, .name = "Si3054", .patch = patch_si3054 },
{ .id = 0x11c13026, .name = "Si3054", .patch = patch_si3054 },
diff --git a/sound/pci/hda/patch_sigmatel.c b/sound/pci/hda/patch_sigmatel.c
index 94d19c03a7f..3744ea4e843 100644
--- a/sound/pci/hda/patch_sigmatel.c
+++ b/sound/pci/hda/patch_sigmatel.c
@@ -29,25 +29,23 @@
#include <linux/slab.h>
#include <linux/pci.h>
#include <linux/dmi.h>
+#include <linux/module.h>
#include <sound/core.h>
-#include <sound/asoundef.h>
#include <sound/jack.h>
#include <sound/tlv.h>
#include "hda_codec.h"
#include "hda_local.h"
+#include "hda_auto_parser.h"
#include "hda_beep.h"
+#include "hda_jack.h"
+#include "hda_generic.h"
enum {
- STAC_VREF_EVENT = 1,
- STAC_INSERT_EVENT,
+ STAC_VREF_EVENT = 8,
STAC_PWR_EVENT,
- STAC_HP_EVENT,
- STAC_LO_EVENT,
- STAC_MIC_EVENT,
};
enum {
- STAC_AUTO,
STAC_REF,
STAC_9200_OQO,
STAC_9200_DELL_D21,
@@ -63,11 +61,11 @@ enum {
STAC_9200_M4,
STAC_9200_M4_2,
STAC_9200_PANASONIC,
+ STAC_9200_EAPD_INIT,
STAC_9200_MODELS
};
enum {
- STAC_9205_AUTO,
STAC_9205_REF,
STAC_9205_DELL_M42,
STAC_9205_DELL_M43,
@@ -77,7 +75,6 @@ enum {
};
enum {
- STAC_92HD73XX_AUTO,
STAC_92HD73XX_NO_JD, /* no jack-detection */
STAC_92HD73XX_REF,
STAC_92HD73XX_INTEL,
@@ -86,21 +83,30 @@ enum {
STAC_DELL_M6_BOTH,
STAC_DELL_EQ,
STAC_ALIENWARE_M17X,
+ STAC_92HD89XX_HP_FRONT_JACK,
STAC_92HD73XX_MODELS
};
enum {
- STAC_92HD83XXX_AUTO,
STAC_92HD83XXX_REF,
STAC_92HD83XXX_PWR_REF,
STAC_DELL_S14,
- STAC_92HD83XXX_HP,
+ STAC_DELL_VOSTRO_3500,
+ STAC_92HD83XXX_HP_cNB11_INTQUAD,
STAC_HP_DV7_4000,
+ STAC_HP_ZEPHYR,
+ STAC_92HD83XXX_HP_LED,
+ STAC_92HD83XXX_HP_INV_LED,
+ STAC_92HD83XXX_HP_MIC_LED,
+ STAC_HP_LED_GPIO10,
+ STAC_92HD83XXX_HEADSET_JACK,
+ STAC_92HD83XXX_HP,
+ STAC_HP_ENVY_BASS,
+ STAC_HP_BNB13_EQ,
STAC_92HD83XXX_MODELS
};
enum {
- STAC_92HD71BXX_AUTO,
STAC_92HD71BXX_REF,
STAC_DELL_M4_1,
STAC_DELL_M4_2,
@@ -109,12 +115,19 @@ enum {
STAC_HP_DV4,
STAC_HP_DV5,
STAC_HP_HDX,
- STAC_HP_DV4_1222NR,
+ STAC_92HD71BXX_HP,
+ STAC_92HD71BXX_NO_DMIC,
+ STAC_92HD71BXX_NO_SMUX,
STAC_92HD71BXX_MODELS
};
enum {
- STAC_925x_AUTO,
+ STAC_92HD95_HP_LED,
+ STAC_92HD95_HP_BASS,
+ STAC_92HD95_MODELS
+};
+
+enum {
STAC_925x_REF,
STAC_M1,
STAC_M1_2,
@@ -127,7 +140,6 @@ enum {
};
enum {
- STAC_922X_AUTO,
STAC_D945_REF,
STAC_D945GTP3,
STAC_D945GTP5,
@@ -136,73 +148,46 @@ enum {
STAC_INTEL_MAC_V3,
STAC_INTEL_MAC_V4,
STAC_INTEL_MAC_V5,
- STAC_INTEL_MAC_AUTO, /* This model is selected if no module parameter
- * is given, one of the above models will be
- * chosen according to the subsystem id. */
- /* for backward compatibility */
- STAC_MACMINI,
- STAC_MACBOOK,
- STAC_MACBOOK_PRO_V1,
- STAC_MACBOOK_PRO_V2,
- STAC_IMAC_INTEL,
- STAC_IMAC_INTEL_20,
+ STAC_INTEL_MAC_AUTO,
STAC_ECS_202,
STAC_922X_DELL_D81,
STAC_922X_DELL_D82,
STAC_922X_DELL_M81,
STAC_922X_DELL_M82,
+ STAC_922X_INTEL_MAC_GPIO,
STAC_922X_MODELS
};
enum {
- STAC_927X_AUTO,
STAC_D965_REF_NO_JD, /* no jack-detection */
STAC_D965_REF,
STAC_D965_3ST,
STAC_D965_5ST,
STAC_D965_5ST_NO_FP,
+ STAC_D965_VERBS,
STAC_DELL_3ST,
STAC_DELL_BIOS,
+ STAC_DELL_BIOS_AMIC,
+ STAC_DELL_BIOS_SPDIF,
+ STAC_927X_DELL_DMIC,
STAC_927X_VOLKNOB,
STAC_927X_MODELS
};
enum {
- STAC_9872_AUTO,
STAC_9872_VAIO,
STAC_9872_MODELS
};
-struct sigmatel_event {
- hda_nid_t nid;
- unsigned char type;
- unsigned char tag;
- int data;
-};
-
-struct sigmatel_mic_route {
- hda_nid_t pin;
- signed char mux_idx;
- signed char dmux_idx;
-};
-
-#define MAX_PINS_NUM 16
-#define MAX_ADCS_NUM 4
-#define MAX_DMICS_NUM 4
-
struct sigmatel_spec {
- struct snd_kcontrol_new *mixers[4];
- unsigned int num_mixers;
+ struct hda_gen_spec gen;
- int board_config;
unsigned int eapd_switch: 1;
- unsigned int surr_switch: 1;
- unsigned int alt_switch: 1;
- unsigned int hp_detect: 1;
- unsigned int spdif_mute: 1;
- unsigned int check_volume_offset:1;
- unsigned int auto_mic:1;
unsigned int linear_tone_beep:1;
+ unsigned int headset_jack:1; /* 4-pin headset jack (hp + mono mic) */
+ unsigned int volknob_init:1; /* special volume-knob initialization */
+ unsigned int powerdown_adcs:1;
+ unsigned int have_spdif_mux:1;
/* gpio lines */
unsigned int eapd_mask;
@@ -212,674 +197,886 @@ struct sigmatel_spec {
unsigned int gpio_mute;
unsigned int gpio_led;
unsigned int gpio_led_polarity;
+ unsigned int vref_mute_led_nid; /* pin NID for mute-LED vref control */
+ unsigned int vref_led;
+ int default_polarity;
+
+ unsigned int mic_mute_led_gpio; /* capture mute LED GPIO */
+ unsigned int mic_enabled; /* current mic mute state (bitmask) */
/* stream */
unsigned int stream_delay;
/* analog loopback */
- struct snd_kcontrol_new *aloopback_ctl;
+ const struct snd_kcontrol_new *aloopback_ctl;
+ unsigned int aloopback;
unsigned char aloopback_mask;
unsigned char aloopback_shift;
/* power management */
+ unsigned int power_map_bits;
unsigned int num_pwrs;
- unsigned int *pwr_mapping;
- hda_nid_t *pwr_nids;
- hda_nid_t *dac_list;
-
- /* events */
- struct snd_array events;
-
- /* playback */
- struct hda_input_mux *mono_mux;
- unsigned int cur_mmux;
- struct hda_multi_out multiout;
- hda_nid_t dac_nids[5];
- hda_nid_t hp_dacs[5];
- hda_nid_t speaker_dacs[5];
-
- int volume_offset;
-
- /* capture */
- hda_nid_t *adc_nids;
- unsigned int num_adcs;
- hda_nid_t *mux_nids;
- unsigned int num_muxes;
- hda_nid_t *dmic_nids;
- unsigned int num_dmics;
- hda_nid_t *dmux_nids;
- unsigned int num_dmuxes;
- hda_nid_t *smux_nids;
- unsigned int num_smuxes;
- unsigned int num_analog_muxes;
-
- unsigned long *capvols; /* amp-volume attr: HDA_COMPOSE_AMP_VAL() */
- unsigned long *capsws; /* amp-mute attr: HDA_COMPOSE_AMP_VAL() */
- unsigned int num_caps; /* number of capture volume/switch elements */
-
- struct sigmatel_mic_route ext_mic;
- struct sigmatel_mic_route int_mic;
- struct sigmatel_mic_route dock_mic;
-
- const char * const *spdif_labels;
+ const hda_nid_t *pwr_nids;
+ unsigned int active_adcs;
- hda_nid_t dig_in_nid;
- hda_nid_t mono_nid;
+ /* beep widgets */
hda_nid_t anabeep_nid;
- hda_nid_t digbeep_nid;
-
- /* pin widgets */
- hda_nid_t *pin_nids;
- unsigned int num_pins;
-
- /* codec specific stuff */
- struct hda_verb *init;
- struct snd_kcontrol_new *mixer;
-
- /* capture source */
- struct hda_input_mux *dinput_mux;
- unsigned int cur_dmux[2];
- struct hda_input_mux *input_mux;
- unsigned int cur_mux[3];
- struct hda_input_mux *sinput_mux;
- unsigned int cur_smux[2];
- unsigned int cur_amux;
- hda_nid_t *amp_nids;
- unsigned int powerdown_adcs;
-
- /* i/o switches */
- unsigned int io_switch[2];
- unsigned int clfe_swap;
- hda_nid_t line_switch; /* shared line-in for input and output */
- hda_nid_t mic_switch; /* shared mic-in for input and output */
- hda_nid_t hp_switch; /* NID of HP as line-out */
- unsigned int aloopback;
-
- struct hda_pcm pcm_rec[2]; /* PCM information */
-
- /* dynamic controls and input_mux */
- struct auto_pin_cfg autocfg;
- struct snd_array kctls;
- struct hda_input_mux private_dimux;
- struct hda_input_mux private_imux;
- struct hda_input_mux private_smux;
- struct hda_input_mux private_mono_mux;
-
- /* auto spec */
- unsigned auto_pin_cnt;
- hda_nid_t auto_pin_nids[MAX_PINS_NUM];
- unsigned auto_adc_cnt;
- hda_nid_t auto_adc_nids[MAX_ADCS_NUM];
- hda_nid_t auto_mux_nids[MAX_ADCS_NUM];
- hda_nid_t auto_dmux_nids[MAX_ADCS_NUM];
- unsigned long auto_capvols[MAX_ADCS_NUM];
- unsigned auto_dmic_cnt;
- hda_nid_t auto_dmic_nids[MAX_DMICS_NUM];
-};
-
-static hda_nid_t stac9200_adc_nids[1] = {
- 0x03,
-};
-static hda_nid_t stac9200_mux_nids[1] = {
- 0x0c,
+ /* SPDIF-out mux */
+ const char * const *spdif_labels;
+ struct hda_input_mux spdif_mux;
+ unsigned int cur_smux[2];
};
-static hda_nid_t stac9200_dac_nids[1] = {
- 0x02,
-};
+#define AC_VERB_IDT_SET_POWER_MAP 0x7ec
+#define AC_VERB_IDT_GET_POWER_MAP 0xfec
-static hda_nid_t stac92hd73xx_pwr_nids[8] = {
+static const hda_nid_t stac92hd73xx_pwr_nids[8] = {
0x0a, 0x0b, 0x0c, 0xd, 0x0e,
0x0f, 0x10, 0x11
};
-static hda_nid_t stac92hd73xx_slave_dig_outs[2] = {
- 0x26, 0,
-};
-
-static hda_nid_t stac92hd73xx_adc_nids[2] = {
- 0x1a, 0x1b
-};
-
-#define STAC92HD73XX_NUM_DMICS 2
-static hda_nid_t stac92hd73xx_dmic_nids[STAC92HD73XX_NUM_DMICS + 1] = {
- 0x13, 0x14, 0
+static const hda_nid_t stac92hd83xxx_pwr_nids[7] = {
+ 0x0a, 0x0b, 0x0c, 0xd, 0x0e,
+ 0x0f, 0x10
};
-#define STAC92HD73_DAC_COUNT 5
-
-static hda_nid_t stac92hd73xx_mux_nids[2] = {
- 0x20, 0x21,
+static const hda_nid_t stac92hd71bxx_pwr_nids[3] = {
+ 0x0a, 0x0d, 0x0f
};
-static hda_nid_t stac92hd73xx_dmux_nids[2] = {
- 0x20, 0x21,
-};
-static hda_nid_t stac92hd73xx_smux_nids[2] = {
- 0x22, 0x23,
-};
+/*
+ * PCM hooks
+ */
+static void stac_playback_pcm_hook(struct hda_pcm_stream *hinfo,
+ struct hda_codec *codec,
+ struct snd_pcm_substream *substream,
+ int action)
+{
+ struct sigmatel_spec *spec = codec->spec;
+ if (action == HDA_GEN_PCM_ACT_OPEN && spec->stream_delay)
+ msleep(spec->stream_delay);
+}
-#define STAC92HD73XX_NUM_CAPS 2
-static unsigned long stac92hd73xx_capvols[] = {
- HDA_COMPOSE_AMP_VAL(0x20, 3, 0, HDA_OUTPUT),
- HDA_COMPOSE_AMP_VAL(0x21, 3, 0, HDA_OUTPUT),
-};
-#define stac92hd73xx_capsws stac92hd73xx_capvols
+static void stac_capture_pcm_hook(struct hda_pcm_stream *hinfo,
+ struct hda_codec *codec,
+ struct snd_pcm_substream *substream,
+ int action)
+{
+ struct sigmatel_spec *spec = codec->spec;
+ int i, idx = 0;
-#define STAC92HD83_DAC_COUNT 3
+ if (!spec->powerdown_adcs)
+ return;
-static hda_nid_t stac92hd83xxx_pwr_nids[4] = {
- 0xa, 0xb, 0xd, 0xe,
-};
+ for (i = 0; i < spec->gen.num_all_adcs; i++) {
+ if (spec->gen.all_adcs[i] == hinfo->nid) {
+ idx = i;
+ break;
+ }
+ }
-static hda_nid_t stac92hd83xxx_slave_dig_outs[2] = {
- 0x1e, 0,
-};
+ switch (action) {
+ case HDA_GEN_PCM_ACT_OPEN:
+ msleep(40);
+ snd_hda_codec_write(codec, hinfo->nid, 0,
+ AC_VERB_SET_POWER_STATE, AC_PWRST_D0);
+ spec->active_adcs |= (1 << idx);
+ break;
+ case HDA_GEN_PCM_ACT_CLOSE:
+ snd_hda_codec_write(codec, hinfo->nid, 0,
+ AC_VERB_SET_POWER_STATE, AC_PWRST_D3);
+ spec->active_adcs &= ~(1 << idx);
+ break;
+ }
+}
-static unsigned int stac92hd83xxx_pwr_mapping[4] = {
- 0x03, 0x0c, 0x20, 0x40,
-};
+/*
+ * Early 2006 Intel Macintoshes with STAC9220X5 codecs seem to have a
+ * funky external mute control using GPIO pins.
+ */
-static hda_nid_t stac92hd83xxx_dmic_nids[] = {
- 0x11, 0x20,
-};
+static void stac_gpio_set(struct hda_codec *codec, unsigned int mask,
+ unsigned int dir_mask, unsigned int data)
+{
+ unsigned int gpiostate, gpiomask, gpiodir;
-static hda_nid_t stac92hd71bxx_pwr_nids[3] = {
- 0x0a, 0x0d, 0x0f
-};
+ codec_dbg(codec, "%s msk %x dir %x gpio %x\n", __func__, mask, dir_mask, data);
-static hda_nid_t stac92hd71bxx_adc_nids[2] = {
- 0x12, 0x13,
-};
+ gpiostate = snd_hda_codec_read(codec, codec->afg, 0,
+ AC_VERB_GET_GPIO_DATA, 0);
+ gpiostate = (gpiostate & ~dir_mask) | (data & dir_mask);
-static hda_nid_t stac92hd71bxx_mux_nids[2] = {
- 0x1a, 0x1b
-};
+ gpiomask = snd_hda_codec_read(codec, codec->afg, 0,
+ AC_VERB_GET_GPIO_MASK, 0);
+ gpiomask |= mask;
-static hda_nid_t stac92hd71bxx_dmux_nids[2] = {
- 0x1c, 0x1d,
-};
+ gpiodir = snd_hda_codec_read(codec, codec->afg, 0,
+ AC_VERB_GET_GPIO_DIRECTION, 0);
+ gpiodir |= dir_mask;
-static hda_nid_t stac92hd71bxx_smux_nids[2] = {
- 0x24, 0x25,
-};
+ /* Configure GPIOx as CMOS */
+ snd_hda_codec_write(codec, codec->afg, 0, 0x7e7, 0);
-#define STAC92HD71BXX_NUM_DMICS 2
-static hda_nid_t stac92hd71bxx_dmic_nids[STAC92HD71BXX_NUM_DMICS + 1] = {
- 0x18, 0x19, 0
-};
+ snd_hda_codec_write(codec, codec->afg, 0,
+ AC_VERB_SET_GPIO_MASK, gpiomask);
+ snd_hda_codec_read(codec, codec->afg, 0,
+ AC_VERB_SET_GPIO_DIRECTION, gpiodir); /* sync */
-static hda_nid_t stac92hd71bxx_slave_dig_outs[2] = {
- 0x22, 0
-};
+ msleep(1);
-#define STAC92HD71BXX_NUM_CAPS 2
-static unsigned long stac92hd71bxx_capvols[] = {
- HDA_COMPOSE_AMP_VAL(0x1c, 3, 0, HDA_OUTPUT),
- HDA_COMPOSE_AMP_VAL(0x1d, 3, 0, HDA_OUTPUT),
-};
-#define stac92hd71bxx_capsws stac92hd71bxx_capvols
+ snd_hda_codec_read(codec, codec->afg, 0,
+ AC_VERB_SET_GPIO_DATA, gpiostate); /* sync */
+}
-static hda_nid_t stac925x_adc_nids[1] = {
- 0x03,
-};
+/* hook for controlling mic-mute LED GPIO */
+static void stac_capture_led_hook(struct hda_codec *codec,
+ struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct sigmatel_spec *spec = codec->spec;
+ unsigned int mask;
+ bool cur_mute, prev_mute;
-static hda_nid_t stac925x_mux_nids[1] = {
- 0x0f,
-};
+ if (!kcontrol || !ucontrol)
+ return;
-static hda_nid_t stac925x_dac_nids[1] = {
- 0x02,
-};
+ mask = 1U << snd_ctl_get_ioffidx(kcontrol, &ucontrol->id);
+ prev_mute = !spec->mic_enabled;
+ if (ucontrol->value.integer.value[0] ||
+ ucontrol->value.integer.value[1])
+ spec->mic_enabled |= mask;
+ else
+ spec->mic_enabled &= ~mask;
+ cur_mute = !spec->mic_enabled;
+ if (cur_mute != prev_mute) {
+ if (cur_mute)
+ spec->gpio_data |= spec->mic_mute_led_gpio;
+ else
+ spec->gpio_data &= ~spec->mic_mute_led_gpio;
+ stac_gpio_set(codec, spec->gpio_mask,
+ spec->gpio_dir, spec->gpio_data);
+ }
+}
-#define STAC925X_NUM_DMICS 1
-static hda_nid_t stac925x_dmic_nids[STAC925X_NUM_DMICS + 1] = {
- 0x15, 0
-};
+static int stac_vrefout_set(struct hda_codec *codec,
+ hda_nid_t nid, unsigned int new_vref)
+{
+ int error, pinctl;
-static hda_nid_t stac925x_dmux_nids[1] = {
- 0x14,
-};
+ codec_dbg(codec, "%s, nid %x ctl %x\n", __func__, nid, new_vref);
+ pinctl = snd_hda_codec_read(codec, nid, 0,
+ AC_VERB_GET_PIN_WIDGET_CONTROL, 0);
-static unsigned long stac925x_capvols[] = {
- HDA_COMPOSE_AMP_VAL(0x09, 3, 0, HDA_OUTPUT),
-};
-static unsigned long stac925x_capsws[] = {
- HDA_COMPOSE_AMP_VAL(0x14, 3, 0, HDA_OUTPUT),
-};
+ if (pinctl < 0)
+ return pinctl;
-static hda_nid_t stac922x_adc_nids[2] = {
- 0x06, 0x07,
-};
+ pinctl &= 0xff;
+ pinctl &= ~AC_PINCTL_VREFEN;
+ pinctl |= (new_vref & AC_PINCTL_VREFEN);
-static hda_nid_t stac922x_mux_nids[2] = {
- 0x12, 0x13,
-};
+ error = snd_hda_set_pin_ctl_cache(codec, nid, pinctl);
+ if (error < 0)
+ return error;
-#define STAC922X_NUM_CAPS 2
-static unsigned long stac922x_capvols[] = {
- HDA_COMPOSE_AMP_VAL(0x17, 3, 0, HDA_INPUT),
- HDA_COMPOSE_AMP_VAL(0x18, 3, 0, HDA_INPUT),
-};
-#define stac922x_capsws stac922x_capvols
+ return 1;
+}
-static hda_nid_t stac927x_slave_dig_outs[2] = {
- 0x1f, 0,
-};
+/* prevent codec AFG to D3 state when vref-out pin is used for mute LED */
+/* this hook is set in stac_setup_gpio() */
+static unsigned int stac_vref_led_power_filter(struct hda_codec *codec,
+ hda_nid_t nid,
+ unsigned int power_state)
+{
+ if (nid == codec->afg && power_state == AC_PWRST_D3)
+ return AC_PWRST_D1;
+ return snd_hda_gen_path_power_filter(codec, nid, power_state);
+}
-static hda_nid_t stac927x_adc_nids[3] = {
- 0x07, 0x08, 0x09
-};
+/* update mute-LED accoring to the master switch */
+static void stac_update_led_status(struct hda_codec *codec, int enabled)
+{
+ struct sigmatel_spec *spec = codec->spec;
+ int muted = !enabled;
-static hda_nid_t stac927x_mux_nids[3] = {
- 0x15, 0x16, 0x17
-};
+ if (!spec->gpio_led)
+ return;
-static hda_nid_t stac927x_smux_nids[1] = {
- 0x21,
-};
+ /* LED state is inverted on these systems */
+ if (spec->gpio_led_polarity)
+ muted = !muted;
-static hda_nid_t stac927x_dac_nids[6] = {
- 0x02, 0x03, 0x04, 0x05, 0x06, 0
-};
+ if (!spec->vref_mute_led_nid) {
+ if (muted)
+ spec->gpio_data |= spec->gpio_led;
+ else
+ spec->gpio_data &= ~spec->gpio_led;
+ stac_gpio_set(codec, spec->gpio_mask,
+ spec->gpio_dir, spec->gpio_data);
+ } else {
+ spec->vref_led = muted ? AC_PINCTL_VREF_50 : AC_PINCTL_VREF_GRD;
+ stac_vrefout_set(codec, spec->vref_mute_led_nid,
+ spec->vref_led);
+ }
+}
-static hda_nid_t stac927x_dmux_nids[1] = {
- 0x1b,
-};
+/* vmaster hook to update mute LED */
+static void stac_vmaster_hook(void *private_data, int val)
+{
+ stac_update_led_status(private_data, val);
+}
-#define STAC927X_NUM_DMICS 2
-static hda_nid_t stac927x_dmic_nids[STAC927X_NUM_DMICS + 1] = {
- 0x13, 0x14, 0
-};
+/* automute hook to handle GPIO mute and EAPD updates */
+static void stac_update_outputs(struct hda_codec *codec)
+{
+ struct sigmatel_spec *spec = codec->spec;
-#define STAC927X_NUM_CAPS 3
-static unsigned long stac927x_capvols[] = {
- HDA_COMPOSE_AMP_VAL(0x18, 3, 0, HDA_INPUT),
- HDA_COMPOSE_AMP_VAL(0x19, 3, 0, HDA_INPUT),
- HDA_COMPOSE_AMP_VAL(0x1a, 3, 0, HDA_INPUT),
-};
-static unsigned long stac927x_capsws[] = {
- HDA_COMPOSE_AMP_VAL(0x1b, 3, 0, HDA_OUTPUT),
- HDA_COMPOSE_AMP_VAL(0x1c, 3, 0, HDA_OUTPUT),
- HDA_COMPOSE_AMP_VAL(0x1d, 3, 0, HDA_OUTPUT),
-};
+ if (spec->gpio_mute)
+ spec->gen.master_mute =
+ !(snd_hda_codec_read(codec, codec->afg, 0,
+ AC_VERB_GET_GPIO_DATA, 0) & spec->gpio_mute);
-static const char * const stac927x_spdif_labels[5] = {
- "Digital Playback", "ADAT", "Analog Mux 1",
- "Analog Mux 2", "Analog Mux 3"
-};
+ snd_hda_gen_update_outputs(codec);
-static hda_nid_t stac9205_adc_nids[2] = {
- 0x12, 0x13
-};
+ if (spec->eapd_mask && spec->eapd_switch) {
+ unsigned int val = spec->gpio_data;
+ if (spec->gen.speaker_muted)
+ val &= ~spec->eapd_mask;
+ else
+ val |= spec->eapd_mask;
+ if (spec->gpio_data != val) {
+ spec->gpio_data = val;
+ stac_gpio_set(codec, spec->gpio_mask, spec->gpio_dir,
+ val);
+ }
+ }
+}
-static hda_nid_t stac9205_mux_nids[2] = {
- 0x19, 0x1a
-};
+static void stac_toggle_power_map(struct hda_codec *codec, hda_nid_t nid,
+ bool enable, bool do_write)
+{
+ struct sigmatel_spec *spec = codec->spec;
+ unsigned int idx, val;
-static hda_nid_t stac9205_dmux_nids[1] = {
- 0x1d,
-};
+ for (idx = 0; idx < spec->num_pwrs; idx++) {
+ if (spec->pwr_nids[idx] == nid)
+ break;
+ }
+ if (idx >= spec->num_pwrs)
+ return;
-static hda_nid_t stac9205_smux_nids[1] = {
- 0x21,
-};
+ idx = 1 << idx;
-#define STAC9205_NUM_DMICS 2
-static hda_nid_t stac9205_dmic_nids[STAC9205_NUM_DMICS + 1] = {
- 0x17, 0x18, 0
-};
+ val = spec->power_map_bits;
+ if (enable)
+ val &= ~idx;
+ else
+ val |= idx;
-#define STAC9205_NUM_CAPS 2
-static unsigned long stac9205_capvols[] = {
- HDA_COMPOSE_AMP_VAL(0x1b, 3, 0, HDA_INPUT),
- HDA_COMPOSE_AMP_VAL(0x1c, 3, 0, HDA_INPUT),
-};
-static unsigned long stac9205_capsws[] = {
- HDA_COMPOSE_AMP_VAL(0x1d, 3, 0, HDA_OUTPUT),
- HDA_COMPOSE_AMP_VAL(0x1e, 3, 0, HDA_OUTPUT),
-};
+ /* power down unused output ports */
+ if (val != spec->power_map_bits) {
+ spec->power_map_bits = val;
+ if (do_write)
+ snd_hda_codec_write(codec, codec->afg, 0,
+ AC_VERB_IDT_SET_POWER_MAP, val);
+ }
+}
-static hda_nid_t stac9200_pin_nids[8] = {
- 0x08, 0x09, 0x0d, 0x0e,
- 0x0f, 0x10, 0x11, 0x12,
-};
+/* update power bit per jack plug/unplug */
+static void jack_update_power(struct hda_codec *codec,
+ struct hda_jack_tbl *jack)
+{
+ struct sigmatel_spec *spec = codec->spec;
+ int i;
-static hda_nid_t stac925x_pin_nids[8] = {
- 0x07, 0x08, 0x0a, 0x0b,
- 0x0c, 0x0d, 0x10, 0x11,
-};
+ if (!spec->num_pwrs)
+ return;
-static hda_nid_t stac922x_pin_nids[10] = {
- 0x0a, 0x0b, 0x0c, 0x0d, 0x0e,
- 0x0f, 0x10, 0x11, 0x15, 0x1b,
-};
+ if (jack && jack->nid) {
+ stac_toggle_power_map(codec, jack->nid,
+ snd_hda_jack_detect(codec, jack->nid),
+ true);
+ return;
+ }
-static hda_nid_t stac92hd73xx_pin_nids[13] = {
- 0x0a, 0x0b, 0x0c, 0x0d, 0x0e,
- 0x0f, 0x10, 0x11, 0x12, 0x13,
- 0x14, 0x22, 0x23
-};
+ /* update all jacks */
+ for (i = 0; i < spec->num_pwrs; i++) {
+ hda_nid_t nid = spec->pwr_nids[i];
+ jack = snd_hda_jack_tbl_get(codec, nid);
+ if (!jack || !jack->action)
+ continue;
+ if (jack->action == STAC_PWR_EVENT ||
+ jack->action <= HDA_GEN_LAST_EVENT)
+ stac_toggle_power_map(codec, nid,
+ snd_hda_jack_detect(codec, nid),
+ false);
+ }
-#define STAC92HD71BXX_NUM_PINS 13
-static hda_nid_t stac92hd71bxx_pin_nids_4port[STAC92HD71BXX_NUM_PINS] = {
- 0x0a, 0x0b, 0x0c, 0x0d, 0x00,
- 0x00, 0x14, 0x18, 0x19, 0x1e,
- 0x1f, 0x20, 0x27
-};
-static hda_nid_t stac92hd71bxx_pin_nids_6port[STAC92HD71BXX_NUM_PINS] = {
- 0x0a, 0x0b, 0x0c, 0x0d, 0x0e,
- 0x0f, 0x14, 0x18, 0x19, 0x1e,
- 0x1f, 0x20, 0x27
-};
+ snd_hda_codec_write(codec, codec->afg, 0, AC_VERB_IDT_SET_POWER_MAP,
+ spec->power_map_bits);
+}
-static hda_nid_t stac927x_pin_nids[14] = {
- 0x0a, 0x0b, 0x0c, 0x0d, 0x0e,
- 0x0f, 0x10, 0x11, 0x12, 0x13,
- 0x14, 0x21, 0x22, 0x23,
-};
+static void stac_hp_automute(struct hda_codec *codec,
+ struct hda_jack_tbl *jack)
+{
+ snd_hda_gen_hp_automute(codec, jack);
+ jack_update_power(codec, jack);
+}
-static hda_nid_t stac9205_pin_nids[12] = {
- 0x0a, 0x0b, 0x0c, 0x0d, 0x0e,
- 0x0f, 0x14, 0x16, 0x17, 0x18,
- 0x21, 0x22,
-};
+static void stac_line_automute(struct hda_codec *codec,
+ struct hda_jack_tbl *jack)
+{
+ snd_hda_gen_line_automute(codec, jack);
+ jack_update_power(codec, jack);
+}
-static int stac92xx_dmux_enum_info(struct snd_kcontrol *kcontrol,
- struct snd_ctl_elem_info *uinfo)
+static void stac_mic_autoswitch(struct hda_codec *codec,
+ struct hda_jack_tbl *jack)
{
- struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
- struct sigmatel_spec *spec = codec->spec;
- return snd_hda_input_mux_info(spec->dinput_mux, uinfo);
+ snd_hda_gen_mic_autoswitch(codec, jack);
+ jack_update_power(codec, jack);
}
-static int stac92xx_dmux_enum_get(struct snd_kcontrol *kcontrol,
- struct snd_ctl_elem_value *ucontrol)
+static void stac_vref_event(struct hda_codec *codec, struct hda_jack_tbl *event)
{
- struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
- struct sigmatel_spec *spec = codec->spec;
- unsigned int dmux_idx = snd_ctl_get_ioffidx(kcontrol, &ucontrol->id);
+ unsigned int data;
- ucontrol->value.enumerated.item[0] = spec->cur_dmux[dmux_idx];
- return 0;
+ data = snd_hda_codec_read(codec, codec->afg, 0,
+ AC_VERB_GET_GPIO_DATA, 0);
+ /* toggle VREF state based on GPIOx status */
+ snd_hda_codec_write(codec, codec->afg, 0, 0x7e0,
+ !!(data & (1 << event->private_data)));
}
-static int stac92xx_dmux_enum_put(struct snd_kcontrol *kcontrol,
- struct snd_ctl_elem_value *ucontrol)
+/* initialize the power map and enable the power event to jacks that
+ * haven't been assigned to automute
+ */
+static void stac_init_power_map(struct hda_codec *codec)
{
- struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
struct sigmatel_spec *spec = codec->spec;
- unsigned int dmux_idx = snd_ctl_get_ioffidx(kcontrol, &ucontrol->id);
+ int i;
- return snd_hda_input_mux_put(codec, spec->dinput_mux, ucontrol,
- spec->dmux_nids[dmux_idx], &spec->cur_dmux[dmux_idx]);
+ for (i = 0; i < spec->num_pwrs; i++) {
+ hda_nid_t nid = spec->pwr_nids[i];
+ unsigned int def_conf = snd_hda_codec_get_pincfg(codec, nid);
+ def_conf = get_defcfg_connect(def_conf);
+ if (snd_hda_jack_tbl_get(codec, nid))
+ continue;
+ if (def_conf == AC_JACK_PORT_COMPLEX &&
+ !(spec->vref_mute_led_nid == nid ||
+ is_jack_detectable(codec, nid))) {
+ snd_hda_jack_detect_enable_callback(codec, nid,
+ STAC_PWR_EVENT,
+ jack_update_power);
+ } else {
+ if (def_conf == AC_JACK_PORT_NONE)
+ stac_toggle_power_map(codec, nid, false, false);
+ else
+ stac_toggle_power_map(codec, nid, true, false);
+ }
+ }
}
-static int stac92xx_smux_enum_info(struct snd_kcontrol *kcontrol,
- struct snd_ctl_elem_info *uinfo)
+/*
+ */
+
+static inline bool get_int_hint(struct hda_codec *codec, const char *key,
+ int *valp)
+{
+ return !snd_hda_get_int_hint(codec, key, valp);
+}
+
+/* override some hints from the hwdep entry */
+static void stac_store_hints(struct hda_codec *codec)
{
- struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
struct sigmatel_spec *spec = codec->spec;
- return snd_hda_input_mux_info(spec->sinput_mux, uinfo);
+ int val;
+
+ if (get_int_hint(codec, "gpio_mask", &spec->gpio_mask)) {
+ spec->eapd_mask = spec->gpio_dir = spec->gpio_data =
+ spec->gpio_mask;
+ }
+ if (get_int_hint(codec, "gpio_dir", &spec->gpio_dir))
+ spec->gpio_mask &= spec->gpio_mask;
+ if (get_int_hint(codec, "gpio_data", &spec->gpio_data))
+ spec->gpio_dir &= spec->gpio_mask;
+ if (get_int_hint(codec, "eapd_mask", &spec->eapd_mask))
+ spec->eapd_mask &= spec->gpio_mask;
+ if (get_int_hint(codec, "gpio_mute", &spec->gpio_mute))
+ spec->gpio_mute &= spec->gpio_mask;
+ val = snd_hda_get_bool_hint(codec, "eapd_switch");
+ if (val >= 0)
+ spec->eapd_switch = val;
}
-static int stac92xx_smux_enum_get(struct snd_kcontrol *kcontrol,
- struct snd_ctl_elem_value *ucontrol)
+/*
+ * loopback controls
+ */
+
+#define stac_aloopback_info snd_ctl_boolean_mono_info
+
+static int stac_aloopback_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
{
struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
+ unsigned int idx = snd_ctl_get_ioffidx(kcontrol, &ucontrol->id);
struct sigmatel_spec *spec = codec->spec;
- unsigned int smux_idx = snd_ctl_get_ioffidx(kcontrol, &ucontrol->id);
- ucontrol->value.enumerated.item[0] = spec->cur_smux[smux_idx];
+ ucontrol->value.integer.value[0] = !!(spec->aloopback &
+ (spec->aloopback_mask << idx));
return 0;
}
-static int stac92xx_smux_enum_put(struct snd_kcontrol *kcontrol,
- struct snd_ctl_elem_value *ucontrol)
+static int stac_aloopback_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
{
struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
struct sigmatel_spec *spec = codec->spec;
- struct hda_input_mux *smux = &spec->private_smux;
- unsigned int smux_idx = snd_ctl_get_ioffidx(kcontrol, &ucontrol->id);
- int err, val;
- hda_nid_t nid;
+ unsigned int idx = snd_ctl_get_ioffidx(kcontrol, &ucontrol->id);
+ unsigned int dac_mode;
+ unsigned int val, idx_val;
- err = snd_hda_input_mux_put(codec, spec->sinput_mux, ucontrol,
- spec->smux_nids[smux_idx], &spec->cur_smux[smux_idx]);
- if (err < 0)
- return err;
+ idx_val = spec->aloopback_mask << idx;
+ if (ucontrol->value.integer.value[0])
+ val = spec->aloopback | idx_val;
+ else
+ val = spec->aloopback & ~idx_val;
+ if (spec->aloopback == val)
+ return 0;
- if (spec->spdif_mute) {
- if (smux_idx == 0)
- nid = spec->multiout.dig_out_nid;
- else
- nid = codec->slave_dig_outs[smux_idx - 1];
- if (spec->cur_smux[smux_idx] == smux->num_items - 1)
- val = HDA_AMP_MUTE;
- else
- val = 0;
- /* un/mute SPDIF out */
- snd_hda_codec_amp_stereo(codec, nid, HDA_OUTPUT, 0,
- HDA_AMP_MUTE, val);
+ spec->aloopback = val;
+
+ /* Only return the bits defined by the shift value of the
+ * first two bytes of the mask
+ */
+ dac_mode = snd_hda_codec_read(codec, codec->afg, 0,
+ kcontrol->private_value & 0xFFFF, 0x0);
+ dac_mode >>= spec->aloopback_shift;
+
+ if (spec->aloopback & idx_val) {
+ snd_hda_power_up(codec);
+ dac_mode |= idx_val;
+ } else {
+ snd_hda_power_down(codec);
+ dac_mode &= ~idx_val;
}
- return 0;
+
+ snd_hda_codec_write_cache(codec, codec->afg, 0,
+ kcontrol->private_value >> 16, dac_mode);
+
+ return 1;
}
-static unsigned int stac92xx_vref_set(struct hda_codec *codec,
- hda_nid_t nid, unsigned int new_vref)
+#define STAC_ANALOG_LOOPBACK(verb_read, verb_write, cnt) \
+ { \
+ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, \
+ .name = "Analog Loopback", \
+ .count = cnt, \
+ .info = stac_aloopback_info, \
+ .get = stac_aloopback_get, \
+ .put = stac_aloopback_put, \
+ .private_value = verb_read | (verb_write << 16), \
+ }
+
+/*
+ * Mute LED handling on HP laptops
+ */
+
+/* check whether it's a HP laptop with a docking port */
+static bool hp_bnb2011_with_dock(struct hda_codec *codec)
{
- int error;
- unsigned int pincfg;
- pincfg = snd_hda_codec_read(codec, nid, 0,
- AC_VERB_GET_PIN_WIDGET_CONTROL, 0);
+ if (codec->vendor_id != 0x111d7605 &&
+ codec->vendor_id != 0x111d76d1)
+ return false;
- pincfg &= 0xff;
- pincfg &= ~(AC_PINCTL_VREFEN | AC_PINCTL_IN_EN | AC_PINCTL_OUT_EN);
- pincfg |= new_vref;
+ switch (codec->subsystem_id) {
+ case 0x103c1618:
+ case 0x103c1619:
+ case 0x103c161a:
+ case 0x103c161b:
+ case 0x103c161c:
+ case 0x103c161d:
+ case 0x103c161e:
+ case 0x103c161f:
- if (new_vref == AC_PINCTL_VREF_HIZ)
- pincfg |= AC_PINCTL_OUT_EN;
- else
- pincfg |= AC_PINCTL_IN_EN;
+ case 0x103c162a:
+ case 0x103c162b:
- error = snd_hda_codec_write_cache(codec, nid, 0,
- AC_VERB_SET_PIN_WIDGET_CONTROL, pincfg);
- if (error < 0)
- return error;
- else
- return 1;
+ case 0x103c1630:
+ case 0x103c1631:
+
+ case 0x103c1633:
+ case 0x103c1634:
+ case 0x103c1635:
+
+ case 0x103c3587:
+ case 0x103c3588:
+ case 0x103c3589:
+ case 0x103c358a:
+
+ case 0x103c3667:
+ case 0x103c3668:
+ case 0x103c3669:
+
+ return true;
+ }
+ return false;
}
-static unsigned int stac92xx_vref_get(struct hda_codec *codec, hda_nid_t nid)
+static bool hp_blike_system(u32 subsystem_id)
{
- unsigned int vref;
- vref = snd_hda_codec_read(codec, nid, 0,
- AC_VERB_GET_PIN_WIDGET_CONTROL, 0);
- vref &= AC_PINCTL_VREFEN;
- return vref;
+ switch (subsystem_id) {
+ case 0x103c1520:
+ case 0x103c1521:
+ case 0x103c1523:
+ case 0x103c1524:
+ case 0x103c1525:
+ case 0x103c1722:
+ case 0x103c1723:
+ case 0x103c1724:
+ case 0x103c1725:
+ case 0x103c1726:
+ case 0x103c1727:
+ case 0x103c1728:
+ case 0x103c1729:
+ case 0x103c172a:
+ case 0x103c172b:
+ case 0x103c307e:
+ case 0x103c307f:
+ case 0x103c3080:
+ case 0x103c3081:
+ case 0x103c7007:
+ case 0x103c7008:
+ return true;
+ }
+ return false;
}
-static int stac92xx_mux_enum_info(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *uinfo)
+static void set_hp_led_gpio(struct hda_codec *codec)
{
- struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
struct sigmatel_spec *spec = codec->spec;
- return snd_hda_input_mux_info(spec->input_mux, uinfo);
+ unsigned int gpio;
+
+ if (spec->gpio_led)
+ return;
+
+ gpio = snd_hda_param_read(codec, codec->afg, AC_PAR_GPIO_CAP);
+ gpio &= AC_GPIO_IO_COUNT;
+ if (gpio > 3)
+ spec->gpio_led = 0x08; /* GPIO 3 */
+ else
+ spec->gpio_led = 0x01; /* GPIO 0 */
}
-static int stac92xx_mux_enum_get(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
+/*
+ * This method searches for the mute LED GPIO configuration
+ * provided as OEM string in SMBIOS. The format of that string
+ * is HP_Mute_LED_P_G or HP_Mute_LED_P
+ * where P can be 0 or 1 and defines mute LED GPIO control state (low/high)
+ * that corresponds to the NOT muted state of the master volume
+ * and G is the index of the GPIO to use as the mute LED control (0..9)
+ * If _G portion is missing it is assigned based on the codec ID
+ *
+ * So, HP B-series like systems may have HP_Mute_LED_0 (current models)
+ * or HP_Mute_LED_0_3 (future models) OEM SMBIOS strings
+ *
+ *
+ * The dv-series laptops don't seem to have the HP_Mute_LED* strings in
+ * SMBIOS - at least the ones I have seen do not have them - which include
+ * my own system (HP Pavilion dv6-1110ax) and my cousin's
+ * HP Pavilion dv9500t CTO.
+ * Need more information on whether it is true across the entire series.
+ * -- kunal
+ */
+static int find_mute_led_cfg(struct hda_codec *codec, int default_polarity)
{
- struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
struct sigmatel_spec *spec = codec->spec;
- unsigned int adc_idx = snd_ctl_get_ioffidx(kcontrol, &ucontrol->id);
+ const struct dmi_device *dev = NULL;
+
+ if (get_int_hint(codec, "gpio_led", &spec->gpio_led)) {
+ get_int_hint(codec, "gpio_led_polarity",
+ &spec->gpio_led_polarity);
+ return 1;
+ }
+
+ while ((dev = dmi_find_device(DMI_DEV_TYPE_OEM_STRING, NULL, dev))) {
+ if (sscanf(dev->name, "HP_Mute_LED_%u_%x",
+ &spec->gpio_led_polarity,
+ &spec->gpio_led) == 2) {
+ unsigned int max_gpio;
+ max_gpio = snd_hda_param_read(codec, codec->afg,
+ AC_PAR_GPIO_CAP);
+ max_gpio &= AC_GPIO_IO_COUNT;
+ if (spec->gpio_led < max_gpio)
+ spec->gpio_led = 1 << spec->gpio_led;
+ else
+ spec->vref_mute_led_nid = spec->gpio_led;
+ return 1;
+ }
+ if (sscanf(dev->name, "HP_Mute_LED_%u",
+ &spec->gpio_led_polarity) == 1) {
+ set_hp_led_gpio(codec);
+ return 1;
+ }
+ /* BIOS bug: unfilled OEM string */
+ if (strstr(dev->name, "HP_Mute_LED_P_G")) {
+ set_hp_led_gpio(codec);
+ if (default_polarity >= 0)
+ spec->gpio_led_polarity = default_polarity;
+ else
+ spec->gpio_led_polarity = 1;
+ return 1;
+ }
+ }
- ucontrol->value.enumerated.item[0] = spec->cur_mux[adc_idx];
+ /*
+ * Fallback case - if we don't find the DMI strings,
+ * we statically set the GPIO - if not a B-series system
+ * and default polarity is provided
+ */
+ if (!hp_blike_system(codec->subsystem_id) &&
+ (default_polarity == 0 || default_polarity == 1)) {
+ set_hp_led_gpio(codec);
+ spec->gpio_led_polarity = default_polarity;
+ return 1;
+ }
return 0;
}
-static int stac92xx_mux_enum_put(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
+/* check whether a built-in speaker is included in parsed pins */
+static bool has_builtin_speaker(struct hda_codec *codec)
{
- struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
struct sigmatel_spec *spec = codec->spec;
- unsigned int adc_idx = snd_ctl_get_ioffidx(kcontrol, &ucontrol->id);
- const struct hda_input_mux *imux = spec->input_mux;
- unsigned int idx, prev_idx, didx;
-
- idx = ucontrol->value.enumerated.item[0];
- if (idx >= imux->num_items)
- idx = imux->num_items - 1;
- prev_idx = spec->cur_mux[adc_idx];
- if (prev_idx == idx)
- return 0;
- if (idx < spec->num_analog_muxes) {
- snd_hda_codec_write_cache(codec, spec->mux_nids[adc_idx], 0,
- AC_VERB_SET_CONNECT_SEL,
- imux->items[idx].index);
- if (prev_idx >= spec->num_analog_muxes &&
- spec->mux_nids[adc_idx] != spec->dmux_nids[adc_idx]) {
- imux = spec->dinput_mux;
- /* 0 = analog */
- snd_hda_codec_write_cache(codec,
- spec->dmux_nids[adc_idx], 0,
- AC_VERB_SET_CONNECT_SEL,
- imux->items[0].index);
- }
+ hda_nid_t *nid_pin;
+ int nids, i;
+
+ if (spec->gen.autocfg.line_out_type == AUTO_PIN_SPEAKER_OUT) {
+ nid_pin = spec->gen.autocfg.line_out_pins;
+ nids = spec->gen.autocfg.line_outs;
} else {
- imux = spec->dinput_mux;
- /* first dimux item is hardcoded to select analog imux,
- * so lets skip it
- */
- didx = idx - spec->num_analog_muxes + 1;
- snd_hda_codec_write_cache(codec, spec->dmux_nids[adc_idx], 0,
- AC_VERB_SET_CONNECT_SEL,
- imux->items[didx].index);
+ nid_pin = spec->gen.autocfg.speaker_pins;
+ nids = spec->gen.autocfg.speaker_outs;
}
- spec->cur_mux[adc_idx] = idx;
- return 1;
+
+ for (i = 0; i < nids; i++) {
+ unsigned int def_conf = snd_hda_codec_get_pincfg(codec, nid_pin[i]);
+ if (snd_hda_get_input_pin_attr(def_conf) == INPUT_PIN_ATTR_INT)
+ return true;
+ }
+ return false;
}
-static int stac92xx_mono_mux_enum_info(struct snd_kcontrol *kcontrol,
- struct snd_ctl_elem_info *uinfo)
+/*
+ * PC beep controls
+ */
+
+/* create PC beep volume controls */
+static int stac_auto_create_beep_ctls(struct hda_codec *codec,
+ hda_nid_t nid)
{
- struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
struct sigmatel_spec *spec = codec->spec;
- return snd_hda_input_mux_info(spec->mono_mux, uinfo);
+ u32 caps = query_amp_caps(codec, nid, HDA_OUTPUT);
+ struct snd_kcontrol_new *knew;
+ static struct snd_kcontrol_new abeep_mute_ctl =
+ HDA_CODEC_MUTE(NULL, 0, 0, 0);
+ static struct snd_kcontrol_new dbeep_mute_ctl =
+ HDA_CODEC_MUTE_BEEP(NULL, 0, 0, 0);
+ static struct snd_kcontrol_new beep_vol_ctl =
+ HDA_CODEC_VOLUME(NULL, 0, 0, 0);
+
+ /* check for mute support for the the amp */
+ if ((caps & AC_AMPCAP_MUTE) >> AC_AMPCAP_MUTE_SHIFT) {
+ const struct snd_kcontrol_new *temp;
+ if (spec->anabeep_nid == nid)
+ temp = &abeep_mute_ctl;
+ else
+ temp = &dbeep_mute_ctl;
+ knew = snd_hda_gen_add_kctl(&spec->gen,
+ "Beep Playback Switch", temp);
+ if (!knew)
+ return -ENOMEM;
+ knew->private_value =
+ HDA_COMPOSE_AMP_VAL(nid, 1, 0, HDA_OUTPUT);
+ }
+
+ /* check to see if there is volume support for the amp */
+ if ((caps & AC_AMPCAP_NUM_STEPS) >> AC_AMPCAP_NUM_STEPS_SHIFT) {
+ knew = snd_hda_gen_add_kctl(&spec->gen,
+ "Beep Playback Volume",
+ &beep_vol_ctl);
+ if (!knew)
+ return -ENOMEM;
+ knew->private_value =
+ HDA_COMPOSE_AMP_VAL(nid, 1, 0, HDA_OUTPUT);
+ }
+ return 0;
}
-static int stac92xx_mono_mux_enum_get(struct snd_kcontrol *kcontrol,
- struct snd_ctl_elem_value *ucontrol)
+#ifdef CONFIG_SND_HDA_INPUT_BEEP
+#define stac_dig_beep_switch_info snd_ctl_boolean_mono_info
+
+static int stac_dig_beep_switch_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
{
struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
- struct sigmatel_spec *spec = codec->spec;
-
- ucontrol->value.enumerated.item[0] = spec->cur_mmux;
+ ucontrol->value.integer.value[0] = codec->beep->enabled;
return 0;
}
-static int stac92xx_mono_mux_enum_put(struct snd_kcontrol *kcontrol,
- struct snd_ctl_elem_value *ucontrol)
+static int stac_dig_beep_switch_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
{
struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
+ return snd_hda_enable_beep_device(codec, ucontrol->value.integer.value[0]);
+}
+
+static const struct snd_kcontrol_new stac_dig_beep_ctrl = {
+ .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+ .name = "Beep Playback Switch",
+ .info = stac_dig_beep_switch_info,
+ .get = stac_dig_beep_switch_get,
+ .put = stac_dig_beep_switch_put,
+};
+
+static int stac_beep_switch_ctl(struct hda_codec *codec)
+{
struct sigmatel_spec *spec = codec->spec;
- return snd_hda_input_mux_put(codec, spec->mono_mux, ucontrol,
- spec->mono_nid, &spec->cur_mmux);
+ if (!snd_hda_gen_add_kctl(&spec->gen, NULL, &stac_dig_beep_ctrl))
+ return -ENOMEM;
+ return 0;
}
+#endif
-#define stac92xx_aloopback_info snd_ctl_boolean_mono_info
+/*
+ * SPDIF-out mux controls
+ */
-static int stac92xx_aloopback_get(struct snd_kcontrol *kcontrol,
- struct snd_ctl_elem_value *ucontrol)
+static int stac_smux_enum_info(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_info *uinfo)
{
struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
- unsigned int idx = snd_ctl_get_ioffidx(kcontrol, &ucontrol->id);
struct sigmatel_spec *spec = codec->spec;
+ return snd_hda_input_mux_info(&spec->spdif_mux, uinfo);
+}
- ucontrol->value.integer.value[0] = !!(spec->aloopback &
- (spec->aloopback_mask << idx));
+static int stac_smux_enum_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
+ struct sigmatel_spec *spec = codec->spec;
+ unsigned int smux_idx = snd_ctl_get_ioffidx(kcontrol, &ucontrol->id);
+
+ ucontrol->value.enumerated.item[0] = spec->cur_smux[smux_idx];
return 0;
}
-static int stac92xx_aloopback_put(struct snd_kcontrol *kcontrol,
- struct snd_ctl_elem_value *ucontrol)
+static int stac_smux_enum_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
{
struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
struct sigmatel_spec *spec = codec->spec;
- unsigned int idx = snd_ctl_get_ioffidx(kcontrol, &ucontrol->id);
- unsigned int dac_mode;
- unsigned int val, idx_val;
+ unsigned int smux_idx = snd_ctl_get_ioffidx(kcontrol, &ucontrol->id);
- idx_val = spec->aloopback_mask << idx;
- if (ucontrol->value.integer.value[0])
- val = spec->aloopback | idx_val;
- else
- val = spec->aloopback & ~idx_val;
- if (spec->aloopback == val)
- return 0;
+ return snd_hda_input_mux_put(codec, &spec->spdif_mux, ucontrol,
+ spec->gen.autocfg.dig_out_pins[smux_idx],
+ &spec->cur_smux[smux_idx]);
+}
- spec->aloopback = val;
+static struct snd_kcontrol_new stac_smux_mixer = {
+ .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+ .name = "IEC958 Playback Source",
+ /* count set later */
+ .info = stac_smux_enum_info,
+ .get = stac_smux_enum_get,
+ .put = stac_smux_enum_put,
+};
- /* Only return the bits defined by the shift value of the
- * first two bytes of the mask
- */
- dac_mode = snd_hda_codec_read(codec, codec->afg, 0,
- kcontrol->private_value & 0xFFFF, 0x0);
- dac_mode >>= spec->aloopback_shift;
+static const char * const stac_spdif_labels[] = {
+ "Digital Playback", "Analog Mux 1", "Analog Mux 2", NULL
+};
- if (spec->aloopback & idx_val) {
- snd_hda_power_up(codec);
- dac_mode |= idx_val;
- } else {
- snd_hda_power_down(codec);
- dac_mode &= ~idx_val;
+static int stac_create_spdif_mux_ctls(struct hda_codec *codec)
+{
+ struct sigmatel_spec *spec = codec->spec;
+ struct auto_pin_cfg *cfg = &spec->gen.autocfg;
+ const char * const *labels = spec->spdif_labels;
+ struct snd_kcontrol_new *kctl;
+ int i, num_cons;
+
+ if (cfg->dig_outs < 1)
+ return 0;
+
+ num_cons = snd_hda_get_num_conns(codec, cfg->dig_out_pins[0]);
+ if (num_cons <= 1)
+ return 0;
+
+ if (!labels)
+ labels = stac_spdif_labels;
+ for (i = 0; i < num_cons; i++) {
+ if (snd_BUG_ON(!labels[i]))
+ return -EINVAL;
+ snd_hda_add_imux_item(&spec->spdif_mux, labels[i], i, NULL);
}
- snd_hda_codec_write_cache(codec, codec->afg, 0,
- kcontrol->private_value >> 16, dac_mode);
+ kctl = snd_hda_gen_add_kctl(&spec->gen, NULL, &stac_smux_mixer);
+ if (!kctl)
+ return -ENOMEM;
+ kctl->count = cfg->dig_outs;
- return 1;
+ return 0;
}
-static struct hda_verb stac9200_core_init[] = {
+/*
+ */
+
+static const struct hda_verb stac9200_core_init[] = {
/* set dac0mux for dac converter */
{ 0x07, AC_VERB_SET_CONNECT_SEL, 0x00},
{}
};
-static struct hda_verb stac9200_eapd_init[] = {
+static const struct hda_verb stac9200_eapd_init[] = {
/* set dac0mux for dac converter */
{0x07, AC_VERB_SET_CONNECT_SEL, 0x00},
{0x08, AC_VERB_SET_EAPD_BTLENABLE, 0x02},
{}
};
-static struct hda_verb dell_eq_core_init[] = {
+static const struct hda_verb dell_eq_core_init[] = {
/* set master volume to max value without distortion
* and direct control */
{ 0x1f, AC_VERB_SET_VOLUME_KNOB_CONTROL, 0xec},
{}
};
-static struct hda_verb stac92hd73xx_core_init[] = {
+static const struct hda_verb stac92hd73xx_core_init[] = {
/* set master volume and direct control */
{ 0x1f, AC_VERB_SET_VOLUME_KNOB_CONTROL, 0xff},
{}
};
-static struct hda_verb stac92hd83xxx_core_init[] = {
+static const struct hda_verb stac92hd83xxx_core_init[] = {
/* power state controls amps */
{ 0x01, AC_VERB_SET_EAPD, 1 << 2},
{}
};
-static struct hda_verb stac92hd71bxx_core_init[] = {
+static const struct hda_verb stac92hd83xxx_hp_zephyr_init[] = {
+ { 0x22, 0x785, 0x43 },
+ { 0x22, 0x782, 0xe0 },
+ { 0x22, 0x795, 0x00 },
+ {}
+};
+
+static const struct hda_verb stac92hd71bxx_core_init[] = {
/* set master volume and direct control */
{ 0x28, AC_VERB_SET_VOLUME_KNOB_CONTROL, 0xff},
{}
};
-static struct hda_verb stac92hd71bxx_unmute_core_init[] = {
+static const struct hda_verb stac92hd71bxx_unmute_core_init[] = {
/* unmute right and left channels for nodes 0x0f, 0xa, 0x0d */
{ 0x0f, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
{ 0x0a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
@@ -887,7 +1084,7 @@ static struct hda_verb stac92hd71bxx_unmute_core_init[] = {
{}
};
-static struct hda_verb stac925x_core_init[] = {
+static const struct hda_verb stac925x_core_init[] = {
/* set dac0mux for dac converter */
{ 0x06, AC_VERB_SET_CONNECT_SEL, 0x00},
/* mute the master volume */
@@ -895,23 +1092,21 @@ static struct hda_verb stac925x_core_init[] = {
{}
};
-static struct hda_verb stac922x_core_init[] = {
- /* set master volume and direct control */
+static const struct hda_verb stac922x_core_init[] = {
+ /* set master volume and direct control */
{ 0x16, AC_VERB_SET_VOLUME_KNOB_CONTROL, 0xff},
{}
};
-static struct hda_verb d965_core_init[] = {
- /* set master volume and direct control */
- { 0x24, AC_VERB_SET_VOLUME_KNOB_CONTROL, 0xff},
+static const struct hda_verb d965_core_init[] = {
/* unmute node 0x1b */
{ 0x1b, AC_VERB_SET_AMP_GAIN_MUTE, 0xb000},
- /* select node 0x03 as DAC */
+ /* select node 0x03 as DAC */
{ 0x0b, AC_VERB_SET_CONNECT_SEL, 0x01},
{}
};
-static struct hda_verb dell_3st_core_init[] = {
+static const struct hda_verb dell_3st_core_init[] = {
/* don't set delta bit */
{0x24, AC_VERB_SET_VOLUME_KNOB_CONTROL, 0x7f},
/* unmute node 0x1b */
@@ -921,15 +1116,15 @@ static struct hda_verb dell_3st_core_init[] = {
{}
};
-static struct hda_verb stac927x_core_init[] = {
- /* set master volume and direct control */
+static const struct hda_verb stac927x_core_init[] = {
+ /* set master volume and direct control */
{ 0x24, AC_VERB_SET_VOLUME_KNOB_CONTROL, 0xff},
/* enable analog pc beep path */
{ 0x01, AC_VERB_SET_DIGI_CONVERT_2, 1 << 5},
{}
};
-static struct hda_verb stac927x_volknob_core_init[] = {
+static const struct hda_verb stac927x_volknob_core_init[] = {
/* don't set delta bit */
{0x24, AC_VERB_SET_VOLUME_KNOB_CONTROL, 0x7f},
/* enable analog pc beep path */
@@ -937,287 +1132,104 @@ static struct hda_verb stac927x_volknob_core_init[] = {
{}
};
-static struct hda_verb stac9205_core_init[] = {
- /* set master volume and direct control */
+static const struct hda_verb stac9205_core_init[] = {
+ /* set master volume and direct control */
{ 0x24, AC_VERB_SET_VOLUME_KNOB_CONTROL, 0xff},
/* enable analog pc beep path */
{ 0x01, AC_VERB_SET_DIGI_CONVERT_2, 1 << 5},
{}
};
-#define STAC_MONO_MUX \
- { \
- .iface = SNDRV_CTL_ELEM_IFACE_MIXER, \
- .name = "Mono Mux", \
- .count = 1, \
- .info = stac92xx_mono_mux_enum_info, \
- .get = stac92xx_mono_mux_enum_get, \
- .put = stac92xx_mono_mux_enum_put, \
- }
+static const struct snd_kcontrol_new stac92hd73xx_6ch_loopback =
+ STAC_ANALOG_LOOPBACK(0xFA0, 0x7A1, 3);
-#define STAC_ANALOG_LOOPBACK(verb_read, verb_write, cnt) \
- { \
- .iface = SNDRV_CTL_ELEM_IFACE_MIXER, \
- .name = "Analog Loopback", \
- .count = cnt, \
- .info = stac92xx_aloopback_info, \
- .get = stac92xx_aloopback_get, \
- .put = stac92xx_aloopback_put, \
- .private_value = verb_read | (verb_write << 16), \
- }
+static const struct snd_kcontrol_new stac92hd73xx_8ch_loopback =
+ STAC_ANALOG_LOOPBACK(0xFA0, 0x7A1, 4);
-#define DC_BIAS(xname, idx, nid) \
- { \
- .iface = SNDRV_CTL_ELEM_IFACE_MIXER, \
- .name = xname, \
- .index = idx, \
- .info = stac92xx_dc_bias_info, \
- .get = stac92xx_dc_bias_get, \
- .put = stac92xx_dc_bias_put, \
- .private_value = nid, \
- }
+static const struct snd_kcontrol_new stac92hd73xx_10ch_loopback =
+ STAC_ANALOG_LOOPBACK(0xFA0, 0x7A1, 5);
-static struct snd_kcontrol_new stac9200_mixer[] = {
- HDA_CODEC_VOLUME_MIN_MUTE("Master Playback Volume", 0xb, 0, HDA_OUTPUT),
- HDA_CODEC_MUTE("Master Playback Switch", 0xb, 0, HDA_OUTPUT),
- HDA_CODEC_VOLUME("Capture Volume", 0x0a, 0, HDA_OUTPUT),
- HDA_CODEC_MUTE("Capture Switch", 0x0a, 0, HDA_OUTPUT),
- { } /* end */
-};
+static const struct snd_kcontrol_new stac92hd71bxx_loopback =
+ STAC_ANALOG_LOOPBACK(0xFA0, 0x7A0, 2);
-static struct snd_kcontrol_new stac92hd73xx_6ch_loopback[] = {
- STAC_ANALOG_LOOPBACK(0xFA0, 0x7A1, 3),
- {}
-};
+static const struct snd_kcontrol_new stac9205_loopback =
+ STAC_ANALOG_LOOPBACK(0xFE0, 0x7E0, 1);
-static struct snd_kcontrol_new stac92hd73xx_8ch_loopback[] = {
- STAC_ANALOG_LOOPBACK(0xFA0, 0x7A1, 4),
- {}
-};
+static const struct snd_kcontrol_new stac927x_loopback =
+ STAC_ANALOG_LOOPBACK(0xFEB, 0x7EB, 1);
-static struct snd_kcontrol_new stac92hd73xx_10ch_loopback[] = {
- STAC_ANALOG_LOOPBACK(0xFA0, 0x7A1, 5),
+static const struct hda_pintbl ref9200_pin_configs[] = {
+ { 0x08, 0x01c47010 },
+ { 0x09, 0x01447010 },
+ { 0x0d, 0x0221401f },
+ { 0x0e, 0x01114010 },
+ { 0x0f, 0x02a19020 },
+ { 0x10, 0x01a19021 },
+ { 0x11, 0x90100140 },
+ { 0x12, 0x01813122 },
{}
};
-
-static struct snd_kcontrol_new stac92hd71bxx_loopback[] = {
- STAC_ANALOG_LOOPBACK(0xFA0, 0x7A0, 2)
-};
-
-static struct snd_kcontrol_new stac925x_mixer[] = {
- HDA_CODEC_VOLUME_MIN_MUTE("Master Playback Volume", 0xe, 0, HDA_OUTPUT),
- HDA_CODEC_MUTE("Master Playback Switch", 0x0e, 0, HDA_OUTPUT),
- { } /* end */
-};
-
-static struct snd_kcontrol_new stac9205_loopback[] = {
- STAC_ANALOG_LOOPBACK(0xFE0, 0x7E0, 1),
+static const struct hda_pintbl gateway9200_m4_pin_configs[] = {
+ { 0x08, 0x400000fe },
+ { 0x09, 0x404500f4 },
+ { 0x0d, 0x400100f0 },
+ { 0x0e, 0x90110010 },
+ { 0x0f, 0x400100f1 },
+ { 0x10, 0x02a1902e },
+ { 0x11, 0x500000f2 },
+ { 0x12, 0x500000f3 },
{}
};
-static struct snd_kcontrol_new stac927x_loopback[] = {
- STAC_ANALOG_LOOPBACK(0xFEB, 0x7EB, 1),
+static const struct hda_pintbl gateway9200_m4_2_pin_configs[] = {
+ { 0x08, 0x400000fe },
+ { 0x09, 0x404500f4 },
+ { 0x0d, 0x400100f0 },
+ { 0x0e, 0x90110010 },
+ { 0x0f, 0x400100f1 },
+ { 0x10, 0x02a1902e },
+ { 0x11, 0x500000f2 },
+ { 0x12, 0x500000f3 },
{}
};
-static struct snd_kcontrol_new stac_dmux_mixer = {
- .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
- .name = "Digital Input Source",
- /* count set later */
- .info = stac92xx_dmux_enum_info,
- .get = stac92xx_dmux_enum_get,
- .put = stac92xx_dmux_enum_put,
-};
-
-static struct snd_kcontrol_new stac_smux_mixer = {
- .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
- .name = "IEC958 Playback Source",
- /* count set later */
- .info = stac92xx_smux_enum_info,
- .get = stac92xx_smux_enum_get,
- .put = stac92xx_smux_enum_put,
-};
-
-static const char * const slave_vols[] = {
- "Front Playback Volume",
- "Surround Playback Volume",
- "Center Playback Volume",
- "LFE Playback Volume",
- "Side Playback Volume",
- "Headphone Playback Volume",
- "Speaker Playback Volume",
- NULL
-};
-
-static const char * const slave_sws[] = {
- "Front Playback Switch",
- "Surround Playback Switch",
- "Center Playback Switch",
- "LFE Playback Switch",
- "Side Playback Switch",
- "Headphone Playback Switch",
- "Speaker Playback Switch",
- "IEC958 Playback Switch",
- NULL
-};
-
-static void stac92xx_free_kctls(struct hda_codec *codec);
-static int stac92xx_add_jack(struct hda_codec *codec, hda_nid_t nid, int type);
-
-static int stac92xx_build_controls(struct hda_codec *codec)
-{
- struct sigmatel_spec *spec = codec->spec;
- struct auto_pin_cfg *cfg = &spec->autocfg;
- hda_nid_t nid;
- int err;
- int i;
-
- if (spec->mixer) {
- err = snd_hda_add_new_ctls(codec, spec->mixer);
- if (err < 0)
- return err;
- }
-
- for (i = 0; i < spec->num_mixers; i++) {
- err = snd_hda_add_new_ctls(codec, spec->mixers[i]);
- if (err < 0)
- return err;
- }
- if (!spec->auto_mic && spec->num_dmuxes > 0 &&
- snd_hda_get_bool_hint(codec, "separate_dmux") == 1) {
- stac_dmux_mixer.count = spec->num_dmuxes;
- err = snd_hda_ctl_add(codec, 0,
- snd_ctl_new1(&stac_dmux_mixer, codec));
- if (err < 0)
- return err;
- }
- if (spec->num_smuxes > 0) {
- int wcaps = get_wcaps(codec, spec->multiout.dig_out_nid);
- struct hda_input_mux *smux = &spec->private_smux;
- /* check for mute support on SPDIF out */
- if (wcaps & AC_WCAP_OUT_AMP) {
- snd_hda_add_imux_item(smux, "Off", 0, NULL);
- spec->spdif_mute = 1;
- }
- stac_smux_mixer.count = spec->num_smuxes;
- err = snd_hda_ctl_add(codec, 0,
- snd_ctl_new1(&stac_smux_mixer, codec));
- if (err < 0)
- return err;
- }
-
- if (spec->multiout.dig_out_nid) {
- err = snd_hda_create_spdif_out_ctls(codec, spec->multiout.dig_out_nid);
- if (err < 0)
- return err;
- err = snd_hda_create_spdif_share_sw(codec,
- &spec->multiout);
- if (err < 0)
- return err;
- spec->multiout.share_spdif = 1;
- }
- if (spec->dig_in_nid && !(spec->gpio_dir & 0x01)) {
- err = snd_hda_create_spdif_in_ctls(codec, spec->dig_in_nid);
- if (err < 0)
- return err;
- }
-
- /* if we have no master control, let's create it */
- if (!snd_hda_find_mixer_ctl(codec, "Master Playback Volume")) {
- unsigned int vmaster_tlv[4];
- snd_hda_set_vmaster_tlv(codec, spec->multiout.dac_nids[0],
- HDA_OUTPUT, vmaster_tlv);
- /* correct volume offset */
- vmaster_tlv[2] += vmaster_tlv[3] * spec->volume_offset;
- /* minimum value is actually mute */
- vmaster_tlv[3] |= TLV_DB_SCALE_MUTE;
- err = snd_hda_add_vmaster(codec, "Master Playback Volume",
- vmaster_tlv, slave_vols);
- if (err < 0)
- return err;
- }
- if (!snd_hda_find_mixer_ctl(codec, "Master Playback Switch")) {
- err = snd_hda_add_vmaster(codec, "Master Playback Switch",
- NULL, slave_sws);
- if (err < 0)
- return err;
- }
-
- if (spec->aloopback_ctl &&
- snd_hda_get_bool_hint(codec, "loopback") == 1) {
- err = snd_hda_add_new_ctls(codec, spec->aloopback_ctl);
- if (err < 0)
- return err;
- }
-
- stac92xx_free_kctls(codec); /* no longer needed */
-
- /* create jack input elements */
- if (spec->hp_detect) {
- for (i = 0; i < cfg->hp_outs; i++) {
- int type = SND_JACK_HEADPHONE;
- nid = cfg->hp_pins[i];
- /* jack detection */
- if (cfg->hp_outs == i)
- type |= SND_JACK_LINEOUT;
- err = stac92xx_add_jack(codec, nid, type);
- if (err < 0)
- return err;
- }
- }
- for (i = 0; i < cfg->line_outs; i++) {
- err = stac92xx_add_jack(codec, cfg->line_out_pins[i],
- SND_JACK_LINEOUT);
- if (err < 0)
- return err;
- }
- for (i = 0; i < cfg->num_inputs; i++) {
- nid = cfg->inputs[i].pin;
- err = stac92xx_add_jack(codec, nid, SND_JACK_MICROPHONE);
- if (err < 0)
- return err;
- }
-
- return 0;
-}
-
-static unsigned int ref9200_pin_configs[8] = {
- 0x01c47010, 0x01447010, 0x0221401f, 0x01114010,
- 0x02a19020, 0x01a19021, 0x90100140, 0x01813122,
-};
-
-static unsigned int gateway9200_m4_pin_configs[8] = {
- 0x400000fe, 0x404500f4, 0x400100f0, 0x90110010,
- 0x400100f1, 0x02a1902e, 0x500000f2, 0x500000f3,
-};
-static unsigned int gateway9200_m4_2_pin_configs[8] = {
- 0x400000fe, 0x404500f4, 0x400100f0, 0x90110010,
- 0x400100f1, 0x02a1902e, 0x500000f2, 0x500000f3,
-};
-
/*
STAC 9200 pin configs for
102801A8
102801DE
102801E8
*/
-static unsigned int dell9200_d21_pin_configs[8] = {
- 0x400001f0, 0x400001f1, 0x02214030, 0x01014010,
- 0x02a19020, 0x01a19021, 0x90100140, 0x01813122,
+static const struct hda_pintbl dell9200_d21_pin_configs[] = {
+ { 0x08, 0x400001f0 },
+ { 0x09, 0x400001f1 },
+ { 0x0d, 0x02214030 },
+ { 0x0e, 0x01014010 },
+ { 0x0f, 0x02a19020 },
+ { 0x10, 0x01a19021 },
+ { 0x11, 0x90100140 },
+ { 0x12, 0x01813122 },
+ {}
};
-/*
+/*
STAC 9200 pin configs for
102801C0
102801C1
*/
-static unsigned int dell9200_d22_pin_configs[8] = {
- 0x400001f0, 0x400001f1, 0x0221401f, 0x01014010,
- 0x01813020, 0x02a19021, 0x90100140, 0x400001f2,
+static const struct hda_pintbl dell9200_d22_pin_configs[] = {
+ { 0x08, 0x400001f0 },
+ { 0x09, 0x400001f1 },
+ { 0x0d, 0x0221401f },
+ { 0x0e, 0x01014010 },
+ { 0x0f, 0x01813020 },
+ { 0x10, 0x02a19021 },
+ { 0x11, 0x90100140 },
+ { 0x12, 0x400001f2 },
+ {}
};
-/*
+/*
STAC 9200 pin configs for
102801C4 (Dell Dimension E310)
102801C5
@@ -1226,9 +1238,16 @@ static unsigned int dell9200_d22_pin_configs[8] = {
102801DA
102801E3
*/
-static unsigned int dell9200_d23_pin_configs[8] = {
- 0x400001f0, 0x400001f1, 0x0221401f, 0x01014010,
- 0x01813020, 0x01a19021, 0x90100140, 0x400001f2,
+static const struct hda_pintbl dell9200_d23_pin_configs[] = {
+ { 0x08, 0x400001f0 },
+ { 0x09, 0x400001f1 },
+ { 0x0d, 0x0221401f },
+ { 0x0e, 0x01014010 },
+ { 0x0f, 0x01813020 },
+ { 0x10, 0x01a19021 },
+ { 0x11, 0x90100140 },
+ { 0x12, 0x400001f2 },
+ {}
};
@@ -1237,9 +1256,16 @@ static unsigned int dell9200_d23_pin_configs[8] = {
102801B5 (Dell Inspiron 630m)
102801D8 (Dell Inspiron 640m)
*/
-static unsigned int dell9200_m21_pin_configs[8] = {
- 0x40c003fa, 0x03441340, 0x0321121f, 0x90170310,
- 0x408003fb, 0x03a11020, 0x401003fc, 0x403003fd,
+static const struct hda_pintbl dell9200_m21_pin_configs[] = {
+ { 0x08, 0x40c003fa },
+ { 0x09, 0x03441340 },
+ { 0x0d, 0x0321121f },
+ { 0x0e, 0x90170310 },
+ { 0x0f, 0x408003fb },
+ { 0x10, 0x03a11020 },
+ { 0x11, 0x401003fc },
+ { 0x12, 0x403003fd },
+ {}
};
/*
@@ -1250,9 +1276,16 @@ static unsigned int dell9200_m21_pin_configs[8] = {
102801D4
102801D6
*/
-static unsigned int dell9200_m22_pin_configs[8] = {
- 0x40c003fa, 0x0144131f, 0x0321121f, 0x90170310,
- 0x90a70321, 0x03a11020, 0x401003fb, 0x40f000fc,
+static const struct hda_pintbl dell9200_m22_pin_configs[] = {
+ { 0x08, 0x40c003fa },
+ { 0x09, 0x0144131f },
+ { 0x0d, 0x0321121f },
+ { 0x0e, 0x90170310 },
+ { 0x0f, 0x90a70321 },
+ { 0x10, 0x03a11020 },
+ { 0x11, 0x401003fb },
+ { 0x12, 0x40f000fc },
+ {}
};
/*
@@ -1260,9 +1293,16 @@ static unsigned int dell9200_m22_pin_configs[8] = {
102801CE (Dell XPS M1710)
102801CF (Dell Precision M90)
*/
-static unsigned int dell9200_m23_pin_configs[8] = {
- 0x40c003fa, 0x01441340, 0x0421421f, 0x90170310,
- 0x408003fb, 0x04a1102e, 0x90170311, 0x403003fc,
+static const struct hda_pintbl dell9200_m23_pin_configs[] = {
+ { 0x08, 0x40c003fa },
+ { 0x09, 0x01441340 },
+ { 0x0d, 0x0421421f },
+ { 0x0e, 0x90170310 },
+ { 0x0f, 0x408003fb },
+ { 0x10, 0x04a1102e },
+ { 0x11, 0x90170311 },
+ { 0x12, 0x403003fc },
+ {}
};
/*
@@ -1272,9 +1312,16 @@ static unsigned int dell9200_m23_pin_configs[8] = {
102801CB (Dell Latitude 120L)
102801D3
*/
-static unsigned int dell9200_m24_pin_configs[8] = {
- 0x40c003fa, 0x404003fb, 0x0321121f, 0x90170310,
- 0x408003fc, 0x03a11020, 0x401003fd, 0x403003fe,
+static const struct hda_pintbl dell9200_m24_pin_configs[] = {
+ { 0x08, 0x40c003fa },
+ { 0x09, 0x404003fb },
+ { 0x0d, 0x0321121f },
+ { 0x0e, 0x90170310 },
+ { 0x0f, 0x408003fc },
+ { 0x10, 0x03a11020 },
+ { 0x11, 0x401003fd },
+ { 0x12, 0x403003fe },
+ {}
};
/*
@@ -1283,9 +1330,16 @@ static unsigned int dell9200_m24_pin_configs[8] = {
102801EE
102801EF
*/
-static unsigned int dell9200_m25_pin_configs[8] = {
- 0x40c003fa, 0x01441340, 0x0421121f, 0x90170310,
- 0x408003fb, 0x04a11020, 0x401003fc, 0x403003fd,
+static const struct hda_pintbl dell9200_m25_pin_configs[] = {
+ { 0x08, 0x40c003fa },
+ { 0x09, 0x01441340 },
+ { 0x0d, 0x0421121f },
+ { 0x0e, 0x90170310 },
+ { 0x0f, 0x408003fb },
+ { 0x10, 0x04a11020 },
+ { 0x11, 0x401003fc },
+ { 0x12, 0x403003fd },
+ {}
};
/*
@@ -1293,64 +1347,159 @@ static unsigned int dell9200_m25_pin_configs[8] = {
102801F5 (Dell Inspiron 1501)
102801F6
*/
-static unsigned int dell9200_m26_pin_configs[8] = {
- 0x40c003fa, 0x404003fb, 0x0421121f, 0x90170310,
- 0x408003fc, 0x04a11020, 0x401003fd, 0x403003fe,
+static const struct hda_pintbl dell9200_m26_pin_configs[] = {
+ { 0x08, 0x40c003fa },
+ { 0x09, 0x404003fb },
+ { 0x0d, 0x0421121f },
+ { 0x0e, 0x90170310 },
+ { 0x0f, 0x408003fc },
+ { 0x10, 0x04a11020 },
+ { 0x11, 0x401003fd },
+ { 0x12, 0x403003fe },
+ {}
};
/*
STAC 9200-32
102801CD (Dell Inspiron E1705/9400)
*/
-static unsigned int dell9200_m27_pin_configs[8] = {
- 0x40c003fa, 0x01441340, 0x0421121f, 0x90170310,
- 0x90170310, 0x04a11020, 0x90170310, 0x40f003fc,
-};
-
-static unsigned int oqo9200_pin_configs[8] = {
- 0x40c000f0, 0x404000f1, 0x0221121f, 0x02211210,
- 0x90170111, 0x90a70120, 0x400000f2, 0x400000f3,
-};
-
-
-static unsigned int *stac9200_brd_tbl[STAC_9200_MODELS] = {
- [STAC_REF] = ref9200_pin_configs,
- [STAC_9200_OQO] = oqo9200_pin_configs,
- [STAC_9200_DELL_D21] = dell9200_d21_pin_configs,
- [STAC_9200_DELL_D22] = dell9200_d22_pin_configs,
- [STAC_9200_DELL_D23] = dell9200_d23_pin_configs,
- [STAC_9200_DELL_M21] = dell9200_m21_pin_configs,
- [STAC_9200_DELL_M22] = dell9200_m22_pin_configs,
- [STAC_9200_DELL_M23] = dell9200_m23_pin_configs,
- [STAC_9200_DELL_M24] = dell9200_m24_pin_configs,
- [STAC_9200_DELL_M25] = dell9200_m25_pin_configs,
- [STAC_9200_DELL_M26] = dell9200_m26_pin_configs,
- [STAC_9200_DELL_M27] = dell9200_m27_pin_configs,
- [STAC_9200_M4] = gateway9200_m4_pin_configs,
- [STAC_9200_M4_2] = gateway9200_m4_2_pin_configs,
- [STAC_9200_PANASONIC] = ref9200_pin_configs,
-};
-
-static const char * const stac9200_models[STAC_9200_MODELS] = {
- [STAC_AUTO] = "auto",
- [STAC_REF] = "ref",
- [STAC_9200_OQO] = "oqo",
- [STAC_9200_DELL_D21] = "dell-d21",
- [STAC_9200_DELL_D22] = "dell-d22",
- [STAC_9200_DELL_D23] = "dell-d23",
- [STAC_9200_DELL_M21] = "dell-m21",
- [STAC_9200_DELL_M22] = "dell-m22",
- [STAC_9200_DELL_M23] = "dell-m23",
- [STAC_9200_DELL_M24] = "dell-m24",
- [STAC_9200_DELL_M25] = "dell-m25",
- [STAC_9200_DELL_M26] = "dell-m26",
- [STAC_9200_DELL_M27] = "dell-m27",
- [STAC_9200_M4] = "gateway-m4",
- [STAC_9200_M4_2] = "gateway-m4-2",
- [STAC_9200_PANASONIC] = "panasonic",
-};
-
-static struct snd_pci_quirk stac9200_cfg_tbl[] = {
+static const struct hda_pintbl dell9200_m27_pin_configs[] = {
+ { 0x08, 0x40c003fa },
+ { 0x09, 0x01441340 },
+ { 0x0d, 0x0421121f },
+ { 0x0e, 0x90170310 },
+ { 0x0f, 0x90170310 },
+ { 0x10, 0x04a11020 },
+ { 0x11, 0x90170310 },
+ { 0x12, 0x40f003fc },
+ {}
+};
+
+static const struct hda_pintbl oqo9200_pin_configs[] = {
+ { 0x08, 0x40c000f0 },
+ { 0x09, 0x404000f1 },
+ { 0x0d, 0x0221121f },
+ { 0x0e, 0x02211210 },
+ { 0x0f, 0x90170111 },
+ { 0x10, 0x90a70120 },
+ { 0x11, 0x400000f2 },
+ { 0x12, 0x400000f3 },
+ {}
+};
+
+
+static void stac9200_fixup_panasonic(struct hda_codec *codec,
+ const struct hda_fixup *fix, int action)
+{
+ struct sigmatel_spec *spec = codec->spec;
+
+ if (action == HDA_FIXUP_ACT_PRE_PROBE) {
+ spec->gpio_mask = spec->gpio_dir = 0x09;
+ spec->gpio_data = 0x00;
+ /* CF-74 has no headphone detection, and the driver should *NOT*
+ * do detection and HP/speaker toggle because the hardware does it.
+ */
+ spec->gen.suppress_auto_mute = 1;
+ }
+}
+
+
+static const struct hda_fixup stac9200_fixups[] = {
+ [STAC_REF] = {
+ .type = HDA_FIXUP_PINS,
+ .v.pins = ref9200_pin_configs,
+ },
+ [STAC_9200_OQO] = {
+ .type = HDA_FIXUP_PINS,
+ .v.pins = oqo9200_pin_configs,
+ .chained = true,
+ .chain_id = STAC_9200_EAPD_INIT,
+ },
+ [STAC_9200_DELL_D21] = {
+ .type = HDA_FIXUP_PINS,
+ .v.pins = dell9200_d21_pin_configs,
+ },
+ [STAC_9200_DELL_D22] = {
+ .type = HDA_FIXUP_PINS,
+ .v.pins = dell9200_d22_pin_configs,
+ },
+ [STAC_9200_DELL_D23] = {
+ .type = HDA_FIXUP_PINS,
+ .v.pins = dell9200_d23_pin_configs,
+ },
+ [STAC_9200_DELL_M21] = {
+ .type = HDA_FIXUP_PINS,
+ .v.pins = dell9200_m21_pin_configs,
+ },
+ [STAC_9200_DELL_M22] = {
+ .type = HDA_FIXUP_PINS,
+ .v.pins = dell9200_m22_pin_configs,
+ },
+ [STAC_9200_DELL_M23] = {
+ .type = HDA_FIXUP_PINS,
+ .v.pins = dell9200_m23_pin_configs,
+ },
+ [STAC_9200_DELL_M24] = {
+ .type = HDA_FIXUP_PINS,
+ .v.pins = dell9200_m24_pin_configs,
+ },
+ [STAC_9200_DELL_M25] = {
+ .type = HDA_FIXUP_PINS,
+ .v.pins = dell9200_m25_pin_configs,
+ },
+ [STAC_9200_DELL_M26] = {
+ .type = HDA_FIXUP_PINS,
+ .v.pins = dell9200_m26_pin_configs,
+ },
+ [STAC_9200_DELL_M27] = {
+ .type = HDA_FIXUP_PINS,
+ .v.pins = dell9200_m27_pin_configs,
+ },
+ [STAC_9200_M4] = {
+ .type = HDA_FIXUP_PINS,
+ .v.pins = gateway9200_m4_pin_configs,
+ .chained = true,
+ .chain_id = STAC_9200_EAPD_INIT,
+ },
+ [STAC_9200_M4_2] = {
+ .type = HDA_FIXUP_PINS,
+ .v.pins = gateway9200_m4_2_pin_configs,
+ .chained = true,
+ .chain_id = STAC_9200_EAPD_INIT,
+ },
+ [STAC_9200_PANASONIC] = {
+ .type = HDA_FIXUP_FUNC,
+ .v.func = stac9200_fixup_panasonic,
+ },
+ [STAC_9200_EAPD_INIT] = {
+ .type = HDA_FIXUP_VERBS,
+ .v.verbs = (const struct hda_verb[]) {
+ {0x08, AC_VERB_SET_EAPD_BTLENABLE, 0x02},
+ {}
+ },
+ },
+};
+
+static const struct hda_model_fixup stac9200_models[] = {
+ { .id = STAC_REF, .name = "ref" },
+ { .id = STAC_9200_OQO, .name = "oqo" },
+ { .id = STAC_9200_DELL_D21, .name = "dell-d21" },
+ { .id = STAC_9200_DELL_D22, .name = "dell-d22" },
+ { .id = STAC_9200_DELL_D23, .name = "dell-d23" },
+ { .id = STAC_9200_DELL_M21, .name = "dell-m21" },
+ { .id = STAC_9200_DELL_M22, .name = "dell-m22" },
+ { .id = STAC_9200_DELL_M23, .name = "dell-m23" },
+ { .id = STAC_9200_DELL_M24, .name = "dell-m24" },
+ { .id = STAC_9200_DELL_M25, .name = "dell-m25" },
+ { .id = STAC_9200_DELL_M26, .name = "dell-m26" },
+ { .id = STAC_9200_DELL_M27, .name = "dell-m27" },
+ { .id = STAC_9200_M4, .name = "gateway-m4" },
+ { .id = STAC_9200_M4_2, .name = "gateway-m4-2" },
+ { .id = STAC_9200_PANASONIC, .name = "panasonic" },
+ {}
+};
+
+static const struct snd_pci_quirk stac9200_fixup_tbl[] = {
/* SigmaTel reference board */
SND_PCI_QUIRK(PCI_VENDOR_ID_INTEL, 0x2668,
"DFI LanParty", STAC_REF),
@@ -1426,70 +1575,159 @@ static struct snd_pci_quirk stac9200_cfg_tbl[] = {
{} /* terminator */
};
-static unsigned int ref925x_pin_configs[8] = {
- 0x40c003f0, 0x424503f2, 0x01813022, 0x02a19021,
- 0x90a70320, 0x02214210, 0x01019020, 0x9033032e,
+static const struct hda_pintbl ref925x_pin_configs[] = {
+ { 0x07, 0x40c003f0 },
+ { 0x08, 0x424503f2 },
+ { 0x0a, 0x01813022 },
+ { 0x0b, 0x02a19021 },
+ { 0x0c, 0x90a70320 },
+ { 0x0d, 0x02214210 },
+ { 0x10, 0x01019020 },
+ { 0x11, 0x9033032e },
+ {}
};
-static unsigned int stac925xM1_pin_configs[8] = {
- 0x40c003f4, 0x424503f2, 0x400000f3, 0x02a19020,
- 0x40a000f0, 0x90100210, 0x400003f1, 0x9033032e,
+static const struct hda_pintbl stac925xM1_pin_configs[] = {
+ { 0x07, 0x40c003f4 },
+ { 0x08, 0x424503f2 },
+ { 0x0a, 0x400000f3 },
+ { 0x0b, 0x02a19020 },
+ { 0x0c, 0x40a000f0 },
+ { 0x0d, 0x90100210 },
+ { 0x10, 0x400003f1 },
+ { 0x11, 0x9033032e },
+ {}
};
-static unsigned int stac925xM1_2_pin_configs[8] = {
- 0x40c003f4, 0x424503f2, 0x400000f3, 0x02a19020,
- 0x40a000f0, 0x90100210, 0x400003f1, 0x9033032e,
+static const struct hda_pintbl stac925xM1_2_pin_configs[] = {
+ { 0x07, 0x40c003f4 },
+ { 0x08, 0x424503f2 },
+ { 0x0a, 0x400000f3 },
+ { 0x0b, 0x02a19020 },
+ { 0x0c, 0x40a000f0 },
+ { 0x0d, 0x90100210 },
+ { 0x10, 0x400003f1 },
+ { 0x11, 0x9033032e },
+ {}
};
-static unsigned int stac925xM2_pin_configs[8] = {
- 0x40c003f4, 0x424503f2, 0x400000f3, 0x02a19020,
- 0x40a000f0, 0x90100210, 0x400003f1, 0x9033032e,
+static const struct hda_pintbl stac925xM2_pin_configs[] = {
+ { 0x07, 0x40c003f4 },
+ { 0x08, 0x424503f2 },
+ { 0x0a, 0x400000f3 },
+ { 0x0b, 0x02a19020 },
+ { 0x0c, 0x40a000f0 },
+ { 0x0d, 0x90100210 },
+ { 0x10, 0x400003f1 },
+ { 0x11, 0x9033032e },
+ {}
};
-static unsigned int stac925xM2_2_pin_configs[8] = {
- 0x40c003f4, 0x424503f2, 0x400000f3, 0x02a19020,
- 0x40a000f0, 0x90100210, 0x400003f1, 0x9033032e,
+static const struct hda_pintbl stac925xM2_2_pin_configs[] = {
+ { 0x07, 0x40c003f4 },
+ { 0x08, 0x424503f2 },
+ { 0x0a, 0x400000f3 },
+ { 0x0b, 0x02a19020 },
+ { 0x0c, 0x40a000f0 },
+ { 0x0d, 0x90100210 },
+ { 0x10, 0x400003f1 },
+ { 0x11, 0x9033032e },
+ {}
};
-static unsigned int stac925xM3_pin_configs[8] = {
- 0x40c003f4, 0x424503f2, 0x400000f3, 0x02a19020,
- 0x40a000f0, 0x90100210, 0x400003f1, 0x503303f3,
+static const struct hda_pintbl stac925xM3_pin_configs[] = {
+ { 0x07, 0x40c003f4 },
+ { 0x08, 0x424503f2 },
+ { 0x0a, 0x400000f3 },
+ { 0x0b, 0x02a19020 },
+ { 0x0c, 0x40a000f0 },
+ { 0x0d, 0x90100210 },
+ { 0x10, 0x400003f1 },
+ { 0x11, 0x503303f3 },
+ {}
};
-static unsigned int stac925xM5_pin_configs[8] = {
- 0x40c003f4, 0x424503f2, 0x400000f3, 0x02a19020,
- 0x40a000f0, 0x90100210, 0x400003f1, 0x9033032e,
+static const struct hda_pintbl stac925xM5_pin_configs[] = {
+ { 0x07, 0x40c003f4 },
+ { 0x08, 0x424503f2 },
+ { 0x0a, 0x400000f3 },
+ { 0x0b, 0x02a19020 },
+ { 0x0c, 0x40a000f0 },
+ { 0x0d, 0x90100210 },
+ { 0x10, 0x400003f1 },
+ { 0x11, 0x9033032e },
+ {}
};
-static unsigned int stac925xM6_pin_configs[8] = {
- 0x40c003f4, 0x424503f2, 0x400000f3, 0x02a19020,
- 0x40a000f0, 0x90100210, 0x400003f1, 0x90330320,
+static const struct hda_pintbl stac925xM6_pin_configs[] = {
+ { 0x07, 0x40c003f4 },
+ { 0x08, 0x424503f2 },
+ { 0x0a, 0x400000f3 },
+ { 0x0b, 0x02a19020 },
+ { 0x0c, 0x40a000f0 },
+ { 0x0d, 0x90100210 },
+ { 0x10, 0x400003f1 },
+ { 0x11, 0x90330320 },
+ {}
};
-static unsigned int *stac925x_brd_tbl[STAC_925x_MODELS] = {
- [STAC_REF] = ref925x_pin_configs,
- [STAC_M1] = stac925xM1_pin_configs,
- [STAC_M1_2] = stac925xM1_2_pin_configs,
- [STAC_M2] = stac925xM2_pin_configs,
- [STAC_M2_2] = stac925xM2_2_pin_configs,
- [STAC_M3] = stac925xM3_pin_configs,
- [STAC_M5] = stac925xM5_pin_configs,
- [STAC_M6] = stac925xM6_pin_configs,
+static const struct hda_fixup stac925x_fixups[] = {
+ [STAC_REF] = {
+ .type = HDA_FIXUP_PINS,
+ .v.pins = ref925x_pin_configs,
+ },
+ [STAC_M1] = {
+ .type = HDA_FIXUP_PINS,
+ .v.pins = stac925xM1_pin_configs,
+ },
+ [STAC_M1_2] = {
+ .type = HDA_FIXUP_PINS,
+ .v.pins = stac925xM1_2_pin_configs,
+ },
+ [STAC_M2] = {
+ .type = HDA_FIXUP_PINS,
+ .v.pins = stac925xM2_pin_configs,
+ },
+ [STAC_M2_2] = {
+ .type = HDA_FIXUP_PINS,
+ .v.pins = stac925xM2_2_pin_configs,
+ },
+ [STAC_M3] = {
+ .type = HDA_FIXUP_PINS,
+ .v.pins = stac925xM3_pin_configs,
+ },
+ [STAC_M5] = {
+ .type = HDA_FIXUP_PINS,
+ .v.pins = stac925xM5_pin_configs,
+ },
+ [STAC_M6] = {
+ .type = HDA_FIXUP_PINS,
+ .v.pins = stac925xM6_pin_configs,
+ },
};
-static const char * const stac925x_models[STAC_925x_MODELS] = {
- [STAC_925x_AUTO] = "auto",
- [STAC_REF] = "ref",
- [STAC_M1] = "m1",
- [STAC_M1_2] = "m1-2",
- [STAC_M2] = "m2",
- [STAC_M2_2] = "m2-2",
- [STAC_M3] = "m3",
- [STAC_M5] = "m5",
- [STAC_M6] = "m6",
+static const struct hda_model_fixup stac925x_models[] = {
+ { .id = STAC_REF, .name = "ref" },
+ { .id = STAC_M1, .name = "m1" },
+ { .id = STAC_M1_2, .name = "m1-2" },
+ { .id = STAC_M2, .name = "m2" },
+ { .id = STAC_M2_2, .name = "m2-2" },
+ { .id = STAC_M3, .name = "m3" },
+ { .id = STAC_M5, .name = "m5" },
+ { .id = STAC_M6, .name = "m6" },
+ {}
};
-static struct snd_pci_quirk stac925x_codec_id_cfg_tbl[] = {
+static const struct snd_pci_quirk stac925x_fixup_tbl[] = {
+ /* SigmaTel reference board */
+ SND_PCI_QUIRK(PCI_VENDOR_ID_INTEL, 0x2668, "DFI LanParty", STAC_REF),
+ SND_PCI_QUIRK(PCI_VENDOR_ID_DFI, 0x3101, "DFI LanParty", STAC_REF),
+ SND_PCI_QUIRK(0x8384, 0x7632, "Stac9202 Reference Board", STAC_REF),
+
+ /* Default table for unknown ID */
+ SND_PCI_QUIRK(0x1002, 0x437b, "Gateway mobile", STAC_M2_2),
+
+ /* gateway machines are checked via codec ssid */
SND_PCI_QUIRK(0x107b, 0x0316, "Gateway M255", STAC_M2),
SND_PCI_QUIRK(0x107b, 0x0366, "Gateway MP6954", STAC_M5),
SND_PCI_QUIRK(0x107b, 0x0461, "Gateway NX560XL", STAC_M1),
@@ -1503,67 +1741,212 @@ static struct snd_pci_quirk stac925x_codec_id_cfg_tbl[] = {
{} /* terminator */
};
-static struct snd_pci_quirk stac925x_cfg_tbl[] = {
- /* SigmaTel reference board */
- SND_PCI_QUIRK(PCI_VENDOR_ID_INTEL, 0x2668, "DFI LanParty", STAC_REF),
- SND_PCI_QUIRK(PCI_VENDOR_ID_DFI, 0x3101, "DFI LanParty", STAC_REF),
- SND_PCI_QUIRK(0x8384, 0x7632, "Stac9202 Reference Board", STAC_REF),
-
- /* Default table for unknown ID */
- SND_PCI_QUIRK(0x1002, 0x437b, "Gateway mobile", STAC_M2_2),
-
- {} /* terminator */
+static const struct hda_pintbl ref92hd73xx_pin_configs[] = {
+ { 0x0a, 0x02214030 },
+ { 0x0b, 0x02a19040 },
+ { 0x0c, 0x01a19020 },
+ { 0x0d, 0x02214030 },
+ { 0x0e, 0x0181302e },
+ { 0x0f, 0x01014010 },
+ { 0x10, 0x01014020 },
+ { 0x11, 0x01014030 },
+ { 0x12, 0x02319040 },
+ { 0x13, 0x90a000f0 },
+ { 0x14, 0x90a000f0 },
+ { 0x22, 0x01452050 },
+ { 0x23, 0x01452050 },
+ {}
};
-static unsigned int ref92hd73xx_pin_configs[13] = {
- 0x02214030, 0x02a19040, 0x01a19020, 0x02214030,
- 0x0181302e, 0x01014010, 0x01014020, 0x01014030,
- 0x02319040, 0x90a000f0, 0x90a000f0, 0x01452050,
- 0x01452050,
+static const struct hda_pintbl dell_m6_pin_configs[] = {
+ { 0x0a, 0x0321101f },
+ { 0x0b, 0x4f00000f },
+ { 0x0c, 0x4f0000f0 },
+ { 0x0d, 0x90170110 },
+ { 0x0e, 0x03a11020 },
+ { 0x0f, 0x0321101f },
+ { 0x10, 0x4f0000f0 },
+ { 0x11, 0x4f0000f0 },
+ { 0x12, 0x4f0000f0 },
+ { 0x13, 0x90a60160 },
+ { 0x14, 0x4f0000f0 },
+ { 0x22, 0x4f0000f0 },
+ { 0x23, 0x4f0000f0 },
+ {}
};
-static unsigned int dell_m6_pin_configs[13] = {
- 0x0321101f, 0x4f00000f, 0x4f0000f0, 0x90170110,
- 0x03a11020, 0x0321101f, 0x4f0000f0, 0x4f0000f0,
- 0x4f0000f0, 0x90a60160, 0x4f0000f0, 0x4f0000f0,
- 0x4f0000f0,
+static const struct hda_pintbl alienware_m17x_pin_configs[] = {
+ { 0x0a, 0x0321101f },
+ { 0x0b, 0x0321101f },
+ { 0x0c, 0x03a11020 },
+ { 0x0d, 0x03014020 },
+ { 0x0e, 0x90170110 },
+ { 0x0f, 0x4f0000f0 },
+ { 0x10, 0x4f0000f0 },
+ { 0x11, 0x4f0000f0 },
+ { 0x12, 0x4f0000f0 },
+ { 0x13, 0x90a60160 },
+ { 0x14, 0x4f0000f0 },
+ { 0x22, 0x4f0000f0 },
+ { 0x23, 0x904601b0 },
+ {}
};
-static unsigned int alienware_m17x_pin_configs[13] = {
- 0x0321101f, 0x0321101f, 0x03a11020, 0x03014020,
- 0x90170110, 0x4f0000f0, 0x4f0000f0, 0x4f0000f0,
- 0x4f0000f0, 0x90a60160, 0x4f0000f0, 0x4f0000f0,
- 0x904601b0,
+static const struct hda_pintbl intel_dg45id_pin_configs[] = {
+ { 0x0a, 0x02214230 },
+ { 0x0b, 0x02A19240 },
+ { 0x0c, 0x01013214 },
+ { 0x0d, 0x01014210 },
+ { 0x0e, 0x01A19250 },
+ { 0x0f, 0x01011212 },
+ { 0x10, 0x01016211 },
+ {}
};
-static unsigned int intel_dg45id_pin_configs[13] = {
- 0x02214230, 0x02A19240, 0x01013214, 0x01014210,
- 0x01A19250, 0x01011212, 0x01016211
+static const struct hda_pintbl stac92hd89xx_hp_front_jack_pin_configs[] = {
+ { 0x0a, 0x02214030 },
+ { 0x0b, 0x02A19010 },
+ {}
};
-static unsigned int *stac92hd73xx_brd_tbl[STAC_92HD73XX_MODELS] = {
- [STAC_92HD73XX_REF] = ref92hd73xx_pin_configs,
- [STAC_DELL_M6_AMIC] = dell_m6_pin_configs,
- [STAC_DELL_M6_DMIC] = dell_m6_pin_configs,
- [STAC_DELL_M6_BOTH] = dell_m6_pin_configs,
- [STAC_DELL_EQ] = dell_m6_pin_configs,
- [STAC_ALIENWARE_M17X] = alienware_m17x_pin_configs,
- [STAC_92HD73XX_INTEL] = intel_dg45id_pin_configs,
+static void stac92hd73xx_fixup_ref(struct hda_codec *codec,
+ const struct hda_fixup *fix, int action)
+{
+ struct sigmatel_spec *spec = codec->spec;
+
+ if (action != HDA_FIXUP_ACT_PRE_PROBE)
+ return;
+
+ snd_hda_apply_pincfgs(codec, ref92hd73xx_pin_configs);
+ spec->gpio_mask = spec->gpio_dir = spec->gpio_data = 0;
+}
+
+static void stac92hd73xx_fixup_dell(struct hda_codec *codec)
+{
+ struct sigmatel_spec *spec = codec->spec;
+
+ snd_hda_apply_pincfgs(codec, dell_m6_pin_configs);
+ spec->eapd_switch = 0;
+}
+
+static void stac92hd73xx_fixup_dell_eq(struct hda_codec *codec,
+ const struct hda_fixup *fix, int action)
+{
+ struct sigmatel_spec *spec = codec->spec;
+
+ if (action != HDA_FIXUP_ACT_PRE_PROBE)
+ return;
+
+ stac92hd73xx_fixup_dell(codec);
+ snd_hda_add_verbs(codec, dell_eq_core_init);
+ spec->volknob_init = 1;
+}
+
+/* Analog Mics */
+static void stac92hd73xx_fixup_dell_m6_amic(struct hda_codec *codec,
+ const struct hda_fixup *fix, int action)
+{
+ if (action != HDA_FIXUP_ACT_PRE_PROBE)
+ return;
+
+ stac92hd73xx_fixup_dell(codec);
+ snd_hda_codec_set_pincfg(codec, 0x0b, 0x90A70170);
+}
+
+/* Digital Mics */
+static void stac92hd73xx_fixup_dell_m6_dmic(struct hda_codec *codec,
+ const struct hda_fixup *fix, int action)
+{
+ if (action != HDA_FIXUP_ACT_PRE_PROBE)
+ return;
+
+ stac92hd73xx_fixup_dell(codec);
+ snd_hda_codec_set_pincfg(codec, 0x13, 0x90A60160);
+}
+
+/* Both */
+static void stac92hd73xx_fixup_dell_m6_both(struct hda_codec *codec,
+ const struct hda_fixup *fix, int action)
+{
+ if (action != HDA_FIXUP_ACT_PRE_PROBE)
+ return;
+
+ stac92hd73xx_fixup_dell(codec);
+ snd_hda_codec_set_pincfg(codec, 0x0b, 0x90A70170);
+ snd_hda_codec_set_pincfg(codec, 0x13, 0x90A60160);
+}
+
+static void stac92hd73xx_fixup_alienware_m17x(struct hda_codec *codec,
+ const struct hda_fixup *fix, int action)
+{
+ struct sigmatel_spec *spec = codec->spec;
+
+ if (action != HDA_FIXUP_ACT_PRE_PROBE)
+ return;
+
+ snd_hda_apply_pincfgs(codec, alienware_m17x_pin_configs);
+ spec->eapd_switch = 0;
+}
+
+static void stac92hd73xx_fixup_no_jd(struct hda_codec *codec,
+ const struct hda_fixup *fix, int action)
+{
+ if (action == HDA_FIXUP_ACT_PRE_PROBE)
+ codec->no_jack_detect = 1;
+}
+
+static const struct hda_fixup stac92hd73xx_fixups[] = {
+ [STAC_92HD73XX_REF] = {
+ .type = HDA_FIXUP_FUNC,
+ .v.func = stac92hd73xx_fixup_ref,
+ },
+ [STAC_DELL_M6_AMIC] = {
+ .type = HDA_FIXUP_FUNC,
+ .v.func = stac92hd73xx_fixup_dell_m6_amic,
+ },
+ [STAC_DELL_M6_DMIC] = {
+ .type = HDA_FIXUP_FUNC,
+ .v.func = stac92hd73xx_fixup_dell_m6_dmic,
+ },
+ [STAC_DELL_M6_BOTH] = {
+ .type = HDA_FIXUP_FUNC,
+ .v.func = stac92hd73xx_fixup_dell_m6_both,
+ },
+ [STAC_DELL_EQ] = {
+ .type = HDA_FIXUP_FUNC,
+ .v.func = stac92hd73xx_fixup_dell_eq,
+ },
+ [STAC_ALIENWARE_M17X] = {
+ .type = HDA_FIXUP_FUNC,
+ .v.func = stac92hd73xx_fixup_alienware_m17x,
+ },
+ [STAC_92HD73XX_INTEL] = {
+ .type = HDA_FIXUP_PINS,
+ .v.pins = intel_dg45id_pin_configs,
+ },
+ [STAC_92HD73XX_NO_JD] = {
+ .type = HDA_FIXUP_FUNC,
+ .v.func = stac92hd73xx_fixup_no_jd,
+ },
+ [STAC_92HD89XX_HP_FRONT_JACK] = {
+ .type = HDA_FIXUP_PINS,
+ .v.pins = stac92hd89xx_hp_front_jack_pin_configs,
+ }
};
-static const char * const stac92hd73xx_models[STAC_92HD73XX_MODELS] = {
- [STAC_92HD73XX_AUTO] = "auto",
- [STAC_92HD73XX_NO_JD] = "no-jd",
- [STAC_92HD73XX_REF] = "ref",
- [STAC_92HD73XX_INTEL] = "intel",
- [STAC_DELL_M6_AMIC] = "dell-m6-amic",
- [STAC_DELL_M6_DMIC] = "dell-m6-dmic",
- [STAC_DELL_M6_BOTH] = "dell-m6",
- [STAC_DELL_EQ] = "dell-eq",
- [STAC_ALIENWARE_M17X] = "alienware",
+static const struct hda_model_fixup stac92hd73xx_models[] = {
+ { .id = STAC_92HD73XX_NO_JD, .name = "no-jd" },
+ { .id = STAC_92HD73XX_REF, .name = "ref" },
+ { .id = STAC_92HD73XX_INTEL, .name = "intel" },
+ { .id = STAC_DELL_M6_AMIC, .name = "dell-m6-amic" },
+ { .id = STAC_DELL_M6_DMIC, .name = "dell-m6-dmic" },
+ { .id = STAC_DELL_M6_BOTH, .name = "dell-m6" },
+ { .id = STAC_DELL_EQ, .name = "dell-eq" },
+ { .id = STAC_ALIENWARE_M17X, .name = "alienware" },
+ {}
};
-static struct snd_pci_quirk stac92hd73xx_cfg_tbl[] = {
+static const struct snd_pci_quirk stac92hd73xx_fixup_tbl[] = {
/* SigmaTel reference board */
SND_PCI_QUIRK(PCI_VENDOR_ID_INTEL, 0x2668,
"DFI LanParty", STAC_92HD73XX_REF),
@@ -1598,55 +1981,713 @@ static struct snd_pci_quirk stac92hd73xx_cfg_tbl[] = {
SND_PCI_QUIRK(PCI_VENDOR_ID_DELL, 0x02bd,
"Dell Studio 1557", STAC_DELL_M6_DMIC),
SND_PCI_QUIRK(PCI_VENDOR_ID_DELL, 0x02fe,
- "Dell Studio XPS 1645", STAC_DELL_M6_BOTH),
+ "Dell Studio XPS 1645", STAC_DELL_M6_DMIC),
SND_PCI_QUIRK(PCI_VENDOR_ID_DELL, 0x0413,
- "Dell Studio 1558", STAC_DELL_M6_BOTH),
- {} /* terminator */
-};
-
-static struct snd_pci_quirk stac92hd73xx_codec_id_cfg_tbl[] = {
+ "Dell Studio 1558", STAC_DELL_M6_DMIC),
+ /* codec SSID matching */
SND_PCI_QUIRK(PCI_VENDOR_ID_DELL, 0x02a1,
"Alienware M17x", STAC_ALIENWARE_M17X),
SND_PCI_QUIRK(PCI_VENDOR_ID_DELL, 0x043a,
"Alienware M17x", STAC_ALIENWARE_M17X),
+ SND_PCI_QUIRK(PCI_VENDOR_ID_DELL, 0x0490,
+ "Alienware M17x R3", STAC_DELL_EQ),
+ SND_PCI_QUIRK(PCI_VENDOR_ID_HP, 0x2b17,
+ "unknown HP", STAC_92HD89XX_HP_FRONT_JACK),
{} /* terminator */
};
-static unsigned int ref92hd83xxx_pin_configs[10] = {
- 0x02214030, 0x02211010, 0x02a19020, 0x02170130,
- 0x01014050, 0x01819040, 0x01014020, 0x90a3014e,
- 0x01451160, 0x98560170,
+static const struct hda_pintbl ref92hd83xxx_pin_configs[] = {
+ { 0x0a, 0x02214030 },
+ { 0x0b, 0x02211010 },
+ { 0x0c, 0x02a19020 },
+ { 0x0d, 0x02170130 },
+ { 0x0e, 0x01014050 },
+ { 0x0f, 0x01819040 },
+ { 0x10, 0x01014020 },
+ { 0x11, 0x90a3014e },
+ { 0x1f, 0x01451160 },
+ { 0x20, 0x98560170 },
+ {}
+};
+
+static const struct hda_pintbl dell_s14_pin_configs[] = {
+ { 0x0a, 0x0221403f },
+ { 0x0b, 0x0221101f },
+ { 0x0c, 0x02a19020 },
+ { 0x0d, 0x90170110 },
+ { 0x0e, 0x40f000f0 },
+ { 0x0f, 0x40f000f0 },
+ { 0x10, 0x40f000f0 },
+ { 0x11, 0x90a60160 },
+ { 0x1f, 0x40f000f0 },
+ { 0x20, 0x40f000f0 },
+ {}
+};
+
+static const struct hda_pintbl dell_vostro_3500_pin_configs[] = {
+ { 0x0a, 0x02a11020 },
+ { 0x0b, 0x0221101f },
+ { 0x0c, 0x400000f0 },
+ { 0x0d, 0x90170110 },
+ { 0x0e, 0x400000f1 },
+ { 0x0f, 0x400000f2 },
+ { 0x10, 0x400000f3 },
+ { 0x11, 0x90a60160 },
+ { 0x1f, 0x400000f4 },
+ { 0x20, 0x400000f5 },
+ {}
+};
+
+static const struct hda_pintbl hp_dv7_4000_pin_configs[] = {
+ { 0x0a, 0x03a12050 },
+ { 0x0b, 0x0321201f },
+ { 0x0c, 0x40f000f0 },
+ { 0x0d, 0x90170110 },
+ { 0x0e, 0x40f000f0 },
+ { 0x0f, 0x40f000f0 },
+ { 0x10, 0x90170110 },
+ { 0x11, 0xd5a30140 },
+ { 0x1f, 0x40f000f0 },
+ { 0x20, 0x40f000f0 },
+ {}
+};
+
+static const struct hda_pintbl hp_zephyr_pin_configs[] = {
+ { 0x0a, 0x01813050 },
+ { 0x0b, 0x0421201f },
+ { 0x0c, 0x04a1205e },
+ { 0x0d, 0x96130310 },
+ { 0x0e, 0x96130310 },
+ { 0x0f, 0x0101401f },
+ { 0x10, 0x1111611f },
+ { 0x11, 0xd5a30130 },
+ {}
};
-static unsigned int dell_s14_pin_configs[10] = {
- 0x0221403f, 0x0221101f, 0x02a19020, 0x90170110,
- 0x40f000f0, 0x40f000f0, 0x40f000f0, 0x90a60160,
- 0x40f000f0, 0x40f000f0,
+static const struct hda_pintbl hp_cNB11_intquad_pin_configs[] = {
+ { 0x0a, 0x40f000f0 },
+ { 0x0b, 0x0221101f },
+ { 0x0c, 0x02a11020 },
+ { 0x0d, 0x92170110 },
+ { 0x0e, 0x40f000f0 },
+ { 0x0f, 0x92170110 },
+ { 0x10, 0x40f000f0 },
+ { 0x11, 0xd5a30130 },
+ { 0x1f, 0x40f000f0 },
+ { 0x20, 0x40f000f0 },
+ {}
};
-static unsigned int hp_dv7_4000_pin_configs[10] = {
- 0x03a12050, 0x0321201f, 0x40f000f0, 0x90170110,
- 0x40f000f0, 0x40f000f0, 0x90170110, 0xd5a30140,
- 0x40f000f0, 0x40f000f0,
+static void stac92hd83xxx_fixup_hp(struct hda_codec *codec,
+ const struct hda_fixup *fix, int action)
+{
+ struct sigmatel_spec *spec = codec->spec;
+
+ if (action != HDA_FIXUP_ACT_PRE_PROBE)
+ return;
+
+ if (hp_bnb2011_with_dock(codec)) {
+ snd_hda_codec_set_pincfg(codec, 0xa, 0x2101201f);
+ snd_hda_codec_set_pincfg(codec, 0xf, 0x2181205e);
+ }
+
+ if (find_mute_led_cfg(codec, spec->default_polarity))
+ codec_dbg(codec, "mute LED gpio %d polarity %d\n",
+ spec->gpio_led,
+ spec->gpio_led_polarity);
+
+ /* allow auto-switching of dock line-in */
+ spec->gen.line_in_auto_switch = true;
+}
+
+static void stac92hd83xxx_fixup_hp_zephyr(struct hda_codec *codec,
+ const struct hda_fixup *fix, int action)
+{
+ if (action != HDA_FIXUP_ACT_PRE_PROBE)
+ return;
+
+ snd_hda_apply_pincfgs(codec, hp_zephyr_pin_configs);
+ snd_hda_add_verbs(codec, stac92hd83xxx_hp_zephyr_init);
+}
+
+static void stac92hd83xxx_fixup_hp_led(struct hda_codec *codec,
+ const struct hda_fixup *fix, int action)
+{
+ struct sigmatel_spec *spec = codec->spec;
+
+ if (action == HDA_FIXUP_ACT_PRE_PROBE)
+ spec->default_polarity = 0;
+}
+
+static void stac92hd83xxx_fixup_hp_inv_led(struct hda_codec *codec,
+ const struct hda_fixup *fix, int action)
+{
+ struct sigmatel_spec *spec = codec->spec;
+
+ if (action == HDA_FIXUP_ACT_PRE_PROBE)
+ spec->default_polarity = 1;
+}
+
+static void stac92hd83xxx_fixup_hp_mic_led(struct hda_codec *codec,
+ const struct hda_fixup *fix, int action)
+{
+ struct sigmatel_spec *spec = codec->spec;
+
+ if (action == HDA_FIXUP_ACT_PRE_PROBE) {
+ spec->mic_mute_led_gpio = 0x08; /* GPIO3 */
+ /* resetting controller clears GPIO, so we need to keep on */
+ codec->bus->power_keep_link_on = 1;
+ }
+}
+
+static void stac92hd83xxx_fixup_hp_led_gpio10(struct hda_codec *codec,
+ const struct hda_fixup *fix, int action)
+{
+ struct sigmatel_spec *spec = codec->spec;
+
+ if (action == HDA_FIXUP_ACT_PRE_PROBE) {
+ spec->gpio_led = 0x10; /* GPIO4 */
+ spec->default_polarity = 0;
+ }
+}
+
+static void stac92hd83xxx_fixup_headset_jack(struct hda_codec *codec,
+ const struct hda_fixup *fix, int action)
+{
+ struct sigmatel_spec *spec = codec->spec;
+
+ if (action == HDA_FIXUP_ACT_PRE_PROBE)
+ spec->headset_jack = 1;
+}
+
+static const struct hda_verb hp_bnb13_eq_verbs[] = {
+ /* 44.1KHz base */
+ { 0x22, 0x7A6, 0x3E },
+ { 0x22, 0x7A7, 0x68 },
+ { 0x22, 0x7A8, 0x17 },
+ { 0x22, 0x7A9, 0x3E },
+ { 0x22, 0x7AA, 0x68 },
+ { 0x22, 0x7AB, 0x17 },
+ { 0x22, 0x7AC, 0x00 },
+ { 0x22, 0x7AD, 0x80 },
+ { 0x22, 0x7A6, 0x83 },
+ { 0x22, 0x7A7, 0x2F },
+ { 0x22, 0x7A8, 0xD1 },
+ { 0x22, 0x7A9, 0x83 },
+ { 0x22, 0x7AA, 0x2F },
+ { 0x22, 0x7AB, 0xD1 },
+ { 0x22, 0x7AC, 0x01 },
+ { 0x22, 0x7AD, 0x80 },
+ { 0x22, 0x7A6, 0x3E },
+ { 0x22, 0x7A7, 0x68 },
+ { 0x22, 0x7A8, 0x17 },
+ { 0x22, 0x7A9, 0x3E },
+ { 0x22, 0x7AA, 0x68 },
+ { 0x22, 0x7AB, 0x17 },
+ { 0x22, 0x7AC, 0x02 },
+ { 0x22, 0x7AD, 0x80 },
+ { 0x22, 0x7A6, 0x7C },
+ { 0x22, 0x7A7, 0xC6 },
+ { 0x22, 0x7A8, 0x0C },
+ { 0x22, 0x7A9, 0x7C },
+ { 0x22, 0x7AA, 0xC6 },
+ { 0x22, 0x7AB, 0x0C },
+ { 0x22, 0x7AC, 0x03 },
+ { 0x22, 0x7AD, 0x80 },
+ { 0x22, 0x7A6, 0xC3 },
+ { 0x22, 0x7A7, 0x25 },
+ { 0x22, 0x7A8, 0xAF },
+ { 0x22, 0x7A9, 0xC3 },
+ { 0x22, 0x7AA, 0x25 },
+ { 0x22, 0x7AB, 0xAF },
+ { 0x22, 0x7AC, 0x04 },
+ { 0x22, 0x7AD, 0x80 },
+ { 0x22, 0x7A6, 0x3E },
+ { 0x22, 0x7A7, 0x85 },
+ { 0x22, 0x7A8, 0x73 },
+ { 0x22, 0x7A9, 0x3E },
+ { 0x22, 0x7AA, 0x85 },
+ { 0x22, 0x7AB, 0x73 },
+ { 0x22, 0x7AC, 0x05 },
+ { 0x22, 0x7AD, 0x80 },
+ { 0x22, 0x7A6, 0x85 },
+ { 0x22, 0x7A7, 0x39 },
+ { 0x22, 0x7A8, 0xC7 },
+ { 0x22, 0x7A9, 0x85 },
+ { 0x22, 0x7AA, 0x39 },
+ { 0x22, 0x7AB, 0xC7 },
+ { 0x22, 0x7AC, 0x06 },
+ { 0x22, 0x7AD, 0x80 },
+ { 0x22, 0x7A6, 0x3C },
+ { 0x22, 0x7A7, 0x90 },
+ { 0x22, 0x7A8, 0xB0 },
+ { 0x22, 0x7A9, 0x3C },
+ { 0x22, 0x7AA, 0x90 },
+ { 0x22, 0x7AB, 0xB0 },
+ { 0x22, 0x7AC, 0x07 },
+ { 0x22, 0x7AD, 0x80 },
+ { 0x22, 0x7A6, 0x7A },
+ { 0x22, 0x7A7, 0xC6 },
+ { 0x22, 0x7A8, 0x39 },
+ { 0x22, 0x7A9, 0x7A },
+ { 0x22, 0x7AA, 0xC6 },
+ { 0x22, 0x7AB, 0x39 },
+ { 0x22, 0x7AC, 0x08 },
+ { 0x22, 0x7AD, 0x80 },
+ { 0x22, 0x7A6, 0xC4 },
+ { 0x22, 0x7A7, 0xE9 },
+ { 0x22, 0x7A8, 0xDC },
+ { 0x22, 0x7A9, 0xC4 },
+ { 0x22, 0x7AA, 0xE9 },
+ { 0x22, 0x7AB, 0xDC },
+ { 0x22, 0x7AC, 0x09 },
+ { 0x22, 0x7AD, 0x80 },
+ { 0x22, 0x7A6, 0x3D },
+ { 0x22, 0x7A7, 0xE1 },
+ { 0x22, 0x7A8, 0x0D },
+ { 0x22, 0x7A9, 0x3D },
+ { 0x22, 0x7AA, 0xE1 },
+ { 0x22, 0x7AB, 0x0D },
+ { 0x22, 0x7AC, 0x0A },
+ { 0x22, 0x7AD, 0x80 },
+ { 0x22, 0x7A6, 0x89 },
+ { 0x22, 0x7A7, 0xB6 },
+ { 0x22, 0x7A8, 0xEB },
+ { 0x22, 0x7A9, 0x89 },
+ { 0x22, 0x7AA, 0xB6 },
+ { 0x22, 0x7AB, 0xEB },
+ { 0x22, 0x7AC, 0x0B },
+ { 0x22, 0x7AD, 0x80 },
+ { 0x22, 0x7A6, 0x39 },
+ { 0x22, 0x7A7, 0x9D },
+ { 0x22, 0x7A8, 0xFE },
+ { 0x22, 0x7A9, 0x39 },
+ { 0x22, 0x7AA, 0x9D },
+ { 0x22, 0x7AB, 0xFE },
+ { 0x22, 0x7AC, 0x0C },
+ { 0x22, 0x7AD, 0x80 },
+ { 0x22, 0x7A6, 0x76 },
+ { 0x22, 0x7A7, 0x49 },
+ { 0x22, 0x7A8, 0x15 },
+ { 0x22, 0x7A9, 0x76 },
+ { 0x22, 0x7AA, 0x49 },
+ { 0x22, 0x7AB, 0x15 },
+ { 0x22, 0x7AC, 0x0D },
+ { 0x22, 0x7AD, 0x80 },
+ { 0x22, 0x7A6, 0xC8 },
+ { 0x22, 0x7A7, 0x80 },
+ { 0x22, 0x7A8, 0xF5 },
+ { 0x22, 0x7A9, 0xC8 },
+ { 0x22, 0x7AA, 0x80 },
+ { 0x22, 0x7AB, 0xF5 },
+ { 0x22, 0x7AC, 0x0E },
+ { 0x22, 0x7AD, 0x80 },
+ { 0x22, 0x7A6, 0x40 },
+ { 0x22, 0x7A7, 0x00 },
+ { 0x22, 0x7A8, 0x00 },
+ { 0x22, 0x7A9, 0x40 },
+ { 0x22, 0x7AA, 0x00 },
+ { 0x22, 0x7AB, 0x00 },
+ { 0x22, 0x7AC, 0x0F },
+ { 0x22, 0x7AD, 0x80 },
+ { 0x22, 0x7A6, 0x90 },
+ { 0x22, 0x7A7, 0x68 },
+ { 0x22, 0x7A8, 0xF1 },
+ { 0x22, 0x7A9, 0x90 },
+ { 0x22, 0x7AA, 0x68 },
+ { 0x22, 0x7AB, 0xF1 },
+ { 0x22, 0x7AC, 0x10 },
+ { 0x22, 0x7AD, 0x80 },
+ { 0x22, 0x7A6, 0x34 },
+ { 0x22, 0x7A7, 0x47 },
+ { 0x22, 0x7A8, 0x6C },
+ { 0x22, 0x7A9, 0x34 },
+ { 0x22, 0x7AA, 0x47 },
+ { 0x22, 0x7AB, 0x6C },
+ { 0x22, 0x7AC, 0x11 },
+ { 0x22, 0x7AD, 0x80 },
+ { 0x22, 0x7A6, 0x6F },
+ { 0x22, 0x7A7, 0x97 },
+ { 0x22, 0x7A8, 0x0F },
+ { 0x22, 0x7A9, 0x6F },
+ { 0x22, 0x7AA, 0x97 },
+ { 0x22, 0x7AB, 0x0F },
+ { 0x22, 0x7AC, 0x12 },
+ { 0x22, 0x7AD, 0x80 },
+ { 0x22, 0x7A6, 0xCB },
+ { 0x22, 0x7A7, 0xB8 },
+ { 0x22, 0x7A8, 0x94 },
+ { 0x22, 0x7A9, 0xCB },
+ { 0x22, 0x7AA, 0xB8 },
+ { 0x22, 0x7AB, 0x94 },
+ { 0x22, 0x7AC, 0x13 },
+ { 0x22, 0x7AD, 0x80 },
+ { 0x22, 0x7A6, 0x40 },
+ { 0x22, 0x7A7, 0x00 },
+ { 0x22, 0x7A8, 0x00 },
+ { 0x22, 0x7A9, 0x40 },
+ { 0x22, 0x7AA, 0x00 },
+ { 0x22, 0x7AB, 0x00 },
+ { 0x22, 0x7AC, 0x14 },
+ { 0x22, 0x7AD, 0x80 },
+ { 0x22, 0x7A6, 0x95 },
+ { 0x22, 0x7A7, 0x76 },
+ { 0x22, 0x7A8, 0x5B },
+ { 0x22, 0x7A9, 0x95 },
+ { 0x22, 0x7AA, 0x76 },
+ { 0x22, 0x7AB, 0x5B },
+ { 0x22, 0x7AC, 0x15 },
+ { 0x22, 0x7AD, 0x80 },
+ { 0x22, 0x7A6, 0x31 },
+ { 0x22, 0x7A7, 0xAC },
+ { 0x22, 0x7A8, 0x31 },
+ { 0x22, 0x7A9, 0x31 },
+ { 0x22, 0x7AA, 0xAC },
+ { 0x22, 0x7AB, 0x31 },
+ { 0x22, 0x7AC, 0x16 },
+ { 0x22, 0x7AD, 0x80 },
+ { 0x22, 0x7A6, 0x6A },
+ { 0x22, 0x7A7, 0x89 },
+ { 0x22, 0x7A8, 0xA5 },
+ { 0x22, 0x7A9, 0x6A },
+ { 0x22, 0x7AA, 0x89 },
+ { 0x22, 0x7AB, 0xA5 },
+ { 0x22, 0x7AC, 0x17 },
+ { 0x22, 0x7AD, 0x80 },
+ { 0x22, 0x7A6, 0xCE },
+ { 0x22, 0x7A7, 0x53 },
+ { 0x22, 0x7A8, 0xCF },
+ { 0x22, 0x7A9, 0xCE },
+ { 0x22, 0x7AA, 0x53 },
+ { 0x22, 0x7AB, 0xCF },
+ { 0x22, 0x7AC, 0x18 },
+ { 0x22, 0x7AD, 0x80 },
+ { 0x22, 0x7A6, 0x40 },
+ { 0x22, 0x7A7, 0x00 },
+ { 0x22, 0x7A8, 0x00 },
+ { 0x22, 0x7A9, 0x40 },
+ { 0x22, 0x7AA, 0x00 },
+ { 0x22, 0x7AB, 0x00 },
+ { 0x22, 0x7AC, 0x19 },
+ { 0x22, 0x7AD, 0x80 },
+ /* 48KHz base */
+ { 0x22, 0x7A6, 0x3E },
+ { 0x22, 0x7A7, 0x88 },
+ { 0x22, 0x7A8, 0xDC },
+ { 0x22, 0x7A9, 0x3E },
+ { 0x22, 0x7AA, 0x88 },
+ { 0x22, 0x7AB, 0xDC },
+ { 0x22, 0x7AC, 0x1A },
+ { 0x22, 0x7AD, 0x80 },
+ { 0x22, 0x7A6, 0x82 },
+ { 0x22, 0x7A7, 0xEE },
+ { 0x22, 0x7A8, 0x46 },
+ { 0x22, 0x7A9, 0x82 },
+ { 0x22, 0x7AA, 0xEE },
+ { 0x22, 0x7AB, 0x46 },
+ { 0x22, 0x7AC, 0x1B },
+ { 0x22, 0x7AD, 0x80 },
+ { 0x22, 0x7A6, 0x3E },
+ { 0x22, 0x7A7, 0x88 },
+ { 0x22, 0x7A8, 0xDC },
+ { 0x22, 0x7A9, 0x3E },
+ { 0x22, 0x7AA, 0x88 },
+ { 0x22, 0x7AB, 0xDC },
+ { 0x22, 0x7AC, 0x1C },
+ { 0x22, 0x7AD, 0x80 },
+ { 0x22, 0x7A6, 0x7D },
+ { 0x22, 0x7A7, 0x09 },
+ { 0x22, 0x7A8, 0x28 },
+ { 0x22, 0x7A9, 0x7D },
+ { 0x22, 0x7AA, 0x09 },
+ { 0x22, 0x7AB, 0x28 },
+ { 0x22, 0x7AC, 0x1D },
+ { 0x22, 0x7AD, 0x80 },
+ { 0x22, 0x7A6, 0xC2 },
+ { 0x22, 0x7A7, 0xE5 },
+ { 0x22, 0x7A8, 0xB4 },
+ { 0x22, 0x7A9, 0xC2 },
+ { 0x22, 0x7AA, 0xE5 },
+ { 0x22, 0x7AB, 0xB4 },
+ { 0x22, 0x7AC, 0x1E },
+ { 0x22, 0x7AD, 0x80 },
+ { 0x22, 0x7A6, 0x3E },
+ { 0x22, 0x7A7, 0xA3 },
+ { 0x22, 0x7A8, 0x1F },
+ { 0x22, 0x7A9, 0x3E },
+ { 0x22, 0x7AA, 0xA3 },
+ { 0x22, 0x7AB, 0x1F },
+ { 0x22, 0x7AC, 0x1F },
+ { 0x22, 0x7AD, 0x80 },
+ { 0x22, 0x7A6, 0x84 },
+ { 0x22, 0x7A7, 0xCA },
+ { 0x22, 0x7A8, 0xF1 },
+ { 0x22, 0x7A9, 0x84 },
+ { 0x22, 0x7AA, 0xCA },
+ { 0x22, 0x7AB, 0xF1 },
+ { 0x22, 0x7AC, 0x20 },
+ { 0x22, 0x7AD, 0x80 },
+ { 0x22, 0x7A6, 0x3C },
+ { 0x22, 0x7A7, 0xD5 },
+ { 0x22, 0x7A8, 0x9C },
+ { 0x22, 0x7A9, 0x3C },
+ { 0x22, 0x7AA, 0xD5 },
+ { 0x22, 0x7AB, 0x9C },
+ { 0x22, 0x7AC, 0x21 },
+ { 0x22, 0x7AD, 0x80 },
+ { 0x22, 0x7A6, 0x7B },
+ { 0x22, 0x7A7, 0x35 },
+ { 0x22, 0x7A8, 0x0F },
+ { 0x22, 0x7A9, 0x7B },
+ { 0x22, 0x7AA, 0x35 },
+ { 0x22, 0x7AB, 0x0F },
+ { 0x22, 0x7AC, 0x22 },
+ { 0x22, 0x7AD, 0x80 },
+ { 0x22, 0x7A6, 0xC4 },
+ { 0x22, 0x7A7, 0x87 },
+ { 0x22, 0x7A8, 0x45 },
+ { 0x22, 0x7A9, 0xC4 },
+ { 0x22, 0x7AA, 0x87 },
+ { 0x22, 0x7AB, 0x45 },
+ { 0x22, 0x7AC, 0x23 },
+ { 0x22, 0x7AD, 0x80 },
+ { 0x22, 0x7A6, 0x3E },
+ { 0x22, 0x7A7, 0x0A },
+ { 0x22, 0x7A8, 0x78 },
+ { 0x22, 0x7A9, 0x3E },
+ { 0x22, 0x7AA, 0x0A },
+ { 0x22, 0x7AB, 0x78 },
+ { 0x22, 0x7AC, 0x24 },
+ { 0x22, 0x7AD, 0x80 },
+ { 0x22, 0x7A6, 0x88 },
+ { 0x22, 0x7A7, 0xE2 },
+ { 0x22, 0x7A8, 0x05 },
+ { 0x22, 0x7A9, 0x88 },
+ { 0x22, 0x7AA, 0xE2 },
+ { 0x22, 0x7AB, 0x05 },
+ { 0x22, 0x7AC, 0x25 },
+ { 0x22, 0x7AD, 0x80 },
+ { 0x22, 0x7A6, 0x3A },
+ { 0x22, 0x7A7, 0x1A },
+ { 0x22, 0x7A8, 0xA3 },
+ { 0x22, 0x7A9, 0x3A },
+ { 0x22, 0x7AA, 0x1A },
+ { 0x22, 0x7AB, 0xA3 },
+ { 0x22, 0x7AC, 0x26 },
+ { 0x22, 0x7AD, 0x80 },
+ { 0x22, 0x7A6, 0x77 },
+ { 0x22, 0x7A7, 0x1D },
+ { 0x22, 0x7A8, 0xFB },
+ { 0x22, 0x7A9, 0x77 },
+ { 0x22, 0x7AA, 0x1D },
+ { 0x22, 0x7AB, 0xFB },
+ { 0x22, 0x7AC, 0x27 },
+ { 0x22, 0x7AD, 0x80 },
+ { 0x22, 0x7A6, 0xC7 },
+ { 0x22, 0x7A7, 0xDA },
+ { 0x22, 0x7A8, 0xE5 },
+ { 0x22, 0x7A9, 0xC7 },
+ { 0x22, 0x7AA, 0xDA },
+ { 0x22, 0x7AB, 0xE5 },
+ { 0x22, 0x7AC, 0x28 },
+ { 0x22, 0x7AD, 0x80 },
+ { 0x22, 0x7A6, 0x40 },
+ { 0x22, 0x7A7, 0x00 },
+ { 0x22, 0x7A8, 0x00 },
+ { 0x22, 0x7A9, 0x40 },
+ { 0x22, 0x7AA, 0x00 },
+ { 0x22, 0x7AB, 0x00 },
+ { 0x22, 0x7AC, 0x29 },
+ { 0x22, 0x7AD, 0x80 },
+ { 0x22, 0x7A6, 0x8E },
+ { 0x22, 0x7A7, 0xD7 },
+ { 0x22, 0x7A8, 0x22 },
+ { 0x22, 0x7A9, 0x8E },
+ { 0x22, 0x7AA, 0xD7 },
+ { 0x22, 0x7AB, 0x22 },
+ { 0x22, 0x7AC, 0x2A },
+ { 0x22, 0x7AD, 0x80 },
+ { 0x22, 0x7A6, 0x35 },
+ { 0x22, 0x7A7, 0x26 },
+ { 0x22, 0x7A8, 0xC6 },
+ { 0x22, 0x7A9, 0x35 },
+ { 0x22, 0x7AA, 0x26 },
+ { 0x22, 0x7AB, 0xC6 },
+ { 0x22, 0x7AC, 0x2B },
+ { 0x22, 0x7AD, 0x80 },
+ { 0x22, 0x7A6, 0x71 },
+ { 0x22, 0x7A7, 0x28 },
+ { 0x22, 0x7A8, 0xDE },
+ { 0x22, 0x7A9, 0x71 },
+ { 0x22, 0x7AA, 0x28 },
+ { 0x22, 0x7AB, 0xDE },
+ { 0x22, 0x7AC, 0x2C },
+ { 0x22, 0x7AD, 0x80 },
+ { 0x22, 0x7A6, 0xCA },
+ { 0x22, 0x7A7, 0xD9 },
+ { 0x22, 0x7A8, 0x3A },
+ { 0x22, 0x7A9, 0xCA },
+ { 0x22, 0x7AA, 0xD9 },
+ { 0x22, 0x7AB, 0x3A },
+ { 0x22, 0x7AC, 0x2D },
+ { 0x22, 0x7AD, 0x80 },
+ { 0x22, 0x7A6, 0x40 },
+ { 0x22, 0x7A7, 0x00 },
+ { 0x22, 0x7A8, 0x00 },
+ { 0x22, 0x7A9, 0x40 },
+ { 0x22, 0x7AA, 0x00 },
+ { 0x22, 0x7AB, 0x00 },
+ { 0x22, 0x7AC, 0x2E },
+ { 0x22, 0x7AD, 0x80 },
+ { 0x22, 0x7A6, 0x93 },
+ { 0x22, 0x7A7, 0x5E },
+ { 0x22, 0x7A8, 0xD8 },
+ { 0x22, 0x7A9, 0x93 },
+ { 0x22, 0x7AA, 0x5E },
+ { 0x22, 0x7AB, 0xD8 },
+ { 0x22, 0x7AC, 0x2F },
+ { 0x22, 0x7AD, 0x80 },
+ { 0x22, 0x7A6, 0x32 },
+ { 0x22, 0x7A7, 0xB7 },
+ { 0x22, 0x7A8, 0xB1 },
+ { 0x22, 0x7A9, 0x32 },
+ { 0x22, 0x7AA, 0xB7 },
+ { 0x22, 0x7AB, 0xB1 },
+ { 0x22, 0x7AC, 0x30 },
+ { 0x22, 0x7AD, 0x80 },
+ { 0x22, 0x7A6, 0x6C },
+ { 0x22, 0x7A7, 0xA1 },
+ { 0x22, 0x7A8, 0x28 },
+ { 0x22, 0x7A9, 0x6C },
+ { 0x22, 0x7AA, 0xA1 },
+ { 0x22, 0x7AB, 0x28 },
+ { 0x22, 0x7AC, 0x31 },
+ { 0x22, 0x7AD, 0x80 },
+ { 0x22, 0x7A6, 0xCD },
+ { 0x22, 0x7A7, 0x48 },
+ { 0x22, 0x7A8, 0x4F },
+ { 0x22, 0x7A9, 0xCD },
+ { 0x22, 0x7AA, 0x48 },
+ { 0x22, 0x7AB, 0x4F },
+ { 0x22, 0x7AC, 0x32 },
+ { 0x22, 0x7AD, 0x80 },
+ { 0x22, 0x7A6, 0x40 },
+ { 0x22, 0x7A7, 0x00 },
+ { 0x22, 0x7A8, 0x00 },
+ { 0x22, 0x7A9, 0x40 },
+ { 0x22, 0x7AA, 0x00 },
+ { 0x22, 0x7AB, 0x00 },
+ { 0x22, 0x7AC, 0x33 },
+ { 0x22, 0x7AD, 0x80 },
+ /* common */
+ { 0x22, 0x782, 0xC1 },
+ { 0x22, 0x771, 0x2C },
+ { 0x22, 0x772, 0x2C },
+ { 0x22, 0x788, 0x04 },
+ { 0x01, 0x7B0, 0x08 },
+ {}
};
-static unsigned int *stac92hd83xxx_brd_tbl[STAC_92HD83XXX_MODELS] = {
- [STAC_92HD83XXX_REF] = ref92hd83xxx_pin_configs,
- [STAC_92HD83XXX_PWR_REF] = ref92hd83xxx_pin_configs,
- [STAC_DELL_S14] = dell_s14_pin_configs,
- [STAC_HP_DV7_4000] = hp_dv7_4000_pin_configs,
+static const struct hda_fixup stac92hd83xxx_fixups[] = {
+ [STAC_92HD83XXX_REF] = {
+ .type = HDA_FIXUP_PINS,
+ .v.pins = ref92hd83xxx_pin_configs,
+ },
+ [STAC_92HD83XXX_PWR_REF] = {
+ .type = HDA_FIXUP_PINS,
+ .v.pins = ref92hd83xxx_pin_configs,
+ },
+ [STAC_DELL_S14] = {
+ .type = HDA_FIXUP_PINS,
+ .v.pins = dell_s14_pin_configs,
+ },
+ [STAC_DELL_VOSTRO_3500] = {
+ .type = HDA_FIXUP_PINS,
+ .v.pins = dell_vostro_3500_pin_configs,
+ },
+ [STAC_92HD83XXX_HP_cNB11_INTQUAD] = {
+ .type = HDA_FIXUP_PINS,
+ .v.pins = hp_cNB11_intquad_pin_configs,
+ .chained = true,
+ .chain_id = STAC_92HD83XXX_HP,
+ },
+ [STAC_92HD83XXX_HP] = {
+ .type = HDA_FIXUP_FUNC,
+ .v.func = stac92hd83xxx_fixup_hp,
+ },
+ [STAC_HP_DV7_4000] = {
+ .type = HDA_FIXUP_PINS,
+ .v.pins = hp_dv7_4000_pin_configs,
+ .chained = true,
+ .chain_id = STAC_92HD83XXX_HP,
+ },
+ [STAC_HP_ZEPHYR] = {
+ .type = HDA_FIXUP_FUNC,
+ .v.func = stac92hd83xxx_fixup_hp_zephyr,
+ .chained = true,
+ .chain_id = STAC_92HD83XXX_HP,
+ },
+ [STAC_92HD83XXX_HP_LED] = {
+ .type = HDA_FIXUP_FUNC,
+ .v.func = stac92hd83xxx_fixup_hp_led,
+ .chained = true,
+ .chain_id = STAC_92HD83XXX_HP,
+ },
+ [STAC_92HD83XXX_HP_INV_LED] = {
+ .type = HDA_FIXUP_FUNC,
+ .v.func = stac92hd83xxx_fixup_hp_inv_led,
+ .chained = true,
+ .chain_id = STAC_92HD83XXX_HP,
+ },
+ [STAC_92HD83XXX_HP_MIC_LED] = {
+ .type = HDA_FIXUP_FUNC,
+ .v.func = stac92hd83xxx_fixup_hp_mic_led,
+ .chained = true,
+ .chain_id = STAC_92HD83XXX_HP,
+ },
+ [STAC_HP_LED_GPIO10] = {
+ .type = HDA_FIXUP_FUNC,
+ .v.func = stac92hd83xxx_fixup_hp_led_gpio10,
+ .chained = true,
+ .chain_id = STAC_92HD83XXX_HP,
+ },
+ [STAC_92HD83XXX_HEADSET_JACK] = {
+ .type = HDA_FIXUP_FUNC,
+ .v.func = stac92hd83xxx_fixup_headset_jack,
+ },
+ [STAC_HP_ENVY_BASS] = {
+ .type = HDA_FIXUP_PINS,
+ .v.pins = (const struct hda_pintbl[]) {
+ { 0x0f, 0x90170111 },
+ {}
+ },
+ },
+ [STAC_HP_BNB13_EQ] = {
+ .type = HDA_FIXUP_VERBS,
+ .v.verbs = hp_bnb13_eq_verbs,
+ .chained = true,
+ .chain_id = STAC_92HD83XXX_HP_MIC_LED,
+ },
};
-static const char * const stac92hd83xxx_models[STAC_92HD83XXX_MODELS] = {
- [STAC_92HD83XXX_AUTO] = "auto",
- [STAC_92HD83XXX_REF] = "ref",
- [STAC_92HD83XXX_PWR_REF] = "mic-ref",
- [STAC_DELL_S14] = "dell-s14",
- [STAC_92HD83XXX_HP] = "hp",
- [STAC_HP_DV7_4000] = "hp-dv7-4000",
+static const struct hda_model_fixup stac92hd83xxx_models[] = {
+ { .id = STAC_92HD83XXX_REF, .name = "ref" },
+ { .id = STAC_92HD83XXX_PWR_REF, .name = "mic-ref" },
+ { .id = STAC_DELL_S14, .name = "dell-s14" },
+ { .id = STAC_DELL_VOSTRO_3500, .name = "dell-vostro-3500" },
+ { .id = STAC_92HD83XXX_HP_cNB11_INTQUAD, .name = "hp_cNB11_intquad" },
+ { .id = STAC_HP_DV7_4000, .name = "hp-dv7-4000" },
+ { .id = STAC_HP_ZEPHYR, .name = "hp-zephyr" },
+ { .id = STAC_92HD83XXX_HP_LED, .name = "hp-led" },
+ { .id = STAC_92HD83XXX_HP_INV_LED, .name = "hp-inv-led" },
+ { .id = STAC_92HD83XXX_HP_MIC_LED, .name = "hp-mic-led" },
+ { .id = STAC_92HD83XXX_HEADSET_JACK, .name = "headset-jack" },
+ { .id = STAC_HP_ENVY_BASS, .name = "hp-envy-bass" },
+ { .id = STAC_HP_BNB13_EQ, .name = "hp-bnb13-eq" },
+ {}
};
-static struct snd_pci_quirk stac92hd83xxx_cfg_tbl[] = {
+static const struct snd_pci_quirk stac92hd83xxx_fixup_tbl[] = {
/* SigmaTel reference board */
SND_PCI_QUIRK(PCI_VENDOR_ID_INTEL, 0x2668,
"DFI LanParty", STAC_92HD83XXX_REF),
@@ -1654,72 +2695,469 @@ static struct snd_pci_quirk stac92hd83xxx_cfg_tbl[] = {
"DFI LanParty", STAC_92HD83XXX_REF),
SND_PCI_QUIRK(PCI_VENDOR_ID_DELL, 0x02ba,
"unknown Dell", STAC_DELL_S14),
- SND_PCI_QUIRK_MASK(PCI_VENDOR_ID_HP, 0xff00, 0x3600,
- "HP", STAC_92HD83XXX_HP),
+ SND_PCI_QUIRK(PCI_VENDOR_ID_DELL, 0x0532,
+ "Dell Latitude E6230", STAC_92HD83XXX_HEADSET_JACK),
+ SND_PCI_QUIRK(PCI_VENDOR_ID_DELL, 0x0533,
+ "Dell Latitude E6330", STAC_92HD83XXX_HEADSET_JACK),
+ SND_PCI_QUIRK(PCI_VENDOR_ID_DELL, 0x0534,
+ "Dell Latitude E6430", STAC_92HD83XXX_HEADSET_JACK),
+ SND_PCI_QUIRK(PCI_VENDOR_ID_DELL, 0x0535,
+ "Dell Latitude E6530", STAC_92HD83XXX_HEADSET_JACK),
+ SND_PCI_QUIRK(PCI_VENDOR_ID_DELL, 0x053c,
+ "Dell Latitude E5430", STAC_92HD83XXX_HEADSET_JACK),
+ SND_PCI_QUIRK(PCI_VENDOR_ID_DELL, 0x053d,
+ "Dell Latitude E5530", STAC_92HD83XXX_HEADSET_JACK),
+ SND_PCI_QUIRK(PCI_VENDOR_ID_DELL, 0x0549,
+ "Dell Latitude E5430", STAC_92HD83XXX_HEADSET_JACK),
+ SND_PCI_QUIRK(PCI_VENDOR_ID_DELL, 0x057d,
+ "Dell Latitude E6430s", STAC_92HD83XXX_HEADSET_JACK),
+ SND_PCI_QUIRK(PCI_VENDOR_ID_DELL, 0x0584,
+ "Dell Latitude E6430U", STAC_92HD83XXX_HEADSET_JACK),
+ SND_PCI_QUIRK(PCI_VENDOR_ID_DELL, 0x1028,
+ "Dell Vostro 3500", STAC_DELL_VOSTRO_3500),
+ SND_PCI_QUIRK(PCI_VENDOR_ID_HP, 0x1656,
+ "HP", STAC_92HD83XXX_HP_cNB11_INTQUAD),
+ SND_PCI_QUIRK(PCI_VENDOR_ID_HP, 0x1657,
+ "HP", STAC_92HD83XXX_HP_cNB11_INTQUAD),
+ SND_PCI_QUIRK(PCI_VENDOR_ID_HP, 0x1658,
+ "HP", STAC_92HD83XXX_HP_cNB11_INTQUAD),
+ SND_PCI_QUIRK(PCI_VENDOR_ID_HP, 0x1659,
+ "HP Pavilion dv7", STAC_HP_DV7_4000),
+ SND_PCI_QUIRK(PCI_VENDOR_ID_HP, 0x165A,
+ "HP", STAC_92HD83XXX_HP_cNB11_INTQUAD),
+ SND_PCI_QUIRK(PCI_VENDOR_ID_HP, 0x165B,
+ "HP", STAC_92HD83XXX_HP_cNB11_INTQUAD),
+ SND_PCI_QUIRK(PCI_VENDOR_ID_HP, 0x1888,
+ "HP Envy Spectre", STAC_HP_ENVY_BASS),
+ SND_PCI_QUIRK(PCI_VENDOR_ID_HP, 0x1899,
+ "HP Folio 13", STAC_HP_LED_GPIO10),
+ SND_PCI_QUIRK(PCI_VENDOR_ID_HP, 0x18df,
+ "HP Folio", STAC_HP_BNB13_EQ),
+ SND_PCI_QUIRK(PCI_VENDOR_ID_HP, 0x18F8,
+ "HP bNB13", STAC_HP_BNB13_EQ),
+ SND_PCI_QUIRK(PCI_VENDOR_ID_HP, 0x1909,
+ "HP bNB13", STAC_HP_BNB13_EQ),
+ SND_PCI_QUIRK(PCI_VENDOR_ID_HP, 0x190A,
+ "HP bNB13", STAC_HP_BNB13_EQ),
+ SND_PCI_QUIRK(PCI_VENDOR_ID_HP, 0x1940,
+ "HP bNB13", STAC_HP_BNB13_EQ),
+ SND_PCI_QUIRK(PCI_VENDOR_ID_HP, 0x1941,
+ "HP bNB13", STAC_HP_BNB13_EQ),
+ SND_PCI_QUIRK(PCI_VENDOR_ID_HP, 0x1942,
+ "HP bNB13", STAC_HP_BNB13_EQ),
+ SND_PCI_QUIRK(PCI_VENDOR_ID_HP, 0x1943,
+ "HP bNB13", STAC_HP_BNB13_EQ),
+ SND_PCI_QUIRK(PCI_VENDOR_ID_HP, 0x1944,
+ "HP bNB13", STAC_HP_BNB13_EQ),
+ SND_PCI_QUIRK(PCI_VENDOR_ID_HP, 0x1945,
+ "HP bNB13", STAC_HP_BNB13_EQ),
+ SND_PCI_QUIRK(PCI_VENDOR_ID_HP, 0x1946,
+ "HP bNB13", STAC_HP_BNB13_EQ),
+ SND_PCI_QUIRK(PCI_VENDOR_ID_HP, 0x1948,
+ "HP bNB13", STAC_HP_BNB13_EQ),
+ SND_PCI_QUIRK(PCI_VENDOR_ID_HP, 0x1949,
+ "HP bNB13", STAC_HP_BNB13_EQ),
+ SND_PCI_QUIRK(PCI_VENDOR_ID_HP, 0x194A,
+ "HP bNB13", STAC_HP_BNB13_EQ),
+ SND_PCI_QUIRK(PCI_VENDOR_ID_HP, 0x194B,
+ "HP bNB13", STAC_HP_BNB13_EQ),
+ SND_PCI_QUIRK(PCI_VENDOR_ID_HP, 0x194C,
+ "HP bNB13", STAC_HP_BNB13_EQ),
+ SND_PCI_QUIRK(PCI_VENDOR_ID_HP, 0x194E,
+ "HP bNB13", STAC_HP_BNB13_EQ),
+ SND_PCI_QUIRK(PCI_VENDOR_ID_HP, 0x194F,
+ "HP bNB13", STAC_HP_BNB13_EQ),
+ SND_PCI_QUIRK(PCI_VENDOR_ID_HP, 0x1950,
+ "HP bNB13", STAC_HP_BNB13_EQ),
+ SND_PCI_QUIRK(PCI_VENDOR_ID_HP, 0x1951,
+ "HP bNB13", STAC_HP_BNB13_EQ),
+ SND_PCI_QUIRK(PCI_VENDOR_ID_HP, 0x195A,
+ "HP bNB13", STAC_HP_BNB13_EQ),
+ SND_PCI_QUIRK(PCI_VENDOR_ID_HP, 0x195B,
+ "HP bNB13", STAC_HP_BNB13_EQ),
+ SND_PCI_QUIRK(PCI_VENDOR_ID_HP, 0x195C,
+ "HP bNB13", STAC_HP_BNB13_EQ),
+ SND_PCI_QUIRK(PCI_VENDOR_ID_HP, 0x1991,
+ "HP bNB13", STAC_HP_BNB13_EQ),
+ SND_PCI_QUIRK(PCI_VENDOR_ID_HP, 0x2103,
+ "HP bNB13", STAC_HP_BNB13_EQ),
+ SND_PCI_QUIRK(PCI_VENDOR_ID_HP, 0x2104,
+ "HP bNB13", STAC_HP_BNB13_EQ),
+ SND_PCI_QUIRK(PCI_VENDOR_ID_HP, 0x2105,
+ "HP bNB13", STAC_HP_BNB13_EQ),
+ SND_PCI_QUIRK(PCI_VENDOR_ID_HP, 0x2106,
+ "HP bNB13", STAC_HP_BNB13_EQ),
+ SND_PCI_QUIRK(PCI_VENDOR_ID_HP, 0x2107,
+ "HP bNB13", STAC_HP_BNB13_EQ),
+ SND_PCI_QUIRK(PCI_VENDOR_ID_HP, 0x2108,
+ "HP bNB13", STAC_HP_BNB13_EQ),
+ SND_PCI_QUIRK(PCI_VENDOR_ID_HP, 0x2109,
+ "HP bNB13", STAC_HP_BNB13_EQ),
+ SND_PCI_QUIRK(PCI_VENDOR_ID_HP, 0x210A,
+ "HP bNB13", STAC_HP_BNB13_EQ),
+ SND_PCI_QUIRK(PCI_VENDOR_ID_HP, 0x210B,
+ "HP bNB13", STAC_HP_BNB13_EQ),
+ SND_PCI_QUIRK(PCI_VENDOR_ID_HP, 0x211C,
+ "HP bNB13", STAC_HP_BNB13_EQ),
+ SND_PCI_QUIRK(PCI_VENDOR_ID_HP, 0x211D,
+ "HP bNB13", STAC_HP_BNB13_EQ),
+ SND_PCI_QUIRK(PCI_VENDOR_ID_HP, 0x211E,
+ "HP bNB13", STAC_HP_BNB13_EQ),
+ SND_PCI_QUIRK(PCI_VENDOR_ID_HP, 0x211F,
+ "HP bNB13", STAC_HP_BNB13_EQ),
+ SND_PCI_QUIRK(PCI_VENDOR_ID_HP, 0x2120,
+ "HP bNB13", STAC_HP_BNB13_EQ),
+ SND_PCI_QUIRK(PCI_VENDOR_ID_HP, 0x2121,
+ "HP bNB13", STAC_HP_BNB13_EQ),
+ SND_PCI_QUIRK(PCI_VENDOR_ID_HP, 0x2122,
+ "HP bNB13", STAC_HP_BNB13_EQ),
+ SND_PCI_QUIRK(PCI_VENDOR_ID_HP, 0x2123,
+ "HP bNB13", STAC_HP_BNB13_EQ),
+ SND_PCI_QUIRK(PCI_VENDOR_ID_HP, 0x213E,
+ "HP bNB13", STAC_HP_BNB13_EQ),
+ SND_PCI_QUIRK(PCI_VENDOR_ID_HP, 0x213F,
+ "HP bNB13", STAC_HP_BNB13_EQ),
+ SND_PCI_QUIRK(PCI_VENDOR_ID_HP, 0x2140,
+ "HP bNB13", STAC_HP_BNB13_EQ),
+ SND_PCI_QUIRK(PCI_VENDOR_ID_HP, 0x21B2,
+ "HP bNB13", STAC_HP_BNB13_EQ),
+ SND_PCI_QUIRK(PCI_VENDOR_ID_HP, 0x21B3,
+ "HP bNB13", STAC_HP_BNB13_EQ),
+ SND_PCI_QUIRK(PCI_VENDOR_ID_HP, 0x21B5,
+ "HP bNB13", STAC_HP_BNB13_EQ),
+ SND_PCI_QUIRK(PCI_VENDOR_ID_HP, 0x21B6,
+ "HP bNB13", STAC_HP_BNB13_EQ),
+ SND_PCI_QUIRK_MASK(PCI_VENDOR_ID_HP, 0xff00, 0x1900,
+ "HP", STAC_92HD83XXX_HP_MIC_LED),
+ SND_PCI_QUIRK_MASK(PCI_VENDOR_ID_HP, 0xff00, 0x2000,
+ "HP", STAC_92HD83XXX_HP_MIC_LED),
+ SND_PCI_QUIRK_MASK(PCI_VENDOR_ID_HP, 0xff00, 0x2100,
+ "HP", STAC_92HD83XXX_HP_MIC_LED),
+ SND_PCI_QUIRK(PCI_VENDOR_ID_HP, 0x3388,
+ "HP", STAC_92HD83XXX_HP_cNB11_INTQUAD),
+ SND_PCI_QUIRK(PCI_VENDOR_ID_HP, 0x3389,
+ "HP", STAC_92HD83XXX_HP_cNB11_INTQUAD),
+ SND_PCI_QUIRK(PCI_VENDOR_ID_HP, 0x355B,
+ "HP", STAC_92HD83XXX_HP_cNB11_INTQUAD),
+ SND_PCI_QUIRK(PCI_VENDOR_ID_HP, 0x355C,
+ "HP", STAC_92HD83XXX_HP_cNB11_INTQUAD),
+ SND_PCI_QUIRK(PCI_VENDOR_ID_HP, 0x355D,
+ "HP", STAC_92HD83XXX_HP_cNB11_INTQUAD),
+ SND_PCI_QUIRK(PCI_VENDOR_ID_HP, 0x355E,
+ "HP", STAC_92HD83XXX_HP_cNB11_INTQUAD),
+ SND_PCI_QUIRK(PCI_VENDOR_ID_HP, 0x355F,
+ "HP", STAC_92HD83XXX_HP_cNB11_INTQUAD),
+ SND_PCI_QUIRK(PCI_VENDOR_ID_HP, 0x3560,
+ "HP", STAC_92HD83XXX_HP_cNB11_INTQUAD),
+ SND_PCI_QUIRK(PCI_VENDOR_ID_HP, 0x358B,
+ "HP", STAC_92HD83XXX_HP_cNB11_INTQUAD),
+ SND_PCI_QUIRK(PCI_VENDOR_ID_HP, 0x358C,
+ "HP", STAC_92HD83XXX_HP_cNB11_INTQUAD),
+ SND_PCI_QUIRK(PCI_VENDOR_ID_HP, 0x358D,
+ "HP", STAC_92HD83XXX_HP_cNB11_INTQUAD),
+ SND_PCI_QUIRK(PCI_VENDOR_ID_HP, 0x3591,
+ "HP", STAC_92HD83XXX_HP_cNB11_INTQUAD),
+ SND_PCI_QUIRK(PCI_VENDOR_ID_HP, 0x3592,
+ "HP", STAC_92HD83XXX_HP_cNB11_INTQUAD),
+ SND_PCI_QUIRK(PCI_VENDOR_ID_HP, 0x3593,
+ "HP", STAC_92HD83XXX_HP_cNB11_INTQUAD),
+ SND_PCI_QUIRK(PCI_VENDOR_ID_HP, 0x3561,
+ "HP", STAC_HP_ZEPHYR),
+ SND_PCI_QUIRK(PCI_VENDOR_ID_HP, 0x3660,
+ "HP Mini", STAC_92HD83XXX_HP_LED),
+ SND_PCI_QUIRK(PCI_VENDOR_ID_HP, 0x144E,
+ "HP Pavilion dv5", STAC_92HD83XXX_HP_INV_LED),
+ SND_PCI_QUIRK(PCI_VENDOR_ID_HP, 0x148a,
+ "HP Mini", STAC_92HD83XXX_HP_LED),
+ SND_PCI_QUIRK_VENDOR(PCI_VENDOR_ID_HP, "HP", STAC_92HD83XXX_HP),
{} /* terminator */
};
-static unsigned int ref92hd71bxx_pin_configs[STAC92HD71BXX_NUM_PINS] = {
- 0x02214030, 0x02a19040, 0x01a19020, 0x01014010,
- 0x0181302e, 0x01014010, 0x01019020, 0x90a000f0,
- 0x90a000f0, 0x01452050, 0x01452050, 0x00000000,
- 0x00000000
+/* HP dv7 bass switch - GPIO5 */
+#define stac_hp_bass_gpio_info snd_ctl_boolean_mono_info
+static int stac_hp_bass_gpio_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
+ struct sigmatel_spec *spec = codec->spec;
+ ucontrol->value.integer.value[0] = !!(spec->gpio_data & 0x20);
+ return 0;
+}
+
+static int stac_hp_bass_gpio_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
+ struct sigmatel_spec *spec = codec->spec;
+ unsigned int gpio_data;
+
+ gpio_data = (spec->gpio_data & ~0x20) |
+ (ucontrol->value.integer.value[0] ? 0x20 : 0);
+ if (gpio_data == spec->gpio_data)
+ return 0;
+ spec->gpio_data = gpio_data;
+ stac_gpio_set(codec, spec->gpio_mask, spec->gpio_dir, spec->gpio_data);
+ return 1;
+}
+
+static const struct snd_kcontrol_new stac_hp_bass_sw_ctrl = {
+ .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+ .info = stac_hp_bass_gpio_info,
+ .get = stac_hp_bass_gpio_get,
+ .put = stac_hp_bass_gpio_put,
};
-static unsigned int dell_m4_1_pin_configs[STAC92HD71BXX_NUM_PINS] = {
- 0x0421101f, 0x04a11221, 0x40f000f0, 0x90170110,
- 0x23a1902e, 0x23014250, 0x40f000f0, 0x90a000f0,
- 0x40f000f0, 0x4f0000f0, 0x4f0000f0, 0x00000000,
- 0x00000000
+static int stac_add_hp_bass_switch(struct hda_codec *codec)
+{
+ struct sigmatel_spec *spec = codec->spec;
+
+ if (!snd_hda_gen_add_kctl(&spec->gen, "Bass Speaker Playback Switch",
+ &stac_hp_bass_sw_ctrl))
+ return -ENOMEM;
+
+ spec->gpio_mask |= 0x20;
+ spec->gpio_dir |= 0x20;
+ spec->gpio_data |= 0x20;
+ return 0;
+}
+
+static const struct hda_pintbl ref92hd71bxx_pin_configs[] = {
+ { 0x0a, 0x02214030 },
+ { 0x0b, 0x02a19040 },
+ { 0x0c, 0x01a19020 },
+ { 0x0d, 0x01014010 },
+ { 0x0e, 0x0181302e },
+ { 0x0f, 0x01014010 },
+ { 0x14, 0x01019020 },
+ { 0x18, 0x90a000f0 },
+ { 0x19, 0x90a000f0 },
+ { 0x1e, 0x01452050 },
+ { 0x1f, 0x01452050 },
+ {}
};
-static unsigned int dell_m4_2_pin_configs[STAC92HD71BXX_NUM_PINS] = {
- 0x0421101f, 0x04a11221, 0x90a70330, 0x90170110,
- 0x23a1902e, 0x23014250, 0x40f000f0, 0x40f000f0,
- 0x40f000f0, 0x044413b0, 0x044413b0, 0x00000000,
- 0x00000000
+static const struct hda_pintbl dell_m4_1_pin_configs[] = {
+ { 0x0a, 0x0421101f },
+ { 0x0b, 0x04a11221 },
+ { 0x0c, 0x40f000f0 },
+ { 0x0d, 0x90170110 },
+ { 0x0e, 0x23a1902e },
+ { 0x0f, 0x23014250 },
+ { 0x14, 0x40f000f0 },
+ { 0x18, 0x90a000f0 },
+ { 0x19, 0x40f000f0 },
+ { 0x1e, 0x4f0000f0 },
+ { 0x1f, 0x4f0000f0 },
+ {}
};
-static unsigned int dell_m4_3_pin_configs[STAC92HD71BXX_NUM_PINS] = {
- 0x0421101f, 0x04a11221, 0x90a70330, 0x90170110,
- 0x40f000f0, 0x40f000f0, 0x40f000f0, 0x90a000f0,
- 0x40f000f0, 0x044413b0, 0x044413b0, 0x00000000,
- 0x00000000
+static const struct hda_pintbl dell_m4_2_pin_configs[] = {
+ { 0x0a, 0x0421101f },
+ { 0x0b, 0x04a11221 },
+ { 0x0c, 0x90a70330 },
+ { 0x0d, 0x90170110 },
+ { 0x0e, 0x23a1902e },
+ { 0x0f, 0x23014250 },
+ { 0x14, 0x40f000f0 },
+ { 0x18, 0x40f000f0 },
+ { 0x19, 0x40f000f0 },
+ { 0x1e, 0x044413b0 },
+ { 0x1f, 0x044413b0 },
+ {}
};
-static unsigned int *stac92hd71bxx_brd_tbl[STAC_92HD71BXX_MODELS] = {
- [STAC_92HD71BXX_REF] = ref92hd71bxx_pin_configs,
- [STAC_DELL_M4_1] = dell_m4_1_pin_configs,
- [STAC_DELL_M4_2] = dell_m4_2_pin_configs,
- [STAC_DELL_M4_3] = dell_m4_3_pin_configs,
- [STAC_HP_M4] = NULL,
- [STAC_HP_DV4] = NULL,
- [STAC_HP_DV5] = NULL,
- [STAC_HP_HDX] = NULL,
- [STAC_HP_DV4_1222NR] = NULL,
+static const struct hda_pintbl dell_m4_3_pin_configs[] = {
+ { 0x0a, 0x0421101f },
+ { 0x0b, 0x04a11221 },
+ { 0x0c, 0x90a70330 },
+ { 0x0d, 0x90170110 },
+ { 0x0e, 0x40f000f0 },
+ { 0x0f, 0x40f000f0 },
+ { 0x14, 0x40f000f0 },
+ { 0x18, 0x90a000f0 },
+ { 0x19, 0x40f000f0 },
+ { 0x1e, 0x044413b0 },
+ { 0x1f, 0x044413b0 },
+ {}
};
-static const char * const stac92hd71bxx_models[STAC_92HD71BXX_MODELS] = {
- [STAC_92HD71BXX_AUTO] = "auto",
- [STAC_92HD71BXX_REF] = "ref",
- [STAC_DELL_M4_1] = "dell-m4-1",
- [STAC_DELL_M4_2] = "dell-m4-2",
- [STAC_DELL_M4_3] = "dell-m4-3",
- [STAC_HP_M4] = "hp-m4",
- [STAC_HP_DV4] = "hp-dv4",
- [STAC_HP_DV5] = "hp-dv5",
- [STAC_HP_HDX] = "hp-hdx",
- [STAC_HP_DV4_1222NR] = "hp-dv4-1222nr",
+static void stac92hd71bxx_fixup_ref(struct hda_codec *codec,
+ const struct hda_fixup *fix, int action)
+{
+ struct sigmatel_spec *spec = codec->spec;
+
+ if (action != HDA_FIXUP_ACT_PRE_PROBE)
+ return;
+
+ snd_hda_apply_pincfgs(codec, ref92hd71bxx_pin_configs);
+ spec->gpio_mask = spec->gpio_dir = spec->gpio_data = 0;
+}
+
+static void stac92hd71bxx_fixup_hp_m4(struct hda_codec *codec,
+ const struct hda_fixup *fix, int action)
+{
+ struct sigmatel_spec *spec = codec->spec;
+ struct hda_jack_tbl *jack;
+
+ if (action != HDA_FIXUP_ACT_PRE_PROBE)
+ return;
+
+ /* Enable VREF power saving on GPIO1 detect */
+ snd_hda_codec_write_cache(codec, codec->afg, 0,
+ AC_VERB_SET_GPIO_UNSOLICITED_RSP_MASK, 0x02);
+ snd_hda_jack_detect_enable_callback(codec, codec->afg,
+ STAC_VREF_EVENT,
+ stac_vref_event);
+ jack = snd_hda_jack_tbl_get(codec, codec->afg);
+ if (jack)
+ jack->private_data = 0x02;
+
+ spec->gpio_mask |= 0x02;
+
+ /* enable internal microphone */
+ snd_hda_codec_set_pincfg(codec, 0x0e, 0x01813040);
+}
+
+static void stac92hd71bxx_fixup_hp_dv4(struct hda_codec *codec,
+ const struct hda_fixup *fix, int action)
+{
+ struct sigmatel_spec *spec = codec->spec;
+
+ if (action != HDA_FIXUP_ACT_PRE_PROBE)
+ return;
+ spec->gpio_led = 0x01;
+}
+
+static void stac92hd71bxx_fixup_hp_dv5(struct hda_codec *codec,
+ const struct hda_fixup *fix, int action)
+{
+ unsigned int cap;
+
+ switch (action) {
+ case HDA_FIXUP_ACT_PRE_PROBE:
+ snd_hda_codec_set_pincfg(codec, 0x0d, 0x90170010);
+ break;
+
+ case HDA_FIXUP_ACT_PROBE:
+ /* enable bass on HP dv7 */
+ cap = snd_hda_param_read(codec, 0x1, AC_PAR_GPIO_CAP);
+ cap &= AC_GPIO_IO_COUNT;
+ if (cap >= 6)
+ stac_add_hp_bass_switch(codec);
+ break;
+ }
+}
+
+static void stac92hd71bxx_fixup_hp_hdx(struct hda_codec *codec,
+ const struct hda_fixup *fix, int action)
+{
+ struct sigmatel_spec *spec = codec->spec;
+
+ if (action != HDA_FIXUP_ACT_PRE_PROBE)
+ return;
+ spec->gpio_led = 0x08;
+}
+
+
+static void stac92hd71bxx_fixup_hp(struct hda_codec *codec,
+ const struct hda_fixup *fix, int action)
+{
+ struct sigmatel_spec *spec = codec->spec;
+
+ if (action != HDA_FIXUP_ACT_PRE_PROBE)
+ return;
+
+ if (hp_blike_system(codec->subsystem_id)) {
+ unsigned int pin_cfg = snd_hda_codec_get_pincfg(codec, 0x0f);
+ if (get_defcfg_device(pin_cfg) == AC_JACK_LINE_OUT ||
+ get_defcfg_device(pin_cfg) == AC_JACK_SPEAKER ||
+ get_defcfg_device(pin_cfg) == AC_JACK_HP_OUT) {
+ /* It was changed in the BIOS to just satisfy MS DTM.
+ * Lets turn it back into slaved HP
+ */
+ pin_cfg = (pin_cfg & (~AC_DEFCFG_DEVICE))
+ | (AC_JACK_HP_OUT <<
+ AC_DEFCFG_DEVICE_SHIFT);
+ pin_cfg = (pin_cfg & (~(AC_DEFCFG_DEF_ASSOC
+ | AC_DEFCFG_SEQUENCE)))
+ | 0x1f;
+ snd_hda_codec_set_pincfg(codec, 0x0f, pin_cfg);
+ }
+ }
+
+ if (find_mute_led_cfg(codec, 1))
+ codec_dbg(codec, "mute LED gpio %d polarity %d\n",
+ spec->gpio_led,
+ spec->gpio_led_polarity);
+
+}
+
+static const struct hda_fixup stac92hd71bxx_fixups[] = {
+ [STAC_92HD71BXX_REF] = {
+ .type = HDA_FIXUP_FUNC,
+ .v.func = stac92hd71bxx_fixup_ref,
+ },
+ [STAC_DELL_M4_1] = {
+ .type = HDA_FIXUP_PINS,
+ .v.pins = dell_m4_1_pin_configs,
+ },
+ [STAC_DELL_M4_2] = {
+ .type = HDA_FIXUP_PINS,
+ .v.pins = dell_m4_2_pin_configs,
+ },
+ [STAC_DELL_M4_3] = {
+ .type = HDA_FIXUP_PINS,
+ .v.pins = dell_m4_3_pin_configs,
+ },
+ [STAC_HP_M4] = {
+ .type = HDA_FIXUP_FUNC,
+ .v.func = stac92hd71bxx_fixup_hp_m4,
+ .chained = true,
+ .chain_id = STAC_92HD71BXX_HP,
+ },
+ [STAC_HP_DV4] = {
+ .type = HDA_FIXUP_FUNC,
+ .v.func = stac92hd71bxx_fixup_hp_dv4,
+ .chained = true,
+ .chain_id = STAC_HP_DV5,
+ },
+ [STAC_HP_DV5] = {
+ .type = HDA_FIXUP_FUNC,
+ .v.func = stac92hd71bxx_fixup_hp_dv5,
+ .chained = true,
+ .chain_id = STAC_92HD71BXX_HP,
+ },
+ [STAC_HP_HDX] = {
+ .type = HDA_FIXUP_FUNC,
+ .v.func = stac92hd71bxx_fixup_hp_hdx,
+ .chained = true,
+ .chain_id = STAC_92HD71BXX_HP,
+ },
+ [STAC_92HD71BXX_HP] = {
+ .type = HDA_FIXUP_FUNC,
+ .v.func = stac92hd71bxx_fixup_hp,
+ },
};
-static struct snd_pci_quirk stac92hd71bxx_cfg_tbl[] = {
+static const struct hda_model_fixup stac92hd71bxx_models[] = {
+ { .id = STAC_92HD71BXX_REF, .name = "ref" },
+ { .id = STAC_DELL_M4_1, .name = "dell-m4-1" },
+ { .id = STAC_DELL_M4_2, .name = "dell-m4-2" },
+ { .id = STAC_DELL_M4_3, .name = "dell-m4-3" },
+ { .id = STAC_HP_M4, .name = "hp-m4" },
+ { .id = STAC_HP_DV4, .name = "hp-dv4" },
+ { .id = STAC_HP_DV5, .name = "hp-dv5" },
+ { .id = STAC_HP_HDX, .name = "hp-hdx" },
+ { .id = STAC_HP_DV4, .name = "hp-dv4-1222nr" },
+ {}
+};
+
+static const struct snd_pci_quirk stac92hd71bxx_fixup_tbl[] = {
/* SigmaTel reference board */
SND_PCI_QUIRK(PCI_VENDOR_ID_INTEL, 0x2668,
"DFI LanParty", STAC_92HD71BXX_REF),
SND_PCI_QUIRK(PCI_VENDOR_ID_DFI, 0x3101,
"DFI LanParty", STAC_92HD71BXX_REF),
- SND_PCI_QUIRK(PCI_VENDOR_ID_HP, 0x30fb,
- "HP dv4-1222nr", STAC_HP_DV4_1222NR),
SND_PCI_QUIRK_MASK(PCI_VENDOR_ID_HP, 0xfff0, 0x1720,
"HP", STAC_HP_DV5),
SND_PCI_QUIRK_MASK(PCI_VENDOR_ID_HP, 0xfff0, 0x3080,
@@ -1742,6 +3180,7 @@ static struct snd_pci_quirk stac92hd71bxx_cfg_tbl[] = {
"HP DV6", STAC_HP_DV5),
SND_PCI_QUIRK_MASK(PCI_VENDOR_ID_HP, 0xfff0, 0x7010,
"HP", STAC_HP_DV5),
+ SND_PCI_QUIRK_VENDOR(PCI_VENDOR_ID_HP, "HP", STAC_92HD71BXX_HP),
SND_PCI_QUIRK(PCI_VENDOR_ID_DELL, 0x0233,
"unknown Dell", STAC_DELL_M4_1),
SND_PCI_QUIRK(PCI_VENDOR_ID_DELL, 0x0234,
@@ -1769,10 +3208,18 @@ static struct snd_pci_quirk stac92hd71bxx_cfg_tbl[] = {
{} /* terminator */
};
-static unsigned int ref922x_pin_configs[10] = {
- 0x01014010, 0x01016011, 0x01012012, 0x0221401f,
- 0x01813122, 0x01011014, 0x01441030, 0x01c41030,
- 0x40000100, 0x40000100,
+static const struct hda_pintbl ref922x_pin_configs[] = {
+ { 0x0a, 0x01014010 },
+ { 0x0b, 0x01016011 },
+ { 0x0c, 0x01012012 },
+ { 0x0d, 0x0221401f },
+ { 0x0e, 0x01813122 },
+ { 0x0f, 0x01011014 },
+ { 0x10, 0x01441030 },
+ { 0x11, 0x01c41030 },
+ { 0x15, 0x40000100 },
+ { 0x1b, 0x40000100 },
+ {}
};
/*
@@ -1783,10 +3230,18 @@ static unsigned int ref922x_pin_configs[10] = {
102801D1
102801D2
*/
-static unsigned int dell_922x_d81_pin_configs[10] = {
- 0x02214030, 0x01a19021, 0x01111012, 0x01114010,
- 0x02a19020, 0x01117011, 0x400001f0, 0x400001f1,
- 0x01813122, 0x400001f2,
+static const struct hda_pintbl dell_922x_d81_pin_configs[] = {
+ { 0x0a, 0x02214030 },
+ { 0x0b, 0x01a19021 },
+ { 0x0c, 0x01111012 },
+ { 0x0d, 0x01114010 },
+ { 0x0e, 0x02a19020 },
+ { 0x0f, 0x01117011 },
+ { 0x10, 0x400001f0 },
+ { 0x11, 0x400001f1 },
+ { 0x15, 0x01813122 },
+ { 0x1b, 0x400001f2 },
+ {}
};
/*
@@ -1794,130 +3249,312 @@ static unsigned int dell_922x_d81_pin_configs[10] = {
102801AC
102801D0
*/
-static unsigned int dell_922x_d82_pin_configs[10] = {
- 0x02214030, 0x01a19021, 0x01111012, 0x01114010,
- 0x02a19020, 0x01117011, 0x01451140, 0x400001f0,
- 0x01813122, 0x400001f1,
+static const struct hda_pintbl dell_922x_d82_pin_configs[] = {
+ { 0x0a, 0x02214030 },
+ { 0x0b, 0x01a19021 },
+ { 0x0c, 0x01111012 },
+ { 0x0d, 0x01114010 },
+ { 0x0e, 0x02a19020 },
+ { 0x0f, 0x01117011 },
+ { 0x10, 0x01451140 },
+ { 0x11, 0x400001f0 },
+ { 0x15, 0x01813122 },
+ { 0x1b, 0x400001f1 },
+ {}
};
/*
STAC 922X pin configs for
102801BF
*/
-static unsigned int dell_922x_m81_pin_configs[10] = {
- 0x0321101f, 0x01112024, 0x01111222, 0x91174220,
- 0x03a11050, 0x01116221, 0x90a70330, 0x01452340,
- 0x40C003f1, 0x405003f0,
+static const struct hda_pintbl dell_922x_m81_pin_configs[] = {
+ { 0x0a, 0x0321101f },
+ { 0x0b, 0x01112024 },
+ { 0x0c, 0x01111222 },
+ { 0x0d, 0x91174220 },
+ { 0x0e, 0x03a11050 },
+ { 0x0f, 0x01116221 },
+ { 0x10, 0x90a70330 },
+ { 0x11, 0x01452340 },
+ { 0x15, 0x40C003f1 },
+ { 0x1b, 0x405003f0 },
+ {}
};
/*
STAC 9221 A1 pin configs for
102801D7 (Dell XPS M1210)
*/
-static unsigned int dell_922x_m82_pin_configs[10] = {
- 0x02211211, 0x408103ff, 0x02a1123e, 0x90100310,
- 0x408003f1, 0x0221121f, 0x03451340, 0x40c003f2,
- 0x508003f3, 0x405003f4,
+static const struct hda_pintbl dell_922x_m82_pin_configs[] = {
+ { 0x0a, 0x02211211 },
+ { 0x0b, 0x408103ff },
+ { 0x0c, 0x02a1123e },
+ { 0x0d, 0x90100310 },
+ { 0x0e, 0x408003f1 },
+ { 0x0f, 0x0221121f },
+ { 0x10, 0x03451340 },
+ { 0x11, 0x40c003f2 },
+ { 0x15, 0x508003f3 },
+ { 0x1b, 0x405003f4 },
+ {}
+};
+
+static const struct hda_pintbl d945gtp3_pin_configs[] = {
+ { 0x0a, 0x0221401f },
+ { 0x0b, 0x01a19022 },
+ { 0x0c, 0x01813021 },
+ { 0x0d, 0x01014010 },
+ { 0x0e, 0x40000100 },
+ { 0x0f, 0x40000100 },
+ { 0x10, 0x40000100 },
+ { 0x11, 0x40000100 },
+ { 0x15, 0x02a19120 },
+ { 0x1b, 0x40000100 },
+ {}
};
-static unsigned int d945gtp3_pin_configs[10] = {
- 0x0221401f, 0x01a19022, 0x01813021, 0x01014010,
- 0x40000100, 0x40000100, 0x40000100, 0x40000100,
- 0x02a19120, 0x40000100,
+static const struct hda_pintbl d945gtp5_pin_configs[] = {
+ { 0x0a, 0x0221401f },
+ { 0x0b, 0x01011012 },
+ { 0x0c, 0x01813024 },
+ { 0x0d, 0x01014010 },
+ { 0x0e, 0x01a19021 },
+ { 0x0f, 0x01016011 },
+ { 0x10, 0x01452130 },
+ { 0x11, 0x40000100 },
+ { 0x15, 0x02a19320 },
+ { 0x1b, 0x40000100 },
+ {}
};
-static unsigned int d945gtp5_pin_configs[10] = {
- 0x0221401f, 0x01011012, 0x01813024, 0x01014010,
- 0x01a19021, 0x01016011, 0x01452130, 0x40000100,
- 0x02a19320, 0x40000100,
+static const struct hda_pintbl intel_mac_v1_pin_configs[] = {
+ { 0x0a, 0x0121e21f },
+ { 0x0b, 0x400000ff },
+ { 0x0c, 0x9017e110 },
+ { 0x0d, 0x400000fd },
+ { 0x0e, 0x400000fe },
+ { 0x0f, 0x0181e020 },
+ { 0x10, 0x1145e030 },
+ { 0x11, 0x11c5e240 },
+ { 0x15, 0x400000fc },
+ { 0x1b, 0x400000fb },
+ {}
};
-static unsigned int intel_mac_v1_pin_configs[10] = {
- 0x0121e21f, 0x400000ff, 0x9017e110, 0x400000fd,
- 0x400000fe, 0x0181e020, 0x1145e030, 0x11c5e240,
- 0x400000fc, 0x400000fb,
+static const struct hda_pintbl intel_mac_v2_pin_configs[] = {
+ { 0x0a, 0x0121e21f },
+ { 0x0b, 0x90a7012e },
+ { 0x0c, 0x9017e110 },
+ { 0x0d, 0x400000fd },
+ { 0x0e, 0x400000fe },
+ { 0x0f, 0x0181e020 },
+ { 0x10, 0x1145e230 },
+ { 0x11, 0x500000fa },
+ { 0x15, 0x400000fc },
+ { 0x1b, 0x400000fb },
+ {}
};
-static unsigned int intel_mac_v2_pin_configs[10] = {
- 0x0121e21f, 0x90a7012e, 0x9017e110, 0x400000fd,
- 0x400000fe, 0x0181e020, 0x1145e230, 0x500000fa,
- 0x400000fc, 0x400000fb,
+static const struct hda_pintbl intel_mac_v3_pin_configs[] = {
+ { 0x0a, 0x0121e21f },
+ { 0x0b, 0x90a7012e },
+ { 0x0c, 0x9017e110 },
+ { 0x0d, 0x400000fd },
+ { 0x0e, 0x400000fe },
+ { 0x0f, 0x0181e020 },
+ { 0x10, 0x1145e230 },
+ { 0x11, 0x11c5e240 },
+ { 0x15, 0x400000fc },
+ { 0x1b, 0x400000fb },
+ {}
};
-static unsigned int intel_mac_v3_pin_configs[10] = {
- 0x0121e21f, 0x90a7012e, 0x9017e110, 0x400000fd,
- 0x400000fe, 0x0181e020, 0x1145e230, 0x11c5e240,
- 0x400000fc, 0x400000fb,
+static const struct hda_pintbl intel_mac_v4_pin_configs[] = {
+ { 0x0a, 0x0321e21f },
+ { 0x0b, 0x03a1e02e },
+ { 0x0c, 0x9017e110 },
+ { 0x0d, 0x9017e11f },
+ { 0x0e, 0x400000fe },
+ { 0x0f, 0x0381e020 },
+ { 0x10, 0x1345e230 },
+ { 0x11, 0x13c5e240 },
+ { 0x15, 0x400000fc },
+ { 0x1b, 0x400000fb },
+ {}
};
-static unsigned int intel_mac_v4_pin_configs[10] = {
- 0x0321e21f, 0x03a1e02e, 0x9017e110, 0x9017e11f,
- 0x400000fe, 0x0381e020, 0x1345e230, 0x13c5e240,
- 0x400000fc, 0x400000fb,
+static const struct hda_pintbl intel_mac_v5_pin_configs[] = {
+ { 0x0a, 0x0321e21f },
+ { 0x0b, 0x03a1e02e },
+ { 0x0c, 0x9017e110 },
+ { 0x0d, 0x9017e11f },
+ { 0x0e, 0x400000fe },
+ { 0x0f, 0x0381e020 },
+ { 0x10, 0x1345e230 },
+ { 0x11, 0x13c5e240 },
+ { 0x15, 0x400000fc },
+ { 0x1b, 0x400000fb },
+ {}
};
-static unsigned int intel_mac_v5_pin_configs[10] = {
- 0x0321e21f, 0x03a1e02e, 0x9017e110, 0x9017e11f,
- 0x400000fe, 0x0381e020, 0x1345e230, 0x13c5e240,
- 0x400000fc, 0x400000fb,
+static const struct hda_pintbl ecs202_pin_configs[] = {
+ { 0x0a, 0x0221401f },
+ { 0x0b, 0x02a19020 },
+ { 0x0c, 0x01a19020 },
+ { 0x0d, 0x01114010 },
+ { 0x0e, 0x408000f0 },
+ { 0x0f, 0x01813022 },
+ { 0x10, 0x074510a0 },
+ { 0x11, 0x40c400f1 },
+ { 0x15, 0x9037012e },
+ { 0x1b, 0x40e000f2 },
+ {}
};
-static unsigned int ecs202_pin_configs[10] = {
- 0x0221401f, 0x02a19020, 0x01a19020, 0x01114010,
- 0x408000f0, 0x01813022, 0x074510a0, 0x40c400f1,
- 0x9037012e, 0x40e000f2,
+/* codec SSIDs for Intel Mac sharing the same PCI SSID 8384:7680 */
+static const struct snd_pci_quirk stac922x_intel_mac_fixup_tbl[] = {
+ SND_PCI_QUIRK(0x0000, 0x0100, "Mac Mini", STAC_INTEL_MAC_V3),
+ SND_PCI_QUIRK(0x106b, 0x0800, "Mac", STAC_INTEL_MAC_V1),
+ SND_PCI_QUIRK(0x106b, 0x0600, "Mac", STAC_INTEL_MAC_V2),
+ SND_PCI_QUIRK(0x106b, 0x0700, "Mac", STAC_INTEL_MAC_V2),
+ SND_PCI_QUIRK(0x106b, 0x0e00, "Mac", STAC_INTEL_MAC_V3),
+ SND_PCI_QUIRK(0x106b, 0x0f00, "Mac", STAC_INTEL_MAC_V3),
+ SND_PCI_QUIRK(0x106b, 0x1600, "Mac", STAC_INTEL_MAC_V3),
+ SND_PCI_QUIRK(0x106b, 0x1700, "Mac", STAC_INTEL_MAC_V3),
+ SND_PCI_QUIRK(0x106b, 0x0200, "Mac", STAC_INTEL_MAC_V3),
+ SND_PCI_QUIRK(0x106b, 0x1e00, "Mac", STAC_INTEL_MAC_V3),
+ SND_PCI_QUIRK(0x106b, 0x1a00, "Mac", STAC_INTEL_MAC_V4),
+ SND_PCI_QUIRK(0x106b, 0x0a00, "Mac", STAC_INTEL_MAC_V5),
+ SND_PCI_QUIRK(0x106b, 0x2200, "Mac", STAC_INTEL_MAC_V5),
+ {}
};
-static unsigned int *stac922x_brd_tbl[STAC_922X_MODELS] = {
- [STAC_D945_REF] = ref922x_pin_configs,
- [STAC_D945GTP3] = d945gtp3_pin_configs,
- [STAC_D945GTP5] = d945gtp5_pin_configs,
- [STAC_INTEL_MAC_V1] = intel_mac_v1_pin_configs,
- [STAC_INTEL_MAC_V2] = intel_mac_v2_pin_configs,
- [STAC_INTEL_MAC_V3] = intel_mac_v3_pin_configs,
- [STAC_INTEL_MAC_V4] = intel_mac_v4_pin_configs,
- [STAC_INTEL_MAC_V5] = intel_mac_v5_pin_configs,
- [STAC_INTEL_MAC_AUTO] = intel_mac_v3_pin_configs,
- /* for backward compatibility */
- [STAC_MACMINI] = intel_mac_v3_pin_configs,
- [STAC_MACBOOK] = intel_mac_v5_pin_configs,
- [STAC_MACBOOK_PRO_V1] = intel_mac_v3_pin_configs,
- [STAC_MACBOOK_PRO_V2] = intel_mac_v3_pin_configs,
- [STAC_IMAC_INTEL] = intel_mac_v2_pin_configs,
- [STAC_IMAC_INTEL_20] = intel_mac_v3_pin_configs,
- [STAC_ECS_202] = ecs202_pin_configs,
- [STAC_922X_DELL_D81] = dell_922x_d81_pin_configs,
- [STAC_922X_DELL_D82] = dell_922x_d82_pin_configs,
- [STAC_922X_DELL_M81] = dell_922x_m81_pin_configs,
- [STAC_922X_DELL_M82] = dell_922x_m82_pin_configs,
-};
-
-static const char * const stac922x_models[STAC_922X_MODELS] = {
- [STAC_922X_AUTO] = "auto",
- [STAC_D945_REF] = "ref",
- [STAC_D945GTP5] = "5stack",
- [STAC_D945GTP3] = "3stack",
- [STAC_INTEL_MAC_V1] = "intel-mac-v1",
- [STAC_INTEL_MAC_V2] = "intel-mac-v2",
- [STAC_INTEL_MAC_V3] = "intel-mac-v3",
- [STAC_INTEL_MAC_V4] = "intel-mac-v4",
- [STAC_INTEL_MAC_V5] = "intel-mac-v5",
- [STAC_INTEL_MAC_AUTO] = "intel-mac-auto",
+static const struct hda_fixup stac922x_fixups[];
+
+/* remap the fixup from codec SSID and apply it */
+static void stac922x_fixup_intel_mac_auto(struct hda_codec *codec,
+ const struct hda_fixup *fix,
+ int action)
+{
+ if (action != HDA_FIXUP_ACT_PRE_PROBE)
+ return;
+ snd_hda_pick_fixup(codec, NULL, stac922x_intel_mac_fixup_tbl,
+ stac922x_fixups);
+ if (codec->fixup_id != STAC_INTEL_MAC_AUTO)
+ snd_hda_apply_fixup(codec, action);
+}
+
+static void stac922x_fixup_intel_mac_gpio(struct hda_codec *codec,
+ const struct hda_fixup *fix,
+ int action)
+{
+ struct sigmatel_spec *spec = codec->spec;
+
+ if (action == HDA_FIXUP_ACT_PRE_PROBE) {
+ spec->gpio_mask = spec->gpio_dir = 0x03;
+ spec->gpio_data = 0x03;
+ }
+}
+
+static const struct hda_fixup stac922x_fixups[] = {
+ [STAC_D945_REF] = {
+ .type = HDA_FIXUP_PINS,
+ .v.pins = ref922x_pin_configs,
+ },
+ [STAC_D945GTP3] = {
+ .type = HDA_FIXUP_PINS,
+ .v.pins = d945gtp3_pin_configs,
+ },
+ [STAC_D945GTP5] = {
+ .type = HDA_FIXUP_PINS,
+ .v.pins = d945gtp5_pin_configs,
+ },
+ [STAC_INTEL_MAC_AUTO] = {
+ .type = HDA_FIXUP_FUNC,
+ .v.func = stac922x_fixup_intel_mac_auto,
+ },
+ [STAC_INTEL_MAC_V1] = {
+ .type = HDA_FIXUP_PINS,
+ .v.pins = intel_mac_v1_pin_configs,
+ .chained = true,
+ .chain_id = STAC_922X_INTEL_MAC_GPIO,
+ },
+ [STAC_INTEL_MAC_V2] = {
+ .type = HDA_FIXUP_PINS,
+ .v.pins = intel_mac_v2_pin_configs,
+ .chained = true,
+ .chain_id = STAC_922X_INTEL_MAC_GPIO,
+ },
+ [STAC_INTEL_MAC_V3] = {
+ .type = HDA_FIXUP_PINS,
+ .v.pins = intel_mac_v3_pin_configs,
+ .chained = true,
+ .chain_id = STAC_922X_INTEL_MAC_GPIO,
+ },
+ [STAC_INTEL_MAC_V4] = {
+ .type = HDA_FIXUP_PINS,
+ .v.pins = intel_mac_v4_pin_configs,
+ .chained = true,
+ .chain_id = STAC_922X_INTEL_MAC_GPIO,
+ },
+ [STAC_INTEL_MAC_V5] = {
+ .type = HDA_FIXUP_PINS,
+ .v.pins = intel_mac_v5_pin_configs,
+ .chained = true,
+ .chain_id = STAC_922X_INTEL_MAC_GPIO,
+ },
+ [STAC_922X_INTEL_MAC_GPIO] = {
+ .type = HDA_FIXUP_FUNC,
+ .v.func = stac922x_fixup_intel_mac_gpio,
+ },
+ [STAC_ECS_202] = {
+ .type = HDA_FIXUP_PINS,
+ .v.pins = ecs202_pin_configs,
+ },
+ [STAC_922X_DELL_D81] = {
+ .type = HDA_FIXUP_PINS,
+ .v.pins = dell_922x_d81_pin_configs,
+ },
+ [STAC_922X_DELL_D82] = {
+ .type = HDA_FIXUP_PINS,
+ .v.pins = dell_922x_d82_pin_configs,
+ },
+ [STAC_922X_DELL_M81] = {
+ .type = HDA_FIXUP_PINS,
+ .v.pins = dell_922x_m81_pin_configs,
+ },
+ [STAC_922X_DELL_M82] = {
+ .type = HDA_FIXUP_PINS,
+ .v.pins = dell_922x_m82_pin_configs,
+ },
+};
+
+static const struct hda_model_fixup stac922x_models[] = {
+ { .id = STAC_D945_REF, .name = "ref" },
+ { .id = STAC_D945GTP5, .name = "5stack" },
+ { .id = STAC_D945GTP3, .name = "3stack" },
+ { .id = STAC_INTEL_MAC_V1, .name = "intel-mac-v1" },
+ { .id = STAC_INTEL_MAC_V2, .name = "intel-mac-v2" },
+ { .id = STAC_INTEL_MAC_V3, .name = "intel-mac-v3" },
+ { .id = STAC_INTEL_MAC_V4, .name = "intel-mac-v4" },
+ { .id = STAC_INTEL_MAC_V5, .name = "intel-mac-v5" },
+ { .id = STAC_INTEL_MAC_AUTO, .name = "intel-mac-auto" },
+ { .id = STAC_ECS_202, .name = "ecs202" },
+ { .id = STAC_922X_DELL_D81, .name = "dell-d81" },
+ { .id = STAC_922X_DELL_D82, .name = "dell-d82" },
+ { .id = STAC_922X_DELL_M81, .name = "dell-m81" },
+ { .id = STAC_922X_DELL_M82, .name = "dell-m82" },
/* for backward compatibility */
- [STAC_MACMINI] = "macmini",
- [STAC_MACBOOK] = "macbook",
- [STAC_MACBOOK_PRO_V1] = "macbook-pro-v1",
- [STAC_MACBOOK_PRO_V2] = "macbook-pro",
- [STAC_IMAC_INTEL] = "imac-intel",
- [STAC_IMAC_INTEL_20] = "imac-intel-20",
- [STAC_ECS_202] = "ecs202",
- [STAC_922X_DELL_D81] = "dell-d81",
- [STAC_922X_DELL_D82] = "dell-d82",
- [STAC_922X_DELL_M81] = "dell-m81",
- [STAC_922X_DELL_M82] = "dell-m82",
-};
-
-static struct snd_pci_quirk stac922x_cfg_tbl[] = {
+ { .id = STAC_INTEL_MAC_V3, .name = "macmini" },
+ { .id = STAC_INTEL_MAC_V5, .name = "macbook" },
+ { .id = STAC_INTEL_MAC_V3, .name = "macbook-pro-v1" },
+ { .id = STAC_INTEL_MAC_V3, .name = "macbook-pro" },
+ { .id = STAC_INTEL_MAC_V2, .name = "imac-intel" },
+ { .id = STAC_INTEL_MAC_V3, .name = "imac-intel-20" },
+ {}
+};
+
+static const struct snd_pci_quirk stac922x_fixup_tbl[] = {
/* SigmaTel reference board */
SND_PCI_QUIRK(PCI_VENDOR_ID_INTEL, 0x2668,
"DFI LanParty", STAC_D945_REF),
@@ -1980,9 +3617,10 @@ static struct snd_pci_quirk stac922x_cfg_tbl[] = {
SND_PCI_QUIRK(PCI_VENDOR_ID_INTEL, 0x0204,
"Intel D945", STAC_D945_REF),
/* other systems */
+
/* Apple Intel Mac (Mac Mini, MacBook, MacBook Pro...) */
- SND_PCI_QUIRK(0x8384, 0x7680,
- "Mac", STAC_INTEL_MAC_AUTO),
+ SND_PCI_QUIRK(0x8384, 0x7680, "Mac", STAC_INTEL_MAC_AUTO),
+
/* Dell systems */
SND_PCI_QUIRK(PCI_VENDOR_ID_DELL, 0x01a7,
"unknown Dell", STAC_922X_DELL_D81),
@@ -2008,65 +3646,238 @@ static struct snd_pci_quirk stac922x_cfg_tbl[] = {
{} /* terminator */
};
-static unsigned int ref927x_pin_configs[14] = {
- 0x02214020, 0x02a19080, 0x0181304e, 0x01014010,
- 0x01a19040, 0x01011012, 0x01016011, 0x0101201f,
- 0x183301f0, 0x18a001f0, 0x18a001f0, 0x01442070,
- 0x01c42190, 0x40000100,
+static const struct hda_pintbl ref927x_pin_configs[] = {
+ { 0x0a, 0x02214020 },
+ { 0x0b, 0x02a19080 },
+ { 0x0c, 0x0181304e },
+ { 0x0d, 0x01014010 },
+ { 0x0e, 0x01a19040 },
+ { 0x0f, 0x01011012 },
+ { 0x10, 0x01016011 },
+ { 0x11, 0x0101201f },
+ { 0x12, 0x183301f0 },
+ { 0x13, 0x18a001f0 },
+ { 0x14, 0x18a001f0 },
+ { 0x21, 0x01442070 },
+ { 0x22, 0x01c42190 },
+ { 0x23, 0x40000100 },
+ {}
};
-static unsigned int d965_3st_pin_configs[14] = {
- 0x0221401f, 0x02a19120, 0x40000100, 0x01014011,
- 0x01a19021, 0x01813024, 0x40000100, 0x40000100,
- 0x40000100, 0x40000100, 0x40000100, 0x40000100,
- 0x40000100, 0x40000100
+static const struct hda_pintbl d965_3st_pin_configs[] = {
+ { 0x0a, 0x0221401f },
+ { 0x0b, 0x02a19120 },
+ { 0x0c, 0x40000100 },
+ { 0x0d, 0x01014011 },
+ { 0x0e, 0x01a19021 },
+ { 0x0f, 0x01813024 },
+ { 0x10, 0x40000100 },
+ { 0x11, 0x40000100 },
+ { 0x12, 0x40000100 },
+ { 0x13, 0x40000100 },
+ { 0x14, 0x40000100 },
+ { 0x21, 0x40000100 },
+ { 0x22, 0x40000100 },
+ { 0x23, 0x40000100 },
+ {}
};
-static unsigned int d965_5st_pin_configs[14] = {
- 0x02214020, 0x02a19080, 0x0181304e, 0x01014010,
- 0x01a19040, 0x01011012, 0x01016011, 0x40000100,
- 0x40000100, 0x40000100, 0x40000100, 0x01442070,
- 0x40000100, 0x40000100
+static const struct hda_pintbl d965_5st_pin_configs[] = {
+ { 0x0a, 0x02214020 },
+ { 0x0b, 0x02a19080 },
+ { 0x0c, 0x0181304e },
+ { 0x0d, 0x01014010 },
+ { 0x0e, 0x01a19040 },
+ { 0x0f, 0x01011012 },
+ { 0x10, 0x01016011 },
+ { 0x11, 0x40000100 },
+ { 0x12, 0x40000100 },
+ { 0x13, 0x40000100 },
+ { 0x14, 0x40000100 },
+ { 0x21, 0x01442070 },
+ { 0x22, 0x40000100 },
+ { 0x23, 0x40000100 },
+ {}
};
-static unsigned int d965_5st_no_fp_pin_configs[14] = {
- 0x40000100, 0x40000100, 0x0181304e, 0x01014010,
- 0x01a19040, 0x01011012, 0x01016011, 0x40000100,
- 0x40000100, 0x40000100, 0x40000100, 0x01442070,
- 0x40000100, 0x40000100
+static const struct hda_pintbl d965_5st_no_fp_pin_configs[] = {
+ { 0x0a, 0x40000100 },
+ { 0x0b, 0x40000100 },
+ { 0x0c, 0x0181304e },
+ { 0x0d, 0x01014010 },
+ { 0x0e, 0x01a19040 },
+ { 0x0f, 0x01011012 },
+ { 0x10, 0x01016011 },
+ { 0x11, 0x40000100 },
+ { 0x12, 0x40000100 },
+ { 0x13, 0x40000100 },
+ { 0x14, 0x40000100 },
+ { 0x21, 0x01442070 },
+ { 0x22, 0x40000100 },
+ { 0x23, 0x40000100 },
+ {}
};
-static unsigned int dell_3st_pin_configs[14] = {
- 0x02211230, 0x02a11220, 0x01a19040, 0x01114210,
- 0x01111212, 0x01116211, 0x01813050, 0x01112214,
- 0x403003fa, 0x90a60040, 0x90a60040, 0x404003fb,
- 0x40c003fc, 0x40000100
+static const struct hda_pintbl dell_3st_pin_configs[] = {
+ { 0x0a, 0x02211230 },
+ { 0x0b, 0x02a11220 },
+ { 0x0c, 0x01a19040 },
+ { 0x0d, 0x01114210 },
+ { 0x0e, 0x01111212 },
+ { 0x0f, 0x01116211 },
+ { 0x10, 0x01813050 },
+ { 0x11, 0x01112214 },
+ { 0x12, 0x403003fa },
+ { 0x13, 0x90a60040 },
+ { 0x14, 0x90a60040 },
+ { 0x21, 0x404003fb },
+ { 0x22, 0x40c003fc },
+ { 0x23, 0x40000100 },
+ {}
};
-static unsigned int *stac927x_brd_tbl[STAC_927X_MODELS] = {
- [STAC_D965_REF_NO_JD] = ref927x_pin_configs,
- [STAC_D965_REF] = ref927x_pin_configs,
- [STAC_D965_3ST] = d965_3st_pin_configs,
- [STAC_D965_5ST] = d965_5st_pin_configs,
- [STAC_D965_5ST_NO_FP] = d965_5st_no_fp_pin_configs,
- [STAC_DELL_3ST] = dell_3st_pin_configs,
- [STAC_DELL_BIOS] = NULL,
- [STAC_927X_VOLKNOB] = NULL,
+static void stac927x_fixup_ref_no_jd(struct hda_codec *codec,
+ const struct hda_fixup *fix, int action)
+{
+ /* no jack detecion for ref-no-jd model */
+ if (action == HDA_FIXUP_ACT_PRE_PROBE)
+ codec->no_jack_detect = 1;
+}
+
+static void stac927x_fixup_ref(struct hda_codec *codec,
+ const struct hda_fixup *fix, int action)
+{
+ struct sigmatel_spec *spec = codec->spec;
+
+ if (action == HDA_FIXUP_ACT_PRE_PROBE) {
+ snd_hda_apply_pincfgs(codec, ref927x_pin_configs);
+ spec->eapd_mask = spec->gpio_mask = 0;
+ spec->gpio_dir = spec->gpio_data = 0;
+ }
+}
+
+static void stac927x_fixup_dell_dmic(struct hda_codec *codec,
+ const struct hda_fixup *fix, int action)
+{
+ struct sigmatel_spec *spec = codec->spec;
+
+ if (action != HDA_FIXUP_ACT_PRE_PROBE)
+ return;
+
+ if (codec->subsystem_id != 0x1028022f) {
+ /* GPIO2 High = Enable EAPD */
+ spec->eapd_mask = spec->gpio_mask = 0x04;
+ spec->gpio_dir = spec->gpio_data = 0x04;
+ }
+
+ snd_hda_add_verbs(codec, dell_3st_core_init);
+ spec->volknob_init = 1;
+}
+
+static void stac927x_fixup_volknob(struct hda_codec *codec,
+ const struct hda_fixup *fix, int action)
+{
+ struct sigmatel_spec *spec = codec->spec;
+
+ if (action == HDA_FIXUP_ACT_PRE_PROBE) {
+ snd_hda_add_verbs(codec, stac927x_volknob_core_init);
+ spec->volknob_init = 1;
+ }
+}
+
+static const struct hda_fixup stac927x_fixups[] = {
+ [STAC_D965_REF_NO_JD] = {
+ .type = HDA_FIXUP_FUNC,
+ .v.func = stac927x_fixup_ref_no_jd,
+ .chained = true,
+ .chain_id = STAC_D965_REF,
+ },
+ [STAC_D965_REF] = {
+ .type = HDA_FIXUP_FUNC,
+ .v.func = stac927x_fixup_ref,
+ },
+ [STAC_D965_3ST] = {
+ .type = HDA_FIXUP_PINS,
+ .v.pins = d965_3st_pin_configs,
+ .chained = true,
+ .chain_id = STAC_D965_VERBS,
+ },
+ [STAC_D965_5ST] = {
+ .type = HDA_FIXUP_PINS,
+ .v.pins = d965_5st_pin_configs,
+ .chained = true,
+ .chain_id = STAC_D965_VERBS,
+ },
+ [STAC_D965_VERBS] = {
+ .type = HDA_FIXUP_VERBS,
+ .v.verbs = d965_core_init,
+ },
+ [STAC_D965_5ST_NO_FP] = {
+ .type = HDA_FIXUP_PINS,
+ .v.pins = d965_5st_no_fp_pin_configs,
+ },
+ [STAC_DELL_3ST] = {
+ .type = HDA_FIXUP_PINS,
+ .v.pins = dell_3st_pin_configs,
+ .chained = true,
+ .chain_id = STAC_927X_DELL_DMIC,
+ },
+ [STAC_DELL_BIOS] = {
+ .type = HDA_FIXUP_PINS,
+ .v.pins = (const struct hda_pintbl[]) {
+ /* correct the front output jack as a hp out */
+ { 0x0f, 0x0221101f },
+ /* correct the front input jack as a mic */
+ { 0x0e, 0x02a79130 },
+ {}
+ },
+ .chained = true,
+ .chain_id = STAC_927X_DELL_DMIC,
+ },
+ [STAC_DELL_BIOS_AMIC] = {
+ .type = HDA_FIXUP_PINS,
+ .v.pins = (const struct hda_pintbl[]) {
+ /* configure the analog microphone on some laptops */
+ { 0x0c, 0x90a79130 },
+ {}
+ },
+ .chained = true,
+ .chain_id = STAC_DELL_BIOS,
+ },
+ [STAC_DELL_BIOS_SPDIF] = {
+ .type = HDA_FIXUP_PINS,
+ .v.pins = (const struct hda_pintbl[]) {
+ /* correct the device field to SPDIF out */
+ { 0x21, 0x01442070 },
+ {}
+ },
+ .chained = true,
+ .chain_id = STAC_DELL_BIOS,
+ },
+ [STAC_927X_DELL_DMIC] = {
+ .type = HDA_FIXUP_FUNC,
+ .v.func = stac927x_fixup_dell_dmic,
+ },
+ [STAC_927X_VOLKNOB] = {
+ .type = HDA_FIXUP_FUNC,
+ .v.func = stac927x_fixup_volknob,
+ },
};
-static const char * const stac927x_models[STAC_927X_MODELS] = {
- [STAC_927X_AUTO] = "auto",
- [STAC_D965_REF_NO_JD] = "ref-no-jd",
- [STAC_D965_REF] = "ref",
- [STAC_D965_3ST] = "3stack",
- [STAC_D965_5ST] = "5stack",
- [STAC_D965_5ST_NO_FP] = "5stack-no-fp",
- [STAC_DELL_3ST] = "dell-3stack",
- [STAC_DELL_BIOS] = "dell-bios",
- [STAC_927X_VOLKNOB] = "volknob",
+static const struct hda_model_fixup stac927x_models[] = {
+ { .id = STAC_D965_REF_NO_JD, .name = "ref-no-jd" },
+ { .id = STAC_D965_REF, .name = "ref" },
+ { .id = STAC_D965_3ST, .name = "3stack" },
+ { .id = STAC_D965_5ST, .name = "5stack" },
+ { .id = STAC_D965_5ST_NO_FP, .name = "5stack-no-fp" },
+ { .id = STAC_DELL_3ST, .name = "dell-3stack" },
+ { .id = STAC_DELL_BIOS, .name = "dell-bios" },
+ { .id = STAC_DELL_BIOS_AMIC, .name = "dell-bios-amic" },
+ { .id = STAC_927X_VOLKNOB, .name = "volknob" },
+ {}
};
-static struct snd_pci_quirk stac927x_cfg_tbl[] = {
+static const struct snd_pci_quirk stac927x_fixup_tbl[] = {
/* SigmaTel reference board */
SND_PCI_QUIRK(PCI_VENDOR_ID_INTEL, 0x2668,
"DFI LanParty", STAC_D965_REF),
@@ -2088,12 +3899,12 @@ static struct snd_pci_quirk stac927x_cfg_tbl[] = {
SND_PCI_QUIRK(PCI_VENDOR_ID_DELL, 0x01f3, "Dell Inspiron 1420", STAC_DELL_BIOS),
SND_PCI_QUIRK(PCI_VENDOR_ID_DELL, 0x01f7, "Dell XPS M1730", STAC_DELL_BIOS),
SND_PCI_QUIRK(PCI_VENDOR_ID_DELL, 0x0227, "Dell Vostro 1400 ", STAC_DELL_BIOS),
- SND_PCI_QUIRK(PCI_VENDOR_ID_DELL, 0x022e, "Dell ", STAC_DELL_BIOS),
+ SND_PCI_QUIRK(PCI_VENDOR_ID_DELL, 0x022e, "Dell ", STAC_DELL_BIOS_SPDIF),
SND_PCI_QUIRK(PCI_VENDOR_ID_DELL, 0x022f, "Dell Inspiron 1525", STAC_DELL_BIOS),
SND_PCI_QUIRK(PCI_VENDOR_ID_DELL, 0x0242, "Dell ", STAC_DELL_BIOS),
SND_PCI_QUIRK(PCI_VENDOR_ID_DELL, 0x0243, "Dell ", STAC_DELL_BIOS),
SND_PCI_QUIRK(PCI_VENDOR_ID_DELL, 0x02ff, "Dell ", STAC_DELL_BIOS),
- SND_PCI_QUIRK(PCI_VENDOR_ID_DELL, 0x0209, "Dell XPS 1330", STAC_DELL_BIOS),
+ SND_PCI_QUIRK(PCI_VENDOR_ID_DELL, 0x0209, "Dell XPS 1330", STAC_DELL_BIOS_SPDIF),
/* 965 based 5 stack systems */
SND_PCI_QUIRK_MASK(PCI_VENDOR_ID_INTEL, 0xff00, 0x2300,
"Intel D965", STAC_D965_5ST),
@@ -2104,10 +3915,20 @@ static struct snd_pci_quirk stac927x_cfg_tbl[] = {
{} /* terminator */
};
-static unsigned int ref9205_pin_configs[12] = {
- 0x40000100, 0x40000100, 0x01016011, 0x01014010,
- 0x01813122, 0x01a19021, 0x01019020, 0x40000100,
- 0x90a000f0, 0x90a000f0, 0x01441030, 0x01c41030
+static const struct hda_pintbl ref9205_pin_configs[] = {
+ { 0x0a, 0x40000100 },
+ { 0x0b, 0x40000100 },
+ { 0x0c, 0x01016011 },
+ { 0x0d, 0x01014010 },
+ { 0x0e, 0x01813122 },
+ { 0x0f, 0x01a19021 },
+ { 0x14, 0x01019020 },
+ { 0x16, 0x40000100 },
+ { 0x17, 0x90a000f0 },
+ { 0x18, 0x90a000f0 },
+ { 0x21, 0x01441030 },
+ { 0x22, 0x01c41030 },
+ {}
};
/*
@@ -2121,10 +3942,20 @@ static unsigned int ref9205_pin_configs[12] = {
10280228 (Dell Vostro 1500)
10280229 (Dell Vostro 1700)
*/
-static unsigned int dell_9205_m42_pin_configs[12] = {
- 0x0321101F, 0x03A11020, 0x400003FA, 0x90170310,
- 0x400003FB, 0x400003FC, 0x400003FD, 0x40F000F9,
- 0x90A60330, 0x400003FF, 0x0144131F, 0x40C003FE,
+static const struct hda_pintbl dell_9205_m42_pin_configs[] = {
+ { 0x0a, 0x0321101F },
+ { 0x0b, 0x03A11020 },
+ { 0x0c, 0x400003FA },
+ { 0x0d, 0x90170310 },
+ { 0x0e, 0x400003FB },
+ { 0x0f, 0x400003FC },
+ { 0x14, 0x400003FD },
+ { 0x16, 0x40F000F9 },
+ { 0x17, 0x90A60330 },
+ { 0x18, 0x400003FF },
+ { 0x21, 0x0144131F },
+ { 0x22, 0x40C003FE },
+ {}
};
/*
@@ -2137,36 +3968,126 @@ static unsigned int dell_9205_m42_pin_configs[12] = {
10280200
10280201
*/
-static unsigned int dell_9205_m43_pin_configs[12] = {
- 0x0321101f, 0x03a11020, 0x90a70330, 0x90170310,
- 0x400000fe, 0x400000ff, 0x400000fd, 0x40f000f9,
- 0x400000fa, 0x400000fc, 0x0144131f, 0x40c003f8,
+static const struct hda_pintbl dell_9205_m43_pin_configs[] = {
+ { 0x0a, 0x0321101f },
+ { 0x0b, 0x03a11020 },
+ { 0x0c, 0x90a70330 },
+ { 0x0d, 0x90170310 },
+ { 0x0e, 0x400000fe },
+ { 0x0f, 0x400000ff },
+ { 0x14, 0x400000fd },
+ { 0x16, 0x40f000f9 },
+ { 0x17, 0x400000fa },
+ { 0x18, 0x400000fc },
+ { 0x21, 0x0144131f },
+ { 0x22, 0x40c003f8 },
+ /* Enable SPDIF in/out */
+ { 0x1f, 0x01441030 },
+ { 0x20, 0x1c410030 },
+ {}
};
-static unsigned int dell_9205_m44_pin_configs[12] = {
- 0x0421101f, 0x04a11020, 0x400003fa, 0x90170310,
- 0x400003fb, 0x400003fc, 0x400003fd, 0x400003f9,
- 0x90a60330, 0x400003ff, 0x01441340, 0x40c003fe,
+static const struct hda_pintbl dell_9205_m44_pin_configs[] = {
+ { 0x0a, 0x0421101f },
+ { 0x0b, 0x04a11020 },
+ { 0x0c, 0x400003fa },
+ { 0x0d, 0x90170310 },
+ { 0x0e, 0x400003fb },
+ { 0x0f, 0x400003fc },
+ { 0x14, 0x400003fd },
+ { 0x16, 0x400003f9 },
+ { 0x17, 0x90a60330 },
+ { 0x18, 0x400003ff },
+ { 0x21, 0x01441340 },
+ { 0x22, 0x40c003fe },
+ {}
};
-static unsigned int *stac9205_brd_tbl[STAC_9205_MODELS] = {
- [STAC_9205_REF] = ref9205_pin_configs,
- [STAC_9205_DELL_M42] = dell_9205_m42_pin_configs,
- [STAC_9205_DELL_M43] = dell_9205_m43_pin_configs,
- [STAC_9205_DELL_M44] = dell_9205_m44_pin_configs,
- [STAC_9205_EAPD] = NULL,
+static void stac9205_fixup_ref(struct hda_codec *codec,
+ const struct hda_fixup *fix, int action)
+{
+ struct sigmatel_spec *spec = codec->spec;
+
+ if (action == HDA_FIXUP_ACT_PRE_PROBE) {
+ snd_hda_apply_pincfgs(codec, ref9205_pin_configs);
+ /* SPDIF-In enabled */
+ spec->eapd_mask = spec->gpio_mask = spec->gpio_dir = 0;
+ }
+}
+
+static void stac9205_fixup_dell_m43(struct hda_codec *codec,
+ const struct hda_fixup *fix, int action)
+{
+ struct sigmatel_spec *spec = codec->spec;
+ struct hda_jack_tbl *jack;
+
+ if (action == HDA_FIXUP_ACT_PRE_PROBE) {
+ snd_hda_apply_pincfgs(codec, dell_9205_m43_pin_configs);
+
+ /* Enable unsol response for GPIO4/Dock HP connection */
+ snd_hda_codec_write_cache(codec, codec->afg, 0,
+ AC_VERB_SET_GPIO_UNSOLICITED_RSP_MASK, 0x10);
+ snd_hda_jack_detect_enable_callback(codec, codec->afg,
+ STAC_VREF_EVENT,
+ stac_vref_event);
+ jack = snd_hda_jack_tbl_get(codec, codec->afg);
+ if (jack)
+ jack->private_data = 0x01;
+
+ spec->gpio_dir = 0x0b;
+ spec->eapd_mask = 0x01;
+ spec->gpio_mask = 0x1b;
+ spec->gpio_mute = 0x10;
+ /* GPIO0 High = EAPD, GPIO1 Low = Headphone Mute,
+ * GPIO3 Low = DRM
+ */
+ spec->gpio_data = 0x01;
+ }
+}
+
+static void stac9205_fixup_eapd(struct hda_codec *codec,
+ const struct hda_fixup *fix, int action)
+{
+ struct sigmatel_spec *spec = codec->spec;
+
+ if (action == HDA_FIXUP_ACT_PRE_PROBE)
+ spec->eapd_switch = 0;
+}
+
+static const struct hda_fixup stac9205_fixups[] = {
+ [STAC_9205_REF] = {
+ .type = HDA_FIXUP_FUNC,
+ .v.func = stac9205_fixup_ref,
+ },
+ [STAC_9205_DELL_M42] = {
+ .type = HDA_FIXUP_PINS,
+ .v.pins = dell_9205_m42_pin_configs,
+ },
+ [STAC_9205_DELL_M43] = {
+ .type = HDA_FIXUP_FUNC,
+ .v.func = stac9205_fixup_dell_m43,
+ },
+ [STAC_9205_DELL_M44] = {
+ .type = HDA_FIXUP_PINS,
+ .v.pins = dell_9205_m44_pin_configs,
+ },
+ [STAC_9205_EAPD] = {
+ .type = HDA_FIXUP_FUNC,
+ .v.func = stac9205_fixup_eapd,
+ },
+ {}
};
-static const char * const stac9205_models[STAC_9205_MODELS] = {
- [STAC_9205_AUTO] = "auto",
- [STAC_9205_REF] = "ref",
- [STAC_9205_DELL_M42] = "dell-m42",
- [STAC_9205_DELL_M43] = "dell-m43",
- [STAC_9205_DELL_M44] = "dell-m44",
- [STAC_9205_EAPD] = "eapd",
+static const struct hda_model_fixup stac9205_models[] = {
+ { .id = STAC_9205_REF, .name = "ref" },
+ { .id = STAC_9205_DELL_M42, .name = "dell-m42" },
+ { .id = STAC_9205_DELL_M43, .name = "dell-m43" },
+ { .id = STAC_9205_DELL_M44, .name = "dell-m44" },
+ { .id = STAC_9205_EAPD, .name = "eapd" },
+ {}
};
-static struct snd_pci_quirk stac9205_cfg_tbl[] = {
+static const struct snd_pci_quirk stac9205_fixup_tbl[] = {
/* SigmaTel reference board */
SND_PCI_QUIRK(PCI_VENDOR_ID_INTEL, 0x2668,
"DFI LanParty", STAC_9205_REF),
@@ -2213,1616 +4134,92 @@ static struct snd_pci_quirk stac9205_cfg_tbl[] = {
{} /* terminator */
};
-static void stac92xx_set_config_regs(struct hda_codec *codec,
- unsigned int *pincfgs)
+static void stac92hd95_fixup_hp_led(struct hda_codec *codec,
+ const struct hda_fixup *fix, int action)
{
- int i;
struct sigmatel_spec *spec = codec->spec;
- if (!pincfgs)
+ if (action != HDA_FIXUP_ACT_PRE_PROBE)
return;
- for (i = 0; i < spec->num_pins; i++)
- if (spec->pin_nids[i] && pincfgs[i])
- snd_hda_codec_set_pincfg(codec, spec->pin_nids[i],
- pincfgs[i]);
-}
-
-/*
- * Analog playback callbacks
- */
-static int stac92xx_playback_pcm_open(struct hda_pcm_stream *hinfo,
- struct hda_codec *codec,
- struct snd_pcm_substream *substream)
-{
- struct sigmatel_spec *spec = codec->spec;
- if (spec->stream_delay)
- msleep(spec->stream_delay);
- return snd_hda_multi_out_analog_open(codec, &spec->multiout, substream,
- hinfo);
-}
-
-static int stac92xx_playback_pcm_prepare(struct hda_pcm_stream *hinfo,
- struct hda_codec *codec,
- unsigned int stream_tag,
- unsigned int format,
- struct snd_pcm_substream *substream)
-{
- struct sigmatel_spec *spec = codec->spec;
- return snd_hda_multi_out_analog_prepare(codec, &spec->multiout, stream_tag, format, substream);
-}
-
-static int stac92xx_playback_pcm_cleanup(struct hda_pcm_stream *hinfo,
- struct hda_codec *codec,
- struct snd_pcm_substream *substream)
-{
- struct sigmatel_spec *spec = codec->spec;
- return snd_hda_multi_out_analog_cleanup(codec, &spec->multiout);
-}
-
-/*
- * Digital playback callbacks
- */
-static int stac92xx_dig_playback_pcm_open(struct hda_pcm_stream *hinfo,
- struct hda_codec *codec,
- struct snd_pcm_substream *substream)
-{
- struct sigmatel_spec *spec = codec->spec;
- return snd_hda_multi_out_dig_open(codec, &spec->multiout);
-}
-
-static int stac92xx_dig_playback_pcm_close(struct hda_pcm_stream *hinfo,
- struct hda_codec *codec,
- struct snd_pcm_substream *substream)
-{
- struct sigmatel_spec *spec = codec->spec;
- return snd_hda_multi_out_dig_close(codec, &spec->multiout);
-}
-
-static int stac92xx_dig_playback_pcm_prepare(struct hda_pcm_stream *hinfo,
- struct hda_codec *codec,
- unsigned int stream_tag,
- unsigned int format,
- struct snd_pcm_substream *substream)
-{
- struct sigmatel_spec *spec = codec->spec;
- return snd_hda_multi_out_dig_prepare(codec, &spec->multiout,
- stream_tag, format, substream);
-}
-
-static int stac92xx_dig_playback_pcm_cleanup(struct hda_pcm_stream *hinfo,
- struct hda_codec *codec,
- struct snd_pcm_substream *substream)
-{
- struct sigmatel_spec *spec = codec->spec;
- return snd_hda_multi_out_dig_cleanup(codec, &spec->multiout);
-}
-
-
-/*
- * Analog capture callbacks
- */
-static int stac92xx_capture_pcm_prepare(struct hda_pcm_stream *hinfo,
- struct hda_codec *codec,
- unsigned int stream_tag,
- unsigned int format,
- struct snd_pcm_substream *substream)
-{
- struct sigmatel_spec *spec = codec->spec;
- hda_nid_t nid = spec->adc_nids[substream->number];
-
- if (spec->powerdown_adcs) {
- msleep(40);
- snd_hda_codec_write(codec, nid, 0,
- AC_VERB_SET_POWER_STATE, AC_PWRST_D0);
- }
- snd_hda_codec_setup_stream(codec, nid, stream_tag, 0, format);
- return 0;
-}
-
-static int stac92xx_capture_pcm_cleanup(struct hda_pcm_stream *hinfo,
- struct hda_codec *codec,
- struct snd_pcm_substream *substream)
-{
- struct sigmatel_spec *spec = codec->spec;
- hda_nid_t nid = spec->adc_nids[substream->number];
-
- snd_hda_codec_cleanup_stream(codec, nid);
- if (spec->powerdown_adcs)
- snd_hda_codec_write(codec, nid, 0,
- AC_VERB_SET_POWER_STATE, AC_PWRST_D3);
- return 0;
+ if (find_mute_led_cfg(codec, spec->default_polarity))
+ codec_dbg(codec, "mute LED gpio %d polarity %d\n",
+ spec->gpio_led,
+ spec->gpio_led_polarity);
}
-static struct hda_pcm_stream stac92xx_pcm_digital_playback = {
- .substreams = 1,
- .channels_min = 2,
- .channels_max = 2,
- /* NID is set in stac92xx_build_pcms */
- .ops = {
- .open = stac92xx_dig_playback_pcm_open,
- .close = stac92xx_dig_playback_pcm_close,
- .prepare = stac92xx_dig_playback_pcm_prepare,
- .cleanup = stac92xx_dig_playback_pcm_cleanup
- },
-};
-
-static struct hda_pcm_stream stac92xx_pcm_digital_capture = {
- .substreams = 1,
- .channels_min = 2,
- .channels_max = 2,
- /* NID is set in stac92xx_build_pcms */
-};
-
-static struct hda_pcm_stream stac92xx_pcm_analog_playback = {
- .substreams = 1,
- .channels_min = 2,
- .channels_max = 8,
- .nid = 0x02, /* NID to query formats and rates */
- .ops = {
- .open = stac92xx_playback_pcm_open,
- .prepare = stac92xx_playback_pcm_prepare,
- .cleanup = stac92xx_playback_pcm_cleanup
+static const struct hda_fixup stac92hd95_fixups[] = {
+ [STAC_92HD95_HP_LED] = {
+ .type = HDA_FIXUP_FUNC,
+ .v.func = stac92hd95_fixup_hp_led,
},
-};
-
-static struct hda_pcm_stream stac92xx_pcm_analog_alt_playback = {
- .substreams = 1,
- .channels_min = 2,
- .channels_max = 2,
- .nid = 0x06, /* NID to query formats and rates */
- .ops = {
- .open = stac92xx_playback_pcm_open,
- .prepare = stac92xx_playback_pcm_prepare,
- .cleanup = stac92xx_playback_pcm_cleanup
+ [STAC_92HD95_HP_BASS] = {
+ .type = HDA_FIXUP_VERBS,
+ .v.verbs = (const struct hda_verb[]) {
+ {0x1a, 0x795, 0x00}, /* HPF to 100Hz */
+ {}
+ },
+ .chained = true,
+ .chain_id = STAC_92HD95_HP_LED,
},
};
-static struct hda_pcm_stream stac92xx_pcm_analog_capture = {
- .channels_min = 2,
- .channels_max = 2,
- /* NID + .substreams is set in stac92xx_build_pcms */
- .ops = {
- .prepare = stac92xx_capture_pcm_prepare,
- .cleanup = stac92xx_capture_pcm_cleanup
- },
+static const struct snd_pci_quirk stac92hd95_fixup_tbl[] = {
+ SND_PCI_QUIRK(PCI_VENDOR_ID_HP, 0x1911, "HP Spectre 13", STAC_92HD95_HP_BASS),
+ {} /* terminator */
};
-static int stac92xx_build_pcms(struct hda_codec *codec)
-{
- struct sigmatel_spec *spec = codec->spec;
- struct hda_pcm *info = spec->pcm_rec;
-
- codec->num_pcms = 1;
- codec->pcm_info = info;
-
- info->name = "STAC92xx Analog";
- info->stream[SNDRV_PCM_STREAM_PLAYBACK] = stac92xx_pcm_analog_playback;
- info->stream[SNDRV_PCM_STREAM_PLAYBACK].nid =
- spec->multiout.dac_nids[0];
- info->stream[SNDRV_PCM_STREAM_CAPTURE] = stac92xx_pcm_analog_capture;
- info->stream[SNDRV_PCM_STREAM_CAPTURE].nid = spec->adc_nids[0];
- info->stream[SNDRV_PCM_STREAM_CAPTURE].substreams = spec->num_adcs;
-
- if (spec->alt_switch) {
- codec->num_pcms++;
- info++;
- info->name = "STAC92xx Analog Alt";
- info->stream[SNDRV_PCM_STREAM_PLAYBACK] = stac92xx_pcm_analog_alt_playback;
- }
-
- if (spec->multiout.dig_out_nid || spec->dig_in_nid) {
- codec->num_pcms++;
- info++;
- info->name = "STAC92xx Digital";
- info->pcm_type = spec->autocfg.dig_out_type[0];
- if (spec->multiout.dig_out_nid) {
- info->stream[SNDRV_PCM_STREAM_PLAYBACK] = stac92xx_pcm_digital_playback;
- info->stream[SNDRV_PCM_STREAM_PLAYBACK].nid = spec->multiout.dig_out_nid;
- }
- if (spec->dig_in_nid) {
- info->stream[SNDRV_PCM_STREAM_CAPTURE] = stac92xx_pcm_digital_capture;
- info->stream[SNDRV_PCM_STREAM_CAPTURE].nid = spec->dig_in_nid;
- }
- }
-
- return 0;
-}
-
-static unsigned int stac92xx_get_default_vref(struct hda_codec *codec,
- hda_nid_t nid)
-{
- unsigned int pincap = snd_hda_query_pin_caps(codec, nid);
- pincap = (pincap & AC_PINCAP_VREF) >> AC_PINCAP_VREF_SHIFT;
- if (pincap & AC_PINCAP_VREF_100)
- return AC_PINCTL_VREF_100;
- if (pincap & AC_PINCAP_VREF_80)
- return AC_PINCTL_VREF_80;
- if (pincap & AC_PINCAP_VREF_50)
- return AC_PINCTL_VREF_50;
- if (pincap & AC_PINCAP_VREF_GRD)
- return AC_PINCTL_VREF_GRD;
- return 0;
-}
-
-static void stac92xx_auto_set_pinctl(struct hda_codec *codec, hda_nid_t nid, int pin_type)
-
-{
- snd_hda_codec_write_cache(codec, nid, 0,
- AC_VERB_SET_PIN_WIDGET_CONTROL, pin_type);
-}
-
-#define stac92xx_hp_switch_info snd_ctl_boolean_mono_info
-
-static int stac92xx_hp_switch_get(struct snd_kcontrol *kcontrol,
- struct snd_ctl_elem_value *ucontrol)
-{
- struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
- struct sigmatel_spec *spec = codec->spec;
-
- ucontrol->value.integer.value[0] = !!spec->hp_switch;
- return 0;
-}
-
-static void stac_issue_unsol_event(struct hda_codec *codec, hda_nid_t nid);
-
-static int stac92xx_hp_switch_put(struct snd_kcontrol *kcontrol,
- struct snd_ctl_elem_value *ucontrol)
-{
- struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
- struct sigmatel_spec *spec = codec->spec;
- int nid = kcontrol->private_value;
-
- spec->hp_switch = ucontrol->value.integer.value[0] ? nid : 0;
-
- /* check to be sure that the ports are up to date with
- * switch changes
- */
- stac_issue_unsol_event(codec, nid);
-
- return 1;
-}
-
-static int stac92xx_dc_bias_info(struct snd_kcontrol *kcontrol,
- struct snd_ctl_elem_info *uinfo)
-{
- int i;
- static char *texts[] = {
- "Mic In", "Line In", "Line Out"
- };
-
- struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
- struct sigmatel_spec *spec = codec->spec;
- hda_nid_t nid = kcontrol->private_value;
-
- if (nid == spec->mic_switch || nid == spec->line_switch)
- i = 3;
- else
- i = 2;
-
- uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED;
- uinfo->value.enumerated.items = i;
- uinfo->count = 1;
- if (uinfo->value.enumerated.item >= i)
- uinfo->value.enumerated.item = i-1;
- strcpy(uinfo->value.enumerated.name,
- texts[uinfo->value.enumerated.item]);
-
- return 0;
-}
-
-static int stac92xx_dc_bias_get(struct snd_kcontrol *kcontrol,
- struct snd_ctl_elem_value *ucontrol)
-{
- struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
- hda_nid_t nid = kcontrol->private_value;
- unsigned int vref = stac92xx_vref_get(codec, nid);
-
- if (vref == stac92xx_get_default_vref(codec, nid))
- ucontrol->value.enumerated.item[0] = 0;
- else if (vref == AC_PINCTL_VREF_GRD)
- ucontrol->value.enumerated.item[0] = 1;
- else if (vref == AC_PINCTL_VREF_HIZ)
- ucontrol->value.enumerated.item[0] = 2;
-
- return 0;
-}
-
-static int stac92xx_dc_bias_put(struct snd_kcontrol *kcontrol,
- struct snd_ctl_elem_value *ucontrol)
-{
- struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
- unsigned int new_vref = 0;
- int error;
- hda_nid_t nid = kcontrol->private_value;
-
- if (ucontrol->value.enumerated.item[0] == 0)
- new_vref = stac92xx_get_default_vref(codec, nid);
- else if (ucontrol->value.enumerated.item[0] == 1)
- new_vref = AC_PINCTL_VREF_GRD;
- else if (ucontrol->value.enumerated.item[0] == 2)
- new_vref = AC_PINCTL_VREF_HIZ;
- else
- return 0;
-
- if (new_vref != stac92xx_vref_get(codec, nid)) {
- error = stac92xx_vref_set(codec, nid, new_vref);
- return error;
- }
-
- return 0;
-}
-
-static int stac92xx_io_switch_info(struct snd_kcontrol *kcontrol,
- struct snd_ctl_elem_info *uinfo)
-{
- static char *texts[2];
- struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
- struct sigmatel_spec *spec = codec->spec;
-
- if (kcontrol->private_value == spec->line_switch)
- texts[0] = "Line In";
- else
- texts[0] = "Mic In";
- texts[1] = "Line Out";
- uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED;
- uinfo->value.enumerated.items = 2;
- uinfo->count = 1;
-
- if (uinfo->value.enumerated.item >= 2)
- uinfo->value.enumerated.item = 1;
- strcpy(uinfo->value.enumerated.name,
- texts[uinfo->value.enumerated.item]);
-
- return 0;
-}
-
-static int stac92xx_io_switch_get(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
-{
- struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
- struct sigmatel_spec *spec = codec->spec;
- hda_nid_t nid = kcontrol->private_value;
- int io_idx = (nid == spec->mic_switch) ? 1 : 0;
-
- ucontrol->value.enumerated.item[0] = spec->io_switch[io_idx];
- return 0;
-}
-
-static int stac92xx_io_switch_put(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
-{
- struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
- struct sigmatel_spec *spec = codec->spec;
- hda_nid_t nid = kcontrol->private_value;
- int io_idx = (nid == spec->mic_switch) ? 1 : 0;
- unsigned short val = !!ucontrol->value.enumerated.item[0];
-
- spec->io_switch[io_idx] = val;
-
- if (val)
- stac92xx_auto_set_pinctl(codec, nid, AC_PINCTL_OUT_EN);
- else {
- unsigned int pinctl = AC_PINCTL_IN_EN;
- if (io_idx) /* set VREF for mic */
- pinctl |= stac92xx_get_default_vref(codec, nid);
- stac92xx_auto_set_pinctl(codec, nid, pinctl);
- }
-
- /* check the auto-mute again: we need to mute/unmute the speaker
- * appropriately according to the pin direction
- */
- if (spec->hp_detect)
- stac_issue_unsol_event(codec, nid);
-
- return 1;
-}
-
-#define stac92xx_clfe_switch_info snd_ctl_boolean_mono_info
-
-static int stac92xx_clfe_switch_get(struct snd_kcontrol *kcontrol,
- struct snd_ctl_elem_value *ucontrol)
-{
- struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
- struct sigmatel_spec *spec = codec->spec;
-
- ucontrol->value.integer.value[0] = spec->clfe_swap;
- return 0;
-}
-
-static int stac92xx_clfe_switch_put(struct snd_kcontrol *kcontrol,
- struct snd_ctl_elem_value *ucontrol)
-{
- struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
- struct sigmatel_spec *spec = codec->spec;
- hda_nid_t nid = kcontrol->private_value & 0xff;
- unsigned int val = !!ucontrol->value.integer.value[0];
-
- if (spec->clfe_swap == val)
- return 0;
-
- spec->clfe_swap = val;
-
- snd_hda_codec_write_cache(codec, nid, 0, AC_VERB_SET_EAPD_BTLENABLE,
- spec->clfe_swap ? 0x4 : 0x0);
-
- return 1;
-}
-
-#define STAC_CODEC_HP_SWITCH(xname) \
- { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, \
- .name = xname, \
- .index = 0, \
- .info = stac92xx_hp_switch_info, \
- .get = stac92xx_hp_switch_get, \
- .put = stac92xx_hp_switch_put, \
- }
-
-#define STAC_CODEC_IO_SWITCH(xname, xpval) \
- { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, \
- .name = xname, \
- .index = 0, \
- .info = stac92xx_io_switch_info, \
- .get = stac92xx_io_switch_get, \
- .put = stac92xx_io_switch_put, \
- .private_value = xpval, \
- }
-
-#define STAC_CODEC_CLFE_SWITCH(xname, xpval) \
- { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, \
- .name = xname, \
- .index = 0, \
- .info = stac92xx_clfe_switch_info, \
- .get = stac92xx_clfe_switch_get, \
- .put = stac92xx_clfe_switch_put, \
- .private_value = xpval, \
- }
-
-enum {
- STAC_CTL_WIDGET_VOL,
- STAC_CTL_WIDGET_MUTE,
- STAC_CTL_WIDGET_MUTE_BEEP,
- STAC_CTL_WIDGET_MONO_MUX,
- STAC_CTL_WIDGET_HP_SWITCH,
- STAC_CTL_WIDGET_IO_SWITCH,
- STAC_CTL_WIDGET_CLFE_SWITCH,
- STAC_CTL_WIDGET_DC_BIAS
-};
-
-static struct snd_kcontrol_new stac92xx_control_templates[] = {
- HDA_CODEC_VOLUME(NULL, 0, 0, 0),
- HDA_CODEC_MUTE(NULL, 0, 0, 0),
- HDA_CODEC_MUTE_BEEP(NULL, 0, 0, 0),
- STAC_MONO_MUX,
- STAC_CODEC_HP_SWITCH(NULL),
- STAC_CODEC_IO_SWITCH(NULL, 0),
- STAC_CODEC_CLFE_SWITCH(NULL, 0),
- DC_BIAS(NULL, 0, 0),
-};
-
-/* add dynamic controls */
-static struct snd_kcontrol_new *
-stac_control_new(struct sigmatel_spec *spec,
- struct snd_kcontrol_new *ktemp,
- const char *name,
- unsigned int subdev)
-{
- struct snd_kcontrol_new *knew;
-
- snd_array_init(&spec->kctls, sizeof(*knew), 32);
- knew = snd_array_new(&spec->kctls);
- if (!knew)
- return NULL;
- *knew = *ktemp;
- knew->name = kstrdup(name, GFP_KERNEL);
- if (!knew->name) {
- /* roolback */
- memset(knew, 0, sizeof(*knew));
- spec->kctls.alloced--;
- return NULL;
- }
- knew->subdevice = subdev;
- return knew;
-}
-
-static int stac92xx_add_control_temp(struct sigmatel_spec *spec,
- struct snd_kcontrol_new *ktemp,
- int idx, const char *name,
- unsigned long val)
-{
- struct snd_kcontrol_new *knew = stac_control_new(spec, ktemp, name,
- HDA_SUBDEV_AMP_FLAG);
- if (!knew)
- return -ENOMEM;
- knew->index = idx;
- knew->private_value = val;
- return 0;
-}
-
-static inline int stac92xx_add_control_idx(struct sigmatel_spec *spec,
- int type, int idx, const char *name,
- unsigned long val)
-{
- return stac92xx_add_control_temp(spec,
- &stac92xx_control_templates[type],
- idx, name, val);
-}
-
-
-/* add dynamic controls */
-static inline int stac92xx_add_control(struct sigmatel_spec *spec, int type,
- const char *name, unsigned long val)
-{
- return stac92xx_add_control_idx(spec, type, 0, name, val);
-}
-
-static struct snd_kcontrol_new stac_input_src_temp = {
- .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
- .name = "Input Source",
- .info = stac92xx_mux_enum_info,
- .get = stac92xx_mux_enum_get,
- .put = stac92xx_mux_enum_put,
+static const struct hda_model_fixup stac92hd95_models[] = {
+ { .id = STAC_92HD95_HP_LED, .name = "hp-led" },
+ { .id = STAC_92HD95_HP_BASS, .name = "hp-bass" },
+ {}
};
-static inline int stac92xx_add_jack_mode_control(struct hda_codec *codec,
- hda_nid_t nid, int idx)
-{
- int def_conf = snd_hda_codec_get_pincfg(codec, nid);
- int control = 0;
- struct sigmatel_spec *spec = codec->spec;
- char name[22];
-
- if (snd_hda_get_input_pin_attr(def_conf) != INPUT_PIN_ATTR_INT) {
- if (stac92xx_get_default_vref(codec, nid) == AC_PINCTL_VREF_GRD
- && nid == spec->line_switch)
- control = STAC_CTL_WIDGET_IO_SWITCH;
- else if (snd_hda_query_pin_caps(codec, nid)
- & (AC_PINCAP_VREF_GRD << AC_PINCAP_VREF_SHIFT))
- control = STAC_CTL_WIDGET_DC_BIAS;
- else if (nid == spec->mic_switch)
- control = STAC_CTL_WIDGET_IO_SWITCH;
- }
-
- if (control) {
- strcpy(name, hda_get_input_pin_label(codec, nid, 1));
- return stac92xx_add_control(codec->spec, control,
- strcat(name, " Jack Mode"), nid);
- }
-
- return 0;
-}
-
-static int stac92xx_add_input_source(struct sigmatel_spec *spec)
-{
- struct snd_kcontrol_new *knew;
- struct hda_input_mux *imux = &spec->private_imux;
-
- if (spec->auto_mic)
- return 0; /* no need for input source */
- if (!spec->num_adcs || imux->num_items <= 1)
- return 0; /* no need for input source control */
- knew = stac_control_new(spec, &stac_input_src_temp,
- stac_input_src_temp.name, 0);
- if (!knew)
- return -ENOMEM;
- knew->count = spec->num_adcs;
- return 0;
-}
-
-/* check whether the line-input can be used as line-out */
-static hda_nid_t check_line_out_switch(struct hda_codec *codec)
-{
- struct sigmatel_spec *spec = codec->spec;
- struct auto_pin_cfg *cfg = &spec->autocfg;
- hda_nid_t nid;
- unsigned int pincap;
- int i;
-
- if (cfg->line_out_type != AUTO_PIN_LINE_OUT)
- return 0;
- for (i = 0; i < cfg->num_inputs; i++) {
- if (cfg->inputs[i].type == AUTO_PIN_LINE_IN) {
- nid = cfg->inputs[i].pin;
- pincap = snd_hda_query_pin_caps(codec, nid);
- if (pincap & AC_PINCAP_OUT)
- return nid;
- }
- }
- return 0;
-}
-
-static hda_nid_t get_unassigned_dac(struct hda_codec *codec, hda_nid_t nid);
-
-/* check whether the mic-input can be used as line-out */
-static hda_nid_t check_mic_out_switch(struct hda_codec *codec, hda_nid_t *dac)
-{
- struct sigmatel_spec *spec = codec->spec;
- struct auto_pin_cfg *cfg = &spec->autocfg;
- unsigned int def_conf, pincap;
- int i;
-
- *dac = 0;
- if (cfg->line_out_type != AUTO_PIN_LINE_OUT)
- return 0;
- for (i = 0; i < cfg->num_inputs; i++) {
- hda_nid_t nid = cfg->inputs[i].pin;
- if (cfg->inputs[i].type != AUTO_PIN_MIC)
- continue;
- def_conf = snd_hda_codec_get_pincfg(codec, nid);
- /* some laptops have an internal analog microphone
- * which can't be used as a output */
- if (snd_hda_get_input_pin_attr(def_conf) != INPUT_PIN_ATTR_INT) {
- pincap = snd_hda_query_pin_caps(codec, nid);
- if (pincap & AC_PINCAP_OUT) {
- *dac = get_unassigned_dac(codec, nid);
- if (*dac)
- return nid;
- }
- }
- }
- return 0;
-}
-
-static int is_in_dac_nids(struct sigmatel_spec *spec, hda_nid_t nid)
-{
- int i;
-
- for (i = 0; i < spec->multiout.num_dacs; i++) {
- if (spec->multiout.dac_nids[i] == nid)
- return 1;
- }
-
- return 0;
-}
-
-static int check_all_dac_nids(struct sigmatel_spec *spec, hda_nid_t nid)
-{
- int i;
- if (is_in_dac_nids(spec, nid))
- return 1;
- for (i = 0; i < spec->autocfg.hp_outs; i++)
- if (spec->hp_dacs[i] == nid)
- return 1;
- for (i = 0; i < spec->autocfg.speaker_outs; i++)
- if (spec->speaker_dacs[i] == nid)
- return 1;
- return 0;
-}
-
-static hda_nid_t get_unassigned_dac(struct hda_codec *codec, hda_nid_t nid)
-{
- struct sigmatel_spec *spec = codec->spec;
- int j, conn_len;
- hda_nid_t conn[HDA_MAX_CONNECTIONS];
- unsigned int wcaps, wtype;
-
- conn_len = snd_hda_get_connections(codec, nid, conn,
- HDA_MAX_CONNECTIONS);
- /* 92HD88: trace back up the link of nids to find the DAC */
- while (conn_len == 1 && (get_wcaps_type(get_wcaps(codec, conn[0]))
- != AC_WID_AUD_OUT)) {
- nid = conn[0];
- conn_len = snd_hda_get_connections(codec, nid, conn,
- HDA_MAX_CONNECTIONS);
- }
- for (j = 0; j < conn_len; j++) {
- wcaps = get_wcaps(codec, conn[j]);
- wtype = get_wcaps_type(wcaps);
- /* we check only analog outputs */
- if (wtype != AC_WID_AUD_OUT || (wcaps & AC_WCAP_DIGITAL))
- continue;
- /* if this route has a free DAC, assign it */
- if (!check_all_dac_nids(spec, conn[j])) {
- if (conn_len > 1) {
- /* select this DAC in the pin's input mux */
- snd_hda_codec_write_cache(codec, nid, 0,
- AC_VERB_SET_CONNECT_SEL, j);
- }
- return conn[j];
- }
- }
- /* if all DACs are already assigned, connect to the primary DAC */
- if (conn_len > 1) {
- for (j = 0; j < conn_len; j++) {
- if (conn[j] == spec->multiout.dac_nids[0]) {
- snd_hda_codec_write_cache(codec, nid, 0,
- AC_VERB_SET_CONNECT_SEL, j);
- break;
- }
- }
- }
- return 0;
-}
-
-static int add_spec_dacs(struct sigmatel_spec *spec, hda_nid_t nid);
-static int add_spec_extra_dacs(struct sigmatel_spec *spec, hda_nid_t nid);
-
-/*
- * Fill in the dac_nids table from the parsed pin configuration
- * This function only works when every pin in line_out_pins[]
- * contains atleast one DAC in its connection list. Some 92xx
- * codecs are not connected directly to a DAC, such as the 9200
- * and 9202/925x. For those, dac_nids[] must be hard-coded.
- */
-static int stac92xx_auto_fill_dac_nids(struct hda_codec *codec)
-{
- struct sigmatel_spec *spec = codec->spec;
- struct auto_pin_cfg *cfg = &spec->autocfg;
- int i;
- hda_nid_t nid, dac;
-
- for (i = 0; i < cfg->line_outs; i++) {
- nid = cfg->line_out_pins[i];
- dac = get_unassigned_dac(codec, nid);
- if (!dac) {
- if (spec->multiout.num_dacs > 0) {
- /* we have already working output pins,
- * so let's drop the broken ones again
- */
- cfg->line_outs = spec->multiout.num_dacs;
- break;
- }
- /* error out, no available DAC found */
- snd_printk(KERN_ERR
- "%s: No available DAC for pin 0x%x\n",
- __func__, nid);
- return -ENODEV;
- }
- add_spec_dacs(spec, dac);
- }
-
- for (i = 0; i < cfg->hp_outs; i++) {
- nid = cfg->hp_pins[i];
- dac = get_unassigned_dac(codec, nid);
- if (dac) {
- if (!spec->multiout.hp_nid)
- spec->multiout.hp_nid = dac;
- else
- add_spec_extra_dacs(spec, dac);
- }
- spec->hp_dacs[i] = dac;
- }
-
- for (i = 0; i < cfg->speaker_outs; i++) {
- nid = cfg->speaker_pins[i];
- dac = get_unassigned_dac(codec, nid);
- if (dac)
- add_spec_extra_dacs(spec, dac);
- spec->speaker_dacs[i] = dac;
- }
-
- /* add line-in as output */
- nid = check_line_out_switch(codec);
- if (nid) {
- dac = get_unassigned_dac(codec, nid);
- if (dac) {
- snd_printdd("STAC: Add line-in 0x%x as output %d\n",
- nid, cfg->line_outs);
- cfg->line_out_pins[cfg->line_outs] = nid;
- cfg->line_outs++;
- spec->line_switch = nid;
- add_spec_dacs(spec, dac);
- }
- }
- /* add mic as output */
- nid = check_mic_out_switch(codec, &dac);
- if (nid && dac) {
- snd_printdd("STAC: Add mic-in 0x%x as output %d\n",
- nid, cfg->line_outs);
- cfg->line_out_pins[cfg->line_outs] = nid;
- cfg->line_outs++;
- spec->mic_switch = nid;
- add_spec_dacs(spec, dac);
- }
-
- snd_printd("stac92xx: dac_nids=%d (0x%x/0x%x/0x%x/0x%x/0x%x)\n",
- spec->multiout.num_dacs,
- spec->multiout.dac_nids[0],
- spec->multiout.dac_nids[1],
- spec->multiout.dac_nids[2],
- spec->multiout.dac_nids[3],
- spec->multiout.dac_nids[4]);
-
- return 0;
-}
-
-/* create volume control/switch for the given prefx type */
-static int create_controls_idx(struct hda_codec *codec, const char *pfx,
- int idx, hda_nid_t nid, int chs)
-{
- struct sigmatel_spec *spec = codec->spec;
- char name[32];
- int err;
-
- if (!spec->check_volume_offset) {
- unsigned int caps, step, nums, db_scale;
- caps = query_amp_caps(codec, nid, HDA_OUTPUT);
- step = (caps & AC_AMPCAP_STEP_SIZE) >>
- AC_AMPCAP_STEP_SIZE_SHIFT;
- step = (step + 1) * 25; /* in .01dB unit */
- nums = (caps & AC_AMPCAP_NUM_STEPS) >>
- AC_AMPCAP_NUM_STEPS_SHIFT;
- db_scale = nums * step;
- /* if dB scale is over -64dB, and finer enough,
- * let's reduce it to half
- */
- if (db_scale > 6400 && nums >= 0x1f)
- spec->volume_offset = nums / 2;
- spec->check_volume_offset = 1;
- }
-
- sprintf(name, "%s Playback Volume", pfx);
- err = stac92xx_add_control_idx(spec, STAC_CTL_WIDGET_VOL, idx, name,
- HDA_COMPOSE_AMP_VAL_OFS(nid, chs, 0, HDA_OUTPUT,
- spec->volume_offset));
- if (err < 0)
- return err;
- sprintf(name, "%s Playback Switch", pfx);
- err = stac92xx_add_control_idx(spec, STAC_CTL_WIDGET_MUTE, idx, name,
- HDA_COMPOSE_AMP_VAL(nid, chs, 0, HDA_OUTPUT));
- if (err < 0)
- return err;
- return 0;
-}
-
-#define create_controls(codec, pfx, nid, chs) \
- create_controls_idx(codec, pfx, 0, nid, chs)
-
-static int add_spec_dacs(struct sigmatel_spec *spec, hda_nid_t nid)
-{
- if (spec->multiout.num_dacs > 4) {
- printk(KERN_WARNING "stac92xx: No space for DAC 0x%x\n", nid);
- return 1;
- } else {
- spec->multiout.dac_nids[spec->multiout.num_dacs] = nid;
- spec->multiout.num_dacs++;
- }
- return 0;
-}
-
-static int add_spec_extra_dacs(struct sigmatel_spec *spec, hda_nid_t nid)
-{
- int i;
- for (i = 0; i < ARRAY_SIZE(spec->multiout.extra_out_nid); i++) {
- if (!spec->multiout.extra_out_nid[i]) {
- spec->multiout.extra_out_nid[i] = nid;
- return 0;
- }
- }
- printk(KERN_WARNING "stac92xx: No space for extra DAC 0x%x\n", nid);
- return 1;
-}
-
-/* Create output controls
- * The mixer elements are named depending on the given type (AUTO_PIN_XXX_OUT)
- */
-static int create_multi_out_ctls(struct hda_codec *codec, int num_outs,
- const hda_nid_t *pins,
- const hda_nid_t *dac_nids,
- int type)
-{
- struct sigmatel_spec *spec = codec->spec;
- static const char * const chname[4] = {
- "Front", "Surround", NULL /*CLFE*/, "Side"
- };
- hda_nid_t nid;
- int i, err;
- unsigned int wid_caps;
-
- for (i = 0; i < num_outs && i < ARRAY_SIZE(chname); i++) {
- if (type == AUTO_PIN_HP_OUT && !spec->hp_detect) {
- wid_caps = get_wcaps(codec, pins[i]);
- if (wid_caps & AC_WCAP_UNSOL_CAP)
- spec->hp_detect = 1;
- }
- nid = dac_nids[i];
- if (!nid)
- continue;
- if (type != AUTO_PIN_HP_OUT && i == 2) {
- /* Center/LFE */
- err = create_controls(codec, "Center", nid, 1);
- if (err < 0)
- return err;
- err = create_controls(codec, "LFE", nid, 2);
- if (err < 0)
- return err;
-
- wid_caps = get_wcaps(codec, nid);
-
- if (wid_caps & AC_WCAP_LR_SWAP) {
- err = stac92xx_add_control(spec,
- STAC_CTL_WIDGET_CLFE_SWITCH,
- "Swap Center/LFE Playback Switch", nid);
-
- if (err < 0)
- return err;
- }
-
- } else {
- const char *name;
- int idx;
- switch (type) {
- case AUTO_PIN_HP_OUT:
- name = "Headphone";
- idx = i;
- break;
- case AUTO_PIN_SPEAKER_OUT:
- name = "Speaker";
- idx = i;
- break;
- default:
- name = chname[i];
- idx = 0;
- break;
- }
- err = create_controls_idx(codec, name, idx, nid, 3);
- if (err < 0)
- return err;
- }
- }
- return 0;
-}
-
-static int stac92xx_add_capvol_ctls(struct hda_codec *codec, unsigned long vol,
- unsigned long sw, int idx)
-{
- int err;
- err = stac92xx_add_control_idx(codec->spec, STAC_CTL_WIDGET_VOL, idx,
- "Capture Volume", vol);
- if (err < 0)
- return err;
- err = stac92xx_add_control_idx(codec->spec, STAC_CTL_WIDGET_MUTE, idx,
- "Capture Switch", sw);
- if (err < 0)
- return err;
- return 0;
-}
-
-/* add playback controls from the parsed DAC table */
-static int stac92xx_auto_create_multi_out_ctls(struct hda_codec *codec,
- const struct auto_pin_cfg *cfg)
-{
- struct sigmatel_spec *spec = codec->spec;
- hda_nid_t nid;
- int err;
- int idx;
-
- err = create_multi_out_ctls(codec, cfg->line_outs, cfg->line_out_pins,
- spec->multiout.dac_nids,
- cfg->line_out_type);
- if (err < 0)
- return err;
-
- if (cfg->hp_outs > 1 && cfg->line_out_type == AUTO_PIN_LINE_OUT) {
- err = stac92xx_add_control(spec,
- STAC_CTL_WIDGET_HP_SWITCH,
- "Headphone as Line Out Switch",
- cfg->hp_pins[cfg->hp_outs - 1]);
- if (err < 0)
- return err;
- }
-
- for (idx = 0; idx < cfg->num_inputs; idx++) {
- if (cfg->inputs[idx].type > AUTO_PIN_LINE_IN)
- break;
- nid = cfg->inputs[idx].pin;
- err = stac92xx_add_jack_mode_control(codec, nid, idx);
- if (err < 0)
- return err;
- }
-
- return 0;
-}
-/* add playback controls for Speaker and HP outputs */
-static int stac92xx_auto_create_hp_ctls(struct hda_codec *codec,
- struct auto_pin_cfg *cfg)
+static int stac_parse_auto_config(struct hda_codec *codec)
{
struct sigmatel_spec *spec = codec->spec;
int err;
+ int flags = 0;
- err = create_multi_out_ctls(codec, cfg->hp_outs, cfg->hp_pins,
- spec->hp_dacs, AUTO_PIN_HP_OUT);
- if (err < 0)
- return err;
+ if (spec->headset_jack)
+ flags |= HDA_PINCFG_HEADSET_MIC;
- err = create_multi_out_ctls(codec, cfg->speaker_outs, cfg->speaker_pins,
- spec->speaker_dacs, AUTO_PIN_SPEAKER_OUT);
+ err = snd_hda_parse_pin_defcfg(codec, &spec->gen.autocfg, NULL, flags);
if (err < 0)
return err;
- return 0;
-}
-
-/* labels for mono mux outputs */
-static const char * const stac92xx_mono_labels[4] = {
- "DAC0", "DAC1", "Mixer", "DAC2"
-};
-
-/* create mono mux for mono out on capable codecs */
-static int stac92xx_auto_create_mono_output_ctls(struct hda_codec *codec)
-{
- struct sigmatel_spec *spec = codec->spec;
- struct hda_input_mux *mono_mux = &spec->private_mono_mux;
- int i, num_cons;
- hda_nid_t con_lst[ARRAY_SIZE(stac92xx_mono_labels)];
-
- num_cons = snd_hda_get_connections(codec,
- spec->mono_nid,
- con_lst,
- HDA_MAX_NUM_INPUTS);
- if (num_cons <= 0 || num_cons > ARRAY_SIZE(stac92xx_mono_labels))
- return -EINVAL;
-
- for (i = 0; i < num_cons; i++)
- snd_hda_add_imux_item(mono_mux, stac92xx_mono_labels[i], i,
- NULL);
-
- return stac92xx_add_control(spec, STAC_CTL_WIDGET_MONO_MUX,
- "Mono Mux", spec->mono_nid);
-}
-
-/* create PC beep volume controls */
-static int stac92xx_auto_create_beep_ctls(struct hda_codec *codec,
- hda_nid_t nid)
-{
- struct sigmatel_spec *spec = codec->spec;
- u32 caps = query_amp_caps(codec, nid, HDA_OUTPUT);
- int err, type = STAC_CTL_WIDGET_MUTE_BEEP;
-
- if (spec->anabeep_nid == nid)
- type = STAC_CTL_WIDGET_MUTE;
-
- /* check for mute support for the the amp */
- if ((caps & AC_AMPCAP_MUTE) >> AC_AMPCAP_MUTE_SHIFT) {
- err = stac92xx_add_control(spec, type,
- "Beep Playback Switch",
- HDA_COMPOSE_AMP_VAL(nid, 1, 0, HDA_OUTPUT));
- if (err < 0)
- return err;
- }
-
- /* check to see if there is volume support for the amp */
- if ((caps & AC_AMPCAP_NUM_STEPS) >> AC_AMPCAP_NUM_STEPS_SHIFT) {
- err = stac92xx_add_control(spec, STAC_CTL_WIDGET_VOL,
- "Beep Playback Volume",
- HDA_COMPOSE_AMP_VAL(nid, 1, 0, HDA_OUTPUT));
- if (err < 0)
- return err;
- }
- return 0;
-}
-
-#ifdef CONFIG_SND_HDA_INPUT_BEEP
-#define stac92xx_dig_beep_switch_info snd_ctl_boolean_mono_info
-
-static int stac92xx_dig_beep_switch_get(struct snd_kcontrol *kcontrol,
- struct snd_ctl_elem_value *ucontrol)
-{
- struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
- ucontrol->value.integer.value[0] = codec->beep->enabled;
- return 0;
-}
-
-static int stac92xx_dig_beep_switch_put(struct snd_kcontrol *kcontrol,
- struct snd_ctl_elem_value *ucontrol)
-{
- struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
- return snd_hda_enable_beep_device(codec, ucontrol->value.integer.value[0]);
-}
-
-static struct snd_kcontrol_new stac92xx_dig_beep_ctrl = {
- .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
- .info = stac92xx_dig_beep_switch_info,
- .get = stac92xx_dig_beep_switch_get,
- .put = stac92xx_dig_beep_switch_put,
-};
-
-static int stac92xx_beep_switch_ctl(struct hda_codec *codec)
-{
- return stac92xx_add_control_temp(codec->spec, &stac92xx_dig_beep_ctrl,
- 0, "Beep Playback Switch", 0);
-}
-#endif
-
-static int stac92xx_auto_create_mux_input_ctls(struct hda_codec *codec)
-{
- struct sigmatel_spec *spec = codec->spec;
- int i, j, err = 0;
-
- for (i = 0; i < spec->num_muxes; i++) {
- hda_nid_t nid;
- unsigned int wcaps;
- unsigned long val;
-
- nid = spec->mux_nids[i];
- wcaps = get_wcaps(codec, nid);
- if (!(wcaps & AC_WCAP_OUT_AMP))
- continue;
-
- /* check whether already the same control was created as
- * normal Capture Volume.
- */
- val = HDA_COMPOSE_AMP_VAL(nid, 3, 0, HDA_OUTPUT);
- for (j = 0; j < spec->num_caps; j++) {
- if (spec->capvols[j] == val)
- break;
- }
- if (j < spec->num_caps)
- continue;
-
- err = stac92xx_add_control_idx(spec, STAC_CTL_WIDGET_VOL, i,
- "Mux Capture Volume", val);
- if (err < 0)
- return err;
- }
- return 0;
-};
-
-static const char * const stac92xx_spdif_labels[3] = {
- "Digital Playback", "Analog Mux 1", "Analog Mux 2",
-};
-
-static int stac92xx_auto_create_spdif_mux_ctls(struct hda_codec *codec)
-{
- struct sigmatel_spec *spec = codec->spec;
- struct hda_input_mux *spdif_mux = &spec->private_smux;
- const char * const *labels = spec->spdif_labels;
- int i, num_cons;
- hda_nid_t con_lst[HDA_MAX_NUM_INPUTS];
-
- num_cons = snd_hda_get_connections(codec,
- spec->smux_nids[0],
- con_lst,
- HDA_MAX_NUM_INPUTS);
- if (num_cons <= 0)
- return -EINVAL;
-
- if (!labels)
- labels = stac92xx_spdif_labels;
-
- for (i = 0; i < num_cons; i++)
- snd_hda_add_imux_item(spdif_mux, labels[i], i, NULL);
-
- return 0;
-}
-
-/* labels for dmic mux inputs */
-static const char * const stac92xx_dmic_labels[5] = {
- "Analog Inputs", "Digital Mic 1", "Digital Mic 2",
- "Digital Mic 3", "Digital Mic 4"
-};
-
-static hda_nid_t get_connected_node(struct hda_codec *codec, hda_nid_t mux,
- int idx)
-{
- hda_nid_t conn[HDA_MAX_NUM_INPUTS];
- int nums;
- nums = snd_hda_get_connections(codec, mux, conn, ARRAY_SIZE(conn));
- if (idx >= 0 && idx < nums)
- return conn[idx];
- return 0;
-}
-
-static int get_connection_index(struct hda_codec *codec, hda_nid_t mux,
- hda_nid_t nid)
-{
- hda_nid_t conn[HDA_MAX_NUM_INPUTS];
- int i, nums;
-
- if (!(get_wcaps(codec, mux) & AC_WCAP_CONN_LIST))
- return -1;
-
- nums = snd_hda_get_connections(codec, mux, conn, ARRAY_SIZE(conn));
- for (i = 0; i < nums; i++)
- if (conn[i] == nid)
- return i;
-
- for (i = 0; i < nums; i++) {
- unsigned int wid_caps = get_wcaps(codec, conn[i]);
- unsigned int wid_type = get_wcaps_type(wid_caps);
-
- if (wid_type != AC_WID_PIN && wid_type != AC_WID_AUD_MIX)
- if (get_connection_index(codec, conn[i], nid) >= 0)
- return i;
- }
- return -1;
-}
+ /* add hooks */
+ spec->gen.pcm_playback_hook = stac_playback_pcm_hook;
+ spec->gen.pcm_capture_hook = stac_capture_pcm_hook;
-/* create a volume assigned to the given pin (only if supported) */
-/* return 1 if the volume control is created */
-static int create_elem_capture_vol(struct hda_codec *codec, hda_nid_t nid,
- const char *label, int idx, int direction)
-{
- unsigned int caps, nums;
- char name[32];
- int err;
+ spec->gen.automute_hook = stac_update_outputs;
+ spec->gen.hp_automute_hook = stac_hp_automute;
+ spec->gen.line_automute_hook = stac_line_automute;
+ spec->gen.mic_autoswitch_hook = stac_mic_autoswitch;
- if (direction == HDA_OUTPUT)
- caps = AC_WCAP_OUT_AMP;
- else
- caps = AC_WCAP_IN_AMP;
- if (!(get_wcaps(codec, nid) & caps))
- return 0;
- caps = query_amp_caps(codec, nid, direction);
- nums = (caps & AC_AMPCAP_NUM_STEPS) >> AC_AMPCAP_NUM_STEPS_SHIFT;
- if (!nums)
- return 0;
- snprintf(name, sizeof(name), "%s Capture Volume", label);
- err = stac92xx_add_control_idx(codec->spec, STAC_CTL_WIDGET_VOL, idx, name,
- HDA_COMPOSE_AMP_VAL(nid, 3, 0, direction));
+ err = snd_hda_gen_parse_auto_config(codec, &spec->gen.autocfg);
if (err < 0)
return err;
- return 1;
-}
-
-/* create playback/capture controls for input pins on dmic capable codecs */
-static int stac92xx_auto_create_dmic_input_ctls(struct hda_codec *codec,
- const struct auto_pin_cfg *cfg)
-{
- struct sigmatel_spec *spec = codec->spec;
- struct hda_input_mux *imux = &spec->private_imux;
- struct hda_input_mux *dimux = &spec->private_dimux;
- int err, i;
- unsigned int def_conf;
-
- snd_hda_add_imux_item(dimux, stac92xx_dmic_labels[0], 0, NULL);
-
- for (i = 0; i < spec->num_dmics; i++) {
- hda_nid_t nid;
- int index, type_idx;
- const char *label;
-
- nid = spec->dmic_nids[i];
- if (get_wcaps_type(get_wcaps(codec, nid)) != AC_WID_PIN)
- continue;
- def_conf = snd_hda_codec_get_pincfg(codec, nid);
- if (get_defcfg_connect(def_conf) == AC_JACK_PORT_NONE)
- continue;
-
- index = get_connection_index(codec, spec->dmux_nids[0], nid);
- if (index < 0)
- continue;
-
- label = hda_get_input_pin_label(codec, nid, 1);
- snd_hda_add_imux_item(dimux, label, index, &type_idx);
- if (snd_hda_get_bool_hint(codec, "separate_dmux") != 1)
- snd_hda_add_imux_item(imux, label, index, &type_idx);
- err = create_elem_capture_vol(codec, nid, label, type_idx,
- HDA_INPUT);
- if (err < 0)
- return err;
- if (!err) {
- err = create_elem_capture_vol(codec, nid, label,
- type_idx, HDA_OUTPUT);
- if (err < 0)
- return err;
- if (!err) {
- nid = get_connected_node(codec,
- spec->dmux_nids[0], index);
- if (nid)
- err = create_elem_capture_vol(codec,
- nid, label,
- type_idx, HDA_INPUT);
- if (err < 0)
- return err;
- }
- }
- }
-
- return 0;
-}
-
-static int check_mic_pin(struct hda_codec *codec, hda_nid_t nid,
- hda_nid_t *fixed, hda_nid_t *ext, hda_nid_t *dock)
-{
- unsigned int cfg;
-
- if (!nid)
- return 0;
- cfg = snd_hda_codec_get_pincfg(codec, nid);
- switch (snd_hda_get_input_pin_attr(cfg)) {
- case INPUT_PIN_ATTR_INT:
- if (*fixed)
- return 1; /* already occupied */
- *fixed = nid;
- break;
- case INPUT_PIN_ATTR_UNUSED:
- break;
- case INPUT_PIN_ATTR_DOCK:
- if (*dock)
- return 1; /* already occupied */
- *dock = nid;
- break;
- default:
- if (*ext)
- return 1; /* already occupied */
- *ext = nid;
- break;
- }
- return 0;
-}
-
-static int set_mic_route(struct hda_codec *codec,
- struct sigmatel_mic_route *mic,
- hda_nid_t pin)
-{
- struct sigmatel_spec *spec = codec->spec;
- struct auto_pin_cfg *cfg = &spec->autocfg;
- int i;
-
- mic->pin = pin;
- if (pin == 0)
- return 0;
- for (i = 0; i < cfg->num_inputs; i++) {
- if (pin == cfg->inputs[i].pin)
- break;
- }
- if (i < cfg->num_inputs && cfg->inputs[i].type == AUTO_PIN_MIC) {
- /* analog pin */
- i = get_connection_index(codec, spec->mux_nids[0], pin);
- if (i < 0)
- return -1;
- mic->mux_idx = i;
- mic->dmux_idx = -1;
- if (spec->dmux_nids)
- mic->dmux_idx = get_connection_index(codec,
- spec->dmux_nids[0],
- spec->mux_nids[0]);
- } else if (spec->dmux_nids) {
- /* digital pin */
- i = get_connection_index(codec, spec->dmux_nids[0], pin);
- if (i < 0)
- return -1;
- mic->dmux_idx = i;
- mic->mux_idx = -1;
- if (spec->mux_nids)
- mic->mux_idx = get_connection_index(codec,
- spec->mux_nids[0],
- spec->dmux_nids[0]);
- }
- return 0;
-}
-
-/* return non-zero if the device is for automatic mic switch */
-static int stac_check_auto_mic(struct hda_codec *codec)
-{
- struct sigmatel_spec *spec = codec->spec;
- struct auto_pin_cfg *cfg = &spec->autocfg;
- hda_nid_t fixed, ext, dock;
- int i;
-
- for (i = 0; i < cfg->num_inputs; i++) {
- if (cfg->inputs[i].type >= AUTO_PIN_LINE_IN)
- return 0; /* must be exclusively mics */
- }
- fixed = ext = dock = 0;
- for (i = 0; i < cfg->num_inputs; i++)
- if (check_mic_pin(codec, cfg->inputs[i].pin,
- &fixed, &ext, &dock))
- return 0;
- for (i = 0; i < spec->num_dmics; i++)
- if (check_mic_pin(codec, spec->dmic_nids[i],
- &fixed, &ext, &dock))
- return 0;
- if (!fixed || (!ext && !dock))
- return 0; /* no input to switch */
- if (!(get_wcaps(codec, ext) & AC_WCAP_UNSOL_CAP))
- return 0; /* no unsol support */
- if (set_mic_route(codec, &spec->ext_mic, ext) ||
- set_mic_route(codec, &spec->int_mic, fixed) ||
- set_mic_route(codec, &spec->dock_mic, dock))
- return 0; /* something is wrong */
- return 1;
-}
-
-/* create playback/capture controls for input pins */
-static int stac92xx_auto_create_analog_input_ctls(struct hda_codec *codec, const struct auto_pin_cfg *cfg)
-{
- struct sigmatel_spec *spec = codec->spec;
- struct hda_input_mux *imux = &spec->private_imux;
- int i, j;
- const char *label;
-
- for (i = 0; i < cfg->num_inputs; i++) {
- hda_nid_t nid = cfg->inputs[i].pin;
- int index, err, type_idx;
-
- index = -1;
- for (j = 0; j < spec->num_muxes; j++) {
- index = get_connection_index(codec, spec->mux_nids[j],
- nid);
- if (index >= 0)
- break;
- }
- if (index < 0)
- continue;
-
- label = hda_get_autocfg_input_label(codec, cfg, i);
- snd_hda_add_imux_item(imux, label, index, &type_idx);
-
- err = create_elem_capture_vol(codec, nid,
- label, type_idx,
- HDA_INPUT);
- if (err < 0)
- return err;
- }
- spec->num_analog_muxes = imux->num_items;
-
- if (imux->num_items) {
- /*
- * Set the current input for the muxes.
- * The STAC9221 has two input muxes with identical source
- * NID lists. Hopefully this won't get confused.
- */
- for (i = 0; i < spec->num_muxes; i++) {
- snd_hda_codec_write_cache(codec, spec->mux_nids[i], 0,
- AC_VERB_SET_CONNECT_SEL,
- imux->items[0].index);
- }
- }
-
- return 0;
-}
-
-static void stac92xx_auto_init_multi_out(struct hda_codec *codec)
-{
- struct sigmatel_spec *spec = codec->spec;
- int i;
-
- for (i = 0; i < spec->autocfg.line_outs; i++) {
- hda_nid_t nid = spec->autocfg.line_out_pins[i];
- stac92xx_auto_set_pinctl(codec, nid, AC_PINCTL_OUT_EN);
- }
-}
-
-static void stac92xx_auto_init_hp_out(struct hda_codec *codec)
-{
- struct sigmatel_spec *spec = codec->spec;
- int i;
-
- for (i = 0; i < spec->autocfg.hp_outs; i++) {
- hda_nid_t pin;
- pin = spec->autocfg.hp_pins[i];
- if (pin) /* connect to front */
- stac92xx_auto_set_pinctl(codec, pin, AC_PINCTL_OUT_EN | AC_PINCTL_HP_EN);
- }
- for (i = 0; i < spec->autocfg.speaker_outs; i++) {
- hda_nid_t pin;
- pin = spec->autocfg.speaker_pins[i];
- if (pin) /* connect to front */
- stac92xx_auto_set_pinctl(codec, pin, AC_PINCTL_OUT_EN);
- }
-}
-
-static int is_dual_headphones(struct hda_codec *codec)
-{
- struct sigmatel_spec *spec = codec->spec;
- int i, valid_hps;
-
- if (spec->autocfg.line_out_type != AUTO_PIN_SPEAKER_OUT ||
- spec->autocfg.hp_outs <= 1)
- return 0;
- valid_hps = 0;
- for (i = 0; i < spec->autocfg.hp_outs; i++) {
- hda_nid_t nid = spec->autocfg.hp_pins[i];
- unsigned int cfg = snd_hda_codec_get_pincfg(codec, nid);
- if (get_defcfg_location(cfg) & AC_JACK_LOC_SEPARATE)
- continue;
- valid_hps++;
- }
- return (valid_hps > 1);
-}
-
-
-static int stac92xx_parse_auto_config(struct hda_codec *codec, hda_nid_t dig_out, hda_nid_t dig_in)
-{
- struct sigmatel_spec *spec = codec->spec;
- int hp_swap = 0;
- int i, err;
-
- if ((err = snd_hda_parse_pin_def_config(codec,
- &spec->autocfg,
- spec->dmic_nids)) < 0)
- return err;
- if (! spec->autocfg.line_outs)
- return 0; /* can't find valid pin config */
-
- /* If we have no real line-out pin and multiple hp-outs, HPs should
- * be set up as multi-channel outputs.
- */
- if (is_dual_headphones(codec)) {
- /* Copy hp_outs to line_outs, backup line_outs in
- * speaker_outs so that the following routines can handle
- * HP pins as primary outputs.
- */
- snd_printdd("stac92xx: Enabling multi-HPs workaround\n");
- memcpy(spec->autocfg.speaker_pins, spec->autocfg.line_out_pins,
- sizeof(spec->autocfg.line_out_pins));
- spec->autocfg.speaker_outs = spec->autocfg.line_outs;
- memcpy(spec->autocfg.line_out_pins, spec->autocfg.hp_pins,
- sizeof(spec->autocfg.hp_pins));
- spec->autocfg.line_outs = spec->autocfg.hp_outs;
- spec->autocfg.line_out_type = AUTO_PIN_HP_OUT;
- spec->autocfg.hp_outs = 0;
- hp_swap = 1;
- }
- if (spec->autocfg.mono_out_pin) {
- int dir = get_wcaps(codec, spec->autocfg.mono_out_pin) &
- (AC_WCAP_OUT_AMP | AC_WCAP_IN_AMP);
- u32 caps = query_amp_caps(codec,
- spec->autocfg.mono_out_pin, dir);
- hda_nid_t conn_list[1];
-
- /* get the mixer node and then the mono mux if it exists */
- if (snd_hda_get_connections(codec,
- spec->autocfg.mono_out_pin, conn_list, 1) &&
- snd_hda_get_connections(codec, conn_list[0],
- conn_list, 1) > 0) {
-
- int wcaps = get_wcaps(codec, conn_list[0]);
- int wid_type = get_wcaps_type(wcaps);
- /* LR swap check, some stac925x have a mux that
- * changes the DACs output path instead of the
- * mono-mux path.
- */
- if (wid_type == AC_WID_AUD_SEL &&
- !(wcaps & AC_WCAP_LR_SWAP))
- spec->mono_nid = conn_list[0];
- }
- if (dir) {
- hda_nid_t nid = spec->autocfg.mono_out_pin;
-
- /* most mono outs have a least a mute/unmute switch */
- dir = (dir & AC_WCAP_OUT_AMP) ? HDA_OUTPUT : HDA_INPUT;
- err = stac92xx_add_control(spec, STAC_CTL_WIDGET_MUTE,
- "Mono Playback Switch",
- HDA_COMPOSE_AMP_VAL(nid, 1, 0, dir));
- if (err < 0)
- return err;
- /* check for volume support for the amp */
- if ((caps & AC_AMPCAP_NUM_STEPS)
- >> AC_AMPCAP_NUM_STEPS_SHIFT) {
- err = stac92xx_add_control(spec,
- STAC_CTL_WIDGET_VOL,
- "Mono Playback Volume",
- HDA_COMPOSE_AMP_VAL(nid, 1, 0, dir));
- if (err < 0)
- return err;
- }
- }
-
- stac92xx_auto_set_pinctl(codec, spec->autocfg.mono_out_pin,
- AC_PINCTL_OUT_EN);
- }
-
- if (!spec->multiout.num_dacs) {
- err = stac92xx_auto_fill_dac_nids(codec);
- if (err < 0)
- return err;
- err = stac92xx_auto_create_multi_out_ctls(codec,
- &spec->autocfg);
- if (err < 0)
- return err;
- }
+ /* minimum value is actually mute */
+ spec->gen.vmaster_tlv[3] |= TLV_DB_SCALE_MUTE;
/* setup analog beep controls */
if (spec->anabeep_nid > 0) {
- err = stac92xx_auto_create_beep_ctls(codec,
- spec->anabeep_nid);
+ err = stac_auto_create_beep_ctls(codec,
+ spec->anabeep_nid);
if (err < 0)
return err;
}
/* setup digital beep controls and input device */
#ifdef CONFIG_SND_HDA_INPUT_BEEP
- if (spec->digbeep_nid > 0) {
- hda_nid_t nid = spec->digbeep_nid;
+ if (spec->gen.beep_nid) {
+ hda_nid_t nid = spec->gen.beep_nid;
unsigned int caps;
- err = stac92xx_auto_create_beep_ctls(codec, nid);
- if (err < 0)
- return err;
- err = snd_hda_attach_beep_device(codec, nid);
+ err = stac_auto_create_beep_ctls(codec, nid);
if (err < 0)
return err;
if (codec->beep) {
@@ -3831,7 +4228,7 @@ static int stac92xx_parse_auto_config(struct hda_codec *codec, hda_nid_t dig_out
/* if no beep switch is available, make its own one */
caps = query_amp_caps(codec, nid, HDA_OUTPUT);
if (!(caps & AC_AMPCAP_MUTE)) {
- err = stac92xx_beep_switch_ctl(codec);
+ err = stac_beep_switch_ctl(codec);
if (err < 0)
return err;
}
@@ -3839,563 +4236,66 @@ static int stac92xx_parse_auto_config(struct hda_codec *codec, hda_nid_t dig_out
}
#endif
- err = stac92xx_auto_create_hp_ctls(codec, &spec->autocfg);
- if (err < 0)
- return err;
-
- /* All output parsing done, now restore the swapped hp pins */
- if (hp_swap) {
- memcpy(spec->autocfg.hp_pins, spec->autocfg.line_out_pins,
- sizeof(spec->autocfg.hp_pins));
- spec->autocfg.hp_outs = spec->autocfg.line_outs;
- spec->autocfg.line_out_type = AUTO_PIN_HP_OUT;
- spec->autocfg.line_outs = 0;
- }
-
- if (stac_check_auto_mic(codec)) {
- spec->auto_mic = 1;
- /* only one capture for auto-mic */
- spec->num_adcs = 1;
- spec->num_caps = 1;
- spec->num_muxes = 1;
- }
+ if (spec->gpio_led)
+ spec->gen.vmaster_mute.hook = stac_vmaster_hook;
- for (i = 0; i < spec->num_caps; i++) {
- err = stac92xx_add_capvol_ctls(codec, spec->capvols[i],
- spec->capsws[i], i);
- if (err < 0)
- return err;
+ if (spec->aloopback_ctl &&
+ snd_hda_get_bool_hint(codec, "loopback") == 1) {
+ if (!snd_hda_gen_add_kctl(&spec->gen, NULL, spec->aloopback_ctl))
+ return -ENOMEM;
}
- err = stac92xx_auto_create_analog_input_ctls(codec, &spec->autocfg);
- if (err < 0)
- return err;
-
- if (spec->mono_nid > 0) {
- err = stac92xx_auto_create_mono_output_ctls(codec);
- if (err < 0)
- return err;
- }
- if (spec->num_dmics > 0 && !spec->dinput_mux)
- if ((err = stac92xx_auto_create_dmic_input_ctls(codec,
- &spec->autocfg)) < 0)
- return err;
- if (spec->num_muxes > 0) {
- err = stac92xx_auto_create_mux_input_ctls(codec);
- if (err < 0)
- return err;
- }
- if (spec->num_smuxes > 0) {
- err = stac92xx_auto_create_spdif_mux_ctls(codec);
+ if (spec->have_spdif_mux) {
+ err = stac_create_spdif_mux_ctls(codec);
if (err < 0)
return err;
}
- err = stac92xx_add_input_source(spec);
- if (err < 0)
- return err;
-
- spec->multiout.max_channels = spec->multiout.num_dacs * 2;
- if (spec->multiout.max_channels > 2)
- spec->surr_switch = 1;
-
- if (spec->autocfg.dig_outs)
- spec->multiout.dig_out_nid = dig_out;
- if (dig_in && spec->autocfg.dig_in_pin)
- spec->dig_in_nid = dig_in;
-
- if (spec->kctls.list)
- spec->mixers[spec->num_mixers++] = spec->kctls.list;
-
- spec->input_mux = &spec->private_imux;
- if (!spec->dinput_mux)
- spec->dinput_mux = &spec->private_dimux;
- spec->sinput_mux = &spec->private_smux;
- spec->mono_mux = &spec->private_mono_mux;
- return 1;
-}
-
-/* add playback controls for HP output */
-static int stac9200_auto_create_hp_ctls(struct hda_codec *codec,
- struct auto_pin_cfg *cfg)
-{
- struct sigmatel_spec *spec = codec->spec;
- hda_nid_t pin = cfg->hp_pins[0];
- unsigned int wid_caps;
-
- if (! pin)
- return 0;
-
- wid_caps = get_wcaps(codec, pin);
- if (wid_caps & AC_WCAP_UNSOL_CAP)
- spec->hp_detect = 1;
+ stac_init_power_map(codec);
return 0;
}
-/* add playback controls for LFE output */
-static int stac9200_auto_create_lfe_ctls(struct hda_codec *codec,
- struct auto_pin_cfg *cfg)
-{
- struct sigmatel_spec *spec = codec->spec;
- int err;
- hda_nid_t lfe_pin = 0x0;
- int i;
-
- /*
- * search speaker outs and line outs for a mono speaker pin
- * with an amp. If one is found, add LFE controls
- * for it.
- */
- for (i = 0; i < spec->autocfg.speaker_outs && lfe_pin == 0x0; i++) {
- hda_nid_t pin = spec->autocfg.speaker_pins[i];
- unsigned int wcaps = get_wcaps(codec, pin);
- wcaps &= (AC_WCAP_STEREO | AC_WCAP_OUT_AMP);
- if (wcaps == AC_WCAP_OUT_AMP)
- /* found a mono speaker with an amp, must be lfe */
- lfe_pin = pin;
- }
-
- /* if speaker_outs is 0, then speakers may be in line_outs */
- if (lfe_pin == 0 && spec->autocfg.speaker_outs == 0) {
- for (i = 0; i < spec->autocfg.line_outs && lfe_pin == 0x0; i++) {
- hda_nid_t pin = spec->autocfg.line_out_pins[i];
- unsigned int defcfg;
- defcfg = snd_hda_codec_get_pincfg(codec, pin);
- if (get_defcfg_device(defcfg) == AC_JACK_SPEAKER) {
- unsigned int wcaps = get_wcaps(codec, pin);
- wcaps &= (AC_WCAP_STEREO | AC_WCAP_OUT_AMP);
- if (wcaps == AC_WCAP_OUT_AMP)
- /* found a mono speaker with an amp,
- must be lfe */
- lfe_pin = pin;
- }
- }
- }
-
- if (lfe_pin) {
- err = create_controls(codec, "LFE", lfe_pin, 1);
- if (err < 0)
- return err;
- }
-
- return 0;
-}
-static int stac9200_parse_auto_config(struct hda_codec *codec)
+static int stac_init(struct hda_codec *codec)
{
struct sigmatel_spec *spec = codec->spec;
- int err;
-
- if ((err = snd_hda_parse_pin_def_config(codec, &spec->autocfg, NULL)) < 0)
- return err;
-
- if ((err = stac92xx_auto_create_analog_input_ctls(codec, &spec->autocfg)) < 0)
- return err;
-
- if ((err = stac9200_auto_create_hp_ctls(codec, &spec->autocfg)) < 0)
- return err;
-
- if ((err = stac9200_auto_create_lfe_ctls(codec, &spec->autocfg)) < 0)
- return err;
-
- if (spec->num_muxes > 0) {
- err = stac92xx_auto_create_mux_input_ctls(codec);
- if (err < 0)
- return err;
- }
-
- err = stac92xx_add_input_source(spec);
- if (err < 0)
- return err;
-
- if (spec->autocfg.dig_outs)
- spec->multiout.dig_out_nid = 0x05;
- if (spec->autocfg.dig_in_pin)
- spec->dig_in_nid = 0x04;
-
- if (spec->kctls.list)
- spec->mixers[spec->num_mixers++] = spec->kctls.list;
-
- spec->input_mux = &spec->private_imux;
- spec->dinput_mux = &spec->private_dimux;
-
- return 1;
-}
-
-/*
- * Early 2006 Intel Macintoshes with STAC9220X5 codecs seem to have a
- * funky external mute control using GPIO pins.
- */
-
-static void stac_gpio_set(struct hda_codec *codec, unsigned int mask,
- unsigned int dir_mask, unsigned int data)
-{
- unsigned int gpiostate, gpiomask, gpiodir;
-
- gpiostate = snd_hda_codec_read(codec, codec->afg, 0,
- AC_VERB_GET_GPIO_DATA, 0);
- gpiostate = (gpiostate & ~dir_mask) | (data & dir_mask);
-
- gpiomask = snd_hda_codec_read(codec, codec->afg, 0,
- AC_VERB_GET_GPIO_MASK, 0);
- gpiomask |= mask;
-
- gpiodir = snd_hda_codec_read(codec, codec->afg, 0,
- AC_VERB_GET_GPIO_DIRECTION, 0);
- gpiodir |= dir_mask;
-
- /* Configure GPIOx as CMOS */
- snd_hda_codec_write(codec, codec->afg, 0, 0x7e7, 0);
-
- snd_hda_codec_write(codec, codec->afg, 0,
- AC_VERB_SET_GPIO_MASK, gpiomask);
- snd_hda_codec_read(codec, codec->afg, 0,
- AC_VERB_SET_GPIO_DIRECTION, gpiodir); /* sync */
-
- msleep(1);
-
- snd_hda_codec_read(codec, codec->afg, 0,
- AC_VERB_SET_GPIO_DATA, gpiostate); /* sync */
-}
-
-static int stac92xx_add_jack(struct hda_codec *codec,
- hda_nid_t nid, int type)
-{
-#ifdef CONFIG_SND_HDA_INPUT_JACK
- int def_conf = snd_hda_codec_get_pincfg(codec, nid);
- int connectivity = get_defcfg_connect(def_conf);
- char name[32];
- int err;
-
- if (connectivity && connectivity != AC_JACK_PORT_FIXED)
- return 0;
-
- snprintf(name, sizeof(name), "%s at %s %s Jack",
- snd_hda_get_jack_type(def_conf),
- snd_hda_get_jack_connectivity(def_conf),
- snd_hda_get_jack_location(def_conf));
-
- err = snd_hda_input_jack_add(codec, nid, type, name);
- if (err < 0)
- return err;
-#endif /* CONFIG_SND_HDA_INPUT_JACK */
- return 0;
-}
-
-static int stac_add_event(struct sigmatel_spec *spec, hda_nid_t nid,
- unsigned char type, int data)
-{
- struct sigmatel_event *event;
-
- snd_array_init(&spec->events, sizeof(*event), 32);
- event = snd_array_new(&spec->events);
- if (!event)
- return -ENOMEM;
- event->nid = nid;
- event->type = type;
- event->tag = spec->events.used;
- event->data = data;
-
- return event->tag;
-}
-
-static struct sigmatel_event *stac_get_event(struct hda_codec *codec,
- hda_nid_t nid)
-{
- struct sigmatel_spec *spec = codec->spec;
- struct sigmatel_event *event = spec->events.list;
- int i;
-
- for (i = 0; i < spec->events.used; i++, event++) {
- if (event->nid == nid)
- return event;
- }
- return NULL;
-}
-
-static struct sigmatel_event *stac_get_event_from_tag(struct hda_codec *codec,
- unsigned char tag)
-{
- struct sigmatel_spec *spec = codec->spec;
- struct sigmatel_event *event = spec->events.list;
int i;
- for (i = 0; i < spec->events.used; i++, event++) {
- if (event->tag == tag)
- return event;
- }
- return NULL;
-}
-
-/* check if given nid is a valid pin and no other events are assigned
- * to it. If OK, assign the event, set the unsol flag, and returns 1.
- * Otherwise, returns zero.
- */
-static int enable_pin_detect(struct hda_codec *codec, hda_nid_t nid,
- unsigned int type)
-{
- struct sigmatel_event *event;
- int tag;
-
- if (!(get_wcaps(codec, nid) & AC_WCAP_UNSOL_CAP))
- return 0;
- event = stac_get_event(codec, nid);
- if (event) {
- if (event->type != type)
- return 0;
- tag = event->tag;
- } else {
- tag = stac_add_event(codec->spec, nid, type, 0);
- if (tag < 0)
- return 0;
- }
- snd_hda_codec_write_cache(codec, nid, 0,
- AC_VERB_SET_UNSOLICITED_ENABLE,
- AC_USRSP_EN | tag);
- return 1;
-}
-
-static int is_nid_hp_pin(struct auto_pin_cfg *cfg, hda_nid_t nid)
-{
- int i;
- for (i = 0; i < cfg->hp_outs; i++)
- if (cfg->hp_pins[i] == nid)
- return 1; /* nid is a HP-Out */
-
- return 0; /* nid is not a HP-Out */
-};
-
-static void stac92xx_power_down(struct hda_codec *codec)
-{
- struct sigmatel_spec *spec = codec->spec;
-
- /* power down inactive DACs */
- hda_nid_t *dac;
- for (dac = spec->dac_list; *dac; dac++)
- if (!check_all_dac_nids(spec, *dac))
- snd_hda_codec_write(codec, *dac, 0,
- AC_VERB_SET_POWER_STATE, AC_PWRST_D3);
-}
-
-static void stac_toggle_power_map(struct hda_codec *codec, hda_nid_t nid,
- int enable);
-
-static inline int get_int_hint(struct hda_codec *codec, const char *key,
- int *valp)
-{
- const char *p;
- p = snd_hda_get_hint(codec, key);
- if (p) {
- unsigned long val;
- if (!strict_strtoul(p, 0, &val)) {
- *valp = val;
- return 1;
- }
- }
- return 0;
-}
-
-/* override some hints from the hwdep entry */
-static void stac_store_hints(struct hda_codec *codec)
-{
- struct sigmatel_spec *spec = codec->spec;
- int val;
-
- val = snd_hda_get_bool_hint(codec, "hp_detect");
- if (val >= 0)
- spec->hp_detect = val;
- if (get_int_hint(codec, "gpio_mask", &spec->gpio_mask)) {
- spec->eapd_mask = spec->gpio_dir = spec->gpio_data =
- spec->gpio_mask;
- }
- if (get_int_hint(codec, "gpio_dir", &spec->gpio_dir))
- spec->gpio_mask &= spec->gpio_mask;
- if (get_int_hint(codec, "gpio_data", &spec->gpio_data))
- spec->gpio_dir &= spec->gpio_mask;
- if (get_int_hint(codec, "eapd_mask", &spec->eapd_mask))
- spec->eapd_mask &= spec->gpio_mask;
- if (get_int_hint(codec, "gpio_mute", &spec->gpio_mute))
- spec->gpio_mute &= spec->gpio_mask;
- val = snd_hda_get_bool_hint(codec, "eapd_switch");
- if (val >= 0)
- spec->eapd_switch = val;
- get_int_hint(codec, "gpio_led_polarity", &spec->gpio_led_polarity);
- if (get_int_hint(codec, "gpio_led", &spec->gpio_led)) {
- spec->gpio_mask |= spec->gpio_led;
- spec->gpio_dir |= spec->gpio_led;
- if (spec->gpio_led_polarity)
- spec->gpio_data |= spec->gpio_led;
- }
-}
-
-static int stac92xx_init(struct hda_codec *codec)
-{
- struct sigmatel_spec *spec = codec->spec;
- struct auto_pin_cfg *cfg = &spec->autocfg;
- unsigned int gpio;
- int i;
-
- snd_hda_sequence_write(codec, spec->init);
-
- /* power down adcs initially */
- if (spec->powerdown_adcs)
- for (i = 0; i < spec->num_adcs; i++)
- snd_hda_codec_write(codec,
- spec->adc_nids[i], 0,
- AC_VERB_SET_POWER_STATE, AC_PWRST_D3);
-
/* override some hints */
stac_store_hints(codec);
/* set up GPIO */
- gpio = spec->gpio_data;
/* turn on EAPD statically when spec->eapd_switch isn't set.
* otherwise, unsol event will turn it on/off dynamically
*/
if (!spec->eapd_switch)
- gpio |= spec->eapd_mask;
- stac_gpio_set(codec, spec->gpio_mask, spec->gpio_dir, gpio);
-
- /* set up pins */
- if (spec->hp_detect) {
- /* Enable unsolicited responses on the HP widget */
- for (i = 0; i < cfg->hp_outs; i++) {
- hda_nid_t nid = cfg->hp_pins[i];
- enable_pin_detect(codec, nid, STAC_HP_EVENT);
- }
- if (cfg->line_out_type == AUTO_PIN_LINE_OUT &&
- cfg->speaker_outs > 0) {
- /* enable pin-detect for line-outs as well */
- for (i = 0; i < cfg->line_outs; i++) {
- hda_nid_t nid = cfg->line_out_pins[i];
- enable_pin_detect(codec, nid, STAC_LO_EVENT);
- }
- }
-
- /* force to enable the first line-out; the others are set up
- * in unsol_event
- */
- stac92xx_auto_set_pinctl(codec, spec->autocfg.line_out_pins[0],
- AC_PINCTL_OUT_EN);
- /* fake event to set up pins */
- if (cfg->hp_pins[0])
- stac_issue_unsol_event(codec, cfg->hp_pins[0]);
- else if (cfg->line_out_pins[0])
- stac_issue_unsol_event(codec, cfg->line_out_pins[0]);
- } else {
- stac92xx_auto_init_multi_out(codec);
- stac92xx_auto_init_hp_out(codec);
- for (i = 0; i < cfg->hp_outs; i++)
- stac_toggle_power_map(codec, cfg->hp_pins[i], 1);
- }
- if (spec->auto_mic) {
- /* initialize connection to analog input */
- if (spec->dmux_nids)
- snd_hda_codec_write_cache(codec, spec->dmux_nids[0], 0,
- AC_VERB_SET_CONNECT_SEL, 0);
- if (enable_pin_detect(codec, spec->ext_mic.pin, STAC_MIC_EVENT))
- stac_issue_unsol_event(codec, spec->ext_mic.pin);
- if (enable_pin_detect(codec, spec->dock_mic.pin,
- STAC_MIC_EVENT))
- stac_issue_unsol_event(codec, spec->dock_mic.pin);
- }
- for (i = 0; i < cfg->num_inputs; i++) {
- hda_nid_t nid = cfg->inputs[i].pin;
- int type = cfg->inputs[i].type;
- unsigned int pinctl, conf;
- if (type == AUTO_PIN_MIC) {
- /* for mic pins, force to initialize */
- pinctl = stac92xx_get_default_vref(codec, nid);
- pinctl |= AC_PINCTL_IN_EN;
- stac92xx_auto_set_pinctl(codec, nid, pinctl);
- } else {
- pinctl = snd_hda_codec_read(codec, nid, 0,
- AC_VERB_GET_PIN_WIDGET_CONTROL, 0);
- /* if PINCTL already set then skip */
- /* Also, if both INPUT and OUTPUT are set,
- * it must be a BIOS bug; need to override, too
- */
- if (!(pinctl & AC_PINCTL_IN_EN) ||
- (pinctl & AC_PINCTL_OUT_EN)) {
- pinctl &= ~AC_PINCTL_OUT_EN;
- pinctl |= AC_PINCTL_IN_EN;
- stac92xx_auto_set_pinctl(codec, nid, pinctl);
- }
- }
- conf = snd_hda_codec_get_pincfg(codec, nid);
- if (get_defcfg_connect(conf) != AC_JACK_PORT_FIXED) {
- if (enable_pin_detect(codec, nid, STAC_INSERT_EVENT))
- stac_issue_unsol_event(codec, nid);
- }
- }
- for (i = 0; i < spec->num_dmics; i++)
- stac92xx_auto_set_pinctl(codec, spec->dmic_nids[i],
- AC_PINCTL_IN_EN);
- if (cfg->dig_out_pins[0])
- stac92xx_auto_set_pinctl(codec, cfg->dig_out_pins[0],
- AC_PINCTL_OUT_EN);
- if (cfg->dig_in_pin)
- stac92xx_auto_set_pinctl(codec, cfg->dig_in_pin,
- AC_PINCTL_IN_EN);
- for (i = 0; i < spec->num_pwrs; i++) {
- hda_nid_t nid = spec->pwr_nids[i];
- int pinctl, def_conf;
+ spec->gpio_data |= spec->eapd_mask;
+ stac_gpio_set(codec, spec->gpio_mask, spec->gpio_dir, spec->gpio_data);
- /* power on when no jack detection is available */
- if (!spec->hp_detect) {
- stac_toggle_power_map(codec, nid, 1);
- continue;
- }
+ snd_hda_gen_init(codec);
- if (is_nid_hp_pin(cfg, nid))
- continue; /* already has an unsol event */
+ /* sync the power-map */
+ if (spec->num_pwrs)
+ snd_hda_codec_write(codec, codec->afg, 0,
+ AC_VERB_IDT_SET_POWER_MAP,
+ spec->power_map_bits);
- pinctl = snd_hda_codec_read(codec, nid, 0,
- AC_VERB_GET_PIN_WIDGET_CONTROL, 0);
- /* outputs are only ports capable of power management
- * any attempts on powering down a input port cause the
- * referenced VREF to act quirky.
- */
- if (pinctl & AC_PINCTL_IN_EN) {
- stac_toggle_power_map(codec, nid, 1);
- continue;
- }
- def_conf = snd_hda_codec_get_pincfg(codec, nid);
- def_conf = get_defcfg_connect(def_conf);
- /* skip any ports that don't have jacks since presence
- * detection is useless */
- if (def_conf != AC_JACK_PORT_COMPLEX) {
- if (def_conf != AC_JACK_PORT_NONE)
- stac_toggle_power_map(codec, nid, 1);
- continue;
+ /* power down inactive ADCs */
+ if (spec->powerdown_adcs) {
+ for (i = 0; i < spec->gen.num_all_adcs; i++) {
+ if (spec->active_adcs & (1 << i))
+ continue;
+ snd_hda_codec_write(codec, spec->gen.all_adcs[i], 0,
+ AC_VERB_SET_POWER_STATE,
+ AC_PWRST_D3);
}
- if (enable_pin_detect(codec, nid, STAC_PWR_EVENT))
- stac_issue_unsol_event(codec, nid);
}
- /* sync mute LED */
- if (spec->gpio_led)
- hda_call_check_power_status(codec, 0x01);
- if (spec->dac_list)
- stac92xx_power_down(codec);
return 0;
}
-static void stac92xx_free_kctls(struct hda_codec *codec)
-{
- struct sigmatel_spec *spec = codec->spec;
-
- if (spec->kctls.list) {
- struct snd_kcontrol_new *kctl = spec->kctls.list;
- int i;
- for (i = 0; i < spec->kctls.used; i++)
- kfree(kctl[i].name);
- }
- snd_array_free(&spec->kctls);
-}
-
-static void stac92xx_shutup(struct hda_codec *codec)
+static void stac_shutup(struct hda_codec *codec)
{
struct sigmatel_spec *spec = codec->spec;
@@ -4407,450 +4307,7 @@ static void stac92xx_shutup(struct hda_codec *codec)
~spec->eapd_mask);
}
-static void stac92xx_free(struct hda_codec *codec)
-{
- struct sigmatel_spec *spec = codec->spec;
-
- if (! spec)
- return;
-
- stac92xx_shutup(codec);
- snd_hda_input_jack_free(codec);
- snd_array_free(&spec->events);
-
- kfree(spec);
- snd_hda_detach_beep_device(codec);
-}
-
-static void stac92xx_set_pinctl(struct hda_codec *codec, hda_nid_t nid,
- unsigned int flag)
-{
- unsigned int old_ctl, pin_ctl;
-
- pin_ctl = snd_hda_codec_read(codec, nid,
- 0, AC_VERB_GET_PIN_WIDGET_CONTROL, 0x00);
-
- if (pin_ctl & AC_PINCTL_IN_EN) {
- /*
- * we need to check the current set-up direction of
- * shared input pins since they can be switched via
- * "xxx as Output" mixer switch
- */
- struct sigmatel_spec *spec = codec->spec;
- if (nid == spec->line_switch || nid == spec->mic_switch)
- return;
- }
-
- old_ctl = pin_ctl;
- /* if setting pin direction bits, clear the current
- direction bits first */
- if (flag & (AC_PINCTL_IN_EN | AC_PINCTL_OUT_EN))
- pin_ctl &= ~(AC_PINCTL_IN_EN | AC_PINCTL_OUT_EN);
-
- pin_ctl |= flag;
- if (old_ctl != pin_ctl)
- snd_hda_codec_write_cache(codec, nid, 0,
- AC_VERB_SET_PIN_WIDGET_CONTROL,
- pin_ctl);
-}
-
-static void stac92xx_reset_pinctl(struct hda_codec *codec, hda_nid_t nid,
- unsigned int flag)
-{
- unsigned int pin_ctl = snd_hda_codec_read(codec, nid,
- 0, AC_VERB_GET_PIN_WIDGET_CONTROL, 0x00);
- if (pin_ctl & flag)
- snd_hda_codec_write_cache(codec, nid, 0,
- AC_VERB_SET_PIN_WIDGET_CONTROL,
- pin_ctl & ~flag);
-}
-
-static inline int get_pin_presence(struct hda_codec *codec, hda_nid_t nid)
-{
- if (!nid)
- return 0;
- return snd_hda_jack_detect(codec, nid);
-}
-
-static void stac92xx_line_out_detect(struct hda_codec *codec,
- int presence)
-{
- struct sigmatel_spec *spec = codec->spec;
- struct auto_pin_cfg *cfg = &spec->autocfg;
- int i;
-
- for (i = 0; i < cfg->line_outs; i++) {
- if (presence)
- break;
- presence = get_pin_presence(codec, cfg->line_out_pins[i]);
- if (presence) {
- unsigned int pinctl;
- pinctl = snd_hda_codec_read(codec,
- cfg->line_out_pins[i], 0,
- AC_VERB_GET_PIN_WIDGET_CONTROL, 0);
- if (pinctl & AC_PINCTL_IN_EN)
- presence = 0; /* mic- or line-input */
- }
- }
-
- if (presence) {
- /* disable speakers */
- for (i = 0; i < cfg->speaker_outs; i++)
- stac92xx_reset_pinctl(codec, cfg->speaker_pins[i],
- AC_PINCTL_OUT_EN);
- if (spec->eapd_mask && spec->eapd_switch)
- stac_gpio_set(codec, spec->gpio_mask,
- spec->gpio_dir, spec->gpio_data &
- ~spec->eapd_mask);
- } else {
- /* enable speakers */
- for (i = 0; i < cfg->speaker_outs; i++)
- stac92xx_set_pinctl(codec, cfg->speaker_pins[i],
- AC_PINCTL_OUT_EN);
- if (spec->eapd_mask && spec->eapd_switch)
- stac_gpio_set(codec, spec->gpio_mask,
- spec->gpio_dir, spec->gpio_data |
- spec->eapd_mask);
- }
-}
-
-/* return non-zero if the hp-pin of the given array index isn't
- * a jack-detection target
- */
-static int no_hp_sensing(struct sigmatel_spec *spec, int i)
-{
- struct auto_pin_cfg *cfg = &spec->autocfg;
-
- /* ignore sensing of shared line and mic jacks */
- if (cfg->hp_pins[i] == spec->line_switch)
- return 1;
- if (cfg->hp_pins[i] == spec->mic_switch)
- return 1;
- /* ignore if the pin is set as line-out */
- if (cfg->hp_pins[i] == spec->hp_switch)
- return 1;
- return 0;
-}
-
-static void stac92xx_hp_detect(struct hda_codec *codec)
-{
- struct sigmatel_spec *spec = codec->spec;
- struct auto_pin_cfg *cfg = &spec->autocfg;
- int i, presence;
-
- presence = 0;
- if (spec->gpio_mute)
- presence = !(snd_hda_codec_read(codec, codec->afg, 0,
- AC_VERB_GET_GPIO_DATA, 0) & spec->gpio_mute);
-
- for (i = 0; i < cfg->hp_outs; i++) {
- if (presence)
- break;
- if (no_hp_sensing(spec, i))
- continue;
- presence = get_pin_presence(codec, cfg->hp_pins[i]);
- if (presence) {
- unsigned int pinctl;
- pinctl = snd_hda_codec_read(codec, cfg->hp_pins[i], 0,
- AC_VERB_GET_PIN_WIDGET_CONTROL, 0);
- if (pinctl & AC_PINCTL_IN_EN)
- presence = 0; /* mic- or line-input */
- }
- }
-
- if (presence) {
- /* disable lineouts */
- if (spec->hp_switch)
- stac92xx_reset_pinctl(codec, spec->hp_switch,
- AC_PINCTL_OUT_EN);
- for (i = 0; i < cfg->line_outs; i++)
- stac92xx_reset_pinctl(codec, cfg->line_out_pins[i],
- AC_PINCTL_OUT_EN);
- } else {
- /* enable lineouts */
- if (spec->hp_switch)
- stac92xx_set_pinctl(codec, spec->hp_switch,
- AC_PINCTL_OUT_EN);
- for (i = 0; i < cfg->line_outs; i++)
- stac92xx_set_pinctl(codec, cfg->line_out_pins[i],
- AC_PINCTL_OUT_EN);
- }
- stac92xx_line_out_detect(codec, presence);
- /* toggle hp outs */
- for (i = 0; i < cfg->hp_outs; i++) {
- unsigned int val = AC_PINCTL_OUT_EN | AC_PINCTL_HP_EN;
- if (no_hp_sensing(spec, i))
- continue;
- if (presence)
- stac92xx_set_pinctl(codec, cfg->hp_pins[i], val);
-#if 0 /* FIXME */
-/* Resetting the pinctl like below may lead to (a sort of) regressions
- * on some devices since they use the HP pin actually for line/speaker
- * outs although the default pin config shows a different pin (that is
- * wrong and useless).
- *
- * So, it's basically a problem of default pin configs, likely a BIOS issue.
- * But, disabling the code below just works around it, and I'm too tired of
- * bug reports with such devices...
- */
- else
- stac92xx_reset_pinctl(codec, cfg->hp_pins[i], val);
-#endif /* FIXME */
- }
-}
-
-static void stac_toggle_power_map(struct hda_codec *codec, hda_nid_t nid,
- int enable)
-{
- struct sigmatel_spec *spec = codec->spec;
- unsigned int idx, val;
-
- for (idx = 0; idx < spec->num_pwrs; idx++) {
- if (spec->pwr_nids[idx] == nid)
- break;
- }
- if (idx >= spec->num_pwrs)
- return;
-
- /* several codecs have two power down bits */
- if (spec->pwr_mapping)
- idx = spec->pwr_mapping[idx];
- else
- idx = 1 << idx;
-
- val = snd_hda_codec_read(codec, codec->afg, 0, 0x0fec, 0x0) & 0xff;
- if (enable)
- val &= ~idx;
- else
- val |= idx;
-
- /* power down unused output ports */
- snd_hda_codec_write(codec, codec->afg, 0, 0x7ec, val);
-}
-
-static void stac92xx_pin_sense(struct hda_codec *codec, hda_nid_t nid)
-{
- stac_toggle_power_map(codec, nid, get_pin_presence(codec, nid));
-}
-
-/* get the pin connection (fixed, none, etc) */
-static unsigned int stac_get_defcfg_connect(struct hda_codec *codec, int idx)
-{
- struct sigmatel_spec *spec = codec->spec;
- unsigned int cfg;
-
- cfg = snd_hda_codec_get_pincfg(codec, spec->pin_nids[idx]);
- return get_defcfg_connect(cfg);
-}
-
-static int stac92xx_connected_ports(struct hda_codec *codec,
- hda_nid_t *nids, int num_nids)
-{
- struct sigmatel_spec *spec = codec->spec;
- int idx, num;
- unsigned int def_conf;
-
- for (num = 0; num < num_nids; num++) {
- for (idx = 0; idx < spec->num_pins; idx++)
- if (spec->pin_nids[idx] == nids[num])
- break;
- if (idx >= spec->num_pins)
- break;
- def_conf = stac_get_defcfg_connect(codec, idx);
- if (def_conf == AC_JACK_PORT_NONE)
- break;
- }
- return num;
-}
-
-static void stac92xx_mic_detect(struct hda_codec *codec)
-{
- struct sigmatel_spec *spec = codec->spec;
- struct sigmatel_mic_route *mic;
-
- if (get_pin_presence(codec, spec->ext_mic.pin))
- mic = &spec->ext_mic;
- else if (get_pin_presence(codec, spec->dock_mic.pin))
- mic = &spec->dock_mic;
- else
- mic = &spec->int_mic;
- if (mic->dmux_idx >= 0)
- snd_hda_codec_write_cache(codec, spec->dmux_nids[0], 0,
- AC_VERB_SET_CONNECT_SEL,
- mic->dmux_idx);
- if (mic->mux_idx >= 0)
- snd_hda_codec_write_cache(codec, spec->mux_nids[0], 0,
- AC_VERB_SET_CONNECT_SEL,
- mic->mux_idx);
-}
-
-static void stac_issue_unsol_event(struct hda_codec *codec, hda_nid_t nid)
-{
- struct sigmatel_event *event = stac_get_event(codec, nid);
- if (!event)
- return;
- codec->patch_ops.unsol_event(codec, (unsigned)event->tag << 26);
-}
-
-static void stac92xx_unsol_event(struct hda_codec *codec, unsigned int res)
-{
- struct sigmatel_spec *spec = codec->spec;
- struct sigmatel_event *event;
- int tag, data;
-
- tag = (res >> 26) & 0x7f;
- event = stac_get_event_from_tag(codec, tag);
- if (!event)
- return;
-
- switch (event->type) {
- case STAC_HP_EVENT:
- case STAC_LO_EVENT:
- stac92xx_hp_detect(codec);
- break;
- case STAC_MIC_EVENT:
- stac92xx_mic_detect(codec);
- break;
- }
-
- switch (event->type) {
- case STAC_HP_EVENT:
- case STAC_LO_EVENT:
- case STAC_MIC_EVENT:
- case STAC_INSERT_EVENT:
- case STAC_PWR_EVENT:
- if (spec->num_pwrs > 0)
- stac92xx_pin_sense(codec, event->nid);
- snd_hda_input_jack_report(codec, event->nid);
-
- switch (codec->subsystem_id) {
- case 0x103c308f:
- if (event->nid == 0xb) {
- int pin = AC_PINCTL_IN_EN;
-
- if (get_pin_presence(codec, 0xa)
- && get_pin_presence(codec, 0xb))
- pin |= AC_PINCTL_VREF_80;
- if (!get_pin_presence(codec, 0xb))
- pin |= AC_PINCTL_VREF_80;
-
- /* toggle VREF state based on mic + hp pin
- * status
- */
- stac92xx_auto_set_pinctl(codec, 0x0a, pin);
- }
- }
- break;
- case STAC_VREF_EVENT:
- data = snd_hda_codec_read(codec, codec->afg, 0,
- AC_VERB_GET_GPIO_DATA, 0);
- /* toggle VREF state based on GPIOx status */
- snd_hda_codec_write(codec, codec->afg, 0, 0x7e0,
- !!(data & (1 << event->data)));
- break;
- }
-}
-
-static int hp_blike_system(u32 subsystem_id);
-
-static void set_hp_led_gpio(struct hda_codec *codec)
-{
- struct sigmatel_spec *spec = codec->spec;
- unsigned int gpio;
-
- if (spec->gpio_led)
- return;
-
- gpio = snd_hda_param_read(codec, codec->afg, AC_PAR_GPIO_CAP);
- gpio &= AC_GPIO_IO_COUNT;
- if (gpio > 3)
- spec->gpio_led = 0x08; /* GPIO 3 */
- else
- spec->gpio_led = 0x01; /* GPIO 0 */
-}
-
-/*
- * This method searches for the mute LED GPIO configuration
- * provided as OEM string in SMBIOS. The format of that string
- * is HP_Mute_LED_P_G or HP_Mute_LED_P
- * where P can be 0 or 1 and defines mute LED GPIO control state (low/high)
- * that corresponds to the NOT muted state of the master volume
- * and G is the index of the GPIO to use as the mute LED control (0..9)
- * If _G portion is missing it is assigned based on the codec ID
- *
- * So, HP B-series like systems may have HP_Mute_LED_0 (current models)
- * or HP_Mute_LED_0_3 (future models) OEM SMBIOS strings
- *
- *
- * The dv-series laptops don't seem to have the HP_Mute_LED* strings in
- * SMBIOS - at least the ones I have seen do not have them - which include
- * my own system (HP Pavilion dv6-1110ax) and my cousin's
- * HP Pavilion dv9500t CTO.
- * Need more information on whether it is true across the entire series.
- * -- kunal
- */
-static int find_mute_led_gpio(struct hda_codec *codec, int default_polarity)
-{
- struct sigmatel_spec *spec = codec->spec;
- const struct dmi_device *dev = NULL;
-
- if ((codec->subsystem_id >> 16) == PCI_VENDOR_ID_HP) {
- while ((dev = dmi_find_device(DMI_DEV_TYPE_OEM_STRING,
- NULL, dev))) {
- if (sscanf(dev->name, "HP_Mute_LED_%d_%d",
- &spec->gpio_led_polarity,
- &spec->gpio_led) == 2) {
- spec->gpio_led = 1 << spec->gpio_led;
- return 1;
- }
- if (sscanf(dev->name, "HP_Mute_LED_%d",
- &spec->gpio_led_polarity) == 1) {
- set_hp_led_gpio(codec);
- return 1;
- }
- }
-
- /*
- * Fallback case - if we don't find the DMI strings,
- * we statically set the GPIO - if not a B-series system.
- */
- if (!hp_blike_system(codec->subsystem_id)) {
- set_hp_led_gpio(codec);
- spec->gpio_led_polarity = default_polarity;
- return 1;
- }
- }
- return 0;
-}
-
-static int hp_blike_system(u32 subsystem_id)
-{
- switch (subsystem_id) {
- case 0x103c1520:
- case 0x103c1521:
- case 0x103c1523:
- case 0x103c1524:
- case 0x103c1525:
- case 0x103c1722:
- case 0x103c1723:
- case 0x103c1724:
- case 0x103c1725:
- case 0x103c1726:
- case 0x103c1727:
- case 0x103c1728:
- case 0x103c1729:
- case 0x103c172a:
- case 0x103c172b:
- case 0x103c307e:
- case 0x103c307f:
- case 0x103c3080:
- case 0x103c3081:
- case 0x103c7007:
- case 0x103c7008:
- return 1;
- }
- return 0;
-}
+#define stac_free snd_hda_gen_free
#ifdef CONFIG_PROC_FS
static void stac92hd_proc_hook(struct snd_info_buffer *buffer,
@@ -4858,7 +4315,8 @@ static void stac92hd_proc_hook(struct snd_info_buffer *buffer,
{
if (nid == codec->afg)
snd_iprintf(buffer, "Power-Map: 0x%02x\n",
- snd_hda_codec_read(codec, nid, 0, 0x0fec, 0x0));
+ snd_hda_codec_read(codec, nid, 0,
+ AC_VERB_IDT_GET_POWER_MAP, 0));
}
static void analog_loop_proc_hook(struct snd_info_buffer *buffer,
@@ -4898,149 +4356,70 @@ static void stac927x_proc_hook(struct snd_info_buffer *buffer,
#define stac927x_proc_hook NULL
#endif
-#ifdef SND_HDA_NEEDS_RESUME
-static int stac92xx_resume(struct hda_codec *codec)
+#ifdef CONFIG_PM
+static int stac_suspend(struct hda_codec *codec)
{
- struct sigmatel_spec *spec = codec->spec;
-
- stac92xx_init(codec);
- snd_hda_codec_resume_amp(codec);
- snd_hda_codec_resume_cache(codec);
- /* fake event to set up pins again to override cached values */
- if (spec->hp_detect) {
- if (spec->autocfg.hp_pins[0])
- stac_issue_unsol_event(codec, spec->autocfg.hp_pins[0]);
- else if (spec->autocfg.line_out_pins[0])
- stac_issue_unsol_event(codec,
- spec->autocfg.line_out_pins[0]);
- }
- /* sync mute LED */
- if (spec->gpio_led)
- hda_call_check_power_status(codec, 0x01);
- return 0;
-}
-
-/*
- * using power check for controlling mute led of HP notebooks
- * check for mute state only on Speakers (nid = 0x10)
- *
- * For this feature CONFIG_SND_HDA_POWER_SAVE is needed, otherwise
- * the LED is NOT working properly !
- *
- * Changed name to reflect that it now works for any designated
- * model, not just HP HDX.
- */
-
-#ifdef CONFIG_SND_HDA_POWER_SAVE
-static int stac92xx_hp_check_power_status(struct hda_codec *codec,
- hda_nid_t nid)
-{
- struct sigmatel_spec *spec = codec->spec;
- int i, muted = 1;
-
- for (i = 0; i < spec->multiout.num_dacs; i++) {
- nid = spec->multiout.dac_nids[i];
- if (!(snd_hda_codec_amp_read(codec, nid, 0, HDA_OUTPUT, 0) &
- HDA_AMP_MUTE)) {
- muted = 0; /* something heard */
- break;
- }
- }
- if (muted)
- spec->gpio_data &= ~spec->gpio_led; /* orange */
- else
- spec->gpio_data |= spec->gpio_led; /* white */
-
- if (!spec->gpio_led_polarity) {
- /* LED state is inverted on these systems */
- spec->gpio_data ^= spec->gpio_led;
- }
-
- stac_gpio_set(codec, spec->gpio_mask, spec->gpio_dir, spec->gpio_data);
+ stac_shutup(codec);
return 0;
}
+#else
+#define stac_suspend NULL
+#endif /* CONFIG_PM */
+
+static const struct hda_codec_ops stac_patch_ops = {
+ .build_controls = snd_hda_gen_build_controls,
+ .build_pcms = snd_hda_gen_build_pcms,
+ .init = stac_init,
+ .free = stac_free,
+ .unsol_event = snd_hda_jack_unsol_event,
+#ifdef CONFIG_PM
+ .suspend = stac_suspend,
#endif
+ .reboot_notify = stac_shutup,
+};
-static int stac92xx_suspend(struct hda_codec *codec, pm_message_t state)
+static int alloc_stac_spec(struct hda_codec *codec)
{
- stac92xx_shutup(codec);
+ struct sigmatel_spec *spec;
+
+ spec = kzalloc(sizeof(*spec), GFP_KERNEL);
+ if (!spec)
+ return -ENOMEM;
+ snd_hda_gen_spec_init(&spec->gen);
+ codec->spec = spec;
+ codec->no_trigger_sense = 1; /* seems common with STAC/IDT codecs */
return 0;
}
-#endif
-
-static struct hda_codec_ops stac92xx_patch_ops = {
- .build_controls = stac92xx_build_controls,
- .build_pcms = stac92xx_build_pcms,
- .init = stac92xx_init,
- .free = stac92xx_free,
- .unsol_event = stac92xx_unsol_event,
-#ifdef SND_HDA_NEEDS_RESUME
- .suspend = stac92xx_suspend,
- .resume = stac92xx_resume,
-#endif
- .reboot_notify = stac92xx_shutup,
-};
static int patch_stac9200(struct hda_codec *codec)
{
struct sigmatel_spec *spec;
int err;
- spec = kzalloc(sizeof(*spec), GFP_KERNEL);
- if (spec == NULL)
- return -ENOMEM;
+ err = alloc_stac_spec(codec);
+ if (err < 0)
+ return err;
- codec->no_trigger_sense = 1;
- codec->spec = spec;
+ spec = codec->spec;
spec->linear_tone_beep = 1;
- spec->num_pins = ARRAY_SIZE(stac9200_pin_nids);
- spec->pin_nids = stac9200_pin_nids;
- spec->board_config = snd_hda_check_board_config(codec, STAC_9200_MODELS,
- stac9200_models,
- stac9200_cfg_tbl);
- if (spec->board_config < 0)
- snd_printdd(KERN_INFO "hda_codec: %s: BIOS auto-probing.\n",
- codec->chip_name);
- else
- stac92xx_set_config_regs(codec,
- stac9200_brd_tbl[spec->board_config]);
-
- spec->multiout.max_channels = 2;
- spec->multiout.num_dacs = 1;
- spec->multiout.dac_nids = stac9200_dac_nids;
- spec->adc_nids = stac9200_adc_nids;
- spec->mux_nids = stac9200_mux_nids;
- spec->num_muxes = 1;
- spec->num_dmics = 0;
- spec->num_adcs = 1;
- spec->num_pwrs = 0;
-
- if (spec->board_config == STAC_9200_M4 ||
- spec->board_config == STAC_9200_M4_2 ||
- spec->board_config == STAC_9200_OQO)
- spec->init = stac9200_eapd_init;
- else
- spec->init = stac9200_core_init;
- spec->mixer = stac9200_mixer;
+ spec->gen.own_eapd_ctl = 1;
- if (spec->board_config == STAC_9200_PANASONIC) {
- spec->gpio_mask = spec->gpio_dir = 0x09;
- spec->gpio_data = 0x00;
- }
+ codec->patch_ops = stac_patch_ops;
+ codec->power_filter = snd_hda_codec_eapd_power_filter;
+
+ snd_hda_add_verbs(codec, stac9200_eapd_init);
- err = stac9200_parse_auto_config(codec);
+ snd_hda_pick_fixup(codec, stac9200_models, stac9200_fixup_tbl,
+ stac9200_fixups);
+ snd_hda_apply_fixup(codec, HDA_FIXUP_ACT_PRE_PROBE);
+
+ err = stac_parse_auto_config(codec);
if (err < 0) {
- stac92xx_free(codec);
+ stac_free(codec);
return err;
}
- /* CF-74 has no headphone detection, and the driver should *NOT*
- * do detection and HP/speaker toggle because the hardware does it.
- */
- if (spec->board_config == STAC_9200_PANASONIC)
- spec->hp_detect = 0;
-
- codec->patch_ops = stac92xx_patch_ops;
+ snd_hda_apply_fixup(codec, HDA_FIXUP_ACT_PROBE);
return 0;
}
@@ -5050,81 +4429,29 @@ static int patch_stac925x(struct hda_codec *codec)
struct sigmatel_spec *spec;
int err;
- spec = kzalloc(sizeof(*spec), GFP_KERNEL);
- if (spec == NULL)
- return -ENOMEM;
+ err = alloc_stac_spec(codec);
+ if (err < 0)
+ return err;
- codec->no_trigger_sense = 1;
- codec->spec = spec;
+ spec = codec->spec;
spec->linear_tone_beep = 1;
- spec->num_pins = ARRAY_SIZE(stac925x_pin_nids);
- spec->pin_nids = stac925x_pin_nids;
-
- /* Check first for codec ID */
- spec->board_config = snd_hda_check_board_codec_sid_config(codec,
- STAC_925x_MODELS,
- stac925x_models,
- stac925x_codec_id_cfg_tbl);
-
- /* Now checks for PCI ID, if codec ID is not found */
- if (spec->board_config < 0)
- spec->board_config = snd_hda_check_board_config(codec,
- STAC_925x_MODELS,
- stac925x_models,
- stac925x_cfg_tbl);
- again:
- if (spec->board_config < 0)
- snd_printdd(KERN_INFO "hda_codec: %s: BIOS auto-probing.\n",
- codec->chip_name);
- else
- stac92xx_set_config_regs(codec,
- stac925x_brd_tbl[spec->board_config]);
-
- spec->multiout.max_channels = 2;
- spec->multiout.num_dacs = 1;
- spec->multiout.dac_nids = stac925x_dac_nids;
- spec->adc_nids = stac925x_adc_nids;
- spec->mux_nids = stac925x_mux_nids;
- spec->num_muxes = 1;
- spec->num_adcs = 1;
- spec->num_pwrs = 0;
- switch (codec->vendor_id) {
- case 0x83847632: /* STAC9202 */
- case 0x83847633: /* STAC9202D */
- case 0x83847636: /* STAC9251 */
- case 0x83847637: /* STAC9251D */
- spec->num_dmics = STAC925X_NUM_DMICS;
- spec->dmic_nids = stac925x_dmic_nids;
- spec->num_dmuxes = ARRAY_SIZE(stac925x_dmux_nids);
- spec->dmux_nids = stac925x_dmux_nids;
- break;
- default:
- spec->num_dmics = 0;
- break;
- }
+ spec->gen.own_eapd_ctl = 1;
- spec->init = stac925x_core_init;
- spec->mixer = stac925x_mixer;
- spec->num_caps = 1;
- spec->capvols = stac925x_capvols;
- spec->capsws = stac925x_capsws;
-
- err = stac92xx_parse_auto_config(codec, 0x8, 0x7);
- if (!err) {
- if (spec->board_config < 0) {
- printk(KERN_WARNING "hda_codec: No auto-config is "
- "available, default to model=ref\n");
- spec->board_config = STAC_925x_REF;
- goto again;
- }
- err = -EINVAL;
- }
+ codec->patch_ops = stac_patch_ops;
+
+ snd_hda_add_verbs(codec, stac925x_core_init);
+
+ snd_hda_pick_fixup(codec, stac925x_models, stac925x_fixup_tbl,
+ stac925x_fixups);
+ snd_hda_apply_fixup(codec, HDA_FIXUP_ACT_PRE_PROBE);
+
+ err = stac_parse_auto_config(codec);
if (err < 0) {
- stac92xx_free(codec);
+ stac_free(codec);
return err;
}
- codec->patch_ops = stac92xx_patch_ops;
+ snd_hda_apply_fixup(codec, HDA_FIXUP_ACT_PROBE);
return 0;
}
@@ -5132,740 +4459,275 @@ static int patch_stac925x(struct hda_codec *codec)
static int patch_stac92hd73xx(struct hda_codec *codec)
{
struct sigmatel_spec *spec;
- hda_nid_t conn[STAC92HD73_DAC_COUNT + 2];
- int err = 0;
+ int err;
int num_dacs;
- spec = kzalloc(sizeof(*spec), GFP_KERNEL);
- if (spec == NULL)
- return -ENOMEM;
+ err = alloc_stac_spec(codec);
+ if (err < 0)
+ return err;
- codec->no_trigger_sense = 1;
- codec->spec = spec;
+ spec = codec->spec;
spec->linear_tone_beep = 0;
- codec->slave_dig_outs = stac92hd73xx_slave_dig_outs;
- spec->num_pins = ARRAY_SIZE(stac92hd73xx_pin_nids);
- spec->pin_nids = stac92hd73xx_pin_nids;
- spec->board_config = snd_hda_check_board_config(codec,
- STAC_92HD73XX_MODELS,
- stac92hd73xx_models,
- stac92hd73xx_cfg_tbl);
- /* check codec subsystem id if not found */
- if (spec->board_config < 0)
- spec->board_config =
- snd_hda_check_board_codec_sid_config(codec,
- STAC_92HD73XX_MODELS, stac92hd73xx_models,
- stac92hd73xx_codec_id_cfg_tbl);
-again:
- if (spec->board_config < 0)
- snd_printdd(KERN_INFO "hda_codec: %s: BIOS auto-probing.\n",
- codec->chip_name);
- else
- stac92xx_set_config_regs(codec,
- stac92hd73xx_brd_tbl[spec->board_config]);
-
- num_dacs = snd_hda_get_connections(codec, 0x0a,
- conn, STAC92HD73_DAC_COUNT + 2) - 1;
+ spec->gen.mixer_nid = 0x1d;
+ spec->have_spdif_mux = 1;
+ num_dacs = snd_hda_get_num_conns(codec, 0x0a) - 1;
if (num_dacs < 3 || num_dacs > 5) {
- printk(KERN_WARNING "hda_codec: Could not determine "
- "number of channels defaulting to DAC count\n");
- num_dacs = STAC92HD73_DAC_COUNT;
+ codec_warn(codec,
+ "Could not determine number of channels defaulting to DAC count\n");
+ num_dacs = 5;
}
- spec->init = stac92hd73xx_core_init;
+
switch (num_dacs) {
case 0x3: /* 6 Channel */
- spec->aloopback_ctl = stac92hd73xx_6ch_loopback;
+ spec->aloopback_ctl = &stac92hd73xx_6ch_loopback;
break;
case 0x4: /* 8 Channel */
- spec->aloopback_ctl = stac92hd73xx_8ch_loopback;
+ spec->aloopback_ctl = &stac92hd73xx_8ch_loopback;
break;
case 0x5: /* 10 Channel */
- spec->aloopback_ctl = stac92hd73xx_10ch_loopback;
+ spec->aloopback_ctl = &stac92hd73xx_10ch_loopback;
break;
}
- spec->multiout.dac_nids = spec->dac_nids;
spec->aloopback_mask = 0x01;
spec->aloopback_shift = 8;
- spec->digbeep_nid = 0x1c;
- spec->mux_nids = stac92hd73xx_mux_nids;
- spec->adc_nids = stac92hd73xx_adc_nids;
- spec->dmic_nids = stac92hd73xx_dmic_nids;
- spec->dmux_nids = stac92hd73xx_dmux_nids;
- spec->smux_nids = stac92hd73xx_smux_nids;
-
- spec->num_muxes = ARRAY_SIZE(stac92hd73xx_mux_nids);
- spec->num_adcs = ARRAY_SIZE(stac92hd73xx_adc_nids);
- spec->num_dmuxes = ARRAY_SIZE(stac92hd73xx_dmux_nids);
-
- spec->num_caps = STAC92HD73XX_NUM_CAPS;
- spec->capvols = stac92hd73xx_capvols;
- spec->capsws = stac92hd73xx_capsws;
-
- switch (spec->board_config) {
- case STAC_DELL_EQ:
- spec->init = dell_eq_core_init;
- /* fallthru */
- case STAC_DELL_M6_AMIC:
- case STAC_DELL_M6_DMIC:
- case STAC_DELL_M6_BOTH:
- spec->num_smuxes = 0;
- spec->eapd_switch = 0;
+ spec->gen.beep_nid = 0x1c; /* digital beep */
- switch (spec->board_config) {
- case STAC_DELL_M6_AMIC: /* Analog Mics */
- snd_hda_codec_set_pincfg(codec, 0x0b, 0x90A70170);
- spec->num_dmics = 0;
- break;
- case STAC_DELL_M6_DMIC: /* Digital Mics */
- snd_hda_codec_set_pincfg(codec, 0x13, 0x90A60160);
- spec->num_dmics = 1;
- break;
- case STAC_DELL_M6_BOTH: /* Both */
- snd_hda_codec_set_pincfg(codec, 0x0b, 0x90A70170);
- snd_hda_codec_set_pincfg(codec, 0x13, 0x90A60160);
- spec->num_dmics = 1;
- break;
- }
- break;
- case STAC_ALIENWARE_M17X:
- spec->num_dmics = STAC92HD73XX_NUM_DMICS;
- spec->num_smuxes = ARRAY_SIZE(stac92hd73xx_smux_nids);
- spec->eapd_switch = 0;
- break;
- default:
- spec->num_dmics = STAC92HD73XX_NUM_DMICS;
- spec->num_smuxes = ARRAY_SIZE(stac92hd73xx_smux_nids);
- spec->eapd_switch = 1;
- break;
- }
- if (spec->board_config != STAC_92HD73XX_REF) {
- /* GPIO0 High = Enable EAPD */
- spec->eapd_mask = spec->gpio_mask = spec->gpio_dir = 0x1;
- spec->gpio_data = 0x01;
- }
+ /* GPIO0 High = Enable EAPD */
+ spec->eapd_mask = spec->gpio_mask = spec->gpio_dir = 0x1;
+ spec->gpio_data = 0x01;
+
+ spec->eapd_switch = 1;
spec->num_pwrs = ARRAY_SIZE(stac92hd73xx_pwr_nids);
spec->pwr_nids = stac92hd73xx_pwr_nids;
- err = stac92xx_parse_auto_config(codec, 0x25, 0x27);
+ spec->gen.own_eapd_ctl = 1;
+ spec->gen.power_down_unused = 1;
- if (!err) {
- if (spec->board_config < 0) {
- printk(KERN_WARNING "hda_codec: No auto-config is "
- "available, default to model=ref\n");
- spec->board_config = STAC_92HD73XX_REF;
- goto again;
- }
- err = -EINVAL;
- }
+ codec->patch_ops = stac_patch_ops;
+
+ snd_hda_pick_fixup(codec, stac92hd73xx_models, stac92hd73xx_fixup_tbl,
+ stac92hd73xx_fixups);
+ snd_hda_apply_fixup(codec, HDA_FIXUP_ACT_PRE_PROBE);
+ if (!spec->volknob_init)
+ snd_hda_add_verbs(codec, stac92hd73xx_core_init);
+
+ err = stac_parse_auto_config(codec);
if (err < 0) {
- stac92xx_free(codec);
+ stac_free(codec);
return err;
}
- if (spec->board_config == STAC_92HD73XX_NO_JD)
- spec->hp_detect = 0;
-
- codec->patch_ops = stac92xx_patch_ops;
+ /* Don't GPIO-mute speakers if there are no internal speakers, because
+ * the GPIO might be necessary for Headphone
+ */
+ if (spec->eapd_switch && !has_builtin_speaker(codec))
+ spec->eapd_switch = 0;
codec->proc_widget_hook = stac92hd7x_proc_hook;
- return 0;
-}
-
-static int hp_bnb2011_with_dock(struct hda_codec *codec)
-{
- if (codec->vendor_id != 0x111d7605 &&
- codec->vendor_id != 0x111d76d1)
- return 0;
+ snd_hda_apply_fixup(codec, HDA_FIXUP_ACT_PROBE);
- switch (codec->subsystem_id) {
- case 0x103c1618:
- case 0x103c1619:
- case 0x103c161a:
- case 0x103c161b:
- case 0x103c161c:
- case 0x103c161d:
- case 0x103c161e:
- case 0x103c161f:
-
- case 0x103c162a:
- case 0x103c162b:
-
- case 0x103c1630:
- case 0x103c1631:
-
- case 0x103c1633:
- case 0x103c1634:
- case 0x103c1635:
-
- case 0x103c3587:
- case 0x103c3588:
- case 0x103c3589:
- case 0x103c358a:
-
- case 0x103c3667:
- case 0x103c3668:
- case 0x103c3669:
-
- return 1;
- }
return 0;
}
-static void stac92hd8x_add_pin(struct hda_codec *codec, hda_nid_t nid)
-{
- struct sigmatel_spec *spec = codec->spec;
- unsigned int def_conf = snd_hda_codec_get_pincfg(codec, nid);
- int i;
-
- spec->auto_pin_nids[spec->auto_pin_cnt] = nid;
- spec->auto_pin_cnt++;
-
- if (get_defcfg_device(def_conf) == AC_JACK_MIC_IN &&
- get_defcfg_connect(def_conf) != AC_JACK_PORT_NONE) {
- for (i = 0; i < ARRAY_SIZE(stac92hd83xxx_dmic_nids); i++) {
- if (nid == stac92hd83xxx_dmic_nids[i]) {
- spec->auto_dmic_nids[spec->auto_dmic_cnt] = nid;
- spec->auto_dmic_cnt++;
- }
- }
- }
-}
-
-static void stac92hd8x_add_adc(struct hda_codec *codec, hda_nid_t nid)
-{
- struct sigmatel_spec *spec = codec->spec;
-
- spec->auto_adc_nids[spec->auto_adc_cnt] = nid;
- spec->auto_adc_cnt++;
-}
-
-static void stac92hd8x_add_mux(struct hda_codec *codec, hda_nid_t nid)
+static void stac_setup_gpio(struct hda_codec *codec)
{
- int i, j;
struct sigmatel_spec *spec = codec->spec;
- for (i = 0; i < spec->auto_adc_cnt; i++) {
- if (get_connection_index(codec,
- spec->auto_adc_nids[i], nid) >= 0) {
- /* mux and volume for adc_nids[i] */
- if (!spec->auto_mux_nids[i]) {
- spec->auto_mux_nids[i] = nid;
- /* 92hd codecs capture volume is in mux */
- spec->auto_capvols[i] = HDA_COMPOSE_AMP_VAL(nid,
- 3, 0, HDA_OUTPUT);
- }
- for (j = 0; j < spec->auto_dmic_cnt; j++) {
- if (get_connection_index(codec, nid,
- spec->auto_dmic_nids[j]) >= 0) {
- /* dmux for adc_nids[i] */
- if (!spec->auto_dmux_nids[i])
- spec->auto_dmux_nids[i] = nid;
- break;
- }
- }
- break;
+ spec->gpio_mask |= spec->eapd_mask;
+ if (spec->gpio_led) {
+ if (!spec->vref_mute_led_nid) {
+ spec->gpio_mask |= spec->gpio_led;
+ spec->gpio_dir |= spec->gpio_led;
+ spec->gpio_data |= spec->gpio_led;
+ } else {
+ codec->power_filter = stac_vref_led_power_filter;
}
}
-}
-
-static void stac92hd8x_fill_auto_spec(struct hda_codec *codec)
-{
- hda_nid_t nid, end_nid;
- unsigned int wid_caps, wid_type;
- struct sigmatel_spec *spec = codec->spec;
-
- end_nid = codec->start_nid + codec->num_nodes;
-
- for (nid = codec->start_nid; nid < end_nid; nid++) {
- wid_caps = get_wcaps(codec, nid);
- wid_type = get_wcaps_type(wid_caps);
-
- if (wid_type == AC_WID_PIN)
- stac92hd8x_add_pin(codec, nid);
-
- if (wid_type == AC_WID_AUD_IN && !(wid_caps & AC_WCAP_DIGITAL))
- stac92hd8x_add_adc(codec, nid);
- }
- for (nid = codec->start_nid; nid < end_nid; nid++) {
- wid_caps = get_wcaps(codec, nid);
- wid_type = get_wcaps_type(wid_caps);
+ if (spec->mic_mute_led_gpio) {
+ spec->gpio_mask |= spec->mic_mute_led_gpio;
+ spec->gpio_dir |= spec->mic_mute_led_gpio;
+ spec->mic_enabled = 0;
+ spec->gpio_data |= spec->mic_mute_led_gpio;
- if (wid_type == AC_WID_AUD_SEL)
- stac92hd8x_add_mux(codec, nid);
+ spec->gen.cap_sync_hook = stac_capture_led_hook;
}
-
- spec->pin_nids = spec->auto_pin_nids;
- spec->num_pins = spec->auto_pin_cnt;
- spec->adc_nids = spec->auto_adc_nids;
- spec->num_adcs = spec->auto_adc_cnt;
- spec->capvols = spec->auto_capvols;
- spec->capsws = spec->auto_capvols;
- spec->num_caps = spec->auto_adc_cnt;
- spec->mux_nids = spec->auto_mux_nids;
- spec->num_muxes = spec->auto_adc_cnt;
- spec->dmux_nids = spec->auto_dmux_nids;
- spec->num_dmuxes = spec->auto_adc_cnt;
- spec->dmic_nids = spec->auto_dmic_nids;
- spec->num_dmics = spec->auto_dmic_cnt;
}
static int patch_stac92hd83xxx(struct hda_codec *codec)
{
struct sigmatel_spec *spec;
- hda_nid_t conn[STAC92HD83_DAC_COUNT + 1];
int err;
- int num_dacs;
-
- spec = kzalloc(sizeof(*spec), GFP_KERNEL);
- if (spec == NULL)
- return -ENOMEM;
-
- if (hp_bnb2011_with_dock(codec)) {
- snd_hda_codec_set_pincfg(codec, 0xa, 0x2101201f);
- snd_hda_codec_set_pincfg(codec, 0xf, 0x2181205e);
- }
- /* reset pin power-down; Windows may leave these bits after reboot */
- snd_hda_codec_write_cache(codec, codec->afg, 0, 0x7EC, 0);
- snd_hda_codec_write_cache(codec, codec->afg, 0, 0x7ED, 0);
- codec->no_trigger_sense = 1;
- codec->spec = spec;
+ err = alloc_stac_spec(codec);
+ if (err < 0)
+ return err;
- stac92hd8x_fill_auto_spec(codec);
+ codec->epss = 0; /* longer delay needed for D3 */
+ spec = codec->spec;
spec->linear_tone_beep = 0;
- codec->slave_dig_outs = stac92hd83xxx_slave_dig_outs;
- spec->digbeep_nid = 0x21;
+ spec->gen.own_eapd_ctl = 1;
+ spec->gen.power_down_unused = 1;
+ spec->gen.mixer_nid = 0x1b;
+
+ spec->gen.beep_nid = 0x21; /* digital beep */
spec->pwr_nids = stac92hd83xxx_pwr_nids;
- spec->pwr_mapping = stac92hd83xxx_pwr_mapping;
spec->num_pwrs = ARRAY_SIZE(stac92hd83xxx_pwr_nids);
- spec->multiout.dac_nids = spec->dac_nids;
- spec->init = stac92hd83xxx_core_init;
-
- spec->board_config = snd_hda_check_board_config(codec,
- STAC_92HD83XXX_MODELS,
- stac92hd83xxx_models,
- stac92hd83xxx_cfg_tbl);
-again:
- if (spec->board_config < 0)
- snd_printdd(KERN_INFO "hda_codec: %s: BIOS auto-probing.\n",
- codec->chip_name);
- else
- stac92xx_set_config_regs(codec,
- stac92hd83xxx_brd_tbl[spec->board_config]);
+ spec->default_polarity = -1; /* no default cfg */
- switch (codec->vendor_id) {
- case 0x111d76d1:
- case 0x111d76d9:
- case 0x111d76e5:
- case 0x111d7666:
- case 0x111d7667:
- case 0x111d7668:
- case 0x111d7669:
- case 0x111d76e3:
- case 0x111d7604:
- case 0x111d76d4:
- case 0x111d7605:
- case 0x111d76d5:
- case 0x111d76e7:
- if (spec->board_config == STAC_92HD83XXX_PWR_REF)
- break;
- spec->num_pwrs = 0;
- break;
- }
+ codec->patch_ops = stac_patch_ops;
- codec->patch_ops = stac92xx_patch_ops;
+ snd_hda_add_verbs(codec, stac92hd83xxx_core_init);
- if (find_mute_led_gpio(codec, 0))
- snd_printd("mute LED gpio %d polarity %d\n",
- spec->gpio_led,
- spec->gpio_led_polarity);
+ snd_hda_pick_fixup(codec, stac92hd83xxx_models, stac92hd83xxx_fixup_tbl,
+ stac92hd83xxx_fixups);
+ snd_hda_apply_fixup(codec, HDA_FIXUP_ACT_PRE_PROBE);
-#ifdef CONFIG_SND_HDA_POWER_SAVE
- if (spec->gpio_led) {
- spec->gpio_mask |= spec->gpio_led;
- spec->gpio_dir |= spec->gpio_led;
- spec->gpio_data |= spec->gpio_led;
- /* register check_power_status callback. */
- codec->patch_ops.check_power_status =
- stac92xx_hp_check_power_status;
- }
-#endif
-
- err = stac92xx_parse_auto_config(codec, 0x1d, 0);
- if (!err) {
- if (spec->board_config < 0) {
- printk(KERN_WARNING "hda_codec: No auto-config is "
- "available, default to model=ref\n");
- spec->board_config = STAC_92HD83XXX_REF;
- goto again;
- }
- err = -EINVAL;
- }
+ stac_setup_gpio(codec);
+ err = stac_parse_auto_config(codec);
if (err < 0) {
- stac92xx_free(codec);
+ stac_free(codec);
return err;
}
- /* docking output support */
- num_dacs = snd_hda_get_connections(codec, 0xF,
- conn, STAC92HD83_DAC_COUNT + 1) - 1;
- /* skip non-DAC connections */
- while (num_dacs >= 0 &&
- (get_wcaps_type(get_wcaps(codec, conn[num_dacs]))
- != AC_WID_AUD_OUT))
- num_dacs--;
- /* set port E and F to select the last DAC */
- if (num_dacs >= 0) {
- snd_hda_codec_write_cache(codec, 0xE, 0,
- AC_VERB_SET_CONNECT_SEL, num_dacs);
- snd_hda_codec_write_cache(codec, 0xF, 0,
- AC_VERB_SET_CONNECT_SEL, num_dacs);
- }
-
codec->proc_widget_hook = stac92hd_proc_hook;
+ snd_hda_apply_fixup(codec, HDA_FIXUP_ACT_PROBE);
+
return 0;
}
-static int stac92hd71bxx_connected_smuxes(struct hda_codec *codec,
- hda_nid_t dig0pin)
+static const hda_nid_t stac92hd95_pwr_nids[] = {
+ 0x0a, 0x0b, 0x0c, 0x0d
+};
+
+static int patch_stac92hd95(struct hda_codec *codec)
{
- struct sigmatel_spec *spec = codec->spec;
- int idx;
+ struct sigmatel_spec *spec;
+ int err;
- for (idx = 0; idx < spec->num_pins; idx++)
- if (spec->pin_nids[idx] == dig0pin)
- break;
- if ((idx + 2) >= spec->num_pins)
- return 0;
+ err = alloc_stac_spec(codec);
+ if (err < 0)
+ return err;
- /* dig1pin case */
- if (stac_get_defcfg_connect(codec, idx + 1) != AC_JACK_PORT_NONE)
- return 2;
+ codec->epss = 0; /* longer delay needed for D3 */
- /* dig0pin + dig2pin case */
- if (stac_get_defcfg_connect(codec, idx + 2) != AC_JACK_PORT_NONE)
- return 2;
- if (stac_get_defcfg_connect(codec, idx) != AC_JACK_PORT_NONE)
- return 1;
- else
- return 0;
-}
+ spec = codec->spec;
+ spec->linear_tone_beep = 0;
+ spec->gen.own_eapd_ctl = 1;
+ spec->gen.power_down_unused = 1;
-/* HP dv7 bass switch - GPIO5 */
-#define stac_hp_bass_gpio_info snd_ctl_boolean_mono_info
-static int stac_hp_bass_gpio_get(struct snd_kcontrol *kcontrol,
- struct snd_ctl_elem_value *ucontrol)
-{
- struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
- struct sigmatel_spec *spec = codec->spec;
- ucontrol->value.integer.value[0] = !!(spec->gpio_data & 0x20);
- return 0;
-}
+ spec->gen.beep_nid = 0x19; /* digital beep */
+ spec->pwr_nids = stac92hd95_pwr_nids;
+ spec->num_pwrs = ARRAY_SIZE(stac92hd95_pwr_nids);
+ spec->default_polarity = 0;
-static int stac_hp_bass_gpio_put(struct snd_kcontrol *kcontrol,
- struct snd_ctl_elem_value *ucontrol)
-{
- struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
- struct sigmatel_spec *spec = codec->spec;
- unsigned int gpio_data;
+ codec->patch_ops = stac_patch_ops;
- gpio_data = (spec->gpio_data & ~0x20) |
- (ucontrol->value.integer.value[0] ? 0x20 : 0);
- if (gpio_data == spec->gpio_data)
- return 0;
- spec->gpio_data = gpio_data;
- stac_gpio_set(codec, spec->gpio_mask, spec->gpio_dir, spec->gpio_data);
- return 1;
-}
+ snd_hda_pick_fixup(codec, stac92hd95_models, stac92hd95_fixup_tbl,
+ stac92hd95_fixups);
+ snd_hda_apply_fixup(codec, HDA_FIXUP_ACT_PRE_PROBE);
-static struct snd_kcontrol_new stac_hp_bass_sw_ctrl = {
- .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
- .info = stac_hp_bass_gpio_info,
- .get = stac_hp_bass_gpio_get,
- .put = stac_hp_bass_gpio_put,
-};
+ stac_setup_gpio(codec);
-static int stac_add_hp_bass_switch(struct hda_codec *codec)
-{
- struct sigmatel_spec *spec = codec->spec;
+ err = stac_parse_auto_config(codec);
+ if (err < 0) {
+ stac_free(codec);
+ return err;
+ }
- if (!stac_control_new(spec, &stac_hp_bass_sw_ctrl,
- "Bass Speaker Playback Switch", 0))
- return -ENOMEM;
+ codec->proc_widget_hook = stac92hd_proc_hook;
+
+ snd_hda_apply_fixup(codec, HDA_FIXUP_ACT_PROBE);
- spec->gpio_mask |= 0x20;
- spec->gpio_dir |= 0x20;
- spec->gpio_data |= 0x20;
return 0;
}
static int patch_stac92hd71bxx(struct hda_codec *codec)
{
struct sigmatel_spec *spec;
- struct hda_verb *unmute_init = stac92hd71bxx_unmute_core_init;
- unsigned int pin_cfg;
- int err = 0;
+ const struct hda_verb *unmute_init = stac92hd71bxx_unmute_core_init;
+ int err;
- spec = kzalloc(sizeof(*spec), GFP_KERNEL);
- if (spec == NULL)
- return -ENOMEM;
+ err = alloc_stac_spec(codec);
+ if (err < 0)
+ return err;
- codec->no_trigger_sense = 1;
- codec->spec = spec;
+ spec = codec->spec;
spec->linear_tone_beep = 0;
- codec->patch_ops = stac92xx_patch_ops;
- spec->num_pins = STAC92HD71BXX_NUM_PINS;
- switch (codec->vendor_id) {
- case 0x111d76b6:
- case 0x111d76b7:
- spec->pin_nids = stac92hd71bxx_pin_nids_4port;
- break;
- case 0x111d7603:
- case 0x111d7608:
- /* On 92HD75Bx 0x27 isn't a pin nid */
- spec->num_pins--;
- /* fallthrough */
- default:
- spec->pin_nids = stac92hd71bxx_pin_nids_6port;
- }
- spec->num_pwrs = ARRAY_SIZE(stac92hd71bxx_pwr_nids);
- spec->board_config = snd_hda_check_board_config(codec,
- STAC_92HD71BXX_MODELS,
- stac92hd71bxx_models,
- stac92hd71bxx_cfg_tbl);
-again:
- if (spec->board_config < 0)
- snd_printdd(KERN_INFO "hda_codec: %s: BIOS auto-probing.\n",
- codec->chip_name);
- else
- stac92xx_set_config_regs(codec,
- stac92hd71bxx_brd_tbl[spec->board_config]);
+ spec->gen.own_eapd_ctl = 1;
+ spec->gen.power_down_unused = 1;
+ spec->gen.mixer_nid = 0x17;
+ spec->have_spdif_mux = 1;
- if (spec->board_config != STAC_92HD71BXX_REF) {
- /* GPIO0 = EAPD */
- spec->gpio_mask = 0x01;
- spec->gpio_dir = 0x01;
- spec->gpio_data = 0x01;
- }
-
- spec->dmic_nids = stac92hd71bxx_dmic_nids;
- spec->dmux_nids = stac92hd71bxx_dmux_nids;
+ codec->patch_ops = stac_patch_ops;
- spec->num_caps = STAC92HD71BXX_NUM_CAPS;
- spec->capvols = stac92hd71bxx_capvols;
- spec->capsws = stac92hd71bxx_capsws;
+ /* GPIO0 = EAPD */
+ spec->gpio_mask = 0x01;
+ spec->gpio_dir = 0x01;
+ spec->gpio_data = 0x01;
switch (codec->vendor_id) {
case 0x111d76b6: /* 4 Port without Analog Mixer */
case 0x111d76b7:
unmute_init++;
- /* fallthru */
- case 0x111d76b4: /* 6 Port without Analog Mixer */
- case 0x111d76b5:
- spec->init = stac92hd71bxx_core_init;
- codec->slave_dig_outs = stac92hd71bxx_slave_dig_outs;
- spec->num_dmics = stac92xx_connected_ports(codec,
- stac92hd71bxx_dmic_nids,
- STAC92HD71BXX_NUM_DMICS);
break;
case 0x111d7608: /* 5 Port with Analog Mixer */
- switch (spec->board_config) {
- case STAC_HP_M4:
- /* Enable VREF power saving on GPIO1 detect */
- err = stac_add_event(spec, codec->afg,
- STAC_VREF_EVENT, 0x02);
- if (err < 0)
- return err;
- snd_hda_codec_write_cache(codec, codec->afg, 0,
- AC_VERB_SET_GPIO_UNSOLICITED_RSP_MASK, 0x02);
- snd_hda_codec_write_cache(codec, codec->afg, 0,
- AC_VERB_SET_UNSOLICITED_ENABLE,
- AC_USRSP_EN | err);
- spec->gpio_mask |= 0x02;
- break;
- }
if ((codec->revision_id & 0xf) == 0 ||
(codec->revision_id & 0xf) == 1)
spec->stream_delay = 40; /* 40 milliseconds */
- /* no output amps */
- spec->num_pwrs = 0;
/* disable VSW */
- spec->init = stac92hd71bxx_core_init;
unmute_init++;
snd_hda_codec_set_pincfg(codec, 0x0f, 0x40f000f0);
snd_hda_codec_set_pincfg(codec, 0x19, 0x40f000f3);
- stac92hd71bxx_dmic_nids[STAC92HD71BXX_NUM_DMICS - 1] = 0;
- spec->num_dmics = stac92xx_connected_ports(codec,
- stac92hd71bxx_dmic_nids,
- STAC92HD71BXX_NUM_DMICS - 1);
break;
case 0x111d7603: /* 6 Port with Analog Mixer */
if ((codec->revision_id & 0xf) == 1)
spec->stream_delay = 40; /* 40 milliseconds */
- /* no output amps */
- spec->num_pwrs = 0;
- /* fallthru */
- default:
- spec->init = stac92hd71bxx_core_init;
- codec->slave_dig_outs = stac92hd71bxx_slave_dig_outs;
- spec->num_dmics = stac92xx_connected_ports(codec,
- stac92hd71bxx_dmic_nids,
- STAC92HD71BXX_NUM_DMICS);
break;
}
+ if (get_wcaps_type(get_wcaps(codec, 0x28)) == AC_WID_VOL_KNB)
+ snd_hda_add_verbs(codec, stac92hd71bxx_core_init);
+
if (get_wcaps(codec, 0xa) & AC_WCAP_IN_AMP)
snd_hda_sequence_write_cache(codec, unmute_init);
- /* Some HP machines seem to have unstable codec communications
- * especially with ATI fglrx driver. For recovering from the
- * CORB/RIRB stall, allow the BUS reset and keep always sync
- */
- if (spec->board_config == STAC_HP_DV5) {
- codec->bus->sync_write = 1;
- codec->bus->allow_bus_reset = 1;
- }
-
- spec->aloopback_ctl = stac92hd71bxx_loopback;
+ spec->aloopback_ctl = &stac92hd71bxx_loopback;
spec->aloopback_mask = 0x50;
spec->aloopback_shift = 0;
spec->powerdown_adcs = 1;
- spec->digbeep_nid = 0x26;
- spec->mux_nids = stac92hd71bxx_mux_nids;
- spec->adc_nids = stac92hd71bxx_adc_nids;
- spec->smux_nids = stac92hd71bxx_smux_nids;
+ spec->gen.beep_nid = 0x26; /* digital beep */
+ spec->num_pwrs = ARRAY_SIZE(stac92hd71bxx_pwr_nids);
spec->pwr_nids = stac92hd71bxx_pwr_nids;
- spec->num_muxes = ARRAY_SIZE(stac92hd71bxx_mux_nids);
- spec->num_adcs = ARRAY_SIZE(stac92hd71bxx_adc_nids);
- spec->num_dmuxes = ARRAY_SIZE(stac92hd71bxx_dmux_nids);
- spec->num_smuxes = stac92hd71bxx_connected_smuxes(codec, 0x1e);
-
- snd_printdd("Found board config: %d\n", spec->board_config);
-
- switch (spec->board_config) {
- case STAC_HP_M4:
- /* enable internal microphone */
- snd_hda_codec_set_pincfg(codec, 0x0e, 0x01813040);
- stac92xx_auto_set_pinctl(codec, 0x0e,
- AC_PINCTL_IN_EN | AC_PINCTL_VREF_80);
- /* fallthru */
- case STAC_DELL_M4_2:
- spec->num_dmics = 0;
- spec->num_smuxes = 0;
- spec->num_dmuxes = 0;
- break;
- case STAC_DELL_M4_1:
- case STAC_DELL_M4_3:
- spec->num_dmics = 1;
- spec->num_smuxes = 0;
- spec->num_dmuxes = 1;
- break;
- case STAC_HP_DV4_1222NR:
- spec->num_dmics = 1;
- /* I don't know if it needs 1 or 2 smuxes - will wait for
- * bug reports to fix if needed
- */
- spec->num_smuxes = 1;
- spec->num_dmuxes = 1;
- /* fallthrough */
- case STAC_HP_DV4:
- spec->gpio_led = 0x01;
- /* fallthrough */
- case STAC_HP_DV5:
- snd_hda_codec_set_pincfg(codec, 0x0d, 0x90170010);
- stac92xx_auto_set_pinctl(codec, 0x0d, AC_PINCTL_OUT_EN);
- /* HP dv6 gives the headphone pin as a line-out. Thus we
- * need to set hp_detect flag here to force to enable HP
- * detection.
- */
- spec->hp_detect = 1;
- break;
- case STAC_HP_HDX:
- spec->num_dmics = 1;
- spec->num_dmuxes = 1;
- spec->num_smuxes = 1;
- spec->gpio_led = 0x08;
- break;
- }
-
- if (hp_blike_system(codec->subsystem_id)) {
- pin_cfg = snd_hda_codec_get_pincfg(codec, 0x0f);
- if (get_defcfg_device(pin_cfg) == AC_JACK_LINE_OUT ||
- get_defcfg_device(pin_cfg) == AC_JACK_SPEAKER ||
- get_defcfg_device(pin_cfg) == AC_JACK_HP_OUT) {
- /* It was changed in the BIOS to just satisfy MS DTM.
- * Lets turn it back into slaved HP
- */
- pin_cfg = (pin_cfg & (~AC_DEFCFG_DEVICE))
- | (AC_JACK_HP_OUT <<
- AC_DEFCFG_DEVICE_SHIFT);
- pin_cfg = (pin_cfg & (~(AC_DEFCFG_DEF_ASSOC
- | AC_DEFCFG_SEQUENCE)))
- | 0x1f;
- snd_hda_codec_set_pincfg(codec, 0x0f, pin_cfg);
- }
- }
-
- if (find_mute_led_gpio(codec, 1))
- snd_printd("mute LED gpio %d polarity %d\n",
- spec->gpio_led,
- spec->gpio_led_polarity);
-
-#ifdef CONFIG_SND_HDA_POWER_SAVE
- if (spec->gpio_led) {
- spec->gpio_mask |= spec->gpio_led;
- spec->gpio_dir |= spec->gpio_led;
- spec->gpio_data |= spec->gpio_led;
- /* register check_power_status callback. */
- codec->patch_ops.check_power_status =
- stac92xx_hp_check_power_status;
- }
-#endif
+ snd_hda_pick_fixup(codec, stac92hd71bxx_models, stac92hd71bxx_fixup_tbl,
+ stac92hd71bxx_fixups);
+ snd_hda_apply_fixup(codec, HDA_FIXUP_ACT_PRE_PROBE);
- spec->multiout.dac_nids = spec->dac_nids;
-
- err = stac92xx_parse_auto_config(codec, 0x21, 0);
- if (!err) {
- if (spec->board_config < 0) {
- printk(KERN_WARNING "hda_codec: No auto-config is "
- "available, default to model=ref\n");
- spec->board_config = STAC_92HD71BXX_REF;
- goto again;
- }
- err = -EINVAL;
- }
+ stac_setup_gpio(codec);
+ err = stac_parse_auto_config(codec);
if (err < 0) {
- stac92xx_free(codec);
+ stac_free(codec);
return err;
}
- /* enable bass on HP dv7 */
- if (spec->board_config == STAC_HP_DV4 ||
- spec->board_config == STAC_HP_DV5) {
- unsigned int cap;
- cap = snd_hda_param_read(codec, 0x1, AC_PAR_GPIO_CAP);
- cap &= AC_GPIO_IO_COUNT;
- if (cap >= 6)
- stac_add_hp_bass_switch(codec);
- }
-
codec->proc_widget_hook = stac92hd7x_proc_hook;
+ snd_hda_apply_fixup(codec, HDA_FIXUP_ACT_PROBE);
+
return 0;
}
@@ -5874,95 +4736,17 @@ static int patch_stac922x(struct hda_codec *codec)
struct sigmatel_spec *spec;
int err;
- spec = kzalloc(sizeof(*spec), GFP_KERNEL);
- if (spec == NULL)
- return -ENOMEM;
+ err = alloc_stac_spec(codec);
+ if (err < 0)
+ return err;
- codec->no_trigger_sense = 1;
- codec->spec = spec;
+ spec = codec->spec;
spec->linear_tone_beep = 1;
- spec->num_pins = ARRAY_SIZE(stac922x_pin_nids);
- spec->pin_nids = stac922x_pin_nids;
- spec->board_config = snd_hda_check_board_config(codec, STAC_922X_MODELS,
- stac922x_models,
- stac922x_cfg_tbl);
- if (spec->board_config == STAC_INTEL_MAC_AUTO) {
- spec->gpio_mask = spec->gpio_dir = 0x03;
- spec->gpio_data = 0x03;
- /* Intel Macs have all same PCI SSID, so we need to check
- * codec SSID to distinguish the exact models
- */
- printk(KERN_INFO "hda_codec: STAC922x, Apple subsys_id=%x\n", codec->subsystem_id);
- switch (codec->subsystem_id) {
-
- case 0x106b0800:
- spec->board_config = STAC_INTEL_MAC_V1;
- break;
- case 0x106b0600:
- case 0x106b0700:
- spec->board_config = STAC_INTEL_MAC_V2;
- break;
- case 0x106b0e00:
- case 0x106b0f00:
- case 0x106b1600:
- case 0x106b1700:
- case 0x106b0200:
- case 0x106b1e00:
- spec->board_config = STAC_INTEL_MAC_V3;
- break;
- case 0x106b1a00:
- case 0x00000100:
- spec->board_config = STAC_INTEL_MAC_V4;
- break;
- case 0x106b0a00:
- case 0x106b2200:
- spec->board_config = STAC_INTEL_MAC_V5;
- break;
- default:
- spec->board_config = STAC_INTEL_MAC_V3;
- break;
- }
- }
-
- again:
- if (spec->board_config < 0)
- snd_printdd(KERN_INFO "hda_codec: %s: BIOS auto-probing.\n",
- codec->chip_name);
- else
- stac92xx_set_config_regs(codec,
- stac922x_brd_tbl[spec->board_config]);
-
- spec->adc_nids = stac922x_adc_nids;
- spec->mux_nids = stac922x_mux_nids;
- spec->num_muxes = ARRAY_SIZE(stac922x_mux_nids);
- spec->num_adcs = ARRAY_SIZE(stac922x_adc_nids);
- spec->num_dmics = 0;
- spec->num_pwrs = 0;
+ spec->gen.own_eapd_ctl = 1;
- spec->init = stac922x_core_init;
+ codec->patch_ops = stac_patch_ops;
- spec->num_caps = STAC922X_NUM_CAPS;
- spec->capvols = stac922x_capvols;
- spec->capsws = stac922x_capsws;
-
- spec->multiout.dac_nids = spec->dac_nids;
-
- err = stac92xx_parse_auto_config(codec, 0x08, 0x09);
- if (!err) {
- if (spec->board_config < 0) {
- printk(KERN_WARNING "hda_codec: No auto-config is "
- "available, default to model=ref\n");
- spec->board_config = STAC_D945_REF;
- goto again;
- }
- err = -EINVAL;
- }
- if (err < 0) {
- stac92xx_free(codec);
- return err;
- }
-
- codec->patch_ops = stac92xx_patch_ops;
+ snd_hda_add_verbs(codec, stac922x_core_init);
/* Fix Mux capture level; max to 2 */
snd_hda_override_amp_caps(codec, 0x12, HDA_OUTPUT,
@@ -5971,124 +4755,67 @@ static int patch_stac922x(struct hda_codec *codec)
(0x27 << AC_AMPCAP_STEP_SIZE_SHIFT) |
(0 << AC_AMPCAP_MUTE_SHIFT));
+ snd_hda_pick_fixup(codec, stac922x_models, stac922x_fixup_tbl,
+ stac922x_fixups);
+ snd_hda_apply_fixup(codec, HDA_FIXUP_ACT_PRE_PROBE);
+
+ err = stac_parse_auto_config(codec);
+ if (err < 0) {
+ stac_free(codec);
+ return err;
+ }
+
+ snd_hda_apply_fixup(codec, HDA_FIXUP_ACT_PROBE);
+
return 0;
}
+static const char * const stac927x_spdif_labels[] = {
+ "Digital Playback", "ADAT", "Analog Mux 1",
+ "Analog Mux 2", "Analog Mux 3", NULL
+};
+
static int patch_stac927x(struct hda_codec *codec)
{
struct sigmatel_spec *spec;
int err;
- spec = kzalloc(sizeof(*spec), GFP_KERNEL);
- if (spec == NULL)
- return -ENOMEM;
+ err = alloc_stac_spec(codec);
+ if (err < 0)
+ return err;
- codec->no_trigger_sense = 1;
- codec->spec = spec;
+ spec = codec->spec;
spec->linear_tone_beep = 1;
- codec->slave_dig_outs = stac927x_slave_dig_outs;
- spec->num_pins = ARRAY_SIZE(stac927x_pin_nids);
- spec->pin_nids = stac927x_pin_nids;
- spec->board_config = snd_hda_check_board_config(codec, STAC_927X_MODELS,
- stac927x_models,
- stac927x_cfg_tbl);
- again:
- if (spec->board_config < 0)
- snd_printdd(KERN_INFO "hda_codec: %s: BIOS auto-probing.\n",
- codec->chip_name);
- else
- stac92xx_set_config_regs(codec,
- stac927x_brd_tbl[spec->board_config]);
-
- spec->digbeep_nid = 0x23;
- spec->adc_nids = stac927x_adc_nids;
- spec->num_adcs = ARRAY_SIZE(stac927x_adc_nids);
- spec->mux_nids = stac927x_mux_nids;
- spec->num_muxes = ARRAY_SIZE(stac927x_mux_nids);
- spec->smux_nids = stac927x_smux_nids;
- spec->num_smuxes = ARRAY_SIZE(stac927x_smux_nids);
+ spec->gen.own_eapd_ctl = 1;
+ spec->have_spdif_mux = 1;
spec->spdif_labels = stac927x_spdif_labels;
- spec->dac_list = stac927x_dac_nids;
- spec->multiout.dac_nids = spec->dac_nids;
-
- if (spec->board_config != STAC_D965_REF) {
- /* GPIO0 High = Enable EAPD */
- spec->eapd_mask = spec->gpio_mask = 0x01;
- spec->gpio_dir = spec->gpio_data = 0x01;
- }
-
- switch (spec->board_config) {
- case STAC_D965_3ST:
- case STAC_D965_5ST:
- /* GPIO0 High = Enable EAPD */
- spec->num_dmics = 0;
- spec->init = d965_core_init;
- break;
- case STAC_DELL_BIOS:
- switch (codec->subsystem_id) {
- case 0x10280209:
- case 0x1028022e:
- /* correct the device field to SPDIF out */
- snd_hda_codec_set_pincfg(codec, 0x21, 0x01442070);
- break;
- }
- /* configure the analog microphone on some laptops */
- snd_hda_codec_set_pincfg(codec, 0x0c, 0x90a79130);
- /* correct the front output jack as a hp out */
- snd_hda_codec_set_pincfg(codec, 0x0f, 0x0227011f);
- /* correct the front input jack as a mic */
- snd_hda_codec_set_pincfg(codec, 0x0e, 0x02a79130);
- /* fallthru */
- case STAC_DELL_3ST:
- if (codec->subsystem_id != 0x1028022f) {
- /* GPIO2 High = Enable EAPD */
- spec->eapd_mask = spec->gpio_mask = 0x04;
- spec->gpio_dir = spec->gpio_data = 0x04;
- }
- spec->dmic_nids = stac927x_dmic_nids;
- spec->num_dmics = STAC927X_NUM_DMICS;
- spec->init = dell_3st_core_init;
- spec->dmux_nids = stac927x_dmux_nids;
- spec->num_dmuxes = ARRAY_SIZE(stac927x_dmux_nids);
- break;
- case STAC_927X_VOLKNOB:
- spec->num_dmics = 0;
- spec->init = stac927x_volknob_core_init;
- break;
- default:
- spec->num_dmics = 0;
- spec->init = stac927x_core_init;
- break;
- }
+ spec->gen.beep_nid = 0x23; /* digital beep */
- spec->num_caps = STAC927X_NUM_CAPS;
- spec->capvols = stac927x_capvols;
- spec->capsws = stac927x_capsws;
+ /* GPIO0 High = Enable EAPD */
+ spec->eapd_mask = spec->gpio_mask = 0x01;
+ spec->gpio_dir = spec->gpio_data = 0x01;
- spec->num_pwrs = 0;
- spec->aloopback_ctl = stac927x_loopback;
+ spec->aloopback_ctl = &stac927x_loopback;
spec->aloopback_mask = 0x40;
spec->aloopback_shift = 0;
spec->eapd_switch = 1;
- err = stac92xx_parse_auto_config(codec, 0x1e, 0x20);
- if (!err) {
- if (spec->board_config < 0) {
- printk(KERN_WARNING "hda_codec: No auto-config is "
- "available, default to model=ref\n");
- spec->board_config = STAC_D965_REF;
- goto again;
- }
- err = -EINVAL;
- }
+ codec->patch_ops = stac_patch_ops;
+
+ snd_hda_pick_fixup(codec, stac927x_models, stac927x_fixup_tbl,
+ stac927x_fixups);
+ snd_hda_apply_fixup(codec, HDA_FIXUP_ACT_PRE_PROBE);
+
+ if (!spec->volknob_init)
+ snd_hda_add_verbs(codec, stac927x_core_init);
+
+ err = stac_parse_auto_config(codec);
if (err < 0) {
- stac92xx_free(codec);
+ stac_free(codec);
return err;
}
- codec->patch_ops = stac92xx_patch_ops;
-
codec->proc_widget_hook = stac927x_proc_hook;
/*
@@ -6103,9 +4830,7 @@ static int patch_stac927x(struct hda_codec *codec)
*/
codec->bus->needs_damn_long_delay = 1;
- /* no jack detecion for ref-no-jd model */
- if (spec->board_config == STAC_D965_REF_NO_JD)
- spec->hp_detect = 0;
+ snd_hda_apply_fixup(codec, HDA_FIXUP_ACT_PROBE);
return 0;
}
@@ -6115,107 +4840,46 @@ static int patch_stac9205(struct hda_codec *codec)
struct sigmatel_spec *spec;
int err;
- spec = kzalloc(sizeof(*spec), GFP_KERNEL);
- if (spec == NULL)
- return -ENOMEM;
+ err = alloc_stac_spec(codec);
+ if (err < 0)
+ return err;
- codec->no_trigger_sense = 1;
- codec->spec = spec;
+ spec = codec->spec;
spec->linear_tone_beep = 1;
- spec->num_pins = ARRAY_SIZE(stac9205_pin_nids);
- spec->pin_nids = stac9205_pin_nids;
- spec->board_config = snd_hda_check_board_config(codec, STAC_9205_MODELS,
- stac9205_models,
- stac9205_cfg_tbl);
- again:
- if (spec->board_config < 0)
- snd_printdd(KERN_INFO "hda_codec: %s: BIOS auto-probing.\n",
- codec->chip_name);
- else
- stac92xx_set_config_regs(codec,
- stac9205_brd_tbl[spec->board_config]);
-
- spec->digbeep_nid = 0x23;
- spec->adc_nids = stac9205_adc_nids;
- spec->num_adcs = ARRAY_SIZE(stac9205_adc_nids);
- spec->mux_nids = stac9205_mux_nids;
- spec->num_muxes = ARRAY_SIZE(stac9205_mux_nids);
- spec->smux_nids = stac9205_smux_nids;
- spec->num_smuxes = ARRAY_SIZE(stac9205_smux_nids);
- spec->dmic_nids = stac9205_dmic_nids;
- spec->num_dmics = STAC9205_NUM_DMICS;
- spec->dmux_nids = stac9205_dmux_nids;
- spec->num_dmuxes = ARRAY_SIZE(stac9205_dmux_nids);
- spec->num_pwrs = 0;
-
- spec->init = stac9205_core_init;
- spec->aloopback_ctl = stac9205_loopback;
-
- spec->num_caps = STAC9205_NUM_CAPS;
- spec->capvols = stac9205_capvols;
- spec->capsws = stac9205_capsws;
+ spec->gen.own_eapd_ctl = 1;
+ spec->have_spdif_mux = 1;
+
+ spec->gen.beep_nid = 0x23; /* digital beep */
+
+ snd_hda_add_verbs(codec, stac9205_core_init);
+ spec->aloopback_ctl = &stac9205_loopback;
spec->aloopback_mask = 0x40;
spec->aloopback_shift = 0;
- /* Turn on/off EAPD per HP plugging */
- if (spec->board_config != STAC_9205_EAPD)
- spec->eapd_switch = 1;
- spec->multiout.dac_nids = spec->dac_nids;
- switch (spec->board_config){
- case STAC_9205_DELL_M43:
- /* Enable SPDIF in/out */
- snd_hda_codec_set_pincfg(codec, 0x1f, 0x01441030);
- snd_hda_codec_set_pincfg(codec, 0x20, 0x1c410030);
+ /* GPIO0 High = EAPD */
+ spec->eapd_mask = spec->gpio_mask = spec->gpio_dir = 0x1;
+ spec->gpio_data = 0x01;
- /* Enable unsol response for GPIO4/Dock HP connection */
- err = stac_add_event(spec, codec->afg, STAC_VREF_EVENT, 0x01);
- if (err < 0)
- return err;
- snd_hda_codec_write_cache(codec, codec->afg, 0,
- AC_VERB_SET_GPIO_UNSOLICITED_RSP_MASK, 0x10);
- snd_hda_codec_write_cache(codec, codec->afg, 0,
- AC_VERB_SET_UNSOLICITED_ENABLE,
- AC_USRSP_EN | err);
+ /* Turn on/off EAPD per HP plugging */
+ spec->eapd_switch = 1;
- spec->gpio_dir = 0x0b;
- spec->eapd_mask = 0x01;
- spec->gpio_mask = 0x1b;
- spec->gpio_mute = 0x10;
- /* GPIO0 High = EAPD, GPIO1 Low = Headphone Mute,
- * GPIO3 Low = DRM
- */
- spec->gpio_data = 0x01;
- break;
- case STAC_9205_REF:
- /* SPDIF-In enabled */
- break;
- default:
- /* GPIO0 High = EAPD */
- spec->eapd_mask = spec->gpio_mask = spec->gpio_dir = 0x1;
- spec->gpio_data = 0x01;
- break;
- }
+ codec->patch_ops = stac_patch_ops;
- err = stac92xx_parse_auto_config(codec, 0x1f, 0x20);
- if (!err) {
- if (spec->board_config < 0) {
- printk(KERN_WARNING "hda_codec: No auto-config is "
- "available, default to model=ref\n");
- spec->board_config = STAC_9205_REF;
- goto again;
- }
- err = -EINVAL;
- }
+ snd_hda_pick_fixup(codec, stac9205_models, stac9205_fixup_tbl,
+ stac9205_fixups);
+ snd_hda_apply_fixup(codec, HDA_FIXUP_ACT_PRE_PROBE);
+
+ err = stac_parse_auto_config(codec);
if (err < 0) {
- stac92xx_free(codec);
+ stac_free(codec);
return err;
}
- codec->patch_ops = stac92xx_patch_ops;
-
codec->proc_widget_hook = stac9205_proc_hook;
+ snd_hda_apply_fixup(codec, HDA_FIXUP_ACT_PROBE);
+
return 0;
}
@@ -6223,46 +4887,38 @@ static int patch_stac9205(struct hda_codec *codec)
* STAC9872 hack
*/
-static struct hda_verb stac9872_core_init[] = {
+static const struct hda_verb stac9872_core_init[] = {
{0x15, AC_VERB_SET_CONNECT_SEL, 0x1}, /* mic-sel: 0a,0d,14,02 */
{0x15, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE}, /* Mic-in -> 0x9 */
{}
};
-static hda_nid_t stac9872_pin_nids[] = {
- 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f,
- 0x11, 0x13, 0x14,
-};
-
-static hda_nid_t stac9872_adc_nids[] = {
- 0x8 /*,0x6*/
-};
-
-static hda_nid_t stac9872_mux_nids[] = {
- 0x15
-};
-
-static unsigned long stac9872_capvols[] = {
- HDA_COMPOSE_AMP_VAL(0x09, 3, 0, HDA_INPUT),
-};
-#define stac9872_capsws stac9872_capvols
-
-static unsigned int stac9872_vaio_pin_configs[9] = {
- 0x03211020, 0x411111f0, 0x411111f0, 0x03a15030,
- 0x411111f0, 0x90170110, 0x411111f0, 0x411111f0,
- 0x90a7013e
+static const struct hda_pintbl stac9872_vaio_pin_configs[] = {
+ { 0x0a, 0x03211020 },
+ { 0x0b, 0x411111f0 },
+ { 0x0c, 0x411111f0 },
+ { 0x0d, 0x03a15030 },
+ { 0x0e, 0x411111f0 },
+ { 0x0f, 0x90170110 },
+ { 0x11, 0x411111f0 },
+ { 0x13, 0x411111f0 },
+ { 0x14, 0x90a7013e },
+ {}
};
-static const char * const stac9872_models[STAC_9872_MODELS] = {
- [STAC_9872_AUTO] = "auto",
- [STAC_9872_VAIO] = "vaio",
+static const struct hda_model_fixup stac9872_models[] = {
+ { .id = STAC_9872_VAIO, .name = "vaio" },
+ {}
};
-static unsigned int *stac9872_brd_tbl[STAC_9872_MODELS] = {
- [STAC_9872_VAIO] = stac9872_vaio_pin_configs,
+static const struct hda_fixup stac9872_fixups[] = {
+ [STAC_9872_VAIO] = {
+ .type = HDA_FIXUP_PINS,
+ .v.pins = stac9872_vaio_pin_configs,
+ },
};
-static struct snd_pci_quirk stac9872_cfg_tbl[] = {
+static const struct snd_pci_quirk stac9872_fixup_tbl[] = {
SND_PCI_QUIRK_MASK(0x104d, 0xfff0, 0x81e0,
"Sony VAIO F/S", STAC_9872_VAIO),
{} /* terminator */
@@ -6273,42 +4929,30 @@ static int patch_stac9872(struct hda_codec *codec)
struct sigmatel_spec *spec;
int err;
- spec = kzalloc(sizeof(*spec), GFP_KERNEL);
- if (spec == NULL)
- return -ENOMEM;
- codec->no_trigger_sense = 1;
- codec->spec = spec;
+ err = alloc_stac_spec(codec);
+ if (err < 0)
+ return err;
+
+ spec = codec->spec;
spec->linear_tone_beep = 1;
- spec->num_pins = ARRAY_SIZE(stac9872_pin_nids);
- spec->pin_nids = stac9872_pin_nids;
-
- spec->board_config = snd_hda_check_board_config(codec, STAC_9872_MODELS,
- stac9872_models,
- stac9872_cfg_tbl);
- if (spec->board_config < 0)
- snd_printdd(KERN_INFO "hda_codec: %s: BIOS auto-probing.\n",
- codec->chip_name);
- else
- stac92xx_set_config_regs(codec,
- stac9872_brd_tbl[spec->board_config]);
-
- spec->multiout.dac_nids = spec->dac_nids;
- spec->num_adcs = ARRAY_SIZE(stac9872_adc_nids);
- spec->adc_nids = stac9872_adc_nids;
- spec->num_muxes = ARRAY_SIZE(stac9872_mux_nids);
- spec->mux_nids = stac9872_mux_nids;
- spec->init = stac9872_core_init;
- spec->num_caps = 1;
- spec->capvols = stac9872_capvols;
- spec->capsws = stac9872_capsws;
-
- err = stac92xx_parse_auto_config(codec, 0x10, 0x12);
+ spec->gen.own_eapd_ctl = 1;
+
+ codec->patch_ops = stac_patch_ops;
+
+ snd_hda_add_verbs(codec, stac9872_core_init);
+
+ snd_hda_pick_fixup(codec, stac9872_models, stac9872_fixup_tbl,
+ stac9872_fixups);
+ snd_hda_apply_fixup(codec, HDA_FIXUP_ACT_PRE_PROBE);
+
+ err = stac_parse_auto_config(codec);
if (err < 0) {
- stac92xx_free(codec);
+ stac_free(codec);
return -EINVAL;
}
- spec->input_mux = &spec->private_imux;
- codec->patch_ops = stac92xx_patch_ops;
+
+ snd_hda_apply_fixup(codec, HDA_FIXUP_ACT_PROBE);
+
return 0;
}
@@ -6316,7 +4960,7 @@ static int patch_stac9872(struct hda_codec *codec)
/*
* patch entries
*/
-static struct hda_codec_preset snd_hda_preset_sigmatel[] = {
+static const struct hda_codec_preset snd_hda_preset_sigmatel[] = {
{ .id = 0x83847690, .name = "STAC9200", .patch = patch_stac9200 },
{ .id = 0x83847882, .name = "STAC9220 A1", .patch = patch_stac922x },
{ .id = 0x83847680, .name = "STAC9221 A1", .patch = patch_stac922x },
@@ -6379,6 +5023,7 @@ static struct hda_codec_preset snd_hda_preset_sigmatel[] = {
{ .id = 0x111d7674, .name = "92HD73D1X5", .patch = patch_stac92hd73xx },
{ .id = 0x111d7675, .name = "92HD73C1X5", .patch = patch_stac92hd73xx },
{ .id = 0x111d7676, .name = "92HD73E1X5", .patch = patch_stac92hd73xx },
+ { .id = 0x111d7695, .name = "92HD95", .patch = patch_stac92hd95 },
{ .id = 0x111d76b0, .name = "92HD71B8X", .patch = patch_stac92hd71bxx },
{ .id = 0x111d76b1, .name = "92HD71B8X", .patch = patch_stac92hd71bxx },
{ .id = 0x111d76b2, .name = "92HD71B7X", .patch = patch_stac92hd71bxx },
@@ -6402,10 +5047,23 @@ static struct hda_codec_preset snd_hda_preset_sigmatel[] = {
{ .id = 0x111d76cc, .name = "92HD89F3", .patch = patch_stac92hd73xx },
{ .id = 0x111d76cd, .name = "92HD89F2", .patch = patch_stac92hd73xx },
{ .id = 0x111d76ce, .name = "92HD89F1", .patch = patch_stac92hd73xx },
+ { .id = 0x111d76df, .name = "92HD93BXX", .patch = patch_stac92hd83xxx},
{ .id = 0x111d76e0, .name = "92HD91BXX", .patch = patch_stac92hd83xxx},
{ .id = 0x111d76e3, .name = "92HD98BXX", .patch = patch_stac92hd83xxx},
{ .id = 0x111d76e5, .name = "92HD99BXX", .patch = patch_stac92hd83xxx},
{ .id = 0x111d76e7, .name = "92HD90BXX", .patch = patch_stac92hd83xxx},
+ { .id = 0x111d76e8, .name = "92HD66B1X5", .patch = patch_stac92hd83xxx},
+ { .id = 0x111d76e9, .name = "92HD66B2X5", .patch = patch_stac92hd83xxx},
+ { .id = 0x111d76ea, .name = "92HD66B3X5", .patch = patch_stac92hd83xxx},
+ { .id = 0x111d76eb, .name = "92HD66C1X5", .patch = patch_stac92hd83xxx},
+ { .id = 0x111d76ec, .name = "92HD66C2X5", .patch = patch_stac92hd83xxx},
+ { .id = 0x111d76ed, .name = "92HD66C3X5", .patch = patch_stac92hd83xxx},
+ { .id = 0x111d76ee, .name = "92HD66B1X3", .patch = patch_stac92hd83xxx},
+ { .id = 0x111d76ef, .name = "92HD66B2X3", .patch = patch_stac92hd83xxx},
+ { .id = 0x111d76f0, .name = "92HD66B3X3", .patch = patch_stac92hd83xxx},
+ { .id = 0x111d76f1, .name = "92HD66C1X3", .patch = patch_stac92hd83xxx},
+ { .id = 0x111d76f2, .name = "92HD66C2X3", .patch = patch_stac92hd83xxx},
+ { .id = 0x111d76f3, .name = "92HD66C3/65", .patch = patch_stac92hd83xxx},
{} /* terminator */
};
diff --git a/sound/pci/hda/patch_via.c b/sound/pci/hda/patch_via.c
index 1371b57c11e..778166259b3 100644
--- a/sound/pci/hda/patch_via.c
+++ b/sound/pci/hda/patch_via.c
@@ -49,41 +49,19 @@
#include <linux/init.h>
#include <linux/delay.h>
#include <linux/slab.h>
+#include <linux/module.h>
#include <sound/core.h>
#include <sound/asoundef.h>
#include "hda_codec.h"
#include "hda_local.h"
-
-#define NID_MAPPING (-1)
-
-/* amp values */
-#define AMP_VAL_IDX_SHIFT 19
-#define AMP_VAL_IDX_MASK (0x0f<<19)
+#include "hda_auto_parser.h"
+#include "hda_jack.h"
+#include "hda_generic.h"
/* Pin Widget NID */
-#define VT1708_HP_NID 0x13
-#define VT1708_DIGOUT_NID 0x14
-#define VT1708_DIGIN_NID 0x16
-#define VT1708_DIGIN_PIN 0x26
#define VT1708_HP_PIN_NID 0x20
#define VT1708_CD_PIN_NID 0x24
-#define VT1709_HP_DAC_NID 0x28
-#define VT1709_DIGOUT_NID 0x13
-#define VT1709_DIGIN_NID 0x17
-#define VT1709_DIGIN_PIN 0x25
-
-#define VT1708B_HP_NID 0x25
-#define VT1708B_DIGOUT_NID 0x12
-#define VT1708B_DIGIN_NID 0x15
-#define VT1708B_DIGIN_PIN 0x21
-
-#define VT1708S_HP_NID 0x25
-#define VT1708S_DIGOUT_NID 0x12
-
-#define VT1702_HP_NID 0x17
-#define VT1702_DIGOUT_NID 0x11
-
enum VIA_HDA_CODEC {
UNKNOWN = -1,
VT1708,
@@ -98,69 +76,51 @@ enum VIA_HDA_CODEC {
VT1716S,
VT2002P,
VT1812,
+ VT1802,
+ VT1705CF,
+ VT1808,
CODEC_TYPES,
};
+#define VT2002P_COMPATIBLE(spec) \
+ ((spec)->codec_type == VT2002P ||\
+ (spec)->codec_type == VT1812 ||\
+ (spec)->codec_type == VT1802)
+
struct via_spec {
+ struct hda_gen_spec gen;
+
/* codec parameterization */
- struct snd_kcontrol_new *mixers[6];
+ const struct snd_kcontrol_new *mixers[6];
unsigned int num_mixers;
- struct hda_verb *init_verbs[5];
+ const struct hda_verb *init_verbs[5];
unsigned int num_iverbs;
- char *stream_name_analog;
- struct hda_pcm_stream *stream_analog_playback;
- struct hda_pcm_stream *stream_analog_capture;
-
- char *stream_name_digital;
- struct hda_pcm_stream *stream_digital_playback;
- struct hda_pcm_stream *stream_digital_capture;
-
- /* playback */
- struct hda_multi_out multiout;
- hda_nid_t slave_dig_outs[2];
-
- /* capture */
- unsigned int num_adc_nids;
- hda_nid_t *adc_nids;
- hda_nid_t mux_nids[3];
- hda_nid_t dig_in_nid;
- hda_nid_t dig_in_pin;
-
- /* capture source */
- const struct hda_input_mux *input_mux;
- unsigned int cur_mux[3];
-
- /* PCM information */
- struct hda_pcm pcm_rec[3];
-
- /* dynamic controls, init_verbs and input_mux */
- struct auto_pin_cfg autocfg;
- struct snd_array kctls;
- struct hda_input_mux private_imux[2];
- hda_nid_t private_dac_nids[AUTO_CFG_MAX_OUTS];
-
/* HP mode source */
- const struct hda_input_mux *hp_mux;
- unsigned int hp_independent_mode;
- unsigned int hp_independent_mode_index;
- unsigned int smart51_enabled;
unsigned int dmic_enabled;
+ unsigned int no_pin_power_ctl;
enum VIA_HDA_CODEC codec_type;
+ /* analog low-power control */
+ bool alc_mode;
+
/* work to check hp jack state */
- struct hda_codec *codec;
- struct delayed_work vt1708_hp_work;
- int vt1708_jack_detectect;
- int vt1708_hp_present;
-#ifdef CONFIG_SND_HDA_POWER_SAVE
- struct hda_loopback_check loopback;
-#endif
+ int hp_work_active;
+ int vt1708_jack_detect;
+
+ void (*set_widgets_power_state)(struct hda_codec *codec);
+ unsigned int dac_stream_tag[4];
};
static enum VIA_HDA_CODEC get_codec_type(struct hda_codec *codec);
-static struct via_spec * via_new_spec(struct hda_codec *codec)
+static void via_playback_pcm_hook(struct hda_pcm_stream *hinfo,
+ struct hda_codec *codec,
+ struct snd_pcm_substream *substream,
+ int action);
+static void via_hp_automute(struct hda_codec *codec, struct hda_jack_tbl *tbl);
+
+static struct via_spec *via_new_spec(struct hda_codec *codec)
{
struct via_spec *spec;
@@ -169,11 +129,16 @@ static struct via_spec * via_new_spec(struct hda_codec *codec)
return NULL;
codec->spec = spec;
- spec->codec = codec;
+ snd_hda_gen_spec_init(&spec->gen);
spec->codec_type = get_codec_type(codec);
/* VT1708BCE & VT1708S are almost same */
if (spec->codec_type == VT1708BCE)
spec->codec_type = VT1708S;
+ spec->no_pin_power_ctl = 1;
+ spec->gen.indep_hp = 1;
+ spec->gen.keep_eapd_on = 1;
+ spec->gen.pcm_playback_hook = via_playback_pcm_hook;
+ spec->gen.add_stereo_mix_input = 1;
return spec;
}
@@ -218,367 +183,111 @@ static enum VIA_HDA_CODEC get_codec_type(struct hda_codec *codec)
codec_type = VT1812;
else if (dev_id == 0x0440)
codec_type = VT1708S;
+ else if ((dev_id & 0xfff) == 0x446)
+ codec_type = VT1802;
+ else if (dev_id == 0x4760)
+ codec_type = VT1705CF;
+ else if (dev_id == 0x4761 || dev_id == 0x4762)
+ codec_type = VT1808;
else
codec_type = UNKNOWN;
return codec_type;
};
-#define VIA_HP_EVENT 0x01
-#define VIA_GPIO_EVENT 0x02
-#define VIA_JACK_EVENT 0x04
-#define VIA_MONO_EVENT 0x08
-#define VIA_SPEAKER_EVENT 0x10
-#define VIA_BIND_HP_EVENT 0x20
+static void analog_low_current_mode(struct hda_codec *codec);
+static bool is_aa_path_mute(struct hda_codec *codec);
-enum {
- VIA_CTL_WIDGET_VOL,
- VIA_CTL_WIDGET_MUTE,
- VIA_CTL_WIDGET_ANALOG_MUTE,
- VIA_CTL_WIDGET_BIND_PIN_MUTE,
-};
+#define hp_detect_with_aa(codec) \
+ (snd_hda_get_bool_hint(codec, "analog_loopback_hp_detect") == 1 && \
+ !is_aa_path_mute(codec))
-enum {
- AUTO_SEQ_FRONT = 0,
- AUTO_SEQ_SURROUND,
- AUTO_SEQ_CENLFE,
- AUTO_SEQ_SIDE
-};
-
-static void analog_low_current_mode(struct hda_codec *codec, int stream_idle);
-static void set_jack_power_state(struct hda_codec *codec);
-static int is_aa_path_mute(struct hda_codec *codec);
-
-static void vt1708_start_hp_work(struct via_spec *spec)
+static void vt1708_stop_hp_work(struct hda_codec *codec)
{
- if (spec->codec_type != VT1708 || spec->autocfg.hp_pins[0] == 0)
- return;
- snd_hda_codec_write(spec->codec, 0x1, 0, 0xf81,
- !spec->vt1708_jack_detectect);
- if (!delayed_work_pending(&spec->vt1708_hp_work))
- schedule_delayed_work(&spec->vt1708_hp_work,
- msecs_to_jiffies(100));
-}
-
-static void vt1708_stop_hp_work(struct via_spec *spec)
-{
- if (spec->codec_type != VT1708 || spec->autocfg.hp_pins[0] == 0)
- return;
- if (snd_hda_get_bool_hint(spec->codec, "analog_loopback_hp_detect") == 1
- && !is_aa_path_mute(spec->codec))
+ struct via_spec *spec = codec->spec;
+ if (spec->codec_type != VT1708 || !spec->gen.autocfg.hp_outs)
return;
- snd_hda_codec_write(spec->codec, 0x1, 0, 0xf81,
- !spec->vt1708_jack_detectect);
- cancel_delayed_work_sync(&spec->vt1708_hp_work);
-}
-
-
-static int analog_input_switch_put(struct snd_kcontrol *kcontrol,
- struct snd_ctl_elem_value *ucontrol)
-{
- int change = snd_hda_mixer_amp_switch_put(kcontrol, ucontrol);
- struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
-
- set_jack_power_state(codec);
- analog_low_current_mode(snd_kcontrol_chip(kcontrol), -1);
- if (snd_hda_get_bool_hint(codec, "analog_loopback_hp_detect") == 1) {
- if (is_aa_path_mute(codec))
- vt1708_start_hp_work(codec->spec);
- else
- vt1708_stop_hp_work(codec->spec);
+ if (spec->hp_work_active) {
+ snd_hda_codec_write(codec, 0x1, 0, 0xf81, 1);
+ codec->jackpoll_interval = 0;
+ cancel_delayed_work_sync(&codec->jackpoll_work);
+ spec->hp_work_active = false;
}
- return change;
}
-/* modify .put = snd_hda_mixer_amp_switch_put */
-#define ANALOG_INPUT_MUTE \
- { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, \
- .name = NULL, \
- .index = 0, \
- .info = snd_hda_mixer_amp_switch_info, \
- .get = snd_hda_mixer_amp_switch_get, \
- .put = analog_input_switch_put, \
- .private_value = HDA_COMPOSE_AMP_VAL(0, 3, 0, 0) }
-
-static void via_hp_bind_automute(struct hda_codec *codec);
-
-static int bind_pin_switch_put(struct snd_kcontrol *kcontrol,
- struct snd_ctl_elem_value *ucontrol)
+static void vt1708_update_hp_work(struct hda_codec *codec)
{
- struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
struct via_spec *spec = codec->spec;
- int i;
- int change = 0;
-
- long *valp = ucontrol->value.integer.value;
- int lmute, rmute;
- if (strstr(kcontrol->id.name, "Switch") == NULL) {
- snd_printd("Invalid control!\n");
- return change;
- }
- change = snd_hda_mixer_amp_switch_put(kcontrol,
- ucontrol);
- /* Get mute value */
- lmute = *valp ? 0 : HDA_AMP_MUTE;
- valp++;
- rmute = *valp ? 0 : HDA_AMP_MUTE;
-
- /* Set hp pins */
- if (!spec->hp_independent_mode) {
- for (i = 0; i < spec->autocfg.hp_outs; i++) {
- snd_hda_codec_amp_update(
- codec, spec->autocfg.hp_pins[i],
- 0, HDA_OUTPUT, 0, HDA_AMP_MUTE,
- lmute);
- snd_hda_codec_amp_update(
- codec, spec->autocfg.hp_pins[i],
- 1, HDA_OUTPUT, 0, HDA_AMP_MUTE,
- rmute);
- }
- }
-
- if (!lmute && !rmute) {
- /* Line Outs */
- for (i = 0; i < spec->autocfg.line_outs; i++)
- snd_hda_codec_amp_stereo(
- codec, spec->autocfg.line_out_pins[i],
- HDA_OUTPUT, 0, HDA_AMP_MUTE, 0);
- /* Speakers */
- for (i = 0; i < spec->autocfg.speaker_outs; i++)
- snd_hda_codec_amp_stereo(
- codec, spec->autocfg.speaker_pins[i],
- HDA_OUTPUT, 0, HDA_AMP_MUTE, 0);
- /* unmute */
- via_hp_bind_automute(codec);
-
- } else {
- if (lmute) {
- /* Mute all left channels */
- for (i = 1; i < spec->autocfg.line_outs; i++)
- snd_hda_codec_amp_update(
- codec,
- spec->autocfg.line_out_pins[i],
- 0, HDA_OUTPUT, 0, HDA_AMP_MUTE,
- lmute);
- for (i = 0; i < spec->autocfg.speaker_outs; i++)
- snd_hda_codec_amp_update(
- codec,
- spec->autocfg.speaker_pins[i],
- 0, HDA_OUTPUT, 0, HDA_AMP_MUTE,
- lmute);
- }
- if (rmute) {
- /* mute all right channels */
- for (i = 1; i < spec->autocfg.line_outs; i++)
- snd_hda_codec_amp_update(
- codec,
- spec->autocfg.line_out_pins[i],
- 1, HDA_OUTPUT, 0, HDA_AMP_MUTE,
- rmute);
- for (i = 0; i < spec->autocfg.speaker_outs; i++)
- snd_hda_codec_amp_update(
- codec,
- spec->autocfg.speaker_pins[i],
- 1, HDA_OUTPUT, 0, HDA_AMP_MUTE,
- rmute);
+ if (spec->codec_type != VT1708 || !spec->gen.autocfg.hp_outs)
+ return;
+ if (spec->vt1708_jack_detect) {
+ if (!spec->hp_work_active) {
+ codec->jackpoll_interval = msecs_to_jiffies(100);
+ snd_hda_codec_write(codec, 0x1, 0, 0xf81, 0);
+ queue_delayed_work(codec->bus->workq,
+ &codec->jackpoll_work, 0);
+ spec->hp_work_active = true;
}
- }
- return change;
+ } else if (!hp_detect_with_aa(codec))
+ vt1708_stop_hp_work(codec);
}
-#define BIND_PIN_MUTE \
- { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, \
- .name = NULL, \
- .index = 0, \
- .info = snd_hda_mixer_amp_switch_info, \
- .get = snd_hda_mixer_amp_switch_get, \
- .put = bind_pin_switch_put, \
- .private_value = HDA_COMPOSE_AMP_VAL(0, 3, 0, 0) }
-
-static struct snd_kcontrol_new via_control_templates[] = {
- HDA_CODEC_VOLUME(NULL, 0, 0, 0),
- HDA_CODEC_MUTE(NULL, 0, 0, 0),
- ANALOG_INPUT_MUTE,
- BIND_PIN_MUTE,
-};
-
-static hda_nid_t vt1708_adc_nids[2] = {
- /* ADC1-2 */
- 0x15, 0x27
-};
-
-static hda_nid_t vt1709_adc_nids[3] = {
- /* ADC1-2 */
- 0x14, 0x15, 0x16
-};
-
-static hda_nid_t vt1708B_adc_nids[2] = {
- /* ADC1-2 */
- 0x13, 0x14
-};
-
-static hda_nid_t vt1708S_adc_nids[2] = {
- /* ADC1-2 */
- 0x13, 0x14
-};
-
-static hda_nid_t vt1702_adc_nids[3] = {
- /* ADC1-2 */
- 0x12, 0x20, 0x1F
-};
-
-static hda_nid_t vt1718S_adc_nids[2] = {
- /* ADC1-2 */
- 0x10, 0x11
-};
-
-static hda_nid_t vt1716S_adc_nids[2] = {
- /* ADC1-2 */
- 0x13, 0x14
-};
-
-static hda_nid_t vt2002P_adc_nids[2] = {
- /* ADC1-2 */
- 0x10, 0x11
-};
-
-static hda_nid_t vt1812_adc_nids[2] = {
- /* ADC1-2 */
- 0x10, 0x11
-};
-
-
-/* add dynamic controls */
-static int __via_add_control(struct via_spec *spec, int type, const char *name,
- int idx, unsigned long val)
-{
- struct snd_kcontrol_new *knew;
-
- snd_array_init(&spec->kctls, sizeof(*knew), 32);
- knew = snd_array_new(&spec->kctls);
- if (!knew)
- return -ENOMEM;
- *knew = via_control_templates[type];
- knew->name = kstrdup(name, GFP_KERNEL);
- if (!knew->name)
- return -ENOMEM;
- if (get_amp_nid_(val))
- knew->subdevice = HDA_SUBDEV_AMP_FLAG;
- knew->private_value = val;
- return 0;
-}
-
-#define via_add_control(spec, type, name, val) \
- __via_add_control(spec, type, name, 0, val)
-
-static struct snd_kcontrol_new *via_clone_control(struct via_spec *spec,
- struct snd_kcontrol_new *tmpl)
-{
- struct snd_kcontrol_new *knew;
-
- snd_array_init(&spec->kctls, sizeof(*knew), 32);
- knew = snd_array_new(&spec->kctls);
- if (!knew)
- return NULL;
- *knew = *tmpl;
- knew->name = kstrdup(tmpl->name, GFP_KERNEL);
- if (!knew->name)
- return NULL;
- return knew;
-}
-
-static void via_free_kctls(struct hda_codec *codec)
+static void set_widgets_power_state(struct hda_codec *codec)
{
+#if 0 /* FIXME: the assumed connections don't match always with the
+ * actual routes by the generic parser, so better to disable
+ * the control for safety.
+ */
struct via_spec *spec = codec->spec;
-
- if (spec->kctls.list) {
- struct snd_kcontrol_new *kctl = spec->kctls.list;
- int i;
- for (i = 0; i < spec->kctls.used; i++)
- kfree(kctl[i].name);
- }
- snd_array_free(&spec->kctls);
-}
-
-/* create input playback/capture controls for the given pin */
-static int via_new_analog_input(struct via_spec *spec, const char *ctlname,
- int type_idx, int idx, int mix_nid)
-{
- char name[32];
- int err;
-
- sprintf(name, "%s Playback Volume", ctlname);
- err = __via_add_control(spec, VIA_CTL_WIDGET_VOL, name, type_idx,
- HDA_COMPOSE_AMP_VAL(mix_nid, 3, idx, HDA_INPUT));
- if (err < 0)
- return err;
- sprintf(name, "%s Playback Switch", ctlname);
- err = __via_add_control(spec, VIA_CTL_WIDGET_ANALOG_MUTE, name, type_idx,
- HDA_COMPOSE_AMP_VAL(mix_nid, 3, idx, HDA_INPUT));
- if (err < 0)
- return err;
- return 0;
+ if (spec->set_widgets_power_state)
+ spec->set_widgets_power_state(codec);
+#endif
}
-static void via_auto_set_output_and_unmute(struct hda_codec *codec,
- hda_nid_t nid, int pin_type,
- int dac_idx)
+static void update_power_state(struct hda_codec *codec, hda_nid_t nid,
+ unsigned int parm)
{
- /* set as output */
- snd_hda_codec_write(codec, nid, 0, AC_VERB_SET_PIN_WIDGET_CONTROL,
- pin_type);
- snd_hda_codec_write(codec, nid, 0, AC_VERB_SET_AMP_GAIN_MUTE,
- AMP_OUT_UNMUTE);
- if (snd_hda_query_pin_caps(codec, nid) & AC_PINCAP_EAPD)
- snd_hda_codec_write(codec, nid, 0,
- AC_VERB_SET_EAPD_BTLENABLE, 0x02);
+ if (snd_hda_check_power_state(codec, nid, parm))
+ return;
+ snd_hda_codec_write(codec, nid, 0, AC_VERB_SET_POWER_STATE, parm);
}
-
-static void via_auto_init_multi_out(struct hda_codec *codec)
+static void update_conv_power_state(struct hda_codec *codec, hda_nid_t nid,
+ unsigned int parm, unsigned int index)
{
struct via_spec *spec = codec->spec;
- int i;
+ unsigned int format;
+
+ if (snd_hda_check_power_state(codec, nid, parm))
+ return;
+ format = snd_hda_codec_read(codec, nid, 0, AC_VERB_GET_CONV, 0);
+ if (format && (spec->dac_stream_tag[index] != format))
+ spec->dac_stream_tag[index] = format;
- for (i = 0; i <= AUTO_SEQ_SIDE; i++) {
- hda_nid_t nid = spec->autocfg.line_out_pins[i];
- if (nid)
- via_auto_set_output_and_unmute(codec, nid, PIN_OUT, i);
+ snd_hda_codec_write(codec, nid, 0, AC_VERB_SET_POWER_STATE, parm);
+ if (parm == AC_PWRST_D0) {
+ format = snd_hda_codec_read(codec, nid, 0, AC_VERB_GET_CONV, 0);
+ if (!format && (spec->dac_stream_tag[index] != format))
+ snd_hda_codec_write(codec, nid, 0,
+ AC_VERB_SET_CHANNEL_STREAMID,
+ spec->dac_stream_tag[index]);
}
}
-static void via_auto_init_hp_out(struct hda_codec *codec)
+static bool smart51_enabled(struct hda_codec *codec)
{
struct via_spec *spec = codec->spec;
- hda_nid_t pin;
- int i;
-
- for (i = 0; i < spec->autocfg.hp_outs; i++) {
- pin = spec->autocfg.hp_pins[i];
- if (pin) /* connect to front */
- via_auto_set_output_and_unmute(codec, pin, PIN_HP, 0);
- }
+ return spec->gen.ext_channel_count > 2;
}
-static int is_smart51_pins(struct via_spec *spec, hda_nid_t pin);
-
-static void via_auto_init_analog_input(struct hda_codec *codec)
+static bool is_smart51_pins(struct hda_codec *codec, hda_nid_t pin)
{
struct via_spec *spec = codec->spec;
- const struct auto_pin_cfg *cfg = &spec->autocfg;
- unsigned int ctl;
int i;
- for (i = 0; i < cfg->num_inputs; i++) {
- hda_nid_t nid = cfg->inputs[i].pin;
- if (spec->smart51_enabled && is_smart51_pins(spec, nid))
- ctl = PIN_OUT;
- else if (cfg->inputs[i].type == AUTO_PIN_MIC)
- ctl = PIN_VREF50;
- else
- ctl = PIN_IN;
- snd_hda_codec_write(codec, nid, 0,
- AC_VERB_SET_PIN_WIDGET_CONTROL, ctl);
- }
+ for (i = 0; i < spec->gen.multi_ios; i++)
+ if (spec->gen.multi_io[i].pin == pin)
+ return true;
+ return false;
}
static void set_pin_power_state(struct hda_codec *codec, hda_nid_t nid,
@@ -589,9 +298,13 @@ static void set_pin_power_state(struct hda_codec *codec, hda_nid_t nid,
unsigned no_presence = (def_conf & AC_DEFCFG_MISC)
>> AC_DEFCFG_MISC_SHIFT
& AC_DEFCFG_MISC_NO_PRESENCE; /* do not support pin sense */
- unsigned present = snd_hda_jack_detect(codec, nid);
struct via_spec *spec = codec->spec;
- if ((spec->smart51_enabled && is_smart51_pins(spec, nid))
+ unsigned present = 0;
+
+ no_presence |= spec->no_pin_power_ctl;
+ if (!no_presence)
+ present = snd_hda_jack_detect(codec, nid);
+ if ((smart51_enabled(codec) && is_smart51_pins(codec, nid))
|| ((no_presence || present)
&& get_defcfg_connect(def_conf) != AC_JACK_PORT_NONE)) {
*affected_parm = AC_PWRST_D0; /* if it's connected */
@@ -599,990 +312,86 @@ static void set_pin_power_state(struct hda_codec *codec, hda_nid_t nid,
} else
parm = AC_PWRST_D3;
- snd_hda_codec_write(codec, nid, 0, AC_VERB_SET_POWER_STATE, parm);
+ update_power_state(codec, nid, parm);
}
-static void set_jack_power_state(struct hda_codec *codec)
+static int via_pin_power_ctl_info(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_info *uinfo)
{
- struct via_spec *spec = codec->spec;
- int imux_is_smixer;
- unsigned int parm;
-
- if (spec->codec_type == VT1702) {
- imux_is_smixer = snd_hda_codec_read(
- codec, 0x13, 0, AC_VERB_GET_CONNECT_SEL, 0x00) == 3;
- /* inputs */
- /* PW 1/2/5 (14h/15h/18h) */
- parm = AC_PWRST_D3;
- set_pin_power_state(codec, 0x14, &parm);
- set_pin_power_state(codec, 0x15, &parm);
- set_pin_power_state(codec, 0x18, &parm);
- if (imux_is_smixer)
- parm = AC_PWRST_D0; /* SW0 = stereo mixer (idx 3) */
- /* SW0 (13h), AIW 0/1/2 (12h/1fh/20h) */
- snd_hda_codec_write(codec, 0x13, 0, AC_VERB_SET_POWER_STATE,
- parm);
- snd_hda_codec_write(codec, 0x12, 0, AC_VERB_SET_POWER_STATE,
- parm);
- snd_hda_codec_write(codec, 0x1f, 0, AC_VERB_SET_POWER_STATE,
- parm);
- snd_hda_codec_write(codec, 0x20, 0, AC_VERB_SET_POWER_STATE,
- parm);
-
- /* outputs */
- /* PW 3/4 (16h/17h) */
- parm = AC_PWRST_D3;
- set_pin_power_state(codec, 0x16, &parm);
- set_pin_power_state(codec, 0x17, &parm);
- /* MW0 (1ah), AOW 0/1 (10h/1dh) */
- snd_hda_codec_write(codec, 0x1a, 0, AC_VERB_SET_POWER_STATE,
- imux_is_smixer ? AC_PWRST_D0 : parm);
- snd_hda_codec_write(codec, 0x10, 0, AC_VERB_SET_POWER_STATE,
- parm);
- snd_hda_codec_write(codec, 0x1d, 0, AC_VERB_SET_POWER_STATE,
- parm);
- } else if (spec->codec_type == VT1708B_8CH
- || spec->codec_type == VT1708B_4CH
- || spec->codec_type == VT1708S) {
- /* SW0 (17h) = stereo mixer */
- int is_8ch = spec->codec_type != VT1708B_4CH;
- imux_is_smixer = snd_hda_codec_read(
- codec, 0x17, 0, AC_VERB_GET_CONNECT_SEL, 0x00)
- == ((spec->codec_type == VT1708S) ? 5 : 0);
- /* inputs */
- /* PW 1/2/5 (1ah/1bh/1eh) */
- parm = AC_PWRST_D3;
- set_pin_power_state(codec, 0x1a, &parm);
- set_pin_power_state(codec, 0x1b, &parm);
- set_pin_power_state(codec, 0x1e, &parm);
- if (imux_is_smixer)
- parm = AC_PWRST_D0;
- /* SW0 (17h), AIW 0/1 (13h/14h) */
- snd_hda_codec_write(codec, 0x17, 0, AC_VERB_SET_POWER_STATE,
- parm);
- snd_hda_codec_write(codec, 0x13, 0, AC_VERB_SET_POWER_STATE,
- parm);
- snd_hda_codec_write(codec, 0x14, 0, AC_VERB_SET_POWER_STATE,
- parm);
-
- /* outputs */
- /* PW0 (19h), SW1 (18h), AOW1 (11h) */
- parm = AC_PWRST_D3;
- set_pin_power_state(codec, 0x19, &parm);
- if (spec->smart51_enabled)
- parm = AC_PWRST_D0;
- snd_hda_codec_write(codec, 0x18, 0, AC_VERB_SET_POWER_STATE,
- parm);
- snd_hda_codec_write(codec, 0x11, 0, AC_VERB_SET_POWER_STATE,
- parm);
-
- /* PW6 (22h), SW2 (26h), AOW2 (24h) */
- if (is_8ch) {
- parm = AC_PWRST_D3;
- set_pin_power_state(codec, 0x22, &parm);
- if (spec->smart51_enabled)
- parm = AC_PWRST_D0;
- snd_hda_codec_write(codec, 0x26, 0,
- AC_VERB_SET_POWER_STATE, parm);
- snd_hda_codec_write(codec, 0x24, 0,
- AC_VERB_SET_POWER_STATE, parm);
- }
-
- /* PW 3/4/7 (1ch/1dh/23h) */
- parm = AC_PWRST_D3;
- /* force to D0 for internal Speaker */
- set_pin_power_state(codec, 0x1c, &parm);
- set_pin_power_state(codec, 0x1d, &parm);
- if (is_8ch)
- set_pin_power_state(codec, 0x23, &parm);
- /* MW0 (16h), Sw3 (27h), AOW 0/3 (10h/25h) */
- snd_hda_codec_write(codec, 0x16, 0, AC_VERB_SET_POWER_STATE,
- imux_is_smixer ? AC_PWRST_D0 : parm);
- snd_hda_codec_write(codec, 0x10, 0, AC_VERB_SET_POWER_STATE,
- parm);
- if (is_8ch) {
- snd_hda_codec_write(codec, 0x25, 0,
- AC_VERB_SET_POWER_STATE, parm);
- snd_hda_codec_write(codec, 0x27, 0,
- AC_VERB_SET_POWER_STATE, parm);
- }
- } else if (spec->codec_type == VT1718S) {
- /* MUX6 (1eh) = stereo mixer */
- imux_is_smixer = snd_hda_codec_read(
- codec, 0x1e, 0, AC_VERB_GET_CONNECT_SEL, 0x00) == 5;
- /* inputs */
- /* PW 5/6/7 (29h/2ah/2bh) */
- parm = AC_PWRST_D3;
- set_pin_power_state(codec, 0x29, &parm);
- set_pin_power_state(codec, 0x2a, &parm);
- set_pin_power_state(codec, 0x2b, &parm);
- if (imux_is_smixer)
- parm = AC_PWRST_D0;
- /* MUX6/7 (1eh/1fh), AIW 0/1 (10h/11h) */
- snd_hda_codec_write(codec, 0x1e, 0, AC_VERB_SET_POWER_STATE,
- parm);
- snd_hda_codec_write(codec, 0x1f, 0, AC_VERB_SET_POWER_STATE,
- parm);
- snd_hda_codec_write(codec, 0x10, 0, AC_VERB_SET_POWER_STATE,
- parm);
- snd_hda_codec_write(codec, 0x11, 0, AC_VERB_SET_POWER_STATE,
- parm);
-
- /* outputs */
- /* PW3 (27h), MW2 (1ah), AOW3 (bh) */
- parm = AC_PWRST_D3;
- set_pin_power_state(codec, 0x27, &parm);
- snd_hda_codec_write(codec, 0x1a, 0, AC_VERB_SET_POWER_STATE,
- parm);
- snd_hda_codec_write(codec, 0xb, 0, AC_VERB_SET_POWER_STATE,
- parm);
-
- /* PW2 (26h), AOW2 (ah) */
- parm = AC_PWRST_D3;
- set_pin_power_state(codec, 0x26, &parm);
- snd_hda_codec_write(codec, 0xa, 0, AC_VERB_SET_POWER_STATE,
- parm);
-
- /* PW0/1 (24h/25h) */
- parm = AC_PWRST_D3;
- set_pin_power_state(codec, 0x24, &parm);
- set_pin_power_state(codec, 0x25, &parm);
- if (!spec->hp_independent_mode) /* check for redirected HP */
- set_pin_power_state(codec, 0x28, &parm);
- snd_hda_codec_write(codec, 0x8, 0, AC_VERB_SET_POWER_STATE,
- parm);
- snd_hda_codec_write(codec, 0x9, 0, AC_VERB_SET_POWER_STATE,
- parm);
- /* MW9 (21h), Mw2 (1ah), AOW0 (8h) */
- snd_hda_codec_write(codec, 0x21, 0, AC_VERB_SET_POWER_STATE,
- imux_is_smixer ? AC_PWRST_D0 : parm);
- if (spec->hp_independent_mode) {
- /* PW4 (28h), MW3 (1bh), MUX1(34h), AOW4 (ch) */
- parm = AC_PWRST_D3;
- set_pin_power_state(codec, 0x28, &parm);
- snd_hda_codec_write(codec, 0x1b, 0,
- AC_VERB_SET_POWER_STATE, parm);
- snd_hda_codec_write(codec, 0x34, 0,
- AC_VERB_SET_POWER_STATE, parm);
- snd_hda_codec_write(codec, 0xc, 0,
- AC_VERB_SET_POWER_STATE, parm);
- }
- } else if (spec->codec_type == VT1716S) {
- unsigned int mono_out, present;
- /* SW0 (17h) = stereo mixer */
- imux_is_smixer = snd_hda_codec_read(
- codec, 0x17, 0, AC_VERB_GET_CONNECT_SEL, 0x00) == 5;
- /* inputs */
- /* PW 1/2/5 (1ah/1bh/1eh) */
- parm = AC_PWRST_D3;
- set_pin_power_state(codec, 0x1a, &parm);
- set_pin_power_state(codec, 0x1b, &parm);
- set_pin_power_state(codec, 0x1e, &parm);
- if (imux_is_smixer)
- parm = AC_PWRST_D0;
- /* SW0 (17h), AIW0(13h) */
- snd_hda_codec_write(codec, 0x17, 0, AC_VERB_SET_POWER_STATE,
- parm);
- snd_hda_codec_write(codec, 0x13, 0, AC_VERB_SET_POWER_STATE,
- parm);
-
- parm = AC_PWRST_D3;
- set_pin_power_state(codec, 0x1e, &parm);
- /* PW11 (22h) */
- if (spec->dmic_enabled)
- set_pin_power_state(codec, 0x22, &parm);
- else
- snd_hda_codec_write(
- codec, 0x22, 0,
- AC_VERB_SET_POWER_STATE, AC_PWRST_D3);
-
- /* SW2(26h), AIW1(14h) */
- snd_hda_codec_write(codec, 0x26, 0, AC_VERB_SET_POWER_STATE,
- parm);
- snd_hda_codec_write(codec, 0x14, 0, AC_VERB_SET_POWER_STATE,
- parm);
-
- /* outputs */
- /* PW0 (19h), SW1 (18h), AOW1 (11h) */
- parm = AC_PWRST_D3;
- set_pin_power_state(codec, 0x19, &parm);
- /* Smart 5.1 PW2(1bh) */
- if (spec->smart51_enabled)
- set_pin_power_state(codec, 0x1b, &parm);
- snd_hda_codec_write(codec, 0x18, 0, AC_VERB_SET_POWER_STATE,
- parm);
- snd_hda_codec_write(codec, 0x11, 0, AC_VERB_SET_POWER_STATE,
- parm);
-
- /* PW7 (23h), SW3 (27h), AOW3 (25h) */
- parm = AC_PWRST_D3;
- set_pin_power_state(codec, 0x23, &parm);
- /* Smart 5.1 PW1(1ah) */
- if (spec->smart51_enabled)
- set_pin_power_state(codec, 0x1a, &parm);
- snd_hda_codec_write(codec, 0x27, 0, AC_VERB_SET_POWER_STATE,
- parm);
-
- /* Smart 5.1 PW5(1eh) */
- if (spec->smart51_enabled)
- set_pin_power_state(codec, 0x1e, &parm);
- snd_hda_codec_write(codec, 0x25, 0, AC_VERB_SET_POWER_STATE,
- parm);
-
- /* Mono out */
- /* SW4(28h)->MW1(29h)-> PW12 (2ah)*/
- present = snd_hda_jack_detect(codec, 0x1c);
- if (present)
- mono_out = 0;
- else {
- present = snd_hda_jack_detect(codec, 0x1d);
- if (!spec->hp_independent_mode && present)
- mono_out = 0;
- else
- mono_out = 1;
- }
- parm = mono_out ? AC_PWRST_D0 : AC_PWRST_D3;
- snd_hda_codec_write(codec, 0x28, 0, AC_VERB_SET_POWER_STATE,
- parm);
- snd_hda_codec_write(codec, 0x29, 0, AC_VERB_SET_POWER_STATE,
- parm);
- snd_hda_codec_write(codec, 0x2a, 0, AC_VERB_SET_POWER_STATE,
- parm);
-
- /* PW 3/4 (1ch/1dh) */
- parm = AC_PWRST_D3;
- set_pin_power_state(codec, 0x1c, &parm);
- set_pin_power_state(codec, 0x1d, &parm);
- /* HP Independent Mode, power on AOW3 */
- if (spec->hp_independent_mode)
- snd_hda_codec_write(codec, 0x25, 0,
- AC_VERB_SET_POWER_STATE, parm);
-
- /* force to D0 for internal Speaker */
- /* MW0 (16h), AOW0 (10h) */
- snd_hda_codec_write(codec, 0x16, 0, AC_VERB_SET_POWER_STATE,
- imux_is_smixer ? AC_PWRST_D0 : parm);
- snd_hda_codec_write(codec, 0x10, 0, AC_VERB_SET_POWER_STATE,
- mono_out ? AC_PWRST_D0 : parm);
- } else if (spec->codec_type == VT2002P) {
- unsigned int present;
- /* MUX9 (1eh) = stereo mixer */
- imux_is_smixer = snd_hda_codec_read(
- codec, 0x1e, 0, AC_VERB_GET_CONNECT_SEL, 0x00) == 3;
- /* inputs */
- /* PW 5/6/7 (29h/2ah/2bh) */
- parm = AC_PWRST_D3;
- set_pin_power_state(codec, 0x29, &parm);
- set_pin_power_state(codec, 0x2a, &parm);
- set_pin_power_state(codec, 0x2b, &parm);
- if (imux_is_smixer)
- parm = AC_PWRST_D0;
- /* MUX9/10 (1eh/1fh), AIW 0/1 (10h/11h) */
- snd_hda_codec_write(codec, 0x1e, 0,
- AC_VERB_SET_POWER_STATE, parm);
- snd_hda_codec_write(codec, 0x1f, 0,
- AC_VERB_SET_POWER_STATE, parm);
- snd_hda_codec_write(codec, 0x10, 0,
- AC_VERB_SET_POWER_STATE, parm);
- snd_hda_codec_write(codec, 0x11, 0,
- AC_VERB_SET_POWER_STATE, parm);
-
- /* outputs */
- /* AOW0 (8h)*/
- snd_hda_codec_write(codec, 0x8, 0,
- AC_VERB_SET_POWER_STATE, AC_PWRST_D0);
-
- /* PW4 (26h), MW4 (1ch), MUX4(37h) */
- parm = AC_PWRST_D3;
- set_pin_power_state(codec, 0x26, &parm);
- snd_hda_codec_write(codec, 0x1c, 0,
- AC_VERB_SET_POWER_STATE, parm);
- snd_hda_codec_write(codec, 0x37,
- 0, AC_VERB_SET_POWER_STATE, parm);
-
- /* PW1 (25h), MW1 (19h), MUX1(35h), AOW1 (9h) */
- parm = AC_PWRST_D3;
- set_pin_power_state(codec, 0x25, &parm);
- snd_hda_codec_write(codec, 0x19, 0,
- AC_VERB_SET_POWER_STATE, parm);
- snd_hda_codec_write(codec, 0x35, 0,
- AC_VERB_SET_POWER_STATE, parm);
- if (spec->hp_independent_mode) {
- snd_hda_codec_write(codec, 0x9, 0,
- AC_VERB_SET_POWER_STATE, parm);
- }
-
- /* Class-D */
- /* PW0 (24h), MW0(18h), MUX0(34h) */
- present = snd_hda_jack_detect(codec, 0x25);
- parm = AC_PWRST_D3;
- set_pin_power_state(codec, 0x24, &parm);
- if (present) {
- snd_hda_codec_write(
- codec, 0x18, 0,
- AC_VERB_SET_POWER_STATE, AC_PWRST_D3);
- snd_hda_codec_write(
- codec, 0x34, 0,
- AC_VERB_SET_POWER_STATE, AC_PWRST_D3);
- } else {
- snd_hda_codec_write(
- codec, 0x18, 0,
- AC_VERB_SET_POWER_STATE, AC_PWRST_D0);
- snd_hda_codec_write(
- codec, 0x34, 0,
- AC_VERB_SET_POWER_STATE, AC_PWRST_D0);
- }
-
- /* Mono Out */
- /* PW15 (31h), MW8(17h), MUX8(3bh) */
- present = snd_hda_jack_detect(codec, 0x26);
- parm = AC_PWRST_D3;
- set_pin_power_state(codec, 0x31, &parm);
- if (present) {
- snd_hda_codec_write(
- codec, 0x17, 0,
- AC_VERB_SET_POWER_STATE, AC_PWRST_D3);
- snd_hda_codec_write(
- codec, 0x3b, 0,
- AC_VERB_SET_POWER_STATE, AC_PWRST_D3);
- } else {
- snd_hda_codec_write(
- codec, 0x17, 0,
- AC_VERB_SET_POWER_STATE, AC_PWRST_D0);
- snd_hda_codec_write(
- codec, 0x3b, 0,
- AC_VERB_SET_POWER_STATE, AC_PWRST_D0);
- }
-
- /* MW9 (21h) */
- if (imux_is_smixer || !is_aa_path_mute(codec))
- snd_hda_codec_write(
- codec, 0x21, 0,
- AC_VERB_SET_POWER_STATE, AC_PWRST_D0);
- else
- snd_hda_codec_write(
- codec, 0x21, 0,
- AC_VERB_SET_POWER_STATE, AC_PWRST_D3);
- } else if (spec->codec_type == VT1812) {
- unsigned int present;
- /* MUX10 (1eh) = stereo mixer */
- imux_is_smixer = snd_hda_codec_read(
- codec, 0x1e, 0, AC_VERB_GET_CONNECT_SEL, 0x00) == 5;
- /* inputs */
- /* PW 5/6/7 (29h/2ah/2bh) */
- parm = AC_PWRST_D3;
- set_pin_power_state(codec, 0x29, &parm);
- set_pin_power_state(codec, 0x2a, &parm);
- set_pin_power_state(codec, 0x2b, &parm);
- if (imux_is_smixer)
- parm = AC_PWRST_D0;
- /* MUX10/11 (1eh/1fh), AIW 0/1 (10h/11h) */
- snd_hda_codec_write(codec, 0x1e, 0,
- AC_VERB_SET_POWER_STATE, parm);
- snd_hda_codec_write(codec, 0x1f, 0,
- AC_VERB_SET_POWER_STATE, parm);
- snd_hda_codec_write(codec, 0x10, 0,
- AC_VERB_SET_POWER_STATE, parm);
- snd_hda_codec_write(codec, 0x11, 0,
- AC_VERB_SET_POWER_STATE, parm);
-
- /* outputs */
- /* AOW0 (8h)*/
- snd_hda_codec_write(codec, 0x8, 0,
- AC_VERB_SET_POWER_STATE, AC_PWRST_D0);
-
- /* PW4 (28h), MW4 (18h), MUX4(38h) */
- parm = AC_PWRST_D3;
- set_pin_power_state(codec, 0x28, &parm);
- snd_hda_codec_write(codec, 0x18, 0,
- AC_VERB_SET_POWER_STATE, parm);
- snd_hda_codec_write(codec, 0x38, 0,
- AC_VERB_SET_POWER_STATE, parm);
-
- /* PW1 (25h), MW1 (15h), MUX1(35h), AOW1 (9h) */
- parm = AC_PWRST_D3;
- set_pin_power_state(codec, 0x25, &parm);
- snd_hda_codec_write(codec, 0x15, 0,
- AC_VERB_SET_POWER_STATE, parm);
- snd_hda_codec_write(codec, 0x35, 0,
- AC_VERB_SET_POWER_STATE, parm);
- if (spec->hp_independent_mode) {
- snd_hda_codec_write(codec, 0x9, 0,
- AC_VERB_SET_POWER_STATE, parm);
- }
-
- /* Internal Speaker */
- /* PW0 (24h), MW0(14h), MUX0(34h) */
- present = snd_hda_jack_detect(codec, 0x25);
- parm = AC_PWRST_D3;
- set_pin_power_state(codec, 0x24, &parm);
- if (present) {
- snd_hda_codec_write(codec, 0x14, 0,
- AC_VERB_SET_POWER_STATE,
- AC_PWRST_D3);
- snd_hda_codec_write(codec, 0x34, 0,
- AC_VERB_SET_POWER_STATE,
- AC_PWRST_D3);
- } else {
- snd_hda_codec_write(codec, 0x14, 0,
- AC_VERB_SET_POWER_STATE,
- AC_PWRST_D0);
- snd_hda_codec_write(codec, 0x34, 0,
- AC_VERB_SET_POWER_STATE,
- AC_PWRST_D0);
- }
- /* Mono Out */
- /* PW13 (31h), MW13(1ch), MUX13(3ch), MW14(3eh) */
- present = snd_hda_jack_detect(codec, 0x28);
- parm = AC_PWRST_D3;
- set_pin_power_state(codec, 0x31, &parm);
- if (present) {
- snd_hda_codec_write(codec, 0x1c, 0,
- AC_VERB_SET_POWER_STATE,
- AC_PWRST_D3);
- snd_hda_codec_write(codec, 0x3c, 0,
- AC_VERB_SET_POWER_STATE,
- AC_PWRST_D3);
- snd_hda_codec_write(codec, 0x3e, 0,
- AC_VERB_SET_POWER_STATE,
- AC_PWRST_D3);
- } else {
- snd_hda_codec_write(codec, 0x1c, 0,
- AC_VERB_SET_POWER_STATE,
- AC_PWRST_D0);
- snd_hda_codec_write(codec, 0x3c, 0,
- AC_VERB_SET_POWER_STATE,
- AC_PWRST_D0);
- snd_hda_codec_write(codec, 0x3e, 0,
- AC_VERB_SET_POWER_STATE,
- AC_PWRST_D0);
- }
-
- /* PW15 (33h), MW15 (1dh), MUX15(3dh) */
- parm = AC_PWRST_D3;
- set_pin_power_state(codec, 0x33, &parm);
- snd_hda_codec_write(codec, 0x1d, 0,
- AC_VERB_SET_POWER_STATE, parm);
- snd_hda_codec_write(codec, 0x3d, 0,
- AC_VERB_SET_POWER_STATE, parm);
-
- /* MW9 (21h) */
- if (imux_is_smixer || !is_aa_path_mute(codec))
- snd_hda_codec_write(
- codec, 0x21, 0,
- AC_VERB_SET_POWER_STATE, AC_PWRST_D0);
- else
- snd_hda_codec_write(
- codec, 0x21, 0,
- AC_VERB_SET_POWER_STATE, AC_PWRST_D3);
- }
+ return snd_hda_enum_bool_helper_info(kcontrol, uinfo);
}
-/*
- * input MUX handling
- */
-static int via_mux_enum_info(struct snd_kcontrol *kcontrol,
- struct snd_ctl_elem_info *uinfo)
+static int via_pin_power_ctl_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
{
struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
struct via_spec *spec = codec->spec;
- return snd_hda_input_mux_info(spec->input_mux, uinfo);
-}
-
-static int via_mux_enum_get(struct snd_kcontrol *kcontrol,
- struct snd_ctl_elem_value *ucontrol)
-{
- struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
- struct via_spec *spec = codec->spec;
- unsigned int adc_idx = snd_ctl_get_ioffidx(kcontrol, &ucontrol->id);
-
- ucontrol->value.enumerated.item[0] = spec->cur_mux[adc_idx];
+ ucontrol->value.enumerated.item[0] = !spec->no_pin_power_ctl;
return 0;
}
-static int via_mux_enum_put(struct snd_kcontrol *kcontrol,
- struct snd_ctl_elem_value *ucontrol)
+static int via_pin_power_ctl_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
{
struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
struct via_spec *spec = codec->spec;
- unsigned int adc_idx = snd_ctl_get_ioffidx(kcontrol, &ucontrol->id);
- int ret;
-
- if (!spec->mux_nids[adc_idx])
- return -EINVAL;
- /* switch to D0 beofre change index */
- if (snd_hda_codec_read(codec, spec->mux_nids[adc_idx], 0,
- AC_VERB_GET_POWER_STATE, 0x00) != AC_PWRST_D0)
- snd_hda_codec_write(codec, spec->mux_nids[adc_idx], 0,
- AC_VERB_SET_POWER_STATE, AC_PWRST_D0);
-
- ret = snd_hda_input_mux_put(codec, spec->input_mux, ucontrol,
- spec->mux_nids[adc_idx],
- &spec->cur_mux[adc_idx]);
- /* update jack power state */
- set_jack_power_state(codec);
-
- return ret;
-}
+ unsigned int val = !ucontrol->value.enumerated.item[0];
-static int via_independent_hp_info(struct snd_kcontrol *kcontrol,
- struct snd_ctl_elem_info *uinfo)
-{
- struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
- struct via_spec *spec = codec->spec;
- return snd_hda_input_mux_info(spec->hp_mux, uinfo);
-}
-
-static int via_independent_hp_get(struct snd_kcontrol *kcontrol,
- struct snd_ctl_elem_value *ucontrol)
-{
- struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
- hda_nid_t nid = kcontrol->private_value;
- unsigned int pinsel;
-
- /* use !! to translate conn sel 2 for VT1718S */
- pinsel = !!snd_hda_codec_read(codec, nid, 0,
- AC_VERB_GET_CONNECT_SEL,
- 0x00);
- ucontrol->value.enumerated.item[0] = pinsel;
-
- return 0;
-}
-
-static void activate_ctl(struct hda_codec *codec, const char *name, int active)
-{
- struct snd_kcontrol *ctl = snd_hda_find_mixer_ctl(codec, name);
- if (ctl) {
- ctl->vd[0].access &= ~SNDRV_CTL_ELEM_ACCESS_INACTIVE;
- ctl->vd[0].access |= active
- ? 0 : SNDRV_CTL_ELEM_ACCESS_INACTIVE;
- snd_ctl_notify(codec->bus->card,
- SNDRV_CTL_EVENT_MASK_VALUE, &ctl->id);
- }
-}
-
-static hda_nid_t side_mute_channel(struct via_spec *spec)
-{
- switch (spec->codec_type) {
- case VT1708: return 0x1b;
- case VT1709_10CH: return 0x29;
- case VT1708B_8CH: /* fall thru */
- case VT1708S: return 0x27;
- default: return 0;
- }
-}
-
-static int update_side_mute_status(struct hda_codec *codec)
-{
- /* mute side channel */
- struct via_spec *spec = codec->spec;
- unsigned int parm = spec->hp_independent_mode
- ? AMP_OUT_MUTE : AMP_OUT_UNMUTE;
- hda_nid_t sw3 = side_mute_channel(spec);
-
- if (sw3)
- snd_hda_codec_write(codec, sw3, 0, AC_VERB_SET_AMP_GAIN_MUTE,
- parm);
- return 0;
-}
-
-static int via_independent_hp_put(struct snd_kcontrol *kcontrol,
- struct snd_ctl_elem_value *ucontrol)
-{
- struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
- struct via_spec *spec = codec->spec;
- hda_nid_t nid = kcontrol->private_value;
- unsigned int pinsel = ucontrol->value.enumerated.item[0];
- /* Get Independent Mode index of headphone pin widget */
- spec->hp_independent_mode = spec->hp_independent_mode_index == pinsel
- ? 1 : 0;
- if (spec->codec_type == VT1718S)
- snd_hda_codec_write(codec, nid, 0,
- AC_VERB_SET_CONNECT_SEL, pinsel ? 2 : 0);
- else
- snd_hda_codec_write(codec, nid, 0,
- AC_VERB_SET_CONNECT_SEL, pinsel);
-
- if (spec->codec_type == VT1812)
- snd_hda_codec_write(codec, 0x35, 0,
- AC_VERB_SET_CONNECT_SEL, pinsel);
- if (spec->multiout.hp_nid && spec->multiout.hp_nid
- != spec->multiout.dac_nids[HDA_FRONT])
- snd_hda_codec_setup_stream(codec, spec->multiout.hp_nid,
- 0, 0, 0);
-
- update_side_mute_status(codec);
- /* update HP volume/swtich active state */
- if (spec->codec_type == VT1708S
- || spec->codec_type == VT1702
- || spec->codec_type == VT1718S
- || spec->codec_type == VT1716S
- || spec->codec_type == VT2002P
- || spec->codec_type == VT1812) {
- activate_ctl(codec, "Headphone Playback Volume",
- spec->hp_independent_mode);
- activate_ctl(codec, "Headphone Playback Switch",
- spec->hp_independent_mode);
- }
- /* update jack power state */
- set_jack_power_state(codec);
- return 0;
-}
-
-static struct snd_kcontrol_new via_hp_mixer[2] = {
- {
- .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
- .name = "Independent HP",
- .info = via_independent_hp_info,
- .get = via_independent_hp_get,
- .put = via_independent_hp_put,
- },
- {
- .iface = NID_MAPPING,
- .name = "Independent HP",
- },
-};
-
-static int via_hp_build(struct hda_codec *codec)
-{
- struct via_spec *spec = codec->spec;
- struct snd_kcontrol_new *knew;
- hda_nid_t nid;
- int nums;
- hda_nid_t conn[HDA_MAX_CONNECTIONS];
-
- switch (spec->codec_type) {
- case VT1718S:
- nid = 0x34;
- break;
- case VT2002P:
- nid = 0x35;
- break;
- case VT1812:
- nid = 0x3d;
- break;
- default:
- nid = spec->autocfg.hp_pins[0];
- break;
- }
-
- if (spec->codec_type != VT1708) {
- nums = snd_hda_get_connections(codec, nid,
- conn, HDA_MAX_CONNECTIONS);
- if (nums <= 1)
- return 0;
- }
-
- knew = via_clone_control(spec, &via_hp_mixer[0]);
- if (knew == NULL)
- return -ENOMEM;
-
- knew->subdevice = HDA_SUBDEV_NID_FLAG | nid;
- knew->private_value = nid;
-
- knew = via_clone_control(spec, &via_hp_mixer[1]);
- if (knew == NULL)
- return -ENOMEM;
- knew->subdevice = side_mute_channel(spec);
-
- return 0;
-}
-
-static void notify_aa_path_ctls(struct hda_codec *codec)
-{
- int i;
- struct snd_ctl_elem_id id;
- const char *labels[] = {"Mic", "Front Mic", "Line"};
-
- memset(&id, 0, sizeof(id));
- id.iface = SNDRV_CTL_ELEM_IFACE_MIXER;
- for (i = 0; i < ARRAY_SIZE(labels); i++) {
- sprintf(id.name, "%s Playback Volume", labels[i]);
- snd_ctl_notify(codec->bus->card, SNDRV_CTL_EVENT_MASK_VALUE,
- &id);
- }
-}
-
-static void mute_aa_path(struct hda_codec *codec, int mute)
-{
- struct via_spec *spec = codec->spec;
- hda_nid_t nid_mixer;
- int start_idx;
- int end_idx;
- int i;
- /* get nid of MW0 and start & end index */
- switch (spec->codec_type) {
- case VT1708:
- nid_mixer = 0x17;
- start_idx = 2;
- end_idx = 4;
- break;
- case VT1709_10CH:
- case VT1709_6CH:
- nid_mixer = 0x18;
- start_idx = 2;
- end_idx = 4;
- break;
- case VT1708B_8CH:
- case VT1708B_4CH:
- case VT1708S:
- case VT1716S:
- nid_mixer = 0x16;
- start_idx = 2;
- end_idx = 4;
- break;
- case VT1718S:
- nid_mixer = 0x21;
- start_idx = 1;
- end_idx = 3;
- break;
- default:
- return;
- }
- /* check AA path's mute status */
- for (i = start_idx; i <= end_idx; i++) {
- int val = mute ? HDA_AMP_MUTE : HDA_AMP_UNMUTE;
- snd_hda_codec_amp_stereo(codec, nid_mixer, HDA_INPUT, i,
- HDA_AMP_MUTE, val);
- }
-}
-static int is_smart51_pins(struct via_spec *spec, hda_nid_t pin)
-{
- const struct auto_pin_cfg *cfg = &spec->autocfg;
- int i;
-
- for (i = 0; i < cfg->num_inputs; i++) {
- if (pin == cfg->inputs[i].pin)
- return cfg->inputs[i].type <= AUTO_PIN_LINE_IN;
- }
- return 0;
-}
-
-static int via_smart51_info(struct snd_kcontrol *kcontrol,
- struct snd_ctl_elem_info *uinfo)
-{
- uinfo->type = SNDRV_CTL_ELEM_TYPE_BOOLEAN;
- uinfo->count = 1;
- uinfo->value.integer.min = 0;
- uinfo->value.integer.max = 1;
- return 0;
-}
-
-static int via_smart51_get(struct snd_kcontrol *kcontrol,
- struct snd_ctl_elem_value *ucontrol)
-{
- struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
- struct via_spec *spec = codec->spec;
- const struct auto_pin_cfg *cfg = &spec->autocfg;
- int on = 1;
- int i;
-
- for (i = 0; i < cfg->num_inputs; i++) {
- hda_nid_t nid = cfg->inputs[i].pin;
- int ctl = snd_hda_codec_read(codec, nid, 0,
- AC_VERB_GET_PIN_WIDGET_CONTROL, 0);
- if (cfg->inputs[i].type > AUTO_PIN_LINE_IN)
- continue;
- if (cfg->inputs[i].type == AUTO_PIN_MIC &&
- spec->hp_independent_mode && spec->codec_type != VT1718S)
- continue; /* ignore FMic for independent HP */
- if ((ctl & AC_PINCTL_IN_EN) && !(ctl & AC_PINCTL_OUT_EN))
- on = 0;
- }
- *ucontrol->value.integer.value = on;
- return 0;
-}
-
-static int via_smart51_put(struct snd_kcontrol *kcontrol,
- struct snd_ctl_elem_value *ucontrol)
-{
- struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
- struct via_spec *spec = codec->spec;
- const struct auto_pin_cfg *cfg = &spec->autocfg;
- int out_in = *ucontrol->value.integer.value
- ? AC_PINCTL_OUT_EN : AC_PINCTL_IN_EN;
- int i;
-
- for (i = 0; i < cfg->num_inputs; i++) {
- hda_nid_t nid = cfg->inputs[i].pin;
- unsigned int parm;
-
- if (cfg->inputs[i].type > AUTO_PIN_LINE_IN)
- continue;
- if (cfg->inputs[i].type == AUTO_PIN_MIC &&
- spec->hp_independent_mode && spec->codec_type != VT1718S)
- continue; /* don't retask FMic for independent HP */
-
- parm = snd_hda_codec_read(codec, nid, 0,
- AC_VERB_GET_PIN_WIDGET_CONTROL, 0);
- parm &= ~(AC_PINCTL_IN_EN | AC_PINCTL_OUT_EN);
- parm |= out_in;
- snd_hda_codec_write(codec, nid, 0,
- AC_VERB_SET_PIN_WIDGET_CONTROL,
- parm);
- if (out_in == AC_PINCTL_OUT_EN) {
- mute_aa_path(codec, 1);
- notify_aa_path_ctls(codec);
- }
- if (spec->codec_type == VT1718S) {
- snd_hda_codec_amp_stereo(
- codec, nid, HDA_OUTPUT, 0, HDA_AMP_MUTE,
- HDA_AMP_UNMUTE);
- }
- if (cfg->inputs[i].type == AUTO_PIN_MIC) {
- if (spec->codec_type == VT1708S
- || spec->codec_type == VT1716S) {
- /* input = index 1 (AOW3) */
- snd_hda_codec_write(
- codec, nid, 0,
- AC_VERB_SET_CONNECT_SEL, 1);
- snd_hda_codec_amp_stereo(
- codec, nid, HDA_OUTPUT,
- 0, HDA_AMP_MUTE, HDA_AMP_UNMUTE);
- }
- }
- }
- spec->smart51_enabled = *ucontrol->value.integer.value;
- set_jack_power_state(codec);
+ if (val == spec->no_pin_power_ctl)
+ return 0;
+ spec->no_pin_power_ctl = val;
+ set_widgets_power_state(codec);
+ analog_low_current_mode(codec);
return 1;
}
-static struct snd_kcontrol_new via_smart51_mixer[2] = {
- {
- .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
- .name = "Smart 5.1",
- .count = 1,
- .info = via_smart51_info,
- .get = via_smart51_get,
- .put = via_smart51_put,
- },
- {
- .iface = NID_MAPPING,
- .name = "Smart 5.1",
- }
-};
-
-static int via_smart51_build(struct via_spec *spec)
-{
- struct snd_kcontrol_new *knew;
- const struct auto_pin_cfg *cfg = &spec->autocfg;
- hda_nid_t nid;
- int i;
-
- knew = via_clone_control(spec, &via_smart51_mixer[0]);
- if (knew == NULL)
- return -ENOMEM;
-
- for (i = 0; i < cfg->num_inputs; i++) {
- nid = cfg->inputs[i].pin;
- if (cfg->inputs[i].type <= AUTO_PIN_LINE_IN) {
- knew = via_clone_control(spec, &via_smart51_mixer[1]);
- if (knew == NULL)
- return -ENOMEM;
- knew->subdevice = nid;
- break;
- }
- }
-
- return 0;
-}
-
-/* capture mixer elements */
-static struct snd_kcontrol_new vt1708_capture_mixer[] = {
- HDA_CODEC_VOLUME("Capture Volume", 0x15, 0x0, HDA_INPUT),
- HDA_CODEC_MUTE("Capture Switch", 0x15, 0x0, HDA_INPUT),
- HDA_CODEC_VOLUME_IDX("Capture Volume", 1, 0x27, 0x0, HDA_INPUT),
- HDA_CODEC_MUTE_IDX("Capture Switch", 1, 0x27, 0x0, HDA_INPUT),
+static const struct snd_kcontrol_new via_pin_power_ctl_enum[] = {
{
- .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
- /* The multiple "Capture Source" controls confuse alsamixer
- * So call somewhat different..
- */
- /* .name = "Capture Source", */
- .name = "Input Source",
- .count = 1,
- .info = via_mux_enum_info,
- .get = via_mux_enum_get,
- .put = via_mux_enum_put,
+ .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+ .name = "Dynamic Power-Control",
+ .info = via_pin_power_ctl_info,
+ .get = via_pin_power_ctl_get,
+ .put = via_pin_power_ctl_put,
},
- { } /* end */
+ {} /* terminator */
};
-/* check AA path's mute statue */
-static int is_aa_path_mute(struct hda_codec *codec)
+
+/* check AA path's mute status */
+static bool is_aa_path_mute(struct hda_codec *codec)
{
- int mute = 1;
- hda_nid_t nid_mixer;
- int start_idx;
- int end_idx;
- int i;
struct via_spec *spec = codec->spec;
- /* get nid of MW0 and start & end index */
- switch (spec->codec_type) {
- case VT1708B_8CH:
- case VT1708B_4CH:
- case VT1708S:
- case VT1716S:
- nid_mixer = 0x16;
- start_idx = 2;
- end_idx = 4;
- break;
- case VT1702:
- nid_mixer = 0x1a;
- start_idx = 1;
- end_idx = 3;
- break;
- case VT1718S:
- nid_mixer = 0x21;
- start_idx = 1;
- end_idx = 3;
- break;
- case VT2002P:
- case VT1812:
- nid_mixer = 0x21;
- start_idx = 0;
- end_idx = 2;
- break;
- default:
- return 0;
- }
- /* check AA path's mute status */
- for (i = start_idx; i <= end_idx; i++) {
- unsigned int con_list = snd_hda_codec_read(
- codec, nid_mixer, 0, AC_VERB_GET_CONNECT_LIST, i/4*4);
- int shift = 8 * (i % 4);
- hda_nid_t nid_pin = (con_list & (0xff << shift)) >> shift;
- unsigned int defconf = snd_hda_codec_get_pincfg(codec, nid_pin);
- if (get_defcfg_connect(defconf) == AC_JACK_PORT_COMPLEX) {
- /* check mute status while the pin is connected */
- int mute_l = snd_hda_codec_amp_read(codec, nid_mixer, 0,
- HDA_INPUT, i) >> 7;
- int mute_r = snd_hda_codec_amp_read(codec, nid_mixer, 1,
- HDA_INPUT, i) >> 7;
- if (!mute_l || !mute_r) {
- mute = 0;
- break;
- }
+ const struct hda_amp_list *p;
+ int ch, v;
+
+ p = spec->gen.loopback.amplist;
+ if (!p)
+ return true;
+ for (; p->nid; p++) {
+ for (ch = 0; ch < 2; ch++) {
+ v = snd_hda_codec_amp_read(codec, p->nid, ch, p->dir,
+ p->idx);
+ if (!(v & HDA_AMP_MUTE) && v > 0)
+ return false;
}
}
- return mute;
+ return true;
}
/* enter/exit analog low-current mode */
-static void analog_low_current_mode(struct hda_codec *codec, int stream_idle)
+static void __analog_low_current_mode(struct hda_codec *codec, bool force)
{
struct via_spec *spec = codec->spec;
- static int saved_stream_idle = 1; /* saved stream idle status */
- int enable = is_aa_path_mute(codec);
- unsigned int verb = 0;
- unsigned int parm = 0;
+ bool enable;
+ unsigned int verb, parm;
- if (stream_idle == -1) /* stream status did not change */
- enable = enable && saved_stream_idle;
- else {
- enable = enable && stream_idle;
- saved_stream_idle = stream_idle;
- }
+ if (spec->no_pin_power_ctl)
+ enable = false;
+ else
+ enable = is_aa_path_mute(codec) && !spec->gen.active_streams;
+ if (enable == spec->alc_mode && !force)
+ return;
+ spec->alc_mode = enable;
/* decide low current mode's verb & parameter */
switch (spec->codec_type) {
@@ -1603,9 +412,15 @@ static void analog_low_current_mode(struct hda_codec *codec, int stream_idle)
break;
case VT2002P:
case VT1812:
+ case VT1802:
verb = 0xf93;
parm = enable ? 0x00 : 0xe0; /* 0x00: 4/40x, 0xe0: 1x */
break;
+ case VT1705CF:
+ case VT1808:
+ verb = 0xf82;
+ parm = enable ? 0x00 : 0xe0; /* 0x00: 4/40x, 0xe0: 1x */
+ break;
default:
return; /* other codecs are not supported */
}
@@ -1613,887 +428,95 @@ static void analog_low_current_mode(struct hda_codec *codec, int stream_idle)
snd_hda_codec_write(codec, codec->afg, 0, verb, parm);
}
-/*
- * generic initialization of ADC, input mixers and output mixers
- */
-static struct hda_verb vt1708_volume_init_verbs[] = {
- /*
- * Unmute ADC0-1 and set the default input to mic-in
- */
- {0x15, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
- {0x27, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
-
-
- /* Unmute input amps (CD, Line In, Mic 1 & Mic 2) of the analog-loopback
- * mixer widget
- */
- /* Amp Indices: CD = 1, Mic1 = 2, Line = 3, Mic2 = 4 */
- {0x17, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
- {0x17, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
- {0x17, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(2)},
- {0x17, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(3)},
- {0x17, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(4)},
-
- /*
- * Set up output mixers (0x19 - 0x1b)
- */
- /* set vol=0 to output mixers */
- {0x19, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
- {0x1a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
- {0x1b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
-
- /* Setup default input MW0 to PW4 */
- {0x20, AC_VERB_SET_CONNECT_SEL, 0},
- /* PW9 Output enable */
- {0x25, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x40},
- { }
-};
-
-static int via_playback_pcm_open(struct hda_pcm_stream *hinfo,
- struct hda_codec *codec,
- struct snd_pcm_substream *substream)
-{
- struct via_spec *spec = codec->spec;
- int idle = substream->pstr->substream_opened == 1
- && substream->ref_count == 0;
- analog_low_current_mode(codec, idle);
- return snd_hda_multi_out_analog_open(codec, &spec->multiout, substream,
- hinfo);
-}
-
-static void playback_multi_pcm_prep_0(struct hda_codec *codec,
- unsigned int stream_tag,
- unsigned int format,
- struct snd_pcm_substream *substream)
-{
- struct via_spec *spec = codec->spec;
- struct hda_multi_out *mout = &spec->multiout;
- hda_nid_t *nids = mout->dac_nids;
- int chs = substream->runtime->channels;
- int i;
-
- mutex_lock(&codec->spdif_mutex);
- if (mout->dig_out_nid && mout->dig_out_used != HDA_DIG_EXCLUSIVE) {
- if (chs == 2 &&
- snd_hda_is_supported_format(codec, mout->dig_out_nid,
- format) &&
- !(codec->spdif_status & IEC958_AES0_NONAUDIO)) {
- mout->dig_out_used = HDA_DIG_ANALOG_DUP;
- /* turn off SPDIF once; otherwise the IEC958 bits won't
- * be updated */
- if (codec->spdif_ctls & AC_DIG1_ENABLE)
- snd_hda_codec_write(codec, mout->dig_out_nid, 0,
- AC_VERB_SET_DIGI_CONVERT_1,
- codec->spdif_ctls &
- ~AC_DIG1_ENABLE & 0xff);
- snd_hda_codec_setup_stream(codec, mout->dig_out_nid,
- stream_tag, 0, format);
- /* turn on again (if needed) */
- if (codec->spdif_ctls & AC_DIG1_ENABLE)
- snd_hda_codec_write(codec, mout->dig_out_nid, 0,
- AC_VERB_SET_DIGI_CONVERT_1,
- codec->spdif_ctls & 0xff);
- } else {
- mout->dig_out_used = 0;
- snd_hda_codec_setup_stream(codec, mout->dig_out_nid,
- 0, 0, 0);
- }
- }
- mutex_unlock(&codec->spdif_mutex);
-
- /* front */
- snd_hda_codec_setup_stream(codec, nids[HDA_FRONT], stream_tag,
- 0, format);
-
- if (mout->hp_nid && mout->hp_nid != nids[HDA_FRONT]
- && !spec->hp_independent_mode)
- /* headphone out will just decode front left/right (stereo) */
- snd_hda_codec_setup_stream(codec, mout->hp_nid, stream_tag,
- 0, format);
-
- /* extra outputs copied from front */
- for (i = 0; i < ARRAY_SIZE(mout->extra_out_nid); i++)
- if (mout->extra_out_nid[i])
- snd_hda_codec_setup_stream(codec,
- mout->extra_out_nid[i],
- stream_tag, 0, format);
-
- /* surrounds */
- for (i = 1; i < mout->num_dacs; i++) {
- if (chs >= (i + 1) * 2) /* independent out */
- snd_hda_codec_setup_stream(codec, nids[i], stream_tag,
- i * 2, format);
- else /* copy front */
- snd_hda_codec_setup_stream(codec, nids[i], stream_tag,
- 0, format);
- }
-}
-
-static int via_playback_multi_pcm_prepare(struct hda_pcm_stream *hinfo,
- struct hda_codec *codec,
- unsigned int stream_tag,
- unsigned int format,
- struct snd_pcm_substream *substream)
+static void analog_low_current_mode(struct hda_codec *codec)
{
- struct via_spec *spec = codec->spec;
- struct hda_multi_out *mout = &spec->multiout;
- hda_nid_t *nids = mout->dac_nids;
-
- if (substream->number == 0)
- playback_multi_pcm_prep_0(codec, stream_tag, format,
- substream);
- else {
- if (mout->hp_nid && mout->hp_nid != nids[HDA_FRONT] &&
- spec->hp_independent_mode)
- snd_hda_codec_setup_stream(codec, mout->hp_nid,
- stream_tag, 0, format);
- }
- vt1708_start_hp_work(spec);
- return 0;
+ return __analog_low_current_mode(codec, false);
}
-static int via_playback_multi_pcm_cleanup(struct hda_pcm_stream *hinfo,
- struct hda_codec *codec,
- struct snd_pcm_substream *substream)
-{
- struct via_spec *spec = codec->spec;
- struct hda_multi_out *mout = &spec->multiout;
- hda_nid_t *nids = mout->dac_nids;
- int i;
-
- if (substream->number == 0) {
- for (i = 0; i < mout->num_dacs; i++)
- snd_hda_codec_setup_stream(codec, nids[i], 0, 0, 0);
-
- if (mout->hp_nid && !spec->hp_independent_mode)
- snd_hda_codec_setup_stream(codec, mout->hp_nid,
- 0, 0, 0);
-
- for (i = 0; i < ARRAY_SIZE(mout->extra_out_nid); i++)
- if (mout->extra_out_nid[i])
- snd_hda_codec_setup_stream(codec,
- mout->extra_out_nid[i],
- 0, 0, 0);
- mutex_lock(&codec->spdif_mutex);
- if (mout->dig_out_nid &&
- mout->dig_out_used == HDA_DIG_ANALOG_DUP) {
- snd_hda_codec_setup_stream(codec, mout->dig_out_nid,
- 0, 0, 0);
- mout->dig_out_used = 0;
- }
- mutex_unlock(&codec->spdif_mutex);
- } else {
- if (mout->hp_nid && mout->hp_nid != nids[HDA_FRONT] &&
- spec->hp_independent_mode)
- snd_hda_codec_setup_stream(codec, mout->hp_nid,
- 0, 0, 0);
- }
- vt1708_stop_hp_work(spec);
- return 0;
-}
-
-/*
- * Digital out
- */
-static int via_dig_playback_pcm_open(struct hda_pcm_stream *hinfo,
- struct hda_codec *codec,
- struct snd_pcm_substream *substream)
-{
- struct via_spec *spec = codec->spec;
- return snd_hda_multi_out_dig_open(codec, &spec->multiout);
-}
-
-static int via_dig_playback_pcm_close(struct hda_pcm_stream *hinfo,
- struct hda_codec *codec,
- struct snd_pcm_substream *substream)
-{
- struct via_spec *spec = codec->spec;
- return snd_hda_multi_out_dig_close(codec, &spec->multiout);
-}
-
-static int via_dig_playback_pcm_prepare(struct hda_pcm_stream *hinfo,
- struct hda_codec *codec,
- unsigned int stream_tag,
- unsigned int format,
- struct snd_pcm_substream *substream)
-{
- struct via_spec *spec = codec->spec;
- return snd_hda_multi_out_dig_prepare(codec, &spec->multiout,
- stream_tag, format, substream);
-}
-
-static int via_dig_playback_pcm_cleanup(struct hda_pcm_stream *hinfo,
- struct hda_codec *codec,
- struct snd_pcm_substream *substream)
-{
- struct via_spec *spec = codec->spec;
- snd_hda_multi_out_dig_cleanup(codec, &spec->multiout);
- return 0;
-}
-
-/*
- * Analog capture
- */
-static int via_capture_pcm_prepare(struct hda_pcm_stream *hinfo,
- struct hda_codec *codec,
- unsigned int stream_tag,
- unsigned int format,
- struct snd_pcm_substream *substream)
-{
- struct via_spec *spec = codec->spec;
-
- snd_hda_codec_setup_stream(codec, spec->adc_nids[substream->number],
- stream_tag, 0, format);
- return 0;
-}
-
-static int via_capture_pcm_cleanup(struct hda_pcm_stream *hinfo,
- struct hda_codec *codec,
- struct snd_pcm_substream *substream)
-{
- struct via_spec *spec = codec->spec;
- snd_hda_codec_cleanup_stream(codec, spec->adc_nids[substream->number]);
- return 0;
-}
-
-static struct hda_pcm_stream vt1708_pcm_analog_playback = {
- .substreams = 2,
- .channels_min = 2,
- .channels_max = 8,
- .nid = 0x10, /* NID to query formats and rates */
- .ops = {
- .open = via_playback_pcm_open,
- .prepare = via_playback_multi_pcm_prepare,
- .cleanup = via_playback_multi_pcm_cleanup
- },
-};
-
-static struct hda_pcm_stream vt1708_pcm_analog_s16_playback = {
- .substreams = 2,
- .channels_min = 2,
- .channels_max = 8,
- .nid = 0x10, /* NID to query formats and rates */
- /* We got noisy outputs on the right channel on VT1708 when
- * 24bit samples are used. Until any workaround is found,
- * disable the 24bit format, so far.
- */
- .formats = SNDRV_PCM_FMTBIT_S16_LE,
- .ops = {
- .open = via_playback_pcm_open,
- .prepare = via_playback_multi_pcm_prepare,
- .cleanup = via_playback_multi_pcm_cleanup
- },
-};
-
-static struct hda_pcm_stream vt1708_pcm_analog_capture = {
- .substreams = 2,
- .channels_min = 2,
- .channels_max = 2,
- .nid = 0x15, /* NID to query formats and rates */
- .ops = {
- .prepare = via_capture_pcm_prepare,
- .cleanup = via_capture_pcm_cleanup
- },
-};
-
-static struct hda_pcm_stream vt1708_pcm_digital_playback = {
- .substreams = 1,
- .channels_min = 2,
- .channels_max = 2,
- /* NID is set in via_build_pcms */
- .ops = {
- .open = via_dig_playback_pcm_open,
- .close = via_dig_playback_pcm_close,
- .prepare = via_dig_playback_pcm_prepare,
- .cleanup = via_dig_playback_pcm_cleanup
- },
-};
-
-static struct hda_pcm_stream vt1708_pcm_digital_capture = {
- .substreams = 1,
- .channels_min = 2,
- .channels_max = 2,
-};
-
static int via_build_controls(struct hda_codec *codec)
{
struct via_spec *spec = codec->spec;
- struct snd_kcontrol *kctl;
- struct snd_kcontrol_new *knew;
int err, i;
- for (i = 0; i < spec->num_mixers; i++) {
- err = snd_hda_add_new_ctls(codec, spec->mixers[i]);
- if (err < 0)
- return err;
- }
+ err = snd_hda_gen_build_controls(codec);
+ if (err < 0)
+ return err;
- if (spec->multiout.dig_out_nid) {
- err = snd_hda_create_spdif_out_ctls(codec,
- spec->multiout.dig_out_nid);
- if (err < 0)
- return err;
- err = snd_hda_create_spdif_share_sw(codec,
- &spec->multiout);
- if (err < 0)
- return err;
- spec->multiout.share_spdif = 1;
- }
- if (spec->dig_in_nid) {
- err = snd_hda_create_spdif_in_ctls(codec, spec->dig_in_nid);
- if (err < 0)
- return err;
- }
+ if (spec->set_widgets_power_state)
+ spec->mixers[spec->num_mixers++] = via_pin_power_ctl_enum;
- /* assign Capture Source enums to NID */
- kctl = snd_hda_find_mixer_ctl(codec, "Input Source");
- for (i = 0; kctl && i < kctl->count; i++) {
- err = snd_hda_add_nid(codec, kctl, i, spec->mux_nids[i]);
+ for (i = 0; i < spec->num_mixers; i++) {
+ err = snd_hda_add_new_ctls(codec, spec->mixers[i]);
if (err < 0)
return err;
}
- /* other nid->control mapping */
- for (i = 0; i < spec->num_mixers; i++) {
- for (knew = spec->mixers[i]; knew->name; knew++) {
- if (knew->iface != NID_MAPPING)
- continue;
- kctl = snd_hda_find_mixer_ctl(codec, knew->name);
- if (kctl == NULL)
- continue;
- err = snd_hda_add_nid(codec, kctl, 0,
- knew->subdevice);
- }
- }
-
- /* init power states */
- set_jack_power_state(codec);
- analog_low_current_mode(codec, 1);
-
- via_free_kctls(codec); /* no longer needed */
return 0;
}
-static int via_build_pcms(struct hda_codec *codec)
+static void via_playback_pcm_hook(struct hda_pcm_stream *hinfo,
+ struct hda_codec *codec,
+ struct snd_pcm_substream *substream,
+ int action)
{
- struct via_spec *spec = codec->spec;
- struct hda_pcm *info = spec->pcm_rec;
-
- codec->num_pcms = 1;
- codec->pcm_info = info;
-
- info->name = spec->stream_name_analog;
- info->stream[SNDRV_PCM_STREAM_PLAYBACK] =
- *(spec->stream_analog_playback);
- info->stream[SNDRV_PCM_STREAM_PLAYBACK].nid =
- spec->multiout.dac_nids[0];
- info->stream[SNDRV_PCM_STREAM_CAPTURE] = *(spec->stream_analog_capture);
- info->stream[SNDRV_PCM_STREAM_CAPTURE].nid = spec->adc_nids[0];
-
- info->stream[SNDRV_PCM_STREAM_PLAYBACK].channels_max =
- spec->multiout.max_channels;
-
- if (spec->multiout.dig_out_nid || spec->dig_in_nid) {
- codec->num_pcms++;
- info++;
- info->name = spec->stream_name_digital;
- info->pcm_type = HDA_PCM_TYPE_SPDIF;
- if (spec->multiout.dig_out_nid) {
- info->stream[SNDRV_PCM_STREAM_PLAYBACK] =
- *(spec->stream_digital_playback);
- info->stream[SNDRV_PCM_STREAM_PLAYBACK].nid =
- spec->multiout.dig_out_nid;
- }
- if (spec->dig_in_nid) {
- info->stream[SNDRV_PCM_STREAM_CAPTURE] =
- *(spec->stream_digital_capture);
- info->stream[SNDRV_PCM_STREAM_CAPTURE].nid =
- spec->dig_in_nid;
- }
- }
-
- return 0;
+ analog_low_current_mode(codec);
+ vt1708_update_hp_work(codec);
}
static void via_free(struct hda_codec *codec)
{
- struct via_spec *spec = codec->spec;
-
- if (!spec)
- return;
-
- via_free_kctls(codec);
- vt1708_stop_hp_work(spec);
- kfree(codec->spec);
-}
-
-/* mute internal speaker if HP is plugged */
-static void via_hp_automute(struct hda_codec *codec)
-{
- unsigned int present = 0;
- struct via_spec *spec = codec->spec;
-
- present = snd_hda_jack_detect(codec, spec->autocfg.hp_pins[0]);
-
- if (!spec->hp_independent_mode) {
- struct snd_ctl_elem_id id;
- /* auto mute */
- snd_hda_codec_amp_stereo(
- codec, spec->autocfg.line_out_pins[0], HDA_OUTPUT, 0,
- HDA_AMP_MUTE, present ? HDA_AMP_MUTE : 0);
- /* notify change */
- memset(&id, 0, sizeof(id));
- id.iface = SNDRV_CTL_ELEM_IFACE_MIXER;
- strcpy(id.name, "Front Playback Switch");
- snd_ctl_notify(codec->bus->card, SNDRV_CTL_EVENT_MASK_VALUE,
- &id);
- }
+ vt1708_stop_hp_work(codec);
+ snd_hda_gen_free(codec);
}
-/* mute mono out if HP or Line out is plugged */
-static void via_mono_automute(struct hda_codec *codec)
+#ifdef CONFIG_PM
+static int via_suspend(struct hda_codec *codec)
{
- unsigned int hp_present, lineout_present;
struct via_spec *spec = codec->spec;
+ vt1708_stop_hp_work(codec);
- if (spec->codec_type != VT1716S)
- return;
-
- lineout_present = snd_hda_jack_detect(codec,
- spec->autocfg.line_out_pins[0]);
-
- /* Mute Mono Out if Line Out is plugged */
- if (lineout_present) {
- snd_hda_codec_amp_stereo(
- codec, 0x2A, HDA_OUTPUT, 0, HDA_AMP_MUTE, HDA_AMP_MUTE);
- return;
- }
-
- hp_present = snd_hda_jack_detect(codec, spec->autocfg.hp_pins[0]);
-
- if (!spec->hp_independent_mode)
- snd_hda_codec_amp_stereo(
- codec, 0x2A, HDA_OUTPUT, 0, HDA_AMP_MUTE,
- hp_present ? HDA_AMP_MUTE : 0);
-}
-
-static void via_gpio_control(struct hda_codec *codec)
-{
- unsigned int gpio_data;
- unsigned int vol_counter;
- unsigned int vol;
- unsigned int master_vol;
-
- struct via_spec *spec = codec->spec;
-
- gpio_data = snd_hda_codec_read(codec, codec->afg, 0,
- AC_VERB_GET_GPIO_DATA, 0) & 0x03;
-
- vol_counter = (snd_hda_codec_read(codec, codec->afg, 0,
- 0xF84, 0) & 0x3F0000) >> 16;
-
- vol = vol_counter & 0x1F;
- master_vol = snd_hda_codec_read(codec, 0x1A, 0,
- AC_VERB_GET_AMP_GAIN_MUTE,
- AC_AMP_GET_INPUT);
-
- if (gpio_data == 0x02) {
- /* unmute line out */
- snd_hda_codec_amp_stereo(codec, spec->autocfg.line_out_pins[0],
- HDA_OUTPUT, 0, HDA_AMP_MUTE, 0);
-
- if (vol_counter & 0x20) {
- /* decrease volume */
- if (vol > master_vol)
- vol = master_vol;
- snd_hda_codec_amp_stereo(codec, 0x1A, HDA_INPUT,
- 0, HDA_AMP_VOLMASK,
- master_vol-vol);
- } else {
- /* increase volume */
- snd_hda_codec_amp_stereo(codec, 0x1A, HDA_INPUT, 0,
- HDA_AMP_VOLMASK,
- ((master_vol+vol) > 0x2A) ? 0x2A :
- (master_vol+vol));
- }
- } else if (!(gpio_data & 0x02)) {
- /* mute line out */
- snd_hda_codec_amp_stereo(codec,
- spec->autocfg.line_out_pins[0],
- HDA_OUTPUT, 0, HDA_AMP_MUTE,
- HDA_AMP_MUTE);
- }
-}
-
-/* mute Internal-Speaker if HP is plugged */
-static void via_speaker_automute(struct hda_codec *codec)
-{
- unsigned int hp_present;
- struct via_spec *spec = codec->spec;
-
- if (spec->codec_type != VT2002P && spec->codec_type != VT1812)
- return;
-
- hp_present = snd_hda_jack_detect(codec, spec->autocfg.hp_pins[0]);
-
- if (!spec->hp_independent_mode) {
- struct snd_ctl_elem_id id;
- snd_hda_codec_amp_stereo(
- codec, spec->autocfg.speaker_pins[0], HDA_OUTPUT, 0,
- HDA_AMP_MUTE, hp_present ? HDA_AMP_MUTE : 0);
- /* notify change */
- memset(&id, 0, sizeof(id));
- id.iface = SNDRV_CTL_ELEM_IFACE_MIXER;
- strcpy(id.name, "Speaker Playback Switch");
- snd_ctl_notify(codec->bus->card, SNDRV_CTL_EVENT_MASK_VALUE,
- &id);
- }
-}
-
-/* mute line-out and internal speaker if HP is plugged */
-static void via_hp_bind_automute(struct hda_codec *codec)
-{
- /* use long instead of int below just to avoid an internal compiler
- * error with gcc 4.0.x
- */
- unsigned long hp_present, present = 0;
- struct via_spec *spec = codec->spec;
- int i;
-
- if (!spec->autocfg.hp_pins[0] || !spec->autocfg.line_out_pins[0])
- return;
-
- hp_present = snd_hda_jack_detect(codec, spec->autocfg.hp_pins[0]);
-
- present = snd_hda_jack_detect(codec, spec->autocfg.line_out_pins[0]);
-
- if (!spec->hp_independent_mode) {
- /* Mute Line-Outs */
- for (i = 0; i < spec->autocfg.line_outs; i++)
- snd_hda_codec_amp_stereo(
- codec, spec->autocfg.line_out_pins[i],
- HDA_OUTPUT, 0,
- HDA_AMP_MUTE, hp_present ? HDA_AMP_MUTE : 0);
- if (hp_present)
- present = hp_present;
- }
- /* Speakers */
- for (i = 0; i < spec->autocfg.speaker_outs; i++)
- snd_hda_codec_amp_stereo(
- codec, spec->autocfg.speaker_pins[i], HDA_OUTPUT, 0,
- HDA_AMP_MUTE, present ? HDA_AMP_MUTE : 0);
-}
-
-
-/* unsolicited event for jack sensing */
-static void via_unsol_event(struct hda_codec *codec,
- unsigned int res)
-{
- res >>= 26;
- if (res & VIA_HP_EVENT)
- via_hp_automute(codec);
- if (res & VIA_GPIO_EVENT)
- via_gpio_control(codec);
- if (res & VIA_JACK_EVENT)
- set_jack_power_state(codec);
- if (res & VIA_MONO_EVENT)
- via_mono_automute(codec);
- if (res & VIA_SPEAKER_EVENT)
- via_speaker_automute(codec);
- if (res & VIA_BIND_HP_EVENT)
- via_hp_bind_automute(codec);
-}
-
-static int via_init(struct hda_codec *codec)
-{
- struct via_spec *spec = codec->spec;
- int i;
- for (i = 0; i < spec->num_iverbs; i++)
- snd_hda_sequence_write(codec, spec->init_verbs[i]);
-
- /* Lydia Add for EAPD enable */
- if (!spec->dig_in_nid) { /* No Digital In connection */
- if (spec->dig_in_pin) {
- snd_hda_codec_write(codec, spec->dig_in_pin, 0,
- AC_VERB_SET_PIN_WIDGET_CONTROL,
- PIN_OUT);
- snd_hda_codec_write(codec, spec->dig_in_pin, 0,
- AC_VERB_SET_EAPD_BTLENABLE, 0x02);
- }
- } else /* enable SPDIF-input pin */
- snd_hda_codec_write(codec, spec->autocfg.dig_in_pin, 0,
- AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN);
-
- /* assign slave outs */
- if (spec->slave_dig_outs[0])
- codec->slave_dig_outs = spec->slave_dig_outs;
+ /* Fix pop noise on headphones */
+ if (spec->codec_type == VT1802)
+ snd_hda_shutup_pins(codec);
return 0;
}
-
-#ifdef SND_HDA_NEEDS_RESUME
-static int via_suspend(struct hda_codec *codec, pm_message_t state)
-{
- struct via_spec *spec = codec->spec;
- vt1708_stop_hp_work(spec);
- return 0;
-}
#endif
-#ifdef CONFIG_SND_HDA_POWER_SAVE
+#ifdef CONFIG_PM
static int via_check_power_status(struct hda_codec *codec, hda_nid_t nid)
{
struct via_spec *spec = codec->spec;
- return snd_hda_check_amp_list_power(codec, &spec->loopback, nid);
+ set_widgets_power_state(codec);
+ analog_low_current_mode(codec);
+ vt1708_update_hp_work(codec);
+ return snd_hda_check_amp_list_power(codec, &spec->gen.loopback, nid);
}
#endif
/*
*/
-static struct hda_codec_ops via_patch_ops = {
+
+static int via_init(struct hda_codec *codec);
+
+static const struct hda_codec_ops via_patch_ops = {
.build_controls = via_build_controls,
- .build_pcms = via_build_pcms,
+ .build_pcms = snd_hda_gen_build_pcms,
.init = via_init,
.free = via_free,
-#ifdef SND_HDA_NEEDS_RESUME
+ .unsol_event = snd_hda_jack_unsol_event,
+#ifdef CONFIG_PM
.suspend = via_suspend,
-#endif
-#ifdef CONFIG_SND_HDA_POWER_SAVE
.check_power_status = via_check_power_status,
#endif
};
-/* fill in the dac_nids table from the parsed pin configuration */
-static int vt1708_auto_fill_dac_nids(struct via_spec *spec,
- const struct auto_pin_cfg *cfg)
-{
- int i;
- hda_nid_t nid;
-
- spec->multiout.num_dacs = cfg->line_outs;
-
- spec->multiout.dac_nids = spec->private_dac_nids;
-
- for (i = 0; i < 4; i++) {
- nid = cfg->line_out_pins[i];
- if (nid) {
- /* config dac list */
- switch (i) {
- case AUTO_SEQ_FRONT:
- spec->multiout.dac_nids[i] = 0x10;
- break;
- case AUTO_SEQ_CENLFE:
- spec->multiout.dac_nids[i] = 0x12;
- break;
- case AUTO_SEQ_SURROUND:
- spec->multiout.dac_nids[i] = 0x11;
- break;
- case AUTO_SEQ_SIDE:
- spec->multiout.dac_nids[i] = 0x13;
- break;
- }
- }
- }
-
- return 0;
-}
-
-/* add playback controls from the parsed DAC table */
-static int vt1708_auto_create_multi_out_ctls(struct via_spec *spec,
- const struct auto_pin_cfg *cfg)
-{
- char name[32];
- static const char * const chname[4] = {
- "Front", "Surround", "C/LFE", "Side"
- };
- hda_nid_t nid, nid_vol, nid_vols[] = {0x17, 0x19, 0x1a, 0x1b};
- int i, err;
-
- for (i = 0; i <= AUTO_SEQ_SIDE; i++) {
- nid = cfg->line_out_pins[i];
-
- if (!nid)
- continue;
-
- nid_vol = nid_vols[i];
-
- if (i == AUTO_SEQ_CENLFE) {
- /* Center/LFE */
- err = via_add_control(spec, VIA_CTL_WIDGET_VOL,
- "Center Playback Volume",
- HDA_COMPOSE_AMP_VAL(nid_vol, 1, 0,
- HDA_OUTPUT));
- if (err < 0)
- return err;
- err = via_add_control(spec, VIA_CTL_WIDGET_VOL,
- "LFE Playback Volume",
- HDA_COMPOSE_AMP_VAL(nid_vol, 2, 0,
- HDA_OUTPUT));
- if (err < 0)
- return err;
- err = via_add_control(spec, VIA_CTL_WIDGET_MUTE,
- "Center Playback Switch",
- HDA_COMPOSE_AMP_VAL(nid_vol, 1, 0,
- HDA_OUTPUT));
- if (err < 0)
- return err;
- err = via_add_control(spec, VIA_CTL_WIDGET_MUTE,
- "LFE Playback Switch",
- HDA_COMPOSE_AMP_VAL(nid_vol, 2, 0,
- HDA_OUTPUT));
- if (err < 0)
- return err;
- } else if (i == AUTO_SEQ_FRONT) {
- /* add control to mixer index 0 */
- err = via_add_control(spec, VIA_CTL_WIDGET_VOL,
- "Master Front Playback Volume",
- HDA_COMPOSE_AMP_VAL(nid_vol, 3, 0,
- HDA_INPUT));
- if (err < 0)
- return err;
- err = via_add_control(spec, VIA_CTL_WIDGET_MUTE,
- "Master Front Playback Switch",
- HDA_COMPOSE_AMP_VAL(nid_vol, 3, 0,
- HDA_INPUT));
- if (err < 0)
- return err;
-
- /* add control to PW3 */
- sprintf(name, "%s Playback Volume", chname[i]);
- err = via_add_control(spec, VIA_CTL_WIDGET_VOL, name,
- HDA_COMPOSE_AMP_VAL(nid, 3, 0,
- HDA_OUTPUT));
- if (err < 0)
- return err;
- sprintf(name, "%s Playback Switch", chname[i]);
- err = via_add_control(spec, VIA_CTL_WIDGET_MUTE, name,
- HDA_COMPOSE_AMP_VAL(nid, 3, 0,
- HDA_OUTPUT));
- if (err < 0)
- return err;
- } else {
- sprintf(name, "%s Playback Volume", chname[i]);
- err = via_add_control(spec, VIA_CTL_WIDGET_VOL, name,
- HDA_COMPOSE_AMP_VAL(nid_vol, 3, 0,
- HDA_OUTPUT));
- if (err < 0)
- return err;
- sprintf(name, "%s Playback Switch", chname[i]);
- err = via_add_control(spec, VIA_CTL_WIDGET_MUTE, name,
- HDA_COMPOSE_AMP_VAL(nid_vol, 3, 0,
- HDA_OUTPUT));
- if (err < 0)
- return err;
- }
- }
-
- return 0;
-}
-
-static void create_hp_imux(struct via_spec *spec)
-{
- int i;
- struct hda_input_mux *imux = &spec->private_imux[1];
- static const char * const texts[] = { "OFF", "ON", NULL};
-
- /* for hp mode select */
- for (i = 0; texts[i]; i++)
- snd_hda_add_imux_item(imux, texts[i], i, NULL);
-
- spec->hp_mux = &spec->private_imux[1];
-}
-
-static int vt1708_auto_create_hp_ctls(struct via_spec *spec, hda_nid_t pin)
-{
- int err;
-
- if (!pin)
- return 0;
-
- spec->multiout.hp_nid = VT1708_HP_NID; /* AOW3 */
- spec->hp_independent_mode_index = 1;
-
- err = via_add_control(spec, VIA_CTL_WIDGET_VOL,
- "Headphone Playback Volume",
- HDA_COMPOSE_AMP_VAL(pin, 3, 0, HDA_OUTPUT));
- if (err < 0)
- return err;
- err = via_add_control(spec, VIA_CTL_WIDGET_MUTE,
- "Headphone Playback Switch",
- HDA_COMPOSE_AMP_VAL(pin, 3, 0, HDA_OUTPUT));
- if (err < 0)
- return err;
-
- create_hp_imux(spec);
-
- return 0;
-}
-
-/* create playback/capture controls for input pins */
-static int vt_auto_create_analog_input_ctls(struct hda_codec *codec,
- const struct auto_pin_cfg *cfg,
- hda_nid_t cap_nid,
- hda_nid_t pin_idxs[], int num_idxs)
-{
- struct via_spec *spec = codec->spec;
- struct hda_input_mux *imux = &spec->private_imux[0];
- int i, err, idx, type, type_idx = 0;
-
- /* for internal loopback recording select */
- for (idx = 0; idx < num_idxs; idx++) {
- if (pin_idxs[idx] == 0xff) {
- snd_hda_add_imux_item(imux, "Stereo Mixer", idx, NULL);
- break;
- }
- }
-
- for (i = 0; i < cfg->num_inputs; i++) {
- const char *label;
- type = cfg->inputs[i].type;
- for (idx = 0; idx < num_idxs; idx++)
- if (pin_idxs[idx] == cfg->inputs[i].pin)
- break;
- if (idx >= num_idxs)
- continue;
- if (i > 0 && type == cfg->inputs[i - 1].type)
- type_idx++;
- else
- type_idx = 0;
- label = hda_get_autocfg_input_label(codec, cfg, i);
- if (spec->codec_type == VT1708S ||
- spec->codec_type == VT1702 ||
- spec->codec_type == VT1716S)
- err = via_new_analog_input(spec, label, type_idx,
- idx+1, cap_nid);
- else
- err = via_new_analog_input(spec, label, type_idx,
- idx, cap_nid);
- if (err < 0)
- return err;
- snd_hda_add_imux_item(imux, label, idx, NULL);
- }
- return 0;
-}
-
-/* create playback/capture controls for input pins */
-static int vt1708_auto_create_analog_input_ctls(struct hda_codec *codec,
- const struct auto_pin_cfg *cfg)
-{
- static hda_nid_t pin_idxs[] = { 0xff, 0x24, 0x1d, 0x1e, 0x21 };
- return vt_auto_create_analog_input_ctls(codec, cfg, 0x17, pin_idxs,
- ARRAY_SIZE(pin_idxs));
-}
-#ifdef CONFIG_SND_HDA_POWER_SAVE
-static struct hda_amp_list vt1708_loopbacks[] = {
- { 0x17, HDA_INPUT, 1 },
- { 0x17, HDA_INPUT, 2 },
- { 0x17, HDA_INPUT, 3 },
- { 0x17, HDA_INPUT, 4 },
- { } /* end */
+static const struct hda_verb vt1708_init_verbs[] = {
+ /* power down jack detect function */
+ {0x1, 0xf81, 0x1},
+ { }
};
-#endif
-
static void vt1708_set_pinconfig_connect(struct hda_codec *codec, hda_nid_t nid)
{
unsigned int def_conf;
@@ -2511,7 +534,7 @@ static void vt1708_set_pinconfig_connect(struct hda_codec *codec, hda_nid_t nid)
return;
}
-static int vt1708_jack_detectect_get(struct snd_kcontrol *kcontrol,
+static int vt1708_jack_detect_get(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
@@ -2519,599 +542,182 @@ static int vt1708_jack_detectect_get(struct snd_kcontrol *kcontrol,
if (spec->codec_type != VT1708)
return 0;
- spec->vt1708_jack_detectect =
- !((snd_hda_codec_read(codec, 0x1, 0, 0xf84, 0) >> 8) & 0x1);
- ucontrol->value.integer.value[0] = spec->vt1708_jack_detectect;
+ ucontrol->value.integer.value[0] = spec->vt1708_jack_detect;
return 0;
}
-static int vt1708_jack_detectect_put(struct snd_kcontrol *kcontrol,
+static int vt1708_jack_detect_put(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
struct via_spec *spec = codec->spec;
- int change;
+ int val;
if (spec->codec_type != VT1708)
return 0;
- spec->vt1708_jack_detectect = ucontrol->value.integer.value[0];
- change = (0x1 & (snd_hda_codec_read(codec, 0x1, 0, 0xf84, 0) >> 8))
- == !spec->vt1708_jack_detectect;
- if (spec->vt1708_jack_detectect) {
- mute_aa_path(codec, 1);
- notify_aa_path_ctls(codec);
- }
- return change;
+ val = !!ucontrol->value.integer.value[0];
+ if (spec->vt1708_jack_detect == val)
+ return 0;
+ spec->vt1708_jack_detect = val;
+ vt1708_update_hp_work(codec);
+ return 1;
}
-static struct snd_kcontrol_new vt1708_jack_detectect[] = {
+static const struct snd_kcontrol_new vt1708_jack_detect_ctl[] = {
{
- .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
- .name = "Jack Detect",
- .count = 1,
- .info = snd_ctl_boolean_mono_info,
- .get = vt1708_jack_detectect_get,
- .put = vt1708_jack_detectect_put,
+ .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+ .name = "Jack Detect",
+ .count = 1,
+ .info = snd_ctl_boolean_mono_info,
+ .get = vt1708_jack_detect_get,
+ .put = vt1708_jack_detect_put,
},
- {} /* end */
+ {} /* terminator */
};
-static int vt1708_parse_auto_config(struct hda_codec *codec)
+static void via_hp_automute(struct hda_codec *codec, struct hda_jack_tbl *tbl)
{
- struct via_spec *spec = codec->spec;
- int err;
-
- /* Add HP and CD pin config connect bit re-config action */
- vt1708_set_pinconfig_connect(codec, VT1708_HP_PIN_NID);
- vt1708_set_pinconfig_connect(codec, VT1708_CD_PIN_NID);
-
- err = snd_hda_parse_pin_def_config(codec, &spec->autocfg, NULL);
- if (err < 0)
- return err;
- err = vt1708_auto_fill_dac_nids(spec, &spec->autocfg);
- if (err < 0)
- return err;
- if (!spec->autocfg.line_outs && !spec->autocfg.hp_pins[0])
- return 0; /* can't find valid BIOS pin config */
-
- err = vt1708_auto_create_multi_out_ctls(spec, &spec->autocfg);
- if (err < 0)
- return err;
- err = vt1708_auto_create_hp_ctls(spec, spec->autocfg.hp_pins[0]);
- if (err < 0)
- return err;
- err = vt1708_auto_create_analog_input_ctls(codec, &spec->autocfg);
- if (err < 0)
- return err;
- /* add jack detect on/off control */
- err = snd_hda_add_new_ctls(codec, vt1708_jack_detectect);
- if (err < 0)
- return err;
-
- spec->multiout.max_channels = spec->multiout.num_dacs * 2;
-
- if (spec->autocfg.dig_outs)
- spec->multiout.dig_out_nid = VT1708_DIGOUT_NID;
- spec->dig_in_pin = VT1708_DIGIN_PIN;
- if (spec->autocfg.dig_in_pin)
- spec->dig_in_nid = VT1708_DIGIN_NID;
-
- if (spec->kctls.list)
- spec->mixers[spec->num_mixers++] = spec->kctls.list;
-
- spec->init_verbs[spec->num_iverbs++] = vt1708_volume_init_verbs;
-
- spec->input_mux = &spec->private_imux[0];
-
- if (spec->hp_mux)
- via_hp_build(codec);
-
- via_smart51_build(spec);
- return 1;
+ set_widgets_power_state(codec);
+ snd_hda_gen_hp_automute(codec, tbl);
}
-/* init callback for auto-configuration model -- overriding the default init */
-static int via_auto_init(struct hda_codec *codec)
+static void via_line_automute(struct hda_codec *codec, struct hda_jack_tbl *tbl)
{
- struct via_spec *spec = codec->spec;
-
- via_init(codec);
- via_auto_init_multi_out(codec);
- via_auto_init_hp_out(codec);
- via_auto_init_analog_input(codec);
- if (spec->codec_type == VT2002P || spec->codec_type == VT1812) {
- via_hp_bind_automute(codec);
- } else {
- via_hp_automute(codec);
- via_speaker_automute(codec);
- }
-
- return 0;
+ set_widgets_power_state(codec);
+ snd_hda_gen_line_automute(codec, tbl);
}
-static void vt1708_update_hp_jack_state(struct work_struct *work)
+static void via_jack_powerstate_event(struct hda_codec *codec, struct hda_jack_tbl *tbl)
{
- struct via_spec *spec = container_of(work, struct via_spec,
- vt1708_hp_work.work);
- if (spec->codec_type != VT1708)
- return;
- /* if jack state toggled */
- if (spec->vt1708_hp_present
- != snd_hda_jack_detect(spec->codec, spec->autocfg.hp_pins[0])) {
- spec->vt1708_hp_present ^= 1;
- via_hp_automute(spec->codec);
- }
- vt1708_start_hp_work(spec);
+ set_widgets_power_state(codec);
}
-static int get_mux_nids(struct hda_codec *codec)
-{
- struct via_spec *spec = codec->spec;
- hda_nid_t nid, conn[8];
- unsigned int type;
- int i, n;
-
- for (i = 0; i < spec->num_adc_nids; i++) {
- nid = spec->adc_nids[i];
- while (nid) {
- type = get_wcaps_type(get_wcaps(codec, nid));
- if (type == AC_WID_PIN)
- break;
- n = snd_hda_get_connections(codec, nid, conn,
- ARRAY_SIZE(conn));
- if (n <= 0)
- break;
- if (n > 1) {
- spec->mux_nids[i] = nid;
- break;
- }
- nid = conn[0];
- }
- }
- return 0;
-}
+#define VIA_JACK_EVENT (HDA_GEN_LAST_EVENT + 1)
-static int patch_vt1708(struct hda_codec *codec)
+static void via_set_jack_unsol_events(struct hda_codec *codec)
{
- struct via_spec *spec;
- int err;
+ struct via_spec *spec = codec->spec;
+ struct auto_pin_cfg *cfg = &spec->gen.autocfg;
+ hda_nid_t pin;
+ int i;
- /* create a codec specific record */
- spec = via_new_spec(codec);
- if (spec == NULL)
- return -ENOMEM;
+ spec->gen.hp_automute_hook = via_hp_automute;
+ if (cfg->speaker_pins[0])
+ spec->gen.line_automute_hook = via_line_automute;
- /* automatic parse from the BIOS config */
- err = vt1708_parse_auto_config(codec);
- if (err < 0) {
- via_free(codec);
- return err;
- } else if (!err) {
- printk(KERN_INFO "hda_codec: Cannot set up configuration "
- "from BIOS. Using genenic mode...\n");
+ for (i = 0; i < cfg->line_outs; i++) {
+ pin = cfg->line_out_pins[i];
+ if (pin && !snd_hda_jack_tbl_get(codec, pin) &&
+ is_jack_detectable(codec, pin))
+ snd_hda_jack_detect_enable_callback(codec, pin,
+ VIA_JACK_EVENT,
+ via_jack_powerstate_event);
}
-
- spec->stream_name_analog = "VT1708 Analog";
- spec->stream_analog_playback = &vt1708_pcm_analog_playback;
- /* disable 32bit format on VT1708 */
- if (codec->vendor_id == 0x11061708)
- spec->stream_analog_playback = &vt1708_pcm_analog_s16_playback;
- spec->stream_analog_capture = &vt1708_pcm_analog_capture;
-
- spec->stream_name_digital = "VT1708 Digital";
- spec->stream_digital_playback = &vt1708_pcm_digital_playback;
- spec->stream_digital_capture = &vt1708_pcm_digital_capture;
-
-
- if (!spec->adc_nids && spec->input_mux) {
- spec->adc_nids = vt1708_adc_nids;
- spec->num_adc_nids = ARRAY_SIZE(vt1708_adc_nids);
- get_mux_nids(codec);
- spec->mixers[spec->num_mixers] = vt1708_capture_mixer;
- spec->num_mixers++;
+ for (i = 0; i < cfg->num_inputs; i++) {
+ pin = cfg->line_out_pins[i];
+ if (pin && !snd_hda_jack_tbl_get(codec, pin) &&
+ is_jack_detectable(codec, pin))
+ snd_hda_jack_detect_enable_callback(codec, pin,
+ VIA_JACK_EVENT,
+ via_jack_powerstate_event);
}
-
- codec->patch_ops = via_patch_ops;
-
- codec->patch_ops.init = via_auto_init;
-#ifdef CONFIG_SND_HDA_POWER_SAVE
- spec->loopback.amplist = vt1708_loopbacks;
-#endif
- INIT_DELAYED_WORK(&spec->vt1708_hp_work, vt1708_update_hp_jack_state);
- return 0;
}
-/* capture mixer elements */
-static struct snd_kcontrol_new vt1709_capture_mixer[] = {
- HDA_CODEC_VOLUME("Capture Volume", 0x14, 0x0, HDA_INPUT),
- HDA_CODEC_MUTE("Capture Switch", 0x14, 0x0, HDA_INPUT),
- HDA_CODEC_VOLUME_IDX("Capture Volume", 1, 0x15, 0x0, HDA_INPUT),
- HDA_CODEC_MUTE_IDX("Capture Switch", 1, 0x15, 0x0, HDA_INPUT),
- HDA_CODEC_VOLUME_IDX("Capture Volume", 2, 0x16, 0x0, HDA_INPUT),
- HDA_CODEC_MUTE_IDX("Capture Switch", 2, 0x16, 0x0, HDA_INPUT),
- {
- .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
- /* The multiple "Capture Source" controls confuse alsamixer
- * So call somewhat different..
- */
- /* .name = "Capture Source", */
- .name = "Input Source",
- .count = 1,
- .info = via_mux_enum_info,
- .get = via_mux_enum_get,
- .put = via_mux_enum_put,
- },
- { } /* end */
-};
-
-static struct hda_verb vt1709_uniwill_init_verbs[] = {
- {0x20, AC_VERB_SET_UNSOLICITED_ENABLE,
- AC_USRSP_EN | VIA_HP_EVENT | VIA_JACK_EVENT},
- { }
-};
-
-/*
- * generic initialization of ADC, input mixers and output mixers
- */
-static struct hda_verb vt1709_10ch_volume_init_verbs[] = {
- /*
- * Unmute ADC0-2 and set the default input to mic-in
- */
- {0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
- {0x15, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
- {0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
-
-
- /* Unmute input amps (CD, Line In, Mic 1 & Mic 2) of the analog-loopback
- * mixer widget
- */
- /* Amp Indices: AOW0=0, CD = 1, Mic1 = 2, Line = 3, Mic2 = 4 */
- {0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
- {0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
- {0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(2)},
- {0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(3)},
- {0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(4)},
-
- /*
- * Set up output selector (0x1a, 0x1b, 0x29)
- */
- /* set vol=0 to output mixers */
- {0x1a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
- {0x1b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
- {0x29, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
-
- /*
- * Unmute PW3 and PW4
- */
- {0x1f, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
- {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
-
- /* Set input of PW4 as MW0 */
- {0x20, AC_VERB_SET_CONNECT_SEL, 0},
- /* PW9 Output enable */
- {0x24, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x40},
- { }
+static const struct badness_table via_main_out_badness = {
+ .no_primary_dac = 0x10000,
+ .no_dac = 0x4000,
+ .shared_primary = 0x10000,
+ .shared_surr = 0x20,
+ .shared_clfe = 0x20,
+ .shared_surr_main = 0x20,
};
-
-static struct hda_pcm_stream vt1709_10ch_pcm_analog_playback = {
- .substreams = 1,
- .channels_min = 2,
- .channels_max = 10,
- .nid = 0x10, /* NID to query formats and rates */
- .ops = {
- .open = via_playback_pcm_open,
- .prepare = via_playback_multi_pcm_prepare,
- .cleanup = via_playback_multi_pcm_cleanup,
- },
+static const struct badness_table via_extra_out_badness = {
+ .no_primary_dac = 0x4000,
+ .no_dac = 0x4000,
+ .shared_primary = 0x12,
+ .shared_surr = 0x20,
+ .shared_clfe = 0x20,
+ .shared_surr_main = 0x10,
};
-static struct hda_pcm_stream vt1709_6ch_pcm_analog_playback = {
- .substreams = 1,
- .channels_min = 2,
- .channels_max = 6,
- .nid = 0x10, /* NID to query formats and rates */
- .ops = {
- .open = via_playback_pcm_open,
- .prepare = via_playback_multi_pcm_prepare,
- .cleanup = via_playback_multi_pcm_cleanup,
- },
-};
-
-static struct hda_pcm_stream vt1709_pcm_analog_capture = {
- .substreams = 2,
- .channels_min = 2,
- .channels_max = 2,
- .nid = 0x14, /* NID to query formats and rates */
- .ops = {
- .prepare = via_capture_pcm_prepare,
- .cleanup = via_capture_pcm_cleanup
- },
-};
-
-static struct hda_pcm_stream vt1709_pcm_digital_playback = {
- .substreams = 1,
- .channels_min = 2,
- .channels_max = 2,
- /* NID is set in via_build_pcms */
- .ops = {
- .open = via_dig_playback_pcm_open,
- .close = via_dig_playback_pcm_close
- },
-};
-
-static struct hda_pcm_stream vt1709_pcm_digital_capture = {
- .substreams = 1,
- .channels_min = 2,
- .channels_max = 2,
-};
-
-static int vt1709_auto_fill_dac_nids(struct via_spec *spec,
- const struct auto_pin_cfg *cfg)
-{
- int i;
- hda_nid_t nid;
-
- if (cfg->line_outs == 4) /* 10 channels */
- spec->multiout.num_dacs = cfg->line_outs+1; /* AOW0~AOW4 */
- else if (cfg->line_outs == 3) /* 6 channels */
- spec->multiout.num_dacs = cfg->line_outs; /* AOW0~AOW2 */
-
- spec->multiout.dac_nids = spec->private_dac_nids;
-
- if (cfg->line_outs == 4) { /* 10 channels */
- for (i = 0; i < cfg->line_outs; i++) {
- nid = cfg->line_out_pins[i];
- if (nid) {
- /* config dac list */
- switch (i) {
- case AUTO_SEQ_FRONT:
- /* AOW0 */
- spec->multiout.dac_nids[i] = 0x10;
- break;
- case AUTO_SEQ_CENLFE:
- /* AOW2 */
- spec->multiout.dac_nids[i] = 0x12;
- break;
- case AUTO_SEQ_SURROUND:
- /* AOW3 */
- spec->multiout.dac_nids[i] = 0x11;
- break;
- case AUTO_SEQ_SIDE:
- /* AOW1 */
- spec->multiout.dac_nids[i] = 0x27;
- break;
- default:
- break;
- }
- }
- }
- spec->multiout.dac_nids[cfg->line_outs] = 0x28; /* AOW4 */
-
- } else if (cfg->line_outs == 3) { /* 6 channels */
- for (i = 0; i < cfg->line_outs; i++) {
- nid = cfg->line_out_pins[i];
- if (nid) {
- /* config dac list */
- switch (i) {
- case AUTO_SEQ_FRONT:
- /* AOW0 */
- spec->multiout.dac_nids[i] = 0x10;
- break;
- case AUTO_SEQ_CENLFE:
- /* AOW2 */
- spec->multiout.dac_nids[i] = 0x12;
- break;
- case AUTO_SEQ_SURROUND:
- /* AOW1 */
- spec->multiout.dac_nids[i] = 0x11;
- break;
- default:
- break;
- }
- }
- }
- }
-
- return 0;
-}
-
-/* add playback controls from the parsed DAC table */
-static int vt1709_auto_create_multi_out_ctls(struct via_spec *spec,
- const struct auto_pin_cfg *cfg)
+static int via_parse_auto_config(struct hda_codec *codec)
{
- char name[32];
- static const char * const chname[4] = {
- "Front", "Surround", "C/LFE", "Side"
- };
- hda_nid_t nid, nid_vol, nid_vols[] = {0x18, 0x1a, 0x1b, 0x29};
- int i, err;
+ struct via_spec *spec = codec->spec;
+ int err;
- for (i = 0; i <= AUTO_SEQ_SIDE; i++) {
- nid = cfg->line_out_pins[i];
+ spec->gen.main_out_badness = &via_main_out_badness;
+ spec->gen.extra_out_badness = &via_extra_out_badness;
- if (!nid)
- continue;
+ err = snd_hda_parse_pin_defcfg(codec, &spec->gen.autocfg, NULL, 0);
+ if (err < 0)
+ return err;
- nid_vol = nid_vols[i];
-
- if (i == AUTO_SEQ_CENLFE) {
- /* Center/LFE */
- err = via_add_control(spec, VIA_CTL_WIDGET_VOL,
- "Center Playback Volume",
- HDA_COMPOSE_AMP_VAL(nid_vol, 1, 0,
- HDA_OUTPUT));
- if (err < 0)
- return err;
- err = via_add_control(spec, VIA_CTL_WIDGET_VOL,
- "LFE Playback Volume",
- HDA_COMPOSE_AMP_VAL(nid_vol, 2, 0,
- HDA_OUTPUT));
- if (err < 0)
- return err;
- err = via_add_control(spec, VIA_CTL_WIDGET_MUTE,
- "Center Playback Switch",
- HDA_COMPOSE_AMP_VAL(nid_vol, 1, 0,
- HDA_OUTPUT));
- if (err < 0)
- return err;
- err = via_add_control(spec, VIA_CTL_WIDGET_MUTE,
- "LFE Playback Switch",
- HDA_COMPOSE_AMP_VAL(nid_vol, 2, 0,
- HDA_OUTPUT));
- if (err < 0)
- return err;
- } else if (i == AUTO_SEQ_FRONT) {
- /* ADD control to mixer index 0 */
- err = via_add_control(spec, VIA_CTL_WIDGET_VOL,
- "Master Front Playback Volume",
- HDA_COMPOSE_AMP_VAL(nid_vol, 3, 0,
- HDA_INPUT));
- if (err < 0)
- return err;
- err = via_add_control(spec, VIA_CTL_WIDGET_MUTE,
- "Master Front Playback Switch",
- HDA_COMPOSE_AMP_VAL(nid_vol, 3, 0,
- HDA_INPUT));
- if (err < 0)
- return err;
-
- /* add control to PW3 */
- sprintf(name, "%s Playback Volume", chname[i]);
- err = via_add_control(spec, VIA_CTL_WIDGET_VOL, name,
- HDA_COMPOSE_AMP_VAL(nid, 3, 0,
- HDA_OUTPUT));
- if (err < 0)
- return err;
- sprintf(name, "%s Playback Switch", chname[i]);
- err = via_add_control(spec, VIA_CTL_WIDGET_MUTE, name,
- HDA_COMPOSE_AMP_VAL(nid, 3, 0,
- HDA_OUTPUT));
- if (err < 0)
- return err;
- } else if (i == AUTO_SEQ_SURROUND) {
- sprintf(name, "%s Playback Volume", chname[i]);
- err = via_add_control(spec, VIA_CTL_WIDGET_VOL, name,
- HDA_COMPOSE_AMP_VAL(nid_vol, 3, 0,
- HDA_OUTPUT));
- if (err < 0)
- return err;
- sprintf(name, "%s Playback Switch", chname[i]);
- err = via_add_control(spec, VIA_CTL_WIDGET_MUTE, name,
- HDA_COMPOSE_AMP_VAL(nid_vol, 3, 0,
- HDA_OUTPUT));
- if (err < 0)
- return err;
- } else if (i == AUTO_SEQ_SIDE) {
- sprintf(name, "%s Playback Volume", chname[i]);
- err = via_add_control(spec, VIA_CTL_WIDGET_VOL, name,
- HDA_COMPOSE_AMP_VAL(nid_vol, 3, 0,
- HDA_OUTPUT));
- if (err < 0)
- return err;
- sprintf(name, "%s Playback Switch", chname[i]);
- err = via_add_control(spec, VIA_CTL_WIDGET_MUTE, name,
- HDA_COMPOSE_AMP_VAL(nid_vol, 3, 0,
- HDA_OUTPUT));
- if (err < 0)
- return err;
- }
- }
+ err = snd_hda_gen_parse_auto_config(codec, &spec->gen.autocfg);
+ if (err < 0)
+ return err;
+ via_set_jack_unsol_events(codec);
return 0;
}
-static int vt1709_auto_create_hp_ctls(struct via_spec *spec, hda_nid_t pin)
+static int via_init(struct hda_codec *codec)
{
- int err;
+ struct via_spec *spec = codec->spec;
+ int i;
- if (!pin)
- return 0;
+ for (i = 0; i < spec->num_iverbs; i++)
+ snd_hda_sequence_write(codec, spec->init_verbs[i]);
- if (spec->multiout.num_dacs == 5) /* 10 channels */
- spec->multiout.hp_nid = VT1709_HP_DAC_NID;
- else if (spec->multiout.num_dacs == 3) /* 6 channels */
- spec->multiout.hp_nid = 0;
- spec->hp_independent_mode_index = 1;
+ /* init power states */
+ set_widgets_power_state(codec);
+ __analog_low_current_mode(codec, true);
- err = via_add_control(spec, VIA_CTL_WIDGET_VOL,
- "Headphone Playback Volume",
- HDA_COMPOSE_AMP_VAL(pin, 3, 0, HDA_OUTPUT));
- if (err < 0)
- return err;
- err = via_add_control(spec, VIA_CTL_WIDGET_MUTE,
- "Headphone Playback Switch",
- HDA_COMPOSE_AMP_VAL(pin, 3, 0, HDA_OUTPUT));
- if (err < 0)
- return err;
+ snd_hda_gen_init(codec);
+
+ vt1708_update_hp_work(codec);
return 0;
}
-/* create playback/capture controls for input pins */
-static int vt1709_auto_create_analog_input_ctls(struct hda_codec *codec,
- const struct auto_pin_cfg *cfg)
+static int vt1708_build_controls(struct hda_codec *codec)
{
- static hda_nid_t pin_idxs[] = { 0xff, 0x23, 0x1d, 0x1e, 0x21 };
- return vt_auto_create_analog_input_ctls(codec, cfg, 0x18, pin_idxs,
- ARRAY_SIZE(pin_idxs));
+ /* In order not to create "Phantom Jack" controls,
+ temporary enable jackpoll */
+ int err;
+ int old_interval = codec->jackpoll_interval;
+ codec->jackpoll_interval = msecs_to_jiffies(100);
+ err = via_build_controls(codec);
+ codec->jackpoll_interval = old_interval;
+ return err;
}
-static int vt1709_parse_auto_config(struct hda_codec *codec)
+static int vt1708_build_pcms(struct hda_codec *codec)
{
struct via_spec *spec = codec->spec;
- int err;
-
- err = snd_hda_parse_pin_def_config(codec, &spec->autocfg, NULL);
- if (err < 0)
- return err;
- err = vt1709_auto_fill_dac_nids(spec, &spec->autocfg);
- if (err < 0)
- return err;
- if (!spec->autocfg.line_outs && !spec->autocfg.hp_pins[0])
- return 0; /* can't find valid BIOS pin config */
+ int i, err;
- err = vt1709_auto_create_multi_out_ctls(spec, &spec->autocfg);
- if (err < 0)
+ err = snd_hda_gen_build_pcms(codec);
+ if (err < 0 || codec->vendor_id != 0x11061708)
return err;
- err = vt1709_auto_create_hp_ctls(spec, spec->autocfg.hp_pins[0]);
- if (err < 0)
- return err;
- err = vt1709_auto_create_analog_input_ctls(codec, &spec->autocfg);
- if (err < 0)
- return err;
-
- spec->multiout.max_channels = spec->multiout.num_dacs * 2;
-
- if (spec->autocfg.dig_outs)
- spec->multiout.dig_out_nid = VT1709_DIGOUT_NID;
- spec->dig_in_pin = VT1709_DIGIN_PIN;
- if (spec->autocfg.dig_in_pin)
- spec->dig_in_nid = VT1709_DIGIN_NID;
-
- if (spec->kctls.list)
- spec->mixers[spec->num_mixers++] = spec->kctls.list;
- spec->input_mux = &spec->private_imux[0];
-
- if (spec->hp_mux)
- via_hp_build(codec);
+ /* We got noisy outputs on the right channel on VT1708 when
+ * 24bit samples are used. Until any workaround is found,
+ * disable the 24bit format, so far.
+ */
+ for (i = 0; i < codec->num_pcms; i++) {
+ struct hda_pcm *info = &spec->gen.pcm_rec[i];
+ if (!info->stream[SNDRV_PCM_STREAM_PLAYBACK].substreams ||
+ info->pcm_type != HDA_PCM_TYPE_AUDIO)
+ continue;
+ info->stream[SNDRV_PCM_STREAM_PLAYBACK].formats =
+ SNDRV_PCM_FMTBIT_S16_LE;
+ }
- via_smart51_build(spec);
- return 1;
+ return 0;
}
-#ifdef CONFIG_SND_HDA_POWER_SAVE
-static struct hda_amp_list vt1709_loopbacks[] = {
- { 0x18, HDA_INPUT, 1 },
- { 0x18, HDA_INPUT, 2 },
- { 0x18, HDA_INPUT, 3 },
- { 0x18, HDA_INPUT, 4 },
- { } /* end */
-};
-#endif
-
-static int patch_vt1709_10ch(struct hda_codec *codec)
+static int patch_vt1708(struct hda_codec *codec)
{
struct via_spec *spec;
int err;
@@ -3121,89 +727,45 @@ static int patch_vt1709_10ch(struct hda_codec *codec)
if (spec == NULL)
return -ENOMEM;
- err = vt1709_parse_auto_config(codec);
+ spec->gen.mixer_nid = 0x17;
+
+ /* set jackpoll_interval while parsing the codec */
+ codec->jackpoll_interval = msecs_to_jiffies(100);
+ spec->vt1708_jack_detect = 1;
+
+ /* don't support the input jack switching due to lack of unsol event */
+ /* (it may work with polling, though, but it needs testing) */
+ spec->gen.suppress_auto_mic = 1;
+ /* Some machines show the broken speaker mute */
+ spec->gen.auto_mute_via_amp = 1;
+
+ /* Add HP and CD pin config connect bit re-config action */
+ vt1708_set_pinconfig_connect(codec, VT1708_HP_PIN_NID);
+ vt1708_set_pinconfig_connect(codec, VT1708_CD_PIN_NID);
+
+ /* automatic parse from the BIOS config */
+ err = via_parse_auto_config(codec);
if (err < 0) {
via_free(codec);
return err;
- } else if (!err) {
- printk(KERN_INFO "hda_codec: Cannot set up configuration. "
- "Using genenic mode...\n");
}
- spec->init_verbs[spec->num_iverbs++] = vt1709_10ch_volume_init_verbs;
- spec->init_verbs[spec->num_iverbs++] = vt1709_uniwill_init_verbs;
-
- spec->stream_name_analog = "VT1709 Analog";
- spec->stream_analog_playback = &vt1709_10ch_pcm_analog_playback;
- spec->stream_analog_capture = &vt1709_pcm_analog_capture;
-
- spec->stream_name_digital = "VT1709 Digital";
- spec->stream_digital_playback = &vt1709_pcm_digital_playback;
- spec->stream_digital_capture = &vt1709_pcm_digital_capture;
+ /* add jack detect on/off control */
+ spec->mixers[spec->num_mixers++] = vt1708_jack_detect_ctl;
-
- if (!spec->adc_nids && spec->input_mux) {
- spec->adc_nids = vt1709_adc_nids;
- spec->num_adc_nids = ARRAY_SIZE(vt1709_adc_nids);
- get_mux_nids(codec);
- spec->mixers[spec->num_mixers] = vt1709_capture_mixer;
- spec->num_mixers++;
- }
+ spec->init_verbs[spec->num_iverbs++] = vt1708_init_verbs;
codec->patch_ops = via_patch_ops;
+ codec->patch_ops.build_controls = vt1708_build_controls;
+ codec->patch_ops.build_pcms = vt1708_build_pcms;
- codec->patch_ops.init = via_auto_init;
- codec->patch_ops.unsol_event = via_unsol_event;
-#ifdef CONFIG_SND_HDA_POWER_SAVE
- spec->loopback.amplist = vt1709_loopbacks;
-#endif
+ /* clear jackpoll_interval again; it's set dynamically */
+ codec->jackpoll_interval = 0;
return 0;
}
-/*
- * generic initialization of ADC, input mixers and output mixers
- */
-static struct hda_verb vt1709_6ch_volume_init_verbs[] = {
- /*
- * Unmute ADC0-2 and set the default input to mic-in
- */
- {0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
- {0x15, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
- {0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
-
- /* Unmute input amps (CD, Line In, Mic 1 & Mic 2) of the analog-loopback
- * mixer widget
- */
- /* Amp Indices: AOW0=0, CD = 1, Mic1 = 2, Line = 3, Mic2 = 4 */
- {0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
- {0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
- {0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(2)},
- {0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(3)},
- {0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(4)},
-
- /*
- * Set up output selector (0x1a, 0x1b, 0x29)
- */
- /* set vol=0 to output mixers */
- {0x1a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
- {0x1b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
- {0x29, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
-
- /*
- * Unmute PW3 and PW4
- */
- {0x1f, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
- {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
-
- /* Set input of PW4 as MW0 */
- {0x20, AC_VERB_SET_CONNECT_SEL, 0},
- /* PW9 Output enable */
- {0x24, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x40},
- { }
-};
-
-static int patch_vt1709_6ch(struct hda_codec *codec)
+static int patch_vt1709(struct hda_codec *codec)
{
struct via_spec *spec;
int err;
@@ -3213,583 +775,123 @@ static int patch_vt1709_6ch(struct hda_codec *codec)
if (spec == NULL)
return -ENOMEM;
- err = vt1709_parse_auto_config(codec);
+ spec->gen.mixer_nid = 0x18;
+
+ err = via_parse_auto_config(codec);
if (err < 0) {
via_free(codec);
return err;
- } else if (!err) {
- printk(KERN_INFO "hda_codec: Cannot set up configuration. "
- "Using genenic mode...\n");
- }
-
- spec->init_verbs[spec->num_iverbs++] = vt1709_6ch_volume_init_verbs;
- spec->init_verbs[spec->num_iverbs++] = vt1709_uniwill_init_verbs;
-
- spec->stream_name_analog = "VT1709 Analog";
- spec->stream_analog_playback = &vt1709_6ch_pcm_analog_playback;
- spec->stream_analog_capture = &vt1709_pcm_analog_capture;
-
- spec->stream_name_digital = "VT1709 Digital";
- spec->stream_digital_playback = &vt1709_pcm_digital_playback;
- spec->stream_digital_capture = &vt1709_pcm_digital_capture;
-
-
- if (!spec->adc_nids && spec->input_mux) {
- spec->adc_nids = vt1709_adc_nids;
- spec->num_adc_nids = ARRAY_SIZE(vt1709_adc_nids);
- get_mux_nids(codec);
- spec->mixers[spec->num_mixers] = vt1709_capture_mixer;
- spec->num_mixers++;
}
codec->patch_ops = via_patch_ops;
- codec->patch_ops.init = via_auto_init;
- codec->patch_ops.unsol_event = via_unsol_event;
-#ifdef CONFIG_SND_HDA_POWER_SAVE
- spec->loopback.amplist = vt1709_loopbacks;
-#endif
return 0;
}
-/* capture mixer elements */
-static struct snd_kcontrol_new vt1708B_capture_mixer[] = {
- HDA_CODEC_VOLUME("Capture Volume", 0x13, 0x0, HDA_INPUT),
- HDA_CODEC_MUTE("Capture Switch", 0x13, 0x0, HDA_INPUT),
- HDA_CODEC_VOLUME_IDX("Capture Volume", 1, 0x14, 0x0, HDA_INPUT),
- HDA_CODEC_MUTE_IDX("Capture Switch", 1, 0x14, 0x0, HDA_INPUT),
- {
- .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
- /* The multiple "Capture Source" controls confuse alsamixer
- * So call somewhat different..
- */
- /* .name = "Capture Source", */
- .name = "Input Source",
- .count = 1,
- .info = via_mux_enum_info,
- .get = via_mux_enum_get,
- .put = via_mux_enum_put,
- },
- { } /* end */
-};
-/*
- * generic initialization of ADC, input mixers and output mixers
- */
-static struct hda_verb vt1708B_8ch_volume_init_verbs[] = {
- /*
- * Unmute ADC0-1 and set the default input to mic-in
- */
- {0x13, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
- {0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
-
-
- /* Unmute input amps (CD, Line In, Mic 1 & Mic 2) of the analog-loopback
- * mixer widget
- */
- /* Amp Indices: CD = 1, Mic1 = 2, Line = 3, Mic2 = 4 */
- {0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
- {0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
- {0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(2)},
- {0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(3)},
- {0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(4)},
-
- /*
- * Set up output mixers
- */
- /* set vol=0 to output mixers */
- {0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
- {0x26, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
- {0x27, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
-
- /* Setup default input to PW4 */
- {0x1d, AC_VERB_SET_CONNECT_SEL, 0},
- /* PW9 Output enable */
- {0x20, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x40},
- /* PW10 Input enable */
- {0x21, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x20},
- { }
-};
-
-static struct hda_verb vt1708B_4ch_volume_init_verbs[] = {
- /*
- * Unmute ADC0-1 and set the default input to mic-in
- */
- {0x13, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
- {0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
-
-
- /* Unmute input amps (CD, Line In, Mic 1 & Mic 2) of the analog-loopback
- * mixer widget
- */
- /* Amp Indices: CD = 1, Mic1 = 2, Line = 3, Mic2 = 4 */
- {0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
- {0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
- {0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(2)},
- {0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(3)},
- {0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(4)},
-
- /*
- * Set up output mixers
- */
- /* set vol=0 to output mixers */
- {0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
- {0x26, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
- {0x27, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
-
- /* Setup default input of PW4 to MW0 */
- {0x1d, AC_VERB_SET_CONNECT_SEL, 0x0},
- /* PW9 Output enable */
- {0x20, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x40},
- /* PW10 Input enable */
- {0x21, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x20},
- { }
-};
-
-static struct hda_verb vt1708B_uniwill_init_verbs[] = {
- {0x1d, AC_VERB_SET_UNSOLICITED_ENABLE,
- AC_USRSP_EN | VIA_HP_EVENT | VIA_JACK_EVENT},
- {0x19, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT},
- {0x1a, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT},
- {0x1b, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT},
- {0x1c, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT},
- {0x1e, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT},
- {0x22, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT},
- {0x23, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT},
- { }
-};
-
-static int via_pcm_open_close(struct hda_pcm_stream *hinfo,
- struct hda_codec *codec,
- struct snd_pcm_substream *substream)
-{
- int idle = substream->pstr->substream_opened == 1
- && substream->ref_count == 0;
-
- analog_low_current_mode(codec, idle);
- return 0;
-}
-
-static struct hda_pcm_stream vt1708B_8ch_pcm_analog_playback = {
- .substreams = 2,
- .channels_min = 2,
- .channels_max = 8,
- .nid = 0x10, /* NID to query formats and rates */
- .ops = {
- .open = via_playback_pcm_open,
- .prepare = via_playback_multi_pcm_prepare,
- .cleanup = via_playback_multi_pcm_cleanup,
- .close = via_pcm_open_close
- },
-};
-
-static struct hda_pcm_stream vt1708B_4ch_pcm_analog_playback = {
- .substreams = 2,
- .channels_min = 2,
- .channels_max = 4,
- .nid = 0x10, /* NID to query formats and rates */
- .ops = {
- .open = via_playback_pcm_open,
- .prepare = via_playback_multi_pcm_prepare,
- .cleanup = via_playback_multi_pcm_cleanup
- },
-};
-
-static struct hda_pcm_stream vt1708B_pcm_analog_capture = {
- .substreams = 2,
- .channels_min = 2,
- .channels_max = 2,
- .nid = 0x13, /* NID to query formats and rates */
- .ops = {
- .open = via_pcm_open_close,
- .prepare = via_capture_pcm_prepare,
- .cleanup = via_capture_pcm_cleanup,
- .close = via_pcm_open_close
- },
-};
-
-static struct hda_pcm_stream vt1708B_pcm_digital_playback = {
- .substreams = 1,
- .channels_min = 2,
- .channels_max = 2,
- /* NID is set in via_build_pcms */
- .ops = {
- .open = via_dig_playback_pcm_open,
- .close = via_dig_playback_pcm_close,
- .prepare = via_dig_playback_pcm_prepare,
- .cleanup = via_dig_playback_pcm_cleanup
- },
-};
-
-static struct hda_pcm_stream vt1708B_pcm_digital_capture = {
- .substreams = 1,
- .channels_min = 2,
- .channels_max = 2,
-};
-
-/* fill in the dac_nids table from the parsed pin configuration */
-static int vt1708B_auto_fill_dac_nids(struct via_spec *spec,
- const struct auto_pin_cfg *cfg)
-{
- int i;
- hda_nid_t nid;
-
- spec->multiout.num_dacs = cfg->line_outs;
-
- spec->multiout.dac_nids = spec->private_dac_nids;
-
- for (i = 0; i < 4; i++) {
- nid = cfg->line_out_pins[i];
- if (nid) {
- /* config dac list */
- switch (i) {
- case AUTO_SEQ_FRONT:
- spec->multiout.dac_nids[i] = 0x10;
- break;
- case AUTO_SEQ_CENLFE:
- spec->multiout.dac_nids[i] = 0x24;
- break;
- case AUTO_SEQ_SURROUND:
- spec->multiout.dac_nids[i] = 0x11;
- break;
- case AUTO_SEQ_SIDE:
- spec->multiout.dac_nids[i] = 0x25;
- break;
- }
- }
- }
-
- return 0;
-}
-
-/* add playback controls from the parsed DAC table */
-static int vt1708B_auto_create_multi_out_ctls(struct via_spec *spec,
- const struct auto_pin_cfg *cfg)
-{
- char name[32];
- static const char * const chname[4] = {
- "Front", "Surround", "C/LFE", "Side"
- };
- hda_nid_t nid_vols[] = {0x16, 0x18, 0x26, 0x27};
- hda_nid_t nid, nid_vol = 0;
- int i, err;
-
- for (i = 0; i <= AUTO_SEQ_SIDE; i++) {
- nid = cfg->line_out_pins[i];
-
- if (!nid)
- continue;
-
- nid_vol = nid_vols[i];
-
- if (i == AUTO_SEQ_CENLFE) {
- /* Center/LFE */
- err = via_add_control(spec, VIA_CTL_WIDGET_VOL,
- "Center Playback Volume",
- HDA_COMPOSE_AMP_VAL(nid_vol, 1, 0,
- HDA_OUTPUT));
- if (err < 0)
- return err;
- err = via_add_control(spec, VIA_CTL_WIDGET_VOL,
- "LFE Playback Volume",
- HDA_COMPOSE_AMP_VAL(nid_vol, 2, 0,
- HDA_OUTPUT));
- if (err < 0)
- return err;
- err = via_add_control(spec, VIA_CTL_WIDGET_MUTE,
- "Center Playback Switch",
- HDA_COMPOSE_AMP_VAL(nid_vol, 1, 0,
- HDA_OUTPUT));
- if (err < 0)
- return err;
- err = via_add_control(spec, VIA_CTL_WIDGET_MUTE,
- "LFE Playback Switch",
- HDA_COMPOSE_AMP_VAL(nid_vol, 2, 0,
- HDA_OUTPUT));
- if (err < 0)
- return err;
- } else if (i == AUTO_SEQ_FRONT) {
- /* add control to mixer index 0 */
- err = via_add_control(spec, VIA_CTL_WIDGET_VOL,
- "Master Front Playback Volume",
- HDA_COMPOSE_AMP_VAL(nid_vol, 3, 0,
- HDA_INPUT));
- if (err < 0)
- return err;
- err = via_add_control(spec, VIA_CTL_WIDGET_MUTE,
- "Master Front Playback Switch",
- HDA_COMPOSE_AMP_VAL(nid_vol, 3, 0,
- HDA_INPUT));
- if (err < 0)
- return err;
-
- /* add control to PW3 */
- sprintf(name, "%s Playback Volume", chname[i]);
- err = via_add_control(spec, VIA_CTL_WIDGET_VOL, name,
- HDA_COMPOSE_AMP_VAL(nid, 3, 0,
- HDA_OUTPUT));
- if (err < 0)
- return err;
- sprintf(name, "%s Playback Switch", chname[i]);
- err = via_add_control(spec, VIA_CTL_WIDGET_MUTE, name,
- HDA_COMPOSE_AMP_VAL(nid, 3, 0,
- HDA_OUTPUT));
- if (err < 0)
- return err;
- } else {
- sprintf(name, "%s Playback Volume", chname[i]);
- err = via_add_control(spec, VIA_CTL_WIDGET_VOL, name,
- HDA_COMPOSE_AMP_VAL(nid_vol, 3, 0,
- HDA_OUTPUT));
- if (err < 0)
- return err;
- sprintf(name, "%s Playback Switch", chname[i]);
- err = via_add_control(spec, VIA_CTL_WIDGET_MUTE, name,
- HDA_COMPOSE_AMP_VAL(nid_vol, 3, 0,
- HDA_OUTPUT));
- if (err < 0)
- return err;
- }
- }
-
- return 0;
-}
-
-static int vt1708B_auto_create_hp_ctls(struct via_spec *spec, hda_nid_t pin)
-{
- int err;
-
- if (!pin)
- return 0;
-
- spec->multiout.hp_nid = VT1708B_HP_NID; /* AOW3 */
- spec->hp_independent_mode_index = 1;
-
- err = via_add_control(spec, VIA_CTL_WIDGET_VOL,
- "Headphone Playback Volume",
- HDA_COMPOSE_AMP_VAL(pin, 3, 0, HDA_OUTPUT));
- if (err < 0)
- return err;
- err = via_add_control(spec, VIA_CTL_WIDGET_MUTE,
- "Headphone Playback Switch",
- HDA_COMPOSE_AMP_VAL(pin, 3, 0, HDA_OUTPUT));
- if (err < 0)
- return err;
-
- create_hp_imux(spec);
-
- return 0;
-}
-
-/* create playback/capture controls for input pins */
-static int vt1708B_auto_create_analog_input_ctls(struct hda_codec *codec,
- const struct auto_pin_cfg *cfg)
-{
- static hda_nid_t pin_idxs[] = { 0xff, 0x1f, 0x1a, 0x1b, 0x1e };
- return vt_auto_create_analog_input_ctls(codec, cfg, 0x16, pin_idxs,
- ARRAY_SIZE(pin_idxs));
-}
-
-static int vt1708B_parse_auto_config(struct hda_codec *codec)
+static void set_widgets_power_state_vt1708B(struct hda_codec *codec)
{
struct via_spec *spec = codec->spec;
- int err;
-
- err = snd_hda_parse_pin_def_config(codec, &spec->autocfg, NULL);
- if (err < 0)
- return err;
- err = vt1708B_auto_fill_dac_nids(spec, &spec->autocfg);
- if (err < 0)
- return err;
- if (!spec->autocfg.line_outs && !spec->autocfg.hp_pins[0])
- return 0; /* can't find valid BIOS pin config */
-
- err = vt1708B_auto_create_multi_out_ctls(spec, &spec->autocfg);
- if (err < 0)
- return err;
- err = vt1708B_auto_create_hp_ctls(spec, spec->autocfg.hp_pins[0]);
- if (err < 0)
- return err;
- err = vt1708B_auto_create_analog_input_ctls(codec, &spec->autocfg);
- if (err < 0)
- return err;
-
- spec->multiout.max_channels = spec->multiout.num_dacs * 2;
-
- if (spec->autocfg.dig_outs)
- spec->multiout.dig_out_nid = VT1708B_DIGOUT_NID;
- spec->dig_in_pin = VT1708B_DIGIN_PIN;
- if (spec->autocfg.dig_in_pin)
- spec->dig_in_nid = VT1708B_DIGIN_NID;
-
- if (spec->kctls.list)
- spec->mixers[spec->num_mixers++] = spec->kctls.list;
+ int imux_is_smixer;
+ unsigned int parm;
+ int is_8ch = 0;
+ if ((spec->codec_type != VT1708B_4CH) &&
+ (codec->vendor_id != 0x11064397))
+ is_8ch = 1;
+
+ /* SW0 (17h) = stereo mixer */
+ imux_is_smixer =
+ (snd_hda_codec_read(codec, 0x17, 0, AC_VERB_GET_CONNECT_SEL, 0x00)
+ == ((spec->codec_type == VT1708S) ? 5 : 0));
+ /* inputs */
+ /* PW 1/2/5 (1ah/1bh/1eh) */
+ parm = AC_PWRST_D3;
+ set_pin_power_state(codec, 0x1a, &parm);
+ set_pin_power_state(codec, 0x1b, &parm);
+ set_pin_power_state(codec, 0x1e, &parm);
+ if (imux_is_smixer)
+ parm = AC_PWRST_D0;
+ /* SW0 (17h), AIW 0/1 (13h/14h) */
+ update_power_state(codec, 0x17, parm);
+ update_power_state(codec, 0x13, parm);
+ update_power_state(codec, 0x14, parm);
+
+ /* outputs */
+ /* PW0 (19h), SW1 (18h), AOW1 (11h) */
+ parm = AC_PWRST_D3;
+ set_pin_power_state(codec, 0x19, &parm);
+ if (smart51_enabled(codec))
+ set_pin_power_state(codec, 0x1b, &parm);
+ update_power_state(codec, 0x18, parm);
+ update_power_state(codec, 0x11, parm);
- spec->input_mux = &spec->private_imux[0];
+ /* PW6 (22h), SW2 (26h), AOW2 (24h) */
+ if (is_8ch) {
+ parm = AC_PWRST_D3;
+ set_pin_power_state(codec, 0x22, &parm);
+ if (smart51_enabled(codec))
+ set_pin_power_state(codec, 0x1a, &parm);
+ update_power_state(codec, 0x26, parm);
+ update_power_state(codec, 0x24, parm);
+ } else if (codec->vendor_id == 0x11064397) {
+ /* PW7(23h), SW2(27h), AOW2(25h) */
+ parm = AC_PWRST_D3;
+ set_pin_power_state(codec, 0x23, &parm);
+ if (smart51_enabled(codec))
+ set_pin_power_state(codec, 0x1a, &parm);
+ update_power_state(codec, 0x27, parm);
+ update_power_state(codec, 0x25, parm);
+ }
- if (spec->hp_mux)
- via_hp_build(codec);
+ /* PW 3/4/7 (1ch/1dh/23h) */
+ parm = AC_PWRST_D3;
+ /* force to D0 for internal Speaker */
+ set_pin_power_state(codec, 0x1c, &parm);
+ set_pin_power_state(codec, 0x1d, &parm);
+ if (is_8ch)
+ set_pin_power_state(codec, 0x23, &parm);
- via_smart51_build(spec);
- return 1;
+ /* MW0 (16h), Sw3 (27h), AOW 0/3 (10h/25h) */
+ update_power_state(codec, 0x16, imux_is_smixer ? AC_PWRST_D0 : parm);
+ update_power_state(codec, 0x10, parm);
+ if (is_8ch) {
+ update_power_state(codec, 0x25, parm);
+ update_power_state(codec, 0x27, parm);
+ } else if (codec->vendor_id == 0x11064397 && spec->gen.indep_hp_enabled)
+ update_power_state(codec, 0x25, parm);
}
-#ifdef CONFIG_SND_HDA_POWER_SAVE
-static struct hda_amp_list vt1708B_loopbacks[] = {
- { 0x16, HDA_INPUT, 1 },
- { 0x16, HDA_INPUT, 2 },
- { 0x16, HDA_INPUT, 3 },
- { 0x16, HDA_INPUT, 4 },
- { } /* end */
-};
-#endif
static int patch_vt1708S(struct hda_codec *codec);
-static int patch_vt1708B_8ch(struct hda_codec *codec)
+static int patch_vt1708B(struct hda_codec *codec)
{
struct via_spec *spec;
int err;
if (get_codec_type(codec) == VT1708BCE)
return patch_vt1708S(codec);
- /* create a codec specific record */
- spec = via_new_spec(codec);
- if (spec == NULL)
- return -ENOMEM;
-
- /* automatic parse from the BIOS config */
- err = vt1708B_parse_auto_config(codec);
- if (err < 0) {
- via_free(codec);
- return err;
- } else if (!err) {
- printk(KERN_INFO "hda_codec: Cannot set up configuration "
- "from BIOS. Using genenic mode...\n");
- }
-
- spec->init_verbs[spec->num_iverbs++] = vt1708B_8ch_volume_init_verbs;
- spec->init_verbs[spec->num_iverbs++] = vt1708B_uniwill_init_verbs;
-
- spec->stream_name_analog = "VT1708B Analog";
- spec->stream_analog_playback = &vt1708B_8ch_pcm_analog_playback;
- spec->stream_analog_capture = &vt1708B_pcm_analog_capture;
-
- spec->stream_name_digital = "VT1708B Digital";
- spec->stream_digital_playback = &vt1708B_pcm_digital_playback;
- spec->stream_digital_capture = &vt1708B_pcm_digital_capture;
-
- if (!spec->adc_nids && spec->input_mux) {
- spec->adc_nids = vt1708B_adc_nids;
- spec->num_adc_nids = ARRAY_SIZE(vt1708B_adc_nids);
- get_mux_nids(codec);
- spec->mixers[spec->num_mixers] = vt1708B_capture_mixer;
- spec->num_mixers++;
- }
-
- codec->patch_ops = via_patch_ops;
-
- codec->patch_ops.init = via_auto_init;
- codec->patch_ops.unsol_event = via_unsol_event;
-#ifdef CONFIG_SND_HDA_POWER_SAVE
- spec->loopback.amplist = vt1708B_loopbacks;
-#endif
-
- return 0;
-}
-
-static int patch_vt1708B_4ch(struct hda_codec *codec)
-{
- struct via_spec *spec;
- int err;
/* create a codec specific record */
spec = via_new_spec(codec);
if (spec == NULL)
return -ENOMEM;
+ spec->gen.mixer_nid = 0x16;
+
/* automatic parse from the BIOS config */
- err = vt1708B_parse_auto_config(codec);
+ err = via_parse_auto_config(codec);
if (err < 0) {
via_free(codec);
return err;
- } else if (!err) {
- printk(KERN_INFO "hda_codec: Cannot set up configuration "
- "from BIOS. Using genenic mode...\n");
- }
-
- spec->init_verbs[spec->num_iverbs++] = vt1708B_4ch_volume_init_verbs;
- spec->init_verbs[spec->num_iverbs++] = vt1708B_uniwill_init_verbs;
-
- spec->stream_name_analog = "VT1708B Analog";
- spec->stream_analog_playback = &vt1708B_4ch_pcm_analog_playback;
- spec->stream_analog_capture = &vt1708B_pcm_analog_capture;
-
- spec->stream_name_digital = "VT1708B Digital";
- spec->stream_digital_playback = &vt1708B_pcm_digital_playback;
- spec->stream_digital_capture = &vt1708B_pcm_digital_capture;
-
- if (!spec->adc_nids && spec->input_mux) {
- spec->adc_nids = vt1708B_adc_nids;
- spec->num_adc_nids = ARRAY_SIZE(vt1708B_adc_nids);
- get_mux_nids(codec);
- spec->mixers[spec->num_mixers] = vt1708B_capture_mixer;
- spec->num_mixers++;
}
codec->patch_ops = via_patch_ops;
- codec->patch_ops.init = via_auto_init;
- codec->patch_ops.unsol_event = via_unsol_event;
-#ifdef CONFIG_SND_HDA_POWER_SAVE
- spec->loopback.amplist = vt1708B_loopbacks;
-#endif
+ spec->set_widgets_power_state = set_widgets_power_state_vt1708B;
return 0;
}
/* Patch for VT1708S */
-
-/* capture mixer elements */
-static struct snd_kcontrol_new vt1708S_capture_mixer[] = {
- HDA_CODEC_VOLUME("Capture Volume", 0x13, 0x0, HDA_INPUT),
- HDA_CODEC_MUTE("Capture Switch", 0x13, 0x0, HDA_INPUT),
- HDA_CODEC_VOLUME_IDX("Capture Volume", 1, 0x14, 0x0, HDA_INPUT),
- HDA_CODEC_MUTE_IDX("Capture Switch", 1, 0x14, 0x0, HDA_INPUT),
- HDA_CODEC_VOLUME("Mic Boost Capture Volume", 0x1A, 0x0, HDA_INPUT),
- HDA_CODEC_VOLUME("Front Mic Boost Capture Volume", 0x1E, 0x0,
- HDA_INPUT),
- {
- .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
- /* The multiple "Capture Source" controls confuse alsamixer
- * So call somewhat different..
- */
- /* .name = "Capture Source", */
- .name = "Input Source",
- .count = 1,
- .info = via_mux_enum_info,
- .get = via_mux_enum_get,
- .put = via_mux_enum_put,
- },
- { } /* end */
-};
-
-static struct hda_verb vt1708S_volume_init_verbs[] = {
- /* Unmute ADC0-1 and set the default input to mic-in */
- {0x13, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
- {0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
-
- /* Unmute input amps (CD, Line In, Mic 1 & Mic 2) of the
- * analog-loopback mixer widget */
- /* Amp Indices: CD = 1, Mic1 = 2, Line = 3, Mic2 = 4 */
- {0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
- {0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
- {0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(2)},
- {0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(3)},
- {0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(4)},
-
- /* Setup default input of PW4 to MW0 */
- {0x1d, AC_VERB_SET_CONNECT_SEL, 0x0},
- /* PW9, PW10 Output enable */
- {0x20, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x40},
- {0x21, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x40},
+static const struct hda_verb vt1708S_init_verbs[] = {
/* Enable Mic Boost Volume backdoor */
{0x1, 0xf98, 0x1},
/* don't bybass mixer */
@@ -3797,314 +899,11 @@ static struct hda_verb vt1708S_volume_init_verbs[] = {
{ }
};
-static struct hda_verb vt1708S_uniwill_init_verbs[] = {
- {0x1d, AC_VERB_SET_UNSOLICITED_ENABLE,
- AC_USRSP_EN | VIA_HP_EVENT | VIA_JACK_EVENT},
- {0x19, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT},
- {0x1a, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT},
- {0x1b, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT},
- {0x1c, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT},
- {0x1e, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT},
- {0x22, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT},
- {0x23, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT},
- { }
-};
-
-static struct hda_pcm_stream vt1708S_pcm_analog_playback = {
- .substreams = 2,
- .channels_min = 2,
- .channels_max = 8,
- .nid = 0x10, /* NID to query formats and rates */
- .ops = {
- .open = via_playback_pcm_open,
- .prepare = via_playback_multi_pcm_prepare,
- .cleanup = via_playback_multi_pcm_cleanup,
- .close = via_pcm_open_close
- },
-};
-
-static struct hda_pcm_stream vt1708S_pcm_analog_capture = {
- .substreams = 2,
- .channels_min = 2,
- .channels_max = 2,
- .nid = 0x13, /* NID to query formats and rates */
- .ops = {
- .open = via_pcm_open_close,
- .prepare = via_capture_pcm_prepare,
- .cleanup = via_capture_pcm_cleanup,
- .close = via_pcm_open_close
- },
-};
-
-static struct hda_pcm_stream vt1708S_pcm_digital_playback = {
- .substreams = 1,
- .channels_min = 2,
- .channels_max = 2,
- /* NID is set in via_build_pcms */
- .ops = {
- .open = via_dig_playback_pcm_open,
- .close = via_dig_playback_pcm_close,
- .prepare = via_dig_playback_pcm_prepare,
- .cleanup = via_dig_playback_pcm_cleanup
- },
-};
-
-/* fill in the dac_nids table from the parsed pin configuration */
-static int vt1708S_auto_fill_dac_nids(struct via_spec *spec,
- const struct auto_pin_cfg *cfg)
-{
- int i;
- hda_nid_t nid;
-
- spec->multiout.num_dacs = cfg->line_outs;
-
- spec->multiout.dac_nids = spec->private_dac_nids;
-
- for (i = 0; i < 4; i++) {
- nid = cfg->line_out_pins[i];
- if (nid) {
- /* config dac list */
- switch (i) {
- case AUTO_SEQ_FRONT:
- spec->multiout.dac_nids[i] = 0x10;
- break;
- case AUTO_SEQ_CENLFE:
- spec->multiout.dac_nids[i] = 0x24;
- break;
- case AUTO_SEQ_SURROUND:
- spec->multiout.dac_nids[i] = 0x11;
- break;
- case AUTO_SEQ_SIDE:
- spec->multiout.dac_nids[i] = 0x25;
- break;
- }
- }
- }
-
- /* for Smart 5.1, line/mic inputs double as output pins */
- if (cfg->line_outs == 1) {
- spec->multiout.num_dacs = 3;
- spec->multiout.dac_nids[AUTO_SEQ_SURROUND] = 0x11;
- spec->multiout.dac_nids[AUTO_SEQ_CENLFE] = 0x24;
- }
-
- return 0;
-}
-
-/* add playback controls from the parsed DAC table */
-static int vt1708S_auto_create_multi_out_ctls(struct via_spec *spec,
- const struct auto_pin_cfg *cfg)
-{
- char name[32];
- static const char * const chname[4] = {
- "Front", "Surround", "C/LFE", "Side"
- };
- hda_nid_t nid_vols[] = {0x10, 0x11, 0x24, 0x25};
- hda_nid_t nid_mutes[] = {0x1C, 0x18, 0x26, 0x27};
- hda_nid_t nid, nid_vol, nid_mute;
- int i, err;
-
- for (i = 0; i <= AUTO_SEQ_SIDE; i++) {
- nid = cfg->line_out_pins[i];
-
- /* for Smart 5.1, there are always at least six channels */
- if (!nid && i > AUTO_SEQ_CENLFE)
- continue;
-
- nid_vol = nid_vols[i];
- nid_mute = nid_mutes[i];
-
- if (i == AUTO_SEQ_CENLFE) {
- /* Center/LFE */
- err = via_add_control(spec, VIA_CTL_WIDGET_VOL,
- "Center Playback Volume",
- HDA_COMPOSE_AMP_VAL(nid_vol, 1, 0,
- HDA_OUTPUT));
- if (err < 0)
- return err;
- err = via_add_control(spec, VIA_CTL_WIDGET_VOL,
- "LFE Playback Volume",
- HDA_COMPOSE_AMP_VAL(nid_vol, 2, 0,
- HDA_OUTPUT));
- if (err < 0)
- return err;
- err = via_add_control(spec, VIA_CTL_WIDGET_MUTE,
- "Center Playback Switch",
- HDA_COMPOSE_AMP_VAL(nid_mute,
- 1, 0,
- HDA_OUTPUT));
- if (err < 0)
- return err;
- err = via_add_control(spec, VIA_CTL_WIDGET_MUTE,
- "LFE Playback Switch",
- HDA_COMPOSE_AMP_VAL(nid_mute,
- 2, 0,
- HDA_OUTPUT));
- if (err < 0)
- return err;
- } else if (i == AUTO_SEQ_FRONT) {
- /* add control to mixer index 0 */
- err = via_add_control(spec, VIA_CTL_WIDGET_VOL,
- "Master Front Playback Volume",
- HDA_COMPOSE_AMP_VAL(0x16, 3, 0,
- HDA_INPUT));
- if (err < 0)
- return err;
- err = via_add_control(spec, VIA_CTL_WIDGET_MUTE,
- "Master Front Playback Switch",
- HDA_COMPOSE_AMP_VAL(0x16, 3, 0,
- HDA_INPUT));
- if (err < 0)
- return err;
-
- /* Front */
- sprintf(name, "%s Playback Volume", chname[i]);
- err = via_add_control(spec, VIA_CTL_WIDGET_VOL, name,
- HDA_COMPOSE_AMP_VAL(nid_vol, 3, 0,
- HDA_OUTPUT));
- if (err < 0)
- return err;
- sprintf(name, "%s Playback Switch", chname[i]);
- err = via_add_control(spec, VIA_CTL_WIDGET_MUTE, name,
- HDA_COMPOSE_AMP_VAL(nid_mute,
- 3, 0,
- HDA_OUTPUT));
- if (err < 0)
- return err;
- } else {
- sprintf(name, "%s Playback Volume", chname[i]);
- err = via_add_control(spec, VIA_CTL_WIDGET_VOL, name,
- HDA_COMPOSE_AMP_VAL(nid_vol, 3, 0,
- HDA_OUTPUT));
- if (err < 0)
- return err;
- sprintf(name, "%s Playback Switch", chname[i]);
- err = via_add_control(spec, VIA_CTL_WIDGET_MUTE, name,
- HDA_COMPOSE_AMP_VAL(nid_mute,
- 3, 0,
- HDA_OUTPUT));
- if (err < 0)
- return err;
- }
- }
-
- return 0;
-}
-
-static int vt1708S_auto_create_hp_ctls(struct via_spec *spec, hda_nid_t pin)
-{
- int err;
-
- if (!pin)
- return 0;
-
- spec->multiout.hp_nid = VT1708S_HP_NID; /* AOW3 */
- spec->hp_independent_mode_index = 1;
-
- err = via_add_control(spec, VIA_CTL_WIDGET_VOL,
- "Headphone Playback Volume",
- HDA_COMPOSE_AMP_VAL(0x25, 3, 0, HDA_OUTPUT));
- if (err < 0)
- return err;
-
- err = via_add_control(spec, VIA_CTL_WIDGET_MUTE,
- "Headphone Playback Switch",
- HDA_COMPOSE_AMP_VAL(pin, 3, 0, HDA_OUTPUT));
- if (err < 0)
- return err;
-
- create_hp_imux(spec);
-
- return 0;
-}
-
-/* create playback/capture controls for input pins */
-static int vt1708S_auto_create_analog_input_ctls(struct hda_codec *codec,
- const struct auto_pin_cfg *cfg)
-{
- static hda_nid_t pin_idxs[] = { 0x1f, 0x1a, 0x1b, 0x1e, 0, 0xff };
- return vt_auto_create_analog_input_ctls(codec, cfg, 0x16, pin_idxs,
- ARRAY_SIZE(pin_idxs));
-}
-
-/* fill out digital output widgets; one for master and one for slave outputs */
-static void fill_dig_outs(struct hda_codec *codec)
-{
- struct via_spec *spec = codec->spec;
- int i;
-
- for (i = 0; i < spec->autocfg.dig_outs; i++) {
- hda_nid_t nid;
- int conn;
-
- nid = spec->autocfg.dig_out_pins[i];
- if (!nid)
- continue;
- conn = snd_hda_get_connections(codec, nid, &nid, 1);
- if (conn < 1)
- continue;
- if (!spec->multiout.dig_out_nid)
- spec->multiout.dig_out_nid = nid;
- else {
- spec->slave_dig_outs[0] = nid;
- break; /* at most two dig outs */
- }
- }
-}
-
-static int vt1708S_parse_auto_config(struct hda_codec *codec)
-{
- struct via_spec *spec = codec->spec;
- int err;
-
- err = snd_hda_parse_pin_def_config(codec, &spec->autocfg, NULL);
- if (err < 0)
- return err;
- err = vt1708S_auto_fill_dac_nids(spec, &spec->autocfg);
- if (err < 0)
- return err;
- if (!spec->autocfg.line_outs && !spec->autocfg.hp_pins[0])
- return 0; /* can't find valid BIOS pin config */
-
- err = vt1708S_auto_create_multi_out_ctls(spec, &spec->autocfg);
- if (err < 0)
- return err;
- err = vt1708S_auto_create_hp_ctls(spec, spec->autocfg.hp_pins[0]);
- if (err < 0)
- return err;
- err = vt1708S_auto_create_analog_input_ctls(codec, &spec->autocfg);
- if (err < 0)
- return err;
-
- spec->multiout.max_channels = spec->multiout.num_dacs * 2;
-
- fill_dig_outs(codec);
-
- if (spec->kctls.list)
- spec->mixers[spec->num_mixers++] = spec->kctls.list;
-
- spec->input_mux = &spec->private_imux[0];
-
- if (spec->hp_mux)
- via_hp_build(codec);
-
- via_smart51_build(spec);
- return 1;
-}
-
-#ifdef CONFIG_SND_HDA_POWER_SAVE
-static struct hda_amp_list vt1708S_loopbacks[] = {
- { 0x16, HDA_INPUT, 1 },
- { 0x16, HDA_INPUT, 2 },
- { 0x16, HDA_INPUT, 3 },
- { 0x16, HDA_INPUT, 4 },
- { } /* end */
-};
-#endif
-
static void override_mic_boost(struct hda_codec *codec, hda_nid_t pin,
int offset, int num_steps, int step_size)
{
+ snd_hda_override_wcaps(codec, pin,
+ get_wcaps(codec, pin) | AC_WCAP_IN_AMP);
snd_hda_override_amp_caps(codec, pin, HDA_INPUT,
(offset << AC_AMPCAP_OFFSET_SHIFT) |
(num_steps << AC_AMPCAP_NUM_STEPS_SHIFT) |
@@ -4122,49 +921,9 @@ static int patch_vt1708S(struct hda_codec *codec)
if (spec == NULL)
return -ENOMEM;
- /* automatic parse from the BIOS config */
- err = vt1708S_parse_auto_config(codec);
- if (err < 0) {
- via_free(codec);
- return err;
- } else if (!err) {
- printk(KERN_INFO "hda_codec: Cannot set up configuration "
- "from BIOS. Using genenic mode...\n");
- }
-
- spec->init_verbs[spec->num_iverbs++] = vt1708S_volume_init_verbs;
- spec->init_verbs[spec->num_iverbs++] = vt1708S_uniwill_init_verbs;
-
- if (codec->vendor_id == 0x11060440)
- spec->stream_name_analog = "VT1818S Analog";
- else
- spec->stream_name_analog = "VT1708S Analog";
- spec->stream_analog_playback = &vt1708S_pcm_analog_playback;
- spec->stream_analog_capture = &vt1708S_pcm_analog_capture;
-
- if (codec->vendor_id == 0x11060440)
- spec->stream_name_digital = "VT1818S Digital";
- else
- spec->stream_name_digital = "VT1708S Digital";
- spec->stream_digital_playback = &vt1708S_pcm_digital_playback;
-
- if (!spec->adc_nids && spec->input_mux) {
- spec->adc_nids = vt1708S_adc_nids;
- spec->num_adc_nids = ARRAY_SIZE(vt1708S_adc_nids);
- get_mux_nids(codec);
- override_mic_boost(codec, 0x1a, 0, 3, 40);
- override_mic_boost(codec, 0x1e, 0, 3, 40);
- spec->mixers[spec->num_mixers] = vt1708S_capture_mixer;
- spec->num_mixers++;
- }
-
- codec->patch_ops = via_patch_ops;
-
- codec->patch_ops.init = via_auto_init;
- codec->patch_ops.unsol_event = via_unsol_event;
-#ifdef CONFIG_SND_HDA_POWER_SAVE
- spec->loopback.amplist = vt1708S_loopbacks;
-#endif
+ spec->gen.mixer_nid = 0x16;
+ override_mic_boost(codec, 0x1a, 0, 3, 40);
+ override_mic_boost(codec, 0x1e, 0, 3, 40);
/* correct names for VT1708BCE */
if (get_codec_type(codec) == VT1708BCE) {
@@ -4173,68 +932,34 @@ static int patch_vt1708S(struct hda_codec *codec)
snprintf(codec->bus->card->mixername,
sizeof(codec->bus->card->mixername),
"%s %s", codec->vendor_name, codec->chip_name);
- spec->stream_name_analog = "VT1708BCE Analog";
- spec->stream_name_digital = "VT1708BCE Digital";
}
- /* correct names for VT1818S */
- if (codec->vendor_id == 0x11060440) {
- spec->stream_name_analog = "VT1818S Analog";
- spec->stream_name_digital = "VT1818S Digital";
+ /* correct names for VT1705 */
+ if (codec->vendor_id == 0x11064397) {
+ kfree(codec->chip_name);
+ codec->chip_name = kstrdup("VT1705", GFP_KERNEL);
+ snprintf(codec->bus->card->mixername,
+ sizeof(codec->bus->card->mixername),
+ "%s %s", codec->vendor_name, codec->chip_name);
}
- return 0;
-}
-/* Patch for VT1702 */
+ /* automatic parse from the BIOS config */
+ err = via_parse_auto_config(codec);
+ if (err < 0) {
+ via_free(codec);
+ return err;
+ }
-/* capture mixer elements */
-static struct snd_kcontrol_new vt1702_capture_mixer[] = {
- HDA_CODEC_VOLUME("Capture Volume", 0x12, 0x0, HDA_INPUT),
- HDA_CODEC_MUTE("Capture Switch", 0x12, 0x0, HDA_INPUT),
- HDA_CODEC_VOLUME_IDX("Capture Volume", 1, 0x20, 0x0, HDA_INPUT),
- HDA_CODEC_MUTE_IDX("Capture Switch", 1, 0x20, 0x0, HDA_INPUT),
- HDA_CODEC_VOLUME("Digital Mic Capture Volume", 0x1F, 0x0, HDA_INPUT),
- HDA_CODEC_MUTE("Digital Mic Capture Switch", 0x1F, 0x0, HDA_INPUT),
- HDA_CODEC_VOLUME("Digital Mic Boost Capture Volume", 0x1E, 0x0,
- HDA_INPUT),
- {
- .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
- /* The multiple "Capture Source" controls confuse alsamixer
- * So call somewhat different..
- */
- /* .name = "Capture Source", */
- .name = "Input Source",
- .count = 1,
- .info = via_mux_enum_info,
- .get = via_mux_enum_get,
- .put = via_mux_enum_put,
- },
- { } /* end */
-};
+ spec->init_verbs[spec->num_iverbs++] = vt1708S_init_verbs;
-static struct hda_verb vt1702_volume_init_verbs[] = {
- /*
- * Unmute ADC0-1 and set the default input to mic-in
- */
- {0x12, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
- {0x1F, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
- {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
+ codec->patch_ops = via_patch_ops;
+
+ spec->set_widgets_power_state = set_widgets_power_state_vt1708B;
+ return 0;
+}
+/* Patch for VT1702 */
- /* Unmute input amps (CD, Line In, Mic 1 & Mic 2) of the analog-loopback
- * mixer widget
- */
- /* Amp Indices: Mic1 = 1, Line = 1, Mic2 = 3 */
- {0x1A, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
- {0x1A, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
- {0x1A, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(2)},
- {0x1A, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(3)},
- {0x1A, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(4)},
-
- /* Setup default input of PW4 to MW0 */
- {0x17, AC_VERB_SET_CONNECT_SEL, 0x1},
- /* PW6 PW7 Output enable */
- {0x19, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x40},
- {0x1C, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x40},
+static const struct hda_verb vt1702_init_verbs[] = {
/* mixer enable */
{0x1, 0xF88, 0x3},
/* GPIO 0~2 */
@@ -4242,588 +967,180 @@ static struct hda_verb vt1702_volume_init_verbs[] = {
{ }
};
-static struct hda_verb vt1702_uniwill_init_verbs[] = {
- {0x17, AC_VERB_SET_UNSOLICITED_ENABLE,
- AC_USRSP_EN | VIA_HP_EVENT | VIA_JACK_EVENT},
- {0x14, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT},
- {0x15, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT},
- {0x16, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT},
- {0x18, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT},
- { }
-};
-
-static struct hda_pcm_stream vt1702_pcm_analog_playback = {
- .substreams = 2,
- .channels_min = 2,
- .channels_max = 2,
- .nid = 0x10, /* NID to query formats and rates */
- .ops = {
- .open = via_playback_pcm_open,
- .prepare = via_playback_multi_pcm_prepare,
- .cleanup = via_playback_multi_pcm_cleanup,
- .close = via_pcm_open_close
- },
-};
-
-static struct hda_pcm_stream vt1702_pcm_analog_capture = {
- .substreams = 3,
- .channels_min = 2,
- .channels_max = 2,
- .nid = 0x12, /* NID to query formats and rates */
- .ops = {
- .open = via_pcm_open_close,
- .prepare = via_capture_pcm_prepare,
- .cleanup = via_capture_pcm_cleanup,
- .close = via_pcm_open_close
- },
-};
-
-static struct hda_pcm_stream vt1702_pcm_digital_playback = {
- .substreams = 2,
- .channels_min = 2,
- .channels_max = 2,
- /* NID is set in via_build_pcms */
- .ops = {
- .open = via_dig_playback_pcm_open,
- .close = via_dig_playback_pcm_close,
- .prepare = via_dig_playback_pcm_prepare,
- .cleanup = via_dig_playback_pcm_cleanup
- },
-};
-
-/* fill in the dac_nids table from the parsed pin configuration */
-static int vt1702_auto_fill_dac_nids(struct via_spec *spec,
- const struct auto_pin_cfg *cfg)
+static void set_widgets_power_state_vt1702(struct hda_codec *codec)
{
- spec->multiout.num_dacs = 1;
- spec->multiout.dac_nids = spec->private_dac_nids;
-
- if (cfg->line_out_pins[0]) {
- /* config dac list */
- spec->multiout.dac_nids[0] = 0x10;
- }
-
- return 0;
+ int imux_is_smixer =
+ snd_hda_codec_read(codec, 0x13, 0, AC_VERB_GET_CONNECT_SEL, 0x00) == 3;
+ unsigned int parm;
+ /* inputs */
+ /* PW 1/2/5 (14h/15h/18h) */
+ parm = AC_PWRST_D3;
+ set_pin_power_state(codec, 0x14, &parm);
+ set_pin_power_state(codec, 0x15, &parm);
+ set_pin_power_state(codec, 0x18, &parm);
+ if (imux_is_smixer)
+ parm = AC_PWRST_D0; /* SW0 (13h) = stereo mixer (idx 3) */
+ /* SW0 (13h), AIW 0/1/2 (12h/1fh/20h) */
+ update_power_state(codec, 0x13, parm);
+ update_power_state(codec, 0x12, parm);
+ update_power_state(codec, 0x1f, parm);
+ update_power_state(codec, 0x20, parm);
+
+ /* outputs */
+ /* PW 3/4 (16h/17h) */
+ parm = AC_PWRST_D3;
+ set_pin_power_state(codec, 0x17, &parm);
+ set_pin_power_state(codec, 0x16, &parm);
+ /* MW0 (1ah), AOW 0/1 (10h/1dh) */
+ update_power_state(codec, 0x1a, imux_is_smixer ? AC_PWRST_D0 : parm);
+ update_power_state(codec, 0x10, parm);
+ update_power_state(codec, 0x1d, parm);
}
-/* add playback controls from the parsed DAC table */
-static int vt1702_auto_create_line_out_ctls(struct via_spec *spec,
- const struct auto_pin_cfg *cfg)
+static int patch_vt1702(struct hda_codec *codec)
{
+ struct via_spec *spec;
int err;
- if (!cfg->line_out_pins[0])
- return -1;
-
- /* add control to mixer index 0 */
- err = via_add_control(spec, VIA_CTL_WIDGET_VOL,
- "Master Front Playback Volume",
- HDA_COMPOSE_AMP_VAL(0x1A, 3, 0, HDA_INPUT));
- if (err < 0)
- return err;
- err = via_add_control(spec, VIA_CTL_WIDGET_MUTE,
- "Master Front Playback Switch",
- HDA_COMPOSE_AMP_VAL(0x1A, 3, 0, HDA_INPUT));
- if (err < 0)
- return err;
-
- /* Front */
- err = via_add_control(spec, VIA_CTL_WIDGET_VOL,
- "Front Playback Volume",
- HDA_COMPOSE_AMP_VAL(0x10, 3, 0, HDA_OUTPUT));
- if (err < 0)
- return err;
- err = via_add_control(spec, VIA_CTL_WIDGET_MUTE,
- "Front Playback Switch",
- HDA_COMPOSE_AMP_VAL(0x16, 3, 0, HDA_OUTPUT));
- if (err < 0)
- return err;
-
- return 0;
-}
-
-static int vt1702_auto_create_hp_ctls(struct via_spec *spec, hda_nid_t pin)
-{
- int err, i;
- struct hda_input_mux *imux;
- static const char * const texts[] = { "ON", "OFF", NULL};
- if (!pin)
- return 0;
- spec->multiout.hp_nid = 0x1D;
- spec->hp_independent_mode_index = 0;
-
- err = via_add_control(spec, VIA_CTL_WIDGET_VOL,
- "Headphone Playback Volume",
- HDA_COMPOSE_AMP_VAL(0x1D, 3, 0, HDA_OUTPUT));
- if (err < 0)
- return err;
-
- err = via_add_control(spec, VIA_CTL_WIDGET_MUTE,
- "Headphone Playback Switch",
- HDA_COMPOSE_AMP_VAL(pin, 3, 0, HDA_OUTPUT));
- if (err < 0)
- return err;
-
- imux = &spec->private_imux[1];
-
- /* for hp mode select */
- for (i = 0; texts[i]; i++)
- snd_hda_add_imux_item(imux, texts[i], i, NULL);
-
- spec->hp_mux = &spec->private_imux[1];
- return 0;
-}
-
-/* create playback/capture controls for input pins */
-static int vt1702_auto_create_analog_input_ctls(struct hda_codec *codec,
- const struct auto_pin_cfg *cfg)
-{
- static hda_nid_t pin_idxs[] = { 0x14, 0x15, 0x18, 0xff };
- return vt_auto_create_analog_input_ctls(codec, cfg, 0x1a, pin_idxs,
- ARRAY_SIZE(pin_idxs));
-}
-
-static int vt1702_parse_auto_config(struct hda_codec *codec)
-{
- struct via_spec *spec = codec->spec;
- int err;
+ /* create a codec specific record */
+ spec = via_new_spec(codec);
+ if (spec == NULL)
+ return -ENOMEM;
- err = snd_hda_parse_pin_def_config(codec, &spec->autocfg, NULL);
- if (err < 0)
- return err;
- err = vt1702_auto_fill_dac_nids(spec, &spec->autocfg);
- if (err < 0)
- return err;
- if (!spec->autocfg.line_outs && !spec->autocfg.hp_pins[0])
- return 0; /* can't find valid BIOS pin config */
+ spec->gen.mixer_nid = 0x1a;
- err = vt1702_auto_create_line_out_ctls(spec, &spec->autocfg);
- if (err < 0)
- return err;
- err = vt1702_auto_create_hp_ctls(spec, spec->autocfg.hp_pins[0]);
- if (err < 0)
- return err;
/* limit AA path volume to 0 dB */
snd_hda_override_amp_caps(codec, 0x1A, HDA_INPUT,
(0x17 << AC_AMPCAP_OFFSET_SHIFT) |
(0x17 << AC_AMPCAP_NUM_STEPS_SHIFT) |
(0x5 << AC_AMPCAP_STEP_SIZE_SHIFT) |
(1 << AC_AMPCAP_MUTE_SHIFT));
- err = vt1702_auto_create_analog_input_ctls(codec, &spec->autocfg);
- if (err < 0)
- return err;
-
- spec->multiout.max_channels = spec->multiout.num_dacs * 2;
-
- fill_dig_outs(codec);
-
- if (spec->kctls.list)
- spec->mixers[spec->num_mixers++] = spec->kctls.list;
-
- spec->input_mux = &spec->private_imux[0];
-
- if (spec->hp_mux)
- via_hp_build(codec);
-
- return 1;
-}
-
-#ifdef CONFIG_SND_HDA_POWER_SAVE
-static struct hda_amp_list vt1702_loopbacks[] = {
- { 0x1A, HDA_INPUT, 1 },
- { 0x1A, HDA_INPUT, 2 },
- { 0x1A, HDA_INPUT, 3 },
- { 0x1A, HDA_INPUT, 4 },
- { } /* end */
-};
-#endif
-
-static int patch_vt1702(struct hda_codec *codec)
-{
- struct via_spec *spec;
- int err;
-
- /* create a codec specific record */
- spec = via_new_spec(codec);
- if (spec == NULL)
- return -ENOMEM;
/* automatic parse from the BIOS config */
- err = vt1702_parse_auto_config(codec);
+ err = via_parse_auto_config(codec);
if (err < 0) {
via_free(codec);
return err;
- } else if (!err) {
- printk(KERN_INFO "hda_codec: Cannot set up configuration "
- "from BIOS. Using genenic mode...\n");
}
- spec->init_verbs[spec->num_iverbs++] = vt1702_volume_init_verbs;
- spec->init_verbs[spec->num_iverbs++] = vt1702_uniwill_init_verbs;
-
- spec->stream_name_analog = "VT1702 Analog";
- spec->stream_analog_playback = &vt1702_pcm_analog_playback;
- spec->stream_analog_capture = &vt1702_pcm_analog_capture;
-
- spec->stream_name_digital = "VT1702 Digital";
- spec->stream_digital_playback = &vt1702_pcm_digital_playback;
-
- if (!spec->adc_nids && spec->input_mux) {
- spec->adc_nids = vt1702_adc_nids;
- spec->num_adc_nids = ARRAY_SIZE(vt1702_adc_nids);
- get_mux_nids(codec);
- spec->mixers[spec->num_mixers] = vt1702_capture_mixer;
- spec->num_mixers++;
- }
+ spec->init_verbs[spec->num_iverbs++] = vt1702_init_verbs;
codec->patch_ops = via_patch_ops;
- codec->patch_ops.init = via_auto_init;
- codec->patch_ops.unsol_event = via_unsol_event;
-#ifdef CONFIG_SND_HDA_POWER_SAVE
- spec->loopback.amplist = vt1702_loopbacks;
-#endif
-
+ spec->set_widgets_power_state = set_widgets_power_state_vt1702;
return 0;
}
/* Patch for VT1718S */
-/* capture mixer elements */
-static struct snd_kcontrol_new vt1718S_capture_mixer[] = {
- HDA_CODEC_VOLUME("Capture Volume", 0x10, 0x0, HDA_INPUT),
- HDA_CODEC_MUTE("Capture Switch", 0x10, 0x0, HDA_INPUT),
- HDA_CODEC_VOLUME_IDX("Capture Volume", 1, 0x11, 0x0, HDA_INPUT),
- HDA_CODEC_MUTE_IDX("Capture Switch", 1, 0x11, 0x0, HDA_INPUT),
- HDA_CODEC_VOLUME("Mic Boost Capture Volume", 0x2b, 0x0, HDA_INPUT),
- HDA_CODEC_VOLUME("Front Mic Boost Capture Volume", 0x29, 0x0,
- HDA_INPUT),
- {
- .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
- /* The multiple "Capture Source" controls confuse alsamixer
- * So call somewhat different..
- */
- .name = "Input Source",
- .count = 2,
- .info = via_mux_enum_info,
- .get = via_mux_enum_get,
- .put = via_mux_enum_put,
- },
- { } /* end */
-};
-
-static struct hda_verb vt1718S_volume_init_verbs[] = {
- /*
- * Unmute ADC0-1 and set the default input to mic-in
- */
- {0x10, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
- {0x11, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
-
-
- /* Mute input amps (CD, Line In, Mic 1 & Mic 2) of the analog-loopback
- * mixer widget
- */
- /* Amp Indices: CD = 1, Mic1 = 2, Line = 3, Mic2 = 4 */
- {0x21, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
- {0x21, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
- {0x21, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(2)},
- {0x21, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(3)},
- {0x21, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(5)},
-
- /* Setup default input of Front HP to MW9 */
- {0x28, AC_VERB_SET_CONNECT_SEL, 0x1},
- /* PW9 PW10 Output enable */
- {0x2d, AC_VERB_SET_PIN_WIDGET_CONTROL, AC_PINCTL_OUT_EN},
- {0x2e, AC_VERB_SET_PIN_WIDGET_CONTROL, AC_PINCTL_OUT_EN},
- /* PW11 Input enable */
- {0x2f, AC_VERB_SET_PIN_WIDGET_CONTROL, AC_PINCTL_IN_EN},
+static const struct hda_verb vt1718S_init_verbs[] = {
+ /* Enable MW0 adjust Gain 5 */
+ {0x1, 0xfb2, 0x10},
/* Enable Boost Volume backdoor */
{0x1, 0xf88, 0x8},
- /* MW0/1/2/3/4: un-mute index 0 (AOWx), mute index 1 (MW9) */
- {0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
- {0x19, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
- {0x1a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
- {0x1b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
- {0x1c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
- {0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
- {0x19, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
- {0x1a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
- {0x1b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
- {0x1c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
- /* set MUX1 = 2 (AOW4), MUX2 = 1 (AOW3) */
- {0x34, AC_VERB_SET_CONNECT_SEL, 0x2},
- {0x35, AC_VERB_SET_CONNECT_SEL, 0x1},
- /* Unmute MW4's index 0 */
- {0x1c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
- { }
-};
-
-static struct hda_verb vt1718S_uniwill_init_verbs[] = {
- {0x28, AC_VERB_SET_UNSOLICITED_ENABLE,
- AC_USRSP_EN | VIA_HP_EVENT | VIA_JACK_EVENT},
- {0x24, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT},
- {0x25, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT},
- {0x26, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT},
- {0x27, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT},
- {0x29, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT},
- {0x2a, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT},
- {0x2b, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT},
{ }
};
-static struct hda_pcm_stream vt1718S_pcm_analog_playback = {
- .substreams = 2,
- .channels_min = 2,
- .channels_max = 10,
- .nid = 0x8, /* NID to query formats and rates */
- .ops = {
- .open = via_playback_pcm_open,
- .prepare = via_playback_multi_pcm_prepare,
- .cleanup = via_playback_multi_pcm_cleanup,
- .close = via_pcm_open_close,
- },
-};
-
-static struct hda_pcm_stream vt1718S_pcm_analog_capture = {
- .substreams = 2,
- .channels_min = 2,
- .channels_max = 2,
- .nid = 0x10, /* NID to query formats and rates */
- .ops = {
- .open = via_pcm_open_close,
- .prepare = via_capture_pcm_prepare,
- .cleanup = via_capture_pcm_cleanup,
- .close = via_pcm_open_close,
- },
-};
-
-static struct hda_pcm_stream vt1718S_pcm_digital_playback = {
- .substreams = 2,
- .channels_min = 2,
- .channels_max = 2,
- /* NID is set in via_build_pcms */
- .ops = {
- .open = via_dig_playback_pcm_open,
- .close = via_dig_playback_pcm_close,
- .prepare = via_dig_playback_pcm_prepare,
- .cleanup = via_dig_playback_pcm_cleanup
- },
-};
-
-static struct hda_pcm_stream vt1718S_pcm_digital_capture = {
- .substreams = 1,
- .channels_min = 2,
- .channels_max = 2,
-};
-
-/* fill in the dac_nids table from the parsed pin configuration */
-static int vt1718S_auto_fill_dac_nids(struct via_spec *spec,
- const struct auto_pin_cfg *cfg)
+static void set_widgets_power_state_vt1718S(struct hda_codec *codec)
{
- int i;
- hda_nid_t nid;
-
- spec->multiout.num_dacs = cfg->line_outs;
-
- spec->multiout.dac_nids = spec->private_dac_nids;
-
- for (i = 0; i < 4; i++) {
- nid = cfg->line_out_pins[i];
- if (nid) {
- /* config dac list */
- switch (i) {
- case AUTO_SEQ_FRONT:
- spec->multiout.dac_nids[i] = 0x8;
- break;
- case AUTO_SEQ_CENLFE:
- spec->multiout.dac_nids[i] = 0xa;
- break;
- case AUTO_SEQ_SURROUND:
- spec->multiout.dac_nids[i] = 0x9;
- break;
- case AUTO_SEQ_SIDE:
- spec->multiout.dac_nids[i] = 0xb;
- break;
- }
- }
- }
-
- return 0;
-}
-
-/* add playback controls from the parsed DAC table */
-static int vt1718S_auto_create_multi_out_ctls(struct via_spec *spec,
- const struct auto_pin_cfg *cfg)
-{
- char name[32];
- static const char * const chname[4] = {
- "Front", "Surround", "C/LFE", "Side"
- };
- hda_nid_t nid_vols[] = {0x8, 0x9, 0xa, 0xb};
- hda_nid_t nid_mutes[] = {0x24, 0x25, 0x26, 0x27};
- hda_nid_t nid, nid_vol, nid_mute = 0;
- int i, err;
+ struct via_spec *spec = codec->spec;
+ int imux_is_smixer;
+ unsigned int parm, parm2;
+ /* MUX6 (1eh) = stereo mixer */
+ imux_is_smixer =
+ snd_hda_codec_read(codec, 0x1e, 0, AC_VERB_GET_CONNECT_SEL, 0x00) == 5;
+ /* inputs */
+ /* PW 5/6/7 (29h/2ah/2bh) */
+ parm = AC_PWRST_D3;
+ set_pin_power_state(codec, 0x29, &parm);
+ set_pin_power_state(codec, 0x2a, &parm);
+ set_pin_power_state(codec, 0x2b, &parm);
+ if (imux_is_smixer)
+ parm = AC_PWRST_D0;
+ /* MUX6/7 (1eh/1fh), AIW 0/1 (10h/11h) */
+ update_power_state(codec, 0x1e, parm);
+ update_power_state(codec, 0x1f, parm);
+ update_power_state(codec, 0x10, parm);
+ update_power_state(codec, 0x11, parm);
+
+ /* outputs */
+ /* PW3 (27h), MW2 (1ah), AOW3 (bh) */
+ parm = AC_PWRST_D3;
+ set_pin_power_state(codec, 0x27, &parm);
+ update_power_state(codec, 0x1a, parm);
+ parm2 = parm; /* for pin 0x0b */
+
+ /* PW2 (26h), AOW2 (ah) */
+ parm = AC_PWRST_D3;
+ set_pin_power_state(codec, 0x26, &parm);
+ if (smart51_enabled(codec))
+ set_pin_power_state(codec, 0x2b, &parm);
+ update_power_state(codec, 0xa, parm);
- for (i = 0; i <= AUTO_SEQ_SIDE; i++) {
- nid = cfg->line_out_pins[i];
+ /* PW0 (24h), AOW0 (8h) */
+ parm = AC_PWRST_D3;
+ set_pin_power_state(codec, 0x24, &parm);
+ if (!spec->gen.indep_hp_enabled) /* check for redirected HP */
+ set_pin_power_state(codec, 0x28, &parm);
+ update_power_state(codec, 0x8, parm);
+ if (!spec->gen.indep_hp_enabled && parm2 != AC_PWRST_D3)
+ parm = parm2;
+ update_power_state(codec, 0xb, parm);
+ /* MW9 (21h), Mw2 (1ah), AOW0 (8h) */
+ update_power_state(codec, 0x21, imux_is_smixer ? AC_PWRST_D0 : parm);
+
+ /* PW1 (25h), AOW1 (9h) */
+ parm = AC_PWRST_D3;
+ set_pin_power_state(codec, 0x25, &parm);
+ if (smart51_enabled(codec))
+ set_pin_power_state(codec, 0x2a, &parm);
+ update_power_state(codec, 0x9, parm);
- if (!nid)
- continue;
- nid_vol = nid_vols[i];
- nid_mute = nid_mutes[i];
-
- if (i == AUTO_SEQ_CENLFE) {
- /* Center/LFE */
- err = via_add_control(spec, VIA_CTL_WIDGET_VOL,
- "Center Playback Volume",
- HDA_COMPOSE_AMP_VAL(nid_vol, 1, 0,
- HDA_OUTPUT));
- if (err < 0)
- return err;
- err = via_add_control(spec, VIA_CTL_WIDGET_VOL,
- "LFE Playback Volume",
- HDA_COMPOSE_AMP_VAL(nid_vol, 2, 0,
- HDA_OUTPUT));
- if (err < 0)
- return err;
- err = via_add_control(
- spec, VIA_CTL_WIDGET_MUTE,
- "Center Playback Switch",
- HDA_COMPOSE_AMP_VAL(nid_mute, 1, 0,
- HDA_OUTPUT));
- if (err < 0)
- return err;
- err = via_add_control(
- spec, VIA_CTL_WIDGET_MUTE,
- "LFE Playback Switch",
- HDA_COMPOSE_AMP_VAL(nid_mute, 2, 0,
- HDA_OUTPUT));
- if (err < 0)
- return err;
- } else if (i == AUTO_SEQ_FRONT) {
- /* Front */
- sprintf(name, "%s Playback Volume", chname[i]);
- err = via_add_control(
- spec, VIA_CTL_WIDGET_VOL, name,
- HDA_COMPOSE_AMP_VAL(nid_vol, 3, 0, HDA_OUTPUT));
- if (err < 0)
- return err;
- sprintf(name, "%s Playback Switch", chname[i]);
- err = via_add_control(
- spec, VIA_CTL_WIDGET_MUTE, name,
- HDA_COMPOSE_AMP_VAL(nid_mute, 3, 0,
- HDA_OUTPUT));
- if (err < 0)
- return err;
- } else {
- sprintf(name, "%s Playback Volume", chname[i]);
- err = via_add_control(
- spec, VIA_CTL_WIDGET_VOL, name,
- HDA_COMPOSE_AMP_VAL(nid_vol, 3, 0, HDA_OUTPUT));
- if (err < 0)
- return err;
- sprintf(name, "%s Playback Switch", chname[i]);
- err = via_add_control(
- spec, VIA_CTL_WIDGET_MUTE, name,
- HDA_COMPOSE_AMP_VAL(nid_mute, 3, 0,
- HDA_OUTPUT));
- if (err < 0)
- return err;
- }
+ if (spec->gen.indep_hp_enabled) {
+ /* PW4 (28h), MW3 (1bh), MUX1(34h), AOW4 (ch) */
+ parm = AC_PWRST_D3;
+ set_pin_power_state(codec, 0x28, &parm);
+ update_power_state(codec, 0x1b, parm);
+ update_power_state(codec, 0x34, parm);
+ update_power_state(codec, 0xc, parm);
}
- return 0;
}
-static int vt1718S_auto_create_hp_ctls(struct via_spec *spec, hda_nid_t pin)
+/* Add a connection to the primary DAC from AA-mixer for some codecs
+ * This isn't listed from the raw info, but the chip has a secret connection.
+ */
+static int add_secret_dac_path(struct hda_codec *codec)
{
- int err;
+ struct via_spec *spec = codec->spec;
+ int i, nums;
+ hda_nid_t conn[8];
+ hda_nid_t nid;
- if (!pin)
+ if (!spec->gen.mixer_nid)
return 0;
+ nums = snd_hda_get_connections(codec, spec->gen.mixer_nid, conn,
+ ARRAY_SIZE(conn) - 1);
+ for (i = 0; i < nums; i++) {
+ if (get_wcaps_type(get_wcaps(codec, conn[i])) == AC_WID_AUD_OUT)
+ return 0;
+ }
- spec->multiout.hp_nid = 0xc; /* AOW4 */
- spec->hp_independent_mode_index = 1;
-
- err = via_add_control(spec, VIA_CTL_WIDGET_VOL,
- "Headphone Playback Volume",
- HDA_COMPOSE_AMP_VAL(0xc, 3, 0, HDA_OUTPUT));
- if (err < 0)
- return err;
-
- err = via_add_control(spec, VIA_CTL_WIDGET_MUTE,
- "Headphone Playback Switch",
- HDA_COMPOSE_AMP_VAL(pin, 3, 0, HDA_OUTPUT));
- if (err < 0)
- return err;
-
- create_hp_imux(spec);
+ /* find the primary DAC and add to the connection list */
+ nid = codec->start_nid;
+ for (i = 0; i < codec->num_nodes; i++, nid++) {
+ unsigned int caps = get_wcaps(codec, nid);
+ if (get_wcaps_type(caps) == AC_WID_AUD_OUT &&
+ !(caps & AC_WCAP_DIGITAL)) {
+ conn[nums++] = nid;
+ return snd_hda_override_conn_list(codec,
+ spec->gen.mixer_nid,
+ nums, conn);
+ }
+ }
return 0;
}
-/* create playback/capture controls for input pins */
-static int vt1718S_auto_create_analog_input_ctls(struct hda_codec *codec,
- const struct auto_pin_cfg *cfg)
-{
- static hda_nid_t pin_idxs[] = { 0x2c, 0x2b, 0x2a, 0x29, 0, 0xff };
- return vt_auto_create_analog_input_ctls(codec, cfg, 0x21, pin_idxs,
- ARRAY_SIZE(pin_idxs));
-}
-
-static int vt1718S_parse_auto_config(struct hda_codec *codec)
-{
- struct via_spec *spec = codec->spec;
- int err;
-
- err = snd_hda_parse_pin_def_config(codec, &spec->autocfg, NULL);
-
- if (err < 0)
- return err;
- err = vt1718S_auto_fill_dac_nids(spec, &spec->autocfg);
- if (err < 0)
- return err;
- if (!spec->autocfg.line_outs && !spec->autocfg.hp_pins[0])
- return 0; /* can't find valid BIOS pin config */
-
- err = vt1718S_auto_create_multi_out_ctls(spec, &spec->autocfg);
- if (err < 0)
- return err;
- err = vt1718S_auto_create_hp_ctls(spec, spec->autocfg.hp_pins[0]);
- if (err < 0)
- return err;
- err = vt1718S_auto_create_analog_input_ctls(codec, &spec->autocfg);
- if (err < 0)
- return err;
-
- spec->multiout.max_channels = spec->multiout.num_dacs * 2;
-
- fill_dig_outs(codec);
-
- if (spec->autocfg.dig_in_pin && codec->vendor_id == 0x11060428)
- spec->dig_in_nid = 0x13;
-
- if (spec->kctls.list)
- spec->mixers[spec->num_mixers++] = spec->kctls.list;
-
- spec->input_mux = &spec->private_imux[0];
-
- if (spec->hp_mux)
- via_hp_build(codec);
-
- via_smart51_build(spec);
-
- return 1;
-}
-
-#ifdef CONFIG_SND_HDA_POWER_SAVE
-static struct hda_amp_list vt1718S_loopbacks[] = {
- { 0x21, HDA_INPUT, 1 },
- { 0x21, HDA_INPUT, 2 },
- { 0x21, HDA_INPUT, 3 },
- { 0x21, HDA_INPUT, 4 },
- { } /* end */
-};
-#endif
static int patch_vt1718S(struct hda_codec *codec)
{
@@ -4835,56 +1152,23 @@ static int patch_vt1718S(struct hda_codec *codec)
if (spec == NULL)
return -ENOMEM;
+ spec->gen.mixer_nid = 0x21;
+ override_mic_boost(codec, 0x2b, 0, 3, 40);
+ override_mic_boost(codec, 0x29, 0, 3, 40);
+ add_secret_dac_path(codec);
+
/* automatic parse from the BIOS config */
- err = vt1718S_parse_auto_config(codec);
+ err = via_parse_auto_config(codec);
if (err < 0) {
via_free(codec);
return err;
- } else if (!err) {
- printk(KERN_INFO "hda_codec: Cannot set up configuration "
- "from BIOS. Using genenic mode...\n");
}
- spec->init_verbs[spec->num_iverbs++] = vt1718S_volume_init_verbs;
- spec->init_verbs[spec->num_iverbs++] = vt1718S_uniwill_init_verbs;
-
- if (codec->vendor_id == 0x11060441)
- spec->stream_name_analog = "VT2020 Analog";
- else if (codec->vendor_id == 0x11064441)
- spec->stream_name_analog = "VT1828S Analog";
- else
- spec->stream_name_analog = "VT1718S Analog";
- spec->stream_analog_playback = &vt1718S_pcm_analog_playback;
- spec->stream_analog_capture = &vt1718S_pcm_analog_capture;
-
- if (codec->vendor_id == 0x11060441)
- spec->stream_name_digital = "VT2020 Digital";
- else if (codec->vendor_id == 0x11064441)
- spec->stream_name_digital = "VT1828S Digital";
- else
- spec->stream_name_digital = "VT1718S Digital";
- spec->stream_digital_playback = &vt1718S_pcm_digital_playback;
- if (codec->vendor_id == 0x11060428 || codec->vendor_id == 0x11060441)
- spec->stream_digital_capture = &vt1718S_pcm_digital_capture;
-
- if (!spec->adc_nids && spec->input_mux) {
- spec->adc_nids = vt1718S_adc_nids;
- spec->num_adc_nids = ARRAY_SIZE(vt1718S_adc_nids);
- get_mux_nids(codec);
- override_mic_boost(codec, 0x2b, 0, 3, 40);
- override_mic_boost(codec, 0x29, 0, 3, 40);
- spec->mixers[spec->num_mixers] = vt1718S_capture_mixer;
- spec->num_mixers++;
- }
+ spec->init_verbs[spec->num_iverbs++] = vt1718S_init_verbs;
codec->patch_ops = via_patch_ops;
- codec->patch_ops.init = via_auto_init;
- codec->patch_ops.unsol_event = via_unsol_event;
-
-#ifdef CONFIG_SND_HDA_POWER_SAVE
- spec->loopback.amplist = vt1718S_loopbacks;
-#endif
+ spec->set_widgets_power_state = set_widgets_power_state_vt1718S;
return 0;
}
@@ -4925,32 +1209,11 @@ static int vt1716s_dmic_put(struct snd_kcontrol *kcontrol,
snd_hda_codec_write(codec, 0x26, 0,
AC_VERB_SET_CONNECT_SEL, index);
spec->dmic_enabled = index;
- set_jack_power_state(codec);
-
+ set_widgets_power_state(codec);
return 1;
}
-/* capture mixer elements */
-static struct snd_kcontrol_new vt1716S_capture_mixer[] = {
- HDA_CODEC_VOLUME("Capture Volume", 0x13, 0x0, HDA_INPUT),
- HDA_CODEC_MUTE("Capture Switch", 0x13, 0x0, HDA_INPUT),
- HDA_CODEC_VOLUME_IDX("Capture Volume", 1, 0x14, 0x0, HDA_INPUT),
- HDA_CODEC_MUTE_IDX("Capture Switch", 1, 0x14, 0x0, HDA_INPUT),
- HDA_CODEC_VOLUME("Mic Boost Capture Volume", 0x1A, 0x0, HDA_INPUT),
- HDA_CODEC_VOLUME("Front Mic Boost Capture Volume", 0x1E, 0x0,
- HDA_INPUT),
- {
- .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
- .name = "Input Source",
- .count = 1,
- .info = via_mux_enum_info,
- .get = via_mux_enum_get,
- .put = via_mux_enum_put,
- },
- { } /* end */
-};
-
-static struct snd_kcontrol_new vt1716s_dmic_mixer[] = {
+static const struct snd_kcontrol_new vt1716s_dmic_mixer[] = {
HDA_CODEC_VOLUME("Digital Mic Capture Volume", 0x22, 0x0, HDA_INPUT),
{
.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
@@ -4966,50 +1229,12 @@ static struct snd_kcontrol_new vt1716s_dmic_mixer[] = {
/* mono-out mixer elements */
-static struct snd_kcontrol_new vt1716S_mono_out_mixer[] = {
+static const struct snd_kcontrol_new vt1716S_mono_out_mixer[] = {
HDA_CODEC_MUTE("Mono Playback Switch", 0x2a, 0x0, HDA_OUTPUT),
{ } /* end */
};
-static struct hda_verb vt1716S_volume_init_verbs[] = {
- /*
- * Unmute ADC0-1 and set the default input to mic-in
- */
- {0x13, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
- {0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
-
-
- /* Mute input amps (CD, Line In, Mic 1 & Mic 2) of the analog-loopback
- * mixer widget
- */
- /* Amp Indices: CD = 1, Mic1 = 2, Line = 3, Mic2 = 4 */
- {0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
- {0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
- {0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(2)},
- {0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(3)},
- {0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(4)},
-
- /* MUX Indices: Stereo Mixer = 5 */
- {0x17, AC_VERB_SET_CONNECT_SEL, 0x5},
-
- /* Setup default input of PW4 to MW0 */
- {0x1d, AC_VERB_SET_CONNECT_SEL, 0x0},
-
- /* Setup default input of SW1 as MW0 */
- {0x18, AC_VERB_SET_CONNECT_SEL, 0x1},
-
- /* Setup default input of SW4 as AOW0 */
- {0x28, AC_VERB_SET_CONNECT_SEL, 0x1},
-
- /* PW9 PW10 Output enable */
- {0x20, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x40},
- {0x21, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x40},
-
- /* Unmute SW1, PW12 */
- {0x29, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
- {0x2a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
- /* PW12 Output enable */
- {0x2a, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x40},
+static const struct hda_verb vt1716S_init_verbs[] = {
/* Enable Boost Volume backdoor */
{0x1, 0xf8a, 0x80},
/* don't bybass mixer */
@@ -5019,272 +1244,95 @@ static struct hda_verb vt1716S_volume_init_verbs[] = {
{ }
};
-
-static struct hda_verb vt1716S_uniwill_init_verbs[] = {
- {0x1d, AC_VERB_SET_UNSOLICITED_ENABLE,
- AC_USRSP_EN | VIA_HP_EVENT | VIA_JACK_EVENT},
- {0x19, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT},
- {0x1a, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT},
- {0x1b, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT},
- {0x1c, AC_VERB_SET_UNSOLICITED_ENABLE,
- AC_USRSP_EN | VIA_MONO_EVENT | VIA_JACK_EVENT},
- {0x1e, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT},
- {0x23, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT},
- { }
-};
-
-static struct hda_pcm_stream vt1716S_pcm_analog_playback = {
- .substreams = 2,
- .channels_min = 2,
- .channels_max = 6,
- .nid = 0x10, /* NID to query formats and rates */
- .ops = {
- .open = via_playback_pcm_open,
- .prepare = via_playback_multi_pcm_prepare,
- .cleanup = via_playback_multi_pcm_cleanup,
- .close = via_pcm_open_close,
- },
-};
-
-static struct hda_pcm_stream vt1716S_pcm_analog_capture = {
- .substreams = 2,
- .channels_min = 2,
- .channels_max = 2,
- .nid = 0x13, /* NID to query formats and rates */
- .ops = {
- .open = via_pcm_open_close,
- .prepare = via_capture_pcm_prepare,
- .cleanup = via_capture_pcm_cleanup,
- .close = via_pcm_open_close,
- },
-};
-
-static struct hda_pcm_stream vt1716S_pcm_digital_playback = {
- .substreams = 2,
- .channels_min = 2,
- .channels_max = 2,
- /* NID is set in via_build_pcms */
- .ops = {
- .open = via_dig_playback_pcm_open,
- .close = via_dig_playback_pcm_close,
- .prepare = via_dig_playback_pcm_prepare,
- .cleanup = via_dig_playback_pcm_cleanup
- },
-};
-
-/* fill in the dac_nids table from the parsed pin configuration */
-static int vt1716S_auto_fill_dac_nids(struct via_spec *spec,
- const struct auto_pin_cfg *cfg)
-{ int i;
- hda_nid_t nid;
-
- spec->multiout.num_dacs = cfg->line_outs;
-
- spec->multiout.dac_nids = spec->private_dac_nids;
-
- for (i = 0; i < 3; i++) {
- nid = cfg->line_out_pins[i];
- if (nid) {
- /* config dac list */
- switch (i) {
- case AUTO_SEQ_FRONT:
- spec->multiout.dac_nids[i] = 0x10;
- break;
- case AUTO_SEQ_CENLFE:
- spec->multiout.dac_nids[i] = 0x25;
- break;
- case AUTO_SEQ_SURROUND:
- spec->multiout.dac_nids[i] = 0x11;
- break;
- }
- }
- }
-
- return 0;
-}
-
-/* add playback controls from the parsed DAC table */
-static int vt1716S_auto_create_multi_out_ctls(struct via_spec *spec,
- const struct auto_pin_cfg *cfg)
-{
- char name[32];
- static const char * const chname[3] = {
- "Front", "Surround", "C/LFE"
- };
- hda_nid_t nid_vols[] = {0x10, 0x11, 0x25};
- hda_nid_t nid_mutes[] = {0x1C, 0x18, 0x27};
- hda_nid_t nid, nid_vol, nid_mute;
- int i, err;
-
- for (i = 0; i <= AUTO_SEQ_CENLFE; i++) {
- nid = cfg->line_out_pins[i];
-
- if (!nid)
- continue;
-
- nid_vol = nid_vols[i];
- nid_mute = nid_mutes[i];
-
- if (i == AUTO_SEQ_CENLFE) {
- err = via_add_control(
- spec, VIA_CTL_WIDGET_VOL,
- "Center Playback Volume",
- HDA_COMPOSE_AMP_VAL(nid_vol, 1, 0, HDA_OUTPUT));
- if (err < 0)
- return err;
- err = via_add_control(
- spec, VIA_CTL_WIDGET_VOL,
- "LFE Playback Volume",
- HDA_COMPOSE_AMP_VAL(nid_vol, 2, 0, HDA_OUTPUT));
- if (err < 0)
- return err;
- err = via_add_control(
- spec, VIA_CTL_WIDGET_MUTE,
- "Center Playback Switch",
- HDA_COMPOSE_AMP_VAL(nid_mute, 1, 0,
- HDA_OUTPUT));
- if (err < 0)
- return err;
- err = via_add_control(
- spec, VIA_CTL_WIDGET_MUTE,
- "LFE Playback Switch",
- HDA_COMPOSE_AMP_VAL(nid_mute, 2, 0,
- HDA_OUTPUT));
- if (err < 0)
- return err;
- } else if (i == AUTO_SEQ_FRONT) {
-
- err = via_add_control(
- spec, VIA_CTL_WIDGET_VOL,
- "Master Front Playback Volume",
- HDA_COMPOSE_AMP_VAL(0x16, 3, 0, HDA_INPUT));
- if (err < 0)
- return err;
- err = via_add_control(
- spec, VIA_CTL_WIDGET_MUTE,
- "Master Front Playback Switch",
- HDA_COMPOSE_AMP_VAL(0x16, 3, 0, HDA_INPUT));
- if (err < 0)
- return err;
-
- sprintf(name, "%s Playback Volume", chname[i]);
- err = via_add_control(
- spec, VIA_CTL_WIDGET_VOL, name,
- HDA_COMPOSE_AMP_VAL(nid_vol, 3, 0, HDA_OUTPUT));
- if (err < 0)
- return err;
- sprintf(name, "%s Playback Switch", chname[i]);
- err = via_add_control(
- spec, VIA_CTL_WIDGET_MUTE, name,
- HDA_COMPOSE_AMP_VAL(nid_mute, 3, 0,
- HDA_OUTPUT));
- if (err < 0)
- return err;
- } else {
- sprintf(name, "%s Playback Volume", chname[i]);
- err = via_add_control(
- spec, VIA_CTL_WIDGET_VOL, name,
- HDA_COMPOSE_AMP_VAL(nid_vol, 3, 0, HDA_OUTPUT));
- if (err < 0)
- return err;
- sprintf(name, "%s Playback Switch", chname[i]);
- err = via_add_control(
- spec, VIA_CTL_WIDGET_MUTE, name,
- HDA_COMPOSE_AMP_VAL(nid_mute, 3, 0,
- HDA_OUTPUT));
- if (err < 0)
- return err;
- }
- }
- return 0;
-}
-
-static int vt1716S_auto_create_hp_ctls(struct via_spec *spec, hda_nid_t pin)
-{
- int err;
-
- if (!pin)
- return 0;
-
- spec->multiout.hp_nid = 0x25; /* AOW3 */
- spec->hp_independent_mode_index = 1;
-
- err = via_add_control(spec, VIA_CTL_WIDGET_VOL,
- "Headphone Playback Volume",
- HDA_COMPOSE_AMP_VAL(0x25, 3, 0, HDA_OUTPUT));
- if (err < 0)
- return err;
-
- err = via_add_control(spec, VIA_CTL_WIDGET_MUTE,
- "Headphone Playback Switch",
- HDA_COMPOSE_AMP_VAL(pin, 3, 0, HDA_OUTPUT));
- if (err < 0)
- return err;
-
- create_hp_imux(spec);
- return 0;
-}
-
-/* create playback/capture controls for input pins */
-static int vt1716S_auto_create_analog_input_ctls(struct hda_codec *codec,
- const struct auto_pin_cfg *cfg)
-{
- static hda_nid_t pin_idxs[] = { 0x1f, 0x1a, 0x1b, 0x1e, 0, 0xff };
- return vt_auto_create_analog_input_ctls(codec, cfg, 0x16, pin_idxs,
- ARRAY_SIZE(pin_idxs));
-}
-
-static int vt1716S_parse_auto_config(struct hda_codec *codec)
+static void set_widgets_power_state_vt1716S(struct hda_codec *codec)
{
struct via_spec *spec = codec->spec;
- int err;
-
- err = snd_hda_parse_pin_def_config(codec, &spec->autocfg, NULL);
- if (err < 0)
- return err;
- err = vt1716S_auto_fill_dac_nids(spec, &spec->autocfg);
- if (err < 0)
- return err;
- if (!spec->autocfg.line_outs && !spec->autocfg.hp_pins[0])
- return 0; /* can't find valid BIOS pin config */
-
- err = vt1716S_auto_create_multi_out_ctls(spec, &spec->autocfg);
- if (err < 0)
- return err;
- err = vt1716S_auto_create_hp_ctls(spec, spec->autocfg.hp_pins[0]);
- if (err < 0)
- return err;
- err = vt1716S_auto_create_analog_input_ctls(codec, &spec->autocfg);
- if (err < 0)
- return err;
-
- spec->multiout.max_channels = spec->multiout.num_dacs * 2;
-
- fill_dig_outs(codec);
+ int imux_is_smixer;
+ unsigned int parm;
+ unsigned int mono_out, present;
+ /* SW0 (17h) = stereo mixer */
+ imux_is_smixer =
+ (snd_hda_codec_read(codec, 0x17, 0,
+ AC_VERB_GET_CONNECT_SEL, 0x00) == 5);
+ /* inputs */
+ /* PW 1/2/5 (1ah/1bh/1eh) */
+ parm = AC_PWRST_D3;
+ set_pin_power_state(codec, 0x1a, &parm);
+ set_pin_power_state(codec, 0x1b, &parm);
+ set_pin_power_state(codec, 0x1e, &parm);
+ if (imux_is_smixer)
+ parm = AC_PWRST_D0;
+ /* SW0 (17h), AIW0(13h) */
+ update_power_state(codec, 0x17, parm);
+ update_power_state(codec, 0x13, parm);
+
+ parm = AC_PWRST_D3;
+ set_pin_power_state(codec, 0x1e, &parm);
+ /* PW11 (22h) */
+ if (spec->dmic_enabled)
+ set_pin_power_state(codec, 0x22, &parm);
+ else
+ update_power_state(codec, 0x22, AC_PWRST_D3);
+
+ /* SW2(26h), AIW1(14h) */
+ update_power_state(codec, 0x26, parm);
+ update_power_state(codec, 0x14, parm);
+
+ /* outputs */
+ /* PW0 (19h), SW1 (18h), AOW1 (11h) */
+ parm = AC_PWRST_D3;
+ set_pin_power_state(codec, 0x19, &parm);
+ /* Smart 5.1 PW2(1bh) */
+ if (smart51_enabled(codec))
+ set_pin_power_state(codec, 0x1b, &parm);
+ update_power_state(codec, 0x18, parm);
+ update_power_state(codec, 0x11, parm);
+
+ /* PW7 (23h), SW3 (27h), AOW3 (25h) */
+ parm = AC_PWRST_D3;
+ set_pin_power_state(codec, 0x23, &parm);
+ /* Smart 5.1 PW1(1ah) */
+ if (smart51_enabled(codec))
+ set_pin_power_state(codec, 0x1a, &parm);
+ update_power_state(codec, 0x27, parm);
- if (spec->kctls.list)
- spec->mixers[spec->num_mixers++] = spec->kctls.list;
+ /* Smart 5.1 PW5(1eh) */
+ if (smart51_enabled(codec))
+ set_pin_power_state(codec, 0x1e, &parm);
+ update_power_state(codec, 0x25, parm);
- spec->input_mux = &spec->private_imux[0];
+ /* Mono out */
+ /* SW4(28h)->MW1(29h)-> PW12 (2ah)*/
+ present = snd_hda_jack_detect(codec, 0x1c);
- if (spec->hp_mux)
- via_hp_build(codec);
+ if (present)
+ mono_out = 0;
+ else {
+ present = snd_hda_jack_detect(codec, 0x1d);
+ if (!spec->gen.indep_hp_enabled && present)
+ mono_out = 0;
+ else
+ mono_out = 1;
+ }
+ parm = mono_out ? AC_PWRST_D0 : AC_PWRST_D3;
+ update_power_state(codec, 0x28, parm);
+ update_power_state(codec, 0x29, parm);
+ update_power_state(codec, 0x2a, parm);
- via_smart51_build(spec);
+ /* PW 3/4 (1ch/1dh) */
+ parm = AC_PWRST_D3;
+ set_pin_power_state(codec, 0x1c, &parm);
+ set_pin_power_state(codec, 0x1d, &parm);
+ /* HP Independent Mode, power on AOW3 */
+ if (spec->gen.indep_hp_enabled)
+ update_power_state(codec, 0x25, parm);
- return 1;
+ /* force to D0 for internal Speaker */
+ /* MW0 (16h), AOW0 (10h) */
+ update_power_state(codec, 0x16, imux_is_smixer ? AC_PWRST_D0 : parm);
+ update_power_state(codec, 0x10, mono_out ? AC_PWRST_D0 : parm);
}
-#ifdef CONFIG_SND_HDA_POWER_SAVE
-static struct hda_amp_list vt1716S_loopbacks[] = {
- { 0x16, HDA_INPUT, 1 },
- { 0x16, HDA_INPUT, 2 },
- { 0x16, HDA_INPUT, 3 },
- { 0x16, HDA_INPUT, 4 },
- { } /* end */
-};
-#endif
-
static int patch_vt1716S(struct hda_codec *codec)
{
struct via_spec *spec;
@@ -5295,321 +1343,191 @@ static int patch_vt1716S(struct hda_codec *codec)
if (spec == NULL)
return -ENOMEM;
+ spec->gen.mixer_nid = 0x16;
+ override_mic_boost(codec, 0x1a, 0, 3, 40);
+ override_mic_boost(codec, 0x1e, 0, 3, 40);
+
/* automatic parse from the BIOS config */
- err = vt1716S_parse_auto_config(codec);
+ err = via_parse_auto_config(codec);
if (err < 0) {
via_free(codec);
return err;
- } else if (!err) {
- printk(KERN_INFO "hda_codec: Cannot set up configuration "
- "from BIOS. Using genenic mode...\n");
}
- spec->init_verbs[spec->num_iverbs++] = vt1716S_volume_init_verbs;
- spec->init_verbs[spec->num_iverbs++] = vt1716S_uniwill_init_verbs;
-
- spec->stream_name_analog = "VT1716S Analog";
- spec->stream_analog_playback = &vt1716S_pcm_analog_playback;
- spec->stream_analog_capture = &vt1716S_pcm_analog_capture;
-
- spec->stream_name_digital = "VT1716S Digital";
- spec->stream_digital_playback = &vt1716S_pcm_digital_playback;
-
- if (!spec->adc_nids && spec->input_mux) {
- spec->adc_nids = vt1716S_adc_nids;
- spec->num_adc_nids = ARRAY_SIZE(vt1716S_adc_nids);
- get_mux_nids(codec);
- override_mic_boost(codec, 0x1a, 0, 3, 40);
- override_mic_boost(codec, 0x1e, 0, 3, 40);
- spec->mixers[spec->num_mixers] = vt1716S_capture_mixer;
- spec->num_mixers++;
- }
-
- spec->mixers[spec->num_mixers] = vt1716s_dmic_mixer;
- spec->num_mixers++;
+ spec->init_verbs[spec->num_iverbs++] = vt1716S_init_verbs;
+ spec->mixers[spec->num_mixers++] = vt1716s_dmic_mixer;
spec->mixers[spec->num_mixers++] = vt1716S_mono_out_mixer;
codec->patch_ops = via_patch_ops;
- codec->patch_ops.init = via_auto_init;
- codec->patch_ops.unsol_event = via_unsol_event;
-
-#ifdef CONFIG_SND_HDA_POWER_SAVE
- spec->loopback.amplist = vt1716S_loopbacks;
-#endif
-
+ spec->set_widgets_power_state = set_widgets_power_state_vt1716S;
return 0;
}
/* for vt2002P */
-/* capture mixer elements */
-static struct snd_kcontrol_new vt2002P_capture_mixer[] = {
- HDA_CODEC_VOLUME("Capture Volume", 0x10, 0x0, HDA_INPUT),
- HDA_CODEC_MUTE("Capture Switch", 0x10, 0x0, HDA_INPUT),
- HDA_CODEC_VOLUME_IDX("Capture Volume", 1, 0x11, 0x0, HDA_INPUT),
- HDA_CODEC_MUTE_IDX("Capture Switch", 1, 0x11, 0x0, HDA_INPUT),
- HDA_CODEC_VOLUME("Mic Boost Capture Volume", 0x2b, 0x0, HDA_INPUT),
- HDA_CODEC_VOLUME("Front Mic Boost Capture Volume", 0x29, 0x0,
- HDA_INPUT),
- {
- .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
- /* The multiple "Capture Source" controls confuse alsamixer
- * So call somewhat different..
- */
- /* .name = "Capture Source", */
- .name = "Input Source",
- .count = 2,
- .info = via_mux_enum_info,
- .get = via_mux_enum_get,
- .put = via_mux_enum_put,
- },
- { } /* end */
-};
-
-static struct hda_verb vt2002P_volume_init_verbs[] = {
- /*
- * Unmute ADC0-1 and set the default input to mic-in
- */
- {0x8, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
- {0x9, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
-
-
- /* Mute input amps (CD, Line In, Mic 1 & Mic 2) of the analog-loopback
- * mixer widget
- */
- /* Amp Indices: CD = 1, Mic1 = 2, Line = 3, Mic2 = 4 */
- {0x21, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
- {0x21, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
- {0x21, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(2)},
- {0x21, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(3)},
- {0x21, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(4)},
-
- /* MUX Indices: Mic = 0 */
- {0x1e, AC_VERB_SET_CONNECT_SEL, 0},
- {0x1f, AC_VERB_SET_CONNECT_SEL, 0},
-
- /* PW9 Output enable */
- {0x2d, AC_VERB_SET_PIN_WIDGET_CONTROL, AC_PINCTL_OUT_EN},
-
+static const struct hda_verb vt2002P_init_verbs[] = {
+ /* Class-D speaker related verbs */
+ {0x1, 0xfe0, 0x4},
+ {0x1, 0xfe9, 0x80},
+ {0x1, 0xfe2, 0x22},
/* Enable Boost Volume backdoor */
{0x1, 0xfb9, 0x24},
-
- /* MW0/1/4/8: un-mute index 0 (MUXx), un-mute index 1 (MW9) */
- {0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
- {0x19, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
- {0x1c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
- {0x17, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
- {0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
- {0x19, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
- {0x1c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
- {0x17, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
-
- /* set MUX0/1/4/8 = 0 (AOW0) */
- {0x34, AC_VERB_SET_CONNECT_SEL, 0},
- {0x35, AC_VERB_SET_CONNECT_SEL, 0},
- {0x37, AC_VERB_SET_CONNECT_SEL, 0},
- {0x3b, AC_VERB_SET_CONNECT_SEL, 0},
-
- /* set PW0 index=0 (MW0) */
- {0x24, AC_VERB_SET_CONNECT_SEL, 0},
-
/* Enable AOW0 to MW9 */
{0x1, 0xfb8, 0x88},
{ }
};
-
-static struct hda_verb vt2002P_uniwill_init_verbs[] = {
- {0x25, AC_VERB_SET_UNSOLICITED_ENABLE,
- AC_USRSP_EN | VIA_JACK_EVENT | VIA_BIND_HP_EVENT},
- {0x26, AC_VERB_SET_UNSOLICITED_ENABLE,
- AC_USRSP_EN | VIA_JACK_EVENT | VIA_BIND_HP_EVENT},
- {0x29, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT},
- {0x2a, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT},
- {0x2b, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT},
+static const struct hda_verb vt1802_init_verbs[] = {
+ /* Enable Boost Volume backdoor */
+ {0x1, 0xfb9, 0x24},
+ /* Enable AOW0 to MW9 */
+ {0x1, 0xfb8, 0x88},
{ }
};
-static struct hda_pcm_stream vt2002P_pcm_analog_playback = {
- .substreams = 2,
- .channels_min = 2,
- .channels_max = 2,
- .nid = 0x8, /* NID to query formats and rates */
- .ops = {
- .open = via_playback_pcm_open,
- .prepare = via_playback_multi_pcm_prepare,
- .cleanup = via_playback_multi_pcm_cleanup,
- .close = via_pcm_open_close,
- },
-};
-
-static struct hda_pcm_stream vt2002P_pcm_analog_capture = {
- .substreams = 2,
- .channels_min = 2,
- .channels_max = 2,
- .nid = 0x10, /* NID to query formats and rates */
- .ops = {
- .open = via_pcm_open_close,
- .prepare = via_capture_pcm_prepare,
- .cleanup = via_capture_pcm_cleanup,
- .close = via_pcm_open_close,
- },
-};
-
-static struct hda_pcm_stream vt2002P_pcm_digital_playback = {
- .substreams = 1,
- .channels_min = 2,
- .channels_max = 2,
- /* NID is set in via_build_pcms */
- .ops = {
- .open = via_dig_playback_pcm_open,
- .close = via_dig_playback_pcm_close,
- .prepare = via_dig_playback_pcm_prepare,
- .cleanup = via_dig_playback_pcm_cleanup
- },
-};
-
-/* fill in the dac_nids table from the parsed pin configuration */
-static int vt2002P_auto_fill_dac_nids(struct via_spec *spec,
- const struct auto_pin_cfg *cfg)
-{
- spec->multiout.num_dacs = 1;
- spec->multiout.dac_nids = spec->private_dac_nids;
- if (cfg->line_out_pins[0])
- spec->multiout.dac_nids[0] = 0x8;
- return 0;
-}
-
-/* add playback controls from the parsed DAC table */
-static int vt2002P_auto_create_multi_out_ctls(struct via_spec *spec,
- const struct auto_pin_cfg *cfg)
+static void set_widgets_power_state_vt2002P(struct hda_codec *codec)
{
- int err;
+ struct via_spec *spec = codec->spec;
+ int imux_is_smixer;
+ unsigned int parm;
+ unsigned int present;
+ /* MUX9 (1eh) = stereo mixer */
+ imux_is_smixer =
+ snd_hda_codec_read(codec, 0x1e, 0, AC_VERB_GET_CONNECT_SEL, 0x00) == 3;
+ /* inputs */
+ /* PW 5/6/7 (29h/2ah/2bh) */
+ parm = AC_PWRST_D3;
+ set_pin_power_state(codec, 0x29, &parm);
+ set_pin_power_state(codec, 0x2a, &parm);
+ set_pin_power_state(codec, 0x2b, &parm);
+ parm = AC_PWRST_D0;
+ /* MUX9/10 (1eh/1fh), AIW 0/1 (10h/11h) */
+ update_power_state(codec, 0x1e, parm);
+ update_power_state(codec, 0x1f, parm);
+ update_power_state(codec, 0x10, parm);
+ update_power_state(codec, 0x11, parm);
+
+ /* outputs */
+ /* AOW0 (8h)*/
+ update_power_state(codec, 0x8, parm);
+
+ if (spec->codec_type == VT1802) {
+ /* PW4 (28h), MW4 (18h), MUX4(38h) */
+ parm = AC_PWRST_D3;
+ set_pin_power_state(codec, 0x28, &parm);
+ update_power_state(codec, 0x18, parm);
+ update_power_state(codec, 0x38, parm);
+ } else {
+ /* PW4 (26h), MW4 (1ch), MUX4(37h) */
+ parm = AC_PWRST_D3;
+ set_pin_power_state(codec, 0x26, &parm);
+ update_power_state(codec, 0x1c, parm);
+ update_power_state(codec, 0x37, parm);
+ }
- if (!cfg->line_out_pins[0])
- return -1;
+ if (spec->codec_type == VT1802) {
+ /* PW1 (25h), MW1 (15h), MUX1(35h), AOW1 (9h) */
+ parm = AC_PWRST_D3;
+ set_pin_power_state(codec, 0x25, &parm);
+ update_power_state(codec, 0x15, parm);
+ update_power_state(codec, 0x35, parm);
+ } else {
+ /* PW1 (25h), MW1 (19h), MUX1(35h), AOW1 (9h) */
+ parm = AC_PWRST_D3;
+ set_pin_power_state(codec, 0x25, &parm);
+ update_power_state(codec, 0x19, parm);
+ update_power_state(codec, 0x35, parm);
+ }
+ if (spec->gen.indep_hp_enabled)
+ update_power_state(codec, 0x9, AC_PWRST_D0);
- /* Line-Out: PortE */
- err = via_add_control(spec, VIA_CTL_WIDGET_VOL,
- "Master Front Playback Volume",
- HDA_COMPOSE_AMP_VAL(0x8, 3, 0, HDA_OUTPUT));
- if (err < 0)
- return err;
- err = via_add_control(spec, VIA_CTL_WIDGET_BIND_PIN_MUTE,
- "Master Front Playback Switch",
- HDA_COMPOSE_AMP_VAL(0x26, 3, 0, HDA_OUTPUT));
- if (err < 0)
- return err;
+ /* Class-D */
+ /* PW0 (24h), MW0(18h/14h), MUX0(34h) */
+ present = snd_hda_jack_detect(codec, 0x25);
- return 0;
+ parm = AC_PWRST_D3;
+ set_pin_power_state(codec, 0x24, &parm);
+ parm = present ? AC_PWRST_D3 : AC_PWRST_D0;
+ if (spec->codec_type == VT1802)
+ update_power_state(codec, 0x14, parm);
+ else
+ update_power_state(codec, 0x18, parm);
+ update_power_state(codec, 0x34, parm);
+
+ /* Mono Out */
+ present = snd_hda_jack_detect(codec, 0x26);
+
+ parm = present ? AC_PWRST_D3 : AC_PWRST_D0;
+ if (spec->codec_type == VT1802) {
+ /* PW15 (33h), MW8(1ch), MUX8(3ch) */
+ update_power_state(codec, 0x33, parm);
+ update_power_state(codec, 0x1c, parm);
+ update_power_state(codec, 0x3c, parm);
+ } else {
+ /* PW15 (31h), MW8(17h), MUX8(3bh) */
+ update_power_state(codec, 0x31, parm);
+ update_power_state(codec, 0x17, parm);
+ update_power_state(codec, 0x3b, parm);
+ }
+ /* MW9 (21h) */
+ if (imux_is_smixer || !is_aa_path_mute(codec))
+ update_power_state(codec, 0x21, AC_PWRST_D0);
+ else
+ update_power_state(codec, 0x21, AC_PWRST_D3);
}
-static int vt2002P_auto_create_hp_ctls(struct via_spec *spec, hda_nid_t pin)
-{
- int err;
-
- if (!pin)
- return 0;
-
- spec->multiout.hp_nid = 0x9;
- spec->hp_independent_mode_index = 1;
-
- err = via_add_control(spec, VIA_CTL_WIDGET_VOL,
- "Headphone Playback Volume",
- HDA_COMPOSE_AMP_VAL(
- spec->multiout.hp_nid, 3, 0, HDA_OUTPUT));
- if (err < 0)
- return err;
-
- err = via_add_control(spec, VIA_CTL_WIDGET_MUTE,
- "Headphone Playback Switch",
- HDA_COMPOSE_AMP_VAL(0x25, 3, 0, HDA_OUTPUT));
- if (err < 0)
- return err;
-
- create_hp_imux(spec);
- return 0;
-}
+/*
+ * pin fix-up
+ */
+enum {
+ VIA_FIXUP_INTMIC_BOOST,
+ VIA_FIXUP_ASUS_G75,
+};
-/* create playback/capture controls for input pins */
-static int vt2002P_auto_create_analog_input_ctls(struct hda_codec *codec,
- const struct auto_pin_cfg *cfg)
+static void via_fixup_intmic_boost(struct hda_codec *codec,
+ const struct hda_fixup *fix, int action)
{
- struct via_spec *spec = codec->spec;
- struct hda_input_mux *imux = &spec->private_imux[0];
- static hda_nid_t pin_idxs[] = { 0x2b, 0x2a, 0x29, 0xff };
- int err;
-
- err = vt_auto_create_analog_input_ctls(codec, cfg, 0x21, pin_idxs,
- ARRAY_SIZE(pin_idxs));
- if (err < 0)
- return err;
- /* build volume/mute control of loopback */
- err = via_new_analog_input(spec, "Stereo Mixer", 0, 3, 0x21);
- if (err < 0)
- return err;
-
- /* for digital mic select */
- snd_hda_add_imux_item(imux, "Digital Mic", 4, NULL);
-
- return 0;
+ if (action == HDA_FIXUP_ACT_PRE_PROBE)
+ override_mic_boost(codec, 0x30, 0, 2, 40);
}
-static int vt2002P_parse_auto_config(struct hda_codec *codec)
-{
- struct via_spec *spec = codec->spec;
- int err;
-
-
- err = snd_hda_parse_pin_def_config(codec, &spec->autocfg, NULL);
- if (err < 0)
- return err;
-
- err = vt2002P_auto_fill_dac_nids(spec, &spec->autocfg);
- if (err < 0)
- return err;
-
- if (!spec->autocfg.line_outs && !spec->autocfg.hp_pins[0])
- return 0; /* can't find valid BIOS pin config */
-
- err = vt2002P_auto_create_multi_out_ctls(spec, &spec->autocfg);
- if (err < 0)
- return err;
- err = vt2002P_auto_create_hp_ctls(spec, spec->autocfg.hp_pins[0]);
- if (err < 0)
- return err;
- err = vt2002P_auto_create_analog_input_ctls(codec, &spec->autocfg);
- if (err < 0)
- return err;
-
- spec->multiout.max_channels = spec->multiout.num_dacs * 2;
-
- fill_dig_outs(codec);
-
- if (spec->kctls.list)
- spec->mixers[spec->num_mixers++] = spec->kctls.list;
+static const struct hda_fixup via_fixups[] = {
+ [VIA_FIXUP_INTMIC_BOOST] = {
+ .type = HDA_FIXUP_FUNC,
+ .v.func = via_fixup_intmic_boost,
+ },
+ [VIA_FIXUP_ASUS_G75] = {
+ .type = HDA_FIXUP_PINS,
+ .v.pins = (const struct hda_pintbl[]) {
+ /* set 0x24 and 0x33 as speakers */
+ { 0x24, 0x991301f0 },
+ { 0x33, 0x991301f1 }, /* subwoofer */
+ { }
+ }
+ },
+};
- spec->input_mux = &spec->private_imux[0];
+static const struct snd_pci_quirk vt2002p_fixups[] = {
+ SND_PCI_QUIRK(0x1043, 0x1487, "Asus G75", VIA_FIXUP_ASUS_G75),
+ SND_PCI_QUIRK(0x1043, 0x8532, "Asus X202E", VIA_FIXUP_INTMIC_BOOST),
+ {}
+};
- if (spec->hp_mux)
- via_hp_build(codec);
+/* NIDs 0x24 and 0x33 on VT1802 have connections to non-existing NID 0x3e
+ * Replace this with mixer NID 0x1c
+ */
+static void fix_vt1802_connections(struct hda_codec *codec)
+{
+ static hda_nid_t conn_24[] = { 0x14, 0x1c };
+ static hda_nid_t conn_33[] = { 0x1c };
- return 1;
+ snd_hda_override_conn_list(codec, 0x24, ARRAY_SIZE(conn_24), conn_24);
+ snd_hda_override_conn_list(codec, 0x33, ARRAY_SIZE(conn_33), conn_33);
}
-#ifdef CONFIG_SND_HDA_POWER_SAVE
-static struct hda_amp_list vt2002P_loopbacks[] = {
- { 0x21, HDA_INPUT, 0 },
- { 0x21, HDA_INPUT, 1 },
- { 0x21, HDA_INPUT, 2 },
- { } /* end */
-};
-#endif
-
-
/* patch for vt2002P */
static int patch_vt2002P(struct hda_codec *codec)
{
@@ -5621,319 +1539,241 @@ static int patch_vt2002P(struct hda_codec *codec)
if (spec == NULL)
return -ENOMEM;
+ spec->gen.mixer_nid = 0x21;
+ override_mic_boost(codec, 0x2b, 0, 3, 40);
+ override_mic_boost(codec, 0x29, 0, 3, 40);
+ if (spec->codec_type == VT1802)
+ fix_vt1802_connections(codec);
+ add_secret_dac_path(codec);
+
+ snd_hda_pick_fixup(codec, NULL, vt2002p_fixups, via_fixups);
+ snd_hda_apply_fixup(codec, HDA_FIXUP_ACT_PRE_PROBE);
+
/* automatic parse from the BIOS config */
- err = vt2002P_parse_auto_config(codec);
+ err = via_parse_auto_config(codec);
if (err < 0) {
via_free(codec);
return err;
- } else if (!err) {
- printk(KERN_INFO "hda_codec: Cannot set up configuration "
- "from BIOS. Using genenic mode...\n");
}
- spec->init_verbs[spec->num_iverbs++] = vt2002P_volume_init_verbs;
- spec->init_verbs[spec->num_iverbs++] = vt2002P_uniwill_init_verbs;
-
- spec->stream_name_analog = "VT2002P Analog";
- spec->stream_analog_playback = &vt2002P_pcm_analog_playback;
- spec->stream_analog_capture = &vt2002P_pcm_analog_capture;
-
- spec->stream_name_digital = "VT2002P Digital";
- spec->stream_digital_playback = &vt2002P_pcm_digital_playback;
-
- if (!spec->adc_nids && spec->input_mux) {
- spec->adc_nids = vt2002P_adc_nids;
- spec->num_adc_nids = ARRAY_SIZE(vt2002P_adc_nids);
- get_mux_nids(codec);
- override_mic_boost(codec, 0x2b, 0, 3, 40);
- override_mic_boost(codec, 0x29, 0, 3, 40);
- spec->mixers[spec->num_mixers] = vt2002P_capture_mixer;
- spec->num_mixers++;
- }
+ if (spec->codec_type == VT1802)
+ spec->init_verbs[spec->num_iverbs++] = vt1802_init_verbs;
+ else
+ spec->init_verbs[spec->num_iverbs++] = vt2002P_init_verbs;
codec->patch_ops = via_patch_ops;
- codec->patch_ops.init = via_auto_init;
- codec->patch_ops.unsol_event = via_unsol_event;
-
-#ifdef CONFIG_SND_HDA_POWER_SAVE
- spec->loopback.amplist = vt2002P_loopbacks;
-#endif
-
+ spec->set_widgets_power_state = set_widgets_power_state_vt2002P;
return 0;
}
/* for vt1812 */
-/* capture mixer elements */
-static struct snd_kcontrol_new vt1812_capture_mixer[] = {
- HDA_CODEC_VOLUME("Capture Volume", 0x10, 0x0, HDA_INPUT),
- HDA_CODEC_MUTE("Capture Switch", 0x10, 0x0, HDA_INPUT),
- HDA_CODEC_VOLUME_IDX("Capture Volume", 1, 0x11, 0x0, HDA_INPUT),
- HDA_CODEC_MUTE_IDX("Capture Switch", 1, 0x11, 0x0, HDA_INPUT),
- HDA_CODEC_MUTE("Mic Boost Capture Volume", 0x2b, 0x0, HDA_INPUT),
- HDA_CODEC_MUTE("Front Mic Boost Capture Volume", 0x29, 0x0,
- HDA_INPUT),
- {
- .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
- /* The multiple "Capture Source" controls confuse alsamixer
- * So call somewhat different..
- */
- .name = "Input Source",
- .count = 2,
- .info = via_mux_enum_info,
- .get = via_mux_enum_get,
- .put = via_mux_enum_put,
- },
- { } /* end */
-};
-
-static struct hda_verb vt1812_volume_init_verbs[] = {
- /*
- * Unmute ADC0-1 and set the default input to mic-in
- */
- {0x8, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
- {0x9, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
-
-
- /* Mute input amps (CD, Line In, Mic 1 & Mic 2) of the analog-loopback
- * mixer widget
- */
- /* Amp Indices: CD = 1, Mic1 = 2, Line = 3, Mic2 = 4 */
- {0x21, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
- {0x21, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
- {0x21, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(2)},
- {0x21, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(3)},
- {0x21, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(4)},
-
- /* MUX Indices: Mic = 0 */
- {0x1e, AC_VERB_SET_CONNECT_SEL, 0},
- {0x1f, AC_VERB_SET_CONNECT_SEL, 0},
-
- /* PW9 Output enable */
- {0x2d, AC_VERB_SET_PIN_WIDGET_CONTROL, AC_PINCTL_OUT_EN},
-
+static const struct hda_verb vt1812_init_verbs[] = {
/* Enable Boost Volume backdoor */
{0x1, 0xfb9, 0x24},
-
- /* MW0/1/4/13/15: un-mute index 0 (MUXx), un-mute index 1 (MW9) */
- {0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
- {0x15, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
- {0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
- {0x1c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
- {0x1d, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
- {0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
- {0x15, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
- {0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
- {0x1c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
- {0x1d, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
-
- /* set MUX0/1/4/13/15 = 0 (AOW0) */
- {0x34, AC_VERB_SET_CONNECT_SEL, 0},
- {0x35, AC_VERB_SET_CONNECT_SEL, 0},
- {0x38, AC_VERB_SET_CONNECT_SEL, 0},
- {0x3c, AC_VERB_SET_CONNECT_SEL, 0},
- {0x3d, AC_VERB_SET_CONNECT_SEL, 0},
-
/* Enable AOW0 to MW9 */
{0x1, 0xfb8, 0xa8},
{ }
};
-
-static struct hda_verb vt1812_uniwill_init_verbs[] = {
- {0x33, AC_VERB_SET_UNSOLICITED_ENABLE,
- AC_USRSP_EN | VIA_JACK_EVENT | VIA_BIND_HP_EVENT},
- {0x25, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT },
- {0x28, AC_VERB_SET_UNSOLICITED_ENABLE,
- AC_USRSP_EN | VIA_JACK_EVENT | VIA_BIND_HP_EVENT},
- {0x29, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT},
- {0x2a, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT},
- {0x2b, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT},
- { }
-};
-
-static struct hda_pcm_stream vt1812_pcm_analog_playback = {
- .substreams = 2,
- .channels_min = 2,
- .channels_max = 2,
- .nid = 0x8, /* NID to query formats and rates */
- .ops = {
- .open = via_playback_pcm_open,
- .prepare = via_playback_multi_pcm_prepare,
- .cleanup = via_playback_multi_pcm_cleanup,
- .close = via_pcm_open_close,
- },
-};
-
-static struct hda_pcm_stream vt1812_pcm_analog_capture = {
- .substreams = 2,
- .channels_min = 2,
- .channels_max = 2,
- .nid = 0x10, /* NID to query formats and rates */
- .ops = {
- .open = via_pcm_open_close,
- .prepare = via_capture_pcm_prepare,
- .cleanup = via_capture_pcm_cleanup,
- .close = via_pcm_open_close,
- },
-};
-
-static struct hda_pcm_stream vt1812_pcm_digital_playback = {
- .substreams = 1,
- .channels_min = 2,
- .channels_max = 2,
- /* NID is set in via_build_pcms */
- .ops = {
- .open = via_dig_playback_pcm_open,
- .close = via_dig_playback_pcm_close,
- .prepare = via_dig_playback_pcm_prepare,
- .cleanup = via_dig_playback_pcm_cleanup
- },
-};
-/* fill in the dac_nids table from the parsed pin configuration */
-static int vt1812_auto_fill_dac_nids(struct via_spec *spec,
- const struct auto_pin_cfg *cfg)
+static void set_widgets_power_state_vt1812(struct hda_codec *codec)
{
- spec->multiout.num_dacs = 1;
- spec->multiout.dac_nids = spec->private_dac_nids;
- if (cfg->line_out_pins[0])
- spec->multiout.dac_nids[0] = 0x8;
- return 0;
-}
+ struct via_spec *spec = codec->spec;
+ unsigned int parm;
+ unsigned int present;
+ /* inputs */
+ /* PW 5/6/7 (29h/2ah/2bh) */
+ parm = AC_PWRST_D3;
+ set_pin_power_state(codec, 0x29, &parm);
+ set_pin_power_state(codec, 0x2a, &parm);
+ set_pin_power_state(codec, 0x2b, &parm);
+ parm = AC_PWRST_D0;
+ /* MUX10/11 (1eh/1fh), AIW 0/1 (10h/11h) */
+ update_power_state(codec, 0x1e, parm);
+ update_power_state(codec, 0x1f, parm);
+ update_power_state(codec, 0x10, parm);
+ update_power_state(codec, 0x11, parm);
+
+ /* outputs */
+ /* AOW0 (8h)*/
+ update_power_state(codec, 0x8, AC_PWRST_D0);
+
+ /* PW4 (28h), MW4 (18h), MUX4(38h) */
+ parm = AC_PWRST_D3;
+ set_pin_power_state(codec, 0x28, &parm);
+ update_power_state(codec, 0x18, parm);
+ update_power_state(codec, 0x38, parm);
+
+ /* PW1 (25h), MW1 (15h), MUX1(35h), AOW1 (9h) */
+ parm = AC_PWRST_D3;
+ set_pin_power_state(codec, 0x25, &parm);
+ update_power_state(codec, 0x15, parm);
+ update_power_state(codec, 0x35, parm);
+ if (spec->gen.indep_hp_enabled)
+ update_power_state(codec, 0x9, AC_PWRST_D0);
+
+ /* Internal Speaker */
+ /* PW0 (24h), MW0(14h), MUX0(34h) */
+ present = snd_hda_jack_detect(codec, 0x25);
+
+ parm = AC_PWRST_D3;
+ set_pin_power_state(codec, 0x24, &parm);
+ if (present) {
+ update_power_state(codec, 0x14, AC_PWRST_D3);
+ update_power_state(codec, 0x34, AC_PWRST_D3);
+ } else {
+ update_power_state(codec, 0x14, AC_PWRST_D0);
+ update_power_state(codec, 0x34, AC_PWRST_D0);
+ }
-/* add playback controls from the parsed DAC table */
-static int vt1812_auto_create_multi_out_ctls(struct via_spec *spec,
- const struct auto_pin_cfg *cfg)
-{
- int err;
+ /* Mono Out */
+ /* PW13 (31h), MW13(1ch), MUX13(3ch), MW14(3eh) */
+ present = snd_hda_jack_detect(codec, 0x28);
- if (!cfg->line_out_pins[0])
- return -1;
+ parm = AC_PWRST_D3;
+ set_pin_power_state(codec, 0x31, &parm);
+ if (present) {
+ update_power_state(codec, 0x1c, AC_PWRST_D3);
+ update_power_state(codec, 0x3c, AC_PWRST_D3);
+ update_power_state(codec, 0x3e, AC_PWRST_D3);
+ } else {
+ update_power_state(codec, 0x1c, AC_PWRST_D0);
+ update_power_state(codec, 0x3c, AC_PWRST_D0);
+ update_power_state(codec, 0x3e, AC_PWRST_D0);
+ }
- /* Line-Out: PortE */
- err = via_add_control(spec, VIA_CTL_WIDGET_VOL,
- "Front Playback Volume",
- HDA_COMPOSE_AMP_VAL(0x8, 3, 0, HDA_OUTPUT));
- if (err < 0)
- return err;
- err = via_add_control(spec, VIA_CTL_WIDGET_BIND_PIN_MUTE,
- "Front Playback Switch",
- HDA_COMPOSE_AMP_VAL(0x28, 3, 0, HDA_OUTPUT));
- if (err < 0)
- return err;
+ /* PW15 (33h), MW15 (1dh), MUX15(3dh) */
+ parm = AC_PWRST_D3;
+ set_pin_power_state(codec, 0x33, &parm);
+ update_power_state(codec, 0x1d, parm);
+ update_power_state(codec, 0x3d, parm);
- return 0;
}
-static int vt1812_auto_create_hp_ctls(struct via_spec *spec, hda_nid_t pin)
+/* patch for vt1812 */
+static int patch_vt1812(struct hda_codec *codec)
{
+ struct via_spec *spec;
int err;
- if (!pin)
- return 0;
-
- spec->multiout.hp_nid = 0x9;
- spec->hp_independent_mode_index = 1;
+ /* create a codec specific record */
+ spec = via_new_spec(codec);
+ if (spec == NULL)
+ return -ENOMEM;
+ spec->gen.mixer_nid = 0x21;
+ override_mic_boost(codec, 0x2b, 0, 3, 40);
+ override_mic_boost(codec, 0x29, 0, 3, 40);
+ add_secret_dac_path(codec);
- err = via_add_control(spec, VIA_CTL_WIDGET_VOL,
- "Headphone Playback Volume",
- HDA_COMPOSE_AMP_VAL(
- spec->multiout.hp_nid, 3, 0, HDA_OUTPUT));
- if (err < 0)
+ /* automatic parse from the BIOS config */
+ err = via_parse_auto_config(codec);
+ if (err < 0) {
+ via_free(codec);
return err;
+ }
- err = via_add_control(spec, VIA_CTL_WIDGET_MUTE,
- "Headphone Playback Switch",
- HDA_COMPOSE_AMP_VAL(pin, 3, 0, HDA_OUTPUT));
- if (err < 0)
- return err;
+ spec->init_verbs[spec->num_iverbs++] = vt1812_init_verbs;
- create_hp_imux(spec);
+ codec->patch_ops = via_patch_ops;
+
+ spec->set_widgets_power_state = set_widgets_power_state_vt1812;
return 0;
}
-/* create playback/capture controls for input pins */
-static int vt1812_auto_create_analog_input_ctls(struct hda_codec *codec,
- const struct auto_pin_cfg *cfg)
-{
- struct via_spec *spec = codec->spec;
- struct hda_input_mux *imux = &spec->private_imux[0];
- static hda_nid_t pin_idxs[] = { 0x2b, 0x2a, 0x29, 0, 0, 0xff };
- int err;
-
- err = vt_auto_create_analog_input_ctls(codec, cfg, 0x21, pin_idxs,
- ARRAY_SIZE(pin_idxs));
- if (err < 0)
- return err;
-
- /* build volume/mute control of loopback */
- err = via_new_analog_input(spec, "Stereo Mixer", 0, 5, 0x21);
- if (err < 0)
- return err;
+/* patch for vt3476 */
- /* for digital mic select */
- snd_hda_add_imux_item(imux, "Digital Mic", 6, NULL);
-
- return 0;
-}
+static const struct hda_verb vt3476_init_verbs[] = {
+ /* Enable DMic 8/16/32K */
+ {0x1, 0xF7B, 0x30},
+ /* Enable Boost Volume backdoor */
+ {0x1, 0xFB9, 0x20},
+ /* Enable AOW-MW9 path */
+ {0x1, 0xFB8, 0x10},
+ { }
+};
-static int vt1812_parse_auto_config(struct hda_codec *codec)
+static void set_widgets_power_state_vt3476(struct hda_codec *codec)
{
struct via_spec *spec = codec->spec;
- int err;
-
-
- err = snd_hda_parse_pin_def_config(codec, &spec->autocfg, NULL);
- if (err < 0)
- return err;
- fill_dig_outs(codec);
- err = vt1812_auto_fill_dac_nids(spec, &spec->autocfg);
- if (err < 0)
- return err;
-
- if (!spec->autocfg.line_outs && !spec->autocfg.hp_outs)
- return 0; /* can't find valid BIOS pin config */
-
- err = vt1812_auto_create_multi_out_ctls(spec, &spec->autocfg);
- if (err < 0)
- return err;
- err = vt1812_auto_create_hp_ctls(spec, spec->autocfg.hp_pins[0]);
- if (err < 0)
- return err;
- err = vt1812_auto_create_analog_input_ctls(codec, &spec->autocfg);
- if (err < 0)
- return err;
-
- spec->multiout.max_channels = spec->multiout.num_dacs * 2;
-
- fill_dig_outs(codec);
+ int imux_is_smixer;
+ unsigned int parm, parm2;
+ /* MUX10 (1eh) = stereo mixer */
+ imux_is_smixer =
+ snd_hda_codec_read(codec, 0x1e, 0, AC_VERB_GET_CONNECT_SEL, 0x00) == 4;
+ /* inputs */
+ /* PW 5/6/7 (29h/2ah/2bh) */
+ parm = AC_PWRST_D3;
+ set_pin_power_state(codec, 0x29, &parm);
+ set_pin_power_state(codec, 0x2a, &parm);
+ set_pin_power_state(codec, 0x2b, &parm);
+ if (imux_is_smixer)
+ parm = AC_PWRST_D0;
+ /* MUX10/11 (1eh/1fh), AIW 0/1 (10h/11h) */
+ update_power_state(codec, 0x1e, parm);
+ update_power_state(codec, 0x1f, parm);
+ update_power_state(codec, 0x10, parm);
+ update_power_state(codec, 0x11, parm);
+
+ /* outputs */
+ /* PW3 (27h), MW3(37h), AOW3 (bh) */
+ if (spec->codec_type == VT1705CF) {
+ parm = AC_PWRST_D3;
+ update_power_state(codec, 0x27, parm);
+ update_power_state(codec, 0x37, parm);
+ } else {
+ parm = AC_PWRST_D3;
+ set_pin_power_state(codec, 0x27, &parm);
+ update_power_state(codec, 0x37, parm);
+ }
- if (spec->kctls.list)
- spec->mixers[spec->num_mixers++] = spec->kctls.list;
+ /* PW2 (26h), MW2(36h), AOW2 (ah) */
+ parm = AC_PWRST_D3;
+ set_pin_power_state(codec, 0x26, &parm);
+ update_power_state(codec, 0x36, parm);
+ if (smart51_enabled(codec)) {
+ /* PW7(2bh), MW7(3bh), MUX7(1Bh) */
+ set_pin_power_state(codec, 0x2b, &parm);
+ update_power_state(codec, 0x3b, parm);
+ update_power_state(codec, 0x1b, parm);
+ }
+ update_conv_power_state(codec, 0xa, parm, 2);
- spec->input_mux = &spec->private_imux[0];
+ /* PW1 (25h), MW1(35h), AOW1 (9h) */
+ parm = AC_PWRST_D3;
+ set_pin_power_state(codec, 0x25, &parm);
+ update_power_state(codec, 0x35, parm);
+ if (smart51_enabled(codec)) {
+ /* PW6(2ah), MW6(3ah), MUX6(1ah) */
+ set_pin_power_state(codec, 0x2a, &parm);
+ update_power_state(codec, 0x3a, parm);
+ update_power_state(codec, 0x1a, parm);
+ }
+ update_conv_power_state(codec, 0x9, parm, 1);
- if (spec->hp_mux)
- via_hp_build(codec);
+ /* PW4 (28h), MW4 (38h), MUX4(18h), AOW3(bh)/AOW0(8h) */
+ parm = AC_PWRST_D3;
+ set_pin_power_state(codec, 0x28, &parm);
+ update_power_state(codec, 0x38, parm);
+ update_power_state(codec, 0x18, parm);
+ if (spec->gen.indep_hp_enabled)
+ update_conv_power_state(codec, 0xb, parm, 3);
+ parm2 = parm; /* for pin 0x0b */
- return 1;
+ /* PW0 (24h), MW0(34h), MW9(3fh), AOW0 (8h) */
+ parm = AC_PWRST_D3;
+ set_pin_power_state(codec, 0x24, &parm);
+ update_power_state(codec, 0x34, parm);
+ if (!spec->gen.indep_hp_enabled && parm2 != AC_PWRST_D3)
+ parm = parm2;
+ update_conv_power_state(codec, 0x8, parm, 0);
+ /* MW9 (21h), Mw2 (1ah), AOW0 (8h) */
+ update_power_state(codec, 0x3f, imux_is_smixer ? AC_PWRST_D0 : parm);
}
-#ifdef CONFIG_SND_HDA_POWER_SAVE
-static struct hda_amp_list vt1812_loopbacks[] = {
- { 0x21, HDA_INPUT, 0 },
- { 0x21, HDA_INPUT, 1 },
- { 0x21, HDA_INPUT, 2 },
- { } /* end */
-};
-#endif
-
-
-/* patch for vt1812 */
-static int patch_vt1812(struct hda_codec *codec)
+static int patch_vt3476(struct hda_codec *codec)
{
struct via_spec *spec;
int err;
@@ -5943,46 +1783,21 @@ static int patch_vt1812(struct hda_codec *codec)
if (spec == NULL)
return -ENOMEM;
+ spec->gen.mixer_nid = 0x3f;
+ add_secret_dac_path(codec);
+
/* automatic parse from the BIOS config */
- err = vt1812_parse_auto_config(codec);
+ err = via_parse_auto_config(codec);
if (err < 0) {
via_free(codec);
return err;
- } else if (!err) {
- printk(KERN_INFO "hda_codec: Cannot set up configuration "
- "from BIOS. Using genenic mode...\n");
}
-
- spec->init_verbs[spec->num_iverbs++] = vt1812_volume_init_verbs;
- spec->init_verbs[spec->num_iverbs++] = vt1812_uniwill_init_verbs;
-
- spec->stream_name_analog = "VT1812 Analog";
- spec->stream_analog_playback = &vt1812_pcm_analog_playback;
- spec->stream_analog_capture = &vt1812_pcm_analog_capture;
-
- spec->stream_name_digital = "VT1812 Digital";
- spec->stream_digital_playback = &vt1812_pcm_digital_playback;
-
-
- if (!spec->adc_nids && spec->input_mux) {
- spec->adc_nids = vt1812_adc_nids;
- spec->num_adc_nids = ARRAY_SIZE(vt1812_adc_nids);
- get_mux_nids(codec);
- override_mic_boost(codec, 0x2b, 0, 3, 40);
- override_mic_boost(codec, 0x29, 0, 3, 40);
- spec->mixers[spec->num_mixers] = vt1812_capture_mixer;
- spec->num_mixers++;
- }
+ spec->init_verbs[spec->num_iverbs++] = vt3476_init_verbs;
codec->patch_ops = via_patch_ops;
- codec->patch_ops.init = via_auto_init;
- codec->patch_ops.unsol_event = via_unsol_event;
-
-#ifdef CONFIG_SND_HDA_POWER_SAVE
- spec->loopback.amplist = vt1812_loopbacks;
-#endif
+ spec->set_widgets_power_state = set_widgets_power_state_vt3476;
return 0;
}
@@ -5990,43 +1805,43 @@ static int patch_vt1812(struct hda_codec *codec)
/*
* patch entries
*/
-static struct hda_codec_preset snd_hda_preset_via[] = {
+static const struct hda_codec_preset snd_hda_preset_via[] = {
{ .id = 0x11061708, .name = "VT1708", .patch = patch_vt1708},
{ .id = 0x11061709, .name = "VT1708", .patch = patch_vt1708},
{ .id = 0x1106170a, .name = "VT1708", .patch = patch_vt1708},
{ .id = 0x1106170b, .name = "VT1708", .patch = patch_vt1708},
{ .id = 0x1106e710, .name = "VT1709 10-Ch",
- .patch = patch_vt1709_10ch},
+ .patch = patch_vt1709},
{ .id = 0x1106e711, .name = "VT1709 10-Ch",
- .patch = patch_vt1709_10ch},
+ .patch = patch_vt1709},
{ .id = 0x1106e712, .name = "VT1709 10-Ch",
- .patch = patch_vt1709_10ch},
+ .patch = patch_vt1709},
{ .id = 0x1106e713, .name = "VT1709 10-Ch",
- .patch = patch_vt1709_10ch},
+ .patch = patch_vt1709},
{ .id = 0x1106e714, .name = "VT1709 6-Ch",
- .patch = patch_vt1709_6ch},
+ .patch = patch_vt1709},
{ .id = 0x1106e715, .name = "VT1709 6-Ch",
- .patch = patch_vt1709_6ch},
+ .patch = patch_vt1709},
{ .id = 0x1106e716, .name = "VT1709 6-Ch",
- .patch = patch_vt1709_6ch},
+ .patch = patch_vt1709},
{ .id = 0x1106e717, .name = "VT1709 6-Ch",
- .patch = patch_vt1709_6ch},
+ .patch = patch_vt1709},
{ .id = 0x1106e720, .name = "VT1708B 8-Ch",
- .patch = patch_vt1708B_8ch},
+ .patch = patch_vt1708B},
{ .id = 0x1106e721, .name = "VT1708B 8-Ch",
- .patch = patch_vt1708B_8ch},
+ .patch = patch_vt1708B},
{ .id = 0x1106e722, .name = "VT1708B 8-Ch",
- .patch = patch_vt1708B_8ch},
+ .patch = patch_vt1708B},
{ .id = 0x1106e723, .name = "VT1708B 8-Ch",
- .patch = patch_vt1708B_8ch},
+ .patch = patch_vt1708B},
{ .id = 0x1106e724, .name = "VT1708B 4-Ch",
- .patch = patch_vt1708B_4ch},
+ .patch = patch_vt1708B},
{ .id = 0x1106e725, .name = "VT1708B 4-Ch",
- .patch = patch_vt1708B_4ch},
+ .patch = patch_vt1708B},
{ .id = 0x1106e726, .name = "VT1708B 4-Ch",
- .patch = patch_vt1708B_4ch},
+ .patch = patch_vt1708B},
{ .id = 0x1106e727, .name = "VT1708B 4-Ch",
- .patch = patch_vt1708B_4ch},
+ .patch = patch_vt1708B},
{ .id = 0x11060397, .name = "VT1708S",
.patch = patch_vt1708S},
{ .id = 0x11061397, .name = "VT1708S",
@@ -6035,7 +1850,7 @@ static struct hda_codec_preset snd_hda_preset_via[] = {
.patch = patch_vt1708S},
{ .id = 0x11063397, .name = "VT1708S",
.patch = patch_vt1708S},
- { .id = 0x11064397, .name = "VT1708S",
+ { .id = 0x11064397, .name = "VT1705",
.patch = patch_vt1708S},
{ .id = 0x11065397, .name = "VT1708S",
.patch = patch_vt1708S},
@@ -6076,6 +1891,16 @@ static struct hda_codec_preset snd_hda_preset_via[] = {
{ .id = 0x11060448, .name = "VT1812", .patch = patch_vt1812},
{ .id = 0x11060440, .name = "VT1818S",
.patch = patch_vt1708S},
+ { .id = 0x11060446, .name = "VT1802",
+ .patch = patch_vt2002P},
+ { .id = 0x11068446, .name = "VT1802",
+ .patch = patch_vt2002P},
+ { .id = 0x11064760, .name = "VT1705CF",
+ .patch = patch_vt3476},
+ { .id = 0x11064761, .name = "VT1708SCE",
+ .patch = patch_vt3476},
+ { .id = 0x11064762, .name = "VT1808",
+ .patch = patch_vt3476},
{} /* terminator */
};
diff --git a/sound/pci/hda/thinkpad_helper.c b/sound/pci/hda/thinkpad_helper.c
new file mode 100644
index 00000000000..6ba0b5517c4
--- /dev/null
+++ b/sound/pci/hda/thinkpad_helper.c
@@ -0,0 +1,102 @@
+/* Helper functions for Thinkpad LED control;
+ * to be included from codec driver
+ */
+
+#if IS_ENABLED(CONFIG_THINKPAD_ACPI)
+
+#include <linux/acpi.h>
+#include <linux/thinkpad_acpi.h>
+
+static int (*led_set_func)(int, bool);
+static void (*old_vmaster_hook)(void *, int);
+
+static acpi_status acpi_check_cb(acpi_handle handle, u32 lvl, void *context,
+ void **rv)
+{
+ bool *found = context;
+ *found = true;
+ return AE_OK;
+}
+
+static bool is_thinkpad(struct hda_codec *codec)
+{
+ bool found = false;
+ if (codec->subsystem_id >> 16 != 0x17aa)
+ return false;
+ if (ACPI_SUCCESS(acpi_get_devices("LEN0068", acpi_check_cb, &found, NULL)) && found)
+ return true;
+ found = false;
+ return ACPI_SUCCESS(acpi_get_devices("IBM0068", acpi_check_cb, &found, NULL)) && found;
+}
+
+static void update_tpacpi_mute_led(void *private_data, int enabled)
+{
+ if (old_vmaster_hook)
+ old_vmaster_hook(private_data, enabled);
+
+ if (led_set_func)
+ led_set_func(TPACPI_LED_MUTE, !enabled);
+}
+
+static void update_tpacpi_micmute_led(struct hda_codec *codec,
+ struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ if (!ucontrol || !led_set_func)
+ return;
+ if (strcmp("Capture Switch", ucontrol->id.name) == 0 && ucontrol->id.index == 0) {
+ /* TODO: How do I verify if it's a mono or stereo here? */
+ bool val = ucontrol->value.integer.value[0] || ucontrol->value.integer.value[1];
+ led_set_func(TPACPI_LED_MICMUTE, !val);
+ }
+}
+
+static void hda_fixup_thinkpad_acpi(struct hda_codec *codec,
+ const struct hda_fixup *fix, int action)
+{
+ struct hda_gen_spec *spec = codec->spec;
+ bool removefunc = false;
+
+ if (action == HDA_FIXUP_ACT_PROBE) {
+ if (!is_thinkpad(codec))
+ return;
+ if (!led_set_func)
+ led_set_func = symbol_request(tpacpi_led_set);
+ if (!led_set_func) {
+ codec_warn(codec,
+ "Failed to find thinkpad-acpi symbol tpacpi_led_set\n");
+ return;
+ }
+
+ removefunc = true;
+ if (led_set_func(TPACPI_LED_MUTE, false) >= 0) {
+ old_vmaster_hook = spec->vmaster_mute.hook;
+ spec->vmaster_mute.hook = update_tpacpi_mute_led;
+ removefunc = false;
+ }
+ if (led_set_func(TPACPI_LED_MICMUTE, false) >= 0) {
+ if (spec->num_adc_nids > 1)
+ codec_dbg(codec,
+ "Skipping micmute LED control due to several ADCs");
+ else {
+ spec->cap_sync_hook = update_tpacpi_micmute_led;
+ removefunc = false;
+ }
+ }
+ }
+
+ if (led_set_func && (action == HDA_FIXUP_ACT_FREE || removefunc)) {
+ symbol_put(tpacpi_led_set);
+ led_set_func = NULL;
+ old_vmaster_hook = NULL;
+ }
+}
+
+#else /* CONFIG_THINKPAD_ACPI */
+
+static void hda_fixup_thinkpad_acpi(struct hda_codec *codec,
+ const struct hda_fixup *fix, int action)
+{
+}
+
+#endif /* CONFIG_THINKPAD_ACPI */