aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--Documentation/sound/alsa/ALSA-Configuration.txt60
-rw-r--r--Documentation/sound/alsa/DocBook/alsa-driver-api.tmpl4
-rw-r--r--Documentation/sound/alsa/DocBook/writing-an-alsa-driver.tmpl33
-rw-r--r--Documentation/sound/alsa/hda_codec.txt10
-rw-r--r--Documentation/sound/alsa/soc/DAI.txt56
-rw-r--r--Documentation/sound/alsa/soc/clocking.txt51
-rw-r--r--Documentation/sound/alsa/soc/codec.txt197
-rw-r--r--Documentation/sound/alsa/soc/dapm.txt297
-rw-r--r--Documentation/sound/alsa/soc/machine.txt113
-rw-r--r--Documentation/sound/alsa/soc/overview.txt83
-rw-r--r--Documentation/sound/alsa/soc/platform.txt58
-rw-r--r--Documentation/sound/alsa/soc/pops_clicks.txt52
-rw-r--r--MAINTAINERS6
-rw-r--r--drivers/input/touchscreen/ucb1400_ts.c2
-rw-r--r--include/linux/i2c-id.h2
-rw-r--r--include/sound/ac97_codec.h4
-rw-r--r--include/sound/ad1848.h2
-rw-r--r--include/sound/ak4114.h3
-rw-r--r--include/sound/ak4117.h2
-rw-r--r--include/sound/ak4xxx-adda.h6
-rw-r--r--include/sound/control.h5
-rw-r--r--include/sound/core.h62
-rw-r--r--include/sound/emu10k1.h418
-rw-r--r--include/sound/pcm.h4
-rw-r--r--include/sound/pt2258.h37
-rw-r--r--include/sound/sb16_csp.h14
-rw-r--r--include/sound/snd_wavefront.h2
-rw-r--r--include/sound/soc-dapm.h286
-rw-r--r--include/sound/soc.h461
-rw-r--r--include/sound/typedefs.h173
-rw-r--r--include/sound/version.h4
-rw-r--r--include/sound/vx_core.h2
-rw-r--r--include/sound/ymfpci.h6
-rw-r--r--sound/Kconfig2
-rw-r--r--sound/Makefile2
-rw-r--r--sound/ac97_bus.c4
-rw-r--r--sound/aoa/aoa.h2
-rw-r--r--sound/aoa/codecs/snd-aoa-codec-onyx.c11
-rw-r--r--sound/aoa/core/snd-aoa-alsa.c5
-rw-r--r--sound/aoa/core/snd-aoa-alsa.h2
-rw-r--r--sound/aoa/core/snd-aoa-core.c4
-rw-r--r--sound/aoa/fabrics/snd-aoa-fabric-layout.c13
-rw-r--r--sound/aoa/soundbus/i2sbus/i2sbus-core.c22
-rw-r--r--sound/aoa/soundbus/i2sbus/i2sbus-pcm.c407
-rw-r--r--sound/aoa/soundbus/i2sbus/i2sbus.h6
-rw-r--r--sound/arm/aaci.h2
-rw-r--r--sound/core/control.c42
-rw-r--r--sound/core/control_compat.c5
-rw-r--r--sound/core/device.c24
-rw-r--r--sound/core/hwdep.c19
-rw-r--r--sound/core/init.c22
-rw-r--r--sound/core/memalloc.c10
-rw-r--r--sound/core/misc.c28
-rw-r--r--sound/core/pcm.c50
-rw-r--r--sound/core/pcm_lib.c5
-rw-r--r--sound/core/pcm_memory.c23
-rw-r--r--sound/core/rawmidi.c29
-rw-r--r--sound/core/seq/seq_clientmgr.c14
-rw-r--r--sound/core/seq/seq_device.c25
-rw-r--r--sound/core/seq/seq_ports.c51
-rw-r--r--sound/core/seq/seq_virmidi.c4
-rw-r--r--sound/core/sound.c15
-rw-r--r--sound/core/timer.c80
-rw-r--r--sound/drivers/Kconfig11
-rw-r--r--sound/drivers/Makefile2
-rw-r--r--sound/drivers/dummy.c2
-rw-r--r--sound/drivers/portman2x4.c876
-rw-r--r--sound/drivers/serial-u16550.c221
-rw-r--r--sound/drivers/vx/vx_mixer.c2
-rw-r--r--sound/i2c/Makefile1
-rw-r--r--sound/i2c/other/Makefile4
-rw-r--r--sound/i2c/other/ak4114.c29
-rw-r--r--sound/i2c/other/ak4117.c2
-rw-r--r--sound/i2c/other/ak4xxx-adda.c110
-rw-r--r--sound/i2c/other/pt2258.c233
-rw-r--r--sound/isa/Kconfig2
-rw-r--r--sound/isa/ad1816a/ad1816a_lib.c10
-rw-r--r--sound/isa/ad1848/ad1848_lib.c6
-rw-r--r--sound/isa/gus/gus_main.c6
-rw-r--r--sound/isa/opl3sa2.c4
-rw-r--r--sound/isa/sb/sb16_csp.c61
-rw-r--r--sound/isa/wavefront/wavefront.c1
-rw-r--r--sound/isa/wavefront/wavefront_fx.c812
-rw-r--r--sound/isa/wavefront/yss225.c2739
-rw-r--r--sound/pci/Kconfig30
-rw-r--r--sound/pci/ac97/ac97_codec.c64
-rw-r--r--sound/pci/ac97/ac97_patch.c549
-rw-r--r--sound/pci/ac97/ac97_patch.h1
-rw-r--r--sound/pci/ac97/ak4531_codec.c6
-rw-r--r--sound/pci/als300.c6
-rw-r--r--sound/pci/atiixp.c31
-rw-r--r--sound/pci/atiixp_modem.c2
-rw-r--r--sound/pci/ca0106/ca0106_main.c21
-rw-r--r--sound/pci/ca0106/ca0106_mixer.c50
-rw-r--r--sound/pci/cs4281.c2
-rw-r--r--sound/pci/echoaudio/darla20.c1
-rw-r--r--sound/pci/echoaudio/darla24.c1
-rw-r--r--sound/pci/echoaudio/echo3g.c1
-rw-r--r--sound/pci/echoaudio/echo3g_dsp.c2
-rw-r--r--sound/pci/echoaudio/echoaudio.c18
-rw-r--r--sound/pci/echoaudio/gina20.c1
-rw-r--r--sound/pci/echoaudio/gina24.c1
-rw-r--r--sound/pci/echoaudio/indigo.c1
-rw-r--r--sound/pci/echoaudio/indigodj.c1
-rw-r--r--sound/pci/echoaudio/indigoio.c1
-rw-r--r--sound/pci/echoaudio/layla20.c1
-rw-r--r--sound/pci/echoaudio/layla24.c1
-rw-r--r--sound/pci/echoaudio/mia.c1
-rw-r--r--sound/pci/echoaudio/mona.c1
-rw-r--r--sound/pci/emu10k1/emu10k1_main.c616
-rw-r--r--sound/pci/emu10k1/emu10k1x.c6
-rw-r--r--sound/pci/emu10k1/emufx.c204
-rw-r--r--sound/pci/emu10k1/emumixer.c745
-rw-r--r--sound/pci/emu10k1/emupcm.c147
-rw-r--r--sound/pci/emu10k1/emuproc.c34
-rw-r--r--sound/pci/emu10k1/io.c104
-rw-r--r--sound/pci/emu10k1/p16v.c14
-rw-r--r--sound/pci/emu10k1/p17v.h47
-rw-r--r--sound/pci/emu10k1/voice.c2
-rw-r--r--sound/pci/ens1370.c154
-rw-r--r--sound/pci/es1938.c2
-rw-r--r--sound/pci/fm801.c2
-rw-r--r--sound/pci/hda/Makefile11
-rw-r--r--sound/pci/hda/hda_codec.c68
-rw-r--r--sound/pci/hda/hda_intel.c33
-rw-r--r--sound/pci/hda/hda_local.h12
-rw-r--r--sound/pci/hda/hda_patch.h6
-rw-r--r--sound/pci/hda/patch_analog.c165
-rw-r--r--sound/pci/hda/patch_cmedia.c24
-rw-r--r--sound/pci/hda/patch_conexant.c1311
-rw-r--r--sound/pci/hda/patch_realtek.c2367
-rw-r--r--sound/pci/hda/patch_sigmatel.c692
-rw-r--r--sound/pci/hda/patch_via.c1396
-rw-r--r--sound/pci/ice1712/Makefile2
-rw-r--r--sound/pci/ice1712/amp.c4
-rw-r--r--sound/pci/ice1712/amp.h2
-rw-r--r--sound/pci/ice1712/aureon.c186
-rw-r--r--sound/pci/ice1712/aureon.h6
-rw-r--r--sound/pci/ice1712/delta.c34
-rw-r--r--sound/pci/ice1712/delta.h2
-rw-r--r--sound/pci/ice1712/ews.c24
-rw-r--r--sound/pci/ice1712/ews.h2
-rw-r--r--sound/pci/ice1712/hoontech.c7
-rw-r--r--sound/pci/ice1712/hoontech.h2
-rw-r--r--sound/pci/ice1712/ice1712.c74
-rw-r--r--sound/pci/ice1712/ice1712.h18
-rw-r--r--sound/pci/ice1712/ice1724.c78
-rw-r--r--sound/pci/ice1712/juli.c36
-rw-r--r--sound/pci/ice1712/juli.h2
-rw-r--r--sound/pci/ice1712/phase.c76
-rw-r--r--sound/pci/ice1712/phase.h2
-rw-r--r--sound/pci/ice1712/pontis.c42
-rw-r--r--sound/pci/ice1712/pontis.h2
-rw-r--r--sound/pci/ice1712/prodigy192.c40
-rw-r--r--sound/pci/ice1712/prodigy192.h2
-rw-r--r--sound/pci/ice1712/revo.c372
-rw-r--r--sound/pci/ice1712/revo.h13
-rw-r--r--sound/pci/ice1712/vt1720_mobo.c59
-rw-r--r--sound/pci/ice1712/vt1720_mobo.h2
-rw-r--r--sound/pci/ice1712/wtm.c542
-rw-r--r--sound/pci/ice1712/wtm.h20
-rw-r--r--sound/pci/intel8x0.c208
-rw-r--r--sound/pci/intel8x0m.c120
-rw-r--r--sound/pci/korg1212/korg1212.c45
-rw-r--r--sound/pci/maestro3.c373
-rw-r--r--sound/pci/mixart/mixart_mixer.c4
-rw-r--r--sound/pci/nm256/nm256.c56
-rw-r--r--sound/pci/pcxhr/pcxhr_mixer.c6
-rw-r--r--sound/pci/rme9652/hdsp.c38
-rw-r--r--sound/pci/rme9652/hdspm.c1242
-rw-r--r--sound/pci/trident/trident_main.c4
-rw-r--r--sound/pci/via82xx.c134
-rw-r--r--sound/pci/via82xx_modem.c2
-rw-r--r--sound/pci/vx222/vx222.c4
-rw-r--r--sound/pci/vx222/vx222_ops.c2
-rw-r--r--sound/pci/ymfpci/ymfpci_image.h6
-rw-r--r--sound/pci/ymfpci/ymfpci_main.c171
-rw-r--r--sound/pcmcia/vx/vxp_mixer.c2
-rw-r--r--sound/pcmcia/vx/vxpocket.c2
-rw-r--r--sound/soc/Kconfig32
-rw-r--r--sound/soc/Makefile4
-rw-r--r--sound/soc/at91/Kconfig32
-rw-r--r--sound/soc/at91/Makefile11
-rw-r--r--sound/soc/at91/at91-i2s.c720
-rw-r--r--sound/soc/at91/at91-i2s.h27
-rw-r--r--sound/soc/at91/at91-pcm.c432
-rw-r--r--sound/soc/at91/at91-pcm.h72
-rw-r--r--sound/soc/at91/eti_b1_wm8731.c375
-rw-r--r--sound/soc/codecs/Kconfig15
-rw-r--r--sound/soc/codecs/Makefile9
-rw-r--r--sound/soc/codecs/ac97.c156
-rw-r--r--sound/soc/codecs/ac97.h18
-rw-r--r--sound/soc/codecs/wm8731.c758
-rw-r--r--sound/soc/codecs/wm8731.h44
-rw-r--r--sound/soc/codecs/wm8750.c1049
-rw-r--r--sound/soc/codecs/wm8750.h67
-rw-r--r--sound/soc/codecs/wm9712.c771
-rw-r--r--sound/soc/codecs/wm9712.h14
-rw-r--r--sound/soc/pxa/Kconfig60
-rw-r--r--sound/soc/pxa/Makefile20
-rw-r--r--sound/soc/pxa/corgi.c383
-rw-r--r--sound/soc/pxa/poodle.c352
-rw-r--r--sound/soc/pxa/pxa2xx-ac97.c431
-rw-r--r--sound/soc/pxa/pxa2xx-ac97.h22
-rw-r--r--sound/soc/pxa/pxa2xx-i2s.c318
-rw-r--r--sound/soc/pxa/pxa2xx-i2s.h20
-rw-r--r--sound/soc/pxa/pxa2xx-pcm.c372
-rw-r--r--sound/soc/pxa/pxa2xx-pcm.h34
-rw-r--r--sound/soc/pxa/spitz.c394
-rw-r--r--sound/soc/pxa/tosa.c289
-rw-r--r--sound/soc/soc-core.c1587
-rw-r--r--sound/soc/soc-dapm.c1323
-rw-r--r--sound/sparc/dbri.c2
-rw-r--r--sound/usb/usbaudio.c189
-rw-r--r--sound/usb/usbaudio.h1
-rw-r--r--sound/usb/usbquirks.h32
216 files changed, 28241 insertions, 3797 deletions
diff --git a/Documentation/sound/alsa/ALSA-Configuration.txt b/Documentation/sound/alsa/ALSA-Configuration.txt
index 9fef210ab50a..c30ff1bb2d10 100644
--- a/Documentation/sound/alsa/ALSA-Configuration.txt
+++ b/Documentation/sound/alsa/ALSA-Configuration.txt
@@ -242,6 +242,12 @@ Prior to version 0.9.0rc4 options had a 'snd_' prefix. This was removed.
ac97_clock - AC'97 clock (default = 48000)
ac97_quirk - AC'97 workaround for strange hardware
See "AC97 Quirk Option" section below.
+ ac97_codec - Workaround to specify which AC'97 codec
+ instead of probing. If this works for you
+ file a bug with your `lspci -vn` output.
+ -2 -- Force probing.
+ -1 -- Default behavior.
+ 0-2 -- Use the specified codec.
spdif_aclink - S/PDIF transfer over AC-link (default = 1)
This module supports one card and autoprobe.
@@ -779,6 +785,7 @@ Prior to version 0.9.0rc4 options had a 'snd_' prefix. This was removed.
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
@@ -800,14 +807,18 @@ Prior to version 0.9.0rc4 options had a 'snd_' prefix. This was removed.
ALC262
fujitsu Fujitsu Laptop
hp-bpc HP xw4400/6400/8400/9400 laptops
+ hp-bpc-d7000 HP BPC D7000
benq Benq ED8
+ hippo Hippo (ATI) with jack detection, Sony UX-90s
+ hippo_1 Hippo (Benq) with jack detection
basic fixed pin assignment w/o SPDIF
auto auto-config reading BIOS (default)
ALC882/885
3stack-dig 3-jack with SPDIF I/O
- 6stck-dig 6-jack digital with SPDIF I/O
+ 6stack-dig 6-jack digital with SPDIF I/O
arima Arima W820Di1
+ macpro MacPro support
auto auto-config reading BIOS (default)
ALC883/888
@@ -817,6 +828,10 @@ Prior to version 0.9.0rc4 options had a 'snd_' prefix. This was removed.
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)
+ medion Medion Laptops
+ 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)
auto auto-config reading BIOS (default)
ALC861/660
@@ -825,6 +840,16 @@ Prior to version 0.9.0rc4 options had a 'snd_' prefix. This was removed.
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)
auto auto-config reading BIOS (default)
CMI9880
@@ -845,6 +870,7 @@ Prior to version 0.9.0rc4 options had a 'snd_' prefix. This was removed.
3stack 3-stack, shared surrounds
laptop 2-channel only (FSC V2060, Samsung M50)
laptop-eapd 2-channel with EAPD (Samsung R65, ASUS A6J)
+ ultra 2-channel with EAPD (Samsung Ultra tablet PC)
AD1988
6stack 6-jack
@@ -854,12 +880,31 @@ Prior to version 0.9.0rc4 options had a 'snd_' prefix. This was removed.
laptop 3-jack with hp-jack automute
laptop-dig ditto with SPDIF
auto auto-config reading BIOS (default)
+
+ Conexant 5045
+ laptop Laptop config
+ 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
STAC9200/9205/9220/9221/9254
ref Reference board
3stack D945 3stack
5stack D945 5stack + SPDIF
+ STAC9202/9250/9251
+ ref Reference board, base config
+ m2-2 Some Gateway MX series laptops
+ m6 Some Gateway NX series laptops
+
STAC9227/9228/9229/927x
ref Reference board
3stack D965 3stack
@@ -974,6 +1019,7 @@ Prior to version 0.9.0rc4 options had a 'snd_' prefix. This was removed.
Module for Envy24HT (VT/ICE1724), Envy24PT (VT1720) based PCI sound cards.
* MidiMan M Audio Revolution 5.1
* MidiMan M Audio Revolution 7.1
+ * MidiMan M Audio Audiophile 192
* AMP Ltd AUDIO2000
* TerraTec Aureon 5.1 Sky
* TerraTec Aureon 7.1 Space
@@ -993,7 +1039,7 @@ Prior to version 0.9.0rc4 options had a 'snd_' prefix. This was removed.
model - Use the given board model, one of the following:
revo51, revo71, amp2000, prodigy71, prodigy71lt,
- prodigy192, aureon51, aureon71, universe,
+ prodigy192, aureon51, aureon71, universe, ap192,
k8x800, phase22, phase28, ms300, av710
This module supports multiple cards and autoprobe.
@@ -1049,6 +1095,9 @@ Prior to version 0.9.0rc4 options had a 'snd_' prefix. This was removed.
buggy_semaphore - Enable workaround for hardwares with buggy
semaphores (e.g. on some ASUS laptops)
(default off)
+ spdif_aclink - Use S/PDIF over AC-link instead of direct connection
+ from the controller chip
+ (0 = off, 1 = on, -1 = default)
This module supports one chip and autoprobe.
@@ -1371,6 +1420,13 @@ Prior to version 0.9.0rc4 options had a 'snd_' prefix. This was removed.
This module supports multiple cards.
+ Module snd-portman2x4
+ ---------------------
+
+ Module for Midiman Portman 2x4 parallel port MIDI interface
+
+ This module supports multiple cards.
+
Module snd-powermac (on ppc only)
---------------------------------
diff --git a/Documentation/sound/alsa/DocBook/alsa-driver-api.tmpl b/Documentation/sound/alsa/DocBook/alsa-driver-api.tmpl
index 1f3ae3e32d69..c4d2e3507af9 100644
--- a/Documentation/sound/alsa/DocBook/alsa-driver-api.tmpl
+++ b/Documentation/sound/alsa/DocBook/alsa-driver-api.tmpl
@@ -36,7 +36,7 @@
</bookinfo>
<chapter><title>Management of Cards and Devices</title>
- <sect1><title>Card Managment</title>
+ <sect1><title>Card Management</title>
!Esound/core/init.c
</sect1>
<sect1><title>Device Components</title>
@@ -59,7 +59,7 @@
<sect1><title>PCM Format Helpers</title>
!Esound/core/pcm_misc.c
</sect1>
- <sect1><title>PCM Memory Managment</title>
+ <sect1><title>PCM Memory Management</title>
!Esound/core/pcm_memory.c
</sect1>
</chapter>
diff --git a/Documentation/sound/alsa/DocBook/writing-an-alsa-driver.tmpl b/Documentation/sound/alsa/DocBook/writing-an-alsa-driver.tmpl
index ccd0a953953d..74d3a35b59bc 100644
--- a/Documentation/sound/alsa/DocBook/writing-an-alsa-driver.tmpl
+++ b/Documentation/sound/alsa/DocBook/writing-an-alsa-driver.tmpl
@@ -1360,8 +1360,7 @@
<informalexample>
<programlisting>
<![CDATA[
- static irqreturn_t snd_mychip_interrupt(int irq, void *dev_id,
- struct pt_regs *regs)
+ static irqreturn_t snd_mychip_interrupt(int irq, void *dev_id)
{
struct mychip *chip = dev_id;
....
@@ -2127,7 +2126,7 @@
accessible via <constant>substream-&gt;runtime</constant>.
This runtime pointer holds the various information; it holds
the copy of hw_params and sw_params configurations, the buffer
- pointers, mmap records, spinlocks, etc. Almost everyhing you
+ pointers, mmap records, spinlocks, etc. Almost everything you
need for controlling the PCM can be found there.
</para>
@@ -2340,7 +2339,7 @@ struct _snd_pcm_runtime {
<para>
When the PCM substreams can be synchronized (typically,
- synchorinized start/stop of a playback and a capture streams),
+ synchronized start/stop of a playback and a capture streams),
you can give <constant>SNDRV_PCM_INFO_SYNC_START</constant>,
too. In this case, you'll need to check the linked-list of
PCM substreams in the trigger callback. This will be
@@ -3062,8 +3061,7 @@ struct _snd_pcm_runtime {
<title>Interrupt Handler Case #1</title>
<programlisting>
<![CDATA[
- static irqreturn_t snd_mychip_interrupt(int irq, void *dev_id,
- struct pt_regs *regs)
+ static irqreturn_t snd_mychip_interrupt(int irq, void *dev_id)
{
struct mychip *chip = dev_id;
spin_lock(&chip->lock);
@@ -3106,8 +3104,7 @@ struct _snd_pcm_runtime {
<title>Interrupt Handler Case #2</title>
<programlisting>
<![CDATA[
- static irqreturn_t snd_mychip_interrupt(int irq, void *dev_id,
- struct pt_regs *regs)
+ static irqreturn_t snd_mychip_interrupt(int irq, void *dev_id)
{
struct mychip *chip = dev_id;
spin_lock(&chip->lock);
@@ -3247,7 +3244,7 @@ struct _snd_pcm_runtime {
You can even define your own constraint rules.
For example, let's suppose my_chip can manage a substream of 1 channel
if and only if the format is S16_LE, otherwise it supports any format
- specified in the <structname>snd_pcm_hardware</structname> stucture (or in any
+ specified in the <structname>snd_pcm_hardware</structname> structure (or in any
other constraint_list). You can build a rule like this:
<example>
@@ -3691,16 +3688,6 @@ struct _snd_pcm_runtime {
</para>
<para>
- Here, the chip instance is retrieved via
- <function>snd_kcontrol_chip()</function> macro. This macro
- just accesses to kcontrol-&gt;private_data. The
- kcontrol-&gt;private_data field is
- given as the argument of <function>snd_ctl_new()</function>
- (see the later subsection
- <link linkend="control-interface-constructor"><citetitle>Constructor</citetitle></link>).
- </para>
-
- <para>
The <structfield>value</structfield> field is depending on
the type of control as well as on info callback. For example,
the sb driver uses this field to store the register offset,
@@ -3780,7 +3767,7 @@ struct _snd_pcm_runtime {
<para>
Like <structfield>get</structfield> callback,
when the control has more than one elements,
- all elemehts must be evaluated in this callback, too.
+ all elements must be evaluated in this callback, too.
</para>
</section>
@@ -5541,12 +5528,12 @@ struct _snd_pcm_runtime {
#ifdef CONFIG_PM
static int snd_my_suspend(struct pci_dev *pci, pm_message_t state)
{
- .... /* do things for suspsend */
+ .... /* do things for suspend */
return 0;
}
static int snd_my_resume(struct pci_dev *pci)
{
- .... /* do things for suspsend */
+ .... /* do things for suspend */
return 0;
}
#endif
@@ -6111,7 +6098,7 @@ struct _snd_pcm_runtime {
<!-- ****************************************************** -->
<!-- Acknowledgments -->
<!-- ****************************************************** -->
- <chapter id="acknowledments">
+ <chapter id="acknowledgments">
<title>Acknowledgments</title>
<para>
I would like to thank Phil Kerr for his help for improvement and
diff --git a/Documentation/sound/alsa/hda_codec.txt b/Documentation/sound/alsa/hda_codec.txt
index 0be57ed81302..4eaae2a45534 100644
--- a/Documentation/sound/alsa/hda_codec.txt
+++ b/Documentation/sound/alsa/hda_codec.txt
@@ -277,11 +277,11 @@ Helper Functions
snd_hda_get_codec_name() stores the codec name on the given string.
snd_hda_check_board_config() can be used to obtain the configuration
-information matching with the device. Define the table with struct
-hda_board_config entries (zero-terminated), and pass it to the
-function. The function checks the modelname given as a module
-parameter, and PCI subsystem IDs. If the matching entry is found, it
-returns the config field value.
+information matching with the device. Define the model string table
+and the table with struct snd_pci_quirk entries (zero-terminated),
+and pass it to the function. The function checks the modelname given
+as a module parameter, and PCI subsystem IDs. If the matching entry
+is found, it returns the config field value.
snd_hda_add_new_ctls() can be used to create and add control entries.
Pass the zero-terminated array of struct snd_kcontrol_new. The same array
diff --git a/Documentation/sound/alsa/soc/DAI.txt b/Documentation/sound/alsa/soc/DAI.txt
new file mode 100644
index 000000000000..58cbfd01ea8f
--- /dev/null
+++ b/Documentation/sound/alsa/soc/DAI.txt
@@ -0,0 +1,56 @@
+ASoC currently supports the three main Digital Audio Interfaces (DAI) found on
+SoC controllers and portable audio CODECS today, namely AC97, I2S and PCM.
+
+
+AC97
+====
+
+ AC97 is a five wire interface commonly found on many PC sound cards. It is
+now also popular in many portable devices. This DAI has a reset line and time
+multiplexes its data on its SDATA_OUT (playback) and SDATA_IN (capture) lines.
+The bit clock (BCLK) is always driven by the CODEC (usually 12.288MHz) and the
+frame (FRAME) (usually 48kHz) is always driven by the controller. Each AC97
+frame is 21uS long and is divided into 13 time slots.
+
+The AC97 specification can be found at :-
+http://www.intel.com/design/chipsets/audio/ac97_r23.pdf
+
+
+I2S
+===
+
+ I2S is a common 4 wire DAI used in HiFi, STB and portable devices. The Tx and
+Rx lines are used for audio transmision, whilst the bit clock (BCLK) and
+left/right clock (LRC) synchronise the link. I2S is flexible in that either the
+controller or CODEC can drive (master) the BCLK and LRC clock lines. Bit clock
+usually varies depending on the sample rate and the master system clock
+(SYSCLK). LRCLK is the same as the sample rate. A few devices support separate
+ADC and DAC LRCLK's, this allows for similtanious capture and playback at
+different sample rates.
+
+I2S has several different operating modes:-
+
+ o I2S - MSB is transmitted on the falling edge of the first BCLK after LRC
+ transition.
+
+ o Left Justified - MSB is transmitted on transition of LRC.
+
+ o Right Justified - MSB is transmitted sample size BCLK's before LRC
+ transition.
+
+PCM
+===
+
+PCM is another 4 wire interface, very similar to I2S, that can support a more
+flexible protocol. It has bit clock (BCLK) and sync (SYNC) lines that are used
+to synchronise the link whilst the Tx and Rx lines are used to transmit and
+receive the audio data. Bit clock usually varies depending on sample rate
+whilst sync runs at the sample rate. PCM also supports Time Division
+Multiplexing (TDM) in that several devices can use the bus similtaniuosly (This
+is sometimes referred to as network mode).
+
+Common PCM operating modes:-
+
+ o Mode A - MSB is transmitted on falling edge of first BCLK after FRAME/SYNC.
+
+ o Mode B - MSB is transmitted on rising edge of FRAME/SYNC.
diff --git a/Documentation/sound/alsa/soc/clocking.txt b/Documentation/sound/alsa/soc/clocking.txt
new file mode 100644
index 000000000000..e93960d53a1e
--- /dev/null
+++ b/Documentation/sound/alsa/soc/clocking.txt
@@ -0,0 +1,51 @@
+Audio Clocking
+==============
+
+This text describes the audio clocking terms in ASoC and digital audio in
+general. Note: Audio clocking can be complex !
+
+
+Master Clock
+------------
+
+Every audio subsystem is driven by a master clock (sometimes refered to as MCLK
+or SYSCLK). This audio master clock can be derived from a number of sources
+(e.g. crystal, PLL, CPU clock) and is responsible for producing the correct
+audio playback and capture sample rates.
+
+Some master clocks (e.g. PLL's and CPU based clocks) are configuarble in that
+their speed can be altered by software (depending on the system use and to save
+power). Other master clocks are fixed at at set frequency (i.e. crystals).
+
+
+DAI Clocks
+----------
+The Digital Audio Interface is usually driven by a Bit Clock (often referred to
+as BCLK). This clock is used to drive the digital audio data across the link
+between the codec and CPU.
+
+The DAI also has a frame clock to signal the start of each audio frame. This
+clock is sometimes referred to as LRC (left right clock) or FRAME. This clock
+runs at exactly the sample rate (LRC = Rate).
+
+Bit Clock can be generated as follows:-
+
+BCLK = MCLK / x
+
+ or
+
+BCLK = LRC * x
+
+ or
+
+BCLK = LRC * Channels * Word Size
+
+This relationship depends on the codec or SoC CPU in particular. In general
+it's best to configure BCLK to the lowest possible speed (depending on your
+rate, number of channels and wordsize) to save on power.
+
+It's also desireable to use the codec (if possible) to drive (or master) the
+audio clocks as it's usually gives more accurate sample rates than the CPU.
+
+
+
diff --git a/Documentation/sound/alsa/soc/codec.txt b/Documentation/sound/alsa/soc/codec.txt
new file mode 100644
index 000000000000..48983c75aad9
--- /dev/null
+++ b/Documentation/sound/alsa/soc/codec.txt
@@ -0,0 +1,197 @@
+ASoC Codec Driver
+=================
+
+The codec driver is generic and hardware independent code that configures the
+codec to provide audio capture and playback. It should contain no code that is
+specific to the target platform or machine. All platform and machine specific
+code should be added to the platform and machine drivers respectively.
+
+Each codec driver *must* provide the following features:-
+
+ 1) Codec DAI and PCM configuration
+ 2) Codec control IO - using I2C, 3 Wire(SPI) or both API's
+ 3) Mixers and audio controls
+ 4) Codec audio operations
+
+Optionally, codec drivers can also provide:-
+
+ 5) DAPM description.
+ 6) DAPM event handler.
+ 7) DAC Digital mute control.
+
+It's probably best to use this guide in conjuction with the existing codec
+driver code in sound/soc/codecs/
+
+ASoC Codec driver breakdown
+===========================
+
+1 - Codec DAI and PCM configuration
+-----------------------------------
+Each codec driver must have a struct snd_soc_codec_dai to define it's DAI and
+PCM's capablities and operations. This struct is exported so that it can be
+registered with the core by your machine driver.
+
+e.g.
+
+struct snd_soc_codec_dai wm8731_dai = {
+ .name = "WM8731",
+ /* playback capabilities */
+ .playback = {
+ .stream_name = "Playback",
+ .channels_min = 1,
+ .channels_max = 2,
+ .rates = WM8731_RATES,
+ .formats = WM8731_FORMATS,},
+ /* capture capabilities */
+ .capture = {
+ .stream_name = "Capture",
+ .channels_min = 1,
+ .channels_max = 2,
+ .rates = WM8731_RATES,
+ .formats = WM8731_FORMATS,},
+ /* pcm operations - see section 4 below */
+ .ops = {
+ .prepare = wm8731_pcm_prepare,
+ .hw_params = wm8731_hw_params,
+ .shutdown = wm8731_shutdown,
+ },
+ /* DAI operations - see DAI.txt */
+ .dai_ops = {
+ .digital_mute = wm8731_mute,
+ .set_sysclk = wm8731_set_dai_sysclk,
+ .set_fmt = wm8731_set_dai_fmt,
+ }
+};
+EXPORT_SYMBOL_GPL(wm8731_dai);
+
+
+2 - Codec control IO
+--------------------
+The codec can ususally be controlled via an I2C or SPI style interface (AC97
+combines control with data in the DAI). The codec drivers will have to provide
+functions to read and write the codec registers along with supplying a register
+cache:-
+
+ /* IO control data and register cache */
+ void *control_data; /* codec control (i2c/3wire) data */
+ void *reg_cache;
+
+Codec read/write should do any data formatting and call the hardware read write
+below to perform the IO. These functions are called by the core and alsa when
+performing DAPM or changing the mixer:-
+
+ unsigned int (*read)(struct snd_soc_codec *, unsigned int);
+ int (*write)(struct snd_soc_codec *, unsigned int, unsigned int);
+
+Codec hardware IO functions - usually points to either the I2C, SPI or AC97
+read/write:-
+
+ hw_write_t hw_write;
+ hw_read_t hw_read;
+
+
+3 - Mixers and audio controls
+-----------------------------
+All the codec mixers and audio controls can be defined using the convenience
+macros defined in soc.h.
+
+ #define SOC_SINGLE(xname, reg, shift, mask, invert)
+
+Defines a single control as follows:-
+
+ xname = Control name e.g. "Playback Volume"
+ reg = codec register
+ shift = control bit(s) offset in register
+ mask = control bit size(s) e.g. mask of 7 = 3 bits
+ invert = the control is inverted
+
+Other macros include:-
+
+ #define SOC_DOUBLE(xname, reg, shift_left, shift_right, mask, invert)
+
+A stereo control
+
+ #define SOC_DOUBLE_R(xname, reg_left, reg_right, shift, mask, invert)
+
+A stereo control spanning 2 registers
+
+ #define SOC_ENUM_SINGLE(xreg, xshift, xmask, xtexts)
+
+Defines an single enumerated control as follows:-
+
+ xreg = register
+ xshift = control bit(s) offset in register
+ xmask = control bit(s) size
+ xtexts = pointer to array of strings that describe each setting
+
+ #define SOC_ENUM_DOUBLE(xreg, xshift_l, xshift_r, xmask, xtexts)
+
+Defines a stereo enumerated control
+
+
+4 - Codec Audio Operations
+--------------------------
+The codec driver also supports the following alsa operations:-
+
+/* SoC audio ops */
+struct snd_soc_ops {
+ int (*startup)(struct snd_pcm_substream *);
+ void (*shutdown)(struct snd_pcm_substream *);
+ int (*hw_params)(struct snd_pcm_substream *, struct snd_pcm_hw_params *);
+ int (*hw_free)(struct snd_pcm_substream *);
+ int (*prepare)(struct snd_pcm_substream *);
+};
+
+Please refer to the alsa driver PCM documentation for details.
+http://www.alsa-project.org/~iwai/writing-an-alsa-driver/c436.htm
+
+
+5 - DAPM description.
+---------------------
+The Dynamic Audio Power Management description describes the codec's power
+components, their relationships and registers to the ASoC core. Please read
+dapm.txt for details of building the description.
+
+Please also see the examples in other codec drivers.
+
+
+6 - DAPM event handler
+----------------------
+This function is a callback that handles codec domain PM calls and system
+domain PM calls (e.g. suspend and resume). It's used to put the codec to sleep
+when not in use.
+
+Power states:-
+
+ SNDRV_CTL_POWER_D0: /* full On */
+ /* vref/mid, clk and osc on, active */
+
+ SNDRV_CTL_POWER_D1: /* partial On */
+ SNDRV_CTL_POWER_D2: /* partial On */
+
+ SNDRV_CTL_POWER_D3hot: /* Off, with power */
+ /* everything off except vref/vmid, inactive */
+
+ SNDRV_CTL_POWER_D3cold: /* Everything Off, without power */
+
+
+7 - Codec DAC digital mute control.
+------------------------------------
+Most codecs have a digital mute before the DAC's that can be used to minimise
+any system noise. The mute stops any digital data from entering the DAC.
+
+A callback can be created that is called by the core for each codec DAI when the
+mute is applied or freed.
+
+i.e.
+
+static int wm8974_mute(struct snd_soc_codec *codec,
+ struct snd_soc_codec_dai *dai, int mute)
+{
+ u16 mute_reg = wm8974_read_reg_cache(codec, WM8974_DAC) & 0xffbf;
+ if(mute)
+ wm8974_write(codec, WM8974_DAC, mute_reg | 0x40);
+ else
+ wm8974_write(codec, WM8974_DAC, mute_reg);
+ return 0;
+}
diff --git a/Documentation/sound/alsa/soc/dapm.txt b/Documentation/sound/alsa/soc/dapm.txt
new file mode 100644
index 000000000000..c11877f5b4a1
--- /dev/null
+++ b/Documentation/sound/alsa/soc/dapm.txt
@@ -0,0 +1,297 @@
+Dynamic Audio Power Management for Portable Devices
+===================================================
+
+1. Description
+==============
+
+Dynamic Audio Power Management (DAPM) is designed to allow portable Linux devices
+to use the minimum amount of power within the audio subsystem at all times. It
+is independent of other kernel PM and as such, can easily co-exist with the
+other PM systems.
+
+DAPM is also completely transparent to all user space applications as all power
+switching is done within the ASoC core. No code changes or recompiling are
+required for user space applications. DAPM makes power switching descisions based
+upon any audio stream (capture/playback) activity and audio mixer settings
+within the device.
+
+DAPM spans the whole machine. It covers power control within the entire audio
+subsystem, this includes internal codec power blocks and machine level power
+systems.
+
+There are 4 power domains within DAPM
+
+ 1. Codec domain - VREF, VMID (core codec and audio power)
+ Usually controlled at codec probe/remove and suspend/resume, although
+ can be set at stream time if power is not needed for sidetone, etc.
+
+ 2. Platform/Machine domain - physically connected inputs and outputs
+ Is platform/machine and user action specific, is configured by the
+ machine driver and responds to asynchronous events e.g when HP
+ are inserted
+
+ 3. Path domain - audio susbsystem signal paths
+ Automatically set when mixer and mux settings are changed by the user.
+ e.g. alsamixer, amixer.
+
+ 4. Stream domain - DAC's and ADC's.
+ Enabled and disabled when stream playback/capture is started and
+ stopped respectively. e.g. aplay, arecord.
+
+All DAPM power switching descisons are made automatically by consulting an audio
+routing map of the whole machine. This map is specific to each machine and
+consists of the interconnections between every audio component (including
+internal codec components). All audio components that effect power are called
+widgets hereafter.
+
+
+2. DAPM Widgets
+===============
+
+Audio DAPM widgets fall into a number of types:-
+
+ o Mixer - Mixes several analog signals into a single analog signal.
+ o Mux - An analog switch that outputs only 1 of it's inputs.
+ o PGA - A programmable gain amplifier or attenuation widget.
+ o ADC - Analog to Digital Converter
+ o DAC - Digital to Analog Converter
+ o Switch - An analog switch
+ o Input - A codec input pin
+ o Output - A codec output pin
+ o Headphone - Headphone (and optional Jack)
+ o Mic - Mic (and optional Jack)
+ o Line - Line Input/Output (and optional Jack)
+ o Speaker - Speaker
+ o Pre - Special PRE widget (exec before all others)
+ o Post - Special POST widget (exec after all others)
+
+(Widgets are defined in include/sound/soc-dapm.h)
+
+Widgets are usually added in the codec driver and the machine driver. There are
+convience macros defined in soc-dapm.h that can be used to quickly build a
+list of widgets of the codecs and machines DAPM widgets.
+
+Most widgets have a name, register, shift and invert. Some widgets have extra
+parameters for stream name and kcontrols.
+
+
+2.1 Stream Domain Widgets
+-------------------------
+
+Stream Widgets relate to the stream power domain and only consist of ADC's
+(analog to digital converters) and DAC's (digital to analog converters).
+
+Stream widgets have the following format:-
+
+SND_SOC_DAPM_DAC(name, stream name, reg, shift, invert),
+
+NOTE: the stream name must match the corresponding stream name in your codecs
+snd_soc_codec_dai.
+
+e.g. stream widgets for HiFi playback and capture
+
+SND_SOC_DAPM_DAC("HiFi DAC", "HiFi Playback", REG, 3, 1),
+SND_SOC_DAPM_ADC("HiFi ADC", "HiFi Capture", REG, 2, 1),
+
+
+2.2 Path Domain Widgets
+-----------------------
+
+Path domain widgets have a ability to control or effect the audio signal or
+audio paths within the audio subsystem. They have the following form:-
+
+SND_SOC_DAPM_PGA(name, reg, shift, invert, controls, num_controls)
+
+Any widget kcontrols can be set using the controls and num_controls members.
+
+e.g. Mixer widget (the kcontrols are declared first)
+
+/* Output Mixer */
+static const snd_kcontrol_new_t wm8731_output_mixer_controls[] = {
+SOC_DAPM_SINGLE("Line Bypass Switch", WM8731_APANA, 3, 1, 0),
+SOC_DAPM_SINGLE("Mic Sidetone Switch", WM8731_APANA, 5, 1, 0),
+SOC_DAPM_SINGLE("HiFi Playback Switch", WM8731_APANA, 4, 1, 0),
+};
+
+SND_SOC_DAPM_MIXER("Output Mixer", WM8731_PWR, 4, 1, wm8731_output_mixer_controls,
+ ARRAY_SIZE(wm8731_output_mixer_controls)),
+
+
+2.3 Platform/Machine domain Widgets
+-----------------------------------
+
+Machine widgets are different from codec widgets in that they don't have a
+codec register bit associated with them. A machine widget is assigned to each
+machine audio component (non codec) that can be independently powered. e.g.
+
+ o Speaker Amp
+ o Microphone Bias
+ o Jack connectors
+
+A machine widget can have an optional call back.
+
+e.g. Jack connector widget for an external Mic that enables Mic Bias
+when the Mic is inserted:-
+
+static int spitz_mic_bias(struct snd_soc_dapm_widget* w, int event)
+{
+ if(SND_SOC_DAPM_EVENT_ON(event))
+ set_scoop_gpio(&spitzscoop2_device.dev, SPITZ_SCP2_MIC_BIAS);
+ else
+ reset_scoop_gpio(&spitzscoop2_device.dev, SPITZ_SCP2_MIC_BIAS);
+
+ return 0;
+}
+
+SND_SOC_DAPM_MIC("Mic Jack", spitz_mic_bias),
+
+
+2.4 Codec Domain
+----------------
+
+The Codec power domain has no widgets and is handled by the codecs DAPM event
+handler. This handler is called when the codec powerstate is changed wrt to any
+stream event or by kernel PM events.
+
+
+2.5 Virtual Widgets
+-------------------
+
+Sometimes widgets exist in the codec or machine audio map that don't have any
+corresponding register bit for power control. In this case it's necessary to
+create a virtual widget - a widget with no control bits e.g.
+
+SND_SOC_DAPM_MIXER("AC97 Mixer", SND_SOC_DAPM_NOPM, 0, 0, NULL, 0),
+
+This can be used to merge to signal paths together in software.
+
+After all the widgets have been defined, they can then be added to the DAPM
+subsystem individually with a call to snd_soc_dapm_new_control().
+
+
+3. Codec Widget Interconnections
+================================
+
+Widgets are connected to each other within the codec and machine by audio
+paths (called interconnections). Each interconnection must be defined in order
+to create a map of all audio paths between widgets.
+This is easiest with a diagram of the codec (and schematic of the machine audio
+system), as it requires joining widgets together via their audio signal paths.
+
+i.e. from the WM8731 codec's output mixer (wm8731.c)
+
+The WM8731 output mixer has 3 inputs (sources)
+
+ 1. Line Bypass Input
+ 2. DAC (HiFi playback)
+ 3. Mic Sidetone Input
+
+Each input in this example has a kcontrol associated with it (defined in example
+above) and is connected to the output mixer via it's kcontrol name. We can now
+connect the destination widget (wrt audio signal) with it's source widgets.
+
+ /* output mixer */
+ {"Output Mixer", "Line Bypass Switch", "Line Input"},
+ {"Output Mixer", "HiFi Playback Switch", "DAC"},
+ {"Output Mixer", "Mic Sidetone Switch", "Mic Bias"},
+
+So we have :-
+
+ Destination Widget <=== Path Name <=== Source Widget
+
+Or:-
+
+ Sink, Path, Source
+
+Or :-
+
+ "Output Mixer" is connected to the "DAC" via the "HiFi Playback Switch".
+
+When there is no path name connecting widgets (e.g. a direct connection) we
+pass NULL for the path name.
+
+Interconnections are created with a call to:-
+
+snd_soc_dapm_connect_input(codec, sink, path, source);
+
+Finally, snd_soc_dapm_new_widgets(codec) must be called after all widgets and
+interconnections have been registered with the core. This causes the core to
+scan the codec and machine so that the internal DAPM state matches the
+physical state of the machine.
+
+
+3.1 Machine Widget Interconnections
+-----------------------------------
+Machine widget interconnections are created in the same way as codec ones and
+directly connect the codec pins to machine level widgets.
+
+e.g. connects the speaker out codec pins to the internal speaker.
+
+ /* ext speaker connected to codec pins LOUT2, ROUT2 */
+ {"Ext Spk", NULL , "ROUT2"},
+ {"Ext Spk", NULL , "LOUT2"},
+
+This allows the DAPM to power on and off pins that are connected (and in use)
+and pins that are NC respectively.
+
+
+4 Endpoint Widgets
+===================
+An endpoint is a start or end point (widget) of an audio signal within the
+machine and includes the codec. e.g.
+
+ o Headphone Jack
+ o Internal Speaker
+ o Internal Mic
+ o Mic Jack
+ o Codec Pins
+
+When a codec pin is NC it can be marked as not used with a call to
+
+snd_soc_dapm_set_endpoint(codec, "Widget Name", 0);
+
+The last argument is 0 for inactive and 1 for active. This way the pin and its
+input widget will never be powered up and consume power.
+
+This also applies to machine widgets. e.g. if a headphone is connected to a
+jack then the jack can be marked active. If the headphone is removed, then
+the headphone jack can be marked inactive.
+
+
+5 DAPM Widget Events
+====================
+
+Some widgets can register their interest with the DAPM core in PM events.
+e.g. A Speaker with an amplifier registers a widget so the amplifier can be
+powered only when the spk is in use.
+
+/* turn speaker amplifier on/off depending on use */
+static int corgi_amp_event(struct snd_soc_dapm_widget *w, int event)
+{
+ if (SND_SOC_DAPM_EVENT_ON(event))
+ set_scoop_gpio(&corgiscoop_device.dev, CORGI_SCP_APM_ON);
+ else
+ reset_scoop_gpio(&corgiscoop_device.dev, CORGI_SCP_APM_ON);
+
+ return 0;
+}
+
+/* corgi machine dapm widgets */
+static const struct snd_soc_dapm_widget wm8731_dapm_widgets =
+ SND_SOC_DAPM_SPK("Ext Spk", corgi_amp_event);
+
+Please see soc-dapm.h for all other widgets that support events.
+
+
+5.1 Event types
+---------------
+
+The following event types are supported by event widgets.
+
+/* dapm event types */
+#define SND_SOC_DAPM_PRE_PMU 0x1 /* before widget power up */
+#define SND_SOC_DAPM_POST_PMU 0x2 /* after widget power up */
+#define SND_SOC_DAPM_PRE_PMD 0x4 /* before widget power down */
+#define SND_SOC_DAPM_POST_PMD 0x8 /* after widget power down */
+#define SND_SOC_DAPM_PRE_REG 0x10 /* before audio path setup */
+#define SND_SOC_DAPM_POST_REG 0x20 /* after audio path setup */
diff --git a/Documentation/sound/alsa/soc/machine.txt b/Documentation/sound/alsa/soc/machine.txt
new file mode 100644
index 000000000000..72bd222f2a21
--- /dev/null
+++ b/Documentation/sound/alsa/soc/machine.txt
@@ -0,0 +1,113 @@
+ASoC Machine Driver
+===================
+
+The ASoC machine (or board) driver is the code that glues together the platform
+and codec drivers.
+
+The machine driver can contain codec and platform specific code. It registers
+the audio subsystem with the kernel as a platform device and is represented by
+the following struct:-
+
+/* SoC machine */
+struct snd_soc_machine {
+ char *name;
+
+ int (*probe)(struct platform_device *pdev);
+ int (*remove)(struct platform_device *pdev);
+
+ /* the pre and post PM functions are used to do any PM work before and
+ * after the codec and DAI's do any PM work. */
+ int (*suspend_pre)(struct platform_device *pdev, pm_message_t state);
+ int (*suspend_post)(struct platform_device *pdev, pm_message_t state);
+ int (*resume_pre)(struct platform_device *pdev);
+ int (*resume_post)(struct platform_device *pdev);
+
+ /* machine stream operations */
+ struct snd_soc_ops *ops;
+
+ /* CPU <--> Codec DAI links */
+ struct snd_soc_dai_link *dai_link;
+ int num_links;
+};
+
+probe()/remove()
+----------------
+probe/remove are optional. Do any machine specific probe here.
+
+
+suspend()/resume()
+------------------
+The machine driver has pre and post versions of suspend and resume to take care
+of any machine audio tasks that have to be done before or after the codec, DAI's
+and DMA is suspended and resumed. Optional.
+
+
+Machine operations
+------------------
+The machine specific audio operations can be set here. Again this is optional.
+
+
+Machine DAI Configuration
+-------------------------
+The machine DAI configuration glues all the codec and CPU DAI's together. It can
+also be used to set up the DAI system clock and for any machine related DAI
+initialisation e.g. the machine audio map can be connected to the codec audio
+map, unconnnected codec pins can be set as such. Please see corgi.c, spitz.c
+for examples.
+
+struct snd_soc_dai_link is used to set up each DAI in your machine. e.g.
+
+/* corgi digital audio interface glue - connects codec <--> CPU */
+static struct snd_soc_dai_link corgi_dai = {
+ .name = "WM8731",
+ .stream_name = "WM8731",
+ .cpu_dai = &pxa_i2s_dai,
+ .codec_dai = &wm8731_dai,
+ .init = corgi_wm8731_init,
+ .ops = &corgi_ops,
+};
+
+struct snd_soc_machine then sets up the machine with it's DAI's. e.g.
+
+/* corgi audio machine driver */
+static struct snd_soc_machine snd_soc_machine_corgi = {
+ .name = "Corgi",
+ .dai_link = &corgi_dai,
+ .num_links = 1,
+};
+
+
+Machine Audio Subsystem
+-----------------------
+
+The machine soc device glues the platform, machine and codec driver together.
+Private data can also be set here. e.g.
+
+/* corgi audio private data */
+static struct wm8731_setup_data corgi_wm8731_setup = {
+ .i2c_address = 0x1b,
+};
+
+/* corgi audio subsystem */
+static struct snd_soc_device corgi_snd_devdata = {
+ .machine = &snd_soc_machine_corgi,
+ .platform = &pxa2xx_soc_platform,
+ .codec_dev = &soc_codec_dev_wm8731,
+ .codec_data = &corgi_wm8731_setup,
+};
+
+
+Machine Power Map
+-----------------
+
+The machine driver can optionally extend the codec power map and to become an
+audio power map of the audio subsystem. This allows for automatic power up/down
+of speaker/HP amplifiers, etc. Codec pins can be connected to the machines jack
+sockets in the machine init function. See soc/pxa/spitz.c and dapm.txt for
+details.
+
+
+Machine Controls
+----------------
+
+Machine specific audio mixer controls can be added in the dai init function. \ No newline at end of file
diff --git a/Documentation/sound/alsa/soc/overview.txt b/Documentation/sound/alsa/soc/overview.txt
new file mode 100644
index 000000000000..753c5cc5984a
--- /dev/null
+++ b/Documentation/sound/alsa/soc/overview.txt
@@ -0,0 +1,83 @@
+ALSA SoC Layer
+==============
+
+The overall project goal of the ALSA System on Chip (ASoC) layer is to provide
+better ALSA support for embedded system on chip procesors (e.g. pxa2xx, au1x00,
+iMX, etc) and portable audio codecs. Currently there is some support in the
+kernel for SoC audio, however it has some limitations:-
+
+ * Currently, codec drivers are often tightly coupled to the underlying SoC
+ cpu. This is not ideal and leads to code duplication i.e. Linux now has 4
+ different wm8731 drivers for 4 different SoC platforms.
+
+ * There is no standard method to signal user initiated audio events.
+ e.g. Headphone/Mic insertion, Headphone/Mic detection after an insertion
+ event. These are quite common events on portable devices and ofter require
+ machine specific code to re route audio, enable amps etc after such an event.
+
+ * Current drivers tend to power up the entire codec when playing
+ (or recording) audio. This is fine for a PC, but tends to waste a lot of
+ power on portable devices. There is also no support for saving power via
+ changing codec oversampling rates, bias currents, etc.
+
+
+ASoC Design
+===========
+
+The ASoC layer is designed to address these issues and provide the following
+features :-
+
+ * Codec independence. Allows reuse of codec drivers on other platforms
+ and machines.
+
+ * Easy I2S/PCM audio interface setup between codec and SoC. Each SoC interface
+ and codec registers it's audio interface capabilities with the core and are
+ subsequently matched and configured when the application hw params are known.
+
+ * Dynamic Audio Power Management (DAPM). DAPM automatically sets the codec to
+ it's minimum power state at all times. This includes powering up/down
+ internal power blocks depending on the internal codec audio routing and any
+ active streams.
+
+ * Pop and click reduction. Pops and clicks can be reduced by powering the
+ codec up/down in the correct sequence (including using digital mute). ASoC
+ signals the codec when to change power states.
+
+ * Machine specific controls: Allow machines to add controls to the sound card
+ e.g. volume control for speaker amp.
+
+To achieve all this, ASoC basically splits an embedded audio system into 3
+components :-
+
+ * Codec driver: The codec driver is platform independent and contains audio
+ controls, audio interface capabilities, codec dapm definition and codec IO
+ functions.
+
+ * Platform driver: The platform driver contains the audio dma engine and audio
+ interface drivers (e.g. I2S, AC97, PCM) for that platform.
+
+ * Machine driver: The machine driver handles any machine specific controls and
+ audio events. i.e. turing on an amp at start of playback.
+
+
+Documentation
+=============
+
+The documentation is spilt into the following sections:-
+
+overview.txt: This file.
+
+codec.txt: Codec driver internals.
+
+DAI.txt: Description of Digital Audio Interface standards and how to configure
+a DAI within your codec and CPU DAI drivers.
+
+dapm.txt: Dynamic Audio Power Management
+
+platform.txt: Platform audio DMA and DAI.
+
+machine.txt: Machine driver internals.
+
+pop_clicks.txt: How to minimise audio artifacts.
+
+clocking.txt: ASoC clocking for best power performance. \ No newline at end of file
diff --git a/Documentation/sound/alsa/soc/platform.txt b/Documentation/sound/alsa/soc/platform.txt
new file mode 100644
index 000000000000..e95b16d5a53b
--- /dev/null
+++ b/Documentation/sound/alsa/soc/platform.txt
@@ -0,0 +1,58 @@
+ASoC Platform Driver
+====================
+
+An ASoC platform driver can be divided into audio DMA and SoC DAI configuration
+and control. The platform drivers only target the SoC CPU and must have no board
+specific code.
+
+Audio DMA
+=========
+
+The platform DMA driver optionally supports the following alsa operations:-
+
+/* SoC audio ops */
+struct snd_soc_ops {
+ int (*startup)(struct snd_pcm_substream *);
+ void (*shutdown)(struct snd_pcm_substream *);
+ int (*hw_params)(struct snd_pcm_substream *, struct snd_pcm_hw_params *);
+ int (*hw_free)(struct snd_pcm_substream *);
+ int (*prepare)(struct snd_pcm_substream *);
+ int (*trigger)(struct snd_pcm_substream *, int);
+};
+
+The platform driver exports it's DMA functionailty via struct snd_soc_platform:-
+
+struct snd_soc_platform {
+ char *name;
+
+ int (*probe)(struct platform_device *pdev);
+ int (*remove)(struct platform_device *pdev);
+ int (*suspend)(struct platform_device *pdev, struct snd_soc_cpu_dai *cpu_dai);
+ int (*resume)(struct platform_device *pdev, struct snd_soc_cpu_dai *cpu_dai);
+
+ /* pcm creation and destruction */
+ int (*pcm_new)(struct snd_card *, struct snd_soc_codec_dai *, struct snd_pcm *);
+ void (*pcm_free)(struct snd_pcm *);
+
+ /* platform stream ops */
+ struct snd_pcm_ops *pcm_ops;
+};
+
+Please refer to the alsa driver documentation for details of audio DMA.
+http://www.alsa-project.org/~iwai/writing-an-alsa-driver/c436.htm
+
+An example DMA driver is soc/pxa/pxa2xx-pcm.c
+
+
+SoC DAI Drivers
+===============
+
+Each SoC DAI driver must provide the following features:-
+
+ 1) Digital audio interface (DAI) description
+ 2) Digital audio interface configuration
+ 3) PCM's description
+ 4) Sysclk configuration
+ 5) Suspend and resume (optional)
+
+Please see codec.txt for a description of items 1 - 4.
diff --git a/Documentation/sound/alsa/soc/pops_clicks.txt b/Documentation/sound/alsa/soc/pops_clicks.txt
new file mode 100644
index 000000000000..2cf7ee5b3d74
--- /dev/null
+++ b/Documentation/sound/alsa/soc/pops_clicks.txt
@@ -0,0 +1,52 @@
+Audio Pops and Clicks
+=====================
+
+Pops and clicks are unwanted audio artifacts caused by the powering up and down
+of components within the audio subsystem. This is noticable on PC's when an
+audio module is either loaded or unloaded (at module load time the sound card is
+powered up and causes a popping noise on the speakers).
+
+Pops and clicks can be more frequent on portable systems with DAPM. This is
+because the components within the subsystem are being dynamically powered
+depending on the audio usage and this can subsequently cause a small pop or
+click every time a component power state is changed.
+
+
+Minimising Playback Pops and Clicks
+===================================
+
+Playback pops in portable audio subsystems cannot be completely eliminated atm,
+however future audio codec hardware will have better pop and click supression.
+Pops can be reduced within playback by powering the audio components in a
+specific order. This order is different for startup and shutdown and follows
+some basic rules:-
+
+ Startup Order :- DAC --> Mixers --> Output PGA --> Digital Unmute
+
+ Shutdown Order :- Digital Mute --> Output PGA --> Mixers --> DAC
+
+This assumes that the codec PCM output path from the DAC is via a mixer and then
+a PGA (programmable gain amplifier) before being output to the speakers.
+
+
+Minimising Capture Pops and Clicks
+==================================
+
+Capture artifacts are somewhat easier to get rid as we can delay activating the
+ADC until all the pops have occured. This follows similar power rules to
+playback in that components are powered in a sequence depending upon stream
+startup or shutdown.
+
+ Startup Order - Input PGA --> Mixers --> ADC
+
+ Shutdown Order - ADC --> Mixers --> Input PGA
+
+
+Zipper Noise
+============
+An unwanted zipper noise can occur within the audio playback or capture stream
+when a volume control is changed near its maximum gain value. The zipper noise
+is heard when the gain increase or decrease changes the mean audio signal
+amplitude too quickly. It can be minimised by enabling the zero cross setting
+for each volume control. The ZC forces the gain change to occur when the signal
+crosses the zero amplitude line.
diff --git a/MAINTAINERS b/MAINTAINERS
index 6ddae2bb6821..f2a79481f1c1 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -3037,6 +3037,12 @@ M: perex@suse.cz
L: alsa-devel@alsa-project.org
S: Maintained
+SOUND - SOC LAYER / DYNAMIC AUDIO POWER MANAGEMENT
+P: Liam Girdwood
+M: liam.girdwood@wolfsonmicro.com
+L: alsa-devel@alsa-project.org
+S: Supported
+
SPI SUBSYSTEM
P: David Brownell
M: dbrownell@users.sourceforge.net
diff --git a/drivers/input/touchscreen/ucb1400_ts.c b/drivers/input/touchscreen/ucb1400_ts.c
index 4358a0a78eaa..c7db4032ef02 100644
--- a/drivers/input/touchscreen/ucb1400_ts.c
+++ b/drivers/input/touchscreen/ucb1400_ts.c
@@ -83,7 +83,7 @@
struct ucb1400 {
- ac97_t *ac97;
+ struct snd_ac97 *ac97;
struct input_dev *ts_idev;
int irq;
diff --git a/include/linux/i2c-id.h b/include/linux/i2c-id.h
index d38778f2fbec..6e7ec4c76178 100644
--- a/include/linux/i2c-id.h
+++ b/include/linux/i2c-id.h
@@ -115,6 +115,8 @@
#define I2C_DRIVERID_KS0127 86 /* Samsung ks0127 video decoder */
#define I2C_DRIVERID_TLV320AIC23B 87 /* TI TLV320AIC23B audio codec */
#define I2C_DRIVERID_ISL1208 88 /* Intersil ISL1208 RTC */
+#define I2C_DRIVERID_WM8731 89 /* Wolfson WM8731 audio codec */
+#define I2C_DRIVERID_WM8750 90 /* Wolfson WM8750 audio codec */
#define I2C_DRIVERID_I2CDEV 900
#define I2C_DRIVERID_ARP 902 /* SMBus ARP Client */
diff --git a/include/sound/ac97_codec.h b/include/sound/ac97_codec.h
index 33720397a904..246ac23534bd 100644
--- a/include/sound/ac97_codec.h
+++ b/include/sound/ac97_codec.h
@@ -375,6 +375,7 @@
#define AC97_SCAP_DETECT_BY_VENDOR (1<<8) /* use vendor registers for read tests */
#define AC97_SCAP_NO_SPDIF (1<<9) /* don't build SPDIF controls */
#define AC97_SCAP_EAPD_LED (1<<10) /* EAPD as mute LED */
+#define AC97_SCAP_POWER_SAVE (1<<11) /* capable for aggresive power-saving */
/* ac97->flags */
#define AC97_HAS_PC_BEEP (1<<0) /* force PC Speaker usage */
@@ -425,6 +426,7 @@ struct snd_ac97_build_ops {
struct snd_ac97_bus_ops {
void (*reset) (struct snd_ac97 *ac97);
+ void (*warm_reset)(struct snd_ac97 *ac97);
void (*write) (struct snd_ac97 *ac97, unsigned short reg, unsigned short val);
unsigned short (*read) (struct snd_ac97 *ac97, unsigned short reg);
void (*wait) (struct snd_ac97 *ac97);
@@ -501,6 +503,7 @@ struct snd_ac97 {
unsigned short id[3]; // codec IDs (lower 16-bit word)
unsigned short pcmreg[3]; // PCM registers
unsigned short codec_cfg[3]; // CODEC_CFG bits
+ unsigned char swap_mic_linein; // AD1986/AD1986A only
} ad18xx;
unsigned int dev_flags; /* device specific */
} spec;
@@ -510,7 +513,6 @@ struct snd_ac97 {
#ifdef CONFIG_SND_AC97_POWER_SAVE
unsigned int power_up; /* power states */
- struct workqueue_struct *power_workq;
struct delayed_work power_work;
#endif
struct device dev;
diff --git a/include/sound/ad1848.h b/include/sound/ad1848.h
index c8de6f83338f..b2c3f00a9b35 100644
--- a/include/sound/ad1848.h
+++ b/include/sound/ad1848.h
@@ -185,7 +185,7 @@ struct ad1848_mix_elem {
int index;
int type;
unsigned long private_value;
- unsigned int *tlv;
+ const unsigned int *tlv;
};
#define AD1848_SINGLE(xname, xindex, reg, shift, mask, invert) \
diff --git a/include/sound/ak4114.h b/include/sound/ak4114.h
index 2ee061625fd0..c149d3b2558b 100644
--- a/include/sound/ak4114.h
+++ b/include/sound/ak4114.h
@@ -181,7 +181,6 @@ struct ak4114 {
unsigned long ccrc_errors;
unsigned char rcs0;
unsigned char rcs1;
- struct workqueue_struct *workqueue;
struct delayed_work work;
void *change_callback_private;
void (*change_callback)(struct ak4114 *ak4114, unsigned char c0, unsigned char c1);
@@ -189,7 +188,7 @@ struct ak4114 {
int snd_ak4114_create(struct snd_card *card,
ak4114_read_t *read, ak4114_write_t *write,
- unsigned char pgm[7], unsigned char txcsb[5],
+ const unsigned char pgm[7], const unsigned char txcsb[5],
void *private_data, struct ak4114 **r_ak4114);
void snd_ak4114_reg_write(struct ak4114 *ak4114, unsigned char reg, unsigned char mask, unsigned char val);
void snd_ak4114_reinit(struct ak4114 *ak4114);
diff --git a/include/sound/ak4117.h b/include/sound/ak4117.h
index 2b96c32f06fd..d650d52e3d29 100644
--- a/include/sound/ak4117.h
+++ b/include/sound/ak4117.h
@@ -178,7 +178,7 @@ struct ak4117 {
};
int snd_ak4117_create(struct snd_card *card, ak4117_read_t *read, ak4117_write_t *write,
- unsigned char pgm[5], void *private_data, struct ak4117 **r_ak4117);
+ const unsigned char pgm[5], void *private_data, struct ak4117 **r_ak4117);
void snd_ak4117_reg_write(struct ak4117 *ak4117, unsigned char reg, unsigned char mask, unsigned char val);
void snd_ak4117_reinit(struct ak4117 *ak4117);
int snd_ak4117_build(struct ak4117 *ak4117, struct snd_pcm_substream *capture_substream);
diff --git a/include/sound/ak4xxx-adda.h b/include/sound/ak4xxx-adda.h
index d0deca669b92..aa49dda4f410 100644
--- a/include/sound/ak4xxx-adda.h
+++ b/include/sound/ak4xxx-adda.h
@@ -50,6 +50,8 @@ struct snd_akm4xxx_adc_channel {
char *name; /* capture gain volume label */
char *switch_name; /* capture switch */
unsigned int num_channels;
+ char *selector_name; /* capture source select label */
+ const char **input_names; /* capture source names (NULL terminated) */
};
struct snd_akm4xxx {
@@ -69,8 +71,8 @@ struct snd_akm4xxx {
} type;
/* (array) information of combined codecs */
- struct snd_akm4xxx_dac_channel *dac_info;
- struct snd_akm4xxx_adc_channel *adc_info;
+ const struct snd_akm4xxx_dac_channel *dac_info;
+ const struct snd_akm4xxx_adc_channel *adc_info;
struct snd_ak4xxx_ops ops;
};
diff --git a/include/sound/control.h b/include/sound/control.h
index 1de148b0fd94..72e759f619b1 100644
--- a/include/sound/control.h
+++ b/include/sound/control.h
@@ -49,7 +49,7 @@ struct snd_kcontrol_new {
snd_kcontrol_put_t *put;
union {
snd_kcontrol_tlv_rw_t *c;
- unsigned int *p;
+ const unsigned int *p;
} tlv;
unsigned long private_value;
};
@@ -69,7 +69,7 @@ struct snd_kcontrol {
snd_kcontrol_put_t *put;
union {
snd_kcontrol_tlv_rw_t *c;
- unsigned int *p;
+ const unsigned int *p;
} tlv;
unsigned long private_value;
void *private_data;
@@ -108,7 +108,6 @@ typedef int (*snd_kctl_ioctl_func_t) (struct snd_card * card,
void snd_ctl_notify(struct snd_card * card, unsigned int mask, struct snd_ctl_elem_id * id);
-struct snd_kcontrol *snd_ctl_new(struct snd_kcontrol * kcontrol, unsigned int access);
struct snd_kcontrol *snd_ctl_new1(const struct snd_kcontrol_new * kcontrolnew, void * private_data);
void snd_ctl_free_one(struct snd_kcontrol * kcontrol);
int snd_ctl_add(struct snd_card * card, struct snd_kcontrol * kcontrol);
diff --git a/include/sound/core.h b/include/sound/core.h
index 521f036cce99..4b9e609975ab 100644
--- a/include/sound/core.h
+++ b/include/sound/core.h
@@ -211,9 +211,40 @@ extern struct class *sound_class;
void snd_request_card(int card);
-int snd_register_device(int type, struct snd_card *card, int dev,
- const struct file_operations *f_ops, void *private_data,
- const char *name);
+int snd_register_device_for_dev(int type, struct snd_card *card,
+ int dev,
+ const struct file_operations *f_ops,
+ void *private_data,
+ const char *name,
+ struct device *device);
+
+/**
+ * snd_register_device - Register the ALSA device file for the card
+ * @type: the device type, SNDRV_DEVICE_TYPE_XXX
+ * @card: the card instance
+ * @dev: the device index
+ * @f_ops: the file operations
+ * @private_data: user pointer for f_ops->open()
+ * @name: the device file name
+ *
+ * Registers an ALSA device file for the given card.
+ * The operators have to be set in reg parameter.
+ *
+ * This function uses the card's device pointer to link to the
+ * correct &struct device.
+ *
+ * Returns zero if successful, or a negative error code on failure.
+ */
+static inline int snd_register_device(int type, struct snd_card *card, int dev,
+ const struct file_operations *f_ops,
+ void *private_data,
+ const char *name)
+{
+ return snd_register_device_for_dev(type, card, dev, f_ops,
+ private_data, name,
+ snd_card_get_device_link(card));
+}
+
int snd_unregister_device(int type, struct snd_card *card, int dev);
void *snd_lookup_minor_data(unsigned int minor, int type);
int snd_add_device_sysfs_file(int type, struct snd_card *card, int dev,
@@ -396,6 +427,29 @@ void snd_verbose_printd(const char *file, int line, const char *format, ...)
#endif
#endif
-#include "typedefs.h"
+/* PCI quirk list helper */
+struct snd_pci_quirk {
+ unsigned short subvendor; /* PCI subvendor ID */
+ unsigned short subdevice; /* PCI subdevice ID */
+ int value; /* value */
+#ifdef CONFIG_SND_DEBUG_DETECT
+ const char *name; /* name of the device (optional) */
+#endif
+};
+
+#define _SND_PCI_QUIRK_ID(vend,dev) \
+ .subvendor = (vend), .subdevice = (dev)
+#define SND_PCI_QUIRK_ID(vend,dev) {_SND_PCI_QUIRK_ID(vend, dev)}
+#ifdef CONFIG_SND_DEBUG_DETECT
+#define SND_PCI_QUIRK(vend,dev,xname,val) \
+ {_SND_PCI_QUIRK_ID(vend, dev), .value = (val), .name = (xname)}
+#else
+#define SND_PCI_QUIRK(vend,dev,xname,val) \
+ {_SND_PCI_QUIRK_ID(vend, dev), .value = (val)}
+#endif
+
+const struct snd_pci_quirk *
+snd_pci_quirk_lookup(struct pci_dev *pci, const struct snd_pci_quirk *list);
+
#endif /* __SOUND_CORE_H */
diff --git a/include/sound/emu10k1.h b/include/sound/emu10k1.h
index 3d3c1514cf71..eb7ce96ddf3a 100644
--- a/include/sound/emu10k1.h
+++ b/include/sound/emu10k1.h
@@ -188,7 +188,35 @@
#define HCFG_LEGACYINT 0x00200000 /* 1 = legacy event captured. Write 1 to clear. */
/* NOTE: The rest of the bits in this register */
/* _are_ relevant under Linux. */
-#define HCFG_CODECFORMAT_MASK 0x00070000 /* CODEC format */
+#define HCFG_PUSH_BUTTON_ENABLE 0x00100000 /* Enables Volume Inc/Dec and Mute functions */
+#define HCFG_BAUD_RATE 0x00080000 /* 0 = 48kHz, 1 = 44.1kHz */
+#define HCFG_EXPANDED_MEM 0x00040000 /* 1 = any 16M of 4G addr, 0 = 32M of 2G addr */
+#define HCFG_CODECFORMAT_MASK 0x00030000 /* CODEC format */
+
+/* Specific to Alice2, CA0102 */
+#define HCFG_CODECFORMAT_AC97_1 0x00000000 /* AC97 CODEC format -- Ver 1.03 */
+#define HCFG_CODECFORMAT_AC97_2 0x00010000 /* AC97 CODEC format -- Ver 2.1 */
+#define HCFG_AUTOMUTE_ASYNC 0x00008000 /* When set, the async sample rate convertors */
+ /* will automatically mute their output when */
+ /* they are not rate-locked to the external */
+ /* async audio source */
+#define HCFG_AUTOMUTE_SPDIF 0x00004000 /* When set, the async sample rate convertors */
+ /* will automatically mute their output when */
+ /* the SPDIF V-bit indicates invalid audio */
+#define HCFG_EMU32_SLAVE 0x00002000 /* 0 = Master, 1 = Slave. Slave for EMU1010 */
+#define HCFG_SLOW_RAMP 0x00001000 /* Increases Send Smoothing time constant */
+/* 0x00000800 not used on Alice2 */
+#define HCFG_PHASE_TRACK_MASK 0x00000700 /* When set, forces corresponding input to */
+ /* phase track the previous input. */
+ /* I2S0 can phase track the last S/PDIF input */
+#define HCFG_I2S_ASRC_ENABLE 0x00000070 /* When set, enables asynchronous sample rate */
+ /* conversion for the corresponding */
+ /* I2S format input */
+/* Rest of HCFG 0x0000000f same as below. LOCKSOUNDCACHE etc. */
+
+
+
+/* Older chips */
#define HCFG_CODECFORMAT_AC97 0x00000000 /* AC97 CODEC format -- Primary Output */
#define HCFG_CODECFORMAT_I2S 0x00010000 /* I2S CODEC format -- Secondary (Rear) Output */
#define HCFG_GPINPUT0 0x00004000 /* External pin112 */
@@ -432,6 +460,7 @@
#define FXRT_CHANNELC 0x0f000000 /* Effects send bus number for channel's effects send C */
#define FXRT_CHANNELD 0xf0000000 /* Effects send bus number for channel's effects send D */
+#define A_HR 0x0b /* High Resolution. 24bit playback from host to DSP. */
#define MAPA 0x0c /* Cache map A */
#define MAPB 0x0d /* Cache map B */
@@ -439,6 +468,8 @@
#define MAP_PTE_MASK 0xffffe000 /* The 19 MSBs of the PTE indexed by the PTI */
#define MAP_PTI_MASK 0x00001fff /* The 13 bit index to one of the 8192 PTE dwords */
+/* 0x0e, 0x0f: Not used */
+
#define ENVVOL 0x10 /* Volume envelope register */
#define ENVVOL_MASK 0x0000ffff /* Current value of volume envelope state variable */
/* 0x8000-n == 666*n usec delay */
@@ -527,7 +558,7 @@
/* NOTE: All channels contain internal variables; do */
/* not write to these locations. */
-/* 1f something */
+/* 0x1f: not used */
#define CD0 0x20 /* Cache data 0 register */
#define CD1 0x21 /* Cache data 1 register */
@@ -597,6 +628,8 @@
#define FXWC_SPDIFLEFT (1<<22) /* 0x00400000 */
#define FXWC_SPDIFRIGHT (1<<23) /* 0x00800000 */
+#define A_TBLSZ ` 0x43 /* Effects Tank Internal Table Size. Only low byte or register used */
+
#define TCBS 0x44 /* Tank cache buffer size register */
#define TCBS_MASK 0x00000007 /* Tank cache buffer size field */
#define TCBS_BUFFSIZE_16K 0x00000000
@@ -617,7 +650,7 @@
#define FXBA 0x47 /* FX Buffer Address */
#define FXBA_MASK 0xfffff000 /* 20 bit base address */
-/* 0x48 something - word access, defaults to 3f */
+#define A_HWM 0x48 /* High PCI Water Mark - word access, defaults to 3f */
#define MICBS 0x49 /* Microphone buffer size register */
@@ -661,6 +694,18 @@
#define ADCBS_BUFSIZE_57344 0x0000001e
#define ADCBS_BUFSIZE_65536 0x0000001f
+/* Current Send B, A Amounts */
+#define A_CSBA 0x4c
+
+/* Current Send D, C Amounts */
+#define A_CSDC 0x4d
+
+/* Current Send F, E Amounts */
+#define A_CSFE 0x4e
+
+/* Current Send H, G Amounts */
+#define A_CSHG 0x4f
+
#define CDCS 0x50 /* CD-ROM digital channel status register */
@@ -668,6 +713,9 @@
#define DBG 0x52 /* DO NOT PROGRAM THIS REGISTER!!! MAY DESTROY CHIP */
+/* S/PDIF Input C Channel Status */
+#define A_SPSC 0x52
+
#define REG53 0x53 /* DO NOT PROGRAM THIS REGISTER!!! MAY DESTROY CHIP */
#define A_DBG 0x53
@@ -708,6 +756,8 @@
#define SPCS_NOTAUDIODATA 0x00000002 /* 0 = Digital audio, 1 = not audio */
#define SPCS_PROFESSIONAL 0x00000001 /* 0 = Consumer (IEC-958), 1 = pro (AES3-1992) */
+/* 0x57: Not used */
+
/* The 32-bit CLIx and SOLx registers all have one bit per channel control/status */
#define CLIEL 0x58 /* Channel loop interrupt enable low register */
@@ -733,6 +783,9 @@
#define AC97SLOT_CNTR 0x10 /* Center enable */
#define AC97SLOT_LFE 0x20 /* LFE enable */
+/* PCB Revision */
+#define A_PCB 0x5f
+
// NOTE: 0x60,61,62: 64-bit
#define CDSRCS 0x60 /* CD-ROM Sample Rate Converter status register */
@@ -780,9 +833,18 @@
#define HLIPH 0x69 /* Channel half loop interrupt pending high register */
-// 0x6a,6b,6c used for some recording
-// 0x6d unused
-// 0x6e,6f - tanktable base / offset
+/* S/PDIF Host Record Index (bypasses SRC) */
+#define A_SPRI 0x6a
+/* S/PDIF Host Record Address */
+#define A_SPRA 0x6b
+/* S/PDIF Host Record Control */
+#define A_SPRC 0x6c
+/* Delayed Interrupt Counter & Enable */
+#define A_DICE 0x6d
+/* Tank Table Base */
+#define A_TTB 0x6e
+/* Tank Delay Offset */
+#define A_TDOF 0x6f
/* This is the MPU port on the card (via the game port) */
#define A_MUDATA1 0x70
@@ -800,6 +862,7 @@
#define A_FXWC1 0x74 /* Selects 0x7f-0x60 for FX recording */
#define A_FXWC2 0x75 /* Selects 0x9f-0x80 for FX recording */
+/* Extended Hardware Control */
#define A_SPDIF_SAMPLERATE 0x76 /* Set the sample rate of SPDIF output */
#define A_SAMPLE_RATE 0x76 /* Various sample rate settings. */
#define A_SAMPLE_RATE_NOT_USED 0x0ffc111e /* Bits that are not used and cannot be set. */
@@ -822,8 +885,20 @@
#define A_PCM_96000 0x00004000
#define A_PCM_44100 0x00008000
-/* 0x77,0x78,0x79 "something i2s-related" - default to 0x01080000 on my audigy 2 ZS --rlrevell */
-/* 0x7a, 0x7b - lookup tables */
+/* I2S0 Sample Rate Tracker Status */
+#define A_SRT3 0x77
+
+/* I2S1 Sample Rate Tracker Status */
+#define A_SRT4 0x78
+
+/* I2S2 Sample Rate Tracker Status */
+#define A_SRT5 0x79
+/* - default to 0x01080000 on my audigy 2 ZS --rlrevell */
+
+/* Tank Table DMA Address */
+#define A_TTDA 0x7a
+/* Tank Table DMA Data */
+#define A_TTDD 0x7b
#define A_FXRT2 0x7c
#define A_FXRT_CHANNELE 0x0000003f /* Effects send bus number for channel's effects send E */
@@ -845,7 +920,7 @@
#define A_FXRT_CHANNELC 0x003f0000
#define A_FXRT_CHANNELD 0x3f000000
-
+/* 0x7f: Not used */
/* Each FX general purpose register is 32 bits in length, all bits are used */
#define FXGPREGBASE 0x100 /* FX general purpose registers base */
#define A_FXGPREGBASE 0x400 /* Audigy GPRs, 0x400 to 0x5ff */
@@ -886,6 +961,293 @@
#define A_HIWORD_RESULT_MASK 0x007ff000
#define A_HIWORD_OPA_MASK 0x000007ff
+/************************************************************************************************/
+/* EMU1010m HANA FPGA registers */
+/************************************************************************************************/
+#define EMU_HANA_DESTHI 0x00 /* 0000xxx 3 bits Link Destination */
+#define EMU_HANA_DESTLO 0x01 /* 00xxxxx 5 bits */
+#define EMU_HANA_SRCHI 0x02 /* 0000xxx 3 bits Link Source */
+#define EMU_HANA_SRCLO 0x03 /* 00xxxxx 5 bits */
+#define EMU_HANA_DOCK_PWR 0x04 /* 000000x 1 bits Audio Dock power */
+#define EMU_HANA_DOCK_PWR_ON 0x01 /* Audio Dock power on */
+#define EMU_HANA_WCLOCK 0x05 /* 0000xxx 3 bits Word Clock source select */
+ /* Must be written after power on to reset DLL */
+ /* One is unable to detect the Audio dock without this */
+#define EMU_HANA_WCLOCK_SRC_MASK 0x07
+#define EMU_HANA_WCLOCK_INT_48K 0x00
+#define EMU_HANA_WCLOCK_INT_44_1K 0x01
+#define EMU_HANA_WCLOCK_HANA_SPDIF_IN 0x02
+#define EMU_HANA_WCLOCK_HANA_ADAT_IN 0x03
+#define EMU_HANA_WCLOCK_SYNC_BNCN 0x04
+#define EMU_HANA_WCLOCK_2ND_HANA 0x05
+#define EMU_HANA_WCLOCK_SRC_RESERVED 0x06
+#define EMU_HANA_WCLOCK_OFF 0x07 /* For testing, forces fallback to DEFCLOCK */
+#define EMU_HANA_WCLOCK_MULT_MASK 0x18
+#define EMU_HANA_WCLOCK_1X 0x00
+#define EMU_HANA_WCLOCK_2X 0x08
+#define EMU_HANA_WCLOCK_4X 0x10
+#define EMU_HANA_WCLOCK_MULT_RESERVED 0x18
+
+#define EMU_HANA_DEFCLOCK 0x06 /* 000000x 1 bits Default Word Clock */
+#define EMU_HANA_DEFCLOCK_48K 0x00
+#define EMU_HANA_DEFCLOCK_44_1K 0x01
+
+#define EMU_HANA_UNMUTE 0x07 /* 000000x 1 bits Mute all audio outputs */
+#define EMU_MUTE 0x00
+#define EMU_UNMUTE 0x01
+
+#define EMU_HANA_FPGA_CONFIG 0x08 /* 00000xx 2 bits Config control of FPGAs */
+#define EMU_HANA_FPGA_CONFIG_AUDIODOCK 0x01 /* Set in order to program FPGA on Audio Dock */
+#define EMU_HANA_FPGA_CONFIG_HANA 0x02 /* Set in order to program FPGA on Hana */
+
+#define EMU_HANA_IRQ_ENABLE 0x09 /* 000xxxx 4 bits IRQ Enable */
+#define EMU_HANA_IRQ_WCLK_CHANGED 0x01
+#define EMU_HANA_IRQ_ADAT 0x02
+#define EMU_HANA_IRQ_DOCK 0x04
+#define EMU_HANA_IRQ_DOCK_LOST 0x08
+
+#define EMU_HANA_SPDIF_MODE 0x0a /* 00xxxxx 5 bits SPDIF MODE */
+#define EMU_HANA_SPDIF_MODE_TX_COMSUMER 0x00
+#define EMU_HANA_SPDIF_MODE_TX_PRO 0x01
+#define EMU_HANA_SPDIF_MODE_TX_NOCOPY 0x02
+#define EMU_HANA_SPDIF_MODE_RX_COMSUMER 0x00
+#define EMU_HANA_SPDIF_MODE_RX_PRO 0x04
+#define EMU_HANA_SPDIF_MODE_RX_NOCOPY 0x08
+#define EMU_HANA_SPDIF_MODE_RX_INVALID 0x10
+
+#define EMU_HANA_OPTICAL_TYPE 0x0b /* 00000xx 2 bits ADAT or SPDIF in/out */
+#define EMU_HANA_OPTICAL_IN_SPDIF 0x00
+#define EMU_HANA_OPTICAL_IN_ADAT 0x01
+#define EMU_HANA_OPTICAL_OUT_SPDIF 0x00
+#define EMU_HANA_OPTICAL_OUT_ADAT 0x02
+
+#define EMU_HANA_MIDI_IN 0x0c /* 000000x 1 bit Control MIDI */
+#define EMU_HANA_MIDI_IN_FROM_HAMOA 0x00 /* HAMOA MIDI in to Alice 2 MIDI B */
+#define EMU_HANA_MIDI_IN_FROM_DOCK 0x01 /* Audio Dock MIDI in to Alice 2 MIDI B */
+
+#define EMU_HANA_DOCK_LEDS_1 0x0d /* 000xxxx 4 bit Audio Dock LEDs */
+#define EMU_HANA_DOCK_LEDS_1_MIDI1 0x01 /* MIDI 1 LED on */
+#define EMU_HANA_DOCK_LEDS_1_MIDI2 0x02 /* MIDI 2 LED on */
+#define EMU_HANA_DOCK_LEDS_1_SMPTE_IN 0x04 /* SMPTE IN LED on */
+#define EMU_HANA_DOCK_LEDS_1_SMPTE_OUT 0x08 /* SMPTE OUT LED on */
+
+#define EMU_HANA_DOCK_LEDS_2 0x0e /* 0xxxxxx 6 bit Audio Dock LEDs */
+#define EMU_HANA_DOCK_LEDS_2_44K 0x01 /* 44.1 kHz LED on */
+#define EMU_HANA_DOCK_LEDS_2_48K 0x02 /* 48 kHz LED on */
+#define EMU_HANA_DOCK_LEDS_2_96K 0x04 /* 96 kHz LED on */
+#define EMU_HANA_DOCK_LEDS_2_192K 0x08 /* 192 kHz LED on */
+#define EMU_HANA_DOCK_LEDS_2_LOCK 0x10 /* LOCK LED on */
+#define EMU_HANA_DOCK_LEDS_2_EXT 0x20 /* EXT LED on */
+
+#define EMU_HANA_DOCK_LEDS_3 0x0f /* 0xxxxxx 6 bit Audio Dock LEDs */
+#define EMU_HANA_DOCK_LEDS_3_CLIP_A 0x01 /* Mic A Clip LED on */
+#define EMU_HANA_DOCK_LEDS_3_CLIP_B 0x02 /* Mic B Clip LED on */
+#define EMU_HANA_DOCK_LEDS_3_SIGNAL_A 0x04 /* Signal A Clip LED on */
+#define EMU_HANA_DOCK_LEDS_3_SIGNAL_B 0x08 /* Signal B Clip LED on */
+#define EMU_HANA_DOCK_LEDS_3_MANUAL_CLIP 0x10 /* Manual Clip detection */
+#define EMU_HANA_DOCK_LEDS_3_MANUAL_SIGNAL 0x20 /* Manual Signal detection */
+
+#define EMU_HANA_ADC_PADS 0x10 /* 0000xxx 3 bit Audio Dock ADC 14dB pads */
+#define EMU_HANA_DOCK_ADC_PAD1 0x01 /* 14dB Attenuation on Audio Dock ADC 1 */
+#define EMU_HANA_DOCK_ADC_PAD2 0x02 /* 14dB Attenuation on Audio Dock ADC 2 */
+#define EMU_HANA_DOCK_ADC_PAD3 0x04 /* 14dB Attenuation on Audio Dock ADC 3 */
+#define EMU_HANA_0202_ADC_PAD1 0x08 /* 14dB Attenuation on 0202 ADC 1 */
+
+#define EMU_HANA_DOCK_MISC 0x11 /* 0xxxxxx 6 bit Audio Dock misc bits */
+#define EMU_HANA_DOCK_DAC1_MUTE 0x01 /* DAC 1 Mute */
+#define EMU_HANA_DOCK_DAC2_MUTE 0x02 /* DAC 2 Mute */
+#define EMU_HANA_DOCK_DAC3_MUTE 0x04 /* DAC 3 Mute */
+#define EMU_HANA_DOCK_DAC4_MUTE 0x08 /* DAC 4 Mute */
+#define EMU_HANA_DOCK_PHONES_192_DAC1 0x00 /* DAC 1 Headphones source at 192kHz */
+#define EMU_HANA_DOCK_PHONES_192_DAC2 0x10 /* DAC 2 Headphones source at 192kHz */
+#define EMU_HANA_DOCK_PHONES_192_DAC3 0x20 /* DAC 3 Headphones source at 192kHz */
+#define EMU_HANA_DOCK_PHONES_192_DAC4 0x30 /* DAC 4 Headphones source at 192kHz */
+
+#define EMU_HANA_MIDI_OUT 0x12 /* 00xxxxx 5 bit Source for each MIDI out port */
+#define EMU_HANA_MIDI_OUT_0202 0x01 /* 0202 MIDI from Alice 2. 0 = A, 1 = B */
+#define EMU_HANA_MIDI_OUT_DOCK1 0x02 /* Audio Dock MIDI1 front, from Alice 2. 0 = A, 1 = B */
+#define EMU_HANA_MIDI_OUT_DOCK2 0x04 /* Audio Dock MIDI2 rear, from Alice 2. 0 = A, 1 = B */
+#define EMU_HANA_MIDI_OUT_SYNC2 0x08 /* Sync card. Not the actual MIDI out jack. 0 = A, 1 = B */
+#define EMU_HANA_MIDI_OUT_LOOP 0x10 /* 0 = bits (3:0) normal. 1 = MIDI loopback enabled. */
+
+#define EMU_HANA_DAC_PADS 0x13 /* 00xxxxx 5 bit DAC 14dB attenuation pads */
+#define EMU_HANA_DOCK_DAC_PAD1 0x01 /* 14dB Attenuation on AudioDock DAC 1. Left and Right */
+#define EMU_HANA_DOCK_DAC_PAD2 0x02 /* 14dB Attenuation on AudioDock DAC 2. Left and Right */
+#define EMU_HANA_DOCK_DAC_PAD3 0x04 /* 14dB Attenuation on AudioDock DAC 3. Left and Right */
+#define EMU_HANA_DOCK_DAC_PAD4 0x08 /* 14dB Attenuation on AudioDock DAC 4. Left and Right */
+#define EMU_HANA_0202_DAC_PAD1 0x10 /* 14dB Attenuation on 0202 DAC 1. Left and Right */
+
+/* 0x14 - 0x1f Unused R/W registers */
+#define EMU_HANA_IRQ_STATUS 0x20 /* 000xxxx 4 bits IRQ Status */
+#if 0 /* Already defined for reg 0x09 IRQ_ENABLE */
+#define EMU_HANA_IRQ_WCLK_CHANGED 0x01
+#define EMU_HANA_IRQ_ADAT 0x02
+#define EMU_HANA_IRQ_DOCK 0x04
+#define EMU_HANA_IRQ_DOCK_LOST 0x08
+#endif
+
+#define EMU_HANA_OPTION_CARDS 0x21 /* 000xxxx 4 bits Presence of option cards */
+#define EMU_HANA_OPTION_HAMOA 0x01 /* HAMOA card present */
+#define EMU_HANA_OPTION_SYNC 0x02 /* Sync card present */
+#define EMU_HANA_OPTION_DOCK_ONLINE 0x04 /* Audio Dock online and FPGA configured */
+#define EMU_HANA_OPTION_DOCK_OFFLINE 0x08 /* Audio Dock online and FPGA not configured */
+
+#define EMU_HANA_ID 0x22 /* 1010101 7 bits ID byte & 0x7f = 0x55 */
+
+#define EMU_HANA_MAJOR_REV 0x23 /* 0000xxx 3 bit Hana FPGA Major rev */
+#define EMU_HANA_MINOR_REV 0x24 /* 0000xxx 3 bit Hana FPGA Minor rev */
+
+#define EMU_DOCK_MAJOR_REV 0x25 /* 0000xxx 3 bit Audio Dock FPGA Major rev */
+#define EMU_DOCK_MINOR_REV 0x26 /* 0000xxx 3 bit Audio Dock FPGA Minor rev */
+
+#define EMU_DOCK_BOARD_ID 0x27 /* 00000xx 2 bits Audio Dock ID pins */
+#define EMU_DOCK_BOARD_ID0 0x00 /* ID bit 0 */
+#define EMU_DOCK_BOARD_ID1 0x03 /* ID bit 1 */
+
+#define EMU_HANA_WC_SPDIF_HI 0x28 /* 0xxxxxx 6 bit SPDIF IN Word clock, upper 6 bits */
+#define EMU_HANA_WC_SPDIF_LO 0x29 /* 0xxxxxx 6 bit SPDIF IN Word clock, lower 6 bits */
+
+#define EMU_HANA_WC_ADAT_HI 0x2a /* 0xxxxxx 6 bit ADAT IN Word clock, upper 6 bits */
+#define EMU_HANA_WC_ADAT_LO 0x2b /* 0xxxxxx 6 bit ADAT IN Word clock, lower 6 bits */
+
+#define EMU_HANA_WC_BNC_LO 0x2c /* 0xxxxxx 6 bit BNC IN Word clock, lower 6 bits */
+#define EMU_HANA_WC_BNC_HI 0x2d /* 0xxxxxx 6 bit BNC IN Word clock, upper 6 bits */
+
+#define EMU_HANA2_WC_SPDIF_HI 0x2e /* 0xxxxxx 6 bit HANA2 SPDIF IN Word clock, upper 6 bits */
+#define EMU_HANA2_WC_SPDIF_LO 0x2f /* 0xxxxxx 6 bit HANA2 SPDIF IN Word clock, lower 6 bits */
+/* 0x30 - 0x3f Unused Read only registers */
+
+/************************************************************************************************/
+/* EMU1010m HANA Destinations */
+/************************************************************************************************/
+#define EMU_DST_ALICE2_EMU32_0 0x000f /* 16 EMU32 channels to Alice2 +0 to +0xf */
+#define EMU_DST_ALICE2_EMU32_1 0x0000 /* 16 EMU32 channels to Alice2 +0 to +0xf */
+#define EMU_DST_ALICE2_EMU32_2 0x0001 /* 16 EMU32 channels to Alice2 +0 to +0xf */
+#define EMU_DST_ALICE2_EMU32_3 0x0002 /* 16 EMU32 channels to Alice2 +0 to +0xf */
+#define EMU_DST_ALICE2_EMU32_4 0x0003 /* 16 EMU32 channels to Alice2 +0 to +0xf */
+#define EMU_DST_ALICE2_EMU32_5 0x0004 /* 16 EMU32 channels to Alice2 +0 to +0xf */
+#define EMU_DST_ALICE2_EMU32_6 0x0005 /* 16 EMU32 channels to Alice2 +0 to +0xf */
+#define EMU_DST_ALICE2_EMU32_7 0x0006 /* 16 EMU32 channels to Alice2 +0 to +0xf */
+#define EMU_DST_ALICE2_EMU32_8 0x0007 /* 16 EMU32 channels to Alice2 +0 to +0xf */
+#define EMU_DST_ALICE2_EMU32_9 0x0008 /* 16 EMU32 channels to Alice2 +0 to +0xf */
+#define EMU_DST_ALICE2_EMU32_A 0x0009 /* 16 EMU32 channels to Alice2 +0 to +0xf */
+#define EMU_DST_ALICE2_EMU32_B 0x000a /* 16 EMU32 channels to Alice2 +0 to +0xf */
+#define EMU_DST_ALICE2_EMU32_C 0x000b /* 16 EMU32 channels to Alice2 +0 to +0xf */
+#define EMU_DST_ALICE2_EMU32_D 0x000c /* 16 EMU32 channels to Alice2 +0 to +0xf */
+#define EMU_DST_ALICE2_EMU32_E 0x000d /* 16 EMU32 channels to Alice2 +0 to +0xf */
+#define EMU_DST_ALICE2_EMU32_F 0x000e /* 16 EMU32 channels to Alice2 +0 to +0xf */
+#define EMU_DST_DOCK_DAC1_LEFT1 0x0100 /* Audio Dock DAC1 Left, 1st or 48kHz only */
+#define EMU_DST_DOCK_DAC1_LEFT2 0x0101 /* Audio Dock DAC1 Left, 2nd or 96kHz */
+#define EMU_DST_DOCK_DAC1_LEFT3 0x0102 /* Audio Dock DAC1 Left, 3rd or 192kHz */
+#define EMU_DST_DOCK_DAC1_LEFT4 0x0103 /* Audio Dock DAC1 Left, 4th or 192kHz */
+#define EMU_DST_DOCK_DAC1_RIGHT1 0x0104 /* Audio Dock DAC1 Right, 1st or 48kHz only */
+#define EMU_DST_DOCK_DAC1_RIGHT2 0x0105 /* Audio Dock DAC1 Right, 2nd or 96kHz */
+#define EMU_DST_DOCK_DAC1_RIGHT3 0x0106 /* Audio Dock DAC1 Right, 3rd or 192kHz */
+#define EMU_DST_DOCK_DAC1_RIGHT4 0x0107 /* Audio Dock DAC1 Right, 4th or 192kHz */
+#define EMU_DST_DOCK_DAC2_LEFT1 0x0108 /* Audio Dock DAC2 Left, 1st or 48kHz only */
+#define EMU_DST_DOCK_DAC2_LEFT2 0x0109 /* Audio Dock DAC2 Left, 2nd or 96kHz */
+#define EMU_DST_DOCK_DAC2_LEFT3 0x010a /* Audio Dock DAC2 Left, 3rd or 192kHz */
+#define EMU_DST_DOCK_DAC2_LEFT4 0x010b /* Audio Dock DAC2 Left, 4th or 192kHz */
+#define EMU_DST_DOCK_DAC2_RIGHT1 0x010c /* Audio Dock DAC2 Right, 1st or 48kHz only */
+#define EMU_DST_DOCK_DAC2_RIGHT2 0x010d /* Audio Dock DAC2 Right, 2nd or 96kHz */
+#define EMU_DST_DOCK_DAC2_RIGHT3 0x010e /* Audio Dock DAC2 Right, 3rd or 192kHz */
+#define EMU_DST_DOCK_DAC2_RIGHT4 0x010f /* Audio Dock DAC2 Right, 4th or 192kHz */
+#define EMU_DST_DOCK_DAC3_LEFT1 0x0110 /* Audio Dock DAC1 Left, 1st or 48kHz only */
+#define EMU_DST_DOCK_DAC3_LEFT2 0x0111 /* Audio Dock DAC1 Left, 2nd or 96kHz */
+#define EMU_DST_DOCK_DAC3_LEFT3 0x0112 /* Audio Dock DAC1 Left, 3rd or 192kHz */
+#define EMU_DST_DOCK_DAC3_LEFT4 0x0113 /* Audio Dock DAC1 Left, 4th or 192kHz */
+#define EMU_DST_DOCK_PHONES_LEFT1 0x0112 /* Audio Dock PHONES Left, 1st or 48kHz only */
+#define EMU_DST_DOCK_PHONES_LEFT2 0x0113 /* Audio Dock PHONES Left, 2nd or 96kHz */
+#define EMU_DST_DOCK_DAC3_RIGHT1 0x0114 /* Audio Dock DAC1 Right, 1st or 48kHz only */
+#define EMU_DST_DOCK_DAC3_RIGHT2 0x0115 /* Audio Dock DAC1 Right, 2nd or 96kHz */
+#define EMU_DST_DOCK_DAC3_RIGHT3 0x0116 /* Audio Dock DAC1 Right, 3rd or 192kHz */
+#define EMU_DST_DOCK_DAC3_RIGHT4 0x0117 /* Audio Dock DAC1 Right, 4th or 192kHz */
+#define EMU_DST_DOCK_PHONES_RIGHT1 0x0116 /* Audio Dock PHONES Right, 1st or 48kHz only */
+#define EMU_DST_DOCK_PHONES_RIGHT2 0x0117 /* Audio Dock PHONES Right, 2nd or 96kHz */
+#define EMU_DST_DOCK_DAC4_LEFT1 0x0118 /* Audio Dock DAC2 Left, 1st or 48kHz only */
+#define EMU_DST_DOCK_DAC4_LEFT2 0x0119 /* Audio Dock DAC2 Left, 2nd or 96kHz */
+#define EMU_DST_DOCK_DAC4_LEFT3 0x011a /* Audio Dock DAC2 Left, 3rd or 192kHz */
+#define EMU_DST_DOCK_DAC4_LEFT4 0x011b /* Audio Dock DAC2 Left, 4th or 192kHz */
+#define EMU_DST_DOCK_SPDIF_LEFT1 0x011a /* Audio Dock SPDIF Left, 1st or 48kHz only */
+#define EMU_DST_DOCK_SPDIF_LEFT2 0x011b /* Audio Dock SPDIF Left, 2nd or 96kHz */
+#define EMU_DST_DOCK_DAC4_RIGHT1 0x011c /* Audio Dock DAC2 Right, 1st or 48kHz only */
+#define EMU_DST_DOCK_DAC4_RIGHT2 0x011d /* Audio Dock DAC2 Right, 2nd or 96kHz */
+#define EMU_DST_DOCK_DAC4_RIGHT3 0x011e /* Audio Dock DAC2 Right, 3rd or 192kHz */
+#define EMU_DST_DOCK_DAC4_RIGHT4 0x011f /* Audio Dock DAC2 Right, 4th or 192kHz */
+#define EMU_DST_DOCK_SPDIF_RIGHT1 0x011e /* Audio Dock SPDIF Right, 1st or 48kHz only */
+#define EMU_DST_DOCK_SPDIF_RIGHT2 0x011f /* Audio Dock SPDIF Right, 2nd or 96kHz */
+#define EMU_DST_HANA_SPDIF_LEFT1 0x0200 /* Hana SPDIF Left, 1st or 48kHz only */
+#define EMU_DST_HANA_SPDIF_LEFT2 0x0202 /* Hana SPDIF Left, 2nd or 96kHz */
+#define EMU_DST_HANA_SPDIF_RIGHT1 0x0201 /* Hana SPDIF Right, 1st or 48kHz only */
+#define EMU_DST_HANA_SPDIF_RIGHT2 0x0203 /* Hana SPDIF Right, 2nd or 96kHz */
+#define EMU_DST_HAMOA_DAC_LEFT1 0x0300 /* Hamoa DAC Left, 1st or 48kHz only */
+#define EMU_DST_HAMOA_DAC_LEFT2 0x0302 /* Hamoa DAC Left, 2nd or 96kHz */
+#define EMU_DST_HAMOA_DAC_LEFT3 0x0304 /* Hamoa DAC Left, 3rd or 192kHz */
+#define EMU_DST_HAMOA_DAC_LEFT4 0x0306 /* Hamoa DAC Left, 4th or 192kHz */
+#define EMU_DST_HAMOA_DAC_RIGHT1 0x0301 /* Hamoa DAC Right, 1st or 48kHz only */
+#define EMU_DST_HAMOA_DAC_RIGHT2 0x0303 /* Hamoa DAC Right, 2nd or 96kHz */
+#define EMU_DST_HAMOA_DAC_RIGHT3 0x0305 /* Hamoa DAC Right, 3rd or 192kHz */
+#define EMU_DST_HAMOA_DAC_RIGHT4 0x0307 /* Hamoa DAC Right, 4th or 192kHz */
+#define EMU_DST_HANA_ADAT 0x0400 /* Hana ADAT 8 channel out +0 to +7 */
+#define EMU_DST_ALICE_I2S0_LEFT 0x0500 /* Alice2 I2S0 Left */
+#define EMU_DST_ALICE_I2S0_RIGHT 0x0501 /* Alice2 I2S0 Right */
+#define EMU_DST_ALICE_I2S1_LEFT 0x0600 /* Alice2 I2S1 Left */
+#define EMU_DST_ALICE_I2S1_RIGHT 0x0601 /* Alice2 I2S1 Right */
+#define EMU_DST_ALICE_I2S2_LEFT 0x0700 /* Alice2 I2S2 Left */
+#define EMU_DST_ALICE_I2S2_RIGHT 0x0701 /* Alice2 I2S2 Right */
+
+/************************************************************************************************/
+/* EMU1010m HANA Sources */
+/************************************************************************************************/
+#define EMU_SRC_SILENCE 0x0000 /* Silence */
+#define EMU_SRC_DOCK_MIC_A1 0x0100 /* Audio Dock Mic A, 1st or 48kHz only */
+#define EMU_SRC_DOCK_MIC_A2 0x0101 /* Audio Dock Mic A, 2nd or 96kHz */
+#define EMU_SRC_DOCK_MIC_A3 0x0102 /* Audio Dock Mic A, 3rd or 192kHz */
+#define EMU_SRC_DOCK_MIC_A4 0x0103 /* Audio Dock Mic A, 4th or 192kHz */
+#define EMU_SRC_DOCK_MIC_B1 0x0104 /* Audio Dock Mic B, 1st or 48kHz only */
+#define EMU_SRC_DOCK_MIC_B2 0x0105 /* Audio Dock Mic B, 2nd or 96kHz */
+#define EMU_SRC_DOCK_MIC_B3 0x0106 /* Audio Dock Mic B, 3rd or 192kHz */
+#define EMU_SRC_DOCK_MIC_B4 0x0107 /* Audio Dock Mic B, 4th or 192kHz */
+#define EMU_SRC_DOCK_ADC1_LEFT1 0x0108 /* Audio Dock ADC1 Left, 1st or 48kHz only */
+#define EMU_SRC_DOCK_ADC1_LEFT2 0x0109 /* Audio Dock ADC1 Left, 2nd or 96kHz */
+#define EMU_SRC_DOCK_ADC1_LEFT3 0x010a /* Audio Dock ADC1 Left, 3rd or 192kHz */
+#define EMU_SRC_DOCK_ADC1_LEFT4 0x010b /* Audio Dock ADC1 Left, 4th or 192kHz */
+#define EMU_SRC_DOCK_ADC1_RIGHT1 0x010c /* Audio Dock ADC1 Right, 1st or 48kHz only */
+#define EMU_SRC_DOCK_ADC1_RIGHT2 0x010d /* Audio Dock ADC1 Right, 2nd or 96kHz */
+#define EMU_SRC_DOCK_ADC1_RIGHT3 0x010e /* Audio Dock ADC1 Right, 3rd or 192kHz */
+#define EMU_SRC_DOCK_ADC1_RIGHT4 0x010f /* Audio Dock ADC1 Right, 4th or 192kHz */
+#define EMU_SRC_DOCK_ADC2_LEFT1 0x0110 /* Audio Dock ADC2 Left, 1st or 48kHz only */
+#define EMU_SRC_DOCK_ADC2_LEFT2 0x0111 /* Audio Dock ADC2 Left, 2nd or 96kHz */
+#define EMU_SRC_DOCK_ADC2_LEFT3 0x0112 /* Audio Dock ADC2 Left, 3rd or 192kHz */
+#define EMU_SRC_DOCK_ADC2_LEFT4 0x0113 /* Audio Dock ADC2 Left, 4th or 192kHz */
+#define EMU_SRC_DOCK_ADC2_RIGHT1 0x0114 /* Audio Dock ADC2 Right, 1st or 48kHz only */
+#define EMU_SRC_DOCK_ADC2_RIGHT2 0x0115 /* Audio Dock ADC2 Right, 2nd or 96kHz */
+#define EMU_SRC_DOCK_ADC2_RIGHT3 0x0116 /* Audio Dock ADC2 Right, 3rd or 192kHz */
+#define EMU_SRC_DOCK_ADC2_RIGHT4 0x0117 /* Audio Dock ADC2 Right, 4th or 192kHz */
+#define EMU_SRC_DOCK_ADC3_LEFT1 0x0118 /* Audio Dock ADC3 Left, 1st or 48kHz only */
+#define EMU_SRC_DOCK_ADC3_LEFT2 0x0119 /* Audio Dock ADC3 Left, 2nd or 96kHz */
+#define EMU_SRC_DOCK_ADC3_LEFT3 0x011a /* Audio Dock ADC3 Left, 3rd or 192kHz */
+#define EMU_SRC_DOCK_ADC3_LEFT4 0x011b /* Audio Dock ADC3 Left, 4th or 192kHz */
+#define EMU_SRC_DOCK_ADC3_RIGHT1 0x011c /* Audio Dock ADC3 Right, 1st or 48kHz only */
+#define EMU_SRC_DOCK_ADC3_RIGHT2 0x011d /* Audio Dock ADC3 Right, 2nd or 96kHz */
+#define EMU_SRC_DOCK_ADC3_RIGHT3 0x011e /* Audio Dock ADC3 Right, 3rd or 192kHz */
+#define EMU_SRC_DOCK_ADC3_RIGHT4 0x011f /* Audio Dock ADC3 Right, 4th or 192kHz */
+#define EMU_SRC_HAMOA_ADC_LEFT1 0x0200 /* Hamoa ADC Left, 1st or 48kHz only */
+#define EMU_SRC_HAMOA_ADC_LEFT2 0x0202 /* Hamoa ADC Left, 2nd or 96kHz */
+#define EMU_SRC_HAMOA_ADC_LEFT3 0x0204 /* Hamoa ADC Left, 3rd or 192kHz */
+#define EMU_SRC_HAMOA_ADC_LEFT4 0x0206 /* Hamoa ADC Left, 4th or 192kHz */
+#define EMU_SRC_HAMOA_ADC_RIGHT1 0x0201 /* Hamoa ADC Right, 1st or 48kHz only */
+#define EMU_SRC_HAMOA_ADC_RIGHT2 0x0203 /* Hamoa ADC Right, 2nd or 96kHz */
+#define EMU_SRC_HAMOA_ADC_RIGHT3 0x0205 /* Hamoa ADC Right, 3rd or 192kHz */
+#define EMU_SRC_HAMOA_ADC_RIGHT4 0x0207 /* Hamoa ADC Right, 4th or 192kHz */
+#define EMU_SRC_ALICE_EMU32A 0x0300 /* Alice2 EMU32a 16 outputs. +0 to +0xf */
+#define EMU_SRC_ALICE_EMU32B 0x0310 /* Alice2 EMU32b 16 outputs. +0 to +0xf */
+#define EMU_SRC_HANA_ADAT 0x0400 /* Hana ADAT 8 channel in +0 to +7 */
+#define EMU_SRC_HANA_SPDIF_LEFT1 0x0500 /* Hana SPDIF Left, 1st or 48kHz only */
+#define EMU_SRC_HANA_SPDIF_LEFT2 0x0502 /* Hana SPDIF Left, 2nd or 96kHz */
+#define EMU_SRC_HANA_SPDIF_RIGHT1 0x0501 /* Hana SPDIF Right, 1st or 48kHz only */
+#define EMU_SRC_HANA_SPDIF_RIGHT2 0x0503 /* Hana SPDIF Right, 2nd or 96kHz */
+/* 0x600 and 0x700 no used */
/* ------------------- STRUCTURES -------------------- */
@@ -1063,7 +1425,7 @@ struct snd_emu_chip_details {
unsigned char spdif_bug; /* Has Spdif phasing bug */
unsigned char ac97_chip; /* Has an AC97 chip: 1 = mandatory, 2 = optional */
unsigned char ecard; /* APS EEPROM */
- unsigned char emu1212m; /* EMU 1212m card */
+ unsigned char emu1010; /* EMU 1010m card */
unsigned char spi_dac; /* SPI interface for DAC */
unsigned char i2c_adc; /* I2C interface for ADC */
unsigned char adc_1361t; /* Use Philips 1361T ADC */
@@ -1072,6 +1434,14 @@ struct snd_emu_chip_details {
const char *id; /* for backward compatibility - can be NULL if not needed */
};
+struct snd_emu1010 {
+ unsigned int output_source[64];
+ unsigned int input_source[64];
+ unsigned int adc_pads; /* bit mask */
+ unsigned int dac_pads; /* bit mask */
+ unsigned int internal_clock; /* 44100 or 48000 */
+};
+
struct snd_emu10k1 {
int irq;
@@ -1079,6 +1449,7 @@ struct snd_emu10k1 {
unsigned int tos_link: 1, /* tos link detected */
rear_ac97: 1, /* rear channels are on AC'97 */
enable_ir: 1;
+ unsigned int support_tlv :1;
/* Contains profile of card capabilities */
const struct snd_emu_chip_details *card_capabilities;
unsigned int audigy; /* is Audigy? */
@@ -1104,6 +1475,8 @@ struct snd_emu10k1 {
spinlock_t memblk_lock;
unsigned int spdif_bits[3]; /* s/pdif out setup */
+ unsigned int i2c_capture_source;
+ u8 i2c_capture_volume[4][2];
struct snd_emu10k1_fx8010 fx8010; /* FX8010 info */
int gpr_base;
@@ -1132,6 +1505,7 @@ struct snd_emu10k1 {
int p16v_device_offset;
u32 p16v_capture_source;
u32 p16v_capture_channel;
+ struct snd_emu1010 emu1010;
struct snd_emu10k1_pcm_mixer pcm_mixer[32];
struct snd_emu10k1_pcm_mixer efx_pcm_mixer[NUM_EFX_PLAYBACK];
struct snd_kcontrol *ctl_send_routing;
@@ -1208,6 +1582,10 @@ void snd_emu10k1_ptr_write(struct snd_emu10k1 *emu, unsigned int reg, unsigned i
unsigned int snd_emu10k1_ptr20_read(struct snd_emu10k1 * emu, unsigned int reg, unsigned int chn);
void snd_emu10k1_ptr20_write(struct snd_emu10k1 *emu, unsigned int reg, unsigned int chn, unsigned int data);
int snd_emu10k1_spi_write(struct snd_emu10k1 * emu, unsigned int data);
+int snd_emu10k1_i2c_write(struct snd_emu10k1 *emu, u32 reg, u32 value);
+int snd_emu1010_fpga_write(struct snd_emu10k1 * emu, int reg, int value);
+int snd_emu1010_fpga_read(struct snd_emu10k1 * emu, int reg, int *value);
+int snd_emu1010_fpga_link_dst_src_write(struct snd_emu10k1 * emu, int dst, int src);
unsigned int snd_emu10k1_efx_read(struct snd_emu10k1 *emu, unsigned int pc);
void snd_emu10k1_intr_enable(struct snd_emu10k1 *emu, unsigned int intrenb);
void snd_emu10k1_intr_disable(struct snd_emu10k1 *emu, unsigned int intrenb);
@@ -1524,11 +1902,20 @@ struct snd_emu10k1_fx8010_control_gpr {
unsigned int value[32]; /* initial values */
unsigned int min; /* minimum range */
unsigned int max; /* maximum range */
- union {
- snd_kcontrol_tlv_rw_t *c;
- unsigned int *p;
- } tlv;
unsigned int translation; /* translation type (EMU10K1_GPR_TRANSLATION*) */
+ const unsigned int *tlv;
+};
+
+/* old ABI without TLV support */
+struct snd_emu10k1_fx8010_control_old_gpr {
+ struct snd_ctl_elem_id id;
+ unsigned int vcount;
+ unsigned int count;
+ unsigned short gpr[32];
+ unsigned int value[32];
+ unsigned int min;
+ unsigned int max;
+ unsigned int translation;
};
struct snd_emu10k1_fx8010_code {
@@ -1579,6 +1966,8 @@ struct snd_emu10k1_fx8010_pcm_rec {
unsigned int res2; /* reserved */
};
+#define SNDRV_EMU10K1_VERSION SNDRV_PROTOCOL_VERSION(1, 0, 1)
+
#define SNDRV_EMU10K1_IOCTL_INFO _IOR ('H', 0x10, struct snd_emu10k1_fx8010_info)
#define SNDRV_EMU10K1_IOCTL_CODE_POKE _IOW ('H', 0x11, struct snd_emu10k1_fx8010_code)
#define SNDRV_EMU10K1_IOCTL_CODE_PEEK _IOWR('H', 0x12, struct snd_emu10k1_fx8010_code)
@@ -1587,6 +1976,7 @@ struct snd_emu10k1_fx8010_pcm_rec {
#define SNDRV_EMU10K1_IOCTL_TRAM_PEEK _IOWR('H', 0x22, struct snd_emu10k1_fx8010_tram)
#define SNDRV_EMU10K1_IOCTL_PCM_POKE _IOW ('H', 0x30, struct snd_emu10k1_fx8010_pcm_rec)
#define SNDRV_EMU10K1_IOCTL_PCM_PEEK _IOWR('H', 0x31, struct snd_emu10k1_fx8010_pcm_rec)
+#define SNDRV_EMU10K1_IOCTL_PVERSION _IOR ('H', 0x40, int)
#define SNDRV_EMU10K1_IOCTL_STOP _IO ('H', 0x80)
#define SNDRV_EMU10K1_IOCTL_CONTINUE _IO ('H', 0x81)
#define SNDRV_EMU10K1_IOCTL_ZERO_TRAM_COUNTER _IO ('H', 0x82)
diff --git a/include/sound/pcm.h b/include/sound/pcm.h
index 2f645dfd7f70..ee6bc2d06803 100644
--- a/include/sound/pcm.h
+++ b/include/sound/pcm.h
@@ -56,6 +56,8 @@ struct snd_pcm_hardware {
size_t fifo_size; /* fifo size in bytes */
};
+struct snd_pcm_substream;
+
struct snd_pcm_ops {
int (*open)(struct snd_pcm_substream *substream);
int (*close)(struct snd_pcm_substream *substream);
@@ -384,6 +386,7 @@ struct snd_pcm_substream {
struct snd_info_entry *proc_sw_params_entry;
struct snd_info_entry *proc_status_entry;
struct snd_info_entry *proc_prealloc_entry;
+ struct snd_info_entry *proc_prealloc_max_entry;
#endif
/* misc flags */
unsigned int hw_opened: 1;
@@ -427,6 +430,7 @@ struct snd_pcm {
wait_queue_head_t open_wait;
void *private_data;
void (*private_free) (struct snd_pcm *pcm);
+ struct device *dev; /* actual hw device this belongs to */
#if defined(CONFIG_SND_PCM_OSS) || defined(CONFIG_SND_PCM_OSS_MODULE)
struct snd_pcm_oss oss;
#endif
diff --git a/include/sound/pt2258.h b/include/sound/pt2258.h
new file mode 100644
index 000000000000..160f812faa42
--- /dev/null
+++ b/include/sound/pt2258.h
@@ -0,0 +1,37 @@
+/*
+ * ALSA Driver for the PT2258 volume controller.
+ *
+ * Copyright (c) 2006 Jochen Voss <voss@seehuhn.de>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ */
+
+#ifndef __SOUND_PT2258_H
+#define __SOUND_PT2258_H
+
+struct snd_pt2258 {
+ struct snd_card *card;
+ struct snd_i2c_bus *i2c_bus;
+ struct snd_i2c_device *i2c_dev;
+
+ unsigned char volume[6];
+ int mute;
+};
+
+extern int snd_pt2258_reset(struct snd_pt2258 *pt);
+extern int snd_pt2258_build_controls(struct snd_pt2258 *pt);
+
+#endif /* __SOUND_PT2258_H */
diff --git a/include/sound/sb16_csp.h b/include/sound/sb16_csp.h
index caf6fe21514d..736eac71d053 100644
--- a/include/sound/sb16_csp.h
+++ b/include/sound/sb16_csp.h
@@ -114,9 +114,21 @@ struct snd_sb_csp_info {
#ifdef __KERNEL__
#include "sb.h"
#include "hwdep.h"
+#include <linux/firmware.h>
struct snd_sb_csp;
+/* indices for the known CSP programs */
+enum {
+ CSP_PROGRAM_MULAW,
+ CSP_PROGRAM_ALAW,
+ CSP_PROGRAM_ADPCM_INIT,
+ CSP_PROGRAM_ADPCM_PLAYBACK,
+ CSP_PROGRAM_ADPCM_CAPTURE,
+
+ CSP_PROGRAM_COUNT
+};
+
/*
* CSP operators
*/
@@ -159,6 +171,8 @@ struct snd_sb_csp {
struct snd_kcontrol *qsound_space;
struct mutex access_mutex; /* locking */
+
+ const struct firmware *csp_programs[CSP_PROGRAM_COUNT];
};
int snd_sb_csp_new(struct snd_sb *chip, int device, struct snd_hwdep ** rhwdep);
diff --git a/include/sound/snd_wavefront.h b/include/sound/snd_wavefront.h
index 0b9e5de94ff1..9688d4be918e 100644
--- a/include/sound/snd_wavefront.h
+++ b/include/sound/snd_wavefront.h
@@ -85,6 +85,7 @@ struct _snd_wavefront {
char hw_version[2]; /* major = [0], minor = [1] */
char israw; /* needs Motorola microcode */
char has_fx; /* has FX processor (Tropez+) */
+ char fx_initialized; /* FX's register pages initialized */
char prog_status[WF_MAX_PROGRAM]; /* WF_SLOT_* */
char patch_status[WF_MAX_PATCH]; /* WF_SLOT_* */
char sample_status[WF_MAX_SAMPLE]; /* WF_ST_* | WF_SLOT_* */
@@ -94,6 +95,7 @@ struct _snd_wavefront {
spinlock_t irq_lock;
wait_queue_head_t interrupt_sleeper;
snd_wavefront_midi_t midi; /* ICS2115 MIDI interface */
+ struct snd_card *card;
};
struct _snd_wavefront_card {
diff --git a/include/sound/soc-dapm.h b/include/sound/soc-dapm.h
new file mode 100644
index 000000000000..2b1ae8edc43c
--- /dev/null
+++ b/include/sound/soc-dapm.h
@@ -0,0 +1,286 @@
+/*
+ * linux/sound/soc-dapm.h -- ALSA SoC Dynamic Audio Power Management
+ *
+ * Author: Liam Girdwood
+ * Created: Aug 11th 2005
+ * Copyright: 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.
+ */
+
+#ifndef __LINUX_SND_SOC_DAPM_H
+#define __LINUX_SND_SOC_DAPM_H
+
+#include <linux/device.h>
+#include <linux/types.h>
+#include <sound/control.h>
+#include <sound/soc.h>
+
+/* widget has no PM register bit */
+#define SND_SOC_NOPM -1
+
+/*
+ * SoC dynamic audio power managment
+ *
+ * We can have upto 4 power domains
+ * 1. Codec domain - VREF, VMID
+ * Usually controlled at codec probe/remove, although can be set
+ * at stream time if power is not needed for sidetone, etc.
+ * 2. Platform/Machine domain - physically connected inputs and outputs
+ * Is platform/machine and user action specific, is set in the machine
+ * driver and by userspace e.g when HP are inserted
+ * 3. Path domain - Internal codec path mixers
+ * Are automatically set when mixer and mux settings are
+ * changed by the user.
+ * 4. Stream domain - DAC's and ADC's.
+ * Enabled when stream playback/capture is started.
+ */
+
+/* codec domain */
+#define SND_SOC_DAPM_VMID(wname) \
+{ .id = snd_soc_dapm_vmid, .name = wname, .kcontrols = NULL, \
+ .num_kcontrols = 0}
+
+/* platform domain */
+#define SND_SOC_DAPM_INPUT(wname) \
+{ .id = snd_soc_dapm_input, .name = wname, .kcontrols = NULL, \
+ .num_kcontrols = 0}
+#define SND_SOC_DAPM_OUTPUT(wname) \
+{ .id = snd_soc_dapm_output, .name = wname, .kcontrols = NULL, \
+ .num_kcontrols = 0}
+#define SND_SOC_DAPM_MIC(wname, wevent) \
+{ .id = snd_soc_dapm_mic, .name = wname, .kcontrols = NULL, \
+ .num_kcontrols = 0, .event = wevent, \
+ .event_flags = SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD}
+#define SND_SOC_DAPM_HP(wname, wevent) \
+{ .id = snd_soc_dapm_hp, .name = wname, .kcontrols = NULL, \
+ .num_kcontrols = 0, .event = wevent, \
+ .event_flags = SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD}
+#define SND_SOC_DAPM_SPK(wname, wevent) \
+{ .id = snd_soc_dapm_spk, .name = wname, .kcontrols = NULL, \
+ .num_kcontrols = 0, .event = wevent, \
+ .event_flags = SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD}
+#define SND_SOC_DAPM_LINE(wname, wevent) \
+{ .id = snd_soc_dapm_line, .name = wname, .kcontrols = NULL, \
+ .num_kcontrols = 0, .event = wevent, \
+ .event_flags = SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD}
+
+/* path domain */
+#define SND_SOC_DAPM_PGA(wname, wreg, wshift, winvert,\
+ wcontrols, wncontrols) \
+{ .id = snd_soc_dapm_pga, .name = wname, .reg = wreg, .shift = wshift, \
+ .invert = winvert, .kcontrols = wcontrols, .num_kcontrols = wncontrols}
+#define SND_SOC_DAPM_MIXER(wname, wreg, wshift, winvert, \
+ wcontrols, wncontrols)\
+{ .id = snd_soc_dapm_mixer, .name = wname, .reg = wreg, .shift = wshift, \
+ .invert = winvert, .kcontrols = wcontrols, .num_kcontrols = wncontrols}
+#define SND_SOC_DAPM_MICBIAS(wname, wreg, wshift, winvert) \
+{ .id = snd_soc_dapm_micbias, .name = wname, .reg = wreg, .shift = wshift, \
+ .invert = winvert, .kcontrols = NULL, .num_kcontrols = 0}
+#define SND_SOC_DAPM_SWITCH(wname, wreg, wshift, winvert, wcontrols) \
+{ .id = snd_soc_dapm_switch, .name = wname, .reg = wreg, .shift = wshift, \
+ .invert = winvert, .kcontrols = wcontrols, .num_kcontrols = 1}
+#define SND_SOC_DAPM_MUX(wname, wreg, wshift, winvert, wcontrols) \
+{ .id = snd_soc_dapm_mux, .name = wname, .reg = wreg, .shift = wshift, \
+ .invert = winvert, .kcontrols = wcontrols, .num_kcontrols = 1}
+
+/* path domain with event - event handler must return 0 for success */
+#define SND_SOC_DAPM_PGA_E(wname, wreg, wshift, winvert, wcontrols, \
+ wncontrols, wevent, wflags) \
+{ .id = snd_soc_dapm_pga, .name = wname, .reg = wreg, .shift = wshift, \
+ .invert = winvert, .kcontrols = wcontrols, .num_kcontrols = wncontrols, \
+ .event = wevent, .event_flags = wflags}
+#define SND_SOC_DAPM_MIXER_E(wname, wreg, wshift, winvert, wcontrols, \
+ wncontrols, wevent, wflags) \
+{ .id = snd_soc_dapm_mixer, .name = wname, .reg = wreg, .shift = wshift, \
+ .invert = winvert, .kcontrols = wcontrols, .num_kcontrols = wncontrols, \
+ .event = wevent, .event_flags = wflags}
+#define SND_SOC_DAPM_MICBIAS_E(wname, wreg, wshift, winvert, wevent, wflags) \
+{ .id = snd_soc_dapm_micbias, .name = wname, .reg = wreg, .shift = wshift, \
+ .invert = winvert, .kcontrols = NULL, .num_kcontrols = 0, \
+ .event = wevent, .event_flags = wflags}
+#define SND_SOC_DAPM_SWITCH_E(wname, wreg, wshift, winvert, wcontrols, \
+ wevent, wflags) \
+{ .id = snd_soc_dapm_switch, .name = wname, .reg = wreg, .shift = wshift, \
+ .invert = winvert, .kcontrols = wcontrols, .num_kcontrols = 1 \
+ .event = wevent, .event_flags = wflags}
+#define SND_SOC_DAPM_MUX_E(wname, wreg, wshift, winvert, wcontrols, \
+ wevent, wflags) \
+{ .id = snd_soc_dapm_mux, .name = wname, .reg = wreg, .shift = wshift, \
+ .invert = winvert, .kcontrols = wcontrols, .num_kcontrols = 1, \
+ .event = wevent, .event_flags = wflags}
+
+/* events that are pre and post DAPM */
+#define SND_SOC_DAPM_PRE(wname, wevent) \
+{ .id = snd_soc_dapm_pre, .name = wname, .kcontrols = NULL, \
+ .num_kcontrols = 0, .event = wevent, \
+ .event_flags = SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_PRE_PMD}
+#define SND_SOC_DAPM_POST(wname, wevent) \
+{ .id = snd_soc_dapm_post, .name = wname, .kcontrols = NULL, \
+ .num_kcontrols = 0, .event = wevent, \
+ .event_flags = SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_POST_PMD}
+
+/* stream domain */
+#define SND_SOC_DAPM_DAC(wname, stname, wreg, wshift, winvert) \
+{ .id = snd_soc_dapm_dac, .name = wname, .sname = stname, .reg = wreg, \
+ .shift = wshift, .invert = winvert}
+#define SND_SOC_DAPM_ADC(wname, stname, wreg, wshift, winvert) \
+{ .id = snd_soc_dapm_adc, .name = wname, .sname = stname, .reg = wreg, \
+ .shift = wshift, .invert = winvert}
+
+/* dapm kcontrol types */
+#define SOC_DAPM_SINGLE(xname, reg, shift, mask, invert) \
+{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, \
+ .info = snd_soc_info_volsw, \
+ .get = snd_soc_dapm_get_volsw, .put = snd_soc_dapm_put_volsw, \
+ .private_value = SOC_SINGLE_VALUE(reg, shift, mask, invert) }
+#define SOC_DAPM_DOUBLE(xname, reg, shift_left, shift_right, mask, invert, \
+ power) \
+{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = (xname), \
+ .info = snd_soc_info_volsw, \
+ .get = snd_soc_dapm_get_volsw, .put = snd_soc_dapm_put_volsw, \
+ .private_value = (reg) | ((shift_left) << 8) | ((shift_right) << 12) |\
+ ((mask) << 16) | ((invert) << 24) }
+#define SOC_DAPM_ENUM(xname, xenum) \
+{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, \
+ .info = snd_soc_info_enum_double, \
+ .get = snd_soc_dapm_get_enum_double, \
+ .put = snd_soc_dapm_put_enum_double, \
+ .private_value = (unsigned long)&xenum }
+
+/* dapm stream operations */
+#define SND_SOC_DAPM_STREAM_NOP 0x0
+#define SND_SOC_DAPM_STREAM_START 0x1
+#define SND_SOC_DAPM_STREAM_STOP 0x2
+#define SND_SOC_DAPM_STREAM_SUSPEND 0x4
+#define SND_SOC_DAPM_STREAM_RESUME 0x8
+#define SND_SOC_DAPM_STREAM_PAUSE_PUSH 0x10
+#define SND_SOC_DAPM_STREAM_PAUSE_RELEASE 0x20
+
+/* dapm event types */
+#define SND_SOC_DAPM_PRE_PMU 0x1 /* before widget power up */
+#define SND_SOC_DAPM_POST_PMU 0x2 /* after widget power up */
+#define SND_SOC_DAPM_PRE_PMD 0x4 /* before widget power down */
+#define SND_SOC_DAPM_POST_PMD 0x8 /* after widget power down */
+#define SND_SOC_DAPM_PRE_REG 0x10 /* before audio path setup */
+#define SND_SOC_DAPM_POST_REG 0x20 /* after audio path setup */
+
+/* convenience event type detection */
+#define SND_SOC_DAPM_EVENT_ON(e) \
+ (e & (SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU))
+#define SND_SOC_DAPM_EVENT_OFF(e) \
+ (e & (SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMD))
+
+struct snd_soc_dapm_widget;
+enum snd_soc_dapm_type;
+struct snd_soc_dapm_path;
+struct snd_soc_dapm_pin;
+
+/* dapm controls */
+int snd_soc_dapm_put_volsw(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol);
+int snd_soc_dapm_get_volsw(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol);
+int snd_soc_dapm_get_enum_double(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol);
+int snd_soc_dapm_put_enum_double(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol);
+int snd_soc_dapm_new_control(struct snd_soc_codec *codec,
+ const struct snd_soc_dapm_widget *widget);
+
+/* dapm path setup */
+int 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);
+
+/* dapm events */
+int snd_soc_dapm_stream_event(struct snd_soc_codec *codec, char *stream,
+ int event);
+
+/* dapm sys fs - used by the core */
+int snd_soc_dapm_sys_add(struct device *dev);
+
+/* dapm audio endpoint control */
+int snd_soc_dapm_set_endpoint(struct snd_soc_codec *codec,
+ char *pin, int status);
+int snd_soc_dapm_sync_endpoints(struct snd_soc_codec *codec);
+
+/* dapm widget types */
+enum snd_soc_dapm_type {
+ snd_soc_dapm_input = 0, /* input pin */
+ snd_soc_dapm_output, /* output pin */
+ snd_soc_dapm_mux, /* selects 1 analog signal from many inputs */
+ snd_soc_dapm_mixer, /* mixes several analog signals together */
+ snd_soc_dapm_pga, /* programmable gain/attenuation (volume) */
+ snd_soc_dapm_adc, /* analog to digital converter */
+ snd_soc_dapm_dac, /* digital to analog converter */
+ snd_soc_dapm_micbias, /* microphone bias (power) */
+ snd_soc_dapm_mic, /* microphone */
+ snd_soc_dapm_hp, /* headphones */
+ snd_soc_dapm_spk, /* speaker */
+ snd_soc_dapm_line, /* line input/output */
+ snd_soc_dapm_switch, /* analog switch */
+ snd_soc_dapm_vmid, /* codec bias/vmid - to minimise pops */
+ snd_soc_dapm_pre, /* machine specific pre widget - exec first */
+ snd_soc_dapm_post, /* machine specific post widget - exec last */
+};
+
+/* dapm audio path between two widgets */
+struct snd_soc_dapm_path {
+ char *name;
+ char *long_name;
+
+ /* source (input) and sink (output) widgets */
+ struct snd_soc_dapm_widget *source;
+ struct snd_soc_dapm_widget *sink;
+ struct snd_kcontrol *kcontrol;
+
+ /* status */
+ u32 connect:1; /* source and sink widgets are connected */
+ u32 walked:1; /* path has been walked */
+
+ struct list_head list_source;
+ struct list_head list_sink;
+ struct list_head list;
+};
+
+/* dapm widget */
+struct snd_soc_dapm_widget {
+ enum snd_soc_dapm_type id;
+ char *name; /* widget name */
+ char *sname; /* stream name */
+ struct snd_soc_codec *codec;
+ struct list_head list;
+
+ /* dapm control */
+ short reg; /* negative reg = no direct dapm */
+ unsigned char shift; /* bits to shift */
+ unsigned int saved_value; /* widget saved value */
+ unsigned int value; /* widget current value */
+ unsigned char power:1; /* block power status */
+ unsigned char invert:1; /* invert the power bit */
+ unsigned char active:1; /* active stream on DAC, ADC's */
+ unsigned char connected:1; /* connected codec pin */
+ unsigned char new:1; /* cnew complete */
+ unsigned char ext:1; /* has external widgets */
+ unsigned char muted:1; /* muted for pop reduction */
+ unsigned char suspend:1; /* was active before suspend */
+ unsigned char pmdown:1; /* waiting for timeout */
+
+ /* external events */
+ unsigned short event_flags; /* flags to specify event types */
+ int (*event)(struct snd_soc_dapm_widget*, int);
+
+ /* kcontrols that relate to this widget */
+ int num_kcontrols;
+ const struct snd_kcontrol_new *kcontrols;
+
+ /* widget input and outputs */
+ struct list_head sources;
+ struct list_head sinks;
+};
+
+#endif
diff --git a/include/sound/soc.h b/include/sound/soc.h
new file mode 100644
index 000000000000..b1dc364b8f74
--- /dev/null
+++ b/include/sound/soc.h
@@ -0,0 +1,461 @@
+/*
+ * linux/sound/soc.h -- ALSA SoC Layer
+ *
+ * Author: Liam Girdwood
+ * Created: Aug 11th 2005
+ * Copyright: 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.
+ */
+
+#ifndef __LINUX_SND_SOC_H
+#define __LINUX_SND_SOC_H
+
+#include <linux/platform_device.h>
+#include <linux/types.h>
+#include <linux/workqueue.h>
+#include <sound/driver.h>
+#include <sound/core.h>
+#include <sound/pcm.h>
+#include <sound/control.h>
+#include <sound/ac97_codec.h>
+
+#define SND_SOC_VERSION "0.13.0"
+
+/*
+ * Convenience kcontrol builders
+ */
+#define SOC_SINGLE_VALUE(reg,shift,mask,invert) ((reg) | ((shift) << 8) |\
+ ((shift) << 12) | ((mask) << 16) | ((invert) << 24))
+#define SOC_SINGLE_VALUE_EXT(reg,mask,invert) ((reg) | ((mask) << 16) |\
+ ((invert) << 31))
+#define SOC_SINGLE(xname, reg, shift, mask, invert) \
+{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, \
+ .info = snd_soc_info_volsw, .get = snd_soc_get_volsw,\
+ .put = snd_soc_put_volsw, \
+ .private_value = SOC_SINGLE_VALUE(reg, shift, mask, invert) }
+#define SOC_DOUBLE(xname, reg, shift_left, shift_right, mask, invert) \
+{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = (xname),\
+ .info = snd_soc_info_volsw, .get = snd_soc_get_volsw, \
+ .put = snd_soc_put_volsw, \
+ .private_value = (reg) | ((shift_left) << 8) | \
+ ((shift_right) << 12) | ((mask) << 16) | ((invert) << 24) }
+#define SOC_DOUBLE_R(xname, reg_left, reg_right, shift, mask, invert) \
+{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = (xname), \
+ .info = snd_soc_info_volsw_2r, \
+ .get = snd_soc_get_volsw_2r, .put = snd_soc_put_volsw_2r, \
+ .private_value = (reg_left) | ((shift) << 8) | \
+ ((mask) << 12) | ((invert) << 20) | ((reg_right) << 24) }
+#define SOC_ENUM_DOUBLE(xreg, xshift_l, xshift_r, xmask, xtexts) \
+{ .reg = xreg, .shift_l = xshift_l, .shift_r = xshift_r, \
+ .mask = xmask, .texts = xtexts }
+#define SOC_ENUM_SINGLE(xreg, xshift, xmask, xtexts) \
+ SOC_ENUM_DOUBLE(xreg, xshift, xshift, xmask, xtexts)
+#define SOC_ENUM_SINGLE_EXT(xmask, xtexts) \
+{ .mask = xmask, .texts = xtexts }
+#define SOC_ENUM(xname, xenum) \
+{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname,\
+ .info = snd_soc_info_enum_double, \
+ .get = snd_soc_get_enum_double, .put = snd_soc_put_enum_double, \
+ .private_value = (unsigned long)&xenum }
+#define SOC_SINGLE_EXT(xname, xreg, xshift, xmask, xinvert,\
+ xhandler_get, xhandler_put) \
+{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, \
+ .info = snd_soc_info_volsw, \
+ .get = xhandler_get, .put = xhandler_put, \
+ .private_value = SOC_SINGLE_VALUE(xreg, xshift, xmask, xinvert) }
+#define SOC_SINGLE_BOOL_EXT(xname, xdata, xhandler_get, xhandler_put) \
+{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, \
+ .info = snd_soc_info_bool_ext, \
+ .get = xhandler_get, .put = xhandler_put, \
+ .private_value = xdata }
+#define SOC_ENUM_EXT(xname, xenum, xhandler_get, xhandler_put) \
+{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, \
+ .info = snd_soc_info_enum_ext, \
+ .get = xhandler_get, .put = xhandler_put, \
+ .private_value = (unsigned long)&xenum }
+
+/*
+ * Digital Audio Interface (DAI) types
+ */
+#define SND_SOC_DAI_AC97 0x1
+#define SND_SOC_DAI_I2S 0x2
+#define SND_SOC_DAI_PCM 0x4
+
+/*
+ * 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 hardware signal inversions
+ */
+#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
+
+/*
+ * 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_codec_dai;
+struct snd_soc_cpu_dai;
+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;
+
+/* 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);
+
+/* set runtime hw params */
+int snd_soc_set_runtime_hwparams(struct snd_pcm_substream *substream,
+ const struct snd_pcm_hardware *hw);
+
+/* codec IO */
+#define snd_soc_read(codec, reg) codec->read(codec, reg)
+#define snd_soc_write(codec, reg, value) codec->write(codec, reg, value)
+
+/* codec register bit access */
+int snd_soc_update_bits(struct snd_soc_codec *codec, unsigned short reg,
+ unsigned short mask, unsigned short value);
+int snd_soc_test_bits(struct snd_soc_codec *codec, unsigned short reg,
+ unsigned short mask, unsigned short value);
+
+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);
+
+/*
+ *Controls
+ */
+struct snd_kcontrol *snd_soc_cnew(const struct snd_kcontrol_new *_template,
+ void *data, char *long_name);
+int snd_soc_info_enum_double(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_info *uinfo);
+int snd_soc_info_enum_ext(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_info *uinfo);
+int snd_soc_get_enum_double(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol);
+int snd_soc_put_enum_double(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol);
+int snd_soc_info_volsw(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_info *uinfo);
+int snd_soc_info_volsw_ext(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_info *uinfo);
+int snd_soc_info_bool_ext(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_info *uinfo);
+int snd_soc_get_volsw(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol);
+int snd_soc_put_volsw(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol);
+int snd_soc_info_volsw_2r(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_info *uinfo);
+int snd_soc_get_volsw_2r(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol);
+int snd_soc_put_volsw_2r(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol);
+
+/* SoC PCM stream information */
+struct snd_soc_pcm_stream {
+ char *stream_name;
+ u64 formats; /* SNDRV_PCM_FMTBIT_* */
+ unsigned int rates; /* SNDRV_PCM_RATE_* */
+ unsigned int rate_min; /* min rate */
+ unsigned int rate_max; /* max rate */
+ unsigned int channels_min; /* min channels */
+ unsigned int channels_max; /* max channels */
+ unsigned int active:1; /* stream is in use */
+};
+
+/* SoC audio ops */
+struct snd_soc_ops {
+ int (*startup)(struct snd_pcm_substream *);
+ void (*shutdown)(struct snd_pcm_substream *);
+ int (*hw_params)(struct snd_pcm_substream *, struct snd_pcm_hw_params *);
+ int (*hw_free)(struct snd_pcm_substream *);
+ int (*prepare)(struct snd_pcm_substream *);
+ int (*trigger)(struct snd_pcm_substream *, int);
+};
+
+/* ASoC codec DAI ops */
+struct snd_soc_codec_ops {
+ /* codec DAI clocking configuration */
+ int (*set_sysclk)(struct snd_soc_codec_dai *codec_dai,
+ int clk_id, unsigned int freq, int dir);
+ int (*set_pll)(struct snd_soc_codec_dai *codec_dai,
+ int pll_id, unsigned int freq_in, unsigned int freq_out);
+ int (*set_clkdiv)(struct snd_soc_codec_dai *codec_dai,
+ int div_id, int div);
+
+ /* CPU DAI format configuration */
+ int (*set_fmt)(struct snd_soc_codec_dai *codec_dai,
+ unsigned int fmt);
+ int (*set_tdm_slot)(struct snd_soc_codec_dai *codec_dai,
+ unsigned int mask, int slots);
+ int (*set_tristate)(struct snd_soc_codec_dai *, int tristate);
+
+ /* digital mute */
+ int (*digital_mute)(struct snd_soc_codec_dai *, int mute);
+};
+
+/* ASoC cpu DAI ops */
+struct snd_soc_cpu_ops {
+ /* CPU DAI clocking configuration */
+ int (*set_sysclk)(struct snd_soc_cpu_dai *cpu_dai,
+ int clk_id, unsigned int freq, int dir);
+ int (*set_clkdiv)(struct snd_soc_cpu_dai *cpu_dai,
+ int div_id, int div);
+ int (*set_pll)(struct snd_soc_cpu_dai *cpu_dai,
+ int pll_id, unsigned int freq_in, unsigned int freq_out);
+
+ /* CPU DAI format configuration */
+ int (*set_fmt)(struct snd_soc_cpu_dai *cpu_dai,
+ unsigned int fmt);
+ int (*set_tdm_slot)(struct snd_soc_cpu_dai *cpu_dai,
+ unsigned int mask, int slots);
+ int (*set_tristate)(struct snd_soc_cpu_dai *, int tristate);
+};
+
+/* SoC Codec DAI */
+struct snd_soc_codec_dai {
+ char *name;
+ int id;
+
+ /* DAI capabilities */
+ struct snd_soc_pcm_stream playback;
+ struct snd_soc_pcm_stream capture;
+
+ /* DAI runtime info */
+ struct snd_soc_codec *codec;
+ unsigned int active;
+ unsigned char pop_wait:1;
+
+ /* ops */
+ struct snd_soc_ops ops;
+ struct snd_soc_codec_ops dai_ops;
+
+ /* DAI private data */
+ void *private_data;
+};
+
+/* SoC CPU DAI */
+struct snd_soc_cpu_dai {
+
+ /* DAI description */
+ char *name;
+ unsigned int id;
+ unsigned char type;
+
+ /* DAI callbacks */
+ int (*probe)(struct platform_device *pdev);
+ void (*remove)(struct platform_device *pdev);
+ int (*suspend)(struct platform_device *pdev,
+ struct snd_soc_cpu_dai *cpu_dai);
+ int (*resume)(struct platform_device *pdev,
+ struct snd_soc_cpu_dai *cpu_dai);
+
+ /* ops */
+ struct snd_soc_ops ops;
+ struct snd_soc_cpu_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;
+ unsigned char active: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;
+
+ /* callbacks */
+ int (*dapm_event)(struct snd_soc_codec *codec, int event);
+
+ /* runtime */
+ struct snd_card *card;
+ struct snd_ac97 *ac97; /* for ad-hoc ac97 devices */
+ unsigned int active;
+ unsigned int pcm_devs;
+ void *private_data;
+
+ /* codec IO */
+ void *control_data; /* codec control (i2c/3wire) data */
+ unsigned int (*read)(struct snd_soc_codec *, unsigned int);
+ int (*write)(struct snd_soc_codec *, unsigned int, unsigned int);
+ hw_write_t hw_write;
+ hw_read_t hw_read;
+ void *reg_cache;
+ short reg_cache_size;
+ short reg_cache_step;
+
+ /* dapm */
+ struct list_head dapm_widgets;
+ struct list_head dapm_paths;
+ unsigned int dapm_state;
+ unsigned int suspend_dapm_state;
+ struct delayed_work delayed_work;
+
+ /* codec DAI's */
+ struct snd_soc_codec_dai *dai;
+ unsigned int num_dai;
+};
+
+/* codec device */
+struct snd_soc_codec_device {
+ int (*probe)(struct platform_device *pdev);
+ int (*remove)(struct platform_device *pdev);
+ int (*suspend)(struct platform_device *pdev, pm_message_t state);
+ int (*resume)(struct platform_device *pdev);
+};
+
+/* SoC platform interface */
+struct snd_soc_platform {
+ char *name;
+
+ int (*probe)(struct platform_device *pdev);
+ int (*remove)(struct platform_device *pdev);
+ int (*suspend)(struct platform_device *pdev,
+ struct snd_soc_cpu_dai *cpu_dai);
+ int (*resume)(struct platform_device *pdev,
+ struct snd_soc_cpu_dai *cpu_dai);
+
+ /* pcm creation and destruction */
+ int (*pcm_new)(struct snd_card *, struct snd_soc_codec_dai *,
+ struct snd_pcm *);
+ void (*pcm_free)(struct snd_pcm *);
+
+ /* platform stream ops */
+ struct snd_pcm_ops *pcm_ops;
+};
+
+/* SoC machine DAI configuration, glues a codec and cpu DAI together */
+struct snd_soc_dai_link {
+ char *name; /* Codec name */
+ char *stream_name; /* Stream name */
+
+ /* DAI */
+ struct snd_soc_codec_dai *codec_dai;
+ struct snd_soc_cpu_dai *cpu_dai;
+
+ /* machine stream operations */
+ struct snd_soc_ops *ops;
+
+ /* codec/machine specific init - e.g. add machine controls */
+ int (*init)(struct snd_soc_codec *codec);
+};
+
+/* SoC machine */
+struct snd_soc_machine {
+ char *name;
+
+ int (*probe)(struct platform_device *pdev);
+ int (*remove)(struct platform_device *pdev);
+
+ /* the pre and post PM functions are used to do any PM work before and
+ * after the codec and DAI's do any PM work. */
+ int (*suspend_pre)(struct platform_device *pdev, pm_message_t state);
+ int (*suspend_post)(struct platform_device *pdev, pm_message_t state);
+ int (*resume_pre)(struct platform_device *pdev);
+ int (*resume_post)(struct platform_device *pdev);
+
+ /* CPU <--> Codec DAI links */
+ struct snd_soc_dai_link *dai_link;
+ int num_links;
+};
+
+/* 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_codec *codec;
+ struct snd_soc_codec_device *codec_dev;
+ struct delayed_work delayed_work;
+ void *codec_data;
+};
+
+/* runtime channel data */
+struct snd_soc_pcm_runtime {
+ struct snd_soc_dai_link *dai;
+ struct snd_soc_device *socdev;
+};
+
+/* enumerated kcontrol */
+struct soc_enum {
+ unsigned short reg;
+ unsigned short reg2;
+ unsigned char shift_l;
+ unsigned char shift_r;
+ unsigned int mask;
+ const char **texts;
+ void *dapm;
+};
+
+#endif
diff --git a/include/sound/typedefs.h b/include/sound/typedefs.h
deleted file mode 100644
index f454b0206b93..000000000000
--- a/include/sound/typedefs.h
+++ /dev/null
@@ -1,173 +0,0 @@
-/*
- * Typedef's for backward compatibility (for out-of-kernel drivers)
- *
- * This file will be removed soon in future
- */
-
-/* core stuff */
-typedef struct snd_card snd_card_t;
-typedef struct snd_device snd_device_t;
-typedef struct snd_device_ops snd_device_ops_t;
-typedef enum snd_card_type snd_card_type_t;
-typedef struct snd_minor snd_minor_t;
-
-/* info */
-typedef struct snd_info_entry snd_info_entry_t;
-typedef struct snd_info_buffer snd_info_buffer_t;
-
-/* control */
-typedef struct snd_ctl_file snd_ctl_file_t;
-typedef struct snd_kcontrol snd_kcontrol_t;
-typedef struct snd_kcontrol_new snd_kcontrol_new_t;
-typedef struct snd_kcontrol_volatile snd_kcontrol_volatile_t;
-typedef struct snd_kctl_event snd_kctl_event_t;
-typedef struct snd_aes_iec958 snd_aes_iec958_t;
-typedef struct snd_ctl_card_info snd_ctl_card_info_t;
-typedef struct snd_ctl_elem_id snd_ctl_elem_id_t;
-typedef struct snd_ctl_elem_list snd_ctl_elem_list_t;
-typedef struct snd_ctl_elem_info snd_ctl_elem_info_t;
-typedef struct snd_ctl_elem_value snd_ctl_elem_value_t;
-typedef struct snd_ctl_event snd_ctl_event_t;
-#if defined(CONFIG_SND_MIXER_OSS) || defined(CONFIG_SND_MIXER_OSS_MODULE)
-typedef struct snd_mixer_oss snd_mixer_oss_t;
-#endif
-
-/* timer */
-typedef struct snd_timer snd_timer_t;
-typedef struct snd_timer_instance snd_timer_instance_t;
-typedef struct snd_timer_id snd_timer_id_t;
-typedef struct snd_timer_ginfo snd_timer_ginfo_t;
-typedef struct snd_timer_gparams snd_timer_gparams_t;
-typedef struct snd_timer_gstatus snd_timer_gstatus_t;
-typedef struct snd_timer_select snd_timer_select_t;
-typedef struct snd_timer_info snd_timer_info_t;
-typedef struct snd_timer_params snd_timer_params_t;
-typedef struct snd_timer_status snd_timer_status_t;
-typedef struct snd_timer_read snd_timer_read_t;
-typedef struct snd_timer_tread snd_timer_tread_t;
-
-/* PCM */
-typedef struct snd_pcm snd_pcm_t;
-typedef struct snd_pcm_str snd_pcm_str_t;
-typedef struct snd_pcm_substream snd_pcm_substream_t;
-typedef struct snd_pcm_info snd_pcm_info_t;
-typedef struct snd_pcm_hw_params snd_pcm_hw_params_t;
-typedef struct snd_pcm_sw_params snd_pcm_sw_params_t;
-typedef struct snd_pcm_channel_info snd_pcm_channel_info_t;
-typedef struct snd_pcm_status snd_pcm_status_t;
-typedef struct snd_pcm_mmap_status snd_pcm_mmap_status_t;
-typedef struct snd_pcm_mmap_control snd_pcm_mmap_control_t;
-typedef struct snd_mask snd_mask_t;
-typedef struct snd_sg_buf snd_pcm_sgbuf_t;
-
-typedef struct snd_interval snd_interval_t;
-typedef struct snd_xferi snd_xferi_t;
-typedef struct snd_xfern snd_xfern_t;
-typedef struct snd_xferv snd_xferv_t;
-
-typedef struct snd_pcm_file snd_pcm_file_t;
-typedef struct snd_pcm_runtime snd_pcm_runtime_t;
-typedef struct snd_pcm_hardware snd_pcm_hardware_t;
-typedef struct snd_pcm_ops snd_pcm_ops_t;
-typedef struct snd_pcm_hw_rule snd_pcm_hw_rule_t;
-typedef struct snd_pcm_hw_constraints snd_pcm_hw_constraints_t;
-typedef struct snd_ratnum ratnum_t;
-typedef struct snd_ratden ratden_t;
-typedef struct snd_pcm_hw_constraint_ratnums snd_pcm_hw_constraint_ratnums_t;
-typedef struct snd_pcm_hw_constraint_ratdens snd_pcm_hw_constraint_ratdens_t;
-typedef struct snd_pcm_hw_constraint_list snd_pcm_hw_constraint_list_t;
-typedef struct snd_pcm_group snd_pcm_group_t;
-typedef struct snd_pcm_notify snd_pcm_notify_t;
-
-/* rawmidi */
-typedef struct snd_rawmidi snd_rawmidi_t;
-typedef struct snd_rawmidi_info snd_rawmidi_info_t;
-typedef struct snd_rawmidi_params snd_rawmidi_params_t;
-typedef struct snd_rawmidi_status snd_rawmidi_status_t;
-typedef struct snd_rawmidi_runtime snd_rawmidi_runtime_t;
-typedef struct snd_rawmidi_substream snd_rawmidi_substream_t;
-typedef struct snd_rawmidi_str snd_rawmidi_str_t;
-typedef struct snd_rawmidi_ops snd_rawmidi_ops_t;
-typedef struct snd_rawmidi_global_ops snd_rawmidi_global_ops_t;
-typedef struct snd_rawmidi_file snd_rawmidi_file_t;
-
-/* hwdep */
-typedef struct snd_hwdep snd_hwdep_t;
-typedef struct snd_hwdep_info snd_hwdep_info_t;
-typedef struct snd_hwdep_dsp_status snd_hwdep_dsp_status_t;
-typedef struct snd_hwdep_dsp_image snd_hwdep_dsp_image_t;
-typedef struct snd_hwdep_ops snd_hwdep_ops_t;
-
-/* sequencer */
-typedef struct snd_seq_port_info snd_seq_port_info_t;
-typedef struct snd_seq_port_subscribe snd_seq_port_subscribe_t;
-typedef struct snd_seq_event snd_seq_event_t;
-typedef struct snd_seq_addr snd_seq_addr_t;
-typedef struct snd_seq_ev_volume snd_seq_ev_volume_t;
-typedef struct snd_seq_ev_loop snd_seq_ev_loop_t;
-typedef struct snd_seq_remove_events snd_seq_remove_events_t;
-typedef struct snd_seq_query_subs snd_seq_query_subs_t;
-typedef struct snd_seq_system_info snd_seq_system_info_t;
-typedef struct snd_seq_client_info snd_seq_client_info_t;
-typedef struct snd_seq_queue_info snd_seq_queue_info_t;
-typedef struct snd_seq_queue_status snd_seq_queue_status_t;
-typedef struct snd_seq_queue_tempo snd_seq_queue_tempo_t;
-typedef struct snd_seq_queue_owner snd_seq_queue_owner_t;
-typedef struct snd_seq_queue_timer snd_seq_queue_timer_t;
-typedef struct snd_seq_queue_client snd_seq_queue_client_t;
-typedef struct snd_seq_client_pool snd_seq_client_pool_t;
-typedef struct snd_seq_instr snd_seq_instr_t;
-typedef struct snd_seq_instr_data snd_seq_instr_data_t;
-typedef struct snd_seq_instr_header snd_seq_instr_header_t;
-
-typedef struct snd_seq_user_client user_client_t;
-typedef struct snd_seq_kernel_client kernel_client_t;
-typedef struct snd_seq_client client_t;
-typedef struct snd_seq_queue queue_t;
-
-/* seq_device */
-typedef struct snd_seq_device snd_seq_device_t;
-typedef struct snd_seq_dev_ops snd_seq_dev_ops_t;
-
-/* seq_midi */
-typedef struct snd_midi_event snd_midi_event_t;
-
-/* seq_midi_emul */
-typedef struct snd_midi_channel snd_midi_channel_t;
-typedef struct snd_midi_channel_set snd_midi_channel_set_t;
-typedef struct snd_midi_op snd_midi_op_t;
-
-/* seq_oss */
-typedef struct snd_seq_oss_arg snd_seq_oss_arg_t;
-typedef struct snd_seq_oss_callback snd_seq_oss_callback_t;
-typedef struct snd_seq_oss_reg snd_seq_oss_reg_t;
-
-/* virmidi */
-typedef struct snd_virmidi_dev snd_virmidi_dev_t;
-typedef struct snd_virmidi snd_virmidi_t;
-
-/* seq_instr */
-typedef struct snd_seq_kcluster snd_seq_kcluster_t;
-typedef struct snd_seq_kinstr_ops snd_seq_kinstr_ops_t;
-typedef struct snd_seq_kinstr snd_seq_kinstr_t;
-typedef struct snd_seq_kinstr_list snd_seq_kinstr_list_t;
-
-/* ac97 */
-typedef struct snd_ac97_bus ac97_bus_t;
-typedef struct snd_ac97_bus_ops ac97_bus_ops_t;
-typedef struct snd_ac97_template ac97_template_t;
-typedef struct snd_ac97 ac97_t;
-
-/* opl3/4 */
-typedef struct snd_opl3 opl3_t;
-typedef struct snd_opl4 opl4_t;
-
-/* mpu401 */
-typedef struct snd_mpu401 mpu401_t;
-
-/* i2c */
-typedef struct snd_i2c_device snd_i2c_device_t;
-typedef struct snd_i2c_bus snd_i2c_bus_t;
-
-typedef struct snd_ak4531 ak4531_t;
-
diff --git a/include/sound/version.h b/include/sound/version.h
index 20f7babad514..c39b3802cf18 100644
--- a/include/sound/version.h
+++ b/include/sound/version.h
@@ -1,3 +1,3 @@
/* include/version.h. Generated by alsa/ksync script. */
-#define CONFIG_SND_VERSION "1.0.14rc1"
-#define CONFIG_SND_DATE " (Tue Jan 09 09:56:17 2007 UTC)"
+#define CONFIG_SND_VERSION "1.0.14rc2"
+#define CONFIG_SND_DATE " (Fri Feb 09 13:50:10 2007 UTC)"
diff --git a/include/sound/vx_core.h b/include/sound/vx_core.h
index 217394652090..4830651cc4cf 100644
--- a/include/sound/vx_core.h
+++ b/include/sound/vx_core.h
@@ -128,7 +128,7 @@ struct snd_vx_hardware {
unsigned int num_ins;
unsigned int num_outs;
unsigned int output_level_max;
- unsigned int *output_level_db_scale;
+ const unsigned int *output_level_db_scale;
};
/* hwdep id string */
diff --git a/include/sound/ymfpci.h b/include/sound/ymfpci.h
index f3514ee96bd9..203d2b45b788 100644
--- a/include/sound/ymfpci.h
+++ b/include/sound/ymfpci.h
@@ -270,6 +270,7 @@ struct snd_ymfpci_pcm {
struct snd_pcm_substream *substream;
struct snd_ymfpci_voice *voices[2]; /* playback only */
unsigned int running: 1,
+ use_441_slot: 1,
output_front: 1,
output_rear: 1,
swap_rear: 1;
@@ -324,6 +325,7 @@ struct snd_ymfpci {
u32 active_bank;
struct snd_ymfpci_voice voices[64];
+ int src441_used;
struct snd_ac97_bus *ac97_bus;
struct snd_ac97 *ac97;
@@ -346,7 +348,7 @@ struct snd_ymfpci {
int mode_dup4ch;
int rear_opened;
int spdif_opened;
- struct {
+ struct snd_ymfpci_pcm_mixer {
u16 left;
u16 right;
struct snd_kcontrol *ctl;
@@ -357,6 +359,8 @@ struct snd_ymfpci {
wait_queue_head_t interrupt_sleep;
atomic_t interrupt_sleep_count;
struct snd_info_entry *proc_entry;
+ const struct firmware *dsp_microcode;
+ const struct firmware *controller_microcode;
#ifdef CONFIG_PM
u32 *saved_regs;
diff --git a/sound/Kconfig b/sound/Kconfig
index 9d77300746c6..97532bbc2ccb 100644
--- a/sound/Kconfig
+++ b/sound/Kconfig
@@ -76,6 +76,8 @@ source "sound/sparc/Kconfig"
source "sound/parisc/Kconfig"
+source "sound/soc/Kconfig"
+
endmenu
menu "Open Sound System"
diff --git a/sound/Makefile b/sound/Makefile
index 9aee54c4882d..b7c7fb7c24c8 100644
--- a/sound/Makefile
+++ b/sound/Makefile
@@ -5,7 +5,7 @@ obj-$(CONFIG_SOUND) += soundcore.o
obj-$(CONFIG_SOUND_PRIME) += sound_firmware.o
obj-$(CONFIG_SOUND_PRIME) += oss/
obj-$(CONFIG_DMASOUND) += oss/
-obj-$(CONFIG_SND) += core/ i2c/ drivers/ isa/ pci/ ppc/ arm/ synth/ usb/ sparc/ parisc/ pcmcia/ mips/
+obj-$(CONFIG_SND) += core/ i2c/ drivers/ isa/ pci/ ppc/ arm/ synth/ usb/ sparc/ parisc/ pcmcia/ mips/ soc/
obj-$(CONFIG_SND_AOA) += aoa/
# This one must be compilable even if sound is configured out
diff --git a/sound/ac97_bus.c b/sound/ac97_bus.c
index 66de2c2f1554..7fa37e15f196 100644
--- a/sound/ac97_bus.c
+++ b/sound/ac97_bus.c
@@ -26,6 +26,7 @@ static int ac97_bus_match(struct device *dev, struct device_driver *drv)
return 1;
}
+#ifdef CONFIG_PM
static int ac97_bus_suspend(struct device *dev, pm_message_t state)
{
int ret = 0;
@@ -45,12 +46,15 @@ static int ac97_bus_resume(struct device *dev)
return ret;
}
+#endif /* CONFIG_PM */
struct bus_type ac97_bus_type = {
.name = "ac97",
.match = ac97_bus_match,
+#ifdef CONFIG_PM
.suspend = ac97_bus_suspend,
.resume = ac97_bus_resume,
+#endif /* CONFIG_PM */
};
static int __init ac97_bus_init(void)
diff --git a/sound/aoa/aoa.h b/sound/aoa/aoa.h
index 378ef1e9879b..541b908f3cdf 100644
--- a/sound/aoa/aoa.h
+++ b/sound/aoa/aoa.h
@@ -99,7 +99,7 @@ struct aoa_fabric {
* that are not assigned yet are passed to the fabric
* again for reconsideration. */
extern int
-aoa_fabric_register(struct aoa_fabric *fabric);
+aoa_fabric_register(struct aoa_fabric *fabric, struct device *dev);
/* it is vital to call this when the fabric exits!
* When calling, the remove_codec will be called
diff --git a/sound/aoa/codecs/snd-aoa-codec-onyx.c b/sound/aoa/codecs/snd-aoa-codec-onyx.c
index 0b7650788f1f..b00fc4842c93 100644
--- a/sound/aoa/codecs/snd-aoa-codec-onyx.c
+++ b/sound/aoa/codecs/snd-aoa-codec-onyx.c
@@ -825,7 +825,16 @@ static int onyx_resume(struct codec_info_item *cii)
int err = -ENXIO;
mutex_lock(&onyx->mutex);
- /* take codec out of suspend */
+
+ /* reset codec */
+ onyx->codec.gpio->methods->set_hw_reset(onyx->codec.gpio, 0);
+ msleep(1);
+ onyx->codec.gpio->methods->set_hw_reset(onyx->codec.gpio, 1);
+ msleep(1);
+ onyx->codec.gpio->methods->set_hw_reset(onyx->codec.gpio, 0);
+ msleep(1);
+
+ /* take codec out of suspend (if it still is after reset) */
if (onyx_read_register(onyx, ONYX_REG_CONTROL, &v))
goto out_unlock;
onyx_write_register(onyx, ONYX_REG_CONTROL, v & ~(ONYX_ADPSV | ONYX_DAPSV));
diff --git a/sound/aoa/core/snd-aoa-alsa.c b/sound/aoa/core/snd-aoa-alsa.c
index b42fdea77ed0..17fe689ed287 100644
--- a/sound/aoa/core/snd-aoa-alsa.c
+++ b/sound/aoa/core/snd-aoa-alsa.c
@@ -14,7 +14,7 @@ MODULE_PARM_DESC(index, "index for AOA sound card.");
static struct aoa_card *aoa_card;
-int aoa_alsa_init(char *name, struct module *mod)
+int aoa_alsa_init(char *name, struct module *mod, struct device *dev)
{
struct snd_card *alsa_card;
int err;
@@ -28,6 +28,7 @@ int aoa_alsa_init(char *name, struct module *mod)
return -ENOMEM;
aoa_card = alsa_card->private_data;
aoa_card->alsa_card = alsa_card;
+ alsa_card->dev = dev;
strlcpy(alsa_card->driver, "AppleOnbdAudio", sizeof(alsa_card->driver));
strlcpy(alsa_card->shortname, name, sizeof(alsa_card->shortname));
strlcpy(alsa_card->longname, name, sizeof(alsa_card->longname));
@@ -59,7 +60,7 @@ void aoa_alsa_cleanup(void)
}
int aoa_snd_device_new(snd_device_type_t type,
- void * device_data, struct snd_device_ops * ops)
+ void * device_data, struct snd_device_ops * ops)
{
struct snd_card *card = aoa_get_card();
int err;
diff --git a/sound/aoa/core/snd-aoa-alsa.h b/sound/aoa/core/snd-aoa-alsa.h
index 660d2f1793bb..9669e4489cab 100644
--- a/sound/aoa/core/snd-aoa-alsa.h
+++ b/sound/aoa/core/snd-aoa-alsa.h
@@ -10,7 +10,7 @@
#define __SND_AOA_ALSA_H
#include "../aoa.h"
-extern int aoa_alsa_init(char *name, struct module *mod);
+extern int aoa_alsa_init(char *name, struct module *mod, struct device *dev);
extern void aoa_alsa_cleanup(void);
#endif /* __SND_AOA_ALSA_H */
diff --git a/sound/aoa/core/snd-aoa-core.c b/sound/aoa/core/snd-aoa-core.c
index ecd2d8263f2d..19fdae400687 100644
--- a/sound/aoa/core/snd-aoa-core.c
+++ b/sound/aoa/core/snd-aoa-core.c
@@ -82,7 +82,7 @@ void aoa_codec_unregister(struct aoa_codec *codec)
}
EXPORT_SYMBOL_GPL(aoa_codec_unregister);
-int aoa_fabric_register(struct aoa_fabric *new_fabric)
+int aoa_fabric_register(struct aoa_fabric *new_fabric, struct device *dev)
{
struct aoa_codec *c;
int err;
@@ -98,7 +98,7 @@ int aoa_fabric_register(struct aoa_fabric *new_fabric)
if (!new_fabric)
return -EINVAL;
- err = aoa_alsa_init(new_fabric->name, new_fabric->owner);
+ err = aoa_alsa_init(new_fabric->name, new_fabric->owner, dev);
if (err)
return err;
diff --git a/sound/aoa/fabrics/snd-aoa-fabric-layout.c b/sound/aoa/fabrics/snd-aoa-fabric-layout.c
index 172eb95476c0..1b94ba6dd279 100644
--- a/sound/aoa/fabrics/snd-aoa-fabric-layout.c
+++ b/sound/aoa/fabrics/snd-aoa-fabric-layout.c
@@ -1014,7 +1014,7 @@ static int aoa_fabric_layout_probe(struct soundbus_dev *sdev)
ldev->gpio.methods->init(&ldev->gpio);
- err = aoa_fabric_register(&layout_fabric);
+ err = aoa_fabric_register(&layout_fabric, &sdev->ofdev.dev);
if (err && err != -EALREADY) {
printk(KERN_INFO "snd-aoa-fabric-layout: can't use,"
" another fabric is active!\n");
@@ -1034,9 +1034,9 @@ static int aoa_fabric_layout_probe(struct soundbus_dev *sdev)
list_del(&ldev->list);
layouts_list_items--;
outnodev:
- if (sound) of_node_put(sound);
+ of_node_put(sound);
layout_device = NULL;
- if (ldev) kfree(ldev);
+ kfree(ldev);
return -ENODEV;
}
@@ -1077,8 +1077,6 @@ static int aoa_fabric_layout_suspend(struct soundbus_dev *sdev, pm_message_t sta
{
struct layout_dev *ldev = sdev->ofdev.dev.driver_data;
- printk("aoa_fabric_layout_suspend()\n");
-
if (ldev->gpio.methods && ldev->gpio.methods->all_amps_off)
ldev->gpio.methods->all_amps_off(&ldev->gpio);
@@ -1089,8 +1087,6 @@ static int aoa_fabric_layout_resume(struct soundbus_dev *sdev)
{
struct layout_dev *ldev = sdev->ofdev.dev.driver_data;
- printk("aoa_fabric_layout_resume()\n");
-
if (ldev->gpio.methods && ldev->gpio.methods->all_amps_off)
ldev->gpio.methods->all_amps_restore(&ldev->gpio);
@@ -1107,6 +1103,9 @@ static struct soundbus_driver aoa_soundbus_driver = {
.suspend = aoa_fabric_layout_suspend,
.resume = aoa_fabric_layout_resume,
#endif
+ .driver = {
+ .owner = THIS_MODULE,
+ }
};
static int __init aoa_fabric_layout_init(void)
diff --git a/sound/aoa/soundbus/i2sbus/i2sbus-core.c b/sound/aoa/soundbus/i2sbus/i2sbus-core.c
index e593a1333fe3..e36f6aa448d4 100644
--- a/sound/aoa/soundbus/i2sbus/i2sbus-core.c
+++ b/sound/aoa/soundbus/i2sbus/i2sbus-core.c
@@ -41,8 +41,8 @@ static int alloc_dbdma_descriptor_ring(struct i2sbus_dev *i2sdev,
struct dbdma_command_mem *r,
int numcmds)
{
- /* one more for rounding */
- r->size = (numcmds+1) * sizeof(struct dbdma_cmd);
+ /* one more for rounding, one for branch back, one for stop command */
+ r->size = (numcmds + 3) * sizeof(struct dbdma_cmd);
/* We use the PCI APIs for now until the generic one gets fixed
* enough or until we get some macio-specific versions
*/
@@ -377,11 +377,8 @@ static int i2sbus_suspend(struct macio_dev* dev, pm_message_t state)
if (i2sdev->sound.pcm) {
/* Suspend PCM streams */
snd_pcm_suspend_all(i2sdev->sound.pcm);
- /* Probably useless as we handle
- * power transitions ourselves */
- snd_power_change_state(i2sdev->sound.pcm->card,
- SNDRV_CTL_POWER_D3hot);
}
+
/* Notify codecs */
list_for_each_entry(cii, &i2sdev->sound.codec_list, list) {
err = 0;
@@ -390,7 +387,11 @@ static int i2sbus_suspend(struct macio_dev* dev, pm_message_t state)
if (err)
ret = err;
}
+
+ /* wait until streams are stopped */
+ i2sbus_wait_for_stop_both(i2sdev);
}
+
return ret;
}
@@ -402,6 +403,9 @@ static int i2sbus_resume(struct macio_dev* dev)
int err, ret = 0;
list_for_each_entry(i2sdev, &control->list, item) {
+ /* reset i2s bus format etc. */
+ i2sbus_pcm_prepare_both(i2sdev);
+
/* Notify codecs so they can re-initialize */
list_for_each_entry(cii, &i2sdev->sound.codec_list, list) {
err = 0;
@@ -410,12 +414,6 @@ static int i2sbus_resume(struct macio_dev* dev)
if (err)
ret = err;
}
- /* Notify Alsa */
- if (i2sdev->sound.pcm) {
- /* Same comment as above, probably useless */
- snd_power_change_state(i2sdev->sound.pcm->card,
- SNDRV_CTL_POWER_D0);
- }
}
return ret;
diff --git a/sound/aoa/soundbus/i2sbus/i2sbus-pcm.c b/sound/aoa/soundbus/i2sbus/i2sbus-pcm.c
index 5eff30b10201..c6b42f9bdbc9 100644
--- a/sound/aoa/soundbus/i2sbus/i2sbus-pcm.c
+++ b/sound/aoa/soundbus/i2sbus/i2sbus-pcm.c
@@ -125,7 +125,8 @@ static int i2sbus_pcm_open(struct i2sbus_dev *i2sdev, int in)
}
/* bus dependent stuff */
hw->info = SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_MMAP_VALID |
- SNDRV_PCM_INFO_INTERLEAVED | SNDRV_PCM_INFO_RESUME;
+ SNDRV_PCM_INFO_INTERLEAVED | SNDRV_PCM_INFO_RESUME |
+ SNDRV_PCM_INFO_JOINT_DUPLEX;
CHECK_RATE(5512);
CHECK_RATE(8000);
@@ -245,18 +246,78 @@ static int i2sbus_pcm_close(struct i2sbus_dev *i2sdev, int in)
return err;
}
+static void i2sbus_wait_for_stop(struct i2sbus_dev *i2sdev,
+ struct pcm_info *pi)
+{
+ unsigned long flags;
+ struct completion done;
+ long timeout;
+
+ spin_lock_irqsave(&i2sdev->low_lock, flags);
+ if (pi->dbdma_ring.stopping) {
+ init_completion(&done);
+ pi->stop_completion = &done;
+ spin_unlock_irqrestore(&i2sdev->low_lock, flags);
+ timeout = wait_for_completion_timeout(&done, HZ);
+ spin_lock_irqsave(&i2sdev->low_lock, flags);
+ pi->stop_completion = NULL;
+ if (timeout == 0) {
+ /* timeout expired, stop dbdma forcefully */
+ printk(KERN_ERR "i2sbus_wait_for_stop: timed out\n");
+ /* make sure RUN, PAUSE and S0 bits are cleared */
+ out_le32(&pi->dbdma->control, (RUN | PAUSE | 1) << 16);
+ pi->dbdma_ring.stopping = 0;
+ timeout = 10;
+ while (in_le32(&pi->dbdma->status) & ACTIVE) {
+ if (--timeout <= 0)
+ break;
+ udelay(1);
+ }
+ }
+ }
+ spin_unlock_irqrestore(&i2sdev->low_lock, flags);
+}
+
+#ifdef CONFIG_PM
+void i2sbus_wait_for_stop_both(struct i2sbus_dev *i2sdev)
+{
+ struct pcm_info *pi;
+
+ get_pcm_info(i2sdev, 0, &pi, NULL);
+ i2sbus_wait_for_stop(i2sdev, pi);
+ get_pcm_info(i2sdev, 1, &pi, NULL);
+ i2sbus_wait_for_stop(i2sdev, pi);
+}
+#endif
+
static int i2sbus_hw_params(struct snd_pcm_substream *substream,
struct snd_pcm_hw_params *params)
{
return snd_pcm_lib_malloc_pages(substream, params_buffer_bytes(params));
}
-static int i2sbus_hw_free(struct snd_pcm_substream *substream)
+static inline int i2sbus_hw_free(struct snd_pcm_substream *substream, int in)
{
+ struct i2sbus_dev *i2sdev = snd_pcm_substream_chip(substream);
+ struct pcm_info *pi;
+
+ get_pcm_info(i2sdev, in, &pi, NULL);
+ if (pi->dbdma_ring.stopping)
+ i2sbus_wait_for_stop(i2sdev, pi);
snd_pcm_lib_free_pages(substream);
return 0;
}
+static int i2sbus_playback_hw_free(struct snd_pcm_substream *substream)
+{
+ return i2sbus_hw_free(substream, 0);
+}
+
+static int i2sbus_record_hw_free(struct snd_pcm_substream *substream)
+{
+ return i2sbus_hw_free(substream, 1);
+}
+
static int i2sbus_pcm_prepare(struct i2sbus_dev *i2sdev, int in)
{
/* whee. Hard work now. The user has selected a bitrate
@@ -264,7 +325,7 @@ static int i2sbus_pcm_prepare(struct i2sbus_dev *i2sdev, int in)
* I2S controller appropriately. */
struct snd_pcm_runtime *runtime;
struct dbdma_cmd *command;
- int i, periodsize;
+ int i, periodsize, nperiods;
dma_addr_t offset;
struct bus_info bi;
struct codec_info_item *cii;
@@ -274,6 +335,7 @@ static int i2sbus_pcm_prepare(struct i2sbus_dev *i2sdev, int in)
struct pcm_info *pi, *other;
int cnt;
int result = 0;
+ unsigned int cmd, stopaddr;
mutex_lock(&i2sdev->lock);
@@ -283,6 +345,13 @@ static int i2sbus_pcm_prepare(struct i2sbus_dev *i2sdev, int in)
result = -EBUSY;
goto out_unlock;
}
+ if (pi->dbdma_ring.stopping)
+ i2sbus_wait_for_stop(i2sdev, pi);
+
+ if (!pi->substream || !pi->substream->runtime) {
+ result = -EINVAL;
+ goto out_unlock;
+ }
runtime = pi->substream->runtime;
pi->active = 1;
@@ -297,24 +366,43 @@ static int i2sbus_pcm_prepare(struct i2sbus_dev *i2sdev, int in)
i2sdev->rate = runtime->rate;
periodsize = snd_pcm_lib_period_bytes(pi->substream);
+ nperiods = pi->substream->runtime->periods;
pi->current_period = 0;
/* generate dbdma command ring first */
command = pi->dbdma_ring.cmds;
+ memset(command, 0, (nperiods + 2) * sizeof(struct dbdma_cmd));
+
+ /* commands to DMA to/from the ring */
+ /*
+ * For input, we need to do a graceful stop; if we abort
+ * the DMA, we end up with leftover bytes that corrupt
+ * the next recording. To do this we set the S0 status
+ * bit and wait for the DMA controller to stop. Each
+ * command has a branch condition to
+ * make it branch to a stop command if S0 is set.
+ * On input we also need to wait for the S7 bit to be
+ * set before turning off the DMA controller.
+ * In fact we do the graceful stop for output as well.
+ */
offset = runtime->dma_addr;
- for (i = 0; i < pi->substream->runtime->periods;
- i++, command++, offset += periodsize) {
- memset(command, 0, sizeof(struct dbdma_cmd));
- command->command =
- cpu_to_le16((in ? INPUT_MORE : OUTPUT_MORE) | INTR_ALWAYS);
+ cmd = (in? INPUT_MORE: OUTPUT_MORE) | BR_IFSET | INTR_ALWAYS;
+ stopaddr = pi->dbdma_ring.bus_cmd_start +
+ (nperiods + 1) * sizeof(struct dbdma_cmd);
+ for (i = 0; i < nperiods; i++, command++, offset += periodsize) {
+ command->command = cpu_to_le16(cmd);
+ command->cmd_dep = cpu_to_le32(stopaddr);
command->phy_addr = cpu_to_le32(offset);
command->req_count = cpu_to_le16(periodsize);
- command->xfer_status = cpu_to_le16(0);
}
- /* last one branches back to first */
- command--;
- command->command |= cpu_to_le16(BR_ALWAYS);
+
+ /* branch back to beginning of ring */
+ command->command = cpu_to_le16(DBDMA_NOP | BR_ALWAYS);
command->cmd_dep = cpu_to_le32(pi->dbdma_ring.bus_cmd_start);
+ command++;
+
+ /* set stop command */
+ command->command = cpu_to_le16(DBDMA_STOP);
/* ok, let's set the serial format and stuff */
switch (runtime->format) {
@@ -435,16 +523,18 @@ static int i2sbus_pcm_prepare(struct i2sbus_dev *i2sdev, int in)
return result;
}
-static struct dbdma_cmd STOP_CMD = {
- .command = __constant_cpu_to_le16(DBDMA_STOP),
-};
+#ifdef CONFIG_PM
+void i2sbus_pcm_prepare_both(struct i2sbus_dev *i2sdev)
+{
+ i2sbus_pcm_prepare(i2sdev, 0);
+ i2sbus_pcm_prepare(i2sdev, 1);
+}
+#endif
static int i2sbus_pcm_trigger(struct i2sbus_dev *i2sdev, int in, int cmd)
{
struct codec_info_item *cii;
struct pcm_info *pi;
- int timeout;
- struct dbdma_cmd tmp;
int result = 0;
unsigned long flags;
@@ -464,92 +554,50 @@ static int i2sbus_pcm_trigger(struct i2sbus_dev *i2sdev, int in, int cmd)
cii->codec->start(cii, pi->substream);
pi->dbdma_ring.running = 1;
- /* reset dma engine */
- out_le32(&pi->dbdma->control,
- 0 | (RUN | PAUSE | FLUSH | WAKE) << 16);
- timeout = 100;
- while (in_le32(&pi->dbdma->status) & RUN && timeout--)
- udelay(1);
- if (timeout <= 0) {
- printk(KERN_ERR
- "i2sbus: error waiting for dma reset\n");
- result = -ENXIO;
- goto out_unlock;
+ if (pi->dbdma_ring.stopping) {
+ /* Clear the S0 bit, then see if we stopped yet */
+ out_le32(&pi->dbdma->control, 1 << 16);
+ if (in_le32(&pi->dbdma->status) & ACTIVE) {
+ /* possible race here? */
+ udelay(10);
+ if (in_le32(&pi->dbdma->status) & ACTIVE) {
+ pi->dbdma_ring.stopping = 0;
+ goto out_unlock; /* keep running */
+ }
+ }
}
+ /* make sure RUN, PAUSE and S0 bits are cleared */
+ out_le32(&pi->dbdma->control, (RUN | PAUSE | 1) << 16);
+
+ /* set branch condition select register */
+ out_le32(&pi->dbdma->br_sel, (1 << 16) | 1);
+
/* write dma command buffer address to the dbdma chip */
out_le32(&pi->dbdma->cmdptr, pi->dbdma_ring.bus_cmd_start);
- /* post PCI write */
- mb();
- (void)in_le32(&pi->dbdma->status);
-
- /* change first command to STOP */
- tmp = *pi->dbdma_ring.cmds;
- *pi->dbdma_ring.cmds = STOP_CMD;
-
- /* set running state, remember that the first command is STOP */
- out_le32(&pi->dbdma->control, RUN | (RUN << 16));
- timeout = 100;
- /* wait for STOP to be executed */
- while (in_le32(&pi->dbdma->status) & ACTIVE && timeout--)
- udelay(1);
- if (timeout <= 0) {
- printk(KERN_ERR "i2sbus: error waiting for dma stop\n");
- result = -ENXIO;
- goto out_unlock;
- }
- /* again, write dma command buffer address to the dbdma chip,
- * this time of the first real command */
- *pi->dbdma_ring.cmds = tmp;
- out_le32(&pi->dbdma->cmdptr, pi->dbdma_ring.bus_cmd_start);
- /* post write */
- mb();
- (void)in_le32(&pi->dbdma->status);
-
- /* reset dma engine again */
- out_le32(&pi->dbdma->control,
- 0 | (RUN | PAUSE | FLUSH | WAKE) << 16);
- timeout = 100;
- while (in_le32(&pi->dbdma->status) & RUN && timeout--)
- udelay(1);
- if (timeout <= 0) {
- printk(KERN_ERR
- "i2sbus: error waiting for dma reset\n");
- result = -ENXIO;
- goto out_unlock;
- }
- /* wake up the chip with the next descriptor */
- out_le32(&pi->dbdma->control,
- (RUN | WAKE) | ((RUN | WAKE) << 16));
- /* get the frame count */
+ /* initialize the frame count and current period */
+ pi->current_period = 0;
pi->frame_count = in_le32(&i2sdev->intfregs->frame_count);
+ /* set the DMA controller running */
+ out_le32(&pi->dbdma->control, (RUN << 16) | RUN);
+
/* off you go! */
break;
+
case SNDRV_PCM_TRIGGER_STOP:
case SNDRV_PCM_TRIGGER_SUSPEND:
if (!pi->dbdma_ring.running) {
result = -EALREADY;
goto out_unlock;
}
+ pi->dbdma_ring.running = 0;
- /* turn off all relevant bits */
- out_le32(&pi->dbdma->control,
- (RUN | WAKE | FLUSH | PAUSE) << 16);
- {
- /* FIXME: move to own function */
- int timeout = 5000;
- while ((in_le32(&pi->dbdma->status) & RUN)
- && --timeout > 0)
- udelay(1);
- if (!timeout)
- printk(KERN_ERR
- "i2sbus: timed out turning "
- "off dbdma engine!\n");
- }
+ /* Set the S0 bit to make the DMA branch to the stop cmd */
+ out_le32(&pi->dbdma->control, (1 << 16) | 1);
+ pi->dbdma_ring.stopping = 1;
- pi->dbdma_ring.running = 0;
list_for_each_entry(cii, &i2sdev->sound.codec_list, list)
if (cii->codec->stop)
cii->codec->stop(cii, pi->substream);
@@ -574,70 +622,82 @@ static snd_pcm_uframes_t i2sbus_pcm_pointer(struct i2sbus_dev *i2sdev, int in)
fc = in_le32(&i2sdev->intfregs->frame_count);
fc = fc - pi->frame_count;
- return (bytes_to_frames(pi->substream->runtime,
- pi->current_period *
- snd_pcm_lib_period_bytes(pi->substream))
- + fc) % pi->substream->runtime->buffer_size;
+ if (fc >= pi->substream->runtime->buffer_size)
+ fc %= pi->substream->runtime->buffer_size;
+ return fc;
}
static inline void handle_interrupt(struct i2sbus_dev *i2sdev, int in)
{
struct pcm_info *pi;
- u32 fc;
- u32 delta;
+ u32 fc, nframes;
+ u32 status;
+ int timeout, i;
+ int dma_stopped = 0;
+ struct snd_pcm_runtime *runtime;
spin_lock(&i2sdev->low_lock);
get_pcm_info(i2sdev, in, &pi, NULL);
-
- if (!pi->dbdma_ring.running) {
- /* there was still an interrupt pending
- * while we stopped. or maybe another
- * processor (not the one that was stopping
- * the DMA engine) was spinning above
- * waiting for the lock. */
+ if (!pi->dbdma_ring.running && !pi->dbdma_ring.stopping)
goto out_unlock;
- }
- fc = in_le32(&i2sdev->intfregs->frame_count);
- /* a counter overflow does not change the calculation. */
- delta = fc - pi->frame_count;
-
- /* update current_period */
- while (delta >= pi->substream->runtime->period_size) {
- pi->current_period++;
- delta = delta - pi->substream->runtime->period_size;
- }
-
- if (unlikely(delta)) {
- /* Some interrupt came late, so check the dbdma.
- * This special case exists to syncronize the frame_count with
- * the dbdma transfer, but is hit every once in a while. */
- int period;
-
- period = (in_le32(&pi->dbdma->cmdptr)
- - pi->dbdma_ring.bus_cmd_start)
- / sizeof(struct dbdma_cmd);
- pi->current_period = pi->current_period
- % pi->substream->runtime->periods;
-
- while (pi->current_period != period) {
- pi->current_period++;
- pi->current_period %= pi->substream->runtime->periods;
- /* Set delta to zero, as the frame_count value is too
- * high (otherwise the code path will not be executed).
- * This corrects the fact that the frame_count is too
- * low at the beginning due to buffering. */
- delta = 0;
+ i = pi->current_period;
+ runtime = pi->substream->runtime;
+ while (pi->dbdma_ring.cmds[i].xfer_status) {
+ if (le16_to_cpu(pi->dbdma_ring.cmds[i].xfer_status) & BT)
+ /*
+ * BT is the branch taken bit. If it took a branch
+ * it is because we set the S0 bit to make it
+ * branch to the stop command.
+ */
+ dma_stopped = 1;
+ pi->dbdma_ring.cmds[i].xfer_status = 0;
+
+ if (++i >= runtime->periods) {
+ i = 0;
+ pi->frame_count += runtime->buffer_size;
}
+ pi->current_period = i;
+
+ /*
+ * Check the frame count. The DMA tends to get a bit
+ * ahead of the frame counter, which confuses the core.
+ */
+ fc = in_le32(&i2sdev->intfregs->frame_count);
+ nframes = i * runtime->period_size;
+ if (fc < pi->frame_count + nframes)
+ pi->frame_count = fc - nframes;
}
- pi->frame_count = fc - delta;
- pi->current_period %= pi->substream->runtime->periods;
+ if (dma_stopped) {
+ timeout = 1000;
+ for (;;) {
+ status = in_le32(&pi->dbdma->status);
+ if (!(status & ACTIVE) && (!in || (status & 0x80)))
+ break;
+ if (--timeout <= 0) {
+ printk(KERN_ERR "i2sbus: timed out "
+ "waiting for DMA to stop!\n");
+ break;
+ }
+ udelay(1);
+ }
+
+ /* Turn off DMA controller, clear S0 bit */
+ out_le32(&pi->dbdma->control, (RUN | PAUSE | 1) << 16);
+
+ pi->dbdma_ring.stopping = 0;
+ if (pi->stop_completion)
+ complete(pi->stop_completion);
+ }
+ if (!pi->dbdma_ring.running)
+ goto out_unlock;
spin_unlock(&i2sdev->low_lock);
/* may call _trigger again, hence needs to be unlocked */
snd_pcm_period_elapsed(pi->substream);
return;
+
out_unlock:
spin_unlock(&i2sdev->low_lock);
}
@@ -718,7 +778,7 @@ static struct snd_pcm_ops i2sbus_playback_ops = {
.close = i2sbus_playback_close,
.ioctl = snd_pcm_lib_ioctl,
.hw_params = i2sbus_hw_params,
- .hw_free = i2sbus_hw_free,
+ .hw_free = i2sbus_playback_hw_free,
.prepare = i2sbus_playback_prepare,
.trigger = i2sbus_playback_trigger,
.pointer = i2sbus_playback_pointer,
@@ -788,7 +848,7 @@ static struct snd_pcm_ops i2sbus_record_ops = {
.close = i2sbus_record_close,
.ioctl = snd_pcm_lib_ioctl,
.hw_params = i2sbus_hw_params,
- .hw_free = i2sbus_hw_free,
+ .hw_free = i2sbus_record_hw_free,
.prepare = i2sbus_record_prepare,
.trigger = i2sbus_record_trigger,
.pointer = i2sbus_record_pointer,
@@ -812,7 +872,6 @@ static void i2sbus_private_free(struct snd_pcm *pcm)
module_put(THIS_MODULE);
}
-/* FIXME: this function needs an error handling strategy with labels */
int
i2sbus_attach_codec(struct soundbus_dev *dev, struct snd_card *card,
struct codec_info *ci, void *data)
@@ -880,41 +939,31 @@ i2sbus_attach_codec(struct soundbus_dev *dev, struct snd_card *card,
if (!cii->sdev) {
printk(KERN_DEBUG
"i2sbus: failed to get soundbus dev reference\n");
- kfree(cii);
- return -ENODEV;
+ err = -ENODEV;
+ goto out_free_cii;
}
if (!try_module_get(THIS_MODULE)) {
printk(KERN_DEBUG "i2sbus: failed to get module reference!\n");
- soundbus_dev_put(dev);
- kfree(cii);
- return -EBUSY;
+ err = -EBUSY;
+ goto out_put_sdev;
}
if (!try_module_get(ci->owner)) {
printk(KERN_DEBUG
"i2sbus: failed to get module reference to codec owner!\n");
- module_put(THIS_MODULE);
- soundbus_dev_put(dev);
- kfree(cii);
- return -EBUSY;
+ err = -EBUSY;
+ goto out_put_this_module;
}
if (!dev->pcm) {
- err = snd_pcm_new(card,
- dev->pcmname,
- dev->pcmid,
- 0,
- 0,
+ err = snd_pcm_new(card, dev->pcmname, dev->pcmid, 0, 0,
&dev->pcm);
if (err) {
printk(KERN_DEBUG "i2sbus: failed to create pcm\n");
- kfree(cii);
- module_put(ci->owner);
- soundbus_dev_put(dev);
- module_put(THIS_MODULE);
- return err;
+ goto out_put_ci_module;
}
+ dev->pcm->dev = &dev->ofdev.dev;
}
/* ALSA yet again sucks.
@@ -926,20 +975,12 @@ i2sbus_attach_codec(struct soundbus_dev *dev, struct snd_card *card,
/* eh? */
printk(KERN_ERR
"Can't attach same bus to different cards!\n");
- module_put(ci->owner);
- kfree(cii);
- soundbus_dev_put(dev);
- module_put(THIS_MODULE);
- return -EINVAL;
- }
- if ((err =
- snd_pcm_new_stream(dev->pcm, SNDRV_PCM_STREAM_PLAYBACK, 1))) {
- module_put(ci->owner);
- kfree(cii);
- soundbus_dev_put(dev);
- module_put(THIS_MODULE);
- return err;
+ err = -EINVAL;
+ goto out_put_ci_module;
}
+ err = snd_pcm_new_stream(dev->pcm, SNDRV_PCM_STREAM_PLAYBACK, 1);
+ if (err)
+ goto out_put_ci_module;
snd_pcm_set_ops(dev->pcm, SNDRV_PCM_STREAM_PLAYBACK,
&i2sbus_playback_ops);
i2sdev->out.created = 1;
@@ -949,20 +990,11 @@ i2sbus_attach_codec(struct soundbus_dev *dev, struct snd_card *card,
if (dev->pcm->card != card) {
printk(KERN_ERR
"Can't attach same bus to different cards!\n");
- module_put(ci->owner);
- kfree(cii);
- soundbus_dev_put(dev);
- module_put(THIS_MODULE);
- return -EINVAL;
- }
- if ((err =
- snd_pcm_new_stream(dev->pcm, SNDRV_PCM_STREAM_CAPTURE, 1))) {
- module_put(ci->owner);
- kfree(cii);
- soundbus_dev_put(dev);
- module_put(THIS_MODULE);
- return err;
+ goto out_put_ci_module;
}
+ err = snd_pcm_new_stream(dev->pcm, SNDRV_PCM_STREAM_CAPTURE, 1);
+ if (err)
+ goto out_put_ci_module;
snd_pcm_set_ops(dev->pcm, SNDRV_PCM_STREAM_CAPTURE,
&i2sbus_record_ops);
i2sdev->in.created = 1;
@@ -977,11 +1009,7 @@ i2sbus_attach_codec(struct soundbus_dev *dev, struct snd_card *card,
err = snd_device_register(card, dev->pcm);
if (err) {
printk(KERN_ERR "i2sbus: error registering new pcm\n");
- module_put(ci->owner);
- kfree(cii);
- soundbus_dev_put(dev);
- module_put(THIS_MODULE);
- return err;
+ goto out_put_ci_module;
}
/* no errors any more, so let's add this to our list */
list_add(&cii->list, &dev->codec_list);
@@ -996,6 +1024,15 @@ i2sbus_attach_codec(struct soundbus_dev *dev, struct snd_card *card,
64 * 1024, 64 * 1024);
return 0;
+ out_put_ci_module:
+ module_put(ci->owner);
+ out_put_this_module:
+ module_put(THIS_MODULE);
+ out_put_sdev:
+ soundbus_dev_put(dev);
+ out_free_cii:
+ kfree(cii);
+ return err;
}
void i2sbus_detach_codec(struct soundbus_dev *dev, void *data)
diff --git a/sound/aoa/soundbus/i2sbus/i2sbus.h b/sound/aoa/soundbus/i2sbus/i2sbus.h
index ec20ee615d7f..ff29654782c9 100644
--- a/sound/aoa/soundbus/i2sbus/i2sbus.h
+++ b/sound/aoa/soundbus/i2sbus/i2sbus.h
@@ -10,6 +10,7 @@
#include <linux/interrupt.h>
#include <linux/spinlock.h>
#include <linux/mutex.h>
+#include <linux/completion.h>
#include <sound/pcm.h>
@@ -34,6 +35,7 @@ struct dbdma_command_mem {
void *space;
int size;
u32 running:1;
+ u32 stopping:1;
};
struct pcm_info {
@@ -45,6 +47,7 @@ struct pcm_info {
u32 frame_count;
struct dbdma_command_mem dbdma_ring;
volatile struct dbdma_regs __iomem *dbdma;
+ struct completion *stop_completion;
};
enum {
@@ -101,6 +104,9 @@ i2sbus_tx_intr(int irq, void *devid);
extern irqreturn_t
i2sbus_rx_intr(int irq, void *devid);
+extern void i2sbus_wait_for_stop_both(struct i2sbus_dev *i2sdev);
+extern void i2sbus_pcm_prepare_both(struct i2sbus_dev *i2sdev);
+
/* control specific functions */
extern int i2sbus_control_init(struct macio_dev* dev,
struct i2sbus_control **c);
diff --git a/sound/arm/aaci.h b/sound/arm/aaci.h
index 06295190606c..9175ff9ded01 100644
--- a/sound/arm/aaci.h
+++ b/sound/arm/aaci.h
@@ -228,7 +228,7 @@ struct aaci {
/* AC'97 */
struct mutex ac97_sem;
- ac97_bus_t *ac97_bus;
+ struct snd_ac97_bus *ac97_bus;
u32 maincr;
spinlock_t lock;
diff --git a/sound/core/control.c b/sound/core/control.c
index 0c7bcd62e5b2..42bcf2794b28 100644
--- a/sound/core/control.c
+++ b/sound/core/control.c
@@ -108,7 +108,6 @@ static void snd_ctl_empty_read_queue(struct snd_ctl_file * ctl)
static int snd_ctl_release(struct inode *inode, struct file *file)
{
unsigned long flags;
- struct list_head *list;
struct snd_card *card;
struct snd_ctl_file *ctl;
struct snd_kcontrol *control;
@@ -122,12 +121,10 @@ static int snd_ctl_release(struct inode *inode, struct file *file)
list_del(&ctl->list);
write_unlock_irqrestore(&card->ctl_files_rwlock, flags);
down_write(&card->controls_rwsem);
- list_for_each(list, &card->controls) {
- control = snd_kcontrol(list);
+ list_for_each_entry(control, &card->controls, list)
for (idx = 0; idx < control->count; idx++)
if (control->vd[idx].owner == ctl)
control->vd[idx].owner = NULL;
- }
up_write(&card->controls_rwsem);
snd_ctl_empty_read_queue(ctl);
kfree(ctl);
@@ -140,7 +137,6 @@ void snd_ctl_notify(struct snd_card *card, unsigned int mask,
struct snd_ctl_elem_id *id)
{
unsigned long flags;
- struct list_head *flist;
struct snd_ctl_file *ctl;
struct snd_kctl_event *ev;
@@ -149,14 +145,11 @@ void snd_ctl_notify(struct snd_card *card, unsigned int mask,
#if defined(CONFIG_SND_MIXER_OSS) || defined(CONFIG_SND_MIXER_OSS_MODULE)
card->mixer_oss_change_count++;
#endif
- list_for_each(flist, &card->ctl_files) {
- struct list_head *elist;
- ctl = snd_ctl_file(flist);
+ list_for_each_entry(ctl, &card->ctl_files, list) {
if (!ctl->subscribed)
continue;
spin_lock_irqsave(&ctl->read_lock, flags);
- list_for_each(elist, &ctl->events) {
- ev = snd_kctl_event(elist);
+ list_for_each_entry(ev, &ctl->events, list) {
if (ev->id.numid == id->numid) {
ev->mask |= mask;
goto _found;
@@ -190,7 +183,8 @@ EXPORT_SYMBOL(snd_ctl_notify);
*
* Returns the pointer of the new instance, or NULL on failure.
*/
-struct snd_kcontrol *snd_ctl_new(struct snd_kcontrol *control, unsigned int access)
+static struct snd_kcontrol *snd_ctl_new(struct snd_kcontrol *control,
+ unsigned int access)
{
struct snd_kcontrol *kctl;
unsigned int idx;
@@ -208,8 +202,6 @@ struct snd_kcontrol *snd_ctl_new(struct snd_kcontrol *control, unsigned int acce
return kctl;
}
-EXPORT_SYMBOL(snd_ctl_new);
-
/**
* snd_ctl_new1 - create a control instance from the template
* @ncontrol: the initialization record
@@ -277,11 +269,9 @@ EXPORT_SYMBOL(snd_ctl_free_one);
static unsigned int snd_ctl_hole_check(struct snd_card *card,
unsigned int count)
{
- struct list_head *list;
struct snd_kcontrol *kctl;
- list_for_each(list, &card->controls) {
- kctl = snd_kcontrol(list);
+ list_for_each_entry(kctl, &card->controls, list) {
if ((kctl->id.numid <= card->last_numid &&
kctl->id.numid + kctl->count > card->last_numid) ||
(kctl->id.numid <= card->last_numid + count - 1 &&
@@ -498,12 +488,10 @@ EXPORT_SYMBOL(snd_ctl_rename_id);
*/
struct snd_kcontrol *snd_ctl_find_numid(struct snd_card *card, unsigned int numid)
{
- struct list_head *list;
struct snd_kcontrol *kctl;
snd_assert(card != NULL && numid != 0, return NULL);
- list_for_each(list, &card->controls) {
- kctl = snd_kcontrol(list);
+ list_for_each_entry(kctl, &card->controls, list) {
if (kctl->id.numid <= numid && kctl->id.numid + kctl->count > numid)
return kctl;
}
@@ -527,14 +515,12 @@ EXPORT_SYMBOL(snd_ctl_find_numid);
struct snd_kcontrol *snd_ctl_find_id(struct snd_card *card,
struct snd_ctl_elem_id *id)
{
- struct list_head *list;
struct snd_kcontrol *kctl;
snd_assert(card != NULL && id != NULL, return NULL);
if (id->numid != 0)
return snd_ctl_find_numid(card, id->numid);
- list_for_each(list, &card->controls) {
- kctl = snd_kcontrol(list);
+ list_for_each_entry(kctl, &card->controls, list) {
if (kctl->id.iface != id->iface)
continue;
if (kctl->id.device != id->device)
@@ -1182,7 +1168,6 @@ static long snd_ctl_ioctl(struct file *file, unsigned int cmd, unsigned long arg
{
struct snd_ctl_file *ctl;
struct snd_card *card;
- struct list_head *list;
struct snd_kctl_ioctl *p;
void __user *argp = (void __user *)arg;
int __user *ip = argp;
@@ -1232,8 +1217,7 @@ static long snd_ctl_ioctl(struct file *file, unsigned int cmd, unsigned long arg
#endif
}
down_read(&snd_ioctl_rwsem);
- list_for_each(list, &snd_control_ioctls) {
- p = list_entry(list, struct snd_kctl_ioctl, list);
+ list_for_each_entry(p, &snd_control_ioctls, list) {
err = p->fioctl(card, ctl, cmd, arg);
if (err != -ENOIOCTLCMD) {
up_read(&snd_ioctl_rwsem);
@@ -1357,13 +1341,11 @@ EXPORT_SYMBOL(snd_ctl_register_ioctl_compat);
static int _snd_ctl_unregister_ioctl(snd_kctl_ioctl_func_t fcn,
struct list_head *lists)
{
- struct list_head *list;
struct snd_kctl_ioctl *p;
snd_assert(fcn != NULL, return -EINVAL);
down_write(&snd_ioctl_rwsem);
- list_for_each(list, lists) {
- p = list_entry(list, struct snd_kctl_ioctl, list);
+ list_for_each_entry(p, lists, list) {
if (p->fioctl == fcn) {
list_del(&p->list);
up_write(&snd_ioctl_rwsem);
@@ -1453,7 +1435,6 @@ static int snd_ctl_dev_register(struct snd_device *device)
static int snd_ctl_dev_disconnect(struct snd_device *device)
{
struct snd_card *card = device->device_data;
- struct list_head *flist;
struct snd_ctl_file *ctl;
int err, cardnum;
@@ -1462,8 +1443,7 @@ static int snd_ctl_dev_disconnect(struct snd_device *device)
snd_assert(cardnum >= 0 && cardnum < SNDRV_CARDS, return -ENXIO);
down_read(&card->controls_rwsem);
- list_for_each(flist, &card->ctl_files) {
- ctl = snd_ctl_file(flist);
+ list_for_each_entry(ctl, &card->ctl_files, list) {
wake_up(&ctl->change_sleep);
kill_fasync(&ctl->fasync, SIGIO, POLL_ERR);
}
diff --git a/sound/core/control_compat.c b/sound/core/control_compat.c
index ab48962c48ce..9311ca397bbc 100644
--- a/sound/core/control_compat.c
+++ b/sound/core/control_compat.c
@@ -392,7 +392,7 @@ enum {
static inline long snd_ctl_ioctl_compat(struct file *file, unsigned int cmd, unsigned long arg)
{
struct snd_ctl_file *ctl;
- struct list_head *list;
+ struct snd_kctl_ioctl *p;
void __user *argp = compat_ptr(arg);
int err;
@@ -427,8 +427,7 @@ static inline long snd_ctl_ioctl_compat(struct file *file, unsigned int cmd, uns
}
down_read(&snd_ioctl_rwsem);
- list_for_each(list, &snd_control_compat_ioctls) {
- struct snd_kctl_ioctl *p = list_entry(list, struct snd_kctl_ioctl, list);
+ list_for_each_entry(p, &snd_control_compat_ioctls, list) {
if (p->fioctl) {
err = p->fioctl(ctl->card, ctl, cmd, arg);
if (err != -ENOIOCTLCMD) {
diff --git a/sound/core/device.c b/sound/core/device.c
index ccb25816ac9e..5858b02b0b1d 100644
--- a/sound/core/device.c
+++ b/sound/core/device.c
@@ -79,13 +79,11 @@ EXPORT_SYMBOL(snd_device_new);
*/
int snd_device_free(struct snd_card *card, void *device_data)
{
- struct list_head *list;
struct snd_device *dev;
snd_assert(card != NULL, return -ENXIO);
snd_assert(device_data != NULL, return -ENXIO);
- list_for_each(list, &card->devices) {
- dev = snd_device(list);
+ list_for_each_entry(dev, &card->devices, list) {
if (dev->device_data != device_data)
continue;
/* unlink */
@@ -124,13 +122,11 @@ EXPORT_SYMBOL(snd_device_free);
*/
int snd_device_disconnect(struct snd_card *card, void *device_data)
{
- struct list_head *list;
struct snd_device *dev;
snd_assert(card != NULL, return -ENXIO);
snd_assert(device_data != NULL, return -ENXIO);
- list_for_each(list, &card->devices) {
- dev = snd_device(list);
+ list_for_each_entry(dev, &card->devices, list) {
if (dev->device_data != device_data)
continue;
if (dev->state == SNDRV_DEV_REGISTERED &&
@@ -161,14 +157,12 @@ int snd_device_disconnect(struct snd_card *card, void *device_data)
*/
int snd_device_register(struct snd_card *card, void *device_data)
{
- struct list_head *list;
struct snd_device *dev;
int err;
snd_assert(card != NULL, return -ENXIO);
snd_assert(device_data != NULL, return -ENXIO);
- list_for_each(list, &card->devices) {
- dev = snd_device(list);
+ list_for_each_entry(dev, &card->devices, list) {
if (dev->device_data != device_data)
continue;
if (dev->state == SNDRV_DEV_BUILD && dev->ops->dev_register) {
@@ -192,13 +186,11 @@ EXPORT_SYMBOL(snd_device_register);
*/
int snd_device_register_all(struct snd_card *card)
{
- struct list_head *list;
struct snd_device *dev;
int err;
snd_assert(card != NULL, return -ENXIO);
- list_for_each(list, &card->devices) {
- dev = snd_device(list);
+ list_for_each_entry(dev, &card->devices, list) {
if (dev->state == SNDRV_DEV_BUILD && dev->ops->dev_register) {
if ((err = dev->ops->dev_register(dev)) < 0)
return err;
@@ -215,12 +207,10 @@ int snd_device_register_all(struct snd_card *card)
int snd_device_disconnect_all(struct snd_card *card)
{
struct snd_device *dev;
- struct list_head *list;
int err = 0;
snd_assert(card != NULL, return -ENXIO);
- list_for_each(list, &card->devices) {
- dev = snd_device(list);
+ list_for_each_entry(dev, &card->devices, list) {
if (snd_device_disconnect(card, dev->device_data) < 0)
err = -ENXIO;
}
@@ -234,7 +224,6 @@ int snd_device_disconnect_all(struct snd_card *card)
int snd_device_free_all(struct snd_card *card, snd_device_cmd_t cmd)
{
struct snd_device *dev;
- struct list_head *list;
int err;
unsigned int range_low, range_high;
@@ -242,8 +231,7 @@ int snd_device_free_all(struct snd_card *card, snd_device_cmd_t cmd)
range_low = cmd * SNDRV_DEV_TYPE_RANGE_SIZE;
range_high = range_low + SNDRV_DEV_TYPE_RANGE_SIZE - 1;
__again:
- list_for_each(list, &card->devices) {
- dev = snd_device(list);
+ list_for_each_entry(dev, &card->devices, list) {
if (dev->type >= range_low && dev->type <= range_high) {
if ((err = snd_device_free(card, dev->device_data)) < 0)
return err;
diff --git a/sound/core/hwdep.c b/sound/core/hwdep.c
index 46b47689362c..39c03f3dfbfa 100644
--- a/sound/core/hwdep.c
+++ b/sound/core/hwdep.c
@@ -47,14 +47,11 @@ static int snd_hwdep_dev_disconnect(struct snd_device *device);
static struct snd_hwdep *snd_hwdep_search(struct snd_card *card, int device)
{
- struct list_head *p;
struct snd_hwdep *hwdep;
- list_for_each(p, &snd_hwdep_devices) {
- hwdep = list_entry(p, struct snd_hwdep, list);
+ list_for_each_entry(hwdep, &snd_hwdep_devices, list)
if (hwdep->card == card && hwdep->device == device)
return hwdep;
- }
return NULL;
}
@@ -159,15 +156,16 @@ static int snd_hwdep_release(struct inode *inode, struct file * file)
int err = -ENXIO;
struct snd_hwdep *hw = file->private_data;
struct module *mod = hw->card->module;
+
mutex_lock(&hw->open_mutex);
- if (hw->ops.release) {
+ if (hw->ops.release)
err = hw->ops.release(hw, file);
- wake_up(&hw->open_wait);
- }
if (hw->used > 0)
hw->used--;
- snd_card_file_remove(hw->card, file);
mutex_unlock(&hw->open_mutex);
+ wake_up(&hw->open_wait);
+
+ snd_card_file_remove(hw->card, file);
module_put(mod);
return err;
}
@@ -468,15 +466,12 @@ static int snd_hwdep_dev_disconnect(struct snd_device *device)
static void snd_hwdep_proc_read(struct snd_info_entry *entry,
struct snd_info_buffer *buffer)
{
- struct list_head *p;
struct snd_hwdep *hwdep;
mutex_lock(&register_mutex);
- list_for_each(p, &snd_hwdep_devices) {
- hwdep = list_entry(p, struct snd_hwdep, list);
+ list_for_each_entry(hwdep, &snd_hwdep_devices, list)
snd_iprintf(buffer, "%02i-%02i: %s\n",
hwdep->card->number, hwdep->device, hwdep->name);
- }
mutex_unlock(&register_mutex);
}
diff --git a/sound/core/init.c b/sound/core/init.c
index a4cc6b155ae9..db6103733742 100644
--- a/sound/core/init.c
+++ b/sound/core/init.c
@@ -114,22 +114,28 @@ struct snd_card *snd_card_new(int idx, const char *xid,
if (idx < 0) {
int idx2;
for (idx2 = 0; idx2 < SNDRV_CARDS; idx2++)
+ /* idx == -1 == 0xffff means: take any free slot */
if (~snd_cards_lock & idx & 1<<idx2) {
idx = idx2;
if (idx >= snd_ecards_limit)
snd_ecards_limit = idx + 1;
break;
}
- } else if (idx < snd_ecards_limit) {
- if (snd_cards_lock & (1 << idx))
- err = -ENODEV; /* invalid */
- } else if (idx < SNDRV_CARDS)
- snd_ecards_limit = idx + 1; /* increase the limit */
- else
- err = -ENODEV;
+ } else {
+ if (idx < snd_ecards_limit) {
+ if (snd_cards_lock & (1 << idx))
+ err = -EBUSY; /* invalid */
+ } else {
+ if (idx < SNDRV_CARDS)
+ snd_ecards_limit = idx + 1; /* increase the limit */
+ else
+ err = -ENODEV;
+ }
+ }
if (idx < 0 || err < 0) {
mutex_unlock(&snd_card_mutex);
- snd_printk(KERN_ERR "cannot find the slot for index %d (range 0-%i)\n", idx, snd_ecards_limit - 1);
+ snd_printk(KERN_ERR "cannot find the slot for index %d (range 0-%i), error: %d\n",
+ idx, snd_ecards_limit - 1, err);
goto __error;
}
snd_cards_lock |= 1 << idx; /* lock it */
diff --git a/sound/core/memalloc.c b/sound/core/memalloc.c
index bc0bd0910a62..f057430db0d0 100644
--- a/sound/core/memalloc.c
+++ b/sound/core/memalloc.c
@@ -406,19 +406,17 @@ void snd_dma_free_pages(struct snd_dma_buffer *dmab)
*/
size_t snd_dma_get_reserved_buf(struct snd_dma_buffer *dmab, unsigned int id)
{
- struct list_head *p;
struct snd_mem_list *mem;
snd_assert(dmab, return 0);
mutex_lock(&list_mutex);
- list_for_each(p, &mem_list_head) {
- mem = list_entry(p, struct snd_mem_list, list);
+ list_for_each_entry(mem, &mem_list_head, list) {
if (mem->id == id &&
(mem->buffer.dev.dev == NULL || dmab->dev.dev == NULL ||
! memcmp(&mem->buffer.dev, &dmab->dev, sizeof(dmab->dev)))) {
struct device *dev = dmab->dev.dev;
- list_del(p);
+ list_del(&mem->list);
*dmab = mem->buffer;
if (dmab->dev.dev == NULL)
dmab->dev.dev = dev;
@@ -488,7 +486,6 @@ static int snd_mem_proc_read(char *page, char **start, off_t off,
{
int len = 0;
long pages = snd_allocated_pages >> (PAGE_SHIFT-12);
- struct list_head *p;
struct snd_mem_list *mem;
int devno;
static char *types[] = { "UNKNOWN", "CONT", "DEV", "DEV-SG", "SBUS" };
@@ -498,8 +495,7 @@ static int snd_mem_proc_read(char *page, char **start, off_t off,
"pages : %li bytes (%li pages per %likB)\n",
pages * PAGE_SIZE, pages, PAGE_SIZE / 1024);
devno = 0;
- list_for_each(p, &mem_list_head) {
- mem = list_entry(p, struct snd_mem_list, list);
+ list_for_each_entry(mem, &mem_list_head, list) {
devno++;
len += snprintf(page + len, count - len,
"buffer %d : ID %08x : type %s\n",
diff --git a/sound/core/misc.c b/sound/core/misc.c
index 03fc711f4127..6db86a7c9704 100644
--- a/sound/core/misc.c
+++ b/sound/core/misc.c
@@ -78,3 +78,31 @@ void snd_verbose_printd(const char *file, int line, const char *format, ...)
EXPORT_SYMBOL(snd_verbose_printd);
#endif
+
+#ifdef CONFIG_PCI
+#include <linux/pci.h>
+/**
+ * snd_pci_quirk_lookup - look up a PCI SSID quirk list
+ * @pci: pci_dev handle
+ * @list: quirk list, terminated by a null entry
+ *
+ * Look through the given quirk list and finds a matching entry
+ * with the same PCI SSID. When subdevice is 0, all subdevice
+ * values may match.
+ *
+ * Returns the matched entry pointer, or NULL if nothing matched.
+ */
+const struct snd_pci_quirk *
+snd_pci_quirk_lookup(struct pci_dev *pci, const struct snd_pci_quirk *list)
+{
+ const struct snd_pci_quirk *q;
+
+ for (q = list; q->subvendor; q++)
+ if (q->subvendor == pci->subsystem_vendor &&
+ (!q->subdevice || q->subdevice == pci->subsystem_device))
+ return q;
+ return NULL;
+}
+
+EXPORT_SYMBOL(snd_pci_quirk_lookup);
+#endif
diff --git a/sound/core/pcm.c b/sound/core/pcm.c
index 8e0189885516..2743414fc8fa 100644
--- a/sound/core/pcm.c
+++ b/sound/core/pcm.c
@@ -45,11 +45,9 @@ static int snd_pcm_dev_disconnect(struct snd_device *device);
static struct snd_pcm *snd_pcm_search(struct snd_card *card, int device)
{
- struct list_head *p;
struct snd_pcm *pcm;
- list_for_each(p, &snd_pcm_devices) {
- pcm = list_entry(p, struct snd_pcm, list);
+ list_for_each_entry(pcm, &snd_pcm_devices, list) {
if (pcm->card == card && pcm->device == device)
return pcm;
}
@@ -782,7 +780,6 @@ int snd_pcm_attach_substream(struct snd_pcm *pcm, int stream,
struct snd_pcm_runtime *runtime;
struct snd_ctl_file *kctl;
struct snd_card *card;
- struct list_head *list;
int prefer_subdevice = -1;
size_t size;
@@ -795,8 +792,7 @@ int snd_pcm_attach_substream(struct snd_pcm *pcm, int stream,
card = pcm->card;
down_read(&card->controls_rwsem);
- list_for_each(list, &card->ctl_files) {
- kctl = snd_ctl_file(list);
+ list_for_each_entry(kctl, &card->ctl_files, list) {
if (kctl->pid == current->pid) {
prefer_subdevice = kctl->prefer_pcm_subdevice;
if (prefer_subdevice != -1)
@@ -941,9 +937,10 @@ static int snd_pcm_dev_register(struct snd_device *device)
{
int cidx, err;
struct snd_pcm_substream *substream;
- struct list_head *list;
+ struct snd_pcm_notify *notify;
char str[16];
struct snd_pcm *pcm = device->device_data;
+ struct device *dev;
snd_assert(pcm != NULL && device != NULL, return -ENXIO);
mutex_lock(&register_mutex);
@@ -966,11 +963,18 @@ static int snd_pcm_dev_register(struct snd_device *device)
devtype = SNDRV_DEVICE_TYPE_PCM_CAPTURE;
break;
}
- if ((err = snd_register_device(devtype, pcm->card,
- pcm->device,
- &snd_pcm_f_ops[cidx],
- pcm, str)) < 0)
- {
+ /* device pointer to use, pcm->dev takes precedence if
+ * it is assigned, otherwise fall back to card's device
+ * if possible */
+ dev = pcm->dev;
+ if (!dev)
+ dev = snd_card_get_device_link(pcm->card);
+ /* register pcm */
+ err = snd_register_device_for_dev(devtype, pcm->card,
+ pcm->device,
+ &snd_pcm_f_ops[cidx],
+ pcm, str, dev);
+ if (err < 0) {
list_del(&pcm->list);
mutex_unlock(&register_mutex);
return err;
@@ -980,11 +984,10 @@ static int snd_pcm_dev_register(struct snd_device *device)
for (substream = pcm->streams[cidx].substream; substream; substream = substream->next)
snd_pcm_timer_init(substream);
}
- list_for_each(list, &snd_pcm_notify_list) {
- struct snd_pcm_notify *notify;
- notify = list_entry(list, struct snd_pcm_notify, list);
+
+ list_for_each_entry(notify, &snd_pcm_notify_list, list)
notify->n_register(pcm);
- }
+
mutex_unlock(&register_mutex);
return 0;
}
@@ -1027,7 +1030,7 @@ static int snd_pcm_dev_disconnect(struct snd_device *device)
int snd_pcm_notify(struct snd_pcm_notify *notify, int nfree)
{
- struct list_head *p;
+ struct snd_pcm *pcm;
snd_assert(notify != NULL &&
notify->n_register != NULL &&
@@ -1036,13 +1039,12 @@ int snd_pcm_notify(struct snd_pcm_notify *notify, int nfree)
mutex_lock(&register_mutex);
if (nfree) {
list_del(&notify->list);
- list_for_each(p, &snd_pcm_devices)
- notify->n_unregister(list_entry(p,
- struct snd_pcm, list));
+ list_for_each_entry(pcm, &snd_pcm_devices, list)
+ notify->n_unregister(pcm);
} else {
list_add_tail(&notify->list, &snd_pcm_notify_list);
- list_for_each(p, &snd_pcm_devices)
- notify->n_register(list_entry(p, struct snd_pcm, list));
+ list_for_each_entry(pcm, &snd_pcm_devices, list)
+ notify->n_register(pcm);
}
mutex_unlock(&register_mutex);
return 0;
@@ -1058,12 +1060,10 @@ EXPORT_SYMBOL(snd_pcm_notify);
static void snd_pcm_proc_read(struct snd_info_entry *entry,
struct snd_info_buffer *buffer)
{
- struct list_head *p;
struct snd_pcm *pcm;
mutex_lock(&register_mutex);
- list_for_each(p, &snd_pcm_devices) {
- pcm = list_entry(p, struct snd_pcm, list);
+ list_for_each_entry(pcm, &snd_pcm_devices, list) {
snd_iprintf(buffer, "%02i-%02i: %s : %s",
pcm->card->number, pcm->device, pcm->id, pcm->name);
if (pcm->streams[SNDRV_PCM_STREAM_PLAYBACK].substream)
diff --git a/sound/core/pcm_lib.c b/sound/core/pcm_lib.c
index b336797be4fc..9fefcaa2c324 100644
--- a/sound/core/pcm_lib.c
+++ b/sound/core/pcm_lib.c
@@ -781,6 +781,11 @@ int snd_interval_list(struct snd_interval *i, unsigned int count, unsigned int *
{
unsigned int k;
int changed = 0;
+
+ if (!count) {
+ i->empty = 1;
+ return -EINVAL;
+ }
for (k = 0; k < count; k++) {
if (mask && !(mask & (1 << k)))
continue;
diff --git a/sound/core/pcm_memory.c b/sound/core/pcm_memory.c
index be030cb4d373..95b1b2f0b1e2 100644
--- a/sound/core/pcm_memory.c
+++ b/sound/core/pcm_memory.c
@@ -101,6 +101,8 @@ int snd_pcm_lib_preallocate_free(struct snd_pcm_substream *substream)
{
snd_pcm_lib_preallocate_dma_free(substream);
#ifdef CONFIG_SND_VERBOSE_PROCFS
+ snd_info_free_entry(substream->proc_prealloc_max_entry);
+ substream->proc_prealloc_max_entry = NULL;
snd_info_free_entry(substream->proc_prealloc_entry);
substream->proc_prealloc_entry = NULL;
#endif
@@ -142,6 +144,18 @@ static void snd_pcm_lib_preallocate_proc_read(struct snd_info_entry *entry,
}
/*
+ * read callback for prealloc_max proc file
+ *
+ * prints the maximum allowed size in kB.
+ */
+static void snd_pcm_lib_preallocate_max_proc_read(struct snd_info_entry *entry,
+ struct snd_info_buffer *buffer)
+{
+ struct snd_pcm_substream *substream = entry->private_data;
+ snd_iprintf(buffer, "%lu\n", (unsigned long) substream->dma_max / 1024);
+}
+
+/*
* write callback for prealloc proc file
*
* accepts the preallocation size in kB.
@@ -203,6 +217,15 @@ static inline void preallocate_info_init(struct snd_pcm_substream *substream)
}
}
substream->proc_prealloc_entry = entry;
+ if ((entry = snd_info_create_card_entry(substream->pcm->card, "prealloc_max", substream->proc_root)) != NULL) {
+ entry->c.text.read = snd_pcm_lib_preallocate_max_proc_read;
+ entry->private_data = substream;
+ if (snd_info_register(entry) < 0) {
+ snd_info_free_entry(entry);
+ entry = NULL;
+ }
+ }
+ substream->proc_prealloc_max_entry = entry;
}
#else /* !CONFIG_SND_VERBOSE_PROCFS */
diff --git a/sound/core/rawmidi.c b/sound/core/rawmidi.c
index 0f055bfcbdac..7e6ceec738d5 100644
--- a/sound/core/rawmidi.c
+++ b/sound/core/rawmidi.c
@@ -61,14 +61,11 @@ static DEFINE_MUTEX(register_mutex);
static struct snd_rawmidi *snd_rawmidi_search(struct snd_card *card, int device)
{
- struct list_head *p;
struct snd_rawmidi *rawmidi;
- list_for_each(p, &snd_rawmidi_devices) {
- rawmidi = list_entry(p, struct snd_rawmidi, list);
+ list_for_each_entry(rawmidi, &snd_rawmidi_devices, list)
if (rawmidi->card == card && rawmidi->device == device)
return rawmidi;
- }
return NULL;
}
@@ -389,7 +386,6 @@ static int snd_rawmidi_open(struct inode *inode, struct file *file)
struct snd_rawmidi *rmidi;
struct snd_rawmidi_file *rawmidi_file;
wait_queue_t wait;
- struct list_head *list;
struct snd_ctl_file *kctl;
if (maj == snd_major) {
@@ -426,8 +422,7 @@ static int snd_rawmidi_open(struct inode *inode, struct file *file)
while (1) {
subdevice = -1;
down_read(&card->controls_rwsem);
- list_for_each(list, &card->ctl_files) {
- kctl = snd_ctl_file(list);
+ list_for_each_entry(kctl, &card->ctl_files, list) {
if (kctl->pid == current->pid) {
subdevice = kctl->prefer_rawmidi_subdevice;
if (subdevice != -1)
@@ -575,7 +570,6 @@ int snd_rawmidi_info_select(struct snd_card *card, struct snd_rawmidi_info *info
struct snd_rawmidi *rmidi;
struct snd_rawmidi_str *pstr;
struct snd_rawmidi_substream *substream;
- struct list_head *list;
mutex_lock(&register_mutex);
rmidi = snd_rawmidi_search(card, info->device);
@@ -589,8 +583,7 @@ int snd_rawmidi_info_select(struct snd_card *card, struct snd_rawmidi_info *info
return -ENOENT;
if (info->subdevice >= pstr->substream_count)
return -ENXIO;
- list_for_each(list, &pstr->substreams) {
- substream = list_entry(list, struct snd_rawmidi_substream, list);
+ list_for_each_entry(substream, &pstr->substreams, list) {
if ((unsigned int)substream->number == info->subdevice)
return snd_rawmidi_info(substream, info);
}
@@ -1313,14 +1306,14 @@ static void snd_rawmidi_proc_info_read(struct snd_info_entry *entry,
struct snd_rawmidi *rmidi;
struct snd_rawmidi_substream *substream;
struct snd_rawmidi_runtime *runtime;
- struct list_head *list;
rmidi = entry->private_data;
snd_iprintf(buffer, "%s\n\n", rmidi->name);
mutex_lock(&rmidi->open_mutex);
if (rmidi->info_flags & SNDRV_RAWMIDI_INFO_OUTPUT) {
- list_for_each(list, &rmidi->streams[SNDRV_RAWMIDI_STREAM_OUTPUT].substreams) {
- substream = list_entry(list, struct snd_rawmidi_substream, list);
+ list_for_each_entry(substream,
+ &rmidi->streams[SNDRV_RAWMIDI_STREAM_OUTPUT].substreams,
+ list) {
snd_iprintf(buffer,
"Output %d\n"
" Tx bytes : %lu\n",
@@ -1339,8 +1332,9 @@ static void snd_rawmidi_proc_info_read(struct snd_info_entry *entry,
}
}
if (rmidi->info_flags & SNDRV_RAWMIDI_INFO_INPUT) {
- list_for_each(list, &rmidi->streams[SNDRV_RAWMIDI_STREAM_INPUT].substreams) {
- substream = list_entry(list, struct snd_rawmidi_substream, list);
+ list_for_each_entry(substream,
+ &rmidi->streams[SNDRV_RAWMIDI_STREAM_INPUT].substreams,
+ list) {
snd_iprintf(buffer,
"Input %d\n"
" Rx bytes : %lu\n",
@@ -1625,13 +1619,10 @@ static int snd_rawmidi_dev_disconnect(struct snd_device *device)
void snd_rawmidi_set_ops(struct snd_rawmidi *rmidi, int stream,
struct snd_rawmidi_ops *ops)
{
- struct list_head *list;
struct snd_rawmidi_substream *substream;
- list_for_each(list, &rmidi->streams[stream].substreams) {
- substream = list_entry(list, struct snd_rawmidi_substream, list);
+ list_for_each_entry(substream, &rmidi->streams[stream].substreams, list)
substream->ops = ops;
- }
}
/*
diff --git a/sound/core/seq/seq_clientmgr.c b/sound/core/seq/seq_clientmgr.c
index 532a660df51d..bb9dd9fa8e51 100644
--- a/sound/core/seq/seq_clientmgr.c
+++ b/sound/core/seq/seq_clientmgr.c
@@ -659,7 +659,6 @@ static int deliver_to_subscribers(struct snd_seq_client *client,
int err = 0, num_ev = 0;
struct snd_seq_event event_saved;
struct snd_seq_client_port *src_port;
- struct list_head *p;
struct snd_seq_port_subs_info *grp;
src_port = snd_seq_port_use_ptr(client, event->source.port);
@@ -674,8 +673,7 @@ static int deliver_to_subscribers(struct snd_seq_client *client,
read_lock(&grp->list_lock);
else
down_read(&grp->list_mutex);
- list_for_each(p, &grp->list_head) {
- subs = list_entry(p, struct snd_seq_subscribers, src_list);
+ list_for_each_entry(subs, &grp->list_head, src_list) {
event->dest = subs->info.dest;
if (subs->info.flags & SNDRV_SEQ_PORT_SUBS_TIMESTAMP)
/* convert time according to flag with subscription */
@@ -709,15 +707,14 @@ static int port_broadcast_event(struct snd_seq_client *client,
{
int num_ev = 0, err = 0;
struct snd_seq_client *dest_client;
- struct list_head *p;
+ struct snd_seq_client_port *port;
dest_client = get_event_dest_client(event, SNDRV_SEQ_FILTER_BROADCAST);
if (dest_client == NULL)
return 0; /* no matching destination */
read_lock(&dest_client->ports_lock);
- list_for_each(p, &dest_client->ports_list_head) {
- struct snd_seq_client_port *port = list_entry(p, struct snd_seq_client_port, list);
+ list_for_each_entry(port, &dest_client->ports_list_head, list) {
event->dest.port = port->addr.port;
/* pass NULL as source client to avoid error bounce */
err = snd_seq_deliver_single_event(NULL, event,
@@ -2473,11 +2470,10 @@ static void snd_seq_info_dump_subscribers(struct snd_info_buffer *buffer,
static void snd_seq_info_dump_ports(struct snd_info_buffer *buffer,
struct snd_seq_client *client)
{
- struct list_head *l;
+ struct snd_seq_client_port *p;
mutex_lock(&client->ports_mutex);
- list_for_each(l, &client->ports_list_head) {
- struct snd_seq_client_port *p = list_entry(l, struct snd_seq_client_port, list);
+ list_for_each_entry(p, &client->ports_list_head, list) {
snd_iprintf(buffer, " Port %3d : \"%s\" (%c%c%c%c)\n",
p->addr.port, p->name,
FLAG_PERM_RD(p->capability),
diff --git a/sound/core/seq/seq_device.c b/sound/core/seq/seq_device.c
index b79d011813c0..37852cdace76 100644
--- a/sound/core/seq/seq_device.c
+++ b/sound/core/seq/seq_device.c
@@ -106,11 +106,10 @@ static void remove_drivers(void);
static void snd_seq_device_info(struct snd_info_entry *entry,
struct snd_info_buffer *buffer)
{
- struct list_head *head;
+ struct ops_list *ops;
mutex_lock(&ops_mutex);
- list_for_each(head, &opslist) {
- struct ops_list *ops = list_entry(head, struct ops_list, list);
+ list_for_each_entry(ops, &opslist, list) {
snd_iprintf(buffer, "snd-%s%s%s%s,%d\n",
ops->id,
ops->driver & DRIVER_LOADED ? ",loaded" : (ops->driver == DRIVER_EMPTY ? ",empty" : ""),
@@ -143,7 +142,7 @@ void snd_seq_autoload_unlock(void)
void snd_seq_device_load_drivers(void)
{
#ifdef CONFIG_KMOD
- struct list_head *head;
+ struct ops_list *ops;
/* Calling request_module during module_init()
* may cause blocking.
@@ -155,8 +154,7 @@ void snd_seq_device_load_drivers(void)
return;
mutex_lock(&ops_mutex);
- list_for_each(head, &opslist) {
- struct ops_list *ops = list_entry(head, struct ops_list, list);
+ list_for_each_entry(ops, &opslist, list) {
if (! (ops->driver & DRIVER_LOADED) &&
! (ops->driver & DRIVER_REQUESTED)) {
ops->used++;
@@ -314,8 +312,8 @@ static int snd_seq_device_dev_disconnect(struct snd_device *device)
int snd_seq_device_register_driver(char *id, struct snd_seq_dev_ops *entry,
int argsize)
{
- struct list_head *head;
struct ops_list *ops;
+ struct snd_seq_device *dev;
if (id == NULL || entry == NULL ||
entry->init_device == NULL || entry->free_device == NULL)
@@ -341,8 +339,7 @@ int snd_seq_device_register_driver(char *id, struct snd_seq_dev_ops *entry,
ops->argsize = argsize;
/* initialize existing devices if necessary */
- list_for_each(head, &ops->dev_list) {
- struct snd_seq_device *dev = list_entry(head, struct snd_seq_device, list);
+ list_for_each_entry(dev, &ops->dev_list, list) {
init_device(dev, ops);
}
mutex_unlock(&ops->reg_mutex);
@@ -394,8 +391,8 @@ static struct ops_list * create_driver(char *id)
*/
int snd_seq_device_unregister_driver(char *id)
{
- struct list_head *head;
struct ops_list *ops;
+ struct snd_seq_device *dev;
ops = find_driver(id, 0);
if (ops == NULL)
@@ -411,8 +408,7 @@ int snd_seq_device_unregister_driver(char *id)
/* close and release all devices associated with this driver */
mutex_lock(&ops->reg_mutex);
ops->driver |= DRIVER_LOCKED; /* do not remove this driver recursively */
- list_for_each(head, &ops->dev_list) {
- struct snd_seq_device *dev = list_entry(head, struct snd_seq_device, list);
+ list_for_each_entry(dev, &ops->dev_list, list) {
free_device(dev, ops);
}
@@ -512,11 +508,10 @@ static int free_device(struct snd_seq_device *dev, struct ops_list *ops)
*/
static struct ops_list * find_driver(char *id, int create_if_empty)
{
- struct list_head *head;
+ struct ops_list *ops;
mutex_lock(&ops_mutex);
- list_for_each(head, &opslist) {
- struct ops_list *ops = list_entry(head, struct ops_list, list);
+ list_for_each_entry(ops, &opslist, list) {
if (strcmp(ops->id, id) == 0) {
ops->used++;
mutex_unlock(&ops_mutex);
diff --git a/sound/core/seq/seq_ports.c b/sound/core/seq/seq_ports.c
index 8c64b58ff77b..eefd1cf872b4 100644
--- a/sound/core/seq/seq_ports.c
+++ b/sound/core/seq/seq_ports.c
@@ -59,14 +59,12 @@ much elements are in array.
struct snd_seq_client_port *snd_seq_port_use_ptr(struct snd_seq_client *client,
int num)
{
- struct list_head *p;
struct snd_seq_client_port *port;
if (client == NULL)
return NULL;
read_lock(&client->ports_lock);
- list_for_each(p, &client->ports_list_head) {
- port = list_entry(p, struct snd_seq_client_port, list);
+ list_for_each_entry(port, &client->ports_list_head, list) {
if (port->addr.port == num) {
if (port->closing)
break; /* deleting now */
@@ -85,14 +83,12 @@ struct snd_seq_client_port *snd_seq_port_query_nearest(struct snd_seq_client *cl
struct snd_seq_port_info *pinfo)
{
int num;
- struct list_head *p;
struct snd_seq_client_port *port, *found;
num = pinfo->addr.port;
found = NULL;
read_lock(&client->ports_lock);
- list_for_each(p, &client->ports_list_head) {
- port = list_entry(p, struct snd_seq_client_port, list);
+ list_for_each_entry(port, &client->ports_list_head, list) {
if (port->addr.port < num)
continue;
if (port->addr.port == num) {
@@ -131,8 +127,7 @@ struct snd_seq_client_port *snd_seq_create_port(struct snd_seq_client *client,
int port)
{
unsigned long flags;
- struct snd_seq_client_port *new_port;
- struct list_head *l;
+ struct snd_seq_client_port *new_port, *p;
int num = -1;
/* sanity check */
@@ -161,15 +156,14 @@ struct snd_seq_client_port *snd_seq_create_port(struct snd_seq_client *client,
num = port >= 0 ? port : 0;
mutex_lock(&client->ports_mutex);
write_lock_irqsave(&client->ports_lock, flags);
- list_for_each(l, &client->ports_list_head) {
- struct snd_seq_client_port *p = list_entry(l, struct snd_seq_client_port, list);
+ list_for_each_entry(p, &client->ports_list_head, list) {
if (p->addr.port > num)
break;
if (port < 0) /* auto-probe mode */
num = p->addr.port + 1;
}
/* insert the new port */
- list_add_tail(&new_port->list, l);
+ list_add_tail(&new_port->list, &p->list);
client->num_ports++;
new_port->addr.port = num; /* store the port number in the port */
write_unlock_irqrestore(&client->ports_lock, flags);
@@ -251,9 +245,9 @@ static void clear_subscriber_list(struct snd_seq_client *client,
list_del(&subs->dest_list);
else
list_del(&subs->src_list);
+ up_write(&agrp->list_mutex);
unsubscribe_port(c, aport, agrp, &subs->info, 1);
kfree(subs);
- up_write(&agrp->list_mutex);
snd_seq_port_unlock(aport);
snd_seq_client_unlock(c);
}
@@ -287,16 +281,14 @@ static int port_delete(struct snd_seq_client *client,
int snd_seq_delete_port(struct snd_seq_client *client, int port)
{
unsigned long flags;
- struct list_head *l;
- struct snd_seq_client_port *found = NULL;
+ struct snd_seq_client_port *found = NULL, *p;
mutex_lock(&client->ports_mutex);
write_lock_irqsave(&client->ports_lock, flags);
- list_for_each(l, &client->ports_list_head) {
- struct snd_seq_client_port *p = list_entry(l, struct snd_seq_client_port, list);
+ list_for_each_entry(p, &client->ports_list_head, list) {
if (p->addr.port == port) {
/* ok found. delete from the list at first */
- list_del(l);
+ list_del(&p->list);
client->num_ports--;
found = p;
break;
@@ -314,7 +306,8 @@ int snd_seq_delete_port(struct snd_seq_client *client, int port)
int snd_seq_delete_all_ports(struct snd_seq_client *client)
{
unsigned long flags;
- struct list_head deleted_list, *p, *n;
+ struct list_head deleted_list;
+ struct snd_seq_client_port *port, *tmp;
/* move the port list to deleted_list, and
* clear the port list in the client data.
@@ -331,9 +324,8 @@ int snd_seq_delete_all_ports(struct snd_seq_client *client)
write_unlock_irqrestore(&client->ports_lock, flags);
/* remove each port in deleted_list */
- list_for_each_safe(p, n, &deleted_list) {
- struct snd_seq_client_port *port = list_entry(p, struct snd_seq_client_port, list);
- list_del(p);
+ list_for_each_entry_safe(port, tmp, &deleted_list, list) {
+ list_del(&port->list);
snd_seq_system_client_ev_port_exit(port->addr.client, port->addr.port);
port_delete(client, port);
}
@@ -500,8 +492,7 @@ int snd_seq_port_connect(struct snd_seq_client *connector,
{
struct snd_seq_port_subs_info *src = &src_port->c_src;
struct snd_seq_port_subs_info *dest = &dest_port->c_dest;
- struct snd_seq_subscribers *subs;
- struct list_head *p;
+ struct snd_seq_subscribers *subs, *s;
int err, src_called = 0;
unsigned long flags;
int exclusive;
@@ -525,13 +516,11 @@ int snd_seq_port_connect(struct snd_seq_client *connector,
if (src->exclusive || dest->exclusive)
goto __error;
/* check whether already exists */
- list_for_each(p, &src->list_head) {
- struct snd_seq_subscribers *s = list_entry(p, struct snd_seq_subscribers, src_list);
+ list_for_each_entry(s, &src->list_head, src_list) {
if (match_subs_info(info, &s->info))
goto __error;
}
- list_for_each(p, &dest->list_head) {
- struct snd_seq_subscribers *s = list_entry(p, struct snd_seq_subscribers, dest_list);
+ list_for_each_entry(s, &dest->list_head, dest_list) {
if (match_subs_info(info, &s->info))
goto __error;
}
@@ -582,7 +571,6 @@ int snd_seq_port_disconnect(struct snd_seq_client *connector,
struct snd_seq_port_subs_info *src = &src_port->c_src;
struct snd_seq_port_subs_info *dest = &dest_port->c_dest;
struct snd_seq_subscribers *subs;
- struct list_head *p;
int err = -ENOENT;
unsigned long flags;
@@ -590,8 +578,7 @@ int snd_seq_port_disconnect(struct snd_seq_client *connector,
down_write_nested(&dest->list_mutex, SINGLE_DEPTH_NESTING);
/* look for the connection */
- list_for_each(p, &src->list_head) {
- subs = list_entry(p, struct snd_seq_subscribers, src_list);
+ list_for_each_entry(subs, &src->list_head, src_list) {
if (match_subs_info(info, &subs->info)) {
write_lock_irqsave(&src->list_lock, flags);
// write_lock(&dest->list_lock); // no lock yet
@@ -620,12 +607,10 @@ int snd_seq_port_disconnect(struct snd_seq_client *connector,
struct snd_seq_subscribers *snd_seq_port_get_subscription(struct snd_seq_port_subs_info *src_grp,
struct snd_seq_addr *dest_addr)
{
- struct list_head *p;
struct snd_seq_subscribers *s, *found = NULL;
down_read(&src_grp->list_mutex);
- list_for_each(p, &src_grp->list_head) {
- s = list_entry(p, struct snd_seq_subscribers, src_list);
+ list_for_each_entry(s, &src_grp->list_head, src_list) {
if (addr_match(dest_addr, &s->info.dest)) {
found = s;
break;
diff --git a/sound/core/seq/seq_virmidi.c b/sound/core/seq/seq_virmidi.c
index 0cfa06c6b81f..972f93405364 100644
--- a/sound/core/seq/seq_virmidi.c
+++ b/sound/core/seq/seq_virmidi.c
@@ -81,13 +81,11 @@ static int snd_virmidi_dev_receive_event(struct snd_virmidi_dev *rdev,
struct snd_seq_event *ev)
{
struct snd_virmidi *vmidi;
- struct list_head *list;
unsigned char msg[4];
int len;
read_lock(&rdev->filelist_lock);
- list_for_each(list, &rdev->filelist) {
- vmidi = list_entry(list, struct snd_virmidi, list);
+ list_for_each_entry(vmidi, &rdev->filelist, list) {
if (!vmidi->trigger)
continue;
if (ev->type == SNDRV_SEQ_EVENT_SYSEX) {
diff --git a/sound/core/sound.c b/sound/core/sound.c
index 82a61c67cf3a..4084de064127 100644
--- a/sound/core/sound.c
+++ b/sound/core/sound.c
@@ -219,26 +219,27 @@ static int snd_kernel_minor(int type, struct snd_card *card, int dev)
#endif
/**
- * snd_register_device - Register the ALSA device file for the card
+ * snd_register_device_for_dev - Register the ALSA device file for the card
* @type: the device type, SNDRV_DEVICE_TYPE_XXX
* @card: the card instance
* @dev: the device index
* @f_ops: the file operations
* @private_data: user pointer for f_ops->open()
* @name: the device file name
+ * @device: the &struct device to link this new device to
*
* Registers an ALSA device file for the given card.
* The operators have to be set in reg parameter.
*
- * Retrurns zero if successful, or a negative error code on failure.
+ * Returns zero if successful, or a negative error code on failure.
*/
-int snd_register_device(int type, struct snd_card *card, int dev,
- const struct file_operations *f_ops, void *private_data,
- const char *name)
+int snd_register_device_for_dev(int type, struct snd_card *card, int dev,
+ const struct file_operations *f_ops,
+ void *private_data,
+ const char *name, struct device *device)
{
int minor;
struct snd_minor *preg;
- struct device *device = snd_card_get_device_link(card);
snd_assert(name, return -EINVAL);
preg = kmalloc(sizeof *preg, GFP_KERNEL);
@@ -272,7 +273,7 @@ int snd_register_device(int type, struct snd_card *card, int dev,
return 0;
}
-EXPORT_SYMBOL(snd_register_device);
+EXPORT_SYMBOL(snd_register_device_for_dev);
/* find the matching minor record
* return the index of snd_minor, or -1 if not found
diff --git a/sound/core/timer.c b/sound/core/timer.c
index 10a79aed33f8..3e0638351069 100644
--- a/sound/core/timer.c
+++ b/sound/core/timer.c
@@ -35,9 +35,6 @@
#include <sound/minors.h>
#include <sound/initval.h>
#include <linux/kmod.h>
-#ifdef CONFIG_KERNELD
-#include <linux/kerneld.h>
-#endif
#if defined(CONFIG_SND_HPET) || defined(CONFIG_SND_HPET_MODULE)
#define DEFAULT_TIMER_LIMIT 3
@@ -130,11 +127,8 @@ static struct snd_timer_instance *snd_timer_instance_new(char *owner,
static struct snd_timer *snd_timer_find(struct snd_timer_id *tid)
{
struct snd_timer *timer = NULL;
- struct list_head *p;
-
- list_for_each(p, &snd_timer_list) {
- timer = list_entry(p, struct snd_timer, device_list);
+ list_for_each_entry(timer, &snd_timer_list, device_list) {
if (timer->tmr_class != tid->dev_class)
continue;
if ((timer->tmr_class == SNDRV_TIMER_CLASS_CARD ||
@@ -184,13 +178,10 @@ static void snd_timer_check_slave(struct snd_timer_instance *slave)
{
struct snd_timer *timer;
struct snd_timer_instance *master;
- struct list_head *p, *q;
/* FIXME: it's really dumb to look up all entries.. */
- list_for_each(p, &snd_timer_list) {
- timer = list_entry(p, struct snd_timer, device_list);
- list_for_each(q, &timer->open_list_head) {
- master = list_entry(q, struct snd_timer_instance, open_list);
+ list_for_each_entry(timer, &snd_timer_list, device_list) {
+ list_for_each_entry(master, &timer->open_list_head, open_list) {
if (slave->slave_class == master->slave_class &&
slave->slave_id == master->slave_id) {
list_del(&slave->open_list);
@@ -214,16 +205,13 @@ static void snd_timer_check_slave(struct snd_timer_instance *slave)
*/
static void snd_timer_check_master(struct snd_timer_instance *master)
{
- struct snd_timer_instance *slave;
- struct list_head *p, *n;
+ struct snd_timer_instance *slave, *tmp;
/* check all pending slaves */
- list_for_each_safe(p, n, &snd_timer_slave_list) {
- slave = list_entry(p, struct snd_timer_instance, open_list);
+ list_for_each_entry_safe(slave, tmp, &snd_timer_slave_list, open_list) {
if (slave->slave_class == master->slave_class &&
slave->slave_id == master->slave_id) {
- list_del(p);
- list_add_tail(p, &master->slave_list_head);
+ list_move_tail(&slave->open_list, &master->slave_list_head);
spin_lock_irq(&slave_active_lock);
slave->master = master;
slave->timer = master->timer;
@@ -317,8 +305,7 @@ static int _snd_timer_stop(struct snd_timer_instance *timeri,
int snd_timer_close(struct snd_timer_instance *timeri)
{
struct snd_timer *timer = NULL;
- struct list_head *p, *n;
- struct snd_timer_instance *slave;
+ struct snd_timer_instance *slave, *tmp;
snd_assert(timeri != NULL, return -ENXIO);
@@ -353,12 +340,11 @@ int snd_timer_close(struct snd_timer_instance *timeri)
timer->hw.close)
timer->hw.close(timer);
/* remove slave links */
- list_for_each_safe(p, n, &timeri->slave_list_head) {
- slave = list_entry(p, struct snd_timer_instance, open_list);
+ list_for_each_entry_safe(slave, tmp, &timeri->slave_list_head,
+ open_list) {
spin_lock_irq(&slave_active_lock);
_snd_timer_stop(slave, 1, SNDRV_TIMER_EVENT_RESOLUTION);
- list_del(p);
- list_add_tail(p, &snd_timer_slave_list);
+ list_move_tail(&slave->open_list, &snd_timer_slave_list);
slave->master = NULL;
slave->timer = NULL;
spin_unlock_irq(&slave_active_lock);
@@ -394,7 +380,6 @@ static void snd_timer_notify1(struct snd_timer_instance *ti, int event)
unsigned long flags;
unsigned long resolution = 0;
struct snd_timer_instance *ts;
- struct list_head *n;
struct timespec tstamp;
getnstimeofday(&tstamp);
@@ -413,11 +398,9 @@ static void snd_timer_notify1(struct snd_timer_instance *ti, int event)
if (timer->hw.flags & SNDRV_TIMER_HW_SLAVE)
return;
spin_lock_irqsave(&timer->lock, flags);
- list_for_each(n, &ti->slave_active_head) {
- ts = list_entry(n, struct snd_timer_instance, active_list);
+ list_for_each_entry(ts, &ti->slave_active_head, active_list)
if (ts->ccallback)
ts->ccallback(ti, event + 100, &tstamp, resolution);
- }
spin_unlock_irqrestore(&timer->lock, flags);
}
@@ -593,10 +576,8 @@ static void snd_timer_reschedule(struct snd_timer * timer, unsigned long ticks_l
{
struct snd_timer_instance *ti;
unsigned long ticks = ~0UL;
- struct list_head *p;
- list_for_each(p, &timer->active_list_head) {
- ti = list_entry(p, struct snd_timer_instance, active_list);
+ list_for_each_entry(ti, &timer->active_list_head, active_list) {
if (ti->flags & SNDRV_TIMER_IFLG_START) {
ti->flags &= ~SNDRV_TIMER_IFLG_START;
ti->flags |= SNDRV_TIMER_IFLG_RUNNING;
@@ -661,9 +642,9 @@ static void snd_timer_tasklet(unsigned long arg)
*/
void snd_timer_interrupt(struct snd_timer * timer, unsigned long ticks_left)
{
- struct snd_timer_instance *ti, *ts;
+ struct snd_timer_instance *ti, *ts, *tmp;
unsigned long resolution, ticks;
- struct list_head *p, *q, *n, *ack_list_head;
+ struct list_head *p, *ack_list_head;
unsigned long flags;
int use_tasklet = 0;
@@ -679,12 +660,12 @@ void snd_timer_interrupt(struct snd_timer * timer, unsigned long ticks_left)
resolution = timer->hw.resolution;
/* loop for all active instances
- * Here we cannot use list_for_each because the active_list of a
+ * Here we cannot use list_for_each_entry because the active_list of a
* processed instance is relinked to done_list_head before the callback
* is called.
*/
- list_for_each_safe(p, n, &timer->active_list_head) {
- ti = list_entry(p, struct snd_timer_instance, active_list);
+ list_for_each_entry_safe(ti, tmp, &timer->active_list_head,
+ active_list) {
if (!(ti->flags & SNDRV_TIMER_IFLG_RUNNING))
continue;
ti->pticks += ticks_left;
@@ -700,7 +681,7 @@ void snd_timer_interrupt(struct snd_timer * timer, unsigned long ticks_left)
} else {
ti->flags &= ~SNDRV_TIMER_IFLG_RUNNING;
if (--timer->running)
- list_del(p);
+ list_del(&ti->active_list);
}
if ((timer->hw.flags & SNDRV_TIMER_HW_TASKLET) ||
(ti->flags & SNDRV_TIMER_IFLG_FAST))
@@ -709,8 +690,7 @@ void snd_timer_interrupt(struct snd_timer * timer, unsigned long ticks_left)
ack_list_head = &timer->sack_list_head;
if (list_empty(&ti->ack_list))
list_add_tail(&ti->ack_list, ack_list_head);
- list_for_each(q, &ti->slave_active_head) {
- ts = list_entry(q, struct snd_timer_instance, active_list);
+ list_for_each_entry(ts, &ti->slave_active_head, active_list) {
ts->pticks = ti->pticks;
ts->resolution = resolution;
if (list_empty(&ts->ack_list))
@@ -844,7 +824,6 @@ static int snd_timer_dev_register(struct snd_device *dev)
{
struct snd_timer *timer = dev->device_data;
struct snd_timer *timer1;
- struct list_head *p;
snd_assert(timer != NULL && timer->hw.start != NULL &&
timer->hw.stop != NULL, return -ENXIO);
@@ -853,8 +832,7 @@ static int snd_timer_dev_register(struct snd_device *dev)
return -EINVAL;
mutex_lock(&register_mutex);
- list_for_each(p, &snd_timer_list) {
- timer1 = list_entry(p, struct snd_timer, device_list);
+ list_for_each_entry(timer1, &snd_timer_list, device_list) {
if (timer1->tmr_class > timer->tmr_class)
break;
if (timer1->tmr_class < timer->tmr_class)
@@ -877,7 +855,7 @@ static int snd_timer_dev_register(struct snd_device *dev)
mutex_unlock(&register_mutex);
return -EBUSY;
}
- list_add_tail(&timer->device_list, p);
+ list_add_tail(&timer->device_list, &timer1->device_list);
mutex_unlock(&register_mutex);
return 0;
}
@@ -896,7 +874,6 @@ void snd_timer_notify(struct snd_timer *timer, int event, struct timespec *tstam
unsigned long flags;
unsigned long resolution = 0;
struct snd_timer_instance *ti, *ts;
- struct list_head *p, *n;
if (! (timer->hw.flags & SNDRV_TIMER_HW_SLAVE))
return;
@@ -911,15 +888,12 @@ void snd_timer_notify(struct snd_timer *timer, int event, struct timespec *tstam
else
resolution = timer->hw.resolution;
}
- list_for_each(p, &timer->active_list_head) {
- ti = list_entry(p, struct snd_timer_instance, active_list);
+ list_for_each_entry(ti, &timer->active_list_head, active_list) {
if (ti->ccallback)
ti->ccallback(ti, event, tstamp, resolution);
- list_for_each(n, &ti->slave_active_head) {
- ts = list_entry(n, struct snd_timer_instance, active_list);
+ list_for_each_entry(ts, &ti->slave_active_head, active_list)
if (ts->ccallback)
ts->ccallback(ts, event, tstamp, resolution);
- }
}
spin_unlock_irqrestore(&timer->lock, flags);
}
@@ -1057,11 +1031,9 @@ static void snd_timer_proc_read(struct snd_info_entry *entry,
{
struct snd_timer *timer;
struct snd_timer_instance *ti;
- struct list_head *p, *q;
mutex_lock(&register_mutex);
- list_for_each(p, &snd_timer_list) {
- timer = list_entry(p, struct snd_timer, device_list);
+ list_for_each_entry(timer, &snd_timer_list, device_list) {
switch (timer->tmr_class) {
case SNDRV_TIMER_CLASS_GLOBAL:
snd_iprintf(buffer, "G%i: ", timer->tmr_device);
@@ -1088,14 +1060,12 @@ static void snd_timer_proc_read(struct snd_info_entry *entry,
if (timer->hw.flags & SNDRV_TIMER_HW_SLAVE)
snd_iprintf(buffer, " SLAVE");
snd_iprintf(buffer, "\n");
- list_for_each(q, &timer->open_list_head) {
- ti = list_entry(q, struct snd_timer_instance, open_list);
+ list_for_each_entry(ti, &timer->open_list_head, open_list)
snd_iprintf(buffer, " Client %s : %s\n",
ti->owner ? ti->owner : "unknown",
ti->flags & (SNDRV_TIMER_IFLG_START |
SNDRV_TIMER_IFLG_RUNNING)
? "running" : "stopped");
- }
}
mutex_unlock(&register_mutex);
}
diff --git a/sound/drivers/Kconfig b/sound/drivers/Kconfig
index 40ebd2f44056..83529b08d019 100644
--- a/sound/drivers/Kconfig
+++ b/sound/drivers/Kconfig
@@ -109,4 +109,15 @@ config SND_MPU401
To compile this driver as a module, choose M here: the module
will be called snd-mpu401.
+config SND_PORTMAN2X4
+ tristate "Portman 2x4 driver"
+ depends on SND && PARPORT
+ select SND_RAWMIDI
+ help
+ Say Y here to include support for Midiman Portman 2x4 parallel
+ port MIDI device.
+
+ To compile this driver as a module, choose M here: the module
+ will be called snd-portman2x4.
+
endmenu
diff --git a/sound/drivers/Makefile b/sound/drivers/Makefile
index c9bad6d67e73..04112642611a 100644
--- a/sound/drivers/Makefile
+++ b/sound/drivers/Makefile
@@ -6,6 +6,7 @@
snd-dummy-objs := dummy.o
snd-mtpav-objs := mtpav.o
snd-mts64-objs := mts64.o
+snd-portman2x4-objs := portman2x4.o
snd-serial-u16550-objs := serial-u16550.o
snd-virmidi-objs := virmidi.o
@@ -15,5 +16,6 @@ obj-$(CONFIG_SND_VIRMIDI) += snd-virmidi.o
obj-$(CONFIG_SND_SERIAL_U16550) += snd-serial-u16550.o
obj-$(CONFIG_SND_MTPAV) += snd-mtpav.o
obj-$(CONFIG_SND_MTS64) += snd-mts64.o
+obj-$(CONFIG_SND_PORTMAN2X4) += snd-portman2x4.o
obj-$(CONFIG_SND) += opl3/ opl4/ mpu401/ vx/
diff --git a/sound/drivers/dummy.c b/sound/drivers/dummy.c
index 42001efa9f3e..8339bad969ba 100644
--- a/sound/drivers/dummy.c
+++ b/sound/drivers/dummy.c
@@ -501,7 +501,7 @@ static int snd_dummy_volume_put(struct snd_kcontrol *kcontrol,
return change;
}
-static DECLARE_TLV_DB_SCALE(db_scale_dummy, -4500, 30, 0);
+static const DECLARE_TLV_DB_SCALE(db_scale_dummy, -4500, 30, 0);
#define DUMMY_CAPSRC(xname, xindex, addr) \
{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, .index = xindex, \
diff --git a/sound/drivers/portman2x4.c b/sound/drivers/portman2x4.c
new file mode 100644
index 000000000000..6c48772aaefd
--- /dev/null
+++ b/sound/drivers/portman2x4.c
@@ -0,0 +1,876 @@
+/*
+ * Driver for Midiman Portman2x4 parallel port midi interface
+ *
+ * Copyright (c) by Levent Guendogdu <levon@feature-it.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., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ * ChangeLog
+ * Jan 24 2007 Matthias Koenig <mkoenig@suse.de>
+ * - cleanup and rewrite
+ * Sep 30 2004 Tobias Gehrig <tobias@gehrig.tk>
+ * - source code cleanup
+ * Sep 03 2004 Tobias Gehrig <tobias@gehrig.tk>
+ * - fixed compilation problem with alsa 1.0.6a (removed MODULE_CLASSES,
+ * MODULE_PARM_SYNTAX and changed MODULE_DEVICES to
+ * MODULE_SUPPORTED_DEVICE)
+ * Mar 24 2004 Tobias Gehrig <tobias@gehrig.tk>
+ * - added 2.6 kernel support
+ * Mar 18 2004 Tobias Gehrig <tobias@gehrig.tk>
+ * - added parport_unregister_driver to the startup routine if the driver fails to detect a portman
+ * - added support for all 4 output ports in portman_putmidi
+ * Mar 17 2004 Tobias Gehrig <tobias@gehrig.tk>
+ * - added checks for opened input device in interrupt handler
+ * Feb 20 2004 Tobias Gehrig <tobias@gehrig.tk>
+ * - ported from alsa 0.5 to 1.0
+ */
+
+#include <sound/driver.h>
+#include <linux/init.h>
+#include <linux/platform_device.h>
+#include <linux/parport.h>
+#include <linux/spinlock.h>
+#include <linux/delay.h>
+#include <sound/core.h>
+#include <sound/initval.h>
+#include <sound/rawmidi.h>
+#include <sound/control.h>
+
+#define CARD_NAME "Portman 2x4"
+#define DRIVER_NAME "portman"
+#define PLATFORM_DRIVER "snd_portman2x4"
+
+static int index[SNDRV_CARDS] = SNDRV_DEFAULT_IDX;
+static char *id[SNDRV_CARDS] = SNDRV_DEFAULT_STR;
+static int enable[SNDRV_CARDS] = SNDRV_DEFAULT_ENABLE_PNP;
+
+static struct platform_device *platform_devices[SNDRV_CARDS];
+static int device_count;
+
+module_param_array(index, int, NULL, S_IRUGO);
+MODULE_PARM_DESC(index, "Index value for " CARD_NAME " soundcard.");
+module_param_array(id, charp, NULL, S_IRUGO);
+MODULE_PARM_DESC(id, "ID string for " CARD_NAME " soundcard.");
+module_param_array(enable, bool, NULL, S_IRUGO);
+MODULE_PARM_DESC(enable, "Enable " CARD_NAME " soundcard.");
+
+MODULE_AUTHOR("Levent Guendogdu, Tobias Gehrig, Matthias Koenig");
+MODULE_DESCRIPTION("Midiman Portman2x4");
+MODULE_LICENSE("GPL");
+MODULE_SUPPORTED_DEVICE("{{Midiman,Portman2x4}}");
+
+/*********************************************************************
+ * Chip specific
+ *********************************************************************/
+#define PORTMAN_NUM_INPUT_PORTS 2
+#define PORTMAN_NUM_OUTPUT_PORTS 4
+
+struct portman {
+ spinlock_t reg_lock;
+ struct snd_card *card;
+ struct snd_rawmidi *rmidi;
+ struct pardevice *pardev;
+ int pardev_claimed;
+
+ int open_count;
+ int mode[PORTMAN_NUM_INPUT_PORTS];
+ struct snd_rawmidi_substream *midi_input[PORTMAN_NUM_INPUT_PORTS];
+};
+
+static int portman_free(struct portman *pm)
+{
+ kfree(pm);
+ return 0;
+}
+
+static int __devinit portman_create(struct snd_card *card,
+ struct pardevice *pardev,
+ struct portman **rchip)
+{
+ struct portman *pm;
+
+ *rchip = NULL;
+
+ pm = kzalloc(sizeof(struct portman), GFP_KERNEL);
+ if (pm == NULL)
+ return -ENOMEM;
+
+ /* Init chip specific data */
+ spin_lock_init(&pm->reg_lock);
+ pm->card = card;
+ pm->pardev = pardev;
+
+ *rchip = pm;
+
+ return 0;
+}
+
+/*********************************************************************
+ * HW related constants
+ *********************************************************************/
+
+/* Standard PC parallel port status register equates. */
+#define PP_STAT_BSY 0x80 /* Busy status. Inverted. */
+#define PP_STAT_ACK 0x40 /* Acknowledge. Non-Inverted. */
+#define PP_STAT_POUT 0x20 /* Paper Out. Non-Inverted. */
+#define PP_STAT_SEL 0x10 /* Select. Non-Inverted. */
+#define PP_STAT_ERR 0x08 /* Error. Non-Inverted. */
+
+/* Standard PC parallel port command register equates. */
+#define PP_CMD_IEN 0x10 /* IRQ Enable. Non-Inverted. */
+#define PP_CMD_SELI 0x08 /* Select Input. Inverted. */
+#define PP_CMD_INIT 0x04 /* Init Printer. Non-Inverted. */
+#define PP_CMD_FEED 0x02 /* Auto Feed. Inverted. */
+#define PP_CMD_STB 0x01 /* Strobe. Inverted. */
+
+/* Parallel Port Command Register as implemented by PCP2x4. */
+#define INT_EN PP_CMD_IEN /* Interrupt enable. */
+#define STROBE PP_CMD_STB /* Command strobe. */
+
+/* The parallel port command register field (b1..b3) selects the
+ * various "registers" within the PC/P 2x4. These are the internal
+ * address of these "registers" that must be written to the parallel
+ * port command register.
+ */
+#define RXDATA0 (0 << 1) /* PCP RxData channel 0. */
+#define RXDATA1 (1 << 1) /* PCP RxData channel 1. */
+#define GEN_CTL (2 << 1) /* PCP General Control Register. */
+#define SYNC_CTL (3 << 1) /* PCP Sync Control Register. */
+#define TXDATA0 (4 << 1) /* PCP TxData channel 0. */
+#define TXDATA1 (5 << 1) /* PCP TxData channel 1. */
+#define TXDATA2 (6 << 1) /* PCP TxData channel 2. */
+#define TXDATA3 (7 << 1) /* PCP TxData channel 3. */
+
+/* Parallel Port Status Register as implemented by PCP2x4. */
+#define ESTB PP_STAT_POUT /* Echoed strobe. */
+#define INT_REQ PP_STAT_ACK /* Input data int request. */
+#define BUSY PP_STAT_ERR /* Interface Busy. */
+
+/* Parallel Port Status Register BUSY and SELECT lines are multiplexed
+ * between several functions. Depending on which 2x4 "register" is
+ * currently selected (b1..b3), the BUSY and SELECT lines are
+ * assigned as follows:
+ *
+ * SELECT LINE: A3 A2 A1
+ * --------
+ */
+#define RXAVAIL PP_STAT_SEL /* Rx Available, channel 0. 0 0 0 */
+// RXAVAIL1 PP_STAT_SEL /* Rx Available, channel 1. 0 0 1 */
+#define SYNC_STAT PP_STAT_SEL /* Reserved - Sync Status. 0 1 0 */
+// /* Reserved. 0 1 1 */
+#define TXEMPTY PP_STAT_SEL /* Tx Empty, channel 0. 1 0 0 */
+// TXEMPTY1 PP_STAT_SEL /* Tx Empty, channel 1. 1 0 1 */
+// TXEMPTY2 PP_STAT_SEL /* Tx Empty, channel 2. 1 1 0 */
+// TXEMPTY3 PP_STAT_SEL /* Tx Empty, channel 3. 1 1 1 */
+
+/* BUSY LINE: A3 A2 A1
+ * --------
+ */
+#define RXDATA PP_STAT_BSY /* Rx Input Data, channel 0. 0 0 0 */
+// RXDATA1 PP_STAT_BSY /* Rx Input Data, channel 1. 0 0 1 */
+#define SYNC_DATA PP_STAT_BSY /* Reserved - Sync Data. 0 1 0 */
+ /* Reserved. 0 1 1 */
+#define DATA_ECHO PP_STAT_BSY /* Parallel Port Data Echo. 1 0 0 */
+#define A0_ECHO PP_STAT_BSY /* Address 0 Echo. 1 0 1 */
+#define A1_ECHO PP_STAT_BSY /* Address 1 Echo. 1 1 0 */
+#define A2_ECHO PP_STAT_BSY /* Address 2 Echo. 1 1 1 */
+
+#define PORTMAN2X4_MODE_INPUT_TRIGGERED 0x01
+
+/*********************************************************************
+ * Hardware specific functions
+ *********************************************************************/
+static inline void portman_write_command(struct portman *pm, u8 value)
+{
+ parport_write_control(pm->pardev->port, value);
+}
+
+static inline u8 portman_read_command(struct portman *pm)
+{
+ return parport_read_control(pm->pardev->port);
+}
+
+static inline u8 portman_read_status(struct portman *pm)
+{
+ return parport_read_status(pm->pardev->port);
+}
+
+static inline u8 portman_read_data(struct portman *pm)
+{
+ return parport_read_data(pm->pardev->port);
+}
+
+static inline void portman_write_data(struct portman *pm, u8 value)
+{
+ parport_write_data(pm->pardev->port, value);
+}
+
+static void portman_write_midi(struct portman *pm,
+ int port, u8 mididata)
+{
+ int command = ((port + 4) << 1);
+
+ /* Get entering data byte and port number in BL and BH respectively.
+ * Set up Tx Channel address field for use with PP Cmd Register.
+ * Store address field in BH register.
+ * Inputs: AH = Output port number (0..3).
+ * AL = Data byte.
+ * command = TXDATA0 | INT_EN;
+ * Align port num with address field (b1...b3),
+ * set address for TXDatax, Strobe=0
+ */
+ command |= INT_EN;
+
+ /* Disable interrupts so that the process is not interrupted, then
+ * write the address associated with the current Tx channel to the
+ * PP Command Reg. Do not set the Strobe signal yet.
+ */
+
+ do {
+ portman_write_command(pm, command);
+
+ /* While the address lines settle, write parallel output data to
+ * PP Data Reg. This has no effect until Strobe signal is asserted.
+ */
+
+ portman_write_data(pm, mididata);
+
+ /* If PCP channel's TxEmpty is set (TxEmpty is read through the PP
+ * Status Register), then go write data. Else go back and wait.
+ */
+ } while ((portman_read_status(pm) & TXEMPTY) != TXEMPTY);
+
+ /* TxEmpty is set. Maintain PC/P destination address and assert
+ * Strobe through the PP Command Reg. This will Strobe data into
+ * the PC/P transmitter and set the PC/P BUSY signal.
+ */
+
+ portman_write_command(pm, command | STROBE);
+
+ /* Wait for strobe line to settle and echo back through hardware.
+ * Once it has echoed back, assume that the address and data lines
+ * have settled!
+ */
+
+ while ((portman_read_status(pm) & ESTB) == 0)
+ cpu_relax();
+
+ /* Release strobe and immediately re-allow interrupts. */
+ portman_write_command(pm, command);
+
+ while ((portman_read_status(pm) & ESTB) == ESTB)
+ cpu_relax();
+
+ /* PC/P BUSY is now set. We must wait until BUSY resets itself.
+ * We'll reenable ints while we're waiting.
+ */
+
+ while ((portman_read_status(pm) & BUSY) == BUSY)
+ cpu_relax();
+
+ /* Data sent. */
+}
+
+
+/*
+ * Read MIDI byte from port
+ * Attempt to read input byte from specified hardware input port (0..).
+ * Return -1 if no data
+ */
+static int portman_read_midi(struct portman *pm, int port)
+{
+ unsigned char midi_data = 0;
+ unsigned char cmdout; /* Saved address+IE bit. */
+
+ /* Make sure clocking edge is down before starting... */
+ portman_write_data(pm, 0); /* Make sure edge is down. */
+
+ /* Set destination address to PCP. */
+ cmdout = (port << 1) | INT_EN; /* Address + IE + No Strobe. */
+ portman_write_command(pm, cmdout);
+
+ while ((portman_read_status(pm) & ESTB) == ESTB)
+ cpu_relax(); /* Wait for strobe echo. */
+
+ /* After the address lines settle, check multiplexed RxAvail signal.
+ * If data is available, read it.
+ */
+ if ((portman_read_status(pm) & RXAVAIL) == 0)
+ return -1; /* No data. */
+
+ /* Set the Strobe signal to enable the Rx clocking circuitry. */
+ portman_write_command(pm, cmdout | STROBE); /* Write address+IE+Strobe. */
+
+ while ((portman_read_status(pm) & ESTB) == 0)
+ cpu_relax(); /* Wait for strobe echo. */
+
+ /* The first data bit (msb) is already sitting on the input line. */
+ midi_data = (portman_read_status(pm) & 128);
+ portman_write_data(pm, 1); /* Cause rising edge, which shifts data. */
+
+ /* Data bit 6. */
+ portman_write_data(pm, 0); /* Cause falling edge while data settles. */
+ midi_data |= (portman_read_status(pm) >> 1) & 64;
+ portman_write_data(pm, 1); /* Cause rising edge, which shifts data. */
+
+ /* Data bit 5. */
+ portman_write_data(pm, 0); /* Cause falling edge while data settles. */
+ midi_data |= (portman_read_status(pm) >> 2) & 32;
+ portman_write_data(pm, 1); /* Cause rising edge, which shifts data. */
+
+ /* Data bit 4. */
+ portman_write_data(pm, 0); /* Cause falling edge while data settles. */
+ midi_data |= (portman_read_status(pm) >> 3) & 16;
+ portman_write_data(pm, 1); /* Cause rising edge, which shifts data. */
+
+ /* Data bit 3. */
+ portman_write_data(pm, 0); /* Cause falling edge while data settles. */
+ midi_data |= (portman_read_status(pm) >> 4) & 8;
+ portman_write_data(pm, 1); /* Cause rising edge, which shifts data. */
+
+ /* Data bit 2. */
+ portman_write_data(pm, 0); /* Cause falling edge while data settles. */
+ midi_data |= (portman_read_status(pm) >> 5) & 4;
+ portman_write_data(pm, 1); /* Cause rising edge, which shifts data. */
+
+ /* Data bit 1. */
+ portman_write_data(pm, 0); /* Cause falling edge while data settles. */
+ midi_data |= (portman_read_status(pm) >> 6) & 2;
+ portman_write_data(pm, 1); /* Cause rising edge, which shifts data. */
+
+ /* Data bit 0. */
+ portman_write_data(pm, 0); /* Cause falling edge while data settles. */
+ midi_data |= (portman_read_status(pm) >> 7) & 1;
+ portman_write_data(pm, 1); /* Cause rising edge, which shifts data. */
+ portman_write_data(pm, 0); /* Return data clock low. */
+
+
+ /* De-assert Strobe and return data. */
+ portman_write_command(pm, cmdout); /* Output saved address+IE. */
+
+ /* Wait for strobe echo. */
+ while ((portman_read_status(pm) & ESTB) == ESTB)
+ cpu_relax();
+
+ return (midi_data & 255); /* Shift back and return value. */
+}
+
+/*
+ * Checks if any input data on the given channel is available
+ * Checks RxAvail
+ */
+static int portman_data_avail(struct portman *pm, int channel)
+{
+ int command = INT_EN;
+ switch (channel) {
+ case 0:
+ command |= RXDATA0;
+ break;
+ case 1:
+ command |= RXDATA1;
+ break;
+ }
+ /* Write hardware (assumme STROBE=0) */
+ portman_write_command(pm, command);
+ /* Check multiplexed RxAvail signal */
+ if ((portman_read_status(pm) & RXAVAIL) == RXAVAIL)
+ return 1; /* Data available */
+
+ /* No Data available */
+ return 0;
+}
+
+
+/*
+ * Flushes any input
+ */
+static void portman_flush_input(struct portman *pm, unsigned char port)
+{
+ /* Local variable for counting things */
+ unsigned int i = 0;
+ unsigned char command = 0;
+
+ switch (port) {
+ case 0:
+ command = RXDATA0;
+ break;
+ case 1:
+ command = RXDATA1;
+ break;
+ default:
+ snd_printk(KERN_WARNING
+ "portman_flush_input() Won't flush port %i\n",
+ port);
+ return;
+ }
+
+ /* Set address for specified channel in port and allow to settle. */
+ portman_write_command(pm, command);
+
+ /* Assert the Strobe and wait for echo back. */
+ portman_write_command(pm, command | STROBE);
+
+ /* Wait for ESTB */
+ while ((portman_read_status(pm) & ESTB) == 0)
+ cpu_relax();
+
+ /* Output clock cycles to the Rx circuitry. */
+ portman_write_data(pm, 0);
+
+ /* Flush 250 bits... */
+ for (i = 0; i < 250; i++) {
+ portman_write_data(pm, 1);
+ portman_write_data(pm, 0);
+ }
+
+ /* Deassert the Strobe signal of the port and wait for it to settle. */
+ portman_write_command(pm, command | INT_EN);
+
+ /* Wait for settling */
+ while ((portman_read_status(pm) & ESTB) == ESTB)
+ cpu_relax();
+}
+
+static int portman_probe(struct parport *p)
+{
+ /* Initialize the parallel port data register. Will set Rx clocks
+ * low in case we happen to be addressing the Rx ports at this time.
+ */
+ /* 1 */
+ parport_write_data(p, 0);
+
+ /* Initialize the parallel port command register, thus initializing
+ * hardware handshake lines to midi box:
+ *
+ * Strobe = 0
+ * Interrupt Enable = 0
+ */
+ /* 2 */
+ parport_write_control(p, 0);
+
+ /* Check if Portman PC/P 2x4 is out there. */
+ /* 3 */
+ parport_write_control(p, RXDATA0); /* Write Strobe=0 to command reg. */
+
+ /* Check for ESTB to be clear */
+ /* 4 */
+ if ((parport_read_status(p) & ESTB) == ESTB)
+ return 1; /* CODE 1 - Strobe Failure. */
+
+ /* Set for RXDATA0 where no damage will be done. */
+ /* 5 */
+ parport_write_control(p, RXDATA0 + STROBE); /* Write Strobe=1 to command reg. */
+
+ /* 6 */
+ if ((parport_read_status(p) & ESTB) != ESTB)
+ return 1; /* CODE 1 - Strobe Failure. */
+
+ /* 7 */
+ parport_write_control(p, 0); /* Reset Strobe=0. */
+
+ /* Check if Tx circuitry is functioning properly. If initialized
+ * unit TxEmpty is false, send out char and see if if goes true.
+ */
+ /* 8 */
+ parport_write_control(p, TXDATA0); /* Tx channel 0, strobe off. */
+
+ /* If PCP channel's TxEmpty is set (TxEmpty is read through the PP
+ * Status Register), then go write data. Else go back and wait.
+ */
+ /* 9 */
+ if ((parport_read_status(p) & TXEMPTY) == 0)
+ return 2;
+
+ /* Return OK status. */
+ return 0;
+}
+
+static int portman_device_init(struct portman *pm)
+{
+ portman_flush_input(pm, 0);
+ portman_flush_input(pm, 1);
+
+ return 0;
+}
+
+/*********************************************************************
+ * Rawmidi
+ *********************************************************************/
+static int snd_portman_midi_open(struct snd_rawmidi_substream *substream)
+{
+ return 0;
+}
+
+static int snd_portman_midi_close(struct snd_rawmidi_substream *substream)
+{
+ return 0;
+}
+
+static void snd_portman_midi_input_trigger(struct snd_rawmidi_substream *substream,
+ int up)
+{
+ struct portman *pm = substream->rmidi->private_data;
+ unsigned long flags;
+
+ spin_lock_irqsave(&pm->reg_lock, flags);
+ if (up)
+ pm->mode[substream->number] |= PORTMAN2X4_MODE_INPUT_TRIGGERED;
+ else
+ pm->mode[substream->number] &= ~PORTMAN2X4_MODE_INPUT_TRIGGERED;
+ spin_unlock_irqrestore(&pm->reg_lock, flags);
+}
+
+static void snd_portman_midi_output_trigger(struct snd_rawmidi_substream *substream,
+ int up)
+{
+ struct portman *pm = substream->rmidi->private_data;
+ unsigned long flags;
+ unsigned char byte;
+
+ spin_lock_irqsave(&pm->reg_lock, flags);
+ if (up) {
+ while ((snd_rawmidi_transmit(substream, &byte, 1) == 1))
+ portman_write_midi(pm, substream->number, byte);
+ }
+ spin_unlock_irqrestore(&pm->reg_lock, flags);
+}
+
+static struct snd_rawmidi_ops snd_portman_midi_output = {
+ .open = snd_portman_midi_open,
+ .close = snd_portman_midi_close,
+ .trigger = snd_portman_midi_output_trigger,
+};
+
+static struct snd_rawmidi_ops snd_portman_midi_input = {
+ .open = snd_portman_midi_open,
+ .close = snd_portman_midi_close,
+ .trigger = snd_portman_midi_input_trigger,
+};
+
+/* Create and initialize the rawmidi component */
+static int __devinit snd_portman_rawmidi_create(struct snd_card *card)
+{
+ struct portman *pm = card->private_data;
+ struct snd_rawmidi *rmidi;
+ struct snd_rawmidi_substream *substream;
+ int err;
+
+ err = snd_rawmidi_new(card, CARD_NAME, 0,
+ PORTMAN_NUM_OUTPUT_PORTS,
+ PORTMAN_NUM_INPUT_PORTS,
+ &rmidi);
+ if (err < 0)
+ return err;
+
+ rmidi->private_data = pm;
+ strcpy(rmidi->name, CARD_NAME);
+ rmidi->info_flags = SNDRV_RAWMIDI_INFO_OUTPUT |
+ SNDRV_RAWMIDI_INFO_INPUT |
+ SNDRV_RAWMIDI_INFO_DUPLEX;
+
+ pm->rmidi = rmidi;
+
+ /* register rawmidi ops */
+ snd_rawmidi_set_ops(rmidi, SNDRV_RAWMIDI_STREAM_OUTPUT,
+ &snd_portman_midi_output);
+ snd_rawmidi_set_ops(rmidi, SNDRV_RAWMIDI_STREAM_INPUT,
+ &snd_portman_midi_input);
+
+ /* name substreams */
+ /* output */
+ list_for_each_entry(substream,
+ &rmidi->streams[SNDRV_RAWMIDI_STREAM_OUTPUT].substreams,
+ list) {
+ sprintf(substream->name,
+ "Portman2x4 %d", substream->number+1);
+ }
+ /* input */
+ list_for_each_entry(substream,
+ &rmidi->streams[SNDRV_RAWMIDI_STREAM_INPUT].substreams,
+ list) {
+ pm->midi_input[substream->number] = substream;
+ sprintf(substream->name,
+ "Portman2x4 %d", substream->number+1);
+ }
+
+ return err;
+}
+
+/*********************************************************************
+ * parport stuff
+ *********************************************************************/
+static void snd_portman_interrupt(int irq, void *userdata)
+{
+ unsigned char midivalue = 0;
+ struct portman *pm = ((struct snd_card*)userdata)->private_data;
+
+ spin_lock(&pm->reg_lock);
+
+ /* While any input data is waiting */
+ while ((portman_read_status(pm) & INT_REQ) == INT_REQ) {
+ /* If data available on channel 0,
+ read it and stuff it into the queue. */
+ if (portman_data_avail(pm, 0)) {
+ /* Read Midi */
+ midivalue = portman_read_midi(pm, 0);
+ /* put midi into queue... */
+ if (pm->mode[0] & PORTMAN2X4_MODE_INPUT_TRIGGERED)
+ snd_rawmidi_receive(pm->midi_input[0],
+ &midivalue, 1);
+
+ }
+ /* If data available on channel 1,
+ read it and stuff it into the queue. */
+ if (portman_data_avail(pm, 1)) {
+ /* Read Midi */
+ midivalue = portman_read_midi(pm, 1);
+ /* put midi into queue... */
+ if (pm->mode[1] & PORTMAN2X4_MODE_INPUT_TRIGGERED)
+ snd_rawmidi_receive(pm->midi_input[1],
+ &midivalue, 1);
+ }
+
+ }
+
+ spin_unlock(&pm->reg_lock);
+}
+
+static int __devinit snd_portman_probe_port(struct parport *p)
+{
+ struct pardevice *pardev;
+ int res;
+
+ pardev = parport_register_device(p, DRIVER_NAME,
+ NULL, NULL, NULL,
+ 0, NULL);
+ if (!pardev)
+ return -EIO;
+
+ if (parport_claim(pardev)) {
+ parport_unregister_device(pardev);
+ return -EIO;
+ }
+
+ res = portman_probe(p);
+
+ parport_release(pardev);
+ parport_unregister_device(pardev);
+
+ return res;
+}
+
+static void __devinit snd_portman_attach(struct parport *p)
+{
+ struct platform_device *device;
+
+ device = platform_device_alloc(PLATFORM_DRIVER, device_count);
+ if (!device)
+ return;
+
+ /* Temporary assignment to forward the parport */
+ platform_set_drvdata(device, p);
+
+ if (platform_device_register(device) < 0) {
+ platform_device_put(device);
+ return;
+ }
+
+ /* Since we dont get the return value of probe
+ * We need to check if device probing succeeded or not */
+ if (!platform_get_drvdata(device)) {
+ platform_device_unregister(device);
+ return;
+ }
+
+ /* register device in global table */
+ platform_devices[device_count] = device;
+ device_count++;
+}
+
+static void snd_portman_detach(struct parport *p)
+{
+ /* nothing to do here */
+}
+
+static struct parport_driver portman_parport_driver = {
+ .name = "portman2x4",
+ .attach = snd_portman_attach,
+ .detach = snd_portman_detach
+};
+
+/*********************************************************************
+ * platform stuff
+ *********************************************************************/
+static void snd_portman_card_private_free(struct snd_card *card)
+{
+ struct portman *pm = card->private_data;
+ struct pardevice *pardev = pm->pardev;
+
+ if (pardev) {
+ if (pm->pardev_claimed)
+ parport_release(pardev);
+ parport_unregister_device(pardev);
+ }
+
+ portman_free(pm);
+}
+
+static int __devinit snd_portman_probe(struct platform_device *pdev)
+{
+ struct pardevice *pardev;
+ struct parport *p;
+ int dev = pdev->id;
+ struct snd_card *card = NULL;
+ struct portman *pm = NULL;
+ int err;
+
+ p = platform_get_drvdata(pdev);
+ platform_set_drvdata(pdev, NULL);
+
+ if (dev >= SNDRV_CARDS)
+ return -ENODEV;
+ if (!enable[dev])
+ return -ENOENT;
+
+ if ((err = snd_portman_probe_port(p)) < 0)
+ return err;
+
+ card = snd_card_new(index[dev], id[dev], THIS_MODULE, 0);
+ if (card == NULL) {
+ snd_printd("Cannot create card\n");
+ return -ENOMEM;
+ }
+ strcpy(card->driver, DRIVER_NAME);
+ strcpy(card->shortname, CARD_NAME);
+ sprintf(card->longname, "%s at 0x%lx, irq %i",
+ card->shortname, p->base, p->irq);
+
+ pardev = parport_register_device(p, /* port */
+ DRIVER_NAME, /* name */
+ NULL, /* preempt */
+ NULL, /* wakeup */
+ snd_portman_interrupt, /* ISR */
+ PARPORT_DEV_EXCL, /* flags */
+ (void *)card); /* private */
+ if (pardev == NULL) {
+ snd_printd("Cannot register pardevice\n");
+ err = -EIO;
+ goto __err;
+ }
+
+ if ((err = portman_create(card, pardev, &pm)) < 0) {
+ snd_printd("Cannot create main component\n");
+ parport_unregister_device(pardev);
+ goto __err;
+ }
+ card->private_data = pm;
+ card->private_free = snd_portman_card_private_free;
+
+ if ((err = snd_portman_rawmidi_create(card)) < 0) {
+ snd_printd("Creating Rawmidi component failed\n");
+ goto __err;
+ }
+
+ /* claim parport */
+ if (parport_claim(pardev)) {
+ snd_printd("Cannot claim parport 0x%lx\n", pardev->port->base);
+ err = -EIO;
+ goto __err;
+ }
+ pm->pardev_claimed = 1;
+
+ /* init device */
+ if ((err = portman_device_init(pm)) < 0)
+ goto __err;
+
+ platform_set_drvdata(pdev, card);
+
+ /* At this point card will be usable */
+ if ((err = snd_card_register(card)) < 0) {
+ snd_printd("Cannot register card\n");
+ goto __err;
+ }
+
+ snd_printk(KERN_INFO "Portman 2x4 on 0x%lx\n", p->base);
+ return 0;
+
+__err:
+ snd_card_free(card);
+ return err;
+}
+
+static int snd_portman_remove(struct platform_device *pdev)
+{
+ struct snd_card *card = platform_get_drvdata(pdev);
+
+ if (card)
+ snd_card_free(card);
+
+ return 0;
+}
+
+
+static struct platform_driver snd_portman_driver = {
+ .probe = snd_portman_probe,
+ .remove = snd_portman_remove,
+ .driver = {
+ .name = PLATFORM_DRIVER
+ }
+};
+
+/*********************************************************************
+ * module init stuff
+ *********************************************************************/
+static void snd_portman_unregister_all(void)
+{
+ int i;
+
+ for (i = 0; i < SNDRV_CARDS; ++i) {
+ if (platform_devices[i]) {
+ platform_device_unregister(platform_devices[i]);
+ platform_devices[i] = NULL;
+ }
+ }
+ platform_driver_unregister(&snd_portman_driver);
+ parport_unregister_driver(&portman_parport_driver);
+}
+
+static int __init snd_portman_module_init(void)
+{
+ int err;
+
+ if ((err = platform_driver_register(&snd_portman_driver)) < 0)
+ return err;
+
+ if (parport_register_driver(&portman_parport_driver) != 0) {
+ platform_driver_unregister(&snd_portman_driver);
+ return -EIO;
+ }
+
+ if (device_count == 0) {
+ snd_portman_unregister_all();
+ return -ENODEV;
+ }
+
+ return 0;
+}
+
+static void __exit snd_portman_module_exit(void)
+{
+ snd_portman_unregister_all();
+}
+
+module_init(snd_portman_module_init);
+module_exit(snd_portman_module_exit);
diff --git a/sound/drivers/serial-u16550.c b/sound/drivers/serial-u16550.c
index 74028b2219c2..3a86a5820726 100644
--- a/sound/drivers/serial-u16550.c
+++ b/sound/drivers/serial-u16550.c
@@ -117,13 +117,13 @@ MODULE_PARM_DESC(adaptor, "Type of adaptor.");
#define SERIAL_MODE_INPUT_TRIGGERED (1 << 2)
#define SERIAL_MODE_OUTPUT_TRIGGERED (1 << 3)
-typedef struct _snd_uart16550 {
+struct snd_uart16550 {
struct snd_card *card;
struct snd_rawmidi *rmidi;
struct snd_rawmidi_substream *midi_output[SNDRV_SERIAL_MAX_OUTS];
struct snd_rawmidi_substream *midi_input[SNDRV_SERIAL_MAX_INS];
- int filemode; //open status of file
+ int filemode; /* open status of file */
spinlock_t open_lock;
@@ -140,39 +140,39 @@ typedef struct _snd_uart16550 {
unsigned char old_divisor_msb;
unsigned char old_line_ctrl_reg;
- // parameter for using of write loop
- short int fifo_limit; //used in uart16550
- short int fifo_count; //used in uart16550
+ /* parameter for using of write loop */
+ short int fifo_limit; /* used in uart16550 */
+ short int fifo_count; /* used in uart16550 */
- // type of adaptor
+ /* type of adaptor */
int adaptor;
- // inputs
+ /* inputs */
int prev_in;
unsigned char rstatus;
- // outputs
+ /* outputs */
int prev_out;
unsigned char prev_status[SNDRV_SERIAL_MAX_OUTS];
- // write buffer and its writing/reading position
+ /* write buffer and its writing/reading position */
unsigned char tx_buff[TX_BUFF_SIZE];
int buff_in_count;
int buff_in;
int buff_out;
int drop_on_full;
- // wait timer
+ /* wait timer */
unsigned int timer_running:1;
struct timer_list buffer_timer;
-} snd_uart16550_t;
+};
static struct platform_device *devices[SNDRV_CARDS];
-static inline void snd_uart16550_add_timer(snd_uart16550_t *uart)
+static inline void snd_uart16550_add_timer(struct snd_uart16550 *uart)
{
- if (! uart->timer_running) {
+ if (!uart->timer_running) {
/* timer 38600bps * 10bit * 16byte */
uart->buffer_timer.expires = jiffies + (HZ+255)/256;
uart->timer_running = 1;
@@ -180,7 +180,7 @@ static inline void snd_uart16550_add_timer(snd_uart16550_t *uart)
}
}
-static inline void snd_uart16550_del_timer(snd_uart16550_t *uart)
+static inline void snd_uart16550_del_timer(struct snd_uart16550 *uart)
{
if (uart->timer_running) {
del_timer(&uart->buffer_timer);
@@ -189,10 +189,10 @@ static inline void snd_uart16550_del_timer(snd_uart16550_t *uart)
}
/* This macro is only used in snd_uart16550_io_loop */
-static inline void snd_uart16550_buffer_output(snd_uart16550_t *uart)
+static inline void snd_uart16550_buffer_output(struct snd_uart16550 *uart)
{
unsigned short buff_out = uart->buff_out;
- if( uart->buff_in_count > 0 ) {
+ if (uart->buff_in_count > 0) {
outb(uart->tx_buff[buff_out], uart->base + UART_TX);
uart->fifo_count++;
buff_out++;
@@ -206,7 +206,7 @@ static inline void snd_uart16550_buffer_output(snd_uart16550_t *uart)
* We don't want to interrupt this,
* as we're already handling an interrupt
*/
-static void snd_uart16550_io_loop(snd_uart16550_t * uart)
+static void snd_uart16550_io_loop(struct snd_uart16550 * uart)
{
unsigned char c, status;
int substream;
@@ -220,9 +220,8 @@ static void snd_uart16550_io_loop(snd_uart16550_t * uart)
c = inb(uart->base + UART_RX);
/* keep track of last status byte */
- if (c & 0x80) {
+ if (c & 0x80)
uart->rstatus = c;
- }
/* handle stream switch */
if (uart->adaptor == SNDRV_SERIAL_GENERIC) {
@@ -230,14 +229,16 @@ static void snd_uart16550_io_loop(snd_uart16550_t * uart)
if (c <= SNDRV_SERIAL_MAX_INS && c > 0)
substream = c - 1;
if (c != 0xf5)
- uart->rstatus = 0; /* prevent future bytes from being interpreted as streams */
- }
- else if ((uart->filemode & SERIAL_MODE_INPUT_OPEN) && (uart->midi_input[substream] != NULL)) {
- snd_rawmidi_receive(uart->midi_input[substream], &c, 1);
- }
- } else if ((uart->filemode & SERIAL_MODE_INPUT_OPEN) && (uart->midi_input[substream] != NULL)) {
+ /* prevent future bytes from being
+ interpreted as streams */
+ uart->rstatus = 0;
+ } else if ((uart->filemode & SERIAL_MODE_INPUT_OPEN)
+ && uart->midi_input[substream])
+ snd_rawmidi_receive(uart->midi_input[substream],
+ &c, 1);
+ } else if ((uart->filemode & SERIAL_MODE_INPUT_OPEN) &&
+ uart->midi_input[substream])
snd_rawmidi_receive(uart->midi_input[substream], &c, 1);
- }
if (status & UART_LSR_OE)
snd_printk("%s: Overrun on device at 0x%lx\n",
@@ -250,21 +251,20 @@ static void snd_uart16550_io_loop(snd_uart16550_t * uart)
/* no need of check SERIAL_MODE_OUTPUT_OPEN because if not,
buffer is never filled. */
/* Check write status */
- if (status & UART_LSR_THRE) {
+ if (status & UART_LSR_THRE)
uart->fifo_count = 0;
- }
if (uart->adaptor == SNDRV_SERIAL_MS124W_SA
|| uart->adaptor == SNDRV_SERIAL_GENERIC) {
/* Can't use FIFO, must send only when CTS is true */
status = inb(uart->base + UART_MSR);
- while( (uart->fifo_count == 0) && (status & UART_MSR_CTS) &&
- (uart->buff_in_count > 0) ) {
+ while (uart->fifo_count == 0 && (status & UART_MSR_CTS) &&
+ uart->buff_in_count > 0) {
snd_uart16550_buffer_output(uart);
- status = inb( uart->base + UART_MSR );
+ status = inb(uart->base + UART_MSR);
}
} else {
/* Write loop */
- while (uart->fifo_count < uart->fifo_limit /* Can we write ? */
+ while (uart->fifo_count < uart->fifo_limit /* Can we write ? */
&& uart->buff_in_count > 0) /* Do we want to? */
snd_uart16550_buffer_output(uart);
}
@@ -294,15 +294,16 @@ static void snd_uart16550_io_loop(snd_uart16550_t * uart)
*/
static irqreturn_t snd_uart16550_interrupt(int irq, void *dev_id)
{
- snd_uart16550_t *uart;
+ struct snd_uart16550 *uart;
- uart = (snd_uart16550_t *) dev_id;
+ uart = dev_id;
spin_lock(&uart->open_lock);
if (uart->filemode == SERIAL_MODE_NOT_OPENED) {
spin_unlock(&uart->open_lock);
return IRQ_NONE;
}
- inb(uart->base + UART_IIR); /* indicate to the UART that the interrupt has been serviced */
+ /* indicate to the UART that the interrupt has been serviced */
+ inb(uart->base + UART_IIR);
snd_uart16550_io_loop(uart);
spin_unlock(&uart->open_lock);
return IRQ_HANDLED;
@@ -312,9 +313,9 @@ static irqreturn_t snd_uart16550_interrupt(int irq, void *dev_id)
static void snd_uart16550_buffer_timer(unsigned long data)
{
unsigned long flags;
- snd_uart16550_t *uart;
+ struct snd_uart16550 *uart;
- uart = (snd_uart16550_t *)data;
+ uart = (struct snd_uart16550 *)data;
spin_lock_irqsave(&uart->open_lock, flags);
snd_uart16550_del_timer(uart);
snd_uart16550_io_loop(uart);
@@ -326,7 +327,7 @@ static void snd_uart16550_buffer_timer(unsigned long data)
* return 0 if found
* return negative error if not found
*/
-static int __init snd_uart16550_detect(snd_uart16550_t *uart)
+static int __init snd_uart16550_detect(struct snd_uart16550 *uart)
{
unsigned long io_base = uart->base;
int ok;
@@ -343,7 +344,8 @@ static int __init snd_uart16550_detect(snd_uart16550_t *uart)
return -EBUSY;
}
- ok = 1; /* uart detected unless one of the following tests should fail */
+ /* uart detected unless one of the following tests should fail */
+ ok = 1;
/* 8 data-bits, 1 stop-bit, parity off, DLAB = 0 */
outb(UART_LCR_WLEN8, io_base + UART_LCR); /* Line Control Register */
c = inb(io_base + UART_IER);
@@ -368,7 +370,7 @@ static int __init snd_uart16550_detect(snd_uart16550_t *uart)
return ok;
}
-static void snd_uart16550_do_open(snd_uart16550_t * uart)
+static void snd_uart16550_do_open(struct snd_uart16550 * uart)
{
char byte;
@@ -460,7 +462,7 @@ static void snd_uart16550_do_open(snd_uart16550_t * uart)
inb(uart->base + UART_RX); /* Clear any pre-existing receive interrupt */
}
-static void snd_uart16550_do_close(snd_uart16550_t * uart)
+static void snd_uart16550_do_close(struct snd_uart16550 * uart)
{
if (uart->irq < 0)
snd_uart16550_del_timer(uart);
@@ -514,7 +516,7 @@ static void snd_uart16550_do_close(snd_uart16550_t * uart)
static int snd_uart16550_input_open(struct snd_rawmidi_substream *substream)
{
unsigned long flags;
- snd_uart16550_t *uart = substream->rmidi->private_data;
+ struct snd_uart16550 *uart = substream->rmidi->private_data;
spin_lock_irqsave(&uart->open_lock, flags);
if (uart->filemode == SERIAL_MODE_NOT_OPENED)
@@ -528,7 +530,7 @@ static int snd_uart16550_input_open(struct snd_rawmidi_substream *substream)
static int snd_uart16550_input_close(struct snd_rawmidi_substream *substream)
{
unsigned long flags;
- snd_uart16550_t *uart = substream->rmidi->private_data;
+ struct snd_uart16550 *uart = substream->rmidi->private_data;
spin_lock_irqsave(&uart->open_lock, flags);
uart->filemode &= ~SERIAL_MODE_INPUT_OPEN;
@@ -539,24 +541,24 @@ static int snd_uart16550_input_close(struct snd_rawmidi_substream *substream)
return 0;
}
-static void snd_uart16550_input_trigger(struct snd_rawmidi_substream *substream, int up)
+static void snd_uart16550_input_trigger(struct snd_rawmidi_substream *substream,
+ int up)
{
unsigned long flags;
- snd_uart16550_t *uart = substream->rmidi->private_data;
+ struct snd_uart16550 *uart = substream->rmidi->private_data;
spin_lock_irqsave(&uart->open_lock, flags);
- if (up) {
+ if (up)
uart->filemode |= SERIAL_MODE_INPUT_TRIGGERED;
- } else {
+ else
uart->filemode &= ~SERIAL_MODE_INPUT_TRIGGERED;
- }
spin_unlock_irqrestore(&uart->open_lock, flags);
}
static int snd_uart16550_output_open(struct snd_rawmidi_substream *substream)
{
unsigned long flags;
- snd_uart16550_t *uart = substream->rmidi->private_data;
+ struct snd_uart16550 *uart = substream->rmidi->private_data;
spin_lock_irqsave(&uart->open_lock, flags);
if (uart->filemode == SERIAL_MODE_NOT_OPENED)
@@ -570,7 +572,7 @@ static int snd_uart16550_output_open(struct snd_rawmidi_substream *substream)
static int snd_uart16550_output_close(struct snd_rawmidi_substream *substream)
{
unsigned long flags;
- snd_uart16550_t *uart = substream->rmidi->private_data;
+ struct snd_uart16550 *uart = substream->rmidi->private_data;
spin_lock_irqsave(&uart->open_lock, flags);
uart->filemode &= ~SERIAL_MODE_OUTPUT_OPEN;
@@ -581,18 +583,20 @@ static int snd_uart16550_output_close(struct snd_rawmidi_substream *substream)
return 0;
};
-static inline int snd_uart16550_buffer_can_write( snd_uart16550_t *uart, int Num )
+static inline int snd_uart16550_buffer_can_write(struct snd_uart16550 *uart,
+ int Num)
{
- if( uart->buff_in_count + Num < TX_BUFF_SIZE )
+ if (uart->buff_in_count + Num < TX_BUFF_SIZE)
return 1;
else
return 0;
}
-static inline int snd_uart16550_write_buffer(snd_uart16550_t *uart, unsigned char byte)
+static inline int snd_uart16550_write_buffer(struct snd_uart16550 *uart,
+ unsigned char byte)
{
unsigned short buff_in = uart->buff_in;
- if( uart->buff_in_count < TX_BUFF_SIZE ) {
+ if (uart->buff_in_count < TX_BUFF_SIZE) {
uart->tx_buff[buff_in] = byte;
buff_in++;
buff_in &= TX_BUFF_MASK;
@@ -605,12 +609,14 @@ static inline int snd_uart16550_write_buffer(snd_uart16550_t *uart, unsigned cha
return 0;
}
-static int snd_uart16550_output_byte(snd_uart16550_t *uart, struct snd_rawmidi_substream *substream, unsigned char midi_byte)
+static int snd_uart16550_output_byte(struct snd_uart16550 *uart,
+ struct snd_rawmidi_substream *substream,
+ unsigned char midi_byte)
{
- if (uart->buff_in_count == 0 /* Buffer empty? */
+ if (uart->buff_in_count == 0 /* Buffer empty? */
&& ((uart->adaptor != SNDRV_SERIAL_MS124W_SA &&
uart->adaptor != SNDRV_SERIAL_GENERIC) ||
- (uart->fifo_count == 0 /* FIFO empty? */
+ (uart->fifo_count == 0 /* FIFO empty? */
&& (inb(uart->base + UART_MSR) & UART_MSR_CTS)))) { /* CTS? */
/* Tx Buffer Empty - try to write immediately */
@@ -623,12 +629,13 @@ static int snd_uart16550_output_byte(snd_uart16550_t *uart, struct snd_rawmidi_s
uart->fifo_count++;
outb(midi_byte, uart->base + UART_TX);
} else {
- /* Cannot write (buffer empty) - put char in buffer */
+ /* Cannot write (buffer empty) -
+ * put char in buffer */
snd_uart16550_write_buffer(uart, midi_byte);
}
}
} else {
- if( !snd_uart16550_write_buffer(uart, midi_byte) ) {
+ if (!snd_uart16550_write_buffer(uart, midi_byte)) {
snd_printk("%s: Buffer overrun on device at 0x%lx\n",
uart->rmidi->name, uart->base);
return 0;
@@ -642,9 +649,9 @@ static void snd_uart16550_output_write(struct snd_rawmidi_substream *substream)
{
unsigned long flags;
unsigned char midi_byte, addr_byte;
- snd_uart16550_t *uart = substream->rmidi->private_data;
+ struct snd_uart16550 *uart = substream->rmidi->private_data;
char first;
- static unsigned long lasttime=0;
+ static unsigned long lasttime = 0;
/* Interupts are disabled during the updating of the tx_buff,
* since it is 'bad' to have two processes updating the same
@@ -653,7 +660,7 @@ static void snd_uart16550_output_write(struct snd_rawmidi_substream *substream)
spin_lock_irqsave(&uart->open_lock, flags);
- if (uart->irq < 0) //polling
+ if (uart->irq < 0) /* polling */
snd_uart16550_io_loop(uart);
if (uart->adaptor == SNDRV_SERIAL_MS124W_MB) {
@@ -671,7 +678,8 @@ static void snd_uart16550_output_write(struct snd_rawmidi_substream *substream)
/* select any combination of the four ports */
addr_byte = (substream->number << 4) | 0x08;
/* ...except none */
- if (addr_byte == 0x08) addr_byte = 0xf8;
+ if (addr_byte == 0x08)
+ addr_byte = 0xf8;
#endif
snd_uart16550_output_byte(uart, substream, addr_byte);
/* send midi byte */
@@ -679,31 +687,42 @@ static void snd_uart16550_output_write(struct snd_rawmidi_substream *substream)
}
} else {
first = 0;
- while( 1 == snd_rawmidi_transmit_peek(substream, &midi_byte, 1) ) {
- /* Also send F5 after 3 seconds with no data to handle device disconnect */
- if (first == 0 && (uart->adaptor == SNDRV_SERIAL_SOUNDCANVAS ||
- uart->adaptor == SNDRV_SERIAL_GENERIC) &&
- (uart->prev_out != substream->number || jiffies-lasttime > 3*HZ)) {
-
- if( snd_uart16550_buffer_can_write( uart, 3 ) ) {
+ while (snd_rawmidi_transmit_peek(substream, &midi_byte, 1) == 1) {
+ /* Also send F5 after 3 seconds with no data
+ * to handle device disconnect */
+ if (first == 0 &&
+ (uart->adaptor == SNDRV_SERIAL_SOUNDCANVAS ||
+ uart->adaptor == SNDRV_SERIAL_GENERIC) &&
+ (uart->prev_out != substream->number ||
+ jiffies-lasttime > 3*HZ)) {
+
+ if (snd_uart16550_buffer_can_write(uart, 3)) {
/* Roland Soundcanvas part selection */
- /* If this substream of the data is different previous
- substream in this uart, send the change part event */
+ /* If this substream of the data is
+ * different previous substream
+ * in this uart, send the change part
+ * event
+ */
uart->prev_out = substream->number;
/* change part */
- snd_uart16550_output_byte(uart, substream, 0xf5);
+ snd_uart16550_output_byte(uart, substream,
+ 0xf5);
/* data */
- snd_uart16550_output_byte(uart, substream, uart->prev_out + 1);
- /* If midi_byte is a data byte, send the previous status byte */
- if ((midi_byte < 0x80) && (uart->adaptor == SNDRV_SERIAL_SOUNDCANVAS))
+ snd_uart16550_output_byte(uart, substream,
+ uart->prev_out + 1);
+ /* If midi_byte is a data byte,
+ * send the previous status byte */
+ if (midi_byte < 0x80 &&
+ uart->adaptor == SNDRV_SERIAL_SOUNDCANVAS)
snd_uart16550_output_byte(uart, substream, uart->prev_status[uart->prev_out]);
- } else if( !uart->drop_on_full )
+ } else if (!uart->drop_on_full)
break;
}
/* send midi byte */
- if( !snd_uart16550_output_byte(uart, substream, midi_byte) && !uart->drop_on_full )
+ if (!snd_uart16550_output_byte(uart, substream, midi_byte) &&
+ !uart->drop_on_full )
break;
if (midi_byte >= 0x80 && midi_byte < 0xf0)
@@ -717,17 +736,17 @@ static void snd_uart16550_output_write(struct snd_rawmidi_substream *substream)
spin_unlock_irqrestore(&uart->open_lock, flags);
}
-static void snd_uart16550_output_trigger(struct snd_rawmidi_substream *substream, int up)
+static void snd_uart16550_output_trigger(struct snd_rawmidi_substream *substream,
+ int up)
{
unsigned long flags;
- snd_uart16550_t *uart = substream->rmidi->private_data;
+ struct snd_uart16550 *uart = substream->rmidi->private_data;
spin_lock_irqsave(&uart->open_lock, flags);
- if (up) {
+ if (up)
uart->filemode |= SERIAL_MODE_OUTPUT_TRIGGERED;
- } else {
+ else
uart->filemode &= ~SERIAL_MODE_OUTPUT_TRIGGERED;
- }
spin_unlock_irqrestore(&uart->open_lock, flags);
if (up)
snd_uart16550_output_write(substream);
@@ -747,10 +766,10 @@ static struct snd_rawmidi_ops snd_uart16550_input =
.trigger = snd_uart16550_input_trigger,
};
-static int snd_uart16550_free(snd_uart16550_t *uart)
+static int snd_uart16550_free(struct snd_uart16550 *uart)
{
if (uart->irq >= 0)
- free_irq(uart->irq, (void *)uart);
+ free_irq(uart->irq, uart);
release_and_free_resource(uart->res_base);
kfree(uart);
return 0;
@@ -758,7 +777,7 @@ static int snd_uart16550_free(snd_uart16550_t *uart)
static int snd_uart16550_dev_free(struct snd_device *device)
{
- snd_uart16550_t *uart = device->device_data;
+ struct snd_uart16550 *uart = device->device_data;
return snd_uart16550_free(uart);
}
@@ -769,12 +788,12 @@ static int __init snd_uart16550_create(struct snd_card *card,
unsigned int base,
int adaptor,
int droponfull,
- snd_uart16550_t **ruart)
+ struct snd_uart16550 **ruart)
{
static struct snd_device_ops ops = {
.dev_free = snd_uart16550_dev_free,
};
- snd_uart16550_t *uart;
+ struct snd_uart16550 *uart;
int err;
@@ -795,7 +814,7 @@ static int __init snd_uart16550_create(struct snd_card *card,
if (irq >= 0 && irq != SNDRV_AUTO_IRQ) {
if (request_irq(irq, snd_uart16550_interrupt,
- IRQF_DISABLED, "Serial MIDI", (void *) uart)) {
+ IRQF_DISABLED, "Serial MIDI", uart)) {
snd_printk("irq %d busy. Using Polling.\n", irq);
} else {
uart->irq = irq;
@@ -843,23 +862,28 @@ static int __init snd_uart16550_create(struct snd_card *card,
static void __init snd_uart16550_substreams(struct snd_rawmidi_str *stream)
{
- struct list_head *list;
+ struct snd_rawmidi_substream *substream;
- list_for_each(list, &stream->substreams) {
- struct snd_rawmidi_substream *substream = list_entry(list, struct snd_rawmidi_substream, list);
+ list_for_each_entry(substream, &stream->substreams, list) {
sprintf(substream->name, "Serial MIDI %d", substream->number + 1);
}
}
-static int __init snd_uart16550_rmidi(snd_uart16550_t *uart, int device, int outs, int ins, struct snd_rawmidi **rmidi)
+static int __init snd_uart16550_rmidi(struct snd_uart16550 *uart, int device,
+ int outs, int ins,
+ struct snd_rawmidi **rmidi)
{
struct snd_rawmidi *rrawmidi;
int err;
- if ((err = snd_rawmidi_new(uart->card, "UART Serial MIDI", device, outs, ins, &rrawmidi)) < 0)
+ err = snd_rawmidi_new(uart->card, "UART Serial MIDI", device,
+ outs, ins, &rrawmidi);
+ if (err < 0)
return err;
- snd_rawmidi_set_ops(rrawmidi, SNDRV_RAWMIDI_STREAM_INPUT, &snd_uart16550_input);
- snd_rawmidi_set_ops(rrawmidi, SNDRV_RAWMIDI_STREAM_OUTPUT, &snd_uart16550_output);
+ snd_rawmidi_set_ops(rrawmidi, SNDRV_RAWMIDI_STREAM_INPUT,
+ &snd_uart16550_input);
+ snd_rawmidi_set_ops(rrawmidi, SNDRV_RAWMIDI_STREAM_OUTPUT,
+ &snd_uart16550_output);
strcpy(rrawmidi->name, "Serial MIDI");
snd_uart16550_substreams(&rrawmidi->streams[SNDRV_RAWMIDI_STREAM_OUTPUT]);
snd_uart16550_substreams(&rrawmidi->streams[SNDRV_RAWMIDI_STREAM_INPUT]);
@@ -875,7 +899,7 @@ static int __init snd_uart16550_rmidi(snd_uart16550_t *uart, int device, int out
static int __init snd_serial_probe(struct platform_device *devptr)
{
struct snd_card *card;
- snd_uart16550_t *uart;
+ struct snd_uart16550 *uart;
int err;
int dev = devptr->id;
@@ -929,7 +953,8 @@ static int __init snd_serial_probe(struct platform_device *devptr)
&uart)) < 0)
goto _err;
- if ((err = snd_uart16550_rmidi(uart, 0, outs[dev], ins[dev], &uart->rmidi)) < 0)
+ err = snd_uart16550_rmidi(uart, 0, outs[dev], ins[dev], &uart->rmidi);
+ if (err < 0)
goto _err;
sprintf(card->longname, "%s at 0x%lx, irq %d speed %d div %d outs %d ins %d adaptor %s droponfull %d",
diff --git a/sound/drivers/vx/vx_mixer.c b/sound/drivers/vx/vx_mixer.c
index 1613ed844ac6..f63152a6a223 100644
--- a/sound/drivers/vx/vx_mixer.c
+++ b/sound/drivers/vx/vx_mixer.c
@@ -716,7 +716,7 @@ static int vx_monitor_sw_put(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_
return 0;
}
-static DECLARE_TLV_DB_SCALE(db_scale_audio_gain, -10975, 25, 0);
+static const DECLARE_TLV_DB_SCALE(db_scale_audio_gain, -10975, 25, 0);
static struct snd_kcontrol_new vx_control_audio_gain = {
.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
diff --git a/sound/i2c/Makefile b/sound/i2c/Makefile
index 816a2e7c88ca..45902d48c89c 100644
--- a/sound/i2c/Makefile
+++ b/sound/i2c/Makefile
@@ -16,3 +16,4 @@ obj-$(CONFIG_SND) += other/
# Toplevel Module Dependency
obj-$(CONFIG_SND_INTERWAVE_STB) += snd-tea6330t.o snd-i2c.o
obj-$(CONFIG_SND_ICE1712) += snd-cs8427.o snd-i2c.o
+obj-$(CONFIG_SND_ICE1724) += snd-i2c.o
diff --git a/sound/i2c/other/Makefile b/sound/i2c/other/Makefile
index 2fe023ef00a7..77a8a7c75dd9 100644
--- a/sound/i2c/other/Makefile
+++ b/sound/i2c/other/Makefile
@@ -6,11 +6,11 @@
snd-ak4114-objs := ak4114.o
snd-ak4117-objs := ak4117.o
snd-ak4xxx-adda-objs := ak4xxx-adda.o
+snd-pt2258-objs := pt2258.o
snd-tea575x-tuner-objs := tea575x-tuner.o
# Module Dependency
obj-$(CONFIG_SND_PDAUDIOCF) += snd-ak4117.o
obj-$(CONFIG_SND_ICE1712) += snd-ak4xxx-adda.o
-obj-$(CONFIG_SND_ICE1724) += snd-ak4xxx-adda.o
-obj-$(CONFIG_SND_ICE1724) += snd-ak4114.o
+obj-$(CONFIG_SND_ICE1724) += snd-ak4114.o snd-ak4xxx-adda.o snd-pt2258.o
obj-$(CONFIG_SND_FM801_TEA575X) += snd-tea575x-tuner.o
diff --git a/sound/i2c/other/ak4114.c b/sound/i2c/other/ak4114.c
index d2f2c5078e65..adbfd5884d06 100644
--- a/sound/i2c/other/ak4114.c
+++ b/sound/i2c/other/ak4114.c
@@ -42,8 +42,8 @@ static void reg_write(struct ak4114 *ak4114, unsigned char reg, unsigned char va
ak4114->write(ak4114->private_data, reg, val);
if (reg <= AK4114_REG_INT1_MASK)
ak4114->regmap[reg] = val;
- else if (reg >= AK4114_REG_RXCSB0 && reg <= AK4114_REG_TXCSB4)
- ak4114->txcsb[reg-AK4114_REG_RXCSB0] = val;
+ else if (reg >= AK4114_REG_TXCSB0 && reg <= AK4114_REG_TXCSB4)
+ ak4114->txcsb[reg-AK4114_REG_TXCSB0] = val;
}
static inline unsigned char reg_read(struct ak4114 *ak4114, unsigned char reg)
@@ -66,10 +66,8 @@ static void snd_ak4114_free(struct ak4114 *chip)
{
chip->init = 1; /* don't schedule new work */
mb();
- if (chip->workqueue != NULL) {
- flush_workqueue(chip->workqueue);
- destroy_workqueue(chip->workqueue);
- }
+ cancel_delayed_work(&chip->work);
+ flush_scheduled_work();
kfree(chip);
}
@@ -82,7 +80,7 @@ static int snd_ak4114_dev_free(struct snd_device *device)
int snd_ak4114_create(struct snd_card *card,
ak4114_read_t *read, ak4114_write_t *write,
- unsigned char pgm[7], unsigned char txcsb[5],
+ const unsigned char pgm[7], const unsigned char txcsb[5],
void *private_data, struct ak4114 **r_ak4114)
{
struct ak4114 *chip;
@@ -100,18 +98,13 @@ int snd_ak4114_create(struct snd_card *card,
chip->read = read;
chip->write = write;
chip->private_data = private_data;
+ INIT_DELAYED_WORK(&chip->work, ak4114_stats);
for (reg = 0; reg < 7; reg++)
chip->regmap[reg] = pgm[reg];
for (reg = 0; reg < 5; reg++)
chip->txcsb[reg] = txcsb[reg];
- chip->workqueue = create_workqueue("snd-ak4114");
- if (chip->workqueue == NULL) {
- kfree(chip);
- return -ENOMEM;
- }
-
snd_ak4114_reinit(chip);
chip->rcs0 = reg_read(chip, AK4114_REG_RCS0) & ~(AK4114_QINT | AK4114_CINT);
@@ -134,7 +127,8 @@ void snd_ak4114_reg_write(struct ak4114 *chip, unsigned char reg, unsigned char
if (reg <= AK4114_REG_INT1_MASK)
reg_write(chip, reg, (chip->regmap[reg] & ~mask) | val);
else if (reg >= AK4114_REG_TXCSB0 && reg <= AK4114_REG_TXCSB4)
- reg_write(chip, reg, (chip->txcsb[reg] & ~mask) | val);
+ reg_write(chip, reg,
+ (chip->txcsb[reg-AK4114_REG_TXCSB0] & ~mask) | val);
}
void snd_ak4114_reinit(struct ak4114 *chip)
@@ -143,7 +137,7 @@ void snd_ak4114_reinit(struct ak4114 *chip)
chip->init = 1;
mb();
- flush_workqueue(chip->workqueue);
+ flush_scheduled_work();
/* bring the chip to reset state and powerdown state */
reg_write(chip, AK4114_REG_PWRDN, old & ~(AK4114_RST|AK4114_PWN));
udelay(200);
@@ -158,8 +152,7 @@ void snd_ak4114_reinit(struct ak4114 *chip)
reg_write(chip, AK4114_REG_PWRDN, old | AK4114_RST | AK4114_PWN);
/* bring up statistics / event queing */
chip->init = 0;
- INIT_DELAYED_WORK(&chip->work, ak4114_stats);
- queue_delayed_work(chip->workqueue, &chip->work, HZ / 10);
+ schedule_delayed_work(&chip->work, HZ / 10);
}
static unsigned int external_rate(unsigned char rcs1)
@@ -568,7 +561,7 @@ static void ak4114_stats(struct work_struct *work)
if (chip->init)
return;
snd_ak4114_check_rate_and_errors(chip, 0);
- queue_delayed_work(chip->workqueue, &chip->work, HZ / 10);
+ schedule_delayed_work(&chip->work, HZ / 10);
}
EXPORT_SYMBOL(snd_ak4114_create);
diff --git a/sound/i2c/other/ak4117.c b/sound/i2c/other/ak4117.c
index 4e45952dd95a..c022f29da2f7 100644
--- a/sound/i2c/other/ak4117.c
+++ b/sound/i2c/other/ak4117.c
@@ -74,7 +74,7 @@ static int snd_ak4117_dev_free(struct snd_device *device)
}
int snd_ak4117_create(struct snd_card *card, ak4117_read_t *read, ak4117_write_t *write,
- unsigned char pgm[5], void *private_data, struct ak4117 **r_ak4117)
+ const unsigned char pgm[5], void *private_data, struct ak4117 **r_ak4117)
{
struct ak4117 *chip;
int err = 0;
diff --git a/sound/i2c/other/ak4xxx-adda.c b/sound/i2c/other/ak4xxx-adda.c
index 5da49e2eb350..8805110017a7 100644
--- a/sound/i2c/other/ak4xxx-adda.c
+++ b/sound/i2c/other/ak4xxx-adda.c
@@ -140,7 +140,7 @@ EXPORT_SYMBOL(snd_akm4xxx_reset);
* Used for AK4524 input/ouput attenuation, AK4528, and
* AK5365 input attenuation
*/
-static unsigned char vol_cvt_datt[128] = {
+static const unsigned char vol_cvt_datt[128] = {
0x00, 0x01, 0x01, 0x02, 0x02, 0x03, 0x03, 0x04,
0x04, 0x04, 0x04, 0x05, 0x05, 0x05, 0x06, 0x06,
0x06, 0x07, 0x07, 0x08, 0x08, 0x08, 0x09, 0x0a,
@@ -162,17 +162,17 @@ static unsigned char vol_cvt_datt[128] = {
/*
* dB tables
*/
-static DECLARE_TLV_DB_SCALE(db_scale_vol_datt, -6350, 50, 1);
-static DECLARE_TLV_DB_SCALE(db_scale_8bit, -12750, 50, 1);
-static DECLARE_TLV_DB_SCALE(db_scale_7bit, -6350, 50, 1);
-static DECLARE_TLV_DB_LINEAR(db_scale_linear, TLV_DB_GAIN_MUTE, 0);
+static const DECLARE_TLV_DB_SCALE(db_scale_vol_datt, -6350, 50, 1);
+static const DECLARE_TLV_DB_SCALE(db_scale_8bit, -12750, 50, 1);
+static const DECLARE_TLV_DB_SCALE(db_scale_7bit, -6350, 50, 1);
+static const DECLARE_TLV_DB_LINEAR(db_scale_linear, TLV_DB_GAIN_MUTE, 0);
/*
* initialize all the ak4xxx chips
*/
void snd_akm4xxx_init(struct snd_akm4xxx *ak)
{
- static unsigned char inits_ak4524[] = {
+ static const unsigned char inits_ak4524[] = {
0x00, 0x07, /* 0: all power up */
0x01, 0x00, /* 1: ADC/DAC reset */
0x02, 0x60, /* 2: 24bit I2S */
@@ -184,7 +184,7 @@ void snd_akm4xxx_init(struct snd_akm4xxx *ak)
0x07, 0x00, /* 7: DAC right muted */
0xff, 0xff
};
- static unsigned char inits_ak4528[] = {
+ static const unsigned char inits_ak4528[] = {
0x00, 0x07, /* 0: all power up */
0x01, 0x00, /* 1: ADC/DAC reset */
0x02, 0x60, /* 2: 24bit I2S */
@@ -194,7 +194,7 @@ void snd_akm4xxx_init(struct snd_akm4xxx *ak)
0x05, 0x00, /* 5: ADC right muted */
0xff, 0xff
};
- static unsigned char inits_ak4529[] = {
+ static const unsigned char inits_ak4529[] = {
0x09, 0x01, /* 9: ATS=0, RSTN=1 */
0x0a, 0x3f, /* A: all power up, no zero/overflow detection */
0x00, 0x0c, /* 0: TDM=0, 24bit I2S, SMUTE=0 */
@@ -210,7 +210,7 @@ void snd_akm4xxx_init(struct snd_akm4xxx *ak)
0x08, 0x55, /* 8: deemphasis all off */
0xff, 0xff
};
- static unsigned char inits_ak4355[] = {
+ static const unsigned char inits_ak4355[] = {
0x01, 0x02, /* 1: reset and soft-mute */
0x00, 0x06, /* 0: mode3(i2s), disable auto-clock detect,
* disable DZF, sharp roll-off, RSTN#=0 */
@@ -227,7 +227,7 @@ void snd_akm4xxx_init(struct snd_akm4xxx *ak)
0x01, 0x01, /* 1: un-reset, unmute */
0xff, 0xff
};
- static unsigned char inits_ak4358[] = {
+ static const unsigned char inits_ak4358[] = {
0x01, 0x02, /* 1: reset and soft-mute */
0x00, 0x06, /* 0: mode3(i2s), disable auto-clock detect,
* disable DZF, sharp roll-off, RSTN#=0 */
@@ -246,7 +246,7 @@ void snd_akm4xxx_init(struct snd_akm4xxx *ak)
0x01, 0x01, /* 1: un-reset, unmute */
0xff, 0xff
};
- static unsigned char inits_ak4381[] = {
+ static const unsigned char inits_ak4381[] = {
0x00, 0x0c, /* 0: mode3(i2s), disable auto-clock detect */
0x01, 0x02, /* 1: de-emphasis off, normal speed,
* sharp roll-off, DZF off */
@@ -259,7 +259,8 @@ void snd_akm4xxx_init(struct snd_akm4xxx *ak)
};
int chip, num_chips;
- unsigned char *ptr, reg, data, *inits;
+ const unsigned char *ptr, *inits;
+ unsigned char reg, data;
memset(ak->images, 0, sizeof(ak->images));
memset(ak->volumes, 0, sizeof(ak->volumes));
@@ -513,6 +514,66 @@ static int ak4xxx_switch_put(struct snd_kcontrol *kcontrol,
return change;
}
+#define AK5365_NUM_INPUTS 5
+
+static int ak4xxx_capture_source_info(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_info *uinfo)
+{
+ struct snd_akm4xxx *ak = snd_kcontrol_chip(kcontrol);
+ int mixer_ch = AK_GET_SHIFT(kcontrol->private_value);
+ const char **input_names;
+ int num_names, idx;
+
+ input_names = ak->adc_info[mixer_ch].input_names;
+
+ num_names = 0;
+ while (num_names < AK5365_NUM_INPUTS && input_names[num_names])
+ ++num_names;
+
+ uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED;
+ uinfo->count = 1;
+ uinfo->value.enumerated.items = num_names;
+ idx = uinfo->value.enumerated.item;
+ if (idx >= num_names)
+ return -EINVAL;
+ strncpy(uinfo->value.enumerated.name, input_names[idx],
+ sizeof(uinfo->value.enumerated.name));
+ return 0;
+}
+
+static int ak4xxx_capture_source_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_akm4xxx *ak = snd_kcontrol_chip(kcontrol);
+ int chip = AK_GET_CHIP(kcontrol->private_value);
+ int addr = AK_GET_ADDR(kcontrol->private_value);
+ int mask = AK_GET_MASK(kcontrol->private_value);
+ unsigned char val;
+
+ val = snd_akm4xxx_get(ak, chip, addr) & mask;
+ ucontrol->value.enumerated.item[0] = val;
+ return 0;
+}
+
+static int ak4xxx_capture_source_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_akm4xxx *ak = snd_kcontrol_chip(kcontrol);
+ int chip = AK_GET_CHIP(kcontrol->private_value);
+ int addr = AK_GET_ADDR(kcontrol->private_value);
+ int mask = AK_GET_MASK(kcontrol->private_value);
+ unsigned char oval, val;
+
+ oval = snd_akm4xxx_get(ak, chip, addr);
+ val = oval & ~mask;
+ val |= ucontrol->value.enumerated.item[0] & mask;
+ if (val != oval) {
+ snd_akm4xxx_write(ak, chip, addr, val);
+ return 1;
+ }
+ return 0;
+}
+
/*
* build AK4xxx controls
*/
@@ -647,9 +708,10 @@ static int build_adc_controls(struct snd_akm4xxx *ak)
if (ak->type == SND_AK5365 && (idx % 2) == 0) {
if (! ak->adc_info ||
- ! ak->adc_info[mixer_ch].switch_name)
+ ! ak->adc_info[mixer_ch].switch_name) {
knew.name = "Capture Switch";
- else
+ knew.index = mixer_ch + ak->idx_offset * 2;
+ } else
knew.name = ak->adc_info[mixer_ch].switch_name;
knew.info = ak4xxx_switch_info;
knew.get = ak4xxx_switch_get;
@@ -662,6 +724,26 @@ static int build_adc_controls(struct snd_akm4xxx *ak)
err = snd_ctl_add(ak->card, snd_ctl_new1(&knew, ak));
if (err < 0)
return err;
+
+ memset(&knew, 0, sizeof(knew));
+ knew.name = ak->adc_info[mixer_ch].selector_name;
+ if (!knew.name) {
+ knew.name = "Capture Channel";
+ knew.index = mixer_ch + ak->idx_offset * 2;
+ }
+
+ knew.iface = SNDRV_CTL_ELEM_IFACE_MIXER;
+ knew.info = ak4xxx_capture_source_info;
+ knew.get = ak4xxx_capture_source_get;
+ knew.put = ak4xxx_capture_source_put;
+ knew.access = 0;
+ /* input selector control: reg. 1, bits 0-2.
+ * mis-use 'shift' to pass mixer_ch */
+ knew.private_value
+ = AK_COMPOSE(idx/2, 1, mixer_ch, 0x07);
+ err = snd_ctl_add(ak->card, snd_ctl_new1(&knew, ak));
+ if (err < 0)
+ return err;
}
idx += num_stereo;
diff --git a/sound/i2c/other/pt2258.c b/sound/i2c/other/pt2258.c
new file mode 100644
index 000000000000..e91cc3b44de5
--- /dev/null
+++ b/sound/i2c/other/pt2258.c
@@ -0,0 +1,233 @@
+/*
+ * ALSA Driver for the PT2258 volume controller.
+ *
+ * Copyright (c) 2006 Jochen Voss <voss@seehuhn.de>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ */
+
+#include <sound/driver.h>
+#include <sound/core.h>
+#include <sound/control.h>
+#include <sound/tlv.h>
+#include <sound/i2c.h>
+#include <sound/pt2258.h>
+
+MODULE_AUTHOR("Jochen Voss <voss@seehuhn.de>");
+MODULE_DESCRIPTION("PT2258 volume controller (Princeton Technology Corp.)");
+MODULE_LICENSE("GPL");
+
+#define PT2258_CMD_RESET 0xc0
+#define PT2258_CMD_UNMUTE 0xf8
+#define PT2258_CMD_MUTE 0xf9
+
+static const unsigned char pt2258_channel_code[12] = {
+ 0x80, 0x90, /* channel 1: -10dB, -1dB */
+ 0x40, 0x50, /* channel 2: -10dB, -1dB */
+ 0x00, 0x10, /* channel 3: -10dB, -1dB */
+ 0x20, 0x30, /* channel 4: -10dB, -1dB */
+ 0x60, 0x70, /* channel 5: -10dB, -1dB */
+ 0xa0, 0xb0 /* channel 6: -10dB, -1dB */
+};
+
+int snd_pt2258_reset(struct snd_pt2258 *pt)
+{
+ unsigned char bytes[2];
+ int i;
+
+ /* reset chip */
+ bytes[0] = PT2258_CMD_RESET;
+ snd_i2c_lock(pt->i2c_bus);
+ if (snd_i2c_sendbytes(pt->i2c_dev, bytes, 1) != 1)
+ goto __error;
+ snd_i2c_unlock(pt->i2c_bus);
+
+ /* mute all channels */
+ pt->mute = 1;
+ bytes[0] = PT2258_CMD_MUTE;
+ snd_i2c_lock(pt->i2c_bus);
+ if (snd_i2c_sendbytes(pt->i2c_dev, bytes, 1) != 1)
+ goto __error;
+ snd_i2c_unlock(pt->i2c_bus);
+
+ /* set all channels to 0dB */
+ for (i = 0; i < 6; ++i)
+ pt->volume[i] = 0;
+ bytes[0] = 0xd0;
+ bytes[1] = 0xe0;
+ snd_i2c_lock(pt->i2c_bus);
+ if (snd_i2c_sendbytes(pt->i2c_dev, bytes, 2) != 2)
+ goto __error;
+ snd_i2c_unlock(pt->i2c_bus);
+
+ return 0;
+
+ __error:
+ snd_i2c_unlock(pt->i2c_bus);
+ snd_printk(KERN_ERR "PT2258 reset failed\n");
+ return -EIO;
+}
+
+static int pt2258_stereo_volume_info(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_info *uinfo)
+{
+ uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
+ uinfo->count = 2;
+ uinfo->value.integer.min = 0;
+ uinfo->value.integer.max = 79;
+ return 0;
+}
+
+static int pt2258_stereo_volume_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_pt2258 *pt = kcontrol->private_data;
+ int base = kcontrol->private_value;
+
+ /* chip does not support register reads */
+ ucontrol->value.integer.value[0] = 79 - pt->volume[base];
+ ucontrol->value.integer.value[1] = 79 - pt->volume[base + 1];
+ return 0;
+}
+
+static int pt2258_stereo_volume_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_pt2258 *pt = kcontrol->private_data;
+ int base = kcontrol->private_value;
+ unsigned char bytes[2];
+ int val0, val1;
+
+ val0 = 79 - ucontrol->value.integer.value[0];
+ val1 = 79 - ucontrol->value.integer.value[1];
+ if (val0 == pt->volume[base] && val1 == pt->volume[base + 1])
+ return 0;
+
+ pt->volume[base] = val0;
+ bytes[0] = pt2258_channel_code[2 * base] | (val0 / 10);
+ bytes[1] = pt2258_channel_code[2 * base + 1] | (val0 % 10);
+ snd_i2c_lock(pt->i2c_bus);
+ if (snd_i2c_sendbytes(pt->i2c_dev, bytes, 2) != 2)
+ goto __error;
+ snd_i2c_unlock(pt->i2c_bus);
+
+ pt->volume[base + 1] = val1;
+ bytes[0] = pt2258_channel_code[2 * base + 2] | (val1 / 10);
+ bytes[1] = pt2258_channel_code[2 * base + 3] | (val1 % 10);
+ snd_i2c_lock(pt->i2c_bus);
+ if (snd_i2c_sendbytes(pt->i2c_dev, bytes, 2) != 2)
+ goto __error;
+ snd_i2c_unlock(pt->i2c_bus);
+
+ return 1;
+
+ __error:
+ snd_i2c_unlock(pt->i2c_bus);
+ snd_printk(KERN_ERR "PT2258 access failed\n");
+ return -EIO;
+}
+
+static int pt2258_switch_info(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_info *uinfo)
+{
+ uinfo->type = SNDRV_CTL_ELEM_TYPE_BOOLEAN;
+ uinfo->count = 1;
+ uinfo->value.integer.min = 0;
+ uinfo->value.integer.max = 1;
+ return 0;
+}
+
+static int pt2258_switch_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_pt2258 *pt = kcontrol->private_data;
+
+ ucontrol->value.integer.value[0] = !pt->mute;
+ return 0;
+}
+
+static int pt2258_switch_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_pt2258 *pt = kcontrol->private_data;
+ unsigned char bytes[2];
+ int val;
+
+ val = !ucontrol->value.integer.value[0];
+ if (pt->mute == val)
+ return 0;
+
+ pt->mute = val;
+ bytes[0] = val ? PT2258_CMD_MUTE : PT2258_CMD_UNMUTE;
+ snd_i2c_lock(pt->i2c_bus);
+ if (snd_i2c_sendbytes(pt->i2c_dev, bytes, 1) != 1)
+ goto __error;
+ snd_i2c_unlock(pt->i2c_bus);
+
+ return 1;
+
+ __error:
+ snd_i2c_unlock(pt->i2c_bus);
+ snd_printk(KERN_ERR "PT2258 access failed 2\n");
+ return -EIO;
+}
+
+static const DECLARE_TLV_DB_SCALE(pt2258_db_scale, -7900, 100, 0);
+
+int snd_pt2258_build_controls(struct snd_pt2258 *pt)
+{
+ struct snd_kcontrol_new knew;
+ char *names[3] = {
+ "Mic Loopback Playback Volume",
+ "Line Loopback Playback Volume",
+ "CD Loopback Playback Volume"
+ };
+ int i, err;
+
+ for (i = 0; i < 3; ++i) {
+ memset(&knew, 0, sizeof(knew));
+ knew.name = names[i];
+ knew.iface = SNDRV_CTL_ELEM_IFACE_MIXER;
+ knew.count = 1;
+ knew.access = SNDRV_CTL_ELEM_ACCESS_READWRITE |
+ SNDRV_CTL_ELEM_ACCESS_TLV_READ;
+ knew.private_value = 2 * i;
+ knew.info = pt2258_stereo_volume_info;
+ knew.get = pt2258_stereo_volume_get;
+ knew.put = pt2258_stereo_volume_put;
+ knew.tlv.p = pt2258_db_scale;
+
+ err = snd_ctl_add(pt->card, snd_ctl_new1(&knew, pt));
+ if (err < 0)
+ return err;
+ }
+
+ memset(&knew, 0, sizeof(knew));
+ knew.name = "Loopback Switch";
+ knew.iface = SNDRV_CTL_ELEM_IFACE_MIXER;
+ knew.info = pt2258_switch_info;
+ knew.get = pt2258_switch_get;
+ knew.put = pt2258_switch_put;
+ knew.access = 0;
+ err = snd_ctl_add(pt->card, snd_ctl_new1(&knew, pt));
+ if (err < 0)
+ return err;
+
+ return 0;
+}
+
+EXPORT_SYMBOL(snd_pt2258_reset);
+EXPORT_SYMBOL(snd_pt2258_build_controls);
diff --git a/sound/isa/Kconfig b/sound/isa/Kconfig
index 57371f1a441f..4e3a9729f569 100644
--- a/sound/isa/Kconfig
+++ b/sound/isa/Kconfig
@@ -358,6 +358,7 @@ config SND_SBAWE
config SND_SB16_CSP
bool "Sound Blaster 16/AWE CSP support"
depends on (SND_SB16 || SND_SBAWE) && (BROKEN || !PPC)
+ select FW_LOADER
help
Say Y here to include support for the CSP core. This special
coprocessor can do variable tasks like various compression and
@@ -390,6 +391,7 @@ config SND_SSCAPE
config SND_WAVEFRONT
tristate "Turtle Beach Maui,Tropez,Tropez+ (Wavefront)"
depends on SND
+ select FW_LOADER
select SND_OPL3_LIB
select SND_MPU401_UART
select SND_CS4231_LIB
diff --git a/sound/isa/ad1816a/ad1816a_lib.c b/sound/isa/ad1816a/ad1816a_lib.c
index b524e0d9ee44..ec9209cd5177 100644
--- a/sound/isa/ad1816a/ad1816a_lib.c
+++ b/sound/isa/ad1816a/ad1816a_lib.c
@@ -906,11 +906,11 @@ static int snd_ad1816a_put_double(struct snd_kcontrol *kcontrol, struct snd_ctl_
return change;
}
-static DECLARE_TLV_DB_SCALE(db_scale_4bit, -4500, 300, 0);
-static DECLARE_TLV_DB_SCALE(db_scale_5bit, -4650, 150, 0);
-static DECLARE_TLV_DB_SCALE(db_scale_6bit, -9450, 150, 0);
-static DECLARE_TLV_DB_SCALE(db_scale_5bit_12db_max, -3450, 150, 0);
-static DECLARE_TLV_DB_SCALE(db_scale_rec_gain, 0, 150, 0);
+static const DECLARE_TLV_DB_SCALE(db_scale_4bit, -4500, 300, 0);
+static const DECLARE_TLV_DB_SCALE(db_scale_5bit, -4650, 150, 0);
+static const DECLARE_TLV_DB_SCALE(db_scale_6bit, -9450, 150, 0);
+static const DECLARE_TLV_DB_SCALE(db_scale_5bit_12db_max, -3450, 150, 0);
+static const DECLARE_TLV_DB_SCALE(db_scale_rec_gain, 0, 150, 0);
static struct snd_kcontrol_new snd_ad1816a_controls[] __devinitdata = {
AD1816A_DOUBLE("Master Playback Switch", AD1816A_MASTER_ATT, 15, 7, 1, 1),
diff --git a/sound/isa/ad1848/ad1848_lib.c b/sound/isa/ad1848/ad1848_lib.c
index 666b3bcc19f0..8094282c2ae1 100644
--- a/sound/isa/ad1848/ad1848_lib.c
+++ b/sound/isa/ad1848/ad1848_lib.c
@@ -1223,9 +1223,9 @@ int snd_ad1848_add_ctl_elem(struct snd_ad1848 *chip,
EXPORT_SYMBOL(snd_ad1848_add_ctl_elem);
-static DECLARE_TLV_DB_SCALE(db_scale_6bit, -9450, 150, 0);
-static DECLARE_TLV_DB_SCALE(db_scale_5bit_12db_max, -3450, 150, 0);
-static DECLARE_TLV_DB_SCALE(db_scale_rec_gain, 0, 150, 0);
+static const DECLARE_TLV_DB_SCALE(db_scale_6bit, -9450, 150, 0);
+static const DECLARE_TLV_DB_SCALE(db_scale_5bit_12db_max, -3450, 150, 0);
+static const DECLARE_TLV_DB_SCALE(db_scale_rec_gain, 0, 150, 0);
static struct ad1848_mix_elem snd_ad1848_controls[] = {
AD1848_DOUBLE("PCM Playback Switch", 0, AD1848_LEFT_OUTPUT, AD1848_RIGHT_OUTPUT, 7, 7, 1, 1),
diff --git a/sound/isa/gus/gus_main.c b/sound/isa/gus/gus_main.c
index b680fddf0d74..8ced5e81b9a7 100644
--- a/sound/isa/gus/gus_main.c
+++ b/sound/isa/gus/gus_main.c
@@ -294,10 +294,10 @@ static int snd_gus_init_dma_irq(struct snd_gus_card * gus, int latches)
gus->mix_cntrl_reg |= 4; /* enable MIC */
}
dma1 = gus->gf1.dma1;
- dma1 = dma1 < 0 ? -dma1 : dma1;
+ dma1 = abs(dma1);
dma1 = dmas[dma1 & 7];
dma2 = gus->gf1.dma2;
- dma2 = dma2 < 0 ? -dma2 : dma2;
+ dma2 = abs(dma2);
dma2 = dmas[dma2 & 7];
dma1 |= gus->equal_dma ? 0x40 : (dma2 << 3);
@@ -306,7 +306,7 @@ static int snd_gus_init_dma_irq(struct snd_gus_card * gus, int latches)
return -EINVAL;
}
irq = gus->gf1.irq;
- irq = irq < 0 ? -irq : irq;
+ irq = abs(irq);
irq = irqs[irq & 0x0f];
if (irq == 0) {
snd_printk(KERN_ERR "Error! IRQ isn't defined.\n");
diff --git a/sound/isa/opl3sa2.c b/sound/isa/opl3sa2.c
index 419b4ebbf00e..1e30713d2cad 100644
--- a/sound/isa/opl3sa2.c
+++ b/sound/isa/opl3sa2.c
@@ -486,8 +486,8 @@ static int snd_opl3sa2_put_double(struct snd_kcontrol *kcontrol, struct snd_ctl_
return change;
}
-static DECLARE_TLV_DB_SCALE(db_scale_master, -3000, 200, 0);
-static DECLARE_TLV_DB_SCALE(db_scale_5bit_12db_max, -3450, 150, 0);
+static const DECLARE_TLV_DB_SCALE(db_scale_master, -3000, 200, 0);
+static const DECLARE_TLV_DB_SCALE(db_scale_5bit_12db_max, -3450, 150, 0);
static struct snd_kcontrol_new snd_opl3sa2_controls[] = {
OPL3SA2_DOUBLE("Master Playback Switch", 0, 0x07, 0x08, 7, 7, 1, 1),
diff --git a/sound/isa/sb/sb16_csp.c b/sound/isa/sb/sb16_csp.c
index fcd638090a9e..3d9d7e0107ca 100644
--- a/sound/isa/sb/sb16_csp.c
+++ b/sound/isa/sb/sb16_csp.c
@@ -161,10 +161,13 @@ int snd_sb_csp_new(struct snd_sb *chip, int device, struct snd_hwdep ** rhwdep)
*/
static void snd_sb_csp_free(struct snd_hwdep *hwdep)
{
+ int i;
struct snd_sb_csp *p = hwdep->private_data;
if (p) {
if (p->running & SNDRV_SB_CSP_ST_RUNNING)
snd_sb_csp_stop(p);
+ for (i = 0; i < ARRAY_SIZE(p->csp_programs); ++i)
+ release_firmware(p->csp_programs[i]);
kfree(p);
}
}
@@ -687,8 +690,50 @@ static int snd_sb_csp_load_user(struct snd_sb_csp * p, const unsigned char __use
return err;
}
+#define FIRMWARE_IN_THE_KERNEL
+
+#ifdef FIRMWARE_IN_THE_KERNEL
#include "sb16_csp_codecs.h"
+static const struct firmware snd_sb_csp_static_programs[] = {
+ { .data = mulaw_main, .size = sizeof mulaw_main },
+ { .data = alaw_main, .size = sizeof alaw_main },
+ { .data = ima_adpcm_init, .size = sizeof ima_adpcm_init },
+ { .data = ima_adpcm_playback, .size = sizeof ima_adpcm_playback },
+ { .data = ima_adpcm_capture, .size = sizeof ima_adpcm_capture },
+};
+#endif
+
+static int snd_sb_csp_firmware_load(struct snd_sb_csp *p, int index, int flags)
+{
+ static const char *const names[] = {
+ "sb16/mulaw_main.csp",
+ "sb16/alaw_main.csp",
+ "sb16/ima_adpcm_init.csp",
+ "sb16/ima_adpcm_playback.csp",
+ "sb16/ima_adpcm_capture.csp",
+ };
+ const struct firmware *program;
+ int err;
+
+ BUILD_BUG_ON(ARRAY_SIZE(names) != CSP_PROGRAM_COUNT);
+ program = p->csp_programs[index];
+ if (!program) {
+ err = request_firmware(&program, names[index],
+ p->chip->card->dev);
+ if (err >= 0)
+ p->csp_programs[index] = program;
+ else {
+#ifdef FIRMWARE_IN_THE_KERNEL
+ program = &snd_sb_csp_static_programs[index];
+#else
+ return err;
+#endif
+ }
+ }
+ return snd_sb_csp_load(p, program->data, program->size, flags);
+}
+
/*
* autoload hardware codec if necessary
* return 0 if CSP is loaded and ready to run (p->running != 0)
@@ -708,27 +753,27 @@ static int snd_sb_csp_autoload(struct snd_sb_csp * p, int pcm_sfmt, int play_rec
} else {
switch (pcm_sfmt) {
case SNDRV_PCM_FORMAT_MU_LAW:
- err = snd_sb_csp_load(p, &mulaw_main[0], sizeof(mulaw_main), 0);
+ err = snd_sb_csp_firmware_load(p, CSP_PROGRAM_MULAW, 0);
p->acc_format = SNDRV_PCM_FMTBIT_MU_LAW;
p->mode = SNDRV_SB_CSP_MODE_DSP_READ | SNDRV_SB_CSP_MODE_DSP_WRITE;
break;
case SNDRV_PCM_FORMAT_A_LAW:
- err = snd_sb_csp_load(p, &alaw_main[0], sizeof(alaw_main), 0);
+ err = snd_sb_csp_firmware_load(p, CSP_PROGRAM_ALAW, 0);
p->acc_format = SNDRV_PCM_FMTBIT_A_LAW;
p->mode = SNDRV_SB_CSP_MODE_DSP_READ | SNDRV_SB_CSP_MODE_DSP_WRITE;
break;
case SNDRV_PCM_FORMAT_IMA_ADPCM:
- err = snd_sb_csp_load(p, &ima_adpcm_init[0], sizeof(ima_adpcm_init),
- SNDRV_SB_CSP_LOAD_INITBLOCK);
+ err = snd_sb_csp_firmware_load(p, CSP_PROGRAM_ADPCM_INIT,
+ SNDRV_SB_CSP_LOAD_INITBLOCK);
if (err)
break;
if (play_rec_mode == SNDRV_SB_CSP_MODE_DSP_WRITE) {
- err = snd_sb_csp_load(p, &ima_adpcm_playback[0],
- sizeof(ima_adpcm_playback), 0);
+ err = snd_sb_csp_firmware_load
+ (p, CSP_PROGRAM_ADPCM_PLAYBACK, 0);
p->mode = SNDRV_SB_CSP_MODE_DSP_WRITE;
} else {
- err = snd_sb_csp_load(p, &ima_adpcm_capture[0],
- sizeof(ima_adpcm_capture), 0);
+ err = snd_sb_csp_firmware_load
+ (p, CSP_PROGRAM_ADPCM_CAPTURE, 0);
p->mode = SNDRV_SB_CSP_MODE_DSP_READ;
}
p->acc_format = SNDRV_PCM_FMTBIT_IMA_ADPCM;
diff --git a/sound/isa/wavefront/wavefront.c b/sound/isa/wavefront/wavefront.c
index 85db535aea9b..e2fdd5fd39d0 100644
--- a/sound/isa/wavefront/wavefront.c
+++ b/sound/isa/wavefront/wavefront.c
@@ -402,6 +402,7 @@ static struct snd_card *snd_wavefront_card_new(int dev)
init_waitqueue_head(&acard->wavefront.interrupt_sleeper);
spin_lock_init(&acard->wavefront.midi.open);
spin_lock_init(&acard->wavefront.midi.virtual);
+ acard->wavefront.card = card;
card->private_free = snd_wavefront_free;
return card;
diff --git a/sound/isa/wavefront/wavefront_fx.c b/sound/isa/wavefront/wavefront_fx.c
index 4f0846feb73f..15331ed88194 100644
--- a/sound/isa/wavefront/wavefront_fx.c
+++ b/sound/isa/wavefront/wavefront_fx.c
@@ -21,6 +21,7 @@
#include <linux/init.h>
#include <linux/time.h>
#include <linux/wait.h>
+#include <linux/firmware.h>
#include <sound/core.h>
#include <sound/snd_wavefront.h>
#include <sound/initval.h>
@@ -32,325 +33,17 @@
#define FX_MSB_TRANSFER 0x02 /* transfer after DSP MSB byte written */
#define FX_AUTO_INCR 0x04 /* auto-increment DSP address after transfer */
-/* weird stuff, derived from port I/O tracing with dosemu */
-
-static unsigned char page_zero[] __devinitdata = {
-0x01, 0x7c, 0x00, 0x1e, 0x00, 0x00, 0x00, 0x00, 0x00, 0xf5, 0x00,
-0x11, 0x00, 0x20, 0x00, 0x32, 0x00, 0x40, 0x00, 0x13, 0x00, 0x00,
-0x00, 0x14, 0x02, 0x76, 0x00, 0x60, 0x00, 0x80, 0x02, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x18, 0x00, 0x19,
-0x01, 0x1a, 0x01, 0x20, 0x01, 0x40, 0x01, 0x17, 0x00, 0x00, 0x01,
-0x80, 0x01, 0x20, 0x00, 0x10, 0x01, 0xa0, 0x03, 0xd1, 0x00, 0x00,
-0x01, 0xf2, 0x02, 0x00, 0x00, 0x13, 0x00, 0x00, 0x00, 0xf4, 0x02,
-0xe0, 0x00, 0x15, 0x00, 0x00, 0x00, 0x16, 0x00, 0x00, 0x00, 0x17,
-0x00, 0x20, 0x00, 0x00, 0x00, 0x20, 0x00, 0x50, 0x00, 0x00, 0x00,
-0x40, 0x00, 0x00, 0x00, 0x71, 0x02, 0x00, 0x00, 0x60, 0x00, 0x00,
-0x00, 0x92, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, 0xb3, 0x02,
-0x00, 0x00, 0xa0, 0x00, 0x00, 0x00, 0xd4, 0x00, 0x00, 0x00, 0x40,
-0x00, 0x80, 0x00, 0xf5, 0x00, 0x20, 0x00, 0x70, 0x00, 0xa0, 0x02,
-0x11, 0x00, 0x16, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x20,
-0x02, 0x00, 0x00, 0x20, 0x00, 0x10, 0x00, 0x17, 0x00, 0x1b, 0x00,
-0x1d, 0x02, 0xdf
-};
-
-static unsigned char page_one[] __devinitdata = {
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x19, 0x00,
-0x1f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0xd8, 0x00, 0x00,
-0x02, 0x20, 0x00, 0x19, 0x00, 0x00, 0x00, 0x00, 0x00, 0x18, 0x01,
-0xc0, 0x01, 0xfa, 0x00, 0x1a, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x40, 0x02, 0x60,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xc0, 0x02, 0x80, 0x00,
-0x00, 0x02, 0xfb, 0x02, 0xa0, 0x00, 0x00, 0x00, 0x1b, 0x02, 0xd7,
-0x00, 0x00, 0x02, 0xf7, 0x03, 0x20, 0x03, 0x00, 0x00, 0x00, 0x00,
-0x1c, 0x03, 0x3c, 0x00, 0x00, 0x03, 0x3f, 0x00, 0x00, 0x03, 0xc0,
-0x00, 0x00, 0x03, 0xdf, 0x00, 0x00, 0x00, 0x00, 0x03, 0x5d, 0x00,
-0x00, 0x03, 0xc0, 0x00, 0x00, 0x03, 0x7d, 0x00, 0x00, 0x03, 0xc0,
-0x00, 0x00, 0x03, 0x9e, 0x00, 0x00, 0x03, 0xc0, 0x00, 0x00, 0x03,
-0xbe, 0x00, 0x00, 0x03, 0xc0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x1b, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02,
-0xdb, 0x00, 0x00, 0x02, 0xdb, 0x00, 0x00, 0x02, 0xe0, 0x00, 0x00,
-0x02, 0xfb, 0x00, 0x00, 0x02, 0xc0, 0x02, 0x40, 0x02, 0xfb, 0x02,
-0x60, 0x00, 0x1b
-};
-
-static unsigned char page_two[] __devinitdata = {
-0xc4, 0x00, 0x44, 0x07, 0x44, 0x00, 0x40, 0x25, 0x01, 0x06, 0xc4,
-0x07, 0x40, 0x25, 0x01, 0x00, 0x46, 0x46, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x46, 0x07,
-0x05, 0x05, 0x05, 0x04, 0x07, 0x05, 0x04, 0x07, 0x05, 0x44, 0x46,
-0x44, 0x46, 0x46, 0x07, 0x05, 0x44, 0x46, 0x05, 0x46, 0x05, 0x46,
-0x05, 0x46, 0x05, 0x44, 0x46, 0x05, 0x07, 0x44, 0x46, 0x05, 0x07,
-0x44, 0x46, 0x05, 0x07, 0x44, 0x46, 0x05, 0x07, 0x44, 0x05, 0x05,
-0x05, 0x44, 0x05, 0x05, 0x05, 0x46, 0x05, 0x46, 0x05, 0x46, 0x05,
-0x46, 0x05, 0x46, 0x07, 0x46, 0x07, 0x44
-};
-
-static unsigned char page_three[] __devinitdata = {
-0x07, 0x40, 0x00, 0x00, 0x00, 0x47, 0x00, 0x40, 0x00, 0x40, 0x06,
-0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x80,
-0xc0, 0x00, 0x00, 0x40, 0x00, 0x00, 0x00, 0x40, 0x00, 0x40, 0x00,
-0x60, 0x00, 0x70, 0x00, 0x40, 0x00, 0x40, 0x00, 0x42, 0x00, 0x40,
-0x00, 0x02, 0x00, 0x40, 0x00, 0x00, 0x00, 0x40, 0x00, 0x00, 0x00,
-0x40, 0x00, 0x00, 0x00, 0x40, 0x00, 0x00, 0x00, 0x40, 0x00, 0x00,
-0x00, 0x42, 0x00, 0x40, 0x00, 0x42, 0x00, 0x02, 0x00, 0x02, 0x00,
-0x02, 0x00, 0x42, 0x00, 0xc0, 0x00, 0x40
-};
-
-static unsigned char page_four[] __devinitdata = {
-0x63, 0x03, 0x26, 0x02, 0x2c, 0x00, 0x24, 0x00, 0x2e, 0x02, 0x02,
-0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01,
-0x20, 0x00, 0x60, 0x00, 0x20, 0x00, 0x20, 0x00, 0x20, 0x00, 0x20,
-0x00, 0x20, 0x00, 0x20, 0x00, 0x20, 0x00, 0x20, 0x00, 0x60, 0x00,
-0x20, 0x00, 0x60, 0x00, 0x20, 0x00, 0x60, 0x00, 0x20, 0x00, 0x60,
-0x00, 0x20, 0x00, 0x60, 0x00, 0x20, 0x00, 0x60, 0x00, 0x20, 0x00,
-0x20, 0x00, 0x22, 0x02, 0x22, 0x02, 0x20, 0x00, 0x60, 0x00, 0x22,
-0x02, 0x62, 0x02, 0x20, 0x01, 0x21, 0x01
-};
-
-static unsigned char page_six[] __devinitdata = {
-0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x04, 0x00, 0x00, 0x06, 0x00,
-0x00, 0x08, 0x00, 0x00, 0x0a, 0x00, 0x00, 0x0c, 0x00, 0x00, 0x0e,
-0x00, 0x00, 0x10, 0x00, 0x00, 0x12, 0x00, 0x00, 0x14, 0x00, 0x00,
-0x16, 0x00, 0x00, 0x18, 0x00, 0x00, 0x1a, 0x00, 0x00, 0x1c, 0x00,
-0x00, 0x1e, 0x00, 0x00, 0x20, 0x00, 0x00, 0x22, 0x00, 0x00, 0x24,
-0x00, 0x00, 0x26, 0x00, 0x00, 0x28, 0x00, 0x00, 0x2a, 0x00, 0x00,
-0x2c, 0x00, 0x00, 0x2e, 0x00, 0x00, 0x30, 0x00, 0x00, 0x32, 0x00,
-0x00, 0x34, 0x00, 0x00, 0x36, 0x00, 0x00, 0x38, 0x00, 0x00, 0x3a,
-0x00, 0x00, 0x3c, 0x00, 0x00, 0x3e, 0x00, 0x00, 0x40, 0x00, 0x00,
-0x42, 0x03, 0x00, 0x44, 0x01, 0x00, 0x46, 0x0a, 0x21, 0x48, 0x0d,
-0x23, 0x4a, 0x23, 0x1b, 0x4c, 0x37, 0x8f, 0x4e, 0x45, 0x77, 0x50,
-0x52, 0xe2, 0x52, 0x1c, 0x92, 0x54, 0x1c, 0x52, 0x56, 0x07, 0x00,
-0x58, 0x2f, 0xc6, 0x5a, 0x0b, 0x00, 0x5c, 0x30, 0x06, 0x5e, 0x17,
-0x00, 0x60, 0x3d, 0xda, 0x62, 0x29, 0x00, 0x64, 0x3e, 0x41, 0x66,
-0x39, 0x00, 0x68, 0x4c, 0x48, 0x6a, 0x49, 0x00, 0x6c, 0x4c, 0x6c,
-0x6e, 0x11, 0xd2, 0x70, 0x16, 0x0c, 0x72, 0x00, 0x00, 0x74, 0x00,
-0x80, 0x76, 0x0f, 0x00, 0x78, 0x00, 0x80, 0x7a, 0x13, 0x00, 0x7c,
-0x80, 0x00, 0x7e, 0x80, 0x80
-};
-
-static unsigned char page_seven[] __devinitdata = {
-0x0f, 0xff, 0x00, 0x00, 0x08, 0x00, 0x08, 0x00, 0x02, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x0f, 0xff, 0x00, 0x00, 0x00, 0x00, 0x08, 0x00,
-0x08, 0x00, 0x00, 0x00, 0x0f, 0xff, 0x00, 0x00, 0x00, 0x00, 0x0f,
-0xff, 0x0f, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0f, 0xff, 0x0f, 0xff,
-0x0f, 0xff, 0x0f, 0xff, 0x02, 0xe9, 0x06, 0x8c, 0x06, 0x8c, 0x0f,
-0xff, 0x1a, 0x75, 0x0d, 0x8b, 0x04, 0xe9, 0x0b, 0x16, 0x1a, 0x38,
-0x0d, 0xc8, 0x04, 0x6f, 0x0b, 0x91, 0x0f, 0xff, 0x06, 0x40, 0x06,
-0x40, 0x02, 0x8f, 0x0f, 0xff, 0x06, 0x62, 0x06, 0x62, 0x02, 0x7b,
-0x0f, 0xff, 0x06, 0x97, 0x06, 0x97, 0x02, 0x52, 0x0f, 0xff, 0x06,
-0xf6, 0x06, 0xf6, 0x02, 0x19, 0x05, 0x55, 0x05, 0x55, 0x05, 0x55,
-0x05, 0x55, 0x05, 0x55, 0x05, 0x55, 0x05, 0x55, 0x05, 0x55, 0x14,
-0xda, 0x0d, 0x93, 0x04, 0xda, 0x05, 0x93, 0x14, 0xda, 0x0d, 0x93,
-0x04, 0xda, 0x05, 0x93, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x02, 0x00
-};
-
-static unsigned char page_zero_v2[] __devinitdata = {
-0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
-};
-
-static unsigned char page_one_v2[] __devinitdata = {
-0x01, 0xc0, 0x01, 0xfa, 0x00, 0x1a, 0x00, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
-};
-
-static unsigned char page_two_v2[] __devinitdata = {
-0x46, 0x46, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00
-};
-static unsigned char page_three_v2[] __devinitdata = {
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00
-};
-static unsigned char page_four_v2[] __devinitdata = {
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00
-};
+#define WAIT_IDLE 0xff
-static unsigned char page_seven_v2[] __devinitdata = {
-0x0f, 0xff, 0x0f, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
-};
+#define FIRMWARE_IN_THE_KERNEL
-static unsigned char mod_v2[] __devinitdata = {
-0x01, 0x00, 0x02, 0x00, 0x01, 0x01, 0x02, 0x00, 0x01, 0x02, 0x02,
-0x00, 0x01, 0x03, 0x02, 0x00, 0x01, 0x04, 0x02, 0x00, 0x01, 0x05,
-0x02, 0x00, 0x01, 0x06, 0x02, 0x00, 0x01, 0x07, 0x02, 0x00, 0xb0,
-0x20, 0xb1, 0x20, 0xb2, 0x20, 0xb3, 0x20, 0xb4, 0x20, 0xb5, 0x20,
-0xb6, 0x20, 0xb7, 0x20, 0xf0, 0x20, 0xf1, 0x20, 0xf2, 0x20, 0xf3,
-0x20, 0xf4, 0x20, 0xf5, 0x20, 0xf6, 0x20, 0xf7, 0x20, 0x10, 0xff,
-0x11, 0xff, 0x12, 0xff, 0x13, 0xff, 0x14, 0xff, 0x15, 0xff, 0x16,
-0xff, 0x17, 0xff, 0x20, 0xff, 0x21, 0xff, 0x22, 0xff, 0x23, 0xff,
-0x24, 0xff, 0x25, 0xff, 0x26, 0xff, 0x27, 0xff, 0x30, 0x00, 0x31,
-0x00, 0x32, 0x00, 0x33, 0x00, 0x34, 0x00, 0x35, 0x00, 0x36, 0x00,
-0x37, 0x00, 0x40, 0x00, 0x41, 0x00, 0x42, 0x00, 0x43, 0x00, 0x44,
-0x00, 0x45, 0x00, 0x46, 0x00, 0x47, 0x00, 0x50, 0x00, 0x51, 0x00,
-0x52, 0x00, 0x53, 0x00, 0x54, 0x00, 0x55, 0x00, 0x56, 0x00, 0x57,
-0x00, 0x60, 0x00, 0x61, 0x00, 0x62, 0x00, 0x63, 0x00, 0x64, 0x00,
-0x65, 0x00, 0x66, 0x00, 0x67, 0x00, 0x70, 0xc0, 0x71, 0xc0, 0x72,
-0xc0, 0x73, 0xc0, 0x74, 0xc0, 0x75, 0xc0, 0x76, 0xc0, 0x77, 0xc0,
-0x80, 0x00, 0x81, 0x00, 0x82, 0x00, 0x83, 0x00, 0x84, 0x00, 0x85,
-0x00, 0x86, 0x00, 0x87, 0x00, 0x90, 0x00, 0x91, 0x00, 0x92, 0x00,
-0x93, 0x00, 0x94, 0x00, 0x95, 0x00, 0x96, 0x00, 0x97, 0x00, 0xa0,
-0x00, 0xa1, 0x00, 0xa2, 0x00, 0xa3, 0x00, 0xa4, 0x00, 0xa5, 0x00,
-0xa6, 0x00, 0xa7, 0x00, 0xc0, 0x00, 0xc1, 0x00, 0xc2, 0x00, 0xc3,
-0x00, 0xc4, 0x00, 0xc5, 0x00, 0xc6, 0x00, 0xc7, 0x00, 0xd0, 0x00,
-0xd1, 0x00, 0xd2, 0x00, 0xd3, 0x00, 0xd4, 0x00, 0xd5, 0x00, 0xd6,
-0x00, 0xd7, 0x00, 0xe0, 0x00, 0xe1, 0x00, 0xe2, 0x00, 0xe3, 0x00,
-0xe4, 0x00, 0xe5, 0x00, 0xe6, 0x00, 0xe7, 0x00, 0x01, 0x00, 0x02,
-0x01, 0x01, 0x01, 0x02, 0x01, 0x01, 0x02, 0x02, 0x01, 0x01, 0x03,
-0x02, 0x01, 0x01, 0x04, 0x02, 0x01, 0x01, 0x05, 0x02, 0x01, 0x01,
-0x06, 0x02, 0x01, 0x01, 0x07, 0x02, 0x01
-};
-static unsigned char coefficients[] __devinitdata = {
-0x07, 0x46, 0x00, 0x00, 0x07, 0x49, 0x00, 0x00, 0x00, 0x4b, 0x03,
-0x11, 0x00, 0x4d, 0x01, 0x32, 0x07, 0x46, 0x00, 0x00, 0x07, 0x49,
-0x00, 0x00, 0x07, 0x40, 0x00, 0x00, 0x07, 0x41, 0x00, 0x00, 0x01,
-0x40, 0x02, 0x40, 0x01, 0x41, 0x02, 0x60, 0x07, 0x40, 0x00, 0x00,
-0x07, 0x41, 0x00, 0x00, 0x07, 0x47, 0x00, 0x00, 0x07, 0x4a, 0x00,
-0x00, 0x00, 0x47, 0x01, 0x00, 0x00, 0x4a, 0x01, 0x20, 0x07, 0x47,
-0x00, 0x00, 0x07, 0x4a, 0x00, 0x00, 0x07, 0x7c, 0x00, 0x00, 0x07,
-0x7e, 0x00, 0x00, 0x00, 0x00, 0x01, 0x1c, 0x07, 0x7c, 0x00, 0x00,
-0x07, 0x7e, 0x00, 0x00, 0x07, 0x44, 0x00, 0x00, 0x00, 0x44, 0x01,
-0x00, 0x07, 0x44, 0x00, 0x00, 0x07, 0x42, 0x00, 0x00, 0x07, 0x43,
-0x00, 0x00, 0x00, 0x42, 0x01, 0x1a, 0x00, 0x43, 0x01, 0x20, 0x07,
-0x42, 0x00, 0x00, 0x07, 0x43, 0x00, 0x00, 0x07, 0x40, 0x00, 0x00,
-0x07, 0x41, 0x00, 0x00, 0x01, 0x40, 0x02, 0x40, 0x01, 0x41, 0x02,
-0x60, 0x07, 0x40, 0x00, 0x00, 0x07, 0x41, 0x00, 0x00, 0x07, 0x44,
-0x0f, 0xff, 0x07, 0x42, 0x00, 0x00, 0x07, 0x43, 0x00, 0x00, 0x07,
-0x40, 0x00, 0x00, 0x07, 0x41, 0x00, 0x00, 0x07, 0x51, 0x06, 0x40,
-0x07, 0x50, 0x06, 0x40, 0x07, 0x4f, 0x03, 0x81, 0x07, 0x53, 0x1a,
-0x76, 0x07, 0x54, 0x0d, 0x8b, 0x07, 0x55, 0x04, 0xe9, 0x07, 0x56,
-0x0b, 0x17, 0x07, 0x57, 0x1a, 0x38, 0x07, 0x58, 0x0d, 0xc9, 0x07,
-0x59, 0x04, 0x6f, 0x07, 0x5a, 0x0b, 0x91, 0x07, 0x73, 0x14, 0xda,
-0x07, 0x74, 0x0d, 0x93, 0x07, 0x75, 0x04, 0xd9, 0x07, 0x76, 0x05,
-0x93, 0x07, 0x77, 0x14, 0xda, 0x07, 0x78, 0x0d, 0x93, 0x07, 0x79,
-0x04, 0xd9, 0x07, 0x7a, 0x05, 0x93, 0x07, 0x5e, 0x03, 0x68, 0x07,
-0x5c, 0x04, 0x31, 0x07, 0x5d, 0x04, 0x31, 0x07, 0x62, 0x03, 0x52,
-0x07, 0x60, 0x04, 0x76, 0x07, 0x61, 0x04, 0x76, 0x07, 0x66, 0x03,
-0x2e, 0x07, 0x64, 0x04, 0xda, 0x07, 0x65, 0x04, 0xda, 0x07, 0x6a,
-0x02, 0xf6, 0x07, 0x68, 0x05, 0x62, 0x07, 0x69, 0x05, 0x62, 0x06,
-0x46, 0x0a, 0x22, 0x06, 0x48, 0x0d, 0x24, 0x06, 0x6e, 0x11, 0xd3,
-0x06, 0x70, 0x15, 0xcb, 0x06, 0x52, 0x20, 0x93, 0x06, 0x54, 0x20,
-0x54, 0x06, 0x4a, 0x27, 0x1d, 0x06, 0x58, 0x2f, 0xc8, 0x06, 0x5c,
-0x30, 0x07, 0x06, 0x4c, 0x37, 0x90, 0x06, 0x60, 0x3d, 0xdb, 0x06,
-0x64, 0x3e, 0x42, 0x06, 0x4e, 0x45, 0x78, 0x06, 0x68, 0x4c, 0x48,
-0x06, 0x6c, 0x4c, 0x6c, 0x06, 0x50, 0x52, 0xe2, 0x06, 0x42, 0x02,
-0xba
-};
-static unsigned char coefficients2[] __devinitdata = {
-0x07, 0x46, 0x00, 0x00, 0x07, 0x49, 0x00, 0x00, 0x07, 0x45, 0x0f,
-0xff, 0x07, 0x48, 0x0f, 0xff, 0x07, 0x7b, 0x04, 0xcc, 0x07, 0x7d,
-0x04, 0xcc, 0x07, 0x7c, 0x00, 0x00, 0x07, 0x7e, 0x00, 0x00, 0x07,
-0x46, 0x00, 0x00, 0x07, 0x49, 0x00, 0x00, 0x07, 0x47, 0x00, 0x00,
-0x07, 0x4a, 0x00, 0x00, 0x07, 0x4c, 0x00, 0x00, 0x07, 0x4e, 0x00, 0x00
-};
-static unsigned char coefficients3[] __devinitdata = {
-0x00, 0x00, 0x00, 0x00, 0x00, 0x28, 0x00, 0x28, 0x00, 0x51, 0x00,
-0x51, 0x00, 0x7a, 0x00, 0x7a, 0x00, 0xa3, 0x00, 0xa3, 0x00, 0xcc,
-0x00, 0xcc, 0x00, 0xf5, 0x00, 0xf5, 0x01, 0x1e, 0x01, 0x1e, 0x01,
-0x47, 0x01, 0x47, 0x01, 0x70, 0x01, 0x70, 0x01, 0x99, 0x01, 0x99,
-0x01, 0xc2, 0x01, 0xc2, 0x01, 0xeb, 0x01, 0xeb, 0x02, 0x14, 0x02,
-0x14, 0x02, 0x3d, 0x02, 0x3d, 0x02, 0x66, 0x02, 0x66, 0x02, 0x8f,
-0x02, 0x8f, 0x02, 0xb8, 0x02, 0xb8, 0x02, 0xe1, 0x02, 0xe1, 0x03,
-0x0a, 0x03, 0x0a, 0x03, 0x33, 0x03, 0x33, 0x03, 0x5c, 0x03, 0x5c,
-0x03, 0x85, 0x03, 0x85, 0x03, 0xae, 0x03, 0xae, 0x03, 0xd7, 0x03,
-0xd7, 0x04, 0x00, 0x04, 0x00, 0x04, 0x28, 0x04, 0x28, 0x04, 0x51,
-0x04, 0x51, 0x04, 0x7a, 0x04, 0x7a, 0x04, 0xa3, 0x04, 0xa3, 0x04,
-0xcc, 0x04, 0xcc, 0x04, 0xf5, 0x04, 0xf5, 0x05, 0x1e, 0x05, 0x1e,
-0x05, 0x47, 0x05, 0x47, 0x05, 0x70, 0x05, 0x70, 0x05, 0x99, 0x05,
-0x99, 0x05, 0xc2, 0x05, 0xc2, 0x05, 0xeb, 0x05, 0xeb, 0x06, 0x14,
-0x06, 0x14, 0x06, 0x3d, 0x06, 0x3d, 0x06, 0x66, 0x06, 0x66, 0x06,
-0x8f, 0x06, 0x8f, 0x06, 0xb8, 0x06, 0xb8, 0x06, 0xe1, 0x06, 0xe1,
-0x07, 0x0a, 0x07, 0x0a, 0x07, 0x33, 0x07, 0x33, 0x07, 0x5c, 0x07,
-0x5c, 0x07, 0x85, 0x07, 0x85, 0x07, 0xae, 0x07, 0xae, 0x07, 0xd7,
-0x07, 0xd7, 0x08, 0x00, 0x08, 0x00, 0x08, 0x28, 0x08, 0x28, 0x08,
-0x51, 0x08, 0x51, 0x08, 0x7a, 0x08, 0x7a, 0x08, 0xa3, 0x08, 0xa3,
-0x08, 0xcc, 0x08, 0xcc, 0x08, 0xf5, 0x08, 0xf5, 0x09, 0x1e, 0x09,
-0x1e, 0x09, 0x47, 0x09, 0x47, 0x09, 0x70, 0x09, 0x70, 0x09, 0x99,
-0x09, 0x99, 0x09, 0xc2, 0x09, 0xc2, 0x09, 0xeb, 0x09, 0xeb, 0x0a,
-0x14, 0x0a, 0x14, 0x0a, 0x3d, 0x0a, 0x3d, 0x0a, 0x66, 0x0a, 0x66,
-0x0a, 0x8f, 0x0a, 0x8f, 0x0a, 0xb8, 0x0a, 0xb8, 0x0a, 0xe1, 0x0a,
-0xe1, 0x0b, 0x0a, 0x0b, 0x0a, 0x0b, 0x33, 0x0b, 0x33, 0x0b, 0x5c,
-0x0b, 0x5c, 0x0b, 0x85, 0x0b, 0x85, 0x0b, 0xae, 0x0b, 0xae, 0x0b,
-0xd7, 0x0b, 0xd7, 0x0c, 0x00, 0x0c, 0x00, 0x0c, 0x28, 0x0c, 0x28,
-0x0c, 0x51, 0x0c, 0x51, 0x0c, 0x7a, 0x0c, 0x7a, 0x0c, 0xa3, 0x0c,
-0xa3, 0x0c, 0xcc, 0x0c, 0xcc, 0x0c, 0xf5, 0x0c, 0xf5, 0x0d, 0x1e,
-0x0d, 0x1e, 0x0d, 0x47, 0x0d, 0x47, 0x0d, 0x70, 0x0d, 0x70, 0x0d,
-0x99, 0x0d, 0x99, 0x0d, 0xc2, 0x0d, 0xc2, 0x0d, 0xeb, 0x0d, 0xeb,
-0x0e, 0x14, 0x0e, 0x14, 0x0e, 0x3d, 0x0e, 0x3d, 0x0e, 0x66, 0x0e,
-0x66, 0x0e, 0x8f, 0x0e, 0x8f, 0x0e, 0xb8, 0x0e, 0xb8, 0x0e, 0xe1,
-0x0e, 0xe1, 0x0f, 0x0a, 0x0f, 0x0a, 0x0f, 0x33, 0x0f, 0x33, 0x0f,
-0x5c, 0x0f, 0x5c, 0x0f, 0x85, 0x0f, 0x85, 0x0f, 0xae, 0x0f, 0xae,
-0x0f, 0xd7, 0x0f, 0xd7, 0x0f, 0xff, 0x0f, 0xff
+#ifdef FIRMWARE_IN_THE_KERNEL
+#include "yss225.c"
+static const struct firmware yss225_registers_firmware = {
+ .data = (u8 *)yss225_registers,
+ .size = sizeof yss225_registers
};
+#endif
static int
wavefront_fx_idle (snd_wavefront_t *dev)
@@ -555,465 +248,56 @@ snd_wavefront_fx_ioctl (struct snd_hwdep *sdev, struct file *file,
of the port I/O done, using the Yamaha faxback document as a guide
to add more logic to the code. Its really pretty weird.
- There was an alternative approach of just dumping the whole I/O
+ This is the approach of just dumping the whole I/O
sequence as a series of port/value pairs and a simple loop
- that output it. However, I hope that eventually I'll get more
- control over what this code does, and so I tried to stick with
- a somewhat "algorithmic" approach.
+ that outputs it.
*/
-
int __devinit
snd_wavefront_fx_start (snd_wavefront_t *dev)
-
{
- unsigned int i, j;
+ unsigned int i;
+ int err;
+ const struct firmware *firmware;
- /* Set all bits for all channels on the MOD unit to zero */
- /* XXX But why do this twice ? */
+ if (dev->fx_initialized)
+ return 0;
- for (j = 0; j < 2; j++) {
- for (i = 0x10; i <= 0xff; i++) {
-
- if (!wavefront_fx_idle (dev)) {
- return (-1);
+ err = request_firmware(&firmware, "yamaha/yss225_registers.bin",
+ dev->card->dev);
+ if (err < 0) {
+#ifdef FIRMWARE_IN_THE_KERNEL
+ firmware = &yss225_registers_firmware;
+#else
+ err = -1;
+ goto out;
+#endif
+ }
+
+ for (i = 0; i + 1 < firmware->size; i += 2) {
+ if (firmware->data[i] >= 8 && firmware->data[i] < 16) {
+ outb(firmware->data[i + 1],
+ dev->base + firmware->data[i]);
+ } else if (firmware->data[i] == WAIT_IDLE) {
+ if (!wavefront_fx_idle(dev)) {
+ err = -1;
+ goto out;
}
-
- outb (i, dev->fx_mod_addr);
- outb (0x0, dev->fx_mod_data);
- }
- }
-
- if (!wavefront_fx_idle (dev)) return (-1);
- outb (0x02, dev->fx_op); /* mute on */
-
- if (!wavefront_fx_idle (dev)) return (-1);
- outb (0x07, dev->fx_dsp_page);
- outb (0x44, dev->fx_dsp_addr);
- outb (0x00, dev->fx_dsp_msb);
- outb (0x00, dev->fx_dsp_lsb);
- if (!wavefront_fx_idle (dev)) return (-1);
- outb (0x07, dev->fx_dsp_page);
- outb (0x42, dev->fx_dsp_addr);
- outb (0x00, dev->fx_dsp_msb);
- outb (0x00, dev->fx_dsp_lsb);
- if (!wavefront_fx_idle (dev)) return (-1);
- outb (0x07, dev->fx_dsp_page);
- outb (0x43, dev->fx_dsp_addr);
- outb (0x00, dev->fx_dsp_msb);
- outb (0x00, dev->fx_dsp_lsb);
- if (!wavefront_fx_idle (dev)) return (-1);
- outb (0x07, dev->fx_dsp_page);
- outb (0x7c, dev->fx_dsp_addr);
- outb (0x00, dev->fx_dsp_msb);
- outb (0x00, dev->fx_dsp_lsb);
- if (!wavefront_fx_idle (dev)) return (-1);
- outb (0x07, dev->fx_dsp_page);
- outb (0x7e, dev->fx_dsp_addr);
- outb (0x00, dev->fx_dsp_msb);
- outb (0x00, dev->fx_dsp_lsb);
- if (!wavefront_fx_idle (dev)) return (-1);
- outb (0x07, dev->fx_dsp_page);
- outb (0x46, dev->fx_dsp_addr);
- outb (0x00, dev->fx_dsp_msb);
- outb (0x00, dev->fx_dsp_lsb);
- if (!wavefront_fx_idle (dev)) return (-1);
- outb (0x07, dev->fx_dsp_page);
- outb (0x49, dev->fx_dsp_addr);
- outb (0x00, dev->fx_dsp_msb);
- outb (0x00, dev->fx_dsp_lsb);
- if (!wavefront_fx_idle (dev)) return (-1);
- outb (0x07, dev->fx_dsp_page);
- outb (0x47, dev->fx_dsp_addr);
- outb (0x00, dev->fx_dsp_msb);
- outb (0x00, dev->fx_dsp_lsb);
- if (!wavefront_fx_idle (dev)) return (-1);
- outb (0x07, dev->fx_dsp_page);
- outb (0x4a, dev->fx_dsp_addr);
- outb (0x00, dev->fx_dsp_msb);
- outb (0x00, dev->fx_dsp_lsb);
-
- /* either because of stupidity by TB's programmers, or because it
- actually does something, rezero the MOD page.
- */
- for (i = 0x10; i <= 0xff; i++) {
-
- if (!wavefront_fx_idle (dev)) {
- return (-1);
+ } else {
+ snd_printk(KERN_ERR "invalid address"
+ " in register data\n");
+ err = -1;
+ goto out;
}
-
- outb (i, dev->fx_mod_addr);
- outb (0x0, dev->fx_mod_data);
- }
- /* load page zero */
-
- outb (FX_AUTO_INCR|FX_LSB_TRANSFER, dev->fx_lcr);
- outb (0x00, dev->fx_dsp_page);
- outb (0x00, dev->fx_dsp_addr);
-
- for (i = 0; i < sizeof (page_zero); i += 2) {
- outb (page_zero[i], dev->fx_dsp_msb);
- outb (page_zero[i+1], dev->fx_dsp_lsb);
- if (!wavefront_fx_idle (dev)) return (-1);
- }
-
- /* Now load page one */
-
- outb (FX_AUTO_INCR|FX_LSB_TRANSFER, dev->fx_lcr);
- outb (0x01, dev->fx_dsp_page);
- outb (0x00, dev->fx_dsp_addr);
-
- for (i = 0; i < sizeof (page_one); i += 2) {
- outb (page_one[i], dev->fx_dsp_msb);
- outb (page_one[i+1], dev->fx_dsp_lsb);
- if (!wavefront_fx_idle (dev)) return (-1);
- }
-
- outb (FX_AUTO_INCR|FX_LSB_TRANSFER, dev->fx_lcr);
- outb (0x02, dev->fx_dsp_page);
- outb (0x00, dev->fx_dsp_addr);
-
- for (i = 0; i < sizeof (page_two); i++) {
- outb (page_two[i], dev->fx_dsp_lsb);
- if (!wavefront_fx_idle (dev)) return (-1);
- }
-
- outb (FX_AUTO_INCR|FX_LSB_TRANSFER, dev->fx_lcr);
- outb (0x03, dev->fx_dsp_page);
- outb (0x00, dev->fx_dsp_addr);
-
- for (i = 0; i < sizeof (page_three); i++) {
- outb (page_three[i], dev->fx_dsp_lsb);
- if (!wavefront_fx_idle (dev)) return (-1);
- }
-
- outb (FX_AUTO_INCR|FX_LSB_TRANSFER, dev->fx_lcr);
- outb (0x04, dev->fx_dsp_page);
- outb (0x00, dev->fx_dsp_addr);
-
- for (i = 0; i < sizeof (page_four); i++) {
- outb (page_four[i], dev->fx_dsp_lsb);
- if (!wavefront_fx_idle (dev)) return (-1);
- }
-
- /* Load memory area (page six) */
-
- outb (FX_LSB_TRANSFER, dev->fx_lcr);
- outb (0x06, dev->fx_dsp_page);
-
- for (i = 0; i < sizeof (page_six); i += 3) {
- outb (page_six[i], dev->fx_dsp_addr);
- outb (page_six[i+1], dev->fx_dsp_msb);
- outb (page_six[i+2], dev->fx_dsp_lsb);
- if (!wavefront_fx_idle (dev)) return (-1);
- }
-
- outb (FX_AUTO_INCR|FX_LSB_TRANSFER, dev->fx_lcr);
- outb (0x07, dev->fx_dsp_page);
- outb (0x00, dev->fx_dsp_addr);
-
- for (i = 0; i < sizeof (page_seven); i += 2) {
- outb (page_seven[i], dev->fx_dsp_msb);
- outb (page_seven[i+1], dev->fx_dsp_lsb);
- if (!wavefront_fx_idle (dev)) return (-1);
- }
-
- /* Now setup the MOD area. We do this algorithmically in order to
- save a little data space. It could be done in the same fashion
- as the "pages".
- */
-
- for (i = 0x00; i <= 0x0f; i++) {
- outb (0x01, dev->fx_mod_addr);
- outb (i, dev->fx_mod_data);
- if (!wavefront_fx_idle (dev)) return (-1);
- outb (0x02, dev->fx_mod_addr);
- outb (0x00, dev->fx_mod_data);
- if (!wavefront_fx_idle (dev)) return (-1);
- }
-
- for (i = 0xb0; i <= 0xbf; i++) {
- outb (i, dev->fx_mod_addr);
- outb (0x20, dev->fx_mod_data);
- if (!wavefront_fx_idle (dev)) return (-1);
}
- for (i = 0xf0; i <= 0xff; i++) {
- outb (i, dev->fx_mod_addr);
- outb (0x20, dev->fx_mod_data);
- if (!wavefront_fx_idle (dev)) return (-1);
- }
-
- for (i = 0x10; i <= 0x1d; i++) {
- outb (i, dev->fx_mod_addr);
- outb (0xff, dev->fx_mod_data);
- if (!wavefront_fx_idle (dev)) return (-1);
- }
-
- outb (0x1e, dev->fx_mod_addr);
- outb (0x40, dev->fx_mod_data);
- if (!wavefront_fx_idle (dev)) return (-1);
-
- for (i = 0x1f; i <= 0x2d; i++) {
- outb (i, dev->fx_mod_addr);
- outb (0xff, dev->fx_mod_data);
- if (!wavefront_fx_idle (dev)) return (-1);
- }
-
- outb (0x2e, dev->fx_mod_addr);
- outb (0x00, dev->fx_mod_data);
- if (!wavefront_fx_idle (dev)) return (-1);
-
- for (i = 0x2f; i <= 0x3e; i++) {
- outb (i, dev->fx_mod_addr);
- outb (0x00, dev->fx_mod_data);
- if (!wavefront_fx_idle (dev)) return (-1);
- }
-
- outb (0x3f, dev->fx_mod_addr);
- outb (0x20, dev->fx_mod_data);
- if (!wavefront_fx_idle (dev)) return (-1);
-
- for (i = 0x40; i <= 0x4d; i++) {
- outb (i, dev->fx_mod_addr);
- outb (0x00, dev->fx_mod_data);
- if (!wavefront_fx_idle (dev)) return (-1);
- }
-
- outb (0x4e, dev->fx_mod_addr);
- outb (0x0e, dev->fx_mod_data);
- if (!wavefront_fx_idle (dev)) return (-1);
- outb (0x4f, dev->fx_mod_addr);
- outb (0x0e, dev->fx_mod_data);
- if (!wavefront_fx_idle (dev)) return (-1);
-
-
- for (i = 0x50; i <= 0x6b; i++) {
- outb (i, dev->fx_mod_addr);
- outb (0x00, dev->fx_mod_data);
- if (!wavefront_fx_idle (dev)) return (-1);
- }
-
- outb (0x6c, dev->fx_mod_addr);
- outb (0x40, dev->fx_mod_data);
- if (!wavefront_fx_idle (dev)) return (-1);
-
- outb (0x6d, dev->fx_mod_addr);
- outb (0x00, dev->fx_mod_data);
- if (!wavefront_fx_idle (dev)) return (-1);
-
- outb (0x6e, dev->fx_mod_addr);
- outb (0x40, dev->fx_mod_data);
- if (!wavefront_fx_idle (dev)) return (-1);
-
- outb (0x6f, dev->fx_mod_addr);
- outb (0x40, dev->fx_mod_data);
- if (!wavefront_fx_idle (dev)) return (-1);
-
- for (i = 0x70; i <= 0x7f; i++) {
- outb (i, dev->fx_mod_addr);
- outb (0xc0, dev->fx_mod_data);
- if (!wavefront_fx_idle (dev)) return (-1);
- }
-
- for (i = 0x80; i <= 0xaf; i++) {
- outb (i, dev->fx_mod_addr);
- outb (0x00, dev->fx_mod_data);
- if (!wavefront_fx_idle (dev)) return (-1);
- }
-
- for (i = 0xc0; i <= 0xdd; i++) {
- outb (i, dev->fx_mod_addr);
- outb (0x00, dev->fx_mod_data);
- if (!wavefront_fx_idle (dev)) return (-1);
- }
-
- outb (0xde, dev->fx_mod_addr);
- outb (0x10, dev->fx_mod_data);
- if (!wavefront_fx_idle (dev)) return (-1);
- outb (0xdf, dev->fx_mod_addr);
- outb (0x10, dev->fx_mod_data);
- if (!wavefront_fx_idle (dev)) return (-1);
-
- for (i = 0xe0; i <= 0xef; i++) {
- outb (i, dev->fx_mod_addr);
- outb (0x00, dev->fx_mod_data);
- if (!wavefront_fx_idle (dev)) return (-1);
- }
-
- for (i = 0x00; i <= 0x0f; i++) {
- outb (0x01, dev->fx_mod_addr);
- outb (i, dev->fx_mod_data);
- outb (0x02, dev->fx_mod_addr);
- outb (0x01, dev->fx_mod_data);
- if (!wavefront_fx_idle (dev)) return (-1);
- }
-
- outb (0x02, dev->fx_op); /* mute on */
-
- /* Now set the coefficients and so forth for the programs above */
-
- for (i = 0; i < sizeof (coefficients); i += 4) {
- outb (coefficients[i], dev->fx_dsp_page);
- outb (coefficients[i+1], dev->fx_dsp_addr);
- outb (coefficients[i+2], dev->fx_dsp_msb);
- outb (coefficients[i+3], dev->fx_dsp_lsb);
- if (!wavefront_fx_idle (dev)) return (-1);
- }
-
- /* Some settings (?) that are too small to bundle into loops */
-
- if (!wavefront_fx_idle (dev)) return (-1);
- outb (0x1e, dev->fx_mod_addr);
- outb (0x14, dev->fx_mod_data);
- if (!wavefront_fx_idle (dev)) return (-1);
- outb (0xde, dev->fx_mod_addr);
- outb (0x20, dev->fx_mod_data);
- if (!wavefront_fx_idle (dev)) return (-1);
- outb (0xdf, dev->fx_mod_addr);
- outb (0x20, dev->fx_mod_data);
-
- /* some more coefficients */
-
- if (!wavefront_fx_idle (dev)) return (-1);
- outb (0x06, dev->fx_dsp_page);
- outb (0x78, dev->fx_dsp_addr);
- outb (0x00, dev->fx_dsp_msb);
- outb (0x40, dev->fx_dsp_lsb);
- if (!wavefront_fx_idle (dev)) return (-1);
- outb (0x07, dev->fx_dsp_page);
- outb (0x03, dev->fx_dsp_addr);
- outb (0x0f, dev->fx_dsp_msb);
- outb (0xff, dev->fx_dsp_lsb);
- if (!wavefront_fx_idle (dev)) return (-1);
- outb (0x07, dev->fx_dsp_page);
- outb (0x0b, dev->fx_dsp_addr);
- outb (0x0f, dev->fx_dsp_msb);
- outb (0xff, dev->fx_dsp_lsb);
- if (!wavefront_fx_idle (dev)) return (-1);
- outb (0x07, dev->fx_dsp_page);
- outb (0x02, dev->fx_dsp_addr);
- outb (0x00, dev->fx_dsp_msb);
- outb (0x00, dev->fx_dsp_lsb);
- if (!wavefront_fx_idle (dev)) return (-1);
- outb (0x07, dev->fx_dsp_page);
- outb (0x0a, dev->fx_dsp_addr);
- outb (0x00, dev->fx_dsp_msb);
- outb (0x00, dev->fx_dsp_lsb);
- if (!wavefront_fx_idle (dev)) return (-1);
- outb (0x07, dev->fx_dsp_page);
- outb (0x46, dev->fx_dsp_addr);
- outb (0x00, dev->fx_dsp_msb);
- outb (0x00, dev->fx_dsp_lsb);
- if (!wavefront_fx_idle (dev)) return (-1);
- outb (0x07, dev->fx_dsp_page);
- outb (0x49, dev->fx_dsp_addr);
- outb (0x00, dev->fx_dsp_msb);
- outb (0x00, dev->fx_dsp_lsb);
-
- /* Now, for some strange reason, lets reload every page
- and all the coefficients over again. I have *NO* idea
- why this is done. I do know that no sound is produced
- is this phase is omitted.
- */
-
- outb (FX_AUTO_INCR|FX_LSB_TRANSFER, dev->fx_lcr);
- outb (0x00, dev->fx_dsp_page);
- outb (0x10, dev->fx_dsp_addr);
-
- for (i = 0; i < sizeof (page_zero_v2); i += 2) {
- outb (page_zero_v2[i], dev->fx_dsp_msb);
- outb (page_zero_v2[i+1], dev->fx_dsp_lsb);
- if (!wavefront_fx_idle (dev)) return (-1);
- }
-
- outb (FX_AUTO_INCR|FX_LSB_TRANSFER, dev->fx_lcr);
- outb (0x01, dev->fx_dsp_page);
- outb (0x10, dev->fx_dsp_addr);
-
- for (i = 0; i < sizeof (page_one_v2); i += 2) {
- outb (page_one_v2[i], dev->fx_dsp_msb);
- outb (page_one_v2[i+1], dev->fx_dsp_lsb);
- if (!wavefront_fx_idle (dev)) return (-1);
- }
-
- if (!wavefront_fx_idle (dev)) return (-1);
- if (!wavefront_fx_idle (dev)) return (-1);
-
- outb (FX_AUTO_INCR|FX_LSB_TRANSFER, dev->fx_lcr);
- outb (0x02, dev->fx_dsp_page);
- outb (0x10, dev->fx_dsp_addr);
-
- for (i = 0; i < sizeof (page_two_v2); i++) {
- outb (page_two_v2[i], dev->fx_dsp_lsb);
- if (!wavefront_fx_idle (dev)) return (-1);
- }
- outb (FX_AUTO_INCR|FX_LSB_TRANSFER, dev->fx_lcr);
- outb (0x03, dev->fx_dsp_page);
- outb (0x10, dev->fx_dsp_addr);
-
- for (i = 0; i < sizeof (page_three_v2); i++) {
- outb (page_three_v2[i], dev->fx_dsp_lsb);
- if (!wavefront_fx_idle (dev)) return (-1);
- }
-
- outb (FX_AUTO_INCR|FX_LSB_TRANSFER, dev->fx_lcr);
- outb (0x04, dev->fx_dsp_page);
- outb (0x10, dev->fx_dsp_addr);
-
- for (i = 0; i < sizeof (page_four_v2); i++) {
- outb (page_four_v2[i], dev->fx_dsp_lsb);
- if (!wavefront_fx_idle (dev)) return (-1);
- }
-
- outb (FX_LSB_TRANSFER, dev->fx_lcr);
- outb (0x06, dev->fx_dsp_page);
-
- /* Page six v.2 is algorithmic */
-
- for (i = 0x10; i <= 0x3e; i += 2) {
- outb (i, dev->fx_dsp_addr);
- outb (0x00, dev->fx_dsp_msb);
- outb (0x00, dev->fx_dsp_lsb);
- if (!wavefront_fx_idle (dev)) return (-1);
- }
-
- outb (FX_AUTO_INCR|FX_LSB_TRANSFER, dev->fx_lcr);
- outb (0x07, dev->fx_dsp_page);
- outb (0x10, dev->fx_dsp_addr);
-
- for (i = 0; i < sizeof (page_seven_v2); i += 2) {
- outb (page_seven_v2[i], dev->fx_dsp_msb);
- outb (page_seven_v2[i+1], dev->fx_dsp_lsb);
- if (!wavefront_fx_idle (dev)) return (-1);
- }
-
- for (i = 0x00; i < sizeof(mod_v2); i += 2) {
- outb (mod_v2[i], dev->fx_mod_addr);
- outb (mod_v2[i+1], dev->fx_mod_data);
- if (!wavefront_fx_idle (dev)) return (-1);
- }
+ dev->fx_initialized = 1;
+ err = 0;
- for (i = 0; i < sizeof (coefficients2); i += 4) {
- outb (coefficients2[i], dev->fx_dsp_page);
- outb (coefficients2[i+1], dev->fx_dsp_addr);
- outb (coefficients2[i+2], dev->fx_dsp_msb);
- outb (coefficients2[i+3], dev->fx_dsp_lsb);
- if (!wavefront_fx_idle (dev)) return (-1);
- }
-
- for (i = 0; i < sizeof (coefficients3); i += 2) {
- int x;
-
- outb (0x07, dev->fx_dsp_page);
- x = (i % 4) ? 0x4e : 0x4c;
- outb (x, dev->fx_dsp_addr);
- outb (coefficients3[i], dev->fx_dsp_msb);
- outb (coefficients3[i+1], dev->fx_dsp_lsb);
- }
-
- outb (0x00, dev->fx_op); /* mute off */
- if (!wavefront_fx_idle (dev)) return (-1);
-
- return (0);
+out:
+#ifdef FIRMWARE_IN_THE_KERNEL
+ if (firmware != &yss225_registers_firmware)
+#endif
+ release_firmware(firmware);
+ return err;
}
diff --git a/sound/isa/wavefront/yss225.c b/sound/isa/wavefront/yss225.c
new file mode 100644
index 000000000000..9f6be3ff8ecf
--- /dev/null
+++ b/sound/isa/wavefront/yss225.c
@@ -0,0 +1,2739 @@
+/*
+ * Copyright (c) 1998-2002 by Paul Davis <pbd@op.net>
+ *
+ * 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
+ */
+
+/* weird stuff, derived from port I/O tracing with dosemu */
+
+static const struct {
+ unsigned char addr;
+ unsigned char data;
+} yss225_registers[] __devinitdata = {
+/* Set all bits for all channels on the MOD unit to zero */
+{ WAIT_IDLE }, { 0xe, 0x10 }, { 0xf, 0x00 },
+{ WAIT_IDLE }, { 0xe, 0x11 }, { 0xf, 0x00 },
+{ WAIT_IDLE }, { 0xe, 0x12 }, { 0xf, 0x00 },
+{ WAIT_IDLE }, { 0xe, 0x13 }, { 0xf, 0x00 },
+{ WAIT_IDLE }, { 0xe, 0x14 }, { 0xf, 0x00 },
+{ WAIT_IDLE }, { 0xe, 0x15 }, { 0xf, 0x00 },
+{ WAIT_IDLE }, { 0xe, 0x16 }, { 0xf, 0x00 },
+{ WAIT_IDLE }, { 0xe, 0x17 }, { 0xf, 0x00 },
+{ WAIT_IDLE }, { 0xe, 0x18 }, { 0xf, 0x00 },
+{ WAIT_IDLE }, { 0xe, 0x19 }, { 0xf, 0x00 },
+{ WAIT_IDLE }, { 0xe, 0x1a }, { 0xf, 0x00 },
+{ WAIT_IDLE }, { 0xe, 0x1b }, { 0xf, 0x00 },
+{ WAIT_IDLE }, { 0xe, 0x1c }, { 0xf, 0x00 },
+{ WAIT_IDLE }, { 0xe, 0x1d }, { 0xf, 0x00 },
+{ WAIT_IDLE }, { 0xe, 0x1e }, { 0xf, 0x00 },
+{ WAIT_IDLE }, { 0xe, 0x1f }, { 0xf, 0x00 },
+{ WAIT_IDLE }, { 0xe, 0x20 }, { 0xf, 0x00 },
+{ WAIT_IDLE }, { 0xe, 0x21 }, { 0xf, 0x00 },
+{ WAIT_IDLE }, { 0xe, 0x22 }, { 0xf, 0x00 },
+{ WAIT_IDLE }, { 0xe, 0x23 }, { 0xf, 0x00 },
+{ WAIT_IDLE }, { 0xe, 0x24 }, { 0xf, 0x00 },
+{ WAIT_IDLE }, { 0xe, 0x25 }, { 0xf, 0x00 },
+{ WAIT_IDLE }, { 0xe, 0x26 }, { 0xf, 0x00 },
+{ WAIT_IDLE }, { 0xe, 0x27 }, { 0xf, 0x00 },
+{ WAIT_IDLE }, { 0xe, 0x28 }, { 0xf, 0x00 },
+{ WAIT_IDLE }, { 0xe, 0x29 }, { 0xf, 0x00 },
+{ WAIT_IDLE }, { 0xe, 0x2a }, { 0xf, 0x00 },
+{ WAIT_IDLE }, { 0xe, 0x2b }, { 0xf, 0x00 },
+{ WAIT_IDLE }, { 0xe, 0x2c }, { 0xf, 0x00 },
+{ WAIT_IDLE }, { 0xe, 0x2d }, { 0xf, 0x00 },
+{ WAIT_IDLE }, { 0xe, 0x2e }, { 0xf, 0x00 },
+{ WAIT_IDLE }, { 0xe, 0x2f }, { 0xf, 0x00 },
+{ WAIT_IDLE }, { 0xe, 0x30 }, { 0xf, 0x00 },
+{ WAIT_IDLE }, { 0xe, 0x31 }, { 0xf, 0x00 },
+{ WAIT_IDLE }, { 0xe, 0x32 }, { 0xf, 0x00 },
+{ WAIT_IDLE }, { 0xe, 0x33 }, { 0xf, 0x00 },
+{ WAIT_IDLE }, { 0xe, 0x34 }, { 0xf, 0x00 },
+{ WAIT_IDLE }, { 0xe, 0x35 }, { 0xf, 0x00 },
+{ WAIT_IDLE }, { 0xe, 0x36 }, { 0xf, 0x00 },
+{ WAIT_IDLE }, { 0xe, 0x37 }, { 0xf, 0x00 },
+{ WAIT_IDLE }, { 0xe, 0x38 }, { 0xf, 0x00 },
+{ WAIT_IDLE }, { 0xe, 0x39 }, { 0xf, 0x00 },
+{ WAIT_IDLE }, { 0xe, 0x3a }, { 0xf, 0x00 },
+{ WAIT_IDLE }, { 0xe, 0x3b }, { 0xf, 0x00 },
+{ WAIT_IDLE }, { 0xe, 0x3c }, { 0xf, 0x00 },
+{ WAIT_IDLE }, { 0xe, 0x3d }, { 0xf, 0x00 },
+{ WAIT_IDLE }, { 0xe, 0x3e }, { 0xf, 0x00 },
+{ WAIT_IDLE }, { 0xe, 0x3f }, { 0xf, 0x00 },
+{ WAIT_IDLE }, { 0xe, 0x40 }, { 0xf, 0x00 },
+{ WAIT_IDLE }, { 0xe, 0x41 }, { 0xf, 0x00 },
+{ WAIT_IDLE }, { 0xe, 0x42 }, { 0xf, 0x00 },
+{ WAIT_IDLE }, { 0xe, 0x43 }, { 0xf, 0x00 },
+{ WAIT_IDLE }, { 0xe, 0x44 }, { 0xf, 0x00 },
+{ WAIT_IDLE }, { 0xe, 0x45 }, { 0xf, 0x00 },
+{ WAIT_IDLE }, { 0xe, 0x46 }, { 0xf, 0x00 },
+{ WAIT_IDLE }, { 0xe, 0x47 }, { 0xf, 0x00 },
+{ WAIT_IDLE }, { 0xe, 0x48 }, { 0xf, 0x00 },
+{ WAIT_IDLE }, { 0xe, 0x49 }, { 0xf, 0x00 },
+{ WAIT_IDLE }, { 0xe, 0x4a }, { 0xf, 0x00 },
+{ WAIT_IDLE }, { 0xe, 0x4b }, { 0xf, 0x00 },
+{ WAIT_IDLE }, { 0xe, 0x4c }, { 0xf, 0x00 },
+{ WAIT_IDLE }, { 0xe, 0x4d }, { 0xf, 0x00 },
+{ WAIT_IDLE }, { 0xe, 0x4e }, { 0xf, 0x00 },
+{ WAIT_IDLE }, { 0xe, 0x4f }, { 0xf, 0x00 },
+{ WAIT_IDLE }, { 0xe, 0x50 }, { 0xf, 0x00 },
+{ WAIT_IDLE }, { 0xe, 0x51 }, { 0xf, 0x00 },
+{ WAIT_IDLE }, { 0xe, 0x52 }, { 0xf, 0x00 },
+{ WAIT_IDLE }, { 0xe, 0x53 }, { 0xf, 0x00 },
+{ WAIT_IDLE }, { 0xe, 0x54 }, { 0xf, 0x00 },
+{ WAIT_IDLE }, { 0xe, 0x55 }, { 0xf, 0x00 },
+{ WAIT_IDLE }, { 0xe, 0x56 }, { 0xf, 0x00 },
+{ WAIT_IDLE }, { 0xe, 0x57 }, { 0xf, 0x00 },
+{ WAIT_IDLE }, { 0xe, 0x58 }, { 0xf, 0x00 },
+{ WAIT_IDLE }, { 0xe, 0x59 }, { 0xf, 0x00 },
+{ WAIT_IDLE }, { 0xe, 0x5a }, { 0xf, 0x00 },
+{ WAIT_IDLE }, { 0xe, 0x5b }, { 0xf, 0x00 },
+{ WAIT_IDLE }, { 0xe, 0x5c }, { 0xf, 0x00 },
+{ WAIT_IDLE }, { 0xe, 0x5d }, { 0xf, 0x00 },
+{ WAIT_IDLE }, { 0xe, 0x5e }, { 0xf, 0x00 },
+{ WAIT_IDLE }, { 0xe, 0x5f }, { 0xf, 0x00 },
+{ WAIT_IDLE }, { 0xe, 0x60 }, { 0xf, 0x00 },
+{ WAIT_IDLE }, { 0xe, 0x61 }, { 0xf, 0x00 },
+{ WAIT_IDLE }, { 0xe, 0x62 }, { 0xf, 0x00 },
+{ WAIT_IDLE }, { 0xe, 0x63 }, { 0xf, 0x00 },
+{ WAIT_IDLE }, { 0xe, 0x64 }, { 0xf, 0x00 },
+{ WAIT_IDLE }, { 0xe, 0x65 }, { 0xf, 0x00 },
+{ WAIT_IDLE }, { 0xe, 0x66 }, { 0xf, 0x00 },
+{ WAIT_IDLE }, { 0xe, 0x67 }, { 0xf, 0x00 },
+{ WAIT_IDLE }, { 0xe, 0x68 }, { 0xf, 0x00 },
+{ WAIT_IDLE }, { 0xe, 0x69 }, { 0xf, 0x00 },
+{ WAIT_IDLE }, { 0xe, 0x6a }, { 0xf, 0x00 },
+{ WAIT_IDLE }, { 0xe, 0x6b }, { 0xf, 0x00 },
+{ WAIT_IDLE }, { 0xe, 0x6c }, { 0xf, 0x00 },
+{ WAIT_IDLE }, { 0xe, 0x6d }, { 0xf, 0x00 },
+{ WAIT_IDLE }, { 0xe, 0x6e }, { 0xf, 0x00 },
+{ WAIT_IDLE }, { 0xe, 0x6f }, { 0xf, 0x00 },
+{ WAIT_IDLE }, { 0xe, 0x70 }, { 0xf, 0x00 },
+{ WAIT_IDLE }, { 0xe, 0x71 }, { 0xf, 0x00 },
+{ WAIT_IDLE }, { 0xe, 0x72 }, { 0xf, 0x00 },
+{ WAIT_IDLE }, { 0xe, 0x73 }, { 0xf, 0x00 },
+{ WAIT_IDLE }, { 0xe, 0x74 }, { 0xf, 0x00 },
+{ WAIT_IDLE }, { 0xe, 0x75 }, { 0xf, 0x00 },
+{ WAIT_IDLE }, { 0xe, 0x76 }, { 0xf, 0x00 },
+{ WAIT_IDLE }, { 0xe, 0x77 }, { 0xf, 0x00 },
+{ WAIT_IDLE }, { 0xe, 0x78 }, { 0xf, 0x00 },
+{ WAIT_IDLE }, { 0xe, 0x79 }, { 0xf, 0x00 },
+{ WAIT_IDLE }, { 0xe, 0x7a }, { 0xf, 0x00 },
+{ WAIT_IDLE }, { 0xe, 0x7b }, { 0xf, 0x00 },
+{ WAIT_IDLE }, { 0xe, 0x7c }, { 0xf, 0x00 },
+{ WAIT_IDLE }, { 0xe, 0x7d }, { 0xf, 0x00 },
+{ WAIT_IDLE }, { 0xe, 0x7e }, { 0xf, 0x00 },
+{ WAIT_IDLE }, { 0xe, 0x7f }, { 0xf, 0x00 },
+{ WAIT_IDLE }, { 0xe, 0x80 }, { 0xf, 0x00 },
+{ WAIT_IDLE }, { 0xe, 0x81 }, { 0xf, 0x00 },
+{ WAIT_IDLE }, { 0xe, 0x82 }, { 0xf, 0x00 },
+{ WAIT_IDLE }, { 0xe, 0x83 }, { 0xf, 0x00 },
+{ WAIT_IDLE }, { 0xe, 0x84 }, { 0xf, 0x00 },
+{ WAIT_IDLE }, { 0xe, 0x85 }, { 0xf, 0x00 },
+{ WAIT_IDLE }, { 0xe, 0x86 }, { 0xf, 0x00 },
+{ WAIT_IDLE }, { 0xe, 0x87 }, { 0xf, 0x00 },
+{ WAIT_IDLE }, { 0xe, 0x88 }, { 0xf, 0x00 },
+{ WAIT_IDLE }, { 0xe, 0x89 }, { 0xf, 0x00 },
+{ WAIT_IDLE }, { 0xe, 0x8a }, { 0xf, 0x00 },
+{ WAIT_IDLE }, { 0xe, 0x8b }, { 0xf, 0x00 },
+{ WAIT_IDLE }, { 0xe, 0x8c }, { 0xf, 0x00 },
+{ WAIT_IDLE }, { 0xe, 0x8d }, { 0xf, 0x00 },
+{ WAIT_IDLE }, { 0xe, 0x8e }, { 0xf, 0x00 },
+{ WAIT_IDLE }, { 0xe, 0x8f }, { 0xf, 0x00 },
+{ WAIT_IDLE }, { 0xe, 0x90 }, { 0xf, 0x00 },
+{ WAIT_IDLE }, { 0xe, 0x91 }, { 0xf, 0x00 },
+{ WAIT_IDLE }, { 0xe, 0x92 }, { 0xf, 0x00 },
+{ WAIT_IDLE }, { 0xe, 0x93 }, { 0xf, 0x00 },
+{ WAIT_IDLE }, { 0xe, 0x94 }, { 0xf, 0x00 },
+{ WAIT_IDLE }, { 0xe, 0x95 }, { 0xf, 0x00 },
+{ WAIT_IDLE }, { 0xe, 0x96 }, { 0xf, 0x00 },
+{ WAIT_IDLE }, { 0xe, 0x97 }, { 0xf, 0x00 },
+{ WAIT_IDLE }, { 0xe, 0x98 }, { 0xf, 0x00 },
+{ WAIT_IDLE }, { 0xe, 0x99 }, { 0xf, 0x00 },
+{ WAIT_IDLE }, { 0xe, 0x9a }, { 0xf, 0x00 },
+{ WAIT_IDLE }, { 0xe, 0x9b }, { 0xf, 0x00 },
+{ WAIT_IDLE }, { 0xe, 0x9c }, { 0xf, 0x00 },
+{ WAIT_IDLE }, { 0xe, 0x9d }, { 0xf, 0x00 },
+{ WAIT_IDLE }, { 0xe, 0x9e }, { 0xf, 0x00 },
+{ WAIT_IDLE }, { 0xe, 0x9f }, { 0xf, 0x00 },
+{ WAIT_IDLE }, { 0xe, 0xa0 }, { 0xf, 0x00 },
+{ WAIT_IDLE }, { 0xe, 0xa1 }, { 0xf, 0x00 },
+{ WAIT_IDLE }, { 0xe, 0xa2 }, { 0xf, 0x00 },
+{ WAIT_IDLE }, { 0xe, 0xa3 }, { 0xf, 0x00 },
+{ WAIT_IDLE }, { 0xe, 0xa4 }, { 0xf, 0x00 },
+{ WAIT_IDLE }, { 0xe, 0xa5 }, { 0xf, 0x00 },
+{ WAIT_IDLE }, { 0xe, 0xa6 }, { 0xf, 0x00 },
+{ WAIT_IDLE }, { 0xe, 0xa7 }, { 0xf, 0x00 },
+{ WAIT_IDLE }, { 0xe, 0xa8 }, { 0xf, 0x00 },
+{ WAIT_IDLE }, { 0xe, 0xa9 }, { 0xf, 0x00 },
+{ WAIT_IDLE }, { 0xe, 0xaa }, { 0xf, 0x00 },
+{ WAIT_IDLE }, { 0xe, 0xab }, { 0xf, 0x00 },
+{ WAIT_IDLE }, { 0xe, 0xac }, { 0xf, 0x00 },
+{ WAIT_IDLE }, { 0xe, 0xad }, { 0xf, 0x00 },
+{ WAIT_IDLE }, { 0xe, 0xae }, { 0xf, 0x00 },
+{ WAIT_IDLE }, { 0xe, 0xaf }, { 0xf, 0x00 },
+{ WAIT_IDLE }, { 0xe, 0xb0 }, { 0xf, 0x00 },
+{ WAIT_IDLE }, { 0xe, 0xb1 }, { 0xf, 0x00 },
+{ WAIT_IDLE }, { 0xe, 0xb2 }, { 0xf, 0x00 },
+{ WAIT_IDLE }, { 0xe, 0xb3 }, { 0xf, 0x00 },
+{ WAIT_IDLE }, { 0xe, 0xb4 }, { 0xf, 0x00 },
+{ WAIT_IDLE }, { 0xe, 0xb5 }, { 0xf, 0x00 },
+{ WAIT_IDLE }, { 0xe, 0xb6 }, { 0xf, 0x00 },
+{ WAIT_IDLE }, { 0xe, 0xb7 }, { 0xf, 0x00 },
+{ WAIT_IDLE }, { 0xe, 0xb8 }, { 0xf, 0x00 },
+{ WAIT_IDLE }, { 0xe, 0xb9 }, { 0xf, 0x00 },
+{ WAIT_IDLE }, { 0xe, 0xba }, { 0xf, 0x00 },
+{ WAIT_IDLE }, { 0xe, 0xbb }, { 0xf, 0x00 },
+{ WAIT_IDLE }, { 0xe, 0xbc }, { 0xf, 0x00 },
+{ WAIT_IDLE }, { 0xe, 0xbd }, { 0xf, 0x00 },
+{ WAIT_IDLE }, { 0xe, 0xbe }, { 0xf, 0x00 },
+{ WAIT_IDLE }, { 0xe, 0xbf }, { 0xf, 0x00 },
+{ WAIT_IDLE }, { 0xe, 0xc0 }, { 0xf, 0x00 },
+{ WAIT_IDLE }, { 0xe, 0xc1 }, { 0xf, 0x00 },
+{ WAIT_IDLE }, { 0xe, 0xc2 }, { 0xf, 0x00 },
+{ WAIT_IDLE }, { 0xe, 0xc3 }, { 0xf, 0x00 },
+{ WAIT_IDLE }, { 0xe, 0xc4 }, { 0xf, 0x00 },
+{ WAIT_IDLE }, { 0xe, 0xc5 }, { 0xf, 0x00 },
+{ WAIT_IDLE }, { 0xe, 0xc6 }, { 0xf, 0x00 },
+{ WAIT_IDLE }, { 0xe, 0xc7 }, { 0xf, 0x00 },
+{ WAIT_IDLE }, { 0xe, 0xc8 }, { 0xf, 0x00 },
+{ WAIT_IDLE }, { 0xe, 0xc9 }, { 0xf, 0x00 },
+{ WAIT_IDLE }, { 0xe, 0xca }, { 0xf, 0x00 },
+{ WAIT_IDLE }, { 0xe, 0xcb }, { 0xf, 0x00 },
+{ WAIT_IDLE }, { 0xe, 0xcc }, { 0xf, 0x00 },
+{ WAIT_IDLE }, { 0xe, 0xcd }, { 0xf, 0x00 },
+{ WAIT_IDLE }, { 0xe, 0xce }, { 0xf, 0x00 },
+{ WAIT_IDLE }, { 0xe, 0xcf }, { 0xf, 0x00 },
+{ WAIT_IDLE }, { 0xe, 0xd0 }, { 0xf, 0x00 },
+{ WAIT_IDLE }, { 0xe, 0xd1 }, { 0xf, 0x00 },
+{ WAIT_IDLE }, { 0xe, 0xd2 }, { 0xf, 0x00 },
+{ WAIT_IDLE }, { 0xe, 0xd3 }, { 0xf, 0x00 },
+{ WAIT_IDLE }, { 0xe, 0xd4 }, { 0xf, 0x00 },
+{ WAIT_IDLE }, { 0xe, 0xd5 }, { 0xf, 0x00 },
+{ WAIT_IDLE }, { 0xe, 0xd6 }, { 0xf, 0x00 },
+{ WAIT_IDLE }, { 0xe, 0xd7 }, { 0xf, 0x00 },
+{ WAIT_IDLE }, { 0xe, 0xd8 }, { 0xf, 0x00 },
+{ WAIT_IDLE }, { 0xe, 0xd9 }, { 0xf, 0x00 },
+{ WAIT_IDLE }, { 0xe, 0xda }, { 0xf, 0x00 },
+{ WAIT_IDLE }, { 0xe, 0xdb }, { 0xf, 0x00 },
+{ WAIT_IDLE }, { 0xe, 0xdc }, { 0xf, 0x00 },
+{ WAIT_IDLE }, { 0xe, 0xdd }, { 0xf, 0x00 },
+{ WAIT_IDLE }, { 0xe, 0xde }, { 0xf, 0x00 },
+{ WAIT_IDLE }, { 0xe, 0xdf }, { 0xf, 0x00 },
+{ WAIT_IDLE }, { 0xe, 0xe0 }, { 0xf, 0x00 },
+{ WAIT_IDLE }, { 0xe, 0xe1 }, { 0xf, 0x00 },
+{ WAIT_IDLE }, { 0xe, 0xe2 }, { 0xf, 0x00 },
+{ WAIT_IDLE }, { 0xe, 0xe3 }, { 0xf, 0x00 },
+{ WAIT_IDLE }, { 0xe, 0xe4 }, { 0xf, 0x00 },
+{ WAIT_IDLE }, { 0xe, 0xe5 }, { 0xf, 0x00 },
+{ WAIT_IDLE }, { 0xe, 0xe6 }, { 0xf, 0x00 },
+{ WAIT_IDLE }, { 0xe, 0xe7 }, { 0xf, 0x00 },
+{ WAIT_IDLE }, { 0xe, 0xe8 }, { 0xf, 0x00 },
+{ WAIT_IDLE }, { 0xe, 0xe9 }, { 0xf, 0x00 },
+{ WAIT_IDLE }, { 0xe, 0xea }, { 0xf, 0x00 },
+{ WAIT_IDLE }, { 0xe, 0xeb }, { 0xf, 0x00 },
+{ WAIT_IDLE }, { 0xe, 0xec }, { 0xf, 0x00 },
+{ WAIT_IDLE }, { 0xe, 0xed }, { 0xf, 0x00 },
+{ WAIT_IDLE }, { 0xe, 0xee }, { 0xf, 0x00 },
+{ WAIT_IDLE }, { 0xe, 0xef }, { 0xf, 0x00 },
+{ WAIT_IDLE }, { 0xe, 0xf0 }, { 0xf, 0x00 },
+{ WAIT_IDLE }, { 0xe, 0xf1 }, { 0xf, 0x00 },
+{ WAIT_IDLE }, { 0xe, 0xf2 }, { 0xf, 0x00 },
+{ WAIT_IDLE }, { 0xe, 0xf3 }, { 0xf, 0x00 },
+{ WAIT_IDLE }, { 0xe, 0xf4 }, { 0xf, 0x00 },
+{ WAIT_IDLE }, { 0xe, 0xf5 }, { 0xf, 0x00 },
+{ WAIT_IDLE }, { 0xe, 0xf6 }, { 0xf, 0x00 },
+{ WAIT_IDLE }, { 0xe, 0xf7 }, { 0xf, 0x00 },
+{ WAIT_IDLE }, { 0xe, 0xf8 }, { 0xf, 0x00 },
+{ WAIT_IDLE }, { 0xe, 0xf9 }, { 0xf, 0x00 },
+{ WAIT_IDLE }, { 0xe, 0xfa }, { 0xf, 0x00 },
+{ WAIT_IDLE }, { 0xe, 0xfb }, { 0xf, 0x00 },
+{ WAIT_IDLE }, { 0xe, 0xfc }, { 0xf, 0x00 },
+{ WAIT_IDLE }, { 0xe, 0xfd }, { 0xf, 0x00 },
+{ WAIT_IDLE }, { 0xe, 0xfe }, { 0xf, 0x00 },
+{ WAIT_IDLE }, { 0xe, 0xff }, { 0xf, 0x00 },
+
+/* XXX But why do this twice? */
+{ WAIT_IDLE }, { 0xe, 0x10 }, { 0xf, 0x00 },
+{ WAIT_IDLE }, { 0xe, 0x11 }, { 0xf, 0x00 },
+{ WAIT_IDLE }, { 0xe, 0x12 }, { 0xf, 0x00 },
+{ WAIT_IDLE }, { 0xe, 0x13 }, { 0xf, 0x00 },
+{ WAIT_IDLE }, { 0xe, 0x14 }, { 0xf, 0x00 },
+{ WAIT_IDLE }, { 0xe, 0x15 }, { 0xf, 0x00 },
+{ WAIT_IDLE }, { 0xe, 0x16 }, { 0xf, 0x00 },
+{ WAIT_IDLE }, { 0xe, 0x17 }, { 0xf, 0x00 },
+{ WAIT_IDLE }, { 0xe, 0x18 }, { 0xf, 0x00 },
+{ WAIT_IDLE }, { 0xe, 0x19 }, { 0xf, 0x00 },
+{ WAIT_IDLE }, { 0xe, 0x1a }, { 0xf, 0x00 },
+{ WAIT_IDLE }, { 0xe, 0x1b }, { 0xf, 0x00 },
+{ WAIT_IDLE }, { 0xe, 0x1c }, { 0xf, 0x00 },
+{ WAIT_IDLE }, { 0xe, 0x1d }, { 0xf, 0x00 },
+{ WAIT_IDLE }, { 0xe, 0x1e }, { 0xf, 0x00 },
+{ WAIT_IDLE }, { 0xe, 0x1f }, { 0xf, 0x00 },
+{ WAIT_IDLE }, { 0xe, 0x20 }, { 0xf, 0x00 },
+{ WAIT_IDLE }, { 0xe, 0x21 }, { 0xf, 0x00 },
+{ WAIT_IDLE }, { 0xe, 0x22 }, { 0xf, 0x00 },
+{ WAIT_IDLE }, { 0xe, 0x23 }, { 0xf, 0x00 },
+{ WAIT_IDLE }, { 0xe, 0x24 }, { 0xf, 0x00 },
+{ WAIT_IDLE }, { 0xe, 0x25 }, { 0xf, 0x00 },
+{ WAIT_IDLE }, { 0xe, 0x26 }, { 0xf, 0x00 },
+{ WAIT_IDLE }, { 0xe, 0x27 }, { 0xf, 0x00 },
+{ WAIT_IDLE }, { 0xe, 0x28 }, { 0xf, 0x00 },
+{ WAIT_IDLE }, { 0xe, 0x29 }, { 0xf, 0x00 },
+{ WAIT_IDLE }, { 0xe, 0x2a }, { 0xf, 0x00 },
+{ WAIT_IDLE }, { 0xe, 0x2b }, { 0xf, 0x00 },
+{ WAIT_IDLE }, { 0xe, 0x2c }, { 0xf, 0x00 },
+{ WAIT_IDLE }, { 0xe, 0x2d }, { 0xf, 0x00 },
+{ WAIT_IDLE }, { 0xe, 0x2e }, { 0xf, 0x00 },
+{ WAIT_IDLE }, { 0xe, 0x2f }, { 0xf, 0x00 },
+{ WAIT_IDLE }, { 0xe, 0x30 }, { 0xf, 0x00 },
+{ WAIT_IDLE }, { 0xe, 0x31 }, { 0xf, 0x00 },
+{ WAIT_IDLE }, { 0xe, 0x32 }, { 0xf, 0x00 },
+{ WAIT_IDLE }, { 0xe, 0x33 }, { 0xf, 0x00 },
+{ WAIT_IDLE }, { 0xe, 0x34 }, { 0xf, 0x00 },
+{ WAIT_IDLE }, { 0xe, 0x35 }, { 0xf, 0x00 },
+{ WAIT_IDLE }, { 0xe, 0x36 }, { 0xf, 0x00 },
+{ WAIT_IDLE }, { 0xe, 0x37 }, { 0xf, 0x00 },
+{ WAIT_IDLE }, { 0xe, 0x38 }, { 0xf, 0x00 },
+{ WAIT_IDLE }, { 0xe, 0x39 }, { 0xf, 0x00 },
+{ WAIT_IDLE }, { 0xe, 0x3a }, { 0xf, 0x00 },
+{ WAIT_IDLE }, { 0xe, 0x3b }, { 0xf, 0x00 },
+{ WAIT_IDLE }, { 0xe, 0x3c }, { 0xf, 0x00 },
+{ WAIT_IDLE }, { 0xe, 0x3d }, { 0xf, 0x00 },
+{ WAIT_IDLE }, { 0xe, 0x3e }, { 0xf, 0x00 },
+{ WAIT_IDLE }, { 0xe, 0x3f }, { 0xf, 0x00 },
+{ WAIT_IDLE }, { 0xe, 0x40 }, { 0xf, 0x00 },
+{ WAIT_IDLE }, { 0xe, 0x41 }, { 0xf, 0x00 },
+{ WAIT_IDLE }, { 0xe, 0x42 }, { 0xf, 0x00 },
+{ WAIT_IDLE }, { 0xe, 0x43 }, { 0xf, 0x00 },
+{ WAIT_IDLE }, { 0xe, 0x44 }, { 0xf, 0x00 },
+{ WAIT_IDLE }, { 0xe, 0x45 }, { 0xf, 0x00 },
+{ WAIT_IDLE }, { 0xe, 0x46 }, { 0xf, 0x00 },
+{ WAIT_IDLE }, { 0xe, 0x47 }, { 0xf, 0x00 },
+{ WAIT_IDLE }, { 0xe, 0x48 }, { 0xf, 0x00 },
+{ WAIT_IDLE }, { 0xe, 0x49 }, { 0xf, 0x00 },
+{ WAIT_IDLE }, { 0xe, 0x4a }, { 0xf, 0x00 },
+{ WAIT_IDLE }, { 0xe, 0x4b }, { 0xf, 0x00 },
+{ WAIT_IDLE }, { 0xe, 0x4c }, { 0xf, 0x00 },
+{ WAIT_IDLE }, { 0xe, 0x4d }, { 0xf, 0x00 },
+{ WAIT_IDLE }, { 0xe, 0x4e }, { 0xf, 0x00 },
+{ WAIT_IDLE }, { 0xe, 0x4f }, { 0xf, 0x00 },
+{ WAIT_IDLE }, { 0xe, 0x50 }, { 0xf, 0x00 },
+{ WAIT_IDLE }, { 0xe, 0x51 }, { 0xf, 0x00 },
+{ WAIT_IDLE }, { 0xe, 0x52 }, { 0xf, 0x00 },
+{ WAIT_IDLE }, { 0xe, 0x53 }, { 0xf, 0x00 },
+{ WAIT_IDLE }, { 0xe, 0x54 }, { 0xf, 0x00 },
+{ WAIT_IDLE }, { 0xe, 0x55 }, { 0xf, 0x00 },
+{ WAIT_IDLE }, { 0xe, 0x56 }, { 0xf, 0x00 },
+{ WAIT_IDLE }, { 0xe, 0x57 }, { 0xf, 0x00 },
+{ WAIT_IDLE }, { 0xe, 0x58 }, { 0xf, 0x00 },
+{ WAIT_IDLE }, { 0xe, 0x59 }, { 0xf, 0x00 },
+{ WAIT_IDLE }, { 0xe, 0x5a }, { 0xf, 0x00 },
+{ WAIT_IDLE }, { 0xe, 0x5b }, { 0xf, 0x00 },
+{ WAIT_IDLE }, { 0xe, 0x5c }, { 0xf, 0x00 },
+{ WAIT_IDLE }, { 0xe, 0x5d }, { 0xf, 0x00 },
+{ WAIT_IDLE }, { 0xe, 0x5e }, { 0xf, 0x00 },
+{ WAIT_IDLE }, { 0xe, 0x5f }, { 0xf, 0x00 },
+{ WAIT_IDLE }, { 0xe, 0x60 }, { 0xf, 0x00 },
+{ WAIT_IDLE }, { 0xe, 0x61 }, { 0xf, 0x00 },
+{ WAIT_IDLE }, { 0xe, 0x62 }, { 0xf, 0x00 },
+{ WAIT_IDLE }, { 0xe, 0x63 }, { 0xf, 0x00 },
+{ WAIT_IDLE }, { 0xe, 0x64 }, { 0xf, 0x00 },
+{ WAIT_IDLE }, { 0xe, 0x65 }, { 0xf, 0x00 },
+{ WAIT_IDLE }, { 0xe, 0x66 }, { 0xf, 0x00 },
+{ WAIT_IDLE }, { 0xe, 0x67 }, { 0xf, 0x00 },
+{ WAIT_IDLE }, { 0xe, 0x68 }, { 0xf, 0x00 },
+{ WAIT_IDLE }, { 0xe, 0x69 }, { 0xf, 0x00 },
+{ WAIT_IDLE }, { 0xe, 0x6a }, { 0xf, 0x00 },
+{ WAIT_IDLE }, { 0xe, 0x6b }, { 0xf, 0x00 },
+{ WAIT_IDLE }, { 0xe, 0x6c }, { 0xf, 0x00 },
+{ WAIT_IDLE }, { 0xe, 0x6d }, { 0xf, 0x00 },
+{ WAIT_IDLE }, { 0xe, 0x6e }, { 0xf, 0x00 },
+{ WAIT_IDLE }, { 0xe, 0x6f }, { 0xf, 0x00 },
+{ WAIT_IDLE }, { 0xe, 0x70 }, { 0xf, 0x00 },
+{ WAIT_IDLE }, { 0xe, 0x71 }, { 0xf, 0x00 },
+{ WAIT_IDLE }, { 0xe, 0x72 }, { 0xf, 0x00 },
+{ WAIT_IDLE }, { 0xe, 0x73 }, { 0xf, 0x00 },
+{ WAIT_IDLE }, { 0xe, 0x74 }, { 0xf, 0x00 },
+{ WAIT_IDLE }, { 0xe, 0x75 }, { 0xf, 0x00 },
+{ WAIT_IDLE }, { 0xe, 0x76 }, { 0xf, 0x00 },
+{ WAIT_IDLE }, { 0xe, 0x77 }, { 0xf, 0x00 },
+{ WAIT_IDLE }, { 0xe, 0x78 }, { 0xf, 0x00 },
+{ WAIT_IDLE }, { 0xe, 0x79 }, { 0xf, 0x00 },
+{ WAIT_IDLE }, { 0xe, 0x7a }, { 0xf, 0x00 },
+{ WAIT_IDLE }, { 0xe, 0x7b }, { 0xf, 0x00 },
+{ WAIT_IDLE }, { 0xe, 0x7c }, { 0xf, 0x00 },
+{ WAIT_IDLE }, { 0xe, 0x7d }, { 0xf, 0x00 },
+{ WAIT_IDLE }, { 0xe, 0x7e }, { 0xf, 0x00 },
+{ WAIT_IDLE }, { 0xe, 0x7f }, { 0xf, 0x00 },
+{ WAIT_IDLE }, { 0xe, 0x80 }, { 0xf, 0x00 },
+{ WAIT_IDLE }, { 0xe, 0x81 }, { 0xf, 0x00 },
+{ WAIT_IDLE }, { 0xe, 0x82 }, { 0xf, 0x00 },
+{ WAIT_IDLE }, { 0xe, 0x83 }, { 0xf, 0x00 },
+{ WAIT_IDLE }, { 0xe, 0x84 }, { 0xf, 0x00 },
+{ WAIT_IDLE }, { 0xe, 0x85 }, { 0xf, 0x00 },
+{ WAIT_IDLE }, { 0xe, 0x86 }, { 0xf, 0x00 },
+{ WAIT_IDLE }, { 0xe, 0x87 }, { 0xf, 0x00 },
+{ WAIT_IDLE }, { 0xe, 0x88 }, { 0xf, 0x00 },
+{ WAIT_IDLE }, { 0xe, 0x89 }, { 0xf, 0x00 },
+{ WAIT_IDLE }, { 0xe, 0x8a }, { 0xf, 0x00 },
+{ WAIT_IDLE }, { 0xe, 0x8b }, { 0xf, 0x00 },
+{ WAIT_IDLE }, { 0xe, 0x8c }, { 0xf, 0x00 },
+{ WAIT_IDLE }, { 0xe, 0x8d }, { 0xf, 0x00 },
+{ WAIT_IDLE }, { 0xe, 0x8e }, { 0xf, 0x00 },
+{ WAIT_IDLE }, { 0xe, 0x8f }, { 0xf, 0x00 },
+{ WAIT_IDLE }, { 0xe, 0x90 }, { 0xf, 0x00 },
+{ WAIT_IDLE }, { 0xe, 0x91 }, { 0xf, 0x00 },
+{ WAIT_IDLE }, { 0xe, 0x92 }, { 0xf, 0x00 },
+{ WAIT_IDLE }, { 0xe, 0x93 }, { 0xf, 0x00 },
+{ WAIT_IDLE }, { 0xe, 0x94 }, { 0xf, 0x00 },
+{ WAIT_IDLE }, { 0xe, 0x95 }, { 0xf, 0x00 },
+{ WAIT_IDLE }, { 0xe, 0x96 }, { 0xf, 0x00 },
+{ WAIT_IDLE }, { 0xe, 0x97 }, { 0xf, 0x00 },
+{ WAIT_IDLE }, { 0xe, 0x98 }, { 0xf, 0x00 },
+{ WAIT_IDLE }, { 0xe, 0x99 }, { 0xf, 0x00 },
+{ WAIT_IDLE }, { 0xe, 0x9a }, { 0xf, 0x00 },
+{ WAIT_IDLE }, { 0xe, 0x9b }, { 0xf, 0x00 },
+{ WAIT_IDLE }, { 0xe, 0x9c }, { 0xf, 0x00 },
+{ WAIT_IDLE }, { 0xe, 0x9d }, { 0xf, 0x00 },
+{ WAIT_IDLE }, { 0xe, 0x9e }, { 0xf, 0x00 },
+{ WAIT_IDLE }, { 0xe, 0x9f }, { 0xf, 0x00 },
+{ WAIT_IDLE }, { 0xe, 0xa0 }, { 0xf, 0x00 },
+{ WAIT_IDLE }, { 0xe, 0xa1 }, { 0xf, 0x00 },
+{ WAIT_IDLE }, { 0xe, 0xa2 }, { 0xf, 0x00 },
+{ WAIT_IDLE }, { 0xe, 0xa3 }, { 0xf, 0x00 },
+{ WAIT_IDLE }, { 0xe, 0xa4 }, { 0xf, 0x00 },
+{ WAIT_IDLE }, { 0xe, 0xa5 }, { 0xf, 0x00 },
+{ WAIT_IDLE }, { 0xe, 0xa6 }, { 0xf, 0x00 },
+{ WAIT_IDLE }, { 0xe, 0xa7 }, { 0xf, 0x00 },
+{ WAIT_IDLE }, { 0xe, 0xa8 }, { 0xf, 0x00 },
+{ WAIT_IDLE }, { 0xe, 0xa9 }, { 0xf, 0x00 },
+{ WAIT_IDLE }, { 0xe, 0xaa }, { 0xf, 0x00 },
+{ WAIT_IDLE }, { 0xe, 0xab }, { 0xf, 0x00 },
+{ WAIT_IDLE }, { 0xe, 0xac }, { 0xf, 0x00 },
+{ WAIT_IDLE }, { 0xe, 0xad }, { 0xf, 0x00 },
+{ WAIT_IDLE }, { 0xe, 0xae }, { 0xf, 0x00 },
+{ WAIT_IDLE }, { 0xe, 0xaf }, { 0xf, 0x00 },
+{ WAIT_IDLE }, { 0xe, 0xb0 }, { 0xf, 0x00 },
+{ WAIT_IDLE }, { 0xe, 0xb1 }, { 0xf, 0x00 },
+{ WAIT_IDLE }, { 0xe, 0xb2 }, { 0xf, 0x00 },
+{ WAIT_IDLE }, { 0xe, 0xb3 }, { 0xf, 0x00 },
+{ WAIT_IDLE }, { 0xe, 0xb4 }, { 0xf, 0x00 },
+{ WAIT_IDLE }, { 0xe, 0xb5 }, { 0xf, 0x00 },
+{ WAIT_IDLE }, { 0xe, 0xb6 }, { 0xf, 0x00 },
+{ WAIT_IDLE }, { 0xe, 0xb7 }, { 0xf, 0x00 },
+{ WAIT_IDLE }, { 0xe, 0xb8 }, { 0xf, 0x00 },
+{ WAIT_IDLE }, { 0xe, 0xb9 }, { 0xf, 0x00 },
+{ WAIT_IDLE }, { 0xe, 0xba }, { 0xf, 0x00 },
+{ WAIT_IDLE }, { 0xe, 0xbb }, { 0xf, 0x00 },
+{ WAIT_IDLE }, { 0xe, 0xbc }, { 0xf, 0x00 },
+{ WAIT_IDLE }, { 0xe, 0xbd }, { 0xf, 0x00 },
+{ WAIT_IDLE }, { 0xe, 0xbe }, { 0xf, 0x00 },
+{ WAIT_IDLE }, { 0xe, 0xbf }, { 0xf, 0x00 },
+{ WAIT_IDLE }, { 0xe, 0xc0 }, { 0xf, 0x00 },
+{ WAIT_IDLE }, { 0xe, 0xc1 }, { 0xf, 0x00 },
+{ WAIT_IDLE }, { 0xe, 0xc2 }, { 0xf, 0x00 },
+{ WAIT_IDLE }, { 0xe, 0xc3 }, { 0xf, 0x00 },
+{ WAIT_IDLE }, { 0xe, 0xc4 }, { 0xf, 0x00 },
+{ WAIT_IDLE }, { 0xe, 0xc5 }, { 0xf, 0x00 },
+{ WAIT_IDLE }, { 0xe, 0xc6 }, { 0xf, 0x00 },
+{ WAIT_IDLE }, { 0xe, 0xc7 }, { 0xf, 0x00 },
+{ WAIT_IDLE }, { 0xe, 0xc8 }, { 0xf, 0x00 },
+{ WAIT_IDLE }, { 0xe, 0xc9 }, { 0xf, 0x00 },
+{ WAIT_IDLE }, { 0xe, 0xca }, { 0xf, 0x00 },
+{ WAIT_IDLE }, { 0xe, 0xcb }, { 0xf, 0x00 },
+{ WAIT_IDLE }, { 0xe, 0xcc }, { 0xf, 0x00 },
+{ WAIT_IDLE }, { 0xe, 0xcd }, { 0xf, 0x00 },
+{ WAIT_IDLE }, { 0xe, 0xce }, { 0xf, 0x00 },
+{ WAIT_IDLE }, { 0xe, 0xcf }, { 0xf, 0x00 },
+{ WAIT_IDLE }, { 0xe, 0xd0 }, { 0xf, 0x00 },
+{ WAIT_IDLE }, { 0xe, 0xd1 }, { 0xf, 0x00 },
+{ WAIT_IDLE }, { 0xe, 0xd2 }, { 0xf, 0x00 },
+{ WAIT_IDLE }, { 0xe, 0xd3 }, { 0xf, 0x00 },
+{ WAIT_IDLE }, { 0xe, 0xd4 }, { 0xf, 0x00 },
+{ WAIT_IDLE }, { 0xe, 0xd5 }, { 0xf, 0x00 },
+{ WAIT_IDLE }, { 0xe, 0xd6 }, { 0xf, 0x00 },
+{ WAIT_IDLE }, { 0xe, 0xd7 }, { 0xf, 0x00 },
+{ WAIT_IDLE }, { 0xe, 0xd8 }, { 0xf, 0x00 },
+{ WAIT_IDLE }, { 0xe, 0xd9 }, { 0xf, 0x00 },
+{ WAIT_IDLE }, { 0xe, 0xda }, { 0xf, 0x00 },
+{ WAIT_IDLE }, { 0xe, 0xdb }, { 0xf, 0x00 },
+{ WAIT_IDLE }, { 0xe, 0xdc }, { 0xf, 0x00 },
+{ WAIT_IDLE }, { 0xe, 0xdd }, { 0xf, 0x00 },
+{ WAIT_IDLE }, { 0xe, 0xde }, { 0xf, 0x00 },
+{ WAIT_IDLE }, { 0xe, 0xdf }, { 0xf, 0x00 },
+{ WAIT_IDLE }, { 0xe, 0xe0 }, { 0xf, 0x00 },
+{ WAIT_IDLE }, { 0xe, 0xe1 }, { 0xf, 0x00 },
+{ WAIT_IDLE }, { 0xe, 0xe2 }, { 0xf, 0x00 },
+{ WAIT_IDLE }, { 0xe, 0xe3 }, { 0xf, 0x00 },
+{ WAIT_IDLE }, { 0xe, 0xe4 }, { 0xf, 0x00 },
+{ WAIT_IDLE }, { 0xe, 0xe5 }, { 0xf, 0x00 },
+{ WAIT_IDLE }, { 0xe, 0xe6 }, { 0xf, 0x00 },
+{ WAIT_IDLE }, { 0xe, 0xe7 }, { 0xf, 0x00 },
+{ WAIT_IDLE }, { 0xe, 0xe8 }, { 0xf, 0x00 },
+{ WAIT_IDLE }, { 0xe, 0xe9 }, { 0xf, 0x00 },
+{ WAIT_IDLE }, { 0xe, 0xea }, { 0xf, 0x00 },
+{ WAIT_IDLE }, { 0xe, 0xeb }, { 0xf, 0x00 },
+{ WAIT_IDLE }, { 0xe, 0xec }, { 0xf, 0x00 },
+{ WAIT_IDLE }, { 0xe, 0xed }, { 0xf, 0x00 },
+{ WAIT_IDLE }, { 0xe, 0xee }, { 0xf, 0x00 },
+{ WAIT_IDLE }, { 0xe, 0xef }, { 0xf, 0x00 },
+{ WAIT_IDLE }, { 0xe, 0xf0 }, { 0xf, 0x00 },
+{ WAIT_IDLE }, { 0xe, 0xf1 }, { 0xf, 0x00 },
+{ WAIT_IDLE }, { 0xe, 0xf2 }, { 0xf, 0x00 },
+{ WAIT_IDLE }, { 0xe, 0xf3 }, { 0xf, 0x00 },
+{ WAIT_IDLE }, { 0xe, 0xf4 }, { 0xf, 0x00 },
+{ WAIT_IDLE }, { 0xe, 0xf5 }, { 0xf, 0x00 },
+{ WAIT_IDLE }, { 0xe, 0xf6 }, { 0xf, 0x00 },
+{ WAIT_IDLE }, { 0xe, 0xf7 }, { 0xf, 0x00 },
+{ WAIT_IDLE }, { 0xe, 0xf8 }, { 0xf, 0x00 },
+{ WAIT_IDLE }, { 0xe, 0xf9 }, { 0xf, 0x00 },
+{ WAIT_IDLE }, { 0xe, 0xfa }, { 0xf, 0x00 },
+{ WAIT_IDLE }, { 0xe, 0xfb }, { 0xf, 0x00 },
+{ WAIT_IDLE }, { 0xe, 0xfc }, { 0xf, 0x00 },
+{ WAIT_IDLE }, { 0xe, 0xfd }, { 0xf, 0x00 },
+{ WAIT_IDLE }, { 0xe, 0xfe }, { 0xf, 0x00 },
+{ WAIT_IDLE }, { 0xe, 0xff }, { 0xf, 0x00 },
+
+/* mute on */
+{ WAIT_IDLE }, { 0x8, 0x02 },
+
+{ WAIT_IDLE }, { 0xb, 0x07 }, { 0xa, 0x44 }, { 0xd, 0x00 }, { 0xc, 0x00 },
+{ WAIT_IDLE }, { 0xb, 0x07 }, { 0xa, 0x42 }, { 0xd, 0x00 }, { 0xc, 0x00 },
+{ WAIT_IDLE }, { 0xb, 0x07 }, { 0xa, 0x43 }, { 0xd, 0x00 }, { 0xc, 0x00 },
+{ WAIT_IDLE }, { 0xb, 0x07 }, { 0xa, 0x7c }, { 0xd, 0x00 }, { 0xc, 0x00 },
+{ WAIT_IDLE }, { 0xb, 0x07 }, { 0xa, 0x7e }, { 0xd, 0x00 }, { 0xc, 0x00 },
+{ WAIT_IDLE }, { 0xb, 0x07 }, { 0xa, 0x46 }, { 0xd, 0x00 }, { 0xc, 0x00 },
+{ WAIT_IDLE }, { 0xb, 0x07 }, { 0xa, 0x49 }, { 0xd, 0x00 }, { 0xc, 0x00 },
+{ WAIT_IDLE }, { 0xb, 0x07 }, { 0xa, 0x47 }, { 0xd, 0x00 }, { 0xc, 0x00 },
+{ WAIT_IDLE }, { 0xb, 0x07 }, { 0xa, 0x4a }, { 0xd, 0x00 }, { 0xc, 0x00 },
+
+/* either because of stupidity by TB's programmers, or because it
+ actually does something, rezero the MOD page. */
+{ WAIT_IDLE }, { 0xe, 0x10 }, { 0xf, 0x00 },
+{ WAIT_IDLE }, { 0xe, 0x11 }, { 0xf, 0x00 },
+{ WAIT_IDLE }, { 0xe, 0x12 }, { 0xf, 0x00 },
+{ WAIT_IDLE }, { 0xe, 0x13 }, { 0xf, 0x00 },
+{ WAIT_IDLE }, { 0xe, 0x14 }, { 0xf, 0x00 },
+{ WAIT_IDLE }, { 0xe, 0x15 }, { 0xf, 0x00 },
+{ WAIT_IDLE }, { 0xe, 0x16 }, { 0xf, 0x00 },
+{ WAIT_IDLE }, { 0xe, 0x17 }, { 0xf, 0x00 },
+{ WAIT_IDLE }, { 0xe, 0x18 }, { 0xf, 0x00 },
+{ WAIT_IDLE }, { 0xe, 0x19 }, { 0xf, 0x00 },
+{ WAIT_IDLE }, { 0xe, 0x1a }, { 0xf, 0x00 },
+{ WAIT_IDLE }, { 0xe, 0x1b }, { 0xf, 0x00 },
+{ WAIT_IDLE }, { 0xe, 0x1c }, { 0xf, 0x00 },
+{ WAIT_IDLE }, { 0xe, 0x1d }, { 0xf, 0x00 },
+{ WAIT_IDLE }, { 0xe, 0x1e }, { 0xf, 0x00 },
+{ WAIT_IDLE }, { 0xe, 0x1f }, { 0xf, 0x00 },
+{ WAIT_IDLE }, { 0xe, 0x20 }, { 0xf, 0x00 },
+{ WAIT_IDLE }, { 0xe, 0x21 }, { 0xf, 0x00 },
+{ WAIT_IDLE }, { 0xe, 0x22 }, { 0xf, 0x00 },
+{ WAIT_IDLE }, { 0xe, 0x23 }, { 0xf, 0x00 },
+{ WAIT_IDLE }, { 0xe, 0x24 }, { 0xf, 0x00 },
+{ WAIT_IDLE }, { 0xe, 0x25 }, { 0xf, 0x00 },
+{ WAIT_IDLE }, { 0xe, 0x26 }, { 0xf, 0x00 },
+{ WAIT_IDLE }, { 0xe, 0x27 }, { 0xf, 0x00 },
+{ WAIT_IDLE }, { 0xe, 0x28 }, { 0xf, 0x00 },
+{ WAIT_IDLE }, { 0xe, 0x29 }, { 0xf, 0x00 },
+{ WAIT_IDLE }, { 0xe, 0x2a }, { 0xf, 0x00 },
+{ WAIT_IDLE }, { 0xe, 0x2b }, { 0xf, 0x00 },
+{ WAIT_IDLE }, { 0xe, 0x2c }, { 0xf, 0x00 },
+{ WAIT_IDLE }, { 0xe, 0x2d }, { 0xf, 0x00 },
+{ WAIT_IDLE }, { 0xe, 0x2e }, { 0xf, 0x00 },
+{ WAIT_IDLE }, { 0xe, 0x2f }, { 0xf, 0x00 },
+{ WAIT_IDLE }, { 0xe, 0x30 }, { 0xf, 0x00 },
+{ WAIT_IDLE }, { 0xe, 0x31 }, { 0xf, 0x00 },
+{ WAIT_IDLE }, { 0xe, 0x32 }, { 0xf, 0x00 },
+{ WAIT_IDLE }, { 0xe, 0x33 }, { 0xf, 0x00 },
+{ WAIT_IDLE }, { 0xe, 0x34 }, { 0xf, 0x00 },
+{ WAIT_IDLE }, { 0xe, 0x35 }, { 0xf, 0x00 },
+{ WAIT_IDLE }, { 0xe, 0x36 }, { 0xf, 0x00 },
+{ WAIT_IDLE }, { 0xe, 0x37 }, { 0xf, 0x00 },
+{ WAIT_IDLE }, { 0xe, 0x38 }, { 0xf, 0x00 },
+{ WAIT_IDLE }, { 0xe, 0x39 }, { 0xf, 0x00 },
+{ WAIT_IDLE }, { 0xe, 0x3a }, { 0xf, 0x00 },
+{ WAIT_IDLE }, { 0xe, 0x3b }, { 0xf, 0x00 },
+{ WAIT_IDLE }, { 0xe, 0x3c }, { 0xf, 0x00 },
+{ WAIT_IDLE }, { 0xe, 0x3d }, { 0xf, 0x00 },
+{ WAIT_IDLE }, { 0xe, 0x3e }, { 0xf, 0x00 },
+{ WAIT_IDLE }, { 0xe, 0x3f }, { 0xf, 0x00 },
+{ WAIT_IDLE }, { 0xe, 0x40 }, { 0xf, 0x00 },
+{ WAIT_IDLE }, { 0xe, 0x41 }, { 0xf, 0x00 },
+{ WAIT_IDLE }, { 0xe, 0x42 }, { 0xf, 0x00 },
+{ WAIT_IDLE }, { 0xe, 0x43 }, { 0xf, 0x00 },
+{ WAIT_IDLE }, { 0xe, 0x44 }, { 0xf, 0x00 },
+{ WAIT_IDLE }, { 0xe, 0x45 }, { 0xf, 0x00 },
+{ WAIT_IDLE }, { 0xe, 0x46 }, { 0xf, 0x00 },
+{ WAIT_IDLE }, { 0xe, 0x47 }, { 0xf, 0x00 },
+{ WAIT_IDLE }, { 0xe, 0x48 }, { 0xf, 0x00 },
+{ WAIT_IDLE }, { 0xe, 0x49 }, { 0xf, 0x00 },
+{ WAIT_IDLE }, { 0xe, 0x4a }, { 0xf, 0x00 },
+{ WAIT_IDLE }, { 0xe, 0x4b }, { 0xf, 0x00 },
+{ WAIT_IDLE }, { 0xe, 0x4c }, { 0xf, 0x00 },
+{ WAIT_IDLE }, { 0xe, 0x4d }, { 0xf, 0x00 },
+{ WAIT_IDLE }, { 0xe, 0x4e }, { 0xf, 0x00 },
+{ WAIT_IDLE }, { 0xe, 0x4f }, { 0xf, 0x00 },
+{ WAIT_IDLE }, { 0xe, 0x50 }, { 0xf, 0x00 },
+{ WAIT_IDLE }, { 0xe, 0x51 }, { 0xf, 0x00 },
+{ WAIT_IDLE }, { 0xe, 0x52 }, { 0xf, 0x00 },
+{ WAIT_IDLE }, { 0xe, 0x53 }, { 0xf, 0x00 },
+{ WAIT_IDLE }, { 0xe, 0x54 }, { 0xf, 0x00 },
+{ WAIT_IDLE }, { 0xe, 0x55 }, { 0xf, 0x00 },
+{ WAIT_IDLE }, { 0xe, 0x56 }, { 0xf, 0x00 },
+{ WAIT_IDLE }, { 0xe, 0x57 }, { 0xf, 0x00 },
+{ WAIT_IDLE }, { 0xe, 0x58 }, { 0xf, 0x00 },
+{ WAIT_IDLE }, { 0xe, 0x59 }, { 0xf, 0x00 },
+{ WAIT_IDLE }, { 0xe, 0x5a }, { 0xf, 0x00 },
+{ WAIT_IDLE }, { 0xe, 0x5b }, { 0xf, 0x00 },
+{ WAIT_IDLE }, { 0xe, 0x5c }, { 0xf, 0x00 },
+{ WAIT_IDLE }, { 0xe, 0x5d }, { 0xf, 0x00 },
+{ WAIT_IDLE }, { 0xe, 0x5e }, { 0xf, 0x00 },
+{ WAIT_IDLE }, { 0xe, 0x5f }, { 0xf, 0x00 },
+{ WAIT_IDLE }, { 0xe, 0x60 }, { 0xf, 0x00 },
+{ WAIT_IDLE }, { 0xe, 0x61 }, { 0xf, 0x00 },
+{ WAIT_IDLE }, { 0xe, 0x62 }, { 0xf, 0x00 },
+{ WAIT_IDLE }, { 0xe, 0x63 }, { 0xf, 0x00 },
+{ WAIT_IDLE }, { 0xe, 0x64 }, { 0xf, 0x00 },
+{ WAIT_IDLE }, { 0xe, 0x65 }, { 0xf, 0x00 },
+{ WAIT_IDLE }, { 0xe, 0x66 }, { 0xf, 0x00 },
+{ WAIT_IDLE }, { 0xe, 0x67 }, { 0xf, 0x00 },
+{ WAIT_IDLE }, { 0xe, 0x68 }, { 0xf, 0x00 },
+{ WAIT_IDLE }, { 0xe, 0x69 }, { 0xf, 0x00 },
+{ WAIT_IDLE }, { 0xe, 0x6a }, { 0xf, 0x00 },
+{ WAIT_IDLE }, { 0xe, 0x6b }, { 0xf, 0x00 },
+{ WAIT_IDLE }, { 0xe, 0x6c }, { 0xf, 0x00 },
+{ WAIT_IDLE }, { 0xe, 0x6d }, { 0xf, 0x00 },
+{ WAIT_IDLE }, { 0xe, 0x6e }, { 0xf, 0x00 },
+{ WAIT_IDLE }, { 0xe, 0x6f }, { 0xf, 0x00 },
+{ WAIT_IDLE }, { 0xe, 0x70 }, { 0xf, 0x00 },
+{ WAIT_IDLE }, { 0xe, 0x71 }, { 0xf, 0x00 },
+{ WAIT_IDLE }, { 0xe, 0x72 }, { 0xf, 0x00 },
+{ WAIT_IDLE }, { 0xe, 0x73 }, { 0xf, 0x00 },
+{ WAIT_IDLE }, { 0xe, 0x74 }, { 0xf, 0x00 },
+{ WAIT_IDLE }, { 0xe, 0x75 }, { 0xf, 0x00 },
+{ WAIT_IDLE }, { 0xe, 0x76 }, { 0xf, 0x00 },
+{ WAIT_IDLE }, { 0xe, 0x77 }, { 0xf, 0x00 },
+{ WAIT_IDLE }, { 0xe, 0x78 }, { 0xf, 0x00 },
+{ WAIT_IDLE }, { 0xe, 0x79 }, { 0xf, 0x00 },
+{ WAIT_IDLE }, { 0xe, 0x7a }, { 0xf, 0x00 },
+{ WAIT_IDLE }, { 0xe, 0x7b }, { 0xf, 0x00 },
+{ WAIT_IDLE }, { 0xe, 0x7c }, { 0xf, 0x00 },
+{ WAIT_IDLE }, { 0xe, 0x7d }, { 0xf, 0x00 },
+{ WAIT_IDLE }, { 0xe, 0x7e }, { 0xf, 0x00 },
+{ WAIT_IDLE }, { 0xe, 0x7f }, { 0xf, 0x00 },
+{ WAIT_IDLE }, { 0xe, 0x80 }, { 0xf, 0x00 },
+{ WAIT_IDLE }, { 0xe, 0x81 }, { 0xf, 0x00 },
+{ WAIT_IDLE }, { 0xe, 0x82 }, { 0xf, 0x00 },
+{ WAIT_IDLE }, { 0xe, 0x83 }, { 0xf, 0x00 },
+{ WAIT_IDLE }, { 0xe, 0x84 }, { 0xf, 0x00 },
+{ WAIT_IDLE }, { 0xe, 0x85 }, { 0xf, 0x00 },
+{ WAIT_IDLE }, { 0xe, 0x86 }, { 0xf, 0x00 },
+{ WAIT_IDLE }, { 0xe, 0x87 }, { 0xf, 0x00 },
+{ WAIT_IDLE }, { 0xe, 0x88 }, { 0xf, 0x00 },
+{ WAIT_IDLE }, { 0xe, 0x89 }, { 0xf, 0x00 },
+{ WAIT_IDLE }, { 0xe, 0x8a }, { 0xf, 0x00 },
+{ WAIT_IDLE }, { 0xe, 0x8b }, { 0xf, 0x00 },
+{ WAIT_IDLE }, { 0xe, 0x8c }, { 0xf, 0x00 },
+{ WAIT_IDLE }, { 0xe, 0x8d }, { 0xf, 0x00 },
+{ WAIT_IDLE }, { 0xe, 0x8e }, { 0xf, 0x00 },
+{ WAIT_IDLE }, { 0xe, 0x8f }, { 0xf, 0x00 },
+{ WAIT_IDLE }, { 0xe, 0x90 }, { 0xf, 0x00 },
+{ WAIT_IDLE }, { 0xe, 0x91 }, { 0xf, 0x00 },
+{ WAIT_IDLE }, { 0xe, 0x92 }, { 0xf, 0x00 },
+{ WAIT_IDLE }, { 0xe, 0x93 }, { 0xf, 0x00 },
+{ WAIT_IDLE }, { 0xe, 0x94 }, { 0xf, 0x00 },
+{ WAIT_IDLE }, { 0xe, 0x95 }, { 0xf, 0x00 },
+{ WAIT_IDLE }, { 0xe, 0x96 }, { 0xf, 0x00 },
+{ WAIT_IDLE }, { 0xe, 0x97 }, { 0xf, 0x00 },
+{ WAIT_IDLE }, { 0xe, 0x98 }, { 0xf, 0x00 },
+{ WAIT_IDLE }, { 0xe, 0x99 }, { 0xf, 0x00 },
+{ WAIT_IDLE }, { 0xe, 0x9a }, { 0xf, 0x00 },
+{ WAIT_IDLE }, { 0xe, 0x9b }, { 0xf, 0x00 },
+{ WAIT_IDLE }, { 0xe, 0x9c }, { 0xf, 0x00 },
+{ WAIT_IDLE }, { 0xe, 0x9d }, { 0xf, 0x00 },
+{ WAIT_IDLE }, { 0xe, 0x9e }, { 0xf, 0x00 },
+{ WAIT_IDLE }, { 0xe, 0x9f }, { 0xf, 0x00 },
+{ WAIT_IDLE }, { 0xe, 0xa0 }, { 0xf, 0x00 },
+{ WAIT_IDLE }, { 0xe, 0xa1 }, { 0xf, 0x00 },
+{ WAIT_IDLE }, { 0xe, 0xa2 }, { 0xf, 0x00 },
+{ WAIT_IDLE }, { 0xe, 0xa3 }, { 0xf, 0x00 },
+{ WAIT_IDLE }, { 0xe, 0xa4 }, { 0xf, 0x00 },
+{ WAIT_IDLE }, { 0xe, 0xa5 }, { 0xf, 0x00 },
+{ WAIT_IDLE }, { 0xe, 0xa6 }, { 0xf, 0x00 },
+{ WAIT_IDLE }, { 0xe, 0xa7 }, { 0xf, 0x00 },
+{ WAIT_IDLE }, { 0xe, 0xa8 }, { 0xf, 0x00 },
+{ WAIT_IDLE }, { 0xe, 0xa9 }, { 0xf, 0x00 },
+{ WAIT_IDLE }, { 0xe, 0xaa }, { 0xf, 0x00 },
+{ WAIT_IDLE }, { 0xe, 0xab }, { 0xf, 0x00 },
+{ WAIT_IDLE }, { 0xe, 0xac }, { 0xf, 0x00 },
+{ WAIT_IDLE }, { 0xe, 0xad }, { 0xf, 0x00 },
+{ WAIT_IDLE }, { 0xe, 0xae }, { 0xf, 0x00 },
+{ WAIT_IDLE }, { 0xe, 0xaf }, { 0xf, 0x00 },
+{ WAIT_IDLE }, { 0xe, 0xb0 }, { 0xf, 0x00 },
+{ WAIT_IDLE }, { 0xe, 0xb1 }, { 0xf, 0x00 },
+{ WAIT_IDLE }, { 0xe, 0xb2 }, { 0xf, 0x00 },
+{ WAIT_IDLE }, { 0xe, 0xb3 }, { 0xf, 0x00 },
+{ WAIT_IDLE }, { 0xe, 0xb4 }, { 0xf, 0x00 },
+{ WAIT_IDLE }, { 0xe, 0xb5 }, { 0xf, 0x00 },
+{ WAIT_IDLE }, { 0xe, 0xb6 }, { 0xf, 0x00 },
+{ WAIT_IDLE }, { 0xe, 0xb7 }, { 0xf, 0x00 },
+{ WAIT_IDLE }, { 0xe, 0xb8 }, { 0xf, 0x00 },
+{ WAIT_IDLE }, { 0xe, 0xb9 }, { 0xf, 0x00 },
+{ WAIT_IDLE }, { 0xe, 0xba }, { 0xf, 0x00 },
+{ WAIT_IDLE }, { 0xe, 0xbb }, { 0xf, 0x00 },
+{ WAIT_IDLE }, { 0xe, 0xbc }, { 0xf, 0x00 },
+{ WAIT_IDLE }, { 0xe, 0xbd }, { 0xf, 0x00 },
+{ WAIT_IDLE }, { 0xe, 0xbe }, { 0xf, 0x00 },
+{ WAIT_IDLE }, { 0xe, 0xbf }, { 0xf, 0x00 },
+{ WAIT_IDLE }, { 0xe, 0xc0 }, { 0xf, 0x00 },
+{ WAIT_IDLE }, { 0xe, 0xc1 }, { 0xf, 0x00 },
+{ WAIT_IDLE }, { 0xe, 0xc2 }, { 0xf, 0x00 },
+{ WAIT_IDLE }, { 0xe, 0xc3 }, { 0xf, 0x00 },
+{ WAIT_IDLE }, { 0xe, 0xc4 }, { 0xf, 0x00 },
+{ WAIT_IDLE }, { 0xe, 0xc5 }, { 0xf, 0x00 },
+{ WAIT_IDLE }, { 0xe, 0xc6 }, { 0xf, 0x00 },
+{ WAIT_IDLE }, { 0xe, 0xc7 }, { 0xf, 0x00 },
+{ WAIT_IDLE }, { 0xe, 0xc8 }, { 0xf, 0x00 },
+{ WAIT_IDLE }, { 0xe, 0xc9 }, { 0xf, 0x00 },
+{ WAIT_IDLE }, { 0xe, 0xca }, { 0xf, 0x00 },
+{ WAIT_IDLE }, { 0xe, 0xcb }, { 0xf, 0x00 },
+{ WAIT_IDLE }, { 0xe, 0xcc }, { 0xf, 0x00 },
+{ WAIT_IDLE }, { 0xe, 0xcd }, { 0xf, 0x00 },
+{ WAIT_IDLE }, { 0xe, 0xce }, { 0xf, 0x00 },
+{ WAIT_IDLE }, { 0xe, 0xcf }, { 0xf, 0x00 },
+{ WAIT_IDLE }, { 0xe, 0xd0 }, { 0xf, 0x00 },
+{ WAIT_IDLE }, { 0xe, 0xd1 }, { 0xf, 0x00 },
+{ WAIT_IDLE }, { 0xe, 0xd2 }, { 0xf, 0x00 },
+{ WAIT_IDLE }, { 0xe, 0xd3 }, { 0xf, 0x00 },
+{ WAIT_IDLE }, { 0xe, 0xd4 }, { 0xf, 0x00 },
+{ WAIT_IDLE }, { 0xe, 0xd5 }, { 0xf, 0x00 },
+{ WAIT_IDLE }, { 0xe, 0xd6 }, { 0xf, 0x00 },
+{ WAIT_IDLE }, { 0xe, 0xd7 }, { 0xf, 0x00 },
+{ WAIT_IDLE }, { 0xe, 0xd8 }, { 0xf, 0x00 },
+{ WAIT_IDLE }, { 0xe, 0xd9 }, { 0xf, 0x00 },
+{ WAIT_IDLE }, { 0xe, 0xda }, { 0xf, 0x00 },
+{ WAIT_IDLE }, { 0xe, 0xdb }, { 0xf, 0x00 },
+{ WAIT_IDLE }, { 0xe, 0xdc }, { 0xf, 0x00 },
+{ WAIT_IDLE }, { 0xe, 0xdd }, { 0xf, 0x00 },
+{ WAIT_IDLE }, { 0xe, 0xde }, { 0xf, 0x00 },
+{ WAIT_IDLE }, { 0xe, 0xdf }, { 0xf, 0x00 },
+{ WAIT_IDLE }, { 0xe, 0xe0 }, { 0xf, 0x00 },
+{ WAIT_IDLE }, { 0xe, 0xe1 }, { 0xf, 0x00 },
+{ WAIT_IDLE }, { 0xe, 0xe2 }, { 0xf, 0x00 },
+{ WAIT_IDLE }, { 0xe, 0xe3 }, { 0xf, 0x00 },
+{ WAIT_IDLE }, { 0xe, 0xe4 }, { 0xf, 0x00 },
+{ WAIT_IDLE }, { 0xe, 0xe5 }, { 0xf, 0x00 },
+{ WAIT_IDLE }, { 0xe, 0xe6 }, { 0xf, 0x00 },
+{ WAIT_IDLE }, { 0xe, 0xe7 }, { 0xf, 0x00 },
+{ WAIT_IDLE }, { 0xe, 0xe8 }, { 0xf, 0x00 },
+{ WAIT_IDLE }, { 0xe, 0xe9 }, { 0xf, 0x00 },
+{ WAIT_IDLE }, { 0xe, 0xea }, { 0xf, 0x00 },
+{ WAIT_IDLE }, { 0xe, 0xeb }, { 0xf, 0x00 },
+{ WAIT_IDLE }, { 0xe, 0xec }, { 0xf, 0x00 },
+{ WAIT_IDLE }, { 0xe, 0xed }, { 0xf, 0x00 },
+{ WAIT_IDLE }, { 0xe, 0xee }, { 0xf, 0x00 },
+{ WAIT_IDLE }, { 0xe, 0xef }, { 0xf, 0x00 },
+{ WAIT_IDLE }, { 0xe, 0xf0 }, { 0xf, 0x00 },
+{ WAIT_IDLE }, { 0xe, 0xf1 }, { 0xf, 0x00 },
+{ WAIT_IDLE }, { 0xe, 0xf2 }, { 0xf, 0x00 },
+{ WAIT_IDLE }, { 0xe, 0xf3 }, { 0xf, 0x00 },
+{ WAIT_IDLE }, { 0xe, 0xf4 }, { 0xf, 0x00 },
+{ WAIT_IDLE }, { 0xe, 0xf5 }, { 0xf, 0x00 },
+{ WAIT_IDLE }, { 0xe, 0xf6 }, { 0xf, 0x00 },
+{ WAIT_IDLE }, { 0xe, 0xf7 }, { 0xf, 0x00 },
+{ WAIT_IDLE }, { 0xe, 0xf8 }, { 0xf, 0x00 },
+{ WAIT_IDLE }, { 0xe, 0xf9 }, { 0xf, 0x00 },
+{ WAIT_IDLE }, { 0xe, 0xfa }, { 0xf, 0x00 },
+{ WAIT_IDLE }, { 0xe, 0xfb }, { 0xf, 0x00 },
+{ WAIT_IDLE }, { 0xe, 0xfc }, { 0xf, 0x00 },
+{ WAIT_IDLE }, { 0xe, 0xfd }, { 0xf, 0x00 },
+{ WAIT_IDLE }, { 0xe, 0xfe }, { 0xf, 0x00 },
+{ WAIT_IDLE }, { 0xe, 0xff }, { 0xf, 0x00 },
+
+/* load page zero */
+{ 0x9, 0x05 }, { 0xb, 0x00 }, { 0xa, 0x00 },
+
+{ 0xd, 0x01 }, { 0xc, 0x7c }, { WAIT_IDLE },
+{ 0xd, 0x00 }, { 0xc, 0x1e }, { WAIT_IDLE },
+{ 0xd, 0x00 }, { 0xc, 0x00 }, { WAIT_IDLE },
+{ 0xd, 0x00 }, { 0xc, 0x00 }, { WAIT_IDLE },
+{ 0xd, 0x00 }, { 0xc, 0xf5 }, { WAIT_IDLE },
+{ 0xd, 0x00 }, { 0xc, 0x11 }, { WAIT_IDLE },
+{ 0xd, 0x00 }, { 0xc, 0x20 }, { WAIT_IDLE },
+{ 0xd, 0x00 }, { 0xc, 0x32 }, { WAIT_IDLE },
+{ 0xd, 0x00 }, { 0xc, 0x40 }, { WAIT_IDLE },
+{ 0xd, 0x00 }, { 0xc, 0x13 }, { WAIT_IDLE },
+{ 0xd, 0x00 }, { 0xc, 0x00 }, { WAIT_IDLE },
+{ 0xd, 0x00 }, { 0xc, 0x14 }, { WAIT_IDLE },
+{ 0xd, 0x02 }, { 0xc, 0x76 }, { WAIT_IDLE },
+{ 0xd, 0x00 }, { 0xc, 0x60 }, { WAIT_IDLE },
+{ 0xd, 0x00 }, { 0xc, 0x80 }, { WAIT_IDLE },
+{ 0xd, 0x02 }, { 0xc, 0x00 }, { WAIT_IDLE },
+{ 0xd, 0x00 }, { 0xc, 0x00 }, { WAIT_IDLE },
+{ 0xd, 0x00 }, { 0xc, 0x00 }, { WAIT_IDLE },
+{ 0xd, 0x02 }, { 0xc, 0x00 }, { WAIT_IDLE },
+{ 0xd, 0x00 }, { 0xc, 0x00 }, { WAIT_IDLE },
+{ 0xd, 0x00 }, { 0xc, 0x00 }, { WAIT_IDLE },
+{ 0xd, 0x00 }, { 0xc, 0x00 }, { WAIT_IDLE },
+{ 0xd, 0x00 }, { 0xc, 0x00 }, { WAIT_IDLE },
+{ 0xd, 0x00 }, { 0xc, 0x00 }, { WAIT_IDLE },
+{ 0xd, 0x00 }, { 0xc, 0x00 }, { WAIT_IDLE },
+{ 0xd, 0x00 }, { 0xc, 0x00 }, { WAIT_IDLE },
+{ 0xd, 0x00 }, { 0xc, 0x00 }, { WAIT_IDLE },
+{ 0xd, 0x00 }, { 0xc, 0x00 }, { WAIT_IDLE },
+{ 0xd, 0x00 }, { 0xc, 0x00 }, { WAIT_IDLE },
+{ 0xd, 0x00 }, { 0xc, 0x00 }, { WAIT_IDLE },
+{ 0xd, 0x00 }, { 0xc, 0x00 }, { WAIT_IDLE },
+{ 0xd, 0x00 }, { 0xc, 0x00 }, { WAIT_IDLE },
+{ 0xd, 0x00 }, { 0xc, 0x00 }, { WAIT_IDLE },
+{ 0xd, 0x00 }, { 0xc, 0x00 }, { WAIT_IDLE },
+{ 0xd, 0x00 }, { 0xc, 0x00 }, { WAIT_IDLE },
+{ 0xd, 0x00 }, { 0xc, 0x00 }, { WAIT_IDLE },
+{ 0xd, 0x00 }, { 0xc, 0x00 }, { WAIT_IDLE },
+{ 0xd, 0x00 }, { 0xc, 0x00 }, { WAIT_IDLE },
+{ 0xd, 0x00 }, { 0xc, 0x00 }, { WAIT_IDLE },
+{ 0xd, 0x00 }, { 0xc, 0x00 }, { WAIT_IDLE },
+{ 0xd, 0x00 }, { 0xc, 0x00 }, { WAIT_IDLE },
+{ 0xd, 0x00 }, { 0xc, 0x00 }, { WAIT_IDLE },
+{ 0xd, 0x00 }, { 0xc, 0x00 }, { WAIT_IDLE },
+{ 0xd, 0x00 }, { 0xc, 0x00 }, { WAIT_IDLE },
+{ 0xd, 0x00 }, { 0xc, 0x00 }, { WAIT_IDLE },
+{ 0xd, 0x00 }, { 0xc, 0x00 }, { WAIT_IDLE },
+{ 0xd, 0x00 }, { 0xc, 0x00 }, { WAIT_IDLE },
+{ 0xd, 0x00 }, { 0xc, 0x00 }, { WAIT_IDLE },
+{ 0xd, 0x00 }, { 0xc, 0x00 }, { WAIT_IDLE },
+{ 0xd, 0x00 }, { 0xc, 0x00 }, { WAIT_IDLE },
+{ 0xd, 0x00 }, { 0xc, 0x00 }, { WAIT_IDLE },
+{ 0xd, 0x00 }, { 0xc, 0x00 }, { WAIT_IDLE },
+{ 0xd, 0x00 }, { 0xc, 0x00 }, { WAIT_IDLE },
+{ 0xd, 0x00 }, { 0xc, 0x00 }, { WAIT_IDLE },
+{ 0xd, 0x00 }, { 0xc, 0x00 }, { WAIT_IDLE },
+{ 0xd, 0x00 }, { 0xc, 0x00 }, { WAIT_IDLE },
+{ 0xd, 0x00 }, { 0xc, 0x00 }, { WAIT_IDLE },
+{ 0xd, 0x00 }, { 0xc, 0x00 }, { WAIT_IDLE },
+{ 0xd, 0x00 }, { 0xc, 0x00 }, { WAIT_IDLE },
+{ 0xd, 0x00 }, { 0xc, 0x00 }, { WAIT_IDLE },
+{ 0xd, 0x00 }, { 0xc, 0x00 }, { WAIT_IDLE },
+{ 0xd, 0x00 }, { 0xc, 0x00 }, { WAIT_IDLE },
+{ 0xd, 0x00 }, { 0xc, 0x00 }, { WAIT_IDLE },
+{ 0xd, 0x00 }, { 0xc, 0x00 }, { WAIT_IDLE },
+{ 0xd, 0x00 }, { 0xc, 0x18 }, { WAIT_IDLE },
+{ 0xd, 0x00 }, { 0xc, 0x19 }, { WAIT_IDLE },
+{ 0xd, 0x01 }, { 0xc, 0x1a }, { WAIT_IDLE },
+{ 0xd, 0x01 }, { 0xc, 0x20 }, { WAIT_IDLE },
+{ 0xd, 0x01 }, { 0xc, 0x40 }, { WAIT_IDLE },
+{ 0xd, 0x01 }, { 0xc, 0x17 }, { WAIT_IDLE },
+{ 0xd, 0x00 }, { 0xc, 0x00 }, { WAIT_IDLE },
+{ 0xd, 0x01 }, { 0xc, 0x80 }, { WAIT_IDLE },
+{ 0xd, 0x01 }, { 0xc, 0x20 }, { WAIT_IDLE },
+{ 0xd, 0x00 }, { 0xc, 0x10 }, { WAIT_IDLE },
+{ 0xd, 0x01 }, { 0xc, 0xa0 }, { WAIT_IDLE },
+{ 0xd, 0x03 }, { 0xc, 0xd1 }, { WAIT_IDLE },
+{ 0xd, 0x00 }, { 0xc, 0x00 }, { WAIT_IDLE },
+{ 0xd, 0x01 }, { 0xc, 0xf2 }, { WAIT_IDLE },
+{ 0xd, 0x02 }, { 0xc, 0x00 }, { WAIT_IDLE },
+{ 0xd, 0x00 }, { 0xc, 0x13 }, { WAIT_IDLE },
+{ 0xd, 0x00 }, { 0xc, 0x00 }, { WAIT_IDLE },
+{ 0xd, 0x00 }, { 0xc, 0xf4 }, { WAIT_IDLE },
+{ 0xd, 0x02 }, { 0xc, 0xe0 }, { WAIT_IDLE },
+{ 0xd, 0x00 }, { 0xc, 0x15 }, { WAIT_IDLE },
+{ 0xd, 0x00 }, { 0xc, 0x00 }, { WAIT_IDLE },
+{ 0xd, 0x00 }, { 0xc, 0x16 }, { WAIT_IDLE },
+{ 0xd, 0x00 }, { 0xc, 0x00 }, { WAIT_IDLE },
+{ 0xd, 0x00 }, { 0xc, 0x17 }, { WAIT_IDLE },
+{ 0xd, 0x00 }, { 0xc, 0x20 }, { WAIT_IDLE },
+{ 0xd, 0x00 }, { 0xc, 0x00 }, { WAIT_IDLE },
+{ 0xd, 0x00 }, { 0xc, 0x20 }, { WAIT_IDLE },
+{ 0xd, 0x00 }, { 0xc, 0x50 }, { WAIT_IDLE },
+{ 0xd, 0x00 }, { 0xc, 0x00 }, { WAIT_IDLE },
+{ 0xd, 0x00 }, { 0xc, 0x40 }, { WAIT_IDLE },
+{ 0xd, 0x00 }, { 0xc, 0x00 }, { WAIT_IDLE },
+{ 0xd, 0x00 }, { 0xc, 0x71 }, { WAIT_IDLE },
+{ 0xd, 0x02 }, { 0xc, 0x00 }, { WAIT_IDLE },
+{ 0xd, 0x00 }, { 0xc, 0x60 }, { WAIT_IDLE },
+{ 0xd, 0x00 }, { 0xc, 0x00 }, { WAIT_IDLE },
+{ 0xd, 0x00 }, { 0xc, 0x92 }, { WAIT_IDLE },
+{ 0xd, 0x00 }, { 0xc, 0x00 }, { WAIT_IDLE },
+{ 0xd, 0x00 }, { 0xc, 0x80 }, { WAIT_IDLE },
+{ 0xd, 0x00 }, { 0xc, 0x00 }, { WAIT_IDLE },
+{ 0xd, 0x00 }, { 0xc, 0xb3 }, { WAIT_IDLE },
+{ 0xd, 0x02 }, { 0xc, 0x00 }, { WAIT_IDLE },
+{ 0xd, 0x00 }, { 0xc, 0xa0 }, { WAIT_IDLE },
+{ 0xd, 0x00 }, { 0xc, 0x00 }, { WAIT_IDLE },
+{ 0xd, 0x00 }, { 0xc, 0xd4 }, { WAIT_IDLE },
+{ 0xd, 0x00 }, { 0xc, 0x00 }, { WAIT_IDLE },
+{ 0xd, 0x00 }, { 0xc, 0x40 }, { WAIT_IDLE },
+{ 0xd, 0x00 }, { 0xc, 0x80 }, { WAIT_IDLE },
+{ 0xd, 0x00 }, { 0xc, 0xf5 }, { WAIT_IDLE },
+{ 0xd, 0x00 }, { 0xc, 0x20 }, { WAIT_IDLE },
+{ 0xd, 0x00 }, { 0xc, 0x70 }, { WAIT_IDLE },
+{ 0xd, 0x00 }, { 0xc, 0xa0 }, { WAIT_IDLE },
+{ 0xd, 0x02 }, { 0xc, 0x11 }, { WAIT_IDLE },
+{ 0xd, 0x00 }, { 0xc, 0x16 }, { WAIT_IDLE },
+{ 0xd, 0x00 }, { 0xc, 0x00 }, { WAIT_IDLE },
+{ 0xd, 0x00 }, { 0xc, 0x00 }, { WAIT_IDLE },
+{ 0xd, 0x00 }, { 0xc, 0x00 }, { WAIT_IDLE },
+{ 0xd, 0x00 }, { 0xc, 0x20 }, { WAIT_IDLE },
+{ 0xd, 0x02 }, { 0xc, 0x00 }, { WAIT_IDLE },
+{ 0xd, 0x00 }, { 0xc, 0x20 }, { WAIT_IDLE },
+{ 0xd, 0x00 }, { 0xc, 0x10 }, { WAIT_IDLE },
+{ 0xd, 0x00 }, { 0xc, 0x17 }, { WAIT_IDLE },
+{ 0xd, 0x00 }, { 0xc, 0x1b }, { WAIT_IDLE },
+{ 0xd, 0x00 }, { 0xc, 0x1d }, { WAIT_IDLE },
+{ 0xd, 0x02 }, { 0xc, 0xdf }, { WAIT_IDLE },
+
+/* Now load page one */
+{ 0x9, 0x05 }, { 0xb, 0x01 }, { 0xa, 0x00 },
+
+{ 0xd, 0x00 }, { 0xc, 0x00 }, { WAIT_IDLE },
+{ 0xd, 0x00 }, { 0xc, 0x00 }, { WAIT_IDLE },
+{ 0xd, 0x00 }, { 0xc, 0x00 }, { WAIT_IDLE },
+{ 0xd, 0x02 }, { 0xc, 0x00 }, { WAIT_IDLE },
+{ 0xd, 0x00 }, { 0xc, 0x19 }, { WAIT_IDLE },
+{ 0xd, 0x00 }, { 0xc, 0x1f }, { WAIT_IDLE },
+{ 0xd, 0x00 }, { 0xc, 0x00 }, { WAIT_IDLE },
+{ 0xd, 0x00 }, { 0xc, 0x00 }, { WAIT_IDLE },
+{ 0xd, 0x00 }, { 0xc, 0x00 }, { WAIT_IDLE },
+{ 0xd, 0x03 }, { 0xc, 0xd8 }, { WAIT_IDLE },
+{ 0xd, 0x00 }, { 0xc, 0x00 }, { WAIT_IDLE },
+{ 0xd, 0x02 }, { 0xc, 0x20 }, { WAIT_IDLE },
+{ 0xd, 0x00 }, { 0xc, 0x19 }, { WAIT_IDLE },
+{ 0xd, 0x00 }, { 0xc, 0x00 }, { WAIT_IDLE },
+{ 0xd, 0x00 }, { 0xc, 0x00 }, { WAIT_IDLE },
+{ 0xd, 0x00 }, { 0xc, 0x18 }, { WAIT_IDLE },
+{ 0xd, 0x01 }, { 0xc, 0xc0 }, { WAIT_IDLE },
+{ 0xd, 0x01 }, { 0xc, 0xfa }, { WAIT_IDLE },
+{ 0xd, 0x00 }, { 0xc, 0x1a }, { WAIT_IDLE },
+{ 0xd, 0x00 }, { 0xc, 0x00 }, { WAIT_IDLE },
+{ 0xd, 0x00 }, { 0xc, 0x00 }, { WAIT_IDLE },
+{ 0xd, 0x00 }, { 0xc, 0x00 }, { WAIT_IDLE },
+{ 0xd, 0x00 }, { 0xc, 0x00 }, { WAIT_IDLE },
+{ 0xd, 0x00 }, { 0xc, 0x00 }, { WAIT_IDLE },
+{ 0xd, 0x00 }, { 0xc, 0x00 }, { WAIT_IDLE },
+{ 0xd, 0x00 }, { 0xc, 0x00 }, { WAIT_IDLE },
+{ 0xd, 0x00 }, { 0xc, 0x00 }, { WAIT_IDLE },
+{ 0xd, 0x00 }, { 0xc, 0x00 }, { WAIT_IDLE },
+{ 0xd, 0x00 }, { 0xc, 0x00 }, { WAIT_IDLE },
+{ 0xd, 0x00 }, { 0xc, 0x00 }, { WAIT_IDLE },
+{ 0xd, 0x00 }, { 0xc, 0x00 }, { WAIT_IDLE },
+{ 0xd, 0x00 }, { 0xc, 0x00 }, { WAIT_IDLE },
+{ 0xd, 0x00 }, { 0xc, 0x00 }, { WAIT_IDLE },
+{ 0xd, 0x00 }, { 0xc, 0x00 }, { WAIT_IDLE },
+{ 0xd, 0x00 }, { 0xc, 0x00 }, { WAIT_IDLE },
+{ 0xd, 0x00 }, { 0xc, 0x00 }, { WAIT_IDLE },
+{ 0xd, 0x00 }, { 0xc, 0x00 }, { WAIT_IDLE },
+{ 0xd, 0x00 }, { 0xc, 0x00 }, { WAIT_IDLE },
+{ 0xd, 0x00 }, { 0xc, 0x00 }, { WAIT_IDLE },
+{ 0xd, 0x00 }, { 0xc, 0x00 }, { WAIT_IDLE },
+{ 0xd, 0x00 }, { 0xc, 0x00 }, { WAIT_IDLE },
+{ 0xd, 0x00 }, { 0xc, 0x00 }, { WAIT_IDLE },
+{ 0xd, 0x00 }, { 0xc, 0x00 }, { WAIT_IDLE },
+{ 0xd, 0x00 }, { 0xc, 0x00 }, { WAIT_IDLE },
+{ 0xd, 0x00 }, { 0xc, 0x00 }, { WAIT_IDLE },
+{ 0xd, 0x00 }, { 0xc, 0x00 }, { WAIT_IDLE },
+{ 0xd, 0x00 }, { 0xc, 0x00 }, { WAIT_IDLE },
+{ 0xd, 0x00 }, { 0xc, 0x00 }, { WAIT_IDLE },
+{ 0xd, 0x00 }, { 0xc, 0x00 }, { WAIT_IDLE },
+{ 0xd, 0x00 }, { 0xc, 0x00 }, { WAIT_IDLE },
+{ 0xd, 0x00 }, { 0xc, 0x00 }, { WAIT_IDLE },
+{ 0xd, 0x00 }, { 0xc, 0x00 }, { WAIT_IDLE },
+{ 0xd, 0x00 }, { 0xc, 0x00 }, { WAIT_IDLE },
+{ 0xd, 0x00 }, { 0xc, 0x00 }, { WAIT_IDLE },
+{ 0xd, 0x00 }, { 0xc, 0x00 }, { WAIT_IDLE },
+{ 0xd, 0x00 }, { 0xc, 0x00 }, { WAIT_IDLE },
+{ 0xd, 0x00 }, { 0xc, 0x00 }, { WAIT_IDLE },
+{ 0xd, 0x00 }, { 0xc, 0x00 }, { WAIT_IDLE },
+{ 0xd, 0x00 }, { 0xc, 0x00 }, { WAIT_IDLE },
+{ 0xd, 0x00 }, { 0xc, 0x00 }, { WAIT_IDLE },
+{ 0xd, 0x00 }, { 0xc, 0x00 }, { WAIT_IDLE },
+{ 0xd, 0x00 }, { 0xc, 0x00 }, { WAIT_IDLE },
+{ 0xd, 0x00 }, { 0xc, 0x00 }, { WAIT_IDLE },
+{ 0xd, 0x00 }, { 0xc, 0x00 }, { WAIT_IDLE },
+{ 0xd, 0x02 }, { 0xc, 0x40 }, { WAIT_IDLE },
+{ 0xd, 0x02 }, { 0xc, 0x60 }, { WAIT_IDLE },
+{ 0xd, 0x00 }, { 0xc, 0x00 }, { WAIT_IDLE },
+{ 0xd, 0x00 }, { 0xc, 0x00 }, { WAIT_IDLE },
+{ 0xd, 0x00 }, { 0xc, 0x00 }, { WAIT_IDLE },
+{ 0xd, 0x02 }, { 0xc, 0xc0 }, { WAIT_IDLE },
+{ 0xd, 0x02 }, { 0xc, 0x80 }, { WAIT_IDLE },
+{ 0xd, 0x00 }, { 0xc, 0x00 }, { WAIT_IDLE },
+{ 0xd, 0x02 }, { 0xc, 0xfb }, { WAIT_IDLE },
+{ 0xd, 0x02 }, { 0xc, 0xa0 }, { WAIT_IDLE },
+{ 0xd, 0x00 }, { 0xc, 0x00 }, { WAIT_IDLE },
+{ 0xd, 0x00 }, { 0xc, 0x1b }, { WAIT_IDLE },
+{ 0xd, 0x02 }, { 0xc, 0xd7 }, { WAIT_IDLE },
+{ 0xd, 0x00 }, { 0xc, 0x00 }, { WAIT_IDLE },
+{ 0xd, 0x02 }, { 0xc, 0xf7 }, { WAIT_IDLE },
+{ 0xd, 0x03 }, { 0xc, 0x20 }, { WAIT_IDLE },
+{ 0xd, 0x03 }, { 0xc, 0x00 }, { WAIT_IDLE },
+{ 0xd, 0x00 }, { 0xc, 0x00 }, { WAIT_IDLE },
+{ 0xd, 0x00 }, { 0xc, 0x1c }, { WAIT_IDLE },
+{ 0xd, 0x03 }, { 0xc, 0x3c }, { WAIT_IDLE },
+{ 0xd, 0x00 }, { 0xc, 0x00 }, { WAIT_IDLE },
+{ 0xd, 0x03 }, { 0xc, 0x3f }, { WAIT_IDLE },
+{ 0xd, 0x00 }, { 0xc, 0x00 }, { WAIT_IDLE },
+{ 0xd, 0x03 }, { 0xc, 0xc0 }, { WAIT_IDLE },
+{ 0xd, 0x00 }, { 0xc, 0x00 }, { WAIT_IDLE },
+{ 0xd, 0x03 }, { 0xc, 0xdf }, { WAIT_IDLE },
+{ 0xd, 0x00 }, { 0xc, 0x00 }, { WAIT_IDLE },
+{ 0xd, 0x00 }, { 0xc, 0x00 }, { WAIT_IDLE },
+{ 0xd, 0x03 }, { 0xc, 0x5d }, { WAIT_IDLE },
+{ 0xd, 0x00 }, { 0xc, 0x00 }, { WAIT_IDLE },
+{ 0xd, 0x03 }, { 0xc, 0xc0 }, { WAIT_IDLE },
+{ 0xd, 0x00 }, { 0xc, 0x00 }, { WAIT_IDLE },
+{ 0xd, 0x03 }, { 0xc, 0x7d }, { WAIT_IDLE },
+{ 0xd, 0x00 }, { 0xc, 0x00 }, { WAIT_IDLE },
+{ 0xd, 0x03 }, { 0xc, 0xc0 }, { WAIT_IDLE },
+{ 0xd, 0x00 }, { 0xc, 0x00 }, { WAIT_IDLE },
+{ 0xd, 0x03 }, { 0xc, 0x9e }, { WAIT_IDLE },
+{ 0xd, 0x00 }, { 0xc, 0x00 }, { WAIT_IDLE },
+{ 0xd, 0x03 }, { 0xc, 0xc0 }, { WAIT_IDLE },
+{ 0xd, 0x00 }, { 0xc, 0x00 }, { WAIT_IDLE },
+{ 0xd, 0x03 }, { 0xc, 0xbe }, { WAIT_IDLE },
+{ 0xd, 0x00 }, { 0xc, 0x00 }, { WAIT_IDLE },
+{ 0xd, 0x03 }, { 0xc, 0xc0 }, { WAIT_IDLE },
+{ 0xd, 0x00 }, { 0xc, 0x00 }, { WAIT_IDLE },
+{ 0xd, 0x00 }, { 0xc, 0x00 }, { WAIT_IDLE },
+{ 0xd, 0x00 }, { 0xc, 0x00 }, { WAIT_IDLE },
+{ 0xd, 0x00 }, { 0xc, 0x00 }, { WAIT_IDLE },
+{ 0xd, 0x00 }, { 0xc, 0x1b }, { WAIT_IDLE },
+{ 0xd, 0x00 }, { 0xc, 0x00 }, { WAIT_IDLE },
+{ 0xd, 0x00 }, { 0xc, 0x00 }, { WAIT_IDLE },
+{ 0xd, 0x00 }, { 0xc, 0x00 }, { WAIT_IDLE },
+{ 0xd, 0x02 }, { 0xc, 0xdb }, { WAIT_IDLE },
+{ 0xd, 0x00 }, { 0xc, 0x00 }, { WAIT_IDLE },
+{ 0xd, 0x02 }, { 0xc, 0xdb }, { WAIT_IDLE },
+{ 0xd, 0x00 }, { 0xc, 0x00 }, { WAIT_IDLE },
+{ 0xd, 0x02 }, { 0xc, 0xe0 }, { WAIT_IDLE },
+{ 0xd, 0x00 }, { 0xc, 0x00 }, { WAIT_IDLE },
+{ 0xd, 0x02 }, { 0xc, 0xfb }, { WAIT_IDLE },
+{ 0xd, 0x00 }, { 0xc, 0x00 }, { WAIT_IDLE },
+{ 0xd, 0x02 }, { 0xc, 0xc0 }, { WAIT_IDLE },
+{ 0xd, 0x02 }, { 0xc, 0x40 }, { WAIT_IDLE },
+{ 0xd, 0x02 }, { 0xc, 0xfb }, { WAIT_IDLE },
+{ 0xd, 0x02 }, { 0xc, 0x60 }, { WAIT_IDLE },
+{ 0xd, 0x00 }, { 0xc, 0x1b }, { WAIT_IDLE },
+
+{ 0x9, 0x05 }, { 0xb, 0x02 }, { 0xa, 0x00 },
+
+{ 0xc, 0xc4 }, { WAIT_IDLE },
+{ 0xc, 0x00 }, { WAIT_IDLE },
+{ 0xc, 0x44 }, { WAIT_IDLE },
+{ 0xc, 0x07 }, { WAIT_IDLE },
+{ 0xc, 0x44 }, { WAIT_IDLE },
+{ 0xc, 0x00 }, { WAIT_IDLE },
+{ 0xc, 0x40 }, { WAIT_IDLE },
+{ 0xc, 0x25 }, { WAIT_IDLE },
+{ 0xc, 0x01 }, { WAIT_IDLE },
+{ 0xc, 0x06 }, { WAIT_IDLE },
+{ 0xc, 0xc4 }, { WAIT_IDLE },
+{ 0xc, 0x07 }, { WAIT_IDLE },
+{ 0xc, 0x40 }, { WAIT_IDLE },
+{ 0xc, 0x25 }, { WAIT_IDLE },
+{ 0xc, 0x01 }, { WAIT_IDLE },
+{ 0xc, 0x00 }, { WAIT_IDLE },
+{ 0xc, 0x46 }, { WAIT_IDLE },
+{ 0xc, 0x46 }, { WAIT_IDLE },
+{ 0xc, 0x00 }, { WAIT_IDLE },
+{ 0xc, 0x00 }, { WAIT_IDLE },
+{ 0xc, 0x00 }, { WAIT_IDLE },
+{ 0xc, 0x00 }, { WAIT_IDLE },
+{ 0xc, 0x00 }, { WAIT_IDLE },
+{ 0xc, 0x00 }, { WAIT_IDLE },
+{ 0xc, 0x00 }, { WAIT_IDLE },
+{ 0xc, 0x00 }, { WAIT_IDLE },
+{ 0xc, 0x00 }, { WAIT_IDLE },
+{ 0xc, 0x00 }, { WAIT_IDLE },
+{ 0xc, 0x00 }, { WAIT_IDLE },
+{ 0xc, 0x00 }, { WAIT_IDLE },
+{ 0xc, 0x00 }, { WAIT_IDLE },
+{ 0xc, 0x00 }, { WAIT_IDLE },
+{ 0xc, 0x00 }, { WAIT_IDLE },
+{ 0xc, 0x00 }, { WAIT_IDLE },
+{ 0xc, 0x00 }, { WAIT_IDLE },
+{ 0xc, 0x00 }, { WAIT_IDLE },
+{ 0xc, 0x00 }, { WAIT_IDLE },
+{ 0xc, 0x00 }, { WAIT_IDLE },
+{ 0xc, 0x00 }, { WAIT_IDLE },
+{ 0xc, 0x00 }, { WAIT_IDLE },
+{ 0xc, 0x00 }, { WAIT_IDLE },
+{ 0xc, 0x00 }, { WAIT_IDLE },
+{ 0xc, 0x00 }, { WAIT_IDLE },
+{ 0xc, 0x00 }, { WAIT_IDLE },
+{ 0xc, 0x00 }, { WAIT_IDLE },
+{ 0xc, 0x00 }, { WAIT_IDLE },
+{ 0xc, 0x00 }, { WAIT_IDLE },
+{ 0xc, 0x00 }, { WAIT_IDLE },
+{ 0xc, 0x00 }, { WAIT_IDLE },
+{ 0xc, 0x00 }, { WAIT_IDLE },
+{ 0xc, 0x00 }, { WAIT_IDLE },
+{ 0xc, 0x00 }, { WAIT_IDLE },
+{ 0xc, 0x00 }, { WAIT_IDLE },
+{ 0xc, 0x00 }, { WAIT_IDLE },
+{ 0xc, 0x00 }, { WAIT_IDLE },
+{ 0xc, 0x00 }, { WAIT_IDLE },
+{ 0xc, 0x00 }, { WAIT_IDLE },
+{ 0xc, 0x00 }, { WAIT_IDLE },
+{ 0xc, 0x00 }, { WAIT_IDLE },
+{ 0xc, 0x00 }, { WAIT_IDLE },
+{ 0xc, 0x00 }, { WAIT_IDLE },
+{ 0xc, 0x00 }, { WAIT_IDLE },
+{ 0xc, 0x00 }, { WAIT_IDLE },
+{ 0xc, 0x00 }, { WAIT_IDLE },
+{ 0xc, 0x46 }, { WAIT_IDLE },
+{ 0xc, 0x07 }, { WAIT_IDLE },
+{ 0xc, 0x05 }, { WAIT_IDLE },
+{ 0xc, 0x05 }, { WAIT_IDLE },
+{ 0xc, 0x05 }, { WAIT_IDLE },
+{ 0xc, 0x04 }, { WAIT_IDLE },
+{ 0xc, 0x07 }, { WAIT_IDLE },
+{ 0xc, 0x05 }, { WAIT_IDLE },
+{ 0xc, 0x04 }, { WAIT_IDLE },
+{ 0xc, 0x07 }, { WAIT_IDLE },
+{ 0xc, 0x05 }, { WAIT_IDLE },
+{ 0xc, 0x44 }, { WAIT_IDLE },
+{ 0xc, 0x46 }, { WAIT_IDLE },
+{ 0xc, 0x44 }, { WAIT_IDLE },
+{ 0xc, 0x46 }, { WAIT_IDLE },
+{ 0xc, 0x46 }, { WAIT_IDLE },
+{ 0xc, 0x07 }, { WAIT_IDLE },
+{ 0xc, 0x05 }, { WAIT_IDLE },
+{ 0xc, 0x44 }, { WAIT_IDLE },
+{ 0xc, 0x46 }, { WAIT_IDLE },
+{ 0xc, 0x05 }, { WAIT_IDLE },
+{ 0xc, 0x46 }, { WAIT_IDLE },
+{ 0xc, 0x05 }, { WAIT_IDLE },
+{ 0xc, 0x46 }, { WAIT_IDLE },
+{ 0xc, 0x05 }, { WAIT_IDLE },
+{ 0xc, 0x46 }, { WAIT_IDLE },
+{ 0xc, 0x05 }, { WAIT_IDLE },
+{ 0xc, 0x44 }, { WAIT_IDLE },
+{ 0xc, 0x46 }, { WAIT_IDLE },
+{ 0xc, 0x05 }, { WAIT_IDLE },
+{ 0xc, 0x07 }, { WAIT_IDLE },
+{ 0xc, 0x44 }, { WAIT_IDLE },
+{ 0xc, 0x46 }, { WAIT_IDLE },
+{ 0xc, 0x05 }, { WAIT_IDLE },
+{ 0xc, 0x07 }, { WAIT_IDLE },
+{ 0xc, 0x44 }, { WAIT_IDLE },
+{ 0xc, 0x46 }, { WAIT_IDLE },
+{ 0xc, 0x05 }, { WAIT_IDLE },
+{ 0xc, 0x07 }, { WAIT_IDLE },
+{ 0xc, 0x44 }, { WAIT_IDLE },
+{ 0xc, 0x46 }, { WAIT_IDLE },
+{ 0xc, 0x05 }, { WAIT_IDLE },
+{ 0xc, 0x07 }, { WAIT_IDLE },
+{ 0xc, 0x44 }, { WAIT_IDLE },
+{ 0xc, 0x05 }, { WAIT_IDLE },
+{ 0xc, 0x05 }, { WAIT_IDLE },
+{ 0xc, 0x05 }, { WAIT_IDLE },
+{ 0xc, 0x44 }, { WAIT_IDLE },
+{ 0xc, 0x05 }, { WAIT_IDLE },
+{ 0xc, 0x05 }, { WAIT_IDLE },
+{ 0xc, 0x05 }, { WAIT_IDLE },
+{ 0xc, 0x46 }, { WAIT_IDLE },
+{ 0xc, 0x05 }, { WAIT_IDLE },
+{ 0xc, 0x46 }, { WAIT_IDLE },
+{ 0xc, 0x05 }, { WAIT_IDLE },
+{ 0xc, 0x46 }, { WAIT_IDLE },
+{ 0xc, 0x05 }, { WAIT_IDLE },
+{ 0xc, 0x46 }, { WAIT_IDLE },
+{ 0xc, 0x05 }, { WAIT_IDLE },
+{ 0xc, 0x46 }, { WAIT_IDLE },
+{ 0xc, 0x07 }, { WAIT_IDLE },
+{ 0xc, 0x46 }, { WAIT_IDLE },
+{ 0xc, 0x07 }, { WAIT_IDLE },
+{ 0xc, 0x44 }, { WAIT_IDLE },
+
+{ 0x9, 0x05 }, { 0xb, 0x03 }, { 0xa, 0x00 },
+
+{ 0xc, 0x07 }, { WAIT_IDLE },
+{ 0xc, 0x40 }, { WAIT_IDLE },
+{ 0xc, 0x00 }, { WAIT_IDLE },
+{ 0xc, 0x00 }, { WAIT_IDLE },
+{ 0xc, 0x00 }, { WAIT_IDLE },
+{ 0xc, 0x47 }, { WAIT_IDLE },
+{ 0xc, 0x00 }, { WAIT_IDLE },
+{ 0xc, 0x40 }, { WAIT_IDLE },
+{ 0xc, 0x00 }, { WAIT_IDLE },
+{ 0xc, 0x40 }, { WAIT_IDLE },
+{ 0xc, 0x06 }, { WAIT_IDLE },
+{ 0xc, 0x40 }, { WAIT_IDLE },
+{ 0xc, 0x00 }, { WAIT_IDLE },
+{ 0xc, 0x00 }, { WAIT_IDLE },
+{ 0xc, 0x00 }, { WAIT_IDLE },
+{ 0xc, 0x00 }, { WAIT_IDLE },
+{ 0xc, 0x00 }, { WAIT_IDLE },
+{ 0xc, 0x00 }, { WAIT_IDLE },
+{ 0xc, 0x00 }, { WAIT_IDLE },
+{ 0xc, 0x00 }, { WAIT_IDLE },
+{ 0xc, 0x00 }, { WAIT_IDLE },
+{ 0xc, 0x00 }, { WAIT_IDLE },
+{ 0xc, 0x00 }, { WAIT_IDLE },
+{ 0xc, 0x00 }, { WAIT_IDLE },
+{ 0xc, 0x00 }, { WAIT_IDLE },
+{ 0xc, 0x00 }, { WAIT_IDLE },
+{ 0xc, 0x00 }, { WAIT_IDLE },
+{ 0xc, 0x00 }, { WAIT_IDLE },
+{ 0xc, 0x00 }, { WAIT_IDLE },
+{ 0xc, 0x00 }, { WAIT_IDLE },
+{ 0xc, 0x00 }, { WAIT_IDLE },
+{ 0xc, 0x00 }, { WAIT_IDLE },
+{ 0xc, 0x00 }, { WAIT_IDLE },
+{ 0xc, 0x00 }, { WAIT_IDLE },
+{ 0xc, 0x00 }, { WAIT_IDLE },
+{ 0xc, 0x00 }, { WAIT_IDLE },
+{ 0xc, 0x00 }, { WAIT_IDLE },
+{ 0xc, 0x00 }, { WAIT_IDLE },
+{ 0xc, 0x00 }, { WAIT_IDLE },
+{ 0xc, 0x00 }, { WAIT_IDLE },
+{ 0xc, 0x00 }, { WAIT_IDLE },
+{ 0xc, 0x00 }, { WAIT_IDLE },
+{ 0xc, 0x00 }, { WAIT_IDLE },
+{ 0xc, 0x00 }, { WAIT_IDLE },
+{ 0xc, 0x00 }, { WAIT_IDLE },
+{ 0xc, 0x00 }, { WAIT_IDLE },
+{ 0xc, 0x00 }, { WAIT_IDLE },
+{ 0xc, 0x00 }, { WAIT_IDLE },
+{ 0xc, 0x00 }, { WAIT_IDLE },
+{ 0xc, 0x00 }, { WAIT_IDLE },
+{ 0xc, 0x00 }, { WAIT_IDLE },
+{ 0xc, 0x00 }, { WAIT_IDLE },
+{ 0xc, 0x00 }, { WAIT_IDLE },
+{ 0xc, 0x00 }, { WAIT_IDLE },
+{ 0xc, 0x00 }, { WAIT_IDLE },
+{ 0xc, 0x00 }, { WAIT_IDLE },
+{ 0xc, 0x00 }, { WAIT_IDLE },
+{ 0xc, 0x00 }, { WAIT_IDLE },
+{ 0xc, 0x00 }, { WAIT_IDLE },
+{ 0xc, 0x00 }, { WAIT_IDLE },
+{ 0xc, 0x00 }, { WAIT_IDLE },
+{ 0xc, 0x00 }, { WAIT_IDLE },
+{ 0xc, 0x00 }, { WAIT_IDLE },
+{ 0xc, 0x00 }, { WAIT_IDLE },
+{ 0xc, 0x80 }, { WAIT_IDLE },
+{ 0xc, 0x80 }, { WAIT_IDLE },
+{ 0xc, 0xc0 }, { WAIT_IDLE },
+{ 0xc, 0x00 }, { WAIT_IDLE },
+{ 0xc, 0x00 }, { WAIT_IDLE },
+{ 0xc, 0x40 }, { WAIT_IDLE },
+{ 0xc, 0x00 }, { WAIT_IDLE },
+{ 0xc, 0x00 }, { WAIT_IDLE },
+{ 0xc, 0x00 }, { WAIT_IDLE },
+{ 0xc, 0x40 }, { WAIT_IDLE },
+{ 0xc, 0x00 }, { WAIT_IDLE },
+{ 0xc, 0x40 }, { WAIT_IDLE },
+{ 0xc, 0x00 }, { WAIT_IDLE },
+{ 0xc, 0x60 }, { WAIT_IDLE },
+{ 0xc, 0x00 }, { WAIT_IDLE },
+{ 0xc, 0x70 }, { WAIT_IDLE },
+{ 0xc, 0x00 }, { WAIT_IDLE },
+{ 0xc, 0x40 }, { WAIT_IDLE },
+{ 0xc, 0x00 }, { WAIT_IDLE },
+{ 0xc, 0x40 }, { WAIT_IDLE },
+{ 0xc, 0x00 }, { WAIT_IDLE },
+{ 0xc, 0x42 }, { WAIT_IDLE },
+{ 0xc, 0x00 }, { WAIT_IDLE },
+{ 0xc, 0x40 }, { WAIT_IDLE },
+{ 0xc, 0x00 }, { WAIT_IDLE },
+{ 0xc, 0x02 }, { WAIT_IDLE },
+{ 0xc, 0x00 }, { WAIT_IDLE },
+{ 0xc, 0x40 }, { WAIT_IDLE },
+{ 0xc, 0x00 }, { WAIT_IDLE },
+{ 0xc, 0x00 }, { WAIT_IDLE },
+{ 0xc, 0x00 }, { WAIT_IDLE },
+{ 0xc, 0x40 }, { WAIT_IDLE },
+{ 0xc, 0x00 }, { WAIT_IDLE },
+{ 0xc, 0x00 }, { WAIT_IDLE },
+{ 0xc, 0x00 }, { WAIT_IDLE },
+{ 0xc, 0x40 }, { WAIT_IDLE },
+{ 0xc, 0x00 }, { WAIT_IDLE },
+{ 0xc, 0x00 }, { WAIT_IDLE },
+{ 0xc, 0x00 }, { WAIT_IDLE },
+{ 0xc, 0x40 }, { WAIT_IDLE },
+{ 0xc, 0x00 }, { WAIT_IDLE },
+{ 0xc, 0x00 }, { WAIT_IDLE },
+{ 0xc, 0x00 }, { WAIT_IDLE },
+{ 0xc, 0x40 }, { WAIT_IDLE },
+{ 0xc, 0x00 }, { WAIT_IDLE },
+{ 0xc, 0x00 }, { WAIT_IDLE },
+{ 0xc, 0x00 }, { WAIT_IDLE },
+{ 0xc, 0x42 }, { WAIT_IDLE },
+{ 0xc, 0x00 }, { WAIT_IDLE },
+{ 0xc, 0x40 }, { WAIT_IDLE },
+{ 0xc, 0x00 }, { WAIT_IDLE },
+{ 0xc, 0x42 }, { WAIT_IDLE },
+{ 0xc, 0x00 }, { WAIT_IDLE },
+{ 0xc, 0x02 }, { WAIT_IDLE },
+{ 0xc, 0x00 }, { WAIT_IDLE },
+{ 0xc, 0x02 }, { WAIT_IDLE },
+{ 0xc, 0x00 }, { WAIT_IDLE },
+{ 0xc, 0x02 }, { WAIT_IDLE },
+{ 0xc, 0x00 }, { WAIT_IDLE },
+{ 0xc, 0x42 }, { WAIT_IDLE },
+{ 0xc, 0x00 }, { WAIT_IDLE },
+{ 0xc, 0xc0 }, { WAIT_IDLE },
+{ 0xc, 0x00 }, { WAIT_IDLE },
+{ 0xc, 0x40 }, { WAIT_IDLE },
+
+{ 0x9, 0x05 }, { 0xb, 0x04 }, { 0xa, 0x00 },
+
+{ 0xc, 0x63 }, { WAIT_IDLE },
+{ 0xc, 0x03 }, { WAIT_IDLE },
+{ 0xc, 0x26 }, { WAIT_IDLE },
+{ 0xc, 0x02 }, { WAIT_IDLE },
+{ 0xc, 0x2c }, { WAIT_IDLE },
+{ 0xc, 0x00 }, { WAIT_IDLE },
+{ 0xc, 0x24 }, { WAIT_IDLE },
+{ 0xc, 0x00 }, { WAIT_IDLE },
+{ 0xc, 0x2e }, { WAIT_IDLE },
+{ 0xc, 0x02 }, { WAIT_IDLE },
+{ 0xc, 0x02 }, { WAIT_IDLE },
+{ 0xc, 0x02 }, { WAIT_IDLE },
+{ 0xc, 0x00 }, { WAIT_IDLE },
+{ 0xc, 0x00 }, { WAIT_IDLE },
+{ 0xc, 0x00 }, { WAIT_IDLE },
+{ 0xc, 0x00 }, { WAIT_IDLE },
+{ 0xc, 0x00 }, { WAIT_IDLE },
+{ 0xc, 0x00 }, { WAIT_IDLE },
+{ 0xc, 0x00 }, { WAIT_IDLE },
+{ 0xc, 0x00 }, { WAIT_IDLE },
+{ 0xc, 0x00 }, { WAIT_IDLE },
+{ 0xc, 0x00 }, { WAIT_IDLE },
+{ 0xc, 0x00 }, { WAIT_IDLE },
+{ 0xc, 0x00 }, { WAIT_IDLE },
+{ 0xc, 0x00 }, { WAIT_IDLE },
+{ 0xc, 0x00 }, { WAIT_IDLE },
+{ 0xc, 0x00 }, { WAIT_IDLE },
+{ 0xc, 0x00 }, { WAIT_IDLE },
+{ 0xc, 0x00 }, { WAIT_IDLE },
+{ 0xc, 0x00 }, { WAIT_IDLE },
+{ 0xc, 0x00 }, { WAIT_IDLE },
+{ 0xc, 0x00 }, { WAIT_IDLE },
+{ 0xc, 0x00 }, { WAIT_IDLE },
+{ 0xc, 0x00 }, { WAIT_IDLE },
+{ 0xc, 0x00 }, { WAIT_IDLE },
+{ 0xc, 0x00 }, { WAIT_IDLE },
+{ 0xc, 0x00 }, { WAIT_IDLE },
+{ 0xc, 0x00 }, { WAIT_IDLE },
+{ 0xc, 0x00 }, { WAIT_IDLE },
+{ 0xc, 0x00 }, { WAIT_IDLE },
+{ 0xc, 0x00 }, { WAIT_IDLE },
+{ 0xc, 0x00 }, { WAIT_IDLE },
+{ 0xc, 0x00 }, { WAIT_IDLE },
+{ 0xc, 0x00 }, { WAIT_IDLE },
+{ 0xc, 0x00 }, { WAIT_IDLE },
+{ 0xc, 0x00 }, { WAIT_IDLE },
+{ 0xc, 0x00 }, { WAIT_IDLE },
+{ 0xc, 0x00 }, { WAIT_IDLE },
+{ 0xc, 0x00 }, { WAIT_IDLE },
+{ 0xc, 0x00 }, { WAIT_IDLE },
+{ 0xc, 0x00 }, { WAIT_IDLE },
+{ 0xc, 0x00 }, { WAIT_IDLE },
+{ 0xc, 0x00 }, { WAIT_IDLE },
+{ 0xc, 0x00 }, { WAIT_IDLE },
+{ 0xc, 0x00 }, { WAIT_IDLE },
+{ 0xc, 0x00 }, { WAIT_IDLE },
+{ 0xc, 0x00 }, { WAIT_IDLE },
+{ 0xc, 0x00 }, { WAIT_IDLE },
+{ 0xc, 0x00 }, { WAIT_IDLE },
+{ 0xc, 0x00 }, { WAIT_IDLE },
+{ 0xc, 0x00 }, { WAIT_IDLE },
+{ 0xc, 0x00 }, { WAIT_IDLE },
+{ 0xc, 0x00 }, { WAIT_IDLE },
+{ 0xc, 0x00 }, { WAIT_IDLE },
+{ 0xc, 0x00 }, { WAIT_IDLE },
+{ 0xc, 0x01 }, { WAIT_IDLE },
+{ 0xc, 0x20 }, { WAIT_IDLE },
+{ 0xc, 0x00 }, { WAIT_IDLE },
+{ 0xc, 0x60 }, { WAIT_IDLE },
+{ 0xc, 0x00 }, { WAIT_IDLE },
+{ 0xc, 0x20 }, { WAIT_IDLE },
+{ 0xc, 0x00 }, { WAIT_IDLE },
+{ 0xc, 0x20 }, { WAIT_IDLE },
+{ 0xc, 0x00 }, { WAIT_IDLE },
+{ 0xc, 0x20 }, { WAIT_IDLE },
+{ 0xc, 0x00 }, { WAIT_IDLE },
+{ 0xc, 0x20 }, { WAIT_IDLE },
+{ 0xc, 0x00 }, { WAIT_IDLE },
+{ 0xc, 0x20 }, { WAIT_IDLE },
+{ 0xc, 0x00 }, { WAIT_IDLE },
+{ 0xc, 0x20 }, { WAIT_IDLE },
+{ 0xc, 0x00 }, { WAIT_IDLE },
+{ 0xc, 0x20 }, { WAIT_IDLE },
+{ 0xc, 0x00 }, { WAIT_IDLE },
+{ 0xc, 0x20 }, { WAIT_IDLE },
+{ 0xc, 0x00 }, { WAIT_IDLE },
+{ 0xc, 0x60 }, { WAIT_IDLE },
+{ 0xc, 0x00 }, { WAIT_IDLE },
+{ 0xc, 0x20 }, { WAIT_IDLE },
+{ 0xc, 0x00 }, { WAIT_IDLE },
+{ 0xc, 0x60 }, { WAIT_IDLE },
+{ 0xc, 0x00 }, { WAIT_IDLE },
+{ 0xc, 0x20 }, { WAIT_IDLE },
+{ 0xc, 0x00 }, { WAIT_IDLE },
+{ 0xc, 0x60 }, { WAIT_IDLE },
+{ 0xc, 0x00 }, { WAIT_IDLE },
+{ 0xc, 0x20 }, { WAIT_IDLE },
+{ 0xc, 0x00 }, { WAIT_IDLE },
+{ 0xc, 0x60 }, { WAIT_IDLE },
+{ 0xc, 0x00 }, { WAIT_IDLE },
+{ 0xc, 0x20 }, { WAIT_IDLE },
+{ 0xc, 0x00 }, { WAIT_IDLE },
+{ 0xc, 0x60 }, { WAIT_IDLE },
+{ 0xc, 0x00 }, { WAIT_IDLE },
+{ 0xc, 0x20 }, { WAIT_IDLE },
+{ 0xc, 0x00 }, { WAIT_IDLE },
+{ 0xc, 0x60 }, { WAIT_IDLE },
+{ 0xc, 0x00 }, { WAIT_IDLE },
+{ 0xc, 0x20 }, { WAIT_IDLE },
+{ 0xc, 0x00 }, { WAIT_IDLE },
+{ 0xc, 0x20 }, { WAIT_IDLE },
+{ 0xc, 0x00 }, { WAIT_IDLE },
+{ 0xc, 0x22 }, { WAIT_IDLE },
+{ 0xc, 0x02 }, { WAIT_IDLE },
+{ 0xc, 0x22 }, { WAIT_IDLE },
+{ 0xc, 0x02 }, { WAIT_IDLE },
+{ 0xc, 0x20 }, { WAIT_IDLE },
+{ 0xc, 0x00 }, { WAIT_IDLE },
+{ 0xc, 0x60 }, { WAIT_IDLE },
+{ 0xc, 0x00 }, { WAIT_IDLE },
+{ 0xc, 0x22 }, { WAIT_IDLE },
+{ 0xc, 0x02 }, { WAIT_IDLE },
+{ 0xc, 0x62 }, { WAIT_IDLE },
+{ 0xc, 0x02 }, { WAIT_IDLE },
+{ 0xc, 0x20 }, { WAIT_IDLE },
+{ 0xc, 0x01 }, { WAIT_IDLE },
+{ 0xc, 0x21 }, { WAIT_IDLE },
+{ 0xc, 0x01 }, { WAIT_IDLE },
+
+/* Load memory area (page six) */
+{ 0x9, 0x01 }, { 0xb, 0x06 },
+
+{ 0xa, 0x00 }, { 0xd, 0x00 }, { 0xc, 0x00 }, { WAIT_IDLE },
+{ 0xa, 0x02 }, { 0xd, 0x00 }, { 0xc, 0x00 }, { WAIT_IDLE },
+{ 0xa, 0x04 }, { 0xd, 0x00 }, { 0xc, 0x00 }, { WAIT_IDLE },
+{ 0xa, 0x06 }, { 0xd, 0x00 }, { 0xc, 0x00 }, { WAIT_IDLE },
+{ 0xa, 0x08 }, { 0xd, 0x00 }, { 0xc, 0x00 }, { WAIT_IDLE },
+{ 0xa, 0x0a }, { 0xd, 0x00 }, { 0xc, 0x00 }, { WAIT_IDLE },
+{ 0xa, 0x0c }, { 0xd, 0x00 }, { 0xc, 0x00 }, { WAIT_IDLE },
+{ 0xa, 0x0e }, { 0xd, 0x00 }, { 0xc, 0x00 }, { WAIT_IDLE },
+{ 0xa, 0x10 }, { 0xd, 0x00 }, { 0xc, 0x00 }, { WAIT_IDLE },
+{ 0xa, 0x12 }, { 0xd, 0x00 }, { 0xc, 0x00 }, { WAIT_IDLE },
+{ 0xa, 0x14 }, { 0xd, 0x00 }, { 0xc, 0x00 }, { WAIT_IDLE },
+{ 0xa, 0x16 }, { 0xd, 0x00 }, { 0xc, 0x00 }, { WAIT_IDLE },
+{ 0xa, 0x18 }, { 0xd, 0x00 }, { 0xc, 0x00 }, { WAIT_IDLE },
+{ 0xa, 0x1a }, { 0xd, 0x00 }, { 0xc, 0x00 }, { WAIT_IDLE },
+{ 0xa, 0x1c }, { 0xd, 0x00 }, { 0xc, 0x00 }, { WAIT_IDLE },
+{ 0xa, 0x1e }, { 0xd, 0x00 }, { 0xc, 0x00 }, { WAIT_IDLE },
+{ 0xa, 0x20 }, { 0xd, 0x00 }, { 0xc, 0x00 }, { WAIT_IDLE },
+{ 0xa, 0x22 }, { 0xd, 0x00 }, { 0xc, 0x00 }, { WAIT_IDLE },
+{ 0xa, 0x24 }, { 0xd, 0x00 }, { 0xc, 0x00 }, { WAIT_IDLE },
+{ 0xa, 0x26 }, { 0xd, 0x00 }, { 0xc, 0x00 }, { WAIT_IDLE },
+{ 0xa, 0x28 }, { 0xd, 0x00 }, { 0xc, 0x00 }, { WAIT_IDLE },
+{ 0xa, 0x2a }, { 0xd, 0x00 }, { 0xc, 0x00 }, { WAIT_IDLE },
+{ 0xa, 0x2c }, { 0xd, 0x00 }, { 0xc, 0x00 }, { WAIT_IDLE },
+{ 0xa, 0x2e }, { 0xd, 0x00 }, { 0xc, 0x00 }, { WAIT_IDLE },
+{ 0xa, 0x30 }, { 0xd, 0x00 }, { 0xc, 0x00 }, { WAIT_IDLE },
+{ 0xa, 0x32 }, { 0xd, 0x00 }, { 0xc, 0x00 }, { WAIT_IDLE },
+{ 0xa, 0x34 }, { 0xd, 0x00 }, { 0xc, 0x00 }, { WAIT_IDLE },
+{ 0xa, 0x36 }, { 0xd, 0x00 }, { 0xc, 0x00 }, { WAIT_IDLE },
+{ 0xa, 0x38 }, { 0xd, 0x00 }, { 0xc, 0x00 }, { WAIT_IDLE },
+{ 0xa, 0x3a }, { 0xd, 0x00 }, { 0xc, 0x00 }, { WAIT_IDLE },
+{ 0xa, 0x3c }, { 0xd, 0x00 }, { 0xc, 0x00 }, { WAIT_IDLE },
+{ 0xa, 0x3e }, { 0xd, 0x00 }, { 0xc, 0x00 }, { WAIT_IDLE },
+{ 0xa, 0x40 }, { 0xd, 0x00 }, { 0xc, 0x00 }, { WAIT_IDLE },
+{ 0xa, 0x42 }, { 0xd, 0x03 }, { 0xc, 0x00 }, { WAIT_IDLE },
+{ 0xa, 0x44 }, { 0xd, 0x01 }, { 0xc, 0x00 }, { WAIT_IDLE },
+{ 0xa, 0x46 }, { 0xd, 0x0a }, { 0xc, 0x21 }, { WAIT_IDLE },
+{ 0xa, 0x48 }, { 0xd, 0x0d }, { 0xc, 0x23 }, { WAIT_IDLE },
+{ 0xa, 0x4a }, { 0xd, 0x23 }, { 0xc, 0x1b }, { WAIT_IDLE },
+{ 0xa, 0x4c }, { 0xd, 0x37 }, { 0xc, 0x8f }, { WAIT_IDLE },
+{ 0xa, 0x4e }, { 0xd, 0x45 }, { 0xc, 0x77 }, { WAIT_IDLE },
+{ 0xa, 0x50 }, { 0xd, 0x52 }, { 0xc, 0xe2 }, { WAIT_IDLE },
+{ 0xa, 0x52 }, { 0xd, 0x1c }, { 0xc, 0x92 }, { WAIT_IDLE },
+{ 0xa, 0x54 }, { 0xd, 0x1c }, { 0xc, 0x52 }, { WAIT_IDLE },
+{ 0xa, 0x56 }, { 0xd, 0x07 }, { 0xc, 0x00 }, { WAIT_IDLE },
+{ 0xa, 0x58 }, { 0xd, 0x2f }, { 0xc, 0xc6 }, { WAIT_IDLE },
+{ 0xa, 0x5a }, { 0xd, 0x0b }, { 0xc, 0x00 }, { WAIT_IDLE },
+{ 0xa, 0x5c }, { 0xd, 0x30 }, { 0xc, 0x06 }, { WAIT_IDLE },
+{ 0xa, 0x5e }, { 0xd, 0x17 }, { 0xc, 0x00 }, { WAIT_IDLE },
+{ 0xa, 0x60 }, { 0xd, 0x3d }, { 0xc, 0xda }, { WAIT_IDLE },
+{ 0xa, 0x62 }, { 0xd, 0x29 }, { 0xc, 0x00 }, { WAIT_IDLE },
+{ 0xa, 0x64 }, { 0xd, 0x3e }, { 0xc, 0x41 }, { WAIT_IDLE },
+{ 0xa, 0x66 }, { 0xd, 0x39 }, { 0xc, 0x00 }, { WAIT_IDLE },
+{ 0xa, 0x68 }, { 0xd, 0x4c }, { 0xc, 0x48 }, { WAIT_IDLE },
+{ 0xa, 0x6a }, { 0xd, 0x49 }, { 0xc, 0x00 }, { WAIT_IDLE },
+{ 0xa, 0x6c }, { 0xd, 0x4c }, { 0xc, 0x6c }, { WAIT_IDLE },
+{ 0xa, 0x6e }, { 0xd, 0x11 }, { 0xc, 0xd2 }, { WAIT_IDLE },
+{ 0xa, 0x70 }, { 0xd, 0x16 }, { 0xc, 0x0c }, { WAIT_IDLE },
+{ 0xa, 0x72 }, { 0xd, 0x00 }, { 0xc, 0x00 }, { WAIT_IDLE },
+{ 0xa, 0x74 }, { 0xd, 0x00 }, { 0xc, 0x80 }, { WAIT_IDLE },
+{ 0xa, 0x76 }, { 0xd, 0x0f }, { 0xc, 0x00 }, { WAIT_IDLE },
+{ 0xa, 0x78 }, { 0xd, 0x00 }, { 0xc, 0x80 }, { WAIT_IDLE },
+{ 0xa, 0x7a }, { 0xd, 0x13 }, { 0xc, 0x00 }, { WAIT_IDLE },
+{ 0xa, 0x7c }, { 0xd, 0x80 }, { 0xc, 0x00 }, { WAIT_IDLE },
+{ 0xa, 0x7e }, { 0xd, 0x80 }, { 0xc, 0x80 }, { WAIT_IDLE },
+
+{ 0x9, 0x05 }, { 0xb, 0x07 }, { 0xa, 0x00 },
+
+{ 0xd, 0x0f }, { 0xc, 0xff }, { WAIT_IDLE },
+{ 0xd, 0x00 }, { 0xc, 0x00 }, { WAIT_IDLE },
+{ 0xd, 0x08 }, { 0xc, 0x00 }, { WAIT_IDLE },
+{ 0xd, 0x08 }, { 0xc, 0x00 }, { WAIT_IDLE },
+{ 0xd, 0x02 }, { 0xc, 0x00 }, { WAIT_IDLE },
+{ 0xd, 0x00 }, { 0xc, 0x00 }, { WAIT_IDLE },
+{ 0xd, 0x00 }, { 0xc, 0x00 }, { WAIT_IDLE },
+{ 0xd, 0x0f }, { 0xc, 0xff }, { WAIT_IDLE },
+{ 0xd, 0x00 }, { 0xc, 0x00 }, { WAIT_IDLE },
+{ 0xd, 0x00 }, { 0xc, 0x00 }, { WAIT_IDLE },
+{ 0xd, 0x08 }, { 0xc, 0x00 }, { WAIT_IDLE },
+{ 0xd, 0x08 }, { 0xc, 0x00 }, { WAIT_IDLE },
+{ 0xd, 0x00 }, { 0xc, 0x00 }, { WAIT_IDLE },
+{ 0xd, 0x0f }, { 0xc, 0xff }, { WAIT_IDLE },
+{ 0xd, 0x00 }, { 0xc, 0x00 }, { WAIT_IDLE },
+{ 0xd, 0x00 }, { 0xc, 0x00 }, { WAIT_IDLE },
+{ 0xd, 0x0f }, { 0xc, 0xff }, { WAIT_IDLE },
+{ 0xd, 0x0f }, { 0xc, 0xff }, { WAIT_IDLE },
+{ 0xd, 0x00 }, { 0xc, 0x00 }, { WAIT_IDLE },
+{ 0xd, 0x00 }, { 0xc, 0x00 }, { WAIT_IDLE },
+{ 0xd, 0x00 }, { 0xc, 0x00 }, { WAIT_IDLE },
+{ 0xd, 0x00 }, { 0xc, 0x00 }, { WAIT_IDLE },
+{ 0xd, 0x00 }, { 0xc, 0x00 }, { WAIT_IDLE },
+{ 0xd, 0x00 }, { 0xc, 0x00 }, { WAIT_IDLE },
+{ 0xd, 0x00 }, { 0xc, 0x00 }, { WAIT_IDLE },
+{ 0xd, 0x00 }, { 0xc, 0x00 }, { WAIT_IDLE },
+{ 0xd, 0x00 }, { 0xc, 0x00 }, { WAIT_IDLE },
+{ 0xd, 0x00 }, { 0xc, 0x00 }, { WAIT_IDLE },
+{ 0xd, 0x00 }, { 0xc, 0x00 }, { WAIT_IDLE },
+{ 0xd, 0x00 }, { 0xc, 0x00 }, { WAIT_IDLE },
+{ 0xd, 0x00 }, { 0xc, 0x00 }, { WAIT_IDLE },
+{ 0xd, 0x00 }, { 0xc, 0x00 }, { WAIT_IDLE },
+{ 0xd, 0x00 }, { 0xc, 0x00 }, { WAIT_IDLE },
+{ 0xd, 0x00 }, { 0xc, 0x00 }, { WAIT_IDLE },
+{ 0xd, 0x00 }, { 0xc, 0x00 }, { WAIT_IDLE },
+{ 0xd, 0x00 }, { 0xc, 0x00 }, { WAIT_IDLE },
+{ 0xd, 0x00 }, { 0xc, 0x00 }, { WAIT_IDLE },
+{ 0xd, 0x00 }, { 0xc, 0x00 }, { WAIT_IDLE },
+{ 0xd, 0x00 }, { 0xc, 0x00 }, { WAIT_IDLE },
+{ 0xd, 0x00 }, { 0xc, 0x00 }, { WAIT_IDLE },
+{ 0xd, 0x00 }, { 0xc, 0x00 }, { WAIT_IDLE },
+{ 0xd, 0x00 }, { 0xc, 0x00 }, { WAIT_IDLE },
+{ 0xd, 0x00 }, { 0xc, 0x00 }, { WAIT_IDLE },
+{ 0xd, 0x00 }, { 0xc, 0x00 }, { WAIT_IDLE },
+{ 0xd, 0x00 }, { 0xc, 0x00 }, { WAIT_IDLE },
+{ 0xd, 0x00 }, { 0xc, 0x00 }, { WAIT_IDLE },
+{ 0xd, 0x00 }, { 0xc, 0x00 }, { WAIT_IDLE },
+{ 0xd, 0x00 }, { 0xc, 0x00 }, { WAIT_IDLE },
+{ 0xd, 0x00 }, { 0xc, 0x00 }, { WAIT_IDLE },
+{ 0xd, 0x00 }, { 0xc, 0x00 }, { WAIT_IDLE },
+{ 0xd, 0x00 }, { 0xc, 0x00 }, { WAIT_IDLE },
+{ 0xd, 0x00 }, { 0xc, 0x00 }, { WAIT_IDLE },
+{ 0xd, 0x00 }, { 0xc, 0x00 }, { WAIT_IDLE },
+{ 0xd, 0x00 }, { 0xc, 0x00 }, { WAIT_IDLE },
+{ 0xd, 0x00 }, { 0xc, 0x00 }, { WAIT_IDLE },
+{ 0xd, 0x00 }, { 0xc, 0x00 }, { WAIT_IDLE },
+{ 0xd, 0x00 }, { 0xc, 0x00 }, { WAIT_IDLE },
+{ 0xd, 0x00 }, { 0xc, 0x00 }, { WAIT_IDLE },
+{ 0xd, 0x00 }, { 0xc, 0x00 }, { WAIT_IDLE },
+{ 0xd, 0x00 }, { 0xc, 0x00 }, { WAIT_IDLE },
+{ 0xd, 0x00 }, { 0xc, 0x00 }, { WAIT_IDLE },
+{ 0xd, 0x00 }, { 0xc, 0x00 }, { WAIT_IDLE },
+{ 0xd, 0x00 }, { 0xc, 0x00 }, { WAIT_IDLE },
+{ 0xd, 0x00 }, { 0xc, 0x00 }, { WAIT_IDLE },
+{ 0xd, 0x00 }, { 0xc, 0x00 }, { WAIT_IDLE },
+{ 0xd, 0x00 }, { 0xc, 0x00 }, { WAIT_IDLE },
+{ 0xd, 0x00 }, { 0xc, 0x00 }, { WAIT_IDLE },
+{ 0xd, 0x00 }, { 0xc, 0x00 }, { WAIT_IDLE },
+{ 0xd, 0x00 }, { 0xc, 0x00 }, { WAIT_IDLE },
+{ 0xd, 0x00 }, { 0xc, 0x00 }, { WAIT_IDLE },
+{ 0xd, 0x00 }, { 0xc, 0x00 }, { WAIT_IDLE },
+{ 0xd, 0x00 }, { 0xc, 0x00 }, { WAIT_IDLE },
+{ 0xd, 0x00 }, { 0xc, 0x00 }, { WAIT_IDLE },
+{ 0xd, 0x00 }, { 0xc, 0x00 }, { WAIT_IDLE },
+{ 0xd, 0x00 }, { 0xc, 0x00 }, { WAIT_IDLE },
+{ 0xd, 0x0f }, { 0xc, 0xff }, { WAIT_IDLE },
+{ 0xd, 0x0f }, { 0xc, 0xff }, { WAIT_IDLE },
+{ 0xd, 0x0f }, { 0xc, 0xff }, { WAIT_IDLE },
+{ 0xd, 0x0f }, { 0xc, 0xff }, { WAIT_IDLE },
+{ 0xd, 0x02 }, { 0xc, 0xe9 }, { WAIT_IDLE },
+{ 0xd, 0x06 }, { 0xc, 0x8c }, { WAIT_IDLE },
+{ 0xd, 0x06 }, { 0xc, 0x8c }, { WAIT_IDLE },
+{ 0xd, 0x0f }, { 0xc, 0xff }, { WAIT_IDLE },
+{ 0xd, 0x1a }, { 0xc, 0x75 }, { WAIT_IDLE },
+{ 0xd, 0x0d }, { 0xc, 0x8b }, { WAIT_IDLE },
+{ 0xd, 0x04 }, { 0xc, 0xe9 }, { WAIT_IDLE },
+{ 0xd, 0x0b }, { 0xc, 0x16 }, { WAIT_IDLE },
+{ 0xd, 0x1a }, { 0xc, 0x38 }, { WAIT_IDLE },
+{ 0xd, 0x0d }, { 0xc, 0xc8 }, { WAIT_IDLE },
+{ 0xd, 0x04 }, { 0xc, 0x6f }, { WAIT_IDLE },
+{ 0xd, 0x0b }, { 0xc, 0x91 }, { WAIT_IDLE },
+{ 0xd, 0x0f }, { 0xc, 0xff }, { WAIT_IDLE },
+{ 0xd, 0x06 }, { 0xc, 0x40 }, { WAIT_IDLE },
+{ 0xd, 0x06 }, { 0xc, 0x40 }, { WAIT_IDLE },
+{ 0xd, 0x02 }, { 0xc, 0x8f }, { WAIT_IDLE },
+{ 0xd, 0x0f }, { 0xc, 0xff }, { WAIT_IDLE },
+{ 0xd, 0x06 }, { 0xc, 0x62 }, { WAIT_IDLE },
+{ 0xd, 0x06 }, { 0xc, 0x62 }, { WAIT_IDLE },
+{ 0xd, 0x02 }, { 0xc, 0x7b }, { WAIT_IDLE },
+{ 0xd, 0x0f }, { 0xc, 0xff }, { WAIT_IDLE },
+{ 0xd, 0x06 }, { 0xc, 0x97 }, { WAIT_IDLE },
+{ 0xd, 0x06 }, { 0xc, 0x97 }, { WAIT_IDLE },
+{ 0xd, 0x02 }, { 0xc, 0x52 }, { WAIT_IDLE },
+{ 0xd, 0x0f }, { 0xc, 0xff }, { WAIT_IDLE },
+{ 0xd, 0x06 }, { 0xc, 0xf6 }, { WAIT_IDLE },
+{ 0xd, 0x06 }, { 0xc, 0xf6 }, { WAIT_IDLE },
+{ 0xd, 0x02 }, { 0xc, 0x19 }, { WAIT_IDLE },
+{ 0xd, 0x05 }, { 0xc, 0x55 }, { WAIT_IDLE },
+{ 0xd, 0x05 }, { 0xc, 0x55 }, { WAIT_IDLE },
+{ 0xd, 0x05 }, { 0xc, 0x55 }, { WAIT_IDLE },
+{ 0xd, 0x05 }, { 0xc, 0x55 }, { WAIT_IDLE },
+{ 0xd, 0x05 }, { 0xc, 0x55 }, { WAIT_IDLE },
+{ 0xd, 0x05 }, { 0xc, 0x55 }, { WAIT_IDLE },
+{ 0xd, 0x05 }, { 0xc, 0x55 }, { WAIT_IDLE },
+{ 0xd, 0x05 }, { 0xc, 0x55 }, { WAIT_IDLE },
+{ 0xd, 0x14 }, { 0xc, 0xda }, { WAIT_IDLE },
+{ 0xd, 0x0d }, { 0xc, 0x93 }, { WAIT_IDLE },
+{ 0xd, 0x04 }, { 0xc, 0xda }, { WAIT_IDLE },
+{ 0xd, 0x05 }, { 0xc, 0x93 }, { WAIT_IDLE },
+{ 0xd, 0x14 }, { 0xc, 0xda }, { WAIT_IDLE },
+{ 0xd, 0x0d }, { 0xc, 0x93 }, { WAIT_IDLE },
+{ 0xd, 0x04 }, { 0xc, 0xda }, { WAIT_IDLE },
+{ 0xd, 0x05 }, { 0xc, 0x93 }, { WAIT_IDLE },
+{ 0xd, 0x00 }, { 0xc, 0x00 }, { WAIT_IDLE },
+{ 0xd, 0x00 }, { 0xc, 0x00 }, { WAIT_IDLE },
+{ 0xd, 0x00 }, { 0xc, 0x00 }, { WAIT_IDLE },
+{ 0xd, 0x00 }, { 0xc, 0x00 }, { WAIT_IDLE },
+{ 0xd, 0x02 }, { 0xc, 0x00 }, { WAIT_IDLE },
+
+/* Now setup the MOD area. */
+{ 0xe, 0x01 }, { 0xf, 0x00 }, { WAIT_IDLE },
+{ 0xe, 0x02 }, { 0xf, 0x00 }, { WAIT_IDLE },
+{ 0xe, 0x01 }, { 0xf, 0x01 }, { WAIT_IDLE },
+{ 0xe, 0x02 }, { 0xf, 0x00 }, { WAIT_IDLE },
+{ 0xe, 0x01 }, { 0xf, 0x02 }, { WAIT_IDLE },
+{ 0xe, 0x02 }, { 0xf, 0x00 }, { WAIT_IDLE },
+{ 0xe, 0x01 }, { 0xf, 0x03 }, { WAIT_IDLE },
+{ 0xe, 0x02 }, { 0xf, 0x00 }, { WAIT_IDLE },
+{ 0xe, 0x01 }, { 0xf, 0x04 }, { WAIT_IDLE },
+{ 0xe, 0x02 }, { 0xf, 0x00 }, { WAIT_IDLE },
+{ 0xe, 0x01 }, { 0xf, 0x05 }, { WAIT_IDLE },
+{ 0xe, 0x02 }, { 0xf, 0x00 }, { WAIT_IDLE },
+{ 0xe, 0x01 }, { 0xf, 0x06 }, { WAIT_IDLE },
+{ 0xe, 0x02 }, { 0xf, 0x00 }, { WAIT_IDLE },
+{ 0xe, 0x01 }, { 0xf, 0x07 }, { WAIT_IDLE },
+{ 0xe, 0x02 }, { 0xf, 0x00 }, { WAIT_IDLE },
+{ 0xe, 0x01 }, { 0xf, 0x08 }, { WAIT_IDLE },
+{ 0xe, 0x02 }, { 0xf, 0x00 }, { WAIT_IDLE },
+{ 0xe, 0x01 }, { 0xf, 0x09 }, { WAIT_IDLE },
+{ 0xe, 0x02 }, { 0xf, 0x00 }, { WAIT_IDLE },
+{ 0xe, 0x01 }, { 0xf, 0x0a }, { WAIT_IDLE },
+{ 0xe, 0x02 }, { 0xf, 0x00 }, { WAIT_IDLE },
+{ 0xe, 0x01 }, { 0xf, 0x0b }, { WAIT_IDLE },
+{ 0xe, 0x02 }, { 0xf, 0x00 }, { WAIT_IDLE },
+{ 0xe, 0x01 }, { 0xf, 0x0c }, { WAIT_IDLE },
+{ 0xe, 0x02 }, { 0xf, 0x00 }, { WAIT_IDLE },
+{ 0xe, 0x01 }, { 0xf, 0x0d }, { WAIT_IDLE },
+{ 0xe, 0x02 }, { 0xf, 0x00 }, { WAIT_IDLE },
+{ 0xe, 0x01 }, { 0xf, 0x0e }, { WAIT_IDLE },
+{ 0xe, 0x02 }, { 0xf, 0x00 }, { WAIT_IDLE },
+{ 0xe, 0x01 }, { 0xf, 0x0f }, { WAIT_IDLE },
+{ 0xe, 0x02 }, { 0xf, 0x00 }, { WAIT_IDLE },
+
+{ 0xe, 0xb0 }, { 0xf, 0x20 }, { WAIT_IDLE },
+{ 0xe, 0xb1 }, { 0xf, 0x20 }, { WAIT_IDLE },
+{ 0xe, 0xb2 }, { 0xf, 0x20 }, { WAIT_IDLE },
+{ 0xe, 0xb3 }, { 0xf, 0x20 }, { WAIT_IDLE },
+{ 0xe, 0xb4 }, { 0xf, 0x20 }, { WAIT_IDLE },
+{ 0xe, 0xb5 }, { 0xf, 0x20 }, { WAIT_IDLE },
+{ 0xe, 0xb6 }, { 0xf, 0x20 }, { WAIT_IDLE },
+{ 0xe, 0xb7 }, { 0xf, 0x20 }, { WAIT_IDLE },
+{ 0xe, 0xb8 }, { 0xf, 0x20 }, { WAIT_IDLE },
+{ 0xe, 0xb9 }, { 0xf, 0x20 }, { WAIT_IDLE },
+{ 0xe, 0xba }, { 0xf, 0x20 }, { WAIT_IDLE },
+{ 0xe, 0xbb }, { 0xf, 0x20 }, { WAIT_IDLE },
+{ 0xe, 0xbc }, { 0xf, 0x20 }, { WAIT_IDLE },
+{ 0xe, 0xbd }, { 0xf, 0x20 }, { WAIT_IDLE },
+{ 0xe, 0xbe }, { 0xf, 0x20 }, { WAIT_IDLE },
+{ 0xe, 0xbf }, { 0xf, 0x20 }, { WAIT_IDLE },
+
+{ 0xe, 0xf0 }, { 0xf, 0x20 }, { WAIT_IDLE },
+{ 0xe, 0xf1 }, { 0xf, 0x20 }, { WAIT_IDLE },
+{ 0xe, 0xf2 }, { 0xf, 0x20 }, { WAIT_IDLE },
+{ 0xe, 0xf3 }, { 0xf, 0x20 }, { WAIT_IDLE },
+{ 0xe, 0xf4 }, { 0xf, 0x20 }, { WAIT_IDLE },
+{ 0xe, 0xf5 }, { 0xf, 0x20 }, { WAIT_IDLE },
+{ 0xe, 0xf6 }, { 0xf, 0x20 }, { WAIT_IDLE },
+{ 0xe, 0xf7 }, { 0xf, 0x20 }, { WAIT_IDLE },
+{ 0xe, 0xf8 }, { 0xf, 0x20 }, { WAIT_IDLE },
+{ 0xe, 0xf9 }, { 0xf, 0x20 }, { WAIT_IDLE },
+{ 0xe, 0xfa }, { 0xf, 0x20 }, { WAIT_IDLE },
+{ 0xe, 0xfb }, { 0xf, 0x20 }, { WAIT_IDLE },
+{ 0xe, 0xfc }, { 0xf, 0x20 }, { WAIT_IDLE },
+{ 0xe, 0xfd }, { 0xf, 0x20 }, { WAIT_IDLE },
+{ 0xe, 0xfe }, { 0xf, 0x20 }, { WAIT_IDLE },
+{ 0xe, 0xff }, { 0xf, 0x20 }, { WAIT_IDLE },
+
+{ 0xe, 0x10 }, { 0xf, 0xff }, { WAIT_IDLE },
+{ 0xe, 0x11 }, { 0xf, 0xff }, { WAIT_IDLE },
+{ 0xe, 0x12 }, { 0xf, 0xff }, { WAIT_IDLE },
+{ 0xe, 0x13 }, { 0xf, 0xff }, { WAIT_IDLE },
+{ 0xe, 0x14 }, { 0xf, 0xff }, { WAIT_IDLE },
+{ 0xe, 0x15 }, { 0xf, 0xff }, { WAIT_IDLE },
+{ 0xe, 0x16 }, { 0xf, 0xff }, { WAIT_IDLE },
+{ 0xe, 0x17 }, { 0xf, 0xff }, { WAIT_IDLE },
+{ 0xe, 0x18 }, { 0xf, 0xff }, { WAIT_IDLE },
+{ 0xe, 0x19 }, { 0xf, 0xff }, { WAIT_IDLE },
+{ 0xe, 0x1a }, { 0xf, 0xff }, { WAIT_IDLE },
+{ 0xe, 0x1b }, { 0xf, 0xff }, { WAIT_IDLE },
+{ 0xe, 0x1c }, { 0xf, 0xff }, { WAIT_IDLE },
+{ 0xe, 0x1d }, { 0xf, 0xff }, { WAIT_IDLE },
+{ 0xe, 0x1e }, { 0xf, 0x40 }, { WAIT_IDLE },
+{ 0xe, 0x1f }, { 0xf, 0xff }, { WAIT_IDLE },
+{ 0xe, 0x20 }, { 0xf, 0xff }, { WAIT_IDLE },
+{ 0xe, 0x21 }, { 0xf, 0xff }, { WAIT_IDLE },
+{ 0xe, 0x22 }, { 0xf, 0xff }, { WAIT_IDLE },
+{ 0xe, 0x23 }, { 0xf, 0xff }, { WAIT_IDLE },
+{ 0xe, 0x24 }, { 0xf, 0xff }, { WAIT_IDLE },
+{ 0xe, 0x25 }, { 0xf, 0xff }, { WAIT_IDLE },
+{ 0xe, 0x26 }, { 0xf, 0xff }, { WAIT_IDLE },
+{ 0xe, 0x27 }, { 0xf, 0xff }, { WAIT_IDLE },
+{ 0xe, 0x28 }, { 0xf, 0xff }, { WAIT_IDLE },
+{ 0xe, 0x29 }, { 0xf, 0xff }, { WAIT_IDLE },
+{ 0xe, 0x2a }, { 0xf, 0xff }, { WAIT_IDLE },
+{ 0xe, 0x2b }, { 0xf, 0xff }, { WAIT_IDLE },
+{ 0xe, 0x2c }, { 0xf, 0xff }, { WAIT_IDLE },
+{ 0xe, 0x2d }, { 0xf, 0xff }, { WAIT_IDLE },
+{ 0xe, 0x2e }, { 0xf, 0x00 }, { WAIT_IDLE },
+{ 0xe, 0x2f }, { 0xf, 0x00 }, { WAIT_IDLE },
+{ 0xe, 0x30 }, { 0xf, 0x00 }, { WAIT_IDLE },
+{ 0xe, 0x31 }, { 0xf, 0x00 }, { WAIT_IDLE },
+{ 0xe, 0x32 }, { 0xf, 0x00 }, { WAIT_IDLE },
+{ 0xe, 0x33 }, { 0xf, 0x00 }, { WAIT_IDLE },
+{ 0xe, 0x34 }, { 0xf, 0x00 }, { WAIT_IDLE },
+{ 0xe, 0x35 }, { 0xf, 0x00 }, { WAIT_IDLE },
+{ 0xe, 0x36 }, { 0xf, 0x00 }, { WAIT_IDLE },
+{ 0xe, 0x37 }, { 0xf, 0x00 }, { WAIT_IDLE },
+{ 0xe, 0x38 }, { 0xf, 0x00 }, { WAIT_IDLE },
+{ 0xe, 0x39 }, { 0xf, 0x00 }, { WAIT_IDLE },
+{ 0xe, 0x3a }, { 0xf, 0x00 }, { WAIT_IDLE },
+{ 0xe, 0x3b }, { 0xf, 0x00 }, { WAIT_IDLE },
+{ 0xe, 0x3c }, { 0xf, 0x00 }, { WAIT_IDLE },
+{ 0xe, 0x3d }, { 0xf, 0x00 }, { WAIT_IDLE },
+{ 0xe, 0x3e }, { 0xf, 0x00 }, { WAIT_IDLE },
+{ 0xe, 0x3f }, { 0xf, 0x20 }, { WAIT_IDLE },
+{ 0xe, 0x40 }, { 0xf, 0x00 }, { WAIT_IDLE },
+{ 0xe, 0x41 }, { 0xf, 0x00 }, { WAIT_IDLE },
+{ 0xe, 0x42 }, { 0xf, 0x00 }, { WAIT_IDLE },
+{ 0xe, 0x43 }, { 0xf, 0x00 }, { WAIT_IDLE },
+{ 0xe, 0x44 }, { 0xf, 0x00 }, { WAIT_IDLE },
+{ 0xe, 0x45 }, { 0xf, 0x00 }, { WAIT_IDLE },
+{ 0xe, 0x46 }, { 0xf, 0x00 }, { WAIT_IDLE },
+{ 0xe, 0x47 }, { 0xf, 0x00 }, { WAIT_IDLE },
+{ 0xe, 0x48 }, { 0xf, 0x00 }, { WAIT_IDLE },
+{ 0xe, 0x49 }, { 0xf, 0x00 }, { WAIT_IDLE },
+{ 0xe, 0x4a }, { 0xf, 0x00 }, { WAIT_IDLE },
+{ 0xe, 0x4b }, { 0xf, 0x00 }, { WAIT_IDLE },
+{ 0xe, 0x4c }, { 0xf, 0x00 }, { WAIT_IDLE },
+{ 0xe, 0x4d }, { 0xf, 0x00 }, { WAIT_IDLE },
+{ 0xe, 0x4e }, { 0xf, 0x0e }, { WAIT_IDLE },
+{ 0xe, 0x4f }, { 0xf, 0x0e }, { WAIT_IDLE },
+{ 0xe, 0x50 }, { 0xf, 0x00 }, { WAIT_IDLE },
+{ 0xe, 0x51 }, { 0xf, 0x00 }, { WAIT_IDLE },
+{ 0xe, 0x52 }, { 0xf, 0x00 }, { WAIT_IDLE },
+{ 0xe, 0x53 }, { 0xf, 0x00 }, { WAIT_IDLE },
+{ 0xe, 0x54 }, { 0xf, 0x00 }, { WAIT_IDLE },
+{ 0xe, 0x55 }, { 0xf, 0x00 }, { WAIT_IDLE },
+{ 0xe, 0x56 }, { 0xf, 0x00 }, { WAIT_IDLE },
+{ 0xe, 0x57 }, { 0xf, 0x00 }, { WAIT_IDLE },
+{ 0xe, 0x58 }, { 0xf, 0x00 }, { WAIT_IDLE },
+{ 0xe, 0x59 }, { 0xf, 0x00 }, { WAIT_IDLE },
+{ 0xe, 0x5a }, { 0xf, 0x00 }, { WAIT_IDLE },
+{ 0xe, 0x5b }, { 0xf, 0x00 }, { WAIT_IDLE },
+{ 0xe, 0x5c }, { 0xf, 0x00 }, { WAIT_IDLE },
+{ 0xe, 0x5d }, { 0xf, 0x00 }, { WAIT_IDLE },
+{ 0xe, 0x5e }, { 0xf, 0x00 }, { WAIT_IDLE },
+{ 0xe, 0x5f }, { 0xf, 0x00 }, { WAIT_IDLE },
+{ 0xe, 0x60 }, { 0xf, 0x00 }, { WAIT_IDLE },
+{ 0xe, 0x61 }, { 0xf, 0x00 }, { WAIT_IDLE },
+{ 0xe, 0x62 }, { 0xf, 0x00 }, { WAIT_IDLE },
+{ 0xe, 0x63 }, { 0xf, 0x00 }, { WAIT_IDLE },
+{ 0xe, 0x64 }, { 0xf, 0x00 }, { WAIT_IDLE },
+{ 0xe, 0x65 }, { 0xf, 0x00 }, { WAIT_IDLE },
+{ 0xe, 0x66 }, { 0xf, 0x00 }, { WAIT_IDLE },
+{ 0xe, 0x67 }, { 0xf, 0x00 }, { WAIT_IDLE },
+{ 0xe, 0x68 }, { 0xf, 0x00 }, { WAIT_IDLE },
+{ 0xe, 0x69 }, { 0xf, 0x00 }, { WAIT_IDLE },
+{ 0xe, 0x6a }, { 0xf, 0x00 }, { WAIT_IDLE },
+{ 0xe, 0x6b }, { 0xf, 0x00 }, { WAIT_IDLE },
+{ 0xe, 0x6c }, { 0xf, 0x40 }, { WAIT_IDLE },
+{ 0xe, 0x6d }, { 0xf, 0x00 }, { WAIT_IDLE },
+{ 0xe, 0x6e }, { 0xf, 0x40 }, { WAIT_IDLE },
+{ 0xe, 0x6f }, { 0xf, 0x40 }, { WAIT_IDLE },
+{ 0xe, 0x70 }, { 0xf, 0xc0 }, { WAIT_IDLE },
+{ 0xe, 0x71 }, { 0xf, 0xc0 }, { WAIT_IDLE },
+{ 0xe, 0x72 }, { 0xf, 0xc0 }, { WAIT_IDLE },
+{ 0xe, 0x73 }, { 0xf, 0xc0 }, { WAIT_IDLE },
+{ 0xe, 0x74 }, { 0xf, 0xc0 }, { WAIT_IDLE },
+{ 0xe, 0x75 }, { 0xf, 0xc0 }, { WAIT_IDLE },
+{ 0xe, 0x76 }, { 0xf, 0xc0 }, { WAIT_IDLE },
+{ 0xe, 0x77 }, { 0xf, 0xc0 }, { WAIT_IDLE },
+{ 0xe, 0x78 }, { 0xf, 0xc0 }, { WAIT_IDLE },
+{ 0xe, 0x79 }, { 0xf, 0xc0 }, { WAIT_IDLE },
+{ 0xe, 0x7a }, { 0xf, 0xc0 }, { WAIT_IDLE },
+{ 0xe, 0x7b }, { 0xf, 0xc0 }, { WAIT_IDLE },
+{ 0xe, 0x7c }, { 0xf, 0xc0 }, { WAIT_IDLE },
+{ 0xe, 0x7d }, { 0xf, 0xc0 }, { WAIT_IDLE },
+{ 0xe, 0x7e }, { 0xf, 0xc0 }, { WAIT_IDLE },
+{ 0xe, 0x7f }, { 0xf, 0xc0 }, { WAIT_IDLE },
+{ 0xe, 0x80 }, { 0xf, 0x00 }, { WAIT_IDLE },
+{ 0xe, 0x81 }, { 0xf, 0x00 }, { WAIT_IDLE },
+{ 0xe, 0x82 }, { 0xf, 0x00 }, { WAIT_IDLE },
+{ 0xe, 0x83 }, { 0xf, 0x00 }, { WAIT_IDLE },
+{ 0xe, 0x84 }, { 0xf, 0x00 }, { WAIT_IDLE },
+{ 0xe, 0x85 }, { 0xf, 0x00 }, { WAIT_IDLE },
+{ 0xe, 0x86 }, { 0xf, 0x00 }, { WAIT_IDLE },
+{ 0xe, 0x87 }, { 0xf, 0x00 }, { WAIT_IDLE },
+{ 0xe, 0x88 }, { 0xf, 0x00 }, { WAIT_IDLE },
+{ 0xe, 0x89 }, { 0xf, 0x00 }, { WAIT_IDLE },
+{ 0xe, 0x8a }, { 0xf, 0x00 }, { WAIT_IDLE },
+{ 0xe, 0x8b }, { 0xf, 0x00 }, { WAIT_IDLE },
+{ 0xe, 0x8c }, { 0xf, 0x00 }, { WAIT_IDLE },
+{ 0xe, 0x8d }, { 0xf, 0x00 }, { WAIT_IDLE },
+{ 0xe, 0x8e }, { 0xf, 0x00 }, { WAIT_IDLE },
+{ 0xe, 0x8f }, { 0xf, 0x00 }, { WAIT_IDLE },
+{ 0xe, 0x90 }, { 0xf, 0x00 }, { WAIT_IDLE },
+{ 0xe, 0x91 }, { 0xf, 0x00 }, { WAIT_IDLE },
+{ 0xe, 0x92 }, { 0xf, 0x00 }, { WAIT_IDLE },
+{ 0xe, 0x93 }, { 0xf, 0x00 }, { WAIT_IDLE },
+{ 0xe, 0x94 }, { 0xf, 0x00 }, { WAIT_IDLE },
+{ 0xe, 0x95 }, { 0xf, 0x00 }, { WAIT_IDLE },
+{ 0xe, 0x96 }, { 0xf, 0x00 }, { WAIT_IDLE },
+{ 0xe, 0x97 }, { 0xf, 0x00 }, { WAIT_IDLE },
+{ 0xe, 0x98 }, { 0xf, 0x00 }, { WAIT_IDLE },
+{ 0xe, 0x99 }, { 0xf, 0x00 }, { WAIT_IDLE },
+{ 0xe, 0x9a }, { 0xf, 0x00 }, { WAIT_IDLE },
+{ 0xe, 0x9b }, { 0xf, 0x00 }, { WAIT_IDLE },
+{ 0xe, 0x9c }, { 0xf, 0x00 }, { WAIT_IDLE },
+{ 0xe, 0x9d }, { 0xf, 0x00 }, { WAIT_IDLE },
+{ 0xe, 0x9e }, { 0xf, 0x00 }, { WAIT_IDLE },
+{ 0xe, 0x9f }, { 0xf, 0x00 }, { WAIT_IDLE },
+{ 0xe, 0xa0 }, { 0xf, 0x00 }, { WAIT_IDLE },
+{ 0xe, 0xa1 }, { 0xf, 0x00 }, { WAIT_IDLE },
+{ 0xe, 0xa2 }, { 0xf, 0x00 }, { WAIT_IDLE },
+{ 0xe, 0xa3 }, { 0xf, 0x00 }, { WAIT_IDLE },
+{ 0xe, 0xa4 }, { 0xf, 0x00 }, { WAIT_IDLE },
+{ 0xe, 0xa5 }, { 0xf, 0x00 }, { WAIT_IDLE },
+{ 0xe, 0xa6 }, { 0xf, 0x00 }, { WAIT_IDLE },
+{ 0xe, 0xa7 }, { 0xf, 0x00 }, { WAIT_IDLE },
+{ 0xe, 0xa8 }, { 0xf, 0x00 }, { WAIT_IDLE },
+{ 0xe, 0xa9 }, { 0xf, 0x00 }, { WAIT_IDLE },
+{ 0xe, 0xaa }, { 0xf, 0x00 }, { WAIT_IDLE },
+{ 0xe, 0xab }, { 0xf, 0x00 }, { WAIT_IDLE },
+{ 0xe, 0xac }, { 0xf, 0x00 }, { WAIT_IDLE },
+{ 0xe, 0xad }, { 0xf, 0x00 }, { WAIT_IDLE },
+{ 0xe, 0xae }, { 0xf, 0x00 }, { WAIT_IDLE },
+{ 0xe, 0xaf }, { 0xf, 0x00 }, { WAIT_IDLE },
+
+{ 0xe, 0xc0 }, { 0xf, 0x00 }, { WAIT_IDLE },
+{ 0xe, 0xc1 }, { 0xf, 0x00 }, { WAIT_IDLE },
+{ 0xe, 0xc2 }, { 0xf, 0x00 }, { WAIT_IDLE },
+{ 0xe, 0xc3 }, { 0xf, 0x00 }, { WAIT_IDLE },
+{ 0xe, 0xc4 }, { 0xf, 0x00 }, { WAIT_IDLE },
+{ 0xe, 0xc5 }, { 0xf, 0x00 }, { WAIT_IDLE },
+{ 0xe, 0xc6 }, { 0xf, 0x00 }, { WAIT_IDLE },
+{ 0xe, 0xc7 }, { 0xf, 0x00 }, { WAIT_IDLE },
+{ 0xe, 0xc8 }, { 0xf, 0x00 }, { WAIT_IDLE },
+{ 0xe, 0xc9 }, { 0xf, 0x00 }, { WAIT_IDLE },
+{ 0xe, 0xca }, { 0xf, 0x00 }, { WAIT_IDLE },
+{ 0xe, 0xcb }, { 0xf, 0x00 }, { WAIT_IDLE },
+{ 0xe, 0xcc }, { 0xf, 0x00 }, { WAIT_IDLE },
+{ 0xe, 0xcd }, { 0xf, 0x00 }, { WAIT_IDLE },
+{ 0xe, 0xce }, { 0xf, 0x00 }, { WAIT_IDLE },
+{ 0xe, 0xcf }, { 0xf, 0x00 }, { WAIT_IDLE },
+{ 0xe, 0xd0 }, { 0xf, 0x00 }, { WAIT_IDLE },
+{ 0xe, 0xd1 }, { 0xf, 0x00 }, { WAIT_IDLE },
+{ 0xe, 0xd2 }, { 0xf, 0x00 }, { WAIT_IDLE },
+{ 0xe, 0xd3 }, { 0xf, 0x00 }, { WAIT_IDLE },
+{ 0xe, 0xd4 }, { 0xf, 0x00 }, { WAIT_IDLE },
+{ 0xe, 0xd5 }, { 0xf, 0x00 }, { WAIT_IDLE },
+{ 0xe, 0xd6 }, { 0xf, 0x00 }, { WAIT_IDLE },
+{ 0xe, 0xd7 }, { 0xf, 0x00 }, { WAIT_IDLE },
+{ 0xe, 0xd8 }, { 0xf, 0x00 }, { WAIT_IDLE },
+{ 0xe, 0xd9 }, { 0xf, 0x00 }, { WAIT_IDLE },
+{ 0xe, 0xda }, { 0xf, 0x00 }, { WAIT_IDLE },
+{ 0xe, 0xdb }, { 0xf, 0x00 }, { WAIT_IDLE },
+{ 0xe, 0xdc }, { 0xf, 0x00 }, { WAIT_IDLE },
+{ 0xe, 0xdd }, { 0xf, 0x00 }, { WAIT_IDLE },
+{ 0xe, 0xde }, { 0xf, 0x10 }, { WAIT_IDLE },
+{ 0xe, 0xdf }, { 0xf, 0x10 }, { WAIT_IDLE },
+{ 0xe, 0xe0 }, { 0xf, 0x00 }, { WAIT_IDLE },
+{ 0xe, 0xe1 }, { 0xf, 0x00 }, { WAIT_IDLE },
+{ 0xe, 0xe2 }, { 0xf, 0x00 }, { WAIT_IDLE },
+{ 0xe, 0xe3 }, { 0xf, 0x00 }, { WAIT_IDLE },
+{ 0xe, 0xe4 }, { 0xf, 0x00 }, { WAIT_IDLE },
+{ 0xe, 0xe5 }, { 0xf, 0x00 }, { WAIT_IDLE },
+{ 0xe, 0xe6 }, { 0xf, 0x00 }, { WAIT_IDLE },
+{ 0xe, 0xe7 }, { 0xf, 0x00 }, { WAIT_IDLE },
+{ 0xe, 0xe8 }, { 0xf, 0x00 }, { WAIT_IDLE },
+{ 0xe, 0xe9 }, { 0xf, 0x00 }, { WAIT_IDLE },
+{ 0xe, 0xea }, { 0xf, 0x00 }, { WAIT_IDLE },
+{ 0xe, 0xeb }, { 0xf, 0x00 }, { WAIT_IDLE },
+{ 0xe, 0xec }, { 0xf, 0x00 }, { WAIT_IDLE },
+{ 0xe, 0xed }, { 0xf, 0x00 }, { WAIT_IDLE },
+{ 0xe, 0xee }, { 0xf, 0x00 }, { WAIT_IDLE },
+{ 0xe, 0xef }, { 0xf, 0x00 }, { WAIT_IDLE },
+
+{ 0xe, 0x01 }, { 0xf, 0x00 }, { 0xe, 0x02 }, { 0xf, 0x01 }, { WAIT_IDLE },
+{ 0xe, 0x01 }, { 0xf, 0x01 }, { 0xe, 0x02 }, { 0xf, 0x01 }, { WAIT_IDLE },
+{ 0xe, 0x01 }, { 0xf, 0x02 }, { 0xe, 0x02 }, { 0xf, 0x01 }, { WAIT_IDLE },
+{ 0xe, 0x01 }, { 0xf, 0x03 }, { 0xe, 0x02 }, { 0xf, 0x01 }, { WAIT_IDLE },
+{ 0xe, 0x01 }, { 0xf, 0x04 }, { 0xe, 0x02 }, { 0xf, 0x01 }, { WAIT_IDLE },
+{ 0xe, 0x01 }, { 0xf, 0x05 }, { 0xe, 0x02 }, { 0xf, 0x01 }, { WAIT_IDLE },
+{ 0xe, 0x01 }, { 0xf, 0x06 }, { 0xe, 0x02 }, { 0xf, 0x01 }, { WAIT_IDLE },
+{ 0xe, 0x01 }, { 0xf, 0x07 }, { 0xe, 0x02 }, { 0xf, 0x01 }, { WAIT_IDLE },
+{ 0xe, 0x01 }, { 0xf, 0x08 }, { 0xe, 0x02 }, { 0xf, 0x01 }, { WAIT_IDLE },
+{ 0xe, 0x01 }, { 0xf, 0x09 }, { 0xe, 0x02 }, { 0xf, 0x01 }, { WAIT_IDLE },
+{ 0xe, 0x01 }, { 0xf, 0x0a }, { 0xe, 0x02 }, { 0xf, 0x01 }, { WAIT_IDLE },
+{ 0xe, 0x01 }, { 0xf, 0x0b }, { 0xe, 0x02 }, { 0xf, 0x01 }, { WAIT_IDLE },
+{ 0xe, 0x01 }, { 0xf, 0x0c }, { 0xe, 0x02 }, { 0xf, 0x01 }, { WAIT_IDLE },
+{ 0xe, 0x01 }, { 0xf, 0x0d }, { 0xe, 0x02 }, { 0xf, 0x01 }, { WAIT_IDLE },
+{ 0xe, 0x01 }, { 0xf, 0x0e }, { 0xe, 0x02 }, { 0xf, 0x01 }, { WAIT_IDLE },
+{ 0xe, 0x01 }, { 0xf, 0x0f }, { 0xe, 0x02 }, { 0xf, 0x01 }, { WAIT_IDLE },
+
+/* mute on */
+{ 0x8, 0x02 },
+
+/* Now set the coefficients and so forth for the programs above */
+{ 0xb, 0x07 }, { 0xa, 0x46 }, { 0xd, 0x00 }, { 0xc, 0x00 }, { WAIT_IDLE },
+{ 0xb, 0x07 }, { 0xa, 0x49 }, { 0xd, 0x00 }, { 0xc, 0x00 }, { WAIT_IDLE },
+{ 0xb, 0x00 }, { 0xa, 0x4b }, { 0xd, 0x03 }, { 0xc, 0x11 }, { WAIT_IDLE },
+{ 0xb, 0x00 }, { 0xa, 0x4d }, { 0xd, 0x01 }, { 0xc, 0x32 }, { WAIT_IDLE },
+{ 0xb, 0x07 }, { 0xa, 0x46 }, { 0xd, 0x00 }, { 0xc, 0x00 }, { WAIT_IDLE },
+{ 0xb, 0x07 }, { 0xa, 0x49 }, { 0xd, 0x00 }, { 0xc, 0x00 }, { WAIT_IDLE },
+{ 0xb, 0x07 }, { 0xa, 0x40 }, { 0xd, 0x00 }, { 0xc, 0x00 }, { WAIT_IDLE },
+{ 0xb, 0x07 }, { 0xa, 0x41 }, { 0xd, 0x00 }, { 0xc, 0x00 }, { WAIT_IDLE },
+{ 0xb, 0x01 }, { 0xa, 0x40 }, { 0xd, 0x02 }, { 0xc, 0x40 }, { WAIT_IDLE },
+{ 0xb, 0x01 }, { 0xa, 0x41 }, { 0xd, 0x02 }, { 0xc, 0x60 }, { WAIT_IDLE },
+{ 0xb, 0x07 }, { 0xa, 0x40 }, { 0xd, 0x00 }, { 0xc, 0x00 }, { WAIT_IDLE },
+{ 0xb, 0x07 }, { 0xa, 0x41 }, { 0xd, 0x00 }, { 0xc, 0x00 }, { WAIT_IDLE },
+{ 0xb, 0x07 }, { 0xa, 0x47 }, { 0xd, 0x00 }, { 0xc, 0x00 }, { WAIT_IDLE },
+{ 0xb, 0x07 }, { 0xa, 0x4a }, { 0xd, 0x00 }, { 0xc, 0x00 }, { WAIT_IDLE },
+{ 0xb, 0x00 }, { 0xa, 0x47 }, { 0xd, 0x01 }, { 0xc, 0x00 }, { WAIT_IDLE },
+{ 0xb, 0x00 }, { 0xa, 0x4a }, { 0xd, 0x01 }, { 0xc, 0x20 }, { WAIT_IDLE },
+{ 0xb, 0x07 }, { 0xa, 0x47 }, { 0xd, 0x00 }, { 0xc, 0x00 }, { WAIT_IDLE },
+{ 0xb, 0x07 }, { 0xa, 0x4a }, { 0xd, 0x00 }, { 0xc, 0x00 }, { WAIT_IDLE },
+{ 0xb, 0x07 }, { 0xa, 0x7c }, { 0xd, 0x00 }, { 0xc, 0x00 }, { WAIT_IDLE },
+{ 0xb, 0x07 }, { 0xa, 0x7e }, { 0xd, 0x00 }, { 0xc, 0x00 }, { WAIT_IDLE },
+{ 0xb, 0x00 }, { 0xa, 0x00 }, { 0xd, 0x01 }, { 0xc, 0x1c }, { WAIT_IDLE },
+{ 0xb, 0x07 }, { 0xa, 0x7c }, { 0xd, 0x00 }, { 0xc, 0x00 }, { WAIT_IDLE },
+{ 0xb, 0x07 }, { 0xa, 0x7e }, { 0xd, 0x00 }, { 0xc, 0x00 }, { WAIT_IDLE },
+{ 0xb, 0x07 }, { 0xa, 0x44 }, { 0xd, 0x00 }, { 0xc, 0x00 }, { WAIT_IDLE },
+{ 0xb, 0x00 }, { 0xa, 0x44 }, { 0xd, 0x01 }, { 0xc, 0x00 }, { WAIT_IDLE },
+{ 0xb, 0x07 }, { 0xa, 0x44 }, { 0xd, 0x00 }, { 0xc, 0x00 }, { WAIT_IDLE },
+{ 0xb, 0x07 }, { 0xa, 0x42 }, { 0xd, 0x00 }, { 0xc, 0x00 }, { WAIT_IDLE },
+{ 0xb, 0x07 }, { 0xa, 0x43 }, { 0xd, 0x00 }, { 0xc, 0x00 }, { WAIT_IDLE },
+{ 0xb, 0x00 }, { 0xa, 0x42 }, { 0xd, 0x01 }, { 0xc, 0x1a }, { WAIT_IDLE },
+{ 0xb, 0x00 }, { 0xa, 0x43 }, { 0xd, 0x01 }, { 0xc, 0x20 }, { WAIT_IDLE },
+{ 0xb, 0x07 }, { 0xa, 0x42 }, { 0xd, 0x00 }, { 0xc, 0x00 }, { WAIT_IDLE },
+{ 0xb, 0x07 }, { 0xa, 0x43 }, { 0xd, 0x00 }, { 0xc, 0x00 }, { WAIT_IDLE },
+{ 0xb, 0x07 }, { 0xa, 0x40 }, { 0xd, 0x00 }, { 0xc, 0x00 }, { WAIT_IDLE },
+{ 0xb, 0x07 }, { 0xa, 0x41 }, { 0xd, 0x00 }, { 0xc, 0x00 }, { WAIT_IDLE },
+{ 0xb, 0x01 }, { 0xa, 0x40 }, { 0xd, 0x02 }, { 0xc, 0x40 }, { WAIT_IDLE },
+{ 0xb, 0x01 }, { 0xa, 0x41 }, { 0xd, 0x02 }, { 0xc, 0x60 }, { WAIT_IDLE },
+{ 0xb, 0x07 }, { 0xa, 0x40 }, { 0xd, 0x00 }, { 0xc, 0x00 }, { WAIT_IDLE },
+{ 0xb, 0x07 }, { 0xa, 0x41 }, { 0xd, 0x00 }, { 0xc, 0x00 }, { WAIT_IDLE },
+{ 0xb, 0x07 }, { 0xa, 0x44 }, { 0xd, 0x0f }, { 0xc, 0xff }, { WAIT_IDLE },
+{ 0xb, 0x07 }, { 0xa, 0x42 }, { 0xd, 0x00 }, { 0xc, 0x00 }, { WAIT_IDLE },
+{ 0xb, 0x07 }, { 0xa, 0x43 }, { 0xd, 0x00 }, { 0xc, 0x00 }, { WAIT_IDLE },
+{ 0xb, 0x07 }, { 0xa, 0x40 }, { 0xd, 0x00 }, { 0xc, 0x00 }, { WAIT_IDLE },
+{ 0xb, 0x07 }, { 0xa, 0x41 }, { 0xd, 0x00 }, { 0xc, 0x00 }, { WAIT_IDLE },
+{ 0xb, 0x07 }, { 0xa, 0x51 }, { 0xd, 0x06 }, { 0xc, 0x40 }, { WAIT_IDLE },
+{ 0xb, 0x07 }, { 0xa, 0x50 }, { 0xd, 0x06 }, { 0xc, 0x40 }, { WAIT_IDLE },
+{ 0xb, 0x07 }, { 0xa, 0x4f }, { 0xd, 0x03 }, { 0xc, 0x81 }, { WAIT_IDLE },
+{ 0xb, 0x07 }, { 0xa, 0x53 }, { 0xd, 0x1a }, { 0xc, 0x76 }, { WAIT_IDLE },
+{ 0xb, 0x07 }, { 0xa, 0x54 }, { 0xd, 0x0d }, { 0xc, 0x8b }, { WAIT_IDLE },
+{ 0xb, 0x07 }, { 0xa, 0x55 }, { 0xd, 0x04 }, { 0xc, 0xe9 }, { WAIT_IDLE },
+{ 0xb, 0x07 }, { 0xa, 0x56 }, { 0xd, 0x0b }, { 0xc, 0x17 }, { WAIT_IDLE },
+{ 0xb, 0x07 }, { 0xa, 0x57 }, { 0xd, 0x1a }, { 0xc, 0x38 }, { WAIT_IDLE },
+{ 0xb, 0x07 }, { 0xa, 0x58 }, { 0xd, 0x0d }, { 0xc, 0xc9 }, { WAIT_IDLE },
+{ 0xb, 0x07 }, { 0xa, 0x59 }, { 0xd, 0x04 }, { 0xc, 0x6f }, { WAIT_IDLE },
+{ 0xb, 0x07 }, { 0xa, 0x5a }, { 0xd, 0x0b }, { 0xc, 0x91 }, { WAIT_IDLE },
+{ 0xb, 0x07 }, { 0xa, 0x73 }, { 0xd, 0x14 }, { 0xc, 0xda }, { WAIT_IDLE },
+{ 0xb, 0x07 }, { 0xa, 0x74 }, { 0xd, 0x0d }, { 0xc, 0x93 }, { WAIT_IDLE },
+{ 0xb, 0x07 }, { 0xa, 0x75 }, { 0xd, 0x04 }, { 0xc, 0xd9 }, { WAIT_IDLE },
+{ 0xb, 0x07 }, { 0xa, 0x76 }, { 0xd, 0x05 }, { 0xc, 0x93 }, { WAIT_IDLE },
+{ 0xb, 0x07 }, { 0xa, 0x77 }, { 0xd, 0x14 }, { 0xc, 0xda }, { WAIT_IDLE },
+{ 0xb, 0x07 }, { 0xa, 0x78 }, { 0xd, 0x0d }, { 0xc, 0x93 }, { WAIT_IDLE },
+{ 0xb, 0x07 }, { 0xa, 0x79 }, { 0xd, 0x04 }, { 0xc, 0xd9 }, { WAIT_IDLE },
+{ 0xb, 0x07 }, { 0xa, 0x7a }, { 0xd, 0x05 }, { 0xc, 0x93 }, { WAIT_IDLE },
+{ 0xb, 0x07 }, { 0xa, 0x5e }, { 0xd, 0x03 }, { 0xc, 0x68 }, { WAIT_IDLE },
+{ 0xb, 0x07 }, { 0xa, 0x5c }, { 0xd, 0x04 }, { 0xc, 0x31 }, { WAIT_IDLE },
+{ 0xb, 0x07 }, { 0xa, 0x5d }, { 0xd, 0x04 }, { 0xc, 0x31 }, { WAIT_IDLE },
+{ 0xb, 0x07 }, { 0xa, 0x62 }, { 0xd, 0x03 }, { 0xc, 0x52 }, { WAIT_IDLE },
+{ 0xb, 0x07 }, { 0xa, 0x60 }, { 0xd, 0x04 }, { 0xc, 0x76 }, { WAIT_IDLE },
+{ 0xb, 0x07 }, { 0xa, 0x61 }, { 0xd, 0x04 }, { 0xc, 0x76 }, { WAIT_IDLE },
+{ 0xb, 0x07 }, { 0xa, 0x66 }, { 0xd, 0x03 }, { 0xc, 0x2e }, { WAIT_IDLE },
+{ 0xb, 0x07 }, { 0xa, 0x64 }, { 0xd, 0x04 }, { 0xc, 0xda }, { WAIT_IDLE },
+{ 0xb, 0x07 }, { 0xa, 0x65 }, { 0xd, 0x04 }, { 0xc, 0xda }, { WAIT_IDLE },
+{ 0xb, 0x07 }, { 0xa, 0x6a }, { 0xd, 0x02 }, { 0xc, 0xf6 }, { WAIT_IDLE },
+{ 0xb, 0x07 }, { 0xa, 0x68 }, { 0xd, 0x05 }, { 0xc, 0x62 }, { WAIT_IDLE },
+{ 0xb, 0x07 }, { 0xa, 0x69 }, { 0xd, 0x05 }, { 0xc, 0x62 }, { WAIT_IDLE },
+{ 0xb, 0x06 }, { 0xa, 0x46 }, { 0xd, 0x0a }, { 0xc, 0x22 }, { WAIT_IDLE },
+{ 0xb, 0x06 }, { 0xa, 0x48 }, { 0xd, 0x0d }, { 0xc, 0x24 }, { WAIT_IDLE },
+{ 0xb, 0x06 }, { 0xa, 0x6e }, { 0xd, 0x11 }, { 0xc, 0xd3 }, { WAIT_IDLE },
+{ 0xb, 0x06 }, { 0xa, 0x70 }, { 0xd, 0x15 }, { 0xc, 0xcb }, { WAIT_IDLE },
+{ 0xb, 0x06 }, { 0xa, 0x52 }, { 0xd, 0x20 }, { 0xc, 0x93 }, { WAIT_IDLE },
+{ 0xb, 0x06 }, { 0xa, 0x54 }, { 0xd, 0x20 }, { 0xc, 0x54 }, { WAIT_IDLE },
+{ 0xb, 0x06 }, { 0xa, 0x4a }, { 0xd, 0x27 }, { 0xc, 0x1d }, { WAIT_IDLE },
+{ 0xb, 0x06 }, { 0xa, 0x58 }, { 0xd, 0x2f }, { 0xc, 0xc8 }, { WAIT_IDLE },
+{ 0xb, 0x06 }, { 0xa, 0x5c }, { 0xd, 0x30 }, { 0xc, 0x07 }, { WAIT_IDLE },
+{ 0xb, 0x06 }, { 0xa, 0x4c }, { 0xd, 0x37 }, { 0xc, 0x90 }, { WAIT_IDLE },
+{ 0xb, 0x06 }, { 0xa, 0x60 }, { 0xd, 0x3d }, { 0xc, 0xdb }, { WAIT_IDLE },
+{ 0xb, 0x06 }, { 0xa, 0x64 }, { 0xd, 0x3e }, { 0xc, 0x42 }, { WAIT_IDLE },
+{ 0xb, 0x06 }, { 0xa, 0x4e }, { 0xd, 0x45 }, { 0xc, 0x78 }, { WAIT_IDLE },
+{ 0xb, 0x06 }, { 0xa, 0x68 }, { 0xd, 0x4c }, { 0xc, 0x48 }, { WAIT_IDLE },
+{ 0xb, 0x06 }, { 0xa, 0x6c }, { 0xd, 0x4c }, { 0xc, 0x6c }, { WAIT_IDLE },
+{ 0xb, 0x06 }, { 0xa, 0x50 }, { 0xd, 0x52 }, { 0xc, 0xe2 }, { WAIT_IDLE },
+{ 0xb, 0x06 }, { 0xa, 0x42 }, { 0xd, 0x02 }, { 0xc, 0xba }, { WAIT_IDLE },
+
+/* Some settings (?) */
+{ WAIT_IDLE }, { 0xe, 0x1e }, { 0xf, 0x14 },
+{ WAIT_IDLE }, { 0xe, 0xde }, { 0xf, 0x20 },
+{ WAIT_IDLE }, { 0xe, 0xdf }, { 0xf, 0x20 },
+
+/* some more coefficients */
+{ WAIT_IDLE }, { 0xb, 0x06 }, { 0xa, 0x78 }, { 0xd, 0x00 }, { 0xc, 0x40 },
+{ WAIT_IDLE }, { 0xb, 0x07 }, { 0xa, 0x03 }, { 0xd, 0x0f }, { 0xc, 0xff },
+{ WAIT_IDLE }, { 0xb, 0x07 }, { 0xa, 0x0b }, { 0xd, 0x0f }, { 0xc, 0xff },
+{ WAIT_IDLE }, { 0xb, 0x07 }, { 0xa, 0x02 }, { 0xd, 0x00 }, { 0xc, 0x00 },
+{ WAIT_IDLE }, { 0xb, 0x07 }, { 0xa, 0x0a }, { 0xd, 0x00 }, { 0xc, 0x00 },
+{ WAIT_IDLE }, { 0xb, 0x07 }, { 0xa, 0x46 }, { 0xd, 0x00 }, { 0xc, 0x00 },
+{ WAIT_IDLE }, { 0xb, 0x07 }, { 0xa, 0x49 }, { 0xd, 0x00 }, { 0xc, 0x00 },
+
+/* Now, for some strange reason, lets reload every page
+ and all the coefficients over again. I have *NO* idea
+ why this is done. I do know that no sound is produced
+ is this phase is omitted. */
+{ 0x9, 0x05 }, { 0xb, 0x00 }, { 0xa, 0x10 },
+
+{ 0xd, 0x00 }, { 0xc, 0x00 }, { WAIT_IDLE },
+{ 0xd, 0x00 }, { 0xc, 0x00 }, { WAIT_IDLE },
+{ 0xd, 0x02 }, { 0xc, 0x00 }, { WAIT_IDLE },
+{ 0xd, 0x00 }, { 0xc, 0x00 }, { WAIT_IDLE },
+{ 0xd, 0x00 }, { 0xc, 0x00 }, { WAIT_IDLE },
+{ 0xd, 0x00 }, { 0xc, 0x00 }, { WAIT_IDLE },
+{ 0xd, 0x00 }, { 0xc, 0x00 }, { WAIT_IDLE },
+{ 0xd, 0x00 }, { 0xc, 0x00 }, { WAIT_IDLE },
+{ 0xd, 0x00 }, { 0xc, 0x00 }, { WAIT_IDLE },
+{ 0xd, 0x00 }, { 0xc, 0x00 }, { WAIT_IDLE },
+{ 0xd, 0x00 }, { 0xc, 0x00 }, { WAIT_IDLE },
+{ 0xd, 0x00 }, { 0xc, 0x00 }, { WAIT_IDLE },
+{ 0xd, 0x00 }, { 0xc, 0x00 }, { WAIT_IDLE },
+{ 0xd, 0x00 }, { 0xc, 0x00 }, { WAIT_IDLE },
+{ 0xd, 0x00 }, { 0xc, 0x00 }, { WAIT_IDLE },
+{ 0xd, 0x00 }, { 0xc, 0x00 }, { WAIT_IDLE },
+{ 0xd, 0x00 }, { 0xc, 0x00 }, { WAIT_IDLE },
+{ 0xd, 0x00 }, { 0xc, 0x00 }, { WAIT_IDLE },
+{ 0xd, 0x00 }, { 0xc, 0x00 }, { WAIT_IDLE },
+{ 0xd, 0x00 }, { 0xc, 0x00 }, { WAIT_IDLE },
+{ 0xd, 0x00 }, { 0xc, 0x00 }, { WAIT_IDLE },
+{ 0xd, 0x00 }, { 0xc, 0x00 }, { WAIT_IDLE },
+{ 0xd, 0x00 }, { 0xc, 0x00 }, { WAIT_IDLE },
+{ 0xd, 0x00 }, { 0xc, 0x00 }, { WAIT_IDLE },
+{ 0xd, 0x00 }, { 0xc, 0x00 }, { WAIT_IDLE },
+{ 0xd, 0x00 }, { 0xc, 0x00 }, { WAIT_IDLE },
+{ 0xd, 0x00 }, { 0xc, 0x00 }, { WAIT_IDLE },
+{ 0xd, 0x00 }, { 0xc, 0x00 }, { WAIT_IDLE },
+{ 0xd, 0x00 }, { 0xc, 0x00 }, { WAIT_IDLE },
+{ 0xd, 0x00 }, { 0xc, 0x00 }, { WAIT_IDLE },
+{ 0xd, 0x00 }, { 0xc, 0x00 }, { WAIT_IDLE },
+{ 0xd, 0x00 }, { 0xc, 0x00 }, { WAIT_IDLE },
+{ 0xd, 0x00 }, { 0xc, 0x00 }, { WAIT_IDLE },
+{ 0xd, 0x00 }, { 0xc, 0x00 }, { WAIT_IDLE },
+{ 0xd, 0x00 }, { 0xc, 0x00 }, { WAIT_IDLE },
+{ 0xd, 0x00 }, { 0xc, 0x00 }, { WAIT_IDLE },
+{ 0xd, 0x00 }, { 0xc, 0x00 }, { WAIT_IDLE },
+{ 0xd, 0x00 }, { 0xc, 0x00 }, { WAIT_IDLE },
+{ 0xd, 0x00 }, { 0xc, 0x00 }, { WAIT_IDLE },
+{ 0xd, 0x00 }, { 0xc, 0x00 }, { WAIT_IDLE },
+{ 0xd, 0x00 }, { 0xc, 0x00 }, { WAIT_IDLE },
+{ 0xd, 0x00 }, { 0xc, 0x00 }, { WAIT_IDLE },
+{ 0xd, 0x00 }, { 0xc, 0x00 }, { WAIT_IDLE },
+{ 0xd, 0x00 }, { 0xc, 0x00 }, { WAIT_IDLE },
+{ 0xd, 0x00 }, { 0xc, 0x00 }, { WAIT_IDLE },
+{ 0xd, 0x00 }, { 0xc, 0x00 }, { WAIT_IDLE },
+{ 0xd, 0x00 }, { 0xc, 0x00 }, { WAIT_IDLE },
+{ 0xd, 0x00 }, { 0xc, 0x00 }, { WAIT_IDLE },
+
+{ 0x9, 0x05 }, { 0xb, 0x01 }, { 0xa, 0x10 },
+
+{ 0xd, 0x01 }, { 0xc, 0xc0 }, { WAIT_IDLE },
+{ 0xd, 0x01 }, { 0xc, 0xfa }, { WAIT_IDLE },
+{ 0xd, 0x00 }, { 0xc, 0x1a }, { WAIT_IDLE },
+{ 0xd, 0x00 }, { 0xc, 0x00 }, { WAIT_IDLE },
+{ 0xd, 0x00 }, { 0xc, 0x00 }, { WAIT_IDLE },
+{ 0xd, 0x00 }, { 0xc, 0x00 }, { WAIT_IDLE },
+{ 0xd, 0x00 }, { 0xc, 0x00 }, { WAIT_IDLE },
+{ 0xd, 0x00 }, { 0xc, 0x00 }, { WAIT_IDLE },
+{ 0xd, 0x00 }, { 0xc, 0x00 }, { WAIT_IDLE },
+{ 0xd, 0x00 }, { 0xc, 0x00 }, { WAIT_IDLE },
+{ 0xd, 0x00 }, { 0xc, 0x00 }, { WAIT_IDLE },
+{ 0xd, 0x00 }, { 0xc, 0x00 }, { WAIT_IDLE },
+{ 0xd, 0x00 }, { 0xc, 0x00 }, { WAIT_IDLE },
+{ 0xd, 0x00 }, { 0xc, 0x00 }, { WAIT_IDLE },
+{ 0xd, 0x00 }, { 0xc, 0x00 }, { WAIT_IDLE },
+{ 0xd, 0x00 }, { 0xc, 0x00 }, { WAIT_IDLE },
+{ 0xd, 0x00 }, { 0xc, 0x00 }, { WAIT_IDLE },
+{ 0xd, 0x00 }, { 0xc, 0x00 }, { WAIT_IDLE },
+{ 0xd, 0x00 }, { 0xc, 0x00 }, { WAIT_IDLE },
+{ 0xd, 0x00 }, { 0xc, 0x00 }, { WAIT_IDLE },
+{ 0xd, 0x00 }, { 0xc, 0x00 }, { WAIT_IDLE },
+{ 0xd, 0x00 }, { 0xc, 0x00 }, { WAIT_IDLE },
+{ 0xd, 0x00 }, { 0xc, 0x00 }, { WAIT_IDLE },
+{ 0xd, 0x00 }, { 0xc, 0x00 }, { WAIT_IDLE },
+{ 0xd, 0x00 }, { 0xc, 0x00 }, { WAIT_IDLE },
+{ 0xd, 0x00 }, { 0xc, 0x00 }, { WAIT_IDLE },
+{ 0xd, 0x00 }, { 0xc, 0x00 }, { WAIT_IDLE },
+{ 0xd, 0x00 }, { 0xc, 0x00 }, { WAIT_IDLE },
+{ 0xd, 0x00 }, { 0xc, 0x00 }, { WAIT_IDLE },
+{ 0xd, 0x00 }, { 0xc, 0x00 }, { WAIT_IDLE },
+{ 0xd, 0x00 }, { 0xc, 0x00 }, { WAIT_IDLE },
+{ 0xd, 0x00 }, { 0xc, 0x00 }, { WAIT_IDLE },
+{ 0xd, 0x00 }, { 0xc, 0x00 }, { WAIT_IDLE },
+{ 0xd, 0x00 }, { 0xc, 0x00 }, { WAIT_IDLE },
+{ 0xd, 0x00 }, { 0xc, 0x00 }, { WAIT_IDLE },
+{ 0xd, 0x00 }, { 0xc, 0x00 }, { WAIT_IDLE },
+{ 0xd, 0x00 }, { 0xc, 0x00 }, { WAIT_IDLE },
+{ 0xd, 0x00 }, { 0xc, 0x00 }, { WAIT_IDLE },
+{ 0xd, 0x00 }, { 0xc, 0x00 }, { WAIT_IDLE },
+{ 0xd, 0x00 }, { 0xc, 0x00 }, { WAIT_IDLE },
+{ 0xd, 0x00 }, { 0xc, 0x00 }, { WAIT_IDLE },
+{ 0xd, 0x00 }, { 0xc, 0x00 }, { WAIT_IDLE },
+{ 0xd, 0x00 }, { 0xc, 0x00 }, { WAIT_IDLE },
+{ 0xd, 0x00 }, { 0xc, 0x00 }, { WAIT_IDLE },
+{ 0xd, 0x00 }, { 0xc, 0x00 }, { WAIT_IDLE },
+{ 0xd, 0x00 }, { 0xc, 0x00 }, { WAIT_IDLE },
+{ 0xd, 0x00 }, { 0xc, 0x00 }, { WAIT_IDLE },
+{ 0xd, 0x00 }, { 0xc, 0x00 }, { WAIT_IDLE },
+
+{ WAIT_IDLE }, { WAIT_IDLE },
+
+{ 0x9, 0x05 }, { 0xb, 0x02 }, { 0xa, 0x10 },
+
+{ 0xc, 0x46 }, { WAIT_IDLE },
+{ 0xc, 0x46 }, { WAIT_IDLE },
+{ 0xc, 0x00 }, { WAIT_IDLE },
+{ 0xc, 0x00 }, { WAIT_IDLE },
+{ 0xc, 0x00 }, { WAIT_IDLE },
+{ 0xc, 0x00 }, { WAIT_IDLE },
+{ 0xc, 0x00 }, { WAIT_IDLE },
+{ 0xc, 0x00 }, { WAIT_IDLE },
+{ 0xc, 0x00 }, { WAIT_IDLE },
+{ 0xc, 0x00 }, { WAIT_IDLE },
+{ 0xc, 0x00 }, { WAIT_IDLE },
+{ 0xc, 0x00 }, { WAIT_IDLE },
+{ 0xc, 0x00 }, { WAIT_IDLE },
+{ 0xc, 0x00 }, { WAIT_IDLE },
+{ 0xc, 0x00 }, { WAIT_IDLE },
+{ 0xc, 0x00 }, { WAIT_IDLE },
+{ 0xc, 0x00 }, { WAIT_IDLE },
+{ 0xc, 0x00 }, { WAIT_IDLE },
+{ 0xc, 0x00 }, { WAIT_IDLE },
+{ 0xc, 0x00 }, { WAIT_IDLE },
+{ 0xc, 0x00 }, { WAIT_IDLE },
+{ 0xc, 0x00 }, { WAIT_IDLE },
+{ 0xc, 0x00 }, { WAIT_IDLE },
+{ 0xc, 0x00 }, { WAIT_IDLE },
+{ 0xc, 0x00 }, { WAIT_IDLE },
+{ 0xc, 0x00 }, { WAIT_IDLE },
+{ 0xc, 0x00 }, { WAIT_IDLE },
+{ 0xc, 0x00 }, { WAIT_IDLE },
+{ 0xc, 0x00 }, { WAIT_IDLE },
+{ 0xc, 0x00 }, { WAIT_IDLE },
+{ 0xc, 0x00 }, { WAIT_IDLE },
+{ 0xc, 0x00 }, { WAIT_IDLE },
+{ 0xc, 0x00 }, { WAIT_IDLE },
+{ 0xc, 0x00 }, { WAIT_IDLE },
+{ 0xc, 0x00 }, { WAIT_IDLE },
+{ 0xc, 0x00 }, { WAIT_IDLE },
+{ 0xc, 0x00 }, { WAIT_IDLE },
+{ 0xc, 0x00 }, { WAIT_IDLE },
+{ 0xc, 0x00 }, { WAIT_IDLE },
+{ 0xc, 0x00 }, { WAIT_IDLE },
+{ 0xc, 0x00 }, { WAIT_IDLE },
+{ 0xc, 0x00 }, { WAIT_IDLE },
+{ 0xc, 0x00 }, { WAIT_IDLE },
+{ 0xc, 0x00 }, { WAIT_IDLE },
+{ 0xc, 0x00 }, { WAIT_IDLE },
+{ 0xc, 0x00 }, { WAIT_IDLE },
+{ 0xc, 0x00 }, { WAIT_IDLE },
+{ 0xc, 0x00 }, { WAIT_IDLE },
+
+{ 0x9, 0x05 }, { 0xb, 0x03 }, { 0xa, 0x10 },
+
+{ 0xc, 0x00 }, { WAIT_IDLE },
+{ 0xc, 0x00 }, { WAIT_IDLE },
+{ 0xc, 0x00 }, { WAIT_IDLE },
+{ 0xc, 0x00 }, { WAIT_IDLE },
+{ 0xc, 0x00 }, { WAIT_IDLE },
+{ 0xc, 0x00 }, { WAIT_IDLE },
+{ 0xc, 0x00 }, { WAIT_IDLE },
+{ 0xc, 0x00 }, { WAIT_IDLE },
+{ 0xc, 0x00 }, { WAIT_IDLE },
+{ 0xc, 0x00 }, { WAIT_IDLE },
+{ 0xc, 0x00 }, { WAIT_IDLE },
+{ 0xc, 0x00 }, { WAIT_IDLE },
+{ 0xc, 0x00 }, { WAIT_IDLE },
+{ 0xc, 0x00 }, { WAIT_IDLE },
+{ 0xc, 0x00 }, { WAIT_IDLE },
+{ 0xc, 0x00 }, { WAIT_IDLE },
+{ 0xc, 0x00 }, { WAIT_IDLE },
+{ 0xc, 0x00 }, { WAIT_IDLE },
+{ 0xc, 0x00 }, { WAIT_IDLE },
+{ 0xc, 0x00 }, { WAIT_IDLE },
+{ 0xc, 0x00 }, { WAIT_IDLE },
+{ 0xc, 0x00 }, { WAIT_IDLE },
+{ 0xc, 0x00 }, { WAIT_IDLE },
+{ 0xc, 0x00 }, { WAIT_IDLE },
+{ 0xc, 0x00 }, { WAIT_IDLE },
+{ 0xc, 0x00 }, { WAIT_IDLE },
+{ 0xc, 0x00 }, { WAIT_IDLE },
+{ 0xc, 0x00 }, { WAIT_IDLE },
+{ 0xc, 0x00 }, { WAIT_IDLE },
+{ 0xc, 0x00 }, { WAIT_IDLE },
+{ 0xc, 0x00 }, { WAIT_IDLE },
+{ 0xc, 0x00 }, { WAIT_IDLE },
+{ 0xc, 0x00 }, { WAIT_IDLE },
+{ 0xc, 0x00 }, { WAIT_IDLE },
+{ 0xc, 0x00 }, { WAIT_IDLE },
+{ 0xc, 0x00 }, { WAIT_IDLE },
+{ 0xc, 0x00 }, { WAIT_IDLE },
+{ 0xc, 0x00 }, { WAIT_IDLE },
+{ 0xc, 0x00 }, { WAIT_IDLE },
+{ 0xc, 0x00 }, { WAIT_IDLE },
+{ 0xc, 0x00 }, { WAIT_IDLE },
+{ 0xc, 0x00 }, { WAIT_IDLE },
+{ 0xc, 0x00 }, { WAIT_IDLE },
+{ 0xc, 0x00 }, { WAIT_IDLE },
+{ 0xc, 0x00 }, { WAIT_IDLE },
+{ 0xc, 0x00 }, { WAIT_IDLE },
+{ 0xc, 0x00 }, { WAIT_IDLE },
+{ 0xc, 0x00 }, { WAIT_IDLE },
+
+{ 0x9, 0x05 }, { 0xb, 0x04 }, { 0xa, 0x10 },
+
+{ 0xc, 0x00 }, { WAIT_IDLE },
+{ 0xc, 0x00 }, { WAIT_IDLE },
+{ 0xc, 0x00 }, { WAIT_IDLE },
+{ 0xc, 0x00 }, { WAIT_IDLE },
+{ 0xc, 0x00 }, { WAIT_IDLE },
+{ 0xc, 0x00 }, { WAIT_IDLE },
+{ 0xc, 0x00 }, { WAIT_IDLE },
+{ 0xc, 0x00 }, { WAIT_IDLE },
+{ 0xc, 0x00 }, { WAIT_IDLE },
+{ 0xc, 0x00 }, { WAIT_IDLE },
+{ 0xc, 0x00 }, { WAIT_IDLE },
+{ 0xc, 0x00 }, { WAIT_IDLE },
+{ 0xc, 0x00 }, { WAIT_IDLE },
+{ 0xc, 0x00 }, { WAIT_IDLE },
+{ 0xc, 0x00 }, { WAIT_IDLE },
+{ 0xc, 0x00 }, { WAIT_IDLE },
+{ 0xc, 0x00 }, { WAIT_IDLE },
+{ 0xc, 0x00 }, { WAIT_IDLE },
+{ 0xc, 0x00 }, { WAIT_IDLE },
+{ 0xc, 0x00 }, { WAIT_IDLE },
+{ 0xc, 0x00 }, { WAIT_IDLE },
+{ 0xc, 0x00 }, { WAIT_IDLE },
+{ 0xc, 0x00 }, { WAIT_IDLE },
+{ 0xc, 0x00 }, { WAIT_IDLE },
+{ 0xc, 0x00 }, { WAIT_IDLE },
+{ 0xc, 0x00 }, { WAIT_IDLE },
+{ 0xc, 0x00 }, { WAIT_IDLE },
+{ 0xc, 0x00 }, { WAIT_IDLE },
+{ 0xc, 0x00 }, { WAIT_IDLE },
+{ 0xc, 0x00 }, { WAIT_IDLE },
+{ 0xc, 0x00 }, { WAIT_IDLE },
+{ 0xc, 0x00 }, { WAIT_IDLE },
+{ 0xc, 0x00 }, { WAIT_IDLE },
+{ 0xc, 0x00 }, { WAIT_IDLE },
+{ 0xc, 0x00 }, { WAIT_IDLE },
+{ 0xc, 0x00 }, { WAIT_IDLE },
+{ 0xc, 0x00 }, { WAIT_IDLE },
+{ 0xc, 0x00 }, { WAIT_IDLE },
+{ 0xc, 0x00 }, { WAIT_IDLE },
+{ 0xc, 0x00 }, { WAIT_IDLE },
+{ 0xc, 0x00 }, { WAIT_IDLE },
+{ 0xc, 0x00 }, { WAIT_IDLE },
+{ 0xc, 0x00 }, { WAIT_IDLE },
+{ 0xc, 0x00 }, { WAIT_IDLE },
+{ 0xc, 0x00 }, { WAIT_IDLE },
+{ 0xc, 0x00 }, { WAIT_IDLE },
+{ 0xc, 0x00 }, { WAIT_IDLE },
+{ 0xc, 0x00 }, { WAIT_IDLE },
+
+/* Page six v.2 */
+{ 0x9, 0x01 }, { 0xb, 0x06 },
+
+{ 0xa, 0x10 }, { 0xd, 0x00 }, { 0xc, 0x00 }, { WAIT_IDLE },
+{ 0xa, 0x12 }, { 0xd, 0x00 }, { 0xc, 0x00 }, { WAIT_IDLE },
+{ 0xa, 0x14 }, { 0xd, 0x00 }, { 0xc, 0x00 }, { WAIT_IDLE },
+{ 0xa, 0x16 }, { 0xd, 0x00 }, { 0xc, 0x00 }, { WAIT_IDLE },
+{ 0xa, 0x18 }, { 0xd, 0x00 }, { 0xc, 0x00 }, { WAIT_IDLE },
+{ 0xa, 0x1a }, { 0xd, 0x00 }, { 0xc, 0x00 }, { WAIT_IDLE },
+{ 0xa, 0x1c }, { 0xd, 0x00 }, { 0xc, 0x00 }, { WAIT_IDLE },
+{ 0xa, 0x1e }, { 0xd, 0x00 }, { 0xc, 0x00 }, { WAIT_IDLE },
+{ 0xa, 0x20 }, { 0xd, 0x00 }, { 0xc, 0x00 }, { WAIT_IDLE },
+{ 0xa, 0x22 }, { 0xd, 0x00 }, { 0xc, 0x00 }, { WAIT_IDLE },
+{ 0xa, 0x24 }, { 0xd, 0x00 }, { 0xc, 0x00 }, { WAIT_IDLE },
+{ 0xa, 0x26 }, { 0xd, 0x00 }, { 0xc, 0x00 }, { WAIT_IDLE },
+{ 0xa, 0x28 }, { 0xd, 0x00 }, { 0xc, 0x00 }, { WAIT_IDLE },
+{ 0xa, 0x2a }, { 0xd, 0x00 }, { 0xc, 0x00 }, { WAIT_IDLE },
+{ 0xa, 0x2c }, { 0xd, 0x00 }, { 0xc, 0x00 }, { WAIT_IDLE },
+{ 0xa, 0x2e }, { 0xd, 0x00 }, { 0xc, 0x00 }, { WAIT_IDLE },
+{ 0xa, 0x30 }, { 0xd, 0x00 }, { 0xc, 0x00 }, { WAIT_IDLE },
+{ 0xa, 0x32 }, { 0xd, 0x00 }, { 0xc, 0x00 }, { WAIT_IDLE },
+{ 0xa, 0x34 }, { 0xd, 0x00 }, { 0xc, 0x00 }, { WAIT_IDLE },
+{ 0xa, 0x36 }, { 0xd, 0x00 }, { 0xc, 0x00 }, { WAIT_IDLE },
+{ 0xa, 0x38 }, { 0xd, 0x00 }, { 0xc, 0x00 }, { WAIT_IDLE },
+{ 0xa, 0x3a }, { 0xd, 0x00 }, { 0xc, 0x00 }, { WAIT_IDLE },
+{ 0xa, 0x3c }, { 0xd, 0x00 }, { 0xc, 0x00 }, { WAIT_IDLE },
+{ 0xa, 0x3e }, { 0xd, 0x00 }, { 0xc, 0x00 }, { WAIT_IDLE },
+
+{ 0x9, 0x05 }, { 0xb, 0x07 }, { 0xa, 0x10 },
+
+{ 0xd, 0x0f }, { 0xc, 0xff }, { WAIT_IDLE },
+{ 0xd, 0x0f }, { 0xc, 0xff }, { WAIT_IDLE },
+{ 0xd, 0x00 }, { 0xc, 0x00 }, { WAIT_IDLE },
+{ 0xd, 0x00 }, { 0xc, 0x00 }, { WAIT_IDLE },
+{ 0xd, 0x00 }, { 0xc, 0x00 }, { WAIT_IDLE },
+{ 0xd, 0x00 }, { 0xc, 0x00 }, { WAIT_IDLE },
+{ 0xd, 0x00 }, { 0xc, 0x00 }, { WAIT_IDLE },
+{ 0xd, 0x00 }, { 0xc, 0x00 }, { WAIT_IDLE },
+{ 0xd, 0x00 }, { 0xc, 0x00 }, { WAIT_IDLE },
+{ 0xd, 0x00 }, { 0xc, 0x00 }, { WAIT_IDLE },
+{ 0xd, 0x00 }, { 0xc, 0x00 }, { WAIT_IDLE },
+{ 0xd, 0x00 }, { 0xc, 0x00 }, { WAIT_IDLE },
+{ 0xd, 0x00 }, { 0xc, 0x00 }, { WAIT_IDLE },
+{ 0xd, 0x00 }, { 0xc, 0x00 }, { WAIT_IDLE },
+{ 0xd, 0x00 }, { 0xc, 0x00 }, { WAIT_IDLE },
+{ 0xd, 0x00 }, { 0xc, 0x00 }, { WAIT_IDLE },
+{ 0xd, 0x00 }, { 0xc, 0x00 }, { WAIT_IDLE },
+{ 0xd, 0x00 }, { 0xc, 0x00 }, { WAIT_IDLE },
+{ 0xd, 0x00 }, { 0xc, 0x00 }, { WAIT_IDLE },
+{ 0xd, 0x00 }, { 0xc, 0x00 }, { WAIT_IDLE },
+{ 0xd, 0x00 }, { 0xc, 0x00 }, { WAIT_IDLE },
+{ 0xd, 0x00 }, { 0xc, 0x00 }, { WAIT_IDLE },
+{ 0xd, 0x00 }, { 0xc, 0x00 }, { WAIT_IDLE },
+{ 0xd, 0x00 }, { 0xc, 0x00 }, { WAIT_IDLE },
+{ 0xd, 0x00 }, { 0xc, 0x00 }, { WAIT_IDLE },
+{ 0xd, 0x00 }, { 0xc, 0x00 }, { WAIT_IDLE },
+{ 0xd, 0x00 }, { 0xc, 0x00 }, { WAIT_IDLE },
+{ 0xd, 0x00 }, { 0xc, 0x00 }, { WAIT_IDLE },
+{ 0xd, 0x00 }, { 0xc, 0x00 }, { WAIT_IDLE },
+{ 0xd, 0x00 }, { 0xc, 0x00 }, { WAIT_IDLE },
+{ 0xd, 0x00 }, { 0xc, 0x00 }, { WAIT_IDLE },
+{ 0xd, 0x00 }, { 0xc, 0x00 }, { WAIT_IDLE },
+{ 0xd, 0x00 }, { 0xc, 0x00 }, { WAIT_IDLE },
+{ 0xd, 0x00 }, { 0xc, 0x00 }, { WAIT_IDLE },
+{ 0xd, 0x00 }, { 0xc, 0x00 }, { WAIT_IDLE },
+{ 0xd, 0x00 }, { 0xc, 0x00 }, { WAIT_IDLE },
+{ 0xd, 0x00 }, { 0xc, 0x00 }, { WAIT_IDLE },
+{ 0xd, 0x00 }, { 0xc, 0x00 }, { WAIT_IDLE },
+{ 0xd, 0x00 }, { 0xc, 0x00 }, { WAIT_IDLE },
+{ 0xd, 0x00 }, { 0xc, 0x00 }, { WAIT_IDLE },
+{ 0xd, 0x00 }, { 0xc, 0x00 }, { WAIT_IDLE },
+{ 0xd, 0x00 }, { 0xc, 0x00 }, { WAIT_IDLE },
+{ 0xd, 0x00 }, { 0xc, 0x00 }, { WAIT_IDLE },
+{ 0xd, 0x00 }, { 0xc, 0x00 }, { WAIT_IDLE },
+{ 0xd, 0x00 }, { 0xc, 0x00 }, { WAIT_IDLE },
+{ 0xd, 0x00 }, { 0xc, 0x00 }, { WAIT_IDLE },
+{ 0xd, 0x00 }, { 0xc, 0x00 }, { WAIT_IDLE },
+{ 0xd, 0x00 }, { 0xc, 0x00 }, { WAIT_IDLE },
+
+{ 0xe, 0x01 }, { 0xf, 0x00 }, { WAIT_IDLE },
+{ 0xe, 0x02 }, { 0xf, 0x00 }, { WAIT_IDLE },
+{ 0xe, 0x01 }, { 0xf, 0x01 }, { WAIT_IDLE },
+{ 0xe, 0x02 }, { 0xf, 0x00 }, { WAIT_IDLE },
+{ 0xe, 0x01 }, { 0xf, 0x02 }, { WAIT_IDLE },
+{ 0xe, 0x02 }, { 0xf, 0x00 }, { WAIT_IDLE },
+{ 0xe, 0x01 }, { 0xf, 0x03 }, { WAIT_IDLE },
+{ 0xe, 0x02 }, { 0xf, 0x00 }, { WAIT_IDLE },
+{ 0xe, 0x01 }, { 0xf, 0x04 }, { WAIT_IDLE },
+{ 0xe, 0x02 }, { 0xf, 0x00 }, { WAIT_IDLE },
+{ 0xe, 0x01 }, { 0xf, 0x05 }, { WAIT_IDLE },
+{ 0xe, 0x02 }, { 0xf, 0x00 }, { WAIT_IDLE },
+{ 0xe, 0x01 }, { 0xf, 0x06 }, { WAIT_IDLE },
+{ 0xe, 0x02 }, { 0xf, 0x00 }, { WAIT_IDLE },
+{ 0xe, 0x01 }, { 0xf, 0x07 }, { WAIT_IDLE },
+{ 0xe, 0x02 }, { 0xf, 0x00 }, { WAIT_IDLE },
+{ 0xe, 0xb0 }, { 0xf, 0x20 }, { WAIT_IDLE },
+{ 0xe, 0xb1 }, { 0xf, 0x20 }, { WAIT_IDLE },
+{ 0xe, 0xb2 }, { 0xf, 0x20 }, { WAIT_IDLE },
+{ 0xe, 0xb3 }, { 0xf, 0x20 }, { WAIT_IDLE },
+{ 0xe, 0xb4 }, { 0xf, 0x20 }, { WAIT_IDLE },
+{ 0xe, 0xb5 }, { 0xf, 0x20 }, { WAIT_IDLE },
+{ 0xe, 0xb6 }, { 0xf, 0x20 }, { WAIT_IDLE },
+{ 0xe, 0xb7 }, { 0xf, 0x20 }, { WAIT_IDLE },
+{ 0xe, 0xf0 }, { 0xf, 0x20 }, { WAIT_IDLE },
+{ 0xe, 0xf1 }, { 0xf, 0x20 }, { WAIT_IDLE },
+{ 0xe, 0xf2 }, { 0xf, 0x20 }, { WAIT_IDLE },
+{ 0xe, 0xf3 }, { 0xf, 0x20 }, { WAIT_IDLE },
+{ 0xe, 0xf4 }, { 0xf, 0x20 }, { WAIT_IDLE },
+{ 0xe, 0xf5 }, { 0xf, 0x20 }, { WAIT_IDLE },
+{ 0xe, 0xf6 }, { 0xf, 0x20 }, { WAIT_IDLE },
+{ 0xe, 0xf7 }, { 0xf, 0x20 }, { WAIT_IDLE },
+{ 0xe, 0x10 }, { 0xf, 0xff }, { WAIT_IDLE },
+{ 0xe, 0x11 }, { 0xf, 0xff }, { WAIT_IDLE },
+{ 0xe, 0x12 }, { 0xf, 0xff }, { WAIT_IDLE },
+{ 0xe, 0x13 }, { 0xf, 0xff }, { WAIT_IDLE },
+{ 0xe, 0x14 }, { 0xf, 0xff }, { WAIT_IDLE },
+{ 0xe, 0x15 }, { 0xf, 0xff }, { WAIT_IDLE },
+{ 0xe, 0x16 }, { 0xf, 0xff }, { WAIT_IDLE },
+{ 0xe, 0x17 }, { 0xf, 0xff }, { WAIT_IDLE },
+{ 0xe, 0x20 }, { 0xf, 0xff }, { WAIT_IDLE },
+{ 0xe, 0x21 }, { 0xf, 0xff }, { WAIT_IDLE },
+{ 0xe, 0x22 }, { 0xf, 0xff }, { WAIT_IDLE },
+{ 0xe, 0x23 }, { 0xf, 0xff }, { WAIT_IDLE },
+{ 0xe, 0x24 }, { 0xf, 0xff }, { WAIT_IDLE },
+{ 0xe, 0x25 }, { 0xf, 0xff }, { WAIT_IDLE },
+{ 0xe, 0x26 }, { 0xf, 0xff }, { WAIT_IDLE },
+{ 0xe, 0x27 }, { 0xf, 0xff }, { WAIT_IDLE },
+{ 0xe, 0x30 }, { 0xf, 0x00 }, { WAIT_IDLE },
+{ 0xe, 0x31 }, { 0xf, 0x00 }, { WAIT_IDLE },
+{ 0xe, 0x32 }, { 0xf, 0x00 }, { WAIT_IDLE },
+{ 0xe, 0x33 }, { 0xf, 0x00 }, { WAIT_IDLE },
+{ 0xe, 0x34 }, { 0xf, 0x00 }, { WAIT_IDLE },
+{ 0xe, 0x35 }, { 0xf, 0x00 }, { WAIT_IDLE },
+{ 0xe, 0x36 }, { 0xf, 0x00 }, { WAIT_IDLE },
+{ 0xe, 0x37 }, { 0xf, 0x00 }, { WAIT_IDLE },
+{ 0xe, 0x40 }, { 0xf, 0x00 }, { WAIT_IDLE },
+{ 0xe, 0x41 }, { 0xf, 0x00 }, { WAIT_IDLE },
+{ 0xe, 0x42 }, { 0xf, 0x00 }, { WAIT_IDLE },
+{ 0xe, 0x43 }, { 0xf, 0x00 }, { WAIT_IDLE },
+{ 0xe, 0x44 }, { 0xf, 0x00 }, { WAIT_IDLE },
+{ 0xe, 0x45 }, { 0xf, 0x00 }, { WAIT_IDLE },
+{ 0xe, 0x46 }, { 0xf, 0x00 }, { WAIT_IDLE },
+{ 0xe, 0x47 }, { 0xf, 0x00 }, { WAIT_IDLE },
+{ 0xe, 0x50 }, { 0xf, 0x00 }, { WAIT_IDLE },
+{ 0xe, 0x51 }, { 0xf, 0x00 }, { WAIT_IDLE },
+{ 0xe, 0x52 }, { 0xf, 0x00 }, { WAIT_IDLE },
+{ 0xe, 0x53 }, { 0xf, 0x00 }, { WAIT_IDLE },
+{ 0xe, 0x54 }, { 0xf, 0x00 }, { WAIT_IDLE },
+{ 0xe, 0x55 }, { 0xf, 0x00 }, { WAIT_IDLE },
+{ 0xe, 0x56 }, { 0xf, 0x00 }, { WAIT_IDLE },
+{ 0xe, 0x57 }, { 0xf, 0x00 }, { WAIT_IDLE },
+{ 0xe, 0x60 }, { 0xf, 0x00 }, { WAIT_IDLE },
+{ 0xe, 0x61 }, { 0xf, 0x00 }, { WAIT_IDLE },
+{ 0xe, 0x62 }, { 0xf, 0x00 }, { WAIT_IDLE },
+{ 0xe, 0x63 }, { 0xf, 0x00 }, { WAIT_IDLE },
+{ 0xe, 0x64 }, { 0xf, 0x00 }, { WAIT_IDLE },
+{ 0xe, 0x65 }, { 0xf, 0x00 }, { WAIT_IDLE },
+{ 0xe, 0x66 }, { 0xf, 0x00 }, { WAIT_IDLE },
+{ 0xe, 0x67 }, { 0xf, 0x00 }, { WAIT_IDLE },
+{ 0xe, 0x70 }, { 0xf, 0xc0 }, { WAIT_IDLE },
+{ 0xe, 0x71 }, { 0xf, 0xc0 }, { WAIT_IDLE },
+{ 0xe, 0x72 }, { 0xf, 0xc0 }, { WAIT_IDLE },
+{ 0xe, 0x73 }, { 0xf, 0xc0 }, { WAIT_IDLE },
+{ 0xe, 0x74 }, { 0xf, 0xc0 }, { WAIT_IDLE },
+{ 0xe, 0x75 }, { 0xf, 0xc0 }, { WAIT_IDLE },
+{ 0xe, 0x76 }, { 0xf, 0xc0 }, { WAIT_IDLE },
+{ 0xe, 0x77 }, { 0xf, 0xc0 }, { WAIT_IDLE },
+{ 0xe, 0x80 }, { 0xf, 0x00 }, { WAIT_IDLE },
+{ 0xe, 0x81 }, { 0xf, 0x00 }, { WAIT_IDLE },
+{ 0xe, 0x82 }, { 0xf, 0x00 }, { WAIT_IDLE },
+{ 0xe, 0x83 }, { 0xf, 0x00 }, { WAIT_IDLE },
+{ 0xe, 0x84 }, { 0xf, 0x00 }, { WAIT_IDLE },
+{ 0xe, 0x85 }, { 0xf, 0x00 }, { WAIT_IDLE },
+{ 0xe, 0x86 }, { 0xf, 0x00 }, { WAIT_IDLE },
+{ 0xe, 0x87 }, { 0xf, 0x00 }, { WAIT_IDLE },
+{ 0xe, 0x90 }, { 0xf, 0x00 }, { WAIT_IDLE },
+{ 0xe, 0x91 }, { 0xf, 0x00 }, { WAIT_IDLE },
+{ 0xe, 0x92 }, { 0xf, 0x00 }, { WAIT_IDLE },
+{ 0xe, 0x93 }, { 0xf, 0x00 }, { WAIT_IDLE },
+{ 0xe, 0x94 }, { 0xf, 0x00 }, { WAIT_IDLE },
+{ 0xe, 0x95 }, { 0xf, 0x00 }, { WAIT_IDLE },
+{ 0xe, 0x96 }, { 0xf, 0x00 }, { WAIT_IDLE },
+{ 0xe, 0x97 }, { 0xf, 0x00 }, { WAIT_IDLE },
+{ 0xe, 0xa0 }, { 0xf, 0x00 }, { WAIT_IDLE },
+{ 0xe, 0xa1 }, { 0xf, 0x00 }, { WAIT_IDLE },
+{ 0xe, 0xa2 }, { 0xf, 0x00 }, { WAIT_IDLE },
+{ 0xe, 0xa3 }, { 0xf, 0x00 }, { WAIT_IDLE },
+{ 0xe, 0xa4 }, { 0xf, 0x00 }, { WAIT_IDLE },
+{ 0xe, 0xa5 }, { 0xf, 0x00 }, { WAIT_IDLE },
+{ 0xe, 0xa6 }, { 0xf, 0x00 }, { WAIT_IDLE },
+{ 0xe, 0xa7 }, { 0xf, 0x00 }, { WAIT_IDLE },
+{ 0xe, 0xc0 }, { 0xf, 0x00 }, { WAIT_IDLE },
+{ 0xe, 0xc1 }, { 0xf, 0x00 }, { WAIT_IDLE },
+{ 0xe, 0xc2 }, { 0xf, 0x00 }, { WAIT_IDLE },
+{ 0xe, 0xc3 }, { 0xf, 0x00 }, { WAIT_IDLE },
+{ 0xe, 0xc4 }, { 0xf, 0x00 }, { WAIT_IDLE },
+{ 0xe, 0xc5 }, { 0xf, 0x00 }, { WAIT_IDLE },
+{ 0xe, 0xc6 }, { 0xf, 0x00 }, { WAIT_IDLE },
+{ 0xe, 0xc7 }, { 0xf, 0x00 }, { WAIT_IDLE },
+{ 0xe, 0xd0 }, { 0xf, 0x00 }, { WAIT_IDLE },
+{ 0xe, 0xd1 }, { 0xf, 0x00 }, { WAIT_IDLE },
+{ 0xe, 0xd2 }, { 0xf, 0x00 }, { WAIT_IDLE },
+{ 0xe, 0xd3 }, { 0xf, 0x00 }, { WAIT_IDLE },
+{ 0xe, 0xd4 }, { 0xf, 0x00 }, { WAIT_IDLE },
+{ 0xe, 0xd5 }, { 0xf, 0x00 }, { WAIT_IDLE },
+{ 0xe, 0xd6 }, { 0xf, 0x00 }, { WAIT_IDLE },
+{ 0xe, 0xd7 }, { 0xf, 0x00 }, { WAIT_IDLE },
+{ 0xe, 0xe0 }, { 0xf, 0x00 }, { WAIT_IDLE },
+{ 0xe, 0xe1 }, { 0xf, 0x00 }, { WAIT_IDLE },
+{ 0xe, 0xe2 }, { 0xf, 0x00 }, { WAIT_IDLE },
+{ 0xe, 0xe3 }, { 0xf, 0x00 }, { WAIT_IDLE },
+{ 0xe, 0xe4 }, { 0xf, 0x00 }, { WAIT_IDLE },
+{ 0xe, 0xe5 }, { 0xf, 0x00 }, { WAIT_IDLE },
+{ 0xe, 0xe6 }, { 0xf, 0x00 }, { WAIT_IDLE },
+{ 0xe, 0xe7 }, { 0xf, 0x00 }, { WAIT_IDLE },
+{ 0xe, 0x01 }, { 0xf, 0x00 }, { WAIT_IDLE },
+{ 0xe, 0x02 }, { 0xf, 0x01 }, { WAIT_IDLE },
+{ 0xe, 0x01 }, { 0xf, 0x01 }, { WAIT_IDLE },
+{ 0xe, 0x02 }, { 0xf, 0x01 }, { WAIT_IDLE },
+{ 0xe, 0x01 }, { 0xf, 0x02 }, { WAIT_IDLE },
+{ 0xe, 0x02 }, { 0xf, 0x01 }, { WAIT_IDLE },
+{ 0xe, 0x01 }, { 0xf, 0x03 }, { WAIT_IDLE },
+{ 0xe, 0x02 }, { 0xf, 0x01 }, { WAIT_IDLE },
+{ 0xe, 0x01 }, { 0xf, 0x04 }, { WAIT_IDLE },
+{ 0xe, 0x02 }, { 0xf, 0x01 }, { WAIT_IDLE },
+{ 0xe, 0x01 }, { 0xf, 0x05 }, { WAIT_IDLE },
+{ 0xe, 0x02 }, { 0xf, 0x01 }, { WAIT_IDLE },
+{ 0xe, 0x01 }, { 0xf, 0x06 }, { WAIT_IDLE },
+{ 0xe, 0x02 }, { 0xf, 0x01 }, { WAIT_IDLE },
+{ 0xe, 0x01 }, { 0xf, 0x07 }, { WAIT_IDLE },
+{ 0xe, 0x02 }, { 0xf, 0x01 }, { WAIT_IDLE },
+
+{ 0xb, 0x07 }, { 0xa, 0x46 }, { 0xd, 0x00 }, { 0xc, 0x00 }, { WAIT_IDLE },
+{ 0xb, 0x07 }, { 0xa, 0x49 }, { 0xd, 0x00 }, { 0xc, 0x00 }, { WAIT_IDLE },
+{ 0xb, 0x07 }, { 0xa, 0x45 }, { 0xd, 0x0f }, { 0xc, 0xff }, { WAIT_IDLE },
+{ 0xb, 0x07 }, { 0xa, 0x48 }, { 0xd, 0x0f }, { 0xc, 0xff }, { WAIT_IDLE },
+{ 0xb, 0x07 }, { 0xa, 0x7b }, { 0xd, 0x04 }, { 0xc, 0xcc }, { WAIT_IDLE },
+{ 0xb, 0x07 }, { 0xa, 0x7d }, { 0xd, 0x04 }, { 0xc, 0xcc }, { WAIT_IDLE },
+{ 0xb, 0x07 }, { 0xa, 0x7c }, { 0xd, 0x00 }, { 0xc, 0x00 }, { WAIT_IDLE },
+{ 0xb, 0x07 }, { 0xa, 0x7e }, { 0xd, 0x00 }, { 0xc, 0x00 }, { WAIT_IDLE },
+{ 0xb, 0x07 }, { 0xa, 0x46 }, { 0xd, 0x00 }, { 0xc, 0x00 }, { WAIT_IDLE },
+{ 0xb, 0x07 }, { 0xa, 0x49 }, { 0xd, 0x00 }, { 0xc, 0x00 }, { WAIT_IDLE },
+{ 0xb, 0x07 }, { 0xa, 0x47 }, { 0xd, 0x00 }, { 0xc, 0x00 }, { WAIT_IDLE },
+{ 0xb, 0x07 }, { 0xa, 0x4a }, { 0xd, 0x00 }, { 0xc, 0x00 }, { WAIT_IDLE },
+{ 0xb, 0x07 }, { 0xa, 0x4c }, { 0xd, 0x00 }, { 0xc, 0x00 }, { WAIT_IDLE },
+{ 0xb, 0x07 }, { 0xa, 0x4e }, { 0xd, 0x00 }, { 0xc, 0x00 }, { WAIT_IDLE },
+
+{ 0xb, 0x07 }, { 0xa, 0x4c }, { 0xd, 0x00 }, { 0xc, 0x00 },
+{ 0xb, 0x07 }, { 0xa, 0x4e }, { 0xd, 0x00 }, { 0xc, 0x00 },
+{ 0xb, 0x07 }, { 0xa, 0x4c }, { 0xd, 0x00 }, { 0xc, 0x28 },
+{ 0xb, 0x07 }, { 0xa, 0x4e }, { 0xd, 0x00 }, { 0xc, 0x28 },
+{ 0xb, 0x07 }, { 0xa, 0x4c }, { 0xd, 0x00 }, { 0xc, 0x51 },
+{ 0xb, 0x07 }, { 0xa, 0x4e }, { 0xd, 0x00 }, { 0xc, 0x51 },
+{ 0xb, 0x07 }, { 0xa, 0x4c }, { 0xd, 0x00 }, { 0xc, 0x7a },
+{ 0xb, 0x07 }, { 0xa, 0x4e }, { 0xd, 0x00 }, { 0xc, 0x7a },
+{ 0xb, 0x07 }, { 0xa, 0x4c }, { 0xd, 0x00 }, { 0xc, 0xa3 },
+{ 0xb, 0x07 }, { 0xa, 0x4e }, { 0xd, 0x00 }, { 0xc, 0xa3 },
+{ 0xb, 0x07 }, { 0xa, 0x4c }, { 0xd, 0x00 }, { 0xc, 0xcc },
+{ 0xb, 0x07 }, { 0xa, 0x4e }, { 0xd, 0x00 }, { 0xc, 0xcc },
+{ 0xb, 0x07 }, { 0xa, 0x4c }, { 0xd, 0x00 }, { 0xc, 0xf5 },
+{ 0xb, 0x07 }, { 0xa, 0x4e }, { 0xd, 0x00 }, { 0xc, 0xf5 },
+{ 0xb, 0x07 }, { 0xa, 0x4c }, { 0xd, 0x01 }, { 0xc, 0x1e },
+{ 0xb, 0x07 }, { 0xa, 0x4e }, { 0xd, 0x01 }, { 0xc, 0x1e },
+{ 0xb, 0x07 }, { 0xa, 0x4c }, { 0xd, 0x01 }, { 0xc, 0x47 },
+{ 0xb, 0x07 }, { 0xa, 0x4e }, { 0xd, 0x01 }, { 0xc, 0x47 },
+{ 0xb, 0x07 }, { 0xa, 0x4c }, { 0xd, 0x01 }, { 0xc, 0x70 },
+{ 0xb, 0x07 }, { 0xa, 0x4e }, { 0xd, 0x01 }, { 0xc, 0x70 },
+{ 0xb, 0x07 }, { 0xa, 0x4c }, { 0xd, 0x01 }, { 0xc, 0x99 },
+{ 0xb, 0x07 }, { 0xa, 0x4e }, { 0xd, 0x01 }, { 0xc, 0x99 },
+{ 0xb, 0x07 }, { 0xa, 0x4c }, { 0xd, 0x01 }, { 0xc, 0xc2 },
+{ 0xb, 0x07 }, { 0xa, 0x4e }, { 0xd, 0x01 }, { 0xc, 0xc2 },
+{ 0xb, 0x07 }, { 0xa, 0x4c }, { 0xd, 0x01 }, { 0xc, 0xeb },
+{ 0xb, 0x07 }, { 0xa, 0x4e }, { 0xd, 0x01 }, { 0xc, 0xeb },
+{ 0xb, 0x07 }, { 0xa, 0x4c }, { 0xd, 0x02 }, { 0xc, 0x14 },
+{ 0xb, 0x07 }, { 0xa, 0x4e }, { 0xd, 0x02 }, { 0xc, 0x14 },
+{ 0xb, 0x07 }, { 0xa, 0x4c }, { 0xd, 0x02 }, { 0xc, 0x3d },
+{ 0xb, 0x07 }, { 0xa, 0x4e }, { 0xd, 0x02 }, { 0xc, 0x3d },
+{ 0xb, 0x07 }, { 0xa, 0x4c }, { 0xd, 0x02 }, { 0xc, 0x66 },
+{ 0xb, 0x07 }, { 0xa, 0x4e }, { 0xd, 0x02 }, { 0xc, 0x66 },
+{ 0xb, 0x07 }, { 0xa, 0x4c }, { 0xd, 0x02 }, { 0xc, 0x8f },
+{ 0xb, 0x07 }, { 0xa, 0x4e }, { 0xd, 0x02 }, { 0xc, 0x8f },
+{ 0xb, 0x07 }, { 0xa, 0x4c }, { 0xd, 0x02 }, { 0xc, 0xb8 },
+{ 0xb, 0x07 }, { 0xa, 0x4e }, { 0xd, 0x02 }, { 0xc, 0xb8 },
+{ 0xb, 0x07 }, { 0xa, 0x4c }, { 0xd, 0x02 }, { 0xc, 0xe1 },
+{ 0xb, 0x07 }, { 0xa, 0x4e }, { 0xd, 0x02 }, { 0xc, 0xe1 },
+{ 0xb, 0x07 }, { 0xa, 0x4c }, { 0xd, 0x03 }, { 0xc, 0x0a },
+{ 0xb, 0x07 }, { 0xa, 0x4e }, { 0xd, 0x03 }, { 0xc, 0x0a },
+{ 0xb, 0x07 }, { 0xa, 0x4c }, { 0xd, 0x03 }, { 0xc, 0x33 },
+{ 0xb, 0x07 }, { 0xa, 0x4e }, { 0xd, 0x03 }, { 0xc, 0x33 },
+{ 0xb, 0x07 }, { 0xa, 0x4c }, { 0xd, 0x03 }, { 0xc, 0x5c },
+{ 0xb, 0x07 }, { 0xa, 0x4e }, { 0xd, 0x03 }, { 0xc, 0x5c },
+{ 0xb, 0x07 }, { 0xa, 0x4c }, { 0xd, 0x03 }, { 0xc, 0x85 },
+{ 0xb, 0x07 }, { 0xa, 0x4e }, { 0xd, 0x03 }, { 0xc, 0x85 },
+{ 0xb, 0x07 }, { 0xa, 0x4c }, { 0xd, 0x03 }, { 0xc, 0xae },
+{ 0xb, 0x07 }, { 0xa, 0x4e }, { 0xd, 0x03 }, { 0xc, 0xae },
+{ 0xb, 0x07 }, { 0xa, 0x4c }, { 0xd, 0x03 }, { 0xc, 0xd7 },
+{ 0xb, 0x07 }, { 0xa, 0x4e }, { 0xd, 0x03 }, { 0xc, 0xd7 },
+{ 0xb, 0x07 }, { 0xa, 0x4c }, { 0xd, 0x04 }, { 0xc, 0x00 },
+{ 0xb, 0x07 }, { 0xa, 0x4e }, { 0xd, 0x04 }, { 0xc, 0x00 },
+{ 0xb, 0x07 }, { 0xa, 0x4c }, { 0xd, 0x04 }, { 0xc, 0x28 },
+{ 0xb, 0x07 }, { 0xa, 0x4e }, { 0xd, 0x04 }, { 0xc, 0x28 },
+{ 0xb, 0x07 }, { 0xa, 0x4c }, { 0xd, 0x04 }, { 0xc, 0x51 },
+{ 0xb, 0x07 }, { 0xa, 0x4e }, { 0xd, 0x04 }, { 0xc, 0x51 },
+{ 0xb, 0x07 }, { 0xa, 0x4c }, { 0xd, 0x04 }, { 0xc, 0x7a },
+{ 0xb, 0x07 }, { 0xa, 0x4e }, { 0xd, 0x04 }, { 0xc, 0x7a },
+{ 0xb, 0x07 }, { 0xa, 0x4c }, { 0xd, 0x04 }, { 0xc, 0xa3 },
+{ 0xb, 0x07 }, { 0xa, 0x4e }, { 0xd, 0x04 }, { 0xc, 0xa3 },
+{ 0xb, 0x07 }, { 0xa, 0x4c }, { 0xd, 0x04 }, { 0xc, 0xcc },
+{ 0xb, 0x07 }, { 0xa, 0x4e }, { 0xd, 0x04 }, { 0xc, 0xcc },
+{ 0xb, 0x07 }, { 0xa, 0x4c }, { 0xd, 0x04 }, { 0xc, 0xf5 },
+{ 0xb, 0x07 }, { 0xa, 0x4e }, { 0xd, 0x04 }, { 0xc, 0xf5 },
+{ 0xb, 0x07 }, { 0xa, 0x4c }, { 0xd, 0x05 }, { 0xc, 0x1e },
+{ 0xb, 0x07 }, { 0xa, 0x4e }, { 0xd, 0x05 }, { 0xc, 0x1e },
+{ 0xb, 0x07 }, { 0xa, 0x4c }, { 0xd, 0x05 }, { 0xc, 0x47 },
+{ 0xb, 0x07 }, { 0xa, 0x4e }, { 0xd, 0x05 }, { 0xc, 0x47 },
+{ 0xb, 0x07 }, { 0xa, 0x4c }, { 0xd, 0x05 }, { 0xc, 0x70 },
+{ 0xb, 0x07 }, { 0xa, 0x4e }, { 0xd, 0x05 }, { 0xc, 0x70 },
+{ 0xb, 0x07 }, { 0xa, 0x4c }, { 0xd, 0x05 }, { 0xc, 0x99 },
+{ 0xb, 0x07 }, { 0xa, 0x4e }, { 0xd, 0x05 }, { 0xc, 0x99 },
+{ 0xb, 0x07 }, { 0xa, 0x4c }, { 0xd, 0x05 }, { 0xc, 0xc2 },
+{ 0xb, 0x07 }, { 0xa, 0x4e }, { 0xd, 0x05 }, { 0xc, 0xc2 },
+{ 0xb, 0x07 }, { 0xa, 0x4c }, { 0xd, 0x05 }, { 0xc, 0xeb },
+{ 0xb, 0x07 }, { 0xa, 0x4e }, { 0xd, 0x05 }, { 0xc, 0xeb },
+{ 0xb, 0x07 }, { 0xa, 0x4c }, { 0xd, 0x06 }, { 0xc, 0x14 },
+{ 0xb, 0x07 }, { 0xa, 0x4e }, { 0xd, 0x06 }, { 0xc, 0x14 },
+{ 0xb, 0x07 }, { 0xa, 0x4c }, { 0xd, 0x06 }, { 0xc, 0x3d },
+{ 0xb, 0x07 }, { 0xa, 0x4e }, { 0xd, 0x06 }, { 0xc, 0x3d },
+{ 0xb, 0x07 }, { 0xa, 0x4c }, { 0xd, 0x06 }, { 0xc, 0x66 },
+{ 0xb, 0x07 }, { 0xa, 0x4e }, { 0xd, 0x06 }, { 0xc, 0x66 },
+{ 0xb, 0x07 }, { 0xa, 0x4c }, { 0xd, 0x06 }, { 0xc, 0x8f },
+{ 0xb, 0x07 }, { 0xa, 0x4e }, { 0xd, 0x06 }, { 0xc, 0x8f },
+{ 0xb, 0x07 }, { 0xa, 0x4c }, { 0xd, 0x06 }, { 0xc, 0xb8 },
+{ 0xb, 0x07 }, { 0xa, 0x4e }, { 0xd, 0x06 }, { 0xc, 0xb8 },
+{ 0xb, 0x07 }, { 0xa, 0x4c }, { 0xd, 0x06 }, { 0xc, 0xe1 },
+{ 0xb, 0x07 }, { 0xa, 0x4e }, { 0xd, 0x06 }, { 0xc, 0xe1 },
+{ 0xb, 0x07 }, { 0xa, 0x4c }, { 0xd, 0x07 }, { 0xc, 0x0a },
+{ 0xb, 0x07 }, { 0xa, 0x4e }, { 0xd, 0x07 }, { 0xc, 0x0a },
+{ 0xb, 0x07 }, { 0xa, 0x4c }, { 0xd, 0x07 }, { 0xc, 0x33 },
+{ 0xb, 0x07 }, { 0xa, 0x4e }, { 0xd, 0x07 }, { 0xc, 0x33 },
+{ 0xb, 0x07 }, { 0xa, 0x4c }, { 0xd, 0x07 }, { 0xc, 0x5c },
+{ 0xb, 0x07 }, { 0xa, 0x4e }, { 0xd, 0x07 }, { 0xc, 0x5c },
+{ 0xb, 0x07 }, { 0xa, 0x4c }, { 0xd, 0x07 }, { 0xc, 0x85 },
+{ 0xb, 0x07 }, { 0xa, 0x4e }, { 0xd, 0x07 }, { 0xc, 0x85 },
+{ 0xb, 0x07 }, { 0xa, 0x4c }, { 0xd, 0x07 }, { 0xc, 0xae },
+{ 0xb, 0x07 }, { 0xa, 0x4e }, { 0xd, 0x07 }, { 0xc, 0xae },
+{ 0xb, 0x07 }, { 0xa, 0x4c }, { 0xd, 0x07 }, { 0xc, 0xd7 },
+{ 0xb, 0x07 }, { 0xa, 0x4e }, { 0xd, 0x07 }, { 0xc, 0xd7 },
+{ 0xb, 0x07 }, { 0xa, 0x4c }, { 0xd, 0x08 }, { 0xc, 0x00 },
+{ 0xb, 0x07 }, { 0xa, 0x4e }, { 0xd, 0x08 }, { 0xc, 0x00 },
+{ 0xb, 0x07 }, { 0xa, 0x4c }, { 0xd, 0x08 }, { 0xc, 0x28 },
+{ 0xb, 0x07 }, { 0xa, 0x4e }, { 0xd, 0x08 }, { 0xc, 0x28 },
+{ 0xb, 0x07 }, { 0xa, 0x4c }, { 0xd, 0x08 }, { 0xc, 0x51 },
+{ 0xb, 0x07 }, { 0xa, 0x4e }, { 0xd, 0x08 }, { 0xc, 0x51 },
+{ 0xb, 0x07 }, { 0xa, 0x4c }, { 0xd, 0x08 }, { 0xc, 0x7a },
+{ 0xb, 0x07 }, { 0xa, 0x4e }, { 0xd, 0x08 }, { 0xc, 0x7a },
+{ 0xb, 0x07 }, { 0xa, 0x4c }, { 0xd, 0x08 }, { 0xc, 0xa3 },
+{ 0xb, 0x07 }, { 0xa, 0x4e }, { 0xd, 0x08 }, { 0xc, 0xa3 },
+{ 0xb, 0x07 }, { 0xa, 0x4c }, { 0xd, 0x08 }, { 0xc, 0xcc },
+{ 0xb, 0x07 }, { 0xa, 0x4e }, { 0xd, 0x08 }, { 0xc, 0xcc },
+{ 0xb, 0x07 }, { 0xa, 0x4c }, { 0xd, 0x08 }, { 0xc, 0xf5 },
+{ 0xb, 0x07 }, { 0xa, 0x4e }, { 0xd, 0x08 }, { 0xc, 0xf5 },
+{ 0xb, 0x07 }, { 0xa, 0x4c }, { 0xd, 0x09 }, { 0xc, 0x1e },
+{ 0xb, 0x07 }, { 0xa, 0x4e }, { 0xd, 0x09 }, { 0xc, 0x1e },
+{ 0xb, 0x07 }, { 0xa, 0x4c }, { 0xd, 0x09 }, { 0xc, 0x47 },
+{ 0xb, 0x07 }, { 0xa, 0x4e }, { 0xd, 0x09 }, { 0xc, 0x47 },
+{ 0xb, 0x07 }, { 0xa, 0x4c }, { 0xd, 0x09 }, { 0xc, 0x70 },
+{ 0xb, 0x07 }, { 0xa, 0x4e }, { 0xd, 0x09 }, { 0xc, 0x70 },
+{ 0xb, 0x07 }, { 0xa, 0x4c }, { 0xd, 0x09 }, { 0xc, 0x99 },
+{ 0xb, 0x07 }, { 0xa, 0x4e }, { 0xd, 0x09 }, { 0xc, 0x99 },
+{ 0xb, 0x07 }, { 0xa, 0x4c }, { 0xd, 0x09 }, { 0xc, 0xc2 },
+{ 0xb, 0x07 }, { 0xa, 0x4e }, { 0xd, 0x09 }, { 0xc, 0xc2 },
+{ 0xb, 0x07 }, { 0xa, 0x4c }, { 0xd, 0x09 }, { 0xc, 0xeb },
+{ 0xb, 0x07 }, { 0xa, 0x4e }, { 0xd, 0x09 }, { 0xc, 0xeb },
+{ 0xb, 0x07 }, { 0xa, 0x4c }, { 0xd, 0x0a }, { 0xc, 0x14 },
+{ 0xb, 0x07 }, { 0xa, 0x4e }, { 0xd, 0x0a }, { 0xc, 0x14 },
+{ 0xb, 0x07 }, { 0xa, 0x4c }, { 0xd, 0x0a }, { 0xc, 0x3d },
+{ 0xb, 0x07 }, { 0xa, 0x4e }, { 0xd, 0x0a }, { 0xc, 0x3d },
+{ 0xb, 0x07 }, { 0xa, 0x4c }, { 0xd, 0x0a }, { 0xc, 0x66 },
+{ 0xb, 0x07 }, { 0xa, 0x4e }, { 0xd, 0x0a }, { 0xc, 0x66 },
+{ 0xb, 0x07 }, { 0xa, 0x4c }, { 0xd, 0x0a }, { 0xc, 0x8f },
+{ 0xb, 0x07 }, { 0xa, 0x4e }, { 0xd, 0x0a }, { 0xc, 0x8f },
+{ 0xb, 0x07 }, { 0xa, 0x4c }, { 0xd, 0x0a }, { 0xc, 0xb8 },
+{ 0xb, 0x07 }, { 0xa, 0x4e }, { 0xd, 0x0a }, { 0xc, 0xb8 },
+{ 0xb, 0x07 }, { 0xa, 0x4c }, { 0xd, 0x0a }, { 0xc, 0xe1 },
+{ 0xb, 0x07 }, { 0xa, 0x4e }, { 0xd, 0x0a }, { 0xc, 0xe1 },
+{ 0xb, 0x07 }, { 0xa, 0x4c }, { 0xd, 0x0b }, { 0xc, 0x0a },
+{ 0xb, 0x07 }, { 0xa, 0x4e }, { 0xd, 0x0b }, { 0xc, 0x0a },
+{ 0xb, 0x07 }, { 0xa, 0x4c }, { 0xd, 0x0b }, { 0xc, 0x33 },
+{ 0xb, 0x07 }, { 0xa, 0x4e }, { 0xd, 0x0b }, { 0xc, 0x33 },
+{ 0xb, 0x07 }, { 0xa, 0x4c }, { 0xd, 0x0b }, { 0xc, 0x5c },
+{ 0xb, 0x07 }, { 0xa, 0x4e }, { 0xd, 0x0b }, { 0xc, 0x5c },
+{ 0xb, 0x07 }, { 0xa, 0x4c }, { 0xd, 0x0b }, { 0xc, 0x85 },
+{ 0xb, 0x07 }, { 0xa, 0x4e }, { 0xd, 0x0b }, { 0xc, 0x85 },
+{ 0xb, 0x07 }, { 0xa, 0x4c }, { 0xd, 0x0b }, { 0xc, 0xae },
+{ 0xb, 0x07 }, { 0xa, 0x4e }, { 0xd, 0x0b }, { 0xc, 0xae },
+{ 0xb, 0x07 }, { 0xa, 0x4c }, { 0xd, 0x0b }, { 0xc, 0xd7 },
+{ 0xb, 0x07 }, { 0xa, 0x4e }, { 0xd, 0x0b }, { 0xc, 0xd7 },
+{ 0xb, 0x07 }, { 0xa, 0x4c }, { 0xd, 0x0c }, { 0xc, 0x00 },
+{ 0xb, 0x07 }, { 0xa, 0x4e }, { 0xd, 0x0c }, { 0xc, 0x00 },
+{ 0xb, 0x07 }, { 0xa, 0x4c }, { 0xd, 0x0c }, { 0xc, 0x28 },
+{ 0xb, 0x07 }, { 0xa, 0x4e }, { 0xd, 0x0c }, { 0xc, 0x28 },
+{ 0xb, 0x07 }, { 0xa, 0x4c }, { 0xd, 0x0c }, { 0xc, 0x51 },
+{ 0xb, 0x07 }, { 0xa, 0x4e }, { 0xd, 0x0c }, { 0xc, 0x51 },
+{ 0xb, 0x07 }, { 0xa, 0x4c }, { 0xd, 0x0c }, { 0xc, 0x7a },
+{ 0xb, 0x07 }, { 0xa, 0x4e }, { 0xd, 0x0c }, { 0xc, 0x7a },
+{ 0xb, 0x07 }, { 0xa, 0x4c }, { 0xd, 0x0c }, { 0xc, 0xa3 },
+{ 0xb, 0x07 }, { 0xa, 0x4e }, { 0xd, 0x0c }, { 0xc, 0xa3 },
+{ 0xb, 0x07 }, { 0xa, 0x4c }, { 0xd, 0x0c }, { 0xc, 0xcc },
+{ 0xb, 0x07 }, { 0xa, 0x4e }, { 0xd, 0x0c }, { 0xc, 0xcc },
+{ 0xb, 0x07 }, { 0xa, 0x4c }, { 0xd, 0x0c }, { 0xc, 0xf5 },
+{ 0xb, 0x07 }, { 0xa, 0x4e }, { 0xd, 0x0c }, { 0xc, 0xf5 },
+{ 0xb, 0x07 }, { 0xa, 0x4c }, { 0xd, 0x0d }, { 0xc, 0x1e },
+{ 0xb, 0x07 }, { 0xa, 0x4e }, { 0xd, 0x0d }, { 0xc, 0x1e },
+{ 0xb, 0x07 }, { 0xa, 0x4c }, { 0xd, 0x0d }, { 0xc, 0x47 },
+{ 0xb, 0x07 }, { 0xa, 0x4e }, { 0xd, 0x0d }, { 0xc, 0x47 },
+{ 0xb, 0x07 }, { 0xa, 0x4c }, { 0xd, 0x0d }, { 0xc, 0x70 },
+{ 0xb, 0x07 }, { 0xa, 0x4e }, { 0xd, 0x0d }, { 0xc, 0x70 },
+{ 0xb, 0x07 }, { 0xa, 0x4c }, { 0xd, 0x0d }, { 0xc, 0x99 },
+{ 0xb, 0x07 }, { 0xa, 0x4e }, { 0xd, 0x0d }, { 0xc, 0x99 },
+{ 0xb, 0x07 }, { 0xa, 0x4c }, { 0xd, 0x0d }, { 0xc, 0xc2 },
+{ 0xb, 0x07 }, { 0xa, 0x4e }, { 0xd, 0x0d }, { 0xc, 0xc2 },
+{ 0xb, 0x07 }, { 0xa, 0x4c }, { 0xd, 0x0d }, { 0xc, 0xeb },
+{ 0xb, 0x07 }, { 0xa, 0x4e }, { 0xd, 0x0d }, { 0xc, 0xeb },
+{ 0xb, 0x07 }, { 0xa, 0x4c }, { 0xd, 0x0e }, { 0xc, 0x14 },
+{ 0xb, 0x07 }, { 0xa, 0x4e }, { 0xd, 0x0e }, { 0xc, 0x14 },
+{ 0xb, 0x07 }, { 0xa, 0x4c }, { 0xd, 0x0e }, { 0xc, 0x3d },
+{ 0xb, 0x07 }, { 0xa, 0x4e }, { 0xd, 0x0e }, { 0xc, 0x3d },
+{ 0xb, 0x07 }, { 0xa, 0x4c }, { 0xd, 0x0e }, { 0xc, 0x66 },
+{ 0xb, 0x07 }, { 0xa, 0x4e }, { 0xd, 0x0e }, { 0xc, 0x66 },
+{ 0xb, 0x07 }, { 0xa, 0x4c }, { 0xd, 0x0e }, { 0xc, 0x8f },
+{ 0xb, 0x07 }, { 0xa, 0x4e }, { 0xd, 0x0e }, { 0xc, 0x8f },
+{ 0xb, 0x07 }, { 0xa, 0x4c }, { 0xd, 0x0e }, { 0xc, 0xb8 },
+{ 0xb, 0x07 }, { 0xa, 0x4e }, { 0xd, 0x0e }, { 0xc, 0xb8 },
+{ 0xb, 0x07 }, { 0xa, 0x4c }, { 0xd, 0x0e }, { 0xc, 0xe1 },
+{ 0xb, 0x07 }, { 0xa, 0x4e }, { 0xd, 0x0e }, { 0xc, 0xe1 },
+{ 0xb, 0x07 }, { 0xa, 0x4c }, { 0xd, 0x0f }, { 0xc, 0x0a },
+{ 0xb, 0x07 }, { 0xa, 0x4e }, { 0xd, 0x0f }, { 0xc, 0x0a },
+{ 0xb, 0x07 }, { 0xa, 0x4c }, { 0xd, 0x0f }, { 0xc, 0x33 },
+{ 0xb, 0x07 }, { 0xa, 0x4e }, { 0xd, 0x0f }, { 0xc, 0x33 },
+{ 0xb, 0x07 }, { 0xa, 0x4c }, { 0xd, 0x0f }, { 0xc, 0x5c },
+{ 0xb, 0x07 }, { 0xa, 0x4e }, { 0xd, 0x0f }, { 0xc, 0x5c },
+{ 0xb, 0x07 }, { 0xa, 0x4c }, { 0xd, 0x0f }, { 0xc, 0x85 },
+{ 0xb, 0x07 }, { 0xa, 0x4e }, { 0xd, 0x0f }, { 0xc, 0x85 },
+{ 0xb, 0x07 }, { 0xa, 0x4c }, { 0xd, 0x0f }, { 0xc, 0xae },
+{ 0xb, 0x07 }, { 0xa, 0x4e }, { 0xd, 0x0f }, { 0xc, 0xae },
+{ 0xb, 0x07 }, { 0xa, 0x4c }, { 0xd, 0x0f }, { 0xc, 0xd7 },
+{ 0xb, 0x07 }, { 0xa, 0x4e }, { 0xd, 0x0f }, { 0xc, 0xd7 },
+{ 0xb, 0x07 }, { 0xa, 0x4c }, { 0xd, 0x0f }, { 0xc, 0xff },
+{ 0xb, 0x07 }, { 0xa, 0x4e }, { 0xd, 0x0f }, { 0xc, 0xff },
+
+/* mute off */
+{ 0x8, 0x00 }, { WAIT_IDLE }
+};
diff --git a/sound/pci/Kconfig b/sound/pci/Kconfig
index 8a6b1803c763..1bcfb3aac18d 100644
--- a/sound/pci/Kconfig
+++ b/sound/pci/Kconfig
@@ -236,7 +236,7 @@ config SND_CS5535AUDIO
config SND_DARLA20
tristate "(Echoaudio) Darla20"
depends on SND
- depends on FW_LOADER
+ select FW_LOADER
select SND_PCM
help
Say 'Y' or 'M' to include support for Echoaudio Darla.
@@ -247,7 +247,7 @@ config SND_DARLA20
config SND_GINA20
tristate "(Echoaudio) Gina20"
depends on SND
- depends on FW_LOADER
+ select FW_LOADER
select SND_PCM
help
Say 'Y' or 'M' to include support for Echoaudio Gina.
@@ -258,7 +258,7 @@ config SND_GINA20
config SND_LAYLA20
tristate "(Echoaudio) Layla20"
depends on SND
- depends on FW_LOADER
+ select FW_LOADER
select SND_RAWMIDI
select SND_PCM
help
@@ -270,7 +270,7 @@ config SND_LAYLA20
config SND_DARLA24
tristate "(Echoaudio) Darla24"
depends on SND
- depends on FW_LOADER
+ select FW_LOADER
select SND_PCM
help
Say 'Y' or 'M' to include support for Echoaudio Darla24.
@@ -281,7 +281,7 @@ config SND_DARLA24
config SND_GINA24
tristate "(Echoaudio) Gina24"
depends on SND
- depends on FW_LOADER
+ select FW_LOADER
select SND_PCM
help
Say 'Y' or 'M' to include support for Echoaudio Gina24.
@@ -292,7 +292,7 @@ config SND_GINA24
config SND_LAYLA24
tristate "(Echoaudio) Layla24"
depends on SND
- depends on FW_LOADER
+ select FW_LOADER
select SND_RAWMIDI
select SND_PCM
help
@@ -304,7 +304,7 @@ config SND_LAYLA24
config SND_MONA
tristate "(Echoaudio) Mona"
depends on SND
- depends on FW_LOADER
+ select FW_LOADER
select SND_RAWMIDI
select SND_PCM
help
@@ -316,7 +316,7 @@ config SND_MONA
config SND_MIA
tristate "(Echoaudio) Mia"
depends on SND
- depends on FW_LOADER
+ select FW_LOADER
select SND_RAWMIDI
select SND_PCM
help
@@ -328,7 +328,7 @@ config SND_MIA
config SND_ECHO3G
tristate "(Echoaudio) 3G cards"
depends on SND
- depends on FW_LOADER
+ select FW_LOADER
select SND_RAWMIDI
select SND_PCM
help
@@ -340,7 +340,7 @@ config SND_ECHO3G
config SND_INDIGO
tristate "(Echoaudio) Indigo"
depends on SND
- depends on FW_LOADER
+ select FW_LOADER
select SND_PCM
help
Say 'Y' or 'M' to include support for Echoaudio Indigo.
@@ -351,7 +351,7 @@ config SND_INDIGO
config SND_INDIGOIO
tristate "(Echoaudio) Indigo IO"
depends on SND
- depends on FW_LOADER
+ select FW_LOADER
select SND_PCM
help
Say 'Y' or 'M' to include support for Echoaudio Indigo IO.
@@ -362,7 +362,7 @@ config SND_INDIGOIO
config SND_INDIGODJ
tristate "(Echoaudio) Indigo DJ"
depends on SND
- depends on FW_LOADER
+ select FW_LOADER
select SND_PCM
help
Say 'Y' or 'M' to include support for Echoaudio Indigo DJ.
@@ -373,6 +373,7 @@ config SND_INDIGODJ
config SND_EMU10K1
tristate "Emu10k1 (SB Live!, Audigy, E-mu APS)"
depends on SND
+ select FW_LOADER
select SND_HWDEP
select SND_RAWMIDI
select SND_AC97_CODEC
@@ -575,6 +576,7 @@ config SND_INTEL8X0M
config SND_KORG1212
tristate "Korg 1212 IO"
depends on SND
+ select FW_LOADER
select SND_PCM
help
Say Y here to include support for Korg 1212IO soundcards.
@@ -585,6 +587,7 @@ config SND_KORG1212
config SND_MAESTRO3
tristate "ESS Allegro/Maestro3"
depends on SND
+ select FW_LOADER
select SND_AC97_CODEC
help
Say Y here to include support for soundcards based on ESS Maestro 3
@@ -629,7 +632,7 @@ config SND_PCXHR
config SND_RIPTIDE
tristate "Conexant Riptide"
depends on SND
- depends on FW_LOADER
+ select FW_LOADER
select SND_OPL3_LIB
select SND_MPU401_UART
select SND_AC97_CODEC
@@ -734,6 +737,7 @@ config SND_VX222
config SND_YMFPCI
tristate "Yamaha YMF724/740/744/754"
depends on SND
+ select FW_LOADER
select SND_OPL3_LIB
select SND_MPU401_UART
select SND_AC97_CODEC
diff --git a/sound/pci/ac97/ac97_codec.c b/sound/pci/ac97/ac97_codec.c
index d2994cb4c8c9..74ed81081478 100644
--- a/sound/pci/ac97/ac97_codec.c
+++ b/sound/pci/ac97/ac97_codec.c
@@ -111,7 +111,7 @@ static const struct ac97_codec_id snd_ac97_codec_ids[] = {
{ 0x41445372, 0xffffffff, "AD1981A", patch_ad1981a, NULL },
{ 0x41445374, 0xffffffff, "AD1981B", patch_ad1981b, NULL },
{ 0x41445375, 0xffffffff, "AD1985", patch_ad1985, NULL },
-{ 0x41445378, 0xffffffff, "AD1986", patch_ad1985, NULL },
+{ 0x41445378, 0xffffffff, "AD1986", patch_ad1986, NULL },
{ 0x414c4300, 0xffffff00, "ALC100,100P", NULL, NULL },
{ 0x414c4710, 0xfffffff0, "ALC200,200P", NULL, NULL },
{ 0x414c4721, 0xffffffff, "ALC650D", NULL, NULL }, /* already patched */
@@ -194,6 +194,13 @@ static const struct ac97_codec_id snd_ac97_codec_ids[] = {
static void update_power_regs(struct snd_ac97 *ac97);
+#ifdef CONFIG_SND_AC97_POWER_SAVE
+#define ac97_is_power_save_mode(ac97) \
+ ((ac97->scaps & AC97_SCAP_POWER_SAVE) && power_save)
+#else
+#define ac97_is_power_save_mode(ac97) 0
+#endif
+
/*
* I/O routines
@@ -982,8 +989,8 @@ static int snd_ac97_free(struct snd_ac97 *ac97)
{
if (ac97) {
#ifdef CONFIG_SND_AC97_POWER_SAVE
- if (ac97->power_workq)
- destroy_workqueue(ac97->power_workq);
+ cancel_delayed_work(&ac97->power_work);
+ flush_scheduled_work();
#endif
snd_ac97_proc_done(ac97);
if (ac97->bus)
@@ -1184,13 +1191,13 @@ static int snd_ac97_cmute_new_stereo(struct snd_card *card, char *name, int reg,
/*
* set dB information
*/
-static DECLARE_TLV_DB_SCALE(db_scale_4bit, -4500, 300, 0);
-static DECLARE_TLV_DB_SCALE(db_scale_5bit, -4650, 150, 0);
-static DECLARE_TLV_DB_SCALE(db_scale_6bit, -9450, 150, 0);
-static DECLARE_TLV_DB_SCALE(db_scale_5bit_12db_max, -3450, 150, 0);
-static DECLARE_TLV_DB_SCALE(db_scale_rec_gain, 0, 150, 0);
+static const DECLARE_TLV_DB_SCALE(db_scale_4bit, -4500, 300, 0);
+static const DECLARE_TLV_DB_SCALE(db_scale_5bit, -4650, 150, 0);
+static const DECLARE_TLV_DB_SCALE(db_scale_6bit, -9450, 150, 0);
+static const DECLARE_TLV_DB_SCALE(db_scale_5bit_12db_max, -3450, 150, 0);
+static const DECLARE_TLV_DB_SCALE(db_scale_rec_gain, 0, 150, 0);
-static unsigned int *find_db_scale(unsigned int maxval)
+static const unsigned int *find_db_scale(unsigned int maxval)
{
switch (maxval) {
case 0x0f: return db_scale_4bit;
@@ -1200,8 +1207,8 @@ static unsigned int *find_db_scale(unsigned int maxval)
return NULL;
}
-static void set_tlv_db_scale(struct snd_kcontrol *kctl, unsigned int *tlv)
-{
+static void set_tlv_db_scale(struct snd_kcontrol *kctl, const unsigned int *tlv)
+{
kctl->tlv.p = tlv;
if (tlv)
kctl->vd[0].access |= SNDRV_CTL_ELEM_ACCESS_TLV_READ;
@@ -1989,7 +1996,6 @@ int snd_ac97_mixer(struct snd_ac97_bus *bus, struct snd_ac97_template *template,
mutex_init(&ac97->reg_mutex);
mutex_init(&ac97->page_mutex);
#ifdef CONFIG_SND_AC97_POWER_SAVE
- ac97->power_workq = create_workqueue("ac97");
INIT_DELAYED_WORK(&ac97->power_work, do_update_power);
#endif
@@ -2275,15 +2281,13 @@ static void snd_ac97_powerdown(struct snd_ac97 *ac97)
udelay(100);
power |= AC97_PD_PR2 | AC97_PD_PR3; /* Analog Mixer powerdown */
snd_ac97_write(ac97, AC97_POWERDOWN, power);
-#ifdef CONFIG_SND_AC97_POWER_SAVE
- if (power_save) {
+ if (ac97_is_power_save_mode(ac97)) {
udelay(100);
/* AC-link powerdown, internal Clk disable */
/* FIXME: this may cause click noises on some boards */
power |= AC97_PD_PR4 | AC97_PD_PR5;
snd_ac97_write(ac97, AC97_POWERDOWN, power);
}
-#endif
}
@@ -2337,14 +2341,16 @@ int snd_ac97_update_power(struct snd_ac97 *ac97, int reg, int powerup)
}
}
- if (power_save && !powerup && ac97->power_workq)
+ if (ac97_is_power_save_mode(ac97) && !powerup)
/* adjust power-down bits after two seconds delay
* (for avoiding loud click noises for many (OSS) apps
* that open/close frequently)
*/
- queue_delayed_work(ac97->power_workq, &ac97->power_work, HZ*2);
- else
+ schedule_delayed_work(&ac97->power_work, HZ*2);
+ else {
+ cancel_delayed_work(&ac97->power_work);
update_power_regs(ac97);
+ }
return 0;
}
@@ -2357,19 +2363,15 @@ static void update_power_regs(struct snd_ac97 *ac97)
unsigned int power_up, bits;
int i;
+ power_up = (1 << PWIDX_FRONT) | (1 << PWIDX_ADC);
+ power_up |= (1 << PWIDX_MIC);
+ if (ac97->scaps & AC97_SCAP_SURROUND_DAC)
+ power_up |= (1 << PWIDX_SURR);
+ if (ac97->scaps & AC97_SCAP_CENTER_LFE_DAC)
+ power_up |= (1 << PWIDX_CLFE);
#ifdef CONFIG_SND_AC97_POWER_SAVE
- if (power_save)
+ if (ac97_is_power_save_mode(ac97))
power_up = ac97->power_up;
- else {
-#endif
- power_up = (1 << PWIDX_FRONT) | (1 << PWIDX_ADC);
- power_up |= (1 << PWIDX_MIC);
- if (ac97->scaps & AC97_SCAP_SURROUND_DAC)
- power_up |= (1 << PWIDX_SURR);
- if (ac97->scaps & AC97_SCAP_CENTER_LFE_DAC)
- power_up |= (1 << PWIDX_CLFE);
-#ifdef CONFIG_SND_AC97_POWER_SAVE
- }
#endif
if (power_up) {
if (ac97->regs[AC97_POWERDOWN] & AC97_PD_PR2) {
@@ -2414,6 +2416,10 @@ void snd_ac97_suspend(struct snd_ac97 *ac97)
return;
if (ac97->build_ops->suspend)
ac97->build_ops->suspend(ac97);
+#ifdef CONFIG_SND_AC97_POWER_SAVE
+ cancel_delayed_work(&ac97->power_work);
+ flush_scheduled_work();
+#endif
snd_ac97_powerdown(ac97);
}
diff --git a/sound/pci/ac97/ac97_patch.c b/sound/pci/ac97/ac97_patch.c
index e813968e0cf8..641d0c8d659e 100644
--- a/sound/pci/ac97/ac97_patch.c
+++ b/sound/pci/ac97/ac97_patch.c
@@ -54,7 +54,7 @@ static int patch_build_controls(struct snd_ac97 * ac97, const struct snd_kcontro
/* replace with a new TLV */
static void reset_tlv(struct snd_ac97 *ac97, const char *name,
- unsigned int *tlv)
+ const unsigned int *tlv)
{
struct snd_ctl_elem_id sid;
struct snd_kcontrol *kctl;
@@ -190,14 +190,28 @@ static inline int is_clfe_on(struct snd_ac97 *ac97)
return ac97->channel_mode >= 2;
}
+/* system has shared jacks with surround out enabled */
+static inline int is_shared_surrout(struct snd_ac97 *ac97)
+{
+ return !ac97->indep_surround && is_surround_on(ac97);
+}
+
+/* system has shared jacks with center/lfe out enabled */
+static inline int is_shared_clfeout(struct snd_ac97 *ac97)
+{
+ return !ac97->indep_surround && is_clfe_on(ac97);
+}
+
+/* system has shared jacks with line in enabled */
static inline int is_shared_linein(struct snd_ac97 *ac97)
{
- return ! ac97->indep_surround && is_surround_on(ac97);
+ return !ac97->indep_surround && !is_surround_on(ac97);
}
+/* system has shared jacks with mic in enabled */
static inline int is_shared_micin(struct snd_ac97 *ac97)
{
- return ! ac97->indep_surround && is_clfe_on(ac97);
+ return !ac97->indep_surround && !is_clfe_on(ac97);
}
@@ -941,6 +955,9 @@ static int patch_sigmatel_stac9708_specific(struct snd_ac97 *ac97)
{
int err;
+ /* the register bit is writable, but the function is not implemented: */
+ snd_ac97_remove_ctl(ac97, "PCM Out Path & Mute", NULL);
+
snd_ac97_rename_vol_ctl(ac97, "Headphone Playback", "Sigmatel Surround Playback");
if ((err = patch_build_controls(ac97, &snd_ac97_stac9708_bias_control, 1)) < 0)
return err;
@@ -1552,7 +1569,7 @@ static const struct snd_kcontrol_new snd_ac97_controls_ad1885[] = {
AC97_SINGLE("Line Jack Sense", AC97_AD_JACK_SPDIF, 8, 1, 1), /* inverted */
};
-static DECLARE_TLV_DB_SCALE(db_scale_6bit_6db_max, -8850, 150, 0);
+static const DECLARE_TLV_DB_SCALE(db_scale_6bit_6db_max, -8850, 150, 0);
static int patch_ad1885_specific(struct snd_ac97 * ac97)
{
@@ -1609,19 +1626,22 @@ int patch_ad1886(struct snd_ac97 * ac97)
return 0;
}
-/* MISC bits */
+/* MISC bits (AD1888/AD1980/AD1985 register 0x76) */
#define AC97_AD198X_MBC 0x0003 /* mic boost */
#define AC97_AD198X_MBC_20 0x0000 /* +20dB */
#define AC97_AD198X_MBC_10 0x0001 /* +10dB */
#define AC97_AD198X_MBC_30 0x0002 /* +30dB */
#define AC97_AD198X_VREFD 0x0004 /* VREF high-Z */
-#define AC97_AD198X_VREFH 0x0008 /* 2.25V, 3.7V */
-#define AC97_AD198X_VREF_0 0x000c /* 0V */
+#define AC97_AD198X_VREFH 0x0008 /* 0=2.25V, 1=3.7V */
+#define AC97_AD198X_VREF_0 0x000c /* 0V (AD1985 only) */
+#define AC97_AD198X_VREF_MASK (AC97_AD198X_VREFH | AC97_AD198X_VREFD)
+#define AC97_AD198X_VREF_SHIFT 2
#define AC97_AD198X_SRU 0x0010 /* sample rate unlock */
#define AC97_AD198X_LOSEL 0x0020 /* LINE_OUT amplifiers input select */
#define AC97_AD198X_2MIC 0x0040 /* 2-channel mic select */
#define AC97_AD198X_SPRD 0x0080 /* SPREAD enable */
-#define AC97_AD198X_DMIX0 0x0100 /* downmix mode: 0 = 6-to-4, 1 = 6-to-2 downmix */
+#define AC97_AD198X_DMIX0 0x0100 /* downmix mode: */
+ /* 0 = 6-to-4, 1 = 6-to-2 downmix */
#define AC97_AD198X_DMIX1 0x0200 /* downmix mode: 1 = enabled */
#define AC97_AD198X_HPSEL 0x0400 /* headphone amplifier input select */
#define AC97_AD198X_CLDIS 0x0800 /* center/lfe disable */
@@ -1630,6 +1650,83 @@ int patch_ad1886(struct snd_ac97 * ac97)
#define AC97_AD198X_AC97NC 0x4000 /* AC97 no compatible mode */
#define AC97_AD198X_DACZ 0x8000 /* DAC zero-fill mode */
+/* MISC 1 bits (AD1986 register 0x76) */
+#define AC97_AD1986_MBC 0x0003 /* mic boost */
+#define AC97_AD1986_MBC_20 0x0000 /* +20dB */
+#define AC97_AD1986_MBC_10 0x0001 /* +10dB */
+#define AC97_AD1986_MBC_30 0x0002 /* +30dB */
+#define AC97_AD1986_LISEL0 0x0004 /* LINE_IN select bit 0 */
+#define AC97_AD1986_LISEL1 0x0008 /* LINE_IN select bit 1 */
+#define AC97_AD1986_LISEL_MASK (AC97_AD1986_LISEL1 | AC97_AD1986_LISEL0)
+#define AC97_AD1986_LISEL_LI 0x0000 /* LINE_IN pins as LINE_IN source */
+#define AC97_AD1986_LISEL_SURR 0x0004 /* SURROUND pins as LINE_IN source */
+#define AC97_AD1986_LISEL_MIC 0x0008 /* MIC_1/2 pins as LINE_IN source */
+#define AC97_AD1986_SRU 0x0010 /* sample rate unlock */
+#define AC97_AD1986_SOSEL 0x0020 /* SURROUND_OUT amplifiers input sel */
+#define AC97_AD1986_2MIC 0x0040 /* 2-channel mic select */
+#define AC97_AD1986_SPRD 0x0080 /* SPREAD enable */
+#define AC97_AD1986_DMIX0 0x0100 /* downmix mode: */
+ /* 0 = 6-to-4, 1 = 6-to-2 downmix */
+#define AC97_AD1986_DMIX1 0x0200 /* downmix mode: 1 = enabled */
+#define AC97_AD1986_CLDIS 0x0800 /* center/lfe disable */
+#define AC97_AD1986_SODIS 0x1000 /* SURROUND_OUT disable */
+#define AC97_AD1986_MSPLT 0x2000 /* mute split (read only 1) */
+#define AC97_AD1986_AC97NC 0x4000 /* AC97 no compatible mode (r/o 1) */
+#define AC97_AD1986_DACZ 0x8000 /* DAC zero-fill mode */
+
+/* MISC 2 bits (AD1986 register 0x70) */
+#define AC97_AD_MISC2 0x70 /* Misc Control Bits 2 (AD1986) */
+
+#define AC97_AD1986_CVREF0 0x0004 /* C/LFE VREF_OUT 2.25V */
+#define AC97_AD1986_CVREF1 0x0008 /* C/LFE VREF_OUT 0V */
+#define AC97_AD1986_CVREF2 0x0010 /* C/LFE VREF_OUT 3.7V */
+#define AC97_AD1986_CVREF_MASK \
+ (AC97_AD1986_CVREF2 | AC97_AD1986_CVREF1 | AC97_AD1986_CVREF0)
+#define AC97_AD1986_JSMAP 0x0020 /* Jack Sense Mapping 1 = alternate */
+#define AC97_AD1986_MMDIS 0x0080 /* Mono Mute Disable */
+#define AC97_AD1986_MVREF0 0x0400 /* MIC VREF_OUT 2.25V */
+#define AC97_AD1986_MVREF1 0x0800 /* MIC VREF_OUT 0V */
+#define AC97_AD1986_MVREF2 0x1000 /* MIC VREF_OUT 3.7V */
+#define AC97_AD1986_MVREF_MASK \
+ (AC97_AD1986_MVREF2 | AC97_AD1986_MVREF1 | AC97_AD1986_MVREF0)
+
+/* MISC 3 bits (AD1986 register 0x7a) */
+#define AC97_AD_MISC3 0x7a /* Misc Control Bits 3 (AD1986) */
+
+#define AC97_AD1986_MMIX 0x0004 /* Mic Mix, left/right */
+#define AC97_AD1986_GPO 0x0008 /* General Purpose Out */
+#define AC97_AD1986_LOHPEN 0x0010 /* LINE_OUT headphone drive */
+#define AC97_AD1986_LVREF0 0x0100 /* LINE_OUT VREF_OUT 2.25V */
+#define AC97_AD1986_LVREF1 0x0200 /* LINE_OUT VREF_OUT 0V */
+#define AC97_AD1986_LVREF2 0x0400 /* LINE_OUT VREF_OUT 3.7V */
+#define AC97_AD1986_LVREF_MASK \
+ (AC97_AD1986_LVREF2 | AC97_AD1986_LVREF1 | AC97_AD1986_LVREF0)
+#define AC97_AD1986_JSINVA 0x0800 /* Jack Sense Invert SENSE_A */
+#define AC97_AD1986_LOSEL 0x1000 /* LINE_OUT amplifiers input select */
+#define AC97_AD1986_HPSEL0 0x2000 /* Headphone amplifiers */
+ /* input select Surround DACs */
+#define AC97_AD1986_HPSEL1 0x4000 /* Headphone amplifiers input */
+ /* select C/LFE DACs */
+#define AC97_AD1986_JSINVB 0x8000 /* Jack Sense Invert SENSE_B */
+
+/* Serial Config bits (AD1986 register 0x74) (incomplete) */
+#define AC97_AD1986_OMS0 0x0100 /* Optional Mic Selector bit 0 */
+#define AC97_AD1986_OMS1 0x0200 /* Optional Mic Selector bit 1 */
+#define AC97_AD1986_OMS2 0x0400 /* Optional Mic Selector bit 2 */
+#define AC97_AD1986_OMS_MASK \
+ (AC97_AD1986_OMS2 | AC97_AD1986_OMS1 | AC97_AD1986_OMS0)
+#define AC97_AD1986_OMS_M 0x0000 /* MIC_1/2 pins are MIC sources */
+#define AC97_AD1986_OMS_L 0x0100 /* LINE_IN pins are MIC sources */
+#define AC97_AD1986_OMS_C 0x0200 /* Center/LFE pins are MCI sources */
+#define AC97_AD1986_OMS_MC 0x0400 /* Mix of MIC and C/LFE pins */
+ /* are MIC sources */
+#define AC97_AD1986_OMS_ML 0x0500 /* MIX of MIC and LINE_IN pins */
+ /* are MIC sources */
+#define AC97_AD1986_OMS_LC 0x0600 /* MIX of LINE_IN and C/LFE pins */
+ /* are MIC sources */
+#define AC97_AD1986_OMS_MLC 0x0700 /* MIX of MIC, LINE_IN, C/LFE pins */
+ /* are MIC sources */
+
static int snd_ac97_ad198x_spdif_source_info(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *uinfo)
{
@@ -1952,8 +2049,80 @@ int patch_ad1980(struct snd_ac97 * ac97)
return 0;
}
+static int snd_ac97_ad1985_vrefout_info(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_info *uinfo)
+{
+ static char *texts[4] = {"High-Z", "3.7 V", "2.25 V", "0 V"};
+
+ uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED;
+ uinfo->count = 1;
+ uinfo->value.enumerated.items = 4;
+ if (uinfo->value.enumerated.item > 3)
+ uinfo->value.enumerated.item = 3;
+ strcpy(uinfo->value.enumerated.name,
+ texts[uinfo->value.enumerated.item]);
+ return 0;
+}
+
+static int snd_ac97_ad1985_vrefout_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ static const int reg2ctrl[4] = {2, 0, 1, 3};
+ struct snd_ac97 *ac97 = snd_kcontrol_chip(kcontrol);
+ unsigned short val;
+ val = (ac97->regs[AC97_AD_MISC] & AC97_AD198X_VREF_MASK)
+ >> AC97_AD198X_VREF_SHIFT;
+ ucontrol->value.enumerated.item[0] = reg2ctrl[val];
+ return 0;
+}
+
+static int snd_ac97_ad1985_vrefout_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ static const int ctrl2reg[4] = {1, 2, 0, 3};
+ struct snd_ac97 *ac97 = snd_kcontrol_chip(kcontrol);
+ unsigned short val;
+
+ if (ucontrol->value.enumerated.item[0] > 3
+ || ucontrol->value.enumerated.item[0] < 0)
+ return -EINVAL;
+ val = ctrl2reg[ucontrol->value.enumerated.item[0]]
+ << AC97_AD198X_VREF_SHIFT;
+ return snd_ac97_update_bits(ac97, AC97_AD_MISC,
+ AC97_AD198X_VREF_MASK, val);
+}
+
static const struct snd_kcontrol_new snd_ac97_ad1985_controls[] = {
- AC97_SINGLE("Exchange Center/LFE", AC97_AD_SERIAL_CFG, 3, 1, 0)
+ AC97_SINGLE("Exchange Center/LFE", AC97_AD_SERIAL_CFG, 3, 1, 0),
+ {
+ .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+ .name = "Exchange Front/Surround",
+ .info = snd_ac97_ad1888_lohpsel_info,
+ .get = snd_ac97_ad1888_lohpsel_get,
+ .put = snd_ac97_ad1888_lohpsel_put
+ },
+ AC97_SINGLE("High Pass Filter Enable", AC97_AD_TEST2, 12, 1, 1),
+ AC97_SINGLE("Spread Front to Surround and Center/LFE",
+ AC97_AD_MISC, 7, 1, 0),
+ {
+ .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+ .name = "Downmix",
+ .info = snd_ac97_ad1888_downmix_info,
+ .get = snd_ac97_ad1888_downmix_get,
+ .put = snd_ac97_ad1888_downmix_put
+ },
+ {
+ .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+ .name = "V_REFOUT",
+ .info = snd_ac97_ad1985_vrefout_info,
+ .get = snd_ac97_ad1985_vrefout_get,
+ .put = snd_ac97_ad1985_vrefout_put
+ },
+ AC97_SURROUND_JACK_MODE_CTL,
+ AC97_CHANNEL_MODE_CTL,
+
+ AC97_SINGLE("Headphone Jack Sense", AC97_AD_JACK_SPDIF, 10, 1, 0),
+ AC97_SINGLE("Line Jack Sense", AC97_AD_JACK_SPDIF, 12, 1, 0),
};
static void ad1985_update_jacks(struct snd_ac97 *ac97)
@@ -1967,9 +2136,16 @@ static int patch_ad1985_specific(struct snd_ac97 *ac97)
{
int err;
- if ((err = patch_ad1980_specific(ac97)) < 0)
+ /* rename 0x04 as "Master" and 0x02 as "Master Surround" */
+ snd_ac97_rename_vol_ctl(ac97, "Master Playback",
+ "Master Surround Playback");
+ snd_ac97_rename_vol_ctl(ac97, "Headphone Playback", "Master Playback");
+
+ if ((err = patch_build_controls(ac97, &snd_ac97_ad198x_2cmic, 1)) < 0)
return err;
- return patch_build_controls(ac97, snd_ac97_ad1985_controls, ARRAY_SIZE(snd_ac97_ad1985_controls));
+
+ return patch_build_controls(ac97, snd_ac97_ad1985_controls,
+ ARRAY_SIZE(snd_ac97_ad1985_controls));
}
static struct snd_ac97_build_ops patch_ad1985_build_ops = {
@@ -1989,24 +2165,311 @@ int patch_ad1985(struct snd_ac97 * ac97)
ac97->build_ops = &patch_ad1985_build_ops;
misc = snd_ac97_read(ac97, AC97_AD_MISC);
/* switch front/surround line-out/hp-out */
- /* center/LFE, mic in 3.75V mode */
/* AD-compatible mode */
/* Stereo mutes enabled */
- /* in accordance with ADI driver: misc | 0x5c28 */
snd_ac97_write_cache(ac97, AC97_AD_MISC, misc |
- AC97_AD198X_VREFH |
AC97_AD198X_LOSEL |
AC97_AD198X_HPSEL |
- AC97_AD198X_CLDIS |
- AC97_AD198X_LODIS |
AC97_AD198X_MSPLT |
AC97_AD198X_AC97NC);
ac97->flags |= AC97_STEREO_MUTES;
+
+ /* update current jack configuration */
+ ad1985_update_jacks(ac97);
+
/* on AD1985 rev. 3, AC'97 revision bits are zero */
ac97->ext_id = (ac97->ext_id & ~AC97_EI_REV_MASK) | AC97_EI_REV_23;
return 0;
}
+static int snd_ac97_ad1986_bool_info(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_info *uinfo)
+{
+ uinfo->type = SNDRV_CTL_ELEM_TYPE_BOOLEAN;
+ uinfo->count = 1;
+ uinfo->value.integer.min = 0;
+ uinfo->value.integer.max = 1;
+ return 0;
+}
+
+static int snd_ac97_ad1986_lososel_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_ac97 *ac97 = snd_kcontrol_chip(kcontrol);
+ unsigned short val;
+
+ val = ac97->regs[AC97_AD_MISC3];
+ ucontrol->value.integer.value[0] = (val & AC97_AD1986_LOSEL) != 0;
+ return 0;
+}
+
+static int snd_ac97_ad1986_lososel_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_ac97 *ac97 = snd_kcontrol_chip(kcontrol);
+ int ret0;
+ int ret1;
+ int sprd = (ac97->regs[AC97_AD_MISC] & AC97_AD1986_SPRD) != 0;
+
+ ret0 = snd_ac97_update_bits(ac97, AC97_AD_MISC3, AC97_AD1986_LOSEL,
+ ucontrol->value.integer.value[0] != 0
+ ? AC97_AD1986_LOSEL : 0);
+ if (ret0 < 0)
+ return ret0;
+
+ /* SOSEL is set to values of "Spread" or "Exchange F/S" controls */
+ ret1 = snd_ac97_update_bits(ac97, AC97_AD_MISC, AC97_AD1986_SOSEL,
+ (ucontrol->value.integer.value[0] != 0
+ || sprd)
+ ? AC97_AD1986_SOSEL : 0);
+ if (ret1 < 0)
+ return ret1;
+
+ return (ret0 > 0 || ret1 > 0) ? 1 : 0;
+}
+
+static int snd_ac97_ad1986_spread_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_ac97 *ac97 = snd_kcontrol_chip(kcontrol);
+ unsigned short val;
+
+ val = ac97->regs[AC97_AD_MISC];
+ ucontrol->value.integer.value[0] = (val & AC97_AD1986_SPRD) != 0;
+ return 0;
+}
+
+static int snd_ac97_ad1986_spread_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_ac97 *ac97 = snd_kcontrol_chip(kcontrol);
+ int ret0;
+ int ret1;
+ int sprd = (ac97->regs[AC97_AD_MISC3] & AC97_AD1986_LOSEL) != 0;
+
+ ret0 = snd_ac97_update_bits(ac97, AC97_AD_MISC, AC97_AD1986_SPRD,
+ ucontrol->value.integer.value[0] != 0
+ ? AC97_AD1986_SPRD : 0);
+ if (ret0 < 0)
+ return ret0;
+
+ /* SOSEL is set to values of "Spread" or "Exchange F/S" controls */
+ ret1 = snd_ac97_update_bits(ac97, AC97_AD_MISC, AC97_AD1986_SOSEL,
+ (ucontrol->value.integer.value[0] != 0
+ || sprd)
+ ? AC97_AD1986_SOSEL : 0);
+ if (ret1 < 0)
+ return ret1;
+
+ return (ret0 > 0 || ret1 > 0) ? 1 : 0;
+}
+
+static int snd_ac97_ad1986_miclisel_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_ac97 *ac97 = snd_kcontrol_chip(kcontrol);
+
+ ucontrol->value.integer.value[0] = ac97->spec.ad18xx.swap_mic_linein;
+ return 0;
+}
+
+static int snd_ac97_ad1986_miclisel_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_ac97 *ac97 = snd_kcontrol_chip(kcontrol);
+ unsigned char swap = ucontrol->value.integer.value[0] != 0;
+
+ if (swap != ac97->spec.ad18xx.swap_mic_linein) {
+ ac97->spec.ad18xx.swap_mic_linein = swap;
+ if (ac97->build_ops->update_jacks)
+ ac97->build_ops->update_jacks(ac97);
+ return 1;
+ }
+ return 0;
+}
+
+static int snd_ac97_ad1986_vrefout_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ /* Use MIC_1/2 V_REFOUT as the "get" value */
+ struct snd_ac97 *ac97 = snd_kcontrol_chip(kcontrol);
+ unsigned short val;
+ unsigned short reg = ac97->regs[AC97_AD_MISC2];
+ if ((reg & AC97_AD1986_MVREF0) != 0)
+ val = 2;
+ else if ((reg & AC97_AD1986_MVREF1) != 0)
+ val = 3;
+ else if ((reg & AC97_AD1986_MVREF2) != 0)
+ val = 1;
+ else
+ val = 0;
+ ucontrol->value.enumerated.item[0] = val;
+ return 0;
+}
+
+static int snd_ac97_ad1986_vrefout_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_ac97 *ac97 = snd_kcontrol_chip(kcontrol);
+ unsigned short cval;
+ unsigned short lval;
+ unsigned short mval;
+ int cret;
+ int lret;
+ int mret;
+
+ switch (ucontrol->value.enumerated.item[0])
+ {
+ case 0: /* High-Z */
+ cval = 0;
+ lval = 0;
+ mval = 0;
+ break;
+ case 1: /* 3.7 V */
+ cval = AC97_AD1986_CVREF2;
+ lval = AC97_AD1986_LVREF2;
+ mval = AC97_AD1986_MVREF2;
+ break;
+ case 2: /* 2.25 V */
+ cval = AC97_AD1986_CVREF0;
+ lval = AC97_AD1986_LVREF0;
+ mval = AC97_AD1986_MVREF0;
+ break;
+ case 3: /* 0 V */
+ cval = AC97_AD1986_CVREF1;
+ lval = AC97_AD1986_LVREF1;
+ mval = AC97_AD1986_MVREF1;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ cret = snd_ac97_update_bits(ac97, AC97_AD_MISC2,
+ AC97_AD1986_CVREF_MASK, cval);
+ if (cret < 0)
+ return cret;
+ lret = snd_ac97_update_bits(ac97, AC97_AD_MISC3,
+ AC97_AD1986_LVREF_MASK, lval);
+ if (lret < 0)
+ return lret;
+ mret = snd_ac97_update_bits(ac97, AC97_AD_MISC2,
+ AC97_AD1986_MVREF_MASK, mval);
+ if (mret < 0)
+ return mret;
+
+ return (cret > 0 || lret > 0 || mret > 0) ? 1 : 0;
+}
+
+static const struct snd_kcontrol_new snd_ac97_ad1986_controls[] = {
+ AC97_SINGLE("Exchange Center/LFE", AC97_AD_SERIAL_CFG, 3, 1, 0),
+ {
+ .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+ .name = "Exchange Front/Surround",
+ .info = snd_ac97_ad1986_bool_info,
+ .get = snd_ac97_ad1986_lososel_get,
+ .put = snd_ac97_ad1986_lososel_put
+ },
+ {
+ .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+ .name = "Exchange Mic/Line In",
+ .info = snd_ac97_ad1986_bool_info,
+ .get = snd_ac97_ad1986_miclisel_get,
+ .put = snd_ac97_ad1986_miclisel_put
+ },
+ {
+ .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+ .name = "Spread Front to Surround and Center/LFE",
+ .info = snd_ac97_ad1986_bool_info,
+ .get = snd_ac97_ad1986_spread_get,
+ .put = snd_ac97_ad1986_spread_put
+ },
+ {
+ .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+ .name = "Downmix",
+ .info = snd_ac97_ad1888_downmix_info,
+ .get = snd_ac97_ad1888_downmix_get,
+ .put = snd_ac97_ad1888_downmix_put
+ },
+ {
+ .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+ .name = "V_REFOUT",
+ .info = snd_ac97_ad1985_vrefout_info,
+ .get = snd_ac97_ad1986_vrefout_get,
+ .put = snd_ac97_ad1986_vrefout_put
+ },
+ AC97_SURROUND_JACK_MODE_CTL,
+ AC97_CHANNEL_MODE_CTL,
+
+ AC97_SINGLE("Headphone Jack Sense", AC97_AD_JACK_SPDIF, 10, 1, 0),
+ AC97_SINGLE("Line Jack Sense", AC97_AD_JACK_SPDIF, 12, 1, 0)
+};
+
+static void ad1986_update_jacks(struct snd_ac97 *ac97)
+{
+ unsigned short misc_val = 0;
+ unsigned short ser_val;
+
+ /* disable SURROUND and CENTER/LFE if not surround mode */
+ if (! is_surround_on(ac97))
+ misc_val |= AC97_AD1986_SODIS;
+ if (! is_clfe_on(ac97))
+ misc_val |= AC97_AD1986_CLDIS;
+
+ /* select line input (default=LINE_IN, SURROUND or MIC_1/2) */
+ if (is_shared_linein(ac97))
+ misc_val |= AC97_AD1986_LISEL_SURR;
+ else if (ac97->spec.ad18xx.swap_mic_linein != 0)
+ misc_val |= AC97_AD1986_LISEL_MIC;
+ snd_ac97_update_bits(ac97, AC97_AD_MISC,
+ AC97_AD1986_SODIS | AC97_AD1986_CLDIS |
+ AC97_AD1986_LISEL_MASK,
+ misc_val);
+
+ /* select microphone input (MIC_1/2, Center/LFE or LINE_IN) */
+ if (is_shared_micin(ac97))
+ ser_val = AC97_AD1986_OMS_C;
+ else if (ac97->spec.ad18xx.swap_mic_linein != 0)
+ ser_val = AC97_AD1986_OMS_L;
+ else
+ ser_val = AC97_AD1986_OMS_M;
+ snd_ac97_update_bits(ac97, AC97_AD_SERIAL_CFG,
+ AC97_AD1986_OMS_MASK,
+ ser_val);
+}
+
+static int patch_ad1986_specific(struct snd_ac97 *ac97)
+{
+ int err;
+
+ if ((err = patch_build_controls(ac97, &snd_ac97_ad198x_2cmic, 1)) < 0)
+ return err;
+
+ return patch_build_controls(ac97, snd_ac97_ad1986_controls,
+ ARRAY_SIZE(snd_ac97_ad1985_controls));
+}
+
+static struct snd_ac97_build_ops patch_ad1986_build_ops = {
+ .build_post_spdif = patch_ad198x_post_spdif,
+ .build_specific = patch_ad1986_specific,
+#ifdef CONFIG_PM
+ .resume = ad18xx_resume,
+#endif
+ .update_jacks = ad1986_update_jacks,
+};
+
+int patch_ad1986(struct snd_ac97 * ac97)
+{
+ patch_ad1881(ac97);
+ ac97->build_ops = &patch_ad1986_build_ops;
+ ac97->flags |= AC97_STEREO_MUTES;
+
+ /* update current jack configuration */
+ ad1986_update_jacks(ac97);
+
+ return 0;
+}
+
+
/*
* realtek ALC65x/850 codecs
*/
@@ -2014,12 +2477,12 @@ static void alc650_update_jacks(struct snd_ac97 *ac97)
{
int shared;
- /* shared Line-In */
- shared = is_shared_linein(ac97);
+ /* shared Line-In / Surround Out */
+ shared = is_shared_surrout(ac97);
snd_ac97_update_bits(ac97, AC97_ALC650_MULTICH, 1 << 9,
shared ? (1 << 9) : 0);
- /* update shared Mic */
- shared = is_shared_micin(ac97);
+ /* update shared Mic In / Center/LFE Out */
+ shared = is_shared_clfeout(ac97);
/* disable/enable vref */
snd_ac97_update_bits(ac97, AC97_ALC650_CLOCK, 1 << 12,
shared ? (1 << 12) : 0);
@@ -2064,7 +2527,7 @@ static const struct snd_kcontrol_new snd_ac97_spdif_controls_alc650[] = {
/* AC97_SINGLE("IEC958 Input Monitor", AC97_ALC650_MULTICH, 13, 1, 0), */
};
-static DECLARE_TLV_DB_SCALE(db_scale_5bit_3db_max, -4350, 150, 0);
+static const DECLARE_TLV_DB_SCALE(db_scale_5bit_3db_max, -4350, 150, 0);
static int patch_alc650_specific(struct snd_ac97 * ac97)
{
@@ -2149,12 +2612,12 @@ static void alc655_update_jacks(struct snd_ac97 *ac97)
{
int shared;
- /* shared Line-In */
- shared = is_shared_linein(ac97);
+ /* shared Line-In / Surround Out */
+ shared = is_shared_surrout(ac97);
ac97_update_bits_page(ac97, AC97_ALC650_MULTICH, 1 << 9,
shared ? (1 << 9) : 0, 0);
- /* update shared mic */
- shared = is_shared_micin(ac97);
+ /* update shared Mic In / Center/LFE Out */
+ shared = is_shared_clfeout(ac97);
/* misc control; vrefout disable */
snd_ac97_update_bits(ac97, AC97_ALC650_CLOCK, 1 << 12,
shared ? (1 << 12) : 0);
@@ -2264,7 +2727,8 @@ int patch_alc655(struct snd_ac97 * ac97)
if (ac97->subsystem_vendor == 0x1462 &&
(ac97->subsystem_device == 0x0131 || /* MSI S270 laptop */
ac97->subsystem_device == 0x0161 || /* LG K1 Express */
- ac97->subsystem_device == 0x0351)) /* MSI L725 laptop */
+ ac97->subsystem_device == 0x0351 || /* MSI L725 laptop */
+ ac97->subsystem_device == 0x0061)) /* MSI S250 laptop */
val &= ~(1 << 1); /* Pin 47 is EAPD (for internal speaker) */
else
val |= (1 << 1); /* Pin 47 is spdif input pin */
@@ -2297,16 +2761,16 @@ static void alc850_update_jacks(struct snd_ac97 *ac97)
{
int shared;
- /* shared Line-In */
- shared = is_shared_linein(ac97);
+ /* shared Line-In / Surround Out */
+ shared = is_shared_surrout(ac97);
/* SURR 1kOhm (bit4), Amp (bit5) */
snd_ac97_update_bits(ac97, AC97_ALC850_MISC1, (1<<4)|(1<<5),
shared ? (1<<5) : (1<<4));
/* LINE-IN = 0, SURROUND = 2 */
snd_ac97_update_bits(ac97, AC97_ALC850_JACK_SELECT, 7 << 12,
shared ? (2<<12) : (0<<12));
- /* update shared mic */
- shared = is_shared_micin(ac97);
+ /* update shared Mic In / Center/LFE Out */
+ shared = is_shared_clfeout(ac97);
/* Vref disable (bit12), 1kOhm (bit13) */
snd_ac97_update_bits(ac97, AC97_ALC850_MISC1, (1<<12)|(1<<13),
shared ? (1<<12) : (1<<13));
@@ -2379,9 +2843,9 @@ int patch_alc850(struct snd_ac97 *ac97)
*/
static void cm9738_update_jacks(struct snd_ac97 *ac97)
{
- /* shared Line-In */
+ /* shared Line-In / Surround Out */
snd_ac97_update_bits(ac97, AC97_CM9738_VENDOR_CTRL, 1 << 10,
- is_shared_linein(ac97) ? (1 << 10) : 0);
+ is_shared_surrout(ac97) ? (1 << 10) : 0);
}
static const struct snd_kcontrol_new snd_ac97_cm9738_controls[] = {
@@ -2463,12 +2927,12 @@ static const struct snd_kcontrol_new snd_ac97_cm9739_controls_spdif[] = {
static void cm9739_update_jacks(struct snd_ac97 *ac97)
{
- /* shared Line-In */
+ /* shared Line-In / Surround Out */
snd_ac97_update_bits(ac97, AC97_CM9739_MULTI_CHAN, 1 << 10,
- is_shared_linein(ac97) ? (1 << 10) : 0);
- /* shared Mic */
+ is_shared_surrout(ac97) ? (1 << 10) : 0);
+ /* shared Mic In / Center/LFE Out **/
snd_ac97_update_bits(ac97, AC97_CM9739_MULTI_CHAN, 0x3000,
- is_shared_micin(ac97) ? 0x1000 : 0x2000);
+ is_shared_clfeout(ac97) ? 0x1000 : 0x2000);
}
static const struct snd_kcontrol_new snd_ac97_cm9739_controls[] = {
@@ -2580,8 +3044,8 @@ static void cm9761_update_jacks(struct snd_ac97 *ac97)
val |= surr_on[ac97->spec.dev_flags][is_surround_on(ac97)];
val |= clfe_on[ac97->spec.dev_flags][is_clfe_on(ac97)];
- val |= surr_shared[ac97->spec.dev_flags][is_shared_linein(ac97)];
- val |= clfe_shared[ac97->spec.dev_flags][is_shared_micin(ac97)];
+ val |= surr_shared[ac97->spec.dev_flags][is_shared_surrout(ac97)];
+ val |= clfe_shared[ac97->spec.dev_flags][is_shared_clfeout(ac97)];
snd_ac97_update_bits(ac97, AC97_CM9761_MULTI_CHAN, 0x3c88, val);
}
@@ -2821,6 +3285,7 @@ int patch_vt1617a(struct snd_ac97 * ac97)
snd_ac97_write_cache(ac97, 0x5c, 0x20);
ac97->ext_id |= AC97_EI_SPDIF; /* force the detection of spdif */
ac97->rates[AC97_RATES_SPDIF] = SNDRV_PCM_RATE_44100 | SNDRV_PCM_RATE_48000;
+ ac97->build_ops = &patch_vt1616_ops;
return 0;
}
@@ -2828,12 +3293,12 @@ int patch_vt1617a(struct snd_ac97 * ac97)
*/
static void it2646_update_jacks(struct snd_ac97 *ac97)
{
- /* shared Line-In */
+ /* shared Line-In / Surround Out */
snd_ac97_update_bits(ac97, 0x76, 1 << 9,
- is_shared_linein(ac97) ? (1<<9) : 0);
- /* shared Mic */
+ is_shared_surrout(ac97) ? (1<<9) : 0);
+ /* shared Mic / Center/LFE Out */
snd_ac97_update_bits(ac97, 0x76, 1 << 10,
- is_shared_micin(ac97) ? (1<<10) : 0);
+ is_shared_clfeout(ac97) ? (1<<10) : 0);
}
static const struct snd_kcontrol_new snd_ac97_controls_it2646[] = {
diff --git a/sound/pci/ac97/ac97_patch.h b/sound/pci/ac97/ac97_patch.h
index 741979217207..94340daaaf1f 100644
--- a/sound/pci/ac97/ac97_patch.h
+++ b/sound/pci/ac97/ac97_patch.h
@@ -48,6 +48,7 @@ int patch_ad1980(struct snd_ac97 * ac97);
int patch_ad1981a(struct snd_ac97 * ac97);
int patch_ad1981b(struct snd_ac97 * ac97);
int patch_ad1985(struct snd_ac97 * ac97);
+int patch_ad1986(struct snd_ac97 * ac97);
int patch_alc650(struct snd_ac97 * ac97);
int patch_alc655(struct snd_ac97 * ac97);
int patch_alc850(struct snd_ac97 * ac97);
diff --git a/sound/pci/ac97/ak4531_codec.c b/sound/pci/ac97/ak4531_codec.c
index c153cb79c518..dc26820a03a5 100644
--- a/sound/pci/ac97/ak4531_codec.c
+++ b/sound/pci/ac97/ak4531_codec.c
@@ -267,9 +267,9 @@ static int snd_ak4531_put_input_sw(struct snd_kcontrol *kcontrol, struct snd_ctl
return change;
}
-static DECLARE_TLV_DB_SCALE(db_scale_master, -6200, 200, 0);
-static DECLARE_TLV_DB_SCALE(db_scale_mono, -2800, 400, 0);
-static DECLARE_TLV_DB_SCALE(db_scale_input, -5000, 200, 0);
+static const DECLARE_TLV_DB_SCALE(db_scale_master, -6200, 200, 0);
+static const DECLARE_TLV_DB_SCALE(db_scale_mono, -2800, 400, 0);
+static const DECLARE_TLV_DB_SCALE(db_scale_input, -5000, 200, 0);
static struct snd_kcontrol_new snd_ak4531_controls[] = {
diff --git a/sound/pci/als300.c b/sound/pci/als300.c
index 9f406fbe0d95..8afcb98ca7bb 100644
--- a/sound/pci/als300.c
+++ b/sound/pci/als300.c
@@ -444,7 +444,7 @@ static int snd_als300_capture_close(struct snd_pcm_substream *substream)
}
static int snd_als300_pcm_hw_params(struct snd_pcm_substream *substream,
- snd_pcm_hw_params_t * hw_params)
+ struct snd_pcm_hw_params *hw_params)
{
return snd_pcm_lib_malloc_pages(substream,
params_buffer_bytes(hw_params));
@@ -673,7 +673,7 @@ static void snd_als300_init(struct snd_als300 *chip)
snd_als300_dbgcallleave();
}
-static int __devinit snd_als300_create(snd_card_t *card,
+static int __devinit snd_als300_create(struct snd_card *card,
struct pci_dev *pci, int chip_type,
struct snd_als300 **rchip)
{
@@ -681,7 +681,7 @@ static int __devinit snd_als300_create(snd_card_t *card,
void *irq_handler;
int err;
- static snd_device_ops_t ops = {
+ static struct snd_device_ops ops = {
.dev_free = snd_als300_dev_free,
};
*rchip = NULL;
diff --git a/sound/pci/atiixp.c b/sound/pci/atiixp.c
index 476c3433073e..7d8053b5e8d5 100644
--- a/sound/pci/atiixp.c
+++ b/sound/pci/atiixp.c
@@ -45,6 +45,7 @@ static char *id = SNDRV_DEFAULT_STR1; /* ID for this card */
static int ac97_clock = 48000;
static char *ac97_quirk;
static int spdif_aclink = 1;
+static int ac97_codec = -1;
module_param(index, int, 0444);
MODULE_PARM_DESC(index, "Index value for ATI IXP controller.");
@@ -54,6 +55,8 @@ module_param(ac97_clock, int, 0444);
MODULE_PARM_DESC(ac97_clock, "AC'97 codec clock (default 48000Hz).");
module_param(ac97_quirk, charp, 0444);
MODULE_PARM_DESC(ac97_quirk, "AC'97 workaround for strange hardware.");
+module_param(ac97_codec, int, 0444);
+MODULE_PARM_DESC(ac97_codec, "Specify codec instead of probing.");
module_param(spdif_aclink, bool, 0444);
MODULE_PARM_DESC(spdif_aclink, "S/PDIF over AC-link.");
@@ -293,6 +296,10 @@ static struct pci_device_id snd_atiixp_ids[] = {
MODULE_DEVICE_TABLE(pci, snd_atiixp_ids);
+static struct snd_pci_quirk atiixp_quirks[] __devinitdata = {
+ SND_PCI_QUIRK(0x15bd, 0x3100, "DFI RS482", 0),
+ { } /* terminator */
+};
/*
* lowlevel functions
@@ -553,11 +560,33 @@ static int snd_atiixp_aclink_down(struct atiixp *chip)
ATI_REG_ISR_CODEC2_NOT_READY)
#define CODEC_CHECK_BITS (ALL_CODEC_NOT_READY|ATI_REG_ISR_NEW_FRAME)
+static int ac97_probing_bugs(struct pci_dev *pci)
+{
+ const struct snd_pci_quirk *q;
+
+ q = snd_pci_quirk_lookup(pci, atiixp_quirks);
+ if (q) {
+ snd_printdd(KERN_INFO "Atiixp quirk for %s. "
+ "Forcing codec %d\n", q->name, q->value);
+ return q->value;
+ }
+ /* this hardware doesn't need workarounds. Probe for codec */
+ return -1;
+}
+
static int snd_atiixp_codec_detect(struct atiixp *chip)
{
int timeout;
chip->codec_not_ready_bits = 0;
+ if (ac97_codec == -1)
+ ac97_codec = ac97_probing_bugs(chip->pci);
+ if (ac97_codec >= 0) {
+ chip->codec_not_ready_bits |=
+ CODEC_CHECK_BITS ^ (1 << (ac97_codec + 10));
+ return 0;
+ }
+
atiixp_write(chip, IER, CODEC_CHECK_BITS);
/* wait for the interrupts */
timeout = 50;
@@ -1396,7 +1425,7 @@ static int __devinit snd_atiixp_mixer_new(struct atiixp *chip, int clock,
ac97.private_data = chip;
ac97.pci = chip->pci;
ac97.num = i;
- ac97.scaps = AC97_SCAP_SKIP_MODEM;
+ ac97.scaps = AC97_SCAP_SKIP_MODEM | AC97_SCAP_POWER_SAVE;
if (! chip->spdif_over_aclink)
ac97.scaps |= AC97_SCAP_NO_SPDIF;
if ((err = snd_ac97_mixer(pbus, &ac97, &chip->ac97[i])) < 0) {
diff --git a/sound/pci/atiixp_modem.c b/sound/pci/atiixp_modem.c
index cc2e6b9d407e..904023fe4f26 100644
--- a/sound/pci/atiixp_modem.c
+++ b/sound/pci/atiixp_modem.c
@@ -1090,7 +1090,7 @@ static int __devinit snd_atiixp_mixer_new(struct atiixp_modem *chip, int clock)
ac97.private_data = chip;
ac97.pci = chip->pci;
ac97.num = i;
- ac97.scaps = AC97_SCAP_SKIP_AUDIO;
+ ac97.scaps = AC97_SCAP_SKIP_AUDIO | AC97_SCAP_POWER_SAVE;
if ((err = snd_ac97_mixer(pbus, &ac97, &chip->ac97[i])) < 0) {
chip->ac97[i] = NULL; /* to be sure */
snd_printdd("atiixp-modem: codec %d not available for modem\n", i);
diff --git a/sound/pci/ca0106/ca0106_main.c b/sound/pci/ca0106/ca0106_main.c
index f61f052f6d14..ea6712b63c9f 100644
--- a/sound/pci/ca0106/ca0106_main.c
+++ b/sound/pci/ca0106/ca0106_main.c
@@ -1382,7 +1382,6 @@ static int __devinit snd_ca0106_create(int dev, struct snd_card *card,
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 */
- chip->capture_source = 3; /* Set CAPTURE_SOURCE */
//snd_ca0106_ptr_write(chip, 0x45, 0, 0); /* Analogue out */
//snd_ca0106_ptr_write(chip, 0x45, 0, 0xf00); /* Digital out */
@@ -1402,8 +1401,22 @@ static int __devinit snd_ca0106_create(int dev, struct snd_card *card,
snd_ca0106_ptr_write(chip, PLAYBACK_VOLUME1, ch, 0xffffffff); /* Mute */
snd_ca0106_ptr_write(chip, PLAYBACK_VOLUME2, ch, 0xffffffff); /* Mute */
}
- snd_ca0106_ptr_write(chip, CAPTURE_SOURCE, 0x0, 0x333300e4); /* Select MIC, Line in, TAD in, AUX in */
- chip->capture_source = 3; /* Set CAPTURE_SOURCE */
+ 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;
+ } 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;
+ } 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 (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. */
@@ -1605,6 +1618,8 @@ static int __devinit snd_ca0106_probe(struct pci_dev *pci,
snd_ca0106_proc_init(chip);
#endif
+ snd_card_set_dev(card, &pci->dev);
+
if ((err = snd_card_register(card)) < 0) {
snd_card_free(card);
return err;
diff --git a/sound/pci/ca0106/ca0106_mixer.c b/sound/pci/ca0106/ca0106_mixer.c
index 9855f528ea78..b913a1fb8c21 100644
--- a/sound/pci/ca0106/ca0106_mixer.c
+++ b/sound/pci/ca0106/ca0106_mixer.c
@@ -74,8 +74,8 @@
#include "ca0106.h"
-static DECLARE_TLV_DB_SCALE(snd_ca0106_db_scale1, -5175, 25, 1);
-static DECLARE_TLV_DB_SCALE(snd_ca0106_db_scale2, -10350, 50, 1);
+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);
static int snd_ca0106_shared_spdif_info(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_info *uinfo)
@@ -482,19 +482,6 @@ static int snd_ca0106_i2c_volume_put(struct snd_kcontrol *kcontrol,
.private_value = ((chid) << 8) | (reg) \
}
-#define I2C_VOLUME(xname,chid) \
-{ \
- .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, \
- .access = SNDRV_CTL_ELEM_ACCESS_READWRITE | \
- SNDRV_CTL_ELEM_ACCESS_TLV_READ, \
- .info = snd_ca0106_i2c_volume_info, \
- .get = snd_ca0106_i2c_volume_get, \
- .put = snd_ca0106_i2c_volume_put, \
- .tlv = { .p = snd_ca0106_db_scale2 }, \
- .private_value = chid \
-}
-
-
static struct snd_kcontrol_new snd_ca0106_volume_ctls[] __devinitdata = {
CA_VOLUME("Analog Front Playback Volume",
CONTROL_FRONT_CHANNEL, PLAYBACK_VOLUME2),
@@ -517,11 +504,6 @@ static struct snd_kcontrol_new snd_ca0106_volume_ctls[] __devinitdata = {
CA_VOLUME("CAPTURE feedback Playback Volume",
1, CAPTURE_CONTROL),
- I2C_VOLUME("Phone Capture Volume", 0),
- I2C_VOLUME("Mic Capture Volume", 1),
- I2C_VOLUME("Line in Capture Volume", 2),
- I2C_VOLUME("Aux Capture Volume", 3),
-
{
.access = SNDRV_CTL_ELEM_ACCESS_READ,
.iface = SNDRV_CTL_ELEM_IFACE_PCM,
@@ -539,14 +521,14 @@ static struct snd_kcontrol_new snd_ca0106_volume_ctls[] __devinitdata = {
},
{
.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
- .name = "Digital Capture Source",
+ .name = "Digital Source Capture Enum",
.info = snd_ca0106_capture_source_info,
.get = snd_ca0106_capture_source_get,
.put = snd_ca0106_capture_source_put
},
{
.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
- .name = "Capture Source",
+ .name = "Analog Source Capture Enum",
.info = snd_ca0106_i2c_capture_source_info,
.get = snd_ca0106_i2c_capture_source_get,
.put = snd_ca0106_i2c_capture_source_put
@@ -561,6 +543,25 @@ static struct snd_kcontrol_new snd_ca0106_volume_ctls[] __devinitdata = {
},
};
+#define I2C_VOLUME(xname,chid) \
+{ \
+ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, \
+ .access = SNDRV_CTL_ELEM_ACCESS_READWRITE | \
+ SNDRV_CTL_ELEM_ACCESS_TLV_READ, \
+ .info = snd_ca0106_i2c_volume_info, \
+ .get = snd_ca0106_i2c_volume_get, \
+ .put = snd_ca0106_i2c_volume_put, \
+ .tlv = { .p = snd_ca0106_db_scale2 }, \
+ .private_value = chid \
+}
+
+static struct snd_kcontrol_new snd_ca0106_volume_i2c_adc_ctls[] __devinitdata = {
+ I2C_VOLUME("Phone Capture Volume", 0),
+ I2C_VOLUME("Mic Capture Volume", 1),
+ I2C_VOLUME("Line in Capture Volume", 2),
+ I2C_VOLUME("Aux Capture Volume", 3),
+};
+
static int __devinit remove_ctl(struct snd_card *card, const char *name)
{
struct snd_ctl_elem_id id;
@@ -645,6 +646,11 @@ int __devinit snd_ca0106_mixer(struct snd_ca0106 *emu)
return err;
}
if (emu->details->i2c_adc == 1) {
+ for (i = 0; i < ARRAY_SIZE(snd_ca0106_volume_i2c_adc_ctls); i++) {
+ err = snd_ctl_add(card, snd_ctl_new1(&snd_ca0106_volume_i2c_adc_ctls[i], emu));
+ if (err < 0)
+ return err;
+ }
if (emu->details->gpio_type == 1)
err = snd_ctl_add(card, snd_ctl_new1(&snd_ca0106_capture_mic_line_in, emu));
else /* gpio_type == 2 */
diff --git a/sound/pci/cs4281.c b/sound/pci/cs4281.c
index 8e5519de7115..44cf54607647 100644
--- a/sound/pci/cs4281.c
+++ b/sound/pci/cs4281.c
@@ -1055,7 +1055,7 @@ static int snd_cs4281_put_volume(struct snd_kcontrol *kcontrol,
return change;
}
-static DECLARE_TLV_DB_SCALE(db_scale_dsp, -4650, 150, 0);
+static const DECLARE_TLV_DB_SCALE(db_scale_dsp, -4650, 150, 0);
static struct snd_kcontrol_new snd_cs4281_fm_vol =
{
diff --git a/sound/pci/echoaudio/darla20.c b/sound/pci/echoaudio/darla20.c
index b7108e29a668..8e7fe033270f 100644
--- a/sound/pci/echoaudio/darla20.c
+++ b/sound/pci/echoaudio/darla20.c
@@ -47,6 +47,7 @@
#include <sound/core.h>
#include <sound/info.h>
#include <sound/control.h>
+#include <sound/tlv.h>
#include <sound/pcm.h>
#include <sound/pcm_params.h>
#include <sound/asoundef.h>
diff --git a/sound/pci/echoaudio/darla24.c b/sound/pci/echoaudio/darla24.c
index e59a982ee361..a13c623eb999 100644
--- a/sound/pci/echoaudio/darla24.c
+++ b/sound/pci/echoaudio/darla24.c
@@ -51,6 +51,7 @@
#include <sound/core.h>
#include <sound/info.h>
#include <sound/control.h>
+#include <sound/tlv.h>
#include <sound/pcm.h>
#include <sound/pcm_params.h>
#include <sound/asoundef.h>
diff --git a/sound/pci/echoaudio/echo3g.c b/sound/pci/echoaudio/echo3g.c
index 12099fe1547d..8fb15823aca5 100644
--- a/sound/pci/echoaudio/echo3g.c
+++ b/sound/pci/echoaudio/echo3g.c
@@ -58,6 +58,7 @@
#include <sound/core.h>
#include <sound/info.h>
#include <sound/control.h>
+#include <sound/tlv.h>
#include <sound/pcm.h>
#include <sound/pcm_params.h>
#include <sound/asoundef.h>
diff --git a/sound/pci/echoaudio/echo3g_dsp.c b/sound/pci/echoaudio/echo3g_dsp.c
index d26a1d1f3ed1..48eb7c599111 100644
--- a/sound/pci/echoaudio/echo3g_dsp.c
+++ b/sound/pci/echoaudio/echo3g_dsp.c
@@ -39,7 +39,7 @@ static int set_phantom_power(struct echoaudio *chip, char on);
static int write_control_reg(struct echoaudio *chip, u32 ctl, u32 frq,
char force);
-#include <linux/irq.h>
+#include <linux/interrupt.h>
static int init_hw(struct echoaudio *chip, u16 device_id, u16 subdevice_id)
{
diff --git a/sound/pci/echoaudio/echoaudio.c b/sound/pci/echoaudio/echoaudio.c
index 047e0b5bf15d..6a428b81dba6 100644
--- a/sound/pci/echoaudio/echoaudio.c
+++ b/sound/pci/echoaudio/echoaudio.c
@@ -34,6 +34,7 @@ module_param_array(enable, bool, NULL, 0444);
MODULE_PARM_DESC(enable, "Enable " ECHOCARD_NAME " soundcard.");
static unsigned int channels_list[10] = {1, 2, 4, 6, 8, 10, 12, 14, 16, 999999};
+static const DECLARE_TLV_DB_SCALE(db_scale_output_gain, -12800, 100, 1);
static int get_firmware(const struct firmware **fw_entry,
const struct firmware *frm, struct echoaudio *chip)
@@ -1011,17 +1012,21 @@ static int snd_echo_output_gain_put(struct snd_kcontrol *kcontrol,
static struct snd_kcontrol_new snd_echo_line_output_gain __devinitdata = {
.name = "Line Playback Volume",
.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+ .access = SNDRV_CTL_ELEM_ACCESS_READWRITE | SNDRV_CTL_ELEM_ACCESS_TLV_READ,
.info = snd_echo_output_gain_info,
.get = snd_echo_output_gain_get,
.put = snd_echo_output_gain_put,
+ .tlv = {.p = db_scale_output_gain},
};
#else
static struct snd_kcontrol_new snd_echo_pcm_output_gain __devinitdata = {
.name = "PCM Playback Volume",
.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+ .access = SNDRV_CTL_ELEM_ACCESS_READWRITE | SNDRV_CTL_ELEM_ACCESS_TLV_READ,
.info = snd_echo_output_gain_info,
.get = snd_echo_output_gain_get,
.put = snd_echo_output_gain_put,
+ .tlv = {.p = db_scale_output_gain},
};
#endif
@@ -1080,12 +1085,16 @@ static int snd_echo_input_gain_put(struct snd_kcontrol *kcontrol,
return changed;
}
+static const DECLARE_TLV_DB_SCALE(db_scale_input_gain, -2500, 50, 0);
+
static struct snd_kcontrol_new snd_echo_line_input_gain __devinitdata = {
.name = "Line Capture Volume",
.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+ .access = SNDRV_CTL_ELEM_ACCESS_READWRITE | SNDRV_CTL_ELEM_ACCESS_TLV_READ,
.info = snd_echo_input_gain_info,
.get = snd_echo_input_gain_get,
.put = snd_echo_input_gain_put,
+ .tlv = {.p = db_scale_input_gain},
};
#endif /* ECHOCARD_HAS_INPUT_GAIN */
@@ -1277,9 +1286,11 @@ static int snd_echo_mixer_put(struct snd_kcontrol *kcontrol,
static struct snd_kcontrol_new snd_echo_monitor_mixer __devinitdata = {
.name = "Monitor Mixer Volume",
.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+ .access = SNDRV_CTL_ELEM_ACCESS_READWRITE | SNDRV_CTL_ELEM_ACCESS_TLV_READ,
.info = snd_echo_mixer_info,
.get = snd_echo_mixer_get,
.put = snd_echo_mixer_put,
+ .tlv = {.p = db_scale_output_gain},
};
#endif /* ECHOCARD_HAS_MONITOR */
@@ -1343,9 +1354,11 @@ static int snd_echo_vmixer_put(struct snd_kcontrol *kcontrol,
static struct snd_kcontrol_new snd_echo_vmixer __devinitdata = {
.name = "VMixer Volume",
.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+ .access = SNDRV_CTL_ELEM_ACCESS_READWRITE | SNDRV_CTL_ELEM_ACCESS_TLV_READ,
.info = snd_echo_vmixer_info,
.get = snd_echo_vmixer_get,
.put = snd_echo_vmixer_put,
+ .tlv = {.p = db_scale_output_gain},
};
#endif /* ECHOCARD_HAS_VMIXER */
@@ -1753,9 +1766,12 @@ static int snd_echo_vumeters_get(struct snd_kcontrol *kcontrol,
static struct snd_kcontrol_new snd_echo_vumeters __devinitdata = {
.name = "VU-meters",
.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
- .access = SNDRV_CTL_ELEM_ACCESS_READ | SNDRV_CTL_ELEM_ACCESS_VOLATILE,
+ .access = SNDRV_CTL_ELEM_ACCESS_READ |
+ SNDRV_CTL_ELEM_ACCESS_VOLATILE |
+ SNDRV_CTL_ELEM_ACCESS_TLV_READ,
.info = snd_echo_vumeters_info,
.get = snd_echo_vumeters_get,
+ .tlv = {.p = db_scale_output_gain},
};
diff --git a/sound/pci/echoaudio/gina20.c b/sound/pci/echoaudio/gina20.c
index 29d6d12f80ca..af4d32026e4a 100644
--- a/sound/pci/echoaudio/gina20.c
+++ b/sound/pci/echoaudio/gina20.c
@@ -51,6 +51,7 @@
#include <sound/core.h>
#include <sound/info.h>
#include <sound/control.h>
+#include <sound/tlv.h>
#include <sound/pcm.h>
#include <sound/pcm_params.h>
#include <sound/asoundef.h>
diff --git a/sound/pci/echoaudio/gina24.c b/sound/pci/echoaudio/gina24.c
index e464d720d0bd..9ff454a947ed 100644
--- a/sound/pci/echoaudio/gina24.c
+++ b/sound/pci/echoaudio/gina24.c
@@ -57,6 +57,7 @@
#include <sound/core.h>
#include <sound/info.h>
#include <sound/control.h>
+#include <sound/tlv.h>
#include <sound/pcm.h>
#include <sound/pcm_params.h>
#include <sound/asoundef.h>
diff --git a/sound/pci/echoaudio/indigo.c b/sound/pci/echoaudio/indigo.c
index bfd2467099ac..37eb726fd03d 100644
--- a/sound/pci/echoaudio/indigo.c
+++ b/sound/pci/echoaudio/indigo.c
@@ -49,6 +49,7 @@
#include <sound/core.h>
#include <sound/info.h>
#include <sound/control.h>
+#include <sound/tlv.h>
#include <sound/pcm.h>
#include <sound/pcm_params.h>
#include <sound/asoundef.h>
diff --git a/sound/pci/echoaudio/indigodj.c b/sound/pci/echoaudio/indigodj.c
index 8ed7ff1fd875..dc8b91824181 100644
--- a/sound/pci/echoaudio/indigodj.c
+++ b/sound/pci/echoaudio/indigodj.c
@@ -49,6 +49,7 @@
#include <sound/core.h>
#include <sound/info.h>
#include <sound/control.h>
+#include <sound/tlv.h>
#include <sound/pcm.h>
#include <sound/pcm_params.h>
#include <sound/asoundef.h>
diff --git a/sound/pci/echoaudio/indigoio.c b/sound/pci/echoaudio/indigoio.c
index a8788e959171..eadf3263453a 100644
--- a/sound/pci/echoaudio/indigoio.c
+++ b/sound/pci/echoaudio/indigoio.c
@@ -50,6 +50,7 @@
#include <sound/core.h>
#include <sound/info.h>
#include <sound/control.h>
+#include <sound/tlv.h>
#include <sound/pcm.h>
#include <sound/pcm_params.h>
#include <sound/asoundef.h>
diff --git a/sound/pci/echoaudio/layla20.c b/sound/pci/echoaudio/layla20.c
index e503d74b3ba9..6cede497579e 100644
--- a/sound/pci/echoaudio/layla20.c
+++ b/sound/pci/echoaudio/layla20.c
@@ -56,6 +56,7 @@
#include <sound/core.h>
#include <sound/info.h>
#include <sound/control.h>
+#include <sound/tlv.h>
#include <sound/pcm.h>
#include <sound/pcm_params.h>
#include <sound/asoundef.h>
diff --git a/sound/pci/echoaudio/layla24.c b/sound/pci/echoaudio/layla24.c
index d4581fdc841c..44f735426aa0 100644
--- a/sound/pci/echoaudio/layla24.c
+++ b/sound/pci/echoaudio/layla24.c
@@ -58,6 +58,7 @@
#include <sound/core.h>
#include <sound/info.h>
#include <sound/control.h>
+#include <sound/tlv.h>
#include <sound/pcm.h>
#include <sound/pcm_params.h>
#include <sound/asoundef.h>
diff --git a/sound/pci/echoaudio/mia.c b/sound/pci/echoaudio/mia.c
index be40c64263d2..dc172d03ac3f 100644
--- a/sound/pci/echoaudio/mia.c
+++ b/sound/pci/echoaudio/mia.c
@@ -56,6 +56,7 @@
#include <sound/core.h>
#include <sound/info.h>
#include <sound/control.h>
+#include <sound/tlv.h>
#include <sound/pcm.h>
#include <sound/pcm_params.h>
#include <sound/asoundef.h>
diff --git a/sound/pci/echoaudio/mona.c b/sound/pci/echoaudio/mona.c
index 5dc512add372..c856ed50dd9a 100644
--- a/sound/pci/echoaudio/mona.c
+++ b/sound/pci/echoaudio/mona.c
@@ -55,6 +55,7 @@
#include <sound/core.h>
#include <sound/info.h>
#include <sound/control.h>
+#include <sound/tlv.h>
#include <sound/pcm.h>
#include <sound/pcm_params.h>
#include <sound/asoundef.h>
diff --git a/sound/pci/emu10k1/emu10k1_main.c b/sound/pci/emu10k1/emu10k1_main.c
index 972ec40d8166..80aa585eade4 100644
--- a/sound/pci/emu10k1/emu10k1_main.c
+++ b/sound/pci/emu10k1/emu10k1_main.c
@@ -3,8 +3,10 @@
* Creative Labs, Inc.
* Routines for control of EMU10K1 chips
*
- * Copyright (c) by James Courtier-Dutton <James@superbug.demon.co.uk>
+ * Copyright (c) by James Courtier-Dutton <James@superbug.co.uk>
* Added support for Audigy 2 Value.
+ * Added EMU 1010 support.
+ * General bug fixes and enhancements.
*
*
* BUGS:
@@ -41,8 +43,10 @@
#include <sound/core.h>
#include <sound/emu10k1.h>
+#include <linux/firmware.h>
#include "p16v.h"
#include "tina2.h"
+#include "p17v.h"
/*************************************************************************
@@ -117,11 +121,28 @@ static unsigned int spi_dac_init[] = {
0x0622,
0x1400,
};
+
+static unsigned int i2c_adc_init[][2] = {
+ { 0x17, 0x00 }, /* Reset */
+ { 0x07, 0x00 }, /* Timeout */
+ { 0x0b, 0x22 }, /* Interface control */
+ { 0x0c, 0x22 }, /* Master mode control */
+ { 0x0d, 0x08 }, /* Powerdown control */
+ { 0x0e, 0xcf }, /* Attenuation Left 0x01 = -103dB, 0xff = 24dB */
+ { 0x0f, 0xcf }, /* Attenuation Right 0.5dB steps */
+ { 0x10, 0x7b }, /* ALC Control 1 */
+ { 0x11, 0x00 }, /* ALC Control 2 */
+ { 0x12, 0x32 }, /* ALC Control 3 */
+ { 0x13, 0x00 }, /* Noise gate control */
+ { 0x14, 0xa6 }, /* Limiter control */
+ { 0x15, ADC_MUX_2 }, /* ADC Mixer control. Mic for Audigy 2 ZS Notebook */
+};
static int snd_emu10k1_init(struct snd_emu10k1 *emu, int enable_ir, int resume)
{
unsigned int silent_page;
int ch;
+ u32 tmp;
/* disable audio and lock cache */
outl(HCFG_LOCKSOUNDCACHE | HCFG_LOCKTANKCACHE_MASK | HCFG_MUTEBUTTONENABLE,
@@ -160,8 +181,6 @@ static int snd_emu10k1_init(struct snd_emu10k1 *emu, int enable_ir, int resume)
if (emu->card_capabilities->ca0151_chip) { /* audigy2 */
/* Hacks for Alice3 to work independent of haP16V driver */
- u32 tmp;
-
//Setup SRCMulti_I2S SamplingRate
tmp = snd_emu10k1_ptr_read(emu, A_SPDIF_SAMPLERATE, 0);
tmp &= 0xfffff1ff;
@@ -181,8 +200,6 @@ static int snd_emu10k1_init(struct snd_emu10k1 *emu, int enable_ir, int resume)
}
if (emu->card_capabilities->ca0108_chip) { /* audigy2 Value */
/* Hacks for Alice3 to work independent of haP16V driver */
- u32 tmp;
-
snd_printk(KERN_INFO "Audigy2 value: Special config.\n");
//Setup SRCMulti_I2S SamplingRate
tmp = snd_emu10k1_ptr_read(emu, A_SPDIF_SAMPLERATE, 0);
@@ -211,7 +228,7 @@ static int snd_emu10k1_init(struct snd_emu10k1 *emu, int enable_ir, int resume)
int size, n;
size = ARRAY_SIZE(spi_dac_init);
- for (n=0; n < size; n++)
+ for (n = 0; n < size; n++)
snd_emu10k1_spi_write(emu, spi_dac_init[n]);
snd_emu10k1_ptr20_write(emu, 0x60, 0, 0x10);
@@ -228,6 +245,23 @@ static int snd_emu10k1_init(struct snd_emu10k1 *emu, int enable_ir, int resume)
outl(0x76, emu->port + A_IOCFG); /* Windows uses 0x3f76 */
}
+ if (emu->card_capabilities->i2c_adc) { /* Audigy 2 ZS Notebook with ADC Wolfson WM8775 */
+ int size, n;
+
+ snd_emu10k1_ptr20_write(emu, P17V_I2S_SRC_SEL, 0, 0x2020205f);
+ tmp = inl(emu->port + A_IOCFG);
+ outl(tmp | 0x4, emu->port + A_IOCFG); /* Set bit 2 for mic input */
+ tmp = inl(emu->port + A_IOCFG);
+ size = ARRAY_SIZE(i2c_adc_init);
+ for (n = 0; n < size; n++)
+ snd_emu10k1_i2c_write(emu, i2c_adc_init[n][0], i2c_adc_init[n][1]);
+ for (n=0; n < 4; n++) {
+ emu->i2c_capture_volume[n][0]= 0xcf;
+ emu->i2c_capture_volume[n][1]= 0xcf;
+ }
+
+ }
+
snd_emu10k1_ptr_write(emu, PTB, 0, emu->ptb_pages.addr);
snd_emu10k1_ptr_write(emu, TCB, 0, 0); /* taken from original driver */
@@ -239,6 +273,10 @@ static int snd_emu10k1_init(struct snd_emu10k1 *emu, int enable_ir, int resume)
snd_emu10k1_ptr_write(emu, MAPB, ch, silent_page);
}
+ if (emu->card_capabilities->emu1010) {
+ outl(HCFG_AUTOMUTE_ASYNC |
+ HCFG_EMU32_SLAVE |
+ HCFG_AUDIOENABLE, emu->port + HCFG);
/*
* Hokay, setup HCFG
* Mute Disable Audio = 0
@@ -246,7 +284,7 @@ static int snd_emu10k1_init(struct snd_emu10k1 *emu, int enable_ir, int resume)
* Lock Sound Memory = 0
* Auto Mute = 1
*/
- if (emu->audigy) {
+ } else if (emu->audigy) {
if (emu->revision == 4) /* audigy2 */
outl(HCFG_AUDIOENABLE |
HCFG_AC3ENABLE_CDSPDIF |
@@ -265,8 +303,10 @@ static int snd_emu10k1_init(struct snd_emu10k1 *emu, int enable_ir, int resume)
outl(HCFG_LOCKTANKCACHE_MASK | HCFG_AUTOMUTE | HCFG_JOYENABLE, emu->port + HCFG);
if (enable_ir) { /* enable IR for SB Live */
- if ( emu->card_capabilities->emu1212m) {
- ; /* Disable all access to A_IOCFG for the emu1212m */
+ if (emu->card_capabilities->emu1010) {
+ ; /* Disable all access to A_IOCFG for the emu1010 */
+ } else if (emu->card_capabilities->i2c_adc) {
+ ; /* Disable A_IOCFG for Audigy 2 ZS Notebook */
} else if (emu->audigy) {
unsigned int reg = inl(emu->port + A_IOCFG);
outl(reg | A_IOCFG_GPOUT2, emu->port + A_IOCFG);
@@ -284,8 +324,10 @@ static int snd_emu10k1_init(struct snd_emu10k1 *emu, int enable_ir, int resume)
}
}
- if ( emu->card_capabilities->emu1212m) {
- ; /* Disable all access to A_IOCFG for the emu1212m */
+ if (emu->card_capabilities->emu1010) {
+ ; /* Disable all access to A_IOCFG for the emu1010 */
+ } else if (emu->card_capabilities->i2c_adc) {
+ ; /* Disable A_IOCFG for Audigy 2 ZS Notebook */
} else if (emu->audigy) { /* enable analog output */
unsigned int reg = inl(emu->port + A_IOCFG);
outl(reg | A_IOCFG_GPOUT0, emu->port + A_IOCFG);
@@ -302,8 +344,10 @@ static void snd_emu10k1_audio_enable(struct snd_emu10k1 *emu)
outl(inl(emu->port + HCFG) | HCFG_AUDIOENABLE, emu->port + HCFG);
/* Enable analog/digital outs on audigy */
- if ( emu->card_capabilities->emu1212m) {
- ; /* Disable all access to A_IOCFG for the emu1212m */
+ if (emu->card_capabilities->emu1010) {
+ ; /* Disable all access to A_IOCFG for the emu1010 */
+ } else if (emu->card_capabilities->i2c_adc) {
+ ; /* Disable A_IOCFG for Audigy 2 ZS Notebook */
} else if (emu->audigy) {
outl(inl(emu->port + A_IOCFG) & ~0x44, emu->port + A_IOCFG);
@@ -596,133 +640,423 @@ static int snd_emu10k1_cardbus_init(struct snd_emu10k1 * emu)
return 0;
}
-static int snd_emu1212m_fpga_write(struct snd_emu10k1 * emu, int reg, int value)
+static int snd_emu1010_load_firmware(struct snd_emu10k1 * emu, const char * filename)
{
- if (reg<0 || reg>0x3f)
- return 1;
- reg+=0x40; /* 0x40 upwards are registers. */
- if (value<0 || value>0x3f) /* 0 to 0x3f are values */
- return 1;
- outl(reg, emu->port + A_IOCFG);
- outl(reg | 0x80, emu->port + A_IOCFG); /* High bit clocks the value into the fpga. */
- outl(value, emu->port + A_IOCFG);
- outl(value | 0x80 , emu->port + A_IOCFG); /* High bit clocks the value into the fpga. */
-
- return 0;
-}
-
-static int snd_emu1212m_fpga_read(struct snd_emu10k1 * emu, int reg, int *value)
-{
- if (reg<0 || reg>0x3f)
- return 1;
- reg+=0x40; /* 0x40 upwards are registers. */
- outl(reg, emu->port + A_IOCFG);
- outl(reg | 0x80, emu->port + A_IOCFG); /* High bit clocks the value into the fpga. */
- *value = inl(emu->port + A_IOCFG);
-
- return 0;
-}
+ int err;
+ int n, i;
+ int reg;
+ int value;
+ const struct firmware *fw_entry;
+
+ if ((err = request_firmware(&fw_entry, filename, &emu->pci->dev)) != 0) {
+ snd_printk(KERN_ERR "firmware: %s not found. Err=%d\n",filename, err);
+ return err;
+ }
+ snd_printk(KERN_INFO "firmware size=0x%zx\n", fw_entry->size);
+ if (fw_entry->size != 0x133a4) {
+ snd_printk(KERN_ERR "firmware: %s wrong size.\n",filename);
+ return -EINVAL;
+ }
-static int snd_emu1212m_fpga_netlist_write(struct snd_emu10k1 * emu, int reg, int value)
-{
- snd_emu1212m_fpga_write(emu, 0x00, ((reg >> 8) & 0x3f) );
- snd_emu1212m_fpga_write(emu, 0x01, (reg & 0x3f) );
- snd_emu1212m_fpga_write(emu, 0x02, ((value >> 8) & 0x3f) );
- snd_emu1212m_fpga_write(emu, 0x03, (value & 0x3f) );
+ /* The FPGA is a Xilinx Spartan IIE XC2S50E */
+ /* GPIO7 -> FPGA PGMN
+ * GPIO6 -> FPGA CCLK
+ * GPIO5 -> FPGA DIN
+ * FPGA CONFIG OFF -> FPGA PGMN
+ */
+ outl(0x00, emu->port + A_IOCFG); /* Set PGMN low for 1uS. */
+ udelay(1);
+ outl(0x80, emu->port + A_IOCFG); /* Leave bit 7 set during netlist setup. */
+ udelay(100); /* Allow FPGA memory to clean */
+ for(n = 0; n < fw_entry->size; n++) {
+ value=fw_entry->data[n];
+ for(i = 0; i < 8; i++) {
+ reg = 0x80;
+ if (value & 0x1)
+ reg = reg | 0x20;
+ value = value >> 1;
+ outl(reg, emu->port + A_IOCFG);
+ outl(reg | 0x40, emu->port + A_IOCFG);
+ }
+ }
+ /* After programming, set GPIO bit 4 high again. */
+ outl(0x10, emu->port + A_IOCFG);
+
+ release_firmware(fw_entry);
return 0;
}
-static int snd_emu10k1_emu1212m_init(struct snd_emu10k1 * emu)
+static int snd_emu10k1_emu1010_init(struct snd_emu10k1 * emu)
{
unsigned int i;
- int tmp;
-
- snd_printk(KERN_ERR "emu1212m: Special config.\n");
+ int tmp,tmp2;
+ int reg;
+ int err;
+ const char *hana_filename = "emu/hana.fw";
+ const char *dock_filename = "emu/audio_dock.fw";
+
+ snd_printk(KERN_INFO "emu1010: Special config.\n");
+ /* AC97 2.1, Any 16Meg of 4Gig address, Auto-Mute, EMU32 Slave,
+ * Lock Sound Memory Cache, Lock Tank Memory Cache,
+ * Mute all codecs.
+ */
outl(0x0005a00c, emu->port + HCFG);
- outl(0x0005a004, emu->port + HCFG);
+ /* AC97 2.1, Any 16Meg of 4Gig address, Auto-Mute, EMU32 Slave,
+ * Lock Tank Memory Cache,
+ * Mute all codecs.
+ */
+ outl(0x0005a004, emu->port + HCFG);
+ /* AC97 2.1, Any 16Meg of 4Gig address, Auto-Mute, EMU32 Slave,
+ * Mute all codecs.
+ */
outl(0x0005a000, emu->port + HCFG);
+ /* AC97 2.1, Any 16Meg of 4Gig address, Auto-Mute, EMU32 Slave,
+ * Mute all codecs.
+ */
outl(0x0005a000, emu->port + HCFG);
- snd_emu1212m_fpga_read(emu, 0x22, &tmp );
- snd_emu1212m_fpga_read(emu, 0x23, &tmp );
- snd_emu1212m_fpga_read(emu, 0x24, &tmp );
- snd_emu1212m_fpga_write(emu, 0x04, 0x01 );
- snd_emu1212m_fpga_read(emu, 0x0b, &tmp );
- snd_emu1212m_fpga_write(emu, 0x0b, 0x01 );
- snd_emu1212m_fpga_read(emu, 0x10, &tmp );
- snd_emu1212m_fpga_write(emu, 0x10, 0x00 );
- snd_emu1212m_fpga_read(emu, 0x11, &tmp );
- snd_emu1212m_fpga_write(emu, 0x11, 0x30 );
- snd_emu1212m_fpga_read(emu, 0x13, &tmp );
- snd_emu1212m_fpga_write(emu, 0x13, 0x0f );
- snd_emu1212m_fpga_read(emu, 0x11, &tmp );
- snd_emu1212m_fpga_write(emu, 0x11, 0x30 );
- snd_emu1212m_fpga_read(emu, 0x0a, &tmp );
- snd_emu1212m_fpga_write(emu, 0x0a, 0x10 );
- snd_emu1212m_fpga_write(emu, 0x0c, 0x19 );
- snd_emu1212m_fpga_write(emu, 0x12, 0x0c );
- snd_emu1212m_fpga_write(emu, 0x09, 0x0f );
- snd_emu1212m_fpga_write(emu, 0x06, 0x00 );
- snd_emu1212m_fpga_write(emu, 0x05, 0x00 );
- snd_emu1212m_fpga_write(emu, 0x0e, 0x12 );
- snd_emu1212m_fpga_netlist_write(emu, 0x0000, 0x0200);
- snd_emu1212m_fpga_netlist_write(emu, 0x0001, 0x0201);
- snd_emu1212m_fpga_netlist_write(emu, 0x0002, 0x0500);
- snd_emu1212m_fpga_netlist_write(emu, 0x0003, 0x0501);
- snd_emu1212m_fpga_netlist_write(emu, 0x0004, 0x0400);
- snd_emu1212m_fpga_netlist_write(emu, 0x0005, 0x0401);
- snd_emu1212m_fpga_netlist_write(emu, 0x0006, 0x0402);
- snd_emu1212m_fpga_netlist_write(emu, 0x0007, 0x0403);
- snd_emu1212m_fpga_netlist_write(emu, 0x0008, 0x0404);
- snd_emu1212m_fpga_netlist_write(emu, 0x0009, 0x0405);
- snd_emu1212m_fpga_netlist_write(emu, 0x000a, 0x0406);
- snd_emu1212m_fpga_netlist_write(emu, 0x000b, 0x0407);
- snd_emu1212m_fpga_netlist_write(emu, 0x000c, 0x0100);
- snd_emu1212m_fpga_netlist_write(emu, 0x000d, 0x0104);
- snd_emu1212m_fpga_netlist_write(emu, 0x000e, 0x0200);
- snd_emu1212m_fpga_netlist_write(emu, 0x000f, 0x0201);
- for (i=0;i < 0x20;i++) {
- snd_emu1212m_fpga_netlist_write(emu, 0x0100+i, 0x0000);
+ /* Disable 48Volt power to Audio Dock */
+ snd_emu1010_fpga_write(emu, EMU_HANA_DOCK_PWR, 0 );
+
+ /* ID, should read & 0x7f = 0x55. (Bit 7 is the IRQ bit) */
+ snd_emu1010_fpga_read(emu, EMU_HANA_ID, &reg );
+ snd_printdd("reg1=0x%x\n",reg);
+ if (reg == 0x55) {
+ /* FPGA netlist already present so clear it */
+ /* Return to programming mode */
+
+ snd_emu1010_fpga_write(emu, EMU_HANA_FPGA_CONFIG, 0x02 );
}
- for (i=0;i < 4;i++) {
- snd_emu1212m_fpga_netlist_write(emu, 0x0200+i, 0x0000);
+ snd_emu1010_fpga_read(emu, EMU_HANA_ID, &reg );
+ snd_printdd("reg2=0x%x\n",reg);
+ if (reg == 0x55) {
+ /* FPGA failed to return to programming mode */
+ return -ENODEV;
}
- for (i=0;i < 7;i++) {
- snd_emu1212m_fpga_netlist_write(emu, 0x0300+i, 0x0000);
+ snd_printk(KERN_INFO "emu1010: EMU_HANA_ID=0x%x\n",reg);
+ if ((err = snd_emu1010_load_firmware(emu, hana_filename)) != 0) {
+ snd_printk(KERN_INFO "emu1010: Loading Hana Firmware file %s failed\n", hana_filename);
+ return err;
}
- for (i=0;i < 7;i++) {
- snd_emu1212m_fpga_netlist_write(emu, 0x0400+i, 0x0000);
+
+ /* ID, should read & 0x7f = 0x55 when FPGA programmed. */
+ snd_emu1010_fpga_read(emu, EMU_HANA_ID, &reg );
+ if (reg != 0x55) {
+ /* FPGA failed to be programmed */
+ snd_printk(KERN_INFO "emu1010: Loading Hana Firmware file failed, reg=0x%x\n", reg);
+ return -ENODEV;
}
- snd_emu1212m_fpga_netlist_write(emu, 0x0500, 0x0108);
- snd_emu1212m_fpga_netlist_write(emu, 0x0501, 0x010c);
- snd_emu1212m_fpga_netlist_write(emu, 0x0600, 0x0110);
- snd_emu1212m_fpga_netlist_write(emu, 0x0601, 0x0114);
- snd_emu1212m_fpga_netlist_write(emu, 0x0700, 0x0118);
- snd_emu1212m_fpga_netlist_write(emu, 0x0701, 0x011c);
- snd_emu1212m_fpga_write(emu, 0x07, 0x01 );
- snd_emu1212m_fpga_read(emu, 0x21, &tmp );
+ snd_printk(KERN_INFO "emu1010: Hana Firmware loaded\n");
+ snd_emu1010_fpga_read(emu, EMU_HANA_MAJOR_REV, &tmp );
+ snd_emu1010_fpga_read(emu, EMU_HANA_MINOR_REV, &tmp2 );
+ snd_printk("Hana ver:%d.%d\n",tmp ,tmp2);
+ /* Enable 48Volt power to Audio Dock */
+ snd_emu1010_fpga_write(emu, EMU_HANA_DOCK_PWR, EMU_HANA_DOCK_PWR_ON );
+
+ snd_emu1010_fpga_read(emu, EMU_HANA_OPTION_CARDS, &reg );
+ snd_printk(KERN_INFO "emu1010: Card options=0x%x\n",reg);
+ snd_emu1010_fpga_read(emu, EMU_HANA_OPTION_CARDS, &reg );
+ snd_printk(KERN_INFO "emu1010: Card options=0x%x\n",reg);
+ snd_emu1010_fpga_read(emu, EMU_HANA_OPTICAL_TYPE, &tmp );
+ /* ADAT input. */
+ snd_emu1010_fpga_write(emu, EMU_HANA_OPTICAL_TYPE, 0x01 );
+ snd_emu1010_fpga_read(emu, EMU_HANA_ADC_PADS, &tmp );
+ /* Set no attenuation on Audio Dock pads. */
+ snd_emu1010_fpga_write(emu, EMU_HANA_ADC_PADS, 0x00 );
+ emu->emu1010.adc_pads = 0x00;
+ snd_emu1010_fpga_read(emu, EMU_HANA_DOCK_MISC, &tmp );
+ /* Unmute Audio dock DACs, Headphone source DAC-4. */
+ snd_emu1010_fpga_write(emu, EMU_HANA_DOCK_MISC, 0x30 );
+ snd_emu1010_fpga_write(emu, EMU_HANA_DOCK_LEDS_2, 0x12 );
+ snd_emu1010_fpga_read(emu, EMU_HANA_DAC_PADS, &tmp );
+ /* DAC PADs. */
+ snd_emu1010_fpga_write(emu, EMU_HANA_DAC_PADS, 0x0f );
+ emu->emu1010.dac_pads = 0x0f;
+ snd_emu1010_fpga_read(emu, EMU_HANA_DOCK_MISC, &tmp );
+ snd_emu1010_fpga_write(emu, EMU_HANA_DOCK_MISC, 0x30 );
+ snd_emu1010_fpga_read(emu, EMU_HANA_SPDIF_MODE, &tmp );
+ /* SPDIF Format. Set Consumer mode, 24bit, copy enable */
+ snd_emu1010_fpga_write(emu, EMU_HANA_SPDIF_MODE, 0x10 );
+ /* MIDI routing */
+ snd_emu1010_fpga_write(emu, EMU_HANA_MIDI_IN, 0x19 );
+ /* Unknown. */
+ snd_emu1010_fpga_write(emu, EMU_HANA_MIDI_OUT, 0x0c );
+ /* snd_emu1010_fpga_write(emu, 0x09, 0x0f ); // IRQ Enable: All on */
+ /* IRQ Enable: All off */
+ snd_emu1010_fpga_write(emu, EMU_HANA_IRQ_ENABLE, 0x00 );
+
+ snd_emu1010_fpga_read(emu, EMU_HANA_OPTION_CARDS, &reg );
+ snd_printk(KERN_INFO "emu1010: Card options3=0x%x\n",reg);
+ /* Default WCLK set to 48kHz. */
+ snd_emu1010_fpga_write(emu, EMU_HANA_DEFCLOCK, 0x00 );
+ /* Word Clock source, Internal 48kHz x1 */
+ snd_emu1010_fpga_write(emu, EMU_HANA_WCLOCK, EMU_HANA_WCLOCK_INT_48K );
+ //snd_emu1010_fpga_write(emu, EMU_HANA_WCLOCK, EMU_HANA_WCLOCK_INT_48K | EMU_HANA_WCLOCK_4X );
+ /* Audio Dock LEDs. */
+ snd_emu1010_fpga_write(emu, EMU_HANA_DOCK_LEDS_2, 0x12 );
- outl(0x0000a000, emu->port + HCFG);
+#if 0
+ /* For 96kHz */
+ snd_emu1010_fpga_link_dst_src_write(emu,
+ EMU_DST_ALICE2_EMU32_0, EMU_SRC_HAMOA_ADC_LEFT1);
+ snd_emu1010_fpga_link_dst_src_write(emu,
+ EMU_DST_ALICE2_EMU32_1, EMU_SRC_HAMOA_ADC_RIGHT1);
+ snd_emu1010_fpga_link_dst_src_write(emu,
+ EMU_DST_ALICE2_EMU32_4, EMU_SRC_HAMOA_ADC_LEFT2);
+ snd_emu1010_fpga_link_dst_src_write(emu,
+ EMU_DST_ALICE2_EMU32_5, EMU_SRC_HAMOA_ADC_RIGHT2);
+#endif
+#if 0
+ /* For 192kHz */
+ snd_emu1010_fpga_link_dst_src_write(emu,
+ EMU_DST_ALICE2_EMU32_0, EMU_SRC_HAMOA_ADC_LEFT1);
+ snd_emu1010_fpga_link_dst_src_write(emu,
+ EMU_DST_ALICE2_EMU32_1, EMU_SRC_HAMOA_ADC_RIGHT1);
+ snd_emu1010_fpga_link_dst_src_write(emu,
+ EMU_DST_ALICE2_EMU32_2, EMU_SRC_HAMOA_ADC_LEFT2);
+ snd_emu1010_fpga_link_dst_src_write(emu,
+ EMU_DST_ALICE2_EMU32_3, EMU_SRC_HAMOA_ADC_RIGHT2);
+ snd_emu1010_fpga_link_dst_src_write(emu,
+ EMU_DST_ALICE2_EMU32_4, EMU_SRC_HAMOA_ADC_LEFT3);
+ snd_emu1010_fpga_link_dst_src_write(emu,
+ EMU_DST_ALICE2_EMU32_5, EMU_SRC_HAMOA_ADC_RIGHT3);
+ snd_emu1010_fpga_link_dst_src_write(emu,
+ EMU_DST_ALICE2_EMU32_6, EMU_SRC_HAMOA_ADC_LEFT4);
+ snd_emu1010_fpga_link_dst_src_write(emu,
+ EMU_DST_ALICE2_EMU32_7, EMU_SRC_HAMOA_ADC_RIGHT4);
+#endif
+#if 1
+ /* For 48kHz */
+ snd_emu1010_fpga_link_dst_src_write(emu,
+ EMU_DST_ALICE2_EMU32_0, EMU_SRC_DOCK_MIC_A1);
+ snd_emu1010_fpga_link_dst_src_write(emu,
+ EMU_DST_ALICE2_EMU32_1, EMU_SRC_DOCK_MIC_B1);
+ snd_emu1010_fpga_link_dst_src_write(emu,
+ EMU_DST_ALICE2_EMU32_2, EMU_SRC_HAMOA_ADC_LEFT2);
+ snd_emu1010_fpga_link_dst_src_write(emu,
+ EMU_DST_ALICE2_EMU32_3, EMU_SRC_HAMOA_ADC_LEFT2);
+ snd_emu1010_fpga_link_dst_src_write(emu,
+ EMU_DST_ALICE2_EMU32_4, EMU_SRC_DOCK_ADC1_LEFT1);
+ snd_emu1010_fpga_link_dst_src_write(emu,
+ EMU_DST_ALICE2_EMU32_5, EMU_SRC_DOCK_ADC1_RIGHT1);
+ snd_emu1010_fpga_link_dst_src_write(emu,
+ EMU_DST_ALICE2_EMU32_6, EMU_SRC_DOCK_ADC2_LEFT1);
+ snd_emu1010_fpga_link_dst_src_write(emu,
+ EMU_DST_ALICE2_EMU32_7, EMU_SRC_DOCK_ADC2_RIGHT1);
+#endif
+#if 0
+ /* Original */
+ snd_emu1010_fpga_link_dst_src_write(emu,
+ EMU_DST_ALICE2_EMU32_4, EMU_SRC_HANA_ADAT);
+ snd_emu1010_fpga_link_dst_src_write(emu,
+ EMU_DST_ALICE2_EMU32_5, EMU_SRC_HANA_ADAT + 1);
+ snd_emu1010_fpga_link_dst_src_write(emu,
+ EMU_DST_ALICE2_EMU32_6, EMU_SRC_HANA_ADAT + 2);
+ snd_emu1010_fpga_link_dst_src_write(emu,
+ EMU_DST_ALICE2_EMU32_7, EMU_SRC_HANA_ADAT + 3);
+ snd_emu1010_fpga_link_dst_src_write(emu,
+ EMU_DST_ALICE2_EMU32_8, EMU_SRC_HANA_ADAT + 4);
+ snd_emu1010_fpga_link_dst_src_write(emu,
+ EMU_DST_ALICE2_EMU32_9, EMU_SRC_HANA_ADAT + 5);
+ snd_emu1010_fpga_link_dst_src_write(emu,
+ EMU_DST_ALICE2_EMU32_A, EMU_SRC_HANA_ADAT + 6);
+ snd_emu1010_fpga_link_dst_src_write(emu,
+ EMU_DST_ALICE2_EMU32_B, EMU_SRC_HANA_ADAT + 7);
+ snd_emu1010_fpga_link_dst_src_write(emu,
+ EMU_DST_ALICE2_EMU32_C, EMU_SRC_DOCK_MIC_A1);
+ snd_emu1010_fpga_link_dst_src_write(emu,
+ EMU_DST_ALICE2_EMU32_D, EMU_SRC_DOCK_MIC_B1);
+ snd_emu1010_fpga_link_dst_src_write(emu,
+ EMU_DST_ALICE2_EMU32_E, EMU_SRC_HAMOA_ADC_LEFT2);
+ snd_emu1010_fpga_link_dst_src_write(emu,
+ EMU_DST_ALICE2_EMU32_F, EMU_SRC_HAMOA_ADC_LEFT2);
+#endif
+ for (i = 0;i < 0x20; i++ ) {
+ /* AudioDock Elink <- Silence */
+ snd_emu1010_fpga_link_dst_src_write(emu, 0x0100+i, EMU_SRC_SILENCE);
+ }
+ for (i = 0;i < 4; i++) {
+ /* Hana SPDIF Out <- Silence */
+ snd_emu1010_fpga_link_dst_src_write(emu, 0x0200+i, EMU_SRC_SILENCE);
+ }
+ for (i = 0;i < 7; i++) {
+ /* Hamoa DAC <- Silence */
+ snd_emu1010_fpga_link_dst_src_write(emu, 0x0300+i, EMU_SRC_SILENCE);
+ }
+ for (i = 0;i < 7; i++) {
+ /* Hana ADAT Out <- Silence */
+ snd_emu1010_fpga_link_dst_src_write(emu, EMU_DST_HANA_ADAT + i, EMU_SRC_SILENCE);
+ }
+ snd_emu1010_fpga_link_dst_src_write(emu,
+ EMU_DST_ALICE_I2S0_LEFT, EMU_SRC_DOCK_ADC1_LEFT1);
+ snd_emu1010_fpga_link_dst_src_write(emu,
+ EMU_DST_ALICE_I2S0_RIGHT, EMU_SRC_DOCK_ADC1_RIGHT1);
+ snd_emu1010_fpga_link_dst_src_write(emu,
+ EMU_DST_ALICE_I2S1_LEFT, EMU_SRC_DOCK_ADC2_LEFT1);
+ snd_emu1010_fpga_link_dst_src_write(emu,
+ EMU_DST_ALICE_I2S1_RIGHT, EMU_SRC_DOCK_ADC2_RIGHT1);
+ snd_emu1010_fpga_link_dst_src_write(emu,
+ EMU_DST_ALICE_I2S2_LEFT, EMU_SRC_DOCK_ADC3_LEFT1);
+ snd_emu1010_fpga_link_dst_src_write(emu,
+ EMU_DST_ALICE_I2S2_RIGHT, EMU_SRC_DOCK_ADC3_RIGHT1);
+ snd_emu1010_fpga_write(emu, EMU_HANA_UNMUTE, 0x01 ); // Unmute all
+
+ snd_emu1010_fpga_read(emu, EMU_HANA_OPTION_CARDS, &tmp );
+
+ /* AC97 1.03, Any 32Meg of 2Gig address, Auto-Mute, EMU32 Slave,
+ * Lock Sound Memory Cache, Lock Tank Memory Cache,
+ * Mute all codecs.
+ */
+ outl(0x0000a000, emu->port + HCFG);
+ /* AC97 1.03, Any 32Meg of 2Gig address, Auto-Mute, EMU32 Slave,
+ * Lock Sound Memory Cache, Lock Tank Memory Cache,
+ * Un-Mute all codecs.
+ */
outl(0x0000a001, emu->port + HCFG);
+
/* Initial boot complete. Now patches */
- snd_emu1212m_fpga_read(emu, 0x21, &tmp );
- snd_emu1212m_fpga_write(emu, 0x0c, 0x19 );
- snd_emu1212m_fpga_write(emu, 0x12, 0x0c );
- snd_emu1212m_fpga_write(emu, 0x0c, 0x19 );
- snd_emu1212m_fpga_write(emu, 0x12, 0x0c );
- snd_emu1212m_fpga_read(emu, 0x0a, &tmp );
- snd_emu1212m_fpga_write(emu, 0x0a, 0x10 );
-
- snd_emu1212m_fpga_read(emu, 0x20, &tmp );
- snd_emu1212m_fpga_read(emu, 0x21, &tmp );
-
- snd_emu1212m_fpga_netlist_write(emu, 0x0300, 0x0312);
- snd_emu1212m_fpga_netlist_write(emu, 0x0301, 0x0313);
- snd_emu1212m_fpga_netlist_write(emu, 0x0200, 0x0302);
- snd_emu1212m_fpga_netlist_write(emu, 0x0201, 0x0303);
+ snd_emu1010_fpga_read(emu, EMU_HANA_OPTION_CARDS, &tmp );
+ snd_emu1010_fpga_write(emu, EMU_HANA_MIDI_IN, 0x19 ); /* MIDI Route */
+ snd_emu1010_fpga_write(emu, EMU_HANA_MIDI_OUT, 0x0c ); /* Unknown */
+ snd_emu1010_fpga_write(emu, EMU_HANA_MIDI_IN, 0x19 ); /* MIDI Route */
+ snd_emu1010_fpga_write(emu, EMU_HANA_MIDI_OUT, 0x0c ); /* Unknown */
+ snd_emu1010_fpga_read(emu, EMU_HANA_SPDIF_MODE, &tmp );
+ snd_emu1010_fpga_write(emu, EMU_HANA_SPDIF_MODE, 0x10 ); /* SPDIF Format spdif (or 0x11 for aes/ebu) */
+
+ /* Delay to allow Audio Dock to settle */
+ msleep(100);
+ snd_emu1010_fpga_read(emu, EMU_HANA_IRQ_STATUS, &tmp ); /* IRQ Status */
+ snd_emu1010_fpga_read(emu, EMU_HANA_OPTION_CARDS, &reg ); /* OPTIONS: Which cards are attached to the EMU */
+ /* FIXME: The loading of this should be able to happen any time,
+ * as the user can plug/unplug it at any time
+ */
+ if (reg & (EMU_HANA_OPTION_DOCK_ONLINE | EMU_HANA_OPTION_DOCK_OFFLINE) ) {
+ /* Audio Dock attached */
+ /* Return to Audio Dock programming mode */
+ snd_printk(KERN_INFO "emu1010: Loading Audio Dock Firmware\n");
+ snd_emu1010_fpga_write(emu, EMU_HANA_FPGA_CONFIG, EMU_HANA_FPGA_CONFIG_AUDIODOCK );
+ if ((err = snd_emu1010_load_firmware(emu, dock_filename)) != 0) {
+ return err;
+ }
+ snd_emu1010_fpga_write(emu, EMU_HANA_FPGA_CONFIG, 0 );
+ snd_emu1010_fpga_read(emu, EMU_HANA_IRQ_STATUS, &reg );
+ snd_printk(KERN_INFO "emu1010: EMU_HANA+DOCK_IRQ_STATUS=0x%x\n",reg);
+ /* ID, should read & 0x7f = 0x55 when FPGA programmed. */
+ snd_emu1010_fpga_read(emu, EMU_HANA_ID, &reg );
+ snd_printk(KERN_INFO "emu1010: EMU_HANA+DOCK_ID=0x%x\n",reg);
+ if (reg != 0x55) {
+ /* FPGA failed to be programmed */
+ snd_printk(KERN_INFO "emu1010: Loading Audio Dock Firmware file failed, reg=0x%x\n", reg);
+ return 0;
+ return -ENODEV;
+ }
+ snd_printk(KERN_INFO "emu1010: Audio Dock Firmware loaded\n");
+ snd_emu1010_fpga_read(emu, EMU_DOCK_MAJOR_REV, &tmp );
+ snd_emu1010_fpga_read(emu, EMU_DOCK_MINOR_REV, &tmp2 );
+ snd_printk("Audio Dock ver:%d.%d\n",tmp ,tmp2);
+ }
+#if 0
+ snd_emu1010_fpga_link_dst_src_write(emu,
+ EMU_DST_HAMOA_DAC_LEFT1, EMU_SRC_ALICE_EMU32B + 2); /* ALICE2 bus 0xa2 */
+ snd_emu1010_fpga_link_dst_src_write(emu,
+ EMU_DST_HAMOA_DAC_RIGHT1, EMU_SRC_ALICE_EMU32B + 3); /* ALICE2 bus 0xa3 */
+ snd_emu1010_fpga_link_dst_src_write(emu,
+ EMU_DST_HANA_SPDIF_LEFT1, EMU_SRC_ALICE_EMU32A + 2); /* ALICE2 bus 0xb2 */
+ snd_emu1010_fpga_link_dst_src_write(emu,
+ EMU_DST_HANA_SPDIF_RIGHT1, EMU_SRC_ALICE_EMU32A + 3); /* ALICE2 bus 0xb3 */
+#endif
+ /* Default outputs */
+ snd_emu1010_fpga_link_dst_src_write(emu,
+ EMU_DST_DOCK_DAC1_LEFT1, EMU_SRC_ALICE_EMU32A + 0); /* ALICE2 bus 0xa0 */
+ emu->emu1010.output_source[0] = 21;
+ snd_emu1010_fpga_link_dst_src_write(emu,
+ EMU_DST_DOCK_DAC1_RIGHT1, EMU_SRC_ALICE_EMU32A + 1);
+ emu->emu1010.output_source[1] = 22;
+ snd_emu1010_fpga_link_dst_src_write(emu,
+ EMU_DST_DOCK_DAC2_LEFT1, EMU_SRC_ALICE_EMU32A + 2);
+ emu->emu1010.output_source[2] = 23;
+ snd_emu1010_fpga_link_dst_src_write(emu,
+ EMU_DST_DOCK_DAC2_RIGHT1, EMU_SRC_ALICE_EMU32A + 3);
+ emu->emu1010.output_source[3] = 24;
+ snd_emu1010_fpga_link_dst_src_write(emu,
+ EMU_DST_DOCK_DAC3_LEFT1, EMU_SRC_ALICE_EMU32A + 4);
+ emu->emu1010.output_source[4] = 25;
+ snd_emu1010_fpga_link_dst_src_write(emu,
+ EMU_DST_DOCK_DAC3_RIGHT1, EMU_SRC_ALICE_EMU32A + 5);
+ emu->emu1010.output_source[5] = 26;
+ snd_emu1010_fpga_link_dst_src_write(emu,
+ EMU_DST_DOCK_DAC4_LEFT1, EMU_SRC_ALICE_EMU32A + 6);
+ emu->emu1010.output_source[6] = 27;
+ snd_emu1010_fpga_link_dst_src_write(emu,
+ EMU_DST_DOCK_DAC4_RIGHT1, EMU_SRC_ALICE_EMU32A + 7);
+ emu->emu1010.output_source[7] = 28;
+ snd_emu1010_fpga_link_dst_src_write(emu,
+ EMU_DST_DOCK_PHONES_LEFT1, EMU_SRC_ALICE_EMU32A + 0); /* ALICE2 bus 0xa0 */
+ emu->emu1010.output_source[8] = 21;
+ snd_emu1010_fpga_link_dst_src_write(emu,
+ EMU_DST_DOCK_PHONES_RIGHT1, EMU_SRC_ALICE_EMU32A + 1);
+ emu->emu1010.output_source[9] = 22;
+ snd_emu1010_fpga_link_dst_src_write(emu,
+ EMU_DST_DOCK_SPDIF_LEFT1, EMU_SRC_ALICE_EMU32A + 0); /* ALICE2 bus 0xa0 */
+ emu->emu1010.output_source[10] = 21;
+ snd_emu1010_fpga_link_dst_src_write(emu,
+ EMU_DST_DOCK_SPDIF_RIGHT1, EMU_SRC_ALICE_EMU32A + 1);
+ emu->emu1010.output_source[11] = 22;
+ snd_emu1010_fpga_link_dst_src_write(emu,
+ EMU_DST_HANA_SPDIF_LEFT1, EMU_SRC_ALICE_EMU32A + 0); /* ALICE2 bus 0xa0 */
+ emu->emu1010.output_source[12] = 21;
+ snd_emu1010_fpga_link_dst_src_write(emu,
+ EMU_DST_HANA_SPDIF_RIGHT1, EMU_SRC_ALICE_EMU32A + 1);
+ emu->emu1010.output_source[13] = 22;
+ snd_emu1010_fpga_link_dst_src_write(emu,
+ EMU_DST_HAMOA_DAC_LEFT1, EMU_SRC_ALICE_EMU32A + 0); /* ALICE2 bus 0xa0 */
+ emu->emu1010.output_source[14] = 21;
+ snd_emu1010_fpga_link_dst_src_write(emu,
+ EMU_DST_HAMOA_DAC_RIGHT1, EMU_SRC_ALICE_EMU32A + 1);
+ emu->emu1010.output_source[15] = 22;
+ snd_emu1010_fpga_link_dst_src_write(emu,
+ EMU_DST_HANA_ADAT, EMU_SRC_ALICE_EMU32A + 0); /* ALICE2 bus 0xa0 */
+ emu->emu1010.output_source[16] = 21;
+ snd_emu1010_fpga_link_dst_src_write(emu,
+ EMU_DST_HANA_ADAT + 1, EMU_SRC_ALICE_EMU32A + 1);
+ emu->emu1010.output_source[17] = 22;
+ snd_emu1010_fpga_link_dst_src_write(emu,
+ EMU_DST_HANA_ADAT + 2, EMU_SRC_ALICE_EMU32A + 2);
+ emu->emu1010.output_source[18] = 23;
+ snd_emu1010_fpga_link_dst_src_write(emu,
+ EMU_DST_HANA_ADAT + 3, EMU_SRC_ALICE_EMU32A + 3);
+ emu->emu1010.output_source[19] = 24;
+ snd_emu1010_fpga_link_dst_src_write(emu,
+ EMU_DST_HANA_ADAT + 4, EMU_SRC_ALICE_EMU32A + 4);
+ emu->emu1010.output_source[20] = 25;
+ snd_emu1010_fpga_link_dst_src_write(emu,
+ EMU_DST_HANA_ADAT + 5, EMU_SRC_ALICE_EMU32A + 5);
+ emu->emu1010.output_source[21] = 26;
+ snd_emu1010_fpga_link_dst_src_write(emu,
+ EMU_DST_HANA_ADAT + 6, EMU_SRC_ALICE_EMU32A + 6);
+ emu->emu1010.output_source[22] = 27;
+ snd_emu1010_fpga_link_dst_src_write(emu,
+ EMU_DST_HANA_ADAT + 7, EMU_SRC_ALICE_EMU32A + 7);
+ emu->emu1010.output_source[23] = 28;
+
+ /* TEMP: Select SPDIF in/out */
+ snd_emu1010_fpga_write(emu, EMU_HANA_OPTICAL_TYPE, 0x0); /* Output spdif */
+
+ /* TEMP: Select 48kHz SPDIF out */
+ snd_emu1010_fpga_write(emu, EMU_HANA_UNMUTE, 0x0); /* Mute all */
+ snd_emu1010_fpga_write(emu, EMU_HANA_DEFCLOCK, 0x0); /* Default fallback clock 48kHz */
+ /* Word Clock source, Internal 48kHz x1 */
+ snd_emu1010_fpga_write(emu, EMU_HANA_WCLOCK, EMU_HANA_WCLOCK_INT_48K );
+ //snd_emu1010_fpga_write(emu, EMU_HANA_WCLOCK, EMU_HANA_WCLOCK_INT_48K | EMU_HANA_WCLOCK_4X );
+ emu->emu1010.internal_clock = 1; /* 48000 */
+ snd_emu1010_fpga_write(emu, EMU_HANA_DOCK_LEDS_2, 0x12);/* Set LEDs on Audio Dock */
+ snd_emu1010_fpga_write(emu, EMU_HANA_UNMUTE, 0x1); /* Unmute all */
+ //snd_emu1010_fpga_write(emu, 0x7, 0x0); /* Mute all */
+ //snd_emu1010_fpga_write(emu, 0x7, 0x1); /* Unmute all */
+ //snd_emu1010_fpga_write(emu, 0xe, 0x12); /* Set LEDs on Audio Dock */
return 0;
}
@@ -747,6 +1081,10 @@ static int snd_emu10k1_free(struct snd_emu10k1 *emu)
}
snd_emu10k1_free_efx(emu);
}
+ if (emu->card_capabilities->emu1010) {
+ /* Disable 48Volt power to Audio Dock */
+ snd_emu1010_fpga_write(emu, EMU_HANA_DOCK_PWR, 0 );
+ }
if (emu->memhdr)
snd_util_memhdr_free(emu->memhdr);
if (emu->silent_page.area)
@@ -838,10 +1176,11 @@ static struct snd_emu_chip_details emu_chip_details[] = {
.adc_1361t = 1, /* 24 bit capture instead of 16bit */
.ac97_chip = 1} ,
/* Audigy 2 ZS Notebook Cardbus card.*/
- /* Tested by James@superbug.co.uk 22th December 2005 */
+ /* Tested by James@superbug.co.uk 6th November 2006 */
/* Audio output 7.1/Headphones working.
* Digital output working. (AC3 not checked, only PCM)
- * Audio inputs not tested.
+ * Audio Mic/Line inputs working.
+ * Digital input not tested.
*/
/* DSP: Tina2
* DAC: Wolfson WM8768/WM8568
@@ -849,6 +1188,25 @@ static struct snd_emu_chip_details emu_chip_details[] = {
* AC97: None
* CA0151: None
*/
+ /* Tested by James@superbug.co.uk 4th April 2006 */
+ /* A_IOCFG bits
+ * Output
+ * 0: Not Used
+ * 1: 0 = Mute all the 7.1 channel out. 1 = unmute.
+ * 2: Analog input 0 = line in, 1 = mic in
+ * 3: Not Used
+ * 4: Digital output 0 = off, 1 = on.
+ * 5: Not Used
+ * 6: Not Used
+ * 7: Not Used
+ * Input
+ * All bits 1 (0x3fxx) means nothing plugged in.
+ * 8-9: 0 = Line in/Mic, 2 = Optical in, 3 = Nothing.
+ * A-B: 0 = Headphones, 2 = Optical out, 3 = Nothing.
+ * C-D: 2 = Front/Rear/etc, 3 = nothing.
+ * E-F: Always 0
+ *
+ */
{.vendor = 0x1102, .device = 0x0008, .subsystem = 0x20011102,
.driver = "Audigy2", .name = "Audigy 2 ZS Notebook [SB0530]",
.id = "Audigy2",
@@ -856,6 +1214,7 @@ static struct snd_emu_chip_details emu_chip_details[] = {
.ca0108_chip = 1,
.ca_cardbus_chip = 1,
.spi_dac = 1,
+ .i2c_adc = 1,
.spk71 = 1} ,
{.vendor = 0x1102, .device = 0x0008,
.driver = "Audigy2", .name = "Audigy 2 Value [Unknown]",
@@ -865,11 +1224,12 @@ static struct snd_emu_chip_details emu_chip_details[] = {
.ac97_chip = 1} ,
/* Tested by James@superbug.co.uk 8th July 2005. No sound available yet. */
{.vendor = 0x1102, .device = 0x0004, .subsystem = 0x40011102,
- .driver = "Audigy2", .name = "E-mu 1212m [4001]",
- .id = "EMU1212m",
+ .driver = "Audigy2", .name = "E-mu 1010 [4001]",
+ .id = "EMU1010",
.emu10k2_chip = 1,
.ca0102_chip = 1,
- .emu1212m = 1} ,
+ .spk71 = 1,
+ .emu1010 = 1} ,
/* Tested by James@superbug.co.uk 3rd July 2005 */
{.vendor = 0x1102, .device = 0x0004, .subsystem = 0x20071102,
.driver = "Audigy2", .name = "Audigy 4 PRO [SB0380]",
@@ -1297,8 +1657,8 @@ int __devinit snd_emu10k1_create(struct snd_card *card,
} else if (emu->card_capabilities->ca_cardbus_chip) {
if ((err = snd_emu10k1_cardbus_init(emu)) < 0)
goto error;
- } else if (emu->card_capabilities->emu1212m) {
- if ((err = snd_emu10k1_emu1212m_init(emu)) < 0) {
+ } else if (emu->card_capabilities->emu1010) {
+ if ((err = snd_emu10k1_emu1010_init(emu)) < 0) {
snd_emu10k1_free(emu);
return err;
}
@@ -1446,8 +1806,8 @@ void snd_emu10k1_resume_init(struct snd_emu10k1 *emu)
snd_emu10k1_ecard_init(emu);
else if (emu->card_capabilities->ca_cardbus_chip)
snd_emu10k1_cardbus_init(emu);
- else if (emu->card_capabilities->emu1212m)
- snd_emu10k1_emu1212m_init(emu);
+ else if (emu->card_capabilities->emu1010)
+ snd_emu10k1_emu1010_init(emu);
else
snd_emu10k1_ptr_write(emu, AC97SLOT, 0, AC97SLOT_CNTR|AC97SLOT_LFE);
snd_emu10k1_init(emu, emu->enable_ir, 1);
diff --git a/sound/pci/emu10k1/emu10k1x.c b/sound/pci/emu10k1/emu10k1x.c
index 2199b42a6019..bb0fec7f7e1b 100644
--- a/sound/pci/emu10k1/emu10k1x.c
+++ b/sound/pci/emu10k1/emu10k1x.c
@@ -460,7 +460,7 @@ static int snd_emu10k1x_pcm_prepare(struct snd_pcm_substream *substream)
u32 period_size_bytes = frames_to_bytes(runtime, runtime->period_size);
int i;
- for(i=0; i < runtime->periods; i++) {
+ for(i = 0; i < runtime->periods; i++) {
*table_base++=runtime->dma_addr+(i*period_size_bytes);
*table_base++=period_size_bytes<<16;
}
@@ -1042,8 +1042,8 @@ static void snd_emu10k1x_proc_reg_write(struct snd_info_entry *entry,
if (sscanf(line, "%x %x %x", &reg, &channel_id, &val) != 3)
continue;
- if ((reg < 0x49) && (reg >=0) && (val <= 0xffffffff)
- && (channel_id >=0) && (channel_id <= 2) )
+ if ((reg < 0x49) && (reg >= 0) && (val <= 0xffffffff)
+ && (channel_id >= 0) && (channel_id <= 2) )
snd_emu10k1x_ptr_write(emu, reg, channel_id, val);
}
}
diff --git a/sound/pci/emu10k1/emufx.c b/sound/pci/emu10k1/emufx.c
index 13cd6ce89811..c02012cccd8e 100644
--- a/sound/pci/emu10k1/emufx.c
+++ b/sound/pci/emu10k1/emufx.c
@@ -3,6 +3,9 @@
* Creative Labs, Inc.
* Routines for effect processor FX8010
*
+ * Copyright (c) by James Courtier-Dutton <James@superbug.co.uk>
+ * Added EMU 1010 support.
+ *
* BUGS:
* --
*
@@ -293,7 +296,7 @@ static const u32 db_table[101] = {
};
/* EMU10k1/EMU10k2 DSP control db gain */
-static DECLARE_TLV_DB_SCALE(snd_emu10k1_db_scale1, -4000, 40, 1);
+static const DECLARE_TLV_DB_SCALE(snd_emu10k1_db_scale1, -4000, 40, 1);
static const u32 onoff_table[2] = {
0x00000000, 0x00000001
@@ -652,13 +655,66 @@ snd_emu10k1_look_for_ctl(struct snd_emu10k1 *emu, struct snd_ctl_elem_id *id)
return NULL;
}
+#define MAX_TLV_SIZE 256
+
+static unsigned int *copy_tlv(const unsigned int __user *_tlv)
+{
+ unsigned int data[2];
+ unsigned int *tlv;
+
+ if (!_tlv)
+ return NULL;
+ if (copy_from_user(data, _tlv, sizeof(data)))
+ return NULL;
+ if (data[1] >= MAX_TLV_SIZE)
+ return NULL;
+ tlv = kmalloc(data[1] * 4 + sizeof(data), GFP_KERNEL);
+ if (!tlv)
+ return NULL;
+ memcpy(tlv, data, sizeof(data));
+ if (copy_from_user(tlv + 2, _tlv + 2, data[1])) {
+ kfree(tlv);
+ return NULL;
+ }
+ return tlv;
+}
+
+static int copy_gctl(struct snd_emu10k1 *emu,
+ struct snd_emu10k1_fx8010_control_gpr *gctl,
+ struct snd_emu10k1_fx8010_control_gpr __user *_gctl,
+ int idx)
+{
+ struct snd_emu10k1_fx8010_control_old_gpr __user *octl;
+
+ if (emu->support_tlv)
+ return copy_from_user(gctl, &_gctl[idx], sizeof(*gctl));
+ octl = (struct snd_emu10k1_fx8010_control_old_gpr __user *)_gctl;
+ if (copy_from_user(gctl, &octl[idx], sizeof(*octl)))
+ return -EFAULT;
+ gctl->tlv = NULL;
+ return 0;
+}
+
+static int copy_gctl_to_user(struct snd_emu10k1 *emu,
+ struct snd_emu10k1_fx8010_control_gpr __user *_gctl,
+ struct snd_emu10k1_fx8010_control_gpr *gctl,
+ int idx)
+{
+ struct snd_emu10k1_fx8010_control_old_gpr __user *octl;
+
+ if (emu->support_tlv)
+ return copy_to_user(&_gctl[idx], gctl, sizeof(*gctl));
+
+ octl = (struct snd_emu10k1_fx8010_control_old_gpr __user *)_gctl;
+ return copy_to_user(&octl[idx], gctl, sizeof(*octl));
+}
+
static int snd_emu10k1_verify_controls(struct snd_emu10k1 *emu,
struct snd_emu10k1_fx8010_code *icode)
{
unsigned int i;
struct snd_ctl_elem_id __user *_id;
struct snd_ctl_elem_id id;
- struct snd_emu10k1_fx8010_control_gpr __user *_gctl;
struct snd_emu10k1_fx8010_control_gpr *gctl;
int err;
@@ -673,9 +729,8 @@ static int snd_emu10k1_verify_controls(struct snd_emu10k1 *emu,
if (! gctl)
return -ENOMEM;
err = 0;
- for (i = 0, _gctl = icode->gpr_add_controls;
- i < icode->gpr_add_control_count; i++, _gctl++) {
- if (copy_from_user(gctl, _gctl, sizeof(*gctl))) {
+ for (i = 0; i < icode->gpr_add_control_count; i++) {
+ if (copy_gctl(emu, gctl, icode->gpr_add_controls, i)) {
err = -EFAULT;
goto __error;
}
@@ -694,10 +749,9 @@ static int snd_emu10k1_verify_controls(struct snd_emu10k1 *emu,
goto __error;
}
}
- for (i = 0, _gctl = icode->gpr_list_controls;
- i < icode->gpr_list_control_count; i++, _gctl++) {
+ for (i = 0; i < icode->gpr_list_control_count; i++) {
/* FIXME: we need to check the WRITE access */
- if (copy_from_user(gctl, _gctl, sizeof(*gctl))) {
+ if (copy_gctl(emu, gctl, icode->gpr_list_controls, i)) {
err = -EFAULT;
goto __error;
}
@@ -715,13 +769,14 @@ static void snd_emu10k1_ctl_private_free(struct snd_kcontrol *kctl)
kctl->private_value = 0;
list_del(&ctl->list);
kfree(ctl);
+ if (kctl->tlv.p)
+ kfree(kctl->tlv.p);
}
static int snd_emu10k1_add_controls(struct snd_emu10k1 *emu,
struct snd_emu10k1_fx8010_code *icode)
{
unsigned int i, j;
- struct snd_emu10k1_fx8010_control_gpr __user *_gctl;
struct snd_emu10k1_fx8010_control_gpr *gctl;
struct snd_emu10k1_fx8010_ctl *ctl, *nctl;
struct snd_kcontrol_new knew;
@@ -737,9 +792,8 @@ static int snd_emu10k1_add_controls(struct snd_emu10k1 *emu,
goto __error;
}
- for (i = 0, _gctl = icode->gpr_add_controls;
- i < icode->gpr_add_control_count; i++, _gctl++) {
- if (copy_from_user(gctl, _gctl, sizeof(*gctl))) {
+ for (i = 0; i < icode->gpr_add_control_count; i++) {
+ if (copy_gctl(emu, gctl, icode->gpr_add_controls, i)) {
err = -EFAULT;
goto __error;
}
@@ -760,11 +814,10 @@ static int snd_emu10k1_add_controls(struct snd_emu10k1 *emu,
knew.device = gctl->id.device;
knew.subdevice = gctl->id.subdevice;
knew.info = snd_emu10k1_gpr_ctl_info;
- if (gctl->tlv.p) {
- knew.tlv.p = gctl->tlv.p;
+ knew.tlv.p = copy_tlv(gctl->tlv);
+ if (knew.tlv.p)
knew.access = SNDRV_CTL_ELEM_ACCESS_READWRITE |
SNDRV_CTL_ELEM_ACCESS_TLV_READ;
- }
knew.get = snd_emu10k1_gpr_ctl_get;
knew.put = snd_emu10k1_gpr_ctl_put;
memset(nctl, 0, sizeof(*nctl));
@@ -782,12 +835,14 @@ static int snd_emu10k1_add_controls(struct snd_emu10k1 *emu,
ctl = kmalloc(sizeof(*ctl), GFP_KERNEL);
if (ctl == NULL) {
err = -ENOMEM;
+ kfree(knew.tlv.p);
goto __error;
}
knew.private_value = (unsigned long)ctl;
*ctl = *nctl;
if ((err = snd_ctl_add(emu->card, kctl = snd_ctl_new1(&knew, emu))) < 0) {
kfree(ctl);
+ kfree(knew.tlv.p);
goto __error;
}
kctl->private_free = snd_emu10k1_ctl_private_free;
@@ -838,7 +893,6 @@ static int snd_emu10k1_list_controls(struct snd_emu10k1 *emu,
unsigned int i = 0, j;
unsigned int total = 0;
struct snd_emu10k1_fx8010_control_gpr *gctl;
- struct snd_emu10k1_fx8010_control_gpr __user *_gctl;
struct snd_emu10k1_fx8010_ctl *ctl;
struct snd_ctl_elem_id *id;
struct list_head *list;
@@ -847,11 +901,11 @@ static int snd_emu10k1_list_controls(struct snd_emu10k1 *emu,
if (! gctl)
return -ENOMEM;
- _gctl = icode->gpr_list_controls;
list_for_each(list, &emu->fx8010.gpr_ctl) {
ctl = emu10k1_gpr_ctl(list);
total++;
- if (_gctl && i < icode->gpr_list_control_count) {
+ if (icode->gpr_list_controls &&
+ i < icode->gpr_list_control_count) {
memset(gctl, 0, sizeof(*gctl));
id = &ctl->kcontrol->id;
gctl->id.iface = id->iface;
@@ -868,11 +922,11 @@ static int snd_emu10k1_list_controls(struct snd_emu10k1 *emu,
gctl->min = ctl->min;
gctl->max = ctl->max;
gctl->translation = ctl->translation;
- if (copy_to_user(_gctl, gctl, sizeof(*gctl))) {
+ if (copy_gctl_to_user(emu, icode->gpr_list_controls,
+ gctl, i)) {
kfree(gctl);
return -EFAULT;
}
- _gctl++;
i++;
}
}
@@ -1023,7 +1077,7 @@ snd_emu10k1_init_mono_control(struct snd_emu10k1_fx8010_control_gpr *ctl,
ctl->gpr[0] = gpr + 0; ctl->value[0] = defval;
ctl->min = 0;
ctl->max = 100;
- ctl->tlv.p = snd_emu10k1_db_scale1;
+ ctl->tlv = snd_emu10k1_db_scale1;
ctl->translation = EMU10K1_GPR_TRANSLATION_TABLE100;
}
@@ -1038,7 +1092,7 @@ snd_emu10k1_init_stereo_control(struct snd_emu10k1_fx8010_control_gpr *ctl,
ctl->gpr[1] = gpr + 1; ctl->value[1] = defval;
ctl->min = 0;
ctl->max = 100;
- ctl->tlv.p = snd_emu10k1_db_scale1;
+ ctl->tlv = snd_emu10k1_db_scale1;
ctl->translation = EMU10K1_GPR_TRANSLATION_TABLE100;
}
@@ -1069,6 +1123,21 @@ snd_emu10k1_init_stereo_onoff_control(struct snd_emu10k1_fx8010_control_gpr *ctl
ctl->translation = EMU10K1_GPR_TRANSLATION_ONOFF;
}
+static int snd_emu10k1_audigy_dsp_convert_32_to_2x16(
+ struct snd_emu10k1_fx8010_code *icode,
+ u32 *ptr, int tmp, int bit_shifter16,
+ int reg_in, int reg_out)
+{
+ A_OP(icode, ptr, iACC3, A_GPR(tmp + 1), reg_in, A_C_00000000, A_C_00000000);
+ A_OP(icode, ptr, iANDXOR, A_GPR(tmp), A_GPR(tmp + 1), A_GPR(bit_shifter16 - 1), A_C_00000000);
+ A_OP(icode, ptr, iTSTNEG, A_GPR(tmp + 2), A_GPR(tmp), A_C_80000000, A_GPR(bit_shifter16 - 2));
+ A_OP(icode, ptr, iANDXOR, A_GPR(tmp + 2), A_GPR(tmp + 2), A_C_80000000, A_C_00000000);
+ A_OP(icode, ptr, iANDXOR, A_GPR(tmp), A_GPR(tmp), A_GPR(bit_shifter16 - 3), A_C_00000000);
+ A_OP(icode, ptr, iMACINT0, A_GPR(tmp), A_C_00000000, A_GPR(tmp), A_C_00010000);
+ A_OP(icode, ptr, iANDXOR, reg_out, A_GPR(tmp), A_C_ffffffff, A_GPR(tmp + 2));
+ A_OP(icode, ptr, iACC3, reg_out + 1, A_GPR(tmp + 1), A_C_00000000, A_C_00000000);
+ return 1;
+}
/*
* initial DSP configuration for Audigy
@@ -1077,6 +1146,7 @@ snd_emu10k1_init_stereo_onoff_control(struct snd_emu10k1_fx8010_control_gpr *ctl
static int __devinit _snd_emu10k1_audigy_init_efx(struct snd_emu10k1 *emu)
{
int err, i, z, gpr, nctl;
+ int bit_shifter16;
const int playback = 10;
const int capture = playback + (SND_EMU10K1_PLAYBACK_CHANNELS * 2); /* we reserve 10 voices */
const int stereo_mix = capture + 2;
@@ -1114,17 +1184,14 @@ static int __devinit _snd_emu10k1_audigy_init_efx(struct snd_emu10k1 *emu)
ptr = 0;
nctl = 0;
gpr = stereo_mix + 10;
+ gpr_map[gpr++] = 0x00007fff;
+ gpr_map[gpr++] = 0x00008000;
+ gpr_map[gpr++] = 0x0000ffff;
+ bit_shifter16 = gpr;
/* stop FX processor */
snd_emu10k1_ptr_write(emu, A_DBG, 0, (emu->fx8010.dbg = 0) | A_DBG_SINGLE_STEP);
-#if 0
- /* FIX: jcd test */
- for (z = 0; z < 80; z=z+2) {
- A_OP(icode, &ptr, iACC3, A_EXTOUT(z), A_FXBUS(FXBUS_PCM_LEFT_FRONT), A_C_00000000, A_C_00000000); /* left */
- A_OP(icode, &ptr, iACC3, A_EXTOUT(z+1), A_FXBUS(FXBUS_PCM_RIGHT_FRONT), A_C_00000000, A_C_00000000); /* right */
- }
-#endif /* jcd test */
#if 1
/* PCM front Playback Volume (independent from stereo mix) */
A_OP(icode, &ptr, iMAC0, A_GPR(playback), A_C_00000000, A_GPR(gpr), A_FXBUS(FXBUS_PCM_LEFT_FRONT));
@@ -1182,13 +1249,20 @@ static int __devinit _snd_emu10k1_audigy_init_efx(struct snd_emu10k1 *emu)
A_OP(icode, &ptr, iMAC0, A_GPR(capture+1), A_GPR(capture+1), A_GPR(gpr+1), A_FXBUS(FXBUS_MIDI_RIGHT));
snd_emu10k1_init_stereo_control(&controls[nctl++], "Synth Capture Volume", gpr, 0);
gpr += 2;
-
+
/*
* inputs
*/
#define A_ADD_VOLUME_IN(var,vol,input) \
A_OP(icode, &ptr, iMAC0, A_GPR(var), A_GPR(var), A_GPR(vol), A_EXTIN(input))
+ /* emu1212 DSP 0 and DSP 1 Capture */
+ if (emu->card_capabilities->emu1010) {
+ A_OP(icode, &ptr, iMAC0, A_GPR(capture+0), A_GPR(capture+0), A_GPR(gpr), A_P16VIN(0x0));
+ A_OP(icode, &ptr, iMAC0, A_GPR(capture+1), A_GPR(capture+1), A_GPR(gpr+1), A_P16VIN(0x1));
+ snd_emu10k1_init_stereo_control(&controls[nctl++], "EMU Capture Volume", gpr, 0);
+ gpr += 2;
+ }
/* AC'97 Playback Volume - used only for mic (renamed later) */
A_ADD_VOLUME_IN(stereo_mix, gpr, A_EXTIN_AC97_L);
A_ADD_VOLUME_IN(stereo_mix+1, gpr+1, A_EXTIN_AC97_R);
@@ -1429,6 +1503,13 @@ A_OP(icode, &ptr, iMAC0, A_GPR(var), A_GPR(var), A_GPR(vol), A_EXTIN(input))
/* digital outputs */
/* A_PUT_STEREO_OUTPUT(A_EXTOUT_FRONT_L, A_EXTOUT_FRONT_R, playback + SND_EMU10K1_PLAYBACK_CHANNELS); */
+ if (emu->card_capabilities->emu1010) {
+ /* EMU1010 Outputs from PCM Front, Rear, Center, LFE, Side */
+ snd_printk("EMU outputs on\n");
+ for (z = 0; z < 8; z++) {
+ A_OP(icode, &ptr, iACC3, A_EMU32OUTL(z), A_GPR(playback + SND_EMU10K1_PLAYBACK_CHANNELS + z), A_C_00000000, A_C_00000000);
+ }
+ }
/* IEC958 Optical Raw Playback Switch */
gpr_map[gpr++] = 0;
@@ -1466,9 +1547,57 @@ A_OP(icode, &ptr, iMAC0, A_GPR(var), A_GPR(var), A_GPR(vol), A_EXTIN(input))
A_PUT_OUTPUT(A_EXTOUT_ADC_CAP_R, capture+1);
#endif
- /* EFX capture - capture the 16 EXTINs */
- for (z = 0; z < 16; z++) {
- A_OP(icode, &ptr, iACC3, A_FXBUS2(z), A_C_00000000, A_C_00000000, A_EXTIN(z));
+ if (emu->card_capabilities->emu1010) {
+ snd_printk("EMU inputs on\n");
+ /* Capture 8 channels of S32_LE sound */
+
+ /* printk("emufx.c: gpr=0x%x, tmp=0x%x\n",gpr, tmp); */
+ /* For the EMU1010: How to get 32bit values from the DSP. High 16bits into L, low 16bits into R. */
+ /* A_P16VIN(0) is delayed by one sample,
+ * so all other A_P16VIN channels will need to also be delayed
+ */
+ /* Left ADC in. 1 of 2 */
+ snd_emu10k1_audigy_dsp_convert_32_to_2x16( icode, &ptr, tmp, bit_shifter16, A_P16VIN(0x0), A_FXBUS2(0) );
+ /* Right ADC in 1 of 2 */
+ gpr_map[gpr++] = 0x00000000;
+ snd_emu10k1_audigy_dsp_convert_32_to_2x16( icode, &ptr, tmp, bit_shifter16, A_GPR(gpr - 1), A_FXBUS2(2) );
+ A_OP(icode, &ptr, iACC3, A_GPR(gpr - 1), A_P16VIN(0x1), A_C_00000000, A_C_00000000);
+ gpr_map[gpr++] = 0x00000000;
+ snd_emu10k1_audigy_dsp_convert_32_to_2x16( icode, &ptr, tmp, bit_shifter16, A_GPR(gpr - 1), A_FXBUS2(4) );
+ A_OP(icode, &ptr, iACC3, A_GPR(gpr - 1), A_P16VIN(0x2), A_C_00000000, A_C_00000000);
+ gpr_map[gpr++] = 0x00000000;
+ snd_emu10k1_audigy_dsp_convert_32_to_2x16( icode, &ptr, tmp, bit_shifter16, A_GPR(gpr - 1), A_FXBUS2(6) );
+ A_OP(icode, &ptr, iACC3, A_GPR(gpr - 1), A_P16VIN(0x3), A_C_00000000, A_C_00000000);
+ /* For 96kHz mode */
+ /* Left ADC in. 2 of 2 */
+ gpr_map[gpr++] = 0x00000000;
+ snd_emu10k1_audigy_dsp_convert_32_to_2x16( icode, &ptr, tmp, bit_shifter16, A_GPR(gpr - 1), A_FXBUS2(0x8) );
+ A_OP(icode, &ptr, iACC3, A_GPR(gpr - 1), A_P16VIN(0x4), A_C_00000000, A_C_00000000);
+ /* Right ADC in 2 of 2 */
+ gpr_map[gpr++] = 0x00000000;
+ snd_emu10k1_audigy_dsp_convert_32_to_2x16( icode, &ptr, tmp, bit_shifter16, A_GPR(gpr - 1), A_FXBUS2(0xa) );
+ A_OP(icode, &ptr, iACC3, A_GPR(gpr - 1), A_P16VIN(0x5), A_C_00000000, A_C_00000000);
+ gpr_map[gpr++] = 0x00000000;
+ snd_emu10k1_audigy_dsp_convert_32_to_2x16( icode, &ptr, tmp, bit_shifter16, A_GPR(gpr - 1), A_FXBUS2(0xc) );
+ A_OP(icode, &ptr, iACC3, A_GPR(gpr - 1), A_P16VIN(0x6), A_C_00000000, A_C_00000000);
+ gpr_map[gpr++] = 0x00000000;
+ snd_emu10k1_audigy_dsp_convert_32_to_2x16( icode, &ptr, tmp, bit_shifter16, A_GPR(gpr - 1), A_FXBUS2(0xe) );
+ A_OP(icode, &ptr, iACC3, A_GPR(gpr - 1), A_P16VIN(0x7), A_C_00000000, A_C_00000000);
+
+#if 0
+ for (z = 4; z < 8; z++) {
+ A_OP(icode, &ptr, iACC3, A_FXBUS2(z), A_C_00000000, A_C_00000000, A_C_00000000);
+ }
+ for (z = 0xc; z < 0x10; z++) {
+ A_OP(icode, &ptr, iACC3, A_FXBUS2(z), A_C_00000000, A_C_00000000, A_C_00000000);
+ }
+#endif
+ } else {
+ /* EFX capture - capture the 16 EXTINs */
+ /* Capture 16 channels of S16_LE sound */
+ for (z = 0; z < 16; z++) {
+ A_OP(icode, &ptr, iACC3, A_FXBUS2(z), A_C_00000000, A_C_00000000, A_EXTIN(z));
+ }
}
#endif /* JCD test */
@@ -1488,7 +1617,9 @@ A_OP(icode, &ptr, iMAC0, A_GPR(var), A_GPR(var), A_GPR(vol), A_EXTIN(input))
seg = snd_enter_user();
icode->gpr_add_control_count = nctl;
icode->gpr_add_controls = (struct snd_emu10k1_fx8010_control_gpr __user *)controls;
+ emu->support_tlv = 1; /* support TLV */
err = snd_emu10k1_icode_poke(emu, icode);
+ emu->support_tlv = 0; /* clear again */
snd_leave_user(seg);
__err:
@@ -2105,7 +2236,9 @@ static int __devinit _snd_emu10k1_init_efx(struct snd_emu10k1 *emu)
seg = snd_enter_user();
icode->gpr_add_control_count = i;
icode->gpr_add_controls = (struct snd_emu10k1_fx8010_control_gpr __user *)controls;
+ emu->support_tlv = 1; /* support TLV */
err = snd_emu10k1_icode_poke(emu, icode);
+ emu->support_tlv = 0; /* clear again */
snd_leave_user(seg);
if (err >= 0)
err = snd_emu10k1_ipcm_poke(emu, ipcm);
@@ -2138,7 +2271,7 @@ void snd_emu10k1_free_efx(struct snd_emu10k1 *emu)
snd_emu10k1_ptr_write(emu, DBG, 0, emu->fx8010.dbg = EMU10K1_DBG_SINGLE_STEP);
}
-#if 0 // FIXME: who use them?
+#if 0 /* FIXME: who use them? */
int snd_emu10k1_fx8010_tone_control_activate(struct snd_emu10k1 *emu, int output)
{
if (output < 0 || output >= 6)
@@ -2249,6 +2382,9 @@ static int snd_emu10k1_fx8010_ioctl(struct snd_hwdep * hw, struct file *file, un
int res;
switch (cmd) {
+ case SNDRV_EMU10K1_IOCTL_PVERSION:
+ emu->support_tlv = 1;
+ return put_user(SNDRV_EMU10K1_VERSION, (int __user *)argp);
case SNDRV_EMU10K1_IOCTL_INFO:
info = kmalloc(sizeof(*info), GFP_KERNEL);
if (!info)
diff --git a/sound/pci/emu10k1/emumixer.c b/sound/pci/emu10k1/emumixer.c
index c31f3d0877fa..4db6e1ca1665 100644
--- a/sound/pci/emu10k1/emumixer.c
+++ b/sound/pci/emu10k1/emumixer.c
@@ -5,6 +5,9 @@
* Routines for control of EMU10K1 chips / mixer routines
* Multichannel PCM support Copyright (c) Lee Revell <rlrevell@joe-job.com>
*
+ * Copyright (c) by James Courtier-Dutton <James@superbug.co.uk>
+ * Added EMU 1010 support.
+ *
* BUGS:
* --
*
@@ -32,9 +35,15 @@
#include <linux/init.h>
#include <sound/core.h>
#include <sound/emu10k1.h>
+#include <linux/delay.h>
+#include <sound/tlv.h>
+
+#include "p17v.h"
#define AC97_ID_STAC9758 0x83847658
+static const DECLARE_TLV_DB_SCALE(snd_audigy_db_scale2, -10350, 50, 1); /* WM8775 gain scale */
+
static int snd_emu10k1_spdif_info(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *uinfo)
{
uinfo->type = SNDRV_CTL_ELEM_TYPE_IEC958;
@@ -68,6 +77,669 @@ static int snd_emu10k1_spdif_get_mask(struct snd_kcontrol *kcontrol,
return 0;
}
+static char *emu1010_src_texts[] = {
+ "Silence",
+ "Dock Mic A",
+ "Dock Mic B",
+ "Dock ADC1 Left",
+ "Dock ADC1 Right",
+ "Dock ADC2 Left",
+ "Dock ADC2 Right",
+ "Dock ADC3 Left",
+ "Dock ADC3 Right",
+ "0202 ADC Left",
+ "0202 ADC Right",
+ "0202 SPDIF Left",
+ "0202 SPDIF Right",
+ "ADAT 0",
+ "ADAT 1",
+ "ADAT 2",
+ "ADAT 3",
+ "ADAT 4",
+ "ADAT 5",
+ "ADAT 6",
+ "ADAT 7",
+ "DSP 0",
+ "DSP 1",
+ "DSP 2",
+ "DSP 3",
+ "DSP 4",
+ "DSP 5",
+ "DSP 6",
+ "DSP 7",
+ "DSP 8",
+ "DSP 9",
+ "DSP 10",
+ "DSP 11",
+ "DSP 12",
+ "DSP 13",
+ "DSP 14",
+ "DSP 15",
+ "DSP 16",
+ "DSP 17",
+ "DSP 18",
+ "DSP 19",
+ "DSP 20",
+ "DSP 21",
+ "DSP 22",
+ "DSP 23",
+ "DSP 24",
+ "DSP 25",
+ "DSP 26",
+ "DSP 27",
+ "DSP 28",
+ "DSP 29",
+ "DSP 30",
+ "DSP 31",
+};
+
+static unsigned int emu1010_src_regs[] = {
+ EMU_SRC_SILENCE,/* 0 */
+ EMU_SRC_DOCK_MIC_A1, /* 1 */
+ EMU_SRC_DOCK_MIC_B1, /* 2 */
+ EMU_SRC_DOCK_ADC1_LEFT1, /* 3 */
+ EMU_SRC_DOCK_ADC1_RIGHT1, /* 4 */
+ EMU_SRC_DOCK_ADC2_LEFT1, /* 5 */
+ EMU_SRC_DOCK_ADC2_RIGHT1, /* 6 */
+ EMU_SRC_DOCK_ADC3_LEFT1, /* 7 */
+ EMU_SRC_DOCK_ADC3_RIGHT1, /* 8 */
+ EMU_SRC_HAMOA_ADC_LEFT1, /* 9 */
+ EMU_SRC_HAMOA_ADC_RIGHT1, /* 10 */
+ EMU_SRC_HANA_SPDIF_LEFT1, /* 11 */
+ EMU_SRC_HANA_SPDIF_RIGHT1, /* 12 */
+ EMU_SRC_HANA_ADAT, /* 13 */
+ EMU_SRC_HANA_ADAT+1, /* 14 */
+ EMU_SRC_HANA_ADAT+2, /* 15 */
+ EMU_SRC_HANA_ADAT+3, /* 16 */
+ EMU_SRC_HANA_ADAT+4, /* 17 */
+ EMU_SRC_HANA_ADAT+5, /* 18 */
+ EMU_SRC_HANA_ADAT+6, /* 19 */
+ EMU_SRC_HANA_ADAT+7, /* 20 */
+ EMU_SRC_ALICE_EMU32A, /* 21 */
+ EMU_SRC_ALICE_EMU32A+1, /* 22 */
+ EMU_SRC_ALICE_EMU32A+2, /* 23 */
+ EMU_SRC_ALICE_EMU32A+3, /* 24 */
+ EMU_SRC_ALICE_EMU32A+4, /* 25 */
+ EMU_SRC_ALICE_EMU32A+5, /* 26 */
+ EMU_SRC_ALICE_EMU32A+6, /* 27 */
+ EMU_SRC_ALICE_EMU32A+7, /* 28 */
+ EMU_SRC_ALICE_EMU32A+8, /* 29 */
+ EMU_SRC_ALICE_EMU32A+9, /* 30 */
+ EMU_SRC_ALICE_EMU32A+0xa, /* 31 */
+ EMU_SRC_ALICE_EMU32A+0xb, /* 32 */
+ EMU_SRC_ALICE_EMU32A+0xc, /* 33 */
+ EMU_SRC_ALICE_EMU32A+0xd, /* 34 */
+ EMU_SRC_ALICE_EMU32A+0xe, /* 35 */
+ EMU_SRC_ALICE_EMU32A+0xf, /* 36 */
+ EMU_SRC_ALICE_EMU32B, /* 37 */
+ EMU_SRC_ALICE_EMU32B+1, /* 38 */
+ EMU_SRC_ALICE_EMU32B+2, /* 39 */
+ EMU_SRC_ALICE_EMU32B+3, /* 40 */
+ EMU_SRC_ALICE_EMU32B+4, /* 41 */
+ EMU_SRC_ALICE_EMU32B+5, /* 42 */
+ EMU_SRC_ALICE_EMU32B+6, /* 43 */
+ EMU_SRC_ALICE_EMU32B+7, /* 44 */
+ EMU_SRC_ALICE_EMU32B+8, /* 45 */
+ EMU_SRC_ALICE_EMU32B+9, /* 46 */
+ EMU_SRC_ALICE_EMU32B+0xa, /* 47 */
+ EMU_SRC_ALICE_EMU32B+0xb, /* 48 */
+ EMU_SRC_ALICE_EMU32B+0xc, /* 49 */
+ EMU_SRC_ALICE_EMU32B+0xd, /* 50 */
+ EMU_SRC_ALICE_EMU32B+0xe, /* 51 */
+ EMU_SRC_ALICE_EMU32B+0xf, /* 52 */
+};
+
+static unsigned int emu1010_output_dst[] = {
+ EMU_DST_DOCK_DAC1_LEFT1, /* 0 */
+ EMU_DST_DOCK_DAC1_RIGHT1, /* 1 */
+ EMU_DST_DOCK_DAC2_LEFT1, /* 2 */
+ EMU_DST_DOCK_DAC2_RIGHT1, /* 3 */
+ EMU_DST_DOCK_DAC3_LEFT1, /* 4 */
+ EMU_DST_DOCK_DAC3_RIGHT1, /* 5 */
+ EMU_DST_DOCK_DAC4_LEFT1, /* 6 */
+ EMU_DST_DOCK_DAC4_RIGHT1, /* 7 */
+ EMU_DST_DOCK_PHONES_LEFT1, /* 8 */
+ EMU_DST_DOCK_PHONES_RIGHT1, /* 9 */
+ EMU_DST_DOCK_SPDIF_LEFT1, /* 10 */
+ EMU_DST_DOCK_SPDIF_RIGHT1, /* 11 */
+ EMU_DST_HANA_SPDIF_LEFT1, /* 12 */
+ EMU_DST_HANA_SPDIF_RIGHT1, /* 13 */
+ EMU_DST_HAMOA_DAC_LEFT1, /* 14 */
+ EMU_DST_HAMOA_DAC_RIGHT1, /* 15 */
+ EMU_DST_HANA_ADAT, /* 16 */
+ EMU_DST_HANA_ADAT+1, /* 17 */
+ EMU_DST_HANA_ADAT+2, /* 18 */
+ EMU_DST_HANA_ADAT+3, /* 19 */
+ EMU_DST_HANA_ADAT+4, /* 20 */
+ EMU_DST_HANA_ADAT+5, /* 21 */
+ EMU_DST_HANA_ADAT+6, /* 22 */
+ EMU_DST_HANA_ADAT+7, /* 23 */
+};
+
+static unsigned int emu1010_input_dst[] = {
+ EMU_DST_ALICE2_EMU32_0,
+ EMU_DST_ALICE2_EMU32_1,
+ EMU_DST_ALICE2_EMU32_2,
+ EMU_DST_ALICE2_EMU32_3,
+ EMU_DST_ALICE2_EMU32_4,
+ EMU_DST_ALICE2_EMU32_5,
+ EMU_DST_ALICE2_EMU32_6,
+ EMU_DST_ALICE2_EMU32_7,
+ EMU_DST_ALICE2_EMU32_8,
+ EMU_DST_ALICE2_EMU32_9,
+ EMU_DST_ALICE2_EMU32_A,
+ EMU_DST_ALICE2_EMU32_B,
+ EMU_DST_ALICE2_EMU32_C,
+ EMU_DST_ALICE2_EMU32_D,
+ EMU_DST_ALICE2_EMU32_E,
+ EMU_DST_ALICE2_EMU32_F,
+ EMU_DST_ALICE_I2S0_LEFT,
+ EMU_DST_ALICE_I2S0_RIGHT,
+ EMU_DST_ALICE_I2S1_LEFT,
+ EMU_DST_ALICE_I2S1_RIGHT,
+ EMU_DST_ALICE_I2S2_LEFT,
+ EMU_DST_ALICE_I2S2_RIGHT,
+};
+
+static int snd_emu1010_input_output_source_info(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *uinfo)
+{
+ uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED;
+ uinfo->count = 1;
+ uinfo->value.enumerated.items = 53;
+ if (uinfo->value.enumerated.item >= uinfo->value.enumerated.items)
+ uinfo->value.enumerated.item = uinfo->value.enumerated.items - 1;
+ strcpy(uinfo->value.enumerated.name, emu1010_src_texts[uinfo->value.enumerated.item]);
+ return 0;
+}
+
+static int snd_emu1010_output_source_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_emu10k1 *emu = snd_kcontrol_chip(kcontrol);
+ int channel;
+
+ channel = (kcontrol->private_value) & 0xff;
+ ucontrol->value.enumerated.item[0] = emu->emu1010.output_source[channel];
+ return 0;
+}
+
+static int snd_emu1010_output_source_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_emu10k1 *emu = snd_kcontrol_chip(kcontrol);
+ int change = 0;
+ unsigned int val;
+ int channel;
+
+ channel = (kcontrol->private_value) & 0xff;
+ if (emu->emu1010.output_source[channel] != ucontrol->value.enumerated.item[0]) {
+ val = emu->emu1010.output_source[channel] = ucontrol->value.enumerated.item[0];
+ change = 1;
+ snd_emu1010_fpga_link_dst_src_write(emu,
+ emu1010_output_dst[channel], emu1010_src_regs[val]);
+ }
+ return change;
+}
+
+static int snd_emu1010_input_source_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_emu10k1 *emu = snd_kcontrol_chip(kcontrol);
+ int channel;
+
+ channel = (kcontrol->private_value) & 0xff;
+ ucontrol->value.enumerated.item[0] = emu->emu1010.input_source[channel];
+ return 0;
+}
+
+static int snd_emu1010_input_source_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_emu10k1 *emu = snd_kcontrol_chip(kcontrol);
+ int change = 0;
+ unsigned int val;
+ int channel;
+
+ channel = (kcontrol->private_value) & 0xff;
+ if (emu->emu1010.input_source[channel] != ucontrol->value.enumerated.item[0]) {
+ val = emu->emu1010.input_source[channel] = ucontrol->value.enumerated.item[0];
+ change = 1;
+ snd_emu1010_fpga_link_dst_src_write(emu,
+ emu1010_input_dst[channel], emu1010_src_regs[val]);
+ }
+ return change;
+}
+
+#define EMU1010_SOURCE_OUTPUT(xname,chid) \
+{ \
+ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, \
+ .access = SNDRV_CTL_ELEM_ACCESS_READWRITE, \
+ .info = snd_emu1010_input_output_source_info, \
+ .get = snd_emu1010_output_source_get, \
+ .put = snd_emu1010_output_source_put, \
+ .private_value = chid \
+}
+
+static struct snd_kcontrol_new snd_emu1010_output_enum_ctls[] __devinitdata = {
+ EMU1010_SOURCE_OUTPUT("Dock DAC1 Left Playback Enum", 0),
+ EMU1010_SOURCE_OUTPUT("Dock DAC1 Right Playback Enum", 1),
+ EMU1010_SOURCE_OUTPUT("Dock DAC2 Left Playback Enum", 2),
+ EMU1010_SOURCE_OUTPUT("Dock DAC2 Right Playback Enum", 3),
+ EMU1010_SOURCE_OUTPUT("Dock DAC3 Left Playback Enum", 4),
+ EMU1010_SOURCE_OUTPUT("Dock DAC3 Right Playback Enum", 5),
+ EMU1010_SOURCE_OUTPUT("Dock DAC4 Left Playback Enum", 6),
+ EMU1010_SOURCE_OUTPUT("Dock DAC4 Right Playback Enum", 7),
+ EMU1010_SOURCE_OUTPUT("Dock Phones Left Playback Enum", 8),
+ EMU1010_SOURCE_OUTPUT("Dock Phones Right Playback Enum", 9),
+ EMU1010_SOURCE_OUTPUT("Dock SPDIF Left Playback Enum", 0xa),
+ EMU1010_SOURCE_OUTPUT("Dock SPDIF Right Playback Enum", 0xb),
+ EMU1010_SOURCE_OUTPUT("1010 SPDIF Left Playback Enum", 0xc),
+ EMU1010_SOURCE_OUTPUT("1010 SPDIF Right Playback Enum", 0xd),
+ EMU1010_SOURCE_OUTPUT("0202 DAC Left Playback Enum", 0xe),
+ EMU1010_SOURCE_OUTPUT("0202 DAC Right Playback Enum", 0xf),
+ EMU1010_SOURCE_OUTPUT("1010 ADAT 0 Playback Enum", 0x10),
+ EMU1010_SOURCE_OUTPUT("1010 ADAT 1 Playback Enum", 0x11),
+ EMU1010_SOURCE_OUTPUT("1010 ADAT 2 Playback Enum", 0x12),
+ EMU1010_SOURCE_OUTPUT("1010 ADAT 3 Playback Enum", 0x13),
+ EMU1010_SOURCE_OUTPUT("1010 ADAT 4 Playback Enum", 0x14),
+ EMU1010_SOURCE_OUTPUT("1010 ADAT 5 Playback Enum", 0x15),
+ EMU1010_SOURCE_OUTPUT("1010 ADAT 6 Playback Enum", 0x16),
+ EMU1010_SOURCE_OUTPUT("1010 ADAT 7 Playback Enum", 0x17),
+};
+
+#define EMU1010_SOURCE_INPUT(xname,chid) \
+{ \
+ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, \
+ .access = SNDRV_CTL_ELEM_ACCESS_READWRITE, \
+ .info = snd_emu1010_input_output_source_info, \
+ .get = snd_emu1010_input_source_get, \
+ .put = snd_emu1010_input_source_put, \
+ .private_value = chid \
+}
+
+static struct snd_kcontrol_new snd_emu1010_input_enum_ctls[] __devinitdata = {
+ EMU1010_SOURCE_INPUT("DSP 0 Capture Enum", 0),
+ EMU1010_SOURCE_INPUT("DSP 1 Capture Enum", 1),
+ EMU1010_SOURCE_INPUT("DSP 2 Capture Enum", 2),
+ EMU1010_SOURCE_INPUT("DSP 3 Capture Enum", 3),
+ EMU1010_SOURCE_INPUT("DSP 4 Capture Enum", 4),
+ EMU1010_SOURCE_INPUT("DSP 5 Capture Enum", 5),
+ EMU1010_SOURCE_INPUT("DSP 6 Capture Enum", 6),
+ EMU1010_SOURCE_INPUT("DSP 7 Capture Enum", 7),
+ EMU1010_SOURCE_INPUT("DSP 8 Capture Enum", 8),
+ EMU1010_SOURCE_INPUT("DSP 9 Capture Enum", 9),
+ EMU1010_SOURCE_INPUT("DSP A Capture Enum", 0xa),
+ EMU1010_SOURCE_INPUT("DSP B Capture Enum", 0xb),
+ EMU1010_SOURCE_INPUT("DSP C Capture Enum", 0xc),
+ EMU1010_SOURCE_INPUT("DSP D Capture Enum", 0xd),
+ EMU1010_SOURCE_INPUT("DSP E Capture Enum", 0xe),
+ EMU1010_SOURCE_INPUT("DSP F Capture Enum", 0xf),
+ EMU1010_SOURCE_INPUT("DSP 10 Capture Enum", 0x10),
+ EMU1010_SOURCE_INPUT("DSP 11 Capture Enum", 0x11),
+ EMU1010_SOURCE_INPUT("DSP 12 Capture Enum", 0x12),
+ EMU1010_SOURCE_INPUT("DSP 13 Capture Enum", 0x13),
+ EMU1010_SOURCE_INPUT("DSP 14 Capture Enum", 0x14),
+ EMU1010_SOURCE_INPUT("DSP 15 Capture Enum", 0x15),
+};
+
+
+
+
+static int snd_emu1010_adc_pads_info(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *uinfo)
+{
+ uinfo->type = SNDRV_CTL_ELEM_TYPE_BOOLEAN;
+ uinfo->count = 1;
+ uinfo->value.integer.min = 0;
+ uinfo->value.integer.max = 1;
+ return 0;
+}
+
+static int snd_emu1010_adc_pads_get(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_emu10k1 *emu = snd_kcontrol_chip(kcontrol);
+ unsigned int mask = kcontrol->private_value & 0xff;
+ ucontrol->value.integer.value[0] = (emu->emu1010.adc_pads & mask) ? 1 : 0;
+ return 0;
+}
+
+static int snd_emu1010_adc_pads_put(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_emu10k1 *emu = snd_kcontrol_chip(kcontrol);
+ unsigned int mask = kcontrol->private_value & 0xff;
+ unsigned int val, cache;
+ val = ucontrol->value.integer.value[0];
+ cache = emu->emu1010.adc_pads;
+ if (val == 1)
+ cache = cache | mask;
+ else
+ cache = cache & ~mask;
+ if (cache != emu->emu1010.adc_pads) {
+ snd_emu1010_fpga_write(emu, EMU_HANA_ADC_PADS, cache );
+ emu->emu1010.adc_pads = cache;
+ }
+
+ return 0;
+}
+
+
+
+#define EMU1010_ADC_PADS(xname,chid) \
+{ \
+ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, \
+ .access = SNDRV_CTL_ELEM_ACCESS_READWRITE, \
+ .info = snd_emu1010_adc_pads_info, \
+ .get = snd_emu1010_adc_pads_get, \
+ .put = snd_emu1010_adc_pads_put, \
+ .private_value = chid \
+}
+
+static struct snd_kcontrol_new snd_emu1010_adc_pads[] __devinitdata = {
+ EMU1010_ADC_PADS("ADC1 14dB PAD Audio Dock Capture Switch", EMU_HANA_DOCK_ADC_PAD1),
+ EMU1010_ADC_PADS("ADC2 14dB PAD Audio Dock Capture Switch", EMU_HANA_DOCK_ADC_PAD2),
+ EMU1010_ADC_PADS("ADC3 14dB PAD Audio Dock Capture Switch", EMU_HANA_DOCK_ADC_PAD3),
+ EMU1010_ADC_PADS("ADC1 14dB PAD 0202 Capture Switch", EMU_HANA_0202_ADC_PAD1),
+};
+
+static int snd_emu1010_dac_pads_info(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *uinfo)
+{
+ uinfo->type = SNDRV_CTL_ELEM_TYPE_BOOLEAN;
+ uinfo->count = 1;
+ uinfo->value.integer.min = 0;
+ uinfo->value.integer.max = 1;
+ return 0;
+}
+
+static int snd_emu1010_dac_pads_get(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_emu10k1 *emu = snd_kcontrol_chip(kcontrol);
+ unsigned int mask = kcontrol->private_value & 0xff;
+ ucontrol->value.integer.value[0] = (emu->emu1010.dac_pads & mask) ? 1 : 0;
+ return 0;
+}
+
+static int snd_emu1010_dac_pads_put(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_emu10k1 *emu = snd_kcontrol_chip(kcontrol);
+ unsigned int mask = kcontrol->private_value & 0xff;
+ unsigned int val, cache;
+ val = ucontrol->value.integer.value[0];
+ cache = emu->emu1010.dac_pads;
+ if (val == 1)
+ cache = cache | mask;
+ else
+ cache = cache & ~mask;
+ if (cache != emu->emu1010.dac_pads) {
+ snd_emu1010_fpga_write(emu, EMU_HANA_DAC_PADS, cache );
+ emu->emu1010.dac_pads = cache;
+ }
+
+ return 0;
+}
+
+
+
+#define EMU1010_DAC_PADS(xname,chid) \
+{ \
+ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, \
+ .access = SNDRV_CTL_ELEM_ACCESS_READWRITE, \
+ .info = snd_emu1010_dac_pads_info, \
+ .get = snd_emu1010_dac_pads_get, \
+ .put = snd_emu1010_dac_pads_put, \
+ .private_value = chid \
+}
+
+static struct snd_kcontrol_new snd_emu1010_dac_pads[] __devinitdata = {
+ EMU1010_DAC_PADS("DAC1 Audio Dock 14dB PAD Playback Switch", EMU_HANA_DOCK_DAC_PAD1),
+ EMU1010_DAC_PADS("DAC2 Audio Dock 14dB PAD Playback Switch", EMU_HANA_DOCK_DAC_PAD2),
+ EMU1010_DAC_PADS("DAC3 Audio Dock 14dB PAD Playback Switch", EMU_HANA_DOCK_DAC_PAD3),
+ EMU1010_DAC_PADS("DAC4 Audio Dock 14dB PAD Playback Switch", EMU_HANA_DOCK_DAC_PAD4),
+ EMU1010_DAC_PADS("DAC1 0202 14dB PAD Playback Switch", EMU_HANA_0202_DAC_PAD1),
+};
+
+
+static int snd_emu1010_internal_clock_info(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_info *uinfo)
+{
+ static char *texts[2] = {
+ "44100", "48000"
+ };
+
+ uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED;
+ uinfo->count = 1;
+ uinfo->value.enumerated.items = 2;
+ if (uinfo->value.enumerated.item > 1)
+ uinfo->value.enumerated.item = 1;
+ strcpy(uinfo->value.enumerated.name, texts[uinfo->value.enumerated.item]);
+ return 0;
+}
+
+static int snd_emu1010_internal_clock_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_emu10k1 *emu = snd_kcontrol_chip(kcontrol);
+
+ ucontrol->value.enumerated.item[0] = emu->emu1010.internal_clock;
+ return 0;
+}
+
+static int snd_emu1010_internal_clock_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_emu10k1 *emu = snd_kcontrol_chip(kcontrol);
+ unsigned int val;
+ int change = 0;
+
+ val = ucontrol->value.enumerated.item[0] ;
+ change = (emu->emu1010.internal_clock != val);
+ if (change) {
+ emu->emu1010.internal_clock = val;
+ switch (val) {
+ case 0:
+ /* 44100 */
+ /* Mute all */
+ snd_emu1010_fpga_write(emu, EMU_HANA_UNMUTE, EMU_MUTE );
+ /* Default fallback clock 48kHz */
+ snd_emu1010_fpga_write(emu, EMU_HANA_DEFCLOCK, EMU_HANA_DEFCLOCK_44_1K );
+ /* Word Clock source, Internal 44.1kHz x1 */
+ snd_emu1010_fpga_write(emu, EMU_HANA_WCLOCK,
+ EMU_HANA_WCLOCK_INT_44_1K | EMU_HANA_WCLOCK_1X );
+ /* Set LEDs on Audio Dock */
+ snd_emu1010_fpga_write(emu, EMU_HANA_DOCK_LEDS_2,
+ EMU_HANA_DOCK_LEDS_2_44K | EMU_HANA_DOCK_LEDS_2_LOCK );
+ /* Allow DLL to settle */
+ msleep(10);
+ /* Unmute all */
+ snd_emu1010_fpga_write(emu, EMU_HANA_UNMUTE, EMU_UNMUTE );
+ break;
+ case 1:
+ /* 48000 */
+ /* Mute all */
+ snd_emu1010_fpga_write(emu, EMU_HANA_UNMUTE, EMU_MUTE );
+ /* Default fallback clock 48kHz */
+ snd_emu1010_fpga_write(emu, EMU_HANA_DEFCLOCK, EMU_HANA_DEFCLOCK_48K );
+ /* Word Clock source, Internal 48kHz x1 */
+ snd_emu1010_fpga_write(emu, EMU_HANA_WCLOCK,
+ EMU_HANA_WCLOCK_INT_48K | EMU_HANA_WCLOCK_1X );
+ /* Set LEDs on Audio Dock */
+ snd_emu1010_fpga_write(emu, EMU_HANA_DOCK_LEDS_2,
+ EMU_HANA_DOCK_LEDS_2_48K | EMU_HANA_DOCK_LEDS_2_LOCK );
+ /* Allow DLL to settle */
+ msleep(10);
+ /* Unmute all */
+ snd_emu1010_fpga_write(emu, EMU_HANA_UNMUTE, EMU_UNMUTE );
+ break;
+ }
+ }
+ return change;
+}
+
+static struct snd_kcontrol_new snd_emu1010_internal_clock =
+{
+ .access = SNDRV_CTL_ELEM_ACCESS_READWRITE,
+ .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+ .name = "Clock Internal Rate",
+ .count = 1,
+ .info = snd_emu1010_internal_clock_info,
+ .get = snd_emu1010_internal_clock_get,
+ .put = snd_emu1010_internal_clock_put
+};
+
+static int snd_audigy_i2c_capture_source_info(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_info *uinfo)
+{
+#if 0
+ static char *texts[4] = {
+ "Unknown1", "Unknown2", "Mic", "Line"
+ };
+#endif
+ static char *texts[2] = {
+ "Mic", "Line"
+ };
+
+ uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED;
+ uinfo->count = 1;
+ uinfo->value.enumerated.items = 2;
+ if (uinfo->value.enumerated.item > 1)
+ uinfo->value.enumerated.item = 1;
+ strcpy(uinfo->value.enumerated.name, texts[uinfo->value.enumerated.item]);
+ return 0;
+}
+
+static int snd_audigy_i2c_capture_source_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_emu10k1 *emu = snd_kcontrol_chip(kcontrol);
+
+ ucontrol->value.enumerated.item[0] = emu->i2c_capture_source;
+ return 0;
+}
+
+static int snd_audigy_i2c_capture_source_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_emu10k1 *emu = snd_kcontrol_chip(kcontrol);
+ unsigned int source_id;
+ unsigned int ngain, ogain;
+ u32 gpio;
+ int change = 0;
+ unsigned long flags;
+ u32 source;
+ /* If the capture source has changed,
+ * update the capture volume from the cached value
+ * for the particular source.
+ */
+ source_id = ucontrol->value.enumerated.item[0]; /* Use 2 and 3 */
+ change = (emu->i2c_capture_source != source_id);
+ if (change) {
+ snd_emu10k1_i2c_write(emu, ADC_MUX, 0); /* Mute input */
+ spin_lock_irqsave(&emu->emu_lock, flags);
+ gpio = inl(emu->port + A_IOCFG);
+ if (source_id==0)
+ outl(gpio | 0x4, emu->port + A_IOCFG);
+ else
+ outl(gpio & ~0x4, emu->port + A_IOCFG);
+ spin_unlock_irqrestore(&emu->emu_lock, flags);
+
+ ngain = emu->i2c_capture_volume[source_id][0]; /* Left */
+ ogain = emu->i2c_capture_volume[emu->i2c_capture_source][0]; /* Left */
+ if (ngain != ogain)
+ snd_emu10k1_i2c_write(emu, ADC_ATTEN_ADCL, ((ngain) & 0xff));
+ ngain = emu->i2c_capture_volume[source_id][1]; /* Right */
+ ogain = emu->i2c_capture_volume[emu->i2c_capture_source][1]; /* Right */
+ if (ngain != ogain)
+ snd_emu10k1_i2c_write(emu, ADC_ATTEN_ADCR, ((ngain) & 0xff));
+
+ source = 1 << (source_id + 2);
+ snd_emu10k1_i2c_write(emu, ADC_MUX, source); /* Set source */
+ emu->i2c_capture_source = source_id;
+ }
+ return change;
+}
+
+static struct snd_kcontrol_new snd_audigy_i2c_capture_source =
+{
+ .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+ .name = "Capture Source",
+ .info = snd_audigy_i2c_capture_source_info,
+ .get = snd_audigy_i2c_capture_source_get,
+ .put = snd_audigy_i2c_capture_source_put
+};
+
+static int snd_audigy_i2c_volume_info(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_info *uinfo)
+{
+ uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
+ uinfo->count = 2;
+ uinfo->value.integer.min = 0;
+ uinfo->value.integer.max = 255;
+ return 0;
+}
+
+static int snd_audigy_i2c_volume_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_emu10k1 *emu = snd_kcontrol_chip(kcontrol);
+ int source_id;
+
+ source_id = kcontrol->private_value;
+
+ ucontrol->value.integer.value[0] = emu->i2c_capture_volume[source_id][0];
+ ucontrol->value.integer.value[1] = emu->i2c_capture_volume[source_id][1];
+ return 0;
+}
+
+static int snd_audigy_i2c_volume_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_emu10k1 *emu = snd_kcontrol_chip(kcontrol);
+ unsigned int ogain;
+ unsigned int ngain;
+ int source_id;
+ int change = 0;
+
+ source_id = kcontrol->private_value;
+ ogain = emu->i2c_capture_volume[source_id][0]; /* Left */
+ ngain = ucontrol->value.integer.value[0];
+ if (ngain > 0xff)
+ return 0;
+ if (ogain != ngain) {
+ if (emu->i2c_capture_source == source_id)
+ snd_emu10k1_i2c_write(emu, ADC_ATTEN_ADCL, ((ngain) & 0xff) );
+ emu->i2c_capture_volume[source_id][0] = ucontrol->value.integer.value[0];
+ change = 1;
+ }
+ ogain = emu->i2c_capture_volume[source_id][1]; /* Right */
+ ngain = ucontrol->value.integer.value[1];
+ if (ngain > 0xff)
+ return 0;
+ if (ogain != ngain) {
+ if (emu->i2c_capture_source == source_id)
+ snd_emu10k1_i2c_write(emu, ADC_ATTEN_ADCR, ((ngain) & 0xff));
+ emu->i2c_capture_volume[source_id][1] = ucontrol->value.integer.value[1];
+ change = 1;
+ }
+
+ return change;
+}
+
+#define I2C_VOLUME(xname,chid) \
+{ \
+ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, \
+ .access = SNDRV_CTL_ELEM_ACCESS_READWRITE | \
+ SNDRV_CTL_ELEM_ACCESS_TLV_READ, \
+ .info = snd_audigy_i2c_volume_info, \
+ .get = snd_audigy_i2c_volume_get, \
+ .put = snd_audigy_i2c_volume_put, \
+ .tlv = { .p = snd_audigy_db_scale2 }, \
+ .private_value = chid \
+}
+
+
+static struct snd_kcontrol_new snd_audigy_i2c_volume_ctls[] __devinitdata = {
+ I2C_VOLUME("Mic Capture Volume", 0),
+ I2C_VOLUME("Line Capture Volume", 0)
+};
+
#if 0
static int snd_audigy_spdif_output_rate_info(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *uinfo)
{
@@ -668,7 +1340,9 @@ static int snd_emu10k1_shared_spdif_put(struct snd_kcontrol *kcontrol,
int change = 0;
spin_lock_irqsave(&emu->reg_lock, flags);
- if (emu->audigy) {
+ if ( emu->card_capabilities->i2c_adc) {
+ /* Do nothing for Audigy 2 ZS Notebook */
+ } else if (emu->audigy) {
reg = inl(emu->port + A_IOCFG);
val = ucontrol->value.integer.value[0] ? A_IOCFG_GPOUT0 : 0;
change = (reg & A_IOCFG_GPOUT0) != val;
@@ -806,6 +1480,24 @@ int __devinit snd_emu10k1_mixer(struct snd_emu10k1 *emu,
"AMic Playback Volume", "Mic Playback Volume",
NULL
};
+ static char *audigy_rename_ctls_i2c_adc[] = {
+ //"Analog Mix Capture Volume","OLD Analog Mix Capture Volume",
+ "Line Capture Volume", "Analog Mix Capture Volume",
+ "Wave Playback Volume", "OLD PCM Playback Volume",
+ "Wave Master Playback Volume", "Master Playback Volume",
+ "AMic Playback Volume", "Old Mic Playback Volume",
+ "CD Capture Volume", "IEC958 Optical Capture Volume",
+ NULL
+ };
+ static char *audigy_remove_ctls_i2c_adc[] = {
+ /* On the Audigy2 ZS Notebook
+ * Capture via WM8775 */
+ "Mic Capture Volume",
+ "Analog Mix Capture Volume",
+ "Aux Capture Volume",
+ "IEC958 Optical Capture Volume",
+ NULL
+ };
static char *audigy_remove_ctls_1361t_adc[] = {
/* On the Audigy2 the AC97 playback is piped into
* the Philips ADC for 24bit capture */
@@ -890,6 +1582,7 @@ int __devinit snd_emu10k1_mixer(struct snd_emu10k1 *emu,
if (emu->ac97->id == AC97_ID_STAC9758) {
emu->rear_ac97 = 1;
snd_emu10k1_ptr_write(emu, AC97SLOT, 0, AC97SLOT_CNTR|AC97SLOT_LFE|AC97SLOT_REAR_LEFT|AC97SLOT_REAR_RIGHT);
+ snd_ac97_write_cache(emu->ac97, AC97_HEADPHONE, 0x0202);
}
/* remove unused AC97 controls */
snd_ac97_write_cache(emu->ac97, AC97_SURROUND_MASTER, 0x0202);
@@ -898,6 +1591,10 @@ int __devinit snd_emu10k1_mixer(struct snd_emu10k1 *emu,
}
for (; *c; c++)
remove_ctl(card, *c);
+ } else if (emu->card_capabilities->i2c_adc) {
+ c = audigy_remove_ctls_i2c_adc;
+ for (; *c; c++)
+ remove_ctl(card, *c);
} else {
no_ac97:
if (emu->card_capabilities->ecard)
@@ -911,6 +1608,8 @@ int __devinit snd_emu10k1_mixer(struct snd_emu10k1 *emu,
if (emu->audigy)
if (emu->card_capabilities->adc_1361t)
c = audigy_rename_ctls_1361t_adc;
+ else if (emu->card_capabilities->i2c_adc)
+ c = audigy_rename_ctls_i2c_adc;
else
c = audigy_rename_ctls;
else
@@ -1021,7 +1720,7 @@ int __devinit snd_emu10k1_mixer(struct snd_emu10k1 *emu,
return err;
}
- if ( emu->card_capabilities->emu1212m) {
+ if ( emu->card_capabilities->emu1010) {
; /* Disable the snd_audigy_spdif_shared_spdif */
} else if (emu->audigy) {
if ((kctl = snd_ctl_new1(&snd_audigy_shared_spdif, emu)) == NULL)
@@ -1045,6 +1744,48 @@ int __devinit snd_emu10k1_mixer(struct snd_emu10k1 *emu,
if ((err = snd_p16v_mixer(emu)))
return err;
}
+
+ if ( emu->card_capabilities->emu1010) {
+ int i;
+
+ for (i = 0; i < ARRAY_SIZE(snd_emu1010_output_enum_ctls); i++) {
+ err = snd_ctl_add(card, snd_ctl_new1(&snd_emu1010_output_enum_ctls[i], emu));
+ if (err < 0)
+ return err;
+ }
+ for (i = 0; i < ARRAY_SIZE(snd_emu1010_input_enum_ctls); i++) {
+ err = snd_ctl_add(card, snd_ctl_new1(&snd_emu1010_input_enum_ctls[i], emu));
+ if (err < 0)
+ return err;
+ }
+ for (i = 0; i < ARRAY_SIZE(snd_emu1010_adc_pads); i++) {
+ err = snd_ctl_add(card, snd_ctl_new1(&snd_emu1010_adc_pads[i], emu));
+ if (err < 0)
+ return err;
+ }
+ for (i = 0; i < ARRAY_SIZE(snd_emu1010_dac_pads); i++) {
+ err = snd_ctl_add(card, snd_ctl_new1(&snd_emu1010_dac_pads[i], emu));
+ if (err < 0)
+ return err;
+ }
+ err = snd_ctl_add(card, snd_ctl_new1(&snd_emu1010_internal_clock, emu));
+ if (err < 0)
+ return err;
+ }
+
+ if ( emu->card_capabilities->i2c_adc) {
+ int i;
+
+ err = snd_ctl_add(card, snd_ctl_new1(&snd_audigy_i2c_capture_source, emu));
+ if (err < 0)
+ return err;
+
+ for (i = 0; i < ARRAY_SIZE(snd_audigy_i2c_volume_ctls); i++) {
+ err = snd_ctl_add(card, snd_ctl_new1(&snd_audigy_i2c_volume_ctls[i], emu));
+ if (err < 0)
+ return err;
+ }
+ }
return 0;
}
diff --git a/sound/pci/emu10k1/emupcm.c b/sound/pci/emu10k1/emupcm.c
index 717e92ec9e0a..ab4f5df5241b 100644
--- a/sound/pci/emu10k1/emupcm.c
+++ b/sound/pci/emu10k1/emupcm.c
@@ -147,7 +147,7 @@ static int snd_emu10k1_pcm_channel_alloc(struct snd_emu10k1_pcm * epcm, int voic
1,
&epcm->extra);
if (err < 0) {
- // printk("pcm_channel_alloc: failed extra: voices=%d, frame=%d\n", voices, frame);
+ /* printk("pcm_channel_alloc: failed extra: voices=%d, frame=%d\n", voices, frame); */
for (i = 0; i < voices; i++) {
snd_emu10k1_voice_free(epcm->emu, epcm->voices[i]);
epcm->voices[i] = NULL;
@@ -339,7 +339,7 @@ static void snd_emu10k1_pcm_init_voice(struct snd_emu10k1 *emu,
}
}
- // setup routing
+ /* setup routing */
if (emu->audigy) {
snd_emu10k1_ptr_write(emu, A_FXRT1, voice,
snd_emu10k1_compose_audigy_fxrt1(send_routing));
@@ -353,12 +353,15 @@ static void snd_emu10k1_pcm_init_voice(struct snd_emu10k1 *emu,
} else
snd_emu10k1_ptr_write(emu, FXRT, voice,
snd_emu10k1_compose_send_routing(send_routing));
- // Stop CA
- // Assumption that PT is already 0 so no harm overwriting
+ /* Stop CA */
+ /* Assumption that PT is already 0 so no harm overwriting */
snd_emu10k1_ptr_write(emu, PTRX, voice, (send_amount[0] << 8) | send_amount[1]);
snd_emu10k1_ptr_write(emu, DSL, voice, end_addr | (send_amount[3] << 24));
snd_emu10k1_ptr_write(emu, PSST, voice, start_addr | (send_amount[2] << 24));
- pitch_target = emu10k1_calc_pitch_target(runtime->rate);
+ if (emu->card_capabilities->emu1010)
+ pitch_target = PITCH_48000; /* Disable interpolators on emu1010 card */
+ else
+ pitch_target = emu10k1_calc_pitch_target(runtime->rate);
if (extra)
snd_emu10k1_ptr_write(emu, CCCA, voice, start_addr |
emu10k1_select_interprom(pitch_target) |
@@ -367,14 +370,14 @@ static void snd_emu10k1_pcm_init_voice(struct snd_emu10k1 *emu,
snd_emu10k1_ptr_write(emu, CCCA, voice, (start_addr + ccis) |
emu10k1_select_interprom(pitch_target) |
(w_16 ? 0 : CCCA_8BITSELECT));
- // Clear filter delay memory
+ /* Clear filter delay memory */
snd_emu10k1_ptr_write(emu, Z1, voice, 0);
snd_emu10k1_ptr_write(emu, Z2, voice, 0);
- // invalidate maps
+ /* invalidate maps */
silent_page = ((unsigned int)emu->silent_page.addr << 1) | MAP_PTI_MASK;
snd_emu10k1_ptr_write(emu, MAPA, voice, silent_page);
snd_emu10k1_ptr_write(emu, MAPB, voice, silent_page);
- // modulation envelope
+ /* modulation envelope */
snd_emu10k1_ptr_write(emu, CVCF, voice, 0xffff);
snd_emu10k1_ptr_write(emu, VTFT, voice, 0xffff);
snd_emu10k1_ptr_write(emu, ATKHLDM, voice, 0);
@@ -385,12 +388,12 @@ static void snd_emu10k1_pcm_init_voice(struct snd_emu10k1 *emu,
snd_emu10k1_ptr_write(emu, TREMFRQ, voice, 0);
snd_emu10k1_ptr_write(emu, FM2FRQ2, voice, 0);
snd_emu10k1_ptr_write(emu, ENVVAL, voice, 0x8000);
- // volume envelope
+ /* volume envelope */
snd_emu10k1_ptr_write(emu, ATKHLDV, voice, 0x7f7f);
snd_emu10k1_ptr_write(emu, ENVVOL, voice, 0x0000);
- // filter envelope
+ /* filter envelope */
snd_emu10k1_ptr_write(emu, PEFE_FILTERAMOUNT, voice, 0x7f);
- // pitch envelope
+ /* pitch envelope */
snd_emu10k1_ptr_write(emu, PEFE_PITCHAMOUNT, voice, 0);
spin_unlock_irqrestore(&emu->reg_lock, flags);
@@ -468,7 +471,7 @@ static int snd_emu10k1_efx_playback_hw_free(struct snd_pcm_substream *substream)
snd_emu10k1_voice_free(epcm->emu, epcm->extra);
epcm->extra = NULL;
}
- for (i=0; i < NUM_EFX_PLAYBACK; i++) {
+ for (i = 0; i < NUM_EFX_PLAYBACK; i++) {
if (epcm->voices[i]) {
snd_emu10k1_voice_free(epcm->emu, epcm->voices[i]);
epcm->voices[i] = NULL;
@@ -637,7 +640,7 @@ static void snd_emu10k1_playback_invalidate_cache(struct snd_emu10k1 *emu, int e
stereo = (!extra && runtime->channels == 2);
sample = snd_pcm_format_width(runtime->format) == 16 ? 0 : 0x80808080;
ccis = emu10k1_ccis(stereo, sample == 0);
- // set cs to 2 * number of cache registers beside the invalidated
+ /* set cs to 2 * number of cache registers beside the invalidated */
cs = (sample == 0) ? (32-ccis) : (64-ccis+1) >> 1;
if (cs > 16) cs = 16;
for (i = 0; i < cs; i++) {
@@ -646,14 +649,14 @@ static void snd_emu10k1_playback_invalidate_cache(struct snd_emu10k1 *emu, int e
snd_emu10k1_ptr_write(emu, CD0 + i, voice + 1, sample);
}
}
- // reset cache
+ /* reset cache */
snd_emu10k1_ptr_write(emu, CCR_CACHEINVALIDSIZE, voice, 0);
snd_emu10k1_ptr_write(emu, CCR_READADDRESS, voice, cra);
if (stereo) {
snd_emu10k1_ptr_write(emu, CCR_CACHEINVALIDSIZE, voice + 1, 0);
snd_emu10k1_ptr_write(emu, CCR_READADDRESS, voice + 1, cra);
}
- // fill cache
+ /* fill cache */
snd_emu10k1_ptr_write(emu, CCR_CACHEINVALIDSIZE, voice, ccis);
if (stereo) {
snd_emu10k1_ptr_write(emu, CCR_CACHEINVALIDSIZE, voice+1, ccis);
@@ -698,7 +701,10 @@ static void snd_emu10k1_playback_trigger_voice(struct snd_emu10k1 *emu, struct s
voice = evoice->number;
pitch = snd_emu10k1_rate_to_pitch(runtime->rate) >> 8;
- pitch_target = emu10k1_calc_pitch_target(runtime->rate);
+ if (emu->card_capabilities->emu1010)
+ pitch_target = PITCH_48000; /* Disable interpolators on emu1010 card */
+ else
+ pitch_target = emu10k1_calc_pitch_target(runtime->rate);
snd_emu10k1_ptr_write(emu, PTRX_PITCHTARGET, voice, pitch_target);
if (master || evoice->epcm->type == PLAYBACK_EFX)
snd_emu10k1_ptr_write(emu, CPF_CURRENTPITCH, voice, pitch_target);
@@ -732,7 +738,7 @@ static int snd_emu10k1_playback_trigger(struct snd_pcm_substream *substream,
struct snd_emu10k1_pcm_mixer *mix;
int result = 0;
- // printk("trigger - emu10k1 = 0x%x, cmd = %i, pointer = %i\n", (int)emu, cmd, substream->ops->pointer(substream));
+ /* printk("trigger - emu10k1 = 0x%x, cmd = %i, pointer = %i\n", (int)emu, cmd, substream->ops->pointer(substream)); */
spin_lock(&emu->reg_lock);
switch (cmd) {
case SNDRV_PCM_TRIGGER_START:
@@ -778,10 +784,10 @@ static int snd_emu10k1_capture_trigger(struct snd_pcm_substream *substream,
switch (cmd) {
case SNDRV_PCM_TRIGGER_START:
case SNDRV_PCM_TRIGGER_RESUME:
- // hmm this should cause full and half full interrupt to be raised?
+ /* hmm this should cause full and half full interrupt to be raised? */
outl(epcm->capture_ipr, emu->port + IPR);
snd_emu10k1_intr_enable(emu, epcm->capture_inte);
- // printk("adccr = 0x%x, adcbs = 0x%x\n", epcm->adccr, epcm->adcbs);
+ /* printk("adccr = 0x%x, adcbs = 0x%x\n", epcm->adccr, epcm->adcbs); */
switch (epcm->type) {
case CAPTURE_AC97ADC:
snd_emu10k1_ptr_write(emu, ADCCR, 0, epcm->capture_cr_val);
@@ -790,6 +796,7 @@ static int snd_emu10k1_capture_trigger(struct snd_pcm_substream *substream,
if (emu->audigy) {
snd_emu10k1_ptr_write(emu, A_FXWC1, 0, epcm->capture_cr_val);
snd_emu10k1_ptr_write(emu, A_FXWC2, 0, epcm->capture_cr_val2);
+ snd_printdd("cr_val=0x%x, cr_val2=0x%x\n", epcm->capture_cr_val, epcm->capture_cr_val2);
} else
snd_emu10k1_ptr_write(emu, FXWC, 0, epcm->capture_cr_val);
break;
@@ -851,7 +858,7 @@ static snd_pcm_uframes_t snd_emu10k1_playback_pointer(struct snd_pcm_substream *
ptr -= runtime->buffer_size;
}
#endif
- // printk("ptr = 0x%x, buffer_size = 0x%x, period_size = 0x%x\n", ptr, runtime->buffer_size, runtime->period_size);
+ /* printk("ptr = 0x%x, buffer_size = 0x%x, period_size = 0x%x\n", ptr, runtime->buffer_size, runtime->period_size); */
return ptr;
}
@@ -868,7 +875,7 @@ static int snd_emu10k1_efx_playback_trigger(struct snd_pcm_substream *substream,
spin_lock(&emu->reg_lock);
switch (cmd) {
case SNDRV_PCM_TRIGGER_START:
- // prepare voices
+ /* prepare voices */
for (i = 0; i < NUM_EFX_PLAYBACK; i++) {
snd_emu10k1_playback_invalidate_cache(emu, 0, epcm->voices[i]);
}
@@ -917,7 +924,7 @@ static snd_pcm_uframes_t snd_emu10k1_capture_pointer(struct snd_pcm_substream *s
if (!epcm->running)
return 0;
if (epcm->first_ptr) {
- udelay(50); // hack, it takes awhile until capture is started
+ udelay(50); /* hack, it takes awhile until capture is started */
epcm->first_ptr = 0;
}
ptr = snd_emu10k1_ptr_read(emu, epcm->capture_idx_reg, 0) & 0x0000ffff;
@@ -972,6 +979,28 @@ static struct snd_pcm_hardware snd_emu10k1_capture =
.fifo_size = 0,
};
+static struct snd_pcm_hardware snd_emu10k1_capture_efx =
+{
+ .info = (SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_INTERLEAVED |
+ SNDRV_PCM_INFO_BLOCK_TRANSFER |
+ SNDRV_PCM_INFO_RESUME |
+ SNDRV_PCM_INFO_MMAP_VALID),
+ .formats = SNDRV_PCM_FMTBIT_S16_LE,
+ .rates = SNDRV_PCM_RATE_44100 | SNDRV_PCM_RATE_48000 |
+ SNDRV_PCM_RATE_88200 | SNDRV_PCM_RATE_96000 |
+ SNDRV_PCM_RATE_176400 | SNDRV_PCM_RATE_192000,
+ .rate_min = 44100,
+ .rate_max = 192000,
+ .channels_min = 8,
+ .channels_max = 8,
+ .buffer_bytes_max = (64*1024),
+ .period_bytes_min = 384,
+ .period_bytes_max = (64*1024),
+ .periods_min = 2,
+ .periods_max = 2,
+ .fifo_size = 0,
+};
+
/*
*
*/
@@ -1016,7 +1045,7 @@ static int snd_emu10k1_efx_playback_close(struct snd_pcm_substream *substream)
struct snd_emu10k1_pcm_mixer *mix;
int i;
- for (i=0; i < NUM_EFX_PLAYBACK; i++) {
+ for (i = 0; i < NUM_EFX_PLAYBACK; i++) {
mix = &emu->efx_pcm_mixer[i];
mix->epcm = NULL;
snd_emu10k1_pcm_efx_mixer_notify(emu, i, 0);
@@ -1045,7 +1074,7 @@ static int snd_emu10k1_efx_playback_open(struct snd_pcm_substream *substream)
runtime->private_free = snd_emu10k1_pcm_free_substream;
runtime->hw = snd_emu10k1_efx_playback;
- for (i=0; i < NUM_EFX_PLAYBACK; i++) {
+ for (i = 0; i < NUM_EFX_PLAYBACK; i++) {
mix = &emu->efx_pcm_mixer[i];
mix->send_routing[0][0] = i;
memset(&mix->send_volume, 0, sizeof(mix->send_volume));
@@ -1199,15 +1228,69 @@ static int snd_emu10k1_capture_efx_open(struct snd_pcm_substream *substream)
epcm->capture_idx_reg = FXIDX;
substream->runtime->private_data = epcm;
substream->runtime->private_free = snd_emu10k1_pcm_free_substream;
- runtime->hw = snd_emu10k1_capture;
+ runtime->hw = snd_emu10k1_capture_efx;
runtime->hw.rates = SNDRV_PCM_RATE_48000;
runtime->hw.rate_min = runtime->hw.rate_max = 48000;
spin_lock_irq(&emu->reg_lock);
- runtime->hw.channels_min = runtime->hw.channels_max = 0;
- for (idx = 0; idx < nefx; idx++) {
- if (emu->efx_voices_mask[idx/32] & (1 << (idx%32))) {
- runtime->hw.channels_min++;
- runtime->hw.channels_max++;
+ if (emu->card_capabilities->emu1010) {
+ /* TODO
+ * SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S32_LE
+ * SNDRV_PCM_RATE_44100 | SNDRV_PCM_RATE_48000 |
+ * SNDRV_PCM_RATE_88200 | SNDRV_PCM_RATE_96000 |
+ * SNDRV_PCM_RATE_176400 | SNDRV_PCM_RATE_192000
+ * rate_min = 44100,
+ * rate_max = 192000,
+ * channels_min = 8,
+ * channels_max = 8,
+ * Need to add mixer control to fix sample rate
+ *
+ * There are 16 mono channels of 16bits each.
+ * 24bit Audio uses 2x channels over 16bit
+ * 96kHz uses 2x channels over 48kHz
+ * 192kHz uses 4x channels over 48kHz
+ * So, for 48kHz 24bit, one has 8 channels
+ * for 96kHz 24bit, one has 4 channels
+ * for 192kHz 24bit, one has 2 channels
+ */
+#if 1
+ switch (emu->emu1010.internal_clock) {
+ case 0:
+ /* For 44.1kHz */
+ runtime->hw.rates = SNDRV_PCM_RATE_44100;
+ runtime->hw.rate_min = runtime->hw.rate_max = 44100;
+ runtime->hw.channels_min = runtime->hw.channels_max = 8;
+ break;
+ case 1:
+ /* For 48kHz */
+ runtime->hw.rates = SNDRV_PCM_RATE_48000;
+ runtime->hw.rate_min = runtime->hw.rate_max = 48000;
+ runtime->hw.channels_min = runtime->hw.channels_max = 8;
+ break;
+ };
+#endif
+#if 0
+ /* For 96kHz */
+ runtime->hw.rates = SNDRV_PCM_RATE_96000;
+ runtime->hw.rate_min = runtime->hw.rate_max = 96000;
+ runtime->hw.channels_min = runtime->hw.channels_max = 4;
+#endif
+#if 0
+ /* For 192kHz */
+ runtime->hw.rates = SNDRV_PCM_RATE_192000;
+ runtime->hw.rate_min = runtime->hw.rate_max = 192000;
+ runtime->hw.channels_min = runtime->hw.channels_max = 2;
+#endif
+ runtime->hw.formats = SNDRV_PCM_FMTBIT_S32_LE;
+ /* efx_voices_mask[0] is expected to be zero
+ * efx_voices_mask[1] is expected to have 16bits set
+ */
+ } else {
+ runtime->hw.channels_min = runtime->hw.channels_max = 0;
+ for (idx = 0; idx < nefx; idx++) {
+ if (emu->efx_voices_mask[idx/32] & (1 << (idx%32))) {
+ runtime->hw.channels_min++;
+ runtime->hw.channels_max++;
+ }
}
}
epcm->capture_cr_val = emu->efx_voices_mask[0];
@@ -1460,7 +1543,7 @@ static void snd_emu10k1_fx8010_playback_tram_poke1(unsigned short *dst_left,
unsigned int count,
unsigned int tram_shift)
{
- // printk("tram_poke1: dst_left = 0x%p, dst_right = 0x%p, src = 0x%p, count = 0x%x\n", dst_left, dst_right, src, count);
+ /* printk("tram_poke1: dst_left = 0x%p, dst_right = 0x%p, src = 0x%p, count = 0x%x\n", dst_left, dst_right, src, count); */
if ((tram_shift & 1) == 0) {
while (count--) {
*dst_left-- = *src++;
@@ -1537,7 +1620,7 @@ static int snd_emu10k1_fx8010_playback_prepare(struct snd_pcm_substream *substre
struct snd_emu10k1_fx8010_pcm *pcm = &emu->fx8010.pcm[substream->number];
unsigned int i;
- // printk("prepare: etram_pages = 0x%p, dma_area = 0x%x, buffer_size = 0x%x (0x%x)\n", emu->fx8010.etram_pages, runtime->dma_area, runtime->buffer_size, runtime->buffer_size << 2);
+ /* printk("prepare: etram_pages = 0x%p, dma_area = 0x%x, buffer_size = 0x%x (0x%x)\n", emu->fx8010.etram_pages, runtime->dma_area, runtime->buffer_size, runtime->buffer_size << 2); */
memset(&pcm->pcm_rec, 0, sizeof(pcm->pcm_rec));
pcm->pcm_rec.hw_buffer_size = pcm->buffer_size * 2; /* byte size */
pcm->pcm_rec.sw_buffer_size = snd_pcm_lib_buffer_bytes(substream);
diff --git a/sound/pci/emu10k1/emuproc.c b/sound/pci/emu10k1/emuproc.c
index b939e03aaedf..2c1585991bc8 100644
--- a/sound/pci/emu10k1/emuproc.c
+++ b/sound/pci/emu10k1/emuproc.c
@@ -3,6 +3,9 @@
* Creative Labs, Inc.
* Routines for control of EMU10K1 chips / proc interface routines
*
+ * Copyright (c) by James Courtier-Dutton <James@superbug.co.uk>
+ * Added EMU 1010 support.
+ *
* BUGS:
* --
*
@@ -255,7 +258,7 @@ static void snd_emu10k1_proc_rates_read(struct snd_info_entry *entry,
unsigned int val, tmp, n;
val = snd_emu10k1_ptr20_read(emu, CAPTURE_RATE_STATUS, 0);
tmp = (val >> 16) & 0x8;
- for (n=0;n<4;n++) {
+ for (n = 0; n < 4; n++) {
tmp = val >> (16 + (n*4));
if (tmp & 0x8) snd_iprintf(buffer, "Channel %d: Rate=%d\n", n, samplerate[tmp & 0x7]);
else snd_iprintf(buffer, "Channel %d: No input\n", n);
@@ -372,6 +375,27 @@ static void snd_emu10k1_proc_voices_read(struct snd_info_entry *entry,
}
#ifdef CONFIG_SND_DEBUG
+static void snd_emu_proc_emu1010_reg_read(struct snd_info_entry *entry,
+ struct snd_info_buffer *buffer)
+{
+ struct snd_emu10k1 *emu = entry->private_data;
+ unsigned long value;
+ unsigned long flags;
+ unsigned long regs;
+ int i;
+ snd_iprintf(buffer, "EMU1010 Registers:\n\n");
+
+ for(i = 0; i < 0x30; i+=1) {
+ spin_lock_irqsave(&emu->emu_lock, flags);
+ regs=i+0x40; /* 0x40 upwards are registers. */
+ outl(regs, emu->port + A_IOCFG);
+ outl(regs | 0x80, emu->port + A_IOCFG); /* High bit clocks the value into the fpga. */
+ value = inl(emu->port + A_IOCFG);
+ spin_unlock_irqrestore(&emu->emu_lock, flags);
+ snd_iprintf(buffer, "%02X: %08lX, %02lX\n", i, value, (value >> 8) & 0x7f);
+ }
+}
+
static void snd_emu_proc_io_reg_read(struct snd_info_entry *entry,
struct snd_info_buffer *buffer)
{
@@ -398,7 +422,7 @@ static void snd_emu_proc_io_reg_write(struct snd_info_entry *entry,
while (!snd_info_get_line(buffer, line, sizeof(line))) {
if (sscanf(line, "%x %x", &reg, &val) != 2)
continue;
- if ((reg < 0x40) && (reg >=0) && (val <= 0xffffffff) ) {
+ if ((reg < 0x40) && (reg >= 0) && (val <= 0xffffffff) ) {
spin_lock_irqsave(&emu->emu_lock, flags);
outl(val, emu->port + (reg & 0xfffffffc));
spin_unlock_irqrestore(&emu->emu_lock, flags);
@@ -474,7 +498,7 @@ static void snd_emu_proc_ptr_reg_write(struct snd_info_entry *entry,
while (!snd_info_get_line(buffer, line, sizeof(line))) {
if (sscanf(line, "%x %x %x", &reg, &channel_id, &val) != 3)
continue;
- if ((reg < 0xa0) && (reg >=0) && (val <= 0xffffffff) && (channel_id >=0) && (channel_id <= 3) )
+ if ((reg < 0xa0) && (reg >= 0) && (val <= 0xffffffff) && (channel_id >= 0) && (channel_id <= 3) )
snd_ptr_write(emu, iobase, reg, channel_id, val);
}
}
@@ -531,6 +555,10 @@ int __devinit snd_emu10k1_proc_init(struct snd_emu10k1 * emu)
{
struct snd_info_entry *entry;
#ifdef CONFIG_SND_DEBUG
+ if ((emu->card_capabilities->emu1010) &&
+ snd_card_proc_new(emu->card, "emu1010_regs", &entry)) {
+ snd_info_set_text_ops(entry, emu, snd_emu_proc_emu1010_reg_read);
+ }
if (! snd_card_proc_new(emu->card, "io_regs", &entry)) {
snd_info_set_text_ops(entry, emu, snd_emu_proc_io_reg_read);
entry->c.text.write = snd_emu_proc_io_reg_write;
diff --git a/sound/pci/emu10k1/io.c b/sound/pci/emu10k1/io.c
index 029e7856c43b..116e1c8d9361 100644
--- a/sound/pci/emu10k1/io.c
+++ b/sound/pci/emu10k1/io.c
@@ -30,6 +30,7 @@
#include <sound/core.h>
#include <sound/emu10k1.h>
#include <linux/delay.h>
+#include "p17v.h"
unsigned int snd_emu10k1_ptr_read(struct snd_emu10k1 * emu, unsigned int reg, unsigned int chn)
{
@@ -167,6 +168,109 @@ int snd_emu10k1_spi_write(struct snd_emu10k1 * emu,
return 0;
}
+/* The ADC does not support i2c read, so only write is implemented */
+int snd_emu10k1_i2c_write(struct snd_emu10k1 *emu,
+ u32 reg,
+ u32 value)
+{
+ u32 tmp;
+ int timeout = 0;
+ int status;
+ int retry;
+ if ((reg > 0x7f) || (value > 0x1ff)) {
+ snd_printk(KERN_ERR "i2c_write: invalid values.\n");
+ return -EINVAL;
+ }
+
+ tmp = reg << 25 | value << 16;
+ // snd_printk("I2C-write:reg=0x%x, value=0x%x\n", reg, value);
+ /* Not sure what this I2C channel controls. */
+ /* snd_emu10k1_ptr_write(emu, P17V_I2C_0, 0, tmp); */
+
+ /* This controls the I2C connected to the WM8775 ADC Codec */
+ snd_emu10k1_ptr20_write(emu, P17V_I2C_1, 0, tmp);
+ tmp = snd_emu10k1_ptr20_read(emu, P17V_I2C_1, 0); /* write post */
+
+ for (retry = 0; retry < 10; retry++) {
+ /* Send the data to i2c */
+ //tmp = snd_emu10k1_ptr_read(emu, P17V_I2C_ADDR, 0);
+ //tmp = tmp & ~(I2C_A_ADC_READ|I2C_A_ADC_LAST|I2C_A_ADC_START|I2C_A_ADC_ADD_MASK);
+ tmp = 0;
+ tmp = tmp | (I2C_A_ADC_LAST|I2C_A_ADC_START|I2C_A_ADC_ADD);
+ snd_emu10k1_ptr20_write(emu, P17V_I2C_ADDR, 0, tmp);
+
+ /* Wait till the transaction ends */
+ while (1) {
+ udelay(10);
+ status = snd_emu10k1_ptr20_read(emu, P17V_I2C_ADDR, 0);
+ // snd_printk("I2C:status=0x%x\n", status);
+ timeout++;
+ if ((status & I2C_A_ADC_START) == 0)
+ break;
+
+ if (timeout > 1000) {
+ snd_printk("emu10k1:I2C:timeout status=0x%x\n", status);
+ break;
+ }
+ }
+ //Read back and see if the transaction is successful
+ if ((status & I2C_A_ADC_ABORT) == 0)
+ break;
+ }
+
+ if (retry == 10) {
+ snd_printk(KERN_ERR "Writing to ADC failed!\n");
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+int snd_emu1010_fpga_write(struct snd_emu10k1 * emu, int reg, int value)
+{
+ if (reg < 0 || reg > 0x3f)
+ return 1;
+ reg += 0x40; /* 0x40 upwards are registers. */
+ if (value < 0 || value > 0x3f) /* 0 to 0x3f are values */
+ return 1;
+ outl(reg, emu->port + A_IOCFG);
+ udelay(10);
+ outl(reg | 0x80, emu->port + A_IOCFG); /* High bit clocks the value into the fpga. */
+ udelay(10);
+ outl(value, emu->port + A_IOCFG);
+ udelay(10);
+ outl(value | 0x80 , emu->port + A_IOCFG); /* High bit clocks the value into the fpga. */
+
+ return 0;
+}
+
+int snd_emu1010_fpga_read(struct snd_emu10k1 * emu, int reg, int *value)
+{
+ if (reg < 0 || reg > 0x3f)
+ return 1;
+ reg += 0x40; /* 0x40 upwards are registers. */
+ outl(reg, emu->port + A_IOCFG);
+ udelay(10);
+ outl(reg | 0x80, emu->port + A_IOCFG); /* High bit clocks the value into the fpga. */
+ udelay(10);
+ *value = ((inl(emu->port + A_IOCFG) >> 8) & 0x7f);
+
+ return 0;
+}
+
+/* Each Destination has one and only one Source,
+ * but one Source can feed any number of Destinations simultaneously.
+ */
+int snd_emu1010_fpga_link_dst_src_write(struct snd_emu10k1 * emu, int dst, int src)
+{
+ snd_emu1010_fpga_write(emu, 0x00, ((dst >> 8) & 0x3f) );
+ snd_emu1010_fpga_write(emu, 0x01, (dst & 0x3f) );
+ snd_emu1010_fpga_write(emu, 0x02, ((src >> 8) & 0x3f) );
+ snd_emu1010_fpga_write(emu, 0x03, (src & 0x3f) );
+
+ return 0;
+}
+
void snd_emu10k1_intr_enable(struct snd_emu10k1 *emu, unsigned int intrenb)
{
unsigned long flags;
diff --git a/sound/pci/emu10k1/p16v.c b/sound/pci/emu10k1/p16v.c
index 4e0f95438f47..465f8d505329 100644
--- a/sound/pci/emu10k1/p16v.c
+++ b/sound/pci/emu10k1/p16v.c
@@ -253,7 +253,7 @@ static int snd_p16v_pcm_close_playback(struct snd_pcm_substream *substream)
struct snd_emu10k1 *emu = snd_pcm_substream_chip(substream);
//struct snd_pcm_runtime *runtime = substream->runtime;
//struct snd_emu10k1_pcm *epcm = runtime->private_data;
- emu->p16v_voices[substream->pcm->device - emu->p16v_device_offset].use=0;
+ emu->p16v_voices[substream->pcm->device - emu->p16v_device_offset].use = 0;
/* FIXME: maybe zero others */
return 0;
}
@@ -264,7 +264,7 @@ static int snd_p16v_pcm_close_capture(struct snd_pcm_substream *substream)
struct snd_emu10k1 *emu = snd_pcm_substream_chip(substream);
//struct snd_pcm_runtime *runtime = substream->runtime;
//struct snd_emu10k1_pcm *epcm = runtime->private_data;
- emu->p16v_capture_voice.use=0;
+ emu->p16v_capture_voice.use = 0;
/* FIXME: maybe zero others */
return 0;
}
@@ -349,7 +349,7 @@ static int snd_p16v_pcm_prepare_playback(struct snd_pcm_substream *substream)
break;
}
/* FIXME: Check emu->buffer.size before actually writing to it. */
- for(i=0; i < runtime->periods; i++) {
+ for(i = 0; i < runtime->periods; i++) {
table_base[i*2]=runtime->dma_addr+(i*period_size_bytes);
table_base[(i*2)+1]=period_size_bytes<<16;
}
@@ -394,7 +394,7 @@ static int snd_p16v_pcm_prepare_capture(struct snd_pcm_substream *substream)
/* FIXME: Check emu->buffer.size before actually writing to it. */
snd_emu10k1_ptr20_write(emu, 0x13, channel, 0);
snd_emu10k1_ptr20_write(emu, CAPTURE_DMA_ADDR, channel, runtime->dma_addr);
- snd_emu10k1_ptr20_write(emu, CAPTURE_BUFFER_SIZE, channel, frames_to_bytes(runtime, runtime->buffer_size)<<16); // buffer size in bytes
+ snd_emu10k1_ptr20_write(emu, CAPTURE_BUFFER_SIZE, channel, frames_to_bytes(runtime, runtime->buffer_size) << 16); // buffer size in bytes
snd_emu10k1_ptr20_write(emu, CAPTURE_POINTER, channel, 0);
//snd_emu10k1_ptr20_write(emu, CAPTURE_SOURCE, 0x0, 0x333300e4); /* Select MIC or Line in */
//snd_emu10k1_ptr20_write(emu, EXTENDED_INT_MASK, 0, snd_emu10k1_ptr20_read(emu, EXTENDED_INT_MASK, 0) | (0x110000<<channel));
@@ -437,7 +437,7 @@ static int snd_p16v_pcm_trigger_playback(struct snd_pcm_substream *substream,
struct snd_pcm_substream *s;
u32 basic = 0;
u32 inte = 0;
- int running=0;
+ int running = 0;
switch (cmd) {
case SNDRV_PCM_TRIGGER_START:
@@ -445,7 +445,7 @@ static int snd_p16v_pcm_trigger_playback(struct snd_pcm_substream *substream,
break;
case SNDRV_PCM_TRIGGER_STOP:
default:
- running=0;
+ running = 0;
break;
}
snd_pcm_group_for_each(pos, substream) {
@@ -785,7 +785,7 @@ static int snd_p16v_capture_channel_put(struct snd_kcontrol *kcontrol,
}
return change;
}
-static DECLARE_TLV_DB_SCALE(snd_p16v_db_scale1, -5175, 25, 1);
+static const DECLARE_TLV_DB_SCALE(snd_p16v_db_scale1, -5175, 25, 1);
#define P16V_VOL(xname,xreg,xhl) { \
.iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, \
diff --git a/sound/pci/emu10k1/p17v.h b/sound/pci/emu10k1/p17v.h
index 7ddb5be632cf..4ef5f68a9cd0 100644
--- a/sound/pci/emu10k1/p17v.h
+++ b/sound/pci/emu10k1/p17v.h
@@ -43,6 +43,53 @@
#define P17V_I2C_ADDR 0x3d /* I2C Address */
#define P17V_I2C_0 0x3e /* I2C Data */
#define P17V_I2C_1 0x3f /* I2C Data */
+/* I2C values */
+#define I2C_A_ADC_ADD_MASK 0x000000fe /*The address is a 7 bit address */
+#define I2C_A_ADC_RW_MASK 0x00000001 /*bit mask for R/W */
+#define I2C_A_ADC_TRANS_MASK 0x00000010 /*Bit mask for I2c address DAC value */
+#define I2C_A_ADC_ABORT_MASK 0x00000020 /*Bit mask for I2C transaction abort flag */
+#define I2C_A_ADC_LAST_MASK 0x00000040 /*Bit mask for Last word transaction */
+#define I2C_A_ADC_BYTE_MASK 0x00000080 /*Bit mask for Byte Mode */
+
+#define I2C_A_ADC_ADD 0x00000034 /*This is the Device address for ADC */
+#define I2C_A_ADC_READ 0x00000001 /*To perform a read operation */
+#define I2C_A_ADC_START 0x00000100 /*Start I2C transaction */
+#define I2C_A_ADC_ABORT 0x00000200 /*I2C transaction abort */
+#define I2C_A_ADC_LAST 0x00000400 /*I2C last transaction */
+#define I2C_A_ADC_BYTE 0x00000800 /*I2C one byte mode */
+
+#define I2C_D_ADC_REG_MASK 0xfe000000 /*ADC address register */
+#define I2C_D_ADC_DAT_MASK 0x01ff0000 /*ADC data register */
+
+#define ADC_TIMEOUT 0x00000007 /*ADC Timeout Clock Disable */
+#define ADC_IFC_CTRL 0x0000000b /*ADC Interface Control */
+#define ADC_MASTER 0x0000000c /*ADC Master Mode Control */
+#define ADC_POWER 0x0000000d /*ADC PowerDown Control */
+#define ADC_ATTEN_ADCL 0x0000000e /*ADC Attenuation ADCL */
+#define ADC_ATTEN_ADCR 0x0000000f /*ADC Attenuation ADCR */
+#define ADC_ALC_CTRL1 0x00000010 /*ADC ALC Control 1 */
+#define ADC_ALC_CTRL2 0x00000011 /*ADC ALC Control 2 */
+#define ADC_ALC_CTRL3 0x00000012 /*ADC ALC Control 3 */
+#define ADC_NOISE_CTRL 0x00000013 /*ADC Noise Gate Control */
+#define ADC_LIMIT_CTRL 0x00000014 /*ADC Limiter Control */
+#define ADC_MUX 0x00000015 /*ADC Mux offset */
+#if 0
+/* FIXME: Not tested yet. */
+#define ADC_GAIN_MASK 0x000000ff //Mask for ADC Gain
+#define ADC_ZERODB 0x000000cf //Value to set ADC to 0dB
+#define ADC_MUTE_MASK 0x000000c0 //Mask for ADC mute
+#define ADC_MUTE 0x000000c0 //Value to mute ADC
+#define ADC_OSR 0x00000008 //Mask for ADC oversample rate select
+#define ADC_TIMEOUT_DISABLE 0x00000008 //Value and mask to disable Timeout clock
+#define ADC_HPF_DISABLE 0x00000100 //Value and mask to disable High pass filter
+#define ADC_TRANWIN_MASK 0x00000070 //Mask for Length of Transient Window
+#endif
+
+#define ADC_MUX_MASK 0x0000000f //Mask for ADC Mux
+#define ADC_MUX_0 0x00000001 //Value to select Unknown at ADC Mux (Not used)
+#define ADC_MUX_1 0x00000002 //Value to select Unknown at ADC Mux (Not used)
+#define ADC_MUX_2 0x00000004 //Value to select Mic at ADC Mux
+#define ADC_MUX_3 0x00000008 //Value to select Line-In at ADC Mux
#define P17V_START_AUDIO 0x40 /* Start Audio bit */
/* 41 - 47: Reserved */
diff --git a/sound/pci/emu10k1/voice.c b/sound/pci/emu10k1/voice.c
index 94eca82dd4fc..1db50fe61475 100644
--- a/sound/pci/emu10k1/voice.c
+++ b/sound/pci/emu10k1/voice.c
@@ -83,7 +83,7 @@ static int voice_alloc(struct snd_emu10k1 *emu, int type, int number,
if (first_voice == last_voice)
return -ENOMEM;
- for (i=0; i < number; i++) {
+ for (i = 0; i < number; i++) {
voice = &emu->voices[(first_voice + i) % NUM_G];
// printk("voice alloc - %i, %i of %i\n", voice->number, idx-first_voice+1, number);
voice->use = 1;
diff --git a/sound/pci/ens1370.c b/sound/pci/ens1370.c
index a84f6b21024f..425b167522d5 100644
--- a/sound/pci/ens1370.c
+++ b/sound/pci/ens1370.c
@@ -413,8 +413,6 @@ struct ensoniq {
} u;
struct pci_dev *pci;
- unsigned short subsystem_vendor_id;
- unsigned short subsystem_device_id;
struct snd_card *card;
struct snd_pcm *pcm1; /* DAC1/ADC PCM */
struct snd_pcm *pcm2; /* DAC2 PCM */
@@ -1607,11 +1605,26 @@ static void snd_ensoniq_mixer_free_ac97(struct snd_ac97 *ac97)
ensoniq->u.es1371.ac97 = NULL;
}
-static struct {
+struct es1371_quirk {
unsigned short vid; /* vendor ID */
unsigned short did; /* device ID */
unsigned char rev; /* revision */
-} es1371_spdif_present[] __devinitdata = {
+};
+
+static int __devinit es1371_quirk_lookup(struct ensoniq *ensoniq,
+ struct es1371_quirk *list)
+{
+ while (list->vid != (unsigned short)PCI_ANY_ID) {
+ if (ensoniq->pci->vendor == list->vid &&
+ ensoniq->pci->device == list->did &&
+ ensoniq->rev == list->rev)
+ return 1;
+ list++;
+ }
+ return 0;
+}
+
+static struct es1371_quirk es1371_spdif_present[] __devinitdata = {
{ .vid = PCI_VENDOR_ID_ENSONIQ, .did = PCI_DEVICE_ID_ENSONIQ_CT5880, .rev = CT5880REV_CT5880_C },
{ .vid = PCI_VENDOR_ID_ENSONIQ, .did = PCI_DEVICE_ID_ENSONIQ_CT5880, .rev = CT5880REV_CT5880_D },
{ .vid = PCI_VENDOR_ID_ENSONIQ, .did = PCI_DEVICE_ID_ENSONIQ_CT5880, .rev = CT5880REV_CT5880_E },
@@ -1620,12 +1633,19 @@ static struct {
{ .vid = PCI_ANY_ID, .did = PCI_ANY_ID }
};
-static int snd_ensoniq_1371_mixer(struct ensoniq * ensoniq, int has_spdif, int has_line)
+static struct snd_pci_quirk ens1373_line_quirk[] __devinitdata = {
+ SND_PCI_QUIRK_ID(0x1274, 0x2000), /* GA-7DXR */
+ SND_PCI_QUIRK_ID(0x1458, 0xa000), /* GA-8IEXP */
+ { } /* end */
+};
+
+static int __devinit snd_ensoniq_1371_mixer(struct ensoniq *ensoniq,
+ int has_spdif, int has_line)
{
struct snd_card *card = ensoniq->card;
struct snd_ac97_bus *pbus;
struct snd_ac97_template ac97;
- int err, idx;
+ int err;
static struct snd_ac97_bus_ops ops = {
.write = snd_es1371_codec_write,
.read = snd_es1371_codec_read,
@@ -1641,33 +1661,28 @@ static int snd_ensoniq_1371_mixer(struct ensoniq * ensoniq, int has_spdif, int h
ac97.scaps = AC97_SCAP_AUDIO;
if ((err = snd_ac97_mixer(pbus, &ac97, &ensoniq->u.es1371.ac97)) < 0)
return err;
- for (idx = 0; es1371_spdif_present[idx].vid != (unsigned short)PCI_ANY_ID; idx++)
- if ((ensoniq->pci->vendor == es1371_spdif_present[idx].vid &&
- ensoniq->pci->device == es1371_spdif_present[idx].did &&
- ensoniq->rev == es1371_spdif_present[idx].rev) || has_spdif > 0) {
- struct snd_kcontrol *kctl;
- int i, index = 0;
-
- if (has_spdif < 0)
- break;
-
- ensoniq->spdif_default = ensoniq->spdif_stream =
- SNDRV_PCM_DEFAULT_CON_SPDIF;
- outl(ensoniq->spdif_default, ES_REG(ensoniq, CHANNEL_STATUS));
-
- if (ensoniq->u.es1371.ac97->ext_id & AC97_EI_SPDIF)
- index++;
-
- for (i = 0; i < (int)ARRAY_SIZE(snd_es1371_mixer_spdif); i++) {
- kctl = snd_ctl_new1(&snd_es1371_mixer_spdif[i], ensoniq);
- if (! kctl)
- return -ENOMEM;
- kctl->id.index = index;
- if ((err = snd_ctl_add(card, kctl)) < 0)
- return err;
- }
- break;
+ if (has_spdif > 0 ||
+ (!has_spdif && es1371_quirk_lookup(ensoniq, es1371_spdif_present))) {
+ struct snd_kcontrol *kctl;
+ int i, index = 0;
+
+ ensoniq->spdif_default = ensoniq->spdif_stream =
+ SNDRV_PCM_DEFAULT_CON_SPDIF;
+ outl(ensoniq->spdif_default, ES_REG(ensoniq, CHANNEL_STATUS));
+
+ if (ensoniq->u.es1371.ac97->ext_id & AC97_EI_SPDIF)
+ index++;
+
+ for (i = 0; i < ARRAY_SIZE(snd_es1371_mixer_spdif); i++) {
+ kctl = snd_ctl_new1(&snd_es1371_mixer_spdif[i], ensoniq);
+ if (!kctl)
+ return -ENOMEM;
+ kctl->id.index = index;
+ err = snd_ctl_add(card, kctl);
+ if (err < 0)
+ return err;
}
+ }
if (ensoniq->u.es1371.ac97->ext_id & AC97_EI_SDAC) {
/* mirror rear to front speakers */
ensoniq->cssr &= ~(ES_1373_REAR_BIT27|ES_1373_REAR_BIT24);
@@ -1676,12 +1691,10 @@ static int snd_ensoniq_1371_mixer(struct ensoniq * ensoniq, int has_spdif, int h
if (err < 0)
return err;
}
- if (((ensoniq->subsystem_vendor_id == 0x1274) &&
- (ensoniq->subsystem_device_id == 0x2000)) || /* GA-7DXR */
- ((ensoniq->subsystem_vendor_id == 0x1458) &&
- (ensoniq->subsystem_device_id == 0xa000)) || /* GA-8IEXP */
- has_line > 0) {
- err = snd_ctl_add(card, snd_ctl_new1(&snd_ens1373_line, ensoniq));
+ if (has_line > 0 ||
+ snd_pci_quirk_lookup(ensoniq->pci, ens1373_line_quirk)) {
+ err = snd_ctl_add(card, snd_ctl_new1(&snd_ens1373_line,
+ ensoniq));
if (err < 0)
return err;
}
@@ -1956,21 +1969,15 @@ static int snd_ensoniq_dev_free(struct snd_device *device)
}
#ifdef CHIP1371
-static struct {
- unsigned short svid; /* subsystem vendor ID */
- unsigned short sdid; /* subsystem device ID */
-} es1371_amplifier_hack[] = {
- { .svid = 0x107b, .sdid = 0x2150 }, /* Gateway Solo 2150 */
- { .svid = 0x13bd, .sdid = 0x100c }, /* EV1938 on Mebius PC-MJ100V */
- { .svid = 0x1102, .sdid = 0x5938 }, /* Targa Xtender300 */
- { .svid = 0x1102, .sdid = 0x8938 }, /* IPC Topnote G notebook */
- { .svid = PCI_ANY_ID, .sdid = PCI_ANY_ID }
+static struct snd_pci_quirk es1371_amplifier_hack[] __devinitdata = {
+ SND_PCI_QUIRK_ID(0x107b, 0x2150), /* Gateway Solo 2150 */
+ SND_PCI_QUIRK_ID(0x13bd, 0x100c), /* EV1938 on Mebius PC-MJ100V */
+ SND_PCI_QUIRK_ID(0x1102, 0x5938), /* Targa Xtender300 */
+ SND_PCI_QUIRK_ID(0x1102, 0x8938), /* IPC Topnote G notebook */
+ { } /* end */
};
-static struct {
- unsigned short vid; /* vendor ID */
- unsigned short did; /* device ID */
- unsigned char rev; /* revision */
-} es1371_ac97_reset_hack[] = {
+
+static struct es1371_quirk es1371_ac97_reset_hack[] = {
{ .vid = PCI_VENDOR_ID_ENSONIQ, .did = PCI_DEVICE_ID_ENSONIQ_CT5880, .rev = CT5880REV_CT5880_C },
{ .vid = PCI_VENDOR_ID_ENSONIQ, .did = PCI_DEVICE_ID_ENSONIQ_CT5880, .rev = CT5880REV_CT5880_D },
{ .vid = PCI_VENDOR_ID_ENSONIQ, .did = PCI_DEVICE_ID_ENSONIQ_CT5880, .rev = CT5880REV_CT5880_E },
@@ -1984,7 +1991,6 @@ static void snd_ensoniq_chip_init(struct ensoniq *ensoniq)
{
#ifdef CHIP1371
int idx;
- struct pci_dev *pci = ensoniq->pci;
#endif
/* this code was part of snd_ensoniq_create before intruduction
* of suspend/resume
@@ -1999,16 +2005,12 @@ static void snd_ensoniq_chip_init(struct ensoniq *ensoniq)
outl(ensoniq->ctrl, ES_REG(ensoniq, CONTROL));
outl(ensoniq->sctrl, ES_REG(ensoniq, SERIAL));
outl(0, ES_REG(ensoniq, 1371_LEGACY));
- for (idx = 0; es1371_ac97_reset_hack[idx].vid != (unsigned short)PCI_ANY_ID; idx++)
- if (pci->vendor == es1371_ac97_reset_hack[idx].vid &&
- pci->device == es1371_ac97_reset_hack[idx].did &&
- ensoniq->rev == es1371_ac97_reset_hack[idx].rev) {
- outl(ensoniq->cssr, ES_REG(ensoniq, STATUS));
- /* need to delay around 20ms(bleech) to give
- some CODECs enough time to wakeup */
- msleep(20);
- break;
- }
+ if (es1371_quirk_lookup(ensoniq, es1371_ac97_reset_hack)) {
+ outl(ensoniq->cssr, ES_REG(ensoniq, STATUS));
+ /* need to delay around 20ms(bleech) to give
+ some CODECs enough time to wakeup */
+ msleep(20);
+ }
/* AC'97 warm reset to start the bitclk */
outl(ensoniq->ctrl | ES_1371_SYNC_RES, ES_REG(ensoniq, CONTROL));
inl(ES_REG(ensoniq, CONTROL));
@@ -2112,11 +2114,7 @@ static int __devinit snd_ensoniq_create(struct snd_card *card,
struct ensoniq ** rensoniq)
{
struct ensoniq *ensoniq;
- unsigned short cmdw;
unsigned char cmdb;
-#ifdef CHIP1371
- int idx;
-#endif
int err;
static struct snd_device_ops ops = {
.dev_free = snd_ensoniq_dev_free,
@@ -2159,10 +2157,6 @@ static int __devinit snd_ensoniq_create(struct snd_card *card,
pci_set_master(pci);
pci_read_config_byte(pci, PCI_REVISION_ID, &cmdb);
ensoniq->rev = cmdb;
- pci_read_config_word(pci, PCI_SUBSYSTEM_VENDOR_ID, &cmdw);
- ensoniq->subsystem_vendor_id = cmdw;
- pci_read_config_word(pci, PCI_SUBSYSTEM_ID, &cmdw);
- ensoniq->subsystem_device_id = cmdw;
#ifdef CHIP1370
#if 0
ensoniq->ctrl = ES_1370_CDC_EN | ES_1370_SERR_DISABLE |
@@ -2175,19 +2169,11 @@ static int __devinit snd_ensoniq_create(struct snd_card *card,
ensoniq->ctrl = 0;
ensoniq->sctrl = 0;
ensoniq->cssr = 0;
- for (idx = 0; es1371_amplifier_hack[idx].svid != (unsigned short)PCI_ANY_ID; idx++)
- if (ensoniq->subsystem_vendor_id == es1371_amplifier_hack[idx].svid &&
- ensoniq->subsystem_device_id == es1371_amplifier_hack[idx].sdid) {
- ensoniq->ctrl |= ES_1371_GPIO_OUT(1); /* turn amplifier on */
- break;
- }
- for (idx = 0; es1371_ac97_reset_hack[idx].vid != (unsigned short)PCI_ANY_ID; idx++)
- if (pci->vendor == es1371_ac97_reset_hack[idx].vid &&
- pci->device == es1371_ac97_reset_hack[idx].did &&
- ensoniq->rev == es1371_ac97_reset_hack[idx].rev) {
- ensoniq->cssr |= ES_1371_ST_AC97_RST;
- break;
- }
+ if (snd_pci_quirk_lookup(pci, es1371_amplifier_hack))
+ ensoniq->ctrl |= ES_1371_GPIO_OUT(1); /* turn amplifier on */
+
+ if (es1371_quirk_lookup(ensoniq, es1371_ac97_reset_hack))
+ ensoniq->cssr |= ES_1371_ST_AC97_RST;
#endif
snd_ensoniq_chip_init(ensoniq);
diff --git a/sound/pci/es1938.c b/sound/pci/es1938.c
index 66ac26c5a240..fec29a108945 100644
--- a/sound/pci/es1938.c
+++ b/sound/pci/es1938.c
@@ -1344,7 +1344,7 @@ static unsigned int db_scale_line[] = {
8, 15, TLV_DB_SCALE_ITEM(-750, 150, 0),
};
-static DECLARE_TLV_DB_SCALE(db_scale_capture, 0, 150, 0);
+static const DECLARE_TLV_DB_SCALE(db_scale_capture, 0, 150, 0);
static struct snd_kcontrol_new snd_es1938_controls[] = {
ES1938_DOUBLE_TLV("Master Playback Volume", 0, 0x60, 0x62, 0, 0, 63, 0,
diff --git a/sound/pci/fm801.c b/sound/pci/fm801.c
index b7b361ce3a93..6dc578bbeec9 100644
--- a/sound/pci/fm801.c
+++ b/sound/pci/fm801.c
@@ -1157,7 +1157,7 @@ static int snd_fm801_put_mux(struct snd_kcontrol *kcontrol,
return snd_fm801_update_bits(chip, FM801_REC_SRC, 7, val);
}
-static DECLARE_TLV_DB_SCALE(db_scale_dsp, -3450, 150, 0);
+static const DECLARE_TLV_DB_SCALE(db_scale_dsp, -3450, 150, 0);
#define FM801_CONTROLS ARRAY_SIZE(snd_fm801_controls)
diff --git a/sound/pci/hda/Makefile b/sound/pci/hda/Makefile
index dbacba6177db..60d7b05a204a 100644
--- a/sound/pci/hda/Makefile
+++ b/sound/pci/hda/Makefile
@@ -1,5 +1,14 @@
snd-hda-intel-objs := hda_intel.o
-snd-hda-codec-objs := hda_codec.o hda_generic.o patch_realtek.o patch_cmedia.o patch_analog.o patch_sigmatel.o patch_si3054.o patch_atihdmi.o
+snd-hda-codec-objs := hda_codec.o \
+ hda_generic.o \
+ patch_realtek.o \
+ patch_cmedia.o \
+ patch_analog.o \
+ patch_sigmatel.o \
+ patch_si3054.o \
+ patch_atihdmi.o \
+ patch_conexant.o \
+ patch_via.o
ifdef CONFIG_PROC_FS
snd-hda-codec-objs += hda_proc.o
endif
diff --git a/sound/pci/hda/hda_codec.c b/sound/pci/hda/hda_codec.c
index 18bbc87e376f..8f34fb447983 100644
--- a/sound/pci/hda/hda_codec.c
+++ b/sound/pci/hda/hda_codec.c
@@ -52,6 +52,7 @@ struct hda_vendor_id {
static struct hda_vendor_id hda_vendor_ids[] = {
{ 0x10ec, "Realtek" },
{ 0x1057, "Motorola" },
+ { 0x1106, "VIA" },
{ 0x11d4, "Analog Devices" },
{ 0x13f6, "C-Media" },
{ 0x14f1, "Conexant" },
@@ -262,7 +263,7 @@ int snd_hda_queue_unsol_event(struct hda_bus *bus, u32 res, u32 res_ex)
unsol->queue[wp] = res;
unsol->queue[wp + 1] = res_ex;
- queue_work(unsol->workq, &unsol->work);
+ schedule_work(&unsol->work);
return 0;
}
@@ -309,12 +310,6 @@ static int init_unsol_queue(struct hda_bus *bus)
snd_printk(KERN_ERR "hda_codec: can't allocate unsolicited queue\n");
return -ENOMEM;
}
- unsol->workq = create_singlethread_workqueue("hda_codec");
- if (! unsol->workq) {
- snd_printk(KERN_ERR "hda_codec: can't create workqueue\n");
- kfree(unsol);
- return -ENOMEM;
- }
INIT_WORK(&unsol->work, process_unsol_events);
unsol->bus = bus;
bus->unsol = unsol;
@@ -333,7 +328,7 @@ static int snd_hda_bus_free(struct hda_bus *bus)
if (! bus)
return 0;
if (bus->unsol) {
- destroy_workqueue(bus->unsol->workq);
+ flush_scheduled_work();
kfree(bus->unsol);
}
list_for_each_safe(p, n, &bus->codec_list) {
@@ -1714,6 +1709,8 @@ EXPORT_SYMBOL(snd_hda_build_pcms);
/**
* snd_hda_check_board_config - compare the current codec with the config table
* @codec: the HDA codec
+ * @num_configs: number of config enums
+ * @models: array of model name strings
* @tbl: configuration table, terminated by null entries
*
* Compares the modelname or PCI subsystem id of the current codec with the
@@ -1722,33 +1719,44 @@ EXPORT_SYMBOL(snd_hda_build_pcms);
*
* If no entries are matching, the function returns a negative value.
*/
-int snd_hda_check_board_config(struct hda_codec *codec, const struct hda_board_config *tbl)
-{
- const struct hda_board_config *c;
-
- if (codec->bus->modelname) {
- for (c = tbl; c->modelname || c->pci_subvendor; c++) {
- if (c->modelname &&
- ! strcmp(codec->bus->modelname, c->modelname)) {
- snd_printd(KERN_INFO "hda_codec: model '%s' is selected\n", c->modelname);
- return c->config;
+int snd_hda_check_board_config(struct hda_codec *codec,
+ int num_configs, const char **models,
+ const struct snd_pci_quirk *tbl)
+{
+ if (codec->bus->modelname && models) {
+ int i;
+ for (i = 0; i < num_configs; i++) {
+ if (models[i] &&
+ !strcmp(codec->bus->modelname, models[i])) {
+ snd_printd(KERN_INFO "hda_codec: model '%s' is "
+ "selected\n", models[i]);
+ return i;
}
}
}
- if (codec->bus->pci) {
- u16 subsystem_vendor, subsystem_device;
- pci_read_config_word(codec->bus->pci, PCI_SUBSYSTEM_VENDOR_ID, &subsystem_vendor);
- pci_read_config_word(codec->bus->pci, PCI_SUBSYSTEM_ID, &subsystem_device);
- for (c = tbl; c->modelname || c->pci_subvendor; c++) {
- if (c->pci_subvendor == subsystem_vendor &&
- (! c->pci_subdevice /* all match */||
- (c->pci_subdevice == subsystem_device))) {
- snd_printdd(KERN_INFO "hda_codec: PCI %x:%x, codec config %d is selected\n",
- subsystem_vendor, subsystem_device, c->config);
- return c->config;
- }
+ if (!codec->bus->pci || !tbl)
+ return -1;
+
+ tbl = snd_pci_quirk_lookup(codec->bus->pci, tbl);
+ if (!tbl)
+ return -1;
+ if (tbl->value >= 0 && tbl->value < num_configs) {
+#ifdef CONFIG_SND_DEBUG_DETECT
+ char tmp[10];
+ const char *model = NULL;
+ if (models)
+ model = models[tbl->value];
+ if (!model) {
+ sprintf(tmp, "#%d", tbl->value);
+ model = tmp;
}
+ snd_printdd(KERN_INFO "hda_codec: model '%s' is selected "
+ "for config %x:%x (%s)\n",
+ model, tbl->subvendor, tbl->subdevice,
+ (tbl->name ? tbl->name : "Unknown device"));
+#endif
+ return tbl->value;
}
return -1;
}
diff --git a/sound/pci/hda/hda_intel.c b/sound/pci/hda/hda_intel.c
index 1a7e82104bb9..b9a8e238b0a8 100644
--- a/sound/pci/hda/hda_intel.c
+++ b/sound/pci/hda/hda_intel.c
@@ -199,7 +199,7 @@ enum { SDI0, SDI1, SDI2, SDI3, SDO0, SDO1, SDO2, SDO3 };
/* STATESTS int mask: SD2,SD1,SD0 */
#define STATESTS_INT_MASK 0x07
-#define AZX_MAX_CODECS 4
+#define AZX_MAX_CODECS 3
/* SD_CTL bits */
#define SD_CTL_STREAM_RESET 0x01 /* stream reset bit */
@@ -1285,7 +1285,7 @@ static int __devinit create_codec_pcm(struct azx *chip, struct hda_codec *codec,
snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, &azx_pcm_ops);
snd_pcm_lib_preallocate_pages_for_all(pcm, SNDRV_DMA_TYPE_DEV,
snd_dma_pci_data(chip->pci),
- 1024 * 64, 1024 * 128);
+ 1024 * 64, 1024 * 1024);
chip->pcm[pcm_dev] = pcm;
if (chip->pcm_devs < pcm_dev + 1)
chip->pcm_devs = pcm_dev + 1;
@@ -1391,6 +1391,7 @@ static int azx_acquire_irq(struct azx *chip, int do_disconnect)
return -1;
}
chip->irq = chip->pci->irq;
+ pci_intx(chip->pci, !chip->msi);
return 0;
}
@@ -1502,6 +1503,31 @@ static int azx_dev_free(struct snd_device *device)
}
/*
+ * white/black-listing for position_fix
+ */
+static const struct snd_pci_quirk position_fix_list[] __devinitdata = {
+ SND_PCI_QUIRK(0x1028, 0x01cc, "Dell D820", POS_FIX_NONE),
+ {}
+};
+
+static int __devinit check_position_fix(struct azx *chip, int fix)
+{
+ const struct snd_pci_quirk *q;
+
+ if (fix == POS_FIX_AUTO) {
+ q = snd_pci_quirk_lookup(chip->pci, position_fix_list);
+ if (q) {
+ snd_printdd(KERN_INFO
+ "hda_intel: position_fix set to %d "
+ "for device %04x:%04x\n",
+ q->value, q->subvendor, q->subdevice);
+ return q->value;
+ }
+ }
+ return fix;
+}
+
+/*
* constructor
*/
static int __devinit azx_create(struct snd_card *card, struct pci_dev *pci,
@@ -1535,7 +1561,8 @@ static int __devinit azx_create(struct snd_card *card, struct pci_dev *pci,
chip->driver_type = driver_type;
chip->msi = enable_msi;
- chip->position_fix = position_fix;
+ chip->position_fix = check_position_fix(chip, position_fix);
+
chip->single_cmd = single_cmd;
#if BITS_PER_LONG != 64
diff --git a/sound/pci/hda/hda_local.h b/sound/pci/hda/hda_local.h
index 9ca1baf860bd..39718d6cdadd 100644
--- a/sound/pci/hda/hda_local.h
+++ b/sound/pci/hda/hda_local.h
@@ -173,14 +173,9 @@ static inline int snd_hda_codec_proc_new(struct hda_codec *codec) { return 0; }
/*
* Misc
*/
-struct hda_board_config {
- const char *modelname;
- int config;
- unsigned short pci_subvendor;
- unsigned short pci_subdevice;
-};
-
-int snd_hda_check_board_config(struct hda_codec *codec, const struct hda_board_config *tbl);
+int snd_hda_check_board_config(struct hda_codec *codec, int num_configs,
+ const char **modelnames,
+ const struct snd_pci_quirk *pci_list);
int snd_hda_add_new_ctls(struct hda_codec *codec, struct snd_kcontrol_new *knew);
/*
@@ -204,7 +199,6 @@ struct hda_bus_unsolicited {
unsigned int rp, wp;
/* workqueue */
- struct workqueue_struct *workq;
struct work_struct work;
struct hda_bus *bus;
};
diff --git a/sound/pci/hda/hda_patch.h b/sound/pci/hda/hda_patch.h
index 0b668793face..9f9e9ae44a9d 100644
--- a/sound/pci/hda/hda_patch.h
+++ b/sound/pci/hda/hda_patch.h
@@ -14,6 +14,10 @@ extern struct hda_codec_preset snd_hda_preset_sigmatel[];
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[];
static const struct hda_codec_preset *hda_preset_tables[] = {
snd_hda_preset_realtek,
@@ -22,5 +26,7 @@ static const struct hda_codec_preset *hda_preset_tables[] = {
snd_hda_preset_sigmatel,
snd_hda_preset_si3054,
snd_hda_preset_atihdmi,
+ snd_hda_preset_conexant,
+ snd_hda_preset_via,
NULL
};
diff --git a/sound/pci/hda/patch_analog.c b/sound/pci/hda/patch_analog.c
index 076365bc10e9..38977bce70e2 100644
--- a/sound/pci/hda/patch_analog.c
+++ b/sound/pci/hda/patch_analog.c
@@ -782,54 +782,63 @@ static struct hda_channel_mode ad1986a_modes[3] = {
/* eapd initialization */
static struct hda_verb ad1986a_eapd_init_verbs[] = {
- {0x1b, AC_VERB_SET_EAPD_BTLENABLE, 0x00},
+ {0x1b, AC_VERB_SET_EAPD_BTLENABLE, 0x00 },
{}
};
+/* Ultra initialization */
+static struct hda_verb ad1986a_ultra_init[] = {
+ /* eapd initialization */
+ { 0x1b, AC_VERB_SET_EAPD_BTLENABLE, 0x00 },
+ /* CLFE -> Mic in */
+ { 0x0f, AC_VERB_SET_CONNECT_SEL, 0x2 },
+ { 0x1d, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x24 },
+ { 0x1d, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080 },
+ { } /* end */
+};
+
/* models */
-enum { AD1986A_6STACK, AD1986A_3STACK, AD1986A_LAPTOP, AD1986A_LAPTOP_EAPD };
-
-static struct hda_board_config ad1986a_cfg_tbl[] = {
- { .modelname = "6stack", .config = AD1986A_6STACK },
- { .modelname = "3stack", .config = AD1986A_3STACK },
- { .pci_subvendor = 0x10de, .pci_subdevice = 0xcb84,
- .config = AD1986A_3STACK }, /* ASUS A8N-VM CSM */
- { .pci_subvendor = 0x1043, .pci_subdevice = 0x817f,
- .config = AD1986A_3STACK }, /* ASUS P5P-L2 */
- { .pci_subvendor = 0x1043, .pci_subdevice = 0x81b3,
- .config = AD1986A_3STACK }, /* ASUS P5RD2-VM / P5GPL-X SE */
- { .pci_subvendor = 0x1043, .pci_subdevice = 0x81cb,
- .config = AD1986A_3STACK }, /* ASUS M2NPV-VM */
- { .modelname = "laptop", .config = AD1986A_LAPTOP },
- { .pci_subvendor = 0x144d, .pci_subdevice = 0xc01e,
- .config = AD1986A_LAPTOP }, /* FSC V2060 */
- { .pci_subvendor = 0x17c0, .pci_subdevice = 0x2017,
- .config = AD1986A_LAPTOP }, /* Samsung M50 */
- { .pci_subvendor = 0x1043, .pci_subdevice = 0x818f,
- .config = AD1986A_LAPTOP }, /* ASUS P5GV-MX */
- { .modelname = "laptop-eapd", .config = AD1986A_LAPTOP_EAPD },
- { .pci_subvendor = 0x144d, .pci_subdevice = 0xc023,
- .config = AD1986A_LAPTOP_EAPD }, /* Samsung X60 Chane */
- { .pci_subvendor = 0x144d, .pci_subdevice = 0xc024,
- .config = AD1986A_LAPTOP_EAPD }, /* Samsung R65-T2300 Charis */
- { .pci_subvendor = 0x144d, .pci_subdevice = 0xc026,
- .config = AD1986A_LAPTOP_EAPD }, /* Samsung X11-T2300 Culesa */
- { .pci_subvendor = 0x1043, .pci_subdevice = 0x1153,
- .config = AD1986A_LAPTOP_EAPD }, /* ASUS M9 */
- { .pci_subvendor = 0x1043, .pci_subdevice = 0x1213,
- .config = AD1986A_LAPTOP_EAPD }, /* ASUS A6J */
- { .pci_subvendor = 0x1043, .pci_subdevice = 0x11f7,
- .config = AD1986A_LAPTOP_EAPD }, /* ASUS U5A */
- { .pci_subvendor = 0x1043, .pci_subdevice = 0x1263,
- .config = AD1986A_LAPTOP_EAPD }, /* ASUS U5F */
- { .pci_subvendor = 0x1043, .pci_subdevice = 0x1297,
- .config = AD1986A_LAPTOP_EAPD }, /* ASUS Z62F */
- { .pci_subvendor = 0x1043, .pci_subdevice = 0x12b3,
- .config = AD1986A_LAPTOP_EAPD }, /* ASUS V1j */
- { .pci_subvendor = 0x103c, .pci_subdevice = 0x30af,
- .config = AD1986A_LAPTOP_EAPD }, /* HP Compaq Presario B2800 */
- { .pci_subvendor = 0x17aa, .pci_subdevice = 0x2066,
- .config = AD1986A_LAPTOP_EAPD }, /* Lenovo 3000 N100-07684JU */
+enum {
+ AD1986A_6STACK,
+ AD1986A_3STACK,
+ AD1986A_LAPTOP,
+ AD1986A_LAPTOP_EAPD,
+ AD1986A_ULTRA,
+ AD1986A_MODELS
+};
+
+static const char *ad1986a_models[AD1986A_MODELS] = {
+ [AD1986A_6STACK] = "6stack",
+ [AD1986A_3STACK] = "3stack",
+ [AD1986A_LAPTOP] = "laptop",
+ [AD1986A_LAPTOP_EAPD] = "laptop-eapd",
+ [AD1986A_ULTRA] = "ultra",
+};
+
+static struct snd_pci_quirk ad1986a_cfg_tbl[] = {
+ SND_PCI_QUIRK(0x103c, 0x30af, "HP B2800", AD1986A_LAPTOP_EAPD),
+ SND_PCI_QUIRK(0x10de, 0xcb84, "ASUS A8N-VM", AD1986A_3STACK),
+ SND_PCI_QUIRK(0x1043, 0x1153, "ASUS M9", AD1986A_LAPTOP_EAPD),
+ SND_PCI_QUIRK(0x1043, 0x1213, "ASUS A6J", AD1986A_LAPTOP_EAPD),
+ SND_PCI_QUIRK(0x1043, 0x11f7, "ASUS U5A", AD1986A_LAPTOP_EAPD),
+ SND_PCI_QUIRK(0x1043, 0x1263, "ASUS U5F", AD1986A_LAPTOP_EAPD),
+ SND_PCI_QUIRK(0x1043, 0x1297, "ASUS Z62F", AD1986A_LAPTOP_EAPD),
+ SND_PCI_QUIRK(0x1043, 0x12b3, "ASUS V1j", AD1986A_LAPTOP_EAPD),
+ SND_PCI_QUIRK(0x1043, 0x1302, "ASUS W3j", AD1986A_LAPTOP_EAPD),
+ SND_PCI_QUIRK(0x1043, 0x817f, "ASUS P5", AD1986A_3STACK),
+ SND_PCI_QUIRK(0x1043, 0x818f, "ASUS P5", AD1986A_LAPTOP),
+ SND_PCI_QUIRK(0x1043, 0x81b3, "ASUS P5", AD1986A_3STACK),
+ SND_PCI_QUIRK(0x1043, 0x81cb, "ASUS M2N", AD1986A_3STACK),
+ SND_PCI_QUIRK(0x1043, 0x8234, "ASUS M2N", AD1986A_3STACK),
+ SND_PCI_QUIRK(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, 0xc504, "Samsung Q35", AD1986A_3STACK),
+ SND_PCI_QUIRK(0x144d, 0xc027, "Samsung Q1", AD1986A_ULTRA),
+ SND_PCI_QUIRK(0x17aa, 0x1017, "Lenovo A60", AD1986A_3STACK),
+ SND_PCI_QUIRK(0x17aa, 0x2066, "Lenovo N100", AD1986A_LAPTOP_EAPD),
+ SND_PCI_QUIRK(0x17c0, 0x2017, "Samsung M50", AD1986A_LAPTOP),
{}
};
@@ -861,7 +870,9 @@ static int patch_ad1986a(struct hda_codec *codec)
codec->patch_ops = ad198x_patch_ops;
/* override some parameters */
- board_config = snd_hda_check_board_config(codec, ad1986a_cfg_tbl);
+ board_config = snd_hda_check_board_config(codec, AD1986A_MODELS,
+ ad1986a_models,
+ ad1986a_cfg_tbl);
switch (board_config) {
case AD1986A_3STACK:
spec->num_mixers = 2;
@@ -891,6 +902,15 @@ static int patch_ad1986a(struct hda_codec *codec)
spec->multiout.dig_out_nid = 0;
spec->input_mux = &ad1986a_laptop_eapd_capture_source;
break;
+ case AD1986A_ULTRA:
+ spec->mixers[0] = ad1986a_laptop_eapd_mixers;
+ spec->num_init_verbs = 2;
+ spec->init_verbs[1] = ad1986a_ultra_init;
+ spec->multiout.max_channels = 2;
+ spec->multiout.num_dacs = 1;
+ spec->multiout.dac_nids = ad1986a_laptop_dac_nids;
+ spec->multiout.dig_out_nid = 0;
+ break;
}
return 0;
@@ -1391,20 +1411,27 @@ static struct hda_input_mux ad1981_thinkpad_capture_source = {
};
/* models */
-enum { AD1981_BASIC, AD1981_HP, AD1981_THINKPAD };
+enum {
+ AD1981_BASIC,
+ AD1981_HP,
+ AD1981_THINKPAD,
+ AD1981_MODELS
+};
+
+static const char *ad1981_models[AD1981_MODELS] = {
+ [AD1981_HP] = "hp",
+ [AD1981_THINKPAD] = "thinkpad",
+ [AD1981_BASIC] = "basic",
+};
-static struct hda_board_config ad1981_cfg_tbl[] = {
- { .modelname = "hp", .config = AD1981_HP },
+static struct snd_pci_quirk ad1981_cfg_tbl[] = {
/* All HP models */
- { .pci_subvendor = 0x103c, .config = AD1981_HP },
- { .pci_subvendor = 0x30b0, .pci_subdevice = 0x103c,
- .config = AD1981_HP }, /* HP nx6320 (reversed SSID, H/W bug) */
- { .modelname = "thinkpad", .config = AD1981_THINKPAD },
+ SND_PCI_QUIRK(0x103c, 0, "HP nx", AD1981_HP),
+ /* HP nx6320 (reversed SSID, H/W bug) */
+ SND_PCI_QUIRK(0x30b0, 0x103c, "HP nx6320", AD1981_HP),
/* Lenovo Thinkpad T60/X60/Z6xx */
- { .pci_subvendor = 0x17aa, .config = AD1981_THINKPAD },
- { .pci_subvendor = 0x1014, .pci_subdevice = 0x0597,
- .config = AD1981_THINKPAD }, /* Z60m/t */
- { .modelname = "basic", .config = AD1981_BASIC },
+ SND_PCI_QUIRK(0x17aa, 0, "Lenovo Thinkpad", AD1981_THINKPAD),
+ SND_PCI_QUIRK(0x1014, 0x0597, "Lenovo Z60", AD1981_THINKPAD),
{}
};
@@ -1437,7 +1464,9 @@ static int patch_ad1981(struct hda_codec *codec)
codec->patch_ops = ad198x_patch_ops;
/* override some parameters */
- board_config = snd_hda_check_board_config(codec, ad1981_cfg_tbl);
+ board_config = snd_hda_check_board_config(codec, AD1981_MODELS,
+ ad1981_models,
+ ad1981_cfg_tbl);
switch (board_config) {
case AD1981_HP:
spec->mixers[0] = ad1981_hp_mixers;
@@ -2565,15 +2594,14 @@ static int ad1988_auto_init(struct hda_codec *codec)
/*
*/
-static struct hda_board_config ad1988_cfg_tbl[] = {
- { .modelname = "6stack", .config = AD1988_6STACK },
- { .modelname = "6stack-dig", .config = AD1988_6STACK_DIG },
- { .modelname = "3stack", .config = AD1988_3STACK },
- { .modelname = "3stack-dig", .config = AD1988_3STACK_DIG },
- { .modelname = "laptop", .config = AD1988_LAPTOP },
- { .modelname = "laptop-dig", .config = AD1988_LAPTOP_DIG },
- { .modelname = "auto", .config = AD1988_AUTO },
- {}
+static const char *ad1988_models[AD1988_MODEL_LAST] = {
+ [AD1988_6STACK] = "6stack",
+ [AD1988_6STACK_DIG] = "6stack-dig",
+ [AD1988_3STACK] = "3stack",
+ [AD1988_3STACK_DIG] = "3stack-dig",
+ [AD1988_LAPTOP] = "laptop",
+ [AD1988_LAPTOP_DIG] = "laptop-dig",
+ [AD1988_AUTO] = "auto",
};
static int patch_ad1988(struct hda_codec *codec)
@@ -2591,8 +2619,9 @@ static int patch_ad1988(struct hda_codec *codec)
if (is_rev2(codec))
snd_printk(KERN_INFO "patch_analog: AD1988A rev.2 is detected, enable workarounds\n");
- board_config = snd_hda_check_board_config(codec, ad1988_cfg_tbl);
- if (board_config < 0 || board_config >= AD1988_MODEL_LAST) {
+ board_config = snd_hda_check_board_config(codec, AD1988_MODEL_LAST,
+ ad1988_models, NULL);
+ if (board_config < 0) {
printk(KERN_INFO "hda_codec: Unknown model for AD1988, trying auto-probe from BIOS...\n");
board_config = AD1988_AUTO;
}
diff --git a/sound/pci/hda/patch_cmedia.c b/sound/pci/hda/patch_cmedia.c
index d38ce22507ae..5b9d3a31a1ae 100644
--- a/sound/pci/hda/patch_cmedia.c
+++ b/sound/pci/hda/patch_cmedia.c
@@ -40,6 +40,7 @@ enum {
CMI_FULL_DIG, /* back 6-jack + front-panel 2-jack + digital I/O */
CMI_ALLOUT, /* back 5-jack + front-panel 2-jack + digital out */
CMI_AUTO, /* let driver guess it */
+ CMI_MODELS
};
struct cmi_spec {
@@ -603,14 +604,17 @@ static void cmi9880_free(struct hda_codec *codec)
/*
*/
-static struct hda_board_config cmi9880_cfg_tbl[] = {
- { .modelname = "minimal", .config = CMI_MINIMAL },
- { .modelname = "min_fp", .config = CMI_MIN_FP },
- { .modelname = "full", .config = CMI_FULL },
- { .modelname = "full_dig", .config = CMI_FULL_DIG },
- { .pci_subvendor = 0x1043, .pci_subdevice = 0x813d, .config = CMI_FULL_DIG }, /* ASUS P5AD2 */
- { .modelname = "allout", .config = CMI_ALLOUT },
- { .modelname = "auto", .config = CMI_AUTO },
+static const char *cmi9880_models[CMI_MODELS] = {
+ [CMI_MINIMAL] = "minimal",
+ [CMI_MIN_FP] = "min_fp",
+ [CMI_FULL] = "full",
+ [CMI_FULL_DIG] = "full_dig",
+ [CMI_ALLOUT] = "allout",
+ [CMI_AUTO] = "auto",
+};
+
+static struct snd_pci_quirk cmi9880_cfg_tbl[] = {
+ SND_PCI_QUIRK(0x1043, 0x813d, "ASUS P5AD2", CMI_FULL_DIG),
{} /* terminator */
};
@@ -633,7 +637,9 @@ static int patch_cmi9880(struct hda_codec *codec)
return -ENOMEM;
codec->spec = spec;
- spec->board_config = snd_hda_check_board_config(codec, cmi9880_cfg_tbl);
+ spec->board_config = snd_hda_check_board_config(codec, CMI_MODELS,
+ cmi9880_models,
+ cmi9880_cfg_tbl);
if (spec->board_config < 0) {
snd_printdd(KERN_INFO "hda_codec: Unknown model for CMI9880\n");
spec->board_config = CMI_AUTO; /* try everything */
diff --git a/sound/pci/hda/patch_conexant.c b/sound/pci/hda/patch_conexant.c
new file mode 100644
index 000000000000..73f4668238c6
--- /dev/null
+++ b/sound/pci/hda/patch_conexant.c
@@ -0,0 +1,1311 @@
+/*
+ * HD audio interface patch for Conexant HDA audio codec
+ *
+ * Copyright (c) 2006 Pototskiy Akex <alex.pototskiy@gmail.com>
+ * Takashi Iwai <tiwai@suse.de>
+ * Tobin Davis <tdavis@dsl-only.net>
+ *
+ * This driver is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * 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 <sound/driver.h>
+#include <linux/init.h>
+#include <linux/delay.h>
+#include <linux/slab.h>
+#include <linux/pci.h>
+#include <sound/core.h>
+#include "hda_codec.h"
+#include "hda_local.h"
+
+#define CXT_PIN_DIR_IN 0x00
+#define CXT_PIN_DIR_OUT 0x01
+#define CXT_PIN_DIR_INOUT 0x02
+#define CXT_PIN_DIR_IN_NOMICBIAS 0x03
+#define CXT_PIN_DIR_INOUT_NOMICBIAS 0x04
+
+#define CONEXANT_HP_EVENT 0x37
+#define CONEXANT_MIC_EVENT 0x38
+
+
+
+struct conexant_spec {
+
+ struct snd_kcontrol_new *mixers[5];
+ int num_mixers;
+
+ const struct hda_verb *init_verbs[5]; /* initialization verbs
+ * don't forget NULL
+ * termination!
+ */
+ unsigned int num_init_verbs;
+
+ /* playback */
+ struct hda_multi_out multiout; /* playback set-up
+ * max_channels, dacs must be set
+ * dig_out_nid and hp_nid are optional
+ */
+ unsigned int cur_eapd;
+ unsigned int need_dac_fix;
+
+ /* capture */
+ unsigned int num_adc_nids;
+ hda_nid_t *adc_nids;
+ hda_nid_t dig_in_nid; /* digital-in NID; optional */
+
+ /* capture source */
+ const struct hda_input_mux *input_mux;
+ hda_nid_t *capsrc_nids;
+ unsigned int cur_mux[3];
+
+ /* channel model */
+ const struct hda_channel_mode *channel_mode;
+ int num_channel_mode;
+
+ /* PCM information */
+ struct hda_pcm pcm_rec[2]; /* used in build_pcms() */
+
+ struct mutex amp_mutex; /* PCM volume/mute control mutex */
+ unsigned int spdif_route;
+
+ /* 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[4];
+
+};
+
+static int conexant_playback_pcm_open(struct hda_pcm_stream *hinfo,
+ struct hda_codec *codec,
+ struct snd_pcm_substream *substream)
+{
+ struct conexant_spec *spec = codec->spec;
+ return snd_hda_multi_out_analog_open(codec, &spec->multiout, substream);
+}
+
+static int conexant_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 conexant_spec *spec = codec->spec;
+ return snd_hda_multi_out_analog_prepare(codec, &spec->multiout,
+ stream_tag,
+ format, substream);
+}
+
+static int conexant_playback_pcm_cleanup(struct hda_pcm_stream *hinfo,
+ struct hda_codec *codec,
+ struct snd_pcm_substream *substream)
+{
+ struct conexant_spec *spec = codec->spec;
+ return snd_hda_multi_out_analog_cleanup(codec, &spec->multiout);
+}
+
+/*
+ * Digital out
+ */
+static int conexant_dig_playback_pcm_open(struct hda_pcm_stream *hinfo,
+ struct hda_codec *codec,
+ struct snd_pcm_substream *substream)
+{
+ struct conexant_spec *spec = codec->spec;
+ return snd_hda_multi_out_dig_open(codec, &spec->multiout);
+}
+
+static int conexant_dig_playback_pcm_close(struct hda_pcm_stream *hinfo,
+ struct hda_codec *codec,
+ struct snd_pcm_substream *substream)
+{
+ struct conexant_spec *spec = codec->spec;
+ return snd_hda_multi_out_dig_close(codec, &spec->multiout);
+}
+
+/*
+ * Analog capture
+ */
+static int conexant_capture_pcm_prepare(struct hda_pcm_stream *hinfo,
+ struct hda_codec *codec,
+ unsigned int stream_tag,
+ unsigned int format,
+ struct snd_pcm_substream *substream)
+{
+ struct conexant_spec *spec = codec->spec;
+ snd_hda_codec_setup_stream(codec, spec->adc_nids[substream->number],
+ stream_tag, 0, format);
+ return 0;
+}
+
+static int conexant_capture_pcm_cleanup(struct hda_pcm_stream *hinfo,
+ struct hda_codec *codec,
+ struct snd_pcm_substream *substream)
+{
+ struct conexant_spec *spec = codec->spec;
+ snd_hda_codec_setup_stream(codec, spec->adc_nids[substream->number],
+ 0, 0, 0);
+ return 0;
+}
+
+
+
+static struct hda_pcm_stream conexant_pcm_analog_playback = {
+ .substreams = 1,
+ .channels_min = 2,
+ .channels_max = 2,
+ .nid = 0, /* fill later */
+ .ops = {
+ .open = conexant_playback_pcm_open,
+ .prepare = conexant_playback_pcm_prepare,
+ .cleanup = conexant_playback_pcm_cleanup
+ },
+};
+
+static struct hda_pcm_stream conexant_pcm_analog_capture = {
+ .substreams = 1,
+ .channels_min = 2,
+ .channels_max = 2,
+ .nid = 0, /* fill later */
+ .ops = {
+ .prepare = conexant_capture_pcm_prepare,
+ .cleanup = conexant_capture_pcm_cleanup
+ },
+};
+
+
+static struct hda_pcm_stream conexant_pcm_digital_playback = {
+ .substreams = 1,
+ .channels_min = 2,
+ .channels_max = 2,
+ .nid = 0, /* fill later */
+ .ops = {
+ .open = conexant_dig_playback_pcm_open,
+ .close = conexant_dig_playback_pcm_close
+ },
+};
+
+static struct hda_pcm_stream conexant_pcm_digital_capture = {
+ .substreams = 1,
+ .channels_min = 2,
+ .channels_max = 2,
+ /* NID is set in alc_build_pcms */
+};
+
+static int conexant_build_pcms(struct hda_codec *codec)
+{
+ struct conexant_spec *spec = codec->spec;
+ struct hda_pcm *info = spec->pcm_rec;
+
+ codec->num_pcms = 1;
+ codec->pcm_info = info;
+
+ info->name = "CONEXANT Analog";
+ info->stream[SNDRV_PCM_STREAM_PLAYBACK] = conexant_pcm_analog_playback;
+ info->stream[SNDRV_PCM_STREAM_PLAYBACK].channels_max =
+ spec->multiout.max_channels;
+ info->stream[SNDRV_PCM_STREAM_PLAYBACK].nid =
+ spec->multiout.dac_nids[0];
+ info->stream[SNDRV_PCM_STREAM_CAPTURE] = conexant_pcm_analog_capture;
+ info->stream[SNDRV_PCM_STREAM_CAPTURE].substreams = spec->num_adc_nids;
+ info->stream[SNDRV_PCM_STREAM_CAPTURE].nid = spec->adc_nids[0];
+
+ if (spec->multiout.dig_out_nid) {
+ info++;
+ codec->num_pcms++;
+ info->name = "Conexant Digital";
+ info->stream[SNDRV_PCM_STREAM_PLAYBACK] =
+ conexant_pcm_digital_playback;
+ info->stream[SNDRV_PCM_STREAM_PLAYBACK].nid =
+ spec->multiout.dig_out_nid;
+ if (spec->dig_in_nid) {
+ info->stream[SNDRV_PCM_STREAM_CAPTURE] =
+ conexant_pcm_digital_capture;
+ info->stream[SNDRV_PCM_STREAM_CAPTURE].nid =
+ spec->dig_in_nid;
+ }
+ }
+
+ return 0;
+}
+
+static int conexant_mux_enum_info(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_info *uinfo)
+{
+ struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
+ struct conexant_spec *spec = codec->spec;
+
+ return snd_hda_input_mux_info(spec->input_mux, uinfo);
+}
+
+static int conexant_mux_enum_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
+ struct conexant_spec *spec = codec->spec;
+ unsigned int adc_idx = snd_ctl_get_ioffidx(kcontrol, &ucontrol->id);
+
+ ucontrol->value.enumerated.item[0] = spec->cur_mux[adc_idx];
+ return 0;
+}
+
+static int conexant_mux_enum_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
+ struct conexant_spec *spec = codec->spec;
+ unsigned int adc_idx = snd_ctl_get_ioffidx(kcontrol, &ucontrol->id);
+
+ return snd_hda_input_mux_put(codec, spec->input_mux, ucontrol,
+ spec->capsrc_nids[adc_idx],
+ &spec->cur_mux[adc_idx]);
+}
+
+static int conexant_init(struct hda_codec *codec)
+{
+ struct conexant_spec *spec = codec->spec;
+ int i;
+
+ for (i = 0; i < spec->num_init_verbs; i++)
+ snd_hda_sequence_write(codec, spec->init_verbs[i]);
+ return 0;
+}
+
+static 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);
+}
+
+#ifdef CONFIG_PM
+static int conexant_resume(struct hda_codec *codec)
+{
+ struct conexant_spec *spec = codec->spec;
+ int i;
+
+ codec->patch_ops.init(codec);
+ for (i = 0; i < spec->num_mixers; i++)
+ snd_hda_resume_ctls(codec, spec->mixers[i]);
+ if (spec->multiout.dig_out_nid)
+ snd_hda_resume_spdif_out(codec);
+ if (spec->dig_in_nid)
+ snd_hda_resume_spdif_in(codec);
+ return 0;
+}
+#endif
+
+static int conexant_build_controls(struct hda_codec *codec)
+{
+ struct conexant_spec *spec = codec->spec;
+ unsigned int i;
+ int err;
+
+ for (i = 0; i < spec->num_mixers; i++) {
+ err = snd_hda_add_new_ctls(codec, spec->mixers[i]);
+ if (err < 0)
+ return err;
+ }
+ if (spec->multiout.dig_out_nid) {
+ err = snd_hda_create_spdif_out_ctls(codec,
+ spec->multiout.dig_out_nid);
+ if (err < 0)
+ return err;
+ }
+ if (spec->dig_in_nid) {
+ err = snd_hda_create_spdif_in_ctls(codec,spec->dig_in_nid);
+ if (err < 0)
+ return err;
+ }
+ return 0;
+}
+
+static struct hda_codec_ops conexant_patch_ops = {
+ .build_controls = conexant_build_controls,
+ .build_pcms = conexant_build_pcms,
+ .init = conexant_init,
+ .free = conexant_free,
+#ifdef CONFIG_PM
+ .resume = conexant_resume,
+#endif
+};
+
+/*
+ * EAPD control
+ * the private value = nid | (invert << 8)
+ */
+
+static int conexant_eapd_info(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_info *uinfo)
+{
+ uinfo->type = SNDRV_CTL_ELEM_TYPE_BOOLEAN;
+ uinfo->count = 1;
+ uinfo->value.integer.min = 0;
+ uinfo->value.integer.max = 1;
+ return 0;
+}
+
+static int conexant_eapd_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
+ struct conexant_spec *spec = codec->spec;
+ int invert = (kcontrol->private_value >> 8) & 1;
+ if (invert)
+ ucontrol->value.integer.value[0] = !spec->cur_eapd;
+ else
+ ucontrol->value.integer.value[0] = spec->cur_eapd;
+ return 0;
+}
+
+static int conexant_eapd_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
+ struct conexant_spec *spec = codec->spec;
+ int invert = (kcontrol->private_value >> 8) & 1;
+ hda_nid_t nid = kcontrol->private_value & 0xff;
+ unsigned int eapd;
+ eapd = ucontrol->value.integer.value[0];
+ if (invert)
+ eapd = !eapd;
+ if (eapd == spec->cur_eapd && !codec->in_resume)
+ return 0;
+ spec->cur_eapd = eapd;
+ snd_hda_codec_write(codec, nid,
+ 0, AC_VERB_SET_EAPD_BTLENABLE,
+ eapd ? 0x02 : 0x00);
+ return 1;
+}
+
+/* controls for test mode */
+#ifdef CONFIG_SND_DEBUG
+
+static int conexant_ch_mode_info(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_info *uinfo)
+{
+ struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
+ struct conexant_spec *spec = codec->spec;
+ return snd_hda_ch_mode_info(codec, uinfo, spec->channel_mode,
+ spec->num_channel_mode);
+}
+
+static int conexant_ch_mode_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
+ struct conexant_spec *spec = codec->spec;
+ return snd_hda_ch_mode_get(codec, ucontrol, spec->channel_mode,
+ spec->num_channel_mode,
+ spec->multiout.max_channels);
+}
+
+static int conexant_ch_mode_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
+ struct conexant_spec *spec = codec->spec;
+ int err = snd_hda_ch_mode_put(codec, ucontrol, spec->channel_mode,
+ spec->num_channel_mode,
+ &spec->multiout.max_channels);
+ if (err >= 0 && spec->need_dac_fix)
+ spec->multiout.num_dacs = spec->multiout.max_channels / 2;
+ return err;
+}
+
+#define CXT_PIN_MODE(xname, nid, dir) \
+ { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, .index = 0, \
+ .info = conexant_ch_mode_info, \
+ .get = conexant_ch_mode_get, \
+ .put = conexant_ch_mode_put, \
+ .private_value = nid | (dir<<16) }
+
+static int cxt_gpio_data_info(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_info *uinfo)
+{
+ uinfo->type = SNDRV_CTL_ELEM_TYPE_BOOLEAN;
+ uinfo->count = 1;
+ uinfo->value.integer.min = 0;
+ uinfo->value.integer.max = 1;
+ return 0;
+}
+
+static int cxt_gpio_data_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
+ hda_nid_t nid = kcontrol->private_value & 0xffff;
+ unsigned char mask = (kcontrol->private_value >> 16) & 0xff;
+ long *valp = ucontrol->value.integer.value;
+ unsigned int val = snd_hda_codec_read(codec, nid, 0,
+ AC_VERB_GET_GPIO_DATA, 0x00);
+
+ *valp = (val & mask) != 0;
+ return 0;
+}
+
+static int cxt_gpio_data_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
+ hda_nid_t nid = kcontrol->private_value & 0xffff;
+ unsigned char mask = (kcontrol->private_value >> 16) & 0xff;
+ long val = *ucontrol->value.integer.value;
+ unsigned int gpio_data = snd_hda_codec_read(codec, nid, 0,
+ AC_VERB_GET_GPIO_DATA,
+ 0x00);
+ unsigned int old_data = gpio_data;
+
+ /* Set/unset the masked GPIO bit(s) as needed */
+ if (val == 0)
+ gpio_data &= ~mask;
+ else
+ gpio_data |= mask;
+ if (gpio_data == old_data && !codec->in_resume)
+ return 0;
+ snd_hda_codec_write(codec, nid, 0, AC_VERB_SET_GPIO_DATA, gpio_data);
+ return 1;
+}
+
+#define CXT_GPIO_DATA_SWITCH(xname, nid, mask) \
+ { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, .index = 0, \
+ .info = cxt_gpio_data_info, \
+ .get = cxt_gpio_data_get, \
+ .put = cxt_gpio_data_put, \
+ .private_value = nid | (mask<<16) }
+
+static int cxt_spdif_ctrl_info(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_info *uinfo)
+{
+ uinfo->type = SNDRV_CTL_ELEM_TYPE_BOOLEAN;
+ uinfo->count = 1;
+ uinfo->value.integer.min = 0;
+ uinfo->value.integer.max = 1;
+ return 0;
+}
+
+static int cxt_spdif_ctrl_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
+ hda_nid_t nid = kcontrol->private_value & 0xffff;
+ unsigned char mask = (kcontrol->private_value >> 16) & 0xff;
+ long *valp = ucontrol->value.integer.value;
+ unsigned int val = snd_hda_codec_read(codec, nid, 0,
+ AC_VERB_GET_DIGI_CONVERT, 0x00);
+
+ *valp = (val & mask) != 0;
+ return 0;
+}
+
+static int cxt_spdif_ctrl_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
+ hda_nid_t nid = kcontrol->private_value & 0xffff;
+ unsigned char mask = (kcontrol->private_value >> 16) & 0xff;
+ long val = *ucontrol->value.integer.value;
+ unsigned int ctrl_data = snd_hda_codec_read(codec, nid, 0,
+ AC_VERB_GET_DIGI_CONVERT,
+ 0x00);
+ unsigned int old_data = ctrl_data;
+
+ /* Set/unset the masked control bit(s) as needed */
+ if (val == 0)
+ ctrl_data &= ~mask;
+ else
+ ctrl_data |= mask;
+ if (ctrl_data == old_data && !codec->in_resume)
+ return 0;
+ snd_hda_codec_write(codec, nid, 0, AC_VERB_SET_DIGI_CONVERT_1,
+ ctrl_data);
+ return 1;
+}
+
+#define CXT_SPDIF_CTRL_SWITCH(xname, nid, mask) \
+ { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, .index = 0, \
+ .info = cxt_spdif_ctrl_info, \
+ .get = cxt_spdif_ctrl_get, \
+ .put = cxt_spdif_ctrl_put, \
+ .private_value = nid | (mask<<16) }
+
+#endif /* CONFIG_SND_DEBUG */
+
+/* Conexant 5045 specific */
+
+static hda_nid_t cxt5045_dac_nids[1] = { 0x19 };
+static hda_nid_t cxt5045_adc_nids[1] = { 0x1a };
+static hda_nid_t cxt5045_capsrc_nids[1] = { 0x1a };
+#define CXT5045_SPDIF_OUT 0x13
+
+static struct hda_channel_mode cxt5045_modes[1] = {
+ { 2, NULL },
+};
+
+static struct hda_input_mux cxt5045_capture_source = {
+ .num_items = 2,
+ .items = {
+ { "ExtMic", 0x1 },
+ { "LineIn", 0x2 },
+ }
+};
+
+/* turn on/off EAPD (+ mute HP) as a master switch */
+static int cxt5045_hp_master_sw_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
+ struct conexant_spec *spec = codec->spec;
+
+ if (!conexant_eapd_put(kcontrol, ucontrol))
+ return 0;
+
+ /* toggle HP mute appropriately */
+ snd_hda_codec_amp_update(codec, 0x11, 0, HDA_OUTPUT, 0,
+ 0x80, spec->cur_eapd ? 0 : 0x80);
+ snd_hda_codec_amp_update(codec, 0x11, 1, HDA_OUTPUT, 0,
+ 0x80, spec->cur_eapd ? 0 : 0x80);
+ return 1;
+}
+
+/* bind volumes of both NID 0x10 and 0x11 */
+static int cxt5045_hp_master_vol_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
+ long *valp = ucontrol->value.integer.value;
+ int change;
+
+ change = snd_hda_codec_amp_update(codec, 0x10, 0, HDA_OUTPUT, 0,
+ 0x7f, valp[0] & 0x7f);
+ change |= snd_hda_codec_amp_update(codec, 0x10, 1, HDA_OUTPUT, 0,
+ 0x7f, valp[1] & 0x7f);
+ snd_hda_codec_amp_update(codec, 0x11, 0, HDA_OUTPUT, 0,
+ 0x7f, valp[0] & 0x7f);
+ snd_hda_codec_amp_update(codec, 0x11, 1, HDA_OUTPUT, 0,
+ 0x7f, valp[1] & 0x7f);
+ return change;
+}
+
+
+/* mute internal speaker if HP is plugged */
+static void cxt5045_hp_automute(struct hda_codec *codec)
+{
+ unsigned int present;
+
+ present = snd_hda_codec_read(codec, 0x11, 0,
+ AC_VERB_GET_PIN_SENSE, 0) & 0x80000000;
+ snd_hda_codec_amp_update(codec, 0x10, 0, HDA_OUTPUT, 0,
+ 0x80, present ? 0x80 : 0);
+ snd_hda_codec_amp_update(codec, 0x10, 1, HDA_OUTPUT, 0,
+ 0x80, present ? 0x80 : 0);
+}
+
+/* unsolicited event for HP jack sensing */
+static void cxt5045_hp_unsol_event(struct hda_codec *codec,
+ unsigned int res)
+{
+ res >>= 26;
+ switch (res) {
+ case CONEXANT_HP_EVENT:
+ cxt5045_hp_automute(codec);
+ break;
+ }
+}
+
+static struct snd_kcontrol_new cxt5045_mixers[] = {
+ {
+ .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+ .name = "Capture Source",
+ .info = conexant_mux_enum_info,
+ .get = conexant_mux_enum_get,
+ .put = conexant_mux_enum_put
+ },
+ HDA_CODEC_VOLUME("Mic Bypass Capture Volume", 0x17, 0x02, HDA_INPUT),
+ HDA_CODEC_MUTE("Mic Bypass Capture Switch", 0x17, 0x02, HDA_INPUT),
+ HDA_CODEC_VOLUME("Capture Volume", 0x1a, 0x02, HDA_INPUT),
+ HDA_CODEC_MUTE("Capture Switch", 0x1a, 0x02, HDA_INPUT),
+ {
+ .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+ .name = "Master Playback Volume",
+ .info = snd_hda_mixer_amp_volume_info,
+ .get = snd_hda_mixer_amp_volume_get,
+ .put = cxt5045_hp_master_vol_put,
+ .private_value = HDA_COMPOSE_AMP_VAL(0x11, 3, 0, HDA_OUTPUT),
+ },
+ {
+ .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+ .name = "Master Playback Switch",
+ .info = conexant_eapd_info,
+ .get = conexant_eapd_get,
+ .put = cxt5045_hp_master_sw_put,
+ .private_value = 0x11,
+ },
+
+ {}
+};
+
+static struct hda_verb cxt5045_init_verbs[] = {
+ /* Line in, Mic */
+ {0x12, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN },
+ {0x14, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN|AC_PINCTL_VREF_50 },
+ /* HP, Amp */
+ {0x11, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT },
+ {0x1A, AC_VERB_SET_CONNECT_SEL,0x01},
+ {0x1A, AC_VERB_SET_AMP_GAIN_MUTE,
+ AC_AMP_SET_OUTPUT|AC_AMP_SET_RIGHT|AC_AMP_SET_LEFT|0x00},
+ {0x1A, AC_VERB_SET_AMP_GAIN_MUTE,
+ AC_AMP_SET_OUTPUT|AC_AMP_SET_RIGHT|AC_AMP_SET_LEFT|0x03},
+ /* Record selector: Front mic */
+ {0x14, AC_VERB_SET_CONNECT_SEL,0x03},
+ {0x17, AC_VERB_SET_AMP_GAIN_MUTE,
+ AC_AMP_SET_INPUT|AC_AMP_SET_RIGHT|AC_AMP_SET_LEFT|0x17},
+ /* SPDIF route: PCM */
+ { 0x13, AC_VERB_SET_CONNECT_SEL, 0x0 },
+ /* pin sensing on HP and Mic jacks */
+ {0x11, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | CONEXANT_HP_EVENT},
+ /* EAPD */
+ {0x10, AC_VERB_SET_EAPD_BTLENABLE, 0x0 }, /* default on */
+ { } /* end */
+};
+
+#ifdef CONFIG_SND_DEBUG
+/* Test configuration for debugging, modelled after the ALC260 test
+ * configuration.
+ */
+static struct hda_input_mux cxt5045_test_capture_source = {
+ .num_items = 5,
+ .items = {
+ { "MIXER", 0x0 },
+ { "MIC1 pin", 0x1 },
+ { "LINE1 pin", 0x2 },
+ { "HP-OUT pin", 0x3 },
+ { "CD pin", 0x4 },
+ },
+};
+
+static struct snd_kcontrol_new cxt5045_test_mixer[] = {
+
+ /* Output controls */
+ HDA_CODEC_VOLUME("OutAmp-1 Volume", 0x19, 0x00, HDA_OUTPUT),
+ HDA_CODEC_MUTE("OutAmp-1 Switch", 0x19,0x00, HDA_OUTPUT),
+ HDA_CODEC_VOLUME("Speaker Playback Volume", 0x10, 0x0, HDA_OUTPUT),
+ HDA_CODEC_MUTE("Speaker Playback Switch", 0x10, 0x0, HDA_OUTPUT),
+
+ /* Modes for retasking pin widgets */
+ CXT_PIN_MODE("HP-OUT pin mode", 0x11, CXT_PIN_DIR_INOUT),
+ CXT_PIN_MODE("LINE1 pin mode", 0x12, CXT_PIN_DIR_INOUT),
+
+ /* Loopback mixer controls */
+ HDA_CODEC_VOLUME("MIC1 Playback Volume", 0x17, 0x01, HDA_INPUT),
+ HDA_CODEC_MUTE("MIC1 Playback Switch", 0x17, 0x01, HDA_INPUT),
+ HDA_CODEC_VOLUME("LINE loopback Playback Volume", 0x17, 0x02, HDA_INPUT),
+ HDA_CODEC_MUTE("LINE loopback Playback Switch", 0x17, 0x02, HDA_INPUT),
+ HDA_CODEC_VOLUME("HP-OUT loopback Playback Volume", 0x17, 0x03, HDA_INPUT),
+ HDA_CODEC_MUTE("HP-OUT loopback Playback Switch", 0x17, 0x03, HDA_INPUT),
+ HDA_CODEC_VOLUME("CD Playback Volume", 0x17, 0x04, HDA_INPUT),
+ HDA_CODEC_MUTE("CD Playback Switch", 0x17, 0x04, HDA_INPUT),
+
+ /* Controls for GPIO pins, assuming they exist and are configured as outputs */
+ CXT_GPIO_DATA_SWITCH("GPIO pin 0", 0x01, 0x01),
+#if 0 /* limit this to one GPIO pin for now */
+ CXT_GPIO_DATA_SWITCH("GPIO pin 1", 0x01, 0x02),
+ CXT_GPIO_DATA_SWITCH("GPIO pin 2", 0x01, 0x04),
+ CXT_GPIO_DATA_SWITCH("GPIO pin 3", 0x01, 0x08),
+#endif
+ CXT_SPDIF_CTRL_SWITCH("SPDIF Playback Switch", 0x13, 0x01),
+
+ HDA_CODEC_VOLUME("Capture Volume", 0x17, 0x0, HDA_OUTPUT),
+ HDA_CODEC_MUTE("Capture Switch", 0x17, 0x0, HDA_OUTPUT),
+ {
+ .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+ .name = "Input Source",
+ .info = conexant_mux_enum_info,
+ .get = conexant_mux_enum_get,
+ .put = conexant_mux_enum_put,
+ },
+
+ { } /* end */
+};
+
+static struct hda_verb cxt5045_test_init_verbs[] = {
+ /* Enable all GPIOs as outputs with an initial value of 0 */
+ {0x01, AC_VERB_SET_GPIO_DIRECTION, 0x0f},
+ {0x01, AC_VERB_SET_GPIO_DATA, 0x00},
+ {0x01, AC_VERB_SET_GPIO_MASK, 0x0f},
+
+ /* Enable retasking pins as output, initially without power amp */
+ {0x12, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
+ {0x11, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
+
+ /* Disable digital (SPDIF) pins initially, but users can enable
+ * them via a mixer switch. In the case of SPDIF-out, this initverb
+ * payload also sets the generation to 0, output to be in "consumer"
+ * PCM format, copyright asserted, no pre-emphasis and no validity
+ * control.
+ */
+ {0x13, AC_VERB_SET_DIGI_CONVERT_1, 0},
+
+ /* Start with output sum widgets muted and their output gains at min */
+ {0x17, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
+ {0x17, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
+
+ /* Unmute retasking pin widget output buffers since the default
+ * state appears to be output. As the pin mode is changed by the
+ * user the pin mode control will take care of enabling the pin's
+ * input/output buffers as needed.
+ */
+ {0x12, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
+ {0x11, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
+
+ /* Mute capture amp left and right */
+ {0x1a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
+
+ /* Set ADC connection select to match default mixer setting (mic1
+ * pin)
+ */
+ {0x1a, AC_VERB_SET_CONNECT_SEL, 0x00},
+
+ /* Mute all inputs to mixer widget (even unconnected ones) */
+ {0x17, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)}, /* Mixer pin */
+ {0x17, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)}, /* Mic1 pin */
+ {0x17, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(2)}, /* Line pin */
+ {0x17, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(3)}, /* HP pin */
+ {0x17, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(4)}, /* CD pin */
+
+ { }
+};
+#endif
+
+
+/* initialize jack-sensing, too */
+static int cxt5045_init(struct hda_codec *codec)
+{
+ conexant_init(codec);
+ cxt5045_hp_automute(codec);
+ return 0;
+}
+
+
+enum {
+ CXT5045_LAPTOP, /* Laptops w/ EAPD support */
+#ifdef CONFIG_SND_DEBUG
+ CXT5045_TEST,
+#endif
+ CXT5045_MODELS
+};
+
+static const char *cxt5045_models[CXT5045_MODELS] = {
+ [CXT5045_LAPTOP] = "laptop",
+#ifdef CONFIG_SND_DEBUG
+ [CXT5045_TEST] = "test",
+#endif
+};
+
+static struct snd_pci_quirk cxt5045_cfg_tbl[] = {
+ SND_PCI_QUIRK(0x103c, 0x30b7, "HP DV6000Z", CXT5045_LAPTOP),
+ {}
+};
+
+static int patch_cxt5045(struct hda_codec *codec)
+{
+ struct conexant_spec *spec;
+ int board_config;
+
+ spec = kzalloc(sizeof(*spec), GFP_KERNEL);
+ if (!spec)
+ return -ENOMEM;
+ mutex_init(&spec->amp_mutex);
+ codec->spec = spec;
+
+ spec->multiout.max_channels = 2;
+ spec->multiout.num_dacs = ARRAY_SIZE(cxt5045_dac_nids);
+ spec->multiout.dac_nids = cxt5045_dac_nids;
+ spec->multiout.dig_out_nid = CXT5045_SPDIF_OUT;
+ spec->num_adc_nids = 1;
+ spec->adc_nids = cxt5045_adc_nids;
+ spec->capsrc_nids = cxt5045_capsrc_nids;
+ spec->input_mux = &cxt5045_capture_source;
+ spec->num_mixers = 1;
+ spec->mixers[0] = cxt5045_mixers;
+ spec->num_init_verbs = 1;
+ spec->init_verbs[0] = cxt5045_init_verbs;
+ spec->spdif_route = 0;
+ spec->num_channel_mode = ARRAY_SIZE(cxt5045_modes),
+ spec->channel_mode = cxt5045_modes,
+
+
+ codec->patch_ops = conexant_patch_ops;
+ codec->patch_ops.unsol_event = cxt5045_hp_unsol_event;
+
+ board_config = snd_hda_check_board_config(codec, CXT5045_MODELS,
+ cxt5045_models,
+ cxt5045_cfg_tbl);
+ switch (board_config) {
+ case CXT5045_LAPTOP:
+ spec->input_mux = &cxt5045_capture_source;
+ spec->num_init_verbs = 2;
+ spec->init_verbs[1] = cxt5045_init_verbs;
+ spec->mixers[0] = cxt5045_mixers;
+ codec->patch_ops.init = cxt5045_init;
+ break;
+#ifdef CONFIG_SND_DEBUG
+ case CXT5045_TEST:
+ spec->input_mux = &cxt5045_test_capture_source;
+ spec->mixers[0] = cxt5045_test_mixer;
+ spec->init_verbs[0] = cxt5045_test_init_verbs;
+#endif
+ }
+ return 0;
+}
+
+
+/* Conexant 5047 specific */
+
+static hda_nid_t cxt5047_dac_nids[1] = { 0x10 };
+static hda_nid_t cxt5047_adc_nids[1] = { 0x12 };
+static hda_nid_t cxt5047_capsrc_nids[1] = { 0x1a };
+#define CXT5047_SPDIF_OUT 0x11
+
+static struct hda_channel_mode cxt5047_modes[1] = {
+ { 2, NULL },
+};
+
+static struct hda_input_mux cxt5047_capture_source = {
+ .num_items = 2,
+ .items = {
+ { "ExtMic", 0x1 },
+ { "IntMic", 0x2 },
+ }
+};
+
+static struct hda_input_mux cxt5047_hp_capture_source = {
+ .num_items = 1,
+ .items = {
+ { "ExtMic", 0x1 },
+ }
+};
+
+/* turn on/off EAPD (+ mute HP) as a master switch */
+static int cxt5047_hp_master_sw_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
+ struct conexant_spec *spec = codec->spec;
+
+ if (!conexant_eapd_put(kcontrol, ucontrol))
+ return 0;
+
+ /* toggle HP mute appropriately */
+ snd_hda_codec_amp_update(codec, 0x13, 0, HDA_OUTPUT, 0,
+ 0x80, spec->cur_eapd ? 0 : 0x80);
+ snd_hda_codec_amp_update(codec, 0x13, 1, HDA_OUTPUT, 0,
+ 0x80, spec->cur_eapd ? 0 : 0x80);
+ return 1;
+}
+
+#if 0
+/* bind volumes of both NID 0x13 and 0x1d */
+static int cxt5047_hp_master_vol_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
+ long *valp = ucontrol->value.integer.value;
+ int change;
+
+ change = snd_hda_codec_amp_update(codec, 0x1c, 0, HDA_OUTPUT, 0,
+ 0x7f, valp[0] & 0x7f);
+ change |= snd_hda_codec_amp_update(codec, 0x1c, 1, HDA_OUTPUT, 0,
+ 0x7f, valp[1] & 0x7f);
+ snd_hda_codec_amp_update(codec, 0x13, 0, HDA_OUTPUT, 0,
+ 0x7f, valp[0] & 0x7f);
+ snd_hda_codec_amp_update(codec, 0x13, 1, HDA_OUTPUT, 0,
+ 0x7f, valp[1] & 0x7f);
+ return change;
+}
+#endif
+
+/* mute internal speaker if HP is plugged */
+static void cxt5047_hp_automute(struct hda_codec *codec)
+{
+ unsigned int present;
+
+ present = snd_hda_codec_read(codec, 0x13, 0,
+ AC_VERB_GET_PIN_SENSE, 0) & 0x80000000;
+ snd_hda_codec_amp_update(codec, 0x1c, 0, HDA_OUTPUT, 0,
+ 0x80, present ? 0x80 : 0);
+ snd_hda_codec_amp_update(codec, 0x1c, 1, HDA_OUTPUT, 0,
+ 0x80, present ? 0x80 : 0);
+}
+
+/* toggle input of built-in and mic jack appropriately */
+static void cxt5047_hp_automic(struct hda_codec *codec)
+{
+ static struct hda_verb mic_jack_on[] = {
+ {0x15, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080},
+ {0x17, AC_VERB_SET_AMP_GAIN_MUTE, 0xb000},
+ {}
+ };
+ static struct hda_verb mic_jack_off[] = {
+ {0x17, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080},
+ {0x15, AC_VERB_SET_AMP_GAIN_MUTE, 0xb000},
+ {}
+ };
+ unsigned int present;
+
+ present = snd_hda_codec_read(codec, 0x08, 0,
+ AC_VERB_GET_PIN_SENSE, 0) & 0x80000000;
+ if (present)
+ snd_hda_sequence_write(codec, mic_jack_on);
+ else
+ snd_hda_sequence_write(codec, mic_jack_off);
+}
+
+/* unsolicited event for HP jack sensing */
+static void cxt5047_hp_unsol_event(struct hda_codec *codec,
+ unsigned int res)
+{
+ res >>= 26;
+ switch (res) {
+ case CONEXANT_HP_EVENT:
+ cxt5047_hp_automute(codec);
+ break;
+ case CONEXANT_MIC_EVENT:
+ cxt5047_hp_automic(codec);
+ break;
+ }
+}
+
+static struct snd_kcontrol_new cxt5047_mixers[] = {
+ {
+ .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+ .name = "Capture Source",
+ .info = conexant_mux_enum_info,
+ .get = conexant_mux_enum_get,
+ .put = conexant_mux_enum_put
+ },
+ HDA_CODEC_VOLUME("Mic Bypass Capture Volume", 0x19, 0x02, HDA_INPUT),
+ HDA_CODEC_MUTE("Mic Bypass Capture Switch", 0x19, 0x02, HDA_INPUT),
+ HDA_CODEC_VOLUME("Capture Volume", 0x12, 0x03, HDA_INPUT),
+ HDA_CODEC_MUTE("Capture Switch", 0x12, 0x03, HDA_INPUT),
+ HDA_CODEC_VOLUME("PCM Volume", 0x10, 0x00, HDA_OUTPUT),
+ HDA_CODEC_MUTE("PCM Switch", 0x10, 0x00, HDA_OUTPUT),
+ HDA_CODEC_VOLUME("Master Playback Volume", 0x13, 0x00, HDA_OUTPUT),
+ {
+ .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+ .name = "Master Playback Switch",
+ .info = conexant_eapd_info,
+ .get = conexant_eapd_get,
+ .put = cxt5047_hp_master_sw_put,
+ .private_value = 0x13,
+ },
+
+ {}
+};
+
+static struct snd_kcontrol_new cxt5047_hp_mixers[] = {
+ {
+ .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+ .name = "Capture Source",
+ .info = conexant_mux_enum_info,
+ .get = conexant_mux_enum_get,
+ .put = conexant_mux_enum_put
+ },
+ HDA_CODEC_VOLUME("Mic Bypass Capture Volume", 0x19, 0x02, HDA_INPUT),
+ HDA_CODEC_MUTE("Mic Bypass Capture Switch", 0x19,0x02,HDA_INPUT),
+ HDA_CODEC_VOLUME("Capture Volume", 0x12, 0x03, HDA_INPUT),
+ HDA_CODEC_MUTE("Capture Switch", 0x12, 0x03, HDA_INPUT),
+ HDA_CODEC_VOLUME("PCM Volume", 0x10, 0x00, HDA_OUTPUT),
+ HDA_CODEC_MUTE("PCM Switch", 0x10, 0x00, HDA_OUTPUT),
+ HDA_CODEC_VOLUME("Master Playback Volume", 0x13, 0x00, HDA_OUTPUT),
+ {
+ .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+ .name = "Master Playback Switch",
+ .info = conexant_eapd_info,
+ .get = conexant_eapd_get,
+ .put = cxt5047_hp_master_sw_put,
+ .private_value = 0x13,
+ },
+ { } /* end */
+};
+
+static struct hda_verb cxt5047_init_verbs[] = {
+ /* Line in, Mic, Built-in Mic */
+ {0x14, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN },
+ {0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN|AC_PINCTL_VREF_50 },
+ {0x17, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN|AC_PINCTL_VREF_50 },
+ /* HP, Amp */
+ {0x13, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT },
+ {0x1A, AC_VERB_SET_CONNECT_SEL,0x03},
+ {0x1A, AC_VERB_SET_AMP_GAIN_MUTE,
+ AC_AMP_SET_OUTPUT|AC_AMP_SET_RIGHT|AC_AMP_SET_LEFT|0x00},
+ {0x1A, AC_VERB_SET_AMP_GAIN_MUTE,
+ AC_AMP_SET_OUTPUT|AC_AMP_SET_RIGHT|AC_AMP_SET_LEFT|0x03},
+ /* Record selector: Front mic */
+ {0x12, AC_VERB_SET_CONNECT_SEL,0x03},
+ {0x19, AC_VERB_SET_AMP_GAIN_MUTE,
+ AC_AMP_SET_INPUT|AC_AMP_SET_RIGHT|AC_AMP_SET_LEFT|0x17},
+ /* SPDIF route: PCM */
+ { 0x18, AC_VERB_SET_CONNECT_SEL, 0x0 },
+ { } /* end */
+};
+
+/* configuration for Toshiba Laptops */
+static struct hda_verb cxt5047_toshiba_init_verbs[] = {
+ {0x13, AC_VERB_SET_EAPD_BTLENABLE, 0x0 }, /* default on */
+ /* pin sensing on HP and Mic jacks */
+ {0x13, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | CONEXANT_HP_EVENT},
+ {0x15, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | CONEXANT_MIC_EVENT},
+ {}
+};
+
+/* configuration for HP Laptops */
+static struct hda_verb cxt5047_hp_init_verbs[] = {
+ /* pin sensing on HP and Mic jacks */
+ {0x13, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | CONEXANT_HP_EVENT},
+ {0x15, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | CONEXANT_MIC_EVENT},
+ {}
+};
+
+/* Test configuration for debugging, modelled after the ALC260 test
+ * configuration.
+ */
+#ifdef CONFIG_SND_DEBUG
+static struct hda_input_mux cxt5047_test_capture_source = {
+ .num_items = 5,
+ .items = {
+ { "MIXER", 0x0 },
+ { "LINE1 pin", 0x1 },
+ { "MIC1 pin", 0x2 },
+ { "MIC2 pin", 0x3 },
+ { "CD pin", 0x4 },
+ },
+};
+
+static struct snd_kcontrol_new cxt5047_test_mixer[] = {
+
+ /* Output only controls */
+ HDA_CODEC_VOLUME("OutAmp-1 Volume", 0x10, 0x00, HDA_OUTPUT),
+ HDA_CODEC_MUTE("OutAmp-1 Switch", 0x10,0x00, HDA_OUTPUT),
+ HDA_CODEC_VOLUME("OutAmp-2 Volume", 0x1c, 0x00, HDA_OUTPUT),
+ HDA_CODEC_MUTE("OutAmp-2 Switch", 0x1c, 0x00, HDA_OUTPUT),
+ HDA_CODEC_VOLUME("Speaker Playback Volume", 0x1d, 0x0, HDA_OUTPUT),
+ HDA_CODEC_MUTE("Speaker Playback Switch", 0x1d, 0x0, HDA_OUTPUT),
+ HDA_CODEC_VOLUME("HeadPhone Playback Volume", 0x13, 0x0, HDA_OUTPUT),
+ HDA_CODEC_MUTE("HeadPhone Playback Switch", 0x13, 0x0, HDA_OUTPUT),
+
+ /* Modes for retasking pin widgets */
+ CXT_PIN_MODE("LINE1 pin mode", 0x14, CXT_PIN_DIR_INOUT),
+ CXT_PIN_MODE("MIC1 pin mode", 0x15, CXT_PIN_DIR_INOUT),
+
+ /* Loopback mixer controls */
+ HDA_CODEC_VOLUME("MIC1 Playback Volume", 0x19, 0x02, HDA_INPUT),
+ HDA_CODEC_MUTE("MIC1 Playback Switch", 0x19, 0x02, HDA_INPUT),
+ HDA_CODEC_VOLUME("MIC2 Playback Volume", 0x19, 0x03, HDA_INPUT),
+ HDA_CODEC_MUTE("MIC2 Playback Switch", 0x19, 0x03, HDA_INPUT),
+ HDA_CODEC_VOLUME("LINE Playback Volume", 0x19, 0x01, HDA_INPUT),
+ HDA_CODEC_MUTE("LINE Playback Switch", 0x19, 0x01, HDA_INPUT),
+ HDA_CODEC_VOLUME("CD Playback Volume", 0x19, 0x04, HDA_INPUT),
+ HDA_CODEC_MUTE("CD Playback Switch", 0x19, 0x04, HDA_INPUT),
+
+#if 0
+ /* Controls for GPIO pins, assuming they exist and are configured as outputs */
+ CXT_GPIO_DATA_SWITCH("GPIO pin 0", 0x01, 0x01),
+ CXT_GPIO_DATA_SWITCH("GPIO pin 1", 0x01, 0x02),
+ CXT_GPIO_DATA_SWITCH("GPIO pin 2", 0x01, 0x04),
+ CXT_GPIO_DATA_SWITCH("GPIO pin 3", 0x01, 0x08),
+#endif
+ CXT_SPDIF_CTRL_SWITCH("SPDIF Playback Switch", 0x18, 0x01),
+
+ HDA_CODEC_VOLUME("Capture Volume", 0x19, 0x0, HDA_OUTPUT),
+ HDA_CODEC_MUTE("Capture Switch", 0x19, 0x0, HDA_OUTPUT),
+ {
+ .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+ .name = "Input Source",
+ .info = conexant_mux_enum_info,
+ .get = conexant_mux_enum_get,
+ .put = conexant_mux_enum_put,
+ },
+
+ { } /* end */
+};
+
+static struct hda_verb cxt5047_test_init_verbs[] = {
+ /* Enable all GPIOs as outputs with an initial value of 0 */
+ {0x01, AC_VERB_SET_GPIO_DIRECTION, 0x0f},
+ {0x01, AC_VERB_SET_GPIO_DATA, 0x00},
+ {0x01, AC_VERB_SET_GPIO_MASK, 0x0f},
+
+ /* Enable retasking pins as output, initially without power amp */
+ {0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
+ {0x14, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
+ {0x13, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
+
+ /* Disable digital (SPDIF) pins initially, but users can enable
+ * them via a mixer switch. In the case of SPDIF-out, this initverb
+ * payload also sets the generation to 0, output to be in "consumer"
+ * PCM format, copyright asserted, no pre-emphasis and no validity
+ * control.
+ */
+ {0x18, AC_VERB_SET_DIGI_CONVERT_1, 0},
+
+ /* Ensure mic1, mic2, line1 pin widgets take input from the
+ * OUT1 sum bus when acting as an output.
+ */
+ {0x1a, AC_VERB_SET_CONNECT_SEL, 0},
+ {0x1b, AC_VERB_SET_CONNECT_SEL, 0},
+
+ /* Start with output sum widgets muted and their output gains at min */
+ {0x19, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
+ {0x19, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
+
+ /* Unmute retasking pin widget output buffers since the default
+ * state appears to be output. As the pin mode is changed by the
+ * user the pin mode control will take care of enabling the pin's
+ * input/output buffers as needed.
+ */
+ {0x15, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
+ {0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
+ {0x13, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
+
+ /* Mute capture amp left and right */
+ {0x12, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
+
+ /* Set ADC connection select to match default mixer setting (mic1
+ * pin)
+ */
+ {0x12, AC_VERB_SET_CONNECT_SEL, 0x00},
+
+ /* Mute all inputs to mixer widget (even unconnected ones) */
+ {0x19, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)}, /* mic1 pin */
+ {0x19, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)}, /* mic2 pin */
+ {0x19, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(2)}, /* line1 pin */
+ {0x19, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(3)}, /* line2 pin */
+ {0x19, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(4)}, /* CD pin */
+ {0x19, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(5)}, /* Beep-gen pin */
+ {0x19, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(6)}, /* Line-out pin */
+ {0x19, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(7)}, /* HP-pin pin */
+
+ { }
+};
+#endif
+
+
+/* initialize jack-sensing, too */
+static int cxt5047_hp_init(struct hda_codec *codec)
+{
+ conexant_init(codec);
+ cxt5047_hp_automute(codec);
+ cxt5047_hp_automic(codec);
+ return 0;
+}
+
+
+enum {
+ CXT5047_LAPTOP, /* Laptops w/o EAPD support */
+ CXT5047_LAPTOP_HP, /* Some HP laptops */
+ CXT5047_LAPTOP_EAPD, /* Laptops with EAPD support */
+#ifdef CONFIG_SND_DEBUG
+ CXT5047_TEST,
+#endif
+ CXT5047_MODELS
+};
+
+static const char *cxt5047_models[CXT5047_MODELS] = {
+ [CXT5047_LAPTOP] = "laptop",
+ [CXT5047_LAPTOP_HP] = "laptop-hp",
+ [CXT5047_LAPTOP_EAPD] = "laptop-eapd",
+#ifdef CONFIG_SND_DEBUG
+ [CXT5047_TEST] = "test",
+#endif
+};
+
+static struct snd_pci_quirk cxt5047_cfg_tbl[] = {
+ SND_PCI_QUIRK(0x103c, 0x30a0, "HP DV1000", CXT5047_LAPTOP),
+ SND_PCI_QUIRK(0x103c, 0x30b2, "HP DV2000T/DV3000T", CXT5047_LAPTOP),
+ SND_PCI_QUIRK(0x103c, 0x30a5, "HP DV5200T/DV8000T", CXT5047_LAPTOP_HP),
+ SND_PCI_QUIRK(0x1179, 0xff31, "Toshiba P100", CXT5047_LAPTOP_EAPD),
+ {}
+};
+
+static int patch_cxt5047(struct hda_codec *codec)
+{
+ struct conexant_spec *spec;
+ int board_config;
+
+ spec = kzalloc(sizeof(*spec), GFP_KERNEL);
+ if (!spec)
+ return -ENOMEM;
+ mutex_init(&spec->amp_mutex);
+ codec->spec = spec;
+
+ spec->multiout.max_channels = 2;
+ spec->multiout.num_dacs = ARRAY_SIZE(cxt5047_dac_nids);
+ spec->multiout.dac_nids = cxt5047_dac_nids;
+ spec->multiout.dig_out_nid = CXT5047_SPDIF_OUT;
+ spec->num_adc_nids = 1;
+ spec->adc_nids = cxt5047_adc_nids;
+ spec->capsrc_nids = cxt5047_capsrc_nids;
+ spec->input_mux = &cxt5047_capture_source;
+ spec->num_mixers = 1;
+ spec->mixers[0] = cxt5047_mixers;
+ spec->num_init_verbs = 1;
+ spec->init_verbs[0] = cxt5047_init_verbs;
+ spec->spdif_route = 0;
+ spec->num_channel_mode = ARRAY_SIZE(cxt5047_modes),
+ spec->channel_mode = cxt5047_modes,
+
+ codec->patch_ops = conexant_patch_ops;
+ codec->patch_ops.unsol_event = cxt5047_hp_unsol_event;
+
+ board_config = snd_hda_check_board_config(codec, CXT5047_MODELS,
+ cxt5047_models,
+ cxt5047_cfg_tbl);
+ switch (board_config) {
+ case CXT5047_LAPTOP:
+ break;
+ case CXT5047_LAPTOP_HP:
+ spec->input_mux = &cxt5047_hp_capture_source;
+ spec->num_init_verbs = 2;
+ spec->init_verbs[1] = cxt5047_hp_init_verbs;
+ spec->mixers[0] = cxt5047_hp_mixers;
+ codec->patch_ops.init = cxt5047_hp_init;
+ break;
+ case CXT5047_LAPTOP_EAPD:
+ spec->num_init_verbs = 2;
+ spec->init_verbs[1] = cxt5047_toshiba_init_verbs;
+ break;
+#ifdef CONFIG_SND_DEBUG
+ case CXT5047_TEST:
+ spec->input_mux = &cxt5047_test_capture_source;
+ spec->mixers[0] = cxt5047_test_mixer;
+ spec->init_verbs[0] = cxt5047_test_init_verbs;
+#endif
+ }
+ return 0;
+}
+
+struct hda_codec_preset snd_hda_preset_conexant[] = {
+ { .id = 0x14f15045, .name = "CXT5045", .patch = patch_cxt5045 },
+ { .id = 0x14f15047, .name = "CXT5047", .patch = patch_cxt5047 },
+ {} /* terminator */
+};
diff --git a/sound/pci/hda/patch_realtek.c b/sound/pci/hda/patch_realtek.c
index 4e0c3c1b908b..145682b78071 100644
--- a/sound/pci/hda/patch_realtek.c
+++ b/sound/pci/hda/patch_realtek.c
@@ -32,6 +32,10 @@
#include "hda_codec.h"
#include "hda_local.h"
+#define ALC880_FRONT_EVENT 0x01
+#define ALC880_DCVOL_EVENT 0x02
+#define ALC880_HP_EVENT 0x04
+#define ALC880_MIC_EVENT 0x08
/* ALC880 board config type */
enum {
@@ -48,7 +52,10 @@ enum {
ALC880_ASUS_DIG,
ALC880_ASUS_W1V,
ALC880_ASUS_DIG2,
+ ALC880_FUJITSU,
ALC880_UNIWILL_DIG,
+ ALC880_UNIWILL,
+ ALC880_UNIWILL_P53,
ALC880_CLEVO,
ALC880_TCL_S700,
ALC880_LG,
@@ -77,8 +84,12 @@ enum {
/* ALC262 models */
enum {
ALC262_BASIC,
+ ALC262_HIPPO,
+ ALC262_HIPPO_1,
ALC262_FUJITSU,
ALC262_HP_BPC,
+ ALC262_HP_BPC_D7000_WL,
+ ALC262_HP_BPC_D7000_WF,
ALC262_BENQ_ED8,
ALC262_AUTO,
ALC262_MODEL_LAST /* last tag */
@@ -91,16 +102,30 @@ enum {
ALC861_3ST_DIG,
ALC861_6ST_DIG,
ALC861_UNIWILL_M31,
+ ALC861_TOSHIBA,
+ ALC861_ASUS,
+ ALC861_ASUS_LAPTOP,
ALC861_AUTO,
ALC861_MODEL_LAST,
};
+/* ALC861-VD models */
+enum {
+ ALC660VD_3ST,
+ ALC861VD_3ST,
+ ALC861VD_3ST_DIG,
+ ALC861VD_6ST_DIG,
+ ALC861VD_AUTO,
+ ALC861VD_MODEL_LAST,
+};
+
/* ALC882 models */
enum {
ALC882_3ST_DIG,
ALC882_6ST_DIG,
ALC882_ARIMA,
ALC882_AUTO,
+ ALC885_MACPRO,
ALC882_MODEL_LAST,
};
@@ -110,8 +135,12 @@ enum {
ALC883_3ST_6ch_DIG,
ALC883_3ST_6ch,
ALC883_6ST_DIG,
+ ALC883_TARGA_DIG,
+ ALC883_TARGA_2ch_DIG,
ALC888_DEMO_BOARD,
ALC883_ACER,
+ ALC883_MEDION,
+ ALC883_LAPTOP_EAPD,
ALC883_AUTO,
ALC883_MODEL_LAST,
};
@@ -1015,6 +1044,60 @@ static struct snd_kcontrol_new alc880_tcl_s700_mixer[] = {
{ } /* end */
};
+/* Uniwill */
+static struct snd_kcontrol_new alc880_uniwill_mixer[] = {
+ HDA_CODEC_VOLUME("HPhone Playback Volume", 0x0c, 0x0, HDA_OUTPUT),
+ HDA_BIND_MUTE("HPhone Playback Switch", 0x0c, 2, HDA_INPUT),
+ HDA_CODEC_VOLUME("iSpeaker Playback Volume", 0x0d, 0x0, HDA_OUTPUT),
+ HDA_BIND_MUTE("iSpeaker Playback Switch", 0x0d, 2, HDA_INPUT),
+ HDA_CODEC_VOLUME_MONO("Center Playback Volume", 0x0e, 1, 0x0, HDA_OUTPUT),
+ HDA_CODEC_VOLUME_MONO("LFE Playback Volume", 0x0e, 2, 0x0, HDA_OUTPUT),
+ HDA_BIND_MUTE_MONO("Center Playback Switch", 0x0e, 1, 2, HDA_INPUT),
+ HDA_BIND_MUTE_MONO("LFE Playback Switch", 0x0e, 2, 2, HDA_INPUT),
+ HDA_CODEC_VOLUME("CD Playback Volume", 0x0b, 0x04, HDA_INPUT),
+ HDA_CODEC_MUTE("CD Playback Switch", 0x0b, 0x04, HDA_INPUT),
+ HDA_CODEC_VOLUME("Line Playback Volume", 0x0b, 0x02, HDA_INPUT),
+ HDA_CODEC_MUTE("Line Playback Switch", 0x0b, 0x02, HDA_INPUT),
+ HDA_CODEC_VOLUME("Mic Playback Volume", 0x0b, 0x0, HDA_INPUT),
+ HDA_CODEC_MUTE("Mic Playback Switch", 0x0b, 0x0, HDA_INPUT),
+ HDA_CODEC_VOLUME("Front Mic Playback Volume", 0x0b, 0x1, HDA_INPUT),
+ HDA_CODEC_MUTE("Front Mic Playback Switch", 0x0b, 0x1, HDA_INPUT),
+ HDA_CODEC_VOLUME("PC Speaker Playback Volume", 0x0b, 0x05, HDA_INPUT),
+ HDA_CODEC_MUTE("PC Speaker Playback Switch", 0x0b, 0x05, HDA_INPUT),
+ {
+ .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+ .name = "Channel Mode",
+ .info = alc_ch_mode_info,
+ .get = alc_ch_mode_get,
+ .put = alc_ch_mode_put,
+ },
+ { } /* end */
+};
+
+static struct snd_kcontrol_new alc880_fujitsu_mixer[] = {
+ HDA_CODEC_VOLUME("Headphone Playback Volume", 0x0c, 0x0, HDA_OUTPUT),
+ HDA_BIND_MUTE("Headphone Playback Switch", 0x0c, 2, HDA_INPUT),
+ HDA_CODEC_VOLUME("Speaker Playback Volume", 0x0d, 0x0, HDA_OUTPUT),
+ HDA_BIND_MUTE("Speaker Playback Switch", 0x0d, 2, HDA_INPUT),
+ HDA_CODEC_VOLUME("CD Playback Volume", 0x0b, 0x04, HDA_INPUT),
+ HDA_CODEC_MUTE("CD Playback Switch", 0x0b, 0x04, HDA_INPUT),
+ HDA_CODEC_VOLUME("Ext Mic Playback Volume", 0x0b, 0x0, HDA_INPUT),
+ HDA_CODEC_MUTE("Ext Mic Playback Switch", 0x0b, 0x0, HDA_INPUT),
+ HDA_CODEC_VOLUME("Int Mic Playback Volume", 0x0b, 0x1, HDA_INPUT),
+ HDA_CODEC_MUTE("Int Mic Playback Switch", 0x0b, 0x1, HDA_INPUT),
+ { } /* end */
+};
+
+static struct snd_kcontrol_new alc880_uniwill_p53_mixer[] = {
+ HDA_CODEC_VOLUME("HPhone Playback Volume", 0x0c, 0x0, HDA_OUTPUT),
+ HDA_BIND_MUTE("HPhone Playback Switch", 0x0c, 2, HDA_INPUT),
+ HDA_CODEC_VOLUME("iSpeaker Playback Volume", 0x0d, 0x0, HDA_OUTPUT),
+ HDA_BIND_MUTE("iSpeaker Playback Switch", 0x0d, 2, HDA_INPUT),
+ HDA_CODEC_VOLUME("Mic Playback Volume", 0x0b, 0x0, HDA_INPUT),
+ HDA_CODEC_MUTE("Mic Playback Switch", 0x0b, 0x0, HDA_INPUT),
+ { } /* end */
+};
+
/*
* build control elements
*/
@@ -1248,6 +1331,159 @@ static struct hda_verb alc880_pin_6stack_init_verbs[] = {
{ }
};
+/*
+ * Uniwill pin configuration:
+ * HP = 0x14, InternalSpeaker = 0x15, mic = 0x18, internal mic = 0x19,
+ * line = 0x1a
+ */
+static struct hda_verb alc880_uniwill_init_verbs[] = {
+ {0x13, AC_VERB_SET_CONNECT_SEL, 0x00}, /* HP */
+
+ {0x14, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP},
+ {0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
+ {0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP},
+ {0x15, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
+ {0x16, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP},
+ {0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
+ {0x17, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
+ {0x17, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
+ {0x0c, AC_VERB_SET_AMP_GAIN_MUTE, (0x7000 | (0x00 << 8))},
+ {0x0c, AC_VERB_SET_AMP_GAIN_MUTE, (0x7000 | (0x01 << 8))},
+ {0x0d, AC_VERB_SET_AMP_GAIN_MUTE, (0x7000 | (0x00 << 8))},
+ {0x0d, AC_VERB_SET_AMP_GAIN_MUTE, (0x7000 | (0x01 << 8))},
+ {0x0e, AC_VERB_SET_AMP_GAIN_MUTE, (0x7000 | (0x00 << 8))},
+ {0x0e, AC_VERB_SET_AMP_GAIN_MUTE, (0x7000 | (0x01 << 8))},
+
+ {0x18, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80},
+ {0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
+ {0x19, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80},
+ {0x19, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
+ {0x1a, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN},
+ {0x1a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
+ /* {0x1b, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP}, */
+ /* {0x1b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE}, */
+ {0x1c, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN},
+
+ {0x14, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | ALC880_HP_EVENT},
+ {0x18, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | ALC880_MIC_EVENT},
+
+ { }
+};
+
+/*
+* Uniwill P53
+* HP = 0x14, InternalSpeaker = 0x15, mic = 0x19,
+ */
+static struct hda_verb alc880_uniwill_p53_init_verbs[] = {
+ {0x13, AC_VERB_SET_CONNECT_SEL, 0x00}, /* HP */
+
+ {0x14, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP},
+ {0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
+ {0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP},
+ {0x15, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
+ {0x16, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP},
+ {0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
+ {0x0c, AC_VERB_SET_AMP_GAIN_MUTE, (0x7000 | (0x00 << 8))},
+ {0x0c, AC_VERB_SET_AMP_GAIN_MUTE, (0x7000 | (0x01 << 8))},
+ {0x0d, AC_VERB_SET_AMP_GAIN_MUTE, (0x7000 | (0x00 << 8))},
+ {0x0d, AC_VERB_SET_AMP_GAIN_MUTE, (0x7000 | (0x01 << 8))},
+ {0x0e, AC_VERB_SET_AMP_GAIN_MUTE, (0x7000 | (0x00 << 8))},
+ {0x0e, AC_VERB_SET_AMP_GAIN_MUTE, (0x7000 | (0x01 << 8))},
+
+ {0x18, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80},
+ {0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
+ {0x19, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80},
+ {0x19, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
+ {0x1a, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN},
+ {0x1a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
+
+ {0x14, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | ALC880_HP_EVENT},
+ {0x21, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | ALC880_DCVOL_EVENT},
+
+ { }
+};
+
+static struct hda_verb alc880_beep_init_verbs[] = {
+ { 0x0b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(5) },
+ { }
+};
+
+/* toggle speaker-output according to the hp-jack state */
+static void alc880_uniwill_automute(struct hda_codec *codec)
+{
+ unsigned int present;
+
+ present = snd_hda_codec_read(codec, 0x14, 0,
+ AC_VERB_GET_PIN_SENSE, 0) & 0x80000000;
+ snd_hda_codec_amp_update(codec, 0x15, 0, HDA_OUTPUT, 0,
+ 0x80, present ? 0x80 : 0);
+ snd_hda_codec_amp_update(codec, 0x15, 1, HDA_OUTPUT, 0,
+ 0x80, present ? 0x80 : 0);
+ snd_hda_codec_amp_update(codec, 0x16, 0, HDA_OUTPUT, 0,
+ 0x80, present ? 0x80 : 0);
+ snd_hda_codec_amp_update(codec, 0x16, 1, HDA_OUTPUT, 0,
+ 0x80, present ? 0x80 : 0);
+
+ present = snd_hda_codec_read(codec, 0x18, 0,
+ AC_VERB_GET_PIN_SENSE, 0) & 0x80000000;
+ snd_hda_codec_write(codec, 0x0b, 0, AC_VERB_SET_AMP_GAIN_MUTE,
+ 0x7000 | (0x01 << 8) | (present ? 0x80 : 0));
+}
+
+static void alc880_uniwill_unsol_event(struct hda_codec *codec,
+ unsigned int res)
+{
+ /* Looks like the unsol event is incompatible with the standard
+ * definition. 4bit tag is placed at 28 bit!
+ */
+ if ((res >> 28) == ALC880_HP_EVENT ||
+ (res >> 28) == ALC880_MIC_EVENT)
+ alc880_uniwill_automute(codec);
+}
+
+static void alc880_uniwill_p53_hp_automute(struct hda_codec *codec)
+{
+ unsigned int present;
+
+ present = snd_hda_codec_read(codec, 0x14, 0,
+ AC_VERB_GET_PIN_SENSE, 0) & 0x80000000;
+
+ snd_hda_codec_amp_update(codec, 0x15, 0, HDA_INPUT, 0,
+ 0x80, present ? 0x80 : 0);
+ snd_hda_codec_amp_update(codec, 0x15, 1, HDA_INPUT, 0,
+ 0x80, present ? 0x80 : 0);
+}
+
+static void alc880_uniwill_p53_dcvol_automute(struct hda_codec *codec)
+{
+ unsigned int present;
+
+ present = snd_hda_codec_read(codec, 0x21, 0,
+ AC_VERB_GET_VOLUME_KNOB_CONTROL, 0) & 0x7f;
+
+ snd_hda_codec_amp_update(codec, 0x0c, 0, HDA_OUTPUT, 0,
+ 0x7f, present);
+ snd_hda_codec_amp_update(codec, 0x0c, 1, HDA_OUTPUT, 0,
+ 0x7f, present);
+
+ snd_hda_codec_amp_update(codec, 0x0d, 0, HDA_OUTPUT, 0,
+ 0x7f, present);
+ snd_hda_codec_amp_update(codec, 0x0d, 1, HDA_OUTPUT, 0,
+ 0x7f, present);
+
+}
+static void alc880_uniwill_p53_unsol_event(struct hda_codec *codec,
+ unsigned int res)
+{
+ /* Looks like the unsol event is incompatible with the standard
+ * definition. 4bit tag is placed at 28 bit!
+ */
+ if ((res >> 28) == ALC880_HP_EVENT)
+ alc880_uniwill_p53_hp_automute(codec);
+ if ((res >> 28) == ALC880_DCVOL_EVENT)
+ alc880_uniwill_p53_dcvol_automute(codec);
+}
+
/* FIXME! */
/*
* F1734 pin configuration:
@@ -2125,159 +2361,112 @@ static struct hda_verb alc880_test_init_verbs[] = {
/*
*/
-static struct hda_board_config alc880_cfg_tbl[] = {
- /* Back 3 jack, front 2 jack */
- { .modelname = "3stack", .config = ALC880_3ST },
- { .pci_subvendor = 0x8086, .pci_subdevice = 0xe200, .config = ALC880_3ST },
- { .pci_subvendor = 0x8086, .pci_subdevice = 0xe201, .config = ALC880_3ST },
- { .pci_subvendor = 0x8086, .pci_subdevice = 0xe202, .config = ALC880_3ST },
- { .pci_subvendor = 0x8086, .pci_subdevice = 0xe203, .config = ALC880_3ST },
- { .pci_subvendor = 0x8086, .pci_subdevice = 0xe204, .config = ALC880_3ST },
- { .pci_subvendor = 0x8086, .pci_subdevice = 0xe205, .config = ALC880_3ST },
- { .pci_subvendor = 0x8086, .pci_subdevice = 0xe206, .config = ALC880_3ST },
- { .pci_subvendor = 0x8086, .pci_subdevice = 0xe207, .config = ALC880_3ST },
- { .pci_subvendor = 0x8086, .pci_subdevice = 0xe208, .config = ALC880_3ST },
- { .pci_subvendor = 0x8086, .pci_subdevice = 0xe209, .config = ALC880_3ST },
- { .pci_subvendor = 0x8086, .pci_subdevice = 0xe20a, .config = ALC880_3ST },
- { .pci_subvendor = 0x8086, .pci_subdevice = 0xe20b, .config = ALC880_3ST },
- { .pci_subvendor = 0x8086, .pci_subdevice = 0xe20c, .config = ALC880_3ST },
- { .pci_subvendor = 0x8086, .pci_subdevice = 0xe20d, .config = ALC880_3ST },
- { .pci_subvendor = 0x8086, .pci_subdevice = 0xe20e, .config = ALC880_3ST },
- { .pci_subvendor = 0x8086, .pci_subdevice = 0xe20f, .config = ALC880_3ST },
- { .pci_subvendor = 0x8086, .pci_subdevice = 0xe210, .config = ALC880_3ST },
- { .pci_subvendor = 0x8086, .pci_subdevice = 0xe211, .config = ALC880_3ST },
- { .pci_subvendor = 0x8086, .pci_subdevice = 0xe212, .config = ALC880_3ST },
- { .pci_subvendor = 0x8086, .pci_subdevice = 0xe213, .config = ALC880_3ST },
- { .pci_subvendor = 0x8086, .pci_subdevice = 0xe214, .config = ALC880_3ST },
- { .pci_subvendor = 0x8086, .pci_subdevice = 0xe234, .config = ALC880_3ST },
- { .pci_subvendor = 0x8086, .pci_subdevice = 0xe302, .config = ALC880_3ST },
- { .pci_subvendor = 0x8086, .pci_subdevice = 0xe303, .config = ALC880_3ST },
- { .pci_subvendor = 0x8086, .pci_subdevice = 0xe304, .config = ALC880_3ST },
- { .pci_subvendor = 0x8086, .pci_subdevice = 0xe306, .config = ALC880_3ST },
- { .pci_subvendor = 0x8086, .pci_subdevice = 0xe307, .config = ALC880_3ST },
- { .pci_subvendor = 0x8086, .pci_subdevice = 0xe404, .config = ALC880_3ST },
- { .pci_subvendor = 0x8086, .pci_subdevice = 0xa101, .config = ALC880_3ST },
- { .pci_subvendor = 0x107b, .pci_subdevice = 0x3031, .config = ALC880_3ST },
- { .pci_subvendor = 0x107b, .pci_subdevice = 0x4036, .config = ALC880_3ST },
- { .pci_subvendor = 0x107b, .pci_subdevice = 0x4037, .config = ALC880_3ST },
- { .pci_subvendor = 0x107b, .pci_subdevice = 0x4038, .config = ALC880_3ST },
- { .pci_subvendor = 0x107b, .pci_subdevice = 0x4040, .config = ALC880_3ST },
- { .pci_subvendor = 0x107b, .pci_subdevice = 0x4041, .config = ALC880_3ST },
- /* TCL S700 */
- { .modelname = "tcl", .config = ALC880_TCL_S700 },
- { .pci_subvendor = 0x19db, .pci_subdevice = 0x4188, .config = ALC880_TCL_S700 },
-
- /* Back 3 jack, front 2 jack (Internal add Aux-In) */
- { .pci_subvendor = 0x1025, .pci_subdevice = 0xe310, .config = ALC880_3ST },
- { .pci_subvendor = 0x104d, .pci_subdevice = 0x81d6, .config = ALC880_3ST },
- { .pci_subvendor = 0x104d, .pci_subdevice = 0x81a0, .config = ALC880_3ST },
-
- /* Back 3 jack plus 1 SPDIF out jack, front 2 jack */
- { .modelname = "3stack-digout", .config = ALC880_3ST_DIG },
- { .pci_subvendor = 0x8086, .pci_subdevice = 0xe308, .config = ALC880_3ST_DIG },
- { .pci_subvendor = 0x1025, .pci_subdevice = 0x0070, .config = ALC880_3ST_DIG },
-
- /* Clevo laptops */
- { .modelname = "clevo", .config = ALC880_CLEVO },
- { .pci_subvendor = 0x1558, .pci_subdevice = 0x0520,
- .config = ALC880_CLEVO }, /* Clevo m520G NB */
- { .pci_subvendor = 0x1558, .pci_subdevice = 0x0660,
- .config = ALC880_CLEVO }, /* Clevo m665n */
-
- /* Back 3 jack plus 1 SPDIF out jack, front 2 jack (Internal add Aux-In)*/
- { .pci_subvendor = 0x8086, .pci_subdevice = 0xe305, .config = ALC880_3ST_DIG },
- { .pci_subvendor = 0x8086, .pci_subdevice = 0xd402, .config = ALC880_3ST_DIG },
- { .pci_subvendor = 0x1025, .pci_subdevice = 0xe309, .config = ALC880_3ST_DIG },
-
- /* Back 5 jack, front 2 jack */
- { .modelname = "5stack", .config = ALC880_5ST },
- { .pci_subvendor = 0x107b, .pci_subdevice = 0x3033, .config = ALC880_5ST },
- { .pci_subvendor = 0x107b, .pci_subdevice = 0x4039, .config = ALC880_5ST },
- { .pci_subvendor = 0x107b, .pci_subdevice = 0x3032, .config = ALC880_5ST },
- { .pci_subvendor = 0x103c, .pci_subdevice = 0x2a09, .config = ALC880_5ST },
- { .pci_subvendor = 0x1043, .pci_subdevice = 0x814e, .config = ALC880_5ST },
-
- /* Back 5 jack plus 1 SPDIF out jack, front 2 jack */
- { .modelname = "5stack-digout", .config = ALC880_5ST_DIG },
- { .pci_subvendor = 0x8086, .pci_subdevice = 0xe224, .config = ALC880_5ST_DIG },
- { .pci_subvendor = 0x8086, .pci_subdevice = 0xe400, .config = ALC880_5ST_DIG },
- { .pci_subvendor = 0x8086, .pci_subdevice = 0xe401, .config = ALC880_5ST_DIG },
- { .pci_subvendor = 0x8086, .pci_subdevice = 0xe402, .config = ALC880_5ST_DIG },
- { .pci_subvendor = 0x8086, .pci_subdevice = 0xd400, .config = ALC880_5ST_DIG },
- { .pci_subvendor = 0x8086, .pci_subdevice = 0xd401, .config = ALC880_5ST_DIG },
- { .pci_subvendor = 0x8086, .pci_subdevice = 0xa100, .config = ALC880_5ST_DIG },
- { .pci_subvendor = 0x1565, .pci_subdevice = 0x8202, .config = ALC880_5ST_DIG },
- { .pci_subvendor = 0x1019, .pci_subdevice = 0xa880, .config = ALC880_5ST_DIG },
- { .pci_subvendor = 0xa0a0, .pci_subdevice = 0x0560,
- .config = ALC880_5ST_DIG }, /* Aopen i915GMm-HFS */
- /* { .pci_subvendor = 0x1019, .pci_subdevice = 0xa884, .config = ALC880_5ST_DIG }, */ /* conflict with 6stack */
- { .pci_subvendor = 0x1695, .pci_subdevice = 0x400d, .config = ALC880_5ST_DIG },
- /* note subvendor = 0 below */
- /* { .pci_subvendor = 0x0000, .pci_subdevice = 0x8086, .config = ALC880_5ST_DIG }, */
-
- { .modelname = "w810", .config = ALC880_W810 },
- { .pci_subvendor = 0x161f, .pci_subdevice = 0x203d, .config = ALC880_W810 },
-
- { .modelname = "z71v", .config = ALC880_Z71V },
- { .pci_subvendor = 0x1043, .pci_subdevice = 0x1964, .config = ALC880_Z71V },
-
- { .modelname = "6stack", .config = ALC880_6ST },
- { .pci_subvendor = 0x1043, .pci_subdevice = 0x8196, .config = ALC880_6ST }, /* ASUS P5GD1-HVM */
- { .pci_subvendor = 0x1043, .pci_subdevice = 0x81b4, .config = ALC880_6ST },
- { .pci_subvendor = 0x1019, .pci_subdevice = 0xa884, .config = ALC880_6ST }, /* Acer APFV */
- { .pci_subvendor = 0x1458, .pci_subdevice = 0xa102, .config = ALC880_6ST }, /* Gigabyte K8N51 */
-
- { .modelname = "6stack-digout", .config = ALC880_6ST_DIG },
- { .pci_subvendor = 0x2668, .pci_subdevice = 0x8086, .config = ALC880_6ST_DIG },
- { .pci_subvendor = 0x8086, .pci_subdevice = 0x2668, .config = ALC880_6ST_DIG },
- { .pci_subvendor = 0x1462, .pci_subdevice = 0x1150, .config = ALC880_6ST_DIG },
- { .pci_subvendor = 0xe803, .pci_subdevice = 0x1019, .config = ALC880_6ST_DIG },
- { .pci_subvendor = 0x1039, .pci_subdevice = 0x1234, .config = ALC880_6ST_DIG },
- { .pci_subvendor = 0x1025, .pci_subdevice = 0x0077, .config = ALC880_6ST_DIG },
- { .pci_subvendor = 0x1025, .pci_subdevice = 0x0078, .config = ALC880_6ST_DIG },
- { .pci_subvendor = 0x1025, .pci_subdevice = 0x0087, .config = ALC880_6ST_DIG },
- { .pci_subvendor = 0x1297, .pci_subdevice = 0xc790, .config = ALC880_6ST_DIG }, /* Shuttle ST20G5 */
- { .pci_subvendor = 0x1509, .pci_subdevice = 0x925d, .config = ALC880_6ST_DIG }, /* FIC P4M-915GD1 */
- { .pci_subvendor = 0x1695, .pci_subdevice = 0x4012, .config = ALC880_5ST_DIG }, /* Epox EP-5LDA+ GLi */
-
- { .modelname = "asus", .config = ALC880_ASUS },
- { .pci_subvendor = 0x1043, .pci_subdevice = 0x1964, .config = ALC880_ASUS_DIG },
- { .pci_subvendor = 0x1043, .pci_subdevice = 0x1973, .config = ALC880_ASUS_DIG },
- { .pci_subvendor = 0x1043, .pci_subdevice = 0x19b3, .config = ALC880_ASUS_DIG },
- { .pci_subvendor = 0x1043, .pci_subdevice = 0x1113, .config = ALC880_ASUS_DIG },
- { .pci_subvendor = 0x1043, .pci_subdevice = 0x1173, .config = ALC880_ASUS_DIG },
- { .pci_subvendor = 0x1043, .pci_subdevice = 0x1993, .config = ALC880_ASUS },
- { .pci_subvendor = 0x1043, .pci_subdevice = 0x10c2, .config = ALC880_ASUS_DIG }, /* Asus W6A */
- { .pci_subvendor = 0x1043, .pci_subdevice = 0x10c3, .config = ALC880_ASUS_DIG },
- { .pci_subvendor = 0x1043, .pci_subdevice = 0x1133, .config = ALC880_ASUS },
- { .pci_subvendor = 0x1043, .pci_subdevice = 0x1123, .config = ALC880_ASUS_DIG },
- { .pci_subvendor = 0x1043, .pci_subdevice = 0x1143, .config = ALC880_ASUS },
- { .modelname = "asus-w1v", .config = ALC880_ASUS_W1V },
- { .pci_subvendor = 0x1043, .pci_subdevice = 0x10b3, .config = ALC880_ASUS_W1V },
- { .modelname = "asus-dig", .config = ALC880_ASUS_DIG },
- { .pci_subvendor = 0x1043, .pci_subdevice = 0x8181, .config = ALC880_ASUS_DIG }, /* ASUS P4GPL-X */
- { .modelname = "asus-dig2", .config = ALC880_ASUS_DIG2 },
- { .pci_subvendor = 0x1558, .pci_subdevice = 0x5401, .config = ALC880_ASUS_DIG2 },
-
- { .modelname = "uniwill", .config = ALC880_UNIWILL_DIG },
- { .pci_subvendor = 0x1584, .pci_subdevice = 0x9050, .config = ALC880_UNIWILL_DIG },
-
- { .modelname = "F1734", .config = ALC880_F1734 },
- { .pci_subvendor = 0x1734, .pci_subdevice = 0x107c, .config = ALC880_F1734 },
- { .pci_subvendor = 0x1584, .pci_subdevice = 0x9054, .config = ALC880_F1734 },
-
- { .modelname = "lg", .config = ALC880_LG },
- { .pci_subvendor = 0x1854, .pci_subdevice = 0x003b, .config = ALC880_LG },
- { .pci_subvendor = 0x1854, .pci_subdevice = 0x0068, .config = ALC880_LG },
-
- { .modelname = "lg-lw", .config = ALC880_LG_LW },
- { .pci_subvendor = 0x1854, .pci_subdevice = 0x0018, .config = ALC880_LG_LW },
- { .pci_subvendor = 0x1854, .pci_subdevice = 0x0077, .config = ALC880_LG_LW },
-
+static const char *alc880_models[ALC880_MODEL_LAST] = {
+ [ALC880_3ST] = "3stack",
+ [ALC880_TCL_S700] = "tcl",
+ [ALC880_3ST_DIG] = "3stack-digout",
+ [ALC880_CLEVO] = "clevo",
+ [ALC880_5ST] = "5stack",
+ [ALC880_5ST_DIG] = "5stack-digout",
+ [ALC880_W810] = "w810",
+ [ALC880_Z71V] = "z71v",
+ [ALC880_6ST] = "6stack",
+ [ALC880_6ST_DIG] = "6stack-digout",
+ [ALC880_ASUS] = "asus",
+ [ALC880_ASUS_W1V] = "asus-w1v",
+ [ALC880_ASUS_DIG] = "asus-dig",
+ [ALC880_ASUS_DIG2] = "asus-dig2",
+ [ALC880_UNIWILL_DIG] = "uniwill",
+ [ALC880_UNIWILL_P53] = "uniwill-p53",
+ [ALC880_FUJITSU] = "fujitsu",
+ [ALC880_F1734] = "F1734",
+ [ALC880_LG] = "lg",
+ [ALC880_LG_LW] = "lg-lw",
#ifdef CONFIG_SND_DEBUG
- { .modelname = "test", .config = ALC880_TEST },
+ [ALC880_TEST] = "test",
#endif
- { .modelname = "auto", .config = ALC880_AUTO },
+ [ALC880_AUTO] = "auto",
+};
+
+static struct snd_pci_quirk alc880_cfg_tbl[] = {
+ /* Broken BIOS configuration */
+ SND_PCI_QUIRK(0x2668, 0x8086, NULL, ALC880_6ST_DIG),
+ SND_PCI_QUIRK(0x8086, 0x2668, NULL, ALC880_6ST_DIG),
+
+ SND_PCI_QUIRK(0x1019, 0xa880, "ECS", ALC880_5ST_DIG),
+ SND_PCI_QUIRK(0x1019, 0xa884, "Acer APFV", ALC880_6ST),
+ SND_PCI_QUIRK(0x1019, 0x0f69, "Coeus G610P", ALC880_W810),
+ SND_PCI_QUIRK(0x1025, 0x0070, "ULI", ALC880_3ST_DIG),
+ SND_PCI_QUIRK(0x1025, 0x0077, "ULI", ALC880_6ST_DIG),
+ SND_PCI_QUIRK(0x1025, 0x0078, "ULI", ALC880_6ST_DIG),
+ SND_PCI_QUIRK(0x1025, 0x0087, "ULI", ALC880_6ST_DIG),
+ SND_PCI_QUIRK(0x1025, 0xe309, "ULI", ALC880_3ST_DIG),
+ SND_PCI_QUIRK(0x1025, 0xe310, "ULI", ALC880_3ST),
+
+ SND_PCI_QUIRK(0x1039, 0x1234, NULL, ALC880_6ST_DIG),
+ SND_PCI_QUIRK(0x103c, 0x2a09, "HP", ALC880_5ST),
+
+ SND_PCI_QUIRK(0x1043, 0x10b3, "ASUS W1V", ALC880_ASUS_W1V),
+ SND_PCI_QUIRK(0x1043, 0x10c2, "ASUS W6A", ALC880_ASUS_DIG),
+ SND_PCI_QUIRK(0x1043, 0x10c3, "ASUS Wxx", ALC880_ASUS_DIG),
+ SND_PCI_QUIRK(0x1043, 0x1113, "ASUS", ALC880_ASUS_DIG),
+ SND_PCI_QUIRK(0x1043, 0x1123, "ASUS", ALC880_ASUS_DIG),
+ SND_PCI_QUIRK(0x1043, 0x1173, "ASUS", ALC880_ASUS_DIG),
+ SND_PCI_QUIRK(0x1043, 0x1964, "ASUS Z71V", ALC880_Z71V),
+ /* SND_PCI_QUIRK(0x1043, 0x1964, "ASUS", ALC880_ASUS_DIG), */
+ SND_PCI_QUIRK(0x1043, 0x1973, "ASUS", ALC880_ASUS_DIG),
+ SND_PCI_QUIRK(0x1043, 0x19b3, "ASUS", ALC880_ASUS_DIG),
+ SND_PCI_QUIRK(0x1043, 0x814e, "ASUS", ALC880_ASUS),
+ SND_PCI_QUIRK(0x1043, 0x8181, "ASUS P4GPL", ALC880_ASUS_DIG),
+ SND_PCI_QUIRK(0x1043, 0x8196, "ASUS P5GD1", ALC880_6ST),
+ SND_PCI_QUIRK(0x1043, 0x81b4, "ASUS", ALC880_6ST),
+ SND_PCI_QUIRK(0x1043, 0, "ASUS", ALC880_ASUS),
+
+ SND_PCI_QUIRK(0x104d, 0x81d6, "Sony", ALC880_3ST),
+ SND_PCI_QUIRK(0x104d, 0x81a0, "Sony", ALC880_3ST),
+ SND_PCI_QUIRK(0x107b, 0x3033, "Gateway", ALC880_5ST),
+ SND_PCI_QUIRK(0x107b, 0x4039, "Gateway", ALC880_5ST),
+ SND_PCI_QUIRK(0x107b, 0x3032, "Gateway", ALC880_5ST),
+ SND_PCI_QUIRK(0x1558, 0x0520, "Clevo m520G", ALC880_CLEVO),
+ SND_PCI_QUIRK(0x1558, 0x0660, "Clevo m655n", ALC880_CLEVO),
+ SND_PCI_QUIRK(0x1565, 0x8202, "Biostar", ALC880_5ST_DIG),
+ SND_PCI_QUIRK(0x161f, 0x203d, "W810", ALC880_W810),
+ SND_PCI_QUIRK(0x1695, 0x400d, "EPoX", ALC880_5ST_DIG),
+ SND_PCI_QUIRK(0x19db, 0x4188, "TCL S700", ALC880_TCL_S700),
+ SND_PCI_QUIRK(0xa0a0, 0x0560, "AOpen i915GMm-HFS", ALC880_5ST_DIG),
+ SND_PCI_QUIRK(0xe803, 0x1019, NULL, ALC880_6ST_DIG),
+ SND_PCI_QUIRK(0x1297, 0xc790, "Shuttle ST20G5", ALC880_6ST_DIG),
+ SND_PCI_QUIRK(0x1458, 0xa102, "Gigabyte K8", ALC880_6ST_DIG),
+ SND_PCI_QUIRK(0x1462, 0x1150, "MSI", ALC880_6ST_DIG),
+ SND_PCI_QUIRK(0x1509, 0x925d, "FIC P4M", ALC880_6ST_DIG),
+ SND_PCI_QUIRK(0x1558, 0x5401, "ASUS", ALC880_ASUS_DIG2),
+
+ SND_PCI_QUIRK(0x1584, 0x9050, "Uniwill", ALC880_UNIWILL_DIG),
+ SND_PCI_QUIRK(0x1584, 0x9070, "Uniwill", ALC880_UNIWILL),
+ SND_PCI_QUIRK(0x1584, 0x9077, "Uniwill P53", ALC880_UNIWILL_P53),
+ SND_PCI_QUIRK(0x1584, 0x9054, "Uniwlll", ALC880_F1734),
+
+ SND_PCI_QUIRK(0x1695, 0x4012, "EPox EP-5LDA", ALC880_5ST_DIG),
+ SND_PCI_QUIRK(0x1734, 0x10ac, "FSC", ALC880_UNIWILL),
+ SND_PCI_QUIRK(0x1734, 0x107c, "FSC F1734", ALC880_F1734),
+ SND_PCI_QUIRK(0x1734, 0x10b0, "Fujitsu", ALC880_FUJITSU),
+
+ SND_PCI_QUIRK(0x1854, 0x003b, "LG", ALC880_LG),
+ SND_PCI_QUIRK(0x1854, 0x0068, "LG w1", ALC880_LG),
+ SND_PCI_QUIRK(0x1854, 0x0018, "LG LW20", ALC880_LG_LW),
+ SND_PCI_QUIRK(0x1854, 0x0077, "LG LW25", ALC880_LG_LW),
+
+ SND_PCI_QUIRK(0x8086, 0xe308, "Intel mobo", ALC880_3ST_DIG),
+ SND_PCI_QUIRK(0x8086, 0xe305, "Intel mobo", ALC880_3ST_DIG),
+ SND_PCI_QUIRK(0x8086, 0xd402, "Intel mobo", ALC880_3ST_DIG),
+ SND_PCI_QUIRK(0x8086, 0xd400, "Intel mobo", ALC880_5ST_DIG),
+ SND_PCI_QUIRK(0x8086, 0xd401, "Intel mobo", ALC880_5ST_DIG),
+ SND_PCI_QUIRK(0x8086, 0xe224, "Intel mobo", ALC880_5ST_DIG),
+ SND_PCI_QUIRK(0x8086, 0xe400, "Intel mobo", ALC880_5ST_DIG),
+ SND_PCI_QUIRK(0x8086, 0xe401, "Intel mobo", ALC880_5ST_DIG),
+ SND_PCI_QUIRK(0x8086, 0xe402, "Intel mobo", ALC880_5ST_DIG),
+ SND_PCI_QUIRK(0x8086, 0xa100, "Intel mobo", ALC880_5ST_DIG),
+ SND_PCI_QUIRK(0x8086, 0, "Intel mobo", ALC880_3ST),
{}
};
@@ -2438,7 +2627,8 @@ static struct alc_config_preset alc880_presets[] = {
},
[ALC880_UNIWILL_DIG] = {
.mixers = { alc880_asus_mixer, alc880_pcbeep_mixer },
- .init_verbs = { alc880_volume_init_verbs, alc880_pin_asus_init_verbs },
+ .init_verbs = { alc880_volume_init_verbs,
+ alc880_pin_asus_init_verbs },
.num_dacs = ARRAY_SIZE(alc880_asus_dac_nids),
.dac_nids = alc880_asus_dac_nids,
.dig_out_nid = ALC880_DIGOUT_NID,
@@ -2447,6 +2637,46 @@ static struct alc_config_preset alc880_presets[] = {
.need_dac_fix = 1,
.input_mux = &alc880_capture_source,
},
+ [ALC880_UNIWILL] = {
+ .mixers = { alc880_uniwill_mixer },
+ .init_verbs = { alc880_volume_init_verbs,
+ alc880_uniwill_init_verbs },
+ .num_dacs = ARRAY_SIZE(alc880_asus_dac_nids),
+ .dac_nids = alc880_asus_dac_nids,
+ .dig_out_nid = ALC880_DIGOUT_NID,
+ .num_channel_mode = ARRAY_SIZE(alc880_threestack_modes),
+ .channel_mode = alc880_threestack_modes,
+ .need_dac_fix = 1,
+ .input_mux = &alc880_capture_source,
+ .unsol_event = alc880_uniwill_unsol_event,
+ .init_hook = alc880_uniwill_automute,
+ },
+ [ALC880_UNIWILL_P53] = {
+ .mixers = { alc880_uniwill_p53_mixer },
+ .init_verbs = { alc880_volume_init_verbs,
+ alc880_uniwill_p53_init_verbs },
+ .num_dacs = ARRAY_SIZE(alc880_asus_dac_nids),
+ .dac_nids = alc880_asus_dac_nids,
+ .num_channel_mode = ARRAY_SIZE(alc880_w810_modes),
+ .channel_mode = alc880_threestack_modes,
+ .input_mux = &alc880_capture_source,
+ .unsol_event = alc880_uniwill_p53_unsol_event,
+ .init_hook = alc880_uniwill_p53_hp_automute,
+ },
+ [ALC880_FUJITSU] = {
+ .mixers = { alc880_fujitsu_mixer,
+ alc880_pcbeep_mixer, },
+ .init_verbs = { alc880_volume_init_verbs,
+ alc880_uniwill_p53_init_verbs,
+ alc880_beep_init_verbs },
+ .num_dacs = ARRAY_SIZE(alc880_dac_nids),
+ .dac_nids = alc880_dac_nids,
+ .num_channel_mode = ARRAY_SIZE(alc880_2_jack_modes),
+ .channel_mode = alc880_2_jack_modes,
+ .input_mux = &alc880_capture_source,
+ .unsol_event = alc880_uniwill_p53_unsol_event,
+ .init_hook = alc880_uniwill_p53_hp_automute,
+ },
[ALC880_CLEVO] = {
.mixers = { alc880_three_stack_mixer },
.init_verbs = { alc880_volume_init_verbs,
@@ -2841,8 +3071,10 @@ static int patch_alc880(struct hda_codec *codec)
codec->spec = spec;
- board_config = snd_hda_check_board_config(codec, alc880_cfg_tbl);
- if (board_config < 0 || board_config >= ALC880_MODEL_LAST) {
+ board_config = snd_hda_check_board_config(codec, ALC880_MODEL_LAST,
+ alc880_models,
+ alc880_cfg_tbl);
+ if (board_config < 0) {
printk(KERN_INFO "hda_codec: Unknown model for ALC880, "
"trying auto-probe from BIOS...\n");
board_config = ALC880_AUTO;
@@ -3090,11 +3322,20 @@ static struct snd_kcontrol_new alc260_fujitsu_mixer[] = {
* and the output jack. If this turns out to be the case for all such
* models the "Line Jack Mode" mode could be changed from ALC_PIN_DIR_INOUT
* to ALC_PIN_DIR_INOUT_NOMICBIAS.
+ *
+ * The C20x Tablet series have a mono internal speaker which is controlled
+ * via the chip's Mono sum widget and pin complex, so include the necessary
+ * controls for such models. On models without a "mono speaker" the control
+ * won't do anything.
*/
static struct snd_kcontrol_new alc260_acer_mixer[] = {
HDA_CODEC_VOLUME("Master Playback Volume", 0x08, 0x0, HDA_OUTPUT),
HDA_BIND_MUTE("Master Playback Switch", 0x08, 2, HDA_INPUT),
ALC_PIN_MODE("Headphone Jack Mode", 0x0f, ALC_PIN_DIR_INOUT),
+ HDA_CODEC_VOLUME_MONO("Mono Speaker Playback Volume", 0x0a, 1, 0x0,
+ HDA_OUTPUT),
+ HDA_BIND_MUTE_MONO("Mono Speaker Playback Switch", 0x0a, 1, 2,
+ HDA_INPUT),
HDA_CODEC_VOLUME("CD Playback Volume", 0x07, 0x04, HDA_INPUT),
HDA_CODEC_MUTE("CD Playback Switch", 0x07, 0x04, HDA_INPUT),
HDA_CODEC_VOLUME("Mic Playback Volume", 0x07, 0x0, HDA_INPUT),
@@ -3409,11 +3650,11 @@ static struct hda_verb alc260_acer_init_verbs[] = {
{0x12, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF50},
/* Line In jack is connected to Line1 pin */
{0x14, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN},
+ /* Some Acers (eg: C20x Tablets) use Mono pin for internal speaker */
+ {0x11, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP},
/* Ensure all other unused pins are disabled and muted. */
{0x10, AC_VERB_SET_PIN_WIDGET_CONTROL, 0},
{0x10, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
- {0x11, AC_VERB_SET_PIN_WIDGET_CONTROL, 0},
- {0x11, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
{0x13, AC_VERB_SET_PIN_WIDGET_CONTROL, 0},
{0x13, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
{0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, 0},
@@ -3441,6 +3682,8 @@ static struct hda_verb alc260_acer_init_verbs[] = {
/* Unmute Line-out pin widget amp left and right (no equiv mixer ctrl) */
{0x0f, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
+ /* Unmute mono pin widget amp output (no equiv mixer ctrl) */
+ {0x11, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
/* Unmute Mic1 and Line1 pin widget input buffers since they start as
* inputs. If the pin mode is changed by the user the pin mode control
* will take care of enabling the pin's input/output buffers as needed.
@@ -3928,33 +4171,33 @@ static void alc260_auto_init(struct hda_codec *codec)
/*
* ALC260 configurations
*/
-static struct hda_board_config alc260_cfg_tbl[] = {
- { .modelname = "basic", .config = ALC260_BASIC },
- { .pci_subvendor = 0x104d, .pci_subdevice = 0x81bb,
- .config = ALC260_BASIC }, /* Sony VAIO */
- { .pci_subvendor = 0x104d, .pci_subdevice = 0x81cc,
- .config = ALC260_BASIC }, /* Sony VAIO VGN-S3HP */
- { .pci_subvendor = 0x104d, .pci_subdevice = 0x81cd,
- .config = ALC260_BASIC }, /* Sony VAIO */
- { .pci_subvendor = 0x152d, .pci_subdevice = 0x0729,
- .config = ALC260_BASIC }, /* CTL Travel Master U553W */
- { .modelname = "hp", .config = ALC260_HP },
- { .modelname = "hp-3013", .config = ALC260_HP_3013 },
- { .pci_subvendor = 0x103c, .pci_subdevice = 0x3010, .config = ALC260_HP_3013 },
- { .pci_subvendor = 0x103c, .pci_subdevice = 0x3011, .config = ALC260_HP },
- { .pci_subvendor = 0x103c, .pci_subdevice = 0x3012, .config = ALC260_HP_3013 },
- { .pci_subvendor = 0x103c, .pci_subdevice = 0x3013, .config = ALC260_HP_3013 },
- { .pci_subvendor = 0x103c, .pci_subdevice = 0x3014, .config = ALC260_HP },
- { .pci_subvendor = 0x103c, .pci_subdevice = 0x3015, .config = ALC260_HP },
- { .pci_subvendor = 0x103c, .pci_subdevice = 0x3016, .config = ALC260_HP },
- { .modelname = "fujitsu", .config = ALC260_FUJITSU_S702X },
- { .pci_subvendor = 0x10cf, .pci_subdevice = 0x1326, .config = ALC260_FUJITSU_S702X },
- { .modelname = "acer", .config = ALC260_ACER },
- { .pci_subvendor = 0x1025, .pci_subdevice = 0x008f, .config = ALC260_ACER },
+static const char *alc260_models[ALC260_MODEL_LAST] = {
+ [ALC260_BASIC] = "basic",
+ [ALC260_HP] = "hp",
+ [ALC260_HP_3013] = "hp-3013",
+ [ALC260_FUJITSU_S702X] = "fujitsu",
+ [ALC260_ACER] = "acer",
#ifdef CONFIG_SND_DEBUG
- { .modelname = "test", .config = ALC260_TEST },
+ [ALC260_TEST] = "test",
#endif
- { .modelname = "auto", .config = ALC260_AUTO },
+ [ALC260_AUTO] = "auto",
+};
+
+static struct snd_pci_quirk alc260_cfg_tbl[] = {
+ SND_PCI_QUIRK(0x1025, 0x007b, "Acer C20x", ALC260_ACER),
+ SND_PCI_QUIRK(0x1025, 0x008f, "Acer", ALC260_ACER),
+ SND_PCI_QUIRK(0x103c, 0x3010, "HP", ALC260_HP_3013),
+ SND_PCI_QUIRK(0x103c, 0x3011, "HP", ALC260_HP),
+ SND_PCI_QUIRK(0x103c, 0x3012, "HP", ALC260_HP_3013),
+ SND_PCI_QUIRK(0x103c, 0x3013, "HP", ALC260_HP_3013),
+ SND_PCI_QUIRK(0x103c, 0x3014, "HP", ALC260_HP),
+ SND_PCI_QUIRK(0x103c, 0x3015, "HP", ALC260_HP),
+ SND_PCI_QUIRK(0x103c, 0x3016, "HP", ALC260_HP),
+ SND_PCI_QUIRK(0x104d, 0x81bb, "Sony VAIO", ALC260_BASIC),
+ SND_PCI_QUIRK(0x104d, 0x81cc, "Sony VAIO", ALC260_BASIC),
+ SND_PCI_QUIRK(0x104d, 0x81cd, "Sony VAIO", ALC260_BASIC),
+ SND_PCI_QUIRK(0x10cf, 0x1326, "Fujitsu S702X", ALC260_FUJITSU_S702X),
+ SND_PCI_QUIRK(0x152d, 0x0729, "CTL U553W", ALC260_BASIC),
{}
};
@@ -4053,8 +4296,10 @@ static int patch_alc260(struct hda_codec *codec)
codec->spec = spec;
- board_config = snd_hda_check_board_config(codec, alc260_cfg_tbl);
- if (board_config < 0 || board_config >= ALC260_MODEL_LAST) {
+ board_config = snd_hda_check_board_config(codec, ALC260_MODEL_LAST,
+ alc260_models,
+ alc260_cfg_tbl);
+ if (board_config < 0) {
snd_printd(KERN_INFO "hda_codec: Unknown model for ALC260, "
"trying auto-probe from BIOS...\n");
board_config = ALC260_AUTO;
@@ -4207,8 +4452,10 @@ static struct snd_kcontrol_new alc882_base_mixer[] = {
HDA_CODEC_VOLUME("Line Playback Volume", 0x0b, 0x02, HDA_INPUT),
HDA_CODEC_MUTE("Line Playback Switch", 0x0b, 0x02, HDA_INPUT),
HDA_CODEC_VOLUME("Mic Playback Volume", 0x0b, 0x0, HDA_INPUT),
+ HDA_CODEC_VOLUME("Mic Boost", 0x18, 0, HDA_INPUT),
HDA_CODEC_MUTE("Mic Playback Switch", 0x0b, 0x0, HDA_INPUT),
HDA_CODEC_VOLUME("Front Mic Playback Volume", 0x0b, 0x1, HDA_INPUT),
+ HDA_CODEC_VOLUME("Front Mic Boost", 0x19, 0, HDA_INPUT),
HDA_CODEC_MUTE("Front Mic Playback Switch", 0x0b, 0x1, HDA_INPUT),
HDA_CODEC_VOLUME("PC Speaker Playback Volume", 0x0b, 0x05, HDA_INPUT),
HDA_CODEC_MUTE("PC Speaker Playback Switch", 0x0b, 0x05, HDA_INPUT),
@@ -4313,6 +4560,100 @@ static struct hda_verb alc882_eapd_verbs[] = {
{ }
};
+/* Mac Pro test */
+static struct snd_kcontrol_new alc882_macpro_mixer[] = {
+ HDA_CODEC_VOLUME("Front Playback Volume", 0x0c, 0x0, HDA_OUTPUT),
+ HDA_BIND_MUTE("Front Playback Switch", 0x0c, 2, HDA_INPUT),
+ HDA_CODEC_MUTE("Headphone Playback Switch", 0x18, 0x0, HDA_OUTPUT),
+ HDA_CODEC_VOLUME("Line Playback Volume", 0x0b, 0x01, HDA_INPUT),
+ HDA_CODEC_MUTE("Line Playback Switch", 0x0b, 0x01, HDA_INPUT),
+ HDA_CODEC_VOLUME("PC Speaker Playback Volume", 0x0b, 0x02, HDA_INPUT),
+ HDA_CODEC_MUTE("PC Speaker Playback Switch", 0x0b, 0x02, HDA_INPUT),
+ { } /* end */
+};
+
+static struct hda_verb alc882_macpro_init_verbs[] = {
+ /* Front mixer: unmute input/output amp left and right (volume = 0) */
+ {0x0c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
+ {0x0c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
+ {0x0c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
+ /* Front Pin: output 0 (0x0c) */
+ {0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
+ {0x15, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
+ {0x15, AC_VERB_SET_CONNECT_SEL, 0x00},
+ /* Front Mic pin: input vref at 80% */
+ {0x19, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80},
+ {0x19, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
+ /* Speaker: output */
+ {0x1a, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
+ {0x1a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
+ {0x1a, AC_VERB_SET_CONNECT_SEL, 0x04},
+ /* Headphone output (output 0 - 0x0c) */
+ {0x18, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP},
+ {0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
+ {0x18, AC_VERB_SET_CONNECT_SEL, 0x00},
+
+ /* FIXME: use matrix-type input source selection */
+ /* Mixer elements: 0x18, 19, 1a, 1b, 1c, 1d, 14, 15, 16, 17, 0b */
+ /* Input mixer1: unmute Mic, F-Mic, Line, CD inputs */
+ {0x24, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
+ {0x24, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(3)},
+ {0x24, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(2)},
+ {0x24, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(4)},
+ /* Input mixer2 */
+ {0x23, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
+ {0x23, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(3)},
+ {0x23, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(2)},
+ {0x23, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(4)},
+ /* Input mixer3 */
+ {0x22, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
+ {0x22, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(3)},
+ {0x22, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(2)},
+ {0x22, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(4)},
+ /* ADC1: mute amp left and right */
+ {0x07, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
+ {0x07, AC_VERB_SET_CONNECT_SEL, 0x00},
+ /* ADC2: mute amp left and right */
+ {0x08, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
+ {0x08, AC_VERB_SET_CONNECT_SEL, 0x00},
+ /* ADC3: mute amp left and right */
+ {0x09, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
+ {0x09, AC_VERB_SET_CONNECT_SEL, 0x00},
+
+ { }
+};
+static void alc882_gpio_mute(struct hda_codec *codec, int pin, int muted)
+{
+ unsigned int gpiostate, gpiomask, gpiodir;
+
+ gpiostate = snd_hda_codec_read(codec, codec->afg, 0,
+ AC_VERB_GET_GPIO_DATA, 0);
+
+ if (!muted)
+ gpiostate |= (1 << pin);
+ else
+ gpiostate &= ~(1 << pin);
+
+ gpiomask = snd_hda_codec_read(codec, codec->afg, 0,
+ AC_VERB_GET_GPIO_MASK, 0);
+ gpiomask |= (1 << pin);
+
+ gpiodir = snd_hda_codec_read(codec, codec->afg, 0,
+ AC_VERB_GET_GPIO_DIRECTION, 0);
+ gpiodir |= (1 << pin);
+
+
+ snd_hda_codec_write(codec, codec->afg, 0,
+ AC_VERB_SET_GPIO_MASK, gpiomask);
+ snd_hda_codec_write(codec, codec->afg, 0,
+ AC_VERB_SET_GPIO_DIRECTION, gpiodir);
+
+ msleep(1);
+
+ snd_hda_codec_write(codec, codec->afg, 0,
+ AC_VERB_SET_GPIO_DATA, gpiostate);
+}
+
/*
* generic initialization of ADC, input mixers and output mixers
*/
@@ -4435,19 +4776,20 @@ static struct snd_kcontrol_new alc882_capture_mixer[] = {
/*
* configuration and preset
*/
-static struct hda_board_config alc882_cfg_tbl[] = {
- { .modelname = "3stack-dig", .config = ALC882_3ST_DIG },
- { .modelname = "6stack-dig", .config = ALC882_6ST_DIG },
- { .pci_subvendor = 0x1462, .pci_subdevice = 0x6668,
- .config = ALC882_6ST_DIG }, /* MSI */
- { .pci_subvendor = 0x105b, .pci_subdevice = 0x6668,
- .config = ALC882_6ST_DIG }, /* Foxconn */
- { .pci_subvendor = 0x1019, .pci_subdevice = 0x6668,
- .config = ALC882_6ST_DIG }, /* ECS to Intel*/
- { .modelname = "arima", .config = ALC882_ARIMA },
- { .pci_subvendor = 0x161f, .pci_subdevice = 0x2054,
- .config = ALC882_ARIMA }, /* Arima W820Di1 */
- { .modelname = "auto", .config = ALC882_AUTO },
+static const char *alc882_models[ALC882_MODEL_LAST] = {
+ [ALC882_3ST_DIG] = "3stack-dig",
+ [ALC882_6ST_DIG] = "6stack-dig",
+ [ALC882_ARIMA] = "arima",
+ [ALC885_MACPRO] = "macpro",
+ [ALC882_AUTO] = "auto",
+};
+
+static struct snd_pci_quirk alc882_cfg_tbl[] = {
+ SND_PCI_QUIRK(0x1019, 0x6668, "ECS", ALC882_6ST_DIG),
+ SND_PCI_QUIRK(0x105b, 0x6668, "Foxconn", ALC882_6ST_DIG),
+ SND_PCI_QUIRK(0x1462, 0x6668, "MSI", ALC882_6ST_DIG),
+ SND_PCI_QUIRK(0x161f, 0x2054, "Arima W820", ALC882_ARIMA),
+ SND_PCI_QUIRK(0x1043, 0x81d8, "Asus P5WD", ALC882_6ST_DIG),
{}
};
@@ -4484,6 +4826,17 @@ static struct alc_config_preset alc882_presets[] = {
.channel_mode = alc882_sixstack_modes,
.input_mux = &alc882_capture_source,
},
+ [ALC885_MACPRO] = {
+ .mixers = { alc882_macpro_mixer },
+ .init_verbs = { alc882_macpro_init_verbs },
+ .num_dacs = ARRAY_SIZE(alc882_dac_nids),
+ .dac_nids = alc882_dac_nids,
+ .dig_out_nid = ALC882_DIGOUT_NID,
+ .dig_in_nid = ALC882_DIGIN_NID,
+ .num_channel_mode = ARRAY_SIZE(alc882_ch_modes),
+ .channel_mode = alc882_ch_modes,
+ .input_mux = &alc882_capture_source,
+ },
};
@@ -4584,7 +4937,9 @@ static int patch_alc882(struct hda_codec *codec)
codec->spec = spec;
- board_config = snd_hda_check_board_config(codec, alc882_cfg_tbl);
+ board_config = snd_hda_check_board_config(codec, ALC882_MODEL_LAST,
+ alc882_models,
+ alc882_cfg_tbl);
if (board_config < 0 || board_config >= ALC882_MODEL_LAST) {
printk(KERN_INFO "hda_codec: Unknown model for ALC882, "
@@ -4609,6 +4964,11 @@ static int patch_alc882(struct hda_codec *codec)
if (board_config != ALC882_AUTO)
setup_preset(spec, &alc882_presets[board_config]);
+ if (board_config == ALC885_MACPRO) {
+ alc882_gpio_mute(codec, 0, 0);
+ alc882_gpio_mute(codec, 1, 0);
+ }
+
spec->stream_name_analog = "ALC882 Analog";
spec->stream_analog_playback = &alc882_pcm_analog_playback;
spec->stream_analog_capture = &alc882_pcm_analog_capture;
@@ -4767,6 +5127,13 @@ static struct hda_channel_mode alc883_sixstack_modes[2] = {
{ 8, alc883_sixstack_ch8_init },
};
+static struct hda_verb alc883_medion_eapd_verbs[] = {
+ /* eanable EAPD on medion laptop */
+ {0x20, AC_VERB_SET_COEF_INDEX, 0x07},
+ {0x20, AC_VERB_SET_PROC_COEF, 0x3070},
+ { }
+};
+
/* Pin assignment: Front=0x14, Rear=0x15, CLFE=0x16, Side=0x17
* Mic=0x18, Front Mic=0x19, Line-In=0x1a, HP=0x1b
*/
@@ -4788,8 +5155,10 @@ static struct snd_kcontrol_new alc883_base_mixer[] = {
HDA_CODEC_VOLUME("Line Playback Volume", 0x0b, 0x02, HDA_INPUT),
HDA_CODEC_MUTE("Line Playback Switch", 0x0b, 0x02, HDA_INPUT),
HDA_CODEC_VOLUME("Mic Playback Volume", 0x0b, 0x0, HDA_INPUT),
+ HDA_CODEC_VOLUME("Mic Boost", 0x18, 0, HDA_INPUT),
HDA_CODEC_MUTE("Mic Playback Switch", 0x0b, 0x0, HDA_INPUT),
HDA_CODEC_VOLUME("Front Mic Playback Volume", 0x0b, 0x1, HDA_INPUT),
+ HDA_CODEC_VOLUME("Front Mic Boost", 0x19, 0, HDA_INPUT),
HDA_CODEC_MUTE("Front Mic Playback Switch", 0x0b, 0x1, HDA_INPUT),
HDA_CODEC_VOLUME("PC Speaker Playback Volume", 0x0b, 0x05, HDA_INPUT),
HDA_CODEC_MUTE("PC Speaker Playback Switch", 0x0b, 0x05, HDA_INPUT),
@@ -4818,8 +5187,10 @@ static struct snd_kcontrol_new alc883_3ST_2ch_mixer[] = {
HDA_CODEC_VOLUME("Line Playback Volume", 0x0b, 0x02, HDA_INPUT),
HDA_CODEC_MUTE("Line Playback Switch", 0x0b, 0x02, HDA_INPUT),
HDA_CODEC_VOLUME("Mic Playback Volume", 0x0b, 0x0, HDA_INPUT),
+ HDA_CODEC_VOLUME("Mic Boost", 0x18, 0, HDA_INPUT),
HDA_CODEC_MUTE("Mic Playback Switch", 0x0b, 0x0, HDA_INPUT),
HDA_CODEC_VOLUME("Front Mic Playback Volume", 0x0b, 0x1, HDA_INPUT),
+ HDA_CODEC_VOLUME("Front Mic Boost", 0x19, 0, HDA_INPUT),
HDA_CODEC_MUTE("Front Mic Playback Switch", 0x0b, 0x1, HDA_INPUT),
HDA_CODEC_VOLUME("PC Speaker Playback Volume", 0x0b, 0x05, HDA_INPUT),
HDA_CODEC_MUTE("PC Speaker Playback Switch", 0x0b, 0x05, HDA_INPUT),
@@ -4854,8 +5225,10 @@ static struct snd_kcontrol_new alc883_3ST_6ch_mixer[] = {
HDA_CODEC_VOLUME("Line Playback Volume", 0x0b, 0x02, HDA_INPUT),
HDA_CODEC_MUTE("Line Playback Switch", 0x0b, 0x02, HDA_INPUT),
HDA_CODEC_VOLUME("Mic Playback Volume", 0x0b, 0x0, HDA_INPUT),
+ HDA_CODEC_VOLUME("Mic Boost", 0x18, 0, HDA_INPUT),
HDA_CODEC_MUTE("Mic Playback Switch", 0x0b, 0x0, HDA_INPUT),
HDA_CODEC_VOLUME("Front Mic Playback Volume", 0x0b, 0x1, HDA_INPUT),
+ HDA_CODEC_VOLUME("Front Mic Boost", 0x19, 0, HDA_INPUT),
HDA_CODEC_MUTE("Front Mic Playback Switch", 0x0b, 0x1, HDA_INPUT),
HDA_CODEC_VOLUME("PC Speaker Playback Volume", 0x0b, 0x05, HDA_INPUT),
HDA_CODEC_MUTE("PC Speaker Playback Switch", 0x0b, 0x05, HDA_INPUT),
@@ -4875,6 +5248,101 @@ static struct snd_kcontrol_new alc883_3ST_6ch_mixer[] = {
{ } /* end */
};
+static struct snd_kcontrol_new alc883_fivestack_mixer[] = {
+ HDA_CODEC_VOLUME("Front Playback Volume", 0x0c, 0x0, HDA_OUTPUT),
+ HDA_CODEC_MUTE("Front Playback Switch", 0x14, 0x0, HDA_OUTPUT),
+ HDA_CODEC_VOLUME("Surround Playback Volume", 0x0d, 0x0, HDA_OUTPUT),
+ HDA_CODEC_MUTE("Surround Playback Switch", 0x15, 0x0, HDA_OUTPUT),
+ HDA_CODEC_VOLUME_MONO("Center Playback Volume", 0x0e, 1, 0x0, HDA_OUTPUT),
+ HDA_CODEC_VOLUME_MONO("LFE Playback Volume", 0x0e, 2, 0x0, HDA_OUTPUT),
+ HDA_CODEC_MUTE_MONO("Center Playback Switch", 0x16, 1, 0x0, HDA_OUTPUT),
+ HDA_CODEC_MUTE_MONO("LFE Playback Switch", 0x16, 2, 0x0, HDA_OUTPUT),
+ HDA_CODEC_MUTE("Headphone Playback Switch", 0x1b, 0x0, HDA_OUTPUT),
+ HDA_CODEC_VOLUME("CD Playback Volume", 0x0b, 0x04, HDA_INPUT),
+ HDA_CODEC_MUTE("CD Playback Switch", 0x0b, 0x04, HDA_INPUT),
+ HDA_CODEC_VOLUME("Line Playback Volume", 0x0b, 0x02, HDA_INPUT),
+ HDA_CODEC_MUTE("Line Playback Switch", 0x0b, 0x02, HDA_INPUT),
+ HDA_CODEC_VOLUME("Mic Playback Volume", 0x0b, 0x0, HDA_INPUT),
+ HDA_CODEC_VOLUME("Mic Boost", 0x18, 0, HDA_INPUT),
+ HDA_CODEC_MUTE("Mic Playback Switch", 0x0b, 0x0, HDA_INPUT),
+ HDA_CODEC_VOLUME("Front Mic Playback Volume", 0x0b, 0x1, HDA_INPUT),
+ HDA_CODEC_VOLUME("Front Mic Boost", 0x19, 0, HDA_INPUT),
+ HDA_CODEC_MUTE("Front Mic Playback Switch", 0x0b, 0x1, HDA_INPUT),
+ HDA_CODEC_VOLUME("PC Speaker Playback Volume", 0x0b, 0x05, HDA_INPUT),
+ HDA_CODEC_MUTE("PC Speaker Playback Switch", 0x0b, 0x05, HDA_INPUT),
+ HDA_CODEC_VOLUME("Capture Volume", 0x08, 0x0, HDA_INPUT),
+ HDA_CODEC_MUTE("Capture Switch", 0x08, 0x0, HDA_INPUT),
+
+ {
+ .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+ /* .name = "Capture Source", */
+ .name = "Input Source",
+ .count = 1,
+ .info = alc883_mux_enum_info,
+ .get = alc883_mux_enum_get,
+ .put = alc883_mux_enum_put,
+ },
+ { } /* end */
+};
+
+static struct snd_kcontrol_new alc883_tagra_mixer[] = {
+ HDA_CODEC_VOLUME("Front Playback Volume", 0x0c, 0x0, HDA_OUTPUT),
+ HDA_CODEC_MUTE("Headphone Playback Switch", 0x14, 0x0, HDA_OUTPUT),
+ HDA_CODEC_MUTE("Front Playback Switch", 0x1b, 0x0, HDA_OUTPUT),
+ HDA_CODEC_VOLUME("Surround Playback Volume", 0x0d, 0x0, HDA_OUTPUT),
+ HDA_BIND_MUTE("Surround Playback Switch", 0x0d, 2, HDA_INPUT),
+ HDA_CODEC_VOLUME_MONO("Center Playback Volume", 0x0e, 1, 0x0, HDA_OUTPUT),
+ HDA_CODEC_VOLUME_MONO("LFE Playback Volume", 0x0e, 2, 0x0, HDA_OUTPUT),
+ HDA_BIND_MUTE_MONO("Center Playback Switch", 0x0e, 1, 2, HDA_INPUT),
+ HDA_BIND_MUTE_MONO("LFE Playback Switch", 0x0e, 2, 2, HDA_INPUT),
+ HDA_CODEC_VOLUME("CD Playback Volume", 0x0b, 0x04, HDA_INPUT),
+ HDA_CODEC_MUTE("CD Playback Switch", 0x0b, 0x04, HDA_INPUT),
+ HDA_CODEC_VOLUME("Line Playback Volume", 0x0b, 0x02, HDA_INPUT),
+ HDA_CODEC_MUTE("Line Playback Switch", 0x0b, 0x02, HDA_INPUT),
+ HDA_CODEC_VOLUME("Mic Playback Volume", 0x0b, 0x0, HDA_INPUT),
+ HDA_CODEC_VOLUME("Mic Boost", 0x18, 0, HDA_INPUT),
+ HDA_CODEC_MUTE("Mic Playback Switch", 0x0b, 0x0, HDA_INPUT),
+ HDA_CODEC_VOLUME("Capture Volume", 0x08, 0x0, HDA_INPUT),
+ HDA_CODEC_MUTE("Capture Switch", 0x08, 0x0, HDA_INPUT),
+ HDA_CODEC_VOLUME_IDX("Capture Volume", 1, 0x09, 0x0, HDA_INPUT),
+ HDA_CODEC_MUTE_IDX("Capture Switch", 1, 0x09, 0x0, HDA_INPUT),
+ {
+ .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+ /* .name = "Capture Source", */
+ .name = "Input Source",
+ .count = 2,
+ .info = alc883_mux_enum_info,
+ .get = alc883_mux_enum_get,
+ .put = alc883_mux_enum_put,
+ },
+ { } /* end */
+};
+
+static struct snd_kcontrol_new alc883_tagra_2ch_mixer[] = {
+ HDA_CODEC_VOLUME("Front Playback Volume", 0x0c, 0x0, HDA_OUTPUT),
+ HDA_CODEC_MUTE("Headphone Playback Switch", 0x14, 0x0, HDA_OUTPUT),
+ HDA_CODEC_MUTE("Front Playback Switch", 0x1b, 0x0, HDA_OUTPUT),
+ HDA_CODEC_VOLUME("CD Playback Volume", 0x0b, 0x04, HDA_INPUT),
+ HDA_CODEC_MUTE("CD Playback Switch", 0x0b, 0x04, HDA_INPUT),
+ HDA_CODEC_VOLUME("Mic Playback Volume", 0x0b, 0x0, HDA_INPUT),
+ HDA_CODEC_VOLUME("Mic Boost", 0x18, 0, HDA_INPUT),
+ HDA_CODEC_MUTE("Mic Playback Switch", 0x0b, 0x0, HDA_INPUT),
+ HDA_CODEC_VOLUME("Capture Volume", 0x08, 0x0, HDA_INPUT),
+ HDA_CODEC_MUTE("Capture Switch", 0x08, 0x0, HDA_INPUT),
+ HDA_CODEC_VOLUME_IDX("Capture Volume", 1, 0x09, 0x0, HDA_INPUT),
+ HDA_CODEC_MUTE_IDX("Capture Switch", 1, 0x09, 0x0, HDA_INPUT),
+ {
+ .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+ /* .name = "Capture Source", */
+ .name = "Input Source",
+ .count = 2,
+ .info = alc883_mux_enum_info,
+ .get = alc883_mux_enum_get,
+ .put = alc883_mux_enum_put,
+ },
+ { } /* end */
+};
+
static struct snd_kcontrol_new alc883_chmode_mixer[] = {
{
.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
@@ -4963,6 +5431,45 @@ static struct hda_verb alc883_init_verbs[] = {
{ }
};
+static struct hda_verb alc883_tagra_verbs[] = {
+ {0x0c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
+ {0x0c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
+
+ {0x14, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP},
+ {0x1b, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
+
+ {0x18, AC_VERB_SET_CONNECT_SEL, 0x02}, /* mic/clfe */
+ {0x1a, AC_VERB_SET_CONNECT_SEL, 0x01}, /* line/surround */
+ {0x1b, AC_VERB_SET_CONNECT_SEL, 0x00}, /* HP */
+
+ {0x14, AC_VERB_SET_UNSOLICITED_ENABLE, ALC880_HP_EVENT | AC_USRSP_EN},
+ {0x01, AC_VERB_SET_GPIO_MASK, 0x03},
+ {0x01, AC_VERB_SET_GPIO_DIRECTION, 0x03},
+ {0x01, AC_VERB_SET_GPIO_DATA, 0x03},
+
+ { } /* end */
+};
+
+/* toggle speaker-output according to the hp-jack state */
+static void alc883_tagra_automute(struct hda_codec *codec)
+{
+ unsigned int present;
+
+ present = snd_hda_codec_read(codec, 0x14, 0,
+ AC_VERB_GET_PIN_SENSE, 0) & 0x80000000;
+ snd_hda_codec_amp_update(codec, 0x1b, 0, HDA_OUTPUT, 0,
+ 0x80, present ? 0x80 : 0);
+ snd_hda_codec_amp_update(codec, 0x1b, 1, HDA_OUTPUT, 0,
+ 0x80, present ? 0x80 : 0);
+ snd_hda_codec_write(codec, 1, 0, AC_VERB_SET_GPIO_DATA, present ? 1 : 3);
+}
+
+static void alc883_tagra_unsol_event(struct hda_codec *codec, unsigned int res)
+{
+ if ((res >> 26) == ALC880_HP_EVENT)
+ alc883_tagra_automute(codec);
+}
+
/*
* generic initialization of ADC, input mixers and output mixers
*/
@@ -5057,32 +5564,42 @@ static struct snd_kcontrol_new alc883_capture_mixer[] = {
/*
* configuration and preset
*/
-static struct hda_board_config alc883_cfg_tbl[] = {
- { .modelname = "3stack-dig", .config = ALC883_3ST_2ch_DIG },
- { .modelname = "3stack-6ch-dig", .config = ALC883_3ST_6ch_DIG },
- { .pci_subvendor = 0x1019, .pci_subdevice = 0x6668,
- .config = ALC883_3ST_6ch_DIG }, /* ECS to Intel*/
- { .modelname = "3stack-6ch", .config = ALC883_3ST_6ch },
- { .pci_subvendor = 0x108e, .pci_subdevice = 0x534d,
- .config = ALC883_3ST_6ch },
- { .pci_subvendor = 0x8086, .pci_subdevice = 0xd601,
- .config = ALC883_3ST_6ch }, /* D102GGC */
- { .modelname = "6stack-dig", .config = ALC883_6ST_DIG },
- { .pci_subvendor = 0x1462, .pci_subdevice = 0x6668,
- .config = ALC883_6ST_DIG }, /* MSI */
- { .pci_subvendor = 0x1462, .pci_subdevice = 0x7280,
- .config = ALC883_6ST_DIG }, /* MSI K9A Platinum (MS-7280) */
- { .pci_subvendor = 0x105b, .pci_subdevice = 0x6668,
- .config = ALC883_6ST_DIG }, /* Foxconn */
- { .modelname = "6stack-dig-demo", .config = ALC888_DEMO_BOARD },
- { .modelname = "acer", .config = ALC883_ACER },
- { .pci_subvendor = 0x1025, .pci_subdevice = 0/*0x0102*/,
- .config = ALC883_ACER },
- { .pci_subvendor = 0x1025, .pci_subdevice = 0x0102,
- .config = ALC883_ACER },
- { .pci_subvendor = 0x1025, .pci_subdevice = 0x009f,
- .config = ALC883_ACER },
- { .modelname = "auto", .config = ALC883_AUTO },
+static const char *alc883_models[ALC883_MODEL_LAST] = {
+ [ALC883_3ST_2ch_DIG] = "3stack-dig",
+ [ALC883_3ST_6ch_DIG] = "3stack-6ch-dig",
+ [ALC883_3ST_6ch] = "3stack-6ch",
+ [ALC883_6ST_DIG] = "6stack-dig",
+ [ALC883_TARGA_DIG] = "targa-dig",
+ [ALC883_TARGA_2ch_DIG] = "targa-2ch-dig",
+ [ALC888_DEMO_BOARD] = "6stack-dig-demo",
+ [ALC883_ACER] = "acer",
+ [ALC883_MEDION] = "medion",
+ [ALC883_LAPTOP_EAPD] = "laptop-eapd",
+ [ALC883_AUTO] = "auto",
+};
+
+static struct snd_pci_quirk alc883_cfg_tbl[] = {
+ SND_PCI_QUIRK(0x1019, 0x6668, "ECS", ALC883_3ST_6ch_DIG),
+ SND_PCI_QUIRK(0x108e, 0x534d, NULL, ALC883_3ST_6ch),
+ SND_PCI_QUIRK(0x1558, 0, "Clevo laptop", ALC883_LAPTOP_EAPD),
+ SND_PCI_QUIRK(0x105b, 0x6668, "Foxconn", ALC883_6ST_DIG),
+ SND_PCI_QUIRK(0x1462, 0x6668, "MSI", ALC883_6ST_DIG),
+ SND_PCI_QUIRK(0x1462, 0x7187, "MSI", ALC883_6ST_DIG),
+ SND_PCI_QUIRK(0x1462, 0x7280, "MSI", ALC883_6ST_DIG),
+ SND_PCI_QUIRK(0x1462, 0x0579, "MSI", ALC883_TARGA_2ch_DIG),
+ SND_PCI_QUIRK(0x1462, 0x3ef9, "MSI", ALC883_TARGA_DIG),
+ SND_PCI_QUIRK(0x1462, 0x3b7f, "MSI", ALC883_TARGA_2ch_DIG),
+ SND_PCI_QUIRK(0x1462, 0x3fcc, "MSI", ALC883_TARGA_DIG),
+ SND_PCI_QUIRK(0x1462, 0x3fc1, "MSI", ALC883_TARGA_DIG),
+ SND_PCI_QUIRK(0x1462, 0x3fc3, "MSI", ALC883_TARGA_DIG),
+ SND_PCI_QUIRK(0x1462, 0x4314, "MSI", ALC883_TARGA_DIG),
+ SND_PCI_QUIRK(0x1462, 0x4319, "MSI", ALC883_TARGA_DIG),
+ SND_PCI_QUIRK(0x1462, 0x4324, "MSI", ALC883_TARGA_DIG),
+ SND_PCI_QUIRK(0x1462, 0xa422, "MSI", ALC883_TARGA_2ch_DIG),
+ SND_PCI_QUIRK(0x1025, 0, "Acer laptop", ALC883_ACER),
+ SND_PCI_QUIRK(0x161f, 0x2054, "Medion laptop", ALC883_MEDION),
+ SND_PCI_QUIRK(0x1071, 0x8258, "Evesham Voyaeger", ALC883_LAPTOP_EAPD),
+ SND_PCI_QUIRK(0x8086, 0xd601, "D102GGC", ALC883_3ST_6ch),
{}
};
@@ -5139,6 +5656,35 @@ static struct alc_config_preset alc883_presets[] = {
.channel_mode = alc883_sixstack_modes,
.input_mux = &alc883_capture_source,
},
+ [ALC883_TARGA_DIG] = {
+ .mixers = { alc883_tagra_mixer, alc883_chmode_mixer },
+ .init_verbs = { alc883_init_verbs, alc883_tagra_verbs},
+ .num_dacs = ARRAY_SIZE(alc883_dac_nids),
+ .dac_nids = alc883_dac_nids,
+ .dig_out_nid = ALC883_DIGOUT_NID,
+ .num_adc_nids = ARRAY_SIZE(alc883_adc_nids),
+ .adc_nids = alc883_adc_nids,
+ .num_channel_mode = ARRAY_SIZE(alc883_3ST_6ch_modes),
+ .channel_mode = alc883_3ST_6ch_modes,
+ .need_dac_fix = 1,
+ .input_mux = &alc883_capture_source,
+ .unsol_event = alc883_tagra_unsol_event,
+ .init_hook = alc883_tagra_automute,
+ },
+ [ALC883_TARGA_2ch_DIG] = {
+ .mixers = { alc883_tagra_2ch_mixer},
+ .init_verbs = { alc883_init_verbs, alc883_tagra_verbs},
+ .num_dacs = ARRAY_SIZE(alc883_dac_nids),
+ .dac_nids = alc883_dac_nids,
+ .dig_out_nid = ALC883_DIGOUT_NID,
+ .num_adc_nids = ARRAY_SIZE(alc883_adc_nids),
+ .adc_nids = alc883_adc_nids,
+ .num_channel_mode = ARRAY_SIZE(alc883_3ST_2ch_modes),
+ .channel_mode = alc883_3ST_2ch_modes,
+ .input_mux = &alc883_capture_source,
+ .unsol_event = alc883_tagra_unsol_event,
+ .init_hook = alc883_tagra_automute,
+ },
[ALC888_DEMO_BOARD] = {
.mixers = { alc883_base_mixer, alc883_chmode_mixer },
.init_verbs = { alc883_init_verbs },
@@ -5169,6 +5715,31 @@ static struct alc_config_preset alc883_presets[] = {
.channel_mode = alc883_3ST_2ch_modes,
.input_mux = &alc883_capture_source,
},
+ [ALC883_MEDION] = {
+ .mixers = { alc883_fivestack_mixer,
+ alc883_chmode_mixer },
+ .init_verbs = { alc883_init_verbs,
+ alc883_medion_eapd_verbs },
+ .num_dacs = ARRAY_SIZE(alc883_dac_nids),
+ .dac_nids = alc883_dac_nids,
+ .num_adc_nids = ARRAY_SIZE(alc883_adc_nids),
+ .adc_nids = alc883_adc_nids,
+ .num_channel_mode = ARRAY_SIZE(alc883_sixstack_modes),
+ .channel_mode = alc883_sixstack_modes,
+ .input_mux = &alc883_capture_source,
+ },
+ [ALC883_LAPTOP_EAPD] = {
+ .mixers = { alc883_base_mixer,
+ alc883_chmode_mixer },
+ .init_verbs = { alc883_init_verbs, alc882_eapd_verbs },
+ .num_dacs = ARRAY_SIZE(alc883_dac_nids),
+ .dac_nids = alc883_dac_nids,
+ .num_adc_nids = ARRAY_SIZE(alc883_adc_nids),
+ .adc_nids = alc883_adc_nids,
+ .num_channel_mode = ARRAY_SIZE(alc883_3ST_2ch_modes),
+ .channel_mode = alc883_3ST_2ch_modes,
+ .input_mux = &alc883_capture_source,
+ },
};
@@ -5277,8 +5848,10 @@ static int patch_alc883(struct hda_codec *codec)
codec->spec = spec;
- board_config = snd_hda_check_board_config(codec, alc883_cfg_tbl);
- if (board_config < 0 || board_config >= ALC883_MODEL_LAST) {
+ board_config = snd_hda_check_board_config(codec, ALC883_MODEL_LAST,
+ alc883_models,
+ alc883_cfg_tbl);
+ if (board_config < 0) {
printk(KERN_INFO "hda_codec: Unknown model for ALC883, "
"trying auto-probe from BIOS...\n");
board_config = ALC883_AUTO;
@@ -5355,6 +5928,24 @@ static struct snd_kcontrol_new alc262_base_mixer[] = {
{ } /* end */
};
+static struct snd_kcontrol_new alc262_hippo1_mixer[] = {
+ HDA_CODEC_VOLUME("Front Playback Volume", 0x0c, 0x0, HDA_OUTPUT),
+ HDA_CODEC_MUTE("Front Playback Switch", 0x14, 0x0, HDA_OUTPUT),
+ HDA_CODEC_VOLUME("CD Playback Volume", 0x0b, 0x04, HDA_INPUT),
+ HDA_CODEC_MUTE("CD Playback Switch", 0x0b, 0x04, HDA_INPUT),
+ HDA_CODEC_VOLUME("Line Playback Volume", 0x0b, 0x02, HDA_INPUT),
+ HDA_CODEC_MUTE("Line Playback Switch", 0x0b, 0x02, HDA_INPUT),
+ HDA_CODEC_VOLUME("Mic Playback Volume", 0x0b, 0x0, HDA_INPUT),
+ HDA_CODEC_MUTE("Mic Playback Switch", 0x0b, 0x0, HDA_INPUT),
+ HDA_CODEC_VOLUME("Front Mic Playback Volume", 0x0b, 0x01, HDA_INPUT),
+ HDA_CODEC_MUTE("Front Mic Playback Switch", 0x0b, 0x01, HDA_INPUT),
+ /* HDA_CODEC_VOLUME("PC Beep Playback Volume", 0x0b, 0x05, HDA_INPUT),
+ HDA_CODEC_MUTE("PC Beelp Playback Switch", 0x0b, 0x05, HDA_INPUT), */
+ /*HDA_CODEC_VOLUME("Headphone Playback Volume", 0x0D, 0x0, HDA_OUTPUT),*/
+ HDA_CODEC_MUTE("Headphone Playback Switch", 0x1b, 0x0, HDA_OUTPUT),
+ { } /* end */
+};
+
static struct snd_kcontrol_new alc262_HP_BPC_mixer[] = {
HDA_CODEC_VOLUME("Front Playback Volume", 0x0c, 0x0, HDA_OUTPUT),
HDA_CODEC_MUTE("Front Playback Switch", 0x15, 0x0, HDA_OUTPUT),
@@ -5377,6 +5968,30 @@ static struct snd_kcontrol_new alc262_HP_BPC_mixer[] = {
{ } /* end */
};
+static struct snd_kcontrol_new alc262_HP_BPC_WildWest_mixer[] = {
+ HDA_CODEC_VOLUME("Front Playback Volume", 0x0c, 0x0, HDA_OUTPUT),
+ HDA_CODEC_MUTE("Front Playback Switch", 0x1b, 0x0, HDA_OUTPUT),
+ HDA_CODEC_VOLUME("Headphone Playback Volume", 0x0d, 0x0, HDA_OUTPUT),
+ HDA_CODEC_MUTE("Headphone Playback Switch", 0x15, 0x0, HDA_OUTPUT),
+ HDA_CODEC_VOLUME_MONO("Mono Playback Volume", 0x0e, 2, 0x0, HDA_OUTPUT),
+ HDA_CODEC_MUTE_MONO("Mono Playback Switch", 0x16, 2, 0x0, HDA_OUTPUT),
+ HDA_CODEC_VOLUME("Front Mic Playback Volume", 0x0b, 0x02, HDA_INPUT),
+ HDA_CODEC_MUTE("Front Mic Playback Switch", 0x0b, 0x02, HDA_INPUT),
+ HDA_CODEC_VOLUME("Line Playback Volume", 0x0b, 0x01, HDA_INPUT),
+ HDA_CODEC_MUTE("Line Playback Switch", 0x0b, 0x01, HDA_INPUT),
+ HDA_CODEC_VOLUME("CD Playback Volume", 0x0b, 0x04, HDA_INPUT),
+ HDA_CODEC_MUTE("CD Playback Switch", 0x0b, 0x04, HDA_INPUT),
+ HDA_CODEC_VOLUME("PC Beep Playback Volume", 0x0b, 0x05, HDA_INPUT),
+ HDA_CODEC_MUTE("PC Beep Playback Switch", 0x0b, 0x05, HDA_INPUT),
+ { } /* end */
+};
+
+static struct snd_kcontrol_new alc262_HP_BPC_WildWest_option_mixer[] = {
+ HDA_CODEC_VOLUME("Rear Mic Playback Volume", 0x0b, 0x0, HDA_INPUT),
+ HDA_CODEC_MUTE("Rear Mic Playback Switch", 0x0b, 0x0, HDA_INPUT),
+ { } /* end */
+};
+
#define alc262_capture_mixer alc882_capture_mixer
#define alc262_capture_alt_mixer alc882_capture_alt_mixer
@@ -5459,6 +6074,103 @@ static struct hda_verb alc262_init_verbs[] = {
{ }
};
+static struct hda_verb alc262_hippo_unsol_verbs[] = {
+ {0x15, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | ALC880_HP_EVENT},
+ {0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP},
+ {}
+};
+
+static struct hda_verb alc262_hippo1_unsol_verbs[] = {
+ {0x1b, AC_VERB_SET_PIN_WIDGET_CONTROL, 0xc0},
+ {0x1b, AC_VERB_SET_CONNECT_SEL, 0x00},
+ {0x1b, AC_VERB_SET_AMP_GAIN_MUTE, 0x0000},
+
+ {0x1b, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | ALC880_HP_EVENT},
+ {0x1b, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP},
+ {}
+};
+
+/* mute/unmute internal speaker according to the hp jack and mute state */
+static void alc262_hippo_automute(struct hda_codec *codec, int force)
+{
+ struct alc_spec *spec = codec->spec;
+ unsigned int mute;
+
+ if (force || ! spec->sense_updated) {
+ unsigned int present;
+ /* need to execute and sync at first */
+ snd_hda_codec_read(codec, 0x15, 0, AC_VERB_SET_PIN_SENSE, 0);
+ present = snd_hda_codec_read(codec, 0x15, 0,
+ AC_VERB_GET_PIN_SENSE, 0);
+ spec->jack_present = (present & 0x80000000) != 0;
+ spec->sense_updated = 1;
+ }
+ if (spec->jack_present) {
+ /* mute internal speaker */
+ snd_hda_codec_amp_update(codec, 0x14, 0, HDA_OUTPUT, 0,
+ 0x80, 0x80);
+ snd_hda_codec_amp_update(codec, 0x14, 1, HDA_OUTPUT, 0,
+ 0x80, 0x80);
+ } else {
+ /* unmute internal speaker if necessary */
+ mute = snd_hda_codec_amp_read(codec, 0x15, 0, HDA_OUTPUT, 0);
+ snd_hda_codec_amp_update(codec, 0x14, 0, HDA_OUTPUT, 0,
+ 0x80, mute & 0x80);
+ mute = snd_hda_codec_amp_read(codec, 0x15, 1, HDA_OUTPUT, 0);
+ snd_hda_codec_amp_update(codec, 0x14, 1, HDA_OUTPUT, 0,
+ 0x80, mute & 0x80);
+ }
+}
+
+/* unsolicited event for HP jack sensing */
+static void alc262_hippo_unsol_event(struct hda_codec *codec,
+ unsigned int res)
+{
+ if ((res >> 26) != ALC880_HP_EVENT)
+ return;
+ alc262_hippo_automute(codec, 1);
+}
+
+static void alc262_hippo1_automute(struct hda_codec *codec, int force)
+{
+ struct alc_spec *spec = codec->spec;
+ unsigned int mute;
+
+ if (force || ! spec->sense_updated) {
+ unsigned int present;
+ /* need to execute and sync at first */
+ snd_hda_codec_read(codec, 0x1b, 0, AC_VERB_SET_PIN_SENSE, 0);
+ present = snd_hda_codec_read(codec, 0x1b, 0,
+ AC_VERB_GET_PIN_SENSE, 0);
+ spec->jack_present = (present & 0x80000000) != 0;
+ spec->sense_updated = 1;
+ }
+ if (spec->jack_present) {
+ /* mute internal speaker */
+ snd_hda_codec_amp_update(codec, 0x14, 0, HDA_OUTPUT, 0,
+ 0x80, 0x80);
+ snd_hda_codec_amp_update(codec, 0x14, 1, HDA_OUTPUT, 0,
+ 0x80, 0x80);
+ } else {
+ /* unmute internal speaker if necessary */
+ mute = snd_hda_codec_amp_read(codec, 0x1b, 0, HDA_OUTPUT, 0);
+ snd_hda_codec_amp_update(codec, 0x14, 0, HDA_OUTPUT, 0,
+ 0x80, mute & 0x80);
+ mute = snd_hda_codec_amp_read(codec, 0x1b, 1, HDA_OUTPUT, 0);
+ snd_hda_codec_amp_update(codec, 0x14, 1, HDA_OUTPUT, 0,
+ 0x80, mute & 0x80);
+ }
+}
+
+/* unsolicited event for HP jack sensing */
+static void alc262_hippo1_unsol_event(struct hda_codec *codec,
+ unsigned int res)
+{
+ if ((res >> 26) != ALC880_HP_EVENT)
+ return;
+ alc262_hippo1_automute(codec, 1);
+}
+
/*
* fujitsu model
* 0x14 = headphone/spdif-out, 0x15 = internal speaker
@@ -5809,6 +6521,100 @@ static struct hda_verb alc262_HP_BPC_init_verbs[] = {
{ }
};
+static struct hda_verb alc262_HP_BPC_WildWest_init_verbs[] = {
+ /*
+ * Unmute ADC0-2 and set the default input to mic-in
+ */
+ {0x07, AC_VERB_SET_CONNECT_SEL, 0x00},
+ {0x07, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
+ {0x08, AC_VERB_SET_CONNECT_SEL, 0x00},
+ {0x08, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
+ {0x09, AC_VERB_SET_CONNECT_SEL, 0x00},
+ {0x09, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
+
+ /* Unmute input amps (CD, Line In, Mic 1 & Mic 2) of the analog-loopback
+ * mixer widget
+ * Note: PASD motherboards uses the Line In 2 as the input for front
+ * panel mic (mic 2)
+ */
+ /* Amp Indices: Mic1 = 0, Mic2 = 1, Line1 = 2, Line2 = 3, CD = 4 */
+ {0x0b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
+ {0x0b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
+ {0x0b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(2)},
+ {0x0b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(3)},
+ {0x0b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(4)},
+ {0x0b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(5)},
+ {0x0b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(6)},
+ {0x0b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(7)},
+ /*
+ * Set up output mixers (0x0c - 0x0e)
+ */
+ /* set vol=0 to output mixers */
+ {0x0c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
+ {0x0d, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
+ {0x0e, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
+
+ /* set up input amps for analog loopback */
+ /* Amp Indices: DAC = 0, mixer = 1 */
+ {0x0c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
+ {0x0c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
+ {0x0d, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
+ {0x0d, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
+ {0x0e, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
+ {0x0e, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
+
+
+ {0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP }, /* HP */
+ {0x16, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT }, /* Mono */
+ {0x18, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80 }, /* rear MIC */
+ {0x19, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN }, /* Line in */
+ {0x1a, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80 }, /* Front MIC */
+ {0x1b, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT }, /* Line out */
+ {0x1c, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN }, /* CD in */
+
+ {0x1b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE },
+ {0x15, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE },
+
+ {0x1b, AC_VERB_SET_CONNECT_SEL, 0x00},
+ {0x15, AC_VERB_SET_CONNECT_SEL, 0x01},
+
+ /* {0x14, AC_VERB_SET_AMP_GAIN_MUTE, 0x7023 }, */
+ {0x18, AC_VERB_SET_AMP_GAIN_MUTE, 0x7000 },
+ {0x19, AC_VERB_SET_AMP_GAIN_MUTE, 0x7000 },
+ {0x1a, AC_VERB_SET_AMP_GAIN_MUTE, 0x7023 },
+ {0x1c, AC_VERB_SET_AMP_GAIN_MUTE, 0x7000 },
+ {0x1d, AC_VERB_SET_AMP_GAIN_MUTE, 0x7000 },
+
+ /* FIXME: use matrix-type input source selection */
+ /* Mixer elements: 0x18, 19, 1a, 1b, 1c, 1d, 14, 15, 16, 17, 0b */
+ /* Input mixer1: unmute Mic, F-Mic, Line, CD inputs */
+ {0x24, AC_VERB_SET_AMP_GAIN_MUTE, (0x7000 | (0x00 << 8))}, /*rear MIC*/
+ {0x24, AC_VERB_SET_AMP_GAIN_MUTE, (0x7000 | (0x01 << 8))}, /*Line in*/
+ {0x24, AC_VERB_SET_AMP_GAIN_MUTE, (0x7000 | (0x02 << 8))}, /*F MIC*/
+ {0x24, AC_VERB_SET_AMP_GAIN_MUTE, (0x7000 | (0x03 << 8))}, /*Front*/
+ {0x24, AC_VERB_SET_AMP_GAIN_MUTE, (0x7000 | (0x04 << 8))}, /*CD*/
+ /* {0x24, AC_VERB_SET_AMP_GAIN_MUTE, (0x7000 | (0x06 << 8))}, */
+ {0x24, AC_VERB_SET_AMP_GAIN_MUTE, (0x7000 | (0x07 << 8))}, /*HP*/
+ /* Input mixer2 */
+ {0x23, AC_VERB_SET_AMP_GAIN_MUTE, (0x7000 | (0x00 << 8))},
+ {0x23, AC_VERB_SET_AMP_GAIN_MUTE, (0x7000 | (0x01 << 8))},
+ {0x23, AC_VERB_SET_AMP_GAIN_MUTE, (0x7000 | (0x02 << 8))},
+ {0x23, AC_VERB_SET_AMP_GAIN_MUTE, (0x7000 | (0x03 << 8))},
+ {0x23, AC_VERB_SET_AMP_GAIN_MUTE, (0x7000 | (0x04 << 8))},
+ /* {0x23, AC_VERB_SET_AMP_GAIN_MUTE, (0x7000 | (0x06 << 8))}, */
+ {0x23, AC_VERB_SET_AMP_GAIN_MUTE, (0x7000 | (0x07 << 8))},
+ /* Input mixer3 */
+ {0x22, AC_VERB_SET_AMP_GAIN_MUTE, (0x7000 | (0x00 << 8))},
+ {0x22, AC_VERB_SET_AMP_GAIN_MUTE, (0x7000 | (0x01 << 8))},
+ {0x22, AC_VERB_SET_AMP_GAIN_MUTE, (0x7000 | (0x02 << 8))},
+ {0x22, AC_VERB_SET_AMP_GAIN_MUTE, (0x7000 | (0x03 << 8))},
+ {0x22, AC_VERB_SET_AMP_GAIN_MUTE, (0x7000 | (0x04 << 8))},
+ /* {0x22, AC_VERB_SET_AMP_GAIN_MUTE, (0x7000 | (0x06 << 8))}, */
+ {0x22, AC_VERB_SET_AMP_GAIN_MUTE, (0x7000 | (0x07 << 8))},
+
+ { }
+};
+
/* pcm configuration: identiacal with ALC880 */
#define alc262_pcm_analog_playback alc880_pcm_analog_playback
#define alc262_pcm_analog_capture alc880_pcm_analog_capture
@@ -5866,26 +6672,35 @@ static void alc262_auto_init(struct hda_codec *codec)
/*
* configuration and preset
*/
-static struct hda_board_config alc262_cfg_tbl[] = {
- { .modelname = "basic", .config = ALC262_BASIC },
- { .modelname = "fujitsu", .config = ALC262_FUJITSU },
- { .pci_subvendor = 0x10cf, .pci_subdevice = 0x1397,
- .config = ALC262_FUJITSU },
- { .modelname = "hp-bpc", .config = ALC262_HP_BPC },
- { .pci_subvendor = 0x103c, .pci_subdevice = 0x280c,
- .config = ALC262_HP_BPC }, /* xw4400 */
- { .pci_subvendor = 0x103c, .pci_subdevice = 0x2801,
- .config = ALC262_HP_BPC }, /* q965 */
- { .pci_subvendor = 0x103c, .pci_subdevice = 0x3014,
- .config = ALC262_HP_BPC }, /* xw6400 */
- { .pci_subvendor = 0x103c, .pci_subdevice = 0x3015,
- .config = ALC262_HP_BPC }, /* xw8400 */
- { .pci_subvendor = 0x103c, .pci_subdevice = 0x12fe,
- .config = ALC262_HP_BPC }, /* xw9400 */
- { .modelname = "benq", .config = ALC262_BENQ_ED8 },
- { .pci_subvendor = 0x17ff, .pci_subdevice = 0x0560,
- .config = ALC262_BENQ_ED8 },
- { .modelname = "auto", .config = ALC262_AUTO },
+static const char *alc262_models[ALC262_MODEL_LAST] = {
+ [ALC262_BASIC] = "basic",
+ [ALC262_HIPPO] = "hippo",
+ [ALC262_HIPPO_1] = "hippo_1",
+ [ALC262_FUJITSU] = "fujitsu",
+ [ALC262_HP_BPC] = "hp-bpc",
+ [ALC262_HP_BPC_D7000_WL]= "hp-bpc-d7000",
+ [ALC262_BENQ_ED8] = "benq",
+ [ALC262_AUTO] = "auto",
+};
+
+static struct snd_pci_quirk alc262_cfg_tbl[] = {
+ SND_PCI_QUIRK(0x1002, 0x437b, "Hippo", ALC262_HIPPO),
+ SND_PCI_QUIRK(0x103c, 0x12fe, "HP xw9400", ALC262_HP_BPC),
+ SND_PCI_QUIRK(0x103c, 0x280c, "HP xw4400", ALC262_HP_BPC),
+ SND_PCI_QUIRK(0x103c, 0x3014, "HP xw6400", ALC262_HP_BPC),
+ SND_PCI_QUIRK(0x103c, 0x3015, "HP xw8400", ALC262_HP_BPC),
+ SND_PCI_QUIRK(0x103c, 0x2800, "HP D7000", ALC262_HP_BPC_D7000_WL),
+ SND_PCI_QUIRK(0x103c, 0x2802, "HP D7000", ALC262_HP_BPC_D7000_WL),
+ SND_PCI_QUIRK(0x103c, 0x2804, "HP D7000", ALC262_HP_BPC_D7000_WL),
+ SND_PCI_QUIRK(0x103c, 0x2806, "HP D7000", ALC262_HP_BPC_D7000_WL),
+ SND_PCI_QUIRK(0x103c, 0x2801, "HP D7000", ALC262_HP_BPC_D7000_WF),
+ SND_PCI_QUIRK(0x103c, 0x2803, "HP D7000", ALC262_HP_BPC_D7000_WF),
+ SND_PCI_QUIRK(0x103c, 0x2805, "HP D7000", ALC262_HP_BPC_D7000_WF),
+ SND_PCI_QUIRK(0x103c, 0x2807, "HP D7000", ALC262_HP_BPC_D7000_WF),
+ SND_PCI_QUIRK(0x104d, 0x8203, "Sony UX-90", ALC262_HIPPO),
+ SND_PCI_QUIRK(0x10cf, 0x1397, "Fujitsu", ALC262_FUJITSU),
+ SND_PCI_QUIRK(0x17ff, 0x058f, "Benq Hippo", ALC262_HIPPO_1),
+ SND_PCI_QUIRK(0x17ff, 0x0560, "Benq ED8", ALC262_BENQ_ED8),
{}
};
@@ -5900,6 +6715,30 @@ static struct alc_config_preset alc262_presets[] = {
.channel_mode = alc262_modes,
.input_mux = &alc262_capture_source,
},
+ [ALC262_HIPPO] = {
+ .mixers = { alc262_base_mixer },
+ .init_verbs = { alc262_init_verbs, alc262_hippo_unsol_verbs},
+ .num_dacs = ARRAY_SIZE(alc262_dac_nids),
+ .dac_nids = alc262_dac_nids,
+ .hp_nid = 0x03,
+ .dig_out_nid = ALC262_DIGOUT_NID,
+ .num_channel_mode = ARRAY_SIZE(alc262_modes),
+ .channel_mode = alc262_modes,
+ .input_mux = &alc262_capture_source,
+ .unsol_event = alc262_hippo_unsol_event,
+ },
+ [ALC262_HIPPO_1] = {
+ .mixers = { alc262_hippo1_mixer },
+ .init_verbs = { alc262_init_verbs, alc262_hippo1_unsol_verbs},
+ .num_dacs = ARRAY_SIZE(alc262_dac_nids),
+ .dac_nids = alc262_dac_nids,
+ .hp_nid = 0x02,
+ .dig_out_nid = ALC262_DIGOUT_NID,
+ .num_channel_mode = ARRAY_SIZE(alc262_modes),
+ .channel_mode = alc262_modes,
+ .input_mux = &alc262_capture_source,
+ .unsol_event = alc262_hippo1_unsol_event,
+ },
[ALC262_FUJITSU] = {
.mixers = { alc262_fujitsu_mixer },
.init_verbs = { alc262_init_verbs, alc262_fujitsu_unsol_verbs },
@@ -5922,6 +6761,27 @@ static struct alc_config_preset alc262_presets[] = {
.channel_mode = alc262_modes,
.input_mux = &alc262_HP_capture_source,
},
+ [ALC262_HP_BPC_D7000_WF] = {
+ .mixers = { alc262_HP_BPC_WildWest_mixer },
+ .init_verbs = { alc262_HP_BPC_WildWest_init_verbs },
+ .num_dacs = ARRAY_SIZE(alc262_dac_nids),
+ .dac_nids = alc262_dac_nids,
+ .hp_nid = 0x03,
+ .num_channel_mode = ARRAY_SIZE(alc262_modes),
+ .channel_mode = alc262_modes,
+ .input_mux = &alc262_HP_capture_source,
+ },
+ [ALC262_HP_BPC_D7000_WL] = {
+ .mixers = { alc262_HP_BPC_WildWest_mixer,
+ alc262_HP_BPC_WildWest_option_mixer },
+ .init_verbs = { alc262_HP_BPC_WildWest_init_verbs },
+ .num_dacs = ARRAY_SIZE(alc262_dac_nids),
+ .dac_nids = alc262_dac_nids,
+ .hp_nid = 0x03,
+ .num_channel_mode = ARRAY_SIZE(alc262_modes),
+ .channel_mode = alc262_modes,
+ .input_mux = &alc262_HP_capture_source,
+ },
[ALC262_BENQ_ED8] = {
.mixers = { alc262_base_mixer },
.init_verbs = { alc262_init_verbs, alc262_EAPD_verbs },
@@ -5940,7 +6800,7 @@ static int patch_alc262(struct hda_codec *codec)
int board_config;
int err;
- spec = kcalloc(1, sizeof(*spec), GFP_KERNEL);
+ spec = kzalloc(sizeof(*spec), GFP_KERNEL);
if (spec == NULL)
return -ENOMEM;
@@ -5956,9 +6816,11 @@ static int patch_alc262(struct hda_codec *codec)
}
#endif
- board_config = snd_hda_check_board_config(codec, alc262_cfg_tbl);
-
- if (board_config < 0 || board_config >= ALC262_MODEL_LAST) {
+ board_config = snd_hda_check_board_config(codec, ALC262_MODEL_LAST,
+ alc262_models,
+ alc262_cfg_tbl);
+
+ if (board_config < 0) {
printk(KERN_INFO "hda_codec: Unknown model for ALC262, "
"trying auto-probe from BIOS...\n");
board_config = ALC262_AUTO;
@@ -6078,6 +6940,44 @@ static struct hda_channel_mode alc861_uniwill_m31_modes[2] = {
{ 4, alc861_uniwill_m31_ch4_init },
};
+/* Set mic1 and line-in as input and unmute the mixer */
+static struct hda_verb alc861_asus_ch2_init[] = {
+ /* set pin widget 1Ah (line in) for input */
+ { 0x0c, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x20 },
+ /* set pin widget 18h (mic1/2) for input, for mic also enable the vref */
+ { 0x0d, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x24 },
+
+ { 0x15, AC_VERB_SET_AMP_GAIN_MUTE, 0xb00c },
+#if 0
+ { 0x15, AC_VERB_SET_AMP_GAIN_MUTE, (0x7000 | (0x01 << 8)) }, /*mic*/
+ { 0x15, AC_VERB_SET_AMP_GAIN_MUTE, (0x7000 | (0x02 << 8)) }, /*line-in*/
+#endif
+ { } /* end */
+};
+/* Set mic1 nad line-in as output and mute mixer */
+static struct hda_verb alc861_asus_ch6_init[] = {
+ /* set pin widget 1Ah (line in) for output (Back Surround)*/
+ { 0x0c, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x40 },
+ /* { 0x0c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE }, */
+ /* set pin widget 18h (mic1) for output (CLFE)*/
+ { 0x0d, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x40 },
+ /* { 0x0d, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE }, */
+ { 0x0c, AC_VERB_SET_CONNECT_SEL, 0x00 },
+ { 0x0d, AC_VERB_SET_CONNECT_SEL, 0x00 },
+
+ { 0x15, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080 },
+#if 0
+ { 0x15, AC_VERB_SET_AMP_GAIN_MUTE, (0x7080 | (0x01 << 8)) }, /*mic*/
+ { 0x15, AC_VERB_SET_AMP_GAIN_MUTE, (0x7080 | (0x02 << 8)) }, /*line in*/
+#endif
+ { } /* end */
+};
+
+static struct hda_channel_mode alc861_asus_modes[2] = {
+ { 2, alc861_asus_ch2_init },
+ { 6, alc861_asus_ch6_init },
+};
+
/* patch-ALC861 */
static struct snd_kcontrol_new alc861_base_mixer[] = {
@@ -6154,7 +7054,29 @@ static struct snd_kcontrol_new alc861_3ST_mixer[] = {
.private_value = ARRAY_SIZE(alc861_threestack_modes),
},
{ } /* end */
-};
+};
+
+static struct snd_kcontrol_new alc861_toshiba_mixer[] = {
+ /* output mixer control */
+ HDA_CODEC_MUTE("Master Playback Switch", 0x03, 0x0, HDA_OUTPUT),
+ HDA_CODEC_VOLUME("Mic Playback Volume", 0x15, 0x01, HDA_INPUT),
+ HDA_CODEC_MUTE("Mic Playback Switch", 0x15, 0x01, HDA_INPUT),
+
+ /*Capture mixer control */
+ HDA_CODEC_VOLUME("Capture Volume", 0x08, 0x0, HDA_INPUT),
+ HDA_CODEC_MUTE("Capture Switch", 0x08, 0x0, HDA_INPUT),
+ {
+ .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+ .name = "Capture Source",
+ .count = 1,
+ .info = alc_mux_enum_info,
+ .get = alc_mux_enum_get,
+ .put = alc_mux_enum_put,
+ },
+
+ { } /* end */
+};
+
static struct snd_kcontrol_new alc861_uniwill_m31_mixer[] = {
/* output mixer control */
HDA_CODEC_MUTE("Front Playback Switch", 0x03, 0x0, HDA_OUTPUT),
@@ -6196,7 +7118,58 @@ static struct snd_kcontrol_new alc861_uniwill_m31_mixer[] = {
},
{ } /* end */
};
-
+
+static struct snd_kcontrol_new alc861_asus_mixer[] = {
+ /* output mixer control */
+ HDA_CODEC_MUTE("Front Playback Switch", 0x03, 0x0, HDA_OUTPUT),
+ HDA_CODEC_MUTE("Surround Playback Switch", 0x06, 0x0, HDA_OUTPUT),
+ HDA_CODEC_MUTE_MONO("Center Playback Switch", 0x05, 1, 0x0, HDA_OUTPUT),
+ HDA_CODEC_MUTE_MONO("LFE Playback Switch", 0x05, 2, 0x0, HDA_OUTPUT),
+ HDA_CODEC_MUTE("Side Playback Switch", 0x04, 0x0, HDA_OUTPUT),
+
+ /* Input mixer control */
+ HDA_CODEC_VOLUME("Input Playback Volume", 0x15, 0x0, HDA_OUTPUT),
+ HDA_CODEC_MUTE("Input Playback Switch", 0x15, 0x0, HDA_OUTPUT),
+ HDA_CODEC_VOLUME("CD Playback Volume", 0x15, 0x0, HDA_INPUT),
+ HDA_CODEC_MUTE("CD Playback Switch", 0x15, 0x0, HDA_INPUT),
+ HDA_CODEC_VOLUME("Line Playback Volume", 0x15, 0x02, HDA_INPUT),
+ HDA_CODEC_MUTE("Line Playback Switch", 0x15, 0x02, HDA_INPUT),
+ HDA_CODEC_VOLUME("Mic Playback Volume", 0x15, 0x01, HDA_INPUT),
+ HDA_CODEC_MUTE("Mic Playback Switch", 0x15, 0x01, HDA_INPUT),
+ HDA_CODEC_MUTE("Front Mic Playback Switch", 0x10, 0x01, HDA_OUTPUT),
+ HDA_CODEC_MUTE("Headphone Playback Switch", 0x1a, 0x03, HDA_OUTPUT), /* was HDA_INPUT (why?) */
+
+ /* Capture mixer control */
+ HDA_CODEC_VOLUME("Capture Volume", 0x08, 0x0, HDA_INPUT),
+ HDA_CODEC_MUTE("Capture Switch", 0x08, 0x0, HDA_INPUT),
+ {
+ .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+ .name = "Capture Source",
+ .count = 1,
+ .info = alc_mux_enum_info,
+ .get = alc_mux_enum_get,
+ .put = alc_mux_enum_put,
+ },
+ {
+ .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+ .name = "Channel Mode",
+ .info = alc_ch_mode_info,
+ .get = alc_ch_mode_get,
+ .put = alc_ch_mode_put,
+ .private_value = ARRAY_SIZE(alc861_asus_modes),
+ },
+ { }
+};
+
+/* additional mixer */
+static struct snd_kcontrol_new alc861_asus_laptop_mixer[] = {
+ HDA_CODEC_VOLUME("CD Playback Volume", 0x15, 0x0, HDA_INPUT),
+ HDA_CODEC_MUTE("CD Playback Switch", 0x15, 0x0, HDA_INPUT),
+ HDA_CODEC_VOLUME("PC Beep Playback Volume", 0x23, 0x0, HDA_OUTPUT),
+ HDA_CODEC_MUTE("PC Beep Playback Switch", 0x23, 0x0, HDA_OUTPUT),
+ { }
+};
+
/*
* generic initialization of ADC, input mixers and output mixers
*/
@@ -6217,7 +7190,7 @@ static struct hda_verb alc861_base_init_verbs[] = {
/* port-E for HP out (front panel) */
{ 0x0f, AC_VERB_SET_PIN_WIDGET_CONTROL, 0xc0 },
/* route front PCM to HP */
- { 0x0f, AC_VERB_SET_CONNECT_SEL, 0x01 },
+ { 0x0f, AC_VERB_SET_CONNECT_SEL, 0x00 },
/* port-F for mic-in (front panel) with vref */
{ 0x10, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x24 },
/* port-G for CLFE (rear panel) */
@@ -6281,7 +7254,7 @@ static struct hda_verb alc861_threestack_init_verbs[] = {
/* port-E for HP out (front panel) */
{ 0x0f, AC_VERB_SET_PIN_WIDGET_CONTROL, 0xc0 },
/* route front PCM to HP */
- { 0x0f, AC_VERB_SET_CONNECT_SEL, 0x01 },
+ { 0x0f, AC_VERB_SET_CONNECT_SEL, 0x00 },
/* port-F for mic-in (front panel) with vref */
{ 0x10, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x24 },
/* port-G for CLFE (rear panel) */
@@ -6341,7 +7314,7 @@ static struct hda_verb alc861_uniwill_m31_init_verbs[] = {
/* port-E for HP out (front panel) */
{ 0x0f, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x24 }, // this has to be set to VREF80
/* route front PCM to HP */
- { 0x0f, AC_VERB_SET_CONNECT_SEL, 0x01 },
+ { 0x0f, AC_VERB_SET_CONNECT_SEL, 0x00 },
/* port-F for mic-in (front panel) with vref */
{ 0x10, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x24 },
/* port-G for CLFE (rear panel) */
@@ -6385,6 +7358,74 @@ static struct hda_verb alc861_uniwill_m31_init_verbs[] = {
{ }
};
+static struct hda_verb alc861_asus_init_verbs[] = {
+ /*
+ * Unmute ADC0 and set the default input to mic-in
+ */
+ /* port-A for surround (rear panel) | according to codec#0 this is the HP jack*/
+ { 0x0e, AC_VERB_SET_PIN_WIDGET_CONTROL, 0xc0 }, /* was 0x00 */
+ /* route front PCM to HP */
+ { 0x0e, AC_VERB_SET_CONNECT_SEL, 0x01 },
+ /* port-B for mic-in (rear panel) with vref */
+ { 0x0d, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x24 },
+ /* port-C for line-in (rear panel) */
+ { 0x0c, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x20 },
+ /* port-D for Front */
+ { 0x0b, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x40 },
+ { 0x0b, AC_VERB_SET_CONNECT_SEL, 0x00 },
+ /* port-E for HP out (front panel) */
+ { 0x0f, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x24 }, /* this has to be set to VREF80 */
+ /* route front PCM to HP */
+ { 0x0f, AC_VERB_SET_CONNECT_SEL, 0x00 },
+ /* port-F for mic-in (front panel) with vref */
+ { 0x10, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x24 },
+ /* port-G for CLFE (rear panel) */
+ { 0x1f, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x40 },
+ /* port-H for side (rear panel) */
+ { 0x20, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x40 },
+ /* CD-in */
+ { 0x11, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x20 },
+ /* route front mic to ADC1*/
+ {0x08, AC_VERB_SET_CONNECT_SEL, 0x00},
+ {0x08, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
+ /* Unmute DAC0~3 & spdif out*/
+ {0x03, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
+ {0x04, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
+ {0x05, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
+ {0x06, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
+ {0x07, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
+ /* Unmute Mixer 14 (mic) 1c (Line in)*/
+ {0x014, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
+ {0x014, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
+ {0x01c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
+ {0x01c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
+
+ /* Unmute Stereo Mixer 15 */
+ {0x15, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
+ {0x15, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
+ {0x15, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(2)},
+ {0x15, AC_VERB_SET_AMP_GAIN_MUTE, 0xb00c }, /* Output 0~12 step */
+
+ {0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
+ {0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
+ {0x17, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
+ {0x17, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
+ {0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
+ {0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
+ {0x19, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
+ {0x19, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
+ {0x1a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(3)}, /* hp used DAC 3 (Front) */
+ {0x1a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(2)},
+ { }
+};
+
+/* additional init verbs for ASUS laptops */
+static struct hda_verb alc861_asus_laptop_init_verbs[] = {
+ { 0x0f, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x45 }, /* HP-out */
+ { 0x15, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(2) }, /* mute line-in */
+ { }
+};
+
/*
* generic initialization of ADC, input mixers and output mixers
*/
@@ -6437,6 +7478,39 @@ static struct hda_verb alc861_auto_init_verbs[] = {
{ }
};
+static struct hda_verb alc861_toshiba_init_verbs[] = {
+ {0x0f, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | ALC880_HP_EVENT},
+
+ { }
+};
+
+/* toggle speaker-output according to the hp-jack state */
+static void alc861_toshiba_automute(struct hda_codec *codec)
+{
+ unsigned int present;
+
+ present = snd_hda_codec_read(codec, 0x0f, 0,
+ AC_VERB_GET_PIN_SENSE, 0) & 0x80000000;
+ snd_hda_codec_amp_update(codec, 0x16, 0, HDA_INPUT, 0,
+ 0x80, present ? 0x80 : 0);
+ snd_hda_codec_amp_update(codec, 0x16, 1, HDA_INPUT, 0,
+ 0x80, present ? 0x80 : 0);
+ snd_hda_codec_amp_update(codec, 0x1a, 0, HDA_INPUT, 3,
+ 0x80, present ? 0 : 0x80);
+ snd_hda_codec_amp_update(codec, 0x1a, 1, HDA_INPUT, 3,
+ 0x80, present ? 0 : 0x80);
+}
+
+static void alc861_toshiba_unsol_event(struct hda_codec *codec,
+ unsigned int res)
+{
+ /* Looks like the unsol event is incompatible with the standard
+ * definition. 6bit tag is placed at 26 bit!
+ */
+ if ((res >> 26) == ALC880_HP_EVENT)
+ alc861_toshiba_automute(codec);
+}
+
/* pcm configuration: identiacal with ALC880 */
#define alc861_pcm_analog_playback alc880_pcm_analog_playback
#define alc861_pcm_analog_capture alc880_pcm_analog_capture
@@ -6710,19 +7784,29 @@ static void alc861_auto_init(struct hda_codec *codec)
/*
* configuration and preset
*/
-static struct hda_board_config alc861_cfg_tbl[] = {
- { .modelname = "3stack", .config = ALC861_3ST },
- { .pci_subvendor = 0x8086, .pci_subdevice = 0xd600,
- .config = ALC861_3ST },
- { .modelname = "3stack-660", .config = ALC660_3ST },
- { .pci_subvendor = 0x1043, .pci_subdevice = 0x81e7,
- .config = ALC660_3ST },
- { .modelname = "3stack-dig", .config = ALC861_3ST_DIG },
- { .modelname = "6stack-dig", .config = ALC861_6ST_DIG },
- { .modelname = "uniwill-m31", .config = ALC861_UNIWILL_M31},
- { .pci_subvendor = 0x1584, .pci_subdevice = 0x9072,
- .config = ALC861_UNIWILL_M31 },
- { .modelname = "auto", .config = ALC861_AUTO },
+static const char *alc861_models[ALC861_MODEL_LAST] = {
+ [ALC861_3ST] = "3stack",
+ [ALC660_3ST] = "3stack-660",
+ [ALC861_3ST_DIG] = "3stack-dig",
+ [ALC861_6ST_DIG] = "6stack-dig",
+ [ALC861_UNIWILL_M31] = "uniwill-m31",
+ [ALC861_TOSHIBA] = "toshiba",
+ [ALC861_ASUS] = "asus",
+ [ALC861_ASUS_LAPTOP] = "asus-laptop",
+ [ALC861_AUTO] = "auto",
+};
+
+static struct snd_pci_quirk alc861_cfg_tbl[] = {
+ SND_PCI_QUIRK(0x1043, 0x1205, "ASUS W7J", ALC861_3ST),
+ SND_PCI_QUIRK(0x1043, 0x1335, "ASUS F2/3", ALC861_ASUS_LAPTOP),
+ SND_PCI_QUIRK(0x1043, 0x1338, "ASUS F2/3", ALC861_ASUS_LAPTOP),
+ SND_PCI_QUIRK(0x1043, 0x1393, "ASUS", ALC861_ASUS),
+ SND_PCI_QUIRK(0x1043, 0x81e7, "ASUS", ALC660_3ST),
+ SND_PCI_QUIRK(0x1179, 0xff00, "Toshiba", ALC861_TOSHIBA),
+ SND_PCI_QUIRK(0x1179, 0xff10, "Toshiba", ALC861_TOSHIBA),
+ SND_PCI_QUIRK(0x1584, 0x9072, "Uniwill m31", ALC861_UNIWILL_M31),
+ SND_PCI_QUIRK(0x1584, 0x2b01, "Uniwill X40AIx", ALC861_UNIWILL_M31),
+ SND_PCI_QUIRK(0x8086, 0xd600, "Intel", ALC861_3ST),
{}
};
@@ -6789,8 +7873,48 @@ static struct alc_config_preset alc861_presets[] = {
.adc_nids = alc861_adc_nids,
.input_mux = &alc861_capture_source,
},
-
-};
+ [ALC861_TOSHIBA] = {
+ .mixers = { alc861_toshiba_mixer },
+ .init_verbs = { alc861_base_init_verbs, alc861_toshiba_init_verbs },
+ .num_dacs = ARRAY_SIZE(alc861_dac_nids),
+ .dac_nids = alc861_dac_nids,
+ .num_channel_mode = ARRAY_SIZE(alc883_3ST_2ch_modes),
+ .channel_mode = alc883_3ST_2ch_modes,
+ .num_adc_nids = ARRAY_SIZE(alc861_adc_nids),
+ .adc_nids = alc861_adc_nids,
+ .input_mux = &alc861_capture_source,
+ .unsol_event = alc861_toshiba_unsol_event,
+ .init_hook = alc861_toshiba_automute,
+ },
+ [ALC861_ASUS] = {
+ .mixers = { alc861_asus_mixer },
+ .init_verbs = { alc861_asus_init_verbs },
+ .num_dacs = ARRAY_SIZE(alc861_dac_nids),
+ .dac_nids = alc861_dac_nids,
+ .dig_out_nid = ALC861_DIGOUT_NID,
+ .num_channel_mode = ARRAY_SIZE(alc861_asus_modes),
+ .channel_mode = alc861_asus_modes,
+ .need_dac_fix = 1,
+ .hp_nid = 0x06,
+ .num_adc_nids = ARRAY_SIZE(alc861_adc_nids),
+ .adc_nids = alc861_adc_nids,
+ .input_mux = &alc861_capture_source,
+ },
+ [ALC861_ASUS_LAPTOP] = {
+ .mixers = { alc861_toshiba_mixer, alc861_asus_laptop_mixer },
+ .init_verbs = { alc861_asus_init_verbs,
+ alc861_asus_laptop_init_verbs },
+ .num_dacs = ARRAY_SIZE(alc861_dac_nids),
+ .dac_nids = alc861_dac_nids,
+ .dig_out_nid = ALC861_DIGOUT_NID,
+ .num_channel_mode = ARRAY_SIZE(alc883_3ST_2ch_modes),
+ .channel_mode = alc883_3ST_2ch_modes,
+ .need_dac_fix = 1,
+ .num_adc_nids = ARRAY_SIZE(alc861_adc_nids),
+ .adc_nids = alc861_adc_nids,
+ .input_mux = &alc861_capture_source,
+ },
+};
static int patch_alc861(struct hda_codec *codec)
@@ -6799,15 +7923,17 @@ static int patch_alc861(struct hda_codec *codec)
int board_config;
int err;
- spec = kcalloc(1, sizeof(*spec), GFP_KERNEL);
+ spec = kzalloc(sizeof(*spec), GFP_KERNEL);
if (spec == NULL)
return -ENOMEM;
codec->spec = spec;
- board_config = snd_hda_check_board_config(codec, alc861_cfg_tbl);
+ board_config = snd_hda_check_board_config(codec, ALC861_MODEL_LAST,
+ alc861_models,
+ alc861_cfg_tbl);
- if (board_config < 0 || board_config >= ALC861_MODEL_LAST) {
+ if (board_config < 0) {
printk(KERN_INFO "hda_codec: Unknown model for ALC861, "
"trying auto-probe from BIOS...\n");
board_config = ALC861_AUTO;
@@ -6846,19 +7972,706 @@ static int patch_alc861(struct hda_codec *codec)
}
/*
+ * ALC861-VD support
+ *
+ * Based on ALC882
+ *
+ * In addition, an independent DAC
+ */
+#define ALC861VD_DIGOUT_NID 0x06
+
+static hda_nid_t alc861vd_dac_nids[4] = {
+ /* front, surr, clfe, side surr */
+ 0x02, 0x03, 0x04, 0x05
+};
+
+/* dac_nids for ALC660vd are in a different order - according to
+ * Realtek's driver.
+ * This should probably tesult in a different mixer for 6stack models
+ * of ALC660vd codecs, but for now there is only 3stack mixer
+ * - and it is the same as in 861vd.
+ * adc_nids in ALC660vd are (is) the same as in 861vd
+ */
+static hda_nid_t alc660vd_dac_nids[3] = {
+ /* front, rear, clfe, rear_surr */
+ 0x02, 0x04, 0x03
+};
+
+static hda_nid_t alc861vd_adc_nids[1] = {
+ /* ADC0 */
+ 0x09,
+};
+
+/* input MUX */
+/* FIXME: should be a matrix-type input source selection */
+static struct hda_input_mux alc861vd_capture_source = {
+ .num_items = 4,
+ .items = {
+ { "Mic", 0x0 },
+ { "Front Mic", 0x1 },
+ { "Line", 0x2 },
+ { "CD", 0x4 },
+ },
+};
+
+#define alc861vd_mux_enum_info alc_mux_enum_info
+#define alc861vd_mux_enum_get alc_mux_enum_get
+
+static int alc861vd_mux_enum_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
+ struct alc_spec *spec = codec->spec;
+ const struct hda_input_mux *imux = spec->input_mux;
+ unsigned int adc_idx = snd_ctl_get_ioffidx(kcontrol, &ucontrol->id);
+ static hda_nid_t capture_mixers[1] = { 0x22 };
+ hda_nid_t nid = capture_mixers[adc_idx];
+ unsigned int *cur_val = &spec->cur_mux[adc_idx];
+ unsigned int i, idx;
+
+ idx = ucontrol->value.enumerated.item[0];
+ if (idx >= imux->num_items)
+ idx = imux->num_items - 1;
+ if (*cur_val == idx && ! codec->in_resume)
+ return 0;
+ for (i = 0; i < imux->num_items; i++) {
+ unsigned int v = (i == idx) ? 0x7000 : 0x7080;
+ snd_hda_codec_write(codec, nid, 0, AC_VERB_SET_AMP_GAIN_MUTE,
+ v | (imux->items[i].index << 8));
+ }
+ *cur_val = idx;
+ return 1;
+}
+
+/*
+ * 2ch mode
+ */
+static struct hda_channel_mode alc861vd_3stack_2ch_modes[1] = {
+ { 2, NULL }
+};
+
+/*
+ * 6ch mode
+ */
+static struct hda_verb alc861vd_6stack_ch6_init[] = {
+ { 0x17, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x00 },
+ { 0x16, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT },
+ { 0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT },
+ { 0x14, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT },
+ { } /* end */
+};
+
+/*
+ * 8ch mode
+ */
+static struct hda_verb alc861vd_6stack_ch8_init[] = {
+ { 0x17, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT },
+ { 0x16, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT },
+ { 0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT },
+ { 0x14, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT },
+ { } /* end */
+};
+
+static struct hda_channel_mode alc861vd_6stack_modes[2] = {
+ { 6, alc861vd_6stack_ch6_init },
+ { 8, alc861vd_6stack_ch8_init },
+};
+
+static struct snd_kcontrol_new alc861vd_chmode_mixer[] = {
+ {
+ .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+ .name = "Channel Mode",
+ .info = alc_ch_mode_info,
+ .get = alc_ch_mode_get,
+ .put = alc_ch_mode_put,
+ },
+ { } /* end */
+};
+
+static struct snd_kcontrol_new alc861vd_capture_mixer[] = {
+ HDA_CODEC_VOLUME("Capture Volume", 0x09, 0x0, HDA_INPUT),
+ HDA_CODEC_MUTE("Capture Switch", 0x09, 0x0, HDA_INPUT),
+
+ {
+ .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+ /* The multiple "Capture Source" controls confuse alsamixer
+ * So call somewhat different..
+ *FIXME: the controls appear in the "playback" view!
+ */
+ /* .name = "Capture Source", */
+ .name = "Input Source",
+ .count = 1,
+ .info = alc861vd_mux_enum_info,
+ .get = alc861vd_mux_enum_get,
+ .put = alc861vd_mux_enum_put,
+ },
+ { } /* end */
+};
+
+/* Pin assignment: Front=0x14, Rear=0x15, CLFE=0x16, Side=0x17
+ * Mic=0x18, Front Mic=0x19, Line-In=0x1a, HP=0x1b
+ */
+static struct snd_kcontrol_new alc861vd_6st_mixer[] = {
+ HDA_CODEC_VOLUME("Front Playback Volume", 0x02, 0x0, HDA_OUTPUT),
+ HDA_BIND_MUTE("Front Playback Switch", 0x0c, 2, HDA_INPUT),
+
+ HDA_CODEC_VOLUME("Surround Playback Volume", 0x03, 0x0, HDA_OUTPUT),
+ HDA_BIND_MUTE("Surround Playback Switch", 0x0d, 2, HDA_INPUT),
+
+ HDA_CODEC_VOLUME_MONO("Center Playback Volume", 0x04, 1, 0x0,
+ HDA_OUTPUT),
+ HDA_CODEC_VOLUME_MONO("LFE Playback Volume", 0x04, 2, 0x0,
+ HDA_OUTPUT),
+ HDA_BIND_MUTE_MONO("Center Playback Switch", 0x0e, 1, 2, HDA_INPUT),
+ HDA_BIND_MUTE_MONO("LFE Playback Switch", 0x0e, 2, 2, HDA_INPUT),
+
+ HDA_CODEC_VOLUME("Side Playback Volume", 0x05, 0x0, HDA_OUTPUT),
+ HDA_BIND_MUTE("Side Playback Switch", 0x0f, 2, HDA_INPUT),
+
+ HDA_CODEC_MUTE("Headphone Playback Switch", 0x1b, 0x0, HDA_OUTPUT),
+
+ HDA_CODEC_VOLUME("Mic Boost", 0x18, 0, HDA_INPUT),
+ HDA_CODEC_VOLUME("Mic Playback Volume", 0x0b, 0x0, HDA_INPUT),
+ HDA_CODEC_MUTE("Mic Playback Switch", 0x0b, 0x0, HDA_INPUT),
+
+ HDA_CODEC_VOLUME("Front Mic Boost", 0x19, 0, HDA_INPUT),
+ HDA_CODEC_VOLUME("Front Mic Playback Volume", 0x0b, 0x1, HDA_INPUT),
+ HDA_CODEC_MUTE("Front Mic Playback Switch", 0x0b, 0x1, HDA_INPUT),
+
+ HDA_CODEC_VOLUME("Line Playback Volume", 0x0b, 0x02, HDA_INPUT),
+ HDA_CODEC_MUTE("Line Playback Switch", 0x0b, 0x02, HDA_INPUT),
+
+ HDA_CODEC_VOLUME("CD Playback Volume", 0x0b, 0x04, HDA_INPUT),
+ HDA_CODEC_MUTE("CD Playback Switch", 0x0b, 0x04, HDA_INPUT),
+
+ HDA_CODEC_VOLUME("PC Speaker Playback Volume", 0x0b, 0x05, HDA_INPUT),
+ HDA_CODEC_MUTE("PC Speaker Playback Switch", 0x0b, 0x05, HDA_INPUT),
+
+ { } /* end */
+};
+
+static struct snd_kcontrol_new alc861vd_3st_mixer[] = {
+ HDA_CODEC_VOLUME("Front Playback Volume", 0x02, 0x0, HDA_OUTPUT),
+ HDA_BIND_MUTE("Front Playback Switch", 0x0c, 2, HDA_INPUT),
+
+ HDA_CODEC_MUTE("Headphone Playback Switch", 0x1b, 0x0, HDA_OUTPUT),
+
+ HDA_CODEC_VOLUME("Mic Boost", 0x18, 0, HDA_INPUT),
+ HDA_CODEC_VOLUME("Mic Playback Volume", 0x0b, 0x0, HDA_INPUT),
+ HDA_CODEC_MUTE("Mic Playback Switch", 0x0b, 0x0, HDA_INPUT),
+
+ HDA_CODEC_VOLUME("Front Mic Boost", 0x19, 0, HDA_INPUT),
+ HDA_CODEC_VOLUME("Front Mic Playback Volume", 0x0b, 0x1, HDA_INPUT),
+ HDA_CODEC_MUTE("Front Mic Playback Switch", 0x0b, 0x1, HDA_INPUT),
+
+ HDA_CODEC_VOLUME("Line Playback Volume", 0x0b, 0x02, HDA_INPUT),
+ HDA_CODEC_MUTE("Line Playback Switch", 0x0b, 0x02, HDA_INPUT),
+
+ HDA_CODEC_VOLUME("CD Playback Volume", 0x0b, 0x04, HDA_INPUT),
+ HDA_CODEC_MUTE("CD Playback Switch", 0x0b, 0x04, HDA_INPUT),
+
+ HDA_CODEC_VOLUME("PC Speaker Playback Volume", 0x0b, 0x05, HDA_INPUT),
+ HDA_CODEC_MUTE("PC Speaker Playback Switch", 0x0b, 0x05, HDA_INPUT),
+
+ { } /* end */
+};
+
+/*
+ * generic initialization of ADC, input mixers and output mixers
+ */
+static struct hda_verb alc861vd_volume_init_verbs[] = {
+ /*
+ * Unmute ADC0 and set the default input to mic-in
+ */
+ {0x09, AC_VERB_SET_CONNECT_SEL, 0x00},
+ {0x09, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
+
+ /* Unmute input amps (CD, Line In, Mic 1 & Mic 2) of
+ * the analog-loopback mixer widget
+ */
+ /* Amp Indices: Mic1 = 0, Mic2 = 1, Line1 = 2, Line2 = 3, CD = 4 */
+ {0x0b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
+ {0x0b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
+ {0x0b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(2)},
+ {0x0b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(3)},
+ {0x0b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(4)},
+
+ /* Capture mixer: unmute Mic, F-Mic, Line, CD inputs */
+ {0x22, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(4)},
+ {0x22, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(5)},
+ {0x22, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(6)},
+ {0x22, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(8)},
+
+ /*
+ * Set up output mixers (0x02 - 0x05)
+ */
+ /* set vol=0 to output mixers */
+ {0x02, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
+ {0x03, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
+ {0x04, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
+ {0x05, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
+
+ /* set up input amps for analog loopback */
+ /* Amp Indices: DAC = 0, mixer = 1 */
+ {0x0c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
+ {0x0c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
+ {0x0d, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
+ {0x0d, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
+ {0x0e, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
+ {0x0e, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
+ {0x0f, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
+ {0x0f, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
+
+ { }
+};
+
+/*
+ * 3-stack pin configuration:
+ * front = 0x14, mic/clfe = 0x18, HP = 0x19, line/surr = 0x1a, f-mic = 0x1b
+ */
+static struct hda_verb alc861vd_3stack_init_verbs[] = {
+ /*
+ * Set pin mode and muting
+ */
+ /* set front pin widgets 0x14 for output */
+ {0x14, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
+ {0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
+ {0x14, AC_VERB_SET_CONNECT_SEL, 0x00},
+
+ /* Mic (rear) pin: input vref at 80% */
+ {0x18, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80},
+ {0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
+ /* Front Mic pin: input vref at 80% */
+ {0x19, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80},
+ {0x19, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
+ /* Line In pin: input */
+ {0x1a, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN},
+ {0x1a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
+ /* Line-2 In: Headphone output (output 0 - 0x0c) */
+ {0x1b, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP},
+ {0x1b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
+ {0x1b, AC_VERB_SET_CONNECT_SEL, 0x00},
+ /* CD pin widget for input */
+ {0x1c, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN},
+
+ { }
+};
+
+/*
+ * 6-stack pin configuration:
+ */
+static struct hda_verb alc861vd_6stack_init_verbs[] = {
+ /*
+ * Set pin mode and muting
+ */
+ /* set front pin widgets 0x14 for output */
+ {0x14, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
+ {0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
+ {0x14, AC_VERB_SET_CONNECT_SEL, 0x00},
+
+ /* Rear Pin: output 1 (0x0d) */
+ {0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
+ {0x15, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
+ {0x15, AC_VERB_SET_CONNECT_SEL, 0x01},
+ /* CLFE Pin: output 2 (0x0e) */
+ {0x16, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
+ {0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
+ {0x16, AC_VERB_SET_CONNECT_SEL, 0x02},
+ /* Side Pin: output 3 (0x0f) */
+ {0x17, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
+ {0x17, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
+ {0x17, AC_VERB_SET_CONNECT_SEL, 0x03},
+
+ /* Mic (rear) pin: input vref at 80% */
+ {0x18, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80},
+ {0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
+ /* Front Mic pin: input vref at 80% */
+ {0x19, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80},
+ {0x19, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
+ /* Line In pin: input */
+ {0x1a, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN},
+ {0x1a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
+ /* Line-2 In: Headphone output (output 0 - 0x0c) */
+ {0x1b, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP},
+ {0x1b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
+ {0x1b, AC_VERB_SET_CONNECT_SEL, 0x00},
+ /* CD pin widget for input */
+ {0x1c, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN},
+
+ { }
+};
+
+/* pcm configuration: identiacal with ALC880 */
+#define alc861vd_pcm_analog_playback alc880_pcm_analog_playback
+#define alc861vd_pcm_analog_capture alc880_pcm_analog_capture
+#define alc861vd_pcm_digital_playback alc880_pcm_digital_playback
+#define alc861vd_pcm_digital_capture alc880_pcm_digital_capture
+
+/*
+ * configuration and preset
+ */
+static const char *alc861vd_models[ALC861VD_MODEL_LAST] = {
+ [ALC660VD_3ST] = "3stack-660",
+ [ALC861VD_3ST] = "3stack",
+ [ALC861VD_3ST_DIG] = "3stack-digout",
+ [ALC861VD_6ST_DIG] = "6stack-digout",
+ [ALC861VD_AUTO] = "auto",
+};
+
+static struct snd_pci_quirk alc861vd_cfg_tbl[] = {
+ SND_PCI_QUIRK(0x1043, 0x1339, "Asus G1", ALC660VD_3ST),
+ SND_PCI_QUIRK(0x10de, 0x03f0, "Realtek ALC660 demo", ALC660VD_3ST),
+ SND_PCI_QUIRK(0x1019, 0xa88d, "Realtek ALC660 demo", ALC660VD_3ST),
+
+ SND_PCI_QUIRK(0x17aa, 0x3802, "Lenovo 3000 C200", ALC861VD_3ST),
+ {}
+};
+
+static struct alc_config_preset alc861vd_presets[] = {
+ [ALC660VD_3ST] = {
+ .mixers = { alc861vd_3st_mixer },
+ .init_verbs = { alc861vd_volume_init_verbs,
+ alc861vd_3stack_init_verbs },
+ .num_dacs = ARRAY_SIZE(alc660vd_dac_nids),
+ .dac_nids = alc660vd_dac_nids,
+ .num_adc_nids = ARRAY_SIZE(alc861vd_adc_nids),
+ .adc_nids = alc861vd_adc_nids,
+ .num_channel_mode = ARRAY_SIZE(alc861vd_3stack_2ch_modes),
+ .channel_mode = alc861vd_3stack_2ch_modes,
+ .input_mux = &alc861vd_capture_source,
+ },
+ [ALC861VD_3ST] = {
+ .mixers = { alc861vd_3st_mixer },
+ .init_verbs = { alc861vd_volume_init_verbs,
+ alc861vd_3stack_init_verbs },
+ .num_dacs = ARRAY_SIZE(alc861vd_dac_nids),
+ .dac_nids = alc861vd_dac_nids,
+ .num_channel_mode = ARRAY_SIZE(alc861vd_3stack_2ch_modes),
+ .channel_mode = alc861vd_3stack_2ch_modes,
+ .input_mux = &alc861vd_capture_source,
+ },
+ [ALC861VD_3ST_DIG] = {
+ .mixers = { alc861vd_3st_mixer },
+ .init_verbs = { alc861vd_volume_init_verbs,
+ alc861vd_3stack_init_verbs },
+ .num_dacs = ARRAY_SIZE(alc861vd_dac_nids),
+ .dac_nids = alc861vd_dac_nids,
+ .dig_out_nid = ALC861VD_DIGOUT_NID,
+ .num_channel_mode = ARRAY_SIZE(alc861vd_3stack_2ch_modes),
+ .channel_mode = alc861vd_3stack_2ch_modes,
+ .input_mux = &alc861vd_capture_source,
+ },
+ [ALC861VD_6ST_DIG] = {
+ .mixers = { alc861vd_6st_mixer, alc861vd_chmode_mixer },
+ .init_verbs = { alc861vd_volume_init_verbs,
+ alc861vd_6stack_init_verbs },
+ .num_dacs = ARRAY_SIZE(alc861vd_dac_nids),
+ .dac_nids = alc861vd_dac_nids,
+ .dig_out_nid = ALC861VD_DIGOUT_NID,
+ .num_channel_mode = ARRAY_SIZE(alc861vd_6stack_modes),
+ .channel_mode = alc861vd_6stack_modes,
+ .input_mux = &alc861vd_capture_source,
+ },
+};
+
+/*
+ * BIOS auto configuration
+ */
+static void alc861vd_auto_set_output_and_unmute(struct hda_codec *codec,
+ hda_nid_t nid, int pin_type, int dac_idx)
+{
+ /* set as output */
+ snd_hda_codec_write(codec, nid, 0,
+ AC_VERB_SET_PIN_WIDGET_CONTROL, pin_type);
+ snd_hda_codec_write(codec, nid, 0,
+ AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE);
+}
+
+static void alc861vd_auto_init_multi_out(struct hda_codec *codec)
+{
+ struct alc_spec *spec = codec->spec;
+ int i;
+
+ for (i = 0; i <= HDA_SIDE; i++) {
+ hda_nid_t nid = spec->autocfg.line_out_pins[i];
+ if (nid)
+ alc861vd_auto_set_output_and_unmute(codec, nid,
+ PIN_OUT, i);
+ }
+}
+
+
+static void alc861vd_auto_init_hp_out(struct hda_codec *codec)
+{
+ struct alc_spec *spec = codec->spec;
+ hda_nid_t pin;
+
+ pin = spec->autocfg.hp_pins[0];
+ if (pin) /* connect to front and use dac 0 */
+ alc861vd_auto_set_output_and_unmute(codec, pin, PIN_HP, 0);
+}
+
+#define alc861vd_is_input_pin(nid) alc880_is_input_pin(nid)
+#define ALC861VD_PIN_CD_NID ALC880_PIN_CD_NID
+
+static void alc861vd_auto_init_analog_input(struct hda_codec *codec)
+{
+ struct alc_spec *spec = codec->spec;
+ int i;
+
+ for (i = 0; i < AUTO_PIN_LAST; i++) {
+ hda_nid_t nid = spec->autocfg.input_pins[i];
+ if (alc861vd_is_input_pin(nid)) {
+ snd_hda_codec_write(codec, nid, 0,
+ AC_VERB_SET_PIN_WIDGET_CONTROL,
+ i <= AUTO_PIN_FRONT_MIC ?
+ PIN_VREF80 : PIN_IN);
+ if (nid != ALC861VD_PIN_CD_NID)
+ snd_hda_codec_write(codec, nid, 0,
+ AC_VERB_SET_AMP_GAIN_MUTE,
+ AMP_OUT_MUTE);
+ }
+ }
+}
+
+#define alc861vd_idx_to_mixer_vol(nid) ((nid) + 0x02)
+#define alc861vd_idx_to_mixer_switch(nid) ((nid) + 0x0c)
+
+/* add playback controls from the parsed DAC table */
+/* Based on ALC880 version. But ALC861VD has separate,
+ * different NIDs for mute/unmute switch and volume control */
+static int alc861vd_auto_create_multi_out_ctls(struct alc_spec *spec,
+ const struct auto_pin_cfg *cfg)
+{
+ char name[32];
+ static const char *chname[4] = {"Front", "Surround", "CLFE", "Side"};
+ hda_nid_t nid_v, nid_s;
+ int i, err;
+
+ for (i = 0; i < cfg->line_outs; i++) {
+ if (! spec->multiout.dac_nids[i])
+ continue;
+ nid_v = alc861vd_idx_to_mixer_vol(
+ alc880_dac_to_idx(
+ spec->multiout.dac_nids[i]));
+ nid_s = alc861vd_idx_to_mixer_switch(
+ alc880_dac_to_idx(
+ spec->multiout.dac_nids[i]));
+
+ if (i == 2) {
+ /* Center/LFE */
+ if ((err = add_control(spec, ALC_CTL_WIDGET_VOL,
+ "Center Playback Volume",
+ HDA_COMPOSE_AMP_VAL(nid_v, 1,
+ 0, HDA_OUTPUT))) < 0)
+ return err;
+ if ((err = add_control(spec, ALC_CTL_WIDGET_VOL,
+ "LFE Playback Volume",
+ HDA_COMPOSE_AMP_VAL(nid_v, 2,
+ 0, HDA_OUTPUT))) < 0)
+ return err;
+ if ((err = add_control(spec, ALC_CTL_BIND_MUTE,
+ "Center Playback Switch",
+ HDA_COMPOSE_AMP_VAL(nid_s, 1,
+ 2, HDA_INPUT))) < 0)
+ return err;
+ if ((err = add_control(spec, ALC_CTL_BIND_MUTE,
+ "LFE Playback Switch",
+ HDA_COMPOSE_AMP_VAL(nid_s, 2,
+ 2, HDA_INPUT))) < 0)
+ return err;
+ } else {
+ sprintf(name, "%s Playback Volume", chname[i]);
+ if ((err = add_control(spec, ALC_CTL_WIDGET_VOL, name,
+ HDA_COMPOSE_AMP_VAL(nid_v, 3,
+ 0, HDA_OUTPUT))) < 0)
+ return err;
+ sprintf(name, "%s Playback Switch", chname[i]);
+ if ((err = add_control(spec, ALC_CTL_BIND_MUTE, name,
+ HDA_COMPOSE_AMP_VAL(nid_v, 3,
+ 2, HDA_INPUT))) < 0)
+ return err;
+ }
+ }
+ return 0;
+}
+
+/* add playback controls for speaker and HP outputs */
+/* Based on ALC880 version. But ALC861VD has separate,
+ * different NIDs for mute/unmute switch and volume control */
+static int alc861vd_auto_create_extra_out(struct alc_spec *spec,
+ hda_nid_t pin, const char *pfx)
+{
+ hda_nid_t nid_v, nid_s;
+ int err;
+ char name[32];
+
+ if (! pin)
+ return 0;
+
+ if (alc880_is_fixed_pin(pin)) {
+ nid_v = alc880_idx_to_dac(alc880_fixed_pin_idx(pin));
+ /* specify the DAC as the extra output */
+ if (! spec->multiout.hp_nid)
+ spec->multiout.hp_nid = nid_v;
+ else
+ spec->multiout.extra_out_nid[0] = nid_v;
+ /* control HP volume/switch on the output mixer amp */
+ nid_v = alc861vd_idx_to_mixer_vol(
+ alc880_fixed_pin_idx(pin));
+ nid_s = alc861vd_idx_to_mixer_switch(
+ alc880_fixed_pin_idx(pin));
+
+ sprintf(name, "%s Playback Volume", pfx);
+ if ((err = add_control(spec, ALC_CTL_WIDGET_VOL, name,
+ HDA_COMPOSE_AMP_VAL(nid_v, 3, 0,
+ HDA_OUTPUT))) < 0)
+ return err;
+ sprintf(name, "%s Playback Switch", pfx);
+ if ((err = add_control(spec, ALC_CTL_BIND_MUTE, name,
+ HDA_COMPOSE_AMP_VAL(nid_s, 3, 2,
+ HDA_INPUT))) < 0)
+ return err;
+ } else if (alc880_is_multi_pin(pin)) {
+ /* set manual connection */
+ /* we have only a switch on HP-out PIN */
+ sprintf(name, "%s Playback Switch", pfx);
+ if ((err = add_control(spec, ALC_CTL_WIDGET_MUTE, name,
+ HDA_COMPOSE_AMP_VAL(pin, 3, 0,
+ HDA_OUTPUT))) < 0)
+ return err;
+ }
+ return 0;
+}
+
+/* parse the BIOS configuration and set up the alc_spec
+ * return 1 if successful, 0 if the proper config is not found,
+ * or a negative error code
+ * Based on ALC880 version - had to change it to override
+ * alc880_auto_create_extra_out and alc880_auto_create_multi_out_ctls */
+static int alc861vd_parse_auto_config(struct hda_codec *codec)
+{
+ struct alc_spec *spec = codec->spec;
+ int err;
+ static hda_nid_t alc861vd_ignore[] = { 0x1d, 0 };
+
+ if ((err = snd_hda_parse_pin_def_config(codec, &spec->autocfg,
+ alc861vd_ignore)) < 0)
+ return err;
+ if (! spec->autocfg.line_outs)
+ return 0; /* can't find valid BIOS pin config */
+
+ if ((err = alc880_auto_fill_dac_nids(spec, &spec->autocfg)) < 0 ||
+ (err = alc861vd_auto_create_multi_out_ctls(spec,
+ &spec->autocfg)) < 0 ||
+ (err = alc861vd_auto_create_extra_out(spec,
+ spec->autocfg.speaker_pins[0], "Speaker")) < 0 ||
+ (err = alc861vd_auto_create_extra_out(spec,
+ spec->autocfg.hp_pins[0], "Headphone")) < 0 ||
+ (err = alc880_auto_create_analog_input_ctls(spec,
+ &spec->autocfg)) < 0)
+ return err;
+
+ spec->multiout.max_channels = spec->multiout.num_dacs * 2;
+
+ if (spec->autocfg.dig_out_pin)
+ spec->multiout.dig_out_nid = ALC861VD_DIGOUT_NID;
+
+ if (spec->kctl_alloc)
+ spec->mixers[spec->num_mixers++] = spec->kctl_alloc;
+
+ spec->init_verbs[spec->num_init_verbs++]
+ = alc861vd_volume_init_verbs;
+
+ spec->num_mux_defs = 1;
+ spec->input_mux = &spec->private_imux;
+
+ return 1;
+}
+
+/* additional initialization for auto-configuration model */
+static void alc861vd_auto_init(struct hda_codec *codec)
+{
+ alc861vd_auto_init_multi_out(codec);
+ alc861vd_auto_init_hp_out(codec);
+ alc861vd_auto_init_analog_input(codec);
+}
+
+static int patch_alc861vd(struct hda_codec *codec)
+{
+ struct alc_spec *spec;
+ int err, board_config;
+
+ spec = kzalloc(sizeof(*spec), GFP_KERNEL);
+ if (spec == NULL)
+ return -ENOMEM;
+
+ codec->spec = spec;
+
+ board_config = snd_hda_check_board_config(codec, ALC861VD_MODEL_LAST,
+ alc861vd_models,
+ alc861vd_cfg_tbl);
+
+ if (board_config < 0 || board_config >= ALC861VD_MODEL_LAST) {
+ printk(KERN_INFO "hda_codec: Unknown model for ALC660VD/"
+ "ALC861VD, trying auto-probe from BIOS...\n");
+ board_config = ALC861VD_AUTO;
+ }
+
+ if (board_config == ALC861VD_AUTO) {
+ /* automatic parse from the BIOS config */
+ err = alc861vd_parse_auto_config(codec);
+ if (err < 0) {
+ alc_free(codec);
+ return err;
+ } else if (! err) {
+ printk(KERN_INFO
+ "hda_codec: Cannot set up configuration "
+ "from BIOS. Using base mode...\n");
+ board_config = ALC861VD_3ST;
+ }
+ }
+
+ if (board_config != ALC861VD_AUTO)
+ setup_preset(spec, &alc861vd_presets[board_config]);
+
+ spec->stream_name_analog = "ALC861VD Analog";
+ spec->stream_analog_playback = &alc861vd_pcm_analog_playback;
+ spec->stream_analog_capture = &alc861vd_pcm_analog_capture;
+
+ spec->stream_name_digital = "ALC861VD Digital";
+ spec->stream_digital_playback = &alc861vd_pcm_digital_playback;
+ spec->stream_digital_capture = &alc861vd_pcm_digital_capture;
+
+ spec->adc_nids = alc861vd_adc_nids;
+ spec->num_adc_nids = ARRAY_SIZE(alc861vd_adc_nids);
+
+ spec->mixers[spec->num_mixers] = alc861vd_capture_mixer;
+ spec->num_mixers++;
+
+ codec->patch_ops = alc_patch_ops;
+
+ if (board_config == ALC861VD_AUTO)
+ spec->init_hook = alc861vd_auto_init;
+
+ return 0;
+}
+
+/*
* patch entries
*/
struct hda_codec_preset snd_hda_preset_realtek[] = {
{ .id = 0x10ec0260, .name = "ALC260", .patch = patch_alc260 },
{ .id = 0x10ec0262, .name = "ALC262", .patch = patch_alc262 },
- { .id = 0x10ec0880, .name = "ALC880", .patch = patch_alc880 },
+ { .id = 0x10ec0861, .rev = 0x100340, .name = "ALC660",
+ .patch = patch_alc861 },
+ { .id = 0x10ec0660, .name = "ALC660-VD", .patch = patch_alc861vd },
+ { .id = 0x10ec0861, .name = "ALC861", .patch = patch_alc861 },
+ { .id = 0x10ec0862, .name = "ALC861-VD", .patch = patch_alc861vd },
+ { .id = 0x10ec0880, .name = "ALC880", .patch = patch_alc880 },
{ .id = 0x10ec0882, .name = "ALC882", .patch = patch_alc882 },
{ .id = 0x10ec0883, .name = "ALC883", .patch = patch_alc883 },
{ .id = 0x10ec0885, .name = "ALC885", .patch = patch_alc882 },
{ .id = 0x10ec0888, .name = "ALC888", .patch = patch_alc883 },
- { .id = 0x10ec0861, .rev = 0x100300, .name = "ALC861",
- .patch = patch_alc861 },
- { .id = 0x10ec0861, .rev = 0x100340, .name = "ALC660",
- .patch = patch_alc861 },
{} /* terminator */
};
diff --git a/sound/pci/hda/patch_sigmatel.c b/sound/pci/hda/patch_sigmatel.c
index fe51ef3e49d2..6f4a39273b98 100644
--- a/sound/pci/hda/patch_sigmatel.c
+++ b/sound/pci/hda/patch_sigmatel.c
@@ -37,14 +37,37 @@
#define NUM_CONTROL_ALLOC 32
#define STAC_HP_EVENT 0x37
-#define STAC_REF 0
-#define STAC_D945GTP3 1
-#define STAC_D945GTP5 2
-#define STAC_MACMINI 3
-#define STAC_922X_MODELS 4 /* number of 922x models */
-#define STAC_D965_3ST 4
-#define STAC_D965_5ST 5
-#define STAC_927X_MODELS 6 /* number of 922x models */
+enum {
+ STAC_REF,
+ STAC_9200_MODELS
+};
+
+enum {
+ STAC_9205_REF,
+ STAC_9205_MODELS
+};
+
+enum {
+ STAC_925x_REF,
+ STAC_M2_2,
+ STAC_MA6,
+ STAC_925x_MODELS
+};
+
+enum {
+ STAC_D945_REF,
+ STAC_D945GTP3,
+ STAC_D945GTP5,
+ STAC_MACMINI,
+ STAC_922X_MODELS
+};
+
+enum {
+ STAC_D965_REF,
+ STAC_D965_3ST,
+ STAC_D965_5ST,
+ STAC_927X_MODELS
+};
struct sigmatel_spec {
struct snd_kcontrol_new *mixers[4];
@@ -67,6 +90,9 @@ struct sigmatel_spec {
unsigned int num_adcs;
hda_nid_t *mux_nids;
unsigned int num_muxes;
+ hda_nid_t *dmic_nids;
+ unsigned int num_dmics;
+ hda_nid_t dmux_nid;
hda_nid_t dig_in_nid;
/* pin widgets */
@@ -80,6 +106,8 @@ struct sigmatel_spec {
struct snd_kcontrol_new *mixer;
/* capture source */
+ struct hda_input_mux *dinput_mux;
+ unsigned int cur_dmux;
struct hda_input_mux *input_mux;
unsigned int cur_mux[3];
@@ -92,6 +120,7 @@ struct sigmatel_spec {
struct auto_pin_cfg autocfg;
unsigned int num_kctl_alloc, num_kctl_used;
struct snd_kcontrol_new *kctl_alloc;
+ struct hda_input_mux private_dimux;
struct hda_input_mux private_imux;
};
@@ -107,6 +136,18 @@ static hda_nid_t stac9200_dac_nids[1] = {
0x02,
};
+static hda_nid_t stac925x_adc_nids[1] = {
+ 0x03,
+};
+
+static hda_nid_t stac925x_mux_nids[1] = {
+ 0x0f,
+};
+
+static hda_nid_t stac925x_dac_nids[1] = {
+ 0x02,
+};
+
static hda_nid_t stac922x_adc_nids[2] = {
0x06, 0x07,
};
@@ -131,11 +172,20 @@ static hda_nid_t stac9205_mux_nids[2] = {
0x19, 0x1a
};
+static hda_nid_t stac9205_dmic_nids[3] = {
+ 0x17, 0x18, 0
+};
+
static hda_nid_t stac9200_pin_nids[8] = {
0x08, 0x09, 0x0d, 0x0e,
0x0f, 0x10, 0x11, 0x12,
};
+static hda_nid_t stac925x_pin_nids[8] = {
+ 0x07, 0x08, 0x0a, 0x0b,
+ 0x0c, 0x0d, 0x10, 0x11,
+};
+
static hda_nid_t stac922x_pin_nids[10] = {
0x0a, 0x0b, 0x0c, 0x0d, 0x0e,
0x0f, 0x10, 0x11, 0x15, 0x1b,
@@ -154,6 +204,34 @@ static hda_nid_t stac9205_pin_nids[12] = {
};
+static int stac92xx_dmux_enum_info(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_info *uinfo)
+{
+ struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
+ struct sigmatel_spec *spec = codec->spec;
+ return snd_hda_input_mux_info(spec->dinput_mux, uinfo);
+}
+
+static int stac92xx_dmux_enum_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
+ struct sigmatel_spec *spec = codec->spec;
+
+ ucontrol->value.enumerated.item[0] = spec->cur_dmux;
+ return 0;
+}
+
+static int stac92xx_dmux_enum_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
+ struct sigmatel_spec *spec = codec->spec;
+
+ return snd_hda_input_mux_put(codec, spec->dinput_mux, ucontrol,
+ spec->dmux_nid, &spec->cur_dmux);
+}
+
static int stac92xx_mux_enum_info(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *uinfo)
{
struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
@@ -187,6 +265,12 @@ static struct hda_verb stac9200_core_init[] = {
{}
};
+static struct hda_verb stac925x_core_init[] = {
+ /* set dac0mux for dac converter */
+ { 0x06, AC_VERB_SET_CONNECT_SEL, 0x00},
+ {}
+};
+
static struct hda_verb stac922x_core_init[] = {
/* set master volume and direct control */
{ 0x16, AC_VERB_SET_VOLUME_KNOB_CONTROL, 0xff},
@@ -232,6 +316,23 @@ static struct snd_kcontrol_new stac9200_mixer[] = {
{ } /* end */
};
+static struct snd_kcontrol_new stac925x_mixer[] = {
+ HDA_CODEC_VOLUME("Master Playback Volume", 0xe, 0, HDA_OUTPUT),
+ HDA_CODEC_MUTE("Master Playback Switch", 0xe, 0, HDA_OUTPUT),
+ {
+ .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+ .name = "Input Source",
+ .count = 1,
+ .info = stac92xx_mux_enum_info,
+ .get = stac92xx_mux_enum_get,
+ .put = stac92xx_mux_enum_put,
+ },
+ HDA_CODEC_VOLUME("Capture Volume", 0x09, 0, HDA_OUTPUT),
+ HDA_CODEC_MUTE("Capture Switch", 0x09, 0, HDA_OUTPUT),
+ HDA_CODEC_VOLUME("Capture Mux Volume", 0x0f, 0, HDA_OUTPUT),
+ { } /* end */
+};
+
/* This needs to be generated dynamically based on sequence */
static struct snd_kcontrol_new stac922x_mixer[] = {
{
@@ -263,7 +364,7 @@ static struct snd_kcontrol_new stac9227_mixer[] = {
{ } /* end */
};
-static snd_kcontrol_new_t stac927x_mixer[] = {
+static struct snd_kcontrol_new stac927x_mixer[] = {
{
.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
.name = "Input Source",
@@ -278,7 +379,15 @@ static snd_kcontrol_new_t stac927x_mixer[] = {
{ } /* end */
};
-static snd_kcontrol_new_t stac9205_mixer[] = {
+static struct snd_kcontrol_new stac9205_mixer[] = {
+ {
+ .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+ .name = "Digital Input Source",
+ .count = 1,
+ .info = stac92xx_dmux_enum_info,
+ .get = stac92xx_dmux_enum_get,
+ .put = stac92xx_dmux_enum_put,
+ },
{
.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
.name = "Input Source",
@@ -327,22 +436,64 @@ static unsigned int ref9200_pin_configs[8] = {
0x02a19020, 0x01a19021, 0x90100140, 0x01813122,
};
-static unsigned int *stac9200_brd_tbl[] = {
- ref9200_pin_configs,
+static unsigned int *stac9200_brd_tbl[STAC_9200_MODELS] = {
+ [STAC_REF] = ref9200_pin_configs,
+};
+
+static const char *stac9200_models[STAC_9200_MODELS] = {
+ [STAC_REF] = "ref",
};
-static struct hda_board_config stac9200_cfg_tbl[] = {
- { .modelname = "ref",
- .pci_subvendor = PCI_VENDOR_ID_INTEL,
- .pci_subdevice = 0x2668, /* DFI LanParty */
- .config = STAC_REF },
+static struct snd_pci_quirk stac9200_cfg_tbl[] = {
+ /* SigmaTel reference board */
+ SND_PCI_QUIRK(PCI_VENDOR_ID_INTEL, 0x2668,
+ "DFI LanParty", STAC_REF),
/* Dell laptops have BIOS problem */
- { .pci_subvendor = PCI_VENDOR_ID_DELL, .pci_subdevice = 0x01b5,
- .config = STAC_REF }, /* Dell Inspiron 630m */
- { .pci_subvendor = PCI_VENDOR_ID_DELL, .pci_subdevice = 0x01c2,
- .config = STAC_REF }, /* Dell Latitude D620 */
- { .pci_subvendor = PCI_VENDOR_ID_DELL, .pci_subdevice = 0x01cb,
- .config = STAC_REF }, /* Dell Latitude 120L */
+ SND_PCI_QUIRK(PCI_VENDOR_ID_DELL, 0x01b5,
+ "Dell Inspiron 630m", STAC_REF),
+ SND_PCI_QUIRK(PCI_VENDOR_ID_DELL, 0x01c2,
+ "Dell Latitude D620", STAC_REF),
+ SND_PCI_QUIRK(PCI_VENDOR_ID_DELL, 0x01cb,
+ "Dell Latitude 120L", STAC_REF),
+ SND_PCI_QUIRK(PCI_VENDOR_ID_DELL, 0x01cc,
+ "Dell Latitude D820", STAC_REF),
+ {} /* terminator */
+};
+
+static unsigned int ref925x_pin_configs[8] = {
+ 0x40c003f0, 0x424503f2, 0x01813022, 0x02a19021,
+ 0x90a70320, 0x02214210, 0x400003f1, 0x9033032e,
+};
+
+static unsigned int stac925x_MA6_pin_configs[8] = {
+ 0x40c003f0, 0x424503f2, 0x01813022, 0x02a19021,
+ 0x90a70320, 0x90100211, 0x400003f1, 0x9033032e,
+};
+
+static unsigned int stac925xM2_2_pin_configs[8] = {
+ 0x40c003f3, 0x424503f2, 0x041800f4, 0x02a19020,
+ 0x50a103F0, 0x90100210, 0x400003f1, 0x9033032e,
+};
+
+static unsigned int *stac925x_brd_tbl[STAC_925x_MODELS] = {
+ [STAC_REF] = ref925x_pin_configs,
+ [STAC_M2_2] = stac925xM2_2_pin_configs,
+ [STAC_MA6] = stac925x_MA6_pin_configs,
+};
+
+static const char *stac925x_models[STAC_925x_MODELS] = {
+ [STAC_REF] = "ref",
+ [STAC_M2_2] = "m2-2",
+ [STAC_MA6] = "m6",
+};
+
+static struct snd_pci_quirk stac925x_cfg_tbl[] = {
+ /* SigmaTel reference board */
+ SND_PCI_QUIRK(PCI_VENDOR_ID_INTEL, 0x2668, "DFI LanParty", STAC_REF),
+ SND_PCI_QUIRK(0x107b, 0x0316, "Gateway M255", STAC_REF),
+ SND_PCI_QUIRK(0x107b, 0x0366, "Gateway MP6954", STAC_REF),
+ SND_PCI_QUIRK(0x107b, 0x0461, "Gateway NX560XL", STAC_MA6),
+ SND_PCI_QUIRK(0x1002, 0x437b, "Gateway MX6453", STAC_M2_2),
{} /* terminator */
};
@@ -365,100 +516,80 @@ static unsigned int d945gtp5_pin_configs[10] = {
};
static unsigned int *stac922x_brd_tbl[STAC_922X_MODELS] = {
- [STAC_REF] = ref922x_pin_configs,
+ [STAC_D945_REF] = ref922x_pin_configs,
[STAC_D945GTP3] = d945gtp3_pin_configs,
[STAC_D945GTP5] = d945gtp5_pin_configs,
[STAC_MACMINI] = d945gtp5_pin_configs,
};
-static struct hda_board_config stac922x_cfg_tbl[] = {
- { .modelname = "5stack", .config = STAC_D945GTP5 },
- { .modelname = "3stack", .config = STAC_D945GTP3 },
- { .modelname = "ref",
- .pci_subvendor = PCI_VENDOR_ID_INTEL,
- .pci_subdevice = 0x2668, /* DFI LanParty */
- .config = STAC_REF }, /* SigmaTel reference board */
- /* Intel 945G based systems */
- { .pci_subvendor = PCI_VENDOR_ID_INTEL,
- .pci_subdevice = 0x0101,
- .config = STAC_D945GTP3 }, /* Intel D945GTP - 3 Stack */
- { .pci_subvendor = PCI_VENDOR_ID_INTEL,
- .pci_subdevice = 0x0202,
- .config = STAC_D945GTP3 }, /* Intel D945GNT - 3 Stack */
- { .pci_subvendor = PCI_VENDOR_ID_INTEL,
- .pci_subdevice = 0x0606,
- .config = STAC_D945GTP3 }, /* Intel D945GTP - 3 Stack */
- { .pci_subvendor = PCI_VENDOR_ID_INTEL,
- .pci_subdevice = 0x0601,
- .config = STAC_D945GTP3 }, /* Intel D945GTP - 3 Stack */
- { .pci_subvendor = PCI_VENDOR_ID_INTEL,
- .pci_subdevice = 0x0111,
- .config = STAC_D945GTP3 }, /* Intel D945GZP - 3 Stack */
- { .pci_subvendor = PCI_VENDOR_ID_INTEL,
- .pci_subdevice = 0x1115,
- .config = STAC_D945GTP3 }, /* Intel D945GPM - 3 Stack */
- { .pci_subvendor = PCI_VENDOR_ID_INTEL,
- .pci_subdevice = 0x1116,
- .config = STAC_D945GTP3 }, /* Intel D945GBO - 3 Stack */
- { .pci_subvendor = PCI_VENDOR_ID_INTEL,
- .pci_subdevice = 0x1117,
- .config = STAC_D945GTP3 }, /* Intel D945GPM - 3 Stack */
- { .pci_subvendor = PCI_VENDOR_ID_INTEL,
- .pci_subdevice = 0x1118,
- .config = STAC_D945GTP3 }, /* Intel D945GPM - 3 Stack */
- { .pci_subvendor = PCI_VENDOR_ID_INTEL,
- .pci_subdevice = 0x1119,
- .config = STAC_D945GTP3 }, /* Intel D945GPM - 3 Stack */
- { .pci_subvendor = PCI_VENDOR_ID_INTEL,
- .pci_subdevice = 0x8826,
- .config = STAC_D945GTP3 }, /* Intel D945GPM - 3 Stack */
- { .pci_subvendor = PCI_VENDOR_ID_INTEL,
- .pci_subdevice = 0x5049,
- .config = STAC_D945GTP3 }, /* Intel D945GCZ - 3 Stack */
- { .pci_subvendor = PCI_VENDOR_ID_INTEL,
- .pci_subdevice = 0x5055,
- .config = STAC_D945GTP3 }, /* Intel D945GCZ - 3 Stack */
- { .pci_subvendor = PCI_VENDOR_ID_INTEL,
- .pci_subdevice = 0x5048,
- .config = STAC_D945GTP3 }, /* Intel D945GPB - 3 Stack */
- { .pci_subvendor = PCI_VENDOR_ID_INTEL,
- .pci_subdevice = 0x0110,
- .config = STAC_D945GTP3 }, /* Intel D945GLR - 3 Stack */
- { .pci_subvendor = PCI_VENDOR_ID_INTEL,
- .pci_subdevice = 0x0404,
- .config = STAC_D945GTP5 }, /* Intel D945GTP - 5 Stack */
- { .pci_subvendor = PCI_VENDOR_ID_INTEL,
- .pci_subdevice = 0x0303,
- .config = STAC_D945GTP5 }, /* Intel D945GNT - 5 Stack */
- { .pci_subvendor = PCI_VENDOR_ID_INTEL,
- .pci_subdevice = 0x0013,
- .config = STAC_D945GTP5 }, /* Intel D955XBK - 5 Stack */
- { .pci_subvendor = PCI_VENDOR_ID_INTEL,
- .pci_subdevice = 0x0417,
- .config = STAC_D945GTP5 }, /* Intel D975XBK - 5 Stack */
- /* Intel 945P based systems */
- { .pci_subvendor = PCI_VENDOR_ID_INTEL,
- .pci_subdevice = 0x0b0b,
- .config = STAC_D945GTP3 }, /* Intel D945PSN - 3 Stack */
- { .pci_subvendor = PCI_VENDOR_ID_INTEL,
- .pci_subdevice = 0x0112,
- .config = STAC_D945GTP3 }, /* Intel D945PLN - 3 Stack */
- { .pci_subvendor = PCI_VENDOR_ID_INTEL,
- .pci_subdevice = 0x0d0d,
- .config = STAC_D945GTP3 }, /* Intel D945PLM - 3 Stack */
- { .pci_subvendor = PCI_VENDOR_ID_INTEL,
- .pci_subdevice = 0x0909,
- .config = STAC_D945GTP3 }, /* Intel D945PAW - 3 Stack */
- { .pci_subvendor = PCI_VENDOR_ID_INTEL,
- .pci_subdevice = 0x0505,
- .config = STAC_D945GTP3 }, /* Intel D945PLM - 3 Stack */
- { .pci_subvendor = PCI_VENDOR_ID_INTEL,
- .pci_subdevice = 0x0707,
- .config = STAC_D945GTP5 }, /* Intel D945PSV - 5 Stack */
- /* other systems */
- { .pci_subvendor = 0x8384,
- .pci_subdevice = 0x7680,
- .config = STAC_MACMINI }, /* Apple Mac Mini (early 2006) */
+static const char *stac922x_models[STAC_922X_MODELS] = {
+ [STAC_D945_REF] = "ref",
+ [STAC_D945GTP5] = "5stack",
+ [STAC_D945GTP3] = "3stack",
+ [STAC_MACMINI] = "macmini",
+};
+
+static struct snd_pci_quirk stac922x_cfg_tbl[] = {
+ /* SigmaTel reference board */
+ SND_PCI_QUIRK(PCI_VENDOR_ID_INTEL, 0x2668,
+ "DFI LanParty", STAC_D945_REF),
+ /* Intel 945G based systems */
+ SND_PCI_QUIRK(PCI_VENDOR_ID_INTEL, 0x0101,
+ "Intel D945G", STAC_D945GTP3),
+ SND_PCI_QUIRK(PCI_VENDOR_ID_INTEL, 0x0202,
+ "Intel D945G", STAC_D945GTP3),
+ SND_PCI_QUIRK(PCI_VENDOR_ID_INTEL, 0x0606,
+ "Intel D945G", STAC_D945GTP3),
+ SND_PCI_QUIRK(PCI_VENDOR_ID_INTEL, 0x0601,
+ "Intel D945G", STAC_D945GTP3),
+ SND_PCI_QUIRK(PCI_VENDOR_ID_INTEL, 0x0111,
+ "Intel D945G", STAC_D945GTP3),
+ SND_PCI_QUIRK(PCI_VENDOR_ID_INTEL, 0x1115,
+ "Intel D945G", STAC_D945GTP3),
+ SND_PCI_QUIRK(PCI_VENDOR_ID_INTEL, 0x1116,
+ "Intel D945G", STAC_D945GTP3),
+ SND_PCI_QUIRK(PCI_VENDOR_ID_INTEL, 0x1117,
+ "Intel D945G", STAC_D945GTP3),
+ SND_PCI_QUIRK(PCI_VENDOR_ID_INTEL, 0x1118,
+ "Intel D945G", STAC_D945GTP3),
+ SND_PCI_QUIRK(PCI_VENDOR_ID_INTEL, 0x1119,
+ "Intel D945G", STAC_D945GTP3),
+ SND_PCI_QUIRK(PCI_VENDOR_ID_INTEL, 0x8826,
+ "Intel D945G", STAC_D945GTP3),
+ SND_PCI_QUIRK(PCI_VENDOR_ID_INTEL, 0x5049,
+ "Intel D945G", STAC_D945GTP3),
+ SND_PCI_QUIRK(PCI_VENDOR_ID_INTEL, 0x5055,
+ "Intel D945G", STAC_D945GTP3),
+ SND_PCI_QUIRK(PCI_VENDOR_ID_INTEL, 0x5048,
+ "Intel D945G", STAC_D945GTP3),
+ SND_PCI_QUIRK(PCI_VENDOR_ID_INTEL, 0x0110,
+ "Intel D945G", STAC_D945GTP3),
+ /* Intel D945G 5-stack systems */
+ SND_PCI_QUIRK(PCI_VENDOR_ID_INTEL, 0x0404,
+ "Intel D945G", STAC_D945GTP5),
+ SND_PCI_QUIRK(PCI_VENDOR_ID_INTEL, 0x0303,
+ "Intel D945G", STAC_D945GTP5),
+ SND_PCI_QUIRK(PCI_VENDOR_ID_INTEL, 0x0013,
+ "Intel D945G", STAC_D945GTP5),
+ SND_PCI_QUIRK(PCI_VENDOR_ID_INTEL, 0x0417,
+ "Intel D945G", STAC_D945GTP5),
+ /* Intel 945P based systems */
+ SND_PCI_QUIRK(PCI_VENDOR_ID_INTEL, 0x0b0b,
+ "Intel D945P", STAC_D945GTP3),
+ SND_PCI_QUIRK(PCI_VENDOR_ID_INTEL, 0x0112,
+ "Intel D945P", STAC_D945GTP3),
+ SND_PCI_QUIRK(PCI_VENDOR_ID_INTEL, 0x0d0d,
+ "Intel D945P", STAC_D945GTP3),
+ SND_PCI_QUIRK(PCI_VENDOR_ID_INTEL, 0x0909,
+ "Intel D945P", STAC_D945GTP3),
+ SND_PCI_QUIRK(PCI_VENDOR_ID_INTEL, 0x0505,
+ "Intel D945P", STAC_D945GTP3),
+ SND_PCI_QUIRK(PCI_VENDOR_ID_INTEL, 0x0707,
+ "Intel D945P", STAC_D945GTP5),
+ /* other systems */
+ /* Apple Mac Mini (early 2006) */
+ SND_PCI_QUIRK(0x8384, 0x7680,
+ "Mac Mini", STAC_MACMINI),
{} /* terminator */
};
@@ -484,120 +615,72 @@ static unsigned int d965_5st_pin_configs[14] = {
};
static unsigned int *stac927x_brd_tbl[STAC_927X_MODELS] = {
- [STAC_REF] = ref927x_pin_configs,
+ [STAC_D965_REF] = ref927x_pin_configs,
[STAC_D965_3ST] = d965_3st_pin_configs,
[STAC_D965_5ST] = d965_5st_pin_configs,
};
-static struct hda_board_config stac927x_cfg_tbl[] = {
- { .modelname = "5stack", .config = STAC_D965_5ST },
- { .modelname = "3stack", .config = STAC_D965_3ST },
- { .modelname = "ref",
- .pci_subvendor = PCI_VENDOR_ID_INTEL,
- .pci_subdevice = 0x2668, /* DFI LanParty */
- .config = STAC_REF }, /* SigmaTel reference board */
+static const char *stac927x_models[STAC_927X_MODELS] = {
+ [STAC_D965_REF] = "ref",
+ [STAC_D965_3ST] = "3stack",
+ [STAC_D965_5ST] = "5stack",
+};
+
+static struct snd_pci_quirk stac927x_cfg_tbl[] = {
+ /* SigmaTel reference board */
+ SND_PCI_QUIRK(PCI_VENDOR_ID_INTEL, 0x2668,
+ "DFI LanParty", STAC_D965_REF),
/* Intel 946 based systems */
- { .pci_subvendor = PCI_VENDOR_ID_INTEL,
- .pci_subdevice = 0x3d01,
- .config = STAC_D965_3ST }, /* D946 configuration */
- { .pci_subvendor = PCI_VENDOR_ID_INTEL,
- .pci_subdevice = 0xa301,
- .config = STAC_D965_3ST }, /* Intel D946GZT - 3 stack */
+ SND_PCI_QUIRK(PCI_VENDOR_ID_INTEL, 0x3d01, "Intel D946", STAC_D965_3ST),
+ SND_PCI_QUIRK(PCI_VENDOR_ID_INTEL, 0xa301, "Intel D946", STAC_D965_3ST),
/* 965 based 3 stack systems */
- { .pci_subvendor = PCI_VENDOR_ID_INTEL,
- .pci_subdevice = 0x2116,
- .config = STAC_D965_3ST }, /* Intel D965 3Stack config */
- { .pci_subvendor = PCI_VENDOR_ID_INTEL,
- .pci_subdevice = 0x2115,
- .config = STAC_D965_3ST }, /* Intel DQ965WC - 3 Stack */
- { .pci_subvendor = PCI_VENDOR_ID_INTEL,
- .pci_subdevice = 0x2114,
- .config = STAC_D965_3ST }, /* Intel D965 3Stack config */
- { .pci_subvendor = PCI_VENDOR_ID_INTEL,
- .pci_subdevice = 0x2113,
- .config = STAC_D965_3ST }, /* Intel D965 3Stack config */
- { .pci_subvendor = PCI_VENDOR_ID_INTEL,
- .pci_subdevice = 0x2112,
- .config = STAC_D965_3ST }, /* Intel DG965MS - 3 Stack */
- { .pci_subvendor = PCI_VENDOR_ID_INTEL,
- .pci_subdevice = 0x2111,
- .config = STAC_D965_3ST }, /* Intel D965 3Stack config */
- { .pci_subvendor = PCI_VENDOR_ID_INTEL,
- .pci_subdevice = 0x2110,
- .config = STAC_D965_3ST }, /* Intel D965 3Stack config */
- { .pci_subvendor = PCI_VENDOR_ID_INTEL,
- .pci_subdevice = 0x2009,
- .config = STAC_D965_3ST }, /* Intel D965 3Stack config */
- { .pci_subvendor = PCI_VENDOR_ID_INTEL,
- .pci_subdevice = 0x2008,
- .config = STAC_D965_3ST }, /* Intel DQ965GF - 3 Stack */
- { .pci_subvendor = PCI_VENDOR_ID_INTEL,
- .pci_subdevice = 0x2007,
- .config = STAC_D965_3ST }, /* Intel D965 3Stack config */
- { .pci_subvendor = PCI_VENDOR_ID_INTEL,
- .pci_subdevice = 0x2006,
- .config = STAC_D965_3ST }, /* Intel D965 3Stack config */
- { .pci_subvendor = PCI_VENDOR_ID_INTEL,
- .pci_subdevice = 0x2005,
- .config = STAC_D965_3ST }, /* Intel D965 3Stack config */
- { .pci_subvendor = PCI_VENDOR_ID_INTEL,
- .pci_subdevice = 0x2004,
- .config = STAC_D965_3ST }, /* Intel D965 3Stack config */
- { .pci_subvendor = PCI_VENDOR_ID_INTEL,
- .pci_subdevice = 0x2003,
- .config = STAC_D965_3ST }, /* Intel D965 3Stack config */
- { .pci_subvendor = PCI_VENDOR_ID_INTEL,
- .pci_subdevice = 0x2002,
- .config = STAC_D965_3ST }, /* Intel D965 3Stack config */
- { .pci_subvendor = PCI_VENDOR_ID_INTEL,
- .pci_subdevice = 0x2001,
- .config = STAC_D965_3ST }, /* Intel DQ965GF - 3 Stack */
+ SND_PCI_QUIRK(PCI_VENDOR_ID_INTEL, 0x2116, "Intel D965", STAC_D965_3ST),
+ SND_PCI_QUIRK(PCI_VENDOR_ID_INTEL, 0x2115, "Intel D965", STAC_D965_3ST),
+ SND_PCI_QUIRK(PCI_VENDOR_ID_INTEL, 0x2114, "Intel D965", STAC_D965_3ST),
+ SND_PCI_QUIRK(PCI_VENDOR_ID_INTEL, 0x2113, "Intel D965", STAC_D965_3ST),
+ SND_PCI_QUIRK(PCI_VENDOR_ID_INTEL, 0x2112, "Intel D965", STAC_D965_3ST),
+ SND_PCI_QUIRK(PCI_VENDOR_ID_INTEL, 0x2111, "Intel D965", STAC_D965_3ST),
+ SND_PCI_QUIRK(PCI_VENDOR_ID_INTEL, 0x2110, "Intel D965", STAC_D965_3ST),
+ SND_PCI_QUIRK(PCI_VENDOR_ID_INTEL, 0x2009, "Intel D965", STAC_D965_3ST),
+ SND_PCI_QUIRK(PCI_VENDOR_ID_INTEL, 0x2008, "Intel D965", STAC_D965_3ST),
+ SND_PCI_QUIRK(PCI_VENDOR_ID_INTEL, 0x2007, "Intel D965", STAC_D965_3ST),
+ SND_PCI_QUIRK(PCI_VENDOR_ID_INTEL, 0x2006, "Intel D965", STAC_D965_3ST),
+ SND_PCI_QUIRK(PCI_VENDOR_ID_INTEL, 0x2005, "Intel D965", STAC_D965_3ST),
+ SND_PCI_QUIRK(PCI_VENDOR_ID_INTEL, 0x2004, "Intel D965", STAC_D965_3ST),
+ SND_PCI_QUIRK(PCI_VENDOR_ID_INTEL, 0x2003, "Intel D965", STAC_D965_3ST),
+ SND_PCI_QUIRK(PCI_VENDOR_ID_INTEL, 0x2002, "Intel D965", STAC_D965_3ST),
+ SND_PCI_QUIRK(PCI_VENDOR_ID_INTEL, 0x2001, "Intel D965", STAC_D965_3ST),
/* 965 based 5 stack systems */
- { .pci_subvendor = PCI_VENDOR_ID_INTEL,
- .pci_subdevice = 0x2301,
- .config = STAC_D965_5ST }, /* Intel DG965 - 5 Stack */
- { .pci_subvendor = PCI_VENDOR_ID_INTEL,
- .pci_subdevice = 0x2302,
- .config = STAC_D965_5ST }, /* Intel DG965 - 5 Stack */
- { .pci_subvendor = PCI_VENDOR_ID_INTEL,
- .pci_subdevice = 0x2303,
- .config = STAC_D965_5ST }, /* Intel DG965 - 5 Stack */
- { .pci_subvendor = PCI_VENDOR_ID_INTEL,
- .pci_subdevice = 0x2304,
- .config = STAC_D965_5ST }, /* Intel DG965 - 5 Stack */
- { .pci_subvendor = PCI_VENDOR_ID_INTEL,
- .pci_subdevice = 0x2305,
- .config = STAC_D965_5ST }, /* Intel DG965 - 5 Stack */
- { .pci_subvendor = PCI_VENDOR_ID_INTEL,
- .pci_subdevice = 0x2501,
- .config = STAC_D965_5ST }, /* Intel DG965MQ - 5 Stack */
- { .pci_subvendor = PCI_VENDOR_ID_INTEL,
- .pci_subdevice = 0x2502,
- .config = STAC_D965_5ST }, /* Intel DG965 - 5 Stack */
- { .pci_subvendor = PCI_VENDOR_ID_INTEL,
- .pci_subdevice = 0x2503,
- .config = STAC_D965_5ST }, /* Intel DG965 - 5 Stack */
- { .pci_subvendor = PCI_VENDOR_ID_INTEL,
- .pci_subdevice = 0x2504,
- .config = STAC_D965_5ST }, /* Intel DQ965GF - 5 Stack */
+ SND_PCI_QUIRK(PCI_VENDOR_ID_INTEL, 0x2301, "Intel D965", STAC_D965_5ST),
+ SND_PCI_QUIRK(PCI_VENDOR_ID_INTEL, 0x2302, "Intel D965", STAC_D965_5ST),
+ SND_PCI_QUIRK(PCI_VENDOR_ID_INTEL, 0x2303, "Intel D965", STAC_D965_5ST),
+ SND_PCI_QUIRK(PCI_VENDOR_ID_INTEL, 0x2304, "Intel D965", STAC_D965_5ST),
+ SND_PCI_QUIRK(PCI_VENDOR_ID_INTEL, 0x2305, "Intel D965", STAC_D965_5ST),
+ SND_PCI_QUIRK(PCI_VENDOR_ID_INTEL, 0x2501, "Intel D965", STAC_D965_5ST),
+ SND_PCI_QUIRK(PCI_VENDOR_ID_INTEL, 0x2502, "Intel D965", STAC_D965_5ST),
+ SND_PCI_QUIRK(PCI_VENDOR_ID_INTEL, 0x2503, "Intel D965", STAC_D965_5ST),
+ SND_PCI_QUIRK(PCI_VENDOR_ID_INTEL, 0x2504, "Intel D965", STAC_D965_5ST),
{} /* terminator */
};
static unsigned int ref9205_pin_configs[12] = {
0x40000100, 0x40000100, 0x01016011, 0x01014010,
- 0x01813122, 0x01a19021, 0x40000100, 0x40000100,
- 0x40000100, 0x40000100, 0x01441030, 0x01c41030
+ 0x01813122, 0x01a19021, 0x40000100, 0x40000100,
+ 0x90a000f0, 0x90a000f0, 0x01441030, 0x01c41030
};
-static unsigned int *stac9205_brd_tbl[] = {
+static unsigned int *stac9205_brd_tbl[STAC_9205_MODELS] = {
ref9205_pin_configs,
};
-static struct hda_board_config stac9205_cfg_tbl[] = {
- { .modelname = "ref",
- .pci_subvendor = PCI_VENDOR_ID_INTEL,
- .pci_subdevice = 0x2668, /* DFI LanParty */
- .config = STAC_REF }, /* SigmaTel reference board */
+static const char *stac9205_models[STAC_9205_MODELS] = {
+ [STAC_9205_REF] = "ref",
+};
+
+static struct snd_pci_quirk stac9205_cfg_tbl[] = {
+ /* SigmaTel reference board */
+ SND_PCI_QUIRK(PCI_VENDOR_ID_INTEL, 0x2668,
+ "DFI LanParty", STAC_9205_REF),
{} /* terminator */
};
@@ -1154,6 +1237,58 @@ static int stac92xx_auto_create_hp_ctls(struct hda_codec *codec,
return 0;
}
+/* labels for dmic mux inputs */
+static const char *stac92xx_dmic_labels[5] = {
+ "Analog Inputs", "Digital Mic 1", "Digital Mic 2",
+ "Digital Mic 3", "Digital Mic 4"
+};
+
+/* create playback/capture controls for input pins on dmic capable codecs */
+static int stac92xx_auto_create_dmic_input_ctls(struct hda_codec *codec,
+ const struct auto_pin_cfg *cfg)
+{
+ struct sigmatel_spec *spec = codec->spec;
+ struct hda_input_mux *dimux = &spec->private_dimux;
+ hda_nid_t con_lst[HDA_MAX_NUM_INPUTS];
+ int i, j;
+
+ dimux->items[dimux->num_items].label = stac92xx_dmic_labels[0];
+ dimux->items[dimux->num_items].index = 0;
+ dimux->num_items++;
+
+ for (i = 0; i < spec->num_dmics; i++) {
+ int index;
+ int num_cons;
+ unsigned int def_conf;
+
+ def_conf = snd_hda_codec_read(codec,
+ spec->dmic_nids[i],
+ 0,
+ AC_VERB_GET_CONFIG_DEFAULT,
+ 0);
+ if (get_defcfg_connect(def_conf) == AC_JACK_PORT_NONE)
+ continue;
+
+ num_cons = snd_hda_get_connections(codec,
+ spec->dmux_nid,
+ con_lst,
+ HDA_MAX_NUM_INPUTS);
+ for (j = 0; j < num_cons; j++)
+ if (con_lst[j] == spec->dmic_nids[i]) {
+ index = j;
+ goto found;
+ }
+ continue;
+found:
+ dimux->items[dimux->num_items].label =
+ stac92xx_dmic_labels[dimux->num_items];
+ dimux->items[dimux->num_items].index = index;
+ dimux->num_items++;
+ }
+
+ return 0;
+}
+
/* create playback/capture controls for input pins */
static int stac92xx_auto_create_analog_input_ctls(struct hda_codec *codec, const struct auto_pin_cfg *cfg)
{
@@ -1238,7 +1373,9 @@ static int stac92xx_parse_auto_config(struct hda_codec *codec, hda_nid_t dig_out
struct sigmatel_spec *spec = codec->spec;
int err;
- if ((err = snd_hda_parse_pin_def_config(codec, &spec->autocfg, NULL)) < 0)
+ if ((err = snd_hda_parse_pin_def_config(codec,
+ &spec->autocfg,
+ spec->dmic_nids)) < 0)
return err;
if (! spec->autocfg.line_outs)
return 0; /* can't find valid pin config */
@@ -1254,6 +1391,11 @@ static int stac92xx_parse_auto_config(struct hda_codec *codec, hda_nid_t dig_out
(err = stac92xx_auto_create_analog_input_ctls(codec, &spec->autocfg)) < 0)
return err;
+ if (spec->num_dmics > 0)
+ if ((err = stac92xx_auto_create_dmic_input_ctls(codec,
+ &spec->autocfg)) < 0)
+ return err;
+
spec->multiout.max_channels = spec->multiout.num_dacs * 2;
if (spec->multiout.max_channels > 2)
spec->surr_switch = 1;
@@ -1267,6 +1409,7 @@ static int stac92xx_parse_auto_config(struct hda_codec *codec, hda_nid_t dig_out
spec->mixers[spec->num_mixers++] = spec->kctl_alloc;
spec->input_mux = &spec->private_imux;
+ spec->dinput_mux = &spec->private_dimux;
return 1;
}
@@ -1366,6 +1509,7 @@ static int stac9200_parse_auto_config(struct hda_codec *codec)
spec->mixers[spec->num_mixers++] = spec->kctl_alloc;
spec->input_mux = &spec->private_imux;
+ spec->dinput_mux = &spec->private_dimux;
return 1;
}
@@ -1448,6 +1592,11 @@ static int stac92xx_init(struct hda_codec *codec)
stac92xx_auto_set_pinctl(codec, nid, pinctl);
}
}
+ if (spec->num_dmics > 0)
+ for (i = 0; i < spec->num_dmics; i++)
+ stac92xx_auto_set_pinctl(codec, spec->dmic_nids[i],
+ AC_PINCTL_IN_EN);
+
if (cfg->dig_out_pin)
stac92xx_auto_set_pinctl(codec, cfg->dig_out_pin,
AC_PINCTL_OUT_EN);
@@ -1598,7 +1747,9 @@ static int patch_stac9200(struct hda_codec *codec)
codec->spec = spec;
spec->num_pins = 8;
spec->pin_nids = stac9200_pin_nids;
- spec->board_config = snd_hda_check_board_config(codec, stac9200_cfg_tbl);
+ spec->board_config = snd_hda_check_board_config(codec, STAC_9200_MODELS,
+ stac9200_models,
+ stac9200_cfg_tbl);
if (spec->board_config < 0) {
snd_printdd(KERN_INFO "hda_codec: Unknown model for STAC9200, using BIOS defaults\n");
err = stac92xx_save_bios_config_regs(codec);
@@ -1618,6 +1769,7 @@ static int patch_stac9200(struct hda_codec *codec)
spec->adc_nids = stac9200_adc_nids;
spec->mux_nids = stac9200_mux_nids;
spec->num_muxes = 1;
+ spec->num_dmics = 0;
spec->init = stac9200_core_init;
spec->mixer = stac9200_mixer;
@@ -1633,6 +1785,56 @@ static int patch_stac9200(struct hda_codec *codec)
return 0;
}
+static int patch_stac925x(struct hda_codec *codec)
+{
+ struct sigmatel_spec *spec;
+ int err;
+
+ spec = kzalloc(sizeof(*spec), GFP_KERNEL);
+ if (spec == NULL)
+ return -ENOMEM;
+
+ codec->spec = spec;
+ spec->num_pins = 8;
+ spec->pin_nids = stac925x_pin_nids;
+ spec->board_config = snd_hda_check_board_config(codec, STAC_925x_MODELS,
+ stac925x_models,
+ stac925x_cfg_tbl);
+ if (spec->board_config < 0) {
+ snd_printdd(KERN_INFO "hda_codec: Unknown model for STAC925x, using BIOS defaults\n");
+ err = stac92xx_save_bios_config_regs(codec);
+ if (err < 0) {
+ stac92xx_free(codec);
+ return err;
+ }
+ spec->pin_configs = spec->bios_pin_configs;
+ } else if (stac925x_brd_tbl[spec->board_config] != NULL){
+ spec->pin_configs = stac925x_brd_tbl[spec->board_config];
+ stac92xx_set_config_regs(codec);
+ }
+
+ spec->multiout.max_channels = 2;
+ spec->multiout.num_dacs = 1;
+ spec->multiout.dac_nids = stac925x_dac_nids;
+ spec->adc_nids = stac925x_adc_nids;
+ spec->mux_nids = stac925x_mux_nids;
+ spec->num_muxes = 1;
+ spec->num_dmics = 0;
+
+ spec->init = stac925x_core_init;
+ spec->mixer = stac925x_mixer;
+
+ err = stac92xx_parse_auto_config(codec, 0x8, 0x7);
+ if (err < 0) {
+ stac92xx_free(codec);
+ return err;
+ }
+
+ codec->patch_ops = stac92xx_patch_ops;
+
+ return 0;
+}
+
static int patch_stac922x(struct hda_codec *codec)
{
struct sigmatel_spec *spec;
@@ -1645,7 +1847,9 @@ static int patch_stac922x(struct hda_codec *codec)
codec->spec = spec;
spec->num_pins = 10;
spec->pin_nids = stac922x_pin_nids;
- spec->board_config = snd_hda_check_board_config(codec, stac922x_cfg_tbl);
+ spec->board_config = snd_hda_check_board_config(codec, STAC_922X_MODELS,
+ stac922x_models,
+ stac922x_cfg_tbl);
if (spec->board_config < 0) {
snd_printdd(KERN_INFO "hda_codec: Unknown model for STAC922x, "
"using BIOS defaults\n");
@@ -1663,6 +1867,7 @@ static int patch_stac922x(struct hda_codec *codec)
spec->adc_nids = stac922x_adc_nids;
spec->mux_nids = stac922x_mux_nids;
spec->num_muxes = 2;
+ spec->num_dmics = 0;
spec->init = stac922x_core_init;
spec->mixer = stac922x_mixer;
@@ -1695,7 +1900,9 @@ static int patch_stac927x(struct hda_codec *codec)
codec->spec = spec;
spec->num_pins = 14;
spec->pin_nids = stac927x_pin_nids;
- spec->board_config = snd_hda_check_board_config(codec, stac927x_cfg_tbl);
+ spec->board_config = snd_hda_check_board_config(codec, STAC_927X_MODELS,
+ stac927x_models,
+ stac927x_cfg_tbl);
if (spec->board_config < 0) {
snd_printdd(KERN_INFO "hda_codec: Unknown model for STAC927x, using BIOS defaults\n");
err = stac92xx_save_bios_config_regs(codec);
@@ -1714,6 +1921,7 @@ static int patch_stac927x(struct hda_codec *codec)
spec->adc_nids = stac927x_adc_nids;
spec->mux_nids = stac927x_mux_nids;
spec->num_muxes = 3;
+ spec->num_dmics = 0;
spec->init = d965_core_init;
spec->mixer = stac9227_mixer;
break;
@@ -1721,6 +1929,7 @@ static int patch_stac927x(struct hda_codec *codec)
spec->adc_nids = stac927x_adc_nids;
spec->mux_nids = stac927x_mux_nids;
spec->num_muxes = 3;
+ spec->num_dmics = 0;
spec->init = d965_core_init;
spec->mixer = stac9227_mixer;
break;
@@ -1728,6 +1937,7 @@ static int patch_stac927x(struct hda_codec *codec)
spec->adc_nids = stac927x_adc_nids;
spec->mux_nids = stac927x_mux_nids;
spec->num_muxes = 3;
+ spec->num_dmics = 0;
spec->init = stac927x_core_init;
spec->mixer = stac927x_mixer;
}
@@ -1757,7 +1967,9 @@ static int patch_stac9205(struct hda_codec *codec)
codec->spec = spec;
spec->num_pins = 14;
spec->pin_nids = stac9205_pin_nids;
- spec->board_config = snd_hda_check_board_config(codec, stac9205_cfg_tbl);
+ spec->board_config = snd_hda_check_board_config(codec, STAC_9205_MODELS,
+ stac9205_models,
+ stac9205_cfg_tbl);
if (spec->board_config < 0) {
snd_printdd(KERN_INFO "hda_codec: Unknown model for STAC9205, using BIOS defaults\n");
err = stac92xx_save_bios_config_regs(codec);
@@ -1773,13 +1985,28 @@ static int patch_stac9205(struct hda_codec *codec)
spec->adc_nids = stac9205_adc_nids;
spec->mux_nids = stac9205_mux_nids;
- spec->num_muxes = 3;
+ spec->num_muxes = 2;
+ spec->dmic_nids = stac9205_dmic_nids;
+ spec->num_dmics = 2;
+ spec->dmux_nid = 0x1d;
spec->init = stac9205_core_init;
spec->mixer = stac9205_mixer;
spec->multiout.dac_nids = spec->dac_nids;
+ /* Configure GPIO0 as EAPD output */
+ snd_hda_codec_write(codec, codec->afg, 0,
+ AC_VERB_SET_GPIO_DIRECTION, 0x00000001);
+ /* Configure GPIO0 as CMOS */
+ snd_hda_codec_write(codec, codec->afg, 0, 0x7e7, 0x00000000);
+ /* Assert GPIO0 high */
+ snd_hda_codec_write(codec, codec->afg, 0,
+ AC_VERB_SET_GPIO_DATA, 0x00000001);
+ /* Enable GPIO0 */
+ snd_hda_codec_write(codec, codec->afg, 0,
+ AC_VERB_SET_GPIO_MASK, 0x00000001);
+
err = stac92xx_parse_auto_config(codec, 0x1f, 0x20);
if (err < 0) {
stac92xx_free(codec);
@@ -1963,18 +2190,19 @@ enum { /* FE and SZ series. id=0x83847661 and subsys=0x104D0700 or 104D1000. */
/* Unknown. id=0x83847661 and subsys=0x104D1200. */
STAC9872K_VAIO,
/* AR Series. id=0x83847664 and subsys=104D1300 */
- CXD9872AKD_VAIO
- };
-
-static struct hda_board_config stac9872_cfg_tbl[] = {
- { .modelname = "vaio", .config = CXD9872RD_VAIO },
- { .modelname = "vaio-ar", .config = CXD9872AKD_VAIO },
- { .pci_subvendor = 0x104d, .pci_subdevice = 0x81e6,
- .config = CXD9872RD_VAIO },
- { .pci_subvendor = 0x104d, .pci_subdevice = 0x81ef,
- .config = CXD9872RD_VAIO },
- { .pci_subvendor = 0x104d, .pci_subdevice = 0x81fd,
- .config = CXD9872AKD_VAIO },
+ CXD9872AKD_VAIO,
+ STAC_9872_MODELS,
+};
+
+static const char *stac9872_models[STAC_9872_MODELS] = {
+ [CXD9872RD_VAIO] = "vaio",
+ [CXD9872AKD_VAIO] = "vaio-ar",
+};
+
+static struct snd_pci_quirk stac9872_cfg_tbl[] = {
+ SND_PCI_QUIRK(0x104d, 0x81e6, "Sony VAIO F/S", CXD9872RD_VAIO),
+ SND_PCI_QUIRK(0x104d, 0x81ef, "Sony VAIO F/S", CXD9872RD_VAIO),
+ SND_PCI_QUIRK(0x104d, 0x81fd, "Sony VAIO AR", CXD9872AKD_VAIO),
{}
};
@@ -1983,7 +2211,9 @@ static int patch_stac9872(struct hda_codec *codec)
struct sigmatel_spec *spec;
int board_config;
- board_config = snd_hda_check_board_config(codec, stac9872_cfg_tbl);
+ board_config = snd_hda_check_board_config(codec, STAC_9872_MODELS,
+ stac9872_models,
+ stac9872_cfg_tbl);
if (board_config < 0)
/* unknown config, let generic-parser do its job... */
return snd_hda_parse_generic_codec(codec);
@@ -2055,6 +2285,12 @@ struct hda_codec_preset snd_hda_preset_sigmatel[] = {
{ .id = 0x83847627, .name = "STAC9271D", .patch = patch_stac927x },
{ .id = 0x83847628, .name = "STAC9274X5NH", .patch = patch_stac927x },
{ .id = 0x83847629, .name = "STAC9274D5NH", .patch = patch_stac927x },
+ { .id = 0x83847632, .name = "STAC9202", .patch = patch_stac925x },
+ { .id = 0x83847633, .name = "STAC9202D", .patch = patch_stac925x },
+ { .id = 0x83847634, .name = "STAC9250", .patch = patch_stac925x },
+ { .id = 0x83847635, .name = "STAC9250D", .patch = patch_stac925x },
+ { .id = 0x83847636, .name = "STAC9251", .patch = patch_stac925x },
+ { .id = 0x83847637, .name = "STAC9250D", .patch = patch_stac925x },
/* The following does not take into account .id=0x83847661 when subsys =
* 104D0C00 which is STAC9225s. Because of this, some SZ Notebooks are
* currently not fully supported.
diff --git a/sound/pci/hda/patch_via.c b/sound/pci/hda/patch_via.c
new file mode 100644
index 000000000000..4c839b031729
--- /dev/null
+++ b/sound/pci/hda/patch_via.c
@@ -0,0 +1,1396 @@
+/*
+ * Universal Interface for Intel High Definition Audio Codec
+ *
+ * HD audio interface patch for VIA VT1708 codec
+ *
+ * Copyright (c) 2006 Lydia Wang <lydiawang@viatech.com>
+ * Takashi Iwai <tiwai@suse.de>
+ *
+ * This driver is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This driver is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+/* * * * * * * * * * * * * * Release History * * * * * * * * * * * * * * * * */
+/* */
+/* 2006-03-03 Lydia Wang Create the basic patch to support VT1708 codec */
+/* 2006-03-14 Lydia Wang Modify hard code for some pin widget nid */
+/* 2006-08-02 Lydia Wang Add support to VT1709 codec */
+/* 2006-09-08 Lydia Wang Fix internal loopback recording source select bug */
+/* */
+/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
+
+
+#include <sound/driver.h>
+#include <linux/init.h>
+#include <linux/delay.h>
+#include <linux/slab.h>
+#include <linux/pci.h>
+#include <sound/core.h>
+#include "hda_codec.h"
+#include "hda_local.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
+#define VT1708_DIGIN_NID 0x16
+
+#define VT1709_HP_DAC_NID 0x28
+#define VT1709_DIGOUT_NID 0x13
+#define VT1709_DIGIN_NID 0x17
+
+#define IS_VT1708_VENDORID(x) ((x) >= 0x11061708 && (x) <= 0x1106170b)
+#define IS_VT1709_10CH_VENDORID(x) ((x) >= 0x1106e710 && (x) <= 0x1106e713)
+#define IS_VT1709_6CH_VENDORID(x) ((x) >= 0x1106e714 && (x) <= 0x1106e717)
+
+
+enum {
+ VIA_CTL_WIDGET_VOL,
+ VIA_CTL_WIDGET_MUTE,
+};
+
+enum {
+ AUTO_SEQ_FRONT,
+ AUTO_SEQ_SURROUND,
+ AUTO_SEQ_CENLFE,
+ AUTO_SEQ_SIDE
+};
+
+static struct snd_kcontrol_new vt1708_control_templates[] = {
+ HDA_CODEC_VOLUME(NULL, 0, 0, 0),
+ HDA_CODEC_MUTE(NULL, 0, 0, 0),
+};
+
+
+struct via_spec {
+ /* codec parameterization */
+ struct snd_kcontrol_new *mixers[3];
+ unsigned int num_mixers;
+
+ struct hda_verb *init_verbs;
+
+ char *stream_name_analog;
+ struct hda_pcm_stream *stream_analog_playback;
+ struct hda_pcm_stream *stream_analog_capture;
+
+ char *stream_name_digital;
+ struct hda_pcm_stream *stream_digital_playback;
+ struct hda_pcm_stream *stream_digital_capture;
+
+ /* playback */
+ struct hda_multi_out multiout;
+
+ /* capture */
+ unsigned int num_adc_nids;
+ hda_nid_t *adc_nids;
+ hda_nid_t dig_in_nid;
+
+ /* capture source */
+ const struct hda_input_mux *input_mux;
+ unsigned int cur_mux[3];
+
+ /* PCM information */
+ struct hda_pcm pcm_rec[2];
+
+ /* 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[4];
+};
+
+static hda_nid_t vt1708_adc_nids[2] = {
+ /* ADC1-2 */
+ 0x15, 0x27
+};
+
+static hda_nid_t vt1709_adc_nids[3] = {
+ /* ADC1-2 */
+ 0x14, 0x15, 0x16
+};
+
+/* add dynamic controls */
+static int via_add_control(struct via_spec *spec, int type, const char *name,
+ unsigned long val)
+{
+ 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];
+ *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;
+}
+
+/* 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)
+{
+ char name[32];
+ int err;
+
+ sprintf(name, "%s Playback Volume", ctlname);
+ err = via_add_control(spec, VIA_CTL_WIDGET_VOL, name,
+ HDA_COMPOSE_AMP_VAL(mix_nid, 3, idx, HDA_INPUT));
+ if (err < 0)
+ return err;
+ sprintf(name, "%s Playback Switch", ctlname);
+ err = via_add_control(spec, VIA_CTL_WIDGET_MUTE, name,
+ HDA_COMPOSE_AMP_VAL(mix_nid, 3, idx, HDA_INPUT));
+ if (err < 0)
+ return err;
+ return 0;
+}
+
+static void via_auto_set_output_and_unmute(struct hda_codec *codec,
+ hda_nid_t nid, int pin_type,
+ int dac_idx)
+{
+ /* set as output */
+ snd_hda_codec_write(codec, nid, 0, AC_VERB_SET_PIN_WIDGET_CONTROL,
+ pin_type);
+ snd_hda_codec_write(codec, nid, 0, AC_VERB_SET_AMP_GAIN_MUTE,
+ AMP_OUT_UNMUTE);
+}
+
+
+static void via_auto_init_multi_out(struct hda_codec *codec)
+{
+ struct via_spec *spec = codec->spec;
+ int i;
+
+ for (i = 0; i <= AUTO_SEQ_SIDE; i++) {
+ hda_nid_t nid = spec->autocfg.line_out_pins[i];
+ if (nid)
+ via_auto_set_output_and_unmute(codec, nid, PIN_OUT, i);
+ }
+}
+
+static void via_auto_init_hp_out(struct hda_codec *codec)
+{
+ struct via_spec *spec = codec->spec;
+ hda_nid_t pin;
+
+ pin = spec->autocfg.hp_pins[0];
+ if (pin) /* connect to front */
+ via_auto_set_output_and_unmute(codec, pin, PIN_HP, 0);
+}
+
+static void via_auto_init_analog_input(struct hda_codec *codec)
+{
+ struct via_spec *spec = codec->spec;
+ int i;
+
+ for (i = 0; i < AUTO_PIN_LAST; i++) {
+ hda_nid_t nid = spec->autocfg.input_pins[i];
+
+ snd_hda_codec_write(codec, nid, 0,
+ AC_VERB_SET_PIN_WIDGET_CONTROL,
+ (i <= AUTO_PIN_FRONT_MIC ?
+ PIN_VREF50 : PIN_IN));
+
+ }
+}
+/*
+ * input MUX handling
+ */
+static int via_mux_enum_info(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_info *uinfo)
+{
+ struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
+ struct via_spec *spec = codec->spec;
+ return snd_hda_input_mux_info(spec->input_mux, uinfo);
+}
+
+static int via_mux_enum_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
+ struct via_spec *spec = codec->spec;
+ unsigned int adc_idx = snd_ctl_get_ioffidx(kcontrol, &ucontrol->id);
+
+ ucontrol->value.enumerated.item[0] = spec->cur_mux[adc_idx];
+ return 0;
+}
+
+static int via_mux_enum_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
+ struct via_spec *spec = codec->spec;
+ unsigned int adc_idx = snd_ctl_get_ioffidx(kcontrol, &ucontrol->id);
+ unsigned int vendor_id = codec->vendor_id;
+
+ /* AIW0 lydia 060801 add for correct sw0 input select */
+ if (IS_VT1708_VENDORID(vendor_id) && (adc_idx == 0))
+ return snd_hda_input_mux_put(codec, spec->input_mux, ucontrol,
+ 0x18, &spec->cur_mux[adc_idx]);
+ else if ((IS_VT1709_10CH_VENDORID(vendor_id) ||
+ IS_VT1709_6CH_VENDORID(vendor_id)) && (adc_idx == 0) )
+ return snd_hda_input_mux_put(codec, spec->input_mux, ucontrol,
+ 0x19, &spec->cur_mux[adc_idx]);
+ else
+ return snd_hda_input_mux_put(codec, spec->input_mux, ucontrol,
+ spec->adc_nids[adc_idx],
+ &spec->cur_mux[adc_idx]);
+}
+
+/* capture mixer elements */
+static struct snd_kcontrol_new vt1708_capture_mixer[] = {
+ HDA_CODEC_VOLUME("Capture Volume", 0x15, 0x0, HDA_INPUT),
+ HDA_CODEC_MUTE("Capture Switch", 0x15, 0x0, HDA_INPUT),
+ HDA_CODEC_VOLUME_IDX("Capture Volume", 1, 0x27, 0x0, HDA_INPUT),
+ HDA_CODEC_MUTE_IDX("Capture Switch", 1, 0x27, 0x0, HDA_INPUT),
+ {
+ .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+ /* The multiple "Capture Source" controls confuse alsamixer
+ * So call somewhat different..
+ * FIXME: the controls appear in the "playback" view!
+ */
+ /* .name = "Capture Source", */
+ .name = "Input Source",
+ .count = 1,
+ .info = via_mux_enum_info,
+ .get = via_mux_enum_get,
+ .put = via_mux_enum_put,
+ },
+ { } /* end */
+};
+/*
+ * generic initialization of ADC, input mixers and output mixers
+ */
+static struct hda_verb vt1708_volume_init_verbs[] = {
+ /*
+ * Unmute ADC0-1 and set the default input to mic-in
+ */
+ {0x15, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
+ {0x27, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
+
+
+ /* Unmute input amps (CD, Line In, Mic 1 & Mic 2) of the analog-loopback
+ * mixer widget
+ */
+ /* Amp Indices: CD = 1, Mic1 = 2, Line = 3, Mic2 = 4 */
+ {0x17, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
+ {0x17, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
+ {0x17, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(2)},
+ {0x17, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(3)},
+ {0x17, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(4)},
+
+ /*
+ * Set up output mixers (0x19 - 0x1b)
+ */
+ /* set vol=0 to output mixers */
+ {0x19, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
+ {0x1a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
+ {0x1b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
+
+ /* Setup default input to PW4 */
+ {0x20, AC_VERB_SET_CONNECT_SEL, 0x1},
+ /* Set mic as default input of sw0 */
+ {0x18, AC_VERB_SET_CONNECT_SEL, 0x2},
+ /* PW9 Output enable */
+ {0x25, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x40},
+};
+
+static int via_playback_pcm_open(struct hda_pcm_stream *hinfo,
+ struct hda_codec *codec,
+ struct snd_pcm_substream *substream)
+{
+ struct via_spec *spec = codec->spec;
+ return snd_hda_multi_out_analog_open(codec, &spec->multiout, substream);
+}
+
+static int via_playback_pcm_prepare(struct hda_pcm_stream *hinfo,
+ struct hda_codec *codec,
+ unsigned int stream_tag,
+ unsigned int format,
+ struct snd_pcm_substream *substream)
+{
+ struct via_spec *spec = codec->spec;
+ return snd_hda_multi_out_analog_prepare(codec, &spec->multiout,
+ stream_tag, format, substream);
+}
+
+static int via_playback_pcm_cleanup(struct hda_pcm_stream *hinfo,
+ struct hda_codec *codec,
+ struct snd_pcm_substream *substream)
+{
+ struct via_spec *spec = codec->spec;
+ return snd_hda_multi_out_analog_cleanup(codec, &spec->multiout);
+}
+
+/*
+ * Digital out
+ */
+static int via_dig_playback_pcm_open(struct hda_pcm_stream *hinfo,
+ struct hda_codec *codec,
+ struct snd_pcm_substream *substream)
+{
+ struct via_spec *spec = codec->spec;
+ return snd_hda_multi_out_dig_open(codec, &spec->multiout);
+}
+
+static int via_dig_playback_pcm_close(struct hda_pcm_stream *hinfo,
+ struct hda_codec *codec,
+ struct snd_pcm_substream *substream)
+{
+ struct via_spec *spec = codec->spec;
+ return snd_hda_multi_out_dig_close(codec, &spec->multiout);
+}
+
+/*
+ * Analog capture
+ */
+static int via_capture_pcm_prepare(struct hda_pcm_stream *hinfo,
+ struct hda_codec *codec,
+ unsigned int stream_tag,
+ unsigned int format,
+ struct snd_pcm_substream *substream)
+{
+ struct via_spec *spec = codec->spec;
+
+ snd_hda_codec_setup_stream(codec, spec->adc_nids[substream->number],
+ stream_tag, 0, format);
+ return 0;
+}
+
+static int via_capture_pcm_cleanup(struct hda_pcm_stream *hinfo,
+ struct hda_codec *codec,
+ struct snd_pcm_substream *substream)
+{
+ struct via_spec *spec = codec->spec;
+ snd_hda_codec_setup_stream(codec, spec->adc_nids[substream->number],
+ 0, 0, 0);
+ return 0;
+}
+
+static struct hda_pcm_stream vt1708_pcm_analog_playback = {
+ .substreams = 1,
+ .channels_min = 2,
+ .channels_max = 8,
+ .nid = 0x10, /* NID to query formats and rates */
+ .ops = {
+ .open = via_playback_pcm_open,
+ .prepare = via_playback_pcm_prepare,
+ .cleanup = via_playback_pcm_cleanup
+ },
+};
+
+static struct hda_pcm_stream vt1708_pcm_analog_capture = {
+ .substreams = 2,
+ .channels_min = 2,
+ .channels_max = 2,
+ .nid = 0x15, /* NID to query formats and rates */
+ .ops = {
+ .prepare = via_capture_pcm_prepare,
+ .cleanup = via_capture_pcm_cleanup
+ },
+};
+
+static struct hda_pcm_stream vt1708_pcm_digital_playback = {
+ .substreams = 1,
+ .channels_min = 2,
+ .channels_max = 2,
+ /* NID is set in via_build_pcms */
+ .ops = {
+ .open = via_dig_playback_pcm_open,
+ .close = via_dig_playback_pcm_close
+ },
+};
+
+static struct hda_pcm_stream vt1708_pcm_digital_capture = {
+ .substreams = 1,
+ .channels_min = 2,
+ .channels_max = 2,
+};
+
+static int via_build_controls(struct hda_codec *codec)
+{
+ struct via_spec *spec = codec->spec;
+ int err;
+ int i;
+
+ for (i = 0; i < spec->num_mixers; i++) {
+ err = snd_hda_add_new_ctls(codec, spec->mixers[i]);
+ if (err < 0)
+ return err;
+ }
+
+ if (spec->multiout.dig_out_nid) {
+ err = snd_hda_create_spdif_out_ctls(codec,
+ spec->multiout.dig_out_nid);
+ if (err < 0)
+ return err;
+ }
+ if (spec->dig_in_nid) {
+ err = snd_hda_create_spdif_in_ctls(codec, spec->dig_in_nid);
+ if (err < 0)
+ return err;
+ }
+ return 0;
+}
+
+static int via_build_pcms(struct hda_codec *codec)
+{
+ struct via_spec *spec = codec->spec;
+ struct hda_pcm *info = spec->pcm_rec;
+
+ codec->num_pcms = 1;
+ codec->pcm_info = info;
+
+ info->name = spec->stream_name_analog;
+ info->stream[SNDRV_PCM_STREAM_PLAYBACK] = *(spec->stream_analog_playback);
+ info->stream[SNDRV_PCM_STREAM_PLAYBACK].nid = spec->multiout.dac_nids[0];
+ info->stream[SNDRV_PCM_STREAM_CAPTURE] = *(spec->stream_analog_capture);
+ info->stream[SNDRV_PCM_STREAM_CAPTURE].nid = spec->adc_nids[0];
+
+ info->stream[SNDRV_PCM_STREAM_PLAYBACK].channels_max =
+ spec->multiout.max_channels;
+
+ if (spec->multiout.dig_out_nid || spec->dig_in_nid) {
+ codec->num_pcms++;
+ info++;
+ info->name = spec->stream_name_digital;
+ if (spec->multiout.dig_out_nid) {
+ info->stream[SNDRV_PCM_STREAM_PLAYBACK] =
+ *(spec->stream_digital_playback);
+ info->stream[SNDRV_PCM_STREAM_PLAYBACK].nid =
+ spec->multiout.dig_out_nid;
+ }
+ if (spec->dig_in_nid) {
+ info->stream[SNDRV_PCM_STREAM_CAPTURE] =
+ *(spec->stream_digital_capture);
+ info->stream[SNDRV_PCM_STREAM_CAPTURE].nid =
+ spec->dig_in_nid;
+ }
+ }
+
+ return 0;
+}
+
+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);
+ }
+
+ kfree(codec->spec);
+}
+
+static int via_init(struct hda_codec *codec)
+{
+ struct via_spec *spec = codec->spec;
+ snd_hda_sequence_write(codec, spec->init_verbs);
+ return 0;
+}
+
+#ifdef CONFIG_PM
+/*
+ * resume
+ */
+static int via_resume(struct hda_codec *codec)
+{
+ struct via_spec *spec = codec->spec;
+ int i;
+
+ via_init(codec);
+ for (i = 0; i < spec->num_mixers; i++)
+ snd_hda_resume_ctls(codec, spec->mixers[i]);
+ if (spec->multiout.dig_out_nid)
+ snd_hda_resume_spdif_out(codec);
+ if (spec->dig_in_nid)
+ snd_hda_resume_spdif_in(codec);
+
+ return 0;
+}
+#endif
+
+/*
+ */
+static struct hda_codec_ops via_patch_ops = {
+ .build_controls = via_build_controls,
+ .build_pcms = via_build_pcms,
+ .init = via_init,
+ .free = via_free,
+#ifdef CONFIG_PM
+ .resume = via_resume,
+#endif
+};
+
+/* fill in the dac_nids table from the parsed pin configuration */
+static int vt1708_auto_fill_dac_nids(struct via_spec *spec,
+ const struct auto_pin_cfg *cfg)
+{
+ int i;
+ hda_nid_t nid;
+
+ spec->multiout.num_dacs = cfg->line_outs;
+
+ spec->multiout.dac_nids = spec->private_dac_nids;
+
+ for(i = 0; i < 4; i++) {
+ nid = cfg->line_out_pins[i];
+ if (nid) {
+ /* config dac list */
+ switch (i) {
+ case AUTO_SEQ_FRONT:
+ spec->multiout.dac_nids[i] = 0x10;
+ break;
+ case AUTO_SEQ_CENLFE:
+ spec->multiout.dac_nids[i] = 0x12;
+ break;
+ case AUTO_SEQ_SURROUND:
+ spec->multiout.dac_nids[i] = 0x13;
+ break;
+ case AUTO_SEQ_SIDE:
+ spec->multiout.dac_nids[i] = 0x11;
+ break;
+ }
+ }
+ }
+
+ return 0;
+}
+
+/* add playback controls from the parsed DAC table */
+static int vt1708_auto_create_multi_out_ctls(struct via_spec *spec,
+ const struct auto_pin_cfg *cfg)
+{
+ char name[32];
+ static const char *chname[4] = { "Front", "Surround", "C/LFE", "Side" };
+ hda_nid_t nid, nid_vol = 0;
+ int i, err;
+
+ for (i = 0; i <= AUTO_SEQ_SIDE; i++) {
+ nid = cfg->line_out_pins[i];
+
+ if (!nid)
+ continue;
+
+ if (i != AUTO_SEQ_FRONT)
+ nid_vol = 0x1b - i + 1;
+
+ if (i == AUTO_SEQ_CENLFE) {
+ /* Center/LFE */
+ err = via_add_control(spec, VIA_CTL_WIDGET_VOL,
+ "Center Playback Volume",
+ HDA_COMPOSE_AMP_VAL(nid_vol, 1, 0, HDA_OUTPUT));
+ if (err < 0)
+ return err;
+ err = via_add_control(spec, VIA_CTL_WIDGET_VOL,
+ "LFE Playback Volume",
+ HDA_COMPOSE_AMP_VAL(nid_vol, 2, 0, HDA_OUTPUT));
+ if (err < 0)
+ return err;
+ err = via_add_control(spec, VIA_CTL_WIDGET_MUTE,
+ "Center Playback Switch",
+ HDA_COMPOSE_AMP_VAL(nid_vol, 1, 0, HDA_OUTPUT));
+ if (err < 0)
+ return err;
+ err = via_add_control(spec, VIA_CTL_WIDGET_MUTE,
+ "LFE Playback Switch",
+ HDA_COMPOSE_AMP_VAL(nid_vol, 2, 0, HDA_OUTPUT));
+ if (err < 0)
+ return err;
+ } else if (i == AUTO_SEQ_FRONT){
+ /* add control to mixer index 0 */
+ err = via_add_control(spec, VIA_CTL_WIDGET_VOL,
+ "Master Front Playback Volume",
+ HDA_COMPOSE_AMP_VAL(0x17, 3, 0, HDA_INPUT));
+ if (err < 0)
+ return err;
+ err = via_add_control(spec, VIA_CTL_WIDGET_MUTE,
+ "Master Front Playback Switch",
+ HDA_COMPOSE_AMP_VAL(0x17, 3, 0, HDA_INPUT));
+ if (err < 0)
+ return err;
+
+ /* add control to PW3 */
+ sprintf(name, "%s Playback Volume", chname[i]);
+ err = via_add_control(spec, VIA_CTL_WIDGET_VOL, name,
+ HDA_COMPOSE_AMP_VAL(nid, 3, 0, HDA_OUTPUT));
+ if (err < 0)
+ return err;
+ sprintf(name, "%s Playback Switch", chname[i]);
+ err = via_add_control(spec, VIA_CTL_WIDGET_MUTE, name,
+ HDA_COMPOSE_AMP_VAL(nid, 3, 0, HDA_OUTPUT));
+ if (err < 0)
+ return err;
+ } else {
+ sprintf(name, "%s Playback Volume", chname[i]);
+ err = via_add_control(spec, VIA_CTL_WIDGET_VOL, name,
+ HDA_COMPOSE_AMP_VAL(nid_vol, 3, 0, HDA_OUTPUT));
+ if (err < 0)
+ return err;
+ sprintf(name, "%s Playback Switch", chname[i]);
+ err = via_add_control(spec, VIA_CTL_WIDGET_MUTE, name,
+ HDA_COMPOSE_AMP_VAL(nid_vol, 3, 0, HDA_OUTPUT));
+ if (err < 0)
+ return err;
+ }
+ }
+
+ return 0;
+}
+
+static int vt1708_auto_create_hp_ctls(struct via_spec *spec, hda_nid_t pin)
+{
+ int err;
+
+ if (!pin)
+ return 0;
+
+ spec->multiout.hp_nid = VT1708_HP_NID; /* AOW3 */
+
+ err = via_add_control(spec, VIA_CTL_WIDGET_VOL,
+ "Headphone Playback Volume",
+ HDA_COMPOSE_AMP_VAL(pin, 3, 0, HDA_OUTPUT));
+ if (err < 0)
+ return err;
+ err = via_add_control(spec, VIA_CTL_WIDGET_MUTE,
+ "Headphone Playback Switch",
+ HDA_COMPOSE_AMP_VAL(pin, 3, 0, HDA_OUTPUT));
+ if (err < 0)
+ return err;
+
+ return 0;
+}
+
+/* create playback/capture controls for input pins */
+static int vt1708_auto_create_analog_input_ctls(struct via_spec *spec,
+ const struct auto_pin_cfg *cfg)
+{
+ static char *labels[] = {
+ "Mic", "Front Mic", "Line", "Front Line", "CD", "Aux", NULL
+ };
+ struct hda_input_mux *imux = &spec->private_imux;
+ int i, err, idx = 0;
+
+ /* for internal loopback recording select */
+ imux->items[imux->num_items].label = "Stereo Mixer";
+ imux->items[imux->num_items].index = idx;
+ imux->num_items++;
+
+ for (i = 0; i < AUTO_PIN_LAST; i++) {
+ if (!cfg->input_pins[i])
+ continue;
+
+ switch (cfg->input_pins[i]) {
+ case 0x1d: /* Mic */
+ idx = 2;
+ break;
+
+ case 0x1e: /* Line In */
+ idx = 3;
+ break;
+
+ case 0x21: /* Front Mic */
+ idx = 4;
+ break;
+
+ case 0x24: /* CD */
+ idx = 1;
+ break;
+ }
+ err = via_new_analog_input(spec, cfg->input_pins[i], labels[i],
+ idx, 0x17);
+ if (err < 0)
+ return err;
+ imux->items[imux->num_items].label = labels[i];
+ imux->items[imux->num_items].index = idx;
+ imux->num_items++;
+ }
+ return 0;
+}
+
+static int vt1708_parse_auto_config(struct hda_codec *codec)
+{
+ struct via_spec *spec = codec->spec;
+ int err;
+
+ err = snd_hda_parse_pin_def_config(codec, &spec->autocfg, NULL);
+ if (err < 0)
+ return err;
+ err = vt1708_auto_fill_dac_nids(spec, &spec->autocfg);
+ if (err < 0)
+ return err;
+ if (!spec->autocfg.line_outs && !spec->autocfg.hp_pins[0])
+ return 0; /* can't find valid BIOS pin config */
+
+ err = vt1708_auto_create_multi_out_ctls(spec, &spec->autocfg);
+ if (err < 0)
+ return err;
+ err = vt1708_auto_create_hp_ctls(spec, spec->autocfg.hp_pins[0]);
+ if (err < 0)
+ return err;
+ err = vt1708_auto_create_analog_input_ctls(spec, &spec->autocfg);
+ if (err < 0)
+ return err;
+
+ spec->multiout.max_channels = spec->multiout.num_dacs * 2;
+
+ if (spec->autocfg.dig_out_pin)
+ spec->multiout.dig_out_nid = VT1708_DIGOUT_NID;
+ 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;
+
+ spec->init_verbs = vt1708_volume_init_verbs;
+
+ spec->input_mux = &spec->private_imux;
+
+ return 1;
+}
+
+/* init callback for auto-configuration model -- overriding the default init */
+static int via_auto_init(struct hda_codec *codec)
+{
+ via_init(codec);
+ via_auto_init_multi_out(codec);
+ via_auto_init_hp_out(codec);
+ via_auto_init_analog_input(codec);
+ return 0;
+}
+
+static int patch_vt1708(struct hda_codec *codec)
+{
+ struct via_spec *spec;
+ int err;
+
+ /* create a codec specific record */
+ spec = kcalloc(1, sizeof(*spec), GFP_KERNEL);
+ if (spec == NULL)
+ return -ENOMEM;
+
+ codec->spec = spec;
+
+ /* automatic parse from the BIOS config */
+ err = vt1708_parse_auto_config(codec);
+ if (err < 0) {
+ via_free(codec);
+ return err;
+ } else if (!err) {
+ printk(KERN_INFO "hda_codec: Cannot set up configuration "
+ "from BIOS. Using genenic mode...\n");
+ }
+
+
+ spec->stream_name_analog = "VT1708 Analog";
+ spec->stream_analog_playback = &vt1708_pcm_analog_playback;
+ spec->stream_analog_capture = &vt1708_pcm_analog_capture;
+
+ spec->stream_name_digital = "VT1708 Digital";
+ spec->stream_digital_playback = &vt1708_pcm_digital_playback;
+ spec->stream_digital_capture = &vt1708_pcm_digital_capture;
+
+
+ if (!spec->adc_nids && spec->input_mux) {
+ spec->adc_nids = vt1708_adc_nids;
+ spec->num_adc_nids = ARRAY_SIZE(vt1708_adc_nids);
+ spec->mixers[spec->num_mixers] = vt1708_capture_mixer;
+ spec->num_mixers++;
+ }
+
+ codec->patch_ops = via_patch_ops;
+
+ codec->patch_ops.init = via_auto_init;
+
+ return 0;
+}
+
+/* capture mixer elements */
+static struct snd_kcontrol_new vt1709_capture_mixer[] = {
+ HDA_CODEC_VOLUME("Capture Volume", 0x14, 0x0, HDA_INPUT),
+ HDA_CODEC_MUTE("Capture Switch", 0x14, 0x0, HDA_INPUT),
+ HDA_CODEC_VOLUME_IDX("Capture Volume", 1, 0x15, 0x0, HDA_INPUT),
+ HDA_CODEC_MUTE_IDX("Capture Switch", 1, 0x15, 0x0, HDA_INPUT),
+ HDA_CODEC_VOLUME_IDX("Capture Volume", 2, 0x16, 0x0, HDA_INPUT),
+ HDA_CODEC_MUTE_IDX("Capture Switch", 2, 0x16, 0x0, HDA_INPUT),
+ {
+ .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+ /* The multiple "Capture Source" controls confuse alsamixer
+ * So call somewhat different..
+ * FIXME: the controls appear in the "playback" view!
+ */
+ /* .name = "Capture Source", */
+ .name = "Input Source",
+ .count = 1,
+ .info = via_mux_enum_info,
+ .get = via_mux_enum_get,
+ .put = via_mux_enum_put,
+ },
+ { } /* end */
+};
+
+/*
+ * generic initialization of ADC, input mixers and output mixers
+ */
+static struct hda_verb vt1709_10ch_volume_init_verbs[] = {
+ /*
+ * Unmute ADC0-2 and set the default input to mic-in
+ */
+ {0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
+ {0x15, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
+ {0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
+
+
+ /* Unmute input amps (CD, Line In, Mic 1 & Mic 2) of the analog-loopback
+ * mixer widget
+ */
+ /* Amp Indices: AOW0=0, CD = 1, Mic1 = 2, Line = 3, Mic2 = 4 */
+ {0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
+ {0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
+ {0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(2)},
+ {0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(3)},
+ {0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(4)},
+
+ /*
+ * Set up output selector (0x1a, 0x1b, 0x29)
+ */
+ /* set vol=0 to output mixers */
+ {0x1a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
+ {0x1b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
+ {0x29, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
+
+ /*
+ * Unmute PW3 and PW4
+ */
+ {0x1f, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
+ {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
+
+ /* Set input of PW4 as AOW4 */
+ {0x20, AC_VERB_SET_CONNECT_SEL, 0x1},
+ /* Set mic as default input of sw0 */
+ {0x19, AC_VERB_SET_CONNECT_SEL, 0x2},
+ /* PW9 Output enable */
+ {0x24, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x40},
+ { }
+};
+
+static struct hda_pcm_stream vt1709_10ch_pcm_analog_playback = {
+ .substreams = 1,
+ .channels_min = 2,
+ .channels_max = 10,
+ .nid = 0x10, /* NID to query formats and rates */
+ .ops = {
+ .open = via_playback_pcm_open,
+ .prepare = via_playback_pcm_prepare,
+ .cleanup = via_playback_pcm_cleanup
+ },
+};
+
+static struct hda_pcm_stream vt1709_6ch_pcm_analog_playback = {
+ .substreams = 1,
+ .channels_min = 2,
+ .channels_max = 6,
+ .nid = 0x10, /* NID to query formats and rates */
+ .ops = {
+ .open = via_playback_pcm_open,
+ .prepare = via_playback_pcm_prepare,
+ .cleanup = via_playback_pcm_cleanup
+ },
+};
+
+static struct hda_pcm_stream vt1709_pcm_analog_capture = {
+ .substreams = 2,
+ .channels_min = 2,
+ .channels_max = 2,
+ .nid = 0x14, /* NID to query formats and rates */
+ .ops = {
+ .prepare = via_capture_pcm_prepare,
+ .cleanup = via_capture_pcm_cleanup
+ },
+};
+
+static struct hda_pcm_stream vt1709_pcm_digital_playback = {
+ .substreams = 1,
+ .channels_min = 2,
+ .channels_max = 2,
+ /* NID is set in via_build_pcms */
+ .ops = {
+ .open = via_dig_playback_pcm_open,
+ .close = via_dig_playback_pcm_close
+ },
+};
+
+static struct hda_pcm_stream vt1709_pcm_digital_capture = {
+ .substreams = 1,
+ .channels_min = 2,
+ .channels_max = 2,
+};
+
+static int vt1709_auto_fill_dac_nids(struct via_spec *spec,
+ const struct auto_pin_cfg *cfg)
+{
+ int i;
+ hda_nid_t nid;
+
+ if (cfg->line_outs == 4) /* 10 channels */
+ spec->multiout.num_dacs = cfg->line_outs+1; /* AOW0~AOW4 */
+ else if (cfg->line_outs == 3) /* 6 channels */
+ spec->multiout.num_dacs = cfg->line_outs; /* AOW0~AOW2 */
+
+ spec->multiout.dac_nids = spec->private_dac_nids;
+
+ if (cfg->line_outs == 4) { /* 10 channels */
+ for (i = 0; i < cfg->line_outs; i++) {
+ nid = cfg->line_out_pins[i];
+ if (nid) {
+ /* config dac list */
+ switch (i) {
+ case AUTO_SEQ_FRONT:
+ /* AOW0 */
+ spec->multiout.dac_nids[i] = 0x10;
+ break;
+ case AUTO_SEQ_CENLFE:
+ /* AOW2 */
+ spec->multiout.dac_nids[i] = 0x12;
+ break;
+ case AUTO_SEQ_SURROUND:
+ /* AOW3 */
+ spec->multiout.dac_nids[i] = 0x27;
+ break;
+ case AUTO_SEQ_SIDE:
+ /* AOW1 */
+ spec->multiout.dac_nids[i] = 0x11;
+ break;
+ default:
+ break;
+ }
+ }
+ }
+ spec->multiout.dac_nids[cfg->line_outs] = 0x28; /* AOW4 */
+
+ } else if (cfg->line_outs == 3) { /* 6 channels */
+ for(i = 0; i < cfg->line_outs; i++) {
+ nid = cfg->line_out_pins[i];
+ if (nid) {
+ /* config dac list */
+ switch(i) {
+ case AUTO_SEQ_FRONT:
+ /* AOW0 */
+ spec->multiout.dac_nids[i] = 0x10;
+ break;
+ case AUTO_SEQ_CENLFE:
+ /* AOW2 */
+ spec->multiout.dac_nids[i] = 0x12;
+ break;
+ case AUTO_SEQ_SURROUND:
+ /* AOW1 */
+ spec->multiout.dac_nids[i] = 0x11;
+ break;
+ default:
+ break;
+ }
+ }
+ }
+ }
+
+ return 0;
+}
+
+/* add playback controls from the parsed DAC table */
+static int vt1709_auto_create_multi_out_ctls(struct via_spec *spec,
+ const struct auto_pin_cfg *cfg)
+{
+ char name[32];
+ static const char *chname[4] = { "Front", "Surround", "C/LFE", "Side" };
+ hda_nid_t nid = 0;
+ int i, err;
+
+ for (i = 0; i <= AUTO_SEQ_SIDE; i++) {
+ nid = cfg->line_out_pins[i];
+
+ if (!nid)
+ continue;
+
+ if (i == AUTO_SEQ_CENLFE) {
+ /* Center/LFE */
+ err = via_add_control(spec, VIA_CTL_WIDGET_VOL,
+ "Center Playback Volume",
+ HDA_COMPOSE_AMP_VAL(0x1b, 1, 0, HDA_OUTPUT));
+ if (err < 0)
+ return err;
+ err = via_add_control(spec, VIA_CTL_WIDGET_VOL,
+ "LFE Playback Volume",
+ HDA_COMPOSE_AMP_VAL(0x1b, 2, 0, HDA_OUTPUT));
+ if (err < 0)
+ return err;
+ err = via_add_control(spec, VIA_CTL_WIDGET_MUTE,
+ "Center Playback Switch",
+ HDA_COMPOSE_AMP_VAL(0x1b, 1, 0, HDA_OUTPUT));
+ if (err < 0)
+ return err;
+ err = via_add_control(spec, VIA_CTL_WIDGET_MUTE,
+ "LFE Playback Switch",
+ HDA_COMPOSE_AMP_VAL(0x1b, 2, 0, HDA_OUTPUT));
+ if (err < 0)
+ return err;
+ } else if (i == AUTO_SEQ_FRONT){
+ /* add control to mixer index 0 */
+ err = via_add_control(spec, VIA_CTL_WIDGET_VOL,
+ "Master Front Playback Volume",
+ HDA_COMPOSE_AMP_VAL(0x18, 3, 0, HDA_INPUT));
+ if (err < 0)
+ return err;
+ err = via_add_control(spec, VIA_CTL_WIDGET_MUTE,
+ "Master Front Playback Switch",
+ HDA_COMPOSE_AMP_VAL(0x18, 3, 0, HDA_INPUT));
+ if (err < 0)
+ return err;
+
+ /* add control to PW3 */
+ sprintf(name, "%s Playback Volume", chname[i]);
+ err = via_add_control(spec, VIA_CTL_WIDGET_VOL, name,
+ HDA_COMPOSE_AMP_VAL(nid, 3, 0, HDA_OUTPUT));
+ if (err < 0)
+ return err;
+ sprintf(name, "%s Playback Switch", chname[i]);
+ err = via_add_control(spec, VIA_CTL_WIDGET_MUTE, name,
+ HDA_COMPOSE_AMP_VAL(nid, 3, 0, HDA_OUTPUT));
+ if (err < 0)
+ return err;
+ } else if (i == AUTO_SEQ_SURROUND) {
+ sprintf(name, "%s Playback Volume", chname[i]);
+ err = via_add_control(spec, VIA_CTL_WIDGET_VOL, name,
+ HDA_COMPOSE_AMP_VAL(0x29, 3, 0, HDA_OUTPUT));
+ if (err < 0)
+ return err;
+ sprintf(name, "%s Playback Switch", chname[i]);
+ err = via_add_control(spec, VIA_CTL_WIDGET_MUTE, name,
+ HDA_COMPOSE_AMP_VAL(0x29, 3, 0, HDA_OUTPUT));
+ if (err < 0)
+ return err;
+ } else if (i == AUTO_SEQ_SIDE) {
+ sprintf(name, "%s Playback Volume", chname[i]);
+ err = via_add_control(spec, VIA_CTL_WIDGET_VOL, name,
+ HDA_COMPOSE_AMP_VAL(0x1a, 3, 0, HDA_OUTPUT));
+ if (err < 0)
+ return err;
+ sprintf(name, "%s Playback Switch", chname[i]);
+ err = via_add_control(spec, VIA_CTL_WIDGET_MUTE, name,
+ HDA_COMPOSE_AMP_VAL(0x1a, 3, 0, HDA_OUTPUT));
+ if (err < 0)
+ return err;
+ }
+ }
+
+ return 0;
+}
+
+static int vt1709_auto_create_hp_ctls(struct via_spec *spec, hda_nid_t pin)
+{
+ int err;
+
+ if (!pin)
+ return 0;
+
+ if (spec->multiout.num_dacs == 5) /* 10 channels */
+ spec->multiout.hp_nid = VT1709_HP_DAC_NID;
+ else if (spec->multiout.num_dacs == 3) /* 6 channels */
+ spec->multiout.hp_nid = 0;
+
+ err = via_add_control(spec, VIA_CTL_WIDGET_VOL,
+ "Headphone Playback Volume",
+ HDA_COMPOSE_AMP_VAL(pin, 3, 0, HDA_OUTPUT));
+ if (err < 0)
+ return err;
+ err = via_add_control(spec, VIA_CTL_WIDGET_MUTE,
+ "Headphone Playback Switch",
+ HDA_COMPOSE_AMP_VAL(pin, 3, 0, HDA_OUTPUT));
+ if (err < 0)
+ return err;
+
+ return 0;
+}
+
+/* create playback/capture controls for input pins */
+static int vt1709_auto_create_analog_input_ctls(struct via_spec *spec,
+ const struct auto_pin_cfg *cfg)
+{
+ static char *labels[] = {
+ "Mic", "Front Mic", "Line", "Front Line", "CD", "Aux", NULL
+ };
+ struct hda_input_mux *imux = &spec->private_imux;
+ int i, err, idx = 0;
+
+ /* for internal loopback recording select */
+ imux->items[imux->num_items].label = "Stereo Mixer";
+ imux->items[imux->num_items].index = idx;
+ imux->num_items++;
+
+ for (i = 0; i < AUTO_PIN_LAST; i++) {
+ if (!cfg->input_pins[i])
+ continue;
+
+ switch (cfg->input_pins[i]) {
+ case 0x1d: /* Mic */
+ idx = 2;
+ break;
+
+ case 0x1e: /* Line In */
+ idx = 3;
+ break;
+
+ case 0x21: /* Front Mic */
+ idx = 4;
+ break;
+
+ case 0x23: /* CD */
+ idx = 1;
+ break;
+ }
+ err = via_new_analog_input(spec, cfg->input_pins[i], labels[i],
+ idx, 0x18);
+ if (err < 0)
+ return err;
+ imux->items[imux->num_items].label = labels[i];
+ imux->items[imux->num_items].index = idx;
+ imux->num_items++;
+ }
+ return 0;
+}
+
+static int vt1709_parse_auto_config(struct hda_codec *codec)
+{
+ struct via_spec *spec = codec->spec;
+ int err;
+
+ err = snd_hda_parse_pin_def_config(codec, &spec->autocfg, NULL);
+ if (err < 0)
+ return err;
+ err = vt1709_auto_fill_dac_nids(spec, &spec->autocfg);
+ if (err < 0)
+ return err;
+ if (!spec->autocfg.line_outs && !spec->autocfg.hp_pins[0])
+ return 0; /* can't find valid BIOS pin config */
+
+ err = vt1709_auto_create_multi_out_ctls(spec, &spec->autocfg);
+ if (err < 0)
+ return err;
+ err = vt1709_auto_create_hp_ctls(spec, spec->autocfg.hp_pins[0]);
+ if (err < 0)
+ return err;
+ err = vt1709_auto_create_analog_input_ctls(spec, &spec->autocfg);
+ if (err < 0)
+ return err;
+
+ spec->multiout.max_channels = spec->multiout.num_dacs * 2;
+
+ if (spec->autocfg.dig_out_pin)
+ spec->multiout.dig_out_nid = VT1709_DIGOUT_NID;
+ 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;
+
+ spec->input_mux = &spec->private_imux;
+
+ return 1;
+}
+
+static int patch_vt1709_10ch(struct hda_codec *codec)
+{
+ struct via_spec *spec;
+ int err;
+
+ /* create a codec specific record */
+ spec = kcalloc(1, sizeof(*spec), GFP_KERNEL);
+ if (spec == NULL)
+ return -ENOMEM;
+
+ codec->spec = spec;
+
+ err = vt1709_parse_auto_config(codec);
+ if (err < 0) {
+ via_free(codec);
+ return err;
+ } else if (!err) {
+ printk(KERN_INFO "hda_codec: Cannot set up configuration. "
+ "Using genenic mode...\n");
+ }
+
+ spec->init_verbs = vt1709_10ch_volume_init_verbs;
+
+ spec->stream_name_analog = "VT1709 Analog";
+ spec->stream_analog_playback = &vt1709_10ch_pcm_analog_playback;
+ spec->stream_analog_capture = &vt1709_pcm_analog_capture;
+
+ spec->stream_name_digital = "VT1709 Digital";
+ spec->stream_digital_playback = &vt1709_pcm_digital_playback;
+ spec->stream_digital_capture = &vt1709_pcm_digital_capture;
+
+
+ if (!spec->adc_nids && spec->input_mux) {
+ spec->adc_nids = vt1709_adc_nids;
+ spec->num_adc_nids = ARRAY_SIZE(vt1709_adc_nids);
+ spec->mixers[spec->num_mixers] = vt1709_capture_mixer;
+ spec->num_mixers++;
+ }
+
+ codec->patch_ops = via_patch_ops;
+
+ codec->patch_ops.init = via_auto_init;
+
+ return 0;
+}
+/*
+ * generic initialization of ADC, input mixers and output mixers
+ */
+static struct hda_verb vt1709_6ch_volume_init_verbs[] = {
+ /*
+ * Unmute ADC0-2 and set the default input to mic-in
+ */
+ {0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
+ {0x15, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
+ {0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
+
+
+ /* Unmute input amps (CD, Line In, Mic 1 & Mic 2) of the analog-loopback
+ * mixer widget
+ */
+ /* Amp Indices: AOW0=0, CD = 1, Mic1 = 2, Line = 3, Mic2 = 4 */
+ {0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
+ {0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
+ {0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(2)},
+ {0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(3)},
+ {0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(4)},
+
+ /*
+ * Set up output selector (0x1a, 0x1b, 0x29)
+ */
+ /* set vol=0 to output mixers */
+ {0x1a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
+ {0x1b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
+ {0x29, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
+
+ /*
+ * Unmute PW3 and PW4
+ */
+ {0x1f, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
+ {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
+
+ /* Set input of PW4 as MW0 */
+ {0x20, AC_VERB_SET_CONNECT_SEL, 0},
+ /* Set mic as default input of sw0 */
+ {0x19, AC_VERB_SET_CONNECT_SEL, 0x2},
+ /* PW9 Output enable */
+ {0x24, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x40},
+ { }
+};
+
+static int patch_vt1709_6ch(struct hda_codec *codec)
+{
+ struct via_spec *spec;
+ int err;
+
+ /* create a codec specific record */
+ spec = kcalloc(1, sizeof(*spec), GFP_KERNEL);
+ if (spec == NULL)
+ return -ENOMEM;
+
+ codec->spec = spec;
+
+ err = vt1709_parse_auto_config(codec);
+ if (err < 0) {
+ via_free(codec);
+ return err;
+ } else if (!err) {
+ printk(KERN_INFO "hda_codec: Cannot set up configuration. "
+ "Using genenic mode...\n");
+ }
+
+ spec->init_verbs = vt1709_6ch_volume_init_verbs;
+
+ spec->stream_name_analog = "VT1709 Analog";
+ spec->stream_analog_playback = &vt1709_6ch_pcm_analog_playback;
+ spec->stream_analog_capture = &vt1709_pcm_analog_capture;
+
+ spec->stream_name_digital = "VT1709 Digital";
+ spec->stream_digital_playback = &vt1709_pcm_digital_playback;
+ spec->stream_digital_capture = &vt1709_pcm_digital_capture;
+
+
+ if (!spec->adc_nids && spec->input_mux) {
+ spec->adc_nids = vt1709_adc_nids;
+ spec->num_adc_nids = ARRAY_SIZE(vt1709_adc_nids);
+ spec->mixers[spec->num_mixers] = vt1709_capture_mixer;
+ spec->num_mixers++;
+ }
+
+ codec->patch_ops = via_patch_ops;
+
+ codec->patch_ops.init = via_auto_init;
+
+ return 0;
+}
+
+/*
+ * 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", .patch = patch_vt1709_10ch},
+ { .id = 0x1106E711, .name = "VIA VT1709 10-Ch", .patch = patch_vt1709_10ch},
+ { .id = 0x1106E712, .name = "VIA VT1709 10-Ch", .patch = patch_vt1709_10ch},
+ { .id = 0x1106E713, .name = "VIA VT1709 10-Ch", .patch = patch_vt1709_10ch},
+ { .id = 0x1106E714, .name = "VIA VT1709 6-Ch", .patch = patch_vt1709_6ch},
+ { .id = 0x1106E715, .name = "VIA VT1709 6-Ch", .patch = patch_vt1709_6ch},
+ { .id = 0x1106E716, .name = "VIA VT1709 6-Ch", .patch = patch_vt1709_6ch},
+ { .id = 0x1106E717, .name = "VIA VT1709 6-Ch", .patch = patch_vt1709_6ch},
+ {} /* terminator */
+};
diff --git a/sound/pci/ice1712/Makefile b/sound/pci/ice1712/Makefile
index 7837cef8855c..6efdd62f6837 100644
--- a/sound/pci/ice1712/Makefile
+++ b/sound/pci/ice1712/Makefile
@@ -5,7 +5,7 @@
snd-ice17xx-ak4xxx-objs := ak4xxx.o
snd-ice1712-objs := ice1712.o delta.o hoontech.o ews.o
-snd-ice1724-objs := ice1724.o amp.o revo.o aureon.o vt1720_mobo.o pontis.o prodigy192.o juli.o phase.o
+snd-ice1724-objs := ice1724.o amp.o revo.o aureon.o vt1720_mobo.o pontis.o prodigy192.o juli.o phase.o wtm.o
# Toplevel Module Dependency
obj-$(CONFIG_SND_ICE1712) += snd-ice1712.o snd-ice17xx-ak4xxx.o
diff --git a/sound/pci/ice1712/amp.c b/sound/pci/ice1712/amp.c
index 59c4078ad331..6e22d326df32 100644
--- a/sound/pci/ice1712/amp.c
+++ b/sound/pci/ice1712/amp.c
@@ -42,7 +42,7 @@ static void wm_put(struct snd_ice1712 *ice, int reg, unsigned short val)
static int __devinit snd_vt1724_amp_init(struct snd_ice1712 *ice)
{
- static unsigned short wm_inits[] = {
+ static const unsigned short wm_inits[] = {
WM_ATTEN_L, 0x0000, /* 0 db */
WM_ATTEN_R, 0x0000, /* 0 db */
WM_DAC_CTRL, 0x0008, /* 24bit I2S */
@@ -75,7 +75,7 @@ static int __devinit snd_vt1724_amp_add_controls(struct snd_ice1712 *ice)
/* entry point */
-struct snd_ice1712_card_info snd_vt1724_amp_cards[] __devinitdata = {
+const struct snd_ice1712_card_info snd_vt1724_amp_cards[] __devinitdata = {
{
.subvendor = VT1724_SUBDEVICE_AV710,
.name = "Chaintech AV-710",
diff --git a/sound/pci/ice1712/amp.h b/sound/pci/ice1712/amp.h
index a0fc89b48122..7b667bad0c6b 100644
--- a/sound/pci/ice1712/amp.h
+++ b/sound/pci/ice1712/amp.h
@@ -42,7 +42,7 @@
#define WM_DAC_CTRL 0x02
#define WM_INT_CTRL 0x03
-extern struct snd_ice1712_card_info snd_vt1724_amp_cards[];
+extern const struct snd_ice1712_card_info snd_vt1724_amp_cards[];
#endif /* __SOUND_AMP_H */
diff --git a/sound/pci/ice1712/aureon.c b/sound/pci/ice1712/aureon.c
index 9e76cebd2d22..6941d85dfec9 100644
--- a/sound/pci/ice1712/aureon.c
+++ b/sound/pci/ice1712/aureon.c
@@ -294,7 +294,7 @@ static unsigned short aureon_ac97_read(struct snd_ice1712 *ice, unsigned short r
static int aureon_ac97_init (struct snd_ice1712 *ice)
{
int i;
- static unsigned short ac97_defaults[] = {
+ static const unsigned short ac97_defaults[] = {
0x00, 0x9640,
0x02, 0x8000,
0x04, 0x8000,
@@ -474,7 +474,8 @@ static void aureon_spi_write(struct snd_ice1712 *ice, unsigned int cs, unsigned
tmp = snd_ice1712_gpio_read(ice);
- if (ice->eeprom.subvendor == VT1724_SUBDEVICE_PRODIGY71LT) {
+ if (ice->eeprom.subvendor == VT1724_SUBDEVICE_PRODIGY71LT ||
+ ice->eeprom.subvendor == VT1724_SUBDEVICE_PRODIGY71XT) {
snd_ice1712_gpio_set_mask(ice, ~(PRODIGY_SPI_MOSI|PRODIGY_SPI_CLK|PRODIGY_WM_CS));
mosi = PRODIGY_SPI_MOSI;
clk = PRODIGY_SPI_CLK;
@@ -601,7 +602,9 @@ static unsigned short wm_get(struct snd_ice1712 *ice, int reg)
static void wm_put_nocache(struct snd_ice1712 *ice, int reg, unsigned short val)
{
aureon_spi_write(ice,
- (ice->eeprom.subvendor == VT1724_SUBDEVICE_PRODIGY71LT ? PRODIGY_WM_CS : AUREON_WM_CS),
+ ((ice->eeprom.subvendor == VT1724_SUBDEVICE_PRODIGY71LT ||
+ ice->eeprom.subvendor == VT1724_SUBDEVICE_PRODIGY71XT) ?
+ PRODIGY_WM_CS : AUREON_WM_CS),
(reg << 9) | (val & 0x1ff), 16);
}
@@ -661,17 +664,17 @@ static int aureon_ac97_mmute_put(struct snd_kcontrol *kcontrol, struct snd_ctl_e
return change;
}
-static DECLARE_TLV_DB_SCALE(db_scale_wm_dac, -12700, 100, 1);
-static DECLARE_TLV_DB_SCALE(db_scale_wm_pcm, -6400, 50, 1);
-static DECLARE_TLV_DB_SCALE(db_scale_wm_adc, -1200, 100, 0);
-static DECLARE_TLV_DB_SCALE(db_scale_ac97_master, -4650, 150, 0);
-static DECLARE_TLV_DB_SCALE(db_scale_ac97_gain, -3450, 150, 0);
+static const DECLARE_TLV_DB_SCALE(db_scale_wm_dac, -12700, 100, 1);
+static const DECLARE_TLV_DB_SCALE(db_scale_wm_pcm, -6400, 50, 1);
+static const DECLARE_TLV_DB_SCALE(db_scale_wm_adc, -1200, 100, 0);
+static const DECLARE_TLV_DB_SCALE(db_scale_ac97_master, -4650, 150, 0);
+static const DECLARE_TLV_DB_SCALE(db_scale_ac97_gain, -3450, 150, 0);
/*
* Logarithmic volume values for WM8770
* Computed as 20 * Log10(255 / x)
*/
-static unsigned char wm_vol[256] = {
+static const unsigned char wm_vol[256] = {
127, 48, 42, 39, 36, 34, 33, 31, 30, 29, 28, 27, 27, 26, 25, 25, 24, 24, 23,
23, 22, 22, 21, 21, 21, 20, 20, 20, 19, 19, 19, 18, 18, 18, 18, 17, 17, 17,
17, 16, 16, 16, 16, 15, 15, 15, 15, 15, 15, 14, 14, 14, 14, 14, 13, 13, 13,
@@ -1064,14 +1067,14 @@ static int wm_adc_vol_put(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_val
*/
static int wm_adc_mux_info(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *uinfo)
{
- static char *texts[] = {
+ static const char * const texts[] = {
"CD", //AIN1
"Aux", //AIN2
"Line", //AIN3
"Mic", //AIN4
"AC97" //AIN5
};
- static char *universe_texts[] = {
+ static const char * const universe_texts[] = {
"Aux1", //AIN1
"CD", //AIN2
"Phono", //AIN3
@@ -1137,11 +1140,11 @@ static int wm_adc_mux_put(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_val
static int aureon_cs8415_mux_info(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *uinfo)
{
struct snd_ice1712 *ice = snd_kcontrol_chip(kcontrol);
- static char *aureon_texts[] = {
+ static const char * const aureon_texts[] = {
"CD", //RXP0
"Optical" //RXP1
};
- static char *prodigy_texts[] = {
+ static const char * const prodigy_texts[] = {
"CD",
"Coax"
};
@@ -1288,12 +1291,14 @@ static int aureon_set_headphone_amp(struct snd_ice1712 *ice, int enable)
tmp2 = tmp = snd_ice1712_gpio_read(ice);
if (enable)
- if (ice->eeprom.subvendor != VT1724_SUBDEVICE_PRODIGY71LT)
+ if (ice->eeprom.subvendor != VT1724_SUBDEVICE_PRODIGY71LT &&
+ ice->eeprom.subvendor != VT1724_SUBDEVICE_PRODIGY71XT)
tmp |= AUREON_HP_SEL;
else
tmp |= PRODIGY_HP_SEL;
else
- if (ice->eeprom.subvendor != VT1724_SUBDEVICE_PRODIGY71LT)
+ if (ice->eeprom.subvendor != VT1724_SUBDEVICE_PRODIGY71LT &&
+ ice->eeprom.subvendor != VT1724_SUBDEVICE_PRODIGY71XT)
tmp &= ~ AUREON_HP_SEL;
else
tmp &= ~ PRODIGY_HP_SEL;
@@ -1363,7 +1368,7 @@ static int aureon_deemp_put(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_v
*/
static int aureon_oversampling_info(struct snd_kcontrol *k, struct snd_ctl_elem_info *uinfo)
{
- static char *texts[2] = { "128x", "64x" };
+ static const char * const texts[2] = { "128x", "64x" };
uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED;
uinfo->count = 1;
@@ -1406,7 +1411,7 @@ static int aureon_oversampling_put(struct snd_kcontrol *kcontrol, struct snd_ctl
* mixers
*/
-static struct snd_kcontrol_new aureon_dac_controls[] __devinitdata = {
+static const struct snd_kcontrol_new aureon_dac_controls[] __devinitdata = {
{
.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
.name = "Master Playback Switch",
@@ -1521,7 +1526,7 @@ static struct snd_kcontrol_new aureon_dac_controls[] __devinitdata = {
}
};
-static struct snd_kcontrol_new wm_controls[] __devinitdata = {
+static const struct snd_kcontrol_new wm_controls[] __devinitdata = {
{
.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
.name = "PCM Playback Switch",
@@ -1587,7 +1592,7 @@ static struct snd_kcontrol_new wm_controls[] __devinitdata = {
}
};
-static struct snd_kcontrol_new ac97_controls[] __devinitdata = {
+static const struct snd_kcontrol_new ac97_controls[] __devinitdata = {
{
.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
.name = "AC97 Playback Switch",
@@ -1692,7 +1697,7 @@ static struct snd_kcontrol_new ac97_controls[] __devinitdata = {
}
};
-static struct snd_kcontrol_new universe_ac97_controls[] __devinitdata = {
+static const struct snd_kcontrol_new universe_ac97_controls[] __devinitdata = {
{
.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
.name = "AC97 Playback Switch",
@@ -1824,8 +1829,7 @@ static struct snd_kcontrol_new universe_ac97_controls[] __devinitdata = {
};
-
-static struct snd_kcontrol_new cs8415_controls[] __devinitdata = {
+static const struct snd_kcontrol_new cs8415_controls[] __devinitdata = {
{
.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
.name = SNDRV_CTL_NAME_IEC958("",CAPTURE,SWITCH),
@@ -1870,7 +1874,6 @@ static struct snd_kcontrol_new cs8415_controls[] __devinitdata = {
}
};
-
static int __devinit aureon_add_controls(struct snd_ice1712 *ice)
{
unsigned int i, counts;
@@ -1898,7 +1901,8 @@ static int __devinit aureon_add_controls(struct snd_ice1712 *ice)
return err;
}
}
- else if (ice->eeprom.subvendor != VT1724_SUBDEVICE_PRODIGY71LT) {
+ else if (ice->eeprom.subvendor != VT1724_SUBDEVICE_PRODIGY71LT &&
+ ice->eeprom.subvendor != VT1724_SUBDEVICE_PRODIGY71XT) {
for (i = 0; i < ARRAY_SIZE(ac97_controls); i++) {
err = snd_ctl_add(ice->card, snd_ctl_new1(&ac97_controls[i], ice));
if (err < 0)
@@ -1906,7 +1910,8 @@ static int __devinit aureon_add_controls(struct snd_ice1712 *ice)
}
}
- if (ice->eeprom.subvendor != VT1724_SUBDEVICE_PRODIGY71LT) {
+ if (ice->eeprom.subvendor != VT1724_SUBDEVICE_PRODIGY71LT &&
+ ice->eeprom.subvendor != VT1724_SUBDEVICE_PRODIGY71XT) {
unsigned char id;
snd_ice1712_save_gpio_status(ice);
id = aureon_cs8415_get(ice, CS8415_ID);
@@ -1936,7 +1941,7 @@ static int __devinit aureon_add_controls(struct snd_ice1712 *ice)
*/
static int __devinit aureon_init(struct snd_ice1712 *ice)
{
- static unsigned short wm_inits_aureon[] = {
+ static const unsigned short wm_inits_aureon[] = {
/* These come first to reduce init pop noise */
0x1b, 0x044, /* ADC Mux (AC'97 source) */
0x1c, 0x00B, /* Out Mux1 (VOUT1 = DAC+AUX, VOUT2 = DAC) */
@@ -1972,7 +1977,7 @@ static int __devinit aureon_init(struct snd_ice1712 *ice)
0x1a, 0x000, /* -12dB ADC/R */
(unsigned short)-1
};
- static unsigned short wm_inits_prodigy[] = {
+ static const unsigned short wm_inits_prodigy[] = {
/* These come first to reduce init pop noise */
0x1b, 0x000, /* ADC Mux */
@@ -2014,7 +2019,7 @@ static int __devinit aureon_init(struct snd_ice1712 *ice)
(unsigned short)-1
};
- static unsigned short cs_inits[] = {
+ static const unsigned short cs_inits[] = {
0x0441, /* RUN */
0x0180, /* no mute, OMCK output on RMCK pin */
0x0201, /* S/PDIF source on RXP1 */
@@ -2022,7 +2027,7 @@ static int __devinit aureon_init(struct snd_ice1712 *ice)
(unsigned short)-1
};
unsigned int tmp;
- unsigned short *p;
+ const unsigned short *p;
int err, i;
if (ice->eeprom.subvendor == VT1724_SUBDEVICE_AUREON51_SKY) {
@@ -2062,7 +2067,8 @@ static int __devinit aureon_init(struct snd_ice1712 *ice)
/* initialize WM8770 codec */
if (ice->eeprom.subvendor == VT1724_SUBDEVICE_PRODIGY71 ||
- ice->eeprom.subvendor == VT1724_SUBDEVICE_PRODIGY71LT)
+ ice->eeprom.subvendor == VT1724_SUBDEVICE_PRODIGY71LT ||
+ ice->eeprom.subvendor == VT1724_SUBDEVICE_PRODIGY71XT)
p = wm_inits_prodigy;
else
p = wm_inits_aureon;
@@ -2070,7 +2076,8 @@ static int __devinit aureon_init(struct snd_ice1712 *ice)
wm_put(ice, p[0], p[1]);
/* initialize CS8415A codec */
- if (ice->eeprom.subvendor != VT1724_SUBDEVICE_PRODIGY71LT) {
+ if (ice->eeprom.subvendor != VT1724_SUBDEVICE_PRODIGY71LT &&
+ ice->eeprom.subvendor != VT1724_SUBDEVICE_PRODIGY71XT) {
for (p = cs_inits; *p != (unsigned short)-1; p++)
aureon_spi_write(ice, AUREON_CS8415_CS, *p | 0x200000, 24);
ice->spec.aureon.cs8415_mux = 1;
@@ -2100,73 +2107,58 @@ static int __devinit aureon_init(struct snd_ice1712 *ice)
* hence the driver needs to sets up it properly.
*/
-static unsigned char aureon51_eeprom[] __devinitdata = {
- 0x0a, /* SYSCONF: clock 512, spdif-in/ADC, 3DACs */
- 0x80, /* ACLINK: I2S */
- 0xfc, /* I2S: vol, 96k, 24bit, 192k */
- 0xc3, /* SPDIF: out-en, out-int, spdif-in */
- 0xff, /* GPIO_DIR */
- 0xff, /* GPIO_DIR1 */
- 0x5f, /* GPIO_DIR2 */
- 0x00, /* GPIO_MASK */
- 0x00, /* GPIO_MASK1 */
- 0x00, /* GPIO_MASK2 */
- 0x00, /* GPIO_STATE */
- 0x00, /* GPIO_STATE1 */
- 0x00, /* GPIO_STATE2 */
+static const unsigned char aureon51_eeprom[] __devinitdata = {
+ [ICE_EEP2_SYSCONF] = 0x0a, /* clock 512, spdif-in/ADC, 3DACs */
+ [ICE_EEP2_ACLINK] = 0x80, /* I2S */
+ [ICE_EEP2_I2S] = 0xfc, /* vol, 96k, 24bit, 192k */
+ [ICE_EEP2_SPDIF] = 0xc3, /* out-en, out-int, spdif-in */
+ [ICE_EEP2_GPIO_DIR] = 0xff,
+ [ICE_EEP2_GPIO_DIR1] = 0xff,
+ [ICE_EEP2_GPIO_DIR2] = 0x5f,
+ [ICE_EEP2_GPIO_MASK] = 0x00,
+ [ICE_EEP2_GPIO_MASK1] = 0x00,
+ [ICE_EEP2_GPIO_MASK2] = 0x00,
+ [ICE_EEP2_GPIO_STATE] = 0x00,
+ [ICE_EEP2_GPIO_STATE1] = 0x00,
+ [ICE_EEP2_GPIO_STATE2] = 0x00,
};
-static unsigned char aureon71_eeprom[] __devinitdata = {
- 0x0b, /* SYSCONF: clock 512, spdif-in/ADC, 4DACs */
- 0x80, /* ACLINK: I2S */
- 0xfc, /* I2S: vol, 96k, 24bit, 192k */
- 0xc3, /* SPDIF: out-en, out-int, spdif-in */
- 0xff, /* GPIO_DIR */
- 0xff, /* GPIO_DIR1 */
- 0x5f, /* GPIO_DIR2 */
- 0x00, /* GPIO_MASK */
- 0x00, /* GPIO_MASK1 */
- 0x00, /* GPIO_MASK2 */
- 0x00, /* GPIO_STATE */
- 0x00, /* GPIO_STATE1 */
- 0x00, /* GPIO_STATE2 */
+static const unsigned char aureon71_eeprom[] __devinitdata = {
+ [ICE_EEP2_SYSCONF] = 0x0b, /* clock 512, spdif-in/ADC, 4DACs */
+ [ICE_EEP2_ACLINK] = 0x80, /* I2S */
+ [ICE_EEP2_I2S] = 0xfc, /* vol, 96k, 24bit, 192k */
+ [ICE_EEP2_SPDIF] = 0xc3, /* out-en, out-int, spdif-in */
+ [ICE_EEP2_GPIO_DIR] = 0xff,
+ [ICE_EEP2_GPIO_DIR1] = 0xff,
+ [ICE_EEP2_GPIO_DIR2] = 0x5f,
+ [ICE_EEP2_GPIO_MASK] = 0x00,
+ [ICE_EEP2_GPIO_MASK1] = 0x00,
+ [ICE_EEP2_GPIO_MASK2] = 0x00,
+ [ICE_EEP2_GPIO_STATE] = 0x00,
+ [ICE_EEP2_GPIO_STATE1] = 0x00,
+ [ICE_EEP2_GPIO_STATE2] = 0x00,
};
-
-static unsigned char prodigy71_eeprom[] __devinitdata = {
- 0x0b, /* SYSCONF: clock 512, spdif-in/ADC, 4DACs */
- 0x80, /* ACLINK: I2S */
- 0xfc, /* I2S: vol, 96k, 24bit, 192k */
- 0xc3, /* SPDIF: out-en, out-int, spdif-in */
- 0xff, /* GPIO_DIR */
- 0xff, /* GPIO_DIR1 */
- 0x5f, /* GPIO_DIR2 */
- 0x00, /* GPIO_MASK */
- 0x00, /* GPIO_MASK1 */
- 0x00, /* GPIO_MASK2 */
- 0x00, /* GPIO_STATE */
- 0x00, /* GPIO_STATE1 */
- 0x00, /* GPIO_STATE2 */
+#define prodigy71_eeprom aureon71_eeprom
+
+static const unsigned char prodigy71lt_eeprom[] __devinitdata = {
+ [ICE_EEP2_SYSCONF] = 0x4b, /* clock 384, spdif-in/ADC, 4DACs */
+ [ICE_EEP2_ACLINK] = 0x80, /* I2S */
+ [ICE_EEP2_I2S] = 0xfc, /* vol, 96k, 24bit, 192k */
+ [ICE_EEP2_SPDIF] = 0xc3, /* out-en, out-int, spdif-in */
+ [ICE_EEP2_GPIO_DIR] = 0xff,
+ [ICE_EEP2_GPIO_DIR1] = 0xff,
+ [ICE_EEP2_GPIO_DIR2] = 0x5f,
+ [ICE_EEP2_GPIO_MASK] = 0x00,
+ [ICE_EEP2_GPIO_MASK1] = 0x00,
+ [ICE_EEP2_GPIO_MASK2] = 0x00,
+ [ICE_EEP2_GPIO_STATE] = 0x00,
+ [ICE_EEP2_GPIO_STATE1] = 0x00,
+ [ICE_EEP2_GPIO_STATE2] = 0x00,
};
-
-static unsigned char prodigy71lt_eeprom[] __devinitdata = {
- 0x4b, /* SYSCINF: clock 512, spdif-in/ADC, 4DACs */
- 0x80, /* ACLINK: I2S */
- 0xfc, /* I2S: vol, 96k, 24bit, 192k */
- 0xc3, /* SPDIF: out-en, out-int, spdif-in */
- 0xff, /* GPIO_DIR */
- 0xff, /* GPIO_DIR1 */
- 0x5f, /* GPIO_DIR2 */
- 0x00, /* GPIO_MASK */
- 0x00, /* GPIO_MASK1 */
- 0x00, /* GPIO_MASK2 */
- 0x00, /* GPIO_STATE */
- 0x00, /* GPIO_STATE1 */
- 0x00, /* GPIO_STATE2 */
-};
-
+#define prodigy71xt_eeprom prodigy71lt_eeprom
/* entry point */
-struct snd_ice1712_card_info snd_vt1724_aureon_cards[] __devinitdata = {
+const struct snd_ice1712_card_info snd_vt1724_aureon_cards[] __devinitdata = {
{
.subvendor = VT1724_SUBDEVICE_AUREON51_SKY,
.name = "Terratec Aureon 5.1-Sky",
@@ -2217,5 +2209,15 @@ struct snd_ice1712_card_info snd_vt1724_aureon_cards[] __devinitdata = {
.eeprom_data = prodigy71lt_eeprom,
.driver = "Prodigy71LT",
},
+ {
+ .subvendor = VT1724_SUBDEVICE_PRODIGY71XT,
+ .name = "Audiotrak Prodigy 7.1 XT",
+ .model = "prodigy71xt",
+ .chip_init = aureon_init,
+ .build_controls = aureon_add_controls,
+ .eeprom_size = sizeof(prodigy71xt_eeprom),
+ .eeprom_data = prodigy71xt_eeprom,
+ .driver = "Prodigy71LT",
+ },
{ } /* terminator */
};
diff --git a/sound/pci/ice1712/aureon.h b/sound/pci/ice1712/aureon.h
index 3b7bea656c57..79e58e88ed47 100644
--- a/sound/pci/ice1712/aureon.h
+++ b/sound/pci/ice1712/aureon.h
@@ -28,15 +28,17 @@
"{Terratec,Aureon 7.1 Space},"\
"{Terratec,Aureon 7.1 Universe}," \
"{AudioTrak,Prodigy 7.1}," \
- "{AudioTrak,Prodigy 7.1 LT},"
+ "{AudioTrak,Prodigy 7.1 LT},"\
+ "{AudioTrak,Prodigy 7.1 XT},"
#define VT1724_SUBDEVICE_AUREON51_SKY 0x3b154711 /* Aureon 5.1 Sky */
#define VT1724_SUBDEVICE_AUREON71_SPACE 0x3b154511 /* Aureon 7.1 Space */
#define VT1724_SUBDEVICE_AUREON71_UNIVERSE 0x3b155311 /* Aureon 7.1 Universe */
#define VT1724_SUBDEVICE_PRODIGY71 0x33495345 /* PRODIGY 7.1 */
#define VT1724_SUBDEVICE_PRODIGY71LT 0x32315441 /* PRODIGY 7.1 LT */
+#define VT1724_SUBDEVICE_PRODIGY71XT 0x36315441 /* PRODIGY 7.1 XT*/
-extern struct snd_ice1712_card_info snd_vt1724_aureon_cards[];
+extern const struct snd_ice1712_card_info snd_vt1724_aureon_cards[];
/* GPIO bits */
#define AUREON_CS8415_CS (1 << 22)
diff --git a/sound/pci/ice1712/delta.c b/sound/pci/ice1712/delta.c
index af659800c9b0..3eeb36c6e985 100644
--- a/sound/pci/ice1712/delta.c
+++ b/sound/pci/ice1712/delta.c
@@ -416,7 +416,7 @@ static int snd_ice1712_delta1010lt_wordclock_status_get(struct snd_kcontrol *kco
return 0;
}
-static struct snd_kcontrol_new snd_ice1712_delta1010lt_wordclock_status __devinitdata =
+static const struct snd_kcontrol_new snd_ice1712_delta1010lt_wordclock_status __devinitdata =
{
.access = (SNDRV_CTL_ELEM_ACCESS_READ),
.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
@@ -429,7 +429,7 @@ static struct snd_kcontrol_new snd_ice1712_delta1010lt_wordclock_status __devini
* initialize the chips on M-Audio cards
*/
-static struct snd_akm4xxx akm_audiophile __devinitdata = {
+static const struct snd_akm4xxx akm_audiophile __devinitdata = {
.type = SND_AK4528,
.num_adcs = 2,
.num_dacs = 2,
@@ -438,7 +438,7 @@ static struct snd_akm4xxx akm_audiophile __devinitdata = {
}
};
-static struct snd_ak4xxx_private akm_audiophile_priv __devinitdata = {
+static const struct snd_ak4xxx_private akm_audiophile_priv __devinitdata = {
.caddr = 2,
.cif = 0,
.data_mask = ICE1712_DELTA_AP_DOUT,
@@ -450,7 +450,7 @@ static struct snd_ak4xxx_private akm_audiophile_priv __devinitdata = {
.mask_flags = 0,
};
-static struct snd_akm4xxx akm_delta410 __devinitdata = {
+static const struct snd_akm4xxx akm_delta410 __devinitdata = {
.type = SND_AK4529,
.num_adcs = 2,
.num_dacs = 8,
@@ -459,7 +459,7 @@ static struct snd_akm4xxx akm_delta410 __devinitdata = {
}
};
-static struct snd_ak4xxx_private akm_delta410_priv __devinitdata = {
+static const struct snd_ak4xxx_private akm_delta410_priv __devinitdata = {
.caddr = 0,
.cif = 0,
.data_mask = ICE1712_DELTA_AP_DOUT,
@@ -471,7 +471,7 @@ static struct snd_ak4xxx_private akm_delta410_priv __devinitdata = {
.mask_flags = 0,
};
-static struct snd_akm4xxx akm_delta1010lt __devinitdata = {
+static const struct snd_akm4xxx akm_delta1010lt __devinitdata = {
.type = SND_AK4524,
.num_adcs = 8,
.num_dacs = 8,
@@ -481,7 +481,7 @@ static struct snd_akm4xxx akm_delta1010lt __devinitdata = {
}
};
-static struct snd_ak4xxx_private akm_delta1010lt_priv __devinitdata = {
+static const struct snd_ak4xxx_private akm_delta1010lt_priv __devinitdata = {
.caddr = 2,
.cif = 0, /* the default level of the CIF pin from AK4524 */
.data_mask = ICE1712_DELTA_1010LT_DOUT,
@@ -493,7 +493,7 @@ static struct snd_ak4xxx_private akm_delta1010lt_priv __devinitdata = {
.mask_flags = 0,
};
-static struct snd_akm4xxx akm_delta44 __devinitdata = {
+static const struct snd_akm4xxx akm_delta44 __devinitdata = {
.type = SND_AK4524,
.num_adcs = 4,
.num_dacs = 4,
@@ -503,7 +503,7 @@ static struct snd_akm4xxx akm_delta44 __devinitdata = {
}
};
-static struct snd_ak4xxx_private akm_delta44_priv __devinitdata = {
+static const struct snd_ak4xxx_private akm_delta44_priv __devinitdata = {
.caddr = 2,
.cif = 0, /* the default level of the CIF pin from AK4524 */
.data_mask = ICE1712_DELTA_CODEC_SERIAL_DATA,
@@ -515,7 +515,7 @@ static struct snd_ak4xxx_private akm_delta44_priv __devinitdata = {
.mask_flags = 0,
};
-static struct snd_akm4xxx akm_vx442 __devinitdata = {
+static const struct snd_akm4xxx akm_vx442 __devinitdata = {
.type = SND_AK4524,
.num_adcs = 4,
.num_dacs = 4,
@@ -525,7 +525,7 @@ static struct snd_akm4xxx akm_vx442 __devinitdata = {
}
};
-static struct snd_ak4xxx_private akm_vx442_priv __devinitdata = {
+static const struct snd_ak4xxx_private akm_vx442_priv __devinitdata = {
.caddr = 2,
.cif = 0,
.data_mask = ICE1712_VX442_DOUT,
@@ -650,15 +650,15 @@ static int __devinit snd_ice1712_delta_init(struct snd_ice1712 *ice)
* additional controls for M-Audio cards
*/
-static struct snd_kcontrol_new snd_ice1712_delta1010_wordclock_select __devinitdata =
+static const struct snd_kcontrol_new snd_ice1712_delta1010_wordclock_select __devinitdata =
ICE1712_GPIO(SNDRV_CTL_ELEM_IFACE_MIXER, "Word Clock Sync", 0, ICE1712_DELTA_WORD_CLOCK_SELECT, 1, 0);
-static struct snd_kcontrol_new snd_ice1712_delta1010lt_wordclock_select __devinitdata =
+static const struct snd_kcontrol_new snd_ice1712_delta1010lt_wordclock_select __devinitdata =
ICE1712_GPIO(SNDRV_CTL_ELEM_IFACE_MIXER, "Word Clock Sync", 0, ICE1712_DELTA_1010LT_WORDCLOCK, 0, 0);
-static struct snd_kcontrol_new snd_ice1712_delta1010_wordclock_status __devinitdata =
+static const struct snd_kcontrol_new snd_ice1712_delta1010_wordclock_status __devinitdata =
ICE1712_GPIO(SNDRV_CTL_ELEM_IFACE_MIXER, "Word Clock Status", 0, ICE1712_DELTA_WORD_CLOCK_STATUS, 1, SNDRV_CTL_ELEM_ACCESS_READ | SNDRV_CTL_ELEM_ACCESS_VOLATILE);
-static struct snd_kcontrol_new snd_ice1712_deltadio2496_spdif_in_select __devinitdata =
+static const struct snd_kcontrol_new snd_ice1712_deltadio2496_spdif_in_select __devinitdata =
ICE1712_GPIO(SNDRV_CTL_ELEM_IFACE_MIXER, "IEC958 Input Optical", 0, ICE1712_DELTA_SPDIF_INPUT_SELECT, 0, 0);
-static struct snd_kcontrol_new snd_ice1712_delta_spdif_in_status __devinitdata =
+static const struct snd_kcontrol_new snd_ice1712_delta_spdif_in_status __devinitdata =
ICE1712_GPIO(SNDRV_CTL_ELEM_IFACE_MIXER, "Delta IEC958 Input Status", 0, ICE1712_DELTA_SPDIF_IN_STAT, 1, SNDRV_CTL_ELEM_ACCESS_READ | SNDRV_CTL_ELEM_ACCESS_VOLATILE);
@@ -735,7 +735,7 @@ static int __devinit snd_ice1712_delta_add_controls(struct snd_ice1712 *ice)
/* entry point */
-struct snd_ice1712_card_info snd_ice1712_delta_cards[] __devinitdata = {
+const struct snd_ice1712_card_info snd_ice1712_delta_cards[] __devinitdata = {
{
.subvendor = ICE1712_SUBDEVICE_DELTA1010,
.name = "M Audio Delta 1010",
diff --git a/sound/pci/ice1712/delta.h b/sound/pci/ice1712/delta.h
index 746ebde94522..e65d669af639 100644
--- a/sound/pci/ice1712/delta.h
+++ b/sound/pci/ice1712/delta.h
@@ -46,7 +46,7 @@
#define ICE1712_SUBDEVICE_MEDIASTATION 0x694c0100
/* entry point */
-extern struct snd_ice1712_card_info snd_ice1712_delta_cards[];
+extern const struct snd_ice1712_card_info snd_ice1712_delta_cards[];
/*
diff --git a/sound/pci/ice1712/ews.c b/sound/pci/ice1712/ews.c
index b135389fec6c..9b7ff302c072 100644
--- a/sound/pci/ice1712/ews.c
+++ b/sound/pci/ice1712/ews.c
@@ -332,7 +332,7 @@ static void ews88_setup_spdif(struct snd_ice1712 *ice, int rate)
/*
*/
-static struct snd_akm4xxx akm_ews88mt __devinitdata = {
+static const struct snd_akm4xxx akm_ews88mt __devinitdata = {
.num_adcs = 8,
.num_dacs = 8,
.type = SND_AK4524,
@@ -342,7 +342,7 @@ static struct snd_akm4xxx akm_ews88mt __devinitdata = {
}
};
-static struct snd_ak4xxx_private akm_ews88mt_priv __devinitdata = {
+static const struct snd_ak4xxx_private akm_ews88mt_priv __devinitdata = {
.caddr = 2,
.cif = 1, /* CIF high */
.data_mask = ICE1712_EWS88_SERIAL_DATA,
@@ -354,7 +354,7 @@ static struct snd_ak4xxx_private akm_ews88mt_priv __devinitdata = {
.mask_flags = 0,
};
-static struct snd_akm4xxx akm_ewx2496 __devinitdata = {
+static const struct snd_akm4xxx akm_ewx2496 __devinitdata = {
.num_adcs = 2,
.num_dacs = 2,
.type = SND_AK4524,
@@ -363,7 +363,7 @@ static struct snd_akm4xxx akm_ewx2496 __devinitdata = {
}
};
-static struct snd_ak4xxx_private akm_ewx2496_priv __devinitdata = {
+static const struct snd_ak4xxx_private akm_ewx2496_priv __devinitdata = {
.caddr = 2,
.cif = 1, /* CIF high */
.data_mask = ICE1712_EWS88_SERIAL_DATA,
@@ -375,7 +375,7 @@ static struct snd_ak4xxx_private akm_ewx2496_priv __devinitdata = {
.mask_flags = 0,
};
-static struct snd_akm4xxx akm_6fire __devinitdata = {
+static const struct snd_akm4xxx akm_6fire __devinitdata = {
.num_adcs = 6,
.num_dacs = 6,
.type = SND_AK4524,
@@ -384,7 +384,7 @@ static struct snd_akm4xxx akm_6fire __devinitdata = {
}
};
-static struct snd_ak4xxx_private akm_6fire_priv __devinitdata = {
+static const struct snd_ak4xxx_private akm_6fire_priv __devinitdata = {
.caddr = 2,
.cif = 1, /* CIF high */
.data_mask = ICE1712_6FIRE_SERIAL_DATA,
@@ -578,7 +578,7 @@ static int snd_ice1712_ewx_io_sense_put(struct snd_kcontrol *kcontrol, struct sn
return val != nval;
}
-static struct snd_kcontrol_new snd_ice1712_ewx2496_controls[] __devinitdata = {
+static const struct snd_kcontrol_new snd_ice1712_ewx2496_controls[] __devinitdata = {
{
.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
.name = "Input Sensitivity Switch",
@@ -678,7 +678,7 @@ static int snd_ice1712_ews88mt_input_sense_put(struct snd_kcontrol *kcontrol, st
return ndata != data;
}
-static struct snd_kcontrol_new snd_ice1712_ews88mt_input_sense __devinitdata = {
+static const struct snd_kcontrol_new snd_ice1712_ews88mt_input_sense __devinitdata = {
.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
.name = "Input Sensitivity Switch",
.info = snd_ice1712_ewx_io_sense_info,
@@ -687,7 +687,7 @@ static struct snd_kcontrol_new snd_ice1712_ews88mt_input_sense __devinitdata = {
.count = 8,
};
-static struct snd_kcontrol_new snd_ice1712_ews88mt_output_sense __devinitdata = {
+static const struct snd_kcontrol_new snd_ice1712_ews88mt_output_sense __devinitdata = {
.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
.name = "Output Sensitivity Switch",
.info = snd_ice1712_ewx_io_sense_info,
@@ -769,7 +769,7 @@ static int snd_ice1712_ews88d_control_put(struct snd_kcontrol *kcontrol, struct
.private_value = xshift | (xinvert << 8),\
}
-static struct snd_kcontrol_new snd_ice1712_ews88d_controls[] __devinitdata = {
+static const struct snd_kcontrol_new snd_ice1712_ews88d_controls[] __devinitdata = {
EWS88D_CONTROL(SNDRV_CTL_ELEM_IFACE_MIXER, "IEC958 Input Optical", 0, 1, 0), /* inverted */
EWS88D_CONTROL(SNDRV_CTL_ELEM_IFACE_MIXER, "ADAT Output Optical", 1, 0, 0),
EWS88D_CONTROL(SNDRV_CTL_ELEM_IFACE_MIXER, "ADAT External Master Clock", 2, 0, 0),
@@ -909,7 +909,7 @@ static int snd_ice1712_6fire_select_input_put(struct snd_kcontrol *kcontrol, str
.private_value = xshift | (xinvert << 8),\
}
-static struct snd_kcontrol_new snd_ice1712_6fire_controls[] __devinitdata = {
+static const struct snd_kcontrol_new snd_ice1712_6fire_controls[] __devinitdata = {
{
.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
.name = "Analog Input Select",
@@ -989,7 +989,7 @@ static int __devinit snd_ice1712_ews_add_controls(struct snd_ice1712 *ice)
/* entry point */
-struct snd_ice1712_card_info snd_ice1712_ews_cards[] __devinitdata = {
+const struct snd_ice1712_card_info snd_ice1712_ews_cards[] __devinitdata = {
{
.subvendor = ICE1712_SUBDEVICE_EWX2496,
.name = "TerraTec EWX24/96",
diff --git a/sound/pci/ice1712/ews.h b/sound/pci/ice1712/ews.h
index a12a0b053558..df449b4741f6 100644
--- a/sound/pci/ice1712/ews.h
+++ b/sound/pci/ice1712/ews.h
@@ -40,7 +40,7 @@
#define ICE1712_SUBDEVICE_PHASE88 0x3b155111
/* entry point */
-extern struct snd_ice1712_card_info snd_ice1712_ews_cards[];
+extern const struct snd_ice1712_card_info snd_ice1712_ews_cards[];
/* TerraTec EWX 24/96 configuration definitions */
diff --git a/sound/pci/ice1712/hoontech.c b/sound/pci/ice1712/hoontech.c
index 3f27d04e7d3c..df97313aaf83 100644
--- a/sound/pci/ice1712/hoontech.c
+++ b/sound/pci/ice1712/hoontech.c
@@ -239,7 +239,7 @@ static void stdsp24_ak4524_lock(struct snd_akm4xxx *ak, int chip)
static int __devinit snd_ice1712_value_init(struct snd_ice1712 *ice)
{
/* Hoontech STDSP24 with modified hardware */
- static struct snd_akm4xxx akm_stdsp24_mv __devinitdata = {
+ static const struct snd_akm4xxx akm_stdsp24_mv __devinitdata = {
.num_adcs = 2,
.num_dacs = 2,
.type = SND_AK4524,
@@ -248,7 +248,7 @@ static int __devinit snd_ice1712_value_init(struct snd_ice1712 *ice)
}
};
- static struct snd_ak4xxx_private akm_stdsp24_mv_priv __devinitdata = {
+ static const struct snd_ak4xxx_private akm_stdsp24_mv_priv __devinitdata = {
.caddr = 2,
.cif = 1, /* CIF high */
.data_mask = ICE1712_STDSP24_SERIAL_DATA,
@@ -298,7 +298,7 @@ static int __devinit snd_ice1712_ez8_init(struct snd_ice1712 *ice)
/* entry point */
-struct snd_ice1712_card_info snd_ice1712_hoontech_cards[] __devinitdata = {
+const struct snd_ice1712_card_info snd_ice1712_hoontech_cards[] __devinitdata = {
{
.subvendor = ICE1712_SUBDEVICE_STDSP24,
.name = "Hoontech SoundTrack Audio DSP24",
@@ -325,4 +325,3 @@ struct snd_ice1712_card_info snd_ice1712_hoontech_cards[] __devinitdata = {
},
{ } /* terminator */
};
-
diff --git a/sound/pci/ice1712/hoontech.h b/sound/pci/ice1712/hoontech.h
index 1ee538b20fbf..b62d6e4f6c71 100644
--- a/sound/pci/ice1712/hoontech.h
+++ b/sound/pci/ice1712/hoontech.h
@@ -35,7 +35,7 @@
#define ICE1712_SUBDEVICE_STDSP24_MEDIA7_1 0x16141217 /* Hoontech ST Audio DSP24 Media 7.1 */
#define ICE1712_SUBDEVICE_EVENT_EZ8 0x00010001 /* A dummy id for EZ8 */
-extern struct snd_ice1712_card_info snd_ice1712_hoontech_cards[];
+extern const struct snd_ice1712_card_info snd_ice1712_hoontech_cards[];
/* Hoontech SoundTrack Audio DSP 24 GPIO definitions */
diff --git a/sound/pci/ice1712/ice1712.c b/sound/pci/ice1712/ice1712.c
index 8ba31cfb9045..830a1bbd7110 100644
--- a/sound/pci/ice1712/ice1712.c
+++ b/sound/pci/ice1712/ice1712.c
@@ -107,7 +107,7 @@ module_param_array(dxr_enable, int, NULL, 0444);
MODULE_PARM_DESC(dxr_enable, "Enable DXR support for Terratec DMX6FIRE.");
-static struct pci_device_id snd_ice1712_ids[] = {
+static const struct pci_device_id snd_ice1712_ids[] = {
{ PCI_VENDOR_ID_ICE, PCI_DEVICE_ID_ICE_1712, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0 }, /* ICE1712 */
{ 0, }
};
@@ -287,7 +287,7 @@ static int snd_ice1712_digmix_route_ac97_put(struct snd_kcontrol *kcontrol, stru
return val != nval;
}
-static struct snd_kcontrol_new snd_ice1712_mixer_digmix_route_ac97 __devinitdata = {
+static const struct snd_kcontrol_new snd_ice1712_mixer_digmix_route_ac97 __devinitdata = {
.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
.name = "Digital Mixer To AC97",
.info = snd_ice1712_digmix_route_ac97_info,
@@ -719,7 +719,7 @@ static snd_pcm_uframes_t snd_ice1712_capture_pointer(struct snd_pcm_substream *s
return bytes_to_frames(substream->runtime, ptr);
}
-static struct snd_pcm_hardware snd_ice1712_playback =
+static const struct snd_pcm_hardware snd_ice1712_playback =
{
.info = (SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_INTERLEAVED |
SNDRV_PCM_INFO_BLOCK_TRANSFER |
@@ -739,7 +739,7 @@ static struct snd_pcm_hardware snd_ice1712_playback =
.fifo_size = 0,
};
-static struct snd_pcm_hardware snd_ice1712_playback_ds =
+static const struct snd_pcm_hardware snd_ice1712_playback_ds =
{
.info = (SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_INTERLEAVED |
SNDRV_PCM_INFO_BLOCK_TRANSFER |
@@ -759,7 +759,7 @@ static struct snd_pcm_hardware snd_ice1712_playback_ds =
.fifo_size = 0,
};
-static struct snd_pcm_hardware snd_ice1712_capture =
+static const struct snd_pcm_hardware snd_ice1712_capture =
{
.info = (SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_INTERLEAVED |
SNDRV_PCM_INFO_BLOCK_TRANSFER |
@@ -1133,7 +1133,7 @@ static snd_pcm_uframes_t snd_ice1712_capture_pro_pointer(struct snd_pcm_substrea
return bytes_to_frames(substream->runtime, ptr);
}
-static struct snd_pcm_hardware snd_ice1712_playback_pro =
+static const struct snd_pcm_hardware snd_ice1712_playback_pro =
{
.info = (SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_INTERLEAVED |
SNDRV_PCM_INFO_BLOCK_TRANSFER |
@@ -1153,7 +1153,7 @@ static struct snd_pcm_hardware snd_ice1712_playback_pro =
.fifo_size = 0,
};
-static struct snd_pcm_hardware snd_ice1712_capture_pro =
+static const struct snd_pcm_hardware snd_ice1712_capture_pro =
{
.info = (SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_INTERLEAVED |
SNDRV_PCM_INFO_BLOCK_TRANSFER |
@@ -1378,9 +1378,9 @@ static int snd_ice1712_pro_mixer_volume_put(struct snd_kcontrol *kcontrol, struc
return change;
}
-static DECLARE_TLV_DB_SCALE(db_scale_playback, -14400, 150, 0);
+static const DECLARE_TLV_DB_SCALE(db_scale_playback, -14400, 150, 0);
-static struct snd_kcontrol_new snd_ice1712_multi_playback_ctrls[] __devinitdata = {
+static const struct snd_kcontrol_new snd_ice1712_multi_playback_ctrls[] __devinitdata = {
{
.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
.name = "Multi Playback Switch",
@@ -1404,7 +1404,7 @@ static struct snd_kcontrol_new snd_ice1712_multi_playback_ctrls[] __devinitdata
},
};
-static struct snd_kcontrol_new snd_ice1712_multi_capture_analog_switch __devinitdata = {
+static const struct snd_kcontrol_new snd_ice1712_multi_capture_analog_switch __devinitdata = {
.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
.name = "H/W Multi Capture Switch",
.info = snd_ice1712_pro_mixer_switch_info,
@@ -1413,7 +1413,7 @@ static struct snd_kcontrol_new snd_ice1712_multi_capture_analog_switch __devinit
.private_value = 10,
};
-static struct snd_kcontrol_new snd_ice1712_multi_capture_spdif_switch __devinitdata = {
+static const struct snd_kcontrol_new snd_ice1712_multi_capture_spdif_switch __devinitdata = {
.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
.name = SNDRV_CTL_NAME_IEC958("Multi ",CAPTURE,SWITCH),
.info = snd_ice1712_pro_mixer_switch_info,
@@ -1423,7 +1423,7 @@ static struct snd_kcontrol_new snd_ice1712_multi_capture_spdif_switch __devinitd
.count = 2,
};
-static struct snd_kcontrol_new snd_ice1712_multi_capture_analog_volume __devinitdata = {
+static const struct snd_kcontrol_new snd_ice1712_multi_capture_analog_volume __devinitdata = {
.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
.access = (SNDRV_CTL_ELEM_ACCESS_READWRITE |
SNDRV_CTL_ELEM_ACCESS_TLV_READ),
@@ -1435,7 +1435,7 @@ static struct snd_kcontrol_new snd_ice1712_multi_capture_analog_volume __devinit
.tlv = { .p = db_scale_playback }
};
-static struct snd_kcontrol_new snd_ice1712_multi_capture_spdif_volume __devinitdata = {
+static const struct snd_kcontrol_new snd_ice1712_multi_capture_spdif_volume __devinitdata = {
.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
.name = SNDRV_CTL_NAME_IEC958("Multi ",CAPTURE,VOLUME),
.info = snd_ice1712_pro_mixer_volume_info,
@@ -1627,7 +1627,7 @@ static int snd_ice1712_eeprom_get(struct snd_kcontrol *kcontrol,
return 0;
}
-static struct snd_kcontrol_new snd_ice1712_eeprom __devinitdata = {
+static const struct snd_kcontrol_new snd_ice1712_eeprom __devinitdata = {
.iface = SNDRV_CTL_ELEM_IFACE_CARD,
.name = "ICE1712 EEPROM",
.access = SNDRV_CTL_ELEM_ACCESS_READ,
@@ -1663,7 +1663,7 @@ static int snd_ice1712_spdif_default_put(struct snd_kcontrol *kcontrol,
return 0;
}
-static struct snd_kcontrol_new snd_ice1712_spdif_default __devinitdata =
+static const struct snd_kcontrol_new snd_ice1712_spdif_default __devinitdata =
{
.iface = SNDRV_CTL_ELEM_IFACE_PCM,
.name = SNDRV_CTL_NAME_IEC958("",PLAYBACK,DEFAULT),
@@ -1714,7 +1714,7 @@ static int snd_ice1712_spdif_maskp_get(struct snd_kcontrol *kcontrol,
return 0;
}
-static struct snd_kcontrol_new snd_ice1712_spdif_maskc __devinitdata =
+static const struct snd_kcontrol_new snd_ice1712_spdif_maskc __devinitdata =
{
.access = SNDRV_CTL_ELEM_ACCESS_READ,
.iface = SNDRV_CTL_ELEM_IFACE_PCM,
@@ -1723,7 +1723,7 @@ static struct snd_kcontrol_new snd_ice1712_spdif_maskc __devinitdata =
.get = snd_ice1712_spdif_maskc_get,
};
-static struct snd_kcontrol_new snd_ice1712_spdif_maskp __devinitdata =
+static const struct snd_kcontrol_new snd_ice1712_spdif_maskp __devinitdata =
{
.access = SNDRV_CTL_ELEM_ACCESS_READ,
.iface = SNDRV_CTL_ELEM_IFACE_PCM,
@@ -1750,7 +1750,7 @@ static int snd_ice1712_spdif_stream_put(struct snd_kcontrol *kcontrol,
return 0;
}
-static struct snd_kcontrol_new snd_ice1712_spdif_stream __devinitdata =
+static const struct snd_kcontrol_new snd_ice1712_spdif_stream __devinitdata =
{
.access = (SNDRV_CTL_ELEM_ACCESS_READWRITE |
SNDRV_CTL_ELEM_ACCESS_INACTIVE),
@@ -1811,7 +1811,7 @@ int snd_ice1712_gpio_put(struct snd_kcontrol *kcontrol,
static int snd_ice1712_pro_internal_clock_info(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_info *uinfo)
{
- static char *texts[] = {
+ static const char * const texts[] = {
"8000", /* 0: 6 */
"9600", /* 1: 3 */
"11025", /* 2: 10 */
@@ -1840,7 +1840,7 @@ static int snd_ice1712_pro_internal_clock_get(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
struct snd_ice1712 *ice = snd_kcontrol_chip(kcontrol);
- static unsigned char xlate[16] = {
+ static const unsigned char xlate[16] = {
9, 6, 3, 1, 7, 4, 0, 12, 8, 5, 2, 11, 255, 255, 255, 10
};
unsigned char val;
@@ -1864,7 +1864,7 @@ static int snd_ice1712_pro_internal_clock_put(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
struct snd_ice1712 *ice = snd_kcontrol_chip(kcontrol);
- static unsigned int xrate[13] = {
+ static const unsigned int xrate[13] = {
8000, 9600, 11025, 12000, 16000, 22050, 24000,
32000, 44100, 48000, 64000, 88200, 96000
};
@@ -1891,7 +1891,7 @@ static int snd_ice1712_pro_internal_clock_put(struct snd_kcontrol *kcontrol,
return change;
}
-static struct snd_kcontrol_new snd_ice1712_pro_internal_clock __devinitdata = {
+static const struct snd_kcontrol_new snd_ice1712_pro_internal_clock __devinitdata = {
.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
.name = "Multi Track Internal Clock",
.info = snd_ice1712_pro_internal_clock_info,
@@ -1902,7 +1902,7 @@ static struct snd_kcontrol_new snd_ice1712_pro_internal_clock __devinitdata = {
static int snd_ice1712_pro_internal_clock_default_info(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_info *uinfo)
{
- static char *texts[] = {
+ static const char * const texts[] = {
"8000", /* 0: 6 */
"9600", /* 1: 3 */
"11025", /* 2: 10 */
@@ -1931,7 +1931,7 @@ static int snd_ice1712_pro_internal_clock_default_get(struct snd_kcontrol *kcont
struct snd_ctl_elem_value *ucontrol)
{
int val;
- static unsigned int xrate[13] = {
+ static const unsigned int xrate[13] = {
8000, 9600, 11025, 12000, 16000, 22050, 24000,
32000, 44100, 48000, 64000, 88200, 96000
};
@@ -1948,7 +1948,7 @@ static int snd_ice1712_pro_internal_clock_default_get(struct snd_kcontrol *kcont
static int snd_ice1712_pro_internal_clock_default_put(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
- static unsigned int xrate[13] = {
+ static const unsigned int xrate[13] = {
8000, 9600, 11025, 12000, 16000, 22050, 24000,
32000, 44100, 48000, 64000, 88200, 96000
};
@@ -1962,7 +1962,7 @@ static int snd_ice1712_pro_internal_clock_default_put(struct snd_kcontrol *kcont
return change;
}
-static struct snd_kcontrol_new snd_ice1712_pro_internal_clock_default __devinitdata = {
+static const struct snd_kcontrol_new snd_ice1712_pro_internal_clock_default __devinitdata = {
.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
.name = "Multi Track Internal Clock Default",
.info = snd_ice1712_pro_internal_clock_default_info,
@@ -2001,7 +2001,7 @@ static int snd_ice1712_pro_rate_locking_put(struct snd_kcontrol *kcontrol,
return change;
}
-static struct snd_kcontrol_new snd_ice1712_pro_rate_locking __devinitdata = {
+static const struct snd_kcontrol_new snd_ice1712_pro_rate_locking __devinitdata = {
.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
.name = "Multi Track Rate Locking",
.info = snd_ice1712_pro_rate_locking_info,
@@ -2040,7 +2040,7 @@ static int snd_ice1712_pro_rate_reset_put(struct snd_kcontrol *kcontrol,
return change;
}
-static struct snd_kcontrol_new snd_ice1712_pro_rate_reset __devinitdata = {
+static const struct snd_kcontrol_new snd_ice1712_pro_rate_reset __devinitdata = {
.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
.name = "Multi Track Rate Reset",
.info = snd_ice1712_pro_rate_reset_info,
@@ -2054,7 +2054,7 @@ static struct snd_kcontrol_new snd_ice1712_pro_rate_reset __devinitdata = {
static int snd_ice1712_pro_route_info(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_info *uinfo)
{
- static char *texts[] = {
+ static const char * const texts[] = {
"PCM Out", /* 0 */
"H/W In 0", "H/W In 1", "H/W In 2", "H/W In 3", /* 1-4 */
"H/W In 4", "H/W In 5", "H/W In 6", "H/W In 7", /* 5-8 */
@@ -2207,7 +2207,7 @@ static int snd_ice1712_pro_route_spdif_put(struct snd_kcontrol *kcontrol,
return change;
}
-static struct snd_kcontrol_new snd_ice1712_mixer_pro_analog_route __devinitdata = {
+static const struct snd_kcontrol_new snd_ice1712_mixer_pro_analog_route __devinitdata = {
.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
.name = "H/W Playback Route",
.info = snd_ice1712_pro_route_info,
@@ -2215,7 +2215,7 @@ static struct snd_kcontrol_new snd_ice1712_mixer_pro_analog_route __devinitdata
.put = snd_ice1712_pro_route_analog_put,
};
-static struct snd_kcontrol_new snd_ice1712_mixer_pro_spdif_route __devinitdata = {
+static const struct snd_kcontrol_new snd_ice1712_mixer_pro_spdif_route __devinitdata = {
.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
.name = SNDRV_CTL_NAME_IEC958("",PLAYBACK,NONE) "Route",
.info = snd_ice1712_pro_route_info,
@@ -2257,7 +2257,7 @@ static int snd_ice1712_pro_volume_rate_put(struct snd_kcontrol *kcontrol,
return change;
}
-static struct snd_kcontrol_new snd_ice1712_mixer_pro_volume_rate __devinitdata = {
+static const struct snd_kcontrol_new snd_ice1712_mixer_pro_volume_rate __devinitdata = {
.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
.name = "Multi Track Volume Rate",
.info = snd_ice1712_pro_volume_rate_info,
@@ -2290,7 +2290,7 @@ static int snd_ice1712_pro_peak_get(struct snd_kcontrol *kcontrol,
return 0;
}
-static struct snd_kcontrol_new snd_ice1712_mixer_pro_peak __devinitdata = {
+static const struct snd_kcontrol_new snd_ice1712_mixer_pro_peak __devinitdata = {
.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
.name = "Multi Track Peak",
.access = SNDRV_CTL_ELEM_ACCESS_READ | SNDRV_CTL_ELEM_ACCESS_VOLATILE,
@@ -2305,7 +2305,7 @@ static struct snd_kcontrol_new snd_ice1712_mixer_pro_peak __devinitdata = {
/*
* list of available boards
*/
-static struct snd_ice1712_card_info *card_tables[] __devinitdata = {
+static const struct snd_ice1712_card_info *card_tables[] __devinitdata = {
snd_ice1712_hoontech_cards,
snd_ice1712_delta_cards,
snd_ice1712_ews_cards,
@@ -2329,7 +2329,7 @@ static int __devinit snd_ice1712_read_eeprom(struct snd_ice1712 *ice,
{
int dev = 0xa0; /* EEPROM device address */
unsigned int i, size;
- struct snd_ice1712_card_info **tbl, *c;
+ const struct snd_ice1712_card_info **tbl, *c;
if (! modelname || ! *modelname) {
ice->eeprom.subvendor = 0;
@@ -2658,7 +2658,7 @@ static int __devinit snd_ice1712_create(struct snd_card *card,
*
*/
-static struct snd_ice1712_card_info no_matched __devinitdata;
+static const struct snd_ice1712_card_info no_matched __devinitdata;
static int __devinit snd_ice1712_probe(struct pci_dev *pci,
const struct pci_device_id *pci_id)
@@ -2667,7 +2667,7 @@ static int __devinit snd_ice1712_probe(struct pci_dev *pci,
struct snd_card *card;
struct snd_ice1712 *ice;
int pcm_dev = 0, err;
- struct snd_ice1712_card_info **tbl, *c;
+ const struct snd_ice1712_card_info **tbl, *c;
if (dev >= SNDRV_CARDS)
return -ENODEV;
diff --git a/sound/pci/ice1712/ice1712.h b/sound/pci/ice1712/ice1712.h
index ce27eac40d4e..c3d9feaaf57d 100644
--- a/sound/pci/ice1712/ice1712.h
+++ b/sound/pci/ice1712/ice1712.h
@@ -28,6 +28,7 @@
#include <sound/i2c.h>
#include <sound/ak4xxx-adda.h>
#include <sound/ak4114.h>
+#include <sound/pt2258.h>
#include <sound/pcm.h>
#include <sound/mpu401.h>
@@ -381,6 +382,11 @@ struct snd_ice1712 {
unsigned short master[2];
unsigned short vol[8];
} phase28;
+ /* a non-standard I2C device for revo51 */
+ struct revo51_spec {
+ struct snd_i2c_device *dev;
+ struct snd_pt2258 *pt2258;
+ } revo51;
/* Hoontech-specific setting */
struct hoontech_spec {
unsigned char boxbits[4];
@@ -462,6 +468,14 @@ static inline void snd_ice1712_gpio_write_bits(struct snd_ice1712 *ice,
snd_ice1712_gpio_write(ice, mask & bits);
}
+static inline int snd_ice1712_gpio_read_bits(struct snd_ice1712 *ice,
+ unsigned int mask)
+{
+ ice->gpio.direction &= ~mask;
+ snd_ice1712_gpio_set_dir(ice, ice->gpio.direction);
+ return (snd_ice1712_gpio_read(ice) & mask);
+}
+
int snd_ice1712_spdif_build_controls(struct snd_ice1712 *ice);
int snd_ice1712_akm4xxx_init(struct snd_akm4xxx *ak, const struct snd_akm4xxx *template,
@@ -500,8 +514,8 @@ struct snd_ice1712_card_info {
unsigned int mpu401_2_info_flags;
const char *mpu401_1_name;
const char *mpu401_2_name;
- unsigned int eeprom_size;
- unsigned char *eeprom_data;
+ const unsigned int eeprom_size;
+ const unsigned char *eeprom_data;
};
diff --git a/sound/pci/ice1712/ice1724.c b/sound/pci/ice1712/ice1724.c
index 3e3a102e6c34..1127ebdf5fec 100644
--- a/sound/pci/ice1712/ice1724.c
+++ b/sound/pci/ice1712/ice1724.c
@@ -50,7 +50,7 @@
#include "prodigy192.h"
#include "juli.h"
#include "phase.h"
-
+#include "wtm.h"
MODULE_AUTHOR("Jaroslav Kysela <perex@suse.cz>");
MODULE_DESCRIPTION("VIA ICEnsemble ICE1724/1720 (Envy24HT/PT)");
@@ -64,6 +64,7 @@ MODULE_SUPPORTED_DEVICE("{"
PRODIGY192_DEVICE_DESC
JULI_DEVICE_DESC
PHASE_DEVICE_DESC
+ WTM_DEVICE_DESC
"{VIA,VT1720},"
"{VIA,VT1724},"
"{ICEnsemble,Generic ICE1724},"
@@ -86,7 +87,7 @@ MODULE_PARM_DESC(model, "Use the given board model.");
/* Both VT1720 and VT1724 have the same PCI IDs */
-static struct pci_device_id snd_vt1724_ids[] = {
+static const struct pci_device_id snd_vt1724_ids[] = {
{ PCI_VENDOR_ID_ICE, PCI_DEVICE_ID_VT1724, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0 },
{ 0, }
};
@@ -341,7 +342,7 @@ static int snd_vt1724_pcm_trigger(struct snd_pcm_substream *substream, int cmd)
what = 0;
snd_pcm_group_for_each(pos, substream) {
- struct vt1724_pcm_reg *reg;
+ const struct vt1724_pcm_reg *reg;
s = snd_pcm_group_substream_entry(pos);
reg = s->runtime->private_data;
what |= reg->start;
@@ -605,7 +606,7 @@ static snd_pcm_uframes_t snd_vt1724_playback_pro_pointer(struct snd_pcm_substrea
static int snd_vt1724_pcm_prepare(struct snd_pcm_substream *substream)
{
struct snd_ice1712 *ice = snd_pcm_substream_chip(substream);
- struct vt1724_pcm_reg *reg = substream->runtime->private_data;
+ const struct vt1724_pcm_reg *reg = substream->runtime->private_data;
spin_lock_irq(&ice->reg_lock);
outl(substream->runtime->dma_addr, ice->profi_port + reg->addr);
@@ -620,7 +621,7 @@ static int snd_vt1724_pcm_prepare(struct snd_pcm_substream *substream)
static snd_pcm_uframes_t snd_vt1724_pcm_pointer(struct snd_pcm_substream *substream)
{
struct snd_ice1712 *ice = snd_pcm_substream_chip(substream);
- struct vt1724_pcm_reg *reg = substream->runtime->private_data;
+ const struct vt1724_pcm_reg *reg = substream->runtime->private_data;
size_t ptr;
if (!(inl(ICEMT1724(ice, DMA_CONTROL)) & reg->start))
@@ -646,21 +647,21 @@ static snd_pcm_uframes_t snd_vt1724_pcm_pointer(struct snd_pcm_substream *substr
#endif
}
-static struct vt1724_pcm_reg vt1724_playback_pro_reg = {
+static const struct vt1724_pcm_reg vt1724_playback_pro_reg = {
.addr = VT1724_MT_PLAYBACK_ADDR,
.size = VT1724_MT_PLAYBACK_SIZE,
.count = VT1724_MT_PLAYBACK_COUNT,
.start = VT1724_PDMA0_START,
};
-static struct vt1724_pcm_reg vt1724_capture_pro_reg = {
+static const struct vt1724_pcm_reg vt1724_capture_pro_reg = {
.addr = VT1724_MT_CAPTURE_ADDR,
.size = VT1724_MT_CAPTURE_SIZE,
.count = VT1724_MT_CAPTURE_COUNT,
.start = VT1724_RDMA0_START,
};
-static struct snd_pcm_hardware snd_vt1724_playback_pro =
+static const struct snd_pcm_hardware snd_vt1724_playback_pro =
{
.info = (SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_INTERLEAVED |
SNDRV_PCM_INFO_BLOCK_TRANSFER |
@@ -679,7 +680,7 @@ static struct snd_pcm_hardware snd_vt1724_playback_pro =
.periods_max = 1024,
};
-static struct snd_pcm_hardware snd_vt1724_spdif =
+static const struct snd_pcm_hardware snd_vt1724_spdif =
{
.info = (SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_INTERLEAVED |
SNDRV_PCM_INFO_BLOCK_TRANSFER |
@@ -701,7 +702,7 @@ static struct snd_pcm_hardware snd_vt1724_spdif =
.periods_max = 1024,
};
-static struct snd_pcm_hardware snd_vt1724_2ch_stereo =
+static const struct snd_pcm_hardware snd_vt1724_2ch_stereo =
{
.info = (SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_INTERLEAVED |
SNDRV_PCM_INFO_BLOCK_TRANSFER |
@@ -773,7 +774,7 @@ static int snd_vt1724_playback_pro_open(struct snd_pcm_substream *substream)
struct snd_ice1712 *ice = snd_pcm_substream_chip(substream);
int chs;
- runtime->private_data = &vt1724_playback_pro_reg;
+ runtime->private_data = (void *)&vt1724_playback_pro_reg;
ice->playback_pro_substream = substream;
runtime->hw = snd_vt1724_playback_pro;
snd_pcm_set_sync(substream);
@@ -802,7 +803,7 @@ static int snd_vt1724_capture_pro_open(struct snd_pcm_substream *substream)
struct snd_ice1712 *ice = snd_pcm_substream_chip(substream);
struct snd_pcm_runtime *runtime = substream->runtime;
- runtime->private_data = &vt1724_capture_pro_reg;
+ runtime->private_data = (void *)&vt1724_capture_pro_reg;
ice->capture_pro_substream = substream;
runtime->hw = snd_vt1724_2ch_stereo;
snd_pcm_set_sync(substream);
@@ -888,14 +889,14 @@ static int __devinit snd_vt1724_pcm_profi(struct snd_ice1712 * ice, int device)
* SPDIF PCM
*/
-static struct vt1724_pcm_reg vt1724_playback_spdif_reg = {
+static const struct vt1724_pcm_reg vt1724_playback_spdif_reg = {
.addr = VT1724_MT_PDMA4_ADDR,
.size = VT1724_MT_PDMA4_SIZE,
.count = VT1724_MT_PDMA4_COUNT,
.start = VT1724_PDMA4_START,
};
-static struct vt1724_pcm_reg vt1724_capture_spdif_reg = {
+static const struct vt1724_pcm_reg vt1724_capture_spdif_reg = {
.addr = VT1724_MT_RDMA1_ADDR,
.size = VT1724_MT_RDMA1_SIZE,
.count = VT1724_MT_RDMA1_COUNT,
@@ -953,7 +954,7 @@ static int snd_vt1724_playback_spdif_open(struct snd_pcm_substream *substream)
struct snd_ice1712 *ice = snd_pcm_substream_chip(substream);
struct snd_pcm_runtime *runtime = substream->runtime;
- runtime->private_data = &vt1724_playback_spdif_reg;
+ runtime->private_data = (void *)&vt1724_playback_spdif_reg;
ice->playback_con_substream = substream;
if (ice->force_pdma4) {
runtime->hw = snd_vt1724_2ch_stereo;
@@ -985,7 +986,7 @@ static int snd_vt1724_capture_spdif_open(struct snd_pcm_substream *substream)
struct snd_ice1712 *ice = snd_pcm_substream_chip(substream);
struct snd_pcm_runtime *runtime = substream->runtime;
- runtime->private_data = &vt1724_capture_spdif_reg;
+ runtime->private_data = (void *)&vt1724_capture_spdif_reg;
ice->capture_con_substream = substream;
if (ice->force_rdma1) {
runtime->hw = snd_vt1724_2ch_stereo;
@@ -1090,7 +1091,7 @@ static int __devinit snd_vt1724_pcm_spdif(struct snd_ice1712 * ice, int device)
* independent surround PCMs
*/
-static struct vt1724_pcm_reg vt1724_playback_dma_regs[3] = {
+static const struct vt1724_pcm_reg vt1724_playback_dma_regs[3] = {
{
.addr = VT1724_MT_PDMA1_ADDR,
.size = VT1724_MT_PDMA1_SIZE,
@@ -1136,7 +1137,7 @@ static int snd_vt1724_playback_indep_open(struct snd_pcm_substream *substream)
return -EBUSY; /* FIXME: should handle blocking mode properly */
}
mutex_unlock(&ice->open_mutex);
- runtime->private_data = &vt1724_playback_dma_regs[substream->number];
+ runtime->private_data = (void *)&vt1724_playback_dma_regs[substream->number];
ice->playback_con_substream_ds[substream->number] = substream;
runtime->hw = snd_vt1724_2ch_stereo;
snd_pcm_set_sync(substream);
@@ -1317,7 +1318,7 @@ static int snd_vt1724_eeprom_get(struct snd_kcontrol *kcontrol,
return 0;
}
-static struct snd_kcontrol_new snd_vt1724_eeprom __devinitdata = {
+static const struct snd_kcontrol_new snd_vt1724_eeprom __devinitdata = {
.iface = SNDRV_CTL_ELEM_IFACE_CARD,
.name = "ICE1724 EEPROM",
.access = SNDRV_CTL_ELEM_ACCESS_READ,
@@ -1430,7 +1431,7 @@ static int snd_vt1724_spdif_default_put(struct snd_kcontrol *kcontrol,
return (val != old);
}
-static struct snd_kcontrol_new snd_vt1724_spdif_default __devinitdata =
+static const struct snd_kcontrol_new snd_vt1724_spdif_default __devinitdata =
{
.iface = SNDRV_CTL_ELEM_IFACE_PCM,
.name = SNDRV_CTL_NAME_IEC958("",PLAYBACK,DEFAULT),
@@ -1462,7 +1463,7 @@ static int snd_vt1724_spdif_maskp_get(struct snd_kcontrol *kcontrol,
return 0;
}
-static struct snd_kcontrol_new snd_vt1724_spdif_maskc __devinitdata =
+static const struct snd_kcontrol_new snd_vt1724_spdif_maskc __devinitdata =
{
.access = SNDRV_CTL_ELEM_ACCESS_READ,
.iface = SNDRV_CTL_ELEM_IFACE_PCM,
@@ -1471,7 +1472,7 @@ static struct snd_kcontrol_new snd_vt1724_spdif_maskc __devinitdata =
.get = snd_vt1724_spdif_maskc_get,
};
-static struct snd_kcontrol_new snd_vt1724_spdif_maskp __devinitdata =
+static const struct snd_kcontrol_new snd_vt1724_spdif_maskp __devinitdata =
{
.access = SNDRV_CTL_ELEM_ACCESS_READ,
.iface = SNDRV_CTL_ELEM_IFACE_PCM,
@@ -1516,7 +1517,7 @@ static int snd_vt1724_spdif_sw_put(struct snd_kcontrol *kcontrol,
return old != val;
}
-static struct snd_kcontrol_new snd_vt1724_spdif_switch __devinitdata =
+static const struct snd_kcontrol_new snd_vt1724_spdif_switch __devinitdata =
{
.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
/* FIXME: the following conflict with IEC958 Playback Route */
@@ -1584,7 +1585,7 @@ int snd_ice1712_gpio_put(struct snd_kcontrol *kcontrol,
static int snd_vt1724_pro_internal_clock_info(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_info *uinfo)
{
- static char *texts_1724[] = {
+ static const char * const texts_1724[] = {
"8000", /* 0: 6 */
"9600", /* 1: 3 */
"11025", /* 2: 10 */
@@ -1602,7 +1603,7 @@ static int snd_vt1724_pro_internal_clock_info(struct snd_kcontrol *kcontrol,
"192000", /* 14: 14 */
"IEC958 Input", /* 15: -- */
};
- static char *texts_1720[] = {
+ static const char * const texts_1720[] = {
"8000", /* 0: 6 */
"9600", /* 1: 3 */
"11025", /* 2: 10 */
@@ -1635,7 +1636,7 @@ static int snd_vt1724_pro_internal_clock_get(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
struct snd_ice1712 *ice = snd_kcontrol_chip(kcontrol);
- static unsigned char xlate[16] = {
+ static const unsigned char xlate[16] = {
9, 6, 3, 1, 7, 4, 0, 12, 8, 5, 2, 11, 13, 255, 14, 10
};
unsigned char val;
@@ -1694,7 +1695,7 @@ static int snd_vt1724_pro_internal_clock_put(struct snd_kcontrol *kcontrol,
return change;
}
-static struct snd_kcontrol_new snd_vt1724_pro_internal_clock __devinitdata = {
+static const struct snd_kcontrol_new snd_vt1724_pro_internal_clock __devinitdata = {
.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
.name = "Multi Track Internal Clock",
.info = snd_vt1724_pro_internal_clock_info,
@@ -1733,7 +1734,7 @@ static int snd_vt1724_pro_rate_locking_put(struct snd_kcontrol *kcontrol,
return change;
}
-static struct snd_kcontrol_new snd_vt1724_pro_rate_locking __devinitdata = {
+static const struct snd_kcontrol_new snd_vt1724_pro_rate_locking __devinitdata = {
.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
.name = "Multi Track Rate Locking",
.info = snd_vt1724_pro_rate_locking_info,
@@ -1772,7 +1773,7 @@ static int snd_vt1724_pro_rate_reset_put(struct snd_kcontrol *kcontrol,
return change;
}
-static struct snd_kcontrol_new snd_vt1724_pro_rate_reset __devinitdata = {
+static const struct snd_kcontrol_new snd_vt1724_pro_rate_reset __devinitdata = {
.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
.name = "Multi Track Rate Reset",
.info = snd_vt1724_pro_rate_reset_info,
@@ -1816,7 +1817,7 @@ static int get_route_val(struct snd_ice1712 *ice, int shift)
{
unsigned long val;
unsigned char eitem;
- static unsigned char xlate[8] = {
+ static const unsigned char xlate[8] = {
0, 255, 1, 2, 255, 255, 3, 4,
};
@@ -1835,7 +1836,7 @@ static int put_route_val(struct snd_ice1712 *ice, unsigned int val, int shift)
{
unsigned int old_val, nval;
int change;
- static unsigned char xroute[8] = {
+ static const unsigned char xroute[8] = {
0, /* PCM */
2, /* PSDIN0 Left */
3, /* PSDIN0 Right */
@@ -1891,7 +1892,7 @@ static int snd_vt1724_pro_route_spdif_put(struct snd_kcontrol *kcontrol,
digital_route_shift(idx));
}
-static struct snd_kcontrol_new snd_vt1724_mixer_pro_analog_route __devinitdata = {
+static const struct snd_kcontrol_new snd_vt1724_mixer_pro_analog_route __devinitdata = {
.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
.name = "H/W Playback Route",
.info = snd_vt1724_pro_route_info,
@@ -1899,7 +1900,7 @@ static struct snd_kcontrol_new snd_vt1724_mixer_pro_analog_route __devinitdata =
.put = snd_vt1724_pro_route_analog_put,
};
-static struct snd_kcontrol_new snd_vt1724_mixer_pro_spdif_route __devinitdata = {
+static const struct snd_kcontrol_new snd_vt1724_mixer_pro_spdif_route __devinitdata = {
.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
.name = SNDRV_CTL_NAME_IEC958("",PLAYBACK,NONE) "Route",
.info = snd_vt1724_pro_route_info,
@@ -1935,7 +1936,7 @@ static int snd_vt1724_pro_peak_get(struct snd_kcontrol *kcontrol,
return 0;
}
-static struct snd_kcontrol_new snd_vt1724_mixer_pro_peak __devinitdata = {
+static const struct snd_kcontrol_new snd_vt1724_mixer_pro_peak __devinitdata = {
.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
.name = "Multi Track Peak",
.access = SNDRV_CTL_ELEM_ACCESS_READ | SNDRV_CTL_ELEM_ACCESS_VOLATILE,
@@ -1947,9 +1948,9 @@ static struct snd_kcontrol_new snd_vt1724_mixer_pro_peak __devinitdata = {
*
*/
-static struct snd_ice1712_card_info no_matched __devinitdata;
+static const struct snd_ice1712_card_info no_matched __devinitdata;
-static struct snd_ice1712_card_info *card_tables[] __devinitdata = {
+static const struct snd_ice1712_card_info *card_tables[] __devinitdata = {
snd_vt1724_revo_cards,
snd_vt1724_amp_cards,
snd_vt1724_aureon_cards,
@@ -1958,6 +1959,7 @@ static struct snd_ice1712_card_info *card_tables[] __devinitdata = {
snd_vt1724_prodigy192_cards,
snd_vt1724_juli_cards,
snd_vt1724_phase_cards,
+ snd_vt1724_wtm_cards,
NULL,
};
@@ -2007,7 +2009,7 @@ static int __devinit snd_vt1724_read_eeprom(struct snd_ice1712 *ice,
{
const int dev = 0xa0; /* EEPROM device address */
unsigned int i, size;
- struct snd_ice1712_card_info **tbl, *c;
+ const struct snd_ice1712_card_info **tbl, *c;
if (! modelname || ! *modelname) {
ice->eeprom.subvendor = 0;
@@ -2306,7 +2308,7 @@ static int __devinit snd_vt1724_probe(struct pci_dev *pci,
struct snd_card *card;
struct snd_ice1712 *ice;
int pcm_dev = 0, err;
- struct snd_ice1712_card_info **tbl, *c;
+ const struct snd_ice1712_card_info **tbl, *c;
if (dev >= SNDRV_CARDS)
return -ENODEV;
diff --git a/sound/pci/ice1712/juli.c b/sound/pci/ice1712/juli.c
index 5176b41ea9d3..d88172fa95da 100644
--- a/sound/pci/ice1712/juli.c
+++ b/sound/pci/ice1712/juli.c
@@ -125,7 +125,7 @@ static void juli_akm_set_rate_val(struct snd_akm4xxx *ak, unsigned int rate)
snd_akm4xxx_reset(ak, 0);
}
-static struct snd_akm4xxx akm_juli_dac __devinitdata = {
+static const struct snd_akm4xxx akm_juli_dac __devinitdata = {
.type = SND_AK4358,
.num_dacs = 2,
.ops = {
@@ -146,7 +146,7 @@ static int __devinit juli_add_controls(struct snd_ice1712 *ice)
*/
static int __devinit juli_init(struct snd_ice1712 *ice)
{
- static unsigned char ak4114_init_vals[] = {
+ static const unsigned char ak4114_init_vals[] = {
/* AK4117_REG_PWRDN */ AK4114_RST | AK4114_PWN | AK4114_OCKS0 | AK4114_OCKS1,
/* AK4114_REQ_FORMAT */ AK4114_DIF_I24I2S,
/* AK4114_REG_IO0 */ AK4114_TX1E,
@@ -154,7 +154,7 @@ static int __devinit juli_init(struct snd_ice1712 *ice)
/* AK4114_REG_INT0_MASK */ 0,
/* AK4114_REG_INT1_MASK */ 0
};
- static unsigned char ak4114_init_txcsb[] = {
+ static const unsigned char ak4114_init_txcsb[] = {
0x41, 0x02, 0x2c, 0x00, 0x00
};
int err;
@@ -206,24 +206,24 @@ static int __devinit juli_init(struct snd_ice1712 *ice)
* hence the driver needs to sets up it properly.
*/
-static unsigned char juli_eeprom[] __devinitdata = {
- 0x20, /* SYSCONF: clock 512, mpu401, 1xADC, 1xDACs */
- 0x80, /* ACLINK: I2S */
- 0xf8, /* I2S: vol, 96k, 24bit, 192k */
- 0xc3, /* SPDIF: out-en, out-int, spdif-in */
- 0x9f, /* GPIO_DIR */
- 0xff, /* GPIO_DIR1 */
- 0x7f, /* GPIO_DIR2 */
- 0x9f, /* GPIO_MASK */
- 0xff, /* GPIO_MASK1 */
- 0x7f, /* GPIO_MASK2 */
- 0x16, /* GPIO_STATE: internal clock, multiple 1x, 48kHz */
- 0x80, /* GPIO_STATE1: mute */
- 0x00, /* GPIO_STATE2 */
+static const unsigned char juli_eeprom[] __devinitdata = {
+ [ICE_EEP2_SYSCONF] = 0x20, /* clock 512, mpu401, 1xADC, 1xDACs */
+ [ICE_EEP2_ACLINK] = 0x80, /* I2S */
+ [ICE_EEP2_I2S] = 0xf8, /* vol, 96k, 24bit, 192k */
+ [ICE_EEP2_SPDIF] = 0xc3, /* out-en, out-int, spdif-in */
+ [ICE_EEP2_GPIO_DIR] = 0x9f,
+ [ICE_EEP2_GPIO_DIR1] = 0xff,
+ [ICE_EEP2_GPIO_DIR2] = 0x7f,
+ [ICE_EEP2_GPIO_MASK] = 0x9f,
+ [ICE_EEP2_GPIO_MASK1] = 0xff,
+ [ICE_EEP2_GPIO_MASK2] = 0x7f,
+ [ICE_EEP2_GPIO_STATE] = 0x16, /* internal clock, multiple 1x, 48kHz */
+ [ICE_EEP2_GPIO_STATE1] = 0x80, /* mute */
+ [ICE_EEP2_GPIO_STATE2] = 0x00,
};
/* entry point */
-struct snd_ice1712_card_info snd_vt1724_juli_cards[] __devinitdata = {
+const struct snd_ice1712_card_info snd_vt1724_juli_cards[] __devinitdata = {
{
.subvendor = VT1724_SUBDEVICE_JULI,
.name = "ESI Juli@",
diff --git a/sound/pci/ice1712/juli.h b/sound/pci/ice1712/juli.h
index d9f8534fd92e..1b9294f8bce3 100644
--- a/sound/pci/ice1712/juli.h
+++ b/sound/pci/ice1712/juli.h
@@ -5,6 +5,6 @@
#define VT1724_SUBDEVICE_JULI 0x31305345 /* Juli@ */
-extern struct snd_ice1712_card_info snd_vt1724_juli_cards[];
+extern const struct snd_ice1712_card_info snd_vt1724_juli_cards[];
#endif /* __SOUND_JULI_H */
diff --git a/sound/pci/ice1712/phase.c b/sound/pci/ice1712/phase.c
index e08d73f4ff85..0751718f4d7b 100644
--- a/sound/pci/ice1712/phase.c
+++ b/sound/pci/ice1712/phase.c
@@ -71,7 +71,7 @@
* Logarithmic volume values for WM8770
* Computed as 20 * Log10(255 / x)
*/
-static unsigned char wm_vol[256] = {
+static const unsigned char wm_vol[256] = {
127, 48, 42, 39, 36, 34, 33, 31, 30, 29, 28, 27, 27, 26, 25, 25, 24, 24, 23,
23, 22, 22, 21, 21, 21, 20, 20, 20, 19, 19, 19, 18, 18, 18, 18, 17, 17, 17,
17, 16, 16, 16, 16, 15, 15, 15, 15, 15, 15, 14, 14, 14, 14, 14, 13, 13, 13,
@@ -89,13 +89,13 @@ static unsigned char wm_vol[256] = {
#define WM_VOL_MAX (sizeof(wm_vol) - 1)
#define WM_VOL_MUTE 0x8000
-static struct snd_akm4xxx akm_phase22 __devinitdata = {
+static const struct snd_akm4xxx akm_phase22 __devinitdata = {
.type = SND_AK4524,
.num_dacs = 2,
.num_adcs = 2,
};
-static struct snd_ak4xxx_private akm_phase22_priv __devinitdata = {
+static const struct snd_ak4xxx_private akm_phase22_priv __devinitdata = {
.caddr = 2,
.cif = 1,
.data_mask = 1 << 4,
@@ -152,36 +152,36 @@ static int __devinit phase22_add_controls(struct snd_ice1712 *ice)
return 0;
}
-static unsigned char phase22_eeprom[] __devinitdata = {
- 0x00, /* SYSCONF: 1xADC, 1xDACs */
- 0x80, /* ACLINK: I2S */
- 0xf8, /* I2S: vol, 96k, 24bit*/
- 0xc3, /* SPDIF: out-en, out-int, spdif-in */
- 0xFF, /* GPIO_DIR */
- 0xFF, /* GPIO_DIR1 */
- 0xFF, /* GPIO_DIR2 */
- 0x00, /* GPIO_MASK */
- 0x00, /* GPIO_MASK1 */
- 0x00, /* GPIO_MASK2 */
- 0x00, /* GPIO_STATE: */
- 0x00, /* GPIO_STATE1: */
- 0x00, /* GPIO_STATE2 */
+static const unsigned char phase22_eeprom[] __devinitdata = {
+ [ICE_EEP2_SYSCONF] = 0x00, /* 1xADC, 1xDACs */
+ [ICE_EEP2_ACLINK] = 0x80, /* I2S */
+ [ICE_EEP2_I2S] = 0xf8, /* vol, 96k, 24bit */
+ [ICE_EEP2_SPDIF] = 0xc3, /* out-en, out-int, spdif-in */
+ [ICE_EEP2_GPIO_DIR] = 0xff,
+ [ICE_EEP2_GPIO_DIR1] = 0xff,
+ [ICE_EEP2_GPIO_DIR2] = 0xff,
+ [ICE_EEP2_GPIO_MASK] = 0x00,
+ [ICE_EEP2_GPIO_MASK1] = 0x00,
+ [ICE_EEP2_GPIO_MASK2] = 0x00,
+ [ICE_EEP2_GPIO_STATE] = 0x00,
+ [ICE_EEP2_GPIO_STATE1] = 0x00,
+ [ICE_EEP2_GPIO_STATE2] = 0x00,
};
-static unsigned char phase28_eeprom[] __devinitdata = {
- 0x0b, /* SYSCONF: clock 512, spdif-in/ADC, 4DACs */
- 0x80, /* ACLINK: I2S */
- 0xfc, /* I2S: vol, 96k, 24bit, 192k */
- 0xc3, /* SPDIF: out-en, out-int, spdif-in */
- 0xff, /* GPIO_DIR */
- 0xff, /* GPIO_DIR1 */
- 0x5f, /* GPIO_DIR2 */
- 0x00, /* GPIO_MASK */
- 0x00, /* GPIO_MASK1 */
- 0x00, /* GPIO_MASK2 */
- 0x00, /* GPIO_STATE */
- 0x00, /* GPIO_STATE1 */
- 0x00, /* GPIO_STATE2 */
+static const unsigned char phase28_eeprom[] __devinitdata = {
+ [ICE_EEP2_SYSCONF] = 0x0b, /* clock 512, spdif-in/ADC, 4DACs */
+ [ICE_EEP2_ACLINK] = 0x80, /* I2S */
+ [ICE_EEP2_I2S] = 0xfc, /* vol, 96k, 24bit, 192k */
+ [ICE_EEP2_SPDIF] = 0xc3, /* out-en, out-int, spdif-in */
+ [ICE_EEP2_GPIO_DIR] = 0xff,
+ [ICE_EEP2_GPIO_DIR1] = 0xff,
+ [ICE_EEP2_GPIO_DIR2] = 0x5f,
+ [ICE_EEP2_GPIO_MASK] = 0x00,
+ [ICE_EEP2_GPIO_MASK1] = 0x00,
+ [ICE_EEP2_GPIO_MASK2] = 0x00,
+ [ICE_EEP2_GPIO_STATE] = 0x00,
+ [ICE_EEP2_GPIO_STATE1] = 0x00,
+ [ICE_EEP2_GPIO_STATE2] = 0x00,
};
/*
@@ -343,7 +343,7 @@ static int wm_master_vol_put(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_
static int __devinit phase28_init(struct snd_ice1712 *ice)
{
- static unsigned short wm_inits_phase28[] = {
+ static const unsigned short wm_inits_phase28[] = {
/* These come first to reduce init pop noise */
0x1b, 0x044, /* ADC Mux (AC'97 source) */
0x1c, 0x00B, /* Out Mux1 (VOUT1 = DAC+AUX, VOUT2 = DAC) */
@@ -382,7 +382,7 @@ static int __devinit phase28_init(struct snd_ice1712 *ice)
unsigned int tmp;
struct snd_akm4xxx *ak;
- unsigned short *p;
+ const unsigned short *p;
int i;
ice->num_total_dacs = 8;
@@ -697,10 +697,10 @@ static int phase28_oversampling_put(struct snd_kcontrol *kcontrol, struct snd_ct
return 0;
}
-static DECLARE_TLV_DB_SCALE(db_scale_wm_dac, -12700, 100, 1);
-static DECLARE_TLV_DB_SCALE(db_scale_wm_pcm, -6400, 50, 1);
+static const DECLARE_TLV_DB_SCALE(db_scale_wm_dac, -12700, 100, 1);
+static const DECLARE_TLV_DB_SCALE(db_scale_wm_pcm, -6400, 50, 1);
-static struct snd_kcontrol_new phase28_dac_controls[] __devinitdata = {
+static const struct snd_kcontrol_new phase28_dac_controls[] __devinitdata = {
{
.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
.name = "Master Playback Switch",
@@ -815,7 +815,7 @@ static struct snd_kcontrol_new phase28_dac_controls[] __devinitdata = {
}
};
-static struct snd_kcontrol_new wm_controls[] __devinitdata = {
+static const struct snd_kcontrol_new wm_controls[] __devinitdata = {
{
.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
.name = "PCM Playback Switch",
@@ -870,7 +870,7 @@ static int __devinit phase28_add_controls(struct snd_ice1712 *ice)
return 0;
}
-struct snd_ice1712_card_info snd_vt1724_phase_cards[] __devinitdata = {
+const struct snd_ice1712_card_info snd_vt1724_phase_cards[] __devinitdata = {
{
.subvendor = VT1724_SUBDEVICE_PHASE22,
.name = "Terratec PHASE 22",
diff --git a/sound/pci/ice1712/phase.h b/sound/pci/ice1712/phase.h
index 13e841b55488..ad379a99bf92 100644
--- a/sound/pci/ice1712/phase.h
+++ b/sound/pci/ice1712/phase.h
@@ -31,7 +31,7 @@
#define VT1724_SUBDEVICE_PHASE28 0x3b154911
/* entry point */
-extern struct snd_ice1712_card_info snd_vt1724_phase_cards[];
+extern const struct snd_ice1712_card_info snd_vt1724_phase_cards[];
/* PHASE28 GPIO bits */
#define PHASE28_SPI_MISO (1 << 21)
diff --git a/sound/pci/ice1712/pontis.c b/sound/pci/ice1712/pontis.c
index 6c74c2d2e7f3..9552497f0765 100644
--- a/sound/pci/ice1712/pontis.c
+++ b/sound/pci/ice1712/pontis.c
@@ -434,7 +434,7 @@ static unsigned int spi_read(struct snd_ice1712 *ice, unsigned int dev, unsigned
*/
static int cs_source_info(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *uinfo)
{
- static char *texts[] = {
+ static const char * const texts[] = {
"Coax", /* RXP0 */
"Optical", /* RXP1 */
"CD", /* RXP2 */
@@ -565,13 +565,13 @@ static int pontis_gpio_data_put(struct snd_kcontrol *kcontrol, struct snd_ctl_el
return changed;
}
-static DECLARE_TLV_DB_SCALE(db_scale_volume, -6400, 50, 1);
+static const DECLARE_TLV_DB_SCALE(db_scale_volume, -6400, 50, 1);
/*
* mixers
*/
-static struct snd_kcontrol_new pontis_controls[] __devinitdata = {
+static const struct snd_kcontrol_new pontis_controls[] __devinitdata = {
{
.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
.access = (SNDRV_CTL_ELEM_ACCESS_READWRITE |
@@ -741,7 +741,7 @@ static int __devinit pontis_add_controls(struct snd_ice1712 *ice)
*/
static int __devinit pontis_init(struct snd_ice1712 *ice)
{
- static unsigned short wm_inits[] = {
+ static const unsigned short wm_inits[] = {
/* These come first to reduce init pop noise */
WM_ADC_MUX, 0x00c0, /* ADC mute */
WM_DAC_MUTE, 0x0001, /* DAC softmute */
@@ -750,7 +750,7 @@ static int __devinit pontis_init(struct snd_ice1712 *ice)
WM_POWERDOWN, 0x0008, /* All power-up except HP */
WM_RESET, 0x0000, /* reset */
};
- static unsigned short wm_inits2[] = {
+ static const unsigned short wm_inits2[] = {
WM_MASTER_CTRL, 0x0022, /* 256fs, slave mode */
WM_DAC_INT, 0x0022, /* I2S, normal polarity, 24bit */
WM_ADC_INT, 0x0022, /* I2S, normal polarity, 24bit */
@@ -776,7 +776,7 @@ static int __devinit pontis_init(struct snd_ice1712 *ice)
WM_DAC_MUTE, 0x0000, /* DAC unmute */
WM_ADC_MUX, 0x0003, /* ADC unmute, both CD/Line On */
};
- static unsigned char cs_inits[] = {
+ static const unsigned char cs_inits[] = {
0x04, 0x80, /* RUN, RXP0 */
0x05, 0x05, /* slave, 24bit */
0x01, 0x00,
@@ -826,24 +826,24 @@ static int __devinit pontis_init(struct snd_ice1712 *ice)
* hence the driver needs to sets up it properly.
*/
-static unsigned char pontis_eeprom[] __devinitdata = {
- 0x08, /* SYSCONF: clock 256, mpu401, spdif-in/ADC, 1DAC */
- 0x80, /* ACLINK: I2S */
- 0xf8, /* I2S: vol, 96k, 24bit, 192k */
- 0xc3, /* SPDIF: out-en, out-int, spdif-in */
- 0x07, /* GPIO_DIR */
- 0x00, /* GPIO_DIR1 */
- 0x00, /* GPIO_DIR2 (ignored) */
- 0x0f, /* GPIO_MASK (4-7 reserved for CS8416) */
- 0xff, /* GPIO_MASK1 */
- 0x00, /* GPIO_MASK2 (ignored) */
- 0x06, /* GPIO_STATE (0-low, 1-high, 2-high) */
- 0x00, /* GPIO_STATE1 */
- 0x00, /* GPIO_STATE2 (ignored) */
+static const unsigned char pontis_eeprom[] __devinitdata = {
+ [ICE_EEP2_SYSCONF] = 0x08, /* clock 256, mpu401, spdif-in/ADC, 1DAC */
+ [ICE_EEP2_ACLINK] = 0x80, /* I2S */
+ [ICE_EEP2_I2S] = 0xf8, /* vol, 96k, 24bit, 192k */
+ [ICE_EEP2_SPDIF] = 0xc3, /* out-en, out-int, spdif-in */
+ [ICE_EEP2_GPIO_DIR] = 0x07,
+ [ICE_EEP2_GPIO_DIR1] = 0x00,
+ [ICE_EEP2_GPIO_DIR2] = 0x00, /* ignored */
+ [ICE_EEP2_GPIO_MASK] = 0x0f, /* 4-7 reserved for CS8416 */
+ [ICE_EEP2_GPIO_MASK1] = 0xff,
+ [ICE_EEP2_GPIO_MASK2] = 0x00, /* ignored */
+ [ICE_EEP2_GPIO_STATE] = 0x06, /* 0-low, 1-high, 2-high */
+ [ICE_EEP2_GPIO_STATE1] = 0x00,
+ [ICE_EEP2_GPIO_STATE2] = 0x00, /* ignored */
};
/* entry point */
-struct snd_ice1712_card_info snd_vt1720_pontis_cards[] __devinitdata = {
+const struct snd_ice1712_card_info snd_vt1720_pontis_cards[] __devinitdata = {
{
.subvendor = VT1720_SUBDEVICE_PONTIS_MS300,
.name = "Pontis MS300",
diff --git a/sound/pci/ice1712/pontis.h b/sound/pci/ice1712/pontis.h
index d0d1378b935c..1a418255c19e 100644
--- a/sound/pci/ice1712/pontis.h
+++ b/sound/pci/ice1712/pontis.h
@@ -28,6 +28,6 @@
#define VT1720_SUBDEVICE_PONTIS_MS300 0x00020002 /* a dummy id for MS300 */
-extern struct snd_ice1712_card_info snd_vt1720_pontis_cards[];
+extern const struct snd_ice1712_card_info snd_vt1720_pontis_cards[];
#endif /* __SOUND_PONTIS_H */
diff --git a/sound/pci/ice1712/prodigy192.c b/sound/pci/ice1712/prodigy192.c
index 41b2605daa3a..31cc66eb9f8f 100644
--- a/sound/pci/ice1712/prodigy192.c
+++ b/sound/pci/ice1712/prodigy192.c
@@ -357,14 +357,14 @@ static int aureon_oversampling_put(struct snd_kcontrol *kcontrol, struct snd_ctl
}
#endif
-static DECLARE_TLV_DB_SCALE(db_scale_dac, -19125, 75, 0);
-static DECLARE_TLV_DB_SCALE(db_scale_adc, 0, 150, 0);
+static const DECLARE_TLV_DB_SCALE(db_scale_dac, -19125, 75, 0);
+static const DECLARE_TLV_DB_SCALE(db_scale_adc, 0, 150, 0);
/*
* mixers
*/
-static struct snd_kcontrol_new stac_controls[] __devinitdata = {
+static const struct snd_kcontrol_new stac_controls[] __devinitdata = {
{
.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
.name = "Master Playback Switch",
@@ -475,7 +475,7 @@ static int __devinit prodigy192_add_controls(struct snd_ice1712 *ice)
*/
static int __devinit prodigy192_init(struct snd_ice1712 *ice)
{
- static unsigned short stac_inits_prodigy[] = {
+ static const unsigned short stac_inits_prodigy[] = {
STAC946X_RESET, 0,
/* STAC946X_MASTER_VOLUME, 0,
STAC946X_LF_VOLUME, 0,
@@ -486,7 +486,7 @@ static int __devinit prodigy192_init(struct snd_ice1712 *ice)
STAC946X_LFE_VOLUME, 0,*/
(unsigned short)-1
};
- unsigned short *p;
+ const unsigned short *p;
/* prodigy 192 */
ice->num_total_dacs = 6;
@@ -506,25 +506,25 @@ static int __devinit prodigy192_init(struct snd_ice1712 *ice)
* hence the driver needs to sets up it properly.
*/
-static unsigned char prodigy71_eeprom[] __devinitdata = {
- 0x2b, /* SYSCONF: clock 512, mpu401, spdif-in/ADC, 4DACs */
- 0x80, /* ACLINK: I2S */
- 0xf8, /* I2S: vol, 96k, 24bit, 192k */
- 0xc3, /* SPDIF: out-en, out-int, spdif-in */
- 0xff, /* GPIO_DIR */
- 0xff, /* GPIO_DIR1 */
- 0xbf, /* GPIO_DIR2 */
- 0x00, /* GPIO_MASK */
- 0x00, /* GPIO_MASK1 */
- 0x00, /* GPIO_MASK2 */
- 0x00, /* GPIO_STATE */
- 0x00, /* GPIO_STATE1 */
- 0x00, /* GPIO_STATE2 */
+static const unsigned char prodigy71_eeprom[] __devinitdata = {
+ [ICE_EEP2_SYSCONF] = 0x2b, /* clock 512, mpu401, spdif-in/ADC, 4DACs */
+ [ICE_EEP2_ACLINK] = 0x80, /* I2S */
+ [ICE_EEP2_I2S] = 0xf8, /* vol, 96k, 24bit, 192k */
+ [ICE_EEP2_SPDIF] = 0xc3, /* out-en, out-int, spdif-in */
+ [ICE_EEP2_GPIO_DIR] = 0xff,
+ [ICE_EEP2_GPIO_DIR1] = 0xff,
+ [ICE_EEP2_GPIO_DIR2] = 0xbf,
+ [ICE_EEP2_GPIO_MASK] = 0x00,
+ [ICE_EEP2_GPIO_MASK1] = 0x00,
+ [ICE_EEP2_GPIO_MASK2] = 0x00,
+ [ICE_EEP2_GPIO_STATE] = 0x00,
+ [ICE_EEP2_GPIO_STATE1] = 0x00,
+ [ICE_EEP2_GPIO_STATE2] = 0x00,
};
/* entry point */
-struct snd_ice1712_card_info snd_vt1724_prodigy192_cards[] __devinitdata = {
+const struct snd_ice1712_card_info snd_vt1724_prodigy192_cards[] __devinitdata = {
{
.subvendor = VT1724_SUBDEVICE_PRODIGY192VE,
.name = "Audiotrak Prodigy 192",
diff --git a/sound/pci/ice1712/prodigy192.h b/sound/pci/ice1712/prodigy192.h
index 94c824e24e06..2fa2e62b9e04 100644
--- a/sound/pci/ice1712/prodigy192.h
+++ b/sound/pci/ice1712/prodigy192.h
@@ -6,6 +6,6 @@
#define VT1724_SUBDEVICE_PRODIGY192VE 0x34495345 /* PRODIGY 192 VE */
-extern struct snd_ice1712_card_info snd_vt1724_prodigy192_cards[];
+extern const struct snd_ice1712_card_info snd_vt1724_prodigy192_cards[];
#endif /* __SOUND_PRODIGY192_H */
diff --git a/sound/pci/ice1712/revo.c b/sound/pci/ice1712/revo.c
index bf98ea34feb0..025a7e8497c3 100644
--- a/sound/pci/ice1712/revo.c
+++ b/sound/pci/ice1712/revo.c
@@ -84,38 +84,142 @@ static void revo_set_rate_val(struct snd_akm4xxx *ak, unsigned int rate)
}
/*
+ * I2C access to the PT2258 volume controller on GPIO 6/7 (Revolution 5.1)
+ */
+
+static void revo_i2c_start(struct snd_i2c_bus *bus)
+{
+ struct snd_ice1712 *ice = bus->private_data;
+ snd_ice1712_save_gpio_status(ice);
+}
+
+static void revo_i2c_stop(struct snd_i2c_bus *bus)
+{
+ struct snd_ice1712 *ice = bus->private_data;
+ snd_ice1712_restore_gpio_status(ice);
+}
+
+static void revo_i2c_direction(struct snd_i2c_bus *bus, int clock, int data)
+{
+ struct snd_ice1712 *ice = bus->private_data;
+ unsigned int mask, val;
+
+ val = 0;
+ if (clock)
+ val |= VT1724_REVO_I2C_CLOCK; /* write SCL */
+ if (data)
+ val |= VT1724_REVO_I2C_DATA; /* write SDA */
+ mask = VT1724_REVO_I2C_CLOCK | VT1724_REVO_I2C_DATA;
+ ice->gpio.direction &= ~mask;
+ ice->gpio.direction |= val;
+ snd_ice1712_gpio_set_dir(ice, ice->gpio.direction);
+ snd_ice1712_gpio_set_mask(ice, ~mask);
+}
+
+static void revo_i2c_setlines(struct snd_i2c_bus *bus, int clk, int data)
+{
+ struct snd_ice1712 *ice = bus->private_data;
+ unsigned int val = 0;
+
+ if (clk)
+ val |= VT1724_REVO_I2C_CLOCK;
+ if (data)
+ val |= VT1724_REVO_I2C_DATA;
+ snd_ice1712_gpio_write_bits(ice,
+ VT1724_REVO_I2C_DATA |
+ VT1724_REVO_I2C_CLOCK, val);
+ udelay(5);
+}
+
+static int revo_i2c_getdata(struct snd_i2c_bus *bus, int ack)
+{
+ struct snd_ice1712 *ice = bus->private_data;
+ int bit;
+
+ if (ack)
+ udelay(5);
+ bit = snd_ice1712_gpio_read_bits(ice, VT1724_REVO_I2C_DATA) ? 1 : 0;
+ return bit;
+}
+
+static struct snd_i2c_bit_ops revo51_bit_ops = {
+ .start = revo_i2c_start,
+ .stop = revo_i2c_stop,
+ .direction = revo_i2c_direction,
+ .setlines = revo_i2c_setlines,
+ .getdata = revo_i2c_getdata,
+};
+
+static int revo51_i2c_init(struct snd_ice1712 *ice,
+ struct snd_pt2258 *pt)
+{
+ int err;
+
+ /* create the I2C bus */
+ err = snd_i2c_bus_create(ice->card, "ICE1724 GPIO6", NULL, &ice->i2c);
+ if (err < 0)
+ return err;
+
+ ice->i2c->private_data = ice;
+ ice->i2c->hw_ops.bit = &revo51_bit_ops;
+
+ /* create the I2C device */
+ err = snd_i2c_device_create(ice->i2c, "PT2258", 0x40,
+ &ice->spec.revo51.dev);
+ if (err < 0)
+ return err;
+
+ pt->card = ice->card;
+ pt->i2c_bus = ice->i2c;
+ pt->i2c_dev = ice->spec.revo51.dev;
+ ice->spec.revo51.pt2258 = pt;
+
+ snd_pt2258_reset(pt);
+
+ return 0;
+}
+
+/*
* initialize the chips on M-Audio Revolution cards
*/
#define AK_DAC(xname,xch) { .name = xname, .num_channels = xch }
-static struct snd_akm4xxx_dac_channel revo71_front[] = {
+static const struct snd_akm4xxx_dac_channel revo71_front[] = {
AK_DAC("PCM Playback Volume", 2)
};
-static struct snd_akm4xxx_dac_channel revo71_surround[] = {
+static const struct snd_akm4xxx_dac_channel revo71_surround[] = {
AK_DAC("PCM Center Playback Volume", 1),
AK_DAC("PCM LFE Playback Volume", 1),
AK_DAC("PCM Side Playback Volume", 2),
AK_DAC("PCM Rear Playback Volume", 2),
};
-static struct snd_akm4xxx_dac_channel revo51_dac[] = {
+static const struct snd_akm4xxx_dac_channel revo51_dac[] = {
AK_DAC("PCM Playback Volume", 2),
AK_DAC("PCM Center Playback Volume", 1),
AK_DAC("PCM LFE Playback Volume", 1),
AK_DAC("PCM Rear Playback Volume", 2),
};
-static struct snd_akm4xxx_adc_channel revo51_adc[] = {
+static const char *revo51_adc_input_names[] = {
+ "Mic",
+ "Line",
+ "CD",
+ NULL
+};
+
+static const struct snd_akm4xxx_adc_channel revo51_adc[] = {
{
.name = "PCM Capture Volume",
.switch_name = "PCM Capture Switch",
- .num_channels = 2
+ .num_channels = 2,
+ .input_names = revo51_adc_input_names
},
};
-static struct snd_akm4xxx akm_revo_front __devinitdata = {
+static const struct snd_akm4xxx akm_revo_front __devinitdata = {
.type = SND_AK4381,
.num_dacs = 2,
.ops = {
@@ -124,7 +228,7 @@ static struct snd_akm4xxx akm_revo_front __devinitdata = {
.dac_info = revo71_front,
};
-static struct snd_ak4xxx_private akm_revo_front_priv __devinitdata = {
+static const struct snd_ak4xxx_private akm_revo_front_priv __devinitdata = {
.caddr = 1,
.cif = 0,
.data_mask = VT1724_REVO_CDOUT,
@@ -136,7 +240,7 @@ static struct snd_ak4xxx_private akm_revo_front_priv __devinitdata = {
.mask_flags = 0,
};
-static struct snd_akm4xxx akm_revo_surround __devinitdata = {
+static const struct snd_akm4xxx akm_revo_surround __devinitdata = {
.type = SND_AK4355,
.idx_offset = 1,
.num_dacs = 6,
@@ -146,7 +250,7 @@ static struct snd_akm4xxx akm_revo_surround __devinitdata = {
.dac_info = revo71_surround,
};
-static struct snd_ak4xxx_private akm_revo_surround_priv __devinitdata = {
+static const struct snd_ak4xxx_private akm_revo_surround_priv __devinitdata = {
.caddr = 3,
.cif = 0,
.data_mask = VT1724_REVO_CDOUT,
@@ -158,7 +262,7 @@ static struct snd_ak4xxx_private akm_revo_surround_priv __devinitdata = {
.mask_flags = 0,
};
-static struct snd_akm4xxx akm_revo51 __devinitdata = {
+static const struct snd_akm4xxx akm_revo51 __devinitdata = {
.type = SND_AK4358,
.num_dacs = 6,
.ops = {
@@ -167,36 +271,213 @@ static struct snd_akm4xxx akm_revo51 __devinitdata = {
.dac_info = revo51_dac,
};
-static struct snd_ak4xxx_private akm_revo51_priv __devinitdata = {
+static const struct snd_ak4xxx_private akm_revo51_priv __devinitdata = {
.caddr = 2,
.cif = 0,
.data_mask = VT1724_REVO_CDOUT,
.clk_mask = VT1724_REVO_CCLK,
- .cs_mask = VT1724_REVO_CS0 | VT1724_REVO_CS1 | VT1724_REVO_CS2,
- .cs_addr = VT1724_REVO_CS1 | VT1724_REVO_CS2,
- .cs_none = VT1724_REVO_CS0 | VT1724_REVO_CS1 | VT1724_REVO_CS2,
+ .cs_mask = VT1724_REVO_CS0 | VT1724_REVO_CS1,
+ .cs_addr = VT1724_REVO_CS1,
+ .cs_none = VT1724_REVO_CS0 | VT1724_REVO_CS1,
.add_flags = VT1724_REVO_CCLK, /* high at init */
.mask_flags = 0,
};
-static struct snd_akm4xxx akm_revo51_adc __devinitdata = {
+static const struct snd_akm4xxx akm_revo51_adc __devinitdata = {
.type = SND_AK5365,
.num_adcs = 2,
.adc_info = revo51_adc,
};
-static struct snd_ak4xxx_private akm_revo51_adc_priv __devinitdata = {
+static const struct snd_ak4xxx_private akm_revo51_adc_priv __devinitdata = {
.caddr = 2,
.cif = 0,
.data_mask = VT1724_REVO_CDOUT,
.clk_mask = VT1724_REVO_CCLK,
- .cs_mask = VT1724_REVO_CS0 | VT1724_REVO_CS1 | VT1724_REVO_CS2,
- .cs_addr = VT1724_REVO_CS0 | VT1724_REVO_CS2,
- .cs_none = VT1724_REVO_CS0 | VT1724_REVO_CS1 | VT1724_REVO_CS2,
+ .cs_mask = VT1724_REVO_CS0 | VT1724_REVO_CS1,
+ .cs_addr = VT1724_REVO_CS0,
+ .cs_none = VT1724_REVO_CS0 | VT1724_REVO_CS1,
+ .add_flags = VT1724_REVO_CCLK, /* high at init */
+ .mask_flags = 0,
+};
+
+static struct snd_pt2258 ptc_revo51_volume;
+
+/* AK4358 for AP192 DAC, AK5385A for ADC */
+static void ap192_set_rate_val(struct snd_akm4xxx *ak, unsigned int rate)
+{
+ struct snd_ice1712 *ice = ak->private_data[0];
+
+ revo_set_rate_val(ak, rate);
+
+#if 1 /* FIXME: do we need this procedure? */
+ /* reset DFS pin of AK5385A for ADC, too */
+ /* DFS0 (pin 18) -- GPIO10 pin 77 */
+ snd_ice1712_save_gpio_status(ice);
+ snd_ice1712_gpio_write_bits(ice, 1 << 10,
+ rate > 48000 ? (1 << 10) : 0);
+ snd_ice1712_restore_gpio_status(ice);
+#endif
+}
+
+static const struct snd_akm4xxx_dac_channel ap192_dac[] = {
+ AK_DAC("PCM Playback Volume", 2)
+};
+
+static const struct snd_akm4xxx akm_ap192 __devinitdata = {
+ .type = SND_AK4358,
+ .num_dacs = 2,
+ .ops = {
+ .set_rate_val = ap192_set_rate_val
+ },
+ .dac_info = ap192_dac,
+};
+
+static const struct snd_ak4xxx_private akm_ap192_priv __devinitdata = {
+ .caddr = 2,
+ .cif = 0,
+ .data_mask = VT1724_REVO_CDOUT,
+ .clk_mask = VT1724_REVO_CCLK,
+ .cs_mask = VT1724_REVO_CS0 | VT1724_REVO_CS3,
+ .cs_addr = VT1724_REVO_CS3,
+ .cs_none = VT1724_REVO_CS0 | VT1724_REVO_CS3,
.add_flags = VT1724_REVO_CCLK, /* high at init */
.mask_flags = 0,
};
+#if 0
+/* FIXME: ak4114 makes the sound much lower due to some confliction,
+ * so let's disable it right now...
+ */
+#define BUILD_AK4114_AP192
+#endif
+
+#ifdef BUILD_AK4114_AP192
+/* AK4114 support on Audiophile 192 */
+/* CDTO (pin 32) -- GPIO2 pin 52
+ * CDTI (pin 33) -- GPIO3 pin 53 (shared with AK4358)
+ * CCLK (pin 34) -- GPIO1 pin 51 (shared with AK4358)
+ * CSN (pin 35) -- GPIO7 pin 59
+ */
+#define AK4114_ADDR 0x00
+
+static void write_data(struct snd_ice1712 *ice, unsigned int gpio,
+ unsigned int data, int idx)
+{
+ for (; idx >= 0; idx--) {
+ /* drop clock */
+ gpio &= ~VT1724_REVO_CCLK;
+ snd_ice1712_gpio_write(ice, gpio);
+ udelay(1);
+ /* set data */
+ if (data & (1 << idx))
+ gpio |= VT1724_REVO_CDOUT;
+ else
+ gpio &= ~VT1724_REVO_CDOUT;
+ snd_ice1712_gpio_write(ice, gpio);
+ udelay(1);
+ /* raise clock */
+ gpio |= VT1724_REVO_CCLK;
+ snd_ice1712_gpio_write(ice, gpio);
+ udelay(1);
+ }
+}
+
+static unsigned char read_data(struct snd_ice1712 *ice, unsigned int gpio,
+ int idx)
+{
+ unsigned char data = 0;
+
+ for (; idx >= 0; idx--) {
+ /* drop clock */
+ gpio &= ~VT1724_REVO_CCLK;
+ snd_ice1712_gpio_write(ice, gpio);
+ udelay(1);
+ /* read data */
+ if (snd_ice1712_gpio_read(ice) & VT1724_REVO_CDIN)
+ data |= (1 << idx);
+ udelay(1);
+ /* raise clock */
+ gpio |= VT1724_REVO_CCLK;
+ snd_ice1712_gpio_write(ice, gpio);
+ udelay(1);
+ }
+ return data;
+}
+
+static unsigned char ap192_4wire_start(struct snd_ice1712 *ice)
+{
+ unsigned int tmp;
+
+ snd_ice1712_save_gpio_status(ice);
+ tmp = snd_ice1712_gpio_read(ice);
+ tmp |= VT1724_REVO_CCLK; /* high at init */
+ tmp |= VT1724_REVO_CS0;
+ tmp &= ~VT1724_REVO_CS3;
+ snd_ice1712_gpio_write(ice, tmp);
+ udelay(1);
+ return tmp;
+}
+
+static void ap192_4wire_finish(struct snd_ice1712 *ice, unsigned int tmp)
+{
+ tmp |= VT1724_REVO_CS3;
+ tmp |= VT1724_REVO_CS0;
+ snd_ice1712_gpio_write(ice, tmp);
+ udelay(1);
+ snd_ice1712_restore_gpio_status(ice);
+}
+
+static void ap192_ak4114_write(void *private_data, unsigned char addr,
+ unsigned char data)
+{
+ struct snd_ice1712 *ice = private_data;
+ unsigned int tmp, addrdata;
+
+ tmp = ap192_4wire_start(ice);
+ addrdata = (AK4114_ADDR << 6) | 0x20 | (addr & 0x1f);
+ addrdata = (addrdata << 8) | data;
+ write_data(ice, tmp, addrdata, 15);
+ ap192_4wire_finish(ice, tmp);
+}
+
+static unsigned char ap192_ak4114_read(void *private_data, unsigned char addr)
+{
+ struct snd_ice1712 *ice = private_data;
+ unsigned int tmp;
+ unsigned char data;
+
+ tmp = ap192_4wire_start(ice);
+ write_data(ice, tmp, (AK4114_ADDR << 6) | (addr & 0x1f), 7);
+ data = read_data(ice, tmp, 7);
+ ap192_4wire_finish(ice, tmp);
+ return data;
+}
+
+static int ap192_ak4114_init(struct snd_ice1712 *ice)
+{
+ static const unsigned char ak4114_init_vals[] = {
+ AK4114_RST | AK4114_PWN | AK4114_OCKS0 | AK4114_OCKS1,
+ AK4114_DIF_I24I2S,
+ AK4114_TX1E,
+ AK4114_EFH_1024 | AK4114_DIT | AK4114_IPS(1),
+ 0,
+ 0
+ };
+ static const unsigned char ak4114_init_txcsb[] = {
+ 0x41, 0x02, 0x2c, 0x00, 0x00
+ };
+ struct ak4114 *ak;
+ int err;
+
+ return snd_ak4114_create(ice->card,
+ ap192_ak4114_read,
+ ap192_ak4114_write,
+ ak4114_init_vals, ak4114_init_txcsb,
+ ice, &ak);
+}
+#endif /* BUILD_AK4114_AP192 */
+
static int __devinit revo_init(struct snd_ice1712 *ice)
{
struct snd_akm4xxx *ak;
@@ -213,6 +494,10 @@ static int __devinit revo_init(struct snd_ice1712 *ice)
ice->num_total_dacs = 6;
ice->num_total_adcs = 2;
break;
+ case VT1724_SUBDEVICE_AUDIOPHILE192:
+ ice->num_total_dacs = 2;
+ ice->num_total_adcs = 2;
+ break;
default:
snd_BUG();
return -EINVAL;
@@ -235,14 +520,28 @@ static int __devinit revo_init(struct snd_ice1712 *ice)
break;
case VT1724_SUBDEVICE_REVOLUTION51:
ice->akm_codecs = 2;
- if ((err = snd_ice1712_akm4xxx_init(ak, &akm_revo51, &akm_revo51_priv, ice)) < 0)
+ err = snd_ice1712_akm4xxx_init(ak, &akm_revo51,
+ &akm_revo51_priv, ice);
+ if (err < 0)
return err;
- err = snd_ice1712_akm4xxx_init(ak + 1, &akm_revo51_adc,
+ err = snd_ice1712_akm4xxx_init(ak+1, &akm_revo51_adc,
&akm_revo51_adc_priv, ice);
if (err < 0)
return err;
- /* unmute all codecs - needed! */
- snd_ice1712_gpio_write_bits(ice, VT1724_REVO_MUTE, VT1724_REVO_MUTE);
+ err = revo51_i2c_init(ice, &ptc_revo51_volume);
+ if (err < 0)
+ return err;
+ /* unmute all codecs */
+ snd_ice1712_gpio_write_bits(ice, VT1724_REVO_MUTE,
+ VT1724_REVO_MUTE);
+ break;
+ case VT1724_SUBDEVICE_AUDIOPHILE192:
+ ice->akm_codecs = 1;
+ err = snd_ice1712_akm4xxx_init(ak, &akm_ap192, &akm_ap192_priv,
+ ice);
+ if (err < 0)
+ return err;
+
break;
}
@@ -256,16 +555,34 @@ static int __devinit revo_add_controls(struct snd_ice1712 *ice)
switch (ice->eeprom.subvendor) {
case VT1724_SUBDEVICE_REVOLUTION71:
+ err = snd_ice1712_akm4xxx_build_controls(ice);
+ if (err < 0)
+ return err;
+ break;
case VT1724_SUBDEVICE_REVOLUTION51:
err = snd_ice1712_akm4xxx_build_controls(ice);
if (err < 0)
return err;
+ err = snd_pt2258_build_controls(ice->spec.revo51.pt2258);
+ if (err < 0)
+ return err;
+ break;
+ case VT1724_SUBDEVICE_AUDIOPHILE192:
+ err = snd_ice1712_akm4xxx_build_controls(ice);
+ if (err < 0)
+ return err;
+#ifdef BUILD_AK4114_AP192
+ err = ap192_ak4114_init(ice);
+ if (err < 0)
+ return err;
+#endif
+ break;
}
return 0;
}
/* entry point */
-struct snd_ice1712_card_info snd_vt1724_revo_cards[] __devinitdata = {
+const struct snd_ice1712_card_info snd_vt1724_revo_cards[] __devinitdata = {
{
.subvendor = VT1724_SUBDEVICE_REVOLUTION71,
.name = "M Audio Revolution-7.1",
@@ -280,5 +597,12 @@ struct snd_ice1712_card_info snd_vt1724_revo_cards[] __devinitdata = {
.chip_init = revo_init,
.build_controls = revo_add_controls,
},
+ {
+ .subvendor = VT1724_SUBDEVICE_AUDIOPHILE192,
+ .name = "M Audio Audiophile192",
+ .model = "ap192",
+ .chip_init = revo_init,
+ .build_controls = revo_add_controls,
+ },
{ } /* terminator */
};
diff --git a/sound/pci/ice1712/revo.h b/sound/pci/ice1712/revo.h
index efbb86ec3289..2a24488fad80 100644
--- a/sound/pci/ice1712/revo.h
+++ b/sound/pci/ice1712/revo.h
@@ -26,13 +26,15 @@
#define REVO_DEVICE_DESC \
"{MidiMan M Audio,Revolution 7.1},"\
- "{MidiMan M Audio,Revolution 5.1},"
+ "{MidiMan M Audio,Revolution 5.1},"\
+ "{MidiMan M Audio,Audiophile 192},"
#define VT1724_SUBDEVICE_REVOLUTION71 0x12143036
#define VT1724_SUBDEVICE_REVOLUTION51 0x12143136
+#define VT1724_SUBDEVICE_AUDIOPHILE192 0x12143236
/* entry point */
-extern struct snd_ice1712_card_info snd_vt1724_revo_cards[];
+extern const struct snd_ice1712_card_info snd_vt1724_revo_cards[];
/*
@@ -42,9 +44,12 @@ extern struct snd_ice1712_card_info snd_vt1724_revo_cards[];
#define VT1724_REVO_CCLK 0x02
#define VT1724_REVO_CDIN 0x04 /* not used */
#define VT1724_REVO_CDOUT 0x08
-#define VT1724_REVO_CS0 0x10 /* AK5365 chipselect for Rev. 5.1 */
+#define VT1724_REVO_CS0 0x10 /* AK5365 chipselect for (revo51) */
#define VT1724_REVO_CS1 0x20 /* front AKM4381 chipselect */
-#define VT1724_REVO_CS2 0x40 /* surround AKM4355 chipselect */
+#define VT1724_REVO_CS2 0x40 /* surround AKM4355 CS (revo71) */
+#define VT1724_REVO_I2C_DATA 0x40 /* I2C: PT 2258 SDA (on revo51) */
+#define VT1724_REVO_I2C_CLOCK 0x80 /* I2C: PT 2258 SCL (on revo51) */
+#define VT1724_REVO_CS3 0x80 /* AK4114 for AP192 */
#define VT1724_REVO_MUTE (1<<22) /* 0 = all mute, 1 = normal operation */
#endif /* __SOUND_REVO_H */
diff --git a/sound/pci/ice1712/vt1720_mobo.c b/sound/pci/ice1712/vt1720_mobo.c
index 7ca263c13091..72b060d63c29 100644
--- a/sound/pci/ice1712/vt1720_mobo.c
+++ b/sound/pci/ice1712/vt1720_mobo.c
@@ -30,6 +30,7 @@
#include <sound/core.h>
#include "ice1712.h"
+#include "envy24ht.h"
#include "vt1720_mobo.h"
@@ -55,41 +56,41 @@ static int __devinit k8x800_add_controls(struct snd_ice1712 *ice)
/* EEPROM image */
-static unsigned char k8x800_eeprom[] __devinitdata = {
- 0x01, /* SYSCONF: clock 256, 1ADC, 2DACs */
- 0x02, /* ACLINK: ACLINK, packed */
- 0x00, /* I2S: - */
- 0x00, /* SPDIF: - */
- 0xff, /* GPIO_DIR */
- 0xff, /* GPIO_DIR1 */
- 0x00, /* - */
- 0xff, /* GPIO_MASK */
- 0xff, /* GPIO_MASK1 */
- 0x00, /* - */
- 0x00, /* GPIO_STATE */
- 0x00, /* GPIO_STATE1 */
- 0x00, /* - */
+static const unsigned char k8x800_eeprom[] __devinitdata = {
+ [ICE_EEP2_SYSCONF] = 0x01, /* clock 256, 1ADC, 2DACs */
+ [ICE_EEP2_ACLINK] = 0x02, /* ACLINK, packed */
+ [ICE_EEP2_I2S] = 0x00, /* - */
+ [ICE_EEP2_SPDIF] = 0x00, /* - */
+ [ICE_EEP2_GPIO_DIR] = 0xff,
+ [ICE_EEP2_GPIO_DIR1] = 0xff,
+ [ICE_EEP2_GPIO_DIR2] = 0x00, /* - */
+ [ICE_EEP2_GPIO_MASK] = 0xff,
+ [ICE_EEP2_GPIO_MASK1] = 0xff,
+ [ICE_EEP2_GPIO_MASK2] = 0x00, /* - */
+ [ICE_EEP2_GPIO_STATE] = 0x00,
+ [ICE_EEP2_GPIO_STATE1] = 0x00,
+ [ICE_EEP2_GPIO_STATE2] = 0x00, /* - */
};
-static unsigned char sn25p_eeprom[] __devinitdata = {
- 0x01, /* SYSCONF: clock 256, 1ADC, 2DACs */
- 0x02, /* ACLINK: ACLINK, packed */
- 0x00, /* I2S: - */
- 0x41, /* SPDIF: - */
- 0xff, /* GPIO_DIR */
- 0xff, /* GPIO_DIR1 */
- 0x00, /* - */
- 0xff, /* GPIO_MASK */
- 0xff, /* GPIO_MASK1 */
- 0x00, /* - */
- 0x00, /* GPIO_STATE */
- 0x00, /* GPIO_STATE1 */
- 0x00, /* - */
+static const unsigned char sn25p_eeprom[] __devinitdata = {
+ [ICE_EEP2_SYSCONF] = 0x01, /* clock 256, 1ADC, 2DACs */
+ [ICE_EEP2_ACLINK] = 0x02, /* ACLINK, packed */
+ [ICE_EEP2_I2S] = 0x00, /* - */
+ [ICE_EEP2_SPDIF] = 0x41, /* - */
+ [ICE_EEP2_GPIO_DIR] = 0xff,
+ [ICE_EEP2_GPIO_DIR1] = 0xff,
+ [ICE_EEP2_GPIO_DIR2] = 0x00, /* - */
+ [ICE_EEP2_GPIO_MASK] = 0xff,
+ [ICE_EEP2_GPIO_MASK1] = 0xff,
+ [ICE_EEP2_GPIO_MASK2] = 0x00, /* - */
+ [ICE_EEP2_GPIO_STATE] = 0x00,
+ [ICE_EEP2_GPIO_STATE1] = 0x00,
+ [ICE_EEP2_GPIO_STATE2] = 0x00, /* - */
};
/* entry point */
-struct snd_ice1712_card_info snd_vt1720_mobo_cards[] __devinitdata = {
+const struct snd_ice1712_card_info snd_vt1720_mobo_cards[] __devinitdata = {
{
.subvendor = VT1720_SUBDEVICE_K8X800,
.name = "Albatron K8X800 Pro II",
diff --git a/sound/pci/ice1712/vt1720_mobo.h b/sound/pci/ice1712/vt1720_mobo.h
index 0b1b0ee1bea7..70af3ad64a5d 100644
--- a/sound/pci/ice1712/vt1720_mobo.h
+++ b/sound/pci/ice1712/vt1720_mobo.h
@@ -36,6 +36,6 @@
#define VT1720_SUBDEVICE_9CJS 0x0f272327
#define VT1720_SUBDEVICE_SN25P 0x97123650
-extern struct snd_ice1712_card_info snd_vt1720_mobo_cards[];
+extern const struct snd_ice1712_card_info snd_vt1720_mobo_cards[];
#endif /* __SOUND_VT1720_MOBO_H */
diff --git a/sound/pci/ice1712/wtm.c b/sound/pci/ice1712/wtm.c
new file mode 100644
index 000000000000..4a706b16a0b9
--- /dev/null
+++ b/sound/pci/ice1712/wtm.c
@@ -0,0 +1,542 @@
+/*
+ * ALSA driver for ICEnsemble VT1724 (Envy24HT)
+ *
+ * Lowlevel functions for Ego Sys Waveterminal 192M
+ *
+ * Copyright (c) 2006 Guedez Clement <klem.dev@gmail.com>
+ * Some functions are taken from the Prodigy192 driver
+ * source
+ *
+ * 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 <sound/driver.h>
+#include <asm/io.h>
+#include <linux/delay.h>
+#include <linux/interrupt.h>
+#include <linux/init.h>
+#include <linux/slab.h>
+#include <sound/core.h>
+
+#include "ice1712.h"
+#include "envy24ht.h"
+#include "wtm.h"
+#include "stac946x.h"
+
+
+/*
+ * 2*ADC 6*DAC no1 ringbuffer r/w on i2c bus
+ */
+static inline void stac9460_put(struct snd_ice1712 *ice, int reg,
+ unsigned char val)
+{
+ snd_vt1724_write_i2c(ice, STAC9460_I2C_ADDR, reg, val);
+}
+
+static inline unsigned char stac9460_get(struct snd_ice1712 *ice, int reg)
+{
+ return snd_vt1724_read_i2c(ice, STAC9460_I2C_ADDR, reg);
+}
+
+/*
+ * 2*ADC 2*DAC no2 ringbuffer r/w on i2c bus
+ */
+static inline void stac9460_2_put(struct snd_ice1712 *ice, int reg,
+ unsigned char val)
+{
+ snd_vt1724_write_i2c(ice, STAC9460_2_I2C_ADDR, reg, val);
+}
+
+static inline unsigned char stac9460_2_get(struct snd_ice1712 *ice, int reg)
+{
+ return snd_vt1724_read_i2c(ice, STAC9460_2_I2C_ADDR, reg);
+}
+
+
+/*
+ * DAC mute control
+ */
+static int stac9460_dac_mute_info(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_info *uinfo)
+{
+ uinfo->type = SNDRV_CTL_ELEM_TYPE_BOOLEAN;
+ uinfo->count = 1;
+ uinfo->value.integer.min = 0;
+ return 0;
+}
+
+static int stac9460_dac_mute_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_ice1712 *ice = snd_kcontrol_chip(kcontrol);
+ unsigned char val;
+ int idx, id;
+
+ if (kcontrol->private_value) {
+ idx = STAC946X_MASTER_VOLUME;
+ id = 0;
+ } else {
+ id = snd_ctl_get_ioffidx(kcontrol, &ucontrol->id);
+ idx = id + STAC946X_LF_VOLUME;
+ }
+ if (id < 6)
+ val = stac9460_get(ice, idx);
+ else
+ val = stac9460_2_get(ice,idx - 6);
+ ucontrol->value.integer.value[0] = (~val >> 7) & 0x1;
+ return 0;
+}
+
+static int stac9460_dac_mute_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_ice1712 *ice = snd_kcontrol_chip(kcontrol);
+ unsigned char new, old;
+ int id, idx;
+ int change;
+
+ if (kcontrol->private_value) {
+ idx = STAC946X_MASTER_VOLUME;
+ old = stac9460_get(ice, idx);
+ new = (~ucontrol->value.integer.value[0]<< 7 & 0x80) |
+ (old & ~0x80);
+ change = (new != old);
+ if (change) {
+ stac9460_put(ice, idx, new);
+ stac9460_2_put(ice, idx, new);
+ }
+ } else {
+ id = snd_ctl_get_ioffidx(kcontrol, &ucontrol->id);
+ idx = id + STAC946X_LF_VOLUME;
+ if (id < 6)
+ old = stac9460_get(ice, idx);
+ else
+ old = stac9460_2_get(ice, idx - 6);
+ new = (~ucontrol->value.integer.value[0]<< 7 & 0x80) |
+ (old & ~0x80);
+ change = (new != old);
+ if (change) {
+ if (id < 6)
+ stac9460_put(ice, idx, new);
+ else
+ stac9460_2_put(ice, idx - 6, new);
+ }
+ }
+ return change;
+}
+
+/*
+ * DAC volume attenuation mixer control
+ */
+static int stac9460_dac_vol_info(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_info *uinfo)
+{
+ uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
+ uinfo->count = 1;
+ uinfo->value.integer.min = 0; /* mute */
+ uinfo->value.integer.max = 0x7f; /* 0dB */
+ return 0;
+}
+
+static int stac9460_dac_vol_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_ice1712 *ice = snd_kcontrol_chip(kcontrol);
+ int idx, id;
+ unsigned char vol;
+
+ if (kcontrol->private_value) {
+ idx = STAC946X_MASTER_VOLUME;
+ id = 0;
+ } else {
+ id = snd_ctl_get_ioffidx(kcontrol, &ucontrol->id);
+ idx = id + STAC946X_LF_VOLUME;
+ }
+ if (id < 6)
+ vol = stac9460_get(ice, idx) & 0x7f;
+ else
+ vol = stac9460_2_get(ice, idx - 6) & 0x7f;
+ ucontrol->value.integer.value[0] = 0x7f - vol;
+ return 0;
+}
+
+static int stac9460_dac_vol_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_ice1712 *ice = snd_kcontrol_chip(kcontrol);
+ int idx, id;
+ unsigned char tmp, ovol, nvol;
+ int change;
+
+ if (kcontrol->private_value) {
+ idx = STAC946X_MASTER_VOLUME;
+ nvol = ucontrol->value.integer.value[0];
+ tmp = stac9460_get(ice, idx);
+ ovol = 0x7f - (tmp & 0x7f);
+ change = (ovol != nvol);
+ if (change) {
+ stac9460_put(ice, idx, (0x7f - nvol) | (tmp & 0x80));
+ stac9460_2_put(ice, idx, (0x7f - nvol) | (tmp & 0x80));
+ }
+ } else {
+ id = snd_ctl_get_ioffidx(kcontrol, &ucontrol->id);
+ idx = id + STAC946X_LF_VOLUME;
+ nvol = ucontrol->value.integer.value[0];
+ if (id < 6)
+ tmp = stac9460_get(ice, idx);
+ else
+ tmp = stac9460_2_get(ice, idx - 6);
+ ovol = 0x7f - (tmp & 0x7f);
+ change = (ovol != nvol);
+ if (change) {
+ if (id < 6)
+ stac9460_put(ice, idx, (0x7f - nvol) |
+ (tmp & 0x80));
+ else
+ stac9460_2_put(ice, idx-6, (0x7f - nvol) |
+ (tmp & 0x80));
+ }
+ }
+ return change;
+}
+
+/*
+ * ADC mute control
+ */
+static int stac9460_adc_mute_info(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_info *uinfo)
+{
+ uinfo->type = SNDRV_CTL_ELEM_TYPE_BOOLEAN;
+ uinfo->count = 2;
+ uinfo->value.integer.min = 0;
+ uinfo->value.integer.max = 1;
+ return 0;
+}
+
+static int stac9460_adc_mute_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_ice1712 *ice = snd_kcontrol_chip(kcontrol);
+ unsigned char val;
+ int i, id;
+
+ id = snd_ctl_get_ioffidx(kcontrol, &ucontrol->id);
+ if (id == 0) {
+ for (i = 0; i < 2; ++i) {
+ val = stac9460_get(ice, STAC946X_MIC_L_VOLUME + i);
+ ucontrol->value.integer.value[i] = ~val>>7 & 0x1;
+ }
+ } else {
+ for (i = 0; i < 2; ++i) {
+ val = stac9460_2_get(ice, STAC946X_MIC_L_VOLUME + i);
+ ucontrol->value.integer.value[i] = ~val>>7 & 0x1;
+ }
+ }
+ return 0;
+}
+
+static int stac9460_adc_mute_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_ice1712 *ice = snd_kcontrol_chip(kcontrol);
+ unsigned char new, old;
+ int i, reg, id;
+ int change;
+
+ id = snd_ctl_get_ioffidx(kcontrol, &ucontrol->id);
+ if (id == 0) {
+ for (i = 0; i < 2; ++i) {
+ reg = STAC946X_MIC_L_VOLUME + i;
+ old = stac9460_get(ice, reg);
+ new = (~ucontrol->value.integer.value[i]<<7&0x80) |
+ (old&~0x80);
+ change = (new != old);
+ if (change)
+ stac9460_put(ice, reg, new);
+ }
+ } else {
+ for (i = 0; i < 2; ++i) {
+ reg = STAC946X_MIC_L_VOLUME + i;
+ old = stac9460_2_get(ice, reg);
+ new = (~ucontrol->value.integer.value[i]<<7&0x80) |
+ (old&~0x80);
+ change = (new != old);
+ if (change)
+ stac9460_2_put(ice, reg, new);
+ }
+ }
+ return change;
+}
+
+/*
+ *ADC gain mixer control
+ */
+static int stac9460_adc_vol_info(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_info *uinfo)
+{
+ uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
+ uinfo->count = 2;
+ uinfo->value.integer.min = 0; /* 0dB */
+ uinfo->value.integer.max = 0x0f; /* 22.5dB */
+ return 0;
+}
+
+static int stac9460_adc_vol_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_ice1712 *ice = snd_kcontrol_chip(kcontrol);
+ int i, reg, id;
+ unsigned char vol;
+
+ id = snd_ctl_get_ioffidx(kcontrol, &ucontrol->id);
+ if (id == 0) {
+ for (i = 0; i < 2; ++i) {
+ reg = STAC946X_MIC_L_VOLUME + i;
+ vol = stac9460_get(ice, reg) & 0x0f;
+ ucontrol->value.integer.value[i] = 0x0f - vol;
+ }
+ } else {
+ for (i = 0; i < 2; ++i) {
+ reg = STAC946X_MIC_L_VOLUME + i;
+ vol = stac9460_2_get(ice, reg) & 0x0f;
+ ucontrol->value.integer.value[i] = 0x0f - vol;
+ }
+ }
+ return 0;
+}
+
+static int stac9460_adc_vol_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_ice1712 *ice = snd_kcontrol_chip(kcontrol);
+ int i, reg, id;
+ unsigned char ovol, nvol;
+ int change;
+
+ id = snd_ctl_get_ioffidx(kcontrol, &ucontrol->id);
+ if (id == 0) {
+ for (i = 0; i < 2; ++i) {
+ reg = STAC946X_MIC_L_VOLUME + i;
+ nvol = ucontrol->value.integer.value[i];
+ ovol = 0x0f - stac9460_get(ice, reg);
+ change = ((ovol & 0x0f) != nvol);
+ if (change)
+ stac9460_put(ice, reg, (0x0f - nvol) |
+ (ovol & ~0x0f));
+ }
+ } else {
+ for (i = 0; i < 2; ++i) {
+ reg = STAC946X_MIC_L_VOLUME + i;
+ nvol = ucontrol->value.integer.value[i];
+ ovol = 0x0f - stac9460_2_get(ice, reg);
+ change = ((ovol & 0x0f) != nvol);
+ if (change)
+ stac9460_2_put(ice, reg, (0x0f - nvol) |
+ (ovol & ~0x0f));
+ }
+ }
+ return change;
+}
+
+/*
+ * MIC / LINE switch fonction
+ */
+
+static int stac9460_mic_sw_info(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_info *uinfo)
+{
+ uinfo->type = SNDRV_CTL_ELEM_TYPE_BOOLEAN;
+ uinfo->count = 1;
+ uinfo->value.integer.min = 0;
+ uinfo->value.integer.max = 1;
+ return 0;
+}
+
+static int stac9460_mic_sw_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_ice1712 *ice = snd_kcontrol_chip(kcontrol);
+ unsigned char val;
+ int id;
+
+ id = snd_ctl_get_ioffidx(kcontrol, &ucontrol->id);
+ if (id == 0)
+ val = stac9460_get(ice, STAC946X_GENERAL_PURPOSE);
+ else
+ val = stac9460_2_get(ice, STAC946X_GENERAL_PURPOSE);
+ ucontrol->value.integer.value[0] = ~val>>7 & 0x1;
+ return 0;
+}
+
+static int stac9460_mic_sw_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_ice1712 *ice = snd_kcontrol_chip(kcontrol);
+ unsigned char new, old;
+ int change, id;
+
+ id = snd_ctl_get_ioffidx(kcontrol, &ucontrol->id);
+ if (id == 0)
+ old = stac9460_get(ice, STAC946X_GENERAL_PURPOSE);
+ else
+ old = stac9460_2_get(ice, STAC946X_GENERAL_PURPOSE);
+ new = (~ucontrol->value.integer.value[0]<< 7 & 0x80) | (old & ~0x80);
+ change = (new != old);
+ if (change) {
+ if (id == 0)
+ stac9460_put(ice, STAC946X_GENERAL_PURPOSE, new);
+ else
+ stac9460_2_put(ice, STAC946X_GENERAL_PURPOSE, new);
+ }
+ return change;
+}
+
+/*
+ * Control tabs
+ */
+static const struct snd_kcontrol_new stac9640_controls[] __devinitdata = {
+ {
+ .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+ .name = "Master Playback Switch",
+ .info = stac9460_dac_mute_info,
+ .get = stac9460_dac_mute_get,
+ .put = stac9460_dac_mute_put,
+ .private_value = 1
+ },
+ {
+ .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+ .name = "Master Playback Volume",
+ .info = stac9460_dac_vol_info,
+ .get = stac9460_dac_vol_get,
+ .put = stac9460_dac_vol_put,
+ .private_value = 1,
+ },
+ {
+ .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+ .name = "MIC/Line switch",
+ .count = 2,
+ .info = stac9460_mic_sw_info,
+ .get = stac9460_mic_sw_get,
+ .put = stac9460_mic_sw_put,
+
+ },
+ {
+ .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+ .name = "DAC Switch",
+ .count = 8,
+ .info = stac9460_dac_mute_info,
+ .get = stac9460_dac_mute_get,
+ .put = stac9460_dac_mute_put,
+ },
+ {
+ .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+ .name = "DAC Volume",
+ .count = 8,
+ .info = stac9460_dac_vol_info,
+ .get = stac9460_dac_vol_get,
+ .put = stac9460_dac_vol_put,
+ },
+ {
+ .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+ .name = "ADC Switch",
+ .count = 2,
+ .info = stac9460_adc_mute_info,
+ .get = stac9460_adc_mute_get,
+ .put = stac9460_adc_mute_put,
+ },
+ {
+ .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+ .name = "ADC Volume",
+ .count = 2,
+ .info = stac9460_adc_vol_info,
+ .get = stac9460_adc_vol_get,
+ .put = stac9460_adc_vol_put,
+
+ }
+};
+
+
+
+/*INIT*/
+static int __devinit wtm_add_controls(struct snd_ice1712 *ice)
+{
+ unsigned int i;
+ int err;
+
+ for (i = 0; i < ARRAY_SIZE(stac9640_controls); i++) {
+ err = snd_ctl_add(ice->card,
+ snd_ctl_new1(&stac9640_controls[i], ice));
+ if (err < 0)
+ return err;
+ }
+ return 0;
+}
+
+static int __devinit wtm_init(struct snd_ice1712 *ice)
+{
+ static unsigned short stac_inits_prodigy[] = {
+ STAC946X_RESET, 0,
+ (unsigned short)-1
+ };
+ unsigned short *p;
+
+ /*WTM 192M*/
+ ice->num_total_dacs = 8;
+ ice->num_total_adcs = 4;
+ ice->force_rdma1 = 1;
+
+ /*initialize codec*/
+ p = stac_inits_prodigy;
+ for (; *p != (unsigned short)-1; p += 2) {
+ stac9460_put(ice, p[0], p[1]);
+ stac9460_2_put(ice, p[0], p[1]);
+ }
+ return 0;
+}
+
+
+static unsigned char wtm_eeprom[] __devinitdata = {
+ 0x47, /*SYSCONF: clock 192KHz, 4ADC, 8DAC */
+ 0x80, /* ACLINK : I2S */
+ 0xf8, /* I2S: vol; 96k, 24bit, 192k */
+ 0xc1 /*SPDIF: out-en, spidf ext out*/,
+ 0x9f, /* GPIO_DIR */
+ 0xff, /* GPIO_DIR1 */
+ 0x7f, /* GPIO_DIR2 */
+ 0x9f, /* GPIO_MASK */
+ 0xff, /* GPIO_MASK1 */
+ 0x7f, /* GPIO_MASK2 */
+ 0x16, /* GPIO_STATE */
+ 0x80, /* GPIO_STATE1 */
+ 0x00, /* GPIO_STATE2 */
+};
+
+
+/*entry point*/
+struct snd_ice1712_card_info snd_vt1724_wtm_cards[] __devinitdata = {
+ {
+ .subvendor = VT1724_SUBDEVICE_WTM,
+ .name = "ESI Waveterminal 192M",
+ .model = "WT192M",
+ .chip_init = wtm_init,
+ .build_controls = wtm_add_controls,
+ .eeprom_size = sizeof(wtm_eeprom),
+ .eeprom_data = wtm_eeprom,
+ },
+ {} /*terminator*/
+};
diff --git a/sound/pci/ice1712/wtm.h b/sound/pci/ice1712/wtm.h
new file mode 100644
index 000000000000..03a394e442f1
--- /dev/null
+++ b/sound/pci/ice1712/wtm.h
@@ -0,0 +1,20 @@
+#ifndef __SOUND_WTM_H
+#define __SOUND_WTM_H
+
+/* ID */
+#define WTM_DEVICE_DESC "{EGO SYS INC,WaveTerminal 192M},"
+#define VT1724_SUBDEVICE_WTM 0x36495345 /* WT192M ver1.0 */
+
+/*
+ *chip addresses on I2C bus
+ */
+
+#define AK4114_ADDR 0x20 /*S/PDIF receiver*/
+#define STAC9460_I2C_ADDR 0x54 /* ADC*2 | DAC*6 */
+#define STAC9460_2_I2C_ADDR 0x56 /* ADC|DAC *2 */
+
+
+extern struct snd_ice1712_card_info snd_vt1724_wtm_cards[];
+
+#endif /* __SOUND_WTM_H */
+
diff --git a/sound/pci/intel8x0.c b/sound/pci/intel8x0.c
index 30aaa6092a84..a289abfc7172 100644
--- a/sound/pci/intel8x0.c
+++ b/sound/pci/intel8x0.c
@@ -71,6 +71,7 @@ static char *ac97_quirk;
static int buggy_semaphore;
static int buggy_irq = -1; /* auto-check */
static int xbox;
+static int spdif_aclink = -1;
module_param(index, int, 0444);
MODULE_PARM_DESC(index, "Index value for Intel i8x0 soundcard.");
@@ -86,6 +87,8 @@ module_param(buggy_irq, bool, 0444);
MODULE_PARM_DESC(buggy_irq, "Enable workaround for buggy interrupts on some motherboards.");
module_param(xbox, bool, 0444);
MODULE_PARM_DESC(xbox, "Set to 1 for Xbox, if you have problems with the AC'97 codec detection.");
+module_param(spdif_aclink, int, 0444);
+MODULE_PARM_DESC(spdif_aclink, "S/PDIF over AC-link.");
/* just for backward compatibility */
static int enable;
@@ -368,12 +371,8 @@ struct intel8x0 {
int irq;
- unsigned int mmio;
- unsigned long addr;
- void __iomem *remap_addr;
- unsigned int bm_mmio;
- unsigned long bmaddr;
- void __iomem *remap_bmaddr;
+ void __iomem *addr;
+ void __iomem *bmaddr;
struct pci_dev *pci;
struct snd_card *card;
@@ -446,72 +445,48 @@ MODULE_DEVICE_TABLE(pci, snd_intel8x0_ids);
* Lowlevel I/O - busmaster
*/
-static u8 igetbyte(struct intel8x0 *chip, u32 offset)
+static inline u8 igetbyte(struct intel8x0 *chip, u32 offset)
{
- if (chip->bm_mmio)
- return readb(chip->remap_bmaddr + offset);
- else
- return inb(chip->bmaddr + offset);
+ return ioread8(chip->bmaddr + offset);
}
-static u16 igetword(struct intel8x0 *chip, u32 offset)
+static inline u16 igetword(struct intel8x0 *chip, u32 offset)
{
- if (chip->bm_mmio)
- return readw(chip->remap_bmaddr + offset);
- else
- return inw(chip->bmaddr + offset);
+ return ioread16(chip->bmaddr + offset);
}
-static u32 igetdword(struct intel8x0 *chip, u32 offset)
+static inline u32 igetdword(struct intel8x0 *chip, u32 offset)
{
- if (chip->bm_mmio)
- return readl(chip->remap_bmaddr + offset);
- else
- return inl(chip->bmaddr + offset);
+ return ioread32(chip->bmaddr + offset);
}
-static void iputbyte(struct intel8x0 *chip, u32 offset, u8 val)
+static inline void iputbyte(struct intel8x0 *chip, u32 offset, u8 val)
{
- if (chip->bm_mmio)
- writeb(val, chip->remap_bmaddr + offset);
- else
- outb(val, chip->bmaddr + offset);
+ iowrite8(val, chip->bmaddr + offset);
}
-static void iputword(struct intel8x0 *chip, u32 offset, u16 val)
+static inline void iputword(struct intel8x0 *chip, u32 offset, u16 val)
{
- if (chip->bm_mmio)
- writew(val, chip->remap_bmaddr + offset);
- else
- outw(val, chip->bmaddr + offset);
+ iowrite16(val, chip->bmaddr + offset);
}
-static void iputdword(struct intel8x0 *chip, u32 offset, u32 val)
+static inline void iputdword(struct intel8x0 *chip, u32 offset, u32 val)
{
- if (chip->bm_mmio)
- writel(val, chip->remap_bmaddr + offset);
- else
- outl(val, chip->bmaddr + offset);
+ iowrite32(val, chip->bmaddr + offset);
}
/*
* Lowlevel I/O - AC'97 registers
*/
-static u16 iagetword(struct intel8x0 *chip, u32 offset)
+static inline u16 iagetword(struct intel8x0 *chip, u32 offset)
{
- if (chip->mmio)
- return readw(chip->remap_addr + offset);
- else
- return inw(chip->addr + offset);
+ return ioread16(chip->addr + offset);
}
-static void iaputword(struct intel8x0 *chip, u32 offset, u16 val)
+static inline void iaputword(struct intel8x0 *chip, u32 offset, u16 val)
{
- if (chip->mmio)
- writew(val, chip->remap_addr + offset);
- else
- outw(val, chip->addr + offset);
+ iowrite16(val, chip->addr + offset);
}
/*
@@ -1606,10 +1581,14 @@ static int __devinit snd_intel8x0_pcm(struct intel8x0 *chip)
case DEVICE_INTEL_ICH4:
tbl = intel_pcms;
tblsize = ARRAY_SIZE(intel_pcms);
+ if (spdif_aclink)
+ tblsize--;
break;
case DEVICE_NFORCE:
tbl = nforce_pcms;
tblsize = ARRAY_SIZE(nforce_pcms);
+ if (spdif_aclink)
+ tblsize--;
break;
case DEVICE_ALI:
tbl = ali_pcms;
@@ -2068,24 +2047,26 @@ static int __devinit snd_intel8x0_mixer(struct intel8x0 *chip, int ac97_clock,
};
chip->spdif_idx = -1; /* use PCMOUT (or disabled) */
- switch (chip->device_type) {
- case DEVICE_NFORCE:
- chip->spdif_idx = NVD_SPBAR;
- break;
- case DEVICE_ALI:
- chip->spdif_idx = ALID_AC97SPDIFOUT;
- break;
- case DEVICE_INTEL_ICH4:
- chip->spdif_idx = ICHD_SPBAR;
- break;
- };
+ if (!spdif_aclink) {
+ switch (chip->device_type) {
+ case DEVICE_NFORCE:
+ chip->spdif_idx = NVD_SPBAR;
+ break;
+ case DEVICE_ALI:
+ chip->spdif_idx = ALID_AC97SPDIFOUT;
+ break;
+ case DEVICE_INTEL_ICH4:
+ chip->spdif_idx = ICHD_SPBAR;
+ break;
+ };
+ }
chip->in_ac97_init = 1;
memset(&ac97, 0, sizeof(ac97));
ac97.private_data = chip;
ac97.private_free = snd_intel8x0_mixer_free_ac97;
- ac97.scaps = AC97_SCAP_SKIP_MODEM;
+ ac97.scaps = AC97_SCAP_SKIP_MODEM | AC97_SCAP_POWER_SAVE;
if (chip->xbox)
ac97.scaps |= AC97_SCAP_DETECT_BY_VENDOR;
if (chip->device_type != DEVICE_ALI) {
@@ -2201,11 +2182,11 @@ static int __devinit snd_intel8x0_mixer(struct intel8x0 *chip, int ac97_clock,
if ((igetdword(chip, ICHREG(GLOB_STA)) & ICH_SAMPLE_CAP) == ICH_SAMPLE_16_20)
chip->smp20bit = 1;
}
- if (chip->device_type == DEVICE_NFORCE) {
+ if (chip->device_type == DEVICE_NFORCE && !spdif_aclink) {
/* 48kHz only */
chip->ichd[chip->spdif_idx].pcm->rates = SNDRV_PCM_RATE_48000;
}
- if (chip->device_type == DEVICE_INTEL_ICH4) {
+ if (chip->device_type == DEVICE_INTEL_ICH4 && !spdif_aclink) {
/* use slot 10/11 for SPDIF */
u32 val;
val = igetdword(chip, ICHREG(GLOB_CNT)) & ~ICH_PCM_SPDIF_MASK;
@@ -2333,7 +2314,7 @@ static int snd_intel8x0_ich_chip_init(struct intel8x0 *chip, int probing)
/* unmute the output on SIS7012 */
iputword(chip, 0x4c, igetword(chip, 0x4c) | 1);
}
- if (chip->device_type == DEVICE_NFORCE) {
+ if (chip->device_type == DEVICE_NFORCE && !spdif_aclink) {
/* enable SPDIF interrupt */
unsigned int val;
pci_read_config_dword(chip->pci, 0x4c, &val);
@@ -2426,7 +2407,7 @@ static int snd_intel8x0_free(struct intel8x0 *chip)
/* reset channels */
for (i = 0; i < chip->bdbars_count; i++)
iputbyte(chip, ICH_REG_OFF_CR + chip->ichd[i].reg_offset, ICH_RESETREGS);
- if (chip->device_type == DEVICE_NFORCE) {
+ if (chip->device_type == DEVICE_NFORCE && !spdif_aclink) {
/* stop the spdif interrupt */
unsigned int val;
pci_read_config_dword(chip->pci, 0x4c, &val);
@@ -2443,10 +2424,10 @@ static int snd_intel8x0_free(struct intel8x0 *chip)
fill_nocache(chip->bdbars.area, chip->bdbars.bytes, 0);
snd_dma_free_pages(&chip->bdbars);
}
- if (chip->remap_addr)
- iounmap(chip->remap_addr);
- if (chip->remap_bmaddr)
- iounmap(chip->remap_bmaddr);
+ if (chip->addr)
+ pci_iounmap(chip->pci, chip->addr);
+ if (chip->bmaddr)
+ pci_iounmap(chip->pci, chip->bmaddr);
pci_release_regions(chip->pci);
pci_disable_device(chip->pci);
kfree(chip);
@@ -2520,7 +2501,7 @@ static int intel8x0_resume(struct pci_dev *pci)
snd_intel8x0_chip_init(chip, 0);
/* re-initialize mixer stuff */
- if (chip->device_type == DEVICE_INTEL_ICH4) {
+ if (chip->device_type == DEVICE_INTEL_ICH4 && !spdif_aclink) {
/* enable separate SDINs for ICH4 */
iputbyte(chip, ICHREG(SDM), chip->sdm_saved);
/* use slot 10/11 for SPDIF */
@@ -2793,35 +2774,27 @@ static int __devinit snd_intel8x0_create(struct snd_card *card,
if (device_type == DEVICE_ALI) {
/* ALI5455 has no ac97 region */
- chip->bmaddr = pci_resource_start(pci, 0);
+ chip->bmaddr = pci_iomap(pci, 0, 0);
goto port_inited;
}
- if (pci_resource_flags(pci, 2) & IORESOURCE_MEM) { /* ICH4 and Nforce */
- chip->mmio = 1;
- chip->addr = pci_resource_start(pci, 2);
- chip->remap_addr = ioremap_nocache(chip->addr,
- pci_resource_len(pci, 2));
- if (chip->remap_addr == NULL) {
- snd_printk(KERN_ERR "AC'97 space ioremap problem\n");
- snd_intel8x0_free(chip);
- return -EIO;
- }
- } else {
- chip->addr = pci_resource_start(pci, 0);
- }
- if (pci_resource_flags(pci, 3) & IORESOURCE_MEM) { /* ICH4 */
- chip->bm_mmio = 1;
- chip->bmaddr = pci_resource_start(pci, 3);
- chip->remap_bmaddr = ioremap_nocache(chip->bmaddr,
- pci_resource_len(pci, 3));
- if (chip->remap_bmaddr == NULL) {
- snd_printk(KERN_ERR "Controller space ioremap problem\n");
- snd_intel8x0_free(chip);
- return -EIO;
- }
- } else {
- chip->bmaddr = pci_resource_start(pci, 1);
+ if (pci_resource_flags(pci, 2) & IORESOURCE_MEM) /* ICH4 and Nforce */
+ chip->addr = pci_iomap(pci, 2, 0);
+ else
+ chip->addr = pci_iomap(pci, 0, 0);
+ if (!chip->addr) {
+ snd_printk(KERN_ERR "AC'97 space ioremap problem\n");
+ snd_intel8x0_free(chip);
+ return -EIO;
+ }
+ if (pci_resource_flags(pci, 3) & IORESOURCE_MEM) /* ICH4 */
+ chip->bmaddr = pci_iomap(pci, 3, 0);
+ else
+ chip->bmaddr = pci_iomap(pci, 1, 0);
+ if (!chip->bmaddr) {
+ snd_printk(KERN_ERR "Controller space ioremap problem\n");
+ snd_intel8x0_free(chip);
+ return -EIO;
}
port_inited:
@@ -2964,6 +2937,29 @@ static struct shortname_table {
{ 0, NULL },
};
+static struct snd_pci_quirk spdif_aclink_defaults[] __devinitdata = {
+ SND_PCI_QUIRK(0x147b, 0x1c1a, "ASUS KN8", 1),
+ { } /* end */
+};
+
+/* look up white/black list for SPDIF over ac-link */
+static int __devinit check_default_spdif_aclink(struct pci_dev *pci)
+{
+ const struct snd_pci_quirk *w;
+
+ w = snd_pci_quirk_lookup(pci, spdif_aclink_defaults);
+ if (w) {
+ if (w->value)
+ snd_printdd(KERN_INFO "intel8x0: Using SPDIF over "
+ "AC-Link for %s\n", w->name);
+ else
+ snd_printdd(KERN_INFO "intel8x0: Using integrated "
+ "SPDIF DMA for %s\n", w->name);
+ return w->value;
+ }
+ return 0;
+}
+
static int __devinit snd_intel8x0_probe(struct pci_dev *pci,
const struct pci_device_id *pci_id)
{
@@ -2976,16 +2972,18 @@ static int __devinit snd_intel8x0_probe(struct pci_dev *pci,
if (card == NULL)
return -ENOMEM;
- switch (pci_id->driver_data) {
- case DEVICE_NFORCE:
- strcpy(card->driver, "NFORCE");
- break;
- case DEVICE_INTEL_ICH4:
- strcpy(card->driver, "ICH4");
- break;
- default:
- strcpy(card->driver, "ICH");
- break;
+ if (spdif_aclink < 0)
+ spdif_aclink = check_default_spdif_aclink(pci);
+
+ strcpy(card->driver, "ICH");
+ if (!spdif_aclink) {
+ switch (pci_id->driver_data) {
+ case DEVICE_NFORCE:
+ strcpy(card->driver, "NFORCE");
+ break;
+ case DEVICE_INTEL_ICH4:
+ strcpy(card->driver, "ICH4");
+ }
}
strcpy(card->shortname, "Intel ICH");
@@ -3025,8 +3023,8 @@ static int __devinit snd_intel8x0_probe(struct pci_dev *pci,
snd_intel8x0_proc_init(chip);
snprintf(card->longname, sizeof(card->longname),
- "%s with %s at %#lx, irq %i", card->shortname,
- snd_ac97_get_short_name(chip->ac97[0]), chip->addr, chip->irq);
+ "%s with %s at irq %i", card->shortname,
+ snd_ac97_get_short_name(chip->ac97[0]), chip->irq);
if (! ac97_clock)
intel8x0_measure_ac97_clock(chip);
diff --git a/sound/pci/intel8x0m.c b/sound/pci/intel8x0m.c
index 09dcf923b547..c155e1f3a0e5 100644
--- a/sound/pci/intel8x0m.c
+++ b/sound/pci/intel8x0m.c
@@ -196,12 +196,8 @@ struct intel8x0m {
int irq;
- unsigned int mmio;
- unsigned long addr;
- void __iomem *remap_addr;
- unsigned int bm_mmio;
- unsigned long bmaddr;
- void __iomem *remap_bmaddr;
+ void __iomem *addr;
+ void __iomem *bmaddr;
struct pci_dev *pci;
struct snd_card *card;
@@ -253,72 +249,48 @@ MODULE_DEVICE_TABLE(pci, snd_intel8x0m_ids);
* Lowlevel I/O - busmaster
*/
-static u8 igetbyte(struct intel8x0m *chip, u32 offset)
+static inline u8 igetbyte(struct intel8x0m *chip, u32 offset)
{
- if (chip->bm_mmio)
- return readb(chip->remap_bmaddr + offset);
- else
- return inb(chip->bmaddr + offset);
+ return ioread8(chip->bmaddr + offset);
}
-static u16 igetword(struct intel8x0m *chip, u32 offset)
+static inline u16 igetword(struct intel8x0m *chip, u32 offset)
{
- if (chip->bm_mmio)
- return readw(chip->remap_bmaddr + offset);
- else
- return inw(chip->bmaddr + offset);
+ return ioread16(chip->bmaddr + offset);
}
-static u32 igetdword(struct intel8x0m *chip, u32 offset)
+static inline u32 igetdword(struct intel8x0m *chip, u32 offset)
{
- if (chip->bm_mmio)
- return readl(chip->remap_bmaddr + offset);
- else
- return inl(chip->bmaddr + offset);
+ return ioread32(chip->bmaddr + offset);
}
-static void iputbyte(struct intel8x0m *chip, u32 offset, u8 val)
+static inline void iputbyte(struct intel8x0m *chip, u32 offset, u8 val)
{
- if (chip->bm_mmio)
- writeb(val, chip->remap_bmaddr + offset);
- else
- outb(val, chip->bmaddr + offset);
+ iowrite8(val, chip->bmaddr + offset);
}
-static void iputword(struct intel8x0m *chip, u32 offset, u16 val)
+static inline void iputword(struct intel8x0m *chip, u32 offset, u16 val)
{
- if (chip->bm_mmio)
- writew(val, chip->remap_bmaddr + offset);
- else
- outw(val, chip->bmaddr + offset);
+ iowrite16(val, chip->bmaddr + offset);
}
-static void iputdword(struct intel8x0m *chip, u32 offset, u32 val)
+static inline void iputdword(struct intel8x0m *chip, u32 offset, u32 val)
{
- if (chip->bm_mmio)
- writel(val, chip->remap_bmaddr + offset);
- else
- outl(val, chip->bmaddr + offset);
+ iowrite32(val, chip->bmaddr + offset);
}
/*
* Lowlevel I/O - AC'97 registers
*/
-static u16 iagetword(struct intel8x0m *chip, u32 offset)
+static inline u16 iagetword(struct intel8x0m *chip, u32 offset)
{
- if (chip->mmio)
- return readw(chip->remap_addr + offset);
- else
- return inw(chip->addr + offset);
+ return ioread16(chip->addr + offset);
}
-static void iaputword(struct intel8x0m *chip, u32 offset, u16 val)
+static inline void iaputword(struct intel8x0m *chip, u32 offset, u16 val)
{
- if (chip->mmio)
- writew(val, chip->remap_addr + offset);
- else
- outw(val, chip->addr + offset);
+ iowrite16(val, chip->addr + offset);
}
/*
@@ -858,7 +830,7 @@ static int __devinit snd_intel8x0_mixer(struct intel8x0m *chip, int ac97_clock)
memset(&ac97, 0, sizeof(ac97));
ac97.private_data = chip;
ac97.private_free = snd_intel8x0_mixer_free_ac97;
- ac97.scaps = AC97_SCAP_SKIP_AUDIO;
+ ac97.scaps = AC97_SCAP_SKIP_AUDIO | AC97_SCAP_POWER_SAVE;
glob_sta = igetdword(chip, ICHREG(GLOB_STA));
@@ -1019,10 +991,10 @@ static int snd_intel8x0_free(struct intel8x0m *chip)
__hw_end:
if (chip->bdbars.area)
snd_dma_free_pages(&chip->bdbars);
- if (chip->remap_addr)
- iounmap(chip->remap_addr);
- if (chip->remap_bmaddr)
- iounmap(chip->remap_bmaddr);
+ if (chip->addr)
+ pci_iounmap(chip->pci, chip->addr);
+ if (chip->bmaddr)
+ pci_iounmap(chip->pci, chip->bmaddr);
if (chip->irq >= 0)
free_irq(chip->irq, chip);
pci_release_regions(chip->pci);
@@ -1173,35 +1145,27 @@ static int __devinit snd_intel8x0m_create(struct snd_card *card,
if (device_type == DEVICE_ALI) {
/* ALI5455 has no ac97 region */
- chip->bmaddr = pci_resource_start(pci, 0);
+ chip->bmaddr = pci_iomap(pci, 0, 0);
goto port_inited;
}
- if (pci_resource_flags(pci, 2) & IORESOURCE_MEM) { /* ICH4 and Nforce */
- chip->mmio = 1;
- chip->addr = pci_resource_start(pci, 2);
- chip->remap_addr = ioremap_nocache(chip->addr,
- pci_resource_len(pci, 2));
- if (chip->remap_addr == NULL) {
- snd_printk(KERN_ERR "AC'97 space ioremap problem\n");
- snd_intel8x0_free(chip);
- return -EIO;
- }
- } else {
- chip->addr = pci_resource_start(pci, 0);
+ if (pci_resource_flags(pci, 2) & IORESOURCE_MEM) /* ICH4 and Nforce */
+ chip->addr = pci_iomap(pci, 2, 0);
+ else
+ chip->addr = pci_iomap(pci, 0, 0);
+ if (!chip->addr) {
+ snd_printk(KERN_ERR "AC'97 space ioremap problem\n");
+ snd_intel8x0_free(chip);
+ return -EIO;
}
- if (pci_resource_flags(pci, 3) & IORESOURCE_MEM) { /* ICH4 */
- chip->bm_mmio = 1;
- chip->bmaddr = pci_resource_start(pci, 3);
- chip->remap_bmaddr = ioremap_nocache(chip->bmaddr,
- pci_resource_len(pci, 3));
- if (chip->remap_bmaddr == NULL) {
- snd_printk(KERN_ERR "Controller space ioremap problem\n");
- snd_intel8x0_free(chip);
- return -EIO;
- }
- } else {
- chip->bmaddr = pci_resource_start(pci, 1);
+ if (pci_resource_flags(pci, 3) & IORESOURCE_MEM) /* ICH4 */
+ chip->bmaddr = pci_iomap(pci, 3, 0);
+ else
+ chip->bmaddr = pci_iomap(pci, 1, 0);
+ if (!chip->bmaddr) {
+ snd_printk(KERN_ERR "Controller space ioremap problem\n");
+ snd_intel8x0_free(chip);
+ return -EIO;
}
port_inited:
@@ -1339,8 +1303,8 @@ static int __devinit snd_intel8x0m_probe(struct pci_dev *pci,
snd_intel8x0m_proc_init(chip);
- sprintf(card->longname, "%s at 0x%lx, irq %i",
- card->shortname, chip->addr, chip->irq);
+ sprintf(card->longname, "%s at irq %i",
+ card->shortname, chip->irq);
if ((err = snd_card_register(card)) < 0) {
snd_card_free(card);
diff --git a/sound/pci/korg1212/korg1212.c b/sound/pci/korg1212/korg1212.c
index 345eefeedb39..21d0899ac382 100644
--- a/sound/pci/korg1212/korg1212.c
+++ b/sound/pci/korg1212/korg1212.c
@@ -28,6 +28,7 @@
#include <linux/wait.h>
#include <linux/moduleparam.h>
#include <linux/mutex.h>
+#include <linux/firmware.h>
#include <sound/core.h>
#include <sound/info.h>
@@ -263,7 +264,15 @@ enum MonitorModeSelector {
#define COMMAND_ACK_DELAY 13 // number of RTC ticks to wait for an acknowledgement
// from the card after sending a command.
+#define FIRMWARE_IN_THE_KERNEL
+
+#ifdef FIRMWARE_IN_THE_KERNEL
#include "korg1212-firmware.h"
+static const struct firmware static_dsp_code = {
+ .data = (u8 *)dspCode,
+ .size = sizeof dspCode
+};
+#endif
enum ClockSourceIndex {
K1212_CLKIDX_AdatAt44_1K = 0, // selects source as ADAT at 44.1 kHz
@@ -345,8 +354,6 @@ struct snd_korg1212 {
struct snd_dma_buffer dma_rec;
struct snd_dma_buffer dma_shared;
- u32 dspCodeSize;
-
u32 DataBufsSize;
struct KorgAudioBuffer * playDataBufsPtr;
@@ -1223,8 +1230,6 @@ static int snd_korg1212_downloadDSPCode(struct snd_korg1212 *korg1212)
snd_korg1212_setCardState(korg1212, K1212_STATE_DSP_IN_PROCESS);
- memcpy(korg1212->dma_dsp.area, dspCode, korg1212->dspCodeSize);
-
rc = snd_korg1212_Send1212Command(korg1212, K1212_DB_StartDSPDownload,
UpperWordSwap(korg1212->dma_dsp.addr),
0, 0, 0);
@@ -2156,6 +2161,7 @@ static int __devinit snd_korg1212_create(struct snd_card *card, struct pci_dev *
unsigned int i;
unsigned ioport_size, iomem_size, iomem2_size;
struct snd_korg1212 * korg1212;
+ const struct firmware *dsp_code;
static struct snd_device_ops ops = {
.dev_free = snd_korg1212_dev_free,
@@ -2329,8 +2335,6 @@ static int __devinit snd_korg1212_create(struct snd_card *card, struct pci_dev *
#endif // K1212_LARGEALLOC
- korg1212->dspCodeSize = sizeof (dspCode);
-
korg1212->VolumeTablePhy = korg1212->sharedBufferPhy +
offsetof(struct KorgSharedBuffer, volumeData);
korg1212->RoutingTablePhy = korg1212->sharedBufferPhy +
@@ -2338,17 +2342,40 @@ static int __devinit snd_korg1212_create(struct snd_card *card, struct pci_dev *
korg1212->AdatTimeCodePhy = korg1212->sharedBufferPhy +
offsetof(struct KorgSharedBuffer, AdatTimeCode);
+ err = request_firmware(&dsp_code, "korg/k1212.dsp", &pci->dev);
+ if (err < 0) {
+ release_firmware(dsp_code);
+#ifdef FIRMWARE_IN_THE_KERNEL
+ dsp_code = &static_dsp_code;
+#else
+ snd_printk(KERN_ERR "firmware not available\n");
+ snd_korg1212_free(korg1212);
+ return err;
+#endif
+ }
+
if (snd_dma_alloc_pages(SNDRV_DMA_TYPE_DEV, snd_dma_pci_data(pci),
- korg1212->dspCodeSize, &korg1212->dma_dsp) < 0) {
- snd_printk(KERN_ERR "korg1212: can not allocate dsp code memory (%d bytes)\n", korg1212->dspCodeSize);
+ dsp_code->size, &korg1212->dma_dsp) < 0) {
+ snd_printk(KERN_ERR "korg1212: cannot allocate dsp code memory (%zd bytes)\n", dsp_code->size);
snd_korg1212_free(korg1212);
+#ifdef FIRMWARE_IN_THE_KERNEL
+ if (dsp_code != &static_dsp_code)
+#endif
+ release_firmware(dsp_code);
return -ENOMEM;
}
K1212_DEBUG_PRINTK("K1212_DEBUG: DSP Code area = 0x%p (0x%08x) %d bytes [%s]\n",
- korg1212->dma_dsp.area, korg1212->dma_dsp.addr, korg1212->dspCodeSize,
+ korg1212->dma_dsp.area, korg1212->dma_dsp.addr, dsp_code->size,
stateName[korg1212->cardState]);
+ memcpy(korg1212->dma_dsp.area, dsp_code->data, dsp_code->size);
+
+#ifdef FIRMWARE_IN_THE_KERNEL
+ if (dsp_code != &static_dsp_code)
+#endif
+ release_firmware(dsp_code);
+
rc = snd_korg1212_Send1212Command(korg1212, K1212_DB_RebootCard, 0, 0, 0, 0);
if (rc)
diff --git a/sound/pci/maestro3.c b/sound/pci/maestro3.c
index 6efe6d5ade1e..4526904e3f86 100644
--- a/sound/pci/maestro3.c
+++ b/sound/pci/maestro3.c
@@ -41,6 +41,7 @@
#include <linux/slab.h>
#include <linux/vmalloc.h>
#include <linux/moduleparam.h>
+#include <linux/firmware.h>
#include <sound/core.h>
#include <sound/info.h>
#include <sound/control.h>
@@ -48,6 +49,7 @@
#include <sound/mpu401.h>
#include <sound/ac97_codec.h>
#include <sound/initval.h>
+#include <asm/byteorder.h>
MODULE_AUTHOR("Zach Brown <zab@zabbo.net>, Takashi Iwai <tiwai@suse.de>");
MODULE_DESCRIPTION("ESS Maestro3 PCI");
@@ -768,21 +770,6 @@ MODULE_PARM_DESC(amp_gpio, "GPIO pin number for external amp. (default = -1)");
/*
*/
-/* quirk lists */
-struct m3_quirk {
- const char *name; /* device name */
- u16 vendor, device; /* subsystem ids */
- int amp_gpio; /* gpio pin # for external amp, -1 = default */
- int irda_workaround; /* non-zero if avoid to touch 0x10 on GPIO_DIRECTION
- (e.g. for IrDA on Dell Inspirons) */
-};
-
-struct m3_hv_quirk {
- u16 vendor, device, subsystem_vendor, subsystem_device;
- u32 config; /* ALLEGRO_CONFIG hardware volume bits */
- int is_omnibook; /* Do HP OmniBook GPIO magic? */
-};
-
struct m3_list {
int curlen;
int mem_addr;
@@ -830,8 +817,6 @@ struct snd_m3 {
struct snd_pcm *pcm;
struct pci_dev *pci;
- const struct m3_quirk *quirk;
- const struct m3_hv_quirk *hv_quirk;
int dacs_active;
int timer_users;
@@ -845,7 +830,11 @@ struct snd_m3 {
u8 reset_state;
int external_amp;
- int amp_gpio;
+ int amp_gpio; /* gpio pin # for external amp, -1 = default */
+ unsigned int hv_config; /* hardware-volume config bits */
+ unsigned irda_workaround :1; /* avoid to touch 0x10 on GPIO_DIRECTION
+ (e.g. for IrDA on Dell Inspirons) */
+ unsigned is_omnibook :1; /* Do HP OmniBook GPIO magic? */
/* midi */
struct snd_rawmidi *rmidi;
@@ -864,6 +853,9 @@ struct snd_m3 {
#ifdef CONFIG_PM
u16 *suspend_mem;
#endif
+
+ const struct firmware *assp_kernel_image;
+ const struct firmware *assp_minisrc_image;
};
/*
@@ -891,127 +883,104 @@ static struct pci_device_id snd_m3_ids[] = {
MODULE_DEVICE_TABLE(pci, snd_m3_ids);
-static const struct m3_quirk m3_quirk_list[] = {
- /* panasonic CF-28 "toughbook" */
- {
- .name = "Panasonic CF-28",
- .vendor = 0x10f7,
- .device = 0x833e,
- .amp_gpio = 0x0d,
- },
- /* panasonic CF-72 "toughbook" */
- {
- .name = "Panasonic CF-72",
- .vendor = 0x10f7,
- .device = 0x833d,
- .amp_gpio = 0x0d,
- },
- /* Dell Inspiron 4000 */
- {
- .name = "Dell Inspiron 4000",
- .vendor = 0x1028,
- .device = 0x00b0,
- .amp_gpio = -1,
- .irda_workaround = 1,
- },
- /* Dell Inspiron 8000 */
- {
- .name = "Dell Inspiron 8000",
- .vendor = 0x1028,
- .device = 0x00a4,
- .amp_gpio = -1,
- .irda_workaround = 1,
- },
- /* Dell Inspiron 8100 */
- {
- .name = "Dell Inspiron 8100",
- .vendor = 0x1028,
- .device = 0x00e6,
- .amp_gpio = -1,
- .irda_workaround = 1,
- },
- /* NEC LM800J/7 */
- {
- .name = "NEC LM800J/7",
- .vendor = 0x1033,
- .device = 0x80f1,
- .amp_gpio = 0x03,
- },
- /* LEGEND ZhaoYang 3100CF */
- {
- .name = "LEGEND ZhaoYang 3100CF",
- .vendor = 0x1509,
- .device = 0x1740,
- .amp_gpio = 0x03,
- },
- /* END */
- { NULL }
+static struct snd_pci_quirk m3_amp_quirk_list[] __devinitdata = {
+ SND_PCI_QUIRK(0x10f7, 0x833e, "Panasonic CF-28", 0x0d),
+ SND_PCI_QUIRK(0x10f7, 0x833d, "Panasonic CF-72", 0x0d),
+ SND_PCI_QUIRK(0x1033, 0x80f1, "NEC LM800J/7", 0x03),
+ SND_PCI_QUIRK(0x1509, 0x1740, "LEGEND ZhaoYang 3100CF", 0x03),
+ { } /* END */
};
-/* These values came from the Windows driver. */
-static const struct m3_hv_quirk m3_hv_quirk_list[] = {
+static struct snd_pci_quirk m3_irda_quirk_list[] __devinitdata = {
+ SND_PCI_QUIRK(0x1028, 0x00b0, "Dell Inspiron 4000", 1),
+ SND_PCI_QUIRK(0x1028, 0x00a4, "Dell Inspiron 8000", 1),
+ SND_PCI_QUIRK(0x1028, 0x00e6, "Dell Inspiron 8100", 1),
+ { } /* END */
+};
+
+/* hardware volume quirks */
+static struct snd_pci_quirk m3_hv_quirk_list[] __devinitdata = {
/* Allegro chips */
- { 0x125D, 0x1988, 0x0E11, 0x002E, HV_CTRL_ENABLE | HV_BUTTON_FROM_GD, 0 },
- { 0x125D, 0x1988, 0x0E11, 0x0094, HV_CTRL_ENABLE | HV_BUTTON_FROM_GD, 0 },
- { 0x125D, 0x1988, 0x0E11, 0xB112, HV_CTRL_ENABLE | HV_BUTTON_FROM_GD, 0 },
- { 0x125D, 0x1988, 0x0E11, 0xB114, HV_CTRL_ENABLE | HV_BUTTON_FROM_GD, 0 },
- { 0x125D, 0x1988, 0x103C, 0x0012, HV_CTRL_ENABLE | HV_BUTTON_FROM_GD, 0 },
- { 0x125D, 0x1988, 0x103C, 0x0018, HV_CTRL_ENABLE | HV_BUTTON_FROM_GD, 0 },
- { 0x125D, 0x1988, 0x103C, 0x001C, HV_CTRL_ENABLE | HV_BUTTON_FROM_GD, 0 },
- { 0x125D, 0x1988, 0x103C, 0x001D, HV_CTRL_ENABLE | HV_BUTTON_FROM_GD, 0 },
- { 0x125D, 0x1988, 0x103C, 0x001E, HV_CTRL_ENABLE | HV_BUTTON_FROM_GD, 0 },
- { 0x125D, 0x1988, 0x107B, 0x3350, HV_CTRL_ENABLE | HV_BUTTON_FROM_GD, 0 },
- { 0x125D, 0x1988, 0x10F7, 0x8338, HV_CTRL_ENABLE | HV_BUTTON_FROM_GD, 0 },
- { 0x125D, 0x1988, 0x10F7, 0x833C, HV_CTRL_ENABLE | HV_BUTTON_FROM_GD, 0 },
- { 0x125D, 0x1988, 0x10F7, 0x833D, HV_CTRL_ENABLE | HV_BUTTON_FROM_GD, 0 },
- { 0x125D, 0x1988, 0x10F7, 0x833E, HV_CTRL_ENABLE | HV_BUTTON_FROM_GD, 0 },
- { 0x125D, 0x1988, 0x10F7, 0x833F, HV_CTRL_ENABLE | HV_BUTTON_FROM_GD, 0 },
- { 0x125D, 0x1988, 0x13BD, 0x1018, HV_CTRL_ENABLE | HV_BUTTON_FROM_GD, 0 },
- { 0x125D, 0x1988, 0x13BD, 0x1019, HV_CTRL_ENABLE | HV_BUTTON_FROM_GD, 0 },
- { 0x125D, 0x1988, 0x13BD, 0x101A, HV_CTRL_ENABLE | HV_BUTTON_FROM_GD, 0 },
- { 0x125D, 0x1988, 0x14FF, 0x0F03, HV_CTRL_ENABLE | HV_BUTTON_FROM_GD, 0 },
- { 0x125D, 0x1988, 0x14FF, 0x0F04, HV_CTRL_ENABLE | HV_BUTTON_FROM_GD, 0 },
- { 0x125D, 0x1988, 0x14FF, 0x0F05, HV_CTRL_ENABLE | HV_BUTTON_FROM_GD, 0 },
- { 0x125D, 0x1988, 0x156D, 0xB400, HV_CTRL_ENABLE | HV_BUTTON_FROM_GD, 0 },
- { 0x125D, 0x1988, 0x156D, 0xB795, HV_CTRL_ENABLE | HV_BUTTON_FROM_GD, 0 },
- { 0x125D, 0x1988, 0x156D, 0xB797, HV_CTRL_ENABLE | HV_BUTTON_FROM_GD, 0 },
- { 0x125D, 0x1988, 0x156D, 0xC700, HV_CTRL_ENABLE | HV_BUTTON_FROM_GD, 0 },
- { 0x125D, 0x1988, 0x1033, 0x80F1, HV_CTRL_ENABLE | HV_BUTTON_FROM_GD | REDUCED_DEBOUNCE, 0 },
- { 0x125D, 0x1988, 0x103C, 0x001A, HV_CTRL_ENABLE | HV_BUTTON_FROM_GD | REDUCED_DEBOUNCE, 0 }, /* HP OmniBook 6100 */
- { 0x125D, 0x1988, 0x107B, 0x340A, HV_CTRL_ENABLE | HV_BUTTON_FROM_GD | REDUCED_DEBOUNCE, 0 },
- { 0x125D, 0x1988, 0x107B, 0x3450, HV_CTRL_ENABLE | HV_BUTTON_FROM_GD | REDUCED_DEBOUNCE, 0 },
- { 0x125D, 0x1988, 0x109F, 0x3134, HV_CTRL_ENABLE | HV_BUTTON_FROM_GD | REDUCED_DEBOUNCE, 0 },
- { 0x125D, 0x1988, 0x109F, 0x3161, HV_CTRL_ENABLE | HV_BUTTON_FROM_GD | REDUCED_DEBOUNCE, 0 },
- { 0x125D, 0x1988, 0x144D, 0x3280, HV_CTRL_ENABLE | HV_BUTTON_FROM_GD | REDUCED_DEBOUNCE, 0 },
- { 0x125D, 0x1988, 0x144D, 0x3281, HV_CTRL_ENABLE | HV_BUTTON_FROM_GD | REDUCED_DEBOUNCE, 0 },
- { 0x125D, 0x1988, 0x144D, 0xC002, HV_CTRL_ENABLE | HV_BUTTON_FROM_GD | REDUCED_DEBOUNCE, 0 },
- { 0x125D, 0x1988, 0x144D, 0xC003, HV_CTRL_ENABLE | HV_BUTTON_FROM_GD | REDUCED_DEBOUNCE, 0 },
- { 0x125D, 0x1988, 0x1509, 0x1740, HV_CTRL_ENABLE | HV_BUTTON_FROM_GD | REDUCED_DEBOUNCE, 0 },
- { 0x125D, 0x1988, 0x1610, 0x0010, HV_CTRL_ENABLE | HV_BUTTON_FROM_GD | REDUCED_DEBOUNCE, 0 },
- { 0x125D, 0x1988, 0x1042, 0x1042, HV_CTRL_ENABLE, 0 },
- { 0x125D, 0x1988, 0x107B, 0x9500, HV_CTRL_ENABLE, 0 },
- { 0x125D, 0x1988, 0x14FF, 0x0F06, HV_CTRL_ENABLE, 0 },
- { 0x125D, 0x1988, 0x1558, 0x8586, HV_CTRL_ENABLE, 0 },
- { 0x125D, 0x1988, 0x161F, 0x2011, HV_CTRL_ENABLE, 0 },
+ SND_PCI_QUIRK(0x0E11, 0x002E, NULL, HV_CTRL_ENABLE | HV_BUTTON_FROM_GD),
+ SND_PCI_QUIRK(0x0E11, 0x0094, NULL, HV_CTRL_ENABLE | HV_BUTTON_FROM_GD),
+ SND_PCI_QUIRK(0x0E11, 0xB112, NULL, HV_CTRL_ENABLE | HV_BUTTON_FROM_GD),
+ SND_PCI_QUIRK(0x0E11, 0xB114, NULL, HV_CTRL_ENABLE | HV_BUTTON_FROM_GD),
+ SND_PCI_QUIRK(0x103C, 0x0012, NULL, HV_CTRL_ENABLE | HV_BUTTON_FROM_GD),
+ SND_PCI_QUIRK(0x103C, 0x0018, NULL, HV_CTRL_ENABLE | HV_BUTTON_FROM_GD),
+ SND_PCI_QUIRK(0x103C, 0x001C, NULL, HV_CTRL_ENABLE | HV_BUTTON_FROM_GD),
+ SND_PCI_QUIRK(0x103C, 0x001D, NULL, HV_CTRL_ENABLE | HV_BUTTON_FROM_GD),
+ SND_PCI_QUIRK(0x103C, 0x001E, NULL, HV_CTRL_ENABLE | HV_BUTTON_FROM_GD),
+ SND_PCI_QUIRK(0x107B, 0x3350, NULL, HV_CTRL_ENABLE | HV_BUTTON_FROM_GD),
+ SND_PCI_QUIRK(0x10F7, 0x8338, NULL, HV_CTRL_ENABLE | HV_BUTTON_FROM_GD),
+ SND_PCI_QUIRK(0x10F7, 0x833C, NULL, HV_CTRL_ENABLE | HV_BUTTON_FROM_GD),
+ SND_PCI_QUIRK(0x10F7, 0x833D, NULL, HV_CTRL_ENABLE | HV_BUTTON_FROM_GD),
+ SND_PCI_QUIRK(0x10F7, 0x833E, NULL, HV_CTRL_ENABLE | HV_BUTTON_FROM_GD),
+ SND_PCI_QUIRK(0x10F7, 0x833F, NULL, HV_CTRL_ENABLE | HV_BUTTON_FROM_GD),
+ SND_PCI_QUIRK(0x13BD, 0x1018, NULL, HV_CTRL_ENABLE | HV_BUTTON_FROM_GD),
+ SND_PCI_QUIRK(0x13BD, 0x1019, NULL, HV_CTRL_ENABLE | HV_BUTTON_FROM_GD),
+ SND_PCI_QUIRK(0x13BD, 0x101A, NULL, HV_CTRL_ENABLE | HV_BUTTON_FROM_GD),
+ SND_PCI_QUIRK(0x14FF, 0x0F03, NULL, HV_CTRL_ENABLE | HV_BUTTON_FROM_GD),
+ SND_PCI_QUIRK(0x14FF, 0x0F04, NULL, HV_CTRL_ENABLE | HV_BUTTON_FROM_GD),
+ SND_PCI_QUIRK(0x14FF, 0x0F05, NULL, HV_CTRL_ENABLE | HV_BUTTON_FROM_GD),
+ SND_PCI_QUIRK(0x156D, 0xB400, NULL, HV_CTRL_ENABLE | HV_BUTTON_FROM_GD),
+ SND_PCI_QUIRK(0x156D, 0xB795, NULL, HV_CTRL_ENABLE | HV_BUTTON_FROM_GD),
+ SND_PCI_QUIRK(0x156D, 0xB797, NULL, HV_CTRL_ENABLE | HV_BUTTON_FROM_GD),
+ SND_PCI_QUIRK(0x156D, 0xC700, NULL, HV_CTRL_ENABLE | HV_BUTTON_FROM_GD),
+ SND_PCI_QUIRK(0x1033, 0x80F1, NULL,
+ HV_CTRL_ENABLE | HV_BUTTON_FROM_GD | REDUCED_DEBOUNCE),
+ SND_PCI_QUIRK(0x103C, 0x001A, NULL, /* HP OmniBook 6100 */
+ HV_CTRL_ENABLE | HV_BUTTON_FROM_GD | REDUCED_DEBOUNCE),
+ SND_PCI_QUIRK(0x107B, 0x340A, NULL,
+ HV_CTRL_ENABLE | HV_BUTTON_FROM_GD | REDUCED_DEBOUNCE),
+ SND_PCI_QUIRK(0x107B, 0x3450, NULL,
+ HV_CTRL_ENABLE | HV_BUTTON_FROM_GD | REDUCED_DEBOUNCE),
+ SND_PCI_QUIRK(0x109F, 0x3134, NULL,
+ HV_CTRL_ENABLE | HV_BUTTON_FROM_GD | REDUCED_DEBOUNCE),
+ SND_PCI_QUIRK(0x109F, 0x3161, NULL,
+ HV_CTRL_ENABLE | HV_BUTTON_FROM_GD | REDUCED_DEBOUNCE),
+ SND_PCI_QUIRK(0x144D, 0x3280, NULL,
+ HV_CTRL_ENABLE | HV_BUTTON_FROM_GD | REDUCED_DEBOUNCE),
+ SND_PCI_QUIRK(0x144D, 0x3281, NULL,
+ HV_CTRL_ENABLE | HV_BUTTON_FROM_GD | REDUCED_DEBOUNCE),
+ SND_PCI_QUIRK(0x144D, 0xC002, NULL,
+ HV_CTRL_ENABLE | HV_BUTTON_FROM_GD | REDUCED_DEBOUNCE),
+ SND_PCI_QUIRK(0x144D, 0xC003, NULL,
+ HV_CTRL_ENABLE | HV_BUTTON_FROM_GD | REDUCED_DEBOUNCE),
+ SND_PCI_QUIRK(0x1509, 0x1740, NULL,
+ HV_CTRL_ENABLE | HV_BUTTON_FROM_GD | REDUCED_DEBOUNCE),
+ SND_PCI_QUIRK(0x1610, 0x0010, NULL,
+ HV_CTRL_ENABLE | HV_BUTTON_FROM_GD | REDUCED_DEBOUNCE),
+ SND_PCI_QUIRK(0x1042, 0x1042, NULL, HV_CTRL_ENABLE),
+ SND_PCI_QUIRK(0x107B, 0x9500, NULL, HV_CTRL_ENABLE),
+ SND_PCI_QUIRK(0x14FF, 0x0F06, NULL, HV_CTRL_ENABLE),
+ SND_PCI_QUIRK(0x1558, 0x8586, NULL, HV_CTRL_ENABLE),
+ SND_PCI_QUIRK(0x161F, 0x2011, NULL, HV_CTRL_ENABLE),
/* Maestro3 chips */
- { 0x125D, 0x1998, 0x103C, 0x000E, HV_CTRL_ENABLE, 0 },
- { 0x125D, 0x1998, 0x103C, 0x0010, HV_CTRL_ENABLE, 1 }, /* HP OmniBook 6000 */
- { 0x125D, 0x1998, 0x103C, 0x0011, HV_CTRL_ENABLE, 1 }, /* HP OmniBook 500 */
- { 0x125D, 0x1998, 0x103C, 0x001B, HV_CTRL_ENABLE, 0 },
- { 0x125D, 0x1998, 0x104D, 0x80A6, HV_CTRL_ENABLE, 0 },
- { 0x125D, 0x1998, 0x104D, 0x80AA, HV_CTRL_ENABLE, 0 },
- { 0x125D, 0x1998, 0x107B, 0x5300, HV_CTRL_ENABLE, 0 },
- { 0x125D, 0x1998, 0x110A, 0x1998, HV_CTRL_ENABLE, 0 },
- { 0x125D, 0x1998, 0x13BD, 0x1015, HV_CTRL_ENABLE, 0 },
- { 0x125D, 0x1998, 0x13BD, 0x101C, HV_CTRL_ENABLE, 0 },
- { 0x125D, 0x1998, 0x13BD, 0x1802, HV_CTRL_ENABLE, 0 },
- { 0x125D, 0x1998, 0x1599, 0x0715, HV_CTRL_ENABLE, 0 },
- { 0x125D, 0x1998, 0x5643, 0x5643, HV_CTRL_ENABLE, 0 },
- { 0x125D, 0x199A, 0x144D, 0x3260, HV_CTRL_ENABLE | REDUCED_DEBOUNCE, 0 },
- { 0x125D, 0x199A, 0x144D, 0x3261, HV_CTRL_ENABLE | REDUCED_DEBOUNCE, 0 },
- { 0x125D, 0x199A, 0x144D, 0xC000, HV_CTRL_ENABLE | REDUCED_DEBOUNCE, 0 },
- { 0x125D, 0x199A, 0x144D, 0xC001, HV_CTRL_ENABLE | REDUCED_DEBOUNCE, 0 },
- { 0 }
+ SND_PCI_QUIRK(0x103C, 0x000E, NULL, HV_CTRL_ENABLE),
+ SND_PCI_QUIRK(0x103C, 0x0010, NULL, HV_CTRL_ENABLE),
+ SND_PCI_QUIRK(0x103C, 0x0011, NULL, HV_CTRL_ENABLE),
+ SND_PCI_QUIRK(0x103C, 0x001B, NULL, HV_CTRL_ENABLE),
+ SND_PCI_QUIRK(0x104D, 0x80A6, NULL, HV_CTRL_ENABLE),
+ SND_PCI_QUIRK(0x104D, 0x80AA, NULL, HV_CTRL_ENABLE),
+ SND_PCI_QUIRK(0x107B, 0x5300, NULL, HV_CTRL_ENABLE),
+ SND_PCI_QUIRK(0x110A, 0x1998, NULL, HV_CTRL_ENABLE),
+ SND_PCI_QUIRK(0x13BD, 0x1015, NULL, HV_CTRL_ENABLE),
+ SND_PCI_QUIRK(0x13BD, 0x101C, NULL, HV_CTRL_ENABLE),
+ SND_PCI_QUIRK(0x13BD, 0x1802, NULL, HV_CTRL_ENABLE),
+ SND_PCI_QUIRK(0x1599, 0x0715, NULL, HV_CTRL_ENABLE),
+ SND_PCI_QUIRK(0x5643, 0x5643, NULL, HV_CTRL_ENABLE),
+ SND_PCI_QUIRK(0x144D, 0x3260, NULL, HV_CTRL_ENABLE | REDUCED_DEBOUNCE),
+ SND_PCI_QUIRK(0x144D, 0x3261, NULL, HV_CTRL_ENABLE | REDUCED_DEBOUNCE),
+ SND_PCI_QUIRK(0x144D, 0xC000, NULL, HV_CTRL_ENABLE | REDUCED_DEBOUNCE),
+ SND_PCI_QUIRK(0x144D, 0xC001, NULL, HV_CTRL_ENABLE | REDUCED_DEBOUNCE),
+ { } /* END */
+};
+
+/* HP Omnibook quirks */
+static struct snd_pci_quirk m3_omnibook_quirk_list[] __devinitdata = {
+ SND_PCI_QUIRK_ID(0x103c, 0x0010), /* HP OmniBook 6000 */
+ SND_PCI_QUIRK_ID(0x103c, 0x0011), /* HP OmniBook 500 */
+ { } /* END */
};
/*
@@ -2050,7 +2019,7 @@ static void snd_m3_ac97_reset(struct snd_m3 *chip)
for (i = 0; i < 5; i++) {
dir = inw(io + GPIO_DIRECTION);
- if (! chip->quirk || ! chip->quirk->irda_workaround)
+ if (!chip->irda_workaround)
dir |= 0x10; /* assuming pci bus master? */
snd_m3_remote_codec_config(io, 0);
@@ -2132,6 +2101,10 @@ static int __devinit snd_m3_mixer(struct snd_m3 *chip)
}
+#define FIRMWARE_IN_THE_KERNEL
+
+#ifdef FIRMWARE_IN_THE_KERNEL
+
/*
* DSP Code images
*/
@@ -2260,6 +2233,30 @@ static const u16 assp_minisrc_image[] = {
0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
};
+static const struct firmware assp_kernel = {
+ .data = (u8 *)assp_kernel_image,
+ .size = sizeof assp_kernel_image
+};
+static const struct firmware assp_minisrc = {
+ .data = (u8 *)assp_minisrc_image,
+ .size = sizeof assp_minisrc_image
+};
+
+#endif /* FIRMWARE_IN_THE_KERNEL */
+
+#ifdef __LITTLE_ENDIAN
+static inline void snd_m3_convert_from_le(const struct firmware *fw) { }
+#else
+static void snd_m3_convert_from_le(const struct firmware *fw)
+{
+ int i;
+ u16 *data = (u16 *)fw->data;
+
+ for (i = 0; i < fw->size / 2; ++i)
+ le16_to_cpus(&data[i]);
+}
+#endif
+
/*
* initialize ASSP
@@ -2274,6 +2271,7 @@ static const u16 minisrc_lpf[MINISRC_LPF_LEN] = {
static void snd_m3_assp_init(struct snd_m3 *chip)
{
unsigned int i;
+ u16 *data;
/* zero kernel data */
for (i = 0; i < (REV_B_DATA_MEMORY_UNIT_LENGTH * NUM_UNITS_KERNEL_DATA) / 2; i++)
@@ -2291,10 +2289,10 @@ static void snd_m3_assp_init(struct snd_m3 *chip)
KDATA_DMA_XFER0);
/* write kernel into code memory.. */
- for (i = 0 ; i < ARRAY_SIZE(assp_kernel_image); i++) {
+ data = (u16 *)chip->assp_kernel_image->data;
+ for (i = 0 ; i * 2 < chip->assp_kernel_image->size; i++) {
snd_m3_assp_write(chip, MEMTYPE_INTERNAL_CODE,
- REV_B_CODE_MEMORY_BEGIN + i,
- assp_kernel_image[i]);
+ REV_B_CODE_MEMORY_BEGIN + i, data[i]);
}
/*
@@ -2303,10 +2301,10 @@ static void snd_m3_assp_init(struct snd_m3 *chip)
* drop it there. It seems that the minisrc doesn't
* need vectors, so we won't bother with them..
*/
- for (i = 0; i < ARRAY_SIZE(assp_minisrc_image); i++) {
+ data = (u16 *)chip->assp_minisrc_image->data;
+ for (i = 0; i * 2 < chip->assp_minisrc_image->size; i++) {
snd_m3_assp_write(chip, MEMTYPE_INTERNAL_CODE,
- 0x400 + i,
- assp_minisrc_image[i]);
+ 0x400 + i, data[i]);
}
/*
@@ -2444,7 +2442,7 @@ snd_m3_chip_init(struct snd_m3 *chip)
DISABLE_LEGACY);
pci_write_config_word(pcidev, PCI_LEGACY_AUDIO_CTRL, w);
- if (chip->hv_quirk && chip->hv_quirk->is_omnibook) {
+ if (chip->is_omnibook) {
/*
* Volume buttons on some HP OmniBook laptops don't work
* correctly. This makes them work for the most part.
@@ -2461,8 +2459,7 @@ snd_m3_chip_init(struct snd_m3 *chip)
}
pci_read_config_dword(pcidev, PCI_ALLEGRO_CONFIG, &n);
n &= ~(HV_CTRL_ENABLE | REDUCED_DEBOUNCE | HV_BUTTON_FROM_GD);
- if (chip->hv_quirk)
- n |= chip->hv_quirk->config;
+ n |= chip->hv_config;
/* For some reason we must always use reduced debounce. */
n |= REDUCED_DEBOUNCE;
n |= PM_CTRL_ENABLE | CLK_DIV_BY_49 | USE_PCI_TIMING;
@@ -2510,7 +2507,7 @@ snd_m3_enable_ints(struct snd_m3 *chip)
/* TODO: MPU401 not supported yet */
val = ASSP_INT_ENABLE /*| MPU401_INT_ENABLE*/;
- if (chip->hv_quirk && (chip->hv_quirk->config & HV_CTRL_ENABLE))
+ if (chip->hv_config & HV_CTRL_ENABLE)
val |= HV_INT_ENABLE;
outw(val, io + HOST_INT_CTRL);
outb(inb(io + ASSP_CONTROL_C) | ASSP_HOST_INT_ENABLE,
@@ -2553,6 +2550,15 @@ static int snd_m3_free(struct snd_m3 *chip)
if (chip->iobase)
pci_release_regions(chip->pci);
+#ifdef FIRMWARE_IN_THE_KERNEL
+ if (chip->assp_kernel_image != &assp_kernel)
+#endif
+ release_firmware(chip->assp_kernel_image);
+#ifdef FIRMWARE_IN_THE_KERNEL
+ if (chip->assp_minisrc_image != &assp_minisrc)
+#endif
+ release_firmware(chip->assp_minisrc_image);
+
pci_disable_device(chip->pci);
kfree(chip);
return 0;
@@ -2665,8 +2671,7 @@ snd_m3_create(struct snd_card *card, struct pci_dev *pci,
{
struct snd_m3 *chip;
int i, err;
- const struct m3_quirk *quirk;
- const struct m3_hv_quirk *hv_quirk;
+ const struct snd_pci_quirk *quirk;
static struct snd_device_ops ops = {
.dev_free = snd_m3_dev_free,
};
@@ -2706,34 +2711,32 @@ snd_m3_create(struct snd_card *card, struct pci_dev *pci,
chip->pci = pci;
chip->irq = -1;
- for (quirk = m3_quirk_list; quirk->vendor; quirk++) {
- if (pci->subsystem_vendor == quirk->vendor &&
- pci->subsystem_device == quirk->device) {
- printk(KERN_INFO "maestro3: enabled hack for '%s'\n", quirk->name);
- chip->quirk = quirk;
- break;
- }
- }
-
- for (hv_quirk = m3_hv_quirk_list; hv_quirk->vendor; hv_quirk++) {
- if (pci->vendor == hv_quirk->vendor &&
- pci->device == hv_quirk->device &&
- pci->subsystem_vendor == hv_quirk->subsystem_vendor &&
- pci->subsystem_device == hv_quirk->subsystem_device) {
- chip->hv_quirk = hv_quirk;
- break;
- }
- }
-
chip->external_amp = enable_amp;
if (amp_gpio >= 0 && amp_gpio <= 0x0f)
chip->amp_gpio = amp_gpio;
- else if (chip->quirk && chip->quirk->amp_gpio >= 0)
- chip->amp_gpio = chip->quirk->amp_gpio;
- else if (chip->allegro_flag)
- chip->amp_gpio = GPO_EXT_AMP_ALLEGRO;
- else /* presumably this is for all 'maestro3's.. */
- chip->amp_gpio = GPO_EXT_AMP_M3;
+ else {
+ quirk = snd_pci_quirk_lookup(pci, m3_amp_quirk_list);
+ if (quirk) {
+ snd_printdd(KERN_INFO "maestro3: set amp-gpio "
+ "for '%s'\n", quirk->name);
+ chip->amp_gpio = quirk->value;
+ } else if (chip->allegro_flag)
+ chip->amp_gpio = GPO_EXT_AMP_ALLEGRO;
+ else /* presumably this is for all 'maestro3's.. */
+ chip->amp_gpio = GPO_EXT_AMP_M3;
+ }
+
+ quirk = snd_pci_quirk_lookup(pci, m3_irda_quirk_list);
+ if (quirk) {
+ snd_printdd(KERN_INFO "maestro3: enabled irda workaround "
+ "for '%s'\n", quirk->name);
+ chip->irda_workaround = 1;
+ }
+ quirk = snd_pci_quirk_lookup(pci, m3_hv_quirk_list);
+ if (quirk)
+ chip->hv_config = quirk->value;
+ if (snd_pci_quirk_lookup(pci, m3_omnibook_quirk_list))
+ chip->is_omnibook = 1;
chip->num_substreams = NR_DSPS;
chip->substreams = kcalloc(chip->num_substreams, sizeof(struct m3_dma),
@@ -2744,6 +2747,30 @@ snd_m3_create(struct snd_card *card, struct pci_dev *pci,
return -ENOMEM;
}
+ err = request_firmware(&chip->assp_kernel_image,
+ "ess/maestro3_assp_kernel.fw", &pci->dev);
+ if (err < 0) {
+#ifdef FIRMWARE_IN_THE_KERNEL
+ chip->assp_kernel_image = &assp_kernel;
+#else
+ snd_m3_free(chip);
+ return err;
+#endif
+ } else
+ snd_m3_convert_from_le(chip->assp_kernel_image);
+
+ err = request_firmware(&chip->assp_minisrc_image,
+ "ess/maestro3_assp_minisrc.fw", &pci->dev);
+ if (err < 0) {
+#ifdef FIRMWARE_IN_THE_KERNEL
+ chip->assp_minisrc_image = &assp_minisrc;
+#else
+ snd_m3_free(chip);
+ return err;
+#endif
+ } else
+ snd_m3_convert_from_le(chip->assp_minisrc_image);
+
if ((err = pci_request_regions(pci, card->driver)) < 0) {
snd_m3_free(chip);
return err;
diff --git a/sound/pci/mixart/mixart_mixer.c b/sound/pci/mixart/mixart_mixer.c
index 13de0f71d4b7..d7d15c036e02 100644
--- a/sound/pci/mixart/mixart_mixer.c
+++ b/sound/pci/mixart/mixart_mixer.c
@@ -389,7 +389,7 @@ static int mixart_analog_vol_put(struct snd_kcontrol *kcontrol, struct snd_ctl_e
return changed;
}
-static DECLARE_TLV_DB_SCALE(db_scale_analog, -9600, 50, 0);
+static const DECLARE_TLV_DB_SCALE(db_scale_analog, -9600, 50, 0);
static struct snd_kcontrol_new mixart_control_analog_level = {
.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
@@ -872,7 +872,7 @@ static int mixart_pcm_vol_put(struct snd_kcontrol *kcontrol, struct snd_ctl_elem
return changed;
}
-static DECLARE_TLV_DB_SCALE(db_scale_digital, -10950, 50, 0);
+static const DECLARE_TLV_DB_SCALE(db_scale_digital, -10950, 50, 0);
static struct snd_kcontrol_new snd_mixart_pcm_vol =
{
diff --git a/sound/pci/nm256/nm256.c b/sound/pci/nm256/nm256.c
index 879e31a9f9c6..03b3a4792f73 100644
--- a/sound/pci/nm256/nm256.c
+++ b/sound/pci/nm256/nm256.c
@@ -1628,23 +1628,15 @@ __error:
}
-struct nm256_quirk {
- unsigned short vendor;
- unsigned short device;
- int type;
-};
-
enum { NM_BLACKLISTED, NM_RESET_WORKAROUND, NM_RESET_WORKAROUND_2 };
-static struct nm256_quirk nm256_quirks[] __devinitdata = {
+static struct snd_pci_quirk nm256_quirks[] __devinitdata = {
/* HP omnibook 4150 has cs4232 codec internally */
- { .vendor = 0x103c, .device = 0x0007, .type = NM_BLACKLISTED },
- /* Sony PCG-F305 */
- { .vendor = 0x104d, .device = 0x8041, .type = NM_RESET_WORKAROUND },
- /* Dell Latitude LS */
- { .vendor = 0x1028, .device = 0x0080, .type = NM_RESET_WORKAROUND },
- /* Dell Latitude CSx */
- { .vendor = 0x1028, .device = 0x0091, .type = NM_RESET_WORKAROUND_2 },
+ SND_PCI_QUIRK(0x103c, 0x0007, "HP omnibook 4150", NM_BLACKLISTED),
+ /* Reset workarounds to avoid lock-ups */
+ SND_PCI_QUIRK(0x104d, 0x8041, "Sony PCG-F305", NM_RESET_WORKAROUND),
+ SND_PCI_QUIRK(0x1028, 0x0080, "Dell Latitude LS", NM_RESET_WORKAROUND),
+ SND_PCI_QUIRK(0x1028, 0x0091, "Dell Latitude CSx", NM_RESET_WORKAROUND_2),
{ } /* terminator */
};
@@ -1655,26 +1647,22 @@ static int __devinit snd_nm256_probe(struct pci_dev *pci,
struct snd_card *card;
struct nm256 *chip;
int err;
- struct nm256_quirk *q;
- u16 subsystem_vendor, subsystem_device;
-
- pci_read_config_word(pci, PCI_SUBSYSTEM_VENDOR_ID, &subsystem_vendor);
- pci_read_config_word(pci, PCI_SUBSYSTEM_ID, &subsystem_device);
-
- for (q = nm256_quirks; q->vendor; q++) {
- if (q->vendor == subsystem_vendor && q->device == subsystem_device) {
- switch (q->type) {
- case NM_BLACKLISTED:
- printk(KERN_INFO "nm256: The device is blacklisted. "
- "Loading stopped\n");
- return -ENODEV;
- case NM_RESET_WORKAROUND_2:
- reset_workaround_2 = 1;
- /* Fall-through */
- case NM_RESET_WORKAROUND:
- reset_workaround = 1;
- break;
- }
+ const struct snd_pci_quirk *q;
+
+ q = snd_pci_quirk_lookup(pci, nm256_quirks);
+ if (q) {
+ snd_printdd(KERN_INFO "nm256: Enabled quirk for %s.\n", q->name);
+ switch (q->value) {
+ case NM_BLACKLISTED:
+ printk(KERN_INFO "nm256: The device is blacklisted. "
+ "Loading stopped\n");
+ return -ENODEV;
+ case NM_RESET_WORKAROUND_2:
+ reset_workaround_2 = 1;
+ /* Fall-through */
+ case NM_RESET_WORKAROUND:
+ reset_workaround = 1;
+ break;
}
}
diff --git a/sound/pci/pcxhr/pcxhr_mixer.c b/sound/pci/pcxhr/pcxhr_mixer.c
index b133ad9e095e..d9cc8d2beb6d 100644
--- a/sound/pci/pcxhr/pcxhr_mixer.c
+++ b/sound/pci/pcxhr/pcxhr_mixer.c
@@ -44,8 +44,8 @@
#define PCXHR_ANALOG_PLAYBACK_LEVEL_MAX 128 /* 0.0 dB */
#define PCXHR_ANALOG_PLAYBACK_ZERO_LEVEL 104 /* -24.0 dB ( 0.0 dB - fix level +24.0 dB ) */
-static DECLARE_TLV_DB_SCALE(db_scale_analog_capture, -9600, 50, 0);
-static DECLARE_TLV_DB_SCALE(db_scale_analog_playback, -12800, 100, 0);
+static const DECLARE_TLV_DB_SCALE(db_scale_analog_capture, -9600, 50, 0);
+static const DECLARE_TLV_DB_SCALE(db_scale_analog_playback, -12800, 100, 0);
static int pcxhr_update_analog_audio_level(struct snd_pcxhr *chip, int is_capture, int channel)
{
@@ -195,7 +195,7 @@ static struct snd_kcontrol_new pcxhr_control_output_switch = {
#define PCXHR_DIGITAL_LEVEL_MAX 0x1ff /* +18 dB */
#define PCXHR_DIGITAL_ZERO_LEVEL 0x1b7 /* 0 dB */
-static DECLARE_TLV_DB_SCALE(db_scale_digital, -10950, 50, 0);
+static const DECLARE_TLV_DB_SCALE(db_scale_digital, -10950, 50, 0);
#define MORE_THAN_ONE_STREAM_LEVEL 0x000001
#define VALID_STREAM_PAN_LEVEL_MASK 0x800000
diff --git a/sound/pci/rme9652/hdsp.c b/sound/pci/rme9652/hdsp.c
index 6383987b460e..89b3c7ff5037 100644
--- a/sound/pci/rme9652/hdsp.c
+++ b/sound/pci/rme9652/hdsp.c
@@ -80,6 +80,7 @@ MODULE_SUPPORTED_DEVICE("{{RME Hammerfall-DSP},"
/* Write registers. These are defined as byte-offsets from the iobase value.
*/
#define HDSP_resetPointer 0
+#define HDSP_freqReg 0
#define HDSP_outputBufferAddress 32
#define HDSP_inputBufferAddress 36
#define HDSP_controlRegister 64
@@ -469,6 +470,7 @@ struct hdsp {
struct pci_dev *pci;
struct snd_kcontrol *spdif_ctl;
unsigned short mixer_matrix[HDSP_MATRIX_MIXER_SIZE];
+ unsigned int dds_value; /* last value written to freq register */
};
/* These tables map the ALSA channels 1..N to the channels that we
@@ -598,6 +600,7 @@ static int hdsp_playback_to_output_key (struct hdsp *hdsp, int in, int out)
return (64 * out) + (32 + (in));
case 0x96:
case 0x97:
+ case 0x98:
return (32 * out) + (16 + (in));
default:
return (52 * out) + (26 + (in));
@@ -611,6 +614,7 @@ static int hdsp_input_to_output_key (struct hdsp *hdsp, int in, int out)
return (64 * out) + in;
case 0x96:
case 0x97:
+ case 0x98:
return (32 * out) + in;
default:
return (52 * out) + in;
@@ -938,6 +942,11 @@ static snd_pcm_uframes_t hdsp_hw_pointer(struct hdsp *hdsp)
static void hdsp_reset_hw_pointer(struct hdsp *hdsp)
{
hdsp_write (hdsp, HDSP_resetPointer, 0);
+ if (hdsp->io_type == H9632 && hdsp->firmware_rev >= 152)
+ /* HDSP_resetPointer = HDSP_freqReg, which is strange and
+ * requires (?) to write again DDS value after a reset pointer
+ * (at least, it works like this) */
+ hdsp_write (hdsp, HDSP_freqReg, hdsp->dds_value);
}
static void hdsp_start_audio(struct hdsp *s)
@@ -982,6 +991,30 @@ static int hdsp_set_interrupt_interval(struct hdsp *s, unsigned int frames)
return 0;
}
+static void hdsp_set_dds_value(struct hdsp *hdsp, int rate)
+{
+ u64 n;
+ u32 r;
+
+ if (rate >= 112000)
+ rate /= 4;
+ else if (rate >= 56000)
+ rate /= 2;
+
+ /* RME says n = 104857600000000, but in the windows MADI driver, I see:
+// return 104857600000000 / rate; // 100 MHz
+ return 110100480000000 / rate; // 105 MHz
+ */
+ n = 104857600000000ULL; /* = 2^20 * 10^8 */
+ div64_32(&n, rate, &r);
+ /* n should be less than 2^32 for being written to FREQ register */
+ snd_assert((n >> 32) == 0);
+ /* HDSP_freqReg and HDSP_resetPointer are the same, so keep the DDS
+ value to write it after a reset */
+ hdsp->dds_value = n;
+ hdsp_write(hdsp, HDSP_freqReg, hdsp->dds_value);
+}
+
static int hdsp_set_rate(struct hdsp *hdsp, int rate, int called_internally)
{
int reject_if_open = 0;
@@ -1090,6 +1123,10 @@ static int hdsp_set_rate(struct hdsp *hdsp, int rate, int called_internally)
hdsp->control_register |= rate_bits;
hdsp_write(hdsp, HDSP_controlRegister, hdsp->control_register);
+ /* For HDSP9632 rev 152, need to set DDS value in FREQ register */
+ if (hdsp->io_type == H9632 && hdsp->firmware_rev >= 152)
+ hdsp_set_dds_value(hdsp, rate);
+
if (rate >= 128000) {
hdsp->channel_map = channel_map_H9632_qs;
} else if (rate > 48000) {
@@ -4943,6 +4980,7 @@ static int __devinit snd_hdsp_create(struct snd_card *card,
hdsp->irq = pci->irq;
hdsp->precise_ptr = 0;
hdsp->use_midi_tasklet = 1;
+ hdsp->dds_value = 0;
if ((err = snd_hdsp_initialize_memory(hdsp)) < 0)
return err;
diff --git a/sound/pci/rme9652/hdspm.c b/sound/pci/rme9652/hdspm.c
index 0547f6f04bdc..e0215aca1193 100644
--- a/sound/pci/rme9652/hdspm.c
+++ b/sound/pci/rme9652/hdspm.c
@@ -6,6 +6,8 @@
* code based on hdsp.c Paul Davis
* Marcus Andersson
* Thomas Charbonnel
+ * Modified 2006-06-01 for AES32 support by Remy Bruno
+ * <remy.bruno@trinnov.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
@@ -77,7 +79,8 @@ MODULE_PARM_DESC(enable_monitor,
MODULE_AUTHOR
("Winfried Ritsch <ritsch_AT_iem.at>, Paul Davis <paul@linuxaudiosystems.com>, "
- "Marcus Andersson, Thomas Charbonnel <thomas@undata.org>");
+ "Marcus Andersson, Thomas Charbonnel <thomas@undata.org>, "
+ "Remy Bruno <remy.bruno@trinnov.com>");
MODULE_DESCRIPTION("RME HDSPM");
MODULE_LICENSE("GPL");
MODULE_SUPPORTED_DEVICE("{{RME HDSPM-MADI}}");
@@ -107,7 +110,12 @@ MODULE_SUPPORTED_DEVICE("{{RME HDSPM-MADI}}");
/* --- Read registers. ---
These are defined as byte-offsets from the iobase value */
#define HDSPM_statusRegister 0
-#define HDSPM_statusRegister2 96
+/*#define HDSPM_statusRegister2 96 */
+/* after RME Windows driver sources, status2 is 4-byte word # 48 = word at
+ * offset 192, for AES32 *and* MADI
+ * => need to check that offset 192 is working on MADI */
+#define HDSPM_statusRegister2 192
+#define HDSPM_timecodeRegister 128
#define HDSPM_midiDataIn0 360
#define HDSPM_midiDataIn1 364
@@ -140,37 +148,50 @@ MODULE_SUPPORTED_DEVICE("{{RME HDSPM-MADI}}");
#define HDSPM_Frequency0 (1<<6) /* 0=44.1kHz/88.2kHz 1=48kHz/96kHz */
#define HDSPM_Frequency1 (1<<7) /* 0=32kHz/64kHz */
#define HDSPM_DoubleSpeed (1<<8) /* 0=normal speed, 1=double speed */
-#define HDSPM_QuadSpeed (1<<31) /* quad speed bit, not implemented now */
+#define HDSPM_QuadSpeed (1<<31) /* quad speed bit */
+#define HDSPM_Professional (1<<9) /* Professional */ /* AES32 ONLY */
#define HDSPM_TX_64ch (1<<10) /* Output 64channel MODE=1,
- 56channelMODE=0 */
+ 56channelMODE=0 */ /* MADI ONLY*/
+#define HDSPM_Emphasis (1<<10) /* Emphasis */ /* AES32 ONLY */
#define HDSPM_AutoInp (1<<11) /* Auto Input (takeover) == Safe Mode,
- 0=off, 1=on */
+ 0=off, 1=on */ /* MADI ONLY */
+#define HDSPM_Dolby (1<<11) /* Dolby = "NonAudio" ?? */ /* AES32 ONLY */
-#define HDSPM_InputSelect0 (1<<14) /* Input select 0= optical, 1=coax */
+#define HDSPM_InputSelect0 (1<<14) /* Input select 0= optical, 1=coax */ /* MADI ONLY*/
#define HDSPM_InputSelect1 (1<<15) /* should be 0 */
#define HDSPM_SyncRef0 (1<<16) /* 0=WOrd, 1=MADI */
-#define HDSPM_SyncRef1 (1<<17) /* should be 0 */
+#define HDSPM_SyncRef1 (1<<17) /* for AES32: SyncRefN codes the AES # */
+#define HDSPM_SyncRef2 (1<<13)
+#define HDSPM_SyncRef3 (1<<25)
+#define HDSPM_SMUX (1<<18) /* Frame ??? */ /* MADI ONY */
#define HDSPM_clr_tms (1<<19) /* clear track marker, do not use
AES additional bits in
lower 5 Audiodatabits ??? */
+#define HDSPM_taxi_reset (1<<20) /* ??? */ /* MADI ONLY ? */
+#define HDSPM_WCK48 (1<<20) /* Frame ??? = HDSPM_SMUX */ /* AES32 ONLY */
#define HDSPM_Midi0InterruptEnable (1<<22)
#define HDSPM_Midi1InterruptEnable (1<<23)
#define HDSPM_LineOut (1<<24) /* Analog Out on channel 63/64 on=1, mute=0 */
+#define HDSPM_DS_DoubleWire (1<<26) /* AES32 ONLY */
+#define HDSPM_QS_DoubleWire (1<<27) /* AES32 ONLY */
+#define HDSPM_QS_QuadWire (1<<28) /* AES32 ONLY */
+
+#define HDSPM_wclk_sel (1<<30)
/* --- bit helper defines */
#define HDSPM_LatencyMask (HDSPM_Latency0|HDSPM_Latency1|HDSPM_Latency2)
-#define HDSPM_FrequencyMask (HDSPM_Frequency0|HDSPM_Frequency1)
+#define HDSPM_FrequencyMask (HDSPM_Frequency0|HDSPM_Frequency1|HDSPM_DoubleSpeed|HDSPM_QuadSpeed)
#define HDSPM_InputMask (HDSPM_InputSelect0|HDSPM_InputSelect1)
#define HDSPM_InputOptical 0
#define HDSPM_InputCoaxial (HDSPM_InputSelect0)
-#define HDSPM_SyncRefMask (HDSPM_SyncRef0|HDSPM_SyncRef1)
+#define HDSPM_SyncRefMask (HDSPM_SyncRef0|HDSPM_SyncRef1|HDSPM_SyncRef2|HDSPM_SyncRef3)
#define HDSPM_SyncRef_Word 0
#define HDSPM_SyncRef_MADI (HDSPM_SyncRef0)
@@ -183,6 +204,9 @@ MODULE_SUPPORTED_DEVICE("{{RME HDSPM-MADI}}");
#define HDSPM_Frequency64KHz (HDSPM_DoubleSpeed|HDSPM_Frequency0)
#define HDSPM_Frequency88_2KHz (HDSPM_DoubleSpeed|HDSPM_Frequency1)
#define HDSPM_Frequency96KHz (HDSPM_DoubleSpeed|HDSPM_Frequency1|HDSPM_Frequency0)
+#define HDSPM_Frequency128KHz (HDSPM_QuadSpeed|HDSPM_Frequency0)
+#define HDSPM_Frequency176_4KHz (HDSPM_QuadSpeed|HDSPM_Frequency1)
+#define HDSPM_Frequency192KHz (HDSPM_QuadSpeed|HDSPM_Frequency1|HDSPM_Frequency0)
/* --- for internal discrimination */
#define HDSPM_CLOCK_SOURCE_AUTOSYNC 0 /* Sample Clock Sources */
@@ -229,7 +253,8 @@ MODULE_SUPPORTED_DEVICE("{{RME HDSPM-MADI}}");
#define HDSPM_BIGENDIAN_MODE (1<<9)
#define HDSPM_RD_MULTIPLE (1<<10)
-/* --- Status Register bits --- */
+/* --- Status Register bits --- */ /* MADI ONLY */ /* Bits defined here and
+ that do not conflict with specific bits for AES32 seem to be valid also for the AES32 */
#define HDSPM_audioIRQPending (1<<0) /* IRQ is high and pending */
#define HDSPM_RX_64ch (1<<1) /* Input 64chan. MODE=1, 56chn. MODE=0 */
#define HDSPM_AB_int (1<<2) /* InputChannel Opt=0, Coax=1 (like inp0) */
@@ -263,7 +288,7 @@ MODULE_SUPPORTED_DEVICE("{{RME HDSPM-MADI}}");
#define HDSPM_madiFreq176_4 (HDSPM_madiFreq3)
#define HDSPM_madiFreq192 (HDSPM_madiFreq3|HDSPM_madiFreq0)
-/* Status2 Register bits */
+/* Status2 Register bits */ /* MADI ONLY */
#define HDSPM_version0 (1<<0) /* not realy defined but I guess */
#define HDSPM_version1 (1<<1) /* in former cards it was ??? */
@@ -297,6 +322,56 @@ MODULE_SUPPORTED_DEVICE("{{RME HDSPM-MADI}}");
#define HDSPM_SelSyncRef_MADI (HDSPM_SelSyncRef0)
#define HDSPM_SelSyncRef_NVALID (HDSPM_SelSyncRef0|HDSPM_SelSyncRef1|HDSPM_SelSyncRef2)
+/*
+ For AES32, bits for status, status2 and timecode are different
+*/
+/* status */
+#define HDSPM_AES32_wcLock 0x0200000
+#define HDSPM_AES32_wcFreq_bit 22
+/* (status >> HDSPM_AES32_wcFreq_bit) & 0xF gives WC frequency (cf function
+ HDSPM_bit2freq */
+#define HDSPM_AES32_syncref_bit 16
+/* (status >> HDSPM_AES32_syncref_bit) & 0xF gives sync source */
+
+#define HDSPM_AES32_AUTOSYNC_FROM_WORD 0
+#define HDSPM_AES32_AUTOSYNC_FROM_AES1 1
+#define HDSPM_AES32_AUTOSYNC_FROM_AES2 2
+#define HDSPM_AES32_AUTOSYNC_FROM_AES3 3
+#define HDSPM_AES32_AUTOSYNC_FROM_AES4 4
+#define HDSPM_AES32_AUTOSYNC_FROM_AES5 5
+#define HDSPM_AES32_AUTOSYNC_FROM_AES6 6
+#define HDSPM_AES32_AUTOSYNC_FROM_AES7 7
+#define HDSPM_AES32_AUTOSYNC_FROM_AES8 8
+#define HDSPM_AES32_AUTOSYNC_FROM_NONE -1
+
+/* status2 */
+/* HDSPM_LockAES_bit is given by HDSPM_LockAES >> (AES# - 1) */
+#define HDSPM_LockAES 0x80
+#define HDSPM_LockAES1 0x80
+#define HDSPM_LockAES2 0x40
+#define HDSPM_LockAES3 0x20
+#define HDSPM_LockAES4 0x10
+#define HDSPM_LockAES5 0x8
+#define HDSPM_LockAES6 0x4
+#define HDSPM_LockAES7 0x2
+#define HDSPM_LockAES8 0x1
+/*
+ Timecode
+ After windows driver sources, bits 4*i to 4*i+3 give the input frequency on
+ AES i+1
+ bits 3210
+ 0001 32kHz
+ 0010 44.1kHz
+ 0011 48kHz
+ 0100 64kHz
+ 0101 88.2kHz
+ 0110 96kHz
+ 0111 128kHz
+ 1000 176.4kHz
+ 1001 192kHz
+ NB: Timecode register doesn't seem to work on AES32 card revision 230
+*/
+
/* Mixer Values */
#define UNITY_GAIN 32768 /* = 65536/2 */
#define MINUS_INFINITY_GAIN 0
@@ -314,10 +389,14 @@ MODULE_SUPPORTED_DEVICE("{{RME HDSPM-MADI}}");
size is the same regardless of the number of channels, and
also the latency to use.
for one direction !!!
+ => need to mupltiply by 2!!
*/
-#define HDSPM_DMA_AREA_BYTES (HDSPM_MAX_CHANNELS * HDSPM_CHANNEL_BUFFER_BYTES)
+#define HDSPM_DMA_AREA_BYTES (2 * HDSPM_MAX_CHANNELS * HDSPM_CHANNEL_BUFFER_BYTES)
#define HDSPM_DMA_AREA_KILOBYTES (HDSPM_DMA_AREA_BYTES/1024)
+/* revisions >= 230 indicate AES32 card */
+#define HDSPM_AESREVISION 230
+
struct hdspm_midi {
struct hdspm *hdspm;
int id;
@@ -336,7 +415,9 @@ struct hdspm {
struct snd_pcm_substream *playback_substream; /* and/or capture stream */
char *card_name; /* for procinfo */
- unsigned short firmware_rev; /* dont know if relevant */
+ unsigned short firmware_rev; /* dont know if relevant (yes if AES32)*/
+
+ unsigned char is_aes32; /* indicates if card is AES32 */
int precise_ptr; /* use precise pointers, to be tested */
int monitor_outs; /* set up monitoring outs init flag */
@@ -453,6 +534,15 @@ static int snd_hdspm_set_defaults(struct hdspm * hdspm);
static void hdspm_set_sgbuf(struct hdspm * hdspm, struct snd_sg_buf *sgbuf,
unsigned int reg, int channels);
+static inline int HDSPM_bit2freq(int n)
+{
+ static int bit2freq_tab[] = { 0, 32000, 44100, 48000, 64000, 88200,
+ 96000, 128000, 176400, 192000 };
+ if (n < 1 || n > 9)
+ return 0;
+ return bit2freq_tab[n];
+}
+
/* Write/read to/from HDSPM with Adresses in Bytes
not words but only 32Bit writes are allowed */
@@ -544,86 +634,105 @@ static inline int snd_hdspm_use_is_exclusive(struct hdspm * hdspm)
/* check for external sample rate */
static inline int hdspm_external_sample_rate(struct hdspm * hdspm)
{
- unsigned int status2 = hdspm_read(hdspm, HDSPM_statusRegister2);
- unsigned int status = hdspm_read(hdspm, HDSPM_statusRegister);
- unsigned int rate_bits;
- int rate = 0;
+ if (hdspm->is_aes32) {
+ unsigned int status2 = hdspm_read(hdspm, HDSPM_statusRegister2);
+ unsigned int status = hdspm_read(hdspm, HDSPM_statusRegister);
+ unsigned int timecode = hdspm_read(hdspm, HDSPM_timecodeRegister);
+
+ int syncref = hdspm_autosync_ref(hdspm);
+
+ if (syncref == HDSPM_AES32_AUTOSYNC_FROM_WORD &&
+ status & HDSPM_AES32_wcLock)
+ return HDSPM_bit2freq((status >> HDSPM_AES32_wcFreq_bit) & 0xF);
+ if (syncref >= HDSPM_AES32_AUTOSYNC_FROM_AES1 &&
+ syncref <= HDSPM_AES32_AUTOSYNC_FROM_AES8 &&
+ status2 & (HDSPM_LockAES >>
+ (syncref - HDSPM_AES32_AUTOSYNC_FROM_AES1)))
+ return HDSPM_bit2freq((timecode >>
+ (4*(syncref-HDSPM_AES32_AUTOSYNC_FROM_AES1))) & 0xF);
+ return 0;
+ } else {
+ unsigned int status2 = hdspm_read(hdspm, HDSPM_statusRegister2);
+ unsigned int status = hdspm_read(hdspm, HDSPM_statusRegister);
+ unsigned int rate_bits;
+ int rate = 0;
- /* if wordclock has synced freq and wordclock is valid */
- if ((status2 & HDSPM_wcLock) != 0 &&
- (status & HDSPM_SelSyncRef0) == 0) {
+ /* if wordclock has synced freq and wordclock is valid */
+ if ((status2 & HDSPM_wcLock) != 0 &&
+ (status & HDSPM_SelSyncRef0) == 0) {
- rate_bits = status2 & HDSPM_wcFreqMask;
+ rate_bits = status2 & HDSPM_wcFreqMask;
- switch (rate_bits) {
- case HDSPM_wcFreq32:
- rate = 32000;
- break;
- case HDSPM_wcFreq44_1:
- rate = 44100;
- break;
- case HDSPM_wcFreq48:
- rate = 48000;
- break;
- case HDSPM_wcFreq64:
- rate = 64000;
- break;
- case HDSPM_wcFreq88_2:
- rate = 88200;
- break;
- case HDSPM_wcFreq96:
- rate = 96000;
- break;
- /* Quadspeed Bit missing ???? */
- default:
- rate = 0;
- break;
+ switch (rate_bits) {
+ case HDSPM_wcFreq32:
+ rate = 32000;
+ break;
+ case HDSPM_wcFreq44_1:
+ rate = 44100;
+ break;
+ case HDSPM_wcFreq48:
+ rate = 48000;
+ break;
+ case HDSPM_wcFreq64:
+ rate = 64000;
+ break;
+ case HDSPM_wcFreq88_2:
+ rate = 88200;
+ break;
+ case HDSPM_wcFreq96:
+ rate = 96000;
+ break;
+ /* Quadspeed Bit missing ???? */
+ default:
+ rate = 0;
+ break;
+ }
}
- }
- /* if rate detected and Syncref is Word than have it, word has priority to MADI */
- if (rate != 0
- && (status2 & HDSPM_SelSyncRefMask) == HDSPM_SelSyncRef_WORD)
- return rate;
+ /* if rate detected and Syncref is Word than have it, word has priority to MADI */
+ if (rate != 0 &&
+ (status2 & HDSPM_SelSyncRefMask) == HDSPM_SelSyncRef_WORD)
+ return rate;
- /* maby a madi input (which is taken if sel sync is madi) */
- if (status & HDSPM_madiLock) {
- rate_bits = status & HDSPM_madiFreqMask;
+ /* maby a madi input (which is taken if sel sync is madi) */
+ if (status & HDSPM_madiLock) {
+ rate_bits = status & HDSPM_madiFreqMask;
- switch (rate_bits) {
- case HDSPM_madiFreq32:
- rate = 32000;
- break;
- case HDSPM_madiFreq44_1:
- rate = 44100;
- break;
- case HDSPM_madiFreq48:
- rate = 48000;
- break;
- case HDSPM_madiFreq64:
- rate = 64000;
- break;
- case HDSPM_madiFreq88_2:
- rate = 88200;
- break;
- case HDSPM_madiFreq96:
- rate = 96000;
- break;
- case HDSPM_madiFreq128:
- rate = 128000;
- break;
- case HDSPM_madiFreq176_4:
- rate = 176400;
- break;
- case HDSPM_madiFreq192:
- rate = 192000;
- break;
- default:
- rate = 0;
- break;
+ switch (rate_bits) {
+ case HDSPM_madiFreq32:
+ rate = 32000;
+ break;
+ case HDSPM_madiFreq44_1:
+ rate = 44100;
+ break;
+ case HDSPM_madiFreq48:
+ rate = 48000;
+ break;
+ case HDSPM_madiFreq64:
+ rate = 64000;
+ break;
+ case HDSPM_madiFreq88_2:
+ rate = 88200;
+ break;
+ case HDSPM_madiFreq96:
+ rate = 96000;
+ break;
+ case HDSPM_madiFreq128:
+ rate = 128000;
+ break;
+ case HDSPM_madiFreq176_4:
+ rate = 176400;
+ break;
+ case HDSPM_madiFreq192:
+ rate = 192000;
+ break;
+ default:
+ rate = 0;
+ break;
+ }
}
+ return rate;
}
- return rate;
}
/* Latency function */
@@ -676,7 +785,8 @@ static inline void hdspm_silence_playback(struct hdspm * hdspm)
int n = hdspm->period_bytes;
void *buf = hdspm->playback_buffer;
- snd_assert(buf != NULL, return);
+ if (buf == NULL)
+ return;
for (i = 0; i < HDSPM_MAX_CHANNELS; i++) {
memset(buf, 0, n);
@@ -716,6 +826,7 @@ static int hdspm_set_rate(struct hdspm * hdspm, int rate, int called_internally)
int current_rate;
int rate_bits;
int not_set = 0;
+ int is_single, is_double, is_quad;
/* ASSUMPTION: hdspm->lock is either set, or there is no need for
it (e.g. during module initialization).
@@ -766,43 +877,56 @@ static int hdspm_set_rate(struct hdspm * hdspm, int rate, int called_internally)
changes in the read/write routines.
*/
+ is_single = (current_rate <= 48000);
+ is_double = (current_rate > 48000 && current_rate <= 96000);
+ is_quad = (current_rate > 96000);
+
switch (rate) {
case 32000:
- if (current_rate > 48000) {
+ if (!is_single)
reject_if_open = 1;
- }
rate_bits = HDSPM_Frequency32KHz;
break;
case 44100:
- if (current_rate > 48000) {
+ if (!is_single)
reject_if_open = 1;
- }
rate_bits = HDSPM_Frequency44_1KHz;
break;
case 48000:
- if (current_rate > 48000) {
+ if (!is_single)
reject_if_open = 1;
- }
rate_bits = HDSPM_Frequency48KHz;
break;
case 64000:
- if (current_rate <= 48000) {
+ if (!is_double)
reject_if_open = 1;
- }
rate_bits = HDSPM_Frequency64KHz;
break;
case 88200:
- if (current_rate <= 48000) {
+ if (!is_double)
reject_if_open = 1;
- }
rate_bits = HDSPM_Frequency88_2KHz;
break;
case 96000:
- if (current_rate <= 48000) {
+ if (!is_double)
reject_if_open = 1;
- }
rate_bits = HDSPM_Frequency96KHz;
break;
+ case 128000:
+ if (!is_quad)
+ reject_if_open = 1;
+ rate_bits = HDSPM_Frequency128KHz;
+ break;
+ case 176400:
+ if (!is_quad)
+ reject_if_open = 1;
+ rate_bits = HDSPM_Frequency176_4KHz;
+ break;
+ case 192000:
+ if (!is_quad)
+ reject_if_open = 1;
+ rate_bits = HDSPM_Frequency192KHz;
+ break;
default:
return -EINVAL;
}
@@ -819,7 +943,7 @@ static int hdspm_set_rate(struct hdspm * hdspm, int rate, int called_internally)
hdspm->control_register |= rate_bits;
hdspm_write(hdspm, HDSPM_controlRegister, hdspm->control_register);
- if (rate > 64000)
+ if (rate > 96000 /* 64000*/)
hdspm->channel_map = channel_map_madi_qs;
else if (rate > 48000)
hdspm->channel_map = channel_map_madi_ds;
@@ -1455,11 +1579,27 @@ static int hdspm_pref_sync_ref(struct hdspm * hdspm)
/* Notice that this looks at the requested sync source,
not the one actually in use.
*/
- switch (hdspm->control_register & HDSPM_SyncRefMask) {
- case HDSPM_SyncRef_Word:
- return HDSPM_SYNC_FROM_WORD;
- case HDSPM_SyncRef_MADI:
- return HDSPM_SYNC_FROM_MADI;
+ if (hdspm->is_aes32) {
+ switch (hdspm->control_register & HDSPM_SyncRefMask) {
+ /* number gives AES index, except for 0 which
+ corresponds to WordClock */
+ case 0: return 0;
+ case HDSPM_SyncRef0: return 1;
+ case HDSPM_SyncRef1: return 2;
+ case HDSPM_SyncRef1+HDSPM_SyncRef0: return 3;
+ case HDSPM_SyncRef2: return 4;
+ case HDSPM_SyncRef2+HDSPM_SyncRef0: return 5;
+ case HDSPM_SyncRef2+HDSPM_SyncRef1: return 6;
+ case HDSPM_SyncRef2+HDSPM_SyncRef1+HDSPM_SyncRef0: return 7;
+ case HDSPM_SyncRef3: return 8;
+ }
+ } else {
+ switch (hdspm->control_register & HDSPM_SyncRefMask) {
+ case HDSPM_SyncRef_Word:
+ return HDSPM_SYNC_FROM_WORD;
+ case HDSPM_SyncRef_MADI:
+ return HDSPM_SYNC_FROM_MADI;
+ }
}
return HDSPM_SYNC_FROM_WORD;
@@ -1469,15 +1609,49 @@ static int hdspm_set_pref_sync_ref(struct hdspm * hdspm, int pref)
{
hdspm->control_register &= ~HDSPM_SyncRefMask;
- switch (pref) {
- case HDSPM_SYNC_FROM_MADI:
- hdspm->control_register |= HDSPM_SyncRef_MADI;
- break;
- case HDSPM_SYNC_FROM_WORD:
- hdspm->control_register |= HDSPM_SyncRef_Word;
- break;
- default:
- return -1;
+ if (hdspm->is_aes32) {
+ switch (pref) {
+ case 0:
+ hdspm->control_register |= 0;
+ break;
+ case 1:
+ hdspm->control_register |= HDSPM_SyncRef0;
+ break;
+ case 2:
+ hdspm->control_register |= HDSPM_SyncRef1;
+ break;
+ case 3:
+ hdspm->control_register |= HDSPM_SyncRef1+HDSPM_SyncRef0;
+ break;
+ case 4:
+ hdspm->control_register |= HDSPM_SyncRef2;
+ break;
+ case 5:
+ hdspm->control_register |= HDSPM_SyncRef2+HDSPM_SyncRef0;
+ break;
+ case 6:
+ hdspm->control_register |= HDSPM_SyncRef2+HDSPM_SyncRef1;
+ break;
+ case 7:
+ hdspm->control_register |= HDSPM_SyncRef2+HDSPM_SyncRef1+HDSPM_SyncRef0;
+ break;
+ case 8:
+ hdspm->control_register |= HDSPM_SyncRef3;
+ break;
+ default:
+ return -1;
+ }
+ } else {
+ switch (pref) {
+ case HDSPM_SYNC_FROM_MADI:
+ hdspm->control_register |= HDSPM_SyncRef_MADI;
+ break;
+ case HDSPM_SYNC_FROM_WORD:
+ hdspm->control_register |= HDSPM_SyncRef_Word;
+ break;
+ default:
+ return -1;
+ }
}
hdspm_write(hdspm, HDSPM_controlRegister, hdspm->control_register);
return 0;
@@ -1486,18 +1660,36 @@ static int hdspm_set_pref_sync_ref(struct hdspm * hdspm, int pref)
static int snd_hdspm_info_pref_sync_ref(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_info *uinfo)
{
- static char *texts[] = { "Word", "MADI" };
+ struct hdspm *hdspm = snd_kcontrol_chip(kcontrol);
- uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED;
- uinfo->count = 1;
+ if (hdspm->is_aes32) {
+ static char *texts[] = { "Word", "AES1", "AES2", "AES3",
+ "AES4", "AES5", "AES6", "AES7", "AES8" };
- uinfo->value.enumerated.items = 2;
+ uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED;
+ uinfo->count = 1;
- if (uinfo->value.enumerated.item >= uinfo->value.enumerated.items)
- uinfo->value.enumerated.item =
- uinfo->value.enumerated.items - 1;
- strcpy(uinfo->value.enumerated.name,
- texts[uinfo->value.enumerated.item]);
+ uinfo->value.enumerated.items = 9;
+
+ if (uinfo->value.enumerated.item >= uinfo->value.enumerated.items)
+ uinfo->value.enumerated.item =
+ uinfo->value.enumerated.items - 1;
+ strcpy(uinfo->value.enumerated.name,
+ texts[uinfo->value.enumerated.item]);
+ } else {
+ static char *texts[] = { "Word", "MADI" };
+
+ uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED;
+ uinfo->count = 1;
+
+ uinfo->value.enumerated.items = 2;
+
+ if (uinfo->value.enumerated.item >= uinfo->value.enumerated.items)
+ uinfo->value.enumerated.item =
+ uinfo->value.enumerated.items - 1;
+ strcpy(uinfo->value.enumerated.name,
+ texts[uinfo->value.enumerated.item]);
+ }
return 0;
}
@@ -1517,7 +1709,7 @@ static int snd_hdspm_put_pref_sync_ref(struct snd_kcontrol *kcontrol,
int change, max;
unsigned int val;
- max = 2;
+ max = hdspm->is_aes32 ? 9 : 2;
if (!snd_hdspm_use_is_exclusive(hdspm))
return -EBUSY;
@@ -1542,40 +1734,64 @@ static int snd_hdspm_put_pref_sync_ref(struct snd_kcontrol *kcontrol,
static int hdspm_autosync_ref(struct hdspm * hdspm)
{
- /* This looks at the autosync selected sync reference */
- unsigned int status2 = hdspm_read(hdspm, HDSPM_statusRegister2);
-
- switch (status2 & HDSPM_SelSyncRefMask) {
-
- case HDSPM_SelSyncRef_WORD:
- return HDSPM_AUTOSYNC_FROM_WORD;
-
- case HDSPM_SelSyncRef_MADI:
- return HDSPM_AUTOSYNC_FROM_MADI;
-
- case HDSPM_SelSyncRef_NVALID:
- return HDSPM_AUTOSYNC_FROM_NONE;
+ if (hdspm->is_aes32) {
+ unsigned int status = hdspm_read(hdspm, HDSPM_statusRegister);
+ unsigned int syncref = (status >> HDSPM_AES32_syncref_bit) & 0xF;
+ if (syncref == 0)
+ return HDSPM_AES32_AUTOSYNC_FROM_WORD;
+ if (syncref <= 8)
+ return syncref;
+ return HDSPM_AES32_AUTOSYNC_FROM_NONE;
+ } else {
+ /* This looks at the autosync selected sync reference */
+ unsigned int status2 = hdspm_read(hdspm, HDSPM_statusRegister2);
+
+ switch (status2 & HDSPM_SelSyncRefMask) {
+ case HDSPM_SelSyncRef_WORD:
+ return HDSPM_AUTOSYNC_FROM_WORD;
+ case HDSPM_SelSyncRef_MADI:
+ return HDSPM_AUTOSYNC_FROM_MADI;
+ case HDSPM_SelSyncRef_NVALID:
+ return HDSPM_AUTOSYNC_FROM_NONE;
+ default:
+ return 0;
+ }
- default:
return 0;
}
-
- return 0;
}
static int snd_hdspm_info_autosync_ref(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_info *uinfo)
{
- static char *texts[] = { "WordClock", "MADI", "None" };
+ struct hdspm *hdspm = snd_kcontrol_chip(kcontrol);
- uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED;
- uinfo->count = 1;
- uinfo->value.enumerated.items = 3;
- if (uinfo->value.enumerated.item >= uinfo->value.enumerated.items)
- uinfo->value.enumerated.item =
- uinfo->value.enumerated.items - 1;
- strcpy(uinfo->value.enumerated.name,
- texts[uinfo->value.enumerated.item]);
+ if (hdspm->is_aes32) {
+ static char *texts[] = { "WordClock", "AES1", "AES2", "AES3",
+ "AES4", "AES5", "AES6", "AES7", "AES8", "None"};
+
+ uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED;
+ uinfo->count = 1;
+ uinfo->value.enumerated.items = 10;
+ if (uinfo->value.enumerated.item >= uinfo->value.enumerated.items)
+ uinfo->value.enumerated.item =
+ uinfo->value.enumerated.items - 1;
+ strcpy(uinfo->value.enumerated.name,
+ texts[uinfo->value.enumerated.item]);
+ }
+ else
+ {
+ static char *texts[] = { "WordClock", "MADI", "None" };
+
+ uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED;
+ uinfo->count = 1;
+ uinfo->value.enumerated.items = 3;
+ if (uinfo->value.enumerated.item >= uinfo->value.enumerated.items)
+ uinfo->value.enumerated.item =
+ uinfo->value.enumerated.items - 1;
+ strcpy(uinfo->value.enumerated.name,
+ texts[uinfo->value.enumerated.item]);
+ }
return 0;
}
@@ -1841,6 +2057,195 @@ static int snd_hdspm_put_safe_mode(struct snd_kcontrol *kcontrol,
return change;
}
+#define HDSPM_EMPHASIS(xname, xindex) \
+{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, \
+ .name = xname, \
+ .index = xindex, \
+ .info = snd_hdspm_info_emphasis, \
+ .get = snd_hdspm_get_emphasis, \
+ .put = snd_hdspm_put_emphasis \
+}
+
+static int hdspm_emphasis(struct hdspm * hdspm)
+{
+ return (hdspm->control_register & HDSPM_Emphasis) ? 1 : 0;
+}
+
+static int hdspm_set_emphasis(struct hdspm * hdspm, int emp)
+{
+ if (emp)
+ hdspm->control_register |= HDSPM_Emphasis;
+ else
+ hdspm->control_register &= ~HDSPM_Emphasis;
+ hdspm_write(hdspm, HDSPM_controlRegister, hdspm->control_register);
+
+ return 0;
+}
+
+static int snd_hdspm_info_emphasis(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_info *uinfo)
+{
+ uinfo->type = SNDRV_CTL_ELEM_TYPE_BOOLEAN;
+ uinfo->count = 1;
+ uinfo->value.integer.min = 0;
+ uinfo->value.integer.max = 1;
+ return 0;
+}
+
+static int snd_hdspm_get_emphasis(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct hdspm *hdspm = snd_kcontrol_chip(kcontrol);
+
+ spin_lock_irq(&hdspm->lock);
+ ucontrol->value.enumerated.item[0] = hdspm_emphasis(hdspm);
+ spin_unlock_irq(&hdspm->lock);
+ return 0;
+}
+
+static int snd_hdspm_put_emphasis(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct hdspm *hdspm = snd_kcontrol_chip(kcontrol);
+ int change;
+ unsigned int val;
+
+ if (!snd_hdspm_use_is_exclusive(hdspm))
+ return -EBUSY;
+ val = ucontrol->value.integer.value[0] & 1;
+ spin_lock_irq(&hdspm->lock);
+ change = (int) val != hdspm_emphasis(hdspm);
+ hdspm_set_emphasis(hdspm, val);
+ spin_unlock_irq(&hdspm->lock);
+ return change;
+}
+
+#define HDSPM_DOLBY(xname, xindex) \
+{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, \
+ .name = xname, \
+ .index = xindex, \
+ .info = snd_hdspm_info_dolby, \
+ .get = snd_hdspm_get_dolby, \
+ .put = snd_hdspm_put_dolby \
+}
+
+static int hdspm_dolby(struct hdspm * hdspm)
+{
+ return (hdspm->control_register & HDSPM_Dolby) ? 1 : 0;
+}
+
+static int hdspm_set_dolby(struct hdspm * hdspm, int dol)
+{
+ if (dol)
+ hdspm->control_register |= HDSPM_Dolby;
+ else
+ hdspm->control_register &= ~HDSPM_Dolby;
+ hdspm_write(hdspm, HDSPM_controlRegister, hdspm->control_register);
+
+ return 0;
+}
+
+static int snd_hdspm_info_dolby(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_info *uinfo)
+{
+ uinfo->type = SNDRV_CTL_ELEM_TYPE_BOOLEAN;
+ uinfo->count = 1;
+ uinfo->value.integer.min = 0;
+ uinfo->value.integer.max = 1;
+ return 0;
+}
+
+static int snd_hdspm_get_dolby(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct hdspm *hdspm = snd_kcontrol_chip(kcontrol);
+
+ spin_lock_irq(&hdspm->lock);
+ ucontrol->value.enumerated.item[0] = hdspm_dolby(hdspm);
+ spin_unlock_irq(&hdspm->lock);
+ return 0;
+}
+
+static int snd_hdspm_put_dolby(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct hdspm *hdspm = snd_kcontrol_chip(kcontrol);
+ int change;
+ unsigned int val;
+
+ if (!snd_hdspm_use_is_exclusive(hdspm))
+ return -EBUSY;
+ val = ucontrol->value.integer.value[0] & 1;
+ spin_lock_irq(&hdspm->lock);
+ change = (int) val != hdspm_dolby(hdspm);
+ hdspm_set_dolby(hdspm, val);
+ spin_unlock_irq(&hdspm->lock);
+ return change;
+}
+
+#define HDSPM_PROFESSIONAL(xname, xindex) \
+{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, \
+ .name = xname, \
+ .index = xindex, \
+ .info = snd_hdspm_info_professional, \
+ .get = snd_hdspm_get_professional, \
+ .put = snd_hdspm_put_professional \
+}
+
+static int hdspm_professional(struct hdspm * hdspm)
+{
+ return (hdspm->control_register & HDSPM_Professional) ? 1 : 0;
+}
+
+static int hdspm_set_professional(struct hdspm * hdspm, int dol)
+{
+ if (dol)
+ hdspm->control_register |= HDSPM_Professional;
+ else
+ hdspm->control_register &= ~HDSPM_Professional;
+ hdspm_write(hdspm, HDSPM_controlRegister, hdspm->control_register);
+
+ return 0;
+}
+
+static int snd_hdspm_info_professional(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_info *uinfo)
+{
+ uinfo->type = SNDRV_CTL_ELEM_TYPE_BOOLEAN;
+ uinfo->count = 1;
+ uinfo->value.integer.min = 0;
+ uinfo->value.integer.max = 1;
+ return 0;
+}
+
+static int snd_hdspm_get_professional(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct hdspm *hdspm = snd_kcontrol_chip(kcontrol);
+
+ spin_lock_irq(&hdspm->lock);
+ ucontrol->value.enumerated.item[0] = hdspm_professional(hdspm);
+ spin_unlock_irq(&hdspm->lock);
+ return 0;
+}
+
+static int snd_hdspm_put_professional(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct hdspm *hdspm = snd_kcontrol_chip(kcontrol);
+ int change;
+ unsigned int val;
+
+ if (!snd_hdspm_use_is_exclusive(hdspm))
+ return -EBUSY;
+ val = ucontrol->value.integer.value[0] & 1;
+ spin_lock_irq(&hdspm->lock);
+ change = (int) val != hdspm_professional(hdspm);
+ hdspm_set_professional(hdspm, val);
+ spin_unlock_irq(&hdspm->lock);
+ return change;
+}
+
#define HDSPM_INPUT_SELECT(xname, xindex) \
{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, \
.name = xname, \
@@ -1912,6 +2317,163 @@ static int snd_hdspm_put_input_select(struct snd_kcontrol *kcontrol,
return change;
}
+#define HDSPM_DS_WIRE(xname, xindex) \
+{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, \
+ .name = xname, \
+ .index = xindex, \
+ .info = snd_hdspm_info_ds_wire, \
+ .get = snd_hdspm_get_ds_wire, \
+ .put = snd_hdspm_put_ds_wire \
+}
+
+static int hdspm_ds_wire(struct hdspm * hdspm)
+{
+ return (hdspm->control_register & HDSPM_DS_DoubleWire) ? 1 : 0;
+}
+
+static int hdspm_set_ds_wire(struct hdspm * hdspm, int ds)
+{
+ if (ds)
+ hdspm->control_register |= HDSPM_DS_DoubleWire;
+ else
+ hdspm->control_register &= ~HDSPM_DS_DoubleWire;
+ hdspm_write(hdspm, HDSPM_controlRegister, hdspm->control_register);
+
+ return 0;
+}
+
+static int snd_hdspm_info_ds_wire(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_info *uinfo)
+{
+ static char *texts[] = { "Single", "Double" };
+
+ uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED;
+ uinfo->count = 1;
+ uinfo->value.enumerated.items = 2;
+
+ if (uinfo->value.enumerated.item >= uinfo->value.enumerated.items)
+ uinfo->value.enumerated.item =
+ uinfo->value.enumerated.items - 1;
+ strcpy(uinfo->value.enumerated.name,
+ texts[uinfo->value.enumerated.item]);
+
+ return 0;
+}
+
+static int snd_hdspm_get_ds_wire(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct hdspm *hdspm = snd_kcontrol_chip(kcontrol);
+
+ spin_lock_irq(&hdspm->lock);
+ ucontrol->value.enumerated.item[0] = hdspm_ds_wire(hdspm);
+ spin_unlock_irq(&hdspm->lock);
+ return 0;
+}
+
+static int snd_hdspm_put_ds_wire(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct hdspm *hdspm = snd_kcontrol_chip(kcontrol);
+ int change;
+ unsigned int val;
+
+ if (!snd_hdspm_use_is_exclusive(hdspm))
+ return -EBUSY;
+ val = ucontrol->value.integer.value[0] & 1;
+ spin_lock_irq(&hdspm->lock);
+ change = (int) val != hdspm_ds_wire(hdspm);
+ hdspm_set_ds_wire(hdspm, val);
+ spin_unlock_irq(&hdspm->lock);
+ return change;
+}
+
+#define HDSPM_QS_WIRE(xname, xindex) \
+{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, \
+ .name = xname, \
+ .index = xindex, \
+ .info = snd_hdspm_info_qs_wire, \
+ .get = snd_hdspm_get_qs_wire, \
+ .put = snd_hdspm_put_qs_wire \
+}
+
+static int hdspm_qs_wire(struct hdspm * hdspm)
+{
+ if (hdspm->control_register & HDSPM_QS_DoubleWire)
+ return 1;
+ if (hdspm->control_register & HDSPM_QS_QuadWire)
+ return 2;
+ return 0;
+}
+
+static int hdspm_set_qs_wire(struct hdspm * hdspm, int mode)
+{
+ hdspm->control_register &= ~(HDSPM_QS_DoubleWire | HDSPM_QS_QuadWire);
+ switch (mode) {
+ case 0:
+ break;
+ case 1:
+ hdspm->control_register |= HDSPM_QS_DoubleWire;
+ break;
+ case 2:
+ hdspm->control_register |= HDSPM_QS_QuadWire;
+ break;
+ }
+ hdspm_write(hdspm, HDSPM_controlRegister, hdspm->control_register);
+
+ return 0;
+}
+
+static int snd_hdspm_info_qs_wire(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_info *uinfo)
+{
+ static char *texts[] = { "Single", "Double", "Quad" };
+
+ uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED;
+ uinfo->count = 1;
+ uinfo->value.enumerated.items = 3;
+
+ if (uinfo->value.enumerated.item >= uinfo->value.enumerated.items)
+ uinfo->value.enumerated.item =
+ uinfo->value.enumerated.items - 1;
+ strcpy(uinfo->value.enumerated.name,
+ texts[uinfo->value.enumerated.item]);
+
+ return 0;
+}
+
+static int snd_hdspm_get_qs_wire(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct hdspm *hdspm = snd_kcontrol_chip(kcontrol);
+
+ spin_lock_irq(&hdspm->lock);
+ ucontrol->value.enumerated.item[0] = hdspm_qs_wire(hdspm);
+ spin_unlock_irq(&hdspm->lock);
+ return 0;
+}
+
+static int snd_hdspm_put_qs_wire(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct hdspm *hdspm = snd_kcontrol_chip(kcontrol);
+ int change;
+ int val;
+
+ if (!snd_hdspm_use_is_exclusive(hdspm))
+ return -EBUSY;
+ val = ucontrol->value.integer.value[0];
+ if (val < 0)
+ val = 0;
+ if (val > 2)
+ val = 2;
+ spin_lock_irq(&hdspm->lock);
+ change = (int) val != hdspm_qs_wire(hdspm);
+ hdspm_set_qs_wire(hdspm, val);
+ spin_unlock_irq(&hdspm->lock);
+ return change;
+}
+
/* Simple Mixer
deprecated since to much faders ???
MIXER interface says output (source, destination, value)
@@ -2135,14 +2697,24 @@ static int snd_hdspm_info_sync_check(struct snd_kcontrol *kcontrol,
static int hdspm_wc_sync_check(struct hdspm * hdspm)
{
- int status2 = hdspm_read(hdspm, HDSPM_statusRegister2);
- if (status2 & HDSPM_wcLock) {
- if (status2 & HDSPM_wcSync)
+ if (hdspm->is_aes32) {
+ int status = hdspm_read(hdspm, HDSPM_statusRegister);
+ if (status & HDSPM_AES32_wcLock) {
+ /* I don't know how to differenciate sync from lock.
+ Doing as if sync for now */
return 2;
- else
- return 1;
+ }
+ return 0;
+ } else {
+ int status2 = hdspm_read(hdspm, HDSPM_statusRegister2);
+ if (status2 & HDSPM_wcLock) {
+ if (status2 & HDSPM_wcSync)
+ return 2;
+ else
+ return 1;
+ }
+ return 0;
}
- return 0;
}
static int snd_hdspm_get_wc_sync_check(struct snd_kcontrol *kcontrol,
@@ -2188,9 +2760,43 @@ static int snd_hdspm_get_madisync_sync_check(struct snd_kcontrol *kcontrol,
}
+#define HDSPM_AES_SYNC_CHECK(xname, xindex) \
+{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, \
+ .name = xname, \
+ .index = xindex, \
+ .access = SNDRV_CTL_ELEM_ACCESS_READ | SNDRV_CTL_ELEM_ACCESS_VOLATILE, \
+ .info = snd_hdspm_info_sync_check, \
+ .get = snd_hdspm_get_aes_sync_check \
+}
+
+static int hdspm_aes_sync_check(struct hdspm * hdspm, int idx)
+{
+ int status2 = hdspm_read(hdspm, HDSPM_statusRegister2);
+ if (status2 & (HDSPM_LockAES >> idx)) {
+ /* I don't know how to differenciate sync from lock.
+ Doing as if sync for now */
+ return 2;
+ }
+ return 0;
+}
+
+static int snd_hdspm_get_aes_sync_check(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ int offset;
+ struct hdspm *hdspm = snd_kcontrol_chip(kcontrol);
+
+ offset = ucontrol->id.index - 1;
+ if (offset < 0 || offset >= 8)
+ return -EINVAL;
+
+ ucontrol->value.enumerated.item[0] =
+ hdspm_aes_sync_check(hdspm, offset);
+ return 0;
+}
-static struct snd_kcontrol_new snd_hdspm_controls[] = {
+static struct snd_kcontrol_new snd_hdspm_controls_madi[] = {
HDSPM_MIXER("Mixer", 0),
/* 'Sample Clock Source' complies with the alsa control naming scheme */
@@ -2211,6 +2817,29 @@ static struct snd_kcontrol_new snd_hdspm_controls[] = {
HDSPM_INPUT_SELECT("Input Select", 0),
};
+static struct snd_kcontrol_new snd_hdspm_controls_aes32[] = {
+
+ HDSPM_MIXER("Mixer", 0),
+/* 'Sample Clock Source' complies with the alsa control naming scheme */
+ HDSPM_CLOCK_SOURCE("Sample Clock Source", 0),
+
+ HDSPM_SYSTEM_CLOCK_MODE("System Clock Mode", 0),
+ HDSPM_PREF_SYNC_REF("Preferred Sync Reference", 0),
+ HDSPM_AUTOSYNC_REF("AutoSync Reference", 0),
+ HDSPM_SYSTEM_SAMPLE_RATE("System Sample Rate", 0),
+/* 'External Rate' complies with the alsa control naming scheme */
+ HDSPM_AUTOSYNC_SAMPLE_RATE("External Rate", 0),
+ HDSPM_WC_SYNC_CHECK("Word Clock Lock Status", 0),
+/* HDSPM_AES_SYNC_CHECK("AES Lock Status", 0),*/ /* created in snd_hdspm_create_controls() */
+ HDSPM_LINE_OUT("Line Out", 0),
+ HDSPM_EMPHASIS("Emphasis", 0),
+ HDSPM_DOLBY("Non Audio", 0),
+ HDSPM_PROFESSIONAL("Professional", 0),
+ HDSPM_C_TMS("Clear Track Marker", 0),
+ HDSPM_DS_WIRE("Double Speed Wire Mode", 0),
+ HDSPM_QS_WIRE("Quad Speed Wire Mode", 0),
+};
+
static struct snd_kcontrol_new snd_hdspm_playback_mixer = HDSPM_PLAYBACK_MIXER;
@@ -2245,20 +2874,40 @@ static int snd_hdspm_create_controls(struct snd_card *card, struct hdspm * hdspm
struct snd_kcontrol *kctl;
/* add control list first */
-
- for (idx = 0; idx < ARRAY_SIZE(snd_hdspm_controls); idx++) {
- if ((err =
- snd_ctl_add(card, kctl =
- snd_ctl_new1(&snd_hdspm_controls[idx],
- hdspm))) < 0) {
- return err;
+ if (hdspm->is_aes32) {
+ struct snd_kcontrol_new aes_sync_ctl =
+ HDSPM_AES_SYNC_CHECK("AES Lock Status", 0);
+
+ for (idx = 0; idx < ARRAY_SIZE(snd_hdspm_controls_aes32);
+ idx++) {
+ err = snd_ctl_add(card,
+ snd_ctl_new1(&snd_hdspm_controls_aes32[idx],
+ hdspm));
+ if (err < 0)
+ return err;
+ }
+ for (idx = 1; idx <= 8; idx++) {
+ aes_sync_ctl.index = idx;
+ err = snd_ctl_add(card,
+ snd_ctl_new1(&aes_sync_ctl, hdspm));
+ if (err < 0)
+ return err;
+ }
+ } else {
+ for (idx = 0; idx < ARRAY_SIZE(snd_hdspm_controls_madi);
+ idx++) {
+ err = snd_ctl_add(card,
+ snd_ctl_new1(&snd_hdspm_controls_madi[idx],
+ hdspm));
+ if (err < 0)
+ return err;
}
}
/* Channel playback mixer as default control
- Note: the whole matrix would be 128*HDSPM_MIXER_CHANNELS Faders, thats too big for any alsamixer
- they are accesible via special IOCTL on hwdep
- and the mixer 2dimensional mixer control */
+Note: the whole matrix would be 128*HDSPM_MIXER_CHANNELS Faders, thats too big for any alsamixer
+they are accesible via special IOCTL on hwdep
+and the mixer 2dimensional mixer control */
snd_hdspm_playback_mixer.name = "Chn";
limit = HDSPM_MAX_CHANNELS;
@@ -2289,7 +2938,8 @@ static int snd_hdspm_create_controls(struct snd_card *card, struct hdspm * hdspm
------------------------------------------------------------*/
static void
-snd_hdspm_proc_read(struct snd_info_entry * entry, struct snd_info_buffer *buffer)
+snd_hdspm_proc_read_madi(struct snd_info_entry * entry,
+ struct snd_info_buffer *buffer)
{
struct hdspm *hdspm = (struct hdspm *) entry->private_data;
unsigned int status;
@@ -2420,11 +3070,10 @@ snd_hdspm_proc_read(struct snd_info_entry * entry, struct snd_info_buffer *buffe
clock_source = "Error";
}
snd_iprintf(buffer, "Sample Clock Source: %s\n", clock_source);
- if (!(hdspm->control_register & HDSPM_ClockModeMaster)) {
+ if (!(hdspm->control_register & HDSPM_ClockModeMaster))
system_clock_mode = "Slave";
- } else {
+ else
system_clock_mode = "Master";
- }
snd_iprintf(buffer, "System Clock Mode: %s\n", system_clock_mode);
switch (hdspm_pref_sync_ref(hdspm)) {
@@ -2484,13 +3133,213 @@ snd_hdspm_proc_read(struct snd_info_entry * entry, struct snd_info_buffer *buffe
snd_iprintf(buffer, "\n");
}
+static void
+snd_hdspm_proc_read_aes32(struct snd_info_entry * entry,
+ struct snd_info_buffer *buffer)
+{
+ struct hdspm *hdspm = (struct hdspm *) entry->private_data;
+ unsigned int status;
+ unsigned int status2;
+ unsigned int timecode;
+ int pref_syncref;
+ char *autosync_ref;
+ char *system_clock_mode;
+ char *clock_source;
+ int x;
+
+ status = hdspm_read(hdspm, HDSPM_statusRegister);
+ status2 = hdspm_read(hdspm, HDSPM_statusRegister2);
+ timecode = hdspm_read(hdspm, HDSPM_timecodeRegister);
+
+ snd_iprintf(buffer, "%s (Card #%d) Rev.%x\n",
+ hdspm->card_name, hdspm->card->number + 1,
+ hdspm->firmware_rev);
+
+ snd_iprintf(buffer, "IRQ: %d Registers bus: 0x%lx VM: 0x%lx\n",
+ hdspm->irq, hdspm->port, (unsigned long)hdspm->iobase);
+
+ snd_iprintf(buffer, "--- System ---\n");
+
+ snd_iprintf(buffer,
+ "IRQ Pending: Audio=%d, MIDI0=%d, MIDI1=%d, IRQcount=%d\n",
+ status & HDSPM_audioIRQPending,
+ (status & HDSPM_midi0IRQPending) ? 1 : 0,
+ (status & HDSPM_midi1IRQPending) ? 1 : 0,
+ hdspm->irq_count);
+ snd_iprintf(buffer,
+ "HW pointer: id = %d, rawptr = %d (%d->%d) estimated= %ld (bytes)\n",
+ ((status & HDSPM_BufferID) ? 1 : 0),
+ (status & HDSPM_BufferPositionMask),
+ (status & HDSPM_BufferPositionMask) % (2 *
+ (int)hdspm->
+ period_bytes),
+ ((status & HDSPM_BufferPositionMask) -
+ 64) % (2 * (int)hdspm->period_bytes),
+ (long) hdspm_hw_pointer(hdspm) * 4);
+
+ snd_iprintf(buffer,
+ "MIDI FIFO: Out1=0x%x, Out2=0x%x, In1=0x%x, In2=0x%x \n",
+ hdspm_read(hdspm, HDSPM_midiStatusOut0) & 0xFF,
+ hdspm_read(hdspm, HDSPM_midiStatusOut1) & 0xFF,
+ hdspm_read(hdspm, HDSPM_midiStatusIn0) & 0xFF,
+ hdspm_read(hdspm, HDSPM_midiStatusIn1) & 0xFF);
+ snd_iprintf(buffer,
+ "Register: ctrl1=0x%x, ctrl2=0x%x, status1=0x%x, status2=0x%x, timecode=0x%x\n",
+ hdspm->control_register, hdspm->control2_register,
+ status, status2, timecode);
+
+ snd_iprintf(buffer, "--- Settings ---\n");
+
+ x = 1 << (6 +
+ hdspm_decode_latency(hdspm->
+ control_register &
+ HDSPM_LatencyMask));
+
+ snd_iprintf(buffer,
+ "Size (Latency): %d samples (2 periods of %lu bytes)\n",
+ x, (unsigned long) hdspm->period_bytes);
+
+ snd_iprintf(buffer, "Line out: %s, Precise Pointer: %s\n",
+ (hdspm->
+ control_register & HDSPM_LineOut) ? "on " : "off",
+ (hdspm->precise_ptr) ? "on" : "off");
+
+ snd_iprintf(buffer,
+ "ClearTrackMarker %s, Emphasis %s, Dolby %s\n",
+ (hdspm->
+ control_register & HDSPM_clr_tms) ? "on" : "off",
+ (hdspm->
+ control_register & HDSPM_Emphasis) ? "on" : "off",
+ (hdspm->
+ control_register & HDSPM_Dolby) ? "on" : "off");
+
+ switch (hdspm_clock_source(hdspm)) {
+ case HDSPM_CLOCK_SOURCE_AUTOSYNC:
+ clock_source = "AutoSync";
+ break;
+ case HDSPM_CLOCK_SOURCE_INTERNAL_32KHZ:
+ clock_source = "Internal 32 kHz";
+ break;
+ case HDSPM_CLOCK_SOURCE_INTERNAL_44_1KHZ:
+ clock_source = "Internal 44.1 kHz";
+ break;
+ case HDSPM_CLOCK_SOURCE_INTERNAL_48KHZ:
+ clock_source = "Internal 48 kHz";
+ break;
+ case HDSPM_CLOCK_SOURCE_INTERNAL_64KHZ:
+ clock_source = "Internal 64 kHz";
+ break;
+ case HDSPM_CLOCK_SOURCE_INTERNAL_88_2KHZ:
+ clock_source = "Internal 88.2 kHz";
+ break;
+ case HDSPM_CLOCK_SOURCE_INTERNAL_96KHZ:
+ clock_source = "Internal 96 kHz";
+ break;
+ case HDSPM_CLOCK_SOURCE_INTERNAL_128KHZ:
+ clock_source = "Internal 128 kHz";
+ break;
+ case HDSPM_CLOCK_SOURCE_INTERNAL_176_4KHZ:
+ clock_source = "Internal 176.4 kHz";
+ break;
+ case HDSPM_CLOCK_SOURCE_INTERNAL_192KHZ:
+ clock_source = "Internal 192 kHz";
+ break;
+ default:
+ clock_source = "Error";
+ }
+ snd_iprintf(buffer, "Sample Clock Source: %s\n", clock_source);
+ if (!(hdspm->control_register & HDSPM_ClockModeMaster))
+ system_clock_mode = "Slave";
+ else
+ system_clock_mode = "Master";
+ snd_iprintf(buffer, "System Clock Mode: %s\n", system_clock_mode);
+
+ pref_syncref = hdspm_pref_sync_ref(hdspm);
+ if (pref_syncref == 0)
+ snd_iprintf(buffer, "Preferred Sync Reference: Word Clock\n");
+ else
+ snd_iprintf(buffer, "Preferred Sync Reference: AES%d\n",
+ pref_syncref);
+
+ snd_iprintf(buffer, "System Clock Frequency: %d\n",
+ hdspm->system_sample_rate);
+
+ snd_iprintf(buffer, "Double speed: %s\n",
+ hdspm->control_register & HDSPM_DS_DoubleWire?
+ "Double wire" : "Single wire");
+ snd_iprintf(buffer, "Quad speed: %s\n",
+ hdspm->control_register & HDSPM_QS_DoubleWire?
+ "Double wire" :
+ hdspm->control_register & HDSPM_QS_QuadWire?
+ "Quad wire" : "Single wire");
+
+ snd_iprintf(buffer, "--- Status:\n");
+
+ snd_iprintf(buffer, "Word: %s Frequency: %d\n",
+ (status & HDSPM_AES32_wcLock)? "Sync " : "No Lock",
+ HDSPM_bit2freq((status >> HDSPM_AES32_wcFreq_bit) & 0xF));
+
+ for (x = 0; x < 8; x++) {
+ snd_iprintf(buffer, "AES%d: %s Frequency: %d\n",
+ x+1,
+ (status2 & (HDSPM_LockAES >> x))? "Sync ": "No Lock",
+ HDSPM_bit2freq((timecode >> (4*x)) & 0xF));
+ }
+
+ switch (hdspm_autosync_ref(hdspm)) {
+ case HDSPM_AES32_AUTOSYNC_FROM_NONE: autosync_ref="None"; break;
+ case HDSPM_AES32_AUTOSYNC_FROM_WORD: autosync_ref="Word Clock"; break;
+ case HDSPM_AES32_AUTOSYNC_FROM_AES1: autosync_ref="AES1"; break;
+ case HDSPM_AES32_AUTOSYNC_FROM_AES2: autosync_ref="AES2"; break;
+ case HDSPM_AES32_AUTOSYNC_FROM_AES3: autosync_ref="AES3"; break;
+ case HDSPM_AES32_AUTOSYNC_FROM_AES4: autosync_ref="AES4"; break;
+ case HDSPM_AES32_AUTOSYNC_FROM_AES5: autosync_ref="AES5"; break;
+ case HDSPM_AES32_AUTOSYNC_FROM_AES6: autosync_ref="AES6"; break;
+ case HDSPM_AES32_AUTOSYNC_FROM_AES7: autosync_ref="AES7"; break;
+ case HDSPM_AES32_AUTOSYNC_FROM_AES8: autosync_ref="AES8"; break;
+ default: autosync_ref = "---"; break;
+ }
+ snd_iprintf(buffer, "AutoSync ref = %s\n", autosync_ref);
+
+ snd_iprintf(buffer, "\n");
+}
+
+#ifdef CONFIG_SND_DEBUG
+static void
+snd_hdspm_proc_read_debug(struct snd_info_entry * entry,
+ struct snd_info_buffer *buffer)
+{
+ struct hdspm *hdspm = (struct hdspm *)entry->private_data;
+
+ int j,i;
+
+ for (i = 0; i < 256 /* 1024*64 */; i += j)
+ {
+ snd_iprintf(buffer, "0x%08X: ", i);
+ for (j = 0; j < 16; j += 4)
+ snd_iprintf(buffer, "%08X ", hdspm_read(hdspm, i + j));
+ snd_iprintf(buffer, "\n");
+ }
+}
+#endif
+
+
+
static void __devinit snd_hdspm_proc_init(struct hdspm * hdspm)
{
struct snd_info_entry *entry;
if (!snd_card_proc_new(hdspm->card, "hdspm", &entry))
snd_info_set_text_ops(entry, hdspm,
- snd_hdspm_proc_read);
+ hdspm->is_aes32 ?
+ snd_hdspm_proc_read_aes32 :
+ snd_hdspm_proc_read_madi);
+#ifdef CONFIG_SND_DEBUG
+ /* debug file to read all hdspm registers */
+ if (!snd_card_proc_new(hdspm->card, "debug", &entry))
+ snd_info_set_text_ops(entry, hdspm,
+ snd_hdspm_proc_read_debug);
+#endif
}
/*------------------------------------------------------------
@@ -2507,13 +3356,20 @@ static int snd_hdspm_set_defaults(struct hdspm * hdspm)
/* set defaults: */
- hdspm->control_register = HDSPM_ClockModeMaster | /* Master Cloack Mode on */
- hdspm_encode_latency(7) | /* latency maximum = 8192 samples */
- HDSPM_InputCoaxial | /* Input Coax not Optical */
- HDSPM_SyncRef_MADI | /* Madi is syncclock */
- HDSPM_LineOut | /* Analog output in */
- HDSPM_TX_64ch | /* transmit in 64ch mode */
- HDSPM_AutoInp; /* AutoInput chossing (takeover) */
+ if (hdspm->is_aes32)
+ hdspm->control_register = HDSPM_ClockModeMaster | /* Master Cloack Mode on */
+ hdspm_encode_latency(7) | /* latency maximum = 8192 samples */
+ HDSPM_SyncRef0 | /* AES1 is syncclock */
+ HDSPM_LineOut | /* Analog output in */
+ HDSPM_Professional; /* Professional mode */
+ else
+ hdspm->control_register = HDSPM_ClockModeMaster | /* Master Cloack Mode on */
+ hdspm_encode_latency(7) | /* latency maximum = 8192 samples */
+ HDSPM_InputCoaxial | /* Input Coax not Optical */
+ HDSPM_SyncRef_MADI | /* Madi is syncclock */
+ HDSPM_LineOut | /* Analog output in */
+ HDSPM_TX_64ch | /* transmit in 64ch mode */
+ HDSPM_AutoInp; /* AutoInput chossing (takeover) */
/* ! HDSPM_Frequency0|HDSPM_Frequency1 = 44.1khz */
/* ! HDSPM_DoubleSpeed HDSPM_QuadSpeed = normal speed */
@@ -2822,6 +3678,8 @@ static int snd_hdspm_hw_params(struct snd_pcm_substream *substream,
hdspm->playback_buffer =
(unsigned char *) substream->runtime->dma_area;
+ snd_printdd("Allocated sample buffer for playback at %p\n",
+ hdspm->playback_buffer);
} else {
hdspm_set_sgbuf(hdspm, sgbuf, HDSPM_pageAddressBufferIn,
params_channels(params));
@@ -2831,7 +3689,15 @@ static int snd_hdspm_hw_params(struct snd_pcm_substream *substream,
hdspm->capture_buffer =
(unsigned char *) substream->runtime->dma_area;
+ snd_printdd("Allocated sample buffer for capture at %p\n",
+ hdspm->capture_buffer);
}
+ /*
+ snd_printdd("Allocated sample buffer for %s at 0x%08X\n",
+ substream->stream == SNDRV_PCM_STREAM_PLAYBACK ?
+ "playback" : "capture",
+ snd_pcm_sgbuf_get_addr(sgbuf, 0));
+ */
return 0;
}
@@ -2982,9 +3848,10 @@ static struct snd_pcm_hardware snd_hdspm_playback_subinfo = {
SNDRV_PCM_RATE_44100 |
SNDRV_PCM_RATE_48000 |
SNDRV_PCM_RATE_64000 |
- SNDRV_PCM_RATE_88200 | SNDRV_PCM_RATE_96000),
+ SNDRV_PCM_RATE_88200 | SNDRV_PCM_RATE_96000 |
+ SNDRV_PCM_RATE_176400 | SNDRV_PCM_RATE_192000 ),
.rate_min = 32000,
- .rate_max = 96000,
+ .rate_max = 192000,
.channels_min = 1,
.channels_max = HDSPM_MAX_CHANNELS,
.buffer_bytes_max =
@@ -3006,9 +3873,10 @@ static struct snd_pcm_hardware snd_hdspm_capture_subinfo = {
SNDRV_PCM_RATE_44100 |
SNDRV_PCM_RATE_48000 |
SNDRV_PCM_RATE_64000 |
- SNDRV_PCM_RATE_88200 | SNDRV_PCM_RATE_96000),
+ SNDRV_PCM_RATE_88200 | SNDRV_PCM_RATE_96000 |
+ SNDRV_PCM_RATE_176400 | SNDRV_PCM_RATE_192000),
.rate_min = 32000,
- .rate_max = 96000,
+ .rate_max = 192000,
.channels_min = 1,
.channels_max = HDSPM_MAX_CHANNELS,
.buffer_bytes_max =
@@ -3315,7 +4183,8 @@ static int __devinit snd_hdspm_preallocate_memory(struct hdspm * hdspm)
pcm = hdspm->pcm;
- wanted = HDSPM_DMA_AREA_BYTES + 4096; /* dont know why, but it works */
+/* wanted = HDSPM_DMA_AREA_BYTES + 4096;*/ /* dont know why, but it works */
+ wanted = HDSPM_DMA_AREA_BYTES;
if ((err =
snd_pcm_lib_preallocate_pages_for_all(pcm,
@@ -3467,9 +4336,16 @@ static int __devinit snd_hdspm_create(struct snd_card *card, struct hdspm * hdsp
pci_read_config_word(hdspm->pci,
PCI_CLASS_REVISION, &hdspm->firmware_rev);
- strcpy(card->driver, "HDSPM");
+ hdspm->is_aes32 = (hdspm->firmware_rev >= HDSPM_AESREVISION);
+
strcpy(card->mixername, "Xilinx FPGA");
- hdspm->card_name = "RME HDSPM MADI";
+ if (hdspm->is_aes32) {
+ strcpy(card->driver, "HDSPAES32");
+ hdspm->card_name = "RME HDSPM AES32";
+ } else {
+ strcpy(card->driver, "HDSPM");
+ hdspm->card_name = "RME HDSPM MADI";
+ }
if ((err = pci_enable_device(pci)) < 0)
return err;
diff --git a/sound/pci/trident/trident_main.c b/sound/pci/trident/trident_main.c
index 474f2d451ae8..3bff32167f66 100644
--- a/sound/pci/trident/trident_main.c
+++ b/sound/pci/trident/trident_main.c
@@ -2627,7 +2627,7 @@ static int snd_trident_vol_control_get(struct snd_kcontrol *kcontrol,
return 0;
}
-static DECLARE_TLV_DB_SCALE(db_scale_gvol, -6375, 25, 0);
+static const DECLARE_TLV_DB_SCALE(db_scale_gvol, -6375, 25, 0);
static int snd_trident_vol_control_put(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
@@ -2844,7 +2844,7 @@ static int snd_trident_pcm_rvol_control_put(struct snd_kcontrol *kcontrol,
return change;
}
-static DECLARE_TLV_DB_SCALE(db_scale_crvol, -3175, 25, 1);
+static const DECLARE_TLV_DB_SCALE(db_scale_crvol, -3175, 25, 1);
static struct snd_kcontrol_new snd_trident_pcm_rvol_control __devinitdata =
{
diff --git a/sound/pci/via82xx.c b/sound/pci/via82xx.c
index a572b018807f..a28992269f5e 100644
--- a/sound/pci/via82xx.c
+++ b/sound/pci/via82xx.c
@@ -1699,7 +1699,7 @@ static int snd_via8233_pcmdxs_volume_put(struct snd_kcontrol *kcontrol,
return change;
}
-static DECLARE_TLV_DB_SCALE(db_scale_dxs, -9450, 150, 1);
+static const DECLARE_TLV_DB_SCALE(db_scale_dxs, -9450, 150, 1);
static struct snd_kcontrol_new snd_via8233_pcmdxs_volume_control __devinitdata = {
.name = "PCM Playback Volume",
@@ -1823,7 +1823,7 @@ static int __devinit snd_via82xx_mixer_new(struct via82xx *chip, const char *qui
ac97.private_data = chip;
ac97.private_free = snd_via82xx_mixer_free_ac97;
ac97.pci = chip->pci;
- ac97.scaps = AC97_SCAP_SKIP_MODEM;
+ ac97.scaps = AC97_SCAP_SKIP_MODEM | AC97_SCAP_POWER_SAVE;
if ((err = snd_ac97_mixer(chip->ac97_bus, &ac97, &chip->ac97)) < 0)
return err;
@@ -2357,93 +2357,59 @@ static struct via823x_info via823x_cards[] __devinitdata = {
/*
* auto detection of DXS channel supports.
*/
-struct dxs_whitelist {
- unsigned short subvendor;
- unsigned short subdevice;
- unsigned short mask;
- short action; /* new dxs_support value */
+
+static struct snd_pci_quirk dxs_whitelist[] __devinitdata = {
+ SND_PCI_QUIRK(0x1005, 0x4710, "Avance Logic Mobo", VIA_DXS_ENABLE),
+ SND_PCI_QUIRK(0x1019, 0x0996, "ESC Mobo", VIA_DXS_48K),
+ SND_PCI_QUIRK(0x1019, 0x0a81, "ECS K7VTA3 v8.0", VIA_DXS_NO_VRA),
+ SND_PCI_QUIRK(0x1019, 0x0a85, "ECS L7VMM2", VIA_DXS_NO_VRA),
+ SND_PCI_QUIRK(0x1019, 0, "ESC K8", VIA_DXS_SRC),
+ SND_PCI_QUIRK(0x1019, 0xaa01, "ESC K8T890-A", VIA_DXS_SRC),
+ SND_PCI_QUIRK(0x1025, 0x0033, "Acer Inspire 1353LM", VIA_DXS_NO_VRA),
+ SND_PCI_QUIRK(0x1025, 0x0046, "Acer Aspire 1524 WLMi", VIA_DXS_SRC),
+ SND_PCI_QUIRK(0x1043, 0, "ASUS A7/A8", VIA_DXS_NO_VRA),
+ SND_PCI_QUIRK(0x1071, 0, "Diverse Notebook", VIA_DXS_NO_VRA),
+ SND_PCI_QUIRK(0x10cf, 0x118e, "FSC Laptop", VIA_DXS_ENABLE),
+ SND_PCI_QUIRK(0x1106, 0, "ASRock", VIA_DXS_SRC),
+ SND_PCI_QUIRK(0x1297, 0xa232, "Shuttle", VIA_DXS_ENABLE),
+ SND_PCI_QUIRK(0x1297, 0xc160, "Shuttle Sk41G", VIA_DXS_ENABLE),
+ SND_PCI_QUIRK(0x1458, 0xa002, "Gigabyte GA-7VAXP", VIA_DXS_ENABLE),
+ SND_PCI_QUIRK(0x1462, 0x3800, "MSI KT266", VIA_DXS_ENABLE),
+ SND_PCI_QUIRK(0x1462, 0x7120, "MSI KT4V", VIA_DXS_ENABLE),
+ SND_PCI_QUIRK(0x1462, 0x7142, "MSI K8MM-V", VIA_DXS_ENABLE),
+ SND_PCI_QUIRK(0x1462, 0, "MSI Mobo", VIA_DXS_SRC),
+ SND_PCI_QUIRK(0x147b, 0x1401, "ABIT KD7(-RAID)", VIA_DXS_ENABLE),
+ SND_PCI_QUIRK(0x147b, 0x1411, "ABIT VA-20", VIA_DXS_ENABLE),
+ SND_PCI_QUIRK(0x147b, 0x1413, "ABIT KV8 Pro", VIA_DXS_ENABLE),
+ SND_PCI_QUIRK(0x147b, 0x1415, "ABIT AV8", VIA_DXS_NO_VRA),
+ SND_PCI_QUIRK(0x14ff, 0x0403, "Twinhead mobo", VIA_DXS_ENABLE),
+ SND_PCI_QUIRK(0x14ff, 0x0408, "Twinhead laptop", VIA_DXS_SRC),
+ SND_PCI_QUIRK(0x1558, 0x4701, "Clevo D470", VIA_DXS_SRC),
+ SND_PCI_QUIRK(0x1584, 0x8120, "Diverse Laptop", VIA_DXS_ENABLE),
+ SND_PCI_QUIRK(0x1584, 0x8123, "Targa/Uniwill", VIA_DXS_NO_VRA),
+ SND_PCI_QUIRK(0x161f, 0x202b, "Amira Notebook", VIA_DXS_NO_VRA),
+ SND_PCI_QUIRK(0x161f, 0x2032, "m680x machines", VIA_DXS_48K),
+ SND_PCI_QUIRK(0x1631, 0xe004, "PB EasyNote 3174", VIA_DXS_ENABLE),
+ SND_PCI_QUIRK(0x1695, 0x3005, "EPoX EP-8K9A", VIA_DXS_ENABLE),
+ SND_PCI_QUIRK(0x1695, 0, "EPoX mobo", VIA_DXS_SRC),
+ SND_PCI_QUIRK(0x16f3, 0, "Jetway K8", VIA_DXS_SRC),
+ SND_PCI_QUIRK(0x1734, 0, "FSC Laptop", VIA_DXS_SRC),
+ SND_PCI_QUIRK(0x1849, 0x3059, "ASRock K7VM2", VIA_DXS_NO_VRA),
+ SND_PCI_QUIRK(0x1849, 0, "ASRock mobo", VIA_DXS_SRC),
+ SND_PCI_QUIRK(0x1919, 0x200a, "Soltek SL-K8", VIA_DXS_NO_VRA),
+ SND_PCI_QUIRK(0x4005, 0x4710, "MSI K7T266", VIA_DXS_SRC),
+ { } /* terminator */
};
static int __devinit check_dxs_list(struct pci_dev *pci, int revision)
{
- static struct dxs_whitelist whitelist[] __devinitdata = {
- { .subvendor = 0x1005, .subdevice = 0x4710, .action = VIA_DXS_ENABLE }, /* Avance Logic Mobo */
- { .subvendor = 0x1019, .subdevice = 0x0996, .action = VIA_DXS_48K },
- { .subvendor = 0x1019, .subdevice = 0x0a81, .action = VIA_DXS_NO_VRA }, /* ECS K7VTA3 v8.0 */
- { .subvendor = 0x1019, .subdevice = 0x0a85, .action = VIA_DXS_NO_VRA }, /* ECS L7VMM2 */
- { .subvendor = 0x1019, .subdevice = 0xa101, .action = VIA_DXS_SRC },
- { .subvendor = 0x1019, .subdevice = 0xaa01, .action = VIA_DXS_SRC }, /* ECS K8T890-A */
- { .subvendor = 0x1025, .subdevice = 0x0033, .action = VIA_DXS_NO_VRA }, /* Acer Inspire 1353LM */
- { .subvendor = 0x1025, .subdevice = 0x0046, .action = VIA_DXS_SRC }, /* Acer Aspire 1524 WLMi */
- { .subvendor = 0x1043, .subdevice = 0x8095, .action = VIA_DXS_NO_VRA }, /* ASUS A7V8X (FIXME: possibly VIA_DXS_ENABLE?)*/
- { .subvendor = 0x1043, .subdevice = 0x80a1, .action = VIA_DXS_NO_VRA }, /* ASUS A7V8-X */
- { .subvendor = 0x1043, .subdevice = 0x80b0, .action = VIA_DXS_NO_VRA }, /* ASUS A7V600 & K8V*/
- { .subvendor = 0x1043, .subdevice = 0x810d, .action = VIA_DXS_SRC }, /* ASUS */
- { .subvendor = 0x1043, .subdevice = 0x812a, .action = VIA_DXS_SRC }, /* ASUS A8V Deluxe */
- { .subvendor = 0x1043, .subdevice = 0x8174, .action = VIA_DXS_SRC }, /* ASUS */
- { .subvendor = 0x1043, .subdevice = 0x81b9, .action = VIA_DXS_SRC }, /* ASUS A8V-MX */
- { .subvendor = 0x1071, .subdevice = 0x8375, .action = VIA_DXS_NO_VRA }, /* Vobis/Yakumo/Mitac notebook */
- { .subvendor = 0x1071, .subdevice = 0x8399, .action = VIA_DXS_NO_VRA }, /* Umax AB 595T (VIA K8N800A - VT8237) */
- { .subvendor = 0x10cf, .subdevice = 0x118e, .action = VIA_DXS_ENABLE }, /* FSC laptop */
- { .subvendor = 0x1106, .subdevice = 0x4161, .action = VIA_DXS_NO_VRA }, /* ASRock K7VT2 */
- { .subvendor = 0x1106, .subdevice = 0x4552, .action = VIA_DXS_NO_VRA }, /* QDI Kudoz 7X/600-6AL */
- { .subvendor = 0x1106, .subdevice = 0xaa01, .action = VIA_DXS_NO_VRA }, /* EPIA MII */
- { .subvendor = 0x1106, .subdevice = 0xc001, .action = VIA_DXS_SRC }, /* Insight P4-ITX */
- { .subvendor = 0x1297, .subdevice = 0xa232, .action = VIA_DXS_ENABLE }, /* Shuttle ?? */
- { .subvendor = 0x1297, .subdevice = 0xc160, .action = VIA_DXS_ENABLE }, /* Shuttle SK41G */
- { .subvendor = 0x1458, .subdevice = 0xa002, .action = VIA_DXS_ENABLE }, /* Gigabyte GA-7VAXP */
- { .subvendor = 0x1462, .subdevice = 0x0080, .action = VIA_DXS_SRC }, /* MSI K8T Neo-FIS2R */
- { .subvendor = 0x1462, .subdevice = 0x0430, .action = VIA_DXS_SRC }, /* MSI 7142 (K8MM-V) */
- { .subvendor = 0x1462, .subdevice = 0x0470, .action = VIA_DXS_SRC }, /* MSI KT880 Delta-FSR */
- { .subvendor = 0x1462, .subdevice = 0x3800, .action = VIA_DXS_ENABLE }, /* MSI KT266 */
- { .subvendor = 0x1462, .subdevice = 0x5901, .action = VIA_DXS_NO_VRA }, /* MSI KT6 Delta-SR */
- { .subvendor = 0x1462, .subdevice = 0x7023, .action = VIA_DXS_SRC }, /* MSI K8T Neo2-FI */
- { .subvendor = 0x1462, .subdevice = 0x7120, .action = VIA_DXS_ENABLE }, /* MSI KT4V */
- { .subvendor = 0x1462, .subdevice = 0x7142, .action = VIA_DXS_ENABLE }, /* MSI K8MM-V */
- { .subvendor = 0x1462, .subdevice = 0xb012, .action = VIA_DXS_SRC }, /* P4M800/VIA8237R */
- { .subvendor = 0x147b, .subdevice = 0x1401, .action = VIA_DXS_ENABLE }, /* ABIT KD7(-RAID) */
- { .subvendor = 0x147b, .subdevice = 0x1411, .action = VIA_DXS_ENABLE }, /* ABIT VA-20 */
- { .subvendor = 0x147b, .subdevice = 0x1413, .action = VIA_DXS_ENABLE }, /* ABIT KV8 Pro */
- { .subvendor = 0x147b, .subdevice = 0x1415, .action = VIA_DXS_NO_VRA }, /* Abit AV8 */
- { .subvendor = 0x14ff, .subdevice = 0x0403, .action = VIA_DXS_ENABLE }, /* Twinhead mobo */
- { .subvendor = 0x14ff, .subdevice = 0x0408, .action = VIA_DXS_SRC }, /* Twinhead laptop */
- { .subvendor = 0x1558, .subdevice = 0x4701, .action = VIA_DXS_SRC }, /* Clevo D470 */
- { .subvendor = 0x1584, .subdevice = 0x8120, .action = VIA_DXS_ENABLE }, /* Gericom/Targa/Vobis/Uniwill laptop */
- { .subvendor = 0x1584, .subdevice = 0x8123, .action = VIA_DXS_NO_VRA }, /* Uniwill (Targa Visionary XP-210) */
- { .subvendor = 0x161f, .subdevice = 0x202b, .action = VIA_DXS_NO_VRA }, /* Amira Note book */
- { .subvendor = 0x161f, .subdevice = 0x2032, .action = VIA_DXS_48K }, /* m680x machines */
- { .subvendor = 0x1631, .subdevice = 0xe004, .action = VIA_DXS_ENABLE }, /* Easy Note 3174, Packard Bell */
- { .subvendor = 0x1695, .subdevice = 0x3005, .action = VIA_DXS_ENABLE }, /* EPoX EP-8K9A */
- { .subvendor = 0x1695, .subdevice = 0x300c, .action = VIA_DXS_SRC }, /* EPoX EP-8KRAI */
- { .subvendor = 0x1695, .subdevice = 0x300e, .action = VIA_DXS_SRC }, /* EPoX 9HEAI */
- { .subvendor = 0x16f3, .subdevice = 0x6405, .action = VIA_DXS_SRC }, /* Jetway K8M8MS */
- { .subvendor = 0x1734, .subdevice = 0x1078, .action = VIA_DXS_SRC }, /* FSC Amilo L7300 */
- { .subvendor = 0x1734, .subdevice = 0x1093, .action = VIA_DXS_SRC }, /* FSC */
- { .subvendor = 0x1734, .subdevice = 0x10ab, .action = VIA_DXS_SRC }, /* FSC */
- { .subvendor = 0x1849, .subdevice = 0x3059, .action = VIA_DXS_NO_VRA }, /* ASRock K7VM2 */
- { .subvendor = 0x1849, .subdevice = 0x9739, .action = VIA_DXS_SRC }, /* ASRock mobo(?) */
- { .subvendor = 0x1849, .subdevice = 0x9761, .action = VIA_DXS_SRC }, /* ASRock mobo(?) */
- { .subvendor = 0x1919, .subdevice = 0x200a, .action = VIA_DXS_NO_VRA }, /* Soltek SL-K8Tpro-939 */
- { .subvendor = 0x4005, .subdevice = 0x4710, .action = VIA_DXS_SRC }, /* MSI K7T266 Pro2 (MS-6380 V2.0) BIOS 3.7 */
- { } /* terminator */
- };
- const struct dxs_whitelist *w;
- unsigned short subsystem_vendor;
- unsigned short subsystem_device;
-
- pci_read_config_word(pci, PCI_SUBSYSTEM_VENDOR_ID, &subsystem_vendor);
- pci_read_config_word(pci, PCI_SUBSYSTEM_ID, &subsystem_device);
+ const struct snd_pci_quirk *w;
- for (w = whitelist; w->subvendor; w++) {
- if (w->subvendor != subsystem_vendor)
- continue;
- if (w->mask) {
- if ((w->mask & subsystem_device) == w->subdevice)
- return w->action;
- } else {
- if (subsystem_device == w->subdevice)
- return w->action;
- }
+ w = snd_pci_quirk_lookup(pci, dxs_whitelist);
+ if (w) {
+ snd_printdd(KERN_INFO "via82xx: DXS white list for %s found\n",
+ w->name);
+ return w->value;
}
/* for newer revision, default to DXS_SRC */
diff --git a/sound/pci/via82xx_modem.c b/sound/pci/via82xx_modem.c
index 17d6b847585f..b338e15db0d9 100644
--- a/sound/pci/via82xx_modem.c
+++ b/sound/pci/via82xx_modem.c
@@ -900,7 +900,7 @@ static int __devinit snd_via82xx_mixer_new(struct via82xx_modem *chip)
ac97.private_data = chip;
ac97.private_free = snd_via82xx_mixer_free_ac97;
ac97.pci = chip->pci;
- ac97.scaps = AC97_SCAP_SKIP_AUDIO;
+ ac97.scaps = AC97_SCAP_SKIP_AUDIO | AC97_SCAP_POWER_SAVE;
ac97.num = chip->ac97_secondary;
if ((err = snd_ac97_mixer(chip->ac97_bus, &ac97, &chip->ac97)) < 0)
diff --git a/sound/pci/vx222/vx222.c b/sound/pci/vx222/vx222.c
index 89f58ea180b3..474eac9490ae 100644
--- a/sound/pci/vx222/vx222.c
+++ b/sound/pci/vx222/vx222.c
@@ -73,8 +73,8 @@ MODULE_DEVICE_TABLE(pci, snd_vx222_ids);
/*
*/
-static DECLARE_TLV_DB_SCALE(db_scale_old_vol, -11350, 50, 0);
-static DECLARE_TLV_DB_SCALE(db_scale_akm, -7350, 50, 0);
+static const DECLARE_TLV_DB_SCALE(db_scale_old_vol, -11350, 50, 0);
+static const DECLARE_TLV_DB_SCALE(db_scale_akm, -7350, 50, 0);
static struct snd_vx_hardware vx222_old_hw = {
diff --git a/sound/pci/vx222/vx222_ops.c b/sound/pci/vx222/vx222_ops.c
index 5e51950e05f9..55558bef7166 100644
--- a/sound/pci/vx222/vx222_ops.c
+++ b/sound/pci/vx222/vx222_ops.c
@@ -846,7 +846,7 @@ static void vx2_set_input_level(struct snd_vx222 *chip)
#define MIC_LEVEL_MAX 0xff
-static DECLARE_TLV_DB_SCALE(db_scale_mic, -6450, 50, 0);
+static const DECLARE_TLV_DB_SCALE(db_scale_mic, -6450, 50, 0);
/*
* controls API for input levels
diff --git a/sound/pci/ymfpci/ymfpci_image.h b/sound/pci/ymfpci/ymfpci_image.h
index 1b0746991669..112f2fff6c8e 100644
--- a/sound/pci/ymfpci/ymfpci_image.h
+++ b/sound/pci/ymfpci/ymfpci_image.h
@@ -1,7 +1,7 @@
#ifndef _HWMCODE_
#define _HWMCODE_
-static unsigned long DspInst[YDSXG_DSPLENGTH / 4] = {
+static u32 DspInst[YDSXG_DSPLENGTH / 4] = {
0x00000081, 0x000001a4, 0x0000000a, 0x0000002f,
0x00080253, 0x01800317, 0x0000407b, 0x0000843f,
0x0001483c, 0x0001943c, 0x0005d83c, 0x00001c3c,
@@ -12,7 +12,7 @@ static unsigned long DspInst[YDSXG_DSPLENGTH / 4] = {
0x00000000, 0x00000000, 0x00000000, 0x00000000
};
-static unsigned long CntrlInst[YDSXG_CTRLLENGTH / 4] = {
+static u32 CntrlInst[YDSXG_CTRLLENGTH / 4] = {
0x000007, 0x240007, 0x0C0007, 0x1C0007,
0x060007, 0x700002, 0x000020, 0x030040,
0x007104, 0x004286, 0x030040, 0x000F0D,
@@ -791,7 +791,7 @@ static unsigned long CntrlInst[YDSXG_CTRLLENGTH / 4] = {
// 04/09 creat
// 04/12 stop nise fix
// 06/21 WorkingOff timming
-static unsigned long CntrlInst1E[YDSXG_CTRLLENGTH / 4] = {
+static u32 CntrlInst1E[YDSXG_CTRLLENGTH / 4] = {
0x000007, 0x240007, 0x0C0007, 0x1C0007,
0x060007, 0x700002, 0x000020, 0x030040,
0x007104, 0x004286, 0x030040, 0x000F0D,
diff --git a/sound/pci/ymfpci/ymfpci_main.c b/sound/pci/ymfpci/ymfpci_main.c
index 7881944a1957..fd12674d0394 100644
--- a/sound/pci/ymfpci/ymfpci_main.c
+++ b/sound/pci/ymfpci/ymfpci_main.c
@@ -2,12 +2,6 @@
* Copyright (c) by Jaroslav Kysela <perex@suse.cz>
* Routines for control of YMF724/740/744/754 chips
*
- * BUGS:
- * --
- *
- * TODO:
- * --
- *
* 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
@@ -26,6 +20,7 @@
#include <sound/driver.h>
#include <linux/delay.h>
+#include <linux/firmware.h>
#include <linux/init.h>
#include <linux/interrupt.h>
#include <linux/pci.h>
@@ -42,10 +37,7 @@
#include <sound/mpu401.h>
#include <asm/io.h>
-
-/*
- * constants
- */
+#include <asm/byteorder.h>
/*
* common I/O routines
@@ -179,6 +171,17 @@ static u32 snd_ymfpci_calc_lpfQ(u32 rate)
return val[0];
}
+static void snd_ymfpci_pcm_441_volume_set(struct snd_ymfpci_pcm *ypcm)
+{
+ unsigned int value;
+ struct snd_ymfpci_pcm_mixer *mixer;
+
+ mixer = &ypcm->chip->pcm_mixer[ypcm->substream->number];
+ value = min_t(unsigned int, mixer->left, 0x7fff) >> 1;
+ value |= (min_t(unsigned int, mixer->right, 0x7fff) >> 1) << 16;
+ snd_ymfpci_writel(ypcm->chip, YDSXGR_BUF441OUTVOL, value);
+}
+
/*
* Hardware start management
*/
@@ -290,6 +293,10 @@ static int snd_ymfpci_voice_free(struct snd_ymfpci *chip, struct snd_ymfpci_voic
snd_assert(pvoice != NULL, return -EINVAL);
snd_ymfpci_hw_stop(chip);
spin_lock_irqsave(&chip->voice_lock, flags);
+ if (pvoice->number == chip->src441_used) {
+ chip->src441_used = -1;
+ pvoice->ypcm->use_441_slot = 0;
+ }
pvoice->use = pvoice->pcm = pvoice->synth = pvoice->midi = 0;
pvoice->ypcm = NULL;
pvoice->interrupt = NULL;
@@ -394,7 +401,7 @@ static int snd_ymfpci_playback_trigger(struct snd_pcm_substream *substream,
case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
case SNDRV_PCM_TRIGGER_RESUME:
chip->ctrl_playback[ypcm->voices[0]->number + 1] = cpu_to_le32(ypcm->voices[0]->bank_addr);
- if (ypcm->voices[1] != NULL)
+ if (ypcm->voices[1] != NULL && !ypcm->use_441_slot)
chip->ctrl_playback[ypcm->voices[1]->number + 1] = cpu_to_le32(ypcm->voices[1]->bank_addr);
ypcm->running = 1;
break;
@@ -402,7 +409,7 @@ static int snd_ymfpci_playback_trigger(struct snd_pcm_substream *substream,
case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
case SNDRV_PCM_TRIGGER_SUSPEND:
chip->ctrl_playback[ypcm->voices[0]->number + 1] = 0;
- if (ypcm->voices[1] != NULL)
+ if (ypcm->voices[1] != NULL && !ypcm->use_441_slot)
chip->ctrl_playback[ypcm->voices[1]->number + 1] = 0;
ypcm->running = 0;
break;
@@ -489,6 +496,7 @@ static void snd_ymfpci_pcm_init_voice(struct snd_ymfpci_pcm *ypcm, unsigned int
unsigned int nbank;
u32 vol_left, vol_right;
u8 use_left, use_right;
+ unsigned long flags;
snd_assert(voice != NULL, return);
if (runtime->channels == 1) {
@@ -507,11 +515,27 @@ static void snd_ymfpci_pcm_init_voice(struct snd_ymfpci_pcm *ypcm, unsigned int
vol_left = cpu_to_le32(0x40000000);
vol_right = cpu_to_le32(0x40000000);
}
+ spin_lock_irqsave(&ypcm->chip->voice_lock, flags);
format = runtime->channels == 2 ? 0x00010000 : 0;
if (snd_pcm_format_width(runtime->format) == 8)
format |= 0x80000000;
+ else if (ypcm->chip->device_id == PCI_DEVICE_ID_YAMAHA_754 &&
+ runtime->rate == 44100 && runtime->channels == 2 &&
+ voiceidx == 0 && (ypcm->chip->src441_used == -1 ||
+ ypcm->chip->src441_used == voice->number)) {
+ ypcm->chip->src441_used = voice->number;
+ ypcm->use_441_slot = 1;
+ format |= 0x10000000;
+ snd_ymfpci_pcm_441_volume_set(ypcm);
+ }
+ if (ypcm->chip->src441_used == voice->number &&
+ (format & 0x10000000) == 0) {
+ ypcm->chip->src441_used = -1;
+ ypcm->use_441_slot = 0;
+ }
if (runtime->channels == 2 && (voiceidx & 1) != 0)
format |= 1;
+ spin_unlock_irqrestore(&ypcm->chip->voice_lock, flags);
for (nbank = 0; nbank < 2; nbank++) {
bank = &voice->bank[nbank];
memset(bank, 0, sizeof(*bank));
@@ -1480,7 +1504,7 @@ static int snd_ymfpci_put_single(struct snd_kcontrol *kcontrol,
return change;
}
-static DECLARE_TLV_DB_LINEAR(db_scale_native, TLV_DB_GAIN_MUTE, 0);
+static const DECLARE_TLV_DB_LINEAR(db_scale_native, TLV_DB_GAIN_MUTE, 0);
#define YMFPCI_DOUBLE(xname, xindex, reg) \
{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, .index = xindex, \
@@ -1722,7 +1746,10 @@ static int snd_ymfpci_pcm_vol_put(struct snd_kcontrol *kcontrol,
spin_lock_irqsave(&chip->voice_lock, flags);
if (substream->runtime && substream->runtime->private_data) {
struct snd_ymfpci_pcm *ypcm = substream->runtime->private_data;
- ypcm->update_pcm_vol = 2;
+ if (!ypcm->use_441_slot)
+ ypcm->update_pcm_vol = 2;
+ else
+ snd_ymfpci_pcm_441_volume_set(ypcm);
}
spin_unlock_irqrestore(&chip->voice_lock, flags);
return 1;
@@ -1971,13 +1998,94 @@ static void snd_ymfpci_disable_dsp(struct snd_ymfpci *chip)
}
}
+#define FIRMWARE_IN_THE_KERNEL
+
+#ifdef FIRMWARE_IN_THE_KERNEL
+
#include "ymfpci_image.h"
+static struct firmware snd_ymfpci_dsp_microcode = {
+ .size = YDSXG_DSPLENGTH,
+ .data = (u8 *)DspInst,
+};
+static struct firmware snd_ymfpci_controller_microcode = {
+ .size = YDSXG_CTRLLENGTH,
+ .data = (u8 *)CntrlInst,
+};
+static struct firmware snd_ymfpci_controller_1e_microcode = {
+ .size = YDSXG_CTRLLENGTH,
+ .data = (u8 *)CntrlInst1E,
+};
+#endif
+
+#ifdef __LITTLE_ENDIAN
+static inline void snd_ymfpci_convert_from_le(const struct firmware *fw) { }
+#else
+static void snd_ymfpci_convert_from_le(const struct firmware *fw)
+{
+ int i;
+ u32 *data = (u32 *)fw->data;
+
+ for (i = 0; i < fw->size / 4; ++i)
+ le32_to_cpus(&data[i]);
+}
+#endif
+
+static int snd_ymfpci_request_firmware(struct snd_ymfpci *chip)
+{
+ int err, is_1e;
+ const char *name;
+
+ err = request_firmware(&chip->dsp_microcode, "yamaha/ds1_dsp.fw",
+ &chip->pci->dev);
+ if (err >= 0) {
+ if (chip->dsp_microcode->size == YDSXG_DSPLENGTH)
+ snd_ymfpci_convert_from_le(chip->dsp_microcode);
+ else {
+ snd_printk(KERN_ERR "DSP microcode has wrong size\n");
+ err = -EINVAL;
+ }
+ }
+ if (err < 0) {
+#ifdef FIRMWARE_IN_THE_KERNEL
+ chip->dsp_microcode = &snd_ymfpci_dsp_microcode;
+#else
+ return err;
+#endif
+ }
+ is_1e = chip->device_id == PCI_DEVICE_ID_YAMAHA_724F ||
+ chip->device_id == PCI_DEVICE_ID_YAMAHA_740C ||
+ chip->device_id == PCI_DEVICE_ID_YAMAHA_744 ||
+ chip->device_id == PCI_DEVICE_ID_YAMAHA_754;
+ name = is_1e ? "yamaha/ds1e_ctrl.fw" : "yamaha/ds1_ctrl.fw";
+ err = request_firmware(&chip->controller_microcode, name,
+ &chip->pci->dev);
+ if (err >= 0) {
+ if (chip->controller_microcode->size == YDSXG_CTRLLENGTH)
+ snd_ymfpci_convert_from_le(chip->controller_microcode);
+ else {
+ snd_printk(KERN_ERR "controller microcode"
+ " has wrong size\n");
+ err = -EINVAL;
+ }
+ }
+ if (err < 0) {
+#ifdef FIRMWARE_IN_THE_KERNEL
+ chip->controller_microcode =
+ is_1e ? &snd_ymfpci_controller_1e_microcode
+ : &snd_ymfpci_controller_microcode;
+#else
+ return err;
+#endif
+ }
+ return 0;
+}
+
static void snd_ymfpci_download_image(struct snd_ymfpci *chip)
{
int i;
u16 ctrl;
- unsigned long *inst;
+ u32 *inst;
snd_ymfpci_writel(chip, YDSXGR_NATIVEDACOUTVOL, 0x00000000);
snd_ymfpci_disable_dsp(chip);
@@ -1992,21 +2100,12 @@ static void snd_ymfpci_download_image(struct snd_ymfpci *chip)
snd_ymfpci_writew(chip, YDSXGR_GLOBALCTRL, ctrl & ~0x0007);
/* setup DSP instruction code */
+ inst = (u32 *)chip->dsp_microcode->data;
for (i = 0; i < YDSXG_DSPLENGTH / 4; i++)
- snd_ymfpci_writel(chip, YDSXGR_DSPINSTRAM + (i << 2), DspInst[i]);
+ snd_ymfpci_writel(chip, YDSXGR_DSPINSTRAM + (i << 2), inst[i]);
/* setup control instruction code */
- switch (chip->device_id) {
- case PCI_DEVICE_ID_YAMAHA_724F:
- case PCI_DEVICE_ID_YAMAHA_740C:
- case PCI_DEVICE_ID_YAMAHA_744:
- case PCI_DEVICE_ID_YAMAHA_754:
- inst = CntrlInst1E;
- break;
- default:
- inst = CntrlInst;
- break;
- }
+ inst = (u32 *)chip->controller_microcode->data;
for (i = 0; i < YDSXG_CTRLLENGTH / 4; i++)
snd_ymfpci_writel(chip, YDSXGR_CTRLINSTRAM + (i << 2), inst[i]);
@@ -2160,6 +2259,15 @@ static int snd_ymfpci_free(struct snd_ymfpci *chip)
pci_write_config_word(chip->pci, 0x40, chip->old_legacy_ctrl);
pci_disable_device(chip->pci);
+#ifdef FIRMWARE_IN_THE_KERNEL
+ if (chip->dsp_microcode != &snd_ymfpci_dsp_microcode)
+#endif
+ release_firmware(chip->dsp_microcode);
+#ifdef FIRMWARE_IN_THE_KERNEL
+ if (chip->controller_microcode != &snd_ymfpci_controller_microcode &&
+ chip->controller_microcode != &snd_ymfpci_controller_1e_microcode)
+#endif
+ release_firmware(chip->controller_microcode);
kfree(chip);
return 0;
}
@@ -2180,7 +2288,7 @@ static int saved_regs_index[] = {
YDSXGR_PRIADCLOOPVOL,
YDSXGR_NATIVEDACINVOL,
YDSXGR_NATIVEDACOUTVOL,
- // YDSXGR_BUF441OUTVOL,
+ YDSXGR_BUF441OUTVOL,
YDSXGR_NATIVEADCINVOL,
YDSXGR_SPDIFLOOPVOL,
YDSXGR_SPDIFOUTVOL,
@@ -2295,6 +2403,7 @@ int __devinit snd_ymfpci_create(struct snd_card *card,
chip->reg_area_phys = pci_resource_start(pci, 0);
chip->reg_area_virt = ioremap_nocache(chip->reg_area_phys, 0x8000);
pci_set_master(pci);
+ chip->src441_used = -1;
if ((chip->res_reg_area = request_mem_region(chip->reg_area_phys, 0x8000, "YMFPCI")) == NULL) {
snd_printk(KERN_ERR "unable to grab memory region 0x%lx-0x%lx\n", chip->reg_area_phys, chip->reg_area_phys + 0x8000 - 1);
@@ -2315,6 +2424,12 @@ int __devinit snd_ymfpci_create(struct snd_card *card,
return -EIO;
}
+ err = snd_ymfpci_request_firmware(chip);
+ if (err < 0) {
+ snd_printk(KERN_ERR "firmware request failed: %d\n", err);
+ snd_ymfpci_free(chip);
+ return err;
+ }
snd_ymfpci_download_image(chip);
udelay(100); /* seems we need a delay after downloading image.. */
diff --git a/sound/pcmcia/vx/vxp_mixer.c b/sound/pcmcia/vx/vxp_mixer.c
index bced7b623b12..2b1f996c898d 100644
--- a/sound/pcmcia/vx/vxp_mixer.c
+++ b/sound/pcmcia/vx/vxp_mixer.c
@@ -64,7 +64,7 @@ static int vx_mic_level_put(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_v
return 0;
}
-static DECLARE_TLV_DB_SCALE(db_scale_mic, -21, 3, 0);
+static const DECLARE_TLV_DB_SCALE(db_scale_mic, -21, 3, 0);
static struct snd_kcontrol_new vx_control_mic_level = {
.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
diff --git a/sound/pcmcia/vx/vxpocket.c b/sound/pcmcia/vx/vxpocket.c
index d7df59e9c647..363bcb5f08e6 100644
--- a/sound/pcmcia/vx/vxpocket.c
+++ b/sound/pcmcia/vx/vxpocket.c
@@ -91,7 +91,7 @@ static int snd_vxpocket_dev_free(struct snd_device *device)
* Only output levels can be modified
*/
-static DECLARE_TLV_DB_SCALE(db_scale_old_vol, -11350, 50, 0);
+static const DECLARE_TLV_DB_SCALE(db_scale_old_vol, -11350, 50, 0);
static struct snd_vx_hardware vxpocket_hw = {
.name = "VXPocket",
diff --git a/sound/soc/Kconfig b/sound/soc/Kconfig
new file mode 100644
index 000000000000..ec821a57f843
--- /dev/null
+++ b/sound/soc/Kconfig
@@ -0,0 +1,32 @@
+#
+# SoC audio configuration
+#
+
+menu "SoC audio support"
+ depends on SND!=n
+
+config SND_SOC_AC97_BUS
+ bool
+
+config SND_SOC
+ tristate "SoC audio support"
+ ---help---
+
+ If you want SoC support, you should say Y here and also to the
+ specific driver for your SoC below. You will also need to select the
+ specific codec(s) attached to the SoC
+
+ This SoC audio support can also be built as a module. If so, the module
+ will be called snd-soc-core.
+
+# All the supported Soc's
+menu "SoC Platforms"
+depends on SND_SOC
+source "sound/soc/at91/Kconfig"
+source "sound/soc/pxa/Kconfig"
+endmenu
+
+# Supported codecs
+source "sound/soc/codecs/Kconfig"
+
+endmenu
diff --git a/sound/soc/Makefile b/sound/soc/Makefile
new file mode 100644
index 000000000000..98e6f49dafc2
--- /dev/null
+++ b/sound/soc/Makefile
@@ -0,0 +1,4 @@
+snd-soc-core-objs := soc-core.o soc-dapm.o
+
+obj-$(CONFIG_SND_SOC) += snd-soc-core.o
+obj-$(CONFIG_SND_SOC) += codecs/ at91/ pxa/
diff --git a/sound/soc/at91/Kconfig b/sound/soc/at91/Kconfig
new file mode 100644
index 000000000000..5bcf08b728b0
--- /dev/null
+++ b/sound/soc/at91/Kconfig
@@ -0,0 +1,32 @@
+menu "SoC Audio for the Atmel AT91"
+
+config SND_AT91_SOC
+ tristate "SoC Audio for the Atmel AT91 System-on-Chip"
+ depends on ARCH_AT91 && SND
+ select SND_PCM
+ help
+ Say Y or M if you want to add support for codecs attached to
+ the AT91 SSC interface. You will also need
+ to select the audio interfaces to support below.
+
+config SND_AT91_SOC_I2S
+ tristate
+
+config SND_AT91_SOC_ETI_B1_WM8731
+ tristate "SoC I2S Audio support for WM8731-based Endrelia ETI-B1 boards"
+ depends on SND_AT91_SOC && (MACH_ETI_B1 || MACH_ETI_C1)
+ select SND_AT91_SOC_I2S
+ select SND_SOC_WM8731
+ help
+ Say Y if you want to add support for SoC audio on WM8731-based
+ Endrelia Technologies Inc ETI-B1 or ETI-C1 boards.
+
+config SND_AT91_SOC_ETI_SLAVE
+ bool "Run codec in slave Mode on Endrelia boards"
+ depends on SND_AT91_SOC_ETI_B1_WM8731
+ default n
+ help
+ Say Y if you want to run with the AT91 SSC generating the BCLK
+ and LRC signals on Endrelia boards.
+
+endmenu
diff --git a/sound/soc/at91/Makefile b/sound/soc/at91/Makefile
new file mode 100644
index 000000000000..b77b01ab2028
--- /dev/null
+++ b/sound/soc/at91/Makefile
@@ -0,0 +1,11 @@
+# AT91 Platform Support
+snd-soc-at91-objs := at91-pcm.o
+snd-soc-at91-i2s-objs := at91-i2s.o
+
+obj-$(CONFIG_SND_AT91_SOC) += snd-soc-at91.o
+obj-$(CONFIG_SND_AT91_SOC_I2S) += snd-soc-at91-i2s.o
+
+# AT91 Machine Support
+snd-soc-eti-b1-wm8731-objs := eti_b1_wm8731.o
+
+obj-$(CONFIG_SND_AT91_SOC_ETI_B1_WM8731) += snd-soc-eti-b1-wm8731.o
diff --git a/sound/soc/at91/at91-i2s.c b/sound/soc/at91/at91-i2s.c
new file mode 100644
index 000000000000..fcc544a96ba3
--- /dev/null
+++ b/sound/soc/at91/at91-i2s.c
@@ -0,0 +1,720 @@
+/*
+ * at91-i2s.c -- ALSA SoC I2S Audio Layer Platform driver
+ *
+ * Author: Frank Mandarino <fmandarino@endrelia.com>
+ * Endrelia Technologies Inc.
+ *
+ * Based on pxa2xx Platform drivers by
+ * Liam Girdwood <liam.girdwood@wolfsonmicro.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.
+ *
+ */
+
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/interrupt.h>
+#include <linux/device.h>
+#include <linux/delay.h>
+#include <linux/clk.h>
+#include <sound/driver.h>
+#include <sound/core.h>
+#include <sound/pcm.h>
+#include <sound/initval.h>
+#include <sound/soc.h>
+
+#include <asm/arch/hardware.h>
+#include <asm/arch/at91_pmc.h>
+#include <asm/arch/at91_ssc.h>
+#include <asm/arch/at91_pdc.h>
+
+#include "at91-pcm.h"
+#include "at91-i2s.h"
+
+#if 0
+#define DBG(x...) printk(KERN_DEBUG "at91-i2s:" x)
+#else
+#define DBG(x...)
+#endif
+
+#if defined(CONFIG_ARCH_AT91SAM9260)
+#define NUM_SSC_DEVICES 1
+#else
+#define NUM_SSC_DEVICES 3
+#endif
+
+
+/*
+ * SSC PDC registers required by the PCM DMA engine.
+ */
+static struct at91_pdc_regs pdc_tx_reg = {
+ .xpr = AT91_PDC_TPR,
+ .xcr = AT91_PDC_TCR,
+ .xnpr = AT91_PDC_TNPR,
+ .xncr = AT91_PDC_TNCR,
+};
+
+static struct at91_pdc_regs pdc_rx_reg = {
+ .xpr = AT91_PDC_RPR,
+ .xcr = AT91_PDC_RCR,
+ .xnpr = AT91_PDC_RNPR,
+ .xncr = AT91_PDC_RNCR,
+};
+
+/*
+ * SSC & PDC status bits for transmit and receive.
+ */
+static struct at91_ssc_mask ssc_tx_mask = {
+ .ssc_enable = AT91_SSC_TXEN,
+ .ssc_disable = AT91_SSC_TXDIS,
+ .ssc_endx = AT91_SSC_ENDTX,
+ .ssc_endbuf = AT91_SSC_TXBUFE,
+ .pdc_enable = AT91_PDC_TXTEN,
+ .pdc_disable = AT91_PDC_TXTDIS,
+};
+
+static struct at91_ssc_mask ssc_rx_mask = {
+ .ssc_enable = AT91_SSC_RXEN,
+ .ssc_disable = AT91_SSC_RXDIS,
+ .ssc_endx = AT91_SSC_ENDRX,
+ .ssc_endbuf = AT91_SSC_RXBUFF,
+ .pdc_enable = AT91_PDC_RXTEN,
+ .pdc_disable = AT91_PDC_RXTDIS,
+};
+
+
+/*
+ * DMA parameters.
+ */
+static struct at91_pcm_dma_params ssc_dma_params[NUM_SSC_DEVICES][2] = {
+ {{
+ .name = "SSC0/I2S PCM Stereo out",
+ .pdc = &pdc_tx_reg,
+ .mask = &ssc_tx_mask,
+ },
+ {
+ .name = "SSC0/I2S PCM Stereo in",
+ .pdc = &pdc_rx_reg,
+ .mask = &ssc_rx_mask,
+ }},
+#if NUM_SSC_DEVICES == 3
+ {{
+ .name = "SSC1/I2S PCM Stereo out",
+ .pdc = &pdc_tx_reg,
+ .mask = &ssc_tx_mask,
+ },
+ {
+ .name = "SSC1/I2S PCM Stereo in",
+ .pdc = &pdc_rx_reg,
+ .mask = &ssc_rx_mask,
+ }},
+ {{
+ .name = "SSC2/I2S PCM Stereo out",
+ .pdc = &pdc_tx_reg,
+ .mask = &ssc_tx_mask,
+ },
+ {
+ .name = "SSC1/I2S PCM Stereo in",
+ .pdc = &pdc_rx_reg,
+ .mask = &ssc_rx_mask,
+ }},
+#endif
+};
+
+struct at91_ssc_state {
+ u32 ssc_cmr;
+ u32 ssc_rcmr;
+ u32 ssc_rfmr;
+ u32 ssc_tcmr;
+ u32 ssc_tfmr;
+ u32 ssc_sr;
+ u32 ssc_imr;
+};
+
+static struct at91_ssc_info {
+ char *name;
+ struct at91_ssc_periph ssc;
+ spinlock_t lock; /* lock for dir_mask */
+ unsigned short dir_mask; /* 0=unused, 1=playback, 2=capture */
+ unsigned short initialized; /* 1=SSC has been initialized */
+ unsigned short daifmt;
+ unsigned short cmr_div;
+ unsigned short tcmr_period;
+ unsigned short rcmr_period;
+ struct at91_pcm_dma_params *dma_params[2];
+ struct at91_ssc_state ssc_state;
+
+} ssc_info[NUM_SSC_DEVICES] = {
+ {
+ .name = "ssc0",
+ .lock = SPIN_LOCK_UNLOCKED,
+ .dir_mask = 0,
+ .initialized = 0,
+ },
+#if NUM_SSC_DEVICES == 3
+ {
+ .name = "ssc1",
+ .lock = SPIN_LOCK_UNLOCKED,
+ .dir_mask = 0,
+ .initialized = 0,
+ },
+ {
+ .name = "ssc2",
+ .lock = SPIN_LOCK_UNLOCKED,
+ .dir_mask = 0,
+ .initialized = 0,
+ },
+#endif
+};
+
+static unsigned int at91_i2s_sysclk;
+
+/*
+ * SSC interrupt handler. Passes PDC interrupts to the DMA
+ * interrupt handler in the PCM driver.
+ */
+static irqreturn_t at91_i2s_interrupt(int irq, void *dev_id)
+{
+ struct at91_ssc_info *ssc_p = dev_id;
+ struct at91_pcm_dma_params *dma_params;
+ u32 ssc_sr;
+ int i;
+
+ ssc_sr = at91_ssc_read(ssc_p->ssc.base + AT91_SSC_SR)
+ & at91_ssc_read(ssc_p->ssc.base + AT91_SSC_IMR);
+
+ /*
+ * Loop through the substreams attached to this SSC. If
+ * a DMA-related interrupt occurred on that substream, call
+ * the DMA interrupt handler function, if one has been
+ * registered in the dma_params structure by the PCM driver.
+ */
+ for (i = 0; i < ARRAY_SIZE(ssc_p->dma_params); i++) {
+ dma_params = ssc_p->dma_params[i];
+
+ if (dma_params != NULL && dma_params->dma_intr_handler != NULL &&
+ (ssc_sr &
+ (dma_params->mask->ssc_endx | dma_params->mask->ssc_endbuf)))
+
+ dma_params->dma_intr_handler(ssc_sr, dma_params->substream);
+ }
+
+ return IRQ_HANDLED;
+}
+
+/*
+ * Startup. Only that one substream allowed in each direction.
+ */
+static int at91_i2s_startup(struct snd_pcm_substream *substream)
+{
+ struct snd_soc_pcm_runtime *rtd = substream->private_data;
+ struct at91_ssc_info *ssc_p = &ssc_info[rtd->dai->cpu_dai->id];
+ int dir_mask;
+
+ DBG("i2s_startup: SSC_SR=0x%08lx\n",
+ at91_ssc_read(ssc_p->ssc.base + AT91_SSC_SR));
+ dir_mask = substream->stream == SNDRV_PCM_STREAM_PLAYBACK ? 0x1 : 0x2;
+
+ spin_lock_irq(&ssc_p->lock);
+ if (ssc_p->dir_mask & dir_mask) {
+ spin_unlock_irq(&ssc_p->lock);
+ return -EBUSY;
+ }
+ ssc_p->dir_mask |= dir_mask;
+ spin_unlock_irq(&ssc_p->lock);
+
+ return 0;
+}
+
+/*
+ * Shutdown. Clear DMA parameters and shutdown the SSC if there
+ * are no other substreams open.
+ */
+static void at91_i2s_shutdown(struct snd_pcm_substream *substream)
+{
+ struct snd_soc_pcm_runtime *rtd = substream->private_data;
+ struct at91_ssc_info *ssc_p = &ssc_info[rtd->dai->cpu_dai->id];
+ struct at91_pcm_dma_params *dma_params;
+ int dir, dir_mask;
+
+ dir = substream->stream == SNDRV_PCM_STREAM_PLAYBACK ? 0 : 1;
+ dma_params = ssc_p->dma_params[dir];
+
+ if (dma_params != NULL) {
+ at91_ssc_write(dma_params->ssc_base + AT91_SSC_CR,
+ dma_params->mask->ssc_disable);
+ DBG("%s disabled SSC_SR=0x%08lx\n", (dir ? "receive" : "transmit"),
+ at91_ssc_read(ssc_p->ssc.base + AT91_SSC_SR));
+
+ dma_params->ssc_base = NULL;
+ dma_params->substream = NULL;
+ ssc_p->dma_params[dir] = NULL;
+ }
+
+ dir_mask = 1 << dir;
+
+ spin_lock_irq(&ssc_p->lock);
+ ssc_p->dir_mask &= ~dir_mask;
+ if (!ssc_p->dir_mask) {
+ /* Shutdown the SSC clock. */
+ DBG("Stopping pid %d clock\n", ssc_p->ssc.pid);
+ at91_sys_write(AT91_PMC_PCDR, 1<<ssc_p->ssc.pid);
+
+ if (ssc_p->initialized) {
+ free_irq(ssc_p->ssc.pid, ssc_p);
+ ssc_p->initialized = 0;
+ }
+
+ /* Reset the SSC */
+ at91_ssc_write(ssc_p->ssc.base + AT91_SSC_CR, AT91_SSC_SWRST);
+
+ /* Clear the SSC dividers */
+ ssc_p->cmr_div = ssc_p->tcmr_period = ssc_p->rcmr_period = 0;
+ }
+ spin_unlock_irq(&ssc_p->lock);
+}
+
+/*
+ * Record the SSC system clock rate.
+ */
+static int at91_i2s_set_dai_sysclk(struct snd_soc_cpu_dai *cpu_dai,
+ int clk_id, unsigned int freq, int dir)
+{
+ /*
+ * The only clock supplied to the SSC is the AT91 master clock,
+ * which is only used if the SSC is generating BCLK and/or
+ * LRC clocks.
+ */
+ switch (clk_id) {
+ case AT91_SYSCLK_MCK:
+ at91_i2s_sysclk = freq;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+/*
+ * Record the DAI format for use in hw_params().
+ */
+static int at91_i2s_set_dai_fmt(struct snd_soc_cpu_dai *cpu_dai,
+ unsigned int fmt)
+{
+ struct at91_ssc_info *ssc_p = &ssc_info[cpu_dai->id];
+
+ if ((fmt & SND_SOC_DAIFMT_FORMAT_MASK) != SND_SOC_DAIFMT_I2S)
+ return -EINVAL;
+
+ ssc_p->daifmt = fmt;
+ return 0;
+}
+
+/*
+ * Record SSC clock dividers for use in hw_params().
+ */
+static int at91_i2s_set_dai_clkdiv(struct snd_soc_cpu_dai *cpu_dai,
+ int div_id, int div)
+{
+ struct at91_ssc_info *ssc_p = &ssc_info[cpu_dai->id];
+
+ switch (div_id) {
+ case AT91SSC_CMR_DIV:
+ /*
+ * The same master clock divider is used for both
+ * transmit and receive, so if a value has already
+ * been set, it must match this value.
+ */
+ if (ssc_p->cmr_div == 0)
+ ssc_p->cmr_div = div;
+ else
+ if (div != ssc_p->cmr_div)
+ return -EBUSY;
+ break;
+
+ case AT91SSC_TCMR_PERIOD:
+ ssc_p->tcmr_period = div;
+ break;
+
+ case AT91SSC_RCMR_PERIOD:
+ ssc_p->rcmr_period = div;
+ break;
+
+ default:
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+/*
+ * Configure the SSC.
+ */
+static int at91_i2s_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params)
+{
+ struct snd_soc_pcm_runtime *rtd = substream->private_data;
+ int id = rtd->dai->cpu_dai->id;
+ struct at91_ssc_info *ssc_p = &ssc_info[id];
+ struct at91_pcm_dma_params *dma_params;
+ int dir, channels, bits;
+ u32 tfmr, rfmr, tcmr, rcmr;
+ int start_event;
+ int ret;
+
+ /*
+ * Currently, there is only one set of dma params for
+ * each direction. If more are added, this code will
+ * have to be changed to select the proper set.
+ */
+ dir = substream->stream == SNDRV_PCM_STREAM_PLAYBACK ? 0 : 1;
+
+ dma_params = &ssc_dma_params[id][dir];
+ dma_params->ssc_base = ssc_p->ssc.base;
+ dma_params->substream = substream;
+
+ ssc_p->dma_params[dir] = dma_params;
+
+ /*
+ * The cpu_dai->dma_data field is only used to communicate the
+ * appropriate DMA parameters to the pcm driver hw_params()
+ * function. It should not be used for other purposes
+ * as it is common to all substreams.
+ */
+ rtd->dai->cpu_dai->dma_data = dma_params;
+
+ channels = params_channels(params);
+
+ /*
+ * The SSC only supports up to 16-bit samples in I2S format, due
+ * to the size of the Frame Mode Register FSLEN field. Also, I2S
+ * implies signed data.
+ */
+ bits = 16;
+ dma_params->pdc_xfer_size = 2;
+
+ /*
+ * Compute SSC register settings.
+ */
+ switch (ssc_p->daifmt) {
+ case SND_SOC_DAIFMT_CBS_CFS:
+ /*
+ * SSC provides BCLK and LRC clocks.
+ *
+ * The SSC transmit and receive clocks are generated from the
+ * MCK divider, and the BCLK signal is output on the SSC TK line.
+ */
+ rcmr = (( ssc_p->rcmr_period << 24) & AT91_SSC_PERIOD)
+ | (( 1 << 16) & AT91_SSC_STTDLY)
+ | (( AT91_SSC_START_FALLING_RF ) & AT91_SSC_START)
+ | (( AT91_SSC_CK_RISING ) & AT91_SSC_CKI)
+ | (( AT91_SSC_CKO_NONE ) & AT91_SSC_CKO)
+ | (( AT91_SSC_CKS_DIV ) & AT91_SSC_CKS);
+
+ rfmr = (( AT91_SSC_FSEDGE_POSITIVE ) & AT91_SSC_FSEDGE)
+ | (( AT91_SSC_FSOS_NEGATIVE ) & AT91_SSC_FSOS)
+ | (((bits - 1) << 16) & AT91_SSC_FSLEN)
+ | (((channels - 1) << 8) & AT91_SSC_DATNB)
+ | (( 1 << 7) & AT91_SSC_MSBF)
+ | (( 0 << 5) & AT91_SSC_LOOP)
+ | (((bits - 1) << 0) & AT91_SSC_DATALEN);
+
+ tcmr = (( ssc_p->tcmr_period << 24) & AT91_SSC_PERIOD)
+ | (( 1 << 16) & AT91_SSC_STTDLY)
+ | (( AT91_SSC_START_FALLING_RF ) & AT91_SSC_START)
+ | (( AT91_SSC_CKI_FALLING ) & AT91_SSC_CKI)
+ | (( AT91_SSC_CKO_CONTINUOUS ) & AT91_SSC_CKO)
+ | (( AT91_SSC_CKS_DIV ) & AT91_SSC_CKS);
+
+ tfmr = (( AT91_SSC_FSEDGE_POSITIVE ) & AT91_SSC_FSEDGE)
+ | (( 0 << 23) & AT91_SSC_FSDEN)
+ | (( AT91_SSC_FSOS_NEGATIVE ) & AT91_SSC_FSOS)
+ | (((bits - 1) << 16) & AT91_SSC_FSLEN)
+ | (((channels - 1) << 8) & AT91_SSC_DATNB)
+ | (( 1 << 7) & AT91_SSC_MSBF)
+ | (( 0 << 5) & AT91_SSC_DATDEF)
+ | (((bits - 1) << 0) & AT91_SSC_DATALEN);
+ break;
+
+ case SND_SOC_DAIFMT_CBM_CFM:
+
+ /*
+ * CODEC supplies BCLK and LRC clocks.
+ *
+ * The SSC transmit clock is obtained from the BCLK signal on
+ * on the TK line, and the SSC receive clock is generated from the
+ * transmit clock.
+ *
+ * For single channel data, one sample is transferred on the falling
+ * edge of the LRC clock. For two channel data, one sample is
+ * transferred on both edges of the LRC clock.
+ */
+ start_event = channels == 1
+ ? AT91_SSC_START_FALLING_RF
+ : AT91_SSC_START_EDGE_RF;
+
+ rcmr = (( 0 << 24) & AT91_SSC_PERIOD)
+ | (( 1 << 16) & AT91_SSC_STTDLY)
+ | (( start_event ) & AT91_SSC_START)
+ | (( AT91_SSC_CK_RISING ) & AT91_SSC_CKI)
+ | (( AT91_SSC_CKO_NONE ) & AT91_SSC_CKO)
+ | (( AT91_SSC_CKS_CLOCK ) & AT91_SSC_CKS);
+
+ rfmr = (( AT91_SSC_FSEDGE_POSITIVE ) & AT91_SSC_FSEDGE)
+ | (( AT91_SSC_FSOS_NONE ) & AT91_SSC_FSOS)
+ | (( 0 << 16) & AT91_SSC_FSLEN)
+ | (( 0 << 8) & AT91_SSC_DATNB)
+ | (( 1 << 7) & AT91_SSC_MSBF)
+ | (( 0 << 5) & AT91_SSC_LOOP)
+ | (((bits - 1) << 0) & AT91_SSC_DATALEN);
+
+ tcmr = (( 0 << 24) & AT91_SSC_PERIOD)
+ | (( 1 << 16) & AT91_SSC_STTDLY)
+ | (( start_event ) & AT91_SSC_START)
+ | (( AT91_SSC_CKI_FALLING ) & AT91_SSC_CKI)
+ | (( AT91_SSC_CKO_NONE ) & AT91_SSC_CKO)
+ | (( AT91_SSC_CKS_PIN ) & AT91_SSC_CKS);
+
+ tfmr = (( AT91_SSC_FSEDGE_POSITIVE ) & AT91_SSC_FSEDGE)
+ | (( 0 << 23) & AT91_SSC_FSDEN)
+ | (( AT91_SSC_FSOS_NONE ) & AT91_SSC_FSOS)
+ | (( 0 << 16) & AT91_SSC_FSLEN)
+ | (( 0 << 8) & AT91_SSC_DATNB)
+ | (( 1 << 7) & AT91_SSC_MSBF)
+ | (( 0 << 5) & AT91_SSC_DATDEF)
+ | (((bits - 1) << 0) & AT91_SSC_DATALEN);
+ break;
+
+ case SND_SOC_DAIFMT_CBS_CFM:
+ case SND_SOC_DAIFMT_CBM_CFS:
+ default:
+ printk(KERN_WARNING "at91-i2s: unsupported DAI format 0x%x.\n",
+ ssc_p->daifmt);
+ return -EINVAL;
+ break;
+ }
+ DBG("RCMR=%08x RFMR=%08x TCMR=%08x TFMR=%08x\n", rcmr, rfmr, tcmr, tfmr);
+
+ if (!ssc_p->initialized) {
+
+ /* Enable PMC peripheral clock for this SSC */
+ DBG("Starting pid %d clock\n", ssc_p->ssc.pid);
+ at91_sys_write(AT91_PMC_PCER, 1<<ssc_p->ssc.pid);
+
+ /* Reset the SSC and its PDC registers */
+ at91_ssc_write(ssc_p->ssc.base + AT91_SSC_CR, AT91_SSC_SWRST);
+
+ at91_ssc_write(ssc_p->ssc.base + AT91_PDC_RPR, 0);
+ at91_ssc_write(ssc_p->ssc.base + AT91_PDC_RCR, 0);
+ at91_ssc_write(ssc_p->ssc.base + AT91_PDC_RNPR, 0);
+ at91_ssc_write(ssc_p->ssc.base + AT91_PDC_RNCR, 0);
+ at91_ssc_write(ssc_p->ssc.base + AT91_PDC_TPR, 0);
+ at91_ssc_write(ssc_p->ssc.base + AT91_PDC_TCR, 0);
+ at91_ssc_write(ssc_p->ssc.base + AT91_PDC_TNPR, 0);
+ at91_ssc_write(ssc_p->ssc.base + AT91_PDC_TNCR, 0);
+
+ if ((ret = request_irq(ssc_p->ssc.pid, at91_i2s_interrupt,
+ 0, ssc_p->name, ssc_p)) < 0) {
+ printk(KERN_WARNING "at91-i2s: request_irq failure\n");
+
+ DBG("Stopping pid %d clock\n", ssc_p->ssc.pid);
+ at91_sys_write(AT91_PMC_PCER, 1<<ssc_p->ssc.pid);
+ return ret;
+ }
+
+ ssc_p->initialized = 1;
+ }
+
+ /* set SSC clock mode register */
+ at91_ssc_write(ssc_p->ssc.base + AT91_SSC_CMR, ssc_p->cmr_div);
+
+ /* set receive clock mode and format */
+ at91_ssc_write(ssc_p->ssc.base + AT91_SSC_RCMR, rcmr);
+ at91_ssc_write(ssc_p->ssc.base + AT91_SSC_RFMR, rfmr);
+
+ /* set transmit clock mode and format */
+ at91_ssc_write(ssc_p->ssc.base + AT91_SSC_TCMR, tcmr);
+ at91_ssc_write(ssc_p->ssc.base + AT91_SSC_TFMR, tfmr);
+
+ DBG("hw_params: SSC initialized\n");
+ return 0;
+}
+
+
+static int at91_i2s_prepare(struct snd_pcm_substream *substream)
+{
+ struct snd_soc_pcm_runtime *rtd = substream->private_data;
+ struct at91_ssc_info *ssc_p = &ssc_info[rtd->dai->cpu_dai->id];
+ struct at91_pcm_dma_params *dma_params;
+ int dir;
+
+ dir = substream->stream == SNDRV_PCM_STREAM_PLAYBACK ? 0 : 1;
+ dma_params = ssc_p->dma_params[dir];
+
+ at91_ssc_write(dma_params->ssc_base + AT91_SSC_CR,
+ dma_params->mask->ssc_enable);
+
+ DBG("%s enabled SSC_SR=0x%08lx\n", dir ? "receive" : "transmit",
+ at91_ssc_read(dma_params->ssc_base + AT91_SSC_SR));
+ return 0;
+}
+
+
+#ifdef CONFIG_PM
+static int at91_i2s_suspend(struct platform_device *pdev,
+ struct snd_soc_cpu_dai *cpu_dai)
+{
+ struct at91_ssc_info *ssc_p;
+
+ if(!cpu_dai->active)
+ return 0;
+
+ ssc_p = &ssc_info[cpu_dai->id];
+
+ /* Save the status register before disabling transmit and receive. */
+ ssc_p->ssc_state.ssc_sr = at91_ssc_read(ssc_p->ssc.base + AT91_SSC_SR);
+ at91_ssc_write(ssc_p->ssc.base + AT91_SSC_CR,
+ AT91_SSC_TXDIS | AT91_SSC_RXDIS);
+
+ /* Save the current interrupt mask, then disable unmasked interrupts. */
+ ssc_p->ssc_state.ssc_imr = at91_ssc_read(ssc_p->ssc.base + AT91_SSC_IMR);
+ at91_ssc_write(ssc_p->ssc.base + AT91_SSC_IDR, ssc_p->ssc_state.ssc_imr);
+
+ ssc_p->ssc_state.ssc_cmr = at91_ssc_read(ssc_p->ssc.base + AT91_SSC_CMR);
+ ssc_p->ssc_state.ssc_rcmr = at91_ssc_read(ssc_p->ssc.base + AT91_SSC_RCMR);
+ ssc_p->ssc_state.ssc_rfmr = at91_ssc_read(ssc_p->ssc.base + AT91_SSC_RFMR);
+ ssc_p->ssc_state.ssc_tcmr = at91_ssc_read(ssc_p->ssc.base + AT91_SSC_TCMR);
+ ssc_p->ssc_state.ssc_tfmr = at91_ssc_read(ssc_p->ssc.base + AT91_SSC_TFMR);
+
+ return 0;
+}
+
+static int at91_i2s_resume(struct platform_device *pdev,
+ struct snd_soc_cpu_dai *cpu_dai)
+{
+ struct at91_ssc_info *ssc_p;
+
+ if(!cpu_dai->active)
+ return 0;
+
+ ssc_p = &ssc_info[cpu_dai->id];
+
+ at91_ssc_write(ssc_p->ssc.base + AT91_SSC_TFMR, ssc_p->ssc_state.ssc_tfmr);
+ at91_ssc_write(ssc_p->ssc.base + AT91_SSC_TCMR, ssc_p->ssc_state.ssc_tcmr);
+ at91_ssc_write(ssc_p->ssc.base + AT91_SSC_RFMR, ssc_p->ssc_state.ssc_rfmr);
+ at91_ssc_write(ssc_p->ssc.base + AT91_SSC_RCMR, ssc_p->ssc_state.ssc_rcmr);
+ at91_ssc_write(ssc_p->ssc.base + AT91_SSC_CMR, ssc_p->ssc_state.ssc_cmr);
+
+ at91_ssc_write(ssc_p->ssc.base + AT91_SSC_IER, ssc_p->ssc_state.ssc_imr);
+
+ at91_ssc_write(ssc_p->ssc.base + AT91_SSC_CR,
+ ((ssc_p->ssc_state.ssc_sr & AT91_SSC_RXENA) ? AT91_SSC_RXEN : 0) |
+ ((ssc_p->ssc_state.ssc_sr & AT91_SSC_TXENA) ? AT91_SSC_TXEN : 0));
+
+ return 0;
+}
+
+#else
+#define at91_i2s_suspend NULL
+#define at91_i2s_resume NULL
+#endif
+
+#define AT91_I2S_RATES (SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_11025 |\
+ SNDRV_PCM_RATE_16000 | SNDRV_PCM_RATE_22050 |\
+ SNDRV_PCM_RATE_32000 | SNDRV_PCM_RATE_44100 |\
+ SNDRV_PCM_RATE_48000 | SNDRV_PCM_RATE_88200 |\
+ SNDRV_PCM_RATE_96000)
+
+struct snd_soc_cpu_dai at91_i2s_dai[NUM_SSC_DEVICES] = {
+ { .name = "at91_ssc0/i2s",
+ .id = 0,
+ .type = SND_SOC_DAI_I2S,
+ .suspend = at91_i2s_suspend,
+ .resume = at91_i2s_resume,
+ .playback = {
+ .channels_min = 1,
+ .channels_max = 2,
+ .rates = AT91_I2S_RATES,
+ .formats = SNDRV_PCM_FMTBIT_S16_LE,},
+ .capture = {
+ .channels_min = 1,
+ .channels_max = 2,
+ .rates = AT91_I2S_RATES,
+ .formats = SNDRV_PCM_FMTBIT_S16_LE,},
+ .ops = {
+ .startup = at91_i2s_startup,
+ .shutdown = at91_i2s_shutdown,
+ .prepare = at91_i2s_prepare,
+ .hw_params = at91_i2s_hw_params,},
+ .dai_ops = {
+ .set_sysclk = at91_i2s_set_dai_sysclk,
+ .set_fmt = at91_i2s_set_dai_fmt,
+ .set_clkdiv = at91_i2s_set_dai_clkdiv,},
+ .private_data = &ssc_info[0].ssc,
+ },
+#if NUM_SSC_DEVICES == 3
+ { .name = "at91_ssc1/i2s",
+ .id = 1,
+ .type = SND_SOC_DAI_I2S,
+ .suspend = at91_i2s_suspend,
+ .resume = at91_i2s_resume,
+ .playback = {
+ .channels_min = 1,
+ .channels_max = 2,
+ .rates = AT91_I2S_RATES,
+ .formats = SNDRV_PCM_FMTBIT_S16_LE,},
+ .capture = {
+ .channels_min = 1,
+ .channels_max = 2,
+ .rates = AT91_I2S_RATES,
+ .formats = SNDRV_PCM_FMTBIT_S16_LE,},
+ .ops = {
+ .startup = at91_i2s_startup,
+ .shutdown = at91_i2s_shutdown,
+ .prepare = at91_i2s_prepare,
+ .hw_params = at91_i2s_hw_params,},
+ .dai_ops = {
+ .set_sysclk = at91_i2s_set_dai_sysclk,
+ .set_fmt = at91_i2s_set_dai_fmt,
+ .set_clkdiv = at91_i2s_set_dai_clkdiv,},
+ .private_data = &ssc_info[1].ssc,
+ },
+ { .name = "at91_ssc2/i2s",
+ .id = 2,
+ .type = SND_SOC_DAI_I2S,
+ .suspend = at91_i2s_suspend,
+ .resume = at91_i2s_resume,
+ .playback = {
+ .channels_min = 1,
+ .channels_max = 2,
+ .rates = AT91_I2S_RATES,
+ .formats = SNDRV_PCM_FMTBIT_S16_LE,},
+ .capture = {
+ .channels_min = 1,
+ .channels_max = 2,
+ .rates = AT91_I2S_RATES,
+ .formats = SNDRV_PCM_FMTBIT_S16_LE,},
+ .ops = {
+ .startup = at91_i2s_startup,
+ .shutdown = at91_i2s_shutdown,
+ .prepare = at91_i2s_prepare,
+ .hw_params = at91_i2s_hw_params,},
+ .dai_ops = {
+ .set_sysclk = at91_i2s_set_dai_sysclk,
+ .set_fmt = at91_i2s_set_dai_fmt,
+ .set_clkdiv = at91_i2s_set_dai_clkdiv,},
+ .private_data = &ssc_info[2].ssc,
+ },
+#endif
+};
+
+EXPORT_SYMBOL_GPL(at91_i2s_dai);
+
+/* Module information */
+MODULE_AUTHOR("Frank Mandarino, fmandarino@endrelia.com, www.endrelia.com");
+MODULE_DESCRIPTION("AT91 I2S ASoC Interface");
+MODULE_LICENSE("GPL");
diff --git a/sound/soc/at91/at91-i2s.h b/sound/soc/at91/at91-i2s.h
new file mode 100644
index 000000000000..f8a875ba0ccc
--- /dev/null
+++ b/sound/soc/at91/at91-i2s.h
@@ -0,0 +1,27 @@
+/*
+ * at91-i2s.h - ALSA I2S interface for the Atmel AT91 SoC
+ *
+ * Author: Frank Mandarino <fmandarino@endrelia.com>
+ * Endrelia Technologies Inc.
+ * Created: Jan 9, 2007
+ *
+ * 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 _AT91_I2S_H
+#define _AT91_I2S_H
+
+/* I2S system clock ids */
+#define AT91_SYSCLK_MCK 0 /* SSC uses AT91 MCK as system clock */
+
+/* I2S divider ids */
+#define AT91SSC_CMR_DIV 0 /* MCK divider for BCLK */
+#define AT91SSC_TCMR_PERIOD 1 /* BCLK divider for transmit FS */
+#define AT91SSC_RCMR_PERIOD 2 /* BCLK divider for receive FS */
+
+extern struct snd_soc_cpu_dai at91_i2s_dai[];
+
+#endif /* _AT91_I2S_H */
+
diff --git a/sound/soc/at91/at91-pcm.c b/sound/soc/at91/at91-pcm.c
new file mode 100644
index 000000000000..e88b12e7cc40
--- /dev/null
+++ b/sound/soc/at91/at91-pcm.c
@@ -0,0 +1,432 @@
+/*
+ * at91-pcm.c -- ALSA PCM interface for the Atmel AT91 SoC
+ *
+ * Author: Frank Mandarino <fmandarino@endrelia.com>
+ * Endrelia Technologies Inc.
+ * Created: Mar 3, 2006
+ *
+ * Based on pxa2xx-pcm.c by:
+ *
+ * Author: Nicolas Pitre
+ * Created: Nov 30, 2004
+ * Copyright: (C) 2004 MontaVista Software, Inc.
+ *
+ * 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.
+ */
+
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/platform_device.h>
+#include <linux/slab.h>
+#include <linux/dma-mapping.h>
+
+#include <sound/driver.h>
+#include <sound/core.h>
+#include <sound/pcm.h>
+#include <sound/pcm_params.h>
+#include <sound/soc.h>
+
+#include <asm/arch/hardware.h>
+#include <asm/arch/at91_ssc.h>
+#include <asm/arch/at91_pdc.h>
+
+#include "at91-pcm.h"
+
+#if 0
+#define DBG(x...) printk(KERN_INFO "at91-pcm: " x)
+#else
+#define DBG(x...)
+#endif
+
+static const struct snd_pcm_hardware at91_pcm_hardware = {
+ .info = SNDRV_PCM_INFO_MMAP |
+ SNDRV_PCM_INFO_MMAP_VALID |
+ SNDRV_PCM_INFO_INTERLEAVED |
+ SNDRV_PCM_INFO_PAUSE,
+ .formats = SNDRV_PCM_FMTBIT_S16_LE,
+ .period_bytes_min = 32,
+ .period_bytes_max = 8192,
+ .periods_min = 2,
+ .periods_max = 1024,
+ .buffer_bytes_max = 32 * 1024,
+};
+
+struct at91_runtime_data {
+ struct at91_pcm_dma_params *params;
+ dma_addr_t dma_buffer; /* physical address of dma buffer */
+ dma_addr_t dma_buffer_end; /* first address beyond DMA buffer */
+ size_t period_size;
+ dma_addr_t period_ptr; /* physical address of next period */
+ u32 pdc_xpr_save; /* PDC register save */
+ u32 pdc_xcr_save;
+ u32 pdc_xnpr_save;
+ u32 pdc_xncr_save;
+};
+
+static void at91_pcm_dma_irq(u32 ssc_sr,
+ struct snd_pcm_substream *substream)
+{
+ struct at91_runtime_data *prtd = substream->runtime->private_data;
+ struct at91_pcm_dma_params *params = prtd->params;
+ static int count = 0;
+
+ count++;
+
+ if (ssc_sr & params->mask->ssc_endbuf) {
+
+ printk(KERN_WARNING
+ "at91-pcm: buffer %s on %s (SSC_SR=%#x, count=%d)\n",
+ substream->stream == SNDRV_PCM_STREAM_PLAYBACK
+ ? "underrun" : "overrun",
+ params->name, ssc_sr, count);
+
+ /* re-start the PDC */
+ at91_ssc_write(params->ssc_base + AT91_PDC_PTCR, params->mask->pdc_disable);
+
+ prtd->period_ptr += prtd->period_size;
+ if (prtd->period_ptr >= prtd->dma_buffer_end) {
+ prtd->period_ptr = prtd->dma_buffer;
+ }
+
+ at91_ssc_write(params->ssc_base + params->pdc->xpr, prtd->period_ptr);
+ at91_ssc_write(params->ssc_base + params->pdc->xcr,
+ prtd->period_size / params->pdc_xfer_size);
+
+ at91_ssc_write(params->ssc_base + AT91_PDC_PTCR, params->mask->pdc_enable);
+ }
+
+ if (ssc_sr & params->mask->ssc_endx) {
+
+ /* Load the PDC next pointer and counter registers */
+ prtd->period_ptr += prtd->period_size;
+ if (prtd->period_ptr >= prtd->dma_buffer_end) {
+ prtd->period_ptr = prtd->dma_buffer;
+ }
+ at91_ssc_write(params->ssc_base + params->pdc->xnpr, prtd->period_ptr);
+ at91_ssc_write(params->ssc_base + params->pdc->xncr,
+ prtd->period_size / params->pdc_xfer_size);
+ }
+
+ snd_pcm_period_elapsed(substream);
+}
+
+static int at91_pcm_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params)
+{
+ struct snd_pcm_runtime *runtime = substream->runtime;
+ struct at91_runtime_data *prtd = runtime->private_data;
+ struct snd_soc_pcm_runtime *rtd = substream->private_data;
+
+ /* this may get called several times by oss emulation
+ * with different params */
+
+ snd_pcm_set_runtime_buffer(substream, &substream->dma_buffer);
+ runtime->dma_bytes = params_buffer_bytes(params);
+
+ prtd->params = rtd->dai->cpu_dai->dma_data;
+ prtd->params->dma_intr_handler = at91_pcm_dma_irq;
+
+ prtd->dma_buffer = runtime->dma_addr;
+ prtd->dma_buffer_end = runtime->dma_addr + runtime->dma_bytes;
+ prtd->period_size = params_period_bytes(params);
+
+ DBG("hw_params: DMA for %s initialized (dma_bytes=%d, period_size=%d)\n",
+ prtd->params->name, runtime->dma_bytes, prtd->period_size);
+ return 0;
+}
+
+static int at91_pcm_hw_free(struct snd_pcm_substream *substream)
+{
+ struct at91_runtime_data *prtd = substream->runtime->private_data;
+ struct at91_pcm_dma_params *params = prtd->params;
+
+ if (params != NULL) {
+ at91_ssc_write(params->ssc_base + AT91_PDC_PTCR, params->mask->pdc_disable);
+ prtd->params->dma_intr_handler = NULL;
+ }
+
+ return 0;
+}
+
+static int at91_pcm_prepare(struct snd_pcm_substream *substream)
+{
+ struct at91_runtime_data *prtd = substream->runtime->private_data;
+ struct at91_pcm_dma_params *params = prtd->params;
+
+ at91_ssc_write(params->ssc_base + AT91_SSC_IDR,
+ params->mask->ssc_endx | params->mask->ssc_endbuf);
+
+ at91_ssc_write(params->ssc_base + AT91_PDC_PTCR, params->mask->pdc_disable);
+ return 0;
+}
+
+static int at91_pcm_trigger(struct snd_pcm_substream *substream,
+ int cmd)
+{
+ struct at91_runtime_data *prtd = substream->runtime->private_data;
+ struct at91_pcm_dma_params *params = prtd->params;
+ int ret = 0;
+
+ switch (cmd) {
+ case SNDRV_PCM_TRIGGER_START:
+ prtd->period_ptr = prtd->dma_buffer;
+
+ at91_ssc_write(params->ssc_base + params->pdc->xpr, prtd->period_ptr);
+ at91_ssc_write(params->ssc_base + params->pdc->xcr,
+ prtd->period_size / params->pdc_xfer_size);
+
+ prtd->period_ptr += prtd->period_size;
+ at91_ssc_write(params->ssc_base + params->pdc->xnpr, prtd->period_ptr);
+ at91_ssc_write(params->ssc_base + params->pdc->xncr,
+ prtd->period_size / params->pdc_xfer_size);
+
+ DBG("trigger: period_ptr=%lx, xpr=%lx, xcr=%ld, xnpr=%lx, xncr=%ld\n",
+ (unsigned long) prtd->period_ptr,
+ at91_ssc_read(params->ssc_base + params->pdc->xpr),
+ at91_ssc_read(params->ssc_base + params->pdc->xcr),
+ at91_ssc_read(params->ssc_base + params->pdc->xnpr),
+ at91_ssc_read(params->ssc_base + params->pdc->xncr));
+
+ at91_ssc_write(params->ssc_base + AT91_SSC_IER,
+ params->mask->ssc_endx | params->mask->ssc_endbuf);
+
+ at91_ssc_write(params->ssc_base + AT91_PDC_PTCR, params->mask->pdc_enable);
+
+ DBG("sr=%lx imr=%lx\n", at91_ssc_read(params->ssc_base + AT91_SSC_SR),
+ at91_ssc_read(params->ssc_base + AT91_SSC_IER));
+ break;
+
+ case SNDRV_PCM_TRIGGER_STOP:
+ case SNDRV_PCM_TRIGGER_SUSPEND:
+ case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
+ at91_ssc_write(params->ssc_base + AT91_PDC_PTCR, params->mask->pdc_disable);
+ break;
+
+ case SNDRV_PCM_TRIGGER_RESUME:
+ case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
+ at91_ssc_write(params->ssc_base + AT91_PDC_PTCR, params->mask->pdc_enable);
+ break;
+
+ default:
+ ret = -EINVAL;
+ }
+
+ return ret;
+}
+
+static snd_pcm_uframes_t at91_pcm_pointer(
+ struct snd_pcm_substream *substream)
+{
+ struct snd_pcm_runtime *runtime = substream->runtime;
+ struct at91_runtime_data *prtd = runtime->private_data;
+ struct at91_pcm_dma_params *params = prtd->params;
+ dma_addr_t ptr;
+ snd_pcm_uframes_t x;
+
+ ptr = (dma_addr_t) at91_ssc_read(params->ssc_base + params->pdc->xpr);
+ x = bytes_to_frames(runtime, ptr - prtd->dma_buffer);
+
+ if (x == runtime->buffer_size)
+ x = 0;
+ return x;
+}
+
+static int at91_pcm_open(struct snd_pcm_substream *substream)
+{
+ struct snd_pcm_runtime *runtime = substream->runtime;
+ struct at91_runtime_data *prtd;
+ int ret = 0;
+
+ snd_soc_set_runtime_hwparams(substream, &at91_pcm_hardware);
+
+ /* ensure that buffer size is a multiple of period size */
+ ret = snd_pcm_hw_constraint_integer(runtime, SNDRV_PCM_HW_PARAM_PERIODS);
+ if (ret < 0)
+ goto out;
+
+ prtd = kzalloc(sizeof(struct at91_runtime_data), GFP_KERNEL);
+ if (prtd == NULL) {
+ ret = -ENOMEM;
+ goto out;
+ }
+ runtime->private_data = prtd;
+
+ out:
+ return ret;
+}
+
+static int at91_pcm_close(struct snd_pcm_substream *substream)
+{
+ struct at91_runtime_data *prtd = substream->runtime->private_data;
+
+ kfree(prtd);
+ return 0;
+}
+
+static int at91_pcm_mmap(struct snd_pcm_substream *substream,
+ struct vm_area_struct *vma)
+{
+ struct snd_pcm_runtime *runtime = substream->runtime;
+
+ return dma_mmap_writecombine(substream->pcm->card->dev, vma,
+ runtime->dma_area,
+ runtime->dma_addr,
+ runtime->dma_bytes);
+}
+
+struct snd_pcm_ops at91_pcm_ops = {
+ .open = at91_pcm_open,
+ .close = at91_pcm_close,
+ .ioctl = snd_pcm_lib_ioctl,
+ .hw_params = at91_pcm_hw_params,
+ .hw_free = at91_pcm_hw_free,
+ .prepare = at91_pcm_prepare,
+ .trigger = at91_pcm_trigger,
+ .pointer = at91_pcm_pointer,
+ .mmap = at91_pcm_mmap,
+};
+
+static int at91_pcm_preallocate_dma_buffer(struct snd_pcm *pcm,
+ int stream)
+{
+ struct snd_pcm_substream *substream = pcm->streams[stream].substream;
+ struct snd_dma_buffer *buf = &substream->dma_buffer;
+ size_t size = at91_pcm_hardware.buffer_bytes_max;
+
+ buf->dev.type = SNDRV_DMA_TYPE_DEV;
+ buf->dev.dev = pcm->card->dev;
+ buf->private_data = NULL;
+ buf->area = dma_alloc_writecombine(pcm->card->dev, size,
+ &buf->addr, GFP_KERNEL);
+
+ DBG("preallocate_dma_buffer: area=%p, addr=%p, size=%d\n",
+ (void *) buf->area,
+ (void *) buf->addr,
+ size);
+
+ if (!buf->area)
+ return -ENOMEM;
+
+ buf->bytes = size;
+ return 0;
+}
+
+static u64 at91_pcm_dmamask = 0xffffffff;
+
+static int at91_pcm_new(struct snd_card *card,
+ struct snd_soc_codec_dai *dai, struct snd_pcm *pcm)
+{
+ int ret = 0;
+
+ if (!card->dev->dma_mask)
+ card->dev->dma_mask = &at91_pcm_dmamask;
+ if (!card->dev->coherent_dma_mask)
+ card->dev->coherent_dma_mask = 0xffffffff;
+
+ if (dai->playback.channels_min) {
+ ret = at91_pcm_preallocate_dma_buffer(pcm,
+ SNDRV_PCM_STREAM_PLAYBACK);
+ if (ret)
+ goto out;
+ }
+
+ if (dai->capture.channels_min) {
+ ret = at91_pcm_preallocate_dma_buffer(pcm,
+ SNDRV_PCM_STREAM_CAPTURE);
+ if (ret)
+ goto out;
+ }
+ out:
+ return ret;
+}
+
+static void at91_pcm_free_dma_buffers(struct snd_pcm *pcm)
+{
+ struct snd_pcm_substream *substream;
+ struct snd_dma_buffer *buf;
+ int stream;
+
+ for (stream = 0; stream < 2; stream++) {
+ substream = pcm->streams[stream].substream;
+ if (!substream)
+ continue;
+
+ buf = &substream->dma_buffer;
+ if (!buf->area)
+ continue;
+
+ dma_free_writecombine(pcm->card->dev, buf->bytes,
+ buf->area, buf->addr);
+ buf->area = NULL;
+ }
+}
+
+#ifdef CONFIG_PM
+static int at91_pcm_suspend(struct platform_device *pdev,
+ struct snd_soc_cpu_dai *dai)
+{
+ struct snd_pcm_runtime *runtime = dai->runtime;
+ struct at91_runtime_data *prtd;
+ struct at91_pcm_dma_params *params;
+
+ if (!runtime)
+ return 0;
+
+ prtd = runtime->private_data;
+ params = prtd->params;
+
+ /* disable the PDC and save the PDC registers */
+
+ at91_ssc_write(params->ssc_base + AT91_PDC_PTCR, params->mask->pdc_disable);
+
+ prtd->pdc_xpr_save = at91_ssc_read(params->ssc_base + params->pdc->xpr);
+ prtd->pdc_xcr_save = at91_ssc_read(params->ssc_base + params->pdc->xcr);
+ prtd->pdc_xnpr_save = at91_ssc_read(params->ssc_base + params->pdc->xnpr);
+ prtd->pdc_xncr_save = at91_ssc_read(params->ssc_base + params->pdc->xncr);
+
+ return 0;
+}
+
+static int at91_pcm_resume(struct platform_device *pdev,
+ struct snd_soc_cpu_dai *dai)
+{
+ struct snd_pcm_runtime *runtime = dai->runtime;
+ struct at91_runtime_data *prtd;
+ struct at91_pcm_dma_params *params;
+
+ if (!runtime)
+ return 0;
+
+ prtd = runtime->private_data;
+ params = prtd->params;
+
+ /* restore the PDC registers and enable the PDC */
+ at91_ssc_write(params->ssc_base + params->pdc->xpr, prtd->pdc_xpr_save);
+ at91_ssc_write(params->ssc_base + params->pdc->xcr, prtd->pdc_xcr_save);
+ at91_ssc_write(params->ssc_base + params->pdc->xnpr, prtd->pdc_xnpr_save);
+ at91_ssc_write(params->ssc_base + params->pdc->xncr, prtd->pdc_xncr_save);
+
+ at91_ssc_write(params->ssc_base + AT91_PDC_PTCR, params->mask->pdc_enable);
+ return 0;
+}
+#else
+#define at91_pcm_suspend NULL
+#define at91_pcm_resume NULL
+#endif
+
+struct snd_soc_platform at91_soc_platform = {
+ .name = "at91-audio",
+ .pcm_ops = &at91_pcm_ops,
+ .pcm_new = at91_pcm_new,
+ .pcm_free = at91_pcm_free_dma_buffers,
+ .suspend = at91_pcm_suspend,
+ .resume = at91_pcm_resume,
+};
+
+EXPORT_SYMBOL_GPL(at91_soc_platform);
+
+MODULE_AUTHOR("Frank Mandarino <fmandarino@endrelia.com>");
+MODULE_DESCRIPTION("Atmel AT91 PCM module");
+MODULE_LICENSE("GPL");
diff --git a/sound/soc/at91/at91-pcm.h b/sound/soc/at91/at91-pcm.h
new file mode 100644
index 000000000000..58d0f00a07b2
--- /dev/null
+++ b/sound/soc/at91/at91-pcm.h
@@ -0,0 +1,72 @@
+/*
+ * at91-pcm.h - ALSA PCM interface for the Atmel AT91 SoC
+ *
+ * Author: Frank Mandarino <fmandarino@endrelia.com>
+ * Endrelia Technologies Inc.
+ * Created: Mar 3, 2006
+ *
+ * Based on pxa2xx-pcm.h by:
+ *
+ * Author: Nicolas Pitre
+ * Created: Nov 30, 2004
+ * Copyright: MontaVista Software, Inc.
+ *
+ * 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 _AT91_PCM_H
+#define _AT91_PCM_H
+
+#include <asm/arch/hardware.h>
+
+struct at91_ssc_periph {
+ void __iomem *base;
+ u32 pid;
+};
+
+/*
+ * Registers and status bits that are required by the PCM driver.
+ */
+struct at91_pdc_regs {
+ unsigned int xpr; /* PDC recv/trans pointer */
+ unsigned int xcr; /* PDC recv/trans counter */
+ unsigned int xnpr; /* PDC next recv/trans pointer */
+ unsigned int xncr; /* PDC next recv/trans counter */
+ unsigned int ptcr; /* PDC transfer control */
+};
+
+struct at91_ssc_mask {
+ u32 ssc_enable; /* SSC recv/trans enable */
+ u32 ssc_disable; /* SSC recv/trans disable */
+ u32 ssc_endx; /* SSC ENDTX or ENDRX */
+ u32 ssc_endbuf; /* SSC TXBUFE or RXBUFF */
+ u32 pdc_enable; /* PDC recv/trans enable */
+ u32 pdc_disable; /* PDC recv/trans disable */
+};
+
+/*
+ * This structure, shared between the PCM driver and the interface,
+ * contains all information required by the PCM driver to perform the
+ * PDC DMA operation. All fields except dma_intr_handler() are initialized
+ * by the interface. The dms_intr_handler() pointer is set by the PCM
+ * driver and called by the interface SSC interrupt handler if it is
+ * non-NULL.
+ */
+struct at91_pcm_dma_params {
+ char *name; /* stream identifier */
+ int pdc_xfer_size; /* PDC counter increment in bytes */
+ void __iomem *ssc_base; /* SSC base address */
+ struct at91_pdc_regs *pdc; /* PDC receive or transmit registers */
+ struct at91_ssc_mask *mask;/* SSC & PDC status bits */
+ struct snd_pcm_substream *substream;
+ void (*dma_intr_handler)(u32, struct snd_pcm_substream *);
+};
+
+extern struct snd_soc_platform at91_soc_platform;
+
+#define at91_ssc_read(a) ((unsigned long) __raw_readl(a))
+#define at91_ssc_write(a,v) __raw_writel((v),(a))
+
+#endif /* _AT91_PCM_H */
diff --git a/sound/soc/at91/eti_b1_wm8731.c b/sound/soc/at91/eti_b1_wm8731.c
new file mode 100644
index 000000000000..8179df3bb2f3
--- /dev/null
+++ b/sound/soc/at91/eti_b1_wm8731.c
@@ -0,0 +1,375 @@
+/*
+ * eti_b1_wm8731 -- SoC audio for AT91RM9200-based Endrelia ETI_B1 board.
+ *
+ * Author: Frank Mandarino <fmandarino@endrelia.com>
+ * Endrelia Technologies Inc.
+ * Created: Mar 29, 2006
+ *
+ * Based on corgi.c by:
+ *
+ * Copyright 2005 Wolfson Microelectronics PLC.
+ * Copyright 2005 Openedhand Ltd.
+ *
+ * Authors: Liam Girdwood <liam.girdwood@wolfsonmicro.com>
+ * Richard Purdie <richard@openedhand.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.
+ *
+ */
+
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/version.h>
+#include <linux/kernel.h>
+#include <linux/clk.h>
+#include <linux/timer.h>
+#include <linux/interrupt.h>
+#include <linux/platform_device.h>
+#include <sound/driver.h>
+#include <sound/core.h>
+#include <sound/pcm.h>
+#include <sound/soc.h>
+#include <sound/soc-dapm.h>
+
+#include <asm/arch/hardware.h>
+#include <asm/arch/at91_pio.h>
+#include <asm/arch/gpio.h>
+
+#include "../codecs/wm8731.h"
+#include "at91-pcm.h"
+#include "at91-i2s.h"
+
+#if 0
+#define DBG(x...) printk(KERN_INFO "eti_b1_wm8731: " x)
+#else
+#define DBG(x...)
+#endif
+
+#define AT91_PIO_TF1 (1 << (AT91_PIN_PB6 - PIN_BASE) % 32)
+#define AT91_PIO_TK1 (1 << (AT91_PIN_PB7 - PIN_BASE) % 32)
+#define AT91_PIO_TD1 (1 << (AT91_PIN_PB8 - PIN_BASE) % 32)
+#define AT91_PIO_RD1 (1 << (AT91_PIN_PB9 - PIN_BASE) % 32)
+#define AT91_PIO_RK1 (1 << (AT91_PIN_PB10 - PIN_BASE) % 32)
+#define AT91_PIO_RF1 (1 << (AT91_PIN_PB11 - PIN_BASE) % 32)
+
+static struct clk *pck1_clk;
+static struct clk *pllb_clk;
+
+
+static int eti_b1_startup(struct snd_pcm_substream *substream)
+{
+ struct snd_soc_pcm_runtime *rtd = substream->private_data;
+ struct snd_soc_codec_dai *codec_dai = rtd->dai->codec_dai;
+ struct snd_soc_cpu_dai *cpu_dai = rtd->dai->cpu_dai;
+ int ret;
+
+ /* cpu clock is the AT91 master clock sent to the SSC */
+ ret = cpu_dai->dai_ops.set_sysclk(cpu_dai, AT91_SYSCLK_MCK,
+ 60000000, SND_SOC_CLOCK_IN);
+ if (ret < 0)
+ return ret;
+
+ /* codec system clock is supplied by PCK1, set to 12MHz */
+ ret = codec_dai->dai_ops.set_sysclk(codec_dai, WM8731_SYSCLK,
+ 12000000, SND_SOC_CLOCK_IN);
+ if (ret < 0)
+ return ret;
+
+ /* Start PCK1 clock. */
+ clk_enable(pck1_clk);
+ DBG("pck1 started\n");
+
+ return 0;
+}
+
+static void eti_b1_shutdown(struct snd_pcm_substream *substream)
+{
+ /* Stop PCK1 clock. */
+ clk_disable(pck1_clk);
+ DBG("pck1 stopped\n");
+}
+
+static int eti_b1_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params)
+{
+ struct snd_soc_pcm_runtime *rtd = substream->private_data;
+ struct snd_soc_codec_dai *codec_dai = rtd->dai->codec_dai;
+ struct snd_soc_cpu_dai *cpu_dai = rtd->dai->cpu_dai;
+ int ret;
+
+#ifdef CONFIG_SND_AT91_SOC_ETI_SLAVE
+ unsigned int rate;
+ int cmr_div, period;
+
+ /* set codec DAI configuration */
+ ret = codec_dai->dai_ops.set_fmt(codec_dai, SND_SOC_DAIFMT_I2S |
+ SND_SOC_DAIFMT_NB_NF | SND_SOC_DAIFMT_CBS_CFS);
+ if (ret < 0)
+ return ret;
+
+ /* set cpu DAI configuration */
+ ret = cpu_dai->dai_ops.set_fmt(cpu_dai, SND_SOC_DAIFMT_I2S |
+ SND_SOC_DAIFMT_NB_NF | SND_SOC_DAIFMT_CBS_CFS);
+ if (ret < 0)
+ return ret;
+
+ /*
+ * The SSC clock dividers depend on the sample rate. The CMR.DIV
+ * field divides the system master clock MCK to drive the SSC TK
+ * signal which provides the codec BCLK. The TCMR.PERIOD and
+ * RCMR.PERIOD fields further divide the BCLK signal to drive
+ * the SSC TF and RF signals which provide the codec DACLRC and
+ * ADCLRC clocks.
+ *
+ * The dividers were determined through trial and error, where a
+ * CMR.DIV value is chosen such that the resulting BCLK value is
+ * divisible, or almost divisible, by (2 * sample rate), and then
+ * the TCMR.PERIOD or RCMR.PERIOD is BCLK / (2 * sample rate) - 1.
+ */
+ rate = params_rate(params);
+
+ switch (rate) {
+ case 8000:
+ cmr_div = 25; /* BCLK = 60MHz/(2*25) = 1.2MHz */
+ period = 74; /* LRC = BCLK/(2*(74+1)) = 8000Hz */
+ break;
+ case 32000:
+ cmr_div = 7; /* BCLK = 60MHz/(2*7) ~= 4.28571428MHz */
+ period = 66; /* LRC = BCLK/(2*(66+1)) = 31982.942Hz */
+ break;
+ case 48000:
+ cmr_div = 13; /* BCLK = 60MHz/(2*13) ~= 2.3076923MHz */
+ period = 23; /* LRC = BCLK/(2*(23+1)) = 48076.923Hz */
+ break;
+ default:
+ printk(KERN_WARNING "unsupported rate %d on ETI-B1 board\n", rate);
+ return -EINVAL;
+ }
+
+ /* set the MCK divider for BCLK */
+ ret = cpu_dai->dai_ops.set_clkdiv(cpu_dai, AT91SSC_CMR_DIV, cmr_div);
+ if (ret < 0)
+ return ret;
+
+ if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
+ /* set the BCLK divider for DACLRC */
+ ret = cpu_dai->dai_ops.set_clkdiv(cpu_dai,
+ AT91SSC_TCMR_PERIOD, period);
+ } else {
+ /* set the BCLK divider for ADCLRC */
+ ret = cpu_dai->dai_ops.set_clkdiv(cpu_dai,
+ AT91SSC_RCMR_PERIOD, period);
+ }
+ if (ret < 0)
+ return ret;
+
+#else /* CONFIG_SND_AT91_SOC_ETI_SLAVE */
+ /*
+ * Codec in Master Mode.
+ */
+
+ /* set codec DAI configuration */
+ ret = codec_dai->dai_ops.set_fmt(codec_dai, SND_SOC_DAIFMT_I2S |
+ SND_SOC_DAIFMT_NB_NF | SND_SOC_DAIFMT_CBM_CFM);
+ if (ret < 0)
+ return ret;
+
+ /* set cpu DAI configuration */
+ ret = cpu_dai->dai_ops.set_fmt(cpu_dai, SND_SOC_DAIFMT_I2S |
+ SND_SOC_DAIFMT_NB_NF | SND_SOC_DAIFMT_CBM_CFM);
+ if (ret < 0)
+ return ret;
+
+#endif /* CONFIG_SND_AT91_SOC_ETI_SLAVE */
+
+ return 0;
+}
+
+static struct snd_soc_ops eti_b1_ops = {
+ .startup = eti_b1_startup,
+ .hw_params = eti_b1_hw_params,
+ .shutdown = eti_b1_shutdown,
+};
+
+
+static const struct snd_soc_dapm_widget eti_b1_dapm_widgets[] = {
+ SND_SOC_DAPM_MIC("Int Mic", NULL),
+ SND_SOC_DAPM_SPK("Ext Spk", NULL),
+};
+
+static const char *intercon[][3] = {
+
+ /* speaker connected to LHPOUT */
+ {"Ext Spk", NULL, "LHPOUT"},
+
+ /* mic is connected to Mic Jack, with WM8731 Mic Bias */
+ {"MICIN", NULL, "Mic Bias"},
+ {"Mic Bias", NULL, "Int Mic"},
+
+ /* terminator */
+ {NULL, NULL, NULL},
+};
+
+/*
+ * Logic for a wm8731 as connected on a Endrelia ETI-B1 board.
+ */
+static int eti_b1_wm8731_init(struct snd_soc_codec *codec)
+{
+ int i;
+
+ DBG("eti_b1_wm8731_init() called\n");
+
+ /* Add specific widgets */
+ for(i = 0; i < ARRAY_SIZE(eti_b1_dapm_widgets); i++) {
+ snd_soc_dapm_new_control(codec, &eti_b1_dapm_widgets[i]);
+ }
+
+ /* Set up specific audio path interconnects */
+ for(i = 0; intercon[i][0] != NULL; i++) {
+ snd_soc_dapm_connect_input(codec, intercon[i][0],
+ intercon[i][1], intercon[i][2]);
+ }
+
+ /* not connected */
+ snd_soc_dapm_set_endpoint(codec, "RLINEIN", 0);
+ snd_soc_dapm_set_endpoint(codec, "LLINEIN", 0);
+
+ /* always connected */
+ snd_soc_dapm_set_endpoint(codec, "Int Mic", 1);
+ snd_soc_dapm_set_endpoint(codec, "Ext Spk", 1);
+
+ snd_soc_dapm_sync_endpoints(codec);
+
+ return 0;
+}
+
+static struct snd_soc_dai_link eti_b1_dai = {
+ .name = "WM8731",
+ .stream_name = "WM8731",
+ .cpu_dai = &at91_i2s_dai[1],
+ .codec_dai = &wm8731_dai,
+ .init = eti_b1_wm8731_init,
+ .ops = &eti_b1_ops,
+};
+
+static struct snd_soc_machine snd_soc_machine_eti_b1 = {
+ .name = "ETI_B1",
+ .dai_link = &eti_b1_dai,
+ .num_links = 1,
+};
+
+static struct wm8731_setup_data eti_b1_wm8731_setup = {
+ .i2c_address = 0x1a,
+};
+
+static struct snd_soc_device eti_b1_snd_devdata = {
+ .machine = &snd_soc_machine_eti_b1,
+ .platform = &at91_soc_platform,
+ .codec_dev = &soc_codec_dev_wm8731,
+ .codec_data = &eti_b1_wm8731_setup,
+};
+
+static struct platform_device *eti_b1_snd_device;
+
+static int __init eti_b1_init(void)
+{
+ int ret;
+ u32 ssc_pio_lines;
+ struct at91_ssc_periph *ssc = eti_b1_dai.cpu_dai->private_data;
+
+ if (!request_mem_region(AT91RM9200_BASE_SSC1, SZ_16K, "soc-audio")) {
+ DBG("SSC1 memory region is busy\n");
+ return -EBUSY;
+ }
+
+ ssc->base = ioremap(AT91RM9200_BASE_SSC1, SZ_16K);
+ if (!ssc->base) {
+ DBG("SSC1 memory ioremap failed\n");
+ ret = -ENOMEM;
+ goto fail_release_mem;
+ }
+
+ ssc->pid = AT91RM9200_ID_SSC1;
+
+ eti_b1_snd_device = platform_device_alloc("soc-audio", -1);
+ if (!eti_b1_snd_device) {
+ DBG("platform device allocation failed\n");
+ ret = -ENOMEM;
+ goto fail_io_unmap;
+ }
+
+ platform_set_drvdata(eti_b1_snd_device, &eti_b1_snd_devdata);
+ eti_b1_snd_devdata.dev = &eti_b1_snd_device->dev;
+
+ ret = platform_device_add(eti_b1_snd_device);
+ if (ret) {
+ DBG("platform device add failed\n");
+ platform_device_put(eti_b1_snd_device);
+ goto fail_io_unmap;
+ }
+
+ ssc_pio_lines = AT91_PIO_TF1 | AT91_PIO_TK1 | AT91_PIO_TD1
+ | AT91_PIO_RD1 /* | AT91_PIO_RK1 */ | AT91_PIO_RF1;
+
+ /* Reset all PIO registers and assign lines to peripheral A */
+ at91_sys_write(AT91_PIOB + PIO_PDR, ssc_pio_lines);
+ at91_sys_write(AT91_PIOB + PIO_ODR, ssc_pio_lines);
+ at91_sys_write(AT91_PIOB + PIO_IFDR, ssc_pio_lines);
+ at91_sys_write(AT91_PIOB + PIO_CODR, ssc_pio_lines);
+ at91_sys_write(AT91_PIOB + PIO_IDR, ssc_pio_lines);
+ at91_sys_write(AT91_PIOB + PIO_MDDR, ssc_pio_lines);
+ at91_sys_write(AT91_PIOB + PIO_PUDR, ssc_pio_lines);
+ at91_sys_write(AT91_PIOB + PIO_ASR, ssc_pio_lines);
+ at91_sys_write(AT91_PIOB + PIO_OWDR, ssc_pio_lines);
+
+ /*
+ * Set PCK1 parent to PLLB and its rate to 12 Mhz.
+ */
+ pllb_clk = clk_get(NULL, "pllb");
+ pck1_clk = clk_get(NULL, "pck1");
+
+ clk_set_parent(pck1_clk, pllb_clk);
+ clk_set_rate(pck1_clk, 12000000);
+
+ DBG("MCLK rate %luHz\n", clk_get_rate(pck1_clk));
+
+ /* assign the GPIO pin to PCK1 */
+ at91_set_B_periph(AT91_PIN_PA24, 0);
+
+#ifdef CONFIG_SND_AT91_SOC_ETI_SLAVE
+ printk(KERN_INFO "eti_b1_wm8731: Codec in Slave Mode\n");
+#else
+ printk(KERN_INFO "eti_b1_wm8731: Codec in Master Mode\n");
+#endif
+ return ret;
+
+fail_io_unmap:
+ iounmap(ssc->base);
+fail_release_mem:
+ release_mem_region(AT91RM9200_BASE_SSC1, SZ_16K);
+ return ret;
+}
+
+static void __exit eti_b1_exit(void)
+{
+ struct at91_ssc_periph *ssc = eti_b1_dai.cpu_dai->private_data;
+
+ clk_put(pck1_clk);
+ clk_put(pllb_clk);
+
+ platform_device_unregister(eti_b1_snd_device);
+
+ iounmap(ssc->base);
+ release_mem_region(AT91RM9200_BASE_SSC1, SZ_16K);
+}
+
+module_init(eti_b1_init);
+module_exit(eti_b1_exit);
+
+/* Module information */
+MODULE_AUTHOR("Frank Mandarino <fmandarino@endrelia.com>");
+MODULE_DESCRIPTION("ALSA SoC ETI-B1-WM8731");
+MODULE_LICENSE("GPL");
diff --git a/sound/soc/codecs/Kconfig b/sound/soc/codecs/Kconfig
new file mode 100644
index 000000000000..78ac2688e124
--- /dev/null
+++ b/sound/soc/codecs/Kconfig
@@ -0,0 +1,15 @@
+config SND_SOC_AC97_CODEC
+ tristate
+ depends SND_SOC
+
+config SND_SOC_WM8731
+ tristate
+ depends SND_SOC
+
+config SND_SOC_WM8750
+ tristate
+ depends SND_SOC
+
+config SND_SOC_WM9712
+ tristate
+ depends SND_SOC
diff --git a/sound/soc/codecs/Makefile b/sound/soc/codecs/Makefile
new file mode 100644
index 000000000000..3249a6e4f1d0
--- /dev/null
+++ b/sound/soc/codecs/Makefile
@@ -0,0 +1,9 @@
+snd-soc-ac97-objs := ac97.o
+snd-soc-wm8731-objs := wm8731.o
+snd-soc-wm8750-objs := wm8750.o
+snd-soc-wm9712-objs := wm9712.o
+
+obj-$(CONFIG_SND_SOC_AC97_CODEC) += snd-soc-ac97.o
+obj-$(CONFIG_SND_SOC_WM8731) += snd-soc-wm8731.o
+obj-$(CONFIG_SND_SOC_WM8750) += snd-soc-wm8750.o
+obj-$(CONFIG_SND_SOC_WM9712) += snd-soc-wm9712.o
diff --git a/sound/soc/codecs/ac97.c b/sound/soc/codecs/ac97.c
new file mode 100644
index 000000000000..55bc55eb6e24
--- /dev/null
+++ b/sound/soc/codecs/ac97.c
@@ -0,0 +1,156 @@
+/*
+ * ac97.c -- ALSA Soc AC97 codec support
+ *
+ * Copyright 2005 Wolfson Microelectronics PLC.
+ * Author: Liam Girdwood
+ * liam.girdwood@wolfsonmicro.com or linux@wolfsonmicro.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.
+ *
+ * Revision history
+ * 17th Oct 2005 Initial version.
+ *
+ * Generic AC97 support.
+ */
+
+#include <linux/init.h>
+#include <linux/kernel.h>
+#include <linux/device.h>
+#include <sound/driver.h>
+#include <sound/core.h>
+#include <sound/pcm.h>
+#include <sound/ac97_codec.h>
+#include <sound/initval.h>
+#include <sound/soc.h>
+
+#define AC97_VERSION "0.6"
+
+static int ac97_prepare(struct snd_pcm_substream *substream)
+{
+ struct snd_pcm_runtime *runtime = substream->runtime;
+ struct snd_soc_pcm_runtime *rtd = substream->private_data;
+ struct snd_soc_device *socdev = rtd->socdev;
+ struct snd_soc_codec *codec = socdev->codec;
+
+ int reg = (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) ?
+ AC97_PCM_FRONT_DAC_RATE : AC97_PCM_LR_ADC_RATE;
+ return snd_ac97_set_rate(codec->ac97, reg, runtime->rate);
+}
+
+#define STD_AC97_RATES (SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_11025 |\
+ SNDRV_PCM_RATE_22050 | SNDRV_PCM_RATE_44100 | SNDRV_PCM_RATE_48000)
+
+static struct snd_soc_codec_dai ac97_dai = {
+ .name = "AC97 HiFi",
+ .playback = {
+ .stream_name = "AC97 Playback",
+ .channels_min = 1,
+ .channels_max = 2,
+ .rates = STD_AC97_RATES,
+ .formats = SNDRV_PCM_FMTBIT_S16_LE,},
+ .capture = {
+ .stream_name = "AC97 Capture",
+ .channels_min = 1,
+ .channels_max = 2,
+ .rates = STD_AC97_RATES,
+ .formats = SNDRV_PCM_FMTBIT_S16_LE,},
+ .ops = {
+ .prepare = ac97_prepare,},
+};
+
+static unsigned int ac97_read(struct snd_soc_codec *codec,
+ unsigned int reg)
+{
+ return soc_ac97_ops.read(codec->ac97, reg);
+}
+
+static int ac97_write(struct snd_soc_codec *codec, unsigned int reg,
+ unsigned int val)
+{
+ soc_ac97_ops.write(codec->ac97, reg, val);
+ return 0;
+}
+
+static int ac97_soc_probe(struct platform_device *pdev)
+{
+ struct snd_soc_device *socdev = platform_get_drvdata(pdev);
+ struct snd_soc_codec *codec;
+ struct snd_ac97_bus *ac97_bus;
+ struct snd_ac97_template ac97_template;
+ int ret = 0;
+
+ printk(KERN_INFO "AC97 SoC Audio Codec %s\n", AC97_VERSION);
+
+ socdev->codec = kzalloc(sizeof(struct snd_soc_codec), GFP_KERNEL);
+ if (socdev->codec == NULL)
+ return -ENOMEM;
+ codec = socdev->codec;
+ mutex_init(&codec->mutex);
+
+ codec->name = "AC97";
+ codec->owner = THIS_MODULE;
+ codec->dai = &ac97_dai;
+ codec->num_dai = 1;
+ codec->write = ac97_write;
+ codec->read = ac97_read;
+ INIT_LIST_HEAD(&codec->dapm_widgets);
+ INIT_LIST_HEAD(&codec->dapm_paths);
+
+ /* register pcms */
+ ret = snd_soc_new_pcms(socdev, SNDRV_DEFAULT_IDX1, SNDRV_DEFAULT_STR1);
+ if(ret < 0)
+ goto err;
+
+ /* add codec as bus device for standard ac97 */
+ ret = snd_ac97_bus(codec->card, 0, &soc_ac97_ops, NULL, &ac97_bus);
+ if(ret < 0)
+ goto bus_err;
+
+ memset(&ac97_template, 0, sizeof(struct snd_ac97_template));
+ ret = snd_ac97_mixer(ac97_bus, &ac97_template, &codec->ac97);
+ if(ret < 0)
+ goto bus_err;
+
+ ret = snd_soc_register_card(socdev);
+ if (ret < 0)
+ goto bus_err;
+ return 0;
+
+bus_err:
+ snd_soc_free_pcms(socdev);
+
+err:
+ kfree(socdev->codec->reg_cache);
+ kfree(socdev->codec);
+ socdev->codec = NULL;
+ return ret;
+}
+
+static int ac97_soc_remove(struct platform_device *pdev)
+{
+ struct snd_soc_device *socdev = platform_get_drvdata(pdev);
+ struct snd_soc_codec *codec = socdev->codec;
+
+ if(codec == NULL)
+ return 0;
+
+ snd_soc_free_pcms(socdev);
+ kfree(socdev->codec->reg_cache);
+ kfree(socdev->codec);
+
+ return 0;
+}
+
+struct snd_soc_codec_device soc_codec_dev_ac97= {
+ .probe = ac97_soc_probe,
+ .remove = ac97_soc_remove,
+};
+
+EXPORT_SYMBOL_GPL(soc_codec_dev_ac97);
+
+MODULE_DESCRIPTION("Soc Generic AC97 driver");
+MODULE_AUTHOR("Liam Girdwood");
+MODULE_LICENSE("GPL");
diff --git a/sound/soc/codecs/ac97.h b/sound/soc/codecs/ac97.h
new file mode 100644
index 000000000000..930ddfc2321a
--- /dev/null
+++ b/sound/soc/codecs/ac97.h
@@ -0,0 +1,18 @@
+/*
+ * linux/sound/codecs/ac97.h -- ALSA SoC Layer
+ *
+ * Author: Liam Girdwood
+ * Created: Dec 1st 2005
+ * Copyright: 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.
+ */
+
+#ifndef __LINUX_SND_SOC_AC97_H
+#define __LINUX_SND_SOC_AC97_H
+
+extern struct snd_soc_codec_device soc_codec_dev_ac97;
+
+#endif
diff --git a/sound/soc/codecs/wm8731.c b/sound/soc/codecs/wm8731.c
new file mode 100644
index 000000000000..7ca0b5268289
--- /dev/null
+++ b/sound/soc/codecs/wm8731.c
@@ -0,0 +1,758 @@
+/*
+ * wm8731.c -- WM8731 ALSA SoC Audio driver
+ *
+ * Copyright 2005 Openedhand Ltd.
+ *
+ * Author: Richard Purdie <richard@openedhand.com>
+ *
+ * Based on wm8753.c by Liam Girdwood
+ *
+ * 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.
+ */
+
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/init.h>
+#include <linux/delay.h>
+#include <linux/pm.h>
+#include <linux/i2c.h>
+#include <linux/platform_device.h>
+#include <sound/driver.h>
+#include <sound/core.h>
+#include <sound/pcm.h>
+#include <sound/pcm_params.h>
+#include <sound/soc.h>
+#include <sound/soc-dapm.h>
+#include <sound/initval.h>
+
+#include "wm8731.h"
+
+#define AUDIO_NAME "wm8731"
+#define WM8731_VERSION "0.13"
+
+/*
+ * Debug
+ */
+
+#define WM8731_DEBUG 0
+
+#ifdef WM8731_DEBUG
+#define dbg(format, arg...) \
+ printk(KERN_DEBUG AUDIO_NAME ": " format "\n" , ## arg)
+#else
+#define dbg(format, arg...) do {} while (0)
+#endif
+#define err(format, arg...) \
+ printk(KERN_ERR AUDIO_NAME ": " format "\n" , ## arg)
+#define info(format, arg...) \
+ printk(KERN_INFO AUDIO_NAME ": " format "\n" , ## arg)
+#define warn(format, arg...) \
+ printk(KERN_WARNING AUDIO_NAME ": " format "\n" , ## arg)
+
+struct snd_soc_codec_device soc_codec_dev_wm8731;
+
+/* codec private data */
+struct wm8731_priv {
+ unsigned int sysclk;
+};
+
+/*
+ * wm8731 register cache
+ * We can't read the WM8731 register space when we are
+ * using 2 wire for device control, so we cache them instead.
+ * There is no point in caching the reset register
+ */
+static const u16 wm8731_reg[WM8731_CACHEREGNUM] = {
+ 0x0097, 0x0097, 0x0079, 0x0079,
+ 0x000a, 0x0008, 0x009f, 0x000a,
+ 0x0000, 0x0000
+};
+
+/*
+ * read wm8731 register cache
+ */
+static inline unsigned int wm8731_read_reg_cache(struct snd_soc_codec *codec,
+ unsigned int reg)
+{
+ u16 *cache = codec->reg_cache;
+ if (reg == WM8731_RESET)
+ return 0;
+ if (reg >= WM8731_CACHEREGNUM)
+ return -1;
+ return cache[reg];
+}
+
+/*
+ * write wm8731 register cache
+ */
+static inline void wm8731_write_reg_cache(struct snd_soc_codec *codec,
+ u16 reg, unsigned int value)
+{
+ u16 *cache = codec->reg_cache;
+ if (reg >= WM8731_CACHEREGNUM)
+ return;
+ cache[reg] = value;
+}
+
+/*
+ * write to the WM8731 register space
+ */
+static int wm8731_write(struct snd_soc_codec *codec, unsigned int reg,
+ unsigned int value)
+{
+ u8 data[2];
+
+ /* data is
+ * D15..D9 WM8731 register offset
+ * D8...D0 register data
+ */
+ data[0] = (reg << 1) | ((value >> 8) & 0x0001);
+ data[1] = value & 0x00ff;
+
+ wm8731_write_reg_cache (codec, reg, value);
+ if (codec->hw_write(codec->control_data, data, 2) == 2)
+ return 0;
+ else
+ return -EIO;
+}
+
+#define wm8731_reset(c) wm8731_write(c, WM8731_RESET, 0)
+
+static const char *wm8731_input_select[] = {"Line In", "Mic"};
+static const char *wm8731_deemph[] = {"None", "32Khz", "44.1Khz", "48Khz"};
+
+static const struct soc_enum wm8731_enum[] = {
+ SOC_ENUM_SINGLE(WM8731_APANA, 2, 2, wm8731_input_select),
+ SOC_ENUM_SINGLE(WM8731_APDIGI, 1, 4, wm8731_deemph),
+};
+
+static const struct snd_kcontrol_new wm8731_snd_controls[] = {
+
+SOC_DOUBLE_R("Master Playback Volume", WM8731_LOUT1V, WM8731_ROUT1V,
+ 0, 127, 0),
+SOC_DOUBLE_R("Master Playback ZC Switch", WM8731_LOUT1V, WM8731_ROUT1V,
+ 7, 1, 0),
+
+SOC_DOUBLE_R("Capture Volume", WM8731_LINVOL, WM8731_RINVOL, 0, 31, 0),
+SOC_DOUBLE_R("Line Capture Switch", WM8731_LINVOL, WM8731_RINVOL, 7, 1, 1),
+
+SOC_SINGLE("Mic Boost (+20dB)", WM8731_APANA, 0, 1, 0),
+SOC_SINGLE("Capture Mic Switch", WM8731_APANA, 1, 1, 1),
+
+SOC_SINGLE("Sidetone Playback Volume", WM8731_APANA, 6, 3, 1),
+
+SOC_SINGLE("ADC High Pass Filter Switch", WM8731_APDIGI, 0, 1, 1),
+SOC_SINGLE("Store DC Offset Switch", WM8731_APDIGI, 4, 1, 0),
+
+SOC_ENUM("Playback De-emphasis", wm8731_enum[1]),
+};
+
+/* add non dapm controls */
+static int wm8731_add_controls(struct snd_soc_codec *codec)
+{
+ int err, i;
+
+ for (i = 0; i < ARRAY_SIZE(wm8731_snd_controls); i++) {
+ if ((err = snd_ctl_add(codec->card,
+ snd_soc_cnew(&wm8731_snd_controls[i],codec, NULL))) < 0)
+ return err;
+ }
+
+ return 0;
+}
+
+/* Output Mixer */
+static const struct snd_kcontrol_new wm8731_output_mixer_controls[] = {
+SOC_DAPM_SINGLE("Line Bypass Switch", WM8731_APANA, 3, 1, 0),
+SOC_DAPM_SINGLE("Mic Sidetone Switch", WM8731_APANA, 5, 1, 0),
+SOC_DAPM_SINGLE("HiFi Playback Switch", WM8731_APANA, 4, 1, 0),
+};
+
+/* Input mux */
+static const struct snd_kcontrol_new wm8731_input_mux_controls =
+SOC_DAPM_ENUM("Input Select", wm8731_enum[0]);
+
+static const struct snd_soc_dapm_widget wm8731_dapm_widgets[] = {
+SND_SOC_DAPM_MIXER("Output Mixer", WM8731_PWR, 4, 1,
+ &wm8731_output_mixer_controls[0],
+ ARRAY_SIZE(wm8731_output_mixer_controls)),
+SND_SOC_DAPM_DAC("DAC", "HiFi Playback", WM8731_PWR, 3, 1),
+SND_SOC_DAPM_OUTPUT("LOUT"),
+SND_SOC_DAPM_OUTPUT("LHPOUT"),
+SND_SOC_DAPM_OUTPUT("ROUT"),
+SND_SOC_DAPM_OUTPUT("RHPOUT"),
+SND_SOC_DAPM_ADC("ADC", "HiFi Capture", WM8731_PWR, 2, 1),
+SND_SOC_DAPM_MUX("Input Mux", SND_SOC_NOPM, 0, 0, &wm8731_input_mux_controls),
+SND_SOC_DAPM_PGA("Line Input", WM8731_PWR, 0, 1, NULL, 0),
+SND_SOC_DAPM_MICBIAS("Mic Bias", WM8731_PWR, 1, 1),
+SND_SOC_DAPM_INPUT("MICIN"),
+SND_SOC_DAPM_INPUT("RLINEIN"),
+SND_SOC_DAPM_INPUT("LLINEIN"),
+};
+
+static const char *intercon[][3] = {
+ /* output mixer */
+ {"Output Mixer", "Line Bypass Switch", "Line Input"},
+ {"Output Mixer", "HiFi Playback Switch", "DAC"},
+ {"Output Mixer", "Mic Sidetone Switch", "Mic Bias"},
+
+ /* outputs */
+ {"RHPOUT", NULL, "Output Mixer"},
+ {"ROUT", NULL, "Output Mixer"},
+ {"LHPOUT", NULL, "Output Mixer"},
+ {"LOUT", NULL, "Output Mixer"},
+
+ /* input mux */
+ {"Input Mux", "Line In", "Line Input"},
+ {"Input Mux", "Mic", "Mic Bias"},
+ {"ADC", NULL, "Input Mux"},
+
+ /* inputs */
+ {"Line Input", NULL, "LLINEIN"},
+ {"Line Input", NULL, "RLINEIN"},
+ {"Mic Bias", NULL, "MICIN"},
+
+ /* terminator */
+ {NULL, NULL, NULL},
+};
+
+static int wm8731_add_widgets(struct snd_soc_codec *codec)
+{
+ int i;
+
+ for(i = 0; i < ARRAY_SIZE(wm8731_dapm_widgets); i++) {
+ snd_soc_dapm_new_control(codec, &wm8731_dapm_widgets[i]);
+ }
+
+ /* set up audio path interconnects */
+ for(i = 0; intercon[i][0] != NULL; i++) {
+ snd_soc_dapm_connect_input(codec, intercon[i][0],
+ intercon[i][1], intercon[i][2]);
+ }
+
+ snd_soc_dapm_new_widgets(codec);
+ return 0;
+}
+
+struct _coeff_div {
+ u32 mclk;
+ u32 rate;
+ u16 fs;
+ u8 sr:4;
+ u8 bosr:1;
+ u8 usb:1;
+};
+
+/* codec mclk clock divider coefficients */
+static const struct _coeff_div coeff_div[] = {
+ /* 48k */
+ {12288000, 48000, 256, 0x0, 0x0, 0x0},
+ {18432000, 48000, 384, 0x0, 0x1, 0x0},
+ {12000000, 48000, 250, 0x0, 0x0, 0x1},
+
+ /* 32k */
+ {12288000, 32000, 384, 0x6, 0x0, 0x0},
+ {18432000, 32000, 576, 0x6, 0x1, 0x0},
+ {12000000, 32000, 375, 0x6, 0x0, 0x1},
+
+ /* 8k */
+ {12288000, 8000, 1536, 0x3, 0x0, 0x0},
+ {18432000, 8000, 2304, 0x3, 0x1, 0x0},
+ {11289600, 8000, 1408, 0xb, 0x0, 0x0},
+ {16934400, 8000, 2112, 0xb, 0x1, 0x0},
+ {12000000, 8000, 1500, 0x3, 0x0, 0x1},
+
+ /* 96k */
+ {12288000, 96000, 128, 0x7, 0x0, 0x0},
+ {18432000, 96000, 192, 0x7, 0x1, 0x0},
+ {12000000, 96000, 125, 0x7, 0x0, 0x1},
+
+ /* 44.1k */
+ {11289600, 44100, 256, 0x8, 0x0, 0x0},
+ {16934400, 44100, 384, 0x8, 0x1, 0x0},
+ {12000000, 44100, 272, 0x8, 0x1, 0x1},
+
+ /* 88.2k */
+ {11289600, 88200, 128, 0xf, 0x0, 0x0},
+ {16934400, 88200, 192, 0xf, 0x1, 0x0},
+ {12000000, 88200, 136, 0xf, 0x1, 0x1},
+};
+
+static inline int get_coeff(int mclk, int rate)
+{
+ int i;
+
+ for (i = 0; i < ARRAY_SIZE(coeff_div); i++) {
+ if (coeff_div[i].rate == rate && coeff_div[i].mclk == mclk)
+ return i;
+ }
+ return 0;
+}
+
+static int wm8731_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params)
+{
+ struct snd_soc_pcm_runtime *rtd = substream->private_data;
+ struct snd_soc_device *socdev = rtd->socdev;
+ struct snd_soc_codec *codec = socdev->codec;
+ struct wm8731_priv *wm8731 = codec->private_data;
+ u16 iface = wm8731_read_reg_cache(codec, WM8731_IFACE) & 0xfff3;
+ int i = get_coeff(wm8731->sysclk, params_rate(params));
+ u16 srate = (coeff_div[i].sr << 2) |
+ (coeff_div[i].bosr << 1) | coeff_div[i].usb;
+
+ wm8731_write(codec, WM8731_SRATE, srate);
+
+ /* bit size */
+ switch (params_format(params)) {
+ case SNDRV_PCM_FORMAT_S16_LE:
+ break;
+ case SNDRV_PCM_FORMAT_S20_3LE:
+ iface |= 0x0004;
+ break;
+ case SNDRV_PCM_FORMAT_S24_LE:
+ iface |= 0x0008;
+ break;
+ }
+
+ wm8731_write(codec, WM8731_IFACE, iface);
+ return 0;
+}
+
+static int wm8731_pcm_prepare(struct snd_pcm_substream *substream)
+{
+ struct snd_soc_pcm_runtime *rtd = substream->private_data;
+ struct snd_soc_device *socdev = rtd->socdev;
+ struct snd_soc_codec *codec = socdev->codec;
+
+ /* set active */
+ wm8731_write(codec, WM8731_ACTIVE, 0x0001);
+
+ return 0;
+}
+
+static void wm8731_shutdown(struct snd_pcm_substream *substream)
+{
+ struct snd_soc_pcm_runtime *rtd = substream->private_data;
+ struct snd_soc_device *socdev = rtd->socdev;
+ struct snd_soc_codec *codec = socdev->codec;
+
+ /* deactivate */
+ if (!codec->active) {
+ udelay(50);
+ wm8731_write(codec, WM8731_ACTIVE, 0x0);
+ }
+}
+
+static int wm8731_mute(struct snd_soc_codec_dai *dai, int mute)
+{
+ struct snd_soc_codec *codec = dai->codec;
+ u16 mute_reg = wm8731_read_reg_cache(codec, WM8731_APDIGI) & 0xfff7;
+
+ if (mute)
+ wm8731_write(codec, WM8731_APDIGI, mute_reg | 0x8);
+ else
+ wm8731_write(codec, WM8731_APDIGI, mute_reg);
+ return 0;
+}
+
+static int wm8731_set_dai_sysclk(struct snd_soc_codec_dai *codec_dai,
+ int clk_id, unsigned int freq, int dir)
+{
+ struct snd_soc_codec *codec = codec_dai->codec;
+ struct wm8731_priv *wm8731 = codec->private_data;
+
+ switch (freq) {
+ case 11289600:
+ case 12000000:
+ case 12288000:
+ case 16934400:
+ case 18432000:
+ wm8731->sysclk = freq;
+ return 0;
+ }
+ return -EINVAL;
+}
+
+
+static int wm8731_set_dai_fmt(struct snd_soc_codec_dai *codec_dai,
+ unsigned int fmt)
+{
+ struct snd_soc_codec *codec = codec_dai->codec;
+ u16 iface = 0;
+
+ /* set master/slave audio interface */
+ switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) {
+ case SND_SOC_DAIFMT_CBM_CFM:
+ iface |= 0x0040;
+ break;
+ case SND_SOC_DAIFMT_CBS_CFS:
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ /* interface format */
+ switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
+ case SND_SOC_DAIFMT_I2S:
+ iface |= 0x0002;
+ break;
+ case SND_SOC_DAIFMT_RIGHT_J:
+ break;
+ case SND_SOC_DAIFMT_LEFT_J:
+ iface |= 0x0001;
+ break;
+ case SND_SOC_DAIFMT_DSP_A:
+ iface |= 0x0003;
+ break;
+ case SND_SOC_DAIFMT_DSP_B:
+ iface |= 0x0013;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ /* clock inversion */
+ switch (fmt & SND_SOC_DAIFMT_INV_MASK) {
+ case SND_SOC_DAIFMT_NB_NF:
+ break;
+ case SND_SOC_DAIFMT_IB_IF:
+ iface |= 0x0090;
+ break;
+ case SND_SOC_DAIFMT_IB_NF:
+ iface |= 0x0080;
+ break;
+ case SND_SOC_DAIFMT_NB_IF:
+ iface |= 0x0010;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ /* set iface */
+ wm8731_write(codec, WM8731_IFACE, iface);
+ return 0;
+}
+
+static int wm8731_dapm_event(struct snd_soc_codec *codec, int event)
+{
+ u16 reg = wm8731_read_reg_cache(codec, WM8731_PWR) & 0xff7f;
+
+ switch (event) {
+ case SNDRV_CTL_POWER_D0: /* full On */
+ /* vref/mid, osc on, dac unmute */
+ wm8731_write(codec, WM8731_PWR, reg);
+ break;
+ case SNDRV_CTL_POWER_D1: /* partial On */
+ case SNDRV_CTL_POWER_D2: /* partial On */
+ break;
+ case SNDRV_CTL_POWER_D3hot: /* Off, with power */
+ /* everything off except vref/vmid, */
+ wm8731_write(codec, WM8731_PWR, reg | 0x0040);
+ break;
+ case SNDRV_CTL_POWER_D3cold: /* Off, without power */
+ /* everything off, dac mute, inactive */
+ wm8731_write(codec, WM8731_ACTIVE, 0x0);
+ wm8731_write(codec, WM8731_PWR, 0xffff);
+ break;
+ }
+ codec->dapm_state = event;
+ return 0;
+}
+
+#define WM8731_RATES (SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_11025 |\
+ SNDRV_PCM_RATE_16000 | SNDRV_PCM_RATE_22050 |\
+ SNDRV_PCM_RATE_32000 | SNDRV_PCM_RATE_44100 |\
+ SNDRV_PCM_RATE_48000 | SNDRV_PCM_RATE_88200 |\
+ SNDRV_PCM_RATE_96000)
+
+#define WM8731_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S20_3LE |\
+ SNDRV_PCM_FMTBIT_S24_LE)
+
+struct snd_soc_codec_dai wm8731_dai = {
+ .name = "WM8731",
+ .playback = {
+ .stream_name = "Playback",
+ .channels_min = 1,
+ .channels_max = 2,
+ .rates = WM8731_RATES,
+ .formats = WM8731_FORMATS,},
+ .capture = {
+ .stream_name = "Capture",
+ .channels_min = 1,
+ .channels_max = 2,
+ .rates = WM8731_RATES,
+ .formats = WM8731_FORMATS,},
+ .ops = {
+ .prepare = wm8731_pcm_prepare,
+ .hw_params = wm8731_hw_params,
+ .shutdown = wm8731_shutdown,
+ },
+ .dai_ops = {
+ .digital_mute = wm8731_mute,
+ .set_sysclk = wm8731_set_dai_sysclk,
+ .set_fmt = wm8731_set_dai_fmt,
+ }
+};
+EXPORT_SYMBOL_GPL(wm8731_dai);
+
+static int wm8731_suspend(struct platform_device *pdev, pm_message_t state)
+{
+ struct snd_soc_device *socdev = platform_get_drvdata(pdev);
+ struct snd_soc_codec *codec = socdev->codec;
+
+ wm8731_write(codec, WM8731_ACTIVE, 0x0);
+ wm8731_dapm_event(codec, SNDRV_CTL_POWER_D3cold);
+ return 0;
+}
+
+static int wm8731_resume(struct platform_device *pdev)
+{
+ struct snd_soc_device *socdev = platform_get_drvdata(pdev);
+ struct snd_soc_codec *codec = socdev->codec;
+ int i;
+ u8 data[2];
+ u16 *cache = codec->reg_cache;
+
+ /* Sync reg_cache with the hardware */
+ for (i = 0; i < ARRAY_SIZE(wm8731_reg); i++) {
+ data[0] = (i << 1) | ((cache[i] >> 8) & 0x0001);
+ data[1] = cache[i] & 0x00ff;
+ codec->hw_write(codec->control_data, data, 2);
+ }
+ wm8731_dapm_event(codec, SNDRV_CTL_POWER_D3hot);
+ wm8731_dapm_event(codec, codec->suspend_dapm_state);
+ return 0;
+}
+
+/*
+ * initialise the WM8731 driver
+ * register the mixer and dsp interfaces with the kernel
+ */
+static int wm8731_init(struct snd_soc_device *socdev)
+{
+ struct snd_soc_codec *codec = socdev->codec;
+ int reg, ret = 0;
+
+ codec->name = "WM8731";
+ codec->owner = THIS_MODULE;
+ codec->read = wm8731_read_reg_cache;
+ codec->write = wm8731_write;
+ codec->dapm_event = wm8731_dapm_event;
+ codec->dai = &wm8731_dai;
+ codec->num_dai = 1;
+ codec->reg_cache_size = sizeof(wm8731_reg);
+ codec->reg_cache = kmemdup(wm8731_reg, sizeof(wm8731_reg), GFP_KERNEL);
+ if (codec->reg_cache == NULL)
+ return -ENOMEM;
+
+ wm8731_reset(codec);
+
+ /* register pcms */
+ ret = snd_soc_new_pcms(socdev, SNDRV_DEFAULT_IDX1, SNDRV_DEFAULT_STR1);
+ if (ret < 0) {
+ printk(KERN_ERR "wm8731: failed to create pcms\n");
+ goto pcm_err;
+ }
+
+ /* power on device */
+ wm8731_dapm_event(codec, SNDRV_CTL_POWER_D3hot);
+
+ /* set the update bits */
+ reg = wm8731_read_reg_cache(codec, WM8731_LOUT1V);
+ wm8731_write(codec, WM8731_LOUT1V, reg | 0x0100);
+ reg = wm8731_read_reg_cache(codec, WM8731_ROUT1V);
+ wm8731_write(codec, WM8731_ROUT1V, reg | 0x0100);
+ reg = wm8731_read_reg_cache(codec, WM8731_LINVOL);
+ wm8731_write(codec, WM8731_LINVOL, reg | 0x0100);
+ reg = wm8731_read_reg_cache(codec, WM8731_RINVOL);
+ wm8731_write(codec, WM8731_RINVOL, reg | 0x0100);
+
+ wm8731_add_controls(codec);
+ wm8731_add_widgets(codec);
+ ret = snd_soc_register_card(socdev);
+ if (ret < 0) {
+ printk(KERN_ERR "wm8731: failed to register card\n");
+ goto card_err;
+ }
+
+ return ret;
+
+card_err:
+ snd_soc_free_pcms(socdev);
+ snd_soc_dapm_free(socdev);
+pcm_err:
+ kfree(codec->reg_cache);
+ return ret;
+}
+
+static struct snd_soc_device *wm8731_socdev;
+
+#if defined (CONFIG_I2C) || defined (CONFIG_I2C_MODULE)
+
+/*
+ * WM8731 2 wire address is determined by GPIO5
+ * state during powerup.
+ * low = 0x1a
+ * high = 0x1b
+ */
+static unsigned short normal_i2c[] = { 0, I2C_CLIENT_END };
+
+/* Magic definition of all other variables and things */
+I2C_CLIENT_INSMOD;
+
+static struct i2c_driver wm8731_i2c_driver;
+static struct i2c_client client_template;
+
+/* If the i2c layer weren't so broken, we could pass this kind of data
+ around */
+
+static int wm8731_codec_probe(struct i2c_adapter *adap, int addr, int kind)
+{
+ struct snd_soc_device *socdev = wm8731_socdev;
+ struct wm8731_setup_data *setup = socdev->codec_data;
+ struct snd_soc_codec *codec = socdev->codec;
+ struct i2c_client *i2c;
+ int ret;
+
+ if (addr != setup->i2c_address)
+ return -ENODEV;
+
+ client_template.adapter = adap;
+ client_template.addr = addr;
+
+ i2c = kmemdup(&client_template, sizeof(client_template), GFP_KERNEL);
+ if (i2c == NULL) {
+ kfree(codec);
+ return -ENOMEM;
+ }
+ i2c_set_clientdata(i2c, codec);
+ codec->control_data = i2c;
+
+ ret = i2c_attach_client(i2c);
+ if (ret < 0) {
+ err("failed to attach codec at addr %x\n", addr);
+ goto err;
+ }
+
+ ret = wm8731_init(socdev);
+ if (ret < 0) {
+ err("failed to initialise WM8731\n");
+ goto err;
+ }
+ return ret;
+
+err:
+ kfree(codec);
+ kfree(i2c);
+ return ret;
+}
+
+static int wm8731_i2c_detach(struct i2c_client *client)
+{
+ struct snd_soc_codec* codec = i2c_get_clientdata(client);
+ i2c_detach_client(client);
+ kfree(codec->reg_cache);
+ kfree(client);
+ return 0;
+}
+
+static int wm8731_i2c_attach(struct i2c_adapter *adap)
+{
+ return i2c_probe(adap, &addr_data, wm8731_codec_probe);
+}
+
+/* corgi i2c codec control layer */
+static struct i2c_driver wm8731_i2c_driver = {
+ .driver = {
+ .name = "WM8731 I2C Codec",
+ .owner = THIS_MODULE,
+ },
+ .id = I2C_DRIVERID_WM8731,
+ .attach_adapter = wm8731_i2c_attach,
+ .detach_client = wm8731_i2c_detach,
+ .command = NULL,
+};
+
+static struct i2c_client client_template = {
+ .name = "WM8731",
+ .driver = &wm8731_i2c_driver,
+};
+#endif
+
+static int wm8731_probe(struct platform_device *pdev)
+{
+ struct snd_soc_device *socdev = platform_get_drvdata(pdev);
+ struct wm8731_setup_data *setup;
+ struct snd_soc_codec *codec;
+ struct wm8731_priv *wm8731;
+ int ret = 0;
+
+ info("WM8731 Audio Codec %s", WM8731_VERSION);
+
+ setup = socdev->codec_data;
+ codec = kzalloc(sizeof(struct snd_soc_codec), GFP_KERNEL);
+ if (codec == NULL)
+ return -ENOMEM;
+
+ wm8731 = kzalloc(sizeof(struct wm8731_priv), GFP_KERNEL);
+ if (wm8731 == NULL) {
+ kfree(codec);
+ return -ENOMEM;
+ }
+
+ codec->private_data = wm8731;
+ socdev->codec = codec;
+ mutex_init(&codec->mutex);
+ INIT_LIST_HEAD(&codec->dapm_widgets);
+ INIT_LIST_HEAD(&codec->dapm_paths);
+
+ wm8731_socdev = socdev;
+#if defined (CONFIG_I2C) || defined (CONFIG_I2C_MODULE)
+ if (setup->i2c_address) {
+ normal_i2c[0] = setup->i2c_address;
+ codec->hw_write = (hw_write_t)i2c_master_send;
+ ret = i2c_add_driver(&wm8731_i2c_driver);
+ if (ret != 0)
+ printk(KERN_ERR "can't add i2c driver");
+ }
+#else
+ /* Add other interfaces here */
+#endif
+ return ret;
+}
+
+/* power down chip */
+static int wm8731_remove(struct platform_device *pdev)
+{
+ struct snd_soc_device *socdev = platform_get_drvdata(pdev);
+ struct snd_soc_codec *codec = socdev->codec;
+
+ if (codec->control_data)
+ wm8731_dapm_event(codec, SNDRV_CTL_POWER_D3cold);
+
+ snd_soc_free_pcms(socdev);
+ snd_soc_dapm_free(socdev);
+#if defined (CONFIG_I2C) || defined (CONFIG_I2C_MODULE)
+ i2c_del_driver(&wm8731_i2c_driver);
+#endif
+ kfree(codec->private_data);
+ kfree(codec);
+
+ return 0;
+}
+
+struct snd_soc_codec_device soc_codec_dev_wm8731 = {
+ .probe = wm8731_probe,
+ .remove = wm8731_remove,
+ .suspend = wm8731_suspend,
+ .resume = wm8731_resume,
+};
+
+EXPORT_SYMBOL_GPL(soc_codec_dev_wm8731);
+
+MODULE_DESCRIPTION("ASoC WM8731 driver");
+MODULE_AUTHOR("Richard Purdie");
+MODULE_LICENSE("GPL");
diff --git a/sound/soc/codecs/wm8731.h b/sound/soc/codecs/wm8731.h
new file mode 100644
index 000000000000..5bcab6a7afb4
--- /dev/null
+++ b/sound/soc/codecs/wm8731.h
@@ -0,0 +1,44 @@
+/*
+ * wm8731.h -- WM8731 Soc Audio driver
+ *
+ * Copyright 2005 Openedhand Ltd.
+ *
+ * Author: Richard Purdie <richard@openedhand.com>
+ *
+ * Based on wm8753.h
+ *
+ * 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 _WM8731_H
+#define _WM8731_H
+
+/* WM8731 register space */
+
+#define WM8731_LINVOL 0x00
+#define WM8731_RINVOL 0x01
+#define WM8731_LOUT1V 0x02
+#define WM8731_ROUT1V 0x03
+#define WM8731_APANA 0x04
+#define WM8731_APDIGI 0x05
+#define WM8731_PWR 0x06
+#define WM8731_IFACE 0x07
+#define WM8731_SRATE 0x08
+#define WM8731_ACTIVE 0x09
+#define WM8731_RESET 0x0f
+
+#define WM8731_CACHEREGNUM 10
+
+#define WM8731_SYSCLK 0
+#define WM8731_DAI 0
+
+struct wm8731_setup_data {
+ unsigned short i2c_address;
+};
+
+extern struct snd_soc_codec_dai wm8731_dai;
+extern struct snd_soc_codec_device soc_codec_dev_wm8731;
+
+#endif
diff --git a/sound/soc/codecs/wm8750.c b/sound/soc/codecs/wm8750.c
new file mode 100644
index 000000000000..7073e8e294fc
--- /dev/null
+++ b/sound/soc/codecs/wm8750.c
@@ -0,0 +1,1049 @@
+/*
+ * wm8750.c -- WM8750 ALSA SoC audio driver
+ *
+ * Copyright 2005 Openedhand Ltd.
+ *
+ * Author: Richard Purdie <richard@openedhand.com>
+ *
+ * Based on WM8753.c
+ *
+ * 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.
+ */
+
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/init.h>
+#include <linux/delay.h>
+#include <linux/pm.h>
+#include <linux/i2c.h>
+#include <linux/platform_device.h>
+#include <sound/driver.h>
+#include <sound/core.h>
+#include <sound/pcm.h>
+#include <sound/pcm_params.h>
+#include <sound/soc.h>
+#include <sound/soc-dapm.h>
+#include <sound/initval.h>
+
+#include "wm8750.h"
+
+#define AUDIO_NAME "WM8750"
+#define WM8750_VERSION "0.12"
+
+/*
+ * Debug
+ */
+
+#define WM8750_DEBUG 0
+
+#ifdef WM8750_DEBUG
+#define dbg(format, arg...) \
+ printk(KERN_DEBUG AUDIO_NAME ": " format "\n" , ## arg)
+#else
+#define dbg(format, arg...) do {} while (0)
+#endif
+#define err(format, arg...) \
+ printk(KERN_ERR AUDIO_NAME ": " format "\n" , ## arg)
+#define info(format, arg...) \
+ printk(KERN_INFO AUDIO_NAME ": " format "\n" , ## arg)
+#define warn(format, arg...) \
+ printk(KERN_WARNING AUDIO_NAME ": " format "\n" , ## arg)
+
+/* codec private data */
+struct wm8750_priv {
+ unsigned int sysclk;
+};
+
+/*
+ * wm8750 register cache
+ * We can't read the WM8750 register space when we
+ * are using 2 wire for device control, so we cache them instead.
+ */
+static const u16 wm8750_reg[] = {
+ 0x0097, 0x0097, 0x0079, 0x0079, /* 0 */
+ 0x0000, 0x0008, 0x0000, 0x000a, /* 4 */
+ 0x0000, 0x0000, 0x00ff, 0x00ff, /* 8 */
+ 0x000f, 0x000f, 0x0000, 0x0000, /* 12 */
+ 0x0000, 0x007b, 0x0000, 0x0032, /* 16 */
+ 0x0000, 0x00c3, 0x00c3, 0x00c0, /* 20 */
+ 0x0000, 0x0000, 0x0000, 0x0000, /* 24 */
+ 0x0000, 0x0000, 0x0000, 0x0000, /* 28 */
+ 0x0000, 0x0000, 0x0050, 0x0050, /* 32 */
+ 0x0050, 0x0050, 0x0050, 0x0050, /* 36 */
+ 0x0079, 0x0079, 0x0079, /* 40 */
+};
+
+/*
+ * read wm8750 register cache
+ */
+static inline unsigned int wm8750_read_reg_cache(struct snd_soc_codec *codec,
+ unsigned int reg)
+{
+ u16 *cache = codec->reg_cache;
+ if (reg > WM8750_CACHE_REGNUM)
+ return -1;
+ return cache[reg];
+}
+
+/*
+ * write wm8750 register cache
+ */
+static inline void wm8750_write_reg_cache(struct snd_soc_codec *codec,
+ unsigned int reg, unsigned int value)
+{
+ u16 *cache = codec->reg_cache;
+ if (reg > WM8750_CACHE_REGNUM)
+ return;
+ cache[reg] = value;
+}
+
+static int wm8750_write(struct snd_soc_codec *codec, unsigned int reg,
+ unsigned int value)
+{
+ u8 data[2];
+
+ /* data is
+ * D15..D9 WM8753 register offset
+ * D8...D0 register data
+ */
+ data[0] = (reg << 1) | ((value >> 8) & 0x0001);
+ data[1] = value & 0x00ff;
+
+ wm8750_write_reg_cache (codec, reg, value);
+ if (codec->hw_write(codec->control_data, data, 2) == 2)
+ return 0;
+ else
+ return -EIO;
+}
+
+#define wm8750_reset(c) wm8750_write(c, WM8750_RESET, 0)
+
+/*
+ * WM8750 Controls
+ */
+static const char *wm8750_bass[] = {"Linear Control", "Adaptive Boost"};
+static const char *wm8750_bass_filter[] = { "130Hz @ 48kHz", "200Hz @ 48kHz" };
+static const char *wm8750_treble[] = {"8kHz", "4kHz"};
+static const char *wm8750_3d_lc[] = {"200Hz", "500Hz"};
+static const char *wm8750_3d_uc[] = {"2.2kHz", "1.5kHz"};
+static const char *wm8750_3d_func[] = {"Capture", "Playback"};
+static const char *wm8750_alc_func[] = {"Off", "Right", "Left", "Stereo"};
+static const char *wm8750_ng_type[] = {"Constant PGA Gain",
+ "Mute ADC Output"};
+static const char *wm8750_line_mux[] = {"Line 1", "Line 2", "Line 3", "PGA",
+ "Differential"};
+static const char *wm8750_pga_sel[] = {"Line 1", "Line 2", "Line 3",
+ "Differential"};
+static const char *wm8750_out3[] = {"VREF", "ROUT1 + Vol", "MonoOut",
+ "ROUT1"};
+static const char *wm8750_diff_sel[] = {"Line 1", "Line 2"};
+static const char *wm8750_adcpol[] = {"Normal", "L Invert", "R Invert",
+ "L + R Invert"};
+static const char *wm8750_deemph[] = {"None", "32Khz", "44.1Khz", "48Khz"};
+static const char *wm8750_mono_mux[] = {"Stereo", "Mono (Left)",
+ "Mono (Right)", "Digital Mono"};
+
+static const struct soc_enum wm8750_enum[] = {
+SOC_ENUM_SINGLE(WM8750_BASS, 7, 2, wm8750_bass),
+SOC_ENUM_SINGLE(WM8750_BASS, 6, 2, wm8750_bass_filter),
+SOC_ENUM_SINGLE(WM8750_TREBLE, 6, 2, wm8750_treble),
+SOC_ENUM_SINGLE(WM8750_3D, 5, 2, wm8750_3d_lc),
+SOC_ENUM_SINGLE(WM8750_3D, 6, 2, wm8750_3d_uc),
+SOC_ENUM_SINGLE(WM8750_3D, 7, 2, wm8750_3d_func),
+SOC_ENUM_SINGLE(WM8750_ALC1, 7, 4, wm8750_alc_func),
+SOC_ENUM_SINGLE(WM8750_NGATE, 1, 2, wm8750_ng_type),
+SOC_ENUM_SINGLE(WM8750_LOUTM1, 0, 5, wm8750_line_mux),
+SOC_ENUM_SINGLE(WM8750_ROUTM1, 0, 5, wm8750_line_mux),
+SOC_ENUM_SINGLE(WM8750_LADCIN, 6, 4, wm8750_pga_sel), /* 10 */
+SOC_ENUM_SINGLE(WM8750_RADCIN, 6, 4, wm8750_pga_sel),
+SOC_ENUM_SINGLE(WM8750_ADCTL2, 7, 4, wm8750_out3),
+SOC_ENUM_SINGLE(WM8750_ADCIN, 8, 2, wm8750_diff_sel),
+SOC_ENUM_SINGLE(WM8750_ADCDAC, 5, 4, wm8750_adcpol),
+SOC_ENUM_SINGLE(WM8750_ADCDAC, 1, 4, wm8750_deemph),
+SOC_ENUM_SINGLE(WM8750_ADCIN, 6, 4, wm8750_mono_mux), /* 16 */
+
+};
+
+static const struct snd_kcontrol_new wm8750_snd_controls[] = {
+
+SOC_DOUBLE_R("Capture Volume", WM8750_LINVOL, WM8750_RINVOL, 0, 63, 0),
+SOC_DOUBLE_R("Capture ZC Switch", WM8750_LINVOL, WM8750_RINVOL, 6, 1, 0),
+SOC_DOUBLE_R("Capture Switch", WM8750_LINVOL, WM8750_RINVOL, 7, 1, 1),
+
+SOC_DOUBLE_R("Headphone Playback ZC Switch", WM8750_LOUT1V,
+ WM8750_ROUT1V, 7, 1, 0),
+SOC_DOUBLE_R("Speaker Playback ZC Switch", WM8750_LOUT2V,
+ WM8750_ROUT2V, 7, 1, 0),
+
+SOC_ENUM("Playback De-emphasis", wm8750_enum[15]),
+
+SOC_ENUM("Capture Polarity", wm8750_enum[14]),
+SOC_SINGLE("Playback 6dB Attenuate", WM8750_ADCDAC, 7, 1, 0),
+SOC_SINGLE("Capture 6dB Attenuate", WM8750_ADCDAC, 8, 1, 0),
+
+SOC_DOUBLE_R("PCM Volume", WM8750_LDAC, WM8750_RDAC, 0, 255, 0),
+
+SOC_ENUM("Bass Boost", wm8750_enum[0]),
+SOC_ENUM("Bass Filter", wm8750_enum[1]),
+SOC_SINGLE("Bass Volume", WM8750_BASS, 0, 15, 1),
+
+SOC_SINGLE("Treble Volume", WM8750_TREBLE, 0, 15, 0),
+SOC_ENUM("Treble Cut-off", wm8750_enum[2]),
+
+SOC_SINGLE("3D Switch", WM8750_3D, 0, 1, 0),
+SOC_SINGLE("3D Volume", WM8750_3D, 1, 15, 0),
+SOC_ENUM("3D Lower Cut-off", wm8750_enum[3]),
+SOC_ENUM("3D Upper Cut-off", wm8750_enum[4]),
+SOC_ENUM("3D Mode", wm8750_enum[5]),
+
+SOC_SINGLE("ALC Capture Target Volume", WM8750_ALC1, 0, 7, 0),
+SOC_SINGLE("ALC Capture Max Volume", WM8750_ALC1, 4, 7, 0),
+SOC_ENUM("ALC Capture Function", wm8750_enum[6]),
+SOC_SINGLE("ALC Capture ZC Switch", WM8750_ALC2, 7, 1, 0),
+SOC_SINGLE("ALC Capture Hold Time", WM8750_ALC2, 0, 15, 0),
+SOC_SINGLE("ALC Capture Decay Time", WM8750_ALC3, 4, 15, 0),
+SOC_SINGLE("ALC Capture Attack Time", WM8750_ALC3, 0, 15, 0),
+SOC_SINGLE("ALC Capture NG Threshold", WM8750_NGATE, 3, 31, 0),
+SOC_ENUM("ALC Capture NG Type", wm8750_enum[4]),
+SOC_SINGLE("ALC Capture NG Switch", WM8750_NGATE, 0, 1, 0),
+
+SOC_SINGLE("Left ADC Capture Volume", WM8750_LADC, 0, 255, 0),
+SOC_SINGLE("Right ADC Capture Volume", WM8750_RADC, 0, 255, 0),
+
+SOC_SINGLE("ZC Timeout Switch", WM8750_ADCTL1, 0, 1, 0),
+SOC_SINGLE("Playback Invert Switch", WM8750_ADCTL1, 1, 1, 0),
+
+SOC_SINGLE("Right Speaker Playback Invert Switch", WM8750_ADCTL2, 4, 1, 0),
+
+/* Unimplemented */
+/* ADCDAC Bit 0 - ADCHPD */
+/* ADCDAC Bit 4 - HPOR */
+/* ADCTL1 Bit 2,3 - DATSEL */
+/* ADCTL1 Bit 4,5 - DMONOMIX */
+/* ADCTL1 Bit 6,7 - VSEL */
+/* ADCTL2 Bit 2 - LRCM */
+/* ADCTL2 Bit 3 - TRI */
+/* ADCTL3 Bit 5 - HPFLREN */
+/* ADCTL3 Bit 6 - VROI */
+/* ADCTL3 Bit 7,8 - ADCLRM */
+/* ADCIN Bit 4 - LDCM */
+/* ADCIN Bit 5 - RDCM */
+
+SOC_DOUBLE_R("Mic Boost", WM8750_LADCIN, WM8750_RADCIN, 4, 3, 0),
+
+SOC_DOUBLE_R("Bypass Left Playback Volume", WM8750_LOUTM1,
+ WM8750_LOUTM2, 4, 7, 1),
+SOC_DOUBLE_R("Bypass Right Playback Volume", WM8750_ROUTM1,
+ WM8750_ROUTM2, 4, 7, 1),
+SOC_DOUBLE_R("Bypass Mono Playback Volume", WM8750_MOUTM1,
+ WM8750_MOUTM2, 4, 7, 1),
+
+SOC_SINGLE("Mono Playback ZC Switch", WM8750_MOUTV, 7, 1, 0),
+
+SOC_DOUBLE_R("Headphone Playback Volume", WM8750_LOUT1V, WM8750_ROUT1V,
+ 0, 127, 0),
+SOC_DOUBLE_R("Speaker Playback Volume", WM8750_LOUT2V, WM8750_ROUT2V,
+ 0, 127, 0),
+
+SOC_SINGLE("Mono Playback Volume", WM8750_MOUTV, 0, 127, 0),
+
+};
+
+/* add non dapm controls */
+static int wm8750_add_controls(struct snd_soc_codec *codec)
+{
+ int err, i;
+
+ for (i = 0; i < ARRAY_SIZE(wm8750_snd_controls); i++) {
+ err = snd_ctl_add(codec->card,
+ snd_soc_cnew(&wm8750_snd_controls[i],codec, NULL));
+ if (err < 0)
+ return err;
+ }
+ return 0;
+}
+
+/*
+ * DAPM Controls
+ */
+
+/* Left Mixer */
+static const struct snd_kcontrol_new wm8750_left_mixer_controls[] = {
+SOC_DAPM_SINGLE("Playback Switch", WM8750_LOUTM1, 8, 1, 0),
+SOC_DAPM_SINGLE("Left Bypass Switch", WM8750_LOUTM1, 7, 1, 0),
+SOC_DAPM_SINGLE("Right Playback Switch", WM8750_LOUTM2, 8, 1, 0),
+SOC_DAPM_SINGLE("Right Bypass Switch", WM8750_LOUTM2, 7, 1, 0),
+};
+
+/* Right Mixer */
+static const struct snd_kcontrol_new wm8750_right_mixer_controls[] = {
+SOC_DAPM_SINGLE("Left Playback Switch", WM8750_ROUTM1, 8, 1, 0),
+SOC_DAPM_SINGLE("Left Bypass Switch", WM8750_ROUTM1, 7, 1, 0),
+SOC_DAPM_SINGLE("Playback Switch", WM8750_ROUTM2, 8, 1, 0),
+SOC_DAPM_SINGLE("Right Bypass Switch", WM8750_ROUTM2, 7, 1, 0),
+};
+
+/* Mono Mixer */
+static const struct snd_kcontrol_new wm8750_mono_mixer_controls[] = {
+SOC_DAPM_SINGLE("Left Playback Switch", WM8750_MOUTM1, 8, 1, 0),
+SOC_DAPM_SINGLE("Left Bypass Switch", WM8750_MOUTM1, 7, 1, 0),
+SOC_DAPM_SINGLE("Right Playback Switch", WM8750_MOUTM2, 8, 1, 0),
+SOC_DAPM_SINGLE("Right Bypass Switch", WM8750_MOUTM2, 7, 1, 0),
+};
+
+/* Left Line Mux */
+static const struct snd_kcontrol_new wm8750_left_line_controls =
+SOC_DAPM_ENUM("Route", wm8750_enum[8]);
+
+/* Right Line Mux */
+static const struct snd_kcontrol_new wm8750_right_line_controls =
+SOC_DAPM_ENUM("Route", wm8750_enum[9]);
+
+/* Left PGA Mux */
+static const struct snd_kcontrol_new wm8750_left_pga_controls =
+SOC_DAPM_ENUM("Route", wm8750_enum[10]);
+
+/* Right PGA Mux */
+static const struct snd_kcontrol_new wm8750_right_pga_controls =
+SOC_DAPM_ENUM("Route", wm8750_enum[11]);
+
+/* Out 3 Mux */
+static const struct snd_kcontrol_new wm8750_out3_controls =
+SOC_DAPM_ENUM("Route", wm8750_enum[12]);
+
+/* Differential Mux */
+static const struct snd_kcontrol_new wm8750_diffmux_controls =
+SOC_DAPM_ENUM("Route", wm8750_enum[13]);
+
+/* Mono ADC Mux */
+static const struct snd_kcontrol_new wm8750_monomux_controls =
+SOC_DAPM_ENUM("Route", wm8750_enum[16]);
+
+static const struct snd_soc_dapm_widget wm8750_dapm_widgets[] = {
+ SND_SOC_DAPM_MIXER("Left Mixer", SND_SOC_NOPM, 0, 0,
+ &wm8750_left_mixer_controls[0],
+ ARRAY_SIZE(wm8750_left_mixer_controls)),
+ SND_SOC_DAPM_MIXER("Right Mixer", SND_SOC_NOPM, 0, 0,
+ &wm8750_right_mixer_controls[0],
+ ARRAY_SIZE(wm8750_right_mixer_controls)),
+ SND_SOC_DAPM_MIXER("Mono Mixer", WM8750_PWR2, 2, 0,
+ &wm8750_mono_mixer_controls[0],
+ ARRAY_SIZE(wm8750_mono_mixer_controls)),
+
+ SND_SOC_DAPM_PGA("Right Out 2", WM8750_PWR2, 3, 0, NULL, 0),
+ SND_SOC_DAPM_PGA("Left Out 2", WM8750_PWR2, 4, 0, NULL, 0),
+ SND_SOC_DAPM_PGA("Right Out 1", WM8750_PWR2, 5, 0, NULL, 0),
+ SND_SOC_DAPM_PGA("Left Out 1", WM8750_PWR2, 6, 0, NULL, 0),
+ SND_SOC_DAPM_DAC("Right DAC", "Right Playback", WM8750_PWR2, 7, 0),
+ SND_SOC_DAPM_DAC("Left DAC", "Left Playback", WM8750_PWR2, 8, 0),
+
+ SND_SOC_DAPM_MICBIAS("Mic Bias", WM8750_PWR1, 1, 0),
+ SND_SOC_DAPM_ADC("Right ADC", "Right Capture", WM8750_PWR1, 2, 0),
+ SND_SOC_DAPM_ADC("Left ADC", "Left Capture", WM8750_PWR1, 3, 0),
+
+ SND_SOC_DAPM_MUX("Left PGA Mux", WM8750_PWR1, 5, 0,
+ &wm8750_left_pga_controls),
+ SND_SOC_DAPM_MUX("Right PGA Mux", WM8750_PWR1, 4, 0,
+ &wm8750_right_pga_controls),
+ SND_SOC_DAPM_MUX("Left Line Mux", SND_SOC_NOPM, 0, 0,
+ &wm8750_left_line_controls),
+ SND_SOC_DAPM_MUX("Right Line Mux", SND_SOC_NOPM, 0, 0,
+ &wm8750_right_line_controls),
+
+ SND_SOC_DAPM_MUX("Out3 Mux", SND_SOC_NOPM, 0, 0, &wm8750_out3_controls),
+ SND_SOC_DAPM_PGA("Out 3", WM8750_PWR2, 1, 0, NULL, 0),
+ SND_SOC_DAPM_PGA("Mono Out 1", WM8750_PWR2, 2, 0, NULL, 0),
+
+ SND_SOC_DAPM_MUX("Differential Mux", SND_SOC_NOPM, 0, 0,
+ &wm8750_diffmux_controls),
+ SND_SOC_DAPM_MUX("Left ADC Mux", SND_SOC_NOPM, 0, 0,
+ &wm8750_monomux_controls),
+ SND_SOC_DAPM_MUX("Right ADC Mux", SND_SOC_NOPM, 0, 0,
+ &wm8750_monomux_controls),
+
+ SND_SOC_DAPM_OUTPUT("LOUT1"),
+ SND_SOC_DAPM_OUTPUT("ROUT1"),
+ SND_SOC_DAPM_OUTPUT("LOUT2"),
+ SND_SOC_DAPM_OUTPUT("ROUT2"),
+ SND_SOC_DAPM_OUTPUT("MONO"),
+ SND_SOC_DAPM_OUTPUT("OUT3"),
+
+ SND_SOC_DAPM_INPUT("LINPUT1"),
+ SND_SOC_DAPM_INPUT("LINPUT2"),
+ SND_SOC_DAPM_INPUT("LINPUT3"),
+ SND_SOC_DAPM_INPUT("RINPUT1"),
+ SND_SOC_DAPM_INPUT("RINPUT2"),
+ SND_SOC_DAPM_INPUT("RINPUT3"),
+};
+
+static const char *audio_map[][3] = {
+ /* left mixer */
+ {"Left Mixer", "Playback Switch", "Left DAC"},
+ {"Left Mixer", "Left Bypass Switch", "Left Line Mux"},
+ {"Left Mixer", "Right Playback Switch", "Right DAC"},
+ {"Left Mixer", "Right Bypass Switch", "Right Line Mux"},
+
+ /* right mixer */
+ {"Right Mixer", "Left Playback Switch", "Left DAC"},
+ {"Right Mixer", "Left Bypass Switch", "Left Line Mux"},
+ {"Right Mixer", "Playback Switch", "Right DAC"},
+ {"Right Mixer", "Right Bypass Switch", "Right Line Mux"},
+
+ /* left out 1 */
+ {"Left Out 1", NULL, "Left Mixer"},
+ {"LOUT1", NULL, "Left Out 1"},
+
+ /* left out 2 */
+ {"Left Out 2", NULL, "Left Mixer"},
+ {"LOUT2", NULL, "Left Out 2"},
+
+ /* right out 1 */
+ {"Right Out 1", NULL, "Right Mixer"},
+ {"ROUT1", NULL, "Right Out 1"},
+
+ /* right out 2 */
+ {"Right Out 2", NULL, "Right Mixer"},
+ {"ROUT2", NULL, "Right Out 2"},
+
+ /* mono mixer */
+ {"Mono Mixer", "Left Playback Switch", "Left DAC"},
+ {"Mono Mixer", "Left Bypass Switch", "Left Line Mux"},
+ {"Mono Mixer", "Right Playback Switch", "Right DAC"},
+ {"Mono Mixer", "Right Bypass Switch", "Right Line Mux"},
+
+ /* mono out */
+ {"Mono Out 1", NULL, "Mono Mixer"},
+ {"MONO1", NULL, "Mono Out 1"},
+
+ /* out 3 */
+ {"Out3 Mux", "VREF", "VREF"},
+ {"Out3 Mux", "ROUT1 + Vol", "ROUT1"},
+ {"Out3 Mux", "ROUT1", "Right Mixer"},
+ {"Out3 Mux", "MonoOut", "MONO1"},
+ {"Out 3", NULL, "Out3 Mux"},
+ {"OUT3", NULL, "Out 3"},
+
+ /* Left Line Mux */
+ {"Left Line Mux", "Line 1", "LINPUT1"},
+ {"Left Line Mux", "Line 2", "LINPUT2"},
+ {"Left Line Mux", "Line 3", "LINPUT3"},
+ {"Left Line Mux", "PGA", "Left PGA Mux"},
+ {"Left Line Mux", "Differential", "Differential Mux"},
+
+ /* Right Line Mux */
+ {"Right Line Mux", "Line 1", "RINPUT1"},
+ {"Right Line Mux", "Line 2", "RINPUT2"},
+ {"Right Line Mux", "Line 3", "RINPUT3"},
+ {"Right Line Mux", "PGA", "Right PGA Mux"},
+ {"Right Line Mux", "Differential", "Differential Mux"},
+
+ /* Left PGA Mux */
+ {"Left PGA Mux", "Line 1", "LINPUT1"},
+ {"Left PGA Mux", "Line 2", "LINPUT2"},
+ {"Left PGA Mux", "Line 3", "LINPUT3"},
+ {"Left PGA Mux", "Differential", "Differential Mux"},
+
+ /* Right PGA Mux */
+ {"Right PGA Mux", "Line 1", "RINPUT1"},
+ {"Right PGA Mux", "Line 2", "RINPUT2"},
+ {"Right PGA Mux", "Line 3", "RINPUT3"},
+ {"Right PGA Mux", "Differential", "Differential Mux"},
+
+ /* Differential Mux */
+ {"Differential Mux", "Line 1", "LINPUT1"},
+ {"Differential Mux", "Line 1", "RINPUT1"},
+ {"Differential Mux", "Line 2", "LINPUT2"},
+ {"Differential Mux", "Line 2", "RINPUT2"},
+
+ /* Left ADC Mux */
+ {"Left ADC Mux", "Stereo", "Left PGA Mux"},
+ {"Left ADC Mux", "Mono (Left)", "Left PGA Mux"},
+ {"Left ADC Mux", "Digital Mono", "Left PGA Mux"},
+
+ /* Right ADC Mux */
+ {"Right ADC Mux", "Stereo", "Right PGA Mux"},
+ {"Right ADC Mux", "Mono (Right)", "Right PGA Mux"},
+ {"Right ADC Mux", "Digital Mono", "Right PGA Mux"},
+
+ /* ADC */
+ {"Left ADC", NULL, "Left ADC Mux"},
+ {"Right ADC", NULL, "Right ADC Mux"},
+
+ /* terminator */
+ {NULL, NULL, NULL},
+};
+
+static int wm8750_add_widgets(struct snd_soc_codec *codec)
+{
+ int i;
+
+ for(i = 0; i < ARRAY_SIZE(wm8750_dapm_widgets); i++) {
+ snd_soc_dapm_new_control(codec, &wm8750_dapm_widgets[i]);
+ }
+
+ /* set up audio path audio_mapnects */
+ for(i = 0; audio_map[i][0] != NULL; i++) {
+ snd_soc_dapm_connect_input(codec, audio_map[i][0],
+ audio_map[i][1], audio_map[i][2]);
+ }
+
+ snd_soc_dapm_new_widgets(codec);
+ return 0;
+}
+
+struct _coeff_div {
+ u32 mclk;
+ u32 rate;
+ u16 fs;
+ u8 sr:5;
+ u8 usb:1;
+};
+
+/* codec hifi mclk clock divider coefficients */
+static const struct _coeff_div coeff_div[] = {
+ /* 8k */
+ {12288000, 8000, 1536, 0x6, 0x0},
+ {11289600, 8000, 1408, 0x16, 0x0},
+ {18432000, 8000, 2304, 0x7, 0x0},
+ {16934400, 8000, 2112, 0x17, 0x0},
+ {12000000, 8000, 1500, 0x6, 0x1},
+
+ /* 11.025k */
+ {11289600, 11025, 1024, 0x18, 0x0},
+ {16934400, 11025, 1536, 0x19, 0x0},
+ {12000000, 11025, 1088, 0x19, 0x1},
+
+ /* 16k */
+ {12288000, 16000, 768, 0xa, 0x0},
+ {18432000, 16000, 1152, 0xb, 0x0},
+ {12000000, 16000, 750, 0xa, 0x1},
+
+ /* 22.05k */
+ {11289600, 22050, 512, 0x1a, 0x0},
+ {16934400, 22050, 768, 0x1b, 0x0},
+ {12000000, 22050, 544, 0x1b, 0x1},
+
+ /* 32k */
+ {12288000, 32000, 384, 0xc, 0x0},
+ {18432000, 32000, 576, 0xd, 0x0},
+ {12000000, 32000, 375, 0xa, 0x1},
+
+ /* 44.1k */
+ {11289600, 44100, 256, 0x10, 0x0},
+ {16934400, 44100, 384, 0x11, 0x0},
+ {12000000, 44100, 272, 0x11, 0x1},
+
+ /* 48k */
+ {12288000, 48000, 256, 0x0, 0x0},
+ {18432000, 48000, 384, 0x1, 0x0},
+ {12000000, 48000, 250, 0x0, 0x1},
+
+ /* 88.2k */
+ {11289600, 88200, 128, 0x1e, 0x0},
+ {16934400, 88200, 192, 0x1f, 0x0},
+ {12000000, 88200, 136, 0x1f, 0x1},
+
+ /* 96k */
+ {12288000, 96000, 128, 0xe, 0x0},
+ {18432000, 96000, 192, 0xf, 0x0},
+ {12000000, 96000, 125, 0xe, 0x1},
+};
+
+static inline int get_coeff(int mclk, int rate)
+{
+ int i;
+
+ for (i = 0; i < ARRAY_SIZE(coeff_div); i++) {
+ if (coeff_div[i].rate == rate && coeff_div[i].mclk == mclk)
+ return i;
+ }
+
+ printk(KERN_ERR "wm8750: could not get coeff for mclk %d @ rate %d\n",
+ mclk, rate);
+ return -EINVAL;
+}
+
+static int wm8750_set_dai_sysclk(struct snd_soc_codec_dai *codec_dai,
+ int clk_id, unsigned int freq, int dir)
+{
+ struct snd_soc_codec *codec = codec_dai->codec;
+ struct wm8750_priv *wm8750 = codec->private_data;
+
+ switch (freq) {
+ case 11289600:
+ case 12000000:
+ case 12288000:
+ case 16934400:
+ case 18432000:
+ wm8750->sysclk = freq;
+ return 0;
+ }
+ return -EINVAL;
+}
+
+static int wm8750_set_dai_fmt(struct snd_soc_codec_dai *codec_dai,
+ unsigned int fmt)
+{
+ struct snd_soc_codec *codec = codec_dai->codec;
+ u16 iface = 0;
+
+ /* set master/slave audio interface */
+ switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) {
+ case SND_SOC_DAIFMT_CBM_CFM:
+ iface = 0x0040;
+ break;
+ case SND_SOC_DAIFMT_CBS_CFS:
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ /* interface format */
+ switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
+ case SND_SOC_DAIFMT_I2S:
+ iface |= 0x0002;
+ break;
+ case SND_SOC_DAIFMT_RIGHT_J:
+ break;
+ case SND_SOC_DAIFMT_LEFT_J:
+ iface |= 0x0001;
+ break;
+ case SND_SOC_DAIFMT_DSP_A:
+ iface |= 0x0003;
+ break;
+ case SND_SOC_DAIFMT_DSP_B:
+ iface |= 0x0013;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ /* clock inversion */
+ switch (fmt & SND_SOC_DAIFMT_INV_MASK) {
+ case SND_SOC_DAIFMT_NB_NF:
+ break;
+ case SND_SOC_DAIFMT_IB_IF:
+ iface |= 0x0090;
+ break;
+ case SND_SOC_DAIFMT_IB_NF:
+ iface |= 0x0080;
+ break;
+ case SND_SOC_DAIFMT_NB_IF:
+ iface |= 0x0010;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ wm8750_write(codec, WM8750_IFACE, iface);
+ return 0;
+}
+
+static int wm8750_pcm_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params)
+{
+ struct snd_soc_pcm_runtime *rtd = substream->private_data;
+ struct snd_soc_device *socdev = rtd->socdev;
+ struct snd_soc_codec *codec = socdev->codec;
+ struct wm8750_priv *wm8750 = codec->private_data;
+ u16 iface = wm8750_read_reg_cache(codec, WM8750_IFACE) & 0x1f3;
+ u16 srate = wm8750_read_reg_cache(codec, WM8750_SRATE) & 0x1c0;
+ int coeff = get_coeff(wm8750->sysclk, params_rate(params));
+
+ /* bit size */
+ switch (params_format(params)) {
+ case SNDRV_PCM_FORMAT_S16_LE:
+ break;
+ case SNDRV_PCM_FORMAT_S20_3LE:
+ iface |= 0x0004;
+ break;
+ case SNDRV_PCM_FORMAT_S24_LE:
+ iface |= 0x0008;
+ break;
+ case SNDRV_PCM_FORMAT_S32_LE:
+ iface |= 0x000c;
+ break;
+ }
+
+ /* set iface & srate */
+ wm8750_write(codec, WM8750_IFACE, iface);
+ if (coeff >= 0)
+ wm8750_write(codec, WM8750_SRATE, srate |
+ (coeff_div[coeff].sr << 1) | coeff_div[coeff].usb);
+
+ return 0;
+}
+
+static int wm8750_mute(struct snd_soc_codec_dai *dai, int mute)
+{
+ struct snd_soc_codec *codec = dai->codec;
+ u16 mute_reg = wm8750_read_reg_cache(codec, WM8750_ADCDAC) & 0xfff7;
+
+ if (mute)
+ wm8750_write(codec, WM8750_ADCDAC, mute_reg | 0x8);
+ else
+ wm8750_write(codec, WM8750_ADCDAC, mute_reg);
+ return 0;
+}
+
+static int wm8750_dapm_event(struct snd_soc_codec *codec, int event)
+{
+ u16 pwr_reg = wm8750_read_reg_cache(codec, WM8750_PWR1) & 0xfe3e;
+
+ switch (event) {
+ case SNDRV_CTL_POWER_D0: /* full On */
+ /* set vmid to 50k and unmute dac */
+ wm8750_write(codec, WM8750_PWR1, pwr_reg | 0x00c0);
+ break;
+ case SNDRV_CTL_POWER_D1: /* partial On */
+ case SNDRV_CTL_POWER_D2: /* partial On */
+ /* set vmid to 5k for quick power up */
+ wm8750_write(codec, WM8750_PWR1, pwr_reg | 0x01c1);
+ break;
+ case SNDRV_CTL_POWER_D3hot: /* Off, with power */
+ /* mute dac and set vmid to 500k, enable VREF */
+ wm8750_write(codec, WM8750_PWR1, pwr_reg | 0x0141);
+ break;
+ case SNDRV_CTL_POWER_D3cold: /* Off, without power */
+ wm8750_write(codec, WM8750_PWR1, 0x0001);
+ break;
+ }
+ codec->dapm_state = event;
+ return 0;
+}
+
+#define WM8750_RATES (SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_11025 |\
+ SNDRV_PCM_RATE_16000 | SNDRV_PCM_RATE_22050 | SNDRV_PCM_RATE_44100 | \
+ SNDRV_PCM_RATE_48000 | SNDRV_PCM_RATE_88200 | SNDRV_PCM_RATE_96000)
+
+#define WM8750_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S20_3LE |\
+ SNDRV_PCM_FMTBIT_S24_LE)
+
+struct snd_soc_codec_dai wm8750_dai = {
+ .name = "WM8750",
+ .playback = {
+ .stream_name = "Playback",
+ .channels_min = 1,
+ .channels_max = 2,
+ .rates = WM8750_RATES,
+ .formats = WM8750_FORMATS,},
+ .capture = {
+ .stream_name = "Capture",
+ .channels_min = 1,
+ .channels_max = 2,
+ .rates = WM8750_RATES,
+ .formats = WM8750_FORMATS,},
+ .ops = {
+ .hw_params = wm8750_pcm_hw_params,
+ },
+ .dai_ops = {
+ .digital_mute = wm8750_mute,
+ .set_fmt = wm8750_set_dai_fmt,
+ .set_sysclk = wm8750_set_dai_sysclk,
+ },
+};
+EXPORT_SYMBOL_GPL(wm8750_dai);
+
+static void wm8750_work(struct work_struct *work)
+{
+ struct snd_soc_codec *codec =
+ container_of(work, struct snd_soc_codec, delayed_work.work);
+ wm8750_dapm_event(codec, codec->dapm_state);
+}
+
+static int wm8750_suspend(struct platform_device *pdev, pm_message_t state)
+{
+ struct snd_soc_device *socdev = platform_get_drvdata(pdev);
+ struct snd_soc_codec *codec = socdev->codec;
+
+ wm8750_dapm_event(codec, SNDRV_CTL_POWER_D3cold);
+ return 0;
+}
+
+static int wm8750_resume(struct platform_device *pdev)
+{
+ struct snd_soc_device *socdev = platform_get_drvdata(pdev);
+ struct snd_soc_codec *codec = socdev->codec;
+ int i;
+ u8 data[2];
+ u16 *cache = codec->reg_cache;
+
+ /* Sync reg_cache with the hardware */
+ for (i = 0; i < ARRAY_SIZE(wm8750_reg); i++) {
+ if (i == WM8750_RESET)
+ continue;
+ data[0] = (i << 1) | ((cache[i] >> 8) & 0x0001);
+ data[1] = cache[i] & 0x00ff;
+ codec->hw_write(codec->control_data, data, 2);
+ }
+
+ wm8750_dapm_event(codec, SNDRV_CTL_POWER_D3hot);
+
+ /* charge wm8750 caps */
+ if (codec->suspend_dapm_state == SNDRV_CTL_POWER_D0) {
+ wm8750_dapm_event(codec, SNDRV_CTL_POWER_D2);
+ codec->dapm_state = SNDRV_CTL_POWER_D0;
+ schedule_delayed_work(&codec->delayed_work, msecs_to_jiffies(1000));
+ }
+
+ return 0;
+}
+
+/*
+ * initialise the WM8750 driver
+ * register the mixer and dsp interfaces with the kernel
+ */
+static int wm8750_init(struct snd_soc_device *socdev)
+{
+ struct snd_soc_codec *codec = socdev->codec;
+ int reg, ret = 0;
+
+ codec->name = "WM8750";
+ codec->owner = THIS_MODULE;
+ codec->read = wm8750_read_reg_cache;
+ codec->write = wm8750_write;
+ codec->dapm_event = wm8750_dapm_event;
+ codec->dai = &wm8750_dai;
+ codec->num_dai = 1;
+ codec->reg_cache_size = sizeof(wm8750_reg);
+ codec->reg_cache = kmemdup(wm8750_reg, sizeof(wm8750_reg), GFP_KRENEL);
+ if (codec->reg_cache == NULL)
+ return -ENOMEM;
+
+ wm8750_reset(codec);
+
+ /* register pcms */
+ ret = snd_soc_new_pcms(socdev, SNDRV_DEFAULT_IDX1, SNDRV_DEFAULT_STR1);
+ if (ret < 0) {
+ printk(KERN_ERR "wm8750: failed to create pcms\n");
+ goto pcm_err;
+ }
+
+ /* charge output caps */
+ wm8750_dapm_event(codec, SNDRV_CTL_POWER_D2);
+ codec->dapm_state = SNDRV_CTL_POWER_D3hot;
+ schedule_delayed_work(&codec->delayed_work, msecs_to_jiffies(1000));
+
+ /* set the update bits */
+ reg = wm8750_read_reg_cache(codec, WM8750_LDAC);
+ wm8750_write(codec, WM8750_LDAC, reg | 0x0100);
+ reg = wm8750_read_reg_cache(codec, WM8750_RDAC);
+ wm8750_write(codec, WM8750_RDAC, reg | 0x0100);
+ reg = wm8750_read_reg_cache(codec, WM8750_LOUT1V);
+ wm8750_write(codec, WM8750_LOUT1V, reg | 0x0100);
+ reg = wm8750_read_reg_cache(codec, WM8750_ROUT1V);
+ wm8750_write(codec, WM8750_ROUT1V, reg | 0x0100);
+ reg = wm8750_read_reg_cache(codec, WM8750_LOUT2V);
+ wm8750_write(codec, WM8750_LOUT2V, reg | 0x0100);
+ reg = wm8750_read_reg_cache(codec, WM8750_ROUT2V);
+ wm8750_write(codec, WM8750_ROUT2V, reg | 0x0100);
+ reg = wm8750_read_reg_cache(codec, WM8750_LINVOL);
+ wm8750_write(codec, WM8750_LINVOL, reg | 0x0100);
+ reg = wm8750_read_reg_cache(codec, WM8750_RINVOL);
+ wm8750_write(codec, WM8750_RINVOL, reg | 0x0100);
+
+ wm8750_add_controls(codec);
+ wm8750_add_widgets(codec);
+ ret = snd_soc_register_card(socdev);
+ if (ret < 0) {
+ printk(KERN_ERR "wm8750: failed to register card\n");
+ goto card_err;
+ }
+ return ret;
+
+card_err:
+ snd_soc_free_pcms(socdev);
+ snd_soc_dapm_free(socdev);
+pcm_err:
+ kfree(codec->reg_cache);
+ return ret;
+}
+
+/* If the i2c layer weren't so broken, we could pass this kind of data
+ around */
+static struct snd_soc_device *wm8750_socdev;
+
+#if defined (CONFIG_I2C) || defined (CONFIG_I2C_MODULE)
+
+/*
+ * WM8731 2 wire address is determined by GPIO5
+ * state during powerup.
+ * low = 0x1a
+ * high = 0x1b
+ */
+static unsigned short normal_i2c[] = { 0, I2C_CLIENT_END };
+
+/* Magic definition of all other variables and things */
+I2C_CLIENT_INSMOD;
+
+static struct i2c_driver wm8750_i2c_driver;
+static struct i2c_client client_template;
+
+static int wm8750_codec_probe(struct i2c_adapter *adap, int addr, int kind)
+{
+ struct snd_soc_device *socdev = wm8750_socdev;
+ struct wm8750_setup_data *setup = socdev->codec_data;
+ struct snd_soc_codec *codec = socdev->codec;
+ struct i2c_client *i2c;
+ int ret;
+
+ if (addr != setup->i2c_address)
+ return -ENODEV;
+
+ client_template.adapter = adap;
+ client_template.addr = addr;
+
+ i2c = kmemdup(&client_template, sizeof(client_template), GFP_KERNEL);
+ if (i2c == NULL) {
+ kfree(codec);
+ return -ENOMEM;
+ }
+ i2c_set_clientdata(i2c, codec);
+ codec->control_data = i2c;
+
+ ret = i2c_attach_client(i2c);
+ if (ret < 0) {
+ err("failed to attach codec at addr %x\n", addr);
+ goto err;
+ }
+
+ ret = wm8750_init(socdev);
+ if (ret < 0) {
+ err("failed to initialise WM8750\n");
+ goto err;
+ }
+ return ret;
+
+err:
+ kfree(codec);
+ kfree(i2c);
+ return ret;
+}
+
+static int wm8750_i2c_detach(struct i2c_client *client)
+{
+ struct snd_soc_codec *codec = i2c_get_clientdata(client);
+ i2c_detach_client(client);
+ kfree(codec->reg_cache);
+ kfree(client);
+ return 0;
+}
+
+static int wm8750_i2c_attach(struct i2c_adapter *adap)
+{
+ return i2c_probe(adap, &addr_data, wm8750_codec_probe);
+}
+
+/* corgi i2c codec control layer */
+static struct i2c_driver wm8750_i2c_driver = {
+ .driver = {
+ .name = "WM8750 I2C Codec",
+ .owner = THIS_MODULE,
+ },
+ .id = I2C_DRIVERID_WM8750,
+ .attach_adapter = wm8750_i2c_attach,
+ .detach_client = wm8750_i2c_detach,
+ .command = NULL,
+};
+
+static struct i2c_client client_template = {
+ .name = "WM8750",
+ .driver = &wm8750_i2c_driver,
+};
+#endif
+
+static int wm8750_probe(struct platform_device *pdev)
+{
+ struct snd_soc_device *socdev = platform_get_drvdata(pdev);
+ struct wm8750_setup_data *setup = socdev->codec_data;
+ struct snd_soc_codec *codec;
+ struct wm8750_priv *wm8750;
+ int ret = 0;
+
+ info("WM8750 Audio Codec %s", WM8750_VERSION);
+ codec = kzalloc(sizeof(struct snd_soc_codec), GFP_KERNEL);
+ if (codec == NULL)
+ return -ENOMEM;
+
+ wm8750 = kzalloc(sizeof(struct wm8750_priv), GFP_KERNEL);
+ if (wm8750 == NULL) {
+ kfree(codec);
+ return -ENOMEM;
+ }
+
+ codec->private_data = wm8750;
+ socdev->codec = codec;
+ mutex_init(&codec->mutex);
+ INIT_LIST_HEAD(&codec->dapm_widgets);
+ INIT_LIST_HEAD(&codec->dapm_paths);
+ wm8750_socdev = socdev;
+ INIT_DELAYED_WORK(&codec->delayed_work, wm8750_work);
+
+#if defined (CONFIG_I2C) || defined (CONFIG_I2C_MODULE)
+ if (setup->i2c_address) {
+ normal_i2c[0] = setup->i2c_address;
+ codec->hw_write = (hw_write_t)i2c_master_send;
+ ret = i2c_add_driver(&wm8750_i2c_driver);
+ if (ret != 0)
+ printk(KERN_ERR "can't add i2c driver");
+ }
+#else
+ /* Add other interfaces here */
+#endif
+
+ return ret;
+}
+
+/*
+ * This function forces any delayed work to be queued and run.
+ */
+static int run_delayed_work(struct delayed_work *dwork)
+{
+ int ret;
+
+ /* cancel any work waiting to be queued. */
+ ret = cancel_delayed_work(dwork);
+
+ /* if there was any work waiting then we run it now and
+ * wait for it's completion */
+ if (ret) {
+ schedule_delayed_work(dwork, 0);
+ flush_scheduled_work();
+ }
+ return ret;
+}
+
+/* power down chip */
+static int wm8750_remove(struct platform_device *pdev)
+{
+ struct snd_soc_device *socdev = platform_get_drvdata(pdev);
+ struct snd_soc_codec *codec = socdev->codec;
+
+ if (codec->control_data)
+ wm8750_dapm_event(codec, SNDRV_CTL_POWER_D3cold);
+ run_delayed_work(&codec->delayed_work);
+ snd_soc_free_pcms(socdev);
+ snd_soc_dapm_free(socdev);
+#if defined (CONFIG_I2C) || defined (CONFIG_I2C_MODULE)
+ i2c_del_driver(&wm8750_i2c_driver);
+#endif
+ kfree(codec->private_data);
+ kfree(codec);
+
+ return 0;
+}
+
+struct snd_soc_codec_device soc_codec_dev_wm8750 = {
+ .probe = wm8750_probe,
+ .remove = wm8750_remove,
+ .suspend = wm8750_suspend,
+ .resume = wm8750_resume,
+};
+
+EXPORT_SYMBOL_GPL(soc_codec_dev_wm8750);
+
+MODULE_DESCRIPTION("ASoC WM8750 driver");
+MODULE_AUTHOR("Liam Girdwood");
+MODULE_LICENSE("GPL");
diff --git a/sound/soc/codecs/wm8750.h b/sound/soc/codecs/wm8750.h
new file mode 100644
index 000000000000..a97a54a6348e
--- /dev/null
+++ b/sound/soc/codecs/wm8750.h
@@ -0,0 +1,67 @@
+/*
+ * Copyright 2005 Openedhand Ltd.
+ *
+ * Author: Richard Purdie <richard@openedhand.com>
+ *
+ * Based on WM8753.h
+ *
+ * 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 _WM8750_H
+#define _WM8750_H
+
+/* WM8750 register space */
+
+#define WM8750_LINVOL 0x00
+#define WM8750_RINVOL 0x01
+#define WM8750_LOUT1V 0x02
+#define WM8750_ROUT1V 0x03
+#define WM8750_ADCDAC 0x05
+#define WM8750_IFACE 0x07
+#define WM8750_SRATE 0x08
+#define WM8750_LDAC 0x0a
+#define WM8750_RDAC 0x0b
+#define WM8750_BASS 0x0c
+#define WM8750_TREBLE 0x0d
+#define WM8750_RESET 0x0f
+#define WM8750_3D 0x10
+#define WM8750_ALC1 0x11
+#define WM8750_ALC2 0x12
+#define WM8750_ALC3 0x13
+#define WM8750_NGATE 0x14
+#define WM8750_LADC 0x15
+#define WM8750_RADC 0x16
+#define WM8750_ADCTL1 0x17
+#define WM8750_ADCTL2 0x18
+#define WM8750_PWR1 0x19
+#define WM8750_PWR2 0x1a
+#define WM8750_ADCTL3 0x1b
+#define WM8750_ADCIN 0x1f
+#define WM8750_LADCIN 0x20
+#define WM8750_RADCIN 0x21
+#define WM8750_LOUTM1 0x22
+#define WM8750_LOUTM2 0x23
+#define WM8750_ROUTM1 0x24
+#define WM8750_ROUTM2 0x25
+#define WM8750_MOUTM1 0x26
+#define WM8750_MOUTM2 0x27
+#define WM8750_LOUT2V 0x28
+#define WM8750_ROUT2V 0x29
+#define WM8750_MOUTV 0x2a
+
+#define WM8750_CACHE_REGNUM 0x2a
+
+#define WM8750_SYSCLK 0
+
+struct wm8750_setup_data {
+ unsigned short i2c_address;
+};
+
+extern struct snd_soc_codec_dai wm8750_dai;
+extern struct snd_soc_codec_device soc_codec_dev_wm8750;
+
+#endif
diff --git a/sound/soc/codecs/wm9712.c b/sound/soc/codecs/wm9712.c
new file mode 100644
index 000000000000..92a64871bcd0
--- /dev/null
+++ b/sound/soc/codecs/wm9712.c
@@ -0,0 +1,771 @@
+/*
+ * wm9712.c -- ALSA Soc WM9712 codec support
+ *
+ * Copyright 2006 Wolfson Microelectronics PLC.
+ * Author: Liam Girdwood
+ * liam.girdwood@wolfsonmicro.com or linux@wolfsonmicro.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.
+ *
+ * Revision history
+ * 4th Feb 2006 Initial version.
+ */
+
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/version.h>
+#include <linux/kernel.h>
+#include <linux/device.h>
+#include <sound/driver.h>
+#include <sound/core.h>
+#include <sound/pcm.h>
+#include <sound/ac97_codec.h>
+#include <sound/initval.h>
+#include <sound/soc.h>
+#include <sound/soc-dapm.h>
+
+#define WM9712_VERSION "0.4"
+
+static unsigned int ac97_read(struct snd_soc_codec *codec,
+ unsigned int reg);
+static int ac97_write(struct snd_soc_codec *codec,
+ unsigned int reg, unsigned int val);
+
+/*
+ * WM9712 register cache
+ */
+static const u16 wm9712_reg[] = {
+ 0x6174, 0x8000, 0x8000, 0x8000, // 6
+ 0xf0f0, 0xaaa0, 0xc008, 0x6808, // e
+ 0xe808, 0xaaa0, 0xad00, 0x8000, // 16
+ 0xe808, 0x3000, 0x8000, 0x0000, // 1e
+ 0x0000, 0x0000, 0x0000, 0x000f, // 26
+ 0x0405, 0x0410, 0xbb80, 0xbb80, // 2e
+ 0x0000, 0xbb80, 0x0000, 0x0000, // 36
+ 0x0000, 0x2000, 0x0000, 0x0000, // 3e
+ 0x0000, 0x0000, 0x0000, 0x0000, // 46
+ 0x0000, 0x0000, 0xf83e, 0xffff, // 4e
+ 0x0000, 0x0000, 0x0000, 0xf83e, // 56
+ 0x0008, 0x0000, 0x0000, 0x0000, // 5e
+ 0xb032, 0x3e00, 0x0000, 0x0000, // 66
+ 0x0000, 0x0000, 0x0000, 0x0000, // 6e
+ 0x0000, 0x0000, 0x0000, 0x0006, // 76
+ 0x0001, 0x0000, 0x574d, 0x4c12, // 7e
+ 0x0000, 0x0000 // virtual hp mixers
+};
+
+/* virtual HP mixers regs */
+#define HPL_MIXER 0x80
+#define HPR_MIXER 0x82
+
+static const char *wm9712_alc_select[] = {"None", "Left", "Right", "Stereo"};
+static const char *wm9712_alc_mux[] = {"Stereo", "Left", "Right", "None"};
+static const char *wm9712_out3_src[] = {"Left", "VREF", "Left + Right",
+ "Mono"};
+static const char *wm9712_spk_src[] = {"Speaker Mix", "Headphone Mix"};
+static const char *wm9712_rec_adc[] = {"Stereo", "Left", "Right", "Mute"};
+static const char *wm9712_base[] = {"Linear Control", "Adaptive Boost"};
+static const char *wm9712_rec_gain[] = {"+1.5dB Steps", "+0.75dB Steps"};
+static const char *wm9712_mic[] = {"Mic 1", "Differential", "Mic 2",
+ "Stereo"};
+static const char *wm9712_rec_sel[] = {"Mic", "NC", "NC", "Speaker Mixer",
+ "Line", "Headphone Mixer", "Phone Mixer", "Phone"};
+static const char *wm9712_ng_type[] = {"Constant Gain", "Mute"};
+static const char *wm9712_diff_sel[] = {"Mic", "Line"};
+
+static const struct soc_enum wm9712_enum[] = {
+SOC_ENUM_SINGLE(AC97_PCI_SVID, 14, 4, wm9712_alc_select),
+SOC_ENUM_SINGLE(AC97_VIDEO, 12, 4, wm9712_alc_mux),
+SOC_ENUM_SINGLE(AC97_AUX, 9, 4, wm9712_out3_src),
+SOC_ENUM_SINGLE(AC97_AUX, 8, 2, wm9712_spk_src),
+SOC_ENUM_SINGLE(AC97_REC_SEL, 12, 4, wm9712_rec_adc),
+SOC_ENUM_SINGLE(AC97_MASTER_TONE, 15, 2, wm9712_base),
+SOC_ENUM_DOUBLE(AC97_REC_GAIN, 14, 6, 2, wm9712_rec_gain),
+SOC_ENUM_SINGLE(AC97_MIC, 5, 4, wm9712_mic),
+SOC_ENUM_SINGLE(AC97_REC_SEL, 8, 8, wm9712_rec_sel),
+SOC_ENUM_SINGLE(AC97_REC_SEL, 0, 8, wm9712_rec_sel),
+SOC_ENUM_SINGLE(AC97_PCI_SVID, 5, 2, wm9712_ng_type),
+SOC_ENUM_SINGLE(0x5c, 8, 2, wm9712_diff_sel),
+};
+
+static const struct snd_kcontrol_new wm9712_snd_ac97_controls[] = {
+SOC_DOUBLE("Speaker Playback Volume", AC97_MASTER, 8, 0, 31, 1),
+SOC_SINGLE("Speaker Playback Switch", AC97_MASTER, 15, 1, 1),
+SOC_DOUBLE("Headphone Playback Volume", AC97_HEADPHONE, 8, 0, 31, 1),
+SOC_SINGLE("Headphone Playback Switch", AC97_HEADPHONE,15, 1, 1),
+
+SOC_SINGLE("Speaker Playback ZC Switch", AC97_MASTER, 7, 1, 0),
+SOC_SINGLE("Speaker Playback Invert Switch", AC97_MASTER, 6, 1, 0),
+SOC_SINGLE("Headphone Playback ZC Switch", AC97_HEADPHONE, 7, 1, 0),
+SOC_SINGLE("Mono Playback ZC Switch", AC97_MASTER_MONO, 7, 1, 0),
+SOC_SINGLE("Mono Playback Volume", AC97_MASTER_MONO, 0, 31, 0),
+
+SOC_SINGLE("ALC Target Volume", AC97_CODEC_CLASS_REV, 12, 15, 0),
+SOC_SINGLE("ALC Hold Time", AC97_CODEC_CLASS_REV, 8, 15, 0),
+SOC_SINGLE("ALC Decay Time", AC97_CODEC_CLASS_REV, 4, 15, 0),
+SOC_SINGLE("ALC Attack Time", AC97_CODEC_CLASS_REV, 0, 15, 0),
+SOC_ENUM("ALC Function", wm9712_enum[0]),
+SOC_SINGLE("ALC Max Volume", AC97_PCI_SVID, 11, 7, 0),
+SOC_SINGLE("ALC ZC Timeout", AC97_PCI_SVID, 9, 3, 1),
+SOC_SINGLE("ALC ZC Switch", AC97_PCI_SVID, 8, 1, 0),
+SOC_SINGLE("ALC NG Switch", AC97_PCI_SVID, 7, 1, 0),
+SOC_ENUM("ALC NG Type", wm9712_enum[10]),
+SOC_SINGLE("ALC NG Threshold", AC97_PCI_SVID, 0, 31, 1),
+
+SOC_SINGLE("Mic Headphone Volume", AC97_VIDEO, 12, 7, 1),
+SOC_SINGLE("ALC Headphone Volume", AC97_VIDEO, 7, 7, 1),
+
+SOC_SINGLE("Out3 Switch", AC97_AUX, 15, 1, 1),
+SOC_SINGLE("Out3 ZC Switch", AC97_AUX, 7, 1, 1),
+SOC_SINGLE("Out3 Volume", AC97_AUX, 0, 31, 1),
+
+SOC_SINGLE("PCBeep Bypass Headphone Volume", AC97_PC_BEEP, 12, 7, 1),
+SOC_SINGLE("PCBeep Bypass Speaker Volume", AC97_PC_BEEP, 8, 7, 1),
+SOC_SINGLE("PCBeep Bypass Phone Volume", AC97_PC_BEEP, 4, 7, 1),
+
+SOC_SINGLE("Aux Playback Headphone Volume", AC97_CD, 12, 7, 1),
+SOC_SINGLE("Aux Playback Speaker Volume", AC97_CD, 8, 7, 1),
+SOC_SINGLE("Aux Playback Phone Volume", AC97_CD, 4, 7, 1),
+
+SOC_SINGLE("Phone Volume", AC97_PHONE, 0, 15, 0),
+SOC_DOUBLE("Line Capture Volume", AC97_LINE, 8, 0, 31, 1),
+
+SOC_SINGLE("Capture 20dB Boost Switch", AC97_REC_SEL, 14, 1, 0),
+SOC_SINGLE("Capture to Phone 20dB Boost Switch", AC97_REC_SEL, 11, 1, 1),
+
+SOC_SINGLE("3D Upper Cut-off Switch", AC97_3D_CONTROL, 5, 1, 1),
+SOC_SINGLE("3D Lower Cut-off Switch", AC97_3D_CONTROL, 4, 1, 1),
+SOC_SINGLE("3D Playback Volume", AC97_3D_CONTROL, 0, 15, 0),
+
+SOC_ENUM("Bass Control", wm9712_enum[5]),
+SOC_SINGLE("Bass Cut-off Switch", AC97_MASTER_TONE, 12, 1, 1),
+SOC_SINGLE("Tone Cut-off Switch", AC97_MASTER_TONE, 4, 1, 1),
+SOC_SINGLE("Playback Attenuate (-6dB) Switch", AC97_MASTER_TONE, 6, 1, 0),
+SOC_SINGLE("Bass Volume", AC97_MASTER_TONE, 8, 15, 0),
+SOC_SINGLE("Treble Volume", AC97_MASTER_TONE, 0, 15, 0),
+
+SOC_SINGLE("Capture ADC Switch", AC97_REC_GAIN, 15, 1, 1),
+SOC_ENUM("Capture Volume Steps", wm9712_enum[6]),
+SOC_DOUBLE("Capture Volume", AC97_REC_GAIN, 8, 0, 63, 1),
+SOC_SINGLE("Capture ZC Switch", AC97_REC_GAIN, 7, 1, 0),
+
+SOC_SINGLE("Mic 1 Volume", AC97_MIC, 8, 31, 1),
+SOC_SINGLE("Mic 2 Volume", AC97_MIC, 0, 31, 1),
+SOC_SINGLE("Mic 20dB Boost Switch", AC97_MIC, 7, 1, 0),
+};
+
+/* add non dapm controls */
+static int wm9712_add_controls(struct snd_soc_codec *codec)
+{
+ int err, i;
+
+ for (i = 0; i < ARRAY_SIZE(wm9712_snd_ac97_controls); i++) {
+ err = snd_ctl_add(codec->card,
+ snd_soc_cnew(&wm9712_snd_ac97_controls[i],codec, NULL));
+ if (err < 0)
+ return err;
+ }
+ return 0;
+}
+
+/* We have to create a fake left and right HP mixers because
+ * the codec only has a single control that is shared by both channels.
+ * This makes it impossible to determine the audio path.
+ */
+static int mixer_event (struct snd_soc_dapm_widget *w, int event)
+{
+ u16 l, r, beep, line, phone, mic, pcm, aux;
+
+ l = ac97_read(w->codec, HPL_MIXER);
+ r = ac97_read(w->codec, HPR_MIXER);
+ beep = ac97_read(w->codec, AC97_PC_BEEP);
+ mic = ac97_read(w->codec, AC97_VIDEO);
+ phone = ac97_read(w->codec, AC97_PHONE);
+ line = ac97_read(w->codec, AC97_LINE);
+ pcm = ac97_read(w->codec, AC97_PCM);
+ aux = ac97_read(w->codec, AC97_CD);
+
+ if (l & 0x1 || r & 0x1)
+ ac97_write(w->codec, AC97_VIDEO, mic & 0x7fff);
+ else
+ ac97_write(w->codec, AC97_VIDEO, mic | 0x8000);
+
+ if (l & 0x2 || r & 0x2)
+ ac97_write(w->codec, AC97_PCM, pcm & 0x7fff);
+ else
+ ac97_write(w->codec, AC97_PCM, pcm | 0x8000);
+
+ if (l & 0x4 || r & 0x4)
+ ac97_write(w->codec, AC97_LINE, line & 0x7fff);
+ else
+ ac97_write(w->codec, AC97_LINE, line | 0x8000);
+
+ if (l & 0x8 || r & 0x8)
+ ac97_write(w->codec, AC97_PHONE, phone & 0x7fff);
+ else
+ ac97_write(w->codec, AC97_PHONE, phone | 0x8000);
+
+ if (l & 0x10 || r & 0x10)
+ ac97_write(w->codec, AC97_CD, aux & 0x7fff);
+ else
+ ac97_write(w->codec, AC97_CD, aux | 0x8000);
+
+ if (l & 0x20 || r & 0x20)
+ ac97_write(w->codec, AC97_PC_BEEP, beep & 0x7fff);
+ else
+ ac97_write(w->codec, AC97_PC_BEEP, beep | 0x8000);
+
+ return 0;
+}
+
+/* Left Headphone Mixers */
+static const struct snd_kcontrol_new wm9712_hpl_mixer_controls[] = {
+ SOC_DAPM_SINGLE("PCBeep Bypass Switch", HPL_MIXER, 5, 1, 0),
+ SOC_DAPM_SINGLE("Aux Playback Switch", HPL_MIXER, 4, 1, 0),
+ SOC_DAPM_SINGLE("Phone Bypass Switch", HPL_MIXER, 3, 1, 0),
+ SOC_DAPM_SINGLE("Line Bypass Switch", HPL_MIXER, 2, 1, 0),
+ SOC_DAPM_SINGLE("PCM Playback Switch", HPL_MIXER, 1, 1, 0),
+ SOC_DAPM_SINGLE("Mic Sidetone Switch", HPL_MIXER, 0, 1, 0),
+};
+
+/* Right Headphone Mixers */
+static const struct snd_kcontrol_new wm9712_hpr_mixer_controls[] = {
+ SOC_DAPM_SINGLE("PCBeep Bypass Switch", HPR_MIXER, 5, 1, 0),
+ SOC_DAPM_SINGLE("Aux Playback Switch", HPR_MIXER, 4, 1, 0),
+ SOC_DAPM_SINGLE("Phone Bypass Switch", HPR_MIXER, 3, 1, 0),
+ SOC_DAPM_SINGLE("Line Bypass Switch", HPR_MIXER, 2, 1, 0),
+ SOC_DAPM_SINGLE("PCM Playback Switch", HPR_MIXER, 1, 1, 0),
+ SOC_DAPM_SINGLE("Mic Sidetone Switch", HPR_MIXER, 0, 1, 0),
+};
+
+/* Speaker Mixer */
+static const struct snd_kcontrol_new wm9712_speaker_mixer_controls[] = {
+ SOC_DAPM_SINGLE("PCBeep Bypass Switch", AC97_PC_BEEP, 11, 1, 1),
+ SOC_DAPM_SINGLE("Aux Playback Switch", AC97_CD, 11, 1, 1),
+ SOC_DAPM_SINGLE("Phone Bypass Switch", AC97_PHONE, 14, 1, 1),
+ SOC_DAPM_SINGLE("Line Bypass Switch", AC97_LINE, 14, 1, 1),
+ SOC_DAPM_SINGLE("PCM Playback Switch", AC97_PCM, 14, 1, 1),
+};
+
+/* Phone Mixer */
+static const struct snd_kcontrol_new wm9712_phone_mixer_controls[] = {
+ SOC_DAPM_SINGLE("PCBeep Bypass Switch", AC97_PC_BEEP, 7, 1, 1),
+ SOC_DAPM_SINGLE("Aux Playback Switch", AC97_CD, 7, 1, 1),
+ SOC_DAPM_SINGLE("Line Bypass Switch", AC97_LINE, 13, 1, 1),
+ SOC_DAPM_SINGLE("PCM Playback Switch", AC97_PCM, 13, 1, 1),
+ SOC_DAPM_SINGLE("Mic 1 Sidetone Switch", AC97_MIC, 14, 1, 1),
+ SOC_DAPM_SINGLE("Mic 2 Sidetone Switch", AC97_MIC, 13, 1, 1),
+};
+
+/* ALC headphone mux */
+static const struct snd_kcontrol_new wm9712_alc_mux_controls =
+SOC_DAPM_ENUM("Route", wm9712_enum[1]);
+
+/* out 3 mux */
+static const struct snd_kcontrol_new wm9712_out3_mux_controls =
+SOC_DAPM_ENUM("Route", wm9712_enum[2]);
+
+/* spk mux */
+static const struct snd_kcontrol_new wm9712_spk_mux_controls =
+SOC_DAPM_ENUM("Route", wm9712_enum[3]);
+
+/* Capture to Phone mux */
+static const struct snd_kcontrol_new wm9712_capture_phone_mux_controls =
+SOC_DAPM_ENUM("Route", wm9712_enum[4]);
+
+/* Capture left select */
+static const struct snd_kcontrol_new wm9712_capture_selectl_controls =
+SOC_DAPM_ENUM("Route", wm9712_enum[8]);
+
+/* Capture right select */
+static const struct snd_kcontrol_new wm9712_capture_selectr_controls =
+SOC_DAPM_ENUM("Route", wm9712_enum[9]);
+
+/* Mic select */
+static const struct snd_kcontrol_new wm9712_mic_src_controls =
+SOC_DAPM_ENUM("Route", wm9712_enum[7]);
+
+/* diff select */
+static const struct snd_kcontrol_new wm9712_diff_sel_controls =
+SOC_DAPM_ENUM("Route", wm9712_enum[11]);
+
+static const struct snd_soc_dapm_widget wm9712_dapm_widgets[] = {
+SND_SOC_DAPM_MUX("ALC Sidetone Mux", SND_SOC_NOPM, 0, 0,
+ &wm9712_alc_mux_controls),
+SND_SOC_DAPM_MUX("Out3 Mux", SND_SOC_NOPM, 0, 0,
+ &wm9712_out3_mux_controls),
+SND_SOC_DAPM_MUX("Speaker Mux", SND_SOC_NOPM, 0, 0,
+ &wm9712_spk_mux_controls),
+SND_SOC_DAPM_MUX("Capture Phone Mux", SND_SOC_NOPM, 0, 0,
+ &wm9712_capture_phone_mux_controls),
+SND_SOC_DAPM_MUX("Left Capture Select", SND_SOC_NOPM, 0, 0,
+ &wm9712_capture_selectl_controls),
+SND_SOC_DAPM_MUX("Right Capture Select", SND_SOC_NOPM, 0, 0,
+ &wm9712_capture_selectr_controls),
+SND_SOC_DAPM_MUX("Mic Select Source", SND_SOC_NOPM, 0, 0,
+ &wm9712_mic_src_controls),
+SND_SOC_DAPM_MUX("Differential Source", SND_SOC_NOPM, 0, 0,
+ &wm9712_diff_sel_controls),
+SND_SOC_DAPM_MIXER("AC97 Mixer", SND_SOC_NOPM, 0, 0, NULL, 0),
+SND_SOC_DAPM_MIXER_E("Left HP Mixer", AC97_INT_PAGING, 9, 1,
+ &wm9712_hpl_mixer_controls[0], ARRAY_SIZE(wm9712_hpl_mixer_controls),
+ mixer_event, SND_SOC_DAPM_POST_REG),
+SND_SOC_DAPM_MIXER_E("Right HP Mixer", AC97_INT_PAGING, 8, 1,
+ &wm9712_hpr_mixer_controls[0], ARRAY_SIZE(wm9712_hpr_mixer_controls),
+ mixer_event, SND_SOC_DAPM_POST_REG),
+SND_SOC_DAPM_MIXER("Phone Mixer", AC97_INT_PAGING, 6, 1,
+ &wm9712_phone_mixer_controls[0], ARRAY_SIZE(wm9712_phone_mixer_controls)),
+SND_SOC_DAPM_MIXER("Speaker Mixer", AC97_INT_PAGING, 7, 1,
+ &wm9712_speaker_mixer_controls[0],
+ ARRAY_SIZE(wm9712_speaker_mixer_controls)),
+SND_SOC_DAPM_MIXER("Mono Mixer", SND_SOC_NOPM, 0, 0, NULL, 0),
+SND_SOC_DAPM_DAC("Left DAC", "Left HiFi Playback", AC97_INT_PAGING, 14, 1),
+SND_SOC_DAPM_DAC("Right DAC", "Right HiFi Playback", AC97_INT_PAGING, 13, 1),
+SND_SOC_DAPM_DAC("Aux DAC", "Aux Playback", SND_SOC_NOPM, 0, 0),
+SND_SOC_DAPM_ADC("Left ADC", "Left HiFi Capture", AC97_INT_PAGING, 12, 1),
+SND_SOC_DAPM_ADC("Right ADC", "Right HiFi Capture", AC97_INT_PAGING, 11, 1),
+SND_SOC_DAPM_PGA("Headphone PGA", AC97_INT_PAGING, 4, 1, NULL, 0),
+SND_SOC_DAPM_PGA("Speaker PGA", AC97_INT_PAGING, 3, 1, NULL, 0),
+SND_SOC_DAPM_PGA("Out 3 PGA", AC97_INT_PAGING, 5, 1, NULL, 0),
+SND_SOC_DAPM_PGA("Line PGA", AC97_INT_PAGING, 2, 1, NULL, 0),
+SND_SOC_DAPM_PGA("Phone PGA", AC97_INT_PAGING, 1, 1, NULL, 0),
+SND_SOC_DAPM_PGA("Mic PGA", AC97_INT_PAGING, 0, 1, NULL, 0),
+SND_SOC_DAPM_MICBIAS("Mic Bias", AC97_INT_PAGING, 10, 1),
+SND_SOC_DAPM_OUTPUT("MONOOUT"),
+SND_SOC_DAPM_OUTPUT("HPOUTL"),
+SND_SOC_DAPM_OUTPUT("HPOUTR"),
+SND_SOC_DAPM_OUTPUT("LOUT2"),
+SND_SOC_DAPM_OUTPUT("ROUT2"),
+SND_SOC_DAPM_OUTPUT("OUT3"),
+SND_SOC_DAPM_INPUT("LINEINL"),
+SND_SOC_DAPM_INPUT("LINEINR"),
+SND_SOC_DAPM_INPUT("PHONE"),
+SND_SOC_DAPM_INPUT("PCBEEP"),
+SND_SOC_DAPM_INPUT("MIC1"),
+SND_SOC_DAPM_INPUT("MIC2"),
+};
+
+static const char *audio_map[][3] = {
+ /* virtual mixer - mixes left & right channels for spk and mono */
+ {"AC97 Mixer", NULL, "Left DAC"},
+ {"AC97 Mixer", NULL, "Right DAC"},
+
+ /* Left HP mixer */
+ {"Left HP Mixer", "PCBeep Bypass Switch", "PCBEEP"},
+ {"Left HP Mixer", "Aux Playback Switch", "Aux DAC"},
+ {"Left HP Mixer", "Phone Bypass Switch", "Phone PGA"},
+ {"Left HP Mixer", "Line Bypass Switch", "Line PGA"},
+ {"Left HP Mixer", "PCM Playback Switch", "Left DAC"},
+ {"Left HP Mixer", "Mic Sidetone Switch", "Mic PGA"},
+ {"Left HP Mixer", NULL, "ALC Sidetone Mux"},
+ //{"Right HP Mixer", NULL, "HP Mixer"},
+
+ /* Right HP mixer */
+ {"Right HP Mixer", "PCBeep Bypass Switch", "PCBEEP"},
+ {"Right HP Mixer", "Aux Playback Switch", "Aux DAC"},
+ {"Right HP Mixer", "Phone Bypass Switch", "Phone PGA"},
+ {"Right HP Mixer", "Line Bypass Switch", "Line PGA"},
+ {"Right HP Mixer", "PCM Playback Switch", "Right DAC"},
+ {"Right HP Mixer", "Mic Sidetone Switch", "Mic PGA"},
+ {"Right HP Mixer", NULL, "ALC Sidetone Mux"},
+
+ /* speaker mixer */
+ {"Speaker Mixer", "PCBeep Bypass Switch", "PCBEEP"},
+ {"Speaker Mixer", "Line Bypass Switch", "Line PGA"},
+ {"Speaker Mixer", "PCM Playback Switch", "AC97 Mixer"},
+ {"Speaker Mixer", "Phone Bypass Switch", "Phone PGA"},
+ {"Speaker Mixer", "Aux Playback Switch", "Aux DAC"},
+
+ /* Phone mixer */
+ {"Phone Mixer", "PCBeep Bypass Switch", "PCBEEP"},
+ {"Phone Mixer", "Line Bypass Switch", "Line PGA"},
+ {"Phone Mixer", "Aux Playback Switch", "Aux DAC"},
+ {"Phone Mixer", "PCM Playback Switch", "AC97 Mixer"},
+ {"Phone Mixer", "Mic 1 Sidetone Switch", "Mic PGA"},
+ {"Phone Mixer", "Mic 2 Sidetone Switch", "Mic PGA"},
+
+ /* inputs */
+ {"Line PGA", NULL, "LINEINL"},
+ {"Line PGA", NULL, "LINEINR"},
+ {"Phone PGA", NULL, "PHONE"},
+ {"Mic PGA", NULL, "MIC1"},
+ {"Mic PGA", NULL, "MIC2"},
+
+ /* left capture selector */
+ {"Left Capture Select", "Mic", "MIC1"},
+ {"Left Capture Select", "Speaker Mixer", "Speaker Mixer"},
+ {"Left Capture Select", "Line", "LINEINL"},
+ {"Left Capture Select", "Headphone Mixer", "Left HP Mixer"},
+ {"Left Capture Select", "Phone Mixer", "Phone Mixer"},
+ {"Left Capture Select", "Phone", "PHONE"},
+
+ /* right capture selector */
+ {"Right Capture Select", "Mic", "MIC2"},
+ {"Right Capture Select", "Speaker Mixer", "Speaker Mixer"},
+ {"Right Capture Select", "Line", "LINEINR"},
+ {"Right Capture Select", "Headphone Mixer", "Right HP Mixer"},
+ {"Right Capture Select", "Phone Mixer", "Phone Mixer"},
+ {"Right Capture Select", "Phone", "PHONE"},
+
+ /* ALC Sidetone */
+ {"ALC Sidetone Mux", "Stereo", "Left Capture Select"},
+ {"ALC Sidetone Mux", "Stereo", "Right Capture Select"},
+ {"ALC Sidetone Mux", "Left", "Left Capture Select"},
+ {"ALC Sidetone Mux", "Right", "Right Capture Select"},
+
+ /* ADC's */
+ {"Left ADC", NULL, "Left Capture Select"},
+ {"Right ADC", NULL, "Right Capture Select"},
+
+ /* outputs */
+ {"MONOOUT", NULL, "Phone Mixer"},
+ {"HPOUTL", NULL, "Headphone PGA"},
+ {"Headphone PGA", NULL, "Left HP Mixer"},
+ {"HPOUTR", NULL, "Headphone PGA"},
+ {"Headphone PGA", NULL, "Right HP Mixer"},
+
+ /* mono hp mixer */
+ {"Mono HP Mixer", NULL, "Left HP Mixer"},
+ {"Mono HP Mixer", NULL, "Right HP Mixer"},
+
+ /* Out3 Mux */
+ {"Out3 Mux", "Left", "Left HP Mixer"},
+ {"Out3 Mux", "Mono", "Phone Mixer"},
+ {"Out3 Mux", "Left + Right", "Mono HP Mixer"},
+ {"Out 3 PGA", NULL, "Out3 Mux"},
+ {"OUT3", NULL, "Out 3 PGA"},
+
+ /* speaker Mux */
+ {"Speaker Mux", "Speaker Mix", "Speaker Mixer"},
+ {"Speaker Mux", "Headphone Mix", "Mono HP Mixer"},
+ {"Speaker PGA", NULL, "Speaker Mux"},
+ {"LOUT2", NULL, "Speaker PGA"},
+ {"ROUT2", NULL, "Speaker PGA"},
+
+ {NULL, NULL, NULL},
+};
+
+static int wm9712_add_widgets(struct snd_soc_codec *codec)
+{
+ int i;
+
+ for(i = 0; i < ARRAY_SIZE(wm9712_dapm_widgets); i++) {
+ snd_soc_dapm_new_control(codec, &wm9712_dapm_widgets[i]);
+ }
+
+ /* set up audio path audio_mapnects */
+ for(i = 0; audio_map[i][0] != NULL; i++) {
+ snd_soc_dapm_connect_input(codec, audio_map[i][0],
+ audio_map[i][1], audio_map[i][2]);
+ }
+
+ snd_soc_dapm_new_widgets(codec);
+ return 0;
+}
+
+static unsigned int ac97_read(struct snd_soc_codec *codec,
+ unsigned int reg)
+{
+ u16 *cache = codec->reg_cache;
+
+ if (reg == AC97_RESET || reg == AC97_GPIO_STATUS ||
+ reg == AC97_VENDOR_ID1 || reg == AC97_VENDOR_ID2 ||
+ reg == AC97_REC_GAIN)
+ return soc_ac97_ops.read(codec->ac97, reg);
+ else {
+ reg = reg >> 1;
+
+ if (reg > (ARRAY_SIZE(wm9712_reg)))
+ return -EIO;
+
+ return cache[reg];
+ }
+}
+
+static int ac97_write(struct snd_soc_codec *codec, unsigned int reg,
+ unsigned int val)
+{
+ u16 *cache = codec->reg_cache;
+
+ soc_ac97_ops.write(codec->ac97, reg, val);
+ reg = reg >> 1;
+ if (reg <= (ARRAY_SIZE(wm9712_reg)))
+ cache[reg] = val;
+
+ return 0;
+}
+
+static int ac97_prepare(struct snd_pcm_substream *substream)
+{
+ struct snd_pcm_runtime *runtime = substream->runtime;
+ struct snd_soc_pcm_runtime *rtd = substream->private_data;
+ struct snd_soc_device *socdev = rtd->socdev;
+ struct snd_soc_codec *codec = socdev->codec;
+ int reg;
+ u16 vra;
+
+ vra = ac97_read(codec, AC97_EXTENDED_STATUS);
+ ac97_write(codec, AC97_EXTENDED_STATUS, vra | 0x1);
+
+ if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
+ reg = AC97_PCM_FRONT_DAC_RATE;
+ else
+ reg = AC97_PCM_LR_ADC_RATE;
+
+ return ac97_write(codec, reg, runtime->rate);
+}
+
+static int ac97_aux_prepare(struct snd_pcm_substream *substream)
+{
+ struct snd_pcm_runtime *runtime = substream->runtime;
+ struct snd_soc_pcm_runtime *rtd = substream->private_data;
+ struct snd_soc_device *socdev = rtd->socdev;
+ struct snd_soc_codec *codec = socdev->codec;
+ u16 vra, xsle;
+
+ vra = ac97_read(codec, AC97_EXTENDED_STATUS);
+ ac97_write(codec, AC97_EXTENDED_STATUS, vra | 0x1);
+ xsle = ac97_read(codec, AC97_PCI_SID);
+ ac97_write(codec, AC97_PCI_SID, xsle | 0x8000);
+
+ if (substream->stream != SNDRV_PCM_STREAM_PLAYBACK)
+ return -ENODEV;
+
+ return ac97_write(codec, AC97_PCM_SURR_DAC_RATE, runtime->rate);
+}
+
+#define WM9712_AC97_RATES (SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_11025 |\
+ SNDRV_PCM_RATE_22050 | SNDRV_PCM_RATE_44100 | SNDRV_PCM_RATE_48000)
+
+struct snd_soc_codec_dai wm9712_dai[] = {
+{
+ .name = "AC97 HiFi",
+ .playback = {
+ .stream_name = "HiFi Playback",
+ .channels_min = 1,
+ .channels_max = 2,
+ .rates = WM9712_AC97_RATES,
+ .formats = SNDRV_PCM_FMTBIT_S16_LE,},
+ .capture = {
+ .stream_name = "HiFi Capture",
+ .channels_min = 1,
+ .channels_max = 2,
+ .rates = WM9712_AC97_RATES,
+ .formats = SNDRV_PCM_FMTBIT_S16_LE,},
+ .ops = {
+ .prepare = ac97_prepare,},
+},
+{
+ .name = "AC97 Aux",
+ .playback = {
+ .stream_name = "Aux Playback",
+ .channels_min = 1,
+ .channels_max = 1,
+ .rates = WM9712_AC97_RATES,
+ .formats = SNDRV_PCM_FMTBIT_S16_LE,},
+ .ops = {
+ .prepare = ac97_aux_prepare,},
+}
+};
+EXPORT_SYMBOL_GPL(wm9712_dai);
+
+static int wm9712_dapm_event(struct snd_soc_codec *codec, int event)
+{
+ u16 reg;
+
+ switch (event) {
+ case SNDRV_CTL_POWER_D0: /* full On */
+ /* liam - maybe enable thermal shutdown */
+ reg = ac97_read(codec, AC97_EXTENDED_MID) & 0xdfff;
+ ac97_write(codec, AC97_EXTENDED_MID, reg);
+ break;
+ case SNDRV_CTL_POWER_D1: /* partial On */
+ case SNDRV_CTL_POWER_D2: /* partial On */
+ break;
+ case SNDRV_CTL_POWER_D3hot: /* Off, with power */
+ /* enable master bias and vmid */
+ reg = ac97_read(codec, AC97_EXTENDED_MID) & 0xbbff;
+ ac97_write(codec, AC97_EXTENDED_MID, reg);
+ ac97_write(codec, AC97_POWERDOWN, 0x0000);
+ break;
+ case SNDRV_CTL_POWER_D3cold: /* Off, without power */
+ /* disable everything including AC link */
+ ac97_write(codec, AC97_EXTENDED_MID, 0xffff);
+ ac97_write(codec, AC97_EXTENDED_MSTATUS, 0xffff);
+ ac97_write(codec, AC97_POWERDOWN, 0xffff);
+ break;
+ }
+ codec->dapm_state = event;
+ return 0;
+}
+
+static int wm9712_reset(struct snd_soc_codec *codec, int try_warm)
+{
+ if (try_warm && soc_ac97_ops.warm_reset) {
+ soc_ac97_ops.warm_reset(codec->ac97);
+ if (!(ac97_read(codec, 0) & 0x8000))
+ return 1;
+ }
+
+ soc_ac97_ops.reset(codec->ac97);
+ if (ac97_read(codec, 0) & 0x8000)
+ goto err;
+ return 0;
+
+err:
+ printk(KERN_ERR "WM9712 AC97 reset failed\n");
+ return -EIO;
+}
+
+static int wm9712_soc_suspend(struct platform_device *pdev,
+ pm_message_t state)
+{
+ struct snd_soc_device *socdev = platform_get_drvdata(pdev);
+ struct snd_soc_codec *codec = socdev->codec;
+
+ wm9712_dapm_event(codec, SNDRV_CTL_POWER_D3cold);
+ return 0;
+}
+
+static int wm9712_soc_resume(struct platform_device *pdev)
+{
+ struct snd_soc_device *socdev = platform_get_drvdata(pdev);
+ struct snd_soc_codec *codec = socdev->codec;
+ int i, ret;
+ u16 *cache = codec->reg_cache;
+
+ ret = wm9712_reset(codec, 1);
+ if (ret < 0){
+ printk(KERN_ERR "could not reset AC97 codec\n");
+ return ret;
+ }
+
+ wm9712_dapm_event(codec, SNDRV_CTL_POWER_D3hot);
+
+ if (ret == 0) {
+ /* Sync reg_cache with the hardware after cold reset */
+ for (i = 2; i < ARRAY_SIZE(wm9712_reg) << 1; i+=2) {
+ if (i == AC97_INT_PAGING || i == AC97_POWERDOWN ||
+ (i > 0x58 && i != 0x5c))
+ continue;
+ soc_ac97_ops.write(codec->ac97, i, cache[i>>1]);
+ }
+ }
+
+ if (codec->suspend_dapm_state == SNDRV_CTL_POWER_D0)
+ wm9712_dapm_event(codec, SNDRV_CTL_POWER_D0);
+
+ return ret;
+}
+
+static int wm9712_soc_probe(struct platform_device *pdev)
+{
+ struct snd_soc_device *socdev = platform_get_drvdata(pdev);
+ struct snd_soc_codec *codec;
+ int ret = 0;
+
+ printk(KERN_INFO "WM9711/WM9712 SoC Audio Codec %s\n", WM9712_VERSION);
+
+ socdev->codec = kzalloc(sizeof(struct snd_soc_codec), GFP_KERNEL);
+ if (socdev->codec == NULL)
+ return -ENOMEM;
+ codec = socdev->codec;
+ mutex_init(&codec->mutex);
+
+ codec->reg_cache =
+ kzalloc(sizeof(u16) * ARRAY_SIZE(wm9712_reg), GFP_KERNEL);
+ if (codec->reg_cache == NULL) {
+ ret = -ENOMEM;
+ goto cache_err;
+ }
+ memcpy(codec->reg_cache, wm9712_reg, sizeof(u16) * ARRAY_SIZE(wm9712_reg));
+ codec->reg_cache_size = sizeof(u16) * ARRAY_SIZE(wm9712_reg);
+ codec->reg_cache_step = 2;
+
+ codec->name = "WM9712";
+ codec->owner = THIS_MODULE;
+ codec->dai = wm9712_dai;
+ codec->num_dai = ARRAY_SIZE(wm9712_dai);
+ codec->write = ac97_write;
+ codec->read = ac97_read;
+ codec->dapm_event = wm9712_dapm_event;
+ INIT_LIST_HEAD(&codec->dapm_widgets);
+ INIT_LIST_HEAD(&codec->dapm_paths);
+
+ ret = snd_soc_new_ac97_codec(codec, &soc_ac97_ops, 0);
+ if (ret < 0) {
+ printk(KERN_ERR "wm9712: failed to register AC97 codec\n");
+ goto codec_err;
+ }
+
+ /* register pcms */
+ ret = snd_soc_new_pcms(socdev, SNDRV_DEFAULT_IDX1, SNDRV_DEFAULT_STR1);
+ if (ret < 0)
+ goto pcm_err;
+
+ ret = wm9712_reset(codec, 0);
+ if (ret < 0) {
+ printk(KERN_ERR "AC97 link error\n");
+ goto reset_err;
+ }
+
+ /* set alc mux to none */
+ ac97_write(codec, AC97_VIDEO, ac97_read(codec, AC97_VIDEO) | 0x3000);
+
+ wm9712_dapm_event(codec, SNDRV_CTL_POWER_D3hot);
+ wm9712_add_controls(codec);
+ wm9712_add_widgets(codec);
+ ret = snd_soc_register_card(socdev);
+ if (ret < 0) {
+ printk(KERN_ERR "wm9712: failed to register card\n");
+ goto reset_err;
+ }
+
+ return 0;
+
+reset_err:
+ snd_soc_free_pcms(socdev);
+
+pcm_err:
+ snd_soc_free_ac97_codec(codec);
+
+codec_err:
+ kfree(codec->reg_cache);
+
+cache_err:
+ kfree(socdev->codec);
+ socdev->codec = NULL;
+ return ret;
+}
+
+static int wm9712_soc_remove(struct platform_device *pdev)
+{
+ struct snd_soc_device *socdev = platform_get_drvdata(pdev);
+ struct snd_soc_codec *codec = socdev->codec;
+
+ if (codec == NULL)
+ return 0;
+
+ snd_soc_dapm_free(socdev);
+ snd_soc_free_pcms(socdev);
+ snd_soc_free_ac97_codec(codec);
+ kfree(codec->reg_cache);
+ kfree(codec);
+ return 0;
+}
+
+struct snd_soc_codec_device soc_codec_dev_wm9712 = {
+ .probe = wm9712_soc_probe,
+ .remove = wm9712_soc_remove,
+ .suspend = wm9712_soc_suspend,
+ .resume = wm9712_soc_resume,
+};
+
+EXPORT_SYMBOL_GPL(soc_codec_dev_wm9712);
+
+MODULE_DESCRIPTION("ASoC WM9711/WM9712 driver");
+MODULE_AUTHOR("Liam Girdwood");
+MODULE_LICENSE("GPL");
diff --git a/sound/soc/codecs/wm9712.h b/sound/soc/codecs/wm9712.h
new file mode 100644
index 000000000000..719105d61e65
--- /dev/null
+++ b/sound/soc/codecs/wm9712.h
@@ -0,0 +1,14 @@
+/*
+ * wm9712.h -- WM9712 Soc Audio driver
+ */
+
+#ifndef _WM9712_H
+#define _WM9712_H
+
+#define WM9712_DAI_AC97_HIFI 0
+#define WM9712_DAI_AC97_AUX 1
+
+extern struct snd_soc_codec_dai wm9712_dai[2];
+extern struct snd_soc_codec_device soc_codec_dev_wm9712;
+
+#endif
diff --git a/sound/soc/pxa/Kconfig b/sound/soc/pxa/Kconfig
new file mode 100644
index 000000000000..579e1c8d2b28
--- /dev/null
+++ b/sound/soc/pxa/Kconfig
@@ -0,0 +1,60 @@
+menu "SoC Audio for the Intel PXA2xx"
+
+config SND_PXA2XX_SOC
+ tristate "SoC Audio for the Intel PXA2xx chip"
+ depends on ARCH_PXA && SND
+ select SND_PCM
+ help
+ Say Y or M if you want to add support for codecs attached to
+ the PXA2xx AC97, I2S or SSP interface. You will also need
+ to select the audio interfaces to support below.
+
+config SND_PXA2XX_AC97
+ tristate
+ select SND_AC97_CODEC
+
+config SND_PXA2XX_SOC_AC97
+ tristate
+ select AC97_BUS
+ select SND_SOC_AC97_BUS
+
+config SND_PXA2XX_SOC_I2S
+ tristate
+
+config SND_PXA2XX_SOC_CORGI
+ tristate "SoC Audio support for Sharp Zaurus SL-C7x0"
+ depends on SND_PXA2XX_SOC && PXA_SHARP_C7xx
+ select SND_PXA2XX_SOC_I2S
+ select SND_SOC_WM8731
+ help
+ Say Y if you want to add support for SoC audio on Sharp
+ Zaurus SL-C7x0 models (Corgi, Shepherd, Husky).
+
+config SND_PXA2XX_SOC_SPITZ
+ tristate "SoC Audio support for Sharp Zaurus SL-Cxx00"
+ depends on SND_PXA2XX_SOC && PXA_SHARP_Cxx00
+ select SND_PXA2XX_SOC_I2S
+ select SND_SOC_WM8750
+ help
+ Say Y if you want to add support for SoC audio on Sharp
+ Zaurus SL-Cxx00 models (Spitz, Borzoi and Akita).
+
+config SND_PXA2XX_SOC_POODLE
+ tristate "SoC Audio support for Poodle"
+ depends on SND_PXA2XX_SOC && MACH_POODLE
+ select SND_PXA2XX_SOC_I2S
+ select SND_SOC_WM8731
+ help
+ Say Y if you want to add support for SoC audio on Sharp
+ Zaurus SL-5600 model (Poodle).
+
+config SND_PXA2XX_SOC_TOSA
+ tristate "SoC AC97 Audio support for Tosa"
+ depends on SND_PXA2XX_SOC && MACH_TOSA
+ select SND_PXA2XX_SOC_AC97
+ select SND_SOC_WM9712
+ help
+ Say Y if you want to add support for SoC audio on Sharp
+ Zaurus SL-C6000x models (Tosa).
+
+endmenu
diff --git a/sound/soc/pxa/Makefile b/sound/soc/pxa/Makefile
new file mode 100644
index 000000000000..78e0d6b07d1d
--- /dev/null
+++ b/sound/soc/pxa/Makefile
@@ -0,0 +1,20 @@
+# PXA Platform Support
+snd-soc-pxa2xx-objs := pxa2xx-pcm.o
+snd-soc-pxa2xx-ac97-objs := pxa2xx-ac97.o
+snd-soc-pxa2xx-i2s-objs := pxa2xx-i2s.o
+
+obj-$(CONFIG_SND_PXA2XX_SOC) += snd-soc-pxa2xx.o
+obj-$(CONFIG_SND_PXA2XX_SOC_AC97) += snd-soc-pxa2xx-ac97.o
+obj-$(CONFIG_SND_PXA2XX_SOC_I2S) += snd-soc-pxa2xx-i2s.o
+
+# PXA Machine Support
+snd-soc-corgi-objs := corgi.o
+snd-soc-poodle-objs := poodle.o
+snd-soc-tosa-objs := tosa.o
+snd-soc-spitz-objs := spitz.o
+
+obj-$(CONFIG_SND_PXA2XX_SOC_CORGI) += snd-soc-corgi.o
+obj-$(CONFIG_SND_PXA2XX_SOC_POODLE) += snd-soc-poodle.o
+obj-$(CONFIG_SND_PXA2XX_SOC_TOSA) += snd-soc-tosa.o
+obj-$(CONFIG_SND_PXA2XX_SOC_SPITZ) += snd-soc-spitz.o
+
diff --git a/sound/soc/pxa/corgi.c b/sound/soc/pxa/corgi.c
new file mode 100644
index 000000000000..5ee51a994ac3
--- /dev/null
+++ b/sound/soc/pxa/corgi.c
@@ -0,0 +1,383 @@
+/*
+ * corgi.c -- SoC audio for Corgi
+ *
+ * Copyright 2005 Wolfson Microelectronics PLC.
+ * Copyright 2005 Openedhand Ltd.
+ *
+ * Authors: Liam Girdwood <liam.girdwood@wolfsonmicro.com>
+ * Richard Purdie <richard@openedhand.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.
+ *
+ * Revision history
+ * 30th Nov 2005 Initial version.
+ *
+ */
+
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/timer.h>
+#include <linux/interrupt.h>
+#include <linux/platform_device.h>
+#include <sound/driver.h>
+#include <sound/core.h>
+#include <sound/pcm.h>
+#include <sound/soc.h>
+#include <sound/soc-dapm.h>
+
+#include <asm/mach-types.h>
+#include <asm/hardware/scoop.h>
+#include <asm/arch/pxa-regs.h>
+#include <asm/arch/hardware.h>
+#include <asm/arch/corgi.h>
+#include <asm/arch/audio.h>
+
+#include "../codecs/wm8731.h"
+#include "pxa2xx-pcm.h"
+#include "pxa2xx-i2s.h"
+
+#define CORGI_HP 0
+#define CORGI_MIC 1
+#define CORGI_LINE 2
+#define CORGI_HEADSET 3
+#define CORGI_HP_OFF 4
+#define CORGI_SPK_ON 0
+#define CORGI_SPK_OFF 1
+
+ /* audio clock in Hz - rounded from 12.235MHz */
+#define CORGI_AUDIO_CLOCK 12288000
+
+static int corgi_jack_func;
+static int corgi_spk_func;
+
+static void corgi_ext_control(struct snd_soc_codec *codec)
+{
+ int spk = 0, mic = 0, line = 0, hp = 0, hs = 0;
+
+ /* set up jack connection */
+ switch (corgi_jack_func) {
+ case CORGI_HP:
+ hp = 1;
+ /* set = unmute headphone */
+ set_scoop_gpio(&corgiscoop_device.dev, CORGI_SCP_MUTE_L);
+ set_scoop_gpio(&corgiscoop_device.dev, CORGI_SCP_MUTE_R);
+ break;
+ case CORGI_MIC:
+ mic = 1;
+ /* reset = mute headphone */
+ reset_scoop_gpio(&corgiscoop_device.dev, CORGI_SCP_MUTE_L);
+ reset_scoop_gpio(&corgiscoop_device.dev, CORGI_SCP_MUTE_R);
+ break;
+ case CORGI_LINE:
+ line = 1;
+ reset_scoop_gpio(&corgiscoop_device.dev, CORGI_SCP_MUTE_L);
+ reset_scoop_gpio(&corgiscoop_device.dev, CORGI_SCP_MUTE_R);
+ break;
+ case CORGI_HEADSET:
+ hs = 1;
+ mic = 1;
+ reset_scoop_gpio(&corgiscoop_device.dev, CORGI_SCP_MUTE_L);
+ set_scoop_gpio(&corgiscoop_device.dev, CORGI_SCP_MUTE_R);
+ break;
+ }
+
+ if (corgi_spk_func == CORGI_SPK_ON)
+ spk = 1;
+
+ /* set the enpoints to their new connetion states */
+ snd_soc_dapm_set_endpoint(codec, "Ext Spk", spk);
+ snd_soc_dapm_set_endpoint(codec, "Mic Jack", mic);
+ snd_soc_dapm_set_endpoint(codec, "Line Jack", line);
+ snd_soc_dapm_set_endpoint(codec, "Headphone Jack", hp);
+ snd_soc_dapm_set_endpoint(codec, "Headset Jack", hs);
+
+ /* signal a DAPM event */
+ snd_soc_dapm_sync_endpoints(codec);
+}
+
+static int corgi_startup(struct snd_pcm_substream *substream)
+{
+ struct snd_soc_pcm_runtime *rtd = substream->private_data;
+ struct snd_soc_codec *codec = rtd->socdev->codec;
+
+ /* check the jack status at stream startup */
+ corgi_ext_control(codec);
+ return 0;
+}
+
+/* we need to unmute the HP at shutdown as the mute burns power on corgi */
+static int corgi_shutdown(struct snd_pcm_substream *substream)
+{
+ struct snd_soc_pcm_runtime *rtd = substream->private_data;
+ struct snd_soc_codec *codec = rtd->socdev->codec;
+
+ /* set = unmute headphone */
+ set_scoop_gpio(&corgiscoop_device.dev, CORGI_SCP_MUTE_L);
+ set_scoop_gpio(&corgiscoop_device.dev, CORGI_SCP_MUTE_R);
+ return 0;
+}
+
+static int corgi_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params)
+{
+ struct snd_soc_pcm_runtime *rtd = substream->private_data;
+ struct snd_soc_codec_dai *codec_dai = rtd->dai->codec_dai;
+ struct snd_soc_cpu_dai *cpu_dai = rtd->dai->cpu_dai;
+ unsigned int clk = 0;
+ int ret = 0;
+
+ switch (params_rate(params)) {
+ case 8000:
+ case 16000:
+ case 48000:
+ case 96000:
+ clk = 12288000;
+ break;
+ case 11025:
+ case 22050:
+ case 44100:
+ clk = 11289600;
+ break;
+ }
+
+ /* set codec DAI configuration */
+ ret = codec_dai->dai_ops.set_fmt(codec_dai, SND_SOC_DAIFMT_I2S |
+ SND_SOC_DAIFMT_NB_NF | SND_SOC_DAIFMT_CBS_CFS);
+ if (ret < 0)
+ return ret;
+
+ /* set cpu DAI configuration */
+ ret = cpu_dai->dai_ops.set_fmt(cpu_dai, SND_SOC_DAIFMT_I2S |
+ SND_SOC_DAIFMT_NB_NF | SND_SOC_DAIFMT_CBS_CFS);
+ if (ret < 0)
+ return ret;
+
+ /* set the codec system clock for DAC and ADC */
+ ret = codec_dai->dai_ops.set_sysclk(codec_dai, WM8731_SYSCLK, clk,
+ SND_SOC_CLOCK_IN);
+ if (ret < 0)
+ return ret;
+
+ /* set the I2S system clock as input (unused) */
+ ret = cpu_dai->dai_ops.set_sysclk(cpu_dai, PXA2XX_I2S_SYSCLK, 0,
+ SND_SOC_CLOCK_IN);
+ if (ret < 0)
+ return ret;
+
+ return 0;
+}
+
+static struct snd_soc_ops corgi_ops = {
+ .startup = corgi_startup,
+ .hw_params = corgi_hw_params,
+ .shutdown = corgi_shutdown,
+};
+
+static int corgi_get_jack(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ ucontrol->value.integer.value[0] = corgi_jack_func;
+ return 0;
+}
+
+static int corgi_set_jack(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol);
+
+ if (corgi_jack_func == ucontrol->value.integer.value[0])
+ return 0;
+
+ corgi_jack_func = ucontrol->value.integer.value[0];
+ corgi_ext_control(codec);
+ return 1;
+}
+
+static int corgi_get_spk(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ ucontrol->value.integer.value[0] = corgi_spk_func;
+ return 0;
+}
+
+static int corgi_set_spk(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol);
+
+ if (corgi_spk_func == ucontrol->value.integer.value[0])
+ return 0;
+
+ corgi_spk_func = ucontrol->value.integer.value[0];
+ corgi_ext_control(codec);
+ return 1;
+}
+
+static int corgi_amp_event(struct snd_soc_dapm_widget *w, int event)
+{
+ if (SND_SOC_DAPM_EVENT_ON(event))
+ set_scoop_gpio(&corgiscoop_device.dev, CORGI_SCP_APM_ON);
+ else
+ reset_scoop_gpio(&corgiscoop_device.dev, CORGI_SCP_APM_ON);
+
+ return 0;
+}
+
+static int corgi_mic_event(struct snd_soc_dapm_widget *w, int event)
+{
+ if (SND_SOC_DAPM_EVENT_ON(event))
+ set_scoop_gpio(&corgiscoop_device.dev, CORGI_SCP_MIC_BIAS);
+ else
+ reset_scoop_gpio(&corgiscoop_device.dev, CORGI_SCP_MIC_BIAS);
+
+ return 0;
+}
+
+/* corgi machine dapm widgets */
+static const struct snd_soc_dapm_widget wm8731_dapm_widgets[] = {
+SND_SOC_DAPM_HP("Headphone Jack", NULL),
+SND_SOC_DAPM_MIC("Mic Jack", corgi_mic_event),
+SND_SOC_DAPM_SPK("Ext Spk", corgi_amp_event),
+SND_SOC_DAPM_LINE("Line Jack", NULL),
+SND_SOC_DAPM_HP("Headset Jack", NULL),
+};
+
+/* Corgi machine audio map (connections to the codec pins) */
+static const char *audio_map[][3] = {
+
+ /* headset Jack - in = micin, out = LHPOUT*/
+ {"Headset Jack", NULL, "LHPOUT"},
+
+ /* headphone connected to LHPOUT1, RHPOUT1 */
+ {"Headphone Jack", NULL, "LHPOUT"},
+ {"Headphone Jack", NULL, "RHPOUT"},
+
+ /* speaker connected to LOUT, ROUT */
+ {"Ext Spk", NULL, "ROUT"},
+ {"Ext Spk", NULL, "LOUT"},
+
+ /* mic is connected to MICIN (via right channel of headphone jack) */
+ {"MICIN", NULL, "Mic Jack"},
+
+ /* Same as the above but no mic bias for line signals */
+ {"MICIN", NULL, "Line Jack"},
+
+ {NULL, NULL, NULL},
+};
+
+static const char *jack_function[] = {"Headphone", "Mic", "Line", "Headset",
+ "Off"};
+static const char *spk_function[] = {"On", "Off"};
+static const struct soc_enum corgi_enum[] = {
+ SOC_ENUM_SINGLE_EXT(5, jack_function),
+ SOC_ENUM_SINGLE_EXT(2, spk_function),
+};
+
+static const struct snd_kcontrol_new wm8731_corgi_controls[] = {
+ SOC_ENUM_EXT("Jack Function", corgi_enum[0], corgi_get_jack,
+ corgi_set_jack),
+ SOC_ENUM_EXT("Speaker Function", corgi_enum[1], corgi_get_spk,
+ corgi_set_spk),
+};
+
+/*
+ * Logic for a wm8731 as connected on a Sharp SL-C7x0 Device
+ */
+static int corgi_wm8731_init(struct snd_soc_codec *codec)
+{
+ int i, err;
+
+ snd_soc_dapm_set_endpoint(codec, "LLINEIN", 0);
+ snd_soc_dapm_set_endpoint(codec, "RLINEIN", 0);
+
+ /* Add corgi specific controls */
+ for (i = 0; i < ARRAY_SIZE(wm8731_corgi_controls); i++) {
+ err = snd_ctl_add(codec->card,
+ snd_soc_cnew(&wm8731_corgi_controls[i],codec, NULL));
+ if (err < 0)
+ return err;
+ }
+
+ /* Add corgi specific widgets */
+ for(i = 0; i < ARRAY_SIZE(wm8731_dapm_widgets); i++) {
+ snd_soc_dapm_new_control(codec, &wm8731_dapm_widgets[i]);
+ }
+
+ /* Set up corgi specific audio path audio_map */
+ for(i = 0; audio_map[i][0] != NULL; i++) {
+ snd_soc_dapm_connect_input(codec, audio_map[i][0],
+ audio_map[i][1], audio_map[i][2]);
+ }
+
+ snd_soc_dapm_sync_endpoints(codec);
+ return 0;
+}
+
+/* corgi digital audio interface glue - connects codec <--> CPU */
+static struct snd_soc_dai_link corgi_dai = {
+ .name = "WM8731",
+ .stream_name = "WM8731",
+ .cpu_dai = &pxa_i2s_dai,
+ .codec_dai = &wm8731_dai,
+ .init = corgi_wm8731_init,
+ .ops = &corgi_ops,
+};
+
+/* corgi audio machine driver */
+static struct snd_soc_machine snd_soc_machine_corgi = {
+ .name = "Corgi",
+ .dai_link = &corgi_dai,
+ .num_links = 1,
+};
+
+/* corgi audio private data */
+static struct wm8731_setup_data corgi_wm8731_setup = {
+ .i2c_address = 0x1b,
+};
+
+/* corgi audio subsystem */
+static struct snd_soc_device corgi_snd_devdata = {
+ .machine = &snd_soc_machine_corgi,
+ .platform = &pxa2xx_soc_platform,
+ .codec_dev = &soc_codec_dev_wm8731,
+ .codec_data = &corgi_wm8731_setup,
+};
+
+static struct platform_device *corgi_snd_device;
+
+static int __init corgi_init(void)
+{
+ int ret;
+
+ if (!(machine_is_corgi() || machine_is_shepherd() || machine_is_husky()))
+ return -ENODEV;
+
+ corgi_snd_device = platform_device_alloc("soc-audio", -1);
+ if (!corgi_snd_device)
+ return -ENOMEM;
+
+ platform_set_drvdata(corgi_snd_device, &corgi_snd_devdata);
+ corgi_snd_devdata.dev = &corgi_snd_device->dev;
+ ret = platform_device_add(corgi_snd_device);
+
+ if (ret)
+ platform_device_put(corgi_snd_device);
+
+ return ret;
+}
+
+static void __exit corgi_exit(void)
+{
+ platform_device_unregister(corgi_snd_device);
+}
+
+module_init(corgi_init);
+module_exit(corgi_exit);
+
+/* Module information */
+MODULE_AUTHOR("Richard Purdie");
+MODULE_DESCRIPTION("ALSA SoC Corgi");
+MODULE_LICENSE("GPL");
diff --git a/sound/soc/pxa/poodle.c b/sound/soc/pxa/poodle.c
new file mode 100644
index 000000000000..0915cf740421
--- /dev/null
+++ b/sound/soc/pxa/poodle.c
@@ -0,0 +1,352 @@
+/*
+ * poodle.c -- SoC audio for Poodle
+ *
+ * Copyright 2005 Wolfson Microelectronics PLC.
+ * Copyright 2005 Openedhand Ltd.
+ *
+ * Authors: Liam Girdwood <liam.girdwood@wolfsonmicro.com>
+ * Richard Purdie <richard@openedhand.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.
+ *
+ */
+
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/timer.h>
+#include <linux/interrupt.h>
+#include <linux/platform_device.h>
+#include <sound/driver.h>
+#include <sound/core.h>
+#include <sound/pcm.h>
+#include <sound/soc.h>
+#include <sound/soc-dapm.h>
+
+#include <asm/mach-types.h>
+#include <asm/hardware/locomo.h>
+#include <asm/arch/pxa-regs.h>
+#include <asm/arch/hardware.h>
+#include <asm/arch/poodle.h>
+#include <asm/arch/audio.h>
+
+#include "../codecs/wm8731.h"
+#include "pxa2xx-pcm.h"
+#include "pxa2xx-i2s.h"
+
+#define POODLE_HP 1
+#define POODLE_HP_OFF 0
+#define POODLE_SPK_ON 1
+#define POODLE_SPK_OFF 0
+
+ /* audio clock in Hz - rounded from 12.235MHz */
+#define POODLE_AUDIO_CLOCK 12288000
+
+static int poodle_jack_func;
+static int poodle_spk_func;
+
+static void poodle_ext_control(struct snd_soc_codec *codec)
+{
+ int spk = 0;
+
+ /* set up jack connection */
+ if (poodle_jack_func == POODLE_HP) {
+ /* set = unmute headphone */
+ locomo_gpio_write(&poodle_locomo_device.dev,
+ POODLE_LOCOMO_GPIO_MUTE_L, 1);
+ locomo_gpio_write(&poodle_locomo_device.dev,
+ POODLE_LOCOMO_GPIO_MUTE_R, 1);
+ snd_soc_dapm_set_endpoint(codec, "Headphone Jack", 1);
+ } else {
+ locomo_gpio_write(&poodle_locomo_device.dev,
+ POODLE_LOCOMO_GPIO_MUTE_L, 0);
+ locomo_gpio_write(&poodle_locomo_device.dev,
+ POODLE_LOCOMO_GPIO_MUTE_R, 0);
+ snd_soc_dapm_set_endpoint(codec, "Headphone Jack", 0);
+ }
+
+ if (poodle_spk_func == POODLE_SPK_ON)
+ spk = 1;
+
+ /* set the enpoints to their new connetion states */
+ snd_soc_dapm_set_endpoint(codec, "Ext Spk", spk);
+
+ /* signal a DAPM event */
+ snd_soc_dapm_sync_endpoints(codec);
+}
+
+static int poodle_startup(struct snd_pcm_substream *substream)
+{
+ struct snd_soc_pcm_runtime *rtd = substream->private_data;
+ struct snd_soc_codec *codec = rtd->socdev->codec;
+
+ /* check the jack status at stream startup */
+ poodle_ext_control(codec);
+ return 0;
+}
+
+/* we need to unmute the HP at shutdown as the mute burns power on poodle */
+static int poodle_shutdown(struct snd_pcm_substream *substream)
+{
+ struct snd_soc_pcm_runtime *rtd = substream->private_data;
+ struct snd_soc_codec *codec = rtd->socdev->codec;
+
+ /* set = unmute headphone */
+ locomo_gpio_write(&poodle_locomo_device.dev,
+ POODLE_LOCOMO_GPIO_MUTE_L, 1);
+ locomo_gpio_write(&poodle_locomo_device.dev,
+ POODLE_LOCOMO_GPIO_MUTE_R, 1);
+ return 0;
+}
+
+static int poodle_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params)
+{
+ struct snd_soc_pcm_runtime *rtd = substream->private_data;
+ struct snd_soc_codec_dai *codec_dai = rtd->dai->codec_dai;
+ struct snd_soc_cpu_dai *cpu_dai = rtd->dai->cpu_dai;
+ unsigned int clk = 0;
+ int ret = 0;
+
+ switch (params_rate(params)) {
+ case 8000:
+ case 16000:
+ case 48000:
+ case 96000:
+ clk = 12288000;
+ break;
+ case 11025:
+ case 22050:
+ case 44100:
+ clk = 11289600;
+ break;
+ }
+
+ /* set codec DAI configuration */
+ ret = codec_dai->dai_ops.set_fmt(codec_dai, SND_SOC_DAIFMT_I2S |
+ SND_SOC_DAIFMT_NB_NF | SND_SOC_DAIFMT_CBS_CFS);
+ if (ret < 0)
+ return ret;
+
+ /* set cpu DAI configuration */
+ ret = cpu_dai->dai_ops.set_fmt(cpu_dai, SND_SOC_DAIFMT_I2S |
+ SND_SOC_DAIFMT_NB_NF | SND_SOC_DAIFMT_CBS_CFS);
+ if (ret < 0)
+ return ret;
+
+ /* set the codec system clock for DAC and ADC */
+ ret = codec_dai->dai_ops.set_sysclk(codec_dai, WM8731_SYSCLK, clk,
+ SND_SOC_CLOCK_IN);
+ if (ret < 0)
+ return ret;
+
+ /* set the I2S system clock as input (unused) */
+ ret = cpu_dai->dai_ops.set_sysclk(cpu_dai, PXA2XX_I2S_SYSCLK, 0,
+ SND_SOC_CLOCK_IN);
+ if (ret < 0)
+ return ret;
+
+ return 0;
+}
+
+static struct snd_soc_ops poodle_ops = {
+ .startup = poodle_startup,
+ .hw_params = poodle_hw_params,
+ .shutdown = poodle_shutdown,
+};
+
+static int poodle_get_jack(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ ucontrol->value.integer.value[0] = poodle_jack_func;
+ return 0;
+}
+
+static int poodle_set_jack(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol);
+
+ if (poodle_jack_func == ucontrol->value.integer.value[0])
+ return 0;
+
+ poodle_jack_func = ucontrol->value.integer.value[0];
+ poodle_ext_control(codec);
+ return 1;
+}
+
+static int poodle_get_spk(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ ucontrol->value.integer.value[0] = poodle_spk_func;
+ return 0;
+}
+
+static int poodle_set_spk(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol);
+
+ if (poodle_spk_func == ucontrol->value.integer.value[0])
+ return 0;
+
+ poodle_spk_func = ucontrol->value.integer.value[0];
+ poodle_ext_control(codec);
+ return 1;
+}
+
+static int poodle_amp_event(struct snd_soc_dapm_widget *w, int event)
+{
+ if (SND_SOC_DAPM_EVENT_ON(event))
+ locomo_gpio_write(&poodle_locomo_device.dev,
+ POODLE_LOCOMO_GPIO_AMP_ON, 0);
+ else
+ locomo_gpio_write(&poodle_locomo_device.dev,
+ POODLE_LOCOMO_GPIO_AMP_ON, 1);
+
+ return 0;
+}
+
+/* poodle machine dapm widgets */
+static const struct snd_soc_dapm_widget wm8731_dapm_widgets[] = {
+SND_SOC_DAPM_HP("Headphone Jack", NULL),
+SND_SOC_DAPM_SPK("Ext Spk", poodle_amp_event),
+};
+
+/* Corgi machine audio_mapnections to the codec pins */
+static const char *audio_map[][3] = {
+
+ /* headphone connected to LHPOUT1, RHPOUT1 */
+ {"Headphone Jack", NULL, "LHPOUT"},
+ {"Headphone Jack", NULL, "RHPOUT"},
+
+ /* speaker connected to LOUT, ROUT */
+ {"Ext Spk", NULL, "ROUT"},
+ {"Ext Spk", NULL, "LOUT"},
+
+ {NULL, NULL, NULL},
+};
+
+static const char *jack_function[] = {"Off", "Headphone"};
+static const char *spk_function[] = {"Off", "On"};
+static const struct soc_enum poodle_enum[] = {
+ SOC_ENUM_SINGLE_EXT(2, jack_function),
+ SOC_ENUM_SINGLE_EXT(2, spk_function),
+};
+
+static const snd_kcontrol_new_t wm8731_poodle_controls[] = {
+ SOC_ENUM_EXT("Jack Function", poodle_enum[0], poodle_get_jack,
+ poodle_set_jack),
+ SOC_ENUM_EXT("Speaker Function", poodle_enum[1], poodle_get_spk,
+ poodle_set_spk),
+};
+
+/*
+ * Logic for a wm8731 as connected on a Sharp SL-C7x0 Device
+ */
+static int poodle_wm8731_init(struct snd_soc_codec *codec)
+{
+ int i, err;
+
+ snd_soc_dapm_set_endpoint(codec, "LLINEIN", 0);
+ snd_soc_dapm_set_endpoint(codec, "RLINEIN", 0);
+ snd_soc_dapm_set_endpoint(codec, "MICIN", 1);
+
+ /* Add poodle specific controls */
+ for (i = 0; i < ARRAY_SIZE(wm8731_poodle_controls); i++) {
+ err = snd_ctl_add(codec->card,
+ snd_soc_cnew(&wm8731_poodle_controls[i],codec, NULL));
+ if (err < 0)
+ return err;
+ }
+
+ /* Add poodle specific widgets */
+ for (i = 0; i < ARRAY_SIZE(wm8731_dapm_widgets); i++) {
+ snd_soc_dapm_new_control(codec, &wm8731_dapm_widgets[i]);
+ }
+
+ /* Set up poodle specific audio path audio_map */
+ for (i = 0; audio_map[i][0] != NULL; i++) {
+ snd_soc_dapm_connect_input(codec, audio_map[i][0],
+ audio_map[i][1], audio_map[i][2]);
+ }
+
+ snd_soc_dapm_sync_endpoints(codec);
+ return 0;
+}
+
+/* poodle digital audio interface glue - connects codec <--> CPU */
+static struct snd_soc_dai_link poodle_dai = {
+ .name = "WM8731",
+ .stream_name = "WM8731",
+ .cpu_dai = &pxa_i2s_dai,
+ .codec_dai = &wm8731_dai,
+ .init = poodle_wm8731_init,
+ .ops = &poodle_ops,
+};
+
+/* poodle audio machine driver */
+static struct snd_soc_machine snd_soc_machine_poodle = {
+ .name = "Poodle",
+ .dai_link = &poodle_dai,
+ .num_links = 1,
+};
+
+/* poodle audio private data */
+static struct wm8731_setup_data poodle_wm8731_setup = {
+ .i2c_address = 0x1b,
+};
+
+/* poodle audio subsystem */
+static struct snd_soc_device poodle_snd_devdata = {
+ .machine = &snd_soc_machine_poodle,
+ .platform = &pxa2xx_soc_platform,
+ .codec_dev = &soc_codec_dev_wm8731,
+ .codec_data = &poodle_wm8731_setup,
+};
+
+static struct platform_device *poodle_snd_device;
+
+static int __init poodle_init(void)
+{
+ int ret;
+
+ if (!machine_is_poodle())
+ return -ENODEV;
+
+ locomo_gpio_set_dir(&poodle_locomo_device.dev,
+ POODLE_LOCOMO_GPIO_AMP_ON, 0);
+ /* should we mute HP at startup - burning power ?*/
+ locomo_gpio_set_dir(&poodle_locomo_device.dev,
+ POODLE_LOCOMO_GPIO_MUTE_L, 0);
+ locomo_gpio_set_dir(&poodle_locomo_device.dev,
+ POODLE_LOCOMO_GPIO_MUTE_R, 0);
+
+ poodle_snd_device = platform_device_alloc("soc-audio", -1);
+ if (!poodle_snd_device)
+ return -ENOMEM;
+
+ platform_set_drvdata(poodle_snd_device, &poodle_snd_devdata);
+ poodle_snd_devdata.dev = &poodle_snd_device->dev;
+ ret = platform_device_add(poodle_snd_device);
+
+ if (ret)
+ platform_device_put(poodle_snd_device);
+
+ return ret;
+}
+
+static void __exit poodle_exit(void)
+{
+ platform_device_unregister(poodle_snd_device);
+}
+
+module_init(poodle_init);
+module_exit(poodle_exit);
+
+/* Module information */
+MODULE_AUTHOR("Richard Purdie");
+MODULE_DESCRIPTION("ALSA SoC Poodle");
+MODULE_LICENSE("GPL");
diff --git a/sound/soc/pxa/pxa2xx-ac97.c b/sound/soc/pxa/pxa2xx-ac97.c
new file mode 100644
index 000000000000..1bbbeff84ef0
--- /dev/null
+++ b/sound/soc/pxa/pxa2xx-ac97.c
@@ -0,0 +1,431 @@
+/*
+ * linux/sound/pxa2xx-ac97.c -- AC97 support for the Intel PXA2xx chip.
+ *
+ * Author: Nicolas Pitre
+ * Created: Dec 02, 2004
+ * Copyright: MontaVista Software Inc.
+ *
+ * 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.
+ */
+
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/interrupt.h>
+#include <linux/wait.h>
+#include <linux/delay.h>
+
+#include <sound/driver.h>
+#include <sound/core.h>
+#include <sound/pcm.h>
+#include <sound/ac97_codec.h>
+#include <sound/initval.h>
+#include <sound/soc.h>
+
+#include <asm/irq.h>
+#include <linux/mutex.h>
+#include <asm/hardware.h>
+#include <asm/arch/pxa-regs.h>
+#include <asm/arch/audio.h>
+
+#include "pxa2xx-pcm.h"
+#include "pxa2xx-ac97.h"
+
+static DEFINE_MUTEX(car_mutex);
+static DECLARE_WAIT_QUEUE_HEAD(gsr_wq);
+static volatile long gsr_bits;
+
+/*
+ * Beware PXA27x bugs:
+ *
+ * o Slot 12 read from modem space will hang controller.
+ * o CDONE, SDONE interrupt fails after any slot 12 IO.
+ *
+ * We therefore have an hybrid approach for waiting on SDONE (interrupt or
+ * 1 jiffy timeout if interrupt never comes).
+ */
+
+static unsigned short pxa2xx_ac97_read(struct snd_ac97 *ac97,
+ unsigned short reg)
+{
+ unsigned short val = -1;
+ volatile u32 *reg_addr;
+
+ mutex_lock(&car_mutex);
+
+ /* set up primary or secondary codec/modem space */
+#ifdef CONFIG_PXA27x
+ reg_addr = ac97->num ? &SAC_REG_BASE : &PAC_REG_BASE;
+#else
+ if (reg == AC97_GPIO_STATUS)
+ reg_addr = ac97->num ? &SMC_REG_BASE : &PMC_REG_BASE;
+ else
+ reg_addr = ac97->num ? &SAC_REG_BASE : &PAC_REG_BASE;
+#endif
+ reg_addr += (reg >> 1);
+
+#ifndef CONFIG_PXA27x
+ if (reg == AC97_GPIO_STATUS) {
+ /* read from controller cache */
+ val = *reg_addr;
+ goto out;
+ }
+#endif
+
+ /* start read access across the ac97 link */
+ GSR = GSR_CDONE | GSR_SDONE;
+ gsr_bits = 0;
+ val = *reg_addr;
+
+ wait_event_timeout(gsr_wq, (GSR | gsr_bits) & GSR_SDONE, 1);
+ if (!((GSR | gsr_bits) & GSR_SDONE)) {
+ printk(KERN_ERR "%s: read error (ac97_reg=%x GSR=%#lx)\n",
+ __FUNCTION__, reg, GSR | gsr_bits);
+ val = -1;
+ goto out;
+ }
+
+ /* valid data now */
+ GSR = GSR_CDONE | GSR_SDONE;
+ gsr_bits = 0;
+ val = *reg_addr;
+ /* but we've just started another cycle... */
+ wait_event_timeout(gsr_wq, (GSR | gsr_bits) & GSR_SDONE, 1);
+
+out: mutex_unlock(&car_mutex);
+ return val;
+}
+
+static void pxa2xx_ac97_write(struct snd_ac97 *ac97, unsigned short reg,
+ unsigned short val)
+{
+ volatile u32 *reg_addr;
+
+ mutex_lock(&car_mutex);
+
+ /* set up primary or secondary codec/modem space */
+#ifdef CONFIG_PXA27x
+ reg_addr = ac97->num ? &SAC_REG_BASE : &PAC_REG_BASE;
+#else
+ if (reg == AC97_GPIO_STATUS)
+ reg_addr = ac97->num ? &SMC_REG_BASE : &PMC_REG_BASE;
+ else
+ reg_addr = ac97->num ? &SAC_REG_BASE : &PAC_REG_BASE;
+#endif
+ reg_addr += (reg >> 1);
+
+ GSR = GSR_CDONE | GSR_SDONE;
+ gsr_bits = 0;
+ *reg_addr = val;
+ wait_event_timeout(gsr_wq, (GSR | gsr_bits) & GSR_CDONE, 1);
+ if (!((GSR | gsr_bits) & GSR_CDONE))
+ printk(KERN_ERR "%s: write error (ac97_reg=%x GSR=%#lx)\n",
+ __FUNCTION__, reg, GSR | gsr_bits);
+
+ mutex_unlock(&car_mutex);
+}
+
+static void pxa2xx_ac97_warm_reset(struct snd_ac97 *ac97)
+{
+ gsr_bits = 0;
+
+#ifdef CONFIG_PXA27x
+ /* warm reset broken on Bulverde,
+ so manually keep AC97 reset high */
+ pxa_gpio_mode(113 | GPIO_OUT | GPIO_DFLT_HIGH);
+ udelay(10);
+ GCR |= GCR_WARM_RST;
+ pxa_gpio_mode(113 | GPIO_ALT_FN_2_OUT);
+ udelay(500);
+#else
+ GCR |= GCR_WARM_RST | GCR_PRIRDY_IEN | GCR_SECRDY_IEN;
+ wait_event_timeout(gsr_wq, gsr_bits & (GSR_PCR | GSR_SCR), 1);
+#endif
+
+ if (!((GSR | gsr_bits) & (GSR_PCR | GSR_SCR)))
+ printk(KERN_INFO "%s: warm reset timeout (GSR=%#lx)\n",
+ __FUNCTION__, gsr_bits);
+
+ GCR &= ~(GCR_PRIRDY_IEN|GCR_SECRDY_IEN);
+ GCR |= GCR_SDONE_IE|GCR_CDONE_IE;
+}
+
+static void pxa2xx_ac97_cold_reset(struct snd_ac97 *ac97)
+{
+ GCR &= GCR_COLD_RST; /* clear everything but nCRST */
+ GCR &= ~GCR_COLD_RST; /* then assert nCRST */
+
+ gsr_bits = 0;
+#ifdef CONFIG_PXA27x
+ /* PXA27x Developers Manual section 13.5.2.2.1 */
+ pxa_set_cken(1 << 31, 1);
+ udelay(5);
+ pxa_set_cken(1 << 31, 0);
+ GCR = GCR_COLD_RST;
+ udelay(50);
+#else
+ GCR = GCR_COLD_RST;
+ GCR |= GCR_CDONE_IE|GCR_SDONE_IE;
+ wait_event_timeout(gsr_wq, gsr_bits & (GSR_PCR | GSR_SCR), 1);
+#endif
+
+ if (!((GSR | gsr_bits) & (GSR_PCR | GSR_SCR)))
+ printk(KERN_INFO "%s: cold reset timeout (GSR=%#lx)\n",
+ __FUNCTION__, gsr_bits);
+
+ GCR &= ~(GCR_PRIRDY_IEN|GCR_SECRDY_IEN);
+ GCR |= GCR_SDONE_IE|GCR_CDONE_IE;
+}
+
+static irqreturn_t pxa2xx_ac97_irq(int irq, void *dev_id)
+{
+ long status;
+
+ status = GSR;
+ if (status) {
+ GSR = status;
+ gsr_bits |= status;
+ wake_up(&gsr_wq);
+
+#ifdef CONFIG_PXA27x
+ /* Although we don't use those we still need to clear them
+ since they tend to spuriously trigger when MMC is used
+ (hardware bug? go figure)... */
+ MISR = MISR_EOC;
+ PISR = PISR_EOC;
+ MCSR = MCSR_EOC;
+#endif
+
+ return IRQ_HANDLED;
+ }
+
+ return IRQ_NONE;
+}
+
+struct snd_ac97_bus_ops soc_ac97_ops = {
+ .read = pxa2xx_ac97_read,
+ .write = pxa2xx_ac97_write,
+ .warm_reset = pxa2xx_ac97_warm_reset,
+ .reset = pxa2xx_ac97_cold_reset,
+};
+
+static struct pxa2xx_pcm_dma_params pxa2xx_ac97_pcm_stereo_out = {
+ .name = "AC97 PCM Stereo out",
+ .dev_addr = __PREG(PCDR),
+ .drcmr = &DRCMRTXPCDR,
+ .dcmd = DCMD_INCSRCADDR | DCMD_FLOWTRG |
+ DCMD_BURST32 | DCMD_WIDTH4,
+};
+
+static struct pxa2xx_pcm_dma_params pxa2xx_ac97_pcm_stereo_in = {
+ .name = "AC97 PCM Stereo in",
+ .dev_addr = __PREG(PCDR),
+ .drcmr = &DRCMRRXPCDR,
+ .dcmd = DCMD_INCTRGADDR | DCMD_FLOWSRC |
+ DCMD_BURST32 | DCMD_WIDTH4,
+};
+
+static struct pxa2xx_pcm_dma_params pxa2xx_ac97_pcm_aux_mono_out = {
+ .name = "AC97 Aux PCM (Slot 5) Mono out",
+ .dev_addr = __PREG(MODR),
+ .drcmr = &DRCMRTXMODR,
+ .dcmd = DCMD_INCSRCADDR | DCMD_FLOWTRG |
+ DCMD_BURST16 | DCMD_WIDTH2,
+};
+
+static struct pxa2xx_pcm_dma_params pxa2xx_ac97_pcm_aux_mono_in = {
+ .name = "AC97 Aux PCM (Slot 5) Mono in",
+ .dev_addr = __PREG(MODR),
+ .drcmr = &DRCMRRXMODR,
+ .dcmd = DCMD_INCTRGADDR | DCMD_FLOWSRC |
+ DCMD_BURST16 | DCMD_WIDTH2,
+};
+
+static struct pxa2xx_pcm_dma_params pxa2xx_ac97_pcm_mic_mono_in = {
+ .name = "AC97 Mic PCM (Slot 6) Mono in",
+ .dev_addr = __PREG(MCDR),
+ .drcmr = &DRCMRRXMCDR,
+ .dcmd = DCMD_INCTRGADDR | DCMD_FLOWSRC |
+ DCMD_BURST16 | DCMD_WIDTH2,
+};
+
+#ifdef CONFIG_PM
+static int pxa2xx_ac97_suspend(struct platform_device *pdev,
+ struct snd_soc_cpu_dai *dai)
+{
+ GCR |= GCR_ACLINK_OFF;
+ pxa_set_cken(CKEN2_AC97, 0);
+ return 0;
+}
+
+static int pxa2xx_ac97_resume(struct platform_device *pdev,
+ struct snd_soc_cpu_dai *dai)
+{
+ pxa_gpio_mode(GPIO31_SYNC_AC97_MD);
+ pxa_gpio_mode(GPIO30_SDATA_OUT_AC97_MD);
+ pxa_gpio_mode(GPIO28_BITCLK_AC97_MD);
+ pxa_gpio_mode(GPIO29_SDATA_IN_AC97_MD);
+#ifdef CONFIG_PXA27x
+ /* Use GPIO 113 as AC97 Reset on Bulverde */
+ pxa_gpio_mode(113 | GPIO_ALT_FN_2_OUT);
+#endif
+ pxa_set_cken(CKEN2_AC97, 1);
+ return 0;
+}
+
+#else
+#define pxa2xx_ac97_suspend NULL
+#define pxa2xx_ac97_resume NULL
+#endif
+
+static int pxa2xx_ac97_probe(struct platform_device *pdev)
+{
+ int ret;
+
+ ret = request_irq(IRQ_AC97, pxa2xx_ac97_irq, IRQF_DISABLED, "AC97", NULL);
+ if (ret < 0)
+ goto err;
+
+ pxa_gpio_mode(GPIO31_SYNC_AC97_MD);
+ pxa_gpio_mode(GPIO30_SDATA_OUT_AC97_MD);
+ pxa_gpio_mode(GPIO28_BITCLK_AC97_MD);
+ pxa_gpio_mode(GPIO29_SDATA_IN_AC97_MD);
+#ifdef CONFIG_PXA27x
+ /* Use GPIO 113 as AC97 Reset on Bulverde */
+ pxa_gpio_mode(113 | GPIO_ALT_FN_2_OUT);
+#endif
+ pxa_set_cken(CKEN2_AC97, 1);
+ return 0;
+
+ err:
+ if (CKEN & CKEN2_AC97) {
+ GCR |= GCR_ACLINK_OFF;
+ free_irq(IRQ_AC97, NULL);
+ pxa_set_cken(CKEN2_AC97, 0);
+ }
+ return ret;
+}
+
+static void pxa2xx_ac97_remove(struct platform_device *pdev)
+{
+ GCR |= GCR_ACLINK_OFF;
+ free_irq(IRQ_AC97, NULL);
+ pxa_set_cken(CKEN2_AC97, 0);
+}
+
+static int pxa2xx_ac97_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params)
+{
+ struct snd_soc_pcm_runtime *rtd = substream->private_data;
+ struct snd_soc_cpu_dai *cpu_dai = rtd->dai->cpu_dai;
+
+ if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
+ cpu_dai->dma_data = &pxa2xx_ac97_pcm_stereo_out;
+ else
+ cpu_dai->dma_data = &pxa2xx_ac97_pcm_stereo_in;
+
+ return 0;
+}
+
+static int pxa2xx_ac97_hw_aux_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params)
+{
+ struct snd_soc_pcm_runtime *rtd = substream->private_data;
+ struct snd_soc_cpu_dai *cpu_dai = rtd->dai->cpu_dai;
+
+ if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
+ cpu_dai->dma_data = &pxa2xx_ac97_pcm_aux_mono_out;
+ else
+ cpu_dai->dma_data = &pxa2xx_ac97_pcm_aux_mono_in;
+
+ return 0;
+}
+
+static int pxa2xx_ac97_hw_mic_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params)
+{
+ struct snd_soc_pcm_runtime *rtd = substream->private_data;
+ struct snd_soc_cpu_dai *cpu_dai = rtd->dai->cpu_dai;
+
+ if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
+ return -ENODEV;
+ else
+ cpu_dai->dma_data = &pxa2xx_ac97_pcm_mic_mono_in;
+
+ return 0;
+}
+
+#define PXA2XX_AC97_RATES (SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_11025 |\
+ SNDRV_PCM_RATE_16000 | SNDRV_PCM_RATE_22050 | SNDRV_PCM_RATE_44100 | \
+ SNDRV_PCM_RATE_48000)
+
+/*
+ * There is only 1 physical AC97 interface for pxa2xx, but it
+ * has extra fifo's that can be used for aux DACs and ADCs.
+ */
+struct snd_soc_cpu_dai pxa_ac97_dai[] = {
+{
+ .name = "pxa2xx-ac97",
+ .id = 0,
+ .type = SND_SOC_DAI_AC97,
+ .probe = pxa2xx_ac97_probe,
+ .remove = pxa2xx_ac97_remove,
+ .suspend = pxa2xx_ac97_suspend,
+ .resume = pxa2xx_ac97_resume,
+ .playback = {
+ .stream_name = "AC97 Playback",
+ .channels_min = 2,
+ .channels_max = 2,
+ .rates = PXA2XX_AC97_RATES,
+ .formats = SNDRV_PCM_FMTBIT_S16_LE,},
+ .capture = {
+ .stream_name = "AC97 Capture",
+ .channels_min = 2,
+ .channels_max = 2,
+ .rates = PXA2XX_AC97_RATES,
+ .formats = SNDRV_PCM_FMTBIT_S16_LE,},
+ .ops = {
+ .hw_params = pxa2xx_ac97_hw_params,},
+},
+{
+ .name = "pxa2xx-ac97-aux",
+ .id = 1,
+ .type = SND_SOC_DAI_AC97,
+ .playback = {
+ .stream_name = "AC97 Aux Playback",
+ .channels_min = 1,
+ .channels_max = 1,
+ .rates = PXA2XX_AC97_RATES,
+ .formats = SNDRV_PCM_FMTBIT_S16_LE,},
+ .capture = {
+ .stream_name = "AC97 Aux Capture",
+ .channels_min = 1,
+ .channels_max = 1,
+ .rates = PXA2XX_AC97_RATES,
+ .formats = SNDRV_PCM_FMTBIT_S16_LE,},
+ .ops = {
+ .hw_params = pxa2xx_ac97_hw_aux_params,},
+},
+{
+ .name = "pxa2xx-ac97-mic",
+ .id = 2,
+ .type = SND_SOC_DAI_AC97,
+ .capture = {
+ .stream_name = "AC97 Mic Capture",
+ .channels_min = 1,
+ .channels_max = 1,
+ .rates = PXA2XX_AC97_RATES,
+ .formats = SNDRV_PCM_FMTBIT_S16_LE,},
+ .ops = {
+ .hw_params = pxa2xx_ac97_hw_mic_params,},
+},
+};
+
+EXPORT_SYMBOL_GPL(pxa_ac97_dai);
+EXPORT_SYMBOL_GPL(soc_ac97_ops);
+
+MODULE_AUTHOR("Nicolas Pitre");
+MODULE_DESCRIPTION("AC97 driver for the Intel PXA2xx chip");
+MODULE_LICENSE("GPL");
diff --git a/sound/soc/pxa/pxa2xx-ac97.h b/sound/soc/pxa/pxa2xx-ac97.h
new file mode 100644
index 000000000000..4c4b882316ac
--- /dev/null
+++ b/sound/soc/pxa/pxa2xx-ac97.h
@@ -0,0 +1,22 @@
+/*
+ * linux/sound/arm/pxa2xx-ac97.h
+ *
+ * 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 _PXA2XX_AC97_H
+#define _PXA2XX_AC97_H
+
+/* pxa2xx DAI ID's */
+#define PXA2XX_DAI_AC97_HIFI 0
+#define PXA2XX_DAI_AC97_AUX 1
+#define PXA2XX_DAI_AC97_MIC 2
+
+extern struct snd_soc_cpu_dai pxa_ac97_dai[3];
+
+/* platform data */
+extern struct snd_ac97_bus_ops pxa2xx_ac97_ops;
+
+#endif
diff --git a/sound/soc/pxa/pxa2xx-i2s.c b/sound/soc/pxa/pxa2xx-i2s.c
new file mode 100644
index 000000000000..575a6137c040
--- /dev/null
+++ b/sound/soc/pxa/pxa2xx-i2s.c
@@ -0,0 +1,318 @@
+/*
+ * pxa2xx-i2s.c -- ALSA Soc Audio Layer
+ *
+ * Copyright 2005 Wolfson Microelectronics PLC.
+ * Author: Liam Girdwood
+ * liam.girdwood@wolfsonmicro.com or linux@wolfsonmicro.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.
+ *
+ * Revision history
+ * 12th Aug 2005 Initial version.
+ */
+
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/device.h>
+#include <linux/delay.h>
+#include <sound/driver.h>
+#include <sound/core.h>
+#include <sound/pcm.h>
+#include <sound/initval.h>
+#include <sound/soc.h>
+
+#include <asm/hardware.h>
+#include <asm/arch/pxa-regs.h>
+#include <asm/arch/audio.h>
+
+#include "pxa2xx-pcm.h"
+#include "pxa2xx-i2s.h"
+
+struct pxa_i2s_port {
+ u32 sadiv;
+ u32 sacr0;
+ u32 sacr1;
+ u32 saimr;
+ int master;
+ u32 fmt;
+};
+static struct pxa_i2s_port pxa_i2s;
+
+static struct pxa2xx_pcm_dma_params pxa2xx_i2s_pcm_stereo_out = {
+ .name = "I2S PCM Stereo out",
+ .dev_addr = __PREG(SADR),
+ .drcmr = &DRCMRTXSADR,
+ .dcmd = DCMD_INCSRCADDR | DCMD_FLOWTRG |
+ DCMD_BURST32 | DCMD_WIDTH4,
+};
+
+static struct pxa2xx_pcm_dma_params pxa2xx_i2s_pcm_stereo_in = {
+ .name = "I2S PCM Stereo in",
+ .dev_addr = __PREG(SADR),
+ .drcmr = &DRCMRRXSADR,
+ .dcmd = DCMD_INCTRGADDR | DCMD_FLOWSRC |
+ DCMD_BURST32 | DCMD_WIDTH4,
+};
+
+static struct pxa2xx_gpio gpio_bus[] = {
+ { /* I2S SoC Slave */
+ .rx = GPIO29_SDATA_IN_I2S_MD,
+ .tx = GPIO30_SDATA_OUT_I2S_MD,
+ .clk = GPIO28_BITCLK_IN_I2S_MD,
+ .frm = GPIO31_SYNC_I2S_MD,
+ },
+ { /* I2S SoC Master */
+#ifdef CONFIG_PXA27x
+ .sys = GPIO113_I2S_SYSCLK_MD,
+#else
+ .sys = GPIO32_SYSCLK_I2S_MD,
+#endif
+ .rx = GPIO29_SDATA_IN_I2S_MD,
+ .tx = GPIO30_SDATA_OUT_I2S_MD,
+ .clk = GPIO28_BITCLK_OUT_I2S_MD,
+ .frm = GPIO31_SYNC_I2S_MD,
+ },
+};
+
+static int pxa2xx_i2s_startup(struct snd_pcm_substream *substream)
+{
+ struct snd_soc_pcm_runtime *rtd = substream->private_data;
+ struct snd_soc_cpu_dai *cpu_dai = rtd->dai->cpu_dai;
+
+ if (!cpu_dai->active) {
+ SACR0 |= SACR0_RST;
+ SACR0 = 0;
+ }
+
+ return 0;
+}
+
+/* wait for I2S controller to be ready */
+static int pxa_i2s_wait(void)
+{
+ int i;
+
+ /* flush the Rx FIFO */
+ for(i = 0; i < 16; i++)
+ SADR;
+ return 0;
+}
+
+static int pxa2xx_i2s_set_dai_fmt(struct snd_soc_cpu_dai *cpu_dai,
+ unsigned int fmt)
+{
+ /* interface format */
+ switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
+ case SND_SOC_DAIFMT_I2S:
+ pxa_i2s.fmt = 0;
+ break;
+ case SND_SOC_DAIFMT_LEFT_J:
+ pxa_i2s.fmt = SACR1_AMSL;
+ break;
+ }
+
+ switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) {
+ case SND_SOC_DAIFMT_CBS_CFS:
+ pxa_i2s.master = 1;
+ break;
+ case SND_SOC_DAIFMT_CBM_CFS:
+ pxa_i2s.master = 0;
+ break;
+ default:
+ break;
+ }
+ return 0;
+}
+
+static int pxa2xx_i2s_set_dai_sysclk(struct snd_soc_cpu_dai *cpu_dai,
+ int clk_id, unsigned int freq, int dir)
+{
+ if (clk_id != PXA2XX_I2S_SYSCLK)
+ return -ENODEV;
+
+ if (pxa_i2s.master && dir == SND_SOC_CLOCK_OUT)
+ pxa_gpio_mode(gpio_bus[pxa_i2s.master].sys);
+
+ return 0;
+}
+
+static int pxa2xx_i2s_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params)
+{
+ struct snd_soc_pcm_runtime *rtd = substream->private_data;
+ struct snd_soc_cpu_dai *cpu_dai = rtd->dai->cpu_dai;
+
+ pxa_gpio_mode(gpio_bus[pxa_i2s.master].rx);
+ pxa_gpio_mode(gpio_bus[pxa_i2s.master].tx);
+ pxa_gpio_mode(gpio_bus[pxa_i2s.master].frm);
+ pxa_gpio_mode(gpio_bus[pxa_i2s.master].clk);
+ pxa_set_cken(CKEN8_I2S, 1);
+ pxa_i2s_wait();
+
+ if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
+ cpu_dai->dma_data = &pxa2xx_i2s_pcm_stereo_out;
+ else
+ cpu_dai->dma_data = &pxa2xx_i2s_pcm_stereo_in;
+
+ /* is port used by another stream */
+ if (!(SACR0 & SACR0_ENB)) {
+
+ SACR0 = 0;
+ SACR1 = 0;
+ if (pxa_i2s.master)
+ SACR0 |= SACR0_BCKD;
+
+ SACR0 |= SACR0_RFTH(14) | SACR0_TFTH(1);
+ SACR1 |= pxa_i2s.fmt;
+ }
+ if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
+ SAIMR |= SAIMR_TFS;
+ else
+ SAIMR |= SAIMR_RFS;
+
+ switch (params_rate(params)) {
+ case 8000:
+ SADIV = 0x48;
+ break;
+ case 11025:
+ SADIV = 0x34;
+ break;
+ case 16000:
+ SADIV = 0x24;
+ break;
+ case 22050:
+ SADIV = 0x1a;
+ break;
+ case 44100:
+ SADIV = 0xd;
+ break;
+ case 48000:
+ SADIV = 0xc;
+ break;
+ case 96000: /* not in manual and possibly slightly inaccurate */
+ SADIV = 0x6;
+ break;
+ }
+
+ return 0;
+}
+
+static int pxa2xx_i2s_trigger(struct snd_pcm_substream *substream, int cmd)
+{
+ int ret = 0;
+
+ switch (cmd) {
+ case SNDRV_PCM_TRIGGER_START:
+ SACR0 |= SACR0_ENB;
+ break;
+ case SNDRV_PCM_TRIGGER_RESUME:
+ case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
+ case SNDRV_PCM_TRIGGER_STOP:
+ case SNDRV_PCM_TRIGGER_SUSPEND:
+ case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
+ break;
+ default:
+ ret = -EINVAL;
+ }
+
+ return ret;
+}
+
+static void pxa2xx_i2s_shutdown(struct snd_pcm_substream *substream)
+{
+ if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
+ SACR1 |= SACR1_DRPL;
+ SAIMR &= ~SAIMR_TFS;
+ } else {
+ SACR1 |= SACR1_DREC;
+ SAIMR &= ~SAIMR_RFS;
+ }
+
+ if (SACR1 & (SACR1_DREC | SACR1_DRPL)) {
+ SACR0 &= ~SACR0_ENB;
+ pxa_i2s_wait();
+ pxa_set_cken(CKEN8_I2S, 0);
+ }
+}
+
+#ifdef CONFIG_PM
+static int pxa2xx_i2s_suspend(struct platform_device *dev,
+ struct snd_soc_cpu_dai *dai)
+{
+ if (!dai->active)
+ return 0;
+
+ /* store registers */
+ pxa_i2s.sacr0 = SACR0;
+ pxa_i2s.sacr1 = SACR1;
+ pxa_i2s.saimr = SAIMR;
+ pxa_i2s.sadiv = SADIV;
+
+ /* deactivate link */
+ SACR0 &= ~SACR0_ENB;
+ pxa_i2s_wait();
+ return 0;
+}
+
+static int pxa2xx_i2s_resume(struct platform_device *pdev,
+ struct snd_soc_cpu_dai *dai)
+{
+ if (!dai->active)
+ return 0;
+
+ pxa_i2s_wait();
+
+ SACR0 = pxa_i2s.sacr0 &= ~SACR0_ENB;
+ SACR1 = pxa_i2s.sacr1;
+ SAIMR = pxa_i2s.saimr;
+ SADIV = pxa_i2s.sadiv;
+ SACR0 |= SACR0_ENB;
+
+ return 0;
+}
+
+#else
+#define pxa2xx_i2s_suspend NULL
+#define pxa2xx_i2s_resume NULL
+#endif
+
+#define PXA2XX_I2S_RATES (SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_11025 |\
+ SNDRV_PCM_RATE_16000 | SNDRV_PCM_RATE_22050 | SNDRV_PCM_RATE_44100 | \
+ SNDRV_PCM_RATE_48000 | SNDRV_PCM_RATE_96000)
+
+struct snd_soc_cpu_dai pxa_i2s_dai = {
+ .name = "pxa2xx-i2s",
+ .id = 0,
+ .type = SND_SOC_DAI_I2S,
+ .suspend = pxa2xx_i2s_suspend,
+ .resume = pxa2xx_i2s_resume,
+ .playback = {
+ .channels_min = 2,
+ .channels_max = 2,
+ .rates = PXA2XX_I2S_RATES,
+ .formats = SNDRV_PCM_FMTBIT_S16_LE,},
+ .capture = {
+ .channels_min = 2,
+ .channels_max = 2,
+ .rates = PXA2XX_I2S_RATES,
+ .formats = SNDRV_PCM_FMTBIT_S16_LE,},
+ .ops = {
+ .startup = pxa2xx_i2s_startup,
+ .shutdown = pxa2xx_i2s_shutdown,
+ .trigger = pxa2xx_i2s_trigger,
+ .hw_params = pxa2xx_i2s_hw_params,},
+ .dai_ops = {
+ .set_fmt = pxa2xx_i2s_set_dai_fmt,
+ .set_sysclk = pxa2xx_i2s_set_dai_sysclk,
+ },
+};
+
+EXPORT_SYMBOL_GPL(pxa_i2s_dai);
+
+/* Module information */
+MODULE_AUTHOR("Liam Girdwood, liam.girdwood@wolfsonmicro.com, www.wolfsonmicro.com");
+MODULE_DESCRIPTION("pxa2xx I2S SoC Interface");
+MODULE_LICENSE("GPL");
diff --git a/sound/soc/pxa/pxa2xx-i2s.h b/sound/soc/pxa/pxa2xx-i2s.h
new file mode 100644
index 000000000000..a2484f0881f1
--- /dev/null
+++ b/sound/soc/pxa/pxa2xx-i2s.h
@@ -0,0 +1,20 @@
+/*
+ * linux/sound/arm/pxa2xx-i2s.h
+ *
+ * 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 _PXA2XX_I2S_H
+#define _PXA2XX_I2S_H
+
+/* pxa2xx DAI ID's */
+#define PXA2XX_DAI_I2S 0
+
+/* I2S clock */
+#define PXA2XX_I2S_SYSCLK 0
+
+extern struct snd_soc_cpu_dai pxa_i2s_dai;
+
+#endif
diff --git a/sound/soc/pxa/pxa2xx-pcm.c b/sound/soc/pxa/pxa2xx-pcm.c
new file mode 100644
index 000000000000..35e8fa3a469c
--- /dev/null
+++ b/sound/soc/pxa/pxa2xx-pcm.c
@@ -0,0 +1,372 @@
+/*
+ * linux/sound/arm/pxa2xx-pcm.c -- ALSA PCM interface for the Intel PXA2xx chip
+ *
+ * Author: Nicolas Pitre
+ * Created: Nov 30, 2004
+ * Copyright: (C) 2004 MontaVista Software, Inc.
+ *
+ * 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.
+ */
+
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/platform_device.h>
+#include <linux/slab.h>
+#include <linux/dma-mapping.h>
+
+#include <sound/driver.h>
+#include <sound/core.h>
+#include <sound/pcm.h>
+#include <sound/pcm_params.h>
+#include <sound/soc.h>
+
+#include <asm/dma.h>
+#include <asm/hardware.h>
+#include <asm/arch/pxa-regs.h>
+#include <asm/arch/audio.h>
+
+#include "pxa2xx-pcm.h"
+
+static const struct snd_pcm_hardware pxa2xx_pcm_hardware = {
+ .info = SNDRV_PCM_INFO_MMAP |
+ SNDRV_PCM_INFO_MMAP_VALID |
+ SNDRV_PCM_INFO_INTERLEAVED |
+ SNDRV_PCM_INFO_PAUSE |
+ SNDRV_PCM_INFO_RESUME,
+ .formats = SNDRV_PCM_FMTBIT_S16_LE |
+ SNDRV_PCM_FMTBIT_S24_LE |
+ SNDRV_PCM_FMTBIT_S32_LE,
+ .period_bytes_min = 32,
+ .period_bytes_max = 8192 - 32,
+ .periods_min = 1,
+ .periods_max = PAGE_SIZE/sizeof(pxa_dma_desc),
+ .buffer_bytes_max = 128 * 1024,
+ .fifo_size = 32,
+};
+
+struct pxa2xx_runtime_data {
+ int dma_ch;
+ struct pxa2xx_pcm_dma_params *params;
+ pxa_dma_desc *dma_desc_array;
+ dma_addr_t dma_desc_array_phys;
+};
+
+static void pxa2xx_pcm_dma_irq(int dma_ch, void *dev_id)
+{
+ struct snd_pcm_substream *substream = dev_id;
+ struct pxa2xx_runtime_data *prtd = substream->runtime->private_data;
+ int dcsr;
+
+ dcsr = DCSR(dma_ch);
+ DCSR(dma_ch) = dcsr & ~DCSR_STOPIRQEN;
+
+ if (dcsr & DCSR_ENDINTR) {
+ snd_pcm_period_elapsed(substream);
+ } else {
+ printk( KERN_ERR "%s: DMA error on channel %d (DCSR=%#x)\n",
+ prtd->params->name, dma_ch, dcsr );
+ }
+}
+
+static int pxa2xx_pcm_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params)
+{
+ struct snd_pcm_runtime *runtime = substream->runtime;
+ struct pxa2xx_runtime_data *prtd = runtime->private_data;
+ struct snd_soc_pcm_runtime *rtd = substream->private_data;
+ struct pxa2xx_pcm_dma_params *dma = rtd->dai->cpu_dai->dma_data;
+ size_t totsize = params_buffer_bytes(params);
+ size_t period = params_period_bytes(params);
+ pxa_dma_desc *dma_desc;
+ dma_addr_t dma_buff_phys, next_desc_phys;
+ int ret;
+
+ /* return if this is a bufferless transfer e.g.
+ * codec <--> BT codec or GSM modem -- lg FIXME */
+ if (!dma)
+ return 0;
+
+ /* this may get called several times by oss emulation
+ * with different params */
+ if (prtd->params == NULL) {
+ prtd->params = dma;
+ ret = pxa_request_dma(prtd->params->name, DMA_PRIO_LOW,
+ pxa2xx_pcm_dma_irq, substream);
+ if (ret < 0)
+ return ret;
+ prtd->dma_ch = ret;
+ } else if (prtd->params != dma) {
+ pxa_free_dma(prtd->dma_ch);
+ prtd->params = dma;
+ ret = pxa_request_dma(prtd->params->name, DMA_PRIO_LOW,
+ pxa2xx_pcm_dma_irq, substream);
+ if (ret < 0)
+ return ret;
+ prtd->dma_ch = ret;
+ }
+
+ snd_pcm_set_runtime_buffer(substream, &substream->dma_buffer);
+ runtime->dma_bytes = totsize;
+
+ dma_desc = prtd->dma_desc_array;
+ next_desc_phys = prtd->dma_desc_array_phys;
+ dma_buff_phys = runtime->dma_addr;
+ do {
+ next_desc_phys += sizeof(pxa_dma_desc);
+ dma_desc->ddadr = next_desc_phys;
+ if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
+ dma_desc->dsadr = dma_buff_phys;
+ dma_desc->dtadr = prtd->params->dev_addr;
+ } else {
+ dma_desc->dsadr = prtd->params->dev_addr;
+ dma_desc->dtadr = dma_buff_phys;
+ }
+ if (period > totsize)
+ period = totsize;
+ dma_desc->dcmd = prtd->params->dcmd | period | DCMD_ENDIRQEN;
+ dma_desc++;
+ dma_buff_phys += period;
+ } while (totsize -= period);
+ dma_desc[-1].ddadr = prtd->dma_desc_array_phys;
+
+ return 0;
+}
+
+static int pxa2xx_pcm_hw_free(struct snd_pcm_substream *substream)
+{
+ struct pxa2xx_runtime_data *prtd = substream->runtime->private_data;
+
+ if (prtd && prtd->params)
+ *prtd->params->drcmr = 0;
+
+ if (prtd->dma_ch) {
+ snd_pcm_set_runtime_buffer(substream, NULL);
+ pxa_free_dma(prtd->dma_ch);
+ prtd->dma_ch = 0;
+ }
+
+ return 0;
+}
+
+static int pxa2xx_pcm_prepare(struct snd_pcm_substream *substream)
+{
+ struct pxa2xx_runtime_data *prtd = substream->runtime->private_data;
+
+ DCSR(prtd->dma_ch) &= ~DCSR_RUN;
+ DCSR(prtd->dma_ch) = 0;
+ DCMD(prtd->dma_ch) = 0;
+ *prtd->params->drcmr = prtd->dma_ch | DRCMR_MAPVLD;
+
+ return 0;
+}
+
+static int pxa2xx_pcm_trigger(struct snd_pcm_substream *substream, int cmd)
+{
+ struct pxa2xx_runtime_data *prtd = substream->runtime->private_data;
+ int ret = 0;
+
+ switch (cmd) {
+ case SNDRV_PCM_TRIGGER_START:
+ DDADR(prtd->dma_ch) = prtd->dma_desc_array_phys;
+ DCSR(prtd->dma_ch) = DCSR_RUN;
+ break;
+
+ case SNDRV_PCM_TRIGGER_STOP:
+ case SNDRV_PCM_TRIGGER_SUSPEND:
+ case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
+ DCSR(prtd->dma_ch) &= ~DCSR_RUN;
+ break;
+
+ case SNDRV_PCM_TRIGGER_RESUME:
+ DCSR(prtd->dma_ch) |= DCSR_RUN;
+ break;
+ case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
+ DDADR(prtd->dma_ch) = prtd->dma_desc_array_phys;
+ DCSR(prtd->dma_ch) |= DCSR_RUN;
+ break;
+
+ default:
+ ret = -EINVAL;
+ }
+
+ return ret;
+}
+
+static snd_pcm_uframes_t
+pxa2xx_pcm_pointer(struct snd_pcm_substream *substream)
+{
+ struct snd_pcm_runtime *runtime = substream->runtime;
+ struct pxa2xx_runtime_data *prtd = runtime->private_data;
+
+ dma_addr_t ptr = (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) ?
+ DSADR(prtd->dma_ch) : DTADR(prtd->dma_ch);
+ snd_pcm_uframes_t x = bytes_to_frames(runtime, ptr - runtime->dma_addr);
+
+ if (x == runtime->buffer_size)
+ x = 0;
+ return x;
+}
+
+static int pxa2xx_pcm_open(struct snd_pcm_substream *substream)
+{
+ struct snd_pcm_runtime *runtime = substream->runtime;
+ struct pxa2xx_runtime_data *prtd;
+ int ret;
+
+ snd_soc_set_runtime_hwparams(substream, &pxa2xx_pcm_hardware);
+
+ /*
+ * For mysterious reasons (and despite what the manual says)
+ * playback samples are lost if the DMA count is not a multiple
+ * of the DMA burst size. Let's add a rule to enforce that.
+ */
+ ret = snd_pcm_hw_constraint_step(runtime, 0,
+ SNDRV_PCM_HW_PARAM_PERIOD_BYTES, 32);
+ if (ret)
+ goto out;
+
+ ret = snd_pcm_hw_constraint_step(runtime, 0,
+ SNDRV_PCM_HW_PARAM_BUFFER_BYTES, 32);
+ if (ret)
+ goto out;
+
+ ret = snd_pcm_hw_constraint_integer(runtime, SNDRV_PCM_HW_PARAM_PERIODS);
+ if (ret < 0)
+ goto out;
+
+ prtd = kzalloc(sizeof(struct pxa2xx_runtime_data), GFP_KERNEL);
+ if (prtd == NULL) {
+ ret = -ENOMEM;
+ goto out;
+ }
+
+ prtd->dma_desc_array =
+ dma_alloc_writecombine(substream->pcm->card->dev, PAGE_SIZE,
+ &prtd->dma_desc_array_phys, GFP_KERNEL);
+ if (!prtd->dma_desc_array) {
+ ret = -ENOMEM;
+ goto err1;
+ }
+
+ runtime->private_data = prtd;
+ return 0;
+
+ err1:
+ kfree(prtd);
+ out:
+ return ret;
+}
+
+static int pxa2xx_pcm_close(struct snd_pcm_substream *substream)
+{
+ struct snd_pcm_runtime *runtime = substream->runtime;
+ struct pxa2xx_runtime_data *prtd = runtime->private_data;
+
+ dma_free_writecombine(substream->pcm->card->dev, PAGE_SIZE,
+ prtd->dma_desc_array, prtd->dma_desc_array_phys);
+ kfree(prtd);
+ return 0;
+}
+
+static int pxa2xx_pcm_mmap(struct snd_pcm_substream *substream,
+ struct vm_area_struct *vma)
+{
+ struct snd_pcm_runtime *runtime = substream->runtime;
+ return dma_mmap_writecombine(substream->pcm->card->dev, vma,
+ runtime->dma_area,
+ runtime->dma_addr,
+ runtime->dma_bytes);
+}
+
+struct snd_pcm_ops pxa2xx_pcm_ops = {
+ .open = pxa2xx_pcm_open,
+ .close = pxa2xx_pcm_close,
+ .ioctl = snd_pcm_lib_ioctl,
+ .hw_params = pxa2xx_pcm_hw_params,
+ .hw_free = pxa2xx_pcm_hw_free,
+ .prepare = pxa2xx_pcm_prepare,
+ .trigger = pxa2xx_pcm_trigger,
+ .pointer = pxa2xx_pcm_pointer,
+ .mmap = pxa2xx_pcm_mmap,
+};
+
+static int pxa2xx_pcm_preallocate_dma_buffer(struct snd_pcm *pcm, int stream)
+{
+ struct snd_pcm_substream *substream = pcm->streams[stream].substream;
+ struct snd_dma_buffer *buf = &substream->dma_buffer;
+ size_t size = pxa2xx_pcm_hardware.buffer_bytes_max;
+ buf->dev.type = SNDRV_DMA_TYPE_DEV;
+ buf->dev.dev = pcm->card->dev;
+ buf->private_data = NULL;
+ buf->area = dma_alloc_writecombine(pcm->card->dev, size,
+ &buf->addr, GFP_KERNEL);
+ if (!buf->area)
+ return -ENOMEM;
+ buf->bytes = size;
+ return 0;
+}
+
+static void pxa2xx_pcm_free_dma_buffers(struct snd_pcm *pcm)
+{
+ struct snd_pcm_substream *substream;
+ struct snd_dma_buffer *buf;
+ int stream;
+
+ for (stream = 0; stream < 2; stream++) {
+ substream = pcm->streams[stream].substream;
+ if (!substream)
+ continue;
+
+ buf = &substream->dma_buffer;
+ if (!buf->area)
+ continue;
+
+ dma_free_writecombine(pcm->card->dev, buf->bytes,
+ buf->area, buf->addr);
+ buf->area = NULL;
+ }
+}
+
+static u64 pxa2xx_pcm_dmamask = DMA_32BIT_MASK;
+
+int pxa2xx_pcm_new(struct snd_card *card, struct snd_soc_codec_dai *dai,
+ struct snd_pcm *pcm)
+{
+ int ret = 0;
+
+ if (!card->dev->dma_mask)
+ card->dev->dma_mask = &pxa2xx_pcm_dmamask;
+ if (!card->dev->coherent_dma_mask)
+ card->dev->coherent_dma_mask = DMA_32BIT_MASK;
+
+ if (dai->playback.channels_min) {
+ ret = pxa2xx_pcm_preallocate_dma_buffer(pcm,
+ SNDRV_PCM_STREAM_PLAYBACK);
+ if (ret)
+ goto out;
+ }
+
+ if (dai->capture.channels_min) {
+ ret = pxa2xx_pcm_preallocate_dma_buffer(pcm,
+ SNDRV_PCM_STREAM_CAPTURE);
+ if (ret)
+ goto out;
+ }
+ out:
+ return ret;
+}
+
+struct snd_soc_platform pxa2xx_soc_platform = {
+ .name = "pxa2xx-audio",
+ .pcm_ops = &pxa2xx_pcm_ops,
+ .pcm_new = pxa2xx_pcm_new,
+ .pcm_free = pxa2xx_pcm_free_dma_buffers,
+};
+
+EXPORT_SYMBOL_GPL(pxa2xx_soc_platform);
+
+MODULE_AUTHOR("Nicolas Pitre");
+MODULE_DESCRIPTION("Intel PXA2xx PCM DMA module");
+MODULE_LICENSE("GPL");
diff --git a/sound/soc/pxa/pxa2xx-pcm.h b/sound/soc/pxa/pxa2xx-pcm.h
new file mode 100644
index 000000000000..54c9c755e508
--- /dev/null
+++ b/sound/soc/pxa/pxa2xx-pcm.h
@@ -0,0 +1,34 @@
+/*
+ * linux/sound/arm/pxa2xx-pcm.h -- ALSA PCM interface for the Intel PXA2xx chip
+ *
+ * Author: Nicolas Pitre
+ * Created: Nov 30, 2004
+ * Copyright: MontaVista Software, Inc.
+ *
+ * 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 _PXA2XX_PCM_H
+#define _PXA2XX_PCM_H
+
+struct pxa2xx_pcm_dma_params {
+ char *name; /* stream identifier */
+ u32 dcmd; /* DMA descriptor dcmd field */
+ volatile u32 *drcmr; /* the DMA request channel to use */
+ u32 dev_addr; /* device physical address for DMA */
+};
+
+struct pxa2xx_gpio {
+ u32 sys;
+ u32 rx;
+ u32 tx;
+ u32 clk;
+ u32 frm;
+};
+
+/* platform data */
+extern struct snd_soc_platform pxa2xx_soc_platform;
+
+#endif
diff --git a/sound/soc/pxa/spitz.c b/sound/soc/pxa/spitz.c
new file mode 100644
index 000000000000..80e82109fef7
--- /dev/null
+++ b/sound/soc/pxa/spitz.c
@@ -0,0 +1,394 @@
+/*
+ * spitz.c -- SoC audio for Sharp SL-Cxx00 models Spitz, Borzoi and Akita
+ *
+ * Copyright 2005 Wolfson Microelectronics PLC.
+ * Copyright 2005 Openedhand Ltd.
+ *
+ * Authors: Liam Girdwood <liam.girdwood@wolfsonmicro.com>
+ * Richard Purdie <richard@openedhand.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.
+ *
+ * Revision history
+ * 30th Nov 2005 Initial version.
+ *
+ */
+
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/timer.h>
+#include <linux/interrupt.h>
+#include <linux/platform_device.h>
+#include <sound/driver.h>
+#include <sound/core.h>
+#include <sound/pcm.h>
+#include <sound/soc.h>
+#include <sound/soc-dapm.h>
+
+#include <asm/mach-types.h>
+#include <asm/hardware/scoop.h>
+#include <asm/arch/pxa-regs.h>
+#include <asm/arch/hardware.h>
+#include <asm/arch/akita.h>
+#include <asm/arch/spitz.h>
+#include <asm/mach-types.h>
+#include "../codecs/wm8750.h"
+#include "pxa2xx-pcm.h"
+#include "pxa2xx-i2s.h"
+
+#define SPITZ_HP 0
+#define SPITZ_MIC 1
+#define SPITZ_LINE 2
+#define SPITZ_HEADSET 3
+#define SPITZ_HP_OFF 4
+#define SPITZ_SPK_ON 0
+#define SPITZ_SPK_OFF 1
+
+ /* audio clock in Hz - rounded from 12.235MHz */
+#define SPITZ_AUDIO_CLOCK 12288000
+
+static int spitz_jack_func;
+static int spitz_spk_func;
+
+static void spitz_ext_control(struct snd_soc_codec *codec)
+{
+ if (spitz_spk_func == SPITZ_SPK_ON)
+ snd_soc_dapm_set_endpoint(codec, "Ext Spk", 1);
+ else
+ snd_soc_dapm_set_endpoint(codec, "Ext Spk", 0);
+
+ /* set up jack connection */
+ switch (spitz_jack_func) {
+ case SPITZ_HP:
+ /* enable and unmute hp jack, disable mic bias */
+ snd_soc_dapm_set_endpoint(codec, "Headset Jack", 0);
+ snd_soc_dapm_set_endpoint(codec, "Mic Jack", 0);
+ snd_soc_dapm_set_endpoint(codec, "Line Jack", 0);
+ snd_soc_dapm_set_endpoint(codec, "Headphone Jack", 1);
+ set_scoop_gpio(&spitzscoop_device.dev, SPITZ_SCP_MUTE_L);
+ set_scoop_gpio(&spitzscoop_device.dev, SPITZ_SCP_MUTE_R);
+ break;
+ case SPITZ_MIC:
+ /* enable mic jack and bias, mute hp */
+ snd_soc_dapm_set_endpoint(codec, "Headphone Jack", 0);
+ snd_soc_dapm_set_endpoint(codec, "Headset Jack", 0);
+ snd_soc_dapm_set_endpoint(codec, "Line Jack", 0);
+ snd_soc_dapm_set_endpoint(codec, "Mic Jack", 1);
+ reset_scoop_gpio(&spitzscoop_device.dev, SPITZ_SCP_MUTE_L);
+ reset_scoop_gpio(&spitzscoop_device.dev, SPITZ_SCP_MUTE_R);
+ break;
+ case SPITZ_LINE:
+ /* enable line jack, disable mic bias and mute hp */
+ snd_soc_dapm_set_endpoint(codec, "Headphone Jack", 0);
+ snd_soc_dapm_set_endpoint(codec, "Headset Jack", 0);
+ snd_soc_dapm_set_endpoint(codec, "Mic Jack", 0);
+ snd_soc_dapm_set_endpoint(codec, "Line Jack", 1);
+ reset_scoop_gpio(&spitzscoop_device.dev, SPITZ_SCP_MUTE_L);
+ reset_scoop_gpio(&spitzscoop_device.dev, SPITZ_SCP_MUTE_R);
+ break;
+ case SPITZ_HEADSET:
+ /* enable and unmute headset jack enable mic bias, mute L hp */
+ snd_soc_dapm_set_endpoint(codec, "Headphone Jack", 0);
+ snd_soc_dapm_set_endpoint(codec, "Mic Jack", 1);
+ snd_soc_dapm_set_endpoint(codec, "Line Jack", 0);
+ snd_soc_dapm_set_endpoint(codec, "Headset Jack", 1);
+ reset_scoop_gpio(&spitzscoop_device.dev, SPITZ_SCP_MUTE_L);
+ set_scoop_gpio(&spitzscoop_device.dev, SPITZ_SCP_MUTE_R);
+ break;
+ case SPITZ_HP_OFF:
+
+ /* jack removed, everything off */
+ snd_soc_dapm_set_endpoint(codec, "Headphone Jack", 0);
+ snd_soc_dapm_set_endpoint(codec, "Headset Jack", 0);
+ snd_soc_dapm_set_endpoint(codec, "Mic Jack", 0);
+ snd_soc_dapm_set_endpoint(codec, "Line Jack", 0);
+ reset_scoop_gpio(&spitzscoop_device.dev, SPITZ_SCP_MUTE_L);
+ reset_scoop_gpio(&spitzscoop_device.dev, SPITZ_SCP_MUTE_R);
+ break;
+ }
+ snd_soc_dapm_sync_endpoints(codec);
+}
+
+static int spitz_startup(struct snd_pcm_substream *substream)
+{
+ struct snd_soc_pcm_runtime *rtd = substream->private_data;
+ struct snd_soc_codec *codec = rtd->socdev->codec;
+
+ /* check the jack status at stream startup */
+ spitz_ext_control(codec);
+ return 0;
+}
+
+static int spitz_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params)
+{
+ struct snd_soc_pcm_runtime *rtd = substream->private_data;
+ struct snd_soc_codec_dai *codec_dai = rtd->dai->codec_dai;
+ struct snd_soc_cpu_dai *cpu_dai = rtd->dai->cpu_dai;
+ unsigned int clk = 0;
+ int ret = 0;
+
+ switch (params_rate(params)) {
+ case 8000:
+ case 16000:
+ case 48000:
+ case 96000:
+ clk = 12288000;
+ break;
+ case 11025:
+ case 22050:
+ case 44100:
+ clk = 11289600;
+ break;
+ }
+
+ /* set codec DAI configuration */
+ ret = codec_dai->dai_ops.set_fmt(codec_dai, SND_SOC_DAIFMT_I2S |
+ SND_SOC_DAIFMT_NB_NF | SND_SOC_DAIFMT_CBS_CFS);
+ if (ret < 0)
+ return ret;
+
+ /* set cpu DAI configuration */
+ ret = cpu_dai->dai_ops.set_fmt(cpu_dai, SND_SOC_DAIFMT_I2S |
+ SND_SOC_DAIFMT_NB_NF | SND_SOC_DAIFMT_CBS_CFS);
+ if (ret < 0)
+ return ret;
+
+ /* set the codec system clock for DAC and ADC */
+ ret = codec_dai->dai_ops.set_sysclk(codec_dai, WM8750_SYSCLK, clk,
+ SND_SOC_CLOCK_IN);
+ if (ret < 0)
+ return ret;
+
+ /* set the I2S system clock as input (unused) */
+ ret = cpu_dai->dai_ops.set_sysclk(cpu_dai, PXA2XX_I2S_SYSCLK, 0,
+ SND_SOC_CLOCK_IN);
+ if (ret < 0)
+ return ret;
+
+ return 0;
+}
+
+static struct snd_soc_ops spitz_ops = {
+ .startup = spitz_startup,
+ .hw_params = spitz_hw_params,
+};
+
+static int spitz_get_jack(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ ucontrol->value.integer.value[0] = spitz_jack_func;
+ return 0;
+}
+
+static int spitz_set_jack(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol);
+
+ if (spitz_jack_func == ucontrol->value.integer.value[0])
+ return 0;
+
+ spitz_jack_func = ucontrol->value.integer.value[0];
+ spitz_ext_control(codec);
+ return 1;
+}
+
+static int spitz_get_spk(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ ucontrol->value.integer.value[0] = spitz_spk_func;
+ return 0;
+}
+
+static int spitz_set_spk(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol);
+
+ if (spitz_spk_func == ucontrol->value.integer.value[0])
+ return 0;
+
+ spitz_spk_func = ucontrol->value.integer.value[0];
+ spitz_ext_control(codec);
+ return 1;
+}
+
+static int spitz_mic_bias(struct snd_soc_dapm_widget *w, int event)
+{
+ if (machine_is_borzoi() || machine_is_spitz()) {
+ if (SND_SOC_DAPM_EVENT_ON(event))
+ set_scoop_gpio(&spitzscoop2_device.dev,
+ SPITZ_SCP2_MIC_BIAS);
+ else
+ reset_scoop_gpio(&spitzscoop2_device.dev,
+ SPITZ_SCP2_MIC_BIAS);
+ }
+
+ if (machine_is_akita()) {
+ if (SND_SOC_DAPM_EVENT_ON(event))
+ akita_set_ioexp(&akitaioexp_device.dev,
+ AKITA_IOEXP_MIC_BIAS);
+ else
+ akita_reset_ioexp(&akitaioexp_device.dev,
+ AKITA_IOEXP_MIC_BIAS);
+ }
+ return 0;
+}
+
+/* spitz machine dapm widgets */
+static const struct snd_soc_dapm_widget wm8750_dapm_widgets[] = {
+ SND_SOC_DAPM_HP("Headphone Jack", NULL),
+ SND_SOC_DAPM_MIC("Mic Jack", spitz_mic_bias),
+ SND_SOC_DAPM_SPK("Ext Spk", NULL),
+ SND_SOC_DAPM_LINE("Line Jack", NULL),
+
+ /* headset is a mic and mono headphone */
+ SND_SOC_DAPM_HP("Headset Jack", NULL),
+};
+
+/* Spitz machine audio_map */
+static const char *audio_map[][3] = {
+
+ /* headphone connected to LOUT1, ROUT1 */
+ {"Headphone Jack", NULL, "LOUT1"},
+ {"Headphone Jack", NULL, "ROUT1"},
+
+ /* headset connected to ROUT1 and LINPUT1 with bias (def below) */
+ {"Headset Jack", NULL, "ROUT1"},
+
+ /* ext speaker connected to LOUT2, ROUT2 */
+ {"Ext Spk", NULL , "ROUT2"},
+ {"Ext Spk", NULL , "LOUT2"},
+
+ /* mic is connected to input 1 - with bias */
+ {"LINPUT1", NULL, "Mic Bias"},
+ {"Mic Bias", NULL, "Mic Jack"},
+
+ /* line is connected to input 1 - no bias */
+ {"LINPUT1", NULL, "Line Jack"},
+
+ {NULL, NULL, NULL},
+};
+
+static const char *jack_function[] = {"Headphone", "Mic", "Line", "Headset",
+ "Off"};
+static const char *spk_function[] = {"On", "Off"};
+static const struct soc_enum spitz_enum[] = {
+ SOC_ENUM_SINGLE_EXT(5, jack_function),
+ SOC_ENUM_SINGLE_EXT(2, spk_function),
+};
+
+static const struct snd_kcontrol_new wm8750_spitz_controls[] = {
+ SOC_ENUM_EXT("Jack Function", spitz_enum[0], spitz_get_jack,
+ spitz_set_jack),
+ SOC_ENUM_EXT("Speaker Function", spitz_enum[1], spitz_get_spk,
+ spitz_set_spk),
+};
+
+/*
+ * Logic for a wm8750 as connected on a Sharp SL-Cxx00 Device
+ */
+static int spitz_wm8750_init(struct snd_soc_codec *codec)
+{
+ int i, err;
+
+ /* NC codec pins */
+ snd_soc_dapm_set_endpoint(codec, "RINPUT1", 0);
+ snd_soc_dapm_set_endpoint(codec, "LINPUT2", 0);
+ snd_soc_dapm_set_endpoint(codec, "RINPUT2", 0);
+ snd_soc_dapm_set_endpoint(codec, "LINPUT3", 0);
+ snd_soc_dapm_set_endpoint(codec, "RINPUT3", 0);
+ snd_soc_dapm_set_endpoint(codec, "OUT3", 0);
+ snd_soc_dapm_set_endpoint(codec, "MONO", 0);
+
+ /* Add spitz specific controls */
+ for (i = 0; i < ARRAY_SIZE(wm8750_spitz_controls); i++) {
+ err = snd_ctl_add(codec->card,
+ snd_soc_cnew(&wm8750_spitz_controls[i], codec, NULL));
+ if (err < 0)
+ return err;
+ }
+
+ /* Add spitz specific widgets */
+ for (i = 0; i < ARRAY_SIZE(wm8750_dapm_widgets); i++) {
+ snd_soc_dapm_new_control(codec, &wm8750_dapm_widgets[i]);
+ }
+
+ /* Set up spitz specific audio path audio_map */
+ for (i = 0; audio_map[i][0] != NULL; i++) {
+ snd_soc_dapm_connect_input(codec, audio_map[i][0],
+ audio_map[i][1], audio_map[i][2]);
+ }
+
+ snd_soc_dapm_sync_endpoints(codec);
+ return 0;
+}
+
+/* spitz digital audio interface glue - connects codec <--> CPU */
+static struct snd_soc_dai_link spitz_dai = {
+ .name = "wm8750",
+ .stream_name = "WM8750",
+ .cpu_dai = &pxa_i2s_dai,
+ .codec_dai = &wm8750_dai,
+ .init = spitz_wm8750_init,
+ .ops = &spitz_ops,
+};
+
+/* spitz audio machine driver */
+static struct snd_soc_machine snd_soc_machine_spitz = {
+ .name = "Spitz",
+ .dai_link = &spitz_dai,
+ .num_links = 1,
+};
+
+/* spitz audio private data */
+static struct wm8750_setup_data spitz_wm8750_setup = {
+ .i2c_address = 0x1b,
+};
+
+/* spitz audio subsystem */
+static struct snd_soc_device spitz_snd_devdata = {
+ .machine = &snd_soc_machine_spitz,
+ .platform = &pxa2xx_soc_platform,
+ .codec_dev = &soc_codec_dev_wm8750,
+ .codec_data = &spitz_wm8750_setup,
+};
+
+static struct platform_device *spitz_snd_device;
+
+static int __init spitz_init(void)
+{
+ int ret;
+
+ if (!(machine_is_spitz() || machine_is_borzoi() || machine_is_akita()))
+ return -ENODEV;
+
+ spitz_snd_device = platform_device_alloc("soc-audio", -1);
+ if (!spitz_snd_device)
+ return -ENOMEM;
+
+ platform_set_drvdata(spitz_snd_device, &spitz_snd_devdata);
+ spitz_snd_devdata.dev = &spitz_snd_device->dev;
+ ret = platform_device_add(spitz_snd_device);
+
+ if (ret)
+ platform_device_put(spitz_snd_device);
+
+ return ret;
+}
+
+static void __exit spitz_exit(void)
+{
+ platform_device_unregister(spitz_snd_device);
+}
+
+module_init(spitz_init);
+module_exit(spitz_exit);
+
+MODULE_AUTHOR("Richard Purdie");
+MODULE_DESCRIPTION("ALSA SoC Spitz");
+MODULE_LICENSE("GPL");
diff --git a/sound/soc/pxa/tosa.c b/sound/soc/pxa/tosa.c
new file mode 100644
index 000000000000..5504e30acf14
--- /dev/null
+++ b/sound/soc/pxa/tosa.c
@@ -0,0 +1,289 @@
+/*
+ * tosa.c -- SoC audio for Tosa
+ *
+ * Copyright 2005 Wolfson Microelectronics PLC.
+ * Copyright 2005 Openedhand Ltd.
+ *
+ * Authors: Liam Girdwood <liam.girdwood@wolfsonmicro.com>
+ * Richard Purdie <richard@openedhand.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.
+ *
+ * Revision history
+ * 30th Nov 2005 Initial version.
+ *
+ * GPIO's
+ * 1 - Jack Insertion
+ * 5 - Hookswitch (headset answer/hang up switch)
+ *
+ */
+
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/device.h>
+
+#include <sound/driver.h>
+#include <sound/core.h>
+#include <sound/pcm.h>
+#include <sound/soc.h>
+#include <sound/soc-dapm.h>
+
+#include <asm/mach-types.h>
+#include <asm/hardware/tmio.h>
+#include <asm/arch/pxa-regs.h>
+#include <asm/arch/hardware.h>
+#include <asm/arch/audio.h>
+#include <asm/arch/tosa.h>
+
+#include "../codecs/wm9712.h"
+#include "pxa2xx-pcm.h"
+#include "pxa2xx-ac97.h"
+
+static struct snd_soc_machine tosa;
+
+#define TOSA_HP 0
+#define TOSA_MIC_INT 1
+#define TOSA_HEADSET 2
+#define TOSA_HP_OFF 3
+#define TOSA_SPK_ON 0
+#define TOSA_SPK_OFF 1
+
+static int tosa_jack_func;
+static int tosa_spk_func;
+
+static void tosa_ext_control(struct snd_soc_codec *codec)
+{
+ int spk = 0, mic_int = 0, hp = 0, hs = 0;
+
+ /* set up jack connection */
+ switch (tosa_jack_func) {
+ case TOSA_HP:
+ hp = 1;
+ break;
+ case TOSA_MIC_INT:
+ mic_int = 1;
+ break;
+ case TOSA_HEADSET:
+ hs = 1;
+ break;
+ }
+
+ if (tosa_spk_func == TOSA_SPK_ON)
+ spk = 1;
+
+ snd_soc_dapm_set_endpoint(codec, "Speaker", spk);
+ snd_soc_dapm_set_endpoint(codec, "Mic (Internal)", mic_int);
+ snd_soc_dapm_set_endpoint(codec, "Headphone Jack", hp);
+ snd_soc_dapm_set_endpoint(codec, "Headset Jack", hs);
+ snd_soc_dapm_sync_endpoints(codec);
+}
+
+static int tosa_startup(struct snd_pcm_substream *substream)
+{
+ struct snd_soc_pcm_runtime *rtd = substream->private_data;
+ struct snd_soc_codec *codec = rtd->socdev->codec;
+
+ /* check the jack status at stream startup */
+ tosa_ext_control(codec);
+ return 0;
+}
+
+static struct snd_soc_ops tosa_ops = {
+ .startup = tosa_startup,
+};
+
+static int tosa_get_jack(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ ucontrol->value.integer.value[0] = tosa_jack_func;
+ return 0;
+}
+
+static int tosa_set_jack(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol);
+
+ if (tosa_jack_func == ucontrol->value.integer.value[0])
+ return 0;
+
+ tosa_jack_func = ucontrol->value.integer.value[0];
+ tosa_ext_control(codec);
+ return 1;
+}
+
+static int tosa_get_spk(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ ucontrol->value.integer.value[0] = tosa_spk_func;
+ return 0;
+}
+
+static int tosa_set_spk(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol);
+
+ if (tosa_spk_func == ucontrol->value.integer.value[0])
+ return 0;
+
+ tosa_spk_func = ucontrol->value.integer.value[0];
+ tosa_ext_control(codec);
+ return 1;
+}
+
+/* tosa dapm event handlers */
+static int tosa_hp_event(struct snd_soc_dapm_widget *w, int event)
+{
+ if (SND_SOC_DAPM_EVENT_ON(event))
+ set_tc6393_gpio(&tc6393_device.dev,TOSA_TC6393_L_MUTE);
+ else
+ reset_tc6393_gpio(&tc6393_device.dev,TOSA_TC6393_L_MUTE);
+ return 0;
+}
+
+/* tosa machine dapm widgets */
+static const struct snd_soc_dapm_widget tosa_dapm_widgets[] = {
+SND_SOC_DAPM_HP("Headphone Jack", tosa_hp_event),
+SND_SOC_DAPM_HP("Headset Jack", NULL),
+SND_SOC_DAPM_MIC("Mic (Internal)", NULL),
+SND_SOC_DAPM_SPK("Speaker", NULL),
+};
+
+/* tosa audio map */
+static const char *audio_map[][3] = {
+
+ /* headphone connected to HPOUTL, HPOUTR */
+ {"Headphone Jack", NULL, "HPOUTL"},
+ {"Headphone Jack", NULL, "HPOUTR"},
+
+ /* ext speaker connected to LOUT2, ROUT2 */
+ {"Speaker", NULL, "LOUT2"},
+ {"Speaker", NULL, "ROUT2"},
+
+ /* internal mic is connected to mic1, mic2 differential - with bias */
+ {"MIC1", NULL, "Mic Bias"},
+ {"MIC2", NULL, "Mic Bias"},
+ {"Mic Bias", NULL, "Mic (Internal)"},
+
+ /* headset is connected to HPOUTR, and LINEINR with bias */
+ {"Headset Jack", NULL, "HPOUTR"},
+ {"LINEINR", NULL, "Mic Bias"},
+ {"Mic Bias", NULL, "Headset Jack"},
+
+ {NULL, NULL, NULL},
+};
+
+static const char *jack_function[] = {"Headphone", "Mic", "Line", "Headset",
+ "Off"};
+static const char *spk_function[] = {"On", "Off"};
+static const struct soc_enum tosa_enum[] = {
+ SOC_ENUM_SINGLE_EXT(5, jack_function),
+ SOC_ENUM_SINGLE_EXT(2, spk_function),
+};
+
+static const struct snd_kcontrol_new tosa_controls[] = {
+ SOC_ENUM_EXT("Jack Function", tosa_enum[0], tosa_get_jack,
+ tosa_set_jack),
+ SOC_ENUM_EXT("Speaker Function", tosa_enum[1], tosa_get_spk,
+ tosa_set_spk),
+};
+
+static int tosa_ac97_init(struct snd_soc_codec *codec)
+{
+ int i, err;
+
+ snd_soc_dapm_set_endpoint(codec, "OUT3", 0);
+ snd_soc_dapm_set_endpoint(codec, "MONOOUT", 0);
+
+ /* add tosa specific controls */
+ for (i = 0; i < ARRAY_SIZE(tosa_controls); i++) {
+ err = snd_ctl_add(codec->card,
+ snd_soc_cnew(&tosa_controls[i],codec, NULL));
+ if (err < 0)
+ return err;
+ }
+
+ /* add tosa specific widgets */
+ for (i = 0; i < ARRAY_SIZE(tosa_dapm_widgets); i++) {
+ snd_soc_dapm_new_control(codec, &tosa_dapm_widgets[i]);
+ }
+
+ /* set up tosa specific audio path audio_map */
+ for (i = 0; audio_map[i][0] != NULL; i++) {
+ snd_soc_dapm_connect_input(codec, audio_map[i][0],
+ audio_map[i][1], audio_map[i][2]);
+ }
+
+ snd_soc_dapm_sync_endpoints(codec);
+ return 0;
+}
+
+static struct snd_soc_dai_link tosa_dai[] = {
+{
+ .name = "AC97",
+ .stream_name = "AC97 HiFi",
+ .cpu_dai = &pxa_ac97_dai[PXA2XX_DAI_AC97_HIFI],
+ .codec_dai = &wm9712_dai[WM9712_DAI_AC97_HIFI],
+ .init = tosa_ac97_init,
+ .ops = &tosa_ops,
+},
+{
+ .name = "AC97 Aux",
+ .stream_name = "AC97 Aux",
+ .cpu_dai = &pxa_ac97_dai[PXA2XX_DAI_AC97_AUX],
+ .codec_dai = &wm9712_dai[WM9712_DAI_AC97_AUX],
+ .ops = &tosa_ops,
+},
+};
+
+static struct snd_soc_machine tosa = {
+ .name = "Tosa",
+ .dai_link = tosa_dai,
+ .num_links = ARRAY_SIZE(tosa_dai),
+};
+
+static struct snd_soc_device tosa_snd_devdata = {
+ .machine = &tosa,
+ .platform = &pxa2xx_soc_platform,
+ .codec_dev = &soc_codec_dev_wm9712,
+};
+
+static struct platform_device *tosa_snd_device;
+
+static int __init tosa_init(void)
+{
+ int ret;
+
+ if (!machine_is_tosa())
+ return -ENODEV;
+
+ tosa_snd_device = platform_device_alloc("soc-audio", -1);
+ if (!tosa_snd_device)
+ return -ENOMEM;
+
+ platform_set_drvdata(tosa_snd_device, &tosa_snd_devdata);
+ tosa_snd_devdata.dev = &tosa_snd_device->dev;
+ ret = platform_device_add(tosa_snd_device);
+
+ if (ret)
+ platform_device_put(tosa_snd_device);
+
+ return ret;
+}
+
+static void __exit tosa_exit(void)
+{
+ platform_device_unregister(tosa_snd_device);
+}
+
+module_init(tosa_init);
+module_exit(tosa_exit);
+
+/* Module information */
+MODULE_AUTHOR("Richard Purdie");
+MODULE_DESCRIPTION("ALSA SoC Tosa");
+MODULE_LICENSE("GPL");
diff --git a/sound/soc/soc-core.c b/sound/soc/soc-core.c
new file mode 100644
index 000000000000..36519aef55d9
--- /dev/null
+++ b/sound/soc/soc-core.c
@@ -0,0 +1,1587 @@
+/*
+ * soc-core.c -- ALSA SoC Audio Layer
+ *
+ * Copyright 2005 Wolfson Microelectronics PLC.
+ * Copyright 2005 Openedhand Ltd.
+ *
+ * Author: Liam Girdwood
+ * liam.girdwood@wolfsonmicro.com or linux@wolfsonmicro.com
+ * with code, comments and ideas from :-
+ * Richard Purdie <richard@openedhand.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.
+ *
+ * Revision history
+ * 12th Aug 2005 Initial version.
+ * 25th Oct 2005 Working Codec, Interface and Platform registration.
+ *
+ * TODO:
+ * o Add hw rules to enforce rates, etc.
+ * o More testing with other codecs/machines.
+ * o Add more codecs and platforms to ensure good API coverage.
+ * o Support TDM on PCM and I2S
+ */
+
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/init.h>
+#include <linux/delay.h>
+#include <linux/pm.h>
+#include <linux/bitops.h>
+#include <linux/platform_device.h>
+#include <sound/driver.h>
+#include <sound/core.h>
+#include <sound/pcm.h>
+#include <sound/pcm_params.h>
+#include <sound/soc.h>
+#include <sound/soc-dapm.h>
+#include <sound/initval.h>
+
+/* debug */
+#define SOC_DEBUG 0
+#if SOC_DEBUG
+#define dbg(format, arg...) printk(format, ## arg)
+#else
+#define dbg(format, arg...)
+#endif
+
+static DEFINE_MUTEX(pcm_mutex);
+static DEFINE_MUTEX(io_mutex);
+static DECLARE_WAIT_QUEUE_HEAD(soc_pm_waitq);
+
+/*
+ * This is a timeout to do a DAPM powerdown after a stream is closed().
+ * It can be used to eliminate pops between different playback streams, e.g.
+ * between two audio tracks.
+ */
+static int pmdown_time = 5000;
+module_param(pmdown_time, int, 0);
+MODULE_PARM_DESC(pmdown_time, "DAPM stream powerdown time (msecs)");
+
+/*
+ * This function forces any delayed work to be queued and run.
+ */
+static int run_delayed_work(struct delayed_work *dwork)
+{
+ int ret;
+
+ /* cancel any work waiting to be queued. */
+ ret = cancel_delayed_work(dwork);
+
+ /* if there was any work waiting then we run it now and
+ * wait for it's completion */
+ if (ret) {
+ schedule_delayed_work(dwork, 0);
+ flush_scheduled_work();
+ }
+ return ret;
+}
+
+#ifdef CONFIG_SND_SOC_AC97_BUS
+/* unregister ac97 codec */
+static int soc_ac97_dev_unregister(struct snd_soc_codec *codec)
+{
+ if (codec->ac97->dev.bus)
+ device_unregister(&codec->ac97->dev);
+ return 0;
+}
+
+/* stop no dev release warning */
+static void soc_ac97_device_release(struct device *dev){}
+
+/* register ac97 codec to bus */
+static int soc_ac97_dev_register(struct snd_soc_codec *codec)
+{
+ int err;
+
+ codec->ac97->dev.bus = &ac97_bus_type;
+ codec->ac97->dev.parent = NULL;
+ codec->ac97->dev.release = soc_ac97_device_release;
+
+ snprintf(codec->ac97->dev.bus_id, BUS_ID_SIZE, "%d-%d:%s",
+ codec->card->number, 0, codec->name);
+ err = device_register(&codec->ac97->dev);
+ if (err < 0) {
+ snd_printk(KERN_ERR "Can't register ac97 bus\n");
+ codec->ac97->dev.bus = NULL;
+ return err;
+ }
+ return 0;
+}
+#endif
+
+static inline const char* get_dai_name(int type)
+{
+ switch(type) {
+ case SND_SOC_DAI_AC97:
+ return "AC97";
+ case SND_SOC_DAI_I2S:
+ return "I2S";
+ case SND_SOC_DAI_PCM:
+ return "PCM";
+ }
+ return NULL;
+}
+
+/*
+ * Called by ALSA when a PCM substream is opened, the runtime->hw record is
+ * then initialized and any private data can be allocated. This also calls
+ * startup for the cpu DAI, platform, machine and codec DAI.
+ */
+static int soc_pcm_open(struct snd_pcm_substream *substream)
+{
+ struct snd_soc_pcm_runtime *rtd = substream->private_data;
+ struct snd_soc_device *socdev = rtd->socdev;
+ struct snd_pcm_runtime *runtime = substream->runtime;
+ struct snd_soc_dai_link *machine = rtd->dai;
+ struct snd_soc_platform *platform = socdev->platform;
+ struct snd_soc_cpu_dai *cpu_dai = machine->cpu_dai;
+ struct snd_soc_codec_dai *codec_dai = machine->codec_dai;
+ int ret = 0;
+
+ mutex_lock(&pcm_mutex);
+
+ /* startup the audio subsystem */
+ if (cpu_dai->ops.startup) {
+ ret = cpu_dai->ops.startup(substream);
+ if (ret < 0) {
+ printk(KERN_ERR "asoc: can't open interface %s\n",
+ cpu_dai->name);
+ goto out;
+ }
+ }
+
+ if (platform->pcm_ops->open) {
+ ret = platform->pcm_ops->open(substream);
+ if (ret < 0) {
+ printk(KERN_ERR "asoc: can't open platform %s\n", platform->name);
+ goto platform_err;
+ }
+ }
+
+ if (codec_dai->ops.startup) {
+ ret = codec_dai->ops.startup(substream);
+ if (ret < 0) {
+ printk(KERN_ERR "asoc: can't open codec %s\n",
+ codec_dai->name);
+ goto codec_dai_err;
+ }
+ }
+
+ if (machine->ops && machine->ops->startup) {
+ ret = machine->ops->startup(substream);
+ if (ret < 0) {
+ printk(KERN_ERR "asoc: %s startup failed\n", machine->name);
+ goto machine_err;
+ }
+ }
+
+ /* Check that the codec and cpu DAI's are compatible */
+ if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
+ runtime->hw.rate_min =
+ max(codec_dai->playback.rate_min, cpu_dai->playback.rate_min);
+ runtime->hw.rate_max =
+ min(codec_dai->playback.rate_max, cpu_dai->playback.rate_max);
+ runtime->hw.channels_min =
+ max(codec_dai->playback.channels_min,
+ cpu_dai->playback.channels_min);
+ runtime->hw.channels_max =
+ min(codec_dai->playback.channels_max,
+ cpu_dai->playback.channels_max);
+ runtime->hw.formats =
+ codec_dai->playback.formats & cpu_dai->playback.formats;
+ runtime->hw.rates =
+ codec_dai->playback.rates & cpu_dai->playback.rates;
+ } else {
+ runtime->hw.rate_min =
+ max(codec_dai->capture.rate_min, cpu_dai->capture.rate_min);
+ runtime->hw.rate_max =
+ min(codec_dai->capture.rate_max, cpu_dai->capture.rate_max);
+ runtime->hw.channels_min =
+ max(codec_dai->capture.channels_min,
+ cpu_dai->capture.channels_min);
+ runtime->hw.channels_max =
+ min(codec_dai->capture.channels_max,
+ cpu_dai->capture.channels_max);
+ runtime->hw.formats =
+ codec_dai->capture.formats & cpu_dai->capture.formats;
+ runtime->hw.rates =
+ codec_dai->capture.rates & cpu_dai->capture.rates;
+ }
+
+ snd_pcm_limit_hw_rates(runtime);
+ if (!runtime->hw.rates) {
+ printk(KERN_ERR "asoc: %s <-> %s No matching rates\n",
+ codec_dai->name, cpu_dai->name);
+ goto machine_err;
+ }
+ if (!runtime->hw.formats) {
+ printk(KERN_ERR "asoc: %s <-> %s No matching formats\n",
+ codec_dai->name, cpu_dai->name);
+ goto machine_err;
+ }
+ if (!runtime->hw.channels_min || !runtime->hw.channels_max) {
+ printk(KERN_ERR "asoc: %s <-> %s No matching channels\n",
+ codec_dai->name, cpu_dai->name);
+ goto machine_err;
+ }
+
+ dbg("asoc: %s <-> %s info:\n",codec_dai->name, cpu_dai->name);
+ dbg("asoc: rate mask 0x%x\n", runtime->hw.rates);
+ dbg("asoc: min ch %d max ch %d\n", runtime->hw.channels_min,
+ runtime->hw.channels_max);
+ dbg("asoc: min rate %d max rate %d\n", runtime->hw.rate_min,
+ runtime->hw.rate_max);
+
+ if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
+ cpu_dai->playback.active = codec_dai->playback.active = 1;
+ else
+ cpu_dai->capture.active = codec_dai->capture.active = 1;
+ cpu_dai->active = codec_dai->active = 1;
+ cpu_dai->runtime = runtime;
+ socdev->codec->active++;
+ mutex_unlock(&pcm_mutex);
+ return 0;
+
+machine_err:
+ if (machine->ops && machine->ops->shutdown)
+ machine->ops->shutdown(substream);
+
+codec_dai_err:
+ if (platform->pcm_ops->close)
+ platform->pcm_ops->close(substream);
+
+platform_err:
+ if (cpu_dai->ops.shutdown)
+ cpu_dai->ops.shutdown(substream);
+out:
+ mutex_unlock(&pcm_mutex);
+ return ret;
+}
+
+/*
+ * Power down the audio subsytem pmdown_time msecs after close is called.
+ * This is to ensure there are no pops or clicks in between any music tracks
+ * due to DAPM power cycling.
+ */
+static void close_delayed_work(struct work_struct *work)
+{
+ struct snd_soc_device *socdev =
+ container_of(work, struct snd_soc_device, delayed_work.work);
+ struct snd_soc_codec *codec = socdev->codec;
+ struct snd_soc_codec_dai *codec_dai;
+ int i;
+
+ mutex_lock(&pcm_mutex);
+ for(i = 0; i < codec->num_dai; i++) {
+ codec_dai = &codec->dai[i];
+
+ dbg("pop wq checking: %s status: %s waiting: %s\n",
+ codec_dai->playback.stream_name,
+ codec_dai->playback.active ? "active" : "inactive",
+ codec_dai->pop_wait ? "yes" : "no");
+
+ /* are we waiting on this codec DAI stream */
+ if (codec_dai->pop_wait == 1) {
+
+ codec_dai->pop_wait = 0;
+ snd_soc_dapm_stream_event(codec, codec_dai->playback.stream_name,
+ SND_SOC_DAPM_STREAM_STOP);
+
+ /* power down the codec power domain if no longer active */
+ if (codec->active == 0) {
+ dbg("pop wq D3 %s %s\n", codec->name,
+ codec_dai->playback.stream_name);
+ if (codec->dapm_event)
+ codec->dapm_event(codec, SNDRV_CTL_POWER_D3hot);
+ }
+ }
+ }
+ mutex_unlock(&pcm_mutex);
+}
+
+/*
+ * Called by ALSA when a PCM substream is closed. Private data can be
+ * freed here. The cpu DAI, codec DAI, machine and platform are also
+ * shutdown.
+ */
+static int soc_codec_close(struct snd_pcm_substream *substream)
+{
+ struct snd_soc_pcm_runtime *rtd = substream->private_data;
+ struct snd_soc_device *socdev = rtd->socdev;
+ struct snd_soc_dai_link *machine = rtd->dai;
+ struct snd_soc_platform *platform = socdev->platform;
+ struct snd_soc_cpu_dai *cpu_dai = machine->cpu_dai;
+ struct snd_soc_codec_dai *codec_dai = machine->codec_dai;
+ struct snd_soc_codec *codec = socdev->codec;
+
+ mutex_lock(&pcm_mutex);
+
+ if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
+ cpu_dai->playback.active = codec_dai->playback.active = 0;
+ else
+ cpu_dai->capture.active = codec_dai->capture.active = 0;
+
+ if (codec_dai->playback.active == 0 &&
+ codec_dai->capture.active == 0) {
+ cpu_dai->active = codec_dai->active = 0;
+ }
+ codec->active--;
+
+ if (cpu_dai->ops.shutdown)
+ cpu_dai->ops.shutdown(substream);
+
+ if (codec_dai->ops.shutdown)
+ codec_dai->ops.shutdown(substream);
+
+ if (machine->ops && machine->ops->shutdown)
+ machine->ops->shutdown(substream);
+
+ if (platform->pcm_ops->close)
+ platform->pcm_ops->close(substream);
+ cpu_dai->runtime = NULL;
+
+ if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
+ /* start delayed pop wq here for playback streams */
+ codec_dai->pop_wait = 1;
+ schedule_delayed_work(&socdev->delayed_work,
+ msecs_to_jiffies(pmdown_time));
+ } else {
+ /* capture streams can be powered down now */
+ snd_soc_dapm_stream_event(codec,
+ codec_dai->capture.stream_name, SND_SOC_DAPM_STREAM_STOP);
+
+ if (codec->active == 0 && codec_dai->pop_wait == 0){
+ if (codec->dapm_event)
+ codec->dapm_event(codec, SNDRV_CTL_POWER_D3hot);
+ }
+ }
+
+ mutex_unlock(&pcm_mutex);
+ return 0;
+}
+
+/*
+ * Called by ALSA when the PCM substream is prepared, can set format, sample
+ * rate, etc. This function is non atomic and can be called multiple times,
+ * it can refer to the runtime info.
+ */
+static int soc_pcm_prepare(struct snd_pcm_substream *substream)
+{
+ struct snd_soc_pcm_runtime *rtd = substream->private_data;
+ struct snd_soc_device *socdev = rtd->socdev;
+ struct snd_soc_dai_link *machine = rtd->dai;
+ struct snd_soc_platform *platform = socdev->platform;
+ struct snd_soc_cpu_dai *cpu_dai = machine->cpu_dai;
+ struct snd_soc_codec_dai *codec_dai = machine->codec_dai;
+ struct snd_soc_codec *codec = socdev->codec;
+ int ret = 0;
+
+ mutex_lock(&pcm_mutex);
+
+ if (machine->ops && machine->ops->prepare) {
+ ret = machine->ops->prepare(substream);
+ if (ret < 0) {
+ printk(KERN_ERR "asoc: machine prepare error\n");
+ goto out;
+ }
+ }
+
+ if (platform->pcm_ops->prepare) {
+ ret = platform->pcm_ops->prepare(substream);
+ if (ret < 0) {
+ printk(KERN_ERR "asoc: platform prepare error\n");
+ goto out;
+ }
+ }
+
+ if (codec_dai->ops.prepare) {
+ ret = codec_dai->ops.prepare(substream);
+ if (ret < 0) {
+ printk(KERN_ERR "asoc: codec DAI prepare error\n");
+ goto out;
+ }
+ }
+
+ if (cpu_dai->ops.prepare) {
+ ret = cpu_dai->ops.prepare(substream);
+ if (ret < 0) {
+ printk(KERN_ERR "asoc: cpu DAI prepare error\n");
+ goto out;
+ }
+ }
+
+ /* we only want to start a DAPM playback stream if we are not waiting
+ * on an existing one stopping */
+ if (codec_dai->pop_wait) {
+ /* we are waiting for the delayed work to start */
+ if (substream->stream == SNDRV_PCM_STREAM_CAPTURE)
+ snd_soc_dapm_stream_event(socdev->codec,
+ codec_dai->capture.stream_name,
+ SND_SOC_DAPM_STREAM_START);
+ else {
+ codec_dai->pop_wait = 0;
+ cancel_delayed_work(&socdev->delayed_work);
+ if (codec_dai->dai_ops.digital_mute)
+ codec_dai->dai_ops.digital_mute(codec_dai, 0);
+ }
+ } else {
+ /* no delayed work - do we need to power up codec */
+ if (codec->dapm_state != SNDRV_CTL_POWER_D0) {
+
+ if (codec->dapm_event)
+ codec->dapm_event(codec, SNDRV_CTL_POWER_D1);
+
+ if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
+ snd_soc_dapm_stream_event(codec,
+ codec_dai->playback.stream_name,
+ SND_SOC_DAPM_STREAM_START);
+ else
+ snd_soc_dapm_stream_event(codec,
+ codec_dai->capture.stream_name,
+ SND_SOC_DAPM_STREAM_START);
+
+ if (codec->dapm_event)
+ codec->dapm_event(codec, SNDRV_CTL_POWER_D0);
+ if (codec_dai->dai_ops.digital_mute)
+ codec_dai->dai_ops.digital_mute(codec_dai, 0);
+
+ } else {
+ /* codec already powered - power on widgets */
+ if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
+ snd_soc_dapm_stream_event(codec,
+ codec_dai->playback.stream_name,
+ SND_SOC_DAPM_STREAM_START);
+ else
+ snd_soc_dapm_stream_event(codec,
+ codec_dai->capture.stream_name,
+ SND_SOC_DAPM_STREAM_START);
+ if (codec_dai->dai_ops.digital_mute)
+ codec_dai->dai_ops.digital_mute(codec_dai, 0);
+ }
+ }
+
+out:
+ mutex_unlock(&pcm_mutex);
+ return ret;
+}
+
+/*
+ * Called by ALSA when the hardware params are set by application. This
+ * function can also be called multiple times and can allocate buffers
+ * (using snd_pcm_lib_* ). It's non-atomic.
+ */
+static int soc_pcm_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params)
+{
+ struct snd_soc_pcm_runtime *rtd = substream->private_data;
+ struct snd_soc_device *socdev = rtd->socdev;
+ struct snd_soc_dai_link *machine = rtd->dai;
+ struct snd_soc_platform *platform = socdev->platform;
+ struct snd_soc_cpu_dai *cpu_dai = machine->cpu_dai;
+ struct snd_soc_codec_dai *codec_dai = machine->codec_dai;
+ int ret = 0;
+
+ mutex_lock(&pcm_mutex);
+
+ if (machine->ops && machine->ops->hw_params) {
+ ret = machine->ops->hw_params(substream, params);
+ if (ret < 0) {
+ printk(KERN_ERR "asoc: machine hw_params failed\n");
+ goto out;
+ }
+ }
+
+ if (codec_dai->ops.hw_params) {
+ ret = codec_dai->ops.hw_params(substream, params);
+ if (ret < 0) {
+ printk(KERN_ERR "asoc: can't set codec %s hw params\n",
+ codec_dai->name);
+ goto codec_err;
+ }
+ }
+
+ if (cpu_dai->ops.hw_params) {
+ ret = cpu_dai->ops.hw_params(substream, params);
+ if (ret < 0) {
+ printk(KERN_ERR "asoc: can't set interface %s hw params\n",
+ cpu_dai->name);
+ goto interface_err;
+ }
+ }
+
+ if (platform->pcm_ops->hw_params) {
+ ret = platform->pcm_ops->hw_params(substream, params);
+ if (ret < 0) {
+ printk(KERN_ERR "asoc: can't set platform %s hw params\n",
+ platform->name);
+ goto platform_err;
+ }
+ }
+
+out:
+ mutex_unlock(&pcm_mutex);
+ return ret;
+
+platform_err:
+ if (cpu_dai->ops.hw_free)
+ cpu_dai->ops.hw_free(substream);
+
+interface_err:
+ if (codec_dai->ops.hw_free)
+ codec_dai->ops.hw_free(substream);
+
+codec_err:
+ if(machine->ops && machine->ops->hw_free)
+ machine->ops->hw_free(substream);
+
+ mutex_unlock(&pcm_mutex);
+ return ret;
+}
+
+/*
+ * Free's resources allocated by hw_params, can be called multiple times
+ */
+static int soc_pcm_hw_free(struct snd_pcm_substream *substream)
+{
+ struct snd_soc_pcm_runtime *rtd = substream->private_data;
+ struct snd_soc_device *socdev = rtd->socdev;
+ struct snd_soc_dai_link *machine = rtd->dai;
+ struct snd_soc_platform *platform = socdev->platform;
+ struct snd_soc_cpu_dai *cpu_dai = machine->cpu_dai;
+ struct snd_soc_codec_dai *codec_dai = machine->codec_dai;
+ struct snd_soc_codec *codec = socdev->codec;
+
+ mutex_lock(&pcm_mutex);
+
+ /* apply codec digital mute */
+ if (!codec->active && codec_dai->dai_ops.digital_mute)
+ codec_dai->dai_ops.digital_mute(codec_dai, 1);
+
+ /* free any machine hw params */
+ if (machine->ops && machine->ops->hw_free)
+ machine->ops->hw_free(substream);
+
+ /* free any DMA resources */
+ if (platform->pcm_ops->hw_free)
+ platform->pcm_ops->hw_free(substream);
+
+ /* now free hw params for the DAI's */
+ if (codec_dai->ops.hw_free)
+ codec_dai->ops.hw_free(substream);
+
+ if (cpu_dai->ops.hw_free)
+ cpu_dai->ops.hw_free(substream);
+
+ mutex_unlock(&pcm_mutex);
+ return 0;
+}
+
+static int soc_pcm_trigger(struct snd_pcm_substream *substream, int cmd)
+{
+ struct snd_soc_pcm_runtime *rtd = substream->private_data;
+ struct snd_soc_device *socdev = rtd->socdev;
+ struct snd_soc_dai_link *machine = rtd->dai;
+ struct snd_soc_platform *platform = socdev->platform;
+ struct snd_soc_cpu_dai *cpu_dai = machine->cpu_dai;
+ struct snd_soc_codec_dai *codec_dai = machine->codec_dai;
+ int ret;
+
+ if (codec_dai->ops.trigger) {
+ ret = codec_dai->ops.trigger(substream, cmd);
+ if (ret < 0)
+ return ret;
+ }
+
+ if (platform->pcm_ops->trigger) {
+ ret = platform->pcm_ops->trigger(substream, cmd);
+ if (ret < 0)
+ return ret;
+ }
+
+ if (cpu_dai->ops.trigger) {
+ ret = cpu_dai->ops.trigger(substream, cmd);
+ if (ret < 0)
+ return ret;
+ }
+ return 0;
+}
+
+/* ASoC PCM operations */
+static struct snd_pcm_ops soc_pcm_ops = {
+ .open = soc_pcm_open,
+ .close = soc_codec_close,
+ .hw_params = soc_pcm_hw_params,
+ .hw_free = soc_pcm_hw_free,
+ .prepare = soc_pcm_prepare,
+ .trigger = soc_pcm_trigger,
+};
+
+#ifdef CONFIG_PM
+/* powers down audio subsystem for suspend */
+static int soc_suspend(struct platform_device *pdev, pm_message_t state)
+{
+ struct snd_soc_device *socdev = platform_get_drvdata(pdev);
+ struct snd_soc_machine *machine = socdev->machine;
+ struct snd_soc_platform *platform = socdev->platform;
+ struct snd_soc_codec_device *codec_dev = socdev->codec_dev;
+ struct snd_soc_codec *codec = socdev->codec;
+ int i;
+
+ /* mute any active DAC's */
+ for(i = 0; i < machine->num_links; i++) {
+ struct snd_soc_codec_dai *dai = machine->dai_link[i].codec_dai;
+ if (dai->dai_ops.digital_mute && dai->playback.active)
+ dai->dai_ops.digital_mute(dai, 1);
+ }
+
+ if (machine->suspend_pre)
+ machine->suspend_pre(pdev, state);
+
+ for(i = 0; i < machine->num_links; i++) {
+ struct snd_soc_cpu_dai *cpu_dai = machine->dai_link[i].cpu_dai;
+ if (cpu_dai->suspend && cpu_dai->type != SND_SOC_DAI_AC97)
+ cpu_dai->suspend(pdev, cpu_dai);
+ if (platform->suspend)
+ platform->suspend(pdev, cpu_dai);
+ }
+
+ /* close any waiting streams and save state */
+ run_delayed_work(&socdev->delayed_work);
+ codec->suspend_dapm_state = codec->dapm_state;
+
+ for(i = 0; i < codec->num_dai; i++) {
+ char *stream = codec->dai[i].playback.stream_name;
+ if (stream != NULL)
+ snd_soc_dapm_stream_event(codec, stream,
+ SND_SOC_DAPM_STREAM_SUSPEND);
+ stream = codec->dai[i].capture.stream_name;
+ if (stream != NULL)
+ snd_soc_dapm_stream_event(codec, stream,
+ SND_SOC_DAPM_STREAM_SUSPEND);
+ }
+
+ if (codec_dev->suspend)
+ codec_dev->suspend(pdev, state);
+
+ for(i = 0; i < machine->num_links; i++) {
+ struct snd_soc_cpu_dai *cpu_dai = machine->dai_link[i].cpu_dai;
+ if (cpu_dai->suspend && cpu_dai->type == SND_SOC_DAI_AC97)
+ cpu_dai->suspend(pdev, cpu_dai);
+ }
+
+ if (machine->suspend_post)
+ machine->suspend_post(pdev, state);
+
+ return 0;
+}
+
+/* powers up audio subsystem after a suspend */
+static int soc_resume(struct platform_device *pdev)
+{
+ struct snd_soc_device *socdev = platform_get_drvdata(pdev);
+ struct snd_soc_machine *machine = socdev->machine;
+ struct snd_soc_platform *platform = socdev->platform;
+ struct snd_soc_codec_device *codec_dev = socdev->codec_dev;
+ struct snd_soc_codec *codec = socdev->codec;
+ int i;
+
+ if (machine->resume_pre)
+ machine->resume_pre(pdev);
+
+ for(i = 0; i < machine->num_links; i++) {
+ struct snd_soc_cpu_dai *cpu_dai = machine->dai_link[i].cpu_dai;
+ if (cpu_dai->resume && cpu_dai->type == SND_SOC_DAI_AC97)
+ cpu_dai->resume(pdev, cpu_dai);
+ }
+
+ if (codec_dev->resume)
+ codec_dev->resume(pdev);
+
+ for(i = 0; i < codec->num_dai; i++) {
+ char* stream = codec->dai[i].playback.stream_name;
+ if (stream != NULL)
+ snd_soc_dapm_stream_event(codec, stream,
+ SND_SOC_DAPM_STREAM_RESUME);
+ stream = codec->dai[i].capture.stream_name;
+ if (stream != NULL)
+ snd_soc_dapm_stream_event(codec, stream,
+ SND_SOC_DAPM_STREAM_RESUME);
+ }
+
+ /* unmute any active DAC's */
+ for(i = 0; i < machine->num_links; i++) {
+ struct snd_soc_codec_dai *dai = machine->dai_link[i].codec_dai;
+ if (dai->dai_ops.digital_mute && dai->playback.active)
+ dai->dai_ops.digital_mute(dai, 0);
+ }
+
+ for(i = 0; i < machine->num_links; i++) {
+ struct snd_soc_cpu_dai *cpu_dai = machine->dai_link[i].cpu_dai;
+ if (cpu_dai->resume && cpu_dai->type != SND_SOC_DAI_AC97)
+ cpu_dai->resume(pdev, cpu_dai);
+ if (platform->resume)
+ platform->resume(pdev, cpu_dai);
+ }
+
+ if (machine->resume_post)
+ machine->resume_post(pdev);
+
+ return 0;
+}
+
+#else
+#define soc_suspend NULL
+#define soc_resume NULL
+#endif
+
+/* probes a new socdev */
+static int soc_probe(struct platform_device *pdev)
+{
+ int ret = 0, i;
+ struct snd_soc_device *socdev = platform_get_drvdata(pdev);
+ struct snd_soc_machine *machine = socdev->machine;
+ struct snd_soc_platform *platform = socdev->platform;
+ struct snd_soc_codec_device *codec_dev = socdev->codec_dev;
+
+ if (machine->probe) {
+ ret = machine->probe(pdev);
+ if(ret < 0)
+ return ret;
+ }
+
+ for (i = 0; i < machine->num_links; i++) {
+ struct snd_soc_cpu_dai *cpu_dai = machine->dai_link[i].cpu_dai;
+ if (cpu_dai->probe) {
+ ret = cpu_dai->probe(pdev);
+ if(ret < 0)
+ goto cpu_dai_err;
+ }
+ }
+
+ if (codec_dev->probe) {
+ ret = codec_dev->probe(pdev);
+ if(ret < 0)
+ goto cpu_dai_err;
+ }
+
+ if (platform->probe) {
+ ret = platform->probe(pdev);
+ if(ret < 0)
+ goto platform_err;
+ }
+
+ /* DAPM stream work */
+ INIT_DELAYED_WORK(&socdev->delayed_work, close_delayed_work);
+ return 0;
+
+platform_err:
+ if (codec_dev->remove)
+ codec_dev->remove(pdev);
+
+cpu_dai_err:
+ for (i--; i >= 0; i--) {
+ struct snd_soc_cpu_dai *cpu_dai = machine->dai_link[i].cpu_dai;
+ if (cpu_dai->remove)
+ cpu_dai->remove(pdev);
+ }
+
+ if (machine->remove)
+ machine->remove(pdev);
+
+ return ret;
+}
+
+/* removes a socdev */
+static int soc_remove(struct platform_device *pdev)
+{
+ int i;
+ struct snd_soc_device *socdev = platform_get_drvdata(pdev);
+ struct snd_soc_machine *machine = socdev->machine;
+ struct snd_soc_platform *platform = socdev->platform;
+ struct snd_soc_codec_device *codec_dev = socdev->codec_dev;
+
+ run_delayed_work(&socdev->delayed_work);
+
+ if (platform->remove)
+ platform->remove(pdev);
+
+ if (codec_dev->remove)
+ codec_dev->remove(pdev);
+
+ for (i = 0; i < machine->num_links; i++) {
+ struct snd_soc_cpu_dai *cpu_dai = machine->dai_link[i].cpu_dai;
+ if (cpu_dai->remove)
+ cpu_dai->remove(pdev);
+ }
+
+ if (machine->remove)
+ machine->remove(pdev);
+
+ return 0;
+}
+
+/* ASoC platform driver */
+static struct platform_driver soc_driver = {
+ .driver = {
+ .name = "soc-audio",
+ },
+ .probe = soc_probe,
+ .remove = soc_remove,
+ .suspend = soc_suspend,
+ .resume = soc_resume,
+};
+
+/* create a new pcm */
+static int soc_new_pcm(struct snd_soc_device *socdev,
+ struct snd_soc_dai_link *dai_link, int num)
+{
+ struct snd_soc_codec *codec = socdev->codec;
+ struct snd_soc_codec_dai *codec_dai = dai_link->codec_dai;
+ struct snd_soc_cpu_dai *cpu_dai = dai_link->cpu_dai;
+ struct snd_soc_pcm_runtime *rtd;
+ struct snd_pcm *pcm;
+ char new_name[64];
+ int ret = 0, playback = 0, capture = 0;
+
+ rtd = kzalloc(sizeof(struct snd_soc_pcm_runtime), GFP_KERNEL);
+ if (rtd == NULL)
+ return -ENOMEM;
+
+ rtd->dai = dai_link;
+ rtd->socdev = socdev;
+ codec_dai->codec = socdev->codec;
+
+ /* check client and interface hw capabilities */
+ sprintf(new_name, "%s %s-%s-%d",dai_link->stream_name, codec_dai->name,
+ get_dai_name(cpu_dai->type), num);
+
+ if (codec_dai->playback.channels_min)
+ playback = 1;
+ if (codec_dai->capture.channels_min)
+ capture = 1;
+
+ ret = snd_pcm_new(codec->card, new_name, codec->pcm_devs++, playback,
+ capture, &pcm);
+ if (ret < 0) {
+ printk(KERN_ERR "asoc: can't create pcm for codec %s\n", codec->name);
+ kfree(rtd);
+ return ret;
+ }
+
+ pcm->private_data = rtd;
+ soc_pcm_ops.mmap = socdev->platform->pcm_ops->mmap;
+ soc_pcm_ops.pointer = socdev->platform->pcm_ops->pointer;
+ soc_pcm_ops.ioctl = socdev->platform->pcm_ops->ioctl;
+ soc_pcm_ops.copy = socdev->platform->pcm_ops->copy;
+ soc_pcm_ops.silence = socdev->platform->pcm_ops->silence;
+ soc_pcm_ops.ack = socdev->platform->pcm_ops->ack;
+ soc_pcm_ops.page = socdev->platform->pcm_ops->page;
+
+ if (playback)
+ snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &soc_pcm_ops);
+
+ if (capture)
+ snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, &soc_pcm_ops);
+
+ ret = socdev->platform->pcm_new(codec->card, codec_dai, pcm);
+ if (ret < 0) {
+ printk(KERN_ERR "asoc: platform pcm constructor failed\n");
+ kfree(rtd);
+ return ret;
+ }
+
+ pcm->private_free = socdev->platform->pcm_free;
+ printk(KERN_INFO "asoc: %s <-> %s mapping ok\n", codec_dai->name,
+ cpu_dai->name);
+ return ret;
+}
+
+/* codec register dump */
+static ssize_t codec_reg_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct snd_soc_device *devdata = dev_get_drvdata(dev);
+ struct snd_soc_codec *codec = devdata->codec;
+ int i, step = 1, count = 0;
+
+ if (!codec->reg_cache_size)
+ return 0;
+
+ if (codec->reg_cache_step)
+ step = codec->reg_cache_step;
+
+ count += sprintf(buf, "%s registers\n", codec->name);
+ for(i = 0; i < codec->reg_cache_size; i += step)
+ count += sprintf(buf + count, "%2x: %4x\n", i, codec->read(codec, i));
+
+ return count;
+}
+static DEVICE_ATTR(codec_reg, 0444, codec_reg_show, NULL);
+
+/**
+ * snd_soc_new_ac97_codec - initailise AC97 device
+ * @codec: audio codec
+ * @ops: AC97 bus operations
+ * @num: AC97 codec number
+ *
+ * Initialises AC97 codec resources for use by ad-hoc devices only.
+ */
+int snd_soc_new_ac97_codec(struct snd_soc_codec *codec,
+ struct snd_ac97_bus_ops *ops, int num)
+{
+ mutex_lock(&codec->mutex);
+
+ codec->ac97 = kzalloc(sizeof(struct snd_ac97), GFP_KERNEL);
+ if (codec->ac97 == NULL) {
+ mutex_unlock(&codec->mutex);
+ return -ENOMEM;
+ }
+
+ codec->ac97->bus = kzalloc(sizeof(struct snd_ac97_bus), GFP_KERNEL);
+ if (codec->ac97->bus == NULL) {
+ kfree(codec->ac97);
+ codec->ac97 = NULL;
+ mutex_unlock(&codec->mutex);
+ return -ENOMEM;
+ }
+
+ codec->ac97->bus->ops = ops;
+ codec->ac97->num = num;
+ mutex_unlock(&codec->mutex);
+ return 0;
+}
+EXPORT_SYMBOL_GPL(snd_soc_new_ac97_codec);
+
+/**
+ * snd_soc_free_ac97_codec - free AC97 codec device
+ * @codec: audio codec
+ *
+ * Frees AC97 codec device resources.
+ */
+void snd_soc_free_ac97_codec(struct snd_soc_codec *codec)
+{
+ mutex_lock(&codec->mutex);
+ kfree(codec->ac97->bus);
+ kfree(codec->ac97);
+ codec->ac97 = NULL;
+ mutex_unlock(&codec->mutex);
+}
+EXPORT_SYMBOL_GPL(snd_soc_free_ac97_codec);
+
+/**
+ * snd_soc_update_bits - update codec register bits
+ * @codec: audio codec
+ * @reg: codec register
+ * @mask: register mask
+ * @value: new value
+ *
+ * Writes new register value.
+ *
+ * Returns 1 for change else 0.
+ */
+int snd_soc_update_bits(struct snd_soc_codec *codec, unsigned short reg,
+ unsigned short mask, unsigned short value)
+{
+ int change;
+ unsigned short old, new;
+
+ mutex_lock(&io_mutex);
+ old = snd_soc_read(codec, reg);
+ new = (old & ~mask) | value;
+ change = old != new;
+ if (change)
+ snd_soc_write(codec, reg, new);
+
+ mutex_unlock(&io_mutex);
+ return change;
+}
+EXPORT_SYMBOL_GPL(snd_soc_update_bits);
+
+/**
+ * snd_soc_test_bits - test register for change
+ * @codec: audio codec
+ * @reg: codec register
+ * @mask: register mask
+ * @value: new value
+ *
+ * Tests a register with a new value and checks if the new value is
+ * different from the old value.
+ *
+ * Returns 1 for change else 0.
+ */
+int snd_soc_test_bits(struct snd_soc_codec *codec, unsigned short reg,
+ unsigned short mask, unsigned short value)
+{
+ int change;
+ unsigned short old, new;
+
+ mutex_lock(&io_mutex);
+ old = snd_soc_read(codec, reg);
+ new = (old & ~mask) | value;
+ change = old != new;
+ mutex_unlock(&io_mutex);
+
+ return change;
+}
+EXPORT_SYMBOL_GPL(snd_soc_test_bits);
+
+/**
+ * snd_soc_new_pcms - create new sound card and pcms
+ * @socdev: the SoC audio device
+ *
+ * Create a new sound card based upon the codec and interface pcms.
+ *
+ * Returns 0 for success, else error.
+ */
+int snd_soc_new_pcms(struct snd_soc_device *socdev, int idx, const char *xid)
+{
+ struct snd_soc_codec *codec = socdev->codec;
+ struct snd_soc_machine *machine = socdev->machine;
+ int ret = 0, i;
+
+ mutex_lock(&codec->mutex);
+
+ /* register a sound card */
+ codec->card = snd_card_new(idx, xid, codec->owner, 0);
+ if (!codec->card) {
+ printk(KERN_ERR "asoc: can't create sound card for codec %s\n",
+ codec->name);
+ mutex_unlock(&codec->mutex);
+ return -ENODEV;
+ }
+
+ codec->card->dev = socdev->dev;
+ codec->card->private_data = codec;
+ strncpy(codec->card->driver, codec->name, sizeof(codec->card->driver));
+
+ /* create the pcms */
+ for(i = 0; i < machine->num_links; i++) {
+ ret = soc_new_pcm(socdev, &machine->dai_link[i], i);
+ if (ret < 0) {
+ printk(KERN_ERR "asoc: can't create pcm %s\n",
+ machine->dai_link[i].stream_name);
+ mutex_unlock(&codec->mutex);
+ return ret;
+ }
+ }
+
+ mutex_unlock(&codec->mutex);
+ return ret;
+}
+EXPORT_SYMBOL_GPL(snd_soc_new_pcms);
+
+/**
+ * snd_soc_register_card - register sound card
+ * @socdev: the SoC audio device
+ *
+ * Register a SoC sound card. Also registers an AC97 device if the
+ * codec is AC97 for ad hoc devices.
+ *
+ * Returns 0 for success, else error.
+ */
+int snd_soc_register_card(struct snd_soc_device *socdev)
+{
+ struct snd_soc_codec *codec = socdev->codec;
+ struct snd_soc_machine *machine = socdev->machine;
+ int ret = 0, i, ac97 = 0, err = 0;
+
+ mutex_lock(&codec->mutex);
+ for(i = 0; i < machine->num_links; i++) {
+ if (socdev->machine->dai_link[i].init) {
+ err = socdev->machine->dai_link[i].init(codec);
+ if (err < 0) {
+ printk(KERN_ERR "asoc: failed to init %s\n",
+ socdev->machine->dai_link[i].stream_name);
+ continue;
+ }
+ }
+ if (socdev->machine->dai_link[i].cpu_dai->type == SND_SOC_DAI_AC97)
+ ac97 = 1;
+ }
+ snprintf(codec->card->shortname, sizeof(codec->card->shortname),
+ "%s", machine->name);
+ snprintf(codec->card->longname, sizeof(codec->card->longname),
+ "%s (%s)", machine->name, codec->name);
+
+ ret = snd_card_register(codec->card);
+ if (ret < 0) {
+ printk(KERN_ERR "asoc: failed to register soundcard for codec %s\n",
+ codec->name);
+ goto out;
+ }
+
+#ifdef CONFIG_SND_SOC_AC97_BUS
+ if (ac97) {
+ ret = soc_ac97_dev_register(codec);
+ if (ret < 0) {
+ printk(KERN_ERR "asoc: AC97 device register failed\n");
+ snd_card_free(codec->card);
+ goto out;
+ }
+ }
+#endif
+
+ err = snd_soc_dapm_sys_add(socdev->dev);
+ if (err < 0)
+ printk(KERN_WARNING "asoc: failed to add dapm sysfs entries\n");
+
+ err = device_create_file(socdev->dev, &dev_attr_codec_reg);
+ if (err < 0)
+ printk(KERN_WARNING "asoc: failed to add codec sysfs entries\n");
+out:
+ mutex_unlock(&codec->mutex);
+ return ret;
+}
+EXPORT_SYMBOL_GPL(snd_soc_register_card);
+
+/**
+ * snd_soc_free_pcms - free sound card and pcms
+ * @socdev: the SoC audio device
+ *
+ * Frees sound card and pcms associated with the socdev.
+ * Also unregister the codec if it is an AC97 device.
+ */
+void snd_soc_free_pcms(struct snd_soc_device *socdev)
+{
+ struct snd_soc_codec *codec = socdev->codec;
+
+ mutex_lock(&codec->mutex);
+#ifdef CONFIG_SND_SOC_AC97_BUS
+ if (codec->ac97)
+ soc_ac97_dev_unregister(codec);
+#endif
+
+ if (codec->card)
+ snd_card_free(codec->card);
+ device_remove_file(socdev->dev, &dev_attr_codec_reg);
+ mutex_unlock(&codec->mutex);
+}
+EXPORT_SYMBOL_GPL(snd_soc_free_pcms);
+
+/**
+ * snd_soc_set_runtime_hwparams - set the runtime hardware parameters
+ * @substream: the pcm substream
+ * @hw: the hardware parameters
+ *
+ * Sets the substream runtime hardware parameters.
+ */
+int snd_soc_set_runtime_hwparams(struct snd_pcm_substream *substream,
+ const struct snd_pcm_hardware *hw)
+{
+ struct snd_pcm_runtime *runtime = substream->runtime;
+ runtime->hw.info = hw->info;
+ runtime->hw.formats = hw->formats;
+ runtime->hw.period_bytes_min = hw->period_bytes_min;
+ runtime->hw.period_bytes_max = hw->period_bytes_max;
+ runtime->hw.periods_min = hw->periods_min;
+ runtime->hw.periods_max = hw->periods_max;
+ runtime->hw.buffer_bytes_max = hw->buffer_bytes_max;
+ runtime->hw.fifo_size = hw->fifo_size;
+ return 0;
+}
+EXPORT_SYMBOL_GPL(snd_soc_set_runtime_hwparams);
+
+/**
+ * snd_soc_cnew - create new control
+ * @_template: control template
+ * @data: control private data
+ * @lnng_name: control long name
+ *
+ * Create a new mixer control from a template control.
+ *
+ * Returns 0 for success, else error.
+ */
+struct snd_kcontrol *snd_soc_cnew(const struct snd_kcontrol_new *_template,
+ void *data, char *long_name)
+{
+ struct snd_kcontrol_new template;
+
+ memcpy(&template, _template, sizeof(template));
+ if (long_name)
+ template.name = long_name;
+ template.access = SNDRV_CTL_ELEM_ACCESS_READWRITE;
+ template.index = 0;
+
+ return snd_ctl_new1(&template, data);
+}
+EXPORT_SYMBOL_GPL(snd_soc_cnew);
+
+/**
+ * snd_soc_info_enum_double - enumerated double mixer info callback
+ * @kcontrol: mixer control
+ * @uinfo: control element information
+ *
+ * Callback to provide information about a double enumerated
+ * mixer control.
+ *
+ * Returns 0 for success.
+ */
+int snd_soc_info_enum_double(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_info *uinfo)
+{
+ struct soc_enum *e = (struct soc_enum *)kcontrol->private_value;
+
+ uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED;
+ uinfo->count = e->shift_l == e->shift_r ? 1 : 2;
+ uinfo->value.enumerated.items = e->mask;
+
+ if (uinfo->value.enumerated.item > e->mask - 1)
+ uinfo->value.enumerated.item = e->mask - 1;
+ strcpy(uinfo->value.enumerated.name,
+ e->texts[uinfo->value.enumerated.item]);
+ return 0;
+}
+EXPORT_SYMBOL_GPL(snd_soc_info_enum_double);
+
+/**
+ * snd_soc_get_enum_double - enumerated double mixer get callback
+ * @kcontrol: mixer control
+ * @uinfo: control element information
+ *
+ * Callback to get the value of a double enumerated mixer.
+ *
+ * Returns 0 for success.
+ */
+int snd_soc_get_enum_double(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol);
+ struct soc_enum *e = (struct soc_enum *)kcontrol->private_value;
+ unsigned short val, bitmask;
+
+ for (bitmask = 1; bitmask < e->mask; bitmask <<= 1)
+ ;
+ val = snd_soc_read(codec, e->reg);
+ ucontrol->value.enumerated.item[0] = (val >> e->shift_l) & (bitmask - 1);
+ if (e->shift_l != e->shift_r)
+ ucontrol->value.enumerated.item[1] =
+ (val >> e->shift_r) & (bitmask - 1);
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(snd_soc_get_enum_double);
+
+/**
+ * snd_soc_put_enum_double - enumerated double mixer put callback
+ * @kcontrol: mixer control
+ * @uinfo: control element information
+ *
+ * Callback to set the value of a double enumerated mixer.
+ *
+ * Returns 0 for success.
+ */
+int snd_soc_put_enum_double(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol);
+ struct soc_enum *e = (struct soc_enum *)kcontrol->private_value;
+ unsigned short val;
+ unsigned short mask, bitmask;
+
+ for (bitmask = 1; bitmask < e->mask; bitmask <<= 1)
+ ;
+ if (ucontrol->value.enumerated.item[0] > e->mask - 1)
+ return -EINVAL;
+ val = ucontrol->value.enumerated.item[0] << e->shift_l;
+ mask = (bitmask - 1) << e->shift_l;
+ if (e->shift_l != e->shift_r) {
+ if (ucontrol->value.enumerated.item[1] > e->mask - 1)
+ return -EINVAL;
+ val |= ucontrol->value.enumerated.item[1] << e->shift_r;
+ mask |= (bitmask - 1) << e->shift_r;
+ }
+
+ return snd_soc_update_bits(codec, e->reg, mask, val);
+}
+EXPORT_SYMBOL_GPL(snd_soc_put_enum_double);
+
+/**
+ * snd_soc_info_enum_ext - external enumerated single mixer info callback
+ * @kcontrol: mixer control
+ * @uinfo: control element information
+ *
+ * Callback to provide information about an external enumerated
+ * single mixer.
+ *
+ * Returns 0 for success.
+ */
+int snd_soc_info_enum_ext(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_info *uinfo)
+{
+ struct soc_enum *e = (struct soc_enum *)kcontrol->private_value;
+
+ uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED;
+ uinfo->count = 1;
+ uinfo->value.enumerated.items = e->mask;
+
+ if (uinfo->value.enumerated.item > e->mask - 1)
+ uinfo->value.enumerated.item = e->mask - 1;
+ strcpy(uinfo->value.enumerated.name,
+ e->texts[uinfo->value.enumerated.item]);
+ return 0;
+}
+EXPORT_SYMBOL_GPL(snd_soc_info_enum_ext);
+
+/**
+ * snd_soc_info_volsw_ext - external single mixer info callback
+ * @kcontrol: mixer control
+ * @uinfo: control element information
+ *
+ * Callback to provide information about a single external mixer control.
+ *
+ * Returns 0 for success.
+ */
+int snd_soc_info_volsw_ext(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_info *uinfo)
+{
+ int mask = kcontrol->private_value;
+
+ uinfo->type =
+ mask == 1 ? SNDRV_CTL_ELEM_TYPE_BOOLEAN : SNDRV_CTL_ELEM_TYPE_INTEGER;
+ uinfo->count = 1;
+ uinfo->value.integer.min = 0;
+ uinfo->value.integer.max = mask;
+ return 0;
+}
+EXPORT_SYMBOL_GPL(snd_soc_info_volsw_ext);
+
+/**
+ * snd_soc_info_bool_ext - external single boolean mixer info callback
+ * @kcontrol: mixer control
+ * @uinfo: control element information
+ *
+ * Callback to provide information about a single boolean external mixer control.
+ *
+ * Returns 0 for success.
+ */
+int snd_soc_info_bool_ext(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_info *uinfo)
+{
+ uinfo->type = SNDRV_CTL_ELEM_TYPE_BOOLEAN;
+ uinfo->count = 1;
+ uinfo->value.integer.min = 0;
+ uinfo->value.integer.max = 1;
+ return 0;
+}
+EXPORT_SYMBOL_GPL(snd_soc_info_bool_ext);
+
+/**
+ * snd_soc_info_volsw - single mixer info callback
+ * @kcontrol: mixer control
+ * @uinfo: control element information
+ *
+ * Callback to provide information about a single mixer control.
+ *
+ * Returns 0 for success.
+ */
+int snd_soc_info_volsw(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_info *uinfo)
+{
+ int mask = (kcontrol->private_value >> 16) & 0xff;
+ int shift = (kcontrol->private_value >> 8) & 0x0f;
+ int rshift = (kcontrol->private_value >> 12) & 0x0f;
+
+ uinfo->type =
+ mask == 1 ? SNDRV_CTL_ELEM_TYPE_BOOLEAN : SNDRV_CTL_ELEM_TYPE_INTEGER;
+ uinfo->count = shift == rshift ? 1 : 2;
+ uinfo->value.integer.min = 0;
+ uinfo->value.integer.max = mask;
+ return 0;
+}
+EXPORT_SYMBOL_GPL(snd_soc_info_volsw);
+
+/**
+ * snd_soc_get_volsw - single mixer get callback
+ * @kcontrol: mixer control
+ * @uinfo: control element information
+ *
+ * Callback to get the value of a single mixer control.
+ *
+ * Returns 0 for success.
+ */
+int snd_soc_get_volsw(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol);
+ int reg = kcontrol->private_value & 0xff;
+ int shift = (kcontrol->private_value >> 8) & 0x0f;
+ int rshift = (kcontrol->private_value >> 12) & 0x0f;
+ int mask = (kcontrol->private_value >> 16) & 0xff;
+ int invert = (kcontrol->private_value >> 24) & 0x01;
+
+ ucontrol->value.integer.value[0] =
+ (snd_soc_read(codec, reg) >> shift) & mask;
+ if (shift != rshift)
+ ucontrol->value.integer.value[1] =
+ (snd_soc_read(codec, reg) >> rshift) & mask;
+ if (invert) {
+ ucontrol->value.integer.value[0] =
+ mask - ucontrol->value.integer.value[0];
+ if (shift != rshift)
+ ucontrol->value.integer.value[1] =
+ mask - ucontrol->value.integer.value[1];
+ }
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(snd_soc_get_volsw);
+
+/**
+ * snd_soc_put_volsw - single mixer put callback
+ * @kcontrol: mixer control
+ * @uinfo: control element information
+ *
+ * Callback to set the value of a single mixer control.
+ *
+ * Returns 0 for success.
+ */
+int snd_soc_put_volsw(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol);
+ int reg = kcontrol->private_value & 0xff;
+ int shift = (kcontrol->private_value >> 8) & 0x0f;
+ int rshift = (kcontrol->private_value >> 12) & 0x0f;
+ int mask = (kcontrol->private_value >> 16) & 0xff;
+ int invert = (kcontrol->private_value >> 24) & 0x01;
+ int err;
+ unsigned short val, val2, val_mask;
+
+ val = (ucontrol->value.integer.value[0] & mask);
+ if (invert)
+ val = mask - val;
+ val_mask = mask << shift;
+ val = val << shift;
+ if (shift != rshift) {
+ val2 = (ucontrol->value.integer.value[1] & mask);
+ if (invert)
+ val2 = mask - val2;
+ val_mask |= mask << rshift;
+ val |= val2 << rshift;
+ }
+ err = snd_soc_update_bits(codec, reg, val_mask, val);
+ return err;
+}
+EXPORT_SYMBOL_GPL(snd_soc_put_volsw);
+
+/**
+ * snd_soc_info_volsw_2r - double mixer info callback
+ * @kcontrol: mixer control
+ * @uinfo: control element information
+ *
+ * Callback to provide information about a double mixer control that
+ * spans 2 codec registers.
+ *
+ * Returns 0 for success.
+ */
+int snd_soc_info_volsw_2r(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_info *uinfo)
+{
+ int mask = (kcontrol->private_value >> 12) & 0xff;
+
+ uinfo->type =
+ mask == 1 ? SNDRV_CTL_ELEM_TYPE_BOOLEAN : SNDRV_CTL_ELEM_TYPE_INTEGER;
+ uinfo->count = 2;
+ uinfo->value.integer.min = 0;
+ uinfo->value.integer.max = mask;
+ return 0;
+}
+EXPORT_SYMBOL_GPL(snd_soc_info_volsw_2r);
+
+/**
+ * snd_soc_get_volsw_2r - double mixer get callback
+ * @kcontrol: mixer control
+ * @uinfo: control element information
+ *
+ * Callback to get the value of a double mixer control that spans 2 registers.
+ *
+ * Returns 0 for success.
+ */
+int snd_soc_get_volsw_2r(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol);
+ int reg = kcontrol->private_value & 0xff;
+ int reg2 = (kcontrol->private_value >> 24) & 0xff;
+ int shift = (kcontrol->private_value >> 8) & 0x0f;
+ int mask = (kcontrol->private_value >> 12) & 0xff;
+ int invert = (kcontrol->private_value >> 20) & 0x01;
+
+ ucontrol->value.integer.value[0] =
+ (snd_soc_read(codec, reg) >> shift) & mask;
+ ucontrol->value.integer.value[1] =
+ (snd_soc_read(codec, reg2) >> shift) & mask;
+ if (invert) {
+ ucontrol->value.integer.value[0] =
+ mask - ucontrol->value.integer.value[0];
+ ucontrol->value.integer.value[1] =
+ mask - ucontrol->value.integer.value[1];
+ }
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(snd_soc_get_volsw_2r);
+
+/**
+ * snd_soc_put_volsw_2r - double mixer set callback
+ * @kcontrol: mixer control
+ * @uinfo: control element information
+ *
+ * Callback to set the value of a double mixer control that spans 2 registers.
+ *
+ * Returns 0 for success.
+ */
+int snd_soc_put_volsw_2r(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol);
+ int reg = kcontrol->private_value & 0xff;
+ int reg2 = (kcontrol->private_value >> 24) & 0xff;
+ int shift = (kcontrol->private_value >> 8) & 0x0f;
+ int mask = (kcontrol->private_value >> 12) & 0xff;
+ int invert = (kcontrol->private_value >> 20) & 0x01;
+ int err;
+ unsigned short val, val2, val_mask;
+
+ val_mask = mask << shift;
+ val = (ucontrol->value.integer.value[0] & mask);
+ val2 = (ucontrol->value.integer.value[1] & mask);
+
+ if (invert) {
+ val = mask - val;
+ val2 = mask - val2;
+ }
+
+ val = val << shift;
+ val2 = val2 << shift;
+
+ if ((err = snd_soc_update_bits(codec, reg, val_mask, val)) < 0)
+ return err;
+
+ err = snd_soc_update_bits(codec, reg2, val_mask, val2);
+ return err;
+}
+EXPORT_SYMBOL_GPL(snd_soc_put_volsw_2r);
+
+static int __devinit snd_soc_init(void)
+{
+ printk(KERN_INFO "ASoC version %s\n", SND_SOC_VERSION);
+ return platform_driver_register(&soc_driver);
+}
+
+static void snd_soc_exit(void)
+{
+ platform_driver_unregister(&soc_driver);
+}
+
+module_init(snd_soc_init);
+module_exit(snd_soc_exit);
+
+/* Module information */
+MODULE_AUTHOR("Liam Girdwood, liam.girdwood@wolfsonmicro.com, www.wolfsonmicro.com");
+MODULE_DESCRIPTION("ALSA SoC Core");
+MODULE_LICENSE("GPL");
diff --git a/sound/soc/soc-dapm.c b/sound/soc/soc-dapm.c
new file mode 100644
index 000000000000..7caf8c7b0ac5
--- /dev/null
+++ b/sound/soc/soc-dapm.c
@@ -0,0 +1,1323 @@
+/*
+ * soc-dapm.c -- ALSA SoC Dynamic Audio Power Management
+ *
+ * Copyright 2005 Wolfson Microelectronics PLC.
+ * Author: Liam Girdwood
+ * liam.girdwood@wolfsonmicro.com or linux@wolfsonmicro.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.
+ *
+ * Revision history
+ * 12th Aug 2005 Initial version.
+ * 25th Oct 2005 Implemented path power domain.
+ * 18th Dec 2005 Implemented machine and stream level power domain.
+ *
+ * Features:
+ * o Changes power status of internal codec blocks depending on the
+ * dynamic configuration of codec internal audio paths and active
+ * DAC's/ADC's.
+ * o Platform power domain - can support external components i.e. amps and
+ * mic/meadphone insertion events.
+ * o Automatic Mic Bias support
+ * o Jack insertion power event initiation - e.g. hp insertion will enable
+ * sinks, dacs, etc
+ * o Delayed powerdown of audio susbsytem to reduce pops between a quick
+ * device reopen.
+ *
+ * Todo:
+ * o DAPM power change sequencing - allow for configurable per
+ * codec sequences.
+ * o Support for analogue bias optimisation.
+ * o Support for reduced codec oversampling rates.
+ * o Support for reduced codec bias currents.
+ */
+
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/init.h>
+#include <linux/delay.h>
+#include <linux/pm.h>
+#include <linux/bitops.h>
+#include <linux/platform_device.h>
+#include <linux/jiffies.h>
+#include <sound/driver.h>
+#include <sound/core.h>
+#include <sound/pcm.h>
+#include <sound/pcm_params.h>
+#include <sound/soc-dapm.h>
+#include <sound/initval.h>
+
+/* debug */
+#define DAPM_DEBUG 0
+#if DAPM_DEBUG
+#define dump_dapm(codec, action) dbg_dump_dapm(codec, action)
+#define dbg(format, arg...) printk(format, ## arg)
+#else
+#define dump_dapm(codec, action)
+#define dbg(format, arg...)
+#endif
+
+#define POP_DEBUG 0
+#if POP_DEBUG
+#define POP_TIME 500 /* 500 msecs - change if pop debug is too fast */
+#define pop_wait(time) schedule_timeout_interruptible(msecs_to_jiffies(time))
+#define pop_dbg(format, arg...) printk(format, ## arg); pop_wait(POP_TIME)
+#else
+#define pop_dbg(format, arg...)
+#define pop_wait(time)
+#endif
+
+/* dapm power sequences - make this per codec in the future */
+static int dapm_up_seq[] = {
+ snd_soc_dapm_pre, snd_soc_dapm_micbias, snd_soc_dapm_mic,
+ snd_soc_dapm_mux, snd_soc_dapm_dac, snd_soc_dapm_mixer, snd_soc_dapm_pga,
+ snd_soc_dapm_adc, snd_soc_dapm_hp, snd_soc_dapm_spk, snd_soc_dapm_post
+};
+static int dapm_down_seq[] = {
+ snd_soc_dapm_pre, snd_soc_dapm_adc, snd_soc_dapm_hp, snd_soc_dapm_spk,
+ snd_soc_dapm_pga, snd_soc_dapm_mixer, snd_soc_dapm_dac, snd_soc_dapm_mic,
+ snd_soc_dapm_micbias, snd_soc_dapm_mux, snd_soc_dapm_post
+};
+
+static int dapm_status = 1;
+module_param(dapm_status, int, 0);
+MODULE_PARM_DESC(dapm_status, "enable DPM sysfs entries");
+
+/* create a new dapm widget */
+static inline struct snd_soc_dapm_widget *dapm_cnew_widget(
+ const struct snd_soc_dapm_widget *_widget)
+{
+ return kmemdup(_widget, sizeof(*_widget), GFP_KERNEL);
+}
+
+/* set up initial codec paths */
+static void dapm_set_path_status(struct snd_soc_dapm_widget *w,
+ struct snd_soc_dapm_path *p, int i)
+{
+ switch (w->id) {
+ case snd_soc_dapm_switch:
+ case snd_soc_dapm_mixer: {
+ int val;
+ int reg = w->kcontrols[i].private_value & 0xff;
+ int shift = (w->kcontrols[i].private_value >> 8) & 0x0f;
+ int mask = (w->kcontrols[i].private_value >> 16) & 0xff;
+ int invert = (w->kcontrols[i].private_value >> 24) & 0x01;
+
+ val = snd_soc_read(w->codec, reg);
+ val = (val >> shift) & mask;
+
+ if ((invert && !val) || (!invert && val))
+ p->connect = 1;
+ else
+ p->connect = 0;
+ }
+ break;
+ case snd_soc_dapm_mux: {
+ struct soc_enum *e = (struct soc_enum *)w->kcontrols[i].private_value;
+ int val, item, bitmask;
+
+ for (bitmask = 1; bitmask < e->mask; bitmask <<= 1)
+ ;
+ val = snd_soc_read(w->codec, e->reg);
+ item = (val >> e->shift_l) & (bitmask - 1);
+
+ p->connect = 0;
+ for (i = 0; i < e->mask; i++) {
+ if (!(strcmp(p->name, e->texts[i])) && item == i)
+ p->connect = 1;
+ }
+ }
+ break;
+ /* does not effect routing - always connected */
+ case snd_soc_dapm_pga:
+ case snd_soc_dapm_output:
+ case snd_soc_dapm_adc:
+ case snd_soc_dapm_input:
+ case snd_soc_dapm_dac:
+ case snd_soc_dapm_micbias:
+ case snd_soc_dapm_vmid:
+ p->connect = 1;
+ break;
+ /* does effect routing - dynamically connected */
+ case snd_soc_dapm_hp:
+ case snd_soc_dapm_mic:
+ case snd_soc_dapm_spk:
+ case snd_soc_dapm_line:
+ case snd_soc_dapm_pre:
+ case snd_soc_dapm_post:
+ p->connect = 0;
+ break;
+ }
+}
+
+/* connect mux widget to it's interconnecting audio paths */
+static int dapm_connect_mux(struct snd_soc_codec *codec,
+ struct snd_soc_dapm_widget *src, struct snd_soc_dapm_widget *dest,
+ struct snd_soc_dapm_path *path, const char *control_name,
+ const struct snd_kcontrol_new *kcontrol)
+{
+ struct soc_enum *e = (struct soc_enum *)kcontrol->private_value;
+ int i;
+
+ for (i = 0; i < e->mask; i++) {
+ if (!(strcmp(control_name, e->texts[i]))) {
+ list_add(&path->list, &codec->dapm_paths);
+ list_add(&path->list_sink, &dest->sources);
+ list_add(&path->list_source, &src->sinks);
+ path->name = (char*)e->texts[i];
+ dapm_set_path_status(dest, path, 0);
+ return 0;
+ }
+ }
+
+ return -ENODEV;
+}
+
+/* connect mixer widget to it's interconnecting audio paths */
+static int dapm_connect_mixer(struct snd_soc_codec *codec,
+ struct snd_soc_dapm_widget *src, struct snd_soc_dapm_widget *dest,
+ struct snd_soc_dapm_path *path, const char *control_name)
+{
+ int i;
+
+ /* search for mixer kcontrol */
+ for (i = 0; i < dest->num_kcontrols; i++) {
+ if (!strcmp(control_name, dest->kcontrols[i].name)) {
+ list_add(&path->list, &codec->dapm_paths);
+ list_add(&path->list_sink, &dest->sources);
+ list_add(&path->list_source, &src->sinks);
+ path->name = dest->kcontrols[i].name;
+ dapm_set_path_status(dest, path, i);
+ return 0;
+ }
+ }
+ return -ENODEV;
+}
+
+/* update dapm codec register bits */
+static int dapm_update_bits(struct snd_soc_dapm_widget *widget)
+{
+ int change, power;
+ unsigned short old, new;
+ struct snd_soc_codec *codec = widget->codec;
+
+ /* check for valid widgets */
+ if (widget->reg < 0 || widget->id == snd_soc_dapm_input ||
+ widget->id == snd_soc_dapm_output ||
+ widget->id == snd_soc_dapm_hp ||
+ widget->id == snd_soc_dapm_mic ||
+ widget->id == snd_soc_dapm_line ||
+ widget->id == snd_soc_dapm_spk)
+ return 0;
+
+ power = widget->power;
+ if (widget->invert)
+ power = (power ? 0:1);
+
+ old = snd_soc_read(codec, widget->reg);
+ new = (old & ~(0x1 << widget->shift)) | (power << widget->shift);
+
+ change = old != new;
+ if (change) {
+ pop_dbg("pop test %s : %s in %d ms\n", widget->name,
+ widget->power ? "on" : "off", POP_TIME);
+ snd_soc_write(codec, widget->reg, new);
+ pop_wait(POP_TIME);
+ }
+ dbg("reg old %x new %x change %d\n", old, new, change);
+ return change;
+}
+
+/* ramps the volume up or down to minimise pops before or after a
+ * DAPM power event */
+static int dapm_set_pga(struct snd_soc_dapm_widget *widget, int power)
+{
+ const struct snd_kcontrol_new *k = widget->kcontrols;
+
+ if (widget->muted && !power)
+ return 0;
+ if (!widget->muted && power)
+ return 0;
+
+ if (widget->num_kcontrols && k) {
+ int reg = k->private_value & 0xff;
+ int shift = (k->private_value >> 8) & 0x0f;
+ int mask = (k->private_value >> 16) & 0xff;
+ int invert = (k->private_value >> 24) & 0x01;
+
+ if (power) {
+ int i;
+ /* power up has happended, increase volume to last level */
+ if (invert) {
+ for (i = mask; i > widget->saved_value; i--)
+ snd_soc_update_bits(widget->codec, reg, mask, i);
+ } else {
+ for (i = 0; i < widget->saved_value; i++)
+ snd_soc_update_bits(widget->codec, reg, mask, i);
+ }
+ widget->muted = 0;
+ } else {
+ /* power down is about to occur, decrease volume to mute */
+ int val = snd_soc_read(widget->codec, reg);
+ int i = widget->saved_value = (val >> shift) & mask;
+ if (invert) {
+ for (; i < mask; i++)
+ snd_soc_update_bits(widget->codec, reg, mask, i);
+ } else {
+ for (; i > 0; i--)
+ snd_soc_update_bits(widget->codec, reg, mask, i);
+ }
+ widget->muted = 1;
+ }
+ }
+ return 0;
+}
+
+/* create new dapm mixer control */
+static int dapm_new_mixer(struct snd_soc_codec *codec,
+ struct snd_soc_dapm_widget *w)
+{
+ int i, ret = 0;
+ char name[32];
+ struct snd_soc_dapm_path *path;
+
+ /* add kcontrol */
+ for (i = 0; i < w->num_kcontrols; i++) {
+
+ /* match name */
+ list_for_each_entry(path, &w->sources, list_sink) {
+
+ /* mixer/mux paths name must match control name */
+ if (path->name != (char*)w->kcontrols[i].name)
+ continue;
+
+ /* add dapm control with long name */
+ snprintf(name, 32, "%s %s", w->name, w->kcontrols[i].name);
+ path->long_name = kstrdup (name, GFP_KERNEL);
+ if (path->long_name == NULL)
+ return -ENOMEM;
+
+ path->kcontrol = snd_soc_cnew(&w->kcontrols[i], w,
+ path->long_name);
+ ret = snd_ctl_add(codec->card, path->kcontrol);
+ if (ret < 0) {
+ printk(KERN_ERR "asoc: failed to add dapm kcontrol %s\n",
+ path->long_name);
+ kfree(path->long_name);
+ path->long_name = NULL;
+ return ret;
+ }
+ }
+ }
+ return ret;
+}
+
+/* create new dapm mux control */
+static int dapm_new_mux(struct snd_soc_codec *codec,
+ struct snd_soc_dapm_widget *w)
+{
+ struct snd_soc_dapm_path *path = NULL;
+ struct snd_kcontrol *kcontrol;
+ int ret = 0;
+
+ if (!w->num_kcontrols) {
+ printk(KERN_ERR "asoc: mux %s has no controls\n", w->name);
+ return -EINVAL;
+ }
+
+ kcontrol = snd_soc_cnew(&w->kcontrols[0], w, w->name);
+ ret = snd_ctl_add(codec->card, kcontrol);
+ if (ret < 0)
+ goto err;
+
+ list_for_each_entry(path, &w->sources, list_sink)
+ path->kcontrol = kcontrol;
+
+ return ret;
+
+err:
+ printk(KERN_ERR "asoc: failed to add kcontrol %s\n", w->name);
+ return ret;
+}
+
+/* create new dapm volume control */
+static int dapm_new_pga(struct snd_soc_codec *codec,
+ struct snd_soc_dapm_widget *w)
+{
+ struct snd_kcontrol *kcontrol;
+ int ret = 0;
+
+ if (!w->num_kcontrols)
+ return -EINVAL;
+
+ kcontrol = snd_soc_cnew(&w->kcontrols[0], w, w->name);
+ ret = snd_ctl_add(codec->card, kcontrol);
+ if (ret < 0) {
+ printk(KERN_ERR "asoc: failed to add kcontrol %s\n", w->name);
+ return ret;
+ }
+
+ return ret;
+}
+
+/* reset 'walked' bit for each dapm path */
+static inline void dapm_clear_walk(struct snd_soc_codec *codec)
+{
+ struct snd_soc_dapm_path *p;
+
+ list_for_each_entry(p, &codec->dapm_paths, list)
+ p->walked = 0;
+}
+
+/*
+ * Recursively check for a completed path to an active or physically connected
+ * output widget. Returns number of complete paths.
+ */
+static int is_connected_output_ep(struct snd_soc_dapm_widget *widget)
+{
+ struct snd_soc_dapm_path *path;
+ int con = 0;
+
+ if (widget->id == snd_soc_dapm_adc && widget->active)
+ return 1;
+
+ if (widget->connected) {
+ /* connected pin ? */
+ if (widget->id == snd_soc_dapm_output && !widget->ext)
+ return 1;
+
+ /* connected jack or spk ? */
+ if (widget->id == snd_soc_dapm_hp || widget->id == snd_soc_dapm_spk ||
+ widget->id == snd_soc_dapm_line)
+ return 1;
+ }
+
+ list_for_each_entry(path, &widget->sinks, list_source) {
+ if (path->walked)
+ continue;
+
+ if (path->sink && path->connect) {
+ path->walked = 1;
+ con += is_connected_output_ep(path->sink);
+ }
+ }
+
+ return con;
+}
+
+/*
+ * Recursively check for a completed path to an active or physically connected
+ * input widget. Returns number of complete paths.
+ */
+static int is_connected_input_ep(struct snd_soc_dapm_widget *widget)
+{
+ struct snd_soc_dapm_path *path;
+ int con = 0;
+
+ /* active stream ? */
+ if (widget->id == snd_soc_dapm_dac && widget->active)
+ return 1;
+
+ if (widget->connected) {
+ /* connected pin ? */
+ if (widget->id == snd_soc_dapm_input && !widget->ext)
+ return 1;
+
+ /* connected VMID/Bias for lower pops */
+ if (widget->id == snd_soc_dapm_vmid)
+ return 1;
+
+ /* connected jack ? */
+ if (widget->id == snd_soc_dapm_mic || widget->id == snd_soc_dapm_line)
+ return 1;
+ }
+
+ list_for_each_entry(path, &widget->sources, list_sink) {
+ if (path->walked)
+ continue;
+
+ if (path->source && path->connect) {
+ path->walked = 1;
+ con += is_connected_input_ep(path->source);
+ }
+ }
+
+ return con;
+}
+
+/*
+ * Scan each dapm widget for complete audio path.
+ * A complete path is a route that has valid endpoints i.e.:-
+ *
+ * o DAC to output pin.
+ * o Input Pin to ADC.
+ * o Input pin to Output pin (bypass, sidetone)
+ * o DAC to ADC (loopback).
+ */
+static int dapm_power_widgets(struct snd_soc_codec *codec, int event)
+{
+ struct snd_soc_dapm_widget *w;
+ int in, out, i, c = 1, *seq = NULL, ret = 0, power_change, power;
+
+ /* do we have a sequenced stream event */
+ if (event == SND_SOC_DAPM_STREAM_START) {
+ c = ARRAY_SIZE(dapm_up_seq);
+ seq = dapm_up_seq;
+ } else if (event == SND_SOC_DAPM_STREAM_STOP) {
+ c = ARRAY_SIZE(dapm_down_seq);
+ seq = dapm_down_seq;
+ }
+
+ for(i = 0; i < c; i++) {
+ list_for_each_entry(w, &codec->dapm_widgets, list) {
+
+ /* is widget in stream order */
+ if (seq && seq[i] && w->id != seq[i])
+ continue;
+
+ /* vmid - no action */
+ if (w->id == snd_soc_dapm_vmid)
+ continue;
+
+ /* active ADC */
+ if (w->id == snd_soc_dapm_adc && w->active) {
+ in = is_connected_input_ep(w);
+ dapm_clear_walk(w->codec);
+ w->power = (in != 0) ? 1 : 0;
+ dapm_update_bits(w);
+ continue;
+ }
+
+ /* active DAC */
+ if (w->id == snd_soc_dapm_dac && w->active) {
+ out = is_connected_output_ep(w);
+ dapm_clear_walk(w->codec);
+ w->power = (out != 0) ? 1 : 0;
+ dapm_update_bits(w);
+ continue;
+ }
+
+ /* programmable gain/attenuation */
+ if (w->id == snd_soc_dapm_pga) {
+ int on;
+ in = is_connected_input_ep(w);
+ dapm_clear_walk(w->codec);
+ out = is_connected_output_ep(w);
+ dapm_clear_walk(w->codec);
+ w->power = on = (out != 0 && in != 0) ? 1 : 0;
+
+ if (!on)
+ dapm_set_pga(w, on); /* lower volume to reduce pops */
+ dapm_update_bits(w);
+ if (on)
+ dapm_set_pga(w, on); /* restore volume from zero */
+
+ continue;
+ }
+
+ /* pre and post event widgets */
+ if (w->id == snd_soc_dapm_pre) {
+ if (!w->event)
+ continue;
+
+ if (event == SND_SOC_DAPM_STREAM_START) {
+ ret = w->event(w, SND_SOC_DAPM_PRE_PMU);
+ if (ret < 0)
+ return ret;
+ } else if (event == SND_SOC_DAPM_STREAM_STOP) {
+ ret = w->event(w, SND_SOC_DAPM_PRE_PMD);
+ if (ret < 0)
+ return ret;
+ }
+ continue;
+ }
+ if (w->id == snd_soc_dapm_post) {
+ if (!w->event)
+ continue;
+
+ if (event == SND_SOC_DAPM_STREAM_START) {
+ ret = w->event(w, SND_SOC_DAPM_POST_PMU);
+ if (ret < 0)
+ return ret;
+ } else if (event == SND_SOC_DAPM_STREAM_STOP) {
+ ret = w->event(w, SND_SOC_DAPM_POST_PMD);
+ if (ret < 0)
+ return ret;
+ }
+ continue;
+ }
+
+ /* all other widgets */
+ in = is_connected_input_ep(w);
+ dapm_clear_walk(w->codec);
+ out = is_connected_output_ep(w);
+ dapm_clear_walk(w->codec);
+ power = (out != 0 && in != 0) ? 1 : 0;
+ power_change = (w->power == power) ? 0: 1;
+ w->power = power;
+
+ /* call any power change event handlers */
+ if (power_change) {
+ if (w->event) {
+ dbg("power %s event for %s flags %x\n",
+ w->power ? "on" : "off", w->name, w->event_flags);
+ if (power) {
+ /* power up event */
+ if (w->event_flags & SND_SOC_DAPM_PRE_PMU) {
+ ret = w->event(w, SND_SOC_DAPM_PRE_PMU);
+ if (ret < 0)
+ return ret;
+ }
+ dapm_update_bits(w);
+ if (w->event_flags & SND_SOC_DAPM_POST_PMU){
+ ret = w->event(w, SND_SOC_DAPM_POST_PMU);
+ if (ret < 0)
+ return ret;
+ }
+ } else {
+ /* power down event */
+ if (w->event_flags & SND_SOC_DAPM_PRE_PMD) {
+ ret = w->event(w, SND_SOC_DAPM_PRE_PMD);
+ if (ret < 0)
+ return ret;
+ }
+ dapm_update_bits(w);
+ if (w->event_flags & SND_SOC_DAPM_POST_PMD) {
+ ret = w->event(w, SND_SOC_DAPM_POST_PMD);
+ if (ret < 0)
+ return ret;
+ }
+ }
+ } else
+ /* no event handler */
+ dapm_update_bits(w);
+ }
+ }
+ }
+
+ return ret;
+}
+
+#if DAPM_DEBUG
+static void dbg_dump_dapm(struct snd_soc_codec* codec, const char *action)
+{
+ struct snd_soc_dapm_widget *w;
+ struct snd_soc_dapm_path *p = NULL;
+ int in, out;
+
+ printk("DAPM %s %s\n", codec->name, action);
+
+ list_for_each_entry(w, &codec->dapm_widgets, list) {
+
+ /* only display widgets that effect routing */
+ switch (w->id) {
+ case snd_soc_dapm_pre:
+ case snd_soc_dapm_post:
+ case snd_soc_dapm_vmid:
+ continue;
+ case snd_soc_dapm_mux:
+ case snd_soc_dapm_output:
+ case snd_soc_dapm_input:
+ case snd_soc_dapm_switch:
+ case snd_soc_dapm_hp:
+ case snd_soc_dapm_mic:
+ case snd_soc_dapm_spk:
+ case snd_soc_dapm_line:
+ case snd_soc_dapm_micbias:
+ case snd_soc_dapm_dac:
+ case snd_soc_dapm_adc:
+ case snd_soc_dapm_pga:
+ case snd_soc_dapm_mixer:
+ if (w->name) {
+ in = is_connected_input_ep(w);
+ dapm_clear_walk(w->codec);
+ out = is_connected_output_ep(w);
+ dapm_clear_walk(w->codec);
+ printk("%s: %s in %d out %d\n", w->name,
+ w->power ? "On":"Off",in, out);
+
+ list_for_each_entry(p, &w->sources, list_sink) {
+ if (p->connect)
+ printk(" in %s %s\n", p->name ? p->name : "static",
+ p->source->name);
+ }
+ list_for_each_entry(p, &w->sinks, list_source) {
+ if (p->connect)
+ printk(" out %s %s\n", p->name ? p->name : "static",
+ p->sink->name);
+ }
+ }
+ break;
+ }
+ }
+}
+#endif
+
+/* test and update the power status of a mux widget */
+static int dapm_mux_update_power(struct snd_soc_dapm_widget *widget,
+ struct snd_kcontrol *kcontrol, int mask,
+ int val, struct soc_enum* e)
+{
+ struct snd_soc_dapm_path *path;
+ int found = 0;
+
+ if (widget->id != snd_soc_dapm_mux)
+ return -ENODEV;
+
+ if (!snd_soc_test_bits(widget->codec, e->reg, mask, val))
+ return 0;
+
+ /* find dapm widget path assoc with kcontrol */
+ list_for_each_entry(path, &widget->codec->dapm_paths, list) {
+ if (path->kcontrol != kcontrol)
+ continue;
+
+ if (!path->name || ! e->texts[val])
+ continue;
+
+ found = 1;
+ /* we now need to match the string in the enum to the path */
+ if (!(strcmp(path->name, e->texts[val])))
+ path->connect = 1; /* new connection */
+ else
+ path->connect = 0; /* old connection must be powered down */
+ }
+
+ if (found)
+ dapm_power_widgets(widget->codec, SND_SOC_DAPM_STREAM_NOP);
+
+ return 0;
+}
+
+/* test and update the power status of a mixer widget */
+static int dapm_mixer_update_power(struct snd_soc_dapm_widget *widget,
+ struct snd_kcontrol *kcontrol, int reg,
+ int val_mask, int val, int invert)
+{
+ struct snd_soc_dapm_path *path;
+ int found = 0;
+
+ if (widget->id != snd_soc_dapm_mixer)
+ return -ENODEV;
+
+ if (!snd_soc_test_bits(widget->codec, reg, val_mask, val))
+ return 0;
+
+ /* find dapm widget path assoc with kcontrol */
+ list_for_each_entry(path, &widget->codec->dapm_paths, list) {
+ if (path->kcontrol != kcontrol)
+ continue;
+
+ /* found, now check type */
+ found = 1;
+ if (val)
+ /* new connection */
+ path->connect = invert ? 0:1;
+ else
+ /* old connection must be powered down */
+ path->connect = invert ? 1:0;
+ break;
+ }
+
+ if (found)
+ dapm_power_widgets(widget->codec, SND_SOC_DAPM_STREAM_NOP);
+
+ return 0;
+}
+
+/* show dapm widget status in sys fs */
+static ssize_t dapm_widget_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct snd_soc_device *devdata = dev_get_drvdata(dev);
+ struct snd_soc_codec *codec = devdata->codec;
+ struct snd_soc_dapm_widget *w;
+ int count = 0;
+ char *state = "not set";
+
+ list_for_each_entry(w, &codec->dapm_widgets, list) {
+
+ /* only display widgets that burnm power */
+ switch (w->id) {
+ case snd_soc_dapm_hp:
+ case snd_soc_dapm_mic:
+ case snd_soc_dapm_spk:
+ case snd_soc_dapm_line:
+ case snd_soc_dapm_micbias:
+ case snd_soc_dapm_dac:
+ case snd_soc_dapm_adc:
+ case snd_soc_dapm_pga:
+ case snd_soc_dapm_mixer:
+ if (w->name)
+ count += sprintf(buf + count, "%s: %s\n",
+ w->name, w->power ? "On":"Off");
+ break;
+ default:
+ break;
+ }
+ }
+
+ switch(codec->dapm_state){
+ case SNDRV_CTL_POWER_D0:
+ state = "D0";
+ break;
+ case SNDRV_CTL_POWER_D1:
+ state = "D1";
+ break;
+ case SNDRV_CTL_POWER_D2:
+ state = "D2";
+ break;
+ case SNDRV_CTL_POWER_D3hot:
+ state = "D3hot";
+ break;
+ case SNDRV_CTL_POWER_D3cold:
+ state = "D3cold";
+ break;
+ }
+ count += sprintf(buf + count, "PM State: %s\n", state);
+
+ return count;
+}
+
+static DEVICE_ATTR(dapm_widget, 0444, dapm_widget_show, NULL);
+
+int snd_soc_dapm_sys_add(struct device *dev)
+{
+ int ret = 0;
+
+ if (dapm_status)
+ ret = device_create_file(dev, &dev_attr_dapm_widget);
+
+ return ret;
+}
+
+static void snd_soc_dapm_sys_remove(struct device *dev)
+{
+ if (dapm_status)
+ device_remove_file(dev, &dev_attr_dapm_widget);
+}
+
+/* free all dapm widgets and resources */
+static void dapm_free_widgets(struct snd_soc_codec *codec)
+{
+ struct snd_soc_dapm_widget *w, *next_w;
+ struct snd_soc_dapm_path *p, *next_p;
+
+ list_for_each_entry_safe(w, next_w, &codec->dapm_widgets, list) {
+ list_del(&w->list);
+ kfree(w);
+ }
+
+ list_for_each_entry_safe(p, next_p, &codec->dapm_paths, list) {
+ list_del(&p->list);
+ kfree(p->long_name);
+ kfree(p);
+ }
+}
+
+/**
+ * snd_soc_dapm_sync_endpoints - scan and power dapm paths
+ * @codec: audio codec
+ *
+ * Walks all dapm audio paths and powers widgets according to their
+ * stream or path usage.
+ *
+ * Returns 0 for success.
+ */
+int snd_soc_dapm_sync_endpoints(struct snd_soc_codec *codec)
+{
+ return dapm_power_widgets(codec, SND_SOC_DAPM_STREAM_NOP);
+}
+EXPORT_SYMBOL_GPL(snd_soc_dapm_sync_endpoints);
+
+/**
+ * snd_soc_dapm_connect_input - connect dapm widgets
+ * @codec: audio codec
+ * @sink: name of target widget
+ * @control: mixer control name
+ * @source: name of source name
+ *
+ * Connects 2 dapm widgets together via a named audio path. The sink is
+ * the widget receiving the audio signal, whilst the source is the sender
+ * of the audio signal.
+ *
+ * Returns 0 for success else error.
+ */
+int snd_soc_dapm_connect_input(struct snd_soc_codec *codec, const char *sink,
+ const char * control, const char *source)
+{
+ struct snd_soc_dapm_path *path;
+ struct snd_soc_dapm_widget *wsource = NULL, *wsink = NULL, *w;
+ int ret = 0;
+
+ /* find src and dest widgets */
+ list_for_each_entry(w, &codec->dapm_widgets, list) {
+
+ if (!wsink && !(strcmp(w->name, sink))) {
+ wsink = w;
+ continue;
+ }
+ if (!wsource && !(strcmp(w->name, source))) {
+ wsource = w;
+ }
+ }
+
+ if (wsource == NULL || wsink == NULL)
+ return -ENODEV;
+
+ path = kzalloc(sizeof(struct snd_soc_dapm_path), GFP_KERNEL);
+ if (!path)
+ return -ENOMEM;
+
+ path->source = wsource;
+ path->sink = wsink;
+ INIT_LIST_HEAD(&path->list);
+ INIT_LIST_HEAD(&path->list_source);
+ INIT_LIST_HEAD(&path->list_sink);
+
+ /* check for external widgets */
+ if (wsink->id == snd_soc_dapm_input) {
+ if (wsource->id == snd_soc_dapm_micbias ||
+ wsource->id == snd_soc_dapm_mic ||
+ wsink->id == snd_soc_dapm_line)
+ wsink->ext = 1;
+ }
+ if (wsource->id == snd_soc_dapm_output) {
+ if (wsink->id == snd_soc_dapm_spk ||
+ wsink->id == snd_soc_dapm_hp ||
+ wsink->id == snd_soc_dapm_line)
+ wsource->ext = 1;
+ }
+
+ /* connect static paths */
+ if (control == NULL) {
+ list_add(&path->list, &codec->dapm_paths);
+ list_add(&path->list_sink, &wsink->sources);
+ list_add(&path->list_source, &wsource->sinks);
+ path->connect = 1;
+ return 0;
+ }
+
+ /* connect dynamic paths */
+ switch(wsink->id) {
+ case snd_soc_dapm_adc:
+ case snd_soc_dapm_dac:
+ case snd_soc_dapm_pga:
+ case snd_soc_dapm_input:
+ case snd_soc_dapm_output:
+ case snd_soc_dapm_micbias:
+ case snd_soc_dapm_vmid:
+ case snd_soc_dapm_pre:
+ case snd_soc_dapm_post:
+ list_add(&path->list, &codec->dapm_paths);
+ list_add(&path->list_sink, &wsink->sources);
+ list_add(&path->list_source, &wsource->sinks);
+ path->connect = 1;
+ return 0;
+ case snd_soc_dapm_mux:
+ ret = dapm_connect_mux(codec, wsource, wsink, path, control,
+ &wsink->kcontrols[0]);
+ if (ret != 0)
+ goto err;
+ break;
+ case snd_soc_dapm_switch:
+ case snd_soc_dapm_mixer:
+ ret = dapm_connect_mixer(codec, wsource, wsink, path, control);
+ if (ret != 0)
+ goto err;
+ break;
+ case snd_soc_dapm_hp:
+ case snd_soc_dapm_mic:
+ case snd_soc_dapm_line:
+ case snd_soc_dapm_spk:
+ list_add(&path->list, &codec->dapm_paths);
+ list_add(&path->list_sink, &wsink->sources);
+ list_add(&path->list_source, &wsource->sinks);
+ path->connect = 0;
+ return 0;
+ }
+ return 0;
+
+err:
+ printk(KERN_WARNING "asoc: no dapm match for %s --> %s --> %s\n", source,
+ control, sink);
+ kfree(path);
+ return ret;
+}
+EXPORT_SYMBOL_GPL(snd_soc_dapm_connect_input);
+
+/**
+ * snd_soc_dapm_new_widgets - add new dapm widgets
+ * @codec: audio codec
+ *
+ * Checks the codec for any new dapm widgets and creates them if found.
+ *
+ * Returns 0 for success.
+ */
+int snd_soc_dapm_new_widgets(struct snd_soc_codec *codec)
+{
+ struct snd_soc_dapm_widget *w;
+
+ mutex_lock(&codec->mutex);
+ list_for_each_entry(w, &codec->dapm_widgets, list)
+ {
+ if (w->new)
+ continue;
+
+ switch(w->id) {
+ case snd_soc_dapm_switch:
+ case snd_soc_dapm_mixer:
+ dapm_new_mixer(codec, w);
+ break;
+ case snd_soc_dapm_mux:
+ dapm_new_mux(codec, w);
+ break;
+ case snd_soc_dapm_adc:
+ case snd_soc_dapm_dac:
+ case snd_soc_dapm_pga:
+ dapm_new_pga(codec, w);
+ break;
+ case snd_soc_dapm_input:
+ case snd_soc_dapm_output:
+ case snd_soc_dapm_micbias:
+ case snd_soc_dapm_spk:
+ case snd_soc_dapm_hp:
+ case snd_soc_dapm_mic:
+ case snd_soc_dapm_line:
+ case snd_soc_dapm_vmid:
+ case snd_soc_dapm_pre:
+ case snd_soc_dapm_post:
+ break;
+ }
+ w->new = 1;
+ }
+
+ dapm_power_widgets(codec, SND_SOC_DAPM_STREAM_NOP);
+ mutex_unlock(&codec->mutex);
+ return 0;
+}
+EXPORT_SYMBOL_GPL(snd_soc_dapm_new_widgets);
+
+/**
+ * snd_soc_dapm_get_volsw - dapm mixer get callback
+ * @kcontrol: mixer control
+ * @uinfo: control element information
+ *
+ * Callback to get the value of a dapm mixer control.
+ *
+ * Returns 0 for success.
+ */
+int snd_soc_dapm_get_volsw(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_dapm_widget *widget = snd_kcontrol_chip(kcontrol);
+ int reg = kcontrol->private_value & 0xff;
+ int shift = (kcontrol->private_value >> 8) & 0x0f;
+ int rshift = (kcontrol->private_value >> 12) & 0x0f;
+ int mask = (kcontrol->private_value >> 16) & 0xff;
+ int invert = (kcontrol->private_value >> 24) & 0x01;
+
+ /* return the saved value if we are powered down */
+ if (widget->id == snd_soc_dapm_pga && !widget->power) {
+ ucontrol->value.integer.value[0] = widget->saved_value;
+ return 0;
+ }
+
+ ucontrol->value.integer.value[0] =
+ (snd_soc_read(widget->codec, reg) >> shift) & mask;
+ if (shift != rshift)
+ ucontrol->value.integer.value[1] =
+ (snd_soc_read(widget->codec, reg) >> rshift) & mask;
+ if (invert) {
+ ucontrol->value.integer.value[0] =
+ mask - ucontrol->value.integer.value[0];
+ if (shift != rshift)
+ ucontrol->value.integer.value[1] =
+ mask - ucontrol->value.integer.value[1];
+ }
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(snd_soc_dapm_get_volsw);
+
+/**
+ * snd_soc_dapm_put_volsw - dapm mixer set callback
+ * @kcontrol: mixer control
+ * @uinfo: control element information
+ *
+ * Callback to set the value of a dapm mixer control.
+ *
+ * Returns 0 for success.
+ */
+int snd_soc_dapm_put_volsw(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_dapm_widget *widget = snd_kcontrol_chip(kcontrol);
+ int reg = kcontrol->private_value & 0xff;
+ int shift = (kcontrol->private_value >> 8) & 0x0f;
+ int rshift = (kcontrol->private_value >> 12) & 0x0f;
+ int mask = (kcontrol->private_value >> 16) & 0xff;
+ int invert = (kcontrol->private_value >> 24) & 0x01;
+ unsigned short val, val2, val_mask;
+ int ret;
+
+ val = (ucontrol->value.integer.value[0] & mask);
+
+ if (invert)
+ val = mask - val;
+ val_mask = mask << shift;
+ val = val << shift;
+ if (shift != rshift) {
+ val2 = (ucontrol->value.integer.value[1] & mask);
+ if (invert)
+ val2 = mask - val2;
+ val_mask |= mask << rshift;
+ val |= val2 << rshift;
+ }
+
+ mutex_lock(&widget->codec->mutex);
+ widget->value = val;
+
+ /* save volume value if the widget is powered down */
+ if (widget->id == snd_soc_dapm_pga && !widget->power) {
+ widget->saved_value = val;
+ mutex_unlock(&widget->codec->mutex);
+ return 1;
+ }
+
+ dapm_mixer_update_power(widget, kcontrol, reg, val_mask, val, invert);
+ if (widget->event) {
+ if (widget->event_flags & SND_SOC_DAPM_PRE_REG) {
+ ret = widget->event(widget, SND_SOC_DAPM_PRE_REG);
+ if (ret < 0)
+ goto out;
+ }
+ ret = snd_soc_update_bits(widget->codec, reg, val_mask, val);
+ if (widget->event_flags & SND_SOC_DAPM_POST_REG)
+ ret = widget->event(widget, SND_SOC_DAPM_POST_REG);
+ } else
+ ret = snd_soc_update_bits(widget->codec, reg, val_mask, val);
+
+out:
+ mutex_unlock(&widget->codec->mutex);
+ return ret;
+}
+EXPORT_SYMBOL_GPL(snd_soc_dapm_put_volsw);
+
+/**
+ * snd_soc_dapm_get_enum_double - dapm enumerated double mixer get callback
+ * @kcontrol: mixer control
+ * @uinfo: control element information
+ *
+ * Callback to get the value of a dapm enumerated double mixer control.
+ *
+ * Returns 0 for success.
+ */
+int snd_soc_dapm_get_enum_double(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_dapm_widget *widget = snd_kcontrol_chip(kcontrol);
+ struct soc_enum *e = (struct soc_enum *)kcontrol->private_value;
+ unsigned short val, bitmask;
+
+ for (bitmask = 1; bitmask < e->mask; bitmask <<= 1)
+ ;
+ val = snd_soc_read(widget->codec, e->reg);
+ ucontrol->value.enumerated.item[0] = (val >> e->shift_l) & (bitmask - 1);
+ if (e->shift_l != e->shift_r)
+ ucontrol->value.enumerated.item[1] =
+ (val >> e->shift_r) & (bitmask - 1);
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(snd_soc_dapm_get_enum_double);
+
+/**
+ * snd_soc_dapm_put_enum_double - dapm enumerated double mixer set callback
+ * @kcontrol: mixer control
+ * @uinfo: control element information
+ *
+ * Callback to set the value of a dapm enumerated double mixer control.
+ *
+ * Returns 0 for success.
+ */
+int snd_soc_dapm_put_enum_double(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_dapm_widget *widget = snd_kcontrol_chip(kcontrol);
+ struct soc_enum *e = (struct soc_enum *)kcontrol->private_value;
+ unsigned short val, mux;
+ unsigned short mask, bitmask;
+ int ret = 0;
+
+ for (bitmask = 1; bitmask < e->mask; bitmask <<= 1)
+ ;
+ if (ucontrol->value.enumerated.item[0] > e->mask - 1)
+ return -EINVAL;
+ mux = ucontrol->value.enumerated.item[0];
+ val = mux << e->shift_l;
+ mask = (bitmask - 1) << e->shift_l;
+ if (e->shift_l != e->shift_r) {
+ if (ucontrol->value.enumerated.item[1] > e->mask - 1)
+ return -EINVAL;
+ val |= ucontrol->value.enumerated.item[1] << e->shift_r;
+ mask |= (bitmask - 1) << e->shift_r;
+ }
+
+ mutex_lock(&widget->codec->mutex);
+ widget->value = val;
+ dapm_mux_update_power(widget, kcontrol, mask, mux, e);
+ if (widget->event) {
+ if (widget->event_flags & SND_SOC_DAPM_PRE_REG) {
+ ret = widget->event(widget, SND_SOC_DAPM_PRE_REG);
+ if (ret < 0)
+ goto out;
+ }
+ ret = snd_soc_update_bits(widget->codec, e->reg, mask, val);
+ if (widget->event_flags & SND_SOC_DAPM_POST_REG)
+ ret = widget->event(widget, SND_SOC_DAPM_POST_REG);
+ } else
+ ret = snd_soc_update_bits(widget->codec, e->reg, mask, val);
+
+out:
+ mutex_unlock(&widget->codec->mutex);
+ return ret;
+}
+EXPORT_SYMBOL_GPL(snd_soc_dapm_put_enum_double);
+
+/**
+ * snd_soc_dapm_new_control - create new dapm control
+ * @codec: audio codec
+ * @widget: widget template
+ *
+ * Creates a new dapm control based upon the template.
+ *
+ * Returns 0 for success else error.
+ */
+int snd_soc_dapm_new_control(struct snd_soc_codec *codec,
+ const struct snd_soc_dapm_widget *widget)
+{
+ struct snd_soc_dapm_widget *w;
+
+ if ((w = dapm_cnew_widget(widget)) == NULL)
+ return -ENOMEM;
+
+ w->codec = codec;
+ INIT_LIST_HEAD(&w->sources);
+ INIT_LIST_HEAD(&w->sinks);
+ INIT_LIST_HEAD(&w->list);
+ list_add(&w->list, &codec->dapm_widgets);
+
+ /* machine layer set ups unconnected pins and insertions */
+ w->connected = 1;
+ return 0;
+}
+EXPORT_SYMBOL_GPL(snd_soc_dapm_new_control);
+
+/**
+ * snd_soc_dapm_stream_event - send a stream event to the dapm core
+ * @codec: audio codec
+ * @stream: stream name
+ * @event: stream event
+ *
+ * Sends a stream event to the dapm core. The core then makes any
+ * necessary widget power changes.
+ *
+ * Returns 0 for success else error.
+ */
+int snd_soc_dapm_stream_event(struct snd_soc_codec *codec,
+ char *stream, int event)
+{
+ struct snd_soc_dapm_widget *w;
+
+ if (stream == NULL)
+ return 0;
+
+ mutex_lock(&codec->mutex);
+ list_for_each_entry(w, &codec->dapm_widgets, list)
+ {
+ if (!w->sname)
+ continue;
+ dbg("widget %s\n %s stream %s event %d\n", w->name, w->sname,
+ stream, event);
+ if (strstr(w->sname, stream)) {
+ switch(event) {
+ case SND_SOC_DAPM_STREAM_START:
+ w->active = 1;
+ break;
+ case SND_SOC_DAPM_STREAM_STOP:
+ w->active = 0;
+ break;
+ case SND_SOC_DAPM_STREAM_SUSPEND:
+ if (w->active)
+ w->suspend = 1;
+ w->active = 0;
+ break;
+ case SND_SOC_DAPM_STREAM_RESUME:
+ if (w->suspend) {
+ w->active = 1;
+ w->suspend = 0;
+ }
+ break;
+ case SND_SOC_DAPM_STREAM_PAUSE_PUSH:
+ break;
+ case SND_SOC_DAPM_STREAM_PAUSE_RELEASE:
+ break;
+ }
+ }
+ }
+ mutex_unlock(&codec->mutex);
+
+ dapm_power_widgets(codec, event);
+ dump_dapm(codec, __FUNCTION__);
+ return 0;
+}
+EXPORT_SYMBOL_GPL(snd_soc_dapm_stream_event);
+
+/**
+ * snd_soc_dapm_set_endpoint - set audio endpoint status
+ * @codec: audio codec
+ * @endpoint: audio signal endpoint (or start point)
+ * @status: point status
+ *
+ * Set audio endpoint status - connected or disconnected.
+ *
+ * Returns 0 for success else error.
+ */
+int snd_soc_dapm_set_endpoint(struct snd_soc_codec *codec,
+ char *endpoint, int status)
+{
+ struct snd_soc_dapm_widget *w;
+
+ list_for_each_entry(w, &codec->dapm_widgets, list) {
+ if (!strcmp(w->name, endpoint)) {
+ w->connected = status;
+ }
+ }
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(snd_soc_dapm_set_endpoint);
+
+/**
+ * snd_soc_dapm_free - free dapm resources
+ * @socdev: SoC device
+ *
+ * Free all dapm widgets and resources.
+ */
+void snd_soc_dapm_free(struct snd_soc_device *socdev)
+{
+ struct snd_soc_codec *codec = socdev->codec;
+
+ snd_soc_dapm_sys_remove(socdev->dev);
+ dapm_free_widgets(codec);
+}
+EXPORT_SYMBOL_GPL(snd_soc_dapm_free);
+
+/* Module information */
+MODULE_AUTHOR("Liam Girdwood, liam.girdwood@wolfsonmicro.com, www.wolfsonmicro.com");
+MODULE_DESCRIPTION("Dynamic Audio Power Management core for ALSA SoC");
+MODULE_LICENSE("GPL");
diff --git a/sound/sparc/dbri.c b/sound/sparc/dbri.c
index 4ceb09d215d8..25a2a7333006 100644
--- a/sound/sparc/dbri.c
+++ b/sound/sparc/dbri.c
@@ -678,7 +678,7 @@ static s32 *dbri_cmdlock(struct snd_dbri * dbri, int len)
* The JUMP cmd points to the new cmd string.
* It also releases the cmdlock spinlock.
*
- * Lock must not be held before calling this.
+ * Lock must be held before calling this.
*/
static void dbri_cmdsend(struct snd_dbri * dbri, s32 * cmd,int len)
{
diff --git a/sound/usb/usbaudio.c b/sound/usb/usbaudio.c
index 19bdcc74c96c..4dfb91d4398a 100644
--- a/sound/usb/usbaudio.c
+++ b/sound/usb/usbaudio.c
@@ -186,6 +186,7 @@ struct snd_usb_substream {
u64 formats; /* format bitmasks (all or'ed) */
unsigned int num_formats; /* number of supported audio formats (list) */
struct list_head fmt_list; /* format list */
+ struct snd_pcm_hw_constraint_list rate_list; /* limited rates */
spinlock_t lock;
struct snd_urb_ops ops; /* callbacks (must be filled at init) */
@@ -253,7 +254,7 @@ static int prepare_capture_sync_urb(struct snd_usb_substream *subs,
struct urb *urb)
{
unsigned char *cp = urb->transfer_buffer;
- struct snd_urb_ctx *ctx = (struct snd_urb_ctx *)urb->context;
+ struct snd_urb_ctx *ctx = urb->context;
urb->dev = ctx->subs->dev; /* we need to set this at each time */
urb->iso_frame_desc[0].length = 3;
@@ -275,7 +276,7 @@ static int prepare_capture_sync_urb_hs(struct snd_usb_substream *subs,
struct urb *urb)
{
unsigned char *cp = urb->transfer_buffer;
- struct snd_urb_ctx *ctx = (struct snd_urb_ctx *)urb->context;
+ struct snd_urb_ctx *ctx = urb->context;
urb->dev = ctx->subs->dev; /* we need to set this at each time */
urb->iso_frame_desc[0].length = 4;
@@ -313,7 +314,7 @@ static int prepare_capture_urb(struct snd_usb_substream *subs,
struct urb *urb)
{
int i, offs;
- struct snd_urb_ctx *ctx = (struct snd_urb_ctx *)urb->context;
+ struct snd_urb_ctx *ctx = urb->context;
offs = 0;
urb->dev = ctx->subs->dev; /* we need to set this at each time */
@@ -391,6 +392,16 @@ static int retire_capture_urb(struct snd_usb_substream *subs,
return 0;
}
+/*
+ * Process after capture complete when paused. Nothing to do.
+ */
+static int retire_paused_capture_urb(struct snd_usb_substream *subs,
+ struct snd_pcm_runtime *runtime,
+ struct urb *urb)
+{
+ return 0;
+}
+
/*
* prepare urb for full speed playback sync pipe
@@ -402,7 +413,7 @@ static int prepare_playback_sync_urb(struct snd_usb_substream *subs,
struct snd_pcm_runtime *runtime,
struct urb *urb)
{
- struct snd_urb_ctx *ctx = (struct snd_urb_ctx *)urb->context;
+ struct snd_urb_ctx *ctx = urb->context;
urb->dev = ctx->subs->dev; /* we need to set this at each time */
urb->iso_frame_desc[0].length = 3;
@@ -420,7 +431,7 @@ static int prepare_playback_sync_urb_hs(struct snd_usb_substream *subs,
struct snd_pcm_runtime *runtime,
struct urb *urb)
{
- struct snd_urb_ctx *ctx = (struct snd_urb_ctx *)urb->context;
+ struct snd_urb_ctx *ctx = urb->context;
urb->dev = ctx->subs->dev; /* we need to set this at each time */
urb->iso_frame_desc[0].length = 4;
@@ -493,13 +504,13 @@ static int snd_usb_audio_next_packet_size(struct snd_usb_substream *subs)
}
/*
- * Prepare urb for streaming before playback starts.
+ * Prepare urb for streaming before playback starts or when paused.
*
- * We don't yet have data, so we send a frame of silence.
+ * We don't have any data, so we send a frame of silence.
*/
-static int prepare_startup_playback_urb(struct snd_usb_substream *subs,
- struct snd_pcm_runtime *runtime,
- struct urb *urb)
+static int prepare_nodata_playback_urb(struct snd_usb_substream *subs,
+ struct snd_pcm_runtime *runtime,
+ struct urb *urb)
{
unsigned int i, offs, counts;
struct snd_urb_ctx *ctx = urb->context;
@@ -537,7 +548,7 @@ static int prepare_playback_urb(struct snd_usb_substream *subs,
unsigned int counts;
unsigned long flags;
int period_elapsed = 0;
- struct snd_urb_ctx *ctx = (struct snd_urb_ctx *)urb->context;
+ struct snd_urb_ctx *ctx = urb->context;
stride = runtime->frame_bits >> 3;
@@ -622,7 +633,7 @@ static int retire_playback_urb(struct snd_usb_substream *subs,
*/
static struct snd_urb_ops audio_urb_ops[2] = {
{
- .prepare = prepare_startup_playback_urb,
+ .prepare = prepare_nodata_playback_urb,
.retire = retire_playback_urb,
.prepare_sync = prepare_playback_sync_urb,
.retire_sync = retire_playback_sync_urb,
@@ -637,7 +648,7 @@ static struct snd_urb_ops audio_urb_ops[2] = {
static struct snd_urb_ops audio_urb_ops_high_speed[2] = {
{
- .prepare = prepare_startup_playback_urb,
+ .prepare = prepare_nodata_playback_urb,
.retire = retire_playback_urb,
.prepare_sync = prepare_playback_sync_urb_hs,
.retire_sync = retire_playback_sync_urb_hs,
@@ -655,7 +666,7 @@ static struct snd_urb_ops audio_urb_ops_high_speed[2] = {
*/
static void snd_complete_urb(struct urb *urb)
{
- struct snd_urb_ctx *ctx = (struct snd_urb_ctx *)urb->context;
+ struct snd_urb_ctx *ctx = urb->context;
struct snd_usb_substream *subs = ctx->subs;
struct snd_pcm_substream *substream = ctx->subs->pcm_substream;
int err = 0;
@@ -678,7 +689,7 @@ static void snd_complete_urb(struct urb *urb)
*/
static void snd_complete_sync_urb(struct urb *urb)
{
- struct snd_urb_ctx *ctx = (struct snd_urb_ctx *)urb->context;
+ struct snd_urb_ctx *ctx = urb->context;
struct snd_usb_substream *subs = ctx->subs;
struct snd_pcm_substream *substream = ctx->subs->pcm_substream;
int err = 0;
@@ -925,10 +936,14 @@ static int snd_usb_pcm_playback_trigger(struct snd_pcm_substream *substream,
switch (cmd) {
case SNDRV_PCM_TRIGGER_START:
+ case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
subs->ops.prepare = prepare_playback_urb;
return 0;
case SNDRV_PCM_TRIGGER_STOP:
return deactivate_urbs(subs, 0, 0);
+ case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
+ subs->ops.prepare = prepare_nodata_playback_urb;
+ return 0;
default:
return -EINVAL;
}
@@ -944,9 +959,16 @@ static int snd_usb_pcm_capture_trigger(struct snd_pcm_substream *substream,
switch (cmd) {
case SNDRV_PCM_TRIGGER_START:
+ subs->ops.retire = retire_capture_urb;
return start_urbs(subs, substream->runtime);
case SNDRV_PCM_TRIGGER_STOP:
return deactivate_urbs(subs, 0, 0);
+ case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
+ subs->ops.retire = retire_paused_capture_urb;
+ return 0;
+ case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
+ subs->ops.retire = retire_capture_urb;
+ return 0;
default:
return -EINVAL;
}
@@ -1408,7 +1430,7 @@ static int set_format(struct snd_usb_substream *subs, struct audioformat *fmt)
static int snd_usb_hw_params(struct snd_pcm_substream *substream,
struct snd_pcm_hw_params *hw_params)
{
- struct snd_usb_substream *subs = (struct snd_usb_substream *)substream->runtime->private_data;
+ struct snd_usb_substream *subs = substream->runtime->private_data;
struct audioformat *fmt;
unsigned int channels, rate, format;
int ret, changed;
@@ -1464,7 +1486,7 @@ static int snd_usb_hw_params(struct snd_pcm_substream *substream,
*/
static int snd_usb_hw_free(struct snd_pcm_substream *substream)
{
- struct snd_usb_substream *subs = (struct snd_usb_substream *)substream->runtime->private_data;
+ struct snd_usb_substream *subs = substream->runtime->private_data;
subs->cur_audiofmt = NULL;
subs->cur_rate = 0;
@@ -1505,33 +1527,20 @@ static int snd_usb_pcm_prepare(struct snd_pcm_substream *substream)
/* for playback, submit the URBs now; otherwise, the first hwptr_done
* updates for all URBs would happen at the same time when starting */
if (subs->direction == SNDRV_PCM_STREAM_PLAYBACK) {
- subs->ops.prepare = prepare_startup_playback_urb;
+ subs->ops.prepare = prepare_nodata_playback_urb;
return start_urbs(subs, runtime);
} else
return 0;
}
-static struct snd_pcm_hardware snd_usb_playback =
+static struct snd_pcm_hardware snd_usb_hardware =
{
.info = SNDRV_PCM_INFO_MMAP |
SNDRV_PCM_INFO_MMAP_VALID |
SNDRV_PCM_INFO_BATCH |
SNDRV_PCM_INFO_INTERLEAVED |
- SNDRV_PCM_INFO_BLOCK_TRANSFER,
- .buffer_bytes_max = 1024 * 1024,
- .period_bytes_min = 64,
- .period_bytes_max = 512 * 1024,
- .periods_min = 2,
- .periods_max = 1024,
-};
-
-static struct snd_pcm_hardware snd_usb_capture =
-{
- .info = SNDRV_PCM_INFO_MMAP |
- SNDRV_PCM_INFO_MMAP_VALID |
- SNDRV_PCM_INFO_BATCH |
- SNDRV_PCM_INFO_INTERLEAVED |
- SNDRV_PCM_INFO_BLOCK_TRANSFER,
+ SNDRV_PCM_INFO_BLOCK_TRANSFER |
+ SNDRV_PCM_INFO_PAUSE,
.buffer_bytes_max = 1024 * 1024,
.period_bytes_min = 64,
.period_bytes_max = 512 * 1024,
@@ -1810,28 +1819,33 @@ static int check_hw_params_convention(struct snd_usb_substream *subs)
static int snd_usb_pcm_check_knot(struct snd_pcm_runtime *runtime,
struct snd_usb_substream *subs)
{
- struct list_head *p;
- struct snd_pcm_hw_constraint_list constraints_rates;
+ struct audioformat *fp;
+ int count = 0, needs_knot = 0;
int err;
- list_for_each(p, &subs->fmt_list) {
- struct audioformat *fp;
- fp = list_entry(p, struct audioformat, list);
-
- if (!fp->needs_knot)
- continue;
-
- constraints_rates.count = fp->nr_rates;
- constraints_rates.list = fp->rate_table;
- constraints_rates.mask = 0;
-
- err = snd_pcm_hw_constraint_list(runtime, 0,
- SNDRV_PCM_HW_PARAM_RATE,
- &constraints_rates);
-
- if (err < 0)
- return err;
+ list_for_each_entry(fp, &subs->fmt_list, list) {
+ if (fp->rates & SNDRV_PCM_RATE_CONTINUOUS)
+ return 0;
+ count += fp->nr_rates;
+ if (fp->needs_knot)
+ needs_knot = 1;
}
+ if (!needs_knot)
+ return 0;
+
+ subs->rate_list.count = count;
+ subs->rate_list.list = kmalloc(sizeof(int) * count, GFP_KERNEL);
+ subs->rate_list.mask = 0;
+ count = 0;
+ list_for_each_entry(fp, &subs->fmt_list, list) {
+ int i;
+ for (i = 0; i < fp->nr_rates; i++)
+ subs->rate_list.list[count++] = fp->rate_table[i];
+ }
+ err = snd_pcm_hw_constraint_list(runtime, 0, SNDRV_PCM_HW_PARAM_RATE,
+ &subs->rate_list);
+ if (err < 0)
+ return err;
return 0;
}
@@ -1904,8 +1918,7 @@ static int setup_hw_info(struct snd_pcm_runtime *runtime, struct snd_usb_substre
return 0;
}
-static int snd_usb_pcm_open(struct snd_pcm_substream *substream, int direction,
- struct snd_pcm_hardware *hw)
+static int snd_usb_pcm_open(struct snd_pcm_substream *substream, int direction)
{
struct snd_usb_stream *as = snd_pcm_substream_chip(substream);
struct snd_pcm_runtime *runtime = substream->runtime;
@@ -1913,7 +1926,7 @@ static int snd_usb_pcm_open(struct snd_pcm_substream *substream, int direction,
subs->interface = -1;
subs->format = 0;
- runtime->hw = *hw;
+ runtime->hw = snd_usb_hardware;
runtime->private_data = subs;
subs->pcm_substream = substream;
return setup_hw_info(runtime, subs);
@@ -1934,7 +1947,7 @@ static int snd_usb_pcm_close(struct snd_pcm_substream *substream, int direction)
static int snd_usb_playback_open(struct snd_pcm_substream *substream)
{
- return snd_usb_pcm_open(substream, SNDRV_PCM_STREAM_PLAYBACK, &snd_usb_playback);
+ return snd_usb_pcm_open(substream, SNDRV_PCM_STREAM_PLAYBACK);
}
static int snd_usb_playback_close(struct snd_pcm_substream *substream)
@@ -1944,7 +1957,7 @@ static int snd_usb_playback_close(struct snd_pcm_substream *substream)
static int snd_usb_capture_open(struct snd_pcm_substream *substream)
{
- return snd_usb_pcm_open(substream, SNDRV_PCM_STREAM_CAPTURE, &snd_usb_capture);
+ return snd_usb_pcm_open(substream, SNDRV_PCM_STREAM_CAPTURE);
}
static int snd_usb_capture_close(struct snd_pcm_substream *substream)
@@ -2231,6 +2244,7 @@ static void free_substream(struct snd_usb_substream *subs)
kfree(fp->rate_table);
kfree(fp);
}
+ kfree(subs->rate_list.list);
}
@@ -2456,6 +2470,7 @@ static int parse_audio_format_rates(struct snd_usb_audio *chip, struct audioform
* build the rate table and bitmap flags
*/
int r, idx, c;
+ unsigned int nonzero_rates = 0;
/* this table corresponds to the SNDRV_PCM_RATE_XXX bit */
static unsigned int conv_rates[] = {
5512, 8000, 11025, 16000, 22050, 32000, 44100, 48000,
@@ -2478,6 +2493,7 @@ static int parse_audio_format_rates(struct snd_usb_audio *chip, struct audioform
fp->altsetting == 5 && fp->maxpacksize == 392)
rate = 96000;
fp->rate_table[r] = rate;
+ nonzero_rates |= rate;
if (rate < fp->rate_min)
fp->rate_min = rate;
else if (rate > fp->rate_max)
@@ -2493,6 +2509,10 @@ static int parse_audio_format_rates(struct snd_usb_audio *chip, struct audioform
if (!found)
fp->needs_knot = 1;
}
+ if (!nonzero_rates) {
+ hwc_debug("All rates were zero. Skipping format!\n");
+ return -1;
+ }
if (fp->needs_knot)
fp->rates |= SNDRV_PCM_RATE_KNOT;
} else {
@@ -3057,6 +3077,58 @@ static int create_ua1000_quirk(struct snd_usb_audio *chip,
return 0;
}
+/*
+ * Create a stream for an Edirol UA-101 interface.
+ * Copy, paste and modify from Edirol UA-1000
+ */
+static int create_ua101_quirk(struct snd_usb_audio *chip,
+ struct usb_interface *iface,
+ const struct snd_usb_audio_quirk *quirk)
+{
+ static const struct audioformat ua101_format = {
+ .format = SNDRV_PCM_FORMAT_S32_LE,
+ .fmt_type = USB_FORMAT_TYPE_I,
+ .altsetting = 1,
+ .altset_idx = 1,
+ .attributes = 0,
+ .rates = SNDRV_PCM_RATE_CONTINUOUS,
+ };
+ struct usb_host_interface *alts;
+ struct usb_interface_descriptor *altsd;
+ struct audioformat *fp;
+ int stream, err;
+
+ if (iface->num_altsetting != 2)
+ return -ENXIO;
+ alts = &iface->altsetting[1];
+ altsd = get_iface_desc(alts);
+ if (alts->extralen != 18 || alts->extra[1] != USB_DT_CS_INTERFACE ||
+ altsd->bNumEndpoints != 1)
+ return -ENXIO;
+
+ fp = kmemdup(&ua101_format, sizeof(*fp), GFP_KERNEL);
+ if (!fp)
+ return -ENOMEM;
+
+ fp->channels = alts->extra[11];
+ fp->iface = altsd->bInterfaceNumber;
+ fp->endpoint = get_endpoint(alts, 0)->bEndpointAddress;
+ fp->ep_attr = get_endpoint(alts, 0)->bmAttributes;
+ fp->maxpacksize = le16_to_cpu(get_endpoint(alts, 0)->wMaxPacketSize);
+ fp->rate_max = fp->rate_min = combine_triple(&alts->extra[15]);
+
+ stream = (fp->endpoint & USB_DIR_IN)
+ ? SNDRV_PCM_STREAM_CAPTURE : SNDRV_PCM_STREAM_PLAYBACK;
+ err = add_audio_endpoint(chip, stream, fp);
+ if (err < 0) {
+ kfree(fp);
+ return err;
+ }
+ /* FIXME: playback must be synchronized to capture */
+ usb_set_interface(chip->dev, fp->iface, 0);
+ return 0;
+}
+
static int snd_usb_create_quirk(struct snd_usb_audio *chip,
struct usb_interface *iface,
const struct snd_usb_audio_quirk *quirk);
@@ -3238,6 +3310,7 @@ static int snd_usb_create_quirk(struct snd_usb_audio *chip,
[QUIRK_AUDIO_FIXED_ENDPOINT] = create_fixed_stream_quirk,
[QUIRK_AUDIO_EDIROL_UA700_UA25] = create_ua700_ua25_quirk,
[QUIRK_AUDIO_EDIROL_UA1000] = create_ua1000_quirk,
+ [QUIRK_AUDIO_EDIROL_UA101] = create_ua101_quirk,
};
if (quirk->type < QUIRK_TYPE_COUNT) {
diff --git a/sound/usb/usbaudio.h b/sound/usb/usbaudio.h
index 0f4b2b8541d6..2272f45a1867 100644
--- a/sound/usb/usbaudio.h
+++ b/sound/usb/usbaudio.h
@@ -159,6 +159,7 @@ enum quirk_type {
QUIRK_AUDIO_FIXED_ENDPOINT,
QUIRK_AUDIO_EDIROL_UA700_UA25,
QUIRK_AUDIO_EDIROL_UA1000,
+ QUIRK_AUDIO_EDIROL_UA101,
QUIRK_TYPE_COUNT
};
diff --git a/sound/usb/usbquirks.h b/sound/usb/usbquirks.h
index a7e9563a01df..25b4ab4f61e7 100644
--- a/sound/usb/usbquirks.h
+++ b/sound/usb/usbquirks.h
@@ -1098,7 +1098,37 @@ YAMAHA_DEVICE(0x7010, "UB99"),
}
}
},
- /* TODO: add Edirol UA-101 support */
+/* Roland UA-101 in High-Speed Mode only */
+{
+ USB_DEVICE(0x0582, 0x007d),
+ .driver_info = (unsigned long) & (const struct snd_usb_audio_quirk) {
+ .vendor_name = "Roland",
+ .product_name = "UA-101",
+ .ifnum = QUIRK_ANY_INTERFACE,
+ .type = QUIRK_COMPOSITE,
+ .data = (const struct snd_usb_audio_quirk[]) {
+ {
+ .ifnum = 0,
+ .type = QUIRK_AUDIO_EDIROL_UA101
+ },
+ {
+ .ifnum = 1,
+ .type = QUIRK_AUDIO_EDIROL_UA101
+ },
+ {
+ .ifnum = 2,
+ .type = QUIRK_MIDI_FIXED_ENDPOINT,
+ .data = & (const struct snd_usb_midi_endpoint_info) {
+ .out_cables = 0x0001,
+ .in_cables = 0x0001
+ }
+ },
+ {
+ .ifnum = -1
+ }
+ }
+ }
+},
{
/* has ID 0x0081 when not in "Advanced Driver" mode */
USB_DEVICE(0x0582, 0x0080),