Merge branch 'for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/tiwai/sound-2.6
* 'for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/tiwai/sound-2.6: (367 commits) ALSA: ASoC: fix a typo in omp-pcm.c ASoC: Fix DSP formats in SSM2602 audio codec ASoC: Fix incorrect DSP format in OMAP McBSP DAI and affected drivers ALSA: hda: fix incorrect mixer index values for 92hd83xx ALSA: hda: dinput_mux check ALSA: hda - Add quirk for another HP dv7 ALSA: ASoC - Add missing __devexit annotation to wm8350.c ALSA: ASoc: DaVinci: davinci-evm use dsp_b mode ALSA: ASoC: DaVinci: i2s, evm, pass same value to codec and cpu_dai ALSA: ASoC: tlv320aic3x add dsp_a ALSA: ASoC: DaVinci: document I2S limitations ALSA: ASoC: DaVinci: davinci-i2s clean up ALSA: ASoC: DaVinci: davinci-i2s clean up ALSA: ASoC: DaVinci: davinci-i2s add comments to explain polarity ALSA: ASoC: DaVinci: davinvi-evm, make requests explicit ALSA: ca0106 - disable 44.1kHz capture ALSA: ca0106 - Add missing card->private_data initialization ALSA: ca0106 - Check ac97 availability at PM ALSA: hda - Power up always when no jack detection is available ALSA: hda - Fix unused variable warnings in patch_sigmatel.c ...
This commit is contained in:
commit
cb10ea549f
|
@ -757,6 +757,8 @@ Prior to version 0.9.0rc4 options had a 'snd_' prefix. This was removed.
|
|||
model - force the model name
|
||||
position_fix - Fix DMA pointer (0 = auto, 1 = use LPIB, 2 = POSBUF)
|
||||
probe_mask - Bitmask to probe codecs (default = -1, meaning all slots)
|
||||
probe_only - Only probing and no codec initialization (default=off);
|
||||
Useful to check the initial codec status for debugging
|
||||
bdl_pos_adj - Specifies the DMA IRQ timing delay in samples.
|
||||
Passing -1 will make the driver to choose the appropriate
|
||||
value based on the controller chip.
|
||||
|
@ -772,327 +774,23 @@ Prior to version 0.9.0rc4 options had a 'snd_' prefix. This was removed.
|
|||
|
||||
This module supports multiple cards and autoprobe.
|
||||
|
||||
See Documentation/sound/alsa/HD-Audio.txt for more details about
|
||||
HD-audio driver.
|
||||
|
||||
Each codec may have a model table for different configurations.
|
||||
If your machine isn't listed there, the default (usually minimal)
|
||||
configuration is set up. You can pass "model=<name>" option to
|
||||
specify a certain model in such a case. There are different
|
||||
models depending on the codec chip.
|
||||
|
||||
Model name Description
|
||||
---------- -----------
|
||||
ALC880
|
||||
3stack 3-jack in back and a headphone out
|
||||
3stack-digout 3-jack in back, a HP out and a SPDIF out
|
||||
5stack 5-jack in back, 2-jack in front
|
||||
5stack-digout 5-jack in back, 2-jack in front, a SPDIF out
|
||||
6stack 6-jack in back, 2-jack in front
|
||||
6stack-digout 6-jack with a SPDIF out
|
||||
w810 3-jack
|
||||
z71v 3-jack (HP shared SPDIF)
|
||||
asus 3-jack (ASUS Mobo)
|
||||
asus-w1v ASUS W1V
|
||||
asus-dig ASUS with SPDIF out
|
||||
asus-dig2 ASUS with SPDIF out (using GPIO2)
|
||||
uniwill 3-jack
|
||||
fujitsu Fujitsu Laptops (Pi1536)
|
||||
F1734 2-jack
|
||||
lg LG laptop (m1 express dual)
|
||||
lg-lw LG LW20/LW25 laptop
|
||||
tcl TCL S700
|
||||
clevo Clevo laptops (m520G, m665n)
|
||||
medion Medion Rim 2150
|
||||
test for testing/debugging purpose, almost all controls can be
|
||||
adjusted. Appearing only when compiled with
|
||||
$CONFIG_SND_DEBUG=y
|
||||
auto auto-config reading BIOS (default)
|
||||
|
||||
ALC260
|
||||
hp HP machines
|
||||
hp-3013 HP machines (3013-variant)
|
||||
hp-dc7600 HP DC7600
|
||||
fujitsu Fujitsu S7020
|
||||
acer Acer TravelMate
|
||||
will Will laptops (PB V7900)
|
||||
replacer Replacer 672V
|
||||
basic fixed pin assignment (old default model)
|
||||
test for testing/debugging purpose, almost all controls can
|
||||
adjusted. Appearing only when compiled with
|
||||
$CONFIG_SND_DEBUG=y
|
||||
auto auto-config reading BIOS (default)
|
||||
|
||||
ALC262
|
||||
fujitsu Fujitsu Laptop
|
||||
hp-bpc HP xw4400/6400/8400/9400 laptops
|
||||
hp-bpc-d7000 HP BPC D7000
|
||||
hp-tc-t5735 HP Thin Client T5735
|
||||
hp-rp5700 HP RP5700
|
||||
benq Benq ED8
|
||||
benq-t31 Benq T31
|
||||
hippo Hippo (ATI) with jack detection, Sony UX-90s
|
||||
hippo_1 Hippo (Benq) with jack detection
|
||||
sony-assamd Sony ASSAMD
|
||||
toshiba-s06 Toshiba S06
|
||||
toshiba-rx1 Toshiba RX1
|
||||
ultra Samsung Q1 Ultra Vista model
|
||||
lenovo-3000 Lenovo 3000 y410
|
||||
nec NEC Versa S9100
|
||||
basic fixed pin assignment w/o SPDIF
|
||||
auto auto-config reading BIOS (default)
|
||||
|
||||
ALC267/268
|
||||
quanta-il1 Quanta IL1 mini-notebook
|
||||
3stack 3-stack model
|
||||
toshiba Toshiba A205
|
||||
acer Acer laptops
|
||||
acer-aspire Acer Aspire One
|
||||
dell Dell OEM laptops (Vostro 1200)
|
||||
zepto Zepto laptops
|
||||
test for testing/debugging purpose, almost all controls can
|
||||
adjusted. Appearing only when compiled with
|
||||
$CONFIG_SND_DEBUG=y
|
||||
auto auto-config reading BIOS (default)
|
||||
|
||||
ALC269
|
||||
basic Basic preset
|
||||
quanta Quanta FL1
|
||||
eeepc-p703 ASUS Eeepc P703 P900A
|
||||
eeepc-p901 ASUS Eeepc P901 S101
|
||||
|
||||
ALC662/663
|
||||
3stack-dig 3-stack (2-channel) with SPDIF
|
||||
3stack-6ch 3-stack (6-channel)
|
||||
3stack-6ch-dig 3-stack (6-channel) with SPDIF
|
||||
6stack-dig 6-stack with SPDIF
|
||||
lenovo-101e Lenovo laptop
|
||||
eeepc-p701 ASUS Eeepc P701
|
||||
eeepc-ep20 ASUS Eeepc EP20
|
||||
ecs ECS/Foxconn mobo
|
||||
m51va ASUS M51VA
|
||||
g71v ASUS G71V
|
||||
h13 ASUS H13
|
||||
g50v ASUS G50V
|
||||
asus-mode1 ASUS
|
||||
asus-mode2 ASUS
|
||||
asus-mode3 ASUS
|
||||
asus-mode4 ASUS
|
||||
asus-mode5 ASUS
|
||||
asus-mode6 ASUS
|
||||
auto auto-config reading BIOS (default)
|
||||
|
||||
ALC882/885
|
||||
3stack-dig 3-jack with SPDIF I/O
|
||||
6stack-dig 6-jack digital with SPDIF I/O
|
||||
arima Arima W820Di1
|
||||
targa Targa T8, MSI-1049 T8
|
||||
asus-a7j ASUS A7J
|
||||
asus-a7m ASUS A7M
|
||||
macpro MacPro support
|
||||
mbp3 Macbook Pro rev3
|
||||
imac24 iMac 24'' with jack detection
|
||||
w2jc ASUS W2JC
|
||||
auto auto-config reading BIOS (default)
|
||||
|
||||
ALC883/888
|
||||
3stack-dig 3-jack with SPDIF I/O
|
||||
6stack-dig 6-jack digital with SPDIF I/O
|
||||
3stack-6ch 3-jack 6-channel
|
||||
3stack-6ch-dig 3-jack 6-channel with SPDIF I/O
|
||||
6stack-dig-demo 6-jack digital for Intel demo board
|
||||
acer Acer laptops (Travelmate 3012WTMi, Aspire 5600, etc)
|
||||
acer-aspire Acer Aspire 9810
|
||||
medion Medion Laptops
|
||||
medion-md2 Medion MD2
|
||||
targa-dig Targa/MSI
|
||||
targa-2ch-dig Targs/MSI with 2-channel
|
||||
laptop-eapd 3-jack with SPDIF I/O and EAPD (Clevo M540JE, M550JE)
|
||||
lenovo-101e Lenovo 101E
|
||||
lenovo-nb0763 Lenovo NB0763
|
||||
lenovo-ms7195-dig Lenovo MS7195
|
||||
lenovo-sky Lenovo Sky
|
||||
haier-w66 Haier W66
|
||||
3stack-hp HP machines with 3stack (Lucknow, Samba boards)
|
||||
6stack-dell Dell machines with 6stack (Inspiron 530)
|
||||
mitac Mitac 8252D
|
||||
clevo-m720 Clevo M720 laptop series
|
||||
fujitsu-pi2515 Fujitsu AMILO Pi2515
|
||||
3stack-6ch-intel Intel DG33* boards
|
||||
auto auto-config reading BIOS (default)
|
||||
|
||||
ALC861/660
|
||||
3stack 3-jack
|
||||
3stack-dig 3-jack with SPDIF I/O
|
||||
6stack-dig 6-jack with SPDIF I/O
|
||||
3stack-660 3-jack (for ALC660)
|
||||
uniwill-m31 Uniwill M31 laptop
|
||||
toshiba Toshiba laptop support
|
||||
asus Asus laptop support
|
||||
asus-laptop ASUS F2/F3 laptops
|
||||
auto auto-config reading BIOS (default)
|
||||
|
||||
ALC861VD/660VD
|
||||
3stack 3-jack
|
||||
3stack-dig 3-jack with SPDIF OUT
|
||||
6stack-dig 6-jack with SPDIF OUT
|
||||
3stack-660 3-jack (for ALC660VD)
|
||||
3stack-660-digout 3-jack with SPDIF OUT (for ALC660VD)
|
||||
lenovo Lenovo 3000 C200
|
||||
dallas Dallas laptops
|
||||
hp HP TX1000
|
||||
auto auto-config reading BIOS (default)
|
||||
|
||||
CMI9880
|
||||
minimal 3-jack in back
|
||||
min_fp 3-jack in back, 2-jack in front
|
||||
full 6-jack in back, 2-jack in front
|
||||
full_dig 6-jack in back, 2-jack in front, SPDIF I/O
|
||||
allout 5-jack in back, 2-jack in front, SPDIF out
|
||||
auto auto-config reading BIOS (default)
|
||||
|
||||
AD1882 / AD1882A
|
||||
3stack 3-stack mode (default)
|
||||
6stack 6-stack mode
|
||||
|
||||
AD1884A / AD1883 / AD1984A / AD1984B
|
||||
desktop 3-stack desktop (default)
|
||||
laptop laptop with HP jack sensing
|
||||
mobile mobile devices with HP jack sensing
|
||||
thinkpad Lenovo Thinkpad X300
|
||||
|
||||
AD1884
|
||||
N/A
|
||||
|
||||
AD1981
|
||||
basic 3-jack (default)
|
||||
hp HP nx6320
|
||||
thinkpad Lenovo Thinkpad T60/X60/Z60
|
||||
toshiba Toshiba U205
|
||||
|
||||
AD1983
|
||||
N/A
|
||||
|
||||
AD1984
|
||||
basic default configuration
|
||||
thinkpad Lenovo Thinkpad T61/X61
|
||||
dell Dell T3400
|
||||
|
||||
AD1986A
|
||||
6stack 6-jack, separate surrounds (default)
|
||||
3stack 3-stack, shared surrounds
|
||||
laptop 2-channel only (FSC V2060, Samsung M50)
|
||||
laptop-eapd 2-channel with EAPD (Samsung R65, ASUS A6J)
|
||||
laptop-automute 2-channel with EAPD and HP-automute (Lenovo N100)
|
||||
ultra 2-channel with EAPD (Samsung Ultra tablet PC)
|
||||
|
||||
AD1988/AD1988B/AD1989A/AD1989B
|
||||
6stack 6-jack
|
||||
6stack-dig ditto with SPDIF
|
||||
3stack 3-jack
|
||||
3stack-dig ditto with SPDIF
|
||||
laptop 3-jack with hp-jack automute
|
||||
laptop-dig ditto with SPDIF
|
||||
auto auto-config reading BIOS (default)
|
||||
|
||||
Conexant 5045
|
||||
laptop-hpsense Laptop with HP sense (old model laptop)
|
||||
laptop-micsense Laptop with Mic sense (old model fujitsu)
|
||||
laptop-hpmicsense Laptop with HP and Mic senses
|
||||
benq Benq R55E
|
||||
test for testing/debugging purpose, almost all controls
|
||||
can be adjusted. Appearing only when compiled with
|
||||
$CONFIG_SND_DEBUG=y
|
||||
|
||||
Conexant 5047
|
||||
laptop Basic Laptop config
|
||||
laptop-hp Laptop config for some HP models (subdevice 30A5)
|
||||
laptop-eapd Laptop config with EAPD support
|
||||
test for testing/debugging purpose, almost all controls
|
||||
can be adjusted. Appearing only when compiled with
|
||||
$CONFIG_SND_DEBUG=y
|
||||
|
||||
Conexant 5051
|
||||
laptop Basic Laptop config (default)
|
||||
hp HP Spartan laptop
|
||||
|
||||
STAC9200
|
||||
ref Reference board
|
||||
dell-d21 Dell (unknown)
|
||||
dell-d22 Dell (unknown)
|
||||
dell-d23 Dell (unknown)
|
||||
dell-m21 Dell Inspiron 630m, Dell Inspiron 640m
|
||||
dell-m22 Dell Latitude D620, Dell Latitude D820
|
||||
dell-m23 Dell XPS M1710, Dell Precision M90
|
||||
dell-m24 Dell Latitude 120L
|
||||
dell-m25 Dell Inspiron E1505n
|
||||
dell-m26 Dell Inspiron 1501
|
||||
dell-m27 Dell Inspiron E1705/9400
|
||||
gateway Gateway laptops with EAPD control
|
||||
panasonic Panasonic CF-74
|
||||
|
||||
STAC9205/9254
|
||||
ref Reference board
|
||||
dell-m42 Dell (unknown)
|
||||
dell-m43 Dell Precision
|
||||
dell-m44 Dell Inspiron
|
||||
|
||||
STAC9220/9221
|
||||
ref Reference board
|
||||
3stack D945 3stack
|
||||
5stack D945 5stack + SPDIF
|
||||
intel-mac-v1 Intel Mac Type 1
|
||||
intel-mac-v2 Intel Mac Type 2
|
||||
intel-mac-v3 Intel Mac Type 3
|
||||
intel-mac-v4 Intel Mac Type 4
|
||||
intel-mac-v5 Intel Mac Type 5
|
||||
intel-mac-auto Intel Mac (detect type according to subsystem id)
|
||||
macmini Intel Mac Mini (equivalent with type 3)
|
||||
macbook Intel Mac Book (eq. type 5)
|
||||
macbook-pro-v1 Intel Mac Book Pro 1st generation (eq. type 3)
|
||||
macbook-pro Intel Mac Book Pro 2nd generation (eq. type 3)
|
||||
imac-intel Intel iMac (eq. type 2)
|
||||
imac-intel-20 Intel iMac (newer version) (eq. type 3)
|
||||
dell-d81 Dell (unknown)
|
||||
dell-d82 Dell (unknown)
|
||||
dell-m81 Dell (unknown)
|
||||
dell-m82 Dell XPS M1210
|
||||
|
||||
STAC9202/9250/9251
|
||||
ref Reference board, base config
|
||||
m2-2 Some Gateway MX series laptops
|
||||
m6 Some Gateway NX series laptops
|
||||
pa6 Gateway NX860 series
|
||||
|
||||
STAC9227/9228/9229/927x
|
||||
ref Reference board
|
||||
ref-no-jd Reference board without HP/Mic jack detection
|
||||
3stack D965 3stack
|
||||
5stack D965 5stack + SPDIF
|
||||
dell-3stack Dell Dimension E520
|
||||
dell-bios Fixes with Dell BIOS setup
|
||||
|
||||
STAC92HD71B*
|
||||
ref Reference board
|
||||
dell-m4-1 Dell desktops
|
||||
dell-m4-2 Dell desktops
|
||||
dell-m4-3 Dell desktops
|
||||
|
||||
STAC92HD73*
|
||||
ref Reference board
|
||||
no-jd BIOS setup but without jack-detection
|
||||
dell-m6-amic Dell desktops/laptops with analog mics
|
||||
dell-m6-dmic Dell desktops/laptops with digital mics
|
||||
dell-m6 Dell desktops/laptops with both type of mics
|
||||
|
||||
STAC9872
|
||||
vaio Setup for VAIO FE550G/SZ110
|
||||
vaio-ar Setup for VAIO AR
|
||||
models depending on the codec chip. The list of available models
|
||||
is found in HD-Audio-Models.txt
|
||||
|
||||
The model name "genric" is treated as a special case. When this
|
||||
model is given, the driver uses the generic codec parser without
|
||||
"codec-patch". It's sometimes good for testing and debugging.
|
||||
|
||||
If the default configuration doesn't work and one of the above
|
||||
matches with your device, report it together with the PCI
|
||||
subsystem ID (output of "lspci -nv") to ALSA BTS or alsa-devel
|
||||
matches with your device, report it together with alsa-info.sh
|
||||
output (with --no-upload option) to kernel bugzilla or alsa-devel
|
||||
ML (see the section "Links and Addresses").
|
||||
|
||||
power_save and power_save_controller options are for power-saving
|
||||
|
@ -1652,7 +1350,8 @@ Prior to version 0.9.0rc4 options had a 'snd_' prefix. This was removed.
|
|||
* AuzenTech X-Meridian
|
||||
* Bgears b-Enspirer
|
||||
* Club3D Theatron DTS
|
||||
* HT-Omega Claro
|
||||
* HT-Omega Claro (plus)
|
||||
* HT-Omega Claro halo (XT)
|
||||
* Razer Barracuda AC-1
|
||||
* Sondigo Inferno
|
||||
|
||||
|
@ -2409,8 +2108,11 @@ Links and Addresses
|
|||
ALSA project homepage
|
||||
http://www.alsa-project.org
|
||||
|
||||
ALSA Bug Tracking System
|
||||
https://bugtrack.alsa-project.org/bugs/
|
||||
Kernel Bugzilla
|
||||
http://bugzilla.kernel.org/
|
||||
|
||||
ALSA Developers ML
|
||||
mailto:alsa-devel@alsa-project.org
|
||||
|
||||
alsa-info.sh script
|
||||
http://www.alsa-project.org/alsa-info.sh
|
||||
|
|
|
@ -0,0 +1,348 @@
|
|||
Model name Description
|
||||
---------- -----------
|
||||
ALC880
|
||||
======
|
||||
3stack 3-jack in back and a headphone out
|
||||
3stack-digout 3-jack in back, a HP out and a SPDIF out
|
||||
5stack 5-jack in back, 2-jack in front
|
||||
5stack-digout 5-jack in back, 2-jack in front, a SPDIF out
|
||||
6stack 6-jack in back, 2-jack in front
|
||||
6stack-digout 6-jack with a SPDIF out
|
||||
w810 3-jack
|
||||
z71v 3-jack (HP shared SPDIF)
|
||||
asus 3-jack (ASUS Mobo)
|
||||
asus-w1v ASUS W1V
|
||||
asus-dig ASUS with SPDIF out
|
||||
asus-dig2 ASUS with SPDIF out (using GPIO2)
|
||||
uniwill 3-jack
|
||||
fujitsu Fujitsu Laptops (Pi1536)
|
||||
F1734 2-jack
|
||||
lg LG laptop (m1 express dual)
|
||||
lg-lw LG LW20/LW25 laptop
|
||||
tcl TCL S700
|
||||
clevo Clevo laptops (m520G, m665n)
|
||||
medion Medion Rim 2150
|
||||
test for testing/debugging purpose, almost all controls can be
|
||||
adjusted. Appearing only when compiled with
|
||||
$CONFIG_SND_DEBUG=y
|
||||
auto auto-config reading BIOS (default)
|
||||
|
||||
ALC260
|
||||
======
|
||||
hp HP machines
|
||||
hp-3013 HP machines (3013-variant)
|
||||
hp-dc7600 HP DC7600
|
||||
fujitsu Fujitsu S7020
|
||||
acer Acer TravelMate
|
||||
will Will laptops (PB V7900)
|
||||
replacer Replacer 672V
|
||||
basic fixed pin assignment (old default model)
|
||||
test for testing/debugging purpose, almost all controls can
|
||||
adjusted. Appearing only when compiled with
|
||||
$CONFIG_SND_DEBUG=y
|
||||
auto auto-config reading BIOS (default)
|
||||
|
||||
ALC262
|
||||
======
|
||||
fujitsu Fujitsu Laptop
|
||||
hp-bpc HP xw4400/6400/8400/9400 laptops
|
||||
hp-bpc-d7000 HP BPC D7000
|
||||
hp-tc-t5735 HP Thin Client T5735
|
||||
hp-rp5700 HP RP5700
|
||||
benq Benq ED8
|
||||
benq-t31 Benq T31
|
||||
hippo Hippo (ATI) with jack detection, Sony UX-90s
|
||||
hippo_1 Hippo (Benq) with jack detection
|
||||
sony-assamd Sony ASSAMD
|
||||
toshiba-s06 Toshiba S06
|
||||
toshiba-rx1 Toshiba RX1
|
||||
ultra Samsung Q1 Ultra Vista model
|
||||
lenovo-3000 Lenovo 3000 y410
|
||||
nec NEC Versa S9100
|
||||
basic fixed pin assignment w/o SPDIF
|
||||
auto auto-config reading BIOS (default)
|
||||
|
||||
ALC267/268
|
||||
==========
|
||||
quanta-il1 Quanta IL1 mini-notebook
|
||||
3stack 3-stack model
|
||||
toshiba Toshiba A205
|
||||
acer Acer laptops
|
||||
acer-dmic Acer laptops with digital-mic
|
||||
acer-aspire Acer Aspire One
|
||||
dell Dell OEM laptops (Vostro 1200)
|
||||
zepto Zepto laptops
|
||||
test for testing/debugging purpose, almost all controls can
|
||||
adjusted. Appearing only when compiled with
|
||||
$CONFIG_SND_DEBUG=y
|
||||
auto auto-config reading BIOS (default)
|
||||
|
||||
ALC269
|
||||
======
|
||||
basic Basic preset
|
||||
quanta Quanta FL1
|
||||
eeepc-p703 ASUS Eeepc P703 P900A
|
||||
eeepc-p901 ASUS Eeepc P901 S101
|
||||
fujitsu FSC Amilo
|
||||
auto auto-config reading BIOS (default)
|
||||
|
||||
ALC662/663
|
||||
==========
|
||||
3stack-dig 3-stack (2-channel) with SPDIF
|
||||
3stack-6ch 3-stack (6-channel)
|
||||
3stack-6ch-dig 3-stack (6-channel) with SPDIF
|
||||
6stack-dig 6-stack with SPDIF
|
||||
lenovo-101e Lenovo laptop
|
||||
eeepc-p701 ASUS Eeepc P701
|
||||
eeepc-ep20 ASUS Eeepc EP20
|
||||
ecs ECS/Foxconn mobo
|
||||
m51va ASUS M51VA
|
||||
g71v ASUS G71V
|
||||
h13 ASUS H13
|
||||
g50v ASUS G50V
|
||||
asus-mode1 ASUS
|
||||
asus-mode2 ASUS
|
||||
asus-mode3 ASUS
|
||||
asus-mode4 ASUS
|
||||
asus-mode5 ASUS
|
||||
asus-mode6 ASUS
|
||||
auto auto-config reading BIOS (default)
|
||||
|
||||
ALC882/885
|
||||
==========
|
||||
3stack-dig 3-jack with SPDIF I/O
|
||||
6stack-dig 6-jack digital with SPDIF I/O
|
||||
arima Arima W820Di1
|
||||
targa Targa T8, MSI-1049 T8
|
||||
asus-a7j ASUS A7J
|
||||
asus-a7m ASUS A7M
|
||||
macpro MacPro support
|
||||
mbp3 Macbook Pro rev3
|
||||
imac24 iMac 24'' with jack detection
|
||||
w2jc ASUS W2JC
|
||||
auto auto-config reading BIOS (default)
|
||||
|
||||
ALC883/888
|
||||
==========
|
||||
3stack-dig 3-jack with SPDIF I/O
|
||||
6stack-dig 6-jack digital with SPDIF I/O
|
||||
3stack-6ch 3-jack 6-channel
|
||||
3stack-6ch-dig 3-jack 6-channel with SPDIF I/O
|
||||
6stack-dig-demo 6-jack digital for Intel demo board
|
||||
acer Acer laptops (Travelmate 3012WTMi, Aspire 5600, etc)
|
||||
acer-aspire Acer Aspire 9810
|
||||
acer-aspire-4930g Acer Aspire 4930G
|
||||
medion Medion Laptops
|
||||
medion-md2 Medion MD2
|
||||
targa-dig Targa/MSI
|
||||
targa-2ch-dig Targs/MSI with 2-channel
|
||||
laptop-eapd 3-jack with SPDIF I/O and EAPD (Clevo M540JE, M550JE)
|
||||
lenovo-101e Lenovo 101E
|
||||
lenovo-nb0763 Lenovo NB0763
|
||||
lenovo-ms7195-dig Lenovo MS7195
|
||||
lenovo-sky Lenovo Sky
|
||||
haier-w66 Haier W66
|
||||
3stack-hp HP machines with 3stack (Lucknow, Samba boards)
|
||||
6stack-dell Dell machines with 6stack (Inspiron 530)
|
||||
mitac Mitac 8252D
|
||||
clevo-m720 Clevo M720 laptop series
|
||||
fujitsu-pi2515 Fujitsu AMILO Pi2515
|
||||
fujitsu-xa3530 Fujitsu AMILO XA3530
|
||||
3stack-6ch-intel Intel DG33* boards
|
||||
auto auto-config reading BIOS (default)
|
||||
|
||||
ALC861/660
|
||||
==========
|
||||
3stack 3-jack
|
||||
3stack-dig 3-jack with SPDIF I/O
|
||||
6stack-dig 6-jack with SPDIF I/O
|
||||
3stack-660 3-jack (for ALC660)
|
||||
uniwill-m31 Uniwill M31 laptop
|
||||
toshiba Toshiba laptop support
|
||||
asus Asus laptop support
|
||||
asus-laptop ASUS F2/F3 laptops
|
||||
auto auto-config reading BIOS (default)
|
||||
|
||||
ALC861VD/660VD
|
||||
==============
|
||||
3stack 3-jack
|
||||
3stack-dig 3-jack with SPDIF OUT
|
||||
6stack-dig 6-jack with SPDIF OUT
|
||||
3stack-660 3-jack (for ALC660VD)
|
||||
3stack-660-digout 3-jack with SPDIF OUT (for ALC660VD)
|
||||
lenovo Lenovo 3000 C200
|
||||
dallas Dallas laptops
|
||||
hp HP TX1000
|
||||
asus-v1s ASUS V1Sn
|
||||
auto auto-config reading BIOS (default)
|
||||
|
||||
CMI9880
|
||||
=======
|
||||
minimal 3-jack in back
|
||||
min_fp 3-jack in back, 2-jack in front
|
||||
full 6-jack in back, 2-jack in front
|
||||
full_dig 6-jack in back, 2-jack in front, SPDIF I/O
|
||||
allout 5-jack in back, 2-jack in front, SPDIF out
|
||||
auto auto-config reading BIOS (default)
|
||||
|
||||
AD1882 / AD1882A
|
||||
================
|
||||
3stack 3-stack mode (default)
|
||||
6stack 6-stack mode
|
||||
|
||||
AD1884A / AD1883 / AD1984A / AD1984B
|
||||
====================================
|
||||
desktop 3-stack desktop (default)
|
||||
laptop laptop with HP jack sensing
|
||||
mobile mobile devices with HP jack sensing
|
||||
thinkpad Lenovo Thinkpad X300
|
||||
|
||||
AD1884
|
||||
======
|
||||
N/A
|
||||
|
||||
AD1981
|
||||
======
|
||||
basic 3-jack (default)
|
||||
hp HP nx6320
|
||||
thinkpad Lenovo Thinkpad T60/X60/Z60
|
||||
toshiba Toshiba U205
|
||||
|
||||
AD1983
|
||||
======
|
||||
N/A
|
||||
|
||||
AD1984
|
||||
======
|
||||
basic default configuration
|
||||
thinkpad Lenovo Thinkpad T61/X61
|
||||
dell Dell T3400
|
||||
|
||||
AD1986A
|
||||
=======
|
||||
6stack 6-jack, separate surrounds (default)
|
||||
3stack 3-stack, shared surrounds
|
||||
laptop 2-channel only (FSC V2060, Samsung M50)
|
||||
laptop-eapd 2-channel with EAPD (ASUS A6J)
|
||||
laptop-automute 2-channel with EAPD and HP-automute (Lenovo N100)
|
||||
ultra 2-channel with EAPD (Samsung Ultra tablet PC)
|
||||
samsung 2-channel with EAPD (Samsung R65)
|
||||
|
||||
AD1988/AD1988B/AD1989A/AD1989B
|
||||
==============================
|
||||
6stack 6-jack
|
||||
6stack-dig ditto with SPDIF
|
||||
3stack 3-jack
|
||||
3stack-dig ditto with SPDIF
|
||||
laptop 3-jack with hp-jack automute
|
||||
laptop-dig ditto with SPDIF
|
||||
auto auto-config reading BIOS (default)
|
||||
|
||||
Conexant 5045
|
||||
=============
|
||||
laptop-hpsense Laptop with HP sense (old model laptop)
|
||||
laptop-micsense Laptop with Mic sense (old model fujitsu)
|
||||
laptop-hpmicsense Laptop with HP and Mic senses
|
||||
benq Benq R55E
|
||||
test for testing/debugging purpose, almost all controls
|
||||
can be adjusted. Appearing only when compiled with
|
||||
$CONFIG_SND_DEBUG=y
|
||||
|
||||
Conexant 5047
|
||||
=============
|
||||
laptop Basic Laptop config
|
||||
laptop-hp Laptop config for some HP models (subdevice 30A5)
|
||||
laptop-eapd Laptop config with EAPD support
|
||||
test for testing/debugging purpose, almost all controls
|
||||
can be adjusted. Appearing only when compiled with
|
||||
$CONFIG_SND_DEBUG=y
|
||||
|
||||
Conexant 5051
|
||||
=============
|
||||
laptop Basic Laptop config (default)
|
||||
hp HP Spartan laptop
|
||||
|
||||
STAC9200
|
||||
========
|
||||
ref Reference board
|
||||
dell-d21 Dell (unknown)
|
||||
dell-d22 Dell (unknown)
|
||||
dell-d23 Dell (unknown)
|
||||
dell-m21 Dell Inspiron 630m, Dell Inspiron 640m
|
||||
dell-m22 Dell Latitude D620, Dell Latitude D820
|
||||
dell-m23 Dell XPS M1710, Dell Precision M90
|
||||
dell-m24 Dell Latitude 120L
|
||||
dell-m25 Dell Inspiron E1505n
|
||||
dell-m26 Dell Inspiron 1501
|
||||
dell-m27 Dell Inspiron E1705/9400
|
||||
gateway Gateway laptops with EAPD control
|
||||
panasonic Panasonic CF-74
|
||||
|
||||
STAC9205/9254
|
||||
=============
|
||||
ref Reference board
|
||||
dell-m42 Dell (unknown)
|
||||
dell-m43 Dell Precision
|
||||
dell-m44 Dell Inspiron
|
||||
|
||||
STAC9220/9221
|
||||
=============
|
||||
ref Reference board
|
||||
3stack D945 3stack
|
||||
5stack D945 5stack + SPDIF
|
||||
intel-mac-v1 Intel Mac Type 1
|
||||
intel-mac-v2 Intel Mac Type 2
|
||||
intel-mac-v3 Intel Mac Type 3
|
||||
intel-mac-v4 Intel Mac Type 4
|
||||
intel-mac-v5 Intel Mac Type 5
|
||||
intel-mac-auto Intel Mac (detect type according to subsystem id)
|
||||
macmini Intel Mac Mini (equivalent with type 3)
|
||||
macbook Intel Mac Book (eq. type 5)
|
||||
macbook-pro-v1 Intel Mac Book Pro 1st generation (eq. type 3)
|
||||
macbook-pro Intel Mac Book Pro 2nd generation (eq. type 3)
|
||||
imac-intel Intel iMac (eq. type 2)
|
||||
imac-intel-20 Intel iMac (newer version) (eq. type 3)
|
||||
dell-d81 Dell (unknown)
|
||||
dell-d82 Dell (unknown)
|
||||
dell-m81 Dell (unknown)
|
||||
dell-m82 Dell XPS M1210
|
||||
|
||||
STAC9202/9250/9251
|
||||
==================
|
||||
ref Reference board, base config
|
||||
m2-2 Some Gateway MX series laptops
|
||||
m6 Some Gateway NX series laptops
|
||||
pa6 Gateway NX860 series
|
||||
|
||||
STAC9227/9228/9229/927x
|
||||
=======================
|
||||
ref Reference board
|
||||
ref-no-jd Reference board without HP/Mic jack detection
|
||||
3stack D965 3stack
|
||||
5stack D965 5stack + SPDIF
|
||||
dell-3stack Dell Dimension E520
|
||||
dell-bios Fixes with Dell BIOS setup
|
||||
|
||||
STAC92HD71B*
|
||||
============
|
||||
ref Reference board
|
||||
dell-m4-1 Dell desktops
|
||||
dell-m4-2 Dell desktops
|
||||
dell-m4-3 Dell desktops
|
||||
|
||||
STAC92HD73*
|
||||
===========
|
||||
ref Reference board
|
||||
no-jd BIOS setup but without jack-detection
|
||||
dell-m6-amic Dell desktops/laptops with analog mics
|
||||
dell-m6-dmic Dell desktops/laptops with digital mics
|
||||
dell-m6 Dell desktops/laptops with both type of mics
|
||||
|
||||
STAC92HD83*
|
||||
===========
|
||||
ref Reference board
|
||||
|
||||
STAC9872
|
||||
========
|
||||
vaio Setup for VAIO FE550G/SZ110
|
||||
vaio-ar Setup for VAIO AR
|
|
@ -0,0 +1,577 @@
|
|||
MORE NOTES ON HD-AUDIO DRIVER
|
||||
=============================
|
||||
Takashi Iwai <tiwai@suse.de>
|
||||
|
||||
|
||||
GENERAL
|
||||
-------
|
||||
|
||||
HD-audio is the new standard on-board audio component on modern PCs
|
||||
after AC97. Although Linux has been supporting HD-audio since long
|
||||
time ago, there are often problems with new machines. A part of the
|
||||
problem is broken BIOS, and the rest is the driver implementation.
|
||||
This document explains the brief trouble-shooting and debugging
|
||||
methods for the HD-audio hardware.
|
||||
|
||||
The HD-audio component consists of two parts: the controller chip and
|
||||
the codec chips on the HD-audio bus. Linux provides a single driver
|
||||
for all controllers, snd-hda-intel. Although the driver name contains
|
||||
a word of a well-known harware vendor, it's not specific to it but for
|
||||
all controller chips by other companies. Since the HD-audio
|
||||
controllers are supposed to be compatible, the single snd-hda-driver
|
||||
should work in most cases. But, not surprisingly, there are known
|
||||
bugs and issues specific to each controller type. The snd-hda-intel
|
||||
driver has a bunch of workarounds for these as described below.
|
||||
|
||||
A controller may have multiple codecs. Usually you have one audio
|
||||
codec and optionally one modem codec. In theory, there might be
|
||||
multiple audio codecs, e.g. for analog and digital outputs, and the
|
||||
driver might not work properly because of conflict of mixer elements.
|
||||
This should be fixed in future if such hardware really exists.
|
||||
|
||||
The snd-hda-intel driver has several different codec parsers depending
|
||||
on the codec. It has a generic parser as a fallback, but this
|
||||
functionality is fairly limited until now. Instead of the generic
|
||||
parser, usually the codec-specific parser (coded in patch_*.c) is used
|
||||
for the codec-specific implementations. The details about the
|
||||
codec-specific problems are explained in the later sections.
|
||||
|
||||
If you are interested in the deep debugging of HD-audio, read the
|
||||
HD-audio specification at first. The specification is found on
|
||||
Intel's web page, for example:
|
||||
|
||||
- http://www.intel.com/standards/hdaudio/
|
||||
|
||||
|
||||
HD-AUDIO CONTROLLER
|
||||
-------------------
|
||||
|
||||
DMA-Position Problem
|
||||
~~~~~~~~~~~~~~~~~~~~
|
||||
The most common problem of the controller is the inaccurate DMA
|
||||
pointer reporting. The DMA pointer for playback and capture can be
|
||||
read in two ways, either via a LPIB register or via a position-buffer
|
||||
map. As default the driver tries to read from the io-mapped
|
||||
position-buffer, and falls back to LPIB if the position-buffer appears
|
||||
dead. However, this detection isn't perfect on some devices. In such
|
||||
a case, you can change the default method via `position_fix` option.
|
||||
|
||||
`position_fix=1` means to use LPIB method explicitly.
|
||||
`position_fix=2` means to use the position-buffer. 0 is the default
|
||||
value, the automatic check and fallback to LPIB as described in the
|
||||
above. If you get a problem of repeated sounds, this option might
|
||||
help.
|
||||
|
||||
In addition to that, every controller is known to be broken regarding
|
||||
the wake-up timing. It wakes up a few samples before actually
|
||||
processing the data on the buffer. This caused a lot of problems, for
|
||||
example, with ALSA dmix or JACK. Since 2.6.27 kernel, the driver puts
|
||||
an artificial delay to the wake up timing. This delay is controlled
|
||||
via `bdl_pos_adj` option.
|
||||
|
||||
When `bdl_pos_adj` is a negative value (as default), it's assigned to
|
||||
an appropriate value depending on the controller chip. For Intel
|
||||
chips, it'd be 1 while it'd be 32 for others. Usually this works.
|
||||
Only in case it doesn't work and you get warning messages, you should
|
||||
change this parameter to other values.
|
||||
|
||||
|
||||
Codec-Probing Problem
|
||||
~~~~~~~~~~~~~~~~~~~~~
|
||||
A less often but a more severe problem is the codec probing. When
|
||||
BIOS reports the available codec slots wrongly, the driver gets
|
||||
confused and tries to access the non-existing codec slot. This often
|
||||
results in the total screw-up, and destructs the further communication
|
||||
with the codec chips. The symptom appears usually as error messages
|
||||
like:
|
||||
------------------------------------------------------------------------
|
||||
hda_intel: azx_get_response timeout, switching to polling mode:
|
||||
last cmd=0x12345678
|
||||
hda_intel: azx_get_response timeout, switching to single_cmd mode:
|
||||
last cmd=0x12345678
|
||||
------------------------------------------------------------------------
|
||||
|
||||
The first line is a warning, and this is usually relatively harmless.
|
||||
It means that the codec response isn't notified via an IRQ. The
|
||||
driver uses explicit polling method to read the response. It gives
|
||||
very slight CPU overhead, but you'd unlikely notice it.
|
||||
|
||||
The second line is, however, a fatal error. If this happens, usually
|
||||
it means that something is really wrong. Most likely you are
|
||||
accessing a non-existing codec slot.
|
||||
|
||||
Thus, if the second error message appears, try to narrow the probed
|
||||
codec slots via `probe_mask` option. It's a bitmask, and each bit
|
||||
corresponds to the codec slot. For example, to probe only the first
|
||||
slot, pass `probe_mask=1`. For the first and the third slots, pass
|
||||
`probe_mask=5` (where 5 = 1 | 4), and so on.
|
||||
|
||||
Since 2.6.29 kernel, the driver has a more robust probing method, so
|
||||
this error might happen rarely, though.
|
||||
|
||||
|
||||
Interrupt Handling
|
||||
~~~~~~~~~~~~~~~~~~
|
||||
In rare but some cases, the interrupt isn't properly handled as
|
||||
default. You would notice this by the DMA transfer error reported by
|
||||
ALSA PCM core, for example. Using MSI might help in such a case.
|
||||
Pass `enable_msi=1` option for enabling MSI.
|
||||
|
||||
|
||||
HD-AUDIO CODEC
|
||||
--------------
|
||||
|
||||
Model Option
|
||||
~~~~~~~~~~~~
|
||||
The most common problem regarding the HD-audio driver is the
|
||||
unsupported codec features or the mismatched device configuration.
|
||||
Most of codec-specific code has several preset models, either to
|
||||
override the BIOS setup or to provide more comprehensive features.
|
||||
|
||||
The driver checks PCI SSID and looks through the static configuration
|
||||
table until any matching entry is found. If you have a new machine,
|
||||
you may see a message like below:
|
||||
------------------------------------------------------------------------
|
||||
hda_codec: Unknown model for ALC880, trying auto-probe from BIOS...
|
||||
------------------------------------------------------------------------
|
||||
Even if you see such a message, DON'T PANIC. Take a deep breath and
|
||||
keep your towel. First of all, it's an informational message, no
|
||||
warning, no error. This means that the PCI SSID of your device isn't
|
||||
listed in the known preset model (white-)list. But, this doesn't mean
|
||||
that the driver is broken. Many codec-drivers provide the automatic
|
||||
configuration mechanism based on the BIOS setup.
|
||||
|
||||
The HD-audio codec has usually "pin" widgets, and BIOS sets the default
|
||||
configuration of each pin, which indicates the location, the
|
||||
connection type, the jack color, etc. The HD-audio driver can guess
|
||||
the right connection judging from these default configuration values.
|
||||
However -- some codec-support codes, such as patch_analog.c, don't
|
||||
support the automatic probing (yet as of 2.6.28). And, BIOS is often,
|
||||
yes, pretty often broken. It sets up wrong values and screws up the
|
||||
driver.
|
||||
|
||||
The preset model is provided basically to overcome such a situation.
|
||||
When the matching preset model is found in the white-list, the driver
|
||||
assumes the static configuration of that preset and builds the mixer
|
||||
elements and PCM streams based on the static information. Thus, if
|
||||
you have a newer machine with a slightly different PCI SSID from the
|
||||
existing one, you may have a good chance to re-use the same model.
|
||||
You can pass the `model` option to specify the preset model instead of
|
||||
PCI SSID look-up.
|
||||
|
||||
What `model` option values are available depends on the codec chip.
|
||||
Check your codec chip from the codec proc file (see "Codec Proc-File"
|
||||
section below). It will show the vendor/product name of your codec
|
||||
chip. Then, see Documentation/sound/alsa/HD-Audio-Modelstxt file,
|
||||
the section of HD-audio driver. You can find a list of codecs
|
||||
and `model` options belonging to each codec. For example, for Realtek
|
||||
ALC262 codec chip, pass `model=ultra` for devices that are compatible
|
||||
with Samsung Q1 Ultra.
|
||||
|
||||
Thus, the first thing you can do for any brand-new, unsupported and
|
||||
non-working HD-audio hardware is to check HD-audio codec and several
|
||||
different `model` option values. If you have a luck, some of them
|
||||
might suit with your device well.
|
||||
|
||||
Some codecs such as ALC880 have a special model option `model=test`.
|
||||
This configures the driver to provide as many mixer controls as
|
||||
possible for every single pin feature except for the unsolicited
|
||||
events (and maybe some other specials). Adjust each mixer element and
|
||||
try the I/O in the way of trial-and-error until figuring out the whole
|
||||
I/O pin mappings.
|
||||
|
||||
Note that `model=generic` has a special meaning. It means to use the
|
||||
generic parser regardless of the codec. Usually the codec-specific
|
||||
parser is much better than the generic parser (as now). Thus this
|
||||
option is more about the debugging purpose.
|
||||
|
||||
|
||||
Speaker and Headphone Output
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
One of the most frequent (and obvious) bugs with HD-audio is the
|
||||
silent output from either or both of a built-in speaker and a
|
||||
headphone jack. In general, you should try a headphone output at
|
||||
first. A speaker output often requires more additional controls like
|
||||
the external amplifier bits. Thus a headphone output has a slightly
|
||||
better chance.
|
||||
|
||||
Before making a bug report, double-check whether the mixer is set up
|
||||
correctly. The recent version of snd-hda-intel driver provides mostly
|
||||
"Master" volume control as well as "Front" volume (where Front
|
||||
indicates the front-channels). In addition, there can be individual
|
||||
"Headphone" and "Speaker" controls.
|
||||
|
||||
Ditto for the speaker output. There can be "External Amplifier"
|
||||
switch on some codecs. Turn on this if present.
|
||||
|
||||
Another related problem is the automatic mute of speaker output by
|
||||
headphone plugging. This feature is implemented in most cases, but
|
||||
not on every preset model or codec-support code.
|
||||
|
||||
In anyway, try a different model option if you have such a problem.
|
||||
Some other models may match better and give you more matching
|
||||
functionality. If none of the available models works, send a bug
|
||||
report. See the bug report section for details.
|
||||
|
||||
If you are masochistic enough to debug the driver problem, note the
|
||||
following:
|
||||
|
||||
- The speaker (and the headphone, too) output often requires the
|
||||
external amplifier. This can be set usually via EAPD verb or a
|
||||
certain GPIO. If the codec pin supports EAPD, you have a better
|
||||
chance via SET_EAPD_BTL verb (0x70c). On others, GPIO pin (mostly
|
||||
it's either GPIO0 or GPIO1) may turn on/off EAPD.
|
||||
- Some Realtek codecs require special vendor-specific coefficients to
|
||||
turn on the amplifier. See patch_realtek.c.
|
||||
- IDT codecs may have extra power-enable/disable controls on each
|
||||
analog pin. See patch_sigmatel.c.
|
||||
- Very rare but some devices don't accept the pin-detection verb until
|
||||
triggered. Issuing GET_PIN_SENSE verb (0xf09) may result in the
|
||||
codec-communication stall. Some examples are found in
|
||||
patch_realtek.c.
|
||||
|
||||
|
||||
Capture Problems
|
||||
~~~~~~~~~~~~~~~~
|
||||
The capture problems are often because of missing setups of mixers.
|
||||
Thus, before submitting a bug report, make sure that you set up the
|
||||
mixer correctly. For example, both "Capture Volume" and "Capture
|
||||
Switch" have to be set properly in addition to the right "Capture
|
||||
Source" or "Input Source" selection. Some devices have "Mic Boost"
|
||||
volume or switch.
|
||||
|
||||
When the PCM device is opened via "default" PCM (without pulse-audio
|
||||
plugin), you'll likely have "Digital Capture Volume" control as well.
|
||||
This is provided for the extra gain/attenuation of the signal in
|
||||
software, especially for the inputs without the hardware volume
|
||||
control such as digital microphones. Unless really needed, this
|
||||
should be set to exactly 50%, corresponding to 0dB -- neither extra
|
||||
gain nor attenuation. When you use "hw" PCM, i.e., a raw access PCM,
|
||||
this control will have no influence, though.
|
||||
|
||||
It's known that some codecs / devices have fairly bad analog circuits,
|
||||
and the recorded sound contains a certain DC-offset. This is no bug
|
||||
of the driver.
|
||||
|
||||
Most of modern laptops have no analog CD-input connection. Thus, the
|
||||
recording from CD input won't work in many cases although the driver
|
||||
provides it as the capture source. Use CDDA instead.
|
||||
|
||||
The automatic switching of the built-in and external mic per plugging
|
||||
is implemented on some codec models but not on every model. Partly
|
||||
because of my laziness but mostly lack of testers. Feel free to
|
||||
submit the improvement patch to the author.
|
||||
|
||||
|
||||
Direct Debugging
|
||||
~~~~~~~~~~~~~~~~
|
||||
If no model option gives you a better result, and you are a tough guy
|
||||
to fight against evil, try debugging via hitting the raw HD-audio
|
||||
codec verbs to the device. Some tools are available: hda-emu and
|
||||
hda-analyzer. The detailed description is found in the sections
|
||||
below. You'd need to enable hwdep for using these tools. See "Kernel
|
||||
Configuration" section.
|
||||
|
||||
|
||||
OTHER ISSUES
|
||||
------------
|
||||
|
||||
Kernel Configuration
|
||||
~~~~~~~~~~~~~~~~~~~~
|
||||
In general, I recommend you to enable the sound debug option,
|
||||
`CONFIG_SND_DEBUG=y`, no matter whether you are debugging or not.
|
||||
This enables snd_printd() macro and others, and you'll get additional
|
||||
kernel messages at probing.
|
||||
|
||||
In addition, you can enable `CONFIG_SND_DEBUG_VERBOSE=y`. But this
|
||||
will give you far more messages. Thus turn this on only when you are
|
||||
sure to want it.
|
||||
|
||||
Don't forget to turn on the appropriate `CONFIG_SND_HDA_CODEC_*`
|
||||
options. Note that each of them corresponds to the codec chip, not
|
||||
the controller chip. Thus, even if lspci shows the Nvidia controller,
|
||||
you may need to choose the option for other vendors. If you are
|
||||
unsure, just select all yes.
|
||||
|
||||
`CONFIG_SND_HDA_HWDEP` is a useful option for debugging the driver.
|
||||
When this is enabled, the driver creates hardware-dependent devices
|
||||
(one per each codec), and you have a raw access to the device via
|
||||
these device files. For example, `hwC0D2` will be created for the
|
||||
codec slot #2 of the first card (#0). For debug-tools such as
|
||||
hda-verb and hda-analyzer, the hwdep device has to be enabled.
|
||||
Thus, it'd be better to turn this on always.
|
||||
|
||||
`CONFIG_SND_HDA_RECONFIG` is a new option, and this depends on the
|
||||
hwdep option above. When enabled, you'll have some sysfs files under
|
||||
the corresponding hwdep directory. See "HD-audio reconfiguration"
|
||||
section below.
|
||||
|
||||
`CONFIG_SND_HDA_POWER_SAVE` option enables the power-saving feature.
|
||||
See "Power-saving" section below.
|
||||
|
||||
|
||||
Codec Proc-File
|
||||
~~~~~~~~~~~~~~~
|
||||
The codec proc-file is a treasure-chest for debugging HD-audio.
|
||||
It shows most of useful information of each codec widget.
|
||||
|
||||
The proc file is located in /proc/asound/card*/codec#*, one file per
|
||||
each codec slot. You can know the codec vendor, product id and
|
||||
names, the type of each widget, capabilities and so on.
|
||||
This file, however, doesn't show the jack sensing state, so far. This
|
||||
is because the jack-sensing might be depending on the trigger state.
|
||||
|
||||
This file will be picked up by the debug tools, and also it can be fed
|
||||
to the emulator as the primary codec information. See the debug tools
|
||||
section below.
|
||||
|
||||
This proc file can be also used to check whether the generic parser is
|
||||
used. When the generic parser is used, the vendor/product ID name
|
||||
will appear as "Realtek ID 0262", instead of "Realtek ALC262".
|
||||
|
||||
|
||||
HD-Audio Reconfiguration
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
This is an experimental feature to allow you re-configure the HD-audio
|
||||
codec dynamically without reloading the driver. The following sysfs
|
||||
files are available under each codec-hwdep device directory (e.g.
|
||||
/sys/class/sound/hwC0D0):
|
||||
|
||||
vendor_id::
|
||||
Shows the 32bit codec vendor-id hex number. You can change the
|
||||
vendor-id value by writing to this file.
|
||||
subsystem_id::
|
||||
Shows the 32bit codec subsystem-id hex number. You can change the
|
||||
subsystem-id value by writing to this file.
|
||||
revision_id::
|
||||
Shows the 32bit codec revision-id hex number. You can change the
|
||||
revision-id value by writing to this file.
|
||||
afg::
|
||||
Shows the AFG ID. This is read-only.
|
||||
mfg::
|
||||
Shows the MFG ID. This is read-only.
|
||||
name::
|
||||
Shows the codec name string. Can be changed by writing to this
|
||||
file.
|
||||
modelname::
|
||||
Shows the currently set `model` option. Can be changed by writing
|
||||
to this file.
|
||||
init_verbs::
|
||||
The extra verbs to execute at initialization. You can add a verb by
|
||||
writing to this file. Pass tree numbers, nid, verb and parameter.
|
||||
hints::
|
||||
Shows hint strings for codec parsers for any use. Right now it's
|
||||
not used.
|
||||
reconfig::
|
||||
Triggers the codec re-configuration. When any value is written to
|
||||
this file, the driver re-initialize and parses the codec tree
|
||||
again. All the changes done by the sysfs entries above are taken
|
||||
into account.
|
||||
clear::
|
||||
Resets the codec, removes the mixer elements and PCM stuff of the
|
||||
specified codec, and clear all init verbs and hints.
|
||||
|
||||
|
||||
Power-Saving
|
||||
~~~~~~~~~~~~
|
||||
The power-saving is a kind of auto-suspend of the device. When the
|
||||
device is inactive for a certain time, the device is automatically
|
||||
turned off to save the power. The time to go down is specified via
|
||||
`power_save` module option, and this option can be changed dynamically
|
||||
via sysfs.
|
||||
|
||||
The power-saving won't work when the analog loopback is enabled on
|
||||
some codecs. Make sure that you mute all unneeded signal routes when
|
||||
you want the power-saving.
|
||||
|
||||
The power-saving feature might cause audible click noises at each
|
||||
power-down/up depending on the device. Some of them might be
|
||||
solvable, but some are hard, I'm afraid. Some distros such as
|
||||
openSUSE enables the power-saving feature automatically when the power
|
||||
cable is unplugged. Thus, if you hear noises, suspect first the
|
||||
power-saving. See /sys/module/snd_hda_intel/parameters/power_save to
|
||||
check the current value. If it's non-zero, the feature is turned on.
|
||||
|
||||
|
||||
Development Tree
|
||||
~~~~~~~~~~~~~~~~
|
||||
The latest development codes for HD-audio are found on sound git tree:
|
||||
|
||||
- git://git.kernel.org/pub/scm/linux/kernel/git/tiwai/sound-2.6.git
|
||||
|
||||
The master branch or for-next branches can be used as the main
|
||||
development branches in general while the HD-audio specific patches
|
||||
are committed in topic/hda branch.
|
||||
|
||||
If you are using the latest Linus tree, it'd be better to pull the
|
||||
above GIT tree onto it. If you are using the older kernels, an easy
|
||||
way to try the latest ALSA code is to build from the snapshot
|
||||
tarball. There are daily tarballs and the latest snapshot tarball.
|
||||
All can be built just like normal alsa-driver release packages, that
|
||||
is, installed via the usual spells: configure, make and make
|
||||
install(-modules). See INSTALL in the package. The snapshot tarballs
|
||||
are found at:
|
||||
|
||||
- ftp://ftp.kernel.org/pub/linux/kernel/people/tiwai/snapshot/
|
||||
|
||||
|
||||
Sending a Bug Report
|
||||
~~~~~~~~~~~~~~~~~~~~
|
||||
If any model or module options don't work for your device, it's time
|
||||
to send a bug report to the developers. Give the following in your
|
||||
bug report:
|
||||
|
||||
- Hardware vendor, product and model names
|
||||
- Kernel version (and ALSA-driver version if you built externally)
|
||||
- `alsa-info.sh` output; run with `--no-upload` option. See the
|
||||
section below about alsa-info
|
||||
|
||||
If it's a regression, at best, send alsa-info outputs of both working
|
||||
and non-working kernels. This is really helpful because we can
|
||||
compare the codec registers directly.
|
||||
|
||||
Send a bug report either the followings:
|
||||
|
||||
kernel-bugzilla::
|
||||
http://bugme.linux-foundation.org/
|
||||
alsa-devel ML::
|
||||
alsa-devel@alsa-project.org
|
||||
|
||||
|
||||
DEBUG TOOLS
|
||||
-----------
|
||||
|
||||
This section describes some tools available for debugging HD-audio
|
||||
problems.
|
||||
|
||||
alsa-info
|
||||
~~~~~~~~~
|
||||
The script `alsa-info.sh` is a very useful tool to gather the audio
|
||||
device information. You can fetch the latest version from:
|
||||
|
||||
- http://www.alsa-project.org/alsa-info.sh
|
||||
|
||||
Run this script as root, and it will gather the important information
|
||||
such as the module lists, module parameters, proc file contents
|
||||
including the codec proc files, mixer outputs and the control
|
||||
elements. As default, it will store the information onto a web server
|
||||
on alsa-project.org. But, if you send a bug report, it'd be better to
|
||||
run with `--no-upload` option, and attach the generated file.
|
||||
|
||||
There are some other useful options. See `--help` option output for
|
||||
details.
|
||||
|
||||
|
||||
hda-verb
|
||||
~~~~~~~~
|
||||
hda-verb is a tiny program that allows you to access the HD-audio
|
||||
codec directly. You can execute a raw HD-audio codec verb with this.
|
||||
This program accesses the hwdep device, thus you need to enable the
|
||||
kernel config `CONFIG_SND_HDA_HWDEP=y` beforehand.
|
||||
|
||||
The hda-verb program takes four arguments: the hwdep device file, the
|
||||
widget NID, the verb and the parameter. When you access to the codec
|
||||
on the slot 2 of the card 0, pass /dev/snd/hwC0D2 to the first
|
||||
argument, typically. (However, the real path name depends on the
|
||||
system.)
|
||||
|
||||
The second parameter is the widget number-id to access. The third
|
||||
parameter can be either a hex/digit number or a string corresponding
|
||||
to a verb. Similarly, the last parameter is the value to write, or
|
||||
can be a string for the parameter type.
|
||||
|
||||
------------------------------------------------------------------------
|
||||
% hda-verb /dev/snd/hwC0D0 0x12 0x701 2
|
||||
nid = 0x12, verb = 0x701, param = 0x2
|
||||
value = 0x0
|
||||
|
||||
% hda-verb /dev/snd/hwC0D0 0x0 PARAMETERS VENDOR_ID
|
||||
nid = 0x0, verb = 0xf00, param = 0x0
|
||||
value = 0x10ec0262
|
||||
|
||||
% hda-verb /dev/snd/hwC0D0 2 set_a 0xb080
|
||||
nid = 0x2, verb = 0x300, param = 0xb080
|
||||
value = 0x0
|
||||
------------------------------------------------------------------------
|
||||
|
||||
Although you can issue any verbs with this program, the driver state
|
||||
won't be always updated. For example, the volume values are usually
|
||||
cached in the driver, and thus changing the widget amp value directly
|
||||
via hda-verb won't change the mixer value.
|
||||
|
||||
The hda-verb program is found in the ftp directory:
|
||||
|
||||
- ftp://ftp.kernel.org/pub/linux/kernel/people/tiwai/misc/
|
||||
|
||||
Also a git repository is available:
|
||||
|
||||
- git://git.kernel.org/pub/scm/linux/kernel/git/tiwai/hda-verb.git
|
||||
|
||||
See README file in the tarball for more details about hda-verb
|
||||
program.
|
||||
|
||||
|
||||
hda-analyzer
|
||||
~~~~~~~~~~~~
|
||||
hda-analyzer provides a graphical interface to access the raw HD-audio
|
||||
control, based on pyGTK2 binding. It's a more powerful version of
|
||||
hda-verb. The program gives you an easy-to-use GUI stuff for showing
|
||||
the widget information and adjusting the amp values, as well as the
|
||||
proc-compatible output.
|
||||
|
||||
The hda-analyzer is a part of alsa.git repository in
|
||||
alsa-project.org:
|
||||
|
||||
- http://git.alsa-project.org/?p=alsa.git;a=tree;f=hda-analyzer
|
||||
|
||||
|
||||
Codecgraph
|
||||
~~~~~~~~~~
|
||||
Codecgraph is a utility program to generate a graph and visualizes the
|
||||
codec-node connection of a codec chip. It's especially useful when
|
||||
you analyze or debug a codec without a proper datasheet. The program
|
||||
parses the given codec proc file and converts to SVG via graphiz
|
||||
program.
|
||||
|
||||
The tarball and GIT trees are found in the web page at:
|
||||
|
||||
- http://helllabs.org/codecgraph/
|
||||
|
||||
|
||||
hda-emu
|
||||
~~~~~~~
|
||||
hda-emu is an HD-audio emulator. The main purpose of this program is
|
||||
to debug an HD-audio codec without the real hardware. Thus, it
|
||||
doesn't emulate the behavior with the real audio I/O, but it just
|
||||
dumps the codec register changes and the ALSA-driver internal changes
|
||||
at probing and operating the HD-audio driver.
|
||||
|
||||
The program requires a codec proc-file to simulate. Get a proc file
|
||||
for the target codec beforehand, or pick up an example codec from the
|
||||
codec proc collections in the tarball. Then, run the program with the
|
||||
proc file, and the hda-emu program will start parsing the codec file
|
||||
and simulates the HD-audio driver:
|
||||
|
||||
------------------------------------------------------------------------
|
||||
% hda-emu codecs/stac9200-dell-d820-laptop
|
||||
# Parsing..
|
||||
hda_codec: Unknown model for STAC9200, using BIOS defaults
|
||||
hda_codec: pin nid 08 bios pin config 40c003fa
|
||||
....
|
||||
------------------------------------------------------------------------
|
||||
|
||||
The program gives you only a very dumb command-line interface. You
|
||||
can get a proc-file dump at the current state, get a list of control
|
||||
(mixer) elements, set/get the control element value, simulate the PCM
|
||||
operation, the jack plugging simulation, etc.
|
||||
|
||||
The package is found in:
|
||||
|
||||
- ftp://ftp.kernel.org/pub/linux/kernel/people/tiwai/misc/
|
||||
|
||||
A git repository is available:
|
||||
|
||||
- git://git.kernel.org/pub/scm/linux/kernel/git/tiwai/hda-emu.git
|
||||
|
||||
See README file in the tarball for more details about hda-emu
|
||||
program.
|
|
@ -153,6 +153,16 @@ card*/codec#*
|
|||
Shows the general codec information and the attribute of each
|
||||
widget node.
|
||||
|
||||
card*/eld#*
|
||||
Available for HDMI or DisplayPort interfaces.
|
||||
Shows ELD(EDID Like Data) info retrieved from the attached HDMI sink,
|
||||
and describes its audio capabilities and configurations.
|
||||
|
||||
Some ELD fields may be modified by doing `echo name hex_value > eld#*`.
|
||||
Only do this if you are sure the HDMI sink provided value is wrong.
|
||||
And if that makes your HDMI audio work, please report to us so that we
|
||||
can fix it in future kernel releases.
|
||||
|
||||
|
||||
Sequencer Information
|
||||
---------------------
|
||||
|
|
|
@ -9,7 +9,7 @@ the audio subsystem with the kernel as a platform device and is represented by
|
|||
the following struct:-
|
||||
|
||||
/* SoC machine */
|
||||
struct snd_soc_machine {
|
||||
struct snd_soc_card {
|
||||
char *name;
|
||||
|
||||
int (*probe)(struct platform_device *pdev);
|
||||
|
@ -67,10 +67,10 @@ static struct snd_soc_dai_link corgi_dai = {
|
|||
.ops = &corgi_ops,
|
||||
};
|
||||
|
||||
struct snd_soc_machine then sets up the machine with it's DAIs. e.g.
|
||||
struct snd_soc_card then sets up the machine with it's DAIs. e.g.
|
||||
|
||||
/* corgi audio machine driver */
|
||||
static struct snd_soc_machine snd_soc_machine_corgi = {
|
||||
static struct snd_soc_card snd_soc_corgi = {
|
||||
.name = "Corgi",
|
||||
.dai_link = &corgi_dai,
|
||||
.num_links = 1,
|
||||
|
@ -90,7 +90,7 @@ static struct wm8731_setup_data corgi_wm8731_setup = {
|
|||
|
||||
/* corgi audio subsystem */
|
||||
static struct snd_soc_device corgi_snd_devdata = {
|
||||
.machine = &snd_soc_machine_corgi,
|
||||
.machine = &snd_soc_corgi,
|
||||
.platform = &pxa2xx_soc_platform,
|
||||
.codec_dev = &soc_codec_dev_wm8731,
|
||||
.codec_data = &corgi_wm8731_setup,
|
||||
|
|
|
@ -3977,7 +3977,7 @@ M: tiwai@suse.de
|
|||
L: alsa-devel@alsa-project.org (subscribers-only)
|
||||
S: Maintained
|
||||
|
||||
SOUND - SOC LAYER / DYNAMIC AUDIO POWER MANAGEMENT
|
||||
SOUND - SOC LAYER / DYNAMIC AUDIO POWER MANAGEMENT (ASoC)
|
||||
P: Liam Girdwood
|
||||
M: lrg@slimlogic.co.uk
|
||||
P: Mark Brown
|
||||
|
|
|
@ -0,0 +1,13 @@
|
|||
#ifndef _INCLUDE_PALMASOC_H_
|
||||
#define _INCLUDE_PALMASOC_H_
|
||||
struct palm27x_asoc_info {
|
||||
int jack_gpio;
|
||||
};
|
||||
|
||||
#ifdef CONFIG_SND_PXA2XX_SOC_PALM27X
|
||||
void __init palm27x_asoc_set_pdata(struct palm27x_asoc_info *data);
|
||||
#else
|
||||
static inline void palm27x_asoc_set_pdata(struct palm27x_asoc_info *data) {}
|
||||
#endif
|
||||
|
||||
#endif
|
|
@ -659,6 +659,8 @@ struct input_absinfo {
|
|||
#define SW_RADIO SW_RFKILL_ALL /* deprecated */
|
||||
#define SW_MICROPHONE_INSERT 0x04 /* set = inserted */
|
||||
#define SW_DOCK 0x05 /* set = plugged into dock */
|
||||
#define SW_LINEOUT_INSERT 0x06 /* set = inserted */
|
||||
#define SW_JACK_PHYSICAL_INSERT 0x07 /* set = mechanical switch set */
|
||||
#define SW_MAX 0x0f
|
||||
#define SW_CNT (SW_MAX+1)
|
||||
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
/*
|
||||
* audio.h -- Audio Driver for Wolfson WM8350 PMIC
|
||||
*
|
||||
* Copyright 2007 Wolfson Microelectronics PLC
|
||||
* Copyright 2007, 2008 Wolfson Microelectronics PLC
|
||||
*
|
||||
* 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
|
||||
|
@ -70,9 +70,9 @@
|
|||
#define WM8350_CODEC_ISEL_0_5 3 /* x0.5 */
|
||||
|
||||
#define WM8350_VMID_OFF 0
|
||||
#define WM8350_VMID_500K 1
|
||||
#define WM8350_VMID_100K 2
|
||||
#define WM8350_VMID_10K 3
|
||||
#define WM8350_VMID_300K 1
|
||||
#define WM8350_VMID_50K 2
|
||||
#define WM8350_VMID_5K 3
|
||||
|
||||
/*
|
||||
* R40 (0x28) - Clock Control 1
|
||||
|
@ -591,8 +591,38 @@
|
|||
#define WM8350_IRQ_CODEC_MICSCD 41
|
||||
#define WM8350_IRQ_CODEC_MICD 42
|
||||
|
||||
/*
|
||||
* WM8350 Platform data.
|
||||
*
|
||||
* This must be initialised per platform for best audio performance.
|
||||
* Please see WM8350 datasheet for information.
|
||||
*/
|
||||
struct wm8350_audio_platform_data {
|
||||
int vmid_discharge_msecs; /* VMID --> OFF discharge time */
|
||||
int drain_msecs; /* OFF drain time */
|
||||
int cap_discharge_msecs; /* Cap ON (from OFF) discharge time */
|
||||
int vmid_charge_msecs; /* vmid power up time */
|
||||
u32 vmid_s_curve:2; /* vmid enable s curve speed */
|
||||
u32 dis_out4:2; /* out4 discharge speed */
|
||||
u32 dis_out3:2; /* out3 discharge speed */
|
||||
u32 dis_out2:2; /* out2 discharge speed */
|
||||
u32 dis_out1:2; /* out1 discharge speed */
|
||||
u32 vroi_out4:1; /* out4 tie off */
|
||||
u32 vroi_out3:1; /* out3 tie off */
|
||||
u32 vroi_out2:1; /* out2 tie off */
|
||||
u32 vroi_out1:1; /* out1 tie off */
|
||||
u32 vroi_enable:1; /* enable tie off */
|
||||
u32 codec_current_on:2; /* current level ON */
|
||||
u32 codec_current_standby:2; /* current level STANDBY */
|
||||
u32 codec_current_charge:2; /* codec current @ vmid charge */
|
||||
};
|
||||
|
||||
struct snd_soc_codec;
|
||||
|
||||
struct wm8350_codec {
|
||||
struct platform_device *pdev;
|
||||
struct snd_soc_codec *codec;
|
||||
struct wm8350_audio_platform_data *platform_data;
|
||||
};
|
||||
|
||||
#endif
|
||||
|
|
|
@ -281,10 +281,12 @@
|
|||
/* specific - Analog Devices */
|
||||
#define AC97_AD_TEST 0x5a /* test register */
|
||||
#define AC97_AD_TEST2 0x5c /* undocumented test register 2 */
|
||||
#define AC97_AD_HPFD_SHIFT 12 /* High Pass Filter Disable */
|
||||
#define AC97_AD_CODEC_CFG 0x70 /* codec configuration */
|
||||
#define AC97_AD_JACK_SPDIF 0x72 /* Jack Sense & S/PDIF */
|
||||
#define AC97_AD_SERIAL_CFG 0x74 /* Serial Configuration */
|
||||
#define AC97_AD_MISC 0x76 /* Misc Control Bits */
|
||||
#define AC97_AD_VREFD_SHIFT 2 /* V_REFOUT Disable (AD1888) */
|
||||
|
||||
/* specific - Cirrus Logic */
|
||||
#define AC97_CSR_ACMODE 0x5e /* AC Mode Register */
|
||||
|
|
|
@ -575,6 +575,7 @@ enum {
|
|||
#define SNDRV_TIMER_GLOBAL_SYSTEM 0
|
||||
#define SNDRV_TIMER_GLOBAL_RTC 1
|
||||
#define SNDRV_TIMER_GLOBAL_HPET 2
|
||||
#define SNDRV_TIMER_GLOBAL_HRTIMER 3
|
||||
|
||||
/* info flags */
|
||||
#define SNDRV_TIMER_FLG_SLAVE (1<<0) /* cannot be controlled */
|
||||
|
|
|
@ -353,7 +353,7 @@ void snd_verbose_printd(const char *file, int line, const char *format, ...)
|
|||
* snd_printk - printk wrapper
|
||||
* @fmt: format string
|
||||
*
|
||||
* Works like print() but prints the file and the line of the caller
|
||||
* Works like printk() but prints the file and the line of the caller
|
||||
* when configured with CONFIG_SND_VERBOSE_PRINTK.
|
||||
*/
|
||||
#define snd_printk(fmt, args...) \
|
||||
|
@ -380,18 +380,40 @@ void snd_verbose_printd(const char *file, int line, const char *format, ...)
|
|||
printk(fmt ,##args)
|
||||
#endif
|
||||
|
||||
/**
|
||||
* snd_BUG - give a BUG warning message and stack trace
|
||||
*
|
||||
* Calls WARN() if CONFIG_SND_DEBUG is set.
|
||||
* Ignored when CONFIG_SND_DEBUG is not set.
|
||||
*/
|
||||
#define snd_BUG() WARN(1, "BUG?\n")
|
||||
|
||||
/**
|
||||
* snd_BUG_ON - debugging check macro
|
||||
* @cond: condition to evaluate
|
||||
*
|
||||
* When CONFIG_SND_DEBUG is set, this macro evaluates the given condition,
|
||||
* and call WARN() and returns the value if it's non-zero.
|
||||
*
|
||||
* When CONFIG_SND_DEBUG is not set, this just returns zero, and the given
|
||||
* condition is ignored.
|
||||
*
|
||||
* NOTE: the argument won't be evaluated at all when CONFIG_SND_DEBUG=n.
|
||||
* Thus, don't put any statement that influences on the code behavior,
|
||||
* such as pre/post increment, to the argument of this macro.
|
||||
* If you want to evaluate and give a warning, use standard WARN_ON().
|
||||
*/
|
||||
#define snd_BUG_ON(cond) WARN((cond), "BUG? (%s)\n", __stringify(cond))
|
||||
|
||||
#else /* !CONFIG_SND_DEBUG */
|
||||
|
||||
#define snd_printd(fmt, args...) do { } while (0)
|
||||
#define snd_BUG() do { } while (0)
|
||||
static inline int __snd_bug_on(void)
|
||||
static inline int __snd_bug_on(int cond)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
#define snd_BUG_ON(cond) __snd_bug_on() /* always false */
|
||||
#define snd_BUG_ON(cond) __snd_bug_on(0 && (cond)) /* always false */
|
||||
|
||||
#endif /* CONFIG_SND_DEBUG */
|
||||
|
||||
|
|
|
@ -40,30 +40,34 @@ struct snd_info_buffer {
|
|||
struct snd_info_entry;
|
||||
|
||||
struct snd_info_entry_text {
|
||||
void (*read) (struct snd_info_entry *entry, struct snd_info_buffer *buffer);
|
||||
void (*write) (struct snd_info_entry *entry, struct snd_info_buffer *buffer);
|
||||
void (*read)(struct snd_info_entry *entry,
|
||||
struct snd_info_buffer *buffer);
|
||||
void (*write)(struct snd_info_entry *entry,
|
||||
struct snd_info_buffer *buffer);
|
||||
};
|
||||
|
||||
struct snd_info_entry_ops {
|
||||
int (*open) (struct snd_info_entry *entry,
|
||||
unsigned short mode, void **file_private_data);
|
||||
int (*release) (struct snd_info_entry * entry,
|
||||
unsigned short mode, void *file_private_data);
|
||||
long (*read) (struct snd_info_entry *entry, void *file_private_data,
|
||||
struct file * file, char __user *buf,
|
||||
int (*open)(struct snd_info_entry *entry,
|
||||
unsigned short mode, void **file_private_data);
|
||||
int (*release)(struct snd_info_entry *entry,
|
||||
unsigned short mode, void *file_private_data);
|
||||
long (*read)(struct snd_info_entry *entry, void *file_private_data,
|
||||
struct file *file, char __user *buf,
|
||||
unsigned long count, unsigned long pos);
|
||||
long (*write)(struct snd_info_entry *entry, void *file_private_data,
|
||||
struct file *file, const char __user *buf,
|
||||
unsigned long count, unsigned long pos);
|
||||
long (*write) (struct snd_info_entry *entry, void *file_private_data,
|
||||
struct file * file, const char __user *buf,
|
||||
unsigned long count, unsigned long pos);
|
||||
long long (*llseek) (struct snd_info_entry *entry, void *file_private_data,
|
||||
struct file * file, long long offset, int orig);
|
||||
unsigned int (*poll) (struct snd_info_entry *entry, void *file_private_data,
|
||||
struct file * file, poll_table * wait);
|
||||
int (*ioctl) (struct snd_info_entry *entry, void *file_private_data,
|
||||
struct file * file, unsigned int cmd, unsigned long arg);
|
||||
int (*mmap) (struct snd_info_entry *entry, void *file_private_data,
|
||||
struct inode * inode, struct file * file,
|
||||
struct vm_area_struct * vma);
|
||||
long long (*llseek)(struct snd_info_entry *entry,
|
||||
void *file_private_data, struct file *file,
|
||||
long long offset, int orig);
|
||||
unsigned int(*poll)(struct snd_info_entry *entry,
|
||||
void *file_private_data, struct file *file,
|
||||
poll_table *wait);
|
||||
int (*ioctl)(struct snd_info_entry *entry, void *file_private_data,
|
||||
struct file *file, unsigned int cmd, unsigned long arg);
|
||||
int (*mmap)(struct snd_info_entry *entry, void *file_private_data,
|
||||
struct inode *inode, struct file *file,
|
||||
struct vm_area_struct *vma);
|
||||
};
|
||||
|
||||
struct snd_info_entry {
|
||||
|
@ -106,34 +110,37 @@ void snd_card_info_read_oss(struct snd_info_buffer *buffer);
|
|||
static inline void snd_card_info_read_oss(struct snd_info_buffer *buffer) {}
|
||||
#endif
|
||||
|
||||
int snd_iprintf(struct snd_info_buffer * buffer, char *fmt,...) __attribute__ ((format (printf, 2, 3)));
|
||||
int snd_iprintf(struct snd_info_buffer *buffer, char *fmt, ...) \
|
||||
__attribute__ ((format (printf, 2, 3)));
|
||||
int snd_info_init(void);
|
||||
int snd_info_done(void);
|
||||
|
||||
int snd_info_get_line(struct snd_info_buffer * buffer, char *line, int len);
|
||||
int snd_info_get_line(struct snd_info_buffer *buffer, char *line, int len);
|
||||
char *snd_info_get_str(char *dest, char *src, int len);
|
||||
struct snd_info_entry *snd_info_create_module_entry(struct module * module,
|
||||
struct snd_info_entry *snd_info_create_module_entry(struct module *module,
|
||||
const char *name,
|
||||
struct snd_info_entry * parent);
|
||||
struct snd_info_entry *snd_info_create_card_entry(struct snd_card * card,
|
||||
struct snd_info_entry *parent);
|
||||
struct snd_info_entry *snd_info_create_card_entry(struct snd_card *card,
|
||||
const char *name,
|
||||
struct snd_info_entry * parent);
|
||||
void snd_info_free_entry(struct snd_info_entry * entry);
|
||||
int snd_info_store_text(struct snd_info_entry * entry);
|
||||
int snd_info_restore_text(struct snd_info_entry * entry);
|
||||
struct snd_info_entry *parent);
|
||||
void snd_info_free_entry(struct snd_info_entry *entry);
|
||||
int snd_info_store_text(struct snd_info_entry *entry);
|
||||
int snd_info_restore_text(struct snd_info_entry *entry);
|
||||
|
||||
int snd_info_card_create(struct snd_card * card);
|
||||
int snd_info_card_register(struct snd_card * card);
|
||||
int snd_info_card_free(struct snd_card * card);
|
||||
void snd_info_card_disconnect(struct snd_card * card);
|
||||
int snd_info_register(struct snd_info_entry * entry);
|
||||
int snd_info_card_create(struct snd_card *card);
|
||||
int snd_info_card_register(struct snd_card *card);
|
||||
int snd_info_card_free(struct snd_card *card);
|
||||
void snd_info_card_disconnect(struct snd_card *card);
|
||||
void snd_info_card_id_change(struct snd_card *card);
|
||||
int snd_info_register(struct snd_info_entry *entry);
|
||||
|
||||
/* for card drivers */
|
||||
int snd_card_proc_new(struct snd_card *card, const char *name, struct snd_info_entry **entryp);
|
||||
int snd_card_proc_new(struct snd_card *card, const char *name,
|
||||
struct snd_info_entry **entryp);
|
||||
|
||||
static inline void snd_info_set_text_ops(struct snd_info_entry *entry,
|
||||
void *private_data,
|
||||
void (*read)(struct snd_info_entry *, struct snd_info_buffer *))
|
||||
void *private_data,
|
||||
void (*read)(struct snd_info_entry *, struct snd_info_buffer *))
|
||||
{
|
||||
entry->private_data = private_data;
|
||||
entry->c.text.read = read;
|
||||
|
@ -146,21 +153,22 @@ int snd_info_check_reserved_words(const char *str);
|
|||
#define snd_seq_root NULL
|
||||
#define snd_oss_root NULL
|
||||
|
||||
static inline int snd_iprintf(struct snd_info_buffer * buffer, char *fmt,...) { return 0; }
|
||||
static inline int snd_iprintf(struct snd_info_buffer *buffer, char *fmt, ...) { return 0; }
|
||||
static inline int snd_info_init(void) { return 0; }
|
||||
static inline int snd_info_done(void) { return 0; }
|
||||
|
||||
static inline int snd_info_get_line(struct snd_info_buffer * buffer, char *line, int len) { return 0; }
|
||||
static inline int snd_info_get_line(struct snd_info_buffer *buffer, char *line, int len) { return 0; }
|
||||
static inline char *snd_info_get_str(char *dest, char *src, int len) { return NULL; }
|
||||
static inline struct snd_info_entry *snd_info_create_module_entry(struct module * module, const char *name, struct snd_info_entry * parent) { return NULL; }
|
||||
static inline struct snd_info_entry *snd_info_create_card_entry(struct snd_card * card, const char *name, struct snd_info_entry * parent) { return NULL; }
|
||||
static inline void snd_info_free_entry(struct snd_info_entry * entry) { ; }
|
||||
static inline struct snd_info_entry *snd_info_create_module_entry(struct module *module, const char *name, struct snd_info_entry *parent) { return NULL; }
|
||||
static inline struct snd_info_entry *snd_info_create_card_entry(struct snd_card *card, const char *name, struct snd_info_entry *parent) { return NULL; }
|
||||
static inline void snd_info_free_entry(struct snd_info_entry *entry) { ; }
|
||||
|
||||
static inline int snd_info_card_create(struct snd_card * card) { return 0; }
|
||||
static inline int snd_info_card_register(struct snd_card * card) { return 0; }
|
||||
static inline int snd_info_card_free(struct snd_card * card) { return 0; }
|
||||
static inline void snd_info_card_disconnect(struct snd_card * card) { }
|
||||
static inline int snd_info_register(struct snd_info_entry * entry) { return 0; }
|
||||
static inline int snd_info_card_create(struct snd_card *card) { return 0; }
|
||||
static inline int snd_info_card_register(struct snd_card *card) { return 0; }
|
||||
static inline int snd_info_card_free(struct snd_card *card) { return 0; }
|
||||
static inline void snd_info_card_disconnect(struct snd_card *card) { }
|
||||
static inline void snd_info_card_id_change(struct snd_card *card) { }
|
||||
static inline int snd_info_register(struct snd_info_entry *entry) { return 0; }
|
||||
|
||||
static inline int snd_card_proc_new(struct snd_card *card, const char *name,
|
||||
struct snd_info_entry **entryp) { return -EINVAL; }
|
||||
|
|
|
@ -35,6 +35,8 @@ enum snd_jack_types {
|
|||
SND_JACK_HEADPHONE = 0x0001,
|
||||
SND_JACK_MICROPHONE = 0x0002,
|
||||
SND_JACK_HEADSET = SND_JACK_HEADPHONE | SND_JACK_MICROPHONE,
|
||||
SND_JACK_LINEOUT = 0x0004,
|
||||
SND_JACK_MECHANICAL = 0x0008, /* If detected separately */
|
||||
};
|
||||
|
||||
struct snd_jack {
|
||||
|
|
|
@ -0,0 +1,18 @@
|
|||
#ifndef _L3_H_
|
||||
#define _L3_H_ 1
|
||||
|
||||
struct l3_pins {
|
||||
void (*setdat)(int);
|
||||
void (*setclk)(int);
|
||||
void (*setmode)(int);
|
||||
int data_hold;
|
||||
int data_setup;
|
||||
int clock_high;
|
||||
int mode_hold;
|
||||
int mode;
|
||||
int mode_setup;
|
||||
};
|
||||
|
||||
int l3_write(struct l3_pins *adap, u8 addr, u8 *data, int len);
|
||||
|
||||
#endif
|
|
@ -0,0 +1,14 @@
|
|||
#ifndef _S3C24XX_UDA134X_H_
|
||||
#define _S3C24XX_UDA134X_H_ 1
|
||||
|
||||
#include <sound/uda134x.h>
|
||||
|
||||
struct s3c24xx_uda134x_platform_data {
|
||||
int l3_clk;
|
||||
int l3_mode;
|
||||
int l3_data;
|
||||
void (*power) (int);
|
||||
int model;
|
||||
};
|
||||
|
||||
#endif
|
|
@ -0,0 +1,231 @@
|
|||
/*
|
||||
* linux/sound/soc-dai.h -- ALSA SoC Layer
|
||||
*
|
||||
* Copyright: 2005-2008 Wolfson Microelectronics. PLC.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License version 2 as
|
||||
* published by the Free Software Foundation.
|
||||
*
|
||||
* Digital Audio Interface (DAI) API.
|
||||
*/
|
||||
|
||||
#ifndef __LINUX_SND_SOC_DAI_H
|
||||
#define __LINUX_SND_SOC_DAI_H
|
||||
|
||||
|
||||
#include <linux/list.h>
|
||||
|
||||
struct snd_pcm_substream;
|
||||
|
||||
/*
|
||||
* DAI hardware audio formats.
|
||||
*
|
||||
* Describes the physical PCM data formating and clocking. Add new formats
|
||||
* to the end.
|
||||
*/
|
||||
#define SND_SOC_DAIFMT_I2S 0 /* I2S mode */
|
||||
#define SND_SOC_DAIFMT_RIGHT_J 1 /* Right Justified mode */
|
||||
#define SND_SOC_DAIFMT_LEFT_J 2 /* Left Justified mode */
|
||||
#define SND_SOC_DAIFMT_DSP_A 3 /* L data msb after FRM LRC */
|
||||
#define SND_SOC_DAIFMT_DSP_B 4 /* L data msb during FRM LRC */
|
||||
#define SND_SOC_DAIFMT_AC97 5 /* AC97 */
|
||||
|
||||
/* left and right justified also known as MSB and LSB respectively */
|
||||
#define SND_SOC_DAIFMT_MSB SND_SOC_DAIFMT_LEFT_J
|
||||
#define SND_SOC_DAIFMT_LSB SND_SOC_DAIFMT_RIGHT_J
|
||||
|
||||
/*
|
||||
* DAI Clock gating.
|
||||
*
|
||||
* DAI bit clocks can be be gated (disabled) when not the DAI is not
|
||||
* sending or receiving PCM data in a frame. This can be used to save power.
|
||||
*/
|
||||
#define SND_SOC_DAIFMT_CONT (0 << 4) /* continuous clock */
|
||||
#define SND_SOC_DAIFMT_GATED (1 << 4) /* clock is gated */
|
||||
|
||||
/*
|
||||
* DAI Left/Right Clocks.
|
||||
*
|
||||
* Specifies whether the DAI can support different samples for similtanious
|
||||
* playback and capture. This usually requires a seperate physical frame
|
||||
* clock for playback and capture.
|
||||
*/
|
||||
#define SND_SOC_DAIFMT_SYNC (0 << 5) /* Tx FRM = Rx FRM */
|
||||
#define SND_SOC_DAIFMT_ASYNC (1 << 5) /* Tx FRM ~ Rx FRM */
|
||||
|
||||
/*
|
||||
* TDM
|
||||
*
|
||||
* Time Division Multiplexing. Allows PCM data to be multplexed with other
|
||||
* data on the DAI.
|
||||
*/
|
||||
#define SND_SOC_DAIFMT_TDM (1 << 6)
|
||||
|
||||
/*
|
||||
* DAI hardware signal inversions.
|
||||
*
|
||||
* Specifies whether the DAI can also support inverted clocks for the specified
|
||||
* format.
|
||||
*/
|
||||
#define SND_SOC_DAIFMT_NB_NF (0 << 8) /* normal bit clock + frame */
|
||||
#define SND_SOC_DAIFMT_NB_IF (1 << 8) /* normal bclk + inv frm */
|
||||
#define SND_SOC_DAIFMT_IB_NF (2 << 8) /* invert bclk + nor frm */
|
||||
#define SND_SOC_DAIFMT_IB_IF (3 << 8) /* invert bclk + frm */
|
||||
|
||||
/*
|
||||
* DAI hardware clock masters.
|
||||
*
|
||||
* This is wrt the codec, the inverse is true for the interface
|
||||
* i.e. if the codec is clk and frm master then the interface is
|
||||
* clk and frame slave.
|
||||
*/
|
||||
#define SND_SOC_DAIFMT_CBM_CFM (0 << 12) /* codec clk & frm master */
|
||||
#define SND_SOC_DAIFMT_CBS_CFM (1 << 12) /* codec clk slave & frm master */
|
||||
#define SND_SOC_DAIFMT_CBM_CFS (2 << 12) /* codec clk master & frame slave */
|
||||
#define SND_SOC_DAIFMT_CBS_CFS (3 << 12) /* codec clk & frm slave */
|
||||
|
||||
#define SND_SOC_DAIFMT_FORMAT_MASK 0x000f
|
||||
#define SND_SOC_DAIFMT_CLOCK_MASK 0x00f0
|
||||
#define SND_SOC_DAIFMT_INV_MASK 0x0f00
|
||||
#define SND_SOC_DAIFMT_MASTER_MASK 0xf000
|
||||
|
||||
/*
|
||||
* Master Clock Directions
|
||||
*/
|
||||
#define SND_SOC_CLOCK_IN 0
|
||||
#define SND_SOC_CLOCK_OUT 1
|
||||
|
||||
struct snd_soc_dai_ops;
|
||||
struct snd_soc_dai;
|
||||
struct snd_ac97_bus_ops;
|
||||
|
||||
/* Digital Audio Interface registration */
|
||||
int snd_soc_register_dai(struct snd_soc_dai *dai);
|
||||
void snd_soc_unregister_dai(struct snd_soc_dai *dai);
|
||||
int snd_soc_register_dais(struct snd_soc_dai *dai, size_t count);
|
||||
void snd_soc_unregister_dais(struct snd_soc_dai *dai, size_t count);
|
||||
|
||||
/* Digital Audio Interface clocking API.*/
|
||||
int snd_soc_dai_set_sysclk(struct snd_soc_dai *dai, int clk_id,
|
||||
unsigned int freq, int dir);
|
||||
|
||||
int snd_soc_dai_set_clkdiv(struct snd_soc_dai *dai,
|
||||
int div_id, int div);
|
||||
|
||||
int snd_soc_dai_set_pll(struct snd_soc_dai *dai,
|
||||
int pll_id, unsigned int freq_in, unsigned int freq_out);
|
||||
|
||||
/* Digital Audio interface formatting */
|
||||
int snd_soc_dai_set_fmt(struct snd_soc_dai *dai, unsigned int fmt);
|
||||
|
||||
int snd_soc_dai_set_tdm_slot(struct snd_soc_dai *dai,
|
||||
unsigned int mask, int slots);
|
||||
|
||||
int snd_soc_dai_set_tristate(struct snd_soc_dai *dai, int tristate);
|
||||
|
||||
/* Digital Audio Interface mute */
|
||||
int snd_soc_dai_digital_mute(struct snd_soc_dai *dai, int mute);
|
||||
|
||||
/*
|
||||
* Digital Audio Interface.
|
||||
*
|
||||
* Describes the Digital Audio Interface in terms of it's ALSA, DAI and AC97
|
||||
* operations an capabilities. Codec and platfom drivers will register a this
|
||||
* structure for every DAI they have.
|
||||
*
|
||||
* This structure covers the clocking, formating and ALSA operations for each
|
||||
* interface a
|
||||
*/
|
||||
struct snd_soc_dai_ops {
|
||||
/*
|
||||
* DAI clocking configuration, all optional.
|
||||
* Called by soc_card drivers, normally in their hw_params.
|
||||
*/
|
||||
int (*set_sysclk)(struct snd_soc_dai *dai,
|
||||
int clk_id, unsigned int freq, int dir);
|
||||
int (*set_pll)(struct snd_soc_dai *dai,
|
||||
int pll_id, unsigned int freq_in, unsigned int freq_out);
|
||||
int (*set_clkdiv)(struct snd_soc_dai *dai, int div_id, int div);
|
||||
|
||||
/*
|
||||
* DAI format configuration
|
||||
* Called by soc_card drivers, normally in their hw_params.
|
||||
*/
|
||||
int (*set_fmt)(struct snd_soc_dai *dai, unsigned int fmt);
|
||||
int (*set_tdm_slot)(struct snd_soc_dai *dai,
|
||||
unsigned int mask, int slots);
|
||||
int (*set_tristate)(struct snd_soc_dai *dai, int tristate);
|
||||
|
||||
/*
|
||||
* DAI digital mute - optional.
|
||||
* Called by soc-core to minimise any pops.
|
||||
*/
|
||||
int (*digital_mute)(struct snd_soc_dai *dai, int mute);
|
||||
|
||||
/*
|
||||
* ALSA PCM audio operations - all optional.
|
||||
* Called by soc-core during audio PCM operations.
|
||||
*/
|
||||
int (*startup)(struct snd_pcm_substream *,
|
||||
struct snd_soc_dai *);
|
||||
void (*shutdown)(struct snd_pcm_substream *,
|
||||
struct snd_soc_dai *);
|
||||
int (*hw_params)(struct snd_pcm_substream *,
|
||||
struct snd_pcm_hw_params *, struct snd_soc_dai *);
|
||||
int (*hw_free)(struct snd_pcm_substream *,
|
||||
struct snd_soc_dai *);
|
||||
int (*prepare)(struct snd_pcm_substream *,
|
||||
struct snd_soc_dai *);
|
||||
int (*trigger)(struct snd_pcm_substream *, int,
|
||||
struct snd_soc_dai *);
|
||||
};
|
||||
|
||||
/*
|
||||
* Digital Audio Interface runtime data.
|
||||
*
|
||||
* Holds runtime data for a DAI.
|
||||
*/
|
||||
struct snd_soc_dai {
|
||||
/* DAI description */
|
||||
char *name;
|
||||
unsigned int id;
|
||||
int ac97_control;
|
||||
|
||||
struct device *dev;
|
||||
|
||||
/* DAI callbacks */
|
||||
int (*probe)(struct platform_device *pdev,
|
||||
struct snd_soc_dai *dai);
|
||||
void (*remove)(struct platform_device *pdev,
|
||||
struct snd_soc_dai *dai);
|
||||
int (*suspend)(struct snd_soc_dai *dai);
|
||||
int (*resume)(struct snd_soc_dai *dai);
|
||||
|
||||
/* ops */
|
||||
struct snd_soc_dai_ops ops;
|
||||
|
||||
/* DAI capabilities */
|
||||
struct snd_soc_pcm_stream capture;
|
||||
struct snd_soc_pcm_stream playback;
|
||||
|
||||
/* DAI runtime info */
|
||||
struct snd_pcm_runtime *runtime;
|
||||
struct snd_soc_codec *codec;
|
||||
unsigned int active;
|
||||
unsigned char pop_wait:1;
|
||||
void *dma_data;
|
||||
|
||||
/* DAI private data */
|
||||
void *private_data;
|
||||
|
||||
/* parent codec/platform */
|
||||
union {
|
||||
struct snd_soc_codec *codec;
|
||||
struct snd_soc_platform *platform;
|
||||
};
|
||||
|
||||
struct list_head list;
|
||||
};
|
||||
|
||||
#endif
|
|
@ -221,8 +221,6 @@ int snd_soc_dapm_new_controls(struct snd_soc_codec *codec,
|
|||
int num);
|
||||
|
||||
/* dapm path setup */
|
||||
int __deprecated snd_soc_dapm_connect_input(struct snd_soc_codec *codec,
|
||||
const char *sink_name, const char *control_name, const char *src_name);
|
||||
int snd_soc_dapm_new_widgets(struct snd_soc_codec *codec);
|
||||
void snd_soc_dapm_free(struct snd_soc_device *socdev);
|
||||
int snd_soc_dapm_add_routes(struct snd_soc_codec *codec,
|
||||
|
|
|
@ -21,8 +21,6 @@
|
|||
#include <sound/control.h>
|
||||
#include <sound/ac97_codec.h>
|
||||
|
||||
#define SND_SOC_VERSION "0.13.2"
|
||||
|
||||
/*
|
||||
* Convenience kcontrol builders
|
||||
*/
|
||||
|
@ -145,105 +143,31 @@ enum snd_soc_bias_level {
|
|||
SND_SOC_BIAS_OFF,
|
||||
};
|
||||
|
||||
/*
|
||||
* Digital Audio Interface (DAI) types
|
||||
*/
|
||||
#define SND_SOC_DAI_AC97 0x1
|
||||
#define SND_SOC_DAI_I2S 0x2
|
||||
#define SND_SOC_DAI_PCM 0x4
|
||||
#define SND_SOC_DAI_AC97_BUS 0x8 /* for custom i.e. non ac97_codec.c */
|
||||
|
||||
/*
|
||||
* DAI hardware audio formats
|
||||
*/
|
||||
#define SND_SOC_DAIFMT_I2S 0 /* I2S mode */
|
||||
#define SND_SOC_DAIFMT_RIGHT_J 1 /* Right justified mode */
|
||||
#define SND_SOC_DAIFMT_LEFT_J 2 /* Left Justified mode */
|
||||
#define SND_SOC_DAIFMT_DSP_A 3 /* L data msb after FRM or LRC */
|
||||
#define SND_SOC_DAIFMT_DSP_B 4 /* L data msb during FRM or LRC */
|
||||
#define SND_SOC_DAIFMT_AC97 5 /* AC97 */
|
||||
|
||||
#define SND_SOC_DAIFMT_MSB SND_SOC_DAIFMT_LEFT_J
|
||||
#define SND_SOC_DAIFMT_LSB SND_SOC_DAIFMT_RIGHT_J
|
||||
|
||||
/*
|
||||
* DAI Gating
|
||||
*/
|
||||
#define SND_SOC_DAIFMT_CONT (0 << 4) /* continuous clock */
|
||||
#define SND_SOC_DAIFMT_GATED (1 << 4) /* clock is gated when not Tx/Rx */
|
||||
|
||||
/*
|
||||
* DAI Sync
|
||||
* Synchronous LR (Left Right) clocks and Frame signals.
|
||||
*/
|
||||
#define SND_SOC_DAIFMT_SYNC (0 << 5) /* Tx FRM = Rx FRM */
|
||||
#define SND_SOC_DAIFMT_ASYNC (1 << 5) /* Tx FRM ~ Rx FRM */
|
||||
|
||||
/*
|
||||
* TDM
|
||||
*/
|
||||
#define SND_SOC_DAIFMT_TDM (1 << 6)
|
||||
|
||||
/*
|
||||
* DAI hardware signal inversions
|
||||
*/
|
||||
#define SND_SOC_DAIFMT_NB_NF (0 << 8) /* normal bclk + frm */
|
||||
#define SND_SOC_DAIFMT_NB_IF (1 << 8) /* normal bclk + inv frm */
|
||||
#define SND_SOC_DAIFMT_IB_NF (2 << 8) /* invert bclk + nor frm */
|
||||
#define SND_SOC_DAIFMT_IB_IF (3 << 8) /* invert bclk + frm */
|
||||
|
||||
/*
|
||||
* DAI hardware clock masters
|
||||
* This is wrt the codec, the inverse is true for the interface
|
||||
* i.e. if the codec is clk and frm master then the interface is
|
||||
* clk and frame slave.
|
||||
*/
|
||||
#define SND_SOC_DAIFMT_CBM_CFM (0 << 12) /* codec clk & frm master */
|
||||
#define SND_SOC_DAIFMT_CBS_CFM (1 << 12) /* codec clk slave & frm master */
|
||||
#define SND_SOC_DAIFMT_CBM_CFS (2 << 12) /* codec clk master & frame slave */
|
||||
#define SND_SOC_DAIFMT_CBS_CFS (3 << 12) /* codec clk & frm slave */
|
||||
|
||||
#define SND_SOC_DAIFMT_FORMAT_MASK 0x000f
|
||||
#define SND_SOC_DAIFMT_CLOCK_MASK 0x00f0
|
||||
#define SND_SOC_DAIFMT_INV_MASK 0x0f00
|
||||
#define SND_SOC_DAIFMT_MASTER_MASK 0xf000
|
||||
|
||||
|
||||
/*
|
||||
* Master Clock Directions
|
||||
*/
|
||||
#define SND_SOC_CLOCK_IN 0
|
||||
#define SND_SOC_CLOCK_OUT 1
|
||||
|
||||
/*
|
||||
* AC97 codec ID's bitmask
|
||||
*/
|
||||
#define SND_SOC_DAI_AC97_ID0 (1 << 0)
|
||||
#define SND_SOC_DAI_AC97_ID1 (1 << 1)
|
||||
#define SND_SOC_DAI_AC97_ID2 (1 << 2)
|
||||
#define SND_SOC_DAI_AC97_ID3 (1 << 3)
|
||||
|
||||
struct snd_soc_device;
|
||||
struct snd_soc_pcm_stream;
|
||||
struct snd_soc_ops;
|
||||
struct snd_soc_dai_mode;
|
||||
struct snd_soc_pcm_runtime;
|
||||
struct snd_soc_dai;
|
||||
struct snd_soc_platform;
|
||||
struct snd_soc_codec;
|
||||
struct snd_soc_machine_config;
|
||||
struct soc_enum;
|
||||
struct snd_soc_ac97_ops;
|
||||
struct snd_soc_clock_info;
|
||||
|
||||
typedef int (*hw_write_t)(void *,const char* ,int);
|
||||
typedef int (*hw_read_t)(void *,char* ,int);
|
||||
|
||||
extern struct snd_ac97_bus_ops soc_ac97_ops;
|
||||
|
||||
int snd_soc_register_platform(struct snd_soc_platform *platform);
|
||||
void snd_soc_unregister_platform(struct snd_soc_platform *platform);
|
||||
int snd_soc_register_codec(struct snd_soc_codec *codec);
|
||||
void snd_soc_unregister_codec(struct snd_soc_codec *codec);
|
||||
|
||||
/* pcm <-> DAI connect */
|
||||
void snd_soc_free_pcms(struct snd_soc_device *socdev);
|
||||
int snd_soc_new_pcms(struct snd_soc_device *socdev, int idx, const char *xid);
|
||||
int snd_soc_register_card(struct snd_soc_device *socdev);
|
||||
int snd_soc_init_card(struct snd_soc_device *socdev);
|
||||
|
||||
/* set runtime hw params */
|
||||
int snd_soc_set_runtime_hwparams(struct snd_pcm_substream *substream,
|
||||
|
@ -263,27 +187,6 @@ int snd_soc_new_ac97_codec(struct snd_soc_codec *codec,
|
|||
struct snd_ac97_bus_ops *ops, int num);
|
||||
void snd_soc_free_ac97_codec(struct snd_soc_codec *codec);
|
||||
|
||||
/* Digital Audio Interface clocking API.*/
|
||||
int snd_soc_dai_set_sysclk(struct snd_soc_dai *dai, int clk_id,
|
||||
unsigned int freq, int dir);
|
||||
|
||||
int snd_soc_dai_set_clkdiv(struct snd_soc_dai *dai,
|
||||
int div_id, int div);
|
||||
|
||||
int snd_soc_dai_set_pll(struct snd_soc_dai *dai,
|
||||
int pll_id, unsigned int freq_in, unsigned int freq_out);
|
||||
|
||||
/* Digital Audio interface formatting */
|
||||
int snd_soc_dai_set_fmt(struct snd_soc_dai *dai, unsigned int fmt);
|
||||
|
||||
int snd_soc_dai_set_tdm_slot(struct snd_soc_dai *dai,
|
||||
unsigned int mask, int slots);
|
||||
|
||||
int snd_soc_dai_set_tristate(struct snd_soc_dai *dai, int tristate);
|
||||
|
||||
/* Digital Audio Interface mute */
|
||||
int snd_soc_dai_digital_mute(struct snd_soc_dai *dai, int mute);
|
||||
|
||||
/*
|
||||
*Controls
|
||||
*/
|
||||
|
@ -341,66 +244,14 @@ struct snd_soc_ops {
|
|||
int (*trigger)(struct snd_pcm_substream *, int);
|
||||
};
|
||||
|
||||
/* ASoC DAI ops */
|
||||
struct snd_soc_dai_ops {
|
||||
/* DAI clocking configuration */
|
||||
int (*set_sysclk)(struct snd_soc_dai *dai,
|
||||
int clk_id, unsigned int freq, int dir);
|
||||
int (*set_pll)(struct snd_soc_dai *dai,
|
||||
int pll_id, unsigned int freq_in, unsigned int freq_out);
|
||||
int (*set_clkdiv)(struct snd_soc_dai *dai, int div_id, int div);
|
||||
|
||||
/* DAI format configuration */
|
||||
int (*set_fmt)(struct snd_soc_dai *dai, unsigned int fmt);
|
||||
int (*set_tdm_slot)(struct snd_soc_dai *dai,
|
||||
unsigned int mask, int slots);
|
||||
int (*set_tristate)(struct snd_soc_dai *dai, int tristate);
|
||||
|
||||
/* digital mute */
|
||||
int (*digital_mute)(struct snd_soc_dai *dai, int mute);
|
||||
};
|
||||
|
||||
/* SoC DAI (Digital Audio Interface) */
|
||||
struct snd_soc_dai {
|
||||
/* DAI description */
|
||||
char *name;
|
||||
unsigned int id;
|
||||
unsigned char type;
|
||||
|
||||
/* DAI callbacks */
|
||||
int (*probe)(struct platform_device *pdev,
|
||||
struct snd_soc_dai *dai);
|
||||
void (*remove)(struct platform_device *pdev,
|
||||
struct snd_soc_dai *dai);
|
||||
int (*suspend)(struct platform_device *pdev,
|
||||
struct snd_soc_dai *dai);
|
||||
int (*resume)(struct platform_device *pdev,
|
||||
struct snd_soc_dai *dai);
|
||||
|
||||
/* ops */
|
||||
struct snd_soc_ops ops;
|
||||
struct snd_soc_dai_ops dai_ops;
|
||||
|
||||
/* DAI capabilities */
|
||||
struct snd_soc_pcm_stream capture;
|
||||
struct snd_soc_pcm_stream playback;
|
||||
|
||||
/* DAI runtime info */
|
||||
struct snd_pcm_runtime *runtime;
|
||||
struct snd_soc_codec *codec;
|
||||
unsigned int active;
|
||||
unsigned char pop_wait:1;
|
||||
void *dma_data;
|
||||
|
||||
/* DAI private data */
|
||||
void *private_data;
|
||||
};
|
||||
|
||||
/* SoC Audio Codec */
|
||||
struct snd_soc_codec {
|
||||
char *name;
|
||||
struct module *owner;
|
||||
struct mutex mutex;
|
||||
struct device *dev;
|
||||
|
||||
struct list_head list;
|
||||
|
||||
/* callbacks */
|
||||
int (*set_bias_level)(struct snd_soc_codec *,
|
||||
|
@ -426,6 +277,7 @@ struct snd_soc_codec {
|
|||
short reg_cache_step;
|
||||
|
||||
/* dapm */
|
||||
u32 pop_time;
|
||||
struct list_head dapm_widgets;
|
||||
struct list_head dapm_paths;
|
||||
enum snd_soc_bias_level bias_level;
|
||||
|
@ -435,6 +287,11 @@ struct snd_soc_codec {
|
|||
/* codec DAI's */
|
||||
struct snd_soc_dai *dai;
|
||||
unsigned int num_dai;
|
||||
|
||||
#ifdef CONFIG_DEBUG_FS
|
||||
struct dentry *debugfs_reg;
|
||||
struct dentry *debugfs_pop_time;
|
||||
#endif
|
||||
};
|
||||
|
||||
/* codec device */
|
||||
|
@ -448,13 +305,12 @@ struct snd_soc_codec_device {
|
|||
/* SoC platform interface */
|
||||
struct snd_soc_platform {
|
||||
char *name;
|
||||
struct list_head list;
|
||||
|
||||
int (*probe)(struct platform_device *pdev);
|
||||
int (*remove)(struct platform_device *pdev);
|
||||
int (*suspend)(struct platform_device *pdev,
|
||||
struct snd_soc_dai *dai);
|
||||
int (*resume)(struct platform_device *pdev,
|
||||
struct snd_soc_dai *dai);
|
||||
int (*suspend)(struct snd_soc_dai *dai);
|
||||
int (*resume)(struct snd_soc_dai *dai);
|
||||
|
||||
/* pcm creation and destruction */
|
||||
int (*pcm_new)(struct snd_card *, struct snd_soc_dai *,
|
||||
|
@ -484,9 +340,14 @@ struct snd_soc_dai_link {
|
|||
struct snd_pcm *pcm;
|
||||
};
|
||||
|
||||
/* SoC machine */
|
||||
struct snd_soc_machine {
|
||||
/* SoC card */
|
||||
struct snd_soc_card {
|
||||
char *name;
|
||||
struct device *dev;
|
||||
|
||||
struct list_head list;
|
||||
|
||||
int instantiated;
|
||||
|
||||
int (*probe)(struct platform_device *pdev);
|
||||
int (*remove)(struct platform_device *pdev);
|
||||
|
@ -499,23 +360,26 @@ struct snd_soc_machine {
|
|||
int (*resume_post)(struct platform_device *pdev);
|
||||
|
||||
/* callbacks */
|
||||
int (*set_bias_level)(struct snd_soc_machine *,
|
||||
int (*set_bias_level)(struct snd_soc_card *,
|
||||
enum snd_soc_bias_level level);
|
||||
|
||||
/* CPU <--> Codec DAI links */
|
||||
struct snd_soc_dai_link *dai_link;
|
||||
int num_links;
|
||||
|
||||
struct snd_soc_device *socdev;
|
||||
|
||||
struct snd_soc_platform *platform;
|
||||
struct delayed_work delayed_work;
|
||||
struct work_struct deferred_resume_work;
|
||||
};
|
||||
|
||||
/* SoC Device - the audio subsystem */
|
||||
struct snd_soc_device {
|
||||
struct device *dev;
|
||||
struct snd_soc_machine *machine;
|
||||
struct snd_soc_platform *platform;
|
||||
struct snd_soc_card *card;
|
||||
struct snd_soc_codec *codec;
|
||||
struct snd_soc_codec_device *codec_dev;
|
||||
struct delayed_work delayed_work;
|
||||
struct work_struct deferred_resume_work;
|
||||
void *codec_data;
|
||||
};
|
||||
|
||||
|
@ -542,4 +406,6 @@ struct soc_enum {
|
|||
void *dapm;
|
||||
};
|
||||
|
||||
#include <sound/soc-dai.h>
|
||||
|
||||
#endif
|
||||
|
|
|
@ -0,0 +1,26 @@
|
|||
/*
|
||||
* uda134x.h -- UDA134x ALSA SoC Codec driver
|
||||
*
|
||||
* Copyright 2007 Dension Audio Systems Ltd.
|
||||
* Author: Zoltan Devai
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License version 2 as
|
||||
* published by the Free Software Foundation.
|
||||
*/
|
||||
|
||||
#ifndef _UDA134X_H
|
||||
#define _UDA134X_H
|
||||
|
||||
#include <sound/l3.h>
|
||||
|
||||
struct uda134x_platform_data {
|
||||
struct l3_pins l3;
|
||||
void (*power) (int);
|
||||
int model;
|
||||
#define UDA134X_UDA1340 1
|
||||
#define UDA134X_UDA1341 2
|
||||
#define UDA134X_UDA1344 3
|
||||
};
|
||||
|
||||
#endif /* _UDA134X_H */
|
|
@ -1,3 +1,3 @@
|
|||
/* include/version.h */
|
||||
#define CONFIG_SND_VERSION "1.0.18rc3"
|
||||
#define CONFIG_SND_VERSION "1.0.18a"
|
||||
#define CONFIG_SND_DATE ""
|
||||
|
|
|
@ -15,6 +15,7 @@
|
|||
#include <linux/init.h>
|
||||
#include <linux/device.h>
|
||||
#include <linux/string.h>
|
||||
#include <sound/ac97_codec.h>
|
||||
|
||||
/*
|
||||
* Let drivers decide whether they want to support given codec from their
|
||||
|
|
|
@ -1,3 +1,7 @@
|
|||
snd-aoa-codec-onyx-objs := onyx.o
|
||||
snd-aoa-codec-tas-objs := tas.o
|
||||
snd-aoa-codec-toonie-objs := toonie.o
|
||||
|
||||
obj-$(CONFIG_SND_AOA_ONYX) += snd-aoa-codec-onyx.o
|
||||
obj-$(CONFIG_SND_AOA_TAS) += snd-aoa-codec-tas.o
|
||||
obj-$(CONFIG_SND_AOA_TOONIE) += snd-aoa-codec-toonie.o
|
||||
|
|
|
@ -37,7 +37,7 @@ MODULE_AUTHOR("Johannes Berg <johannes@sipsolutions.net>");
|
|||
MODULE_LICENSE("GPL");
|
||||
MODULE_DESCRIPTION("pcm3052 (onyx) codec driver for snd-aoa");
|
||||
|
||||
#include "snd-aoa-codec-onyx.h"
|
||||
#include "onyx.h"
|
||||
#include "../aoa.h"
|
||||
#include "../soundbus/soundbus.h"
|
||||
|
||||
|
@ -292,7 +292,7 @@ static int onyx_snd_capture_source_put(struct snd_kcontrol *kcontrol,
|
|||
static struct snd_kcontrol_new capture_source_control = {
|
||||
.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
|
||||
/* If we name this 'Input Source', it properly shows up in
|
||||
* alsamixer as a selection, * but it's shown under the
|
||||
* alsamixer as a selection, * but it's shown under the
|
||||
* 'Playback' category.
|
||||
* If I name it 'Capture Source', it shows up in strange
|
||||
* ways (two bools of which one can be selected at a
|
||||
|
@ -477,7 +477,7 @@ static int onyx_spdif_mask_get(struct snd_kcontrol *kcontrol,
|
|||
|
||||
ucontrol->value.iec958.status[3] = 0x3f;
|
||||
ucontrol->value.iec958.status[4] = 0x0f;
|
||||
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
@ -682,7 +682,7 @@ static int onyx_usable(struct codec_info_item *cii,
|
|||
onyx_read_register(onyx, ONYX_REG_DIG_INFO4, &v);
|
||||
spdif_enabled = !!(v & ONYX_SPDIF_ENABLE);
|
||||
onyx_read_register(onyx, ONYX_REG_DAC_CONTROL, &v);
|
||||
analog_enabled =
|
||||
analog_enabled =
|
||||
(v & (ONYX_MUTE_RIGHT|ONYX_MUTE_LEFT))
|
||||
!= (ONYX_MUTE_RIGHT|ONYX_MUTE_LEFT);
|
||||
mutex_unlock(&onyx->mutex);
|
||||
|
@ -882,7 +882,7 @@ static int onyx_init_codec(struct aoa_codec *codec)
|
|||
msleep(1);
|
||||
onyx->codec.gpio->methods->set_hw_reset(onyx->codec.gpio, 0);
|
||||
msleep(1);
|
||||
|
||||
|
||||
if (onyx_register_init(onyx)) {
|
||||
printk(KERN_ERR PFX "failed to initialise onyx registers\n");
|
||||
return -ENODEV;
|
||||
|
@ -1069,7 +1069,7 @@ static int onyx_i2c_attach(struct i2c_adapter *adapter)
|
|||
|
||||
/* if that didn't work, try desperate mode for older
|
||||
* machines that have stuff missing from the device tree */
|
||||
|
||||
|
||||
if (!of_device_is_compatible(busnode, "k2-i2c"))
|
||||
return -ENODEV;
|
||||
|
|
@ -71,9 +71,9 @@ MODULE_AUTHOR("Johannes Berg <johannes@sipsolutions.net>");
|
|||
MODULE_LICENSE("GPL");
|
||||
MODULE_DESCRIPTION("tas codec driver for snd-aoa");
|
||||
|
||||
#include "snd-aoa-codec-tas.h"
|
||||
#include "snd-aoa-codec-tas-gain-table.h"
|
||||
#include "snd-aoa-codec-tas-basstreble.h"
|
||||
#include "tas.h"
|
||||
#include "tas-gain-table.h"
|
||||
#include "tas-basstreble.h"
|
||||
#include "../aoa.h"
|
||||
#include "../soundbus/soundbus.h"
|
||||
|
||||
|
@ -880,7 +880,7 @@ static void tas_exit_codec(struct aoa_codec *codec)
|
|||
return;
|
||||
tas->codec.soundbus_dev->detach_codec(tas->codec.soundbus_dev, tas);
|
||||
}
|
||||
|
||||
|
||||
|
||||
static struct i2c_driver tas_driver;
|
||||
|
|
@ -131,7 +131,7 @@ static int __init toonie_init(void)
|
|||
toonie->codec.owner = THIS_MODULE;
|
||||
toonie->codec.init = toonie_init_codec;
|
||||
toonie->codec.exit = toonie_exit_codec;
|
||||
|
||||
|
||||
if (aoa_codec_register(&toonie->codec)) {
|
||||
kfree(toonie);
|
||||
return -EINVAL;
|
|
@ -1,5 +1,5 @@
|
|||
obj-$(CONFIG_SND_AOA) += snd-aoa.o
|
||||
snd-aoa-objs := snd-aoa-core.o \
|
||||
snd-aoa-alsa.o \
|
||||
snd-aoa-gpio-pmf.o \
|
||||
snd-aoa-gpio-feature.o
|
||||
snd-aoa-objs := core.o \
|
||||
alsa.o \
|
||||
gpio-pmf.o \
|
||||
gpio-feature.o
|
||||
|
|
|
@ -6,7 +6,7 @@
|
|||
* GPL v2, can be found in COPYING.
|
||||
*/
|
||||
#include <linux/module.h>
|
||||
#include "snd-aoa-alsa.h"
|
||||
#include "alsa.h"
|
||||
|
||||
static int index = -1;
|
||||
module_param(index, int, 0444);
|
||||
|
@ -64,7 +64,7 @@ int aoa_snd_device_new(snd_device_type_t type,
|
|||
{
|
||||
struct snd_card *card = aoa_get_card();
|
||||
int err;
|
||||
|
||||
|
||||
if (!card) return -ENOMEM;
|
||||
|
||||
err = snd_device_new(card, type, device_data, ops);
|
|
@ -10,7 +10,7 @@
|
|||
#include <linux/module.h>
|
||||
#include <linux/list.h>
|
||||
#include "../aoa.h"
|
||||
#include "snd-aoa-alsa.h"
|
||||
#include "alsa.h"
|
||||
|
||||
MODULE_DESCRIPTION("Apple Onboard Audio Sound Driver");
|
||||
MODULE_AUTHOR("Johannes Berg <johannes@sipsolutions.net>");
|
|
@ -5,7 +5,7 @@
|
|||
*
|
||||
* GPL v2, can be found in COPYING.
|
||||
*
|
||||
* This file contains the GPIO control routines for
|
||||
* This file contains the GPIO control routines for
|
||||
* direct (through feature calls) access to the GPIO
|
||||
* registers.
|
||||
*/
|
|
@ -1 +1,3 @@
|
|||
snd-aoa-fabric-layout-objs += layout.o
|
||||
|
||||
obj-$(CONFIG_SND_AOA_FABRIC_LAYOUT) += snd-aoa-fabric-layout.o
|
||||
|
|
|
@ -66,7 +66,7 @@ struct layout {
|
|||
unsigned int layout_id;
|
||||
struct codec_connect_info codecs[MAX_CODECS_PER_BUS];
|
||||
int flags;
|
||||
|
||||
|
||||
/* if busname is not assigned, we use 'Master' below,
|
||||
* so that our layout table doesn't need to be filled
|
||||
* too much.
|
|
@ -1,2 +1,2 @@
|
|||
obj-$(CONFIG_SND_AOA_SOUNDBUS_I2S) += snd-aoa-i2sbus.o
|
||||
snd-aoa-i2sbus-objs := i2sbus-core.o i2sbus-pcm.o i2sbus-control.o
|
||||
snd-aoa-i2sbus-objs := core.o pcm.o control.o
|
||||
|
|
|
@ -64,7 +64,7 @@ static void free_dbdma_descriptor_ring(struct i2sbus_dev *i2sdev,
|
|||
struct dbdma_command_mem *r)
|
||||
{
|
||||
if (!r->space) return;
|
||||
|
||||
|
||||
dma_free_coherent(&macio_get_pci_dev(i2sdev->macio)->dev,
|
||||
r->size, r->space, r->bus_addr);
|
||||
}
|
||||
|
@ -247,7 +247,7 @@ static int i2sbus_add_dev(struct macio_dev *macio,
|
|||
* but request_resource doesn't know about parents and
|
||||
* contained resources...
|
||||
*/
|
||||
dev->allocated_resource[i] =
|
||||
dev->allocated_resource[i] =
|
||||
request_mem_region(dev->resources[i].start,
|
||||
dev->resources[i].end -
|
||||
dev->resources[i].start + 1,
|
|
@ -18,7 +18,7 @@
|
|||
#include <asm/pmac_feature.h>
|
||||
#include <asm/dbdma.h>
|
||||
|
||||
#include "i2sbus-interface.h"
|
||||
#include "interface.h"
|
||||
#include "../soundbus.h"
|
||||
|
||||
struct i2sbus_control {
|
||||
|
|
|
@ -95,6 +95,26 @@ config SND_SEQUENCER_OSS
|
|||
this will be compiled as a module. The module will be called
|
||||
snd-seq-oss.
|
||||
|
||||
config SND_HRTIMER
|
||||
tristate "HR-timer backend support"
|
||||
depends on HIGH_RES_TIMERS
|
||||
select SND_TIMER
|
||||
help
|
||||
Say Y here to enable HR-timer backend for ALSA timer. ALSA uses
|
||||
the hrtimer as a precise timing source. The ALSA sequencer code
|
||||
also can use this timing source.
|
||||
|
||||
To compile this driver as a module, choose M here: the module
|
||||
will be called snd-hrtimer.
|
||||
|
||||
config SND_SEQ_HRTIMER_DEFAULT
|
||||
bool "Use HR-timer as default sequencer timer"
|
||||
depends on SND_HRTIMER && SND_SEQUENCER
|
||||
default y
|
||||
help
|
||||
Say Y here to use the HR-timer backend as the default sequencer
|
||||
timer.
|
||||
|
||||
config SND_RTCTIMER
|
||||
tristate "RTC Timer support"
|
||||
depends on RTC
|
||||
|
@ -114,6 +134,7 @@ config SND_RTCTIMER
|
|||
config SND_SEQ_RTCTIMER_DEFAULT
|
||||
bool "Use RTC as default sequencer timer"
|
||||
depends on SND_RTCTIMER && SND_SEQUENCER
|
||||
depends on !SND_SEQ_HRTIMER_DEFAULT
|
||||
default y
|
||||
help
|
||||
Say Y here to use the RTC timer as the default sequencer
|
||||
|
|
|
@ -17,12 +17,14 @@ snd-page-alloc-$(CONFIG_HAS_DMA) += sgbuf.o
|
|||
|
||||
snd-rawmidi-objs := rawmidi.o
|
||||
snd-timer-objs := timer.o
|
||||
snd-hrtimer-objs := hrtimer.o
|
||||
snd-rtctimer-objs := rtctimer.o
|
||||
snd-hwdep-objs := hwdep.o
|
||||
|
||||
obj-$(CONFIG_SND) += snd.o
|
||||
obj-$(CONFIG_SND_HWDEP) += snd-hwdep.o
|
||||
obj-$(CONFIG_SND_TIMER) += snd-timer.o
|
||||
obj-$(CONFIG_SND_HRTIMER) += snd-hrtimer.o
|
||||
obj-$(CONFIG_SND_RTCTIMER) += snd-rtctimer.o
|
||||
obj-$(CONFIG_SND_PCM) += snd-pcm.o snd-page-alloc.o
|
||||
obj-$(CONFIG_SND_RAWMIDI) += snd-rawmidi.o
|
||||
|
|
|
@ -98,7 +98,7 @@ int snd_device_free(struct snd_card *card, void *device_data)
|
|||
kfree(dev);
|
||||
return 0;
|
||||
}
|
||||
snd_printd("device free %p (from %p), not found\n", device_data,
|
||||
snd_printd("device free %p (from %pF), not found\n", device_data,
|
||||
__builtin_return_address(0));
|
||||
return -ENXIO;
|
||||
}
|
||||
|
@ -135,7 +135,7 @@ int snd_device_disconnect(struct snd_card *card, void *device_data)
|
|||
}
|
||||
return 0;
|
||||
}
|
||||
snd_printd("device disconnect %p (from %p), not found\n", device_data,
|
||||
snd_printd("device disconnect %p (from %pF), not found\n", device_data,
|
||||
__builtin_return_address(0));
|
||||
return -ENXIO;
|
||||
}
|
||||
|
|
|
@ -0,0 +1,155 @@
|
|||
/*
|
||||
* ALSA timer back-end using hrtimer
|
||||
* Copyright (C) 2008 Takashi Iwai
|
||||
*
|
||||
* 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 <linux/moduleparam.h>
|
||||
#include <linux/hrtimer.h>
|
||||
#include <sound/core.h>
|
||||
#include <sound/timer.h>
|
||||
|
||||
MODULE_AUTHOR("Takashi Iwai <tiwai@suse.de>");
|
||||
MODULE_DESCRIPTION("ALSA hrtimer backend");
|
||||
MODULE_LICENSE("GPL");
|
||||
|
||||
MODULE_ALIAS("snd-timer-" __stringify(SNDRV_TIMER_GLOBAL_HRTIMER));
|
||||
|
||||
#define NANO_SEC 1000000000UL /* 10^9 in sec */
|
||||
static unsigned int resolution;
|
||||
|
||||
struct snd_hrtimer {
|
||||
struct snd_timer *timer;
|
||||
struct hrtimer hrt;
|
||||
};
|
||||
|
||||
static enum hrtimer_restart snd_hrtimer_callback(struct hrtimer *hrt)
|
||||
{
|
||||
struct snd_hrtimer *stime = container_of(hrt, struct snd_hrtimer, hrt);
|
||||
struct snd_timer *t = stime->timer;
|
||||
hrtimer_forward_now(hrt, ns_to_ktime(t->sticks * resolution));
|
||||
snd_timer_interrupt(stime->timer, t->sticks);
|
||||
return HRTIMER_RESTART;
|
||||
}
|
||||
|
||||
static int snd_hrtimer_open(struct snd_timer *t)
|
||||
{
|
||||
struct snd_hrtimer *stime;
|
||||
|
||||
stime = kmalloc(sizeof(*stime), GFP_KERNEL);
|
||||
if (!stime)
|
||||
return -ENOMEM;
|
||||
hrtimer_init(&stime->hrt, CLOCK_MONOTONIC, HRTIMER_MODE_REL);
|
||||
stime->timer = t;
|
||||
stime->hrt.cb_mode = HRTIMER_CB_IRQSAFE_UNLOCKED;
|
||||
stime->hrt.function = snd_hrtimer_callback;
|
||||
t->private_data = stime;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int snd_hrtimer_close(struct snd_timer *t)
|
||||
{
|
||||
struct snd_hrtimer *stime = t->private_data;
|
||||
|
||||
if (stime) {
|
||||
hrtimer_cancel(&stime->hrt);
|
||||
kfree(stime);
|
||||
t->private_data = NULL;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int snd_hrtimer_start(struct snd_timer *t)
|
||||
{
|
||||
struct snd_hrtimer *stime = t->private_data;
|
||||
|
||||
hrtimer_start(&stime->hrt, ns_to_ktime(t->sticks * resolution),
|
||||
HRTIMER_MODE_REL);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int snd_hrtimer_stop(struct snd_timer *t)
|
||||
{
|
||||
struct snd_hrtimer *stime = t->private_data;
|
||||
|
||||
hrtimer_cancel(&stime->hrt);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct snd_timer_hardware hrtimer_hw = {
|
||||
.flags = SNDRV_TIMER_HW_AUTO,
|
||||
.open = snd_hrtimer_open,
|
||||
.close = snd_hrtimer_close,
|
||||
.start = snd_hrtimer_start,
|
||||
.stop = snd_hrtimer_stop,
|
||||
};
|
||||
|
||||
/*
|
||||
* entry functions
|
||||
*/
|
||||
|
||||
static struct snd_timer *mytimer;
|
||||
|
||||
static int __init snd_hrtimer_init(void)
|
||||
{
|
||||
struct snd_timer *timer;
|
||||
struct timespec tp;
|
||||
int err;
|
||||
|
||||
hrtimer_get_res(CLOCK_MONOTONIC, &tp);
|
||||
if (tp.tv_sec > 0 || !tp.tv_nsec) {
|
||||
snd_printk(KERN_ERR
|
||||
"snd-hrtimer: Invalid resolution %u.%09u",
|
||||
(unsigned)tp.tv_sec, (unsigned)tp.tv_nsec);
|
||||
return -EINVAL;
|
||||
}
|
||||
resolution = tp.tv_nsec;
|
||||
|
||||
/* Create a new timer and set up the fields */
|
||||
err = snd_timer_global_new("hrtimer", SNDRV_TIMER_GLOBAL_HRTIMER,
|
||||
&timer);
|
||||
if (err < 0)
|
||||
return err;
|
||||
|
||||
timer->module = THIS_MODULE;
|
||||
strcpy(timer->name, "HR timer");
|
||||
timer->hw = hrtimer_hw;
|
||||
timer->hw.resolution = resolution;
|
||||
timer->hw.ticks = NANO_SEC / resolution;
|
||||
|
||||
err = snd_timer_global_register(timer);
|
||||
if (err < 0) {
|
||||
snd_timer_global_free(timer);
|
||||
return err;
|
||||
}
|
||||
mytimer = timer; /* remember this */
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void __exit snd_hrtimer_exit(void)
|
||||
{
|
||||
if (mytimer) {
|
||||
snd_timer_global_free(mytimer);
|
||||
mytimer = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
module_init(snd_hrtimer_init);
|
||||
module_exit(snd_hrtimer_exit);
|
|
@ -652,6 +652,23 @@ int snd_info_card_register(struct snd_card *card)
|
|||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* called on card->id change
|
||||
*/
|
||||
void snd_info_card_id_change(struct snd_card *card)
|
||||
{
|
||||
mutex_lock(&info_mutex);
|
||||
if (card->proc_root_link) {
|
||||
snd_remove_proc_entry(snd_proc_root, card->proc_root_link);
|
||||
card->proc_root_link = NULL;
|
||||
}
|
||||
if (strcmp(card->id, card->proc_root->name))
|
||||
card->proc_root_link = proc_symlink(card->id,
|
||||
snd_proc_root,
|
||||
card->proc_root->name);
|
||||
mutex_unlock(&info_mutex);
|
||||
}
|
||||
|
||||
/*
|
||||
* de-register the card proc file
|
||||
* called from init.c
|
||||
|
|
|
@ -533,6 +533,65 @@ static void choose_default_id(struct snd_card *card)
|
|||
}
|
||||
}
|
||||
|
||||
#ifndef CONFIG_SYSFS_DEPRECATED
|
||||
static ssize_t
|
||||
card_id_show_attr(struct device *dev,
|
||||
struct device_attribute *attr, char *buf)
|
||||
{
|
||||
struct snd_card *card = dev_get_drvdata(dev);
|
||||
return snprintf(buf, PAGE_SIZE, "%s\n", card ? card->id : "(null)");
|
||||
}
|
||||
|
||||
static ssize_t
|
||||
card_id_store_attr(struct device *dev, struct device_attribute *attr,
|
||||
const char *buf, size_t count)
|
||||
{
|
||||
struct snd_card *card = dev_get_drvdata(dev);
|
||||
char buf1[sizeof(card->id)];
|
||||
size_t copy = count > sizeof(card->id) - 1 ?
|
||||
sizeof(card->id) - 1 : count;
|
||||
size_t idx;
|
||||
int c;
|
||||
|
||||
for (idx = 0; idx < copy; idx++) {
|
||||
c = buf[idx];
|
||||
if (!isalnum(c) && c != '_' && c != '-')
|
||||
return -EINVAL;
|
||||
}
|
||||
memcpy(buf1, buf, copy);
|
||||
buf1[copy] = '\0';
|
||||
mutex_lock(&snd_card_mutex);
|
||||
if (!snd_info_check_reserved_words(buf1)) {
|
||||
__exist:
|
||||
mutex_unlock(&snd_card_mutex);
|
||||
return -EEXIST;
|
||||
}
|
||||
for (idx = 0; idx < snd_ecards_limit; idx++) {
|
||||
if (snd_cards[idx] && !strcmp(snd_cards[idx]->id, buf1))
|
||||
goto __exist;
|
||||
}
|
||||
strcpy(card->id, buf1);
|
||||
snd_info_card_id_change(card);
|
||||
mutex_unlock(&snd_card_mutex);
|
||||
|
||||
return count;
|
||||
}
|
||||
|
||||
static struct device_attribute card_id_attrs =
|
||||
__ATTR(id, S_IRUGO | S_IWUSR, card_id_show_attr, card_id_store_attr);
|
||||
|
||||
static ssize_t
|
||||
card_number_show_attr(struct device *dev,
|
||||
struct device_attribute *attr, char *buf)
|
||||
{
|
||||
struct snd_card *card = dev_get_drvdata(dev);
|
||||
return snprintf(buf, PAGE_SIZE, "%i\n", card ? card->number : -1);
|
||||
}
|
||||
|
||||
static struct device_attribute card_number_attrs =
|
||||
__ATTR(number, S_IRUGO, card_number_show_attr, NULL);
|
||||
#endif /* CONFIG_SYSFS_DEPRECATED */
|
||||
|
||||
/**
|
||||
* snd_card_register - register the soundcard
|
||||
* @card: soundcard structure
|
||||
|
@ -553,7 +612,7 @@ int snd_card_register(struct snd_card *card)
|
|||
#ifndef CONFIG_SYSFS_DEPRECATED
|
||||
if (!card->card_dev) {
|
||||
card->card_dev = device_create(sound_class, card->dev,
|
||||
MKDEV(0, 0), NULL,
|
||||
MKDEV(0, 0), card,
|
||||
"card%i", card->number);
|
||||
if (IS_ERR(card->card_dev))
|
||||
card->card_dev = NULL;
|
||||
|
@ -575,6 +634,16 @@ int snd_card_register(struct snd_card *card)
|
|||
#if defined(CONFIG_SND_MIXER_OSS) || defined(CONFIG_SND_MIXER_OSS_MODULE)
|
||||
if (snd_mixer_oss_notify_callback)
|
||||
snd_mixer_oss_notify_callback(card, SND_MIXER_OSS_NOTIFY_REGISTER);
|
||||
#endif
|
||||
#ifndef CONFIG_SYSFS_DEPRECATED
|
||||
if (card->card_dev) {
|
||||
err = device_create_file(card->card_dev, &card_id_attrs);
|
||||
if (err < 0)
|
||||
return err;
|
||||
err = device_create_file(card->card_dev, &card_number_attrs);
|
||||
if (err < 0)
|
||||
return err;
|
||||
}
|
||||
#endif
|
||||
return 0;
|
||||
}
|
||||
|
|
|
@ -34,6 +34,7 @@ static int snd_jack_dev_free(struct snd_device *device)
|
|||
else
|
||||
input_free_device(jack->input_dev);
|
||||
|
||||
kfree(jack->id);
|
||||
kfree(jack);
|
||||
|
||||
return 0;
|
||||
|
@ -87,7 +88,7 @@ int snd_jack_new(struct snd_card *card, const char *id, int type,
|
|||
if (jack == NULL)
|
||||
return -ENOMEM;
|
||||
|
||||
jack->id = id;
|
||||
jack->id = kstrdup(id, GFP_KERNEL);
|
||||
|
||||
jack->input_dev = input_allocate_device();
|
||||
if (jack->input_dev == NULL) {
|
||||
|
@ -102,9 +103,15 @@ int snd_jack_new(struct snd_card *card, const char *id, int type,
|
|||
if (type & SND_JACK_HEADPHONE)
|
||||
input_set_capability(jack->input_dev, EV_SW,
|
||||
SW_HEADPHONE_INSERT);
|
||||
if (type & SND_JACK_LINEOUT)
|
||||
input_set_capability(jack->input_dev, EV_SW,
|
||||
SW_LINEOUT_INSERT);
|
||||
if (type & SND_JACK_MICROPHONE)
|
||||
input_set_capability(jack->input_dev, EV_SW,
|
||||
SW_MICROPHONE_INSERT);
|
||||
if (type & SND_JACK_MECHANICAL)
|
||||
input_set_capability(jack->input_dev, EV_SW,
|
||||
SW_JACK_PHYSICAL_INSERT);
|
||||
|
||||
err = snd_device_new(card, SNDRV_DEV_JACK, jack, &ops);
|
||||
if (err < 0)
|
||||
|
@ -153,9 +160,15 @@ void snd_jack_report(struct snd_jack *jack, int status)
|
|||
if (jack->type & SND_JACK_HEADPHONE)
|
||||
input_report_switch(jack->input_dev, SW_HEADPHONE_INSERT,
|
||||
status & SND_JACK_HEADPHONE);
|
||||
if (jack->type & SND_JACK_LINEOUT)
|
||||
input_report_switch(jack->input_dev, SW_LINEOUT_INSERT,
|
||||
status & SND_JACK_LINEOUT);
|
||||
if (jack->type & SND_JACK_MICROPHONE)
|
||||
input_report_switch(jack->input_dev, SW_MICROPHONE_INSERT,
|
||||
status & SND_JACK_MICROPHONE);
|
||||
if (jack->type & SND_JACK_MECHANICAL)
|
||||
input_report_switch(jack->input_dev, SW_JACK_PHYSICAL_INSERT,
|
||||
status & SND_JACK_MECHANICAL);
|
||||
|
||||
input_sync(jack->input_dev);
|
||||
}
|
||||
|
|
|
@ -151,7 +151,7 @@ static inline void snd_rawmidi_output_trigger(struct snd_rawmidi_substream *subs
|
|||
if (!substream->opened)
|
||||
return;
|
||||
if (up) {
|
||||
tasklet_hi_schedule(&substream->runtime->tasklet);
|
||||
tasklet_schedule(&substream->runtime->tasklet);
|
||||
} else {
|
||||
tasklet_kill(&substream->runtime->tasklet);
|
||||
substream->ops->trigger(substream, 0);
|
||||
|
@ -908,7 +908,7 @@ int snd_rawmidi_receive(struct snd_rawmidi_substream *substream,
|
|||
}
|
||||
if (result > 0) {
|
||||
if (runtime->event)
|
||||
tasklet_hi_schedule(&runtime->tasklet);
|
||||
tasklet_schedule(&runtime->tasklet);
|
||||
else if (snd_rawmidi_ready(substream))
|
||||
wake_up(&runtime->sleep);
|
||||
}
|
||||
|
|
|
@ -118,7 +118,7 @@ static void rtctimer_tasklet(unsigned long data)
|
|||
*/
|
||||
static void rtctimer_interrupt(void *private_data)
|
||||
{
|
||||
tasklet_hi_schedule(private_data);
|
||||
tasklet_schedule(private_data);
|
||||
}
|
||||
|
||||
|
||||
|
|
|
@ -43,7 +43,9 @@ int seq_default_timer_class = SNDRV_TIMER_CLASS_GLOBAL;
|
|||
int seq_default_timer_sclass = SNDRV_TIMER_SCLASS_NONE;
|
||||
int seq_default_timer_card = -1;
|
||||
int seq_default_timer_device =
|
||||
#ifdef CONFIG_SND_SEQ_RTCTIMER_DEFAULT
|
||||
#ifdef CONFIG_SND_SEQ_HRTIMER_DEFAULT
|
||||
SNDRV_TIMER_GLOBAL_HRTIMER
|
||||
#elif defined(CONFIG_SND_SEQ_RTCTIMER_DEFAULT)
|
||||
SNDRV_TIMER_GLOBAL_RTC
|
||||
#else
|
||||
SNDRV_TIMER_GLOBAL_SYSTEM
|
||||
|
|
|
@ -743,7 +743,7 @@ void snd_timer_interrupt(struct snd_timer * timer, unsigned long ticks_left)
|
|||
spin_unlock_irqrestore(&timer->lock, flags);
|
||||
|
||||
if (use_tasklet)
|
||||
tasklet_hi_schedule(&timer->task_queue);
|
||||
tasklet_schedule(&timer->task_queue);
|
||||
}
|
||||
|
||||
/*
|
||||
|
|
|
@ -163,7 +163,7 @@ config SND_ML403_AC97CR
|
|||
|
||||
config SND_AC97_POWER_SAVE
|
||||
bool "AC97 Power-Saving Mode"
|
||||
depends on SND_AC97_CODEC && EXPERIMENTAL
|
||||
depends on SND_AC97_CODEC
|
||||
default n
|
||||
help
|
||||
Say Y here to enable the aggressive power-saving support of
|
||||
|
|
|
@ -96,7 +96,7 @@ static int __devinit snd_card_pcsp_probe(int devnum, struct device *dev)
|
|||
return -EINVAL;
|
||||
|
||||
hrtimer_init(&pcsp_chip.timer, CLOCK_MONOTONIC, HRTIMER_MODE_REL);
|
||||
pcsp_chip.timer.cb_mode = HRTIMER_CB_SOFTIRQ;
|
||||
pcsp_chip.timer.cb_mode = HRTIMER_CB_IRQSAFE_UNLOCKED;
|
||||
pcsp_chip.timer.function = pcsp_do_timer;
|
||||
|
||||
card = snd_card_new(index, id, THIS_MODULE, 0);
|
||||
|
@ -188,10 +188,8 @@ static int __devexit pcsp_remove(struct platform_device *dev)
|
|||
|
||||
static void pcsp_stop_beep(struct snd_pcsp *chip)
|
||||
{
|
||||
spin_lock_irq(&chip->substream_lock);
|
||||
if (!chip->playback_substream)
|
||||
pcspkr_stop_sound();
|
||||
spin_unlock_irq(&chip->substream_lock);
|
||||
pcsp_sync_stop(chip);
|
||||
pcspkr_stop_sound();
|
||||
}
|
||||
|
||||
#ifdef CONFIG_PM
|
||||
|
|
|
@ -62,6 +62,8 @@ struct snd_pcsp {
|
|||
unsigned short port, irq, dma;
|
||||
spinlock_t substream_lock;
|
||||
struct snd_pcm_substream *playback_substream;
|
||||
unsigned int fmt_size;
|
||||
unsigned int is_signed;
|
||||
size_t playback_ptr;
|
||||
size_t period_ptr;
|
||||
atomic_t timer_active;
|
||||
|
@ -77,6 +79,7 @@ struct snd_pcsp {
|
|||
extern struct snd_pcsp pcsp_chip;
|
||||
|
||||
extern enum hrtimer_restart pcsp_do_timer(struct hrtimer *handle);
|
||||
extern void pcsp_sync_stop(struct snd_pcsp *chip);
|
||||
|
||||
extern int snd_pcsp_new_pcm(struct snd_pcsp *chip);
|
||||
extern int snd_pcsp_new_mixer(struct snd_pcsp *chip);
|
||||
|
|
|
@ -8,6 +8,7 @@
|
|||
|
||||
#include <linux/module.h>
|
||||
#include <linux/moduleparam.h>
|
||||
#include <linux/interrupt.h>
|
||||
#include <sound/pcm.h>
|
||||
#include <asm/io.h>
|
||||
#include "pcsp.h"
|
||||
|
@ -19,61 +20,57 @@ MODULE_PARM_DESC(nforce_wa, "Apply NForce chipset workaround "
|
|||
|
||||
#define DMIX_WANTS_S16 1
|
||||
|
||||
enum hrtimer_restart pcsp_do_timer(struct hrtimer *handle)
|
||||
/*
|
||||
* Call snd_pcm_period_elapsed in a tasklet
|
||||
* This avoids spinlock messes and long-running irq contexts
|
||||
*/
|
||||
static void pcsp_call_pcm_elapsed(unsigned long priv)
|
||||
{
|
||||
if (atomic_read(&pcsp_chip.timer_active)) {
|
||||
struct snd_pcm_substream *substream;
|
||||
substream = pcsp_chip.playback_substream;
|
||||
if (substream)
|
||||
snd_pcm_period_elapsed(substream);
|
||||
}
|
||||
}
|
||||
|
||||
static DECLARE_TASKLET(pcsp_pcm_tasklet, pcsp_call_pcm_elapsed, 0);
|
||||
|
||||
/* write the port and returns the next expire time in ns;
|
||||
* called at the trigger-start and in hrtimer callback
|
||||
*/
|
||||
static unsigned long pcsp_timer_update(struct hrtimer *handle)
|
||||
{
|
||||
unsigned char timer_cnt, val;
|
||||
int fmt_size, periods_elapsed;
|
||||
u64 ns;
|
||||
size_t period_bytes, buffer_bytes;
|
||||
struct snd_pcm_substream *substream;
|
||||
struct snd_pcm_runtime *runtime;
|
||||
struct snd_pcsp *chip = container_of(handle, struct snd_pcsp, timer);
|
||||
unsigned long flags;
|
||||
|
||||
if (chip->thalf) {
|
||||
outb(chip->val61, 0x61);
|
||||
chip->thalf = 0;
|
||||
if (!atomic_read(&chip->timer_active))
|
||||
return HRTIMER_NORESTART;
|
||||
hrtimer_forward(&chip->timer, hrtimer_get_expires(&chip->timer),
|
||||
ktime_set(0, chip->ns_rem));
|
||||
return HRTIMER_RESTART;
|
||||
return 0;
|
||||
return chip->ns_rem;
|
||||
}
|
||||
|
||||
spin_lock_irq(&chip->substream_lock);
|
||||
/* Takashi Iwai says regarding this extra lock:
|
||||
|
||||
If the irq handler handles some data on the DMA buffer, it should
|
||||
do snd_pcm_stream_lock().
|
||||
That protects basically against all races among PCM callbacks, yes.
|
||||
However, there are two remaining issues:
|
||||
1. The substream pointer you try to lock isn't protected _before_
|
||||
this lock yet.
|
||||
2. snd_pcm_period_elapsed() itself acquires the lock.
|
||||
The requirement of another lock is because of 1. When you get
|
||||
chip->playback_substream, it's not protected.
|
||||
Keeping this lock while snd_pcm_period_elapsed() assures the substream
|
||||
is still protected (at least, not released). And the other status is
|
||||
handled properly inside snd_pcm_stream_lock() in
|
||||
snd_pcm_period_elapsed().
|
||||
|
||||
*/
|
||||
if (!chip->playback_substream)
|
||||
goto exit_nr_unlock1;
|
||||
substream = chip->playback_substream;
|
||||
snd_pcm_stream_lock(substream);
|
||||
if (!atomic_read(&chip->timer_active))
|
||||
goto exit_nr_unlock2;
|
||||
return 0;
|
||||
substream = chip->playback_substream;
|
||||
if (!substream)
|
||||
return 0;
|
||||
|
||||
runtime = substream->runtime;
|
||||
fmt_size = snd_pcm_format_physical_width(runtime->format) >> 3;
|
||||
/* assume it is mono! */
|
||||
val = runtime->dma_area[chip->playback_ptr + fmt_size - 1];
|
||||
if (snd_pcm_format_signed(runtime->format))
|
||||
val = runtime->dma_area[chip->playback_ptr + chip->fmt_size - 1];
|
||||
if (chip->is_signed)
|
||||
val ^= 0x80;
|
||||
timer_cnt = val * CUR_DIV() / 256;
|
||||
|
||||
if (timer_cnt && chip->enable) {
|
||||
spin_lock(&i8253_lock);
|
||||
spin_lock_irqsave(&i8253_lock, flags);
|
||||
if (!nforce_wa) {
|
||||
outb_p(chip->val61, 0x61);
|
||||
outb_p(timer_cnt, 0x42);
|
||||
|
@ -82,12 +79,39 @@ enum hrtimer_restart pcsp_do_timer(struct hrtimer *handle)
|
|||
outb(chip->val61 ^ 2, 0x61);
|
||||
chip->thalf = 1;
|
||||
}
|
||||
spin_unlock(&i8253_lock);
|
||||
spin_unlock_irqrestore(&i8253_lock, flags);
|
||||
}
|
||||
|
||||
chip->ns_rem = PCSP_PERIOD_NS();
|
||||
ns = (chip->thalf ? PCSP_CALC_NS(timer_cnt) : chip->ns_rem);
|
||||
chip->ns_rem -= ns;
|
||||
return ns;
|
||||
}
|
||||
|
||||
enum hrtimer_restart pcsp_do_timer(struct hrtimer *handle)
|
||||
{
|
||||
struct snd_pcsp *chip = container_of(handle, struct snd_pcsp, timer);
|
||||
struct snd_pcm_substream *substream;
|
||||
int periods_elapsed, pointer_update;
|
||||
size_t period_bytes, buffer_bytes;
|
||||
unsigned long ns;
|
||||
unsigned long flags;
|
||||
|
||||
pointer_update = !chip->thalf;
|
||||
ns = pcsp_timer_update(handle);
|
||||
if (!ns)
|
||||
return HRTIMER_NORESTART;
|
||||
|
||||
/* update the playback position */
|
||||
substream = chip->playback_substream;
|
||||
if (!substream)
|
||||
return HRTIMER_NORESTART;
|
||||
|
||||
period_bytes = snd_pcm_lib_period_bytes(substream);
|
||||
buffer_bytes = snd_pcm_lib_buffer_bytes(substream);
|
||||
chip->playback_ptr += PCSP_INDEX_INC() * fmt_size;
|
||||
|
||||
spin_lock_irqsave(&chip->substream_lock, flags);
|
||||
chip->playback_ptr += PCSP_INDEX_INC() * chip->fmt_size;
|
||||
periods_elapsed = chip->playback_ptr - chip->period_ptr;
|
||||
if (periods_elapsed < 0) {
|
||||
#if PCSP_DEBUG
|
||||
|
@ -102,41 +126,30 @@ enum hrtimer_restart pcsp_do_timer(struct hrtimer *handle)
|
|||
* or ALSA will BUG on us. */
|
||||
chip->playback_ptr %= buffer_bytes;
|
||||
|
||||
snd_pcm_stream_unlock(substream);
|
||||
|
||||
if (periods_elapsed) {
|
||||
snd_pcm_period_elapsed(substream);
|
||||
chip->period_ptr += periods_elapsed * period_bytes;
|
||||
chip->period_ptr %= buffer_bytes;
|
||||
}
|
||||
spin_unlock_irqrestore(&chip->substream_lock, flags);
|
||||
|
||||
spin_unlock_irq(&chip->substream_lock);
|
||||
if (periods_elapsed)
|
||||
tasklet_schedule(&pcsp_pcm_tasklet);
|
||||
|
||||
if (!atomic_read(&chip->timer_active))
|
||||
return HRTIMER_NORESTART;
|
||||
hrtimer_forward(handle, hrtimer_get_expires(handle), ns_to_ktime(ns));
|
||||
|
||||
chip->ns_rem = PCSP_PERIOD_NS();
|
||||
ns = (chip->thalf ? PCSP_CALC_NS(timer_cnt) : chip->ns_rem);
|
||||
chip->ns_rem -= ns;
|
||||
hrtimer_forward(&chip->timer, hrtimer_get_expires(&chip->timer),
|
||||
ktime_set(0, ns));
|
||||
return HRTIMER_RESTART;
|
||||
|
||||
exit_nr_unlock2:
|
||||
snd_pcm_stream_unlock(substream);
|
||||
exit_nr_unlock1:
|
||||
spin_unlock_irq(&chip->substream_lock);
|
||||
return HRTIMER_NORESTART;
|
||||
}
|
||||
|
||||
static void pcsp_start_playing(struct snd_pcsp *chip)
|
||||
static int pcsp_start_playing(struct snd_pcsp *chip)
|
||||
{
|
||||
unsigned long ns;
|
||||
|
||||
#if PCSP_DEBUG
|
||||
printk(KERN_INFO "PCSP: start_playing called\n");
|
||||
#endif
|
||||
if (atomic_read(&chip->timer_active)) {
|
||||
printk(KERN_ERR "PCSP: Timer already active\n");
|
||||
return;
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
spin_lock(&i8253_lock);
|
||||
|
@ -146,7 +159,12 @@ static void pcsp_start_playing(struct snd_pcsp *chip)
|
|||
atomic_set(&chip->timer_active, 1);
|
||||
chip->thalf = 0;
|
||||
|
||||
hrtimer_start(&pcsp_chip.timer, ktime_set(0, 0), HRTIMER_MODE_REL);
|
||||
ns = pcsp_timer_update(&pcsp_chip.timer);
|
||||
if (!ns)
|
||||
return -EIO;
|
||||
|
||||
hrtimer_start(&pcsp_chip.timer, ktime_set(0, ns), HRTIMER_MODE_REL);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void pcsp_stop_playing(struct snd_pcsp *chip)
|
||||
|
@ -165,26 +183,35 @@ static void pcsp_stop_playing(struct snd_pcsp *chip)
|
|||
spin_unlock(&i8253_lock);
|
||||
}
|
||||
|
||||
/*
|
||||
* Force to stop and sync the stream
|
||||
*/
|
||||
void pcsp_sync_stop(struct snd_pcsp *chip)
|
||||
{
|
||||
local_irq_disable();
|
||||
pcsp_stop_playing(chip);
|
||||
local_irq_enable();
|
||||
hrtimer_cancel(&chip->timer);
|
||||
tasklet_kill(&pcsp_pcm_tasklet);
|
||||
}
|
||||
|
||||
static int snd_pcsp_playback_close(struct snd_pcm_substream *substream)
|
||||
{
|
||||
struct snd_pcsp *chip = snd_pcm_substream_chip(substream);
|
||||
#if PCSP_DEBUG
|
||||
printk(KERN_INFO "PCSP: close called\n");
|
||||
#endif
|
||||
if (atomic_read(&chip->timer_active)) {
|
||||
printk(KERN_ERR "PCSP: timer still active\n");
|
||||
pcsp_stop_playing(chip);
|
||||
}
|
||||
spin_lock_irq(&chip->substream_lock);
|
||||
pcsp_sync_stop(chip);
|
||||
chip->playback_substream = NULL;
|
||||
spin_unlock_irq(&chip->substream_lock);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int snd_pcsp_playback_hw_params(struct snd_pcm_substream *substream,
|
||||
struct snd_pcm_hw_params *hw_params)
|
||||
{
|
||||
struct snd_pcsp *chip = snd_pcm_substream_chip(substream);
|
||||
int err;
|
||||
pcsp_sync_stop(chip);
|
||||
err = snd_pcm_lib_malloc_pages(substream,
|
||||
params_buffer_bytes(hw_params));
|
||||
if (err < 0)
|
||||
|
@ -194,9 +221,11 @@ static int snd_pcsp_playback_hw_params(struct snd_pcm_substream *substream,
|
|||
|
||||
static int snd_pcsp_playback_hw_free(struct snd_pcm_substream *substream)
|
||||
{
|
||||
struct snd_pcsp *chip = snd_pcm_substream_chip(substream);
|
||||
#if PCSP_DEBUG
|
||||
printk(KERN_INFO "PCSP: hw_free called\n");
|
||||
#endif
|
||||
pcsp_sync_stop(chip);
|
||||
return snd_pcm_lib_free_pages(substream);
|
||||
}
|
||||
|
||||
|
@ -212,8 +241,12 @@ static int snd_pcsp_playback_prepare(struct snd_pcm_substream *substream)
|
|||
snd_pcm_lib_period_bytes(substream),
|
||||
substream->runtime->periods);
|
||||
#endif
|
||||
pcsp_sync_stop(chip);
|
||||
chip->playback_ptr = 0;
|
||||
chip->period_ptr = 0;
|
||||
chip->fmt_size =
|
||||
snd_pcm_format_physical_width(substream->runtime->format) >> 3;
|
||||
chip->is_signed = snd_pcm_format_signed(substream->runtime->format);
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
@ -226,8 +259,7 @@ static int snd_pcsp_trigger(struct snd_pcm_substream *substream, int cmd)
|
|||
switch (cmd) {
|
||||
case SNDRV_PCM_TRIGGER_START:
|
||||
case SNDRV_PCM_TRIGGER_RESUME:
|
||||
pcsp_start_playing(chip);
|
||||
break;
|
||||
return pcsp_start_playing(chip);
|
||||
case SNDRV_PCM_TRIGGER_STOP:
|
||||
case SNDRV_PCM_TRIGGER_SUSPEND:
|
||||
pcsp_stop_playing(chip);
|
||||
|
@ -242,7 +274,11 @@ static snd_pcm_uframes_t snd_pcsp_playback_pointer(struct snd_pcm_substream
|
|||
*substream)
|
||||
{
|
||||
struct snd_pcsp *chip = snd_pcm_substream_chip(substream);
|
||||
return bytes_to_frames(substream->runtime, chip->playback_ptr);
|
||||
unsigned int pos;
|
||||
spin_lock(&chip->substream_lock);
|
||||
pos = chip->playback_ptr;
|
||||
spin_unlock(&chip->substream_lock);
|
||||
return bytes_to_frames(substream->runtime, pos);
|
||||
}
|
||||
|
||||
static struct snd_pcm_hardware snd_pcsp_playback = {
|
||||
|
@ -279,9 +315,7 @@ static int snd_pcsp_playback_open(struct snd_pcm_substream *substream)
|
|||
return -EBUSY;
|
||||
}
|
||||
runtime->hw = snd_pcsp_playback;
|
||||
spin_lock_irq(&chip->substream_lock);
|
||||
chip->playback_substream = substream;
|
||||
spin_unlock_irq(&chip->substream_lock);
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
|
|
@ -548,7 +548,7 @@ irqreturn_t snd_vx_irq_handler(int irq, void *dev)
|
|||
(chip->chip_status & VX_STAT_IS_STALE))
|
||||
return IRQ_NONE;
|
||||
if (! vx_test_and_ack(chip))
|
||||
tasklet_hi_schedule(&chip->tq);
|
||||
tasklet_schedule(&chip->tq);
|
||||
return IRQ_HANDLED;
|
||||
}
|
||||
|
||||
|
|
|
@ -823,7 +823,7 @@ static int vx_pcm_trigger(struct snd_pcm_substream *subs, int cmd)
|
|||
* we trigger the pipe using tasklet, so that the interrupts are
|
||||
* issued surely after the trigger is completed.
|
||||
*/
|
||||
tasklet_hi_schedule(&pipe->start_tq);
|
||||
tasklet_schedule(&pipe->start_tq);
|
||||
chip->pcm_running++;
|
||||
pipe->running = 1;
|
||||
break;
|
||||
|
|
|
@ -140,8 +140,10 @@ static int __devinit snd_sb8_probe(struct device *pdev, unsigned int dev)
|
|||
break;
|
||||
}
|
||||
}
|
||||
if (i >= ARRAY_SIZE(possible_ports))
|
||||
if (i >= ARRAY_SIZE(possible_ports)) {
|
||||
err = -EINVAL;
|
||||
goto _err;
|
||||
}
|
||||
}
|
||||
acard->chip = chip;
|
||||
|
||||
|
|
|
@ -208,7 +208,8 @@ config SND_OXYGEN
|
|||
* AuzenTech X-Meridian
|
||||
* Bgears b-Enspirer
|
||||
* Club3D Theatron DTS
|
||||
* HT-Omega Claro
|
||||
* HT-Omega Claro (plus)
|
||||
* HT-Omega Claro halo (XT)
|
||||
* Razer Barracuda AC-1
|
||||
* Sondigo Inferno
|
||||
|
||||
|
@ -497,129 +498,7 @@ config SND_FM801_TEA575X
|
|||
depends on SND_FM801_TEA575X_BOOL
|
||||
default SND_FM801
|
||||
|
||||
config SND_HDA_INTEL
|
||||
tristate "Intel HD Audio"
|
||||
select SND_PCM
|
||||
select SND_VMASTER
|
||||
help
|
||||
Say Y here to include support for Intel "High Definition
|
||||
Audio" (Azalia) motherboard devices.
|
||||
|
||||
To compile this driver as a module, choose M here: the module
|
||||
will be called snd-hda-intel.
|
||||
|
||||
config SND_HDA_HWDEP
|
||||
bool "Build hwdep interface for HD-audio driver"
|
||||
depends on SND_HDA_INTEL
|
||||
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_INPUT_BEEP
|
||||
bool "Support digital beep via input layer"
|
||||
depends on SND_HDA_INTEL
|
||||
depends on INPUT=y || INPUT=SND_HDA_INTEL
|
||||
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_CODEC_REALTEK
|
||||
bool "Build Realtek HD-audio codec support"
|
||||
depends on SND_HDA_INTEL
|
||||
default y
|
||||
help
|
||||
Say Y here to include Realtek HD-audio codec support in
|
||||
snd-hda-intel driver, such as ALC880.
|
||||
|
||||
config SND_HDA_CODEC_ANALOG
|
||||
bool "Build Analog Device HD-audio codec support"
|
||||
depends on SND_HDA_INTEL
|
||||
default y
|
||||
help
|
||||
Say Y here to include Analog Device HD-audio codec support in
|
||||
snd-hda-intel driver, such as AD1986A.
|
||||
|
||||
config SND_HDA_CODEC_SIGMATEL
|
||||
bool "Build IDT/Sigmatel HD-audio codec support"
|
||||
depends on SND_HDA_INTEL
|
||||
default y
|
||||
help
|
||||
Say Y here to include IDT (Sigmatel) HD-audio codec support in
|
||||
snd-hda-intel driver, such as STAC9200.
|
||||
|
||||
config SND_HDA_CODEC_VIA
|
||||
bool "Build VIA HD-audio codec support"
|
||||
depends on SND_HDA_INTEL
|
||||
default y
|
||||
help
|
||||
Say Y here to include VIA HD-audio codec support in
|
||||
snd-hda-intel driver, such as VT1708.
|
||||
|
||||
config SND_HDA_CODEC_ATIHDMI
|
||||
bool "Build ATI HDMI HD-audio codec support"
|
||||
depends on SND_HDA_INTEL
|
||||
default y
|
||||
help
|
||||
Say Y here to include ATI HDMI HD-audio codec support in
|
||||
snd-hda-intel driver, such as ATI RS600 HDMI.
|
||||
|
||||
config SND_HDA_CODEC_NVHDMI
|
||||
bool "Build NVIDIA HDMI HD-audio codec support"
|
||||
depends on SND_HDA_INTEL
|
||||
default y
|
||||
help
|
||||
Say Y here to include NVIDIA HDMI HD-audio codec support in
|
||||
snd-hda-intel driver, such as NVIDIA MCP78 HDMI.
|
||||
|
||||
config SND_HDA_CODEC_CONEXANT
|
||||
bool "Build Conexant HD-audio codec support"
|
||||
depends on SND_HDA_INTEL
|
||||
default y
|
||||
help
|
||||
Say Y here to include Conexant HD-audio codec support in
|
||||
snd-hda-intel driver, such as CX20549.
|
||||
|
||||
config SND_HDA_CODEC_CMEDIA
|
||||
bool "Build C-Media HD-audio codec support"
|
||||
depends on SND_HDA_INTEL
|
||||
default y
|
||||
help
|
||||
Say Y here to include C-Media HD-audio codec support in
|
||||
snd-hda-intel driver, such as CMI9880.
|
||||
|
||||
config SND_HDA_CODEC_SI3054
|
||||
bool "Build Silicon Labs 3054 HD-modem codec support"
|
||||
depends on SND_HDA_INTEL
|
||||
default y
|
||||
help
|
||||
Say Y here to include Silicon Labs 3054 HD-modem codec
|
||||
(and compatibles) support in snd-hda-intel driver.
|
||||
|
||||
config SND_HDA_GENERIC
|
||||
bool "Enable generic HD-audio codec parser"
|
||||
depends on SND_HDA_INTEL
|
||||
default y
|
||||
help
|
||||
Say Y here to enable the generic HD-audio codec parser
|
||||
in snd-hda-intel driver.
|
||||
|
||||
config SND_HDA_POWER_SAVE
|
||||
bool "Aggressive power-saving on HD-audio"
|
||||
depends on SND_HDA_INTEL && EXPERIMENTAL
|
||||
help
|
||||
Say Y here to enable more aggressive power-saving mode on
|
||||
HD-audio driver. The power-saving timeout can be configured
|
||||
via power_save option or over sysfs on-the-fly.
|
||||
|
||||
config SND_HDA_POWER_SAVE_DEFAULT
|
||||
int "Default time-out for HD-audio power-save mode"
|
||||
depends on SND_HDA_POWER_SAVE
|
||||
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.
|
||||
source "sound/pci/hda/Kconfig"
|
||||
|
||||
config SND_HDSP
|
||||
tristate "RME Hammerfall DSP Audio"
|
||||
|
|
|
@ -175,7 +175,7 @@ static const struct ac97_codec_id snd_ac97_codec_ids[] = {
|
|||
{ 0x574d4C04, 0xffffffff, "WM9704M,WM9704Q", patch_wolfson04, NULL},
|
||||
{ 0x574d4C05, 0xffffffff, "WM9705,WM9710", patch_wolfson05, NULL},
|
||||
{ 0x574d4C09, 0xffffffff, "WM9709", NULL, NULL},
|
||||
{ 0x574d4C12, 0xffffffff, "WM9711,WM9712", patch_wolfson11, NULL},
|
||||
{ 0x574d4C12, 0xffffffff, "WM9711,WM9712,WM9715", patch_wolfson11, NULL},
|
||||
{ 0x574d4c13, 0xffffffff, "WM9713,WM9714", patch_wolfson13, NULL, AC97_DEFAULT_POWER_OFF},
|
||||
{ 0x594d4800, 0xffffffff, "YMF743", patch_yamaha_ymf743, NULL },
|
||||
{ 0x594d4802, 0xffffffff, "YMF752", NULL, NULL },
|
||||
|
|
|
@ -2054,8 +2054,9 @@ static const struct snd_kcontrol_new snd_ac97_ad1888_controls[] = {
|
|||
.get = snd_ac97_ad1888_lohpsel_get,
|
||||
.put = snd_ac97_ad1888_lohpsel_put
|
||||
},
|
||||
AC97_SINGLE("V_REFOUT Enable", AC97_AD_MISC, 2, 1, 1),
|
||||
AC97_SINGLE("High Pass Filter Enable", AC97_AD_TEST2, 12, 1, 1),
|
||||
AC97_SINGLE("V_REFOUT Enable", AC97_AD_MISC, AC97_AD_VREFD_SHIFT, 1, 1),
|
||||
AC97_SINGLE("High Pass Filter Enable", AC97_AD_TEST2,
|
||||
AC97_AD_HPFD_SHIFT, 1, 1),
|
||||
AC97_SINGLE("Spread Front to Surround and Center/LFE", AC97_AD_MISC, 7, 1, 0),
|
||||
{
|
||||
.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
|
||||
|
@ -2832,6 +2833,8 @@ static int patch_alc655(struct snd_ac97 * ac97)
|
|||
val &= ~(1 << 1); /* Pin 47 is EAPD (for internal speaker) */
|
||||
else
|
||||
val |= (1 << 1); /* Pin 47 is spdif input pin */
|
||||
/* this seems missing on some hardwares */
|
||||
ac97->ext_id |= AC97_EI_SPDIF;
|
||||
}
|
||||
val &= ~(1 << 12); /* vref enable */
|
||||
snd_ac97_write_cache(ac97, 0x7a, val);
|
||||
|
|
|
@ -664,10 +664,14 @@ struct snd_ca0106_pcm {
|
|||
struct snd_ca0106_details {
|
||||
u32 serial;
|
||||
char * name;
|
||||
int ac97;
|
||||
int gpio_type;
|
||||
int i2c_adc;
|
||||
int spi_dac;
|
||||
int ac97; /* ac97 = 0 -> Select MIC, Line in, TAD in, AUX in.
|
||||
ac97 = 1 -> Default to AC97 in. */
|
||||
int gpio_type; /* gpio_type = 1 -> shared mic-in/line-in
|
||||
gpio_type = 2 -> shared side-out/line-in. */
|
||||
int i2c_adc; /* with i2c_adc=1, the driver adds some capture volume
|
||||
controls, phone, mic, line-in and aux. */
|
||||
int spi_dac; /* spi_dac=1 adds the mute switch for each analog
|
||||
output, front, rear, etc. */
|
||||
};
|
||||
|
||||
// definition of the chip-specific record
|
||||
|
@ -686,11 +690,12 @@ struct snd_ca0106 {
|
|||
spinlock_t emu_lock;
|
||||
|
||||
struct snd_ac97 *ac97;
|
||||
struct snd_pcm *pcm;
|
||||
struct snd_pcm *pcm[4];
|
||||
|
||||
struct snd_ca0106_channel playback_channels[4];
|
||||
struct snd_ca0106_channel capture_channels[4];
|
||||
u32 spdif_bits[4]; /* s/pdif out setup */
|
||||
u32 spdif_bits[4]; /* s/pdif out default setup */
|
||||
u32 spdif_str_bits[4]; /* s/pdif out per-stream setup */
|
||||
int spdif_enable;
|
||||
int capture_source;
|
||||
int i2c_capture_source;
|
||||
|
@ -703,6 +708,11 @@ struct snd_ca0106 {
|
|||
struct snd_ca_midi midi2;
|
||||
|
||||
u16 spi_dac_reg[16];
|
||||
|
||||
#ifdef CONFIG_PM
|
||||
#define NUM_SAVED_VOLUMES 9
|
||||
unsigned int saved_vol[NUM_SAVED_VOLUMES];
|
||||
#endif
|
||||
};
|
||||
|
||||
int snd_ca0106_mixer(struct snd_ca0106 *emu);
|
||||
|
@ -721,3 +731,11 @@ int snd_ca0106_i2c_write(struct snd_ca0106 *emu, u32 reg, u32 value);
|
|||
|
||||
int snd_ca0106_spi_write(struct snd_ca0106 * emu,
|
||||
unsigned int data);
|
||||
|
||||
#ifdef CONFIG_PM
|
||||
void snd_ca0106_mixer_suspend(struct snd_ca0106 *chip);
|
||||
void snd_ca0106_mixer_resume(struct snd_ca0106 *chip);
|
||||
#else
|
||||
#define snd_ca0106_mixer_suspend(chip) do { } while (0)
|
||||
#define snd_ca0106_mixer_resume(chip) do { } while (0)
|
||||
#endif
|
||||
|
|
|
@ -254,7 +254,7 @@ static struct snd_ca0106_details ca0106_chip_details[] = {
|
|||
.name = "MSI K8N Diamond MB",
|
||||
.gpio_type = 2,
|
||||
.i2c_adc = 1,
|
||||
.spi_dac = 2 } ,
|
||||
.spi_dac = 1 } ,
|
||||
/* Shuttle XPC SD31P which has an onboard Creative Labs
|
||||
* Sound Blaster Live! 24-bit EAX
|
||||
* high-definition 7.1 audio processor".
|
||||
|
@ -305,9 +305,15 @@ static struct snd_pcm_hardware snd_ca0106_capture_hw = {
|
|||
SNDRV_PCM_INFO_BLOCK_TRANSFER |
|
||||
SNDRV_PCM_INFO_MMAP_VALID),
|
||||
.formats = SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S32_LE,
|
||||
#if 0 /* FIXME: looks like 44.1kHz capture causes noisy output on 48kHz */
|
||||
.rates = (SNDRV_PCM_RATE_44100 | SNDRV_PCM_RATE_48000 |
|
||||
SNDRV_PCM_RATE_96000 | SNDRV_PCM_RATE_192000),
|
||||
.rate_min = 44100,
|
||||
#else
|
||||
.rates = (SNDRV_PCM_RATE_48000 |
|
||||
SNDRV_PCM_RATE_96000 | SNDRV_PCM_RATE_192000),
|
||||
.rate_min = 48000,
|
||||
#endif /* FIXME */
|
||||
.rate_max = 192000,
|
||||
.channels_min = 2,
|
||||
.channels_max = 2,
|
||||
|
@ -479,6 +485,15 @@ static const int spi_dacd_bit[] = {
|
|||
[PCM_UNKNOWN_CHANNEL] = SPI_DACD1_BIT,
|
||||
};
|
||||
|
||||
static void restore_spdif_bits(struct snd_ca0106 *chip, int idx)
|
||||
{
|
||||
if (chip->spdif_str_bits[idx] != chip->spdif_bits[idx]) {
|
||||
chip->spdif_str_bits[idx] = chip->spdif_bits[idx];
|
||||
snd_ca0106_ptr_write(chip, SPCS0 + idx, 0,
|
||||
chip->spdif_str_bits[idx]);
|
||||
}
|
||||
}
|
||||
|
||||
/* open_playback callback */
|
||||
static int snd_ca0106_pcm_open_playback_channel(struct snd_pcm_substream *substream,
|
||||
int channel_id)
|
||||
|
@ -524,6 +539,9 @@ static int snd_ca0106_pcm_open_playback_channel(struct snd_pcm_substream *substr
|
|||
if (err < 0)
|
||||
return err;
|
||||
}
|
||||
|
||||
restore_spdif_bits(chip, channel_id);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
@ -535,6 +553,8 @@ static int snd_ca0106_pcm_close_playback(struct snd_pcm_substream *substream)
|
|||
struct snd_ca0106_pcm *epcm = runtime->private_data;
|
||||
chip->playback_channels[epcm->channel_id].use = 0;
|
||||
|
||||
restore_spdif_bits(chip, epcm->channel_id);
|
||||
|
||||
if (chip->details->spi_dac && epcm->channel_id != PCM_FRONT_CHANNEL) {
|
||||
const int reg = spi_dacd_reg[epcm->channel_id];
|
||||
|
||||
|
@ -847,15 +867,18 @@ static int snd_ca0106_pcm_trigger_playback(struct snd_pcm_substream *substream,
|
|||
struct snd_pcm_substream *s;
|
||||
u32 basic = 0;
|
||||
u32 extended = 0;
|
||||
int running=0;
|
||||
u32 bits;
|
||||
int running = 0;
|
||||
|
||||
switch (cmd) {
|
||||
case SNDRV_PCM_TRIGGER_START:
|
||||
running=1;
|
||||
case SNDRV_PCM_TRIGGER_RESUME:
|
||||
running = 1;
|
||||
break;
|
||||
case SNDRV_PCM_TRIGGER_STOP:
|
||||
case SNDRV_PCM_TRIGGER_SUSPEND:
|
||||
default:
|
||||
running=0;
|
||||
running = 0;
|
||||
break;
|
||||
}
|
||||
snd_pcm_group_for_each_entry(s, substream) {
|
||||
|
@ -865,22 +888,32 @@ static int snd_ca0106_pcm_trigger_playback(struct snd_pcm_substream *substream,
|
|||
runtime = s->runtime;
|
||||
epcm = runtime->private_data;
|
||||
channel = epcm->channel_id;
|
||||
//snd_printk("channel=%d\n",channel);
|
||||
/* snd_printk("channel=%d\n",channel); */
|
||||
epcm->running = running;
|
||||
basic |= (0x1<<channel);
|
||||
extended |= (0x10<<channel);
|
||||
basic |= (0x1 << channel);
|
||||
extended |= (0x10 << channel);
|
||||
snd_pcm_trigger_done(s, substream);
|
||||
}
|
||||
//snd_printk("basic=0x%x, extended=0x%x\n",basic, extended);
|
||||
/* snd_printk("basic=0x%x, extended=0x%x\n",basic, extended); */
|
||||
|
||||
switch (cmd) {
|
||||
case SNDRV_PCM_TRIGGER_START:
|
||||
snd_ca0106_ptr_write(emu, EXTENDED_INT_MASK, 0, snd_ca0106_ptr_read(emu, EXTENDED_INT_MASK, 0) | (extended));
|
||||
snd_ca0106_ptr_write(emu, BASIC_INTERRUPT, 0, snd_ca0106_ptr_read(emu, BASIC_INTERRUPT, 0)|(basic));
|
||||
case SNDRV_PCM_TRIGGER_RESUME:
|
||||
bits = snd_ca0106_ptr_read(emu, EXTENDED_INT_MASK, 0);
|
||||
bits |= extended;
|
||||
snd_ca0106_ptr_write(emu, EXTENDED_INT_MASK, 0, bits);
|
||||
bits = snd_ca0106_ptr_read(emu, BASIC_INTERRUPT, 0);
|
||||
bits |= basic;
|
||||
snd_ca0106_ptr_write(emu, BASIC_INTERRUPT, 0, bits);
|
||||
break;
|
||||
case SNDRV_PCM_TRIGGER_STOP:
|
||||
snd_ca0106_ptr_write(emu, BASIC_INTERRUPT, 0, snd_ca0106_ptr_read(emu, BASIC_INTERRUPT, 0) & ~(basic));
|
||||
snd_ca0106_ptr_write(emu, EXTENDED_INT_MASK, 0, snd_ca0106_ptr_read(emu, EXTENDED_INT_MASK, 0) & ~(extended));
|
||||
case SNDRV_PCM_TRIGGER_SUSPEND:
|
||||
bits = snd_ca0106_ptr_read(emu, BASIC_INTERRUPT, 0);
|
||||
bits &= ~basic;
|
||||
snd_ca0106_ptr_write(emu, BASIC_INTERRUPT, 0, bits);
|
||||
bits = snd_ca0106_ptr_read(emu, EXTENDED_INT_MASK, 0);
|
||||
bits &= ~extended;
|
||||
snd_ca0106_ptr_write(emu, EXTENDED_INT_MASK, 0, bits);
|
||||
break;
|
||||
default:
|
||||
result = -EINVAL;
|
||||
|
@ -1103,21 +1136,13 @@ static int snd_ca0106_ac97(struct snd_ca0106 *chip)
|
|||
return snd_ac97_mixer(pbus, &ac97, &chip->ac97);
|
||||
}
|
||||
|
||||
static void ca0106_stop_chip(struct snd_ca0106 *chip);
|
||||
|
||||
static int snd_ca0106_free(struct snd_ca0106 *chip)
|
||||
{
|
||||
if (chip->res_port != NULL) { /* avoid access to already used hardware */
|
||||
// disable interrupts
|
||||
snd_ca0106_ptr_write(chip, BASIC_INTERRUPT, 0, 0);
|
||||
outl(0, chip->port + INTE);
|
||||
snd_ca0106_ptr_write(chip, EXTENDED_INT_MASK, 0, 0);
|
||||
udelay(1000);
|
||||
// disable audio
|
||||
//outl(HCFG_LOCKSOUNDCACHE, chip->port + HCFG);
|
||||
outl(0, chip->port + HCFG);
|
||||
/* FIXME: We need to stop and DMA transfers here.
|
||||
* But as I am not sure how yet, we cannot from the dma pages.
|
||||
* So we can fix: snd-malloc: Memory leak? pages not freed = 8
|
||||
*/
|
||||
if (chip->res_port != NULL) {
|
||||
/* avoid access to already used hardware */
|
||||
ca0106_stop_chip(chip);
|
||||
}
|
||||
if (chip->irq >= 0)
|
||||
free_irq(chip->irq, chip);
|
||||
|
@ -1203,15 +1228,14 @@ static irqreturn_t snd_ca0106_interrupt(int irq, void *dev_id)
|
|||
return IRQ_HANDLED;
|
||||
}
|
||||
|
||||
static int __devinit snd_ca0106_pcm(struct snd_ca0106 *emu, int device, struct snd_pcm **rpcm)
|
||||
static int __devinit snd_ca0106_pcm(struct snd_ca0106 *emu, int device)
|
||||
{
|
||||
struct snd_pcm *pcm;
|
||||
struct snd_pcm_substream *substream;
|
||||
int err;
|
||||
|
||||
if (rpcm)
|
||||
*rpcm = NULL;
|
||||
if ((err = snd_pcm_new(emu->card, "ca0106", device, 1, 1, &pcm)) < 0)
|
||||
err = snd_pcm_new(emu->card, "ca0106", device, 1, 1, &pcm);
|
||||
if (err < 0)
|
||||
return err;
|
||||
|
||||
pcm->private_data = emu;
|
||||
|
@ -1238,7 +1262,6 @@ static int __devinit snd_ca0106_pcm(struct snd_ca0106 *emu, int device, struct s
|
|||
pcm->info_flags = 0;
|
||||
pcm->dev_subclass = SNDRV_PCM_SUBCLASS_GENERIC_MIX;
|
||||
strcpy(pcm->name, "CA0106");
|
||||
emu->pcm = pcm;
|
||||
|
||||
for(substream = pcm->streams[SNDRV_PCM_STREAM_PLAYBACK].substream;
|
||||
substream;
|
||||
|
@ -1260,8 +1283,7 @@ static int __devinit snd_ca0106_pcm(struct snd_ca0106 *emu, int device, struct s
|
|||
return err;
|
||||
}
|
||||
|
||||
if (rpcm)
|
||||
*rpcm = pcm;
|
||||
emu->pcm[device] = pcm;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
@ -1301,89 +1323,10 @@ static unsigned int i2c_adc_init[][2] = {
|
|||
{ 0x15, ADC_MUX_LINEIN }, /* ADC Mixer control */
|
||||
};
|
||||
|
||||
static int __devinit snd_ca0106_create(int dev, struct snd_card *card,
|
||||
struct pci_dev *pci,
|
||||
struct snd_ca0106 **rchip)
|
||||
static void ca0106_init_chip(struct snd_ca0106 *chip, int resume)
|
||||
{
|
||||
struct snd_ca0106 *chip;
|
||||
struct snd_ca0106_details *c;
|
||||
int err;
|
||||
int ch;
|
||||
static struct snd_device_ops ops = {
|
||||
.dev_free = snd_ca0106_dev_free,
|
||||
};
|
||||
|
||||
*rchip = NULL;
|
||||
|
||||
if ((err = pci_enable_device(pci)) < 0)
|
||||
return err;
|
||||
if (pci_set_dma_mask(pci, DMA_32BIT_MASK) < 0 ||
|
||||
pci_set_consistent_dma_mask(pci, DMA_32BIT_MASK) < 0) {
|
||||
printk(KERN_ERR "error to set 32bit mask DMA\n");
|
||||
pci_disable_device(pci);
|
||||
return -ENXIO;
|
||||
}
|
||||
|
||||
chip = kzalloc(sizeof(*chip), GFP_KERNEL);
|
||||
if (chip == NULL) {
|
||||
pci_disable_device(pci);
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
chip->card = card;
|
||||
chip->pci = pci;
|
||||
chip->irq = -1;
|
||||
|
||||
spin_lock_init(&chip->emu_lock);
|
||||
|
||||
chip->port = pci_resource_start(pci, 0);
|
||||
if ((chip->res_port = request_region(chip->port, 0x20,
|
||||
"snd_ca0106")) == NULL) {
|
||||
snd_ca0106_free(chip);
|
||||
printk(KERN_ERR "cannot allocate the port\n");
|
||||
return -EBUSY;
|
||||
}
|
||||
|
||||
if (request_irq(pci->irq, snd_ca0106_interrupt,
|
||||
IRQF_SHARED, "snd_ca0106", chip)) {
|
||||
snd_ca0106_free(chip);
|
||||
printk(KERN_ERR "cannot grab irq\n");
|
||||
return -EBUSY;
|
||||
}
|
||||
chip->irq = pci->irq;
|
||||
|
||||
/* This stores the periods table. */
|
||||
if(snd_dma_alloc_pages(SNDRV_DMA_TYPE_DEV, snd_dma_pci_data(pci), 1024, &chip->buffer) < 0) {
|
||||
snd_ca0106_free(chip);
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
pci_set_master(pci);
|
||||
/* read serial */
|
||||
pci_read_config_dword(pci, PCI_SUBSYSTEM_VENDOR_ID, &chip->serial);
|
||||
pci_read_config_word(pci, PCI_SUBSYSTEM_ID, &chip->model);
|
||||
#if 1
|
||||
printk(KERN_INFO "snd-ca0106: Model %04x Rev %08x Serial %08x\n", chip->model,
|
||||
pci->revision, chip->serial);
|
||||
#endif
|
||||
strcpy(card->driver, "CA0106");
|
||||
strcpy(card->shortname, "CA0106");
|
||||
|
||||
for (c = ca0106_chip_details; c->serial; c++) {
|
||||
if (subsystem[dev]) {
|
||||
if (c->serial == subsystem[dev])
|
||||
break;
|
||||
} else if (c->serial == chip->serial)
|
||||
break;
|
||||
}
|
||||
chip->details = c;
|
||||
if (subsystem[dev]) {
|
||||
printk(KERN_INFO "snd-ca0106: Sound card name=%s, subsystem=0x%x. Forced to subsystem=0x%x\n",
|
||||
c->name, chip->serial, subsystem[dev]);
|
||||
}
|
||||
|
||||
sprintf(card->longname, "%s at 0x%lx irq %i",
|
||||
c->name, chip->port, chip->irq);
|
||||
unsigned int def_bits;
|
||||
|
||||
outl(0, chip->port + INTE);
|
||||
|
||||
|
@ -1401,31 +1344,22 @@ static int __devinit snd_ca0106_create(int dev, struct snd_card *card,
|
|||
* AN = 0 (Audio data)
|
||||
* P = 0 (Consumer)
|
||||
*/
|
||||
snd_ca0106_ptr_write(chip, SPCS0, 0,
|
||||
chip->spdif_bits[0] =
|
||||
SPCS_CLKACCY_1000PPM | SPCS_SAMPLERATE_48 |
|
||||
SPCS_CHANNELNUM_LEFT | SPCS_SOURCENUM_UNSPEC |
|
||||
SPCS_GENERATIONSTATUS | 0x00001200 |
|
||||
0x00000000 | SPCS_EMPHASIS_NONE | SPCS_COPYRIGHT);
|
||||
def_bits =
|
||||
SPCS_CLKACCY_1000PPM | SPCS_SAMPLERATE_48 |
|
||||
SPCS_CHANNELNUM_LEFT | SPCS_SOURCENUM_UNSPEC |
|
||||
SPCS_GENERATIONSTATUS | 0x00001200 |
|
||||
0x00000000 | SPCS_EMPHASIS_NONE | SPCS_COPYRIGHT;
|
||||
if (!resume) {
|
||||
chip->spdif_str_bits[0] = chip->spdif_bits[0] = def_bits;
|
||||
chip->spdif_str_bits[1] = chip->spdif_bits[1] = def_bits;
|
||||
chip->spdif_str_bits[2] = chip->spdif_bits[2] = def_bits;
|
||||
chip->spdif_str_bits[3] = chip->spdif_bits[3] = def_bits;
|
||||
}
|
||||
/* Only SPCS1 has been tested */
|
||||
snd_ca0106_ptr_write(chip, SPCS1, 0,
|
||||
chip->spdif_bits[1] =
|
||||
SPCS_CLKACCY_1000PPM | SPCS_SAMPLERATE_48 |
|
||||
SPCS_CHANNELNUM_LEFT | SPCS_SOURCENUM_UNSPEC |
|
||||
SPCS_GENERATIONSTATUS | 0x00001200 |
|
||||
0x00000000 | SPCS_EMPHASIS_NONE | SPCS_COPYRIGHT);
|
||||
snd_ca0106_ptr_write(chip, SPCS2, 0,
|
||||
chip->spdif_bits[2] =
|
||||
SPCS_CLKACCY_1000PPM | SPCS_SAMPLERATE_48 |
|
||||
SPCS_CHANNELNUM_LEFT | SPCS_SOURCENUM_UNSPEC |
|
||||
SPCS_GENERATIONSTATUS | 0x00001200 |
|
||||
0x00000000 | SPCS_EMPHASIS_NONE | SPCS_COPYRIGHT);
|
||||
snd_ca0106_ptr_write(chip, SPCS3, 0,
|
||||
chip->spdif_bits[3] =
|
||||
SPCS_CLKACCY_1000PPM | SPCS_SAMPLERATE_48 |
|
||||
SPCS_CHANNELNUM_LEFT | SPCS_SOURCENUM_UNSPEC |
|
||||
SPCS_GENERATIONSTATUS | 0x00001200 |
|
||||
0x00000000 | SPCS_EMPHASIS_NONE | SPCS_COPYRIGHT);
|
||||
snd_ca0106_ptr_write(chip, SPCS1, 0, chip->spdif_str_bits[1]);
|
||||
snd_ca0106_ptr_write(chip, SPCS0, 0, chip->spdif_str_bits[0]);
|
||||
snd_ca0106_ptr_write(chip, SPCS2, 0, chip->spdif_str_bits[2]);
|
||||
snd_ca0106_ptr_write(chip, SPCS3, 0, chip->spdif_str_bits[3]);
|
||||
|
||||
snd_ca0106_ptr_write(chip, PLAYBACK_MUTE, 0, 0x00fc0000);
|
||||
snd_ca0106_ptr_write(chip, CAPTURE_MUTE, 0, 0x00fc0000);
|
||||
|
@ -1433,92 +1367,124 @@ static int __devinit snd_ca0106_create(int dev, struct snd_card *card,
|
|||
/* Write 0x8000 to AC97_REC_GAIN to mute it. */
|
||||
outb(AC97_REC_GAIN, chip->port + AC97ADDRESS);
|
||||
outw(0x8000, chip->port + AC97DATA);
|
||||
#if 0
|
||||
#if 0 /* FIXME: what are these? */
|
||||
snd_ca0106_ptr_write(chip, SPCS0, 0, 0x2108006);
|
||||
snd_ca0106_ptr_write(chip, 0x42, 0, 0x2108006);
|
||||
snd_ca0106_ptr_write(chip, 0x43, 0, 0x2108006);
|
||||
snd_ca0106_ptr_write(chip, 0x44, 0, 0x2108006);
|
||||
#endif
|
||||
|
||||
//snd_ca0106_ptr_write(chip, SPDIF_SELECT2, 0, 0xf0f003f); /* OSS drivers set this. */
|
||||
/* OSS drivers set this. */
|
||||
/* snd_ca0106_ptr_write(chip, SPDIF_SELECT2, 0, 0xf0f003f); */
|
||||
|
||||
/* Analog or Digital output */
|
||||
snd_ca0106_ptr_write(chip, SPDIF_SELECT1, 0, 0xf);
|
||||
snd_ca0106_ptr_write(chip, SPDIF_SELECT2, 0, 0x000f0000); /* 0x0b000000 for digital, 0x000b0000 for analog, from win2000 drivers. Use 0x000f0000 for surround71 */
|
||||
chip->spdif_enable = 0; /* Set digital SPDIF output off */
|
||||
//snd_ca0106_ptr_write(chip, 0x45, 0, 0); /* Analogue out */
|
||||
//snd_ca0106_ptr_write(chip, 0x45, 0, 0xf00); /* Digital out */
|
||||
/* 0x0b000000 for digital, 0x000b0000 for analog, from win2000 drivers.
|
||||
* Use 0x000f0000 for surround71
|
||||
*/
|
||||
snd_ca0106_ptr_write(chip, SPDIF_SELECT2, 0, 0x000f0000);
|
||||
|
||||
chip->spdif_enable = 0; /* Set digital SPDIF output off */
|
||||
/*snd_ca0106_ptr_write(chip, 0x45, 0, 0);*/ /* Analogue out */
|
||||
/*snd_ca0106_ptr_write(chip, 0x45, 0, 0xf00);*/ /* Digital out */
|
||||
|
||||
/* goes to 0x40c80000 when doing SPDIF IN/OUT */
|
||||
snd_ca0106_ptr_write(chip, CAPTURE_CONTROL, 0, 0x40c81000);
|
||||
/* (Mute) CAPTURE feedback into PLAYBACK volume.
|
||||
* Only lower 16 bits matter.
|
||||
*/
|
||||
snd_ca0106_ptr_write(chip, CAPTURE_CONTROL, 1, 0xffffffff);
|
||||
/* SPDIF IN Volume */
|
||||
snd_ca0106_ptr_write(chip, CAPTURE_CONTROL, 2, 0x30300000);
|
||||
/* SPDIF IN Volume, 0x70 = (vol & 0x3f) | 0x40 */
|
||||
snd_ca0106_ptr_write(chip, CAPTURE_CONTROL, 3, 0x00700000);
|
||||
|
||||
snd_ca0106_ptr_write(chip, CAPTURE_CONTROL, 0, 0x40c81000); /* goes to 0x40c80000 when doing SPDIF IN/OUT */
|
||||
snd_ca0106_ptr_write(chip, CAPTURE_CONTROL, 1, 0xffffffff); /* (Mute) CAPTURE feedback into PLAYBACK volume. Only lower 16 bits matter. */
|
||||
snd_ca0106_ptr_write(chip, CAPTURE_CONTROL, 2, 0x30300000); /* SPDIF IN Volume */
|
||||
snd_ca0106_ptr_write(chip, CAPTURE_CONTROL, 3, 0x00700000); /* SPDIF IN Volume, 0x70 = (vol & 0x3f) | 0x40 */
|
||||
snd_ca0106_ptr_write(chip, PLAYBACK_ROUTING1, 0, 0x32765410);
|
||||
snd_ca0106_ptr_write(chip, PLAYBACK_ROUTING2, 0, 0x76767676);
|
||||
snd_ca0106_ptr_write(chip, CAPTURE_ROUTING1, 0, 0x32765410);
|
||||
snd_ca0106_ptr_write(chip, CAPTURE_ROUTING2, 0, 0x76767676);
|
||||
for(ch = 0; ch < 4; ch++) {
|
||||
snd_ca0106_ptr_write(chip, CAPTURE_VOLUME1, ch, 0x30303030); /* Only high 16 bits matter */
|
||||
|
||||
for (ch = 0; ch < 4; ch++) {
|
||||
/* Only high 16 bits matter */
|
||||
snd_ca0106_ptr_write(chip, CAPTURE_VOLUME1, ch, 0x30303030);
|
||||
snd_ca0106_ptr_write(chip, CAPTURE_VOLUME2, ch, 0x30303030);
|
||||
//snd_ca0106_ptr_write(chip, PLAYBACK_VOLUME1, ch, 0x40404040); /* Mute */
|
||||
//snd_ca0106_ptr_write(chip, PLAYBACK_VOLUME2, ch, 0x40404040); /* Mute */
|
||||
snd_ca0106_ptr_write(chip, PLAYBACK_VOLUME1, ch, 0xffffffff); /* Mute */
|
||||
snd_ca0106_ptr_write(chip, PLAYBACK_VOLUME2, ch, 0xffffffff); /* Mute */
|
||||
#if 0 /* Mute */
|
||||
snd_ca0106_ptr_write(chip, PLAYBACK_VOLUME1, ch, 0x40404040);
|
||||
snd_ca0106_ptr_write(chip, PLAYBACK_VOLUME2, ch, 0x40404040);
|
||||
snd_ca0106_ptr_write(chip, PLAYBACK_VOLUME1, ch, 0xffffffff);
|
||||
snd_ca0106_ptr_write(chip, PLAYBACK_VOLUME2, ch, 0xffffffff);
|
||||
#endif
|
||||
}
|
||||
if (chip->details->i2c_adc == 1) {
|
||||
/* Select MIC, Line in, TAD in, AUX in */
|
||||
snd_ca0106_ptr_write(chip, CAPTURE_SOURCE, 0x0, 0x333300e4);
|
||||
/* Default to CAPTURE_SOURCE to i2s in */
|
||||
chip->capture_source = 3;
|
||||
if (!resume)
|
||||
chip->capture_source = 3;
|
||||
} else if (chip->details->ac97 == 1) {
|
||||
/* Default to AC97 in */
|
||||
snd_ca0106_ptr_write(chip, CAPTURE_SOURCE, 0x0, 0x444400e4);
|
||||
/* Default to CAPTURE_SOURCE to AC97 in */
|
||||
chip->capture_source = 4;
|
||||
if (!resume)
|
||||
chip->capture_source = 4;
|
||||
} else {
|
||||
/* Select MIC, Line in, TAD in, AUX in */
|
||||
snd_ca0106_ptr_write(chip, CAPTURE_SOURCE, 0x0, 0x333300e4);
|
||||
/* Default to Set CAPTURE_SOURCE to i2s in */
|
||||
chip->capture_source = 3;
|
||||
if (!resume)
|
||||
chip->capture_source = 3;
|
||||
}
|
||||
|
||||
if (chip->details->gpio_type == 2) { /* The SB0438 use GPIO differently. */
|
||||
/* FIXME: Still need to find out what the other GPIO bits do. E.g. For digital spdif out. */
|
||||
if (chip->details->gpio_type == 2) {
|
||||
/* The SB0438 use GPIO differently. */
|
||||
/* FIXME: Still need to find out what the other GPIO bits do.
|
||||
* E.g. For digital spdif out.
|
||||
*/
|
||||
outl(0x0, chip->port+GPIO);
|
||||
//outl(0x00f0e000, chip->port+GPIO); /* Analog */
|
||||
/* outl(0x00f0e000, chip->port+GPIO); */ /* Analog */
|
||||
outl(0x005f5301, chip->port+GPIO); /* Analog */
|
||||
} else if (chip->details->gpio_type == 1) { /* The SB0410 and SB0413 use GPIO differently. */
|
||||
/* FIXME: Still need to find out what the other GPIO bits do. E.g. For digital spdif out. */
|
||||
} else if (chip->details->gpio_type == 1) {
|
||||
/* The SB0410 and SB0413 use GPIO differently. */
|
||||
/* FIXME: Still need to find out what the other GPIO bits do.
|
||||
* E.g. For digital spdif out.
|
||||
*/
|
||||
outl(0x0, chip->port+GPIO);
|
||||
//outl(0x00f0e000, chip->port+GPIO); /* Analog */
|
||||
/* outl(0x00f0e000, chip->port+GPIO); */ /* Analog */
|
||||
outl(0x005f5301, chip->port+GPIO); /* Analog */
|
||||
} else {
|
||||
outl(0x0, chip->port+GPIO);
|
||||
outl(0x005f03a3, chip->port+GPIO); /* Analog */
|
||||
//outl(0x005f02a2, chip->port+GPIO); /* SPDIF */
|
||||
/* outl(0x005f02a2, chip->port+GPIO); */ /* SPDIF */
|
||||
}
|
||||
snd_ca0106_intr_enable(chip, 0x105); /* Win2000 uses 0x1e0 */
|
||||
|
||||
//outl(HCFG_LOCKSOUNDCACHE|HCFG_AUDIOENABLE, chip->port+HCFG);
|
||||
//outl(0x00001409, chip->port+HCFG); /* 0x1000 causes AC3 to fails. Maybe it effects 24 bit output. */
|
||||
//outl(0x00000009, chip->port+HCFG);
|
||||
outl(HCFG_AC97 | HCFG_AUDIOENABLE, chip->port+HCFG); /* AC97 2.0, Enable outputs. */
|
||||
/* outl(HCFG_LOCKSOUNDCACHE|HCFG_AUDIOENABLE, chip->port+HCFG); */
|
||||
/* 0x1000 causes AC3 to fails. Maybe it effects 24 bit output. */
|
||||
/* outl(0x00001409, chip->port+HCFG); */
|
||||
/* outl(0x00000009, chip->port+HCFG); */
|
||||
/* AC97 2.0, Enable outputs. */
|
||||
outl(HCFG_AC97 | HCFG_AUDIOENABLE, chip->port+HCFG);
|
||||
|
||||
if (chip->details->i2c_adc == 1) { /* The SB0410 and SB0413 use I2C to control ADC. */
|
||||
if (chip->details->i2c_adc == 1) {
|
||||
/* The SB0410 and SB0413 use I2C to control ADC. */
|
||||
int size, n;
|
||||
|
||||
size = ARRAY_SIZE(i2c_adc_init);
|
||||
//snd_printk("I2C:array size=0x%x\n", size);
|
||||
for (n=0; n < size; n++) {
|
||||
snd_ca0106_i2c_write(chip, i2c_adc_init[n][0], i2c_adc_init[n][1]);
|
||||
/* snd_printk("I2C:array size=0x%x\n", size); */
|
||||
for (n = 0; n < size; n++)
|
||||
snd_ca0106_i2c_write(chip, i2c_adc_init[n][0],
|
||||
i2c_adc_init[n][1]);
|
||||
for (n = 0; n < 4; n++) {
|
||||
chip->i2c_capture_volume[n][0] = 0xcf;
|
||||
chip->i2c_capture_volume[n][1] = 0xcf;
|
||||
}
|
||||
for (n=0; n < 4; n++) {
|
||||
chip->i2c_capture_volume[n][0]= 0xcf;
|
||||
chip->i2c_capture_volume[n][1]= 0xcf;
|
||||
}
|
||||
chip->i2c_capture_source=2; /* Line in */
|
||||
//snd_ca0106_i2c_write(chip, ADC_MUX, ADC_MUX_LINEIN); /* Enable Line-in capture. MIC in currently untested. */
|
||||
chip->i2c_capture_source = 2; /* Line in */
|
||||
/* Enable Line-in capture. MIC in currently untested. */
|
||||
/* snd_ca0106_i2c_write(chip, ADC_MUX, ADC_MUX_LINEIN); */
|
||||
}
|
||||
if (chip->details->spi_dac == 1) { /* The SB0570 use SPI to control DAC. */
|
||||
|
||||
if (chip->details->spi_dac == 1) {
|
||||
/* The SB0570 use SPI to control DAC. */
|
||||
int size, n;
|
||||
|
||||
size = ARRAY_SIZE(spi_dac_init);
|
||||
|
@ -1530,9 +1496,112 @@ static int __devinit snd_ca0106_create(int dev, struct snd_card *card,
|
|||
chip->spi_dac_reg[reg] = spi_dac_init[n];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if ((err = snd_device_new(card, SNDRV_DEV_LOWLEVEL,
|
||||
chip, &ops)) < 0) {
|
||||
static void ca0106_stop_chip(struct snd_ca0106 *chip)
|
||||
{
|
||||
/* disable interrupts */
|
||||
snd_ca0106_ptr_write(chip, BASIC_INTERRUPT, 0, 0);
|
||||
outl(0, chip->port + INTE);
|
||||
snd_ca0106_ptr_write(chip, EXTENDED_INT_MASK, 0, 0);
|
||||
udelay(1000);
|
||||
/* disable audio */
|
||||
/* outl(HCFG_LOCKSOUNDCACHE, chip->port + HCFG); */
|
||||
outl(0, chip->port + HCFG);
|
||||
/* FIXME: We need to stop and DMA transfers here.
|
||||
* But as I am not sure how yet, we cannot from the dma pages.
|
||||
* So we can fix: snd-malloc: Memory leak? pages not freed = 8
|
||||
*/
|
||||
}
|
||||
|
||||
static int __devinit snd_ca0106_create(int dev, struct snd_card *card,
|
||||
struct pci_dev *pci,
|
||||
struct snd_ca0106 **rchip)
|
||||
{
|
||||
struct snd_ca0106 *chip;
|
||||
struct snd_ca0106_details *c;
|
||||
int err;
|
||||
static struct snd_device_ops ops = {
|
||||
.dev_free = snd_ca0106_dev_free,
|
||||
};
|
||||
|
||||
*rchip = NULL;
|
||||
|
||||
err = pci_enable_device(pci);
|
||||
if (err < 0)
|
||||
return err;
|
||||
if (pci_set_dma_mask(pci, DMA_32BIT_MASK) < 0 ||
|
||||
pci_set_consistent_dma_mask(pci, DMA_32BIT_MASK) < 0) {
|
||||
printk(KERN_ERR "error to set 32bit mask DMA\n");
|
||||
pci_disable_device(pci);
|
||||
return -ENXIO;
|
||||
}
|
||||
|
||||
chip = kzalloc(sizeof(*chip), GFP_KERNEL);
|
||||
if (chip == NULL) {
|
||||
pci_disable_device(pci);
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
chip->card = card;
|
||||
chip->pci = pci;
|
||||
chip->irq = -1;
|
||||
|
||||
spin_lock_init(&chip->emu_lock);
|
||||
|
||||
chip->port = pci_resource_start(pci, 0);
|
||||
chip->res_port = request_region(chip->port, 0x20, "snd_ca0106");
|
||||
if (!chip->res_port) {
|
||||
snd_ca0106_free(chip);
|
||||
printk(KERN_ERR "cannot allocate the port\n");
|
||||
return -EBUSY;
|
||||
}
|
||||
|
||||
if (request_irq(pci->irq, snd_ca0106_interrupt,
|
||||
IRQF_SHARED, "snd_ca0106", chip)) {
|
||||
snd_ca0106_free(chip);
|
||||
printk(KERN_ERR "cannot grab irq\n");
|
||||
return -EBUSY;
|
||||
}
|
||||
chip->irq = pci->irq;
|
||||
|
||||
/* This stores the periods table. */
|
||||
if (snd_dma_alloc_pages(SNDRV_DMA_TYPE_DEV, snd_dma_pci_data(pci),
|
||||
1024, &chip->buffer) < 0) {
|
||||
snd_ca0106_free(chip);
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
pci_set_master(pci);
|
||||
/* read serial */
|
||||
pci_read_config_dword(pci, PCI_SUBSYSTEM_VENDOR_ID, &chip->serial);
|
||||
pci_read_config_word(pci, PCI_SUBSYSTEM_ID, &chip->model);
|
||||
printk(KERN_INFO "snd-ca0106: Model %04x Rev %08x Serial %08x\n",
|
||||
chip->model, pci->revision, chip->serial);
|
||||
strcpy(card->driver, "CA0106");
|
||||
strcpy(card->shortname, "CA0106");
|
||||
|
||||
for (c = ca0106_chip_details; c->serial; c++) {
|
||||
if (subsystem[dev]) {
|
||||
if (c->serial == subsystem[dev])
|
||||
break;
|
||||
} else if (c->serial == chip->serial)
|
||||
break;
|
||||
}
|
||||
chip->details = c;
|
||||
if (subsystem[dev]) {
|
||||
printk(KERN_INFO "snd-ca0106: Sound card name=%s, "
|
||||
"subsystem=0x%x. Forced to subsystem=0x%x\n",
|
||||
c->name, chip->serial, subsystem[dev]);
|
||||
}
|
||||
|
||||
sprintf(card->longname, "%s at 0x%lx irq %i",
|
||||
c->name, chip->port, chip->irq);
|
||||
|
||||
ca0106_init_chip(chip, 0);
|
||||
|
||||
err = snd_device_new(card, SNDRV_DEV_LOWLEVEL, chip, &ops);
|
||||
if (err < 0) {
|
||||
snd_ca0106_free(chip);
|
||||
return err;
|
||||
}
|
||||
|
@ -1629,7 +1698,7 @@ static int __devinit snd_ca0106_probe(struct pci_dev *pci,
|
|||
static int dev;
|
||||
struct snd_card *card;
|
||||
struct snd_ca0106 *chip;
|
||||
int err;
|
||||
int i, err;
|
||||
|
||||
if (dev >= SNDRV_CARDS)
|
||||
return -ENODEV;
|
||||
|
@ -1642,44 +1711,31 @@ static int __devinit snd_ca0106_probe(struct pci_dev *pci,
|
|||
if (card == NULL)
|
||||
return -ENOMEM;
|
||||
|
||||
if ((err = snd_ca0106_create(dev, card, pci, &chip)) < 0) {
|
||||
snd_card_free(card);
|
||||
return err;
|
||||
err = snd_ca0106_create(dev, card, pci, &chip);
|
||||
if (err < 0)
|
||||
goto error;
|
||||
card->private_data = chip;
|
||||
|
||||
for (i = 0; i < 4; i++) {
|
||||
err = snd_ca0106_pcm(chip, i);
|
||||
if (err < 0)
|
||||
goto error;
|
||||
}
|
||||
|
||||
if ((err = snd_ca0106_pcm(chip, 0, NULL)) < 0) {
|
||||
snd_card_free(card);
|
||||
return err;
|
||||
}
|
||||
if ((err = snd_ca0106_pcm(chip, 1, NULL)) < 0) {
|
||||
snd_card_free(card);
|
||||
return err;
|
||||
}
|
||||
if ((err = snd_ca0106_pcm(chip, 2, NULL)) < 0) {
|
||||
snd_card_free(card);
|
||||
return err;
|
||||
}
|
||||
if ((err = snd_ca0106_pcm(chip, 3, NULL)) < 0) {
|
||||
snd_card_free(card);
|
||||
return err;
|
||||
}
|
||||
if (chip->details->ac97 == 1) { /* The SB0410 and SB0413 do not have an AC97 chip. */
|
||||
if ((err = snd_ca0106_ac97(chip)) < 0) {
|
||||
snd_card_free(card);
|
||||
return err;
|
||||
}
|
||||
}
|
||||
if ((err = snd_ca0106_mixer(chip)) < 0) {
|
||||
snd_card_free(card);
|
||||
return err;
|
||||
if (chip->details->ac97 == 1) {
|
||||
/* The SB0410 and SB0413 do not have an AC97 chip. */
|
||||
err = snd_ca0106_ac97(chip);
|
||||
if (err < 0)
|
||||
goto error;
|
||||
}
|
||||
err = snd_ca0106_mixer(chip);
|
||||
if (err < 0)
|
||||
goto error;
|
||||
|
||||
snd_printdd("ca0106: probe for MIDI channel A ...");
|
||||
if ((err = snd_ca0106_midi(chip,CA0106_MIDI_CHAN_A)) < 0) {
|
||||
snd_card_free(card);
|
||||
snd_printdd(" failed, err=0x%x\n",err);
|
||||
return err;
|
||||
}
|
||||
err = snd_ca0106_midi(chip, CA0106_MIDI_CHAN_A);
|
||||
if (err < 0)
|
||||
goto error;
|
||||
snd_printdd(" done.\n");
|
||||
|
||||
#ifdef CONFIG_PROC_FS
|
||||
|
@ -1688,14 +1744,17 @@ static int __devinit snd_ca0106_probe(struct pci_dev *pci,
|
|||
|
||||
snd_card_set_dev(card, &pci->dev);
|
||||
|
||||
if ((err = snd_card_register(card)) < 0) {
|
||||
snd_card_free(card);
|
||||
return err;
|
||||
}
|
||||
err = snd_card_register(card);
|
||||
if (err < 0)
|
||||
goto error;
|
||||
|
||||
pci_set_drvdata(pci, card);
|
||||
dev++;
|
||||
return 0;
|
||||
|
||||
error:
|
||||
snd_card_free(card);
|
||||
return err;
|
||||
}
|
||||
|
||||
static void __devexit snd_ca0106_remove(struct pci_dev *pci)
|
||||
|
@ -1704,6 +1763,59 @@ static void __devexit snd_ca0106_remove(struct pci_dev *pci)
|
|||
pci_set_drvdata(pci, NULL);
|
||||
}
|
||||
|
||||
#ifdef CONFIG_PM
|
||||
static int snd_ca0106_suspend(struct pci_dev *pci, pm_message_t state)
|
||||
{
|
||||
struct snd_card *card = pci_get_drvdata(pci);
|
||||
struct snd_ca0106 *chip = card->private_data;
|
||||
int i;
|
||||
|
||||
snd_power_change_state(card, SNDRV_CTL_POWER_D3hot);
|
||||
for (i = 0; i < 4; i++)
|
||||
snd_pcm_suspend_all(chip->pcm[i]);
|
||||
if (chip->details->ac97)
|
||||
snd_ac97_suspend(chip->ac97);
|
||||
snd_ca0106_mixer_suspend(chip);
|
||||
|
||||
ca0106_stop_chip(chip);
|
||||
|
||||
pci_disable_device(pci);
|
||||
pci_save_state(pci);
|
||||
pci_set_power_state(pci, pci_choose_state(pci, state));
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int snd_ca0106_resume(struct pci_dev *pci)
|
||||
{
|
||||
struct snd_card *card = pci_get_drvdata(pci);
|
||||
struct snd_ca0106 *chip = card->private_data;
|
||||
int i;
|
||||
|
||||
pci_set_power_state(pci, PCI_D0);
|
||||
pci_restore_state(pci);
|
||||
|
||||
if (pci_enable_device(pci) < 0) {
|
||||
snd_card_disconnect(card);
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
pci_set_master(pci);
|
||||
|
||||
ca0106_init_chip(chip, 1);
|
||||
|
||||
if (chip->details->ac97)
|
||||
snd_ac97_resume(chip->ac97);
|
||||
snd_ca0106_mixer_resume(chip);
|
||||
if (chip->details->spi_dac) {
|
||||
for (i = 0; i < ARRAY_SIZE(chip->spi_dac_reg); i++)
|
||||
snd_ca0106_spi_write(chip, chip->spi_dac_reg[i]);
|
||||
}
|
||||
|
||||
snd_power_change_state(card, SNDRV_CTL_POWER_D0);
|
||||
return 0;
|
||||
}
|
||||
#endif
|
||||
|
||||
// PCI IDs
|
||||
static struct pci_device_id snd_ca0106_ids[] = {
|
||||
{ 0x1102, 0x0007, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0 }, /* Audigy LS or Live 24bit */
|
||||
|
@ -1717,6 +1829,10 @@ static struct pci_driver driver = {
|
|||
.id_table = snd_ca0106_ids,
|
||||
.probe = snd_ca0106_probe,
|
||||
.remove = __devexit_p(snd_ca0106_remove),
|
||||
#ifdef CONFIG_PM
|
||||
.suspend = snd_ca0106_suspend,
|
||||
.resume = snd_ca0106_resume,
|
||||
#endif
|
||||
};
|
||||
|
||||
// initialization of the module
|
||||
|
|
|
@ -75,6 +75,84 @@
|
|||
|
||||
#include "ca0106.h"
|
||||
|
||||
static void ca0106_spdif_enable(struct snd_ca0106 *emu)
|
||||
{
|
||||
unsigned int val;
|
||||
|
||||
if (emu->spdif_enable) {
|
||||
/* Digital */
|
||||
snd_ca0106_ptr_write(emu, SPDIF_SELECT1, 0, 0xf);
|
||||
snd_ca0106_ptr_write(emu, SPDIF_SELECT2, 0, 0x0b000000);
|
||||
val = snd_ca0106_ptr_read(emu, CAPTURE_CONTROL, 0) & ~0x1000;
|
||||
snd_ca0106_ptr_write(emu, CAPTURE_CONTROL, 0, val);
|
||||
val = inl(emu->port + GPIO) & ~0x101;
|
||||
outl(val, emu->port + GPIO);
|
||||
|
||||
} else {
|
||||
/* Analog */
|
||||
snd_ca0106_ptr_write(emu, SPDIF_SELECT1, 0, 0xf);
|
||||
snd_ca0106_ptr_write(emu, SPDIF_SELECT2, 0, 0x000f0000);
|
||||
val = snd_ca0106_ptr_read(emu, CAPTURE_CONTROL, 0) | 0x1000;
|
||||
snd_ca0106_ptr_write(emu, CAPTURE_CONTROL, 0, val);
|
||||
val = inl(emu->port + GPIO) | 0x101;
|
||||
outl(val, emu->port + GPIO);
|
||||
}
|
||||
}
|
||||
|
||||
static void ca0106_set_capture_source(struct snd_ca0106 *emu)
|
||||
{
|
||||
unsigned int val = emu->capture_source;
|
||||
unsigned int source, mask;
|
||||
source = (val << 28) | (val << 24) | (val << 20) | (val << 16);
|
||||
mask = snd_ca0106_ptr_read(emu, CAPTURE_SOURCE, 0) & 0xffff;
|
||||
snd_ca0106_ptr_write(emu, CAPTURE_SOURCE, 0, source | mask);
|
||||
}
|
||||
|
||||
static void ca0106_set_i2c_capture_source(struct snd_ca0106 *emu,
|
||||
unsigned int val, int force)
|
||||
{
|
||||
unsigned int ngain, ogain;
|
||||
u32 source;
|
||||
|
||||
snd_ca0106_i2c_write(emu, ADC_MUX, 0); /* Mute input */
|
||||
ngain = emu->i2c_capture_volume[val][0]; /* Left */
|
||||
ogain = emu->i2c_capture_volume[emu->i2c_capture_source][0]; /* Left */
|
||||
if (force || ngain != ogain)
|
||||
snd_ca0106_i2c_write(emu, ADC_ATTEN_ADCL, ngain & 0xff);
|
||||
ngain = emu->i2c_capture_volume[val][1]; /* Right */
|
||||
ogain = emu->i2c_capture_volume[emu->i2c_capture_source][1]; /* Right */
|
||||
if (force || ngain != ogain)
|
||||
snd_ca0106_i2c_write(emu, ADC_ATTEN_ADCR, ngain & 0xff);
|
||||
source = 1 << val;
|
||||
snd_ca0106_i2c_write(emu, ADC_MUX, source); /* Set source */
|
||||
emu->i2c_capture_source = val;
|
||||
}
|
||||
|
||||
static void ca0106_set_capture_mic_line_in(struct snd_ca0106 *emu)
|
||||
{
|
||||
u32 tmp;
|
||||
|
||||
if (emu->capture_mic_line_in) {
|
||||
/* snd_ca0106_i2c_write(emu, ADC_MUX, 0); */ /* Mute input */
|
||||
tmp = inl(emu->port+GPIO) & ~0x400;
|
||||
tmp = tmp | 0x400;
|
||||
outl(tmp, emu->port+GPIO);
|
||||
/* snd_ca0106_i2c_write(emu, ADC_MUX, ADC_MUX_MIC); */
|
||||
} else {
|
||||
/* snd_ca0106_i2c_write(emu, ADC_MUX, 0); */ /* Mute input */
|
||||
tmp = inl(emu->port+GPIO) & ~0x400;
|
||||
outl(tmp, emu->port+GPIO);
|
||||
/* snd_ca0106_i2c_write(emu, ADC_MUX, ADC_MUX_LINEIN); */
|
||||
}
|
||||
}
|
||||
|
||||
static void ca0106_set_spdif_bits(struct snd_ca0106 *emu, int idx)
|
||||
{
|
||||
snd_ca0106_ptr_write(emu, SPCS0 + idx, 0, emu->spdif_str_bits[idx]);
|
||||
}
|
||||
|
||||
/*
|
||||
*/
|
||||
static const DECLARE_TLV_DB_SCALE(snd_ca0106_db_scale1, -5175, 25, 1);
|
||||
static const DECLARE_TLV_DB_SCALE(snd_ca0106_db_scale2, -10350, 50, 1);
|
||||
|
||||
|
@ -95,30 +173,12 @@ static int snd_ca0106_shared_spdif_put(struct snd_kcontrol *kcontrol,
|
|||
struct snd_ca0106 *emu = snd_kcontrol_chip(kcontrol);
|
||||
unsigned int val;
|
||||
int change = 0;
|
||||
u32 mask;
|
||||
|
||||
val = !!ucontrol->value.integer.value[0];
|
||||
change = (emu->spdif_enable != val);
|
||||
if (change) {
|
||||
emu->spdif_enable = val;
|
||||
if (val) {
|
||||
/* Digital */
|
||||
snd_ca0106_ptr_write(emu, SPDIF_SELECT1, 0, 0xf);
|
||||
snd_ca0106_ptr_write(emu, SPDIF_SELECT2, 0, 0x0b000000);
|
||||
snd_ca0106_ptr_write(emu, CAPTURE_CONTROL, 0,
|
||||
snd_ca0106_ptr_read(emu, CAPTURE_CONTROL, 0) & ~0x1000);
|
||||
mask = inl(emu->port + GPIO) & ~0x101;
|
||||
outl(mask, emu->port + GPIO);
|
||||
|
||||
} else {
|
||||
/* Analog */
|
||||
snd_ca0106_ptr_write(emu, SPDIF_SELECT1, 0, 0xf);
|
||||
snd_ca0106_ptr_write(emu, SPDIF_SELECT2, 0, 0x000f0000);
|
||||
snd_ca0106_ptr_write(emu, CAPTURE_CONTROL, 0,
|
||||
snd_ca0106_ptr_read(emu, CAPTURE_CONTROL, 0) | 0x1000);
|
||||
mask = inl(emu->port + GPIO) | 0x101;
|
||||
outl(mask, emu->port + GPIO);
|
||||
}
|
||||
ca0106_spdif_enable(emu);
|
||||
}
|
||||
return change;
|
||||
}
|
||||
|
@ -154,8 +214,6 @@ static int snd_ca0106_capture_source_put(struct snd_kcontrol *kcontrol,
|
|||
struct snd_ca0106 *emu = snd_kcontrol_chip(kcontrol);
|
||||
unsigned int val;
|
||||
int change = 0;
|
||||
u32 mask;
|
||||
u32 source;
|
||||
|
||||
val = ucontrol->value.enumerated.item[0] ;
|
||||
if (val >= 6)
|
||||
|
@ -163,9 +221,7 @@ static int snd_ca0106_capture_source_put(struct snd_kcontrol *kcontrol,
|
|||
change = (emu->capture_source != val);
|
||||
if (change) {
|
||||
emu->capture_source = val;
|
||||
source = (val << 28) | (val << 24) | (val << 20) | (val << 16);
|
||||
mask = snd_ca0106_ptr_read(emu, CAPTURE_SOURCE, 0) & 0xffff;
|
||||
snd_ca0106_ptr_write(emu, CAPTURE_SOURCE, 0, source | mask);
|
||||
ca0106_set_capture_source(emu);
|
||||
}
|
||||
return change;
|
||||
}
|
||||
|
@ -200,9 +256,7 @@ static int snd_ca0106_i2c_capture_source_put(struct snd_kcontrol *kcontrol,
|
|||
{
|
||||
struct snd_ca0106 *emu = snd_kcontrol_chip(kcontrol);
|
||||
unsigned int source_id;
|
||||
unsigned int ngain, ogain;
|
||||
int change = 0;
|
||||
u32 source;
|
||||
/* If the capture source has changed,
|
||||
* update the capture volume from the cached value
|
||||
* for the particular source.
|
||||
|
@ -212,18 +266,7 @@ static int snd_ca0106_i2c_capture_source_put(struct snd_kcontrol *kcontrol,
|
|||
return -EINVAL;
|
||||
change = (emu->i2c_capture_source != source_id);
|
||||
if (change) {
|
||||
snd_ca0106_i2c_write(emu, ADC_MUX, 0); /* Mute input */
|
||||
ngain = emu->i2c_capture_volume[source_id][0]; /* Left */
|
||||
ogain = emu->i2c_capture_volume[emu->i2c_capture_source][0]; /* Left */
|
||||
if (ngain != ogain)
|
||||
snd_ca0106_i2c_write(emu, ADC_ATTEN_ADCL, ((ngain) & 0xff));
|
||||
ngain = emu->i2c_capture_volume[source_id][1]; /* Left */
|
||||
ogain = emu->i2c_capture_volume[emu->i2c_capture_source][1]; /* Left */
|
||||
if (ngain != ogain)
|
||||
snd_ca0106_i2c_write(emu, ADC_ATTEN_ADCR, ((ngain) & 0xff));
|
||||
source = 1 << source_id;
|
||||
snd_ca0106_i2c_write(emu, ADC_MUX, source); /* Set source */
|
||||
emu->i2c_capture_source = source_id;
|
||||
ca0106_set_i2c_capture_source(emu, source_id, 0);
|
||||
}
|
||||
return change;
|
||||
}
|
||||
|
@ -271,7 +314,6 @@ static int snd_ca0106_capture_mic_line_in_put(struct snd_kcontrol *kcontrol,
|
|||
struct snd_ca0106 *emu = snd_kcontrol_chip(kcontrol);
|
||||
unsigned int val;
|
||||
int change = 0;
|
||||
u32 tmp;
|
||||
|
||||
val = ucontrol->value.enumerated.item[0] ;
|
||||
if (val > 1)
|
||||
|
@ -279,18 +321,7 @@ static int snd_ca0106_capture_mic_line_in_put(struct snd_kcontrol *kcontrol,
|
|||
change = (emu->capture_mic_line_in != val);
|
||||
if (change) {
|
||||
emu->capture_mic_line_in = val;
|
||||
if (val) {
|
||||
//snd_ca0106_i2c_write(emu, ADC_MUX, 0); /* Mute input */
|
||||
tmp = inl(emu->port+GPIO) & ~0x400;
|
||||
tmp = tmp | 0x400;
|
||||
outl(tmp, emu->port+GPIO);
|
||||
//snd_ca0106_i2c_write(emu, ADC_MUX, ADC_MUX_MIC);
|
||||
} else {
|
||||
//snd_ca0106_i2c_write(emu, ADC_MUX, 0); /* Mute input */
|
||||
tmp = inl(emu->port+GPIO) & ~0x400;
|
||||
outl(tmp, emu->port+GPIO);
|
||||
//snd_ca0106_i2c_write(emu, ADC_MUX, ADC_MUX_LINEIN);
|
||||
}
|
||||
ca0106_set_capture_mic_line_in(emu);
|
||||
}
|
||||
return change;
|
||||
}
|
||||
|
@ -322,16 +353,33 @@ static int snd_ca0106_spdif_info(struct snd_kcontrol *kcontrol,
|
|||
return 0;
|
||||
}
|
||||
|
||||
static int snd_ca0106_spdif_get(struct snd_kcontrol *kcontrol,
|
||||
static void decode_spdif_bits(unsigned char *status, unsigned int bits)
|
||||
{
|
||||
status[0] = (bits >> 0) & 0xff;
|
||||
status[1] = (bits >> 8) & 0xff;
|
||||
status[2] = (bits >> 16) & 0xff;
|
||||
status[3] = (bits >> 24) & 0xff;
|
||||
}
|
||||
|
||||
static int snd_ca0106_spdif_get_default(struct snd_kcontrol *kcontrol,
|
||||
struct snd_ctl_elem_value *ucontrol)
|
||||
{
|
||||
struct snd_ca0106 *emu = snd_kcontrol_chip(kcontrol);
|
||||
unsigned int idx = snd_ctl_get_ioffidx(kcontrol, &ucontrol->id);
|
||||
|
||||
ucontrol->value.iec958.status[0] = (emu->spdif_bits[idx] >> 0) & 0xff;
|
||||
ucontrol->value.iec958.status[1] = (emu->spdif_bits[idx] >> 8) & 0xff;
|
||||
ucontrol->value.iec958.status[2] = (emu->spdif_bits[idx] >> 16) & 0xff;
|
||||
ucontrol->value.iec958.status[3] = (emu->spdif_bits[idx] >> 24) & 0xff;
|
||||
decode_spdif_bits(ucontrol->value.iec958.status,
|
||||
emu->spdif_bits[idx]);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int snd_ca0106_spdif_get_stream(struct snd_kcontrol *kcontrol,
|
||||
struct snd_ctl_elem_value *ucontrol)
|
||||
{
|
||||
struct snd_ca0106 *emu = snd_kcontrol_chip(kcontrol);
|
||||
unsigned int idx = snd_ctl_get_ioffidx(kcontrol, &ucontrol->id);
|
||||
|
||||
decode_spdif_bits(ucontrol->value.iec958.status,
|
||||
emu->spdif_str_bits[idx]);
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
@ -345,24 +393,48 @@ static int snd_ca0106_spdif_get_mask(struct snd_kcontrol *kcontrol,
|
|||
return 0;
|
||||
}
|
||||
|
||||
static int snd_ca0106_spdif_put(struct snd_kcontrol *kcontrol,
|
||||
static unsigned int encode_spdif_bits(unsigned char *status)
|
||||
{
|
||||
return ((unsigned int)status[0] << 0) |
|
||||
((unsigned int)status[1] << 8) |
|
||||
((unsigned int)status[2] << 16) |
|
||||
((unsigned int)status[3] << 24);
|
||||
}
|
||||
|
||||
static int snd_ca0106_spdif_put_default(struct snd_kcontrol *kcontrol,
|
||||
struct snd_ctl_elem_value *ucontrol)
|
||||
{
|
||||
struct snd_ca0106 *emu = snd_kcontrol_chip(kcontrol);
|
||||
unsigned int idx = snd_ctl_get_ioffidx(kcontrol, &ucontrol->id);
|
||||
int change;
|
||||
unsigned int val;
|
||||
|
||||
val = (ucontrol->value.iec958.status[0] << 0) |
|
||||
(ucontrol->value.iec958.status[1] << 8) |
|
||||
(ucontrol->value.iec958.status[2] << 16) |
|
||||
(ucontrol->value.iec958.status[3] << 24);
|
||||
change = val != emu->spdif_bits[idx];
|
||||
if (change) {
|
||||
snd_ca0106_ptr_write(emu, SPCS0 + idx, 0, val);
|
||||
val = encode_spdif_bits(ucontrol->value.iec958.status);
|
||||
if (val != emu->spdif_bits[idx]) {
|
||||
emu->spdif_bits[idx] = val;
|
||||
/* FIXME: this isn't safe, but needed to keep the compatibility
|
||||
* with older alsa-lib config
|
||||
*/
|
||||
emu->spdif_str_bits[idx] = val;
|
||||
ca0106_set_spdif_bits(emu, idx);
|
||||
return 1;
|
||||
}
|
||||
return change;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int snd_ca0106_spdif_put_stream(struct snd_kcontrol *kcontrol,
|
||||
struct snd_ctl_elem_value *ucontrol)
|
||||
{
|
||||
struct snd_ca0106 *emu = snd_kcontrol_chip(kcontrol);
|
||||
unsigned int idx = snd_ctl_get_ioffidx(kcontrol, &ucontrol->id);
|
||||
unsigned int val;
|
||||
|
||||
val = encode_spdif_bits(ucontrol->value.iec958.status);
|
||||
if (val != emu->spdif_str_bits[idx]) {
|
||||
emu->spdif_str_bits[idx] = val;
|
||||
ca0106_set_spdif_bits(emu, idx);
|
||||
return 1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int snd_ca0106_volume_info(struct snd_kcontrol *kcontrol,
|
||||
|
@ -573,8 +645,16 @@ static struct snd_kcontrol_new snd_ca0106_volume_ctls[] __devinitdata = {
|
|||
.name = SNDRV_CTL_NAME_IEC958("",PLAYBACK,DEFAULT),
|
||||
.count = 4,
|
||||
.info = snd_ca0106_spdif_info,
|
||||
.get = snd_ca0106_spdif_get,
|
||||
.put = snd_ca0106_spdif_put
|
||||
.get = snd_ca0106_spdif_get_default,
|
||||
.put = snd_ca0106_spdif_put_default
|
||||
},
|
||||
{
|
||||
.iface = SNDRV_CTL_ELEM_IFACE_PCM,
|
||||
.name = SNDRV_CTL_NAME_IEC958("",PLAYBACK,PCM_STREAM),
|
||||
.count = 4,
|
||||
.info = snd_ca0106_spdif_info,
|
||||
.get = snd_ca0106_spdif_get_stream,
|
||||
.put = snd_ca0106_spdif_put_stream
|
||||
},
|
||||
};
|
||||
|
||||
|
@ -773,3 +853,50 @@ int __devinit snd_ca0106_mixer(struct snd_ca0106 *emu)
|
|||
return 0;
|
||||
}
|
||||
|
||||
#ifdef CONFIG_PM
|
||||
struct ca0106_vol_tbl {
|
||||
unsigned int channel_id;
|
||||
unsigned int reg;
|
||||
};
|
||||
|
||||
static struct ca0106_vol_tbl saved_volumes[NUM_SAVED_VOLUMES] = {
|
||||
{ CONTROL_FRONT_CHANNEL, PLAYBACK_VOLUME2 },
|
||||
{ CONTROL_REAR_CHANNEL, PLAYBACK_VOLUME2 },
|
||||
{ CONTROL_CENTER_LFE_CHANNEL, PLAYBACK_VOLUME2 },
|
||||
{ CONTROL_UNKNOWN_CHANNEL, PLAYBACK_VOLUME2 },
|
||||
{ CONTROL_FRONT_CHANNEL, PLAYBACK_VOLUME1 },
|
||||
{ CONTROL_REAR_CHANNEL, PLAYBACK_VOLUME1 },
|
||||
{ CONTROL_CENTER_LFE_CHANNEL, PLAYBACK_VOLUME1 },
|
||||
{ CONTROL_UNKNOWN_CHANNEL, PLAYBACK_VOLUME1 },
|
||||
{ 1, CAPTURE_CONTROL },
|
||||
};
|
||||
|
||||
void snd_ca0106_mixer_suspend(struct snd_ca0106 *chip)
|
||||
{
|
||||
int i;
|
||||
|
||||
/* save volumes */
|
||||
for (i = 0; i < NUM_SAVED_VOLUMES; i++)
|
||||
chip->saved_vol[i] =
|
||||
snd_ca0106_ptr_read(chip, saved_volumes[i].reg,
|
||||
saved_volumes[i].channel_id);
|
||||
}
|
||||
|
||||
void snd_ca0106_mixer_resume(struct snd_ca0106 *chip)
|
||||
{
|
||||
int i;
|
||||
|
||||
for (i = 0; i < NUM_SAVED_VOLUMES; i++)
|
||||
snd_ca0106_ptr_write(chip, saved_volumes[i].reg,
|
||||
saved_volumes[i].channel_id,
|
||||
chip->saved_vol[i]);
|
||||
|
||||
ca0106_spdif_enable(chip);
|
||||
ca0106_set_capture_source(chip);
|
||||
ca0106_set_i2c_capture_source(chip, chip->i2c_capture_source, 1);
|
||||
for (i = 0; i < 4; i++)
|
||||
ca0106_set_spdif_bits(chip, i);
|
||||
if (chip->details->i2c_adc)
|
||||
ca0106_set_capture_mic_line_in(chip);
|
||||
}
|
||||
#endif /* CONFIG_PM */
|
||||
|
|
|
@ -3640,7 +3640,10 @@ int snd_cs46xx_resume(struct pci_dev *pci)
|
|||
{
|
||||
struct snd_card *card = pci_get_drvdata(pci);
|
||||
struct snd_cs46xx *chip = card->private_data;
|
||||
int i, amp_saved;
|
||||
int amp_saved;
|
||||
#ifdef CONFIG_SND_CS46XX_NEW_DSP
|
||||
int i;
|
||||
#endif
|
||||
|
||||
pci_set_power_state(pci, PCI_D0);
|
||||
pci_restore_state(pci);
|
||||
|
|
|
@ -4,6 +4,9 @@
|
|||
|
||||
snd-cs5535audio-y := cs5535audio.o cs5535audio_pcm.o
|
||||
snd-cs5535audio-$(CONFIG_PM) += cs5535audio_pm.o
|
||||
ifdef CONFIG_MGEODE_LX
|
||||
snd-cs5535audio-$(CONFIG_OLPC) += cs5535audio_olpc.o
|
||||
endif
|
||||
|
||||
# Toplevel Module Dependency
|
||||
obj-$(CONFIG_SND_CS5535AUDIO) += snd-cs5535audio.o
|
||||
|
|
|
@ -159,10 +159,14 @@ static int __devinit snd_cs5535audio_mixer(struct cs5535audio *cs5535au)
|
|||
return err;
|
||||
|
||||
memset(&ac97, 0, sizeof(ac97));
|
||||
ac97.scaps = AC97_SCAP_AUDIO|AC97_SCAP_SKIP_MODEM;
|
||||
ac97.scaps = AC97_SCAP_AUDIO | AC97_SCAP_SKIP_MODEM
|
||||
| AC97_SCAP_POWER_SAVE;
|
||||
ac97.private_data = cs5535au;
|
||||
ac97.pci = cs5535au->pci;
|
||||
|
||||
/* set any OLPC-specific scaps */
|
||||
olpc_prequirks(card, &ac97);
|
||||
|
||||
if ((err = snd_ac97_mixer(pbus, &ac97, &cs5535au->ac97)) < 0) {
|
||||
snd_printk(KERN_ERR "mixer failed\n");
|
||||
return err;
|
||||
|
@ -170,6 +174,12 @@ static int __devinit snd_cs5535audio_mixer(struct cs5535audio *cs5535au)
|
|||
|
||||
snd_ac97_tune_hardware(cs5535au->ac97, ac97_quirks, ac97_quirk);
|
||||
|
||||
err = olpc_quirks(card, cs5535au->ac97);
|
||||
if (err < 0) {
|
||||
snd_printk(KERN_ERR "olpc quirks failed\n");
|
||||
return err;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
|
|
@ -78,6 +78,7 @@ struct cs5535audio_dma {
|
|||
unsigned int buf_addr, buf_bytes;
|
||||
unsigned int period_bytes, periods;
|
||||
u32 saved_prd;
|
||||
int pcm_open_flag;
|
||||
};
|
||||
|
||||
struct cs5535audio {
|
||||
|
@ -93,8 +94,46 @@ struct cs5535audio {
|
|||
struct cs5535audio_dma dmas[NUM_CS5535AUDIO_DMAS];
|
||||
};
|
||||
|
||||
#ifdef CONFIG_PM
|
||||
int snd_cs5535audio_suspend(struct pci_dev *pci, pm_message_t state);
|
||||
int snd_cs5535audio_resume(struct pci_dev *pci);
|
||||
#endif
|
||||
|
||||
#if defined(CONFIG_OLPC) && defined(CONFIG_MGEODE_LX)
|
||||
void __devinit olpc_prequirks(struct snd_card *card,
|
||||
struct snd_ac97_template *ac97);
|
||||
int __devinit olpc_quirks(struct snd_card *card, struct snd_ac97 *ac97);
|
||||
void olpc_analog_input(struct snd_ac97 *ac97, int on);
|
||||
void olpc_mic_bias(struct snd_ac97 *ac97, int on);
|
||||
|
||||
static inline void olpc_capture_open(struct snd_ac97 *ac97)
|
||||
{
|
||||
/* default to Analog Input off */
|
||||
olpc_analog_input(ac97, 0);
|
||||
/* enable MIC Bias for recording */
|
||||
olpc_mic_bias(ac97, 1);
|
||||
}
|
||||
|
||||
static inline void olpc_capture_close(struct snd_ac97 *ac97)
|
||||
{
|
||||
/* disable Analog Input */
|
||||
olpc_analog_input(ac97, 0);
|
||||
/* disable the MIC Bias (so the recording LED turns off) */
|
||||
olpc_mic_bias(ac97, 0);
|
||||
}
|
||||
#else
|
||||
static inline void olpc_prequirks(struct snd_card *card,
|
||||
struct snd_ac97_template *ac97) { }
|
||||
static inline int olpc_quirks(struct snd_card *card, struct snd_ac97 *ac97)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
static inline void olpc_analog_input(struct snd_ac97 *ac97, int on) { }
|
||||
static inline void olpc_mic_bias(struct snd_ac97 *ac97, int on) { }
|
||||
static inline void olpc_capture_open(struct snd_ac97 *ac97) { }
|
||||
static inline void olpc_capture_close(struct snd_ac97 *ac97) { }
|
||||
#endif
|
||||
|
||||
int __devinit snd_cs5535audio_pcm(struct cs5535audio *cs5535audio);
|
||||
|
||||
#endif /* __SOUND_CS5535AUDIO_H */
|
||||
|
|
|
@ -0,0 +1,179 @@
|
|||
/*
|
||||
* OLPC XO-1 additional sound features
|
||||
*
|
||||
* Copyright © 2006 Jaya Kumar <jayakumar.lkml@gmail.com>
|
||||
* Copyright © 2007-2008 Andres Salomon <dilinger@debian.org>
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
#include <sound/core.h>
|
||||
#include <sound/info.h>
|
||||
#include <sound/control.h>
|
||||
#include <sound/ac97_codec.h>
|
||||
|
||||
#include <asm/olpc.h>
|
||||
#include "cs5535audio.h"
|
||||
|
||||
/*
|
||||
* OLPC has an additional feature on top of the regular AD1888 codec features.
|
||||
* It has an Analog Input mode that is switched into (after disabling the
|
||||
* High Pass Filter) via GPIO. It is supported on B2 and later models.
|
||||
*/
|
||||
void olpc_analog_input(struct snd_ac97 *ac97, int on)
|
||||
{
|
||||
int err;
|
||||
|
||||
if (!machine_is_olpc())
|
||||
return;
|
||||
|
||||
/* update the High Pass Filter (via AC97_AD_TEST2) */
|
||||
err = snd_ac97_update_bits(ac97, AC97_AD_TEST2,
|
||||
1 << AC97_AD_HPFD_SHIFT, on << AC97_AD_HPFD_SHIFT);
|
||||
if (err < 0) {
|
||||
snd_printk(KERN_ERR "setting High Pass Filter - %d\n", err);
|
||||
return;
|
||||
}
|
||||
|
||||
/* set Analog Input through GPIO */
|
||||
if (on)
|
||||
geode_gpio_set(OLPC_GPIO_MIC_AC, GPIO_OUTPUT_VAL);
|
||||
else
|
||||
geode_gpio_clear(OLPC_GPIO_MIC_AC, GPIO_OUTPUT_VAL);
|
||||
}
|
||||
|
||||
/*
|
||||
* OLPC XO-1's V_REFOUT is a mic bias enable.
|
||||
*/
|
||||
void olpc_mic_bias(struct snd_ac97 *ac97, int on)
|
||||
{
|
||||
int err;
|
||||
|
||||
if (!machine_is_olpc())
|
||||
return;
|
||||
|
||||
on = on ? 0 : 1;
|
||||
err = snd_ac97_update_bits(ac97, AC97_AD_MISC,
|
||||
1 << AC97_AD_VREFD_SHIFT, on << AC97_AD_VREFD_SHIFT);
|
||||
if (err < 0)
|
||||
snd_printk(KERN_ERR "setting MIC Bias - %d\n", err);
|
||||
}
|
||||
|
||||
static int olpc_dc_info(struct snd_kcontrol *kctl,
|
||||
struct snd_ctl_elem_info *uinfo)
|
||||
{
|
||||
uinfo->type = SNDRV_CTL_ELEM_TYPE_BOOLEAN;
|
||||
uinfo->count = 1;
|
||||
uinfo->value.integer.min = 0;
|
||||
uinfo->value.integer.max = 1;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int olpc_dc_get(struct snd_kcontrol *kctl, struct snd_ctl_elem_value *v)
|
||||
{
|
||||
v->value.integer.value[0] = geode_gpio_isset(OLPC_GPIO_MIC_AC,
|
||||
GPIO_OUTPUT_VAL);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int olpc_dc_put(struct snd_kcontrol *kctl, struct snd_ctl_elem_value *v)
|
||||
{
|
||||
struct cs5535audio *cs5535au = snd_kcontrol_chip(kctl);
|
||||
|
||||
olpc_analog_input(cs5535au->ac97, v->value.integer.value[0]);
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int olpc_mic_info(struct snd_kcontrol *kctl,
|
||||
struct snd_ctl_elem_info *uinfo)
|
||||
{
|
||||
uinfo->type = SNDRV_CTL_ELEM_TYPE_BOOLEAN;
|
||||
uinfo->count = 1;
|
||||
uinfo->value.integer.min = 0;
|
||||
uinfo->value.integer.max = 1;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int olpc_mic_get(struct snd_kcontrol *kctl, struct snd_ctl_elem_value *v)
|
||||
{
|
||||
struct cs5535audio *cs5535au = snd_kcontrol_chip(kctl);
|
||||
struct snd_ac97 *ac97 = cs5535au->ac97;
|
||||
int i;
|
||||
|
||||
i = (snd_ac97_read(ac97, AC97_AD_MISC) >> AC97_AD_VREFD_SHIFT) & 0x1;
|
||||
v->value.integer.value[0] = i ? 0 : 1;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int olpc_mic_put(struct snd_kcontrol *kctl, struct snd_ctl_elem_value *v)
|
||||
{
|
||||
struct cs5535audio *cs5535au = snd_kcontrol_chip(kctl);
|
||||
|
||||
olpc_mic_bias(cs5535au->ac97, v->value.integer.value[0]);
|
||||
return 1;
|
||||
}
|
||||
|
||||
static struct snd_kcontrol_new olpc_cs5535audio_ctls[] __devinitdata = {
|
||||
{
|
||||
.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
|
||||
.name = "DC Mode Enable",
|
||||
.info = olpc_dc_info,
|
||||
.get = olpc_dc_get,
|
||||
.put = olpc_dc_put,
|
||||
.private_value = 0,
|
||||
},
|
||||
{
|
||||
.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
|
||||
.name = "MIC Bias Enable",
|
||||
.info = olpc_mic_info,
|
||||
.get = olpc_mic_get,
|
||||
.put = olpc_mic_put,
|
||||
.private_value = 0,
|
||||
},
|
||||
};
|
||||
|
||||
void __devinit olpc_prequirks(struct snd_card *card,
|
||||
struct snd_ac97_template *ac97)
|
||||
{
|
||||
if (!machine_is_olpc())
|
||||
return;
|
||||
|
||||
/* invert EAPD if on an OLPC B3 or higher */
|
||||
if (olpc_board_at_least(olpc_board_pre(0xb3)))
|
||||
ac97->scaps |= AC97_SCAP_INV_EAPD;
|
||||
}
|
||||
|
||||
int __devinit olpc_quirks(struct snd_card *card, struct snd_ac97 *ac97)
|
||||
{
|
||||
struct snd_ctl_elem_id elem;
|
||||
int i, err;
|
||||
|
||||
if (!machine_is_olpc())
|
||||
return 0;
|
||||
|
||||
/* drop the original AD1888 HPF control */
|
||||
memset(&elem, 0, sizeof(elem));
|
||||
elem.iface = SNDRV_CTL_ELEM_IFACE_MIXER;
|
||||
strncpy(elem.name, "High Pass Filter Enable", sizeof(elem.name));
|
||||
snd_ctl_remove_id(card, &elem);
|
||||
|
||||
/* drop the original V_REFOUT control */
|
||||
memset(&elem, 0, sizeof(elem));
|
||||
elem.iface = SNDRV_CTL_ELEM_IFACE_MIXER;
|
||||
strncpy(elem.name, "V_REFOUT Enable", sizeof(elem.name));
|
||||
snd_ctl_remove_id(card, &elem);
|
||||
|
||||
/* add the OLPC-specific controls */
|
||||
for (i = 0; i < ARRAY_SIZE(olpc_cs5535audio_ctls); i++) {
|
||||
err = snd_ctl_add(card, snd_ctl_new1(&olpc_cs5535audio_ctls[i],
|
||||
ac97->private_data));
|
||||
if (err < 0)
|
||||
return err;
|
||||
}
|
||||
|
||||
/* turn off the mic by default */
|
||||
olpc_mic_bias(ac97, 0);
|
||||
return 0;
|
||||
}
|
|
@ -260,6 +260,9 @@ static int snd_cs5535audio_hw_params(struct snd_pcm_substream *substream,
|
|||
err = cs5535audio_build_dma_packets(cs5535au, dma, substream,
|
||||
params_periods(hw_params),
|
||||
params_period_bytes(hw_params));
|
||||
if (!err)
|
||||
dma->pcm_open_flag = 1;
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
|
@ -268,6 +271,15 @@ static int snd_cs5535audio_hw_free(struct snd_pcm_substream *substream)
|
|||
struct cs5535audio *cs5535au = snd_pcm_substream_chip(substream);
|
||||
struct cs5535audio_dma *dma = substream->runtime->private_data;
|
||||
|
||||
if (dma->pcm_open_flag) {
|
||||
if (substream == cs5535au->playback_substream)
|
||||
snd_ac97_update_power(cs5535au->ac97,
|
||||
AC97_PCM_FRONT_DAC_RATE, 0);
|
||||
else
|
||||
snd_ac97_update_power(cs5535au->ac97,
|
||||
AC97_PCM_LR_ADC_RATE, 0);
|
||||
dma->pcm_open_flag = 0;
|
||||
}
|
||||
cs5535audio_clear_dma_packets(cs5535au, dma, substream);
|
||||
return snd_pcm_lib_free_pages(substream);
|
||||
}
|
||||
|
@ -351,11 +363,14 @@ static int snd_cs5535audio_capture_open(struct snd_pcm_substream *substream)
|
|||
if ((err = snd_pcm_hw_constraint_integer(runtime,
|
||||
SNDRV_PCM_HW_PARAM_PERIODS)) < 0)
|
||||
return err;
|
||||
olpc_capture_open(cs5535au->ac97);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int snd_cs5535audio_capture_close(struct snd_pcm_substream *substream)
|
||||
{
|
||||
struct cs5535audio *cs5535au = snd_pcm_substream_chip(substream);
|
||||
olpc_capture_close(cs5535au->ac97);
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
|
File diff suppressed because it is too large
Load Diff
|
@ -1639,6 +1639,45 @@ static struct snd_kcontrol_new snd_audigy_shared_spdif __devinitdata =
|
|||
.put = snd_emu10k1_shared_spdif_put
|
||||
};
|
||||
|
||||
/* workaround for too low volume on Audigy due to 16bit/24bit conversion */
|
||||
|
||||
#define snd_audigy_capture_boost_info snd_ctl_boolean_mono_info
|
||||
|
||||
static int snd_audigy_capture_boost_get(struct snd_kcontrol *kcontrol,
|
||||
struct snd_ctl_elem_value *ucontrol)
|
||||
{
|
||||
struct snd_emu10k1 *emu = snd_kcontrol_chip(kcontrol);
|
||||
unsigned int val;
|
||||
|
||||
/* FIXME: better to use a cached version */
|
||||
val = snd_ac97_read(emu->ac97, AC97_REC_GAIN);
|
||||
ucontrol->value.integer.value[0] = !!val;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int snd_audigy_capture_boost_put(struct snd_kcontrol *kcontrol,
|
||||
struct snd_ctl_elem_value *ucontrol)
|
||||
{
|
||||
struct snd_emu10k1 *emu = snd_kcontrol_chip(kcontrol);
|
||||
unsigned int val;
|
||||
|
||||
if (ucontrol->value.integer.value[0])
|
||||
val = 0x0f0f;
|
||||
else
|
||||
val = 0;
|
||||
return snd_ac97_update(emu->ac97, AC97_REC_GAIN, val);
|
||||
}
|
||||
|
||||
static struct snd_kcontrol_new snd_audigy_capture_boost __devinitdata =
|
||||
{
|
||||
.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
|
||||
.name = "Analog Capture Boost",
|
||||
.info = snd_audigy_capture_boost_info,
|
||||
.get = snd_audigy_capture_boost_get,
|
||||
.put = snd_audigy_capture_boost_put
|
||||
};
|
||||
|
||||
|
||||
/*
|
||||
*/
|
||||
static void snd_emu10k1_mixer_free_ac97(struct snd_ac97 *ac97)
|
||||
|
@ -2087,5 +2126,12 @@ int __devinit snd_emu10k1_mixer(struct snd_emu10k1 *emu,
|
|||
}
|
||||
}
|
||||
|
||||
if (emu->card_capabilities->ac97_chip && emu->audigy) {
|
||||
err = snd_ctl_add(card, snd_ctl_new1(&snd_audigy_capture_boost,
|
||||
emu));
|
||||
if (err < 0)
|
||||
return err;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
|
|
@ -1953,7 +1953,7 @@ static irqreturn_t snd_es1968_interrupt(int irq, void *dev_id)
|
|||
outw(inw(chip->io_port + 4) & 1, chip->io_port + 4);
|
||||
|
||||
if (event & ESM_HWVOL_IRQ)
|
||||
tasklet_hi_schedule(&chip->hwvol_tq); /* we'll do this later */
|
||||
tasklet_schedule(&chip->hwvol_tq); /* we'll do this later */
|
||||
|
||||
/* else ack 'em all, i imagine */
|
||||
outb(0xFF, chip->io_port + 0x1A);
|
||||
|
|
|
@ -0,0 +1,188 @@
|
|||
menuconfig SND_HDA_INTEL
|
||||
tristate "Intel HD Audio"
|
||||
select SND_PCM
|
||||
select SND_VMASTER
|
||||
select SND_JACK if INPUT=y || INPUT=SND
|
||||
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.
|
||||
|
||||
if SND_HDA_INTEL
|
||||
|
||||
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 (EXPERIMENTAL)"
|
||||
depends on SND_HDA_HWDEP && EXPERIMENTAL
|
||||
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_INTEL
|
||||
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_CODEC_REALTEK
|
||||
bool "Build Realtek HD-audio codec support"
|
||||
default y
|
||||
help
|
||||
Say Y here to include Realtek HD-audio codec support in
|
||||
snd-hda-intel driver, such as ALC880.
|
||||
|
||||
When the HD-audio driver is built as a module, the codec
|
||||
support code is also built as another module,
|
||||
snd-hda-codec-realtek.
|
||||
This module is automatically loaded at probing.
|
||||
|
||||
config SND_HDA_CODEC_ANALOG
|
||||
bool "Build Analog Device HD-audio codec support"
|
||||
default y
|
||||
help
|
||||
Say Y here to include Analog Device HD-audio codec support in
|
||||
snd-hda-intel driver, such as AD1986A.
|
||||
|
||||
When the HD-audio driver is built as a module, the codec
|
||||
support code is also built as another module,
|
||||
snd-hda-codec-analog.
|
||||
This module is automatically loaded at probing.
|
||||
|
||||
config SND_HDA_CODEC_SIGMATEL
|
||||
bool "Build IDT/Sigmatel HD-audio codec support"
|
||||
default y
|
||||
help
|
||||
Say Y here to include IDT (Sigmatel) HD-audio codec support in
|
||||
snd-hda-intel driver, such as STAC9200.
|
||||
|
||||
When the HD-audio driver is built as a module, the codec
|
||||
support code is also built as another module,
|
||||
snd-hda-codec-idt.
|
||||
This module is automatically loaded at probing.
|
||||
|
||||
config SND_HDA_CODEC_VIA
|
||||
bool "Build VIA HD-audio codec support"
|
||||
default y
|
||||
help
|
||||
Say Y here to include VIA HD-audio codec support in
|
||||
snd-hda-intel driver, such as VT1708.
|
||||
|
||||
When the HD-audio driver is built as a module, the codec
|
||||
support code is also built as another module,
|
||||
snd-hda-codec-via.
|
||||
This module is automatically loaded at probing.
|
||||
|
||||
config SND_HDA_CODEC_ATIHDMI
|
||||
bool "Build ATI HDMI HD-audio codec support"
|
||||
default y
|
||||
help
|
||||
Say Y here to include ATI HDMI HD-audio codec support in
|
||||
snd-hda-intel driver, such as ATI RS600 HDMI.
|
||||
|
||||
When the HD-audio driver is built as a module, the codec
|
||||
support code is also built as another module,
|
||||
snd-hda-codec-atihdmi.
|
||||
This module is automatically loaded at probing.
|
||||
|
||||
config SND_HDA_CODEC_NVHDMI
|
||||
bool "Build NVIDIA HDMI HD-audio codec support"
|
||||
default y
|
||||
help
|
||||
Say Y here to include NVIDIA HDMI HD-audio codec support in
|
||||
snd-hda-intel driver, such as NVIDIA MCP78 HDMI.
|
||||
|
||||
When the HD-audio driver is built as a module, the codec
|
||||
support code is also built as another module,
|
||||
snd-hda-codec-nvhdmi.
|
||||
This module is automatically loaded at probing.
|
||||
|
||||
config SND_HDA_CODEC_INTELHDMI
|
||||
bool "Build INTEL HDMI HD-audio codec support"
|
||||
default y
|
||||
help
|
||||
Say Y here to include INTEL HDMI HD-audio codec support in
|
||||
snd-hda-intel driver, such as Eaglelake integrated HDMI.
|
||||
|
||||
When the HD-audio driver is built as a module, the codec
|
||||
support code is also built as another module,
|
||||
snd-hda-codec-intelhdmi.
|
||||
This module is automatically loaded at probing.
|
||||
|
||||
config SND_HDA_ELD
|
||||
def_bool y
|
||||
depends on SND_HDA_CODEC_INTELHDMI
|
||||
|
||||
config SND_HDA_CODEC_CONEXANT
|
||||
bool "Build Conexant HD-audio codec support"
|
||||
default y
|
||||
help
|
||||
Say Y here to include Conexant HD-audio codec support in
|
||||
snd-hda-intel driver, such as CX20549.
|
||||
|
||||
When the HD-audio driver is built as a module, the codec
|
||||
support code is also built as another module,
|
||||
snd-hda-codec-conexant.
|
||||
This module is automatically loaded at probing.
|
||||
|
||||
config SND_HDA_CODEC_CMEDIA
|
||||
bool "Build C-Media HD-audio codec support"
|
||||
default y
|
||||
help
|
||||
Say Y here to include C-Media HD-audio codec support in
|
||||
snd-hda-intel driver, such as CMI9880.
|
||||
|
||||
When the HD-audio driver is built as a module, the codec
|
||||
support code is also built as another module,
|
||||
snd-hda-codec-cmedia.
|
||||
This module is automatically loaded at probing.
|
||||
|
||||
config SND_HDA_CODEC_SI3054
|
||||
bool "Build Silicon Labs 3054 HD-modem codec support"
|
||||
default y
|
||||
help
|
||||
Say Y here to include Silicon Labs 3054 HD-modem codec
|
||||
(and compatibles) support in snd-hda-intel driver.
|
||||
|
||||
When the HD-audio driver is built as a module, the codec
|
||||
support code is also built as another module,
|
||||
snd-hda-codec-si3054.
|
||||
This module is automatically loaded at probing.
|
||||
|
||||
config SND_HDA_GENERIC
|
||||
bool "Enable generic HD-audio codec parser"
|
||||
default y
|
||||
help
|
||||
Say Y here to enable the generic HD-audio codec parser
|
||||
in snd-hda-intel driver.
|
||||
|
||||
config SND_HDA_POWER_SAVE
|
||||
bool "Aggressive power-saving on HD-audio"
|
||||
help
|
||||
Say Y here to enable more aggressive power-saving mode on
|
||||
HD-audio driver. The power-saving timeout can be configured
|
||||
via power_save option or over sysfs on-the-fly.
|
||||
|
||||
config SND_HDA_POWER_SAVE_DEFAULT
|
||||
int "Default time-out for HD-audio power-save mode"
|
||||
depends on SND_HDA_POWER_SAVE
|
||||
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
|
|
@ -1,20 +1,59 @@
|
|||
snd-hda-intel-y := hda_intel.o
|
||||
# since snd-hda-intel is the only driver using hda-codec,
|
||||
# merge it into a single module although it was originally
|
||||
# designed to be individual modules
|
||||
snd-hda-intel-y += hda_codec.o
|
||||
snd-hda-intel-$(CONFIG_PROC_FS) += hda_proc.o
|
||||
snd-hda-intel-$(CONFIG_SND_HDA_HWDEP) += hda_hwdep.o
|
||||
snd-hda-intel-$(CONFIG_SND_HDA_INPUT_BEEP) += hda_beep.o
|
||||
snd-hda-intel-$(CONFIG_SND_HDA_GENERIC) += hda_generic.o
|
||||
snd-hda-intel-$(CONFIG_SND_HDA_CODEC_REALTEK) += patch_realtek.o
|
||||
snd-hda-intel-$(CONFIG_SND_HDA_CODEC_CMEDIA) += patch_cmedia.o
|
||||
snd-hda-intel-$(CONFIG_SND_HDA_CODEC_ANALOG) += patch_analog.o
|
||||
snd-hda-intel-$(CONFIG_SND_HDA_CODEC_SIGMATEL) += patch_sigmatel.o
|
||||
snd-hda-intel-$(CONFIG_SND_HDA_CODEC_SI3054) += patch_si3054.o
|
||||
snd-hda-intel-$(CONFIG_SND_HDA_CODEC_ATIHDMI) += patch_atihdmi.o
|
||||
snd-hda-intel-$(CONFIG_SND_HDA_CODEC_CONEXANT) += patch_conexant.o
|
||||
snd-hda-intel-$(CONFIG_SND_HDA_CODEC_VIA) += patch_via.o
|
||||
snd-hda-intel-$(CONFIG_SND_HDA_CODEC_NVHDMI) += patch_nvhdmi.o
|
||||
snd-hda-intel-objs := hda_intel.o
|
||||
|
||||
snd-hda-codec-y := hda_codec.o
|
||||
snd-hda-codec-$(CONFIG_SND_HDA_GENERIC) += hda_generic.o
|
||||
snd-hda-codec-$(CONFIG_PROC_FS) += hda_proc.o
|
||||
# snd-hda-codec-$(CONFIG_SND_HDA_ELD) += hda_eld.o
|
||||
snd-hda-codec-$(CONFIG_SND_HDA_HWDEP) += hda_hwdep.o
|
||||
snd-hda-codec-$(CONFIG_SND_HDA_INPUT_BEEP) += hda_beep.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-atihdmi-objs := patch_atihdmi.o
|
||||
snd-hda-codec-conexant-objs := patch_conexant.o
|
||||
snd-hda-codec-via-objs := patch_via.o
|
||||
snd-hda-codec-nvhdmi-objs := patch_nvhdmi.o
|
||||
snd-hda-codec-intelhdmi-objs := patch_intelhdmi.o hda_eld.o
|
||||
|
||||
# common driver
|
||||
obj-$(CONFIG_SND_HDA_INTEL) := snd-hda-codec.o
|
||||
|
||||
# codec drivers (note: CONFIG_SND_HDA_CODEC_XXX are booleans)
|
||||
ifdef CONFIG_SND_HDA_CODEC_REALTEK
|
||||
obj-$(CONFIG_SND_HDA_INTEL) += snd-hda-codec-realtek.o
|
||||
endif
|
||||
ifdef CONFIG_SND_HDA_CODEC_CMEDIA
|
||||
obj-$(CONFIG_SND_HDA_INTEL) += snd-hda-codec-cmedia.o
|
||||
endif
|
||||
ifdef CONFIG_SND_HDA_CODEC_ANALOG
|
||||
obj-$(CONFIG_SND_HDA_INTEL) += snd-hda-codec-analog.o
|
||||
endif
|
||||
ifdef CONFIG_SND_HDA_CODEC_SIGMATEL
|
||||
obj-$(CONFIG_SND_HDA_INTEL) += snd-hda-codec-idt.o
|
||||
endif
|
||||
ifdef CONFIG_SND_HDA_CODEC_SI3054
|
||||
obj-$(CONFIG_SND_HDA_INTEL) += snd-hda-codec-si3054.o
|
||||
endif
|
||||
ifdef CONFIG_SND_HDA_CODEC_ATIHDMI
|
||||
obj-$(CONFIG_SND_HDA_INTEL) += snd-hda-codec-atihdmi.o
|
||||
endif
|
||||
ifdef CONFIG_SND_HDA_CODEC_CONEXANT
|
||||
obj-$(CONFIG_SND_HDA_INTEL) += snd-hda-codec-conexant.o
|
||||
endif
|
||||
ifdef CONFIG_SND_HDA_CODEC_VIA
|
||||
obj-$(CONFIG_SND_HDA_INTEL) += snd-hda-codec-via.o
|
||||
endif
|
||||
ifdef CONFIG_SND_HDA_CODEC_NVHDMI
|
||||
obj-$(CONFIG_SND_HDA_INTEL) += snd-hda-codec-nvhdmi.o
|
||||
endif
|
||||
ifdef CONFIG_SND_HDA_CODEC_INTELHDMI
|
||||
obj-$(CONFIG_SND_HDA_INTEL) += snd-hda-codec-intelhdmi.o
|
||||
endif
|
||||
|
||||
# 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
|
||||
|
|
|
@ -128,6 +128,7 @@ int snd_hda_attach_beep_device(struct hda_codec *codec, int nid)
|
|||
INIT_WORK(&beep->beep_work, &snd_hda_generate_beep);
|
||||
return 0;
|
||||
}
|
||||
EXPORT_SYMBOL_HDA(snd_hda_attach_beep_device);
|
||||
|
||||
void snd_hda_detach_beep_device(struct hda_codec *codec)
|
||||
{
|
||||
|
@ -140,3 +141,4 @@ void snd_hda_detach_beep_device(struct hda_codec *codec)
|
|||
kfree(beep);
|
||||
}
|
||||
}
|
||||
EXPORT_SYMBOL_HDA(snd_hda_detach_beep_device);
|
||||
|
|
File diff suppressed because it is too large
Load Diff
|
@ -519,6 +519,36 @@ enum {
|
|||
/* max. codec address */
|
||||
#define HDA_MAX_CODEC_ADDRESS 0x0f
|
||||
|
||||
/*
|
||||
* 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
|
||||
*/
|
||||
|
@ -536,15 +566,17 @@ typedef u16 hda_nid_t;
|
|||
/* bus operators */
|
||||
struct hda_bus_ops {
|
||||
/* send a single command */
|
||||
int (*command)(struct hda_codec *codec, hda_nid_t nid, int direct,
|
||||
unsigned int verb, unsigned int parm);
|
||||
int (*command)(struct hda_bus *bus, unsigned int cmd);
|
||||
/* get a response from the last command */
|
||||
unsigned int (*get_response)(struct hda_codec *codec);
|
||||
unsigned int (*get_response)(struct hda_bus *bus);
|
||||
/* 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);
|
||||
#ifdef CONFIG_SND_HDA_POWER_SAVE
|
||||
/* notify power-up/down from codec to controller */
|
||||
void (*pm_notify)(struct hda_codec *codec);
|
||||
void (*pm_notify)(struct hda_bus *bus);
|
||||
#endif
|
||||
};
|
||||
|
||||
|
@ -553,6 +585,7 @@ struct hda_bus_template {
|
|||
void *private_data;
|
||||
struct pci_dev *pci;
|
||||
const char *modelname;
|
||||
int *power_save;
|
||||
struct hda_bus_ops ops;
|
||||
};
|
||||
|
||||
|
@ -569,6 +602,7 @@ struct hda_bus {
|
|||
void *private_data;
|
||||
struct pci_dev *pci;
|
||||
const char *modelname;
|
||||
int *power_save;
|
||||
struct hda_bus_ops ops;
|
||||
|
||||
/* codec linked list */
|
||||
|
@ -581,10 +615,12 @@ struct hda_bus {
|
|||
/* unsolicited event queue */
|
||||
struct hda_bus_unsolicited *unsol;
|
||||
|
||||
struct snd_info_entry *proc;
|
||||
/* assigned PCMs */
|
||||
DECLARE_BITMAP(pcm_dev_bits, SNDRV_PCM_DEVICES);
|
||||
|
||||
/* misc op flags */
|
||||
unsigned int needs_damn_long_delay :1;
|
||||
unsigned int shutdown :1; /* being unloaded */
|
||||
};
|
||||
|
||||
/*
|
||||
|
@ -604,6 +640,16 @@ struct hda_codec_preset {
|
|||
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);
|
||||
|
@ -635,10 +681,7 @@ struct hda_amp_info {
|
|||
|
||||
struct hda_cache_rec {
|
||||
u16 hash[64]; /* hash table for index */
|
||||
unsigned int num_entries; /* number of assigned entries */
|
||||
unsigned int size; /* allocated size */
|
||||
unsigned int record_size; /* record size (including header) */
|
||||
void *buffer; /* hash table entries */
|
||||
struct snd_array buf; /* record entries */
|
||||
};
|
||||
|
||||
/* PCM callbacks */
|
||||
|
@ -680,7 +723,8 @@ struct hda_pcm {
|
|||
char *name;
|
||||
struct hda_pcm_stream stream[2];
|
||||
unsigned int pcm_type; /* HDA_PCM_TYPE_XXX */
|
||||
int device; /* assigned device number */
|
||||
int device; /* device number to assign */
|
||||
struct snd_pcm *pcm; /* assigned PCM instance */
|
||||
};
|
||||
|
||||
/* codec information */
|
||||
|
@ -699,6 +743,9 @@ struct hda_codec {
|
|||
|
||||
/* detected preset */
|
||||
const struct hda_codec_preset *preset;
|
||||
struct module *owner;
|
||||
const char *name; /* codec name */
|
||||
const char *modelname; /* model name for preset */
|
||||
|
||||
/* set by patch */
|
||||
struct hda_codec_ops patch_ops;
|
||||
|
@ -718,6 +765,8 @@ struct hda_codec {
|
|||
hda_nid_t start_nid;
|
||||
u32 *wcaps;
|
||||
|
||||
struct snd_array mixers; /* list of assigned mixer elements */
|
||||
|
||||
struct hda_cache_rec amp_cache; /* cache for amp access */
|
||||
struct hda_cache_rec cmd_cache; /* cache for other commands */
|
||||
|
||||
|
@ -727,7 +776,11 @@ struct hda_codec {
|
|||
unsigned int spdif_in_enable; /* SPDIF input enable? */
|
||||
hda_nid_t *slave_dig_outs; /* optional digital out slave widgets */
|
||||
|
||||
#ifdef CONFIG_SND_HDA_HWDEP
|
||||
struct snd_hwdep *hwdep; /* assigned hwdep device */
|
||||
struct snd_array init_verbs; /* additional init verbs */
|
||||
struct snd_array hints; /* additional hints */
|
||||
#endif
|
||||
|
||||
/* misc flags */
|
||||
unsigned int spdif_status_reset :1; /* needs to toggle SPDIF for each
|
||||
|
@ -740,6 +793,10 @@ struct hda_codec {
|
|||
int power_count; /* current (global) power refcount */
|
||||
struct delayed_work power_work; /* delayed task for powerdown */
|
||||
#endif
|
||||
|
||||
/* codec-specific additional proc output */
|
||||
void (*proc_widget_hook)(struct snd_info_buffer *buffer,
|
||||
struct hda_codec *codec, hda_nid_t nid);
|
||||
};
|
||||
|
||||
/* direction */
|
||||
|
@ -754,7 +811,7 @@ enum {
|
|||
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 do_init, struct hda_codec **codecp);
|
||||
|
||||
/*
|
||||
* low level functions
|
||||
|
@ -799,11 +856,13 @@ void snd_hda_codec_resume_cache(struct hda_codec *codec);
|
|||
* 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);
|
||||
void snd_hda_codec_setup_stream(struct hda_codec *codec, hda_nid_t nid,
|
||||
u32 stream_tag,
|
||||
int channel_id, int format);
|
||||
|
@ -812,8 +871,6 @@ unsigned int snd_hda_calc_stream_format(unsigned int rate,
|
|||
unsigned int channels,
|
||||
unsigned int format,
|
||||
unsigned int maxbps);
|
||||
int snd_hda_query_supported_pcm(struct hda_codec *codec, hda_nid_t nid,
|
||||
u32 *ratesp, u64 *formatsp, unsigned int *bpsp);
|
||||
int snd_hda_is_supported_format(struct hda_codec *codec, hda_nid_t nid,
|
||||
unsigned int format);
|
||||
|
||||
|
@ -830,6 +887,13 @@ int snd_hda_suspend(struct hda_bus *bus, pm_message_t state);
|
|||
int snd_hda_resume(struct hda_bus *bus);
|
||||
#endif
|
||||
|
||||
/*
|
||||
* 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
|
||||
*/
|
||||
|
@ -837,12 +901,25 @@ int snd_hda_resume(struct hda_bus *bus);
|
|||
void snd_hda_power_up(struct hda_codec *codec);
|
||||
void snd_hda_power_down(struct hda_codec *codec);
|
||||
#define snd_hda_codec_needs_resume(codec) codec->power_count
|
||||
int snd_hda_codecs_inuse(struct hda_bus *bus);
|
||||
#else
|
||||
static inline void snd_hda_power_up(struct hda_codec *codec) {}
|
||||
static inline void snd_hda_power_down(struct hda_codec *codec) {}
|
||||
#define snd_hda_codec_needs_resume(codec) 1
|
||||
#define snd_hda_codecs_inuse(bus) 1
|
||||
#endif
|
||||
|
||||
/*
|
||||
* Codec modularization
|
||||
*/
|
||||
|
||||
/* Export symbols only for communication with codec drivers;
|
||||
* When built in kernel, all HD-audio drivers are supposed to be statically
|
||||
* linked to the kernel. Thus, the symbols don't have to (or shouldn't) be
|
||||
* exported unless it's built as a module.
|
||||
*/
|
||||
#ifdef MODULE
|
||||
#define EXPORT_SYMBOL_HDA(sym) EXPORT_SYMBOL_GPL(sym)
|
||||
#else
|
||||
#define EXPORT_SYMBOL_HDA(sym)
|
||||
#endif
|
||||
|
||||
#endif /* __SOUND_HDA_CODEC_H */
|
||||
|
|
|
@ -0,0 +1,590 @@
|
|||
/*
|
||||
* Generic routines and proc interface for ELD(EDID Like Data) information
|
||||
*
|
||||
* Copyright(c) 2008 Intel Corporation.
|
||||
*
|
||||
* 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 <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 char hdmi_get_eld_byte(struct hda_codec *codec, hda_nid_t nid,
|
||||
int byte_index)
|
||||
{
|
||||
unsigned int val;
|
||||
|
||||
val = snd_hda_codec_read(codec, nid, 0,
|
||||
AC_VERB_GET_HDMI_ELDD, byte_index);
|
||||
|
||||
#ifdef BE_PARANOID
|
||||
printk(KERN_INFO "HDMI: ELD data byte %d: 0x%x\n", byte_index, val);
|
||||
#endif
|
||||
|
||||
if ((val & AC_ELDD_ELD_VALID) == 0) {
|
||||
snd_printd(KERN_INFO "HDMI: invalid ELD data byte %d\n",
|
||||
byte_index);
|
||||
val = 0;
|
||||
}
|
||||
|
||||
return val & AC_ELDD_ELD_DATA;
|
||||
}
|
||||
|
||||
#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 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->format = GRAB_BITS(buf, 0, 3, 4);
|
||||
switch (a->format) {
|
||||
case AUDIO_CODING_TYPE_REF_STREAM_HEADER:
|
||||
snd_printd(KERN_INFO
|
||||
"HDMI: audio coding type 0 not expected\n");
|
||||
break;
|
||||
|
||||
case AUDIO_CODING_TYPE_LPCM:
|
||||
val = GRAB_BITS(buf, 2, 0, 3);
|
||||
a->sample_bits = 0;
|
||||
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) {
|
||||
snd_printd(KERN_INFO
|
||||
"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!
|
||||
*/
|
||||
static int hdmi_update_eld(struct 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) {
|
||||
snd_printd(KERN_INFO "HDMI: Unknown ELD version %d\n",
|
||||
e->eld_ver);
|
||||
goto out_fail;
|
||||
}
|
||||
|
||||
e->eld_size = size;
|
||||
e->baseline_len = GRAB_BITS(buf, 2, 0, 8);
|
||||
mnl = GRAB_BITS(buf, 4, 0, 5);
|
||||
e->cea_edid_ver = GRAB_BITS(buf, 4, 5, 3);
|
||||
|
||||
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) {
|
||||
snd_printd(KERN_INFO "HDMI: MNL is reserved value %d\n", mnl);
|
||||
goto out_fail;
|
||||
} else if (ELD_FIXED_BYTES + mnl > size) {
|
||||
snd_printd(KERN_INFO "HDMI: out of range MNL %d\n", mnl);
|
||||
goto out_fail;
|
||||
} else
|
||||
strlcpy(e->monitor_name, buf + ELD_FIXED_BYTES, mnl);
|
||||
|
||||
for (i = 0; i < e->sad_count; i++) {
|
||||
if (ELD_FIXED_BYTES + mnl + 3 * (i + 1) > size) {
|
||||
snd_printd(KERN_INFO "HDMI: out of range SAD %d\n", i);
|
||||
goto out_fail;
|
||||
}
|
||||
hdmi_update_short_audio_desc(e->sad + i,
|
||||
buf + ELD_FIXED_BYTES + mnl + 3 * i);
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
||||
out_fail:
|
||||
e->eld_ver = 0;
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
static int hdmi_present_sense(struct hda_codec *codec, hda_nid_t nid)
|
||||
{
|
||||
return snd_hda_codec_read(codec, nid, 0, AC_VERB_GET_PIN_SENSE, 0);
|
||||
}
|
||||
|
||||
static int hdmi_eld_valid(struct hda_codec *codec, hda_nid_t nid)
|
||||
{
|
||||
int eldv;
|
||||
int present;
|
||||
|
||||
present = hdmi_present_sense(codec, nid);
|
||||
eldv = (present & AC_PINSENSE_ELDV);
|
||||
present = (present & AC_PINSENSE_PRESENCE);
|
||||
|
||||
#ifdef CONFIG_SND_DEBUG_VERBOSE
|
||||
printk(KERN_INFO "HDMI: sink_present = %d, eld_valid = %d\n",
|
||||
!!present, !!eldv);
|
||||
#endif
|
||||
|
||||
return eldv && present;
|
||||
}
|
||||
|
||||
int snd_hdmi_get_eld_size(struct hda_codec *codec, hda_nid_t nid)
|
||||
{
|
||||
return snd_hda_codec_read(codec, nid, 0, AC_VERB_GET_HDMI_DIP_SIZE,
|
||||
AC_DIPSIZE_ELD_BUF);
|
||||
}
|
||||
|
||||
int snd_hdmi_get_eld(struct hdmi_eld *eld,
|
||||
struct hda_codec *codec, hda_nid_t nid)
|
||||
{
|
||||
int i;
|
||||
int ret;
|
||||
int size;
|
||||
unsigned char *buf;
|
||||
|
||||
if (!hdmi_eld_valid(codec, nid))
|
||||
return -ENOENT;
|
||||
|
||||
size = snd_hdmi_get_eld_size(codec, nid);
|
||||
if (size == 0) {
|
||||
/* wfg: workaround for ASUS P5E-VM HDMI board */
|
||||
snd_printd(KERN_INFO "HDMI: ELD buf size is 0, force 128\n");
|
||||
size = 128;
|
||||
}
|
||||
if (size < ELD_FIXED_BYTES || size > PAGE_SIZE) {
|
||||
snd_printd(KERN_INFO "HDMI: invalid ELD buf size %d\n", size);
|
||||
return -ERANGE;
|
||||
}
|
||||
|
||||
buf = kmalloc(size, GFP_KERNEL);
|
||||
if (!buf)
|
||||
return -ENOMEM;
|
||||
|
||||
for (i = 0; i < size; i++)
|
||||
buf[i] = hdmi_get_eld_byte(codec, nid, i);
|
||||
|
||||
ret = hdmi_update_eld(eld, buf, size);
|
||||
|
||||
kfree(buf);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void hdmi_show_short_audio_desc(struct cea_sad *a)
|
||||
{
|
||||
char buf[SND_PRINT_RATES_ADVISED_BUFSIZE];
|
||||
char buf2[8 + SND_PRINT_BITS_ADVISED_BUFSIZE] = ", bits =";
|
||||
|
||||
if (!a->format)
|
||||
return;
|
||||
|
||||
snd_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';
|
||||
|
||||
printk(KERN_INFO "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 hdmi_eld *e)
|
||||
{
|
||||
int i;
|
||||
|
||||
printk(KERN_INFO "HDMI: detected monitor %s at connection type %s\n",
|
||||
e->monitor_name,
|
||||
eld_connection_type_names[e->conn_type]);
|
||||
|
||||
if (e->spk_alloc) {
|
||||
char buf[SND_PRINT_CHANNEL_ALLOCATION_ADVISED_BUFSIZE];
|
||||
snd_print_channel_allocation(e->spk_alloc, buf, sizeof(buf));
|
||||
printk(KERN_INFO "HDMI: available speakers:%s\n", buf);
|
||||
}
|
||||
|
||||
for (i = 0; i < e->sad_count; i++)
|
||||
hdmi_show_short_audio_desc(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);
|
||||
|
||||
snd_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);
|
||||
}
|
||||
|
||||
static void hdmi_print_eld_info(struct snd_info_entry *entry,
|
||||
struct snd_info_buffer *buffer)
|
||||
{
|
||||
struct hdmi_eld *e = entry->private_data;
|
||||
char buf[SND_PRINT_CHANNEL_ALLOCATION_ADVISED_BUFSIZE];
|
||||
int i;
|
||||
static char *eld_versoin_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_name\t\t%s\n", e->monitor_name);
|
||||
snd_iprintf(buffer, "connection_type\t\t%s\n",
|
||||
eld_connection_type_names[e->conn_type]);
|
||||
snd_iprintf(buffer, "eld_version\t\t[0x%x] %s\n", e->eld_ver,
|
||||
eld_versoin_names[e->eld_ver]);
|
||||
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);
|
||||
}
|
||||
|
||||
static void hdmi_write_eld_info(struct snd_info_entry *entry,
|
||||
struct snd_info_buffer *buffer)
|
||||
{
|
||||
struct hdmi_eld *e = entry->private_data;
|
||||
char line[64];
|
||||
char name[64];
|
||||
char *sname;
|
||||
long long val;
|
||||
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, "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 < 0 || n > 31) /* double the CEA limit */
|
||||
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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
int snd_hda_eld_proc_new(struct hda_codec *codec, struct hdmi_eld *eld)
|
||||
{
|
||||
char name[32];
|
||||
struct snd_info_entry *entry;
|
||||
int err;
|
||||
|
||||
snprintf(name, sizeof(name), "eld#%d", codec->addr);
|
||||
err = snd_card_proc_new(codec->bus->card, name, &entry);
|
||||
if (err < 0)
|
||||
return err;
|
||||
|
||||
snd_info_set_text_ops(entry, eld, hdmi_print_eld_info);
|
||||
entry->c.text.write = hdmi_write_eld_info;
|
||||
entry->mode |= S_IWUSR;
|
||||
eld->proc_entry = entry;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void snd_hda_eld_proc_free(struct hda_codec *codec, struct hdmi_eld *eld)
|
||||
{
|
||||
if (!codec->bus->shutdown && eld->proc_entry) {
|
||||
snd_device_free(codec->bus->card, eld->proc_entry);
|
||||
eld->proc_entry = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
#endif /* CONFIG_PROC_FS */
|
|
@ -723,7 +723,8 @@ static int create_mixer(struct hda_codec *codec, struct hda_gnode *node,
|
|||
if (is_loopback)
|
||||
add_input_loopback(codec, node->nid, HDA_INPUT, index);
|
||||
snd_printdd("[%s] NID=0x%x, DIR=IN, IDX=0x%x\n", name, node->nid, index);
|
||||
if ((err = snd_ctl_add(codec->bus->card, snd_ctl_new1(&knew, codec))) < 0)
|
||||
err = snd_hda_ctl_add(codec, snd_ctl_new1(&knew, codec));
|
||||
if (err < 0)
|
||||
return err;
|
||||
created = 1;
|
||||
} else if ((node->wid_caps & AC_WCAP_OUT_AMP) &&
|
||||
|
@ -732,7 +733,8 @@ static int create_mixer(struct hda_codec *codec, struct hda_gnode *node,
|
|||
if (is_loopback)
|
||||
add_input_loopback(codec, node->nid, HDA_OUTPUT, 0);
|
||||
snd_printdd("[%s] NID=0x%x, DIR=OUT\n", name, node->nid);
|
||||
if ((err = snd_ctl_add(codec->bus->card, snd_ctl_new1(&knew, codec))) < 0)
|
||||
err = snd_hda_ctl_add(codec, snd_ctl_new1(&knew, codec));
|
||||
if (err < 0)
|
||||
return err;
|
||||
created = 1;
|
||||
}
|
||||
|
@ -745,14 +747,16 @@ static int create_mixer(struct hda_codec *codec, struct hda_gnode *node,
|
|||
(node->amp_in_caps & AC_AMPCAP_NUM_STEPS)) {
|
||||
knew = (struct snd_kcontrol_new)HDA_CODEC_VOLUME(name, node->nid, index, HDA_INPUT);
|
||||
snd_printdd("[%s] NID=0x%x, DIR=IN, IDX=0x%x\n", name, node->nid, index);
|
||||
if ((err = snd_ctl_add(codec->bus->card, snd_ctl_new1(&knew, codec))) < 0)
|
||||
err = snd_hda_ctl_add(codec, snd_ctl_new1(&knew, codec));
|
||||
if (err < 0)
|
||||
return err;
|
||||
created = 1;
|
||||
} else if ((node->wid_caps & AC_WCAP_OUT_AMP) &&
|
||||
(node->amp_out_caps & AC_AMPCAP_NUM_STEPS)) {
|
||||
knew = (struct snd_kcontrol_new)HDA_CODEC_VOLUME(name, node->nid, 0, HDA_OUTPUT);
|
||||
snd_printdd("[%s] NID=0x%x, DIR=OUT\n", name, node->nid);
|
||||
if ((err = snd_ctl_add(codec->bus->card, snd_ctl_new1(&knew, codec))) < 0)
|
||||
err = snd_hda_ctl_add(codec, snd_ctl_new1(&knew, codec));
|
||||
if (err < 0)
|
||||
return err;
|
||||
created = 1;
|
||||
}
|
||||
|
@ -849,8 +853,8 @@ static int build_input_controls(struct hda_codec *codec)
|
|||
}
|
||||
|
||||
/* create input MUX if multiple sources are available */
|
||||
if ((err = snd_ctl_add(codec->bus->card,
|
||||
snd_ctl_new1(&cap_sel, codec))) < 0)
|
||||
err = snd_hda_ctl_add(codec, snd_ctl_new1(&cap_sel, codec));
|
||||
if (err < 0)
|
||||
return err;
|
||||
|
||||
/* no volume control? */
|
||||
|
@ -867,8 +871,8 @@ static int build_input_controls(struct hda_codec *codec)
|
|||
HDA_CODEC_VOLUME(name, adc_node->nid,
|
||||
spec->input_mux.items[i].index,
|
||||
HDA_INPUT);
|
||||
if ((err = snd_ctl_add(codec->bus->card,
|
||||
snd_ctl_new1(&knew, codec))) < 0)
|
||||
err = snd_hda_ctl_add(codec, snd_ctl_new1(&knew, codec));
|
||||
if (err < 0)
|
||||
return err;
|
||||
}
|
||||
|
||||
|
@ -1097,3 +1101,4 @@ int snd_hda_parse_generic_codec(struct hda_codec *codec)
|
|||
snd_hda_generic_free(codec);
|
||||
return err;
|
||||
}
|
||||
EXPORT_SYMBOL(snd_hda_parse_generic_codec);
|
||||
|
|
|
@ -23,10 +23,12 @@
|
|||
#include <linux/pci.h>
|
||||
#include <linux/compat.h>
|
||||
#include <linux/mutex.h>
|
||||
#include <linux/ctype.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
|
||||
|
@ -95,7 +97,26 @@ static int hda_hwdep_open(struct snd_hwdep *hw, struct file *file)
|
|||
return 0;
|
||||
}
|
||||
|
||||
int __devinit snd_hda_create_hwdep(struct hda_codec *codec)
|
||||
static void clear_hwdep_elements(struct hda_codec *codec)
|
||||
{
|
||||
char **head;
|
||||
int i;
|
||||
|
||||
/* clear init verbs */
|
||||
snd_array_free(&codec->init_verbs);
|
||||
/* clear hints */
|
||||
head = codec->hints.list;
|
||||
for (i = 0; i < codec->hints.used; i++, head++)
|
||||
kfree(*head);
|
||||
snd_array_free(&codec->hints);
|
||||
}
|
||||
|
||||
static void hwdep_free(struct snd_hwdep *hwdep)
|
||||
{
|
||||
clear_hwdep_elements(hwdep->private_data);
|
||||
}
|
||||
|
||||
int /*__devinit*/ snd_hda_create_hwdep(struct hda_codec *codec)
|
||||
{
|
||||
char hwname[16];
|
||||
struct snd_hwdep *hwdep;
|
||||
|
@ -109,6 +130,7 @@ int __devinit snd_hda_create_hwdep(struct hda_codec *codec)
|
|||
sprintf(hwdep->name, "HDA Codec %d", codec->addr);
|
||||
hwdep->iface = SNDRV_HWDEP_IFACE_HDA;
|
||||
hwdep->private_data = codec;
|
||||
hwdep->private_free = hwdep_free;
|
||||
hwdep->exclusive = 1;
|
||||
|
||||
hwdep->ops.open = hda_hwdep_open;
|
||||
|
@ -117,5 +139,215 @@ int __devinit snd_hda_create_hwdep(struct hda_codec *codec)
|
|||
hwdep->ops.ioctl_compat = hda_hwdep_ioctl_compat;
|
||||
#endif
|
||||
|
||||
snd_array_init(&codec->init_verbs, sizeof(struct hda_verb), 32);
|
||||
snd_array_init(&codec->hints, sizeof(char *), 32);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
#ifdef CONFIG_SND_HDA_RECONFIG
|
||||
|
||||
/*
|
||||
* sysfs interface
|
||||
*/
|
||||
|
||||
static int clear_codec(struct hda_codec *codec)
|
||||
{
|
||||
snd_hda_codec_reset(codec);
|
||||
clear_hwdep_elements(codec);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int reconfig_codec(struct hda_codec *codec)
|
||||
{
|
||||
int err;
|
||||
|
||||
snd_printk(KERN_INFO "hda-codec: reconfiguring\n");
|
||||
snd_hda_codec_reset(codec);
|
||||
err = snd_hda_codec_configure(codec);
|
||||
if (err < 0)
|
||||
return err;
|
||||
/* rebuild PCMs */
|
||||
err = snd_hda_codec_build_pcms(codec);
|
||||
if (err < 0)
|
||||
return err;
|
||||
/* rebuild mixers */
|
||||
err = snd_hda_codec_build_controls(codec);
|
||||
if (err < 0)
|
||||
return err;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* allocate a string at most len chars, and remove the trailing EOL
|
||||
*/
|
||||
static char *kstrndup_noeol(const char *src, size_t len)
|
||||
{
|
||||
char *s = kstrndup(src, len, GFP_KERNEL);
|
||||
char *p;
|
||||
if (!s)
|
||||
return NULL;
|
||||
p = strchr(s, '\n');
|
||||
if (p)
|
||||
*p = 0;
|
||||
return s;
|
||||
}
|
||||
|
||||
#define CODEC_INFO_SHOW(type) \
|
||||
static ssize_t type##_show(struct device *dev, \
|
||||
struct device_attribute *attr, \
|
||||
char *buf) \
|
||||
{ \
|
||||
struct snd_hwdep *hwdep = dev_get_drvdata(dev); \
|
||||
struct hda_codec *codec = hwdep->private_data; \
|
||||
return sprintf(buf, "0x%x\n", codec->type); \
|
||||
}
|
||||
|
||||
#define CODEC_INFO_STR_SHOW(type) \
|
||||
static ssize_t type##_show(struct device *dev, \
|
||||
struct device_attribute *attr, \
|
||||
char *buf) \
|
||||
{ \
|
||||
struct snd_hwdep *hwdep = dev_get_drvdata(dev); \
|
||||
struct hda_codec *codec = hwdep->private_data; \
|
||||
return sprintf(buf, "%s\n", \
|
||||
codec->type ? codec->type : ""); \
|
||||
}
|
||||
|
||||
CODEC_INFO_SHOW(vendor_id);
|
||||
CODEC_INFO_SHOW(subsystem_id);
|
||||
CODEC_INFO_SHOW(revision_id);
|
||||
CODEC_INFO_SHOW(afg);
|
||||
CODEC_INFO_SHOW(mfg);
|
||||
CODEC_INFO_STR_SHOW(name);
|
||||
CODEC_INFO_STR_SHOW(modelname);
|
||||
|
||||
#define CODEC_INFO_STORE(type) \
|
||||
static ssize_t type##_store(struct device *dev, \
|
||||
struct device_attribute *attr, \
|
||||
const char *buf, size_t count) \
|
||||
{ \
|
||||
struct snd_hwdep *hwdep = dev_get_drvdata(dev); \
|
||||
struct hda_codec *codec = hwdep->private_data; \
|
||||
char *after; \
|
||||
codec->type = simple_strtoul(buf, &after, 0); \
|
||||
return count; \
|
||||
}
|
||||
|
||||
#define CODEC_INFO_STR_STORE(type) \
|
||||
static ssize_t type##_store(struct device *dev, \
|
||||
struct device_attribute *attr, \
|
||||
const char *buf, size_t count) \
|
||||
{ \
|
||||
struct snd_hwdep *hwdep = dev_get_drvdata(dev); \
|
||||
struct hda_codec *codec = hwdep->private_data; \
|
||||
char *s = kstrndup_noeol(buf, 64); \
|
||||
if (!s) \
|
||||
return -ENOMEM; \
|
||||
kfree(codec->type); \
|
||||
codec->type = s; \
|
||||
return count; \
|
||||
}
|
||||
|
||||
CODEC_INFO_STORE(vendor_id);
|
||||
CODEC_INFO_STORE(subsystem_id);
|
||||
CODEC_INFO_STORE(revision_id);
|
||||
CODEC_INFO_STR_STORE(name);
|
||||
CODEC_INFO_STR_STORE(modelname);
|
||||
|
||||
#define CODEC_ACTION_STORE(type) \
|
||||
static ssize_t type##_store(struct device *dev, \
|
||||
struct device_attribute *attr, \
|
||||
const char *buf, size_t count) \
|
||||
{ \
|
||||
struct snd_hwdep *hwdep = dev_get_drvdata(dev); \
|
||||
struct hda_codec *codec = hwdep->private_data; \
|
||||
int err = 0; \
|
||||
if (*buf) \
|
||||
err = type##_codec(codec); \
|
||||
return err < 0 ? err : count; \
|
||||
}
|
||||
|
||||
CODEC_ACTION_STORE(reconfig);
|
||||
CODEC_ACTION_STORE(clear);
|
||||
|
||||
static ssize_t init_verbs_store(struct device *dev,
|
||||
struct device_attribute *attr,
|
||||
const char *buf, size_t count)
|
||||
{
|
||||
struct snd_hwdep *hwdep = dev_get_drvdata(dev);
|
||||
struct hda_codec *codec = hwdep->private_data;
|
||||
char *p;
|
||||
struct hda_verb verb, *v;
|
||||
|
||||
verb.nid = simple_strtoul(buf, &p, 0);
|
||||
verb.verb = simple_strtoul(p, &p, 0);
|
||||
verb.param = simple_strtoul(p, &p, 0);
|
||||
if (!verb.nid || !verb.verb || !verb.param)
|
||||
return -EINVAL;
|
||||
v = snd_array_new(&codec->init_verbs);
|
||||
if (!v)
|
||||
return -ENOMEM;
|
||||
*v = verb;
|
||||
return count;
|
||||
}
|
||||
|
||||
static ssize_t hints_store(struct device *dev,
|
||||
struct device_attribute *attr,
|
||||
const char *buf, size_t count)
|
||||
{
|
||||
struct snd_hwdep *hwdep = dev_get_drvdata(dev);
|
||||
struct hda_codec *codec = hwdep->private_data;
|
||||
char *p;
|
||||
char **hint;
|
||||
|
||||
if (!*buf || isspace(*buf) || *buf == '#' || *buf == '\n')
|
||||
return count;
|
||||
p = kstrndup_noeol(buf, 1024);
|
||||
if (!p)
|
||||
return -ENOMEM;
|
||||
hint = snd_array_new(&codec->hints);
|
||||
if (!hint) {
|
||||
kfree(p);
|
||||
return -ENOMEM;
|
||||
}
|
||||
*hint = p;
|
||||
return count;
|
||||
}
|
||||
|
||||
#define CODEC_ATTR_RW(type) \
|
||||
__ATTR(type, 0644, type##_show, type##_store)
|
||||
#define CODEC_ATTR_RO(type) \
|
||||
__ATTR_RO(type)
|
||||
#define CODEC_ATTR_WO(type) \
|
||||
__ATTR(type, 0200, NULL, type##_store)
|
||||
|
||||
static struct device_attribute codec_attrs[] = {
|
||||
CODEC_ATTR_RW(vendor_id),
|
||||
CODEC_ATTR_RW(subsystem_id),
|
||||
CODEC_ATTR_RW(revision_id),
|
||||
CODEC_ATTR_RO(afg),
|
||||
CODEC_ATTR_RO(mfg),
|
||||
CODEC_ATTR_RW(name),
|
||||
CODEC_ATTR_RW(modelname),
|
||||
CODEC_ATTR_WO(init_verbs),
|
||||
CODEC_ATTR_WO(hints),
|
||||
CODEC_ATTR_WO(reconfig),
|
||||
CODEC_ATTR_WO(clear),
|
||||
};
|
||||
|
||||
/*
|
||||
* create sysfs files on hwdep directory
|
||||
*/
|
||||
int snd_hda_hwdep_add_sysfs(struct hda_codec *codec)
|
||||
{
|
||||
struct snd_hwdep *hwdep = codec->hwdep;
|
||||
int i;
|
||||
|
||||
for (i = 0; i < ARRAY_SIZE(codec_attrs); i++)
|
||||
snd_add_device_sysfs_file(SNDRV_DEVICE_TYPE_HWDEP, hwdep->card,
|
||||
hwdep->device, &codec_attrs[i]);
|
||||
return 0;
|
||||
}
|
||||
|
||||
#endif /* CONFIG_SND_HDA_RECONFIG */
|
||||
|
|
|
@ -58,6 +58,7 @@ static char *model[SNDRV_CARDS];
|
|||
static int position_fix[SNDRV_CARDS];
|
||||
static int bdl_pos_adj[SNDRV_CARDS] = {[0 ... (SNDRV_CARDS-1)] = -1};
|
||||
static int probe_mask[SNDRV_CARDS] = {[0 ... (SNDRV_CARDS-1)] = -1};
|
||||
static int probe_only[SNDRV_CARDS];
|
||||
static int single_cmd;
|
||||
static int enable_msi;
|
||||
|
||||
|
@ -76,6 +77,8 @@ module_param_array(bdl_pos_adj, int, NULL, 0644);
|
|||
MODULE_PARM_DESC(bdl_pos_adj, "BDL position adjustment offset.");
|
||||
module_param_array(probe_mask, int, NULL, 0444);
|
||||
MODULE_PARM_DESC(probe_mask, "Bitmask to probe codecs (default = -1).");
|
||||
module_param_array(probe_only, bool, NULL, 0444);
|
||||
MODULE_PARM_DESC(probe_only, "Only probing and no codec initialization.");
|
||||
module_param(single_cmd, bool, 0444);
|
||||
MODULE_PARM_DESC(single_cmd, "Use single command to communicate with codecs "
|
||||
"(for debugging only).");
|
||||
|
@ -83,7 +86,10 @@ module_param(enable_msi, int, 0444);
|
|||
MODULE_PARM_DESC(enable_msi, "Enable Message Signaled Interrupt (MSI)");
|
||||
|
||||
#ifdef CONFIG_SND_HDA_POWER_SAVE
|
||||
/* power_save option is defined in hda_codec.c */
|
||||
static int power_save = CONFIG_SND_HDA_POWER_SAVE_DEFAULT;
|
||||
module_param(power_save, int, 0644);
|
||||
MODULE_PARM_DESC(power_save, "Automatic power-saving timeout "
|
||||
"(in second, 0 = disable).");
|
||||
|
||||
/* reset the HD-audio controller in power save mode.
|
||||
* this may give more power-saving, but will take longer time to
|
||||
|
@ -292,6 +298,8 @@ enum {
|
|||
/* Define VIA HD Audio Device ID*/
|
||||
#define VIA_HDAC_DEVICE_ID 0x3288
|
||||
|
||||
/* HD Audio class code */
|
||||
#define PCI_CLASS_MULTIMEDIA_HD_AUDIO 0x0403
|
||||
|
||||
/*
|
||||
*/
|
||||
|
@ -392,6 +400,7 @@ struct azx {
|
|||
unsigned int msi :1;
|
||||
unsigned int irq_pending_warned :1;
|
||||
unsigned int via_dmapos_patch :1; /* enable DMA-position fix for VIA */
|
||||
unsigned int probing :1; /* codec probing phase */
|
||||
|
||||
/* for debugging */
|
||||
unsigned int last_cmd; /* last issued command (to sync) */
|
||||
|
@ -414,6 +423,7 @@ enum {
|
|||
AZX_DRIVER_ULI,
|
||||
AZX_DRIVER_NVIDIA,
|
||||
AZX_DRIVER_TERA,
|
||||
AZX_DRIVER_GENERIC,
|
||||
AZX_NUM_DRIVERS, /* keep this as last entry */
|
||||
};
|
||||
|
||||
|
@ -427,6 +437,7 @@ static char *driver_short_names[] __devinitdata = {
|
|||
[AZX_DRIVER_ULI] = "HDA ULI M5461",
|
||||
[AZX_DRIVER_NVIDIA] = "HDA NVidia",
|
||||
[AZX_DRIVER_TERA] = "HDA Teradici",
|
||||
[AZX_DRIVER_GENERIC] = "HD-Audio Generic",
|
||||
};
|
||||
|
||||
/*
|
||||
|
@ -527,9 +538,9 @@ static void azx_free_cmd_io(struct azx *chip)
|
|||
}
|
||||
|
||||
/* send a command */
|
||||
static int azx_corb_send_cmd(struct hda_codec *codec, u32 val)
|
||||
static int azx_corb_send_cmd(struct hda_bus *bus, u32 val)
|
||||
{
|
||||
struct azx *chip = codec->bus->private_data;
|
||||
struct azx *chip = bus->private_data;
|
||||
unsigned int wp;
|
||||
|
||||
/* add command to corb */
|
||||
|
@ -577,9 +588,9 @@ static void azx_update_rirb(struct azx *chip)
|
|||
}
|
||||
|
||||
/* receive a response */
|
||||
static unsigned int azx_rirb_get_response(struct hda_codec *codec)
|
||||
static unsigned int azx_rirb_get_response(struct hda_bus *bus)
|
||||
{
|
||||
struct azx *chip = codec->bus->private_data;
|
||||
struct azx *chip = bus->private_data;
|
||||
unsigned long timeout;
|
||||
|
||||
again:
|
||||
|
@ -596,7 +607,7 @@ static unsigned int azx_rirb_get_response(struct hda_codec *codec)
|
|||
}
|
||||
if (time_after(jiffies, timeout))
|
||||
break;
|
||||
if (codec->bus->needs_damn_long_delay)
|
||||
if (bus->needs_damn_long_delay)
|
||||
msleep(2); /* temporary workaround */
|
||||
else {
|
||||
udelay(10);
|
||||
|
@ -624,6 +635,14 @@ static unsigned int azx_rirb_get_response(struct hda_codec *codec)
|
|||
goto again;
|
||||
}
|
||||
|
||||
if (chip->probing) {
|
||||
/* If this critical timeout happens during the codec probing
|
||||
* phase, this is likely an access to a non-existing codec
|
||||
* slot. Better to return an error and reset the system.
|
||||
*/
|
||||
return -1;
|
||||
}
|
||||
|
||||
snd_printk(KERN_ERR "hda_intel: azx_get_response timeout, "
|
||||
"switching to single_cmd mode: last cmd=0x%08x\n",
|
||||
chip->last_cmd);
|
||||
|
@ -646,9 +665,9 @@ static unsigned int azx_rirb_get_response(struct hda_codec *codec)
|
|||
*/
|
||||
|
||||
/* send a command */
|
||||
static int azx_single_send_cmd(struct hda_codec *codec, u32 val)
|
||||
static int azx_single_send_cmd(struct hda_bus *bus, u32 val)
|
||||
{
|
||||
struct azx *chip = codec->bus->private_data;
|
||||
struct azx *chip = bus->private_data;
|
||||
int timeout = 50;
|
||||
|
||||
while (timeout--) {
|
||||
|
@ -671,9 +690,9 @@ static int azx_single_send_cmd(struct hda_codec *codec, u32 val)
|
|||
}
|
||||
|
||||
/* receive a response */
|
||||
static unsigned int azx_single_get_response(struct hda_codec *codec)
|
||||
static unsigned int azx_single_get_response(struct hda_bus *bus)
|
||||
{
|
||||
struct azx *chip = codec->bus->private_data;
|
||||
struct azx *chip = bus->private_data;
|
||||
int timeout = 50;
|
||||
|
||||
while (timeout--) {
|
||||
|
@ -696,38 +715,29 @@ static unsigned int azx_single_get_response(struct hda_codec *codec)
|
|||
*/
|
||||
|
||||
/* send a command */
|
||||
static int azx_send_cmd(struct hda_codec *codec, hda_nid_t nid,
|
||||
int direct, unsigned int verb,
|
||||
unsigned int para)
|
||||
static int azx_send_cmd(struct hda_bus *bus, unsigned int val)
|
||||
{
|
||||
struct azx *chip = codec->bus->private_data;
|
||||
u32 val;
|
||||
struct azx *chip = bus->private_data;
|
||||
|
||||
val = (u32)(codec->addr & 0x0f) << 28;
|
||||
val |= (u32)direct << 27;
|
||||
val |= (u32)nid << 20;
|
||||
val |= verb << 8;
|
||||
val |= para;
|
||||
chip->last_cmd = val;
|
||||
|
||||
if (chip->single_cmd)
|
||||
return azx_single_send_cmd(codec, val);
|
||||
return azx_single_send_cmd(bus, val);
|
||||
else
|
||||
return azx_corb_send_cmd(codec, val);
|
||||
return azx_corb_send_cmd(bus, val);
|
||||
}
|
||||
|
||||
/* get a response */
|
||||
static unsigned int azx_get_response(struct hda_codec *codec)
|
||||
static unsigned int azx_get_response(struct hda_bus *bus)
|
||||
{
|
||||
struct azx *chip = codec->bus->private_data;
|
||||
struct azx *chip = bus->private_data;
|
||||
if (chip->single_cmd)
|
||||
return azx_single_get_response(codec);
|
||||
return azx_single_get_response(bus);
|
||||
else
|
||||
return azx_rirb_get_response(codec);
|
||||
return azx_rirb_get_response(bus);
|
||||
}
|
||||
|
||||
#ifdef CONFIG_SND_HDA_POWER_SAVE
|
||||
static void azx_power_notify(struct hda_codec *codec);
|
||||
static void azx_power_notify(struct hda_bus *bus);
|
||||
#endif
|
||||
|
||||
/* reset codec link */
|
||||
|
@ -1184,6 +1194,28 @@ static int azx_setup_controller(struct azx *chip, struct azx_dev *azx_dev)
|
|||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Probe the given codec address
|
||||
*/
|
||||
static int probe_codec(struct azx *chip, int addr)
|
||||
{
|
||||
unsigned int cmd = (addr << 28) | (AC_NODE_ROOT << 20) |
|
||||
(AC_VERB_PARAMETERS << 8) | AC_PAR_VENDOR_ID;
|
||||
unsigned int res;
|
||||
|
||||
chip->probing = 1;
|
||||
azx_send_cmd(chip->bus, cmd);
|
||||
res = azx_get_response(chip->bus);
|
||||
chip->probing = 0;
|
||||
if (res == -1)
|
||||
return -EIO;
|
||||
snd_printdd("hda_intel: codec #%d probed OK\n", addr);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int azx_attach_pcm_stream(struct hda_bus *bus, struct hda_codec *codec,
|
||||
struct hda_pcm *cpcm);
|
||||
static void azx_stop_chip(struct azx *chip);
|
||||
|
||||
/*
|
||||
* Codec initialization
|
||||
|
@ -1194,21 +1226,13 @@ static unsigned int azx_max_codecs[AZX_NUM_DRIVERS] __devinitdata = {
|
|||
[AZX_DRIVER_TERA] = 1,
|
||||
};
|
||||
|
||||
/* number of slots to probe as default
|
||||
* this can be different from azx_max_codecs[] -- e.g. some boards
|
||||
* report wrongly the non-existing 4th slot availability
|
||||
*/
|
||||
static unsigned int azx_default_codecs[AZX_NUM_DRIVERS] __devinitdata = {
|
||||
[AZX_DRIVER_ICH] = 3,
|
||||
[AZX_DRIVER_ATI] = 3,
|
||||
};
|
||||
|
||||
static int __devinit azx_codec_create(struct azx *chip, const char *model,
|
||||
unsigned int codec_probe_mask)
|
||||
unsigned int codec_probe_mask,
|
||||
int no_init)
|
||||
{
|
||||
struct hda_bus_template bus_temp;
|
||||
int c, codecs, audio_codecs, err;
|
||||
int def_slots, max_slots;
|
||||
int c, codecs, err;
|
||||
int max_slots;
|
||||
|
||||
memset(&bus_temp, 0, sizeof(bus_temp));
|
||||
bus_temp.private_data = chip;
|
||||
|
@ -1216,7 +1240,9 @@ static int __devinit azx_codec_create(struct azx *chip, const char *model,
|
|||
bus_temp.pci = chip->pci;
|
||||
bus_temp.ops.command = azx_send_cmd;
|
||||
bus_temp.ops.get_response = azx_get_response;
|
||||
bus_temp.ops.attach_pcm = azx_attach_pcm_stream;
|
||||
#ifdef CONFIG_SND_HDA_POWER_SAVE
|
||||
bus_temp.power_save = &power_save;
|
||||
bus_temp.ops.pm_notify = azx_power_notify;
|
||||
#endif
|
||||
|
||||
|
@ -1227,33 +1253,43 @@ static int __devinit azx_codec_create(struct azx *chip, const char *model,
|
|||
if (chip->driver_type == AZX_DRIVER_NVIDIA)
|
||||
chip->bus->needs_damn_long_delay = 1;
|
||||
|
||||
codecs = audio_codecs = 0;
|
||||
codecs = 0;
|
||||
max_slots = azx_max_codecs[chip->driver_type];
|
||||
if (!max_slots)
|
||||
max_slots = AZX_MAX_CODECS;
|
||||
def_slots = azx_default_codecs[chip->driver_type];
|
||||
if (!def_slots)
|
||||
def_slots = max_slots;
|
||||
for (c = 0; c < def_slots; c++) {
|
||||
|
||||
/* First try to probe all given codec slots */
|
||||
for (c = 0; c < max_slots; c++) {
|
||||
if ((chip->codec_mask & (1 << c)) & codec_probe_mask) {
|
||||
if (probe_codec(chip, c) < 0) {
|
||||
/* Some BIOSen give you wrong codec addresses
|
||||
* that don't exist
|
||||
*/
|
||||
snd_printk(KERN_WARNING
|
||||
"hda_intel: Codec #%d probe error; "
|
||||
"disabling it...\n", c);
|
||||
chip->codec_mask &= ~(1 << c);
|
||||
/* More badly, accessing to a non-existing
|
||||
* codec often screws up the controller chip,
|
||||
* and distrubs the further communications.
|
||||
* Thus if an error occurs during probing,
|
||||
* better to reset the controller chip to
|
||||
* get back to the sanity state.
|
||||
*/
|
||||
azx_stop_chip(chip);
|
||||
azx_init_chip(chip);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* Then create codec instances */
|
||||
for (c = 0; c < max_slots; c++) {
|
||||
if ((chip->codec_mask & (1 << c)) & codec_probe_mask) {
|
||||
struct hda_codec *codec;
|
||||
err = snd_hda_codec_new(chip->bus, c, &codec);
|
||||
err = snd_hda_codec_new(chip->bus, c, !no_init, &codec);
|
||||
if (err < 0)
|
||||
continue;
|
||||
codecs++;
|
||||
if (codec->afg)
|
||||
audio_codecs++;
|
||||
}
|
||||
}
|
||||
if (!audio_codecs) {
|
||||
/* probe additional slots if no codec is found */
|
||||
for (; c < max_slots; c++) {
|
||||
if ((chip->codec_mask & (1 << c)) & codec_probe_mask) {
|
||||
err = snd_hda_codec_new(chip->bus, c, NULL);
|
||||
if (err < 0)
|
||||
continue;
|
||||
codecs++;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (!codecs) {
|
||||
|
@ -1722,111 +1758,59 @@ static struct snd_pcm_ops azx_pcm_ops = {
|
|||
|
||||
static void azx_pcm_free(struct snd_pcm *pcm)
|
||||
{
|
||||
kfree(pcm->private_data);
|
||||
struct azx_pcm *apcm = pcm->private_data;
|
||||
if (apcm) {
|
||||
apcm->chip->pcm[pcm->device] = NULL;
|
||||
kfree(apcm);
|
||||
}
|
||||
}
|
||||
|
||||
static int __devinit create_codec_pcm(struct azx *chip, struct hda_codec *codec,
|
||||
struct hda_pcm *cpcm)
|
||||
static int
|
||||
azx_attach_pcm_stream(struct hda_bus *bus, struct hda_codec *codec,
|
||||
struct hda_pcm *cpcm)
|
||||
{
|
||||
int err;
|
||||
struct azx *chip = bus->private_data;
|
||||
struct snd_pcm *pcm;
|
||||
struct azx_pcm *apcm;
|
||||
int pcm_dev = cpcm->device;
|
||||
int s, err;
|
||||
|
||||
/* if no substreams are defined for both playback and capture,
|
||||
* it's just a placeholder. ignore it.
|
||||
*/
|
||||
if (!cpcm->stream[0].substreams && !cpcm->stream[1].substreams)
|
||||
return 0;
|
||||
|
||||
if (snd_BUG_ON(!cpcm->name))
|
||||
if (pcm_dev >= AZX_MAX_PCMS) {
|
||||
snd_printk(KERN_ERR SFX "Invalid PCM device number %d\n",
|
||||
pcm_dev);
|
||||
return -EINVAL;
|
||||
|
||||
err = snd_pcm_new(chip->card, cpcm->name, cpcm->device,
|
||||
cpcm->stream[0].substreams,
|
||||
cpcm->stream[1].substreams,
|
||||
}
|
||||
if (chip->pcm[pcm_dev]) {
|
||||
snd_printk(KERN_ERR SFX "PCM %d already exists\n", pcm_dev);
|
||||
return -EBUSY;
|
||||
}
|
||||
err = snd_pcm_new(chip->card, cpcm->name, pcm_dev,
|
||||
cpcm->stream[SNDRV_PCM_STREAM_PLAYBACK].substreams,
|
||||
cpcm->stream[SNDRV_PCM_STREAM_CAPTURE].substreams,
|
||||
&pcm);
|
||||
if (err < 0)
|
||||
return err;
|
||||
strcpy(pcm->name, cpcm->name);
|
||||
apcm = kmalloc(sizeof(*apcm), GFP_KERNEL);
|
||||
apcm = kzalloc(sizeof(*apcm), GFP_KERNEL);
|
||||
if (apcm == NULL)
|
||||
return -ENOMEM;
|
||||
apcm->chip = chip;
|
||||
apcm->codec = codec;
|
||||
apcm->hinfo[0] = &cpcm->stream[0];
|
||||
apcm->hinfo[1] = &cpcm->stream[1];
|
||||
pcm->private_data = apcm;
|
||||
pcm->private_free = azx_pcm_free;
|
||||
if (cpcm->stream[0].substreams)
|
||||
snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &azx_pcm_ops);
|
||||
if (cpcm->stream[1].substreams)
|
||||
snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, &azx_pcm_ops);
|
||||
if (cpcm->pcm_type == HDA_PCM_TYPE_MODEM)
|
||||
pcm->dev_class = SNDRV_PCM_CLASS_MODEM;
|
||||
chip->pcm[pcm_dev] = pcm;
|
||||
cpcm->pcm = pcm;
|
||||
for (s = 0; s < 2; s++) {
|
||||
apcm->hinfo[s] = &cpcm->stream[s];
|
||||
if (cpcm->stream[s].substreams)
|
||||
snd_pcm_set_ops(pcm, s, &azx_pcm_ops);
|
||||
}
|
||||
/* buffer pre-allocation */
|
||||
snd_pcm_lib_preallocate_pages_for_all(pcm, SNDRV_DMA_TYPE_DEV_SG,
|
||||
snd_dma_pci_data(chip->pci),
|
||||
1024 * 64, 32 * 1024 * 1024);
|
||||
chip->pcm[cpcm->device] = pcm;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int __devinit azx_pcm_create(struct azx *chip)
|
||||
{
|
||||
static const char *dev_name[HDA_PCM_NTYPES] = {
|
||||
"Audio", "SPDIF", "HDMI", "Modem"
|
||||
};
|
||||
/* starting device index for each PCM type */
|
||||
static int dev_idx[HDA_PCM_NTYPES] = {
|
||||
[HDA_PCM_TYPE_AUDIO] = 0,
|
||||
[HDA_PCM_TYPE_SPDIF] = 1,
|
||||
[HDA_PCM_TYPE_HDMI] = 3,
|
||||
[HDA_PCM_TYPE_MODEM] = 6
|
||||
};
|
||||
/* normal audio device indices; not linear to keep compatibility */
|
||||
static int audio_idx[4] = { 0, 2, 4, 5 };
|
||||
struct hda_codec *codec;
|
||||
int c, err;
|
||||
int num_devs[HDA_PCM_NTYPES];
|
||||
|
||||
err = snd_hda_build_pcms(chip->bus);
|
||||
if (err < 0)
|
||||
return err;
|
||||
|
||||
/* create audio PCMs */
|
||||
memset(num_devs, 0, sizeof(num_devs));
|
||||
list_for_each_entry(codec, &chip->bus->codec_list, list) {
|
||||
for (c = 0; c < codec->num_pcms; c++) {
|
||||
struct hda_pcm *cpcm = &codec->pcm_info[c];
|
||||
int type = cpcm->pcm_type;
|
||||
switch (type) {
|
||||
case HDA_PCM_TYPE_AUDIO:
|
||||
if (num_devs[type] >= ARRAY_SIZE(audio_idx)) {
|
||||
snd_printk(KERN_WARNING
|
||||
"Too many audio devices\n");
|
||||
continue;
|
||||
}
|
||||
cpcm->device = audio_idx[num_devs[type]];
|
||||
break;
|
||||
case HDA_PCM_TYPE_SPDIF:
|
||||
case HDA_PCM_TYPE_HDMI:
|
||||
case HDA_PCM_TYPE_MODEM:
|
||||
if (num_devs[type]) {
|
||||
snd_printk(KERN_WARNING
|
||||
"%s already defined\n",
|
||||
dev_name[type]);
|
||||
continue;
|
||||
}
|
||||
cpcm->device = dev_idx[type];
|
||||
break;
|
||||
default:
|
||||
snd_printk(KERN_WARNING
|
||||
"Invalid PCM type %d\n", type);
|
||||
continue;
|
||||
}
|
||||
num_devs[type]++;
|
||||
err = create_codec_pcm(chip, codec, cpcm);
|
||||
if (err < 0)
|
||||
return err;
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
@ -1903,13 +1887,13 @@ static void azx_stop_chip(struct azx *chip)
|
|||
|
||||
#ifdef CONFIG_SND_HDA_POWER_SAVE
|
||||
/* power-up/down the controller */
|
||||
static void azx_power_notify(struct hda_codec *codec)
|
||||
static void azx_power_notify(struct hda_bus *bus)
|
||||
{
|
||||
struct azx *chip = codec->bus->private_data;
|
||||
struct azx *chip = bus->private_data;
|
||||
struct hda_codec *c;
|
||||
int power_on = 0;
|
||||
|
||||
list_for_each_entry(c, &codec->bus->codec_list, list) {
|
||||
list_for_each_entry(c, &bus->codec_list, list) {
|
||||
if (c->power_on) {
|
||||
power_on = 1;
|
||||
break;
|
||||
|
@ -1926,6 +1910,18 @@ static void azx_power_notify(struct hda_codec *codec)
|
|||
/*
|
||||
* power management
|
||||
*/
|
||||
|
||||
static int snd_hda_codecs_inuse(struct hda_bus *bus)
|
||||
{
|
||||
struct hda_codec *codec;
|
||||
|
||||
list_for_each_entry(codec, &bus->codec_list, list) {
|
||||
if (snd_hda_codec_needs_resume(codec))
|
||||
return 1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int azx_suspend(struct pci_dev *pci, pm_message_t state)
|
||||
{
|
||||
struct snd_card *card = pci_get_drvdata(pci);
|
||||
|
@ -1951,13 +1947,16 @@ static int azx_suspend(struct pci_dev *pci, pm_message_t state)
|
|||
return 0;
|
||||
}
|
||||
|
||||
static int azx_resume_early(struct pci_dev *pci)
|
||||
{
|
||||
return pci_restore_state(pci);
|
||||
}
|
||||
|
||||
static int azx_resume(struct pci_dev *pci)
|
||||
{
|
||||
struct snd_card *card = pci_get_drvdata(pci);
|
||||
struct azx *chip = card->private_data;
|
||||
|
||||
pci_set_power_state(pci, PCI_D0);
|
||||
pci_restore_state(pci);
|
||||
if (pci_enable_device(pci) < 0) {
|
||||
printk(KERN_ERR "hda-intel: pci_enable_device failed, "
|
||||
"disabling device\n");
|
||||
|
@ -2095,6 +2094,10 @@ static struct snd_pci_quirk probe_mask_list[] __devinitdata = {
|
|||
SND_PCI_QUIRK(0x1014, 0x05b7, "Thinkpad Z60", 0x01),
|
||||
SND_PCI_QUIRK(0x17aa, 0x2010, "Thinkpad X/T/R60", 0x01),
|
||||
SND_PCI_QUIRK(0x17aa, 0x20ac, "Thinkpad X/T/R61", 0x01),
|
||||
/* broken BIOS */
|
||||
SND_PCI_QUIRK(0x1028, 0x20ac, "Dell Studio Desktop", 0x01),
|
||||
/* including bogus ALC268 in slot#2 that conflicts with ALC888 */
|
||||
SND_PCI_QUIRK(0x17c0, 0x4085, "Medion MD96630", 0x01),
|
||||
{}
|
||||
};
|
||||
|
||||
|
@ -2229,6 +2232,7 @@ static int __devinit azx_create(struct snd_card *card, struct pci_dev *pci,
|
|||
chip->playback_streams = ATIHDMI_NUM_PLAYBACK;
|
||||
chip->capture_streams = ATIHDMI_NUM_CAPTURE;
|
||||
break;
|
||||
case AZX_DRIVER_GENERIC:
|
||||
default:
|
||||
chip->playback_streams = ICH6_NUM_PLAYBACK;
|
||||
chip->capture_streams = ICH6_NUM_CAPTURE;
|
||||
|
@ -2338,40 +2342,31 @@ static int __devinit azx_probe(struct pci_dev *pci,
|
|||
}
|
||||
|
||||
err = azx_create(card, pci, dev, pci_id->driver_data, &chip);
|
||||
if (err < 0) {
|
||||
snd_card_free(card);
|
||||
return err;
|
||||
}
|
||||
if (err < 0)
|
||||
goto out_free;
|
||||
card->private_data = chip;
|
||||
|
||||
/* create codec instances */
|
||||
err = azx_codec_create(chip, model[dev], probe_mask[dev]);
|
||||
if (err < 0) {
|
||||
snd_card_free(card);
|
||||
return err;
|
||||
}
|
||||
err = azx_codec_create(chip, model[dev], probe_mask[dev],
|
||||
probe_only[dev]);
|
||||
if (err < 0)
|
||||
goto out_free;
|
||||
|
||||
/* create PCM streams */
|
||||
err = azx_pcm_create(chip);
|
||||
if (err < 0) {
|
||||
snd_card_free(card);
|
||||
return err;
|
||||
}
|
||||
err = snd_hda_build_pcms(chip->bus);
|
||||
if (err < 0)
|
||||
goto out_free;
|
||||
|
||||
/* create mixer controls */
|
||||
err = azx_mixer_create(chip);
|
||||
if (err < 0) {
|
||||
snd_card_free(card);
|
||||
return err;
|
||||
}
|
||||
if (err < 0)
|
||||
goto out_free;
|
||||
|
||||
snd_card_set_dev(card, &pci->dev);
|
||||
|
||||
err = snd_card_register(card);
|
||||
if (err < 0) {
|
||||
snd_card_free(card);
|
||||
return err;
|
||||
}
|
||||
if (err < 0)
|
||||
goto out_free;
|
||||
|
||||
pci_set_drvdata(pci, card);
|
||||
chip->running = 1;
|
||||
|
@ -2380,6 +2375,9 @@ static int __devinit azx_probe(struct pci_dev *pci,
|
|||
|
||||
dev++;
|
||||
return err;
|
||||
out_free:
|
||||
snd_card_free(card);
|
||||
return err;
|
||||
}
|
||||
|
||||
static void __devexit azx_remove(struct pci_dev *pci)
|
||||
|
@ -2453,6 +2451,11 @@ static struct pci_device_id azx_ids[] = {
|
|||
{ PCI_DEVICE(0x10de, 0x0bd7), .driver_data = AZX_DRIVER_NVIDIA },
|
||||
/* Teradici */
|
||||
{ PCI_DEVICE(0x6549, 0x1200), .driver_data = AZX_DRIVER_TERA },
|
||||
/* AMD Generic, PCI class code and Vendor ID for HD Audio */
|
||||
{ PCI_DEVICE(PCI_VENDOR_ID_ATI, PCI_ANY_ID),
|
||||
.class = PCI_CLASS_MULTIMEDIA_HD_AUDIO << 8,
|
||||
.class_mask = 0xffffff,
|
||||
.driver_data = AZX_DRIVER_GENERIC },
|
||||
{ 0, }
|
||||
};
|
||||
MODULE_DEVICE_TABLE(pci, azx_ids);
|
||||
|
@ -2465,6 +2468,7 @@ static struct pci_driver driver = {
|
|||
.remove = __devexit_p(azx_remove),
|
||||
#ifdef CONFIG_PM
|
||||
.suspend = azx_suspend,
|
||||
.resume_early = azx_resume_early,
|
||||
.resume = azx_resume,
|
||||
#endif
|
||||
};
|
||||
|
|
|
@ -96,6 +96,8 @@ 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 **slaves);
|
||||
void snd_hda_codec_reset(struct hda_codec *codec);
|
||||
int snd_hda_codec_configure(struct hda_codec *codec);
|
||||
|
||||
/* amp value bits */
|
||||
#define HDA_AMP_MUTE 0x80
|
||||
|
@ -282,6 +284,12 @@ int snd_hda_codec_proc_new(struct hda_codec *codec);
|
|||
static inline int snd_hda_codec_proc_new(struct hda_codec *codec) { return 0; }
|
||||
#endif
|
||||
|
||||
#define SND_PRINT_RATES_ADVISED_BUFSIZE 80
|
||||
void snd_print_pcm_rates(int pcm, char *buf, int buflen);
|
||||
|
||||
#define SND_PRINT_BITS_ADVISED_BUFSIZE 16
|
||||
void snd_print_pcm_bits(int pcm, char *buf, int buflen);
|
||||
|
||||
/*
|
||||
* Misc
|
||||
*/
|
||||
|
@ -364,17 +372,17 @@ int snd_hda_parse_pin_def_config(struct hda_codec *codec,
|
|||
/* 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
|
||||
#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_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_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_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)
|
||||
|
||||
|
@ -393,10 +401,26 @@ 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);
|
||||
|
||||
int snd_hda_ctl_add(struct hda_codec *codec, struct snd_kcontrol *kctl);
|
||||
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
|
||||
|
||||
#ifdef CONFIG_SND_HDA_RECONFIG
|
||||
int snd_hda_hwdep_add_sysfs(struct hda_codec *codec);
|
||||
#else
|
||||
static inline int snd_hda_hwdep_add_sysfs(struct hda_codec *codec)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
#endif
|
||||
|
||||
/*
|
||||
* power-management
|
||||
|
@ -430,4 +454,66 @@ int snd_hda_check_amp_list_power(struct hda_codec *codec,
|
|||
#define get_amp_direction(kc) (((kc)->private_value >> 18) & 0x1)
|
||||
#define get_amp_index(kc) (((kc)->private_value >> 19) & 0xf)
|
||||
|
||||
/*
|
||||
* 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_MNL 16
|
||||
#define ELD_MAX_SAD 16
|
||||
|
||||
/*
|
||||
* ELD: EDID Like Data
|
||||
*/
|
||||
struct hdmi_eld {
|
||||
int eld_size;
|
||||
int baseline_len;
|
||||
int eld_ver; /* (eld_ver == 0) indicates invalid ELD */
|
||||
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];
|
||||
#ifdef CONFIG_PROC_FS
|
||||
struct snd_info_entry *proc_entry;
|
||||
#endif
|
||||
};
|
||||
|
||||
int snd_hdmi_get_eld_size(struct hda_codec *codec, hda_nid_t nid);
|
||||
int snd_hdmi_get_eld(struct hdmi_eld *, struct hda_codec *, hda_nid_t);
|
||||
void snd_hdmi_show_eld(struct hdmi_eld *eld);
|
||||
|
||||
#ifdef CONFIG_PROC_FS
|
||||
int snd_hda_eld_proc_new(struct hda_codec *codec, struct hdmi_eld *eld);
|
||||
void snd_hda_eld_proc_free(struct hda_codec *codec, struct hdmi_eld *eld);
|
||||
#else
|
||||
static inline int snd_hda_eld_proc_new(struct hda_codec *codec,
|
||||
struct hdmi_eld *eld)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
static inline void snd_hda_eld_proc_free(struct hda_codec *codec,
|
||||
struct hdmi_eld *eld)
|
||||
{
|
||||
}
|
||||
#endif
|
||||
|
||||
#define SND_PRINT_CHANNEL_ALLOCATION_ADVISED_BUFSIZE 80
|
||||
void snd_print_channel_allocation(int spk_alloc, char *buf, int buflen);
|
||||
|
||||
#endif /* __SOUND_HDA_LOCAL_H */
|
||||
|
|
|
@ -1,22 +0,0 @@
|
|||
/*
|
||||
* HDA Patches - included by hda_codec.c
|
||||
*/
|
||||
|
||||
/* Realtek codecs */
|
||||
extern struct hda_codec_preset snd_hda_preset_realtek[];
|
||||
/* C-Media codecs */
|
||||
extern struct hda_codec_preset snd_hda_preset_cmedia[];
|
||||
/* Analog Devices codecs */
|
||||
extern struct hda_codec_preset snd_hda_preset_analog[];
|
||||
/* SigmaTel codecs */
|
||||
extern struct hda_codec_preset snd_hda_preset_sigmatel[];
|
||||
/* SiLabs 3054/3055 modem codecs */
|
||||
extern struct hda_codec_preset snd_hda_preset_si3054[];
|
||||
/* ATI HDMI codecs */
|
||||
extern struct hda_codec_preset snd_hda_preset_atihdmi[];
|
||||
/* Conexant audio codec */
|
||||
extern struct hda_codec_preset snd_hda_preset_conexant[];
|
||||
/* VIA codecs */
|
||||
extern struct hda_codec_preset snd_hda_preset_via[];
|
||||
/* NVIDIA HDMI codecs */
|
||||
extern struct hda_codec_preset snd_hda_preset_nvhdmi[];
|
|
@ -91,31 +91,21 @@ static void print_amp_vals(struct snd_info_buffer *buffer,
|
|||
|
||||
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;
|
||||
char buf[SND_PRINT_RATES_ADVISED_BUFSIZE];
|
||||
|
||||
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");
|
||||
snd_print_pcm_rates(pcm, buf, sizeof(buf));
|
||||
snd_iprintf(buffer, "%s\n", buf);
|
||||
}
|
||||
|
||||
static void print_pcm_bits(struct snd_info_buffer *buffer, unsigned int pcm)
|
||||
{
|
||||
static unsigned int bits[] = { 8, 16, 20, 24, 32 };
|
||||
int i;
|
||||
char buf[SND_PRINT_BITS_ADVISED_BUFSIZE];
|
||||
|
||||
pcm = (pcm >> 16) & 0xff;
|
||||
snd_iprintf(buffer, " bits [0x%x]:", pcm);
|
||||
for (i = 0; i < ARRAY_SIZE(bits); i++)
|
||||
if (pcm & (1 << i))
|
||||
snd_iprintf(buffer, " %d", bits[i]);
|
||||
snd_iprintf(buffer, "\n");
|
||||
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,
|
||||
|
@ -145,32 +135,6 @@ static void print_pcm_caps(struct snd_info_buffer *buffer,
|
|||
print_pcm_formats(buffer, stream);
|
||||
}
|
||||
|
||||
static const char *get_jack_location(u32 cfg)
|
||||
{
|
||||
static char *bases[7] = {
|
||||
"N/A", "Rear", "Front", "Left", "Right", "Top", "Bottom",
|
||||
};
|
||||
static unsigned char specials_idx[] = {
|
||||
0x07, 0x08,
|
||||
0x17, 0x18, 0x19,
|
||||
0x37, 0x38
|
||||
};
|
||||
static char *specials[] = {
|
||||
"Rear Panel", "Drive Bar",
|
||||
"Riser", "HDMI", "ATAPI",
|
||||
"Mobile-In", "Mobile-Out"
|
||||
};
|
||||
int i;
|
||||
cfg = (cfg & AC_DEFCFG_LOCATION) >> AC_DEFCFG_LOCATION_SHIFT;
|
||||
if ((cfg & 0x0f) < 7)
|
||||
return bases[cfg & 0x0f];
|
||||
for (i = 0; i < ARRAY_SIZE(specials_idx); i++) {
|
||||
if (cfg == specials_idx[i])
|
||||
return specials[i];
|
||||
}
|
||||
return "UNKNOWN";
|
||||
}
|
||||
|
||||
static const char *get_jack_connection(u32 cfg)
|
||||
{
|
||||
static char *names[16] = {
|
||||
|
@ -206,13 +170,6 @@ static void print_pin_caps(struct snd_info_buffer *buffer,
|
|||
int *supports_vref)
|
||||
{
|
||||
static char *jack_conns[4] = { "Jack", "N/A", "Fixed", "Both" };
|
||||
static char *jack_types[16] = {
|
||||
"Line Out", "Speaker", "HP Out", "CD",
|
||||
"SPDIF Out", "Digital Out", "Modem Line", "Modem Hand",
|
||||
"Line In", "Aux", "Mic", "Telephony",
|
||||
"SPDIF In", "Digitial In", "Reserved", "Other"
|
||||
};
|
||||
static char *jack_locations[4] = { "Ext", "Int", "Sep", "Oth" };
|
||||
unsigned int caps, val;
|
||||
|
||||
caps = snd_hda_param_read(codec, nid, AC_PAR_PIN_CAP);
|
||||
|
@ -274,9 +231,9 @@ static void print_pin_caps(struct snd_info_buffer *buffer,
|
|||
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],
|
||||
jack_types[(caps & AC_DEFCFG_DEVICE) >> AC_DEFCFG_DEVICE_SHIFT],
|
||||
jack_locations[(caps >> (AC_DEFCFG_LOCATION_SHIFT + 4)) & 3],
|
||||
get_jack_location(caps));
|
||||
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));
|
||||
|
@ -457,17 +414,6 @@ static void print_conn_list(struct snd_info_buffer *buffer,
|
|||
}
|
||||
}
|
||||
|
||||
static void print_realtek_coef(struct snd_info_buffer *buffer,
|
||||
struct hda_codec *codec, hda_nid_t nid)
|
||||
{
|
||||
int coeff = snd_hda_codec_read(codec, nid, 0,
|
||||
AC_VERB_GET_PROC_COEF, 0);
|
||||
snd_iprintf(buffer, " Processing Coefficient: 0x%02x\n", coeff);
|
||||
coeff = snd_hda_codec_read(codec, nid, 0,
|
||||
AC_VERB_GET_COEF_INDEX, 0);
|
||||
snd_iprintf(buffer, " Coefficient Index: 0x%02x\n", coeff);
|
||||
}
|
||||
|
||||
static void print_gpio(struct snd_info_buffer *buffer,
|
||||
struct hda_codec *codec, hda_nid_t nid)
|
||||
{
|
||||
|
@ -500,12 +446,13 @@ static void print_gpio(struct snd_info_buffer *buffer,
|
|||
for (i = 0; i < max; ++i)
|
||||
snd_iprintf(buffer,
|
||||
" IO[%d]: enable=%d, dir=%d, wake=%d, "
|
||||
"sticky=%d, data=%d\n", i,
|
||||
"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);
|
||||
(data & (1<<i)) ? 1 : 0,
|
||||
(unsol & (1<<i)) ? 1 : 0);
|
||||
/* FIXME: add GPO and GPI pin information */
|
||||
}
|
||||
|
||||
|
@ -513,12 +460,11 @@ static void print_codec_info(struct snd_info_entry *entry,
|
|||
struct snd_info_buffer *buffer)
|
||||
{
|
||||
struct hda_codec *codec = entry->private_data;
|
||||
char buf[32];
|
||||
hda_nid_t nid;
|
||||
int i, nodes;
|
||||
|
||||
snd_hda_get_codec_name(codec, buf, sizeof(buf));
|
||||
snd_iprintf(buffer, "Codec: %s\n", buf);
|
||||
snd_iprintf(buffer, "Codec: %s\n",
|
||||
codec->name ? codec->name : "Not Set");
|
||||
snd_iprintf(buffer, "Address: %d\n", codec->addr);
|
||||
snd_iprintf(buffer, "Vendor Id: 0x%x\n", codec->vendor_id);
|
||||
snd_iprintf(buffer, "Subsystem Id: 0x%x\n", codec->subsystem_id);
|
||||
|
@ -547,6 +493,8 @@ static void print_codec_info(struct snd_info_entry *entry,
|
|||
}
|
||||
|
||||
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 =
|
||||
|
@ -649,9 +597,8 @@ static void print_codec_info(struct snd_info_entry *entry,
|
|||
if (wid_caps & AC_WCAP_PROC_WID)
|
||||
print_proc_caps(buffer, codec, nid);
|
||||
|
||||
/* NID 0x20 == Realtek Define Registers */
|
||||
if (codec->vendor_id == 0x10ec && nid == 0x20)
|
||||
print_realtek_coef(buffer, codec, nid);
|
||||
if (codec->proc_widget_hook)
|
||||
codec->proc_widget_hook(buffer, codec, nid);
|
||||
}
|
||||
snd_hda_power_down(codec);
|
||||
}
|
||||
|
|
|
@ -27,7 +27,6 @@
|
|||
#include <sound/core.h>
|
||||
#include "hda_codec.h"
|
||||
#include "hda_local.h"
|
||||
#include "hda_patch.h"
|
||||
|
||||
struct ad198x_spec {
|
||||
struct snd_kcontrol_new *mixers[5];
|
||||
|
@ -67,8 +66,7 @@ struct ad198x_spec {
|
|||
|
||||
/* dynamic controls, init_verbs and input_mux */
|
||||
struct auto_pin_cfg autocfg;
|
||||
unsigned int num_kctl_alloc, num_kctl_used;
|
||||
struct snd_kcontrol_new *kctl_alloc;
|
||||
struct snd_array kctls;
|
||||
struct hda_input_mux private_imux;
|
||||
hda_nid_t private_dac_nids[AUTO_CFG_MAX_OUTS];
|
||||
|
||||
|
@ -154,6 +152,8 @@ static const char *ad_slave_sws[] = {
|
|||
NULL
|
||||
};
|
||||
|
||||
static void ad198x_free_kctls(struct hda_codec *codec);
|
||||
|
||||
static int ad198x_build_controls(struct hda_codec *codec)
|
||||
{
|
||||
struct ad198x_spec *spec = codec->spec;
|
||||
|
@ -202,6 +202,7 @@ static int ad198x_build_controls(struct hda_codec *codec)
|
|||
return err;
|
||||
}
|
||||
|
||||
ad198x_free_kctls(codec); /* no longer needed */
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
@ -375,16 +376,27 @@ static int ad198x_build_pcms(struct hda_codec *codec)
|
|||
return 0;
|
||||
}
|
||||
|
||||
static void ad198x_free_kctls(struct hda_codec *codec)
|
||||
{
|
||||
struct ad198x_spec *spec = codec->spec;
|
||||
|
||||
if (spec->kctls.list) {
|
||||
struct snd_kcontrol_new *kctl = spec->kctls.list;
|
||||
int i;
|
||||
for (i = 0; i < spec->kctls.used; i++)
|
||||
kfree(kctl[i].name);
|
||||
}
|
||||
snd_array_free(&spec->kctls);
|
||||
}
|
||||
|
||||
static void ad198x_free(struct hda_codec *codec)
|
||||
{
|
||||
struct ad198x_spec *spec = codec->spec;
|
||||
unsigned int i;
|
||||
|
||||
if (spec->kctl_alloc) {
|
||||
for (i = 0; i < spec->num_kctl_used; i++)
|
||||
kfree(spec->kctl_alloc[i].name);
|
||||
kfree(spec->kctl_alloc);
|
||||
}
|
||||
if (!spec)
|
||||
return;
|
||||
|
||||
ad198x_free_kctls(codec);
|
||||
kfree(codec->spec);
|
||||
}
|
||||
|
||||
|
@ -625,6 +637,36 @@ static struct hda_input_mux ad1986a_automic_capture_source = {
|
|||
};
|
||||
|
||||
static struct snd_kcontrol_new ad1986a_laptop_eapd_mixers[] = {
|
||||
HDA_BIND_VOL("Master Playback Volume", &ad1986a_laptop_master_vol),
|
||||
HDA_BIND_SW("Master Playback Switch", &ad1986a_laptop_master_sw),
|
||||
HDA_CODEC_VOLUME("PCM Playback Volume", 0x03, 0x0, HDA_OUTPUT),
|
||||
HDA_CODEC_MUTE("PCM Playback Switch", 0x03, 0x0, HDA_OUTPUT),
|
||||
HDA_CODEC_VOLUME("Internal Mic Playback Volume", 0x17, 0, HDA_OUTPUT),
|
||||
HDA_CODEC_MUTE("Internal Mic Playback Switch", 0x17, 0, HDA_OUTPUT),
|
||||
HDA_CODEC_VOLUME("Mic Playback Volume", 0x13, 0x0, HDA_OUTPUT),
|
||||
HDA_CODEC_MUTE("Mic Playback Switch", 0x13, 0x0, HDA_OUTPUT),
|
||||
HDA_CODEC_VOLUME("Mic Boost", 0x0f, 0x0, HDA_OUTPUT),
|
||||
HDA_CODEC_VOLUME("Capture Volume", 0x12, 0x0, HDA_OUTPUT),
|
||||
HDA_CODEC_MUTE("Capture Switch", 0x12, 0x0, HDA_OUTPUT),
|
||||
{
|
||||
.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
|
||||
.name = "Capture Source",
|
||||
.info = ad198x_mux_enum_info,
|
||||
.get = ad198x_mux_enum_get,
|
||||
.put = ad198x_mux_enum_put,
|
||||
},
|
||||
{
|
||||
.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
|
||||
.name = "External Amplifier",
|
||||
.info = ad198x_eapd_info,
|
||||
.get = ad198x_eapd_get,
|
||||
.put = ad198x_eapd_put,
|
||||
.private_value = 0x1b | (1 << 8), /* port-D, inversed */
|
||||
},
|
||||
{ } /* end */
|
||||
};
|
||||
|
||||
static struct snd_kcontrol_new ad1986a_samsung_mixers[] = {
|
||||
HDA_BIND_VOL("Master Playback Volume", &ad1986a_laptop_master_vol),
|
||||
HDA_BIND_SW("Master Playback Switch", &ad1986a_laptop_master_sw),
|
||||
HDA_CODEC_VOLUME("PCM Playback Volume", 0x03, 0x0, HDA_OUTPUT),
|
||||
|
@ -917,6 +959,7 @@ enum {
|
|||
AD1986A_LAPTOP_EAPD,
|
||||
AD1986A_LAPTOP_AUTOMUTE,
|
||||
AD1986A_ULTRA,
|
||||
AD1986A_SAMSUNG,
|
||||
AD1986A_MODELS
|
||||
};
|
||||
|
||||
|
@ -927,6 +970,7 @@ static const char *ad1986a_models[AD1986A_MODELS] = {
|
|||
[AD1986A_LAPTOP_EAPD] = "laptop-eapd",
|
||||
[AD1986A_LAPTOP_AUTOMUTE] = "laptop-automute",
|
||||
[AD1986A_ULTRA] = "ultra",
|
||||
[AD1986A_SAMSUNG] = "samsung",
|
||||
};
|
||||
|
||||
static struct snd_pci_quirk ad1986a_cfg_tbl[] = {
|
||||
|
@ -949,9 +993,9 @@ static struct snd_pci_quirk ad1986a_cfg_tbl[] = {
|
|||
SND_PCI_QUIRK(0x1179, 0xff40, "Toshiba", AD1986A_LAPTOP_EAPD),
|
||||
SND_PCI_QUIRK(0x144d, 0xb03c, "Samsung R55", AD1986A_3STACK),
|
||||
SND_PCI_QUIRK(0x144d, 0xc01e, "FSC V2060", AD1986A_LAPTOP),
|
||||
SND_PCI_QUIRK(0x144d, 0xc023, "Samsung X60", AD1986A_LAPTOP_EAPD),
|
||||
SND_PCI_QUIRK(0x144d, 0xc024, "Samsung R65", AD1986A_LAPTOP_EAPD),
|
||||
SND_PCI_QUIRK(0x144d, 0xc026, "Samsung X11", AD1986A_LAPTOP_EAPD),
|
||||
SND_PCI_QUIRK(0x144d, 0xc023, "Samsung X60", AD1986A_SAMSUNG),
|
||||
SND_PCI_QUIRK(0x144d, 0xc024, "Samsung R65", AD1986A_SAMSUNG),
|
||||
SND_PCI_QUIRK(0x144d, 0xc026, "Samsung X11", AD1986A_SAMSUNG),
|
||||
SND_PCI_QUIRK(0x144d, 0xc027, "Samsung Q1", AD1986A_ULTRA),
|
||||
SND_PCI_QUIRK(0x144d, 0xc504, "Samsung Q35", AD1986A_3STACK),
|
||||
SND_PCI_QUIRK(0x17aa, 0x1011, "Lenovo M55", AD1986A_LAPTOP),
|
||||
|
@ -1033,6 +1077,17 @@ static int patch_ad1986a(struct hda_codec *codec)
|
|||
break;
|
||||
case AD1986A_LAPTOP_EAPD:
|
||||
spec->mixers[0] = ad1986a_laptop_eapd_mixers;
|
||||
spec->num_init_verbs = 2;
|
||||
spec->init_verbs[1] = ad1986a_eapd_init_verbs;
|
||||
spec->multiout.max_channels = 2;
|
||||
spec->multiout.num_dacs = 1;
|
||||
spec->multiout.dac_nids = ad1986a_laptop_dac_nids;
|
||||
if (!is_jack_available(codec, 0x25))
|
||||
spec->multiout.dig_out_nid = 0;
|
||||
spec->input_mux = &ad1986a_laptop_eapd_capture_source;
|
||||
break;
|
||||
case AD1986A_SAMSUNG:
|
||||
spec->mixers[0] = ad1986a_samsung_mixers;
|
||||
spec->num_init_verbs = 3;
|
||||
spec->init_verbs[1] = ad1986a_eapd_init_verbs;
|
||||
spec->init_verbs[2] = ad1986a_automic_verbs;
|
||||
|
@ -2452,9 +2507,6 @@ static struct hda_amp_list ad1988_loopbacks[] = {
|
|||
* Automatic parse of I/O pins from the BIOS configuration
|
||||
*/
|
||||
|
||||
#define NUM_CONTROL_ALLOC 32
|
||||
#define NUM_VERB_ALLOC 32
|
||||
|
||||
enum {
|
||||
AD_CTL_WIDGET_VOL,
|
||||
AD_CTL_WIDGET_MUTE,
|
||||
|
@ -2472,27 +2524,15 @@ static int add_control(struct ad198x_spec *spec, int type, const char *name,
|
|||
{
|
||||
struct snd_kcontrol_new *knew;
|
||||
|
||||
if (spec->num_kctl_used >= spec->num_kctl_alloc) {
|
||||
int num = spec->num_kctl_alloc + NUM_CONTROL_ALLOC;
|
||||
|
||||
knew = kcalloc(num + 1, sizeof(*knew), GFP_KERNEL); /* array + terminator */
|
||||
if (! knew)
|
||||
return -ENOMEM;
|
||||
if (spec->kctl_alloc) {
|
||||
memcpy(knew, spec->kctl_alloc, sizeof(*knew) * spec->num_kctl_alloc);
|
||||
kfree(spec->kctl_alloc);
|
||||
}
|
||||
spec->kctl_alloc = knew;
|
||||
spec->num_kctl_alloc = num;
|
||||
}
|
||||
|
||||
knew = &spec->kctl_alloc[spec->num_kctl_used];
|
||||
snd_array_init(&spec->kctls, sizeof(*knew), 32);
|
||||
knew = snd_array_new(&spec->kctls);
|
||||
if (!knew)
|
||||
return -ENOMEM;
|
||||
*knew = ad1988_control_templates[type];
|
||||
knew->name = kstrdup(name, GFP_KERNEL);
|
||||
if (! knew->name)
|
||||
return -ENOMEM;
|
||||
knew->private_value = val;
|
||||
spec->num_kctl_used++;
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
@ -2846,8 +2886,8 @@ static int ad1988_parse_auto_config(struct hda_codec *codec)
|
|||
if (spec->autocfg.dig_in_pin)
|
||||
spec->dig_in_nid = AD1988_SPDIF_IN;
|
||||
|
||||
if (spec->kctl_alloc)
|
||||
spec->mixers[spec->num_mixers++] = spec->kctl_alloc;
|
||||
if (spec->kctls.list)
|
||||
spec->mixers[spec->num_mixers++] = spec->kctls.list;
|
||||
|
||||
spec->init_verbs[spec->num_init_verbs++] = ad1988_6stack_init_verbs;
|
||||
|
||||
|
@ -3861,6 +3901,7 @@ static const char *ad1884a_models[AD1884A_MODELS] = {
|
|||
static struct snd_pci_quirk ad1884a_cfg_tbl[] = {
|
||||
SND_PCI_QUIRK(0x103c, 0x3030, "HP", AD1884A_MOBILE),
|
||||
SND_PCI_QUIRK(0x103c, 0x3056, "HP", AD1884A_MOBILE),
|
||||
SND_PCI_QUIRK(0x103c, 0x30e6, "HP 6730b", AD1884A_LAPTOP),
|
||||
SND_PCI_QUIRK(0x103c, 0x30e7, "HP EliteBook 8530p", AD1884A_LAPTOP),
|
||||
SND_PCI_QUIRK(0x103c, 0x3614, "HP 6730s", AD1884A_LAPTOP),
|
||||
SND_PCI_QUIRK(0x17aa, 0x20ac, "Thinkpad X300", AD1884A_THINKPAD),
|
||||
|
@ -4267,7 +4308,7 @@ static int patch_ad1882(struct hda_codec *codec)
|
|||
/*
|
||||
* patch entries
|
||||
*/
|
||||
struct hda_codec_preset snd_hda_preset_analog[] = {
|
||||
static struct hda_codec_preset snd_hda_preset_analog[] = {
|
||||
{ .id = 0x11d4184a, .name = "AD1884A", .patch = patch_ad1884a },
|
||||
{ .id = 0x11d41882, .name = "AD1882", .patch = patch_ad1882 },
|
||||
{ .id = 0x11d41883, .name = "AD1883", .patch = patch_ad1884a },
|
||||
|
@ -4285,3 +4326,26 @@ struct hda_codec_preset snd_hda_preset_analog[] = {
|
|||
{ .id = 0x11d4989b, .name = "AD1989B", .patch = patch_ad1988 },
|
||||
{} /* terminator */
|
||||
};
|
||||
|
||||
MODULE_ALIAS("snd-hda-codec-id:11d4*");
|
||||
|
||||
MODULE_LICENSE("GPL");
|
||||
MODULE_DESCRIPTION("Analog Devices HD-audio codec");
|
||||
|
||||
static struct hda_codec_preset_list analog_list = {
|
||||
.preset = snd_hda_preset_analog,
|
||||
.owner = THIS_MODULE,
|
||||
};
|
||||
|
||||
static int __init patch_analog_init(void)
|
||||
{
|
||||
return snd_hda_add_codec_preset(&analog_list);
|
||||
}
|
||||
|
||||
static void __exit patch_analog_exit(void)
|
||||
{
|
||||
snd_hda_delete_codec_preset(&analog_list);
|
||||
}
|
||||
|
||||
module_init(patch_analog_init)
|
||||
module_exit(patch_analog_exit)
|
||||
|
|
|
@ -27,7 +27,6 @@
|
|||
#include <sound/core.h>
|
||||
#include "hda_codec.h"
|
||||
#include "hda_local.h"
|
||||
#include "hda_patch.h"
|
||||
|
||||
struct atihdmi_spec {
|
||||
struct hda_multi_out multiout;
|
||||
|
@ -187,13 +186,40 @@ static int patch_atihdmi(struct hda_codec *codec)
|
|||
/*
|
||||
* patch entries
|
||||
*/
|
||||
struct hda_codec_preset snd_hda_preset_atihdmi[] = {
|
||||
{ .id = 0x1002793c, .name = "ATI RS600 HDMI", .patch = patch_atihdmi },
|
||||
{ .id = 0x10027919, .name = "ATI RS600 HDMI", .patch = patch_atihdmi },
|
||||
{ .id = 0x1002791a, .name = "ATI RS690/780 HDMI", .patch = patch_atihdmi },
|
||||
{ .id = 0x1002aa01, .name = "ATI R6xx HDMI", .patch = patch_atihdmi },
|
||||
static struct hda_codec_preset snd_hda_preset_atihdmi[] = {
|
||||
{ .id = 0x1002793c, .name = "RS600 HDMI", .patch = patch_atihdmi },
|
||||
{ .id = 0x10027919, .name = "RS600 HDMI", .patch = patch_atihdmi },
|
||||
{ .id = 0x1002791a, .name = "RS690/780 HDMI", .patch = patch_atihdmi },
|
||||
{ .id = 0x1002aa01, .name = "R6xx HDMI", .patch = patch_atihdmi },
|
||||
{ .id = 0x10951390, .name = "SiI1390 HDMI", .patch = patch_atihdmi },
|
||||
{ .id = 0x10951392, .name = "SiI1392 HDMI", .patch = patch_atihdmi },
|
||||
{ .id = 0x17e80047, .name = "Chrontel HDMI", .patch = patch_atihdmi },
|
||||
{} /* terminator */
|
||||
};
|
||||
|
||||
MODULE_ALIAS("snd-hda-codec-id:1002793c");
|
||||
MODULE_ALIAS("snd-hda-codec-id:10027919");
|
||||
MODULE_ALIAS("snd-hda-codec-id:1002791a");
|
||||
MODULE_ALIAS("snd-hda-codec-id:1002aa01");
|
||||
MODULE_ALIAS("snd-hda-codec-id:10951390");
|
||||
MODULE_ALIAS("snd-hda-codec-id:17e80047");
|
||||
|
||||
MODULE_LICENSE("GPL");
|
||||
MODULE_DESCRIPTION("ATI HDMI HD-audio codec");
|
||||
|
||||
static struct hda_codec_preset_list atihdmi_list = {
|
||||
.preset = snd_hda_preset_atihdmi,
|
||||
.owner = THIS_MODULE,
|
||||
};
|
||||
|
||||
static int __init patch_atihdmi_init(void)
|
||||
{
|
||||
return snd_hda_add_codec_preset(&atihdmi_list);
|
||||
}
|
||||
|
||||
static void __exit patch_atihdmi_exit(void)
|
||||
{
|
||||
snd_hda_delete_codec_preset(&atihdmi_list);
|
||||
}
|
||||
|
||||
module_init(patch_atihdmi_init)
|
||||
module_exit(patch_atihdmi_exit)
|
||||
|
|
|
@ -28,7 +28,6 @@
|
|||
#include <sound/core.h>
|
||||
#include "hda_codec.h"
|
||||
#include "hda_local.h"
|
||||
#include "hda_patch.h"
|
||||
#define NUM_PINS 11
|
||||
|
||||
|
||||
|
@ -736,8 +735,32 @@ static int patch_cmi9880(struct hda_codec *codec)
|
|||
/*
|
||||
* patch entries
|
||||
*/
|
||||
struct hda_codec_preset snd_hda_preset_cmedia[] = {
|
||||
static struct hda_codec_preset snd_hda_preset_cmedia[] = {
|
||||
{ .id = 0x13f69880, .name = "CMI9880", .patch = patch_cmi9880 },
|
||||
{ .id = 0x434d4980, .name = "CMI9880", .patch = patch_cmi9880 },
|
||||
{} /* terminator */
|
||||
};
|
||||
|
||||
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)
|
||||
|
|
|
@ -27,7 +27,6 @@
|
|||
#include <sound/core.h>
|
||||
#include "hda_codec.h"
|
||||
#include "hda_local.h"
|
||||
#include "hda_patch.h"
|
||||
|
||||
#define CXT_PIN_DIR_IN 0x00
|
||||
#define CXT_PIN_DIR_OUT 0x01
|
||||
|
@ -86,8 +85,6 @@ struct conexant_spec {
|
|||
|
||||
/* dynamic controls, init_verbs and input_mux */
|
||||
struct auto_pin_cfg autocfg;
|
||||
unsigned int num_kctl_alloc, num_kctl_used;
|
||||
struct snd_kcontrol_new *kctl_alloc;
|
||||
struct hda_input_mux private_imux;
|
||||
hda_nid_t private_dac_nids[AUTO_CFG_MAX_OUTS];
|
||||
|
||||
|
@ -344,15 +341,6 @@ static int conexant_init(struct hda_codec *codec)
|
|||
|
||||
static void conexant_free(struct hda_codec *codec)
|
||||
{
|
||||
struct conexant_spec *spec = codec->spec;
|
||||
unsigned int i;
|
||||
|
||||
if (spec->kctl_alloc) {
|
||||
for (i = 0; i < spec->num_kctl_used; i++)
|
||||
kfree(spec->kctl_alloc[i].name);
|
||||
kfree(spec->kctl_alloc);
|
||||
}
|
||||
|
||||
kfree(codec->spec);
|
||||
}
|
||||
|
||||
|
@ -1782,7 +1770,7 @@ static int patch_cxt5051(struct hda_codec *codec)
|
|||
/*
|
||||
*/
|
||||
|
||||
struct hda_codec_preset snd_hda_preset_conexant[] = {
|
||||
static struct hda_codec_preset snd_hda_preset_conexant[] = {
|
||||
{ .id = 0x14f15045, .name = "CX20549 (Venice)",
|
||||
.patch = patch_cxt5045 },
|
||||
{ .id = 0x14f15047, .name = "CX20551 (Waikiki)",
|
||||
|
@ -1791,3 +1779,28 @@ struct hda_codec_preset snd_hda_preset_conexant[] = {
|
|||
.patch = patch_cxt5051 },
|
||||
{} /* terminator */
|
||||
};
|
||||
|
||||
MODULE_ALIAS("snd-hda-codec-id:14f15045");
|
||||
MODULE_ALIAS("snd-hda-codec-id:14f15047");
|
||||
MODULE_ALIAS("snd-hda-codec-id:14f15051");
|
||||
|
||||
MODULE_LICENSE("GPL");
|
||||
MODULE_DESCRIPTION("Conexant HD-audio codec");
|
||||
|
||||
static struct hda_codec_preset_list conexant_list = {
|
||||
.preset = snd_hda_preset_conexant,
|
||||
.owner = THIS_MODULE,
|
||||
};
|
||||
|
||||
static int __init patch_conexant_init(void)
|
||||
{
|
||||
return snd_hda_add_codec_preset(&conexant_list);
|
||||
}
|
||||
|
||||
static void __exit patch_conexant_exit(void)
|
||||
{
|
||||
snd_hda_delete_codec_preset(&conexant_list);
|
||||
}
|
||||
|
||||
module_init(patch_conexant_init)
|
||||
module_exit(patch_conexant_exit)
|
||||
|
|
|
@ -0,0 +1,711 @@
|
|||
/*
|
||||
*
|
||||
* patch_intelhdmi.c - Patch for Intel HDMI codecs
|
||||
*
|
||||
* Copyright(c) 2008 Intel Corporation. All rights reserved.
|
||||
*
|
||||
* Authors:
|
||||
* Jiang Zhe <zhe.jiang@intel.com>
|
||||
* Wu Fengguang <wfg@linux.intel.com>
|
||||
*
|
||||
* Maintained by:
|
||||
* Wu Fengguang <wfg@linux.intel.com>
|
||||
*
|
||||
* 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/delay.h>
|
||||
#include <linux/slab.h>
|
||||
#include <sound/core.h>
|
||||
#include "hda_codec.h"
|
||||
#include "hda_local.h"
|
||||
|
||||
#define CVT_NID 0x02 /* audio converter */
|
||||
#define PIN_NID 0x03 /* HDMI output pin */
|
||||
|
||||
#define INTEL_HDMI_EVENT_TAG 0x08
|
||||
|
||||
struct intel_hdmi_spec {
|
||||
struct hda_multi_out multiout;
|
||||
struct hda_pcm pcm_rec;
|
||||
struct hdmi_eld sink_eld;
|
||||
};
|
||||
|
||||
static struct hda_verb pinout_enable_verb[] = {
|
||||
{PIN_NID, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
|
||||
{} /* terminator */
|
||||
};
|
||||
|
||||
static struct hda_verb pinout_disable_verb[] = {
|
||||
{PIN_NID, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x00},
|
||||
{}
|
||||
};
|
||||
|
||||
static struct hda_verb unsolicited_response_verb[] = {
|
||||
{PIN_NID, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN |
|
||||
INTEL_HDMI_EVENT_TAG},
|
||||
{}
|
||||
};
|
||||
|
||||
static struct hda_verb def_chan_map[] = {
|
||||
{CVT_NID, AC_VERB_SET_HDMI_CHAN_SLOT, 0x00},
|
||||
{CVT_NID, AC_VERB_SET_HDMI_CHAN_SLOT, 0x11},
|
||||
{CVT_NID, AC_VERB_SET_HDMI_CHAN_SLOT, 0x22},
|
||||
{CVT_NID, AC_VERB_SET_HDMI_CHAN_SLOT, 0x33},
|
||||
{CVT_NID, AC_VERB_SET_HDMI_CHAN_SLOT, 0x44},
|
||||
{CVT_NID, AC_VERB_SET_HDMI_CHAN_SLOT, 0x55},
|
||||
{CVT_NID, AC_VERB_SET_HDMI_CHAN_SLOT, 0x66},
|
||||
{CVT_NID, AC_VERB_SET_HDMI_CHAN_SLOT, 0x77},
|
||||
{}
|
||||
};
|
||||
|
||||
|
||||
struct hdmi_audio_infoframe {
|
||||
u8 type; /* 0x84 */
|
||||
u8 ver; /* 0x01 */
|
||||
u8 len; /* 0x0a */
|
||||
|
||||
u8 checksum; /* PB0 */
|
||||
u8 CC02_CT47; /* CC in bits 0:2, CT in 4:7 */
|
||||
u8 SS01_SF24;
|
||||
u8 CXT04;
|
||||
u8 CA;
|
||||
u8 LFEPBL01_LSV36_DM_INH7;
|
||||
u8 reserved[5]; /* PB6 - PB10 */
|
||||
};
|
||||
|
||||
/*
|
||||
* CEA speaker placement:
|
||||
*
|
||||
* FLH FCH FRH
|
||||
* FLW FL FLC FC FRC FR FRW
|
||||
*
|
||||
* LFE
|
||||
* TC
|
||||
*
|
||||
* RL RLC RC RRC RR
|
||||
*
|
||||
* The Left/Right Surround channel _notions_ LS/RS in SMPTE 320M corresponds to
|
||||
* CEA RL/RR; The SMPTE channel _assignment_ C/LFE is swapped to CEA LFE/FC.
|
||||
*/
|
||||
enum cea_speaker_placement {
|
||||
FL = (1 << 0), /* Front Left */
|
||||
FC = (1 << 1), /* Front Center */
|
||||
FR = (1 << 2), /* Front Right */
|
||||
FLC = (1 << 3), /* Front Left Center */
|
||||
FRC = (1 << 4), /* Front Right Center */
|
||||
RL = (1 << 5), /* Rear Left */
|
||||
RC = (1 << 6), /* Rear Center */
|
||||
RR = (1 << 7), /* Rear Right */
|
||||
RLC = (1 << 8), /* Rear Left Center */
|
||||
RRC = (1 << 9), /* Rear Right Center */
|
||||
LFE = (1 << 10), /* Low Frequency Effect */
|
||||
FLW = (1 << 11), /* Front Left Wide */
|
||||
FRW = (1 << 12), /* Front Right Wide */
|
||||
FLH = (1 << 13), /* Front Left High */
|
||||
FCH = (1 << 14), /* Front Center High */
|
||||
FRH = (1 << 15), /* Front Right High */
|
||||
TC = (1 << 16), /* Top Center */
|
||||
};
|
||||
|
||||
/*
|
||||
* ELD SA bits in the CEA Speaker Allocation data block
|
||||
*/
|
||||
static int eld_speaker_allocation_bits[] = {
|
||||
[0] = FL | FR,
|
||||
[1] = LFE,
|
||||
[2] = FC,
|
||||
[3] = RL | RR,
|
||||
[4] = RC,
|
||||
[5] = FLC | FRC,
|
||||
[6] = RLC | RRC,
|
||||
/* the following are not defined in ELD yet */
|
||||
[7] = FLW | FRW,
|
||||
[8] = FLH | FRH,
|
||||
[9] = TC,
|
||||
[10] = FCH,
|
||||
};
|
||||
|
||||
struct cea_channel_speaker_allocation {
|
||||
int ca_index;
|
||||
int speakers[8];
|
||||
|
||||
/* derived values, just for convenience */
|
||||
int channels;
|
||||
int spk_mask;
|
||||
};
|
||||
|
||||
/*
|
||||
* This is an ordered list!
|
||||
*
|
||||
* The preceding ones have better chances to be selected by
|
||||
* hdmi_setup_channel_allocation().
|
||||
*/
|
||||
static struct cea_channel_speaker_allocation channel_allocations[] = {
|
||||
/* channel: 8 7 6 5 4 3 2 1 */
|
||||
{ .ca_index = 0x00, .speakers = { 0, 0, 0, 0, 0, 0, FR, FL } },
|
||||
/* 2.1 */
|
||||
{ .ca_index = 0x01, .speakers = { 0, 0, 0, 0, 0, LFE, FR, FL } },
|
||||
/* Dolby Surround */
|
||||
{ .ca_index = 0x02, .speakers = { 0, 0, 0, 0, FC, 0, FR, FL } },
|
||||
{ .ca_index = 0x03, .speakers = { 0, 0, 0, 0, FC, LFE, FR, FL } },
|
||||
{ .ca_index = 0x04, .speakers = { 0, 0, 0, RC, 0, 0, FR, FL } },
|
||||
{ .ca_index = 0x05, .speakers = { 0, 0, 0, RC, 0, LFE, FR, FL } },
|
||||
{ .ca_index = 0x06, .speakers = { 0, 0, 0, RC, FC, 0, FR, FL } },
|
||||
{ .ca_index = 0x07, .speakers = { 0, 0, 0, RC, FC, LFE, FR, FL } },
|
||||
{ .ca_index = 0x08, .speakers = { 0, 0, RR, RL, 0, 0, FR, FL } },
|
||||
{ .ca_index = 0x09, .speakers = { 0, 0, RR, RL, 0, LFE, FR, FL } },
|
||||
{ .ca_index = 0x0a, .speakers = { 0, 0, RR, RL, FC, 0, FR, FL } },
|
||||
/* 5.1 */
|
||||
{ .ca_index = 0x0b, .speakers = { 0, 0, RR, RL, FC, LFE, FR, FL } },
|
||||
{ .ca_index = 0x0c, .speakers = { 0, RC, RR, RL, 0, 0, FR, FL } },
|
||||
{ .ca_index = 0x0d, .speakers = { 0, RC, RR, RL, 0, LFE, FR, FL } },
|
||||
{ .ca_index = 0x0e, .speakers = { 0, RC, RR, RL, FC, 0, FR, FL } },
|
||||
/* 6.1 */
|
||||
{ .ca_index = 0x0f, .speakers = { 0, RC, RR, RL, FC, LFE, FR, FL } },
|
||||
{ .ca_index = 0x10, .speakers = { RRC, RLC, RR, RL, 0, 0, FR, FL } },
|
||||
{ .ca_index = 0x11, .speakers = { RRC, RLC, RR, RL, 0, LFE, FR, FL } },
|
||||
{ .ca_index = 0x12, .speakers = { RRC, RLC, RR, RL, FC, 0, FR, FL } },
|
||||
/* 7.1 */
|
||||
{ .ca_index = 0x13, .speakers = { RRC, RLC, RR, RL, FC, LFE, FR, FL } },
|
||||
{ .ca_index = 0x14, .speakers = { FRC, FLC, 0, 0, 0, 0, FR, FL } },
|
||||
{ .ca_index = 0x15, .speakers = { FRC, FLC, 0, 0, 0, LFE, FR, FL } },
|
||||
{ .ca_index = 0x16, .speakers = { FRC, FLC, 0, 0, FC, 0, FR, FL } },
|
||||
{ .ca_index = 0x17, .speakers = { FRC, FLC, 0, 0, FC, LFE, FR, FL } },
|
||||
{ .ca_index = 0x18, .speakers = { FRC, FLC, 0, RC, 0, 0, FR, FL } },
|
||||
{ .ca_index = 0x19, .speakers = { FRC, FLC, 0, RC, 0, LFE, FR, FL } },
|
||||
{ .ca_index = 0x1a, .speakers = { FRC, FLC, 0, RC, FC, 0, FR, FL } },
|
||||
{ .ca_index = 0x1b, .speakers = { FRC, FLC, 0, RC, FC, LFE, FR, FL } },
|
||||
{ .ca_index = 0x1c, .speakers = { FRC, FLC, RR, RL, 0, 0, FR, FL } },
|
||||
{ .ca_index = 0x1d, .speakers = { FRC, FLC, RR, RL, 0, LFE, FR, FL } },
|
||||
{ .ca_index = 0x1e, .speakers = { FRC, FLC, RR, RL, FC, 0, FR, FL } },
|
||||
{ .ca_index = 0x1f, .speakers = { FRC, FLC, RR, RL, FC, LFE, FR, FL } },
|
||||
{ .ca_index = 0x20, .speakers = { 0, FCH, RR, RL, FC, 0, FR, FL } },
|
||||
{ .ca_index = 0x21, .speakers = { 0, FCH, RR, RL, FC, LFE, FR, FL } },
|
||||
{ .ca_index = 0x22, .speakers = { TC, 0, RR, RL, FC, 0, FR, FL } },
|
||||
{ .ca_index = 0x23, .speakers = { TC, 0, RR, RL, FC, LFE, FR, FL } },
|
||||
{ .ca_index = 0x24, .speakers = { FRH, FLH, RR, RL, 0, 0, FR, FL } },
|
||||
{ .ca_index = 0x25, .speakers = { FRH, FLH, RR, RL, 0, LFE, FR, FL } },
|
||||
{ .ca_index = 0x26, .speakers = { FRW, FLW, RR, RL, 0, 0, FR, FL } },
|
||||
{ .ca_index = 0x27, .speakers = { FRW, FLW, RR, RL, 0, LFE, FR, FL } },
|
||||
{ .ca_index = 0x28, .speakers = { TC, RC, RR, RL, FC, 0, FR, FL } },
|
||||
{ .ca_index = 0x29, .speakers = { TC, RC, RR, RL, FC, LFE, FR, FL } },
|
||||
{ .ca_index = 0x2a, .speakers = { FCH, RC, RR, RL, FC, 0, FR, FL } },
|
||||
{ .ca_index = 0x2b, .speakers = { FCH, RC, RR, RL, FC, LFE, FR, FL } },
|
||||
{ .ca_index = 0x2c, .speakers = { TC, FCH, RR, RL, FC, 0, FR, FL } },
|
||||
{ .ca_index = 0x2d, .speakers = { TC, FCH, RR, RL, FC, LFE, FR, FL } },
|
||||
{ .ca_index = 0x2e, .speakers = { FRH, FLH, RR, RL, FC, 0, FR, FL } },
|
||||
{ .ca_index = 0x2f, .speakers = { FRH, FLH, RR, RL, FC, LFE, FR, FL } },
|
||||
{ .ca_index = 0x30, .speakers = { FRW, FLW, RR, RL, FC, 0, FR, FL } },
|
||||
{ .ca_index = 0x31, .speakers = { FRW, FLW, RR, RL, FC, LFE, FR, FL } },
|
||||
};
|
||||
|
||||
/*
|
||||
* HDMI routines
|
||||
*/
|
||||
|
||||
#ifdef BE_PARANOID
|
||||
static void hdmi_get_dip_index(struct hda_codec *codec, hda_nid_t nid,
|
||||
int *packet_index, int *byte_index)
|
||||
{
|
||||
int val;
|
||||
|
||||
val = snd_hda_codec_read(codec, nid, 0, AC_VERB_GET_HDMI_DIP_INDEX, 0);
|
||||
|
||||
*packet_index = val >> 5;
|
||||
*byte_index = val & 0x1f;
|
||||
}
|
||||
#endif
|
||||
|
||||
static void hdmi_set_dip_index(struct hda_codec *codec, hda_nid_t nid,
|
||||
int packet_index, int byte_index)
|
||||
{
|
||||
int val;
|
||||
|
||||
val = (packet_index << 5) | (byte_index & 0x1f);
|
||||
|
||||
snd_hda_codec_write(codec, nid, 0, AC_VERB_SET_HDMI_DIP_INDEX, val);
|
||||
}
|
||||
|
||||
static void hdmi_write_dip_byte(struct hda_codec *codec, hda_nid_t nid,
|
||||
unsigned char val)
|
||||
{
|
||||
snd_hda_codec_write(codec, nid, 0, AC_VERB_SET_HDMI_DIP_DATA, val);
|
||||
}
|
||||
|
||||
static void hdmi_enable_output(struct hda_codec *codec)
|
||||
{
|
||||
/* Enable Audio InfoFrame Transmission */
|
||||
hdmi_set_dip_index(codec, PIN_NID, 0x0, 0x0);
|
||||
snd_hda_codec_write(codec, PIN_NID, 0, AC_VERB_SET_HDMI_DIP_XMIT,
|
||||
AC_DIPXMIT_BEST);
|
||||
/* Unmute */
|
||||
if (get_wcaps(codec, PIN_NID) & AC_WCAP_OUT_AMP)
|
||||
snd_hda_codec_write(codec, PIN_NID, 0,
|
||||
AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE);
|
||||
/* Enable pin out */
|
||||
snd_hda_sequence_write(codec, pinout_enable_verb);
|
||||
}
|
||||
|
||||
static void hdmi_disable_output(struct hda_codec *codec)
|
||||
{
|
||||
snd_hda_sequence_write(codec, pinout_disable_verb);
|
||||
if (get_wcaps(codec, PIN_NID) & AC_WCAP_OUT_AMP)
|
||||
snd_hda_codec_write(codec, PIN_NID, 0,
|
||||
AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE);
|
||||
|
||||
/*
|
||||
* FIXME: noises may arise when playing music after reloading the
|
||||
* kernel module, until the next X restart or monitor repower.
|
||||
*/
|
||||
}
|
||||
|
||||
static int hdmi_get_channel_count(struct hda_codec *codec)
|
||||
{
|
||||
return 1 + snd_hda_codec_read(codec, CVT_NID, 0,
|
||||
AC_VERB_GET_CVT_CHAN_COUNT, 0);
|
||||
}
|
||||
|
||||
static void hdmi_set_channel_count(struct hda_codec *codec, int chs)
|
||||
{
|
||||
snd_hda_codec_write(codec, CVT_NID, 0,
|
||||
AC_VERB_SET_CVT_CHAN_COUNT, chs - 1);
|
||||
|
||||
if (chs != hdmi_get_channel_count(codec))
|
||||
snd_printd(KERN_INFO "HDMI channel count: expect %d, get %d\n",
|
||||
chs, hdmi_get_channel_count(codec));
|
||||
}
|
||||
|
||||
static void hdmi_debug_channel_mapping(struct hda_codec *codec)
|
||||
{
|
||||
#ifdef CONFIG_SND_DEBUG_VERBOSE
|
||||
int i;
|
||||
int slot;
|
||||
|
||||
for (i = 0; i < 8; i++) {
|
||||
slot = snd_hda_codec_read(codec, CVT_NID, 0,
|
||||
AC_VERB_GET_HDMI_CHAN_SLOT, i);
|
||||
printk(KERN_DEBUG "HDMI: ASP channel %d => slot %d\n",
|
||||
slot >> 4, slot & 0x7);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
static void hdmi_parse_eld(struct hda_codec *codec)
|
||||
{
|
||||
struct intel_hdmi_spec *spec = codec->spec;
|
||||
struct hdmi_eld *eld = &spec->sink_eld;
|
||||
|
||||
if (!snd_hdmi_get_eld(eld, codec, PIN_NID))
|
||||
snd_hdmi_show_eld(eld);
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Audio InfoFrame routines
|
||||
*/
|
||||
|
||||
static void hdmi_debug_dip_size(struct hda_codec *codec)
|
||||
{
|
||||
#ifdef CONFIG_SND_DEBUG_VERBOSE
|
||||
int i;
|
||||
int size;
|
||||
|
||||
size = snd_hdmi_get_eld_size(codec, PIN_NID);
|
||||
printk(KERN_DEBUG "HDMI: ELD buf size is %d\n", size);
|
||||
|
||||
for (i = 0; i < 8; i++) {
|
||||
size = snd_hda_codec_read(codec, PIN_NID, 0,
|
||||
AC_VERB_GET_HDMI_DIP_SIZE, i);
|
||||
printk(KERN_DEBUG "HDMI: DIP GP[%d] buf size is %d\n", i, size);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
static void hdmi_clear_dip_buffers(struct hda_codec *codec)
|
||||
{
|
||||
#ifdef BE_PARANOID
|
||||
int i, j;
|
||||
int size;
|
||||
int pi, bi;
|
||||
for (i = 0; i < 8; i++) {
|
||||
size = snd_hda_codec_read(codec, PIN_NID, 0,
|
||||
AC_VERB_GET_HDMI_DIP_SIZE, i);
|
||||
if (size == 0)
|
||||
continue;
|
||||
|
||||
hdmi_set_dip_index(codec, PIN_NID, i, 0x0);
|
||||
for (j = 1; j < 1000; j++) {
|
||||
hdmi_write_dip_byte(codec, PIN_NID, 0x0);
|
||||
hdmi_get_dip_index(codec, PIN_NID, &pi, &bi);
|
||||
if (pi != i)
|
||||
snd_printd(KERN_INFO "dip index %d: %d != %d\n",
|
||||
bi, pi, i);
|
||||
if (bi == 0) /* byte index wrapped around */
|
||||
break;
|
||||
}
|
||||
snd_printd(KERN_INFO
|
||||
"HDMI: DIP GP[%d] buf reported size=%d, written=%d\n",
|
||||
i, size, j);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
static void hdmi_fill_audio_infoframe(struct hda_codec *codec,
|
||||
struct hdmi_audio_infoframe *ai)
|
||||
{
|
||||
u8 *params = (u8 *)ai;
|
||||
int i;
|
||||
|
||||
hdmi_debug_dip_size(codec);
|
||||
hdmi_clear_dip_buffers(codec); /* be paranoid */
|
||||
|
||||
hdmi_set_dip_index(codec, PIN_NID, 0x0, 0x0);
|
||||
for (i = 0; i < sizeof(ai); i++)
|
||||
hdmi_write_dip_byte(codec, PIN_NID, params[i]);
|
||||
}
|
||||
|
||||
/*
|
||||
* Compute derived values in channel_allocations[].
|
||||
*/
|
||||
static void init_channel_allocations(void)
|
||||
{
|
||||
int i, j;
|
||||
struct cea_channel_speaker_allocation *p;
|
||||
|
||||
for (i = 0; i < ARRAY_SIZE(channel_allocations); i++) {
|
||||
p = channel_allocations + i;
|
||||
p->channels = 0;
|
||||
p->spk_mask = 0;
|
||||
for (j = 0; j < ARRAY_SIZE(p->speakers); j++)
|
||||
if (p->speakers[j]) {
|
||||
p->channels++;
|
||||
p->spk_mask |= p->speakers[j];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* The transformation takes two steps:
|
||||
*
|
||||
* eld->spk_alloc => (eld_speaker_allocation_bits[]) => spk_mask
|
||||
* spk_mask => (channel_allocations[]) => ai->CA
|
||||
*
|
||||
* TODO: it could select the wrong CA from multiple candidates.
|
||||
*/
|
||||
static int hdmi_setup_channel_allocation(struct hda_codec *codec,
|
||||
struct hdmi_audio_infoframe *ai)
|
||||
{
|
||||
struct intel_hdmi_spec *spec = codec->spec;
|
||||
struct hdmi_eld *eld = &spec->sink_eld;
|
||||
int i;
|
||||
int spk_mask = 0;
|
||||
int channels = 1 + (ai->CC02_CT47 & 0x7);
|
||||
char buf[SND_PRINT_CHANNEL_ALLOCATION_ADVISED_BUFSIZE];
|
||||
|
||||
/*
|
||||
* CA defaults to 0 for basic stereo audio
|
||||
*/
|
||||
if (!eld->eld_ver)
|
||||
return 0;
|
||||
if (!eld->spk_alloc)
|
||||
return 0;
|
||||
if (channels <= 2)
|
||||
return 0;
|
||||
|
||||
/*
|
||||
* expand ELD's speaker allocation mask
|
||||
*
|
||||
* ELD tells the speaker mask in a compact(paired) form,
|
||||
* expand ELD's notions to match the ones used by Audio InfoFrame.
|
||||
*/
|
||||
for (i = 0; i < ARRAY_SIZE(eld_speaker_allocation_bits); i++) {
|
||||
if (eld->spk_alloc & (1 << i))
|
||||
spk_mask |= eld_speaker_allocation_bits[i];
|
||||
}
|
||||
|
||||
/* search for the first working match in the CA table */
|
||||
for (i = 0; i < ARRAY_SIZE(channel_allocations); i++) {
|
||||
if (channels == channel_allocations[i].channels &&
|
||||
(spk_mask & channel_allocations[i].spk_mask) ==
|
||||
channel_allocations[i].spk_mask) {
|
||||
ai->CA = channel_allocations[i].ca_index;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
snd_print_channel_allocation(eld->spk_alloc, buf, sizeof(buf));
|
||||
snd_printdd(KERN_INFO
|
||||
"HDMI: select CA 0x%x for %d-channel allocation: %s\n",
|
||||
ai->CA, channels, buf);
|
||||
|
||||
return ai->CA;
|
||||
}
|
||||
|
||||
static void hdmi_setup_channel_mapping(struct hda_codec *codec,
|
||||
struct hdmi_audio_infoframe *ai)
|
||||
{
|
||||
if (!ai->CA)
|
||||
return;
|
||||
|
||||
/*
|
||||
* TODO: adjust channel mapping if necessary
|
||||
* ALSA sequence is front/surr/clfe/side?
|
||||
*/
|
||||
|
||||
snd_hda_sequence_write(codec, def_chan_map);
|
||||
hdmi_debug_channel_mapping(codec);
|
||||
}
|
||||
|
||||
|
||||
static void hdmi_setup_audio_infoframe(struct hda_codec *codec,
|
||||
struct snd_pcm_substream *substream)
|
||||
{
|
||||
struct hdmi_audio_infoframe ai = {
|
||||
.type = 0x84,
|
||||
.ver = 0x01,
|
||||
.len = 0x0a,
|
||||
.CC02_CT47 = substream->runtime->channels - 1,
|
||||
};
|
||||
|
||||
hdmi_setup_channel_allocation(codec, &ai);
|
||||
hdmi_setup_channel_mapping(codec, &ai);
|
||||
|
||||
hdmi_fill_audio_infoframe(codec, &ai);
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Unsolicited events
|
||||
*/
|
||||
|
||||
static void hdmi_intrinsic_event(struct hda_codec *codec, unsigned int res)
|
||||
{
|
||||
int pind = !!(res & AC_UNSOL_RES_PD);
|
||||
int eldv = !!(res & AC_UNSOL_RES_ELDV);
|
||||
|
||||
printk(KERN_INFO
|
||||
"HDMI hot plug event: Presence_Detect=%d ELD_Valid=%d\n",
|
||||
pind, eldv);
|
||||
|
||||
if (pind && eldv) {
|
||||
hdmi_parse_eld(codec);
|
||||
/* TODO: do real things about ELD */
|
||||
}
|
||||
}
|
||||
|
||||
static void hdmi_non_intrinsic_event(struct hda_codec *codec, unsigned int res)
|
||||
{
|
||||
int subtag = (res & AC_UNSOL_RES_SUBTAG) >> AC_UNSOL_RES_SUBTAG_SHIFT;
|
||||
int cp_state = !!(res & AC_UNSOL_RES_CP_STATE);
|
||||
int cp_ready = !!(res & AC_UNSOL_RES_CP_READY);
|
||||
|
||||
printk(KERN_INFO
|
||||
"HDMI content protection event: SUBTAG=0x%x CP_STATE=%d CP_READY=%d\n",
|
||||
subtag,
|
||||
cp_state,
|
||||
cp_ready);
|
||||
|
||||
/* TODO */
|
||||
if (cp_state)
|
||||
;
|
||||
if (cp_ready)
|
||||
;
|
||||
}
|
||||
|
||||
|
||||
static void intel_hdmi_unsol_event(struct hda_codec *codec, unsigned int res)
|
||||
{
|
||||
int tag = res >> AC_UNSOL_RES_TAG_SHIFT;
|
||||
int subtag = (res & AC_UNSOL_RES_SUBTAG) >> AC_UNSOL_RES_SUBTAG_SHIFT;
|
||||
|
||||
if (tag != INTEL_HDMI_EVENT_TAG) {
|
||||
snd_printd(KERN_INFO "Unexpected HDMI event tag 0x%x\n", tag);
|
||||
return;
|
||||
}
|
||||
|
||||
if (subtag == 0)
|
||||
hdmi_intrinsic_event(codec, res);
|
||||
else
|
||||
hdmi_non_intrinsic_event(codec, res);
|
||||
}
|
||||
|
||||
/*
|
||||
* Callbacks
|
||||
*/
|
||||
|
||||
static int intel_hdmi_playback_pcm_open(struct hda_pcm_stream *hinfo,
|
||||
struct hda_codec *codec,
|
||||
struct snd_pcm_substream *substream)
|
||||
{
|
||||
struct intel_hdmi_spec *spec = codec->spec;
|
||||
|
||||
return snd_hda_multi_out_dig_open(codec, &spec->multiout);
|
||||
}
|
||||
|
||||
static int intel_hdmi_playback_pcm_close(struct hda_pcm_stream *hinfo,
|
||||
struct hda_codec *codec,
|
||||
struct snd_pcm_substream *substream)
|
||||
{
|
||||
struct intel_hdmi_spec *spec = codec->spec;
|
||||
|
||||
hdmi_disable_output(codec);
|
||||
|
||||
return snd_hda_multi_out_dig_close(codec, &spec->multiout);
|
||||
}
|
||||
|
||||
static int intel_hdmi_playback_pcm_prepare(struct hda_pcm_stream *hinfo,
|
||||
struct hda_codec *codec,
|
||||
unsigned int stream_tag,
|
||||
unsigned int format,
|
||||
struct snd_pcm_substream *substream)
|
||||
{
|
||||
struct intel_hdmi_spec *spec = codec->spec;
|
||||
|
||||
snd_hda_multi_out_dig_prepare(codec, &spec->multiout, stream_tag,
|
||||
format, substream);
|
||||
|
||||
hdmi_set_channel_count(codec, substream->runtime->channels);
|
||||
|
||||
hdmi_setup_audio_infoframe(codec, substream);
|
||||
|
||||
hdmi_enable_output(codec);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct hda_pcm_stream intel_hdmi_pcm_playback = {
|
||||
.substreams = 1,
|
||||
.channels_min = 2,
|
||||
.channels_max = 8,
|
||||
.nid = CVT_NID, /* NID to query formats and rates and setup streams */
|
||||
.ops = {
|
||||
.open = intel_hdmi_playback_pcm_open,
|
||||
.close = intel_hdmi_playback_pcm_close,
|
||||
.prepare = intel_hdmi_playback_pcm_prepare
|
||||
},
|
||||
};
|
||||
|
||||
static int intel_hdmi_build_pcms(struct hda_codec *codec)
|
||||
{
|
||||
struct intel_hdmi_spec *spec = codec->spec;
|
||||
struct hda_pcm *info = &spec->pcm_rec;
|
||||
|
||||
codec->num_pcms = 1;
|
||||
codec->pcm_info = info;
|
||||
|
||||
info->name = "INTEL HDMI";
|
||||
info->pcm_type = HDA_PCM_TYPE_HDMI;
|
||||
info->stream[SNDRV_PCM_STREAM_PLAYBACK] = intel_hdmi_pcm_playback;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int intel_hdmi_build_controls(struct hda_codec *codec)
|
||||
{
|
||||
struct intel_hdmi_spec *spec = codec->spec;
|
||||
int err;
|
||||
|
||||
err = snd_hda_create_spdif_out_ctls(codec, spec->multiout.dig_out_nid);
|
||||
if (err < 0)
|
||||
return err;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int intel_hdmi_init(struct hda_codec *codec)
|
||||
{
|
||||
/* disable audio output as early as possible */
|
||||
hdmi_disable_output(codec);
|
||||
|
||||
snd_hda_sequence_write(codec, unsolicited_response_verb);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void intel_hdmi_free(struct hda_codec *codec)
|
||||
{
|
||||
struct intel_hdmi_spec *spec = codec->spec;
|
||||
|
||||
snd_hda_eld_proc_free(codec, &spec->sink_eld);
|
||||
kfree(spec);
|
||||
}
|
||||
|
||||
static struct hda_codec_ops intel_hdmi_patch_ops = {
|
||||
.init = intel_hdmi_init,
|
||||
.free = intel_hdmi_free,
|
||||
.build_pcms = intel_hdmi_build_pcms,
|
||||
.build_controls = intel_hdmi_build_controls,
|
||||
.unsol_event = intel_hdmi_unsol_event,
|
||||
};
|
||||
|
||||
static int patch_intel_hdmi(struct hda_codec *codec)
|
||||
{
|
||||
struct intel_hdmi_spec *spec;
|
||||
|
||||
spec = kzalloc(sizeof(*spec), GFP_KERNEL);
|
||||
if (spec == NULL)
|
||||
return -ENOMEM;
|
||||
|
||||
spec->multiout.num_dacs = 0; /* no analog */
|
||||
spec->multiout.max_channels = 8;
|
||||
spec->multiout.dig_out_nid = CVT_NID;
|
||||
|
||||
codec->spec = spec;
|
||||
codec->patch_ops = intel_hdmi_patch_ops;
|
||||
|
||||
snd_hda_eld_proc_new(codec, &spec->sink_eld);
|
||||
|
||||
init_channel_allocations();
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct hda_codec_preset snd_hda_preset_intelhdmi[] = {
|
||||
{ .id = 0x808629fb, .name = "G45 DEVCL", .patch = patch_intel_hdmi },
|
||||
{ .id = 0x80862801, .name = "G45 DEVBLC", .patch = patch_intel_hdmi },
|
||||
{ .id = 0x80862802, .name = "G45 DEVCTG", .patch = patch_intel_hdmi },
|
||||
{ .id = 0x80862803, .name = "G45 DEVELK", .patch = patch_intel_hdmi },
|
||||
{ .id = 0x10951392, .name = "SiI1392 HDMI", .patch = patch_intel_hdmi },
|
||||
{} /* terminator */
|
||||
};
|
||||
|
||||
MODULE_ALIAS("snd-hda-codec-id:808629fb");
|
||||
MODULE_ALIAS("snd-hda-codec-id:80862801");
|
||||
MODULE_ALIAS("snd-hda-codec-id:80862802");
|
||||
MODULE_ALIAS("snd-hda-codec-id:80862803");
|
||||
MODULE_ALIAS("snd-hda-codec-id:10951392");
|
||||
|
||||
MODULE_LICENSE("GPL");
|
||||
MODULE_DESCRIPTION("Intel HDMI HD-audio codec");
|
||||
|
||||
static struct hda_codec_preset_list intel_list = {
|
||||
.preset = snd_hda_preset_intelhdmi,
|
||||
.owner = THIS_MODULE,
|
||||
};
|
||||
|
||||
static int __init patch_intelhdmi_init(void)
|
||||
{
|
||||
return snd_hda_add_codec_preset(&intel_list);
|
||||
}
|
||||
|
||||
static void __exit patch_intelhdmi_exit(void)
|
||||
{
|
||||
snd_hda_delete_codec_preset(&intel_list);
|
||||
}
|
||||
|
||||
module_init(patch_intelhdmi_init)
|
||||
module_exit(patch_intelhdmi_exit)
|
|
@ -158,8 +158,34 @@ static int patch_nvhdmi(struct hda_codec *codec)
|
|||
/*
|
||||
* patch entries
|
||||
*/
|
||||
struct hda_codec_preset snd_hda_preset_nvhdmi[] = {
|
||||
{ .id = 0x10de0002, .name = "NVIDIA MCP78 HDMI", .patch = patch_nvhdmi },
|
||||
{ .id = 0x10de0007, .name = "NVIDIA MCP7A HDMI", .patch = patch_nvhdmi },
|
||||
static struct hda_codec_preset snd_hda_preset_nvhdmi[] = {
|
||||
{ .id = 0x10de0002, .name = "MCP78 HDMI", .patch = patch_nvhdmi },
|
||||
{ .id = 0x10de0007, .name = "MCP7A HDMI", .patch = patch_nvhdmi },
|
||||
{ .id = 0x10de0067, .name = "MCP67 HDMI", .patch = patch_nvhdmi },
|
||||
{} /* terminator */
|
||||
};
|
||||
|
||||
MODULE_ALIAS("snd-hda-codec-id:10de0002");
|
||||
MODULE_ALIAS("snd-hda-codec-id:10de0007");
|
||||
MODULE_ALIAS("snd-hda-codec-id:10de0067");
|
||||
|
||||
MODULE_LICENSE("GPL");
|
||||
MODULE_DESCRIPTION("Nvidia HDMI HD-audio codec");
|
||||
|
||||
static struct hda_codec_preset_list nvhdmi_list = {
|
||||
.preset = snd_hda_preset_nvhdmi,
|
||||
.owner = THIS_MODULE,
|
||||
};
|
||||
|
||||
static int __init patch_nvhdmi_init(void)
|
||||
{
|
||||
return snd_hda_add_codec_preset(&nvhdmi_list);
|
||||
}
|
||||
|
||||
static void __exit patch_nvhdmi_exit(void)
|
||||
{
|
||||
snd_hda_delete_codec_preset(&nvhdmi_list);
|
||||
}
|
||||
|
||||
module_init(patch_nvhdmi_init)
|
||||
module_exit(patch_nvhdmi_exit)
|
||||
|
|
File diff suppressed because it is too large
Load Diff
|
@ -28,7 +28,6 @@
|
|||
#include <sound/core.h>
|
||||
#include "hda_codec.h"
|
||||
#include "hda_local.h"
|
||||
#include "hda_patch.h"
|
||||
|
||||
/* si3054 verbs */
|
||||
#define SI3054_VERB_READ_NODE 0x900
|
||||
|
@ -283,7 +282,7 @@ static int patch_si3054(struct hda_codec *codec)
|
|||
/*
|
||||
* patch entries
|
||||
*/
|
||||
struct hda_codec_preset snd_hda_preset_si3054[] = {
|
||||
static 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 },
|
||||
|
@ -301,3 +300,35 @@ struct hda_codec_preset snd_hda_preset_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
|
@ -47,15 +47,11 @@
|
|||
#include <sound/asoundef.h>
|
||||
#include "hda_codec.h"
|
||||
#include "hda_local.h"
|
||||
#include "hda_patch.h"
|
||||
|
||||
/* amp values */
|
||||
#define AMP_VAL_IDX_SHIFT 19
|
||||
#define AMP_VAL_IDX_MASK (0x0f<<19)
|
||||
|
||||
#define NUM_CONTROL_ALLOC 32
|
||||
#define NUM_VERB_ALLOC 32
|
||||
|
||||
/* Pin Widget NID */
|
||||
#define VT1708_HP_NID 0x13
|
||||
#define VT1708_DIGOUT_NID 0x14
|
||||
|
@ -145,8 +141,6 @@ enum {
|
|||
AUTO_SEQ_SIDE
|
||||
};
|
||||
|
||||
#define get_amp_nid(kc) ((kc)->private_value & 0xffff)
|
||||
|
||||
/* Some VT1708S based boards gets the micboost setting wrong, so we have
|
||||
* to apply some brute-force and re-write the TLV's by software. */
|
||||
static int mic_boost_tlv(struct snd_kcontrol *kcontrol, int op_flag,
|
||||
|
@ -227,8 +221,7 @@ struct via_spec {
|
|||
|
||||
/* dynamic controls, init_verbs and input_mux */
|
||||
struct auto_pin_cfg autocfg;
|
||||
unsigned int num_kctl_alloc, num_kctl_used;
|
||||
struct snd_kcontrol_new *kctl_alloc;
|
||||
struct snd_array kctls;
|
||||
struct hda_input_mux private_imux[2];
|
||||
hda_nid_t private_dac_nids[AUTO_CFG_MAX_OUTS];
|
||||
|
||||
|
@ -272,33 +265,31 @@ static int via_add_control(struct via_spec *spec, int type, const char *name,
|
|||
{
|
||||
struct snd_kcontrol_new *knew;
|
||||
|
||||
if (spec->num_kctl_used >= spec->num_kctl_alloc) {
|
||||
int num = spec->num_kctl_alloc + NUM_CONTROL_ALLOC;
|
||||
|
||||
/* array + terminator */
|
||||
knew = kcalloc(num + 1, sizeof(*knew), GFP_KERNEL);
|
||||
if (!knew)
|
||||
return -ENOMEM;
|
||||
if (spec->kctl_alloc) {
|
||||
memcpy(knew, spec->kctl_alloc,
|
||||
sizeof(*knew) * spec->num_kctl_alloc);
|
||||
kfree(spec->kctl_alloc);
|
||||
}
|
||||
spec->kctl_alloc = knew;
|
||||
spec->num_kctl_alloc = num;
|
||||
}
|
||||
|
||||
knew = &spec->kctl_alloc[spec->num_kctl_used];
|
||||
snd_array_init(&spec->kctls, sizeof(*knew), 32);
|
||||
knew = snd_array_new(&spec->kctls);
|
||||
if (!knew)
|
||||
return -ENOMEM;
|
||||
*knew = vt1708_control_templates[type];
|
||||
knew->name = kstrdup(name, GFP_KERNEL);
|
||||
|
||||
if (!knew->name)
|
||||
return -ENOMEM;
|
||||
knew->private_value = val;
|
||||
spec->num_kctl_used++;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void via_free_kctls(struct hda_codec *codec)
|
||||
{
|
||||
struct via_spec *spec = codec->spec;
|
||||
|
||||
if (spec->kctls.list) {
|
||||
struct snd_kcontrol_new *kctl = spec->kctls.list;
|
||||
int i;
|
||||
for (i = 0; i < spec->kctls.used; i++)
|
||||
kfree(kctl[i].name);
|
||||
}
|
||||
snd_array_free(&spec->kctls);
|
||||
}
|
||||
|
||||
/* create input playback/capture controls for the given pin */
|
||||
static int via_new_analog_input(struct via_spec *spec, hda_nid_t pin,
|
||||
const char *ctlname, int idx, int mix_nid)
|
||||
|
@ -896,6 +887,7 @@ static int via_build_controls(struct hda_codec *codec)
|
|||
if (err < 0)
|
||||
return err;
|
||||
}
|
||||
via_free_kctls(codec); /* no longer needed */
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
@ -941,17 +933,11 @@ static int via_build_pcms(struct hda_codec *codec)
|
|||
static void via_free(struct hda_codec *codec)
|
||||
{
|
||||
struct via_spec *spec = codec->spec;
|
||||
unsigned int i;
|
||||
|
||||
if (!spec)
|
||||
return;
|
||||
|
||||
if (spec->kctl_alloc) {
|
||||
for (i = 0; i < spec->num_kctl_used; i++)
|
||||
kfree(spec->kctl_alloc[i].name);
|
||||
kfree(spec->kctl_alloc);
|
||||
}
|
||||
|
||||
via_free_kctls(codec);
|
||||
kfree(codec->spec);
|
||||
}
|
||||
|
||||
|
@ -1373,8 +1359,8 @@ static int vt1708_parse_auto_config(struct hda_codec *codec)
|
|||
if (spec->autocfg.dig_in_pin)
|
||||
spec->dig_in_nid = VT1708_DIGIN_NID;
|
||||
|
||||
if (spec->kctl_alloc)
|
||||
spec->mixers[spec->num_mixers++] = spec->kctl_alloc;
|
||||
if (spec->kctls.list)
|
||||
spec->mixers[spec->num_mixers++] = spec->kctls.list;
|
||||
|
||||
spec->init_verbs[spec->num_iverbs++] = vt1708_volume_init_verbs;
|
||||
|
||||
|
@ -1846,8 +1832,8 @@ static int vt1709_parse_auto_config(struct hda_codec *codec)
|
|||
if (spec->autocfg.dig_in_pin)
|
||||
spec->dig_in_nid = VT1709_DIGIN_NID;
|
||||
|
||||
if (spec->kctl_alloc)
|
||||
spec->mixers[spec->num_mixers++] = spec->kctl_alloc;
|
||||
if (spec->kctls.list)
|
||||
spec->mixers[spec->num_mixers++] = spec->kctls.list;
|
||||
|
||||
spec->input_mux = &spec->private_imux[0];
|
||||
|
||||
|
@ -2390,8 +2376,8 @@ static int vt1708B_parse_auto_config(struct hda_codec *codec)
|
|||
if (spec->autocfg.dig_in_pin)
|
||||
spec->dig_in_nid = VT1708B_DIGIN_NID;
|
||||
|
||||
if (spec->kctl_alloc)
|
||||
spec->mixers[spec->num_mixers++] = spec->kctl_alloc;
|
||||
if (spec->kctls.list)
|
||||
spec->mixers[spec->num_mixers++] = spec->kctls.list;
|
||||
|
||||
spec->input_mux = &spec->private_imux[0];
|
||||
|
||||
|
@ -2855,8 +2841,8 @@ static int vt1708S_parse_auto_config(struct hda_codec *codec)
|
|||
|
||||
spec->extra_dig_out_nid = 0x15;
|
||||
|
||||
if (spec->kctl_alloc)
|
||||
spec->mixers[spec->num_mixers++] = spec->kctl_alloc;
|
||||
if (spec->kctls.list)
|
||||
spec->mixers[spec->num_mixers++] = spec->kctls.list;
|
||||
|
||||
spec->input_mux = &spec->private_imux[0];
|
||||
|
||||
|
@ -3174,8 +3160,8 @@ static int vt1702_parse_auto_config(struct hda_codec *codec)
|
|||
|
||||
spec->extra_dig_out_nid = 0x1B;
|
||||
|
||||
if (spec->kctl_alloc)
|
||||
spec->mixers[spec->num_mixers++] = spec->kctl_alloc;
|
||||
if (spec->kctls.list)
|
||||
spec->mixers[spec->num_mixers++] = spec->kctls.list;
|
||||
|
||||
spec->input_mux = &spec->private_imux[0];
|
||||
|
||||
|
@ -3262,74 +3248,97 @@ static int patch_vt1702(struct hda_codec *codec)
|
|||
/*
|
||||
* patch entries
|
||||
*/
|
||||
struct hda_codec_preset snd_hda_preset_via[] = {
|
||||
{ .id = 0x11061708, .name = "VIA VT1708", .patch = patch_vt1708},
|
||||
{ .id = 0x11061709, .name = "VIA VT1708", .patch = patch_vt1708},
|
||||
{ .id = 0x1106170A, .name = "VIA VT1708", .patch = patch_vt1708},
|
||||
{ .id = 0x1106170B, .name = "VIA VT1708", .patch = patch_vt1708},
|
||||
{ .id = 0x1106E710, .name = "VIA VT1709 10-Ch",
|
||||
static struct hda_codec_preset snd_hda_preset_via[] = {
|
||||
{ .id = 0x11061708, .name = "VT1708", .patch = patch_vt1708},
|
||||
{ .id = 0x11061709, .name = "VT1708", .patch = patch_vt1708},
|
||||
{ .id = 0x1106170a, .name = "VT1708", .patch = patch_vt1708},
|
||||
{ .id = 0x1106170b, .name = "VT1708", .patch = patch_vt1708},
|
||||
{ .id = 0x1106e710, .name = "VT1709 10-Ch",
|
||||
.patch = patch_vt1709_10ch},
|
||||
{ .id = 0x1106E711, .name = "VIA VT1709 10-Ch",
|
||||
{ .id = 0x1106e711, .name = "VT1709 10-Ch",
|
||||
.patch = patch_vt1709_10ch},
|
||||
{ .id = 0x1106E712, .name = "VIA VT1709 10-Ch",
|
||||
{ .id = 0x1106e712, .name = "VT1709 10-Ch",
|
||||
.patch = patch_vt1709_10ch},
|
||||
{ .id = 0x1106E713, .name = "VIA VT1709 10-Ch",
|
||||
{ .id = 0x1106e713, .name = "VT1709 10-Ch",
|
||||
.patch = patch_vt1709_10ch},
|
||||
{ .id = 0x1106E714, .name = "VIA VT1709 6-Ch",
|
||||
{ .id = 0x1106e714, .name = "VT1709 6-Ch",
|
||||
.patch = patch_vt1709_6ch},
|
||||
{ .id = 0x1106E715, .name = "VIA VT1709 6-Ch",
|
||||
{ .id = 0x1106e715, .name = "VT1709 6-Ch",
|
||||
.patch = patch_vt1709_6ch},
|
||||
{ .id = 0x1106E716, .name = "VIA VT1709 6-Ch",
|
||||
{ .id = 0x1106e716, .name = "VT1709 6-Ch",
|
||||
.patch = patch_vt1709_6ch},
|
||||
{ .id = 0x1106E717, .name = "VIA VT1709 6-Ch",
|
||||
{ .id = 0x1106e717, .name = "VT1709 6-Ch",
|
||||
.patch = patch_vt1709_6ch},
|
||||
{ .id = 0x1106E720, .name = "VIA VT1708B 8-Ch",
|
||||
{ .id = 0x1106e720, .name = "VT1708B 8-Ch",
|
||||
.patch = patch_vt1708B_8ch},
|
||||
{ .id = 0x1106E721, .name = "VIA VT1708B 8-Ch",
|
||||
{ .id = 0x1106e721, .name = "VT1708B 8-Ch",
|
||||
.patch = patch_vt1708B_8ch},
|
||||
{ .id = 0x1106E722, .name = "VIA VT1708B 8-Ch",
|
||||
{ .id = 0x1106e722, .name = "VT1708B 8-Ch",
|
||||
.patch = patch_vt1708B_8ch},
|
||||
{ .id = 0x1106E723, .name = "VIA VT1708B 8-Ch",
|
||||
{ .id = 0x1106e723, .name = "VT1708B 8-Ch",
|
||||
.patch = patch_vt1708B_8ch},
|
||||
{ .id = 0x1106E724, .name = "VIA VT1708B 4-Ch",
|
||||
{ .id = 0x1106e724, .name = "VT1708B 4-Ch",
|
||||
.patch = patch_vt1708B_4ch},
|
||||
{ .id = 0x1106E725, .name = "VIA VT1708B 4-Ch",
|
||||
{ .id = 0x1106e725, .name = "VT1708B 4-Ch",
|
||||
.patch = patch_vt1708B_4ch},
|
||||
{ .id = 0x1106E726, .name = "VIA VT1708B 4-Ch",
|
||||
{ .id = 0x1106e726, .name = "VT1708B 4-Ch",
|
||||
.patch = patch_vt1708B_4ch},
|
||||
{ .id = 0x1106E727, .name = "VIA VT1708B 4-Ch",
|
||||
{ .id = 0x1106e727, .name = "VT1708B 4-Ch",
|
||||
.patch = patch_vt1708B_4ch},
|
||||
{ .id = 0x11060397, .name = "VIA VT1708S",
|
||||
{ .id = 0x11060397, .name = "VT1708S",
|
||||
.patch = patch_vt1708S},
|
||||
{ .id = 0x11061397, .name = "VIA VT1708S",
|
||||
{ .id = 0x11061397, .name = "VT1708S",
|
||||
.patch = patch_vt1708S},
|
||||
{ .id = 0x11062397, .name = "VIA VT1708S",
|
||||
{ .id = 0x11062397, .name = "VT1708S",
|
||||
.patch = patch_vt1708S},
|
||||
{ .id = 0x11063397, .name = "VIA VT1708S",
|
||||
{ .id = 0x11063397, .name = "VT1708S",
|
||||
.patch = patch_vt1708S},
|
||||
{ .id = 0x11064397, .name = "VIA VT1708S",
|
||||
{ .id = 0x11064397, .name = "VT1708S",
|
||||
.patch = patch_vt1708S},
|
||||
{ .id = 0x11065397, .name = "VIA VT1708S",
|
||||
{ .id = 0x11065397, .name = "VT1708S",
|
||||
.patch = patch_vt1708S},
|
||||
{ .id = 0x11066397, .name = "VIA VT1708S",
|
||||
{ .id = 0x11066397, .name = "VT1708S",
|
||||
.patch = patch_vt1708S},
|
||||
{ .id = 0x11067397, .name = "VIA VT1708S",
|
||||
{ .id = 0x11067397, .name = "VT1708S",
|
||||
.patch = patch_vt1708S},
|
||||
{ .id = 0x11060398, .name = "VIA VT1702",
|
||||
{ .id = 0x11060398, .name = "VT1702",
|
||||
.patch = patch_vt1702},
|
||||
{ .id = 0x11061398, .name = "VIA VT1702",
|
||||
{ .id = 0x11061398, .name = "VT1702",
|
||||
.patch = patch_vt1702},
|
||||
{ .id = 0x11062398, .name = "VIA VT1702",
|
||||
{ .id = 0x11062398, .name = "VT1702",
|
||||
.patch = patch_vt1702},
|
||||
{ .id = 0x11063398, .name = "VIA VT1702",
|
||||
{ .id = 0x11063398, .name = "VT1702",
|
||||
.patch = patch_vt1702},
|
||||
{ .id = 0x11064398, .name = "VIA VT1702",
|
||||
{ .id = 0x11064398, .name = "VT1702",
|
||||
.patch = patch_vt1702},
|
||||
{ .id = 0x11065398, .name = "VIA VT1702",
|
||||
{ .id = 0x11065398, .name = "VT1702",
|
||||
.patch = patch_vt1702},
|
||||
{ .id = 0x11066398, .name = "VIA VT1702",
|
||||
{ .id = 0x11066398, .name = "VT1702",
|
||||
.patch = patch_vt1702},
|
||||
{ .id = 0x11067398, .name = "VIA VT1702",
|
||||
{ .id = 0x11067398, .name = "VT1702",
|
||||
.patch = patch_vt1702},
|
||||
{} /* terminator */
|
||||
};
|
||||
|
||||
MODULE_ALIAS("snd-hda-codec-id:1106*");
|
||||
|
||||
static struct hda_codec_preset_list via_list = {
|
||||
.preset = snd_hda_preset_via,
|
||||
.owner = THIS_MODULE,
|
||||
};
|
||||
|
||||
MODULE_LICENSE("GPL");
|
||||
MODULE_DESCRIPTION("VIA HD-audio codec");
|
||||
|
||||
static int __init patch_via_init(void)
|
||||
{
|
||||
return snd_hda_add_codec_preset(&via_list);
|
||||
}
|
||||
|
||||
static void __exit patch_via_exit(void)
|
||||
{
|
||||
snd_hda_delete_codec_preset(&via_list);
|
||||
}
|
||||
|
||||
module_init(patch_via_init)
|
||||
module_exit(patch_via_exit)
|
||||
|
|
|
@ -382,23 +382,25 @@ static irqreturn_t snd_vt1724_interrupt(int irq, void *dev_id)
|
|||
unsigned char status_mask =
|
||||
VT1724_IRQ_MPU_RX | VT1724_IRQ_MPU_TX | VT1724_IRQ_MTPCM;
|
||||
int handled = 0;
|
||||
#ifdef CONFIG_SND_DEBUG
|
||||
int timeout = 0;
|
||||
#endif
|
||||
|
||||
while (1) {
|
||||
status = inb(ICEREG1724(ice, IRQSTAT));
|
||||
status &= status_mask;
|
||||
if (status == 0)
|
||||
break;
|
||||
#ifdef CONFIG_SND_DEBUG
|
||||
if (++timeout > 10) {
|
||||
printk(KERN_ERR
|
||||
"ice1724: Too long irq loop, status = 0x%x\n",
|
||||
status);
|
||||
status = inb(ICEREG1724(ice, IRQSTAT));
|
||||
printk(KERN_ERR "ice1724: Too long irq loop, "
|
||||
"status = 0x%x\n", status);
|
||||
if (status & VT1724_IRQ_MPU_TX) {
|
||||
printk(KERN_ERR "ice1724: Disabling MPU_TX\n");
|
||||
outb(inb(ICEREG1724(ice, IRQMASK)) |
|
||||
VT1724_IRQ_MPU_TX,
|
||||
ICEREG1724(ice, IRQMASK));
|
||||
}
|
||||
break;
|
||||
}
|
||||
#endif
|
||||
handled = 1;
|
||||
if (status & VT1724_IRQ_MPU_TX) {
|
||||
spin_lock(&ice->reg_lock);
|
||||
|
@ -2351,7 +2353,6 @@ static int __devinit snd_vt1724_create(struct snd_card *card,
|
|||
{
|
||||
struct snd_ice1712 *ice;
|
||||
int err;
|
||||
unsigned char mask;
|
||||
static struct snd_device_ops ops = {
|
||||
.dev_free = snd_vt1724_dev_free,
|
||||
};
|
||||
|
@ -2412,9 +2413,9 @@ static int __devinit snd_vt1724_create(struct snd_card *card,
|
|||
return -EIO;
|
||||
}
|
||||
|
||||
/* unmask used interrupts */
|
||||
mask = VT1724_IRQ_MPU_RX | VT1724_IRQ_MPU_TX;
|
||||
outb(mask, ICEREG1724(ice, IRQMASK));
|
||||
/* MPU_RX and TX irq masks are cleared later dynamically */
|
||||
outb(VT1724_IRQ_MPU_RX | VT1724_IRQ_MPU_TX , ICEREG1724(ice, IRQMASK));
|
||||
|
||||
/* don't handle FIFO overrun/underruns (just yet),
|
||||
* since they cause machine lockups
|
||||
*/
|
||||
|
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue