diff options
Diffstat (limited to 'sound/pci/hda')
37 files changed, 26188 insertions, 25076 deletions
diff --git a/sound/pci/hda/Kconfig b/sound/pci/hda/Kconfig index 6eeb8897624..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" @@ -38,7 +63,6 @@ config SND_HDA_HWDEP config SND_HDA_RECONFIG bool "Allow dynamic codec reconfiguration" - depends on SND_HDA_HWDEP 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,7 +71,7 @@ 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. @@ -73,7 +97,6 @@ config SND_HDA_INPUT_JACK config SND_HDA_PATCH_LOADER bool "Support initialization patch loading for HD-audio" 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 @@ -81,152 +104,140 @@ config SND_HDA_PATCH_LOADER start up. The "patch" file can be specified via patch module option, such as patch=hda-init. - This option turns on hwdep and reconfig features automatically. - config SND_HDA_CODEC_REALTEK - bool "Build Realtek HD-audio codec support" - default y + tristate "Build Realtek HD-audio codec support" + select SND_HDA_GENERIC help - Say Y here to include Realtek HD-audio codec support in + Say Y or M here to include Realtek HD-audio codec support in snd-hda-intel driver, such as ALC880. - When the HD-audio driver is built as a module, the codec - support code is also built as another module, - snd-hda-codec-realtek. - This module is automatically loaded at probing. +comment "Set to Y if you want auto-loading the codec driver" + depends on SND_HDA=y && SND_HDA_CODEC_REALTEK=m config SND_HDA_CODEC_ANALOG - bool "Build Analog Device HD-audio codec support" - default y + tristate "Build Analog Device HD-audio codec support" + select SND_HDA_GENERIC help - Say Y here to include Analog Device HD-audio codec support in + Say Y or M here to include Analog Device HD-audio codec support in snd-hda-intel driver, such as AD1986A. - When the HD-audio driver is built as a module, the codec - support code is also built as another module, - snd-hda-codec-analog. - This module is automatically loaded at probing. +comment "Set to Y if you want auto-loading the codec driver" + depends on SND_HDA=y && SND_HDA_CODEC_ANALOG=m config SND_HDA_CODEC_SIGMATEL - bool "Build IDT/Sigmatel HD-audio codec support" - default y + tristate "Build IDT/Sigmatel HD-audio codec support" + select SND_HDA_GENERIC help - Say Y here to include IDT (Sigmatel) HD-audio codec support in + Say Y or M here to include IDT (Sigmatel) HD-audio codec support in snd-hda-intel driver, such as STAC9200. - When the HD-audio driver is built as a module, the codec - support code is also built as another module, - snd-hda-codec-idt. - This module is automatically loaded at probing. +comment "Set to Y if you want auto-loading the codec driver" + depends on SND_HDA=y && SND_HDA_CODEC_SIGMATEL=m config SND_HDA_CODEC_VIA - bool "Build VIA HD-audio codec support" - default y + tristate "Build VIA HD-audio codec support" + select SND_HDA_GENERIC help - Say Y here to include VIA HD-audio codec support in + Say Y or M here to include VIA HD-audio codec support in snd-hda-intel driver, such as VT1708. - When the HD-audio driver is built as a module, the codec - support code is also built as another module, - snd-hda-codec-via. - This module is automatically loaded at probing. +comment "Set to Y if you want auto-loading the codec driver" + depends on SND_HDA=y && SND_HDA_CODEC_VIA=m config SND_HDA_CODEC_HDMI - bool "Build HDMI/DisplayPort HD-audio codec support" - select SND_DYNAMIC_MINORS - default y + tristate "Build HDMI/DisplayPort HD-audio codec support" help - Say Y here to include HDMI and DisplayPort HD-audio codec + Say Y or M here to include HDMI and DisplayPort HD-audio codec support in snd-hda-intel driver. This includes all AMD/ATI, Intel and Nvidia HDMI/DisplayPort codecs. - When the HD-audio driver is built as a module, the codec - support code is also built as another module, - snd-hda-codec-hdmi. - This module is automatically loaded at probing. +comment "Set to Y if you want auto-loading the codec driver" + depends on SND_HDA=y && SND_HDA_CODEC_HDMI=m -config SND_HDA_CODEC_CIRRUS - bool "Build Cirrus Logic codec support" - depends on SND_HDA_INTEL +config SND_HDA_I915 + bool default y + depends on DRM_I915 + +config SND_HDA_CODEC_CIRRUS + tristate "Build Cirrus Logic codec support" + select SND_HDA_GENERIC help - Say Y here to include Cirrus Logic codec support in + Say Y or M here to include Cirrus Logic codec support in snd-hda-intel driver, such as CS4206. - When the HD-audio driver is built as a module, the codec - support code is also built as another module, - snd-hda-codec-cirrus. - This module is automatically loaded at probing. +comment "Set to Y if you want auto-loading the codec driver" + depends on SND_HDA=y && SND_HDA_CODEC_CIRRUS=m config SND_HDA_CODEC_CONEXANT - bool "Build Conexant HD-audio codec support" - default y + tristate "Build Conexant HD-audio codec support" + select SND_HDA_GENERIC help - Say Y here to include Conexant HD-audio codec support in + Say Y or M here to include Conexant HD-audio codec support in snd-hda-intel driver, such as CX20549. - When the HD-audio driver is built as a module, the codec - support code is also built as another module, - snd-hda-codec-conexant. - This module is automatically loaded at probing. +comment "Set to Y if you want auto-loading the codec driver" + depends on SND_HDA=y && SND_HDA_CODEC_CONEXANT=m config SND_HDA_CODEC_CA0110 - bool "Build Creative CA0110-IBG codec support" - depends on SND_HDA_INTEL - default y + tristate "Build Creative CA0110-IBG codec support" + select SND_HDA_GENERIC help - Say Y here to include Creative CA0110-IBG codec support in + Say Y or M here to include Creative CA0110-IBG codec support in snd-hda-intel driver, found on some Creative X-Fi cards. - When the HD-audio driver is built as a module, the codec - support code is also built as another module, - snd-hda-codec-ca0110. - This module is automatically loaded at probing. +comment "Set to Y if you want auto-loading the codec driver" + depends on SND_HDA=y && SND_HDA_CODEC_CA0110=m config SND_HDA_CODEC_CA0132 - 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. +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 PM @@ -236,3 +247,5 @@ config SND_HDA_POWER_SAVE_DEFAULT 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 24a251497a1..194f30935e7 100644 --- a/sound/pci/hda/Makefile +++ b/sound/pci/hda/Makefile @@ -1,15 +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 hda_auto_parser.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_intel.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 @@ -23,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/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 index 7da883a464e..dabe41975a9 100644 --- a/sound/pci/hda/hda_auto_parser.c +++ b/sound/pci/hda/hda_auto_parser.c @@ -97,6 +97,54 @@ static void reorder_outputs(unsigned int nums, hda_nid_t *pins) } } +/* 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 * @@ -126,6 +174,9 @@ int snd_hda_parse_pin_defcfg(struct hda_codec *codec, 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)); @@ -156,10 +207,14 @@ int snd_hda_parse_pin_defcfg(struct hda_codec *codec, /* workaround for buggy BIOS setups */ if (dev == AC_JACK_LINE_OUT) { - if (conn == AC_JACK_PORT_FIXED) + 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); @@ -172,10 +227,18 @@ int snd_hda_parse_pin_defcfg(struct hda_codec *codec, continue; if (!assoc_line_out) assoc_line_out = assoc; - else if (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)) + } + 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++; @@ -183,8 +246,12 @@ int snd_hda_parse_pin_defcfg(struct hda_codec *codec, 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)) + 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++; @@ -192,8 +259,12 @@ int snd_hda_parse_pin_defcfg(struct hda_codec *codec, 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)) + 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++; @@ -212,8 +283,12 @@ int snd_hda_parse_pin_defcfg(struct hda_codec *codec, break; case AC_JACK_SPDIF_OUT: case AC_JACK_DIG_OTHER_OUT: - if (cfg->dig_outs >= ARRAY_SIZE(cfg->dig_out_pins)) + 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) ? @@ -231,6 +306,38 @@ int snd_hda_parse_pin_defcfg(struct hda_codec *codec, } } + /* 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. @@ -297,37 +404,37 @@ int snd_hda_parse_pin_defcfg(struct hda_codec *codec, /* * 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", + 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")); - snd_printd(" speaker_outs=%d (0x%x/0x%x/0x%x/0x%x/0x%x)\n", + 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]); - snd_printd(" hp_outs=%d (0x%x/0x%x/0x%x/0x%x/0x%x)\n", + 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]); - snd_printd(" mono: mono_out=0x%x\n", cfg->mono_out_pin); + codec_info(codec, " mono: mono_out=0x%x\n", cfg->mono_out_pin); if (cfg->dig_outs) - snd_printd(" dig-out=0x%x/0x%x\n", + codec_info(codec, " dig-out=0x%x/0x%x\n", cfg->dig_out_pins[0], cfg->dig_out_pins[1]); - snd_printd(" inputs:\n"); + codec_info(codec, " inputs:\n"); for (i = 0; i < cfg->num_inputs; i++) { - snd_printd(" %s=0x%x\n", + codec_info(codec, " %s=0x%x\n", hda_get_autocfg_input_label(codec, cfg, i), cfg->inputs[i].pin); } if (cfg->dig_in_pin) - snd_printd(" dig-in=0x%x\n", cfg->dig_in_pin); + codec_info(codec, " dig-in=0x%x\n", cfg->dig_in_pin); return 0; } -EXPORT_SYMBOL_HDA(snd_hda_parse_pin_defcfg); +EXPORT_SYMBOL_GPL(snd_hda_parse_pin_defcfg); int snd_hda_get_input_pin_attr(unsigned int def_conf) { @@ -348,7 +455,7 @@ int snd_hda_get_input_pin_attr(unsigned int def_conf) return INPUT_PIN_ATTR_FRONT; return INPUT_PIN_ATTR_NORMAL; } -EXPORT_SYMBOL_HDA(snd_hda_get_input_pin_attr); +EXPORT_SYMBOL_GPL(snd_hda_get_input_pin_attr); /** * hda_get_input_pin_label - Give a label for the given input pin @@ -359,11 +466,12 @@ EXPORT_SYMBOL_HDA(snd_hda_get_input_pin_attr); */ 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", "Front Mic", "Rear Mic", + "Internal Mic", "Dock Mic", "Mic", "Rear Mic", "Front Mic" }; int attr; @@ -371,6 +479,10 @@ static const char *hda_get_input_pin_label(struct hda_codec *codec, 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); @@ -394,6 +506,8 @@ static const char *hda_get_input_pin_label(struct hda_codec *codec, return "SPDIF In"; case AC_JACK_DIG_OTHER_IN: return "Digital In"; + case AC_JACK_HP_OUT: + return "Headphone Mic"; default: return "Misc"; } @@ -449,10 +563,11 @@ const char *hda_get_autocfg_input_label(struct hda_codec *codec, 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, + return hda_get_input_pin_label(codec, &cfg->inputs[input], + cfg->inputs[input].pin, has_multiple_pins); } -EXPORT_SYMBOL_HDA(hda_get_autocfg_input_label); +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) @@ -543,7 +658,7 @@ static int fill_audio_out_name(struct hda_codec *codec, hda_nid_t nid, /* 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) + if (idx >= 0 && indexp) *indexp = idx; sfx = ""; } @@ -552,6 +667,9 @@ static int fill_audio_out_name(struct hda_codec *codec, hda_nid_t nid, 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 * @@ -572,6 +690,7 @@ int snd_hda_get_pin_label(struct hda_codec *codec, hda_nid_t nid, unsigned int def_conf = snd_hda_codec_get_pincfg(codec, nid); const char *name = NULL; int i; + bool hdmi; if (indexp) *indexp = 0; @@ -590,16 +709,18 @@ int snd_hda_get_pin_label(struct hda_codec *codec, hda_nid_t nid, 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; - } + 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) { @@ -612,7 +733,7 @@ int snd_hda_get_pin_label(struct hda_codec *codec, hda_nid_t nid, } } if (!name) - name = hda_get_input_pin_label(codec, nid, true); + name = hda_get_input_pin_label(codec, NULL, nid, true); break; } if (!name) @@ -620,30 +741,29 @@ int snd_hda_get_pin_label(struct hda_codec *codec, hda_nid_t nid, strlcpy(label, name, maxlen); return 1; } -EXPORT_SYMBOL_HDA(snd_hda_get_pin_label); +EXPORT_SYMBOL_GPL(snd_hda_get_pin_label); -int snd_hda_gen_add_verbs(struct hda_gen_spec *spec, - const struct hda_verb *list) +int snd_hda_add_verbs(struct hda_codec *codec, + const struct hda_verb *list) { const struct hda_verb **v; - v = snd_array_new(&spec->verbs); + v = snd_array_new(&codec->verbs); if (!v) return -ENOMEM; *v = list; return 0; } -EXPORT_SYMBOL_HDA(snd_hda_gen_add_verbs); +EXPORT_SYMBOL_GPL(snd_hda_add_verbs); -void snd_hda_gen_apply_verbs(struct hda_codec *codec) +void snd_hda_apply_verbs(struct hda_codec *codec) { - struct hda_gen_spec *spec = codec->spec; int i; - for (i = 0; i < spec->verbs.used; i++) { - struct hda_verb **v = snd_array_elem(&spec->verbs, 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_HDA(snd_hda_gen_apply_verbs); +EXPORT_SYMBOL_GPL(snd_hda_apply_verbs); void snd_hda_apply_pincfgs(struct hda_codec *codec, const struct hda_pintbl *cfg) @@ -651,86 +771,136 @@ void snd_hda_apply_pincfgs(struct hda_codec *codec, for (; cfg->nid; cfg++) snd_hda_codec_set_pincfg(codec, cfg->nid, cfg->val); } -EXPORT_SYMBOL_HDA(snd_hda_apply_pincfgs); +EXPORT_SYMBOL_GPL(snd_hda_apply_pincfgs); -void snd_hda_apply_fixup(struct hda_codec *codec, int action) +static void set_pin_targets(struct hda_codec *codec, + const struct hda_pintbl *cfg) { - struct hda_gen_spec *spec = codec->spec; - int id = spec->fixup_id; -#ifdef CONFIG_SND_DEBUG_VERBOSE - const char *modelname = spec->fixup_name; -#endif - int depth = 0; + for (; cfg->nid; cfg++) + snd_hda_set_pin_ctl_cache(codec, cfg->nid, cfg->val); +} - if (!spec->fixup_list) - return; +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 = spec->fixup_list + id; + 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; - snd_printdd(KERN_INFO SFX - "%s: Apply pincfg for %s\n", + 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; - snd_printdd(KERN_INFO SFX - "%s: Apply fix-verbs for %s\n", + codec_dbg(codec, "%s: Apply fix-verbs for %s\n", codec->chip_name, modelname); - snd_hda_gen_add_verbs(codec->spec, fix->v.verbs); + snd_hda_add_verbs(codec, fix->v.verbs); break; case HDA_FIXUP_FUNC: if (!fix->v.func) break; - snd_printdd(KERN_INFO SFX - "%s: Apply fix-func for %s\n", + 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: - snd_printk(KERN_ERR SFX - "%s: Invalid fixup type %d\n", + codec_err(codec, "%s: Invalid fixup type %d\n", codec->chip_name, fix->type); break; } - if (!fix->chained) + if (!fix->chained || fix->chained_before) break; if (++depth > 10) break; id = fix->chain_id; } } -EXPORT_SYMBOL_HDA(snd_hda_apply_fixup); + +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) { - struct hda_gen_spec *spec = codec->spec; 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")) { - spec->fixup_list = NULL; - spec->fixup_id = -1; + 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)) { - id = models->id; - name = models->name; - break; + codec->fixup_id = models->id; + codec->fixup_name = models->name; + codec->fixup_list = fixlist; + codec->fixup_forced = 1; + return; } models++; } @@ -745,7 +915,7 @@ void snd_hda_pick_fixup(struct hda_codec *codec, } } if (id < 0 && quirk) { - for (q = quirk; q->subvendor; q++) { + for (q = quirk; q->subvendor || q->subdevice; q++) { unsigned int vendorid = q->subdevice | (q->subvendor << 16); unsigned int mask = 0xffff0000 | q->subdevice_mask; @@ -759,10 +929,11 @@ void snd_hda_pick_fixup(struct hda_codec *codec, } } - spec->fixup_id = id; + codec->fixup_forced = 0; + codec->fixup_id = id; if (id >= 0) { - spec->fixup_list = fixlist; - spec->fixup_name = name; + codec->fixup_list = fixlist; + codec->fixup_name = name; } } -EXPORT_SYMBOL_HDA(snd_hda_pick_fixup); +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 index 632ad0ad300..e941f604f5e 100644 --- a/sound/pci/hda/hda_auto_parser.h +++ b/sound/pci/hda/hda_auto_parser.h @@ -36,6 +36,8 @@ enum { 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; @@ -51,8 +53,9 @@ enum { 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 */ + 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); @@ -77,8 +80,10 @@ struct auto_pin_cfg { }; /* 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_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, @@ -89,82 +94,25 @@ int snd_hda_parse_pin_defcfg(struct hda_codec *codec, #define snd_hda_parse_pin_def_config(codec, cfg, ignore) \ snd_hda_parse_pin_defcfg(codec, cfg, ignore, 0) -/* - */ - -struct hda_gen_spec { - /* fix-up list */ - int fixup_id; - const struct hda_fixup *fixup_list; - const char *fixup_name; - - /* additional init verbs */ - struct snd_array verbs; -}; - - -/* - * Fix-up pin default configurations and add default verbs - */ - -struct hda_pintbl { - hda_nid_t nid; - u32 val; -}; - -struct hda_model_fixup { - const int id; - const char *name; -}; - -struct hda_fixup { - int type; - bool chained; - 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; -}; - -/* fixup types */ -enum { - HDA_FIXUP_INVALID, - HDA_FIXUP_PINS, - HDA_FIXUP_VERBS, - HDA_FIXUP_FUNC, -}; - -/* fixup action definitions */ -enum { - HDA_FIXUP_ACT_PRE_PROBE, - HDA_FIXUP_ACT_PROBE, - HDA_FIXUP_ACT_INIT, - HDA_FIXUP_ACT_BUILD, -}; - -int snd_hda_gen_add_verbs(struct hda_gen_spec *spec, - const struct hda_verb *list); -void snd_hda_gen_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); - -static inline void snd_hda_gen_init(struct hda_gen_spec *spec) +static inline int auto_cfg_hp_outs(const struct auto_pin_cfg *cfg) { - snd_array_init(&spec->verbs, sizeof(struct hda_verb *), 8); + return (cfg->line_out_type == AUTO_PIN_HP_OUT) ? + cfg->line_outs : cfg->hp_outs; } - -static inline void snd_hda_gen_free(struct hda_gen_spec *spec) +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) { - snd_array_free(&spec->verbs); + 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 0849aac449f..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,15 +168,9 @@ 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; } @@ -170,17 +183,13 @@ int snd_hda_enable_beep_device(struct hda_codec *codec, int enable) enable = !!enable; if (beep->enabled != enable) { beep->enabled = enable; - if (!enable) { - cancel_work_sync(&beep->beep_work); - /* turn off beep */ - snd_hda_codec_write(beep->codec, beep->nid, 0, - AC_VERB_SET_BEEP_CONTROL, 0); - } + 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) { @@ -198,7 +207,7 @@ 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; @@ -217,7 +226,7 @@ int snd_hda_attach_beep_device(struct hda_codec *codec, int nid) 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) { @@ -229,7 +238,28 @@ void snd_hda_detach_beep_device(struct hda_codec *codec) 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) { @@ -251,7 +281,7 @@ int snd_hda_mixer_amp_switch_get_beep(struct snd_kcontrol *kcontrol, } return snd_hda_mixer_amp_switch_get(kcontrol, ucontrol); } -EXPORT_SYMBOL_HDA(snd_hda_mixer_amp_switch_get_beep); +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) @@ -274,4 +304,4 @@ int snd_hda_mixer_amp_switch_put_beep(struct snd_kcontrol *kcontrol, return 0; return snd_hda_mixer_amp_switch_put(kcontrol, ucontrol); } -EXPORT_SYMBOL_HDA(snd_hda_mixer_amp_switch_put_beep); +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 4dc6933bc65..a63b5e07733 100644 --- a/sound/pci/hda/hda_beep.h +++ b/sound/pci/hda/hda_beep.h @@ -34,8 +34,10 @@ struct hda_beep { char phys[32]; int tone; hda_nid_t nid; + unsigned int registered:1; unsigned int enabled:1; unsigned int linear_tone:1; /* linear tone for IDT/STAC codec */ + unsigned int playing:1; struct work_struct beep_work; /* scheduled task for beep event */ struct mutex mutex; }; @@ -44,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) { @@ -52,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 822df971972..4c20277a683 100644 --- a/sound/pci/hda/hda_codec.c +++ b/sound/pci/hda/hda_codec.c @@ -23,9 +23,9 @@ #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> @@ -67,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" }, @@ -83,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) { @@ -92,23 +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_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 inline void hda_call_pm_notify(struct hda_bus *bus, bool power_up) + +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(bus, state) {} +#define hda_call_pm_notify(codec, state) {} #endif /** @@ -143,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 @@ -158,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 @@ -173,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; @@ -209,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; @@ -222,18 +230,27 @@ 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 (!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); } @@ -249,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 * @@ -258,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 * @@ -281,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 @@ -304,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 @@ -326,35 +343,119 @@ 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[HDA_MAX_CONNECTIONS]; + 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 < 0) - return len; - return snd_hda_override_conn_list(codec, nid, len, 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 pointer + * to the list of NIDs. + * + * Returns the number of connections, or a negative error code. + * + * Note that the returned pointer isn't protected against the list + * modification. If snd_hda_override_conn_list() might be called + * concurrently, protect with a mutex appropriately. + */ +int snd_hda_get_conn_list(struct hda_codec *codec, hda_nid_t nid, + const hda_nid_t **listp) +{ + bool added = false; + + for (;;) { + int err; + const struct hda_conn_list *p; + + /* if the connection-list is already cached, read it */ + p = lookup_conn_list(codec, nid); + if (p) { + if (listp) + *listp = p->conns; + return p->len; + } + if (snd_BUG_ON(added)) + return -EINVAL; + + err = read_and_add_raw_conns(codec, nid); + if (err < 0) + return err; + added = true; + } +} +EXPORT_SYMBOL_GPL(snd_hda_get_conn_list); + +/** * snd_hda_get_connections - copy connection list * @codec: the HDA codec * @nid: NID to parse @@ -369,41 +470,42 @@ static int read_and_add_raw_conns(struct hda_codec *codec, hda_nid_t nid) int snd_hda_get_connections(struct hda_codec *codec, hda_nid_t nid, hda_nid_t *conn_list, int max_conns) { - struct snd_array *array = &codec->conn_lists; - int len; - hda_nid_t *p; - bool added = false; + const hda_nid_t *list; + int len = snd_hda_get_conn_list(codec, nid, &list); - again: - mutex_lock(&codec->hash_mutex); - len = -1; - /* if the connection-list is already cached, read it */ - p = lookup_conn_list(array, nid); - if (p) { - len = p[1]; - if (conn_list && len > max_conns) { - snd_printk(KERN_ERR "hda_codec: " - "Too many connections %d for NID 0x%x\n", + if (len > 0 && conn_list) { + if (len > max_conns) { + codec_err(codec, "Too many connections %d for NID 0x%x\n", len, nid); - mutex_unlock(&codec->hash_mutex); return -EINVAL; } - if (conn_list && len) - memcpy(conn_list, p + 2, len * sizeof(hda_nid_t)); + memcpy(conn_list, list, len * sizeof(hda_nid_t)); } - mutex_unlock(&codec->hash_mutex); - if (len >= 0) - return len; - if (snd_BUG_ON(added)) - return -EINVAL; - len = read_and_add_raw_conns(codec, nid); - if (len < 0) - return len; - added = true; - goto again; + return len; +} +EXPORT_SYMBOL_GPL(snd_hda_get_connections); + +/* return CONNLIST_LEN parameter of the given widget */ +static unsigned int get_num_conns(struct hda_codec *codec, hda_nid_t nid) +{ + unsigned int wcaps = get_wcaps(codec, nid); + unsigned int parm; + + if (!(wcaps & AC_WCAP_CONN_LIST) && + get_wcaps_type(wcaps) != AC_WID_VOL_KNB) + return 0; + + parm = snd_hda_param_read(codec, nid, AC_PAR_CONNLIST_LEN); + if (parm == -1) + parm = 0; + return parm; +} + +int snd_hda_get_num_raw_conns(struct hda_codec *codec, hda_nid_t nid) +{ + return snd_hda_get_raw_connections(codec, nid, NULL, 0); } -EXPORT_SYMBOL_HDA(snd_hda_get_connections); /** * snd_hda_get_raw_connections - copy connection list without cache @@ -422,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; @@ -455,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; } @@ -474,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; } @@ -484,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 @@ -536,30 +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; - mutex_lock(&codec->hash_mutex); - 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; - mutex_unlock(&codec->hash_mutex); - return 0; + p = lookup_conn_list(codec, nid); + if (p) { + list_del(&p->list); + kfree(p); + } - error_add: - array->used = old_used; - mutex_unlock(&codec->hash_mutex); - 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 @@ -575,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++; @@ -598,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 @@ -617,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) @@ -633,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 @@ -672,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); @@ -685,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 @@ -745,7 +865,7 @@ int snd_hda_bus_new(struct snd_card *card, 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, }; @@ -759,7 +879,7 @@ int 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; } @@ -778,7 +898,7 @@ int 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; @@ -793,9 +913,9 @@ int 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 @@ -818,14 +938,11 @@ find_codec_preset(struct hda_codec *codec) const struct hda_codec_preset *preset; unsigned int mod_requested = 0; - if (is_generic_config(codec)) - return NULL; /* use the generic parser */ - 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++) { @@ -1001,8 +1118,14 @@ int snd_hda_add_pincfg(struct hda_codec *codec, struct snd_array *list, { struct hda_pincfg *pin; + /* 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; + */ pin = look_up_pincfg(codec, list, nid); if (!pin) { @@ -1030,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 @@ -1045,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) @@ -1058,7 +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); + +/* remember the current pinctl target value */ +int snd_hda_codec_set_pin_target(struct hda_codec *codec, hda_nid_t nid, + unsigned int val) +{ + 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 @@ -1083,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() */ @@ -1108,11 +1264,13 @@ static void hda_jackpoll_work(struct work_struct *work) { struct hda_codec *codec = container_of(work, struct hda_codec, jackpoll_work.work); - if (!codec->jackpoll_interval) - return; 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); } @@ -1125,7 +1283,7 @@ static void free_hda_cache(struct hda_cache_rec *cache); static void free_init_pincfgs(struct hda_codec *codec) { snd_array_free(&codec->driver_pins); -#ifdef CONFIG_SND_HDA_HWDEP +#ifdef CONFIG_SND_HDA_RECONFIG snd_array_free(&codec->user_pins); #endif snd_array_free(&codec->init_pins); @@ -1162,6 +1320,20 @@ 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) @@ -1179,15 +1351,14 @@ static void snd_hda_codec_free(struct hda_codec *codec) snd_array_free(&codec->mixers); snd_array_free(&codec->nids); snd_array_free(&codec->cvt_setups); - snd_array_free(&codec->conn_lists); 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); -#ifdef CONFIG_PM - if (!codec->pm_down_notified) /* cancel leftover refcounts */ - hda_call_pm_notify(codec->bus, false); -#endif + 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); @@ -1195,7 +1366,8 @@ 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 bool snd_hda_codec_get_supported_ps(struct hda_codec *codec, @@ -1204,6 +1376,38 @@ static bool snd_hda_codec_get_supported_ps(struct hda_codec *codec, 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 @@ -1220,6 +1424,11 @@ int snd_hda_codec_new(struct hda_bus *bus, 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; @@ -1227,17 +1436,27 @@ int 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); @@ -1250,10 +1469,13 @@ int 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); 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_PM spin_lock_init(&codec->power_lock); @@ -1263,18 +1485,21 @@ int snd_hda_codec_new(struct hda_bus *bus, * phase. */ hda_keep_power_on(codec); - hda_call_pm_notify(bus, true); #endif + snd_hda_sysfs_init(codec); + if (codec->bus->modelname) { codec->modelname = kstrdup(codec->bus->modelname, GFP_KERNEL); if (!codec->modelname) { - snd_hda_codec_free(codec); - return -ENODEV; + 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, @@ -1292,7 +1517,7 @@ int 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; } @@ -1300,7 +1525,7 @@ int snd_hda_codec_new(struct hda_bus *bus, 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); @@ -1316,11 +1541,14 @@ int snd_hda_codec_new(struct hda_bus *bus, #ifdef CONFIG_PM codec->d3_stop_clk = snd_hda_codec_get_supported_ps(codec, fg, AC_PWRST_CLKSTOP); - if (!codec->d3_stop_clk) - bus->power_keep_link_on = 1; #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, AC_PWRST_D0); @@ -1333,6 +1561,10 @@ int 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; @@ -1341,7 +1573,56 @@ int 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 @@ -1354,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); @@ -1363,31 +1645,50 @@ 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); /* update the stream-id if changed */ static void update_pcm_stream_id(struct hda_codec *codec, @@ -1447,9 +1748,9 @@ void snd_hda_codec_setup_stream(struct hda_codec *codec, hda_nid_t nid, 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); + 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; @@ -1474,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); @@ -1496,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; @@ -1509,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) @@ -1610,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; @@ -1696,7 +1998,7 @@ u32 query_amp_caps(struct hda_codec *codec, hda_nid_t nid, int 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 @@ -1716,7 +2018,7 @@ int snd_hda_override_amp_caps(struct hda_codec *codec, hda_nid_t nid, int dir, { return write_caps_hash(codec, HDA_HASH_KEY(nid, dir, 0), caps); } -EXPORT_SYMBOL_HDA(snd_hda_override_amp_caps); +EXPORT_SYMBOL_GPL(snd_hda_override_amp_caps); static unsigned int read_pin_cap(struct hda_codec *codec, hda_nid_t nid, int dir) @@ -1740,7 +2042,7 @@ u32 snd_hda_query_pin_caps(struct hda_codec *codec, hda_nid_t 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 @@ -1757,14 +2059,14 @@ int snd_hda_override_pin_caps(struct hda_codec *codec, hda_nid_t nid, { 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 or sync the hash value with the current value; * call within hash_mutex */ static struct hda_amp_info * update_amp_hash(struct hda_codec *codec, hda_nid_t nid, int ch, - int direction, int index) + int direction, int index, bool init_only) { struct hda_amp_info *info; unsigned int parm, val = 0; @@ -1790,14 +2092,15 @@ update_amp_hash(struct hda_codec *codec, hda_nid_t nid, int ch, } 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 */ -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) { @@ -1806,8 +2109,8 @@ 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; @@ -1831,38 +2134,28 @@ int snd_hda_codec_amp_read(struct hda_codec *codec, hda_nid_t nid, int ch, unsigned int val = 0; mutex_lock(&codec->hash_mutex); - info = update_amp_hash(codec, nid, ch, direction, index); + 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_HDA(snd_hda_codec_amp_read); +EXPORT_SYMBOL_GPL(snd_hda_codec_amp_read); -/** - * snd_hda_codec_amp_update - update the AMP value - * @codec: HD-audio codec - * @nid: NID to read the AMP value - * @ch: channel (left=0 or right=1) - * @direction: #HDA_INPUT or #HDA_OUTPUT - * @idx: the index value (only for input direction) - * @mask: bit mask to set - * @val: the bits value to set - * - * Update the AMP value with a bit mask. - * Returns 0 if the value is unchanged, 1 if changed. - */ -int snd_hda_codec_amp_update(struct hda_codec *codec, hda_nid_t nid, int ch, - int direction, int idx, int mask, int val) +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); + info = update_amp_hash(codec, nid, ch, direction, idx, init_only); if (!info) { mutex_unlock(&codec->hash_mutex); return 0; @@ -1873,11 +2166,33 @@ int snd_hda_codec_amp_update(struct hda_codec *codec, hda_nid_t nid, int ch, return 0; } info->vol[ch] = val; + cache_only = info->head.dirty = codec->cached_write; + caps = info->amp_caps; mutex_unlock(&codec->hash_mutex); - put_vol_mute(codec, info, nid, ch, direction, idx, val); + if (!cache_only) + put_vol_mute(codec, caps, nid, ch, direction, idx, val); return 1; } -EXPORT_SYMBOL_HDA(snd_hda_codec_amp_update); + +/** + * snd_hda_codec_amp_update - update the AMP value + * @codec: HD-audio codec + * @nid: NID to read the AMP value + * @ch: channel (left=0 or right=1) + * @direction: #HDA_INPUT or #HDA_OUTPUT + * @idx: the index value (only for input direction) + * @mask: bit mask to set + * @val: the bits value to set + * + * Update the AMP value with a bit mask. + * Returns 0 if the value is unchanged, 1 if changed. + */ +int snd_hda_codec_amp_update(struct hda_codec *codec, hda_nid_t nid, int ch, + int direction, int idx, int mask, int val) +{ + return codec_amp_update(codec, nid, ch, direction, idx, mask, val, false); +} +EXPORT_SYMBOL_GPL(snd_hda_codec_amp_update); /** * snd_hda_codec_amp_stereo - update the AMP stereo values @@ -1903,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 @@ -1914,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) @@ -1968,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 @@ -2032,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 @@ -2062,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 @@ -2100,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 @@ -2128,7 +2479,7 @@ 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 * @@ -2157,14 +2508,15 @@ struct snd_kcontrol *snd_hda_find_mixer_ctl(struct hda_codec *codec, { 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, - int dev) + int start_idx) { - int idx; - for (idx = 0; idx < 16; idx++) { /* 16 ctlrs should be large enough */ - if (!find_mixer_ctl(codec, name, dev, 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; @@ -2216,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 @@ -2243,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 @@ -2298,7 +2650,7 @@ int snd_hda_lock_devices(struct hda_bus *bus) spin_unlock(&card->files_lock); return -EINVAL; } -EXPORT_SYMBOL_HDA(snd_hda_lock_devices); +EXPORT_SYMBOL_GPL(snd_hda_lock_devices); void snd_hda_unlock_devices(struct hda_bus *bus) { @@ -2309,7 +2661,7 @@ void snd_hda_unlock_devices(struct hda_bus *bus) card->shutdown = 0; spin_unlock(&card->files_lock); } -EXPORT_SYMBOL_HDA(snd_hda_unlock_devices); +EXPORT_SYMBOL_GPL(snd_hda_unlock_devices); /** * snd_hda_codec_reset - Clear all objects assigned to the codec @@ -2334,13 +2686,10 @@ int snd_hda_codec_reset(struct hda_codec *codec) cancel_delayed_work_sync(&codec->jackpoll_work); #ifdef CONFIG_PM cancel_delayed_work_sync(&codec->power_work); - codec->power_on = 0; - codec->power_transition = 0; - codec->power_jiffies = jiffies; 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); @@ -2348,6 +2697,7 @@ int snd_hda_codec_reset(struct hda_codec *codec) 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)); @@ -2362,11 +2712,13 @@ int snd_hda_codec_reset(struct hda_codec *codec) snd_array_free(&codec->driver_pins); 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; codec->slave_dig_outs = NULL; codec->spdif_status_reset = 0; + unload_parser(codec); module_put(codec->owner); codec->owner = NULL; @@ -2388,8 +2740,7 @@ 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++) { char tmpname[sizeof(sctl->id.name)]; @@ -2416,7 +2767,7 @@ static int check_slave_present(void *data, struct snd_kcontrol *sctl) } /* guess the value corresponding to 0dB */ -static int get_kctl_0dB_offset(struct snd_kcontrol *kctl) +static int get_kctl_0dB_offset(struct snd_kcontrol *kctl, int *step_to_check) { int _tlv[4]; const int *tlv = NULL; @@ -2431,8 +2782,19 @@ static int get_kctl_0dB_offset(struct snd_kcontrol *kctl) 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) - val = -tlv[2] / tlv[3]; + 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; } @@ -2453,7 +2815,7 @@ static int put_kctl_with_value(struct snd_kcontrol *kctl, int val) /* initialize the slave volume with 0dB */ static int init_slave_0dB(void *data, struct snd_kcontrol *slave) { - int offset = get_kctl_0dB_offset(slave); + int offset = get_kctl_0dB_offset(slave, data); if (offset > 0) put_kctl_with_value(slave, offset); return 0; @@ -2497,7 +2859,7 @@ int __snd_hda_add_vmaster(struct hda_codec *codec, char *name, 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); @@ -2514,15 +2876,17 @@ int __snd_hda_add_vmaster(struct hda_codec *codec, char *name, /* init with master mute & zero volume */ put_kctl_with_value(kctl, 0); - if (init_slave_vol) + if (init_slave_vol) { + int step = 0; map_slaves(codec, slaves, suffix, - tlv ? init_slave_0dB : init_slave_unmute, kctl); + tlv ? init_slave_0dB : init_slave_unmute, &step); + } if (ctl_ret) *ctl_ret = kctl; return 0; } -EXPORT_SYMBOL_HDA(__snd_hda_add_vmaster); +EXPORT_SYMBOL_GPL(__snd_hda_add_vmaster); /* * mute-LED control using vmaster @@ -2599,7 +2963,7 @@ int snd_hda_add_vmaster_hook(struct hda_codec *codec, return -ENOMEM; return snd_hda_ctl_add(codec, 0, kctl); } -EXPORT_SYMBOL_HDA(snd_hda_add_vmaster_hook); +EXPORT_SYMBOL_GPL(snd_hda_add_vmaster_hook); /* * Call the hook with the current value for synchronization @@ -2609,6 +2973,11 @@ 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); @@ -2618,7 +2987,7 @@ void snd_hda_sync_vmaster_hook(struct hda_vmaster_mute_hook *hook) break; } } -EXPORT_SYMBOL_HDA(snd_hda_sync_vmaster_hook); +EXPORT_SYMBOL_GPL(snd_hda_sync_vmaster_hook); /** @@ -2638,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 @@ -2664,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 @@ -2698,7 +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); +EXPORT_SYMBOL_GPL(snd_hda_mixer_amp_switch_put); /* * bound volume controls @@ -2730,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 @@ -2760,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 @@ -2783,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 @@ -2806,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 @@ -2835,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 @@ -2858,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, @@ -2866,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, @@ -2874,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 @@ -2966,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) @@ -3132,40 +3501,40 @@ int snd_hda_create_dig_out_ctls(struct hda_codec *codec, int err; struct snd_kcontrol *kctl; struct snd_kcontrol_new *dig_mix; - int idx, dev = 0; - const int spdif_pcm_dev = 1; + int idx = 0; + const int spdif_index = 16; struct hda_spdif_out *spdif; + struct hda_bus *bus = codec->bus; - if (codec->primary_dig_out_type == HDA_PCM_TYPE_HDMI && + if (bus->primary_dig_out_type == HDA_PCM_TYPE_HDMI && type == HDA_PCM_TYPE_SPDIF) { - dev = spdif_pcm_dev; - } else if (codec->primary_dig_out_type == HDA_PCM_TYPE_SPDIF && + idx = spdif_index; + } else if (bus->primary_dig_out_type == HDA_PCM_TYPE_SPDIF && type == HDA_PCM_TYPE_HDMI) { - for (idx = 0; idx < codec->spdif_out.used; idx++) { - spdif = snd_array_elem(&codec->spdif_out, idx); - for (dig_mix = dig_mixes; dig_mix->name; dig_mix++) { - kctl = find_mixer_ctl(codec, dig_mix->name, 0, idx); - if (!kctl) - break; - kctl->id.device = spdif_pcm_dev; - } + /* 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; } - codec->primary_dig_out_type = HDA_PCM_TYPE_HDMI; + bus->primary_dig_out_type = HDA_PCM_TYPE_HDMI; } - if (!codec->primary_dig_out_type) - codec->primary_dig_out_type = type; + if (!bus->primary_dig_out_type) + bus->primary_dig_out_type = type; - idx = find_empty_mixer_ctl_idx(codec, "IEC958 Playback Switch", dev); + idx = find_empty_mixer_ctl_idx(codec, "IEC958 Playback Switch", idx); if (idx < 0) { - printk(KERN_ERR "hda_codec: too many IEC958 outputs\n"); + codec_err(codec, "too many IEC958 outputs\n"); return -EBUSY; } spdif = snd_array_new(&codec->spdif_out); + if (!spdif) + return -ENOMEM; for (dig_mix = dig_mixes; dig_mix->name; dig_mix++) { kctl = snd_ctl_new1(dig_mix, codec); if (!kctl) return -ENOMEM; - kctl->id.device = dev; kctl->id.index = idx; kctl->private_value = codec->spdif_out.used - 1; err = snd_hda_ctl_add(codec, associated_nid, kctl); @@ -3178,7 +3547,7 @@ int snd_hda_create_dig_out_ctls(struct hda_codec *codec, spdif->status = convert_to_spdif_status(spdif->ctls); return 0; } -EXPORT_SYMBOL_HDA(snd_hda_create_dig_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 @@ -3195,7 +3564,7 @@ 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) { @@ -3206,7 +3575,7 @@ void snd_hda_spdif_ctls_unassign(struct hda_codec *codec, int 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) { @@ -3222,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 @@ -3259,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 @@ -3355,7 +3729,7 @@ int snd_hda_create_spdif_in_ctls(struct hda_codec *codec, hda_nid_t nid) 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++) { @@ -3373,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) @@ -3389,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 * @@ -3398,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 * @@ -3434,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; @@ -3450,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 @@ -3462,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 @@ -3491,36 +3883,40 @@ 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); + state); } } -EXPORT_SYMBOL_HDA(snd_hda_codec_set_power_to_all); +EXPORT_SYMBOL_GPL(snd_hda_codec_set_power_to_all); /* * supported power states check @@ -3564,6 +3960,25 @@ static unsigned int hda_sync_power_state(struct hda_codec *codec, 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 */ @@ -3573,11 +3988,15 @@ static unsigned int hda_set_power_state(struct hda_codec *codec, 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) { - /* transition time less than 10ms for power down */ - msleep(codec->epss ? 10 : 100); + 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*/ @@ -3586,11 +4005,14 @@ static unsigned int hda_set_power_state(struct hda_codec *codec, codec->patch_ops.set_power_state(codec, fg, power_state); else { - 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); + 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)) @@ -3600,7 +4022,33 @@ static unsigned int hda_set_power_state(struct hda_codec *codec, 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) { @@ -3640,6 +4088,22 @@ static unsigned int hda_call_codec_suspend(struct hda_codec *codec, bool in_wq) 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; + } +} + /* * kick up codec; used both from PM and power-save */ @@ -3647,6 +4111,8 @@ static void hda_call_codec_resume(struct hda_codec *codec) { 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 */ @@ -3690,19 +4156,20 @@ int snd_hda_build_controls(struct hda_bus *bus) 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 @@ -3769,6 +4236,7 @@ int snd_hda_codec_build_controls(struct hda_codec *codec) 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; } @@ -3865,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; } @@ -3874,7 +4342,7 @@ 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, int dir) @@ -3941,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; @@ -4004,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) @@ -4020,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 @@ -4087,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 @@ -4165,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, @@ -4175,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] = { @@ -4184,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 }, @@ -4199,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; } @@ -4251,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; } } @@ -4275,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 */ } } @@ -4322,7 +4802,7 @@ int 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 @@ -4346,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; } } @@ -4369,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 @@ -4430,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 @@ -4489,7 +4967,7 @@ 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_PM static void hda_power_work(struct work_struct *work) @@ -4512,11 +4990,8 @@ static void hda_power_work(struct work_struct *work) spin_unlock(&codec->power_lock); state = hda_call_codec_suspend(codec, true); - codec->pm_down_notified = 0; - if (!bus->power_keep_link_on && (state & AC_PWRST_CLK_STOP_OK)) { - codec->pm_down_notified = 1; - hda_call_pm_notify(bus, false); - } + 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) @@ -4526,6 +5001,7 @@ static void hda_keep_power_on(struct hda_codec *codec) 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 */ @@ -4545,8 +5021,6 @@ void snd_hda_update_power_acct(struct hda_codec *codec) /* 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) && @@ -4573,11 +5047,6 @@ static void __snd_hda_power_up(struct hda_codec *codec, bool wait_power_down) codec->power_transition = 1; /* avoid reentrance */ spin_unlock(&codec->power_lock); - if (codec->pm_down_notified) { - codec->pm_down_notified = 0; - hda_call_pm_notify(bus, true); - } - hda_call_codec_resume(codec); spin_lock(&codec->power_lock); @@ -4620,7 +5089,7 @@ void snd_hda_power_save(struct hda_codec *codec, int delta, bool d3wait) __snd_hda_power_down(codec); spin_unlock(&codec->power_lock); } -EXPORT_SYMBOL_HDA(snd_hda_power_save); +EXPORT_SYMBOL_GPL(snd_hda_power_save); /** * snd_hda_check_amp_list_power - Check the amp list and update the power @@ -4670,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 /* @@ -4694,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 @@ -4715,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 @@ -4739,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 @@ -4764,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 @@ -4789,7 +5258,7 @@ 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); /* @@ -4818,7 +5287,7 @@ int snd_hda_enum_helper_info(struct snd_kcontrol *kcontrol, texts[uinfo->value.enumerated.item]); return 0; } -EXPORT_SYMBOL_HDA(snd_hda_enum_helper_info); +EXPORT_SYMBOL_GPL(snd_hda_enum_helper_info); /* * Multi-channel / digital-out PCM helper functions @@ -4884,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 @@ -4900,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 @@ -4916,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 @@ -4929,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 @@ -4942,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 @@ -4992,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. @@ -5043,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++) { @@ -5058,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 @@ -5091,7 +5569,7 @@ 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); +EXPORT_SYMBOL_GPL(snd_hda_multi_out_analog_cleanup); /** * snd_hda_get_default_vref - Get the default (mic) VREF pin bits @@ -5118,25 +5596,64 @@ unsigned int snd_hda_get_default_vref(struct hda_codec *codec, hda_nid_t pin) return AC_PINCTL_VREF_GRD; return AC_PINCTL_VREF_HIZ; } -EXPORT_SYMBOL_HDA(snd_hda_get_default_vref); +EXPORT_SYMBOL_GPL(snd_hda_get_default_vref); -int _snd_hda_set_pin_ctl(struct hda_codec *codec, hda_nid_t pin, - unsigned int val, bool cached) +/* 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) { - if (val) { - unsigned int cap = snd_hda_query_pin_caps(codec, pin); - if (cap && (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 (cap && (val & AC_PINCTL_IN_EN)) { - if (!(cap & AC_PINCAP_IN)) - val &= ~(AC_PINCTL_IN_EN | AC_PINCTL_VREFEN); + 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 }, + }; + unsigned int cap; + + 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 val; +} +EXPORT_SYMBOL_GPL(snd_hda_correct_pin_ctl); + +int _snd_hda_set_pin_ctl(struct hda_codec *codec, hda_nid_t pin, + unsigned int val, bool cached) +{ + 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); @@ -5144,7 +5661,7 @@ int _snd_hda_set_pin_ctl(struct hda_codec *codec, hda_nid_t pin, return snd_hda_codec_write(codec, pin, 0, AC_VERB_SET_PIN_WIDGET_CONTROL, val); } -EXPORT_SYMBOL_HDA(_snd_hda_set_pin_ctl); +EXPORT_SYMBOL_GPL(_snd_hda_set_pin_ctl); /** * snd_hda_add_imux_item - Add an item to input_mux @@ -5178,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 @@ -5186,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 @@ -5195,15 +5723,25 @@ 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) { cancel_delayed_work_sync(&codec->jackpoll_work); - if (hda_codec_is_power_on(codec)) - hda_call_codec_suspend(codec, false); + 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 @@ -5214,13 +5752,21 @@ EXPORT_SYMBOL_HDA(snd_hda_suspend); 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) { - hda_call_codec_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 */ /* @@ -5243,20 +5789,18 @@ void *snd_array_new(struct snd_array *array) 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 @@ -5269,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 @@ -5290,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 8665540e55a..5825aa17d8e 100644 --- a/sound/pci/hda/hda_codec.h +++ b/sound/pci/hda/hda_codec.h @@ -25,537 +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 -#define AC_PWRST_ERROR (1<<8) -#define AC_PWRST_CLK_STOP_OK (1<<9) -#define AC_PWRST_SETTING_RESET (1<<10) - -/* 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) - -/* DIGITAL3 bits */ -#define AC_DIG3_ICT (0xf<<0) -#define AC_DIG3_KAE (1<<7) - -/* 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 @@ -618,6 +88,17 @@ struct hda_bus_ops { /* notify power-up/down from codec to controller */ 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 }; /* template to pass to the bus constructor */ @@ -647,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]; @@ -671,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 */ }; /* @@ -719,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 { @@ -746,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 */ @@ -782,6 +271,7 @@ struct hda_pcm { /* codec information */ struct hda_codec { + struct device dev; struct hda_bus *bus; unsigned int addr; /* codec addr*/ struct list_head list; /* list point */ @@ -801,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 */ @@ -830,26 +321,29 @@ struct hda_codec { struct hda_cache_rec amp_cache; /* cache for amp access */ struct hda_cache_rec cmd_cache; /* cache for other commands */ - struct 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? */ - int primary_dig_out_type; /* primary digital out PCM type */ const hda_nid_t *slave_dig_outs; /* optional digital out slave widgets */ struct snd_array init_pins; /* initial (BIOS) pin configurations */ struct snd_array driver_pins; /* pin configs set by codec parser */ struct snd_array cvt_setups; /* audio convert setups */ -#ifdef CONFIG_SND_HDA_HWDEP - struct snd_hwdep *hwdep; /* assigned hwdep device */ + struct mutex user_mutex; +#ifdef CONFIG_SND_HDA_RECONFIG struct snd_array init_verbs; /* additional init verbs */ struct snd_array hints; /* additional hints */ struct snd_array user_pins; /* default pin configs to override */ #endif +#ifdef CONFIG_SND_HDA_HWDEP + struct snd_hwdep *hwdep; /* assigned hwdep device */ +#endif + /* misc flags */ unsigned int spdif_status_reset :1; /* needs to toggle SPDIF for each * status change @@ -865,12 +359,17 @@ struct hda_codec { unsigned int pins_shutup:1; /* pins are shut up */ unsigned int no_trigger_sense:1; /* don't trigger at pin-sensing */ 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 d3_stop_clk:1; /* support D3 operation without BCLK */ - unsigned int pm_down_notified:1; /* PM notified to controller */ + 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 */ @@ -881,6 +380,10 @@ struct hda_codec { 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); @@ -894,6 +397,17 @@ struct hda_codec { /* 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 */ @@ -901,6 +415,8 @@ enum { HDA_INPUT, HDA_OUTPUT }; +/* snd_hda_codec_read/write optional flags */ +#define HDA_RW_NO_RESPONSE_FALLBACK (1 << 0) /* * constructors @@ -910,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) @@ -930,12 +447,17 @@ snd_hda_get_num_conns(struct hda_codec *codec, hda_nid_t nid) { return snd_hda_get_connections(codec, nid, NULL, 0); } +int snd_hda_get_num_raw_conns(struct hda_codec *codec, hda_nid_t nid); int snd_hda_get_raw_connections(struct hda_codec *codec, hda_nid_t nid, hda_nid_t *conn_list, int max_conns); +int snd_hda_get_conn_list(struct hda_codec *codec, hda_nid_t nid, + const hda_nid_t **listp); int snd_hda_override_conn_list(struct hda_codec *codec, hda_nid_t nid, int nums, const hda_nid_t *list); int snd_hda_get_conn_index(struct hda_codec *codec, hda_nid_t mux, hda_nid_t nid, int recursive); +int snd_hda_get_devices(struct hda_codec *codec, hda_nid_t nid, + u8 *dev_list, int max_devices); int snd_hda_query_supported_pcm(struct hda_codec *codec, hda_nid_t nid, u32 *ratesp, u64 *formatsp, unsigned int *bpsp); @@ -952,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 */ }; @@ -1036,8 +554,7 @@ extern const struct snd_pcm_chmap_elem snd_pcm_2_1_chmaps[]; 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); @@ -1136,19 +653,40 @@ static inline void snd_hda_power_sync(struct hda_codec *codec) 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 4c054f4486b..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; } @@ -438,7 +432,7 @@ 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; @@ -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 b81d3d0b952..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_PM -#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); - 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 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); + +/* 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_PM -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; +} - spec->cur_cap_src = 0; - select_input_connection(codec, adc_node, - spec->input_mux.items[0].index); +/* 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; - /* 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; + ret = snd_hda_mixer_amp_switch_put(kcontrol, ucontrol); + if (ret < 0) + return ret; + + 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; +} - /* no volume control? */ - if (! (adc_node->wid_caps & AC_WCAP_IN_AMP) || - ! (adc_node->amp_in_caps & AC_AMPCAP_NUM_STEPS)) +/* 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; + + 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; - 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)); + 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; + } + + 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; - node->checked = 1; - if (node == dest_node) { - /* loopback connection found */ - return 1; + 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; + + 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]; } - if (spec->adc_node) { - info->stream[1] = generic_pcm_playback; - info->stream[1].nid = spec->adc_node->nid; + + 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); + } +} + +/* 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); + +/* + * free the generic spec; + * this can be put as patch_ops.free function + */ +void snd_hda_gen_free(struct hda_codec *codec) +{ + 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 -static int generic_check_power_status(struct hda_codec *codec, hda_nid_t nid) +/* + * 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_gspec *spec = codec->spec; + 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, + +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 = generic_check_power_status, + .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 a5c9411bb36..014a7849e8f 100644 --- a/sound/pci/hda/hda_hwdep.c +++ b/sound/pci/hda/hda_hwdep.c @@ -20,24 +20,13 @@ #include <linux/init.h> #include <linux/slab.h> -#include <linux/pci.h> #include <linux/compat.h> -#include <linux/mutex.h> -#include <linux/ctype.h> -#include <linux/string.h> -#include <linux/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 */ @@ -105,26 +94,6 @@ 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 snd_hda_create_hwdep(struct hda_codec *codec) { char hwname[16]; @@ -139,8 +108,8 @@ int snd_hda_create_hwdep(struct hda_codec *codec) sprintf(hwdep->name, "HDA Codec %d", codec->addr); hwdep->iface = SNDRV_HWDEP_IFACE_HDA; hwdep->private_data = codec; - hwdep->private_free = hwdep_free; hwdep->exclusive = 1; + hwdep->groups = snd_hda_dev_attr_groups; hwdep->ops.open = hda_hwdep_open; hwdep->ops.ioctl = hda_hwdep_ioctl; @@ -148,657 +117,8 @@ int 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_PM -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)); -} - -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_PM */ - -#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, ¶m) != 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, -}; + /* link to codec */ + hwdep->dev = &codec->dev; -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, 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 || !patch_items[line_mode].need_codec)) - patch_items[line_mode].parser(buf, bus, &codec); - } 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 0b6aebacc56..83cd19017cf 100644 --- a/sound/pci/hda/hda_intel.c +++ b/sound/pci/hda/hda_intel.c @@ -62,6 +62,9 @@ #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; @@ -126,6 +129,7 @@ static struct kernel_param_ops param_ops_xint = { #define param_check_xint param_check_int static int power_save = CONFIG_SND_HDA_POWER_SAVE_DEFAULT; +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)."); @@ -137,6 +141,8 @@ 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."); +#else +static int *power_save_addr; #endif /* CONFIG_PM */ static int align_buffer_size = -1; @@ -148,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 @@ -168,6 +172,7 @@ MODULE_SUPPORTED_DEVICE("{{Intel, ICH6}," "{Intel, PPT}," "{Intel, LPT}," "{Intel, LPT_LP}," + "{Intel, WPT_LP}," "{Intel, HPT}," "{Intel, PBG}," "{Intel, SCH}," @@ -189,354 +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 " -#endif - #if defined(CONFIG_PM) && defined(CONFIG_VGA_SWITCHEROO) -#ifdef CONFIG_SND_HDA_CODEC_HDMI +#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, - 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; - /* - * 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; -}; - -/* 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; - 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 */ - 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; - - /* reboot notifier (for mysterious hangup problem at power-down) */ - struct notifier_block reboot_notifier; - - /* card list (for power_save trigger) */ - struct list_head list; -}; - -#define CREATE_TRACE_POINTS -#include "hda_intel_trace.h" - /* 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, @@ -551,35 +224,25 @@ enum { 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 */ -#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_COUNT_LPIB_DELAY (1 << 25) /* Take LPIB as delay */ -#define AZX_DCAPS_PM_RUNTIME (1 << 26) /* runtime PM support */ - /* quirks for Intel PCH */ #define AZX_DCAPS_INTEL_PCH_NOPM \ (AZX_DCAPS_SCH_SNOOP | AZX_DCAPS_BUFSIZE | \ - AZX_DCAPS_COUNT_LPIB_DELAY) + AZX_DCAPS_COUNT_LPIB_DELAY | AZX_DCAPS_REVERSE_ASSIGN) #define AZX_DCAPS_INTEL_PCH \ (AZX_DCAPS_INTEL_PCH_NOPM | AZX_DCAPS_PM_RUNTIME) +#define AZX_DCAPS_INTEL_HASWELL \ + (AZX_DCAPS_SCH_SNOOP | AZX_DCAPS_ALIGN_BUFSIZE | \ + AZX_DCAPS_COUNT_LPIB_DELAY | AZX_DCAPS_PM_RUNTIME | \ + AZX_DCAPS_I915_POWERWELL) + +/* Broadwell HDMI can't use position buffer reliably, force to use LPIB */ +#define AZX_DCAPS_INTEL_BROADWELL \ + (AZX_DCAPS_SCH_SNOOP | AZX_DCAPS_ALIGN_BUFSIZE | \ + AZX_DCAPS_POSFIX_LPIB | AZX_DCAPS_PM_RUNTIME | \ + AZX_DCAPS_I915_POWERWELL) + /* quirks for ATI SB / AMD Hudson */ #define AZX_DCAPS_PRESET_ATI_SB \ (AZX_DCAPS_ATI_SNOOP | AZX_DCAPS_NO_TCSEL | \ @@ -592,7 +255,8 @@ enum { /* quirks for Nvidia */ #define AZX_DCAPS_PRESET_NVIDIA \ (AZX_DCAPS_NVIDIA_SNOOP | AZX_DCAPS_RIRB_DELAY | AZX_DCAPS_NO_MSI |\ - AZX_DCAPS_ALIGN_BUFSIZE) + AZX_DCAPS_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) @@ -610,6 +274,7 @@ 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", @@ -623,62 +288,49 @@ static char *driver_short_names[] = { [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; } } @@ -689,550 +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 "%s: cannot allocate CORB/RIRB\n", pci_name(chip->pci)); - 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_readw(chip, CORBWP); - if (wp == 0xffff) { - /* something wrong, controller likely turned to D3 */ - spin_unlock_irq(&chip->reg_lock); - return -1; - } - 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_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 = 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 "%s: spurious response %#x:%#x, " - "last cmd=%#08x\n", - pci_name(chip->pci), - 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 (!chip->polling_mode && chip->poll_count < 2) { - snd_printdd(SFX "%s: azx_get_response timeout, " - "polling the codec once: last cmd=0x%08x\n", - pci_name(chip->pci), chip->last_cmd[addr]); - do_poll = 1; - chip->poll_count++; - goto again; - } - - - if (!chip->polling_mode) { - snd_printk(KERN_WARNING SFX "%s: azx_get_response timeout, " - "switching to polling mode: last cmd=0x%08x\n", - pci_name(chip->pci), chip->last_cmd[addr]); - chip->polling_mode = 1; - goto again; - } - - if (chip->msi) { - snd_printk(KERN_WARNING SFX "%s: No response from codec, " - "disabling MSI: last cmd=0x%08x\n", - pci_name(chip->pci), 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 "%s: get_response timeout: IRS=0x%x\n", - pci_name(chip->pci), 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 "%s: send_cmd timeout: IRS=0x%x, val=0x%x\n", - pci_name(chip->pci), 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); -} - -/* 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); -} - -#ifdef CONFIG_PM -static void azx_power_notify(struct hda_bus *bus, bool power_up); -#endif - -/* reset codec link */ -static int azx_reset(struct azx *chip, int full_reset) -{ - unsigned long timeout; - - 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); - - timeout = jiffies + msecs_to_jiffies(100); - while (azx_readb(chip, GCTL) && - time_before(jiffies, timeout)) - usleep_range(500, 1000); - - /* 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_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); - - /* 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)) { - snd_printd(SFX "%s: azx_reset: controller not ready!\n", pci_name(chip->pci)); - 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 "%s: codec_mask = 0x%x\n", pci_name(chip->pci), 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 @@ -1258,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 "%s: Clearing TCSEL\n", pci_name(chip->pci)); + dev_dbg(chip->card->dev, "Clearing TCSEL\n"); update_pci_byte(chip->pci, ICH6_PCIREG_TCSEL, 0x07, 0); } @@ -1266,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 "%s: Setting ATI snoop: %d\n", pci_name(chip->pci), 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); @@ -1274,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 "%s: Setting Nvidia snoop: %d\n", pci_name(chip->pci), 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); @@ -1299,1022 +415,29 @@ static void azx_init_pci(struct azx *chip) pci_read_config_word(chip->pci, INTEL_SCH_HDA_DEVC, &snoop); } - snd_printdd(SFX "%s: SCH snoop: %s\n", - pci_name(chip->pci), (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) +/* called from IRQ */ +static int azx_position_check(struct azx *chip, struct azx_dev *azx_dev) { - struct azx *chip = dev_id; - struct azx_dev *azx_dev; - u32 status; - u8 sd_status; - int i, ok; + int ok; -#ifdef CONFIG_PM_RUNTIME - if (chip->pci->dev.power.runtime_status != RPM_ACTIVE) - 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) { - 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 azx *chip, - 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); - /* 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; - - /* 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 (!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) { - snd_printk(KERN_WARNING SFX "%s: Too big adjustment %d\n", - pci_name(chip->pci), bdl_pos_adj[chip->dev_index]); - pos_adj = 0; - } else { - ofs = setup_bdle(chip, 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, substream, azx_dev, &bdl, ofs, - period_bytes - pos_adj, 0); - else - ofs = setup_bdle(chip, substream, azx_dev, &bdl, ofs, - period_bytes, - !azx_dev->no_period_wakeup); - if (ofs < 0) - goto error; + ok = azx_position_ok(chip, azx_dev); + if (ok == 1) { + azx_dev->irq_pending = 0; + return ok; + } else if (ok == 0 && chip->bus && chip->bus->workq) { + /* bogus IRQ, process it later */ + azx_dev->irq_pending = 1; + queue_work(chip->bus->workq, &chip->irq_pending_work); } return 0; - - error: - snd_printk(KERN_ERR SFX "%s: Too many BDL entries: buffer=%d, period=%d\n", - pci_name(chip->pci), azx_dev->bufsize, period_bytes); - return -EINVAL; -} - -/* reset stream */ -static void azx_stream_reset(struct azx *chip, struct azx_dev *azx_dev) -{ - unsigned char val; - int timeout; - - azx_stream_clear(chip, azx_dev); - - azx_sd_writeb(azx_dev, SD_CTL, azx_sd_readb(azx_dev, SD_CTL) | - SD_CTL_STREAM_RESET); - udelay(3); - timeout = 300; - while (!((val = azx_sd_readb(azx_dev, SD_CTL)) & SD_CTL_STREAM_RESET) && - --timeout) - ; - val &= ~SD_CTL_STREAM_RESET; - azx_sd_writeb(azx_dev, SD_CTL, val); - udelay(3); - - timeout = 300; - /* waiting for hardware to report that the stream is out of reset */ - while (((val = azx_sd_readb(azx_dev, SD_CTL)) & SD_CTL_STREAM_RESET) && - --timeout) - ; - - /* reset first position - may not be synced with hw at this time */ - *azx_dev->posbuf = 0; -} - -/* - * set up the SD for streaming - */ -static int azx_setup_controller(struct azx *chip, struct azx_dev *azx_dev) -{ - 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); - } - - /* 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 "%s: codec #%d probed OK\n", pci_name(chip->pci), 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; -} - -static int get_jackpoll_interval(struct azx *chip) -{ - int i = jackpoll_ms[chip->dev_index]; - unsigned int j; - if (i == 0) - return 0; - if (i < 50 || i > 60000) - j = 0; - else - j = msecs_to_jiffies(i); - if (j == 0) - snd_printk(KERN_WARNING SFX - "jackpoll_ms value out of range: %d\n", i); - return j; -} - -/* - * Codec initialization - */ - -/* 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_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_PM - 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 "%s: Enable delay in RIRB handling\n", pci_name(chip->pci)); - 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 - "%s: Codec #%d probe error; " - "disabling it...\n", pci_name(chip->pci), 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 "%s: Enable sync_write for stable communication\n", - pci_name(chip->pci)); - 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) { - snd_printk(KERN_ERR SFX "%s: no codecs initialized\n", pci_name(chip->pci)); - return -ENXIO; - } - return 0; -} - -/* configure each codec instance */ -static 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; -} - - -/* - * 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 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 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 */ - - *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_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 - "%s: invalid format_val, rate=%d, ch=%d, format=%d\n", - pci_name(chip->pci), 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 "%s: azx_pcm_prepare: bufsize=0x%x, format=0x%x\n", - pci_name(chip->pci), 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) - 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; - - azx_dev = get_azx_dev(substream); - trace_azx_pcm_trigger(chip, azx_dev, cmd); - - 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(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(); - } - } - 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(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; - int delay = 0; - - 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; - - /* calculate runtime delay from LPIB */ - if (azx_dev->substream->runtime && - chip->position_fix[stream] == POS_FIX_POSBUF && - (chip->driver_caps & AZX_DCAPS_COUNT_LPIB_DELAY)) { - unsigned int lpib_pos = azx_sd_readl(azx_dev, SD_LPIB); - if (stream == SNDRV_PCM_STREAM_PLAYBACK) - delay = pos - lpib_pos; - else - delay = lpib_pos - pos; - if (delay < 0) - delay += azx_dev->bufsize; - if (delay >= azx_dev->period_bytes) { - snd_printk(KERN_WARNING SFX - "%s: Unstable LPIB (%d >= %d); " - "disabling LPIB delay counting\n", - pci_name(chip->pci), delay, azx_dev->period_bytes); - delay = 0; - chip->driver_caps &= ~AZX_DCAPS_COUNT_LPIB_DELAY; - } - azx_dev->substream->runtime->delay = - bytes_to_frames(azx_dev->substream->runtime, delay); - } - trace_azx_get_position(chip, azx_dev, pos, delay); - 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)); } /* @@ -2343,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 */ } @@ -2357,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; } @@ -2402,139 +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, - .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) { - snd_printk(KERN_ERR SFX "%s: PCM %d already exists\n", - pci_name(chip->pci), 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 azx_mixer_create(struct azx *chip) -{ - return snd_hda_build_controls(chip->bus); -} - - -/* - * initialize SD streams - */ -static 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; -} - 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; @@ -2544,41 +541,7 @@ static int azx_acquire_irq(struct azx *chip, int do_disconnect) return 0; } - -static 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; -} - #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->pci->dev); - else - pm_runtime_put_sync(&chip->pci->dev); -} - static DEFINE_MUTEX(card_list_lock); static LIST_HEAD(card_list); @@ -2633,7 +596,7 @@ static int azx_suspend(struct device *dev) struct azx *chip = card->private_data; struct azx_pcm *p; - if (chip->disabled) + if (chip->disabled || chip->init_failed) return 0; snd_power_change_state(card, SNDRV_CTL_POWER_D3hot); @@ -2643,15 +606,19 @@ static int azx_suspend(struct device *dev) 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_D3hot); + if (chip->driver_caps & AZX_DCAPS_I915_POWERWELL) + hda_display_power(false); return 0; } @@ -2661,14 +628,18 @@ static int azx_resume(struct device *dev) struct snd_card *card = dev_get_drvdata(dev); struct azx *chip = card->private_data; - if (chip->disabled) + 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; } @@ -2680,7 +651,7 @@ static int azx_resume(struct device *dev) return -EIO; azx_init_pci(chip); - azx_init_chip(chip, 1); + azx_init_chip(chip, true); snd_hda_resume(chip->bus); snd_power_change_state(card, SNDRV_CTL_POWER_D0); @@ -2694,8 +665,22 @@ 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; } @@ -2703,9 +688,39 @@ 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, 1); + 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; } @@ -2714,6 +729,9 @@ 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; @@ -2758,7 +776,6 @@ static void azx_notifier_unregister(struct azx *chip) unregister_reboot_notifier(&chip->reboot_notifier); } -static int azx_first_init(struct azx *chip); static int azx_probe_continue(struct azx *chip); #ifdef SUPPORT_VGA_SWITCHEROO @@ -2782,31 +799,32 @@ static void azx_vs_set_state(struct pci_dev *pci, if (!chip->bus) { chip->disabled = disabled; if (!disabled) { - snd_printk(KERN_INFO SFX - "%s: Start delayed initialization\n", - pci_name(chip->pci)); - if (azx_first_init(chip) < 0 || - azx_probe_continue(chip) < 0) { - snd_printk(KERN_ERR SFX - "%s: initialization error\n", - pci_name(chip->pci)); + 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 { - snd_printk(KERN_INFO SFX - "%s: %s via VGA-switcheroo\n", pci_name(chip->pci), - disabled ? "Disabling" : "Enabling"); + dev_info(chip->card->dev, "%s via VGA-switcheroo\n", + disabled ? "Disabling" : "Enabling"); if (disabled) { - azx_suspend(&pci->dev); + 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)) - snd_printk(KERN_WARNING SFX "%s: Cannot lock devices!\n", - pci_name(chip->pci)); + 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(&pci->dev); + azx_resume(card->dev); } } } @@ -2831,9 +849,8 @@ static void init_vga_switcheroo(struct azx *chip) { struct pci_dev *p = get_bound_vga(chip->pci); if (p) { - snd_printk(KERN_INFO SFX - "%s: Handle VGA-switcheroo audio client\n", - pci_name(chip->pci)); + dev_info(chip->card->dev, + "Handle VGA-switcheroo audio client\n"); chip->use_vga_switcheroo = 1; pci_dev_put(p); } @@ -2859,6 +876,10 @@ static int register_vga_switcheroo(struct azx *chip) 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 @@ -2872,8 +893,15 @@ static int register_vga_switcheroo(struct azx *chip) */ 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); @@ -2902,21 +930,7 @@ 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); - } + azx_free_stream_pages(chip); if (chip->region_requested) pci_release_regions(chip->pci); pci_disable_device(chip->pci); @@ -2925,7 +939,11 @@ static int azx_free(struct azx *chip) if (chip->fw) release_firmware(chip->fw); #endif - kfree(chip); + if (chip->driver_caps & AZX_DCAPS_I915_POWERWELL) { + hda_display_power(false); + hda_i915_exit(); + } + kfree(hda); return 0; } @@ -3012,20 +1030,19 @@ static int check_position_fix(struct azx *chip, int 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 "%s: Using VIACOMBO position fix\n", pci_name(chip->pci)); + 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 "%s: Using LPIB position fix\n", pci_name(chip->pci)); + dev_dbg(chip->card->dev, "Using LPIB position fix\n"); return POS_FIX_LPIB; } return POS_FIX_AUTO; @@ -3063,10 +1080,9 @@ static void 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; } } @@ -3075,8 +1091,8 @@ static void 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); } } @@ -3084,9 +1100,14 @@ static void check_probe_mask(struct azx *chip, int dev) * white/black-list for enable_msi */ 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 */ {} @@ -3103,16 +1124,16 @@ static void 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; } } @@ -3138,25 +1159,35 @@ static void 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 "%s: Force to %s mode\n", - pci_name(chip->pci), 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 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) { static struct snd_device_ops ops = { .dev_free = azx_dev_free, }; + struct hda_intel *hda; struct azx *chip; int err; @@ -3166,22 +1197,25 @@ static int 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 "%s: Cannot allocate chip\n", pci_name(pci)); + 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); @@ -3213,16 +1247,20 @@ static int 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) { - snd_printk(KERN_ERR SFX "%s: Error creating device [card]!\n", - pci_name(chip->pci)); + 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; } @@ -3231,7 +1269,7 @@ 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 i, err; + int err; unsigned short gcap; #if BITS_PER_LONG != 64 @@ -3252,7 +1290,7 @@ static int azx_first_init(struct azx *chip) 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 "%s: ioremap error\n", pci_name(chip->pci)); + dev_err(card->dev, "ioremap error\n"); return -ENXIO; } @@ -3267,7 +1305,7 @@ static int azx_first_init(struct azx *chip) synchronize_irq(chip->irq); gcap = azx_readw(chip, GCAP); - snd_printdd(SFX "%s: chipset global capabilities = 0x%x\n", pci_name(chip->pci), 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) { @@ -3284,7 +1322,7 @@ static int azx_first_init(struct azx *chip) /* disable 64bit DMA address on some devices */ if (chip->driver_caps & AZX_DCAPS_NO_64BIT) { - snd_printd(SFX "%s: Disabling 64bit DMA\n", pci_name(chip->pci)); + dev_dbg(card->dev, "Disabling 64bit DMA\n"); gcap &= ~ICH6_GCAP_64OK; } @@ -3339,32 +1377,11 @@ static int azx_first_init(struct azx *chip) chip->azx_dev = kcalloc(chip->num_streams, sizeof(*chip->azx_dev), GFP_KERNEL); if (!chip->azx_dev) { - snd_printk(KERN_ERR SFX "%s: cannot malloc azx_dev\n", pci_name(chip->pci)); + 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 "%s: cannot allocate BDL\n", pci_name(chip->pci)); - return -ENOMEM; - } - 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 "%s: cannot allocate posbuf\n", pci_name(chip->pci)); - return -ENOMEM; - } - 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) return err; @@ -3373,11 +1390,15 @@ static int azx_first_init(struct azx *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 "%s: no codecs found!\n", pci_name(chip->pci)); + dev_err(card->dev, "no codecs found!\n"); return -ENODEV; } @@ -3413,8 +1434,7 @@ static void azx_firmware_cb(const struct firmware *fw, void *context) struct pci_dev *pci = chip->pci; if (!fw) { - snd_printk(KERN_ERR SFX "%s: Cannot load firmware, aborting\n", - pci_name(chip->pci)); + dev_err(card->dev, "Cannot load firmware, aborting\n"); goto error; } @@ -3432,13 +1452,139 @@ static void azx_firmware_cb(const struct firmware *fw, void *context) } #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 probe_now; + bool schedule_probe; int err; if (dev >= SNDRV_CARDS) @@ -3448,15 +1594,15 @@ static int 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 "hda-intel: Error creating card!\n"); + dev_err(&pci->dev, "Error creating card!\n"); return err; } - 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; @@ -3465,68 +1611,92 @@ static int azx_probe(struct pci_dev *pci, err = register_vga_switcheroo(chip); if (err < 0) { - snd_printk(KERN_ERR SFX - "%s: Error registering VGA-switcheroo client\n", pci_name(pci)); + dev_err(card->dev, "Error registering VGA-switcheroo client\n"); goto out_free; } if (check_hdmi_disabled(pci)) { - snd_printk(KERN_INFO SFX "%s: VGA controller is disabled\n", - pci_name(pci)); - snd_printk(KERN_INFO SFX "%s: Delaying initialization\n", pci_name(pci)); + dev_info(card->dev, "VGA controller is disabled\n"); + dev_info(card->dev, "Delaying initialization\n"); chip->disabled = true; } - probe_now = !chip->disabled; - if (probe_now) { - err = azx_first_init(chip); - if (err < 0) - goto out_free; - } + schedule_probe = !chip->disabled; #ifdef CONFIG_SND_HDA_PATCH_LOADER if (patch[dev] && *patch[dev]) { - snd_printk(KERN_ERR SFX "%s: Applying patch firmware '%s'\n", - pci_name(pci), 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; - probe_now = false; /* continued in azx_firmware_cb() */ + schedule_probe = false; /* continued in azx_firmware_cb() */ } #endif /* CONFIG_SND_HDA_PATCH_LOADER */ - if (probe_now) { - err = azx_probe_continue(chip); - if (err < 0) - goto out_free; - } +#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 (pci_dev_run_wake(pci)) - pm_runtime_put_noidle(&pci->dev); + if (schedule_probe) + schedule_work(&chip->probe_work); dev++; - complete_all(&chip->probe_wait); + if (chip->disabled) + complete_all(&chip->probe_wait); return 0; out_free: snd_card_free(card); - pci_set_drvdata(pci, NULL); 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 @@ -3565,11 +1735,13 @@ static int azx_probe_continue(struct azx *chip) power_down_all_codecs(chip); azx_notifier_register(chip); azx_add_card_list(chip); - - return 0; + if ((chip->driver_caps & AZX_DCAPS_PM_RUNTIME) || chip->use_vga_switcheroo) + pm_runtime_put_noidle(&pci->dev); out_free: - chip->init_failed = 1; + if (err < 0) + chip->init_failed = 1; + complete_all(&chip->probe_wait); return err; } @@ -3577,16 +1749,12 @@ static void azx_remove(struct pci_dev *pci) { struct snd_card *card = pci_get_drvdata(pci); - if (pci_dev_run_wake(pci)) - pm_runtime_get_noresume(&pci->dev); - if (card) snd_card_free(card); - pci_set_drvdata(pci, NULL); } /* 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_INTEL_PCH_NOPM }, @@ -3595,31 +1763,49 @@ static DEFINE_PCI_DEVICE_TABLE(azx_ids) = { .driver_data = AZX_DRIVER_PCH | AZX_DCAPS_INTEL_PCH_NOPM }, /* Panther Point */ { PCI_DEVICE(0x8086, 0x1e20), - .driver_data = AZX_DRIVER_PCH | AZX_DCAPS_INTEL_PCH_NOPM }, + .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_SCH | AZX_DCAPS_INTEL_PCH }, + .driver_data = AZX_DRIVER_HDMI | AZX_DCAPS_INTEL_HASWELL }, { PCI_DEVICE(0x8086, 0x0d0c), - .driver_data = AZX_DRIVER_SCH | AZX_DCAPS_INTEL_PCH }, + .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 }, - /* SCH */ + .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 | @@ -3687,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), @@ -3722,7 +1924,7 @@ static DEFINE_PCI_DEVICE_TABLE(azx_ids) = { .driver_data = AZX_DRIVER_CTHDA | AZX_DCAPS_PRESET_CTHDA }, { PCI_DEVICE(0x1102, 0x0012), .driver_data = AZX_DRIVER_CTHDA | AZX_DCAPS_PRESET_CTHDA }, -#if !defined(CONFIG_SND_CTXFI) && !defined(CONFIG_SND_CTXFI_MODULE) +#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. diff --git a/sound/pci/hda/hda_jack.c b/sound/pci/hda/hda_jack.c index 6e9f57bbe66..9746d73cec5 100644 --- a/sound/pci/hda/hda_jack.c +++ b/sound/pci/hda/hda_jack.c @@ -29,16 +29,18 @@ bool is_jack_detectable(struct hda_codec *codec, hda_nid_t nid) 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)) + if (!(get_wcaps(codec, nid) & AC_WCAP_UNSOL_CAP) && + !codec->jackpoll_interval) return false; return true; } -EXPORT_SYMBOL_HDA(is_jack_detectable); +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); @@ -46,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; } /** @@ -66,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 @@ -84,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 @@ -103,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) { @@ -167,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 @@ -186,21 +191,27 @@ 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); +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 @@ -225,14 +236,14 @@ int snd_hda_jack_detect_enable_callback(struct hda_codec *codec, hda_nid_t nid, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | jack->tag); } -EXPORT_SYMBOL_HDA(snd_hda_jack_detect_enable_callback); +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_HDA(snd_hda_jack_detect_enable); +EXPORT_SYMBOL_GPL(snd_hda_jack_detect_enable); /** * snd_hda_jack_set_gating_jack - Set gating jack. @@ -242,8 +253,8 @@ EXPORT_SYMBOL_HDA(snd_hda_jack_detect_enable); 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_get(codec, gated_nid); - struct hda_jack_tbl *gating = snd_hda_jack_tbl_get(codec, 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; @@ -253,7 +264,7 @@ int snd_hda_jack_set_gating_jack(struct hda_codec *codec, hda_nid_t gated_nid, return 0; } -EXPORT_SYMBOL_HDA(snd_hda_jack_set_gating_jack); +EXPORT_SYMBOL_GPL(snd_hda_jack_set_gating_jack); /** * snd_hda_jack_report_sync - sync the states of all jacks and report if changed @@ -275,7 +286,7 @@ void snd_hda_jack_report_sync(struct hda_codec *codec) jack = codec->jacktbl.list; for (i = 0; i < codec->jacktbl.used; i++, jack++) if (jack->nid) { - if (!jack->kctl) + 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); @@ -286,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 */ @@ -366,7 +377,7 @@ int snd_hda_jack_add_kctl(struct hda_codec *codec, hda_nid_t nid, { return __snd_hda_jack_add_kctl(codec, nid, name, idx, false); } -EXPORT_SYMBOL_HDA(snd_hda_jack_add_kctl); +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) @@ -389,10 +400,11 @@ static int get_unique_index(struct hda_codec *codec, const char *name, int idx) } static int add_jack_kctl(struct hda_codec *codec, hda_nid_t nid, - const struct auto_pin_cfg *cfg) + const struct auto_pin_cfg *cfg, + 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; @@ -405,7 +417,11 @@ static int add_jack_kctl(struct hda_codec *codec, hda_nid_t nid, 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 (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); @@ -428,44 +444,56 @@ int snd_hda_jack_add_kctls(struct hda_codec *codec, const hda_nid_t *p; int i, err; + for (i = 0; i < cfg->num_inputs; i++) { + /* If we have headphone mics; make sure they get the right name + before grabbed by output pins */ + if (cfg->inputs[i].is_headphone_mic) { + if (auto_cfg_hp_outs(cfg) == 1) + err = add_jack_kctl(codec, auto_cfg_hp_pins(cfg)[0], + cfg, "Headphone Mic"); + else + err = add_jack_kctl(codec, cfg->inputs[i].pin, + cfg, "Headphone Mic"); + } else + err = add_jack_kctl(codec, cfg->inputs[i].pin, cfg, + NULL); + if (err < 0) + return err; + } + for (i = 0, p = cfg->line_out_pins; i < cfg->line_outs; i++, p++) { - err = add_jack_kctl(codec, *p, cfg); + 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); + 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); - if (err < 0) - return err; - } - for (i = 0; i < cfg->num_inputs; i++) { - err = add_jack_kctl(codec, cfg->inputs[i].pin, cfg); + 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); + err = add_jack_kctl(codec, *p, cfg, NULL); if (err < 0) return err; } - err = add_jack_kctl(codec, cfg->dig_in_pin, cfg); + 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); + 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) @@ -493,7 +521,7 @@ void snd_hda_jack_unsol_event(struct hda_codec *codec, unsigned int res) call_jack_callback(codec, event); snd_hda_jack_report_sync(codec); } -EXPORT_SYMBOL_HDA(snd_hda_jack_unsol_event); +EXPORT_SYMBOL_GPL(snd_hda_jack_unsol_event); void snd_hda_jack_poll_all(struct hda_codec *codec) { @@ -514,5 +542,5 @@ void snd_hda_jack_poll_all(struct hda_codec *codec) if (changes) snd_hda_jack_report_sync(codec); } -EXPORT_SYMBOL_HDA(snd_hda_jack_poll_all); +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 ec12abd4526..46e1ea83ce3 100644 --- a/sound/pci/hda/hda_jack.h +++ b/sound/pci/hda/hda_jack.h @@ -28,6 +28,7 @@ struct hda_jack_tbl { 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 */ @@ -75,7 +76,18 @@ 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); + +/* the jack state returned from snd_hda_jack_detect_state() */ +enum { + HDA_JACK_NOT_PRESENT, HDA_JACK_PRESENT, HDA_JACK_PHANTOM, +}; + +int snd_hda_jack_detect_state(struct hda_codec *codec, hda_nid_t nid); + +static inline bool snd_hda_jack_detect(struct hda_codec *codec, hda_nid_t nid) +{ + return snd_hda_jack_detect_state(codec, nid) != HDA_JACK_NOT_PRESENT; +} bool is_jack_detectable(struct hda_codec *codec, hda_nid_t nid); diff --git a/sound/pci/hda/hda_local.h b/sound/pci/hda/hda_local.h index 4b40a5e7a8f..4e2d4863daa 100644 --- a/sound/pci/hda/hda_local.h +++ b/sound/pci/hda/hda_local.h @@ -133,9 +133,11 @@ 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); @@ -350,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 @@ -384,6 +380,97 @@ int snd_hda_add_new_ctls(struct hda_codec *codec, const struct snd_kcontrol_new *knew); /* + * Fix-up pin default configurations and add default verbs + */ + +struct hda_pintbl { + hda_nid_t nid; + u32 val; +}; + +struct hda_model_fixup { + const int id; + const char *name; +}; + +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; +}; + +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 { + HDA_FIXUP_INVALID, + HDA_FIXUP_PINS, + HDA_FIXUP_VERBS, + HDA_FIXUP_FUNC, + HDA_FIXUP_PINCTLS, +}; + +/* fixup action definitions */ +enum { + HDA_FIXUP_ACT_PRE_PROBE, + HDA_FIXUP_ACT_PROBE, + HDA_FIXUP_ACT_INIT, + HDA_FIXUP_ACT_BUILD, + HDA_FIXUP_ACT_FREE, +}; + +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); + + +/* * unsolicited event handler */ @@ -431,6 +518,8 @@ struct hda_bus_unsolicited { #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); @@ -470,6 +559,10 @@ snd_hda_set_pin_ctl_cache(struct hda_codec *codec, hda_nid_t pin, 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 */ @@ -499,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); @@ -531,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_PM) && 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) @@ -564,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 /* @@ -587,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 */ @@ -596,7 +708,8 @@ 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) @@ -629,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; @@ -647,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 045e5d32f5d..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"); } @@ -486,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, @@ -502,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 && @@ -519,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, @@ -565,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) { @@ -603,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) { @@ -620,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, @@ -657,9 +736,18 @@ 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: "); @@ -723,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); @@ -732,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, ¶m) != 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/patch_analog.c b/sound/pci/hda/patch_analog.c index 89fc5030ec7..06275f8807a 100644 --- a/sound/pci/hda/patch_analog.c +++ b/sound/pci/hda/patch_analog.c @@ -20,9 +20,7 @@ */ #include <linux/init.h> -#include <linux/delay.h> #include <linux/slab.h> -#include <linux/pci.h> #include <linux/module.h> #include <sound/core.h> @@ -31,126 +29,20 @@ #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 */ - unsigned int avoid_init_slave_vol:1; - -#ifdef CONFIG_PM - struct hda_loopback_check loopback; -#endif - /* for virtual master */ - hda_nid_t vmaster_nid; - const char * const *slave_vols; - const char * const *slave_sws; -}; - -/* - * input MUX handling (common part) - */ -static int ad198x_mux_enum_info(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *uinfo) -{ - struct hda_codec *codec = snd_kcontrol_chip(kcontrol); - struct ad198x_spec *spec = codec->spec; - - return snd_hda_input_mux_info(spec->input_mux, uinfo); -} - -static int ad198x_mux_enum_get(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) -{ - struct hda_codec *codec = snd_kcontrol_chip(kcontrol); - struct ad198x_spec *spec = codec->spec; - unsigned int adc_idx = snd_ctl_get_ioffidx(kcontrol, &ucontrol->id); - - ucontrol->value.enumerated.item[0] = spec->cur_mux[adc_idx]; - return 0; -} -static int ad198x_mux_enum_put(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) -{ - struct hda_codec *codec = snd_kcontrol_chip(kcontrol); - struct ad198x_spec *spec = codec->spec; - unsigned int adc_idx = snd_ctl_get_ioffidx(kcontrol, &ucontrol->id); - - return snd_hda_input_mux_put(codec, spec->input_mux, ucontrol, - spec->capsrc_nids[adc_idx], - &spec->cur_mux[adc_idx]); -} - -/* - * initialization (common callbacks) - */ -static int ad198x_init(struct hda_codec *codec) -{ - struct ad198x_spec *spec = codec->spec; - int i; - - for (i = 0; i < spec->num_init_verbs; i++) - snd_hda_sequence_write(codec, spec->init_verbs[i]); - return 0; -} +struct ad198x_spec { + struct hda_gen_spec gen; -static const char * const ad_slave_pfxs[] = { - "Front", "Surround", "Center", "LFE", "Side", - "Headphone", "Mono", "Speaker", "IEC958", - 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_pfxs[] = { - "Front", "Surround", "Center", "LFE", "Side", "IEC958", - NULL + unsigned int beep_amp; /* beep amp value, set via set_beep_amp() */ }; -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 */ @@ -160,439 +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_pfxs), - "Playback Volume", - !spec->avoid_init_slave_vol, NULL); - 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_pfxs), - "Playback Switch"); - 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_PM -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++; - codec->spdif_status_reset = 1; - 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) @@ -629,18 +130,6 @@ 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_free_kctls(codec); - kfree(spec); - snd_hda_detach_beep_device(codec); -} - #ifdef CONFIG_PM static int ad198x_suspend(struct hda_codec *codec) { @@ -649,752 +138,248 @@ static int ad198x_suspend(struct hda_codec *codec) } #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_PM - .check_power_status = ad198x_check_power_status, - .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_PM -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 alloc_ad_spec(struct hda_codec *codec) -{ - struct ad198x_spec *spec; - - spec = kzalloc(sizeof(*spec), GFP_KERNEL); - if (!spec) - return -ENOMEM; - codec->spec = spec; - snd_array_init(&spec->kctls, sizeof(struct snd_kcontrol_new), 32); - return 0; -} +/* + */ 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 + }; err = alloc_ad_spec(codec); if (err < 0) return err; spec = codec->spec; - err = snd_hda_attach_beep_device(codec, 0x19); - if (err < 0) { - ad198x_free(codec); - return err; - } - set_beep_amp(spec, 0x18, 0, HDA_OUTPUT); + /* AD1986A has the inverted EAPD implementation */ + codec->inv_eapd = 1; - 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_PM - 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; - } + spec->gen.mixer_nid = 0x07; + spec->gen.beep_nid = 0x19; + set_beep_amp(spec, 0x18, 0, HDA_OUTPUT); /* AD1986A has a hardware problem that it can't share a stream * with multiple output pins. The copy of front to surrounds @@ -1402,161 +387,112 @@ static int patch_ad1986a(struct hda_codec *codec) * 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_PM -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; err = alloc_ad_spec(codec); @@ -1564,37 +500,25 @@ static int patch_ad1983(struct hda_codec *codec) return err; spec = codec->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, 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_PM - spec->loopback.amplist = ad1983_loopbacks; -#endif - spec->vmaster_nid = 0x05; - - codec->patch_ops = ad198x_patch_ops; - - 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; } @@ -1602,452 +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_PM -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_set_pin_ctl(codec, 0x05, 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 }, - { "Dock Mic", 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("Dock Mic Playback Volume", 0x13, 0x0, HDA_OUTPUT), - HDA_CODEC_MUTE("Dock Mic 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, +static const struct hda_fixup ad1981_fixups[] = { + [AD1981_FIXUP_AMP_OVERRIDE] = { + .type = HDA_FIXUP_FUNC, + .v.func = ad1981_fixup_amp_override, }, - /* identical with AD1983 */ - { - .iface = SNDRV_CTL_ELEM_IFACE_MIXER, - .name = SNDRV_CTL_NAME_IEC958("",PLAYBACK,NONE) "Source", - .info = ad1983_spdif_route_info, - .get = ad1983_spdif_route_get, - .put = ad1983_spdif_route_put, + [AD1981_FIXUP_HP_EAPD] = { + .type = HDA_FIXUP_FUNC, + .v.func = ad1981_fixup_hp_eapd, + .chained = true, + .chain_id = AD1981_FIXUP_AMP_OVERRIDE, }, - { } /* end */ -}; - -static const struct hda_input_mux ad1981_thinkpad_capture_source = { - .num_items = 3, - .items = { - { "Mic", 0x0 }, - { "Mix", 0x2 }, - { "CD", 0x4 }, - }, -}; - -/* models */ -enum { - AD1981_BASIC, - AD1981_HP, - AD1981_THINKPAD, - AD1981_TOSHIBA, - AD1981_MODELS }; -static const char * const ad1981_models[AD1981_MODELS] = { - [AD1981_HP] = "hp", - [AD1981_THINKPAD] = "thinkpad", - [AD1981_BASIC] = "basic", - [AD1981_TOSHIBA] = "toshiba" -}; - -static 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; err = alloc_ad_spec(codec); if (err < 0) return -ENOMEM; spec = codec->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_PM - 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; } @@ -2136,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) { @@ -2249,1170 +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_PM -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; - - 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_set_pin_ctl(codec, nid, 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; - int val; - 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; - } - val = PIN_IN; - if (type == AUTO_PIN_MIC) - val |= snd_hda_get_default_vref(codec, nid); - snd_hda_set_pin_ctl(codec, nid, val); - 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; + int err; err = alloc_ad_spec(codec); if (err < 0) return err; spec = codec->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; - } - } - - err = snd_hda_attach_beep_device(codec, 0x10); - if (err < 0) { - ad198x_free(codec); - return err; - } + spec->gen.mixer_nid = 0x20; + spec->gen.mixer_merge_nid = 0x21; + spec->gen.beep_nid = 0x10; 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; - } - - if (spec->autocfg.hp_pins[0]) { - spec->mixers[spec->num_mixers++] = ad1988_hp_mixers; - spec->slave_vols = ad1988_6stack_fp_slave_pfxs; - spec->slave_sws = ad1988_6stack_fp_slave_pfxs; - spec->alt_dac_nid = ad1988_alt_dac_nid; - spec->stream_analog_alt_playback = - &ad198x_pcm_analog_alt_playback; - } - - 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_PM - 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; } @@ -3428,433 +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_PM -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", "Mic", "Mono", "Front Mic", "Mic", "CD", - "Internal Mic", "Dock Mic", /* "Beep", */ "IEC958", - NULL -}; - -static int patch_ad1884(struct hda_codec *codec) -{ - struct ad198x_spec *spec; - int err; - - err = alloc_ad_spec(codec); - if (err < 0) - return err; - spec = codec->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_PM - spec->loopback.amplist = ad1884_loopbacks; -#endif - spec->vmaster_nid = 0x04; - /* we need to cover all playback volumes */ - spec->slave_vols = ad1884_slave_vols; - /* slaves may contain input volumes, so we can't raise to 0dB blindly */ - spec->avoid_init_slave_vol = 1; - - 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 }, - { "Dock Mic", 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("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), - 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 @@ -3867,824 +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_PM -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; + int err; err = alloc_ad_spec(codec); if (err < 0) return err; spec = codec->spec; - err = snd_hda_attach_beep_device(codec, 0x10); - if (err < 0) { - ad198x_free(codec); - return err; - } + 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_PM - 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 @@ -4698,370 +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 */ -}; - -/* simple auto-mute control for AD1882 3-stack board */ -#define AD1882_HP_EVENT 0x01 - -static void ad1882_3stack_automute(struct hda_codec *codec) -{ - bool mute = snd_hda_jack_detect(codec, 0x11); - snd_hda_codec_write(codec, 0x12, 0, AC_VERB_SET_PIN_WIDGET_CONTROL, - mute ? 0 : PIN_OUT); -} - -static int ad1882_3stack_automute_init(struct hda_codec *codec) -{ - ad198x_init(codec); - ad1882_3stack_automute(codec); - return 0; -} - -static void ad1882_3stack_unsol_event(struct hda_codec *codec, unsigned int res) -{ - switch (res >> 26) { - case AD1882_HP_EVENT: - ad1882_3stack_automute(codec); - break; - } -} - -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 */ -}; - -static const struct hda_verb ad1882_3stack_automute_verbs[] = { - {0x11, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | AD1882_HP_EVENT}, - { } /* end */ -}; - -#ifdef CONFIG_PM -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_3STACK_AUTOMUTE, - AD1882_MODELS -}; - -static const char * const ad1882_models[AD1986A_MODELS] = { - [AD1882_3STACK] = "3stack", - [AD1882_6STACK] = "6stack", - [AD1882_3STACK_AUTOMUTE] = "3stack-automute", -}; - - static int patch_ad1882(struct hda_codec *codec) { struct ad198x_spec *spec; - int err, board_config; + int err; err = alloc_ad_spec(codec); if (err < 0) return err; spec = codec->spec; - err = snd_hda_attach_beep_device(codec, 0x10); - if (err < 0) { - ad198x_free(codec); - return err; - } + 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 = 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_PM - 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: - case AD1882_3STACK_AUTOMUTE: - 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; - if (board_config != AD1882_3STACK) { - spec->init_verbs[spec->num_init_verbs++] = - ad1882_3stack_automute_verbs; - codec->patch_ops.unsol_event = ad1882_3stack_unsol_event; - codec->patch_ops.init = ad1882_3stack_automute_init; - } - 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; - + 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; } @@ -5069,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 19ae14f739c..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_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) - 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_IN | - snd_hda_get_default_vref(codec, pin)); - 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 49750a96d64..092f2bd030b 100644 --- a/sound/pci/hda/patch_ca0132.c +++ b/sound/pci/hda/patch_ca0132.c @@ -24,19 +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 */ @@ -62,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, @@ -70,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 }; /* @@ -131,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 */ @@ -145,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 */ @@ -225,123 +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_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_IN | - snd_hda_get_default_vref(codec, pin)); - 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)); -} - -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); - if ((query_amp_caps(codec, nid, type) & AC_AMPCAP_MUTE) == 0) { - snd_printdd("Skipping '%s %s Switch' (no mute on node 0x%x)\n", pfx, dirstr[dir], nid); - return 0; - } - 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); - if ((query_amp_caps(codec, nid, type) & AC_AMPCAP_NUM_STEPS) == 0) { - snd_printdd("Skipping '%s %s Volume' (no amp on node 0x%x)\n", pfx, dirstr[dir], nid); - return 0; - } - 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 { @@ -349,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; } @@ -359,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); @@ -371,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 */ @@ -391,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 */ @@ -416,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; } @@ -446,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 */ @@ -472,17 +985,1682 @@ exit: } /* - * PCM callbacks + * Set chip control flags through the chip I/O widget. */ -static int ca0132_playback_pcm_open(struct hda_pcm_stream *hinfo, - struct hda_codec *codec, - struct snd_pcm_substream *substream) +static void chipio_set_control_flag(struct hda_codec *codec, + enum control_flag_id flag_id, + bool flag_state) +{ + unsigned int val; + unsigned int flag_bit; + + flag_bit = (flag_state ? 1 : 0); + val = (flag_bit << 7) | (flag_id); + snd_hda_codec_write(codec, WIDGET_CHIP_CTRL, 0, + VENDOR_CHIPIO_FLAG_SET, val); +} + +/* + * Set chip parameters through the chip I/O widget. + */ +static void chipio_set_control_param(struct hda_codec *codec, + enum control_param_id param_id, int param_val) +{ + struct ca0132_spec *spec = codec->spec; + int val; + + if ((param_id < 32) && (param_val < 8)) { + val = (param_val << 5) | (param_id); + snd_hda_codec_write(codec, WIDGET_CHIP_CTRL, 0, + VENDOR_CHIPIO_PARAM_SET, val); + } else { + mutex_lock(&spec->chipio_mutex); + if (chipio_send(codec, VENDOR_CHIPIO_STATUS, 0) == 0) { + snd_hda_codec_write(codec, WIDGET_CHIP_CTRL, 0, + VENDOR_CHIPIO_PARAM_EX_ID_SET, + param_id); + snd_hda_codec_write(codec, WIDGET_CHIP_CTRL, 0, + VENDOR_CHIPIO_PARAM_EX_VALUE_SET, + param_val); + } + mutex_unlock(&spec->chipio_mutex); + } +} + +/* + * Set sampling rate of the connection point. + */ +static void chipio_set_conn_rate(struct hda_codec *codec, + int connid, enum ca0132_sample_rate rate) +{ + chipio_set_control_param(codec, CONTROL_PARAM_CONN_POINT_ID, connid); + chipio_set_control_param(codec, CONTROL_PARAM_CONN_POINT_SAMPLE_RATE, + rate); +} + +/* + * Enable clocks. + */ +static void chipio_enable_clocks(struct hda_codec *codec) +{ + struct ca0132_spec *spec = codec->spec; + + mutex_lock(&spec->chipio_mutex); + snd_hda_codec_write(codec, WIDGET_CHIP_CTRL, 0, + VENDOR_CHIPIO_8051_ADDRESS_LOW, 0); + snd_hda_codec_write(codec, WIDGET_CHIP_CTRL, 0, + VENDOR_CHIPIO_PLL_PMU_WRITE, 0xff); + snd_hda_codec_write(codec, WIDGET_CHIP_CTRL, 0, + VENDOR_CHIPIO_8051_ADDRESS_LOW, 5); + snd_hda_codec_write(codec, WIDGET_CHIP_CTRL, 0, + VENDOR_CHIPIO_PLL_PMU_WRITE, 0x0b); + snd_hda_codec_write(codec, WIDGET_CHIP_CTRL, 0, + VENDOR_CHIPIO_8051_ADDRESS_LOW, 6); + snd_hda_codec_write(codec, WIDGET_CHIP_CTRL, 0, + VENDOR_CHIPIO_PLL_PMU_WRITE, 0xff); + mutex_unlock(&spec->chipio_mutex); +} + +/* + * CA0132 DSP IO stuffs + */ +static int dspio_send(struct hda_codec *codec, unsigned int reg, + unsigned int data) +{ + int res; + unsigned long timeout = jiffies + msecs_to_jiffies(1000); + + /* send bits of data specified by reg to dsp */ + do { + res = snd_hda_codec_read(codec, WIDGET_DSP_CTRL, 0, reg, data); + if ((res >= 0) && (res != VENDOR_STATUS_DSPIO_BUSY)) + return res; + msleep(20); + } while (time_before(jiffies, timeout)); + + return -EIO; +} + +/* + * Wait for DSP to be ready for commands + */ +static void dspio_write_wait(struct hda_codec *codec) +{ + int status; + unsigned long timeout = jiffies + msecs_to_jiffies(1000); + + do { + status = snd_hda_codec_read(codec, WIDGET_DSP_CTRL, 0, + VENDOR_DSPIO_STATUS, 0); + if ((status == VENDOR_STATUS_DSPIO_OK) || + (status == VENDOR_STATUS_DSPIO_SCP_RESPONSE_QUEUE_EMPTY)) + break; + msleep(1); + } while (time_before(jiffies, timeout)); +} + +/* + * Write SCP data to DSP + */ +static int dspio_write(struct hda_codec *codec, unsigned int scp_data) +{ + struct ca0132_spec *spec = codec->spec; + int status; + + dspio_write_wait(codec); + + mutex_lock(&spec->chipio_mutex); + status = dspio_send(codec, VENDOR_DSPIO_SCP_WRITE_DATA_LOW, + scp_data & 0xffff); + if (status < 0) + goto error; + + status = dspio_send(codec, VENDOR_DSPIO_SCP_WRITE_DATA_HIGH, + scp_data >> 16); + if (status < 0) + goto error; + + /* OK, now check if the write itself has executed*/ + status = snd_hda_codec_read(codec, WIDGET_DSP_CTRL, 0, + VENDOR_DSPIO_STATUS, 0); +error: + mutex_unlock(&spec->chipio_mutex); + + return (status == VENDOR_STATUS_DSPIO_SCP_COMMAND_QUEUE_FULL) ? + -EIO : 0; +} + +/* + * Write multiple SCP data to DSP + */ +static int dspio_write_multiple(struct hda_codec *codec, + unsigned int *buffer, unsigned int size) +{ + int status = 0; + unsigned int count; + + if ((buffer == NULL)) + return -EINVAL; + + count = 0; + while (count < size) { + status = dspio_write(codec, *buffer++); + if (status != 0) + break; + count++; + } + + return status; +} + +static int dspio_read(struct hda_codec *codec, unsigned int *data) +{ + int status; + + status = dspio_send(codec, VENDOR_DSPIO_SCP_POST_READ_DATA, 0); + if (status == -EIO) + return status; + + status = dspio_send(codec, VENDOR_DSPIO_STATUS, 0); + if (status == -EIO || + status == VENDOR_STATUS_DSPIO_SCP_RESPONSE_QUEUE_EMPTY) + return -EIO; + + *data = snd_hda_codec_read(codec, WIDGET_DSP_CTRL, 0, + VENDOR_DSPIO_SCP_READ_DATA, 0); + + return 0; +} + +static int dspio_read_multiple(struct hda_codec *codec, unsigned int *buffer, + unsigned int *buf_size, unsigned int size_count) +{ + int status = 0; + unsigned int size = *buf_size; + unsigned int count; + unsigned int skip_count; + unsigned int dummy; + + if ((buffer == NULL)) + return -1; + + count = 0; + while (count < size && count < size_count) { + status = dspio_read(codec, buffer++); + if (status != 0) + break; + count++; + } + + skip_count = count; + if (status == 0) { + while (skip_count < size) { + status = dspio_read(codec, &dummy); + if (status != 0) + break; + skip_count++; + } + } + *buf_size = count; + + return status; +} + +/* + * Construct the SCP header using corresponding fields + */ +static inline unsigned int +make_scp_header(unsigned int target_id, unsigned int source_id, + unsigned int get_flag, unsigned int req, + unsigned int device_flag, unsigned int resp_flag, + unsigned int error_flag, unsigned int data_size) +{ + unsigned int header = 0; + + header = (data_size & 0x1f) << 27; + header |= (error_flag & 0x01) << 26; + header |= (resp_flag & 0x01) << 25; + header |= (device_flag & 0x01) << 24; + header |= (req & 0x7f) << 17; + header |= (get_flag & 0x01) << 16; + header |= (source_id & 0xff) << 8; + header |= target_id & 0xff; + + return header; +} + +/* + * Extract corresponding fields from SCP header + */ +static inline void +extract_scp_header(unsigned int header, + unsigned int *target_id, unsigned int *source_id, + unsigned int *get_flag, unsigned int *req, + unsigned int *device_flag, unsigned int *resp_flag, + unsigned int *error_flag, unsigned int *data_size) +{ + if (data_size) + *data_size = (header >> 27) & 0x1f; + if (error_flag) + *error_flag = (header >> 26) & 0x01; + if (resp_flag) + *resp_flag = (header >> 25) & 0x01; + if (device_flag) + *device_flag = (header >> 24) & 0x01; + if (req) + *req = (header >> 17) & 0x7f; + if (get_flag) + *get_flag = (header >> 16) & 0x01; + if (source_id) + *source_id = (header >> 8) & 0xff; + if (target_id) + *target_id = header & 0xff; +} + +#define SCP_MAX_DATA_WORDS (16) + +/* Structure to contain any SCP message */ +struct scp_msg { + unsigned int hdr; + unsigned int data[SCP_MAX_DATA_WORDS]; +}; + +static void dspio_clear_response_queue(struct hda_codec *codec) +{ + unsigned int dummy = 0; + int status = -1; + + /* clear all from the response queue */ + do { + status = dspio_read(codec, &dummy); + } while (status == 0); +} + +static int dspio_get_response_data(struct hda_codec *codec) { struct ca0132_spec *spec = codec->spec; - return snd_hda_multi_out_analog_open(codec, &spec->multiout, substream, - hinfo); + unsigned int data = 0; + unsigned int count; + + if (dspio_read(codec, &data) < 0) + return -EIO; + + if ((data & 0x00ffffff) == spec->wait_scp_header) { + spec->scp_resp_header = data; + spec->scp_resp_count = data >> 27; + count = spec->wait_num_data; + dspio_read_multiple(codec, spec->scp_resp_data, + &spec->scp_resp_count, count); + return 0; + } + + return -EIO; } +/* + * Send SCP message to DSP + */ +static int dspio_send_scp_message(struct hda_codec *codec, + unsigned char *send_buf, + unsigned int send_buf_size, + unsigned char *return_buf, + unsigned int return_buf_size, + unsigned int *bytes_returned) +{ + struct ca0132_spec *spec = codec->spec; + int status = -1; + unsigned int scp_send_size = 0; + unsigned int total_size; + bool waiting_for_resp = false; + unsigned int header; + struct scp_msg *ret_msg; + unsigned int resp_src_id, resp_target_id; + unsigned int data_size, src_id, target_id, get_flag, device_flag; + + if (bytes_returned) + *bytes_returned = 0; + + /* get scp header from buffer */ + header = *((unsigned int *)send_buf); + extract_scp_header(header, &target_id, &src_id, &get_flag, NULL, + &device_flag, NULL, NULL, &data_size); + scp_send_size = data_size + 1; + total_size = (scp_send_size * 4); + + if (send_buf_size < total_size) + return -EINVAL; + + if (get_flag || device_flag) { + if (!return_buf || return_buf_size < 4 || !bytes_returned) + return -EINVAL; + + spec->wait_scp_header = *((unsigned int *)send_buf); + + /* swap source id with target id */ + resp_target_id = src_id; + resp_src_id = target_id; + spec->wait_scp_header &= 0xffff0000; + spec->wait_scp_header |= (resp_src_id << 8) | (resp_target_id); + spec->wait_num_data = return_buf_size/sizeof(unsigned int) - 1; + spec->wait_scp = 1; + waiting_for_resp = true; + } + + status = dspio_write_multiple(codec, (unsigned int *)send_buf, + scp_send_size); + if (status < 0) { + spec->wait_scp = 0; + return status; + } + + if (waiting_for_resp) { + unsigned long timeout = jiffies + msecs_to_jiffies(1000); + memset(return_buf, 0, return_buf_size); + do { + msleep(20); + } while (spec->wait_scp && time_before(jiffies, timeout)); + waiting_for_resp = false; + if (!spec->wait_scp) { + ret_msg = (struct scp_msg *)return_buf; + memcpy(&ret_msg->hdr, &spec->scp_resp_header, 4); + memcpy(&ret_msg->data, spec->scp_resp_data, + spec->wait_num_data); + *bytes_returned = (spec->scp_resp_count + 1) * 4; + status = 0; + } else { + status = -EIO; + } + spec->wait_scp = 0; + } + + return status; +} + +/** + * Prepare and send the SCP message to DSP + * @codec: the HDA codec + * @mod_id: ID of the DSP module to send the command + * @req: ID of request to send to the DSP module + * @dir: SET or GET + * @data: pointer to the data to send with the request, request specific + * @len: length of the data, in bytes + * @reply: point to the buffer to hold data returned for a reply + * @reply_len: length of the reply buffer returned from GET + * + * Returns zero or a negative error code. + */ +static int dspio_scp(struct hda_codec *codec, + int mod_id, int req, int dir, void *data, unsigned int len, + void *reply, unsigned int *reply_len) +{ + int status = 0; + struct scp_msg scp_send, scp_reply; + unsigned int ret_bytes, send_size, ret_size; + unsigned int send_get_flag, reply_resp_flag, reply_error_flag; + unsigned int reply_data_size; + + memset(&scp_send, 0, sizeof(scp_send)); + memset(&scp_reply, 0, sizeof(scp_reply)); + + if ((len != 0 && data == NULL) || (len > SCP_MAX_DATA_WORDS)) + return -EINVAL; + + if (dir == SCP_GET && reply == NULL) { + codec_dbg(codec, "dspio_scp get but has no buffer\n"); + return -EINVAL; + } + + if (reply != NULL && (reply_len == NULL || (*reply_len == 0))) { + codec_dbg(codec, "dspio_scp bad resp buf len parms\n"); + return -EINVAL; + } + + scp_send.hdr = make_scp_header(mod_id, 0x20, (dir == SCP_GET), req, + 0, 0, 0, len/sizeof(unsigned int)); + if (data != NULL && len > 0) { + len = min((unsigned int)(sizeof(scp_send.data)), len); + memcpy(scp_send.data, data, len); + } + + ret_bytes = 0; + send_size = sizeof(unsigned int) + len; + status = dspio_send_scp_message(codec, (unsigned char *)&scp_send, + send_size, (unsigned char *)&scp_reply, + sizeof(scp_reply), &ret_bytes); + + if (status < 0) { + codec_dbg(codec, "dspio_scp: send scp msg failed\n"); + return status; + } + + /* extract send and reply headers members */ + extract_scp_header(scp_send.hdr, NULL, NULL, &send_get_flag, + NULL, NULL, NULL, NULL, NULL); + extract_scp_header(scp_reply.hdr, NULL, NULL, NULL, NULL, NULL, + &reply_resp_flag, &reply_error_flag, + &reply_data_size); + + if (!send_get_flag) + return 0; + + if (reply_resp_flag && !reply_error_flag) { + ret_size = (ret_bytes - sizeof(scp_reply.hdr)) + / sizeof(unsigned int); + + if (*reply_len < ret_size*sizeof(unsigned int)) { + codec_dbg(codec, "reply too long for buf\n"); + return -EINVAL; + } else if (ret_size != reply_data_size) { + codec_dbg(codec, "RetLen and HdrLen .NE.\n"); + return -EINVAL; + } else { + *reply_len = ret_size*sizeof(unsigned int); + memcpy(reply, scp_reply.data, *reply_len); + } + } else { + codec_dbg(codec, "reply ill-formed or errflag set\n"); + return -EIO; + } + + return status; +} + +/* + * Set DSP parameters + */ +static int dspio_set_param(struct hda_codec *codec, int mod_id, + int req, void *data, unsigned int len) +{ + return dspio_scp(codec, mod_id, req, SCP_SET, data, len, NULL, NULL); +} + +static int dspio_set_uint_param(struct hda_codec *codec, int mod_id, + int req, unsigned int data) +{ + return dspio_set_param(codec, mod_id, req, &data, sizeof(unsigned int)); +} + +/* + * Allocate a DSP DMA channel via an SCP message + */ +static int dspio_alloc_dma_chan(struct hda_codec *codec, unsigned int *dma_chan) +{ + int status = 0; + unsigned int size = sizeof(dma_chan); + + codec_dbg(codec, " dspio_alloc_dma_chan() -- begin\n"); + status = dspio_scp(codec, MASTERCONTROL, MASTERCONTROL_ALLOC_DMA_CHAN, + SCP_GET, NULL, 0, dma_chan, &size); + + if (status < 0) { + codec_dbg(codec, "dspio_alloc_dma_chan: SCP Failed\n"); + return status; + } + + if ((*dma_chan + 1) == 0) { + codec_dbg(codec, "no free dma channels to allocate\n"); + return -EBUSY; + } + + codec_dbg(codec, "dspio_alloc_dma_chan: chan=%d\n", *dma_chan); + codec_dbg(codec, " dspio_alloc_dma_chan() -- complete\n"); + + return status; +} + +/* + * Free a DSP DMA via an SCP message + */ +static int dspio_free_dma_chan(struct hda_codec *codec, unsigned int dma_chan) +{ + int status = 0; + unsigned int dummy = 0; + + codec_dbg(codec, " dspio_free_dma_chan() -- begin\n"); + codec_dbg(codec, "dspio_free_dma_chan: chan=%d\n", dma_chan); + + status = dspio_scp(codec, MASTERCONTROL, MASTERCONTROL_ALLOC_DMA_CHAN, + SCP_SET, &dma_chan, sizeof(dma_chan), NULL, &dummy); + + if (status < 0) { + codec_dbg(codec, "dspio_free_dma_chan: SCP Failed\n"); + return status; + } + + codec_dbg(codec, " dspio_free_dma_chan() -- complete\n"); + + return status; +} + +/* + * (Re)start the DSP + */ +static int dsp_set_run_state(struct hda_codec *codec) +{ + unsigned int dbg_ctrl_reg; + unsigned int halt_state; + int err; + + err = chipio_read(codec, DSP_DBGCNTL_INST_OFFSET, &dbg_ctrl_reg); + if (err < 0) + return err; + + halt_state = (dbg_ctrl_reg & DSP_DBGCNTL_STATE_MASK) >> + DSP_DBGCNTL_STATE_LOBIT; + + if (halt_state != 0) { + dbg_ctrl_reg &= ~((halt_state << DSP_DBGCNTL_SS_LOBIT) & + DSP_DBGCNTL_SS_MASK); + err = chipio_write(codec, DSP_DBGCNTL_INST_OFFSET, + dbg_ctrl_reg); + if (err < 0) + return err; + + dbg_ctrl_reg |= (halt_state << DSP_DBGCNTL_EXEC_LOBIT) & + DSP_DBGCNTL_EXEC_MASK; + err = chipio_write(codec, DSP_DBGCNTL_INST_OFFSET, + dbg_ctrl_reg); + if (err < 0) + return err; + } + + return 0; +} + +/* + * Reset the DSP + */ +static int dsp_reset(struct hda_codec *codec) +{ + unsigned int res; + int retry = 20; + + codec_dbg(codec, "dsp_reset\n"); + do { + res = dspio_send(codec, VENDOR_DSPIO_DSP_INIT, 0); + retry--; + } while (res == -EIO && retry); + + if (!retry) { + codec_dbg(codec, "dsp_reset timeout\n"); + return -EIO; + } + + return 0; +} + +/* + * Convert chip address to DSP address + */ +static unsigned int dsp_chip_to_dsp_addx(unsigned int chip_addx, + bool *code, bool *yram) +{ + *code = *yram = false; + + if (UC_RANGE(chip_addx, 1)) { + *code = true; + return UC_OFF(chip_addx); + } else if (X_RANGE_ALL(chip_addx, 1)) { + return X_OFF(chip_addx); + } else if (Y_RANGE_ALL(chip_addx, 1)) { + *yram = true; + return Y_OFF(chip_addx); + } + + return INVALID_CHIP_ADDRESS; +} + +/* + * Check if the DSP DMA is active + */ +static bool dsp_is_dma_active(struct hda_codec *codec, unsigned int dma_chan) +{ + unsigned int dma_chnlstart_reg; + + chipio_read(codec, DSPDMAC_CHNLSTART_INST_OFFSET, &dma_chnlstart_reg); + + return ((dma_chnlstart_reg & (1 << + (DSPDMAC_CHNLSTART_EN_LOBIT + dma_chan))) != 0); +} + +static int dsp_dma_setup_common(struct hda_codec *codec, + unsigned int chip_addx, + unsigned int dma_chan, + unsigned int port_map_mask, + bool ovly) +{ + int status = 0; + unsigned int chnl_prop; + unsigned int dsp_addx; + unsigned int active; + bool code, yram; + + codec_dbg(codec, "-- dsp_dma_setup_common() -- Begin ---------\n"); + + if (dma_chan >= DSPDMAC_DMA_CFG_CHANNEL_COUNT) { + codec_dbg(codec, "dma chan num invalid\n"); + return -EINVAL; + } + + if (dsp_is_dma_active(codec, dma_chan)) { + codec_dbg(codec, "dma already active\n"); + return -EBUSY; + } + + dsp_addx = dsp_chip_to_dsp_addx(chip_addx, &code, &yram); + + if (dsp_addx == INVALID_CHIP_ADDRESS) { + codec_dbg(codec, "invalid chip addr\n"); + return -ENXIO; + } + + chnl_prop = DSPDMAC_CHNLPROP_AC_MASK; + active = 0; + + codec_dbg(codec, " dsp_dma_setup_common() start reg pgm\n"); + + if (ovly) { + status = chipio_read(codec, DSPDMAC_CHNLPROP_INST_OFFSET, + &chnl_prop); + + if (status < 0) { + codec_dbg(codec, "read CHNLPROP Reg fail\n"); + return status; + } + codec_dbg(codec, "dsp_dma_setup_common() Read CHNLPROP\n"); + } + + if (!code) + chnl_prop &= ~(1 << (DSPDMAC_CHNLPROP_MSPCE_LOBIT + dma_chan)); + else + chnl_prop |= (1 << (DSPDMAC_CHNLPROP_MSPCE_LOBIT + dma_chan)); + + chnl_prop &= ~(1 << (DSPDMAC_CHNLPROP_DCON_LOBIT + dma_chan)); + + status = chipio_write(codec, DSPDMAC_CHNLPROP_INST_OFFSET, chnl_prop); + if (status < 0) { + codec_dbg(codec, "write CHNLPROP Reg fail\n"); + return status; + } + codec_dbg(codec, " dsp_dma_setup_common() Write CHNLPROP\n"); + + if (ovly) { + status = chipio_read(codec, DSPDMAC_ACTIVE_INST_OFFSET, + &active); + + if (status < 0) { + codec_dbg(codec, "read ACTIVE Reg fail\n"); + return status; + } + codec_dbg(codec, "dsp_dma_setup_common() Read ACTIVE\n"); + } + + active &= (~(1 << (DSPDMAC_ACTIVE_AAR_LOBIT + dma_chan))) & + DSPDMAC_ACTIVE_AAR_MASK; + + status = chipio_write(codec, DSPDMAC_ACTIVE_INST_OFFSET, active); + if (status < 0) { + codec_dbg(codec, "write ACTIVE Reg fail\n"); + return status; + } + + codec_dbg(codec, " dsp_dma_setup_common() Write ACTIVE\n"); + + status = chipio_write(codec, DSPDMAC_AUDCHSEL_INST_OFFSET(dma_chan), + port_map_mask); + if (status < 0) { + codec_dbg(codec, "write AUDCHSEL Reg fail\n"); + return status; + } + codec_dbg(codec, " dsp_dma_setup_common() Write AUDCHSEL\n"); + + status = chipio_write(codec, DSPDMAC_IRQCNT_INST_OFFSET(dma_chan), + DSPDMAC_IRQCNT_BICNT_MASK | DSPDMAC_IRQCNT_CICNT_MASK); + if (status < 0) { + codec_dbg(codec, "write IRQCNT Reg fail\n"); + return status; + } + codec_dbg(codec, " dsp_dma_setup_common() Write IRQCNT\n"); + + codec_dbg(codec, + "ChipA=0x%x,DspA=0x%x,dmaCh=%u, " + "CHSEL=0x%x,CHPROP=0x%x,Active=0x%x\n", + chip_addx, dsp_addx, dma_chan, + port_map_mask, chnl_prop, active); + + codec_dbg(codec, "-- dsp_dma_setup_common() -- Complete ------\n"); + + return 0; +} + +/* + * Setup the DSP DMA per-transfer-specific registers + */ +static int dsp_dma_setup(struct hda_codec *codec, + unsigned int chip_addx, + unsigned int count, + unsigned int dma_chan) +{ + int status = 0; + bool code, yram; + unsigned int dsp_addx; + unsigned int addr_field; + unsigned int incr_field; + unsigned int base_cnt; + unsigned int cur_cnt; + unsigned int dma_cfg = 0; + unsigned int adr_ofs = 0; + unsigned int xfr_cnt = 0; + const unsigned int max_dma_count = 1 << (DSPDMAC_XFRCNT_BCNT_HIBIT - + DSPDMAC_XFRCNT_BCNT_LOBIT + 1); + + codec_dbg(codec, "-- dsp_dma_setup() -- Begin ---------\n"); + + if (count > max_dma_count) { + codec_dbg(codec, "count too big\n"); + return -EINVAL; + } + + dsp_addx = dsp_chip_to_dsp_addx(chip_addx, &code, &yram); + if (dsp_addx == INVALID_CHIP_ADDRESS) { + codec_dbg(codec, "invalid chip addr\n"); + return -ENXIO; + } + + codec_dbg(codec, " dsp_dma_setup() start reg pgm\n"); + + addr_field = dsp_addx << DSPDMAC_DMACFG_DBADR_LOBIT; + incr_field = 0; + + if (!code) { + addr_field <<= 1; + if (yram) + addr_field |= (1 << DSPDMAC_DMACFG_DBADR_LOBIT); + + incr_field = (1 << DSPDMAC_DMACFG_AINCR_LOBIT); + } + + dma_cfg = addr_field + incr_field; + status = chipio_write(codec, DSPDMAC_DMACFG_INST_OFFSET(dma_chan), + dma_cfg); + if (status < 0) { + codec_dbg(codec, "write DMACFG Reg fail\n"); + return status; + } + codec_dbg(codec, " dsp_dma_setup() Write DMACFG\n"); + + adr_ofs = (count - 1) << (DSPDMAC_DSPADROFS_BOFS_LOBIT + + (code ? 0 : 1)); + + status = chipio_write(codec, DSPDMAC_DSPADROFS_INST_OFFSET(dma_chan), + adr_ofs); + if (status < 0) { + codec_dbg(codec, "write DSPADROFS Reg fail\n"); + return status; + } + codec_dbg(codec, " dsp_dma_setup() Write DSPADROFS\n"); + + base_cnt = (count - 1) << DSPDMAC_XFRCNT_BCNT_LOBIT; + + cur_cnt = (count - 1) << DSPDMAC_XFRCNT_CCNT_LOBIT; + + xfr_cnt = base_cnt | cur_cnt; + + status = chipio_write(codec, + DSPDMAC_XFRCNT_INST_OFFSET(dma_chan), xfr_cnt); + if (status < 0) { + codec_dbg(codec, "write XFRCNT Reg fail\n"); + return status; + } + codec_dbg(codec, " dsp_dma_setup() Write XFRCNT\n"); + + codec_dbg(codec, + "ChipA=0x%x, cnt=0x%x, DMACFG=0x%x, " + "ADROFS=0x%x, XFRCNT=0x%x\n", + chip_addx, count, dma_cfg, adr_ofs, xfr_cnt); + + codec_dbg(codec, "-- dsp_dma_setup() -- Complete ---------\n"); + + return 0; +} + +/* + * Start the DSP DMA + */ +static int dsp_dma_start(struct hda_codec *codec, + unsigned int dma_chan, bool ovly) +{ + unsigned int reg = 0; + int status = 0; + + codec_dbg(codec, "-- dsp_dma_start() -- Begin ---------\n"); + + if (ovly) { + status = chipio_read(codec, + DSPDMAC_CHNLSTART_INST_OFFSET, ®); + + 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, ®); + + if (status < 0) { + codec_dbg(codec, "read CHNLSTART reg fail\n"); + return status; + } + codec_dbg(codec, "-- dsp_dma_stop() Read CHNLSTART\n"); + reg &= ~(DSPDMAC_CHNLSTART_EN_MASK | + DSPDMAC_CHNLSTART_DIS_MASK); + } + + status = chipio_write(codec, DSPDMAC_CHNLSTART_INST_OFFSET, + reg | (1 << (dma_chan + DSPDMAC_CHNLSTART_DIS_LOBIT))); + if (status < 0) { + codec_dbg(codec, "write CHNLSTART reg fail\n"); + return status; + } + codec_dbg(codec, "-- dsp_dma_stop() -- Complete ---------\n"); + + return status; +} + +/** + * Allocate router ports + * + * @codec: the HDA codec + * @num_chans: number of channels in the stream + * @ports_per_channel: number of ports per channel + * @start_device: start device + * @port_map: pointer to the port list to hold the allocated ports + * + * Returns zero or a negative error code. + */ +static int dsp_allocate_router_ports(struct hda_codec *codec, + unsigned int num_chans, + unsigned int ports_per_channel, + unsigned int start_device, + unsigned int *port_map) +{ + int status = 0; + int res; + u8 val; + + status = chipio_send(codec, VENDOR_CHIPIO_STATUS, 0); + if (status < 0) + return status; + + val = start_device << 6; + val |= (ports_per_channel - 1) << 4; + val |= num_chans - 1; + + snd_hda_codec_write(codec, WIDGET_CHIP_CTRL, 0, + VENDOR_CHIPIO_PORT_ALLOC_CONFIG_SET, + val); + + snd_hda_codec_write(codec, WIDGET_CHIP_CTRL, 0, + VENDOR_CHIPIO_PORT_ALLOC_SET, + MEM_CONNID_DSP); + + status = chipio_send(codec, VENDOR_CHIPIO_STATUS, 0); + if (status < 0) + return status; + + res = snd_hda_codec_read(codec, WIDGET_CHIP_CTRL, 0, + VENDOR_CHIPIO_PORT_ALLOC_GET, 0); + + *port_map = res; + + return (res < 0) ? res : 0; +} + +/* + * Free router ports + */ +static int dsp_free_router_ports(struct hda_codec *codec) +{ + int status = 0; + + status = chipio_send(codec, VENDOR_CHIPIO_STATUS, 0); + if (status < 0) + return status; + + snd_hda_codec_write(codec, WIDGET_CHIP_CTRL, 0, + VENDOR_CHIPIO_PORT_FREE_SET, + MEM_CONNID_DSP); + + status = chipio_send(codec, VENDOR_CHIPIO_STATUS, 0); + + return status; +} + +/* + * Allocate DSP ports for the download stream + */ +static int dsp_allocate_ports(struct hda_codec *codec, + unsigned int num_chans, + unsigned int rate_multi, unsigned int *port_map) +{ + int status; + + codec_dbg(codec, " dsp_allocate_ports() -- begin\n"); + + if ((rate_multi != 1) && (rate_multi != 2) && (rate_multi != 4)) { + codec_dbg(codec, "bad rate multiple\n"); + return -EINVAL; + } + + status = dsp_allocate_router_ports(codec, num_chans, + rate_multi, 0, port_map); + + codec_dbg(codec, " dsp_allocate_ports() -- complete\n"); + + return status; +} + +static int dsp_allocate_ports_format(struct hda_codec *codec, + const unsigned short fmt, + unsigned int *port_map) +{ + int status; + unsigned int num_chans; + + unsigned int sample_rate_div = ((get_hdafmt_rate(fmt) >> 0) & 3) + 1; + unsigned int sample_rate_mul = ((get_hdafmt_rate(fmt) >> 3) & 3) + 1; + unsigned int rate_multi = sample_rate_mul / sample_rate_div; + + if ((rate_multi != 1) && (rate_multi != 2) && (rate_multi != 4)) { + codec_dbg(codec, "bad rate multiple\n"); + return -EINVAL; + } + + num_chans = get_hdafmt_chs(fmt) + 1; + + status = dsp_allocate_ports(codec, num_chans, rate_multi, port_map); + + return status; +} + +/* + * free DSP ports + */ +static int dsp_free_ports(struct hda_codec *codec) +{ + int status; + + codec_dbg(codec, " dsp_free_ports() -- begin\n"); + + status = dsp_free_router_ports(codec); + if (status < 0) { + codec_dbg(codec, "free router ports fail\n"); + return status; + } + codec_dbg(codec, " dsp_free_ports() -- complete\n"); + + return status; +} + +/* + * HDA DMA engine stuffs for DSP code download + */ +struct dma_engine { + struct hda_codec *codec; + unsigned short m_converter_format; + struct snd_dma_buffer *dmab; + unsigned int buf_size; +}; + + +enum dma_state { + DMA_STATE_STOP = 0, + DMA_STATE_RUN = 1 +}; + +static int dma_convert_to_hda_format( + unsigned int sample_rate, + unsigned short channels, + unsigned short *hda_format) +{ + unsigned int format_val; + + format_val = snd_hda_calc_stream_format( + sample_rate, + channels, + SNDRV_PCM_FORMAT_S32_LE, + 32, 0); + + if (hda_format) + *hda_format = (unsigned short)format_val; + + return 0; +} + +/* + * Reset DMA for DSP download + */ +static int dma_reset(struct dma_engine *dma) +{ + struct hda_codec *codec = dma->codec; + struct ca0132_spec *spec = codec->spec; + int status; + + if (dma->dmab->area) + snd_hda_codec_load_dsp_cleanup(codec, dma->dmab); + + status = snd_hda_codec_load_dsp_prepare(codec, + dma->m_converter_format, + dma->buf_size, + dma->dmab); + if (status < 0) + return status; + spec->dsp_stream_id = status; + return 0; +} + +static int dma_set_state(struct dma_engine *dma, enum dma_state state) +{ + bool cmd; + + switch (state) { + case DMA_STATE_STOP: + cmd = false; + break; + case DMA_STATE_RUN: + cmd = true; + break; + default: + return 0; + } + + snd_hda_codec_load_dsp_trigger(dma->codec, cmd); + return 0; +} + +static unsigned int dma_get_buffer_size(struct dma_engine *dma) +{ + return dma->dmab->bytes; +} + +static unsigned char *dma_get_buffer_addr(struct dma_engine *dma) +{ + return dma->dmab->area; +} + +static int dma_xfer(struct dma_engine *dma, + const unsigned int *data, + unsigned int count) +{ + memcpy(dma->dmab->area, data, count); + return 0; +} + +static void dma_get_converter_format( + struct dma_engine *dma, + unsigned short *format) +{ + if (format) + *format = dma->m_converter_format; +} + +static unsigned int dma_get_stream_id(struct dma_engine *dma) +{ + struct ca0132_spec *spec = dma->codec->spec; + + return spec->dsp_stream_id; +} + +struct dsp_image_seg { + u32 magic; + u32 chip_addr; + u32 count; + u32 data[0]; +}; + +static const u32 g_magic_value = 0x4c46584d; +static const u32 g_chip_addr_magic_value = 0xFFFFFF01; + +static bool is_valid(const struct dsp_image_seg *p) +{ + return p->magic == g_magic_value; +} + +static bool is_hci_prog_list_seg(const struct dsp_image_seg *p) +{ + return g_chip_addr_magic_value == p->chip_addr; +} + +static bool is_last(const struct dsp_image_seg *p) +{ + return p->count == 0; +} + +static size_t dsp_sizeof(const struct dsp_image_seg *p) +{ + return sizeof(*p) + p->count*sizeof(u32); +} + +static const struct dsp_image_seg *get_next_seg_ptr( + const struct dsp_image_seg *p) +{ + return (struct dsp_image_seg *)((unsigned char *)(p) + dsp_sizeof(p)); +} + +/* + * CA0132 chip DSP transfer stuffs. For DSP download. + */ +#define INVALID_DMA_CHANNEL (~0U) + +/* + * Program a list of address/data pairs via the ChipIO widget. + * The segment data is in the format of successive pairs of words. + * These are repeated as indicated by the segment's count field. + */ +static int dspxfr_hci_write(struct hda_codec *codec, + const struct dsp_image_seg *fls) +{ + int status; + const u32 *data; + unsigned int count; + + if (fls == NULL || fls->chip_addr != g_chip_addr_magic_value) { + codec_dbg(codec, "hci_write invalid params\n"); + return -EINVAL; + } + + count = fls->count; + data = (u32 *)(fls->data); + while (count >= 2) { + status = chipio_write(codec, data[0], data[1]); + if (status < 0) { + codec_dbg(codec, "hci_write chipio failed\n"); + return status; + } + count -= 2; + data += 2; + } + return 0; +} + +/** + * Write a block of data into DSP code or data RAM using pre-allocated + * DMA engine. + * + * @codec: the HDA codec + * @fls: pointer to a fast load image + * @reloc: Relocation address for loading single-segment overlays, or 0 for + * no relocation + * @dma_engine: pointer to DMA engine to be used for DSP download + * @dma_chan: The number of DMA channels used for DSP download + * @port_map_mask: port mapping + * @ovly: TRUE if overlay format is required + * + * Returns zero or a negative error code. + */ +static int dspxfr_one_seg(struct hda_codec *codec, + const struct dsp_image_seg *fls, + unsigned int reloc, + struct dma_engine *dma_engine, + unsigned int dma_chan, + unsigned int port_map_mask, + bool ovly) +{ + int status = 0; + bool comm_dma_setup_done = false; + const unsigned int *data; + unsigned int chip_addx; + unsigned int words_to_write; + unsigned int buffer_size_words; + unsigned char *buffer_addx; + unsigned short hda_format; + unsigned int sample_rate_div; + unsigned int sample_rate_mul; + unsigned int num_chans; + unsigned int hda_frame_size_words; + unsigned int remainder_words; + const u32 *data_remainder; + u32 chip_addx_remainder; + unsigned int run_size_words; + const struct dsp_image_seg *hci_write = NULL; + unsigned long timeout; + bool dma_active; + + if (fls == NULL) + return -EINVAL; + if (is_hci_prog_list_seg(fls)) { + hci_write = fls; + fls = get_next_seg_ptr(fls); + } + + if (hci_write && (!fls || is_last(fls))) { + codec_dbg(codec, "hci_write\n"); + return dspxfr_hci_write(codec, hci_write); + } + + if (fls == NULL || dma_engine == NULL || port_map_mask == 0) { + codec_dbg(codec, "Invalid Params\n"); + return -EINVAL; + } + + data = fls->data; + chip_addx = fls->chip_addr, + words_to_write = fls->count; + + if (!words_to_write) + return hci_write ? dspxfr_hci_write(codec, hci_write) : 0; + if (reloc) + chip_addx = (chip_addx & (0xFFFF0000 << 2)) + (reloc << 2); + + if (!UC_RANGE(chip_addx, words_to_write) && + !X_RANGE_ALL(chip_addx, words_to_write) && + !Y_RANGE_ALL(chip_addx, words_to_write)) { + codec_dbg(codec, "Invalid chip_addx Params\n"); + return -EINVAL; + } + + buffer_size_words = (unsigned int)dma_get_buffer_size(dma_engine) / + sizeof(u32); + + buffer_addx = dma_get_buffer_addr(dma_engine); + + if (buffer_addx == NULL) { + codec_dbg(codec, "dma_engine buffer NULL\n"); + return -EINVAL; + } + + dma_get_converter_format(dma_engine, &hda_format); + sample_rate_div = ((get_hdafmt_rate(hda_format) >> 0) & 3) + 1; + sample_rate_mul = ((get_hdafmt_rate(hda_format) >> 3) & 3) + 1; + num_chans = get_hdafmt_chs(hda_format) + 1; + + hda_frame_size_words = ((sample_rate_div == 0) ? 0 : + (num_chans * sample_rate_mul / sample_rate_div)); + + if (hda_frame_size_words == 0) { + codec_dbg(codec, "frmsz zero\n"); + return -EINVAL; + } + + buffer_size_words = min(buffer_size_words, + (unsigned int)(UC_RANGE(chip_addx, 1) ? + 65536 : 32768)); + buffer_size_words -= buffer_size_words % hda_frame_size_words; + codec_dbg(codec, + "chpadr=0x%08x frmsz=%u nchan=%u " + "rate_mul=%u div=%u bufsz=%u\n", + chip_addx, hda_frame_size_words, num_chans, + sample_rate_mul, sample_rate_div, buffer_size_words); + + if (buffer_size_words < hda_frame_size_words) { + codec_dbg(codec, "dspxfr_one_seg:failed\n"); + return -EINVAL; + } + + remainder_words = words_to_write % hda_frame_size_words; + data_remainder = data; + chip_addx_remainder = chip_addx; + + data += remainder_words; + chip_addx += remainder_words*sizeof(u32); + words_to_write -= remainder_words; + + while (words_to_write != 0) { + run_size_words = min(buffer_size_words, words_to_write); + codec_dbg(codec, "dspxfr (seg loop)cnt=%u rs=%u remainder=%u\n", + words_to_write, run_size_words, remainder_words); + dma_xfer(dma_engine, data, run_size_words*sizeof(u32)); + if (!comm_dma_setup_done) { + status = dsp_dma_stop(codec, dma_chan, ovly); + if (status < 0) + return status; + status = dsp_dma_setup_common(codec, chip_addx, + dma_chan, port_map_mask, ovly); + if (status < 0) + return status; + comm_dma_setup_done = true; + } + + status = dsp_dma_setup(codec, chip_addx, + run_size_words, dma_chan); + if (status < 0) + return status; + status = dsp_dma_start(codec, dma_chan, ovly); + if (status < 0) + return status; + if (!dsp_is_dma_active(codec, dma_chan)) { + codec_dbg(codec, "dspxfr:DMA did not start\n"); + return -EIO; + } + status = dma_set_state(dma_engine, DMA_STATE_RUN); + if (status < 0) + return status; + if (remainder_words != 0) { + status = chipio_write_multiple(codec, + chip_addx_remainder, + data_remainder, + remainder_words); + if (status < 0) + return status; + remainder_words = 0; + } + if (hci_write) { + status = dspxfr_hci_write(codec, hci_write); + if (status < 0) + return status; + hci_write = NULL; + } + + timeout = jiffies + msecs_to_jiffies(2000); + do { + dma_active = dsp_is_dma_active(codec, dma_chan); + if (!dma_active) + break; + msleep(20); + } while (time_before(jiffies, timeout)); + if (dma_active) + break; + + codec_dbg(codec, "+++++ DMA complete\n"); + dma_set_state(dma_engine, DMA_STATE_STOP); + status = dma_reset(dma_engine); + + if (status < 0) + return status; + + data += run_size_words; + chip_addx += run_size_words*sizeof(u32); + words_to_write -= run_size_words; + } + + if (remainder_words != 0) { + status = chipio_write_multiple(codec, chip_addx_remainder, + data_remainder, remainder_words); + } + + return status; +} + +/** + * Write the entire DSP image of a DSP code/data overlay to DSP memories + * + * @codec: the HDA codec + * @fls_data: pointer to a fast load image + * @reloc: Relocation address for loading single-segment overlays, or 0 for + * no relocation + * @sample_rate: sampling rate of the stream used for DSP download + * @number_channels: channels of the stream used for DSP download + * @ovly: TRUE if overlay format is required + * + * Returns zero or a negative error code. + */ +static int dspxfr_image(struct hda_codec *codec, + const struct dsp_image_seg *fls_data, + unsigned int reloc, + unsigned int sample_rate, + unsigned short channels, + bool ovly) +{ + struct ca0132_spec *spec = codec->spec; + int status; + unsigned short hda_format = 0; + unsigned int response; + unsigned char stream_id = 0; + struct dma_engine *dma_engine; + unsigned int dma_chan; + unsigned int port_map_mask; + + if (fls_data == NULL) + return -EINVAL; + + dma_engine = kzalloc(sizeof(*dma_engine), GFP_KERNEL); + if (!dma_engine) + return -ENOMEM; + + dma_engine->dmab = kzalloc(sizeof(*dma_engine->dmab), GFP_KERNEL); + if (!dma_engine->dmab) { + kfree(dma_engine); + return -ENOMEM; + } + + dma_engine->codec = codec; + dma_convert_to_hda_format(sample_rate, channels, &hda_format); + dma_engine->m_converter_format = hda_format; + dma_engine->buf_size = (ovly ? DSP_DMA_WRITE_BUFLEN_OVLY : + DSP_DMA_WRITE_BUFLEN_INIT) * 2; + + dma_chan = ovly ? INVALID_DMA_CHANNEL : 0; + + status = codec_set_converter_format(codec, WIDGET_CHIP_CTRL, + hda_format, &response); + + if (status < 0) { + codec_dbg(codec, "set converter format fail\n"); + goto exit; + } + + status = snd_hda_codec_load_dsp_prepare(codec, + dma_engine->m_converter_format, + dma_engine->buf_size, + dma_engine->dmab); + if (status < 0) + goto exit; + spec->dsp_stream_id = status; + + if (ovly) { + status = dspio_alloc_dma_chan(codec, &dma_chan); + if (status < 0) { + codec_dbg(codec, "alloc dmachan fail\n"); + dma_chan = INVALID_DMA_CHANNEL; + goto exit; + } + } + + port_map_mask = 0; + status = dsp_allocate_ports_format(codec, hda_format, + &port_map_mask); + if (status < 0) { + codec_dbg(codec, "alloc ports fail\n"); + goto exit; + } + + stream_id = dma_get_stream_id(dma_engine); + status = codec_set_converter_stream_channel(codec, + WIDGET_CHIP_CTRL, stream_id, 0, &response); + if (status < 0) { + codec_dbg(codec, "set stream chan fail\n"); + goto exit; + } + + while ((fls_data != NULL) && !is_last(fls_data)) { + if (!is_valid(fls_data)) { + codec_dbg(codec, "FLS check fail\n"); + status = -EINVAL; + goto exit; + } + status = dspxfr_one_seg(codec, fls_data, reloc, + dma_engine, dma_chan, + port_map_mask, ovly); + if (status < 0) + break; + + if (is_hci_prog_list_seg(fls_data)) + fls_data = get_next_seg_ptr(fls_data); + + if ((fls_data != NULL) && !is_last(fls_data)) + fls_data = get_next_seg_ptr(fls_data); + } + + if (port_map_mask != 0) + status = dsp_free_ports(codec); + + if (status < 0) + goto exit; + + status = codec_set_converter_stream_channel(codec, + WIDGET_CHIP_CTRL, 0, 0, &response); + +exit: + if (ovly && (dma_chan != INVALID_DMA_CHANNEL)) + dspio_free_dma_chan(codec, dma_chan); + + if (dma_engine->dmab->area) + snd_hda_codec_load_dsp_cleanup(codec, dma_engine->dmab); + kfree(dma_engine->dmab); + kfree(dma_engine); + + return status; +} + +/* + * CA0132 DSP download stuffs. + */ +static void dspload_post_setup(struct hda_codec *codec) +{ + codec_dbg(codec, "---- dspload_post_setup ------\n"); + + /*set DSP speaker to 2.0 configuration*/ + chipio_write(codec, XRAM_XRAM_INST_OFFSET(0x18), 0x08080080); + chipio_write(codec, XRAM_XRAM_INST_OFFSET(0x19), 0x3f800000); + + /*update write pointer*/ + chipio_write(codec, XRAM_XRAM_INST_OFFSET(0x29), 0x00000002); +} + +/** + * Download DSP from a DSP Image Fast Load structure. This structure is a + * linear, non-constant sized element array of structures, each of which + * contain the count of the data to be loaded, the data itself, and the + * corresponding starting chip address of the starting data location. + * + * @codec: the HDA codec + * @fls: pointer to a fast load image + * @ovly: TRUE if overlay format is required + * @reloc: Relocation address for loading single-segment overlays, or 0 for + * no relocation + * @autostart: TRUE if DSP starts after loading; ignored if ovly is TRUE + * @router_chans: number of audio router channels to be allocated (0 means use + * internal defaults; max is 32) + * + * Returns zero or a negative error code. + */ +static int dspload_image(struct hda_codec *codec, + const struct dsp_image_seg *fls, + bool ovly, + unsigned int reloc, + bool autostart, + int router_chans) +{ + int status = 0; + unsigned int sample_rate; + unsigned short channels; + + codec_dbg(codec, "---- dspload_image begin ------\n"); + if (router_chans == 0) { + if (!ovly) + router_chans = DMA_TRANSFER_FRAME_SIZE_NWORDS; + else + router_chans = DMA_OVERLAY_FRAME_SIZE_NWORDS; + } + + sample_rate = 48000; + channels = (unsigned short)router_chans; + + while (channels > 16) { + sample_rate *= 2; + channels /= 2; + } + + do { + codec_dbg(codec, "Ready to program DMA\n"); + if (!ovly) + status = dsp_reset(codec); + + if (status < 0) + break; + + codec_dbg(codec, "dsp_reset() complete\n"); + status = dspxfr_image(codec, fls, reloc, sample_rate, channels, + ovly); + + if (status < 0) + break; + + codec_dbg(codec, "dspxfr_image() complete\n"); + if (autostart && !ovly) { + dspload_post_setup(codec); + status = dsp_set_run_state(codec); + } + + codec_dbg(codec, "LOAD FINISHED\n"); + } while (0); + + return status; +} + +#ifdef CONFIG_SND_HDA_CODEC_CA0132_DSP +static bool dspload_is_loaded(struct hda_codec *codec) +{ + unsigned int data = 0; + int status = 0; + + status = chipio_read(codec, 0x40004, &data); + if ((status < 0) || (data != 1)) + return false; + + return true; +} +#else +#define dspload_is_loaded(codec) false +#endif + +static bool dspload_wait_loaded(struct hda_codec *codec) +{ + unsigned long timeout = jiffies + msecs_to_jiffies(2000); + + do { + if (dspload_is_loaded(codec)) { + pr_info("ca0132 DOWNLOAD OK :-) DSP IS RUNNING.\n"); + return true; + } + msleep(20); + } while (time_before(jiffies, timeout)); + + pr_err("ca0132 DOWNLOAD FAILED!!! DSP IS NOT RUNNING.\n"); + return false; +} + +/* + * PCM callbacks + */ static int ca0132_playback_pcm_prepare(struct hda_pcm_stream *hinfo, struct hda_codec *codec, unsigned int stream_tag, @@ -490,8 +2668,10 @@ static int ca0132_playback_pcm_prepare(struct hda_pcm_stream *hinfo, struct snd_pcm_substream *substream) { struct ca0132_spec *spec = codec->spec; - return snd_hda_multi_out_analog_prepare(codec, &spec->multiout, - stream_tag, format, substream); + + snd_hda_codec_setup_stream(codec, spec->dacs[0], stream_tag, 0, format); + + return 0; } static int ca0132_playback_pcm_cleanup(struct hda_pcm_stream *hinfo, @@ -499,7 +2679,43 @@ static int ca0132_playback_pcm_cleanup(struct hda_pcm_stream *hinfo, struct snd_pcm_substream *substream) { struct ca0132_spec *spec = codec->spec; - return snd_hda_multi_out_analog_cleanup(codec, &spec->multiout); + + 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; } /* @@ -541,308 +2757,1212 @@ static int ca0132_dig_playback_pcm_close(struct hda_pcm_stream *hinfo, } /* + * Analog capture */ -static struct hda_pcm_stream ca0132_pcm_analog_playback = { - .substreams = 1, - .channels_min = 2, - .channels_max = 2, - .ops = { - .open = ca0132_playback_pcm_open, - .prepare = ca0132_playback_pcm_prepare, - .cleanup = ca0132_playback_pcm_cleanup - }, -}; +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); -static struct hda_pcm_stream ca0132_pcm_analog_capture = { - .substreams = 1, - .channels_min = 2, - .channels_max = 2, + return 0; +} + +static int ca0132_capture_pcm_cleanup(struct hda_pcm_stream *hinfo, + struct hda_codec *codec, + struct snd_pcm_substream *substream) +{ + struct ca0132_spec *spec = codec->spec; + + if (spec->dsp_state == DSP_DOWNLOADING) + return 0; + + snd_hda_codec_cleanup_stream(codec, hinfo->nid); + return 0; +} + +static unsigned int ca0132_capture_pcm_delay(struct hda_pcm_stream *info, + struct hda_codec *codec, + struct snd_pcm_substream *substream) +{ + struct ca0132_spec *spec = codec->spec; + unsigned int latency = DSP_CAPTURE_INIT_LATENCY; + struct snd_pcm_runtime *runtime = substream->runtime; + + if (spec->dsp_state != DSP_DOWNLOADED) + return 0; + + if (spec->effects_switch[CRYSTAL_VOICE - EFFECT_START_NID]) + latency += DSP_CRYSTAL_VOICE_LATENCY; + + return (latency * runtime->rate) / 1000; +} + +/* + * Controls stuffs. + */ + +/* + * Mixer controls helpers. + */ +#define CA0132_CODEC_VOL_MONO(xname, nid, channel, dir) \ + { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, \ + .name = xname, \ + .subdevice = HDA_SUBDEV_AMP_FLAG, \ + .access = SNDRV_CTL_ELEM_ACCESS_READWRITE | \ + SNDRV_CTL_ELEM_ACCESS_TLV_READ | \ + SNDRV_CTL_ELEM_ACCESS_TLV_CALLBACK, \ + .info = ca0132_volume_info, \ + .get = ca0132_volume_get, \ + .put = ca0132_volume_put, \ + .tlv = { .c = ca0132_volume_tlv }, \ + .private_value = HDA_COMPOSE_AMP_VAL(nid, channel, 0, dir) } + +#define CA0132_CODEC_MUTE_MONO(xname, nid, channel, dir) \ + { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, \ + .name = xname, \ + .subdevice = HDA_SUBDEV_AMP_FLAG, \ + .info = snd_hda_mixer_amp_switch_info, \ + .get = ca0132_switch_get, \ + .put = ca0132_switch_put, \ + .private_value = HDA_COMPOSE_AMP_VAL(nid, channel, 0, dir) } + +/* stereo */ +#define CA0132_CODEC_VOL(xname, nid, dir) \ + CA0132_CODEC_VOL_MONO(xname, nid, 3, dir) +#define CA0132_CODEC_MUTE(xname, nid, dir) \ + CA0132_CODEC_MUTE_MONO(xname, nid, 3, dir) + +/* The followings are for tuning of products */ +#ifdef ENABLE_TUNING_CONTROLS + +static unsigned int voice_focus_vals_lookup[] = { +0x41A00000, 0x41A80000, 0x41B00000, 0x41B80000, 0x41C00000, 0x41C80000, +0x41D00000, 0x41D80000, 0x41E00000, 0x41E80000, 0x41F00000, 0x41F80000, +0x42000000, 0x42040000, 0x42080000, 0x420C0000, 0x42100000, 0x42140000, +0x42180000, 0x421C0000, 0x42200000, 0x42240000, 0x42280000, 0x422C0000, +0x42300000, 0x42340000, 0x42380000, 0x423C0000, 0x42400000, 0x42440000, +0x42480000, 0x424C0000, 0x42500000, 0x42540000, 0x42580000, 0x425C0000, +0x42600000, 0x42640000, 0x42680000, 0x426C0000, 0x42700000, 0x42740000, +0x42780000, 0x427C0000, 0x42800000, 0x42820000, 0x42840000, 0x42860000, +0x42880000, 0x428A0000, 0x428C0000, 0x428E0000, 0x42900000, 0x42920000, +0x42940000, 0x42960000, 0x42980000, 0x429A0000, 0x429C0000, 0x429E0000, +0x42A00000, 0x42A20000, 0x42A40000, 0x42A60000, 0x42A80000, 0x42AA0000, +0x42AC0000, 0x42AE0000, 0x42B00000, 0x42B20000, 0x42B40000, 0x42B60000, +0x42B80000, 0x42BA0000, 0x42BC0000, 0x42BE0000, 0x42C00000, 0x42C20000, +0x42C40000, 0x42C60000, 0x42C80000, 0x42CA0000, 0x42CC0000, 0x42CE0000, +0x42D00000, 0x42D20000, 0x42D40000, 0x42D60000, 0x42D80000, 0x42DA0000, +0x42DC0000, 0x42DE0000, 0x42E00000, 0x42E20000, 0x42E40000, 0x42E60000, +0x42E80000, 0x42EA0000, 0x42EC0000, 0x42EE0000, 0x42F00000, 0x42F20000, +0x42F40000, 0x42F60000, 0x42F80000, 0x42FA0000, 0x42FC0000, 0x42FE0000, +0x43000000, 0x43010000, 0x43020000, 0x43030000, 0x43040000, 0x43050000, +0x43060000, 0x43070000, 0x43080000, 0x43090000, 0x430A0000, 0x430B0000, +0x430C0000, 0x430D0000, 0x430E0000, 0x430F0000, 0x43100000, 0x43110000, +0x43120000, 0x43130000, 0x43140000, 0x43150000, 0x43160000, 0x43170000, +0x43180000, 0x43190000, 0x431A0000, 0x431B0000, 0x431C0000, 0x431D0000, +0x431E0000, 0x431F0000, 0x43200000, 0x43210000, 0x43220000, 0x43230000, +0x43240000, 0x43250000, 0x43260000, 0x43270000, 0x43280000, 0x43290000, +0x432A0000, 0x432B0000, 0x432C0000, 0x432D0000, 0x432E0000, 0x432F0000, +0x43300000, 0x43310000, 0x43320000, 0x43330000, 0x43340000 }; -static 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 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, +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; + + return 0; +} -static int ca0132_hp_switch_get(struct snd_kcontrol *kcontrol, +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; } -static int ca0132_hp_volume_get(struct snd_kcontrol *kcontrol, +/* Check if Mic1 is streaming, if so, stop streaming */ +static int stop_mic1(struct hda_codec *codec) +{ + struct ca0132_spec *spec = codec->spec; + unsigned int oldval = snd_hda_codec_read(codec, spec->adcs[0], 0, + AC_VERB_GET_CONV, 0); + if (oldval != 0) + snd_hda_codec_write(codec, spec->adcs[0], 0, + AC_VERB_SET_CHANNEL_STREAMID, + 0); + return oldval; +} + +/* Resume Mic1 streaming if it was stopped. */ +static void resume_mic1(struct hda_codec *codec, unsigned int oldval) +{ + struct ca0132_spec *spec = codec->spec; + /* Restore the previous stream and channel */ + if (oldval != 0) + snd_hda_codec_write(codec, spec->adcs[0], 0, + AC_VERB_SET_CHANNEL_STREAMID, + oldval); +} + +/* + * Turn on/off CrystalVoice + */ +static int ca0132_cvoice_switch_set(struct hda_codec *codec) +{ + struct ca0132_spec *spec = codec->spec; + hda_nid_t nid; + int i, ret = 0; + unsigned int oldval; + + codec_dbg(codec, "ca0132_cvoice_switch_set: val=%ld\n", + spec->effects_switch[CRYSTAL_VOICE - EFFECT_START_NID]); + + i = IN_EFFECT_START_NID - EFFECT_START_NID; + nid = IN_EFFECT_START_NID; + /* CrystalVoice affects all in effects */ + for (; nid < IN_EFFECT_END_NID; nid++, i++) + ret |= ca0132_effects_set(codec, nid, spec->effects_switch[i]); + + /* including VoiceFX */ + ret |= ca0132_voicefx_set(codec, (spec->voicefx_val ? 1 : 0)); + + /* set correct vipsource */ + oldval = stop_mic1(codec); + ret |= ca0132_set_vipsource(codec, 1); + resume_mic1(codec, oldval); + return ret; +} + +static int ca0132_mic_boost_set(struct hda_codec *codec, long val) +{ + struct ca0132_spec *spec = codec->spec; + int ret = 0; + + if (val) /* on */ + ret = snd_hda_codec_amp_update(codec, spec->input_pins[0], 0, + HDA_INPUT, 0, HDA_AMP_VOLMASK, 3); + else /* off */ + ret = snd_hda_codec_amp_update(codec, spec->input_pins[0], 0, + HDA_INPUT, 0, HDA_AMP_VOLMASK, 0); + + return ret; +} + +static int ca0132_vnode_switch_set(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) { struct hda_codec *codec = snd_kcontrol_chip(kcontrol); + hda_nid_t nid = get_amp_nid(kcontrol); + hda_nid_t shared_nid = 0; + bool effective; + int ret = 0; struct ca0132_spec *spec = codec->spec; - 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_hp_volume_put(struct snd_kcontrol *kcontrol, +static int ca0132_voicefx_get(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) { struct hda_codec *codec = snd_kcontrol_chip(kcontrol); struct ca0132_spec *spec = codec->spec; + + ucontrol->value.enumerated.item[0] = spec->voicefx_val; + return 0; +} + +static int ca0132_voicefx_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct hda_codec *codec = snd_kcontrol_chip(kcontrol); + struct ca0132_spec *spec = codec->spec; + int i, err = 0; + int sel = ucontrol->value.enumerated.item[0]; + unsigned int items = sizeof(ca0132_voicefx_presets) + / sizeof(struct ct_voicefx_preset); + + if (sel >= items) + return 0; + + codec_dbg(codec, "ca0132_voicefx_put: sel=%d, preset=%s\n", + sel, ca0132_voicefx_presets[sel].name); + + /* + * Idx 0 is default. + * Default needs to qualify with CrystalVoice state. + */ + for (i = 0; i < VOICEFX_MAX_PARAM_COUNT; i++) { + err = dspio_set_uint_param(codec, ca0132_voicefx.mid, + ca0132_voicefx.reqs[i], + ca0132_voicefx_presets[sel].vals[i]); + if (err < 0) + break; + } + + if (err >= 0) { + spec->voicefx_val = sel; + /* enable voice fx */ + ca0132_voicefx_set(codec, (sel ? 1 : 0)); + } + + return 1; +} + +static int ca0132_switch_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct hda_codec *codec = snd_kcontrol_chip(kcontrol); + struct ca0132_spec *spec = codec->spec; + hda_nid_t nid = get_amp_nid(kcontrol); + int ch = get_amp_channels(kcontrol); long *valp = ucontrol->value.integer.value; - 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; - /* set mute-capable, 1db step, 32 steps, ofs 6 */ - caps = 0x80031f06; - snd_hda_override_amp_caps(codec, cfg->hp_pins[0], HDA_OUTPUT, caps); + switch (nid) { + case VNID_SPK: + /* follow shared_out tlv */ + nid = spec->shared_out_nid; + mutex_lock(&codec->control_mutex); + pval = kcontrol->private_value; + kcontrol->private_value = HDA_COMPOSE_AMP_VAL(nid, ch, 0, dir); + err = snd_hda_mixer_amp_tlv(kcontrol, op_flag, size, tlv); + kcontrol->private_value = pval; + mutex_unlock(&codec->control_mutex); + break; + case VNID_MIC: + /* follow shared_mic tlv */ + nid = spec->shared_mic_nid; + mutex_lock(&codec->control_mutex); + pval = kcontrol->private_value; + kcontrol->private_value = HDA_COMPOSE_AMP_VAL(nid, ch, 0, dir); + err = snd_hda_mixer_amp_tlv(kcontrol, op_flag, size, tlv); + kcontrol->private_value = pval; + mutex_unlock(&codec->control_mutex); + break; + default: + err = snd_hda_mixer_amp_tlv(kcontrol, op_flag, size, tlv); + } + return err; +} + +static int add_fx_switch(struct hda_codec *codec, hda_nid_t nid, + const char *pfx, int dir) +{ + char namestr[SNDRV_CTL_ELEM_ID_NAME_MAXLEN]; + int type = dir ? HDA_INPUT : HDA_OUTPUT; + struct snd_kcontrol_new knew = + CA0132_CODEC_MUTE_MONO(namestr, nid, 1, type); + sprintf(namestr, "%s %s Switch", pfx, dirstr[dir]); + return snd_hda_ctl_add(codec, nid, snd_ctl_new1(&knew, codec)); +} + +static int add_voicefx(struct hda_codec *codec) +{ + struct snd_kcontrol_new knew = + HDA_CODEC_MUTE_MONO(ca0132_voicefx.name, + VOICEFX, 1, 0, HDA_INPUT); + knew.info = ca0132_voicefx_info; + knew.get = ca0132_voicefx_get; + knew.put = ca0132_voicefx_put; + return snd_hda_ctl_add(codec, VOICEFX, snd_ctl_new1(&knew, codec)); } +/* + * When changing Node IDs for Mixer Controls below, make sure to update + * Node IDs in ca0132_config() as well. + */ +static struct snd_kcontrol_new ca0132_mixer[] = { + CA0132_CODEC_VOL("Master Playback Volume", VNID_SPK, HDA_OUTPUT), + CA0132_CODEC_MUTE("Master Playback Switch", VNID_SPK, HDA_OUTPUT), + CA0132_CODEC_VOL("Capture Volume", VNID_MIC, HDA_INPUT), + CA0132_CODEC_MUTE("Capture Switch", VNID_MIC, HDA_INPUT), + HDA_CODEC_VOLUME("Analog-Mic2 Capture Volume", 0x08, 0, HDA_INPUT), + HDA_CODEC_MUTE("Analog-Mic2 Capture Switch", 0x08, 0, HDA_INPUT), + HDA_CODEC_VOLUME("What U Hear Capture Volume", 0x0a, 0, HDA_INPUT), + HDA_CODEC_MUTE("What U Hear Capture Switch", 0x0a, 0, HDA_INPUT), + CA0132_CODEC_MUTE_MONO("Mic1-Boost (30dB) Capture Switch", + 0x12, 1, HDA_INPUT), + CA0132_CODEC_MUTE_MONO("HP/Speaker Playback Switch", + VNID_HP_SEL, 1, HDA_OUTPUT), + CA0132_CODEC_MUTE_MONO("AMic1/DMic Capture Switch", + VNID_AMIC1_SEL, 1, HDA_INPUT), + CA0132_CODEC_MUTE_MONO("HP/Speaker Auto Detect Playback Switch", + VNID_HP_ASEL, 1, HDA_OUTPUT), + CA0132_CODEC_MUTE_MONO("AMic1/DMic Auto Detect Capture Switch", + VNID_AMIC1_ASEL, 1, HDA_INPUT), + { } /* end */ +}; + static int ca0132_build_controls(struct hda_codec *codec) { struct ca0132_spec *spec = codec->spec; - 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, @@ -863,83 +3983,567 @@ static int ca0132_build_controls(struct hda_codec *codec) return 0; } +/* + * PCM + */ +static struct hda_pcm_stream ca0132_pcm_analog_playback = { + .substreams = 1, + .channels_min = 2, + .channels_max = 6, + .ops = { + .prepare = ca0132_playback_pcm_prepare, + .cleanup = ca0132_playback_pcm_cleanup, + .get_delay = ca0132_playback_pcm_delay, + }, +}; + +static struct hda_pcm_stream ca0132_pcm_analog_capture = { + .substreams = 1, + .channels_min = 2, + .channels_max = 2, + .ops = { + .prepare = ca0132_capture_pcm_prepare, + .cleanup = ca0132_capture_pcm_cleanup, + .get_delay = ca0132_capture_pcm_delay, + }, +}; + +static struct hda_pcm_stream ca0132_pcm_digital_playback = { + .substreams = 1, + .channels_min = 2, + .channels_max = 2, + .ops = { + .open = ca0132_dig_playback_pcm_open, + .close = ca0132_dig_playback_pcm_close, + .prepare = ca0132_dig_playback_pcm_prepare, + .cleanup = ca0132_dig_playback_pcm_cleanup + }, +}; + +static struct hda_pcm_stream ca0132_pcm_digital_capture = { + .substreams = 1, + .channels_min = 2, + .channels_max = 2, +}; + +static int ca0132_build_pcms(struct hda_codec *codec) +{ + struct ca0132_spec *spec = codec->spec; + struct hda_pcm *info = spec->pcm_rec; + + codec->pcm_info = info; + codec->num_pcms = 0; + + info->name = "CA0132 Analog"; + info->stream[SNDRV_PCM_STREAM_PLAYBACK] = ca0132_pcm_analog_playback; + info->stream[SNDRV_PCM_STREAM_PLAYBACK].nid = spec->dacs[0]; + info->stream[SNDRV_PCM_STREAM_PLAYBACK].channels_max = + spec->multiout.max_channels; + info->stream[SNDRV_PCM_STREAM_CAPTURE] = ca0132_pcm_analog_capture; + info->stream[SNDRV_PCM_STREAM_CAPTURE].substreams = 1; + info->stream[SNDRV_PCM_STREAM_CAPTURE].nid = spec->adcs[0]; + codec->num_pcms++; + + info++; + info->name = "CA0132 Analog Mic-In2"; + info->stream[SNDRV_PCM_STREAM_CAPTURE] = ca0132_pcm_analog_capture; + info->stream[SNDRV_PCM_STREAM_CAPTURE].substreams = 1; + info->stream[SNDRV_PCM_STREAM_CAPTURE].nid = spec->adcs[1]; + codec->num_pcms++; + + info++; + info->name = "CA0132 What U Hear"; + info->stream[SNDRV_PCM_STREAM_CAPTURE] = ca0132_pcm_analog_capture; + info->stream[SNDRV_PCM_STREAM_CAPTURE].substreams = 1; + info->stream[SNDRV_PCM_STREAM_CAPTURE].nid = spec->adcs[2]; + codec->num_pcms++; + + if (!spec->dig_out && !spec->dig_in) + return 0; + + info++; + info->name = "CA0132 Digital"; + info->pcm_type = HDA_PCM_TYPE_SPDIF; + if (spec->dig_out) { + info->stream[SNDRV_PCM_STREAM_PLAYBACK] = + ca0132_pcm_digital_playback; + info->stream[SNDRV_PCM_STREAM_PLAYBACK].nid = spec->dig_out; + } + if (spec->dig_in) { + info->stream[SNDRV_PCM_STREAM_CAPTURE] = + ca0132_pcm_digital_capture; + info->stream[SNDRV_PCM_STREAM_CAPTURE].nid = spec->dig_in; + } + codec->num_pcms++; + + return 0; +} + +static void init_output(struct hda_codec *codec, hda_nid_t pin, hda_nid_t dac) +{ + if (pin) { + snd_hda_set_pin_ctl(codec, pin, PIN_HP); + if (get_wcaps(codec, pin) & AC_WCAP_OUT_AMP) + snd_hda_codec_write(codec, pin, 0, + AC_VERB_SET_AMP_GAIN_MUTE, + AMP_OUT_UNMUTE); + } + if (dac && (get_wcaps(codec, dac) & AC_WCAP_OUT_AMP)) + snd_hda_codec_write(codec, dac, 0, + AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO); +} + +static void init_input(struct hda_codec *codec, hda_nid_t pin, hda_nid_t adc) +{ + if (pin) { + snd_hda_set_pin_ctl(codec, pin, PIN_VREF80); + if (get_wcaps(codec, pin) & AC_WCAP_IN_AMP) + snd_hda_codec_write(codec, pin, 0, + AC_VERB_SET_AMP_GAIN_MUTE, + AMP_IN_UNMUTE(0)); + } + if (adc && (get_wcaps(codec, adc) & AC_WCAP_IN_AMP)) { + snd_hda_codec_write(codec, adc, 0, AC_VERB_SET_AMP_GAIN_MUTE, + AMP_IN_UNMUTE(0)); + + /* init to 0 dB and unmute. */ + snd_hda_codec_amp_stereo(codec, adc, HDA_INPUT, 0, + HDA_AMP_VOLMASK, 0x5a); + snd_hda_codec_amp_stereo(codec, adc, HDA_INPUT, 0, + HDA_AMP_MUTE, 0); + } +} + +static void ca0132_init_unsol(struct hda_codec *codec) +{ + snd_hda_jack_detect_enable(codec, UNSOL_TAG_HP, UNSOL_TAG_HP); + snd_hda_jack_detect_enable(codec, UNSOL_TAG_AMIC1, UNSOL_TAG_AMIC1); +} -static void ca0132_set_ct_ext(struct hda_codec *codec, int enable) +static void refresh_amp_caps(struct hda_codec *codec, hda_nid_t nid, int dir) { - /* Set Creative extension */ - snd_printdd("SET CREATIVE EXTENSION\n"); + unsigned int caps; + + caps = snd_hda_param_read(codec, nid, dir == HDA_OUTPUT ? + AC_PAR_AMP_OUT_CAP : AC_PAR_AMP_IN_CAP); + snd_hda_override_amp_caps(codec, nid, dir, caps); +} + +/* + * Switch between Digital built-in mic and analog mic. + */ +static void ca0132_set_dmic(struct hda_codec *codec, int enable) +{ + struct ca0132_spec *spec = codec->spec; + unsigned int tmp; + u8 val; + unsigned int oldval; + + codec_dbg(codec, "ca0132_set_dmic: enable=%d\n", enable); + + oldval = stop_mic1(codec); + ca0132_set_vipsource(codec, 0); + if (enable) { + /* set DMic input as 2-ch */ + tmp = FLOAT_TWO; + dspio_set_uint_param(codec, 0x80, 0x00, tmp); + + val = spec->dmic_ctl; + val |= 0x80; + snd_hda_codec_write(codec, spec->input_pins[0], 0, + VENDOR_CHIPIO_DMIC_CTL_SET, val); + + if (!(spec->dmic_ctl & 0x20)) + chipio_set_control_flag(codec, CONTROL_FLAG_DMIC, 1); + } else { + /* set AMic input as mono */ + tmp = FLOAT_ONE; + dspio_set_uint_param(codec, 0x80, 0x00, tmp); + + val = spec->dmic_ctl; + /* clear bit7 and bit5 to disable dmic */ + val &= 0x5f; + snd_hda_codec_write(codec, spec->input_pins[0], 0, + VENDOR_CHIPIO_DMIC_CTL_SET, val); + + if (!(spec->dmic_ctl & 0x20)) + chipio_set_control_flag(codec, CONTROL_FLAG_DMIC, 0); + } + ca0132_set_vipsource(codec, 1); + resume_mic1(codec, oldval); +} + +/* + * Initialization for Digital Mic. + */ +static void ca0132_init_dmic(struct hda_codec *codec) +{ + struct ca0132_spec *spec = codec->spec; + u8 val; + + /* Setup Digital Mic here, but don't enable. + * Enable based on jack detect. + */ + + /* MCLK uses MPIO1, set to enable. + * Bit 2-0: MPIO select + * Bit 3: set to disable + * Bit 7-4: reserved + */ + val = 0x01; + snd_hda_codec_write(codec, spec->input_pins[0], 0, + VENDOR_CHIPIO_DMIC_MCLK_SET, val); + + /* Data1 uses MPIO3. Data2 not use + * Bit 2-0: Data1 MPIO select + * Bit 3: set disable Data1 + * Bit 6-4: Data2 MPIO select + * Bit 7: set disable Data2 + */ + val = 0x83; + snd_hda_codec_write(codec, spec->input_pins[0], 0, + VENDOR_CHIPIO_DMIC_PIN_SET, val); + + /* Use Ch-0 and Ch-1. Rate is 48K, mode 1. Disable DMic first. + * Bit 3-0: Channel mask + * Bit 4: set for 48KHz, clear for 32KHz + * Bit 5: mode + * Bit 6: set to select Data2, clear for Data1 + * Bit 7: set to enable DMic, clear for AMic + */ + val = 0x23; + /* keep a copy of dmic ctl val for enable/disable dmic purpuse */ + spec->dmic_ctl = val; + snd_hda_codec_write(codec, spec->input_pins[0], 0, + VENDOR_CHIPIO_DMIC_CTL_SET, val); +} + +/* + * Initialization for Analog Mic 2 + */ +static void ca0132_init_analog_mic2(struct hda_codec *codec) +{ + struct ca0132_spec *spec = codec->spec; + + mutex_lock(&spec->chipio_mutex); + snd_hda_codec_write(codec, WIDGET_CHIP_CTRL, 0, + VENDOR_CHIPIO_8051_ADDRESS_LOW, 0x20); + snd_hda_codec_write(codec, WIDGET_CHIP_CTRL, 0, + VENDOR_CHIPIO_8051_ADDRESS_HIGH, 0x19); + snd_hda_codec_write(codec, WIDGET_CHIP_CTRL, 0, + VENDOR_CHIPIO_8051_DATA_WRITE, 0x00); + snd_hda_codec_write(codec, WIDGET_CHIP_CTRL, 0, + VENDOR_CHIPIO_8051_ADDRESS_LOW, 0x2D); + snd_hda_codec_write(codec, WIDGET_CHIP_CTRL, 0, + VENDOR_CHIPIO_8051_ADDRESS_HIGH, 0x19); snd_hda_codec_write(codec, WIDGET_CHIP_CTRL, 0, - VENDOR_CHIPIO_CT_EXTENSIONS_ENABLE, - enable); - msleep(20); + 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; + + if (spec->dsp_state != DSP_DOWNLOADED) + return; + + /* out, in effects + voicefx */ + num_fx = OUT_EFFECTS_COUNT + IN_EFFECTS_COUNT + 1; + for (idx = 0; idx < num_fx; idx++) { + for (i = 0; i <= ca0132_effects[idx].params; i++) { + dspio_set_uint_param(codec, ca0132_effects[idx].mid, + ca0132_effects[idx].reqs[i], + ca0132_effects[idx].def_vals[i]); + } + } - codec->pcm_format_first = 1; - codec->no_sticky_stream = 1; + /*remove DSP headroom*/ + tmp = FLOAT_ZERO; + dspio_set_uint_param(codec, 0x96, 0x3C, tmp); - /* line-outs */ - cfg->line_outs = 1; - cfg->line_out_pins[0] = 0x0b; /* front */ - cfg->line_out_type = AUTO_PIN_LINE_OUT; + /*set speaker EQ bypass attenuation*/ + dspio_set_uint_param(codec, 0x8f, 0x01, tmp); - 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; + /* 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); - /* headphone */ - cfg->hp_outs = 1; - cfg->hp_pins[0] = 0x0f; + /* set AMic1 as CrystalVoice input */ + tmp = FLOAT_ONE; + dspio_set_uint_param(codec, 0x80, 0x05, tmp); - spec->hp_dac = 0; - spec->multiout.hp_nid = 0; + /* set WUH source */ + tmp = FLOAT_TWO; + dspio_set_uint_param(codec, 0x31, 0x00, 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; +/* + * 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); +} - /* Mic-in */ - spec->input_pins[0] = 0x12; - spec->input_labels[0] = "Mic"; - spec->adcs[0] = 0x07; +/* + * 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); +} - /* Line-In */ - spec->input_pins[1] = 0x11; - spec->input_labels[1] = "Line"; - spec->adcs[1] = 0x08; - spec->num_inputs = 2; +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); +} - /* 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 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) @@ -948,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++) @@ -960,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); } @@ -978,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; } @@ -1013,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 a2537b2f872..387f0b55188 100644 --- a/sound/pci/hda/patch_cirrus.c +++ b/sound/pci/hda/patch_cirrus.c @@ -19,16 +19,15 @@ */ #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" /* */ @@ -36,45 +35,21 @@ struct cs_spec { struct hda_gen_spec gen; - 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]; - unsigned int gpio_mask; unsigned int gpio_dir; unsigned int gpio_data; 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; - unsigned int speaker_2_1: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 */ @@ -86,6 +61,7 @@ enum { CS420X_GPIO_23, CS420X_MBP101, CS420X_MBP81, + CS420X_MBA42, CS420X_AUTO, /* aliases */ CS420X_IMAC27_122 = CS420X_GPIO_23, @@ -96,6 +72,7 @@ enum { enum { CS421X_CDB4210, CS421X_SENSE_B, + CS421X_STUMPY, }; /* Vendor-specific processing widget */ @@ -137,6 +114,9 @@ enum { /* 0x0009 - 0x0014 -> 12 test regs */ /* 0x0015 - visibility reg */ +/* Cirrus Logic CS4208 */ +#define CS4208_VENDOR_NID 0x24 + /* * Cirrus Logic CS4210 * @@ -180,915 +160,43 @@ 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; - if (spec->speaker_2_1) - info->stream[SNDRV_PCM_STREAM_PLAYBACK].chmap = - snd_pcm_2_1_chmaps; - 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; - - if (cfg->line_out_type == AUTO_PIN_SPEAKER_OUT && i == 2) - spec->speaker_2_1 = 1; /* assume 2.1 speakers */ - - /* 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; - memset(cfg->line_out_pins, 0, sizeof(cfg->line_out_pins)); - } - - 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 (spec->speaker_2_1) - name = idx ? "Bass Speaker" : "Speaker"; - else 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_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; - 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 * HP/SPK/SPDIF */ -static void cs_automute(struct hda_codec *codec, struct hda_jack_tbl *tbl) +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_set_pin_ctl(codec, nid, 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_set_pin_ctl(codec, nid, - (spdif_present && spec->sense_b) ? 0 : PIN_HP); - } - - /* SPDIF TX on/off */ - if (cfg->dig_outs) { - nid = cfg->dig_out_pins[0]; - snd_hda_set_pin_ctl(codec, nid, - 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 hda_jack_tbl *tbl) -{ - 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_set_pin_ctl(codec, cfg->line_out_pins[i], PIN_OUT); - /* HP */ - for (i = 0; i < cfg->hp_outs; i++) { - hda_nid_t nid = cfg->hp_pins[i]; - snd_hda_set_pin_ctl(codec, nid, PIN_HP); - if (!cfg->speaker_outs) - continue; - if (get_wcaps(codec, nid) & AC_WCAP_UNSOL_CAP) { - snd_hda_jack_detect_enable_callback(codec, nid, HP_EVENT, cs_automute); - spec->hp_detect = 1; - } - } - - /* Speaker */ - for (i = 0; i < cfg->speaker_outs; i++) - snd_hda_set_pin_ctl(codec, cfg->speaker_pins[i], PIN_OUT); - - /* SPDIF is enabled on presence detect for CS421x */ - if (spec->hp_detect || spec->spdif_detect) - cs_automute(codec, NULL); + 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) - ctl |= snd_hda_get_default_vref(codec, pin); - snd_hda_set_pin_ctl(codec, pin, 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_callback(codec, pin, MIC_EVENT, cs_automic); - } /* 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, NULL); - coef = cs_vendor_coef_get(codec, IDX_BEEP_CFG); if (is_active_pin(codec, CS_DMIC2_PIN_NID)) coef |= 1 << 4; /* DMIC2 2 chan on, GPIO1 off */ @@ -1099,13 +207,6 @@ static void init_input(struct hda_codec *codec) */ cs_vendor_coef_set(codec, IDX_BEEP_CFG, coef); - } else { - if (spec->mic_detect) - cs_automic(codec, NULL); - else { - spec->cur_adc = spec->adc_nid[spec->cur_input]; - cs_update_input_select(codec); - } } } @@ -1128,6 +229,16 @@ static const struct hda_verb cs_coef_init_verbs[] = { {} /* 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 @@ -1178,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; @@ -1196,12 +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); - - snd_hda_sequence_write(codec, cs_coef_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_gen_apply_verbs(codec); + snd_hda_gen_init(codec); if (spec->gpio_mask) { snd_hda_codec_write(codec, 0x01, 0, AC_VERB_SET_GPIO_MASK, @@ -1212,53 +326,30 @@ static int cs_init(struct hda_codec *codec) spec->gpio_data); } - init_output(codec); - init_input(codec); - init_digital(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); - 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); + err = snd_hda_gen_build_controls(codec); 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]); - snd_hda_gen_free(&spec->gen); - kfree(codec->spec); -} +#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 = snd_hda_jack_unsol_event, @@ -1269,22 +360,14 @@ 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; } @@ -1296,6 +379,7 @@ static const struct hda_model_fixup cs420x_models[] = { { .id = CS420X_APPLE, .name = "apple" }, { .id = CS420X_MBP101, .name = "mbp101" }, { .id = CS420X_MBP81, .name = "mbp81" }, + { .id = CS420X_MBA42, .name = "mba42" }, {} }; @@ -1311,6 +395,7 @@ static const struct snd_pci_quirk cs420x_fixup_tbl[] = { 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 */ }; @@ -1364,6 +449,43 @@ static const struct hda_pintbl mbp101_pincfgs[] = { {} /* terminator */ }; +static const struct hda_pintbl mba42_pincfgs[] = { + { 0x09, 0x012b4030 }, /* HP */ + { 0x0a, 0x400000f0 }, + { 0x0b, 0x90100120 }, /* speaker */ + { 0x0c, 0x400000f0 }, + { 0x0d, 0x90a00110 }, /* mic */ + { 0x0e, 0x400000f0 }, + { 0x0f, 0x400000f0 }, + { 0x10, 0x400000f0 }, + { 0x12, 0x400000f0 }, + { 0x15, 0x400000f0 }, + {} /* terminator */ +}; + +static const struct hda_pintbl mba6_pincfgs[] = { + { 0x10, 0x032120f0 }, /* HP */ + { 0x11, 0x500000f0 }, + { 0x12, 0x90100010 }, /* Speaker */ + { 0x13, 0x500000f0 }, + { 0x14, 0x500000f0 }, + { 0x15, 0x770000f0 }, + { 0x16, 0x770000f0 }, + { 0x17, 0x430000f0 }, + { 0x18, 0x43ab9030 }, /* Mic */ + { 0x19, 0x770000f0 }, + { 0x1a, 0x770000f0 }, + { 0x1b, 0x770000f0 }, + { 0x1c, 0x90a00090 }, + { 0x1d, 0x500000f0 }, + { 0x1e, 0x500000f0 }, + { 0x1f, 0x500000f0 }, + { 0x20, 0x500000f0 }, + { 0x21, 0x430000f0 }, + { 0x22, 0x430000f0 }, + {} /* terminator */ +}; + static void cs420x_fixup_gpio_13(struct hda_codec *codec, const struct hda_fixup *fix, int action) { @@ -1432,20 +554,38 @@ static const struct hda_fixup cs420x_fixups[] = { .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) { 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; - snd_hda_gen_init(&spec->gen); - spec->vendor_nid = CS420X_VENDOR_NID; + spec->gen.automute_hook = cs_automute; snd_hda_pick_fixup(codec, cs420x_models, cs420x_fixup_tbl, cs420x_fixups); @@ -1463,7 +603,165 @@ static int patch_cs420x(struct hda_codec *codec) error: cs_free(codec); - codec->spec = NULL; + 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; + } +} + +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) + 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; } @@ -1478,6 +776,7 @@ static int patch_cs420x(struct hda_codec *codec) /* CS4210 board names */ static const struct hda_model_fixup cs421x_models[] = { { .id = CS421X_CDB4210, .name = "cdb4210" }, + { .id = CS421X_STUMPY, .name = "stumpy" }, {} }; @@ -1499,6 +798,17 @@ static const struct hda_pintbl cdb4210_pincfgs[] = { {} /* terminator */ }; +/* 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) @@ -1518,7 +828,11 @@ static const struct hda_fixup cs421x_fixups[] = { [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[] = { @@ -1622,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 | @@ -1667,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_callback(codec, nid, SPDIF_EVENT, cs_automute); spec->spdif_detect = 1; + snd_hda_jack_detect_enable_callback(codec, nid, + SPDIF_EVENT, + cs4210_spdif_automute); } } } @@ -1695,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); @@ -1704,233 +1044,61 @@ static int cs421x_init(struct hda_codec *codec) spec->gpio_data); } - init_output(codec); - init_input(codec); - init_cs421x_digital(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 const 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); - - err = cs421x_add_input_volume_control(codec, i); - if (err < 0) - return err; - } + init_input_coef(codec); - /* - 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); - if (err < 0) - return err; - err = add_volume(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; - 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 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; } @@ -1963,7 +1131,7 @@ static int cs421x_suspend(struct hda_codec *codec) 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 = snd_hda_jack_unsol_event, @@ -1977,13 +1145,11 @@ 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; - snd_hda_gen_init(&spec->gen); - spec->vendor_nid = CS4210_VENDOR_NID; + spec->gen.automute_hook = cs_automute; snd_hda_pick_fixup(codec, cs421x_models, cs421x_fixup_tbl, cs421x_fixups); @@ -2008,7 +1174,6 @@ static int patch_cs4210(struct hda_codec *codec) error: cs_free(codec); - codec->spec = NULL; return err; } @@ -2017,13 +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; - snd_hda_gen_init(&spec->gen); - - spec->vendor_nid = CS4213_VENDOR_NID; err = cs421x_parse_auto_config(codec); if (err < 0) @@ -2034,7 +1195,6 @@ static int patch_cs4213(struct hda_codec *codec) error: cs_free(codec); - codec->spec = NULL; return err; } @@ -2045,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 */ @@ -2052,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 c8fdaaefe70..061ea5965dd 100644 --- a/sound/pci/hda/patch_cmedia.c +++ b/sound/pci/hda/patch_cmedia.c @@ -22,14 +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 @@ -43,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 */ @@ -75,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 */ @@ -356,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; @@ -631,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) { @@ -641,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; @@ -678,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; @@ -741,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 009b77a693c..1dc7e974f3b 100644 --- a/sound/pci/hda/patch_conexant.c +++ b/sound/pci/hda/patch_conexant.c @@ -23,7 +23,6 @@ #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> @@ -33,6 +32,9 @@ #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 @@ -53,27 +55,28 @@ #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 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 */ -}; - struct conexant_spec { struct hda_gen_spec gen; + unsigned int beep_amp; + + /* extra EAPD pins */ + unsigned int num_eapds; + hda_nid_t eapds[4]; + bool dynamic_eapd; + + 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; - struct hda_vmaster_mute_hook vmaster_mute; - bool vmaster_mute_led; const struct hda_verb *init_verbs[5]; /* initialization verbs * don't forget NULL @@ -90,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; @@ -122,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 fixup_stereo_dmic: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) @@ -239,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; @@ -252,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; } @@ -337,8 +339,6 @@ static const struct hda_pcm_stream cx5051_pcm_analog_capture = { }, }; -static bool is_2_1_speaker(struct conexant_spec *spec); - static int conexant_build_pcms(struct hda_codec *codec) { struct conexant_spec *spec = codec->spec; @@ -353,9 +353,6 @@ static int conexant_build_pcms(struct hda_codec *codec) spec->multiout.max_channels; info->stream[SNDRV_PCM_STREAM_PLAYBACK].nid = spec->multiout.dac_nids[0]; - if (is_2_1_speaker(spec)) - info->stream[SNDRV_PCM_STREAM_PLAYBACK].chmap = - snd_pcm_2_1_chmaps; if (spec->capture_stream) info->stream[SNDRV_PCM_STREAM_CAPTURE] = *spec->capture_stream; else { @@ -386,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; @@ -435,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) @@ -450,10 +445,7 @@ static int conexant_init(struct hda_codec *codec) static void conexant_free(struct hda_codec *codec) { - struct conexant_spec *spec = codec->spec; - snd_hda_gen_free(&spec->gen); - snd_hda_detach_beep_device(codec); - kfree(spec); + kfree(codec->spec); } static const struct snd_kcontrol_new cxt_capture_mixers[] = { @@ -467,15 +459,6 @@ 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_pfxs[] = { "Headphone", "Speaker", "Bass Speaker", "Front", "Surround", "CLFE", NULL @@ -524,10 +507,9 @@ static int conexant_build_controls(struct hda_codec *codec) } if (spec->vmaster_nid && !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); + err = snd_hda_add_vmaster(codec, "Master Playback Switch", + NULL, slave_pfxs, + "Playback Switch"); if (err < 0) return err; } @@ -538,22 +520,9 @@ 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 + err = add_beep_ctls(codec); + if (err < 0) + return err; return 0; } @@ -566,13 +535,6 @@ static const struct hda_codec_ops conexant_patch_ops = { .set_power_state = conexant_set_power, }; -#ifdef CONFIG_SND_HDA_INPUT_BEEP -#define set_beep_amp(spec, nid, idx, dir) \ - ((spec)->beep_amp = HDA_COMPOSE_AMP_VAL(nid, 1, idx, dir)) -#else -#define set_beep_amp(spec, nid, idx, dir) /* NOP */ -#endif - static int patch_conexant_auto(struct hda_codec *codec); /* * EAPD control @@ -656,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; } @@ -699,14 +659,6 @@ static const struct hda_input_mux cxt5045_capture_source_benq = { } }; -static const struct hda_input_mux cxt5045_capture_source_hp530 = { - .num_items = 2, - .items = { - { "Mic", 0x1 }, - { "Internal Mic", 0x2 }, - } -}; - /* turn on/off EAPD (+ mute HP) as a master switch */ static int cxt5045_hp_master_sw_put(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) @@ -822,28 +774,6 @@ static const struct snd_kcontrol_new cxt5045_benq_mixers[] = { {} }; -static const struct snd_kcontrol_new cxt5045_mixers_hp530[] = { - 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, 0x2, HDA_INPUT), - HDA_CODEC_MUTE("Internal Mic Playback Switch", 0x17, 0x2, HDA_INPUT), - HDA_CODEC_VOLUME("Mic Playback Volume", 0x17, 0x1, HDA_INPUT), - HDA_CODEC_MUTE("Mic Playback Switch", 0x17, 0x1, HDA_INPUT), - HDA_BIND_VOL("Master Playback Volume", &cxt5045_hp_bind_master_vol), - { - .iface = SNDRV_CTL_ELEM_IFACE_MIXER, - .name = "Master Playback Switch", - .info = cxt_eapd_info, - .get = cxt_eapd_get, - .put = cxt5045_hp_master_sw_put, - .private_value = 0x10, - }, - - {} -}; - static const struct hda_verb cxt5045_init_verbs[] = { /* Line in, Mic */ {0x12, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN|AC_PINCTL_VREF_80 }, @@ -1026,7 +956,6 @@ enum { CXT5045_LAPTOP_MICSENSE, CXT5045_LAPTOP_HPMICSENSE, CXT5045_BENQ, - CXT5045_LAPTOP_HP530, #ifdef CONFIG_SND_DEBUG CXT5045_TEST, #endif @@ -1039,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 @@ -1047,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), @@ -1139,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; @@ -1175,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; } @@ -1954,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; } @@ -1966,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 }, }; @@ -1985,7 +1898,8 @@ 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) */ @@ -2023,88 +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_set_pin_ctl_cache(codec, 0x1a, - 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_set_pin_ctl(codec, 0x1e, 0); - - /* external mic, port B */ - snd_hda_set_pin_ctl(codec, 0x1a, - spec->ext_mic_present ? CXT5066_OLPC_EXT_MIC_BIAS : 0); - - /* internal mic, port C */ - snd_hda_set_pin_ctl(codec, 0x1b, - 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) { @@ -2136,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); } } @@ -2164,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); } } @@ -2179,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); } @@ -2191,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); } @@ -2230,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); } } @@ -2255,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); } @@ -2278,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); @@ -2364,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) @@ -2496,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; } } @@ -2536,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, @@ -2663,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}, @@ -2909,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); @@ -2919,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? */ @@ -2950,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", @@ -2966,21 +2548,16 @@ static const struct snd_pci_quirk cxt5066_cfg_tbl[] = { SND_PCI_QUIRK(0x1028, 0x0401, "Dell Vostro 1014", 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, 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), {} @@ -3063,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; @@ -3136,631 +2692,17 @@ static int patch_cxt5066(struct hda_codec *codec) } if (spec->beep_amp) - snd_hda_attach_beep_device(codec, spec->beep_amp); - - return 0; -} + snd_hda_attach_beep_device(codec, get_amp_nid_(spec->beep_amp)); -/* - * 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) -{ - 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; - - for (i = 0; i < nums; i++) { - if (get_connection_index(codec, pin, dacs[i]) >= 0) { - ret = dacs[i]; - break; - } - } - if (!ret) - return 0; - if (--nums > 0) - memmove(dacs, dacs + 1, nums * sizeof(hda_nid_t)); - *num_dacs = nums; - return ret; -} - -#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) -{ - 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; - 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)) - 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; - 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]; -} - -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) -{ - struct conexant_spec *spec = codec->spec; - int i; - for (i = 0; i < num_pins; i++) - snd_hda_set_pin_ctl(codec, pins[i], 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); - } - return present; -} - -/* auto-mute/unmute speaker and line outs according to headphone jack */ -static void cx_auto_update_speakers(struct hda_codec *codec) -{ - 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); - } +#endif /* ENABLE_CXT_STATIC_QUIRKS */ - /* 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); -} - -static void cx_auto_hp_automute(struct hda_codec *codec, struct hda_jack_tbl *jack) -{ - struct conexant_spec *spec = codec->spec; - struct auto_pin_cfg *cfg = &spec->autocfg; - - if (!spec->auto_mute) - return; - spec->hp_present = detect_jacks(codec, cfg->hp_outs, cfg->hp_pins); - cx_auto_update_speakers(codec); -} - -static void cx_auto_line_automute(struct hda_codec *codec, struct hda_jack_tbl *jack) -{ - struct conexant_spec *spec = codec->spec; - struct auto_pin_cfg *cfg = &spec->autocfg; - - 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); -} - -static int cx_automute_mode_info(struct snd_kcontrol *kcontrol, - struct snd_ctl_elem_info *uinfo) -{ - struct hda_codec *codec = snd_kcontrol_chip(kcontrol); - struct conexant_spec *spec = codec->spec; - static const char * const texts3[] = { - "Disabled", "Speaker Only", "Line Out+Speaker" - }; - - if (spec->automute_hp_lo) - return snd_hda_enum_helper_info(kcontrol, uinfo, 3, texts3); - return snd_hda_enum_bool_helper_info(kcontrol, uinfo); -} - -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; - - 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 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, - }, - { } -}; - -static int cx_auto_mux_enum_info(struct snd_kcontrol *kcontrol, - struct snd_ctl_elem_info *uinfo) -{ - struct hda_codec *codec = snd_kcontrol_chip(kcontrol); - struct conexant_spec *spec = codec->spec; - - return snd_hda_input_mux_info(&spec->private_imux, uinfo); -} - -static int cx_auto_mux_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->cur_mux[0]; - return 0; -} - -/* 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) -{ - struct conexant_spec *spec = codec->spec; - hda_nid_t conn[HDA_MAX_NUM_INPUTS]; - int startidx, i, nums; - - 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; - } - - 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; - - /* Try to rotate around connections to avoid one boost controlling - another input path as well */ - startidx = 0; - for (i = 0; i < spec->private_imux.num_items; i++) - if (spec->imux_info[i].pin == pin) { - startidx = i; - break; - } - - for (i = 0; i < nums; i++) { - int j = (i + startidx) % nums; - int ret = __select_input_connection(codec, conn[j], pin, srcp, - do_select, depth); - if (ret >= 0) { - if (do_select) - snd_hda_codec_write(codec, mux, 0, - AC_VERB_SET_CONNECT_SEL, j); - return j; - } - } - 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); -} - -static int get_input_connection(struct hda_codec *codec, hda_nid_t mux, - hda_nid_t pin) -{ - return __select_input_connection(codec, mux, pin, NULL, false, 0); -} - -static int cx_auto_mux_enum_update(struct hda_codec *codec, - const struct hda_input_mux *imux, - unsigned int idx) -{ - 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); - } - 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; - - return cx_auto_mux_enum_update(codec, &spec->private_imux, - ucontrol->value.enumerated.item[0]); -} - -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 - }, - {} -}; - -static bool select_automic(struct hda_codec *codec, int idx, bool detect) -{ - 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; -} - -/* automatic switch internal and external mic */ -static void cx_auto_automic(struct hda_codec *codec, struct hda_jack_tbl *jack) -{ - struct conexant_spec *spec = codec->spec; - - 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); -} - -/* 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 +/* + * Automatic parser for CX20641 & co */ -static void cx_auto_check_auto_mic(struct hda_codec *codec) -{ - struct conexant_spec *spec = codec->spec; - int pset[INPUT_PIN_ATTR_NORMAL + 1]; - int i; - - 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]; -} - -static void cx_auto_parse_input(struct hda_codec *codec) -{ - 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; - } - } - 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) @@ -3802,24 +2744,8 @@ static void cx_auto_parse_eapd(struct hda_codec *codec) * 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; -} - -static int cx_auto_parse_auto_config(struct hda_codec *codec) -{ - struct conexant_spec *spec = codec->spec; - int err; - - err = snd_hda_parse_pin_def_config(codec, &spec->autocfg, NULL); - if (err < 0) - return err; - - 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); - return 0; + if (spec->num_eapds > 2) + spec->dynamic_eapd = 1; } static void cx_auto_turn_eapd(struct hda_codec *codec, int num_pins, @@ -3834,597 +2760,433 @@ static void cx_auto_turn_eapd(struct hda_codec *codec, int num_pins, } } -static void select_connection(struct hda_codec *codec, hda_nid_t pin, - hda_nid_t src) +/* turn on/off EAPD according to Master switch */ +static void cx_auto_vmaster_hook(void *private_data, int enabled) { - int idx = get_connection_index(codec, pin, src); - if (idx >= 0) - snd_hda_codec_write(codec, pin, 0, - AC_VERB_SET_CONNECT_SEL, idx); + struct hda_codec *codec = private_data; + struct conexant_spec *spec = codec->spec; + + cx_auto_turn_eapd(codec, spec->num_eapds, spec->eapds, enabled); } -static void mute_outputs(struct hda_codec *codec, int num_nids, - const hda_nid_t *nids) +static int cx_auto_build_controls(struct hda_codec *codec) { - int i, val; + int err; - 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); - } -} + err = snd_hda_gen_build_controls(codec); + if (err < 0) + return err; -static void enable_unsol_pins(struct hda_codec *codec, int num_pins, - hda_nid_t *pins, unsigned int action, - hda_jack_callback cb) -{ - int i; - for (i = 0; i < num_pins; i++) - snd_hda_jack_detect_enable_callback(codec, pins[i], action, cb); -} + err = add_beep_ctls(codec); + if (err < 0) + return err; -static bool found_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 true; - return false; + return 0; } -/* is the given NID found in any of autocfg items? */ -static bool found_in_autocfg(struct auto_pin_cfg *cfg, hda_nid_t nid) +static int cx_auto_init(struct hda_codec *codec) { - int i; + struct conexant_spec *spec = codec->spec; + snd_hda_gen_init(codec); + if (!spec->dynamic_eapd) + cx_auto_turn_eapd(codec, spec->num_eapds, spec->eapds, true); - if (found_in_nid_list(nid, cfg->line_out_pins, cfg->line_outs) || - found_in_nid_list(nid, cfg->hp_pins, cfg->hp_outs) || - found_in_nid_list(nid, cfg->speaker_pins, cfg->speaker_outs) || - found_in_nid_list(nid, cfg->dig_out_pins, cfg->dig_outs)) - return true; - for (i = 0; i < cfg->num_inputs; i++) - if (cfg->inputs[i].pin == nid) - return true; - if (cfg->dig_in_pin == nid) - return true; - return false; + snd_hda_apply_fixup(codec, HDA_FIXUP_ACT_INIT); + + return 0; } -/* clear unsol-event tags on unused pins; Conexant codecs seem to leave - * invalid unsol tags by some reason +#define cx_auto_free snd_hda_gen_free + +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 +}; + +/* + * pin fix-up */ -static void clear_unsol_on_unused_pins(struct hda_codec *codec) -{ - struct conexant_spec *spec = codec->spec; - struct auto_pin_cfg *cfg = &spec->autocfg; - int i; +enum { + CXT_PINCFG_LENOVO_X200, + CXT_PINCFG_LENOVO_TP410, + CXT_PINCFG_LEMOTE_A1004, + CXT_PINCFG_LEMOTE_A1205, + CXT_FIXUP_STEREO_DMIC, + CXT_FIXUP_INC_MIC_BOOST, + CXT_FIXUP_HEADPHONE_MIC_PIN, + CXT_FIXUP_HEADPHONE_MIC, + CXT_FIXUP_GPIO1, + CXT_FIXUP_THINKPAD_ACPI, + CXT_FIXUP_OLPC_XO, + CXT_FIXUP_CAP_MIX_AMP, + CXT_FIXUP_TOSHIBA_P105, + CXT_FIXUP_HP_530, + CXT_FIXUP_CAP_MIX_AMP_5047, +}; - for (i = 0; i < codec->init_pins.used; i++) { - struct hda_pincfg *pin = snd_array_elem(&codec->init_pins, i); - if (!found_in_autocfg(cfg, pin->nid)) - snd_hda_codec_write(codec, pin->nid, 0, - AC_VERB_SET_UNSOLICITED_ENABLE, 0); - } -} +/* for hda_fixup_thinkpad_acpi() */ +#include "thinkpad_helper.c" -/* turn on/off EAPD according to Master switch */ -static void cx_auto_vmaster_hook(void *private_data, int enabled) +static void cxt_fixup_stereo_dmic(struct hda_codec *codec, + const struct hda_fixup *fix, int action) { - struct hda_codec *codec = private_data; struct conexant_spec *spec = codec->spec; - - if (enabled && spec->pin_eapd_ctrls) { - cx_auto_update_speakers(codec); - return; - } - cx_auto_turn_eapd(codec, spec->num_eapds, spec->eapds, enabled); + spec->gen.inv_dmic_split = 1; } -static void cx_auto_init_output(struct hda_codec *codec) +static void cxt5066_increase_mic_boost(struct hda_codec *codec, + const struct hda_fixup *fix, int action) { - struct conexant_spec *spec = codec->spec; - struct auto_pin_cfg *cfg = &spec->autocfg; - hda_nid_t nid; - int i; + if (action != HDA_FIXUP_ACT_PRE_PROBE) + return; - mute_outputs(codec, spec->multiout.num_dacs, spec->multiout.dac_nids); - for (i = 0; i < cfg->hp_outs; i++) { - unsigned int val = PIN_OUT; - if (snd_hda_query_pin_caps(codec, cfg->hp_pins[i]) & - AC_PINCAP_HP_DRV) - val |= AC_PINCTL_HP_EN; - snd_hda_set_pin_ctl(codec, cfg->hp_pins[i], val); - } - 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, cx_auto_hp_automute); - 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, - cx_auto_line_automute); - 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); - clear_unsol_on_unused_pins(codec); + snd_hda_override_amp_caps(codec, 0x17, HDA_OUTPUT, + (0x3 << AC_AMPCAP_OFFSET_SHIFT) | + (0x4 << AC_AMPCAP_NUM_STEPS_SHIFT) | + (0x27 << AC_AMPCAP_STEP_SIZE_SHIFT) | + (0 << AC_AMPCAP_MUTE_SHIFT)); } -static void cx_auto_init_input(struct hda_codec *codec) +static void cxt_update_headset_mode(struct hda_codec *codec) { + /* 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->autocfg; - int i, val; + struct auto_pin_cfg *cfg = &spec->gen.autocfg; - 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); - } + hda_nid_t mux_pin = spec->gen.imux_pins[spec->gen.cur_mux[0]]; - for (i = 0; i < cfg->num_inputs; i++) { - hda_nid_t pin = cfg->inputs[i].pin; - unsigned int type = PIN_IN; - if (cfg->inputs[i].type == AUTO_PIN_MIC) - type |= snd_hda_get_default_vref(codec, pin); - snd_hda_set_pin_ctl(codec, pin, type); - } - - if (spec->auto_mic) { - if (spec->auto_mic_ext >= 0) { - snd_hda_jack_detect_enable_callback(codec, - cfg->inputs[spec->auto_mic_ext].pin, - CONEXANT_MIC_EVENT, cx_auto_automic); - } - if (spec->auto_mic_dock >= 0) { - snd_hda_jack_detect_enable_callback(codec, - cfg->inputs[spec->auto_mic_dock].pin, - CONEXANT_MIC_EVENT, cx_auto_automic); + for (i = 0; i < cfg->num_inputs; i++) + if (cfg->inputs[i].pin == mux_pin) { + mic_mode = !!cfg->inputs[i].is_headphone_mic; + break; } - cx_auto_automic(codec, NULL); + + 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 { - select_input_connection(codec, spec->imux_info[0].adc, - spec->imux_info[0].pin); + 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]); } + + snd_hda_gen_update_outputs(codec); } -static void cx_auto_init_digital(struct hda_codec *codec) +static void cxt_update_headset_mode_hook(struct hda_codec *codec, + struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) { - struct conexant_spec *spec = codec->spec; - struct auto_pin_cfg *cfg = &spec->autocfg; - - if (spec->multiout.dig_out_nid) - snd_hda_set_pin_ctl(codec, cfg->dig_out_pins[0], PIN_OUT); - if (spec->dig_in_nid) - snd_hda_set_pin_ctl(codec, cfg->dig_in_pin, PIN_IN); + cxt_update_headset_mode(codec); } -static int cx_auto_init(struct hda_codec *codec) +static void cxt_fixup_headphone_mic(struct hda_codec *codec, + const struct hda_fixup *fix, int action) { struct conexant_spec *spec = codec->spec; - snd_hda_gen_apply_verbs(codec); - cx_auto_init_output(codec); - cx_auto_init_input(codec); - cx_auto_init_digital(codec); - snd_hda_sync_vmaster_hook(&spec->vmaster_mute); - 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, int chs) -{ - static char name[44]; - 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, chs, 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; + 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; } - return 0; } -#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, 3) +/* OPLC XO 1.5 fixup */ -#define cx_auto_add_pb_volume(codec, nid, str, idx) \ - cx_auto_add_volume(codec, str, " Playback", idx, nid, HDA_OUTPUT) +/* 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 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; -} +#define update_mic_pin(codec, nid, val) \ + snd_hda_codec_update_cache(codec, nid, 0, \ + AC_VERB_SET_PIN_WIDGET_CONTROL, val) -static bool is_2_1_speaker(struct conexant_spec *spec) -{ - int i, type, num_spk = 0; - - for (i = 0; i < spec->dac_info_filled; i++) { - type = spec->dac_info[i].type; - if (type == AUTO_PIN_LINE_OUT) - type = spec->autocfg.line_out_type; - if (type == AUTO_PIN_SPEAKER_OUT) - num_spk++; - } - return (num_spk == 2 && spec->autocfg.line_out_type != AUTO_PIN_LINE_OUT); -} +static const struct hda_input_mux olpc_xo_dc_bias = { + .num_items = 3, + .items = { + { "Off", PIN_IN }, + { "50%", PIN_VREF50 }, + { "80%", PIN_VREF80 }, + }, +}; -static int cx_auto_build_output_controls(struct hda_codec *codec) +static void olpc_xo_update_mic_boost(struct hda_codec *codec) { struct conexant_spec *spec = codec->spec; - int i, err; - int num_line = 0, num_hp = 0, num_spk = 0; - bool speaker_2_1; - 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); - - speaker_2_1 = is_2_1_speaker(spec); - - 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: - if (speaker_2_1) { - label = num_spk++ ? "Bass Speaker" : "Speaker"; - idx = 0; - } else { - label = "Speaker"; - idx = num_spk++; - } - break; - } - err = try_add_pb_volume(codec, dac, - spec->dac_info[i].pin, - label, idx); - if (err < 0) - return err; - } + int ch, val; - if (spec->auto_mute) { - err = snd_hda_add_new_ctls(codec, cx_automute_mode_enum); - if (err < 0) - return err; + 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); } - - return 0; } -/* Returns zero if this is a normal stereo channel, and non-zero if it should - be split in two independent channels. - dest_label must be at least 44 characters. */ -static int cx_auto_get_rightch_label(struct hda_codec *codec, const char *label, - char *dest_label, int nid) +static void olpc_xo_update_mic_pins(struct hda_codec *codec) { struct conexant_spec *spec = codec->spec; - int i; - - if (!spec->fixup_stereo_dmic) - return 0; - - for (i = 0; i < AUTO_CFG_MAX_INS; i++) { - int def_conf; - if (spec->autocfg.inputs[i].pin != nid) - continue; + int cur_input, val; + struct nid_path *path; - if (spec->autocfg.inputs[i].type != AUTO_PIN_MIC) - return 0; - def_conf = snd_hda_codec_get_pincfg(codec, nid); - if (snd_hda_get_input_pin_attr(def_conf) != INPUT_PIN_ATTR_INT) - return 0; + cur_input = spec->gen.input_paths[0][spec->gen.cur_mux[0]]; - /* Finally found the inverted internal mic! */ - snprintf(dest_label, 44, "Inverted %s", label); - return 1; + /* 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); } - 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) +/* mic_autoswitch hook */ +static void olpc_xo_automic(struct hda_codec *codec, struct hda_jack_tbl *jack) { struct conexant_spec *spec = codec->spec; - int i; - - for (i = 0; i < spec->num_adc_nids; i++) { - char rightch_label[44]; - hda_nid_t adc_nid = spec->adc_nids[i]; - int idx = get_input_connection(codec, adc_nid, nid); - if (idx < 0) - continue; - if (codec->single_adc_amp) - idx = 0; + int saved_cached_write = codec->cached_write; - if (cx_auto_get_rightch_label(codec, label, rightch_label, nid)) { - /* Make two independent kcontrols for left and right */ - int err = cx_auto_add_volume_idx(codec, label, pfx, - cidx, adc_nid, HDA_INPUT, idx, 1); - if (err < 0) - return err; - return cx_auto_add_volume_idx(codec, rightch_label, pfx, - cidx, adc_nid, HDA_INPUT, idx, 2); - } - return cx_auto_add_volume_idx(codec, label, pfx, - cidx, adc_nid, HDA_INPUT, idx, 3); - } - return 0; + 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 int cx_auto_add_boost_volume(struct hda_codec *codec, int idx, - const char *label, int cidx) +/* 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; - hda_nid_t mux, nid; - int i, con; - - nid = spec->imux_info[idx].pin; - if (get_wcaps(codec, nid) & AC_WCAP_IN_AMP) { - char rightch_label[44]; - if (cx_auto_get_rightch_label(codec, label, rightch_label, nid)) { - int err = cx_auto_add_volume_idx(codec, label, " Boost", - cidx, nid, HDA_INPUT, 0, 1); - if (err < 0) - return err; - return cx_auto_add_volume_idx(codec, rightch_label, " Boost", - cidx, nid, HDA_INPUT, 0, 2); - } - 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", cidx, - mux, HDA_OUTPUT); + /* 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; } +} + +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; + ucontrol->value.integer.value[0] = spec->dc_enable; return 0; } -static int cx_auto_build_input_controls(struct hda_codec *codec) +static int olpc_xo_dc_mode_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) { + struct hda_codec *codec = snd_kcontrol_chip(kcontrol); struct conexant_spec *spec = codec->spec; - struct 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; + int dc_enable = !!ucontrol->value.integer.value[0]; - if (!imux->num_items) + if (dc_enable == spec->dc_enable) 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 (!codec->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; + spec->dc_enable = dc_enable; + olpc_xo_update_mic_pins(codec); + olpc_xo_update_mic_boost(codec); + return 1; +} - 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; +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; + ucontrol->value.enumerated.item[0] = spec->dc_input_bias; + return 0; +} - err = cx_auto_add_boost_volume(codec, i, label, cidx); - if (err < 0) - return err; +static int olpc_xo_dc_bias_enum_info(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_info *uinfo) +{ + return snd_hda_input_mux_info(&olpc_xo_dc_bias, uinfo); +} - 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); - } - if (err < 0) - return err; - } +static int olpc_xo_dc_bias_enum_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct hda_codec *codec = snd_kcontrol_chip(kcontrol); + struct conexant_spec *spec = codec->spec; + const struct hda_input_mux *imux = &olpc_xo_dc_bias; + unsigned int idx; - 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; - } + idx = ucontrol->value.enumerated.item[0]; + if (idx >= imux->num_items) + idx = imux->num_items - 1; + if (spec->dc_input_bias == idx) + return 0; - return 0; + spec->dc_input_bias = idx; + if (spec->dc_enable) + olpc_xo_update_mic_pins(codec); + return 1; } -static int cx_auto_build_controls(struct hda_codec *codec) +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, + }, + {} +}; + +/* overriding mic boost put callback; update mic boost volume only when + * DC mode is disabled + */ +static int olpc_xo_mic_boost_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) { + struct hda_codec *codec = snd_kcontrol_chip(kcontrol); struct conexant_spec *spec = codec->spec; - int 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; - if (spec->vmaster_mute.sw_kctl) { - spec->vmaster_mute.hook = cx_auto_vmaster_hook; - err = snd_hda_add_vmaster_hook(codec, &spec->vmaster_mute, - spec->vmaster_mute_led); - if (err < 0) - return err; - } - return 0; + int ret = snd_hda_mixer_amp_volume_put(kcontrol, ucontrol); + if (ret > 0 && spec->dc_enable) + olpc_xo_update_mic_boost(codec); + return ret; } -static int cx_auto_search_adcs(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; - hda_nid_t nid, end_nid; + int i; - 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))) + if (action != HDA_FIXUP_ACT_PROBE) + return; + + 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); + + snd_hda_add_new_ctls(codec, olpc_xo_mixers); + + /* 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; - 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 = snd_hda_jack_unsol_event, -}; - /* - * pin fix-up + * Fix max input level on mixer widget to 0dB + * (originally it has 0x2b steps with 0dB offset 0x14) */ -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, -}; - -static void cxt_fixup_stereo_dmic(struct hda_codec *codec, +static void cxt_fixup_cap_mix_amp(struct hda_codec *codec, const struct hda_fixup *fix, int action) { - struct conexant_spec *spec = codec->spec; - spec->fixup_stereo_dmic = 1; + 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 void cxt5066_increase_mic_boost(struct hda_codec *codec, - const struct hda_fixup *fix, int action) +/* + * 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) { - if (action != HDA_FIXUP_ACT_PRE_PROBE) - return; - - snd_hda_override_amp_caps(codec, 0x17, HDA_OUTPUT, - (0x3 << AC_AMPCAP_OFFSET_SHIFT) | - (0x4 << AC_AMPCAP_NUM_STEPS_SHIFT) | - (0x27 << AC_AMPCAP_STEP_SIZE_SHIFT) | - (0 << AC_AMPCAP_MUTE_SHIFT)); + snd_hda_override_amp_caps(codec, 0x10, HDA_INPUT, + (0x17 << AC_AMPCAP_OFFSET_SHIFT) | + (0x17 << AC_AMPCAP_NUM_STEPS_SHIFT) | + (0x05 << AC_AMPCAP_STEP_SIZE_SHIFT) | + (1 << AC_AMPCAP_MUTE_SHIFT)); } /* ThinkPad X200 & co with cxt5051 */ @@ -4464,6 +3226,8 @@ static const struct hda_fixup cxt_fixups[] = { [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, @@ -4483,6 +3247,94 @@ static const struct hda_fixup cxt_fixups[] = { .type = HDA_FIXUP_FUNC, .v.func = cxt5066_increase_mic_boost, }, + [CXT_FIXUP_HEADPHONE_MIC_PIN] = { + .type = HDA_FIXUP_PINS, + .chained = true, + .chain_id = CXT_FIXUP_HEADPHONE_MIC, + .v.pins = (const struct hda_pintbl[]) { + { 0x18, 0x03a1913d }, /* use as headphone mic, without its own jack detect */ + { } + } + }, + [CXT_FIXUP_HEADPHONE_MIC] = { + .type = HDA_FIXUP_FUNC, + .v.func = cxt_fixup_headphone_mic, + }, + [CXT_FIXUP_GPIO1] = { + .type = HDA_FIXUP_VERBS, + .v.verbs = (const struct hda_verb[]) { + { 0x01, AC_VERB_SET_GPIO_MASK, 0x01 }, + { 0x01, AC_VERB_SET_GPIO_DIRECTION, 0x01 }, + { 0x01, AC_VERB_SET_GPIO_DATA, 0x01 }, + { } + }, + }, + [CXT_FIXUP_THINKPAD_ACPI] = { + .type = HDA_FIXUP_FUNC, + .v.func = hda_fixup_thinkpad_acpi, + }, + [CXT_FIXUP_OLPC_XO] = { + .type = HDA_FIXUP_FUNC, + .v.func = cxt_fixup_olpc_xo, + }, + [CXT_FIXUP_CAP_MIX_AMP] = { + .type = HDA_FIXUP_FUNC, + .v.func = cxt_fixup_cap_mix_amp, + }, + [CXT_FIXUP_TOSHIBA_P105] = { + .type = HDA_FIXUP_PINS, + .v.pins = (const struct hda_pintbl[]) { + { 0x10, 0x961701f0 }, /* speaker/hp */ + { 0x12, 0x02a1901e }, /* ext mic */ + { 0x14, 0x95a70110 }, /* int mic */ + {} + }, + }, + [CXT_FIXUP_HP_530] = { + .type = HDA_FIXUP_PINS, + .v.pins = (const struct hda_pintbl[]) { + { 0x12, 0x90a60160 }, /* int mic */ + {} + }, + .chained = true, + .chain_id = CXT_FIXUP_CAP_MIX_AMP, + }, + [CXT_FIXUP_CAP_MIX_AMP_5047] = { + .type = HDA_FIXUP_FUNC, + .v.func = cxt_fixup_cap_mix_amp_5047, + }, +}; + +static const struct snd_pci_quirk cxt5045_fixups[] = { + SND_PCI_QUIRK(0x103c, 0x30d5, "HP 530", CXT_FIXUP_HP_530), + SND_PCI_QUIRK(0x1179, 0xff31, "Toshiba P105", CXT_FIXUP_TOSHIBA_P105), + /* HP, Packard Bell, Fujitsu-Siemens & Lenovo laptops have + * really bad sound over 0dB on NID 0x17. + */ + SND_PCI_QUIRK_VENDOR(0x103c, "HP", CXT_FIXUP_CAP_MIX_AMP), + SND_PCI_QUIRK_VENDOR(0x1631, "Packard Bell", CXT_FIXUP_CAP_MIX_AMP), + SND_PCI_QUIRK_VENDOR(0x1734, "Fujitsu", CXT_FIXUP_CAP_MIX_AMP), + SND_PCI_QUIRK_VENDOR(0x17aa, "Lenovo", CXT_FIXUP_CAP_MIX_AMP), + {} +}; + +static const struct hda_model_fixup cxt5045_fixup_models[] = { + { .id = CXT_FIXUP_CAP_MIX_AMP, .name = "cap-mix-amp" }, + { .id = CXT_FIXUP_TOSHIBA_P105, .name = "toshiba-p105" }, + { .id = CXT_FIXUP_HP_530, .name = "hp-530" }, + {} +}; + +static const struct snd_pci_quirk cxt5047_fixups[] = { + /* HP laptops have really bad sound over 0 dB on NID 0x10. + */ + SND_PCI_QUIRK_VENDOR(0x103c, "HP", CXT_FIXUP_CAP_MIX_AMP_5047), + {} +}; + +static const struct hda_model_fixup cxt5047_fixup_models[] = { + { .id = CXT_FIXUP_CAP_MIX_AMP_5047, .name = "cap-mix-amp" }, + {} }; static const struct snd_pci_quirk cxt5051_fixups[] = { @@ -4490,21 +3342,43 @@ static const struct snd_pci_quirk cxt5051_fixups[] = { {} }; +static const struct hda_model_fixup cxt5051_fixup_models[] = { + { .id = CXT_PINCFG_LENOVO_X200, .name = "lenovo-x200" }, + {} +}; + static const struct snd_pci_quirk cxt5066_fixups[] = { SND_PCI_QUIRK(0x1025, 0x0543, "Acer Aspire One 522", CXT_FIXUP_STEREO_DMIC), + SND_PCI_QUIRK(0x1025, 0x054c, "Acer Aspire 3830TG", CXT_FIXUP_GPIO1), + SND_PCI_QUIRK(0x1043, 0x138d, "Asus", CXT_FIXUP_HEADPHONE_MIC_PIN), + SND_PCI_QUIRK(0x152d, 0x0833, "OLPC XO-1.5", CXT_FIXUP_OLPC_XO), SND_PCI_QUIRK(0x17aa, 0x20f2, "Lenovo T400", CXT_PINCFG_LENOVO_TP410), SND_PCI_QUIRK(0x17aa, 0x215e, "Lenovo T410", CXT_PINCFG_LENOVO_TP410), SND_PCI_QUIRK(0x17aa, 0x215f, "Lenovo T510", CXT_PINCFG_LENOVO_TP410), SND_PCI_QUIRK(0x17aa, 0x21ce, "Lenovo T420", CXT_PINCFG_LENOVO_TP410), SND_PCI_QUIRK(0x17aa, 0x21cf, "Lenovo T520", CXT_PINCFG_LENOVO_TP410), + SND_PCI_QUIRK(0x17aa, 0x21da, "Lenovo X220", CXT_PINCFG_LENOVO_TP410), + SND_PCI_QUIRK(0x17aa, 0x21db, "Lenovo X220-tablet", CXT_PINCFG_LENOVO_TP410), SND_PCI_QUIRK(0x17aa, 0x3975, "Lenovo U300s", CXT_FIXUP_STEREO_DMIC), SND_PCI_QUIRK(0x17aa, 0x3977, "Lenovo IdeaPad U310", CXT_FIXUP_STEREO_DMIC), SND_PCI_QUIRK(0x17aa, 0x397b, "Lenovo S205", CXT_FIXUP_STEREO_DMIC), + SND_PCI_QUIRK_VENDOR(0x17aa, "Thinkpad", CXT_FIXUP_THINKPAD_ACPI), SND_PCI_QUIRK(0x1c06, 0x2011, "Lemote A1004", CXT_PINCFG_LEMOTE_A1004), SND_PCI_QUIRK(0x1c06, 0x2012, "Lemote A1205", CXT_PINCFG_LEMOTE_A1205), {} }; +static const struct hda_model_fixup cxt5066_fixup_models[] = { + { .id = CXT_FIXUP_STEREO_DMIC, .name = "stereo-dmic" }, + { .id = CXT_FIXUP_GPIO1, .name = "gpio1" }, + { .id = CXT_FIXUP_HEADPHONE_MIC_PIN, .name = "headphone-mic-pin" }, + { .id = CXT_PINCFG_LENOVO_TP410, .name = "tp410" }, + { .id = CXT_FIXUP_THINKPAD_ACPI, .name = "thinkpad" }, + { .id = CXT_PINCFG_LEMOTE_A1004, .name = "lemote-a1004" }, + { .id = CXT_FIXUP_OLPC_XO, .name = "olpc-xo" }, + {} +}; + /* add "fake" mute amp-caps to DACs on cx5051 so that mixer mute switches * can be created (bko#42825) */ @@ -4526,32 +3400,48 @@ 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; - snd_hda_gen_init(&spec->gen); + + cx_auto_parse_beep(codec); + cx_auto_parse_eapd(codec); + spec->gen.own_eapd_ctl = 1; + if (spec->dynamic_eapd) + spec->gen.vmaster_mute.hook = cx_auto_vmaster_hook; switch (codec->vendor_id) { case 0x14f15045: codec->single_adc_amp = 1; + spec->gen.mixer_nid = 0x17; + spec->gen.add_stereo_mix_input = 1; + snd_hda_pick_fixup(codec, cxt5045_fixup_models, + cxt5045_fixups, cxt_fixups); + break; + case 0x14f15047: + codec->pin_amp_workaround = 1; + spec->gen.mixer_nid = 0x19; + spec->gen.add_stereo_mix_input = 1; + snd_hda_pick_fixup(codec, cxt5047_fixup_models, + cxt5047_fixups, cxt_fixups); break; case 0x14f15051: add_cx5051_fake_mutes(codec); codec->pin_amp_workaround = 1; - snd_hda_pick_fixup(codec, NULL, cxt5051_fixups, cxt_fixups); + snd_hda_pick_fixup(codec, cxt5051_fixup_models, + cxt5051_fixups, cxt_fixups); break; default: codec->pin_amp_workaround = 1; - snd_hda_pick_fixup(codec, NULL, cxt5066_fixups, cxt_fixups); + snd_hda_pick_fixup(codec, cxt5066_fixup_models, + cxt5066_fixups, cxt_fixups); break; } - snd_hda_apply_fixup(codec, HDA_FIXUP_ACT_PRE_PROBE); - /* 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, @@ -4560,38 +3450,50 @@ static int patch_conexant_auto(struct hda_codec *codec) */ switch (codec->subsystem_id >> 16) { case 0x103c: - spec->vmaster_mute_led = 1; + spec->gen.vmaster_mute_enum = 1; break; } - err = cx_auto_search_adcs(codec); + snd_hda_apply_fixup(codec, HDA_FIXUP_ACT_PRE_PROBE); + + err = snd_hda_parse_pin_defcfg(codec, &spec->gen.autocfg, NULL, + spec->parse_flags); if (err < 0) - 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) { - snd_printd("hda_codec: " + 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 + /* */ @@ -4642,6 +3544,8 @@ static const struct hda_codec_preset snd_hda_preset_conexant[] = { .patch = patch_conexant_auto }, { .id = 0x14f15115, .name = "CX20757", .patch = patch_conexant_auto }, + { .id = 0x14f151d7, .name = "CX20952", + .patch = patch_conexant_auto }, {} /* terminator */ }; @@ -4668,6 +3572,7 @@ 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 807a2aa1ff3..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> @@ -44,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; @@ -64,31 +61,83 @@ 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; struct hda_pcm_stream pcm_playback; @@ -298,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; } @@ -339,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; } @@ -355,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; - memcpy(ucontrol->value.bytes.data, - spec->pins[pin_idx].sink_eld.eld_buffer, ELD_MAX_SIZE); + mutex_lock(&per_pin->lock); + if (eld->eld_size > ARRAY_SIZE(ucontrol->value.bytes.data)) { + mutex_unlock(&per_pin->lock); + snd_BUG(); + return -EINVAL; + } + + memset(ucontrol->value.bytes.data, 0, + ARRAY_SIZE(ucontrol->value.bytes.data)); + if (eld->eld_valid) + memcpy(ucontrol->value.bytes.data, eld->eld_buffer, + eld->eld_size); + mutex_unlock(&per_pin->lock); return 0; } @@ -388,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; } @@ -427,15 +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); - /* Enable pin out: some machines with GM965 gets broken output when - * the pin is disabled or changed while using with HDMI - */ + + if (spec->dyn_pin_out) + /* Disable pin out until stream is active */ + pin_out = 0; + else + /* Enable pin out: some machines with GM965 gets broken output + * when the pin is disabled or changed while using with HDMI + */ + pin_out = PIN_OUT; + snd_hda_codec_write(codec, pin_nid, 0, - AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT); + AC_VERB_SET_PIN_WIDGET_CONTROL, pin_out); } static int hdmi_get_channel_count(struct hda_codec *codec, hda_nid_t cvt_nid) @@ -452,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 @@ -516,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]; } @@ -530,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); @@ -541,74 +704,90 @@ 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_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[order].channels; i++) - hdmi_channel_mapping[ca][i] = i | (i << 4); - for (; i < 8; i++) - hdmi_channel_mapping[ca][i] = 0xf | (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 < channel_allocations[order].channels; i++) - non_pcm_mapping[i] = i | (i << 4); + for (i = 0; i < ch_alloc->channels; i++) + non_pcm_mapping[i] = (i << 4) | i; for (; i < 8; i++) - non_pcm_mapping[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, - non_pcm ? non_pcm_mapping[i] : 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; } } - - hdmi_debug_channel_mapping(codec, pin_nid); } struct channel_map_table { unsigned char map; /* ALSA API channel map position */ - unsigned char cea_slot; /* CEA slot value */ int spk_mask; /* speaker position bit mask */ }; static struct channel_map_table map_tables[] = { - { SNDRV_CHMAP_FL, 0x00, FL }, - { SNDRV_CHMAP_FR, 0x01, FR }, - { SNDRV_CHMAP_RL, 0x04, RL }, - { SNDRV_CHMAP_RR, 0x05, RR }, - { SNDRV_CHMAP_LFE, 0x02, LFE }, - { SNDRV_CHMAP_FC, 0x03, FC }, - { SNDRV_CHMAP_RLC, 0x06, RLC }, - { SNDRV_CHMAP_RRC, 0x07, RRC }, + { 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 */ }; @@ -624,25 +803,19 @@ static int to_spk_mask(unsigned char c) } /* from ALSA API channel position to CEA slot */ -static int to_cea_slot(unsigned char c) +static int to_cea_slot(int ordered_ca, unsigned char pos) { - struct channel_map_table *t = map_tables; - for (; t->map; t++) { - if (t->map == c) - return t->cea_slot; - } - return 0x0f; -} + int mask = to_spk_mask(pos); + int i; -/* from CEA slot to ALSA API channel position */ -static int from_cea_slot(unsigned char c) -{ - struct channel_map_table *t = map_tables; - for (; t->map; t++) { - if (t->cea_slot == c) - return t->map; + if (mask) { + for (i = 0; i < 8; i++) { + if (channel_allocations[ordered_ca].speakers[7 - i] == mask) + return i; + } } - return 0; + + return -1; } /* from speaker bit mask to ALSA API channel position */ @@ -656,6 +829,14 @@ static int spk_to_chmap(int spk) 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) { @@ -682,18 +863,29 @@ static int hdmi_manual_channel_allocation(int chs, unsigned char *map) /* 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 chs, unsigned char *map, + int ca) { - int i; - for (i = 0; i < 8; i++) { - int val, err; - if (i < chs) - val = to_cea_slot(map[i]); - else - val = 0xf; - val |= (i << 4); - err = snd_hda_codec_write(codec, pin_nid, 0, - AC_VERB_SET_HDMI_CHAN_SLOT, val); + 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; } @@ -704,9 +896,10 @@ static int hdmi_manual_setup_channel_mapping(struct hda_codec *codec, 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[ca].channels) - map[i] = from_cea_slot((hdmi_channel_mapping[ca][i] >> 4) & 0x0f); + if (i < channel_allocations[ordered_ca].channels) + map[i] = from_cea_slot(ordered_ca, hdmi_channel_mapping[ca][i] & 0x0f); else map[i] = 0; } @@ -714,15 +907,34 @@ static void hdmi_setup_fake_chmap(unsigned char *map, int ca) 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) + int channels, unsigned char *map, + bool chmap_set) { - if (!non_pcm && map) { + if (!non_pcm && chmap_set) { hdmi_manual_setup_channel_mapping(codec, pin_nid, - channels, map); + 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; } /* @@ -758,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 } @@ -785,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); } @@ -846,49 +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, - bool non_pcm, - 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; - - 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; - 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; } @@ -900,59 +1096,98 @@ 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, non_pcm, ca, - channels, per_pin->chmap); + active_channels, ca); hdmi_stop_infoframe_trans(codec, pin_nid); hdmi_fill_audio_infoframe(codec, pin_nid, ai.bytes, sizeof(ai)); hdmi_start_infoframe_trans(codec, pin_nid); - } else { - /* For non-pcm audio switch, setup new channel mapping - * accordingly */ - if (per_pin->non_pcm != non_pcm) - hdmi_setup_channel_mapping(codec, pin_nid, non_pcm, ca, - channels, per_pin->chmap); } +} + +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 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; - _snd_printd(SND_PR_VERBOSE, - "HDMI hot plug event: Codec=%d Pin=%d Presence_Detect=%d ELD_Valid=%d\n", - codec->addr, pin_nid, + 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) @@ -962,7 +1197,7 @@ 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 + codec_info(codec, "HDMI CP event: CODEC=%d TAG=%d SUBTAG=0x%x CP_STATE=%d CP_READY=%d\n", codec->addr, tag, @@ -984,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; } @@ -994,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 */ @@ -1002,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); @@ -1028,41 +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 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) @@ -1076,17 +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); + + /* 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 */ @@ -1098,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 */ @@ -1121,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; } @@ -1139,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 @@ -1152,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; - _snd_printd(SND_PR_VERBOSE, + 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) @@ -1184,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; @@ -1203,11 +1655,13 @@ static int hdmi_add_pin(struct hda_codec *codec, hda_nid_t 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; @@ -1224,19 +1678,16 @@ 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; @@ -1253,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; @@ -1265,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; } @@ -1289,33 +1742,11 @@ static int hdmi_parse_codec(struct hda_codec *codec) } } -#ifdef CONFIG_PM - /* We're seeing some problems with unsolicited hot plug events on - * PantherPoint after S3, if this is not enabled */ - if (codec->vendor_id == 0x80862806) - codec->bus->power_keep_link_on = 1; - /* - * 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. - */ - else 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 char names[MAX_HDMI_PINS][8]; - sprintf(&names[idx][0], "HDMI %d", idx); - return &names[idx][0]; -} - static bool check_non_pcm_per_cvt(struct hda_codec *codec, hda_nid_t cvt_nid) { struct hda_spdif_out *spdif; @@ -1341,17 +1772,42 @@ 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); + } 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_set_channel_count(codec, cvt_nid, substream->runtime->channels); + hdmi_setup_audio_infoframe(codec, per_pin, non_pcm); + mutex_unlock(&per_pin->lock); - hdmi_setup_audio_infoframe(codec, pin_idx, non_pcm, substream); + 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 hdmi_setup_stream(codec, cvt_nid, pin_nid, stream_tag, format); + 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, @@ -1370,25 +1826,40 @@ static int hdmi_pcm_close(struct hda_pcm_stream *hinfo, int cvt_idx, pin_idx; struct hdmi_spec_per_cvt *per_cvt; struct hdmi_spec_per_pin *per_pin; + int pinctl; if (hinfo->nid) { - cvt_idx = cvt_nid_to_cvt_index(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); + } 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; @@ -1417,14 +1888,40 @@ static int hdmi_chmap_ctl_info(struct snd_kcontrol *kcontrol, 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; - const unsigned int valid_mask = - FL | FR | RL | RR | LFE | FC | RLC | RRC; unsigned int __user *dst; int chs, count = 0; @@ -1435,18 +1932,19 @@ static int hdmi_chmap_ctl_tlv(struct snd_kcontrol *kcontrol, int op_flag, size -= 8; dst = tlv + 2; for (chs = 2; chs <= spec->channels_max; chs++) { - int i, c; + 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; - if (cap->channels != chs) - continue; - if (cap->spk_mask & ~valid_mask) + 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(SNDRV_CTL_TLVT_CHMAP_VAR, dst) || + if (put_user(type, dst) || put_user(chs_bytes, dst + 1)) return -EFAULT; dst += 2; @@ -1456,14 +1954,10 @@ static int hdmi_chmap_ctl_tlv(struct snd_kcontrol *kcontrol, int op_flag, return -ENOMEM; size -= chs_bytes; count += chs_bytes; - for (c = 7; c >= 0; c--) { - int spk = cap->speakers[c]; - if (!spk) - continue; - if (put_user(spk_to_chmap(spk), dst)) - return -EFAULT; - dst++; - } + 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)) @@ -1478,7 +1972,7 @@ static int hdmi_chmap_ctl_get(struct snd_kcontrol *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 = &spec->pins[pin_idx]; + struct hdmi_spec_per_pin *per_pin = get_pin(spec, pin_idx); int i; for (i = 0; i < ARRAY_SIZE(per_pin->chmap); i++) @@ -1493,11 +1987,11 @@ static int hdmi_chmap_ctl_put(struct snd_kcontrol *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 = &spec->pins[pin_idx]; + 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, ca, prepared = 0; + int i, err, ca, prepared = 0; ctl_idx = snd_ctl_get_ioffidx(kcontrol, &ucontrol->id); substream = snd_pcm_chmap_substream(info, ctl_idx); @@ -1521,11 +2015,17 @@ static int hdmi_chmap_ctl_put(struct snd_kcontrol *kcontrol, 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, pin_idx, per_pin->non_pcm, - substream); + hdmi_setup_audio_infoframe(codec, per_pin, per_pin->non_pcm); + mutex_unlock(&per_pin->lock); return 0; } @@ -1538,9 +2038,14 @@ 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; @@ -1551,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; } @@ -1560,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); } @@ -1576,7 +2084,7 @@ 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) @@ -1591,9 +2099,8 @@ static int generic_hdmi_build_controls(struct hda_codec *codec) 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; @@ -1606,6 +2113,9 @@ static int generic_hdmi_build_controls(struct hda_codec *codec) 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); @@ -1631,12 +2141,12 @@ static int generic_hdmi_init_per_pins(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_eld *eld = &per_pin->sink_eld; + 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); - snd_hda_eld_proc_new(codec, eld, pin_idx); + eld_proc_new(per_pin, pin_idx); } return 0; } @@ -1647,63 +2157,160 @@ 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; hdmi_init_pin(codec, pin_nid); - snd_hda_jack_detect_enable(codec, pin_nid, pin_nid); + snd_hda_jack_detect_enable_callback(codec, pin_nid, pin_nid, + codec->jackpoll_interval > 0 ? jack_callback : NULL); } return 0; } +static void hdmi_array_init(struct hdmi_spec *spec, int nums) +{ + snd_array_init(&spec->pins, sizeof(struct hdmi_spec_per_pin), nums); + snd_array_init(&spec->cvts, sizeof(struct hdmi_spec_per_cvt), nums); + snd_array_init(&spec->pcm_rec, sizeof(struct hda_pcm), nums); +} + +static void hdmi_array_free(struct hdmi_spec *spec) +{ + snd_array_free(&spec->pins); + snd_array_free(&spec->cvts); + snd_array_free(&spec->pcm_rec); +} + static void generic_hdmi_free(struct hda_codec *codec) { struct hdmi_spec *spec = codec->spec; int 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 void intel_haswell_fixup_connect_list(struct hda_codec *codec) +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; - hda_nid_t list[3] = {0x2, 0x3, 0x4}; - vendor_param = snd_hda_codec_read(codec, 0x08, 0, 0xf81, 0); - if (vendor_param == -1 || vendor_param & 0x02) + 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; - /* enable DP1.2 mode */ - vendor_param |= 0x02; - snd_hda_codec_read(codec, 0x08, 0, 0x781, vendor_param); + 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, 0x08, 0, 0xf81, 0); - if (vendor_param == -1 || !(vendor_param & 0x02)) + 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; - /* override 3 pins connection list */ - snd_hda_override_conn_list(codec, 0x05, 3, list); - snd_hda_override_conn_list(codec, 0x06, 3, list); - snd_hda_override_conn_list(codec, 0x07, 3, list); + /* 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) { @@ -1713,10 +2320,18 @@ 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 (codec->vendor_id == 0x80862807) - intel_haswell_fixup_connect_list(codec); + if (is_haswell(codec) || is_valleyview(codec)) { + codec->depop_delay = 0; + } if (hdmi_parse_codec(codec) < 0) { codec->spec = NULL; @@ -1724,6 +2339,11 @@ static int patch_generic_hdmi(struct hda_codec *codec) 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(); @@ -1738,24 +2358,30 @@ 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; + struct hda_pcm *info; unsigned int chans; struct hda_pcm_stream *pstr; + struct hdmi_spec_per_cvt *per_cvt; - codec->num_pcms = 1; - codec->pcm_info = info; - - chans = get_wcaps(codec, spec->cvts[0].cvt_nid); + 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(0); + 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 = spec->cvts[0].cvt_nid; + 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; } @@ -1775,11 +2401,13 @@ static void simple_hdmi_unsol_event(struct hda_codec *codec, 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; - err = snd_hda_create_spdif_out_ctls(codec, - spec->cvts[0].cvt_nid, - spec->cvts[0].cvt_nid); + 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); @@ -1788,7 +2416,8 @@ static int simple_playback_build_controls(struct hda_codec *codec) static int simple_playback_init(struct hda_codec *codec) { struct hdmi_spec *spec = codec->spec; - hda_nid_t pin = spec->pins[0].pin_nid; + 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); @@ -1804,6 +2433,7 @@ static void simple_playback_free(struct hda_codec *codec) { struct hdmi_spec *spec = codec->spec; + hdmi_array_free(spec); kfree(spec); } @@ -1967,20 +2597,29 @@ 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; - spec->cvts[0].cvt_nid = cvt_nid; - spec->pins[0].pin_nid = pin_nid; + 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; @@ -2057,9 +2696,11 @@ static int nvhdmi_8ch_7x_pcm_prepare(struct hda_pcm_stream *hinfo, int i; struct hdmi_spec *spec = codec->spec; struct hda_spdif_out *spdif; + struct hdmi_spec_per_cvt *per_cvt; mutex_lock(&codec->spdif_mutex); - spdif = snd_hda_spdif_out_of_nid(codec, spec->cvts[0].cvt_nid); + per_cvt = get_cvt(spec, 0); + spdif = snd_hda_spdif_out_of_nid(codec, per_cvt->cvt_nid); chs = substream->runtime->channels; @@ -2181,13 +2822,17 @@ static int nvhdmi_7x_8ch_build_pcms(struct hda_codec *codec) { struct hdmi_spec *spec = codec->spec; int err = simple_playback_build_pcms(codec); - spec->pcm_rec[0].own_chmap = true; + if (!err) { + struct hda_pcm *info = get_pcm_rec(spec, 0); + info->own_chmap = true; + } return err; } 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; @@ -2196,7 +2841,8 @@ static int nvhdmi_7x_8ch_build_controls(struct hda_codec *codec) return err; /* add channel maps */ - err = snd_pcm_add_chmap_ctls(spec->pcm_rec[0].pcm, + 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) @@ -2235,48 +2881,399 @@ 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; + + return hdmi_chmap_cea_alloc_validate_get_type(cap, channels); +} + +static int nvhdmi_chmap_validate(int ca, int chs, unsigned char *map) +{ + if (ca == 0x00 && (map[0] != SNDRV_CHMAP_FL || map[1] != SNDRV_CHMAP_FR)) + return -EINVAL; + + 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; + + 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 ATIHDMI_CVT_NID 0x02 /* audio converter */ -#define ATIHDMI_PIN_NID 0x03 /* HDMI output pin */ +#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 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; + + if (hdmi_slot > 7) + return -EINVAL; + + 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) +{ + 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; -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) + 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; + + return 0; +} + +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 chans = substream->runtime->channels; - int i, err; + int pin_idx, err; - err = simple_playback_pcm_prepare(hinfo, codec, stream_tag, format, - substream); - if (err < 0) + err = generic_hdmi_init(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); + + 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; - int err = patch_simple_hdmi(codec, ATIHDMI_CVT_NID, ATIHDMI_PIN_NID); - if (err < 0) + struct hdmi_spec_per_cvt *per_cvt; + int err, cvt_idx; + + err = patch_generic_hdmi(codec); + + if (err) return err; + + codec->patch_ops.init = atihdmi_init; + spec = codec->spec; - spec->pcm_playback.ops.prepare = atihdmi_playback_pcm_prepare; + + spec->ops.pin_get_eld = atihdmi_pin_get_eld; + spec->ops.pin_get_slot_channel = atihdmi_pin_get_slot_channel; + spec->ops.pin_set_slot_channel = atihdmi_pin_set_slot_channel; + spec->ops.pin_setup_infoframe = atihdmi_pin_setup_infoframe; + spec->ops.pin_hbr_setup = atihdmi_pin_hbr_setup; + spec->ops.setup_stream = atihdmi_setup_stream; + + if (!has_amd_full_remap_support(codec)) { + /* override to ATI/AMD-specific versions with pairwise mapping */ + spec->ops.chmap_cea_alloc_validate_get_type = + atihdmi_paired_chmap_cea_alloc_validate_get_type; + spec->ops.cea_alloc_to_tlv_chmap = atihdmi_paired_cea_alloc_to_tlv_chmap; + spec->ops.chmap_validate = atihdmi_paired_chmap_validate; + } + + /* ATI/AMD converters do not advertise all of their capabilities */ + for (cvt_idx = 0; cvt_idx < spec->num_cvts; cvt_idx++) { + per_cvt = get_cvt(spec, cvt_idx); + per_cvt->channels_max = max(per_cvt->channels_max, 8u); + per_cvt->rates |= SUPPORTED_RATES; + per_cvt->formats |= SUPPORTED_FORMATS; + per_cvt->maxbps = max(per_cvt->maxbps, 24u); + } + + spec->channels_max = max(spec->channels_max, 8u); + return 0; } @@ -2290,13 +3287,22 @@ static int patch_via_hdmi(struct hda_codec *codec) } /* + * called from hda_codec.c for generic HDMI support + */ +int snd_hda_parse_hdmi_codec(struct hda_codec *codec) +{ + return patch_generic_hdmi(codec); +} +EXPORT_SYMBOL_GPL(snd_hda_parse_hdmi_codec); + +/* * patch entries */ static 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 }, @@ -2305,30 +3311,34 @@ 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 = 0x10de0051, .name = "GPU 51 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 }, @@ -2342,7 +3352,9 @@ static const struct hda_codec_preset snd_hda_preset_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 */ }; @@ -2374,13 +3386,17 @@ 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"); @@ -2395,7 +3411,9 @@ 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 cf388617110..b60824e9040 100644 --- a/sound/pci/hda/patch_realtek.c +++ b/sound/pci/hda/patch_realtek.c @@ -27,20 +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_auto_parser.h" -#include "hda_beep.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 @@ -54,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; @@ -67,355 +82,51 @@ struct alc_customize_define { unsigned int fixup:1; /* Means that this sku is set by driver, not read from hw */ }; -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 - -/* make compatible with old code */ -#define alc_apply_pincfgs snd_hda_apply_pincfgs -#define alc_apply_fixup snd_hda_apply_fixup -#define alc_pick_fixup snd_hda_pick_fixup -#define alc_fixup hda_fixup -#define alc_pincfg hda_pintbl -#define alc_model_fixup hda_model_fixup - -#define ALC_FIXUP_PINS HDA_FIXUP_PINS -#define ALC_FIXUP_VERBS HDA_FIXUP_VERBS -#define ALC_FIXUP_FUNC HDA_FIXUP_FUNC - -#define ALC_FIXUP_ACT_PRE_PROBE HDA_FIXUP_ACT_PRE_PROBE -#define ALC_FIXUP_ACT_PROBE HDA_FIXUP_ACT_PROBE -#define ALC_FIXUP_ACT_INIT HDA_FIXUP_ACT_INIT -#define ALC_FIXUP_ACT_BUILD HDA_FIXUP_ACT_BUILD - - struct alc_spec { - struct hda_gen_spec gen; + 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() */ - 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; /* min. channel count (for speakers) */ - int ext_channel_count; /* current channel count for multi-io */ - - /* 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); #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 */ - 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? */ - unsigned int no_primary_hp:1; /* Don't prefer HP pins to speaker pins */ - - /* 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; - struct hda_vmaster_mute_hook vmaster_mute; -#ifdef CONFIG_PM - struct hda_loopback_check loopback; - int num_loopbacks; - struct hda_amp_list loopback_list[8]; -#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; - - /* multi-io */ - int multi_ios; - struct alc_multi_io multi_io[4]; - - /* bind volumes */ - struct snd_array bind_ctls; }; -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); -static void alc_inv_dmic_sync(struct hda_codec *codec, bool force); - -/* for shared I/O, change the pin-control accordingly */ -static void update_shared_mic_hp(struct hda_codec *codec, bool set_as_mic) -{ - struct alc_spec *spec = codec->spec; - unsigned int val; - hda_nid_t pin = spec->autocfg.inputs[1].pin; - /* NOTE: this assumes that there are only two inputs, the - * first is the real internal mic and the second is HP/mic jack. - */ - - val = snd_hda_get_default_vref(codec, pin); - - /* This pin does not have vref caps - let's enable vref on pin 0x18 - instead, as suggested by Realtek */ - if (val == AC_PINCTL_VREF_HIZ) { - const hda_nid_t vref_pin = 0x18; - /* Sanity check pin 0x18 */ - if (get_wcaps_type(get_wcaps(codec, vref_pin)) == AC_WID_PIN && - get_defcfg_connect(snd_hda_codec_get_pincfg(codec, vref_pin)) == AC_JACK_PORT_NONE) { - unsigned int vref_val = snd_hda_get_default_vref(codec, vref_pin); - if (vref_val != AC_PINCTL_VREF_HIZ) - snd_hda_set_pin_ctl(codec, vref_pin, PIN_IN | (set_as_mic ? vref_val : 0)); - } - } - - val = set_as_mic ? val | PIN_IN : PIN_HP; - snd_hda_set_pin_ctl(codec, pin, val); - - spec->automute_speaker = !set_as_mic; - call_update_outputs(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; - - if (!spec->input_mux) - return 0; - - 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; - - if (spec->shared_mic_hp) - update_shared_mic_hp(codec, spec->cur_mux[adc_idx]); - - 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_num_conns(codec, nid); - 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); - } - alc_inv_dmic_sync(codec, true); - 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) - val |= snd_hda_get_default_vref(codec, nid); - snd_hda_set_pin_ctl(codec, nid, val); -} - /* * Append the given mixer and verb elements for the later use * The mixer array is referred in build_controls(), and init_verbs are @@ -485,171 +196,6 @@ 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_set_pin_ctl(codec, nid, 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 hda_jack_tbl *jack) -{ - 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, struct hda_jack_tbl *jack) -{ - struct alc_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); -} - -#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, struct hda_jack_tbl *jack) -{ - 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); -} - /* 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) { @@ -679,14 +225,6 @@ static void alc880_unsol_event(struct hda_codec *codec, unsigned int res) snd_hda_jack_unsol_event(codec, res >> 2); } -/* call init functions of standard auto-mute helpers */ -static void alc_inithook(struct hda_codec *codec) -{ - alc_hp_automute(codec, NULL); - alc_line_automute(codec, NULL); - alc_mic_automute(codec, NULL); -} - /* additional initialization for ALC888 variants */ static void alc888_coef_init(struct hda_codec *codec) { @@ -743,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 */ @@ -807,366 +349,6 @@ 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 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 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, 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; - knew->name = kstrdup(name, GFP_KERNEL); - if (!knew->name) - return NULL; - return knew; -} - -static int alc_add_automute_mode_enum(struct hda_codec *codec) -{ - struct alc_spec *spec = codec->spec; - - if (!alc_kcontrol_new(spec, "Auto-Mute Mode", &alc_automute_mode_enum)) - return -ENOMEM; - return 0; -} - -/* - * Check the availability of HP/line-out auto-mute; - * Set up appropriately if really supported - */ -static int 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, err; - - 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; - - 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_callback(codec, nid, ALC_HP_EVENT, - alc_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; - snd_printdd("realtek: Enable Line-Out " - "auto-muting on NID 0x%x\n", nid); - snd_hda_jack_detect_enable_callback(codec, nid, ALC_FRONT_EVENT, - alc_line_automute); - 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 */ - err = alc_add_automute_mode_enum(codec); - if (err < 0) - return err; - } - 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; -} - -/* 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; -} - -/* check whether all auto-mic pins are valid; setup indices if OK */ -static bool alc_auto_mic_check_imux(struct hda_codec *codec) -{ - 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; - } - - 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_callback(codec, spec->ext_mic_pin, - ALC_MIC_EVENT, alc_mic_automute); - if (spec->dock_mic_pin) - snd_hda_jack_detect_enable_callback(codec, spec->dock_mic_pin, - ALC_MIC_EVENT, - alc_mic_automute); - - 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 int alc_init_auto_mic(struct hda_codec *codec) -{ - 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 0; /* 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 0; /* already occupied */ - if (cfg->inputs[i].type != AUTO_PIN_MIC) - return 0; /* invalid type */ - fixed = nid; - break; - case INPUT_PIN_ATTR_UNUSED: - return 0; /* invalid entry */ - case INPUT_PIN_ATTR_DOCK: - if (dock) - return 0; /* already occupied */ - if (cfg->inputs[i].type > AUTO_PIN_LINE_IN) - return 0; /* invalid type */ - dock = nid; - break; - default: - if (ext) - return 0; /* already occupied */ - if (cfg->inputs[i].type != AUTO_PIN_MIC) - return 0; /* invalid type */ - ext = nid; - break; - } - } - if (!ext && dock) { - ext = dock; - dock = 0; - } - if (!ext || !fixed) - return 0; - if (!is_jack_detectable(codec, ext)) - return 0; /* no unsol support */ - if (dock && !is_jack_detectable(codec, dock)) - return 0; /* 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 0; - - snd_printdd("realtek: Enable auto-mic switch on NID 0x%x/0x%x/0x%x\n", - ext, fixed, dock); - - return 0; -} - -/* check the availabilities of auto-mute and auto-mic switches */ -static int alc_auto_check_switches(struct hda_codec *codec) -{ - int err; - - err = alc_init_automute(codec); - if (err < 0) - return err; - err = alc_init_auto_mic(codec); - if (err < 0) - return err; - return 0; -} /* * Realtek SSID verification @@ -1187,6 +369,17 @@ static void alc_fixup_sku_ignore(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; + + if (action == HDA_FIXUP_ACT_PROBE) { + spec->no_depop_delay = 1; + codec->depop_delay = 0; + } +} + static int alc_auto_parse_customize_define(struct hda_codec *codec) { unsigned int ass, tmp, i; @@ -1202,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; @@ -1212,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; } @@ -1237,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) { @@ -1267,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; @@ -1283,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 */ @@ -1299,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; @@ -1316,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 @@ -1354,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; } @@ -1381,10 +575,10 @@ 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; } } @@ -1392,26 +586,35 @@ static void alc_ssid_check(struct hda_codec *codec, const hda_nid_t *ports) /* * 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) { @@ -1422,252 +625,54 @@ 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) -{ - 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_set_pin_ctl(codec, pin, 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_set_pin_ctl(codec, pin, PIN_IN); -} - -/* parse digital I/Os and set up NIDs in BIOS auto-parse mode */ -static void alc_auto_parse_digital(struct hda_codec *codec) -{ - 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++; - } - - 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; - } - } - } -} - -/* - * capture mixer elements */ -static int alc_cap_vol_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 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_volume_info(kcontrol, uinfo); - mutex_unlock(&codec->control_mutex); - return err; -} -static int alc_cap_vol_tlv(struct snd_kcontrol *kcontrol, int op_flag, - unsigned int size, unsigned int __user *tlv) +static hda_nid_t get_adc_nid(struct hda_codec *codec, int adc_idx, int imux_idx) { - 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; + 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]; } -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 is_put) +static void alc_inv_dmic_sync_adc(struct hda_codec *codec, int adc_idx) { - struct hda_codec *codec = snd_kcontrol_chip(kcontrol); struct alc_spec *spec = codec->spec; - int i, err = 0; - - mutex_lock(&codec->control_mutex); - if (is_put && 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); - } - if (err >= 0 && is_put) - alc_inv_dmic_sync(codec, false); - 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); -} - -/* capture mixer elements */ -#define alc_cap_sw_info snd_ctl_boolean_stereo_info - -static int alc_cap_sw_get(struct snd_kcontrol *kcontrol, - struct snd_ctl_elem_value *ucontrol) -{ - return alc_cap_getput_caller(kcontrol, ucontrol, - snd_hda_mixer_amp_switch_get, false); -} - -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); -} + struct hda_input_mux *imux = &spec->gen.input_mux; + struct nid_path *path; + hda_nid_t nid; + int i, dir, parm; + unsigned int val; -#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 }, \ + 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; -#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, \ - } + 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); -#define DEFINE_CAPMIX(num) \ -static const struct snd_kcontrol_new alc_capture_mixer ## num[] = { \ - _DEFINE_CAPMIX(num), \ - _DEFINE_CAPSRC(num), \ - { } /* end */ \ -} + /* flush all cached amps at first */ + snd_hda_codec_flush_cache(codec); -#define DEFINE_CAPMIX_NOSRC(num) \ -static const struct snd_kcontrol_new alc_capture_mixer_nosrc ## num[] = { \ - _DEFINE_CAPMIX(num), \ - { } /* end */ \ + /* 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); } -/* 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); - /* * Inverted digital-mic handling * @@ -1686,43 +691,32 @@ DEFINE_CAPMIX_NOSRC(3); static void alc_inv_dmic_sync(struct hda_codec *codec, bool force) { struct alc_spec *spec = codec->spec; - int i; + int src, nums; if (!spec->inv_dmic_fixup) return; if (!spec->inv_dmic_muted && !force) return; - for (i = 0; i < spec->num_adc_nids; i++) { - int src = spec->dyn_adc_switch ? 0 : i; + nums = spec->gen.dyn_adc_switch ? 1 : spec->gen.num_adc_nids; + for (src = 0; src < nums; src++) { bool dmic_fixup = false; - hda_nid_t nid; - int parm, dir, v; if (spec->inv_dmic_muted && - spec->imux_pins[spec->cur_mux[src]] == spec->inv_dmic_pin) + spec->gen.imux_pins[spec->gen.cur_mux[src]] == spec->inv_dmic_pin) dmic_fixup = true; if (!dmic_fixup && !force) continue; - if (spec->vol_in_capsrc) { - nid = spec->capsrc_nids[i]; - parm = AC_AMP_SET_RIGHT | AC_AMP_SET_OUTPUT; - dir = HDA_OUTPUT; - } else { - nid = spec->adc_nids[i]; - parm = AC_AMP_SET_RIGHT | AC_AMP_SET_INPUT; - dir = HDA_INPUT; - } - /* we care only right channel */ - v = snd_hda_codec_amp_read(codec, nid, 1, dir, 0); - if (v & 0x80) /* if already muted, we don't need to touch */ - continue; - if (dmic_fixup) /* add mute for d-mic */ - v |= 0x80; - snd_hda_codec_write(codec, nid, 0, AC_VERB_SET_AMP_GAIN_MUTE, - parm | v); + alc_inv_dmic_sync_adc(codec, src); } } +static void alc_inv_dmic_hook(struct hda_codec *codec, + struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + alc_inv_dmic_sync(codec, false); +} + static int alc_inv_dmic_sw_get(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) { @@ -1749,6 +743,7 @@ static int alc_inv_dmic_sw_put(struct snd_kcontrol *kcontrol, 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, @@ -1758,51 +753,23 @@ static int alc_add_inv_dmic_mixer(struct hda_codec *codec, hda_nid_t nid) { struct alc_spec *spec = codec->spec; - if (!alc_kcontrol_new(spec, "Inverted Internal Mic Capture Switch", - &alc_inv_dmic_sw)) + 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; } /* typically the digital mic is put at node 0x12 */ static void alc_fixup_inv_dmic_0x12(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_PROBE) alc_add_inv_dmic_mixer(codec, 0x12); } -/* - * virtual master controls - */ - -/* - * slave controls for virtual master - */ -static const char * const alc_slave_pfxs[] = { - "Front", "Surround", "Center", "LFE", "Side", - "Headphone", "Speaker", "Mono", "Line Out", - "CLFE", "Bass Speaker", "PCM", - 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 */ @@ -1813,45 +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_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; - } #ifdef CONFIG_SND_HDA_INPUT_BEEP /* create beep controls if needed */ @@ -1870,130 +812,7 @@ 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_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, alc_slave_pfxs, - "Playback Switch", - true, &spec->vmaster_mute.sw_kctl); - 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 */ - - return 0; -} - -static int alc_build_jacks(struct hda_codec *codec) -{ - struct alc_spec *spec = codec->spec; - - if (spec->shared_mic_hp) { - int err; - int nid = spec->autocfg.inputs[1].pin; - err = snd_hda_jack_add_kctl(codec, nid, "Headphone Mic", 0); - if (err < 0) - return err; - err = snd_hda_jack_detect_enable(codec, nid, 0); - if (err < 0) - return err; - } - - return snd_hda_jack_add_kctls(codec, &spec->autocfg); -} - -static int alc_build_controls(struct hda_codec *codec) -{ - int err = __alc_build_controls(codec); - if (err < 0) - return err; - - err = alc_build_jacks(codec); - if (err < 0) - return err; - alc_apply_fixup(codec, ALC_FIXUP_ACT_BUILD); + snd_hda_apply_fixup(codec, HDA_FIXUP_ACT_BUILD); return 0; } @@ -2002,9 +821,6 @@ static int alc_build_controls(struct hda_codec *codec) * Common callbacks */ -static void alc_init_special_input_src(struct hda_codec *codec); -static void alc_auto_init_std(struct hda_codec *codec); - static int alc_init(struct hda_codec *codec) { struct alc_spec *spec = codec->spec; @@ -2015,347 +831,9 @@ static int alc_init(struct hda_codec *codec) alc_fix_pll(codec); alc_auto_init_amp(codec, spec->init_amp); - snd_hda_gen_apply_verbs(codec); - alc_init_special_input_src(codec); - alc_auto_init_std(codec); - - alc_apply_fixup(codec, ALC_FIXUP_ACT_INIT); - - hda_call_check_power_status(codec, 0x01); - return 0; -} - -#ifdef CONFIG_PM -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); -} + snd_hda_gen_init(codec); -/* - * 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]; - 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->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; - } - } - } - - 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; - } - - 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; } @@ -2366,47 +844,11 @@ 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; - - alc_free_kctls(codec); - alc_free_bind_ctls(codec); - snd_hda_gen_free(&spec->gen); - kfree(spec); - snd_hda_detach_beep_device(codec); -} +#define alc_free snd_hda_gen_free #ifdef CONFIG_PM static void alc_power_eapd(struct hda_codec *codec) @@ -2427,7 +869,10 @@ static int alc_suspend(struct hda_codec *codec) #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); @@ -2441,16 +886,14 @@ 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 = snd_hda_jack_unsol_event, #ifdef CONFIG_PM .resume = alc_resume, -#endif -#ifdef CONFIG_PM .suspend = alc_suspend, - .check_power_status = alc_check_power_status, + .check_power_status = snd_hda_gen_check_power_status, #endif .reboot_notify = alc_shutup, }; @@ -2469,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; @@ -2478,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" }, @@ -2487,6 +938,7 @@ static struct alc_codec_rename_table rename_tbl[] = { { 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" }, @@ -2497,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) @@ -2507,1730 +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, name, &alc_control_templates[type]); - if (!knew) - 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 (ch >= ARRAY_SIZE(channel_name)) { - snd_BUG(); - return "PCM"; - } - - return channel_name[ch]; -} - -#ifdef CONFIG_PM -/* add the powersave loopback-list entry */ -static void add_loopback_list(struct alc_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; -} -#else -#define add_loopback_list(spec, mix, idx) /* NOP */ -#endif - -/* 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; - add_loopback_list(spec, mix_nid, idx); - 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; - - nid = codec->start_nid; - for (i = 0; i < codec->num_nodes; i++, nid++) { - hda_nid_t src; - 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_num_conns(codec, src); - if (n > 1) { - cap_nids[nums] = src; - break; - } else if (n != 1) - break; - if (snd_hda_get_connections(codec, src, &src, 1) != 1) - break; - } - 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_set_pin_ctl(codec, nid, 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_num_conns(codec, spec->mixer_nid); - 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) + if (!codec->bus->pci) 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; -} - -static bool alc_is_dac_already_used(struct hda_codec *codec, hda_nid_t nid) -{ - struct alc_spec *spec = codec->spec; - int i; - if (found_in_nid_list(nid, spec->multiout.dac_nids, - ARRAY_SIZE(spec->private_dac_nids)) || - found_in_nid_list(nid, spec->multiout.hp_out_nid, - ARRAY_SIZE(spec->multiout.hp_out_nid)) || - found_in_nid_list(nid, spec->multiout.extra_out_nid, - ARRAY_SIZE(spec->multiout.extra_out_nid))) - return true; - for (i = 0; i < spec->multi_ios; i++) { - if (spec->multi_io[i].dac == nid) - return true; - } - return false; -} - -/* look for an empty DAC slot */ -static hda_nid_t alc_auto_look_for_dac(struct hda_codec *codec, hda_nid_t pin) -{ - 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) + for (q = rename_pci_tbl; q->codec_vendor_id; q++) { + if (q->codec_vendor_id != codec->vendor_id) continue; - if (!alc_is_dac_already_used(codec, nid)) - 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; - - if (!pin || !dac) - return false; - 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) -{ - struct alc_spec *spec = codec->spec; - hda_nid_t sel = alc_go_down_to_selector(codec, pin); - hda_nid_t nid, nid_found, srcs[5]; - int i, num = snd_hda_get_connections(codec, sel, srcs, - ARRAY_SIZE(srcs)); - if (num == 1) - return alc_auto_look_for_dac(codec, pin); - nid_found = 0; - for (i = 0; i < num; i++) { - if (srcs[i] == spec->mixer_nid) - continue; - nid = alc_auto_mix_to_dac(codec, srcs[i]); - if (nid && !alc_is_dac_already_used(codec, nid)) { - if (nid_found) - return 0; - nid_found = nid; - } - } - return nid_found; -} - -/* mark up volume and mute control NIDs: used during badness parsing and - * at creating actual controls - */ -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 void clear_vol_marks(struct hda_codec *codec) -{ - struct alc_spec *spec = codec->spec; - memset(spec->vol_ctls, 0, sizeof(spec->vol_ctls)); - memset(spec->sw_ctls, 0, sizeof(spec->sw_ctls)); -} - -/* 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 = 0x103, - /* 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, - /* 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, -}; - -static hda_nid_t alc_look_for_out_mute_nid(struct hda_codec *codec, - hda_nid_t pin, hda_nid_t dac); -static hda_nid_t alc_look_for_out_vol_nid(struct hda_codec *codec, - hda_nid_t pin, hda_nid_t dac); - -static int eval_shared_vol_badness(struct hda_codec *codec, hda_nid_t pin, - hda_nid_t dac) -{ - struct alc_spec *spec = codec->spec; - hda_nid_t nid; - unsigned int val; - int badness = 0; - - nid = alc_look_for_out_vol_nid(codec, pin, dac); - if (nid) { - val = HDA_COMPOSE_AMP_VAL(nid, 3, 0, HDA_OUTPUT); - if (is_ctl_used(spec->vol_ctls, nid)) - badness += BAD_SHARED_VOL; - else - mark_ctl_usage(spec->vol_ctls, val); - } else - badness += BAD_SHARED_VOL; - nid = alc_look_for_out_mute_nid(codec, pin, dac); - 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) - 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(spec->sw_ctls, val)) - badness += BAD_SHARED_VOL; - else - mark_ctl_usage(spec->sw_ctls, val); - } else - badness += BAD_SHARED_VOL; - return badness; -} - -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 */ -}; - -static struct badness_table 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, -}; - -static struct badness_table 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, -}; - -/* try to assign DACs to pins and return the resultant badness */ -static int alc_auto_fill_dacs(struct hda_codec *codec, int num_outs, - const hda_nid_t *pins, hda_nid_t *dacs, - const struct badness_table *bad) -{ - struct alc_spec *spec = codec->spec; - struct auto_pin_cfg *cfg = &spec->autocfg; - int i, j; - int badness = 0; - hda_nid_t dac; - - if (!num_outs) - return 0; - - for (i = 0; i < num_outs; i++) { - hda_nid_t pin = pins[i]; - if (!dacs[i]) - dacs[i] = alc_auto_look_for_dac(codec, pin); - if (!dacs[i] && !i) { - for (j = 1; j < num_outs; j++) { - if (alc_auto_is_dac_reachable(codec, pin, dacs[j])) { - dacs[0] = dacs[j]; - dacs[j] = 0; - break; - } - } - } - dac = dacs[i]; - if (!dac) { - if (alc_auto_is_dac_reachable(codec, pin, dacs[0])) - dac = dacs[0]; - else if (cfg->line_outs > i && - alc_auto_is_dac_reachable(codec, pin, - spec->private_dac_nids[i])) - dac = spec->private_dac_nids[i]; - if (dac) { - if (!i) - badness += bad->shared_primary; - else if (i == 1) - badness += bad->shared_surr; - else - badness += bad->shared_clfe; - } else if (alc_auto_is_dac_reachable(codec, pin, - spec->private_dac_nids[0])) { - 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) - badness += eval_shared_vol_badness(codec, pin, dac); - } - - return badness; -} - -static int alc_auto_fill_multi_ios(struct hda_codec *codec, - hda_nid_t reference_pin, - bool hardwired, int offset); - -static bool alc_map_singles(struct hda_codec *codec, int outs, - const hda_nid_t *pins, hda_nid_t *dacs) -{ - int i; - bool found = false; - for (i = 0; i < outs; i++) { - if (dacs[i]) - continue; - dacs[i] = get_dac_if_single(codec, pins[i]); - if (dacs[i]) - found = true; - } - return found; -} - -/* 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 alc_spec *spec = codec->spec; - struct auto_pin_cfg *cfg = &spec->autocfg; - int i, err, badness; - - /* set num_dacs once to full for alc_auto_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; - clear_vol_marks(codec); - badness = 0; - - /* fill hard-wired DACs first */ - if (fill_hardwired) { - bool mapped; - do { - mapped = alc_map_singles(codec, cfg->line_outs, - cfg->line_out_pins, - spec->private_dac_nids); - mapped |= alc_map_singles(codec, cfg->hp_outs, - cfg->hp_pins, - spec->multiout.hp_out_nid); - mapped |= alc_map_singles(codec, cfg->speaker_outs, - cfg->speaker_pins, - spec->multiout.extra_out_nid); - if (fill_mio_first && cfg->line_outs == 1 && - cfg->line_out_type != AUTO_PIN_SPEAKER_OUT) { - err = alc_auto_fill_multi_ios(codec, cfg->line_out_pins[0], true, 0); - if (!err) - mapped = true; - } - } while (mapped); - } - - badness += alc_auto_fill_dacs(codec, cfg->line_outs, cfg->line_out_pins, - spec->private_dac_nids, - &main_out_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; - } - } - - if (fill_mio_first && - cfg->line_outs == 1 && cfg->line_out_type != AUTO_PIN_SPEAKER_OUT) { - /* try to fill multi-io first */ - err = alc_auto_fill_multi_ios(codec, cfg->line_out_pins[0], false, 0); - if (err < 0) - return err; - /* we don't count badness at this stage yet */ - } - - if (cfg->line_out_type != AUTO_PIN_HP_OUT) { - err = alc_auto_fill_dacs(codec, cfg->hp_outs, cfg->hp_pins, - spec->multiout.hp_out_nid, - &extra_out_badness); - if (err < 0) - return err; - badness += err; - } - if (cfg->line_out_type != AUTO_PIN_SPEAKER_OUT) { - err = alc_auto_fill_dacs(codec, cfg->speaker_outs, - cfg->speaker_pins, - spec->multiout.extra_out_nid, - &extra_out_badness); - if (err < 0) - return err; - badness += err; - } - if (cfg->line_outs == 1 && cfg->line_out_type != AUTO_PIN_SPEAKER_OUT) { - err = alc_auto_fill_multi_ios(codec, cfg->line_out_pins[0], false, 0); - if (err < 0) - return err; - badness += err; - } - if (cfg->hp_outs && cfg->line_out_type == AUTO_PIN_SPEAKER_OUT) { - /* try multi-ios with HP + inputs */ - int offset = 0; - if (cfg->line_outs >= 3) - offset = 1; - err = alc_auto_fill_multi_ios(codec, cfg->hp_pins[0], false, - offset); - if (err < 0) - return err; - badness += err; - } - - if (spec->multi_ios == 2) { - for (i = 0; i < 2; i++) - spec->private_dac_nids[spec->multiout.num_dacs++] = - spec->multi_io[i].dac; - spec->ext_channel_count = 2; - } else if (spec->multi_ios) { - spec->multi_ios = 0; - badness += BAD_MULTI_IO; - } - - return badness; -} - -#define DEBUG_BADNESS - -#ifdef DEBUG_BADNESS -#define debug_badness snd_printdd -#else -#define debug_badness(...) -#endif - -static void debug_show_configs(struct alc_spec *spec, struct auto_pin_cfg *cfg) -{ - debug_badness("multi_outs = %x/%x/%x/%x : %x/%x/%x/%x\n", - cfg->line_out_pins[0], cfg->line_out_pins[1], - cfg->line_out_pins[2], cfg->line_out_pins[2], - spec->multiout.dac_nids[0], - spec->multiout.dac_nids[1], - spec->multiout.dac_nids[2], - spec->multiout.dac_nids[3]); - 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); - 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[2], - spec->multiout.hp_out_nid[0], - spec->multiout.hp_out_nid[1], - spec->multiout.hp_out_nid[2], - spec->multiout.hp_out_nid[3]); - 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]); -} - -static int alc_auto_fill_dac_nids(struct hda_codec *codec) -{ - struct alc_spec *spec = codec->spec; - struct auto_pin_cfg *cfg = &spec->autocfg; - struct auto_pin_cfg *best_cfg; - 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(spec, 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) { - *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(spec, cfg); - - 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]); - - /* clear the bitmap flags for creating controls */ - clear_vol_marks(codec); - kfree(best_cfg); - return 0; -} - -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_num_conns(codec, nid) == 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; - index = 0; - name = channel_name[i]; - } else { - pin = cfg->line_out_pins[i]; - name = alc_get_line_out_pfx(spec, i, true, &index); - } - - sw = alc_look_for_out_mute_nid(codec, pin, dac); - vol = alc_look_for_out_vol_nid(codec, pin, dac); - 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; - 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]) - 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); - } - - for (i = 0; i < num_pins; i++) { - hda_nid_t dac; - if (dacs[num_pins - 1]) - dac = dacs[i]; /* with individual volumes */ - else - dac = 0; - if (num_pins == 2 && i == 1 && !strcmp(pfx, "Speaker")) { - err = alc_auto_create_extra_out(codec, pins[i], dac, - "Bass Speaker", 0); - } else if (num_pins >= 3) { - snprintf(name, sizeof(name), "%s %s", - pfx, channel_name[i]); - err = alc_auto_create_extra_out(codec, pins[i], dac, - name, 0); - } else { - err = alc_auto_create_extra_out(codec, pins[i], dac, - pfx, i); - } - if (err < 0) - return err; - } - if (dacs[num_pins - 1]) - return 0; - - /* Let's create a bind-controls for volumes */ - 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]) - 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); - } -} - -/* 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; -} - -/* - * multi-io helper - * - * 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 int alc_auto_fill_multi_ios(struct hda_codec *codec, - hda_nid_t reference_pin, - bool hardwired, int offset) -{ - struct alc_spec *spec = codec->spec; - struct auto_pin_cfg *cfg = &spec->autocfg; - int type, i, j, dacs, 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; - - old_pins = spec->multi_ios; - if (old_pins >= 2) - goto end_fill; - - 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++; - } - } - if (num_pins < 2) - goto end_fill; - - 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; - - if (cfg->inputs[i].type != type) - continue; - 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 (offset && offset + spec->multi_ios < dacs) { - dac = spec->private_dac_nids[offset + spec->multi_ios]; - if (!alc_auto_is_dac_reachable(codec, nid, dac)) - dac = 0; - } - if (hardwired) - dac = get_dac_if_single(codec, nid); - else if (!dac) - dac = alc_auto_look_for_dac(codec, nid); - if (!dac) { - badness++; - continue; - } - spec->multi_io[spec->multi_ios].pin = nid; - spec->multi_io[spec->multi_ios].dac = dac; - 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) { - spec->multi_ios = old_pins; - return badness; + if (!q->pci_subdevice || + q->pci_subdevice == codec->bus->pci->subsystem_device) + return alc_codec_rename(codec, q->name); } return 0; } -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_set_pin_ctl_cache(codec, nid, 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_set_pin_ctl_cache(codec, nid, - 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 = 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 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) { - if (!alc_kcontrol_new(spec, "Channel Mode", - &alc_auto_channel_mode_enum)) - 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; - - again: - 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_num_conns(codec, cap); - 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)) { - if (spec->shared_mic_hp) { - spec->shared_mic_hp = 0; - spec->private_imux[0].num_items = 1; - goto again; - } - 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->shared_mic_hp) - 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_num_conns(codec, cap) > 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) -{ - 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); - alc_inithook(codec); -} /* * Digital-beep handlers @@ -4241,7 +1011,9 @@ static void alc_auto_init_std(struct hda_codec *codec) 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), @@ -4273,93 +1045,20 @@ 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 (!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 = 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); - if (err < 0) - return err; - - /* check the multiple speaker pins */ - if (cfg->line_out_type == AUTO_PIN_SPEAKER_OUT) - spec->const_channel_count = cfg->line_outs * 2; - else - spec->const_channel_count = cfg->speaker_outs * 2; - - if (spec->multi_ios > 0) - spec->multiout.max_channels = max(spec->ext_channel_count, - spec->const_channel_count); - else - spec->multiout.max_channels = spec->multiout.num_dacs * 2; - - dig_only: - alc_auto_parse_digital(codec); - - if (!spec->no_analog) - alc_remove_invalid_adc_nids(codec); if (ssid_nids) alc_ssid_check(codec, ssid_nids); - if (!spec->no_analog) { - err = alc_auto_check_switches(codec); - if (err < 0) - return err; - err = alc_auto_add_mic_boost(codec); - if (err < 0) - return err; - } - - if (spec->kctls.list) - add_mixer(spec, spec->kctls.list); - - if (!spec->no_analog && !spec->cap_mixer) - set_capture_mixer(codec); + err = snd_hda_gen_parse_auto_config(codec, cfg); + if (err < 0) + return err; return 1; } @@ -4373,11 +1072,12 @@ static int alc_alloc_spec(struct hda_codec *codec, hda_nid_t mixer_nid) 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; - spec->mixer_nid = mixer_nid; - snd_hda_gen_init(&spec->gen); - snd_array_init(&spec->kctls, sizeof(struct snd_kcontrol_new), 32); - snd_array_init(&spec->bind_ctls, sizeof(struct hda_bind_ctls *), 8); + /* FIXME: do we need this for all Realtek codec models? */ + codec->spdif_status_reset = 1; err = alc_codec_rename_from_preset(codec); if (err < 0) { @@ -4402,6 +1102,7 @@ enum { 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, @@ -4411,6 +1112,7 @@ enum { 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, @@ -4420,27 +1122,28 @@ enum { ALC880_FIXUP_6ST_BASE, ALC880_FIXUP_6ST, ALC880_FIXUP_6ST_DIG, + ALC880_FIXUP_6ST_AUTOMUTE, }; /* enable the volume-knob widget support on NID 0x21 */ static void alc880_fixup_vol_knob(struct hda_codec *codec, - const struct alc_fixup *fix, int action) + const struct hda_fixup *fix, int action) { - if (action == ALC_FIXUP_ACT_PROBE) + if (action == HDA_FIXUP_ACT_PROBE) snd_hda_jack_detect_enable_callback(codec, 0x21, ALC_DCVOL_EVENT, alc_update_knob_master); } -static const struct alc_fixup alc880_fixups[] = { +static const struct hda_fixup alc880_fixups[] = { [ALC880_FIXUP_GPIO1] = { - .type = ALC_FIXUP_VERBS, + .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 }, @@ -4450,8 +1153,8 @@ static const struct alc_fixup alc880_fixups[] = { .chain_id = ALC880_FIXUP_GPIO2, }, [ALC880_FIXUP_LG] = { - .type = ALC_FIXUP_PINS, - .v.pins = (const struct alc_pincfg[]) { + .type = HDA_FIXUP_PINS, + .v.pins = (const struct hda_pintbl[]) { /* disable bogus unused pins */ { 0x16, 0x411111f0 }, { 0x18, 0x411111f0 }, @@ -4459,9 +1162,17 @@ static const struct alc_fixup alc880_fixups[] = { { } } }, + [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 = ALC_FIXUP_PINS, - .v.pins = (const struct alc_pincfg[]) { + .type = HDA_FIXUP_PINS, + .v.pins = (const struct hda_pintbl[]) { /* disable bogus unused pins */ { 0x17, 0x411111f0 }, { } @@ -4470,7 +1181,7 @@ static const struct alc_fixup alc880_fixups[] = { .chain_id = ALC880_FIXUP_GPIO2, }, [ALC880_FIXUP_EAPD_COEF] = { - .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 }, @@ -4479,7 +1190,7 @@ static const struct alc_fixup alc880_fixups[] = { }, }, [ALC880_FIXUP_TCL_S700] = { - .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 }, @@ -4490,13 +1201,13 @@ static const struct alc_fixup alc880_fixups[] = { .chain_id = ALC880_FIXUP_GPIO2, }, [ALC880_FIXUP_VOL_KNOB] = { - .type = ALC_FIXUP_FUNC, + .type = HDA_FIXUP_FUNC, .v.func = alc880_fixup_vol_knob, }, [ALC880_FIXUP_FUJITSU] = { /* override all pins as BIOS on old Amilo is broken */ - .type = ALC_FIXUP_PINS, - .v.pins = (const struct alc_pincfg[]) { + .type = HDA_FIXUP_PINS, + .v.pins = (const struct hda_pintbl[]) { { 0x14, 0x0121411f }, /* HP */ { 0x15, 0x99030120 }, /* speaker */ { 0x16, 0x99030130 }, /* bass speaker */ @@ -4515,8 +1226,8 @@ static const struct alc_fixup alc880_fixups[] = { }, [ALC880_FIXUP_F1734] = { /* almost compatible with FUJITSU, but no bass and SPDIF */ - .type = ALC_FIXUP_PINS, - .v.pins = (const struct alc_pincfg[]) { + .type = HDA_FIXUP_PINS, + .v.pins = (const struct hda_pintbl[]) { { 0x14, 0x0121411f }, /* HP */ { 0x15, 0x99030120 }, /* speaker */ { 0x16, 0x411111f0 }, /* N/A */ @@ -4535,8 +1246,8 @@ static const struct alc_fixup alc880_fixups[] = { }, [ALC880_FIXUP_UNIWILL] = { /* need to fix HP and speaker pins to be parsed correctly */ - .type = ALC_FIXUP_PINS, - .v.pins = (const struct alc_pincfg[]) { + .type = HDA_FIXUP_PINS, + .v.pins = (const struct hda_pintbl[]) { { 0x14, 0x0121411f }, /* HP */ { 0x15, 0x99030120 }, /* speaker */ { 0x16, 0x99030130 }, /* bass speaker */ @@ -4544,8 +1255,8 @@ static const struct alc_fixup alc880_fixups[] = { }, }, [ALC880_FIXUP_UNIWILL_DIG] = { - .type = ALC_FIXUP_PINS, - .v.pins = (const struct alc_pincfg[]) { + .type = HDA_FIXUP_PINS, + .v.pins = (const struct hda_pintbl[]) { /* disable bogus unused pins */ { 0x17, 0x411111f0 }, { 0x19, 0x411111f0 }, @@ -4555,8 +1266,8 @@ static const struct alc_fixup alc880_fixups[] = { } }, [ALC880_FIXUP_Z71V] = { - .type = ALC_FIXUP_PINS, - .v.pins = (const struct alc_pincfg[]) { + .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 */ @@ -4572,9 +1283,29 @@ static const struct alc_fixup alc880_fixups[] = { { } } }, + [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 = ALC_FIXUP_PINS, - .v.pins = (const struct alc_pincfg[]) { + .type = HDA_FIXUP_PINS, + .v.pins = (const struct hda_pintbl[]) { { 0x14, 0x01014010 }, /* line-out */ { 0x15, 0x411111f0 }, /* N/A */ { 0x16, 0x411111f0 }, /* N/A */ @@ -4591,8 +1322,8 @@ static const struct alc_fixup alc880_fixups[] = { } }, [ALC880_FIXUP_3ST] = { - .type = ALC_FIXUP_PINS, - .v.pins = (const struct alc_pincfg[]) { + .type = HDA_FIXUP_PINS, + .v.pins = (const struct hda_pintbl[]) { { 0x1e, 0x411111f0 }, /* N/A */ { } }, @@ -4600,8 +1331,8 @@ static const struct alc_fixup alc880_fixups[] = { .chain_id = ALC880_FIXUP_3ST_BASE, }, [ALC880_FIXUP_3ST_DIG] = { - .type = ALC_FIXUP_PINS, - .v.pins = (const struct alc_pincfg[]) { + .type = HDA_FIXUP_PINS, + .v.pins = (const struct hda_pintbl[]) { { 0x1e, 0x0144111e }, /* SPDIF */ { } }, @@ -4609,8 +1340,8 @@ static const struct alc_fixup alc880_fixups[] = { .chain_id = ALC880_FIXUP_3ST_BASE, }, [ALC880_FIXUP_5ST_BASE] = { - .type = ALC_FIXUP_PINS, - .v.pins = (const struct alc_pincfg[]) { + .type = HDA_FIXUP_PINS, + .v.pins = (const struct hda_pintbl[]) { { 0x14, 0x01014010 }, /* front */ { 0x15, 0x411111f0 }, /* N/A */ { 0x16, 0x01011411 }, /* CLFE */ @@ -4627,8 +1358,8 @@ static const struct alc_fixup alc880_fixups[] = { } }, [ALC880_FIXUP_5ST] = { - .type = ALC_FIXUP_PINS, - .v.pins = (const struct alc_pincfg[]) { + .type = HDA_FIXUP_PINS, + .v.pins = (const struct hda_pintbl[]) { { 0x1e, 0x411111f0 }, /* N/A */ { } }, @@ -4636,8 +1367,8 @@ static const struct alc_fixup alc880_fixups[] = { .chain_id = ALC880_FIXUP_5ST_BASE, }, [ALC880_FIXUP_5ST_DIG] = { - .type = ALC_FIXUP_PINS, - .v.pins = (const struct alc_pincfg[]) { + .type = HDA_FIXUP_PINS, + .v.pins = (const struct hda_pintbl[]) { { 0x1e, 0x0144111e }, /* SPDIF */ { } }, @@ -4645,8 +1376,8 @@ static const struct alc_fixup alc880_fixups[] = { .chain_id = ALC880_FIXUP_5ST_BASE, }, [ALC880_FIXUP_6ST_BASE] = { - .type = ALC_FIXUP_PINS, - .v.pins = (const struct alc_pincfg[]) { + .type = HDA_FIXUP_PINS, + .v.pins = (const struct hda_pintbl[]) { { 0x14, 0x01014010 }, /* front */ { 0x15, 0x01016412 }, /* surr */ { 0x16, 0x01011411 }, /* CLFE */ @@ -4663,8 +1394,8 @@ static const struct alc_fixup alc880_fixups[] = { } }, [ALC880_FIXUP_6ST] = { - .type = ALC_FIXUP_PINS, - .v.pins = (const struct alc_pincfg[]) { + .type = HDA_FIXUP_PINS, + .v.pins = (const struct hda_pintbl[]) { { 0x1e, 0x411111f0 }, /* N/A */ { } }, @@ -4672,20 +1403,31 @@ static const struct alc_fixup alc880_fixups[] = { .chain_id = ALC880_FIXUP_6ST_BASE, }, [ALC880_FIXUP_6ST_DIG] = { - .type = ALC_FIXUP_PINS, - .v.pins = (const struct alc_pincfg[]) { + .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), @@ -4694,6 +1436,7 @@ static const struct snd_pci_quirk alc880_fixup_tbl[] = { 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), @@ -4701,6 +1444,7 @@ static const struct snd_pci_quirk alc880_fixup_tbl[] = { 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. @@ -4749,13 +1493,14 @@ static const struct snd_pci_quirk alc880_fixup_tbl[] = { {} }; -static const struct alc_model_fixup alc880_fixup_models[] = { +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"}, {} }; @@ -4773,29 +1518,26 @@ static int patch_alc880(struct hda_codec *codec) return err; spec = codec->spec; - spec->need_dac_fix = 1; + spec->gen.need_dac_fix = 1; + spec->gen.beep_nid = 0x01; - alc_pick_fixup(codec, alc880_fixup_models, alc880_fixup_tbl, + snd_hda_pick_fixup(codec, alc880_fixup_models, alc880_fixup_tbl, alc880_fixups); - alc_apply_fixup(codec, ALC_FIXUP_ACT_PRE_PROBE); + snd_hda_apply_fixup(codec, HDA_FIXUP_ACT_PRE_PROBE); /* 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); - } codec->patch_ops = alc_patch_ops; codec->patch_ops.unsol_event = alc880_unsol_event; - alc_apply_fixup(codec, ALC_FIXUP_ACT_PROBE); + snd_hda_apply_fixup(codec, HDA_FIXUP_ACT_PROBE); return 0; @@ -4827,38 +1569,41 @@ enum { ALC260_FIXUP_REPLACER, ALC260_FIXUP_HP_B1900, ALC260_FIXUP_KN1, + ALC260_FIXUP_FSC_S7020, + ALC260_FIXUP_FSC_S7020_JWSE, + ALC260_FIXUP_VAIO_PINS, }; 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->hp_jack_present); + spec->gen.hp_jack_present); } static void alc260_fixup_gpio1_toggle(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) { /* although the machine has only one output pin, we need to * toggle GPIO1 according to the jack state */ - spec->automute_hook = alc260_gpio1_automute; - spec->detect_hp = 1; - spec->automute_speaker = 1; - spec->autocfg.hp_pins[0] = 0x0f; /* copy it for automute */ - snd_hda_jack_detect_enable_callback(codec, 0x0f, ALC_HP_EVENT, - alc_hp_automute); - snd_hda_gen_add_verbs(&spec->gen, alc_gpio1_init_verbs); + 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 alc_fixup *fix, int action) + const struct hda_fixup *fix, int action) { struct alc_spec *spec = codec->spec; - static const struct alc_pincfg pincfgs[] = { + static const struct hda_pintbl pincfgs[] = { { 0x0f, 0x02214000 }, /* HP/speaker */ { 0x12, 0x90a60160 }, /* int mic */ { 0x13, 0x02a19000 }, /* ext mic */ @@ -4875,70 +1620,114 @@ static void alc260_fixup_kn1(struct hda_codec *codec, }; switch (action) { - case ALC_FIXUP_ACT_PRE_PROBE: - alc_apply_pincfgs(codec, pincfgs); + case HDA_FIXUP_ACT_PRE_PROBE: + snd_hda_apply_pincfgs(codec, pincfgs); break; - case ALC_FIXUP_ACT_PROBE: + case HDA_FIXUP_ACT_PROBE: spec->init_amp = ALC_INIT_NONE; break; } } -static const struct alc_fixup alc260_fixups[] = { +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 = ALC_FIXUP_PINS, - .v.pins = (const struct alc_pincfg[]) { + .type = HDA_FIXUP_PINS, + .v.pins = (const struct hda_pintbl[]) { { 0x11, 0x90130110 }, /* speaker */ { } } }, [ALC260_FIXUP_HP_PIN_0F] = { - .type = ALC_FIXUP_PINS, - .v.pins = (const struct alc_pincfg[]) { + .type = HDA_FIXUP_PINS, + .v.pins = (const struct hda_pintbl[]) { { 0x0f, 0x01214000 }, /* HP */ { } } }, [ALC260_FIXUP_COEF] = { - .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, 0x3040 }, + { 0x1a, AC_VERB_SET_COEF_INDEX, 0x07 }, + { 0x1a, AC_VERB_SET_PROC_COEF, 0x3040 }, { } }, - .chained = true, - .chain_id = ALC260_FIXUP_HP_PIN_0F, }, [ALC260_FIXUP_GPIO1] = { - .type = ALC_FIXUP_VERBS, + .type = HDA_FIXUP_VERBS, .v.verbs = alc_gpio1_init_verbs, }, [ALC260_FIXUP_GPIO1_TOGGLE] = { - .type = ALC_FIXUP_FUNC, + .type = HDA_FIXUP_FUNC, .v.func = alc260_fixup_gpio1_toggle, .chained = true, .chain_id = ALC260_FIXUP_HP_PIN_0F, }, [ALC260_FIXUP_REPLACER] = { - .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 }, + { 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 = ALC_FIXUP_FUNC, + .type = HDA_FIXUP_FUNC, .v.func = alc260_fixup_gpio1_toggle, .chained = true, .chain_id = ALC260_FIXUP_COEF, }, [ALC260_FIXUP_KN1] = { - .type = ALC_FIXUP_FUNC, + .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[] = { @@ -4947,6 +1736,9 @@ static const struct snd_pci_quirk alc260_fixup_tbl[] = { 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), @@ -4954,6 +1746,14 @@ static const struct snd_pci_quirk alc260_fixup_tbl[] = { {} }; +static const struct hda_model_fixup alc260_fixup_models[] = { + {.id = ALC260_FIXUP_GPIO1, .name = "gpio1"}, + {.id = ALC260_FIXUP_COEF, .name = "coef"}, + {.id = ALC260_FIXUP_FSC_S7020, .name = "fujitsu"}, + {.id = ALC260_FIXUP_FSC_S7020_JWSE, .name = "fujitsu-jwse"}, + {} +}; + /* */ static int patch_alc260(struct hda_codec *codec) @@ -4966,26 +1766,29 @@ static int patch_alc260(struct hda_codec *codec) return err; 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; - alc_pick_fixup(codec, NULL, alc260_fixup_tbl, alc260_fixups); - alc_apply_fixup(codec, ALC_FIXUP_ACT_PRE_PROBE); + snd_hda_pick_fixup(codec, alc260_fixup_models, alc260_fixup_tbl, + alc260_fixups); + snd_hda_apply_fixup(codec, HDA_FIXUP_ACT_PRE_PROBE); /* 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); - } codec->patch_ops = alc_patch_ops; spec->shutup = alc_eapd_shutup; - alc_apply_fixup(codec, ALC_FIXUP_ACT_PROBE); + snd_hda_apply_fixup(codec, HDA_FIXUP_ACT_PROBE); return 0; @@ -5017,6 +1820,7 @@ 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, @@ -5034,14 +1838,19 @@ enum { 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); } @@ -5081,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); @@ -5094,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 }; @@ -5104,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); @@ -5116,62 +1925,92 @@ static void alc889_fixup_dac_route(struct hda_codec *codec, /* Set VREF on HP pin */ static void alc889_fixup_mbp_vref(struct hda_codec *codec, - const struct alc_fixup *fix, int action) + 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 != ALC_FIXUP_ACT_INIT) + 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_read(codec, nids[i], 0, - AC_VERB_GET_PIN_WIDGET_CONTROL, 0); + 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->keep_vref_in_automute = 1; + spec->gen.keep_vref_in_automute = 1; break; } } -/* Set VREF on speaker pins on imac91 */ -static void alc889_fixup_imac91_vref(struct hda_codec *codec, - const struct alc_fixup *fix, int action) +static void alc889_fixup_mac_pins(struct hda_codec *codec, + const hda_nid_t *nids, int num_nids) { struct alc_spec *spec = codec->spec; - static hda_nid_t nids[2] = { 0x18, 0x1a }; int i; - if (action != ALC_FIXUP_ACT_INIT) - return; - for (i = 0; i < ARRAY_SIZE(nids); i++) { + for (i = 0; i < num_nids; i++) { unsigned int val; - val = snd_hda_codec_read(codec, nids[i], 0, - AC_VERB_GET_PIN_WIDGET_CONTROL, 0); + 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->keep_vref_in_automute = 1; + 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 through DAC 0x05 + * 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 alc_fixup *fix, int action) + const struct hda_fixup *fix, int action) { struct alc_spec *spec = codec->spec; - if (action == ALC_FIXUP_ACT_PRE_PROBE) - spec->no_primary_hp = 1; + if (action == HDA_FIXUP_ACT_PRE_PROBE) { + spec->gen.no_primary_hp = 1; + spec->gen.no_multi_io = 1; + } } -static const struct alc_fixup alc882_fixups[] = { +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 */ @@ -5179,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_FUNC, + .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 }, @@ -5227,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 }, @@ -5236,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 }, @@ -5245,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 }, @@ -5254,30 +2102,30 @@ static const struct alc_fixup alc882_fixups[] = { } }, [ALC882_FIXUP_GPIO1] = { - .type = ALC_FIXUP_VERBS, + .type = HDA_FIXUP_VERBS, .v.verbs = alc_gpio1_init_verbs, }, [ALC882_FIXUP_GPIO2] = { - .type = ALC_FIXUP_VERBS, + .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 */ { } @@ -5286,8 +2134,8 @@ static const struct alc_fixup alc882_fixups[] = { .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 */ { } @@ -5297,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? */ @@ -5331,33 +2179,64 @@ static const struct alc_fixup alc882_fixups[] = { .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 = ALC_FIXUP_FUNC, + .type = HDA_FIXUP_FUNC, .v.func = alc889_fixup_mbp_vref, .chained = true, .chain_id = ALC882_FIXUP_GPIO1, }, [ALC889_FIXUP_IMAC91_VREF] = { - .type = ALC_FIXUP_FUNC, + .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 = ALC_FIXUP_FUNC, + .type = HDA_FIXUP_FUNC, .v.func = alc_fixup_inv_dmic_0x12, }, [ALC882_FIXUP_NO_PRIMARY_HP] = { - .type = ALC_FIXUP_FUNC, + .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[] = { @@ -5391,21 +2270,23 @@ static const struct snd_pci_quirk alc882_fixup_tbl[] = { SND_PCI_QUIRK(0x1043, 0x1873, "ASUS W90V", ALC882_FIXUP_ASUS_W90V), SND_PCI_QUIRK(0x1043, 0x1971, "Asus W2JC", ALC882_FIXUP_ASUS_W2JC), SND_PCI_QUIRK(0x1043, 0x835f, "Asus Eee 1601", ALC888_FIXUP_EEE1601), + SND_PCI_QUIRK(0x1043, 0x84bc, "ASUS ET2700", ALC887_FIXUP_ASUS_BASS), SND_PCI_QUIRK(0x104d, 0x9047, "Sony Vaio TT", ALC889_FIXUP_VAIO_TT), SND_PCI_QUIRK(0x104d, 0x905a, "Sony Vaio Z", ALC882_FIXUP_NO_PRIMARY_HP), + SND_PCI_QUIRK(0x104d, 0x9043, "Sony Vaio VGC-LN51JGB", ALC882_FIXUP_NO_PRIMARY_HP), /* All Apple entries are in codec SSIDs */ SND_PCI_QUIRK(0x106b, 0x00a0, "MacBookPro 3,1", ALC889_FIXUP_MBP_VREF), SND_PCI_QUIRK(0x106b, 0x00a1, "Macbook", ALC889_FIXUP_MBP_VREF), SND_PCI_QUIRK(0x106b, 0x00a4, "MacbookPro 4,1", ALC889_FIXUP_MBP_VREF), - SND_PCI_QUIRK(0x106b, 0x0c00, "Mac Pro", ALC885_FIXUP_MACPRO_GPIO), + 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_MBP_VREF), - SND_PCI_QUIRK(0x106b, 0x3500, "MacBookAir 2,1", ALC889_FIXUP_MBP_VREF), + 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), @@ -5421,7 +2302,7 @@ static const struct snd_pci_quirk alc882_fixup_tbl[] = { 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), @@ -5430,7 +2311,7 @@ static const struct snd_pci_quirk alc882_fixup_tbl[] = { {} }; -static const struct alc_model_fixup alc882_fixup_models[] = { +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"}, @@ -5473,27 +2354,26 @@ static int patch_alc882(struct hda_codec *codec) break; } - alc_pick_fixup(codec, alc882_fixup_models, alc882_fixup_tbl, + snd_hda_pick_fixup(codec, alc882_fixup_models, alc882_fixup_tbl, alc882_fixups); - alc_apply_fixup(codec, ALC_FIXUP_ACT_PRE_PROBE); + 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 = 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); - } codec->patch_ops = alc_patch_ops; - alc_apply_fixup(codec, ALC_FIXUP_ACT_PROBE); + snd_hda_apply_fixup(codec, HDA_FIXUP_ACT_PROBE); return 0; @@ -5518,49 +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 }, @@ -5568,7 +2459,7 @@ 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 }, @@ -5576,24 +2467,29 @@ static const struct alc_fixup alc262_fixups[] = { } }, [ALC262_FIXUP_INV_DMIC] = { - .type = ALC_FIXUP_FUNC, + .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), {} }; -static const struct alc_model_fixup alc262_fixup_models[] = { +static const struct hda_model_fixup alc262_fixup_models[] = { {.id = ALC262_FIXUP_INV_DMIC, .name = "inv-dmic"}, {} }; @@ -5610,6 +2506,7 @@ static int patch_alc262(struct hda_codec *codec) return err; 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 @@ -5625,28 +2522,27 @@ static int patch_alc262(struct hda_codec *codec) #endif alc_fix_pll_init(codec, 0x20, 0x0a, 10); - alc_pick_fixup(codec, alc262_fixup_models, alc262_fixup_tbl, + snd_hda_pick_fixup(codec, alc262_fixup_models, alc262_fixup_tbl, alc262_fixups); - alc_apply_fixup(codec, ALC_FIXUP_ACT_PRE_PROBE); + 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 && 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); - } codec->patch_ops = alc_patch_ops; spec->shutup = alc_eapd_shutup; - alc_apply_fixup(codec, ALC_FIXUP_ACT_PROBE); + snd_hda_apply_fixup(codec, HDA_FIXUP_ACT_PROBE); return 0; @@ -5685,29 +2581,39 @@ static const struct hda_verb alc268_beep_init_verbs[] = { enum { ALC268_FIXUP_INV_DMIC, ALC268_FIXUP_HP_EAPD, + ALC268_FIXUP_SPDIF, }; -static const struct alc_fixup alc268_fixups[] = { +static const struct hda_fixup alc268_fixups[] = { [ALC268_FIXUP_INV_DMIC] = { - .type = ALC_FIXUP_FUNC, + .type = HDA_FIXUP_FUNC, .v.func = alc_fixup_inv_dmic_0x12, }, [ALC268_FIXUP_HP_EAPD] = { - .type = ALC_FIXUP_VERBS, + .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 alc_model_fixup alc268_fixup_models[] = { +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 */ @@ -5721,15 +2627,7 @@ static const struct snd_pci_quirk alc268_fixup_tbl[] = { 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); - snd_hda_gen_add_verbs(&spec->gen, alc268_beep_init_verbs); - } - } - return err; + return alc_parse_auto_config(codec, NULL, alc268_ssids); } /* @@ -5737,7 +2635,7 @@ 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; /* ALC268 has no aa-loopback mixer */ err = alc_alloc_spec(codec, 0); @@ -5745,27 +2643,20 @@ static int patch_alc268(struct hda_codec *codec) return err; spec = codec->spec; + spec->gen.beep_nid = 0x01; - alc_pick_fixup(codec, alc268_fixup_models, alc268_fixup_tbl, alc268_fixups); - alc_apply_fixup(codec, ALC_FIXUP_ACT_PRE_PROBE); + 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, @@ -5778,7 +2669,7 @@ static int patch_alc268(struct hda_codec *codec) codec->patch_ops = alc_patch_ops; spec->shutup = alc_eapd_shutup; - alc_apply_fixup(codec, ALC_FIXUP_ACT_PROBE); + snd_hda_apply_fixup(codec, HDA_FIXUP_ACT_PROBE); return 0; @@ -5790,6 +2681,35 @@ static int patch_alc268(struct hda_codec *codec) /* * ALC269 */ + +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, .channels_min = 2, @@ -5797,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 }, }; @@ -5819,7 +2739,11 @@ enum { ALC269_TYPE_ALC269VD, ALC269_TYPE_ALC280, ALC269_TYPE_ALC282, + ALC269_TYPE_ALC283, ALC269_TYPE_ALC284, + ALC269_TYPE_ALC285, + ALC269_TYPE_ALC286, + ALC269_TYPE_ALC255, }; /* @@ -5838,11 +2762,15 @@ static int alc269_parse_auto_config(struct hda_codec *codec) 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: @@ -5867,18 +2795,429 @@ static void alc269_shutup(struct hda_codec *codec) { struct alc_spec *spec = codec->spec; - if (spec->codec_variant != ALC269_TYPE_ALC269VB) - return; - 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) { struct alc_spec *spec = codec->spec; @@ -5901,33 +3240,46 @@ static int alc269_resume(struct hda_codec *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); + 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 alc_fixup *fix, int action) + const struct hda_fixup *fix, int action) { struct alc_spec *spec = codec->spec; - if (action == ALC_FIXUP_ACT_PRE_PROBE) + 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}, @@ -5936,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) @@ -5944,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. @@ -5976,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); @@ -5990,72 +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 != ALC_FIXUP_ACT_PROBE) + if (action != HDA_FIXUP_ACT_PROBE) return; - spec->automute_hook = alc269_quanta_automute; + 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 mic1 VREF pin */ -static void alc269_fixup_mic1_mute_hook(void *private_data, int enabled) + +/* 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; - unsigned int pinval = AC_PINCTL_IN_EN + (enabled ? - AC_PINCTL_VREF_HIZ : AC_PINCTL_VREF_80); - snd_hda_set_pin_ctl_cache(codec, 0x18, pinval); + 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); } -static void alc269_fixup_mic1_mute(struct hda_codec *codec, - const struct alc_fixup *fix, int action) +/* 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; - switch (action) { - case ALC_FIXUP_ACT_BUILD: - spec->vmaster_mute.hook = alc269_fixup_mic1_mute_hook; - snd_hda_add_vmaster_hook(codec, &spec->vmaster_mute, true); - /* fallthru */ - case ALC_FIXUP_ACT_INIT: - snd_hda_sync_vmaster_hook(&spec->vmaster_mute); + + 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; + + 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; } } -/* update mute-LED according to the speaker mute state via mic2 VREF pin */ -static void alc269_fixup_mic2_mute_hook(void *private_data, int enabled) +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; - unsigned int pinval = enabled ? 0x20 : 0x24; - snd_hda_set_pin_ctl_cache(codec, 0x19, pinval); + 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_mic2_mute(struct hda_codec *codec, - const struct alc_fixup *fix, int action) +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 ALC_FIXUP_ACT_BUILD: - spec->vmaster_mute.hook = alc269_fixup_mic2_mute_hook; - snd_hda_add_vmaster_hook(codec, &spec->vmaster_mute, true); - /* fallthru */ - case ALC_FIXUP_ACT_INIT: - snd_hda_sync_vmaster_hook(&spec->vmaster_mute); + 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 alc_fixup *fix, + const struct hda_fixup *fix, int action) { struct alc_spec *spec = codec->spec; - if (action == ALC_FIXUP_ACT_PROBE) - snd_hda_jack_set_gating_jack(codec, spec->ext_mic_pin, - spec->autocfg.hp_pins[0]); + 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, @@ -6064,34 +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_MIC1_MUTE_LED, - ALC269_FIXUP_MIC2_MUTE_LED, + 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}, @@ -6102,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}, @@ -6111,50 +4323,60 @@ static const struct alc_fixup alc269_fixups[] = { } }, [ALC269_FIXUP_SKU_IGNORE] = { - .type = ALC_FIXUP_FUNC, + .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 */ { } @@ -6162,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 */ @@ -6173,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 */ @@ -6183,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 */ @@ -6193,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 */ @@ -6202,21 +4431,33 @@ static const struct alc_fixup alc269_fixups[] = { { } }, }, - [ALC269_FIXUP_MIC1_MUTE_LED] = { - .type = ALC_FIXUP_FUNC, - .v.func = alc269_fixup_mic1_mute, + [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_MIC2_MUTE_LED] = { - .type = ALC_FIXUP_FUNC, - .v.func = alc269_fixup_mic2_mute, + [ALC269_FIXUP_HP_GPIO_LED] = { + .type = HDA_FIXUP_FUNC, + .v.func = alc269_fixup_hp_gpio_led, }, [ALC269_FIXUP_INV_DMIC] = { - .type = ALC_FIXUP_FUNC, + .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 = ALC_FIXUP_PINS, - .v.pins = (const struct alc_pincfg[]) { + .type = HDA_FIXUP_PINS, + .v.pins = (const struct hda_pintbl[]) { { 0x19, 0x23a11040 }, /* dock mic */ { 0x1b, 0x2121103f }, /* dock headphone */ { } @@ -6225,12 +4466,85 @@ static const struct alc_fixup alc269_fixups[] = { .chain_id = ALC269_FIXUP_PINCFG_NO_HP_TO_LINEOUT }, [ALC269_FIXUP_PINCFG_NO_HP_TO_LINEOUT] = { - .type = ALC_FIXUP_FUNC, + .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 = ALC_FIXUP_PINS, - .v.pins = (const struct alc_pincfg[]) { + .type = HDA_FIXUP_PINS, + .v.pins = (const struct hda_pintbl[]) { { 0x14, 0x99130110 }, /* speaker */ { 0x19, 0x01a19c20 }, /* mic */ { 0x1b, 0x99a7012f }, /* int-mic */ @@ -6239,37 +4553,320 @@ static const struct alc_fixup alc269_fixups[] = { }, }, [ALC271_FIXUP_HP_GATE_MIC_JACK] = { - .type = ALC_FIXUP_FUNC, + .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(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(0x103c, 0x1586, "HP", ALC269_FIXUP_MIC2_MUTE_LED), - SND_PCI_QUIRK(0x103c, 0x1972, "HP Pavilion 17", ALC269_FIXUP_MIC1_MUTE_LED), - SND_PCI_QUIRK(0x103c, 0x1977, "HP Pavilion 14", ALC269_FIXUP_MIC1_MUTE_LED), - SND_PCI_QUIRK(0x1043, 0x1427, "Asus Zenbook UX31E", ALC269VB_FIXUP_DMIC), - SND_PCI_QUIRK(0x1043, 0x1517, "Asus Zenbook UX31A", ALC269VB_FIXUP_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, 0x16e3, "ASUS UX50", ALC269_FIXUP_STEREO_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(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(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), @@ -6280,8 +4877,22 @@ static const struct snd_pci_quirk alc269_fixup_tbl[] = { 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 0 /* Below is a quirk table taken from the old code. @@ -6334,16 +4945,161 @@ 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 void alc269_fill_coef(struct hda_codec *codec) { @@ -6401,25 +5157,32 @@ static int patch_alc269(struct hda_codec *codec) return err; spec = codec->spec; + spec->gen.shared_mic_vref_pin = 0x18; - alc_pick_fixup(codec, alc269_fixup_models, + snd_hda_pick_fixup(codec, alc269_fixup_models, alc269_fixup_tbl, alc269_fixups); - alc_apply_fixup(codec, ALC_FIXUP_ACT_PRE_PROBE); + snd_hda_pick_pin_fixup(codec, alc269_pin_fixup_tbl, alc269_fixups); + snd_hda_apply_fixup(codec, HDA_FIXUP_ACT_PRE_PROBE); alc_auto_parse_customize_define(codec); + if (has_cdefine_beep(codec)) + spec->gen.beep_nid = 0x01; + switch (codec->vendor_id) { case 0x10ec0269: spec->codec_variant = ALC269_TYPE_ALC269VA; switch (alc_get_coef0(codec) & 0x00f0) { case 0x0010: - if (codec->bus->pci->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; @@ -6441,13 +5204,36 @@ static int patch_alc269(struct hda_codec *codec) spec->codec_variant = ALC269_TYPE_ALC280; break; case 0x10ec0282: - case 0x10ec0283: spec->codec_variant = ALC269_TYPE_ALC282; + spec->shutup = alc282_shutup; + spec->init_hook = alc282_init; + break; + case 0x10ec0233: + case 0x10ec0283: + spec->codec_variant = ALC269_TYPE_ALC283; + spec->shutup = alc283_shutup; + spec->init_hook = alc283_init; break; case 0x10ec0284: case 0x10ec0292: spec->codec_variant = ALC269_TYPE_ALC284; break; + case 0x10ec0285: + case 0x10ec0293: + spec->codec_variant = ALC269_TYPE_ALC285; + break; + case 0x10ec0286: + case 0x10ec0288: + spec->codec_variant = ALC269_TYPE_ALC286; + break; + case 0x10ec0255: + spec->codec_variant = ALC269_TYPE_ALC255; + break; + } + + if (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 */ @@ -6455,20 +5241,18 @@ static int patch_alc269(struct hda_codec *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, 0x04, HDA_INPUT); - } codec->patch_ops = alc_patch_ops; #ifdef CONFIG_PM + codec->patch_ops.suspend = alc269_suspend; codec->patch_ops.resume = alc269_resume; #endif - spec->shutup = alc269_shutup; + if (!spec->shutup) + spec->shutup = alc269_shutup; - alc_apply_fixup(codec, ALC_FIXUP_ACT_PROBE); + snd_hda_apply_fixup(codec, HDA_FIXUP_ACT_PROBE); return 0; @@ -6494,60 +5278,72 @@ enum { 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_set_pin_ctl(codec, 0x0f, val); - spec->keep_vref_in_automute = 1; + spec->gen.keep_vref_in_automute = 1; } /* suppress the jack-detection */ static void alc_fixup_no_jack_detect(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) codec->no_jack_detect = 1; } -static const struct alc_fixup alc861_fixups[] = { +static const struct hda_fixup alc861_fixups[] = { [ALC861_FIXUP_FSC_AMILO_PI1505] = { - .type = ALC_FIXUP_PINS, - .v.pins = (const struct alc_pincfg[]) { + .type = HDA_FIXUP_PINS, + .v.pins = (const struct hda_pintbl[]) { { 0x0b, 0x0221101f }, /* HP */ { 0x0f, 0x90170310 }, /* speaker */ { } } }, [ALC861_FIXUP_AMP_VREF_0F] = { - .type = ALC_FIXUP_FUNC, + .type = HDA_FIXUP_FUNC, .v.func = alc861_fixup_asus_amp_vref_0f, }, [ALC861_FIXUP_NO_JACK_DETECT] = { - .type = ALC_FIXUP_FUNC, + .type = HDA_FIXUP_FUNC, .v.func = alc_fixup_no_jack_detect, }, [ALC861_FIXUP_ASUS_A6RP] = { - .type = ALC_FIXUP_FUNC, + .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(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), @@ -6569,28 +5365,25 @@ static int patch_alc861(struct hda_codec *codec) return err; 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) { - 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); - } codec->patch_ops = alc_patch_ops; #ifdef CONFIG_PM spec->power_hook = alc_power_eapd; #endif - alc_apply_fixup(codec, ALC_FIXUP_ACT_PROBE); + snd_hda_apply_fixup(codec, HDA_FIXUP_ACT_PROBE); return 0; @@ -6620,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) { + 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}, @@ -6640,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, }, }; @@ -6664,27 +5457,24 @@ static int patch_alc861vd(struct hda_codec *codec) return err; 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 (!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); - } codec->patch_ops = alc_patch_ops; spec->shutup = alc_eapd_shutup; - alc_apply_fixup(codec, ALC_FIXUP_ACT_PROBE); + snd_hda_apply_fixup(codec, HDA_FIXUP_ACT_PROBE); return 0; @@ -6717,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; @@ -6725,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, @@ -6756,41 +5611,56 @@ enum { 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_FUNC, + .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 */ { } }, @@ -6798,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 */ @@ -6810,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 */ @@ -6822,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 */ @@ -6835,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 */ @@ -6848,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 */ @@ -6861,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 */ @@ -6874,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 */ @@ -6888,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 */ @@ -6902,29 +5772,103 @@ static const struct alc_fixup alc662_fixups[] = { .chain_id = ALC662_FIXUP_SKU_IGNORE }, [ALC662_FIXUP_NO_JACK_DETECT] = { - .type = ALC_FIXUP_FUNC, + .type = HDA_FIXUP_FUNC, .v.func = alc_fixup_no_jack_detect, }, [ALC662_FIXUP_ZOTAC_Z68] = { - .type = ALC_FIXUP_PINS, - .v.pins = (const struct alc_pincfg[]) { + .type = HDA_FIXUP_PINS, + .v.pins = (const struct hda_pintbl[]) { { 0x1b, 0x02214020 }, /* Front HP */ { } } }, [ALC662_FIXUP_INV_DMIC] = { - .type = ALC_FIXUP_FUNC, + .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), @@ -6993,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"}, @@ -7004,6 +5948,71 @@ static const struct alc_model_fixup alc662_fixup_models[] = { {.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}), {} }; @@ -7054,16 +6063,21 @@ static int patch_alc662(struct hda_codec *codec) spec->init_hook = alc662_fill_coef; alc662_fill_coef(codec); - alc_pick_fixup(codec, alc662_fixup_models, + snd_hda_pick_fixup(codec, alc662_fixup_models, alc662_fixup_tbl, alc662_fixups); - alc_apply_fixup(codec, ALC_FIXUP_ACT_PRE_PROBE); + 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; } @@ -7072,10 +6086,7 @@ static int patch_alc662(struct hda_codec *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) { switch (codec->vendor_id) { case 0x10ec0662: set_beep_amp(spec, 0x0b, 0x05, HDA_INPUT); @@ -7083,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: @@ -7094,7 +6106,7 @@ static int patch_alc662(struct hda_codec *codec) codec->patch_ops = alc_patch_ops; spec->shutup = alc_eapd_shutup; - alc_apply_fixup(codec, ALC_FIXUP_ACT_PROBE); + snd_hda_apply_fixup(codec, HDA_FIXUP_ACT_PROBE); return 0; @@ -7140,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 }, @@ -7153,8 +6169,12 @@ static const struct hda_codec_preset snd_hda_preset_realtek[] = { { .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 }, @@ -7168,9 +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 }, 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 a86547ca17c..3744ea4e843 100644 --- a/sound/pci/hda/patch_sigmatel.c +++ b/sound/pci/hda/patch_sigmatel.c @@ -31,7 +31,6 @@ #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" @@ -39,18 +38,14 @@ #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, @@ -66,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, @@ -80,7 +75,6 @@ enum { }; enum { - STAC_92HD73XX_AUTO, STAC_92HD73XX_NO_JD, /* no jack-detection */ STAC_92HD73XX_REF, STAC_92HD73XX_INTEL, @@ -89,11 +83,11 @@ 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, @@ -104,12 +98,15 @@ enum { 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, @@ -118,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, @@ -136,7 +140,6 @@ enum { }; enum { - STAC_922X_AUTO, STAC_D945_REF, STAC_D945GTP3, STAC_D945GTP5, @@ -145,67 +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; @@ -217,15 +199,17 @@ 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 */ - bool mic_mute_led_on; /* current mic mute state */ + 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; @@ -233,647 +217,820 @@ struct sigmatel_spec { 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; + unsigned int active_adcs; - const char * const *spdif_labels; - - 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]; - - struct hda_vmaster_mute_hook vmaster_mute; + /* SPDIF-out mux */ + const char * const *spdif_labels; + struct hda_input_mux spdif_mux; + unsigned int cur_smux[2]; }; #define AC_VERB_IDT_SET_POWER_MAP 0x7ec #define AC_VERB_IDT_GET_POWER_MAP 0xfec -static const hda_nid_t stac9200_adc_nids[1] = { - 0x03, -}; - -static const hda_nid_t stac9200_mux_nids[1] = { - 0x0c, -}; - -static const hda_nid_t stac9200_dac_nids[1] = { - 0x02, -}; - 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; + + 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); + } + } +} - return snd_hda_input_mux_put(codec, spec->dinput_mux, ucontrol, - spec->dmux_nids[dmux_idx], &spec->cur_dmux[dmux_idx]); +/* + */ + +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; } -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_set_pin_ctl_cache(codec, nid, 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; } -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_set_pin_ctl_cache(codec, nid, 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); + + /* 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); + } - ucontrol->value.enumerated.item[0] = spec->cur_mux[adc_idx]; + /* 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 + +/* + * SPDIF-out mux controls + */ -static int stac92xx_mono_mux_enum_put(struct snd_kcontrol *kcontrol, - struct snd_ctl_elem_value *ucontrol) +static int stac_smux_enum_info(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_info *uinfo) { struct hda_codec *codec = snd_kcontrol_chip(kcontrol); 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}, @@ -936,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}, {} }; @@ -962,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}, @@ -978,261 +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("PCM Playback Volume", 0xb, 0, HDA_OUTPUT), - HDA_CODEC_MUTE("PCM 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("PCM Playback Volume", 0xe, 0, HDA_OUTPUT), - HDA_CODEC_MUTE("PCM 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_pfxs[] = { - "Front", "Surround", "Center", "LFE", "Side", - "Headphone", "Speaker", "Bass Speaker", "IEC958", "PCM", - NULL -}; - -static void stac92xx_update_led_status(struct hda_codec *codec, int enabled); - -static void stac92xx_vmaster_hook(void *private_data, int val) -{ - stac92xx_update_led_status(private_data, val); -} - -static void stac92xx_free_kctls(struct hda_codec *codec); - -static int stac92xx_build_controls(struct hda_codec *codec) -{ - struct sigmatel_spec *spec = codec->spec; - unsigned int vmaster_tlv[4]; - 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_dig_out_ctls(codec, - spec->multiout.dig_out_nid, - spec->multiout.dig_out_nid, - spec->autocfg.dig_out_type[0]); - 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 */ - 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_pfxs, - "Playback Volume"); - if (err < 0) - return err; - - 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->gpio_led) { - spec->vmaster_mute.hook = stac92xx_vmaster_hook; - err = snd_hda_add_vmaster_hook(codec, &spec->vmaster_mute, true); - 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 @@ -1241,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 }, + {} }; @@ -1252,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 }, + {} }; /* @@ -1265,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 }, + {} }; /* @@ -1275,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 }, + {} }; /* @@ -1287,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 }, + {} }; /* @@ -1298,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 }, + {} }; /* @@ -1308,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), @@ -1441,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), @@ -1518,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), @@ -1616,81 +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 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_s14_pin_configs[10] = { - 0x0221403f, 0x0221101f, 0x02a19020, 0x90170110, - 0x40f000f0, 0x40f000f0, 0x40f000f0, 0x90a60160, - 0x40f000f0, 0x40f000f0, +static const struct hda_pintbl dell_vostro_3500_pin_configs[] = { + { 0x0a, 0x02a11020 }, + { 0x0b, 0x0221101f }, + { 0x0c, 0x400000f0 }, + { 0x0d, 0x90170110 }, + { 0x0e, 0x400000f1 }, + { 0x0f, 0x400000f2 }, + { 0x10, 0x400000f3 }, + { 0x11, 0x90a60160 }, + { 0x1f, 0x400000f4 }, + { 0x20, 0x400000f5 }, + {} }; -static const unsigned int dell_vostro_3500_pin_configs[10] = { - 0x02a11020, 0x0221101f, 0x400000f0, 0x90170110, - 0x400000f1, 0x400000f2, 0x400000f3, 0x90a60160, - 0x400000f4, 0x400000f5, +static const struct hda_pintbl hp_dv7_4000_pin_configs[] = { + { 0x0a, 0x03a12050 }, + { 0x0b, 0x0321201f }, + { 0x0c, 0x40f000f0 }, + { 0x0d, 0x90170110 }, + { 0x0e, 0x40f000f0 }, + { 0x0f, 0x40f000f0 }, + { 0x10, 0x90170110 }, + { 0x11, 0xd5a30140 }, + { 0x1f, 0x40f000f0 }, + { 0x20, 0x40f000f0 }, + {} }; -static const unsigned int hp_dv7_4000_pin_configs[10] = { - 0x03a12050, 0x0321201f, 0x40f000f0, 0x90170110, - 0x40f000f0, 0x40f000f0, 0x90170110, 0xd5a30140, - 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 hp_zephyr_pin_configs[10] = { - 0x01813050, 0x0421201f, 0x04a1205e, 0x96130310, - 0x96130310, 0x0101401f, 0x1111611f, 0xd5a30130, - 0, 0, +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 unsigned int hp_cNB11_intquad_pin_configs[10] = { - 0x40f000f0, 0x0221101f, 0x02a11020, 0x92170110, - 0x40f000f0, 0x92170110, 0x40f000f0, 0xd5a30130, - 0x40f000f0, 0x40f000f0, +static void stac92hd83xxx_fixup_hp(struct hda_codec *codec, + const struct hda_fixup *fix, int action) +{ + struct sigmatel_spec *spec = codec->spec; + + if (action != HDA_FIXUP_ACT_PRE_PROBE) + return; + + if (hp_bnb2011_with_dock(codec)) { + snd_hda_codec_set_pincfg(codec, 0xa, 0x2101201f); + snd_hda_codec_set_pincfg(codec, 0xf, 0x2181205e); + } + + if (find_mute_led_cfg(codec, spec->default_polarity)) + codec_dbg(codec, "mute LED gpio %d polarity %d\n", + spec->gpio_led, + spec->gpio_led_polarity); + + /* allow auto-switching of dock line-in */ + spec->gen.line_in_auto_switch = true; +} + +static void stac92hd83xxx_fixup_hp_zephyr(struct hda_codec *codec, + const struct hda_fixup *fix, int action) +{ + if (action != HDA_FIXUP_ACT_PRE_PROBE) + return; + + snd_hda_apply_pincfgs(codec, hp_zephyr_pin_configs); + snd_hda_add_verbs(codec, stac92hd83xxx_hp_zephyr_init); +} + +static void stac92hd83xxx_fixup_hp_led(struct hda_codec *codec, + const struct hda_fixup *fix, int action) +{ + struct sigmatel_spec *spec = codec->spec; + + if (action == HDA_FIXUP_ACT_PRE_PROBE) + spec->default_polarity = 0; +} + +static void stac92hd83xxx_fixup_hp_inv_led(struct hda_codec *codec, + const struct hda_fixup *fix, int action) +{ + struct sigmatel_spec *spec = codec->spec; + + if (action == HDA_FIXUP_ACT_PRE_PROBE) + spec->default_polarity = 1; +} + +static void stac92hd83xxx_fixup_hp_mic_led(struct hda_codec *codec, + const struct hda_fixup *fix, int action) +{ + struct sigmatel_spec *spec = codec->spec; + + if (action == HDA_FIXUP_ACT_PRE_PROBE) { + spec->mic_mute_led_gpio = 0x08; /* GPIO3 */ + /* resetting controller clears GPIO, so we need to keep on */ + codec->bus->power_keep_link_on = 1; + } +} + +static void stac92hd83xxx_fixup_hp_led_gpio10(struct hda_codec *codec, + const struct hda_fixup *fix, int action) +{ + struct sigmatel_spec *spec = codec->spec; + + if (action == HDA_FIXUP_ACT_PRE_PROBE) { + spec->gpio_led = 0x10; /* GPIO4 */ + spec->default_polarity = 0; + } +} + +static void stac92hd83xxx_fixup_headset_jack(struct hda_codec *codec, + const struct hda_fixup *fix, int action) +{ + struct sigmatel_spec *spec = codec->spec; + + if (action == HDA_FIXUP_ACT_PRE_PROBE) + spec->headset_jack = 1; +} + +static const struct hda_verb hp_bnb13_eq_verbs[] = { + /* 44.1KHz base */ + { 0x22, 0x7A6, 0x3E }, + { 0x22, 0x7A7, 0x68 }, + { 0x22, 0x7A8, 0x17 }, + { 0x22, 0x7A9, 0x3E }, + { 0x22, 0x7AA, 0x68 }, + { 0x22, 0x7AB, 0x17 }, + { 0x22, 0x7AC, 0x00 }, + { 0x22, 0x7AD, 0x80 }, + { 0x22, 0x7A6, 0x83 }, + { 0x22, 0x7A7, 0x2F }, + { 0x22, 0x7A8, 0xD1 }, + { 0x22, 0x7A9, 0x83 }, + { 0x22, 0x7AA, 0x2F }, + { 0x22, 0x7AB, 0xD1 }, + { 0x22, 0x7AC, 0x01 }, + { 0x22, 0x7AD, 0x80 }, + { 0x22, 0x7A6, 0x3E }, + { 0x22, 0x7A7, 0x68 }, + { 0x22, 0x7A8, 0x17 }, + { 0x22, 0x7A9, 0x3E }, + { 0x22, 0x7AA, 0x68 }, + { 0x22, 0x7AB, 0x17 }, + { 0x22, 0x7AC, 0x02 }, + { 0x22, 0x7AD, 0x80 }, + { 0x22, 0x7A6, 0x7C }, + { 0x22, 0x7A7, 0xC6 }, + { 0x22, 0x7A8, 0x0C }, + { 0x22, 0x7A9, 0x7C }, + { 0x22, 0x7AA, 0xC6 }, + { 0x22, 0x7AB, 0x0C }, + { 0x22, 0x7AC, 0x03 }, + { 0x22, 0x7AD, 0x80 }, + { 0x22, 0x7A6, 0xC3 }, + { 0x22, 0x7A7, 0x25 }, + { 0x22, 0x7A8, 0xAF }, + { 0x22, 0x7A9, 0xC3 }, + { 0x22, 0x7AA, 0x25 }, + { 0x22, 0x7AB, 0xAF }, + { 0x22, 0x7AC, 0x04 }, + { 0x22, 0x7AD, 0x80 }, + { 0x22, 0x7A6, 0x3E }, + { 0x22, 0x7A7, 0x85 }, + { 0x22, 0x7A8, 0x73 }, + { 0x22, 0x7A9, 0x3E }, + { 0x22, 0x7AA, 0x85 }, + { 0x22, 0x7AB, 0x73 }, + { 0x22, 0x7AC, 0x05 }, + { 0x22, 0x7AD, 0x80 }, + { 0x22, 0x7A6, 0x85 }, + { 0x22, 0x7A7, 0x39 }, + { 0x22, 0x7A8, 0xC7 }, + { 0x22, 0x7A9, 0x85 }, + { 0x22, 0x7AA, 0x39 }, + { 0x22, 0x7AB, 0xC7 }, + { 0x22, 0x7AC, 0x06 }, + { 0x22, 0x7AD, 0x80 }, + { 0x22, 0x7A6, 0x3C }, + { 0x22, 0x7A7, 0x90 }, + { 0x22, 0x7A8, 0xB0 }, + { 0x22, 0x7A9, 0x3C }, + { 0x22, 0x7AA, 0x90 }, + { 0x22, 0x7AB, 0xB0 }, + { 0x22, 0x7AC, 0x07 }, + { 0x22, 0x7AD, 0x80 }, + { 0x22, 0x7A6, 0x7A }, + { 0x22, 0x7A7, 0xC6 }, + { 0x22, 0x7A8, 0x39 }, + { 0x22, 0x7A9, 0x7A }, + { 0x22, 0x7AA, 0xC6 }, + { 0x22, 0x7AB, 0x39 }, + { 0x22, 0x7AC, 0x08 }, + { 0x22, 0x7AD, 0x80 }, + { 0x22, 0x7A6, 0xC4 }, + { 0x22, 0x7A7, 0xE9 }, + { 0x22, 0x7A8, 0xDC }, + { 0x22, 0x7A9, 0xC4 }, + { 0x22, 0x7AA, 0xE9 }, + { 0x22, 0x7AB, 0xDC }, + { 0x22, 0x7AC, 0x09 }, + { 0x22, 0x7AD, 0x80 }, + { 0x22, 0x7A6, 0x3D }, + { 0x22, 0x7A7, 0xE1 }, + { 0x22, 0x7A8, 0x0D }, + { 0x22, 0x7A9, 0x3D }, + { 0x22, 0x7AA, 0xE1 }, + { 0x22, 0x7AB, 0x0D }, + { 0x22, 0x7AC, 0x0A }, + { 0x22, 0x7AD, 0x80 }, + { 0x22, 0x7A6, 0x89 }, + { 0x22, 0x7A7, 0xB6 }, + { 0x22, 0x7A8, 0xEB }, + { 0x22, 0x7A9, 0x89 }, + { 0x22, 0x7AA, 0xB6 }, + { 0x22, 0x7AB, 0xEB }, + { 0x22, 0x7AC, 0x0B }, + { 0x22, 0x7AD, 0x80 }, + { 0x22, 0x7A6, 0x39 }, + { 0x22, 0x7A7, 0x9D }, + { 0x22, 0x7A8, 0xFE }, + { 0x22, 0x7A9, 0x39 }, + { 0x22, 0x7AA, 0x9D }, + { 0x22, 0x7AB, 0xFE }, + { 0x22, 0x7AC, 0x0C }, + { 0x22, 0x7AD, 0x80 }, + { 0x22, 0x7A6, 0x76 }, + { 0x22, 0x7A7, 0x49 }, + { 0x22, 0x7A8, 0x15 }, + { 0x22, 0x7A9, 0x76 }, + { 0x22, 0x7AA, 0x49 }, + { 0x22, 0x7AB, 0x15 }, + { 0x22, 0x7AC, 0x0D }, + { 0x22, 0x7AD, 0x80 }, + { 0x22, 0x7A6, 0xC8 }, + { 0x22, 0x7A7, 0x80 }, + { 0x22, 0x7A8, 0xF5 }, + { 0x22, 0x7A9, 0xC8 }, + { 0x22, 0x7AA, 0x80 }, + { 0x22, 0x7AB, 0xF5 }, + { 0x22, 0x7AC, 0x0E }, + { 0x22, 0x7AD, 0x80 }, + { 0x22, 0x7A6, 0x40 }, + { 0x22, 0x7A7, 0x00 }, + { 0x22, 0x7A8, 0x00 }, + { 0x22, 0x7A9, 0x40 }, + { 0x22, 0x7AA, 0x00 }, + { 0x22, 0x7AB, 0x00 }, + { 0x22, 0x7AC, 0x0F }, + { 0x22, 0x7AD, 0x80 }, + { 0x22, 0x7A6, 0x90 }, + { 0x22, 0x7A7, 0x68 }, + { 0x22, 0x7A8, 0xF1 }, + { 0x22, 0x7A9, 0x90 }, + { 0x22, 0x7AA, 0x68 }, + { 0x22, 0x7AB, 0xF1 }, + { 0x22, 0x7AC, 0x10 }, + { 0x22, 0x7AD, 0x80 }, + { 0x22, 0x7A6, 0x34 }, + { 0x22, 0x7A7, 0x47 }, + { 0x22, 0x7A8, 0x6C }, + { 0x22, 0x7A9, 0x34 }, + { 0x22, 0x7AA, 0x47 }, + { 0x22, 0x7AB, 0x6C }, + { 0x22, 0x7AC, 0x11 }, + { 0x22, 0x7AD, 0x80 }, + { 0x22, 0x7A6, 0x6F }, + { 0x22, 0x7A7, 0x97 }, + { 0x22, 0x7A8, 0x0F }, + { 0x22, 0x7A9, 0x6F }, + { 0x22, 0x7AA, 0x97 }, + { 0x22, 0x7AB, 0x0F }, + { 0x22, 0x7AC, 0x12 }, + { 0x22, 0x7AD, 0x80 }, + { 0x22, 0x7A6, 0xCB }, + { 0x22, 0x7A7, 0xB8 }, + { 0x22, 0x7A8, 0x94 }, + { 0x22, 0x7A9, 0xCB }, + { 0x22, 0x7AA, 0xB8 }, + { 0x22, 0x7AB, 0x94 }, + { 0x22, 0x7AC, 0x13 }, + { 0x22, 0x7AD, 0x80 }, + { 0x22, 0x7A6, 0x40 }, + { 0x22, 0x7A7, 0x00 }, + { 0x22, 0x7A8, 0x00 }, + { 0x22, 0x7A9, 0x40 }, + { 0x22, 0x7AA, 0x00 }, + { 0x22, 0x7AB, 0x00 }, + { 0x22, 0x7AC, 0x14 }, + { 0x22, 0x7AD, 0x80 }, + { 0x22, 0x7A6, 0x95 }, + { 0x22, 0x7A7, 0x76 }, + { 0x22, 0x7A8, 0x5B }, + { 0x22, 0x7A9, 0x95 }, + { 0x22, 0x7AA, 0x76 }, + { 0x22, 0x7AB, 0x5B }, + { 0x22, 0x7AC, 0x15 }, + { 0x22, 0x7AD, 0x80 }, + { 0x22, 0x7A6, 0x31 }, + { 0x22, 0x7A7, 0xAC }, + { 0x22, 0x7A8, 0x31 }, + { 0x22, 0x7A9, 0x31 }, + { 0x22, 0x7AA, 0xAC }, + { 0x22, 0x7AB, 0x31 }, + { 0x22, 0x7AC, 0x16 }, + { 0x22, 0x7AD, 0x80 }, + { 0x22, 0x7A6, 0x6A }, + { 0x22, 0x7A7, 0x89 }, + { 0x22, 0x7A8, 0xA5 }, + { 0x22, 0x7A9, 0x6A }, + { 0x22, 0x7AA, 0x89 }, + { 0x22, 0x7AB, 0xA5 }, + { 0x22, 0x7AC, 0x17 }, + { 0x22, 0x7AD, 0x80 }, + { 0x22, 0x7A6, 0xCE }, + { 0x22, 0x7A7, 0x53 }, + { 0x22, 0x7A8, 0xCF }, + { 0x22, 0x7A9, 0xCE }, + { 0x22, 0x7AA, 0x53 }, + { 0x22, 0x7AB, 0xCF }, + { 0x22, 0x7AC, 0x18 }, + { 0x22, 0x7AD, 0x80 }, + { 0x22, 0x7A6, 0x40 }, + { 0x22, 0x7A7, 0x00 }, + { 0x22, 0x7A8, 0x00 }, + { 0x22, 0x7A9, 0x40 }, + { 0x22, 0x7AA, 0x00 }, + { 0x22, 0x7AB, 0x00 }, + { 0x22, 0x7AC, 0x19 }, + { 0x22, 0x7AD, 0x80 }, + /* 48KHz base */ + { 0x22, 0x7A6, 0x3E }, + { 0x22, 0x7A7, 0x88 }, + { 0x22, 0x7A8, 0xDC }, + { 0x22, 0x7A9, 0x3E }, + { 0x22, 0x7AA, 0x88 }, + { 0x22, 0x7AB, 0xDC }, + { 0x22, 0x7AC, 0x1A }, + { 0x22, 0x7AD, 0x80 }, + { 0x22, 0x7A6, 0x82 }, + { 0x22, 0x7A7, 0xEE }, + { 0x22, 0x7A8, 0x46 }, + { 0x22, 0x7A9, 0x82 }, + { 0x22, 0x7AA, 0xEE }, + { 0x22, 0x7AB, 0x46 }, + { 0x22, 0x7AC, 0x1B }, + { 0x22, 0x7AD, 0x80 }, + { 0x22, 0x7A6, 0x3E }, + { 0x22, 0x7A7, 0x88 }, + { 0x22, 0x7A8, 0xDC }, + { 0x22, 0x7A9, 0x3E }, + { 0x22, 0x7AA, 0x88 }, + { 0x22, 0x7AB, 0xDC }, + { 0x22, 0x7AC, 0x1C }, + { 0x22, 0x7AD, 0x80 }, + { 0x22, 0x7A6, 0x7D }, + { 0x22, 0x7A7, 0x09 }, + { 0x22, 0x7A8, 0x28 }, + { 0x22, 0x7A9, 0x7D }, + { 0x22, 0x7AA, 0x09 }, + { 0x22, 0x7AB, 0x28 }, + { 0x22, 0x7AC, 0x1D }, + { 0x22, 0x7AD, 0x80 }, + { 0x22, 0x7A6, 0xC2 }, + { 0x22, 0x7A7, 0xE5 }, + { 0x22, 0x7A8, 0xB4 }, + { 0x22, 0x7A9, 0xC2 }, + { 0x22, 0x7AA, 0xE5 }, + { 0x22, 0x7AB, 0xB4 }, + { 0x22, 0x7AC, 0x1E }, + { 0x22, 0x7AD, 0x80 }, + { 0x22, 0x7A6, 0x3E }, + { 0x22, 0x7A7, 0xA3 }, + { 0x22, 0x7A8, 0x1F }, + { 0x22, 0x7A9, 0x3E }, + { 0x22, 0x7AA, 0xA3 }, + { 0x22, 0x7AB, 0x1F }, + { 0x22, 0x7AC, 0x1F }, + { 0x22, 0x7AD, 0x80 }, + { 0x22, 0x7A6, 0x84 }, + { 0x22, 0x7A7, 0xCA }, + { 0x22, 0x7A8, 0xF1 }, + { 0x22, 0x7A9, 0x84 }, + { 0x22, 0x7AA, 0xCA }, + { 0x22, 0x7AB, 0xF1 }, + { 0x22, 0x7AC, 0x20 }, + { 0x22, 0x7AD, 0x80 }, + { 0x22, 0x7A6, 0x3C }, + { 0x22, 0x7A7, 0xD5 }, + { 0x22, 0x7A8, 0x9C }, + { 0x22, 0x7A9, 0x3C }, + { 0x22, 0x7AA, 0xD5 }, + { 0x22, 0x7AB, 0x9C }, + { 0x22, 0x7AC, 0x21 }, + { 0x22, 0x7AD, 0x80 }, + { 0x22, 0x7A6, 0x7B }, + { 0x22, 0x7A7, 0x35 }, + { 0x22, 0x7A8, 0x0F }, + { 0x22, 0x7A9, 0x7B }, + { 0x22, 0x7AA, 0x35 }, + { 0x22, 0x7AB, 0x0F }, + { 0x22, 0x7AC, 0x22 }, + { 0x22, 0x7AD, 0x80 }, + { 0x22, 0x7A6, 0xC4 }, + { 0x22, 0x7A7, 0x87 }, + { 0x22, 0x7A8, 0x45 }, + { 0x22, 0x7A9, 0xC4 }, + { 0x22, 0x7AA, 0x87 }, + { 0x22, 0x7AB, 0x45 }, + { 0x22, 0x7AC, 0x23 }, + { 0x22, 0x7AD, 0x80 }, + { 0x22, 0x7A6, 0x3E }, + { 0x22, 0x7A7, 0x0A }, + { 0x22, 0x7A8, 0x78 }, + { 0x22, 0x7A9, 0x3E }, + { 0x22, 0x7AA, 0x0A }, + { 0x22, 0x7AB, 0x78 }, + { 0x22, 0x7AC, 0x24 }, + { 0x22, 0x7AD, 0x80 }, + { 0x22, 0x7A6, 0x88 }, + { 0x22, 0x7A7, 0xE2 }, + { 0x22, 0x7A8, 0x05 }, + { 0x22, 0x7A9, 0x88 }, + { 0x22, 0x7AA, 0xE2 }, + { 0x22, 0x7AB, 0x05 }, + { 0x22, 0x7AC, 0x25 }, + { 0x22, 0x7AD, 0x80 }, + { 0x22, 0x7A6, 0x3A }, + { 0x22, 0x7A7, 0x1A }, + { 0x22, 0x7A8, 0xA3 }, + { 0x22, 0x7A9, 0x3A }, + { 0x22, 0x7AA, 0x1A }, + { 0x22, 0x7AB, 0xA3 }, + { 0x22, 0x7AC, 0x26 }, + { 0x22, 0x7AD, 0x80 }, + { 0x22, 0x7A6, 0x77 }, + { 0x22, 0x7A7, 0x1D }, + { 0x22, 0x7A8, 0xFB }, + { 0x22, 0x7A9, 0x77 }, + { 0x22, 0x7AA, 0x1D }, + { 0x22, 0x7AB, 0xFB }, + { 0x22, 0x7AC, 0x27 }, + { 0x22, 0x7AD, 0x80 }, + { 0x22, 0x7A6, 0xC7 }, + { 0x22, 0x7A7, 0xDA }, + { 0x22, 0x7A8, 0xE5 }, + { 0x22, 0x7A9, 0xC7 }, + { 0x22, 0x7AA, 0xDA }, + { 0x22, 0x7AB, 0xE5 }, + { 0x22, 0x7AC, 0x28 }, + { 0x22, 0x7AD, 0x80 }, + { 0x22, 0x7A6, 0x40 }, + { 0x22, 0x7A7, 0x00 }, + { 0x22, 0x7A8, 0x00 }, + { 0x22, 0x7A9, 0x40 }, + { 0x22, 0x7AA, 0x00 }, + { 0x22, 0x7AB, 0x00 }, + { 0x22, 0x7AC, 0x29 }, + { 0x22, 0x7AD, 0x80 }, + { 0x22, 0x7A6, 0x8E }, + { 0x22, 0x7A7, 0xD7 }, + { 0x22, 0x7A8, 0x22 }, + { 0x22, 0x7A9, 0x8E }, + { 0x22, 0x7AA, 0xD7 }, + { 0x22, 0x7AB, 0x22 }, + { 0x22, 0x7AC, 0x2A }, + { 0x22, 0x7AD, 0x80 }, + { 0x22, 0x7A6, 0x35 }, + { 0x22, 0x7A7, 0x26 }, + { 0x22, 0x7A8, 0xC6 }, + { 0x22, 0x7A9, 0x35 }, + { 0x22, 0x7AA, 0x26 }, + { 0x22, 0x7AB, 0xC6 }, + { 0x22, 0x7AC, 0x2B }, + { 0x22, 0x7AD, 0x80 }, + { 0x22, 0x7A6, 0x71 }, + { 0x22, 0x7A7, 0x28 }, + { 0x22, 0x7A8, 0xDE }, + { 0x22, 0x7A9, 0x71 }, + { 0x22, 0x7AA, 0x28 }, + { 0x22, 0x7AB, 0xDE }, + { 0x22, 0x7AC, 0x2C }, + { 0x22, 0x7AD, 0x80 }, + { 0x22, 0x7A6, 0xCA }, + { 0x22, 0x7A7, 0xD9 }, + { 0x22, 0x7A8, 0x3A }, + { 0x22, 0x7A9, 0xCA }, + { 0x22, 0x7AA, 0xD9 }, + { 0x22, 0x7AB, 0x3A }, + { 0x22, 0x7AC, 0x2D }, + { 0x22, 0x7AD, 0x80 }, + { 0x22, 0x7A6, 0x40 }, + { 0x22, 0x7A7, 0x00 }, + { 0x22, 0x7A8, 0x00 }, + { 0x22, 0x7A9, 0x40 }, + { 0x22, 0x7AA, 0x00 }, + { 0x22, 0x7AB, 0x00 }, + { 0x22, 0x7AC, 0x2E }, + { 0x22, 0x7AD, 0x80 }, + { 0x22, 0x7A6, 0x93 }, + { 0x22, 0x7A7, 0x5E }, + { 0x22, 0x7A8, 0xD8 }, + { 0x22, 0x7A9, 0x93 }, + { 0x22, 0x7AA, 0x5E }, + { 0x22, 0x7AB, 0xD8 }, + { 0x22, 0x7AC, 0x2F }, + { 0x22, 0x7AD, 0x80 }, + { 0x22, 0x7A6, 0x32 }, + { 0x22, 0x7A7, 0xB7 }, + { 0x22, 0x7A8, 0xB1 }, + { 0x22, 0x7A9, 0x32 }, + { 0x22, 0x7AA, 0xB7 }, + { 0x22, 0x7AB, 0xB1 }, + { 0x22, 0x7AC, 0x30 }, + { 0x22, 0x7AD, 0x80 }, + { 0x22, 0x7A6, 0x6C }, + { 0x22, 0x7A7, 0xA1 }, + { 0x22, 0x7A8, 0x28 }, + { 0x22, 0x7A9, 0x6C }, + { 0x22, 0x7AA, 0xA1 }, + { 0x22, 0x7AB, 0x28 }, + { 0x22, 0x7AC, 0x31 }, + { 0x22, 0x7AD, 0x80 }, + { 0x22, 0x7A6, 0xCD }, + { 0x22, 0x7A7, 0x48 }, + { 0x22, 0x7A8, 0x4F }, + { 0x22, 0x7A9, 0xCD }, + { 0x22, 0x7AA, 0x48 }, + { 0x22, 0x7AB, 0x4F }, + { 0x22, 0x7AC, 0x32 }, + { 0x22, 0x7AD, 0x80 }, + { 0x22, 0x7A6, 0x40 }, + { 0x22, 0x7A7, 0x00 }, + { 0x22, 0x7A8, 0x00 }, + { 0x22, 0x7A9, 0x40 }, + { 0x22, 0x7AA, 0x00 }, + { 0x22, 0x7AB, 0x00 }, + { 0x22, 0x7AC, 0x33 }, + { 0x22, 0x7AD, 0x80 }, + /* common */ + { 0x22, 0x782, 0xC1 }, + { 0x22, 0x771, 0x2C }, + { 0x22, 0x772, 0x2C }, + { 0x22, 0x788, 0x04 }, + { 0x01, 0x7B0, 0x08 }, + {} }; -static 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, - [STAC_HP_ZEPHYR] = hp_zephyr_pin_configs, +static const struct hda_fixup stac92hd83xxx_fixups[] = { + [STAC_92HD83XXX_REF] = { + .type = HDA_FIXUP_PINS, + .v.pins = ref92hd83xxx_pin_configs, + }, + [STAC_92HD83XXX_PWR_REF] = { + .type = HDA_FIXUP_PINS, + .v.pins = ref92hd83xxx_pin_configs, + }, + [STAC_DELL_S14] = { + .type = HDA_FIXUP_PINS, + .v.pins = dell_s14_pin_configs, + }, + [STAC_DELL_VOSTRO_3500] = { + .type = HDA_FIXUP_PINS, + .v.pins = dell_vostro_3500_pin_configs, + }, + [STAC_92HD83XXX_HP_cNB11_INTQUAD] = { + .type = HDA_FIXUP_PINS, + .v.pins = hp_cNB11_intquad_pin_configs, + .chained = true, + .chain_id = STAC_92HD83XXX_HP, + }, + [STAC_92HD83XXX_HP] = { + .type = HDA_FIXUP_FUNC, + .v.func = stac92hd83xxx_fixup_hp, + }, + [STAC_HP_DV7_4000] = { + .type = HDA_FIXUP_PINS, + .v.pins = hp_dv7_4000_pin_configs, + .chained = true, + .chain_id = STAC_92HD83XXX_HP, + }, + [STAC_HP_ZEPHYR] = { + .type = HDA_FIXUP_FUNC, + .v.func = stac92hd83xxx_fixup_hp_zephyr, + .chained = true, + .chain_id = STAC_92HD83XXX_HP, + }, + [STAC_92HD83XXX_HP_LED] = { + .type = HDA_FIXUP_FUNC, + .v.func = stac92hd83xxx_fixup_hp_led, + .chained = true, + .chain_id = STAC_92HD83XXX_HP, + }, + [STAC_92HD83XXX_HP_INV_LED] = { + .type = HDA_FIXUP_FUNC, + .v.func = stac92hd83xxx_fixup_hp_inv_led, + .chained = true, + .chain_id = STAC_92HD83XXX_HP, + }, + [STAC_92HD83XXX_HP_MIC_LED] = { + .type = HDA_FIXUP_FUNC, + .v.func = stac92hd83xxx_fixup_hp_mic_led, + .chained = true, + .chain_id = STAC_92HD83XXX_HP, + }, + [STAC_HP_LED_GPIO10] = { + .type = HDA_FIXUP_FUNC, + .v.func = stac92hd83xxx_fixup_hp_led_gpio10, + .chained = true, + .chain_id = STAC_92HD83XXX_HP, + }, + [STAC_92HD83XXX_HEADSET_JACK] = { + .type = HDA_FIXUP_FUNC, + .v.func = stac92hd83xxx_fixup_headset_jack, + }, + [STAC_HP_ENVY_BASS] = { + .type = HDA_FIXUP_PINS, + .v.pins = (const struct hda_pintbl[]) { + { 0x0f, 0x90170111 }, + {} + }, + }, + [STAC_HP_BNB13_EQ] = { + .type = HDA_FIXUP_VERBS, + .v.verbs = hp_bnb13_eq_verbs, + .chained = true, + .chain_id = STAC_92HD83XXX_HP_MIC_LED, + }, }; -static const char * const stac92hd83xxx_models[STAC_92HD83XXX_MODELS] = { - [STAC_92HD83XXX_AUTO] = "auto", - [STAC_92HD83XXX_REF] = "ref", - [STAC_92HD83XXX_PWR_REF] = "mic-ref", - [STAC_DELL_S14] = "dell-s14", - [STAC_DELL_VOSTRO_3500] = "dell-vostro-3500", - [STAC_92HD83XXX_HP_cNB11_INTQUAD] = "hp_cNB11_intquad", - [STAC_HP_DV7_4000] = "hp-dv7-4000", - [STAC_HP_ZEPHYR] = "hp-zephyr", - [STAC_92HD83XXX_HP_LED] = "hp-led", - [STAC_92HD83XXX_HP_INV_LED] = "hp-inv-led", - [STAC_92HD83XXX_HP_MIC_LED] = "hp-mic-led", - [STAC_92HD83XXX_HEADSET_JACK] = "headset-jack", +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), @@ -1730,8 +2727,112 @@ static const struct snd_pci_quirk stac92hd83xxx_cfg_tbl[] = { "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_92HD83XXX_HP_MIC_LED), + "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, @@ -1766,76 +2867,297 @@ static const struct snd_pci_quirk stac92hd83xxx_cfg_tbl[] = { "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 struct snd_pci_quirk stac92hd83xxx_codec_id_cfg_tbl[] = { - SND_PCI_QUIRK(PCI_VENDOR_ID_HP, 0x3561, - "HP", STAC_HP_ZEPHYR), - {} /* terminator */ +/* 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 ref92hd71bxx_pin_configs[STAC92HD71BXX_NUM_PINS] = { - 0x02214030, 0x02a19040, 0x01a19020, 0x01014010, - 0x0181302e, 0x01014010, 0x01019020, 0x90a000f0, - 0x90a000f0, 0x01452050, 0x01452050, 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_1_pin_configs[STAC92HD71BXX_NUM_PINS] = { - 0x0421101f, 0x04a11221, 0x40f000f0, 0x90170110, - 0x23a1902e, 0x23014250, 0x40f000f0, 0x90a000f0, - 0x40f000f0, 0x4f0000f0, 0x4f0000f0, 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_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_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 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_3_pin_configs[] = { + { 0x0a, 0x0421101f }, + { 0x0b, 0x04a11221 }, + { 0x0c, 0x90a70330 }, + { 0x0d, 0x90170110 }, + { 0x0e, 0x40f000f0 }, + { 0x0f, 0x40f000f0 }, + { 0x14, 0x40f000f0 }, + { 0x18, 0x90a000f0 }, + { 0x19, 0x40f000f0 }, + { 0x1e, 0x044413b0 }, + { 0x1f, 0x044413b0 }, + {} }; -static const 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 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, @@ -1858,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, @@ -1885,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 }, + {} }; /* @@ -1899,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 }, + {} }; /* @@ -1910,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), @@ -2096,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), @@ -2124,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), @@ -2204,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), @@ -2220,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 }, + {} }; /* @@ -2237,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 }, + {} }; /* @@ -2253,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), @@ -2329,1654 +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_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 +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_analog_alt_playback = { - .substreams = 1, - .channels_min = 2, - .channels_max = 2, - .nid = 0x06, /* NID to query formats and rates */ - .ops = { - .open = stac92xx_playback_pcm_open, - .prepare = stac92xx_playback_pcm_prepare, - .cleanup = stac92xx_playback_pcm_cleanup + [STAC_92HD95_HP_BASS] = { + .type = HDA_FIXUP_VERBS, + .v.verbs = (const struct hda_verb[]) { + {0x1a, 0x795, 0x00}, /* HPF to 100Hz */ + {} + }, + .chained = true, + .chain_id = STAC_92HD95_HP_LED, }, }; -static 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]; - 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; - - 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 void stac92xx_auto_set_pinctl(struct hda_codec *codec, hda_nid_t nid, int pin_type) - -{ - snd_hda_set_pin_ctl_cache(codec, nid, 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 == snd_hda_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 = snd_hda_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 |= snd_hda_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; - - 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 struct snd_kcontrol_new * -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 NULL; - knew->index = idx; - knew->private_value = val; - 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) -{ - return add_control_temp(spec, ktemp, idx, name, val) ? 0 : -ENOMEM; -} - -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 (spec->headset_jack && snd_hda_get_input_pin_attr(def_conf) - != INPUT_PIN_ATTR_DOCK) - return 0; - if (snd_hda_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) -{ - struct sigmatel_spec *spec = codec->spec; - char name[32]; - int err; - - if (!spec->check_volume_offset) { - unsigned int caps, step, nums, db_scale; - caps = query_amp_caps(codec, nid, HDA_OUTPUT); - step = (caps & AC_AMPCAP_STEP_SIZE) >> - AC_AMPCAP_STEP_SIZE_SHIFT; - step = (step + 1) * 25; /* in .01dB unit */ - nums = (caps & AC_AMPCAP_NUM_STEPS) >> - AC_AMPCAP_NUM_STEPS_SHIFT; - db_scale = nums * step; - /* if dB scale is over -64dB, and finer enough, - * let's reduce it to half - */ - if (db_scale > 6400 && nums >= 0x1f) - spec->volume_offset = nums / 2; - spec->check_volume_offset = 1; - } - - sprintf(name, "%s Playback Volume", pfx); - err = stac92xx_add_control_idx(spec, STAC_CTL_WIDGET_VOL, idx, name, - HDA_COMPOSE_AMP_VAL_OFS(nid, chs, 0, HDA_OUTPUT, - spec->volume_offset)); - if (err < 0) - return err; - sprintf(name, "%s Playback Switch", pfx); - err = stac92xx_add_control_idx(spec, STAC_CTL_WIDGET_MUTE, idx, name, - HDA_COMPOSE_AMP_VAL(nid, chs, 0, HDA_OUTPUT)); - if (err < 0) - return err; - return 0; -} - -#define create_controls(codec, pfx, nid, chs) \ - create_controls_idx(codec, pfx, 0, nid, chs) - -static int add_spec_dacs(struct sigmatel_spec *spec, hda_nid_t nid) -{ - if (spec->multiout.num_dacs > 4) { - printk(KERN_WARNING "stac92xx: No space for DAC 0x%x\n", nid); - return 1; - } else { - 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; -} - -/* 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: - if (num_outs <= 2) { - name = i ? "Bass Speaker" : "Speaker"; - idx = 0; - break; - } - /* Fall through in case of multi speaker outs */ - default: - name = chname[i]; - idx = 0; - break; - } - err = create_controls_idx(codec, name, idx, nid, 3); - if (err < 0) - return err; - } - } - return 0; -} - -static void stac_gpio_set(struct hda_codec *codec, unsigned int mask, - unsigned int dir_mask, unsigned int data); - -/* hook for controlling mic-mute LED GPIO */ -static int stac92xx_capture_sw_put_led(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 err; - bool mute; - - err = snd_hda_mixer_amp_switch_put(kcontrol, ucontrol); - if (err <= 0) - return err; - mute = !(ucontrol->value.integer.value[0] && - ucontrol->value.integer.value[1]); - if (spec->mic_mute_led_on != mute) { - spec->mic_mute_led_on = mute; - if (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); - } - return err; -} -static int stac92xx_add_capvol_ctls(struct hda_codec *codec, unsigned long vol, - unsigned long sw, int idx) +static int stac_parse_auto_config(struct hda_codec *codec) { struct sigmatel_spec *spec = codec->spec; - struct snd_kcontrol_new *knew; int err; + int flags = 0; - err = stac92xx_add_control_idx(codec->spec, STAC_CTL_WIDGET_VOL, idx, - "Capture Volume", vol); - if (err < 0) - return err; - - knew = add_control_temp(spec, - &stac92xx_control_templates[STAC_CTL_WIDGET_MUTE], - idx, "Capture Switch", sw); - if (!knew) - return -ENOMEM; - /* add a LED hook for some HP laptops */ - if (spec->mic_mute_led_gpio) - knew->put = stac92xx_capture_sw_put_led; + if (spec->headset_jack) + flags |= HDA_PINCFG_HEADSET_MIC; - 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); + err = snd_hda_parse_pin_defcfg(codec, &spec->gen.autocfg, NULL, flags); 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; - } + /* add hooks */ + spec->gen.pcm_playback_hook = stac_playback_pcm_hook; + spec->gen.pcm_capture_hook = stac_capture_pcm_hook; - 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; - } + 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; - 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); + err = snd_hda_gen_parse_auto_config(codec, &spec->gen.autocfg); 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) { @@ -3985,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; } @@ -3993,573 +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 (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; - } - - for (i = 0; i < spec->num_caps; i++) { - err = stac92xx_add_capvol_ctls(codec, spec->capvols[i], - spec->capsws[i], i); - if (err < 0) - return err; - } - - 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; - - 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 (spec->gpio_led) + spec->gen.vmaster_mute.hook = stac_vmaster_hook; - /* 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 (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; } - if (lfe_pin) { - err = create_controls(codec, "LFE", lfe_pin, 1); + if (spec->have_spdif_mux) { + err = stac_create_spdif_mux_ctls(codec); if (err < 0) return err; } - return 0; -} - -static int stac9200_parse_auto_config(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; + stac_init_power_map(codec); return 0; } -static void handle_unsol_event(struct hda_codec *codec, - struct hda_jack_tbl *event); -/* 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; - event->callback = handle_unsol_event; - 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) +static int stac_init(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; -} - -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; - if (spec->init) - 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 = snd_hda_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]; - unsigned int pinctl, def_conf; - - def_conf = snd_hda_codec_get_pincfg(codec, nid); - def_conf = get_defcfg_connect(def_conf); - if (def_conf == AC_JACK_PORT_NONE) { - /* power off unused ports */ - stac_toggle_power_map(codec, nid, 0); - continue; - } - if (def_conf == AC_JACK_PORT_FIXED) { - /* no need for jack detection for fixed pins */ - stac_toggle_power_map(codec, nid, 1); - continue; - } - /* 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 || - !is_jack_detectable(codec, nid)) { - stac_toggle_power_map(codec, nid, 1); - continue; - } - - if (is_nid_out_jack_pin(cfg, nid)) - continue; /* already has an unsol event */ - - 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; - } - if (enable_pin_detect(codec, nid, STAC_PWR_EVENT)) { - stac_issue_unsol_event(codec, nid); - continue; - } - /* none of the above, turn the port OFF */ - stac_toggle_power_map(codec, nid, 0); - } + spec->gpio_data |= spec->eapd_mask; + stac_gpio_set(codec, spec->gpio_mask, spec->gpio_dir, spec->gpio_data); - /* sync mute LED */ - if (spec->gpio_led) { - if (spec->vmaster_mute.hook) - snd_hda_sync_vmaster_hook(&spec->vmaster_mute); - else /* the very first init call doesn't have vmaster yet */ - stac92xx_update_led_status(codec, false); - } + snd_hda_gen_init(codec); /* 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); - 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); + /* 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); + } } - 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_set_pin_ctl(codec, pin->nid, 0); - } + return 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, @@ -4567,466 +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; - - 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_set_pin_ctl_cache(codec, nid, 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_set_pin_ctl_cache(codec, nid, 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; - - if (cfg->speaker_outs == 0) - return; - - 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 = spec->power_map_bits; - if (enable) - val &= ~idx; - else - val |= idx; - - /* power down unused output ports */ - if (val != spec->power_map_bits) { - spec->power_map_bits = val; - snd_hda_codec_write(codec, codec->afg, 0, - AC_VERB_IDT_SET_POWER_MAP, 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 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 (get_int_hint(codec, "gpio_led", &spec->gpio_led)) { - get_int_hint(codec, "gpio_led_polarity", - &spec->gpio_led_polarity); - return 1; - } - 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, @@ -5076,101 +4357,37 @@ 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) +static int stac_suspend(struct hda_codec *codec) { - stac92xx_shutup(codec); + stac_shutup(codec); return 0; } - -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); -} #else -#define stac92xx_suspend NULL -#define stac92xx_resume NULL -#define stac92xx_set_power_state NULL +#define stac_suspend NULL #endif /* CONFIG_PM */ -/* update mute-LED accoring to the master switch */ -static void stac92xx_update_led_status(struct hda_codec *codec, int enabled) -{ - struct sigmatel_spec *spec = codec->spec; - int muted = !enabled; - - if (!spec->gpio_led) - return; - - /* LED state is inverted on these systems */ - if (spec->gpio_led_polarity) - muted = !muted; - - 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 struct hda_codec_ops stac92xx_patch_ops = { - .build_controls = stac92xx_build_controls, - .build_pcms = stac92xx_build_pcms, - .init = stac92xx_init, - .free = stac92xx_free, +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 = stac92xx_suspend, - .resume = stac92xx_resume, + .suspend = stac_suspend, #endif - .reboot_notify = stac92xx_shutup, + .reboot_notify = stac_shutup, }; -static int alloc_stac_spec(struct hda_codec *codec, int num_pins, - const hda_nid_t *pin_nids) +static int alloc_stac_spec(struct hda_codec *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 */ - spec->num_pins = num_pins; - spec->pin_nids = pin_nids; - snd_array_init(&spec->kctls, sizeof(struct snd_kcontrol_new), 32); return 0; } @@ -5179,59 +4396,30 @@ static int patch_stac9200(struct hda_codec *codec) struct sigmatel_spec *spec; int err; - err = alloc_stac_spec(codec, ARRAY_SIZE(stac9200_pin_nids), - stac9200_pin_nids); + err = alloc_stac_spec(codec); if (err < 0) return err; spec = codec->spec; spec->linear_tone_beep = 1; - 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; - err = stac9200_parse_auto_config(codec); + 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 = 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; } @@ -5241,79 +4429,29 @@ static int patch_stac925x(struct hda_codec *codec) struct sigmatel_spec *spec; int err; - err = alloc_stac_spec(codec, ARRAY_SIZE(stac925x_pin_nids), - stac925x_pin_nids); + err = alloc_stac_spec(codec); if (err < 0) return err; spec = codec->spec; spec->linear_tone_beep = 1; + spec->gen.own_eapd_ctl = 1; - /* 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; - } + codec->patch_ops = stac_patch_ops; - 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; - } + 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; } @@ -5321,467 +4459,195 @@ 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; int num_dacs; - err = alloc_stac_spec(codec, ARRAY_SIZE(stac92hd73xx_pin_nids), - stac92hd73xx_pin_nids); + err = alloc_stac_spec(codec); if (err < 0) return err; spec = codec->spec; spec->linear_tone_beep = 0; - codec->slave_dig_outs = stac92hd73xx_slave_dig_outs; - 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; + snd_hda_apply_fixup(codec, HDA_FIXUP_ACT_PROBE); - switch (codec->subsystem_id) { - case 0x103c1618: - case 0x103c1619: - case 0x103c161a: - case 0x103c161b: - case 0x103c161c: - case 0x103c161d: - case 0x103c161e: - case 0x103c161f: - - case 0x103c162a: - case 0x103c162b: - - case 0x103c1630: - case 0x103c1631: - - case 0x103c1633: - case 0x103c1634: - case 0x103c1635: - - case 0x103c3587: - case 0x103c3588: - case 0x103c3589: - case 0x103c358a: - - case 0x103c3667: - case 0x103c3668: - case 0x103c3669: - - return 1; - } return 0; } -static void stac92hd8x_add_pin(struct hda_codec *codec, hda_nid_t nid) -{ - struct sigmatel_spec *spec = codec->spec; - unsigned int def_conf = snd_hda_codec_get_pincfg(codec, nid); - int i; - - spec->auto_pin_nids[spec->auto_pin_cnt] = nid; - spec->auto_pin_cnt++; - - if (get_defcfg_device(def_conf) == AC_JACK_MIC_IN && - get_defcfg_connect(def_conf) != AC_JACK_PORT_NONE) { - for (i = 0; i < ARRAY_SIZE(stac92hd83xxx_dmic_nids); i++) { - if (nid == stac92hd83xxx_dmic_nids[i]) { - spec->auto_dmic_nids[spec->auto_dmic_cnt] = nid; - spec->auto_dmic_cnt++; - } - } - } -} - -static void stac92hd8x_add_adc(struct hda_codec *codec, hda_nid_t nid) -{ - struct sigmatel_spec *spec = codec->spec; - - spec->auto_adc_nids[spec->auto_adc_cnt] = nid; - spec->auto_adc_cnt++; -} - -static void stac92hd8x_add_mux(struct hda_codec *codec, hda_nid_t nid) +static void stac_setup_gpio(struct hda_codec *codec) { - int i, j; struct sigmatel_spec *spec = codec->spec; - for (i = 0; i < spec->auto_adc_cnt; i++) { - if (get_connection_index(codec, - spec->auto_adc_nids[i], nid) >= 0) { - /* mux and volume for adc_nids[i] */ - if (!spec->auto_mux_nids[i]) { - spec->auto_mux_nids[i] = nid; - /* 92hd codecs capture volume is in mux */ - spec->auto_capvols[i] = HDA_COMPOSE_AMP_VAL(nid, - 3, 0, HDA_OUTPUT); - } - for (j = 0; j < spec->auto_dmic_cnt; j++) { - if (get_connection_index(codec, nid, - spec->auto_dmic_nids[j]) >= 0) { - /* dmux for adc_nids[i] */ - if (!spec->auto_dmux_nids[i]) - spec->auto_dmux_nids[i] = nid; - break; - } - } - break; + spec->gpio_mask |= spec->eapd_mask; + if (spec->gpio_led) { + if (!spec->vref_mute_led_nid) { + spec->gpio_mask |= spec->gpio_led; + spec->gpio_dir |= spec->gpio_led; + spec->gpio_data |= spec->gpio_led; + } else { + codec->power_filter = stac_vref_led_power_filter; } } -} -static void stac92hd8x_fill_auto_spec(struct hda_codec *codec) -{ - hda_nid_t nid, end_nid; - unsigned int wid_caps, wid_type; - struct sigmatel_spec *spec = codec->spec; - - end_nid = codec->start_nid + codec->num_nodes; - - for (nid = codec->start_nid; nid < end_nid; nid++) { - wid_caps = get_wcaps(codec, nid); - wid_type = get_wcaps_type(wid_caps); - - if (wid_type == AC_WID_PIN) - stac92hd8x_add_pin(codec, nid); - - if (wid_type == AC_WID_AUD_IN && !(wid_caps & AC_WCAP_DIGITAL)) - stac92hd8x_add_adc(codec, nid); - } - - for (nid = codec->start_nid; nid < end_nid; nid++) { - wid_caps = get_wcaps(codec, nid); - wid_type = get_wcaps_type(wid_caps); + if (spec->mic_mute_led_gpio) { + spec->gpio_mask |= spec->mic_mute_led_gpio; + spec->gpio_dir |= spec->mic_mute_led_gpio; + spec->mic_enabled = 0; + spec->gpio_data |= spec->mic_mute_led_gpio; - if (wid_type == AC_WID_AUD_SEL) - stac92hd8x_add_mux(codec, nid); + spec->gen.cap_sync_hook = stac_capture_led_hook; } - - spec->pin_nids = spec->auto_pin_nids; - spec->num_pins = spec->auto_pin_cnt; - spec->adc_nids = spec->auto_adc_nids; - spec->num_adcs = spec->auto_adc_cnt; - spec->capvols = spec->auto_capvols; - spec->capsws = spec->auto_capvols; - spec->num_caps = spec->auto_adc_cnt; - spec->mux_nids = spec->auto_mux_nids; - spec->num_muxes = spec->auto_adc_cnt; - spec->dmux_nids = spec->auto_dmux_nids; - spec->num_dmuxes = spec->auto_adc_cnt; - spec->dmic_nids = spec->auto_dmic_nids; - spec->num_dmics = spec->auto_dmic_cnt; } static int patch_stac92hd83xxx(struct hda_codec *codec) { struct sigmatel_spec *spec; - int default_polarity = -1; /* no default cfg */ int err; - err = alloc_stac_spec(codec, 0, NULL); /* pins filled later */ + err = alloc_stac_spec(codec); if (err < 0) return err; - if (hp_bnb2011_with_dock(codec)) { - snd_hda_codec_set_pincfg(codec, 0xa, 0x2101201f); - snd_hda_codec_set_pincfg(codec, 0xf, 0x2181205e); - } - codec->epss = 0; /* longer delay needed for D3 */ - stac92hd8x_fill_auto_spec(codec); 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); - /* check codec subsystem id if not found */ - if (spec->board_config < 0) - spec->board_config = - snd_hda_check_board_codec_sid_config(codec, - STAC_92HD83XXX_MODELS, stac92hd83xxx_models, - stac92hd83xxx_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, - 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; - switch (spec->board_config) { - case STAC_HP_ZEPHYR: - spec->init = stac92hd83xxx_hp_zephyr_init; - break; - case STAC_92HD83XXX_HP_LED: - default_polarity = 0; - break; - case STAC_92HD83XXX_HP_INV_LED: - default_polarity = 1; - break; - case STAC_92HD83XXX_HP_MIC_LED: - spec->mic_mute_led_gpio = 0x08; /* GPIO3 */ - break; - case STAC_92HD83XXX_HEADSET_JACK: - spec->headset_jack = 1; - break; - } + snd_hda_add_verbs(codec, stac92hd83xxx_core_init); - if (find_mute_led_cfg(codec, default_polarity)) - snd_printd("mute LED gpio %d polarity %d\n", - spec->gpio_led, - spec->gpio_led_polarity); + snd_hda_pick_fixup(codec, stac92hd83xxx_models, stac92hd83xxx_fixup_tbl, + stac92hd83xxx_fixups); + snd_hda_apply_fixup(codec, HDA_FIXUP_ACT_PRE_PROBE); - 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; - } - } - - if (spec->mic_mute_led_gpio) { - spec->gpio_mask |= spec->mic_mute_led_gpio; - spec->gpio_dir |= spec->mic_mute_led_gpio; - spec->mic_mute_led_on = true; - spec->gpio_data |= spec->mic_mute_led_gpio; - } - - 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; - } + 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; } @@ -5789,82 +4655,32 @@ 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; - err = alloc_stac_spec(codec, STAC92HD71BXX_NUM_PINS, - stac92hd71bxx_pin_nids_4port); + err = alloc_stac_spec(codec); if (err < 0) return err; spec = codec->spec; spec->linear_tone_beep = 0; - codec->patch_ops = stac92xx_patch_ops; - switch (codec->vendor_id) { - case 0x111d76b6: - case 0x111d76b7: - break; - case 0x111d7603: - case 0x111d7608: - /* On 92HD75Bx 0x27 isn't a pin nid */ - spec->num_pins--; - /* fallthrough */ - default: - spec->pin_nids = stac92hd71bxx_pin_nids_6port; - } - spec->num_pwrs = ARRAY_SIZE(stac92hd71bxx_pwr_nids); - spec->board_config = snd_hda_check_board_config(codec, - STAC_92HD71BXX_MODELS, - stac92hd71bxx_models, - stac92hd71bxx_cfg_tbl); -again: - if (spec->board_config < 0) - snd_printdd(KERN_INFO "hda_codec: %s: BIOS auto-probing.\n", - codec->chip_name); - else - stac92xx_set_config_regs(codec, - stac92hd71bxx_brd_tbl[spec->board_config]); + spec->gen.own_eapd_ctl = 1; + spec->gen.power_down_unused = 1; + spec->gen.mixer_nid = 0x17; + spec->have_spdif_mux = 1; - if (spec->board_config != STAC_92HD71BXX_REF) { - /* GPIO0 = EAPD */ - spec->gpio_mask = 0x01; - spec->gpio_dir = 0x01; - spec->gpio_data = 0x01; - } - - spec->dmic_nids = stac92hd71bxx_dmic_nids; - spec->dmux_nids = stac92hd71bxx_dmux_nids; + codec->patch_ops = stac_patch_ops; - spec->num_caps = STAC92HD71BXX_NUM_CAPS; - spec->capvols = stac92hd71bxx_capvols; - spec->capsws = stac92hd71bxx_capsws; + /* GPIO0 = EAPD */ + spec->gpio_mask = 0x01; + spec->gpio_dir = 0x01; + spec->gpio_data = 0x01; switch (codec->vendor_id) { case 0x111d76b6: /* 4 Port without Analog Mixer */ case 0x111d76b7: unmute_init++; - /* fallthru */ - case 0x111d76b4: /* 6 Port without Analog Mixer */ - case 0x111d76b5: - 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 */ @@ -5873,158 +4689,45 @@ again: 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: - 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) - spec->init = stac92hd71bxx_core_init; + 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; - } - - 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); + snd_hda_pick_fixup(codec, stac92hd71bxx_models, stac92hd71bxx_fixup_tbl, + stac92hd71bxx_fixups); + snd_hda_apply_fixup(codec, HDA_FIXUP_ACT_PRE_PROBE); - 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; - } - } - - 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; } @@ -6033,93 +4736,17 @@ static int patch_stac922x(struct hda_codec *codec) struct sigmatel_spec *spec; int err; - err = alloc_stac_spec(codec, ARRAY_SIZE(stac922x_pin_nids), - stac922x_pin_nids); + err = alloc_stac_spec(codec); if (err < 0) return err; spec = codec->spec; spec->linear_tone_beep = 1; - spec->board_config = snd_hda_check_board_config(codec, STAC_922X_MODELS, - stac922x_models, - stac922x_cfg_tbl); - if (spec->board_config == STAC_INTEL_MAC_AUTO) { - spec->gpio_mask = spec->gpio_dir = 0x03; - spec->gpio_data = 0x03; - /* Intel Macs have all same PCI SSID, so we need to check - * codec SSID to distinguish the exact models - */ - printk(KERN_INFO "hda_codec: STAC922x, Apple subsys_id=%x\n", codec->subsystem_id); - switch (codec->subsystem_id) { - - case 0x106b0800: - spec->board_config = STAC_INTEL_MAC_V1; - break; - case 0x106b0600: - case 0x106b0700: - spec->board_config = STAC_INTEL_MAC_V2; - break; - case 0x106b0e00: - case 0x106b0f00: - case 0x106b1600: - case 0x106b1700: - case 0x106b0200: - case 0x106b1e00: - spec->board_config = STAC_INTEL_MAC_V3; - break; - case 0x106b1a00: - case 0x00000100: - spec->board_config = STAC_INTEL_MAC_V4; - break; - case 0x106b0a00: - case 0x106b2200: - spec->board_config = STAC_INTEL_MAC_V5; - break; - default: - spec->board_config = STAC_INTEL_MAC_V3; - break; - } - } - - again: - if (spec->board_config < 0) - snd_printdd(KERN_INFO "hda_codec: %s: BIOS auto-probing.\n", - codec->chip_name); - else - stac92xx_set_config_regs(codec, - stac922x_brd_tbl[spec->board_config]); - - spec->adc_nids = stac922x_adc_nids; - spec->mux_nids = stac922x_mux_nids; - spec->num_muxes = ARRAY_SIZE(stac922x_mux_nids); - spec->num_adcs = ARRAY_SIZE(stac922x_adc_nids); - spec->num_dmics = 0; - spec->num_pwrs = 0; + spec->gen.own_eapd_ctl = 1; - spec->init = stac922x_core_init; + codec->patch_ops = stac_patch_ops; - spec->num_caps = STAC922X_NUM_CAPS; - spec->capvols = stac922x_capvols; - spec->capsws = stac922x_capsws; - - spec->multiout.dac_nids = spec->dac_nids; - - err = stac92xx_parse_auto_config(codec); - 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, @@ -6128,122 +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; - err = alloc_stac_spec(codec, ARRAY_SIZE(stac927x_pin_nids), - stac927x_pin_nids); + err = alloc_stac_spec(codec); if (err < 0) return err; spec = codec->spec; spec->linear_tone_beep = 1; - codec->slave_dig_outs = stac927x_slave_dig_outs; - 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; /* @@ -6258,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; } @@ -6270,103 +4840,46 @@ static int patch_stac9205(struct hda_codec *codec) struct sigmatel_spec *spec; int err; - err = alloc_stac_spec(codec, ARRAY_SIZE(stac9205_pin_nids), - stac9205_pin_nids); + err = alloc_stac_spec(codec); if (err < 0) return err; spec = codec->spec; spec->linear_tone_beep = 1; - 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; } @@ -6380,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 */ @@ -6424,41 +4929,30 @@ static int patch_stac9872(struct hda_codec *codec) struct sigmatel_spec *spec; int err; - err = alloc_stac_spec(codec, ARRAY_SIZE(stac9872_pin_nids), - stac9872_pin_nids); + err = alloc_stac_spec(codec); if (err < 0) return err; spec = codec->spec; spec->linear_tone_beep = 1; + spec->gen.own_eapd_ctl = 1; - 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); + 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; } @@ -6529,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 09bb64996d7..778166259b3 100644 --- a/sound/pci/hda/patch_via.c +++ b/sound/pci/hda/patch_via.c @@ -56,6 +56,7 @@ #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 @@ -86,39 +87,6 @@ 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; @@ -129,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; @@ -207,36 +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); unsigned int dac_stream_tag[4]; - - 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; }; 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; @@ -244,15 +128,17 @@ static struct via_spec * via_new_spec(struct hda_codec *codec) if (spec == NULL) return NULL; - snd_array_init(&spec->kctls, sizeof(struct snd_kcontrol_new), 32); - 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; - snd_hda_gen_init(&spec->gen); + 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; } @@ -308,16 +194,6 @@ static enum VIA_HDA_CODEC get_codec_type(struct hda_codec *codec) return codec_type; }; -#define VIA_JACK_EVENT 0x20 -#define VIA_HP_EVENT 0x01 -#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); @@ -325,395 +201,52 @@ 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); -} - -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; - - 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) -{ - int i, num, val; - - if (!path) - return; - num = snd_hda_get_num_conns(codec, mix_nid); - 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); - } -} - -/* enable/disable the output-route */ -static void activate_output_path(struct hda_codec *codec, struct nid_path *path, - bool enable, bool force) -{ - 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); - } - } -} - -/* set the given pin as output */ -static void init_output_pin(struct hda_codec *codec, hda_nid_t pin, - int pin_type) -{ - if (!pin) - return; - snd_hda_set_pin_ctl(codec, pin, 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); -} - -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); - if (get_wcaps(codec, pin) & AC_WCAP_OUT_AMP) - caps = query_amp_caps(codec, pin, HDA_OUTPUT); - else - caps = 0; - 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); - } -} - -static void via_auto_init_hp_out(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); -} - -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) -{ - 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++) { - hda_nid_t nid = spec->adc_nids[i]; - if (!(get_wcaps(codec, nid) & AC_WCAP_IN_AMP) || - !(query_amp_caps(codec, nid, HDA_INPUT) & AC_AMPCAP_MUTE)) - continue; - 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 { - ctl = PIN_IN; - if (cfg->inputs[i].type == AUTO_PIN_MIC) - ctl |= snd_hda_get_default_vref(codec, nid); - } - snd_hda_set_pin_ctl(codec, nid, 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)); - } +#endif } 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) + if (snd_hda_check_power_state(codec, nid, parm)) return; snd_hda_codec_write(codec, nid, 0, AC_VERB_SET_POWER_STATE, parm); } @@ -723,8 +256,8 @@ static void update_conv_power_state(struct hda_codec *codec, hda_nid_t nid, { struct via_spec *spec = codec->spec; unsigned int format; - if (snd_hda_codec_read(codec, nid, 0, - AC_VERB_GET_POWER_STATE, 0) == parm) + + if (snd_hda_check_power_state(codec, nid, parm)) return; format = snd_hda_codec_read(codec, nid, 0, AC_VERB_GET_CONV, 0); if (format && (spec->dac_stream_tag[index] != format)) @@ -740,6 +273,23 @@ static void update_conv_power_state(struct hda_codec *codec, hda_nid_t nid, } } +static bool smart51_enabled(struct hda_codec *codec) +{ + struct via_spec *spec = codec->spec; + return spec->gen.ext_channel_count > 2; +} + +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->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, unsigned int *affected_parm) { @@ -754,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 */ @@ -795,277 +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_set_pin_ctl(codec, nid, 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); @@ -1086,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; @@ -1131,366 +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_pfxs[] = { - "Front", "Surround", "Center", "LFE", "Side", - "Headphone", "Speaker", "Bass Speaker", - 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]); @@ -1498,235 +451,33 @@ 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_pfxs, - "Playback Volume"); - 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_pfxs, - "Playback Switch"); - 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++) { - if (!spec->mux_nids[i]) - continue; - 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->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->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); - snd_hda_gen_free(&spec->gen); - 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_set_pin_ctl(codec, pins[i], 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) && - is_jack_detectable(codec, spec->autocfg.hp_pins[0])) - 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); } #ifdef CONFIG_PM static int via_suspend(struct hda_codec *codec) { struct via_spec *spec = codec->spec; - vt1708_stop_hp_work(spec); + vt1708_stop_hp_work(codec); - if (spec->codec_type == VT1802) { - /* Fix pop noise on headphones */ - int i; - for (i = 0; i < spec->autocfg.hp_outs; i++) - snd_hda_set_pin_ctl(codec, spec->autocfg.hp_pins[i], 0); - } + /* Fix pop noise on headphones */ + if (spec->codec_type == VT1802) + snd_hda_shutup_pins(codec); return 0; } @@ -1736,7 +487,10 @@ static int via_suspend(struct hda_codec *codec) 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 @@ -1747,7 +501,7 @@ 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 = snd_hda_jack_unsol_event, @@ -1757,840 +511,12 @@ static const struct hda_codec_ops via_patch_ops = { #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; - hda_nid_t nid; - - spec->multiout.num_dacs = 0; - spec->multiout.dac_nids = spec->private_dac_nids; - 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[spec->multiout.num_dacs++] = dac; - } - 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; - } - 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", NULL /* "CLFE" */, "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 <= 2) - pfx = i ? "Bass Speaker" : "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; - const char *prev_label = NULL; - int type_idx = 0; - 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); - if (prev_label && !strcmp(label, prev_label)) - type_idx++; - else - type_idx = 0; - prev_label = label; - snprintf(name, sizeof(name), "%s Boost Volume", label); - err = __via_add_control(spec, VIA_CTL_WIDGET_VOL, name, type_idx, - 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; @@ -2633,102 +559,32 @@ 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) -{ - 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; -} - -static void via_auto_init_dig_outs(struct hda_codec *codec) -{ - 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); -} - -static void via_auto_init_dig_in(struct hda_codec *codec) +static void via_hp_automute(struct hda_codec *codec, struct hda_jack_tbl *tbl) { - struct via_spec *spec = codec->spec; - if (!spec->dig_in_nid) - return; - snd_hda_set_pin_ctl(codec, spec->autocfg.dig_in_pin, PIN_IN); + set_widgets_power_state(codec); + snd_hda_gen_hp_automute(codec, tbl); } -static void via_jack_output_event(struct hda_codec *codec, struct hda_jack_tbl *tbl) +static void via_line_automute(struct hda_codec *codec, struct hda_jack_tbl *tbl) { set_widgets_power_state(codec); - via_hp_automute(codec); + snd_hda_gen_line_automute(codec, tbl); } static void via_jack_powerstate_event(struct hda_codec *codec, struct hda_jack_tbl *tbl) @@ -2736,41 +592,75 @@ static void via_jack_powerstate_event(struct hda_codec *codec, struct hda_jack_t 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; - hda_jack_callback cb; - - if (cfg->hp_pins[0] && is_jack_detectable(codec, cfg->hp_pins[0])) - snd_hda_jack_detect_enable_callback(codec, cfg->hp_pins[0], - VIA_HP_EVENT | VIA_JACK_EVENT, - via_jack_output_event); + spec->gen.hp_automute_hook = via_hp_automute; if (cfg->speaker_pins[0]) - ev = VIA_LINE_EVENT; - else - ev = 0; - cb = ev ? via_jack_output_event : via_jack_powerstate_event; + 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_callback(codec, cfg->line_out_pins[i], - ev | VIA_JACK_EVENT, cb); + 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_callback(codec, cfg->inputs[i].pin, + 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; @@ -2783,63 +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); + 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; } @@ -2853,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); @@ -2867,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; } @@ -2892,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) { @@ -2936,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); @@ -2945,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); @@ -2953,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); @@ -2973,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); } @@ -2991,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); @@ -3016,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) | @@ -3088,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); @@ -3119,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; } @@ -3173,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, @@ -3240,17 +1074,17 @@ static void set_widgets_power_state_vt1718S(struct hda_codec *codec) /* 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->hp_independent_mode && parm2 != AC_PWRST_D3) + if (!spec->gen.indep_hp_enabled && parm2 != AC_PWRST_D3) parm = parm2; update_power_state(codec, 0xb, parm); /* MW9 (21h), Mw2 (1ah), AOW0 (8h) */ @@ -3259,11 +1093,11 @@ static void set_widgets_power_state_vt1718S(struct hda_codec *codec) /* 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); @@ -3283,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) @@ -3300,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); } } @@ -3318,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); @@ -3449,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); @@ -3458,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); @@ -3475,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; @@ -3490,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 */ @@ -3509,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); @@ -3522,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; @@ -3609,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 */ @@ -3707,7 +1539,7 @@ 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) @@ -3778,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 */ @@ -3831,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); @@ -3901,7 +1733,7 @@ static void set_widgets_power_state_vt3476(struct hda_codec *codec) parm = AC_PWRST_D3; set_pin_power_state(codec, 0x26, &parm); update_power_state(codec, 0x36, parm); - if (spec->smart51_enabled) { + if (smart51_enabled(codec)) { /* PW7(2bh), MW7(3bh), MUX7(1Bh) */ set_pin_power_state(codec, 0x2b, &parm); update_power_state(codec, 0x3b, parm); @@ -3913,7 +1745,7 @@ static void set_widgets_power_state_vt3476(struct hda_codec *codec) parm = AC_PWRST_D3; set_pin_power_state(codec, 0x25, &parm); update_power_state(codec, 0x35, parm); - if (spec->smart51_enabled) { + if (smart51_enabled(codec)) { /* PW6(2ah), MW6(3ah), MUX6(1ah) */ set_pin_power_state(codec, 0x2a, &parm); update_power_state(codec, 0x3a, parm); @@ -3926,7 +1758,7 @@ static void set_widgets_power_state_vt3476(struct hda_codec *codec) set_pin_power_state(codec, 0x28, &parm); update_power_state(codec, 0x38, parm); update_power_state(codec, 0x18, parm); - if (spec->hp_independent_mode) + if (spec->gen.indep_hp_enabled) update_conv_power_state(codec, 0xb, parm, 3); parm2 = parm; /* for pin 0x0b */ @@ -3934,7 +1766,7 @@ static void set_widgets_power_state_vt3476(struct hda_codec *codec) parm = AC_PWRST_D3; set_pin_power_state(codec, 0x24, &parm); update_power_state(codec, 0x34, parm); - if (!spec->hp_independent_mode && parm2 != AC_PWRST_D3) + 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) */ @@ -3951,7 +1783,7 @@ static int patch_vt3476(struct hda_codec *codec) if (spec == NULL) return -ENOMEM; - spec->aa_mix_nid = 0x3f; + spec->gen.mixer_nid = 0x3f; add_secret_dac_path(codec); /* automatic parse from the BIOS config */ 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 */ |
