aboutsummaryrefslogtreecommitdiff
path: root/sound/pci/hda
diff options
context:
space:
mode:
Diffstat (limited to 'sound/pci/hda')
-rw-r--r--sound/pci/hda/Kconfig228
-rw-r--r--sound/pci/hda/Makefile60
-rw-r--r--sound/pci/hda/alc260_quirks.c968
-rw-r--r--sound/pci/hda/alc880_quirks.c1707
-rw-r--r--sound/pci/hda/alc882_quirks.c866
-rw-r--r--sound/pci/hda/alc_quirks.c480
-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.c183
-rw-r--r--sound/pci/hda/hda_beep.h12
-rw-r--r--sound/pci/hda/hda_codec.c3066
-rw-r--r--sound/pci/hda/hda_codec.h760
-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.c277
-rw-r--r--sound/pci/hda/hda_generic.c5764
-rw-r--r--sound/pci/hda/hda_generic.h342
-rw-r--r--sound/pci/hda/hda_hwdep.c701
-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.c3032
-rw-r--r--sound/pci/hda/hda_intel_trace.h62
-rw-r--r--sound/pci/hda/hda_jack.c307
-rw-r--r--sound/pci/hda/hda_jack.h41
-rw-r--r--sound/pci/hda/hda_local.h389
-rw-r--r--sound/pci/hda/hda_priv.h465
-rw-r--r--sound/pci/hda/hda_proc.c158
-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.h26
-rw-r--r--sound/pci/hda/patch_analog.c5175
-rw-r--r--sound/pci/hda/patch_ca0110.c493
-rw-r--r--sound/pci/hda/patch_ca0132.c4416
-rw-r--r--sound/pci/hda/patch_cirrus.c1814
-rw-r--r--sound/pci/hda/patch_cmedia.c181
-rw-r--r--sound/pci/hda/patch_conexant.c2398
-rw-r--r--sound/pci/hda/patch_hdmi.c2228
-rw-r--r--sound/pci/hda/patch_realtek.c7928
-rw-r--r--sound/pci/hda/patch_si3054.c5
-rw-r--r--sound/pci/hda/patch_sigmatel.c8295
-rw-r--r--sound/pci/hda/patch_via.c2929
-rw-r--r--sound/pci/hda/thinkpad_helper.c102
43 files changed, 30748 insertions, 30199 deletions
diff --git a/sound/pci/hda/Kconfig b/sound/pci/hda/Kconfig
index 163b6b5de3e..ebf4c2fb99d 100644
--- a/sound/pci/hda/Kconfig
+++ b/sound/pci/hda/Kconfig
@@ -1,8 +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.
@@ -13,7 +20,25 @@ 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"
@@ -37,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
@@ -47,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"
@@ -73,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
@@ -83,179 +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.
-
-config SND_HDA_ENABLE_REALTEK_QUIRKS
- bool "Build static quirks for Realtek codecs"
- depends on SND_HDA_CODEC_REALTEK
- default y
- help
- Say Y here to build the static quirks codes for Realtek codecs.
- If you need the "model" preset that the default BIOS auto-parser
- can't handle, turn this option on.
-
- If your device works with model=auto option, basically you don't
- need the quirk code. By turning this off, you can reduce the
- module size quite a lot.
+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
- bool "Build Creative CA0132 codec support"
- depends on SND_HDA_INTEL
- default y
+ tristate "Build Creative CA0132 codec support"
help
- Say Y here to include Creative CA0132 codec support in
+ Say Y or M here to include Creative CA0132 codec 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-ca0132.
- 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_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"
- depends on PM
- 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 ace157cc3d1..194f30935e7 100644
--- a/sound/pci/hda/Makefile
+++ b/sound/pci/hda/Makefile
@@ -1,14 +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 hda_jack.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
@@ -22,44 +27,25 @@ 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_CA0132
-obj-$(CONFIG_SND_HDA_INTEL) += snd-hda-codec-ca0132.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/alc260_quirks.c b/sound/pci/hda/alc260_quirks.c
deleted file mode 100644
index 3b5170b9700..00000000000
--- a/sound/pci/hda/alc260_quirks.c
+++ /dev/null
@@ -1,968 +0,0 @@
-/*
- * ALC260 quirk models
- * included by patch_realtek.c
- */
-
-/* ALC260 models */
-enum {
- ALC260_AUTO,
- ALC260_BASIC,
- ALC260_FUJITSU_S702X,
- ALC260_ACER,
- ALC260_WILL,
- ALC260_REPLACER_672V,
- ALC260_FAVORIT100,
-#ifdef CONFIG_SND_DEBUG
- ALC260_TEST,
-#endif
- ALC260_MODEL_LAST /* last tag */
-};
-
-static const hda_nid_t alc260_dac_nids[1] = {
- /* front */
- 0x02,
-};
-
-static const hda_nid_t alc260_adc_nids[1] = {
- /* ADC0 */
- 0x04,
-};
-
-static const 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 const hda_nid_t alc260_dual_adc_nids[2] = {
- /* ADC0, ADC1 */
- 0x04, 0x05
-};
-
-#define ALC260_DIGOUT_NID 0x03
-#define ALC260_DIGIN_NID 0x06
-
-static const struct hda_input_mux alc260_capture_source = {
- .num_items = 4,
- .items = {
- { "Mic", 0x0 },
- { "Front Mic", 0x1 },
- { "Line", 0x2 },
- { "CD", 0x4 },
- },
-};
-
-/* 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 const struct hda_input_mux alc260_fujitsu_capture_sources[2] = {
- {
- .num_items = 3,
- .items = {
- { "Mic/Line", 0x0 },
- { "CD", 0x4 },
- { "Headphone", 0x2 },
- },
- },
- {
- .num_items = 4,
- .items = {
- { "Mic/Line", 0x0 },
- { "CD", 0x4 },
- { "Headphone", 0x2 },
- { "Mixer", 0x5 },
- },
- },
-
-};
-
-/* Acer TravelMate(/Extensa/Aspire) notebooks have similar configuration to
- * the Fujitsu S702x, but jacks are marked differently.
- */
-static const struct hda_input_mux alc260_acer_capture_sources[2] = {
- {
- .num_items = 4,
- .items = {
- { "Mic", 0x0 },
- { "Line", 0x2 },
- { "CD", 0x4 },
- { "Headphone", 0x5 },
- },
- },
- {
- .num_items = 5,
- .items = {
- { "Mic", 0x0 },
- { "Line", 0x2 },
- { "CD", 0x4 },
- { "Headphone", 0x6 },
- { "Mixer", 0x5 },
- },
- },
-};
-
-/* Maxdata Favorit 100XS */
-static const struct hda_input_mux alc260_favorit100_capture_sources[2] = {
- {
- .num_items = 2,
- .items = {
- { "Line/Mic", 0x0 },
- { "CD", 0x4 },
- },
- },
- {
- .num_items = 3,
- .items = {
- { "Line/Mic", 0x0 },
- { "CD", 0x4 },
- { "Mixer", 0x5 },
- },
- },
-};
-
-/*
- * 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 const struct hda_channel_mode alc260_modes[1] = {
- { 2, NULL },
-};
-
-
-/* Mixer combinations
- *
- * basic: base_output + input + pc_beep + capture
- * fujitsu: fujitsu + capture
- * acer: acer + capture
- */
-
-static const 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 const 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 */
-};
-
-/* Fujitsu S702x series laptops. ALC260 pin usage: Mic/Line jack = 0x12,
- * HP jack = 0x14, CD audio = 0x16, internal speaker = 0x10.
- */
-static const 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 const 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 const 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 const 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 const 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 const 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)},
- { }
-};
-
-/* 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 const 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 const 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 const 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.
- */
- {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 const 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 const 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 | ALC_HP_EVENT},
- {}
-};
-
-/* 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) == ALC_HP_EVENT)
- alc260_replacer_672v_automute(codec);
-}
-
-static const 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 | ALC_HP_EVENT},
- {0x11, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | ALC_HP_EVENT},
- {0x15, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | ALC_HP_EVENT},
- {}
-};
-
-/* Test configuration for debugging, modelled after the ALC880 test
- * configuration.
- */
-#ifdef CONFIG_SND_DEBUG
-static const hda_nid_t alc260_test_dac_nids[1] = {
- 0x02,
-};
-static const 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 const 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 const 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 const 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
-
-/*
- * ALC260 configurations
- */
-static const char * const alc260_models[ALC260_MODEL_LAST] = {
- [ALC260_BASIC] = "basic",
- [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 const 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(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 const 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_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_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_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_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_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,
- },
-#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,
- },
-#endif
-};
-
diff --git a/sound/pci/hda/alc880_quirks.c b/sound/pci/hda/alc880_quirks.c
deleted file mode 100644
index 501501ef36a..00000000000
--- a/sound/pci/hda/alc880_quirks.c
+++ /dev/null
@@ -1,1707 +0,0 @@
-/*
- * ALC880 quirk models
- * included by patch_realtek.c
- */
-
-/* ALC880 board config type */
-enum {
- ALC880_AUTO,
- 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,
-#ifdef CONFIG_SND_DEBUG
- ALC880_TEST,
-#endif
- ALC880_MODEL_LAST /* last tag */
-};
-
-/*
- * 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 const hda_nid_t alc880_dac_nids[4] = {
- /* front, rear, clfe, rear_surr */
- 0x02, 0x05, 0x04, 0x03
-};
-
-static const 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 const hda_nid_t alc880_adc_nids_alt[2] = {
- /* ADC1-2 */
- 0x08, 0x09,
-};
-
-#define ALC880_DIGOUT_NID 0x06
-#define ALC880_DIGIN_NID 0x0a
-#define ALC880_PIN_CD_NID 0x1c
-
-static const 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 const 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 const 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 const struct hda_channel_mode alc880_threestack_modes[2] = {
- { 2, alc880_threestack_ch2_init },
- { 6, alc880_threestack_ch6_init },
-};
-
-static const 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 */
-};
-
-/*
- * 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 const 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 const 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 const 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 const 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 const hda_nid_t alc880_6st_dac_nids[4] = {
- /* front, rear, clfe, rear_surr */
- 0x02, 0x03, 0x04, 0x05
-};
-
-static const 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 const struct hda_channel_mode alc880_sixstack_modes[1] = {
- { 8, NULL },
-};
-
-static const 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 const hda_nid_t alc880_w810_dac_nids[3] = {
- /* front, rear/surround, clfe */
- 0x02, 0x03, 0x04
-};
-
-/* fixed 6 channels */
-static const struct hda_channel_mode alc880_w810_modes[1] = {
- { 6, NULL }
-};
-
-/* Pin assignment: Front = 0x14, Surr = 0x15, CLFE = 0x16, HP = 0x1b */
-static const 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 const hda_nid_t alc880_z71v_dac_nids[1] = {
- 0x02
-};
-#define ALC880_Z71V_HP_DAC 0x03
-
-/* fixed 2 channels */
-static const struct hda_channel_mode alc880_2_jack_modes[1] = {
- { 2, NULL }
-};
-
-static const 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 const hda_nid_t alc880_f1734_dac_nids[1] = {
- 0x03
-};
-#define ALC880_F1734_HP_DAC 0x02
-
-static const 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 const 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 const 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 const 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 const 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 const 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 const 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 const 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 */
-};
-
-/*
- * initialize the codec volumes, etc
- */
-
-/*
- * generic initialization of ADC, input mixers and output mixers
- */
-static const 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 const 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 const 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 const 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 const 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 const 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 const 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 | ALC_HP_EVENT},
- {0x18, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | ALC_MIC_EVENT},
-
- { }
-};
-
-/*
-* Uniwill P53
-* HP = 0x14, InternalSpeaker = 0x15, mic = 0x19,
- */
-static const 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 | ALC_HP_EVENT},
- {0x21, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | ALC_DCVOL_EVENT},
-
- { }
-};
-
-static const struct hda_verb alc880_beep_init_verbs[] = {
- { 0x0b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(5) },
- { }
-};
-
-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;
- alc_simple_setup_automute(spec, ALC_AUTOMUTE_AMP);
-}
-
-static void alc880_uniwill_init_hook(struct hda_codec *codec)
-{
- alc_hp_automute(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!
- */
- res >>= 28;
- switch (res) {
- case ALC_MIC_EVENT:
- alc88x_simple_mic_automute(codec);
- break;
- default:
- alc_exec_unsol_event(codec, res);
- break;
- }
-}
-
-static void alc880_unsol_event(struct hda_codec *codec, unsigned int res)
-{
- alc_exec_unsol_event(codec, res >> 28);
-}
-
-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;
- alc_simple_setup_automute(spec, ALC_AUTOMUTE_AMP);
-}
-
-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!
- */
- res >>= 28;
- if (res == ALC_DCVOL_EVENT)
- alc880_uniwill_p53_dcvol_automute(codec);
- else
- alc_exec_unsol_event(codec, res);
-}
-
-/*
- * F1734 pin configuration:
- * HP = 0x14, speaker-out = 0x15, mic = 0x18
- */
-static const 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|ALC_HP_EVENT},
- {0x21, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN|ALC_DCVOL_EVENT},
-
- { }
-};
-
-/*
- * ASUS pin configuration:
- * HP/front = 0x14, surr = 0x15, clfe = 0x16, mic = 0x18, line = 0x1a
- */
-static const 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 const 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 const 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 const hda_nid_t alc880_lg_dac_nids[3] = {
- 0x05, 0x02, 0x03
-};
-
-/* seems analog CD is not working */
-static const 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 const 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 const 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 const 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 const 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 const 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 const 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 | ALC_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;
- alc_simple_setup_automute(spec, ALC_AUTOMUTE_AMP);
-}
-
-#ifdef CONFIG_SND_HDA_POWER_SAVE
-static const struct hda_amp_list alc880_lg_loopbacks[] = {
- { 0x0b, HDA_INPUT, 1 },
- { 0x0b, HDA_INPUT, 6 },
- { 0x0b, HDA_INPUT, 7 },
- { } /* end */
-};
-#endif
-
-/*
- * Test configuration for debugging
- *
- * Almost all inputs/outputs are enabled. I/O pins can be configured via
- * enum controls.
- */
-#ifdef CONFIG_SND_DEBUG
-static const hda_nid_t alc880_test_dac_nids[4] = {
- 0x02, 0x03, 0x04, 0x05
-};
-
-static const 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 const 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 const char * const 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 const 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 const char * const 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 const 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 const 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",
-#ifdef CONFIG_SND_DEBUG
- [ALC880_TEST] = "test",
-#endif
- [ALC880_AUTO] = "auto",
-};
-
-static const 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(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(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, 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(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 const 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_hp_automute,
- },
- [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_hp_automute,
- },
- [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_hp_automute,
- },
- [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 = alc880_unsol_event,
- .setup = alc880_lg_setup,
- .init_hook = alc_hp_automute,
-#ifdef CONFIG_SND_HDA_POWER_SAVE
- .loopbacks = alc880_lg_loopbacks,
-#endif
- },
-#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
-};
-
diff --git a/sound/pci/hda/alc882_quirks.c b/sound/pci/hda/alc882_quirks.c
deleted file mode 100644
index bb364a53f54..00000000000
--- a/sound/pci/hda/alc882_quirks.c
+++ /dev/null
@@ -1,866 +0,0 @@
-/*
- * ALC882/ALC883/ALC888/ALC889 quirk models
- * included by patch_realtek.c
- */
-
-/* ALC882 models */
-enum {
- ALC882_AUTO,
- ALC885_MBA21,
- ALC885_MBP3,
- ALC885_MB5,
- ALC885_MACMINI3,
- ALC885_IMAC91,
- ALC889A_MB31,
- ALC882_MODEL_LAST,
-};
-
-#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 const struct hda_channel_mode alc882_ch_modes[1] = {
- { 8, NULL }
-};
-
-/* DACs */
-static const 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 const hda_nid_t alc882_capsrc_nids_alt[2] = { 0x23, 0x22 };
-#define alc883_capsrc_nids alc882_capsrc_nids_alt
-
-/* input MUX */
-/* FIXME: should be a matrix-type input source selection */
-
-static const 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 const struct hda_input_mux mb5_capture_source = {
- .num_items = 3,
- .items = {
- { "Mic", 0x1 },
- { "Line", 0x7 },
- { "CD", 0x4 },
- },
-};
-
-static const struct hda_input_mux macmini3_capture_source = {
- .num_items = 2,
- .items = {
- { "Line", 0x2 },
- { "CD", 0x4 },
- },
-};
-
-static const struct hda_input_mux alc883_3stack_6ch_intel = {
- .num_items = 4,
- .items = {
- { "Mic", 0x1 },
- { "Front Mic", 0x0 },
- { "Line", 0x2 },
- { "CD", 0x4 },
- },
-};
-
-static const 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 const struct hda_input_mux alc889A_imac91_capture_source = {
- .num_items = 2,
- .items = {
- { "Mic", 0x01 },
- { "Line", 0x2 }, /* Not sure! */
- },
-};
-
-/* Macbook Air 2,1 */
-
-static const 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 const 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 const 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 const 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 const 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 const 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 const 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
-
-/* Macbook Air 2,1 same control for HP and internal Speaker */
-
-static const 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 const 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 const 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 const 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 const 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 const 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 const 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},
-
- { }
-};
-
-#define alc883_init_verbs alc882_base_init_verbs
-
-/* Macbook 5,1 */
-static const 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, ALC_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 const 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, ALC_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 const 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, (ALC_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 const 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, ALC_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 const 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, (ALC_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},
- { }
-};
-
-/* 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;
- alc_simple_setup_automute(spec, ALC_AUTOMUTE_AMP);
-}
-
-#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;
- alc_simple_setup_automute(spec, ALC_AUTOMUTE_AMP);
-}
-
-
-
-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;
- alc_simple_setup_automute(spec, ALC_AUTOMUTE_AMP);
-}
-
-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;
- alc_simple_setup_automute(spec, ALC_AUTOMUTE_AMP);
-}
-
-/* 2ch mode (Speaker:front, Subwoofer:CLFE, Line:input, Headphones:front) */
-static const 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 const 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 const 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 const 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 const 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 const 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 const 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 const 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 */
-};
-
-static const 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, ALC_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);
- }
-}
-
-static void alc889A_mb31_unsol_event(struct hda_codec *codec, unsigned int res)
-{
- if ((res >> 26) == ALC_HP_EVENT)
- alc889A_mb31_automute(codec);
-}
-
-static void alc882_unsol_event(struct hda_codec *codec, unsigned int res)
-{
- alc_exec_unsol_event(codec, res >> 26);
-}
-
-/*
- * configuration and preset
- */
-static const char * const alc882_models[ALC882_MODEL_LAST] = {
- [ALC885_MB5] = "mb5",
- [ALC885_MACMINI3] = "macmini3",
- [ALC885_MBA21] = "mba21",
- [ALC885_MBP3] = "mbp3",
- [ALC885_IMAC91] = "imac91",
- [ALC889A_MB31] = "mb31",
- [ALC882_AUTO] = "auto",
-};
-
-/* codec SSID table for Intel Mac */
-static const 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, 0x2c00, "MacbookPro rev3", ALC885_MBP3),
- SND_PCI_QUIRK(0x106b, 0x3000, "iMac", ALC889A_MB31),
- 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, 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 const struct alc_config_preset alc882_presets[] = {
- [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 = alc882_unsol_event,
- .setup = alc885_mba21_setup,
- .init_hook = alc_hp_automute,
- },
- [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 = alc882_unsol_event,
- .setup = alc885_mbp3_setup,
- .init_hook = alc_hp_automute,
- },
- [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 = alc882_unsol_event,
- .setup = alc885_mb5_setup,
- .init_hook = alc_hp_automute,
- },
- [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 = alc882_unsol_event,
- .setup = alc885_macmini3_setup,
- .init_hook = alc_hp_automute,
- },
- [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 = alc882_unsol_event,
- .setup = alc885_imac91_setup,
- .init_hook = alc_hp_automute,
- },
- [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,
- },
-};
-
-
diff --git a/sound/pci/hda/alc_quirks.c b/sound/pci/hda/alc_quirks.c
deleted file mode 100644
index a18952ed431..00000000000
--- a/sound/pci/hda/alc_quirks.c
+++ /dev/null
@@ -1,480 +0,0 @@
-/*
- * Common codes for Realtek codec quirks
- * included by patch_realtek.c
- */
-
-/*
- * configuration template - to be copied to the spec instance
- */
-struct alc_config_preset {
- const struct snd_kcontrol_new *mixers[5]; /* should be identical size
- * with spec
- */
- const struct snd_kcontrol_new *cap_mixer; /* capture mixer */
- const struct hda_verb *init_verbs[5];
- unsigned int num_dacs;
- const hda_nid_t *dac_nids;
- hda_nid_t dig_out_nid; /* optional */
- hda_nid_t hp_nid; /* optional */
- const hda_nid_t *slave_dig_outs;
- unsigned int num_adc_nids;
- const hda_nid_t *adc_nids;
- const 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
- const struct hda_amp_list *loopbacks;
- void (*power_hook)(struct hda_codec *codec);
-#endif
-};
-
-/*
- * 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 const char * const alc_pin_mode_names[] = {
- "Mic 50pc bias", "Mic 80pc bias",
- "Line in", "Line out", "Headphone out",
-};
-static const 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 const 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 */
-
-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++;
- }
-}
-
-/*
- * set up from the preset table
- */
-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);
-}
-
-static void alc_simple_setup_automute(struct alc_spec *spec, int mode)
-{
- int lo_pin = spec->autocfg.line_out_pins[0];
-
- if (lo_pin == spec->autocfg.speaker_pins[0] ||
- lo_pin == spec->autocfg.hp_pins[0])
- lo_pin = 0;
- spec->automute_mode = mode;
- spec->detect_hp = !!spec->autocfg.hp_pins[0];
- spec->detect_lo = !!lo_pin;
- spec->automute_lo = spec->automute_lo_possible = !!lo_pin;
- spec->automute_speaker = spec->automute_speaker_possible = !!spec->autocfg.speaker_pins[0];
-}
-
-/* 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);
-}
-
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 60738e52b8f..8c6c50afc0b 100644
--- a/sound/pci/hda/hda_beep.c
+++ b/sound/pci/hda/hda_beep.c
@@ -20,7 +20,6 @@
*/
#include <linux/input.h>
-#include <linux/pci.h>
#include <linux/slab.h>
#include <linux/workqueue.h>
#include <linux/export.h>
@@ -39,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
@@ -100,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);
@@ -115,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";
@@ -149,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 */
@@ -227,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 55f0647458c..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,6 +46,7 @@ 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
static inline int snd_hda_attach_beep_device(struct hda_codec *codec, int nid)
{
@@ -57,5 +55,9 @@ static inline int snd_hda_attach_beep_device(struct hda_codec *codec, int nid)
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 684307372d7..4c20277a683 100644
--- a/sound/pci/hda/hda_codec.c
+++ b/sound/pci/hda/hda_codec.c
@@ -19,12 +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>
@@ -66,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" },
@@ -82,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)
{
@@ -91,15 +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
/**
@@ -134,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
@@ -149,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
@@ -164,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;
@@ -200,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;
@@ -213,25 +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);
- trace_hda_send_cmd(codec, cmd);
- err = bus->ops.command(bus, cmd);
+ 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;
}
@@ -240,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
*
@@ -249,22 +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;
- if (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
*
@@ -272,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
@@ -295,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
@@ -317,70 +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 hda_nid_t *lookup_conn_list(struct snd_array *array, hda_nid_t nid)
+static struct hda_conn_list *
+lookup_conn_list(struct hda_codec *codec, hda_nid_t nid)
{
- int i, len;
- for (i = 0; i < array->used; ) {
- hda_nid_t *p = snd_array_elem(array, i);
- if (nid == *p)
+ struct hda_conn_list *p;
+ list_for_each_entry(p, &codec->conn_list, list) {
+ if (p->nid == nid)
return p;
- len = p[1];
- i += len + 2;
}
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_conn_list - get connection list
* @codec: the HDA codec
* @nid: NID to parse
+ * @len: number of connection list entries
* @listp: the pointer to store NID list
*
- * Parses the connection list of the given widget and stores the list
- * of NIDs.
+ * 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)
{
- struct snd_array *array = &codec->conn_lists;
- int len, err;
- hda_nid_t list[HDA_MAX_CONNECTIONS];
- hda_nid_t *p;
bool added = false;
- again:
- /* if the connection-list is already cached, read it */
- p = lookup_conn_list(array, nid);
- if (p) {
- if (listp)
- *listp = p + 2;
- return p[1];
- }
- if (snd_BUG_ON(added))
- return -EINVAL;
+ for (;;) {
+ int err;
+ const struct hda_conn_list *p;
- /* read the connection and add to the cache */
- len = snd_hda_get_raw_connections(codec, nid, list, HDA_MAX_CONNECTIONS);
- if (len < 0)
- return len;
- err = snd_hda_override_conn_list(codec, nid, len, list);
- if (err < 0)
- return err;
- added = true;
- goto again;
+ /* 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_HDA(snd_hda_get_conn_list);
+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
+ * @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
@@ -389,23 +468,44 @@ EXPORT_SYMBOL_HDA(snd_hda_get_conn_list);
* Returns the number of connections, or a negative error code.
*/
int snd_hda_get_connections(struct hda_codec *codec, hda_nid_t nid,
- hda_nid_t *conn_list, int max_conns)
+ 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)
- return len;
- if (len > max_conns) {
- snd_printk(KERN_ERR "hda_codec: "
- "Too many connections %d for NID 0x%x\n",
- len, nid);
- return -EINVAL;
+ 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));
}
- memcpy(conn_list, list, len * sizeof(hda_nid_t));
+
return len;
}
-EXPORT_SYMBOL_HDA(snd_hda_get_connections);
+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
@@ -424,18 +524,13 @@ int snd_hda_get_raw_connections(struct hda_codec *codec, hda_nid_t nid,
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)
+ 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;
@@ -457,7 +552,8 @@ int snd_hda_get_raw_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;
}
@@ -476,9 +572,9 @@ int snd_hda_get_raw_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;
}
@@ -486,43 +582,32 @@ int snd_hda_get_raw_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;
}
-static bool add_conn_list(struct snd_array *array, hda_nid_t nid)
-{
- hda_nid_t *p = snd_array_new(array);
- if (!p)
- return false;
- *p = nid;
- return true;
-}
-
/**
* snd_hda_override_conn_list - add/modify the connection-list to cache
* @codec: the HDA codec
@@ -538,27 +623,17 @@ static bool add_conn_list(struct snd_array *array, hda_nid_t nid)
int snd_hda_override_conn_list(struct hda_codec *codec, hda_nid_t nid, int len,
const hda_nid_t *list)
{
- struct snd_array *array = &codec->conn_lists;
- hda_nid_t *p;
- int i, old_used;
+ struct hda_conn_list *p;
- p = lookup_conn_list(array, nid);
- if (p)
- *p = -1; /* invalidate the old entry */
-
- old_used = array->used;
- if (!add_conn_list(array, nid) || !add_conn_list(array, len))
- goto error_add;
- for (i = 0; i < len; i++)
- if (!add_conn_list(array, list[i]))
- goto error_add;
- return 0;
+ p = lookup_conn_list(codec, nid);
+ if (p) {
+ list_del(&p->list);
+ kfree(p);
+ }
- error_add:
- array->used = old_used;
- return -ENOMEM;
+ return add_conn_list(codec, nid, len, list);
}
-EXPORT_SYMBOL_HDA(snd_hda_override_conn_list);
+EXPORT_SYMBOL_GPL(snd_hda_override_conn_list);
/**
* snd_hda_get_conn_index - get the connection index of the given NID
@@ -574,17 +649,17 @@ EXPORT_SYMBOL_HDA(snd_hda_override_conn_list);
int snd_hda_get_conn_index(struct hda_codec *codec, hda_nid_t mux,
hda_nid_t nid, int recursive)
{
- hda_nid_t conn[HDA_MAX_NUM_INPUTS];
+ const hda_nid_t *conn;
int i, nums;
- nums = snd_hda_get_connections(codec, mux, conn, ARRAY_SIZE(conn));
+ 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 > 5) {
- snd_printd("hda_codec: too deep connection for 0x%x\n", nid);
+ if (recursive > 10) {
+ codec_dbg(codec, "too deep connection for 0x%x\n", nid);
return -1;
}
recursive++;
@@ -597,7 +672,65 @@ int snd_hda_get_conn_index(struct hda_codec *codec, hda_nid_t mux,
}
return -1;
}
-EXPORT_SYMBOL_HDA(snd_hda_get_conn_index);
+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
@@ -616,6 +749,9 @@ 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)
@@ -632,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
@@ -671,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);
@@ -684,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
@@ -737,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,
};
@@ -758,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;
}
@@ -777,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;
@@ -792,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
@@ -815,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++) {
@@ -907,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;
@@ -992,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()
*/
@@ -1012,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);
@@ -1026,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;
}
@@ -1051,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
@@ -1066,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)
@@ -1079,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
@@ -1115,7 +1239,7 @@ 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() */
@@ -1136,21 +1260,32 @@ static void restore_shutup_pins(struct hda_codec *codec)
}
#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);
}
@@ -1185,25 +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->conn_lists);
+ 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);
@@ -1211,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
@@ -1225,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;
@@ -1239,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);
@@ -1261,17 +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);
- snd_array_init(&codec->conn_lists, sizeof(hda_nid_t), 64);
snd_array_init(&codec->spdif_out, sizeof(struct hda_spdif_out), 16);
- 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->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
@@ -1280,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,
@@ -1298,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);
@@ -1313,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);
@@ -1332,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;
@@ -1340,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
@@ -1353,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);
@@ -1362,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;
@@ -1426,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);
@@ -1438,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;
@@ -1452,7 +1775,7 @@ void snd_hda_codec_setup_stream(struct hda_codec *codec, hda_nid_t nid,
}
}
}
-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);
@@ -1474,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;
@@ -1487,7 +1810,7 @@ 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)
@@ -1545,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));
@@ -1588,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;
@@ -1602,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
@@ -1616,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
@@ -1651,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;
-}
-EXPORT_SYMBOL_HDA(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;
+ return write_caps_hash(codec, HDA_HASH_KEY(nid, dir, 0), caps);
}
+EXPORT_SYMBOL_GPL(snd_hda_override_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);
}
@@ -1696,10 +2039,10 @@ 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_override_pin_caps - Override the pin capabilities
@@ -1714,43 +2057,50 @@ EXPORT_SYMBOL_HDA(snd_hda_query_pin_caps);
int snd_hda_override_pin_caps(struct hda_codec *codec, hda_nid_t nid,
unsigned int caps)
{
- struct hda_amp_info *info;
- info = get_alloc_amp_hash(codec, HDA_HASH_PINCAP_KEY(nid));
- if (!info)
- return -ENOMEM;
- info->amp_caps = caps;
- info->head.val |= INFO_AMP_CAPS;
- return 0;
+ return write_caps_hash(codec, HDA_HASH_PINCAP_KEY(nid), caps);
}
-EXPORT_SYMBOL_HDA(snd_hda_override_pin_caps);
+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)
{
@@ -1759,13 +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;
- if ((val & HDA_AMP_MUTE) && !(info->amp_caps & AC_AMPCAP_MUTE) &&
- (info->amp_caps & AC_AMPCAP_MIN_MUTE))
+ 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;
}
/**
@@ -1782,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
@@ -1805,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
@@ -1845,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 CONFIG_PM
/**
* snd_hda_codec_resume_amp - Resume all AMP commands from the cache
* @codec: HD-audio codec
@@ -1856,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 /* CONFIG_PM */
+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)
@@ -1910,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
@@ -1974,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
@@ -2004,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
@@ -2042,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
@@ -2070,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;
@@ -2097,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;
@@ -2157,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
@@ -2184,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
@@ -2207,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
@@ -2238,44 +2675,32 @@ 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)
+ if (snd_hda_lock_devices(bus) < 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);
- 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;
@@ -2285,18 +2710,20 @@ 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;
}
@@ -2304,7 +2731,7 @@ 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,
- map_slave_func_t func, void *data)
+ const char *suffix, map_slave_func_t func, void *data)
{
struct hda_nid_item *items;
const char * const *s;
@@ -2313,11 +2740,17 @@ static int map_slaves(struct hda_codec *codec, const char * const *slaves,
items = codec->mixers.list;
for (i = 0; i < codec->mixers.used; i++) {
struct snd_kcontrol *sctl = items[i].kctl;
- if (!sctl || !sctl->id.name ||
- sctl->id.iface != SNDRV_CTL_ELEM_IFACE_MIXER)
+ if (!sctl || sctl->id.iface != SNDRV_CTL_ELEM_IFACE_MIXER)
continue;
for (s = slaves; *s; s++) {
- if (!strcmp(sctl->id.name, *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;
@@ -2333,12 +2766,76 @@ 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.
@@ -2349,15 +2846,20 @@ static int check_slave_present(void *data, struct snd_kcontrol *sctl)
*
* 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;
int err;
- err = map_slaves(codec, slaves, check_slave_present, NULL);
+ if (ctl_ret)
+ *ctl_ret = NULL;
+
+ err = map_slaves(codec, slaves, suffix, check_slave_present, NULL);
if (err != 1) {
- snd_printdd("No slave found for %s\n", name);
+ codec_dbg(codec, "No slave found for %s\n", name);
return 0;
}
kctl = snd_ctl_make_virtual_master(name, tlv);
@@ -2367,13 +2869,126 @@ int snd_hda_add_vmaster(struct hda_codec *codec, char *name,
if (err < 0)
return err;
- err = map_slaves(codec, slaves, (map_slave_func_t)snd_ctl_add_slave,
- kctl);
+ 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
@@ -2392,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
@@ -2418,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
@@ -2452,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
@@ -2503,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
@@ -2533,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
@@ -2556,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
@@ -2579,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
@@ -2608,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
@@ -2631,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,
@@ -2639,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,
@@ -2647,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
@@ -2687,12 +3283,15 @@ static int snd_hda_spdif_default_get(struct snd_kcontrol *kcontrol,
{
struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
int idx = kcontrol->private_value;
- struct hda_spdif_out *spdif = snd_array_elem(&codec->spdif_out, idx);
+ struct hda_spdif_out *spdif;
+ 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;
}
@@ -2736,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)
@@ -2778,12 +3377,14 @@ static int snd_hda_spdif_default_put(struct snd_kcontrol *kcontrol,
{
struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
int idx = kcontrol->private_value;
- struct hda_spdif_out *spdif = snd_array_elem(&codec->spdif_out, idx);
- hda_nid_t nid = spdif->nid;
+ struct hda_spdif_out *spdif;
+ hda_nid_t nid;
unsigned short val;
int change;
mutex_lock(&codec->spdif_mutex);
+ 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) |
@@ -2805,9 +3406,12 @@ static int snd_hda_spdif_out_switch_get(struct snd_kcontrol *kcontrol,
{
struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
int idx = kcontrol->private_value;
- struct hda_spdif_out *spdif = snd_array_elem(&codec->spdif_out, idx);
+ struct hda_spdif_out *spdif;
+ 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;
}
@@ -2827,12 +3431,14 @@ static int snd_hda_spdif_out_switch_put(struct snd_kcontrol *kcontrol,
{
struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
int idx = kcontrol->private_value;
- struct hda_spdif_out *spdif = snd_array_elem(&codec->spdif_out, idx);
- hda_nid_t nid = spdif->nid;
+ struct hda_spdif_out *spdif;
+ hda_nid_t nid;
unsigned short val;
int change;
mutex_lock(&codec->spdif_mutex);
+ 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;
@@ -2877,31 +3483,54 @@ 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 associated_nid,
- hda_nid_t cvt_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)
@@ -2918,8 +3547,11 @@ int snd_hda_create_spdif_out_ctls(struct hda_codec *codec,
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)
{
@@ -2932,24 +3564,26 @@ struct hda_spdif_out *snd_hda_spdif_out_of_nid(struct hda_codec *codec,
}
return NULL;
}
-EXPORT_SYMBOL_HDA(snd_hda_spdif_out_of_nid);
+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 = snd_array_elem(&codec->spdif_out, 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_HDA(snd_hda_spdif_ctls_unassign);
+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 = snd_array_elem(&codec->spdif_out, idx);
+ 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;
@@ -2957,7 +3591,7 @@ void snd_hda_spdif_ctls_assign(struct hda_codec *codec, int idx, hda_nid_t nid)
}
mutex_unlock(&codec->spdif_mutex);
}
-EXPORT_SYMBOL_HDA(snd_hda_spdif_ctls_assign);
+EXPORT_SYMBOL_GPL(snd_hda_spdif_ctls_assign);
/*
* SPDIF sharing with analog output
@@ -2994,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
@@ -3088,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++) {
@@ -3108,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 CONFIG_PM
/*
* 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)
@@ -3124,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
*
@@ -3133,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
*
@@ -3169,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;
@@ -3185,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
@@ -3197,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
@@ -3226,71 +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 /* CONFIG_PM */
+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,
- bool eapd_workaround)
+ 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;
- /* don't power down the widget if it controls eapd and
- * EAPD_BTLENABLE is set.
- */
- if (eapd_workaround && power_state == AC_PWRST_D3 &&
- get_wcaps_type(wcaps) == 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)
+ 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,
- 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));
+ state);
}
}
-EXPORT_SYMBOL_HDA(snd_hda_codec_set_power_to_all);
+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)
{
- if (codec->patch_ops.set_power_state) {
- codec->patch_ops.set_power_state(codec, fg, power_state);
- return;
+ 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);
- snd_hda_codec_set_power_to_all(codec, fg, power_state, true);
+ 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;
}
-#ifdef CONFIG_SND_HDA_HWDEP
+/* 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;
+
+ for (i = 0; i < codec->num_nodes; i++, nid++) {
+ unsigned int wcaps = get_wcaps(codec, nid);
+ 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, target);
+ }
+}
+
+#ifdef CONFIG_SND_HDA_RECONFIG
/* execute additional init verbs */
static void hda_exec_init_verbs(struct hda_codec *codec)
{
@@ -3304,22 +4062,46 @@ static inline void hda_exec_init_verbs(struct hda_codec *codec) {}
#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;
+ }
}
/*
@@ -3327,10 +4109,15 @@ 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);
@@ -3342,6 +4129,14 @@ 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 /* CONFIG_PM */
@@ -3354,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)
{
@@ -3386,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;
}
@@ -3482,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;
}
@@ -3491,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 &&
@@ -3508,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)
@@ -3524,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);
}
@@ -3556,10 +4409,10 @@ 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;
@@ -3603,11 +4456,13 @@ 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
@@ -3617,12 +4472,11 @@ 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)
@@ -3633,7 +4487,7 @@ int snd_hda_query_supported_pcm(struct hda_codec *codec, hda_nid_t nid,
return 0;
}
-EXPORT_SYMBOL_HDA(snd_hda_query_supported_pcm);
+EXPORT_SYMBOL_GPL(snd_hda_query_supported_pcm);
/**
* snd_hda_is_supported_format - Check the validity of the format
@@ -3700,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
@@ -3778,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,
@@ -3788,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] = {
@@ -3797,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 },
@@ -3812,22 +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;
}
@@ -3864,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;
}
}
@@ -3888,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 */
}
}
@@ -3924,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;
@@ -3935,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
@@ -3959,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;
}
}
@@ -3982,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
@@ -4043,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
@@ -4093,7 +4958,7 @@ int snd_hda_add_new_ctls(struct hda_codec *codec,
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
@@ -4102,31 +4967,41 @@ int snd_hda_add_new_ctls(struct hda_codec *codec,
}
return 0;
}
-EXPORT_SYMBOL_HDA(snd_hda_add_new_ctls);
+EXPORT_SYMBOL_GPL(snd_hda_add_new_ctls);
-#ifdef CONFIG_SND_HDA_POWER_SAVE
+#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);
- trace_hda_power_down(codec);
- 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 */
@@ -4140,55 +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
@@ -4238,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
/*
@@ -4262,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
@@ -4283,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
@@ -4307,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
@@ -4332,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
@@ -4357,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
*/
@@ -4368,10 +5297,20 @@ 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)
{
- struct hda_spdif_out *spdif = snd_hda_spdif_out_of_nid(codec, nid);
-
- /* turn off SPDIF once; otherwise the IEC958 bits won't be updated */
- if (codec->spdif_status_reset && (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,
spdif->ctls & ~AC_DIG1_ENABLE & 0xff,
-1);
@@ -4383,7 +5322,7 @@ static void setup_dig_out_stream(struct hda_codec *codec, hda_nid_t nid,
format);
}
/* turn on again (if needed) */
- if (codec->spdif_status_reset && (spdif->ctls & AC_DIG1_ENABLE))
+ if (reset)
set_dig_out_convert(codec, nid,
spdif->ctls & 0xff, -1);
}
@@ -4414,7 +5353,7 @@ void snd_hda_bus_reboot_notify(struct hda_bus *bus)
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
@@ -4430,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
@@ -4446,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
@@ -4459,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
@@ -4472,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
@@ -4522,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.
@@ -4538,11 +5477,11 @@ int snd_hda_multi_out_analog_prepare(struct hda_codec *codec,
{
const hda_nid_t *nids = mout->dac_nids;
int chs = substream->runtime->channels;
- struct hda_spdif_out *spdif =
- snd_hda_spdif_out_of_nid(codec, mout->dig_out_nid);
+ 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 &&
@@ -4573,11 +5512,6 @@ int snd_hda_multi_out_analog_prepare(struct hda_codec *codec,
snd_hda_codec_setup_stream(codec,
mout->hp_out_nid[i],
stream_tag, 0, format);
- for (i = 0; i < ARRAY_SIZE(mout->extra_out_nid); i++)
- if (!mout->no_share_stream && 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++) {
@@ -4588,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
@@ -4621,603 +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, const 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;
- }
- }
- }
-}
-
-/* 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;
- }
-}
-
-/*
- * 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;
- 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 = 0;
-
- codec->ignore_misc_bit = true;
- 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);
- if (!(get_defcfg_misc(snd_hda_codec_get_pincfg(codec, nid)) &
- AC_DEFCFG_MISC_NO_PRESENCE))
- codec->ignore_misc_bit = false;
- 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)
- dev = AC_JACK_SPEAKER;
- }
-
- 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)
- 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 (cfg->speaker_outs >= ARRAY_SIZE(cfg->speaker_pins))
- continue;
- cfg->speaker_pins[cfg->speaker_outs] = nid;
- sequences_speaker[cfg->speaker_outs] = (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))
- 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 &&
- !(cond_flags & HDA_PINCFG_NO_HP_FIXUP)) {
- 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 &&
- !(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_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) 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"));
- 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_printd(" %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_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_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".
- */
-
-static const char *hda_get_input_pin_label(struct hda_codec *codec,
- hda_nid_t pin, bool 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";
- }
-}
-
-/* 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;
-}
-/**
- * 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].pin,
- has_multiple_pins);
-}
-EXPORT_SYMBOL_HDA(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;
+ return val;
}
+EXPORT_SYMBOL_GPL(snd_hda_correct_pin_ctl);
-/* 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)
+int _snd_hda_set_pin_ctl(struct hda_codec *codec, hda_nid_t pin,
+ unsigned int val, bool cached)
{
- 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 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";
- /* check the location */
- switch (attr) {
- case INPUT_PIN_ATTR_DOCK:
- pfx = "Dock ";
- break;
- case INPUT_PIN_ATTR_FRONT:
- pfx = "Front ";
- break;
- }
- 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 = find_idx_in_nid_list(nid, cfg->hp_pins,
- cfg->hp_outs);
- if (idx >= 0)
- *indexp = idx;
- sfx = "";
- }
- }
- snprintf(label, maxlen, "%s%s%s", pfx, name, sfx);
- return 1;
-}
-
-/**
- * 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;
-
- 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:
- if (get_defcfg_location(def_conf) == AC_JACK_LOC_HDMI)
- name = "HDMI";
- else
- name = "SPDIF";
- if (cfg && indexp) {
- i = find_idx_in_nid_list(nid, cfg->dig_out_pins,
- cfg->dig_outs);
- if (i >= 0)
- *indexp = i;
- }
- 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, nid, true);
- break;
- }
- if (!name)
- return 0;
- strlcpy(label, name, maxlen);
- return 1;
+ 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(snd_hda_get_pin_label);
+EXPORT_SYMBOL_GPL(_snd_hda_set_pin_ctl);
/**
* snd_hda_add_imux_item - Add an item to input_mux
@@ -5251,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
@@ -5259,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
@@ -5268,39 +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) {
- if (hda_codec_is_power_on(codec))
- hda_call_codec_suspend(codec);
- if (codec->patch_ops.post_suspend)
- codec->patch_ops.post_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 (codec->patch_ops.pre_resume)
- codec->patch_ops.pre_resume(codec);
- 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 */
/*
@@ -5318,23 +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;
- int oldsize = array->alloced * array->elem_size;
void *nlist;
if (snd_BUG_ON(num >= 4096))
return NULL;
- nlist = krealloc(array->list, size, GFP_KERNEL);
+ nlist = krealloc(array->list, size, GFP_KERNEL | __GFP_ZERO);
if (!nlist)
return NULL;
- memset(nlist + oldsize, 0, size - oldsize);
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
@@ -5347,7 +5813,7 @@ void snd_array_free(struct snd_array *array)
array->alloced = 0;
array->list = NULL;
}
-EXPORT_SYMBOL_HDA(snd_array_free);
+EXPORT_SYMBOL_GPL(snd_array_free);
/**
* snd_print_pcm_bits - Print the supported PCM fmt bits to the string buffer
@@ -5368,7 +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);
+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 f0f1943a4b2..5825aa17d8e 100644
--- a/sound/pci/hda/hda_codec.h
+++ b/sound/pci/hda/hda_codec.h
@@ -25,530 +25,7 @@
#include <sound/control.h>
#include <sound/pcm.h>
#include <sound/hwdep.h>
-
-/*
- * 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
-
-/* driver-specific amp-caps: using bits 24-30 */
-#define AC_AMPCAP_MIN_MUTE (1 << 30) /* min-volume = mute */
-
-/* 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
+#include <sound/hda_verbs.h>
/*
* generic arrays
@@ -607,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
};
@@ -640,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];
@@ -664,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 */
};
/*
@@ -703,12 +195,8 @@ struct hda_codec_ops {
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, pm_message_t state);
- int (*post_suspend)(struct hda_codec *codec);
- int (*pre_resume)(struct hda_codec *codec);
+ 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,10 +321,11 @@ struct hda_codec {
struct hda_cache_rec amp_cache; /* cache for amp access */
struct hda_cache_rec cmd_cache; /* cache for other commands */
- struct snd_array conn_lists; /* connection-list array */
+ struct list_head conn_list; /* linked-list of connection-list */
struct mutex spdif_mutex;
struct mutex control_mutex;
+ struct mutex hash_mutex;
struct snd_array spdif_out;
unsigned int spdif_in_enable; /* SPDIF input enable? */
const hda_nid_t *slave_dig_outs; /* optional digital out slave widgets */
@@ -836,13 +333,17 @@ struct hda_codec {
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
@@ -851,31 +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 */
- unsigned int ignore_misc_bit:1; /* ignore MISC_NO_PRESENCE bit */
-#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 */
@@ -883,6 +415,8 @@ enum {
HDA_INPUT, HDA_OUTPUT
};
+/* snd_hda_codec_read/write optional flags */
+#define HDA_RW_NO_RESPONSE_FALLBACK (1 << 0)
/*
* constructors
@@ -892,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)
@@ -907,6 +442,12 @@ 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,
@@ -915,6 +456,8 @@ 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);
@@ -931,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 CONFIG_PM
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 */
};
@@ -1007,14 +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,
- bool eapd_workaround);
+ unsigned int power_state);
+
+int snd_hda_lock_devices(struct hda_bus *bus);
+void snd_hda_unlock_devices(struct hda_bus *bus);
/*
* power management
@@ -1027,7 +570,7 @@ int snd_hda_resume(struct hda_bus *bus);
static inline
int hda_call_check_power_status(struct hda_codec *codec, hda_nid_t nid)
{
-#ifdef CONFIG_SND_HDA_POWER_SAVE
+#ifdef CONFIG_PM
if (codec->patch_ops.check_power_status)
return codec->patch_ops.check_power_status(codec, nid);
#endif
@@ -1044,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 c1da422e085..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>
@@ -152,7 +153,7 @@ static unsigned int hdmi_get_eld_data(struct hda_codec *codec, hda_nid_t nid,
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
return val;
}
@@ -246,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;
@@ -260,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);
@@ -305,7 +305,6 @@ static int hdmi_update_eld(struct hdmi_eld *e,
if (!e->spk_alloc)
e->spk_alloc = 0xffff;
- e->eld_valid = true;
return 0;
out_fail:
@@ -318,33 +317,30 @@ int snd_hdmi_get_eld_size(struct hda_codec *codec, hda_nid_t nid)
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;
/*
* ELD size is initialized to zero in caller function. If no errors and
- * ELD is valid, actual eld_size is assigned in hdmi_update_eld()
+ * 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 > ELD_MAX_SIZE) {
- snd_printd(KERN_INFO "HDMI: invalid ELD buf size %d\n", size);
+ codec_info(codec, "HDMI: invalid ELD buf size %d\n", size);
return -ERANGE;
}
/* set ELD buffer */
- buf = eld->eld_buffer;
-
for (i = 0; i < size; i++) {
unsigned int val = hdmi_get_eld_data(codec, nid, i);
/*
@@ -352,8 +348,7 @@ int snd_hdmi_get_eld(struct hdmi_eld *eld,
* Just abort. The caller will repoll after a while.
*/
if (!(val & AC_ELDD_ELD_VALID)) {
- snd_printd(KERN_INFO
- "HDMI: invalid ELD data byte %d\n", i);
+ codec_info(codec, "HDMI: invalid ELD data byte %d\n", i);
ret = -EINVAL;
goto error;
}
@@ -365,15 +360,14 @@ int snd_hdmi_get_eld(struct hdmi_eld *eld,
* correctly writes ELD content before setting ELD_valid bit.
*/
if (!val && !i) {
- snd_printdd(KERN_INFO "HDMI: 0 ELD data\n");
+ codec_dbg(codec, "HDMI: 0 ELD data\n");
ret = -EINVAL;
goto error;
}
buf[i] = val;
}
- ret = hdmi_update_eld(eld, buf, size);
-
+ *eld_size = size;
error:
return ret;
}
@@ -385,8 +379,8 @@ error:
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, 88200,
- 96000, 176400, 192000, 384000
+ 5512, 8000, 11025, 16000, 22050, 32000, 44100, 48000, 64000,
+ 88200, 96000, 176400, 192000, 384000
};
int i, j;
@@ -418,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,
@@ -438,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++)
@@ -484,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",
@@ -505,15 +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);
- if (!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);
@@ -532,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;
@@ -551,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"))
@@ -594,40 +588,10 @@ 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 snd_hdmi_eld_update_pcm_info(struct hdmi_eld *eld,
+void snd_hdmi_eld_update_pcm_info(struct parsed_hdmi_eld *e,
struct hda_pcm_stream *hinfo)
{
u32 rates;
@@ -644,8 +608,8 @@ void snd_hdmi_eld_update_pcm_info(struct hdmi_eld *eld,
formats = SNDRV_PCM_FMTBIT_S16_LE;
maxbps = 16;
channels_max = 2;
- for (i = 0; i < eld->sad_count; i++) {
- struct cea_sad *a = &eld->sad[i];
+ 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;
@@ -669,3 +633,174 @@ void snd_hdmi_eld_update_pcm_info(struct hdmi_eld *eld,
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 431bf868711..589e47c5aeb 100644
--- a/sound/pci/hda/hda_generic.c
+++ b/sound/pci/hda/hda_generic.c
@@ -23,836 +23,3576 @@
#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;
}
@@ -860,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 6b2efb8cb1f..014a7849e8f 100644
--- a/sound/pci/hda/hda_hwdep.c
+++ b/sound/pci/hda/hda_hwdep.c
@@ -20,25 +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 <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 */
-};
-
/*
* write/read an out-of-bound verb
*/
@@ -106,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;
@@ -140,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;
@@ -149,669 +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)
-{
- 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 (!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;
-
- 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 95dfb687494..83cd19017cf 100644
--- a/sound/pci/hda/hda_intel.c
+++ b/sound/pci/hda/hda_intel.c
@@ -46,6 +46,11 @@
#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>
@@ -53,24 +58,31 @@
#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 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 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
@@ -84,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).");
@@ -119,10 +141,12 @@ MODULE_PARM_DESC(power_save, "Automatic power-saving timeout "
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.");
-#endif
+#else
+static int *power_save_addr;
+#endif /* CONFIG_PM */
-static bool align_buffer_size = 1;
-module_param(align_buffer_size, bool, 0644);
+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.");
@@ -130,10 +154,8 @@ MODULE_PARM_DESC(align_buffer_size,
static bool hda_snoop = true;
module_param_named(snoop, hda_snoop, bool, 0444);
MODULE_PARM_DESC(snoop, "Enable/disable snooping");
-#define azx_snoop(chip) (chip)->snoop
#else
#define hda_snoop true
-#define azx_snoop(chip) true
#endif
@@ -148,6 +170,10 @@ MODULE_SUPPORTED_DEVICE("{{Intel, ICH6},"
"{Intel, PCH},"
"{Intel, CPT},"
"{Intel, PPT},"
+ "{Intel, LPT},"
+ "{Intel, LPT_LP},"
+ "{Intel, WPT_LP},"
+ "{Intel, HPT},"
"{Intel, PBG},"
"{Intel, SCH},"
"{ATI, SB450},"
@@ -168,324 +194,22 @@ 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_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 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 assigned_key; /* last device# key 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;
- unsigned int wc_marked: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_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;
-
- /* 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 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;
-
- /* 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 */
- unsigned int snoop:1;
- unsigned int align_buffer_size:1;
-
- /* 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,
@@ -495,26 +219,29 @@ enum {
AZX_DRIVER_NVIDIA,
AZX_DRIVER_TERA,
AZX_DRIVER_CTX,
+ AZX_DRIVER_CTHDA,
AZX_DRIVER_GENERIC,
AZX_NUM_DRIVERS, /* keep this as last entry */
};
-/* 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 */
+/* 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 \
@@ -527,12 +254,27 @@ enum {
/* quirks for Nvidia */
#define AZX_DCAPS_PRESET_NVIDIA \
- (AZX_DCAPS_NVIDIA_SNOOP | AZX_DCAPS_RIRB_DELAY | AZX_DCAPS_NO_MSI)
+ (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[] __devinitdata = {
+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",
@@ -542,65 +284,53 @@ static char *driver_short_names[] __devinitdata = {
[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)
+struct hda_intel {
+ struct azx chip;
+};
+
#ifdef CONFIG_X86
-static void __mark_pages_wc(struct azx *chip, void *addr, size_t size, bool on)
+static void __mark_pages_wc(struct azx *chip, struct snd_dma_buffer *dmab, bool on)
{
+ int pages;
+
if (azx_snoop(chip))
return;
- if (addr && size) {
- int pages = (size + PAGE_SIZE - 1) >> PAGE_SHIFT;
+ if (!dmab || !dmab->area || !dmab->bytes)
+ return;
+
+#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_memory_wc((unsigned long)addr, pages);
+ set_pages_array_wc(sgbuf->page_table, sgbuf->pages);
else
- set_memory_wb((unsigned long)addr, pages);
+ set_pages_array_wb(sgbuf->page_table, sgbuf->pages);
+ return;
}
+#endif
+
+ 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);
}
static inline void mark_pages_wc(struct azx *chip, struct snd_dma_buffer *buf,
bool on)
{
- __mark_pages_wc(chip, buf->area, buf->bytes, on);
+ __mark_pages_wc(chip, buf, on);
}
static inline void mark_runtime_wc(struct azx *chip, struct azx_dev *azx_dev,
- struct snd_pcm_runtime *runtime, bool on)
+ struct snd_pcm_substream *substream, bool on)
{
if (azx_dev->wc_marked != on) {
- __mark_pages_wc(chip, runtime->dma_area, runtime->dma_bytes, on);
+ __mark_pages_wc(chip, snd_pcm_get_dma_buf(substream), on);
azx_dev->wc_marked = on;
}
}
@@ -611,531 +341,12 @@ static inline void mark_pages_wc(struct azx *chip, struct snd_dma_buffer *buf,
{
}
static inline void mark_runtime_wc(struct azx *chip, struct azx_dev *azx_dev,
- struct snd_pcm_runtime *runtime, bool on)
+ struct snd_pcm_substream *substream, bool on)
{
}
#endif
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;
- }
- mark_pages_wc(chip, &chip->rb, true);
- 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_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);
-}
-
-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;
-}
-
-#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_readb(chip, RIRBWP);
- 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 = 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);
- }
- 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);
-}
-
-/* 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(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));
-}
-
-/* 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(azx_dev, SD_STS, SD_INT_MASK);
- }
-
- /* 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)
-{
- 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)
-{
- azx_stream_clear(chip, azx_dev);
- /* disable SIE */
- azx_writel(chip, INTCTL,
- azx_readl(chip, INTCTL) & ~(1 << azx_dev->index));
-}
-
-
-/*
- * 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;
-}
/*
* initialize the PCI registers
@@ -1161,7 +372,7 @@ static void azx_init_pci(struct azx *chip)
* The PCI register TCSEL is defined in the Intel manuals.
*/
if (!(chip->driver_caps & AZX_DCAPS_NO_TCSEL)) {
- snd_printdd(SFX "Clearing TCSEL\n");
+ dev_dbg(chip->card->dev, "Clearing TCSEL\n");
update_pci_byte(chip->pci, ICH6_PCIREG_TCSEL, 0x07, 0);
}
@@ -1169,7 +380,8 @@ static void azx_init_pci(struct azx *chip)
* we need to enable snoop.
*/
if (chip->driver_caps & AZX_DCAPS_ATI_SNOOP) {
- snd_printdd(SFX "Setting ATI snoop: %d\n", azx_snoop(chip));
+ 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,
azx_snoop(chip) ? ATI_SB450_HDAUDIO_ENABLE_SNOOP : 0);
@@ -1177,7 +389,8 @@ static void azx_init_pci(struct azx *chip)
/* For NVIDIA HDA, enable snoop */
if (chip->driver_caps & AZX_DCAPS_NVIDIA_SNOOP) {
- snd_printdd(SFX "Setting Nvidia snoop: %d\n", azx_snoop(chip));
+ 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);
@@ -1202,877 +415,31 @@ static void azx_init_pci(struct azx *chip)
pci_read_config_word(chip->pci,
INTEL_SCH_HDA_DEVC, &snoop);
}
- snd_printdd(SFX "SCH snoop: %s\n",
- (snoop & INTEL_SCH_HDA_DEVC_NOSNOOP)
- ? "Disabled" : "Enabled");
+ 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_caps & AZX_DCAPS_RIRB_PRE_DELAY)
- 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)
+/* called from IRQ */
+static int azx_position_check(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;
-}
+ int ok;
-/*
- * 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(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(azx_dev, SD_CTL, val);
-
- /* 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);
+ 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);
}
-
- /* 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)
-{
- 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) {
- 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;
-}
-
-/*
- * 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_caps & AZX_DCAPS_RIRB_DELAY) {
- snd_printd(SFX "Enable delay in RIRB handling\n");
- 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);
- }
- }
- }
-
- /* 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) {
- snd_printd(SFX "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->beep_mode = chip->beep_mode;
- codecs++;
- }
- }
- if (!codecs) {
- snd_printk(KERN_ERR SFX "no codecs initialized\n");
- return -ENXIO;
- }
- return 0;
-}
-
-/* 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);
- }
- 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;
- /* 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++)
- if (!chip->azx_dev[dev].opened) {
- res = &chip->azx_dev[dev];
- if (res->assigned_key == key)
- break;
- }
- if (res) {
- res->opened = 1;
- res->assigned_key = key;
- }
- 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,
-};
-
-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);
- 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(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_pcm *apcm = snd_pcm_substream_chip(substream);
- struct azx *chip = apcm->chip;
- struct snd_pcm_runtime *runtime = substream->runtime;
- struct azx_dev *azx_dev = get_azx_dev(substream);
- int ret;
-
- mark_runtime_wc(chip, azx_dev, runtime, false);
- azx_dev->bufsize = 0;
- azx_dev->period_bytes = 0;
- azx_dev->format_val = 0;
- ret = snd_pcm_lib_malloc_pages(substream,
- params_buffer_bytes(hw_params));
- if (ret < 0)
- return ret;
- mark_runtime_wc(chip, azx_dev, runtime, true);
- 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 snd_pcm_runtime *runtime = substream->runtime;
- 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);
-
- mark_runtime_wc(chip, azx_dev, runtime, false);
- 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;
- struct hda_spdif_out *spdif =
- snd_hda_spdif_out_of_nid(apcm->codec, hinfo->nid);
- unsigned short ctls = spdif ? spdif->ctls : 0;
-
- 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) {
- 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_caps & AZX_DCAPS_CTX_WORKAROUND) &&
- 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 */
- 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) {
- 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 */
- 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);
- 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->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;
-}
-
-static unsigned int azx_get_position(struct azx *chip,
- struct azx_dev *azx_dev,
- bool with_check)
-{
- 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 (with_check && chip->position_fix[stream] == POS_FIX_AUTO) {
- if (!pos || pos == (u32)-1) {
- printk(KERN_WARNING
- "hda-intel: Invalid position buffer, "
- "using LPIB read method instead.\n");
- chip->position_fix[stream] = POS_FIX_LPIB;
- pos = azx_sd_readl(azx_dev, SD_LPIB);
- } else
- chip->position_fix[stream] = POS_FIX_POSBUF;
- }
- break;
- }
-
- 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, false));
-}
-
/*
* Check whether the current DMA position is acceptable for updating
* periods. Returns non-zero if it's OK.
@@ -2086,13 +453,11 @@ 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, true);
if (WARN_ONCE(!azx_dev->period_bytes,
@@ -2101,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 */
}
@@ -2115,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;
}
@@ -2160,137 +524,14 @@ static void azx_clear_irq_pending(struct azx *chip)
spin_unlock_irq(&chip->reg_lock);
}
-#ifdef CONFIG_X86
-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 (!azx_snoop(chip))
- area->vm_page_prot = pgprot_writecombine(area->vm_page_prot);
- return snd_pcm_lib_default_mmap(substream, area);
-}
-#else
-#define azx_pcm_mmap NULL
-#endif
-
-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,
- .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) {
- 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->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,
- snd_dma_pci_data(chip->pci),
- size, MAX_PREALLOC_SIZE);
- 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,
KBUILD_MODNAME, chip)) {
- printk(KERN_ERR "hda-intel: unable to grab IRQ %d, "
- "disabling device\n", chip->pci->irq);
+ 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;
@@ -2300,70 +541,64 @@ 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;
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);
list_for_each_entry(p, &chip->pcm_list, list)
@@ -2371,28 +606,40 @@ static int azx_suspend(struct pci_dev *pci, pm_message_t state)
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;
}
@@ -2404,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 */
@@ -2437,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++)
@@ -2460,25 +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) {
- mark_pages_wc(chip, &chip->azx_dev[i].bdl, false);
- snd_dma_free_pages(&chip->azx_dev[i].bdl);
- }
- }
- if (chip->rb.area) {
- mark_pages_wc(chip, &chip->rb, false);
- snd_dma_free_pages(&chip->rb);
- }
- if (chip->posbuf.area) {
- mark_pages_wc(chip, &chip->posbuf, false);
- 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;
}
@@ -2488,10 +953,51 @@ 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 = {
+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(0x103c, 0x306d, "HP dv3", POS_FIX_LPIB),
@@ -2509,33 +1015,34 @@ static struct snd_pci_quirk position_fix_list[] __devinitdata = {
{}
};
-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 */
if (chip->driver_caps & AZX_DCAPS_POSFIX_VIA) {
- snd_printd(SFX "Using VIACOMBO position fix\n");
+ dev_dbg(chip->card->dev, "Using VIACOMBO position fix\n");
return POS_FIX_VIACOMBO;
}
if (chip->driver_caps & AZX_DCAPS_POSFIX_LPIB) {
- snd_printd(SFX "Using LPIB position fix\n");
+ dev_dbg(chip->card->dev, "Using LPIB position fix\n");
return POS_FIX_LPIB;
}
return POS_FIX_AUTO;
@@ -2544,7 +1051,7 @@ static int __devinit check_position_fix(struct azx *chip, int fix)
/*
* 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.
*/
@@ -2558,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;
@@ -2571,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;
}
}
@@ -2583,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;
@@ -2611,22 +1124,22 @@ 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_caps & AZX_DCAPS_NO_MSI) {
- printk(KERN_INFO "hda_intel: Disabling MSI\n");
+ dev_info(chip->card->dev, "Disabling MSI\n");
chip->msi = 0;
}
}
/* check the snoop mode availability */
-static void __devinit azx_check_snoop_available(struct azx *chip)
+static void azx_check_snoop_available(struct azx *chip)
{
bool snoop = chip->snoop;
@@ -2646,28 +1159,37 @@ static void __devinit azx_check_snoop_available(struct azx *chip)
/* new ATI HDMI requires non-snoop */
snoop = false;
break;
+ case AZX_DRIVER_CTHDA:
+ snoop = false;
+ break;
}
if (snoop != chip->snoop) {
- snd_printk(KERN_INFO SFX "Force to %s mode\n",
- snoop ? "snoop" : "non-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, unsigned int driver_caps,
- 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;
@@ -2675,27 +1197,39 @@ 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_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;
@@ -2713,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 */
@@ -2725,34 +1283,29 @@ 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->pci->vendor == PCI_VENDOR_ID_ATI) {
@@ -2769,14 +1322,21 @@ static int __devinit azx_create(struct snd_card *card, struct pci_dev *pci,
/* disable 64bit DMA address on some devices */
if (chip->driver_caps & AZX_DCAPS_NO_64BIT) {
- snd_printd(SFX "Disabling 64bit DMA\n");
+ dev_dbg(card->dev, "Disabling 64bit DMA\n");
gcap &= ~ICH6_GCAP_64OK;
}
/* disable buffer size rounding to 128-byte multiples if supported */
- chip->align_buffer_size = align_buffer_size;
- if (chip->driver_caps & AZX_DCAPS_BUFSIZE)
- chip->align_buffer_size = 0;
+ 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)))
@@ -2817,53 +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;
- }
- mark_pages_wc(chip, &chip->azx_dev[i].bdl, true);
- }
- /* 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;
- }
- mark_pages_wc(chip, &chip->posbuf, true);
- /* 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");
@@ -2873,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
*/
@@ -2894,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)
@@ -2909,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) {
@@ -2956,49 +1727,85 @@ 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 | AZX_DCAPS_SCH_SNOOP |
- AZX_DCAPS_BUFSIZE },
+ .driver_data = AZX_DRIVER_PCH | AZX_DCAPS_INTEL_PCH_NOPM },
/* PBG */
{ PCI_DEVICE(0x8086, 0x1d20),
- .driver_data = AZX_DRIVER_PCH | AZX_DCAPS_SCH_SNOOP |
- AZX_DCAPS_BUFSIZE},
+ .driver_data = AZX_DRIVER_PCH | AZX_DCAPS_INTEL_PCH_NOPM },
/* Panther Point */
{ PCI_DEVICE(0x8086, 0x1e20),
- .driver_data = AZX_DRIVER_PCH | AZX_DCAPS_SCH_SNOOP |
- AZX_DCAPS_BUFSIZE},
- /* SCH */
+ .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_SCH_SNOOP |
- AZX_DCAPS_BUFSIZE | AZX_DCAPS_POSFIX_LPIB }, /* Poulsbo */
+ .driver_data = AZX_DRIVER_SCH | AZX_DCAPS_INTEL_PCH_NOPM },
+ /* Oaktrail */
{ PCI_DEVICE(0x8086, 0x080a),
- .driver_data = AZX_DRIVER_SCH | AZX_DCAPS_SCH_SNOOP |
- AZX_DCAPS_BUFSIZE | AZX_DCAPS_POSFIX_LPIB }, /* Oaktrail */
+ .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 |
@@ -3066,6 +1873,22 @@ static DEFINE_PCI_DEVICE_TABLE(azx_ids) = {
.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),
@@ -3077,6 +1900,10 @@ static DEFINE_PCI_DEVICE_TABLE(azx_ids) = {
/* VIA VT8251/VT8237A */
{ 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 */
@@ -3089,8 +1916,15 @@ static DEFINE_PCI_DEVICE_TABLE(azx_ids) = {
/* Teradici */
{ 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.
@@ -3124,26 +1958,14 @@ static DEFINE_PCI_DEVICE_TABLE(azx_ids) = {
MODULE_DEVICE_TABLE(pci, azx_ids);
/* pci_driver definition */
-static struct pci_driver driver = {
+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
index 9d819c4b492..9746d73cec5 100644
--- a/sound/pci/hda/hda_jack.c
+++ b/sound/pci/hda/hda_jack.c
@@ -17,12 +17,30 @@
#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);
@@ -30,8 +48,11 @@ static u32 read_pin_sense(struct hda_codec *codec, hda_nid_t nid)
snd_hda_codec_read(codec, nid, 0,
AC_VERB_SET_PIN_SENSE, 0);
}
- return snd_hda_codec_read(codec, nid, 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;
}
/**
@@ -50,7 +71,7 @@ snd_hda_jack_tbl_get(struct hda_codec *codec, hda_nid_t nid)
return jack;
return NULL;
}
-EXPORT_SYMBOL_HDA(snd_hda_jack_tbl_get);
+EXPORT_SYMBOL_GPL(snd_hda_jack_tbl_get);
/**
* snd_hda_jack_tbl_get_from_tag - query the jack-table entry for the given tag
@@ -68,7 +89,7 @@ snd_hda_jack_tbl_get_from_tag(struct hda_codec *codec, unsigned char tag)
return jack;
return NULL;
}
-EXPORT_SYMBOL_HDA(snd_hda_jack_tbl_get_from_tag);
+EXPORT_SYMBOL_GPL(snd_hda_jack_tbl_get_from_tag);
/**
* snd_hda_jack_tbl_new - create a jack-table entry for the given NID
@@ -79,7 +100,6 @@ 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;
- snd_array_init(&codec->jacktbl, sizeof(*jack), 16);
jack = snd_array_new(&codec->jacktbl);
if (!jack)
return NULL;
@@ -88,7 +108,7 @@ snd_hda_jack_tbl_new(struct hda_codec *codec, hda_nid_t nid)
jack->tag = codec->jacktbl.used;
return jack;
}
-EXPORT_SYMBOL_HDA(snd_hda_jack_tbl_new);
+EXPORT_SYMBOL_GPL(snd_hda_jack_tbl_new);
void snd_hda_jack_tbl_clear(struct hda_codec *codec)
{
@@ -106,13 +126,34 @@ void snd_hda_jack_tbl_clear(struct hda_codec *codec)
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 || !jack->jack_detect) {
+ 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);
- jack->jack_dirty = 0;
+
+ /* 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);
+ }
}
}
@@ -131,7 +172,7 @@ void snd_hda_jack_set_dirty_all(struct hda_codec *codec)
if (jack->nid)
jack->jack_dirty = 1;
}
-EXPORT_SYMBOL_HDA(snd_hda_jack_set_dirty_all);
+EXPORT_SYMBOL_GPL(snd_hda_jack_set_dirty_all);
/**
* snd_hda_pin_sense - execute pin sense measurement
@@ -150,29 +191,34 @@ u32 snd_hda_pin_sense(struct hda_codec *codec, hda_nid_t nid)
}
return read_pin_sense(codec, nid);
}
-EXPORT_SYMBOL_HDA(snd_hda_pin_sense);
-
-#define get_jack_plug_state(sense) !!(sense & AC_PINSENSE_PRESENCE)
+EXPORT_SYMBOL_GPL(snd_hda_pin_sense);
/**
- * snd_hda_jack_detect - query pin Presence Detect status
+ * 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.
+ * 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(struct hda_codec *codec, hda_nid_t nid)
+int snd_hda_jack_detect_state(struct hda_codec *codec, hda_nid_t nid)
{
- u32 sense = snd_hda_pin_sense(codec, nid);
- return get_jack_plug_state(sense);
+ 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_HDA(snd_hda_jack_detect);
+EXPORT_SYMBOL_GPL(snd_hda_jack_detect_state);
/**
* snd_hda_jack_detect_enable - enable the jack-detection
*/
-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)
{
struct hda_jack_tbl *jack = snd_hda_jack_tbl_new(codec, nid);
if (!jack)
@@ -182,24 +228,65 @@ int snd_hda_jack_detect_enable(struct hda_codec *codec, hda_nid_t nid,
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_HDA(snd_hda_jack_detect_enable);
+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 = codec->jacktbl.list;
+ 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) {
+ if (jack->nid)
jack_detect_update(codec, jack);
- if (!jack->kctl)
+
+ /* 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);
@@ -210,7 +297,7 @@ void snd_hda_jack_report_sync(struct hda_codec *codec)
#endif
}
}
-EXPORT_SYMBOL_HDA(snd_hda_jack_report_sync);
+EXPORT_SYMBOL_GPL(snd_hda_jack_report_sync);
#ifdef CONFIG_SND_HDA_INPUT_JACK
/* guess the jack type from the pin-config */
@@ -247,8 +334,8 @@ static void hda_free_jack_priv(struct snd_jack *jack)
* This assigns a jack-detection kctl to the given pin. The kcontrol
* will have the given name and index.
*/
-int snd_hda_jack_add_kctl(struct hda_codec *codec, hda_nid_t nid,
- const char *name, int idx)
+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;
@@ -266,47 +353,86 @@ int snd_hda_jack_add_kctl(struct hda_codec *codec, hda_nid_t nid,
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
- 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);
+ 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;
}
-EXPORT_SYMBOL_HDA(snd_hda_jack_add_kctl);
+
+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,
- char *lastname, int *lastidx)
+ const char *base_name)
{
unsigned int def_conf, conn;
- char name[44];
+ char name[SNDRV_CTL_ELEM_ID_NAME_MAXLEN];
int idx, err;
+ bool phantom_jack;
if (!nid)
return 0;
- if (!is_jack_detectable(codec, nid))
- return 0;
def_conf = snd_hda_codec_get_pincfg(codec, nid);
conn = get_defcfg_connect(def_conf);
- if (conn != AC_JACK_PORT_COMPLEX)
+ if (conn == AC_JACK_PORT_NONE)
return 0;
+ phantom_jack = (conn != AC_JACK_PORT_COMPLEX) ||
+ !is_jack_detectable(codec, nid);
- snd_hda_get_pin_label(codec, nid, cfg, name, sizeof(name), &idx);
- if (!strcmp(name, lastname) && idx == *lastidx)
- idx++;
- strncpy(lastname, name, 44);
- *lastidx = idx;
- err = snd_hda_jack_add_kctl(codec, nid, name, idx);
+ 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;
- return snd_hda_jack_detect_enable(codec, nid, 0);
+
+ if (!phantom_jack)
+ return snd_hda_jack_detect_enable(codec, nid, 0);
+ return 0;
}
/**
@@ -316,44 +442,105 @@ int snd_hda_jack_add_kctls(struct hda_codec *codec,
const struct auto_pin_cfg *cfg)
{
const hda_nid_t *p;
- int i, err, lastidx = 0;
- char lastname[44] = "";
+ 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, lastname, &lastidx);
+ 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, lastname, &lastidx);
+ 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, lastname, &lastidx);
- if (err < 0)
- return err;
- }
- for (i = 0; i < cfg->num_inputs; i++) {
- err = add_jack_kctl(codec, cfg->inputs[i].pin, cfg, lastname, &lastidx);
+ 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, lastname, &lastidx);
+ err = add_jack_kctl(codec, *p, cfg, NULL);
if (err < 0)
return err;
}
- err = add_jack_kctl(codec, cfg->dig_in_pin, cfg, lastname, &lastidx);
+ 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, lastname, &lastidx);
+ err = add_jack_kctl(codec, cfg->mono_out_pin, cfg, NULL);
if (err < 0)
return err;
return 0;
}
-EXPORT_SYMBOL_HDA(snd_hda_jack_add_kctls);
+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
index f8f97c71c9c..46e1ea83ce3 100644
--- a/sound/pci/hda/hda_jack.h
+++ b/sound/pci/hda/hda_jack.h
@@ -12,15 +12,25 @@
#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;
@@ -58,23 +68,29 @@ 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);
-int snd_hda_jack_detect(struct hda_codec *codec, hda_nid_t nid);
-static inline bool is_jack_detectable(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)
{
- if (!(snd_hda_query_pin_caps(codec, nid) & AC_PINCAP_PRES_DETECT))
- return false;
- if (!codec->ignore_misc_bit &&
- (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))
- return false;
- return true;
+ 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,
@@ -82,5 +98,8 @@ int snd_hda_jack_add_kctls(struct hda_codec *codec,
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 aca8d3193b9..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 CONFIG_PM
+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,9 +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 associated_nid,
- hda_nid_t cvt_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);
/*
@@ -236,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
@@ -318,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
@@ -352,87 +380,113 @@ int snd_hda_add_new_ctls(struct hda_codec *codec,
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 HDA_MAX_OUTS
-#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_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);
-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) \
@@ -446,19 +500,6 @@ struct auto_pin_cfg {
#define get_defcfg_misc(cfg) \
((cfg & AC_DEFCFG_MISC) >> AC_DEFCFG_MISC_SHIFT)
-/* 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 */
-
-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)
-
/* amp values */
#define AMP_IN_MUTE(idx) (0x7080 | ((idx)<<8))
#define AMP_IN_UNMUTE(idx) (0x7000 | ((idx)<<8))
@@ -476,6 +517,52 @@ int snd_hda_parse_pin_defcfg(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
*/
@@ -505,6 +592,14 @@ 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);
@@ -537,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)
@@ -570,6 +653,12 @@ 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
/*
@@ -593,6 +682,23 @@ int snd_hda_check_amp_list_power(struct hda_codec *codec,
struct hda_loopback_check *check,
hda_nid_t nid);
+/* 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
*/
@@ -602,11 +708,21 @@ int snd_hda_check_amp_list_power(struct hda_codec *codec,
#define get_amp_channels(kc) (((kc)->private_value >> 16) & 0x3)
#define get_amp_direction_(pv) (((pv) >> 18) & 0x1)
#define get_amp_direction(kc) get_amp_direction_((kc)->private_value)
-#define get_amp_index(kc) (((kc)->private_value >> 19) & 0xf)
+#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 {
@@ -626,10 +742,10 @@ struct cea_sad {
/*
* 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;
@@ -644,39 +760,44 @@ struct hdmi_eld {
int spk_alloc;
int sad_count;
struct cea_sad sad[ELD_MAX_SAD];
- /*
- * all fields above eld_buffer will be cleared before updating ELD
- */
+};
+
+struct hdmi_eld {
+ bool monitor_present;
+ bool eld_valid;
+ int eld_size;
char eld_buffer[ELD_MAX_SIZE];
-#ifdef CONFIG_PROC_FS
- struct snd_info_entry *proc_entry;
-#endif
+ 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 snd_hdmi_eld_update_pcm_info(struct hdmi_eld *eld,
+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);
+/*
+ */
+#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 254ab520460..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;
@@ -138,16 +144,17 @@ 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");
}
@@ -402,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");
@@ -412,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";
}
@@ -451,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,
@@ -472,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,
@@ -488,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 &&
@@ -505,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,
@@ -551,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)
{
@@ -589,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) {
@@ -606,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,
@@ -643,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_raw_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: ");
@@ -702,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);
@@ -711,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
index 9884871ddb0..3a1c63161eb 100644
--- a/sound/pci/hda/hda_trace.h
+++ b/sound/pci/hda/hda_trace.h
@@ -58,6 +58,7 @@ TRACE_EVENT(hda_bus_reset,
TP_printk("[%d]", __entry->card)
);
+#ifdef CONFIG_PM
DECLARE_EVENT_CLASS(hda_power,
TP_PROTO(struct hda_codec *codec),
@@ -87,6 +88,31 @@ DEFINE_EVENT(hda_power, hda_power_up,
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),
diff --git a/sound/pci/hda/patch_analog.c b/sound/pci/hda/patch_analog.c
index 9cb14b42dff..06275f8807a 100644
--- a/sound/pci/hda/patch_analog.c
+++ b/sound/pci/hda/patch_analog.c
@@ -20,169 +20,29 @@
*/
#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 {
- const 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;
-
- const hda_nid_t *alt_dac_nid;
- const struct hda_pcm_stream *stream_analog_alt_playback;
- int independent_hp;
- int num_active_streams;
-
- /* capture */
- unsigned int num_adc_nids;
- const hda_nid_t *adc_nids;
- hda_nid_t dig_in_nid; /* digital-in NID; optional */
-
- /* capture source */
- const struct hda_input_mux *input_mux;
- const 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;
-}
-
-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
-};
+struct ad198x_spec {
+ struct hda_gen_spec gen;
-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
-};
+ /* for auto parser */
+ int smux_paths[4];
+ unsigned int cur_smux;
+ hda_nid_t eapd_nid;
-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 */
@@ -192,435 +52,48 @@ static const struct snd_kcontrol_new ad_beep_mixer[] = {
{ } /* end */
};
-static const 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,
- 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) {
- const 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
-
- /* 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 */
+ const struct snd_kcontrol_new *knew;
- /* 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
-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;
- ctl->vd[0].access &= ~SNDRV_CTL_ELEM_ACCESS_WRITE;
- ctl->vd[0].access |= active ?
- SNDRV_CTL_ELEM_ACCESS_WRITE : 0;
- snd_ctl_notify(codec->bus->card,
- SNDRV_CTL_EVENT_MASK_INFO, &ctl->id);
- }
-}
-
-static void set_stream_active(struct hda_codec *codec, bool active)
-{
- struct ad198x_spec *spec = codec->spec;
- if (active)
- spec->num_active_streams++;
- else
- spec->num_active_streams--;
- activate_ctl(codec, "Independent HP", spec->num_active_streams == 0);
-}
-
-static int ad1988_independent_hp_info(struct snd_kcontrol *kcontrol,
- struct snd_ctl_elem_info *uinfo)
-{
- static const char * const texts[] = { "OFF", "ON", NULL};
- int index;
- uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED;
- uinfo->count = 1;
- uinfo->value.enumerated.items = 2;
- index = uinfo->value.enumerated.item;
- if (index >= 2)
- index = 1;
- strcpy(uinfo->value.enumerated.name, texts[index]);
- return 0;
-}
-
-static int ad1988_independent_hp_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->independent_hp;
- return 0;
-}
-
-static int ad1988_independent_hp_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 select = ucontrol->value.enumerated.item[0];
- if (spec->independent_hp != select) {
- spec->independent_hp = select;
- if (spec->independent_hp)
- spec->multiout.hp_nid = 0;
- else
- spec->multiout.hp_nid = spec->alt_dac_nid[0];
- return 1;
- }
- return 0;
-}
-
-/*
- * 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;
- int err;
- set_stream_active(codec, true);
- err = snd_hda_multi_out_analog_open(codec, &spec->multiout, substream,
- hinfo);
- if (err < 0) {
- set_stream_active(codec, false);
- return err;
- }
- return 0;
-}
-
-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 int ad198x_playback_pcm_close(struct hda_pcm_stream *hinfo,
- struct hda_codec *codec,
- struct snd_pcm_substream *substream)
-{
- set_stream_active(codec, false);
- return 0;
-}
-
-static int ad1988_alt_playback_pcm_open(struct hda_pcm_stream *hinfo,
- struct hda_codec *codec,
- struct snd_pcm_substream *substream)
-{
- struct ad198x_spec *spec = codec->spec;
- if (!spec->independent_hp)
- return -EBUSY;
- set_stream_active(codec, true);
- return 0;
-}
-
-static int ad1988_alt_playback_pcm_close(struct hda_pcm_stream *hinfo,
- struct hda_codec *codec,
- struct snd_pcm_substream *substream)
-{
- set_stream_active(codec, false);
- return 0;
-}
-
-static const struct hda_pcm_stream ad198x_pcm_analog_alt_playback = {
- .substreams = 1,
- .channels_min = 2,
- .channels_max = 2,
- .ops = {
- .open = ad1988_alt_playback_pcm_open,
- .close = ad1988_alt_playback_pcm_close
- },
-};
-
-/*
- * 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 const 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,
- .close = ad198x_playback_pcm_close
- },
-};
-
-static const 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 const 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 const 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 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;
if (snd_hda_query_pin_caps(codec, front) & AC_PINCAP_EAPD)
snd_hda_codec_write(codec, front, 0, AC_VERB_SET_EAPD_BTLENABLE,
- !spec->inv_eapd ? 0x00 : 0x02);
+ !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,
- !spec->inv_eapd ? 0x00 : 0x02);
+ !codec->inv_eapd ? 0x00 : 0x02);
}
static void ad198x_power_eapd(struct hda_codec *codec)
@@ -657,965 +130,395 @@ static void ad198x_shutup(struct hda_codec *codec)
ad198x_power_eapd(codec);
}
-static void ad198x_free(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);
-}
-
#ifdef CONFIG_PM
-static int ad198x_suspend(struct hda_codec *codec, pm_message_t state)
+static int ad198x_suspend(struct hda_codec *codec)
{
ad198x_shutup(codec);
return 0;
}
#endif
-static const 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 CONFIG_PM
- .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 const hda_nid_t ad1986a_dac_nids[3] = {
- AD1986A_FRONT_DAC, AD1986A_SURR_DAC, AD1986A_CLFE_DAC
-};
-static const hda_nid_t ad1986a_adc_nids[1] = { AD1986A_ADC };
-static const hda_nid_t ad1986a_capsrc_nids[1] = { 0x12 };
-
-static const 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 const 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 const 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 const 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 const 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 const hda_nid_t ad1986a_laptop_dac_nids[1] = { AD1986A_FRONT_DAC };
-
-/* master controls both pins 0x1a and 0x1b */
-static const 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 const 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 const 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 const 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 const 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 const 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 const 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 const 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 const 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 const 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 const 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 const 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 const 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 const struct hda_channel_mode ad1986a_modes[3] = {
- { 2, ad1986a_ch2_init },
- { 4, ad1986a_ch4_init },
- { 6, ad1986a_ch6_init },
-};
-
-/* eapd initialization */
-static const struct hda_verb ad1986a_eapd_init_verbs[] = {
- {0x1b, AC_VERB_SET_EAPD_BTLENABLE, 0x00 },
- {}
-};
-
-static const 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 const 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 const 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 const 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 const 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 const hda_nid_t ad1983_dac_nids[1] = { AD1983_DAC };
-static const hda_nid_t ad1983_adc_nids[1] = { AD1983_ADC };
-static const hda_nid_t ad1983_capsrc_nids[1] = { 0x15 };
-
-static const 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 const char * const 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 const 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 const 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 const 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;
}
@@ -1623,455 +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 const hda_nid_t ad1981_dac_nids[1] = { AD1981_DAC };
-static const hda_nid_t ad1981_adc_nids[1] = { AD1981_ADC };
-static const hda_nid_t ad1981_capsrc_nids[1] = { 0x15 };
-
-/* 0x0c, 0x09, 0x0e, 0x0f, 0x19, 0x05, 0x18, 0x17 */
-static const 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 const 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 const 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 const 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 const 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 const 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 const struct hda_verb mic_jack_on[] = {
- {0x1f, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080},
- {0x1e, AC_VERB_SET_AMP_GAIN_MUTE, 0xb000},
- {}
- };
- static const 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 const struct hda_input_mux ad1981_hp_capture_source = {
- .num_items = 3,
- .items = {
- { "Mic", 0x0 },
- { "Docking-Station", 0x1 },
- { "Mix", 0x2 },
- },
-};
-
-static const 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 const 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 const 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 const 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,
- },
- /* 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,
+static const struct hda_fixup ad1981_fixups[] = {
+ [AD1981_FIXUP_AMP_OVERRIDE] = {
+ .type = HDA_FIXUP_FUNC,
+ .v.func = ad1981_fixup_amp_override,
},
- { } /* end */
-};
-
-static const struct hda_input_mux ad1981_thinkpad_capture_source = {
- .num_items = 3,
- .items = {
- { "Mic", 0x0 },
- { "Mix", 0x2 },
- { "CD", 0x4 },
+ [AD1981_FIXUP_HP_EAPD] = {
+ .type = HDA_FIXUP_FUNC,
+ .v.func = ad1981_fixup_hp_eapd,
+ .chained = true,
+ .chain_id = AD1981_FIXUP_AMP_OVERRIDE,
},
};
-/* 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 const 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;
- if (!is_jack_available(codec, 0x0a))
- 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;
}
@@ -2160,89 +697,7 @@ static int patch_ad1981(struct hda_codec *codec)
* E/F quad mic array
*/
-
-/* models */
-enum {
- AD1988_6STACK,
- AD1988_6STACK_DIG,
- 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 const hda_nid_t ad1988_6stack_dac_nids[4] = {
- 0x04, 0x06, 0x05, 0x0a
-};
-
-static const hda_nid_t ad1988_3stack_dac_nids[3] = {
- 0x04, 0x05, 0x0a
-};
-
-/* for AD1988A revision-2, DAC2-4 are swapped */
-static const hda_nid_t ad1988_6stack_dac_nids_rev2[4] = {
- 0x04, 0x05, 0x0a, 0x06
-};
-
-static const hda_nid_t ad1988_alt_dac_nid[1] = {
- 0x03
-};
-
-static const hda_nid_t ad1988_3stack_dac_nids_rev2[3] = {
- 0x04, 0x0a, 0x06
-};
-
-static const hda_nid_t ad1988_adc_nids[3] = {
- 0x08, 0x09, 0x0f
-};
-
-static const 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 const hda_nid_t ad1989b_slave_dig_outs[] = {
- AD1988_SPDIF_OUT, AD1988_SPDIF_OUT_HDMI, 0
-};
-
-static const 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 const 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)
{
@@ -2273,1169 +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 */
-static const struct snd_kcontrol_new ad1988_hp_mixers[] = {
- {
- .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
- .name = "Independent HP",
- .info = ad1988_independent_hp_info,
- .get = ad1988_independent_hp_get,
- .put = ad1988_independent_hp_put,
- },
- { } /* end */
-};
-
-/* 6-stack mode */
-static const 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 const 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 const struct snd_kcontrol_new ad1988_6stack_mixers2[] = {
- HDA_CODEC_VOLUME("Headphone Playback Volume", 0x03, 0x0, HDA_OUTPUT),
- 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 */
-};
-
-/* 3-stack mode */
-static const 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 const 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 const struct snd_kcontrol_new ad1988_3stack_mixers2[] = {
- HDA_CODEC_VOLUME("Headphone Playback Volume", 0x03, 0x0, HDA_OUTPUT),
- 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 const struct snd_kcontrol_new ad1988_laptop_mixers[] = {
- HDA_CODEC_VOLUME("Headphone Playback Volume", 0x03, 0x0, HDA_OUTPUT),
- 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 const 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)
{
+ struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
static const char * const texts[] = {
- "PCM", "ADC1", "ADC2", "ADC3"
+ "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 const 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 const struct snd_kcontrol_new ad1988_spdif_in_mixers[] = {
- HDA_CODEC_VOLUME("IEC958 Capture Volume", 0x1c, 0x0, HDA_INPUT),
- { } /* end */
-};
-
-static const 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 const 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, 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},
- /* 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 const 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 const 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 const 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 const 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 const 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 const 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 const 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 const struct hda_channel_mode ad1988_3stack_modes[2] = {
- { 2, ad1988_3stack_ch2_init },
- { 6, ad1988_3stack_ch6_init },
-};
-
-static const 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, 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},
- /* 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 const 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 const 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 const 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, 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},
- /* 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 const 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 const 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 const 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 const hda_nid_t idx_to_dac[8] = {
- /* A B C D E F G H */
- 0x03, 0x06, 0x05, 0x04, 0x0a, 0x06, 0x05, 0x0a
- };
- static const hda_nid_t idx_to_dac_rev2[8] = {
- /* A B C D E F G H */
- 0x03, 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 const 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 const 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 const 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 const 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->private_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)
- 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;
- }
- 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)
+ err = snd_hda_gen_init(codec);
+ if (err < 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;
-}
+ if (!spec->gen.autocfg.dig_outs)
+ 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)
-{
- 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;
+ 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);
}
- 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;
}
-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 03 */
- snd_hda_codec_write(codec, 0x37, 0, AC_VERB_SET_CONNECT_SEL, 0x00);
- 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;
- }
-}
-
-static void ad1988_auto_init_multi_out(struct hda_codec *codec)
+static int ad1988_add_spdif_mux_ctl(struct hda_codec *codec)
{
struct ad198x_spec *spec = codec->spec;
- int i;
+ 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 },
+ },
+ };
- 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);
- }
-}
+ /* 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_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);
-}
+ num_conns = snd_hda_get_num_conns(codec, 0x0b) + 1;
+ if (num_conns != 3 && num_conns != 4)
+ return 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;
- int type = cfg->inputs[i].type;
- 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,
- type == 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);
+ 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);
}
-}
-
-/* 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_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 const 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, 0x82c0, "Asus M3N-HT Deluxe", 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);
-
- if (!spec->multiout.hp_nid)
- spec->multiout.hp_nid = ad1988_alt_dac_nid[0];
- switch (board_config) {
- case AD1988_6STACK:
- case AD1988_6STACK_DIG:
- 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) {
- 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 = codec->spec;
- if (spec->autocfg.hp_pins[0]) {
- spec->mixers[spec->num_mixers++] = ad1988_hp_mixers;
- 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;
- }
+ 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;
}
@@ -3451,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 const hda_nid_t ad1884_dac_nids[1] = {
- 0x04,
-};
-
-static const hda_nid_t ad1884_adc_nids[2] = {
- 0x08, 0x09,
-};
-
-static const hda_nid_t ad1884_capsrc_nids[2] = {
- 0x0c, 0x0d,
-};
-
-#define AD1884_SPDIF_OUT 0x02
-
-static const struct hda_input_mux ad1884_capture_source = {
- .num_items = 4,
- .items = {
- { "Front Mic", 0x0 },
- { "Mic", 0x1 },
- { "CD", 0x2 },
- { "Mix", 0x3 },
- },
-};
-
-static const 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 const 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 const 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 const 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 const 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 const struct hda_input_mux ad1984_dell_desktop_capture_source = {
- .num_items = 3,
- .items = {
- { "Front Mic", 0x0 },
- { "Line-In", 0x1 },
- { "Mix", 0x3 },
- },
-};
-
-
-static const 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 const 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 const 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 const 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 const 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
@@ -3897,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 const 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 const struct hda_input_mux ad1884a_capture_source = {
- .num_items = 5,
- .items = {
- { "Front Mic", 0x0 },
- { "Mic", 0x4 },
- { "Line", 0x1 },
- { "CD", 0x2 },
- { "Mix", 0x3 },
- },
-};
-
-static const 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 const 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 const 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 const 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 const 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)
-{
- 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)
+static void ad1884_fixup_thinkpad(struct hda_codec *codec,
+ const struct hda_fixup *fix, int action)
{
- 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 const 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 const 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 const 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 const 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 const 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 const 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 const 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 const 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 const 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 const 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
@@ -4729,331 +1130,31 @@ static int patch_ad1884a(struct hda_codec *codec)
* port-G - rear clfe-out (6stack)
*/
-static const hda_nid_t ad1882_dac_nids[3] = {
- 0x04, 0x03, 0x05
-};
-
-static const hda_nid_t ad1882_adc_nids[2] = {
- 0x08, 0x09,
-};
-
-static const hda_nid_t ad1882_capsrc_nids[2] = {
- 0x0c, 0x0d,
-};
-
-#define AD1882_SPDIF_OUT 0x02
-
-/* list: 0x11, 0x39, 0x3a, 0x18, 0x3c, 0x3b, 0x12, 0x20 */
-static const 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 const struct hda_input_mux ad1882a_capture_source = {
- .num_items = 5,
- .items = {
- { "Front Mic", 0x1 },
- { "Mic", 0x4},
- { "Line", 0x2 },
- { "Digital Mic", 0x06 },
- { "Mix", 0x7 },
- },
-};
-
-static const 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 const 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 const 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 const 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 const 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 const 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 const 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 const 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 const struct hda_channel_mode ad1882_modes[3] = {
- { 2, ad1882_ch2_init },
- { 4, ad1882_ch4_init },
- { 6, ad1882_ch6_init },
-};
-
-/*
- * initialization verbs
- */
-static const 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 const 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;
}
@@ -5061,15 +1162,15 @@ static int patch_ad1882(struct hda_codec *codec)
* patch entries
*/
static const struct hda_codec_preset snd_hda_preset_analog[] = {
- { .id = 0x11d4184a, .name = "AD1884A", .patch = patch_ad1884a },
+ { .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 09ccfabb4a1..5e65999e0d8 100644
--- a/sound/pci/hda/patch_ca0110.c
+++ b/sound/pci/hda/patch_ca0110.c
@@ -19,510 +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;
- char input_labels[AUTO_PIN_LAST][32];
- 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 const char * const 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 const char * const 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,
- 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 const 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 const 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 const 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_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 const struct hda_codec_ops ca0110_patch_ops = {
- .build_controls = ca0110_build_controls,
- .build_pcms = ca0110_build_pcms,
- .init = ca0110_init,
- .free = ca0110_free,
+ .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 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;
- snd_hda_get_pin_label(codec, pin, cfg,
- spec->input_labels[n],
- sizeof(spec->input_labels[n]), NULL);
- 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);
@@ -534,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;
}
diff --git a/sound/pci/hda/patch_ca0132.c b/sound/pci/hda/patch_ca0132.c
index 21d91d580da..092f2bd030b 100644
--- a/sound/pci/hda/patch_ca0132.c
+++ b/sound/pci/hda/patch_ca0132.c
@@ -24,18 +24,454 @@
#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/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 WUH_MEM_CONNID 10
-#define DSP_MEM_CONNID 16
+#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 */
@@ -61,7 +497,11 @@ enum hda_cmd_vendor_io {
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,
@@ -69,18 +509,27 @@ enum hda_cmd_vendor_io {
VENDOR_CHIPIO_8051_ADDRESS_HIGH = 0x70E,
VENDOR_CHIPIO_FLAG_SET = 0x70F,
VENDOR_CHIPIO_FLAGS_GET = 0xF0F,
- VENDOR_CHIPIO_PARAMETER_SET = 0x710,
- VENDOR_CHIPIO_PARAMETER_GET = 0xF10,
+ 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_PARAMETER_EX_ID_GET = 0xF17,
- VENDOR_CHIPIO_PARAMETER_EX_ID_SET = 0x717,
- VENDOR_CHIPIO_PARAMETER_EX_VALUE_GET = 0xF18,
- VENDOR_CHIPIO_PARAMETER_EX_VALUE_SET = 0x718
+ 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
};
/*
@@ -130,7 +579,7 @@ enum control_flag_id {
/* 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_10K0HM_LOAD = 21,
+ 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 */
@@ -144,9 +593,17 @@ enum control_flag_id {
/*
* Control parameter IDs
*/
-enum control_parameter_id {
+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 */
@@ -224,117 +681,118 @@ enum ca0132_sample_rate {
SR_RATE_UNKNOWN = 0x1F
};
-/*
- * Scp Helper function
- */
-enum get_set {
- IS_SET = 0,
- IS_GET = 1,
+enum dsp_download_state {
+ DSP_DOWNLOAD_FAILED = -1,
+ DSP_DOWNLOAD_INIT = 0,
+ DSP_DOWNLOADING = 1,
+ DSP_DOWNLOADED = 2
};
-/*
- * Duplicated from ca0110 codec
- */
-
-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 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)
-#define add_in_mono_switch(codec, nid, pfx, chan) \
- _add_switch(codec, nid, pfx, chan, 1)
-#define add_in_mono_volume(codec, nid, pfx, chan) \
- _add_volume(codec, nid, pfx, chan, 1)
-
+/* 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];
- hda_nid_t hp_dac;
+ 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;
- long curr_hp_switch;
- long curr_hp_volume[2];
- long curr_speaker_switch;
- struct mutex chipio_mutex;
- const char *input_labels[AUTO_PIN_LAST];
- struct hda_pcm pcm_rec[2]; /* PCM information */
+ 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;
- int retry = 50;
+ unsigned long timeout = jiffies + msecs_to_jiffies(1000);
/* send bits of data specified by reg */
do {
@@ -342,7 +800,9 @@ static int chipio_send(struct hda_codec *codec,
reg, data);
if (res == VENDOR_STATUS_CHIPIO_OK)
return 0;
- } while (--retry);
+ msleep(20);
+ } while (time_before(jiffies, timeout));
+
return -EIO;
}
@@ -352,8 +812,12 @@ static int chipio_send(struct hda_codec *codec,
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);
@@ -364,15 +828,17 @@ static int chipio_write_address(struct hda_codec *codec,
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 */
@@ -384,14 +850,40 @@ static int chipio_write_data(struct hda_codec *codec, unsigned int data)
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 */
@@ -409,6 +901,10 @@ static int chipio_read_data(struct hda_codec *codec, unsigned int *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;
}
@@ -439,6 +935,30 @@ exit:
}
/*
+ * 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
*/
@@ -465,105 +985,1683 @@ exit:
}
/*
- * PCM stuffs
+ * Set chip control flags through the chip I/O widget.
*/
-static void ca0132_setup_stream(struct hda_codec *codec, hda_nid_t nid,
- u32 stream_tag,
- int channel_id, int format)
+static void chipio_set_control_flag(struct hda_codec *codec,
+ enum control_flag_id flag_id,
+ bool flag_state)
{
- unsigned int oldval, newval;
+ unsigned int val;
+ unsigned int flag_bit;
- if (!nid)
- return;
+ 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);
+}
- snd_printdd("ca0132_setup_stream: "
- "NID=0x%x, stream=0x%x, channel=%d, format=0x%x\n",
- nid, stream_tag, channel_id, format);
+/*
+ * 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;
- /* update the format-id if changed */
- oldval = snd_hda_codec_read(codec, nid, 0,
- AC_VERB_GET_STREAM_FORMAT,
- 0);
- if (oldval != format) {
+ 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);
- snd_hda_codec_write(codec, nid, 0,
- AC_VERB_SET_STREAM_FORMAT,
- format);
+ } 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++;
}
- oldval = snd_hda_codec_read(codec, nid, 0, AC_VERB_GET_CONV, 0);
- newval = (stream_tag << 4) | channel_id;
- if (oldval != newval) {
- snd_hda_codec_write(codec, nid, 0,
- AC_VERB_SET_CHANNEL_STREAMID,
- newval);
+ 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;
}
-static void ca0132_cleanup_stream(struct hda_codec *codec, hda_nid_t nid)
+/*
+ * 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)
{
- snd_hda_codec_write(codec, nid, 0, AC_VERB_SET_STREAM_FORMAT, 0);
- snd_hda_codec_write(codec, nid, 0, AC_VERB_SET_CHANNEL_STREAMID, 0);
+ 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;
}
/*
- * PCM callbacks
+ * Extract corresponding fields from SCP header
*/
-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)
+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;
- ca0132_setup_stream(codec, spec->dacs[0], stream_tag, 0, format);
+ 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;
}
-static int ca0132_playback_pcm_cleanup(struct hda_pcm_stream *hinfo,
- struct hda_codec *codec,
- struct snd_pcm_substream *substream)
+/*
+ * Reset the DSP
+ */
+static int dsp_reset(struct hda_codec *codec)
{
- struct ca0132_spec *spec = codec->spec;
+ 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);
- ca0132_cleanup_stream(codec, spec->dacs[0]);
+ if (!retry) {
+ codec_dbg(codec, "dsp_reset timeout\n");
+ return -EIO;
+ }
return 0;
}
/*
- * Digital out
+ * Convert chip address to DSP address
*/
-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)
+static unsigned int dsp_chip_to_dsp_addx(unsigned int chip_addx,
+ bool *code, bool *yram)
{
- struct ca0132_spec *spec = codec->spec;
+ *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;
- ca0132_setup_stream(codec, spec->dig_out, stream_tag, 0, format);
+ 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;
}
-static int ca0132_dig_playback_pcm_cleanup(struct hda_pcm_stream *hinfo,
- struct hda_codec *codec,
- struct snd_pcm_substream *substream)
+/*
+ * 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;
+}
- ca0132_cleanup_stream(codec, spec->dig_out);
+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));
+}
+
/*
- * Analog capture
+ * CA0132 chip DSP transfer stuffs. For DSP download.
*/
-static int ca0132_capture_pcm_prepare(struct hda_pcm_stream *hinfo,
+#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,
@@ -571,444 +2669,1881 @@ static int ca0132_capture_pcm_prepare(struct hda_pcm_stream *hinfo,
{
struct ca0132_spec *spec = codec->spec;
- ca0132_setup_stream(codec, spec->adcs[substream->number],
- stream_tag, 0, format);
+ snd_hda_codec_setup_stream(codec, spec->dacs[0], stream_tag, 0, format);
return 0;
}
-static int ca0132_capture_pcm_cleanup(struct hda_pcm_stream *hinfo,
+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;
- ca0132_cleanup_stream(codec, spec->adcs[substream->number]);
+ 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 capture
+ * Digital out
*/
-static int ca0132_dig_capture_pcm_prepare(struct hda_pcm_stream *hinfo,
+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);
+}
- ca0132_setup_stream(codec, spec->dig_in, stream_tag, 0, format);
+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_dig_capture_pcm_cleanup(struct hda_pcm_stream *hinfo,
+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;
- ca0132_cleanup_stream(codec, spec->dig_in);
+ 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.
*/
-static struct hda_pcm_stream ca0132_pcm_analog_playback = {
- .substreams = 1,
- .channels_min = 2,
- .channels_max = 2,
- .ops = {
- .prepare = ca0132_playback_pcm_prepare,
- .cleanup = ca0132_playback_pcm_cleanup
- },
-};
-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
- },
+/*
+ * 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 struct hda_pcm_stream ca0132_pcm_digital_playback = {
- .substreams = 1,
- .channels_min = 2,
- .channels_max = 2,
- .ops = {
- .prepare = ca0132_dig_playback_pcm_prepare,
- .cleanup = ca0132_dig_playback_pcm_cleanup
- },
+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 struct hda_pcm_stream ca0132_pcm_digital_capture = {
- .substreams = 1,
- .channels_min = 2,
- .channels_max = 2,
- .ops = {
- .prepare = ca0132_dig_capture_pcm_prepare,
- .cleanup = ca0132_dig_capture_pcm_cleanup
- },
+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 ca0132_build_pcms(struct hda_codec *codec)
+static int tuning_ctl_set(struct hda_codec *codec, hda_nid_t nid,
+ unsigned int *lookup, int idx)
{
- struct ca0132_spec *spec = codec->spec;
- struct hda_pcm *info = spec->pcm_rec;
+ int i = 0;
- codec->pcm_info = info;
- codec->num_pcms = 0;
+ for (i = 0; i < TUNING_CTLS_COUNT; i++)
+ if (nid == ca0132_tuning_ctls[i].nid)
+ break;
- 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 = spec->num_inputs;
- info->stream[SNDRV_PCM_STREAM_CAPTURE].nid = spec->adcs[0];
- codec->num_pcms++;
+ 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);
- if (!spec->dig_out && !spec->dig_in)
- return 0;
+ return 1;
+}
- 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++;
+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;
}
-#define REG_CODEC_MUTE 0x18b014
-#define REG_CODEC_HP_VOL_L 0x18b070
-#define REG_CODEC_HP_VOL_R 0x18b074
+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;
-static int ca0132_hp_switch_get(struct snd_kcontrol *kcontrol,
+ 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;
- *valp = spec->curr_hp_switch;
return 0;
}
-static int ca0132_hp_switch_put(struct snd_kcontrol *kcontrol,
+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;
- unsigned int data;
- int err;
+ int idx;
+ idx = nid - TUNING_CTL_START_NID;
/* any change? */
- if (spec->curr_hp_switch == *valp)
+ if (spec->cur_ctl_vals[idx] == *valp)
return 0;
- snd_hda_power_up(codec);
+ spec->cur_ctl_vals[idx] = *valp;
- err = chipio_read(codec, REG_CODEC_MUTE, &data);
- if (err < 0)
- goto exit;
+ idx = *valp;
+ tuning_ctl_set(codec, nid, mic_svm_vals_lookup, idx);
- /* *valp 0 is mute, 1 is unmute */
- data = (data & 0x7f) | (*valp ? 0 : 0x80);
- err = chipio_write(codec, REG_CODEC_MUTE, data);
- if (err < 0)
- goto exit;
+ return 0;
+}
- spec->curr_hp_switch = *valp;
+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;
- exit:
- snd_hda_power_down(codec);
- return err < 0 ? err : 1;
+ return 0;
}
-static int ca0132_speaker_switch_get(struct snd_kcontrol *kcontrol,
- struct snd_ctl_elem_value *ucontrol)
+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;
+ }
- *valp = spec->curr_speaker_switch;
return 0;
}
-static int ca0132_speaker_switch_put(struct snd_kcontrol *kcontrol,
- struct snd_ctl_elem_value *ucontrol)
+static void ca0132_init_tuning_defaults(struct hda_codec *codec)
{
- struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
struct ca0132_spec *spec = codec->spec;
- long *valp = ucontrol->value.integer.value;
- unsigned int data;
+ 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;
- /* any change? */
- if (spec->curr_speaker_switch == *valp)
+ 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);
- err = chipio_read(codec, REG_CODEC_MUTE, &data);
- if (err < 0)
- goto exit;
+ 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);
- /* *valp 0 is mute, 1 is unmute */
- data = (data & 0xef) | (*valp ? 0 : 0x10);
- err = chipio_write(codec, REG_CODEC_MUTE, data);
if (err < 0)
- goto exit;
+ return 0; /* no changed */
- spec->curr_speaker_switch = *valp;
+ return 1;
+}
- exit:
- snd_hda_power_down(codec);
- return err < 0 ? err : 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_hp_volume_get(struct snd_kcontrol *kcontrol,
+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;
- long *valp = ucontrol->value.integer.value;
+ 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;
+ }
- *valp++ = spec->curr_hp_volume[0];
- *valp = spec->curr_hp_volume[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_hp_volume_put(struct snd_kcontrol *kcontrol,
+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;
- long left_vol, right_vol;
- unsigned int data;
- int val;
- int err;
- left_vol = *valp++;
- right_vol = *valp;
+ /* 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;
+ }
- /* any change? */
- if ((spec->curr_hp_volume[0] == left_vol) &&
- (spec->curr_hp_volume[1] == right_vol))
+ /* 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;
+ }
- err = chipio_read(codec, REG_CODEC_HP_VOL_L, &data);
- if (err < 0)
+ /* PE */
+ if (nid == PLAY_ENHANCEMENT) {
+ spec->effects_switch[nid - EFFECT_START_NID] = *valp;
+ changed = ca0132_pe_switch_set(codec);
goto exit;
+ }
- val = 31 - left_vol;
- data = (data & 0xe0) | val;
- err = chipio_write(codec, REG_CODEC_HP_VOL_L, data);
- if (err < 0)
+ /* CrystalVoice */
+ if (nid == CRYSTAL_VOICE) {
+ spec->effects_switch[nid - EFFECT_START_NID] = *valp;
+ changed = ca0132_cvoice_switch_set(codec);
goto exit;
+ }
- val = 31 - right_vol;
- data = (data & 0xe0) | val;
- err = chipio_write(codec, REG_CODEC_HP_VOL_R, data);
- if (err < 0)
+ /* 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;
+ }
- spec->curr_hp_volume[0] = left_vol;
- spec->curr_hp_volume[1] = right_vol;
+ /* mic boost */
+ if (nid == spec->input_pins[0]) {
+ spec->cur_mic_boost = *valp;
- exit:
+ /* 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 err < 0 ? err : 1;
+ return changed;
}
-static int add_hp_switch(struct hda_codec *codec, hda_nid_t nid)
+/*
+ * Volume related
+ */
+static int ca0132_volume_info(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_info *uinfo)
{
- struct snd_kcontrol_new knew =
- HDA_CODEC_MUTE_MONO("Headphone Playback Switch",
- nid, 1, 0, HDA_OUTPUT);
- knew.get = ca0132_hp_switch_get;
- knew.put = ca0132_hp_switch_put;
- return snd_hda_ctl_add(codec, nid, snd_ctl_new1(&knew, codec));
+ 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 add_hp_volume(struct hda_codec *codec, hda_nid_t nid)
+static int ca0132_volume_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
{
- struct snd_kcontrol_new knew =
- HDA_CODEC_VOLUME_MONO("Headphone Playback Volume",
- nid, 3, 0, HDA_OUTPUT);
- knew.get = ca0132_hp_volume_get;
- knew.put = ca0132_hp_volume_put;
- return snd_hda_ctl_add(codec, nid, snd_ctl_new1(&knew, codec));
+ 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 add_speaker_switch(struct hda_codec *codec, hda_nid_t nid)
+static int ca0132_volume_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
{
- struct snd_kcontrol_new knew =
- HDA_CODEC_MUTE_MONO("Speaker Playback Switch",
- nid, 1, 0, HDA_OUTPUT);
- knew.get = ca0132_speaker_switch_get;
- knew.put = ca0132_speaker_switch_put;
- return snd_hda_ctl_add(codec, nid, snd_ctl_new1(&knew, codec));
+ 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 void ca0132_fix_hp_caps(struct hda_codec *codec)
+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;
- struct auto_pin_cfg *cfg = &spec->autocfg;
- unsigned int caps;
+ 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));
+}
- /* set mute-capable, 1db step, 32 steps, ofs 6 */
- caps = 0x80031f06;
- snd_hda_override_amp_caps(codec, cfg->hp_pins[0], HDA_OUTPUT, caps);
+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;
- struct auto_pin_cfg *cfg = &spec->autocfg;
- int i, err;
+ int i, num_fx;
+ int err = 0;
- if (spec->multiout.num_dacs) {
- err = add_speaker_switch(codec, spec->out_pins[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;
}
- if (cfg->hp_outs) {
- ca0132_fix_hp_caps(codec);
- err = add_hp_switch(codec, cfg->hp_pins[0]);
- if (err < 0)
- return err;
- err = add_hp_volume(codec, cfg->hp_pins[0]);
+ /* 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;
}
- for (i = 0; i < spec->num_inputs; i++) {
- const char *label = spec->input_labels[i];
+ err = add_fx_switch(codec, PLAY_ENHANCEMENT, "PlayEnhancement", 0);
+ if (err < 0)
+ return err;
- err = add_in_switch(codec, spec->adcs[i], label);
- if (err < 0)
- return err;
- err = add_in_volume(codec, spec->adcs[i], label);
- if (err < 0)
- return err;
- if (cfg->inputs[i].type == AUTO_PIN_MIC) {
- /* add Mic-Boost */
- err = add_in_mono_volume(codec, spec->input_pins[i],
- "Mic Boost", 1);
- 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 = add_out_volume(codec, spec->dig_out, "IEC958");
+ 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");
- 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 void ca0132_set_ct_ext(struct hda_codec *codec, int enable)
+static int ca0132_build_pcms(struct hda_codec *codec)
{
- /* Set Creative extension */
- snd_printdd("SET CREATIVE EXTENSION\n");
+ 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_CT_EXTENSIONS_ENABLE,
- enable);
- msleep(20);
+ 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;
-static void ca0132_config(struct hda_codec *codec)
+ 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;
- struct auto_pin_cfg *cfg = &spec->autocfg;
+ unsigned int tmp;
+ int num_fx;
+ int idx, i;
- /* line-outs */
- cfg->line_outs = 1;
- cfg->line_out_pins[0] = 0x0b; /* front */
- cfg->line_out_type = AUTO_PIN_LINE_OUT;
+ if (spec->dsp_state != DSP_DOWNLOADED)
+ return;
- spec->dacs[0] = 0x02;
- spec->out_pins[0] = 0x0b;
- spec->multiout.dac_nids = spec->dacs;
- spec->multiout.num_dacs = 1;
- spec->multiout.max_channels = 2;
+ /* 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]);
+ }
+ }
- /* headphone */
- cfg->hp_outs = 1;
- cfg->hp_pins[0] = 0x0f;
+ /*remove DSP headroom*/
+ tmp = FLOAT_ZERO;
+ dspio_set_uint_param(codec, 0x96, 0x3C, tmp);
- spec->hp_dac = 0;
- spec->multiout.hp_nid = 0;
+ /*set speaker EQ bypass attenuation*/
+ dspio_set_uint_param(codec, 0x8f, 0x01, tmp);
- /* inputs */
- cfg->num_inputs = 2; /* Mic-in and line-in */
- cfg->inputs[0].pin = 0x12;
- cfg->inputs[0].type = AUTO_PIN_MIC;
- cfg->inputs[1].pin = 0x11;
- cfg->inputs[1].type = AUTO_PIN_LINE_IN;
+ /* 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);
- /* Mic-in */
- spec->input_pins[0] = 0x12;
- spec->input_labels[0] = "Mic-In";
- spec->adcs[0] = 0x07;
+ /* set AMic1 as CrystalVoice input */
+ tmp = FLOAT_ONE;
+ dspio_set_uint_param(codec, 0x80, 0x05, tmp);
- /* Line-In */
- spec->input_pins[1] = 0x11;
- spec->input_labels[1] = "Line-In";
- spec->adcs[1] = 0x08;
- spec->num_inputs = 2;
+ /* 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)
@@ -1017,11 +4552,23 @@ static int ca0132_init(struct hda_codec *codec)
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);
+ 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++)
@@ -1029,16 +4576,30 @@ static int ca0132_init(struct hda_codec *codec)
init_input(codec, cfg->dig_in_pin, spec->dig_in);
- ca0132_set_ct_ext(codec, 1);
+ 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)
{
- ca0132_set_ct_ext(codec, 0);
+ 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);
}
@@ -1047,26 +4608,83 @@ static struct hda_codec_ops ca0132_patch_ops = {
.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;
- snd_printdd("patch_ca0132\n");
+ 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;
}
@@ -1082,7 +4700,7 @@ static struct hda_codec_preset snd_hda_preset_ca0132[] = {
MODULE_ALIAS("snd-hda-codec-id:11020011");
MODULE_LICENSE("GPL");
-MODULE_DESCRIPTION("Creative CA0132, CA0132 HD-audio codec");
+MODULE_DESCRIPTION("Creative Sound Core3D codec");
static struct hda_codec_preset_list ca0132_list = {
.preset = snd_hda_preset_ca0132,
diff --git a/sound/pci/hda/patch_cirrus.c b/sound/pci/hda/patch_cirrus.c
index c83ccdba1e5..387f0b55188 100644
--- a/sound/pci/hda/patch_cirrus.c
+++ b/sound/pci/hda/patch_cirrus.c
@@ -19,42 +19,21 @@
*/
#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 <sound/tlv.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;
-
- const struct hda_bind_ctls *capture_bind[2];
+ struct hda_gen_spec gen;
unsigned int gpio_mask;
unsigned int gpio_dir;
@@ -62,16 +41,15 @@ struct cs_spec {
unsigned int gpio_eapd_hp; /* EAPD GPIO bit for headphones */
unsigned int gpio_eapd_speaker; /* EAPD GPIO bit for speakers */
- struct hda_pcm pcm_rec[2]; /* PCM information */
-
- unsigned int hp_detect:1;
- unsigned int mic_detect:1;
/* CS421x */
unsigned int spdif_detect:1;
+ unsigned int spdif_present:1;
unsigned int sense_b:1;
hda_nid_t vendor_nid;
- struct hda_input_mux input_mux;
- unsigned int last_input;
+
+ /* for MBP SPDIF control */
+ int (*spdif_sw_put)(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol);
};
/* available models with CS420x */
@@ -79,24 +57,30 @@ enum {
CS420X_MBP53,
CS420X_MBP55,
CS420X_IMAC27,
- CS420X_IMAC27_122,
- CS420X_APPLE,
+ 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_MODELS
+ 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
@@ -130,6 +114,9 @@ enum {
/* 0x0009 - 0x0014 -> 12 test regs */
/* 0x0015 - visibility reg */
+/* Cirrus Logic CS4208 */
+#define CS4208_VENDOR_NID 0x24
+
/*
* Cirrus Logic CS4210
*
@@ -173,718 +160,6 @@ static inline void cs_vendor_coef_set(struct hda_codec *codec, unsigned int idx,
AC_VERB_SET_PROC_COEF, coef);
}
-
-#define HP_EVENT 1
-#define MIC_EVENT 2
-
-/*
- * PCM callbacks
- */
-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)
-{
- 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);
-}
-
-/*
- * 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);
-}
-
-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);
-}
-
-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)
-{
- struct cs_spec *spec = codec->spec;
- return snd_hda_multi_out_dig_prepare(codec, &spec->multiout, stream_tag,
- format, substream);
-}
-
-static int cs_dig_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_dig_cleanup(codec, &spec->multiout);
-}
-
-static void cs_update_input_select(struct hda_codec *codec)
-{
- struct cs_spec *spec = codec->spec;
- if (spec->cur_adc)
- snd_hda_codec_write(codec, spec->cur_adc, 0,
- AC_VERB_SET_CONNECT_SEL,
- spec->adc_idx[spec->cur_input]);
-}
-
-/*
- * 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;
- cs_update_input_select(codec);
- snd_hda_codec_setup_stream(codec, spec->cur_adc, stream_tag, 0, format);
- return 0;
-}
-
-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_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
- },
-};
-
-static const 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_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
- },
-};
-
-static const struct hda_pcm_stream cs_pcm_digital_capture = {
- .substreams = 1,
- .channels_min = 2,
- .channels_max = 2,
-};
-
-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;
-
- 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++;
-
- return 0;
-}
-
-/*
- * parse codec topology
- */
-
-static hda_nid_t get_dac(struct hda_codec *codec, hda_nid_t pin)
-{
- hda_nid_t dac;
- if (!pin)
- return 0;
- if (snd_hda_get_connections(codec, pin, &dac, 1) != 1)
- return 0;
- return dac;
-}
-
-static int is_ext_mic(struct hda_codec *codec, unsigned int idx)
-{
- struct cs_spec *spec = codec->spec;
- struct auto_pin_cfg *cfg = &spec->autocfg;
- hda_nid_t pin = cfg->inputs[idx].pin;
- unsigned int val;
- if (!is_jack_detectable(codec, pin))
- 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, idx;
- hda_nid_t nid;
-
- nid = codec->start_nid;
- for (i = 0; i < codec->num_nodes; i++, nid++) {
- unsigned int type;
- type = get_wcaps_type(get_wcaps(codec, nid));
- if (type != AC_WID_AUD_IN)
- continue;
- idx = snd_hda_get_conn_index(codec, nid, pin, false);
- if (idx >= 0) {
- *idxp = idx;
- return nid;
- }
- }
- 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);
-}
-
-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 (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;
- }
-
- return 0;
-}
-
-static int parse_input(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;
-
- /* 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;
- }
- }
- }
- return 0;
-}
-
-
-static int parse_digital_output(struct hda_codec *codec)
-{
- struct cs_spec *spec = codec->spec;
- struct auto_pin_cfg *cfg = &spec->autocfg;
- hda_nid_t nid;
-
- 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;
-}
-
-static int parse_digital_input(struct hda_codec *codec)
-{
- struct cs_spec *spec = codec->spec;
- struct auto_pin_cfg *cfg = &spec->autocfg;
- int idx;
-
- if (cfg->dig_in_pin)
- spec->dig_in = get_adc(codec, cfg->dig_in_pin, &idx);
- return 0;
-}
-
-/*
- * create mixer controls
- */
-
-static const char * const dir_sfx[2] = { "Playback", "Capture" };
-
-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 int add_volume(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_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);
-}
-
-static void fix_volume_caps(struct hda_codec *codec, hda_nid_t dac)
-{
- 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);
-}
-
-static int add_vmaster(struct hda_codec *codec, hda_nid_t dac)
-{
- struct cs_spec *spec = codec->spec;
- unsigned int tlv[4];
- int err;
-
- 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;
-
- 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;
-}
-
-static int add_output(struct hda_codec *codec, hda_nid_t dac, int idx,
- int num_ctls, int type)
-{
- struct cs_spec *spec = codec->spec;
- const char *name;
- int err, index;
- struct snd_kcontrol *kctl;
- static const char * const speakers[] = {
- "Front Speaker", "Surround Speaker", "Bass Speaker"
- };
- static const char * const line_outs[] = {
- "Front Line Out", "Surround Line Out", "Bass Line Out"
- };
-
- fix_volume_caps(codec, dac);
- if (!spec->vmaster_sw) {
- err = add_vmaster(codec, dac);
- if (err < 0)
- return err;
- }
-
- 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;
- }
-
- 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;
-
- 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);
- if (err < 0)
- return err;
-
- return 0;
-}
-
-static int build_output(struct hda_codec *codec)
-{
- struct cs_spec *spec = codec->spec;
- struct auto_pin_cfg *cfg = &spec->autocfg;
- int i, err;
-
- 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;
-}
-
-/*
- */
-
-static const struct snd_kcontrol_new cs_capture_ctls[] = {
- HDA_BIND_SW("Capture Switch", 0),
- HDA_BIND_VOL("Capture Volume", 0),
-};
-
-static int change_cur_input(struct hda_codec *codec, unsigned int idx,
- int force)
-{
- 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);
- }
- spec->cur_input = idx;
- cs_update_input_select(codec);
- 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;
-
- 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];
- snd_hda_get_pin_label(codec, cfg->inputs[idx].pin, cfg,
- uinfo->value.enumerated.name,
- sizeof(uinfo->value.enumerated.name), NULL);
- return 0;
-}
-
-static int cs_capture_source_get(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;
-}
-
-static int cs_capture_source_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;
- unsigned int idx = ucontrol->value.enumerated.item[0];
-
- if (idx >= spec->num_inputs)
- return -EINVAL;
- idx = spec->input_idx[idx];
- return change_cur_input(codec, idx, 0);
-}
-
-static const 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_bind_ctls *make_bind_capture(struct hda_codec *codec,
- struct hda_ctl_ops *ops)
-{
- struct cs_spec *spec = codec->spec;
- struct hda_bind_ctls *bind;
- int i, n;
-
- 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;
-}
-
-/* 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)
-{
- 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);
-}
-
-static int build_input(struct hda_codec *codec)
-{
- struct cs_spec *spec = codec->spec;
- int i, err;
-
- if (!spec->num_inputs)
- return 0;
-
- /* 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;
- }
-
- for (i = 0; i < spec->num_inputs; i++) {
- err = add_input_volume_control(codec, &spec->autocfg, i);
- if (err < 0)
- return err;
- }
-
- return 0;
-}
-
-/*
- */
-
-static int build_digital_output(struct hda_codec *codec)
-{
- struct cs_spec *spec = codec->spec;
- int err;
-
- if (!spec->multiout.dig_out_nid)
- return 0;
-
- 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, &spec->multiout);
- if (err < 0)
- return err;
- return 0;
-}
-
-static int build_digital_input(struct hda_codec *codec)
-{
- struct cs_spec *spec = codec->spec;
- if (spec->dig_in)
- return snd_hda_create_spdif_in_ctls(codec, spec->dig_in);
- return 0;
-}
-
/*
* auto-mute and auto-mic switching
* CS421x auto-output redirecting
@@ -894,211 +169,44 @@ static int build_digital_input(struct hda_codec *codec)
static void cs_automute(struct hda_codec *codec)
{
struct cs_spec *spec = codec->spec;
- struct auto_pin_cfg *cfg = &spec->autocfg;
- unsigned int hp_present;
- unsigned int spdif_present;
- hda_nid_t nid;
- int i;
-
- spdif_present = 0;
- if (cfg->dig_outs) {
- nid = cfg->dig_out_pins[0];
- if (is_jack_detectable(codec, nid)) {
- /*
- TODO: SPDIF output redirect when SENSE_B is enabled.
- Shared (SENSE_A) jack (e.g HP/mini-TOSLINK)
- assumed.
- */
- if (snd_hda_jack_detect(codec, nid)
- /* && spec->sense_b */)
- spdif_present = 1;
- }
- }
- hp_present = 0;
- for (i = 0; i < cfg->hp_outs; i++) {
- nid = cfg->hp_pins[i];
- if (!is_jack_detectable(codec, nid))
- continue;
- hp_present = snd_hda_jack_detect(codec, nid);
- if (hp_present)
- break;
- }
+ /* mute HPs if spdif jack (SENSE_B) is present */
+ spec->gen.master_mute = !!(spec->spdif_present && spec->sense_b);
- /* mute speakers if spdif or hp jack is plugged in */
- for (i = 0; i < cfg->speaker_outs; i++) {
- int pin_ctl = hp_present ? 0 : PIN_OUT;
- /* detect on spdif is specific to CS4210 */
- if (spdif_present && (spec->vendor_nid == CS4210_VENDOR_NID))
- pin_ctl = 0;
+ snd_hda_gen_update_outputs(codec);
- nid = cfg->speaker_pins[i];
- snd_hda_codec_write(codec, nid, 0,
- AC_VERB_SET_PIN_WIDGET_CONTROL, pin_ctl);
- }
- if (spec->gpio_eapd_hp) {
- unsigned int gpio = hp_present ?
+ 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, gpio);
- }
-
- /* specific to CS4210 */
- if (spec->vendor_nid == CS4210_VENDOR_NID) {
- /* mute HPs if spdif jack (SENSE_B) is present */
- for (i = 0; i < cfg->hp_outs; i++) {
- nid = cfg->hp_pins[i];
- snd_hda_codec_write(codec, nid, 0,
- AC_VERB_SET_PIN_WIDGET_CONTROL,
- (spdif_present && spec->sense_b) ? 0 : PIN_HP);
- }
-
- /* SPDIF TX on/off */
- if (cfg->dig_outs) {
- nid = cfg->dig_out_pins[0];
- snd_hda_codec_write(codec, nid, 0,
- AC_VERB_SET_PIN_WIDGET_CONTROL,
- spdif_present ? PIN_OUT : 0);
-
- }
- /* Update board GPIOs if neccessary ... */
- }
-}
-
-/*
- * Auto-input redirect for CS421x
- * Switch max 3 inputs of a single ADC (nid 3)
-*/
-
-static void cs_automic(struct hda_codec *codec)
-{
- 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);
-
- /* specific to CS421x, single ADC */
- if (spec->vendor_nid == CS420X_VENDOR_NID) {
- if (present)
- change_cur_input(codec, spec->automic_idx, 0);
- else
- change_cur_input(codec, !spec->automic_idx, 0);
- } else {
- if (present) {
- if (spec->cur_input != spec->automic_idx) {
- spec->last_input = spec->cur_input;
- spec->cur_input = spec->automic_idx;
- }
- } else {
- spec->cur_input = spec->last_input;
- }
- cs_update_input_select(codec);
+ AC_VERB_SET_GPIO_DATA, spec->gpio_data);
}
}
-/*
- */
-
-static void init_output(struct hda_codec *codec)
+static bool is_active_pin(struct hda_codec *codec, hda_nid_t nid)
{
- struct cs_spec *spec = codec->spec;
- struct auto_pin_cfg *cfg = &spec->autocfg;
- int i;
-
- /* 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);
- }
-
- /* 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);
- /* HP */
- 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_jack_detect_enable(codec, nid, HP_EVENT);
- spec->hp_detect = 1;
- }
- }
-
- /* Speaker */
- 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);
-
- /* SPDIF is enabled on presence detect for CS421x */
- if (spec->hp_detect || spec->spdif_detect)
- cs_automute(codec);
+ unsigned int val;
+ val = snd_hda_codec_get_pincfg(codec, nid);
+ return (get_defcfg_connect(val) != AC_JACK_PORT_NONE);
}
-static void init_input(struct hda_codec *codec)
+static void init_input_coef(struct hda_codec *codec)
{
struct cs_spec *spec = codec->spec;
- struct auto_pin_cfg *cfg = &spec->autocfg;
unsigned int coef;
- int i;
- 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_jack_detect_enable(codec, pin, MIC_EVENT);
- }
/* CS420x has multiple ADC, CS421x has single ADC */
if (spec->vendor_nid == CS420X_VENDOR_NID) {
- change_cur_input(codec, spec->cur_input, 1);
- if (spec->mic_detect)
- cs_automic(codec);
-
- coef = 0x000a; /* ADC1/2 - Digital and Analog Soft Ramp */
+ coef = cs_vendor_coef_get(codec, IDX_BEEP_CFG);
if (is_active_pin(codec, CS_DMIC2_PIN_NID))
- coef |= 0x0500; /* DMIC2 2 chan on, GPIO1 off */
+ coef |= 1 << 4; /* DMIC2 2 chan on, GPIO1 off */
if (is_active_pin(codec, CS_DMIC1_PIN_NID))
- coef |= 0x1800; /* DMIC1 2 chan on, GPIO0 off
+ 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_ADC_CFG, coef);
- } else {
- if (spec->mic_detect)
- cs_automic(codec);
- else {
- spec->cur_adc = spec->adc_nid[spec->cur_input];
- cs_update_input_select(codec);
- }
+
+ cs_vendor_coef_set(codec, IDX_BEEP_CFG, coef);
}
}
@@ -1111,13 +219,26 @@ static const struct hda_verb cs_coef_init_verbs[] = {
| 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_DAC_CFG},
+ {0x11, AC_VERB_SET_COEF_INDEX, IDX_BEEP_CFG},
{0x11, AC_VERB_SET_PROC_COEF, 0x0007}, /* Enable Beep thru DAC1/2/3 */
{} /* terminator */
};
+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 */
+};
+
/* Errata: CS4207 rev C0/C1/C2 Silicon
*
* http://www.cirrus.com/en/pubs/errata/ER880C3.pdf
@@ -1168,7 +289,7 @@ static const struct hda_verb cs_errata_init_verbs[] = {
};
/* SPDIF setup */
-static void init_digital(struct hda_codec *codec)
+static void init_digital_coef(struct hda_codec *codec)
{
unsigned int coef;
@@ -1186,10 +307,15 @@ static int cs_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 == 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);
+ }
- 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,
@@ -1200,69 +326,33 @@ static int cs_init(struct hda_codec *codec)
spec->gpio_data);
}
- init_output(codec);
- init_input(codec);
- init_digital(codec);
- snd_hda_jack_report_sync(codec);
+ if (spec->vendor_nid == CS420X_VENDOR_NID) {
+ init_input_coef(codec);
+ init_digital_coef(codec);
+ }
return 0;
}
static int cs_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);
- if (err < 0)
- return err;
- err = build_digital_output(codec);
+ err = snd_hda_gen_build_controls(codec);
if (err < 0)
return err;
- err = build_digital_input(codec);
- if (err < 0)
- return err;
- err = cs_init(codec);
- if (err < 0)
- return err;
-
- err = snd_hda_jack_add_kctls(codec, &spec->autocfg);
- if (err < 0)
- return err;
-
+ snd_hda_apply_fixup(codec, HDA_FIXUP_ACT_BUILD);
return 0;
}
-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);
-}
-
-static void cs_unsol_event(struct hda_codec *codec, unsigned int res)
-{
- switch (snd_hda_jack_get_action(codec, res >> 26)) {
- case HP_EVENT:
- cs_automute(codec);
- break;
- case MIC_EVENT:
- cs_automic(codec);
- break;
- }
- snd_hda_jack_report_sync(codec);
-}
+#define cs_free snd_hda_gen_free
static const struct hda_codec_ops cs_patch_ops = {
.build_controls = cs_build_controls,
- .build_pcms = cs_build_pcms,
+ .build_pcms = snd_hda_gen_build_pcms,
.init = cs_init,
.free = cs_free,
- .unsol_event = cs_unsol_event,
+ .unsol_event = snd_hda_jack_unsol_event,
};
static int cs_parse_auto_config(struct hda_codec *codec)
@@ -1270,57 +360,47 @@ static int cs_parse_auto_config(struct hda_codec *codec)
struct cs_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->gen.autocfg, NULL, 0);
if (err < 0)
return err;
- err = parse_output(codec);
- 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;
+
return 0;
}
-static const char * const cs420x_models[CS420X_MODELS] = {
- [CS420X_MBP53] = "mbp53",
- [CS420X_MBP55] = "mbp55",
- [CS420X_IMAC27] = "imac27",
- [CS420X_IMAC27_122] = "imac27_122",
- [CS420X_APPLE] = "apple",
- [CS420X_AUTO] = "auto",
+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 struct snd_pci_quirk cs420x_cfg_tbl[] = {
+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),*/
- {} /* terminator */
-};
-static const struct snd_pci_quirk cs420x_codec_cfg_tbl[] = {
+ /* 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 */
};
-struct cs_pincfg {
- hda_nid_t nid;
- u32 val;
-};
-
-static const struct cs_pincfg mbp53_pincfgs[] = {
+static const struct hda_pintbl mbp53_pincfgs[] = {
{ 0x09, 0x012b4050 },
{ 0x0a, 0x90100141 },
{ 0x0b, 0x90100140 },
@@ -1334,7 +414,7 @@ static const struct cs_pincfg mbp53_pincfgs[] = {
{} /* terminator */
};
-static const struct cs_pincfg mbp55_pincfgs[] = {
+static const struct hda_pintbl mbp55_pincfgs[] = {
{ 0x09, 0x012b4030 },
{ 0x0a, 0x90100121 },
{ 0x0b, 0x90100120 },
@@ -1348,7 +428,7 @@ static const struct cs_pincfg mbp55_pincfgs[] = {
{} /* terminator */
};
-static const struct cs_pincfg imac27_pincfgs[] = {
+static const struct hda_pintbl imac27_pincfgs[] = {
{ 0x09, 0x012b4050 },
{ 0x0a, 0x90100140 },
{ 0x0b, 0x90100142 },
@@ -1362,20 +442,138 @@ static const struct cs_pincfg imac27_pincfgs[] = {
{} /* terminator */
};
-static const struct cs_pincfg *cs_pincfgs[CS420X_MODELS] = {
- [CS420X_MBP53] = mbp53_pincfgs,
- [CS420X_MBP55] = mbp55_pincfgs,
- [CS420X_IMAC27] = imac27_pincfgs,
+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 fix_pincfg(struct hda_codec *codec, int model,
- const struct cs_pincfg **pin_configs)
+static void cs420x_fixup_gpio_13(struct hda_codec *codec,
+ const struct hda_fixup *fix, int action)
{
- const struct cs_pincfg *cfg = pin_configs[model];
- if (!cfg)
- return;
- for (; cfg->nid; cfg++)
- snd_hda_codec_set_pincfg(codec, cfg->nid, cfg->val);
+ 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 cs420x_fixup_gpio_23(struct hda_codec *codec,
+ const struct hda_fixup *fix, int action)
+{
+ 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 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;
+
+ 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);
+
+ return spec;
}
static int patch_cs420x(struct hda_codec *codec)
@@ -1383,40 +581,174 @@ static int patch_cs420x(struct hda_codec *codec)
struct cs_spec *spec;
int err;
- spec = kzalloc(sizeof(*spec), GFP_KERNEL);
+ spec = cs_alloc_spec(codec, CS420X_VENDOR_NID);
if (!spec)
return -ENOMEM;
- codec->spec = spec;
- spec->vendor_nid = CS420X_VENDOR_NID;
-
- spec->board_config =
- snd_hda_check_board_config(codec, CS420X_MODELS,
- cs420x_models, cs420x_cfg_tbl);
- if (spec->board_config < 0)
- spec->board_config =
- snd_hda_check_board_codec_sid_config(codec,
- CS420X_MODELS, NULL, cs420x_codec_cfg_tbl);
- if (spec->board_config >= 0)
- fix_pincfg(codec, spec->board_config, cs_pincfgs);
-
- switch (spec->board_config) {
- case CS420X_IMAC27:
- case CS420X_MBP53:
- case CS420X_MBP55:
- case CS420X_APPLE:
- 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;
- break;
- case CS420X_IMAC27_122:
- spec->gpio_eapd_hp = 4; /* GPIO2 = headphones */
- spec->gpio_eapd_speaker = 8; /* GPIO3 = speakers */
+ spec->gen.automute_hook = cs_automute;
+
+ snd_hda_pick_fixup(codec, cs420x_models, cs420x_fixup_tbl,
+ cs420x_fixups);
+ snd_hda_apply_fixup(codec, HDA_FIXUP_ACT_PRE_PROBE);
+
+ 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;
+}
+
+/*
+ * 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 */
+};
+
+/* 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 void cs4208_fixup_gpio0(struct hda_codec *codec,
+ const struct hda_fixup *fix, int action)
+{
+ 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;
- break;
}
+}
+
+static const struct hda_fixup cs4208_fixups[];
+
+/* 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 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;
+ 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);
+}
+
+/* hook the SPDIF switch */
+static void cs4208_fixup_spdif_switch(struct hda_codec *codec,
+ const struct hda_fixup *fix, int action)
+{
+ if (action == HDA_FIXUP_ACT_BUILD) {
+ struct cs_spec *spec = codec->spec;
+ struct snd_kcontrol *kctl;
+
+ 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 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,
+ },
+};
+
+/* correct the 0dB offset of input pins */
+static void cs4208_fix_amp_caps(struct hda_codec *codec, hda_nid_t adc)
+{
+ unsigned int caps;
+
+ caps = query_amp_caps(codec, adc, HDA_INPUT);
+ caps &= ~(AC_AMPCAP_OFFSET);
+ caps |= 0x02;
+ snd_hda_override_amp_caps(codec, adc, HDA_INPUT, caps);
+}
+
+static int patch_cs4208(struct hda_codec *codec)
+{
+ struct cs_spec *spec;
+ int err;
+
+ spec = cs_alloc_spec(codec, CS4208_VENDOR_NID);
+ if (!spec)
+ return -ENOMEM;
+
+ spec->gen.automute_hook = cs_automute;
+ /* exclude NID 0x10 (HP) from output volumes due to different steps */
+ spec->gen.out_vol_mask = 1ULL << 0x10;
+
+ snd_hda_pick_fixup(codec, cs4208_models, cs4208_fixup_tbl,
+ cs4208_fixups);
+ snd_hda_apply_fixup(codec, HDA_FIXUP_ACT_PRE_PROBE);
+
+ 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)
@@ -1424,11 +756,12 @@ static int patch_cs420x(struct hda_codec *codec)
codec->patch_ops = cs_patch_ops;
+ snd_hda_apply_fixup(codec, HDA_FIXUP_ACT_PROBE);
+
return 0;
error:
- kfree(codec->spec);
- codec->spec = NULL;
+ cs_free(codec);
return err;
}
@@ -1441,11 +774,13 @@ static int patch_cs420x(struct hda_codec *codec)
*/
/* CS4210 board names */
-static const char *cs421x_models[CS421X_MODELS] = {
- [CS421X_CDB4210] = "cdb4210",
+static const struct hda_model_fixup cs421x_models[] = {
+ { .id = CS421X_CDB4210, .name = "cdb4210" },
+ { .id = CS421X_STUMPY, .name = "stumpy" },
+ {}
};
-static const struct snd_pci_quirk cs421x_cfg_tbl[] = {
+static const struct snd_pci_quirk cs421x_fixup_tbl[] = {
/* Test Intel board + CDB2410 */
SND_PCI_QUIRK(0x8086, 0x5001, "DP45SG/CDB4210", CS421X_CDB4210),
{} /* terminator */
@@ -1453,7 +788,7 @@ static const struct snd_pci_quirk cs421x_cfg_tbl[] = {
/* CS4210 board pinconfigs */
/* Default CS4210 (CDB4210)*/
-static const struct cs_pincfg cdb4210_pincfgs[] = {
+static const struct hda_pintbl cdb4210_pincfgs[] = {
{ 0x05, 0x0321401f },
{ 0x06, 0x90170010 },
{ 0x07, 0x03813031 },
@@ -1463,8 +798,41 @@ static const struct cs_pincfg cdb4210_pincfgs[] = {
{} /* terminator */
};
-static const struct cs_pincfg *cs421x_pincfgs[CS421X_MODELS] = {
- [CS421X_CDB4210] = cdb4210_pincfgs,
+/* Stumpy ChromeBox */
+static const struct hda_pintbl stumpy_pincfgs[] = {
+ { 0x05, 0x022120f0 },
+ { 0x06, 0x901700f0 },
+ { 0x07, 0x02a120f0 },
+ { 0x08, 0x77a70037 },
+ { 0x09, 0x77a6003e },
+ { 0x0a, 0x434510f0 },
+ {} /* terminator */
+};
+
+/* 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 (action == HDA_FIXUP_ACT_PRE_PROBE)
+ spec->sense_b = 1;
+}
+
+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[] = {
@@ -1568,7 +936,7 @@ static int cs421x_boost_vol_put(struct snd_kcontrol *kcontrol,
}
}
-static const struct snd_kcontrol_new cs421x_speaker_bost_ctl = {
+static const struct snd_kcontrol_new cs421x_speaker_boost_ctl = {
.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
.access = (SNDRV_CTL_ELEM_ACCESS_READWRITE |
@@ -1613,20 +981,44 @@ static void cs4210_pinmux_init(struct hda_codec *codec)
}
}
-static void init_cs421x_digital(struct hda_codec *codec)
+static void cs4210_spdif_automute(struct hda_codec *codec,
+ struct hda_jack_tbl *tbl)
{
struct cs_spec *spec = codec->spec;
- struct auto_pin_cfg *cfg = &spec->autocfg;
- int i;
+ bool spdif_present = false;
+ hda_nid_t spdif_pin = spec->gen.autocfg.dig_out_pins[0];
+
+ /* detect on spdif is specific to CS4210 */
+ if (!spec->spdif_detect ||
+ spec->vendor_nid != CS4210_VENDOR_NID)
+ return;
+
+ spdif_present = snd_hda_jack_detect(codec, spdif_pin);
+ if (spdif_present == spec->spdif_present)
+ return;
+ 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);
+
+ cs_automute(codec);
+}
+
+static void parse_cs421x_digital(struct hda_codec *codec)
+{
+ struct cs_spec *spec = codec->spec;
+ struct auto_pin_cfg *cfg = &spec->gen.autocfg;
+ int i;
for (i = 0; i < cfg->dig_outs; i++) {
hda_nid_t nid = cfg->dig_out_pins[i];
- if (!cfg->speaker_outs)
- continue;
if (get_wcaps(codec, nid) & AC_WCAP_UNSOL_CAP) {
- snd_hda_jack_detect_enable(codec, nid, SPDIF_EVENT);
spec->spdif_detect = 1;
+ snd_hda_jack_detect_enable_callback(codec, nid,
+ SPDIF_EVENT,
+ cs4210_spdif_automute);
}
}
}
@@ -1641,6 +1033,8 @@ static int cs421x_init(struct hda_codec *codec)
cs4210_pinmux_init(codec);
}
+ snd_hda_gen_init(codec);
+
if (spec->gpio_mask) {
snd_hda_codec_write(codec, 0x01, 0, AC_VERB_SET_GPIO_MASK,
spec->gpio_mask);
@@ -1650,250 +1044,61 @@ static int cs421x_init(struct hda_codec *codec)
spec->gpio_data);
}
- init_output(codec);
- init_input(codec);
- init_cs421x_digital(codec);
- snd_hda_jack_report_sync(codec);
-
- return 0;
-}
-
-/*
- * CS4210 Input MUX (1 ADC)
- */
-static int cs421x_mux_enum_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;
-
- return snd_hda_input_mux_info(&spec->input_mux, uinfo);
-}
-
-static int cs421x_mux_enum_get(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->cur_input;
- return 0;
-}
-
-static int cs421x_mux_enum_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;
-
- return snd_hda_input_mux_put(codec, &spec->input_mux, ucontrol,
- spec->adc_nid[0], &spec->cur_input);
-
-}
-
-static struct snd_kcontrol_new cs421x_capture_source = {
-
- .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
- .name = "Capture Source",
- .access = SNDRV_CTL_ELEM_ACCESS_READWRITE,
- .info = cs421x_mux_enum_info,
- .get = cs421x_mux_enum_get,
- .put = cs421x_mux_enum_put,
-};
-
-static int cs421x_add_input_volume_control(struct hda_codec *codec, int item)
-{
- struct cs_spec *spec = codec->spec;
- struct auto_pin_cfg *cfg = &spec->autocfg;
- const struct hda_input_mux *imux = &spec->input_mux;
- hda_nid_t pin = cfg->inputs[item].pin;
- struct snd_kcontrol *kctl;
- u32 caps;
-
- 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;
-
- return add_volume(codec, imux->items[item].label, 0,
- HDA_COMPOSE_AMP_VAL(pin, 3, 0, HDA_INPUT), 1, &kctl);
-}
-
-/* add a (input-boost) volume control to the given input pin */
-static int build_cs421x_input(struct hda_codec *codec)
-{
- struct cs_spec *spec = codec->spec;
- struct auto_pin_cfg *cfg = &spec->autocfg;
- struct hda_input_mux *imux = &spec->input_mux;
- int i, err, type_idx;
- const char *label;
-
- if (!spec->num_inputs)
- return 0;
-
- /* 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;
- }
- }
-
- /* Add Input MUX Items + Capture Volume/Switch */
- for (i = 0; i < spec->num_inputs; i++) {
- label = hda_get_autocfg_input_label(codec, cfg, i);
- snd_hda_add_imux_item(imux, label, spec->adc_idx[i], &type_idx);
+ init_input_coef(codec);
- err = cs421x_add_input_volume_control(codec, i);
- if (err < 0)
- return err;
- }
-
- /*
- Add 'Capture Source' Switch if
- * 2 inputs and no mic detec
- * 3 inputs
- */
- if ((spec->num_inputs == 2 && !spec->mic_detect) ||
- (spec->num_inputs == 3)) {
-
- err = snd_hda_ctl_add(codec, spec->adc_nid[0],
- snd_ctl_new1(&cs421x_capture_source, codec));
- if (err < 0)
- return err;
- }
+ cs4210_spdif_automute(codec, NULL);
return 0;
}
-/* Single DAC (Mute/Gain) */
-static int build_cs421x_output(struct hda_codec *codec)
+static int cs421x_build_controls(struct hda_codec *codec)
{
- hda_nid_t dac = CS4210_DAC_NID;
struct cs_spec *spec = codec->spec;
- struct auto_pin_cfg *cfg = &spec->autocfg;
- struct snd_kcontrol *kctl;
int err;
- char *name = "Master";
-
- fix_volume_caps(codec, dac);
- err = add_mute(codec, name, 0,
- HDA_COMPOSE_AMP_VAL(dac, 3, 0, HDA_OUTPUT), 0, &kctl);
+ err = snd_hda_gen_build_controls(codec);
if (err < 0)
return err;
- err = add_volume(codec, name, 0,
- HDA_COMPOSE_AMP_VAL(dac, 3, 0, HDA_OUTPUT), 0, &kctl);
- if (err < 0)
- return err;
-
- if (cfg->speaker_outs && (spec->vendor_nid == CS4210_VENDOR_NID)) {
+ if (spec->gen.autocfg.speaker_outs &&
+ spec->vendor_nid == CS4210_VENDOR_NID) {
err = snd_hda_ctl_add(codec, 0,
- snd_ctl_new1(&cs421x_speaker_bost_ctl, codec));
+ snd_ctl_new1(&cs421x_speaker_boost_ctl, codec));
if (err < 0)
return err;
}
- return err;
-}
-
-static int cs421x_build_controls(struct hda_codec *codec)
-{
- struct cs_spec *spec = codec->spec;
- int err;
-
- err = build_cs421x_output(codec);
- if (err < 0)
- return err;
- err = build_cs421x_input(codec);
- if (err < 0)
- return err;
- err = build_digital_output(codec);
- if (err < 0)
- return err;
- err = cs421x_init(codec);
- if (err < 0)
- return err;
-
- err = snd_hda_jack_add_kctls(codec, &spec->autocfg);
- if (err < 0)
- return err;
-
return 0;
}
-static void cs421x_unsol_event(struct hda_codec *codec, unsigned int res)
-{
- switch (snd_hda_jack_get_action(codec, res >> 26)) {
- case HP_EVENT:
- case SPDIF_EVENT:
- cs_automute(codec);
- break;
-
- case MIC_EVENT:
- cs_automic(codec);
- break;
- }
- snd_hda_jack_report_sync(codec);
-}
-
-static int parse_cs421x_input(struct hda_codec *codec)
+static void fix_volume_caps(struct hda_codec *codec, hda_nid_t dac)
{
- 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->adc_nid[i] = get_adc(codec, pin, &spec->adc_idx[i]);
- spec->cur_input = spec->last_input = i;
- spec->num_inputs++;
+ unsigned int caps;
- /* check whether the automatic mic switch is available */
- if (is_ext_mic(codec, i) && cfg->num_inputs >= 2) {
- spec->mic_detect = 1;
- spec->automic_idx = i;
- }
- }
- return 0;
+ /* 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 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;
- err = parse_output(codec);
- if (err < 0)
- return err;
- err = parse_cs421x_input(codec);
+ fix_volume_caps(codec, dac);
+
+ err = snd_hda_parse_pin_defcfg(codec, &spec->gen.autocfg, NULL, 0);
if (err < 0)
return err;
- err = parse_digital_output(codec);
+
+ err = snd_hda_gen_parse_auto_config(codec, &spec->gen.autocfg);
if (err < 0)
return err;
+
+ parse_cs421x_digital(codec);
return 0;
}
@@ -1902,7 +1107,7 @@ static int cs421x_parse_auto_config(struct hda_codec *codec)
Manage PDREF, when transitioning to D3hot
(DAC,ADC) -> D3, PDREF=1, AFG->D3
*/
-static int cs421x_suspend(struct hda_codec *codec, pm_message_t state)
+static int cs421x_suspend(struct hda_codec *codec)
{
struct cs_spec *spec = codec->spec;
unsigned int coef;
@@ -1924,12 +1129,12 @@ static int cs421x_suspend(struct hda_codec *codec, pm_message_t state)
}
#endif
-static struct hda_codec_ops cs421x_patch_ops = {
+static const struct hda_codec_ops cs421x_patch_ops = {
.build_controls = cs421x_build_controls,
- .build_pcms = cs_build_pcms,
+ .build_pcms = snd_hda_gen_build_pcms,
.init = cs421x_init,
.free = cs_free,
- .unsol_event = cs421x_unsol_event,
+ .unsol_event = snd_hda_jack_unsol_event,
#ifdef CONFIG_PM
.suspend = cs421x_suspend,
#endif
@@ -1940,33 +1145,15 @@ static int patch_cs4210(struct hda_codec *codec)
struct cs_spec *spec;
int err;
- spec = kzalloc(sizeof(*spec), GFP_KERNEL);
+ spec = cs_alloc_spec(codec, CS4210_VENDOR_NID);
if (!spec)
return -ENOMEM;
- codec->spec = spec;
- spec->vendor_nid = CS4210_VENDOR_NID;
+ spec->gen.automute_hook = cs_automute;
- spec->board_config =
- snd_hda_check_board_config(codec, CS421X_MODELS,
- cs421x_models, cs421x_cfg_tbl);
- if (spec->board_config >= 0)
- fix_pincfg(codec, spec->board_config, cs421x_pincfgs);
- /*
- Setup GPIO/SENSE for each board (if used)
- */
- switch (spec->board_config) {
- case CS421X_CDB4210:
- snd_printd("CS4210 board: %s\n",
- cs421x_models[spec->board_config]);
-/* spec->gpio_mask = 3;
- spec->gpio_dir = 3;
- spec->gpio_data = 3;
-*/
- spec->sense_b = 1;
-
- break;
- }
+ snd_hda_pick_fixup(codec, cs421x_models, cs421x_fixup_tbl,
+ cs421x_fixups);
+ snd_hda_apply_fixup(codec, HDA_FIXUP_ACT_PRE_PROBE);
/*
Update the GPIO/DMIC/SENSE_B pinmux before the configuration
@@ -1981,11 +1168,12 @@ static int patch_cs4210(struct hda_codec *codec)
codec->patch_ops = cs421x_patch_ops;
+ snd_hda_apply_fixup(codec, HDA_FIXUP_ACT_PROBE);
+
return 0;
error:
- kfree(codec->spec);
- codec->spec = NULL;
+ cs_free(codec);
return err;
}
@@ -1994,12 +1182,9 @@ 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->vendor_nid = CS4213_VENDOR_NID;
err = cs421x_parse_auto_config(codec);
if (err < 0)
@@ -2009,8 +1194,7 @@ static int patch_cs4213(struct hda_codec *codec)
return 0;
error:
- kfree(codec->spec);
- codec->spec = NULL;
+ cs_free(codec);
return err;
}
@@ -2021,6 +1205,7 @@ static int patch_cs4213(struct hda_codec *codec)
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 */
@@ -2028,6 +1213,7 @@ static const struct hda_codec_preset snd_hda_preset_cirrus[] = {
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");
diff --git a/sound/pci/hda/patch_cmedia.c b/sound/pci/hda/patch_cmedia.c
index b6767b4ced4..061ea5965dd 100644
--- a/sound/pci/hda/patch_cmedia.c
+++ b/sound/pci/hda/patch_cmedia.c
@@ -22,13 +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
@@ -42,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 */
@@ -74,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
*/
@@ -355,77 +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;
-
- /* 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++) {
- 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 */
- k = snd_hda_get_conn_index(codec, nid,
- spec->dac_nids[i], 0);
- if (k >= 0)
- spec->multi_init[j].param = k;
- j++;
- }
- }
- return 0;
-}
-
static int cmi9880_init(struct hda_codec *codec)
{
struct cmi_spec *spec = codec->spec;
@@ -630,6 +572,42 @@ static const struct hda_codec_ops cmi9880_patch_ops = {
.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,6 +676,9 @@ static int patch_cmi9880(struct hda_codec *codec)
codec->patch_ops = cmi9880_patch_ops;
return 0;
+#else
+ return cmi_parse_auto_config(codec);
+#endif
}
/*
diff --git a/sound/pci/hda/patch_conexant.c b/sound/pci/hda/patch_conexant.c
index d29d6d37790..1dc7e974f3b 100644
--- a/sound/pci/hda/patch_conexant.c
+++ b/sound/pci/hda/patch_conexant.c
@@ -23,15 +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
@@ -52,21 +55,25 @@
#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;
-struct imux_info {
- hda_nid_t pin; /* input pin NID */
- hda_nid_t adc; /* connected ADC NID */
- hda_nid_t boost; /* optional boost volume NID */
- int index; /* corresponding to autocfg.input */
-};
+ unsigned int beep_amp;
-struct conexant_spec {
+ /* extra EAPD pins */
+ unsigned int num_eapds;
+ hda_nid_t eapds[4];
+ bool dynamic_eapd;
+
+ 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;
@@ -86,11 +93,6 @@ struct conexant_spec {
unsigned int hp_present;
unsigned int line_present;
unsigned int auto_mic;
- int auto_mic_ext; /* imux_pins[] index for ext mic */
- int auto_mic_dock; /* imux_pins[] index for dock mic */
- int auto_mic_int; /* imux_pins[] index for int mic */
- unsigned int need_dac_fix;
- hda_nid_t slave_dig_outs[2];
/* capture */
unsigned int num_adc_nids;
@@ -118,53 +120,61 @@ 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;
- struct imux_info imux_info[HDA_MAX_NUM_INPUTS];
- hda_nid_t private_adc_nids[HDA_MAX_NUM_INPUTS];
- 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 detect_line:1; /* Line-out detection enabled */
- unsigned int automute_lines:1; /* automute line-out as well */
- unsigned int automute_hp_lo:1; /* both HP and LO available */
unsigned int dell_automute:1;
unsigned int dell_vostro:1;
unsigned int ideapad:1;
unsigned int thinkpad:1;
unsigned int hp_laptop:1;
unsigned int asus:1;
- unsigned int pin_eapd_ctrls:1;
- unsigned int single_adc_amp:1;
-
- unsigned int adc_switching: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;
- /* extra EAPD pins */
- unsigned int num_eapds;
- hda_nid_t eapds[4];
+#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)
@@ -235,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;
@@ -248,8 +256,6 @@ 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;
}
@@ -377,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;
@@ -426,7 +430,7 @@ static void conexant_set_power(struct hda_codec *codec, hda_nid_t fg,
/* 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, true);
+ snd_hda_codec_set_power_to_all(codec, fg, power_state);
}
static int conexant_init(struct hda_codec *codec)
@@ -441,7 +445,6 @@ static int conexant_init(struct hda_codec *codec)
static void conexant_free(struct hda_codec *codec)
{
- snd_hda_detach_beep_device(codec);
kfree(codec->spec);
}
@@ -456,30 +459,8 @@ static const struct snd_kcontrol_new cxt_capture_mixers[] = {
{}
};
-#ifdef CONFIG_SND_HDA_INPUT_BEEP
-/* 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 */
-};
-#endif
-
-static const char * const slave_vols[] = {
- "Headphone Playback Volume",
- "Speaker Playback Volume",
- "Front Playback Volume",
- "Surround Playback Volume",
- "CLFE Playback Volume",
- NULL
-};
-
-static const char * const slave_sws[] = {
- "Headphone Playback Switch",
- "Speaker Playback Switch",
- "Front Playback Switch",
- "Surround Playback Switch",
- "CLFE Playback Switch",
+static const char * const slave_pfxs[] = {
+ "Headphone", "Speaker", "Bass Speaker", "Front", "Surround", "CLFE",
NULL
};
@@ -519,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;
}
@@ -537,33 +520,12 @@ 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) {
- 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;
- }
- }
-#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 const struct hda_codec_ops conexant_patch_ops = {
.build_controls = conexant_build_controls,
@@ -571,19 +533,8 @@ static const struct hda_codec_ops conexant_patch_ops = {
.init = conexant_init,
.free = conexant_free,
.set_power_state = conexant_set_power,
-#ifdef CONFIG_SND_HDA_POWER_SAVE
- .suspend = conexant_suspend,
-#endif
- .reboot_notify = snd_hda_shutup_pins,
};
-#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
@@ -667,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;
}
@@ -695,27 +644,18 @@ static const struct hda_channel_mode cxt5045_modes[1] = {
static const struct hda_input_mux cxt5045_capture_source = {
.num_items = 2,
.items = {
- { "IntMic", 0x1 },
- { "ExtMic", 0x2 },
+ { "Internal Mic", 0x1 },
+ { "Mic", 0x2 },
}
};
static const struct hda_input_mux cxt5045_capture_source_benq = {
- .num_items = 5,
- .items = {
- { "IntMic", 0x1 },
- { "ExtMic", 0x2 },
- { "LineIn", 0x3 },
- { "CD", 0x4 },
- { "Mixer", 0x0 },
- }
-};
-
-static const struct hda_input_mux cxt5045_capture_source_hp530 = {
- .num_items = 2,
+ .num_items = 4,
.items = {
- { "ExtMic", 0x1 },
- { "IntMic", 0x2 },
+ { "Internal Mic", 0x1 },
+ { "Mic", 0x2 },
+ { "Line", 0x3 },
+ { "Mixer", 0x0 },
}
};
@@ -806,10 +746,8 @@ static void cxt5045_hp_unsol_event(struct hda_codec *codec,
}
static const 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),
+ 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),
@@ -830,42 +768,8 @@ static const struct snd_kcontrol_new cxt5045_mixers[] = {
};
static const 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 const 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,
- },
+ HDA_CODEC_VOLUME("Line Playback Volume", 0x17, 0x3, HDA_INPUT),
+ HDA_CODEC_MUTE("Line Playback Switch", 0x17, 0x3, HDA_INPUT),
{}
};
@@ -954,10 +858,10 @@ 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),
@@ -968,16 +872,16 @@ static const 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",
@@ -986,16 +890,8 @@ static const 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 */
};
@@ -1017,10 +913,6 @@ static const 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
@@ -1035,11 +927,11 @@ static const 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 */
@@ -1064,7 +956,6 @@ enum {
CXT5045_LAPTOP_MICSENSE,
CXT5045_LAPTOP_HPMICSENSE,
CXT5045_BENQ,
- CXT5045_LAPTOP_HP530,
#ifdef CONFIG_SND_DEBUG
CXT5045_TEST,
#endif
@@ -1077,7 +968,6 @@ 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
@@ -1085,8 +975,6 @@ static const char * const cxt5045_models[CXT5045_MODELS] = {
};
static const struct snd_pci_quirk cxt5045_cfg_tbl[] = {
- SND_PCI_QUIRK(0x103c, 0x30d5, "HP 530", CXT5045_LAPTOP_HP530),
- SND_PCI_QUIRK(0x1179, 0xff31, "Toshiba P105", CXT5045_LAPTOP_MICSENSE),
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),
@@ -1118,7 +1006,7 @@ static int patch_cxt5045(struct hda_codec *codec)
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);
@@ -1177,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;
@@ -1213,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;
}
@@ -1637,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 */
@@ -1745,8 +1621,8 @@ static const struct snd_kcontrol_new cxt5051_capture_mixers[] = {
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),
{}
};
@@ -1996,7 +1872,7 @@ 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;
}
@@ -2008,11 +1884,6 @@ 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 const struct hda_channel_mode cxt5066_modes[1] = {
{ 2, NULL },
};
@@ -2027,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;
@@ -2046,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 */
@@ -2068,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)
{
@@ -2182,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);
}
}
@@ -2210,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);
}
}
@@ -2225,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);
}
@@ -2237,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);
}
@@ -2276,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);
}
}
@@ -2301,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);
}
@@ -2324,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);
@@ -2410,124 +2179,10 @@ 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,
const hda_nid_t *dig_pins,
int num_pins)
@@ -2542,10 +2197,6 @@ 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;
}
}
@@ -2582,43 +2233,6 @@ static const struct snd_kcontrol_new cxt5066_mixer_master[] = {
{}
};
-static const 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 const 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 const struct snd_kcontrol_new cxt5066_mixers[] = {
{
.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
@@ -2709,67 +2323,6 @@ static const struct hda_verb cxt5066_init_verbs[] = {
{ } /* end */
};
-static const 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 const struct hda_verb cxt5066_init_verbs_vostro[] = {
/* Port A: headphones */
{0x19, AC_VERB_SET_PIN_WIDGET_CONTROL, 0},
@@ -2955,7 +2508,7 @@ static const 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);
@@ -2965,25 +2518,9 @@ 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? */
@@ -2996,7 +2533,6 @@ enum {
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",
@@ -3006,36 +2542,24 @@ static const char * const cxt5066_models[CXT5066_MODELS] = {
};
static const struct snd_pci_quirk cxt5066_cfg_tbl[] = {
- SND_PCI_QUIRK(0x1025, 0x054c, "Acer Aspire 3830TG", CXT5066_AUTO),
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(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 T510", CXT5066_AUTO),
- SND_PCI_QUIRK(0x17aa, 0x21cf, "Lenovo T520 & W520", CXT5066_AUTO),
- 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(0x17aa, 0x3938, "Lenovo G565", CXT5066_AUTO),
- SND_PCI_QUIRK_VENDOR(0x17aa, "Lenovo", CXT5066_IDEAPAD), /* Fallback for Lenovos without dock mic */
- SND_PCI_QUIRK(0x1b0a, 0x2092, "CyberpowerPC Gamer Xplorer N57001", CXT5066_AUTO),
{}
};
@@ -3116,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;
@@ -3189,1194 +2692,690 @@ 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 int cx_auto_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 conexant_spec *spec = codec->spec;
- hda_nid_t adc = spec->imux_info[spec->cur_mux[0]].adc;
- if (spec->adc_switching) {
- spec->cur_adc = adc;
- spec->cur_adc_stream_tag = stream_tag;
- spec->cur_adc_format = format;
- }
- snd_hda_codec_setup_stream(codec, adc, stream_tag, 0, format);
- return 0;
-}
-
-static int cx_auto_capture_pcm_cleanup(struct hda_pcm_stream *hinfo,
- struct hda_codec *codec,
- struct snd_pcm_substream *substream)
+#ifdef CONFIG_SND_HDA_INPUT_BEEP
+static void cx_auto_parse_beep(struct hda_codec *codec)
{
struct conexant_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 cx_auto_pcm_analog_capture = {
- .substreams = 1,
- .channels_min = 2,
- .channels_max = 2,
- .nid = 0, /* fill later */
- .ops = {
- .prepare = cx_auto_capture_pcm_prepare,
- .cleanup = cx_auto_capture_pcm_cleanup
- },
-};
-
-static const hda_nid_t cx_auto_adc_nids[] = { 0x14 };
-
-#define get_connection_index(codec, mux, nid)\
- snd_hda_get_conn_index(codec, mux, nid, 0)
-
-/* 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)
-{
- int i, nums = *num_dacs;
- hda_nid_t ret = 0;
+ 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
-
-#define DAC_SLAVE_FLAG 0x8000 /* filled dac is a slave */
-
-/* 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;
- }
- }
- return nums;
-}
-
-/* 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 nums,
- int type)
-{
- int i, start = nums;
-
- for (i = 0; i < num_pins; i++, nums++) {
- filled[nums].pin = pins[i];
- filled[nums].type = type;
- filled[nums].dac = get_unassigned_dac(codec, pins[i], dacs, rest);
- if (filled[nums].dac)
- continue;
- if (filled[start].dac && get_connection_index(codec, pins[i], filled[start].dac) >= 0) {
- filled[nums].dac = filled[start].dac | DAC_SLAVE_FLAG;
- continue;
- }
- if (filled[0].dac && get_connection_index(codec, pins[i], filled[0].dac) >= 0) {
- filled[nums].dac = filled[0].dac | DAC_SLAVE_FLAG;
+ if (get_wcaps_type(get_wcaps(codec, nid)) != AC_WID_PIN)
continue;
- }
- snd_printdd("Failed to find a DAC for pin 0x%x", pins[i]);
- }
- return nums;
-}
-
-/* parse analog output paths */
-static void cx_auto_parse_output(struct hda_codec *codec)
-{
- 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, 0,
- 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 || (dac & DAC_SLAVE_FLAG))
+ if (!(snd_hda_query_pin_caps(codec, nid) & AC_PINCAP_EAPD))
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;
-
- for (i = 0; i < cfg->hp_outs; i++) {
- if (is_jack_detectable(codec, cfg->hp_pins[i])) {
- spec->auto_mute = 1;
+ spec->eapds[spec->num_eapds++] = nid;
+ if (spec->num_eapds >= ARRAY_SIZE(spec->eapds))
break;
- }
- }
- if (spec->auto_mute &&
- cfg->line_out_pins[0] &&
- cfg->line_out_type != AUTO_PIN_SPEAKER_OUT &&
- cfg->line_out_pins[0] != cfg->hp_pins[0] &&
- cfg->line_out_pins[0] != cfg->speaker_pins[0]) {
- for (i = 0; i < cfg->line_outs; i++) {
- if (is_jack_detectable(codec, cfg->line_out_pins[i])) {
- spec->detect_line = 1;
- break;
- }
- }
- spec->automute_lines = spec->detect_line;
}
- spec->vmaster_nid = spec->private_dac_nids[0];
+ /* 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;
}
static void cx_auto_turn_eapd(struct hda_codec *codec, int num_pins,
- hda_nid_t *pins, bool on);
-
-static void do_automute(struct hda_codec *codec, int num_pins,
- hda_nid_t *pins, bool on)
+ hda_nid_t *pins, bool on)
{
- struct conexant_spec *spec = codec->spec;
int i;
- for (i = 0; i < num_pins; i++)
- snd_hda_codec_write(codec, pins[i], 0,
- AC_VERB_SET_PIN_WIDGET_CONTROL,
- on ? PIN_OUT : 0);
- if (spec->pin_eapd_ctrls)
- cx_auto_turn_eapd(codec, num_pins, pins, on);
-}
-
-static int detect_jacks(struct hda_codec *codec, int num_pins, hda_nid_t *pins)
-{
- int i, present = 0;
-
for (i = 0; i < num_pins; i++) {
- hda_nid_t nid = pins[i];
- if (!nid || !is_jack_detectable(codec, nid))
- break;
- present |= snd_hda_jack_detect(codec, nid);
+ 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 present;
}
-/* auto-mute/unmute speaker and line outs according to headphone jack */
-static void cx_auto_update_speakers(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;
- int on = 1;
-
- /* turn on HP EAPD when HP jacks are present */
- if (spec->pin_eapd_ctrls) {
- if (spec->auto_mute)
- on = spec->hp_present;
- cx_auto_turn_eapd(codec, cfg->hp_outs, cfg->hp_pins, on);
- }
- /* mute speakers in auto-mode if HP or LO jacks are plugged */
- if (spec->auto_mute)
- on = !(spec->hp_present ||
- (spec->detect_line && spec->line_present));
- do_automute(codec, cfg->speaker_outs, cfg->speaker_pins, 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 (cfg->line_out_pins[0] == cfg->hp_pins[0] ||
- cfg->line_out_pins[0] == cfg->speaker_pins[0])
- return;
- if (spec->auto_mute) {
- /* mute LO in auto-mode when HP jack is present */
- if (cfg->line_out_type == AUTO_PIN_SPEAKER_OUT ||
- spec->automute_lines)
- on = !spec->hp_present;
- else
- on = 1;
- }
- do_automute(codec, cfg->line_outs, cfg->line_out_pins, on);
+ cx_auto_turn_eapd(codec, spec->num_eapds, spec->eapds, enabled);
}
-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 err;
- if (!spec->auto_mute)
- return;
- spec->hp_present = detect_jacks(codec, cfg->hp_outs, cfg->hp_pins);
- cx_auto_update_speakers(codec);
-}
+ err = snd_hda_gen_build_controls(codec);
+ if (err < 0)
+ return err;
-static void cx_auto_line_automute(struct hda_codec *codec)
-{
- struct conexant_spec *spec = codec->spec;
- struct auto_pin_cfg *cfg = &spec->autocfg;
+ err = add_beep_ctls(codec);
+ if (err < 0)
+ return err;
- if (!spec->auto_mute || !spec->detect_line)
- return;
- spec->line_present = detect_jacks(codec, cfg->line_outs,
- cfg->line_out_pins);
- cx_auto_update_speakers(codec);
+ return 0;
}
-static int cx_automute_mode_info(struct snd_kcontrol *kcontrol,
- struct snd_ctl_elem_info *uinfo)
+static int cx_auto_init(struct hda_codec *codec)
{
- struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
struct conexant_spec *spec = codec->spec;
- static const char * const texts2[] = {
- "Disabled", "Enabled"
- };
- static const char * const texts3[] = {
- "Disabled", "Speaker Only", "Line Out+Speaker"
- };
- const char * const *texts;
+ snd_hda_gen_init(codec);
+ if (!spec->dynamic_eapd)
+ cx_auto_turn_eapd(codec, spec->num_eapds, spec->eapds, true);
- uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED;
- uinfo->count = 1;
- if (spec->automute_hp_lo) {
- uinfo->value.enumerated.items = 3;
- texts = texts3;
- } else {
- uinfo->value.enumerated.items = 2;
- texts = texts2;
- }
- 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;
-}
+ snd_hda_apply_fixup(codec, HDA_FIXUP_ACT_INIT);
-static int cx_automute_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;
- unsigned int val;
- if (!spec->auto_mute)
- val = 0;
- else if (!spec->automute_lines)
- val = 1;
- else
- val = 2;
- ucontrol->value.enumerated.item[0] = val;
return 0;
}
-static int cx_automute_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;
+#define cx_auto_free snd_hda_gen_free
- switch (ucontrol->value.enumerated.item[0]) {
- case 0:
- if (!spec->auto_mute)
- return 0;
- spec->auto_mute = 0;
- break;
- case 1:
- if (spec->auto_mute && !spec->automute_lines)
- return 0;
- spec->auto_mute = 1;
- spec->automute_lines = 0;
- break;
- case 2:
- if (!spec->automute_hp_lo)
- return -EINVAL;
- if (spec->auto_mute && spec->automute_lines)
- return 0;
- spec->auto_mute = 1;
- spec->automute_lines = 1;
- break;
- default:
- return -EINVAL;
- }
- cx_auto_update_speakers(codec);
- return 1;
-}
+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
+};
-static const struct snd_kcontrol_new cx_automute_mode_enum[] = {
- {
- .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
- .name = "Auto-Mute Mode",
- .info = cx_automute_mode_info,
- .get = cx_automute_mode_get,
- .put = cx_automute_mode_put,
- },
- { }
+/*
+ * pin fix-up
+ */
+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,
};
-static int cx_auto_mux_enum_info(struct snd_kcontrol *kcontrol,
- struct snd_ctl_elem_info *uinfo)
+/* 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 hda_codec *codec = snd_kcontrol_chip(kcontrol);
struct conexant_spec *spec = codec->spec;
-
- return snd_hda_input_mux_info(&spec->private_imux, uinfo);
+ spec->gen.inv_dmic_split = 1;
}
-static int cx_auto_mux_enum_get(struct snd_kcontrol *kcontrol,
- struct snd_ctl_elem_value *ucontrol)
+static void cxt5066_increase_mic_boost(struct hda_codec *codec,
+ const struct hda_fixup *fix, int action)
{
- struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
- struct conexant_spec *spec = codec->spec;
+ if (action != HDA_FIXUP_ACT_PRE_PROBE)
+ return;
- ucontrol->value.enumerated.item[0] = spec->cur_mux[0];
- return 0;
+ 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));
}
-/* look for the route the given pin from mux and return the index;
- * if do_select is set, actually select the route.
- */
-static int __select_input_connection(struct hda_codec *codec, hda_nid_t mux,
- hda_nid_t pin, hda_nid_t *srcp,
- bool do_select, int depth)
+static void cxt_update_headset_mode(struct hda_codec *codec)
{
- hda_nid_t conn[HDA_MAX_NUM_INPUTS];
- int i, nums;
+ /* 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;
- switch (get_wcaps_type(get_wcaps(codec, mux))) {
- case AC_WID_AUD_IN:
- case AC_WID_AUD_SEL:
- case AC_WID_AUD_MIX:
- break;
- default:
- return -1;
- }
+ hda_nid_t mux_pin = spec->gen.imux_pins[spec->gen.cur_mux[0]];
- nums = snd_hda_get_connections(codec, mux, conn, ARRAY_SIZE(conn));
- for (i = 0; i < nums; i++)
- if (conn[i] == pin) {
- if (do_select)
- snd_hda_codec_write(codec, mux, 0,
- AC_VERB_SET_CONNECT_SEL, i);
- if (srcp)
- *srcp = mux;
- return i;
- }
- depth++;
- if (depth == 2)
- return -1;
- for (i = 0; i < nums; i++) {
- int ret = __select_input_connection(codec, conn[i], pin, srcp,
- do_select, depth);
- if (ret >= 0) {
- if (do_select)
- snd_hda_codec_write(codec, mux, 0,
- AC_VERB_SET_CONNECT_SEL, i);
- return i;
+ 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]);
}
- return -1;
-}
-static void select_input_connection(struct hda_codec *codec, hda_nid_t mux,
- hda_nid_t pin)
-{
- __select_input_connection(codec, mux, pin, NULL, true, 0);
+ snd_hda_gen_update_outputs(codec);
}
-static int get_input_connection(struct hda_codec *codec, hda_nid_t mux,
- 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)
{
- return __select_input_connection(codec, mux, pin, NULL, false, 0);
+ cxt_update_headset_mode(codec);
}
-static int cx_auto_mux_enum_update(struct hda_codec *codec,
- const struct hda_input_mux *imux,
- unsigned int idx)
+static void cxt_fixup_headphone_mic(struct hda_codec *codec,
+ const struct hda_fixup *fix, int action)
{
struct conexant_spec *spec = codec->spec;
- hda_nid_t adc;
- int changed = 1;
- if (!imux->num_items)
- return 0;
- if (idx >= imux->num_items)
- idx = imux->num_items - 1;
- if (spec->cur_mux[0] == idx)
- changed = 0;
- adc = spec->imux_info[idx].adc;
- select_input_connection(codec, spec->imux_info[idx].adc,
- spec->imux_info[idx].pin);
- if (spec->cur_adc && spec->cur_adc != adc) {
- /* stream is running, let's swap the current ADC */
- __snd_hda_codec_cleanup_stream(codec, spec->cur_adc, 1);
- spec->cur_adc = adc;
- snd_hda_codec_setup_stream(codec, adc,
- spec->cur_adc_stream_tag, 0,
- spec->cur_adc_format);
+ 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;
}
- spec->cur_mux[0] = idx;
- return changed;
}
-static int cx_auto_mux_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;
+/* OPLC XO 1.5 fixup */
- return cx_auto_mux_enum_update(codec, &spec->private_imux,
- ucontrol->value.enumerated.item[0]);
-}
+/* 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. */
-static const struct snd_kcontrol_new cx_auto_capture_mixers[] = {
- {
- .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
- .name = "Capture Source",
- .info = cx_auto_mux_enum_info,
- .get = cx_auto_mux_enum_get,
- .put = cx_auto_mux_enum_put
+#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 bool select_automic(struct hda_codec *codec, int idx, bool detect)
+static void olpc_xo_update_mic_boost(struct hda_codec *codec)
{
struct conexant_spec *spec = codec->spec;
- if (idx < 0)
- return false;
- if (detect && !snd_hda_jack_detect(codec, spec->imux_info[idx].pin))
- return false;
- cx_auto_mux_enum_update(codec, &spec->private_imux, idx);
- return true;
+ int ch, val;
+
+ 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);
+ }
}
-/* automatic switch internal and external mic */
-static void cx_auto_automic(struct hda_codec *codec)
+static void olpc_xo_update_mic_pins(struct hda_codec *codec)
{
struct conexant_spec *spec = codec->spec;
+ int cur_input, val;
+ struct nid_path *path;
- if (!spec->auto_mic)
- return;
- if (!select_automic(codec, spec->auto_mic_ext, true))
- if (!select_automic(codec, spec->auto_mic_dock, true))
- select_automic(codec, spec->auto_mic_int, false);
-}
+ cur_input = spec->gen.input_paths[0][spec->gen.cur_mux[0]];
-static void cx_auto_unsol_event(struct hda_codec *codec, unsigned int res)
-{
- switch (snd_hda_jack_get_action(codec, res >> 26)) {
- case CONEXANT_HP_EVENT:
- cx_auto_hp_automute(codec);
- break;
- case CONEXANT_LINE_EVENT:
- cx_auto_line_automute(codec);
- break;
- case CONEXANT_MIC_EVENT:
- cx_auto_automic(codec);
- break;
+ /* 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);
}
- snd_hda_jack_report_sync(codec);
}
-/* check whether the pin config is suitable for auto-mic switching;
- * auto-mic is enabled only when one int-mic and one ext- and/or
- * one dock-mic exist
- */
-static void cx_auto_check_auto_mic(struct hda_codec *codec)
+/* mic_autoswitch hook */
+static void olpc_xo_automic(struct hda_codec *codec, struct hda_jack_tbl *jack)
{
struct conexant_spec *spec = codec->spec;
- int pset[INPUT_PIN_ATTR_NORMAL + 1];
- int i;
+ int saved_cached_write = codec->cached_write;
- for (i = 0; i < ARRAY_SIZE(pset); i++)
- pset[i] = -1;
- for (i = 0; i < spec->private_imux.num_items; i++) {
- hda_nid_t pin = spec->imux_info[i].pin;
- unsigned int def_conf = snd_hda_codec_get_pincfg(codec, pin);
- int type, attr;
- attr = snd_hda_get_input_pin_attr(def_conf);
- if (attr == INPUT_PIN_ATTR_UNUSED)
- return; /* invalid entry */
- if (attr > INPUT_PIN_ATTR_NORMAL)
- attr = INPUT_PIN_ATTR_NORMAL;
- if (attr != INPUT_PIN_ATTR_INT &&
- !is_jack_detectable(codec, pin))
- return; /* non-detectable pin */
- type = get_defcfg_device(def_conf);
- if (type != AC_JACK_MIC_IN &&
- (attr != INPUT_PIN_ATTR_DOCK || type != AC_JACK_LINE_IN))
- return; /* no valid input type */
- if (pset[attr] >= 0)
- return; /* already occupied */
- pset[attr] = i;
- }
- if (pset[INPUT_PIN_ATTR_INT] < 0 ||
- (pset[INPUT_PIN_ATTR_NORMAL] < 0 && pset[INPUT_PIN_ATTR_DOCK]))
- return; /* no input to switch*/
- spec->auto_mic = 1;
- spec->auto_mic_ext = pset[INPUT_PIN_ATTR_NORMAL];
- spec->auto_mic_dock = pset[INPUT_PIN_ATTR_DOCK];
- spec->auto_mic_int = pset[INPUT_PIN_ATTR_INT];
+ 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_parse_input(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;
- struct hda_input_mux *imux;
- int i, j;
-
- imux = &spec->private_imux;
- for (i = 0; i < cfg->num_inputs; i++) {
- for (j = 0; j < spec->num_adc_nids; j++) {
- hda_nid_t adc = spec->adc_nids[j];
- int idx = get_input_connection(codec, adc,
- cfg->inputs[i].pin);
- if (idx >= 0) {
- const char *label;
- label = hda_get_autocfg_input_label(codec, cfg, i);
- spec->imux_info[imux->num_items].index = i;
- spec->imux_info[imux->num_items].boost = 0;
- spec->imux_info[imux->num_items].adc = adc;
- spec->imux_info[imux->num_items].pin =
- cfg->inputs[i].pin;
- snd_hda_add_imux_item(imux, label, idx, NULL);
- break;
- }
- }
- }
- if (imux->num_items >= 2 && cfg->num_inputs == imux->num_items)
- cx_auto_check_auto_mic(codec);
- if (imux->num_items > 1) {
- for (i = 1; i < imux->num_items; i++) {
- if (spec->imux_info[i].adc != spec->imux_info[0].adc) {
- spec->adc_switching = 1;
- break;
- }
- }
- }
-}
-
-/* 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)
-{
- 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;
- }
+ /* 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;
}
- return 0;
}
-static void cx_auto_parse_digital(struct hda_codec *codec)
-{
- 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);
-}
-
-#ifdef CONFIG_SND_HDA_INPUT_BEEP
-static void cx_auto_parse_beep(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;
- hda_nid_t nid, end_nid;
-
- 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;
- }
+ ucontrol->value.integer.value[0] = spec->dc_enable;
+ return 0;
}
-#else
-#define cx_auto_parse_beep(codec)
-#endif
-/* parse EAPDs */
-static void cx_auto_parse_eapd(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;
- hda_nid_t nid, end_nid;
+ int dc_enable = !!ucontrol->value.integer.value[0];
- 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_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;
- }
+ if (dc_enable == spec->dc_enable)
+ return 0;
- /* 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.
- */
- spec->pin_eapd_ctrls = spec->num_eapds > 2;
+ spec->dc_enable = dc_enable;
+ olpc_xo_update_mic_pins(codec);
+ olpc_xo_update_mic_boost(codec);
+ return 1;
}
-static int cx_auto_parse_auto_config(struct hda_codec *codec)
+static int olpc_xo_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;
- int err;
-
- err = snd_hda_parse_pin_def_config(codec, &spec->autocfg, NULL);
- if (err < 0)
- return err;
-
- cx_auto_parse_output(codec);
- cx_auto_parse_input(codec);
- cx_auto_parse_digital(codec);
- cx_auto_parse_beep(codec);
- cx_auto_parse_eapd(codec);
+ ucontrol->value.enumerated.item[0] = spec->dc_input_bias;
return 0;
}
-static void cx_auto_turn_eapd(struct hda_codec *codec, int num_pins,
- hda_nid_t *pins, bool on)
-{
- 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,
- on ? 0x02 : 0);
- }
-}
-
-static void select_connection(struct hda_codec *codec, hda_nid_t pin,
- hda_nid_t src)
+static int olpc_xo_dc_bias_enum_info(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_info *uinfo)
{
- int idx = get_connection_index(codec, pin, src);
- if (idx >= 0)
- snd_hda_codec_write(codec, pin, 0,
- AC_VERB_SET_CONNECT_SEL, idx);
+ return snd_hda_input_mux_info(&olpc_xo_dc_bias, uinfo);
}
-static void mute_outputs(struct hda_codec *codec, int num_nids,
- const hda_nid_t *nids)
+static int olpc_xo_dc_bias_enum_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
{
- int i, val;
+ 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;
- for (i = 0; i < num_nids; i++) {
- hda_nid_t nid = nids[i];
- if (!(get_wcaps(codec, nid) & AC_WCAP_OUT_AMP))
- continue;
- if (query_amp_caps(codec, nid, HDA_OUTPUT) & AC_AMPCAP_MUTE)
- val = AMP_OUT_MUTE;
- else
- val = AMP_OUT_ZERO;
- snd_hda_codec_write(codec, nid, 0,
- AC_VERB_SET_AMP_GAIN_MUTE, val);
- }
-}
+ idx = ucontrol->value.enumerated.item[0];
+ if (idx >= imux->num_items)
+ idx = imux->num_items - 1;
+ if (spec->dc_input_bias == idx)
+ return 0;
-static void enable_unsol_pins(struct hda_codec *codec, int num_pins,
- hda_nid_t *pins, unsigned int action)
-{
- int i;
- for (i = 0; i < num_pins; i++)
- snd_hda_jack_detect_enable(codec, pins[i], action);
+ spec->dc_input_bias = idx;
+ if (spec->dc_enable)
+ olpc_xo_update_mic_pins(codec);
+ return 1;
}
-static void cx_auto_init_output(struct hda_codec *codec)
-{
- struct conexant_spec *spec = codec->spec;
- struct auto_pin_cfg *cfg = &spec->autocfg;
- hda_nid_t nid;
- int i;
-
- mute_outputs(codec, spec->multiout.num_dacs, spec->multiout.dac_nids);
- 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);
- mute_outputs(codec, cfg->hp_outs, cfg->hp_pins);
- mute_outputs(codec, cfg->line_outs, cfg->line_out_pins);
- mute_outputs(codec, cfg->speaker_outs, cfg->speaker_pins);
- for (i = 0; i < spec->dac_info_filled; i++) {
- nid = spec->dac_info[i].dac;
- if (!nid)
- nid = spec->multiout.dac_nids[0];
- else if (nid & DAC_SLAVE_FLAG)
- nid &= ~DAC_SLAVE_FLAG;
- select_connection(codec, spec->dac_info[i].pin, nid);
- }
- if (spec->auto_mute) {
- enable_unsol_pins(codec, cfg->hp_outs, cfg->hp_pins,
- CONEXANT_HP_EVENT);
- spec->hp_present = detect_jacks(codec, cfg->hp_outs,
- cfg->hp_pins);
- if (spec->detect_line) {
- enable_unsol_pins(codec, cfg->line_outs,
- cfg->line_out_pins,
- CONEXANT_LINE_EVENT);
- spec->line_present =
- detect_jacks(codec, cfg->line_outs,
- cfg->line_out_pins);
- }
- }
- cx_auto_update_speakers(codec);
- /* turn on all EAPDs if no individual EAPD control is available */
- if (!spec->pin_eapd_ctrls)
- cx_auto_turn_eapd(codec, spec->num_eapds, spec->eapds, true);
-}
+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 void cx_auto_init_input(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;
- struct auto_pin_cfg *cfg = &spec->autocfg;
- int i, val;
-
- for (i = 0; i < spec->num_adc_nids; i++) {
- hda_nid_t nid = spec->adc_nids[i];
- if (!(get_wcaps(codec, nid) & AC_WCAP_IN_AMP))
- continue;
- if (query_amp_caps(codec, nid, HDA_INPUT) & AC_AMPCAP_MUTE)
- val = AMP_IN_MUTE(0);
- else
- val = AMP_IN_UNMUTE(0);
- snd_hda_codec_write(codec, nid, 0, AC_VERB_SET_AMP_GAIN_MUTE,
- val);
- }
-
- 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) {
- if (spec->auto_mic_ext >= 0) {
- snd_hda_jack_detect_enable(codec,
- cfg->inputs[spec->auto_mic_ext].pin,
- CONEXANT_MIC_EVENT);
- }
- if (spec->auto_mic_dock >= 0) {
- snd_hda_jack_detect_enable(codec,
- cfg->inputs[spec->auto_mic_dock].pin,
- CONEXANT_MIC_EVENT);
- }
- cx_auto_automic(codec);
- } else {
- select_input_connection(codec, spec->imux_info[0].adc,
- spec->imux_info[0].pin);
- }
+ 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 void cx_auto_init_digital(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;
-
- 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);
-}
-
-static int cx_auto_init(struct hda_codec *codec)
-{
- /*snd_hda_sequence_write(codec, cx_auto_init_verbs);*/
- cx_auto_init_output(codec);
- cx_auto_init_input(codec);
- cx_auto_init_digital(codec);
- snd_hda_jack_report_sync(codec);
- 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 char name[32];
- static struct snd_kcontrol_new knew[] = {
- HDA_CODEC_VOLUME(name, 0, 0, 0),
- HDA_CODEC_MUTE(name, 0, 0, 0),
- };
- static const char * const 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 | AC_AMPCAP_MIN_MUTE)))
- break;
- }
- return 0;
-}
+ int i;
-#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)
+ if (action != HDA_FIXUP_ACT_PROBE)
+ return;
-#define cx_auto_add_pb_volume(codec, nid, str, idx) \
- cx_auto_add_volume(codec, str, " Playback", idx, nid, HDA_OUTPUT)
+ 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);
-static int try_add_pb_volume(struct hda_codec *codec, hda_nid_t dac,
- hda_nid_t pin, const char *name, int idx)
-{
- unsigned int caps;
- if (dac && !(dac & DAC_SLAVE_FLAG)) {
- caps = query_amp_caps(codec, dac, HDA_OUTPUT);
- if (caps & AC_AMPCAP_NUM_STEPS)
- return cx_auto_add_pb_volume(codec, dac, name, idx);
- }
- caps = query_amp_caps(codec, pin, HDA_OUTPUT);
- if (caps & AC_AMPCAP_NUM_STEPS)
- return cx_auto_add_pb_volume(codec, pin, name, idx);
- return 0;
-}
+ snd_hda_add_new_ctls(codec, olpc_xo_mixers);
-static int cx_auto_build_output_controls(struct hda_codec *codec)
-{
- 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 try_add_pb_volume(codec, spec->dac_info[0].dac,
- spec->dac_info[0].pin,
- "Master", 0);
-
- for (i = 0; i < spec->dac_info_filled; i++) {
- const char *label;
- int idx, type;
- hda_nid_t dac = spec->dac_info[i].dac;
- 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++;
+ /* 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;
}
- err = try_add_pb_volume(codec, dac,
- spec->dac_info[i].pin,
- label, idx);
- if (err < 0)
- return err;
}
-
- if (spec->auto_mute) {
- err = snd_hda_add_new_ctls(codec, cx_automute_mode_enum);
- if (err < 0)
- return err;
- }
-
- return 0;
}
-static int cx_auto_add_capture_volume(struct hda_codec *codec, hda_nid_t nid,
- const char *label, const char *pfx,
- int cidx)
+/*
+ * 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)
{
- struct conexant_spec *spec = codec->spec;
- int i;
-
- for (i = 0; i < spec->num_adc_nids; i++) {
- hda_nid_t adc_nid = spec->adc_nids[i];
- int idx = get_input_connection(codec, adc_nid, nid);
- if (idx < 0)
- continue;
- if (spec->single_adc_amp)
- idx = 0;
- return cx_auto_add_volume_idx(codec, label, pfx,
- cidx, adc_nid, HDA_INPUT, idx);
- }
- return 0;
+ 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 int cx_auto_add_boost_volume(struct hda_codec *codec, int idx,
- const char *label, int cidx)
+/*
+ * 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)
{
- struct conexant_spec *spec = codec->spec;
- hda_nid_t mux, nid;
- int i, con;
-
- nid = spec->imux_info[idx].pin;
- if (get_wcaps(codec, nid) & AC_WCAP_IN_AMP)
- return cx_auto_add_volume(codec, label, " Boost", cidx,
- nid, HDA_INPUT);
- con = __select_input_connection(codec, spec->imux_info[idx].adc, nid,
- &mux, false, 0);
- if (con < 0)
- return 0;
- for (i = 0; i < idx; i++) {
- if (spec->imux_info[i].boost == mux)
- return 0; /* already present */
- }
-
- if (get_wcaps(codec, mux) & AC_WCAP_OUT_AMP) {
- spec->imux_info[idx].boost = mux;
- return cx_auto_add_volume(codec, label, " Boost", 0,
- mux, HDA_OUTPUT);
- }
- return 0;
+ 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));
}
-static int cx_auto_build_input_controls(struct hda_codec *codec)
-{
- struct conexant_spec *spec = codec->spec;
- struct hda_input_mux *imux = &spec->private_imux;
- const char *prev_label;
- int input_conn[HDA_MAX_NUM_INPUTS];
- int i, j, err, cidx;
- int multi_connection;
-
- if (!imux->num_items)
- return 0;
-
- multi_connection = 0;
- for (i = 0; i < imux->num_items; i++) {
- cidx = get_input_connection(codec, spec->imux_info[i].adc,
- spec->imux_info[i].pin);
- if (cidx < 0)
- continue;
- input_conn[i] = spec->imux_info[i].adc;
- if (!spec->single_adc_amp)
- input_conn[i] |= cidx << 8;
- if (i > 0 && input_conn[i] != input_conn[0])
- multi_connection = 1;
- }
-
- prev_label = NULL;
- cidx = 0;
- for (i = 0; i < imux->num_items; i++) {
- hda_nid_t nid = spec->imux_info[i].pin;
- const char *label;
+/* 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 */
+ {}
+};
- label = hda_get_autocfg_input_label(codec, &spec->autocfg,
- spec->imux_info[i].index);
- if (label == prev_label)
- cidx++;
- else
- cidx = 0;
- prev_label = label;
+/* 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 */
+ {}
+};
- err = cx_auto_add_boost_volume(codec, i, label, cidx);
- if (err < 0)
- return err;
+/* 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 */
+ {}
+};
- if (!multi_connection) {
- if (i > 0)
- continue;
- err = cx_auto_add_capture_volume(codec, nid,
- "Capture", "", cidx);
- } else {
- bool dup_found = false;
- for (j = 0; j < i; j++) {
- if (input_conn[j] == input_conn[i]) {
- dup_found = true;
- break;
- }
- }
- if (dup_found)
- continue;
- err = cx_auto_add_capture_volume(codec, nid,
- label, " Capture", cidx);
+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 */
+ { }
}
- if (err < 0)
- return err;
- }
-
- if (spec->private_imux.num_items > 1 && !spec->auto_mic) {
- err = snd_hda_add_new_ctls(codec, cx_auto_capture_mixers);
- if (err < 0)
- return err;
- }
-
- return 0;
-}
-
-static int cx_auto_build_controls(struct hda_codec *codec)
-{
- struct conexant_spec *spec = codec->spec;
- 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;
- err = conexant_build_controls(codec);
- if (err < 0)
- return err;
- err = snd_hda_jack_add_kctls(codec, &spec->autocfg);
- if (err < 0)
- return err;
- return 0;
-}
-
-static int cx_auto_search_adcs(struct hda_codec *codec)
-{
- struct conexant_spec *spec = codec->spec;
- 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 caps = get_wcaps(codec, nid);
- if (get_wcaps_type(caps) != AC_WID_AUD_IN)
- continue;
- if (caps & AC_WCAP_DIGITAL)
- continue;
- if (snd_BUG_ON(spec->num_adc_nids >=
- ARRAY_SIZE(spec->private_adc_nids)))
- break;
- spec->private_adc_nids[spec->num_adc_nids++] = nid;
- }
- spec->adc_nids = spec->private_adc_nids;
- return 0;
-}
-
-
-static const 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,
+ },
+ [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,
+ },
};
-/*
- * pin fix-up
- */
-struct cxt_pincfg {
- hda_nid_t nid;
- u32 val;
+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 void apply_pincfg(struct hda_codec *codec, const struct cxt_pincfg *cfg)
-{
- for (; cfg->nid; cfg++)
- snd_hda_codec_set_pincfg(codec, cfg->nid, cfg->val);
+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 void apply_pin_fixup(struct hda_codec *codec,
- const struct snd_pci_quirk *quirk,
- const struct cxt_pincfg **table)
-{
- quirk = snd_pci_quirk_lookup(codec->bus->pci, quirk);
- if (quirk) {
- snd_printdd(KERN_INFO "hda_codec: applying pincfg for %s\n",
- quirk->name);
- apply_pincfg(codec, table[quirk->value]);
- }
-}
+static const struct hda_model_fixup cxt5047_fixup_models[] = {
+ { .id = CXT_FIXUP_CAP_MIX_AMP_5047, .name = "cap-mix-amp" },
+ {}
+};
-enum {
- CXT_PINCFG_LENOVO_X200,
+static const struct snd_pci_quirk cxt5051_fixups[] = {
+ SND_PCI_QUIRK(0x17aa, 0x20f2, "Lenovo X200", CXT_PINCFG_LENOVO_X200),
+ {}
};
-static const struct cxt_pincfg cxt_pincfg_lenovo_x200[] = {
- { 0x16, 0x042140ff }, /* HP (seq# overridden) */
- { 0x17, 0x21a11000 }, /* dock-mic */
- { 0x19, 0x2121103f }, /* dock-HP */
+static const struct hda_model_fixup cxt5051_fixup_models[] = {
+ { .id = CXT_PINCFG_LENOVO_X200, .name = "lenovo-x200" },
{}
};
-static const struct cxt_pincfg *cxt_pincfg_tbl[] = {
- [CXT_PINCFG_LENOVO_X200] = cxt_pincfg_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 snd_pci_quirk cxt_fixups[] = {
- SND_PCI_QUIRK(0x17aa, 0x20f2, "Lenovo X200", CXT_PINCFG_LENOVO_X200),
+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" },
{}
};
@@ -4401,42 +3400,100 @@ static int patch_conexant_auto(struct hda_codec *codec)
struct conexant_spec *spec;
int err;
- printk(KERN_INFO "hda_codec: %s: BIOS auto-probing.\n",
- codec->chip_name);
+ 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;
- codec->pin_amp_workaround = 1;
+
+ 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:
- spec->single_adc_amp = 1;
+ 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;
}
- apply_pin_fixup(codec, cxt_fixups, cxt_pincfg_tbl);
+ snd_hda_apply_fixup(codec, HDA_FIXUP_ACT_PRE_PROBE);
- err = cx_auto_search_adcs(codec);
+ err = snd_hda_parse_pin_defcfg(codec, &spec->gen.autocfg, NULL,
+ spec->parse_flags);
if (err < 0)
- return err;
- err = cx_auto_parse_auto_config(codec);
- if (err < 0) {
- kfree(codec->spec);
- codec->spec = NULL;
- return err;
- }
- spec->capture_stream = &cx_auto_pcm_analog_capture;
+ 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
+
/*
*/
@@ -4475,6 +3532,20 @@ static const 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 */
};
@@ -4495,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 1168ebd3fb5..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>
@@ -34,6 +35,8 @@
#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"
@@ -42,15 +45,11 @@ 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 N 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 8
-#define MAX_HDMI_PINS 8
+#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;
@@ -62,30 +61,86 @@ struct hdmi_spec_per_cvt {
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;
- struct hdmi_spec_per_cvt cvts[MAX_HDMI_CVTS];
+ struct snd_array cvts; /* struct hdmi_spec_per_cvt */
+ hda_nid_t cvt_nids[4]; /* only for haswell fix */
int num_pins;
- struct hdmi_spec_per_pin pins[MAX_HDMI_PINS];
- struct hda_pcm pcm_rec[MAX_HDMI_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 */
+
+ struct hdmi_eld temp_eld;
+ struct hdmi_ops ops;
+
+ bool dyn_pin_out;
/*
- * Non-generic ATI/NVIDIA specific
+ * Non-generic VIA/NVIDIA specific
*/
struct hda_multi_out multiout;
- const struct hda_pcm_stream *pcm_playback;
+ struct hda_pcm_stream pcm_playback;
};
@@ -292,40 +347,50 @@ static struct cea_channel_speaker_allocation channel_allocations[] = {
* HDMI routines
*/
-static int pin_nid_to_pin_index(struct hdmi_spec *spec, hda_nid_t pin_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)
{
+ struct hdmi_spec *spec = codec->spec;
int pin_idx;
for (pin_idx = 0; pin_idx < spec->num_pins; pin_idx++)
- if (spec->pins[pin_idx].pin_nid == pin_nid)
+ if (get_pin(spec, pin_idx)->pin_nid == pin_nid)
return pin_idx;
- snd_printk(KERN_WARNING "HDMI: pin nid %d not registered\n", pin_nid);
+ codec_warn(codec, "HDMI: pin nid %d not registered\n", pin_nid);
return -EINVAL;
}
-static int hinfo_to_pin_index(struct hdmi_spec *spec,
+static int hinfo_to_pin_index(struct hda_codec *codec,
struct hda_pcm_stream *hinfo)
{
+ struct hdmi_spec *spec = codec->spec;
int pin_idx;
for (pin_idx = 0; pin_idx < spec->num_pins; pin_idx++)
- if (&spec->pcm_rec[pin_idx].stream[0] == hinfo)
+ if (get_pcm_rec(spec, pin_idx)->stream == hinfo)
return pin_idx;
- snd_printk(KERN_WARNING "HDMI: hinfo %p not registered\n", hinfo);
+ codec_warn(codec, "HDMI: hinfo %p not registered\n", hinfo);
return -EINVAL;
}
-static int cvt_nid_to_cvt_index(struct hdmi_spec *spec, hda_nid_t cvt_nid)
+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 (spec->cvts[cvt_idx].cvt_nid == cvt_nid)
+ if (get_cvt(spec, cvt_idx)->cvt_nid == cvt_nid)
return cvt_idx;
- snd_printk(KERN_WARNING "HDMI: cvt nid %d not registered\n", cvt_nid);
+ codec_warn(codec, "HDMI: cvt nid %d not registered\n", cvt_nid);
return -EINVAL;
}
@@ -333,14 +398,20 @@ 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;
+ struct hdmi_spec *spec = codec->spec;
+ struct hdmi_spec_per_pin *per_pin;
+ struct hdmi_eld *eld;
int pin_idx;
- spec = codec->spec;
uinfo->type = SNDRV_CTL_ELEM_TYPE_BYTES;
pin_idx = kcontrol->private_value;
- uinfo->count = spec->pins[pin_idx].sink_eld.eld_size;
+ 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;
}
@@ -349,14 +420,28 @@ 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;
+ struct hdmi_spec *spec = codec->spec;
+ struct hdmi_spec_per_pin *per_pin;
+ struct hdmi_eld *eld;
int pin_idx;
- spec = codec->spec;
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;
+ }
- memcpy(ucontrol->value.bytes.data,
- spec->pins[pin_idx].sink_eld.eld_buffer, ELD_MAX_SIZE);
+ 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;
}
@@ -382,10 +467,11 @@ static int hdmi_create_eld_ctl(struct hda_codec *codec, int pin_idx,
kctl->private_value = pin_idx;
kctl->id.device = device;
- err = snd_hda_ctl_add(codec, spec->pins[pin_idx].pin_nid, kctl);
+ 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;
}
@@ -421,13 +507,25 @@ static void hdmi_write_dip_byte(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);
- /* Disable pin out until stream is active*/
+
+ 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, 0);
+ AC_VERB_SET_PIN_WIDGET_CONTROL, pin_out);
}
static int hdmi_get_channel_count(struct hda_codec *codec, hda_nid_t cvt_nid)
@@ -444,6 +542,68 @@ static void hdmi_set_channel_count(struct hda_codec *codec,
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
@@ -469,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:
*
@@ -497,7 +668,7 @@ static int hdmi_channel_allocation(struct hdmi_eld *eld, int channels)
* 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];
}
@@ -511,7 +682,18 @@ static int hdmi_channel_allocation(struct hdmi_eld *eld, int channels)
}
}
- 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);
@@ -522,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
@@ -597,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
}
@@ -624,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);
}
@@ -685,43 +1058,33 @@ 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, int pin_idx,
- 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;
- struct hdmi_spec_per_pin *per_pin = &spec->pins[pin_idx];
- hda_nid_t pin_nid = per_pin->pin_nid;
- int channels = substream->runtime->channels;
- struct hdmi_eld *eld;
- int ca;
union audio_infoframe ai;
- eld = &spec->pins[pin_idx].sink_eld;
- if (!eld->monitor_present)
- return;
-
- ca = hdmi_channel_allocation(eld, channels);
-
memset(&ai, 0, sizeof(ai));
- if (eld->conn_type == 0) { /* HDMI */
+ 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 = channels - 1;
+ hdmi_ai->CC02_CT47 = active_channels - 1;
hdmi_ai->CA = ca;
hdmi_checksum_audio_infoframe(hdmi_ai);
- } else if (eld->conn_type == 1) { /* DisplayPort */
+ } 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 = channels - 1;
+ dp_ai->CC02_CT47 = active_channels - 1;
dp_ai->CA = ca;
} else {
- snd_printd("HDMI: unknown connection type at pin %d\n",
+ codec_dbg(codec, "HDMI: unknown connection type at pin %d\n",
pin_nid);
return;
}
@@ -733,11 +1096,10 @@ static void hdmi_setup_audio_infoframe(struct hda_codec *codec, int pin_idx,
*/
if (!hdmi_infoframe_uptodate(codec, pin_nid, ai.bytes,
sizeof(ai))) {
- snd_printdd("hdmi_setup_audio_infoframe: "
- "pin=%d channels=%d\n",
+ codec_dbg(codec,
+ "hdmi_pin_setup_infoframe: pin=%d channels=%d ca=0x%02x\n",
pin_nid,
- channels);
- hdmi_setup_channel_mapping(codec, pin_nid, ca);
+ active_channels, ca);
hdmi_stop_infoframe_trans(codec, pin_nid);
hdmi_fill_audio_infoframe(codec, pin_nid,
ai.bytes, sizeof(ai));
@@ -745,39 +1107,87 @@ static void hdmi_setup_audio_infoframe(struct hda_codec *codec, int pin_idx,
}
}
+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 hdmi_spec_per_pin *per_pin, int repoll);
+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 pin_idx = pin_nid_to_pin_index(codec, jack->nid);
+ if (pin_idx < 0)
+ return;
+
+ if (hdmi_present_sense(get_pin(spec, pin_idx), 1))
+ snd_hda_jack_report_sync(codec);
+}
+
+static void hdmi_intrinsic_event(struct hda_codec *codec, unsigned int res)
+{
int tag = res >> AC_UNSOL_RES_TAG_SHIFT;
- int pin_nid;
- int pd = !!(res & AC_UNSOL_RES_PD);
- int eldv = !!(res & AC_UNSOL_RES_ELDV);
- int pin_idx;
struct hda_jack_tbl *jack;
+ int dev_entry = (res & AC_UNSOL_RES_DE) >> AC_UNSOL_RES_DE_SHIFT;
jack = snd_hda_jack_tbl_get_from_tag(codec, tag);
if (!jack)
return;
- pin_nid = jack->nid;
jack->jack_dirty = 1;
- printk(KERN_INFO
- "HDMI hot plug event: Codec=%d Pin=%d Presence_Detect=%d ELD_Valid=%d\n",
- codec->addr, pin_nid, pd, 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));
- pin_idx = pin_nid_to_pin_index(spec, pin_nid);
- if (pin_idx < 0)
- return;
-
- hdmi_present_sense(&spec->pins[pin_idx], 1);
- snd_hda_jack_report_sync(codec);
+ jack_callback(codec, jack);
}
static void hdmi_non_intrinsic_event(struct hda_codec *codec, unsigned int res)
@@ -787,8 +1197,8 @@ 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: CODEC=%d 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,
@@ -809,7 +1219,7 @@ static void hdmi_unsol_event(struct hda_codec *codec, unsigned int res)
int subtag = (res & AC_UNSOL_RES_SUBTAG) >> AC_UNSOL_RES_SUBTAG_SHIFT;
if (!snd_hda_jack_tbl_get_from_tag(codec, tag)) {
- snd_printd(KERN_INFO "Unexpected HDMI event tag 0x%x\n", tag);
+ codec_dbg(codec, "Unexpected HDMI event tag 0x%x\n", tag);
return;
}
@@ -819,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
*/
@@ -827,24 +1258,26 @@ 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 cvt_nid,
- hda_nid_t pin_nid, u32 stream_tag, int format)
+static int hdmi_pin_hbr_setup(struct hda_codec *codec, hda_nid_t pin_nid,
+ bool hbr)
{
- int pinctl;
- int new_pinctl = 0;
+ int pinctl, new_pinctl;
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",
+ codec_dbg(codec,
+ "hdmi_pin_hbr_setup: NID=0x%x, %spinctl=0x%x\n",
pin_nid,
pinctl == new_pinctl ? "" : "new-",
new_pinctl);
@@ -853,42 +1286,45 @@ static int hdmi_setup_stream(struct hda_codec *codec, hda_nid_t cvt_nid,
snd_hda_codec_write(codec, pin_nid, 0,
AC_VERB_SET_PIN_WIDGET_CONTROL,
new_pinctl);
-
- }
- if (is_hbr_format(format) && !new_pinctl) {
- snd_printdd("hdmi_setup_stream: HBR is not supported\n");
+ } 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;
}
snd_hda_codec_setup_stream(codec, cvt_nid, stream_tag, 0, format);
return 0;
}
-/*
- * HDA PCM callbacks
- */
-static int hdmi_pcm_open(struct hda_pcm_stream *hinfo,
- struct hda_codec *codec,
- struct snd_pcm_substream *substream)
+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 snd_pcm_runtime *runtime = substream->runtime;
- 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 pinctl;
+ int cvt_idx, mux_idx = 0;
- /* Validate hinfo */
- pin_idx = hinfo_to_pin_index(spec, hinfo);
- if (snd_BUG_ON(pin_idx < 0))
- return -EINVAL;
- per_pin = &spec->pins[pin_idx];
- eld = &per_pin->sink_eld;
+ 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 = &spec->cvts[cvt_idx];
+ per_cvt = get_cvt(spec, cvt_idx);
/* Must not already be assigned */
if (per_cvt->assigned)
@@ -902,22 +1338,129 @@ static int hdmi_pcm_open(struct hda_pcm_stream *hinfo,
continue;
break;
}
+
/* 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
+ */
+static int hdmi_pcm_open(struct hda_pcm_stream *hinfo,
+ struct hda_codec *codec,
+ struct snd_pcm_substream *substream)
+{
+ struct hdmi_spec *spec = codec->spec;
+ struct snd_pcm_runtime *runtime = substream->runtime;
+ 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;
+
+ /* 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(codec, per_pin->pin_nid, 0,
+ snd_hda_codec_write_cache(codec, per_pin->pin_nid, 0,
AC_VERB_SET_CONNECT_SEL,
mux_idx);
- 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);
+
+ /* 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);
+
snd_hda_spdif_ctls_assign(codec, pin_idx, per_cvt->cvt_nid);
/* Initially set the converter's capabilities */
@@ -929,10 +1472,14 @@ static int hdmi_pcm_open(struct hda_pcm_stream *hinfo,
/* Restrict capabilities by ELD if this isn't disabled */
if (!static_hdmi_pcm && eld->eld_valid) {
- snd_hdmi_eld_update_pcm_info(eld, hinfo);
+ 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;
+ }
}
/* Store the updated parameters */
@@ -952,13 +1499,12 @@ static int hdmi_pcm_open(struct hda_pcm_stream *hinfo,
static int hdmi_read_pin_conn(struct hda_codec *codec, int pin_idx)
{
struct hdmi_spec *spec = codec->spec;
- struct hdmi_spec_per_pin *per_pin = &spec->pins[pin_idx];
+ 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;
}
@@ -970,10 +1516,13 @@ static int hdmi_read_pin_conn(struct hda_codec *codec, int pin_idx)
return 0;
}
-static void hdmi_present_sense(struct hdmi_spec_per_pin *per_pin, int repoll)
+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_eld *eld = &per_pin->sink_eld;
+ 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
@@ -983,28 +1532,96 @@ static void hdmi_present_sense(struct hdmi_spec_per_pin *per_pin, int repoll)
* specification worked this way. Hence, we just ignore the data in
* the unsolicited response to avoid custom WARs.
*/
- int present = snd_hda_pin_sense(codec, pin_nid);
- bool eld_valid = false;
-
- memset(eld, 0, offsetof(struct hdmi_eld, eld_buffer));
-
- eld->monitor_present = !!(present & AC_PINSENSE_PRESENCE);
- if (eld->monitor_present)
- eld_valid = !!(present & AC_PINSENSE_ELDV);
+ 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
+ eld->eld_valid = false;
- printk(KERN_INFO
+ codec_dbg(codec,
"HDMI status: Codec=%d Pin=%d Presence_Detect=%d ELD_Valid=%d\n",
- codec->addr, pin_nid, eld->monitor_present, eld_valid);
+ 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;
+ }
- if (eld_valid) {
- if (!snd_hdmi_get_eld(eld, codec, pin_nid))
- snd_hdmi_show_eld(eld);
+ 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;
+
+ /*
+ * 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);
+ }
+
+ 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_repoll_eld(struct work_struct *work)
@@ -1015,9 +1632,13 @@ static void hdmi_repoll_eld(struct work_struct *work)
if (per_pin->repoll_count++ > 6)
per_pin->repoll_count = 0;
- hdmi_present_sense(per_pin, per_pin->repoll_count);
+ 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;
@@ -1026,22 +1647,24 @@ static int hdmi_add_pin(struct hda_codec *codec, hda_nid_t pin_nid)
struct hdmi_spec_per_pin *per_pin;
int err;
- caps = snd_hda_param_read(codec, pin_nid, AC_PAR_PIN_CAP);
+ caps = snd_hda_query_pin_caps(codec, pin_nid);
if (!(caps & (AC_PINCAP_HDMI | AC_PINCAP_DP)))
return 0;
- config = snd_hda_codec_read(codec, pin_nid, 0,
- AC_VERB_GET_CONFIG_DEFAULT, 0);
+ config = snd_hda_codec_get_pincfg(codec, pin_nid);
if (get_defcfg_connect(config) == AC_JACK_PORT_NONE)
return 0;
- if (snd_BUG_ON(spec->num_pins >= MAX_HDMI_PINS))
- return -E2BIG;
+ if (is_haswell_plus(codec))
+ intel_haswell_fixup_connect_list(codec, pin_nid);
pin_idx = spec->num_pins;
- per_pin = &spec->pins[pin_idx];
+ 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)
@@ -1055,24 +1678,24 @@ static int hdmi_add_pin(struct hda_codec *codec, hda_nid_t pin_nid)
static int hdmi_add_cvt(struct hda_codec *codec, hda_nid_t cvt_nid)
{
struct hdmi_spec *spec = codec->spec;
- int cvt_idx;
struct hdmi_spec_per_cvt *per_cvt;
unsigned int chans;
int err;
- if (snd_BUG_ON(spec->num_cvts >= MAX_HDMI_CVTS))
- return -E2BIG;
-
chans = get_wcaps(codec, cvt_nid);
chans = get_wcaps_channels(chans);
- cvt_idx = spec->num_cvts;
- per_cvt = &spec->cvts[cvt_idx];
+ 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)
+ if (chans <= 16) {
per_cvt->channels_max = chans;
+ if (chans > spec->channels_max)
+ spec->channels_max = chans;
+ }
err = snd_hda_query_supported_pcm(codec, cvt_nid,
&per_cvt->rates,
@@ -1081,6 +1704,8 @@ static int hdmi_add_cvt(struct hda_codec *codec, hda_nid_t cvt_nid)
if (err < 0)
return err;
+ if (spec->num_cvts < ARRAY_SIZE(spec->cvt_nids))
+ spec->cvt_nids[spec->num_cvts] = cvt_nid;
spec->num_cvts++;
return 0;
@@ -1093,7 +1718,7 @@ static int hdmi_parse_codec(struct hda_codec *codec)
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;
}
@@ -1101,7 +1726,7 @@ static int hdmi_parse_codec(struct hda_codec *codec)
unsigned int caps;
unsigned int type;
- 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))
@@ -1117,29 +1742,24 @@ static int hdmi_parse_codec(struct hda_codec *codec)
}
}
- /*
- * 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 *get_hdmi_pcm_name(int idx)
+static bool check_non_pcm_per_cvt(struct hda_codec *codec, hda_nid_t cvt_nid)
{
- static char names[MAX_HDMI_PINS][8];
- sprintf(&names[idx][0], "HDMI %d", idx);
- return &names[idx][0];
+ 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
*/
@@ -1152,49 +1772,94 @@ static int generic_hdmi_playback_pcm_prepare(struct hda_pcm_stream *hinfo,
{
hda_nid_t cvt_nid = hinfo->nid;
struct hdmi_spec *spec = codec->spec;
- int pin_idx = hinfo_to_pin_index(spec, hinfo);
- hda_nid_t pin_nid = spec->pins[pin_idx].pin_nid;
+ 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);
+ }
- hdmi_set_channel_count(codec, cvt_nid, substream->runtime->channels);
+ 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, pin_idx, substream);
+ hdmi_setup_audio_infoframe(codec, per_pin, non_pcm);
+ mutex_unlock(&per_pin->lock);
- return hdmi_setup_stream(codec, cvt_nid, pin_nid, stream_tag, format);
+ 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;
+}
+
+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;
- snd_hda_codec_cleanup_stream(codec, hinfo->nid);
-
if (hinfo->nid) {
- cvt_idx = cvt_nid_to_cvt_index(spec, hinfo->nid);
+ cvt_idx = cvt_nid_to_cvt_index(codec, hinfo->nid);
if (snd_BUG_ON(cvt_idx < 0))
return -EINVAL;
- per_cvt = &spec->cvts[cvt_idx];
+ 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(spec, hinfo);
+ pin_idx = hinfo_to_pin_index(codec, hinfo);
if (snd_BUG_ON(pin_idx < 0))
return -EINVAL;
- per_pin = &spec->pins[pin_idx];
+ 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);
+ }
- 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);
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;
@@ -1202,10 +1867,169 @@ static int generic_hdmi_playback_pcm_cleanup(struct hda_pcm_stream *hinfo,
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,
};
+/*
+ * 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;
+ 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;
+
+ for (i = 0; i < ARRAY_SIZE(per_pin->chmap); i++)
+ ucontrol->value.integer.value[i] = per_pin->chmap[i];
+ return 0;
+}
+
+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);
+
+ return 0;
+}
+
static int generic_hdmi_build_pcms(struct hda_codec *codec)
{
struct hdmi_spec *spec = codec->spec;
@@ -1214,10 +2038,16 @@ static int generic_hdmi_build_pcms(struct hda_codec *codec)
for (pin_idx = 0; pin_idx < spec->num_pins; pin_idx++) {
struct hda_pcm *info;
struct hda_pcm_stream *pstr;
-
- info = &spec->pcm_rec[pin_idx];
- info->name = get_hdmi_pcm_name(pin_idx);
+ 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];
pstr->substreams = 1;
@@ -1226,7 +2056,7 @@ static int generic_hdmi_build_pcms(struct hda_codec *codec)
}
codec->num_pcms = spec->num_pins;
- codec->pcm_info = spec->pcm_rec;
+ codec->pcm_info = spec->pcm_rec.list;
return 0;
}
@@ -1235,11 +2065,14 @@ 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 = &spec->pins[pin_idx];
- int pcmdev = spec->pcm_rec[pin_idx].device;
+ 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);
}
@@ -1251,23 +2084,23 @@ static int generic_hdmi_build_controls(struct hda_codec *codec)
int pin_idx;
for (pin_idx = 0; pin_idx < spec->num_pins; pin_idx++) {
- struct hdmi_spec_per_pin *per_pin = &spec->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_spdif_out_ctls(codec,
- per_pin->pin_nid,
- per_pin->mux_nids[0]);
+ 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,
- spec->pcm_rec[pin_idx].device);
+ err = hdmi_create_eld_ctl(codec, pin_idx,
+ get_pcm_rec(spec, pin_idx)->device);
if (err < 0)
return err;
@@ -1275,6 +2108,46 @@ static int generic_hdmi_build_controls(struct hda_codec *codec)
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;
}
@@ -1284,46 +2157,161 @@ static int generic_hdmi_init(struct hda_codec *codec)
int pin_idx;
for (pin_idx = 0; pin_idx < spec->num_pins; pin_idx++) {
- struct hdmi_spec_per_pin *per_pin = &spec->pins[pin_idx];
+ struct hdmi_spec_per_pin *per_pin = get_pin(spec, pin_idx);
hda_nid_t pin_nid = per_pin->pin_nid;
- struct hdmi_eld *eld = &per_pin->sink_eld;
hdmi_init_pin(codec, pin_nid);
- snd_hda_jack_detect_enable(codec, pin_nid, pin_nid);
-
- per_pin->codec = codec;
- INIT_DELAYED_WORK(&per_pin->work, hdmi_repoll_eld);
- snd_hda_eld_proc_new(codec, eld, pin_idx);
+ snd_hda_jack_detect_enable_callback(codec, pin_nid, pin_nid,
+ codec->jackpoll_interval > 0 ? jack_callback : NULL);
}
- snd_hda_jack_report_sync(codec);
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 pin_idx;
for (pin_idx = 0; pin_idx < spec->num_pins; pin_idx++) {
- struct hdmi_spec_per_pin *per_pin = &spec->pins[pin_idx];
- struct hdmi_eld *eld = &per_pin->sink_eld;
+ struct hdmi_spec_per_pin *per_pin = get_pin(spec, pin_idx);
cancel_delayed_work(&per_pin->work);
- snd_hda_eld_proc_free(codec, eld);
+ eld_proc_free(per_pin);
}
flush_workqueue(codec->bus->workq);
+ hdmi_array_free(spec);
kfree(spec);
}
+#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;
@@ -1332,13 +2320,31 @@ static int patch_generic_hdmi(struct hda_codec *codec)
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;
+ }
+
+ generic_hdmi_init_per_pins(codec);
init_channel_allocations();
@@ -1352,46 +2358,74 @@ static int patch_generic_hdmi(struct hda_codec *codec)
static int simple_playback_build_pcms(struct hda_codec *codec)
{
struct hdmi_spec *spec = codec->spec;
- struct hda_pcm *info = spec->pcm_rec;
- int i;
-
- codec->num_pcms = spec->num_cvts;
- codec->pcm_info = info;
-
- for (i = 0; i < codec->num_pcms; i++, info++) {
- unsigned int chans;
- struct hda_pcm_stream *pstr;
+ struct hda_pcm *info;
+ unsigned int chans;
+ struct hda_pcm_stream *pstr;
+ struct hdmi_spec_per_cvt *per_cvt;
- chans = get_wcaps(codec, spec->cvts[i].cvt_nid);
- chans = get_wcaps_channels(chans);
+ per_cvt = get_cvt(spec, 0);
+ chans = get_wcaps(codec, per_cvt->cvt_nid);
+ chans = get_wcaps_channels(chans);
- info->name = get_hdmi_pcm_name(i);
- info->pcm_type = HDA_PCM_TYPE_HDMI;
- pstr = &info->stream[SNDRV_PCM_STREAM_PLAYBACK];
- snd_BUG_ON(!spec->pcm_playback);
- *pstr = *spec->pcm_playback;
- pstr->nid = spec->cvts[i].cvt_nid;
- if (pstr->channels_max <= 2 && chans && chans <= 16)
- pstr->channels_max = 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;
- int i;
- for (i = 0; i < codec->num_pcms; i++) {
- err = snd_hda_create_spdif_out_ctls(codec,
- spec->cvts[i].cvt_nid,
- spec->cvts[i].cvt_nid);
- if (err < 0)
- return 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;
}
@@ -1399,6 +2433,7 @@ static void simple_playback_free(struct hda_codec *codec)
{
struct hdmi_spec *spec = codec->spec;
+ hdmi_array_free(spec);
kfree(spec);
}
@@ -1419,7 +2454,15 @@ static const hda_nid_t nvhdmi_con_nids_7x[4] = {
0x6, 0x8, 0xa, 0xc,
};
-static const 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 */
@@ -1447,9 +2490,15 @@ static const 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_2ch);
+ return 0;
+}
+
+static int nvhdmi_7x_init_8ch(struct hda_codec *codec)
{
- snd_hda_sequence_write(codec, nvhdmi_basic_init_7x);
+ snd_hda_sequence_write(codec, nvhdmi_basic_init_7x_8ch);
return 0;
}
@@ -1525,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)
{
@@ -1593,10 +2695,12 @@ static int nvhdmi_8ch_7x_pcm_prepare(struct hda_pcm_stream *hinfo,
unsigned int dataDCC2, channel_id;
int i;
struct hdmi_spec *spec = codec->spec;
- struct hda_spdif_out *spdif =
- snd_hda_spdif_out_of_nid(codec, spec->cvts[0].cvt_nid);
+ 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;
@@ -1697,54 +2801,62 @@ static const struct hda_pcm_stream nvhdmi_pcm_playback_8ch_7x = {
},
};
-static const 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 const struct hda_codec_ops nvhdmi_patch_ops_8ch_7x = {
- .build_controls = simple_playback_build_controls,
- .build_pcms = simple_playback_build_pcms,
- .init = nvhdmi_7x_init,
- .free = simple_playback_free,
-};
-
-static const struct hda_codec_ops nvhdmi_patch_ops_2ch = {
- .build_controls = simple_playback_build_controls,
- .build_pcms = simple_playback_build_pcms,
- .init = nvhdmi_7x_init,
- .free = simple_playback_free,
-};
-
static int patch_nvhdmi_2ch(struct hda_codec *codec)
{
struct hdmi_spec *spec;
+ int err = patch_simple_hdmi(codec, nvhdmi_master_con_nid_7x,
+ nvhdmi_master_pin_nid_7x);
+ if (err < 0)
+ return err;
- spec = kzalloc(sizeof(*spec), GFP_KERNEL);
- if (spec == NULL)
- return -ENOMEM;
+ 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->pcm_playback.rates = SUPPORTED_RATES;
+ spec->pcm_playback.maxbps = SUPPORTED_MAXBPS;
+ spec->pcm_playback.formats = SUPPORTED_FORMATS;
+ return 0;
+}
- codec->spec = spec;
+static int nvhdmi_7x_8ch_build_pcms(struct hda_codec *codec)
+{
+ 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->num_cvts = 1;
- spec->cvts[0].cvt_nid = 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;
}
@@ -1752,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 */
@@ -1768,102 +2881,419 @@ 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->cvts[0].cvt_nid, 0,
- AC_VERB_SET_CVT_CHAN_COUNT, chans - 1);
- /* FIXME: XXX */
- for (i = 0; i < chans; i++) {
- snd_hda_codec_write(codec, spec->cvts[0].cvt_nid, 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 const 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 const 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->pins[0].pin_nid) & AC_WCAP_OUT_AMP)
- snd_hda_codec_write(codec, spec->pins[0].pin_nid, 0,
- AC_VERB_SET_AMP_GAIN_MUTE,
- AMP_OUT_UNMUTE);
return 0;
}
-static const struct hda_codec_ops atihdmi_patch_ops = {
- .build_controls = simple_playback_build_controls,
- .build_pcms = simple_playback_build_pcms,
- .init = atihdmi_init,
- .free = simple_playback_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->cvts[0].cvt_nid = ATIHDMI_CVT_NID;
- spec->pins[0].pin_nid = ATIHDMI_PIN_NID;
- spec->pcm_playback = &atihdmi_pcm_digital_playback;
+ codec->patch_ops.init = atihdmi_init;
+
+ 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);
+ }
- codec->patch_ops = atihdmi_patch_ops;
+ 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
@@ -1872,7 +3302,7 @@ 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 },
@@ -1881,30 +3311,39 @@ static const 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_generic_hdmi },
-{ .id = 0x10de000b, .name = "GPU 0b HDMI/DP", .patch = patch_generic_hdmi },
-{ .id = 0x10de000c, .name = "MCP89 HDMI", .patch = patch_generic_hdmi },
-{ .id = 0x10de000d, .name = "GPU 0d HDMI/DP", .patch = patch_generic_hdmi },
-{ .id = 0x10de0010, .name = "GPU 10 HDMI/DP", .patch = patch_generic_hdmi },
-{ .id = 0x10de0011, .name = "GPU 11 HDMI/DP", .patch = patch_generic_hdmi },
-{ .id = 0x10de0012, .name = "GPU 12 HDMI/DP", .patch = patch_generic_hdmi },
-{ .id = 0x10de0013, .name = "GPU 13 HDMI/DP", .patch = patch_generic_hdmi },
-{ .id = 0x10de0014, .name = "GPU 14 HDMI/DP", .patch = patch_generic_hdmi },
-{ .id = 0x10de0015, .name = "GPU 15 HDMI/DP", .patch = patch_generic_hdmi },
-{ .id = 0x10de0016, .name = "GPU 16 HDMI/DP", .patch = patch_generic_hdmi },
+{ .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_generic_hdmi },
-{ .id = 0x10de0019, .name = "GPU 19 HDMI/DP", .patch = patch_generic_hdmi },
-{ .id = 0x10de001a, .name = "GPU 1a HDMI/DP", .patch = patch_generic_hdmi },
-{ .id = 0x10de001b, .name = "GPU 1b HDMI/DP", .patch = patch_generic_hdmi },
-{ .id = 0x10de001c, .name = "GPU 1c HDMI/DP", .patch = patch_generic_hdmi },
-{ .id = 0x10de0040, .name = "GPU 40 HDMI/DP", .patch = patch_generic_hdmi },
-{ .id = 0x10de0041, .name = "GPU 41 HDMI/DP", .patch = patch_generic_hdmi },
-{ .id = 0x10de0042, .name = "GPU 42 HDMI/DP", .patch = patch_generic_hdmi },
-{ .id = 0x10de0043, .name = "GPU 43 HDMI/DP", .patch = patch_generic_hdmi },
-{ .id = 0x10de0044, .name = "GPU 44 HDMI/DP", .patch = patch_generic_hdmi },
+{ .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 },
@@ -1912,6 +3351,10 @@ static const struct hda_codec_preset snd_hda_preset_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 */
};
@@ -1943,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");
@@ -1958,6 +3410,10 @@ 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 22c73b78ac6..b60824e9040 100644
--- a/sound/pci/hda/patch_realtek.c
+++ b/sound/pci/hda/patch_realtek.c
@@ -6,7 +6,7 @@
* 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,19 +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"
+
+/* keep halting ALC5505 DSP, for power saving */
+#define HALT_REALTEK_ALC5505
/* unsol event tags */
-#define ALC_FRONT_EVENT 0x01
-#define ALC_DCVOL_EVENT 0x02
-#define ALC_HP_EVENT 0x04
-#define ALC_MIC_EVENT 0x08
+#define ALC_DCVOL_EVENT 0x08
/* for GPIO Poll */
#define GPIO_MASK 0x03
@@ -53,6 +55,20 @@ enum {
ALC_INIT_GPIO3,
};
+enum {
+ ALC_HEADSET_MODE_UNKNOWN,
+ ALC_HEADSET_MODE_UNPLUGGED,
+ ALC_HEADSET_MODE_HEADSET,
+ ALC_HEADSET_MODE_MIC,
+ ALC_HEADSET_MODE_HEADPHONE,
+};
+
+enum {
+ ALC_HEADSET_TYPE_UNKNOWN,
+ ALC_HEADSET_TYPE_CTIA,
+ ALC_HEADSET_TYPE_OMTP,
+};
+
struct alc_customize_define {
unsigned int sku_cfg;
unsigned char port_connectivity;
@@ -66,333 +82,51 @@ 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_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 */
-};
-
-enum {
- ALC_AUTOMUTE_PIN, /* change the pin control */
- ALC_AUTOMUTE_AMP, /* mute/unmute the pin AMP */
- ALC_AUTOMUTE_MIXER, /* mute/unmute mixer widget AMP */
-};
-
-#define MAX_VOL_NIDS 0x40
-
struct alc_spec {
+ struct hda_gen_spec gen; /* must be at head */
+
/* codec parameterization */
const struct snd_kcontrol_new *mixers[5]; /* mixer arrays */
unsigned int num_mixers;
- const 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 */
- const struct hda_pcm_stream *stream_analog_playback;
- const struct hda_pcm_stream *stream_analog_capture;
- 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;
-
- /* 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;
- const hda_nid_t *adc_nids;
- const hda_nid_t *capsrc_nids;
- hda_nid_t dig_in_nid; /* digital-in NID; optional */
- hda_nid_t mixer_nid; /* analog-mixer NID */
- DECLARE_BITMAP(vol_ctls, MAX_VOL_NIDS << 1);
- DECLARE_BITMAP(sw_ctls, MAX_VOL_NIDS << 1);
-
- /* 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 */
- unsigned int num_mux_defs;
- const struct hda_input_mux *input_mux;
- unsigned int cur_mux[3];
- hda_nid_t ext_mic_pin;
- hda_nid_t dock_mic_pin;
- hda_nid_t int_mic_pin;
-
- /* 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];
- hda_nid_t imux_pins[HDA_MAX_NUM_INPUTS];
- unsigned int dyn_adc_idx[HDA_MAX_NUM_INPUTS];
- int int_mic_idx, ext_mic_idx, dock_mic_idx; /* for auto-mic */
+ 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);
- void (*automute_hook)(struct hda_codec *codec);
-
- /* for pin sensing */
- unsigned int hp_jack_present:1;
- unsigned int line_jack_present:1;
- unsigned int master_mute:1;
- unsigned int auto_mic:1;
- unsigned int auto_mic_valid_imux:1; /* valid imux for auto-mic */
- unsigned int automute_speaker:1; /* automute speaker outputs */
- unsigned int automute_lo:1; /* automute LO outputs */
- 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 */
- unsigned int keep_vref_in_automute:1; /* Don't clear VREF in automute */
-
- /* other flags */
- unsigned int no_analog :1; /* digital I/O only */
- unsigned int dyn_adc_switch:1; /* switch ADCs (for ALC275) */
- unsigned int single_input_src:1;
- unsigned int vol_in_capsrc:1; /* use capsrc volume (ADC has no vol) */
- unsigned int parse_flags; /* passed to snd_hda_parse_pin_defcfg() */
- unsigned int shared_mic_hp:1; /* HP/Mic-in sharing */
-
- /* auto-mute control */
- int automute_mode;
- hda_nid_t automute_mixer_nid[AUTO_CFG_MAX_OUTS];
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;
unsigned int coef0;
-
- /* fix-up list */
- int fixup_id;
- const struct alc_fixup *fixup_list;
- const char *fixup_name;
-
- /* multi-io */
- int multi_ios;
- struct alc_multi_io multi_io[4];
-
- /* bind volumes */
- struct snd_array bind_ctls;
};
-#define ALC_MODEL_AUTO 0 /* common for all chips */
-
-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;
-}
-
-#define nid_has_mute(codec, nid, dir) \
- check_amp_caps(codec, nid, dir, AC_AMPCAP_MUTE)
-#define nid_has_volume(codec, nid, dir) \
- check_amp_caps(codec, nid, dir, AC_AMPCAP_NUM_STEPS)
-
-/*
- * 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 bool alc_dyn_adc_pcm_resetup(struct hda_codec *codec, int cur)
-{
- struct alc_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;
-}
-
-static inline hda_nid_t get_capsrc(struct alc_spec *spec, int idx)
-{
- return spec->capsrc_nids ?
- spec->capsrc_nids[idx] : spec->adc_nids[idx];
-}
-
-static void call_update_outputs(struct hda_codec *codec);
-
-/* select the given imux item; either unmute exclusively or select the route */
-static int alc_mux_select(struct hda_codec *codec, unsigned int adc_idx,
- unsigned int idx, bool force)
-{
- struct alc_spec *spec = codec->spec;
- const struct hda_input_mux *imux;
- unsigned int mux_idx;
- int i, type, num_conns;
- hda_nid_t nid;
-
- 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];
- if (!imux->num_items)
- return 0;
-
- if (idx >= imux->num_items)
- idx = imux->num_items - 1;
- if (spec->cur_mux[adc_idx] == idx && !force)
- return 0;
- spec->cur_mux[adc_idx] = idx;
-
- /* for shared I/O, change the pin-control accordingly */
- if (spec->shared_mic_hp) {
- /* NOTE: this assumes that there are only two inputs, the
- * first is the real internal mic and the second is HP jack.
- */
- snd_hda_codec_write(codec, spec->autocfg.inputs[1].pin, 0,
- AC_VERB_SET_PIN_WIDGET_CONTROL,
- spec->cur_mux[adc_idx] ?
- PIN_VREF80 : PIN_HP);
- spec->automute_speaker = !spec->cur_mux[adc_idx];
- call_update_outputs(codec);
- }
-
- if (spec->dyn_adc_switch) {
- alc_dyn_adc_pcm_resetup(codec, idx);
- adc_idx = spec->dyn_adc_idx[idx];
- }
-
- nid = get_capsrc(spec, adc_idx);
-
- /* no selection? */
- num_conns = snd_hda_get_conn_list(codec, nid, NULL);
- if (num_conns <= 1)
- return 1;
-
- type = get_wcaps_type(get_wcaps(codec, nid));
- if (type == AC_WID_AUD_MIX) {
- /* Matrix-mixer style (e.g. ALC882) */
- int active = imux->items[idx].index;
- for (i = 0; i < num_conns; i++) {
- unsigned int v = (i == active) ? 0 : HDA_AMP_MUTE;
- snd_hda_codec_amp_stereo(codec, nid, HDA_INPUT, i,
- HDA_AMP_MUTE, v);
- }
- } else {
- /* MUX style (e.g. ALC880) */
- snd_hda_codec_write_cache(codec, nid, 0,
- AC_VERB_SET_CONNECT_SEL,
- imux->items[idx].index);
- }
- return 1;
-}
-
-static int alc_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 alc_mux_select(codec, adc_idx,
- ucontrol->value.enumerated.item[0], false);
-}
-
-/*
- * 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);
-}
-
/*
* Append the given mixer and verb elements for the later use
* The mixer array is referred in build_controls(), and init_verbs are
@@ -405,13 +139,6 @@ static void add_mixer(struct alc_spec *spec, const struct snd_kcontrol_new *mix)
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;
-}
-
/*
* GPIO setup tables, used in initialization
*/
@@ -469,205 +196,33 @@ static void alc_fix_pll_init(struct hda_codec *codec, hda_nid_t nid,
alc_fix_pll(codec);
}
-/*
- * 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, present = 0;
-
- for (i = 0; i < num_pins; i++) {
- hda_nid_t nid = pins[i];
- if (!nid)
- break;
- present |= snd_hda_jack_detect(codec, nid);
- }
- return present;
-}
-
-/* standard HP/line-out auto-mute helper */
-static void do_automute(struct hda_codec *codec, int num_pins, hda_nid_t *pins,
- bool mute, bool hp_out)
-{
- struct alc_spec *spec = codec->spec;
- unsigned int mute_bits = mute ? HDA_AMP_MUTE : 0;
- unsigned int pin_bits = mute ? 0 : (hp_out ? PIN_HP : PIN_OUT);
- int i;
-
- for (i = 0; i < num_pins; i++) {
- hda_nid_t nid = pins[i];
- unsigned int val;
- if (!nid)
- break;
- switch (spec->automute_mode) {
- case ALC_AUTOMUTE_PIN:
- /* 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 = snd_hda_codec_read(codec, nid, 0,
- AC_VERB_GET_PIN_WIDGET_CONTROL, 0);
- val &= ~PIN_HP;
- } else
- val = 0;
- val |= pin_bits;
- snd_hda_codec_write(codec, nid, 0,
- AC_VERB_SET_PIN_WIDGET_CONTROL,
- val);
- break;
- case ALC_AUTOMUTE_AMP:
- snd_hda_codec_amp_stereo(codec, nid, HDA_OUTPUT, 0,
- HDA_AMP_MUTE, mute_bits);
- break;
- case ALC_AUTOMUTE_MIXER:
- nid = spec->automute_mixer_nid[i];
- if (!nid)
- break;
- snd_hda_codec_amp_stereo(codec, nid, HDA_INPUT, 0,
- HDA_AMP_MUTE, mute_bits);
- snd_hda_codec_amp_stereo(codec, nid, HDA_INPUT, 1,
- HDA_AMP_MUTE, mute_bits);
- break;
- }
- }
-}
-
-/* Toggle outputs muting */
-static void update_outputs(struct hda_codec *codec)
-{
- struct alc_spec *spec = codec->spec;
- 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->shared_mic_hp) /* don't change HP-pin when shared with mic */
- do_automute(codec, ARRAY_SIZE(spec->autocfg.hp_pins),
- spec->autocfg.hp_pins, spec->master_mute, true);
-
- if (!spec->automute_speaker)
- on = 0;
- else
- on = spec->hp_jack_present | spec->line_jack_present;
- on |= spec->master_mute;
- do_automute(codec, ARRAY_SIZE(spec->autocfg.speaker_pins),
- spec->autocfg.speaker_pins, on, false);
-
- /* 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;
- do_automute(codec, ARRAY_SIZE(spec->autocfg.line_out_pins),
- spec->autocfg.line_out_pins, on, false);
-}
-
-static void call_update_outputs(struct hda_codec *codec)
-{
- struct alc_spec *spec = codec->spec;
- if (spec->automute_hook)
- spec->automute_hook(codec);
- else
- update_outputs(codec);
-}
-
-/* standard HP-automute helper */
-static void alc_hp_automute(struct hda_codec *codec)
-{
- struct alc_spec *spec = codec->spec;
-
- spec->hp_jack_present =
- detect_jacks(codec, ARRAY_SIZE(spec->autocfg.hp_pins),
- spec->autocfg.hp_pins);
- if (!spec->detect_hp || (!spec->automute_speaker && !spec->automute_lo))
- return;
- call_update_outputs(codec);
-}
-
-/* standard line-out-automute helper */
-static void alc_line_automute(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 val;
+ struct snd_kcontrol *kctl;
+ struct snd_ctl_elem_value *uctl;
- /* check LO jack only when it's different from HP */
- if (spec->autocfg.line_out_pins[0] == spec->autocfg.hp_pins[0])
+ kctl = snd_hda_find_mixer_ctl(codec, "Master Playback Volume");
+ if (!kctl)
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)
+ uctl = kzalloc(sizeof(*uctl), GFP_KERNEL);
+ if (!uctl)
return;
- call_update_outputs(codec);
+ 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);
}
-#define get_connection_index(codec, mux, nid) \
- snd_hda_get_conn_index(codec, mux, nid, 0)
-
-/* standard mic auto-switch helper */
-static void alc_mic_automute(struct hda_codec *codec)
+static void alc880_unsol_event(struct hda_codec *codec, unsigned int res)
{
- struct alc_spec *spec = codec->spec;
- hda_nid_t *pins = spec->imux_pins;
-
- if (!spec->auto_mic || !spec->auto_mic_valid_imux)
- return;
- if (snd_BUG_ON(!spec->adc_nids))
- return;
- if (snd_BUG_ON(spec->int_mic_idx < 0 || spec->ext_mic_idx < 0))
- return;
-
- if (snd_hda_jack_detect(codec, pins[spec->ext_mic_idx]))
- alc_mux_select(codec, 0, spec->ext_mic_idx, false);
- else if (spec->dock_mic_idx >= 0 &&
- snd_hda_jack_detect(codec, pins[spec->dock_mic_idx]))
- alc_mux_select(codec, 0, spec->dock_mic_idx, false);
- else
- alc_mux_select(codec, 0, spec->int_mic_idx, false);
-}
-
-/* handle the specified unsol action (ALC_XXX_EVENT) */
-static void alc_exec_unsol_event(struct hda_codec *codec, int action)
-{
- switch (action) {
- case ALC_HP_EVENT:
- alc_hp_automute(codec);
- break;
- case ALC_FRONT_EVENT:
- alc_line_automute(codec);
- break;
- case ALC_MIC_EVENT:
- alc_mic_automute(codec);
- break;
- }
- snd_hda_jack_report_sync(codec);
-}
-
-/* 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;
- res = snd_hda_jack_get_action(codec, res);
- alc_exec_unsol_event(codec, res);
-}
-
-/* call init functions of standard auto-mute helpers */
-static void alc_inithook(struct hda_codec *codec)
-{
- alc_hp_automute(codec);
- alc_line_automute(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 */
@@ -726,8 +281,12 @@ static void alc_auto_setup_eapd(struct hda_codec *codec, bool on)
*/
static void alc_eapd_shutup(struct hda_codec *codec)
{
+ struct alc_spec *spec = codec->spec;
+
alc_auto_setup_eapd(codec, false);
- msleep(200);
+ if (!spec->no_depop_delay)
+ msleep(200);
+ snd_hda_shutup_pins(codec);
}
/* generic EAPD initialization */
@@ -790,415 +349,37 @@ static void alc_auto_init_amp(struct hda_codec *codec, int type)
}
}
-/*
- * Auto-Mute mode mixer enum support
- */
-static int alc_automute_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;
- static const char * const texts2[] = {
- "Disabled", "Enabled"
- };
- static const char * const texts3[] = {
- "Disabled", "Speaker Only", "Line Out+Speaker"
- };
- const char * const *texts;
-
- uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED;
- uinfo->count = 1;
- if (spec->automute_speaker_possible && spec->automute_lo_possible) {
- uinfo->value.enumerated.items = 3;
- texts = texts3;
- } else {
- uinfo->value.enumerated.items = 2;
- texts = texts2;
- }
- 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;
-}
-
-static int alc_automute_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;
- unsigned int val = 0;
- if (spec->automute_speaker)
- val++;
- if (spec->automute_lo)
- val++;
-
- ucontrol->value.enumerated.item[0] = val;
- return 0;
-}
-
-static int alc_automute_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;
-
- 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 alc_automute_mode_enum = {
- .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
- .name = "Auto-Mute Mode",
- .info = alc_automute_mode_info,
- .get = alc_automute_mode_get,
- .put = alc_automute_mode_put,
-};
-
-static struct snd_kcontrol_new *alc_kcontrol_new(struct alc_spec *spec)
-{
- snd_array_init(&spec->kctls, sizeof(struct snd_kcontrol_new), 32);
- return snd_array_new(&spec->kctls);
-}
-
-static int alc_add_automute_mode_enum(struct hda_codec *codec)
-{
- struct alc_spec *spec = codec->spec;
- struct snd_kcontrol_new *knew;
-
- knew = alc_kcontrol_new(spec);
- if (!knew)
- return -ENOMEM;
- *knew = alc_automute_mode_enum;
- knew->name = kstrdup("Auto-Mute Mode", GFP_KERNEL);
- if (!knew->name)
- return -ENOMEM;
- return 0;
-}
/*
- * Check the availability of HP/line-out auto-mute;
- * Set up appropriately if really supported
+ * Realtek SSID verification
*/
-static void alc_init_automute(struct hda_codec *codec)
-{
- struct alc_spec *spec = codec->spec;
- struct auto_pin_cfg *cfg = &spec->autocfg;
- int present = 0;
- int i;
-
- 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;
-
- 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;
- }
-
- spec->automute_mode = ALC_AUTOMUTE_PIN;
-
- for (i = 0; i < cfg->hp_outs; i++) {
- hda_nid_t nid = cfg->hp_pins[i];
- if (!is_jack_detectable(codec, nid))
- continue;
- snd_printdd("realtek: Enable HP auto-muting on NID 0x%x\n",
- nid);
- snd_hda_jack_detect_enable(codec, nid, ALC_HP_EVENT);
- 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;
- snd_printdd("realtek: Enable Line-Out "
- "auto-muting on NID 0x%x\n", nid);
- snd_hda_jack_detect_enable(codec, nid,
- ALC_FRONT_EVENT);
- spec->detect_lo = 1;
- }
- 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 */
- alc_add_automute_mode_enum(codec);
- spec->unsol_event = alc_sku_unsol_event;
- }
-}
-/* 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;
-}
-
-/* check whether dynamic ADC-switching is available */
-static bool alc_check_dyn_adc_switch(struct hda_codec *codec)
-{
- struct alc_spec *spec = codec->spec;
- struct hda_input_mux *imux = &spec->private_imux[0];
- int i, n, idx;
- hda_nid_t cap, pin;
-
- if (imux != spec->input_mux) /* no dynamic imux? */
- return false;
-
- for (n = 0; n < spec->num_adc_nids; n++) {
- cap = spec->private_capsrc_nids[n];
- for (i = 0; i < imux->num_items; i++) {
- pin = spec->imux_pins[i];
- if (!pin)
- return false;
- if (get_connection_index(codec, cap, pin) < 0)
- break;
- }
- if (i >= imux->num_items)
- return true; /* no ADC-switch is needed */
- }
-
- for (i = 0; i < imux->num_items; i++) {
- pin = spec->imux_pins[i];
- for (n = 0; n < spec->num_adc_nids; n++) {
- cap = spec->private_capsrc_nids[n];
- idx = get_connection_index(codec, cap, pin);
- if (idx >= 0) {
- imux->items[i].index = idx;
- spec->dyn_adc_idx[i] = n;
- break;
- }
- }
- }
-
- snd_printdd("realtek: enabling ADC switching\n");
- spec->dyn_adc_switch = 1;
- return true;
-}
-
-/* rebuild imux for matching with the given auto-mic pins (if not yet) */
-static bool alc_rebuild_imux_for_auto_mic(struct hda_codec *codec)
-{
- struct alc_spec *spec = codec->spec;
- struct hda_input_mux *imux;
- static char * const texts[3] = {
- "Mic", "Internal Mic", "Dock Mic"
- };
- int i;
-
- if (!spec->auto_mic)
- return false;
- imux = &spec->private_imux[0];
- if (spec->input_mux == imux)
- return true;
- spec->imux_pins[0] = spec->ext_mic_pin;
- spec->imux_pins[1] = spec->int_mic_pin;
- spec->imux_pins[2] = spec->dock_mic_pin;
- for (i = 0; i < 3; i++) {
- strcpy(imux->items[i].label, texts[i]);
- if (spec->imux_pins[i]) {
- hda_nid_t pin = spec->imux_pins[i];
- int c;
- for (c = 0; c < spec->num_adc_nids; c++) {
- hda_nid_t cap = get_capsrc(spec, c);
- int idx = get_connection_index(codec, cap, pin);
- if (idx >= 0) {
- imux->items[i].index = idx;
- break;
- }
- }
- imux->num_items = i + 1;
- }
- }
- spec->num_mux_defs = 1;
- spec->input_mux = imux;
- return true;
-}
+/* 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)
-/* check whether all auto-mic pins are valid; setup indices if OK */
-static bool alc_auto_mic_check_imux(struct hda_codec *codec)
+static void alc_fixup_sku_ignore(struct hda_codec *codec,
+ const struct hda_fixup *fix, int action)
{
struct alc_spec *spec = codec->spec;
- const struct hda_input_mux *imux;
-
- if (!spec->auto_mic)
- return false;
- if (spec->auto_mic_valid_imux)
- return true; /* already checked */
-
- /* fill up imux indices */
- if (!alc_check_dyn_adc_switch(codec)) {
- spec->auto_mic = 0;
- return false;
+ if (action == HDA_FIXUP_ACT_PRE_PROBE) {
+ spec->cdefine.fixup = 1;
+ spec->cdefine.sku_cfg = ALC_FIXUP_SKU_IGNORE;
}
-
- imux = spec->input_mux;
- spec->ext_mic_idx = find_idx_in_nid_list(spec->ext_mic_pin,
- spec->imux_pins, imux->num_items);
- spec->int_mic_idx = find_idx_in_nid_list(spec->int_mic_pin,
- spec->imux_pins, imux->num_items);
- spec->dock_mic_idx = find_idx_in_nid_list(spec->dock_mic_pin,
- spec->imux_pins, imux->num_items);
- if (spec->ext_mic_idx < 0 || spec->int_mic_idx < 0) {
- spec->auto_mic = 0;
- return false; /* no corresponding imux */
- }
-
- snd_hda_jack_detect_enable(codec, spec->ext_mic_pin, ALC_MIC_EVENT);
- if (spec->dock_mic_pin)
- snd_hda_jack_detect_enable(codec, spec->dock_mic_pin,
- ALC_MIC_EVENT);
-
- spec->auto_mic_valid_imux = 1;
- spec->auto_mic = 1;
- return true;
}
-/*
- * Check the availability of auto-mic switch;
- * Set up if really supported
- */
-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, dock;
- int i;
-
- if (spec->shared_mic_hp)
- return; /* no auto-mic for the shared I/O */
- spec->ext_mic_idx = spec->int_mic_idx = spec->dock_mic_idx = -1;
-
- fixed = ext = dock = 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 */
- if (cfg->inputs[i].type != AUTO_PIN_MIC)
- return; /* invalid type */
- fixed = nid;
- break;
- case INPUT_PIN_ATTR_UNUSED:
- return; /* invalid entry */
- case INPUT_PIN_ATTR_DOCK:
- if (dock)
- return; /* already occupied */
- if (cfg->inputs[i].type > AUTO_PIN_LINE_IN)
- return; /* invalid type */
- dock = nid;
- break;
- default:
- if (ext)
- return; /* already occupied */
- if (cfg->inputs[i].type != AUTO_PIN_MIC)
- return; /* invalid type */
- ext = nid;
- break;
- }
- }
- if (!ext && dock) {
- ext = dock;
- dock = 0;
+ if (action == HDA_FIXUP_ACT_PROBE) {
+ spec->no_depop_delay = 1;
+ codec->depop_delay = 0;
}
- if (!ext || !fixed)
- return;
- if (!is_jack_detectable(codec, ext))
- return; /* no unsol support */
- if (dock && !is_jack_detectable(codec, dock))
- return; /* no unsol support */
-
- /* check imux indices */
- spec->ext_mic_pin = ext;
- spec->int_mic_pin = fixed;
- spec->dock_mic_pin = dock;
-
- spec->auto_mic = 1;
- if (!alc_auto_mic_check_imux(codec))
- return;
-
- snd_printdd("realtek: Enable auto-mic switch on NID 0x%x/0x%x/0x%x\n",
- ext, fixed, dock);
- spec->unsol_event = alc_sku_unsol_event;
-}
-
-/* check the availabilities of auto-mute and auto-mic switches */
-static void alc_auto_check_switches(struct hda_codec *codec)
-{
- alc_init_automute(codec);
- alc_init_auto_mic(codec);
}
-/*
- * Realtek SSID verification
- */
-
-/* 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;
@@ -1214,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;
@@ -1224,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;
}
@@ -1249,21 +432,30 @@ 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)
{
@@ -1279,9 +471,7 @@ static bool found_in_nid_list(hda_nid_t nid, const hda_nid_t *list, int nums)
* 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;
@@ -1295,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 */
@@ -1311,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;
@@ -1328,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
@@ -1366,25 +557,16 @@ do_sku:
* 15 : 1 --> enable the function "Mute internal speaker
* when the external headphone out jack is plugged"
*/
- if (!spec->autocfg.hp_pins[0] &&
- !(spec->autocfg.line_out_pins[0] &&
- spec->autocfg.line_out_type == AUTO_PIN_HP_OUT)) {
+ 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
- return 1;
- if (found_in_nid_list(nid, spec->autocfg.line_out_pins,
- spec->autocfg.line_outs))
+ nid = ports[tmp];
+ if (found_in_nid_list(nid, spec->gen.autocfg.line_out_pins,
+ spec->gen.autocfg.line_outs))
return 1;
- spec->autocfg.hp_pins[0] = nid;
+ spec->gen.autocfg.hp_pins[0] = nid;
}
return 1;
}
@@ -1393,196 +575,46 @@ do_sku:
* 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, ports[0], ports[1], ports[2], ports[3])) {
+ 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;
}
}
/*
- * Fix-up pin default configurations and add default verbs
- */
-
-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->chained)
- break;
- if (++depth > 10)
- break;
- id = fix->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;
- const struct snd_pci_quirk *q;
- 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) {
- 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) {
- for (q = quirk; q->subvendor; q++) {
- unsigned int vendorid =
- q->subdevice | (q->subvendor << 16);
- if (vendorid == codec->subsystem_id) {
- id = q->value;
-#ifdef CONFIG_SND_DEBUG_VERBOSE
- name = q->name;
-#endif
- break;
- }
- }
- }
-
- spec->fixup_id = id;
- if (id >= 0) {
- spec->fixup_list = fixlist;
- spec->fixup_name = name;
- }
-}
-
-/*
* COEF access helper functions
*/
-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);
}
+#define alc_write_coef_idx(codec, coef_idx, coef_val) \
+ alc_write_coefex_idx(codec, 0x20, coef_idx, coef_val)
+
/* a special bypass for COEF 0; read the cached value at the second time */
static unsigned int alc_get_coef0(struct hda_codec *codec)
{
@@ -1593,307 +625,151 @@ static unsigned int alc_get_coef0(struct hda_codec *codec)
}
/*
- * Digital I/O handling
*/
-/* set right pin controls for digital I/O */
-static void alc_auto_init_digital(struct hda_codec *codec)
+static hda_nid_t get_adc_nid(struct hda_codec *codec, int adc_idx, int imux_idx)
{
- struct alc_spec *spec = codec->spec;
- int i;
- hda_nid_t pin, dac;
-
- for (i = 0; i < spec->autocfg.dig_outs; i++) {
- pin = spec->autocfg.dig_out_pins[i];
- if (!pin)
- continue;
- snd_hda_codec_write(codec, pin, 0,
- AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT);
- if (!i)
- dac = spec->multiout.dig_out_nid;
- else
- dac = spec->slave_dig_outs[i - 1];
- if (!dac || !(get_wcaps(codec, dac) & AC_WCAP_OUT_AMP))
- continue;
- snd_hda_codec_write(codec, dac, 0,
- AC_VERB_SET_AMP_GAIN_MUTE,
- AMP_OUT_UNMUTE);
- }
- pin = spec->autocfg.dig_in_pin;
- if (pin)
- snd_hda_codec_write(codec, pin, 0,
- AC_VERB_SET_PIN_WIDGET_CONTROL,
- PIN_IN);
+ 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];
}
-/* parse digital I/Os and set up NIDs in BIOS auto-parse mode */
-static void alc_auto_parse_digital(struct hda_codec *codec)
+static void alc_inv_dmic_sync_adc(struct hda_codec *codec, int adc_idx)
{
struct alc_spec *spec = codec->spec;
- int i, err, nums;
- hda_nid_t dig_nid;
-
- /* support multiple SPDIFs; the secondary is set up as a slave */
- nums = 0;
- for (i = 0; i < spec->autocfg.dig_outs; i++) {
- hda_nid_t conn[4];
- err = snd_hda_get_connections(codec,
- spec->autocfg.dig_out_pins[i],
- conn, ARRAY_SIZE(conn));
- if (err <= 0)
- continue;
- dig_nid = conn[0]; /* assume the first element is audio-out */
- 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++;
- }
+ struct hda_input_mux *imux = &spec->gen.input_mux;
+ struct nid_path *path;
+ hda_nid_t nid;
+ int i, dir, parm;
+ unsigned int val;
- 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;
- }
- }
+ 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;
+
+ 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);
+
+ /* flush all cached amps at first */
+ snd_hda_codec_flush_cache(codec);
+
+ /* 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);
}
/*
- * capture mixer elements
+ * 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.
*/
-static int alc_cap_vol_info(struct snd_kcontrol *kcontrol,
- struct snd_ctl_elem_info *uinfo)
+static void alc_inv_dmic_sync(struct hda_codec *codec, bool force)
{
- struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
struct alc_spec *spec = codec->spec;
- unsigned long val;
- int err;
+ int src, nums;
- mutex_lock(&codec->control_mutex);
- if (spec->vol_in_capsrc)
- val = HDA_COMPOSE_AMP_VAL(spec->capsrc_nids[0], 3, 0, HDA_OUTPUT);
- else
- val = HDA_COMPOSE_AMP_VAL(spec->adc_nids[0], 3, 0, HDA_INPUT);
- kcontrol->private_value = val;
- err = snd_hda_mixer_amp_volume_info(kcontrol, uinfo);
- mutex_unlock(&codec->control_mutex);
- return err;
+ 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 int alc_cap_vol_tlv(struct snd_kcontrol *kcontrol, int op_flag,
- unsigned int size, unsigned int __user *tlv)
+static void alc_inv_dmic_hook(struct hda_codec *codec,
+ 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 long val;
- int err;
-
- mutex_lock(&codec->control_mutex);
- if (spec->vol_in_capsrc)
- val = HDA_COMPOSE_AMP_VAL(spec->capsrc_nids[0], 3, 0, HDA_OUTPUT);
- else
- val = HDA_COMPOSE_AMP_VAL(spec->adc_nids[0], 3, 0, HDA_INPUT);
- kcontrol->private_value = val;
- err = snd_hda_mixer_amp_tlv(kcontrol, op_flag, size, tlv);
- mutex_unlock(&codec->control_mutex);
- return err;
+ alc_inv_dmic_sync(codec, false);
}
-typedef int (*getput_call_t)(struct snd_kcontrol *kcontrol,
- struct snd_ctl_elem_value *ucontrol);
-
-static int alc_cap_getput_caller(struct snd_kcontrol *kcontrol,
- struct snd_ctl_elem_value *ucontrol,
- getput_call_t func, bool check_adc_switch)
+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 i, err = 0;
-
- mutex_lock(&codec->control_mutex);
- if (check_adc_switch && spec->dyn_adc_switch) {
- for (i = 0; i < spec->num_adc_nids; i++) {
- kcontrol->private_value =
- HDA_COMPOSE_AMP_VAL(spec->adc_nids[i],
- 3, 0, HDA_INPUT);
- err = func(kcontrol, ucontrol);
- if (err < 0)
- goto error;
- }
- } else {
- i = snd_ctl_get_ioffidx(kcontrol, &ucontrol->id);
- if (spec->vol_in_capsrc)
- kcontrol->private_value =
- HDA_COMPOSE_AMP_VAL(spec->capsrc_nids[i],
- 3, 0, HDA_OUTPUT);
- else
- kcontrol->private_value =
- HDA_COMPOSE_AMP_VAL(spec->adc_nids[i],
- 3, 0, HDA_INPUT);
- err = func(kcontrol, ucontrol);
- }
- error:
- 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, false);
-}
-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, true);
+ ucontrol->value.integer.value[0] = !spec->inv_dmic_muted;
+ return 0;
}
-/* 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)
+static int alc_inv_dmic_sw_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
{
- return alc_cap_getput_caller(kcontrol, ucontrol,
- snd_hda_mixer_amp_switch_get, false);
-}
+ struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
+ struct alc_spec *spec = codec->spec;
+ unsigned int val = !ucontrol->value.integer.value[0];
-static int alc_cap_sw_put(struct snd_kcontrol *kcontrol,
- struct snd_ctl_elem_value *ucontrol)
-{
- return alc_cap_getput_caller(kcontrol, ucontrol,
- snd_hda_mixer_amp_switch_put, true);
+ if (val == spec->inv_dmic_muted)
+ return 0;
+ spec->inv_dmic_muted = val;
+ alc_inv_dmic_sync(codec, true);
+ return 0;
}
-#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 }, \
- }
+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,
+};
-#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, \
- }
+static int alc_add_inv_dmic_mixer(struct hda_codec *codec, hda_nid_t nid)
+{
+ struct alc_spec *spec = codec->spec;
-#define DEFINE_CAPMIX(num) \
-static const struct snd_kcontrol_new alc_capture_mixer ## num[] = { \
- _DEFINE_CAPMIX(num), \
- _DEFINE_CAPSRC(num), \
- { } /* end */ \
+ 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;
}
-#define DEFINE_CAPMIX_NOSRC(num) \
-static const struct snd_kcontrol_new alc_capture_mixer_nosrc ## num[] = { \
- _DEFINE_CAPMIX(num), \
- { } /* end */ \
+/* 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)
+{
+ 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);
-
-/*
- * 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",
- "CLFE Playback Volume",
- "Bass Speaker 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",
- "CLFE Playback Switch",
- "Bass Speaker 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 */
@@ -1904,44 +780,20 @@ static const struct snd_kcontrol_new alc_beep_mixer[] = {
};
#endif
-static int __alc_build_controls(struct hda_codec *codec)
+static int alc_build_controls(struct hda_codec *codec)
{
struct alc_spec *spec = codec->spec;
- struct snd_kcontrol *kctl = NULL;
- const 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,
- 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 */
@@ -1960,475 +812,28 @@ 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++) {
- err = snd_hda_add_nid(codec, kctl, i,
- get_capsrc(spec, i));
- if (err < 0)
- return err;
- }
- }
- if (spec->cap_mixer && spec->adc_nids) {
- 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;
}
-static int alc_build_controls(struct hda_codec *codec)
-{
- struct alc_spec *spec = codec->spec;
- int err = __alc_build_controls(codec);
- if (err < 0)
- return err;
- return snd_hda_jack_add_kctls(codec, &spec->autocfg);
-}
-
/*
* Common callbacks
*/
-static void alc_init_special_input_src(struct hda_codec *codec);
-static int alc269_fill_coef(struct hda_codec *codec);
-
static int alc_init(struct hda_codec *codec)
{
struct alc_spec *spec = codec->spec;
- unsigned int i;
-
- if (codec->vendor_id == 0x10ec0269)
- alc269_fill_coef(codec);
-
- 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);
-
- snd_hda_jack_report_sync(codec);
-
- 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 alc_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 alc_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 alc_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 alc_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 alc_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 alc_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 alc_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 alc_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 alc_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 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 alc_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 alc_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 const struct hda_pcm_stream alc_pcm_analog_playback = {
- .substreams = 1,
- .channels_min = 2,
- .channels_max = 8,
- /* NID is set in alc_build_pcms */
- .ops = {
- .open = alc_playback_pcm_open,
- .prepare = alc_playback_pcm_prepare,
- .cleanup = alc_playback_pcm_cleanup
- },
-};
-
-static const struct hda_pcm_stream alc_pcm_analog_capture = {
- .substreams = 1,
- .channels_min = 2,
- .channels_max = 2,
- /* NID is set in alc_build_pcms */
-};
-
-static const struct hda_pcm_stream alc_pcm_analog_alt_playback = {
- .substreams = 1,
- .channels_min = 2,
- .channels_max = 2,
- /* NID is set in alc_build_pcms */
-};
-
-static const struct hda_pcm_stream alc_pcm_analog_alt_capture = {
- .substreams = 2, /* can be overridden */
- .channels_min = 2,
- .channels_max = 2,
- /* NID is set in alc_build_pcms */
- .ops = {
- .prepare = alc_alt_capture_pcm_prepare,
- .cleanup = alc_alt_capture_pcm_cleanup
- },
-};
-
-static const struct hda_pcm_stream alc_pcm_digital_playback = {
- .substreams = 1,
- .channels_min = 2,
- .channels_max = 2,
- /* NID is set in alc_build_pcms */
- .ops = {
- .open = alc_dig_playback_pcm_open,
- .close = alc_dig_playback_pcm_close,
- .prepare = alc_dig_playback_pcm_prepare,
- .cleanup = alc_dig_playback_pcm_cleanup
- },
-};
-
-static const struct hda_pcm_stream alc_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 const 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;
- const struct hda_pcm_stream *p;
- bool have_multi_adcs;
- 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->multiout.num_dacs > 0) {
- p = spec->stream_analog_playback;
- if (!p)
- p = &alc_pcm_analog_playback;
- info->stream[SNDRV_PCM_STREAM_PLAYBACK] = *p;
- info->stream[SNDRV_PCM_STREAM_PLAYBACK].nid = spec->multiout.dac_nids[0];
- }
- if (spec->adc_nids) {
- p = spec->stream_analog_capture;
- if (!p) {
- if (spec->dyn_adc_switch)
- p = &dyn_adc_pcm_analog_capture;
- else
- p = &alc_pcm_analog_capture;
- }
- info->stream[SNDRV_PCM_STREAM_CAPTURE] = *p;
- 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;
- }
- }
- }
+ alc_fix_pll(codec);
+ alc_auto_init_amp(codec, spec->init_amp);
- 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) {
- p = spec->stream_digital_playback;
- if (!p)
- p = &alc_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 = &alc_pcm_digital_capture;
- info->stream[SNDRV_PCM_STREAM_CAPTURE] = *p;
- 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;
- }
+ snd_hda_gen_init(codec);
- 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 &&
- (!spec->input_mux || spec->input_mux->num_items > 1);
- /* Additional Analaog capture for index #2 */
- if (spec->alt_dac_nid || have_multi_adcs) {
- codec->num_pcms = 3;
- info = spec->pcm_rec + 2;
- info->name = spec->stream_name_analog;
- if (spec->alt_dac_nid) {
- p = spec->stream_analog_alt_playback;
- if (!p)
- p = &alc_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] =
- alc_pcm_null_stream;
- info->stream[SNDRV_PCM_STREAM_PLAYBACK].nid = 0;
- }
- if (have_multi_adcs) {
- p = spec->stream_analog_alt_capture;
- if (!p)
- p = &alc_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] =
- alc_pcm_null_stream;
- info->stream[SNDRV_PCM_STREAM_CAPTURE].nid = 0;
- }
- }
+ snd_hda_apply_fixup(codec, HDA_FIXUP_ACT_INIT);
return 0;
}
@@ -2439,55 +844,19 @@ static inline void alc_shutup(struct hda_codec *codec)
if (spec && spec->shutup)
spec->shutup(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);
-}
-
-static void alc_free_bind_ctls(struct hda_codec *codec)
-{
- struct alc_spec *spec = codec->spec;
- if (spec->bind_ctls.list) {
- struct hda_bind_ctls **ctl = spec->bind_ctls.list;
- int i;
- for (i = 0; i < spec->bind_ctls.used; i++)
- kfree(ctl[i]);
- }
- snd_array_free(&spec->bind_ctls);
+ 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);
- alc_free_kctls(codec);
- alc_free_bind_ctls(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)
{
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);
@@ -2500,10 +869,14 @@ static int alc_suspend(struct hda_codec *codec, pm_message_t state)
#ifdef CONFIG_PM
static int alc_resume(struct hda_codec *codec)
{
- msleep(150); /* to avoid pop noise */
+ 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;
}
@@ -2513,20 +886,19 @@ static int alc_resume(struct hda_codec *codec)
*/
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,
+ .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)
{
@@ -2540,7 +912,7 @@ static int alc_codec_rename(struct hda_codec *codec, const char *name)
}
/*
- * Rename codecs appropriately from COEF value
+ * Rename codecs appropriately from COEF value or subvendor id
*/
struct alc_codec_rename_table {
unsigned int vendor_id;
@@ -2549,7 +921,15 @@ struct alc_codec_rename_table {
const char *name;
};
+struct alc_codec_rename_pci_table {
+ unsigned int codec_vendor_id;
+ unsigned short pci_subvendor;
+ unsigned short pci_subdevice;
+ const char *name;
+};
+
static struct alc_codec_rename_table rename_tbl[] = {
+ { 0x10ec0221, 0xf00f, 0x1003, "ALC231" },
{ 0x10ec0269, 0xfff0, 0x3010, "ALC277" },
{ 0x10ec0269, 0xf0f0, 0x2010, "ALC259" },
{ 0x10ec0269, 0xf0f0, 0x3010, "ALC258" },
@@ -2557,6 +937,8 @@ static struct alc_codec_rename_table rename_tbl[] = {
{ 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" },
@@ -2567,9 +949,35 @@ static struct alc_codec_rename_table rename_tbl[] = {
{ } /* 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)
@@ -2577,1374 +985,22 @@ static int alc_codec_rename_from_preset(struct hda_codec *codec)
if ((alc_get_coef0(codec) & p->coef_mask) == p->coef_bits)
return alc_codec_rename(codec, p->name);
}
- return 0;
-}
-
-/*
- * Automatic parse of I/O pins from the BIOS configuration
- */
-
-enum {
- ALC_CTL_WIDGET_VOL,
- ALC_CTL_WIDGET_MUTE,
- ALC_CTL_BIND_MUTE,
- ALC_CTL_BIND_VOL,
- ALC_CTL_BIND_SW,
-};
-static const struct snd_kcontrol_new alc_control_templates[] = {
- HDA_CODEC_VOLUME(NULL, 0, 0, 0),
- HDA_CODEC_MUTE(NULL, 0, 0, 0),
- HDA_BIND_MUTE(NULL, 0, 0, 0),
- HDA_BIND_VOL(NULL, 0),
- HDA_BIND_SW(NULL, 0),
-};
-
-/* 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;
-
- knew = alc_kcontrol_new(spec);
- if (!knew)
- return -ENOMEM;
- *knew = alc_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)
-
-static const char * const channel_name[4] = {
- "Front", "Surround", "CLFE", "Side"
-};
-
-static const char *alc_get_line_out_pfx(struct alc_spec *spec, int ch,
- bool can_be_master, int *index)
-{
- struct auto_pin_cfg *cfg = &spec->autocfg;
-
- *index = 0;
- if (cfg->line_outs == 1 && !spec->multi_ios &&
- !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";
- if (cfg->line_outs == 2)
- return ch ? "Bass Speaker" : "Speaker";
- break;
- case AUTO_PIN_HP_OUT:
- /* for multi-io case, only the primary out */
- if (ch && spec->multi_ios)
- break;
- *index = ch;
- return "Headphone";
- default:
- if (cfg->line_outs == 1 && !spec->multi_ios)
- return "PCM";
- break;
- }
- if (snd_BUG_ON(ch >= ARRAY_SIZE(channel_name)))
- return "PCM";
-
- return channel_name[ch];
-}
-
-/* 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;
-}
-
-/* Parse the codec tree and retrieve ADCs and corresponding capsrc MUXs */
-static int alc_auto_fill_adc_caps(struct hda_codec *codec)
-{
- struct alc_spec *spec = codec->spec;
- hda_nid_t nid;
- hda_nid_t *adc_nids = spec->private_adc_nids;
- hda_nid_t *cap_nids = spec->private_capsrc_nids;
- int max_nums = ARRAY_SIZE(spec->private_adc_nids);
- int i, nums = 0;
-
- if (spec->shared_mic_hp)
- max_nums = 1; /* no multi streams with the shared HP/mic */
-
- nid = codec->start_nid;
- for (i = 0; i < codec->num_nodes; i++, nid++) {
- hda_nid_t src;
- const hda_nid_t *list;
- 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;
- cap_nids[nums] = nid;
- src = nid;
- for (;;) {
- int n;
- type = get_wcaps_type(get_wcaps(codec, src));
- if (type == AC_WID_PIN)
- break;
- if (type == AC_WID_AUD_SEL) {
- cap_nids[nums] = src;
- break;
- }
- n = snd_hda_get_conn_list(codec, src, &list);
- if (n > 1) {
- cap_nids[nums] = src;
- break;
- } else if (n != 1)
- break;
- src = *list;
- }
- if (++nums >= max_nums)
- break;
- }
- spec->adc_nids = spec->private_adc_nids;
- spec->capsrc_nids = spec->private_capsrc_nids;
- spec->num_adc_nids = nums;
- return nums;
-}
-
-/* create playback/capture controls for input pins */
-static int alc_auto_create_input_ctls(struct hda_codec *codec)
-{
- struct alc_spec *spec = codec->spec;
- const struct auto_pin_cfg *cfg = &spec->autocfg;
- hda_nid_t mixer = spec->mixer_nid;
- struct hda_input_mux *imux = &spec->private_imux[0];
- int num_adcs;
- int i, c, err, idx, type_idx = 0;
- const char *prev_label = NULL;
-
- num_adcs = alc_auto_fill_adc_caps(codec);
- if (num_adcs < 0)
- return 0;
-
- 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))
- continue;
-
- label = hda_get_autocfg_input_label(codec, cfg, i);
- if (spec->shared_mic_hp && !strcmp(label, "Misc"))
- label = "Headphone Mic";
- 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;
- }
- }
-
- for (c = 0; c < num_adcs; c++) {
- hda_nid_t cap = get_capsrc(spec, c);
- idx = get_connection_index(codec, cap, pin);
- if (idx >= 0) {
- spec->imux_pins[imux->num_items] = pin;
- snd_hda_add_imux_item(imux, label, idx, NULL);
- break;
- }
- }
- }
-
- spec->num_mux_defs = 1;
- spec->input_mux = imux;
-
- return 0;
-}
-
-/* create a shared input with the headphone out */
-static int alc_auto_create_shared_input(struct hda_codec *codec)
-{
- struct alc_spec *spec = codec->spec;
- struct auto_pin_cfg *cfg = &spec->autocfg;
- unsigned int defcfg;
- hda_nid_t nid;
-
- /* only one internal input pin? */
- if (cfg->num_inputs != 1)
- return 0;
- 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;
-
- if (cfg->hp_outs == 1 && cfg->line_out_type == AUTO_PIN_SPEAKER_OUT)
- nid = cfg->hp_pins[0]; /* OK, we have a single HP-out */
- else if (cfg->line_outs == 1 && cfg->line_out_type == AUTO_PIN_HP_OUT)
- nid = cfg->line_out_pins[0]; /* OK, we have a single line-out */
- else
- return 0; /* both not available */
-
- if (!(snd_hda_query_pin_caps(codec, nid) & AC_PINCAP_IN))
- return 0; /* no input */
-
- cfg->inputs[1].pin = nid;
- cfg->inputs[1].type = AUTO_PIN_MIC;
- cfg->num_inputs = 2;
- spec->shared_mic_hp = 1;
- snd_printdd("realtek: Enable shared I/O jack on NID 0x%x\n", nid);
- return 0;
-}
-
-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 */
- if (nid_has_mute(codec, nid, HDA_OUTPUT))
- snd_hda_codec_write(codec, nid, 0, AC_VERB_SET_AMP_GAIN_MUTE,
- AMP_OUT_UNMUTE);
-}
-
-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 alc_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 (get_wcaps(codec, nid) & AC_WCAP_OUT_AMP)
- snd_hda_codec_write(codec, nid, 0,
- AC_VERB_SET_AMP_GAIN_MUTE,
- AMP_OUT_MUTE);
- }
- }
-
- /* mute all loopback inputs */
- if (spec->mixer_nid) {
- int nums = snd_hda_get_conn_list(codec, spec->mixer_nid, NULL);
- for (i = 0; i < nums; i++)
- snd_hda_codec_write(codec, spec->mixer_nid, 0,
- AC_VERB_SET_AMP_GAIN_MUTE,
- AMP_IN_MUTE(i));
- }
-}
-
-/* convert from MIX nid to DAC */
-static hda_nid_t alc_auto_mix_to_dac(struct hda_codec *codec, hda_nid_t nid)
-{
- hda_nid_t list[5];
- int i, num;
-
- if (get_wcaps_type(get_wcaps(codec, nid)) == AC_WID_AUD_OUT)
- return nid;
- num = snd_hda_get_connections(codec, nid, list, ARRAY_SIZE(list));
- for (i = 0; i < num; i++) {
- if (get_wcaps_type(get_wcaps(codec, list[i])) == AC_WID_AUD_OUT)
- return list[i];
- }
- return 0;
-}
-
-/* go down to the selector widget before the mixer */
-static hda_nid_t alc_go_down_to_selector(struct hda_codec *codec, hda_nid_t pin)
-{
- hda_nid_t srcs[5];
- int num = snd_hda_get_connections(codec, pin, srcs,
- ARRAY_SIZE(srcs));
- if (num != 1 ||
- get_wcaps_type(get_wcaps(codec, srcs[0])) != AC_WID_AUD_SEL)
- return pin;
- return srcs[0];
-}
-
-/* get MIX nid connected to the given pin targeted to DAC */
-static hda_nid_t alc_auto_dac_to_mix(struct hda_codec *codec, hda_nid_t pin,
- hda_nid_t dac)
-{
- hda_nid_t mix[5];
- int i, num;
-
- pin = alc_go_down_to_selector(codec, pin);
- num = snd_hda_get_connections(codec, pin, mix, ARRAY_SIZE(mix));
- for (i = 0; i < num; i++) {
- if (alc_auto_mix_to_dac(codec, mix[i]) == dac)
- return mix[i];
- }
- return 0;
-}
-
-/* select the connection from pin to DAC if needed */
-static int alc_auto_select_dac(struct hda_codec *codec, hda_nid_t pin,
- hda_nid_t dac)
-{
- hda_nid_t mix[5];
- int i, num;
-
- pin = alc_go_down_to_selector(codec, pin);
- num = snd_hda_get_connections(codec, pin, mix, ARRAY_SIZE(mix));
- if (num < 2)
- return 0;
- for (i = 0; i < num; i++) {
- if (alc_auto_mix_to_dac(codec, mix[i]) == dac) {
- snd_hda_codec_update_cache(codec, pin, 0,
- AC_VERB_SET_CONNECT_SEL, i);
- return 0;
- }
- }
- return 0;
-}
-
-/* look for an empty DAC slot */
-static hda_nid_t alc_auto_look_for_dac(struct hda_codec *codec, hda_nid_t pin)
-{
- struct alc_spec *spec = codec->spec;
- hda_nid_t srcs[5];
- int i, num;
-
- pin = alc_go_down_to_selector(codec, pin);
- num = snd_hda_get_connections(codec, pin, srcs, ARRAY_SIZE(srcs));
- for (i = 0; i < num; i++) {
- hda_nid_t nid = alc_auto_mix_to_dac(codec, srcs[i]);
- if (!nid)
- continue;
- if (found_in_nid_list(nid, spec->multiout.dac_nids,
- ARRAY_SIZE(spec->private_dac_nids)))
- continue;
- if (found_in_nid_list(nid, spec->multiout.hp_out_nid,
- ARRAY_SIZE(spec->multiout.hp_out_nid)))
- continue;
- if (found_in_nid_list(nid, spec->multiout.extra_out_nid,
- ARRAY_SIZE(spec->multiout.extra_out_nid)))
- continue;
- return nid;
- }
- return 0;
-}
-
-/* check whether the DAC is reachable from the pin */
-static bool alc_auto_is_dac_reachable(struct hda_codec *codec,
- hda_nid_t pin, hda_nid_t dac)
-{
- hda_nid_t srcs[5];
- int i, num;
-
- pin = alc_go_down_to_selector(codec, pin);
- num = snd_hda_get_connections(codec, pin, srcs, ARRAY_SIZE(srcs));
- for (i = 0; i < num; i++) {
- hda_nid_t nid = alc_auto_mix_to_dac(codec, srcs[i]);
- if (nid == dac)
- return true;
- }
- return false;
-}
-
-static hda_nid_t get_dac_if_single(struct hda_codec *codec, hda_nid_t pin)
-{
- hda_nid_t sel = alc_go_down_to_selector(codec, pin);
- if (snd_hda_get_conn_list(codec, sel, NULL) == 1)
- return alc_auto_look_for_dac(codec, pin);
- return 0;
-}
-
-/* return 0 if no possible DAC is found, 1 if one or more found */
-static int alc_auto_fill_extra_dacs(struct hda_codec *codec, int num_outs,
- const hda_nid_t *pins, hda_nid_t *dacs)
-{
- int i;
-
- if (num_outs && !dacs[0]) {
- dacs[0] = alc_auto_look_for_dac(codec, pins[0]);
- if (!dacs[0])
- return 0;
- }
-
- for (i = 1; i < num_outs; i++)
- dacs[i] = get_dac_if_single(codec, pins[i]);
- for (i = 1; i < num_outs; i++) {
- if (!dacs[i])
- dacs[i] = alc_auto_look_for_dac(codec, pins[i]);
- }
- return 1;
-}
-
-static int alc_auto_fill_multi_ios(struct hda_codec *codec,
- unsigned int location, int offset);
-static hda_nid_t alc_look_for_out_vol_nid(struct hda_codec *codec,
- hda_nid_t pin, hda_nid_t dac);
-
-/* fill in the dac_nids table from the parsed pin configuration */
-static int alc_auto_fill_dac_nids(struct hda_codec *codec)
-{
- struct alc_spec *spec = codec->spec;
- struct auto_pin_cfg *cfg = &spec->autocfg;
- unsigned int location, defcfg;
- int num_pins;
- bool redone = false;
- int i;
-
- again:
- /* set num_dacs once to full for alc_auto_look_for_dac() */
- spec->multiout.num_dacs = cfg->line_outs;
- spec->multiout.hp_out_nid[0] = 0;
- spec->multiout.extra_out_nid[0] = 0;
- memset(spec->private_dac_nids, 0, sizeof(spec->private_dac_nids));
- spec->multiout.dac_nids = spec->private_dac_nids;
- spec->multi_ios = 0;
-
- /* fill hard-wired DACs first */
- if (!redone) {
- for (i = 0; i < cfg->line_outs; i++)
- spec->private_dac_nids[i] =
- get_dac_if_single(codec, cfg->line_out_pins[i]);
- if (cfg->hp_outs)
- spec->multiout.hp_out_nid[0] =
- get_dac_if_single(codec, cfg->hp_pins[0]);
- if (cfg->speaker_outs)
- spec->multiout.extra_out_nid[0] =
- get_dac_if_single(codec, cfg->speaker_pins[0]);
- }
-
- for (i = 0; i < cfg->line_outs; i++) {
- hda_nid_t pin = cfg->line_out_pins[i];
- if (spec->private_dac_nids[i])
- continue;
- spec->private_dac_nids[i] = alc_auto_look_for_dac(codec, pin);
- if (!spec->private_dac_nids[i] && !redone) {
- /* if we can't find primary DACs, re-probe without
- * checking the hard-wired DACs
- */
- redone = true;
- goto again;
- }
- }
-
- /* 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;
- }
- }
-
- if (cfg->line_outs == 1 && cfg->line_out_type != AUTO_PIN_SPEAKER_OUT) {
- /* try to fill multi-io first */
- defcfg = snd_hda_codec_get_pincfg(codec, cfg->line_out_pins[0]);
- location = get_defcfg_location(defcfg);
-
- num_pins = alc_auto_fill_multi_ios(codec, location, 0);
- if (num_pins > 0) {
- spec->multi_ios = num_pins;
- spec->ext_channel_count = 2;
- spec->multiout.num_dacs = num_pins + 1;
- }
- }
-
- if (cfg->line_out_type != AUTO_PIN_HP_OUT)
- alc_auto_fill_extra_dacs(codec, cfg->hp_outs, cfg->hp_pins,
- spec->multiout.hp_out_nid);
- if (cfg->line_out_type != AUTO_PIN_SPEAKER_OUT) {
- int err = alc_auto_fill_extra_dacs(codec, cfg->speaker_outs,
- cfg->speaker_pins,
- spec->multiout.extra_out_nid);
- /* if no speaker volume is assigned, try again as the primary
- * output
- */
- if (!err && 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;
- redone = false;
- goto again;
- }
- }
-
- if (!spec->multi_ios &&
- cfg->line_out_type == AUTO_PIN_SPEAKER_OUT &&
- cfg->hp_outs) {
- /* try multi-ios with HP + inputs */
- defcfg = snd_hda_codec_get_pincfg(codec, cfg->hp_pins[0]);
- location = get_defcfg_location(defcfg);
-
- num_pins = alc_auto_fill_multi_ios(codec, location, 1);
- if (num_pins > 0) {
- spec->multi_ios = num_pins;
- spec->ext_channel_count = 2;
- spec->multiout.num_dacs = num_pins + 1;
- }
- }
-
- if (cfg->line_out_pins[0])
- spec->vmaster_nid =
- alc_look_for_out_vol_nid(codec, cfg->line_out_pins[0],
- spec->multiout.dac_nids[0]);
- return 0;
-}
-
-static inline unsigned int get_ctl_pos(unsigned int data)
-{
- hda_nid_t nid = get_amp_nid_(data);
- unsigned int dir;
- if (snd_BUG_ON(nid >= MAX_VOL_NIDS))
- return 0;
- dir = get_amp_direction_(data);
- return (nid << 1) | dir;
-}
-
-#define is_ctl_used(bits, data) \
- test_bit(get_ctl_pos(data), bits)
-#define mark_ctl_usage(bits, data) \
- set_bit(get_ctl_pos(data), bits)
-
-static int alc_auto_add_vol_ctl(struct hda_codec *codec,
- const char *pfx, int cidx,
- hda_nid_t nid, unsigned int chs)
-{
- struct alc_spec *spec = codec->spec;
- unsigned int val;
- if (!nid)
- return 0;
- val = HDA_COMPOSE_AMP_VAL(nid, chs, 0, HDA_OUTPUT);
- if (is_ctl_used(spec->vol_ctls, val) && chs != 2) /* exclude LFE */
- return 0;
- mark_ctl_usage(spec->vol_ctls, val);
- return __add_pb_vol_ctrl(codec->spec, ALC_CTL_WIDGET_VOL, pfx, cidx,
- val);
-}
-
-static int alc_auto_add_stereo_vol(struct hda_codec *codec,
- const char *pfx, int cidx,
- hda_nid_t nid)
-{
- int chs = 1;
- if (get_wcaps(codec, nid) & AC_WCAP_STEREO)
- chs = 3;
- return alc_auto_add_vol_ctl(codec, pfx, cidx, nid, chs);
-}
-
-/* create a mute-switch for the given mixer widget;
- * if it has multiple sources (e.g. DAC and loopback), create a bind-mute
- */
-static int alc_auto_add_sw_ctl(struct hda_codec *codec,
- const char *pfx, int cidx,
- hda_nid_t nid, unsigned int chs)
-{
- struct alc_spec *spec = codec->spec;
- int wid_type;
- int type;
- unsigned long val;
- if (!nid)
- return 0;
- wid_type = get_wcaps_type(get_wcaps(codec, nid));
- if (wid_type == AC_WID_PIN || wid_type == AC_WID_AUD_OUT) {
- type = ALC_CTL_WIDGET_MUTE;
- val = HDA_COMPOSE_AMP_VAL(nid, chs, 0, HDA_OUTPUT);
- } else if (snd_hda_get_conn_list(codec, nid, NULL) == 1) {
- type = ALC_CTL_WIDGET_MUTE;
- val = HDA_COMPOSE_AMP_VAL(nid, chs, 0, HDA_INPUT);
- } else {
- type = ALC_CTL_BIND_MUTE;
- val = HDA_COMPOSE_AMP_VAL(nid, chs, 2, HDA_INPUT);
- }
- if (is_ctl_used(spec->sw_ctls, val) && chs != 2) /* exclude LFE */
- return 0;
- mark_ctl_usage(spec->sw_ctls, val);
- return __add_pb_sw_ctrl(codec->spec, type, pfx, cidx, val);
-}
-
-static int alc_auto_add_stereo_sw(struct hda_codec *codec, const char *pfx,
- int cidx, hda_nid_t nid)
-{
- int chs = 1;
- if (get_wcaps(codec, nid) & AC_WCAP_STEREO)
- chs = 3;
- return alc_auto_add_sw_ctl(codec, pfx, cidx, nid, chs);
-}
-
-static hda_nid_t alc_look_for_out_mute_nid(struct hda_codec *codec,
- hda_nid_t pin, hda_nid_t dac)
-{
- hda_nid_t mix = alc_auto_dac_to_mix(codec, pin, dac);
- if (nid_has_mute(codec, pin, HDA_OUTPUT))
- return pin;
- else if (mix && nid_has_mute(codec, mix, HDA_INPUT))
- return mix;
- else if (nid_has_mute(codec, dac, HDA_OUTPUT))
- return dac;
- return 0;
-}
-
-static hda_nid_t alc_look_for_out_vol_nid(struct hda_codec *codec,
- hda_nid_t pin, hda_nid_t dac)
-{
- hda_nid_t mix = alc_auto_dac_to_mix(codec, pin, dac);
- if (nid_has_volume(codec, dac, HDA_OUTPUT))
- return dac;
- else if (nid_has_volume(codec, mix, HDA_OUTPUT))
- return mix;
- else if (nid_has_volume(codec, pin, HDA_OUTPUT))
- return pin;
- return 0;
-}
-
-/* add playback controls from the parsed DAC table */
-static int alc_auto_create_multi_out_ctls(struct hda_codec *codec,
- const struct auto_pin_cfg *cfg)
-{
- struct alc_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;
- hda_nid_t dac, pin;
- hda_nid_t sw, vol;
-
- dac = spec->multiout.dac_nids[i];
- if (!dac)
- continue;
- if (i >= cfg->line_outs)
- pin = spec->multi_io[i - 1].pin;
- else
- pin = cfg->line_out_pins[i];
-
- sw = alc_look_for_out_mute_nid(codec, pin, dac);
- vol = alc_look_for_out_vol_nid(codec, pin, dac);
- name = alc_get_line_out_pfx(spec, i, true, &index);
- if (!name || !strcmp(name, "CLFE")) {
- /* Center/LFE */
- err = alc_auto_add_vol_ctl(codec, "Center", 0, vol, 1);
- if (err < 0)
- return err;
- err = alc_auto_add_vol_ctl(codec, "LFE", 0, vol, 2);
- if (err < 0)
- return err;
- err = alc_auto_add_sw_ctl(codec, "Center", 0, sw, 1);
- if (err < 0)
- return err;
- err = alc_auto_add_sw_ctl(codec, "LFE", 0, sw, 2);
- if (err < 0)
- return err;
- } else {
- err = alc_auto_add_stereo_vol(codec, name, index, vol);
- if (err < 0)
- return err;
- err = alc_auto_add_stereo_sw(codec, name, index, sw);
- if (err < 0)
- return err;
- }
- }
- return 0;
-}
-static int alc_auto_create_extra_out(struct hda_codec *codec, hda_nid_t pin,
- hda_nid_t dac, const char *pfx,
- int cidx)
-{
- struct alc_spec *spec = codec->spec;
- hda_nid_t sw, vol;
- int err;
-
- if (!dac) {
- unsigned int val;
- /* the corresponding DAC is already occupied */
- if (!(get_wcaps(codec, pin) & AC_WCAP_OUT_AMP))
- return 0; /* no way */
- /* create a switch only */
- val = HDA_COMPOSE_AMP_VAL(pin, 3, 0, HDA_OUTPUT);
- if (is_ctl_used(spec->sw_ctls, val))
- return 0; /* already created */
- mark_ctl_usage(spec->sw_ctls, val);
- return __add_pb_sw_ctrl(spec, ALC_CTL_WIDGET_MUTE, pfx, cidx, val);
- }
-
- sw = alc_look_for_out_mute_nid(codec, pin, dac);
- vol = alc_look_for_out_vol_nid(codec, pin, dac);
- err = alc_auto_add_stereo_vol(codec, pfx, cidx, vol);
- if (err < 0)
- return err;
- err = alc_auto_add_stereo_sw(codec, pfx, cidx, sw);
- if (err < 0)
- return err;
- return 0;
-}
-
-static struct hda_bind_ctls *new_bind_ctl(struct hda_codec *codec,
- unsigned int nums,
- struct hda_ctl_ops *ops)
-{
- struct alc_spec *spec = codec->spec;
- struct hda_bind_ctls **ctlp, *ctl;
- snd_array_init(&spec->bind_ctls, sizeof(ctl), 8);
- ctlp = snd_array_new(&spec->bind_ctls);
- if (!ctlp)
- return NULL;
- ctl = kzalloc(sizeof(*ctl) + sizeof(long) * (nums + 1), GFP_KERNEL);
- *ctlp = ctl;
- if (ctl)
- ctl->ops = ops;
- return ctl;
-}
-
-/* add playback controls for speaker and HP outputs */
-static int alc_auto_create_extra_outs(struct hda_codec *codec, int num_pins,
- const hda_nid_t *pins,
- const hda_nid_t *dacs,
- const char *pfx)
-{
- struct alc_spec *spec = codec->spec;
- struct hda_bind_ctls *ctl;
- char name[32];
- int i, n, err;
-
- if (!num_pins || !pins[0])
+ if (!codec->bus->pci)
return 0;
-
- if (num_pins == 1) {
- hda_nid_t dac = *dacs;
- if (!dac)
- dac = spec->multiout.dac_nids[0];
- return alc_auto_create_extra_out(codec, *pins, dac, pfx, 0);
- }
-
- if (dacs[num_pins - 1]) {
- /* OK, we have a multi-output system with individual volumes */
- for (i = 0; i < num_pins; i++) {
- if (num_pins >= 3) {
- snprintf(name, sizeof(name), "%s %s",
- pfx, channel_name[i]);
- err = alc_auto_create_extra_out(codec, pins[i], dacs[i],
- name, 0);
- } else {
- err = alc_auto_create_extra_out(codec, pins[i], dacs[i],
- pfx, i);
- }
- if (err < 0)
- return err;
- }
- return 0;
- }
-
- /* Let's create a bind-controls */
- ctl = new_bind_ctl(codec, num_pins, &snd_hda_bind_sw);
- if (!ctl)
- return -ENOMEM;
- n = 0;
- for (i = 0; i < num_pins; i++) {
- if (get_wcaps(codec, pins[i]) & AC_WCAP_OUT_AMP)
- ctl->values[n++] =
- HDA_COMPOSE_AMP_VAL(pins[i], 3, 0, HDA_OUTPUT);
- }
- if (n) {
- snprintf(name, sizeof(name), "%s Playback Switch", pfx);
- err = add_control(spec, ALC_CTL_BIND_SW, name, 0, (long)ctl);
- if (err < 0)
- return err;
- }
-
- ctl = new_bind_ctl(codec, num_pins, &snd_hda_bind_vol);
- if (!ctl)
- return -ENOMEM;
- n = 0;
- for (i = 0; i < num_pins; i++) {
- hda_nid_t vol;
- if (!pins[i] || !dacs[i])
+ for (q = rename_pci_tbl; q->codec_vendor_id; q++) {
+ if (q->codec_vendor_id != codec->vendor_id)
continue;
- vol = alc_look_for_out_vol_nid(codec, pins[i], dacs[i]);
- if (vol)
- ctl->values[n++] =
- HDA_COMPOSE_AMP_VAL(vol, 3, 0, HDA_OUTPUT);
- }
- if (n) {
- snprintf(name, sizeof(name), "%s Playback Volume", pfx);
- err = add_control(spec, ALC_CTL_BIND_VOL, name, 0, (long)ctl);
- if (err < 0)
- return err;
- }
- return 0;
-}
-
-static int alc_auto_create_hp_out(struct hda_codec *codec)
-{
- struct alc_spec *spec = codec->spec;
- return alc_auto_create_extra_outs(codec, spec->autocfg.hp_outs,
- spec->autocfg.hp_pins,
- spec->multiout.hp_out_nid,
- "Headphone");
-}
-
-static int alc_auto_create_speaker_out(struct hda_codec *codec)
-{
- struct alc_spec *spec = codec->spec;
- return alc_auto_create_extra_outs(codec, spec->autocfg.speaker_outs,
- spec->autocfg.speaker_pins,
- spec->multiout.extra_out_nid,
- "Speaker");
-}
-
-static void alc_auto_set_output_and_unmute(struct hda_codec *codec,
- hda_nid_t pin, int pin_type,
- hda_nid_t dac)
-{
- int i, num;
- hda_nid_t nid, mix = 0;
- hda_nid_t srcs[HDA_MAX_CONNECTIONS];
-
- alc_set_pin_output(codec, pin, pin_type);
- nid = alc_go_down_to_selector(codec, pin);
- num = snd_hda_get_connections(codec, nid, srcs, ARRAY_SIZE(srcs));
- for (i = 0; i < num; i++) {
- if (alc_auto_mix_to_dac(codec, srcs[i]) != dac)
+ if (q->pci_subvendor != codec->bus->pci->subsystem_vendor)
continue;
- mix = srcs[i];
- break;
- }
- if (!mix)
- return;
-
- /* need the manual connection? */
- if (num > 1)
- snd_hda_codec_write(codec, nid, 0, AC_VERB_SET_CONNECT_SEL, i);
- /* unmute mixer widget inputs */
- if (nid_has_mute(codec, mix, HDA_INPUT)) {
- snd_hda_codec_write(codec, mix, 0, AC_VERB_SET_AMP_GAIN_MUTE,
- AMP_IN_UNMUTE(0));
- snd_hda_codec_write(codec, mix, 0, AC_VERB_SET_AMP_GAIN_MUTE,
- AMP_IN_UNMUTE(1));
- }
- /* initialize volume */
- nid = alc_look_for_out_vol_nid(codec, pin, dac);
- if (nid)
- snd_hda_codec_write(codec, nid, 0, AC_VERB_SET_AMP_GAIN_MUTE,
- AMP_OUT_ZERO);
-
- /* unmute DAC if it's not assigned to a mixer */
- nid = alc_look_for_out_mute_nid(codec, pin, dac);
- if (nid == mix && nid_has_mute(codec, dac, HDA_OUTPUT))
- snd_hda_codec_write(codec, dac, 0, AC_VERB_SET_AMP_GAIN_MUTE,
- AMP_OUT_ZERO);
-}
-
-static void alc_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)
- alc_auto_set_output_and_unmute(codec, nid, pin_type,
- spec->multiout.dac_nids[i]);
- }
-}
-
-static void alc_auto_init_extra_out(struct hda_codec *codec)
-{
- struct alc_spec *spec = codec->spec;
- int i;
- hda_nid_t pin, dac;
-
- for (i = 0; i < spec->autocfg.hp_outs; i++) {
- if (spec->autocfg.line_out_type == AUTO_PIN_HP_OUT)
- break;
- pin = spec->autocfg.hp_pins[i];
- if (!pin)
- break;
- dac = spec->multiout.hp_out_nid[i];
- if (!dac) {
- if (i > 0 && spec->multiout.hp_out_nid[0])
- dac = spec->multiout.hp_out_nid[0];
- else
- dac = spec->multiout.dac_nids[0];
- }
- alc_auto_set_output_and_unmute(codec, pin, PIN_HP, dac);
- }
- for (i = 0; i < spec->autocfg.speaker_outs; i++) {
- if (spec->autocfg.line_out_type == AUTO_PIN_SPEAKER_OUT)
- break;
- pin = spec->autocfg.speaker_pins[i];
- if (!pin)
- break;
- dac = spec->multiout.extra_out_nid[i];
- if (!dac) {
- if (i > 0 && spec->multiout.extra_out_nid[0])
- dac = spec->multiout.extra_out_nid[0];
- else
- dac = spec->multiout.dac_nids[0];
- }
- alc_auto_set_output_and_unmute(codec, pin, PIN_OUT, dac);
- }
-}
-
-/*
- * multi-io helper
- */
-static int alc_auto_fill_multi_ios(struct hda_codec *codec,
- unsigned int location,
- int offset)
-{
- struct alc_spec *spec = codec->spec;
- struct auto_pin_cfg *cfg = &spec->autocfg;
- hda_nid_t prime_dac = spec->private_dac_nids[0];
- int type, i, dacs, num_pins = 0;
-
- dacs = spec->multiout.num_dacs;
- 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;
- unsigned int defcfg, caps;
- if (cfg->inputs[i].type != type)
- continue;
- defcfg = snd_hda_codec_get_pincfg(codec, nid);
- if (get_defcfg_connect(defcfg) != AC_JACK_PORT_COMPLEX)
- continue;
- if (location && get_defcfg_location(defcfg) != location)
- continue;
- caps = snd_hda_query_pin_caps(codec, nid);
- if (!(caps & AC_PINCAP_OUT))
- continue;
- if (offset && offset + num_pins < dacs) {
- dac = spec->private_dac_nids[offset + num_pins];
- if (!alc_auto_is_dac_reachable(codec, nid, dac))
- dac = 0;
- }
- if (!dac)
- dac = alc_auto_look_for_dac(codec, nid);
- if (!dac)
- continue;
- spec->multi_io[num_pins].pin = nid;
- spec->multi_io[num_pins].dac = dac;
- num_pins++;
- spec->private_dac_nids[spec->multiout.num_dacs++] = dac;
- }
- }
- spec->multiout.num_dacs = dacs;
- if (num_pins < 2) {
- /* clear up again */
- memset(spec->private_dac_nids + dacs, 0,
- sizeof(hda_nid_t) * (AUTO_CFG_MAX_OUTS - dacs));
- spec->private_dac_nids[0] = prime_dac;
- return 0;
+ if (!q->pci_subdevice ||
+ q->pci_subdevice == codec->bus->pci->subsystem_device)
+ return alc_codec_rename(codec, q->name);
}
- return num_pins;
-}
-static int alc_auto_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;
-
- 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;
- sprintf(uinfo->value.enumerated.name, "%dch",
- (uinfo->value.enumerated.item + 1) * 2);
return 0;
}
-static int alc_auto_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;
- ucontrol->value.enumerated.item[0] = (spec->ext_channel_count - 1) / 2;
- return 0;
-}
-
-static int alc_set_multi_io(struct hda_codec *codec, int idx, bool output)
-{
- struct alc_spec *spec = codec->spec;
- hda_nid_t nid = spec->multi_io[idx].pin;
-
- if (!spec->multi_io[idx].ctl_in)
- spec->multi_io[idx].ctl_in =
- snd_hda_codec_read(codec, nid, 0,
- AC_VERB_GET_PIN_WIDGET_CONTROL, 0);
- if (output) {
- snd_hda_codec_update_cache(codec, nid, 0,
- AC_VERB_SET_PIN_WIDGET_CONTROL,
- PIN_OUT);
- if (get_wcaps(codec, nid) & AC_WCAP_OUT_AMP)
- snd_hda_codec_amp_stereo(codec, nid, HDA_OUTPUT, 0,
- HDA_AMP_MUTE, 0);
- alc_auto_select_dac(codec, nid, spec->multi_io[idx].dac);
- } else {
- if (get_wcaps(codec, nid) & AC_WCAP_OUT_AMP)
- snd_hda_codec_amp_stereo(codec, nid, HDA_OUTPUT, 0,
- HDA_AMP_MUTE, HDA_AMP_MUTE);
- snd_hda_codec_update_cache(codec, nid, 0,
- AC_VERB_SET_PIN_WIDGET_CONTROL,
- spec->multi_io[idx].ctl_in);
- }
- return 0;
-}
-
-static int alc_auto_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 i, ch;
-
- ch = ucontrol->value.enumerated.item[0];
- if (ch < 0 || ch > spec->multi_ios)
- return -EINVAL;
- if (ch == (spec->ext_channel_count - 1) / 2)
- return 0;
- spec->ext_channel_count = (ch + 1) * 2;
- for (i = 0; i < spec->multi_ios; i++)
- alc_set_multi_io(codec, i, i < ch);
- spec->multiout.max_channels = spec->ext_channel_count;
- if (spec->need_dac_fix && !spec->const_channel_count)
- spec->multiout.num_dacs = spec->multiout.max_channels / 2;
- return 1;
-}
-
-static const struct snd_kcontrol_new alc_auto_channel_mode_enum = {
- .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
- .name = "Channel Mode",
- .info = alc_auto_ch_mode_info,
- .get = alc_auto_ch_mode_get,
- .put = alc_auto_ch_mode_put,
-};
-
-static int alc_auto_add_multi_channel_mode(struct hda_codec *codec)
-{
- struct alc_spec *spec = codec->spec;
-
- if (spec->multi_ios > 0) {
- struct snd_kcontrol_new *knew;
-
- knew = alc_kcontrol_new(spec);
- if (!knew)
- return -ENOMEM;
- *knew = alc_auto_channel_mode_enum;
- knew->name = kstrdup("Channel Mode", GFP_KERNEL);
- if (!knew->name)
- return -ENOMEM;
- }
- return 0;
-}
-
-/* filter out invalid adc_nids (and capsrc_nids) that don't give all
- * active input pins
- */
-static void alc_remove_invalid_adc_nids(struct hda_codec *codec)
-{
- struct alc_spec *spec = codec->spec;
- const struct hda_input_mux *imux;
- hda_nid_t adc_nids[ARRAY_SIZE(spec->private_adc_nids)];
- hda_nid_t capsrc_nids[ARRAY_SIZE(spec->private_adc_nids)];
- int i, n, nums;
-
- imux = spec->input_mux;
- if (!imux)
- return;
- if (spec->dyn_adc_switch)
- return;
-
- nums = 0;
- for (n = 0; n < spec->num_adc_nids; n++) {
- hda_nid_t cap = spec->private_capsrc_nids[n];
- int num_conns = snd_hda_get_conn_list(codec, cap, NULL);
- for (i = 0; i < imux->num_items; i++) {
- hda_nid_t pin = spec->imux_pins[i];
- if (pin) {
- if (get_connection_index(codec, cap, pin) < 0)
- break;
- } else if (num_conns <= imux->items[i].index)
- break;
- }
- if (i >= imux->num_items) {
- adc_nids[nums] = spec->private_adc_nids[n];
- capsrc_nids[nums++] = cap;
- }
- }
- if (!nums) {
- /* check whether ADC-switch is possible */
- if (!alc_check_dyn_adc_switch(codec)) {
- printk(KERN_WARNING "hda_codec: %s: no valid ADC found;"
- " using fallback 0x%x\n",
- codec->chip_name, spec->private_adc_nids[0]);
- spec->num_adc_nids = 1;
- spec->auto_mic = 0;
- return;
- }
- } else if (nums != spec->num_adc_nids) {
- memcpy(spec->private_adc_nids, adc_nids,
- nums * sizeof(hda_nid_t));
- memcpy(spec->private_capsrc_nids, capsrc_nids,
- nums * sizeof(hda_nid_t));
- spec->num_adc_nids = nums;
- }
-
- if (spec->auto_mic)
- alc_auto_mic_check_imux(codec); /* check auto-mic setups */
- else if (spec->input_mux->num_items == 1)
- spec->num_adc_nids = 1; /* reduce to a single ADC */
-}
-
-/*
- * initialize ADC paths
- */
-static void alc_auto_init_adc(struct hda_codec *codec, int adc_idx)
-{
- struct alc_spec *spec = codec->spec;
- hda_nid_t nid;
-
- nid = spec->adc_nids[adc_idx];
- /* mute ADC */
- if (nid_has_mute(codec, nid, HDA_INPUT)) {
- snd_hda_codec_write(codec, nid, 0,
- AC_VERB_SET_AMP_GAIN_MUTE,
- AMP_IN_MUTE(0));
- return;
- }
- if (!spec->capsrc_nids)
- return;
- nid = spec->capsrc_nids[adc_idx];
- if (nid_has_mute(codec, nid, HDA_OUTPUT))
- snd_hda_codec_write(codec, nid, 0,
- AC_VERB_SET_AMP_GAIN_MUTE,
- AMP_OUT_MUTE);
-}
-
-static void alc_auto_init_input_src(struct hda_codec *codec)
-{
- struct alc_spec *spec = codec->spec;
- int c, nums;
-
- for (c = 0; c < spec->num_adc_nids; c++)
- alc_auto_init_adc(codec, c);
- if (spec->dyn_adc_switch)
- nums = 1;
- else
- nums = spec->num_adc_nids;
- for (c = 0; c < nums; c++)
- alc_mux_select(codec, c, spec->cur_mux[c], true);
-}
-
-/* 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 (spec->shared_mic_hp && !strcmp(label, "Misc"))
- label = "Headphone Mic";
- 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;
-}
-
-/* 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 if (snd_hda_get_conn_list(codec, cap, NULL) > 1) {
- 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;
-
- if (!pin)
- return 0;
- for (i = 0; i < spec->num_adc_nids; i++) {
- hda_nid_t cap = get_capsrc(spec, 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 */
-}
-
-/* initialize some special cases for input sources */
-static void alc_init_special_input_src(struct hda_codec *codec)
-{
- struct alc_spec *spec = codec->spec;
- int i;
-
- for (i = 0; i < spec->autocfg.num_inputs; i++)
- init_capsrc_for_pin(codec, spec->autocfg.inputs[i].pin);
-}
-
-/* assign appropriate capture mixers */
-static void set_capture_mixer(struct hda_codec *codec)
-{
- struct alc_spec *spec = codec->spec;
- static const 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 },
- };
-
- /* check whether either of ADC or MUX has a volume control */
- if (!nid_has_volume(codec, spec->adc_nids[0], HDA_INPUT)) {
- if (!spec->capsrc_nids)
- return; /* no volume */
- if (!nid_has_volume(codec, spec->capsrc_nids[0], HDA_OUTPUT))
- return; /* no volume in capsrc, too */
- spec->vol_in_capsrc = 1;
- }
-
- if (spec->num_adc_nids > 0) {
- int mux = 0;
- int num_adcs = 0;
-
- if (spec->input_mux && spec->input_mux->num_items > 1)
- mux = 1;
- if (spec->auto_mic) {
- num_adcs = 1;
- mux = 0;
- } else if (spec->dyn_adc_switch)
- num_adcs = 1;
- if (!num_adcs) {
- if (spec->num_adc_nids > 3)
- spec->num_adc_nids = 3;
- else if (!spec->num_adc_nids)
- return;
- num_adcs = spec->num_adc_nids;
- }
- spec->cap_mixer = caps[mux][num_adcs - 1];
- }
-}
-
-/*
- * standard auto-parser initializations
- */
-static void alc_auto_init_std(struct hda_codec *codec)
-{
- struct alc_spec *spec = codec->spec;
- alc_auto_init_multi_out(codec);
- alc_auto_init_extra_out(codec);
- alc_auto_init_analog_input(codec);
- alc_auto_init_input_src(codec);
- alc_auto_init_digital(codec);
- if (spec->unsol_event)
- alc_inithook(codec);
-}
/*
* Digital-beep handlers
@@ -3954,10 +1010,14 @@ static void alc_auto_init_std(struct hda_codec *codec)
((spec)->beep_amp = HDA_COMPOSE_AMP_VAL(nid, 3, idx, dir))
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),
{}
};
@@ -3985,114 +1045,105 @@ static int alc_parse_auto_config(struct hda_codec *codec,
const hda_nid_t *ssid_nids)
{
struct alc_spec *spec = codec->spec;
- struct auto_pin_cfg *cfg = &spec->autocfg;
+ struct auto_pin_cfg *cfg = &spec->gen.autocfg;
int err;
err = snd_hda_parse_pin_defcfg(codec, cfg, ignore_nids,
spec->parse_flags);
if (err < 0)
return err;
- if (!cfg->line_outs) {
- if (cfg->dig_outs || cfg->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 (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;
- }
+ if (ssid_nids)
+ alc_ssid_check(codec, ssid_nids);
- err = alc_auto_fill_dac_nids(codec);
- if (err < 0)
- return err;
- err = alc_auto_add_multi_channel_mode(codec);
- if (err < 0)
- return err;
- err = alc_auto_create_multi_out_ctls(codec, cfg);
- if (err < 0)
- return err;
- err = alc_auto_create_hp_out(codec);
- if (err < 0)
- return err;
- err = alc_auto_create_speaker_out(codec);
- if (err < 0)
- return err;
- err = alc_auto_create_shared_input(codec);
- if (err < 0)
- return err;
- err = alc_auto_create_input_ctls(codec);
+ err = snd_hda_gen_parse_auto_config(codec, cfg);
if (err < 0)
return err;
- spec->multiout.max_channels = spec->multiout.num_dacs * 2;
-
- dig_only:
- alc_auto_parse_digital(codec);
+ return 1;
+}
- if (!spec->no_analog)
- alc_remove_invalid_adc_nids(codec);
+/* 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;
- if (ssid_nids)
- alc_ssid_check(codec, ssid_nids);
+ 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;
- if (!spec->no_analog) {
- alc_auto_check_switches(codec);
- err = alc_auto_add_mic_boost(codec);
- if (err < 0)
- return err;
+ err = alc_codec_rename_from_preset(codec);
+ if (err < 0) {
+ kfree(spec);
+ return err;
}
-
- if (spec->kctls.list)
- add_mixer(spec, spec->kctls.list);
-
- return 1;
+ 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 };
+ static const hda_nid_t alc880_ssids[] = { 0x15, 0x1b, 0x14, 0 };
return alc_parse_auto_config(codec, alc880_ignore, alc880_ssids);
}
-#ifdef CONFIG_SND_HDA_POWER_SAVE
-static const 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 */
-};
-#endif
-
/*
* ALC880 fix-ups
*/
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,
};
-static const struct alc_fixup alc880_fixups[] = {
+/* 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 = ALC_FIXUP_VERBS,
+ .type = HDA_FIXUP_VERBS,
.v.verbs = alc_gpio2_init_verbs,
},
[ALC880_FIXUP_MEDION_RIM] = {
- .type = ALC_FIXUP_VERBS,
+ .type = HDA_FIXUP_VERBS,
.v.verbs = (const struct hda_verb[]) {
{ 0x20, AC_VERB_SET_COEF_INDEX, 0x07 },
{ 0x20, AC_VERB_SET_PROC_COEF, 0x3060 },
@@ -4101,111 +1152,392 @@ static const struct alc_fixup alc880_fixups[] = {
.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 },
+ { }
+ }
+ },
+ [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,
+ },
+ [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 },
+ {}
+ },
+ },
+ [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 */
+ { }
+ },
+ },
+ [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 */
+ { }
+ }
+ },
+ [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,
+ },
+ [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 */
+ { }
+ }
+ },
+ [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,
+ },
+ [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 */
+ { }
+ }
+ },
+ [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,
+ },
+ [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,
+ },
+ [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.
+ */
+ 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),
{}
};
+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"},
+ {}
+};
-/*
- * board setups
- */
-#ifdef CONFIG_SND_HDA_ENABLE_REALTEK_QUIRKS
-#define alc_board_config \
- snd_hda_check_board_config
-#define alc_board_codec_sid_config \
- snd_hda_check_board_codec_sid_config
-#include "alc_quirks.c"
-#else
-#define alc_board_config(codec, nums, models, tbl) -1
-#define alc_board_codec_sid_config(codec, nums, models, tbl) -1
-#define setup_preset(codec, x) /* NOP */
-#endif
/*
* OK, here we have finally the patch for ALC880
*/
-#ifdef CONFIG_SND_HDA_ENABLE_REALTEK_QUIRKS
-#include "alc880_quirks.c"
-#endif
-
static int patch_alc880(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;
-
- spec->mixer_nid = 0x0b;
- spec->need_dac_fix = 1;
-
- board_config = alc_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 = ALC_MODEL_AUTO;
- }
-
- if (board_config == ALC_MODEL_AUTO) {
- alc_pick_fixup(codec, NULL, alc880_fixup_tbl, alc880_fixups);
- alc_apply_fixup(codec, ALC_FIXUP_ACT_PRE_PROBE);
- }
-
- if (board_config == ALC_MODEL_AUTO) {
- /* automatic parse from the BIOS config */
- err = alc880_parse_auto_config(codec);
- if (err < 0)
- goto error;
-#ifdef CONFIG_SND_HDA_ENABLE_REALTEK_QUIRKS
- else if (!err) {
- printk(KERN_INFO
- "hda_codec: Cannot set up configuration "
- "from BIOS. Using 3-stack mode...\n");
- board_config = ALC880_3ST;
- }
-#endif
- }
+ err = alc_alloc_spec(codec, 0x0b);
+ if (err < 0)
+ return err;
- if (board_config != ALC_MODEL_AUTO) {
- spec->vmaster_nid = 0x0c;
- setup_preset(codec, &alc880_presets[board_config]);
- }
+ spec = codec->spec;
+ spec->gen.need_dac_fix = 1;
+ spec->gen.beep_nid = 0x01;
- if (!spec->no_analog && !spec->adc_nids) {
- alc_auto_fill_adc_caps(codec);
- alc_rebuild_imux_for_auto_mic(codec);
- alc_remove_invalid_adc_nids(codec);
- }
+ snd_hda_pick_fixup(codec, alc880_fixup_models, alc880_fixup_tbl,
+ alc880_fixups);
+ snd_hda_apply_fixup(codec, HDA_FIXUP_ACT_PRE_PROBE);
- if (!spec->no_analog && !spec->cap_mixer)
- set_capture_mixer(codec);
+ /* automatic parse from the BIOS config */
+ err = alc880_parse_auto_config(codec);
+ if (err < 0)
+ goto error;
- if (!spec->no_analog) {
- err = snd_hda_attach_beep_device(codec, 0x1);
- if (err < 0)
- goto error;
+ if (!spec->gen.no_analog)
set_beep_amp(spec, 0x0b, 0x05, HDA_INPUT);
- }
-
- alc_apply_fixup(codec, ALC_FIXUP_ACT_PROBE);
codec->patch_ops = alc_patch_ops;
- if (board_config == ALC_MODEL_AUTO)
- spec->init_hook = alc_auto_init_std;
- else
- codec->patch_ops.build_controls = __alc_build_controls;
-#ifdef CONFIG_SND_HDA_POWER_SAVE
- if (!spec->loopback.amplist)
- spec->loopback.amplist = alc880_loopbacks;
-#endif
+ codec->patch_ops.unsol_event = alc880_unsol_event;
+
+
+ snd_hda_apply_fixup(codec, HDA_FIXUP_ACT_PROBE);
return 0;
@@ -4225,119 +1557,238 @@ static int alc260_parse_auto_config(struct hda_codec *codec)
return alc_parse_auto_config(codec, alc260_ignore, alc260_ssids);
}
-#ifdef CONFIG_SND_HDA_POWER_SAVE
-static const 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,
+ 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 const struct alc_fixup alc260_fixups[] = {
- [PINFIX_HP_DC5750] = {
- .type = ALC_FIXUP_PINS,
- .v.pins = (const struct alc_pincfg[]) {
+static void alc260_gpio1_automute(struct hda_codec *codec)
+{
+ struct alc_spec *spec = codec->spec;
+ snd_hda_codec_write(codec, 0x01, 0, AC_VERB_SET_GPIO_DATA,
+ spec->gen.hp_jack_present);
+}
+
+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 },
+ { }
+ };
+
+ 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;
+ }
+}
+
+static void alc260_fixup_fsc_s7020(struct hda_codec *codec,
+ const struct hda_fixup *fix, int action)
+{
+ struct alc_spec *spec = codec->spec;
+ if (action == HDA_FIXUP_ACT_PROBE)
+ spec->init_amp = ALC_INIT_NONE;
+}
+
+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 hda_fixup alc260_fixups[] = {
+ [ALC260_FIXUP_HP_DC5750] = {
+ .type = HDA_FIXUP_PINS,
+ .v.pins = (const struct hda_pintbl[]) {
{ 0x11, 0x90130110 }, /* speaker */
{ }
}
},
+ [ALC260_FIXUP_HP_PIN_0F] = {
+ .type = HDA_FIXUP_PINS,
+ .v.pins = (const struct hda_pintbl[]) {
+ { 0x0f, 0x01214000 }, /* HP */
+ { }
+ }
+ },
+ [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_FIXUP_GPIO1] = {
+ .type = HDA_FIXUP_VERBS,
+ .v.verbs = alc_gpio1_init_verbs,
+ },
+ [ALC260_FIXUP_GPIO1_TOGGLE] = {
+ .type = HDA_FIXUP_FUNC,
+ .v.func = alc260_fixup_gpio1_toggle,
+ .chained = true,
+ .chain_id = ALC260_FIXUP_HP_PIN_0F,
+ },
+ [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_FIXUP_HP_B1900] = {
+ .type = HDA_FIXUP_FUNC,
+ .v.func = alc260_fixup_gpio1_toggle,
+ .chained = true,
+ .chain_id = ALC260_FIXUP_COEF,
+ },
+ [ALC260_FIXUP_KN1] = {
+ .type = HDA_FIXUP_FUNC,
+ .v.func = alc260_fixup_kn1,
+ },
+ [ALC260_FIXUP_FSC_S7020] = {
+ .type = HDA_FIXUP_FUNC,
+ .v.func = alc260_fixup_fsc_s7020,
+ },
+ [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 },
+ { }
+ }
+ },
};
static const struct snd_pci_quirk alc260_fixup_tbl[] = {
- SND_PCI_QUIRK(0x103c, 0x280a, "HP dc5750", PINFIX_HP_DC5750),
+ 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"},
{}
};
/*
*/
-#ifdef CONFIG_SND_HDA_ENABLE_REALTEK_QUIRKS
-#include "alc260_quirks.c"
-#endif
-
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;
-
- spec->mixer_nid = 0x07;
-
- board_config = alc_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 = ALC_MODEL_AUTO;
- }
-
- if (board_config == ALC_MODEL_AUTO) {
- alc_pick_fixup(codec, NULL, alc260_fixup_tbl, alc260_fixups);
- alc_apply_fixup(codec, ALC_FIXUP_ACT_PRE_PROBE);
- }
+ int err;
- if (board_config == ALC_MODEL_AUTO) {
- /* automatic parse from the BIOS config */
- err = alc260_parse_auto_config(codec);
- if (err < 0)
- goto error;
-#ifdef CONFIG_SND_HDA_ENABLE_REALTEK_QUIRKS
- else if (!err) {
- printk(KERN_INFO
- "hda_codec: Cannot set up configuration "
- "from BIOS. Using base mode...\n");
- board_config = ALC260_BASIC;
- }
-#endif
- }
+ err = alc_alloc_spec(codec, 0x07);
+ if (err < 0)
+ return err;
- if (board_config != ALC_MODEL_AUTO) {
- setup_preset(codec, &alc260_presets[board_config]);
- spec->vmaster_nid = 0x08;
- }
+ 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;
- if (!spec->no_analog && !spec->adc_nids) {
- alc_auto_fill_adc_caps(codec);
- alc_rebuild_imux_for_auto_mic(codec);
- alc_remove_invalid_adc_nids(codec);
- }
+ snd_hda_pick_fixup(codec, alc260_fixup_models, alc260_fixup_tbl,
+ alc260_fixups);
+ snd_hda_apply_fixup(codec, HDA_FIXUP_ACT_PRE_PROBE);
- if (!spec->no_analog && !spec->cap_mixer)
- set_capture_mixer(codec);
+ /* automatic parse from the BIOS config */
+ err = alc260_parse_auto_config(codec);
+ if (err < 0)
+ goto error;
- if (!spec->no_analog) {
- err = snd_hda_attach_beep_device(codec, 0x1);
- if (err < 0)
- goto error;
+ if (!spec->gen.no_analog)
set_beep_amp(spec, 0x07, 0x05, HDA_INPUT);
- }
-
- alc_apply_fixup(codec, ALC_FIXUP_ACT_PROBE);
codec->patch_ops = alc_patch_ops;
- if (board_config == ALC_MODEL_AUTO)
- spec->init_hook = alc_auto_init_std;
- else
- codec->patch_ops.build_controls = __alc_build_controls;
spec->shutup = alc_eapd_shutup;
-#ifdef CONFIG_SND_HDA_POWER_SAVE
- if (!spec->loopback.amplist)
- spec->loopback.amplist = alc260_loopbacks;
-#endif
+
+ snd_hda_apply_fixup(codec, HDA_FIXUP_ACT_PROBE);
return 0;
@@ -4358,9 +1809,6 @@ static int patch_alc260(struct hda_codec *codec)
* In addition, an independent DAC for the multi-playback (not used in this
* driver yet).
*/
-#ifdef CONFIG_SND_HDA_POWER_SAVE
-#define alc882_loopbacks alc880_loopbacks
-#endif
/*
* Pin config fixes
@@ -4372,11 +1820,14 @@ enum {
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,
@@ -4385,12 +1836,21 @@ enum {
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 alc_fixup *fix, int action)
+ const struct hda_fixup *fix, int action)
{
- if (action != ALC_FIXUP_ACT_INIT)
+ if (action != HDA_FIXUP_ACT_INIT)
return;
alc889_coef_init(codec);
}
@@ -4430,9 +1890,9 @@ static void alc882_gpio_mute(struct hda_codec *codec, int pin, int muted)
/* set up GPIO at initialization */
static void alc885_fixup_macpro_gpio(struct hda_codec *codec,
- const struct alc_fixup *fix, int action)
+ const struct hda_fixup *fix, int action)
{
- if (action != ALC_FIXUP_ACT_INIT)
+ if (action != HDA_FIXUP_ACT_INIT)
return;
alc882_gpio_mute(codec, 0, 0);
alc882_gpio_mute(codec, 1, 0);
@@ -4443,9 +1903,9 @@ static void alc885_fixup_macpro_gpio(struct hda_codec *codec,
* work correctly (bko#42740)
*/
static void alc889_fixup_dac_route(struct hda_codec *codec,
- const struct alc_fixup *fix, int action)
+ const struct hda_fixup *fix, int action)
{
- if (action == ALC_FIXUP_ACT_PRE_PROBE) {
+ 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 };
@@ -4453,7 +1913,7 @@ static void alc889_fixup_dac_route(struct hda_codec *codec,
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 == ALC_FIXUP_ACT_PROBE) {
+ } 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);
@@ -4463,10 +1923,94 @@ static void alc889_fixup_dac_route(struct hda_codec *codec,
}
}
-static const struct alc_fixup alc882_fixups[] = {
+/* 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;
+
+ 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;
+ }
+}
+
+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;
+
+ 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;
+}
+
+/* Set VREF on speaker pins on imac91 */
+static void alc889_fixup_imac91_vref(struct hda_codec *codec,
+ const struct hda_fixup *fix, int action)
+{
+ static hda_nid_t nids[2] = { 0x18, 0x1a };
+
+ if (action == HDA_FIXUP_ACT_INIT)
+ alc889_fixup_mac_pins(codec, nids, ARRAY_SIZE(nids));
+}
+
+/* Set VREF on speaker pins on mba11 */
+static void alc889_fixup_mba11_vref(struct hda_codec *codec,
+ const struct hda_fixup *fix, int action)
+{
+ static hda_nid_t nids[1] = { 0x18 };
+
+ if (action == HDA_FIXUP_ACT_INIT)
+ alc889_fixup_mac_pins(codec, nids, ARRAY_SIZE(nids));
+}
+
+/* Set VREF on speaker pins on mba21 */
+static void alc889_fixup_mba21_vref(struct hda_codec *codec,
+ const struct hda_fixup *fix, int action)
+{
+ static hda_nid_t nids[2] = { 0x18, 0x19 };
+
+ if (action == HDA_FIXUP_ACT_INIT)
+ alc889_fixup_mac_pins(codec, nids, ARRAY_SIZE(nids));
+}
+
+/* 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;
+ if (action == HDA_FIXUP_ACT_PRE_PROBE) {
+ spec->gen.no_primary_hp = 1;
+ spec->gen.no_multi_io = 1;
+ }
+}
+
+static void alc_fixup_bass_chmap(struct hda_codec *codec,
+ const struct hda_fixup *fix, int action);
+
+static const struct hda_fixup alc882_fixups[] = {
[ALC882_FIXUP_ABIT_AW9D_MAX] = {
- .type = ALC_FIXUP_PINS,
- .v.pins = (const struct alc_pincfg[]) {
+ .type = HDA_FIXUP_PINS,
+ .v.pins = (const struct hda_pintbl[]) {
{ 0x15, 0x01080104 }, /* side */
{ 0x16, 0x01011012 }, /* rear */
{ 0x17, 0x01016011 }, /* clfe */
@@ -4474,47 +2018,56 @@ static const struct alc_fixup alc882_fixups[] = {
}
},
[ALC882_FIXUP_LENOVO_Y530] = {
- .type = ALC_FIXUP_PINS,
- .v.pins = (const struct alc_pincfg[]) {
+ .type = HDA_FIXUP_PINS,
+ .v.pins = (const struct hda_pintbl[]) {
{ 0x15, 0x99130112 }, /* rear int speakers */
{ 0x16, 0x99130111 }, /* subwoofer */
{ }
}
},
[ALC882_FIXUP_PB_M5210] = {
- .type = ALC_FIXUP_VERBS,
- .v.verbs = (const struct hda_verb[]) {
- { 0x19, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF50 },
+ .type = HDA_FIXUP_PINCTLS,
+ .v.pins = (const struct hda_pintbl[]) {
+ { 0x19, PIN_VREF50 },
{}
}
},
[ALC882_FIXUP_ACER_ASPIRE_7736] = {
- .type = ALC_FIXUP_SKU,
- .v.sku = ALC_FIXUP_SKU_IGNORE,
+ .type = HDA_FIXUP_FUNC,
+ .v.func = alc_fixup_sku_ignore,
},
[ALC882_FIXUP_ASUS_W90V] = {
- .type = ALC_FIXUP_PINS,
- .v.pins = (const struct alc_pincfg[]) {
+ .type = HDA_FIXUP_PINS,
+ .v.pins = (const struct hda_pintbl[]) {
{ 0x16, 0x99130110 }, /* fix sequence for CLFE */
{ }
}
},
[ALC889_FIXUP_CD] = {
- .type = ALC_FIXUP_PINS,
- .v.pins = (const struct alc_pincfg[]) {
+ .type = HDA_FIXUP_PINS,
+ .v.pins = (const struct hda_pintbl[]) {
{ 0x1c, 0x993301f0 }, /* CD */
{ }
}
},
+ [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,
+ },
[ALC889_FIXUP_VAIO_TT] = {
- .type = ALC_FIXUP_PINS,
- .v.pins = (const struct alc_pincfg[]) {
+ .type = HDA_FIXUP_PINS,
+ .v.pins = (const struct hda_pintbl[]) {
{ 0x17, 0x90170111 }, /* hidden surround speaker */
{ }
}
},
[ALC888_FIXUP_EEE1601] = {
- .type = ALC_FIXUP_VERBS,
+ .type = HDA_FIXUP_VERBS,
.v.verbs = (const struct hda_verb[]) {
{ 0x20, AC_VERB_SET_COEF_INDEX, 0x0b },
{ 0x20, AC_VERB_SET_PROC_COEF, 0x0838 },
@@ -4522,7 +2075,7 @@ static const struct alc_fixup alc882_fixups[] = {
}
},
[ALC882_FIXUP_EAPD] = {
- .type = ALC_FIXUP_VERBS,
+ .type = HDA_FIXUP_VERBS,
.v.verbs = (const struct hda_verb[]) {
/* change to EAPD mode */
{ 0x20, AC_VERB_SET_COEF_INDEX, 0x07 },
@@ -4531,7 +2084,7 @@ static const struct alc_fixup alc882_fixups[] = {
}
},
[ALC883_FIXUP_EAPD] = {
- .type = ALC_FIXUP_VERBS,
+ .type = HDA_FIXUP_VERBS,
.v.verbs = (const struct hda_verb[]) {
/* change to EAPD mode */
{ 0x20, AC_VERB_SET_COEF_INDEX, 0x07 },
@@ -4540,7 +2093,7 @@ static const struct alc_fixup alc882_fixups[] = {
}
},
[ALC883_FIXUP_ACER_EAPD] = {
- .type = ALC_FIXUP_VERBS,
+ .type = HDA_FIXUP_VERBS,
.v.verbs = (const struct hda_verb[]) {
/* eanable EAPD on Acer laptops */
{ 0x20, AC_VERB_SET_COEF_INDEX, 0x07 },
@@ -4548,31 +2101,41 @@ static const struct alc_fixup alc882_fixups[] = {
{ }
}
},
+ [ALC882_FIXUP_GPIO1] = {
+ .type = HDA_FIXUP_VERBS,
+ .v.verbs = alc_gpio1_init_verbs,
+ },
+ [ALC882_FIXUP_GPIO2] = {
+ .type = HDA_FIXUP_VERBS,
+ .v.verbs = alc_gpio2_init_verbs,
+ },
[ALC882_FIXUP_GPIO3] = {
- .type = ALC_FIXUP_VERBS,
+ .type = HDA_FIXUP_VERBS,
.v.verbs = alc_gpio3_init_verbs,
},
[ALC882_FIXUP_ASUS_W2JC] = {
- .type = ALC_FIXUP_VERBS,
+ .type = HDA_FIXUP_VERBS,
.v.verbs = alc_gpio1_init_verbs,
.chained = true,
.chain_id = ALC882_FIXUP_EAPD,
},
[ALC889_FIXUP_COEF] = {
- .type = ALC_FIXUP_FUNC,
+ .type = HDA_FIXUP_FUNC,
.v.func = alc889_fixup_coef,
},
[ALC882_FIXUP_ACER_ASPIRE_4930G] = {
- .type = ALC_FIXUP_PINS,
- .v.pins = (const struct alc_pincfg[]) {
+ .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,
},
[ALC882_FIXUP_ACER_ASPIRE_8930G] = {
- .type = ALC_FIXUP_PINS,
- .v.pins = (const struct alc_pincfg[]) {
+ .type = HDA_FIXUP_PINS,
+ .v.pins = (const struct hda_pintbl[]) {
{ 0x16, 0x99130111 }, /* CLFE speaker */
{ 0x1b, 0x99130112 }, /* surround speaker */
{ }
@@ -4582,7 +2145,7 @@ static const struct alc_fixup alc882_fixups[] = {
},
[ALC882_FIXUP_ASPIRE_8930G_VERBS] = {
/* additional init verbs for Acer Aspire 8930G */
- .type = ALC_FIXUP_VERBS,
+ .type = HDA_FIXUP_VERBS,
.v.verbs = (const struct hda_verb[]) {
/* Enable all DACs */
/* DAC DISABLE/MUTE 1? */
@@ -4611,16 +2174,69 @@ static const struct alc_fixup alc882_fixups[] = {
{ 0x20, AC_VERB_SET_COEF_INDEX, 0x07 },
{ 0x20, AC_VERB_SET_PROC_COEF, 0x3050 },
{ }
- }
+ },
+ .chained = true,
+ .chain_id = ALC882_FIXUP_GPIO1,
},
[ALC885_FIXUP_MACPRO_GPIO] = {
- .type = ALC_FIXUP_FUNC,
+ .type = HDA_FIXUP_FUNC,
.v.func = alc885_fixup_macpro_gpio,
},
[ALC889_FIXUP_DAC_ROUTE] = {
- .type = ALC_FIXUP_FUNC,
+ .type = HDA_FIXUP_FUNC,
.v.func = alc889_fixup_dac_route,
},
+ [ALC889_FIXUP_MBP_VREF] = {
+ .type = HDA_FIXUP_FUNC,
+ .v.func = alc889_fixup_mbp_vref,
+ .chained = true,
+ .chain_id = ALC882_FIXUP_GPIO1,
+ },
+ [ALC889_FIXUP_IMAC91_VREF] = {
+ .type = HDA_FIXUP_FUNC,
+ .v.func = alc889_fixup_imac91_vref,
+ .chained = true,
+ .chain_id = ALC882_FIXUP_GPIO1,
+ },
+ [ALC889_FIXUP_MBA11_VREF] = {
+ .type = HDA_FIXUP_FUNC,
+ .v.func = alc889_fixup_mba11_vref,
+ .chained = true,
+ .chain_id = ALC889_FIXUP_MBP_VREF,
+ },
+ [ALC889_FIXUP_MBA21_VREF] = {
+ .type = HDA_FIXUP_FUNC,
+ .v.func = alc889_fixup_mba21_vref,
+ .chained = true,
+ .chain_id = ALC889_FIXUP_MBP_VREF,
+ },
+ [ALC889_FIXUP_MP11_VREF] = {
+ .type = HDA_FIXUP_FUNC,
+ .v.func = alc889_fixup_mba11_vref,
+ .chained = true,
+ .chain_id = ALC885_FIXUP_MACPRO_GPIO,
+ },
+ [ALC882_FIXUP_INV_DMIC] = {
+ .type = HDA_FIXUP_FUNC,
+ .v.func = alc_fixup_inv_dmic_0x12,
+ },
+ [ALC882_FIXUP_NO_PRIMARY_HP] = {
+ .type = HDA_FIXUP_FUNC,
+ .v.func = alc882_fixup_no_primary_hp,
+ },
+ [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,
+ },
+ [ALC887_FIXUP_BASS_CHMAP] = {
+ .type = HDA_FIXUP_FUNC,
+ .v.func = alc_fixup_bass_chmap,
+ },
};
static const struct snd_pci_quirk alc882_fixup_tbl[] = {
@@ -4645,24 +2261,48 @@ static const struct snd_pci_quirk alc882_fixup_tbl[] = {
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, 0x0c00, "Mac Pro", ALC885_FIXUP_MACPRO_GPIO),
+ 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", ALC889_FIXUP_CD),
+ 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),
@@ -4671,6 +2311,15 @@ static const struct snd_pci_quirk alc882_fixup_tbl[] = {
{}
};
+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
*/
@@ -4684,22 +2333,16 @@ static int alc882_parse_auto_config(struct hda_codec *codec)
/*
*/
-#ifdef CONFIG_SND_HDA_ENABLE_REALTEK_QUIRKS
-#include "alc882_quirks.c"
-#endif
-
static int patch_alc882(struct hda_codec *codec)
{
struct alc_spec *spec;
- int err, board_config;
-
- spec = kzalloc(sizeof(*spec), GFP_KERNEL);
- if (spec == NULL)
- return -ENOMEM;
+ int err;
- codec->spec = spec;
+ err = alc_alloc_spec(codec, 0x0b);
+ if (err < 0)
+ return err;
- spec->mixer_nid = 0x0b;
+ spec = codec->spec;
switch (codec->vendor_id) {
case 0x10ec0882:
@@ -4711,69 +2354,26 @@ static int patch_alc882(struct hda_codec *codec)
break;
}
- err = alc_codec_rename_from_preset(codec);
- if (err < 0)
- goto error;
-
- board_config = alc_board_config(codec, ALC882_MODEL_LAST,
- alc882_models, NULL);
- if (board_config < 0)
- board_config = alc_board_codec_sid_config(codec,
- ALC882_MODEL_LAST, alc882_models, alc882_ssid_cfg_tbl);
-
- if (board_config < 0) {
- printk(KERN_INFO "hda_codec: %s: BIOS auto-probing.\n",
- codec->chip_name);
- board_config = ALC_MODEL_AUTO;
- }
-
- if (board_config == ALC_MODEL_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 == ALC_MODEL_AUTO) {
- /* automatic parse from the BIOS config */
- err = alc882_parse_auto_config(codec);
- if (err < 0)
- goto error;
- }
-
- if (board_config != ALC_MODEL_AUTO) {
- setup_preset(codec, &alc882_presets[board_config]);
- spec->vmaster_nid = 0x0c;
- }
+ if (has_cdefine_beep(codec))
+ spec->gen.beep_nid = 0x01;
- if (!spec->no_analog && !spec->adc_nids) {
- alc_auto_fill_adc_caps(codec);
- alc_rebuild_imux_for_auto_mic(codec);
- alc_remove_invalid_adc_nids(codec);
- }
-
- if (!spec->no_analog && !spec->cap_mixer)
- set_capture_mixer(codec);
+ /* automatic parse from the BIOS config */
+ err = alc882_parse_auto_config(codec);
+ if (err < 0)
+ goto error;
- if (!spec->no_analog && has_cdefine_beep(codec)) {
- err = snd_hda_attach_beep_device(codec, 0x1);
- if (err < 0)
- goto error;
+ if (!spec->gen.no_analog && spec->gen.beep_nid)
set_beep_amp(spec, 0x0b, 0x05, HDA_INPUT);
- }
-
- alc_apply_fixup(codec, ALC_FIXUP_ACT_PROBE);
codec->patch_ops = alc_patch_ops;
- if (board_config == ALC_MODEL_AUTO)
- spec->init_hook = alc_auto_init_std;
- else
- codec->patch_ops.build_controls = __alc_build_controls;
-#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;
@@ -4798,48 +2398,60 @@ static int alc262_parse_auto_config(struct hda_codec *codec)
*/
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 alc_fixup alc262_fixups[] = {
+static const struct hda_fixup alc262_fixups[] = {
[ALC262_FIXUP_FSC_H270] = {
- .type = ALC_FIXUP_PINS,
- .v.pins = (const struct alc_pincfg[]) {
+ .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 = ALC_FIXUP_PINS,
- .v.pins = (const struct alc_pincfg[]) {
+ .type = HDA_FIXUP_PINS,
+ .v.pins = (const struct hda_pintbl[]) {
{ 0x16, 0x99130120 }, /* internal speaker */
{ }
}
},
[ALC262_FIXUP_TYAN] = {
- .type = ALC_FIXUP_PINS,
- .v.pins = (const struct alc_pincfg[]) {
+ .type = HDA_FIXUP_PINS,
+ .v.pins = (const struct hda_pintbl[]) {
{ 0x14, 0x1993e1f0 }, /* int AUX */
{ }
}
},
[ALC262_FIXUP_LENOVO_3000] = {
- .type = ALC_FIXUP_VERBS,
- .v.verbs = (const struct hda_verb[]) {
- { 0x19, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF50 },
+ .type = HDA_FIXUP_PINCTLS,
+ .v.pins = (const struct hda_pintbl[]) {
+ { 0x19, PIN_VREF50 },
{}
},
.chained = true,
.chain_id = ALC262_FIXUP_BENQ,
},
[ALC262_FIXUP_BENQ] = {
- .type = ALC_FIXUP_VERBS,
+ .type = HDA_FIXUP_VERBS,
.v.verbs = (const struct hda_verb[]) {
{ 0x20, AC_VERB_SET_COEF_INDEX, 0x07 },
{ 0x20, AC_VERB_SET_PROC_COEF, 0x3070 },
@@ -4847,31 +2459,40 @@ static const struct alc_fixup alc262_fixups[] = {
}
},
[ALC262_FIXUP_BENQ_T31] = {
- .type = ALC_FIXUP_VERBS,
+ .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 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", ALC262_FIXUP_BENQ),
+ 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),
{}
};
-
-#ifdef CONFIG_SND_HDA_POWER_SAVE
-#define alc262_loopbacks alc880_loopbacks
-#endif
+static const struct hda_model_fixup alc262_fixup_models[] = {
+ {.id = ALC262_FIXUP_INV_DMIC, .name = "inv-dmic"},
+ {}
+};
/*
*/
@@ -4880,13 +2501,12 @@ static int patch_alc262(struct hda_codec *codec)
struct alc_spec *spec;
int err;
- spec = kzalloc(sizeof(*spec), GFP_KERNEL);
- if (spec == NULL)
- return -ENOMEM;
-
- codec->spec = spec;
+ err = alc_alloc_spec(codec, 0x0b);
+ if (err < 0)
+ return err;
- spec->mixer_nid = 0x0b;
+ spec = codec->spec;
+ spec->gen.shared_mic_vref_pin = 0x18;
#if 0
/* pshou 07/11/05 set a zero PCM sample to DAC when FIFO is
@@ -4900,44 +2520,29 @@ static int patch_alc262(struct hda_codec *codec)
snd_hda_codec_write(codec, 0x1a, 0, AC_VERB_SET_PROC_COEF, tmp | 0x80);
}
#endif
- alc_auto_parse_customize_define(codec);
-
alc_fix_pll_init(codec, 0x20, 0x0a, 10);
- alc_pick_fixup(codec, NULL, alc262_fixup_tbl, alc262_fixups);
- alc_apply_fixup(codec, ALC_FIXUP_ACT_PRE_PROBE);
+ snd_hda_pick_fixup(codec, alc262_fixup_models, alc262_fixup_tbl,
+ alc262_fixups);
+ snd_hda_apply_fixup(codec, HDA_FIXUP_ACT_PRE_PROBE);
+
+ alc_auto_parse_customize_define(codec);
+
+ if (has_cdefine_beep(codec))
+ spec->gen.beep_nid = 0x01;
/* automatic parse from the BIOS config */
err = alc262_parse_auto_config(codec);
if (err < 0)
goto error;
- if (!spec->no_analog && !spec->adc_nids) {
- alc_auto_fill_adc_caps(codec);
- alc_rebuild_imux_for_auto_mic(codec);
- alc_remove_invalid_adc_nids(codec);
- }
-
- if (!spec->no_analog && !spec->cap_mixer)
- set_capture_mixer(codec);
-
- if (!spec->no_analog && has_cdefine_beep(codec)) {
- err = snd_hda_attach_beep_device(codec, 0x1);
- if (err < 0)
- goto error;
+ if (!spec->gen.no_analog && spec->gen.beep_nid)
set_beep_amp(spec, 0x0b, 0x05, HDA_INPUT);
- }
-
- alc_apply_fixup(codec, ALC_FIXUP_ACT_PROBE);
codec->patch_ops = alc_patch_ops;
- spec->init_hook = alc_auto_init_std;
spec->shutup = alc_eapd_shutup;
-#ifdef CONFIG_SND_HDA_POWER_SAVE
- if (!spec->loopback.amplist)
- spec->loopback.amplist = alc262_loopbacks;
-#endif
+ snd_hda_apply_fixup(codec, HDA_FIXUP_ACT_PROBE);
return 0;
@@ -4973,21 +2578,56 @@ static const struct hda_verb alc268_beep_init_verbs[] = {
{ }
};
+enum {
+ ALC268_FIXUP_INV_DMIC,
+ ALC268_FIXUP_HP_EAPD,
+ ALC268_FIXUP_SPDIF,
+};
+
+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 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 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),
+ {}
+};
+
/*
* BIOS auto configuration
*/
static int alc268_parse_auto_config(struct hda_codec *codec)
{
static const hda_nid_t alc268_ssids[] = { 0x15, 0x1b, 0x14, 0 };
- struct alc_spec *spec = codec->spec;
- int err = alc_parse_auto_config(codec, NULL, alc268_ssids);
- if (err > 0) {
- if (!spec->no_analog && spec->autocfg.speaker_pins[0] != 0x1d) {
- add_mixer(spec, alc268_beep_mixer);
- add_verb(spec, alc268_beep_init_verbs);
- }
- }
- return err;
+ return alc_parse_auto_config(codec, NULL, alc268_ssids);
}
/*
@@ -4995,33 +2635,28 @@ static int alc268_parse_auto_config(struct hda_codec *codec)
static int patch_alc268(struct hda_codec *codec)
{
struct alc_spec *spec;
- int i, has_beep, err;
+ int err;
- spec = kzalloc(sizeof(*spec), GFP_KERNEL);
- if (spec == NULL)
- return -ENOMEM;
+ /* ALC268 has no aa-loopback mixer */
+ err = alc_alloc_spec(codec, 0);
+ if (err < 0)
+ return err;
- codec->spec = spec;
+ spec = codec->spec;
+ spec->gen.beep_nid = 0x01;
- /* ALC268 has no aa-loopback mixer */
+ snd_hda_pick_fixup(codec, alc268_fixup_models, alc268_fixup_tbl, alc268_fixups);
+ snd_hda_apply_fixup(codec, HDA_FIXUP_ACT_PRE_PROBE);
/* automatic parse from the BIOS config */
err = alc268_parse_auto_config(codec);
if (err < 0)
goto error;
- has_beep = 0;
- for (i = 0; i < spec->num_mixers; i++) {
- if (spec->mixers[i] == alc268_beep_mixer) {
- has_beep = 1;
- break;
- }
- }
-
- if (has_beep) {
- err = snd_hda_attach_beep_device(codec, 0x1);
- if (err < 0)
- goto error;
+ 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,
@@ -5031,19 +2666,11 @@ static int patch_alc268(struct hda_codec *codec)
(0 << AC_AMPCAP_MUTE_SHIFT));
}
- if (!spec->no_analog && !spec->adc_nids) {
- alc_auto_fill_adc_caps(codec);
- alc_rebuild_imux_for_auto_mic(codec);
- alc_remove_invalid_adc_nids(codec);
- }
-
- if (!spec->no_analog && !spec->cap_mixer)
- set_capture_mixer(codec);
-
codec->patch_ops = alc_patch_ops;
- spec->init_hook = alc_auto_init_std;
spec->shutup = alc_eapd_shutup;
+ snd_hda_apply_fixup(codec, HDA_FIXUP_ACT_PROBE);
+
return 0;
error:
@@ -5054,9 +2681,34 @@ static int patch_alc268(struct hda_codec *codec)
/*
* ALC269
*/
-#ifdef CONFIG_SND_HDA_POWER_SAVE
-#define alc269_loopbacks alc880_loopbacks
-#endif
+
+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;
+ return snd_hda_multi_out_analog_open(codec, &spec->multiout, substream,
+ hinfo);
+}
+
+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_gen_spec *spec = codec->spec;
+ return snd_hda_multi_out_analog_prepare(codec, &spec->multiout,
+ stream_tag, format, substream);
+}
+
+static int 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_analog_cleanup(codec, &spec->multiout);
+}
static const struct hda_pcm_stream alc269_44k_pcm_analog_playback = {
.substreams = 1,
@@ -5065,9 +2717,9 @@ static const struct hda_pcm_stream alc269_44k_pcm_analog_playback = {
.rates = SNDRV_PCM_RATE_44100, /* fixed rate */
/* NID is set in alc_build_pcms */
.ops = {
- .open = alc_playback_pcm_open,
- .prepare = alc_playback_pcm_prepare,
- .cleanup = alc_playback_pcm_cleanup
+ .open = playback_pcm_open,
+ .prepare = playback_pcm_prepare,
+ .cleanup = playback_pcm_cleanup
},
};
@@ -5079,40 +2731,19 @@ static const struct hda_pcm_stream alc269_44k_pcm_analog_capture = {
/* NID is set in alc_build_pcms */
};
-#ifdef CONFIG_SND_HDA_POWER_SAVE
-static int alc269_mic2_for_mute_led(struct hda_codec *codec)
-{
- switch (codec->subsystem_id) {
- case 0x103c1586:
- return 1;
- }
- return 0;
-}
-
-static int alc269_mic2_mute_check_ps(struct hda_codec *codec, hda_nid_t nid)
-{
- /* 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);
- }
- return alc_check_power_status(codec, nid);
-}
-#endif /* CONFIG_SND_HDA_POWER_SAVE */
-
/* 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,
};
/*
@@ -5124,13 +2755,33 @@ static int alc269_parse_auto_config(struct hda_codec *codec)
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;
- const hda_nid_t *ssids = spec->codec_variant == ALC269_TYPE_ALC269VA ?
- alc269va_ssids : alc269_ssids;
+ 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;
+ }
return alc_parse_auto_config(codec, alc269_ignore, ssids);
}
-static void alc269_toggle_power_output(struct hda_codec *codec, int power_up)
+static void alc269vb_toggle_power_output(struct hda_codec *codec, int power_up)
{
int val = alc_read_coef_idx(codec, 0x04);
if (power_up)
@@ -5142,52 +2793,493 @@ static void alc269_toggle_power_output(struct hda_codec *codec, int power_up)
static void alc269_shutup(struct hda_codec *codec)
{
- if ((alc_get_coef0(codec) & 0x00ff) == 0x017)
- alc269_toggle_power_output(codec, 0);
- if ((alc_get_coef0(codec) & 0x00ff) == 0x018) {
- alc269_toggle_power_output(codec, 0);
+ struct alc_spec *spec = codec->spec;
+
+ 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 void alc282_restore_default_value(struct hda_codec *codec)
+{
+ int val;
+
+ /* 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);
+
+ /* Index 0x78 Direct Drive HP AMP LPM Control 1 */
+ /* Headphone capless set to high power mode */
+ alc_write_coef_idx(codec, 0x78, 0x9004);
+
+ if (hp_pin_sense)
+ msleep(2);
+
+ snd_hda_codec_write(codec, hp_pin, 0,
+ AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE);
+
+ if (hp_pin_sense)
+ msleep(85);
+
+ snd_hda_codec_write(codec, hp_pin, 0,
+ AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT);
+
+ if (hp_pin_sense)
+ msleep(100);
+
+ /* Headphone capless set to normal mode */
+ alc_write_coef_idx(codec, 0x78, coef78);
+}
+
+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;
+
+ if (!hp_pin) {
+ alc269_shutup(codec);
+ return;
+ }
+
+ 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);
+
+ snd_hda_codec_write(codec, hp_pin, 0,
+ AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE);
+
+ if (hp_pin_sense)
+ msleep(85);
+
+ snd_hda_codec_write(codec, hp_pin, 0,
+ AC_VERB_SET_PIN_WIDGET_CONTROL, 0x0);
+
+ 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 void alc283_restore_default_value(struct hda_codec *codec)
+{
+ int val;
+
+ /* 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;
+
+ 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];
+ }
+
+ alc283_restore_default_value(codec);
+
+ if (!hp_pin)
+ return;
+ hp_pin_sense = snd_hda_jack_detect(codec, hp_pin);
+
+ /* Index 0x43 Direct Drive HP AMP LPM Control 1 */
+ /* Headphone capless set to high power mode */
+ alc_write_coef_idx(codec, 0x43, 0x9004);
+
+ snd_hda_codec_write(codec, hp_pin, 0,
+ AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE);
+
+ if (hp_pin_sense)
+ msleep(85);
+
+ snd_hda_codec_write(codec, hp_pin, 0,
+ AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT);
+
+ 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);
+}
+
+static void alc283_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 val;
+
+ 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];
+ }
+
+ if (!hp_pin) {
+ alc269_shutup(codec);
+ return;
+ }
+
+ hp_pin_sense = snd_hda_jack_detect(codec, hp_pin);
+
+ alc_write_coef_idx(codec, 0x43, 0x9004);
+
+ snd_hda_codec_write(codec, hp_pin, 0,
+ AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE);
+
+ if (hp_pin_sense)
+ msleep(100);
+
+ snd_hda_codec_write(codec, hp_pin, 0,
+ AC_VERB_SET_PIN_WIDGET_CONTROL, 0x0);
+
+ 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);
+}
+
+static void alc5505_coef_set(struct hda_codec *codec, unsigned int index_reg,
+ unsigned int val)
+{
+ 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 */
+}
+
+static int alc5505_coef_get(struct hda_codec *codec, unsigned int index_reg)
+{
+ unsigned int val;
+
+ 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 void alc5505_dsp_halt(struct hda_codec *codec)
+{
+ unsigned int val;
+
+ 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)
+{
+ 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);
+}
+
+static void alc5505_dsp_init(struct hda_codec *codec)
+{
+ unsigned int val;
+
+ 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
+}
+
+#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
+
#ifdef CONFIG_PM
+static int alc269_suspend(struct hda_codec *codec)
+{
+ struct alc_spec *spec = codec->spec;
+
+ if (spec->has_alc5505_dsp)
+ alc5505_dsp_suspend(codec);
+ return alc_suspend(codec);
+}
+
static int alc269_resume(struct hda_codec *codec)
{
- if ((alc_get_coef0(codec) & 0x00ff) == 0x018) {
- alc269_toggle_power_output(codec, 0);
+ struct alc_spec *spec = codec->spec;
+
+ 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);
}
codec->patch_ops.init(codec);
- if ((alc_get_coef0(codec) & 0x00ff) == 0x017) {
- alc269_toggle_power_output(codec, 1);
+ 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 ((alc_get_coef0(codec) & 0x00ff) == 0x018)
- alc269_toggle_power_output(codec, 1);
-
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 */
+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;
+
+ if (action == HDA_FIXUP_ACT_PRE_PROBE)
+ spec->parse_flags = HDA_PINCFG_NO_HP_FIXUP;
+}
+
static void alc269_fixup_hweq(struct hda_codec *codec,
- const struct alc_fixup *fix, int action)
+ const struct hda_fixup *fix, int action)
{
int coef;
- if (action != ALC_FIXUP_ACT_INIT)
+ if (action != HDA_FIXUP_ACT_INIT)
return;
coef = alc_read_coef_idx(codec, 0x1e);
alc_write_coef_idx(codec, 0x1e, coef | 0x80);
}
+static void alc269_fixup_headset_mic(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->parse_flags |= HDA_PINCFG_HEADSET_MIC;
+}
+
static void alc271_fixup_dmic(struct hda_codec *codec,
- const struct alc_fixup *fix, int action)
+ const struct hda_fixup *fix, int action)
{
static const struct hda_verb verbs[] = {
{0x20, AC_VERB_SET_COEF_INDEX, 0x0d},
@@ -5196,7 +3288,8 @@ static void alc271_fixup_dmic(struct hda_codec *codec,
};
unsigned int cfg;
- if (strcmp(codec->chip_name, "ALC271X"))
+ 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)
@@ -5204,26 +3297,26 @@ static void alc271_fixup_dmic(struct hda_codec *codec,
}
static void alc269_fixup_pcm_44k(struct hda_codec *codec,
- const struct alc_fixup *fix, int action)
+ const struct hda_fixup *fix, int action)
{
struct alc_spec *spec = codec->spec;
- if (action != ALC_FIXUP_ACT_PROBE)
+ if (action != HDA_FIXUP_ACT_PROBE)
return;
/* 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;
+ spec->gen.stream_analog_playback = &alc269_44k_pcm_analog_playback;
+ spec->gen.stream_analog_capture = &alc269_44k_pcm_analog_capture;
}
static void alc269_fixup_stereo_dmic(struct hda_codec *codec,
- const struct alc_fixup *fix, int action)
+ const struct hda_fixup *fix, int action)
{
int coef;
- if (action != ALC_FIXUP_ACT_INIT)
+ if (action != HDA_FIXUP_ACT_INIT)
return;
/* The digital-mic unit sends PDM (differential signal) instead of
* the standard PCM, thus you can't record a valid mono stream as is.
@@ -5236,7 +3329,7 @@ static void alc269_fixup_stereo_dmic(struct hda_codec *codec,
static void alc269_quanta_automute(struct hda_codec *codec)
{
- update_outputs(codec);
+ snd_hda_gen_update_outputs(codec);
snd_hda_codec_write(codec, 0x20, 0,
AC_VERB_SET_COEF_INDEX, 0x0c);
@@ -5250,14 +3343,894 @@ static void alc269_quanta_automute(struct hda_codec *codec)
}
static void alc269_fixup_quanta_mute(struct hda_codec *codec,
- const struct alc_fixup *fix, int action)
+ const struct hda_fixup *fix, int action)
+{
+ struct alc_spec *spec = codec->spec;
+ if (action != HDA_FIXUP_ACT_PROBE)
+ return;
+ spec->gen.automute_hook = alc269_quanta_automute;
+}
+
+static void alc269_x101_hp_automute_hook(struct hda_codec *codec,
+ struct hda_jack_tbl *jack)
+{
+ struct alc_spec *spec = codec->spec;
+ 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 alc269_fixup_x101_headset_mic(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->parse_flags |= HDA_PINCFG_HEADSET_MIC;
+ spec->gen.hp_automute_hook = alc269_x101_hp_automute_hook;
+ }
+}
+
+
+/* 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;
+
+ 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);
+}
+
+/* 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;
- if (action != ALC_FIXUP_ACT_PROBE)
+
+ if (power_state != AC_PWRST_D3 || nid != spec->mute_led_nid)
+ return power_state;
+
+ /* 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));
+
+ return AC_PWRST_D0;
+}
+
+static void alc269_fixup_hp_mute_led(struct hda_codec *codec,
+ const struct hda_fixup *fix, int action)
+{
+ struct alc_spec *spec = codec->spec;
+ const struct dmi_device *dev = NULL;
+
+ if (action != HDA_FIXUP_ACT_PRE_PROBE)
return;
- spec->automute_hook = alc269_quanta_automute;
+
+ 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;
+ }
+}
+
+static void alc269_fixup_hp_mute_led_mic1(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->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;
+ }
+}
+
+static void alc269_fixup_hp_mute_led_mic2(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->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;
+ }
+}
+
+/* turn on/off mute LED per vmaster hook */
+static void alc269_fixup_hp_gpio_mute_hook(void *private_data, int enabled)
+{
+ struct hda_codec *codec = private_data;
+ struct alc_spec *spec = codec->spec;
+ unsigned int oldval = spec->gpio_led;
+
+ if (enabled)
+ spec->gpio_led &= ~0x08;
+ else
+ spec->gpio_led |= 0x08;
+ if (spec->gpio_led != oldval)
+ snd_hda_codec_write(codec, 0x01, 0, AC_VERB_SET_GPIO_DATA,
+ spec->gpio_led);
+}
+
+/* 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;
+ unsigned int oldval = spec->gpio_led;
+
+ 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 alc269_fixup_hp_gpio_led(struct hda_codec *codec,
+ const struct hda_fixup *fix, int action)
+{
+ struct alc_spec *spec = codec->spec;
+ static const struct hda_verb gpio_init[] = {
+ { 0x01, AC_VERB_SET_GPIO_MASK, 0x18 },
+ { 0x01, AC_VERB_SET_GPIO_DIRECTION, 0x18 },
+ {}
+ };
+
+ 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);
+ }
+}
+
+static void alc_headset_mode_unplugged(struct hda_codec *codec)
+{
+ int val;
+
+ 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");
+}
+
+
+static void alc_headset_mode_mic_in(struct hda_codec *codec, hda_nid_t hp_pin,
+ hda_nid_t mic_pin)
+{
+ int val;
+
+ 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;
+ }
+ codec_dbg(codec, "Headset jack set to mic-in mode.\n");
+}
+
+static void alc_headset_mode_default(struct hda_codec *codec)
+{
+ int val;
+
+ 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");
+}
+
+/* Iphone type */
+static void alc_headset_mode_ctia(struct hda_codec *codec)
+{
+ int val;
+
+ 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");
+}
+
+/* Nokia type */
+static void alc_headset_mode_omtp(struct hda_codec *codec)
+{
+ int val;
+
+ 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");
+}
+
+static void alc_determine_headset_type(struct hda_codec *codec)
+{
+ int val;
+ bool is_ctia = false;
+ struct alc_spec *spec = codec->spec;
+
+ 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;
+ }
+
+ 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 void alc_update_headset_mode(struct hda_codec *codec)
+{
+ struct alc_spec *spec = codec->spec;
+
+ 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];
+
+ int new_headset_mode;
+
+ 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;
+
+ if (new_headset_mode == spec->current_headset_mode) {
+ snd_hda_gen_update_outputs(codec);
+ return;
+ }
+
+ 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 (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;
+
+ snd_hda_gen_update_outputs(codec);
+}
+
+static void alc_update_headset_mode_hook(struct hda_codec *codec,
+ struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ alc_update_headset_mode(codec);
+}
+
+static void alc_update_headset_jack_cb(struct hda_codec *codec, struct hda_jack_tbl *jack)
+{
+ struct alc_spec *spec = codec->spec;
+ spec->current_headset_type = ALC_HEADSET_TYPE_UNKNOWN;
+ snd_hda_gen_hp_automute(codec, jack);
+}
+
+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;
+
+ /* 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;
+ }
+
+ 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 alc_fixup_headset_mode(struct hda_codec *codec,
+ const struct hda_fixup *fix, int action)
+{
+ struct alc_spec *spec = codec->spec;
+
+ 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 HDA_FIXUP_ACT_INIT:
+ spec->current_headset_mode = 0;
+ alc_update_headset_mode(codec);
+ break;
+ }
+}
+
+static void alc_fixup_headset_mode_no_hp_mic(struct hda_codec *codec,
+ const struct hda_fixup *fix, int action)
+{
+ 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 alc255_set_default_jack_type(struct hda_codec *codec)
+{
+ /* 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 alc_fixup_headset_mode_alc255(struct hda_codec *codec,
+ const struct hda_fixup *fix, int action)
+{
+ if (action == HDA_FIXUP_ACT_PRE_PROBE) {
+ alc255_set_default_jack_type(codec);
+ }
+ alc_fixup_headset_mode(codec, fix, action);
+}
+
+static void alc_fixup_headset_mode_alc255_no_hp_mic(struct hda_codec *codec,
+ const struct hda_fixup *fix, int action)
+{
+ 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);
+}
+
+static void alc_fixup_auto_mute_via_amp(struct hda_codec *codec,
+ const struct hda_fixup *fix, int action)
+{
+ if (action == HDA_FIXUP_ACT_PRE_PROBE) {
+ struct alc_spec *spec = codec->spec;
+ spec->gen.auto_mute_via_amp = 1;
+ }
+}
+
+static void alc_no_shutup(struct hda_codec *codec)
+{
+}
+
+static void alc_fixup_no_shutup(struct hda_codec *codec,
+ const struct hda_fixup *fix, int action)
+{
+ if (action == HDA_FIXUP_ACT_PRE_PROBE) {
+ struct alc_spec *spec = codec->spec;
+ spec->shutup = alc_no_shutup;
+ }
+}
+
+static void alc_fixup_disable_aamix(struct hda_codec *codec,
+ const struct hda_fixup *fix, int action)
+{
+ 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 unsigned int alc_power_filter_xps13(struct hda_codec *codec,
+ hda_nid_t nid,
+ unsigned int power_state)
+{
+ struct alc_spec *spec = codec->spec;
+
+ /* 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;
+}
+
+static void alc_fixup_dell_xps13(struct hda_codec *codec,
+ const struct hda_fixup *fix, int action)
+{
+ if (action == HDA_FIXUP_ACT_PROBE) {
+ struct alc_spec *spec = codec->spec;
+ spec->shutup = alc_no_shutup;
+ codec->power_filter = alc_power_filter_xps13;
+ }
+}
+
+static void alc_fixup_headset_mode_alc668(struct hda_codec *codec,
+ const struct hda_fixup *fix, int action)
+{
+ 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);
+ }
+ alc_fixup_headset_mode(codec, fix, action);
+}
+
+/* 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;
+
+ 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;
+}
+
+static void alc271_hp_gate_mic_jack(struct hda_codec *codec,
+ const struct hda_fixup *fix,
+ int action)
+{
+ struct alc_spec *spec = codec->spec;
+
+ if (action == HDA_FIXUP_ACT_PROBE) {
+ int mic_pin = find_ext_mic_pin(codec);
+ int hp_pin = spec->gen.autocfg.hp_pins[0];
+
+ if (snd_BUG_ON(!mic_pin || !hp_pin))
+ return;
+ snd_hda_jack_set_gating_jack(codec, mic_pin, hp_pin);
+ }
+}
+
+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;
+
+ /* 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 (action != HDA_FIXUP_ACT_PROBE)
+ return;
+
+ 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));
+ }
+}
+
+static void alc283_hp_automute_hook(struct hda_codec *codec,
+ struct hda_jack_tbl *jack)
+{
+ struct alc_spec *spec = codec->spec;
+ int vref;
+
+ msleep(200);
+ snd_hda_gen_hp_automute(codec, jack);
+
+ vref = spec->gen.hp_jack_present ? PIN_VREF80 : 0;
+
+ msleep(600);
+ snd_hda_codec_write(codec, 0x19, 0, AC_VERB_SET_PIN_WIDGET_CONTROL,
+ vref);
+}
+
+static void alc283_fixup_chromebook(struct hda_codec *codec,
+ const struct hda_fixup *fix, int action)
+{
+ struct alc_spec *spec = codec->spec;
+ int 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;
+ }
+}
+
+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;
+
+ 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;
+ }
+}
+
+/* mute tablet speaker pin (0x14) via dock plugging in addition */
+static void asus_tx300_automute(struct hda_codec *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);
+}
+
+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;
+ }
+}
+
+static void alc290_fixup_mono_speakers(struct hda_codec *codec,
+ const struct hda_fixup *fix, int action)
+{
+ 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,
@@ -5266,27 +4239,71 @@ enum {
ALC269_FIXUP_ASUS_G73JW,
ALC269_FIXUP_LENOVO_EAPD,
ALC275_FIXUP_SONY_HWEQ,
+ 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 alc_fixup alc269_fixups[] = {
+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},
@@ -5297,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},
@@ -5306,48 +4323,60 @@ 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 = ALC_FIXUP_FUNC,
+ .type = HDA_FIXUP_FUNC,
.v.func = alc271_fixup_dmic,
},
[ALC269_FIXUP_PCM_44K] = {
- .type = ALC_FIXUP_FUNC,
+ .type = HDA_FIXUP_FUNC,
.v.func = alc269_fixup_pcm_44k,
+ .chained = true,
+ .chain_id = ALC269_FIXUP_QUANTA_MUTE
},
[ALC269_FIXUP_STEREO_DMIC] = {
- .type = ALC_FIXUP_FUNC,
+ .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 = ALC_FIXUP_FUNC,
+ .type = HDA_FIXUP_FUNC,
.v.func = alc269_fixup_quanta_mute,
},
[ALC269_FIXUP_LIFEBOOK] = {
- .type = ALC_FIXUP_PINS,
- .v.pins = (const struct alc_pincfg[]) {
+ .type = HDA_FIXUP_PINS,
+ .v.pins = (const struct hda_pintbl[]) {
{ 0x1a, 0x2101103f }, /* dock line-out */
{ 0x1b, 0x23a11040 }, /* dock mic-in */
{ }
@@ -5355,9 +4384,16 @@ static const struct alc_fixup alc269_fixups[] = {
.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 = ALC_FIXUP_PINS,
- .v.pins = (const struct alc_pincfg[]) {
+ .type = HDA_FIXUP_PINS,
+ .v.pins = (const struct hda_pintbl[]) {
{ 0x14, 0x99130110 }, /* speaker */
{ 0x15, 0x0121401f }, /* HP out */
{ 0x18, 0x01a19c20 }, /* mic */
@@ -5366,8 +4402,8 @@ static const struct alc_fixup alc269_fixups[] = {
},
},
[ALC269_FIXUP_DMIC] = {
- .type = ALC_FIXUP_PINS,
- .v.pins = (const struct alc_pincfg[]) {
+ .type = HDA_FIXUP_PINS,
+ .v.pins = (const struct hda_pintbl[]) {
{ 0x12, 0x99a3092f }, /* int-mic */
{ 0x14, 0x99130110 }, /* speaker */
{ 0x15, 0x0121401f }, /* HP out */
@@ -5376,8 +4412,8 @@ static const struct alc_fixup alc269_fixups[] = {
},
},
[ALC269VB_FIXUP_AMIC] = {
- .type = ALC_FIXUP_PINS,
- .v.pins = (const struct alc_pincfg[]) {
+ .type = HDA_FIXUP_PINS,
+ .v.pins = (const struct hda_pintbl[]) {
{ 0x14, 0x99130110 }, /* speaker */
{ 0x18, 0x01a19c20 }, /* mic */
{ 0x19, 0x99a3092f }, /* int-mic */
@@ -5386,8 +4422,8 @@ static const struct alc_fixup alc269_fixups[] = {
},
},
[ALC269VB_FIXUP_DMIC] = {
- .type = ALC_FIXUP_PINS,
- .v.pins = (const struct alc_pincfg[]) {
+ .type = HDA_FIXUP_PINS,
+ .v.pins = (const struct hda_pintbl[]) {
{ 0x12, 0x99a3092f }, /* int-mic */
{ 0x14, 0x99130110 }, /* speaker */
{ 0x18, 0x01a19c20 }, /* mic */
@@ -5395,37 +4431,475 @@ static const struct alc_fixup alc269_fixups[] = {
{ }
},
},
+ [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 const struct snd_pci_quirk alc269_fixup_tbl[] = {
- SND_PCI_QUIRK(0x1043, 0x1a13, "Asus G73Jw", ALC269_FIXUP_ASUS_G73JW),
+ 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_VENDOR(0x1025, "Acer Aspire", ALC271_FIXUP_DMIC),
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(0x17aa, 0x3bf8, "Quanta FL1", ALC269_FIXUP_QUANTA_MUTE),
- SND_PCI_QUIRK(0x17aa, 0x3bf8, "Lenovo Ideapd", ALC269_FIXUP_PCM_44K),
+ 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 */
-#if 1
+#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),
@@ -5471,20 +4945,169 @@ static const struct snd_pci_quirk alc269_fixup_tbl[] = {
{}
};
-static const struct alc_model_fixup alc269_fixup_models[] = {
+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 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 (spec->codec_variant != ALC269_TYPE_ALC269VB)
- return 0;
+ return;
if ((alc_get_coef0(codec) & 0x00ff) < 0x015) {
alc_write_coef_idx(codec, 0xf, 0x960b);
@@ -5520,8 +5143,6 @@ static int alc269_fill_coef(struct hda_codec *codec)
val = alc_read_coef_idx(codec, 0x4); /* HP */
alc_write_coef_idx(codec, 0x4, val | (1<<11));
-
- return 0;
}
/*
@@ -5529,85 +5150,109 @@ static int alc269_fill_coef(struct hda_codec *codec)
static int patch_alc269(struct hda_codec *codec)
{
struct alc_spec *spec;
- int err = 0;
+ 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;
- spec->mixer_nid = 0x0b;
+ 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);
- err = alc_codec_rename_from_preset(codec);
- if (err < 0)
- goto error;
+ if (has_cdefine_beep(codec))
+ spec->gen.beep_nid = 0x01;
- if (codec->vendor_id == 0x10ec0269) {
+ switch (codec->vendor_id) {
+ case 0x10ec0269:
spec->codec_variant = ALC269_TYPE_ALC269VA;
switch (alc_get_coef0(codec) & 0x00f0) {
case 0x0010:
- if (codec->bus->pci->subsystem_vendor == 0x1025 &&
+ 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->subsystem_vendor == 0x17aa &&
+ 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);
+ break;
+
+ 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;
}
- alc_pick_fixup(codec, alc269_fixup_models,
- alc269_fixup_tbl, alc269_fixups);
- alc_apply_fixup(codec, ALC_FIXUP_ACT_PRE_PROBE);
+ if (snd_hda_codec_read(codec, 0x51, 0, AC_VERB_PARAMETERS, 0) == 0x10ec5505) {
+ spec->has_alc5505_dsp = 1;
+ spec->init_hook = alc5505_dsp_init;
+ }
/* automatic parse from the BIOS config */
err = alc269_parse_auto_config(codec);
if (err < 0)
goto error;
- if (!spec->no_analog && !spec->adc_nids) {
- alc_auto_fill_adc_caps(codec);
- alc_rebuild_imux_for_auto_mic(codec);
- alc_remove_invalid_adc_nids(codec);
- }
-
- if (!spec->no_analog && !spec->cap_mixer)
- set_capture_mixer(codec);
-
- if (!spec->no_analog && has_cdefine_beep(codec)) {
- err = snd_hda_attach_beep_device(codec, 0x1);
- if (err < 0)
- goto error;
+ 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);
codec->patch_ops = alc_patch_ops;
#ifdef CONFIG_PM
+ codec->patch_ops.suspend = alc269_suspend;
codec->patch_ops.resume = alc269_resume;
#endif
- spec->init_hook = alc_auto_init_std;
- spec->shutup = alc269_shutup;
-
-#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
+ if (!spec->shutup)
+ spec->shutup = alc269_shutup;
+
+ snd_hda_apply_fixup(codec, HDA_FIXUP_ACT_PROBE);
return 0;
@@ -5627,62 +5272,84 @@ static int alc861_parse_auto_config(struct hda_codec *codec)
return alc_parse_auto_config(codec, alc861_ignore, alc861_ssids);
}
-#ifdef CONFIG_SND_HDA_POWER_SAVE
-static const struct hda_amp_list alc861_loopbacks[] = {
- { 0x15, HDA_INPUT, 0 },
- { 0x15, HDA_INPUT, 1 },
- { 0x15, HDA_INPUT, 2 },
- { 0x15, HDA_INPUT, 3 },
- { } /* end */
-};
-#endif
-
-
/* Pin config fixes */
enum {
- PINFIX_FSC_AMILO_PI1505,
- PINFIX_ASUS_A6RP,
+ ALC861_FIXUP_FSC_AMILO_PI1505,
+ ALC861_FIXUP_AMP_VREF_0F,
+ ALC861_FIXUP_NO_JACK_DETECT,
+ ALC861_FIXUP_ASUS_A6RP,
+ ALC660_FIXUP_ASUS_W7J,
};
/* 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 alc_fixup *fix, int action)
+ const struct hda_fixup *fix, int action)
{
struct alc_spec *spec = codec->spec;
unsigned int val;
- if (action != ALC_FIXUP_ACT_INIT)
+ if (action != HDA_FIXUP_ACT_INIT)
return;
- val = snd_hda_codec_read(codec, 0x0f, 0,
- AC_VERB_GET_PIN_WIDGET_CONTROL, 0);
+ 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_codec_write(codec, 0x0f, 0,
- AC_VERB_SET_PIN_WIDGET_CONTROL, val);
- spec->keep_vref_in_automute = 1;
+ snd_hda_set_pin_ctl(codec, 0x0f, val);
+ spec->gen.keep_vref_in_automute = 1;
}
-static const struct alc_fixup alc861_fixups[] = {
- [PINFIX_FSC_AMILO_PI1505] = {
- .type = ALC_FIXUP_PINS,
- .v.pins = (const struct alc_pincfg[]) {
+/* suppress the jack-detection */
+static void alc_fixup_no_jack_detect(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 alc861_fixups[] = {
+ [ALC861_FIXUP_FSC_AMILO_PI1505] = {
+ .type = HDA_FIXUP_PINS,
+ .v.pins = (const struct hda_pintbl[]) {
{ 0x0b, 0x0221101f }, /* HP */
{ 0x0f, 0x90170310 }, /* speaker */
{ }
}
},
- [PINFIX_ASUS_A6RP] = {
- .type = ALC_FIXUP_FUNC,
+ [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 const struct snd_pci_quirk alc861_fixup_tbl[] = {
- SND_PCI_QUIRK_VENDOR(0x1043, "ASUS laptop", PINFIX_ASUS_A6RP),
- SND_PCI_QUIRK(0x1584, 0x0000, "Uniwill ECS M31EI", PINFIX_ASUS_A6RP),
- SND_PCI_QUIRK(0x1584, 0x2b01, "Haier W18", PINFIX_ASUS_A6RP),
- SND_PCI_QUIRK(0x1734, 0x10c7, "FSC Amilo Pi1505", PINFIX_FSC_AMILO_PI1505),
+ 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),
{}
};
@@ -5693,48 +5360,31 @@ static int patch_alc861(struct hda_codec *codec)
struct alc_spec *spec;
int err;
- spec = kzalloc(sizeof(*spec), GFP_KERNEL);
- if (spec == NULL)
- return -ENOMEM;
-
- codec->spec = spec;
+ err = alc_alloc_spec(codec, 0x15);
+ if (err < 0)
+ return err;
- spec->mixer_nid = 0x15;
+ spec = codec->spec;
+ spec->gen.beep_nid = 0x23;
- alc_pick_fixup(codec, NULL, alc861_fixup_tbl, alc861_fixups);
- alc_apply_fixup(codec, ALC_FIXUP_ACT_PRE_PROBE);
+ snd_hda_pick_fixup(codec, NULL, alc861_fixup_tbl, alc861_fixups);
+ snd_hda_apply_fixup(codec, HDA_FIXUP_ACT_PRE_PROBE);
/* automatic parse from the BIOS config */
err = alc861_parse_auto_config(codec);
if (err < 0)
goto error;
- if (!spec->no_analog && !spec->adc_nids) {
- alc_auto_fill_adc_caps(codec);
- alc_rebuild_imux_for_auto_mic(codec);
- alc_remove_invalid_adc_nids(codec);
- }
-
- if (!spec->no_analog && !spec->cap_mixer)
- set_capture_mixer(codec);
-
- if (!spec->no_analog) {
- err = snd_hda_attach_beep_device(codec, 0x23);
- if (err < 0)
- goto error;
+ if (!spec->gen.no_analog)
set_beep_amp(spec, 0x23, 0, HDA_OUTPUT);
- }
-
- alc_apply_fixup(codec, ALC_FIXUP_ACT_PROBE);
codec->patch_ops = alc_patch_ops;
- spec->init_hook = alc_auto_init_std;
-#ifdef CONFIG_SND_HDA_POWER_SAVE
+#ifdef CONFIG_PM
spec->power_hook = alc_power_eapd;
- if (!spec->loopback.amplist)
- spec->loopback.amplist = alc861_loopbacks;
#endif
+ snd_hda_apply_fixup(codec, HDA_FIXUP_ACT_PROBE);
+
return 0;
error:
@@ -5749,10 +5399,6 @@ static int patch_alc861(struct hda_codec *codec)
*
* In addition, an independent DAC
*/
-#ifdef CONFIG_SND_HDA_POWER_SAVE
-#define alc861vd_loopbacks alc880_loopbacks
-#endif
-
static int alc861vd_parse_auto_config(struct hda_codec *codec)
{
static const hda_nid_t alc861vd_ignore[] = { 0x1d, 0 };
@@ -5767,17 +5413,17 @@ enum {
/* exclude VREF80 */
static void alc861vd_fixup_dallas(struct hda_codec *codec,
- const struct alc_fixup *fix, int action)
+ const struct hda_fixup *fix, int action)
{
- if (action == ALC_FIXUP_ACT_PRE_PROBE) {
- snd_hda_override_pin_caps(codec, 0x18, 0x00001714);
- snd_hda_override_pin_caps(codec, 0x19, 0x0000171c);
+ if (action == HDA_FIXUP_ACT_PRE_PROBE) {
+ snd_hda_override_pin_caps(codec, 0x18, 0x00000734);
+ snd_hda_override_pin_caps(codec, 0x19, 0x0000073c);
}
}
-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},
@@ -5787,7 +5433,7 @@ static const struct alc_fixup alc861vd_fixups[] = {
}
},
[ALC861VD_FIX_DALLAS] = {
- .type = ALC_FIXUP_FUNC,
+ .type = HDA_FIXUP_FUNC,
.v.func = alc861vd_fixup_dallas,
},
};
@@ -5799,12 +5445,6 @@ static const struct snd_pci_quirk alc861vd_fixup_tbl[] = {
{}
};
-static const struct hda_verb alc660vd_eapd_verbs[] = {
- {0x14, AC_VERB_SET_EAPD_BTLENABLE, 2},
- {0x15, AC_VERB_SET_EAPD_BTLENABLE, 2},
- { }
-};
-
/*
*/
static int patch_alc861vd(struct hda_codec *codec)
@@ -5812,53 +5452,29 @@ static int patch_alc861vd(struct hda_codec *codec)
struct alc_spec *spec;
int err;
- spec = kzalloc(sizeof(*spec), GFP_KERNEL);
- if (spec == NULL)
- return -ENOMEM;
-
- codec->spec = spec;
+ err = alc_alloc_spec(codec, 0x0b);
+ if (err < 0)
+ return err;
- spec->mixer_nid = 0x0b;
+ spec = codec->spec;
+ spec->gen.beep_nid = 0x23;
- alc_pick_fixup(codec, NULL, alc861vd_fixup_tbl, alc861vd_fixups);
- alc_apply_fixup(codec, ALC_FIXUP_ACT_PRE_PROBE);
+ snd_hda_pick_fixup(codec, NULL, alc861vd_fixup_tbl, alc861vd_fixups);
+ snd_hda_apply_fixup(codec, HDA_FIXUP_ACT_PRE_PROBE);
/* automatic parse from the BIOS config */
err = alc861vd_parse_auto_config(codec);
if (err < 0)
goto error;
- if (codec->vendor_id == 0x10ec0660) {
- /* always turn on EAPD */
- add_verb(spec, alc660vd_eapd_verbs);
- }
-
- if (!spec->no_analog && !spec->adc_nids) {
- alc_auto_fill_adc_caps(codec);
- alc_rebuild_imux_for_auto_mic(codec);
- alc_remove_invalid_adc_nids(codec);
- }
-
- if (!spec->no_analog && !spec->cap_mixer)
- set_capture_mixer(codec);
-
- if (!spec->no_analog) {
- err = snd_hda_attach_beep_device(codec, 0x23);
- if (err < 0)
- goto error;
+ if (!spec->gen.no_analog)
set_beep_amp(spec, 0x0b, 0x05, HDA_INPUT);
- }
-
- alc_apply_fixup(codec, ALC_FIXUP_ACT_PROBE);
codec->patch_ops = alc_patch_ops;
- spec->init_hook = alc_auto_init_std;
spec->shutup = alc_eapd_shutup;
-#ifdef CONFIG_SND_HDA_POWER_SAVE
- if (!spec->loopback.amplist)
- spec->loopback.amplist = alc861vd_loopbacks;
-#endif
+
+ snd_hda_apply_fixup(codec, HDA_FIXUP_ACT_PROBE);
return 0;
@@ -5878,9 +5494,6 @@ static int patch_alc861vd(struct hda_codec *codec)
* In addition, an independent DAC for the multi-playback (not used in this
* driver yet).
*/
-#ifdef CONFIG_SND_HDA_POWER_SAVE
-#define alc662_loopbacks alc880_loopbacks
-#endif
/*
* BIOS auto configuration
@@ -5894,7 +5507,8 @@ static int alc662_parse_auto_config(struct hda_codec *codec)
const hda_nid_t *ssids;
if (codec->vendor_id == 0x10ec0272 || codec->vendor_id == 0x10ec0663 ||
- codec->vendor_id == 0x10ec0665 || codec->vendor_id == 0x10ec0670)
+ codec->vendor_id == 0x10ec0665 || codec->vendor_id == 0x10ec0670 ||
+ codec->vendor_id == 0x10ec0671)
ssids = alc663_ssids;
else
ssids = alc662_ssids;
@@ -5902,21 +5516,85 @@ static int alc662_parse_auto_config(struct hda_codec *codec)
}
static void alc272_fixup_mario(struct hda_codec *codec,
- const struct alc_fixup *fix, int action)
+ const struct hda_fixup *fix, int action)
{
- if (action != ALC_FIXUP_ACT_PROBE)
+ 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)))
- printk(KERN_WARNING
- "hda_codec: failed to override amp caps for NID 0x2\n");
+ codec_warn(codec, "failed to override amp caps for NID 0x2\n");
+}
+
+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 */
+ { }
+};
+
+/* override the 2.1 chmap */
+static void alc_fixup_bass_chmap(struct hda_codec *codec,
+ const struct hda_fixup *fix, int action)
+{
+ 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;
+ }
+}
+
+/* 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;
+ unsigned int oldval = spec->gpio_led;
+
+ 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);
+}
+
+/* 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;
+ if (nid == codec->afg && power_state == AC_PWRST_D3 && spec->gpio_led)
+ return AC_PWRST_D0;
+ return power_state;
+}
+
+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 struct hda_verb gpio_init[] = {
+ { 0x01, AC_VERB_SET_GPIO_MASK, 0x01 },
+ { 0x01, AC_VERB_SET_GPIO_DIRECTION, 0x01 },
+ {}
+ };
+
+ 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;
+ }
}
enum {
ALC662_FIXUP_ASPIRE,
+ ALC662_FIXUP_LED_GPIO1,
ALC662_FIXUP_IDEAPAD,
ALC272_FIXUP_MARIO,
ALC662_FIXUP_CZC_P10T,
@@ -5930,41 +5608,59 @@ enum {
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 alc_fixup alc662_fixups[] = {
+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_SKU_IGNORE] = {
- .type = ALC_FIXUP_SKU,
- .v.sku = ALC_FIXUP_SKU_IGNORE,
+ .type = HDA_FIXUP_FUNC,
+ .v.func = alc_fixup_sku_ignore,
},
[ALC662_FIXUP_HP_RP5800] = {
- .type = ALC_FIXUP_PINS,
- .v.pins = (const struct alc_pincfg[]) {
+ .type = HDA_FIXUP_PINS,
+ .v.pins = (const struct hda_pintbl[]) {
{ 0x14, 0x0221201f }, /* HP out */
{ }
},
@@ -5972,8 +5668,8 @@ static const struct alc_fixup alc662_fixups[] = {
.chain_id = ALC662_FIXUP_SKU_IGNORE
},
[ALC662_FIXUP_ASUS_MODE1] = {
- .type = ALC_FIXUP_PINS,
- .v.pins = (const struct alc_pincfg[]) {
+ .type = HDA_FIXUP_PINS,
+ .v.pins = (const struct hda_pintbl[]) {
{ 0x14, 0x99130110 }, /* speaker */
{ 0x18, 0x01a19c20 }, /* mic */
{ 0x19, 0x99a3092f }, /* int-mic */
@@ -5984,8 +5680,8 @@ static const struct alc_fixup alc662_fixups[] = {
.chain_id = ALC662_FIXUP_SKU_IGNORE
},
[ALC662_FIXUP_ASUS_MODE2] = {
- .type = ALC_FIXUP_PINS,
- .v.pins = (const struct alc_pincfg[]) {
+ .type = HDA_FIXUP_PINS,
+ .v.pins = (const struct hda_pintbl[]) {
{ 0x14, 0x99130110 }, /* speaker */
{ 0x18, 0x01a19820 }, /* mic */
{ 0x19, 0x99a3092f }, /* int-mic */
@@ -5996,8 +5692,8 @@ static const struct alc_fixup alc662_fixups[] = {
.chain_id = ALC662_FIXUP_SKU_IGNORE
},
[ALC662_FIXUP_ASUS_MODE3] = {
- .type = ALC_FIXUP_PINS,
- .v.pins = (const struct alc_pincfg[]) {
+ .type = HDA_FIXUP_PINS,
+ .v.pins = (const struct hda_pintbl[]) {
{ 0x14, 0x99130110 }, /* speaker */
{ 0x15, 0x0121441f }, /* HP */
{ 0x18, 0x01a19840 }, /* mic */
@@ -6009,8 +5705,8 @@ static const struct alc_fixup alc662_fixups[] = {
.chain_id = ALC662_FIXUP_SKU_IGNORE
},
[ALC662_FIXUP_ASUS_MODE4] = {
- .type = ALC_FIXUP_PINS,
- .v.pins = (const struct alc_pincfg[]) {
+ .type = HDA_FIXUP_PINS,
+ .v.pins = (const struct hda_pintbl[]) {
{ 0x14, 0x99130110 }, /* speaker */
{ 0x16, 0x99130111 }, /* speaker */
{ 0x18, 0x01a19840 }, /* mic */
@@ -6022,8 +5718,8 @@ static const struct alc_fixup alc662_fixups[] = {
.chain_id = ALC662_FIXUP_SKU_IGNORE
},
[ALC662_FIXUP_ASUS_MODE5] = {
- .type = ALC_FIXUP_PINS,
- .v.pins = (const struct alc_pincfg[]) {
+ .type = HDA_FIXUP_PINS,
+ .v.pins = (const struct hda_pintbl[]) {
{ 0x14, 0x99130110 }, /* speaker */
{ 0x15, 0x0121441f }, /* HP */
{ 0x16, 0x99130111 }, /* speaker */
@@ -6035,8 +5731,8 @@ static const struct alc_fixup alc662_fixups[] = {
.chain_id = ALC662_FIXUP_SKU_IGNORE
},
[ALC662_FIXUP_ASUS_MODE6] = {
- .type = ALC_FIXUP_PINS,
- .v.pins = (const struct alc_pincfg[]) {
+ .type = HDA_FIXUP_PINS,
+ .v.pins = (const struct hda_pintbl[]) {
{ 0x14, 0x99130110 }, /* speaker */
{ 0x15, 0x01211420 }, /* HP2 */
{ 0x18, 0x01a19840 }, /* mic */
@@ -6048,8 +5744,8 @@ static const struct alc_fixup alc662_fixups[] = {
.chain_id = ALC662_FIXUP_SKU_IGNORE
},
[ALC662_FIXUP_ASUS_MODE7] = {
- .type = ALC_FIXUP_PINS,
- .v.pins = (const struct alc_pincfg[]) {
+ .type = HDA_FIXUP_PINS,
+ .v.pins = (const struct hda_pintbl[]) {
{ 0x14, 0x99130110 }, /* speaker */
{ 0x17, 0x99130111 }, /* speaker */
{ 0x18, 0x01a19840 }, /* mic */
@@ -6062,8 +5758,8 @@ static const struct alc_fixup alc662_fixups[] = {
.chain_id = ALC662_FIXUP_SKU_IGNORE
},
[ALC662_FIXUP_ASUS_MODE8] = {
- .type = ALC_FIXUP_PINS,
- .v.pins = (const struct alc_pincfg[]) {
+ .type = HDA_FIXUP_PINS,
+ .v.pins = (const struct hda_pintbl[]) {
{ 0x14, 0x99130110 }, /* speaker */
{ 0x12, 0x99a30970 }, /* int-mic */
{ 0x15, 0x01214020 }, /* HP */
@@ -6075,18 +5771,110 @@ static const struct alc_fixup alc662_fixups[] = {
.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 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(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
@@ -6094,7 +5882,7 @@ static const struct snd_pci_quirk alc662_fixup_tbl[] = {
* 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),
@@ -6149,7 +5937,7 @@ static const struct snd_pci_quirk alc662_fixup_tbl[] = {
{}
};
-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"},
@@ -6159,64 +5947,146 @@ static const struct alc_model_fixup alc662_fixup_models[] = {
{.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 = 0;
-
- spec = kzalloc(sizeof(*spec), GFP_KERNEL);
- if (!spec)
- return -ENOMEM;
+ int err;
- codec->spec = spec;
+ err = alc_alloc_spec(codec, 0x0b);
+ if (err < 0)
+ return err;
- spec->mixer_nid = 0x0b;
+ spec = codec->spec;
/* handle multiple HPs as is */
spec->parse_flags = HDA_PINCFG_NO_HP_FIXUP;
- alc_auto_parse_customize_define(codec);
-
alc_fix_pll_init(codec, 0x20, 0x04, 15);
- err = alc_codec_rename_from_preset(codec);
- if (err < 0)
- goto error;
+ spec->init_hook = alc662_fill_coef;
+ alc662_fill_coef(codec);
+
+ 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);
+
+ alc_auto_parse_customize_define(codec);
+
+ if (has_cdefine_beep(codec))
+ spec->gen.beep_nid = 0x01;
if ((alc_get_coef0(codec) & (1 << 14)) &&
- codec->bus->pci->subsystem_vendor == 0x1025 &&
+ codec->bus->pci && codec->bus->pci->subsystem_vendor == 0x1025 &&
spec->cdefine.platform_type == 1) {
- if (alc_codec_rename(codec, "ALC272X") < 0)
+ err = alc_codec_rename(codec, "ALC272X");
+ if (err < 0)
goto error;
}
- 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)
goto error;
- if (!spec->no_analog && !spec->adc_nids) {
- alc_auto_fill_adc_caps(codec);
- alc_rebuild_imux_for_auto_mic(codec);
- alc_remove_invalid_adc_nids(codec);
- }
-
- if (!spec->no_analog && !spec->cap_mixer)
- set_capture_mixer(codec);
-
- if (!spec->no_analog && has_cdefine_beep(codec)) {
- err = snd_hda_attach_beep_device(codec, 0x1);
- if (err < 0)
- goto error;
+ if (!spec->gen.no_analog && spec->gen.beep_nid) {
switch (codec->vendor_id) {
case 0x10ec0662:
set_beep_amp(spec, 0x0b, 0x05, HDA_INPUT);
@@ -6224,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:
@@ -6232,16 +6103,10 @@ static int patch_alc662(struct hda_codec *codec)
}
}
- alc_apply_fixup(codec, ALC_FIXUP_ACT_PROBE);
-
codec->patch_ops = alc_patch_ops;
- spec->init_hook = alc_auto_init_std;
spec->shutup = alc_eapd_shutup;
-#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;
@@ -6263,16 +6128,12 @@ static int alc680_parse_auto_config(struct hda_codec *codec)
*/
static int patch_alc680(struct hda_codec *codec)
{
- struct alc_spec *spec;
int err;
- spec = kzalloc(sizeof(*spec), GFP_KERNEL);
- if (spec == NULL)
- return -ENOMEM;
-
- codec->spec = spec;
-
/* ALC680 has no aa-loopback mixer */
+ err = alc_alloc_spec(codec, 0);
+ if (err < 0)
+ return err;
/* automatic parse from the BIOS config */
err = alc680_parse_auto_config(codec);
@@ -6281,11 +6142,7 @@ static int patch_alc680(struct hda_codec *codec)
return err;
}
- if (!spec->no_analog && !spec->cap_mixer)
- set_capture_mixer(codec);
-
codec->patch_ops = alc_patch_ops;
- spec->init_hook = alc_auto_init_std;
return 0;
}
@@ -6295,6 +6152,10 @@ static int patch_alc680(struct hda_codec *codec)
*/
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 },
@@ -6304,6 +6165,16 @@ static const struct hda_codec_preset snd_hda_preset_realtek[] = {
{ .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 },
@@ -6317,8 +6188,12 @@ static const struct hda_codec_preset snd_hda_preset_realtek[] = {
.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 },
@@ -6334,6 +6209,7 @@ static const struct hda_codec_preset snd_hda_preset_realtek[] = {
{ .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 6679a5095e5..3208ad69583 100644
--- a/sound/pci/hda/patch_si3054.c
+++ b/sound/pci/hda/patch_si3054.c
@@ -236,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; */
}
@@ -247,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));
}
diff --git a/sound/pci/hda/patch_sigmatel.c b/sound/pci/hda/patch_sigmatel.c
index 9dbb5735d77..3744ea4e843 100644
--- a/sound/pci/hda/patch_sigmatel.c
+++ b/sound/pci/hda/patch_sigmatel.c
@@ -31,25 +31,21 @@
#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,
@@ -65,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,
@@ -79,7 +75,6 @@ enum {
};
enum {
- STAC_92HD73XX_AUTO,
STAC_92HD73XX_NO_JD, /* no jack-detection */
STAC_92HD73XX_REF,
STAC_92HD73XX_INTEL,
@@ -88,22 +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_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,
@@ -112,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,
@@ -130,7 +140,6 @@ enum {
};
enum {
- STAC_922X_AUTO,
STAC_D945_REF,
STAC_D945GTP3,
STAC_D945GTP5,
@@ -139,66 +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_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;
@@ -210,658 +199,838 @@ struct sigmatel_spec {
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 */
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;
const hda_nid_t *pwr_nids;
- const hda_nid_t *dac_list;
-
- /* 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 */
- const hda_nid_t *adc_nids;
- unsigned int num_adcs;
- const hda_nid_t *mux_nids;
- unsigned int num_muxes;
- const hda_nid_t *dmic_nids;
- unsigned int num_dmics;
- const hda_nid_t *dmux_nids;
- unsigned int num_dmuxes;
- const hda_nid_t *smux_nids;
- unsigned int num_smuxes;
- unsigned int num_analog_muxes;
-
- const unsigned long *capvols; /* amp-volume attr: HDA_COMPOSE_AMP_VAL() */
- const 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;
+ 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 */
- const hda_nid_t *pin_nids;
- unsigned int num_pins;
-
- /* codec specific stuff */
- const struct hda_verb *init;
- const 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 const hda_nid_t stac9200_adc_nids[1] = {
- 0x03,
+ /* SPDIF-out mux */
+ const char * const *spdif_labels;
+ struct hda_input_mux spdif_mux;
+ unsigned int cur_smux[2];
};
-static const hda_nid_t stac9200_mux_nids[1] = {
- 0x0c,
-};
-
-static const 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 const hda_nid_t stac92hd73xx_pwr_nids[8] = {
0x0a, 0x0b, 0x0c, 0xd, 0x0e,
0x0f, 0x10, 0x11
};
-static const hda_nid_t stac92hd73xx_slave_dig_outs[2] = {
- 0x26, 0,
-};
-
-static const hda_nid_t stac92hd73xx_adc_nids[2] = {
- 0x1a, 0x1b
-};
-
-#define STAC92HD73XX_NUM_DMICS 2
-static const 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 const hda_nid_t stac92hd73xx_mux_nids[2] = {
- 0x20, 0x21,
+static const hda_nid_t stac92hd71bxx_pwr_nids[3] = {
+ 0x0a, 0x0d, 0x0f
};
-static const hda_nid_t stac92hd73xx_dmux_nids[2] = {
- 0x20, 0x21,
-};
-static const 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 const 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 const hda_nid_t stac92hd83xxx_pwr_nids[7] = {
- 0x0a, 0x0b, 0x0c, 0xd, 0x0e,
- 0x0f, 0x10
-};
+ for (i = 0; i < spec->gen.num_all_adcs; i++) {
+ if (spec->gen.all_adcs[i] == hinfo->nid) {
+ idx = i;
+ break;
+ }
+ }
-static const 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 const hda_nid_t stac92hd83xxx_dmic_nids[] = {
- 0x11, 0x20,
-};
+/*
+ * Early 2006 Intel Macintoshes with STAC9220X5 codecs seem to have a
+ * funky external mute control using GPIO pins.
+ */
-static const hda_nid_t stac92hd71bxx_pwr_nids[3] = {
- 0x0a, 0x0d, 0x0f
-};
+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 const hda_nid_t stac92hd71bxx_adc_nids[2] = {
- 0x12, 0x13,
-};
+ codec_dbg(codec, "%s msk %x dir %x gpio %x\n", __func__, mask, dir_mask, data);
-static const hda_nid_t stac92hd71bxx_mux_nids[2] = {
- 0x1a, 0x1b
-};
+ gpiostate = snd_hda_codec_read(codec, codec->afg, 0,
+ AC_VERB_GET_GPIO_DATA, 0);
+ gpiostate = (gpiostate & ~dir_mask) | (data & dir_mask);
-static const hda_nid_t stac92hd71bxx_dmux_nids[2] = {
- 0x1c, 0x1d,
-};
+ gpiomask = snd_hda_codec_read(codec, codec->afg, 0,
+ AC_VERB_GET_GPIO_MASK, 0);
+ gpiomask |= mask;
-static const hda_nid_t stac92hd71bxx_smux_nids[2] = {
- 0x24, 0x25,
-};
+ gpiodir = snd_hda_codec_read(codec, codec->afg, 0,
+ AC_VERB_GET_GPIO_DIRECTION, 0);
+ gpiodir |= dir_mask;
-#define STAC92HD71BXX_NUM_DMICS 2
-static const hda_nid_t stac92hd71bxx_dmic_nids[STAC92HD71BXX_NUM_DMICS + 1] = {
- 0x18, 0x19, 0
-};
+ /* Configure GPIOx as CMOS */
+ snd_hda_codec_write(codec, codec->afg, 0, 0x7e7, 0);
-static const hda_nid_t stac92hd71bxx_dmic_5port_nids[STAC92HD71BXX_NUM_DMICS] = {
- 0x18, 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 const hda_nid_t stac92hd71bxx_slave_dig_outs[2] = {
- 0x22, 0
-};
+ msleep(1);
-#define STAC92HD71BXX_NUM_CAPS 2
-static const 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 const 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 const hda_nid_t stac925x_mux_nids[1] = {
- 0x0f,
-};
+ if (!kcontrol || !ucontrol)
+ return;
-static const 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 const 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 const 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 const unsigned long stac925x_capvols[] = {
- HDA_COMPOSE_AMP_VAL(0x09, 3, 0, HDA_OUTPUT),
-};
-static const unsigned long stac925x_capsws[] = {
- HDA_COMPOSE_AMP_VAL(0x14, 3, 0, HDA_OUTPUT),
-};
+ if (pinctl < 0)
+ return pinctl;
-static const hda_nid_t stac922x_adc_nids[2] = {
- 0x06, 0x07,
-};
+ pinctl &= 0xff;
+ pinctl &= ~AC_PINCTL_VREFEN;
+ pinctl |= (new_vref & AC_PINCTL_VREFEN);
-static const 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 const 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 const 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 const 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 const hda_nid_t stac927x_mux_nids[3] = {
- 0x15, 0x16, 0x17
-};
+ if (!spec->gpio_led)
+ return;
-static const hda_nid_t stac927x_smux_nids[1] = {
- 0x21,
-};
+ /* LED state is inverted on these systems */
+ if (spec->gpio_led_polarity)
+ muted = !muted;
-static const 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 const 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 const 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 const 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 const 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 const 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 const 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 const 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 const hda_nid_t stac9205_smux_nids[1] = {
- 0x21,
-};
+ idx = 1 << idx;
-#define STAC9205_NUM_DMICS 2
-static const 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 const unsigned long stac9205_capvols[] = {
- HDA_COMPOSE_AMP_VAL(0x1b, 3, 0, HDA_INPUT),
- HDA_COMPOSE_AMP_VAL(0x1c, 3, 0, HDA_INPUT),
-};
-static const 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 const 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 const hda_nid_t stac925x_pin_nids[8] = {
- 0x07, 0x08, 0x0a, 0x0b,
- 0x0c, 0x0d, 0x10, 0x11,
-};
+ if (!spec->num_pwrs)
+ return;
-static const 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 const 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 const hda_nid_t stac92hd71bxx_pin_nids_4port[STAC92HD71BXX_NUM_PINS] = {
- 0x0a, 0x0b, 0x0c, 0x0d, 0x00,
- 0x00, 0x14, 0x18, 0x19, 0x1e,
- 0x1f, 0x20, 0x27
-};
-static const 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 const 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 const 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 inline bool get_int_hint(struct hda_codec *codec, const char *key,
+ int *valp)
+{
+ return !snd_hda_get_int_hint(codec, key, valp);
}
-static int stac92xx_smux_enum_info(struct snd_kcontrol *kcontrol,
- struct snd_ctl_elem_info *uinfo)
+/* 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;
}
-#ifdef CONFIG_SND_HDA_POWER_SAVE
-static int stac_vrefout_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, pinctl;
+ if (codec->vendor_id != 0x111d7605 &&
+ codec->vendor_id != 0x111d76d1)
+ return false;
- snd_printdd("%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);
+ switch (codec->subsystem_id) {
+ case 0x103c1618:
+ case 0x103c1619:
+ case 0x103c161a:
+ case 0x103c161b:
+ case 0x103c161c:
+ case 0x103c161d:
+ case 0x103c161e:
+ case 0x103c161f:
- if (pinctl < 0)
- return pinctl;
+ case 0x103c162a:
+ case 0x103c162b:
- pinctl &= 0xff;
- pinctl &= ~AC_PINCTL_VREFEN;
- pinctl |= (new_vref & AC_PINCTL_VREFEN);
+ case 0x103c1630:
+ case 0x103c1631:
- error = snd_hda_codec_write_cache(codec, nid, 0,
- AC_VERB_SET_PIN_WIDGET_CONTROL, pinctl);
- if (error < 0)
- return error;
+ case 0x103c1633:
+ case 0x103c1634:
+ case 0x103c1635:
- return 1;
+ case 0x103c3587:
+ case 0x103c3588:
+ case 0x103c3589:
+ case 0x103c358a:
+
+ case 0x103c3667:
+ case 0x103c3668:
+ case 0x103c3669:
+
+ return true;
+ }
+ return false;
}
-#endif
-static unsigned int stac92xx_vref_set(struct hda_codec *codec,
- hda_nid_t nid, unsigned int new_vref)
+static bool hp_blike_system(u32 subsystem_id)
{
- int error;
- unsigned int pincfg;
- pincfg = snd_hda_codec_read(codec, nid, 0,
- AC_VERB_GET_PIN_WIDGET_CONTROL, 0);
+ 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;
+}
- pincfg &= 0xff;
- pincfg &= ~(AC_PINCTL_VREFEN | AC_PINCTL_IN_EN | AC_PINCTL_OUT_EN);
- pincfg |= new_vref;
+static void set_hp_led_gpio(struct hda_codec *codec)
+{
+ struct sigmatel_spec *spec = codec->spec;
+ unsigned int gpio;
- if (new_vref == AC_PINCTL_VREF_HIZ)
- pincfg |= AC_PINCTL_OUT_EN;
- else
- pincfg |= AC_PINCTL_IN_EN;
+ if (spec->gpio_led)
+ return;
- error = snd_hda_codec_write_cache(codec, nid, 0,
- AC_VERB_SET_PIN_WIDGET_CONTROL, pincfg);
- if (error < 0)
- return error;
+ 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
- return 1;
+ spec->gpio_led = 0x01; /* GPIO 0 */
}
-static unsigned int stac92xx_vref_get(struct hda_codec *codec, hda_nid_t nid)
+/*
+ * 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)
{
- unsigned int vref;
- vref = snd_hda_codec_read(codec, nid, 0,
- AC_VERB_GET_PIN_WIDGET_CONTROL, 0);
- vref &= AC_PINCTL_VREFEN;
- return vref;
+ struct sigmatel_spec *spec = codec->spec;
+ 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;
+ }
+ }
+
+ /*
+ * 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_info(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *uinfo)
+/* 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;
- return snd_hda_input_mux_info(spec->input_mux, uinfo);
+ 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 {
+ nid_pin = spec->gen.autocfg.speaker_pins;
+ nids = spec->gen.autocfg.speaker_outs;
+ }
+
+ 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_mux_enum_get(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
+/*
+ * 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;
- unsigned int adc_idx = snd_ctl_get_ioffidx(kcontrol, &ucontrol->id);
+ 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);
- ucontrol->value.enumerated.item[0] = spec->cur_mux[adc_idx];
+ /* 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_mux_enum_put(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;
- 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);
- }
- } 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);
- }
- spec->cur_mux[adc_idx] = idx;
- return 1;
+ ucontrol->value.integer.value[0] = codec->beep->enabled;
+ return 0;
}
-static int stac92xx_mono_mux_enum_info(struct snd_kcontrol *kcontrol,
- struct snd_ctl_elem_info *uinfo)
+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);
- struct sigmatel_spec *spec = codec->spec;
- return snd_hda_input_mux_info(spec->mono_mux, uinfo);
+ return snd_hda_enable_beep_device(codec, ucontrol->value.integer.value[0]);
}
-static int stac92xx_mono_mux_enum_get(struct snd_kcontrol *kcontrol,
- struct snd_ctl_elem_value *ucontrol)
+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 hda_codec *codec = snd_kcontrol_chip(kcontrol);
struct sigmatel_spec *spec = codec->spec;
- ucontrol->value.enumerated.item[0] = spec->cur_mmux;
+ if (!snd_hda_gen_add_kctl(&spec->gen, NULL, &stac_dig_beep_ctrl))
+ return -ENOMEM;
return 0;
}
+#endif
-static int stac92xx_mono_mux_enum_put(struct snd_kcontrol *kcontrol,
- struct snd_ctl_elem_value *ucontrol)
+/*
+ * SPDIF-out mux controls
+ */
+
+static int stac_smux_enum_info(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_info *uinfo)
{
struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
struct sigmatel_spec *spec = codec->spec;
-
- return snd_hda_input_mux_put(codec, spec->mono_mux, ucontrol,
- spec->mono_nid, &spec->cur_mmux);
+ return snd_hda_input_mux_info(&spec->spdif_mux, uinfo);
}
-#define stac92xx_aloopback_info snd_ctl_boolean_mono_info
-
-static int stac92xx_aloopback_get(struct snd_kcontrol *kcontrol,
- struct snd_ctl_elem_value *ucontrol)
+static int stac_smux_enum_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.integer.value[0] = !!(spec->aloopback &
- (spec->aloopback_mask << idx));
+ 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 const struct hda_verb stac9200_core_init[] = {
/* set dac0mux for dac converter */
{ 0x07, AC_VERB_SET_CONNECT_SEL, 0x00},
@@ -894,6 +1063,13 @@ static const struct hda_verb stac92hd83xxx_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},
@@ -917,17 +1093,15 @@ static const struct hda_verb stac925x_core_init[] = {
};
static const struct hda_verb stac922x_core_init[] = {
- /* set master volume and direct control */
+ /* set master volume and direct control */
{ 0x16, AC_VERB_SET_VOLUME_KNOB_CONTROL, 0xff},
{}
};
static const struct hda_verb d965_core_init[] = {
- /* set master volume and direct control */
- { 0x24, AC_VERB_SET_VOLUME_KNOB_CONTROL, 0xff},
/* 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},
{}
};
@@ -943,7 +1117,7 @@ static const struct hda_verb dell_3st_core_init[] = {
};
static const struct hda_verb stac927x_core_init[] = {
- /* set master volume and direct control */
+ /* 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},
@@ -959,263 +1133,103 @@ static const struct hda_verb stac927x_volknob_core_init[] = {
};
static const struct hda_verb stac9205_core_init[] = {
- /* set master volume and direct control */
+ /* 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 const 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 const 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 const 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 const 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 const struct snd_kcontrol_new stac92hd71bxx_loopback[] = {
- STAC_ANALOG_LOOPBACK(0xFA0, 0x7A0, 2)
-};
-
-static const 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 const 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 const 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_build_controls(struct hda_codec *codec)
-{
- struct sigmatel_spec *spec = codec->spec;
- 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,
- 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 */
-
- err = snd_hda_jack_add_kctls(codec, &spec->autocfg);
- if (err < 0)
- return err;
-
- return 0;
-}
-
-static const unsigned int ref9200_pin_configs[8] = {
- 0x01c47010, 0x01447010, 0x0221401f, 0x01114010,
- 0x02a19020, 0x01a19021, 0x90100140, 0x01813122,
-};
-
-static const unsigned int gateway9200_m4_pin_configs[8] = {
- 0x400000fe, 0x404500f4, 0x400100f0, 0x90110010,
- 0x400100f1, 0x02a1902e, 0x500000f2, 0x500000f3,
-};
-static const 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 const 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 const 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
@@ -1224,9 +1238,16 @@ static const unsigned int dell9200_d22_pin_configs[8] = {
102801DA
102801E3
*/
-static const 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 },
+ {}
};
@@ -1235,9 +1256,16 @@ static const unsigned int dell9200_d23_pin_configs[8] = {
102801B5 (Dell Inspiron 630m)
102801D8 (Dell Inspiron 640m)
*/
-static const 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 },
+ {}
};
/*
@@ -1248,9 +1276,16 @@ static const unsigned int dell9200_m21_pin_configs[8] = {
102801D4
102801D6
*/
-static const 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 },
+ {}
};
/*
@@ -1258,9 +1293,16 @@ static const unsigned int dell9200_m22_pin_configs[8] = {
102801CE (Dell XPS M1710)
102801CF (Dell Precision M90)
*/
-static const 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 },
+ {}
};
/*
@@ -1270,9 +1312,16 @@ static const unsigned int dell9200_m23_pin_configs[8] = {
102801CB (Dell Latitude 120L)
102801D3
*/
-static const 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 },
+ {}
};
/*
@@ -1281,9 +1330,16 @@ static const unsigned int dell9200_m24_pin_configs[8] = {
102801EE
102801EF
*/
-static const 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 },
+ {}
};
/*
@@ -1291,64 +1347,159 @@ static const unsigned int dell9200_m25_pin_configs[8] = {
102801F5 (Dell Inspiron 1501)
102801F6
*/
-static const 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 const unsigned int dell9200_m27_pin_configs[8] = {
- 0x40c003fa, 0x01441340, 0x0421121f, 0x90170310,
- 0x90170310, 0x04a11020, 0x90170310, 0x40f003fc,
-};
-
-static const unsigned int oqo9200_pin_configs[8] = {
- 0x40c000f0, 0x404000f1, 0x0221121f, 0x02211210,
- 0x90170111, 0x90a70120, 0x400000f2, 0x400000f3,
-};
-
-
-static const 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 const 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),
@@ -1424,70 +1575,159 @@ static const struct snd_pci_quirk stac9200_cfg_tbl[] = {
{} /* terminator */
};
-static const 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 const 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 const 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 const 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 const 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 const 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 const 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 const 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 const 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 const 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),
@@ -1501,67 +1741,212 @@ static const struct snd_pci_quirk stac925x_codec_id_cfg_tbl[] = {
{} /* terminator */
};
-static const 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 const 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 const 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 const 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 const 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 const 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 const 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),
@@ -1599,69 +1984,710 @@ static const struct snd_pci_quirk stac92hd73xx_cfg_tbl[] = {
"Dell Studio XPS 1645", STAC_DELL_M6_DMIC),
SND_PCI_QUIRK(PCI_VENDOR_ID_DELL, 0x0413,
"Dell Studio 1558", STAC_DELL_M6_DMIC),
- {} /* terminator */
-};
-
-static const struct snd_pci_quirk stac92hd73xx_codec_id_cfg_tbl[] = {
+ /* 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 const 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 unsigned int dell_s14_pin_configs[10] = {
- 0x0221403f, 0x0221101f, 0x02a19020, 0x90170110,
- 0x40f000f0, 0x40f000f0, 0x40f000f0, 0x90a60160,
- 0x40f000f0, 0x40f000f0,
+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 unsigned int dell_vostro_3500_pin_configs[10] = {
- 0x02a11020, 0x0221101f, 0x400000f0, 0x90170110,
- 0x400000f1, 0x400000f2, 0x400000f3, 0x90a60160,
- 0x400000f4, 0x400000f5,
+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 unsigned int hp_dv7_4000_pin_configs[10] = {
- 0x03a12050, 0x0321201f, 0x40f000f0, 0x90170110,
- 0x40f000f0, 0x40f000f0, 0x90170110, 0xd5a30140,
- 0x40f000f0, 0x40f000f0,
+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 unsigned int hp_cNB11_intquad_pin_configs[10] = {
- 0x40f000f0, 0x0221101f, 0x02a11020, 0x92170110,
- 0x40f000f0, 0x92170110, 0x40f000f0, 0xd5a30130,
- 0x40f000f0, 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 const 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_DELL_VOSTRO_3500] = dell_vostro_3500_pin_configs,
- [STAC_92HD83XXX_HP_cNB11_INTQUAD] = hp_cNB11_intquad_pin_configs,
- [STAC_HP_DV7_4000] = hp_dv7_4000_pin_configs,
+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 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_DELL_VOSTRO_3500] = "dell-vostro-3500",
- [STAC_92HD83XXX_HP_cNB11_INTQUAD] = "hp_cNB11_intquad",
- [STAC_HP_DV7_4000] = "hp-dv7-4000",
+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 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 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 const 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),
@@ -1669,6 +2695,24 @@ static const 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(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,
@@ -1678,11 +2722,117 @@ static const struct snd_pci_quirk stac92hd83xxx_cfg_tbl[] = {
SND_PCI_QUIRK(PCI_VENDOR_ID_HP, 0x1658,
"HP", STAC_92HD83XXX_HP_cNB11_INTQUAD),
SND_PCI_QUIRK(PCI_VENDOR_ID_HP, 0x1659,
- "HP", STAC_92HD83XXX_HP_cNB11_INTQUAD),
+ "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,
@@ -1711,70 +2861,303 @@ static const struct snd_pci_quirk stac92hd83xxx_cfg_tbl[] = {
"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 const 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 const 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 const 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 const 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 const 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 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 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 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_cfg_tbl[] = {
+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,
@@ -1797,6 +3180,7 @@ static const 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,
@@ -1824,10 +3208,18 @@ static const struct snd_pci_quirk stac92hd71bxx_cfg_tbl[] = {
{} /* terminator */
};
-static const 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 },
+ {}
};
/*
@@ -1838,10 +3230,18 @@ static const unsigned int ref922x_pin_configs[10] = {
102801D1
102801D2
*/
-static const 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 },
+ {}
};
/*
@@ -1849,130 +3249,312 @@ static const unsigned int dell_922x_d81_pin_configs[10] = {
102801AC
102801D0
*/
-static const 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 const 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 const 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 unsigned int d945gtp3_pin_configs[10] = {
- 0x0221401f, 0x01a19022, 0x01813021, 0x01014010,
- 0x40000100, 0x40000100, 0x40000100, 0x40000100,
- 0x02a19120, 0x40000100,
+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 const unsigned int d945gtp5_pin_configs[10] = {
- 0x0221401f, 0x01011012, 0x01813024, 0x01014010,
- 0x01a19021, 0x01016011, 0x01452130, 0x40000100,
- 0x02a19320, 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 const 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_v1_pin_configs[] = {
+ { 0x0a, 0x0121e21f },
+ { 0x0b, 0x400000ff },
+ { 0x0c, 0x9017e110 },
+ { 0x0d, 0x400000fd },
+ { 0x0e, 0x400000fe },
+ { 0x0f, 0x0181e020 },
+ { 0x10, 0x1145e030 },
+ { 0x11, 0x11c5e240 },
+ { 0x15, 0x400000fc },
+ { 0x1b, 0x400000fb },
+ {}
};
-static const 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_v2_pin_configs[] = {
+ { 0x0a, 0x0121e21f },
+ { 0x0b, 0x90a7012e },
+ { 0x0c, 0x9017e110 },
+ { 0x0d, 0x400000fd },
+ { 0x0e, 0x400000fe },
+ { 0x0f, 0x0181e020 },
+ { 0x10, 0x1145e230 },
+ { 0x11, 0x500000fa },
+ { 0x15, 0x400000fc },
+ { 0x1b, 0x400000fb },
+ {}
};
-static const 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_v3_pin_configs[] = {
+ { 0x0a, 0x0121e21f },
+ { 0x0b, 0x90a7012e },
+ { 0x0c, 0x9017e110 },
+ { 0x0d, 0x400000fd },
+ { 0x0e, 0x400000fe },
+ { 0x0f, 0x0181e020 },
+ { 0x10, 0x1145e230 },
+ { 0x11, 0x11c5e240 },
+ { 0x15, 0x400000fc },
+ { 0x1b, 0x400000fb },
+ {}
};
-static const 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_v4_pin_configs[] = {
+ { 0x0a, 0x0321e21f },
+ { 0x0b, 0x03a1e02e },
+ { 0x0c, 0x9017e110 },
+ { 0x0d, 0x9017e11f },
+ { 0x0e, 0x400000fe },
+ { 0x0f, 0x0381e020 },
+ { 0x10, 0x1345e230 },
+ { 0x11, 0x13c5e240 },
+ { 0x15, 0x400000fc },
+ { 0x1b, 0x400000fb },
+ {}
};
-static const unsigned int intel_mac_v5_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 const unsigned int ecs202_pin_configs[10] = {
- 0x0221401f, 0x02a19020, 0x01a19020, 0x01114010,
- 0x408000f0, 0x01813022, 0x074510a0, 0x40c400f1,
- 0x9037012e, 0x40e000f2,
+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 const 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",
+/* 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 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 const 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),
@@ -2035,9 +3617,10 @@ static const 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),
@@ -2063,65 +3646,238 @@ static const struct snd_pci_quirk stac922x_cfg_tbl[] = {
{} /* terminator */
};
-static const 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 const 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 const 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 const 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 const 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 const 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 const 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),
@@ -2143,12 +3899,12 @@ static const 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),
@@ -2159,10 +3915,20 @@ static const struct snd_pci_quirk stac927x_cfg_tbl[] = {
{} /* terminator */
};
-static const 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 },
+ {}
};
/*
@@ -2176,10 +3942,20 @@ static const unsigned int ref9205_pin_configs[12] = {
10280228 (Dell Vostro 1500)
10280229 (Dell Vostro 1700)
*/
-static const 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 },
+ {}
};
/*
@@ -2192,36 +3968,126 @@ static const unsigned int dell_9205_m42_pin_configs[12] = {
10280200
10280201
*/
-static const 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 const 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 const 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 const 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),
@@ -2268,1614 +4134,92 @@ static const struct snd_pci_quirk stac9205_cfg_tbl[] = {
{} /* terminator */
};
-static void stac92xx_set_config_regs(struct hda_codec *codec,
- const 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 const 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 const struct hda_fixup stac92hd95_fixups[] = {
+ [STAC_92HD95_HP_LED] = {
+ .type = HDA_FIXUP_FUNC,
+ .v.func = stac92hd95_fixup_hp_led,
},
-};
-
-static const struct hda_pcm_stream stac92xx_pcm_digital_capture = {
- .substreams = 1,
- .channels_min = 2,
- .channels_max = 2,
- /* NID is set in stac92xx_build_pcms */
-};
-
-static const 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
+ [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 const 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
- },
-};
-
-static const 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 const char * const 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)
-{
- 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 const 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,
- const 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,
- const 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 const 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) {
- snd_hda_get_pin_label(codec, nid, &spec->autocfg,
- name, sizeof(name), NULL);
- 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;
- struct auto_pin_cfg *cfg = &spec->autocfg;
- int j, conn_len;
- hda_nid_t conn[HDA_MAX_CONNECTIONS], fallback_dac;
- 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,
- unless we're assigning a secondary headphone */
- fallback_dac = spec->multiout.dac_nids[0];
- if (spec->multiout.hp_nid) {
- for (j = 0; j < cfg->hp_outs; j++)
- if (cfg->hp_pins[j] == nid) {
- fallback_dac = spec->multiout.hp_nid;
- break;
- }
- }
-
- if (conn_len > 1) {
- for (j = 0; j < conn_len; j++) {
- if (conn[j] == fallback_dac) {
- 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)
+static int stac_parse_auto_config(struct hda_codec *codec)
{
struct sigmatel_spec *spec = codec->spec;
- char name[32];
int err;
+ int flags = 0;
- 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;
- }
+ if (spec->headset_jack)
+ flags |= HDA_PINCFG_HEADSET_MIC;
- 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));
+ err = snd_hda_parse_pin_defcfg(codec, &spec->gen.autocfg, NULL, flags);
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)
+ /* add hooks */
+ spec->gen.pcm_playback_hook = stac_playback_pcm_hook;
+ spec->gen.pcm_capture_hook = stac_capture_pcm_hook;
-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 {
- snd_BUG_ON(spec->multiout.dac_nids != spec->dac_nids);
- spec->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;
-}
+ 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;
-/* 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) {
- if (is_jack_detectable(codec, pins[i]))
- 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);
+ err = snd_hda_gen_parse_auto_config(codec, &spec->gen.autocfg);
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)
-{
- struct sigmatel_spec *spec = codec->spec;
- int err;
-
- err = create_multi_out_ctls(codec, cfg->hp_outs, cfg->hp_pins,
- spec->hp_dacs, AUTO_PIN_HP_OUT);
- if (err < 0)
- return err;
-
- err = create_multi_out_ctls(codec, cfg->speaker_outs, cfg->speaker_pins,
- spec->speaker_dacs, AUTO_PIN_SPEAKER_OUT);
- 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 const 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;
-}
-
-/* look for NID recursively */
-#define get_connection_index(codec, mux, nid) \
- snd_hda_get_conn_index(codec, mux, nid, 1)
-
-/* 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;
-
- 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));
- 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;
- char label[32];
-
- 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;
-
- snd_hda_get_pin_label(codec, nid, &spec->autocfg,
- label, sizeof(label), NULL);
- 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;
- unsigned int type;
-
- if (!nid)
- return 0;
- cfg = snd_hda_codec_get_pincfg(codec, nid);
- type = get_defcfg_device(cfg);
- switch (snd_hda_get_input_pin_attr(cfg)) {
- case INPUT_PIN_ATTR_INT:
- if (*fixed)
- return 1; /* already occupied */
- if (type != AC_JACK_MIC_IN)
- return 1; /* invalid type */
- *fixed = nid;
- break;
- case INPUT_PIN_ATTR_UNUSED:
- break;
- case INPUT_PIN_ATTR_DOCK:
- if (*dock)
- return 1; /* already occupied */
- if (type != AC_JACK_MIC_IN && type != AC_JACK_LINE_IN)
- return 1; /* invalid type */
- *dock = nid;
- break;
- default:
- if (*ext)
- return 1; /* already occupied */
- if (type != AC_JACK_MIC_IN)
- return 1; /* invalid type */
- *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;
-
- 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 (!is_jack_detectable(codec, ext))
- 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)
-{
- struct sigmatel_spec *spec = codec->spec;
- hda_nid_t dig_out = 0, dig_in = 0;
- 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) {
@@ -3884,7 +4228,7 @@ static int stac92xx_parse_auto_config(struct hda_codec *codec)
/* 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;
}
@@ -3892,564 +4236,70 @@ static int stac92xx_parse_auto_config(struct hda_codec *codec)
}
#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 (spec->gpio_led)
+ spec->gen.vmaster_mute.hook = stac_vmaster_hook;
- 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->aloopback_ctl &&
+ snd_hda_get_bool_hint(codec, "loopback") == 1) {
+ if (!snd_hda_gen_add_kctl(&spec->gen, NULL, spec->aloopback_ctl))
+ return -ENOMEM;
}
- for (i = 0; i < spec->num_caps; i++) {
- err = stac92xx_add_capvol_ctls(codec, spec->capvols[i],
- spec->capsws[i], i);
+ if (spec->have_spdif_mux) {
+ err = stac_create_spdif_mux_ctls(codec);
if (err < 0)
return err;
}
- 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 (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;
-
- /* find digital out and in converters */
- for (i = codec->start_nid; i < codec->start_nid + codec->num_nodes; i++) {
- unsigned int wid_caps = get_wcaps(codec, i);
- if (wid_caps & AC_WCAP_DIGITAL) {
- switch (get_wcaps_type(wid_caps)) {
- case AC_WID_AUD_OUT:
- if (!dig_out)
- dig_out = i;
- break;
- case AC_WID_AUD_IN:
- if (!dig_in)
- dig_in = i;
- break;
- }
- }
- }
- 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];
-
- if (! pin)
- return 0;
-
- if (is_jack_detectable(codec, pin))
- 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;
-
- snd_printdd("%s msk %x dir %x gpio %x\n", __func__, mask, dir_mask, data);
-
- 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 stac_add_event(struct hda_codec *codec, hda_nid_t nid,
- unsigned char type, int data)
-{
- struct hda_jack_tbl *event;
-
- event = snd_hda_jack_tbl_new(codec, nid);
- if (!event)
- return -ENOMEM;
- event->action = type;
- event->private_data = data;
-
- return 0;
-}
-
-/* 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 hda_jack_tbl *event;
-
- if (!is_jack_detectable(codec, nid))
- return 0;
- event = snd_hda_jack_tbl_new(codec, nid);
- if (!event)
- return -ENOMEM;
- if (event->action && event->action != type)
- return 0;
- event->action = type;
- snd_hda_jack_detect_enable(codec, nid, 0);
- return 1;
-}
-
-static int is_nid_out_jack_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 */
- for (i = 0; i < cfg->line_outs; i++)
- if (cfg->line_out_pins[i] == nid)
- return 1; /* nid is a line-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 */
- const 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 void stac_issue_unsol_events(struct hda_codec *codec, int num_pins,
- const hda_nid_t *pins)
-{
- while (num_pins--)
- stac_issue_unsol_event(codec, *pins++);
-}
-
-/* fake event to set up pins */
-static void stac_fake_hp_events(struct hda_codec *codec)
-{
- struct sigmatel_spec *spec = codec->spec;
-
- if (spec->autocfg.hp_outs)
- stac_issue_unsol_events(codec, spec->autocfg.hp_outs,
- spec->autocfg.hp_pins);
- if (spec->autocfg.line_outs &&
- spec->autocfg.line_out_pins[0] != spec->autocfg.hp_pins[0])
- stac_issue_unsol_events(codec, spec->autocfg.line_outs,
- spec->autocfg.line_out_pins);
-}
-
-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 */
- stac_fake_hp_events(codec);
- } 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 */
- /* or when the VREF is used for controlling LED */
- if (!spec->hp_detect ||
- spec->vref_mute_led_nid == nid) {
- stac_toggle_power_map(codec, nid, 1);
- continue;
- }
+ snd_hda_gen_init(codec);
- if (is_nid_out_jack_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;
- }
- if (enable_pin_detect(codec, nid, STAC_PWR_EVENT)) {
- stac_issue_unsol_event(codec, nid);
- 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);
}
- /* none of the above, turn the port OFF */
- stac_toggle_power_map(codec, nid, 0);
}
- snd_hda_jack_report_sync(codec);
-
- /* 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_pins(struct hda_codec *codec)
-{
- unsigned int i, def_conf;
-
- if (codec->bus->shutdown)
- return;
- for (i = 0; i < codec->init_pins.used; i++) {
- struct hda_pincfg *pin = snd_array_elem(&codec->init_pins, i);
- def_conf = snd_hda_codec_get_pincfg(codec, pin->nid);
- if (get_defcfg_connect(def_conf) != AC_JACK_PORT_NONE)
- snd_hda_codec_write(codec, pin->nid, 0,
- AC_VERB_SET_PIN_WIDGET_CONTROL, 0);
- }
-}
-
-static void stac92xx_shutup(struct hda_codec *codec)
+static void stac_shutup(struct hda_codec *codec)
{
struct sigmatel_spec *spec = codec->spec;
- stac92xx_shutup_pins(codec);
+ snd_hda_shutup_pins(codec);
if (spec->eapd_mask)
stac_gpio_set(codec, spec->gpio_mask,
@@ -4457,474 +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);
-
- 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 (1 /*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;
-
- 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,
- const 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 handle_unsol_event(struct hda_codec *codec,
- struct hda_jack_tbl *event)
-{
- struct sigmatel_spec *spec = codec->spec;
- int data;
-
- switch (event->action) {
- case STAC_HP_EVENT:
- case STAC_LO_EVENT:
- stac92xx_hp_detect(codec);
- break;
- case STAC_MIC_EVENT:
- stac92xx_mic_detect(codec);
- break;
- }
-
- switch (event->action) {
- 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);
-
- 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->private_data)));
- break;
- }
-}
-
-static void stac_issue_unsol_event(struct hda_codec *codec, hda_nid_t nid)
-{
- struct hda_jack_tbl *event = snd_hda_jack_tbl_get(codec, nid);
- if (!event)
- return;
- handle_unsol_event(codec, event);
-}
-
-static void stac92xx_unsol_event(struct hda_codec *codec, unsigned int res)
-{
- struct hda_jack_tbl *event;
- int tag;
-
- tag = (res >> 26) & 0x7f;
- event = snd_hda_jack_tbl_get_from_tag(codec, tag);
- if (!event)
- return;
- event->jack_dirty = 1;
- handle_unsol_event(codec, event);
- snd_hda_jack_report_sync(codec);
-}
-
-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_cfg(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_%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_%d",
- &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);
- switch (codec->subsystem_id) {
- case 0x103c148a:
- spec->gpio_led_polarity = 0;
- break;
- default:
- spec->gpio_led_polarity = 1;
- break;
- }
- return 1;
- }
- }
-
- /*
- * 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 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,
@@ -4932,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,
@@ -4973,207 +4357,69 @@ static void stac927x_proc_hook(struct snd_info_buffer *buffer,
#endif
#ifdef CONFIG_PM
-static int stac92xx_resume(struct hda_codec *codec)
-{
- 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 */
- stac_fake_hp_events(codec);
- return 0;
-}
-
-static int stac92xx_suspend(struct hda_codec *codec, pm_message_t state)
-{
- stac92xx_shutup(codec);
- return 0;
-}
-
-#ifdef CONFIG_SND_HDA_POWER_SAVE
-static int stac92xx_pre_resume(struct hda_codec *codec)
+static int stac_suspend(struct hda_codec *codec)
{
- struct sigmatel_spec *spec = codec->spec;
-
- /* sync mute LED */
- if (spec->vref_mute_led_nid)
- stac_vrefout_set(codec, spec->vref_mute_led_nid,
- spec->vref_led);
- else if (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 void stac92xx_set_power_state(struct hda_codec *codec, hda_nid_t fg,
- unsigned int power_state)
-{
- unsigned int afg_power_state = power_state;
- struct sigmatel_spec *spec = codec->spec;
-
- if (power_state == AC_PWRST_D3) {
- if (spec->vref_mute_led_nid) {
- /* with vref-out pin used for mute led control
- * codec AFG is prevented from D3 state
- */
- afg_power_state = AC_PWRST_D1;
- }
- /* this delay seems necessary to avoid click noise at power-down */
- msleep(100);
- }
- snd_hda_codec_read(codec, fg, 0, AC_VERB_SET_POWER_STATE,
- afg_power_state);
- snd_hda_codec_set_power_to_all(codec, fg, power_state, true);
-}
-
-/*
- * For this feature CONFIG_SND_HDA_POWER_SAVE is needed
- * as mute LED state is updated in check_power_status hook
- */
-static int stac92xx_update_led_status(struct hda_codec *codec)
-{
- struct sigmatel_spec *spec = codec->spec;
- int i, num_ext_dacs, muted = 1;
- unsigned int muted_lvl, notmtd_lvl;
- hda_nid_t nid;
-
- if (!spec->gpio_led)
- return 0;
-
- 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->multiout.hp_nid)
- if (!(snd_hda_codec_amp_read(codec,
- spec->multiout.hp_nid, 0, HDA_OUTPUT, 0) &
- HDA_AMP_MUTE)) {
- muted = 0; /* HP is not muted */
- }
- num_ext_dacs = ARRAY_SIZE(spec->multiout.extra_out_nid);
- for (i = 0; muted && i < num_ext_dacs; i++) {
- nid = spec->multiout.extra_out_nid[i];
- if (nid == 0)
- break;
- if (!(snd_hda_codec_amp_read(codec, nid, 0, HDA_OUTPUT, 0) &
- HDA_AMP_MUTE)) {
- muted = 0; /* extra output is not muted */
- }
- }
- /*polarity defines *not* muted state level*/
- if (!spec->vref_mute_led_nid) {
- 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);
- } else {
- notmtd_lvl = spec->gpio_led_polarity ?
- AC_PINCTL_VREF_50 : AC_PINCTL_VREF_GRD;
- muted_lvl = spec->gpio_led_polarity ?
- AC_PINCTL_VREF_GRD : AC_PINCTL_VREF_50;
- spec->vref_led = muted ? muted_lvl : notmtd_lvl;
- stac_vrefout_set(codec, spec->vref_mute_led_nid,
- spec->vref_led);
- }
- return 0;
-}
+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,
+};
-/*
- * use power check for controlling mute led of HP notebooks
- */
-static int stac92xx_check_power_status(struct hda_codec *codec,
- hda_nid_t nid)
+static int alloc_stac_spec(struct hda_codec *codec)
{
- stac92xx_update_led_status(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 /* CONFIG_SND_HDA_POWER_SAVE */
-#endif /* CONFIG_PM */
-
-static const 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 CONFIG_PM
- .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);
+
+ snd_hda_pick_fixup(codec, stac9200_models, stac9200_fixup_tbl,
+ stac9200_fixups);
+ snd_hda_apply_fixup(codec, HDA_FIXUP_ACT_PRE_PROBE);
- err = stac9200_parse_auto_config(codec);
+ 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;
}
@@ -5183,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);
- 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;
}
@@ -5265,288 +4459,105 @@ 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);
+ 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;
-
- switch (codec->subsystem_id) {
- case 0x103c1618:
- case 0x103c1619:
- case 0x103c161a:
- case 0x103c161b:
- case 0x103c161c:
- case 0x103c161d:
- case 0x103c161e:
- case 0x103c161f:
-
- case 0x103c162a:
- case 0x103c162b:
+ snd_hda_apply_fixup(codec, HDA_FIXUP_ACT_PROBE);
- 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 (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_PIN)
- stac92hd8x_add_pin(codec, nid);
-
- if (wid_type == AC_WID_AUD_IN && !(wid_caps & AC_WCAP_DIGITAL))
- stac92hd8x_add_adc(codec, nid);
+ spec->gen.cap_sync_hook = stac_capture_led_hook;
}
-
- 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_AUD_SEL)
- stac92hd8x_add_mux(codec, nid);
- }
-
- 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)
@@ -5554,154 +4565,89 @@ static int patch_stac92hd83xxx(struct hda_codec *codec)
struct sigmatel_spec *spec;
int err;
- 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);
- }
-
- 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->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 */
- codec->patch_ops = stac92xx_patch_ops;
+ codec->patch_ops = stac_patch_ops;
- if (find_mute_led_cfg(codec, -1/*no default cfg*/))
- snd_printd("mute LED gpio %d polarity %d\n",
- spec->gpio_led,
- spec->gpio_led_polarity);
+ snd_hda_add_verbs(codec, stac92hd83xxx_core_init);
-#ifdef CONFIG_SND_HDA_POWER_SAVE
- 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->patch_ops.set_power_state =
- stac92xx_set_power_state;
- }
- codec->patch_ops.pre_resume = stac92xx_pre_resume;
- codec->patch_ops.check_power_status =
- stac92xx_check_power_status;
- }
-#endif
-
- err = stac92xx_parse_auto_config(codec);
- 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;
- }
+ snd_hda_pick_fixup(codec, stac92hd83xxx_models, stac92hd83xxx_fixup_tbl,
+ stac92hd83xxx_fixups);
+ snd_hda_apply_fixup(codec, HDA_FIXUP_ACT_PRE_PROBE);
+
+ stac_setup_gpio(codec);
+ err = stac_parse_auto_config(codec);
if (err < 0) {
- stac92xx_free(codec);
+ stac_free(codec);
return err;
}
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 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,
-};
+ 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;
}
@@ -5709,249 +4655,79 @@ static int patch_stac92hd71bxx(struct hda_codec *codec)
{
struct sigmatel_spec *spec;
const struct hda_verb *unmute_init = stac92hd71bxx_unmute_core_init;
- unsigned int pin_cfg;
- int err = 0;
+ 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;
- }
+ codec->patch_ops = stac_patch_ops;
- spec->dmic_nids = stac92hd71bxx_dmic_nids;
- spec->dmux_nids = stac92hd71bxx_dmux_nids;
-
- 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(codec, 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_jack_detect_enable(codec, codec->afg, 0);
- spec->gpio_mask |= 0x02;
- break;
- }
if ((codec->revision_id & 0xf) == 0 ||
(codec->revision_id & 0xf) == 1)
spec->stream_delay = 40; /* 40 milliseconds */
/* 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);
- spec->dmic_nids = stac92hd71bxx_dmic_5port_nids;
- spec->num_dmics = stac92xx_connected_ports(codec,
- stac92hd71bxx_dmic_5port_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 */
- /* 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);
- 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;
- }
+ snd_hda_pick_fixup(codec, stac92hd71bxx_models, stac92hd71bxx_fixup_tbl,
+ stac92hd71bxx_fixups);
+ snd_hda_apply_fixup(codec, HDA_FIXUP_ACT_PRE_PROBE);
- 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_cfg(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) {
- 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->patch_ops.set_power_state =
- stac92xx_set_power_state;
- }
- codec->patch_ops.pre_resume = stac92xx_pre_resume;
- codec->patch_ops.check_power_status =
- stac92xx_check_power_status;
- }
-#endif
-
- spec->multiout.dac_nids = spec->dac_nids;
-
- err = stac92xx_parse_auto_config(codec);
- 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;
}
@@ -5960,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) {
+ spec->gen.own_eapd_ctl = 1;
- 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;
- }
- }
+ codec->patch_ops = stac_patch_ops;
- 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->init = stac922x_core_init;
-
- 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);
- 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,
@@ -6057,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;
- }
+ spec->gen.beep_nid = 0x23; /* digital beep */
- 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;
+ /* GPIO0 High = Enable EAPD */
+ spec->eapd_mask = spec->gpio_mask = 0x01;
+ spec->gpio_dir = spec->gpio_data = 0x01;
- 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->num_caps = STAC927X_NUM_CAPS;
- spec->capvols = stac927x_capvols;
- spec->capsws = stac927x_capsws;
-
- 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);
- 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;
/*
@@ -6189,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;
}
@@ -6201,105 +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(codec, 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_jack_detect_enable(codec, codec->afg, 0);
+ /* 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);
- 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;
}
@@ -6313,40 +4893,32 @@ static const struct hda_verb stac9872_core_init[] = {
{}
};
-static const hda_nid_t stac9872_pin_nids[] = {
- 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f,
- 0x11, 0x13, 0x14,
-};
-
-static const hda_nid_t stac9872_adc_nids[] = {
- 0x8 /*,0x6*/
-};
-
-static const hda_nid_t stac9872_mux_nids[] = {
- 0x15
-};
-
-static const unsigned long stac9872_capvols[] = {
- HDA_COMPOSE_AMP_VAL(0x09, 3, 0, HDA_INPUT),
-};
-#define stac9872_capsws stac9872_capvols
-
-static const 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 const 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 const 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 */
@@ -6357,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);
+ 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;
}
@@ -6463,6 +5023,7 @@ static const 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 },
diff --git a/sound/pci/hda/patch_via.c b/sound/pci/hda/patch_via.c
index dff9a00ee8f..778166259b3 100644
--- a/sound/pci/hda/patch_via.c
+++ b/sound/pci/hda/patch_via.c
@@ -54,7 +54,9 @@
#include <sound/asoundef.h>
#include "hda_codec.h"
#include "hda_local.h"
+#include "hda_auto_parser.h"
#include "hda_jack.h"
+#include "hda_generic.h"
/* Pin Widget NID */
#define VT1708_HP_PIN_NID 0x20
@@ -75,6 +77,8 @@ enum VIA_HDA_CODEC {
VT2002P,
VT1812,
VT1802,
+ VT1705CF,
+ VT1808,
CODEC_TYPES,
};
@@ -83,40 +87,9 @@ enum VIA_HDA_CODEC {
(spec)->codec_type == VT1812 ||\
(spec)->codec_type == VT1802)
-#define MAX_NID_PATH_DEPTH 5
-
-/* output-path: DAC -> ... -> pin
- * idx[] contains the source index number of the next widget;
- * e.g. idx[0] is the index of the DAC 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
- */
-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 vol_ctl;
- unsigned int mute_ctl;
-};
-
-/* input-path */
-struct via_input {
- hda_nid_t pin; /* input-pin or aa-mix */
- int adc_idx; /* ADC index to be used */
- int mux_idx; /* MUX index (if any) */
- const char *label; /* input-source label */
-};
-
-#define VIA_MAX_ADCS 3
-
-enum {
- STREAM_MULTI_OUT = (1 << 0),
- STREAM_INDEP_HP = (1 << 1),
-};
-
struct via_spec {
+ struct hda_gen_spec gen;
+
/* codec parameterization */
const struct snd_kcontrol_new *mixers[6];
unsigned int num_mixers;
@@ -124,77 +97,7 @@ struct via_spec {
const struct hda_verb *init_verbs[5];
unsigned int num_iverbs;
- char stream_name_analog[32];
- char stream_name_hp[32];
- const struct hda_pcm_stream *stream_analog_playback;
- const struct hda_pcm_stream *stream_analog_capture;
-
- char stream_name_digital[32];
- const struct hda_pcm_stream *stream_digital_playback;
- const struct hda_pcm_stream *stream_digital_capture;
-
- /* playback */
- struct hda_multi_out multiout;
- hda_nid_t slave_dig_outs[2];
- hda_nid_t hp_dac_nid;
- hda_nid_t speaker_dac_nid;
- int hp_indep_shared; /* indep HP-DAC is shared with side ch */
- int opened_streams; /* STREAM_* bits */
- int active_streams; /* STREAM_* bits */
- int aamix_mode; /* loopback is enabled for output-path? */
-
- /* Output-paths:
- * There are different output-paths depending on the setup.
- * out_path, hp_path and speaker_path are primary paths. If both
- * direct DAC and aa-loopback routes are available, these contain
- * the former paths. Meanwhile *_mix_path contain the paths with
- * loopback mixer. (Since the loopback is only for front channel,
- * no out_mix_path for surround channels.)
- * The HP output has another path, hp_indep_path, which is used in
- * the independent-HP mode.
- */
- struct nid_path out_path[HDA_SIDE + 1];
- struct nid_path out_mix_path;
- struct nid_path hp_path;
- struct nid_path hp_mix_path;
- struct nid_path hp_indep_path;
- struct nid_path speaker_path;
- struct nid_path speaker_mix_path;
-
- /* capture */
- unsigned int num_adc_nids;
- hda_nid_t adc_nids[VIA_MAX_ADCS];
- hda_nid_t mux_nids[VIA_MAX_ADCS];
- hda_nid_t aa_mix_nid;
- hda_nid_t dig_in_nid;
-
- /* capture source */
- bool dyn_adc_switch;
- int num_inputs;
- struct via_input inputs[AUTO_CFG_MAX_INS + 1];
- unsigned int cur_mux[VIA_MAX_ADCS];
-
- /* dynamic DAC switching */
- unsigned int cur_dac_stream_tag;
- unsigned int cur_dac_format;
- unsigned int cur_hp_stream_tag;
- unsigned int cur_hp_format;
-
- /* dynamic ADC switching */
- hda_nid_t cur_adc;
- unsigned int cur_adc_stream_tag;
- unsigned int cur_adc_format;
-
- /* PCM information */
- struct hda_pcm pcm_rec[3];
-
- /* 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];
-
/* HP mode source */
- unsigned int hp_independent_mode;
unsigned int dmic_enabled;
unsigned int no_pin_power_ctl;
enum VIA_HDA_CODEC codec_type;
@@ -202,35 +105,22 @@ struct via_spec {
/* analog low-power control */
bool alc_mode;
- /* smart51 setup */
- unsigned int smart51_nums;
- hda_nid_t smart51_pins[2];
- int smart51_idxs[2];
- const char *smart51_labels[2];
- unsigned int smart51_enabled;
-
/* work to check hp jack state */
- struct hda_codec *codec;
- struct delayed_work vt1708_hp_work;
int hp_work_active;
int vt1708_jack_detect;
- int vt1708_hp_present;
void (*set_widgets_power_state)(struct hda_codec *codec);
-
- struct hda_loopback_check loopback;
- int num_loopbacks;
- struct hda_amp_list loopback_list[8];
-
- /* bind capture-volume */
- struct hda_bind_ctls *bind_cap_vol;
- struct hda_bind_ctls *bind_cap_sw;
-
- struct mutex config_mutex;
+ 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;
@@ -238,13 +128,17 @@ static struct via_spec * via_new_spec(struct hda_codec *codec)
if (spec == NULL)
return NULL;
- mutex_init(&spec->config_mutex);
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;
}
@@ -291,22 +185,15 @@ static enum VIA_HDA_CODEC get_codec_type(struct hda_codec *codec)
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_JACK_EVENT 0x20
-#define VIA_HP_EVENT 0x01
-#define VIA_GPIO_EVENT 0x02
-#define VIA_LINE_EVENT 0x03
-
-enum {
- VIA_CTL_WIDGET_VOL,
- VIA_CTL_WIDGET_MUTE,
- VIA_CTL_WIDGET_ANALOG_MUTE,
-};
-
static void analog_low_current_mode(struct hda_codec *codec);
static bool is_aa_path_mute(struct hda_codec *codec);
@@ -314,392 +201,93 @@ static bool is_aa_path_mute(struct hda_codec *codec);
(snd_hda_get_bool_hint(codec, "analog_loopback_hp_detect") == 1 && \
!is_aa_path_mute(codec))
-static void vt1708_stop_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)
+ struct via_spec *spec = codec->spec;
+ if (spec->codec_type != VT1708 || !spec->gen.autocfg.hp_outs)
return;
if (spec->hp_work_active) {
- snd_hda_codec_write(spec->codec, 0x1, 0, 0xf81, 1);
- cancel_delayed_work_sync(&spec->vt1708_hp_work);
- spec->hp_work_active = 0;
+ 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;
}
}
-static void vt1708_update_hp_work(struct via_spec *spec)
+static void vt1708_update_hp_work(struct hda_codec *codec)
{
- if (spec->codec_type != VT1708 || spec->autocfg.hp_pins[0] == 0)
+ struct via_spec *spec = codec->spec;
+ if (spec->codec_type != VT1708 || !spec->gen.autocfg.hp_outs)
return;
- if (spec->vt1708_jack_detect &&
- (spec->active_streams || hp_detect_with_aa(spec->codec))) {
+ if (spec->vt1708_jack_detect) {
if (!spec->hp_work_active) {
- snd_hda_codec_write(spec->codec, 0x1, 0, 0xf81, 0);
- schedule_delayed_work(&spec->vt1708_hp_work,
- msecs_to_jiffies(100));
- spec->hp_work_active = 1;
+ 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;
}
- } else if (!hp_detect_with_aa(spec->codec))
- vt1708_stop_hp_work(spec);
+ } else if (!hp_detect_with_aa(codec))
+ vt1708_stop_hp_work(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->set_widgets_power_state)
spec->set_widgets_power_state(codec);
+#endif
}
-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_widgets_power_state(codec);
- analog_low_current_mode(snd_kcontrol_chip(kcontrol));
- vt1708_update_hp_work(codec->spec);
- 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 const struct snd_kcontrol_new via_control_templates[] = {
- HDA_CODEC_VOLUME(NULL, 0, 0, 0),
- HDA_CODEC_MUTE(NULL, 0, 0, 0),
- ANALOG_INPUT_MUTE,
-};
-
-
-/* add dynamic controls */
-static struct snd_kcontrol_new *__via_clone_ctl(struct via_spec *spec,
- const struct snd_kcontrol_new *tmpl,
- const char *name)
-{
- 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;
- if (!name)
- name = tmpl->name;
- if (name) {
- knew->name = kstrdup(name, GFP_KERNEL);
- if (!knew->name)
- return NULL;
- }
- return knew;
-}
-
-static int __via_add_control(struct via_spec *spec, int type, const char *name,
- int idx, unsigned long val)
-{
- struct snd_kcontrol_new *knew;
-
- knew = __via_clone_ctl(spec, &via_control_templates[type], name);
- if (!knew)
- return -ENOMEM;
- knew->index = idx;
- 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)
-
-#define via_clone_control(spec, tmpl) __via_clone_ctl(spec, tmpl, NULL)
-
-static void via_free_kctls(struct hda_codec *codec)
-{
- 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;
-}
-
-#define get_connection_index(codec, mux, nid) \
- snd_hda_get_conn_index(codec, mux, nid, 0)
-
-static bool check_amp_caps(struct hda_codec *codec, hda_nid_t nid, int dir,
- unsigned int mask)
-{
- unsigned int caps;
- if (!nid)
- return false;
- caps = get_wcaps(codec, nid);
- if (dir == HDA_INPUT)
- caps &= AC_WCAP_IN_AMP;
- else
- caps &= AC_WCAP_OUT_AMP;
- if (!caps)
- return false;
- if (query_amp_caps(codec, nid, dir) & mask)
- return true;
- return false;
-}
-
-#define have_mute(codec, nid, dir) \
- check_amp_caps(codec, nid, dir, AC_AMPCAP_MUTE)
-
-/* enable/disable the output-route mixers */
-static void activate_output_mix(struct hda_codec *codec, struct nid_path *path,
- hda_nid_t mix_nid, int idx, bool enable)
+static void update_power_state(struct hda_codec *codec, hda_nid_t nid,
+ unsigned int parm)
{
- int i, num, val;
-
- if (!path)
+ if (snd_hda_check_power_state(codec, nid, parm))
return;
- num = snd_hda_get_conn_list(codec, mix_nid, NULL);
- for (i = 0; i < num; i++) {
- if (i == idx)
- val = AMP_IN_UNMUTE(i);
- else
- val = AMP_IN_MUTE(i);
- snd_hda_codec_write(codec, mix_nid, 0,
- AC_VERB_SET_AMP_GAIN_MUTE, val);
- }
+ snd_hda_codec_write(codec, nid, 0, AC_VERB_SET_POWER_STATE, parm);
}
-/* enable/disable the output-route */
-static void activate_output_path(struct hda_codec *codec, struct nid_path *path,
- bool enable, bool force)
+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;
- for (i = 0; i < path->depth; i++) {
- hda_nid_t src, dst;
- int idx = path->idx[i];
- src = path->path[i];
- if (i < path->depth - 1)
- dst = path->path[i + 1];
- else
- dst = 0;
- if (enable && path->multi[i])
- snd_hda_codec_write(codec, dst, 0,
- AC_VERB_SET_CONNECT_SEL, idx);
- if (!force && (dst == spec->aa_mix_nid))
- continue;
- if (have_mute(codec, dst, HDA_INPUT))
- activate_output_mix(codec, path, dst, idx, enable);
- if (!force && (src == path->vol_ctl || src == path->mute_ctl))
- continue;
- if (have_mute(codec, src, HDA_OUTPUT)) {
- int val = enable ? AMP_OUT_UNMUTE : AMP_OUT_MUTE;
- snd_hda_codec_write(codec, src, 0,
- AC_VERB_SET_AMP_GAIN_MUTE, val);
- }
- }
-}
+ unsigned int format;
-/* set the given pin as output */
-static void init_output_pin(struct hda_codec *codec, hda_nid_t pin,
- int pin_type)
-{
- if (!pin)
+ if (snd_hda_check_power_state(codec, nid, parm))
return;
- snd_hda_codec_write(codec, pin, 0, AC_VERB_SET_PIN_WIDGET_CONTROL,
- pin_type);
- if (snd_hda_query_pin_caps(codec, pin) & AC_PINCAP_EAPD)
- snd_hda_codec_write(codec, pin, 0,
- AC_VERB_SET_EAPD_BTLENABLE, 0x02);
-}
+ 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;
-static void via_auto_init_output(struct hda_codec *codec,
- struct nid_path *path, int pin_type)
-{
- unsigned int caps;
- hda_nid_t pin;
-
- if (!path->depth)
- return;
- pin = path->path[path->depth - 1];
-
- init_output_pin(codec, pin, pin_type);
- caps = query_amp_caps(codec, pin, HDA_OUTPUT);
- if (caps & AC_AMPCAP_MUTE) {
- unsigned int val;
- val = (caps & AC_AMPCAP_OFFSET) >> AC_AMPCAP_OFFSET_SHIFT;
- snd_hda_codec_write(codec, pin, 0, AC_VERB_SET_AMP_GAIN_MUTE,
- AMP_OUT_MUTE | val);
- }
- activate_output_path(codec, path, true, true); /* force on */
-}
-
-static void via_auto_init_multi_out(struct hda_codec *codec)
-{
- struct via_spec *spec = codec->spec;
- struct nid_path *path;
- int i;
-
- for (i = 0; i < spec->autocfg.line_outs + spec->smart51_nums; i++) {
- path = &spec->out_path[i];
- if (!i && spec->aamix_mode && spec->out_mix_path.depth)
- path = &spec->out_mix_path;
- via_auto_init_output(codec, path, PIN_OUT);
- }
-}
-
-/* deactivate the inactive headphone-paths */
-static void deactivate_hp_paths(struct hda_codec *codec)
-{
- struct via_spec *spec = codec->spec;
- int shared = spec->hp_indep_shared;
-
- if (spec->hp_independent_mode) {
- activate_output_path(codec, &spec->hp_path, false, false);
- activate_output_path(codec, &spec->hp_mix_path, false, false);
- if (shared)
- activate_output_path(codec, &spec->out_path[shared],
- false, false);
- } else if (spec->aamix_mode || !spec->hp_path.depth) {
- activate_output_path(codec, &spec->hp_indep_path, false, false);
- activate_output_path(codec, &spec->hp_path, false, false);
- } else {
- activate_output_path(codec, &spec->hp_indep_path, false, false);
- activate_output_path(codec, &spec->hp_mix_path, false, false);
+ 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;
-
- if (!spec->hp_path.depth) {
- via_auto_init_output(codec, &spec->hp_mix_path, PIN_HP);
- return;
- }
- deactivate_hp_paths(codec);
- if (spec->hp_independent_mode)
- via_auto_init_output(codec, &spec->hp_indep_path, PIN_HP);
- else if (spec->aamix_mode)
- via_auto_init_output(codec, &spec->hp_mix_path, PIN_HP);
- else
- via_auto_init_output(codec, &spec->hp_path, PIN_HP);
+ return spec->gen.ext_channel_count > 2;
}
-static void via_auto_init_speaker_out(struct hda_codec *codec)
-{
- struct via_spec *spec = codec->spec;
-
- if (!spec->autocfg.speaker_outs)
- return;
- if (!spec->speaker_path.depth) {
- via_auto_init_output(codec, &spec->speaker_mix_path, PIN_OUT);
- return;
- }
- if (!spec->aamix_mode) {
- activate_output_path(codec, &spec->speaker_mix_path,
- false, false);
- via_auto_init_output(codec, &spec->speaker_path, PIN_OUT);
- } else {
- activate_output_path(codec, &spec->speaker_path, false, false);
- via_auto_init_output(codec, &spec->speaker_mix_path, PIN_OUT);
- }
-}
-
-static bool is_smart51_pins(struct hda_codec *codec, hda_nid_t pin);
-static void via_hp_automute(struct hda_codec *codec);
-
-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;
- hda_nid_t conn[HDA_MAX_CONNECTIONS];
- unsigned int ctl;
- int i, num_conns;
-
- /* init ADCs */
- 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_UNMUTE(0));
- }
-
- /* init pins */
- for (i = 0; i < cfg->num_inputs; i++) {
- hda_nid_t nid = cfg->inputs[i].pin;
- if (spec->smart51_enabled && is_smart51_pins(codec, 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);
- }
-
- /* init input-src */
- for (i = 0; i < spec->num_adc_nids; i++) {
- int adc_idx = spec->inputs[spec->cur_mux[i]].adc_idx;
- /* secondary ADCs must have the unique MUX */
- if (i > 0 && !spec->mux_nids[i])
- break;
- if (spec->mux_nids[adc_idx]) {
- int mux_idx = spec->inputs[spec->cur_mux[i]].mux_idx;
- snd_hda_codec_write(codec, spec->mux_nids[adc_idx], 0,
- AC_VERB_SET_CONNECT_SEL,
- mux_idx);
- }
- if (spec->dyn_adc_switch)
- break; /* only one input-src */
- }
-
- /* init aa-mixer */
- if (!spec->aa_mix_nid)
- return;
- num_conns = snd_hda_get_connections(codec, spec->aa_mix_nid, conn,
- ARRAY_SIZE(conn));
- for (i = 0; i < num_conns; i++) {
- unsigned int caps = get_wcaps(codec, conn[i]);
- if (get_wcaps_type(caps) == AC_WID_PIN)
- snd_hda_codec_write(codec, spec->aa_mix_nid, 0,
- AC_VERB_SET_AMP_GAIN_MUTE,
- AMP_IN_MUTE(i));
- }
-}
+ int i;
-static void update_power_state(struct hda_codec *codec, hda_nid_t nid,
- unsigned int parm)
-{
- if (snd_hda_codec_read(codec, nid, 0,
- AC_VERB_GET_POWER_STATE, 0) == parm)
- return;
- snd_hda_codec_write(codec, nid, 0, AC_VERB_SET_POWER_STATE, parm);
+ 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,
@@ -716,7 +304,7 @@ static void set_pin_power_state(struct hda_codec *codec, hda_nid_t nid,
no_presence |= spec->no_pin_power_ctl;
if (!no_presence)
present = snd_hda_jack_detect(codec, nid);
- if ((spec->smart51_enabled && is_smart51_pins(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 */
@@ -730,18 +318,7 @@ static void set_pin_power_state(struct hda_codec *codec, hda_nid_t nid,
static int via_pin_power_ctl_info(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_info *uinfo)
{
- static const char * const texts[] = {
- "Disabled", "Enabled"
- };
-
- uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED;
- uinfo->count = 1;
- uinfo->value.enumerated.items = 2;
- 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;
+ return snd_hda_enum_bool_helper_info(kcontrol, uinfo);
}
static int via_pin_power_ctl_get(struct snd_kcontrol *kcontrol,
@@ -768,279 +345,29 @@ static int via_pin_power_ctl_put(struct snd_kcontrol *kcontrol,
return 1;
}
-static const struct snd_kcontrol_new via_pin_power_ctl_enum = {
+static const struct snd_kcontrol_new via_pin_power_ctl_enum[] = {
+ {
.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,
+ },
+ {} /* terminator */
};
-static int via_independent_hp_info(struct snd_kcontrol *kcontrol,
- struct snd_ctl_elem_info *uinfo)
-{
- static const char * const texts[] = { "OFF", "ON" };
-
- uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED;
- uinfo->count = 1;
- uinfo->value.enumerated.items = 2;
- 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 via_independent_hp_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;
-
- ucontrol->value.enumerated.item[0] = spec->hp_independent_mode;
- return 0;
-}
-
-/* adjust spec->multiout setup according to the current flags */
-static void setup_playback_multi_pcm(struct via_spec *spec)
-{
- const struct auto_pin_cfg *cfg = &spec->autocfg;
- spec->multiout.num_dacs = cfg->line_outs + spec->smart51_nums;
- spec->multiout.hp_nid = 0;
- if (!spec->hp_independent_mode) {
- if (!spec->hp_indep_shared)
- spec->multiout.hp_nid = spec->hp_dac_nid;
- } else {
- if (spec->hp_indep_shared)
- spec->multiout.num_dacs = cfg->line_outs - 1;
- }
-}
-
-/* update DAC setups according to indep-HP switch;
- * this function is called only when indep-HP is modified
- */
-static void switch_indep_hp_dacs(struct hda_codec *codec)
-{
- struct via_spec *spec = codec->spec;
- int shared = spec->hp_indep_shared;
- hda_nid_t shared_dac, hp_dac;
-
- if (!spec->opened_streams)
- return;
-
- shared_dac = shared ? spec->multiout.dac_nids[shared] : 0;
- hp_dac = spec->hp_dac_nid;
- if (spec->hp_independent_mode) {
- /* switch to indep-HP mode */
- if (spec->active_streams & STREAM_MULTI_OUT) {
- __snd_hda_codec_cleanup_stream(codec, hp_dac, 1);
- __snd_hda_codec_cleanup_stream(codec, shared_dac, 1);
- }
- if (spec->active_streams & STREAM_INDEP_HP)
- snd_hda_codec_setup_stream(codec, hp_dac,
- spec->cur_hp_stream_tag, 0,
- spec->cur_hp_format);
- } else {
- /* back to HP or shared-DAC */
- if (spec->active_streams & STREAM_INDEP_HP)
- __snd_hda_codec_cleanup_stream(codec, hp_dac, 1);
- if (spec->active_streams & STREAM_MULTI_OUT) {
- hda_nid_t dac;
- int ch;
- if (shared_dac) { /* reset mutli-ch DAC */
- dac = shared_dac;
- ch = shared * 2;
- } else { /* reset HP DAC */
- dac = hp_dac;
- ch = 0;
- }
- snd_hda_codec_setup_stream(codec, dac,
- spec->cur_dac_stream_tag, ch,
- spec->cur_dac_format);
- }
- }
- setup_playback_multi_pcm(spec);
-}
-
-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;
- int cur, shared;
-
- mutex_lock(&spec->config_mutex);
- cur = !!ucontrol->value.enumerated.item[0];
- if (spec->hp_independent_mode == cur) {
- mutex_unlock(&spec->config_mutex);
- return 0;
- }
- spec->hp_independent_mode = cur;
- shared = spec->hp_indep_shared;
- deactivate_hp_paths(codec);
- if (cur)
- activate_output_path(codec, &spec->hp_indep_path, true, false);
- else {
- if (shared)
- activate_output_path(codec, &spec->out_path[shared],
- true, false);
- if (spec->aamix_mode || !spec->hp_path.depth)
- activate_output_path(codec, &spec->hp_mix_path,
- true, false);
- else
- activate_output_path(codec, &spec->hp_path,
- true, false);
- }
-
- switch_indep_hp_dacs(codec);
- mutex_unlock(&spec->config_mutex);
-
- /* update jack power state */
- set_widgets_power_state(codec);
- via_hp_automute(codec);
- return 1;
-}
-
-static const struct snd_kcontrol_new via_hp_mixer = {
- .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
- .name = "Independent HP",
- .info = via_independent_hp_info,
- .get = via_independent_hp_get,
- .put = via_independent_hp_put,
-};
-
-static int via_hp_build(struct hda_codec *codec)
-{
- struct via_spec *spec = codec->spec;
- struct snd_kcontrol_new *knew;
- hda_nid_t nid;
-
- nid = spec->autocfg.hp_pins[0];
- knew = via_clone_control(spec, &via_hp_mixer);
- if (knew == NULL)
- return -ENOMEM;
-
- knew->subdevice = HDA_SUBDEV_NID_FLAG | nid;
-
- return 0;
-}
-
-static void notify_aa_path_ctls(struct hda_codec *codec)
-{
- struct via_spec *spec = codec->spec;
- int i;
-
- for (i = 0; i < spec->smart51_nums; i++) {
- struct snd_kcontrol *ctl;
- struct snd_ctl_elem_id id;
- memset(&id, 0, sizeof(id));
- id.iface = SNDRV_CTL_ELEM_IFACE_MIXER;
- sprintf(id.name, "%s Playback Volume", spec->smart51_labels[i]);
- ctl = snd_hda_find_mixer_ctl(codec, id.name);
- if (ctl)
- snd_ctl_notify(codec->bus->card,
- SNDRV_CTL_EVENT_MASK_VALUE,
- &ctl->id);
- }
-}
-
-static void mute_aa_path(struct hda_codec *codec, int mute)
-{
- struct via_spec *spec = codec->spec;
- int val = mute ? HDA_AMP_MUTE : HDA_AMP_UNMUTE;
- int i;
-
- /* check AA path's mute status */
- for (i = 0; i < spec->smart51_nums; i++) {
- if (spec->smart51_idxs[i] < 0)
- continue;
- snd_hda_codec_amp_stereo(codec, spec->aa_mix_nid,
- HDA_INPUT, spec->smart51_idxs[i],
- HDA_AMP_MUTE, val);
- }
-}
-
-static bool is_smart51_pins(struct hda_codec *codec, hda_nid_t pin)
-{
- struct via_spec *spec = codec->spec;
- int i;
-
- for (i = 0; i < spec->smart51_nums; i++)
- if (spec->smart51_pins[i] == pin)
- return true;
- return false;
-}
-
-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;
-
- *ucontrol->value.integer.value = spec->smart51_enabled;
- 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;
- int out_in = *ucontrol->value.integer.value
- ? AC_PINCTL_OUT_EN : AC_PINCTL_IN_EN;
- int i;
-
- for (i = 0; i < spec->smart51_nums; i++) {
- hda_nid_t nid = spec->smart51_pins[i];
- unsigned int parm;
-
- 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);
- }
- }
- spec->smart51_enabled = *ucontrol->value.integer.value;
- set_widgets_power_state(codec);
- return 1;
-}
-
-static const struct snd_kcontrol_new via_smart51_mixer = {
- .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
- .name = "Smart 5.1",
- .count = 1,
- .info = snd_ctl_boolean_mono_info,
- .get = via_smart51_get,
- .put = via_smart51_put,
-};
-
-static int via_smart51_build(struct hda_codec *codec)
-{
- struct via_spec *spec = codec->spec;
-
- if (!spec->smart51_nums)
- return 0;
- if (!via_clone_control(spec, &via_smart51_mixer))
- return -ENOMEM;
- return 0;
-}
-
/* check AA path's mute status */
static bool is_aa_path_mute(struct hda_codec *codec)
{
struct via_spec *spec = codec->spec;
const struct hda_amp_list *p;
- int i, ch, v;
+ int ch, v;
- for (i = 0; i < spec->num_loopbacks; i++) {
- p = &spec->loopback_list[i];
+ 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);
@@ -1061,7 +388,7 @@ static void __analog_low_current_mode(struct hda_codec *codec, bool force)
if (spec->no_pin_power_ctl)
enable = false;
else
- enable = is_aa_path_mute(codec) && !spec->opened_streams;
+ enable = is_aa_path_mute(codec) && !spec->gen.active_streams;
if (enable == spec->alc_mode && !force)
return;
spec->alc_mode = enable;
@@ -1089,6 +416,11 @@ static void __analog_low_current_mode(struct hda_codec *codec, bool force)
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 */
}
@@ -1101,382 +433,17 @@ static void analog_low_current_mode(struct hda_codec *codec)
return __analog_low_current_mode(codec, false);
}
-/*
- * generic initialization of ADC, input mixers and output mixers
- */
-static const struct hda_verb vt1708_init_verbs[] = {
- /* power down jack detect function */
- {0x1, 0xf81, 0x1},
- { }
-};
-
-static void set_stream_open(struct hda_codec *codec, int bit, bool active)
-{
- struct via_spec *spec = codec->spec;
-
- if (active)
- spec->opened_streams |= bit;
- else
- spec->opened_streams &= ~bit;
- analog_low_current_mode(codec);
-}
-
-static int via_playback_multi_pcm_open(struct hda_pcm_stream *hinfo,
- struct hda_codec *codec,
- struct snd_pcm_substream *substream)
-{
- struct via_spec *spec = codec->spec;
- const struct auto_pin_cfg *cfg = &spec->autocfg;
- int err;
-
- spec->multiout.num_dacs = cfg->line_outs + spec->smart51_nums;
- spec->multiout.max_channels = spec->multiout.num_dacs * 2;
- set_stream_open(codec, STREAM_MULTI_OUT, true);
- err = snd_hda_multi_out_analog_open(codec, &spec->multiout, substream,
- hinfo);
- if (err < 0) {
- set_stream_open(codec, STREAM_MULTI_OUT, false);
- return err;
- }
- return 0;
-}
-
-static int via_playback_multi_pcm_close(struct hda_pcm_stream *hinfo,
- struct hda_codec *codec,
- struct snd_pcm_substream *substream)
-{
- set_stream_open(codec, STREAM_MULTI_OUT, false);
- return 0;
-}
-
-static int via_playback_hp_pcm_open(struct hda_pcm_stream *hinfo,
- struct hda_codec *codec,
- struct snd_pcm_substream *substream)
-{
- struct via_spec *spec = codec->spec;
-
- if (snd_BUG_ON(!spec->hp_dac_nid))
- return -EINVAL;
- set_stream_open(codec, STREAM_INDEP_HP, true);
- return 0;
-}
-
-static int via_playback_hp_pcm_close(struct hda_pcm_stream *hinfo,
- struct hda_codec *codec,
- struct snd_pcm_substream *substream)
-{
- set_stream_open(codec, STREAM_INDEP_HP, false);
- return 0;
-}
-
-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)
-{
- struct via_spec *spec = codec->spec;
-
- mutex_lock(&spec->config_mutex);
- setup_playback_multi_pcm(spec);
- snd_hda_multi_out_analog_prepare(codec, &spec->multiout, stream_tag,
- format, substream);
- /* remember for dynamic DAC switch with indep-HP */
- spec->active_streams |= STREAM_MULTI_OUT;
- spec->cur_dac_stream_tag = stream_tag;
- spec->cur_dac_format = format;
- mutex_unlock(&spec->config_mutex);
- vt1708_update_hp_work(spec);
- return 0;
-}
-
-static int via_playback_hp_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;
-
- mutex_lock(&spec->config_mutex);
- if (spec->hp_independent_mode)
- snd_hda_codec_setup_stream(codec, spec->hp_dac_nid,
- stream_tag, 0, format);
- spec->active_streams |= STREAM_INDEP_HP;
- spec->cur_hp_stream_tag = stream_tag;
- spec->cur_hp_format = format;
- mutex_unlock(&spec->config_mutex);
- vt1708_update_hp_work(spec);
- return 0;
-}
-
-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;
-
- mutex_lock(&spec->config_mutex);
- snd_hda_multi_out_analog_cleanup(codec, &spec->multiout);
- spec->active_streams &= ~STREAM_MULTI_OUT;
- mutex_unlock(&spec->config_mutex);
- vt1708_update_hp_work(spec);
- return 0;
-}
-
-static int via_playback_hp_pcm_cleanup(struct hda_pcm_stream *hinfo,
- struct hda_codec *codec,
- struct snd_pcm_substream *substream)
-{
- struct via_spec *spec = codec->spec;
-
- mutex_lock(&spec->config_mutex);
- if (spec->hp_independent_mode)
- snd_hda_codec_setup_stream(codec, spec->hp_dac_nid, 0, 0, 0);
- spec->active_streams &= ~STREAM_INDEP_HP;
- mutex_unlock(&spec->config_mutex);
- vt1708_update_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;
-}
-
-/* analog capture with dynamic ADC switching */
-static int via_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 via_spec *spec = codec->spec;
- int adc_idx = spec->inputs[spec->cur_mux[0]].adc_idx;
-
- mutex_lock(&spec->config_mutex);
- spec->cur_adc = spec->adc_nids[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);
- mutex_unlock(&spec->config_mutex);
- return 0;
-}
-
-static int via_dyn_adc_capture_pcm_cleanup(struct hda_pcm_stream *hinfo,
- struct hda_codec *codec,
- struct snd_pcm_substream *substream)
-{
- struct via_spec *spec = codec->spec;
-
- mutex_lock(&spec->config_mutex);
- snd_hda_codec_cleanup_stream(codec, spec->cur_adc);
- spec->cur_adc = 0;
- mutex_unlock(&spec->config_mutex);
- return 0;
-}
-
-/* re-setup the stream if running; called from input-src put */
-static bool via_dyn_adc_pcm_resetup(struct hda_codec *codec, int cur)
-{
- struct via_spec *spec = codec->spec;
- int adc_idx = spec->inputs[cur].adc_idx;
- hda_nid_t adc = spec->adc_nids[adc_idx];
- bool ret = false;
-
- mutex_lock(&spec->config_mutex);
- if (spec->cur_adc && spec->cur_adc != adc) {
- /* stream is running, let's swap the current ADC */
- __snd_hda_codec_cleanup_stream(codec, spec->cur_adc, 1);
- spec->cur_adc = adc;
- snd_hda_codec_setup_stream(codec, adc,
- spec->cur_adc_stream_tag, 0,
- spec->cur_adc_format);
- ret = true;
- }
- mutex_unlock(&spec->config_mutex);
- return ret;
-}
-
-static const struct hda_pcm_stream via_pcm_analog_playback = {
- .substreams = 1,
- .channels_min = 2,
- .channels_max = 8,
- /* NID is set in via_build_pcms */
- .ops = {
- .open = via_playback_multi_pcm_open,
- .close = via_playback_multi_pcm_close,
- .prepare = via_playback_multi_pcm_prepare,
- .cleanup = via_playback_multi_pcm_cleanup
- },
-};
-
-static const struct hda_pcm_stream via_pcm_hp_playback = {
- .substreams = 1,
- .channels_min = 2,
- .channels_max = 2,
- /* NID is set in via_build_pcms */
- .ops = {
- .open = via_playback_hp_pcm_open,
- .close = via_playback_hp_pcm_close,
- .prepare = via_playback_hp_pcm_prepare,
- .cleanup = via_playback_hp_pcm_cleanup
- },
-};
-
-static const struct hda_pcm_stream vt1708_pcm_analog_s16_playback = {
- .substreams = 1,
- .channels_min = 2,
- .channels_max = 8,
- /* NID is set in via_build_pcms */
- /* 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_multi_pcm_open,
- .close = via_playback_multi_pcm_close,
- .prepare = via_playback_multi_pcm_prepare,
- .cleanup = via_playback_multi_pcm_cleanup
- },
-};
-
-static const struct hda_pcm_stream via_pcm_analog_capture = {
- .substreams = 1, /* will be changed in via_build_pcms() */
- .channels_min = 2,
- .channels_max = 2,
- /* NID is set in via_build_pcms */
- .ops = {
- .prepare = via_capture_pcm_prepare,
- .cleanup = via_capture_pcm_cleanup
- },
-};
-
-static const struct hda_pcm_stream via_pcm_dyn_adc_analog_capture = {
- .substreams = 1,
- .channels_min = 2,
- .channels_max = 2,
- /* NID is set in via_build_pcms */
- .ops = {
- .prepare = via_dyn_adc_capture_pcm_prepare,
- .cleanup = via_dyn_adc_capture_pcm_cleanup,
- },
-};
-
-static const struct hda_pcm_stream via_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 const struct hda_pcm_stream via_pcm_digital_capture = {
- .substreams = 1,
- .channels_min = 2,
- .channels_max = 2,
-};
-
-/*
- * slave controls for virtual master
- */
-static const char * const via_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 via_slave_sws[] = {
- "Front Playback Switch",
- "Surround Playback Switch",
- "Center Playback Switch",
- "LFE Playback Switch",
- "Side Playback Switch",
- "Headphone Playback Switch",
- "Speaker Playback Switch",
- NULL,
-};
-
static int via_build_controls(struct hda_codec *codec)
{
struct via_spec *spec = codec->spec;
- struct snd_kcontrol *kctl;
int err, i;
- spec->no_pin_power_ctl = 1;
+ err = snd_hda_gen_build_controls(codec);
+ if (err < 0)
+ return err;
+
if (spec->set_widgets_power_state)
- if (!via_clone_control(spec, &via_pin_power_ctl_enum))
- return -ENOMEM;
+ spec->mixers[spec->num_mixers++] = via_pin_power_ctl_enum;
for (i = 0; i < spec->num_mixers; i++) {
err = snd_hda_add_new_ctls(codec, spec->mixers[i]);
@@ -1484,294 +451,46 @@ static int via_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,
- 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 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);
- err = snd_hda_add_vmaster(codec, "Master Playback Volume",
- vmaster_tlv, via_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, via_slave_sws);
- if (err < 0)
- return err;
- }
-
- /* 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]);
- if (err < 0)
- return err;
- }
-
- via_free_kctls(codec); /* no longer needed */
-
- err = snd_hda_jack_add_kctls(codec, &spec->autocfg);
- if (err < 0)
- return err;
-
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 = 0;
- codec->pcm_info = info;
-
- if (spec->multiout.num_dacs || spec->num_adc_nids) {
- snprintf(spec->stream_name_analog,
- sizeof(spec->stream_name_analog),
- "%s Analog", codec->chip_name);
- info->name = spec->stream_name_analog;
-
- if (spec->multiout.num_dacs) {
- if (!spec->stream_analog_playback)
- spec->stream_analog_playback =
- &via_pcm_analog_playback;
- 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_PLAYBACK].channels_max =
- spec->multiout.max_channels;
- }
-
- if (!spec->stream_analog_capture) {
- if (spec->dyn_adc_switch)
- spec->stream_analog_capture =
- &via_pcm_dyn_adc_analog_capture;
- else
- spec->stream_analog_capture =
- &via_pcm_analog_capture;
- }
- if (spec->num_adc_nids) {
- info->stream[SNDRV_PCM_STREAM_CAPTURE] =
- *spec->stream_analog_capture;
- info->stream[SNDRV_PCM_STREAM_CAPTURE].nid =
- spec->adc_nids[0];
- if (!spec->dyn_adc_switch)
- info->stream[SNDRV_PCM_STREAM_CAPTURE].substreams =
- spec->num_adc_nids;
- }
- codec->num_pcms++;
- info++;
- }
-
- 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);
- info->name = spec->stream_name_digital;
- info->pcm_type = HDA_PCM_TYPE_SPDIF;
- if (spec->multiout.dig_out_nid) {
- if (!spec->stream_digital_playback)
- spec->stream_digital_playback =
- &via_pcm_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) {
- if (!spec->stream_digital_capture)
- spec->stream_digital_capture =
- &via_pcm_digital_capture;
- info->stream[SNDRV_PCM_STREAM_CAPTURE] =
- *spec->stream_digital_capture;
- info->stream[SNDRV_PCM_STREAM_CAPTURE].nid =
- spec->dig_in_nid;
- }
- codec->num_pcms++;
- info++;
- }
-
- if (spec->hp_dac_nid) {
- snprintf(spec->stream_name_hp, sizeof(spec->stream_name_hp),
- "%s HP", codec->chip_name);
- info->name = spec->stream_name_hp;
- info->stream[SNDRV_PCM_STREAM_PLAYBACK] = via_pcm_hp_playback;
- info->stream[SNDRV_PCM_STREAM_PLAYBACK].nid =
- spec->hp_dac_nid;
- codec->num_pcms++;
- info++;
- }
- 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(spec->bind_cap_vol);
- kfree(spec->bind_cap_sw);
- kfree(spec);
-}
-
-/* mute/unmute outputs */
-static void toggle_output_mutes(struct hda_codec *codec, int num_pins,
- hda_nid_t *pins, bool mute)
-{
- int i;
- for (i = 0; i < num_pins; i++) {
- unsigned int parm = snd_hda_codec_read(codec, pins[i], 0,
- AC_VERB_GET_PIN_WIDGET_CONTROL, 0);
- if (parm & AC_PINCTL_IN_EN)
- continue;
- if (mute)
- parm &= ~AC_PINCTL_OUT_EN;
- else
- parm |= AC_PINCTL_OUT_EN;
- snd_hda_codec_write(codec, pins[i], 0,
- AC_VERB_SET_PIN_WIDGET_CONTROL, parm);
- }
-}
-
-/* mute internal speaker if line-out is plugged */
-static void via_line_automute(struct hda_codec *codec, int present)
-{
- struct via_spec *spec = codec->spec;
-
- if (!spec->autocfg.speaker_outs)
- return;
- if (!present)
- present = snd_hda_jack_detect(codec,
- spec->autocfg.line_out_pins[0]);
- toggle_output_mutes(codec, spec->autocfg.speaker_outs,
- spec->autocfg.speaker_pins,
- present);
-}
-
-/* mute internal speaker if HP is plugged */
-static void via_hp_automute(struct hda_codec *codec)
-{
- int present = 0;
- int nums;
- struct via_spec *spec = codec->spec;
-
- if (!spec->hp_independent_mode && spec->autocfg.hp_pins[0] &&
- (spec->codec_type != VT1708 || spec->vt1708_jack_detect))
- present = snd_hda_jack_detect(codec, spec->autocfg.hp_pins[0]);
-
- if (spec->smart51_enabled)
- nums = spec->autocfg.line_outs + spec->smart51_nums;
- else
- nums = spec->autocfg.line_outs;
- toggle_output_mutes(codec, nums, spec->autocfg.line_out_pins, present);
-
- via_line_automute(codec, present);
+ vt1708_stop_hp_work(codec);
+ snd_hda_gen_free(codec);
}
-static void via_gpio_control(struct hda_codec *codec)
+#ifdef CONFIG_PM
+static int via_suspend(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;
+ vt1708_stop_hp_work(codec);
- 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_write(codec, spec->autocfg.line_out_pins[0], 0,
- AC_VERB_SET_PIN_WIDGET_CONTROL,
- PIN_OUT);
- 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_write(codec, spec->autocfg.line_out_pins[0], 0,
- AC_VERB_SET_PIN_WIDGET_CONTROL,
- 0);
- }
-}
-
-/* unsolicited event for jack sensing */
-static void via_unsol_event(struct hda_codec *codec,
- unsigned int res)
-{
- res >>= 26;
- res = snd_hda_jack_get_action(codec, res);
-
- if (res & VIA_JACK_EVENT)
- set_widgets_power_state(codec);
-
- res &= ~VIA_JACK_EVENT;
-
- if (res == VIA_HP_EVENT || res == VIA_LINE_EVENT)
- via_hp_automute(codec);
- else if (res == VIA_GPIO_EVENT)
- via_gpio_control(codec);
- snd_hda_jack_report_sync(codec);
-}
+ /* Fix pop noise on headphones */
+ if (spec->codec_type == VT1802)
+ snd_hda_shutup_pins(codec);
-#ifdef CONFIG_PM
-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
@@ -1782,848 +501,22 @@ 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,
- .unsol_event = via_unsol_event,
+ .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
};
-static bool is_empty_dac(struct hda_codec *codec, hda_nid_t dac)
-{
- struct via_spec *spec = codec->spec;
- int i;
-
- for (i = 0; i < spec->multiout.num_dacs; i++) {
- if (spec->multiout.dac_nids[i] == dac)
- return false;
- }
- if (spec->hp_dac_nid == dac)
- return false;
- return true;
-}
-static bool __parse_output_path(struct hda_codec *codec, hda_nid_t nid,
- hda_nid_t target_dac, int with_aa_mix,
- struct nid_path *path, int depth)
-{
- struct via_spec *spec = codec->spec;
- hda_nid_t conn[8];
- int i, nums;
-
- if (nid == spec->aa_mix_nid) {
- if (!with_aa_mix)
- return false;
- with_aa_mix = 2; /* mark aa-mix is included */
- }
-
- nums = snd_hda_get_connections(codec, nid, conn, ARRAY_SIZE(conn));
- for (i = 0; i < nums; i++) {
- if (get_wcaps_type(get_wcaps(codec, conn[i])) != AC_WID_AUD_OUT)
- continue;
- if (conn[i] == target_dac || is_empty_dac(codec, conn[i])) {
- /* aa-mix is requested but not included? */
- if (!(spec->aa_mix_nid && with_aa_mix == 1))
- 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)
- continue;
- if (__parse_output_path(codec, conn[i], target_dac,
- with_aa_mix, path, depth + 1))
- goto found;
- }
- return false;
-
- found:
- path->path[path->depth] = conn[i];
- path->idx[path->depth] = i;
- if (nums > 1 && get_wcaps_type(get_wcaps(codec, nid)) != AC_WID_AUD_MIX)
- path->multi[path->depth] = 1;
- path->depth++;
- return true;
-}
-
-static bool parse_output_path(struct hda_codec *codec, hda_nid_t nid,
- hda_nid_t target_dac, int with_aa_mix,
- struct nid_path *path)
-{
- if (__parse_output_path(codec, nid, target_dac, with_aa_mix, path, 1)) {
- path->path[path->depth] = nid;
- path->depth++;
- snd_printdd("output-path: depth=%d, %02x/%02x/%02x/%02x/%02x\n",
- path->depth, path->path[0], path->path[1],
- path->path[2], path->path[3], path->path[4]);
- return true;
- }
- return false;
-}
-
-static int via_auto_fill_dac_nids(struct hda_codec *codec)
-{
- struct via_spec *spec = codec->spec;
- const struct auto_pin_cfg *cfg = &spec->autocfg;
- int i, dac_num;
- hda_nid_t nid;
-
- spec->multiout.dac_nids = spec->private_dac_nids;
- dac_num = 0;
- for (i = 0; i < cfg->line_outs; i++) {
- hda_nid_t dac = 0;
- nid = cfg->line_out_pins[i];
- if (!nid)
- continue;
- if (parse_output_path(codec, nid, 0, 0, &spec->out_path[i]))
- dac = spec->out_path[i].path[0];
- if (!i && parse_output_path(codec, nid, dac, 1,
- &spec->out_mix_path))
- dac = spec->out_mix_path.path[0];
- if (dac) {
- spec->private_dac_nids[i] = dac;
- dac_num++;
- }
- }
- if (!spec->out_path[0].depth && spec->out_mix_path.depth) {
- spec->out_path[0] = spec->out_mix_path;
- spec->out_mix_path.depth = 0;
- }
- spec->multiout.num_dacs = dac_num;
- return 0;
-}
-
-static int create_ch_ctls(struct hda_codec *codec, const char *pfx,
- int chs, bool check_dac, struct nid_path *path)
-{
- struct via_spec *spec = codec->spec;
- char name[32];
- hda_nid_t dac, pin, sel, nid;
- int err;
-
- dac = check_dac ? path->path[0] : 0;
- pin = path->path[path->depth - 1];
- sel = path->depth > 1 ? path->path[1] : 0;
-
- if (dac && check_amp_caps(codec, dac, HDA_OUTPUT, AC_AMPCAP_NUM_STEPS))
- nid = dac;
- else if (check_amp_caps(codec, pin, HDA_OUTPUT, AC_AMPCAP_NUM_STEPS))
- nid = pin;
- else if (check_amp_caps(codec, sel, HDA_OUTPUT, AC_AMPCAP_NUM_STEPS))
- nid = sel;
- else
- nid = 0;
- if (nid) {
- sprintf(name, "%s Playback Volume", pfx);
- err = via_add_control(spec, VIA_CTL_WIDGET_VOL, name,
- HDA_COMPOSE_AMP_VAL(nid, chs, 0, HDA_OUTPUT));
- if (err < 0)
- return err;
- path->vol_ctl = nid;
- }
-
- if (dac && check_amp_caps(codec, dac, HDA_OUTPUT, AC_AMPCAP_MUTE))
- nid = dac;
- else if (check_amp_caps(codec, pin, HDA_OUTPUT, AC_AMPCAP_MUTE))
- nid = pin;
- else if (check_amp_caps(codec, sel, HDA_OUTPUT, AC_AMPCAP_MUTE))
- nid = sel;
- else
- nid = 0;
- if (nid) {
- sprintf(name, "%s Playback Switch", pfx);
- err = via_add_control(spec, VIA_CTL_WIDGET_MUTE, name,
- HDA_COMPOSE_AMP_VAL(nid, chs, 0, HDA_OUTPUT));
- if (err < 0)
- return err;
- path->mute_ctl = nid;
- }
- return 0;
-}
-
-static void mangle_smart51(struct hda_codec *codec)
-{
- struct via_spec *spec = codec->spec;
- struct auto_pin_cfg *cfg = &spec->autocfg;
- struct auto_pin_cfg_item *ins = cfg->inputs;
- int i, j, nums, attr;
- int pins[AUTO_CFG_MAX_INS];
-
- for (attr = INPUT_PIN_ATTR_REAR; attr >= INPUT_PIN_ATTR_NORMAL; attr--) {
- nums = 0;
- for (i = 0; i < cfg->num_inputs; i++) {
- unsigned int def;
- if (ins[i].type > AUTO_PIN_LINE_IN)
- continue;
- def = snd_hda_codec_get_pincfg(codec, ins[i].pin);
- if (snd_hda_get_input_pin_attr(def) != attr)
- continue;
- for (j = 0; j < nums; j++)
- if (ins[pins[j]].type < ins[i].type) {
- memmove(pins + j + 1, pins + j,
- (nums - j) * sizeof(int));
- break;
- }
- pins[j] = i;
- nums++;
- }
- if (cfg->line_outs + nums < 3)
- continue;
- for (i = 0; i < nums; i++) {
- hda_nid_t pin = ins[pins[i]].pin;
- spec->smart51_pins[spec->smart51_nums++] = pin;
- cfg->line_out_pins[cfg->line_outs++] = pin;
- if (cfg->line_outs == 3)
- break;
- }
- return;
- }
-}
-
-static void copy_path_mixer_ctls(struct nid_path *dst, struct nid_path *src)
-{
- dst->vol_ctl = src->vol_ctl;
- dst->mute_ctl = src->mute_ctl;
-}
-
-/* add playback controls from the parsed DAC table */
-static int via_auto_create_multi_out_ctls(struct hda_codec *codec)
-{
- struct via_spec *spec = codec->spec;
- struct auto_pin_cfg *cfg = &spec->autocfg;
- struct nid_path *path;
- static const char * const chname[4] = {
- "Front", "Surround", "C/LFE", "Side"
- };
- int i, idx, err;
- int old_line_outs;
-
- /* check smart51 */
- old_line_outs = cfg->line_outs;
- if (cfg->line_outs == 1)
- mangle_smart51(codec);
-
- err = via_auto_fill_dac_nids(codec);
- if (err < 0)
- return err;
-
- if (spec->multiout.num_dacs < 3) {
- spec->smart51_nums = 0;
- cfg->line_outs = old_line_outs;
- }
- for (i = 0; i < cfg->line_outs; i++) {
- hda_nid_t pin, dac;
- pin = cfg->line_out_pins[i];
- dac = spec->multiout.dac_nids[i];
- if (!pin || !dac)
- continue;
- path = spec->out_path + i;
- if (i == HDA_CLFE) {
- err = create_ch_ctls(codec, "Center", 1, true, path);
- if (err < 0)
- return err;
- err = create_ch_ctls(codec, "LFE", 2, true, path);
- if (err < 0)
- return err;
- } else {
- const char *pfx = chname[i];
- if (cfg->line_out_type == AUTO_PIN_SPEAKER_OUT &&
- cfg->line_outs == 1)
- pfx = "Speaker";
- err = create_ch_ctls(codec, pfx, 3, true, path);
- if (err < 0)
- return err;
- }
- if (path != spec->out_path + i)
- copy_path_mixer_ctls(&spec->out_path[i], path);
- if (path == spec->out_path && spec->out_mix_path.depth)
- copy_path_mixer_ctls(&spec->out_mix_path, path);
- }
-
- idx = get_connection_index(codec, spec->aa_mix_nid,
- spec->multiout.dac_nids[0]);
- if (idx >= 0) {
- /* add control to mixer */
- const char *name;
- name = spec->out_mix_path.depth ?
- "PCM Loopback Playback Volume" : "PCM Playback Volume";
- err = via_add_control(spec, VIA_CTL_WIDGET_VOL, name,
- HDA_COMPOSE_AMP_VAL(spec->aa_mix_nid, 3,
- idx, HDA_INPUT));
- if (err < 0)
- return err;
- name = spec->out_mix_path.depth ?
- "PCM Loopback Playback Switch" : "PCM Playback Switch";
- err = via_add_control(spec, VIA_CTL_WIDGET_MUTE, name,
- HDA_COMPOSE_AMP_VAL(spec->aa_mix_nid, 3,
- idx, HDA_INPUT));
- if (err < 0)
- return err;
- }
-
- cfg->line_outs = old_line_outs;
-
- return 0;
-}
-
-static int via_auto_create_hp_ctls(struct hda_codec *codec, hda_nid_t pin)
-{
- struct via_spec *spec = codec->spec;
- struct nid_path *path;
- bool check_dac;
- int i, err;
-
- if (!pin)
- return 0;
-
- if (!parse_output_path(codec, pin, 0, 0, &spec->hp_indep_path)) {
- for (i = HDA_SIDE; i >= HDA_CLFE; i--) {
- if (i < spec->multiout.num_dacs &&
- parse_output_path(codec, pin,
- spec->multiout.dac_nids[i], 0,
- &spec->hp_indep_path)) {
- spec->hp_indep_shared = i;
- break;
- }
- }
- }
- if (spec->hp_indep_path.depth) {
- spec->hp_dac_nid = spec->hp_indep_path.path[0];
- if (!spec->hp_indep_shared)
- spec->hp_path = spec->hp_indep_path;
- }
- /* optionally check front-path w/o AA-mix */
- if (!spec->hp_path.depth)
- parse_output_path(codec, pin,
- spec->multiout.dac_nids[HDA_FRONT], 0,
- &spec->hp_path);
-
- if (!parse_output_path(codec, pin, spec->multiout.dac_nids[HDA_FRONT],
- 1, &spec->hp_mix_path) && !spec->hp_path.depth)
- return 0;
-
- if (spec->hp_path.depth) {
- path = &spec->hp_path;
- check_dac = true;
- } else {
- path = &spec->hp_mix_path;
- check_dac = false;
- }
- err = create_ch_ctls(codec, "Headphone", 3, check_dac, path);
- if (err < 0)
- return err;
- if (check_dac)
- copy_path_mixer_ctls(&spec->hp_mix_path, path);
- else
- copy_path_mixer_ctls(&spec->hp_path, path);
- if (spec->hp_indep_path.depth)
- copy_path_mixer_ctls(&spec->hp_indep_path, path);
- return 0;
-}
-
-static int via_auto_create_speaker_ctls(struct hda_codec *codec)
-{
- struct via_spec *spec = codec->spec;
- struct nid_path *path;
- bool check_dac;
- hda_nid_t pin, dac = 0;
- int err;
-
- pin = spec->autocfg.speaker_pins[0];
- if (!spec->autocfg.speaker_outs || !pin)
- return 0;
-
- if (parse_output_path(codec, pin, 0, 0, &spec->speaker_path))
- dac = spec->speaker_path.path[0];
- if (!dac)
- parse_output_path(codec, pin,
- spec->multiout.dac_nids[HDA_FRONT], 0,
- &spec->speaker_path);
- if (!parse_output_path(codec, pin, spec->multiout.dac_nids[HDA_FRONT],
- 1, &spec->speaker_mix_path) && !dac)
- return 0;
-
- /* no AA-path for front? */
- if (!spec->out_mix_path.depth && spec->speaker_mix_path.depth)
- dac = 0;
-
- spec->speaker_dac_nid = dac;
- spec->multiout.extra_out_nid[0] = dac;
- if (dac) {
- path = &spec->speaker_path;
- check_dac = true;
- } else {
- path = &spec->speaker_mix_path;
- check_dac = false;
- }
- err = create_ch_ctls(codec, "Speaker", 3, check_dac, path);
- if (err < 0)
- return err;
- if (check_dac)
- copy_path_mixer_ctls(&spec->speaker_mix_path, path);
- else
- copy_path_mixer_ctls(&spec->speaker_path, path);
- return 0;
-}
-
-#define via_aamix_ctl_info via_pin_power_ctl_info
-
-static int via_aamix_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;
- ucontrol->value.enumerated.item[0] = spec->aamix_mode;
- return 0;
-}
-
-static void update_aamix_paths(struct hda_codec *codec, int do_mix,
- struct nid_path *nomix, struct nid_path *mix)
-{
- if (do_mix) {
- activate_output_path(codec, nomix, false, false);
- activate_output_path(codec, mix, true, false);
- } else {
- activate_output_path(codec, mix, false, false);
- activate_output_path(codec, nomix, true, false);
- }
-}
-
-static int via_aamix_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 val = ucontrol->value.enumerated.item[0];
-
- if (val == spec->aamix_mode)
- return 0;
- spec->aamix_mode = val;
- /* update front path */
- update_aamix_paths(codec, val, &spec->out_path[0], &spec->out_mix_path);
- /* update HP path */
- if (!spec->hp_independent_mode) {
- update_aamix_paths(codec, val, &spec->hp_path,
- &spec->hp_mix_path);
- }
- /* update speaker path */
- update_aamix_paths(codec, val, &spec->speaker_path,
- &spec->speaker_mix_path);
- return 1;
-}
-
-static const struct snd_kcontrol_new via_aamix_ctl_enum = {
- .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
- .name = "Loopback Mixing",
- .info = via_aamix_ctl_info,
- .get = via_aamix_ctl_get,
- .put = via_aamix_ctl_put,
-};
-
-static int via_auto_create_loopback_switch(struct hda_codec *codec)
-{
- struct via_spec *spec = codec->spec;
-
- if (!spec->aa_mix_nid)
- return 0; /* no loopback switching available */
- if (!(spec->out_mix_path.depth || spec->hp_mix_path.depth ||
- spec->speaker_path.depth))
- return 0; /* no loopback switching available */
- if (!via_clone_control(spec, &via_aamix_ctl_enum))
- return -ENOMEM;
- return 0;
-}
-
-/* look for ADCs */
-static int via_fill_adcs(struct hda_codec *codec)
-{
- struct via_spec *spec = codec->spec;
- 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);
- if (get_wcaps_type(wcaps) != AC_WID_AUD_IN)
- continue;
- if (wcaps & AC_WCAP_DIGITAL)
- continue;
- if (!(wcaps & AC_WCAP_CONN_LIST))
- continue;
- if (spec->num_adc_nids >= ARRAY_SIZE(spec->adc_nids))
- return -ENOMEM;
- spec->adc_nids[spec->num_adc_nids++] = nid;
- }
- return 0;
-}
-
-/* input-src control */
-static int via_mux_enum_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;
-
- 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;
- strcpy(uinfo->value.enumerated.name,
- spec->inputs[uinfo->value.enumerated.item].label);
- return 0;
-}
-
-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 idx = snd_ctl_get_ioffidx(kcontrol, &ucontrol->id);
-
- ucontrol->value.enumerated.item[0] = spec->cur_mux[idx];
- return 0;
-}
-
-static int via_mux_enum_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 idx = snd_ctl_get_ioffidx(kcontrol, &ucontrol->id);
- hda_nid_t mux;
- int cur;
-
- cur = ucontrol->value.enumerated.item[0];
- if (cur < 0 || cur >= spec->num_inputs)
- return -EINVAL;
- if (spec->cur_mux[idx] == cur)
- return 0;
- spec->cur_mux[idx] = cur;
- if (spec->dyn_adc_switch) {
- int adc_idx = spec->inputs[cur].adc_idx;
- mux = spec->mux_nids[adc_idx];
- via_dyn_adc_pcm_resetup(codec, cur);
- } else {
- mux = spec->mux_nids[idx];
- if (snd_BUG_ON(!mux))
- return -EINVAL;
- }
-
- if (mux) {
- /* switch to D0 beofre change index */
- update_power_state(codec, mux, AC_PWRST_D0);
- snd_hda_codec_write(codec, mux, 0,
- AC_VERB_SET_CONNECT_SEL,
- spec->inputs[cur].mux_idx);
- }
-
- /* update jack power state */
- set_widgets_power_state(codec);
- return 0;
-}
-
-static const struct snd_kcontrol_new via_input_src_ctl = {
- .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
- /* The multiple "Capture Source" controls confuse alsamixer
- * So call somewhat different..
- */
- /* .name = "Capture Source", */
- .name = "Input Source",
- .info = via_mux_enum_info,
- .get = via_mux_enum_get,
- .put = via_mux_enum_put,
+static const struct hda_verb vt1708_init_verbs[] = {
+ /* power down jack detect function */
+ {0x1, 0xf81, 0x1},
+ { }
};
-
-static int create_input_src_ctls(struct hda_codec *codec, int count)
-{
- struct via_spec *spec = codec->spec;
- struct snd_kcontrol_new *knew;
-
- if (spec->num_inputs <= 1 || !count)
- return 0; /* no need for single src */
-
- knew = via_clone_control(spec, &via_input_src_ctl);
- if (!knew)
- return -ENOMEM;
- knew->count = count;
- return 0;
-}
-
-/* add the powersave loopback-list entry */
-static void add_loopback_list(struct via_spec *spec, hda_nid_t mix, int idx)
-{
- struct hda_amp_list *list;
-
- if (spec->num_loopbacks >= ARRAY_SIZE(spec->loopback_list) - 1)
- return;
- list = spec->loopback_list + spec->num_loopbacks;
- list->nid = mix;
- list->dir = HDA_INPUT;
- list->idx = idx;
- spec->num_loopbacks++;
- spec->loopback.amplist = spec->loopback_list;
-}
-
-static bool is_reachable_nid(struct hda_codec *codec, hda_nid_t src,
- hda_nid_t dst)
-{
- return snd_hda_get_conn_index(codec, src, dst, 1) >= 0;
-}
-
-/* add the input-route to the given pin */
-static bool add_input_route(struct hda_codec *codec, hda_nid_t pin)
-{
- struct via_spec *spec = codec->spec;
- int c, idx;
-
- spec->inputs[spec->num_inputs].adc_idx = -1;
- spec->inputs[spec->num_inputs].pin = pin;
- for (c = 0; c < spec->num_adc_nids; c++) {
- if (spec->mux_nids[c]) {
- idx = get_connection_index(codec, spec->mux_nids[c],
- pin);
- if (idx < 0)
- continue;
- spec->inputs[spec->num_inputs].mux_idx = idx;
- } else {
- if (!is_reachable_nid(codec, spec->adc_nids[c], pin))
- continue;
- }
- spec->inputs[spec->num_inputs].adc_idx = c;
- /* Can primary ADC satisfy all inputs? */
- if (!spec->dyn_adc_switch &&
- spec->num_inputs > 0 && spec->inputs[0].adc_idx != c) {
- snd_printd(KERN_INFO
- "via: dynamic ADC switching enabled\n");
- spec->dyn_adc_switch = 1;
- }
- return true;
- }
- return false;
-}
-
-static int get_mux_nids(struct hda_codec *codec);
-
-/* parse input-routes; fill ADCs, MUXs and input-src entries */
-static int parse_analog_inputs(struct hda_codec *codec)
-{
- struct via_spec *spec = codec->spec;
- const struct auto_pin_cfg *cfg = &spec->autocfg;
- int i, err;
-
- err = via_fill_adcs(codec);
- if (err < 0)
- return err;
- err = get_mux_nids(codec);
- if (err < 0)
- return err;
-
- /* fill all input-routes */
- for (i = 0; i < cfg->num_inputs; i++) {
- if (add_input_route(codec, cfg->inputs[i].pin))
- spec->inputs[spec->num_inputs++].label =
- hda_get_autocfg_input_label(codec, cfg, i);
- }
-
- /* check for internal loopback recording */
- if (spec->aa_mix_nid &&
- add_input_route(codec, spec->aa_mix_nid))
- spec->inputs[spec->num_inputs++].label = "Stereo Mixer";
-
- return 0;
-}
-
-/* create analog-loopback volume/switch controls */
-static int create_loopback_ctls(struct hda_codec *codec)
-{
- struct via_spec *spec = codec->spec;
- const struct auto_pin_cfg *cfg = &spec->autocfg;
- const char *prev_label = NULL;
- int type_idx = 0;
- int i, j, err, idx;
-
- if (!spec->aa_mix_nid)
- return 0;
-
- for (i = 0; i < cfg->num_inputs; i++) {
- hda_nid_t pin = cfg->inputs[i].pin;
- const char *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;
- idx = get_connection_index(codec, spec->aa_mix_nid, pin);
- if (idx >= 0) {
- err = via_new_analog_input(spec, label, type_idx,
- idx, spec->aa_mix_nid);
- if (err < 0)
- return err;
- add_loopback_list(spec, spec->aa_mix_nid, idx);
- }
-
- /* remember the label for smart51 control */
- for (j = 0; j < spec->smart51_nums; j++) {
- if (spec->smart51_pins[j] == pin) {
- spec->smart51_idxs[j] = idx;
- spec->smart51_labels[j] = label;
- break;
- }
- }
- }
- return 0;
-}
-
-/* create mic-boost controls (if present) */
-static int create_mic_boost_ctls(struct hda_codec *codec)
-{
- struct via_spec *spec = codec->spec;
- const struct auto_pin_cfg *cfg = &spec->autocfg;
- int i, err;
-
- for (i = 0; i < cfg->num_inputs; i++) {
- hda_nid_t pin = cfg->inputs[i].pin;
- unsigned int caps;
- const char *label;
- char name[32];
-
- if (cfg->inputs[i].type != AUTO_PIN_MIC)
- continue;
- caps = query_amp_caps(codec, pin, HDA_INPUT);
- if (caps == -1 || !(caps & AC_AMPCAP_NUM_STEPS))
- continue;
- label = hda_get_autocfg_input_label(codec, cfg, i);
- snprintf(name, sizeof(name), "%s Boost Volume", label);
- err = via_add_control(spec, VIA_CTL_WIDGET_VOL, name,
- HDA_COMPOSE_AMP_VAL(pin, 3, 0, HDA_INPUT));
- if (err < 0)
- return err;
- }
- return 0;
-}
-
-/* create capture and input-src controls for multiple streams */
-static int create_multi_adc_ctls(struct hda_codec *codec)
-{
- struct via_spec *spec = codec->spec;
- int i, err;
-
- /* create capture mixer elements */
- for (i = 0; i < spec->num_adc_nids; i++) {
- hda_nid_t adc = spec->adc_nids[i];
- err = __via_add_control(spec, VIA_CTL_WIDGET_VOL,
- "Capture Volume", i,
- HDA_COMPOSE_AMP_VAL(adc, 3, 0,
- HDA_INPUT));
- if (err < 0)
- return err;
- err = __via_add_control(spec, VIA_CTL_WIDGET_MUTE,
- "Capture Switch", i,
- HDA_COMPOSE_AMP_VAL(adc, 3, 0,
- HDA_INPUT));
- if (err < 0)
- return err;
- }
-
- /* input-source control */
- for (i = 0; i < spec->num_adc_nids; i++)
- if (!spec->mux_nids[i])
- break;
- err = create_input_src_ctls(codec, i);
- if (err < 0)
- return err;
- return 0;
-}
-
-/* bind capture volume/switch */
-static struct snd_kcontrol_new via_bind_cap_vol_ctl =
- HDA_BIND_VOL("Capture Volume", 0);
-static struct snd_kcontrol_new via_bind_cap_sw_ctl =
- HDA_BIND_SW("Capture Switch", 0);
-
-static int init_bind_ctl(struct via_spec *spec, struct hda_bind_ctls **ctl_ret,
- struct hda_ctl_ops *ops)
-{
- struct hda_bind_ctls *ctl;
- int i;
-
- ctl = kzalloc(sizeof(*ctl) + sizeof(long) * 4, GFP_KERNEL);
- if (!ctl)
- return -ENOMEM;
- ctl->ops = ops;
- for (i = 0; i < spec->num_adc_nids; i++)
- ctl->values[i] =
- HDA_COMPOSE_AMP_VAL(spec->adc_nids[i], 3, 0, HDA_INPUT);
- *ctl_ret = ctl;
- return 0;
-}
-
-/* create capture and input-src controls for dynamic ADC-switch case */
-static int create_dyn_adc_ctls(struct hda_codec *codec)
-{
- struct via_spec *spec = codec->spec;
- struct snd_kcontrol_new *knew;
- int err;
-
- /* set up the bind capture ctls */
- err = init_bind_ctl(spec, &spec->bind_cap_vol, &snd_hda_bind_vol);
- if (err < 0)
- return err;
- err = init_bind_ctl(spec, &spec->bind_cap_sw, &snd_hda_bind_sw);
- if (err < 0)
- return err;
-
- /* create capture mixer elements */
- knew = via_clone_control(spec, &via_bind_cap_vol_ctl);
- if (!knew)
- return -ENOMEM;
- knew->private_value = (long)spec->bind_cap_vol;
-
- knew = via_clone_control(spec, &via_bind_cap_sw_ctl);
- if (!knew)
- return -ENOMEM;
- knew->private_value = (long)spec->bind_cap_sw;
-
- /* input-source control */
- err = create_input_src_ctls(codec, 1);
- if (err < 0)
- return err;
- return 0;
-}
-
-/* parse and create capture-related stuff */
-static int via_auto_create_analog_input_ctls(struct hda_codec *codec)
-{
- struct via_spec *spec = codec->spec;
- int err;
-
- err = parse_analog_inputs(codec);
- if (err < 0)
- return err;
- if (spec->dyn_adc_switch)
- err = create_dyn_adc_ctls(codec);
- else
- err = create_multi_adc_ctls(codec);
- if (err < 0)
- return err;
- err = create_loopback_ctls(codec);
- if (err < 0)
- return err;
- err = create_mic_boost_ctls(codec);
- if (err < 0)
- return err;
- return 0;
-}
-
static void vt1708_set_pinconfig_connect(struct hda_codec *codec, hda_nid_t nid)
{
unsigned int def_conf;
@@ -2666,129 +559,108 @@ static int vt1708_jack_detect_put(struct snd_kcontrol *kcontrol,
if (spec->vt1708_jack_detect == val)
return 0;
spec->vt1708_jack_detect = val;
- if (spec->vt1708_jack_detect &&
- snd_hda_get_bool_hint(codec, "analog_loopback_hp_detect") != 1) {
- mute_aa_path(codec, 1);
- notify_aa_path_ctls(codec);
- }
- via_hp_automute(codec);
- vt1708_update_hp_work(spec);
+ vt1708_update_hp_work(codec);
return 1;
}
-static const struct snd_kcontrol_new vt1708_jack_detect_ctl = {
+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_detect_get,
.put = vt1708_jack_detect_put,
+ },
+ {} /* terminator */
};
-static void fill_dig_outs(struct hda_codec *codec);
-static void fill_dig_in(struct hda_codec *codec);
-
-static int via_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;
-
- err = snd_hda_parse_pin_def_config(codec, &spec->autocfg, NULL);
- if (err < 0)
- return err;
- if (!spec->autocfg.line_outs && !spec->autocfg.hp_pins[0])
- return -EINVAL;
-
- err = via_auto_create_multi_out_ctls(codec);
- if (err < 0)
- return err;
- err = via_auto_create_hp_ctls(codec, spec->autocfg.hp_pins[0]);
- if (err < 0)
- return err;
- err = via_auto_create_speaker_ctls(codec);
- if (err < 0)
- return err;
- err = via_auto_create_loopback_switch(codec);
- if (err < 0)
- return err;
- err = via_auto_create_analog_input_ctls(codec);
- if (err < 0)
- return err;
-
- spec->multiout.max_channels = spec->multiout.num_dacs * 2;
-
- fill_dig_outs(codec);
- fill_dig_in(codec);
-
- if (spec->kctls.list)
- spec->mixers[spec->num_mixers++] = spec->kctls.list;
-
-
- if (spec->hp_dac_nid && spec->hp_mix_path.depth) {
- err = via_hp_build(codec);
- if (err < 0)
- return err;
- }
-
- err = via_smart51_build(codec);
- if (err < 0)
- return err;
-
- /* assign slave outs */
- if (spec->slave_dig_outs[0])
- codec->slave_dig_outs = spec->slave_dig_outs;
-
- return 1;
+ set_widgets_power_state(codec);
+ snd_hda_gen_hp_automute(codec, tbl);
}
-static void via_auto_init_dig_outs(struct hda_codec *codec)
+static void via_line_automute(struct hda_codec *codec, struct hda_jack_tbl *tbl)
{
- struct via_spec *spec = codec->spec;
- if (spec->multiout.dig_out_nid)
- init_output_pin(codec, spec->autocfg.dig_out_pins[0], PIN_OUT);
- if (spec->slave_dig_outs[0])
- init_output_pin(codec, spec->autocfg.dig_out_pins[1], PIN_OUT);
+ set_widgets_power_state(codec);
+ snd_hda_gen_line_automute(codec, tbl);
}
-static void via_auto_init_dig_in(struct hda_codec *codec)
+static void via_jack_powerstate_event(struct hda_codec *codec, struct hda_jack_tbl *tbl)
{
- struct via_spec *spec = codec->spec;
- if (!spec->dig_in_nid)
- return;
- snd_hda_codec_write(codec, spec->autocfg.dig_in_pin, 0,
- AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN);
+ set_widgets_power_state(codec);
}
-/* initialize the unsolicited events */
-static void via_auto_init_unsol_event(struct hda_codec *codec)
+#define VIA_JACK_EVENT (HDA_GEN_LAST_EVENT + 1)
+
+static void via_set_jack_unsol_events(struct hda_codec *codec)
{
struct via_spec *spec = codec->spec;
- struct auto_pin_cfg *cfg = &spec->autocfg;
- unsigned int ev;
+ struct auto_pin_cfg *cfg = &spec->gen.autocfg;
+ hda_nid_t pin;
int i;
- if (cfg->hp_pins[0] && is_jack_detectable(codec, cfg->hp_pins[0]))
- snd_hda_jack_detect_enable(codec, cfg->hp_pins[0],
- VIA_HP_EVENT | VIA_JACK_EVENT);
-
+ spec->gen.hp_automute_hook = via_hp_automute;
if (cfg->speaker_pins[0])
- ev = VIA_LINE_EVENT;
- else
- ev = 0;
+ spec->gen.line_automute_hook = via_line_automute;
+
for (i = 0; i < cfg->line_outs; i++) {
- if (cfg->line_out_pins[i] &&
- is_jack_detectable(codec, cfg->line_out_pins[i]))
- snd_hda_jack_detect_enable(codec, cfg->line_out_pins[i],
- ev | VIA_JACK_EVENT);
+ 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);
}
for (i = 0; i < cfg->num_inputs; i++) {
- if (is_jack_detectable(codec, cfg->inputs[i].pin))
- snd_hda_jack_detect_enable(codec, cfg->inputs[i].pin,
- VIA_JACK_EVENT);
+ 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);
}
}
+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 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 int via_parse_auto_config(struct hda_codec *codec)
+{
+ struct via_spec *spec = codec->spec;
+ int err;
+
+ spec->gen.main_out_badness = &via_main_out_badness;
+ spec->gen.extra_out_badness = &via_extra_out_badness;
+
+ err = snd_hda_parse_pin_defcfg(codec, &spec->gen.autocfg, NULL, 0);
+ 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 via_init(struct hda_codec *codec)
{
struct via_spec *spec = codec->spec;
@@ -2801,64 +673,47 @@ static int via_init(struct hda_codec *codec)
set_widgets_power_state(codec);
__analog_low_current_mode(codec, true);
- via_auto_init_multi_out(codec);
- via_auto_init_hp_out(codec);
- via_auto_init_speaker_out(codec);
- via_auto_init_analog_input(codec);
- via_auto_init_dig_outs(codec);
- via_auto_init_dig_in(codec);
-
- via_auto_init_unsol_event(codec);
+ snd_hda_gen_init(codec);
- via_hp_automute(codec);
- vt1708_update_hp_work(spec);
- snd_hda_jack_report_sync(codec);
+ vt1708_update_hp_work(codec);
return 0;
}
-static void vt1708_update_hp_jack_state(struct work_struct *work)
+static int vt1708_build_controls(struct hda_codec *codec)
{
- struct via_spec *spec = container_of(work, struct via_spec,
- vt1708_hp_work.work);
- if (spec->codec_type != VT1708)
- return;
- snd_hda_jack_set_dirty_all(spec->codec);
- /* 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);
- }
- if (spec->vt1708_jack_detect)
- schedule_delayed_work(&spec->vt1708_hp_work,
- msecs_to_jiffies(100));
+ /* 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 get_mux_nids(struct hda_codec *codec)
+static int vt1708_build_pcms(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];
- }
+ int i, err;
+
+ err = snd_hda_gen_build_pcms(codec);
+ if (err < 0 || codec->vendor_id != 0x11061708)
+ return err;
+
+ /* 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;
}
+
return 0;
}
@@ -2872,7 +727,17 @@ static int patch_vt1708(struct hda_codec *codec)
if (spec == NULL)
return -ENOMEM;
- spec->aa_mix_nid = 0x17;
+ 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);
@@ -2886,18 +751,17 @@ static int patch_vt1708(struct hda_codec *codec)
}
/* add jack detect on/off control */
- if (!via_clone_control(spec, &vt1708_jack_detect_ctl))
- return -ENOMEM;
-
- /* disable 32bit format on VT1708 */
- if (codec->vendor_id == 0x11061708)
- spec->stream_analog_playback = &vt1708_pcm_analog_s16_playback;
+ spec->mixers[spec->num_mixers++] = vt1708_jack_detect_ctl;
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;
+
+ /* clear jackpoll_interval again; it's set dynamically */
+ codec->jackpoll_interval = 0;
- INIT_DELAYED_WORK(&spec->vt1708_hp_work, vt1708_update_hp_jack_state);
return 0;
}
@@ -2911,7 +775,7 @@ static int patch_vt1709(struct hda_codec *codec)
if (spec == NULL)
return -ENOMEM;
- spec->aa_mix_nid = 0x18;
+ spec->gen.mixer_nid = 0x18;
err = via_parse_auto_config(codec);
if (err < 0) {
@@ -2955,7 +819,7 @@ static void set_widgets_power_state_vt1708B(struct hda_codec *codec)
/* PW0 (19h), SW1 (18h), AOW1 (11h) */
parm = AC_PWRST_D3;
set_pin_power_state(codec, 0x19, &parm);
- if (spec->smart51_enabled)
+ if (smart51_enabled(codec))
set_pin_power_state(codec, 0x1b, &parm);
update_power_state(codec, 0x18, parm);
update_power_state(codec, 0x11, parm);
@@ -2964,7 +828,7 @@ static void set_widgets_power_state_vt1708B(struct hda_codec *codec)
if (is_8ch) {
parm = AC_PWRST_D3;
set_pin_power_state(codec, 0x22, &parm);
- if (spec->smart51_enabled)
+ if (smart51_enabled(codec))
set_pin_power_state(codec, 0x1a, &parm);
update_power_state(codec, 0x26, parm);
update_power_state(codec, 0x24, parm);
@@ -2972,7 +836,7 @@ static void set_widgets_power_state_vt1708B(struct hda_codec *codec)
/* PW7(23h), SW2(27h), AOW2(25h) */
parm = AC_PWRST_D3;
set_pin_power_state(codec, 0x23, &parm);
- if (spec->smart51_enabled)
+ if (smart51_enabled(codec))
set_pin_power_state(codec, 0x1a, &parm);
update_power_state(codec, 0x27, parm);
update_power_state(codec, 0x25, parm);
@@ -2992,7 +856,7 @@ static void set_widgets_power_state_vt1708B(struct hda_codec *codec)
if (is_8ch) {
update_power_state(codec, 0x25, parm);
update_power_state(codec, 0x27, parm);
- } else if (codec->vendor_id == 0x11064397 && spec->hp_independent_mode)
+ } else if (codec->vendor_id == 0x11064397 && spec->gen.indep_hp_enabled)
update_power_state(codec, 0x25, parm);
}
@@ -3010,7 +874,7 @@ static int patch_vt1708B(struct hda_codec *codec)
if (spec == NULL)
return -ENOMEM;
- spec->aa_mix_nid = 0x16;
+ spec->gen.mixer_nid = 0x16;
/* automatic parse from the BIOS config */
err = via_parse_auto_config(codec);
@@ -3035,61 +899,11 @@ static const struct hda_verb vt1708S_init_verbs[] = {
{ }
};
-/* 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 void fill_dig_in(struct hda_codec *codec)
-{
- struct via_spec *spec = codec->spec;
- hda_nid_t dig_nid;
- int i, err;
-
- if (!spec->autocfg.dig_in_pin)
- return;
-
- 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;
- }
- }
-}
-
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) |
@@ -3107,21 +921,10 @@ static int patch_vt1708S(struct hda_codec *codec)
if (spec == NULL)
return -ENOMEM;
- spec->aa_mix_nid = 0x16;
+ 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 = via_parse_auto_config(codec);
- if (err < 0) {
- via_free(codec);
- return err;
- }
-
- spec->init_verbs[spec->num_iverbs++] = vt1708S_init_verbs;
-
- codec->patch_ops = via_patch_ops;
-
/* correct names for VT1708BCE */
if (get_codec_type(codec) == VT1708BCE) {
kfree(codec->chip_name);
@@ -3138,6 +941,18 @@ static int patch_vt1708S(struct hda_codec *codec)
sizeof(codec->bus->card->mixername),
"%s %s", codec->vendor_name, codec->chip_name);
}
+
+ /* automatic parse from the BIOS config */
+ err = via_parse_auto_config(codec);
+ if (err < 0) {
+ via_free(codec);
+ return err;
+ }
+
+ spec->init_verbs[spec->num_iverbs++] = vt1708S_init_verbs;
+
+ codec->patch_ops = via_patch_ops;
+
spec->set_widgets_power_state = set_widgets_power_state_vt1708B;
return 0;
}
@@ -3192,7 +1007,7 @@ static int patch_vt1702(struct hda_codec *codec)
if (spec == NULL)
return -ENOMEM;
- spec->aa_mix_nid = 0x1a;
+ spec->gen.mixer_nid = 0x1a;
/* limit AA path volume to 0 dB */
snd_hda_override_amp_caps(codec, 0x1A, HDA_INPUT,
@@ -3231,7 +1046,7 @@ static void set_widgets_power_state_vt1718S(struct hda_codec *codec)
{
struct via_spec *spec = codec->spec;
int imux_is_smixer;
- unsigned int parm;
+ 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;
@@ -3254,32 +1069,35 @@ static void set_widgets_power_state_vt1718S(struct hda_codec *codec)
parm = AC_PWRST_D3;
set_pin_power_state(codec, 0x27, &parm);
update_power_state(codec, 0x1a, parm);
- update_power_state(codec, 0xb, parm);
+ parm2 = parm; /* for pin 0x0b */
/* PW2 (26h), AOW2 (ah) */
parm = AC_PWRST_D3;
set_pin_power_state(codec, 0x26, &parm);
- if (spec->smart51_enabled)
+ if (smart51_enabled(codec))
set_pin_power_state(codec, 0x2b, &parm);
update_power_state(codec, 0xa, parm);
/* PW0 (24h), AOW0 (8h) */
parm = AC_PWRST_D3;
set_pin_power_state(codec, 0x24, &parm);
- if (!spec->hp_independent_mode) /* check for redirected HP */
+ 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 (spec->smart51_enabled)
+ if (smart51_enabled(codec))
set_pin_power_state(codec, 0x2a, &parm);
update_power_state(codec, 0x9, parm);
- if (spec->hp_independent_mode) {
+ 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);
@@ -3299,9 +1117,9 @@ static int add_secret_dac_path(struct hda_codec *codec)
hda_nid_t conn[8];
hda_nid_t nid;
- if (!spec->aa_mix_nid)
+ if (!spec->gen.mixer_nid)
return 0;
- nums = snd_hda_get_connections(codec, spec->aa_mix_nid, conn,
+ 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)
@@ -3316,7 +1134,7 @@ static int add_secret_dac_path(struct hda_codec *codec)
!(caps & AC_WCAP_DIGITAL)) {
conn[nums++] = nid;
return snd_hda_override_conn_list(codec,
- spec->aa_mix_nid,
+ spec->gen.mixer_nid,
nums, conn);
}
}
@@ -3334,7 +1152,7 @@ static int patch_vt1718S(struct hda_codec *codec)
if (spec == NULL)
return -ENOMEM;
- spec->aa_mix_nid = 0x21;
+ 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);
@@ -3465,7 +1283,7 @@ static void set_widgets_power_state_vt1716S(struct hda_codec *codec)
parm = AC_PWRST_D3;
set_pin_power_state(codec, 0x19, &parm);
/* Smart 5.1 PW2(1bh) */
- if (spec->smart51_enabled)
+ if (smart51_enabled(codec))
set_pin_power_state(codec, 0x1b, &parm);
update_power_state(codec, 0x18, parm);
update_power_state(codec, 0x11, parm);
@@ -3474,12 +1292,12 @@ static void set_widgets_power_state_vt1716S(struct hda_codec *codec)
parm = AC_PWRST_D3;
set_pin_power_state(codec, 0x23, &parm);
/* Smart 5.1 PW1(1ah) */
- if (spec->smart51_enabled)
+ if (smart51_enabled(codec))
set_pin_power_state(codec, 0x1a, &parm);
update_power_state(codec, 0x27, parm);
/* Smart 5.1 PW5(1eh) */
- if (spec->smart51_enabled)
+ if (smart51_enabled(codec))
set_pin_power_state(codec, 0x1e, &parm);
update_power_state(codec, 0x25, parm);
@@ -3491,7 +1309,7 @@ static void set_widgets_power_state_vt1716S(struct hda_codec *codec)
mono_out = 0;
else {
present = snd_hda_jack_detect(codec, 0x1d);
- if (!spec->hp_independent_mode && present)
+ if (!spec->gen.indep_hp_enabled && present)
mono_out = 0;
else
mono_out = 1;
@@ -3506,7 +1324,7 @@ static void set_widgets_power_state_vt1716S(struct hda_codec *codec)
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)
+ if (spec->gen.indep_hp_enabled)
update_power_state(codec, 0x25, parm);
/* force to D0 for internal Speaker */
@@ -3525,7 +1343,7 @@ static int patch_vt1716S(struct hda_codec *codec)
if (spec == NULL)
return -ENOMEM;
- spec->aa_mix_nid = 0x16;
+ spec->gen.mixer_nid = 0x16;
override_mic_boost(codec, 0x1a, 0, 3, 40);
override_mic_boost(codec, 0x1e, 0, 3, 40);
@@ -3538,9 +1356,7 @@ static int patch_vt1716S(struct hda_codec *codec)
spec->init_verbs[spec->num_iverbs++] = vt1716S_init_verbs;
- spec->mixers[spec->num_mixers] = vt1716s_dmic_mixer;
- spec->num_mixers++;
-
+ spec->mixers[spec->num_mixers++] = vt1716s_dmic_mixer;
spec->mixers[spec->num_mixers++] = vt1716S_mono_out_mixer;
codec->patch_ops = via_patch_ops;
@@ -3625,7 +1441,7 @@ static void set_widgets_power_state_vt2002P(struct hda_codec *codec)
update_power_state(codec, 0x35, parm);
}
- if (spec->hp_independent_mode)
+ if (spec->gen.indep_hp_enabled)
update_power_state(codec, 0x9, AC_PWRST_D0);
/* Class-D */
@@ -3663,6 +1479,55 @@ static void set_widgets_power_state_vt2002P(struct hda_codec *codec)
update_power_state(codec, 0x21, AC_PWRST_D3);
}
+/*
+ * pin fix-up
+ */
+enum {
+ VIA_FIXUP_INTMIC_BOOST,
+ VIA_FIXUP_ASUS_G75,
+};
+
+static void via_fixup_intmic_boost(struct hda_codec *codec,
+ const struct hda_fixup *fix, int action)
+{
+ if (action == HDA_FIXUP_ACT_PRE_PROBE)
+ override_mic_boost(codec, 0x30, 0, 2, 40);
+}
+
+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 */
+ { }
+ }
+ },
+};
+
+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),
+ {}
+};
+
+/* 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 };
+
+ 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);
+}
+
/* patch for vt2002P */
static int patch_vt2002P(struct hda_codec *codec)
{
@@ -3674,11 +1539,16 @@ static int patch_vt2002P(struct hda_codec *codec)
if (spec == NULL)
return -ENOMEM;
- spec->aa_mix_nid = 0x21;
+ 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 = via_parse_auto_config(codec);
if (err < 0) {
@@ -3740,7 +1610,7 @@ static void set_widgets_power_state_vt1812(struct hda_codec *codec)
set_pin_power_state(codec, 0x25, &parm);
update_power_state(codec, 0x15, parm);
update_power_state(codec, 0x35, parm);
- if (spec->hp_independent_mode)
+ if (spec->gen.indep_hp_enabled)
update_power_state(codec, 0x9, AC_PWRST_D0);
/* Internal Speaker */
@@ -3793,7 +1663,7 @@ static int patch_vt1812(struct hda_codec *codec)
if (spec == NULL)
return -ENOMEM;
- spec->aa_mix_nid = 0x21;
+ 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);
@@ -3813,6 +1683,125 @@ static int patch_vt1812(struct hda_codec *codec)
return 0;
}
+/* patch for vt3476 */
+
+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 void set_widgets_power_state_vt3476(struct hda_codec *codec)
+{
+ struct via_spec *spec = codec->spec;
+ 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);
+ }
+
+ /* 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);
+
+ /* 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);
+
+ /* 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 */
+
+ /* 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);
+}
+
+static int patch_vt3476(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 = 0x3f;
+ add_secret_dac_path(codec);
+
+ /* automatic parse from the BIOS config */
+ err = via_parse_auto_config(codec);
+ if (err < 0) {
+ via_free(codec);
+ return err;
+ }
+
+ spec->init_verbs[spec->num_iverbs++] = vt3476_init_verbs;
+
+ codec->patch_ops = via_patch_ops;
+
+ spec->set_widgets_power_state = set_widgets_power_state_vt3476;
+
+ return 0;
+}
+
/*
* patch entries
*/
@@ -3906,6 +1895,12 @@ static const struct hda_codec_preset snd_hda_preset_via[] = {
.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 */