Fixed MTP to work with TWRP

This commit is contained in:
awab228 2018-06-19 23:16:04 +02:00
commit f6dfaef42e
50820 changed files with 20846062 additions and 0 deletions

251
sound/pci/hda/Kconfig Normal file
View file

@ -0,0 +1,251 @@
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.
This option enables the HD-audio controller. Don't forget
to choose the appropriate codec options below.
To compile this driver as a module, choose M here: the module
will be called snd-hda-intel.
config SND_HDA_TEGRA
tristate "NVIDIA Tegra HD Audio"
depends on ARCH_TEGRA
select SND_HDA
help
Say Y here to support the HDA controller present in NVIDIA
Tegra SoCs
This options enables support for the HD Audio controller
present in some NVIDIA Tegra SoCs, used to communicate audio
to the HDMI output.
To compile this driver as a module, choose M here: the module
will be called snd-hda-tegra.
if SND_HDA
config SND_HDA_DSP_LOADER
bool
config SND_HDA_PREALLOC_SIZE
int "Pre-allocated buffer size for HD-audio driver"
range 0 32768
default 64
help
Specifies the default pre-allocated buffer-size in kB for the
HD-audio driver. A larger buffer (e.g. 2048) is preferred
for systems using PulseAudio. The default 64 is chosen just
for compatibility reasons.
Note that the pre-allocation size can be changed dynamically
via a proc file (/proc/asound/card*/pcm*/sub*/prealloc), too.
config SND_HDA_HWDEP
bool "Build hwdep interface for HD-audio driver"
select SND_HWDEP
help
Say Y here to build a hwdep interface for HD-audio driver.
This interface can be used for out-of-band communication
with codecs for debugging purposes.
config SND_HDA_RECONFIG
bool "Allow dynamic codec reconfiguration"
help
Say Y here to enable the HD-audio codec re-configuration feature.
This adds the sysfs interfaces to allow user to clear the whole
codec configuration, change the codec setup, add extra verbs,
and re-configure the codec dynamically.
config SND_HDA_INPUT_BEEP
bool "Support digital beep via input layer"
depends on INPUT=y || INPUT=SND_HDA
help
Say Y here to build a digital beep interface for HD-audio
driver. This interface is used to generate digital beeps.
config SND_HDA_INPUT_BEEP_MODE
int "Digital beep registration mode (0=off, 1=on)"
depends on SND_HDA_INPUT_BEEP=y
default "1"
range 0 1
help
Set 0 to disable the digital beep interface for HD-audio by default.
Set 1 to always enable the digital beep interface for HD-audio by
default.
config SND_HDA_INPUT_JACK
bool "Support jack plugging notification via input layer"
depends on INPUT=y || INPUT=SND
select SND_JACK
help
Say Y here to enable the jack plugging notification via
input layer.
config SND_HDA_PATCH_LOADER
bool "Support initialization patch loading for HD-audio"
select FW_LOADER
select SND_HDA_RECONFIG
help
Say Y here to allow the HD-audio driver to load a pseudo
firmware file ("patch") for overriding the BIOS setup at
start up. The "patch" file can be specified via patch module
option, such as patch=hda-init.
config SND_HDA_CODEC_REALTEK
tristate "Build Realtek HD-audio codec support"
select SND_HDA_GENERIC
help
Say Y or M here to include Realtek HD-audio codec support in
snd-hda-intel driver, such as ALC880.
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
tristate "Build Analog Device HD-audio codec support"
select SND_HDA_GENERIC
help
Say Y or M here to include Analog Device HD-audio codec support in
snd-hda-intel driver, such as AD1986A.
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
tristate "Build IDT/Sigmatel HD-audio codec support"
select SND_HDA_GENERIC
help
Say Y or M here to include IDT (Sigmatel) HD-audio codec support in
snd-hda-intel driver, such as STAC9200.
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
tristate "Build VIA HD-audio codec support"
select SND_HDA_GENERIC
help
Say Y or M here to include VIA HD-audio codec support in
snd-hda-intel driver, such as VT1708.
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
tristate "Build HDMI/DisplayPort HD-audio codec support"
help
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.
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_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 or M here to include Cirrus Logic codec support in
snd-hda-intel driver, such as CS4206.
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
tristate "Build Conexant HD-audio codec support"
select SND_HDA_GENERIC
help
Say Y or M here to include Conexant HD-audio codec support in
snd-hda-intel driver, such as CX20549.
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
tristate "Build Creative CA0110-IBG codec support"
select SND_HDA_GENERIC
help
Say Y or M here to include Creative CA0110-IBG codec support in
snd-hda-intel driver, found on some Creative X-Fi cards.
comment "Set to Y if you want auto-loading the codec driver"
depends on SND_HDA=y && SND_HDA_CODEC_CA0110=m
config SND_HDA_CODEC_CA0132
tristate "Build Creative CA0132 codec support"
help
Say Y or M here to include Creative CA0132 codec support in
snd-hda-intel driver.
comment "Set to Y if you want auto-loading the codec driver"
depends on SND_HDA=y && SND_HDA_CODEC_CA0132=m
config SND_HDA_CODEC_CA0132_DSP
bool "Support new DSP code for CA0132 codec"
depends on SND_HDA_CODEC_CA0132
select SND_HDA_DSP_LOADER
select FW_LOADER
help
Say Y here to enable the DSP for Creative CA0132 for extended
features like equalizer or echo cancellation.
Note that this option requires the external firmware file
(ctefx.bin).
config SND_HDA_CODEC_CMEDIA
tristate "Build C-Media HD-audio codec support"
select SND_HDA_GENERIC
help
Say Y or M here to include C-Media HD-audio codec support in
snd-hda-intel driver, such as CMI9880.
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
tristate "Build Silicon Labs 3054 HD-modem codec support"
help
Say Y or M here to include Silicon Labs 3054 HD-modem codec
(and compatibles) support in snd-hda-intel driver.
comment "Set to Y if you want auto-loading the codec driver"
depends on SND_HDA=y && SND_HDA_CODEC_SI3054=m
config SND_HDA_GENERIC
tristate "Enable generic HD-audio codec parser"
help
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
default 0
help
The default time-out value in seconds for HD-audio automatic
power-save mode. 0 means to disable the power-save mode.
endif
endmenu

51
sound/pci/hda/Makefile Normal file
View file

@ -0,0 +1,51 @@
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 hda_sysfs.o
snd-hda-codec-$(CONFIG_PROC_FS) += hda_proc.o
snd-hda-codec-$(CONFIG_SND_HDA_HWDEP) += hda_hwdep.o
snd-hda-codec-$(CONFIG_SND_HDA_INPUT_BEEP) += hda_beep.o
# for trace-points
CFLAGS_hda_codec.o := -I$(src)
CFLAGS_hda_controller.o := -I$(src)
snd-hda-codec-generic-objs := hda_generic.o
snd-hda-codec-realtek-objs := patch_realtek.o
snd-hda-codec-cmedia-objs := patch_cmedia.o
snd-hda-codec-analog-objs := patch_analog.o
snd-hda-codec-idt-objs := patch_sigmatel.o
snd-hda-codec-si3054-objs := patch_si3054.o
snd-hda-codec-cirrus-objs := patch_cirrus.o
snd-hda-codec-ca0110-objs := patch_ca0110.o
snd-hda-codec-ca0132-objs := patch_ca0132.o
snd-hda-codec-conexant-objs := patch_conexant.o
snd-hda-codec-via-objs := patch_via.o
snd-hda-codec-hdmi-objs := patch_hdmi.o hda_eld.o
# common driver
obj-$(CONFIG_SND_HDA) := snd-hda-codec.o
obj-$(CONFIG_SND_HDA) += snd-hda-controller.o
# 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

409
sound/pci/hda/ca0132_regs.h Normal file
View file

@ -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 __CA0132_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

View file

@ -0,0 +1,76 @@
/* Helper functions for Dell Mic Mute LED control;
* to be included from codec driver
*/
#if IS_ENABLED(CONFIG_LEDS_DELL_NETBOOKS)
#include <linux/dell-led.h>
static int dell_led_value;
static int (*dell_led_set_func)(int, int);
static void (*dell_old_cap_hook)(struct hda_codec *,
struct snd_kcontrol *,
struct snd_ctl_elem_value *);
static void update_dell_wmi_micmute_led(struct hda_codec *codec,
struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
if (dell_old_cap_hook)
dell_old_cap_hook(codec, kcontrol, ucontrol);
if (!ucontrol || !dell_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? */
int val = (ucontrol->value.integer.value[0] || ucontrol->value.integer.value[1]) ? 0 : 1;
if (val == dell_led_value)
return;
dell_led_value = val;
if (dell_led_set_func)
dell_led_set_func(DELL_LED_MICMUTE, dell_led_value);
}
}
static void alc_fixup_dell_wmi(struct hda_codec *codec,
const struct hda_fixup *fix, int action)
{
struct alc_spec *spec = codec->spec;
bool removefunc = false;
if (action == HDA_FIXUP_ACT_PROBE) {
if (!dell_led_set_func)
dell_led_set_func = symbol_request(dell_app_wmi_led_set);
if (!dell_led_set_func) {
codec_warn(codec, "Failed to find dell wmi symbol dell_app_wmi_led_set\n");
return;
}
removefunc = true;
if (dell_led_set_func(DELL_LED_MICMUTE, false) >= 0) {
dell_led_value = 0;
if (spec->gen.num_adc_nids > 1)
codec_dbg(codec, "Skipping micmute LED control due to several ADCs");
else {
dell_old_cap_hook = spec->gen.cap_sync_hook;
spec->gen.cap_sync_hook = update_dell_wmi_micmute_led;
removefunc = false;
}
}
}
if (dell_led_set_func && (action == HDA_FIXUP_ACT_FREE || removefunc)) {
symbol_put(dell_app_wmi_led_set);
dell_led_set_func = NULL;
dell_old_cap_hook = NULL;
}
}
#else /* CONFIG_LEDS_DELL_NETBOOKS */
static void alc_fixup_dell_wmi(struct hda_codec *codec,
const struct hda_fixup *fix, int action)
{
}
#endif /* CONFIG_LEDS_DELL_NETBOOKS */

View file

@ -0,0 +1,945 @@
/*
* BIOS auto-parser helper functions for HD-audio
*
* Copyright (c) 2012 Takashi Iwai <tiwai@suse.de>
*
* This driver is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*/
#include <linux/slab.h>
#include <linux/export.h>
#include <linux/sort.h>
#include <sound/core.h>
#include "hda_codec.h"
#include "hda_local.h"
#include "hda_auto_parser.h"
/*
* Helper for automatic pin configuration
*/
static int is_in_nid_list(hda_nid_t nid, const hda_nid_t *list)
{
for (; *list; list++)
if (*list == nid)
return 1;
return 0;
}
/* a pair of input pin and its sequence */
struct auto_out_pin {
hda_nid_t pin;
short seq;
};
static int compare_seq(const void *ap, const void *bp)
{
const struct auto_out_pin *a = ap;
const struct auto_out_pin *b = bp;
return (int)(a->seq - b->seq);
}
/*
* Sort an associated group of pins according to their sequence numbers.
* then store it to a pin array.
*/
static void sort_pins_by_sequence(hda_nid_t *pins, struct auto_out_pin *list,
int num_pins)
{
int i;
sort(list, num_pins, sizeof(list[0]), compare_seq, NULL);
for (i = 0; i < num_pins; i++)
pins[i] = list[i].pin;
}
/* add the found input-pin to the cfg->inputs[] table */
static void add_auto_cfg_input_pin(struct hda_codec *codec, struct auto_pin_cfg *cfg,
hda_nid_t nid, int type)
{
if (cfg->num_inputs < AUTO_CFG_MAX_INS) {
cfg->inputs[cfg->num_inputs].pin = nid;
cfg->inputs[cfg->num_inputs].type = type;
cfg->inputs[cfg->num_inputs].has_boost_on_pin =
nid_has_volume(codec, nid, HDA_INPUT);
cfg->num_inputs++;
}
}
static int compare_input_type(const void *ap, const void *bp)
{
const struct auto_pin_cfg_item *a = ap;
const struct auto_pin_cfg_item *b = bp;
if (a->type != b->type)
return (int)(a->type - b->type);
/* In case one has boost and the other one has not,
pick the one with boost first. */
return (int)(b->has_boost_on_pin - a->has_boost_on_pin);
}
/* Reorder the surround channels
* ALSA sequence is front/surr/clfe/side
* HDA sequence is:
* 4-ch: front/surr => OK as it is
* 6-ch: front/clfe/surr
* 8-ch: front/clfe/rear/side|fc
*/
static void reorder_outputs(unsigned int nums, hda_nid_t *pins)
{
hda_nid_t nid;
switch (nums) {
case 3:
case 4:
nid = pins[1];
pins[1] = pins[2];
pins[2] = nid;
break;
}
}
/* check whether the given pin has a proper pin I/O capability bit */
static bool check_pincap_validity(struct hda_codec *codec, hda_nid_t pin,
unsigned int dev)
{
unsigned int pincap = snd_hda_query_pin_caps(codec, pin);
/* some old hardware don't return the proper pincaps */
if (!pincap)
return true;
switch (dev) {
case AC_JACK_LINE_OUT:
case AC_JACK_SPEAKER:
case AC_JACK_HP_OUT:
case AC_JACK_SPDIF_OUT:
case AC_JACK_DIG_OTHER_OUT:
return !!(pincap & AC_PINCAP_OUT);
default:
return !!(pincap & AC_PINCAP_IN);
}
}
static bool can_be_headset_mic(struct hda_codec *codec,
struct auto_pin_cfg_item *item,
int seq_number)
{
int attr;
unsigned int def_conf;
if (item->type != AUTO_PIN_MIC)
return false;
if (item->is_headset_mic || item->is_headphone_mic)
return false; /* Already assigned */
def_conf = snd_hda_codec_get_pincfg(codec, item->pin);
attr = snd_hda_get_input_pin_attr(def_conf);
if (attr <= INPUT_PIN_ATTR_DOCK)
return false;
if (seq_number >= 0) {
int seq = get_defcfg_sequence(def_conf);
if (seq != seq_number)
return false;
}
return true;
}
/*
* Parse all pin widgets and store the useful pin nids to cfg
*
* The number of line-outs or any primary output is stored in line_outs,
* and the corresponding output pins are assigned to line_out_pins[],
* in the order of front, rear, CLFE, side, ...
*
* If more extra outputs (speaker and headphone) are found, the pins are
* assisnged to hp_pins[] and speaker_pins[], respectively. If no line-out jack
* is detected, one of speaker of HP pins is assigned as the primary
* output, i.e. to line_out_pins[0]. So, line_outs is always positive
* if any analog output exists.
*
* The analog input pins are assigned to inputs array.
* The digital input/output pins are assigned to dig_in_pin and dig_out_pin,
* respectively.
*/
int snd_hda_parse_pin_defcfg(struct hda_codec *codec,
struct auto_pin_cfg *cfg,
const hda_nid_t *ignore_nids,
unsigned int cond_flags)
{
hda_nid_t nid, end_nid;
short seq, assoc_line_out;
struct auto_out_pin line_out[ARRAY_SIZE(cfg->line_out_pins)];
struct auto_out_pin speaker_out[ARRAY_SIZE(cfg->speaker_pins)];
struct auto_out_pin hp_out[ARRAY_SIZE(cfg->hp_pins)];
int i;
if (!snd_hda_get_int_hint(codec, "parser_flags", &i))
cond_flags = i;
memset(cfg, 0, sizeof(*cfg));
memset(line_out, 0, sizeof(line_out));
memset(speaker_out, 0, sizeof(speaker_out));
memset(hp_out, 0, sizeof(hp_out));
assoc_line_out = 0;
end_nid = codec->start_nid + codec->num_nodes;
for (nid = codec->start_nid; nid < end_nid; nid++) {
unsigned int wid_caps = get_wcaps(codec, nid);
unsigned int wid_type = get_wcaps_type(wid_caps);
unsigned int def_conf;
short assoc, loc, conn, dev;
/* read all default configuration for pin complex */
if (wid_type != AC_WID_PIN)
continue;
/* ignore the given nids (e.g. pc-beep returns error) */
if (ignore_nids && is_in_nid_list(nid, ignore_nids))
continue;
def_conf = snd_hda_codec_get_pincfg(codec, nid);
conn = get_defcfg_connect(def_conf);
if (conn == AC_JACK_PORT_NONE)
continue;
loc = get_defcfg_location(def_conf);
dev = get_defcfg_device(def_conf);
/* workaround for buggy BIOS setups */
if (dev == AC_JACK_LINE_OUT) {
if (conn == AC_JACK_PORT_FIXED ||
conn == AC_JACK_PORT_BOTH)
dev = AC_JACK_SPEAKER;
}
if (!check_pincap_validity(codec, nid, dev))
continue;
switch (dev) {
case AC_JACK_LINE_OUT:
seq = get_defcfg_sequence(def_conf);
assoc = get_defcfg_association(def_conf);
if (!(wid_caps & AC_WCAP_STEREO))
if (!cfg->mono_out_pin)
cfg->mono_out_pin = nid;
if (!assoc)
continue;
if (!assoc_line_out)
assoc_line_out = assoc;
else if (assoc_line_out != assoc) {
codec_info(codec,
"ignore pin 0x%x with mismatching assoc# 0x%x vs 0x%x\n",
nid, assoc, assoc_line_out);
continue;
}
if (cfg->line_outs >= ARRAY_SIZE(cfg->line_out_pins)) {
codec_info(codec,
"ignore pin 0x%x, too many assigned pins\n",
nid);
continue;
}
line_out[cfg->line_outs].pin = nid;
line_out[cfg->line_outs].seq = seq;
cfg->line_outs++;
break;
case AC_JACK_SPEAKER:
seq = get_defcfg_sequence(def_conf);
assoc = get_defcfg_association(def_conf);
if (cfg->speaker_outs >= ARRAY_SIZE(cfg->speaker_pins)) {
codec_info(codec,
"ignore pin 0x%x, too many assigned pins\n",
nid);
continue;
}
speaker_out[cfg->speaker_outs].pin = nid;
speaker_out[cfg->speaker_outs].seq = (assoc << 4) | seq;
cfg->speaker_outs++;
break;
case AC_JACK_HP_OUT:
seq = get_defcfg_sequence(def_conf);
assoc = get_defcfg_association(def_conf);
if (cfg->hp_outs >= ARRAY_SIZE(cfg->hp_pins)) {
codec_info(codec,
"ignore pin 0x%x, too many assigned pins\n",
nid);
continue;
}
hp_out[cfg->hp_outs].pin = nid;
hp_out[cfg->hp_outs].seq = (assoc << 4) | seq;
cfg->hp_outs++;
break;
case AC_JACK_MIC_IN:
add_auto_cfg_input_pin(codec, cfg, nid, AUTO_PIN_MIC);
break;
case AC_JACK_LINE_IN:
add_auto_cfg_input_pin(codec, cfg, nid, AUTO_PIN_LINE_IN);
break;
case AC_JACK_CD:
add_auto_cfg_input_pin(codec, cfg, nid, AUTO_PIN_CD);
break;
case AC_JACK_AUX:
add_auto_cfg_input_pin(codec, cfg, nid, AUTO_PIN_AUX);
break;
case AC_JACK_SPDIF_OUT:
case AC_JACK_DIG_OTHER_OUT:
if (cfg->dig_outs >= ARRAY_SIZE(cfg->dig_out_pins)) {
codec_info(codec,
"ignore pin 0x%x, too many assigned pins\n",
nid);
continue;
}
cfg->dig_out_pins[cfg->dig_outs] = nid;
cfg->dig_out_type[cfg->dig_outs] =
(loc == AC_JACK_LOC_HDMI) ?
HDA_PCM_TYPE_HDMI : HDA_PCM_TYPE_SPDIF;
cfg->dig_outs++;
break;
case AC_JACK_SPDIF_IN:
case AC_JACK_DIG_OTHER_IN:
cfg->dig_in_pin = nid;
if (loc == AC_JACK_LOC_HDMI)
cfg->dig_in_type = HDA_PCM_TYPE_HDMI;
else
cfg->dig_in_type = HDA_PCM_TYPE_SPDIF;
break;
}
}
/* Find a pin that could be a headset or headphone mic */
if (cond_flags & HDA_PINCFG_HEADSET_MIC || cond_flags & HDA_PINCFG_HEADPHONE_MIC) {
bool hsmic = !!(cond_flags & HDA_PINCFG_HEADSET_MIC);
bool hpmic = !!(cond_flags & HDA_PINCFG_HEADPHONE_MIC);
for (i = 0; (hsmic || hpmic) && (i < cfg->num_inputs); i++)
if (hsmic && can_be_headset_mic(codec, &cfg->inputs[i], 0xc)) {
cfg->inputs[i].is_headset_mic = 1;
hsmic = false;
} else if (hpmic && can_be_headset_mic(codec, &cfg->inputs[i], 0xd)) {
cfg->inputs[i].is_headphone_mic = 1;
hpmic = false;
}
/* If we didn't find our sequence number mark, fall back to any sequence number */
for (i = 0; (hsmic || hpmic) && (i < cfg->num_inputs); i++) {
if (!can_be_headset_mic(codec, &cfg->inputs[i], -1))
continue;
if (hsmic) {
cfg->inputs[i].is_headset_mic = 1;
hsmic = false;
} else if (hpmic) {
cfg->inputs[i].is_headphone_mic = 1;
hpmic = false;
}
}
if (hsmic)
codec_dbg(codec, "Told to look for a headset mic, but didn't find any.\n");
if (hpmic)
codec_dbg(codec, "Told to look for a headphone mic, but didn't find any.\n");
}
/* FIX-UP:
* If no line-out is defined but multiple HPs are found,
* some of them might be the real line-outs.
*/
if (!cfg->line_outs && cfg->hp_outs > 1 &&
!(cond_flags & HDA_PINCFG_NO_HP_FIXUP)) {
int i = 0;
while (i < cfg->hp_outs) {
/* The real HPs should have the sequence 0x0f */
if ((hp_out[i].seq & 0x0f) == 0x0f) {
i++;
continue;
}
/* Move it to the line-out table */
line_out[cfg->line_outs++] = hp_out[i];
cfg->hp_outs--;
memmove(hp_out + i, hp_out + i + 1,
sizeof(hp_out[0]) * (cfg->hp_outs - i));
}
memset(hp_out + cfg->hp_outs, 0,
sizeof(hp_out[0]) * (AUTO_CFG_MAX_OUTS - cfg->hp_outs));
if (!cfg->hp_outs)
cfg->line_out_type = AUTO_PIN_HP_OUT;
}
/* sort by sequence */
sort_pins_by_sequence(cfg->line_out_pins, line_out, cfg->line_outs);
sort_pins_by_sequence(cfg->speaker_pins, speaker_out,
cfg->speaker_outs);
sort_pins_by_sequence(cfg->hp_pins, hp_out, cfg->hp_outs);
/*
* FIX-UP: if no line-outs are detected, try to use speaker or HP pin
* as a primary output
*/
if (!cfg->line_outs &&
!(cond_flags & HDA_PINCFG_NO_LO_FIXUP)) {
if (cfg->speaker_outs) {
cfg->line_outs = cfg->speaker_outs;
memcpy(cfg->line_out_pins, cfg->speaker_pins,
sizeof(cfg->speaker_pins));
cfg->speaker_outs = 0;
memset(cfg->speaker_pins, 0, sizeof(cfg->speaker_pins));
cfg->line_out_type = AUTO_PIN_SPEAKER_OUT;
} else if (cfg->hp_outs) {
cfg->line_outs = cfg->hp_outs;
memcpy(cfg->line_out_pins, cfg->hp_pins,
sizeof(cfg->hp_pins));
cfg->hp_outs = 0;
memset(cfg->hp_pins, 0, sizeof(cfg->hp_pins));
cfg->line_out_type = AUTO_PIN_HP_OUT;
}
}
reorder_outputs(cfg->line_outs, cfg->line_out_pins);
reorder_outputs(cfg->hp_outs, cfg->hp_pins);
reorder_outputs(cfg->speaker_outs, cfg->speaker_pins);
/* sort inputs in the order of AUTO_PIN_* type */
sort(cfg->inputs, cfg->num_inputs, sizeof(cfg->inputs[0]),
compare_input_type, NULL);
/*
* debug prints of the parsed results
*/
codec_info(codec, "autoconfig: line_outs=%d (0x%x/0x%x/0x%x/0x%x/0x%x) type:%s\n",
cfg->line_outs, cfg->line_out_pins[0], cfg->line_out_pins[1],
cfg->line_out_pins[2], cfg->line_out_pins[3],
cfg->line_out_pins[4],
cfg->line_out_type == AUTO_PIN_HP_OUT ? "hp" :
(cfg->line_out_type == AUTO_PIN_SPEAKER_OUT ?
"speaker" : "line"));
codec_info(codec, " speaker_outs=%d (0x%x/0x%x/0x%x/0x%x/0x%x)\n",
cfg->speaker_outs, cfg->speaker_pins[0],
cfg->speaker_pins[1], cfg->speaker_pins[2],
cfg->speaker_pins[3], cfg->speaker_pins[4]);
codec_info(codec, " hp_outs=%d (0x%x/0x%x/0x%x/0x%x/0x%x)\n",
cfg->hp_outs, cfg->hp_pins[0],
cfg->hp_pins[1], cfg->hp_pins[2],
cfg->hp_pins[3], cfg->hp_pins[4]);
codec_info(codec, " mono: mono_out=0x%x\n", cfg->mono_out_pin);
if (cfg->dig_outs)
codec_info(codec, " dig-out=0x%x/0x%x\n",
cfg->dig_out_pins[0], cfg->dig_out_pins[1]);
codec_info(codec, " inputs:\n");
for (i = 0; i < cfg->num_inputs; i++) {
codec_info(codec, " %s=0x%x\n",
hda_get_autocfg_input_label(codec, cfg, i),
cfg->inputs[i].pin);
}
if (cfg->dig_in_pin)
codec_info(codec, " dig-in=0x%x\n", cfg->dig_in_pin);
return 0;
}
EXPORT_SYMBOL_GPL(snd_hda_parse_pin_defcfg);
int snd_hda_get_input_pin_attr(unsigned int def_conf)
{
unsigned int loc = get_defcfg_location(def_conf);
unsigned int conn = get_defcfg_connect(def_conf);
if (conn == AC_JACK_PORT_NONE)
return INPUT_PIN_ATTR_UNUSED;
/* Windows may claim the internal mic to be BOTH, too */
if (conn == AC_JACK_PORT_FIXED || conn == AC_JACK_PORT_BOTH)
return INPUT_PIN_ATTR_INT;
if ((loc & 0x30) == AC_JACK_LOC_INTERNAL)
return INPUT_PIN_ATTR_INT;
if ((loc & 0x30) == AC_JACK_LOC_SEPARATE)
return INPUT_PIN_ATTR_DOCK;
if (loc == AC_JACK_LOC_REAR)
return INPUT_PIN_ATTR_REAR;
if (loc == AC_JACK_LOC_FRONT)
return INPUT_PIN_ATTR_FRONT;
return INPUT_PIN_ATTR_NORMAL;
}
EXPORT_SYMBOL_GPL(snd_hda_get_input_pin_attr);
/**
* hda_get_input_pin_label - Give a label for the given input pin
*
* When check_location is true, the function checks the pin location
* for mic and line-in pins, and set an appropriate prefix like "Front",
* "Rear", "Internal".
*/
static const char *hda_get_input_pin_label(struct hda_codec *codec,
const struct auto_pin_cfg_item *item,
hda_nid_t pin, bool check_location)
{
unsigned int def_conf;
static const char * const mic_names[] = {
"Internal Mic", "Dock Mic", "Mic", "Rear Mic", "Front Mic"
};
int attr;
def_conf = snd_hda_codec_get_pincfg(codec, pin);
switch (get_defcfg_device(def_conf)) {
case AC_JACK_MIC_IN:
if (item && item->is_headset_mic)
return "Headset Mic";
if (item && item->is_headphone_mic)
return "Headphone Mic";
if (!check_location)
return "Mic";
attr = snd_hda_get_input_pin_attr(def_conf);
if (!attr)
return "None";
return mic_names[attr - 1];
case AC_JACK_LINE_IN:
if (!check_location)
return "Line";
attr = snd_hda_get_input_pin_attr(def_conf);
if (!attr)
return "None";
if (attr == INPUT_PIN_ATTR_DOCK)
return "Dock Line";
return "Line";
case AC_JACK_AUX:
return "Aux";
case AC_JACK_CD:
return "CD";
case AC_JACK_SPDIF_IN:
return "SPDIF In";
case AC_JACK_DIG_OTHER_IN:
return "Digital In";
case AC_JACK_HP_OUT:
return "Headphone Mic";
default:
return "Misc";
}
}
/* Check whether the location prefix needs to be added to the label.
* If all mic-jacks are in the same location (e.g. rear panel), we don't
* have to put "Front" prefix to each label. In such a case, returns false.
*/
static int check_mic_location_need(struct hda_codec *codec,
const struct auto_pin_cfg *cfg,
int input)
{
unsigned int defc;
int i, attr, attr2;
defc = snd_hda_codec_get_pincfg(codec, cfg->inputs[input].pin);
attr = snd_hda_get_input_pin_attr(defc);
/* for internal or docking mics, we need locations */
if (attr <= INPUT_PIN_ATTR_NORMAL)
return 1;
attr = 0;
for (i = 0; i < cfg->num_inputs; i++) {
defc = snd_hda_codec_get_pincfg(codec, cfg->inputs[i].pin);
attr2 = snd_hda_get_input_pin_attr(defc);
if (attr2 >= INPUT_PIN_ATTR_NORMAL) {
if (attr && attr != attr2)
return 1; /* different locations found */
attr = attr2;
}
}
return 0;
}
/**
* hda_get_autocfg_input_label - Get a label for the given input
*
* Get a label for the given input pin defined by the autocfg item.
* Unlike hda_get_input_pin_label(), this function checks all inputs
* defined in autocfg and avoids the redundant mic/line prefix as much as
* possible.
*/
const char *hda_get_autocfg_input_label(struct hda_codec *codec,
const struct auto_pin_cfg *cfg,
int input)
{
int type = cfg->inputs[input].type;
int has_multiple_pins = 0;
if ((input > 0 && cfg->inputs[input - 1].type == type) ||
(input < cfg->num_inputs - 1 && cfg->inputs[input + 1].type == type))
has_multiple_pins = 1;
if (has_multiple_pins && type == AUTO_PIN_MIC)
has_multiple_pins &= check_mic_location_need(codec, cfg, input);
return hda_get_input_pin_label(codec, &cfg->inputs[input],
cfg->inputs[input].pin,
has_multiple_pins);
}
EXPORT_SYMBOL_GPL(hda_get_autocfg_input_label);
/* return the position of NID in the list, or -1 if not found */
static int find_idx_in_nid_list(hda_nid_t nid, const hda_nid_t *list, int nums)
{
int i;
for (i = 0; i < nums; i++)
if (list[i] == nid)
return i;
return -1;
}
/* get a unique suffix or an index number */
static const char *check_output_sfx(hda_nid_t nid, const hda_nid_t *pins,
int num_pins, int *indexp)
{
static const char * const channel_sfx[] = {
" Front", " Surround", " CLFE", " Side"
};
int i;
i = find_idx_in_nid_list(nid, pins, num_pins);
if (i < 0)
return NULL;
if (num_pins == 1)
return "";
if (num_pins > ARRAY_SIZE(channel_sfx)) {
if (indexp)
*indexp = i;
return "";
}
return channel_sfx[i];
}
static const char *check_output_pfx(struct hda_codec *codec, hda_nid_t nid)
{
unsigned int def_conf = snd_hda_codec_get_pincfg(codec, nid);
int attr = snd_hda_get_input_pin_attr(def_conf);
/* check the location */
switch (attr) {
case INPUT_PIN_ATTR_DOCK:
return "Dock ";
case INPUT_PIN_ATTR_FRONT:
return "Front ";
}
return "";
}
static int get_hp_label_index(struct hda_codec *codec, hda_nid_t nid,
const hda_nid_t *pins, int num_pins)
{
int i, j, idx = 0;
const char *pfx = check_output_pfx(codec, nid);
i = find_idx_in_nid_list(nid, pins, num_pins);
if (i < 0)
return -1;
for (j = 0; j < i; j++)
if (pfx == check_output_pfx(codec, pins[j]))
idx++;
return idx;
}
static int fill_audio_out_name(struct hda_codec *codec, hda_nid_t nid,
const struct auto_pin_cfg *cfg,
const char *name, char *label, int maxlen,
int *indexp)
{
unsigned int def_conf = snd_hda_codec_get_pincfg(codec, nid);
int attr = snd_hda_get_input_pin_attr(def_conf);
const char *pfx, *sfx = "";
/* handle as a speaker if it's a fixed line-out */
if (!strcmp(name, "Line Out") && attr == INPUT_PIN_ATTR_INT)
name = "Speaker";
pfx = check_output_pfx(codec, nid);
if (cfg) {
/* try to give a unique suffix if needed */
sfx = check_output_sfx(nid, cfg->line_out_pins, cfg->line_outs,
indexp);
if (!sfx)
sfx = check_output_sfx(nid, cfg->speaker_pins, cfg->speaker_outs,
indexp);
if (!sfx) {
/* don't add channel suffix for Headphone controls */
int idx = get_hp_label_index(codec, nid, cfg->hp_pins,
cfg->hp_outs);
if (idx >= 0 && indexp)
*indexp = idx;
sfx = "";
}
}
snprintf(label, maxlen, "%s%s%s", pfx, name, sfx);
return 1;
}
#define is_hdmi_cfg(conf) \
(get_defcfg_location(conf) == AC_JACK_LOC_HDMI)
/**
* snd_hda_get_pin_label - Get a label for the given I/O pin
*
* Get a label for the given pin. This function works for both input and
* output pins. When @cfg is given as non-NULL, the function tries to get
* an optimized label using hda_get_autocfg_input_label().
*
* This function tries to give a unique label string for the pin as much as
* possible. For example, when the multiple line-outs are present, it adds
* the channel suffix like "Front", "Surround", etc (only when @cfg is given).
* If no unique name with a suffix is available and @indexp is non-NULL, the
* index number is stored in the pointer.
*/
int snd_hda_get_pin_label(struct hda_codec *codec, hda_nid_t nid,
const struct auto_pin_cfg *cfg,
char *label, int maxlen, int *indexp)
{
unsigned int def_conf = snd_hda_codec_get_pincfg(codec, nid);
const char *name = NULL;
int i;
bool hdmi;
if (indexp)
*indexp = 0;
if (get_defcfg_connect(def_conf) == AC_JACK_PORT_NONE)
return 0;
switch (get_defcfg_device(def_conf)) {
case AC_JACK_LINE_OUT:
return fill_audio_out_name(codec, nid, cfg, "Line Out",
label, maxlen, indexp);
case AC_JACK_SPEAKER:
return fill_audio_out_name(codec, nid, cfg, "Speaker",
label, maxlen, indexp);
case AC_JACK_HP_OUT:
return fill_audio_out_name(codec, nid, cfg, "Headphone",
label, maxlen, indexp);
case AC_JACK_SPDIF_OUT:
case AC_JACK_DIG_OTHER_OUT:
hdmi = is_hdmi_cfg(def_conf);
name = hdmi ? "HDMI" : "SPDIF";
if (cfg && indexp)
for (i = 0; i < cfg->dig_outs; i++) {
hda_nid_t pin = cfg->dig_out_pins[i];
unsigned int c;
if (pin == nid)
break;
c = snd_hda_codec_get_pincfg(codec, pin);
if (hdmi == is_hdmi_cfg(c))
(*indexp)++;
}
break;
default:
if (cfg) {
for (i = 0; i < cfg->num_inputs; i++) {
if (cfg->inputs[i].pin != nid)
continue;
name = hda_get_autocfg_input_label(codec, cfg, i);
if (name)
break;
}
}
if (!name)
name = hda_get_input_pin_label(codec, NULL, nid, true);
break;
}
if (!name)
return 0;
strlcpy(label, name, maxlen);
return 1;
}
EXPORT_SYMBOL_GPL(snd_hda_get_pin_label);
int snd_hda_add_verbs(struct hda_codec *codec,
const struct hda_verb *list)
{
const struct hda_verb **v;
v = snd_array_new(&codec->verbs);
if (!v)
return -ENOMEM;
*v = list;
return 0;
}
EXPORT_SYMBOL_GPL(snd_hda_add_verbs);
void snd_hda_apply_verbs(struct hda_codec *codec)
{
int i;
for (i = 0; i < codec->verbs.used; i++) {
struct hda_verb **v = snd_array_elem(&codec->verbs, i);
snd_hda_sequence_write(codec, *v);
}
}
EXPORT_SYMBOL_GPL(snd_hda_apply_verbs);
void snd_hda_apply_pincfgs(struct hda_codec *codec,
const struct hda_pintbl *cfg)
{
for (; cfg->nid; cfg++)
snd_hda_codec_set_pincfg(codec, cfg->nid, cfg->val);
}
EXPORT_SYMBOL_GPL(snd_hda_apply_pincfgs);
static void set_pin_targets(struct hda_codec *codec,
const struct hda_pintbl *cfg)
{
for (; cfg->nid; cfg++)
snd_hda_set_pin_ctl_cache(codec, cfg->nid, cfg->val);
}
static void apply_fixup(struct hda_codec *codec, int id, int action, int depth)
{
const char *modelname = codec->fixup_name;
while (id >= 0) {
const struct hda_fixup *fix = codec->fixup_list + id;
if (fix->chained_before)
apply_fixup(codec, fix->chain_id, action, depth + 1);
switch (fix->type) {
case HDA_FIXUP_PINS:
if (action != HDA_FIXUP_ACT_PRE_PROBE || !fix->v.pins)
break;
codec_dbg(codec, "%s: Apply pincfg for %s\n",
codec->chip_name, modelname);
snd_hda_apply_pincfgs(codec, fix->v.pins);
break;
case HDA_FIXUP_VERBS:
if (action != HDA_FIXUP_ACT_PROBE || !fix->v.verbs)
break;
codec_dbg(codec, "%s: Apply fix-verbs for %s\n",
codec->chip_name, modelname);
snd_hda_add_verbs(codec, fix->v.verbs);
break;
case HDA_FIXUP_FUNC:
if (!fix->v.func)
break;
codec_dbg(codec, "%s: Apply fix-func for %s\n",
codec->chip_name, modelname);
fix->v.func(codec, fix, action);
break;
case HDA_FIXUP_PINCTLS:
if (action != HDA_FIXUP_ACT_PROBE || !fix->v.pins)
break;
codec_dbg(codec, "%s: Apply pinctl for %s\n",
codec->chip_name, modelname);
set_pin_targets(codec, fix->v.pins);
break;
default:
codec_err(codec, "%s: Invalid fixup type %d\n",
codec->chip_name, fix->type);
break;
}
if (!fix->chained || fix->chained_before)
break;
if (++depth > 10)
break;
id = fix->chain_id;
}
}
void snd_hda_apply_fixup(struct hda_codec *codec, int action)
{
if (codec->fixup_list)
apply_fixup(codec, codec->fixup_id, action, 0);
}
EXPORT_SYMBOL_GPL(snd_hda_apply_fixup);
static bool pin_config_match(struct hda_codec *codec,
const struct hda_pintbl *pins)
{
for (; pins->nid; pins++) {
u32 def_conf = snd_hda_codec_get_pincfg(codec, pins->nid);
if (pins->val != def_conf)
return false;
}
return true;
}
void snd_hda_pick_pin_fixup(struct hda_codec *codec,
const struct snd_hda_pin_quirk *pin_quirk,
const struct hda_fixup *fixlist)
{
const struct snd_hda_pin_quirk *pq;
if (codec->fixup_id != HDA_FIXUP_ID_NOT_SET)
return;
for (pq = pin_quirk; pq->subvendor; pq++) {
if ((codec->subsystem_id & 0xffff0000) != (pq->subvendor << 16))
continue;
if (codec->vendor_id != pq->codec)
continue;
if (pin_config_match(codec, pq->pins)) {
codec->fixup_id = pq->value;
#ifdef CONFIG_SND_DEBUG_VERBOSE
codec->fixup_name = pq->name;
#endif
codec->fixup_list = fixlist;
return;
}
}
}
EXPORT_SYMBOL_GPL(snd_hda_pick_pin_fixup);
void snd_hda_pick_fixup(struct hda_codec *codec,
const struct hda_model_fixup *models,
const struct snd_pci_quirk *quirk,
const struct hda_fixup *fixlist)
{
const struct snd_pci_quirk *q;
int id = HDA_FIXUP_ID_NOT_SET;
const char *name = NULL;
if (codec->fixup_id != HDA_FIXUP_ID_NOT_SET)
return;
/* when model=nofixup is given, don't pick up any fixups */
if (codec->modelname && !strcmp(codec->modelname, "nofixup")) {
codec->fixup_list = NULL;
codec->fixup_name = NULL;
codec->fixup_id = HDA_FIXUP_ID_NO_FIXUP;
return;
}
if (codec->modelname && models) {
while (models->name) {
if (!strcmp(codec->modelname, models->name)) {
codec->fixup_id = models->id;
codec->fixup_name = models->name;
codec->fixup_list = fixlist;
return;
}
models++;
}
}
if (quirk) {
q = snd_pci_quirk_lookup(codec->bus->pci, quirk);
if (q) {
id = q->value;
#ifdef CONFIG_SND_DEBUG_VERBOSE
name = q->name;
#endif
}
}
if (id < 0 && quirk) {
for (q = quirk; q->subvendor || q->subdevice; q++) {
unsigned int vendorid =
q->subdevice | (q->subvendor << 16);
unsigned int mask = 0xffff0000 | q->subdevice_mask;
if ((codec->subsystem_id & mask) == (vendorid & mask)) {
id = q->value;
#ifdef CONFIG_SND_DEBUG_VERBOSE
name = q->name;
#endif
break;
}
}
}
codec->fixup_id = id;
if (id >= 0) {
codec->fixup_list = fixlist;
codec->fixup_name = name;
}
}
EXPORT_SYMBOL_GPL(snd_hda_pick_fixup);

View file

@ -0,0 +1,119 @@
/*
* BIOS auto-parser helper functions for HD-audio
*
* Copyright (c) 2012 Takashi Iwai <tiwai@suse.de>
*
* This driver is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*/
#ifndef __SOUND_HDA_AUTO_PARSER_H
#define __SOUND_HDA_AUTO_PARSER_H
/*
* Helper for automatic pin configuration
*/
enum {
AUTO_PIN_MIC,
AUTO_PIN_LINE_IN,
AUTO_PIN_CD,
AUTO_PIN_AUX,
AUTO_PIN_LAST
};
enum {
AUTO_PIN_LINE_OUT,
AUTO_PIN_SPEAKER_OUT,
AUTO_PIN_HP_OUT
};
#define AUTO_CFG_MAX_OUTS HDA_MAX_OUTS
#define AUTO_CFG_MAX_INS 8
struct auto_pin_cfg_item {
hda_nid_t pin;
int type;
unsigned int is_headset_mic:1;
unsigned int is_headphone_mic:1; /* Mic-only in headphone jack */
unsigned int has_boost_on_pin:1;
};
struct auto_pin_cfg;
const char *hda_get_autocfg_input_label(struct hda_codec *codec,
const struct auto_pin_cfg *cfg,
int input);
int snd_hda_get_pin_label(struct hda_codec *codec, hda_nid_t nid,
const struct auto_pin_cfg *cfg,
char *label, int maxlen, int *indexp);
enum {
INPUT_PIN_ATTR_UNUSED, /* pin not connected */
INPUT_PIN_ATTR_INT, /* internal mic/line-in */
INPUT_PIN_ATTR_DOCK, /* docking mic/line-in */
INPUT_PIN_ATTR_NORMAL, /* mic/line-in jack */
INPUT_PIN_ATTR_REAR, /* mic/line-in jack in rear */
INPUT_PIN_ATTR_FRONT, /* mic/line-in jack in front */
INPUT_PIN_ATTR_LAST = INPUT_PIN_ATTR_FRONT,
};
int snd_hda_get_input_pin_attr(unsigned int def_conf);
struct auto_pin_cfg {
int line_outs;
/* sorted in the order of Front/Surr/CLFE/Side */
hda_nid_t line_out_pins[AUTO_CFG_MAX_OUTS];
int speaker_outs;
hda_nid_t speaker_pins[AUTO_CFG_MAX_OUTS];
int hp_outs;
int line_out_type; /* AUTO_PIN_XXX_OUT */
hda_nid_t hp_pins[AUTO_CFG_MAX_OUTS];
int num_inputs;
struct auto_pin_cfg_item inputs[AUTO_CFG_MAX_INS];
int dig_outs;
hda_nid_t dig_out_pins[2];
hda_nid_t dig_in_pin;
hda_nid_t mono_out_pin;
int dig_out_type[2]; /* HDA_PCM_TYPE_XXX */
int dig_in_type; /* HDA_PCM_TYPE_XXX */
};
/* bit-flags for snd_hda_parse_pin_def_config() behavior */
#define HDA_PINCFG_NO_HP_FIXUP (1 << 0) /* no HP-split */
#define HDA_PINCFG_NO_LO_FIXUP (1 << 1) /* don't take other outs as LO */
#define HDA_PINCFG_HEADSET_MIC (1 << 2) /* Try to find headset mic; mark seq number as 0xc to trigger */
#define HDA_PINCFG_HEADPHONE_MIC (1 << 3) /* Try to find headphone mic; mark seq number as 0xd to trigger */
int snd_hda_parse_pin_defcfg(struct hda_codec *codec,
struct auto_pin_cfg *cfg,
const hda_nid_t *ignore_nids,
unsigned int cond_flags);
/* older function */
#define snd_hda_parse_pin_def_config(codec, cfg, ignore) \
snd_hda_parse_pin_defcfg(codec, cfg, ignore, 0)
static inline int auto_cfg_hp_outs(const struct auto_pin_cfg *cfg)
{
return (cfg->line_out_type == AUTO_PIN_HP_OUT) ?
cfg->line_outs : cfg->hp_outs;
}
static inline const hda_nid_t *auto_cfg_hp_pins(const struct auto_pin_cfg *cfg)
{
return (cfg->line_out_type == AUTO_PIN_HP_OUT) ?
cfg->line_out_pins : cfg->hp_pins;
}
static inline int auto_cfg_speaker_outs(const struct auto_pin_cfg *cfg)
{
return (cfg->line_out_type == AUTO_PIN_SPEAKER_OUT) ?
cfg->line_outs : cfg->speaker_outs;
}
static inline const hda_nid_t *auto_cfg_speaker_pins(const struct auto_pin_cfg *cfg)
{
return (cfg->line_out_type == AUTO_PIN_SPEAKER_OUT) ?
cfg->line_out_pins : cfg->speaker_pins;
}
#endif /* __SOUND_HDA_AUTO_PARSER_H */

307
sound/pci/hda/hda_beep.c Normal file
View file

@ -0,0 +1,307 @@
/*
* Digital Beep Input Interface for HD-audio codec
*
* Author: Matthew Ranostay <mranostay@embeddedalley.com>
* Copyright (c) 2008 Embedded Alley Solutions Inc
*
* This driver is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This driver is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
#include <linux/input.h>
#include <linux/slab.h>
#include <linux/workqueue.h>
#include <linux/export.h>
#include <sound/core.h>
#include "hda_beep.h"
#include "hda_local.h"
enum {
DIGBEEP_HZ_STEP = 46875, /* 46.875 Hz */
DIGBEEP_HZ_MIN = 93750, /* 93.750 Hz */
DIGBEEP_HZ_MAX = 12000000, /* 12 KHz */
};
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, tone);
if (!tone && beep->playing) {
beep->playing = 0;
snd_hda_power_down(codec);
}
}
/* (non-standard) Linear beep tone calculation for IDT/STAC codecs
*
* The tone frequency of beep generator on IDT/STAC codecs is
* defined from the 8bit tone parameter, in Hz,
* freq = 48000 * (257 - tone) / 1024
* that is from 12kHz to 93.75Hz in steps of 46.875 Hz
*/
static int beep_linear_tone(struct hda_beep *beep, int hz)
{
if (hz <= 0)
return 0;
hz *= 1000; /* fixed point */
hz = hz - DIGBEEP_HZ_MIN
+ DIGBEEP_HZ_STEP / 2; /* round to nearest step */
if (hz < 0)
hz = 0; /* turn off PC beep*/
else if (hz >= (DIGBEEP_HZ_MAX - DIGBEEP_HZ_MIN))
hz = 1; /* max frequency */
else {
hz /= DIGBEEP_HZ_STEP;
hz = 255 - hz;
}
return hz;
}
/* HD-audio standard beep tone parameter calculation
*
* The tone frequency in Hz is calculated as
* freq = 48000 / (tone * 4)
* from 47Hz to 12kHz
*/
static int beep_standard_tone(struct hda_beep *beep, int hz)
{
if (hz <= 0)
return 0; /* disabled */
hz = 12000 / hz;
if (hz > 0xff)
return 0xff;
if (hz <= 0)
return 1;
return hz;
}
static int snd_hda_beep_event(struct input_dev *dev, unsigned int type,
unsigned int code, int hz)
{
struct hda_beep *beep = input_get_drvdata(dev);
switch (code) {
case SND_BELL:
if (hz)
hz = 1000;
/* fallthru */
case SND_TONE:
if (beep->linear_tone)
beep->tone = beep_linear_tone(beep, hz);
else
beep->tone = beep_standard_tone(beep, hz);
break;
default:
return -1;
}
/* schedule beep event */
schedule_work(&beep->beep_work);
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)
{
if (beep->registered)
input_unregister_device(beep->dev);
else
input_free_device(beep->dev);
beep->dev = NULL;
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;
input_dev = input_allocate_device();
if (!input_dev)
return -ENOMEM;
/* setup digital beep device */
input_dev->name = "HDA Digital PCBeep";
input_dev->phys = beep->phys;
input_dev->id.bustype = BUS_PCI;
input_dev->id.vendor = codec->vendor_id >> 16;
input_dev->id.product = codec->vendor_id & 0xffff;
input_dev->id.version = 0x01;
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->dev;
input_set_drvdata(input_dev, beep);
beep->dev = input_dev;
return 0;
}
int snd_hda_enable_beep_device(struct hda_codec *codec, int enable)
{
struct hda_beep *beep = codec->beep;
if (!beep)
return 0;
enable = !!enable;
if (beep->enabled != enable) {
beep->enabled = enable;
if (!enable)
turn_off_beep(beep);
return 1;
}
return 0;
}
EXPORT_SYMBOL_GPL(snd_hda_enable_beep_device);
int snd_hda_attach_beep_device(struct hda_codec *codec, int nid)
{
struct hda_beep *beep;
int err;
if (!snd_hda_get_bool_hint(codec, "beep"))
return 0; /* disabled explicitly by hints */
if (codec->beep_mode == HDA_BEEP_MODE_OFF)
return 0; /* disabled by module option */
beep = kzalloc(sizeof(*beep), GFP_KERNEL);
if (beep == NULL)
return -ENOMEM;
snprintf(beep->phys, sizeof(beep->phys),
"card%d/codec#%d/beep0", codec->bus->card->number, codec->addr);
/* enable linear scale */
snd_hda_codec_write_cache(codec, nid, 0,
AC_VERB_SET_DIGI_CONVERT_2, 0x01);
beep->nid = nid;
beep->codec = codec;
codec->beep = beep;
INIT_WORK(&beep->beep_work, &snd_hda_generate_beep);
mutex_init(&beep->mutex);
err = snd_hda_do_attach(beep);
if (err < 0) {
kfree(beep);
codec->beep = NULL;
return err;
}
return 0;
}
EXPORT_SYMBOL_GPL(snd_hda_attach_beep_device);
void snd_hda_detach_beep_device(struct hda_codec *codec)
{
struct hda_beep *beep = codec->beep;
if (beep) {
if (beep->dev)
snd_hda_do_detach(beep);
codec->beep = NULL;
kfree(beep);
}
}
EXPORT_SYMBOL_GPL(snd_hda_detach_beep_device);
int snd_hda_register_beep_device(struct hda_codec *codec)
{
struct hda_beep *beep = codec->beep;
int err;
if (!beep || !beep->dev)
return 0;
err = input_register_device(beep->dev);
if (err < 0) {
codec_err(codec, "hda_beep: unable to register input device\n");
input_free_device(beep->dev);
codec->beep = NULL;
kfree(beep);
return err;
}
beep->registered = true;
return 0;
}
EXPORT_SYMBOL_GPL(snd_hda_register_beep_device);
static bool ctl_has_mute(struct snd_kcontrol *kcontrol)
{
struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
return query_amp_caps(codec, get_amp_nid(kcontrol),
get_amp_direction(kcontrol)) & AC_AMPCAP_MUTE;
}
/* get/put callbacks for beep mute mixer switches */
int snd_hda_mixer_amp_switch_get_beep(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
struct hda_beep *beep = codec->beep;
if (beep && (!beep->enabled || !ctl_has_mute(kcontrol))) {
ucontrol->value.integer.value[0] =
ucontrol->value.integer.value[1] = beep->enabled;
return 0;
}
return snd_hda_mixer_amp_switch_get(kcontrol, ucontrol);
}
EXPORT_SYMBOL_GPL(snd_hda_mixer_amp_switch_get_beep);
int snd_hda_mixer_amp_switch_put_beep(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
struct hda_beep *beep = codec->beep;
if (beep) {
u8 chs = get_amp_channels(kcontrol);
int enable = 0;
long *valp = ucontrol->value.integer.value;
if (chs & 1) {
enable |= *valp;
valp++;
}
if (chs & 2)
enable |= *valp;
snd_hda_enable_beep_device(codec, enable);
}
if (!ctl_has_mute(kcontrol))
return 0;
return snd_hda_mixer_amp_switch_put(kcontrol, ucontrol);
}
EXPORT_SYMBOL_GPL(snd_hda_mixer_amp_switch_put_beep);

63
sound/pci/hda/hda_beep.h Normal file
View file

@ -0,0 +1,63 @@
/*
* Digital Beep Input Interface for HD-audio codec
*
* Author: Matthew Ranostay <mranostay@embeddedalley.com>
* Copyright (c) 2008 Embedded Alley Solutions Inc
*
* 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 __SOUND_HDA_BEEP_H
#define __SOUND_HDA_BEEP_H
#include "hda_codec.h"
#define HDA_BEEP_MODE_OFF 0
#define HDA_BEEP_MODE_ON 1
/* beep information */
struct hda_beep {
struct input_dev *dev;
struct hda_codec *codec;
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;
};
#ifdef CONFIG_SND_HDA_INPUT_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)
{
return 0;
}
static inline void snd_hda_detach_beep_device(struct hda_codec *codec)
{
}
static inline int snd_hda_register_beep_device(struct hda_codec *codec)
{
return 0;
}
#endif
#endif

5760
sound/pci/hda/hda_codec.c Normal file

File diff suppressed because it is too large Load diff

690
sound/pci/hda/hda_codec.h Normal file
View file

@ -0,0 +1,690 @@
/*
* Universal Interface for Intel High Definition Audio Codec
*
* Copyright (c) 2004 Takashi Iwai <tiwai@suse.de>
*
* 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_CODEC_H
#define __SOUND_HDA_CODEC_H
#include <sound/info.h>
#include <sound/control.h>
#include <sound/pcm.h>
#include <sound/hwdep.h>
#include <sound/hda_verbs.h>
/*
* generic arrays
*/
struct snd_array {
unsigned int used;
unsigned int alloced;
unsigned int elem_size;
unsigned int alloc_align;
void *list;
};
void *snd_array_new(struct snd_array *array);
void snd_array_free(struct snd_array *array);
static inline void snd_array_init(struct snd_array *array, unsigned int size,
unsigned int align)
{
array->elem_size = size;
array->alloc_align = align;
}
static inline void *snd_array_elem(struct snd_array *array, unsigned int idx)
{
return array->list + idx * array->elem_size;
}
static inline unsigned int snd_array_index(struct snd_array *array, void *ptr)
{
return (unsigned long)(ptr - array->list) / array->elem_size;
}
/*
* Structures
*/
struct hda_bus;
struct hda_beep;
struct hda_codec;
struct hda_pcm;
struct hda_pcm_stream;
struct hda_bus_unsolicited;
/* NID type */
typedef u16 hda_nid_t;
/* bus operators */
struct hda_bus_ops {
/* send a single command */
int (*command)(struct hda_bus *bus, unsigned int cmd);
/* get a response from the last command */
unsigned int (*get_response)(struct hda_bus *bus, unsigned int addr);
/* free the private data */
void (*private_free)(struct hda_bus *);
/* attach a PCM stream */
int (*attach_pcm)(struct hda_bus *bus, struct hda_codec *codec,
struct hda_pcm *pcm);
/* reset bus for retry verb */
void (*bus_reset)(struct hda_bus *bus);
#ifdef CONFIG_PM
/* 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 */
struct hda_bus_template {
void *private_data;
struct pci_dev *pci;
const char *modelname;
int *power_save;
struct hda_bus_ops ops;
};
/*
* codec bus
*
* each controller needs to creata a hda_bus to assign the accessor.
* A hda_bus contains several codecs in the list codec_list.
*/
struct hda_bus {
struct snd_card *card;
/* copied from template */
void *private_data;
struct pci_dev *pci;
const char *modelname;
int *power_save;
struct hda_bus_ops ops;
/* 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];
struct mutex cmd_mutex;
struct mutex prepare_mutex;
/* unsolicited event queue */
struct hda_bus_unsolicited *unsol;
char workq_name[16];
struct workqueue_struct *workq; /* common workqueue for codecs */
/* assigned PCMs */
DECLARE_BITMAP(pcm_dev_bits, SNDRV_PCM_DEVICES);
/* misc op flags */
unsigned int needs_damn_long_delay :1;
unsigned int allow_bus_reset:1; /* allow bus reset at fatal error */
unsigned int sync_write:1; /* sync after verb write */
/* status for codec/controller */
unsigned int shutdown :1; /* being unloaded */
unsigned int rirb_error:1; /* error in codec communication */
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 */
};
/*
* codec preset
*
* Known codecs have the patch to build and set up the controls/PCMs
* better than the generic parser.
*/
struct hda_codec_preset {
unsigned int id;
unsigned int mask;
unsigned int subs;
unsigned int subs_mask;
unsigned int rev;
hda_nid_t afg, mfg;
const char *name;
int (*patch)(struct hda_codec *codec);
};
struct hda_codec_preset_list {
const struct hda_codec_preset *preset;
struct module *owner;
struct list_head list;
};
/* initial hook */
int snd_hda_add_codec_preset(struct hda_codec_preset_list *preset);
int snd_hda_delete_codec_preset(struct hda_codec_preset_list *preset);
/* ops set by the preset patch */
struct hda_codec_ops {
int (*build_controls)(struct hda_codec *codec);
int (*build_pcms)(struct hda_codec *codec);
int (*init)(struct hda_codec *codec);
void (*free)(struct hda_codec *codec);
void (*unsol_event)(struct hda_codec *codec, unsigned int res);
void (*set_power_state)(struct hda_codec *codec, hda_nid_t fg,
unsigned int power_state);
#ifdef CONFIG_PM
int (*suspend)(struct hda_codec *codec);
int (*resume)(struct hda_codec *codec);
int (*check_power_status)(struct hda_codec *codec, hda_nid_t nid);
#endif
void (*reboot_notify)(struct hda_codec *codec);
};
/* record for amp information cache */
struct hda_cache_head {
u32 key:31; /* hash key */
u32 dirty:1;
u16 val; /* assigned value */
u16 next;
};
struct hda_amp_info {
struct hda_cache_head head;
u32 amp_caps; /* amp capabilities */
u16 vol[2]; /* current volume & mute */
};
struct hda_cache_rec {
u16 hash[64]; /* hash table for index */
struct snd_array buf; /* record entries */
};
/* PCM callbacks */
struct hda_pcm_ops {
int (*open)(struct hda_pcm_stream *info, struct hda_codec *codec,
struct snd_pcm_substream *substream);
int (*close)(struct hda_pcm_stream *info, struct hda_codec *codec,
struct snd_pcm_substream *substream);
int (*prepare)(struct hda_pcm_stream *info, struct hda_codec *codec,
unsigned int stream_tag, unsigned int format,
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 */
struct hda_pcm_stream {
unsigned int substreams; /* number of substreams, 0 = not exist*/
unsigned int channels_min; /* min. number of channels */
unsigned int channels_max; /* max. number of channels */
hda_nid_t nid; /* default NID to query rates/formats/bps, or set up */
u32 rates; /* supported rates */
u64 formats; /* supported formats (SNDRV_PCM_FMTBIT_) */
unsigned int maxbps; /* supported max. bit per sample */
const struct snd_pcm_chmap_elem *chmap; /* chmap to override */
struct hda_pcm_ops ops;
};
/* PCM types */
enum {
HDA_PCM_TYPE_AUDIO,
HDA_PCM_TYPE_SPDIF,
HDA_PCM_TYPE_HDMI,
HDA_PCM_TYPE_MODEM,
HDA_PCM_NTYPES
};
/* for PCM creation */
struct hda_pcm {
char *name;
struct hda_pcm_stream stream[2];
unsigned int pcm_type; /* HDA_PCM_TYPE_XXX */
int device; /* device number to assign */
struct snd_pcm *pcm; /* assigned PCM instance */
bool own_chmap; /* codec driver provides own channel maps */
};
/* codec information */
struct hda_codec {
struct device dev;
struct hda_bus *bus;
unsigned int addr; /* codec addr*/
struct list_head list; /* list point */
hda_nid_t afg; /* AFG node id */
hda_nid_t mfg; /* MFG node id */
/* ids */
u8 afg_function_id;
u8 mfg_function_id;
u8 afg_unsol;
u8 mfg_unsol;
u32 vendor_id;
u32 subsystem_id;
u32 revision_id;
/* 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 */
/* set by patch */
struct hda_codec_ops patch_ops;
/* PCM to create, set by patch_ops.build_pcms callback */
unsigned int num_pcms;
struct hda_pcm *pcm_info;
/* codec specific info */
void *spec;
/* beep device */
struct hda_beep *beep;
unsigned int beep_mode;
/* widget capabilities cache */
unsigned int num_nodes;
hda_nid_t start_nid;
u32 *wcaps;
struct snd_array mixers; /* list of assigned mixer elements */
struct snd_array nids; /* list of mapped mixer elements */
struct hda_cache_rec amp_cache; /* cache for amp access */
struct hda_cache_rec cmd_cache; /* cache for other commands */
struct list_head conn_list; /* linked-list of connection-list */
struct mutex spdif_mutex;
struct mutex control_mutex;
struct mutex hash_mutex;
struct snd_array spdif_out;
unsigned int spdif_in_enable; /* SPDIF input enable? */
const hda_nid_t *slave_dig_outs; /* optional digital out slave widgets */
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 */
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
* (e.g. Realtek codecs)
*/
unsigned int pin_amp_workaround:1; /* pin out-amp takes index
* (e.g. Conexant codecs)
*/
unsigned int single_adc_amp:1; /* adc in-amp takes no index
* (e.g. CX20549 codec)
*/
unsigned int no_sticky_stream:1; /* no sticky-PCM stream assignment */
unsigned int pins_shutup:1; /* pins are shut up */
unsigned int no_trigger_sense:1; /* don't trigger at pin-sensing */
unsigned int 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_up_notified:1; /* PM notified to controller */
unsigned int in_pm:1; /* suspend/resume being performed */
int power_transition; /* power-state in transition */
int power_count; /* current (global) power refcount */
struct delayed_work power_work; /* delayed task for powerdown */
unsigned long power_on_acct;
unsigned long power_off_acct;
unsigned long power_jiffies;
spinlock_t power_lock;
#endif
/* filter the requested power state per nid */
unsigned int (*power_filter)(struct hda_codec *codec, hda_nid_t nid,
unsigned int power_state);
/* codec-specific additional proc output */
void (*proc_widget_hook)(struct snd_info_buffer *buffer,
struct hda_codec *codec, hda_nid_t nid);
/* jack detection */
struct snd_array jacktbl;
unsigned long jackpoll_interval; /* In jiffies. Zero means no poll, rely on unsol events */
struct delayed_work jackpoll_work;
#ifdef CONFIG_SND_HDA_INPUT_JACK
/* jack detection */
struct snd_array jacks;
#endif
int depop_delay; /* depop delay in ms, -1 for default delay time */
/* fix-up list */
int fixup_id;
const struct hda_fixup *fixup_list;
const char *fixup_name;
/* additional init verbs */
struct snd_array verbs;
};
/* direction */
enum {
HDA_INPUT, HDA_OUTPUT
};
/* snd_hda_codec_read/write optional flags */
#define HDA_RW_NO_RESPONSE_FALLBACK (1 << 0)
/*
* constructors
*/
int snd_hda_bus_new(struct snd_card *card, const struct hda_bus_template *temp,
struct hda_bus **busp);
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 flags,
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);
#define snd_hda_param_read(codec, nid, param) \
snd_hda_codec_read(codec, nid, 0, AC_VERB_PARAMETERS, param)
int snd_hda_get_sub_nodes(struct hda_codec *codec, hda_nid_t nid,
hda_nid_t *start_id);
int snd_hda_get_connections(struct hda_codec *codec, hda_nid_t nid,
hda_nid_t *conn_list, int max_conns);
static inline int
snd_hda_get_num_conns(struct hda_codec *codec, hda_nid_t nid)
{
return snd_hda_get_connections(codec, nid, NULL, 0);
}
int snd_hda_get_num_raw_conns(struct hda_codec *codec, hda_nid_t nid);
int snd_hda_get_raw_connections(struct hda_codec *codec, hda_nid_t nid,
hda_nid_t *conn_list, int max_conns);
int snd_hda_get_conn_list(struct hda_codec *codec, hda_nid_t nid,
const hda_nid_t **listp);
int snd_hda_override_conn_list(struct hda_codec *codec, hda_nid_t nid, int nums,
const hda_nid_t *list);
int snd_hda_get_conn_index(struct hda_codec *codec, hda_nid_t mux,
hda_nid_t nid, int recursive);
int snd_hda_get_devices(struct hda_codec *codec, hda_nid_t nid,
u8 *dev_list, int max_devices);
int snd_hda_query_supported_pcm(struct hda_codec *codec, hda_nid_t nid,
u32 *ratesp, u64 *formatsp, unsigned int *bpsp);
struct hda_verb {
hda_nid_t nid;
u32 verb;
u32 param;
};
void snd_hda_sequence_write(struct hda_codec *codec,
const struct hda_verb *seq);
/* unsolicited event */
int snd_hda_queue_unsol_event(struct hda_bus *bus, u32 res, u32 res_ex);
/* cached write */
int snd_hda_codec_write_cache(struct hda_codec *codec, hda_nid_t nid,
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 flags, unsigned int verb, unsigned int parm);
void snd_hda_codec_resume_cache(struct hda_codec *codec);
/* 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; /* original pin control value */
unsigned char target; /* target pin control value */
unsigned int cfg; /* default configuration */
};
unsigned int snd_hda_codec_get_pincfg(struct hda_codec *codec, hda_nid_t nid);
int snd_hda_codec_set_pincfg(struct hda_codec *codec, hda_nid_t nid,
unsigned int cfg);
int snd_hda_add_pincfg(struct hda_codec *codec, struct snd_array *list,
hda_nid_t nid, unsigned int cfg); /* for hwdep */
void snd_hda_shutup_pins(struct hda_codec *codec);
/* SPDIF controls */
struct hda_spdif_out {
hda_nid_t nid; /* Converter nid values relate to */
unsigned int status; /* IEC958 status bits */
unsigned short ctls; /* SPDIF control bits */
};
struct hda_spdif_out *snd_hda_spdif_out_of_nid(struct hda_codec *codec,
hda_nid_t nid);
void snd_hda_spdif_ctls_unassign(struct hda_codec *codec, int idx);
void snd_hda_spdif_ctls_assign(struct hda_codec *codec, int idx, hda_nid_t nid);
/*
* Mixer
*/
int snd_hda_build_controls(struct hda_bus *bus);
int snd_hda_codec_build_controls(struct hda_codec *codec);
/*
* PCM
*/
int snd_hda_build_pcms(struct hda_bus *bus);
int snd_hda_codec_build_pcms(struct hda_codec *codec);
int snd_hda_codec_prepare(struct hda_codec *codec,
struct hda_pcm_stream *hinfo,
unsigned int stream,
unsigned int format,
struct snd_pcm_substream *substream);
void snd_hda_codec_cleanup(struct hda_codec *codec,
struct hda_pcm_stream *hinfo,
struct snd_pcm_substream *substream);
void snd_hda_codec_setup_stream(struct hda_codec *codec, hda_nid_t nid,
u32 stream_tag,
int channel_id, int format);
void __snd_hda_codec_cleanup_stream(struct hda_codec *codec, hda_nid_t nid,
int do_now);
#define snd_hda_codec_cleanup_stream(codec, nid) \
__snd_hda_codec_cleanup_stream(codec, nid, 0)
unsigned int snd_hda_calc_stream_format(struct hda_codec *codec,
unsigned int rate,
unsigned int channels,
unsigned int format,
unsigned int maxbps,
unsigned short spdif_ctls);
int snd_hda_is_supported_format(struct hda_codec *codec, hda_nid_t nid,
unsigned int format);
extern const struct snd_pcm_chmap_elem snd_pcm_2_1_chmaps[];
/*
* Misc
*/
void snd_hda_get_codec_name(struct hda_codec *codec, char *name, int namelen);
void snd_hda_bus_reboot_notify(struct hda_bus *bus);
void snd_hda_codec_set_power_to_all(struct hda_codec *codec, hda_nid_t fg,
unsigned int power_state);
int snd_hda_lock_devices(struct hda_bus *bus);
void snd_hda_unlock_devices(struct hda_bus *bus);
/*
* power management
*/
#ifdef CONFIG_PM
int snd_hda_suspend(struct hda_bus *bus);
int snd_hda_resume(struct hda_bus *bus);
#endif
static inline
int hda_call_check_power_status(struct hda_codec *codec, hda_nid_t nid)
{
#ifdef CONFIG_PM
if (codec->patch_ops.check_power_status)
return codec->patch_ops.check_power_status(codec, nid);
#endif
return 0;
}
/*
* get widget information
*/
const char *snd_hda_get_jack_connectivity(u32 cfg);
const char *snd_hda_get_jack_type(u32 cfg);
const char *snd_hda_get_jack_location(u32 cfg);
/*
* power saving
*/
#ifdef CONFIG_PM
void snd_hda_power_save(struct hda_codec *codec, int delta, bool d3wait);
void snd_hda_update_power_acct(struct hda_codec *codec);
#else
static inline void snd_hda_power_save(struct hda_codec *codec, int delta,
bool d3wait) {}
#endif
/**
* snd_hda_power_up - Power-up the codec
* @codec: HD-audio codec
*
* Increment the power-up counter and power up the hardware really when
* not turned on yet.
*/
static inline void snd_hda_power_up(struct hda_codec *codec)
{
snd_hda_power_save(codec, 1, false);
}
/**
* snd_hda_power_up_d3wait - Power-up the codec after waiting for any pending
* D3 transition to complete. This differs from snd_hda_power_up() when
* power_transition == -1. snd_hda_power_up sees this case as a nop,
* snd_hda_power_up_d3wait waits for the D3 transition to complete then powers
* back up.
* @codec: HD-audio codec
*
* Cancel any power down operation hapenning on the work queue, then power up.
*/
static inline void snd_hda_power_up_d3wait(struct hda_codec *codec)
{
snd_hda_power_save(codec, 1, true);
}
/**
* snd_hda_power_down - Power-down the codec
* @codec: HD-audio codec
*
* Decrement the power-up counter and schedules the power-off work if
* the counter rearches to zero.
*/
static inline void snd_hda_power_down(struct hda_codec *codec)
{
snd_hda_power_save(codec, -1, false);
}
/**
* snd_hda_power_sync - Synchronize the power-save status
* @codec: HD-audio codec
*
* Synchronize the actual power state with the power account;
* called when power_save parameter is changed
*/
static inline void snd_hda_power_sync(struct hda_codec *codec)
{
snd_hda_power_save(codec, 0, false);
}
#ifdef CONFIG_SND_HDA_PATCH_LOADER
/*
* patch firmware
*/
int snd_hda_load_patch(struct hda_bus *bus, size_t size, const void *buf);
#endif
#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
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
#endif /* __SOUND_HDA_CODEC_H */

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,56 @@
/*
* 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);
unsigned int azx_get_pos_lpib(struct azx *chip, struct azx_dev *azx_dev);
unsigned int azx_get_pos_posbuf(struct azx *chip, struct azx_dev *azx_dev);
/* 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);
void azx_notifier_register(struct azx *chip);
void azx_notifier_unregister(struct azx *chip);
#endif /* __SOUND_HDA_CONTROLLER_H */

804
sound/pci/hda/hda_eld.c Normal file
View file

@ -0,0 +1,804 @@
/*
* 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>
*
* This driver is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This driver is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
#include <linux/init.h>
#include <linux/slab.h>
#include <sound/core.h>
#include <asm/unaligned.h>
#include "hda_codec.h"
#include "hda_local.h"
enum eld_versions {
ELD_VER_CEA_861D = 2,
ELD_VER_PARTIAL = 31,
};
enum cea_edid_versions {
CEA_EDID_VER_NONE = 0,
CEA_EDID_VER_CEA861 = 1,
CEA_EDID_VER_CEA861A = 2,
CEA_EDID_VER_CEA861BCD = 3,
CEA_EDID_VER_RESERVED = 4,
};
static char *cea_speaker_allocation_names[] = {
/* 0 */ "FL/FR",
/* 1 */ "LFE",
/* 2 */ "FC",
/* 3 */ "RL/RR",
/* 4 */ "RC",
/* 5 */ "FLC/FRC",
/* 6 */ "RLC/RRC",
/* 7 */ "FLW/FRW",
/* 8 */ "FLH/FRH",
/* 9 */ "TC",
/* 10 */ "FCH",
};
static char *eld_connection_type_names[4] = {
"HDMI",
"DisplayPort",
"2-reserved",
"3-reserved"
};
enum cea_audio_coding_types {
AUDIO_CODING_TYPE_REF_STREAM_HEADER = 0,
AUDIO_CODING_TYPE_LPCM = 1,
AUDIO_CODING_TYPE_AC3 = 2,
AUDIO_CODING_TYPE_MPEG1 = 3,
AUDIO_CODING_TYPE_MP3 = 4,
AUDIO_CODING_TYPE_MPEG2 = 5,
AUDIO_CODING_TYPE_AACLC = 6,
AUDIO_CODING_TYPE_DTS = 7,
AUDIO_CODING_TYPE_ATRAC = 8,
AUDIO_CODING_TYPE_SACD = 9,
AUDIO_CODING_TYPE_EAC3 = 10,
AUDIO_CODING_TYPE_DTS_HD = 11,
AUDIO_CODING_TYPE_MLP = 12,
AUDIO_CODING_TYPE_DST = 13,
AUDIO_CODING_TYPE_WMAPRO = 14,
AUDIO_CODING_TYPE_REF_CXT = 15,
/* also include valid xtypes below */
AUDIO_CODING_TYPE_HE_AAC = 15,
AUDIO_CODING_TYPE_HE_AAC2 = 16,
AUDIO_CODING_TYPE_MPEG_SURROUND = 17,
};
enum cea_audio_coding_xtypes {
AUDIO_CODING_XTYPE_HE_REF_CT = 0,
AUDIO_CODING_XTYPE_HE_AAC = 1,
AUDIO_CODING_XTYPE_HE_AAC2 = 2,
AUDIO_CODING_XTYPE_MPEG_SURROUND = 3,
AUDIO_CODING_XTYPE_FIRST_RESERVED = 4,
};
static char *cea_audio_coding_type_names[] = {
/* 0 */ "undefined",
/* 1 */ "LPCM",
/* 2 */ "AC-3",
/* 3 */ "MPEG1",
/* 4 */ "MP3",
/* 5 */ "MPEG2",
/* 6 */ "AAC-LC",
/* 7 */ "DTS",
/* 8 */ "ATRAC",
/* 9 */ "DSD (One Bit Audio)",
/* 10 */ "E-AC-3/DD+ (Dolby Digital Plus)",
/* 11 */ "DTS-HD",
/* 12 */ "MLP (Dolby TrueHD)",
/* 13 */ "DST",
/* 14 */ "WMAPro",
/* 15 */ "HE-AAC",
/* 16 */ "HE-AACv2",
/* 17 */ "MPEG Surround",
};
/*
* The following two lists are shared between
* - HDMI audio InfoFrame (source to sink)
* - CEA E-EDID Extension (sink to source)
*/
/*
* SS1:SS0 index => sample size
*/
static int cea_sample_sizes[4] = {
0, /* 0: Refer to Stream Header */
AC_SUPPCM_BITS_16, /* 1: 16 bits */
AC_SUPPCM_BITS_20, /* 2: 20 bits */
AC_SUPPCM_BITS_24, /* 3: 24 bits */
};
/*
* SF2:SF1:SF0 index => sampling frequency
*/
static int cea_sampling_frequencies[8] = {
0, /* 0: Refer to Stream Header */
SNDRV_PCM_RATE_32000, /* 1: 32000Hz */
SNDRV_PCM_RATE_44100, /* 2: 44100Hz */
SNDRV_PCM_RATE_48000, /* 3: 48000Hz */
SNDRV_PCM_RATE_88200, /* 4: 88200Hz */
SNDRV_PCM_RATE_96000, /* 5: 96000Hz */
SNDRV_PCM_RATE_176400, /* 6: 176400Hz */
SNDRV_PCM_RATE_192000, /* 7: 192000Hz */
};
static unsigned int hdmi_get_eld_data(struct hda_codec *codec, hda_nid_t nid,
int byte_index)
{
unsigned int val;
val = snd_hda_codec_read(codec, nid, 0,
AC_VERB_GET_HDMI_ELDD, byte_index);
#ifdef BE_PARANOID
codec_info(codec, "HDMI: ELD data byte %d: 0x%x\n", byte_index, val);
#endif
return val;
}
#define GRAB_BITS(buf, byte, lowbit, bits) \
({ \
BUILD_BUG_ON(lowbit > 7); \
BUILD_BUG_ON(bits > 8); \
BUILD_BUG_ON(bits <= 0); \
\
(buf[byte] >> (lowbit)) & ((1 << (bits)) - 1); \
})
static void hdmi_update_short_audio_desc(struct hda_codec *codec,
struct cea_sad *a,
const unsigned char *buf)
{
int i;
int val;
val = GRAB_BITS(buf, 1, 0, 7);
a->rates = 0;
for (i = 0; i < 7; i++)
if (val & (1 << i))
a->rates |= cea_sampling_frequencies[i + 1];
a->channels = GRAB_BITS(buf, 0, 0, 3);
a->channels++;
a->sample_bits = 0;
a->max_bitrate = 0;
a->format = GRAB_BITS(buf, 0, 3, 4);
switch (a->format) {
case AUDIO_CODING_TYPE_REF_STREAM_HEADER:
codec_info(codec, "HDMI: audio coding type 0 not expected\n");
break;
case AUDIO_CODING_TYPE_LPCM:
val = GRAB_BITS(buf, 2, 0, 3);
for (i = 0; i < 3; i++)
if (val & (1 << i))
a->sample_bits |= cea_sample_sizes[i + 1];
break;
case AUDIO_CODING_TYPE_AC3:
case AUDIO_CODING_TYPE_MPEG1:
case AUDIO_CODING_TYPE_MP3:
case AUDIO_CODING_TYPE_MPEG2:
case AUDIO_CODING_TYPE_AACLC:
case AUDIO_CODING_TYPE_DTS:
case AUDIO_CODING_TYPE_ATRAC:
a->max_bitrate = GRAB_BITS(buf, 2, 0, 8);
a->max_bitrate *= 8000;
break;
case AUDIO_CODING_TYPE_SACD:
break;
case AUDIO_CODING_TYPE_EAC3:
break;
case AUDIO_CODING_TYPE_DTS_HD:
break;
case AUDIO_CODING_TYPE_MLP:
break;
case AUDIO_CODING_TYPE_DST:
break;
case AUDIO_CODING_TYPE_WMAPRO:
a->profile = GRAB_BITS(buf, 2, 0, 3);
break;
case AUDIO_CODING_TYPE_REF_CXT:
a->format = GRAB_BITS(buf, 2, 3, 5);
if (a->format == AUDIO_CODING_XTYPE_HE_REF_CT ||
a->format >= AUDIO_CODING_XTYPE_FIRST_RESERVED) {
codec_info(codec,
"HDMI: audio coding xtype %d not expected\n",
a->format);
a->format = 0;
} else
a->format += AUDIO_CODING_TYPE_HE_AAC -
AUDIO_CODING_XTYPE_HE_AAC;
break;
}
}
/*
* Be careful, ELD buf could be totally rubbish!
*/
int snd_hdmi_parse_eld(struct hda_codec *codec, struct parsed_hdmi_eld *e,
const unsigned char *buf, int size)
{
int mnl;
int i;
e->eld_ver = GRAB_BITS(buf, 0, 3, 5);
if (e->eld_ver != ELD_VER_CEA_861D &&
e->eld_ver != ELD_VER_PARTIAL) {
codec_info(codec, "HDMI: Unknown ELD version %d\n", e->eld_ver);
goto out_fail;
}
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);
e->support_hdcp = GRAB_BITS(buf, 5, 0, 1);
e->support_ai = GRAB_BITS(buf, 5, 1, 1);
e->conn_type = GRAB_BITS(buf, 5, 2, 2);
e->sad_count = GRAB_BITS(buf, 5, 4, 4);
e->aud_synch_delay = GRAB_BITS(buf, 6, 0, 8) * 2;
e->spk_alloc = GRAB_BITS(buf, 7, 0, 7);
e->port_id = get_unaligned_le64(buf + 8);
/* not specified, but the spec's tendency is little endian */
e->manufacture_id = get_unaligned_le16(buf + 16);
e->product_id = get_unaligned_le16(buf + 18);
if (mnl > ELD_MAX_MNL) {
codec_info(codec, "HDMI: MNL is reserved value %d\n", mnl);
goto out_fail;
} else if (ELD_FIXED_BYTES + mnl > size) {
codec_info(codec, "HDMI: out of range MNL %d\n", mnl);
goto out_fail;
} else
strlcpy(e->monitor_name, buf + ELD_FIXED_BYTES, mnl + 1);
for (i = 0; i < e->sad_count; i++) {
if (ELD_FIXED_BYTES + mnl + 3 * (i + 1) > size) {
codec_info(codec, "HDMI: out of range SAD %d\n", i);
goto out_fail;
}
hdmi_update_short_audio_desc(codec, e->sad + i,
buf + ELD_FIXED_BYTES + mnl + 3 * i);
}
/*
* HDMI sink's ELD info cannot always be retrieved for now, e.g.
* in console or for audio devices. Assume the highest speakers
* configuration, to _not_ prohibit multi-channel audio playback.
*/
if (!e->spk_alloc)
e->spk_alloc = 0xffff;
return 0;
out_fail:
return -EINVAL;
}
int snd_hdmi_get_eld_size(struct hda_codec *codec, hda_nid_t nid)
{
return snd_hda_codec_read(codec, nid, 0, AC_VERB_GET_HDMI_DIP_SIZE,
AC_DIPSIZE_ELD_BUF);
}
int snd_hdmi_get_eld(struct hda_codec *codec, hda_nid_t nid,
unsigned char *buf, int *eld_size)
{
int i;
int ret = 0;
int size;
/*
* ELD size is initialized to zero in caller function. If no errors and
* ELD is valid, actual eld_size is assigned.
*/
size = snd_hdmi_get_eld_size(codec, nid);
if (size == 0) {
/* wfg: workaround for ASUS P5E-VM HDMI board */
codec_info(codec, "HDMI: ELD buf size is 0, force 128\n");
size = 128;
}
if (size < ELD_FIXED_BYTES || size > ELD_MAX_SIZE) {
codec_info(codec, "HDMI: invalid ELD buf size %d\n", size);
return -ERANGE;
}
/* set ELD buffer */
for (i = 0; i < size; i++) {
unsigned int val = hdmi_get_eld_data(codec, nid, i);
/*
* Graphics driver might be writing to ELD buffer right now.
* Just abort. The caller will repoll after a while.
*/
if (!(val & AC_ELDD_ELD_VALID)) {
codec_info(codec, "HDMI: invalid ELD data byte %d\n", i);
ret = -EINVAL;
goto error;
}
val &= AC_ELDD_ELD_DATA;
/*
* The first byte cannot be zero. This can happen on some DVI
* connections. Some Intel chips may also need some 250ms delay
* to return non-zero ELD data, even when the graphics driver
* correctly writes ELD content before setting ELD_valid bit.
*/
if (!val && !i) {
codec_dbg(codec, "HDMI: 0 ELD data\n");
ret = -EINVAL;
goto error;
}
buf[i] = val;
}
*eld_size = size;
error:
return ret;
}
/**
* SNDRV_PCM_RATE_* and AC_PAR_PCM values don't match, print correct rates with
* hdmi-specific routine.
*/
static void hdmi_print_pcm_rates(int pcm, char *buf, int buflen)
{
static unsigned int alsa_rates[] = {
5512, 8000, 11025, 16000, 22050, 32000, 44100, 48000, 64000,
88200, 96000, 176400, 192000, 384000
};
int i, j;
for (i = 0, j = 0; i < ARRAY_SIZE(alsa_rates); i++)
if (pcm & (1 << i))
j += snprintf(buf + j, buflen - j, " %d",
alsa_rates[i]);
buf[j] = '\0'; /* necessary when j == 0 */
}
#define SND_PRINT_RATES_ADVISED_BUFSIZE 80
static void hdmi_show_short_audio_desc(struct hda_codec *codec,
struct cea_sad *a)
{
char buf[SND_PRINT_RATES_ADVISED_BUFSIZE];
char buf2[8 + SND_PRINT_BITS_ADVISED_BUFSIZE] = ", bits =";
if (!a->format)
return;
hdmi_print_pcm_rates(a->rates, buf, sizeof(buf));
if (a->format == AUDIO_CODING_TYPE_LPCM)
snd_print_pcm_bits(a->sample_bits, buf2 + 8, sizeof(buf2) - 8);
else if (a->max_bitrate)
snprintf(buf2, sizeof(buf2),
", max bitrate = %d", a->max_bitrate);
else
buf2[0] = '\0';
codec_dbg(codec,
"HDMI: supports coding type %s: channels = %d, rates =%s%s\n",
cea_audio_coding_type_names[a->format],
a->channels, buf, buf2);
}
void snd_print_channel_allocation(int spk_alloc, char *buf, int buflen)
{
int i, j;
for (i = 0, j = 0; i < ARRAY_SIZE(cea_speaker_allocation_names); i++) {
if (spk_alloc & (1 << i))
j += snprintf(buf + j, buflen - j, " %s",
cea_speaker_allocation_names[i]);
}
buf[j] = '\0'; /* necessary when j == 0 */
}
void snd_hdmi_show_eld(struct hda_codec *codec, struct parsed_hdmi_eld *e)
{
int i;
codec_dbg(codec, "HDMI: detected monitor %s at connection type %s\n",
e->monitor_name,
eld_connection_type_names[e->conn_type]);
if (e->spk_alloc) {
char buf[SND_PRINT_CHANNEL_ALLOCATION_ADVISED_BUFSIZE];
snd_print_channel_allocation(e->spk_alloc, buf, sizeof(buf));
codec_dbg(codec, "HDMI: available speakers:%s\n", buf);
}
for (i = 0; i < e->sad_count; i++)
hdmi_show_short_audio_desc(codec, e->sad + i);
}
#ifdef CONFIG_PROC_FS
static void hdmi_print_sad_info(int i, struct cea_sad *a,
struct snd_info_buffer *buffer)
{
char buf[SND_PRINT_RATES_ADVISED_BUFSIZE];
snd_iprintf(buffer, "sad%d_coding_type\t[0x%x] %s\n",
i, a->format, cea_audio_coding_type_names[a->format]);
snd_iprintf(buffer, "sad%d_channels\t\t%d\n", i, a->channels);
hdmi_print_pcm_rates(a->rates, buf, sizeof(buf));
snd_iprintf(buffer, "sad%d_rates\t\t[0x%x]%s\n", i, a->rates, buf);
if (a->format == AUDIO_CODING_TYPE_LPCM) {
snd_print_pcm_bits(a->sample_bits, buf, sizeof(buf));
snd_iprintf(buffer, "sad%d_bits\t\t[0x%x]%s\n",
i, a->sample_bits, buf);
}
if (a->max_bitrate)
snd_iprintf(buffer, "sad%d_max_bitrate\t%d\n",
i, a->max_bitrate);
if (a->profile)
snd_iprintf(buffer, "sad%d_profile\t\t%d\n", i, a->profile);
}
void snd_hdmi_print_eld_info(struct hdmi_eld *eld,
struct snd_info_buffer *buffer)
{
struct parsed_hdmi_eld *e = &eld->info;
char buf[SND_PRINT_CHANNEL_ALLOCATION_ADVISED_BUFSIZE];
int i;
static char *eld_version_names[32] = {
"reserved",
"reserved",
"CEA-861D or below",
[3 ... 30] = "reserved",
[31] = "partial"
};
static char *cea_edid_version_names[8] = {
"no CEA EDID Timing Extension block present",
"CEA-861",
"CEA-861-A",
"CEA-861-B, C or D",
[4 ... 7] = "reserved"
};
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_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);
snd_iprintf(buffer, "product_id\t\t0x%x\n", e->product_id);
snd_iprintf(buffer, "port_id\t\t\t0x%llx\n", (long long)e->port_id);
snd_iprintf(buffer, "support_hdcp\t\t%d\n", e->support_hdcp);
snd_iprintf(buffer, "support_ai\t\t%d\n", e->support_ai);
snd_iprintf(buffer, "audio_sync_delay\t%d\n", e->aud_synch_delay);
snd_print_channel_allocation(e->spk_alloc, buf, sizeof(buf));
snd_iprintf(buffer, "speakers\t\t[0x%x]%s\n", e->spk_alloc, buf);
snd_iprintf(buffer, "sad_count\t\t%d\n", e->sad_count);
for (i = 0; i < e->sad_count; i++)
hdmi_print_sad_info(i, e->sad + i, buffer);
}
void snd_hdmi_write_eld_info(struct hdmi_eld *eld,
struct snd_info_buffer *buffer)
{
struct parsed_hdmi_eld *e = &eld->info;
char line[64];
char name[64];
char *sname;
long long val;
unsigned int n;
while (!snd_info_get_line(buffer, line, sizeof(line))) {
if (sscanf(line, "%s %llx", name, &val) != 2)
continue;
/*
* We don't allow modification to these fields:
* monitor_name manufacture_id product_id
* eld_version edid_version
*/
if (!strcmp(name, "monitor_present"))
eld->monitor_present = val;
else if (!strcmp(name, "eld_valid"))
eld->eld_valid = val;
else if (!strcmp(name, "connection_type"))
e->conn_type = val;
else if (!strcmp(name, "port_id"))
e->port_id = val;
else if (!strcmp(name, "support_hdcp"))
e->support_hdcp = val;
else if (!strcmp(name, "support_ai"))
e->support_ai = val;
else if (!strcmp(name, "audio_sync_delay"))
e->aud_synch_delay = val;
else if (!strcmp(name, "speakers"))
e->spk_alloc = val;
else if (!strcmp(name, "sad_count"))
e->sad_count = val;
else if (!strncmp(name, "sad", 3)) {
sname = name + 4;
n = name[3] - '0';
if (name[4] >= '0' && name[4] <= '9') {
sname++;
n = 10 * n + name[4] - '0';
}
if (n >= ELD_MAX_SAD)
continue;
if (!strcmp(sname, "_coding_type"))
e->sad[n].format = val;
else if (!strcmp(sname, "_channels"))
e->sad[n].channels = val;
else if (!strcmp(sname, "_rates"))
e->sad[n].rates = val;
else if (!strcmp(sname, "_bits"))
e->sad[n].sample_bits = val;
else if (!strcmp(sname, "_max_bitrate"))
e->sad[n].max_bitrate = val;
else if (!strcmp(sname, "_profile"))
e->sad[n].profile = val;
if (n >= e->sad_count)
e->sad_count = n + 1;
}
}
}
#endif /* CONFIG_PROC_FS */
/* update PCM info based on ELD */
void snd_hdmi_eld_update_pcm_info(struct parsed_hdmi_eld *e,
struct hda_pcm_stream *hinfo)
{
u32 rates;
u64 formats;
unsigned int maxbps;
unsigned int channels_max;
int i;
/* assume basic audio support (the basic audio flag is not in ELD;
* however, all audio capable sinks are required to support basic
* audio) */
rates = SNDRV_PCM_RATE_32000 | SNDRV_PCM_RATE_44100 |
SNDRV_PCM_RATE_48000;
formats = SNDRV_PCM_FMTBIT_S16_LE;
maxbps = 16;
channels_max = 2;
for (i = 0; i < e->sad_count; i++) {
struct cea_sad *a = &e->sad[i];
rates |= a->rates;
if (a->channels > channels_max)
channels_max = a->channels;
if (a->format == AUDIO_CODING_TYPE_LPCM) {
if (a->sample_bits & AC_SUPPCM_BITS_20) {
formats |= SNDRV_PCM_FMTBIT_S32_LE;
if (maxbps < 20)
maxbps = 20;
}
if (a->sample_bits & AC_SUPPCM_BITS_24) {
formats |= SNDRV_PCM_FMTBIT_S32_LE;
if (maxbps < 24)
maxbps = 24;
}
}
}
/* restrict the parameters by the values the codec provides */
hinfo->rates &= rates;
hinfo->formats &= formats;
hinfo->maxbps = min(hinfo->maxbps, maxbps);
hinfo->channels_max = min(hinfo->channels_max, channels_max);
}
/* ATI/AMD specific stuff (ELD emulation) */
#define ATI_VERB_SET_AUDIO_DESCRIPTOR 0x776
#define ATI_VERB_SET_SINK_INFO_INDEX 0x780
#define ATI_VERB_GET_SPEAKER_ALLOCATION 0xf70
#define ATI_VERB_GET_AUDIO_DESCRIPTOR 0xf76
#define ATI_VERB_GET_AUDIO_VIDEO_DELAY 0xf7b
#define ATI_VERB_GET_SINK_INFO_INDEX 0xf80
#define ATI_VERB_GET_SINK_INFO_DATA 0xf81
#define ATI_SPKALLOC_SPKALLOC 0x007f
#define ATI_SPKALLOC_TYPE_HDMI 0x0100
#define ATI_SPKALLOC_TYPE_DISPLAYPORT 0x0200
/* first three bytes are just standard SAD */
#define ATI_AUDIODESC_CHANNELS 0x00000007
#define ATI_AUDIODESC_RATES 0x0000ff00
#define ATI_AUDIODESC_LPCM_STEREO_RATES 0xff000000
/* in standard HDMI VSDB format */
#define ATI_DELAY_VIDEO_LATENCY 0x000000ff
#define ATI_DELAY_AUDIO_LATENCY 0x0000ff00
enum ati_sink_info_idx {
ATI_INFO_IDX_MANUFACTURER_ID = 0,
ATI_INFO_IDX_PRODUCT_ID = 1,
ATI_INFO_IDX_SINK_DESC_LEN = 2,
ATI_INFO_IDX_PORT_ID_LOW = 3,
ATI_INFO_IDX_PORT_ID_HIGH = 4,
ATI_INFO_IDX_SINK_DESC_FIRST = 5,
ATI_INFO_IDX_SINK_DESC_LAST = 22, /* max len 18 bytes */
};
int snd_hdmi_get_eld_ati(struct hda_codec *codec, hda_nid_t nid,
unsigned char *buf, int *eld_size, bool rev3_or_later)
{
int spkalloc, ati_sad, aud_synch;
int sink_desc_len = 0;
int pos, i;
/* ATI/AMD does not have ELD, emulate it */
spkalloc = snd_hda_codec_read(codec, nid, 0, ATI_VERB_GET_SPEAKER_ALLOCATION, 0);
if (spkalloc <= 0) {
codec_info(codec, "HDMI ATI/AMD: no speaker allocation for ELD\n");
return -EINVAL;
}
memset(buf, 0, ELD_FIXED_BYTES + ELD_MAX_MNL + ELD_MAX_SAD * 3);
/* version */
buf[0] = ELD_VER_CEA_861D << 3;
/* speaker allocation from EDID */
buf[7] = spkalloc & ATI_SPKALLOC_SPKALLOC;
/* is DisplayPort? */
if (spkalloc & ATI_SPKALLOC_TYPE_DISPLAYPORT)
buf[5] |= 0x04;
pos = ELD_FIXED_BYTES;
if (rev3_or_later) {
int sink_info;
snd_hda_codec_write(codec, nid, 0, ATI_VERB_SET_SINK_INFO_INDEX, ATI_INFO_IDX_PORT_ID_LOW);
sink_info = snd_hda_codec_read(codec, nid, 0, ATI_VERB_GET_SINK_INFO_DATA, 0);
put_unaligned_le32(sink_info, buf + 8);
snd_hda_codec_write(codec, nid, 0, ATI_VERB_SET_SINK_INFO_INDEX, ATI_INFO_IDX_PORT_ID_HIGH);
sink_info = snd_hda_codec_read(codec, nid, 0, ATI_VERB_GET_SINK_INFO_DATA, 0);
put_unaligned_le32(sink_info, buf + 12);
snd_hda_codec_write(codec, nid, 0, ATI_VERB_SET_SINK_INFO_INDEX, ATI_INFO_IDX_MANUFACTURER_ID);
sink_info = snd_hda_codec_read(codec, nid, 0, ATI_VERB_GET_SINK_INFO_DATA, 0);
put_unaligned_le16(sink_info, buf + 16);
snd_hda_codec_write(codec, nid, 0, ATI_VERB_SET_SINK_INFO_INDEX, ATI_INFO_IDX_PRODUCT_ID);
sink_info = snd_hda_codec_read(codec, nid, 0, ATI_VERB_GET_SINK_INFO_DATA, 0);
put_unaligned_le16(sink_info, buf + 18);
snd_hda_codec_write(codec, nid, 0, ATI_VERB_SET_SINK_INFO_INDEX, ATI_INFO_IDX_SINK_DESC_LEN);
sink_desc_len = snd_hda_codec_read(codec, nid, 0, ATI_VERB_GET_SINK_INFO_DATA, 0);
if (sink_desc_len > ELD_MAX_MNL) {
codec_info(codec, "HDMI ATI/AMD: Truncating HDMI sink description with length %d\n",
sink_desc_len);
sink_desc_len = ELD_MAX_MNL;
}
buf[4] |= sink_desc_len;
for (i = 0; i < sink_desc_len; i++) {
snd_hda_codec_write(codec, nid, 0, ATI_VERB_SET_SINK_INFO_INDEX, ATI_INFO_IDX_SINK_DESC_FIRST + i);
buf[pos++] = snd_hda_codec_read(codec, nid, 0, ATI_VERB_GET_SINK_INFO_DATA, 0);
}
}
for (i = AUDIO_CODING_TYPE_LPCM; i <= AUDIO_CODING_TYPE_WMAPRO; i++) {
if (i == AUDIO_CODING_TYPE_SACD || i == AUDIO_CODING_TYPE_DST)
continue; /* not handled by ATI/AMD */
snd_hda_codec_write(codec, nid, 0, ATI_VERB_SET_AUDIO_DESCRIPTOR, i << 3);
ati_sad = snd_hda_codec_read(codec, nid, 0, ATI_VERB_GET_AUDIO_DESCRIPTOR, 0);
if (ati_sad <= 0)
continue;
if (ati_sad & ATI_AUDIODESC_RATES) {
/* format is supported, copy SAD as-is */
buf[pos++] = (ati_sad & 0x0000ff) >> 0;
buf[pos++] = (ati_sad & 0x00ff00) >> 8;
buf[pos++] = (ati_sad & 0xff0000) >> 16;
}
if (i == AUDIO_CODING_TYPE_LPCM
&& (ati_sad & ATI_AUDIODESC_LPCM_STEREO_RATES)
&& (ati_sad & ATI_AUDIODESC_LPCM_STEREO_RATES) >> 16 != (ati_sad & ATI_AUDIODESC_RATES)) {
/* for PCM there is a separate stereo rate mask */
buf[pos++] = ((ati_sad & 0x000000ff) & ~ATI_AUDIODESC_CHANNELS) | 0x1;
/* rates from the extra byte */
buf[pos++] = (ati_sad & 0xff000000) >> 24;
buf[pos++] = (ati_sad & 0x00ff0000) >> 16;
}
}
if (pos == ELD_FIXED_BYTES + sink_desc_len) {
codec_info(codec, "HDMI ATI/AMD: no audio descriptors for ELD\n");
return -EINVAL;
}
/*
* HDMI VSDB latency format:
* separately for both audio and video:
* 0 field not valid or unknown latency
* [1..251] msecs = (x-1)*2 (max 500ms with x = 251 = 0xfb)
* 255 audio/video not supported
*
* HDA latency format:
* single value indicating video latency relative to audio:
* 0 unknown or 0ms
* [1..250] msecs = x*2 (max 500ms with x = 250 = 0xfa)
* [251..255] reserved
*/
aud_synch = snd_hda_codec_read(codec, nid, 0, ATI_VERB_GET_AUDIO_VIDEO_DELAY, 0);
if ((aud_synch & ATI_DELAY_VIDEO_LATENCY) && (aud_synch & ATI_DELAY_AUDIO_LATENCY)) {
int video_latency_hdmi = (aud_synch & ATI_DELAY_VIDEO_LATENCY);
int audio_latency_hdmi = (aud_synch & ATI_DELAY_AUDIO_LATENCY) >> 8;
if (video_latency_hdmi <= 0xfb && audio_latency_hdmi <= 0xfb &&
video_latency_hdmi > audio_latency_hdmi)
buf[6] = video_latency_hdmi - audio_latency_hdmi;
/* else unknown/invalid or 0ms or video ahead of audio, so use zero */
}
/* SAD count */
buf[5] |= ((pos - ELD_FIXED_BYTES - sink_desc_len) / 3) << 4;
/* Baseline ELD block length is 4-byte aligned */
pos = round_up(pos, 4);
/* Baseline ELD length (4-byte header is not counted in) */
buf[2] = (pos - 4) / 4;
*eld_size = pos;
return 0;
}

5443
sound/pci/hda/hda_generic.c Normal file

File diff suppressed because it is too large Load diff

337
sound/pci/hda/hda_generic.h Normal file
View file

@ -0,0 +1,337 @@
/*
* 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
/* 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 */
unsigned int dac_min_mute:1; /* minimal = mute for DACs */
/* 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_callback *cb);
void (*line_automute_hook)(struct hda_codec *codec,
struct hda_jack_callback *cb);
void (*mic_autoswitch_hook)(struct hda_codec *codec,
struct hda_jack_callback *cb);
};
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_callback *jack);
void snd_hda_gen_line_automute(struct hda_codec *codec,
struct hda_jack_callback *jack);
void snd_hda_gen_mic_autoswitch(struct hda_codec *codec,
struct hda_jack_callback *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 */

124
sound/pci/hda/hda_hwdep.c Normal file
View file

@ -0,0 +1,124 @@
/*
* HWDEP Interface for HD-audio codec
*
* Copyright (c) 2007 Takashi Iwai <tiwai@suse.de>
*
* This driver is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This driver is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
#include <linux/init.h>
#include <linux/slab.h>
#include <linux/compat.h>
#include <sound/core.h>
#include "hda_codec.h"
#include "hda_local.h"
#include <sound/hda_hwdep.h>
#include <sound/minors.h>
/*
* write/read an out-of-bound verb
*/
static int verb_write_ioctl(struct hda_codec *codec,
struct hda_verb_ioctl __user *arg)
{
u32 verb, res;
if (get_user(verb, &arg->verb))
return -EFAULT;
res = snd_hda_codec_read(codec, verb >> 24, 0,
(verb >> 8) & 0xffff, verb & 0xff);
if (put_user(res, &arg->res))
return -EFAULT;
return 0;
}
static int get_wcap_ioctl(struct hda_codec *codec,
struct hda_verb_ioctl __user *arg)
{
u32 verb, res;
if (get_user(verb, &arg->verb))
return -EFAULT;
res = get_wcaps(codec, verb >> 24);
if (put_user(res, &arg->res))
return -EFAULT;
return 0;
}
/*
*/
static int hda_hwdep_ioctl(struct snd_hwdep *hw, struct file *file,
unsigned int cmd, unsigned long arg)
{
struct hda_codec *codec = hw->private_data;
void __user *argp = (void __user *)arg;
switch (cmd) {
case HDA_IOCTL_PVERSION:
return put_user(HDA_HWDEP_VERSION, (int __user *)argp);
case HDA_IOCTL_VERB_WRITE:
return verb_write_ioctl(codec, argp);
case HDA_IOCTL_GET_WCAP:
return get_wcap_ioctl(codec, argp);
}
return -ENOIOCTLCMD;
}
#ifdef CONFIG_COMPAT
static int hda_hwdep_ioctl_compat(struct snd_hwdep *hw, struct file *file,
unsigned int cmd, unsigned long arg)
{
return hda_hwdep_ioctl(hw, file, cmd, (unsigned long)compat_ptr(arg));
}
#endif
static int hda_hwdep_open(struct snd_hwdep *hw, struct file *file)
{
#ifndef CONFIG_SND_DEBUG_VERBOSE
if (!capable(CAP_SYS_RAWIO))
return -EACCES;
#endif
return 0;
}
int snd_hda_create_hwdep(struct hda_codec *codec)
{
char hwname[16];
struct snd_hwdep *hwdep;
int err;
sprintf(hwname, "HDA Codec %d", codec->addr);
err = snd_hwdep_new(codec->bus->card, hwname, codec->addr, &hwdep);
if (err < 0)
return err;
codec->hwdep = hwdep;
sprintf(hwdep->name, "HDA Codec %d", codec->addr);
hwdep->iface = SNDRV_HWDEP_IFACE_HDA;
hwdep->private_data = codec;
hwdep->exclusive = 1;
hwdep->groups = snd_hda_dev_attr_groups;
hwdep->ops.open = hda_hwdep_open;
hwdep->ops.ioctl = hda_hwdep_ioctl;
#ifdef CONFIG_COMPAT
hwdep->ops.ioctl_compat = hda_hwdep_ioctl_compat;
#endif
/* link to codec */
hwdep->dev = &codec->dev;
return 0;
}

130
sound/pci/hda/hda_i915.c Normal file
View file

@ -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 AZX_REG_EM4 0x100c
#define AZX_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;
}

37
sound/pci/hda/hda_i915.h Normal file
View file

@ -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

2216
sound/pci/hda/hda_intel.c Normal file

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,62 @@
#undef TRACE_SYSTEM
#define TRACE_SYSTEM hda_intel
#define TRACE_INCLUDE_FILE hda_intel_trace
#if !defined(_TRACE_HDA_INTEL_H) || defined(TRACE_HEADER_MULTI_READ)
#define _TRACE_HDA_INTEL_H
#include <linux/tracepoint.h>
struct azx;
struct azx_dev;
TRACE_EVENT(azx_pcm_trigger,
TP_PROTO(struct azx *chip, struct azx_dev *dev, int cmd),
TP_ARGS(chip, dev, cmd),
TP_STRUCT__entry(
__field( int, card )
__field( int, idx )
__field( int, cmd )
),
TP_fast_assign(
__entry->card = (chip)->card->number;
__entry->idx = (dev)->index;
__entry->cmd = cmd;
),
TP_printk("[%d:%d] cmd=%d", __entry->card, __entry->idx, __entry->cmd)
);
TRACE_EVENT(azx_get_position,
TP_PROTO(struct azx *chip, struct azx_dev *dev, unsigned int pos, unsigned int delay),
TP_ARGS(chip, dev, pos, delay),
TP_STRUCT__entry(
__field( int, card )
__field( int, idx )
__field( unsigned int, pos )
__field( unsigned int, delay )
),
TP_fast_assign(
__entry->card = (chip)->card->number;
__entry->idx = (dev)->index;
__entry->pos = pos;
__entry->delay = delay;
),
TP_printk("[%d:%d] pos=%u, delay=%u", __entry->card, __entry->idx, __entry->pos, __entry->delay)
);
#endif /* _TRACE_HDA_INTEL_H */
/* This part must be outside protection */
#undef TRACE_INCLUDE_PATH
#define TRACE_INCLUDE_PATH .
#include <trace/define_trace.h>

569
sound/pci/hda/hda_jack.c Normal file
View file

@ -0,0 +1,569 @@
/*
* Jack-detection handling for HD-audio
*
* Copyright (c) 2011 Takashi Iwai <tiwai@suse.de>
*
* This driver is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*/
#include <linux/init.h>
#include <linux/slab.h>
#include <linux/export.h>
#include <sound/core.h>
#include <sound/control.h>
#include <sound/jack.h>
#include "hda_codec.h"
#include "hda_local.h"
#include "hda_auto_parser.h"
#include "hda_jack.h"
bool is_jack_detectable(struct hda_codec *codec, hda_nid_t nid)
{
if (codec->no_jack_detect)
return false;
if (!(snd_hda_query_pin_caps(codec, nid) & AC_PINCAP_PRES_DETECT))
return false;
if (get_defcfg_misc(snd_hda_codec_get_pincfg(codec, nid)) &
AC_DEFCFG_MISC_NO_PRESENCE)
return false;
if (!(get_wcaps(codec, nid) & AC_WCAP_UNSOL_CAP) &&
!codec->jackpoll_interval)
return false;
return true;
}
EXPORT_SYMBOL_GPL(is_jack_detectable);
/* execute pin sense measurement */
static u32 read_pin_sense(struct hda_codec *codec, hda_nid_t nid)
{
u32 pincap;
u32 val;
if (!codec->no_trigger_sense) {
pincap = snd_hda_query_pin_caps(codec, nid);
if (pincap & AC_PINCAP_TRIG_REQ) /* need trigger? */
snd_hda_codec_read(codec, nid, 0,
AC_VERB_SET_PIN_SENSE, 0);
}
val = snd_hda_codec_read(codec, nid, 0,
AC_VERB_GET_PIN_SENSE, 0);
if (codec->inv_jack_detect)
val ^= AC_PINSENSE_PRESENCE;
return val;
}
/**
* snd_hda_jack_tbl_get - query the jack-table entry for the given NID
*/
struct hda_jack_tbl *
snd_hda_jack_tbl_get(struct hda_codec *codec, hda_nid_t nid)
{
struct hda_jack_tbl *jack = codec->jacktbl.list;
int i;
if (!nid || !jack)
return NULL;
for (i = 0; i < codec->jacktbl.used; i++, jack++)
if (jack->nid == nid)
return jack;
return NULL;
}
EXPORT_SYMBOL_GPL(snd_hda_jack_tbl_get);
/**
* snd_hda_jack_tbl_get_from_tag - query the jack-table entry for the given tag
*/
struct hda_jack_tbl *
snd_hda_jack_tbl_get_from_tag(struct hda_codec *codec, unsigned char tag)
{
struct hda_jack_tbl *jack = codec->jacktbl.list;
int i;
if (!tag || !jack)
return NULL;
for (i = 0; i < codec->jacktbl.used; i++, jack++)
if (jack->tag == tag)
return jack;
return NULL;
}
EXPORT_SYMBOL_GPL(snd_hda_jack_tbl_get_from_tag);
/**
* snd_hda_jack_tbl_new - create a jack-table entry for the given NID
*/
static struct hda_jack_tbl *
snd_hda_jack_tbl_new(struct hda_codec *codec, hda_nid_t nid)
{
struct hda_jack_tbl *jack = snd_hda_jack_tbl_get(codec, nid);
if (jack)
return jack;
jack = snd_array_new(&codec->jacktbl);
if (!jack)
return NULL;
jack->nid = nid;
jack->jack_dirty = 1;
jack->tag = codec->jacktbl.used;
return jack;
}
void snd_hda_jack_tbl_clear(struct hda_codec *codec)
{
struct hda_jack_tbl *jack = codec->jacktbl.list;
int i;
for (i = 0; i < codec->jacktbl.used; i++, jack++) {
struct hda_jack_callback *cb, *next;
#ifdef CONFIG_SND_HDA_INPUT_JACK
/* free jack instances manually when clearing/reconfiguring */
if (!codec->bus->shutdown && jack->jack)
snd_device_free(codec->bus->card, jack->jack);
#endif
for (cb = jack->callback; cb; cb = next) {
next = cb->next;
kfree(cb);
}
}
snd_array_free(&codec->jacktbl);
}
#define get_jack_plug_state(sense) !!(sense & AC_PINSENSE_PRESENCE)
/* update the cached value and notification flag if needed */
static void jack_detect_update(struct hda_codec *codec,
struct hda_jack_tbl *jack)
{
if (!jack->jack_dirty)
return;
if (jack->phantom_jack)
jack->pin_sense = AC_PINSENSE_PRESENCE;
else
jack->pin_sense = read_pin_sense(codec, jack->nid);
/* A gating jack indicates the jack is invalid if gating is unplugged */
if (jack->gating_jack && !snd_hda_jack_detect(codec, jack->gating_jack))
jack->pin_sense &= ~AC_PINSENSE_PRESENCE;
jack->jack_dirty = 0;
/* If a jack is gated by this one update it. */
if (jack->gated_jack) {
struct hda_jack_tbl *gated =
snd_hda_jack_tbl_get(codec, jack->gated_jack);
if (gated) {
gated->jack_dirty = 1;
jack_detect_update(codec, gated);
}
}
}
/**
* snd_hda_set_dirty_all - Mark all the cached as dirty
*
* This function sets the dirty flag to all entries of jack table.
* It's called from the resume path in hda_codec.c.
*/
void snd_hda_jack_set_dirty_all(struct hda_codec *codec)
{
struct hda_jack_tbl *jack = codec->jacktbl.list;
int i;
for (i = 0; i < codec->jacktbl.used; i++, jack++)
if (jack->nid)
jack->jack_dirty = 1;
}
EXPORT_SYMBOL_GPL(snd_hda_jack_set_dirty_all);
/**
* snd_hda_pin_sense - execute pin sense measurement
* @codec: the CODEC to sense
* @nid: the pin NID to sense
*
* Execute necessary pin sense measurement and return its Presence Detect,
* Impedance, ELD Valid etc. status bits.
*/
u32 snd_hda_pin_sense(struct hda_codec *codec, hda_nid_t nid)
{
struct hda_jack_tbl *jack = snd_hda_jack_tbl_get(codec, nid);
if (jack) {
jack_detect_update(codec, jack);
return jack->pin_sense;
}
return read_pin_sense(codec, nid);
}
EXPORT_SYMBOL_GPL(snd_hda_pin_sense);
/**
* snd_hda_jack_detect_state - query pin Presence Detect status
* @codec: the CODEC to sense
* @nid: the pin NID to sense
*
* Query and return the pin's Presence Detect status, as either
* HDA_JACK_NOT_PRESENT, HDA_JACK_PRESENT or HDA_JACK_PHANTOM.
*/
int snd_hda_jack_detect_state(struct hda_codec *codec, hda_nid_t nid)
{
struct hda_jack_tbl *jack = snd_hda_jack_tbl_get(codec, nid);
if (jack && jack->phantom_jack)
return HDA_JACK_PHANTOM;
else if (snd_hda_pin_sense(codec, nid) & AC_PINSENSE_PRESENCE)
return HDA_JACK_PRESENT;
else
return HDA_JACK_NOT_PRESENT;
}
EXPORT_SYMBOL_GPL(snd_hda_jack_detect_state);
/**
* snd_hda_jack_detect_enable - enable the jack-detection
*
* In the case of error, the return value will be a pointer embedded with
* errno. Check and handle the return value appropriately with standard
* macros such as @IS_ERR() and @PTR_ERR().
*/
struct hda_jack_callback *
snd_hda_jack_detect_enable_callback(struct hda_codec *codec, hda_nid_t nid,
hda_jack_callback_fn func)
{
struct hda_jack_tbl *jack;
struct hda_jack_callback *callback = NULL;
int err;
jack = snd_hda_jack_tbl_new(codec, nid);
if (!jack)
return ERR_PTR(-ENOMEM);
if (func) {
callback = kzalloc(sizeof(*callback), GFP_KERNEL);
if (!callback)
return ERR_PTR(-ENOMEM);
callback->func = func;
callback->tbl = jack;
callback->next = jack->callback;
jack->callback = callback;
}
if (jack->jack_detect)
return callback; /* already registered */
jack->jack_detect = 1;
if (codec->jackpoll_interval > 0)
return callback; /* No unsol if we're polling instead */
err = snd_hda_codec_write_cache(codec, nid, 0,
AC_VERB_SET_UNSOLICITED_ENABLE,
AC_USRSP_EN | jack->tag);
if (err < 0)
return ERR_PTR(err);
return callback;
}
EXPORT_SYMBOL_GPL(snd_hda_jack_detect_enable_callback);
int snd_hda_jack_detect_enable(struct hda_codec *codec, hda_nid_t nid)
{
return PTR_ERR_OR_ZERO(snd_hda_jack_detect_enable_callback(codec, nid, NULL));
}
EXPORT_SYMBOL_GPL(snd_hda_jack_detect_enable);
/**
* snd_hda_jack_set_gating_jack - Set gating jack.
*
* Indicates the gated jack is only valid when the gating jack is plugged.
*/
int snd_hda_jack_set_gating_jack(struct hda_codec *codec, hda_nid_t gated_nid,
hda_nid_t gating_nid)
{
struct hda_jack_tbl *gated = snd_hda_jack_tbl_new(codec, gated_nid);
struct hda_jack_tbl *gating = snd_hda_jack_tbl_new(codec, gating_nid);
if (!gated || !gating)
return -EINVAL;
gated->gating_jack = gating_nid;
gating->gated_jack = gated_nid;
return 0;
}
EXPORT_SYMBOL_GPL(snd_hda_jack_set_gating_jack);
/**
* snd_hda_jack_report_sync - sync the states of all jacks and report if changed
*/
void snd_hda_jack_report_sync(struct hda_codec *codec)
{
struct hda_jack_tbl *jack;
int i, state;
/* update all jacks at first */
jack = codec->jacktbl.list;
for (i = 0; i < codec->jacktbl.used; i++, jack++)
if (jack->nid)
jack_detect_update(codec, jack);
/* report the updated jacks; it's done after updating all jacks
* to make sure that all gating jacks properly have been set
*/
jack = codec->jacktbl.list;
for (i = 0; i < codec->jacktbl.used; i++, jack++)
if (jack->nid) {
if (!jack->kctl || jack->block_report)
continue;
state = get_jack_plug_state(jack->pin_sense);
snd_kctl_jack_report(codec->bus->card, jack->kctl, state);
#ifdef CONFIG_SND_HDA_INPUT_JACK
if (jack->jack)
snd_jack_report(jack->jack,
state ? jack->type : 0);
#endif
}
}
EXPORT_SYMBOL_GPL(snd_hda_jack_report_sync);
#ifdef CONFIG_SND_HDA_INPUT_JACK
/* guess the jack type from the pin-config */
static int get_input_jack_type(struct hda_codec *codec, hda_nid_t nid)
{
unsigned int def_conf = snd_hda_codec_get_pincfg(codec, nid);
switch (get_defcfg_device(def_conf)) {
case AC_JACK_LINE_OUT:
case AC_JACK_SPEAKER:
return SND_JACK_LINEOUT;
case AC_JACK_HP_OUT:
return SND_JACK_HEADPHONE;
case AC_JACK_SPDIF_OUT:
case AC_JACK_DIG_OTHER_OUT:
return SND_JACK_AVOUT;
case AC_JACK_MIC_IN:
return SND_JACK_MICROPHONE;
default:
return SND_JACK_LINEIN;
}
}
static void hda_free_jack_priv(struct snd_jack *jack)
{
struct hda_jack_tbl *jacks = jack->private_data;
jacks->nid = 0;
jacks->jack = NULL;
}
#endif
/**
* snd_hda_jack_add_kctl - Add a kctl for the given pin
*
* This assigns a jack-detection kctl to the given pin. The kcontrol
* will have the given name and index.
*/
static int __snd_hda_jack_add_kctl(struct hda_codec *codec, hda_nid_t nid,
const char *name, int idx, bool phantom_jack)
{
struct hda_jack_tbl *jack;
struct snd_kcontrol *kctl;
int err, state;
jack = snd_hda_jack_tbl_new(codec, nid);
if (!jack)
return 0;
if (jack->kctl)
return 0; /* already created */
kctl = snd_kctl_jack_new(name, idx, codec);
if (!kctl)
return -ENOMEM;
err = snd_hda_ctl_add(codec, nid, kctl);
if (err < 0)
return err;
jack->kctl = kctl;
jack->phantom_jack = !!phantom_jack;
state = snd_hda_jack_detect(codec, nid);
snd_kctl_jack_report(codec->bus->card, kctl, state);
#ifdef CONFIG_SND_HDA_INPUT_JACK
if (!phantom_jack) {
jack->type = get_input_jack_type(codec, nid);
err = snd_jack_new(codec->bus->card, name, jack->type,
&jack->jack);
if (err < 0)
return err;
jack->jack->private_data = jack;
jack->jack->private_free = hda_free_jack_priv;
snd_jack_report(jack->jack, state ? jack->type : 0);
}
#endif
return 0;
}
int snd_hda_jack_add_kctl(struct hda_codec *codec, hda_nid_t nid,
const char *name, int idx)
{
return __snd_hda_jack_add_kctl(codec, nid, name, idx, false);
}
EXPORT_SYMBOL_GPL(snd_hda_jack_add_kctl);
/* get the unique index number for the given kctl name */
static int get_unique_index(struct hda_codec *codec, const char *name, int idx)
{
struct hda_jack_tbl *jack;
int i, len = strlen(name);
again:
jack = codec->jacktbl.list;
for (i = 0; i < codec->jacktbl.used; i++, jack++) {
/* jack->kctl.id contains "XXX Jack" name string with index */
if (jack->kctl &&
!strncmp(name, jack->kctl->id.name, len) &&
!strcmp(" Jack", jack->kctl->id.name + len) &&
jack->kctl->id.index == idx) {
idx++;
goto again;
}
}
return idx;
}
static int add_jack_kctl(struct hda_codec *codec, hda_nid_t nid,
const struct auto_pin_cfg *cfg,
const char *base_name)
{
unsigned int def_conf, conn;
char name[SNDRV_CTL_ELEM_ID_NAME_MAXLEN];
int idx, err;
bool phantom_jack;
if (!nid)
return 0;
def_conf = snd_hda_codec_get_pincfg(codec, nid);
conn = get_defcfg_connect(def_conf);
if (conn == AC_JACK_PORT_NONE)
return 0;
phantom_jack = (conn != AC_JACK_PORT_COMPLEX) ||
!is_jack_detectable(codec, nid);
if (base_name) {
strlcpy(name, base_name, sizeof(name));
idx = 0;
} else
snd_hda_get_pin_label(codec, nid, cfg, name, sizeof(name), &idx);
if (phantom_jack)
/* Example final name: "Internal Mic Phantom Jack" */
strncat(name, " Phantom", sizeof(name) - strlen(name) - 1);
idx = get_unique_index(codec, name, idx);
err = __snd_hda_jack_add_kctl(codec, nid, name, idx, phantom_jack);
if (err < 0)
return err;
if (!phantom_jack)
return snd_hda_jack_detect_enable(codec, nid);
return 0;
}
/**
* snd_hda_jack_add_kctls - Add kctls for all pins included in the given pincfg
*/
int snd_hda_jack_add_kctls(struct hda_codec *codec,
const struct auto_pin_cfg *cfg)
{
const hda_nid_t *p;
int i, err;
for (i = 0; i < cfg->num_inputs; i++) {
/* If we have headphone mics; make sure they get the right name
before grabbed by output pins */
if (cfg->inputs[i].is_headphone_mic) {
if (auto_cfg_hp_outs(cfg) == 1)
err = add_jack_kctl(codec, auto_cfg_hp_pins(cfg)[0],
cfg, "Headphone Mic");
else
err = add_jack_kctl(codec, cfg->inputs[i].pin,
cfg, "Headphone Mic");
} else
err = add_jack_kctl(codec, cfg->inputs[i].pin, cfg,
NULL);
if (err < 0)
return err;
}
for (i = 0, p = cfg->line_out_pins; i < cfg->line_outs; i++, p++) {
err = add_jack_kctl(codec, *p, cfg, NULL);
if (err < 0)
return err;
}
for (i = 0, p = cfg->hp_pins; i < cfg->hp_outs; i++, p++) {
if (*p == *cfg->line_out_pins) /* might be duplicated */
break;
err = add_jack_kctl(codec, *p, cfg, NULL);
if (err < 0)
return err;
}
for (i = 0, p = cfg->speaker_pins; i < cfg->speaker_outs; i++, p++) {
if (*p == *cfg->line_out_pins) /* might be duplicated */
break;
err = add_jack_kctl(codec, *p, cfg, NULL);
if (err < 0)
return err;
}
for (i = 0, p = cfg->dig_out_pins; i < cfg->dig_outs; i++, p++) {
err = add_jack_kctl(codec, *p, cfg, NULL);
if (err < 0)
return err;
}
err = add_jack_kctl(codec, cfg->dig_in_pin, cfg, NULL);
if (err < 0)
return err;
err = add_jack_kctl(codec, cfg->mono_out_pin, cfg, NULL);
if (err < 0)
return err;
return 0;
}
EXPORT_SYMBOL_GPL(snd_hda_jack_add_kctls);
static void call_jack_callback(struct hda_codec *codec,
struct hda_jack_tbl *jack)
{
struct hda_jack_callback *cb;
for (cb = jack->callback; cb; cb = cb->next)
cb->func(codec, cb);
if (jack->gated_jack) {
struct hda_jack_tbl *gated =
snd_hda_jack_tbl_get(codec, jack->gated_jack);
if (gated) {
for (cb = gated->callback; cb; cb = cb->next)
cb->func(codec, cb);
}
}
}
void snd_hda_jack_unsol_event(struct hda_codec *codec, unsigned int res)
{
struct hda_jack_tbl *event;
int tag = (res >> AC_UNSOL_RES_TAG_SHIFT) & 0x7f;
event = snd_hda_jack_tbl_get_from_tag(codec, tag);
if (!event)
return;
event->jack_dirty = 1;
call_jack_callback(codec, event);
snd_hda_jack_report_sync(codec);
}
EXPORT_SYMBOL_GPL(snd_hda_jack_unsol_event);
void snd_hda_jack_poll_all(struct hda_codec *codec)
{
struct hda_jack_tbl *jack = codec->jacktbl.list;
int i, changes = 0;
for (i = 0; i < codec->jacktbl.used; i++, jack++) {
unsigned int old_sense;
if (!jack->nid || !jack->jack_dirty || jack->phantom_jack)
continue;
old_sense = get_jack_plug_state(jack->pin_sense);
jack_detect_update(codec, jack);
if (old_sense == get_jack_plug_state(jack->pin_sense))
continue;
changes = 1;
call_jack_callback(codec, jack);
}
if (changes)
snd_hda_jack_report_sync(codec);
}
EXPORT_SYMBOL_GPL(snd_hda_jack_poll_all);

93
sound/pci/hda/hda_jack.h Normal file
View file

@ -0,0 +1,93 @@
/*
* Jack-detection handling for HD-audio
*
* Copyright (c) 2011 Takashi Iwai <tiwai@suse.de>
*
* This driver is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*/
#ifndef __SOUND_HDA_JACK_H
#define __SOUND_HDA_JACK_H
#include <linux/err.h>
struct auto_pin_cfg;
struct hda_jack_tbl;
struct hda_jack_callback;
typedef void (*hda_jack_callback_fn) (struct hda_codec *, struct hda_jack_callback *);
struct hda_jack_callback {
struct hda_jack_tbl *tbl;
hda_jack_callback_fn func;
unsigned int private_data; /* arbitrary data */
struct hda_jack_callback *next;
};
struct hda_jack_tbl {
hda_nid_t nid;
unsigned char tag; /* unsol event tag */
struct hda_jack_callback *callback;
/* jack-detection stuff */
unsigned int pin_sense; /* cached pin-sense value */
unsigned int jack_detect:1; /* capable of jack-detection? */
unsigned int jack_dirty:1; /* needs to update? */
unsigned int phantom_jack:1; /* a fixed, always present port? */
unsigned int block_report:1; /* in a transitional state - do not report to userspace */
hda_nid_t gating_jack; /* valid when gating jack plugged */
hda_nid_t gated_jack; /* gated is dependent on this jack */
struct snd_kcontrol *kctl; /* assigned kctl for jack-detection */
#ifdef CONFIG_SND_HDA_INPUT_JACK
int type;
struct snd_jack *jack;
#endif
};
struct hda_jack_tbl *
snd_hda_jack_tbl_get(struct hda_codec *codec, hda_nid_t nid);
struct hda_jack_tbl *
snd_hda_jack_tbl_get_from_tag(struct hda_codec *codec, unsigned char tag);
void snd_hda_jack_tbl_clear(struct hda_codec *codec);
void snd_hda_jack_set_dirty_all(struct hda_codec *codec);
int snd_hda_jack_detect_enable(struct hda_codec *codec, hda_nid_t nid);
struct hda_jack_callback *
snd_hda_jack_detect_enable_callback(struct hda_codec *codec, hda_nid_t nid,
hda_jack_callback_fn cb);
int snd_hda_jack_set_gating_jack(struct hda_codec *codec, hda_nid_t gated_nid,
hda_nid_t gating_nid);
u32 snd_hda_pin_sense(struct hda_codec *codec, hda_nid_t nid);
/* the jack state returned from snd_hda_jack_detect_state() */
enum {
HDA_JACK_NOT_PRESENT, HDA_JACK_PRESENT, HDA_JACK_PHANTOM,
};
int snd_hda_jack_detect_state(struct hda_codec *codec, hda_nid_t nid);
static inline bool snd_hda_jack_detect(struct hda_codec *codec, hda_nid_t nid)
{
return snd_hda_jack_detect_state(codec, nid) != HDA_JACK_NOT_PRESENT;
}
bool is_jack_detectable(struct hda_codec *codec, hda_nid_t nid);
int snd_hda_jack_add_kctl(struct hda_codec *codec, hda_nid_t nid,
const char *name, int idx);
int snd_hda_jack_add_kctls(struct hda_codec *codec,
const struct auto_pin_cfg *cfg);
void snd_hda_jack_report_sync(struct hda_codec *codec);
void snd_hda_jack_unsol_event(struct hda_codec *codec, unsigned int res);
void snd_hda_jack_poll_all(struct hda_codec *codec);
#endif /* __SOUND_HDA_JACK_H */

808
sound/pci/hda/hda_local.h Normal file
View file

@ -0,0 +1,808 @@
/*
* Universal Interface for Intel High Definition Audio Codec
*
* Local helper functions
*
* Copyright (c) 2004 Takashi Iwai <tiwai@suse.de>
*
* 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_LOCAL_H
#define __SOUND_HDA_LOCAL_H
/* We abuse kcontrol_new.subdev field to pass the NID corresponding to
* the given new control. If id.subdev has a bit flag HDA_SUBDEV_NID_FLAG,
* snd_hda_ctl_add() takes the lower-bit subdev value as a valid NID.
*
* Note that the subdevice field is cleared again before the real registration
* in snd_hda_ctl_add(), so that this value won't appear in the outside.
*/
#define HDA_SUBDEV_NID_FLAG (1U << 31)
#define HDA_SUBDEV_AMP_FLAG (1U << 30)
/*
* for mixer controls
*/
#define HDA_COMPOSE_AMP_VAL_OFS(nid,chs,idx,dir,ofs) \
((nid) | ((chs)<<16) | ((dir)<<18) | ((idx)<<19) | ((ofs)<<23))
#define HDA_AMP_VAL_MIN_MUTE (1<<29)
#define HDA_COMPOSE_AMP_VAL(nid,chs,idx,dir) \
HDA_COMPOSE_AMP_VAL_OFS(nid, chs, idx, dir, 0)
/* mono volume with index (index=0,1,...) (channel=1,2) */
#define HDA_CODEC_VOLUME_MONO_IDX(xname, xcidx, nid, channel, xindex, dir, flags) \
{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, .index = xcidx, \
.subdevice = HDA_SUBDEV_AMP_FLAG, \
.access = SNDRV_CTL_ELEM_ACCESS_READWRITE | \
SNDRV_CTL_ELEM_ACCESS_TLV_READ | \
SNDRV_CTL_ELEM_ACCESS_TLV_CALLBACK, \
.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 }, \
.private_value = HDA_COMPOSE_AMP_VAL(nid, channel, xindex, dir) | flags }
/* stereo volume with index */
#define HDA_CODEC_VOLUME_IDX(xname, xcidx, nid, xindex, direction) \
HDA_CODEC_VOLUME_MONO_IDX(xname, xcidx, nid, 3, xindex, direction, 0)
/* mono volume */
#define HDA_CODEC_VOLUME_MONO(xname, nid, channel, xindex, direction) \
HDA_CODEC_VOLUME_MONO_IDX(xname, 0, nid, channel, xindex, direction, 0)
/* stereo volume */
#define HDA_CODEC_VOLUME(xname, nid, xindex, direction) \
HDA_CODEC_VOLUME_MONO(xname, nid, 3, xindex, direction)
/* stereo volume with min=mute */
#define HDA_CODEC_VOLUME_MIN_MUTE(xname, nid, xindex, direction) \
HDA_CODEC_VOLUME_MONO_IDX(xname, 0, nid, 3, xindex, direction, \
HDA_AMP_VAL_MIN_MUTE)
/* mono mute switch with index (index=0,1,...) (channel=1,2) */
#define HDA_CODEC_MUTE_MONO_IDX(xname, xcidx, nid, channel, xindex, direction) \
{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, .index = xcidx, \
.subdevice = HDA_SUBDEV_AMP_FLAG, \
.info = snd_hda_mixer_amp_switch_info, \
.get = snd_hda_mixer_amp_switch_get, \
.put = snd_hda_mixer_amp_switch_put, \
.private_value = HDA_COMPOSE_AMP_VAL(nid, channel, xindex, direction) }
/* stereo mute switch with index */
#define HDA_CODEC_MUTE_IDX(xname, xcidx, nid, xindex, direction) \
HDA_CODEC_MUTE_MONO_IDX(xname, xcidx, nid, 3, xindex, direction)
/* mono mute switch */
#define HDA_CODEC_MUTE_MONO(xname, nid, channel, xindex, direction) \
HDA_CODEC_MUTE_MONO_IDX(xname, 0, nid, channel, xindex, direction)
/* stereo mute switch */
#define HDA_CODEC_MUTE(xname, nid, xindex, direction) \
HDA_CODEC_MUTE_MONO(xname, nid, 3, xindex, direction)
#ifdef CONFIG_SND_HDA_INPUT_BEEP
/* special beep mono mute switch with index (index=0,1,...) (channel=1,2) */
#define HDA_CODEC_MUTE_BEEP_MONO_IDX(xname, xcidx, nid, channel, xindex, direction) \
{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, .index = xcidx, \
.subdevice = HDA_SUBDEV_AMP_FLAG, \
.info = snd_hda_mixer_amp_switch_info, \
.get = snd_hda_mixer_amp_switch_get_beep, \
.put = snd_hda_mixer_amp_switch_put_beep, \
.private_value = HDA_COMPOSE_AMP_VAL(nid, channel, xindex, direction) }
#else
/* no digital beep - just the standard one */
#define HDA_CODEC_MUTE_BEEP_MONO_IDX(xname, xcidx, nid, ch, xidx, dir) \
HDA_CODEC_MUTE_MONO_IDX(xname, xcidx, nid, ch, xidx, dir)
#endif /* CONFIG_SND_HDA_INPUT_BEEP */
/* special beep mono mute switch */
#define HDA_CODEC_MUTE_BEEP_MONO(xname, nid, channel, xindex, direction) \
HDA_CODEC_MUTE_BEEP_MONO_IDX(xname, 0, nid, channel, xindex, direction)
/* special beep stereo mute switch */
#define HDA_CODEC_MUTE_BEEP(xname, nid, xindex, direction) \
HDA_CODEC_MUTE_BEEP_MONO(xname, nid, 3, xindex, direction)
extern const char *snd_hda_pcm_type_name[];
int snd_hda_mixer_amp_volume_info(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_info *uinfo);
int snd_hda_mixer_amp_volume_get(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol);
int snd_hda_mixer_amp_volume_put(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol);
int snd_hda_mixer_amp_tlv(struct snd_kcontrol *kcontrol, int op_flag,
unsigned int size, unsigned int __user *tlv);
int snd_hda_mixer_amp_switch_info(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_info *uinfo);
int snd_hda_mixer_amp_switch_get(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol);
int snd_hda_mixer_amp_switch_put(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol);
#ifdef CONFIG_SND_HDA_INPUT_BEEP
int snd_hda_mixer_amp_switch_get_beep(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol);
int snd_hda_mixer_amp_switch_put_beep(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol);
#endif
/* lowlevel accessor with caching; use carefully */
int snd_hda_codec_amp_read(struct hda_codec *codec, hda_nid_t nid, int ch,
int direction, int index);
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);
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);
void snd_hda_set_vmaster_tlv(struct hda_codec *codec, hda_nid_t nid, int dir,
unsigned int *tlv);
struct snd_kcontrol *snd_hda_find_mixer_ctl(struct hda_codec *codec,
const char *name);
int __snd_hda_add_vmaster(struct hda_codec *codec, char *name,
unsigned int *tlv, const char * const *slaves,
const char *suffix, bool init_slave_vol,
struct snd_kcontrol **ctl_ret);
#define snd_hda_add_vmaster(codec, name, tlv, slaves, suffix) \
__snd_hda_add_vmaster(codec, name, tlv, slaves, suffix, true, NULL)
int snd_hda_codec_reset(struct hda_codec *codec);
enum {
HDA_VMUTE_OFF,
HDA_VMUTE_ON,
HDA_VMUTE_FOLLOW_MASTER,
};
struct hda_vmaster_mute_hook {
/* below two fields must be filled by the caller of
* snd_hda_add_vmaster_hook() beforehand
*/
struct snd_kcontrol *sw_kctl;
void (*hook)(void *, int);
/* below are initialized automatically */
unsigned int mute_mode; /* HDA_VMUTE_XXX */
struct hda_codec *codec;
};
int snd_hda_add_vmaster_hook(struct hda_codec *codec,
struct hda_vmaster_mute_hook *hook,
bool expose_enum_ctl);
void snd_hda_sync_vmaster_hook(struct hda_vmaster_mute_hook *hook);
/* amp value bits */
#define HDA_AMP_MUTE 0x80
#define HDA_AMP_UNMUTE 0x00
#define HDA_AMP_VOLMASK 0x7f
/* mono switch binding multiple inputs */
#define HDA_BIND_MUTE_MONO(xname, nid, channel, indices, direction) \
{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, .index = 0, \
.info = snd_hda_mixer_amp_switch_info, \
.get = snd_hda_mixer_bind_switch_get, \
.put = snd_hda_mixer_bind_switch_put, \
.private_value = HDA_COMPOSE_AMP_VAL(nid, channel, indices, direction) }
/* stereo switch binding multiple inputs */
#define HDA_BIND_MUTE(xname,nid,indices,dir) \
HDA_BIND_MUTE_MONO(xname,nid,3,indices,dir)
int snd_hda_mixer_bind_switch_get(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol);
int snd_hda_mixer_bind_switch_put(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol);
/* more generic bound controls */
struct hda_ctl_ops {
snd_kcontrol_info_t *info;
snd_kcontrol_get_t *get;
snd_kcontrol_put_t *put;
snd_kcontrol_tlv_rw_t *tlv;
};
extern struct hda_ctl_ops snd_hda_bind_vol; /* for bind-volume with TLV */
extern struct hda_ctl_ops snd_hda_bind_sw; /* for bind-switch */
struct hda_bind_ctls {
struct hda_ctl_ops *ops;
unsigned long values[];
};
int snd_hda_mixer_bind_ctls_info(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_info *uinfo);
int snd_hda_mixer_bind_ctls_get(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol);
int snd_hda_mixer_bind_ctls_put(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol);
int snd_hda_mixer_bind_tlv(struct snd_kcontrol *kcontrol, int op_flag,
unsigned int size, unsigned int __user *tlv);
#define HDA_BIND_VOL(xname, bindrec) \
{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, \
.name = xname, \
.access = SNDRV_CTL_ELEM_ACCESS_READWRITE |\
SNDRV_CTL_ELEM_ACCESS_TLV_READ |\
SNDRV_CTL_ELEM_ACCESS_TLV_CALLBACK,\
.info = snd_hda_mixer_bind_ctls_info,\
.get = snd_hda_mixer_bind_ctls_get,\
.put = snd_hda_mixer_bind_ctls_put,\
.tlv = { .c = snd_hda_mixer_bind_tlv },\
.private_value = (long) (bindrec) }
#define HDA_BIND_SW(xname, bindrec) \
{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER,\
.name = xname, \
.info = snd_hda_mixer_bind_ctls_info,\
.get = snd_hda_mixer_bind_ctls_get,\
.put = snd_hda_mixer_bind_ctls_put,\
.private_value = (long) (bindrec) }
/*
* SPDIF I/O
*/
int snd_hda_create_dig_out_ctls(struct hda_codec *codec,
hda_nid_t associated_nid,
hda_nid_t cvt_nid, int type);
#define snd_hda_create_spdif_out_ctls(codec, anid, cnid) \
snd_hda_create_dig_out_ctls(codec, anid, cnid, HDA_PCM_TYPE_SPDIF)
int snd_hda_create_spdif_in_ctls(struct hda_codec *codec, hda_nid_t nid);
/*
* input MUX helper
*/
#define HDA_MAX_NUM_INPUTS 16
struct hda_input_mux_item {
char label[32];
unsigned int index;
};
struct hda_input_mux {
unsigned int num_items;
struct hda_input_mux_item items[HDA_MAX_NUM_INPUTS];
};
int snd_hda_input_mux_info(const struct hda_input_mux *imux,
struct snd_ctl_elem_info *uinfo);
int snd_hda_input_mux_put(struct hda_codec *codec,
const struct hda_input_mux *imux,
struct snd_ctl_elem_value *ucontrol, hda_nid_t nid,
unsigned int *cur_val);
int snd_hda_add_imux_item(struct hda_codec *codec,
struct hda_input_mux *imux, const char *label,
int index, int *type_index_ret);
/*
* Channel mode helper
*/
struct hda_channel_mode {
int channels;
const struct hda_verb *sequence;
};
int snd_hda_ch_mode_info(struct hda_codec *codec,
struct snd_ctl_elem_info *uinfo,
const struct hda_channel_mode *chmode,
int num_chmodes);
int snd_hda_ch_mode_get(struct hda_codec *codec,
struct snd_ctl_elem_value *ucontrol,
const struct hda_channel_mode *chmode,
int num_chmodes,
int max_channels);
int snd_hda_ch_mode_put(struct hda_codec *codec,
struct snd_ctl_elem_value *ucontrol,
const struct hda_channel_mode *chmode,
int num_chmodes,
int *max_channelsp);
/*
* Multi-channel / digital-out PCM helper
*/
enum { HDA_FRONT, HDA_REAR, HDA_CLFE, HDA_SIDE }; /* index for dac_nidx */
enum { HDA_DIG_NONE, HDA_DIG_EXCLUSIVE, HDA_DIG_ANALOG_DUP }; /* dig_out_used */
#define HDA_MAX_OUTS 5
struct hda_multi_out {
int num_dacs; /* # of DACs, must be more than 1 */
const hda_nid_t *dac_nids; /* DAC list */
hda_nid_t hp_nid; /* optional DAC for HP, 0 when not exists */
hda_nid_t hp_out_nid[HDA_MAX_OUTS]; /* DACs for multiple HPs */
hda_nid_t extra_out_nid[HDA_MAX_OUTS]; /* other (e.g. speaker) DACs */
hda_nid_t dig_out_nid; /* digital out audio widget */
const hda_nid_t *slave_dig_outs;
int max_channels; /* currently supported analog channels */
int dig_out_used; /* current usage of digital out (HDA_DIG_XXX) */
int no_share_stream; /* don't share a stream with multiple pins */
int share_spdif; /* share SPDIF pin */
/* PCM information for both analog and SPDIF DACs */
unsigned int analog_rates;
unsigned int analog_maxbps;
u64 analog_formats;
unsigned int spdif_rates;
unsigned int spdif_maxbps;
u64 spdif_formats;
};
int snd_hda_create_spdif_share_sw(struct hda_codec *codec,
struct hda_multi_out *mout);
int snd_hda_multi_out_dig_open(struct hda_codec *codec,
struct hda_multi_out *mout);
int snd_hda_multi_out_dig_close(struct hda_codec *codec,
struct hda_multi_out *mout);
int snd_hda_multi_out_dig_prepare(struct hda_codec *codec,
struct hda_multi_out *mout,
unsigned int stream_tag,
unsigned int format,
struct snd_pcm_substream *substream);
int snd_hda_multi_out_dig_cleanup(struct hda_codec *codec,
struct hda_multi_out *mout);
int snd_hda_multi_out_analog_open(struct hda_codec *codec,
struct hda_multi_out *mout,
struct snd_pcm_substream *substream,
struct hda_pcm_stream *hinfo);
int snd_hda_multi_out_analog_prepare(struct hda_codec *codec,
struct hda_multi_out *mout,
unsigned int stream_tag,
unsigned int format,
struct snd_pcm_substream *substream);
int snd_hda_multi_out_analog_cleanup(struct hda_codec *codec,
struct hda_multi_out *mout);
/*
* generic codec parser
*/
int snd_hda_parse_generic_codec(struct hda_codec *codec);
int snd_hda_parse_hdmi_codec(struct hda_codec *codec);
/*
* generic proc interface
*/
#ifdef CONFIG_PROC_FS
int snd_hda_codec_proc_new(struct hda_codec *codec);
#else
static inline int snd_hda_codec_proc_new(struct hda_codec *codec) { return 0; }
#endif
#define SND_PRINT_BITS_ADVISED_BUFSIZE 16
void snd_print_pcm_bits(int pcm, char *buf, int buflen);
/*
* Misc
*/
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, {0, 0}} \
}
#else
#define SND_HDA_PIN_QUIRK(_codec, _subvendor, _name, _value, _pins...) \
{ .codec = _codec,\
.subvendor = _subvendor,\
.value = _value,\
.pins = (const struct hda_pintbl[]) { _pins, {0, 0}} \
}
#endif
#define HDA_FIXUP_ID_NOT_SET -1
#define HDA_FIXUP_ID_NO_FIXUP -2
/* 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
*/
#define HDA_UNSOL_QUEUE_SIZE 64
struct hda_bus_unsolicited {
/* ring buffer */
u32 queue[HDA_UNSOL_QUEUE_SIZE * 2];
unsigned int rp, wp;
/* workqueue */
struct work_struct work;
struct hda_bus *bus;
};
/* helper macros to retrieve pin default-config values */
#define get_defcfg_connect(cfg) \
((cfg & AC_DEFCFG_PORT_CONN) >> AC_DEFCFG_PORT_CONN_SHIFT)
#define get_defcfg_association(cfg) \
((cfg & AC_DEFCFG_DEF_ASSOC) >> AC_DEFCFG_ASSOC_SHIFT)
#define get_defcfg_location(cfg) \
((cfg & AC_DEFCFG_LOCATION) >> AC_DEFCFG_LOCATION_SHIFT)
#define get_defcfg_sequence(cfg) \
(cfg & AC_DEFCFG_SEQUENCE)
#define get_defcfg_device(cfg) \
((cfg & AC_DEFCFG_DEVICE) >> AC_DEFCFG_DEVICE_SHIFT)
#define get_defcfg_misc(cfg) \
((cfg & AC_DEFCFG_MISC) >> AC_DEFCFG_MISC_SHIFT)
/* amp values */
#define AMP_IN_MUTE(idx) (0x7080 | ((idx)<<8))
#define AMP_IN_UNMUTE(idx) (0x7000 | ((idx)<<8))
#define AMP_OUT_MUTE 0xb080
#define AMP_OUT_UNMUTE 0xb000
#define AMP_OUT_ZERO 0xb000
/* pinctl values */
#define PIN_IN (AC_PINCTL_IN_EN)
#define PIN_VREFHIZ (AC_PINCTL_IN_EN | AC_PINCTL_VREF_HIZ)
#define PIN_VREF50 (AC_PINCTL_IN_EN | AC_PINCTL_VREF_50)
#define PIN_VREFGRD (AC_PINCTL_IN_EN | AC_PINCTL_VREF_GRD)
#define PIN_VREF80 (AC_PINCTL_IN_EN | AC_PINCTL_VREF_80)
#define PIN_VREF100 (AC_PINCTL_IN_EN | AC_PINCTL_VREF_100)
#define PIN_OUT (AC_PINCTL_OUT_EN)
#define PIN_HP (AC_PINCTL_OUT_EN | AC_PINCTL_HP_EN)
#define PIN_HP_AMP (AC_PINCTL_HP_EN)
unsigned int snd_hda_get_default_vref(struct hda_codec *codec, hda_nid_t pin);
unsigned int snd_hda_correct_pin_ctl(struct hda_codec *codec,
hda_nid_t pin, unsigned int val);
int _snd_hda_set_pin_ctl(struct hda_codec *codec, hda_nid_t pin,
unsigned int val, bool cached);
/**
* _snd_hda_set_pin_ctl - Set a pin-control value safely
* @codec: the codec instance
* @pin: the pin NID to set the control
* @val: the pin-control value (AC_PINCTL_* bits)
*
* This function sets the pin-control value to the given pin, but
* filters out the invalid pin-control bits when the pin has no such
* capabilities. For example, when PIN_HP is passed but the pin has no
* HP-drive capability, the HP bit is omitted.
*
* The function doesn't check the input VREF capability bits, though.
* Use snd_hda_get_default_vref() to guess the right value.
* Also, this function is only for analog pins, not for HDMI pins.
*/
static inline int
snd_hda_set_pin_ctl(struct hda_codec *codec, hda_nid_t pin, unsigned int val)
{
return _snd_hda_set_pin_ctl(codec, pin, val, false);
}
/**
* snd_hda_set_pin_ctl_cache - Set a pin-control value safely
* @codec: the codec instance
* @pin: the pin NID to set the control
* @val: the pin-control value (AC_PINCTL_* bits)
*
* Just like snd_hda_set_pin_ctl() but write to cache as well.
*/
static inline int
snd_hda_set_pin_ctl_cache(struct hda_codec *codec, hda_nid_t pin,
unsigned int val)
{
return _snd_hda_set_pin_ctl(codec, pin, val, true);
}
int snd_hda_codec_get_pin_target(struct hda_codec *codec, hda_nid_t nid);
int snd_hda_codec_set_pin_target(struct hda_codec *codec, hda_nid_t nid,
unsigned int val);
/*
* get widget capabilities
*/
static inline u32 get_wcaps(struct hda_codec *codec, hda_nid_t nid)
{
if (nid < codec->start_nid ||
nid >= codec->start_nid + codec->num_nodes)
return 0;
return codec->wcaps[nid - codec->start_nid];
}
/* get the widget type from widget capability bits */
static inline int get_wcaps_type(unsigned int wcaps)
{
if (!wcaps)
return -1; /* invalid type */
return (wcaps & AC_WCAP_TYPE) >> AC_WCAP_TYPE_SHIFT;
}
static inline unsigned int get_wcaps_channels(u32 wcaps)
{
unsigned int chans;
chans = (wcaps & AC_WCAP_CHAN_CNT_EXT) >> 13;
chans = ((chans << 1) | 1) + 1;
return chans;
}
static inline void snd_hda_override_wcaps(struct hda_codec *codec,
hda_nid_t nid, u32 val)
{
if (nid >= codec->start_nid &&
nid < codec->start_nid + codec->num_nodes)
codec->wcaps[nid - codec->start_nid] = val;
}
u32 query_amp_caps(struct hda_codec *codec, hda_nid_t nid, int direction);
int snd_hda_override_amp_caps(struct hda_codec *codec, hda_nid_t nid, int dir,
unsigned int caps);
u32 snd_hda_query_pin_caps(struct hda_codec *codec, hda_nid_t nid);
int snd_hda_override_pin_caps(struct hda_codec *codec, hda_nid_t nid,
unsigned int caps);
bool snd_hda_check_amp_caps(struct hda_codec *codec, hda_nid_t nid,
int dir, unsigned int bits);
#define nid_has_mute(codec, nid, dir) \
snd_hda_check_amp_caps(codec, nid, dir, (AC_AMPCAP_MUTE | AC_AMPCAP_MIN_MUTE))
#define nid_has_volume(codec, nid, dir) \
snd_hda_check_amp_caps(codec, nid, dir, AC_AMPCAP_NUM_STEPS)
/* flags for hda_nid_item */
#define HDA_NID_ITEM_AMP (1<<0)
struct hda_nid_item {
struct snd_kcontrol *kctl;
unsigned int index;
hda_nid_t nid;
unsigned short flags;
};
int snd_hda_ctl_add(struct hda_codec *codec, hda_nid_t nid,
struct snd_kcontrol *kctl);
int snd_hda_add_nid(struct hda_codec *codec, struct snd_kcontrol *kctl,
unsigned int index, hda_nid_t nid);
void snd_hda_ctls_clear(struct hda_codec *codec);
/*
* hwdep interface
*/
#ifdef CONFIG_SND_HDA_HWDEP
int snd_hda_create_hwdep(struct hda_codec *codec);
#else
static inline int snd_hda_create_hwdep(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);
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)
{
return NULL;
}
static inline
int snd_hda_get_bool_hint(struct hda_codec *codec, const char *key)
{
return -ENOENT;
}
static inline
int snd_hda_get_int_hint(struct hda_codec *codec, const char *key, int *valp)
{
return -ENOENT;
}
#endif
/*
* power-management
*/
void snd_hda_schedule_power_save(struct hda_codec *codec);
struct hda_amp_list {
hda_nid_t nid;
unsigned char dir;
unsigned char idx;
};
struct hda_loopback_check {
const struct hda_amp_list *amplist;
int power_on;
};
int snd_hda_check_amp_list_power(struct hda_codec *codec,
struct hda_loopback_check *check,
hda_nid_t nid);
/* 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
*/
/* retrieve parameters from private_value */
#define get_amp_nid_(pv) ((pv) & 0xffff)
#define get_amp_nid(kc) get_amp_nid_((kc)->private_value)
#define get_amp_channels(kc) (((kc)->private_value >> 16) & 0x3)
#define get_amp_direction_(pv) (((pv) >> 18) & 0x1)
#define get_amp_direction(kc) get_amp_direction_((kc)->private_value)
#define get_amp_index_(pv) (((pv) >> 19) & 0xf)
#define get_amp_index(kc) get_amp_index_((kc)->private_value)
#define get_amp_offset(kc) (((kc)->private_value >> 23) & 0x3f)
#define get_amp_min_mute(kc) (((kc)->private_value >> 29) & 0x1)
/*
* enum control helper
*/
int snd_hda_enum_helper_info(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_info *uinfo,
int num_entries, const char * const *texts);
#define snd_hda_enum_bool_helper_info(kcontrol, uinfo) \
snd_hda_enum_helper_info(kcontrol, uinfo, 0, NULL)
/*
* CEA Short Audio Descriptor data
*/
struct cea_sad {
int channels;
int format; /* (format == 0) indicates invalid SAD */
int rates;
int sample_bits; /* for LPCM */
int max_bitrate; /* for AC3...ATRAC */
int profile; /* for WMAPRO */
};
#define ELD_FIXED_BYTES 20
#define ELD_MAX_SIZE 256
#define ELD_MAX_MNL 16
#define ELD_MAX_SAD 16
/*
* ELD: EDID Like Data
*/
struct parsed_hdmi_eld {
/*
* all fields will be cleared before updating ELD
*/
int baseline_len;
int eld_ver;
int cea_edid_ver;
char monitor_name[ELD_MAX_MNL + 1];
int manufacture_id;
int product_id;
u64 port_id;
int support_hdcp;
int support_ai;
int conn_type;
int aud_synch_delay;
int spk_alloc;
int sad_count;
struct cea_sad sad[ELD_MAX_SAD];
};
struct hdmi_eld {
bool monitor_present;
bool eld_valid;
int eld_size;
char eld_buffer[ELD_MAX_SIZE];
struct parsed_hdmi_eld info;
};
int snd_hdmi_get_eld_size(struct hda_codec *codec, hda_nid_t nid);
int snd_hdmi_get_eld(struct hda_codec *codec, hda_nid_t nid,
unsigned char *buf, int *eld_size);
int snd_hdmi_parse_eld(struct hda_codec *codec, struct parsed_hdmi_eld *e,
const unsigned char *buf, int size);
void snd_hdmi_show_eld(struct hda_codec *codec, 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
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 */

399
sound/pci/hda/hda_priv.h Normal file
View file

@ -0,0 +1,399 @@
/*
* 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 AZX_REG_GCAP 0x00
#define AZX_GCAP_64OK (1 << 0) /* 64bit address support */
#define AZX_GCAP_NSDO (3 << 1) /* # of serial data out signals */
#define AZX_GCAP_BSS (31 << 3) /* # of bidirectional streams */
#define AZX_GCAP_ISS (15 << 8) /* # of input streams */
#define AZX_GCAP_OSS (15 << 12) /* # of output streams */
#define AZX_REG_VMIN 0x02
#define AZX_REG_VMAJ 0x03
#define AZX_REG_OUTPAY 0x04
#define AZX_REG_INPAY 0x06
#define AZX_REG_GCTL 0x08
#define AZX_GCTL_RESET (1 << 0) /* controller reset */
#define AZX_GCTL_FCNTRL (1 << 1) /* flush control */
#define AZX_GCTL_UNSOL (1 << 8) /* accept unsol. response enable */
#define AZX_REG_WAKEEN 0x0c
#define AZX_REG_STATESTS 0x0e
#define AZX_REG_GSTS 0x10
#define AZX_GSTS_FSTS (1 << 1) /* flush status */
#define AZX_REG_INTCTL 0x20
#define AZX_REG_INTSTS 0x24
#define AZX_REG_WALLCLK 0x30 /* 24Mhz source */
#define AZX_REG_OLD_SSYNC 0x34 /* SSYNC for old ICH */
#define AZX_REG_SSYNC 0x38
#define AZX_REG_CORBLBASE 0x40
#define AZX_REG_CORBUBASE 0x44
#define AZX_REG_CORBWP 0x48
#define AZX_REG_CORBRP 0x4a
#define AZX_CORBRP_RST (1 << 15) /* read pointer reset */
#define AZX_REG_CORBCTL 0x4c
#define AZX_CORBCTL_RUN (1 << 1) /* enable DMA */
#define AZX_CORBCTL_CMEIE (1 << 0) /* enable memory error irq */
#define AZX_REG_CORBSTS 0x4d
#define AZX_CORBSTS_CMEI (1 << 0) /* memory error indication */
#define AZX_REG_CORBSIZE 0x4e
#define AZX_REG_RIRBLBASE 0x50
#define AZX_REG_RIRBUBASE 0x54
#define AZX_REG_RIRBWP 0x58
#define AZX_RIRBWP_RST (1 << 15) /* write pointer reset */
#define AZX_REG_RINTCNT 0x5a
#define AZX_REG_RIRBCTL 0x5c
#define AZX_RBCTL_IRQ_EN (1 << 0) /* enable IRQ */
#define AZX_RBCTL_DMA_EN (1 << 1) /* enable DMA */
#define AZX_RBCTL_OVERRUN_EN (1 << 2) /* enable overrun irq */
#define AZX_REG_RIRBSTS 0x5d
#define AZX_RBSTS_IRQ (1 << 0) /* response irq */
#define AZX_RBSTS_OVERRUN (1 << 2) /* overrun irq */
#define AZX_REG_RIRBSIZE 0x5e
#define AZX_REG_IC 0x60
#define AZX_REG_IR 0x64
#define AZX_REG_IRS 0x68
#define AZX_IRS_VALID (1<<1)
#define AZX_IRS_BUSY (1<<0)
#define AZX_REG_DPLBASE 0x70
#define AZX_REG_DPUBASE 0x74
#define AZX_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 AZX_REG_SD_CTL 0x00
#define AZX_REG_SD_STS 0x03
#define AZX_REG_SD_LPIB 0x04
#define AZX_REG_SD_CBL 0x08
#define AZX_REG_SD_LVI 0x0c
#define AZX_REG_SD_FIFOW 0x0e
#define AZX_REG_SD_FIFOSIZE 0x10
#define AZX_REG_SD_FORMAT 0x12
#define AZX_REG_SD_BDLPL 0x18
#define AZX_REG_SD_BDLPU 0x1c
/* PCI space */
#define AZX_PCIREG_TCSEL 0x44
/*
* other constants
*/
/* 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 AZX_INT_ALL_STREAM 0xff /* all stream interrupts */
#define AZX_INT_CTRL_EN 0x40000000 /* controller interrupt enable bit */
#define AZX_INT_GLOBAL_EN 0x80000000 /* global interrupt enable bit */
/* below are so far hardcoded - should read registers in future */
#define AZX_MAX_CORB_ENTRIES 256
#define AZX_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 */
#define AZX_DCAPS_NO_MSI64 (1 << 29) /* Stick to 32-bit MSIs */
/* 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;
};
typedef unsigned int (*azx_get_pos_callback_t)(struct azx *, struct azx_dev *);
typedef int (*azx_get_delay_callback_t)(struct azx *, struct azx_dev *, unsigned int pos);
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;
/* position adjustment callbacks */
azx_get_pos_callback_t get_position[2];
azx_get_delay_callback_t get_delay[2];
/* 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 */
/* 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 */
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 probing:1; /* codec probing phase */
unsigned int snoop:1;
unsigned int align_buffer_size:1;
unsigned int region_requested:1;
unsigned int disabled:1; /* disabled by VGA-switcher */
/* for debugging */
unsigned int last_cmd[AZX_MAX_CODECS];
/* reboot notifier (for mysterious hangup problem at power-down) */
struct notifier_block reboot_notifier;
#ifdef CONFIG_SND_HDA_DSP_LOADER
struct azx_dev saved_azx_dev;
#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 + AZX_REG_##reg))
#define azx_readl(chip, reg) \
((chip)->ops->reg_readl((chip)->remap_addr + AZX_REG_##reg))
#define azx_writew(chip, reg, value) \
((chip)->ops->reg_writew(value, (chip)->remap_addr + AZX_REG_##reg))
#define azx_readw(chip, reg) \
((chip)->ops->reg_readw((chip)->remap_addr + AZX_REG_##reg))
#define azx_writeb(chip, reg, value) \
((chip)->ops->reg_writeb(value, (chip)->remap_addr + AZX_REG_##reg))
#define azx_readb(chip, reg) \
((chip)->ops->reg_readb((chip)->remap_addr + AZX_REG_##reg))
#define azx_sd_writel(chip, dev, reg, value) \
((chip)->ops->reg_writel(value, (dev)->sd_addr + AZX_REG_##reg))
#define azx_sd_readl(chip, dev, reg) \
((chip)->ops->reg_readl((dev)->sd_addr + AZX_REG_##reg))
#define azx_sd_writew(chip, dev, reg, value) \
((chip)->ops->reg_writew(value, (dev)->sd_addr + AZX_REG_##reg))
#define azx_sd_readw(chip, dev, reg) \
((chip)->ops->reg_readw((dev)->sd_addr + AZX_REG_##reg))
#define azx_sd_writeb(chip, dev, reg, value) \
((chip)->ops->reg_writeb(value, (dev)->sd_addr + AZX_REG_##reg))
#define azx_sd_readb(chip, dev, reg) \
((chip)->ops->reg_readb((dev)->sd_addr + AZX_REG_##reg))
#endif /* __SOUND_HDA_PRIV_H */

871
sound/pci/hda/hda_proc.c Normal file
View file

@ -0,0 +1,871 @@
/*
* Universal Interface for Intel High Definition Audio Codec
*
* Generic proc interface
*
* Copyright (c) 2004 Takashi Iwai <tiwai@suse.de>
*
*
* This driver is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This driver is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
#include <linux/init.h>
#include <linux/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;
static char buf[128];
for (i = 0, n = 0; i < size; i++) {
if (bits & (1U<<i) && names[i])
n += snprintf(buf + n, sizeof(buf) - n, " %s",
names[i]);
}
buf[n] = '\0';
return buf;
}
static const char *get_wid_type_name(unsigned int wid_value)
{
static char *names[16] = {
[AC_WID_AUD_OUT] = "Audio Output",
[AC_WID_AUD_IN] = "Audio Input",
[AC_WID_AUD_MIX] = "Audio Mixer",
[AC_WID_AUD_SEL] = "Audio Selector",
[AC_WID_PIN] = "Pin Complex",
[AC_WID_POWER] = "Power Widget",
[AC_WID_VOL_KNB] = "Volume Knob Widget",
[AC_WID_BEEP] = "Beep Generator Widget",
[AC_WID_VENDOR] = "Vendor Defined Widget",
};
if (wid_value == -1)
return "UNKNOWN Widget";
wid_value &= 0xf;
if (names[wid_value])
return names[wid_value];
else
return "UNKNOWN Widget";
}
static void print_nid_array(struct snd_info_buffer *buffer,
struct hda_codec *codec, hda_nid_t nid,
struct snd_array *array)
{
int i;
struct hda_nid_item *items = array->list, *item;
struct snd_kcontrol *kctl;
for (i = 0; i < array->used; i++) {
item = &items[i];
if (item->nid == nid) {
kctl = item->kctl;
snd_iprintf(buffer,
" Control: name=\"%s\", index=%i, device=%i\n",
kctl->id.name, kctl->id.index + item->index,
kctl->id.device);
if (item->flags & HDA_NID_ITEM_AMP)
snd_iprintf(buffer,
" ControlAmp: chs=%lu, dir=%s, "
"idx=%lu, ofs=%lu\n",
get_amp_channels(kctl),
get_amp_direction(kctl) ? "Out" : "In",
get_amp_index(kctl),
get_amp_offset(kctl));
}
}
}
static void print_nid_pcms(struct snd_info_buffer *buffer,
struct hda_codec *codec, hda_nid_t nid)
{
int pcm, type;
struct hda_pcm *cpcm;
for (pcm = 0; pcm < codec->num_pcms; pcm++) {
cpcm = &codec->pcm_info[pcm];
for (type = 0; type < 2; type++) {
if (cpcm->stream[type].nid != nid || cpcm->pcm == NULL)
continue;
snd_iprintf(buffer, " Device: name=\"%s\", "
"type=\"%s\", device=%i\n",
cpcm->name,
snd_hda_pcm_type_name[cpcm->pcm_type],
cpcm->pcm->device);
}
}
}
static void print_amp_caps(struct snd_info_buffer *buffer,
struct hda_codec *codec, hda_nid_t nid, int dir)
{
unsigned int caps;
caps = snd_hda_param_read(codec, nid,
dir == HDA_OUTPUT ?
AC_PAR_AMP_OUT_CAP : AC_PAR_AMP_IN_CAP);
if (caps == -1 || caps == 0) {
snd_iprintf(buffer, "N/A\n");
return;
}
snd_iprintf(buffer, "ofs=0x%02x, nsteps=0x%02x, stepsize=0x%02x, "
"mute=%x\n",
caps & AC_AMPCAP_OFFSET,
(caps & AC_AMPCAP_NUM_STEPS) >> AC_AMPCAP_NUM_STEPS_SHIFT,
(caps & AC_AMPCAP_STEP_SIZE) >> AC_AMPCAP_STEP_SIZE_SHIFT,
(caps & AC_AMPCAP_MUTE) >> AC_AMPCAP_MUTE_SHIFT);
}
/* is this a stereo widget or a stereo-to-mono mix? */
static bool is_stereo_amps(struct hda_codec *codec, hda_nid_t nid,
int dir, unsigned int wcaps, int indices)
{
hda_nid_t conn;
if (wcaps & AC_WCAP_STEREO)
return true;
/* check for a stereo-to-mono mix; it must be:
* only a single connection, only for input, and only a mixer widget
*/
if (indices != 1 || dir != HDA_INPUT ||
get_wcaps_type(wcaps) != AC_WID_AUD_MIX)
return false;
if (snd_hda_get_raw_connections(codec, nid, &conn, 1) < 0)
return false;
/* the connection source is a stereo? */
wcaps = snd_hda_param_read(codec, conn, AC_PAR_AUDIO_WIDGET_CAP);
return !!(wcaps & AC_WCAP_STEREO);
}
static void print_amp_vals(struct snd_info_buffer *buffer,
struct hda_codec *codec, hda_nid_t nid,
int dir, unsigned int wcaps, int indices)
{
unsigned int val;
bool stereo;
int i;
stereo = is_stereo_amps(codec, nid, dir, wcaps, indices);
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_RIGHT | dir | i);
snd_iprintf(buffer, " 0x%02x", val);
}
snd_iprintf(buffer, "]");
}
snd_iprintf(buffer, "\n");
}
static void print_pcm_rates(struct snd_info_buffer *buffer, unsigned int pcm)
{
static unsigned int rates[] = {
8000, 11025, 16000, 22050, 32000, 44100, 48000, 88200,
96000, 176400, 192000, 384000
};
int i;
pcm &= AC_SUPPCM_RATES;
snd_iprintf(buffer, " rates [0x%x]:", pcm);
for (i = 0; i < ARRAY_SIZE(rates); i++)
if (pcm & (1 << i))
snd_iprintf(buffer, " %d", rates[i]);
snd_iprintf(buffer, "\n");
}
static void print_pcm_bits(struct snd_info_buffer *buffer, unsigned int pcm)
{
char buf[SND_PRINT_BITS_ADVISED_BUFSIZE];
snd_iprintf(buffer, " bits [0x%x]:", (pcm >> 16) & 0xff);
snd_print_pcm_bits(pcm, buf, sizeof(buf));
snd_iprintf(buffer, "%s\n", buf);
}
static void print_pcm_formats(struct snd_info_buffer *buffer,
unsigned int streams)
{
snd_iprintf(buffer, " formats [0x%x]:", streams & 0xf);
if (streams & AC_SUPFMT_PCM)
snd_iprintf(buffer, " PCM");
if (streams & AC_SUPFMT_FLOAT32)
snd_iprintf(buffer, " FLOAT");
if (streams & AC_SUPFMT_AC3)
snd_iprintf(buffer, " AC3");
snd_iprintf(buffer, "\n");
}
static void print_pcm_caps(struct snd_info_buffer *buffer,
struct hda_codec *codec, hda_nid_t nid)
{
unsigned int pcm = snd_hda_param_read(codec, nid, AC_PAR_PCM);
unsigned int stream = snd_hda_param_read(codec, nid, AC_PAR_STREAM);
if (pcm == -1 || stream == -1) {
snd_iprintf(buffer, "N/A\n");
return;
}
print_pcm_rates(buffer, pcm);
print_pcm_bits(buffer, pcm);
print_pcm_formats(buffer, stream);
}
static const char *get_jack_connection(u32 cfg)
{
static char *names[16] = {
"Unknown", "1/8", "1/4", "ATAPI",
"RCA", "Optical","Digital", "Analog",
"DIN", "XLR", "RJ11", "Comb",
NULL, NULL, NULL, "Other"
};
cfg = (cfg & AC_DEFCFG_CONN_TYPE) >> AC_DEFCFG_CONN_TYPE_SHIFT;
if (names[cfg])
return names[cfg];
else
return "UNKNOWN";
}
static const char *get_jack_color(u32 cfg)
{
static char *names[16] = {
"Unknown", "Black", "Grey", "Blue",
"Green", "Red", "Orange", "Yellow",
"Purple", "Pink", NULL, NULL,
NULL, NULL, "White", "Other",
};
cfg = (cfg & AC_DEFCFG_COLOR) >> AC_DEFCFG_COLOR_SHIFT;
if (names[cfg])
return names[cfg];
else
return "UNKNOWN";
}
static void print_pin_caps(struct snd_info_buffer *buffer,
struct hda_codec *codec, hda_nid_t nid,
int *supports_vref)
{
static char *jack_conns[4] = { "Jack", "N/A", "Fixed", "Both" };
unsigned int caps, val;
caps = snd_hda_param_read(codec, nid, AC_PAR_PIN_CAP);
snd_iprintf(buffer, " Pincap 0x%08x:", caps);
if (caps & AC_PINCAP_IN)
snd_iprintf(buffer, " IN");
if (caps & AC_PINCAP_OUT)
snd_iprintf(buffer, " OUT");
if (caps & AC_PINCAP_HP_DRV)
snd_iprintf(buffer, " HP");
if (caps & AC_PINCAP_EAPD)
snd_iprintf(buffer, " EAPD");
if (caps & AC_PINCAP_PRES_DETECT)
snd_iprintf(buffer, " Detect");
if (caps & AC_PINCAP_BALANCE)
snd_iprintf(buffer, " Balanced");
if (caps & AC_PINCAP_HDMI) {
/* Realtek uses this bit as a different meaning */
if ((codec->vendor_id >> 16) == 0x10ec)
snd_iprintf(buffer, " R/L");
else {
if (caps & AC_PINCAP_HBR)
snd_iprintf(buffer, " HBR");
snd_iprintf(buffer, " HDMI");
}
}
if (caps & AC_PINCAP_DP)
snd_iprintf(buffer, " DP");
if (caps & AC_PINCAP_TRIG_REQ)
snd_iprintf(buffer, " Trigger");
if (caps & AC_PINCAP_IMP_SENSE)
snd_iprintf(buffer, " ImpSense");
snd_iprintf(buffer, "\n");
if (caps & AC_PINCAP_VREF) {
unsigned int vref =
(caps & AC_PINCAP_VREF) >> AC_PINCAP_VREF_SHIFT;
snd_iprintf(buffer, " Vref caps:");
if (vref & AC_PINCAP_VREF_HIZ)
snd_iprintf(buffer, " HIZ");
if (vref & AC_PINCAP_VREF_50)
snd_iprintf(buffer, " 50");
if (vref & AC_PINCAP_VREF_GRD)
snd_iprintf(buffer, " GRD");
if (vref & AC_PINCAP_VREF_80)
snd_iprintf(buffer, " 80");
if (vref & AC_PINCAP_VREF_100)
snd_iprintf(buffer, " 100");
snd_iprintf(buffer, "\n");
*supports_vref = 1;
} else
*supports_vref = 0;
if (caps & AC_PINCAP_EAPD) {
val = snd_hda_codec_read(codec, nid, 0,
AC_VERB_GET_EAPD_BTLENABLE, 0);
snd_iprintf(buffer, " EAPD 0x%x:", val);
if (val & AC_EAPDBTL_BALANCED)
snd_iprintf(buffer, " BALANCED");
if (val & AC_EAPDBTL_EAPD)
snd_iprintf(buffer, " EAPD");
if (val & AC_EAPDBTL_LR_SWAP)
snd_iprintf(buffer, " R/L");
snd_iprintf(buffer, "\n");
}
caps = snd_hda_codec_read(codec, nid, 0, AC_VERB_GET_CONFIG_DEFAULT, 0);
snd_iprintf(buffer, " Pin Default 0x%08x: [%s] %s at %s %s\n", caps,
jack_conns[(caps & AC_DEFCFG_PORT_CONN) >> AC_DEFCFG_PORT_CONN_SHIFT],
snd_hda_get_jack_type(caps),
snd_hda_get_jack_connectivity(caps),
snd_hda_get_jack_location(caps));
snd_iprintf(buffer, " Conn = %s, Color = %s\n",
get_jack_connection(caps),
get_jack_color(caps));
/* Default association and sequence values refer to default grouping
* of pin complexes and their sequence within the group. This is used
* for priority and resource allocation.
*/
snd_iprintf(buffer, " DefAssociation = 0x%x, Sequence = 0x%x\n",
(caps & AC_DEFCFG_DEF_ASSOC) >> AC_DEFCFG_ASSOC_SHIFT,
caps & AC_DEFCFG_SEQUENCE);
if (((caps & AC_DEFCFG_MISC) >> AC_DEFCFG_MISC_SHIFT) &
AC_DEFCFG_MISC_NO_PRESENCE) {
/* Miscellaneous bit indicates external hardware does not
* support presence detection even if the pin complex
* indicates it is supported.
*/
snd_iprintf(buffer, " Misc = NO_PRESENCE\n");
}
}
static void print_pin_ctls(struct snd_info_buffer *buffer,
struct hda_codec *codec, hda_nid_t nid,
int supports_vref)
{
unsigned int pinctls;
pinctls = snd_hda_codec_read(codec, nid, 0,
AC_VERB_GET_PIN_WIDGET_CONTROL, 0);
snd_iprintf(buffer, " Pin-ctls: 0x%02x:", pinctls);
if (pinctls & AC_PINCTL_IN_EN)
snd_iprintf(buffer, " IN");
if (pinctls & AC_PINCTL_OUT_EN)
snd_iprintf(buffer, " OUT");
if (pinctls & AC_PINCTL_HP_EN)
snd_iprintf(buffer, " HP");
if (supports_vref) {
int vref = pinctls & AC_PINCTL_VREFEN;
switch (vref) {
case AC_PINCTL_VREF_HIZ:
snd_iprintf(buffer, " VREF_HIZ");
break;
case AC_PINCTL_VREF_50:
snd_iprintf(buffer, " VREF_50");
break;
case AC_PINCTL_VREF_GRD:
snd_iprintf(buffer, " VREF_GRD");
break;
case AC_PINCTL_VREF_80:
snd_iprintf(buffer, " VREF_80");
break;
case AC_PINCTL_VREF_100:
snd_iprintf(buffer, " VREF_100");
break;
}
}
snd_iprintf(buffer, "\n");
}
static void print_vol_knob(struct snd_info_buffer *buffer,
struct hda_codec *codec, hda_nid_t nid)
{
unsigned int cap = snd_hda_param_read(codec, nid,
AC_PAR_VOL_KNB_CAP);
snd_iprintf(buffer, " Volume-Knob: delta=%d, steps=%d, ",
(cap >> 7) & 1, cap & 0x7f);
cap = snd_hda_codec_read(codec, nid, 0,
AC_VERB_GET_VOLUME_KNOB_CONTROL, 0);
snd_iprintf(buffer, "direct=%d, val=%d\n",
(cap >> 7) & 1, cap & 0x7f);
}
static void print_audio_io(struct snd_info_buffer *buffer,
struct hda_codec *codec, hda_nid_t nid,
unsigned int wid_type)
{
int conv = snd_hda_codec_read(codec, nid, 0, AC_VERB_GET_CONV, 0);
snd_iprintf(buffer,
" Converter: stream=%d, channel=%d\n",
(conv & AC_CONV_STREAM) >> AC_CONV_STREAM_SHIFT,
conv & AC_CONV_CHANNEL);
if (wid_type == AC_WID_AUD_IN && (conv & AC_CONV_CHANNEL) == 0) {
int sdi = snd_hda_codec_read(codec, nid, 0,
AC_VERB_GET_SDI_SELECT, 0);
snd_iprintf(buffer, " SDI-Select: %d\n",
sdi & AC_SDI_SELECT);
}
}
static void print_digital_conv(struct snd_info_buffer *buffer,
struct hda_codec *codec, hda_nid_t nid)
{
unsigned int digi1 = snd_hda_codec_read(codec, nid, 0,
AC_VERB_GET_DIGI_CONVERT_1, 0);
unsigned char digi2 = digi1 >> 8;
unsigned char digi3 = digi1 >> 16;
snd_iprintf(buffer, " Digital:");
if (digi1 & AC_DIG1_ENABLE)
snd_iprintf(buffer, " Enabled");
if (digi1 & AC_DIG1_V)
snd_iprintf(buffer, " Validity");
if (digi1 & AC_DIG1_VCFG)
snd_iprintf(buffer, " ValidityCfg");
if (digi1 & AC_DIG1_EMPHASIS)
snd_iprintf(buffer, " Preemphasis");
if (digi1 & AC_DIG1_COPYRIGHT)
snd_iprintf(buffer, " Non-Copyright");
if (digi1 & AC_DIG1_NONAUDIO)
snd_iprintf(buffer, " Non-Audio");
if (digi1 & AC_DIG1_PROFESSIONAL)
snd_iprintf(buffer, " Pro");
if (digi1 & AC_DIG1_LEVEL)
snd_iprintf(buffer, " GenLevel");
if (digi3 & AC_DIG3_KAE)
snd_iprintf(buffer, " KAE");
snd_iprintf(buffer, "\n");
snd_iprintf(buffer, " Digital category: 0x%x\n",
digi2 & AC_DIG2_CC);
snd_iprintf(buffer, " IEC Coding Type: 0x%x\n",
digi3 & AC_DIG3_ICT);
}
static const char *get_pwr_state(u32 state)
{
static const char * const buf[] = {
"D0", "D1", "D2", "D3", "D3cold"
};
if (state < ARRAY_SIZE(buf))
return buf[state];
return "UNKNOWN";
}
static void print_power_state(struct snd_info_buffer *buffer,
struct hda_codec *codec, hda_nid_t nid)
{
static char *names[] = {
[ilog2(AC_PWRST_D0SUP)] = "D0",
[ilog2(AC_PWRST_D1SUP)] = "D1",
[ilog2(AC_PWRST_D2SUP)] = "D2",
[ilog2(AC_PWRST_D3SUP)] = "D3",
[ilog2(AC_PWRST_D3COLDSUP)] = "D3cold",
[ilog2(AC_PWRST_S3D3COLDSUP)] = "S3D3cold",
[ilog2(AC_PWRST_CLKSTOP)] = "CLKSTOP",
[ilog2(AC_PWRST_EPSS)] = "EPSS",
};
int sup = snd_hda_param_read(codec, nid, AC_PAR_POWER_STATE);
int pwr = snd_hda_codec_read(codec, nid, 0,
AC_VERB_GET_POWER_STATE, 0);
if (sup != -1)
snd_iprintf(buffer, " Power states: %s\n",
bits_names(sup, names, ARRAY_SIZE(names)));
snd_iprintf(buffer, " Power: setting=%s, actual=%s",
get_pwr_state(pwr & AC_PWRST_SETTING),
get_pwr_state((pwr & AC_PWRST_ACTUAL) >>
AC_PWRST_ACTUAL_SHIFT));
if (pwr & AC_PWRST_ERROR)
snd_iprintf(buffer, ", Error");
if (pwr & AC_PWRST_CLK_STOP_OK)
snd_iprintf(buffer, ", Clock-stop-OK");
if (pwr & AC_PWRST_SETTING_RESET)
snd_iprintf(buffer, ", Setting-reset");
snd_iprintf(buffer, "\n");
}
static void print_unsol_cap(struct snd_info_buffer *buffer,
struct hda_codec *codec, hda_nid_t nid)
{
int unsol = snd_hda_codec_read(codec, nid, 0,
AC_VERB_GET_UNSOLICITED_RESPONSE, 0);
snd_iprintf(buffer,
" Unsolicited: tag=%02x, enabled=%d\n",
unsol & AC_UNSOL_TAG,
(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, 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,
struct hda_codec *codec, hda_nid_t nid,
unsigned int wid_type, hda_nid_t *conn,
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 &&
wid_type != AC_WID_VOL_KNB &&
wid_type != AC_WID_POWER)
curr = snd_hda_codec_read(codec, nid, 0,
AC_VERB_GET_CONNECT_SEL, 0);
snd_iprintf(buffer, " Connection: %d\n", conn_len);
if (conn_len > 0) {
snd_iprintf(buffer, " ");
for (c = 0; c < conn_len; c++) {
snd_iprintf(buffer, " 0x%02x", conn[c]);
if (c == curr)
snd_iprintf(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,
struct hda_codec *codec, hda_nid_t nid)
{
unsigned int gpio =
snd_hda_param_read(codec, codec->afg, AC_PAR_GPIO_CAP);
unsigned int enable, direction, wake, unsol, sticky, data;
int i, max;
snd_iprintf(buffer, "GPIO: io=%d, o=%d, i=%d, "
"unsolicited=%d, wake=%d\n",
gpio & AC_GPIO_IO_COUNT,
(gpio & AC_GPIO_O_COUNT) >> AC_GPIO_O_COUNT_SHIFT,
(gpio & AC_GPIO_I_COUNT) >> AC_GPIO_I_COUNT_SHIFT,
(gpio & AC_GPIO_UNSOLICITED) ? 1 : 0,
(gpio & AC_GPIO_WAKE) ? 1 : 0);
max = gpio & AC_GPIO_IO_COUNT;
if (!max || max > 8)
return;
enable = snd_hda_codec_read(codec, nid, 0,
AC_VERB_GET_GPIO_MASK, 0);
direction = snd_hda_codec_read(codec, nid, 0,
AC_VERB_GET_GPIO_DIRECTION, 0);
wake = snd_hda_codec_read(codec, nid, 0,
AC_VERB_GET_GPIO_WAKE_MASK, 0);
unsol = snd_hda_codec_read(codec, nid, 0,
AC_VERB_GET_GPIO_UNSOLICITED_RSP_MASK, 0);
sticky = snd_hda_codec_read(codec, nid, 0,
AC_VERB_GET_GPIO_STICKY_MASK, 0);
data = snd_hda_codec_read(codec, nid, 0,
AC_VERB_GET_GPIO_DATA, 0);
for (i = 0; i < max; ++i)
snd_iprintf(buffer,
" IO[%d]: enable=%d, dir=%d, wake=%d, "
"sticky=%d, data=%d, unsol=%d\n", i,
(enable & (1<<i)) ? 1 : 0,
(direction & (1<<i)) ? 1 : 0,
(wake & (1<<i)) ? 1 : 0,
(sticky & (1<<i)) ? 1 : 0,
(data & (1<<i)) ? 1 : 0,
(unsol & (1<<i)) ? 1 : 0);
/* FIXME: add GPO and GPI pin information */
print_nid_array(buffer, codec, nid, &codec->mixers);
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)
{
struct hda_codec *codec = entry->private_data;
hda_nid_t nid;
int i, nodes;
snd_iprintf(buffer, "Codec: ");
if (codec->vendor_name && codec->chip_name)
snd_iprintf(buffer, "%s %s\n",
codec->vendor_name, codec->chip_name);
else
snd_iprintf(buffer, "Not Set\n");
snd_iprintf(buffer, "Address: %d\n", codec->addr);
if (codec->afg)
snd_iprintf(buffer, "AFG Function Id: 0x%x (unsol %u)\n",
codec->afg_function_id, codec->afg_unsol);
if (codec->mfg)
snd_iprintf(buffer, "MFG Function Id: 0x%x (unsol %u)\n",
codec->mfg_function_id, codec->mfg_unsol);
snd_iprintf(buffer, "Vendor Id: 0x%08x\n", codec->vendor_id);
snd_iprintf(buffer, "Subsystem Id: 0x%08x\n", codec->subsystem_id);
snd_iprintf(buffer, "Revision Id: 0x%x\n", codec->revision_id);
if (codec->mfg)
snd_iprintf(buffer, "Modem Function Group: 0x%x\n", codec->mfg);
else
snd_iprintf(buffer, "No Modem Function Group found\n");
if (! codec->afg)
return;
snd_hda_power_up(codec);
snd_iprintf(buffer, "Default PCM:\n");
print_pcm_caps(buffer, codec, codec->afg);
snd_iprintf(buffer, "Default Amp-In caps: ");
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) {
snd_iprintf(buffer, "Invalid AFG subtree\n");
snd_hda_power_down(codec);
return;
}
print_gpio(buffer, codec, codec->afg);
if (codec->proc_widget_hook)
codec->proc_widget_hook(buffer, codec, codec->afg);
for (i = 0; i < nodes; i++, nid++) {
unsigned int wid_caps =
snd_hda_param_read(codec, nid,
AC_PAR_AUDIO_WIDGET_CAP);
unsigned int wid_type = get_wcaps_type(wid_caps);
hda_nid_t *conn = NULL;
int conn_len = 0;
snd_iprintf(buffer, "Node 0x%02x [%s] wcaps 0x%x:", nid,
get_wid_type_name(wid_type), wid_caps);
if (wid_caps & AC_WCAP_STEREO) {
unsigned int chans = get_wcaps_channels(wid_caps);
if (chans == 2)
snd_iprintf(buffer, " Stereo");
else
snd_iprintf(buffer, " %d-Channels", chans);
} else
snd_iprintf(buffer, " Mono");
if (wid_caps & AC_WCAP_DIGITAL)
snd_iprintf(buffer, " Digital");
if (wid_caps & AC_WCAP_IN_AMP)
snd_iprintf(buffer, " Amp-In");
if (wid_caps & AC_WCAP_OUT_AMP)
snd_iprintf(buffer, " Amp-Out");
if (wid_caps & AC_WCAP_STRIPE)
snd_iprintf(buffer, " Stripe");
if (wid_caps & AC_WCAP_LR_SWAP)
snd_iprintf(buffer, " R/L");
if (wid_caps & AC_WCAP_CP_CAPS)
snd_iprintf(buffer, " CP");
snd_iprintf(buffer, "\n");
print_nid_array(buffer, codec, nid, &codec->mixers);
print_nid_array(buffer, codec, nid, &codec->nids);
print_nid_pcms(buffer, codec, nid);
/* volume knob is a special widget that always have connection
* list
*/
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_num_raw_conns(codec, nid);
if (conn_len > 0) {
conn = kmalloc(sizeof(hda_nid_t) * conn_len,
GFP_KERNEL);
if (!conn)
return;
if (snd_hda_get_raw_connections(codec, nid, conn,
conn_len) < 0)
conn_len = 0;
}
}
if (wid_caps & AC_WCAP_IN_AMP) {
snd_iprintf(buffer, " Amp-In caps: ");
print_amp_caps(buffer, codec, nid, HDA_INPUT);
snd_iprintf(buffer, " Amp-In vals: ");
if (wid_type == AC_WID_PIN ||
(codec->single_adc_amp &&
wid_type == AC_WID_AUD_IN))
print_amp_vals(buffer, codec, nid, HDA_INPUT,
wid_caps, 1);
else
print_amp_vals(buffer, codec, nid, HDA_INPUT,
wid_caps, conn_len);
}
if (wid_caps & AC_WCAP_OUT_AMP) {
snd_iprintf(buffer, " Amp-Out caps: ");
print_amp_caps(buffer, codec, nid, HDA_OUTPUT);
snd_iprintf(buffer, " Amp-Out vals: ");
if (wid_type == AC_WID_PIN &&
codec->pin_amp_workaround)
print_amp_vals(buffer, codec, nid, HDA_OUTPUT,
wid_caps, conn_len);
else
print_amp_vals(buffer, codec, nid, HDA_OUTPUT,
wid_caps, 1);
}
switch (wid_type) {
case AC_WID_PIN: {
int supports_vref;
print_pin_caps(buffer, codec, nid, &supports_vref);
print_pin_ctls(buffer, codec, nid, supports_vref);
break;
}
case AC_WID_VOL_KNB:
print_vol_knob(buffer, codec, nid);
break;
case AC_WID_AUD_OUT:
case AC_WID_AUD_IN:
print_audio_io(buffer, codec, nid, wid_type);
if (wid_caps & AC_WCAP_DIGITAL)
print_digital_conv(buffer, codec, nid);
if (wid_caps & AC_WCAP_FORMAT_OVRD) {
snd_iprintf(buffer, " PCM:\n");
print_pcm_caps(buffer, codec, nid);
}
break;
}
if (wid_caps & AC_WCAP_UNSOL_CAP)
print_unsol_cap(buffer, codec, nid);
if (wid_caps & AC_WCAP_POWER)
print_power_state(buffer, codec, nid);
if (wid_caps & AC_WCAP_DELAY)
snd_iprintf(buffer, " Delay: %d samples\n",
(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);
if (wid_caps & AC_WCAP_PROC_WID)
print_proc_caps(buffer, codec, nid);
if (codec->proc_widget_hook)
codec->proc_widget_hook(buffer, codec, nid);
kfree(conn);
}
snd_hda_power_down(codec);
}
/*
* create a proc read
*/
int snd_hda_codec_proc_new(struct hda_codec *codec)
{
char name[32];
struct snd_info_entry *entry;
int err;
snprintf(name, sizeof(name), "codec#%d", codec->addr);
err = snd_card_proc_new(codec->bus->card, name, &entry);
if (err < 0)
return err;
snd_info_set_text_ops(entry, codec, print_codec_info);
return 0;
}

780
sound/pci/hda/hda_sysfs.c Normal file
View file

@ -0,0 +1,780 @@
/*
* sysfs interface for HD-audio codec
*
* Copyright (c) 2014 Takashi Iwai <tiwai@suse.de>
*
* split from hda_hwdep.c
*/
#include <linux/init.h>
#include <linux/slab.h>
#include <linux/compat.h>
#include <linux/mutex.h>
#include <linux/ctype.h>
#include <linux/string.h>
#include <linux/export.h>
#include <sound/core.h>
#include "hda_codec.h"
#include "hda_local.h"
#include <sound/hda_hwdep.h>
#include <sound/minors.h>
/* hint string pair */
struct hda_hint {
const char *key;
const char *val; /* contained in the same alloc as key */
};
#ifdef CONFIG_PM
static ssize_t power_on_acct_show(struct device *dev,
struct device_attribute *attr,
char *buf)
{
struct hda_codec *codec = dev_get_drvdata(dev);
snd_hda_update_power_acct(codec);
return sprintf(buf, "%u\n", jiffies_to_msecs(codec->power_on_acct));
}
static ssize_t power_off_acct_show(struct device *dev,
struct device_attribute *attr,
char *buf)
{
struct hda_codec *codec = dev_get_drvdata(dev);
snd_hda_update_power_acct(codec);
return sprintf(buf, "%u\n", jiffies_to_msecs(codec->power_off_acct));
}
static DEVICE_ATTR_RO(power_on_acct);
static DEVICE_ATTR_RO(power_off_acct);
#endif /* CONFIG_PM */
#define CODEC_INFO_SHOW(type) \
static ssize_t type##_show(struct device *dev, \
struct device_attribute *attr, \
char *buf) \
{ \
struct hda_codec *codec = dev_get_drvdata(dev); \
return sprintf(buf, "0x%x\n", codec->type); \
}
#define CODEC_INFO_STR_SHOW(type) \
static ssize_t type##_show(struct device *dev, \
struct device_attribute *attr, \
char *buf) \
{ \
struct hda_codec *codec = dev_get_drvdata(dev); \
return sprintf(buf, "%s\n", \
codec->type ? codec->type : ""); \
}
CODEC_INFO_SHOW(vendor_id);
CODEC_INFO_SHOW(subsystem_id);
CODEC_INFO_SHOW(revision_id);
CODEC_INFO_SHOW(afg);
CODEC_INFO_SHOW(mfg);
CODEC_INFO_STR_SHOW(vendor_name);
CODEC_INFO_STR_SHOW(chip_name);
CODEC_INFO_STR_SHOW(modelname);
static ssize_t pin_configs_show(struct hda_codec *codec,
struct snd_array *list,
char *buf)
{
int i, len = 0;
mutex_lock(&codec->user_mutex);
for (i = 0; i < list->used; i++) {
struct hda_pincfg *pin = snd_array_elem(list, i);
len += sprintf(buf + len, "0x%02x 0x%08x\n",
pin->nid, pin->cfg);
}
mutex_unlock(&codec->user_mutex);
return len;
}
static ssize_t init_pin_configs_show(struct device *dev,
struct device_attribute *attr,
char *buf)
{
struct hda_codec *codec = dev_get_drvdata(dev);
return pin_configs_show(codec, &codec->init_pins, buf);
}
static ssize_t driver_pin_configs_show(struct device *dev,
struct device_attribute *attr,
char *buf)
{
struct hda_codec *codec = dev_get_drvdata(dev);
return pin_configs_show(codec, &codec->driver_pins, buf);
}
#ifdef CONFIG_SND_HDA_RECONFIG
/*
* sysfs interface
*/
static int clear_codec(struct hda_codec *codec)
{
int err;
err = snd_hda_codec_reset(codec);
if (err < 0) {
codec_err(codec, "The codec is being used, can't free.\n");
return err;
}
snd_hda_sysfs_clear(codec);
return 0;
}
static int reconfig_codec(struct hda_codec *codec)
{
int err;
snd_hda_power_up(codec);
codec_info(codec, "hda-codec: reconfiguring\n");
err = snd_hda_codec_reset(codec);
if (err < 0) {
codec_err(codec,
"The codec is being used, can't reconfigure.\n");
goto error;
}
err = snd_hda_codec_configure(codec);
if (err < 0)
goto error;
/* rebuild PCMs */
err = snd_hda_codec_build_pcms(codec);
if (err < 0)
goto error;
/* rebuild mixers */
err = snd_hda_codec_build_controls(codec);
if (err < 0)
goto error;
err = snd_card_register(codec->bus->card);
error:
snd_hda_power_down(codec);
return err;
}
/*
* allocate a string at most len chars, and remove the trailing EOL
*/
static char *kstrndup_noeol(const char *src, size_t len)
{
char *s = kstrndup(src, len, GFP_KERNEL);
char *p;
if (!s)
return NULL;
p = strchr(s, '\n');
if (p)
*p = 0;
return s;
}
#define CODEC_INFO_STORE(type) \
static ssize_t type##_store(struct device *dev, \
struct device_attribute *attr, \
const char *buf, size_t count) \
{ \
struct hda_codec *codec = dev_get_drvdata(dev); \
unsigned long val; \
int err = kstrtoul(buf, 0, &val); \
if (err < 0) \
return err; \
codec->type = val; \
return count; \
}
#define CODEC_INFO_STR_STORE(type) \
static ssize_t type##_store(struct device *dev, \
struct device_attribute *attr, \
const char *buf, size_t count) \
{ \
struct hda_codec *codec = dev_get_drvdata(dev); \
char *s = kstrndup_noeol(buf, 64); \
if (!s) \
return -ENOMEM; \
kfree(codec->type); \
codec->type = s; \
return count; \
}
CODEC_INFO_STORE(vendor_id);
CODEC_INFO_STORE(subsystem_id);
CODEC_INFO_STORE(revision_id);
CODEC_INFO_STR_STORE(vendor_name);
CODEC_INFO_STR_STORE(chip_name);
CODEC_INFO_STR_STORE(modelname);
#define CODEC_ACTION_STORE(type) \
static ssize_t type##_store(struct device *dev, \
struct device_attribute *attr, \
const char *buf, size_t count) \
{ \
struct hda_codec *codec = dev_get_drvdata(dev); \
int err = 0; \
if (*buf) \
err = type##_codec(codec); \
return err < 0 ? err : count; \
}
CODEC_ACTION_STORE(reconfig);
CODEC_ACTION_STORE(clear);
static ssize_t init_verbs_show(struct device *dev,
struct device_attribute *attr,
char *buf)
{
struct hda_codec *codec = dev_get_drvdata(dev);
int i, len = 0;
mutex_lock(&codec->user_mutex);
for (i = 0; i < codec->init_verbs.used; i++) {
struct hda_verb *v = snd_array_elem(&codec->init_verbs, i);
len += snprintf(buf + len, PAGE_SIZE - len,
"0x%02x 0x%03x 0x%04x\n",
v->nid, v->verb, v->param);
}
mutex_unlock(&codec->user_mutex);
return len;
}
static int parse_init_verbs(struct hda_codec *codec, const char *buf)
{
struct hda_verb *v;
int nid, verb, param;
if (sscanf(buf, "%i %i %i", &nid, &verb, &param) != 3)
return -EINVAL;
if (!nid || !verb)
return -EINVAL;
mutex_lock(&codec->user_mutex);
v = snd_array_new(&codec->init_verbs);
if (!v) {
mutex_unlock(&codec->user_mutex);
return -ENOMEM;
}
v->nid = nid;
v->verb = verb;
v->param = param;
mutex_unlock(&codec->user_mutex);
return 0;
}
static ssize_t init_verbs_store(struct device *dev,
struct device_attribute *attr,
const char *buf, size_t count)
{
struct hda_codec *codec = dev_get_drvdata(dev);
int err = parse_init_verbs(codec, buf);
if (err < 0)
return err;
return count;
}
static ssize_t hints_show(struct device *dev,
struct device_attribute *attr,
char *buf)
{
struct hda_codec *codec = dev_get_drvdata(dev);
int i, len = 0;
mutex_lock(&codec->user_mutex);
for (i = 0; i < codec->hints.used; i++) {
struct hda_hint *hint = snd_array_elem(&codec->hints, i);
len += snprintf(buf + len, PAGE_SIZE - len,
"%s = %s\n", hint->key, hint->val);
}
mutex_unlock(&codec->user_mutex);
return len;
}
static struct hda_hint *get_hint(struct hda_codec *codec, const char *key)
{
int i;
for (i = 0; i < codec->hints.used; i++) {
struct hda_hint *hint = snd_array_elem(&codec->hints, i);
if (!strcmp(hint->key, key))
return hint;
}
return NULL;
}
static void remove_trail_spaces(char *str)
{
char *p;
if (!*str)
return;
p = str + strlen(str) - 1;
for (; isspace(*p); p--) {
*p = 0;
if (p == str)
return;
}
}
#define MAX_HINTS 1024
static int parse_hints(struct hda_codec *codec, const char *buf)
{
char *key, *val;
struct hda_hint *hint;
int err = 0;
buf = skip_spaces(buf);
if (!*buf || *buf == '#' || *buf == '\n')
return 0;
if (*buf == '=')
return -EINVAL;
key = kstrndup_noeol(buf, 1024);
if (!key)
return -ENOMEM;
/* extract key and val */
val = strchr(key, '=');
if (!val) {
kfree(key);
return -EINVAL;
}
*val++ = 0;
val = skip_spaces(val);
remove_trail_spaces(key);
remove_trail_spaces(val);
mutex_lock(&codec->user_mutex);
hint = get_hint(codec, key);
if (hint) {
/* replace */
kfree(hint->key);
hint->key = key;
hint->val = val;
goto unlock;
}
/* allocate a new hint entry */
if (codec->hints.used >= MAX_HINTS)
hint = NULL;
else
hint = snd_array_new(&codec->hints);
if (hint) {
hint->key = key;
hint->val = val;
} else {
err = -ENOMEM;
}
unlock:
mutex_unlock(&codec->user_mutex);
if (err)
kfree(key);
return err;
}
static ssize_t hints_store(struct device *dev,
struct device_attribute *attr,
const char *buf, size_t count)
{
struct hda_codec *codec = dev_get_drvdata(dev);
int err = parse_hints(codec, buf);
if (err < 0)
return err;
return count;
}
static ssize_t user_pin_configs_show(struct device *dev,
struct device_attribute *attr,
char *buf)
{
struct hda_codec *codec = dev_get_drvdata(dev);
return pin_configs_show(codec, &codec->user_pins, buf);
}
#define MAX_PIN_CONFIGS 32
static int parse_user_pin_configs(struct hda_codec *codec, const char *buf)
{
int nid, cfg, err;
if (sscanf(buf, "%i %i", &nid, &cfg) != 2)
return -EINVAL;
if (!nid)
return -EINVAL;
mutex_lock(&codec->user_mutex);
err = snd_hda_add_pincfg(codec, &codec->user_pins, nid, cfg);
mutex_unlock(&codec->user_mutex);
return err;
}
static ssize_t user_pin_configs_store(struct device *dev,
struct device_attribute *attr,
const char *buf, size_t count)
{
struct hda_codec *codec = dev_get_drvdata(dev);
int err = parse_user_pin_configs(codec, buf);
if (err < 0)
return err;
return count;
}
/* sysfs attributes exposed only when CONFIG_SND_HDA_RECONFIG=y */
static DEVICE_ATTR_RW(init_verbs);
static DEVICE_ATTR_RW(hints);
static DEVICE_ATTR_RW(user_pin_configs);
static DEVICE_ATTR_WO(reconfig);
static DEVICE_ATTR_WO(clear);
/*
* Look for hint string
*/
const char *snd_hda_get_hint(struct hda_codec *codec, const char *key)
{
struct hda_hint *hint = get_hint(codec, key);
return hint ? hint->val : NULL;
}
EXPORT_SYMBOL_GPL(snd_hda_get_hint);
int snd_hda_get_bool_hint(struct hda_codec *codec, const char *key)
{
const char *p;
int ret;
mutex_lock(&codec->user_mutex);
p = snd_hda_get_hint(codec, key);
if (!p || !*p)
ret = -ENOENT;
else {
switch (toupper(*p)) {
case 'T': /* true */
case 'Y': /* yes */
case '1':
ret = 1;
break;
default:
ret = 0;
break;
}
}
mutex_unlock(&codec->user_mutex);
return ret;
}
EXPORT_SYMBOL_GPL(snd_hda_get_bool_hint);
int snd_hda_get_int_hint(struct hda_codec *codec, const char *key, int *valp)
{
const char *p;
unsigned long val;
int ret;
mutex_lock(&codec->user_mutex);
p = snd_hda_get_hint(codec, key);
if (!p)
ret = -ENOENT;
else if (kstrtoul(p, 0, &val))
ret = -EINVAL;
else {
*valp = val;
ret = 0;
}
mutex_unlock(&codec->user_mutex);
return ret;
}
EXPORT_SYMBOL_GPL(snd_hda_get_int_hint);
#endif /* CONFIG_SND_HDA_RECONFIG */
/*
* common sysfs attributes
*/
#ifdef CONFIG_SND_HDA_RECONFIG
#define RECONFIG_DEVICE_ATTR(name) DEVICE_ATTR_RW(name)
#else
#define RECONFIG_DEVICE_ATTR(name) DEVICE_ATTR_RO(name)
#endif
static RECONFIG_DEVICE_ATTR(vendor_id);
static RECONFIG_DEVICE_ATTR(subsystem_id);
static RECONFIG_DEVICE_ATTR(revision_id);
static DEVICE_ATTR_RO(afg);
static DEVICE_ATTR_RO(mfg);
static RECONFIG_DEVICE_ATTR(vendor_name);
static RECONFIG_DEVICE_ATTR(chip_name);
static RECONFIG_DEVICE_ATTR(modelname);
static DEVICE_ATTR_RO(init_pin_configs);
static DEVICE_ATTR_RO(driver_pin_configs);
#ifdef CONFIG_SND_HDA_PATCH_LOADER
/* parser mode */
enum {
LINE_MODE_NONE,
LINE_MODE_CODEC,
LINE_MODE_MODEL,
LINE_MODE_PINCFG,
LINE_MODE_VERB,
LINE_MODE_HINT,
LINE_MODE_VENDOR_ID,
LINE_MODE_SUBSYSTEM_ID,
LINE_MODE_REVISION_ID,
LINE_MODE_CHIP_NAME,
NUM_LINE_MODES,
};
static inline int strmatch(const char *a, const char *b)
{
return strncasecmp(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
}

556
sound/pci/hda/hda_tegra.c Normal file
View file

@ -0,0 +1,556 @@
/*
*
* 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/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);
hda_tegra_enable_clocks(hda);
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)
};
/*
* destructor
*/
static int hda_tegra_dev_free(struct snd_device *device)
{
int i;
struct azx *chip = device->device_data;
azx_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);
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);
azx_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");

143
sound/pci/hda/hda_trace.h Normal file
View file

@ -0,0 +1,143 @@
#undef TRACE_SYSTEM
#define TRACE_SYSTEM hda
#define TRACE_INCLUDE_FILE hda_trace
#if !defined(_TRACE_HDA_H) || defined(TRACE_HEADER_MULTI_READ)
#define _TRACE_HDA_H
#include <linux/tracepoint.h>
struct hda_bus;
struct hda_codec;
DECLARE_EVENT_CLASS(hda_cmd,
TP_PROTO(struct hda_codec *codec, unsigned int val),
TP_ARGS(codec, val),
TP_STRUCT__entry(
__field( unsigned int, card )
__field( unsigned int, addr )
__field( unsigned int, val )
),
TP_fast_assign(
__entry->card = (codec)->bus->card->number;
__entry->addr = (codec)->addr;
__entry->val = (val);
),
TP_printk("[%d:%d] val=%x", __entry->card, __entry->addr, __entry->val)
);
DEFINE_EVENT(hda_cmd, hda_send_cmd,
TP_PROTO(struct hda_codec *codec, unsigned int val),
TP_ARGS(codec, val)
);
DEFINE_EVENT(hda_cmd, hda_get_response,
TP_PROTO(struct hda_codec *codec, unsigned int val),
TP_ARGS(codec, val)
);
TRACE_EVENT(hda_bus_reset,
TP_PROTO(struct hda_bus *bus),
TP_ARGS(bus),
TP_STRUCT__entry(
__field( unsigned int, card )
),
TP_fast_assign(
__entry->card = (bus)->card->number;
),
TP_printk("[%d]", __entry->card)
);
#ifdef CONFIG_PM
DECLARE_EVENT_CLASS(hda_power,
TP_PROTO(struct hda_codec *codec),
TP_ARGS(codec),
TP_STRUCT__entry(
__field( unsigned int, card )
__field( unsigned int, addr )
),
TP_fast_assign(
__entry->card = (codec)->bus->card->number;
__entry->addr = (codec)->addr;
),
TP_printk("[%d:%d]", __entry->card, __entry->addr)
);
DEFINE_EVENT(hda_power, hda_power_down,
TP_PROTO(struct hda_codec *codec),
TP_ARGS(codec)
);
DEFINE_EVENT(hda_power, hda_power_up,
TP_PROTO(struct hda_codec *codec),
TP_ARGS(codec)
);
TRACE_EVENT(hda_power_count,
TP_PROTO(struct hda_codec *codec),
TP_ARGS(codec),
TP_STRUCT__entry(
__field( unsigned int, card )
__field( unsigned int, addr )
__field( int, power_count )
__field( int, power_on )
__field( int, power_transition )
),
TP_fast_assign(
__entry->card = (codec)->bus->card->number;
__entry->addr = (codec)->addr;
__entry->power_count = (codec)->power_count;
__entry->power_on = (codec)->power_on;
__entry->power_transition = (codec)->power_transition;
),
TP_printk("[%d:%d] power_count=%d, power_on=%d, power_transition=%d",
__entry->card, __entry->addr, __entry->power_count,
__entry->power_on, __entry->power_transition)
);
#endif /* CONFIG_PM */
TRACE_EVENT(hda_unsol_event,
TP_PROTO(struct hda_bus *bus, u32 res, u32 res_ex),
TP_ARGS(bus, res, res_ex),
TP_STRUCT__entry(
__field( unsigned int, card )
__field( u32, res )
__field( u32, res_ex )
),
TP_fast_assign(
__entry->card = (bus)->card->number;
__entry->res = res;
__entry->res_ex = res_ex;
),
TP_printk("[%d] res=%x, res_ex=%x", __entry->card,
__entry->res, __entry->res_ex)
);
#endif /* _TRACE_HDA_H */
/* This part must be outside protection */
#undef TRACE_INCLUDE_PATH
#define TRACE_INCLUDE_PATH .
#include <trace/define_trace.h>

1205
sound/pci/hda/patch_analog.c Normal file

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,117 @@
/*
* HD audio interface patch for Creative X-Fi CA0110-IBG chip
*
* Copyright (c) 2008 Takashi Iwai <tiwai@suse.de>
*
* This driver is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This driver is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
#include <linux/init.h>
#include <linux/slab.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"
static const struct hda_codec_ops ca0110_patch_ops = {
.build_controls = snd_hda_gen_build_controls,
.build_pcms = snd_hda_gen_build_pcms,
.init = snd_hda_gen_init,
.free = snd_hda_gen_free,
.unsol_event = snd_hda_jack_unsol_event,
};
static int ca0110_parse_auto_config(struct hda_codec *codec)
{
struct hda_gen_spec *spec = codec->spec;
int err;
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;
return 0;
}
static int patch_ca0110(struct hda_codec *codec)
{
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);
if (err < 0)
goto error;
codec->patch_ops = ca0110_patch_ops;
return 0;
error:
snd_hda_gen_free(codec);
return err;
}
/*
* patch entries
*/
static const struct hda_codec_preset snd_hda_preset_ca0110[] = {
{ .id = 0x1102000a, .name = "CA0110-IBG", .patch = patch_ca0110 },
{ .id = 0x1102000b, .name = "CA0110-IBG", .patch = patch_ca0110 },
{ .id = 0x1102000d, .name = "SB0880 X-Fi", .patch = patch_ca0110 },
{} /* terminator */
};
MODULE_ALIAS("snd-hda-codec-id:1102000a");
MODULE_ALIAS("snd-hda-codec-id:1102000b");
MODULE_ALIAS("snd-hda-codec-id:1102000d");
MODULE_LICENSE("GPL");
MODULE_DESCRIPTION("Creative CA0110-IBG HD-audio codec");
static struct hda_codec_preset_list ca0110_list = {
.preset = snd_hda_preset_ca0110,
.owner = THIS_MODULE,
};
static int __init patch_ca0110_init(void)
{
return snd_hda_add_codec_preset(&ca0110_list);
}
static void __exit patch_ca0110_exit(void)
{
snd_hda_delete_codec_preset(&ca0110_list);
}
module_init(patch_ca0110_init)
module_exit(patch_ca0110_exit)

4720
sound/pci/hda/patch_ca0132.c Normal file

File diff suppressed because it is too large Load diff

1240
sound/pci/hda/patch_cirrus.c Normal file

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,156 @@
/*
* Universal Interface for Intel High Definition Audio Codec
*
* HD audio interface patch for C-Media CMI9880
*
* Copyright (c) 2004 Takashi Iwai <tiwai@suse.de>
*
*
* This driver is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This driver is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
#include <linux/init.h>
#include <linux/slab.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 cmi_spec {
struct hda_gen_spec gen;
};
/*
* 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 patch_cmi9880(struct hda_codec *codec)
{
struct cmi_spec *spec;
struct auto_pin_cfg *cfg;
int err;
spec = kzalloc(sizeof(*spec), GFP_KERNEL);
if (spec == NULL)
return -ENOMEM;
codec->spec = spec;
cfg = &spec->gen.autocfg;
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_cmi8888(struct hda_codec *codec)
{
struct cmi_spec *spec;
struct auto_pin_cfg *cfg;
int err;
spec = kzalloc(sizeof(*spec), GFP_KERNEL);
if (!spec)
return -ENOMEM;
codec->spec = spec;
cfg = &spec->gen.autocfg;
snd_hda_gen_spec_init(&spec->gen);
/* mask NID 0x10 from the playback volume selection;
* it's a headphone boost volume handled manually below
*/
spec->gen.out_vol_mask = (1ULL << 0x10);
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;
if (get_defcfg_device(snd_hda_codec_get_pincfg(codec, 0x10)) ==
AC_JACK_HP_OUT) {
static const struct snd_kcontrol_new amp_kctl =
HDA_CODEC_VOLUME("Headphone Amp Playback Volume",
0x10, 0, HDA_OUTPUT);
if (!snd_hda_gen_add_kctl(&spec->gen, NULL, &amp_kctl)) {
err = -ENOMEM;
goto error;
}
}
codec->patch_ops = cmi_auto_patch_ops;
return 0;
error:
snd_hda_gen_free(codec);
return err;
}
/*
* patch entries
*/
static const struct hda_codec_preset snd_hda_preset_cmedia[] = {
{ .id = 0x13f68888, .name = "CMI8888", .patch = patch_cmi8888 },
{ .id = 0x13f69880, .name = "CMI9880", .patch = patch_cmi9880 },
{ .id = 0x434d4980, .name = "CMI9880", .patch = patch_cmi9880 },
{} /* terminator */
};
MODULE_ALIAS("snd-hda-codec-id:13f68888");
MODULE_ALIAS("snd-hda-codec-id:13f69880");
MODULE_ALIAS("snd-hda-codec-id:434d4980");
MODULE_LICENSE("GPL");
MODULE_DESCRIPTION("C-Media HD-audio codec");
static struct hda_codec_preset_list cmedia_list = {
.preset = snd_hda_preset_cmedia,
.owner = THIS_MODULE,
};
static int __init patch_cmedia_init(void)
{
return snd_hda_add_codec_preset(&cmedia_list);
}
static void __exit patch_cmedia_exit(void)
{
snd_hda_delete_codec_preset(&cmedia_list);
}
module_init(patch_cmedia_init)
module_exit(patch_cmedia_exit)

File diff suppressed because it is too large Load diff

3457
sound/pci/hda/patch_hdmi.c Normal file

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,338 @@
/*
* Universal Interface for Intel High Definition Audio Codec
*
* HD audio interface patch for Silicon Labs 3054/5 modem codec
*
* Copyright (c) 2005 Sasha Khapyorsky <sashak@alsa-project.org>
* Takashi Iwai <tiwai@suse.de>
*
*
* This driver is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This driver is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
#include <linux/init.h>
#include <linux/delay.h>
#include <linux/slab.h>
#include <linux/module.h>
#include <sound/core.h>
#include "hda_codec.h"
#include "hda_local.h"
/* si3054 verbs */
#define SI3054_VERB_READ_NODE 0x900
#define SI3054_VERB_WRITE_NODE 0x100
/* si3054 nodes (registers) */
#define SI3054_EXTENDED_MID 2
#define SI3054_LINE_RATE 3
#define SI3054_LINE_LEVEL 4
#define SI3054_GPIO_CFG 5
#define SI3054_GPIO_POLARITY 6
#define SI3054_GPIO_STICKY 7
#define SI3054_GPIO_WAKEUP 8
#define SI3054_GPIO_STATUS 9
#define SI3054_GPIO_CONTROL 10
#define SI3054_MISC_AFE 11
#define SI3054_CHIPID 12
#define SI3054_LINE_CFG1 13
#define SI3054_LINE_STATUS 14
#define SI3054_DC_TERMINATION 15
#define SI3054_LINE_CONFIG 16
#define SI3054_CALLPROG_ATT 17
#define SI3054_SQ_CONTROL 18
#define SI3054_MISC_CONTROL 19
#define SI3054_RING_CTRL1 20
#define SI3054_RING_CTRL2 21
/* extended MID */
#define SI3054_MEI_READY 0xf
/* line level */
#define SI3054_ATAG_MASK 0x00f0
#define SI3054_DTAG_MASK 0xf000
/* GPIO bits */
#define SI3054_GPIO_OH 0x0001
#define SI3054_GPIO_CID 0x0002
/* chipid and revisions */
#define SI3054_CHIPID_CODEC_REV_MASK 0x000f
#define SI3054_CHIPID_DAA_REV_MASK 0x00f0
#define SI3054_CHIPID_INTERNATIONAL 0x0100
#define SI3054_CHIPID_DAA_ID 0x0f00
#define SI3054_CHIPID_CODEC_ID (1<<12)
/* si3054 codec registers (nodes) access macros */
#define GET_REG(codec,reg) (snd_hda_codec_read(codec,reg,0,SI3054_VERB_READ_NODE,0))
#define SET_REG(codec,reg,val) (snd_hda_codec_write(codec,reg,0,SI3054_VERB_WRITE_NODE,val))
#define SET_REG_CACHE(codec,reg,val) \
snd_hda_codec_write_cache(codec,reg,0,SI3054_VERB_WRITE_NODE,val)
struct si3054_spec {
unsigned international;
struct hda_pcm pcm;
};
/*
* Modem mixer
*/
#define PRIVATE_VALUE(reg,mask) ((reg<<16)|(mask&0xffff))
#define PRIVATE_REG(val) ((val>>16)&0xffff)
#define PRIVATE_MASK(val) (val&0xffff)
#define si3054_switch_info snd_ctl_boolean_mono_info
static int si3054_switch_get(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *uvalue)
{
struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
u16 reg = PRIVATE_REG(kcontrol->private_value);
u16 mask = PRIVATE_MASK(kcontrol->private_value);
uvalue->value.integer.value[0] = (GET_REG(codec, reg)) & mask ? 1 : 0 ;
return 0;
}
static int si3054_switch_put(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *uvalue)
{
struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
u16 reg = PRIVATE_REG(kcontrol->private_value);
u16 mask = PRIVATE_MASK(kcontrol->private_value);
if (uvalue->value.integer.value[0])
SET_REG_CACHE(codec, reg, (GET_REG(codec, reg)) | mask);
else
SET_REG_CACHE(codec, reg, (GET_REG(codec, reg)) & ~mask);
return 0;
}
#define SI3054_KCONTROL(kname,reg,mask) { \
.iface = SNDRV_CTL_ELEM_IFACE_MIXER, \
.name = kname, \
.subdevice = HDA_SUBDEV_NID_FLAG | reg, \
.info = si3054_switch_info, \
.get = si3054_switch_get, \
.put = si3054_switch_put, \
.private_value = PRIVATE_VALUE(reg,mask), \
}
static const struct snd_kcontrol_new si3054_modem_mixer[] = {
SI3054_KCONTROL("Off-hook Switch", SI3054_GPIO_CONTROL, SI3054_GPIO_OH),
SI3054_KCONTROL("Caller ID Switch", SI3054_GPIO_CONTROL, SI3054_GPIO_CID),
{}
};
static int si3054_build_controls(struct hda_codec *codec)
{
return snd_hda_add_new_ctls(codec, si3054_modem_mixer);
}
/*
* PCM callbacks
*/
static int si3054_pcm_prepare(struct hda_pcm_stream *hinfo,
struct hda_codec *codec,
unsigned int stream_tag,
unsigned int format,
struct snd_pcm_substream *substream)
{
u16 val;
SET_REG(codec, SI3054_LINE_RATE, substream->runtime->rate);
val = GET_REG(codec, SI3054_LINE_LEVEL);
val &= 0xff << (8 * (substream->stream != SNDRV_PCM_STREAM_PLAYBACK));
val |= ((stream_tag & 0xf) << 4) << (8 * (substream->stream == SNDRV_PCM_STREAM_PLAYBACK));
SET_REG(codec, SI3054_LINE_LEVEL, val);
snd_hda_codec_setup_stream(codec, hinfo->nid,
stream_tag, 0, format);
return 0;
}
static int si3054_pcm_open(struct hda_pcm_stream *hinfo,
struct hda_codec *codec,
struct snd_pcm_substream *substream)
{
static unsigned int rates[] = { 8000, 9600, 16000 };
static struct snd_pcm_hw_constraint_list hw_constraints_rates = {
.count = ARRAY_SIZE(rates),
.list = rates,
.mask = 0,
};
substream->runtime->hw.period_bytes_min = 80;
return snd_pcm_hw_constraint_list(substream->runtime, 0,
SNDRV_PCM_HW_PARAM_RATE, &hw_constraints_rates);
}
static const struct hda_pcm_stream si3054_pcm = {
.substreams = 1,
.channels_min = 1,
.channels_max = 1,
.nid = 0x1,
.rates = SNDRV_PCM_RATE_8000|SNDRV_PCM_RATE_16000|SNDRV_PCM_RATE_KNOT,
.formats = SNDRV_PCM_FMTBIT_S16_LE,
.maxbps = 16,
.ops = {
.open = si3054_pcm_open,
.prepare = si3054_pcm_prepare,
},
};
static int si3054_build_pcms(struct hda_codec *codec)
{
struct si3054_spec *spec = codec->spec;
struct hda_pcm *info = &spec->pcm;
codec->num_pcms = 1;
codec->pcm_info = info;
info->name = "Si3054 Modem";
info->stream[SNDRV_PCM_STREAM_PLAYBACK] = si3054_pcm;
info->stream[SNDRV_PCM_STREAM_CAPTURE] = si3054_pcm;
info->stream[SNDRV_PCM_STREAM_PLAYBACK].nid = codec->mfg;
info->stream[SNDRV_PCM_STREAM_CAPTURE].nid = codec->mfg;
info->pcm_type = HDA_PCM_TYPE_MODEM;
return 0;
}
/*
* Init part
*/
static int si3054_init(struct hda_codec *codec)
{
struct si3054_spec *spec = codec->spec;
unsigned wait_count;
u16 val;
snd_hda_codec_write(codec, AC_NODE_ROOT, 0, AC_VERB_SET_CODEC_RESET, 0);
snd_hda_codec_write(codec, codec->mfg, 0, AC_VERB_SET_STREAM_FORMAT, 0);
SET_REG(codec, SI3054_LINE_RATE, 9600);
SET_REG(codec, SI3054_LINE_LEVEL, SI3054_DTAG_MASK|SI3054_ATAG_MASK);
SET_REG(codec, SI3054_EXTENDED_MID, 0);
wait_count = 10;
do {
msleep(2);
val = GET_REG(codec, SI3054_EXTENDED_MID);
} while ((val & SI3054_MEI_READY) != SI3054_MEI_READY && wait_count--);
if((val&SI3054_MEI_READY) != SI3054_MEI_READY) {
codec_err(codec, "si3054: cannot initialize. EXT MID = %04x\n", val);
/* let's pray that this is no fatal error */
/* return -EACCES; */
}
SET_REG(codec, SI3054_GPIO_POLARITY, 0xffff);
SET_REG(codec, SI3054_GPIO_CFG, 0x0);
SET_REG(codec, SI3054_MISC_AFE, 0);
SET_REG(codec, SI3054_LINE_CFG1,0x200);
if((GET_REG(codec,SI3054_LINE_STATUS) & (1<<6)) == 0) {
codec_dbg(codec,
"Link Frame Detect(FDT) is not ready (line status: %04x)\n",
GET_REG(codec,SI3054_LINE_STATUS));
}
spec->international = GET_REG(codec, SI3054_CHIPID) & SI3054_CHIPID_INTERNATIONAL;
return 0;
}
static void si3054_free(struct hda_codec *codec)
{
kfree(codec->spec);
}
/*
*/
static const struct hda_codec_ops si3054_patch_ops = {
.build_controls = si3054_build_controls,
.build_pcms = si3054_build_pcms,
.init = si3054_init,
.free = si3054_free,
};
static int patch_si3054(struct hda_codec *codec)
{
struct si3054_spec *spec = kzalloc(sizeof(*spec), GFP_KERNEL);
if (spec == NULL)
return -ENOMEM;
codec->spec = spec;
codec->patch_ops = si3054_patch_ops;
return 0;
}
/*
* patch entries
*/
static const struct hda_codec_preset snd_hda_preset_si3054[] = {
{ .id = 0x163c3055, .name = "Si3054", .patch = patch_si3054 },
{ .id = 0x163c3155, .name = "Si3054", .patch = patch_si3054 },
{ .id = 0x11c13026, .name = "Si3054", .patch = patch_si3054 },
{ .id = 0x11c13055, .name = "Si3054", .patch = patch_si3054 },
{ .id = 0x11c13155, .name = "Si3054", .patch = patch_si3054 },
{ .id = 0x10573055, .name = "Si3054", .patch = patch_si3054 },
{ .id = 0x10573057, .name = "Si3054", .patch = patch_si3054 },
{ .id = 0x10573155, .name = "Si3054", .patch = patch_si3054 },
/* VIA HDA on Clevo m540 */
{ .id = 0x11063288, .name = "Si3054", .patch = patch_si3054 },
/* Asus A8J Modem (SM56) */
{ .id = 0x15433155, .name = "Si3054", .patch = patch_si3054 },
/* LG LW20 modem */
{ .id = 0x18540018, .name = "Si3054", .patch = patch_si3054 },
{}
};
MODULE_ALIAS("snd-hda-codec-id:163c3055");
MODULE_ALIAS("snd-hda-codec-id:163c3155");
MODULE_ALIAS("snd-hda-codec-id:11c13026");
MODULE_ALIAS("snd-hda-codec-id:11c13055");
MODULE_ALIAS("snd-hda-codec-id:11c13155");
MODULE_ALIAS("snd-hda-codec-id:10573055");
MODULE_ALIAS("snd-hda-codec-id:10573057");
MODULE_ALIAS("snd-hda-codec-id:10573155");
MODULE_ALIAS("snd-hda-codec-id:11063288");
MODULE_ALIAS("snd-hda-codec-id:15433155");
MODULE_ALIAS("snd-hda-codec-id:18540018");
MODULE_LICENSE("GPL");
MODULE_DESCRIPTION("Si3054 HD-audio modem codec");
static struct hda_codec_preset_list si3054_list = {
.preset = snd_hda_preset_si3054,
.owner = THIS_MODULE,
};
static int __init patch_si3054_init(void)
{
return snd_hda_add_codec_preset(&si3054_list);
}
static void __exit patch_si3054_exit(void)
{
snd_hda_delete_codec_preset(&si3054_list);
}
module_init(patch_si3054_init)
module_exit(patch_si3054_exit)

File diff suppressed because it is too large Load diff

1906
sound/pci/hda/patch_via.c Normal file

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,103 @@
/* 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;
spec->vmaster_mute_enum = 1;
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 */