aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--Documentation/sound/alsa/ALSA-Configuration.txt13
-rw-r--r--Documentation/sound/alsa/ControlNames.txt3
-rw-r--r--Documentation/sound/alsa/HD-Audio-Models.txt1
-rw-r--r--arch/arm/mach-davinci/include/mach/asp.h8
-rw-r--r--arch/arm/mach-omap2/board-3430sdp.c10
-rw-r--r--arch/arm/mach-omap2/board-omap3beagle.c10
-rw-r--r--arch/arm/mach-omap2/board-omap3evm.c10
-rw-r--r--arch/arm/mach-omap2/board-omap3pandora.c10
-rw-r--r--arch/arm/mach-omap2/board-overo.c10
-rw-r--r--arch/arm/mach-omap2/board-zoom2.c10
-rw-r--r--arch/arm/mach-s3c6400/include/mach/map.h2
-rw-r--r--arch/arm/plat-s3c/include/plat/audio.h48
-rw-r--r--arch/arm/plat-s3c/include/plat/devs.h3
-rw-r--r--arch/arm/plat-s3c/include/plat/regs-s3c2412-iis.h2
-rw-r--r--arch/arm/plat-s3c64xx/dev-audio.c101
-rw-r--r--arch/sh/boards/mach-hp6xx/setup.c55
-rw-r--r--arch/sh/boards/mach-se/7724/setup.c3
-rw-r--r--arch/sh/include/mach-common/mach/hp6xx.h4
-rw-r--r--drivers/media/radio/Kconfig18
-rw-r--r--drivers/media/radio/Makefile1
-rw-r--r--drivers/media/radio/radio-miropcm20.c270
-rw-r--r--drivers/mfd/Kconfig6
-rw-r--r--drivers/mfd/Makefile1
-rw-r--r--drivers/mfd/twl4030-codec.c276
-rw-r--r--drivers/mfd/twl4030-core.c16
-rw-r--r--include/linux/i2c/twl4030.h19
-rw-r--r--include/linux/mfd/twl4030-codec.h272
-rw-r--r--include/sound/Kbuild1
-rw-r--r--include/sound/aci.h (renamed from sound/isa/opti9xx/miro.h)23
-rw-r--r--include/sound/ak4113.h321
-rw-r--r--include/sound/ak4114.h12
-rw-r--r--include/sound/ak4xxx-adda.h5
-rw-r--r--include/sound/control.h5
-rw-r--r--include/sound/cs4231-regs.h1
-rw-r--r--include/sound/pcm.h3
-rw-r--r--include/sound/rawmidi.h2
-rw-r--r--include/sound/sh_dac_audio.h21
-rw-r--r--include/sound/soc-dai.h14
-rw-r--r--include/sound/soc-dapm.h17
-rw-r--r--include/sound/soc.h15
-rw-r--r--include/sound/sscape_ioctl.h21
-rw-r--r--include/sound/tlv320dac33-plat.h20
-rw-r--r--include/sound/tpa6130a2-plat.h30
-rw-r--r--include/sound/wss.h1
-rw-r--r--sound/Kconfig2
-rw-r--r--sound/arm/Makefile2
-rw-r--r--sound/arm/aaci.c35
-rw-r--r--sound/arm/devdma.c80
-rw-r--r--sound/arm/devdma.h3
-rw-r--r--sound/core/control.c9
-rw-r--r--sound/core/isadma.c10
-rw-r--r--sound/core/oss/mixer_oss.c4
-rw-r--r--sound/core/pcm.c6
-rw-r--r--sound/core/pcm_native.c83
-rw-r--r--sound/core/rawmidi.c17
-rw-r--r--sound/drivers/pcsp/pcsp.c32
-rw-r--r--sound/drivers/pcsp/pcsp.h2
-rw-r--r--sound/drivers/pcsp/pcsp_mixer.c35
-rw-r--r--sound/i2c/cs8427.c15
-rw-r--r--sound/i2c/other/Makefile3
-rw-r--r--sound/i2c/other/ak4113.c639
-rw-r--r--sound/i2c/other/ak4xxx-adda.c136
-rw-r--r--sound/i2c/other/tea575x-tuner.c2
-rw-r--r--sound/isa/Kconfig12
-rw-r--r--sound/isa/cmi8330.c4
-rw-r--r--sound/isa/cs423x/cs4236.c13
-rw-r--r--sound/isa/cs423x/cs4236_lib.c241
-rw-r--r--sound/isa/es1688/es1688_lib.c2
-rw-r--r--sound/isa/es18xx.c221
-rw-r--r--sound/isa/opti9xx/miro.c783
-rw-r--r--sound/isa/opti9xx/opti92x-ad1848.c110
-rw-r--r--sound/isa/sb/sb_mixer.c4
-rw-r--r--sound/isa/sscape.c727
-rw-r--r--sound/isa/wss/wss_lib.c105
-rw-r--r--sound/oss/Kconfig12
-rw-r--r--sound/oss/Makefile1
-rw-r--r--sound/oss/audio.c2
-rw-r--r--sound/oss/midi_synth.c2
-rw-r--r--sound/oss/mpu401.c2
-rw-r--r--sound/oss/sh_dac_audio.c3
-rw-r--r--sound/oss/sscape.c1480
-rw-r--r--sound/pci/Kconfig1
-rw-r--r--sound/pci/ac97/ac97_codec.c6
-rw-r--r--sound/pci/ac97/ac97_patch.c12
-rw-r--r--sound/pci/azt3328.c4
-rw-r--r--sound/pci/ca0106/ca0106_mixer.c4
-rw-r--r--sound/pci/ca0106/ca0106_proc.c4
-rw-r--r--sound/pci/cmipci.c4
-rw-r--r--sound/pci/ctxfi/ctatc.c2
-rw-r--r--sound/pci/emu10k1/emu10k1x.c3
-rw-r--r--sound/pci/emu10k1/emumixer.c4
-rw-r--r--sound/pci/emu10k1/emuproc.c4
-rw-r--r--sound/pci/emu10k1/io.c2
-rw-r--r--sound/pci/es1938.c2
-rw-r--r--sound/pci/fm801.c40
-rw-r--r--sound/pci/hda/Kconfig13
-rw-r--r--sound/pci/hda/hda_beep.c114
-rw-r--r--sound/pci/hda/hda_beep.h10
-rw-r--r--sound/pci/hda/hda_codec.c607
-rw-r--r--sound/pci/hda/hda_codec.h11
-rw-r--r--sound/pci/hda/hda_eld.c20
-rw-r--r--sound/pci/hda/hda_generic.c17
-rw-r--r--sound/pci/hda/hda_hwdep.c38
-rw-r--r--sound/pci/hda/hda_intel.c50
-rw-r--r--sound/pci/hda/hda_local.h69
-rw-r--r--sound/pci/hda/hda_proc.c70
-rw-r--r--sound/pci/hda/patch_analog.c61
-rw-r--r--sound/pci/hda/patch_ca0110.c4
-rw-r--r--sound/pci/hda/patch_cirrus.c31
-rw-r--r--sound/pci/hda/patch_cmedia.c4
-rw-r--r--sound/pci/hda/patch_conexant.c189
-rw-r--r--sound/pci/hda/patch_intelhdmi.c488
-rw-r--r--sound/pci/hda/patch_realtek.c440
-rw-r--r--sound/pci/hda/patch_sigmatel.c149
-rw-r--r--sound/pci/hda/patch_via.c3509
-rw-r--r--sound/pci/ice1712/Makefile2
-rw-r--r--sound/pci/ice1712/ice1712.c12
-rw-r--r--sound/pci/ice1712/ice1712.h14
-rw-r--r--sound/pci/ice1712/ice1724.c103
-rw-r--r--sound/pci/ice1712/juli.c56
-rw-r--r--sound/pci/ice1712/quartet.c1130
-rw-r--r--sound/pci/ice1712/quartet.h10
-rw-r--r--sound/pci/intel8x0.c6
-rw-r--r--sound/pci/oxygen/Makefile3
-rw-r--r--sound/pci/oxygen/cs2000.h83
-rw-r--r--sound/pci/oxygen/hifier.c61
-rw-r--r--sound/pci/oxygen/oxygen.c248
-rw-r--r--sound/pci/oxygen/oxygen.h5
-rw-r--r--sound/pci/oxygen/oxygen_lib.c29
-rw-r--r--sound/pci/oxygen/oxygen_mixer.c52
-rw-r--r--sound/pci/oxygen/oxygen_pcm.c19
-rw-r--r--sound/pci/oxygen/virtuoso.c1105
-rw-r--r--sound/pci/oxygen/xonar.h50
-rw-r--r--sound/pci/oxygen/xonar_cs43xx.c434
-rw-r--r--sound/pci/oxygen/xonar_hdmi.c128
-rw-r--r--sound/pci/oxygen/xonar_lib.c132
-rw-r--r--sound/pci/oxygen/xonar_pcm179x.c1115
-rw-r--r--sound/ppc/awacs.c12
-rw-r--r--sound/ppc/burgundy.c8
-rw-r--r--sound/ppc/tumbler.c2
-rw-r--r--sound/sh/Kconfig8
-rw-r--r--sound/sh/Makefile2
-rw-r--r--sound/sh/sh_dac_audio.c453
-rw-r--r--sound/soc/Makefile2
-rw-r--r--sound/soc/atmel/playpaq_wm8510.c2
-rw-r--r--sound/soc/atmel/sam9g20_wm8731.c2
-rw-r--r--sound/soc/au1x/dbdma2.c115
-rw-r--r--sound/soc/au1x/psc-ac97.c243
-rw-r--r--sound/soc/au1x/psc-i2s.c189
-rw-r--r--sound/soc/au1x/psc.h7
-rw-r--r--sound/soc/blackfin/bf5xx-ad1836.c7
-rw-r--r--sound/soc/blackfin/bf5xx-ad1938.c9
-rw-r--r--sound/soc/blackfin/bf5xx-i2s.c15
-rw-r--r--sound/soc/blackfin/bf5xx-tdm-pcm.c9
-rw-r--r--sound/soc/blackfin/bf5xx-tdm.c45
-rw-r--r--sound/soc/blackfin/bf5xx-tdm.h11
-rw-r--r--sound/soc/codecs/Kconfig25
-rw-r--r--sound/soc/codecs/Makefile12
-rw-r--r--sound/soc/codecs/ac97.c3
-rw-r--r--sound/soc/codecs/ad1836.c12
-rw-r--r--sound/soc/codecs/ad1938.c12
-rw-r--r--sound/soc/codecs/ad1980.c5
-rw-r--r--sound/soc/codecs/ad73311.c8
-rw-r--r--sound/soc/codecs/ads117x.c123
-rw-r--r--sound/soc/codecs/ads117x.h13
-rw-r--r--sound/soc/codecs/ak4104.c8
-rw-r--r--sound/soc/codecs/ak4535.c9
-rw-r--r--sound/soc/codecs/ak4642.c9
-rw-r--r--sound/soc/codecs/ak4671.c815
-rw-r--r--sound/soc/codecs/ak4671.h156
-rw-r--r--sound/soc/codecs/cs4270.c28
-rw-r--r--sound/soc/codecs/cx20442.c12
-rw-r--r--sound/soc/codecs/pcm3008.c9
-rw-r--r--sound/soc/codecs/ssm2602.c9
-rw-r--r--sound/soc/codecs/stac9766.c3
-rw-r--r--sound/soc/codecs/tlv320aic23.c11
-rw-r--r--sound/soc/codecs/tlv320aic26.c11
-rw-r--r--sound/soc/codecs/tlv320aic3x.c11
-rw-r--r--sound/soc/codecs/tlv320dac33.c1229
-rw-r--r--sound/soc/codecs/tlv320dac33.h267
-rw-r--r--sound/soc/codecs/tpa6130a2.c463
-rw-r--r--sound/soc/codecs/tpa6130a2.h61
-rw-r--r--sound/soc/codecs/twl4030.c452
-rw-r--r--sound/soc/codecs/twl4030.h242
-rw-r--r--sound/soc/codecs/uda134x.c9
-rw-r--r--sound/soc/codecs/uda1380.c9
-rw-r--r--sound/soc/codecs/wm8350.c32
-rw-r--r--sound/soc/codecs/wm8400.c32
-rw-r--r--sound/soc/codecs/wm8510.c14
-rw-r--r--sound/soc/codecs/wm8523.c26
-rw-r--r--sound/soc/codecs/wm8580.c30
-rw-r--r--sound/soc/codecs/wm8711.c633
-rw-r--r--sound/soc/codecs/wm8711.h42
-rw-r--r--sound/soc/codecs/wm8727.c135
-rw-r--r--sound/soc/codecs/wm8727.h21
-rw-r--r--sound/soc/codecs/wm8728.c10
-rw-r--r--sound/soc/codecs/wm8731.c94
-rw-r--r--sound/soc/codecs/wm8750.c9
-rw-r--r--sound/soc/codecs/wm8753.c49
-rw-r--r--sound/soc/codecs/wm8776.c43
-rw-r--r--sound/soc/codecs/wm8900.c34
-rw-r--r--sound/soc/codecs/wm8903.c28
-rw-r--r--sound/soc/codecs/wm8940.c28
-rw-r--r--sound/soc/codecs/wm8960.c30
-rw-r--r--sound/soc/codecs/wm8961.c27
-rw-r--r--sound/soc/codecs/wm8971.c11
-rw-r--r--sound/soc/codecs/wm8974.c36
-rw-r--r--sound/soc/codecs/wm8988.c44
-rw-r--r--sound/soc/codecs/wm8990.c14
-rw-r--r--sound/soc/codecs/wm8993.c49
-rw-r--r--sound/soc/codecs/wm9081.c27
-rw-r--r--sound/soc/codecs/wm9705.c7
-rw-r--r--sound/soc/codecs/wm9712.c7
-rw-r--r--sound/soc/codecs/wm9713.c32
-rw-r--r--sound/soc/codecs/wm_hubs.c51
-rw-r--r--sound/soc/codecs/wm_hubs.h5
-rw-r--r--sound/soc/davinci/Kconfig4
-rw-r--r--sound/soc/davinci/davinci-evm.c7
-rw-r--r--sound/soc/davinci/davinci-i2s.c85
-rw-r--r--sound/soc/davinci/davinci-mcasp.c18
-rw-r--r--sound/soc/davinci/davinci-mcasp.h5
-rw-r--r--sound/soc/davinci/davinci-pcm.c571
-rw-r--r--sound/soc/davinci/davinci-pcm.h2
-rw-r--r--sound/soc/fsl/mpc5200_dma.c123
-rw-r--r--sound/soc/fsl/mpc5200_dma.h24
-rw-r--r--sound/soc/fsl/mpc5200_psc_ac97.c39
-rw-r--r--sound/soc/imx/mx27vis_wm8974.c2
-rw-r--r--sound/soc/omap/Kconfig23
-rw-r--r--sound/soc/omap/Makefile4
-rw-r--r--sound/soc/omap/am3517evm.c202
-rw-r--r--sound/soc/omap/ams-delta.c4
-rw-r--r--sound/soc/omap/igep0020.c148
-rw-r--r--sound/soc/omap/omap-mcbsp.c63
-rw-r--r--sound/soc/omap/omap3evm.c7
-rw-r--r--sound/soc/omap/omap3pandora.c24
-rw-r--r--sound/soc/omap/overo.c4
-rw-r--r--sound/soc/pxa/Kconfig12
-rw-r--r--sound/soc/pxa/Makefile2
-rw-r--r--sound/soc/pxa/magician.c2
-rw-r--r--sound/soc/pxa/pxa-ssp.c20
-rw-r--r--sound/soc/pxa/raumfeld.c335
-rw-r--r--sound/soc/pxa/zylonite.c5
-rw-r--r--sound/soc/s3c24xx/Kconfig12
-rw-r--r--sound/soc/s3c24xx/Makefile6
-rw-r--r--sound/soc/s3c24xx/jive_wm8750.c2
-rw-r--r--sound/soc/s3c24xx/ln2440sbc_alc650.c2
-rw-r--r--sound/soc/s3c24xx/neo1973_gta02_wm8753.c10
-rw-r--r--sound/soc/s3c24xx/neo1973_wm8753.c11
-rw-r--r--sound/soc/s3c24xx/s3c-dma.c (renamed from sound/soc/s3c24xx/s3c24xx-pcm.c)88
-rw-r--r--sound/soc/s3c24xx/s3c-dma.h (renamed from sound/soc/s3c24xx/s3c24xx-pcm.h)8
-rw-r--r--sound/soc/s3c24xx/s3c-i2s-v2.c35
-rw-r--r--sound/soc/s3c24xx/s3c-i2s-v2.h4
-rw-r--r--sound/soc/s3c24xx/s3c-pcm.c552
-rw-r--r--sound/soc/s3c24xx/s3c-pcm.h123
-rw-r--r--sound/soc/s3c24xx/s3c2412-i2s.c7
-rw-r--r--sound/soc/s3c24xx/s3c2443-ac97.c13
-rw-r--r--sound/soc/s3c24xx/s3c24xx-i2s.c14
-rw-r--r--sound/soc/s3c24xx/s3c24xx_simtec.c2
-rw-r--r--sound/soc/s3c24xx/s3c24xx_simtec_hermes.c2
-rw-r--r--sound/soc/s3c24xx/s3c24xx_simtec_tlv320aic23.c2
-rw-r--r--sound/soc/s3c24xx/s3c24xx_uda134x.c2
-rw-r--r--sound/soc/s3c24xx/s3c64xx-i2s.c26
-rw-r--r--sound/soc/s3c24xx/s3c64xx-i2s.h1
-rw-r--r--sound/soc/s3c24xx/smdk2443_wm9710.c2
-rw-r--r--sound/soc/s3c24xx/smdk64xx_wm8580.c268
-rw-r--r--sound/soc/s6000/s6000-pcm.c4
-rw-r--r--sound/soc/sh/Kconfig1
-rw-r--r--sound/soc/sh/fsi.c271
-rw-r--r--sound/soc/soc-cache.c46
-rw-r--r--sound/soc/soc-core.c566
-rw-r--r--sound/soc/soc-dapm.c135
-rw-r--r--sound/soc/soc-jack.c6
-rw-r--r--sound/soc/soc-utils.c74
-rw-r--r--sound/usb/usbaudio.c38
-rw-r--r--sound/usb/usbaudio.h7
-rw-r--r--sound/usb/usbmidi.c208
-rw-r--r--sound/usb/usbmixer_maps.c23
-rw-r--r--sound/usb/usbquirks.h23
-rw-r--r--sound/usb/usx2y/us122l.c135
-rw-r--r--sound/usb/usx2y/us122l.h4
-rw-r--r--sound/usb/usx2y/usX2Yhwdep.c8
-rw-r--r--sound/usb/usx2y/usbusx2y.c28
-rw-r--r--sound/usb/usx2y/usbusx2y.h6
-rw-r--r--sound/usb/usx2y/usbusx2yaudio.c34
-rw-r--r--sound/usb/usx2y/usx2yhwdeppcm.c8
285 files changed, 21108 insertions, 7537 deletions
diff --git a/Documentation/sound/alsa/ALSA-Configuration.txt b/Documentation/sound/alsa/ALSA-Configuration.txt
index fd9a2f67edf2..8923597bd2bd 100644
--- a/Documentation/sound/alsa/ALSA-Configuration.txt
+++ b/Documentation/sound/alsa/ALSA-Configuration.txt
@@ -798,6 +798,9 @@ Prior to version 0.9.0rc4 options had a 'snd_' prefix. This was removed.
setup before initializing the codecs. This option is
available only when CONFIG_SND_HDA_PATCH_LOADER=y is set.
See HD-Audio.txt for details.
+ beep_mode - Selects the beep registration mode (0=off, 1=on, 2=
+ dynamic registration via mute switch on/off); the default
+ value is set via CONFIG_SND_HDA_INPUT_BEEP_MODE kconfig.
[Single (global) options]
single_cmd - Use single immediate commands to communicate with
@@ -1454,6 +1457,7 @@ Prior to version 0.9.0rc4 options had a 'snd_' prefix. This was removed.
Module for internal PC-Speaker.
+ nopcm - Disable PC-Speaker PCM sound. Only beeps remain.
nforce_wa - enable NForce chipset workaround. Expect bad sound.
This module supports system beeps, some kind of PCM playback and
@@ -1631,7 +1635,7 @@ Prior to version 0.9.0rc4 options had a 'snd_' prefix. This was removed.
Module snd-sscape
-----------------
- Module for ENSONIQ SoundScape PnP cards.
+ Module for ENSONIQ SoundScape cards.
port - Port # (PnP setup)
wss_port - WSS Port # (PnP setup)
@@ -1639,10 +1643,11 @@ Prior to version 0.9.0rc4 options had a 'snd_' prefix. This was removed.
mpu_irq - MPU-401 IRQ # (PnP setup)
dma - DMA # (PnP setup)
dma2 - 2nd DMA # (PnP setup, -1 to disable)
+ joystick - Enable gameport - 0 = disable (default), 1 = enable
+
+ This module supports multiple cards.
- This module supports multiple cards. ISA PnP must be enabled.
- You need sscape_ctl tool in alsa-tools package for loading
- the microcode.
+ The driver requires the firmware loader support on kernel.
Module snd-sun-amd7930 (on sparc only)
--------------------------------------
diff --git a/Documentation/sound/alsa/ControlNames.txt b/Documentation/sound/alsa/ControlNames.txt
index 5b18298e9495..fea65bb6269e 100644
--- a/Documentation/sound/alsa/ControlNames.txt
+++ b/Documentation/sound/alsa/ControlNames.txt
@@ -18,8 +18,9 @@ SOURCE:
Master
Master Mono
Hardware Master
+ Speaker (internal speaker)
Headphone
- PC Speaker
+ Beep (beep generator)
Phone
Phone Input
Phone Output
diff --git a/Documentation/sound/alsa/HD-Audio-Models.txt b/Documentation/sound/alsa/HD-Audio-Models.txt
index 4c7f9aee5c4e..9000cd84d076 100644
--- a/Documentation/sound/alsa/HD-Audio-Models.txt
+++ b/Documentation/sound/alsa/HD-Audio-Models.txt
@@ -391,6 +391,7 @@ STAC92HD83*
ref Reference board
mic-ref Reference board with power management for ports
dell-s14 Dell laptop
+ hp HP laptops with (inverted) mute-LED
auto BIOS setup (default)
STAC9872
diff --git a/arch/arm/mach-davinci/include/mach/asp.h b/arch/arm/mach-davinci/include/mach/asp.h
index 18e4ce34ece6..e07f70ed7c53 100644
--- a/arch/arm/mach-davinci/include/mach/asp.h
+++ b/arch/arm/mach-davinci/include/mach/asp.h
@@ -51,6 +51,14 @@ struct snd_platform_data {
u32 rx_dma_offset;
enum dma_event_q eventq_no; /* event queue number */
unsigned int codec_fmt;
+ /*
+ * Allowing this is more efficient and eliminates left and right swaps
+ * caused by underruns, but will swap the left and right channels
+ * when compared to previous behavior.
+ */
+ unsigned enable_channel_combine:1;
+ unsigned sram_size_playback;
+ unsigned sram_size_capture;
/* McASP specific fields */
int tdm_slots;
diff --git a/arch/arm/mach-omap2/board-3430sdp.c b/arch/arm/mach-omap2/board-3430sdp.c
index 0acb5560229c..08e535d92c06 100644
--- a/arch/arm/mach-omap2/board-3430sdp.c
+++ b/arch/arm/mach-omap2/board-3430sdp.c
@@ -410,6 +410,15 @@ static struct regulator_init_data sdp3430_vpll2 = {
.consumer_supplies = &sdp3430_vdvi_supply,
};
+static struct twl4030_codec_audio_data sdp3430_audio = {
+ .audio_mclk = 26000000,
+};
+
+static struct twl4030_codec_data sdp3430_codec = {
+ .audio_mclk = 26000000,
+ .audio = &sdp3430_audio,
+};
+
static struct twl4030_platform_data sdp3430_twldata = {
.irq_base = TWL4030_IRQ_BASE,
.irq_end = TWL4030_IRQ_END,
@@ -420,6 +429,7 @@ static struct twl4030_platform_data sdp3430_twldata = {
.madc = &sdp3430_madc_data,
.keypad = &sdp3430_kp_data,
.usb = &sdp3430_usb_data,
+ .codec = &sdp3430_codec,
.vaux1 = &sdp3430_vaux1,
.vaux2 = &sdp3430_vaux2,
diff --git a/arch/arm/mach-omap2/board-omap3beagle.c b/arch/arm/mach-omap2/board-omap3beagle.c
index 08b0816afa61..af411e11dddf 100644
--- a/arch/arm/mach-omap2/board-omap3beagle.c
+++ b/arch/arm/mach-omap2/board-omap3beagle.c
@@ -254,6 +254,15 @@ static struct twl4030_usb_data beagle_usb_data = {
.usb_mode = T2_USB_MODE_ULPI,
};
+static struct twl4030_codec_audio_data beagle_audio_data = {
+ .audio_mclk = 26000000,
+};
+
+static struct twl4030_codec_data beagle_codec_data = {
+ .audio_mclk = 26000000,
+ .audio = &beagle_audio_data,
+};
+
static struct twl4030_platform_data beagle_twldata = {
.irq_base = TWL4030_IRQ_BASE,
.irq_end = TWL4030_IRQ_END,
@@ -261,6 +270,7 @@ static struct twl4030_platform_data beagle_twldata = {
/* platform_data for children goes here */
.usb = &beagle_usb_data,
.gpio = &beagle_gpio_data,
+ .codec = &beagle_codec_data,
.vmmc1 = &beagle_vmmc1,
.vsim = &beagle_vsim,
.vdac = &beagle_vdac,
diff --git a/arch/arm/mach-omap2/board-omap3evm.c b/arch/arm/mach-omap2/board-omap3evm.c
index 4c4d7f8dbd72..25ca5f6a0d3d 100644
--- a/arch/arm/mach-omap2/board-omap3evm.c
+++ b/arch/arm/mach-omap2/board-omap3evm.c
@@ -194,6 +194,15 @@ static struct twl4030_madc_platform_data omap3evm_madc_data = {
.irq_line = 1,
};
+static struct twl4030_codec_audio_data omap3evm_audio_data = {
+ .audio_mclk = 26000000,
+};
+
+static struct twl4030_codec_data omap3evm_codec_data = {
+ .audio_mclk = 26000000,
+ .audio = &omap3evm_audio_data,
+};
+
static struct twl4030_platform_data omap3evm_twldata = {
.irq_base = TWL4030_IRQ_BASE,
.irq_end = TWL4030_IRQ_END,
@@ -203,6 +212,7 @@ static struct twl4030_platform_data omap3evm_twldata = {
.madc = &omap3evm_madc_data,
.usb = &omap3evm_usb_data,
.gpio = &omap3evm_gpio_data,
+ .codec = &omap3evm_codec_data,
};
static struct i2c_board_info __initdata omap3evm_i2c_boardinfo[] = {
diff --git a/arch/arm/mach-omap2/board-omap3pandora.c b/arch/arm/mach-omap2/board-omap3pandora.c
index 7519edb69155..c4be626c8422 100644
--- a/arch/arm/mach-omap2/board-omap3pandora.c
+++ b/arch/arm/mach-omap2/board-omap3pandora.c
@@ -281,11 +281,21 @@ static struct twl4030_usb_data omap3pandora_usb_data = {
.usb_mode = T2_USB_MODE_ULPI,
};
+static struct twl4030_codec_audio_data omap3pandora_audio_data = {
+ .audio_mclk = 26000000,
+};
+
+static struct twl4030_codec_data omap3pandora_codec_data = {
+ .audio_mclk = 26000000,
+ .audio = &omap3pandora_audio_data,
+};
+
static struct twl4030_platform_data omap3pandora_twldata = {
.irq_base = TWL4030_IRQ_BASE,
.irq_end = TWL4030_IRQ_END,
.gpio = &omap3pandora_gpio_data,
.usb = &omap3pandora_usb_data,
+ .codec = &omap3pandora_codec_data,
.vmmc1 = &pandora_vmmc1,
.vmmc2 = &pandora_vmmc2,
.keypad = &pandora_kp_data,
diff --git a/arch/arm/mach-omap2/board-overo.c b/arch/arm/mach-omap2/board-overo.c
index 9917d2fddc2f..e1fb50451e19 100644
--- a/arch/arm/mach-omap2/board-overo.c
+++ b/arch/arm/mach-omap2/board-overo.c
@@ -329,6 +329,15 @@ static struct regulator_init_data overo_vmmc1 = {
.consumer_supplies = &overo_vmmc1_supply,
};
+static struct twl4030_codec_audio_data overo_audio_data = {
+ .audio_mclk = 26000000,
+};
+
+static struct twl4030_codec_data overo_codec_data = {
+ .audio_mclk = 26000000,
+ .audio = &overo_audio_data,
+};
+
/* mmc2 (WLAN) and Bluetooth don't use twl4030 regulators */
static struct twl4030_platform_data overo_twldata = {
@@ -336,6 +345,7 @@ static struct twl4030_platform_data overo_twldata = {
.irq_end = TWL4030_IRQ_END,
.gpio = &overo_gpio_data,
.usb = &overo_usb_data,
+ .codec = &overo_codec_data,
.vmmc1 = &overo_vmmc1,
};
diff --git a/arch/arm/mach-omap2/board-zoom2.c b/arch/arm/mach-omap2/board-zoom2.c
index 51e0b3ba5f3a..51df584728f6 100644
--- a/arch/arm/mach-omap2/board-zoom2.c
+++ b/arch/arm/mach-omap2/board-zoom2.c
@@ -230,6 +230,15 @@ static struct twl4030_madc_platform_data zoom2_madc_data = {
.irq_line = 1,
};
+static struct twl4030_codec_audio_data zoom2_audio_data = {
+ .audio_mclk = 26000000,
+};
+
+static struct twl4030_codec_data zoom2_codec_data = {
+ .audio_mclk = 26000000,
+ .audio = &zoom2_audio_data,
+};
+
static struct twl4030_platform_data zoom2_twldata = {
.irq_base = TWL4030_IRQ_BASE,
.irq_end = TWL4030_IRQ_END,
@@ -240,6 +249,7 @@ static struct twl4030_platform_data zoom2_twldata = {
.usb = &zoom2_usb_data,
.gpio = &zoom2_gpio_data,
.keypad = &zoom2_kp_twl4030_data,
+ .codec = &zoom2_codec_data,
.vmmc1 = &zoom2_vmmc1,
.vmmc2 = &zoom2_vmmc2,
.vsim = &zoom2_vsim,
diff --git a/arch/arm/mach-s3c6400/include/mach/map.h b/arch/arm/mach-s3c6400/include/mach/map.h
index fc8b223bad4f..866be31872a5 100644
--- a/arch/arm/mach-s3c6400/include/mach/map.h
+++ b/arch/arm/mach-s3c6400/include/mach/map.h
@@ -48,6 +48,8 @@
#define S3C64XX_PA_IIS1 (0x7F003000)
#define S3C64XX_PA_TIMER (0x7F006000)
#define S3C64XX_PA_IIC0 (0x7F004000)
+#define S3C64XX_PA_PCM0 (0x7F009000)
+#define S3C64XX_PA_PCM1 (0x7F00A000)
#define S3C64XX_PA_IISV4 (0x7F00D000)
#define S3C64XX_PA_IIC1 (0x7F00F000)
diff --git a/arch/arm/plat-s3c/include/plat/audio.h b/arch/arm/plat-s3c/include/plat/audio.h
index de0e8da48bc3..f22d23bb6271 100644
--- a/arch/arm/plat-s3c/include/plat/audio.h
+++ b/arch/arm/plat-s3c/include/plat/audio.h
@@ -1,45 +1,17 @@
-/* arch/arm/mach-s3c2410/include/mach/audio.h
+/* arch/arm/plat-s3c/include/plat/audio.h
*
- * Copyright (c) 2004-2005 Simtec Electronics
- * http://www.simtec.co.uk/products/SWLINUX/
- * Ben Dooks <ben@simtec.co.uk>
- *
- * S3C24XX - Audio platfrom_device info
+ * Copyright (c) 2009 Samsung Electronics Co. Ltd
+ * Author: Jaswinder Singh <jassi.brar@samsung.com>
*
* 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 __ASM_ARCH_AUDIO_H
-#define __ASM_ARCH_AUDIO_H __FILE__
-
-/* struct s3c24xx_iis_ops
- *
- * called from the s3c24xx audio core to deal with the architecture
- * or the codec's setup and control.
- *
- * the pointer to itself is passed through in case the caller wants to
- * embed this in an larger structure for easy reference to it's context.
-*/
+ */
-struct s3c24xx_iis_ops {
- struct module *owner;
-
- int (*startup)(struct s3c24xx_iis_ops *me);
- void (*shutdown)(struct s3c24xx_iis_ops *me);
- int (*suspend)(struct s3c24xx_iis_ops *me);
- int (*resume)(struct s3c24xx_iis_ops *me);
-
- int (*open)(struct s3c24xx_iis_ops *me, struct snd_pcm_substream *strm);
- int (*close)(struct s3c24xx_iis_ops *me, struct snd_pcm_substream *strm);
- int (*prepare)(struct s3c24xx_iis_ops *me, struct snd_pcm_substream *strm, struct snd_pcm_runtime *rt);
+/**
+ * struct s3c_audio_pdata - common platform data for audio device drivers
+ * @cfg_gpio: Callback function to setup mux'ed pins in I2S/PCM/AC97 mode
+ */
+struct s3c_audio_pdata {
+ int (*cfg_gpio)(struct platform_device *);
};
-
-struct s3c24xx_platdata_iis {
- const char *codec_clk;
- struct s3c24xx_iis_ops *ops;
- int (*match_dev)(struct device *dev);
-};
-
-#endif /* __ASM_ARCH_AUDIO_H */
diff --git a/arch/arm/plat-s3c/include/plat/devs.h b/arch/arm/plat-s3c/include/plat/devs.h
index 0f540ea1e999..932cbbbb4273 100644
--- a/arch/arm/plat-s3c/include/plat/devs.h
+++ b/arch/arm/plat-s3c/include/plat/devs.h
@@ -28,6 +28,9 @@ extern struct platform_device s3c64xx_device_iis0;
extern struct platform_device s3c64xx_device_iis1;
extern struct platform_device s3c64xx_device_iisv4;
+extern struct platform_device s3c64xx_device_pcm0;
+extern struct platform_device s3c64xx_device_pcm1;
+
extern struct platform_device s3c_device_fb;
extern struct platform_device s3c_device_usb;
extern struct platform_device s3c_device_lcd;
diff --git a/arch/arm/plat-s3c/include/plat/regs-s3c2412-iis.h b/arch/arm/plat-s3c/include/plat/regs-s3c2412-iis.h
index 07659dad1748..abf2fbc2eb2f 100644
--- a/arch/arm/plat-s3c/include/plat/regs-s3c2412-iis.h
+++ b/arch/arm/plat-s3c/include/plat/regs-s3c2412-iis.h
@@ -67,6 +67,8 @@
#define S3C2412_IISMOD_BCLK_MASK (3 << 1)
#define S3C2412_IISMOD_8BIT (1 << 0)
+#define S3C64XX_IISMOD_CDCLKCON (1 << 12)
+
#define S3C2412_IISPSR_PSREN (1 << 15)
#define S3C2412_IISFIC_TXFLUSH (1 << 15)
diff --git a/arch/arm/plat-s3c64xx/dev-audio.c b/arch/arm/plat-s3c64xx/dev-audio.c
index 1322beb40dd7..a21a88fbb7e3 100644
--- a/arch/arm/plat-s3c64xx/dev-audio.c
+++ b/arch/arm/plat-s3c64xx/dev-audio.c
@@ -15,9 +15,14 @@
#include <mach/irqs.h>
#include <mach/map.h>
+#include <mach/dma.h>
+#include <mach/gpio.h>
#include <plat/devs.h>
-
+#include <plat/audio.h>
+#include <plat/gpio-bank-d.h>
+#include <plat/gpio-bank-e.h>
+#include <plat/gpio-cfg.h>
static struct resource s3c64xx_iis0_resource[] = {
[0] = {
@@ -66,3 +71,97 @@ struct platform_device s3c64xx_device_iisv4 = {
.resource = s3c64xx_iisv4_resource,
};
EXPORT_SYMBOL(s3c64xx_device_iisv4);
+
+
+/* PCM Controller platform_devices */
+
+static int s3c64xx_pcm_cfg_gpio(struct platform_device *pdev)
+{
+ switch (pdev->id) {
+ case 0:
+ s3c_gpio_cfgpin(S3C64XX_GPD(0), S3C64XX_GPD0_PCM0_SCLK);
+ s3c_gpio_cfgpin(S3C64XX_GPD(1), S3C64XX_GPD1_PCM0_EXTCLK);
+ s3c_gpio_cfgpin(S3C64XX_GPD(2), S3C64XX_GPD2_PCM0_FSYNC);
+ s3c_gpio_cfgpin(S3C64XX_GPD(3), S3C64XX_GPD3_PCM0_SIN);
+ s3c_gpio_cfgpin(S3C64XX_GPD(4), S3C64XX_GPD4_PCM0_SOUT);
+ break;
+ case 1:
+ s3c_gpio_cfgpin(S3C64XX_GPE(0), S3C64XX_GPE0_PCM1_SCLK);
+ s3c_gpio_cfgpin(S3C64XX_GPE(1), S3C64XX_GPE1_PCM1_EXTCLK);
+ s3c_gpio_cfgpin(S3C64XX_GPE(2), S3C64XX_GPE2_PCM1_FSYNC);
+ s3c_gpio_cfgpin(S3C64XX_GPE(3), S3C64XX_GPE3_PCM1_SIN);
+ s3c_gpio_cfgpin(S3C64XX_GPE(4), S3C64XX_GPE4_PCM1_SOUT);
+ break;
+ default:
+ printk(KERN_DEBUG "Invalid PCM Controller number!");
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static struct resource s3c64xx_pcm0_resource[] = {
+ [0] = {
+ .start = S3C64XX_PA_PCM0,
+ .end = S3C64XX_PA_PCM0 + 0x100 - 1,
+ .flags = IORESOURCE_MEM,
+ },
+ [1] = {
+ .start = DMACH_PCM0_TX,
+ .end = DMACH_PCM0_TX,
+ .flags = IORESOURCE_DMA,
+ },
+ [2] = {
+ .start = DMACH_PCM0_RX,
+ .end = DMACH_PCM0_RX,
+ .flags = IORESOURCE_DMA,
+ },
+};
+
+static struct s3c_audio_pdata s3c_pcm0_pdata = {
+ .cfg_gpio = s3c64xx_pcm_cfg_gpio,
+};
+
+struct platform_device s3c64xx_device_pcm0 = {
+ .name = "samsung-pcm",
+ .id = 0,
+ .num_resources = ARRAY_SIZE(s3c64xx_pcm0_resource),
+ .resource = s3c64xx_pcm0_resource,
+ .dev = {
+ .platform_data = &s3c_pcm0_pdata,
+ },
+};
+EXPORT_SYMBOL(s3c64xx_device_pcm0);
+
+static struct resource s3c64xx_pcm1_resource[] = {
+ [0] = {
+ .start = S3C64XX_PA_PCM1,
+ .end = S3C64XX_PA_PCM1 + 0x100 - 1,
+ .flags = IORESOURCE_MEM,
+ },
+ [1] = {
+ .start = DMACH_PCM1_TX,
+ .end = DMACH_PCM1_TX,
+ .flags = IORESOURCE_DMA,
+ },
+ [2] = {
+ .start = DMACH_PCM1_RX,
+ .end = DMACH_PCM1_RX,
+ .flags = IORESOURCE_DMA,
+ },
+};
+
+static struct s3c_audio_pdata s3c_pcm1_pdata = {
+ .cfg_gpio = s3c64xx_pcm_cfg_gpio,
+};
+
+struct platform_device s3c64xx_device_pcm1 = {
+ .name = "samsung-pcm",
+ .id = 1,
+ .num_resources = ARRAY_SIZE(s3c64xx_pcm1_resource),
+ .resource = s3c64xx_pcm1_resource,
+ .dev = {
+ .platform_data = &s3c_pcm1_pdata,
+ },
+};
+EXPORT_SYMBOL(s3c64xx_device_pcm1);
diff --git a/arch/sh/boards/mach-hp6xx/setup.c b/arch/sh/boards/mach-hp6xx/setup.c
index 8f305b36358b..e6dd5e96321e 100644
--- a/arch/sh/boards/mach-hp6xx/setup.c
+++ b/arch/sh/boards/mach-hp6xx/setup.c
@@ -13,6 +13,7 @@
#include <linux/init.h>
#include <linux/platform_device.h>
#include <linux/irq.h>
+#include <sound/sh_dac_audio.h>
#include <asm/hd64461.h>
#include <asm/io.h>
#include <mach/hp6xx.h>
@@ -51,9 +52,63 @@ static struct platform_device jornadakbd_device = {
.id = -1,
};
+static void dac_audio_start(struct dac_audio_pdata *pdata)
+{
+ u16 v;
+ u8 v8;
+
+ /* HP Jornada 680/690 speaker on */
+ v = inw(HD64461_GPADR);
+ v &= ~HD64461_GPADR_SPEAKER;
+ outw(v, HD64461_GPADR);
+
+ /* HP Palmtop 620lx/660lx speaker on */
+ v8 = inb(PKDR);
+ v8 &= ~PKDR_SPEAKER;
+ outb(v8, PKDR);
+
+ sh_dac_enable(pdata->channel);
+}
+
+static void dac_audio_stop(struct dac_audio_pdata *pdata)
+{
+ u16 v;
+ u8 v8;
+
+ /* HP Jornada 680/690 speaker off */
+ v = inw(HD64461_GPADR);
+ v |= HD64461_GPADR_SPEAKER;
+ outw(v, HD64461_GPADR);
+
+ /* HP Palmtop 620lx/660lx speaker off */
+ v8 = inb(PKDR);
+ v8 |= PKDR_SPEAKER;
+ outb(v8, PKDR);
+
+ sh_dac_output(0, pdata->channel);
+ sh_dac_disable(pdata->channel);
+}
+
+static struct dac_audio_pdata dac_audio_platform_data = {
+ .buffer_size = 64000,
+ .channel = 1,
+ .start = dac_audio_start,
+ .stop = dac_audio_stop,
+};
+
+static struct platform_device dac_audio_device = {
+ .name = "dac_audio",
+ .id = -1,
+ .dev = {
+ .platform_data = &dac_audio_platform_data,
+ }
+
+};
+
static struct platform_device *hp6xx_devices[] __initdata = {
&cf_ide_device,
&jornadakbd_device,
+ &dac_audio_device,
};
static void __init hp6xx_init_irq(void)
diff --git a/arch/sh/boards/mach-se/7724/setup.c b/arch/sh/boards/mach-se/7724/setup.c
index e78c3be8ad2f..0894bba9fade 100644
--- a/arch/sh/boards/mach-se/7724/setup.c
+++ b/arch/sh/boards/mach-se/7724/setup.c
@@ -313,6 +313,9 @@ static struct platform_device fsi_device = {
.dev = {
.platform_data = &fsi_info,
},
+ .archdata = {
+ .hwblk_id = HWBLK_SPU, /* FSI needs SPU hwblk */
+ },
};
/* KEYSC in SoC (Needs SW33-2 set to ON) */
diff --git a/arch/sh/include/mach-common/mach/hp6xx.h b/arch/sh/include/mach-common/mach/hp6xx.h
index 0d4165a32dcd..bcc301ac12f4 100644
--- a/arch/sh/include/mach-common/mach/hp6xx.h
+++ b/arch/sh/include/mach-common/mach/hp6xx.h
@@ -29,6 +29,9 @@
#define PKDR_LED_GREEN 0x10
+/* HP Palmtop 620lx/660lx speaker on/off */
+#define PKDR_SPEAKER 0x20
+
#define SCPDR_TS_SCAN_ENABLE 0x20
#define SCPDR_TS_SCAN_Y 0x02
#define SCPDR_TS_SCAN_X 0x01
@@ -42,6 +45,7 @@
#define ADC_CHANNEL_BACKUP 4
#define ADC_CHANNEL_CHARGE 5
+/* HP Jornada 680/690 speaker on/off */
#define HD64461_GPADR_SPEAKER 0x01
#define HD64461_GPADR_PCMCIA0 (0x02|0x08)
diff --git a/drivers/media/radio/Kconfig b/drivers/media/radio/Kconfig
index a87a477c87f2..b134553eb3b5 100644
--- a/drivers/media/radio/Kconfig
+++ b/drivers/media/radio/Kconfig
@@ -195,6 +195,24 @@ config RADIO_MAESTRO
To compile this driver as a module, choose M here: the
module will be called radio-maestro.
+config RADIO_MIROPCM20
+ tristate "miroSOUND PCM20 radio"
+ depends on ISA && VIDEO_V4L2
+ select SND_MIRO
+ ---help---
+ Choose Y here if you have this FM radio card. You also need to enable
+ the ALSA sound system. This choice automatically selects the ALSA
+ sound card driver "Miro miroSOUND PCM1pro/PCM12/PCM20radio" as this
+ is required for the radio-miropcm20.
+
+ In order to control your radio card, you will need to use programs
+ that are compatible with the Video For Linux API. Information on
+ this API and pointers to "v4l" programs may be found at
+ <file:Documentation/video4linux/API.html>.
+
+ To compile this driver as a module, choose M here: the
+ module will be called radio-miropcm20.
+
config RADIO_SF16FMI
tristate "SF16FMI Radio"
depends on ISA && VIDEO_V4L2
diff --git a/drivers/media/radio/Makefile b/drivers/media/radio/Makefile
index 2a1be3bf4f7c..8a63d543ae41 100644
--- a/drivers/media/radio/Makefile
+++ b/drivers/media/radio/Makefile
@@ -18,6 +18,7 @@ obj-$(CONFIG_RADIO_TRUST) += radio-trust.o
obj-$(CONFIG_I2C_SI4713) += si4713-i2c.o
obj-$(CONFIG_RADIO_SI4713) += radio-si4713.o
obj-$(CONFIG_RADIO_MAESTRO) += radio-maestro.o
+obj-$(CONFIG_RADIO_MIROPCM20) += radio-miropcm20.o
obj-$(CONFIG_USB_DSBR) += dsbr100.o
obj-$(CONFIG_RADIO_SI470X) += si470x/
obj-$(CONFIG_USB_MR800) += radio-mr800.o
diff --git a/drivers/media/radio/radio-miropcm20.c b/drivers/media/radio/radio-miropcm20.c
new file mode 100644
index 000000000000..4ff885445fd4
--- /dev/null
+++ b/drivers/media/radio/radio-miropcm20.c
@@ -0,0 +1,270 @@
+/* Miro PCM20 radio driver for Linux radio support
+ * (c) 1998 Ruurd Reitsma <R.A.Reitsma@wbmt.tudelft.nl>
+ * Thanks to Norberto Pellici for the ACI device interface specification
+ * The API part is based on the radiotrack driver by M. Kirkwood
+ * This driver relies on the aci mixer provided by the snd-miro
+ * ALSA driver.
+ * Look there for further info...
+ */
+
+/* What ever you think about the ACI, version 0x07 is not very well!
+ * I can't get frequency, 'tuner status', 'tuner flags' or mute/mono
+ * conditions... Robert
+ */
+
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/videodev2.h>
+#include <media/v4l2-device.h>
+#include <media/v4l2-ioctl.h>
+#include <sound/aci.h>
+
+static int radio_nr = -1;
+module_param(radio_nr, int, 0);
+MODULE_PARM_DESC(radio_nr, "Set radio device number (/dev/radioX). Default: -1 (autodetect)");
+
+static int mono;
+module_param(mono, bool, 0);
+MODULE_PARM_DESC(mono, "Force tuner into mono mode.");
+
+struct pcm20 {
+ struct v4l2_device v4l2_dev;
+ struct video_device vdev;
+ unsigned long freq;
+ int muted;
+ struct snd_miro_aci *aci;
+};
+
+static struct pcm20 pcm20_card = {
+ .freq = 87*16000,
+ .muted = 1,
+};
+
+static int pcm20_mute(struct pcm20 *dev, unsigned char mute)
+{
+ dev->muted = mute;
+ return snd_aci_cmd(dev->aci, ACI_SET_TUNERMUTE, mute, -1);
+}
+
+static int pcm20_stereo(struct pcm20 *dev, unsigned char stereo)
+{
+ return snd_aci_cmd(dev->aci, ACI_SET_TUNERMONO, !stereo, -1);
+}
+
+static int pcm20_setfreq(struct pcm20 *dev, unsigned long freq)
+{
+ unsigned char freql;
+ unsigned char freqh;
+ struct snd_miro_aci *aci = dev->aci;
+
+ dev->freq = freq;
+
+ freq /= 160;
+ if (!(aci->aci_version == 0x07 || aci->aci_version >= 0xb0))
+ freq /= 10; /* I don't know exactly which version
+ * needs this hack */
+ freql = freq & 0xff;
+ freqh = freq >> 8;
+
+ pcm20_stereo(dev, !mono);
+ return snd_aci_cmd(aci, ACI_WRITE_TUNE, freql, freqh);
+}
+
+static const struct v4l2_file_operations pcm20_fops = {
+ .owner = THIS_MODULE,
+ .ioctl = video_ioctl2,
+};
+
+static int vidioc_querycap(struct file *file, void *priv,
+ struct v4l2_capability *v)
+{
+ strlcpy(v->driver, "Miro PCM20", sizeof(v->driver));
+ strlcpy(v->card, "Miro PCM20", sizeof(v->card));
+ strlcpy(v->bus_info, "ISA", sizeof(v->bus_info));
+ v->version = 0x1;
+ v->capabilities = V4L2_CAP_TUNER | V4L2_CAP_RADIO;
+ return 0;
+}
+
+static int vidioc_g_tuner(struct file *file, void *priv,
+ struct v4l2_tuner *v)
+{
+ if (v->index) /* Only 1 tuner */
+ return -EINVAL;
+ strlcpy(v->name, "FM", sizeof(v->name));
+ v->type = V4L2_TUNER_RADIO;
+ v->rangelow = 87*16000;
+ v->rangehigh = 108*16000;
+ v->signal = 0xffff;
+ v->rxsubchans = V4L2_TUNER_SUB_MONO | V4L2_TUNER_SUB_STEREO;
+ v->capability = V4L2_TUNER_CAP_LOW;
+ v->audmode = V4L2_TUNER_MODE_MONO;
+ return 0;
+}
+
+static int vidioc_s_tuner(struct file *file, void *priv,
+ struct v4l2_tuner *v)
+{
+ return v->index ? -EINVAL : 0;
+}
+
+static int vidioc_g_frequency(struct file *file, void *priv,
+ struct v4l2_frequency *f)
+{
+ struct pcm20 *dev = video_drvdata(file);
+
+ if (f->tuner != 0)
+ return -EINVAL;
+
+ f->type = V4L2_TUNER_RADIO;
+ f->frequency = dev->freq;
+ return 0;
+}
+
+
+static int vidioc_s_frequency(struct file *file, void *priv,
+ struct v4l2_frequency *f)
+{
+ struct pcm20 *dev = video_drvdata(file);
+
+ if (f->tuner != 0 || f->type != V4L2_TUNER_RADIO)
+ return -EINVAL;
+
+ dev->freq = f->frequency;
+ pcm20_setfreq(dev, f->frequency);
+ return 0;
+}
+
+static int vidioc_queryctrl(struct file *file, void *priv,
+ struct v4l2_queryctrl *qc)
+{
+ switch (qc->id) {
+ case V4L2_CID_AUDIO_MUTE:
+ return v4l2_ctrl_query_fill(qc, 0, 1, 1, 1);
+ }
+ return -EINVAL;
+}
+
+static int vidioc_g_ctrl(struct file *file, void *priv,
+ struct v4l2_control *ctrl)
+{
+ struct pcm20 *dev = video_drvdata(file);
+
+ switch (ctrl->id) {
+ case V4L2_CID_AUDIO_MUTE:
+ ctrl->value = dev->muted;
+ break;
+ default:
+ return -EINVAL;
+ }
+ return 0;
+}
+
+static int vidioc_s_ctrl(struct file *file, void *priv,
+ struct v4l2_control *ctrl)
+{
+ struct pcm20 *dev = video_drvdata(file);
+
+ switch (ctrl->id) {
+ case V4L2_CID_AUDIO_MUTE:
+ pcm20_mute(dev, ctrl->value);
+ break;
+ default:
+ return -EINVAL;
+ }
+ return 0;
+}
+
+static int vidioc_g_input(struct file *filp, void *priv, unsigned int *i)
+{
+ *i = 0;
+ return 0;
+}
+
+static int vidioc_s_input(struct file *filp, void *priv, unsigned int i)
+{
+ return i ? -EINVAL : 0;
+}
+
+static int vidioc_g_audio(struct file *file, void *priv,
+ struct v4l2_audio *a)
+{
+ a->index = 0;
+ strlcpy(a->name, "Radio", sizeof(a->name));
+ a->capability = V4L2_AUDCAP_STEREO;
+ return 0;
+}
+
+static int vidioc_s_audio(struct file *file, void *priv,
+ struct v4l2_audio *a)
+{
+ return a->index ? -EINVAL : 0;
+}
+
+static const struct v4l2_ioctl_ops pcm20_ioctl_ops = {
+ .vidioc_querycap = vidioc_querycap,
+ .vidioc_g_tuner = vidioc_g_tuner,
+ .vidioc_s_tuner = vidioc_s_tuner,
+ .vidioc_g_frequency = vidioc_g_frequency,
+ .vidioc_s_frequency = vidioc_s_frequency,
+ .vidioc_queryctrl = vidioc_queryctrl,
+ .vidioc_g_ctrl = vidioc_g_ctrl,
+ .vidioc_s_ctrl = vidioc_s_ctrl,
+ .vidioc_g_audio = vidioc_g_audio,
+ .vidioc_s_audio = vidioc_s_audio,
+ .vidioc_g_input = vidioc_g_input,
+ .vidioc_s_input = vidioc_s_input,
+};
+
+static int __init pcm20_init(void)
+{
+ struct pcm20 *dev = &pcm20_card;
+ struct v4l2_device *v4l2_dev = &dev->v4l2_dev;
+ int res;
+
+ dev->aci = snd_aci_get_aci();
+ if (dev->aci == NULL) {
+ v4l2_err(v4l2_dev,
+ "you must load the snd-miro driver first!\n");
+ return -ENODEV;
+ }
+ strlcpy(v4l2_dev->name, "miropcm20", sizeof(v4l2_dev->name));
+
+
+ res = v4l2_device_register(NULL, v4l2_dev);
+ if (res < 0) {
+ v4l2_err(v4l2_dev, "could not register v4l2_device\n");
+ return -EINVAL;
+ }
+
+ strlcpy(dev->vdev.name, v4l2_dev->name, sizeof(dev->vdev.name));
+ dev->vdev.v4l2_dev = v4l2_dev;
+ dev->vdev.fops = &pcm20_fops;
+ dev->vdev.ioctl_ops = &pcm20_ioctl_ops;
+ dev->vdev.release = video_device_release_empty;
+ video_set_drvdata(&dev->vdev, dev);
+
+ if (video_register_device(&dev->vdev, VFL_TYPE_RADIO, radio_nr) < 0)
+ goto fail;
+
+ v4l2_info(v4l2_dev, "Mirosound PCM20 Radio tuner\n");
+ return 0;
+fail:
+ v4l2_device_unregister(v4l2_dev);
+ return -EINVAL;
+}
+
+MODULE_AUTHOR("Ruurd Reitsma, Krzysztof Helt");
+MODULE_DESCRIPTION("A driver for the Miro PCM20 radio card.");
+MODULE_LICENSE("GPL");
+
+static void __exit pcm20_cleanup(void)
+{
+ struct pcm20 *dev = &pcm20_card;
+
+ video_unregister_device(&dev->vdev);
+ v4l2_device_unregister(&dev->v4l2_dev);
+}
+
+module_init(pcm20_init);
+module_exit(pcm20_cleanup);
diff --git a/drivers/mfd/Kconfig b/drivers/mfd/Kconfig
index 570be139f9df..08f2d07bf56a 100644
--- a/drivers/mfd/Kconfig
+++ b/drivers/mfd/Kconfig
@@ -121,6 +121,12 @@ config TWL4030_POWER
and load scripts controling which resources are switched off/on
or reset when a sleep, wakeup or warm reset event occurs.
+config TWL4030_CODEC
+ bool
+ depends on TWL4030_CORE
+ select MFD_CORE
+ default n
+
config MFD_TMIO
bool
default n
diff --git a/drivers/mfd/Makefile b/drivers/mfd/Makefile
index f3b277b90d40..af0fc903cec8 100644
--- a/drivers/mfd/Makefile
+++ b/drivers/mfd/Makefile
@@ -26,6 +26,7 @@ obj-$(CONFIG_MENELAUS) += menelaus.o
obj-$(CONFIG_TWL4030_CORE) += twl4030-core.o twl4030-irq.o
obj-$(CONFIG_TWL4030_POWER) += twl4030-power.o
+obj-$(CONFIG_TWL4030_CODEC) += twl4030-codec.o
obj-$(CONFIG_MFD_MC13783) += mc13783-core.o
diff --git a/drivers/mfd/twl4030-codec.c b/drivers/mfd/twl4030-codec.c
new file mode 100644
index 000000000000..77b914907d7c
--- /dev/null
+++ b/drivers/mfd/twl4030-codec.c
@@ -0,0 +1,276 @@
+/*
+ * MFD driver for twl4030 codec submodule
+ *
+ * Author: Peter Ujfalusi <peter.ujfalusi@nokia.com>
+ *
+ * Copyright: (C) 2009 Nokia Corporation
+ *
+ * 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.
+ *
+ * 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., 51 Franklin St, Fifth Floor, Boston, MA
+ * 02110-1301 USA
+ *
+ */
+
+#include <linux/module.h>
+#include <linux/types.h>
+#include <linux/kernel.h>
+#include <linux/fs.h>
+#include <linux/platform_device.h>
+#include <linux/i2c/twl4030.h>
+#include <linux/mfd/core.h>
+#include <linux/mfd/twl4030-codec.h>
+
+#define TWL4030_CODEC_CELLS 2
+
+static struct platform_device *twl4030_codec_dev;
+
+struct twl4030_codec_resource {
+ int request_count;
+ u8 reg;
+ u8 mask;
+};
+
+struct twl4030_codec {
+ unsigned int audio_mclk;
+ struct mutex mutex;
+ struct twl4030_codec_resource resource[TWL4030_CODEC_RES_MAX];
+ struct mfd_cell cells[TWL4030_CODEC_CELLS];
+};
+
+/*
+ * Modify the resource, the function returns the content of the register
+ * after the modification.
+ */
+static int twl4030_codec_set_resource(enum twl4030_codec_res id, int enable)
+{
+ struct twl4030_codec *codec = platform_get_drvdata(twl4030_codec_dev);
+ u8 val;
+
+ twl4030_i2c_read_u8(TWL4030_MODULE_AUDIO_VOICE, &val,
+ codec->resource[id].reg);
+
+ if (enable)
+ val |= codec->resource[id].mask;
+ else
+ val &= ~codec->resource[id].mask;
+
+ twl4030_i2c_write_u8(TWL4030_MODULE_AUDIO_VOICE,
+ val, codec->resource[id].reg);
+
+ return val;
+}
+
+static inline int twl4030_codec_get_resource(enum twl4030_codec_res id)
+{
+ struct twl4030_codec *codec = platform_get_drvdata(twl4030_codec_dev);
+ u8 val;
+
+ twl4030_i2c_read_u8(TWL4030_MODULE_AUDIO_VOICE, &val,
+ codec->resource[id].reg);
+
+ return val;
+}
+
+/*
+ * Enable the resource.
+ * The function returns with error or the content of the register
+ */
+int twl4030_codec_enable_resource(enum twl4030_codec_res id)
+{
+ struct twl4030_codec *codec = platform_get_drvdata(twl4030_codec_dev);
+ int val;
+
+ if (id >= TWL4030_CODEC_RES_MAX) {
+ dev_err(&twl4030_codec_dev->dev,
+ "Invalid resource ID (%u)\n", id);
+ return -EINVAL;
+ }
+
+ mutex_lock(&codec->mutex);
+ if (!codec->resource[id].request_count)
+ /* Resource was disabled, enable it */
+ val = twl4030_codec_set_resource(id, 1);
+ else
+ val = twl4030_codec_get_resource(id);
+
+ codec->resource[id].request_count++;
+ mutex_unlock(&codec->mutex);
+
+ return val;
+}
+EXPORT_SYMBOL_GPL(twl4030_codec_enable_resource);
+
+/*
+ * Disable the resource.
+ * The function returns with error or the content of the register
+ */
+int twl4030_codec_disable_resource(unsigned id)
+{
+ struct twl4030_codec *codec = platform_get_drvdata(twl4030_codec_dev);
+ int val;
+
+ if (id >= TWL4030_CODEC_RES_MAX) {
+ dev_err(&twl4030_codec_dev->dev,
+ "Invalid resource ID (%u)\n", id);
+ return -EINVAL;
+ }
+
+ mutex_lock(&codec->mutex);
+ if (!codec->resource[id].request_count) {
+ dev_err(&twl4030_codec_dev->dev,
+ "Resource has been disabled already (%u)\n", id);
+ mutex_unlock(&codec->mutex);
+ return -EPERM;
+ }
+ codec->resource[id].request_count--;
+
+ if (!codec->resource[id].request_count)
+ /* Resource can be disabled now */
+ val = twl4030_codec_set_resource(id, 0);
+ else
+ val = twl4030_codec_get_resource(id);
+
+ mutex_unlock(&codec->mutex);
+
+ return val;
+}
+EXPORT_SYMBOL_GPL(twl4030_codec_disable_resource);
+
+unsigned int twl4030_codec_get_mclk(void)
+{
+ struct twl4030_codec *codec = platform_get_drvdata(twl4030_codec_dev);
+
+ return codec->audio_mclk;
+}
+EXPORT_SYMBOL_GPL(twl4030_codec_get_mclk);
+
+static int __devinit twl4030_codec_probe(struct platform_device *pdev)
+{
+ struct twl4030_codec *codec;
+ struct twl4030_codec_data *pdata = pdev->dev.platform_data;
+ struct mfd_cell *cell = NULL;
+ int ret, childs = 0;
+ u8 val;
+
+ if (!pdata) {
+ dev_err(&pdev->dev, "Platform data is missing\n");
+ return -EINVAL;
+ }
+
+ /* Configure APLL_INFREQ and disable APLL if enabled */
+ val = 0;
+ switch (pdata->audio_mclk) {
+ case 19200000:
+ val |= TWL4030_APLL_INFREQ_19200KHZ;
+ break;
+ case 26000000:
+ val |= TWL4030_APLL_INFREQ_26000KHZ;
+ break;
+ case 38400000:
+ val |= TWL4030_APLL_INFREQ_38400KHZ;
+ break;
+ default:
+ dev_err(&pdev->dev, "Invalid audio_mclk\n");
+ return -EINVAL;
+ }
+ twl4030_i2c_write_u8(TWL4030_MODULE_AUDIO_VOICE,
+ val, TWL4030_REG_APLL_CTL);
+
+ codec = kzalloc(sizeof(struct twl4030_codec), GFP_KERNEL);
+ if (!codec)
+ return -ENOMEM;
+
+ platform_set_drvdata(pdev, codec);
+
+ twl4030_codec_dev = pdev;
+ mutex_init(&codec->mutex);
+ codec->audio_mclk = pdata->audio_mclk;
+
+ /* Codec power */
+ codec->resource[TWL4030_CODEC_RES_POWER].reg = TWL4030_REG_CODEC_MODE;
+ codec->resource[TWL4030_CODEC_RES_POWER].mask = TWL4030_CODECPDZ;
+
+ /* PLL */
+ codec->resource[TWL4030_CODEC_RES_APLL].reg = TWL4030_REG_APLL_CTL;
+ codec->resource[TWL4030_CODEC_RES_APLL].mask = TWL4030_APLL_EN;
+
+ if (pdata->audio) {
+ cell = &codec->cells[childs];
+ cell->name = "twl4030_codec_audio";
+ cell->platform_data = pdata->audio;
+ cell->data_size = sizeof(*pdata->audio);
+ childs++;
+ }
+ if (pdata->vibra) {
+ cell = &codec->cells[childs];
+ cell->name = "twl4030_codec_vibra";
+ cell->platform_data = pdata->vibra;
+ cell->data_size = sizeof(*pdata->vibra);
+ childs++;
+ }
+
+ if (childs)
+ ret = mfd_add_devices(&pdev->dev, pdev->id, codec->cells,
+ childs, NULL, 0);
+ else {
+ dev_err(&pdev->dev, "No platform data found for childs\n");
+ ret = -ENODEV;
+ }
+
+ if (!ret)
+ return 0;
+
+ platform_set_drvdata(pdev, NULL);
+ kfree(codec);
+ twl4030_codec_dev = NULL;
+ return ret;
+}
+
+static int __devexit twl4030_codec_remove(struct platform_device *pdev)
+{
+ struct twl4030_codec *codec = platform_get_drvdata(pdev);
+
+ mfd_remove_devices(&pdev->dev);
+ platform_set_drvdata(pdev, NULL);
+ kfree(codec);
+ twl4030_codec_dev = NULL;
+
+ return 0;
+}
+
+MODULE_ALIAS("platform:twl4030_codec");
+
+static struct platform_driver twl4030_codec_driver = {
+ .probe = twl4030_codec_probe,
+ .remove = __devexit_p(twl4030_codec_remove),
+ .driver = {
+ .owner = THIS_MODULE,
+ .name = "twl4030_codec",
+ },
+};
+
+static int __devinit twl4030_codec_init(void)
+{
+ return platform_driver_register(&twl4030_codec_driver);
+}
+module_init(twl4030_codec_init);
+
+static void __devexit twl4030_codec_exit(void)
+{
+ platform_driver_unregister(&twl4030_codec_driver);
+}
+module_exit(twl4030_codec_exit);
+
+MODULE_AUTHOR("Peter Ujfalusi <peter.ujfalusi@nokia.com>");
+MODULE_LICENSE("GPL");
+
diff --git a/drivers/mfd/twl4030-core.c b/drivers/mfd/twl4030-core.c
index a1c47ee95c0e..98b984e191d5 100644
--- a/drivers/mfd/twl4030-core.c
+++ b/drivers/mfd/twl4030-core.c
@@ -114,6 +114,12 @@
#define twl_has_watchdog() false
#endif
+#if defined(CONFIG_TWL4030_CODEC) || defined(CONFIG_TWL4030_CODEC_MODULE)
+#define twl_has_codec() true
+#else
+#define twl_has_codec() false
+#endif
+
/* Triton Core internal information (BEGIN) */
/* Last - for index max*/
@@ -601,6 +607,14 @@ add_children(struct twl4030_platform_data *pdata, unsigned long features)
return PTR_ERR(child);
}
+ if (twl_has_codec() && pdata->codec) {
+ child = add_child(1, "twl4030_codec",
+ pdata->codec, sizeof(*pdata->codec),
+ false, 0, 0);
+ if (IS_ERR(child))
+ return PTR_ERR(child);
+ }
+
if (twl_has_regulator()) {
/*
child = add_regulator(TWL4030_REG_VPLL1, pdata->vpll1);
@@ -763,7 +777,7 @@ static int twl4030_remove(struct i2c_client *client)
}
/* NOTE: this driver only handles a single twl4030/tps659x0 chip */
-static int
+static int __init
twl4030_probe(struct i2c_client *client, const struct i2c_device_id *id)
{
int status;
diff --git a/include/linux/i2c/twl4030.h b/include/linux/i2c/twl4030.h
index 508824ee35e6..5306a759cbde 100644
--- a/include/linux/i2c/twl4030.h
+++ b/include/linux/i2c/twl4030.h
@@ -401,6 +401,24 @@ struct twl4030_power_data {
extern void twl4030_power_init(struct twl4030_power_data *triton2_scripts);
+struct twl4030_codec_audio_data {
+ unsigned int audio_mclk;
+ unsigned int ramp_delay_value;
+ unsigned int hs_extmute:1;
+ void (*set_hs_extmute)(int mute);
+};
+
+struct twl4030_codec_vibra_data {
+ unsigned int audio_mclk;
+ unsigned int coexist;
+};
+
+struct twl4030_codec_data {
+ unsigned int audio_mclk;
+ struct twl4030_codec_audio_data *audio;
+ struct twl4030_codec_vibra_data *vibra;
+};
+
struct twl4030_platform_data {
unsigned irq_base, irq_end;
struct twl4030_bci_platform_data *bci;
@@ -409,6 +427,7 @@ struct twl4030_platform_data {
struct twl4030_keypad_data *keypad;
struct twl4030_usb_data *usb;
struct twl4030_power_data *power;
+ struct twl4030_codec_data *codec;
/* LDO regulators */
struct regulator_init_data *vdac;
diff --git a/include/linux/mfd/twl4030-codec.h b/include/linux/mfd/twl4030-codec.h
new file mode 100644
index 000000000000..2ec317c68e59
--- /dev/null
+++ b/include/linux/mfd/twl4030-codec.h
@@ -0,0 +1,272 @@
+/*
+ * MFD driver for twl4030 codec submodule
+ *
+ * Author: Peter Ujfalusi <peter.ujfalusi@nokia.com>
+ *
+ * Copyright: (C) 2009 Nokia Corporation
+ *
+ * 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.
+ *
+ * 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., 51 Franklin St, Fifth Floor, Boston, MA
+ * 02110-1301 USA
+ *
+ */
+
+#ifndef __TWL4030_CODEC_H__
+#define __TWL4030_CODEC_H__
+
+/* Codec registers */
+#define TWL4030_REG_CODEC_MODE 0x01
+#define TWL4030_REG_OPTION 0x02
+#define TWL4030_REG_UNKNOWN 0x03
+#define TWL4030_REG_MICBIAS_CTL 0x04
+#define TWL4030_REG_ANAMICL 0x05
+#define TWL4030_REG_ANAMICR 0x06
+#define TWL4030_REG_AVADC_CTL 0x07
+#define TWL4030_REG_ADCMICSEL 0x08
+#define TWL4030_REG_DIGMIXING 0x09
+#define TWL4030_REG_ATXL1PGA 0x0A
+#define TWL4030_REG_ATXR1PGA 0x0B
+#define TWL4030_REG_AVTXL2PGA 0x0C
+#define TWL4030_REG_AVTXR2PGA 0x0D
+#define TWL4030_REG_AUDIO_IF 0x0E
+#define TWL4030_REG_VOICE_IF 0x0F
+#define TWL4030_REG_ARXR1PGA 0x10
+#define TWL4030_REG_ARXL1PGA 0x11
+#define TWL4030_REG_ARXR2PGA 0x12
+#define TWL4030_REG_ARXL2PGA 0x13
+#define TWL4030_REG_VRXPGA 0x14
+#define TWL4030_REG_VSTPGA 0x15
+#define TWL4030_REG_VRX2ARXPGA 0x16
+#define TWL4030_REG_AVDAC_CTL 0x17
+#define TWL4030_REG_ARX2VTXPGA 0x18
+#define TWL4030_REG_ARXL1_APGA_CTL 0x19
+#define TWL4030_REG_ARXR1_APGA_CTL 0x1A
+#define TWL4030_REG_ARXL2_APGA_CTL 0x1B
+#define TWL4030_REG_ARXR2_APGA_CTL 0x1C
+#define TWL4030_REG_ATX2ARXPGA 0x1D
+#define TWL4030_REG_BT_IF 0x1E
+#define TWL4030_REG_BTPGA 0x1F
+#define TWL4030_REG_BTSTPGA 0x20
+#define TWL4030_REG_EAR_CTL 0x21
+#define TWL4030_REG_HS_SEL 0x22
+#define TWL4030_REG_HS_GAIN_SET 0x23
+#define TWL4030_REG_HS_POPN_SET 0x24
+#define TWL4030_REG_PREDL_CTL 0x25
+#define TWL4030_REG_PREDR_CTL 0x26
+#define TWL4030_REG_PRECKL_CTL 0x27
+#define TWL4030_REG_PRECKR_CTL 0x28
+#define TWL4030_REG_HFL_CTL 0x29
+#define TWL4030_REG_HFR_CTL 0x2A
+#define TWL4030_REG_ALC_CTL 0x2B
+#define TWL4030_REG_ALC_SET1 0x2C
+#define TWL4030_REG_ALC_SET2 0x2D
+#define TWL4030_REG_BOOST_CTL 0x2E
+#define TWL4030_REG_SOFTVOL_CTL 0x2F
+#define TWL4030_REG_DTMF_FREQSEL 0x30
+#define TWL4030_REG_DTMF_TONEXT1H 0x31
+#define TWL4030_REG_DTMF_TONEXT1L 0x32
+#define TWL4030_REG_DTMF_TONEXT2H 0x33
+#define TWL4030_REG_DTMF_TONEXT2L 0x34
+#define TWL4030_REG_DTMF_TONOFF 0x35
+#define TWL4030_REG_DTMF_WANONOFF 0x36
+#define TWL4030_REG_I2S_RX_SCRAMBLE_H 0x37
+#define TWL4030_REG_I2S_RX_SCRAMBLE_M 0x38
+#define TWL4030_REG_I2S_RX_SCRAMBLE_L 0x39
+#define TWL4030_REG_APLL_CTL 0x3A
+#define TWL4030_REG_DTMF_CTL 0x3B
+#define TWL4030_REG_DTMF_PGA_CTL2 0x3C
+#define TWL4030_REG_DTMF_PGA_CTL1 0x3D
+#define TWL4030_REG_MISC_SET_1 0x3E
+#define TWL4030_REG_PCMBTMUX 0x3F
+#define TWL4030_REG_RX_PATH_SEL 0x43
+#define TWL4030_REG_VDL_APGA_CTL 0x44
+#define TWL4030_REG_VIBRA_CTL 0x45
+#define TWL4030_REG_VIBRA_SET 0x46
+#define TWL4030_REG_VIBRA_PWM_SET 0x47
+#define TWL4030_REG_ANAMIC_GAIN 0x48
+#define TWL4030_REG_MISC_SET_2 0x49
+
+/* Bitfield Definitions */
+
+/* TWL4030_CODEC_MODE (0x01) Fields */
+#define TWL4030_APLL_RATE 0xF0
+#define TWL4030_APLL_RATE_8000 0x00
+#define TWL4030_APLL_RATE_11025 0x10
+#define TWL4030_APLL_RATE_12000 0x20
+#define TWL4030_APLL_RATE_16000 0x40
+#define TWL4030_APLL_RATE_22050 0x50
+#define TWL4030_APLL_RATE_24000 0x60
+#define TWL4030_APLL_RATE_32000 0x80
+#define TWL4030_APLL_RATE_44100 0x90
+#define TWL4030_APLL_RATE_48000 0xA0
+#define TWL4030_APLL_RATE_96000 0xE0
+#define TWL4030_SEL_16K 0x08
+#define TWL4030_CODECPDZ 0x02
+#define TWL4030_OPT_MODE 0x01
+#define TWL4030_OPTION_1 (1 << 0)
+#define TWL4030_OPTION_2 (0 << 0)
+
+/* TWL4030_OPTION (0x02) Fields */
+#define TWL4030_ATXL1_EN (1 << 0)
+#define TWL4030_ATXR1_EN (1 << 1)
+#define TWL4030_ATXL2_VTXL_EN (1 << 2)
+#define TWL4030_ATXR2_VTXR_EN (1 << 3)
+#define TWL4030_ARXL1_VRX_EN (1 << 4)
+#define TWL4030_ARXR1_EN (1 << 5)
+#define TWL4030_ARXL2_EN (1 << 6)
+#define TWL4030_ARXR2_EN (1 << 7)
+
+/* TWL4030_REG_MICBIAS_CTL (0x04) Fields */
+#define TWL4030_MICBIAS2_CTL 0x40
+#define TWL4030_MICBIAS1_CTL 0x20
+#define TWL4030_HSMICBIAS_EN 0x04
+#define TWL4030_MICBIAS2_EN 0x02
+#define TWL4030_MICBIAS1_EN 0x01
+
+/* ANAMICL (0x05) Fields */
+#define TWL4030_CNCL_OFFSET_START 0x80
+#define TWL4030_OFFSET_CNCL_SEL 0x60
+#define TWL4030_OFFSET_CNCL_SEL_ARX1 0x00
+#define TWL4030_OFFSET_CNCL_SEL_ARX2 0x20
+#define TWL4030_OFFSET_CNCL_SEL_VRX 0x40
+#define TWL4030_OFFSET_CNCL_SEL_ALL 0x60
+#define TWL4030_MICAMPL_EN 0x10
+#define TWL4030_CKMIC_EN 0x08
+#define TWL4030_AUXL_EN 0x04
+#define TWL4030_HSMIC_EN 0x02
+#define TWL4030_MAINMIC_EN 0x01
+
+/* ANAMICR (0x06) Fields */
+#define TWL4030_MICAMPR_EN 0x10
+#define TWL4030_AUXR_EN 0x04
+#define TWL4030_SUBMIC_EN 0x01
+
+/* AVADC_CTL (0x07) Fields */
+#define TWL4030_ADCL_EN 0x08
+#define TWL4030_AVADC_CLK_PRIORITY 0x04
+#define TWL4030_ADCR_EN 0x02
+
+/* TWL4030_REG_ADCMICSEL (0x08) Fields */
+#define TWL4030_DIGMIC1_EN 0x08
+#define TWL4030_TX2IN_SEL 0x04
+#define TWL4030_DIGMIC0_EN 0x02
+#define TWL4030_TX1IN_SEL 0x01
+
+/* AUDIO_IF (0x0E) Fields */
+#define TWL4030_AIF_SLAVE_EN 0x80
+#define TWL4030_DATA_WIDTH 0x60
+#define TWL4030_DATA_WIDTH_16S_16W 0x00
+#define TWL4030_DATA_WIDTH_32S_16W 0x40
+#define TWL4030_DATA_WIDTH_32S_24W 0x60
+#define TWL4030_AIF_FORMAT 0x18
+#define TWL4030_AIF_FORMAT_CODEC 0x00
+#define TWL4030_AIF_FORMAT_LEFT 0x08
+#define TWL4030_AIF_FORMAT_RIGHT 0x10
+#define TWL4030_AIF_FORMAT_TDM 0x18
+#define TWL4030_AIF_TRI_EN 0x04
+#define TWL4030_CLK256FS_EN 0x02
+#define TWL4030_AIF_EN 0x01
+
+/* VOICE_IF (0x0F) Fields */
+#define TWL4030_VIF_SLAVE_EN 0x80
+#define TWL4030_VIF_DIN_EN 0x40
+#define TWL4030_VIF_DOUT_EN 0x20
+#define TWL4030_VIF_SWAP 0x10
+#define TWL4030_VIF_FORMAT 0x08
+#define TWL4030_VIF_TRI_EN 0x04
+#define TWL4030_VIF_SUB_EN 0x02
+#define TWL4030_VIF_EN 0x01
+
+/* EAR_CTL (0x21) */
+#define TWL4030_EAR_GAIN 0x30
+
+/* HS_GAIN_SET (0x23) Fields */
+#define TWL4030_HSR_GAIN 0x0C
+#define TWL4030_HSR_GAIN_PWR_DOWN 0x00
+#define TWL4030_HSR_GAIN_PLUS_6DB 0x04
+#define TWL4030_HSR_GAIN_0DB 0x08
+#define TWL4030_HSR_GAIN_MINUS_6DB 0x0C
+#define TWL4030_HSL_GAIN 0x03
+#define TWL4030_HSL_GAIN_PWR_DOWN 0x00
+#define TWL4030_HSL_GAIN_PLUS_6DB 0x01
+#define TWL4030_HSL_GAIN_0DB 0x02
+#define TWL4030_HSL_GAIN_MINUS_6DB 0x03
+
+/* HS_POPN_SET (0x24) Fields */
+#define TWL4030_VMID_EN 0x40
+#define TWL4030_EXTMUTE 0x20
+#define TWL4030_RAMP_DELAY 0x1C
+#define TWL4030_RAMP_DELAY_20MS 0x00
+#define TWL4030_RAMP_DELAY_40MS 0x04
+#define TWL4030_RAMP_DELAY_81MS 0x08
+#define TWL4030_RAMP_DELAY_161MS 0x0C
+#define TWL4030_RAMP_DELAY_323MS 0x10
+#define TWL4030_RAMP_DELAY_645MS 0x14
+#define TWL4030_RAMP_DELAY_1291MS 0x18
+#define TWL4030_RAMP_DELAY_2581MS 0x1C
+#define TWL4030_RAMP_EN 0x02
+
+/* PREDL_CTL (0x25) */
+#define TWL4030_PREDL_GAIN 0x30
+
+/* PREDR_CTL (0x26) */
+#define TWL4030_PREDR_GAIN 0x30
+
+/* PRECKL_CTL (0x27) */
+#define TWL4030_PRECKL_GAIN 0x30
+
+/* PRECKR_CTL (0x28) */
+#define TWL4030_PRECKR_GAIN 0x30
+
+/* HFL_CTL (0x29, 0x2A) Fields */
+#define TWL4030_HF_CTL_HB_EN 0x04
+#define TWL4030_HF_CTL_LOOP_EN 0x08
+#define TWL4030_HF_CTL_RAMP_EN 0x10
+#define TWL4030_HF_CTL_REF_EN 0x20
+
+/* APLL_CTL (0x3A) Fields */
+#define TWL4030_APLL_EN 0x10
+#define TWL4030_APLL_INFREQ 0x0F
+#define TWL4030_APLL_INFREQ_19200KHZ 0x05
+#define TWL4030_APLL_INFREQ_26000KHZ 0x06
+#define TWL4030_APLL_INFREQ_38400KHZ 0x0F
+
+/* REG_MISC_SET_1 (0x3E) Fields */
+#define TWL4030_CLK64_EN 0x80
+#define TWL4030_SCRAMBLE_EN 0x40
+#define TWL4030_FMLOOP_EN 0x20
+#define TWL4030_SMOOTH_ANAVOL_EN 0x02
+#define TWL4030_DIGMIC_LR_SWAP_EN 0x01
+
+/* VIBRA_CTL (0x45) */
+#define TWL4030_VIBRA_EN 0x01
+#define TWL4030_VIBRA_DIR 0x02
+#define TWL4030_VIBRA_AUDIO_SEL_L1 (0x00 << 2)
+#define TWL4030_VIBRA_AUDIO_SEL_R1 (0x01 << 2)
+#define TWL4030_VIBRA_AUDIO_SEL_L2 (0x02 << 2)
+#define TWL4030_VIBRA_AUDIO_SEL_R2 (0x03 << 2)
+#define TWL4030_VIBRA_SEL 0x10
+#define TWL4030_VIBRA_DIR_SEL 0x20
+
+/* TWL4030 codec resource IDs */
+enum twl4030_codec_res {
+ TWL4030_CODEC_RES_POWER = 0,
+ TWL4030_CODEC_RES_APLL,
+ TWL4030_CODEC_RES_MAX,
+};
+
+int twl4030_codec_disable_resource(enum twl4030_codec_res id);
+int twl4030_codec_enable_resource(enum twl4030_codec_res id);
+unsigned int twl4030_codec_get_mclk(void);
+
+#endif /* End of __TWL4030_CODEC_H__ */
diff --git a/include/sound/Kbuild b/include/sound/Kbuild
index fd054a344324..e9dd9369ecb9 100644
--- a/include/sound/Kbuild
+++ b/include/sound/Kbuild
@@ -2,7 +2,6 @@ header-y += asound_fm.h
header-y += hdsp.h
header-y += hdspm.h
header-y += sfnt_info.h
-header-y += sscape_ioctl.h
unifdef-y += asequencer.h
unifdef-y += asound.h
diff --git a/sound/isa/opti9xx/miro.h b/include/sound/aci.h
index 6e1385b8e07e..ee639d355ef0 100644
--- a/sound/isa/opti9xx/miro.h
+++ b/include/sound/aci.h
@@ -1,5 +1,5 @@
-#ifndef _MIRO_H_
-#define _MIRO_H_
+#ifndef _ACI_H_
+#define _ACI_H_
#define ACI_REG_COMMAND 0 /* write register offset */
#define ACI_REG_STATUS 1 /* read register offset */
@@ -70,4 +70,21 @@
#define ACI_SET_EQ6 0x45
#define ACI_SET_EQ7 0x46 /* ... to Treble */
-#endif /* _MIRO_H_ */
+struct snd_miro_aci {
+ unsigned long aci_port;
+ int aci_vendor;
+ int aci_product;
+ int aci_version;
+ int aci_amp;
+ int aci_preamp;
+ int aci_solomode;
+
+ struct mutex aci_mutex;
+};
+
+int snd_aci_cmd(struct snd_miro_aci *aci, int write1, int write2, int write3);
+
+struct snd_miro_aci *snd_aci_get_aci(void);
+
+#endif /* _ACI_H_ */
+
diff --git a/include/sound/ak4113.h b/include/sound/ak4113.h
new file mode 100644
index 000000000000..8988edae1609
--- /dev/null
+++ b/include/sound/ak4113.h
@@ -0,0 +1,321 @@
+#ifndef __SOUND_AK4113_H
+#define __SOUND_AK4113_H
+
+/*
+ * Routines for Asahi Kasei AK4113
+ * Copyright (c) by Jaroslav Kysela <perex@perex.cz>,
+ * Copyright (c) by Pavel Hofman <pavel.hofman@ivitera.com>,
+ *
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ */
+
+/* AK4113 registers */
+/* power down */
+#define AK4113_REG_PWRDN 0x00
+/* format control */
+#define AK4113_REG_FORMAT 0x01
+/* input/output control */
+#define AK4113_REG_IO0 0x02
+/* input/output control */
+#define AK4113_REG_IO1 0x03
+/* interrupt0 mask */
+#define AK4113_REG_INT0_MASK 0x04
+/* interrupt1 mask */
+#define AK4113_REG_INT1_MASK 0x05
+/* DAT mask & DTS select */
+#define AK4113_REG_DATDTS 0x06
+/* receiver status 0 */
+#define AK4113_REG_RCS0 0x07
+/* receiver status 1 */
+#define AK4113_REG_RCS1 0x08
+/* receiver status 2 */
+#define AK4113_REG_RCS2 0x09
+/* RX channel status byte 0 */
+#define AK4113_REG_RXCSB0 0x0a
+/* RX channel status byte 1 */
+#define AK4113_REG_RXCSB1 0x0b
+/* RX channel status byte 2 */
+#define AK4113_REG_RXCSB2 0x0c
+/* RX channel status byte 3 */
+#define AK4113_REG_RXCSB3 0x0d
+/* RX channel status byte 4 */
+#define AK4113_REG_RXCSB4 0x0e
+/* burst preamble Pc byte 0 */
+#define AK4113_REG_Pc0 0x0f
+/* burst preamble Pc byte 1 */
+#define AK4113_REG_Pc1 0x10
+/* burst preamble Pd byte 0 */
+#define AK4113_REG_Pd0 0x11
+/* burst preamble Pd byte 1 */
+#define AK4113_REG_Pd1 0x12
+/* Q-subcode address + control */
+#define AK4113_REG_QSUB_ADDR 0x13
+/* Q-subcode track */
+#define AK4113_REG_QSUB_TRACK 0x14
+/* Q-subcode index */
+#define AK4113_REG_QSUB_INDEX 0x15
+/* Q-subcode minute */
+#define AK4113_REG_QSUB_MINUTE 0x16
+/* Q-subcode second */
+#define AK4113_REG_QSUB_SECOND 0x17
+/* Q-subcode frame */
+#define AK4113_REG_QSUB_FRAME 0x18
+/* Q-subcode zero */
+#define AK4113_REG_QSUB_ZERO 0x19
+/* Q-subcode absolute minute */
+#define AK4113_REG_QSUB_ABSMIN 0x1a
+/* Q-subcode absolute second */
+#define AK4113_REG_QSUB_ABSSEC 0x1b
+/* Q-subcode absolute frame */
+#define AK4113_REG_QSUB_ABSFRM 0x1c
+
+/* sizes */
+#define AK4113_REG_RXCSB_SIZE ((AK4113_REG_RXCSB4-AK4113_REG_RXCSB0)+1)
+#define AK4113_REG_QSUB_SIZE ((AK4113_REG_QSUB_ABSFRM-AK4113_REG_QSUB_ADDR)\
+ +1)
+
+#define AK4113_WRITABLE_REGS (AK4113_REG_DATDTS + 1)
+
+/* AK4113_REG_PWRDN bits */
+/* Channel Status Select */
+#define AK4113_CS12 (1<<7)
+/* Block Start & C/U Output Mode */
+#define AK4113_BCU (1<<6)
+/* Master Clock Operation Select */
+#define AK4113_CM1 (1<<5)
+/* Master Clock Operation Select */
+#define AK4113_CM0 (1<<4)
+/* Master Clock Frequency Select */
+#define AK4113_OCKS1 (1<<3)
+/* Master Clock Frequency Select */
+#define AK4113_OCKS0 (1<<2)
+/* 0 = power down, 1 = normal operation */
+#define AK4113_PWN (1<<1)
+/* 0 = reset & initialize (except thisregister), 1 = normal operation */
+#define AK4113_RST (1<<0)
+
+/* AK4113_REQ_FORMAT bits */
+/* V/TX Output select: 0 = Validity Flag Output, 1 = TX */
+#define AK4113_VTX (1<<7)
+/* Audio Data Control */
+#define AK4113_DIF2 (1<<6)
+/* Audio Data Control */
+#define AK4113_DIF1 (1<<5)
+/* Audio Data Control */
+#define AK4113_DIF0 (1<<4)
+/* Deemphasis Autodetect Enable (1 = enable) */
+#define AK4113_DEAU (1<<3)
+/* 32kHz-48kHz Deemphasis Control */
+#define AK4113_DEM1 (1<<2)
+/* 32kHz-48kHz Deemphasis Control */
+#define AK4113_DEM0 (1<<1)
+#define AK4113_DEM_OFF (AK4113_DEM0)
+#define AK4113_DEM_44KHZ (0)
+#define AK4113_DEM_48KHZ (AK4113_DEM1)
+#define AK4113_DEM_32KHZ (AK4113_DEM0|AK4113_DEM1)
+/* STDO: 16-bit, right justified */
+#define AK4113_DIF_16R (0)
+/* STDO: 18-bit, right justified */
+#define AK4113_DIF_18R (AK4113_DIF0)
+/* STDO: 20-bit, right justified */
+#define AK4113_DIF_20R (AK4113_DIF1)
+/* STDO: 24-bit, right justified */
+#define AK4113_DIF_24R (AK4113_DIF1|AK4113_DIF0)
+/* STDO: 24-bit, left justified */
+#define AK4113_DIF_24L (AK4113_DIF2)
+/* STDO: I2S */
+#define AK4113_DIF_24I2S (AK4113_DIF2|AK4113_DIF0)
+/* STDO: 24-bit, left justified; LRCLK, BICK = Input */
+#define AK4113_DIF_I24L (AK4113_DIF2|AK4113_DIF1)
+/* STDO: I2S; LRCLK, BICK = Input */
+#define AK4113_DIF_I24I2S (AK4113_DIF2|AK4113_DIF1|AK4113_DIF0)
+
+/* AK4113_REG_IO0 */
+/* XTL1=0,XTL0=0 -> 11.2896Mhz; XTL1=0,XTL0=1 -> 12.288Mhz */
+#define AK4113_XTL1 (1<<6)
+/* XTL1=1,XTL0=0 -> 24.576Mhz; XTL1=1,XTL0=1 -> use channel status */
+#define AK4113_XTL0 (1<<5)
+/* Block Start Signal Output: 0 = U-bit, 1 = C-bit (req. BCU = 1) */
+#define AK4113_UCE (1<<4)
+/* TX Output Enable (1 = enable) */
+#define AK4113_TXE (1<<3)
+/* Output Through Data Selector for TX pin */
+#define AK4113_OPS2 (1<<2)
+/* Output Through Data Selector for TX pin */
+#define AK4113_OPS1 (1<<1)
+/* Output Through Data Selector for TX pin */
+#define AK4113_OPS0 (1<<0)
+/* 11.2896 MHz ref. Xtal freq. */
+#define AK4113_XTL_11_2896M (0)
+/* 12.288 MHz ref. Xtal freq. */
+#define AK4113_XTL_12_288M (AK4113_XTL0)
+/* 24.576 MHz ref. Xtal freq. */
+#define AK4113_XTL_24_576M (AK4113_XTL1)
+
+/* AK4113_REG_IO1 */
+/* Interrupt 0 pin Hold */
+#define AK4113_EFH1 (1<<7)
+/* Interrupt 0 pin Hold */
+#define AK4113_EFH0 (1<<6)
+#define AK4113_EFH_512LRCLK (0)
+#define AK4113_EFH_1024LRCLK (AK4113_EFH0)
+#define AK4113_EFH_2048LRCLK (AK4113_EFH1)
+#define AK4113_EFH_4096LRCLK (AK4113_EFH1|AK4113_EFH0)
+/* PLL Lock Time: 0 = 384/fs, 1 = 1/fs */
+#define AK4113_FAST (1<<5)
+/* MCKO2 Output Select: 0 = CMx/OCKSx, 1 = Xtal */
+#define AK4113_XMCK (1<<4)
+/* MCKO2 Output Freq. Select: 0 = x1, 1 = x0.5 (req. XMCK = 1) */
+#define AK4113_DIV (1<<3)
+/* Input Recovery Data Select */
+#define AK4113_IPS2 (1<<2)
+/* Input Recovery Data Select */
+#define AK4113_IPS1 (1<<1)
+/* Input Recovery Data Select */
+#define AK4113_IPS0 (1<<0)
+#define AK4113_IPS(x) ((x)&7)
+
+/* AK4113_REG_INT0_MASK && AK4113_REG_INT1_MASK*/
+/* mask enable for QINT bit */
+#define AK4113_MQI (1<<7)
+/* mask enable for AUTO bit */
+#define AK4113_MAUT (1<<6)
+/* mask enable for CINT bit */
+#define AK4113_MCIT (1<<5)
+/* mask enable for UNLOCK bit */
+#define AK4113_MULK (1<<4)
+/* mask enable for V bit */
+#define AK4113_V (1<<3)
+/* mask enable for STC bit */
+#define AK4113_STC (1<<2)
+/* mask enable for AUDN bit */
+#define AK4113_MAN (1<<1)
+/* mask enable for PAR bit */
+#define AK4113_MPR (1<<0)
+
+/* AK4113_REG_DATDTS */
+/* DAT Start ID Counter */
+#define AK4113_DCNT (1<<4)
+/* DTS-CD 16-bit Sync Word Detect */
+#define AK4113_DTS16 (1<<3)
+/* DTS-CD 14-bit Sync Word Detect */
+#define AK4113_DTS14 (1<<2)
+/* mask enable for DAT bit (if 1, no INT1 effect */
+#define AK4113_MDAT1 (1<<1)
+/* mask enable for DAT bit (if 1, no INT0 effect */
+#define AK4113_MDAT0 (1<<0)
+
+/* AK4113_REG_RCS0 */
+/* Q-subcode buffer interrupt, 0 = no change, 1 = changed */
+#define AK4113_QINT (1<<7)
+/* Non-PCM or DTS stream auto detection, 0 = no detect, 1 = detect */
+#define AK4113_AUTO (1<<6)
+/* channel status buffer interrupt, 0 = no change, 1 = change */
+#define AK4113_CINT (1<<5)
+/* PLL lock status, 0 = lock, 1 = unlock */
+#define AK4113_UNLCK (1<<4)
+/* Validity bit, 0 = valid, 1 = invalid */
+#define AK4113_V (1<<3)
+/* sampling frequency or Pre-emphasis change, 0 = no detect, 1 = detect */
+#define AK4113_STC (1<<2)
+/* audio bit output, 0 = audio, 1 = non-audio */
+#define AK4113_AUDION (1<<1)
+/* parity error or biphase error status, 0 = no error, 1 = error */
+#define AK4113_PAR (1<<0)
+
+/* AK4113_REG_RCS1 */
+/* sampling frequency detection */
+#define AK4113_FS3 (1<<7)
+#define AK4113_FS2 (1<<6)
+#define AK4113_FS1 (1<<5)
+#define AK4113_FS0 (1<<4)
+/* Pre-emphasis detect, 0 = OFF, 1 = ON */
+#define AK4113_PEM (1<<3)
+/* DAT Start ID Detect, 0 = no detect, 1 = detect */
+#define AK4113_DAT (1<<2)
+/* DTS-CD bit audio stream detect, 0 = no detect, 1 = detect */
+#define AK4113_DTSCD (1<<1)
+/* Non-PCM bit stream detection, 0 = no detect, 1 = detect */
+#define AK4113_NPCM (1<<0)
+#define AK4113_FS_8000HZ (AK4113_FS3|AK4113_FS0)
+#define AK4113_FS_11025HZ (AK4113_FS2|AK4113_FS0)
+#define AK4113_FS_16000HZ (AK4113_FS2|AK4113_FS1|AK4113_FS0)
+#define AK4113_FS_22050HZ (AK4113_FS2)
+#define AK4113_FS_24000HZ (AK4113_FS2|AK4113_FS1)
+#define AK4113_FS_32000HZ (AK4113_FS1|AK4113_FS0)
+#define AK4113_FS_44100HZ (0)
+#define AK4113_FS_48000HZ (AK4113_FS1)
+#define AK4113_FS_64000HZ (AK4113_FS3|AK4113_FS1|AK4113_FS0)
+#define AK4113_FS_88200HZ (AK4113_FS3)
+#define AK4113_FS_96000HZ (AK4113_FS3|AK4113_FS1)
+#define AK4113_FS_176400HZ (AK4113_FS3|AK4113_FS2)
+#define AK4113_FS_192000HZ (AK4113_FS3|AK4113_FS2|AK4113_FS1)
+
+/* AK4113_REG_RCS2 */
+/* CRC for Q-subcode, 0 = no error, 1 = error */
+#define AK4113_QCRC (1<<1)
+/* CRC for channel status, 0 = no error, 1 = error */
+#define AK4113_CCRC (1<<0)
+
+/* flags for snd_ak4113_check_rate_and_errors() */
+#define AK4113_CHECK_NO_STAT (1<<0) /* no statistics */
+#define AK4113_CHECK_NO_RATE (1<<1) /* no rate check */
+
+#define AK4113_CONTROLS 13
+
+typedef void (ak4113_write_t)(void *private_data, unsigned char addr,
+ unsigned char data);
+typedef unsigned char (ak4113_read_t)(void *private_data, unsigned char addr);
+
+struct ak4113 {
+ struct snd_card *card;
+ ak4113_write_t *write;
+ ak4113_read_t *read;
+ void *private_data;
+ unsigned int init:1;
+ spinlock_t lock;
+ unsigned char regmap[AK4113_WRITABLE_REGS];
+ struct snd_kcontrol *kctls[AK4113_CONTROLS];
+ struct snd_pcm_substream *substream;
+ unsigned long parity_errors;
+ unsigned long v_bit_errors;
+ unsigned long qcrc_errors;
+ unsigned long ccrc_errors;
+ unsigned char rcs0;
+ unsigned char rcs1;
+ unsigned char rcs2;
+ struct delayed_work work;
+ unsigned int check_flags;
+ void *change_callback_private;
+ void (*change_callback)(struct ak4113 *ak4113, unsigned char c0,
+ unsigned char c1);
+};
+
+int snd_ak4113_create(struct snd_card *card, ak4113_read_t *read,
+ ak4113_write_t *write,
+ const unsigned char pgm[AK4113_WRITABLE_REGS],
+ void *private_data, struct ak4113 **r_ak4113);
+void snd_ak4113_reg_write(struct ak4113 *ak4113, unsigned char reg,
+ unsigned char mask, unsigned char val);
+void snd_ak4113_reinit(struct ak4113 *ak4113);
+int snd_ak4113_build(struct ak4113 *ak4113,
+ struct snd_pcm_substream *capture_substream);
+int snd_ak4113_external_rate(struct ak4113 *ak4113);
+int snd_ak4113_check_rate_and_errors(struct ak4113 *ak4113, unsigned int flags);
+
+#endif /* __SOUND_AK4113_H */
+
diff --git a/include/sound/ak4114.h b/include/sound/ak4114.h
index d293d36a66b8..3ce69fd92523 100644
--- a/include/sound/ak4114.h
+++ b/include/sound/ak4114.h
@@ -95,13 +95,13 @@
/* AK4114_REG_IO0 */
#define AK4114_TX1E (1<<7) /* TX1 Output Enable (1 = enable) */
-#define AK4114_OPS12 (1<<2) /* Output Though Data Selector for TX1 pin */
-#define AK4114_OPS11 (1<<1) /* Output Though Data Selector for TX1 pin */
-#define AK4114_OPS10 (1<<0) /* Output Though Data Selector for TX1 pin */
+#define AK4114_OPS12 (1<<6) /* Output Data Selector for TX1 pin */
+#define AK4114_OPS11 (1<<5) /* Output Data Selector for TX1 pin */
+#define AK4114_OPS10 (1<<4) /* Output Data Selector for TX1 pin */
#define AK4114_TX0E (1<<3) /* TX0 Output Enable (1 = enable) */
-#define AK4114_OPS02 (1<<2) /* Output Though Data Selector for TX0 pin */
-#define AK4114_OPS01 (1<<1) /* Output Though Data Selector for TX0 pin */
-#define AK4114_OPS00 (1<<0) /* Output Though Data Selector for TX0 pin */
+#define AK4114_OPS02 (1<<2) /* Output Data Selector for TX0 pin */
+#define AK4114_OPS01 (1<<1) /* Output Data Selector for TX0 pin */
+#define AK4114_OPS00 (1<<0) /* Output Data Selector for TX0 pin */
/* AK4114_REG_IO1 */
#define AK4114_EFH1 (1<<7) /* Interrupt 0 pin Hold */
diff --git a/include/sound/ak4xxx-adda.h b/include/sound/ak4xxx-adda.h
index 891cf1aea8b1..030b87c2f6d4 100644
--- a/include/sound/ak4xxx-adda.h
+++ b/include/sound/ak4xxx-adda.h
@@ -68,7 +68,7 @@ struct snd_akm4xxx {
enum {
SND_AK4524, SND_AK4528, SND_AK4529,
SND_AK4355, SND_AK4358, SND_AK4381,
- SND_AK5365
+ SND_AK5365, SND_AK4620,
} type;
/* (array) information of combined codecs */
@@ -76,6 +76,9 @@ struct snd_akm4xxx {
const struct snd_akm4xxx_adc_channel *adc_info;
struct snd_ak4xxx_ops ops;
+ unsigned int num_chips;
+ unsigned int total_regs;
+ const char *name;
};
void snd_akm4xxx_write(struct snd_akm4xxx *ak, int chip, unsigned char reg,
diff --git a/include/sound/control.h b/include/sound/control.h
index ef96f07aa03b..112374dc0c58 100644
--- a/include/sound/control.h
+++ b/include/sound/control.h
@@ -56,7 +56,6 @@ struct snd_kcontrol_new {
struct snd_kcontrol_volatile {
struct snd_ctl_file *owner; /* locked */
- pid_t owner_pid;
unsigned int access; /* access rights */
};
@@ -87,10 +86,12 @@ struct snd_kctl_event {
#define snd_kctl_event(n) list_entry(n, struct snd_kctl_event, list)
+struct pid;
+
struct snd_ctl_file {
struct list_head list; /* list of all control files */
struct snd_card *card;
- pid_t pid;
+ struct pid *pid;
int prefer_pcm_subdevice;
int prefer_rawmidi_subdevice;
wait_queue_head_t change_sleep;
diff --git a/include/sound/cs4231-regs.h b/include/sound/cs4231-regs.h
index 92647532c454..66d28c2cb53d 100644
--- a/include/sound/cs4231-regs.h
+++ b/include/sound/cs4231-regs.h
@@ -70,7 +70,6 @@
#define AD1845_PWR_DOWN 0x1b /* power down control */
#define CS4235_LEFT_MASTER 0x1b /* left master output control */
#define CS4231_REC_FORMAT 0x1c /* clock and data format - record - bits 7-0 MCE */
-#define CS4231_PLY_VAR_FREQ 0x1d /* playback variable frequency */
#define AD1845_CLOCK 0x1d /* crystal clock select and total power down */
#define CS4235_RIGHT_MASTER 0x1d /* right master output control */
#define CS4231_REC_UPR_CNT 0x1e /* record upper count */
diff --git a/include/sound/pcm.h b/include/sound/pcm.h
index de6d981de5d6..c83a4a79f16b 100644
--- a/include/sound/pcm.h
+++ b/include/sound/pcm.h
@@ -348,6 +348,8 @@ struct snd_pcm_group { /* keep linked substreams */
int count;
};
+struct pid;
+
struct snd_pcm_substream {
struct snd_pcm *pcm;
struct snd_pcm_str *pstr;
@@ -379,6 +381,7 @@ struct snd_pcm_substream {
atomic_t mmap_count;
unsigned int f_flags;
void (*pcm_release)(struct snd_pcm_substream *);
+ struct pid *pid;
#if defined(CONFIG_SND_PCM_OSS) || defined(CONFIG_SND_PCM_OSS_MODULE)
/* -- OSS things -- */
struct snd_pcm_oss_substream oss;
diff --git a/include/sound/rawmidi.h b/include/sound/rawmidi.h
index c23c26585700..2480e7d10dcf 100644
--- a/include/sound/rawmidi.h
+++ b/include/sound/rawmidi.h
@@ -46,6 +46,7 @@
struct snd_rawmidi;
struct snd_rawmidi_substream;
struct snd_seq_port_info;
+struct pid;
struct snd_rawmidi_ops {
int (*open) (struct snd_rawmidi_substream * substream);
@@ -97,6 +98,7 @@ struct snd_rawmidi_substream {
struct snd_rawmidi_str *pstr;
char name[32];
struct snd_rawmidi_runtime *runtime;
+ struct pid *pid;
/* hardware layer */
struct snd_rawmidi_ops *ops;
};
diff --git a/include/sound/sh_dac_audio.h b/include/sound/sh_dac_audio.h
new file mode 100644
index 000000000000..f5deaf1ddb9f
--- /dev/null
+++ b/include/sound/sh_dac_audio.h
@@ -0,0 +1,21 @@
+/*
+ * SH_DAC specific configuration, for the dac_audio platform_device
+ *
+ * Copyright (C) 2009 Rafael Ignacio Zurita <rizurita@yahoo.com>
+ *
+ * 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 __INCLUDE_SH_DAC_AUDIO_H
+#define __INCLUDE_SH_DAC_AUDIO_H
+
+struct dac_audio_pdata {
+ int buffer_size;
+ int channel;
+ void (*start)(struct dac_audio_pdata *pd);
+ void (*stop)(struct dac_audio_pdata *pd);
+};
+
+#endif /* __INCLUDE_SH_DAC_AUDIO_H */
diff --git a/include/sound/soc-dai.h b/include/sound/soc-dai.h
index 97ca9af414dc..ca24e7f7a3f5 100644
--- a/include/sound/soc-dai.h
+++ b/include/sound/soc-dai.h
@@ -30,6 +30,7 @@ struct snd_pcm_substream;
#define SND_SOC_DAIFMT_DSP_A 3 /* L data MSB after FRM LRC */
#define SND_SOC_DAIFMT_DSP_B 4 /* L data MSB during FRM LRC */
#define SND_SOC_DAIFMT_AC97 5 /* AC97 */
+#define SND_SOC_DAIFMT_PDM 6 /* Pulse density modulation */
/* left and right justified also known as MSB and LSB respectively */
#define SND_SOC_DAIFMT_MSB SND_SOC_DAIFMT_LEFT_J
@@ -106,7 +107,7 @@ int snd_soc_dai_set_clkdiv(struct snd_soc_dai *dai,
int div_id, int div);
int snd_soc_dai_set_pll(struct snd_soc_dai *dai,
- int pll_id, unsigned int freq_in, unsigned int freq_out);
+ int pll_id, int source, unsigned int freq_in, unsigned int freq_out);
/* Digital Audio interface formatting */
int snd_soc_dai_set_fmt(struct snd_soc_dai *dai, unsigned int fmt);
@@ -114,6 +115,10 @@ int snd_soc_dai_set_fmt(struct snd_soc_dai *dai, unsigned int fmt);
int snd_soc_dai_set_tdm_slot(struct snd_soc_dai *dai,
unsigned int tx_mask, unsigned int rx_mask, int slots, int slot_width);
+int snd_soc_dai_set_channel_map(struct snd_soc_dai *dai,
+ unsigned int tx_num, unsigned int *tx_slot,
+ unsigned int rx_num, unsigned int *rx_slot);
+
int snd_soc_dai_set_tristate(struct snd_soc_dai *dai, int tristate);
/* Digital Audio Interface mute */
@@ -136,8 +141,8 @@ struct snd_soc_dai_ops {
*/
int (*set_sysclk)(struct snd_soc_dai *dai,
int clk_id, unsigned int freq, int dir);
- int (*set_pll)(struct snd_soc_dai *dai,
- int pll_id, unsigned int freq_in, unsigned int freq_out);
+ int (*set_pll)(struct snd_soc_dai *dai, int pll_id, int source,
+ unsigned int freq_in, unsigned int freq_out);
int (*set_clkdiv)(struct snd_soc_dai *dai, int div_id, int div);
/*
@@ -148,6 +153,9 @@ struct snd_soc_dai_ops {
int (*set_tdm_slot)(struct snd_soc_dai *dai,
unsigned int tx_mask, unsigned int rx_mask,
int slots, int slot_width);
+ int (*set_channel_map)(struct snd_soc_dai *dai,
+ unsigned int tx_num, unsigned int *tx_slot,
+ unsigned int rx_num, unsigned int *rx_slot);
int (*set_tristate)(struct snd_soc_dai *dai, int tristate);
/*
diff --git a/include/sound/soc-dapm.h b/include/sound/soc-dapm.h
index c1410e3191e3..c5c95e1da65b 100644
--- a/include/sound/soc-dapm.h
+++ b/include/sound/soc-dapm.h
@@ -206,6 +206,12 @@
.get = snd_soc_dapm_get_enum_double, \
.put = snd_soc_dapm_put_enum_double, \
.private_value = (unsigned long)&xenum }
+#define SOC_DAPM_ENUM_VIRT(xname, xenum) \
+{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, \
+ .info = snd_soc_info_enum_double, \
+ .get = snd_soc_dapm_get_enum_virt, \
+ .put = snd_soc_dapm_put_enum_virt, \
+ .private_value = (unsigned long)&xenum }
#define SOC_DAPM_VALUE_ENUM(xname, xenum) \
{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, \
.info = snd_soc_info_enum_double, \
@@ -260,6 +266,10 @@ 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_get_enum_virt(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol);
+int snd_soc_dapm_put_enum_virt(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol);
int snd_soc_dapm_get_value_enum_double(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol);
int snd_soc_dapm_put_value_enum_double(struct snd_kcontrol *kcontrol,
@@ -333,6 +343,10 @@ struct snd_soc_dapm_route {
const char *sink;
const char *control;
const char *source;
+
+ /* Note: currently only supported for links where source is a supply */
+ int (*connected)(struct snd_soc_dapm_widget *source,
+ struct snd_soc_dapm_widget *sink);
};
/* dapm audio path between two widgets */
@@ -349,6 +363,9 @@ struct snd_soc_dapm_path {
u32 connect:1; /* source and sink widgets are connected */
u32 walked:1; /* path has been walked */
+ int (*connected)(struct snd_soc_dapm_widget *source,
+ struct snd_soc_dapm_widget *sink);
+
struct list_head list_source;
struct list_head list_sink;
struct list_head list;
diff --git a/include/sound/soc.h b/include/sound/soc.h
index 475cb7ed6bec..0d7718f9280d 100644
--- a/include/sound/soc.h
+++ b/include/sound/soc.h
@@ -223,15 +223,15 @@ int snd_soc_codec_set_cache_io(struct snd_soc_codec *codec,
int addr_bits, int data_bits,
enum snd_soc_control_type control);
-#ifdef CONFIG_PM
-int snd_soc_suspend_device(struct device *dev);
-int snd_soc_resume_device(struct device *dev);
-#endif
-
/* 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_init_card(struct snd_soc_device *socdev);
+
+/* Utility functions to get clock rates from various things */
+int snd_soc_calc_frame_size(int sample_size, int channels, int tdm_slots);
+int snd_soc_params_to_frame_size(struct snd_pcm_hw_params *params);
+int snd_soc_calc_bclk(int fs, int sample_size, int channels, int tdm_slots);
+int snd_soc_params_to_bclk(struct snd_pcm_hw_params *parms);
/* set runtime hw params */
int snd_soc_set_runtime_hwparams(struct snd_pcm_substream *substream,
@@ -333,6 +333,8 @@ struct snd_soc_jack_gpio {
int debounce_time;
struct snd_soc_jack *jack;
struct work_struct work;
+
+ int (*jack_status_check)(void);
};
#endif
@@ -413,6 +415,7 @@ struct snd_soc_codec {
unsigned int num_dai;
#ifdef CONFIG_DEBUG_FS
+ struct dentry *debugfs_codec_root;
struct dentry *debugfs_reg;
struct dentry *debugfs_pop_time;
struct dentry *debugfs_dapm;
diff --git a/include/sound/sscape_ioctl.h b/include/sound/sscape_ioctl.h
deleted file mode 100644
index 0d8885969c64..000000000000
--- a/include/sound/sscape_ioctl.h
+++ /dev/null
@@ -1,21 +0,0 @@
-#ifndef SSCAPE_IOCTL_H
-#define SSCAPE_IOCTL_H
-
-
-struct sscape_bootblock
-{
- unsigned char code[256];
- unsigned version;
-};
-
-#define SSCAPE_MICROCODE_SIZE 65536
-
-struct sscape_microcode
-{
- unsigned char __user *code;
-};
-
-#define SND_SSCAPE_LOAD_BOOTB _IOWR('P', 100, struct sscape_bootblock)
-#define SND_SSCAPE_LOAD_MCODE _IOW ('P', 101, struct sscape_microcode)
-
-#endif
diff --git a/include/sound/tlv320dac33-plat.h b/include/sound/tlv320dac33-plat.h
new file mode 100644
index 000000000000..5858d06a7ffa
--- /dev/null
+++ b/include/sound/tlv320dac33-plat.h
@@ -0,0 +1,20 @@
+/*
+ * Platform header for Texas Instruments TLV320DAC33 codec driver
+ *
+ * Author: Peter Ujfalusi <peter.ujfalusi@nokia.com>
+ *
+ * Copyright: (C) 2009 Nokia Corporation
+ *
+ * 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 __TLV320DAC33_PLAT_H
+#define __TLV320DAC33_PLAT_H
+
+struct tlv320dac33_platform_data {
+ int power_gpio;
+};
+
+#endif /* __TLV320DAC33_PLAT_H */
diff --git a/include/sound/tpa6130a2-plat.h b/include/sound/tpa6130a2-plat.h
new file mode 100644
index 000000000000..e8c901e749d8
--- /dev/null
+++ b/include/sound/tpa6130a2-plat.h
@@ -0,0 +1,30 @@
+/*
+ * TPA6130A2 driver platform header
+ *
+ * Copyright (C) Nokia Corporation
+ *
+ * Written by Peter Ujfalusi <peter.ujfalusi@nokia.com>
+ *
+ * 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.
+ *
+ * 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., 51 Franklin St, Fifth Floor, Boston, MA
+ * 02110-1301 USA
+ */
+
+#ifndef TPA6130A2_PLAT_H
+#define TPA6130A2_PLAT_H
+
+struct tpa6130a2_platform_data {
+ int power_gpio;
+};
+
+#endif
diff --git a/include/sound/wss.h b/include/sound/wss.h
index 6d65f322f1d5..fd01f22825cd 100644
--- a/include/sound/wss.h
+++ b/include/sound/wss.h
@@ -154,7 +154,6 @@ int snd_wss_create(struct snd_card *card,
unsigned short hardware,
unsigned short hwshare,
struct snd_wss **rchip);
-int snd_wss_free(struct snd_wss *chip);
int snd_wss_pcm(struct snd_wss *chip, int device, struct snd_pcm **rpcm);
int snd_wss_timer(struct snd_wss *chip, int device, struct snd_timer **rtimer);
int snd_wss_mixer(struct snd_wss *chip);
diff --git a/sound/Kconfig b/sound/Kconfig
index 439e15c8faa3..b3e53e616ec9 100644
--- a/sound/Kconfig
+++ b/sound/Kconfig
@@ -58,7 +58,7 @@ config SOUND_OSS_CORE_PRECLAIM
Please read Documentation/feature-removal-schedule.txt for
details.
- If unusre, say Y.
+ If unsure, say Y.
source "sound/oss/dmasound/Kconfig"
diff --git a/sound/arm/Makefile b/sound/arm/Makefile
index 5a549ed6c8aa..8c0c851d4641 100644
--- a/sound/arm/Makefile
+++ b/sound/arm/Makefile
@@ -3,7 +3,7 @@
#
obj-$(CONFIG_SND_ARMAACI) += snd-aaci.o
-snd-aaci-objs := aaci.o devdma.o
+snd-aaci-objs := aaci.o
obj-$(CONFIG_SND_PXA2XX_PCM) += snd-pxa2xx-pcm.o
snd-pxa2xx-pcm-objs := pxa2xx-pcm.o
diff --git a/sound/arm/aaci.c b/sound/arm/aaci.c
index 6c160a038b23..1497dce1b04a 100644
--- a/sound/arm/aaci.c
+++ b/sound/arm/aaci.c
@@ -18,10 +18,7 @@
#include <linux/interrupt.h>
#include <linux/err.h>
#include <linux/amba/bus.h>
-
-#include <asm/io.h>
-#include <asm/irq.h>
-#include <asm/sizes.h>
+#include <linux/io.h>
#include <sound/core.h>
#include <sound/initval.h>
@@ -30,7 +27,6 @@
#include <sound/pcm_params.h>
#include "aaci.h"
-#include "devdma.h"
#define DRIVER_NAME "aaci-pl041"
@@ -492,7 +488,7 @@ static int aaci_pcm_hw_free(struct snd_pcm_substream *substream)
/*
* Clear out the DMA and any allocated buffers.
*/
- devdma_hw_free(NULL, substream);
+ snd_pcm_lib_free_pages(substream);
return 0;
}
@@ -509,20 +505,14 @@ static int aaci_pcm_hw_params(struct snd_pcm_substream *substream,
aacirun->pcm_open = 0;
}
- err = devdma_hw_alloc(NULL, substream,
- params_buffer_bytes(params));
+ err = snd_pcm_lib_malloc_pages(substream,
+ params_buffer_bytes(params));
if (err < 0)
goto out;
- if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
- err = snd_ac97_pcm_open(aacirun->pcm, params_rate(params),
- params_channels(params),
- aacirun->pcm->r[0].slots);
- else
- err = snd_ac97_pcm_open(aacirun->pcm, params_rate(params),
- params_channels(params),
- aacirun->pcm->r[0].slots);
-
+ err = snd_ac97_pcm_open(aacirun->pcm, params_rate(params),
+ params_channels(params),
+ aacirun->pcm->r[0].slots);
if (err)
goto out;
@@ -538,7 +528,7 @@ static int aaci_pcm_prepare(struct snd_pcm_substream *substream)
struct aaci_runtime *aacirun = runtime->private_data;
aacirun->start = (void *)runtime->dma_area;
- aacirun->end = aacirun->start + runtime->dma_bytes;
+ aacirun->end = aacirun->start + snd_pcm_lib_buffer_bytes(substream);
aacirun->ptr = aacirun->start;
aacirun->period =
aacirun->bytes = frames_to_bytes(runtime, runtime->period_size);
@@ -555,11 +545,6 @@ static snd_pcm_uframes_t aaci_pcm_pointer(struct snd_pcm_substream *substream)
return bytes_to_frames(runtime, bytes);
}
-static int aaci_pcm_mmap(struct snd_pcm_substream *substream, struct vm_area_struct *vma)
-{
- return devdma_mmap(NULL, substream, vma);
-}
-
/*
* Playback specific ALSA stuff
@@ -726,7 +711,6 @@ static struct snd_pcm_ops aaci_playback_ops = {
.prepare = aaci_pcm_prepare,
.trigger = aaci_pcm_playback_trigger,
.pointer = aaci_pcm_pointer,
- .mmap = aaci_pcm_mmap,
};
static int aaci_pcm_capture_hw_params(struct snd_pcm_substream *substream,
@@ -854,7 +838,6 @@ static struct snd_pcm_ops aaci_capture_ops = {
.prepare = aaci_pcm_capture_prepare,
.trigger = aaci_pcm_capture_trigger,
.pointer = aaci_pcm_pointer,
- .mmap = aaci_pcm_mmap,
};
/*
@@ -1044,6 +1027,8 @@ static int __devinit aaci_init_pcm(struct aaci *aaci)
snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &aaci_playback_ops);
snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, &aaci_capture_ops);
+ snd_pcm_lib_preallocate_pages_for_all(pcm, SNDRV_DMA_TYPE_DEV,
+ NULL, 0, 64 * 104);
}
return ret;
diff --git a/sound/arm/devdma.c b/sound/arm/devdma.c
deleted file mode 100644
index 9d1e6665b546..000000000000
--- a/sound/arm/devdma.c
+++ /dev/null
@@ -1,80 +0,0 @@
-/*
- * linux/sound/arm/devdma.c
- *
- * Copyright (C) 2003-2004 Russell King, All rights reserved.
- *
- * 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.
- *
- * ARM DMA shim for ALSA.
- */
-#include <linux/device.h>
-#include <linux/dma-mapping.h>
-
-#include <sound/core.h>
-#include <sound/pcm.h>
-
-#include "devdma.h"
-
-void devdma_hw_free(struct device *dev, struct snd_pcm_substream *substream)
-{
- struct snd_pcm_runtime *runtime = substream->runtime;
- struct snd_dma_buffer *buf = runtime->dma_buffer_p;
-
- if (runtime->dma_area == NULL)
- return;
-
- if (buf != &substream->dma_buffer) {
- dma_free_coherent(buf->dev.dev, buf->bytes, buf->area, buf->addr);
- kfree(runtime->dma_buffer_p);
- }
-
- snd_pcm_set_runtime_buffer(substream, NULL);
-}
-
-int devdma_hw_alloc(struct device *dev, struct snd_pcm_substream *substream, size_t size)
-{
- struct snd_pcm_runtime *runtime = substream->runtime;
- struct snd_dma_buffer *buf = runtime->dma_buffer_p;
- int ret = 0;
-
- if (buf) {
- if (buf->bytes >= size)
- goto out;
- devdma_hw_free(dev, substream);
- }
-
- if (substream->dma_buffer.area != NULL && substream->dma_buffer.bytes >= size) {
- buf = &substream->dma_buffer;
- } else {
- buf = kmalloc(sizeof(struct snd_dma_buffer), GFP_KERNEL);
- if (!buf)
- goto nomem;
-
- buf->dev.type = SNDRV_DMA_TYPE_DEV;
- buf->dev.dev = dev;
- buf->area = dma_alloc_coherent(dev, size, &buf->addr, GFP_KERNEL);
- buf->bytes = size;
- buf->private_data = NULL;
-
- if (!buf->area)
- goto free;
- }
- snd_pcm_set_runtime_buffer(substream, buf);
- ret = 1;
- out:
- runtime->dma_bytes = size;
- return ret;
-
- free:
- kfree(buf);
- nomem:
- return -ENOMEM;
-}
-
-int devdma_mmap(struct device *dev, struct snd_pcm_substream *substream, struct vm_area_struct *vma)
-{
- struct snd_pcm_runtime *runtime = substream->runtime;
- return dma_mmap_coherent(dev, vma, runtime->dma_area, runtime->dma_addr, runtime->dma_bytes);
-}
diff --git a/sound/arm/devdma.h b/sound/arm/devdma.h
deleted file mode 100644
index d025329c8a0f..000000000000
--- a/sound/arm/devdma.h
+++ /dev/null
@@ -1,3 +0,0 @@
-void devdma_hw_free(struct device *dev, struct snd_pcm_substream *substream);
-int devdma_hw_alloc(struct device *dev, struct snd_pcm_substream *substream, size_t size);
-int devdma_mmap(struct device *dev, struct snd_pcm_substream *substream, struct vm_area_struct *vma);
diff --git a/sound/core/control.c b/sound/core/control.c
index a8b7fabe645e..268ab7471224 100644
--- a/sound/core/control.c
+++ b/sound/core/control.c
@@ -75,7 +75,7 @@ static int snd_ctl_open(struct inode *inode, struct file *file)
ctl->card = card;
ctl->prefer_pcm_subdevice = -1;
ctl->prefer_rawmidi_subdevice = -1;
- ctl->pid = current->pid;
+ ctl->pid = get_pid(task_pid(current));
file->private_data = ctl;
write_lock_irqsave(&card->ctl_files_rwlock, flags);
list_add_tail(&ctl->list, &card->ctl_files);
@@ -125,6 +125,7 @@ static int snd_ctl_release(struct inode *inode, struct file *file)
control->vd[idx].owner = NULL;
up_write(&card->controls_rwsem);
snd_ctl_empty_read_queue(ctl);
+ put_pid(ctl->pid);
kfree(ctl);
module_put(card->module);
snd_card_file_remove(card, file);
@@ -672,7 +673,7 @@ static int snd_ctl_elem_info(struct snd_ctl_file *ctl,
info->access |= SNDRV_CTL_ELEM_ACCESS_LOCK;
if (vd->owner == ctl)
info->access |= SNDRV_CTL_ELEM_ACCESS_OWNER;
- info->owner = vd->owner_pid;
+ info->owner = pid_vnr(vd->owner->pid);
} else {
info->owner = -1;
}
@@ -827,7 +828,6 @@ static int snd_ctl_elem_lock(struct snd_ctl_file *file,
result = -EBUSY;
else {
vd->owner = file;
- vd->owner_pid = current->pid;
result = 0;
}
}
@@ -858,7 +858,6 @@ static int snd_ctl_elem_unlock(struct snd_ctl_file *file,
result = -EPERM;
else {
vd->owner = NULL;
- vd->owner_pid = 0;
result = 0;
}
}
@@ -1120,7 +1119,7 @@ static int snd_ctl_tlv_ioctl(struct snd_ctl_file *file,
goto __kctl_end;
}
if (vd->access & SNDRV_CTL_ELEM_ACCESS_TLV_CALLBACK) {
- if (file && vd->owner != NULL && vd->owner != file) {
+ if (vd->owner != NULL && vd->owner != file) {
err = -EPERM;
goto __kctl_end;
}
diff --git a/sound/core/isadma.c b/sound/core/isadma.c
index 79f0f16af339..950e19ba91fc 100644
--- a/sound/core/isadma.c
+++ b/sound/core/isadma.c
@@ -85,16 +85,24 @@ EXPORT_SYMBOL(snd_dma_disable);
unsigned int snd_dma_pointer(unsigned long dma, unsigned int size)
{
unsigned long flags;
- unsigned int result;
+ unsigned int result, result1;
flags = claim_dma_lock();
clear_dma_ff(dma);
if (!isa_dma_bridge_buggy)
disable_dma(dma);
result = get_dma_residue(dma);
+ /*
+ * HACK - read the counter again and choose higher value in order to
+ * avoid reading during counter lower byte roll over if the
+ * isa_dma_bridge_buggy is set.
+ */
+ result1 = get_dma_residue(dma);
if (!isa_dma_bridge_buggy)
enable_dma(dma);
release_dma_lock(flags);
+ if (unlikely(result < result1))
+ result = result1;
#ifdef CONFIG_SND_DEBUG
if (result > size)
snd_printk(KERN_ERR "pointer (0x%x) for DMA #%ld is greater than transfer size (0x%x)\n", result, dma, size);
diff --git a/sound/core/oss/mixer_oss.c b/sound/core/oss/mixer_oss.c
index 772423889eb3..54e2eb56e4c2 100644
--- a/sound/core/oss/mixer_oss.c
+++ b/sound/core/oss/mixer_oss.c
@@ -1251,7 +1251,9 @@ static void snd_mixer_oss_build(struct snd_mixer_oss *mixer)
{ SOUND_MIXER_SYNTH, "FM", 0 }, /* fallback */
{ SOUND_MIXER_SYNTH, "Music", 0 }, /* fallback */
{ SOUND_MIXER_PCM, "PCM", 0 },
- { SOUND_MIXER_SPEAKER, "PC Speaker", 0 },
+ { SOUND_MIXER_SPEAKER, "Beep", 0 },
+ { SOUND_MIXER_SPEAKER, "PC Speaker", 0 }, /* fallback */
+ { SOUND_MIXER_SPEAKER, "Speaker", 0 }, /* fallback */
{ SOUND_MIXER_LINE, "Line", 0 },
{ SOUND_MIXER_MIC, "Mic", 0 },
{ SOUND_MIXER_CD, "CD", 0 },
diff --git a/sound/core/pcm.c b/sound/core/pcm.c
index c69c60b2a48a..6884ae031f6f 100644
--- a/sound/core/pcm.c
+++ b/sound/core/pcm.c
@@ -435,6 +435,7 @@ static void snd_pcm_substream_proc_status_read(struct snd_info_entry *entry,
return;
}
snd_iprintf(buffer, "state: %s\n", snd_pcm_state_name(status.state));
+ snd_iprintf(buffer, "owner_pid : %d\n", pid_vnr(substream->pid));
snd_iprintf(buffer, "trigger_time: %ld.%09ld\n",
status.trigger_tstamp.tv_sec, status.trigger_tstamp.tv_nsec);
snd_iprintf(buffer, "tstamp : %ld.%09ld\n",
@@ -809,7 +810,7 @@ int snd_pcm_attach_substream(struct snd_pcm *pcm, int stream,
card = pcm->card;
read_lock(&card->ctl_files_rwlock);
list_for_each_entry(kctl, &card->ctl_files, list) {
- if (kctl->pid == current->pid) {
+ if (kctl->pid == task_pid(current)) {
prefer_subdevice = kctl->prefer_pcm_subdevice;
if (prefer_subdevice != -1)
break;
@@ -900,6 +901,7 @@ int snd_pcm_attach_substream(struct snd_pcm *pcm, int stream,
substream->private_data = pcm->private_data;
substream->ref_count = 1;
substream->f_flags = file->f_flags;
+ substream->pid = get_pid(task_pid(current));
pstr->substream_opened++;
*rsubstream = substream;
return 0;
@@ -921,6 +923,8 @@ void snd_pcm_detach_substream(struct snd_pcm_substream *substream)
kfree(runtime->hw_constraints.rules);
kfree(runtime);
substream->runtime = NULL;
+ put_pid(substream->pid);
+ substream->pid = NULL;
substream->pstr->substream_opened--;
}
diff --git a/sound/core/pcm_native.c b/sound/core/pcm_native.c
index ab73edf2c89a..29ab46a12e11 100644
--- a/sound/core/pcm_native.c
+++ b/sound/core/pcm_native.c
@@ -26,6 +26,7 @@
#include <linux/time.h>
#include <linux/pm_qos_params.h>
#include <linux/uio.h>
+#include <linux/dma-mapping.h>
#include <sound/core.h>
#include <sound/control.h>
#include <sound/info.h>
@@ -3061,6 +3062,27 @@ static int snd_pcm_mmap_control(struct snd_pcm_substream *substream, struct file
}
#endif /* coherent mmap */
+static inline struct page *
+snd_pcm_default_page_ops(struct snd_pcm_substream *substream, unsigned long ofs)
+{
+ void *vaddr = substream->runtime->dma_area + ofs;
+#if defined(CONFIG_MIPS) && defined(CONFIG_DMA_NONCOHERENT)
+ if (substream->dma_buffer.dev.type == SNDRV_DMA_TYPE_DEV)
+ return virt_to_page(CAC_ADDR(vaddr));
+#endif
+#if defined(CONFIG_PPC32) && defined(CONFIG_NOT_COHERENT_CACHE)
+ if (substream->dma_buffer.dev.type == SNDRV_DMA_TYPE_DEV) {
+ dma_addr_t addr = substream->runtime->dma_addr + ofs;
+ addr -= get_dma_offset(substream->dma_buffer.dev.dev);
+ /* assume dma_handle set via pfn_to_phys() in
+ * mm/dma-noncoherent.c
+ */
+ return pfn_to_page(addr >> PAGE_SHIFT);
+ }
+#endif
+ return virt_to_page(vaddr);
+}
+
/*
* fault callback for mmapping a RAM page
*/
@@ -3071,7 +3093,6 @@ static int snd_pcm_mmap_data_fault(struct vm_area_struct *area,
struct snd_pcm_runtime *runtime;
unsigned long offset;
struct page * page;
- void *vaddr;
size_t dma_bytes;
if (substream == NULL)
@@ -3081,36 +3102,53 @@ static int snd_pcm_mmap_data_fault(struct vm_area_struct *area,
dma_bytes = PAGE_ALIGN(runtime->dma_bytes);
if (offset > dma_bytes - PAGE_SIZE)
return VM_FAULT_SIGBUS;
- if (substream->ops->page) {
+ if (substream->ops->page)
page = substream->ops->page(substream, offset);
- if (!page)
- return VM_FAULT_SIGBUS;
- } else {
- vaddr = runtime->dma_area + offset;
- page = virt_to_page(vaddr);
- }
+ else
+ page = snd_pcm_default_page_ops(substream, offset);
+ if (!page)
+ return VM_FAULT_SIGBUS;
get_page(page);
vmf->page = page;
return 0;
}
-static const struct vm_operations_struct snd_pcm_vm_ops_data =
-{
+static const struct vm_operations_struct snd_pcm_vm_ops_data = {
+ .open = snd_pcm_mmap_data_open,
+ .close = snd_pcm_mmap_data_close,
+};
+
+static const struct vm_operations_struct snd_pcm_vm_ops_data_fault = {
.open = snd_pcm_mmap_data_open,
.close = snd_pcm_mmap_data_close,
.fault = snd_pcm_mmap_data_fault,
};
+#ifndef ARCH_HAS_DMA_MMAP_COHERENT
+/* This should be defined / handled globally! */
+#ifdef CONFIG_ARM
+#define ARCH_HAS_DMA_MMAP_COHERENT
+#endif
+#endif
+
/*
* mmap the DMA buffer on RAM
*/
static int snd_pcm_default_mmap(struct snd_pcm_substream *substream,
struct vm_area_struct *area)
{
- area->vm_ops = &snd_pcm_vm_ops_data;
- area->vm_private_data = substream;
area->vm_flags |= VM_RESERVED;
- atomic_inc(&substream->mmap_count);
+#ifdef ARCH_HAS_DMA_MMAP_COHERENT
+ if (!substream->ops->page &&
+ substream->dma_buffer.dev.type == SNDRV_DMA_TYPE_DEV)
+ return dma_mmap_coherent(substream->dma_buffer.dev.dev,
+ area,
+ substream->runtime->dma_area,
+ substream->runtime->dma_addr,
+ area->vm_end - area->vm_start);
+#endif /* ARCH_HAS_DMA_MMAP_COHERENT */
+ /* mmap with fault handler */
+ area->vm_ops = &snd_pcm_vm_ops_data_fault;
return 0;
}
@@ -3118,12 +3156,6 @@ static int snd_pcm_default_mmap(struct snd_pcm_substream *substream,
* mmap the DMA buffer on I/O memory area
*/
#if SNDRV_PCM_INFO_MMAP_IOMEM
-static const struct vm_operations_struct snd_pcm_vm_ops_data_mmio =
-{
- .open = snd_pcm_mmap_data_open,
- .close = snd_pcm_mmap_data_close,
-};
-
int snd_pcm_lib_mmap_iomem(struct snd_pcm_substream *substream,
struct vm_area_struct *area)
{
@@ -3133,8 +3165,6 @@ int snd_pcm_lib_mmap_iomem(struct snd_pcm_substream *substream,
#ifdef pgprot_noncached
area->vm_page_prot = pgprot_noncached(area->vm_page_prot);
#endif
- area->vm_ops = &snd_pcm_vm_ops_data_mmio;
- area->vm_private_data = substream;
area->vm_flags |= VM_IO;
size = area->vm_end - area->vm_start;
offset = area->vm_pgoff << PAGE_SHIFT;
@@ -3142,7 +3172,6 @@ int snd_pcm_lib_mmap_iomem(struct snd_pcm_substream *substream,
(substream->runtime->dma_addr + offset) >> PAGE_SHIFT,
size, area->vm_page_prot))
return -EAGAIN;
- atomic_inc(&substream->mmap_count);
return 0;
}
@@ -3159,6 +3188,7 @@ int snd_pcm_mmap_data(struct snd_pcm_substream *substream, struct file *file,
long size;
unsigned long offset;
size_t dma_bytes;
+ int err;
if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
if (!(area->vm_flags & (VM_WRITE|VM_READ)))
@@ -3183,10 +3213,15 @@ int snd_pcm_mmap_data(struct snd_pcm_substream *substream, struct file *file,
if (offset > dma_bytes - size)
return -EINVAL;
+ area->vm_ops = &snd_pcm_vm_ops_data;
+ area->vm_private_data = substream;
if (substream->ops->mmap)
- return substream->ops->mmap(substream, area);
+ err = substream->ops->mmap(substream, area);
else
- return snd_pcm_default_mmap(substream, area);
+ err = snd_pcm_default_mmap(substream, area);
+ if (!err)
+ atomic_inc(&substream->mmap_count);
+ return err;
}
EXPORT_SYMBOL(snd_pcm_mmap_data);
diff --git a/sound/core/rawmidi.c b/sound/core/rawmidi.c
index 70d6f25ba526..2f766123b158 100644
--- a/sound/core/rawmidi.c
+++ b/sound/core/rawmidi.c
@@ -242,8 +242,6 @@ static int assign_substream(struct snd_rawmidi *rmidi, int subdevice,
return -ENXIO;
if (subdevice >= 0 && subdevice >= s->substream_count)
return -ENODEV;
- if (s->substream_opened >= s->substream_count)
- return -EAGAIN;
list_for_each_entry(substream, &s->substreams, list) {
if (substream->opened) {
@@ -280,9 +278,10 @@ static int open_substream(struct snd_rawmidi *rmidi,
substream->active_sensing = 0;
if (mode & SNDRV_RAWMIDI_LFLG_APPEND)
substream->append = 1;
+ substream->pid = get_pid(task_pid(current));
+ rmidi->streams[substream->stream].substream_opened++;
}
substream->use_count++;
- rmidi->streams[substream->stream].substream_opened++;
return 0;
}
@@ -413,7 +412,7 @@ static int snd_rawmidi_open(struct inode *inode, struct file *file)
subdevice = -1;
read_lock(&card->ctl_files_rwlock);
list_for_each_entry(kctl, &card->ctl_files, list) {
- if (kctl->pid == current->pid) {
+ if (kctl->pid == task_pid(current)) {
subdevice = kctl->prefer_rawmidi_subdevice;
if (subdevice != -1)
break;
@@ -466,7 +465,6 @@ static void close_substream(struct snd_rawmidi *rmidi,
struct snd_rawmidi_substream *substream,
int cleanup)
{
- rmidi->streams[substream->stream].substream_opened--;
if (--substream->use_count)
return;
@@ -491,6 +489,9 @@ static void close_substream(struct snd_rawmidi *rmidi,
snd_rawmidi_runtime_free(substream);
substream->opened = 0;
substream->append = 0;
+ put_pid(substream->pid);
+ substream->pid = NULL;
+ rmidi->streams[substream->stream].substream_opened--;
}
static void rawmidi_release_priv(struct snd_rawmidi_file *rfile)
@@ -1338,6 +1339,9 @@ static void snd_rawmidi_proc_info_read(struct snd_info_entry *entry,
substream->number,
(unsigned long) substream->bytes);
if (substream->opened) {
+ snd_iprintf(buffer,
+ " Owner PID : %d\n",
+ pid_vnr(substream->pid));
runtime = substream->runtime;
snd_iprintf(buffer,
" Mode : %s\n"
@@ -1359,6 +1363,9 @@ static void snd_rawmidi_proc_info_read(struct snd_info_entry *entry,
substream->number,
(unsigned long) substream->bytes);
if (substream->opened) {
+ snd_iprintf(buffer,
+ " Owner PID : %d\n",
+ pid_vnr(substream->pid));
runtime = substream->runtime;
snd_iprintf(buffer,
" Buffer size : %lu\n"
diff --git a/sound/drivers/pcsp/pcsp.c b/sound/drivers/pcsp/pcsp.c
index b60cef257b58..f165c77d6273 100644
--- a/sound/drivers/pcsp/pcsp.c
+++ b/sound/drivers/pcsp/pcsp.c
@@ -26,6 +26,7 @@ MODULE_ALIAS("platform:pcspkr");
static int index = SNDRV_DEFAULT_IDX1; /* Index 0-MAX */
static char *id = SNDRV_DEFAULT_STR1; /* ID for this card */
static int enable = SNDRV_DEFAULT_ENABLE1; /* Enable this card */
+static int nopcm; /* Disable PCM capability of the driver */
module_param(index, int, 0444);
MODULE_PARM_DESC(index, "Index value for pcsp soundcard.");
@@ -33,6 +34,8 @@ module_param(id, charp, 0444);
MODULE_PARM_DESC(id, "ID string for pcsp soundcard.");
module_param(enable, bool, 0444);
MODULE_PARM_DESC(enable, "Enable PC-Speaker sound.");
+module_param(nopcm, bool, 0444);
+MODULE_PARM_DESC(nopcm, "Disable PC-Speaker PCM sound. Only beeps remain.");
struct snd_pcsp pcsp_chip;
@@ -43,13 +46,16 @@ static int __devinit snd_pcsp_create(struct snd_card *card)
int err;
int div, min_div, order;
- hrtimer_get_res(CLOCK_MONOTONIC, &tp);
- if (tp.tv_sec || tp.tv_nsec > PCSP_MAX_PERIOD_NS) {
- printk(KERN_ERR "PCSP: Timer resolution is not sufficient "
- "(%linS)\n", tp.tv_nsec);
- printk(KERN_ERR "PCSP: Make sure you have HPET and ACPI "
- "enabled.\n");
- return -EIO;
+ if (!nopcm) {
+ hrtimer_get_res(CLOCK_MONOTONIC, &tp);
+ if (tp.tv_sec || tp.tv_nsec > PCSP_MAX_PERIOD_NS) {
+ printk(KERN_ERR "PCSP: Timer resolution is not sufficient "
+ "(%linS)\n", tp.tv_nsec);
+ printk(KERN_ERR "PCSP: Make sure you have HPET and ACPI "
+ "enabled.\n");
+ printk(KERN_ERR "PCSP: Turned into nopcm mode.\n");
+ nopcm = 1;
+ }
}
if (loops_per_jiffy >= PCSP_MIN_LPJ && tp.tv_nsec <= PCSP_MIN_PERIOD_NS)
@@ -107,12 +113,14 @@ static int __devinit snd_card_pcsp_probe(int devnum, struct device *dev)
snd_card_free(card);
return err;
}
- err = snd_pcsp_new_pcm(&pcsp_chip);
- if (err < 0) {
- snd_card_free(card);
- return err;
+ if (!nopcm) {
+ err = snd_pcsp_new_pcm(&pcsp_chip);
+ if (err < 0) {
+ snd_card_free(card);
+ return err;
+ }
}
- err = snd_pcsp_new_mixer(&pcsp_chip);
+ err = snd_pcsp_new_mixer(&pcsp_chip, nopcm);
if (err < 0) {
snd_card_free(card);
return err;
diff --git a/sound/drivers/pcsp/pcsp.h b/sound/drivers/pcsp/pcsp.h
index 174dd2ff0f22..1e123077923d 100644
--- a/sound/drivers/pcsp/pcsp.h
+++ b/sound/drivers/pcsp/pcsp.h
@@ -83,6 +83,6 @@ extern enum hrtimer_restart pcsp_do_timer(struct hrtimer *handle);
extern void pcsp_sync_stop(struct snd_pcsp *chip);
extern int snd_pcsp_new_pcm(struct snd_pcsp *chip);
-extern int snd_pcsp_new_mixer(struct snd_pcsp *chip);
+extern int snd_pcsp_new_mixer(struct snd_pcsp *chip, int nopcm);
#endif
diff --git a/sound/drivers/pcsp/pcsp_mixer.c b/sound/drivers/pcsp/pcsp_mixer.c
index 903bc846763f..6f633f4f3b96 100644
--- a/sound/drivers/pcsp/pcsp_mixer.c
+++ b/sound/drivers/pcsp/pcsp_mixer.c
@@ -119,24 +119,43 @@ static int pcsp_pcspkr_put(struct snd_kcontrol *kcontrol,
.put = pcsp_##ctl_type##_put, \
}
-static struct snd_kcontrol_new __devinitdata snd_pcsp_controls[] = {
+static struct snd_kcontrol_new __devinitdata snd_pcsp_controls_pcm[] = {
PCSP_MIXER_CONTROL(enable, "Master Playback Switch"),
PCSP_MIXER_CONTROL(treble, "BaseFRQ Playback Volume"),
- PCSP_MIXER_CONTROL(pcspkr, "PC Speaker Playback Switch"),
};
-int __devinit snd_pcsp_new_mixer(struct snd_pcsp *chip)
+static struct snd_kcontrol_new __devinitdata snd_pcsp_controls_spkr[] = {
+ PCSP_MIXER_CONTROL(pcspkr, "Beep Playback Switch"),
+};
+
+static int __devinit snd_pcsp_ctls_add(struct snd_pcsp *chip,
+ struct snd_kcontrol_new *ctls, int num)
{
- struct snd_card *card = chip->card;
int i, err;
+ struct snd_card *card = chip->card;
+ for (i = 0; i < num; i++) {
+ err = snd_ctl_add(card, snd_ctl_new1(ctls + i, chip));
+ if (err < 0)
+ return err;
+ }
+ return 0;
+}
+
+int __devinit snd_pcsp_new_mixer(struct snd_pcsp *chip, int nopcm)
+{
+ int err;
+ struct snd_card *card = chip->card;
- for (i = 0; i < ARRAY_SIZE(snd_pcsp_controls); i++) {
- err = snd_ctl_add(card,
- snd_ctl_new1(snd_pcsp_controls + i,
- chip));
+ if (!nopcm) {
+ err = snd_pcsp_ctls_add(chip, snd_pcsp_controls_pcm,
+ ARRAY_SIZE(snd_pcsp_controls_pcm));
if (err < 0)
return err;
}
+ err = snd_pcsp_ctls_add(chip, snd_pcsp_controls_spkr,
+ ARRAY_SIZE(snd_pcsp_controls_spkr));
+ if (err < 0)
+ return err;
strcpy(card->mixername, "PC-Speaker");
diff --git a/sound/i2c/cs8427.c b/sound/i2c/cs8427.c
index 020a5d512472..04ae8704cdcd 100644
--- a/sound/i2c/cs8427.c
+++ b/sound/i2c/cs8427.c
@@ -23,6 +23,7 @@
#include <linux/slab.h>
#include <linux/delay.h>
#include <linux/init.h>
+#include <linux/bitrev.h>
#include <asm/unaligned.h>
#include <sound/core.h>
#include <sound/control.h>
@@ -55,18 +56,6 @@ struct cs8427 {
struct cs8427_stream capture;
};
-static unsigned char swapbits(unsigned char val)
-{
- int bit;
- unsigned char res = 0;
- for (bit = 0; bit < 8; bit++) {
- res <<= 1;
- res |= val & 1;
- val >>= 1;
- }
- return res;
-}
-
int snd_cs8427_reg_write(struct snd_i2c_device *device, unsigned char reg,
unsigned char val)
{
@@ -149,7 +138,7 @@ static int snd_cs8427_send_corudata(struct snd_i2c_device *device,
}
data[0] = CS8427_REG_AUTOINC | CS8427_REG_CORU_DATABUF;
for (idx = 0; idx < count; idx++)
- data[idx + 1] = swapbits(ndata[idx]);
+ data[idx + 1] = bitrev8(ndata[idx]);
if (snd_i2c_sendbytes(device, data, count + 1) != count + 1)
return -EIO;
return 1;
diff --git a/sound/i2c/other/Makefile b/sound/i2c/other/Makefile
index 703d954238f4..2dad40f3f622 100644
--- a/sound/i2c/other/Makefile
+++ b/sound/i2c/other/Makefile
@@ -5,6 +5,7 @@
snd-ak4114-objs := ak4114.o
snd-ak4117-objs := ak4117.o
+snd-ak4113-objs := ak4113.o
snd-ak4xxx-adda-objs := ak4xxx-adda.o
snd-pt2258-objs := pt2258.o
snd-tea575x-tuner-objs := tea575x-tuner.o
@@ -12,5 +13,5 @@ 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-ak4114.o snd-ak4xxx-adda.o snd-pt2258.o
+obj-$(CONFIG_SND_ICE1724) += snd-ak4114.o snd-ak4113.o snd-ak4xxx-adda.o snd-pt2258.o
obj-$(CONFIG_SND_FM801_TEA575X) += snd-tea575x-tuner.o
diff --git a/sound/i2c/other/ak4113.c b/sound/i2c/other/ak4113.c
new file mode 100644
index 000000000000..fff62cc8607c
--- /dev/null
+++ b/sound/i2c/other/ak4113.c
@@ -0,0 +1,639 @@
+/*
+ * Routines for control of the AK4113 via I2C/4-wire serial interface
+ * IEC958 (S/PDIF) receiver by Asahi Kasei
+ * Copyright (c) by Jaroslav Kysela <perex@perex.cz>
+ * Copyright (c) by Pavel Hofman <pavel.hofman@ivitera.com>
+ *
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ */
+
+#include <linux/slab.h>
+#include <linux/delay.h>
+#include <sound/core.h>
+#include <sound/control.h>
+#include <sound/pcm.h>
+#include <sound/ak4113.h>
+#include <sound/asoundef.h>
+#include <sound/info.h>
+
+MODULE_AUTHOR("Pavel Hofman <pavel.hofman@ivitera.com>");
+MODULE_DESCRIPTION("AK4113 IEC958 (S/PDIF) receiver by Asahi Kasei");
+MODULE_LICENSE("GPL");
+
+#define AK4113_ADDR 0x00 /* fixed address */
+
+static void ak4113_stats(struct work_struct *work);
+static void ak4113_init_regs(struct ak4113 *chip);
+
+
+static void reg_write(struct ak4113 *ak4113, unsigned char reg,
+ unsigned char val)
+{
+ ak4113->write(ak4113->private_data, reg, val);
+ if (reg < sizeof(ak4113->regmap))
+ ak4113->regmap[reg] = val;
+}
+
+static inline unsigned char reg_read(struct ak4113 *ak4113, unsigned char reg)
+{
+ return ak4113->read(ak4113->private_data, reg);
+}
+
+static void snd_ak4113_free(struct ak4113 *chip)
+{
+ chip->init = 1; /* don't schedule new work */
+ mb();
+ cancel_delayed_work(&chip->work);
+ flush_scheduled_work();
+ kfree(chip);
+}
+
+static int snd_ak4113_dev_free(struct snd_device *device)
+{
+ struct ak4113 *chip = device->device_data;
+ snd_ak4113_free(chip);
+ return 0;
+}
+
+int snd_ak4113_create(struct snd_card *card, ak4113_read_t *read,
+ ak4113_write_t *write, const unsigned char pgm[5],
+ void *private_data, struct ak4113 **r_ak4113)
+{
+ struct ak4113 *chip;
+ int err = 0;
+ unsigned char reg;
+ static struct snd_device_ops ops = {
+ .dev_free = snd_ak4113_dev_free,
+ };
+
+ chip = kzalloc(sizeof(*chip), GFP_KERNEL);
+ if (chip == NULL)
+ return -ENOMEM;
+ spin_lock_init(&chip->lock);
+ chip->card = card;
+ chip->read = read;
+ chip->write = write;
+ chip->private_data = private_data;
+ INIT_DELAYED_WORK(&chip->work, ak4113_stats);
+
+ for (reg = 0; reg < AK4113_WRITABLE_REGS ; reg++)
+ chip->regmap[reg] = pgm[reg];
+ ak4113_init_regs(chip);
+
+ chip->rcs0 = reg_read(chip, AK4113_REG_RCS0) & ~(AK4113_QINT |
+ AK4113_CINT | AK4113_STC);
+ chip->rcs1 = reg_read(chip, AK4113_REG_RCS1);
+ chip->rcs2 = reg_read(chip, AK4113_REG_RCS2);
+ err = snd_device_new(card, SNDRV_DEV_LOWLEVEL, chip, &ops);
+ if (err < 0)
+ goto __fail;
+
+ if (r_ak4113)
+ *r_ak4113 = chip;
+ return 0;
+
+__fail:
+ snd_ak4113_free(chip);
+ return err < 0 ? err : -EIO;
+}
+EXPORT_SYMBOL_GPL(snd_ak4113_create);
+
+void snd_ak4113_reg_write(struct ak4113 *chip, unsigned char reg,
+ unsigned char mask, unsigned char val)
+{
+ if (reg >= AK4113_WRITABLE_REGS)
+ return;
+ reg_write(chip, reg, (chip->regmap[reg] & ~mask) | val);
+}
+EXPORT_SYMBOL_GPL(snd_ak4113_reg_write);
+
+static void ak4113_init_regs(struct ak4113 *chip)
+{
+ unsigned char old = chip->regmap[AK4113_REG_PWRDN], reg;
+
+ /* bring the chip to reset state and powerdown state */
+ reg_write(chip, AK4113_REG_PWRDN, old & ~(AK4113_RST|AK4113_PWN));
+ udelay(200);
+ /* release reset, but leave powerdown */
+ reg_write(chip, AK4113_REG_PWRDN, (old | AK4113_RST) & ~AK4113_PWN);
+ udelay(200);
+ for (reg = 1; reg < AK4113_WRITABLE_REGS; reg++)
+ reg_write(chip, reg, chip->regmap[reg]);
+ /* release powerdown, everything is initialized now */
+ reg_write(chip, AK4113_REG_PWRDN, old | AK4113_RST | AK4113_PWN);
+}
+
+void snd_ak4113_reinit(struct ak4113 *chip)
+{
+ chip->init = 1;
+ mb();
+ flush_scheduled_work();
+ ak4113_init_regs(chip);
+ /* bring up statistics / event queing */
+ chip->init = 0;
+ if (chip->kctls[0])
+ schedule_delayed_work(&chip->work, HZ / 10);
+}
+EXPORT_SYMBOL_GPL(snd_ak4113_reinit);
+
+static unsigned int external_rate(unsigned char rcs1)
+{
+ switch (rcs1 & (AK4113_FS0|AK4113_FS1|AK4113_FS2|AK4113_FS3)) {
+ case AK4113_FS_8000HZ:
+ return 8000;
+ case AK4113_FS_11025HZ:
+ return 11025;
+ case AK4113_FS_16000HZ:
+ return 16000;
+ case AK4113_FS_22050HZ:
+ return 22050;
+ case AK4113_FS_24000HZ:
+ return 24000;
+ case AK4113_FS_32000HZ:
+ return 32000;
+ case AK4113_FS_44100HZ:
+ return 44100;
+ case AK4113_FS_48000HZ:
+ return 48000;
+ case AK4113_FS_64000HZ:
+ return 64000;
+ case AK4113_FS_88200HZ:
+ return 88200;
+ case AK4113_FS_96000HZ:
+ return 96000;
+ case AK4113_FS_176400HZ:
+ return 176400;
+ case AK4113_FS_192000HZ:
+ return 192000;
+ default:
+ return 0;
+ }
+}
+
+static int snd_ak4113_in_error_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;
+ uinfo->value.integer.max = LONG_MAX;
+ return 0;
+}
+
+static int snd_ak4113_in_error_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct ak4113 *chip = snd_kcontrol_chip(kcontrol);
+ long *ptr;
+
+ spin_lock_irq(&chip->lock);
+ ptr = (long *)(((char *)chip) + kcontrol->private_value);
+ ucontrol->value.integer.value[0] = *ptr;
+ *ptr = 0;
+ spin_unlock_irq(&chip->lock);
+ return 0;
+}
+
+#define snd_ak4113_in_bit_info snd_ctl_boolean_mono_info
+
+static int snd_ak4113_in_bit_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct ak4113 *chip = snd_kcontrol_chip(kcontrol);
+ unsigned char reg = kcontrol->private_value & 0xff;
+ unsigned char bit = (kcontrol->private_value >> 8) & 0xff;
+ unsigned char inv = (kcontrol->private_value >> 31) & 1;
+
+ ucontrol->value.integer.value[0] =
+ ((reg_read(chip, reg) & (1 << bit)) ? 1 : 0) ^ inv;
+ return 0;
+}
+
+static int snd_ak4113_rx_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;
+ uinfo->value.integer.max = 5;
+ return 0;
+}
+
+static int snd_ak4113_rx_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct ak4113 *chip = snd_kcontrol_chip(kcontrol);
+
+ ucontrol->value.integer.value[0] =
+ (AK4113_IPS(chip->regmap[AK4113_REG_IO1]));
+ return 0;
+}
+
+static int snd_ak4113_rx_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct ak4113 *chip = snd_kcontrol_chip(kcontrol);
+ int change;
+ u8 old_val;
+
+ spin_lock_irq(&chip->lock);
+ old_val = chip->regmap[AK4113_REG_IO1];
+ change = ucontrol->value.integer.value[0] != AK4113_IPS(old_val);
+ if (change)
+ reg_write(chip, AK4113_REG_IO1,
+ (old_val & (~AK4113_IPS(0xff))) |
+ (AK4113_IPS(ucontrol->value.integer.value[0])));
+ spin_unlock_irq(&chip->lock);
+ return change;
+}
+
+static int snd_ak4113_rate_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;
+ uinfo->value.integer.max = 192000;
+ return 0;
+}
+
+static int snd_ak4113_rate_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct ak4113 *chip = snd_kcontrol_chip(kcontrol);
+
+ ucontrol->value.integer.value[0] = external_rate(reg_read(chip,
+ AK4113_REG_RCS1));
+ return 0;
+}
+
+static int snd_ak4113_spdif_info(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_info *uinfo)
+{
+ uinfo->type = SNDRV_CTL_ELEM_TYPE_IEC958;
+ uinfo->count = 1;
+ return 0;
+}
+
+static int snd_ak4113_spdif_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct ak4113 *chip = snd_kcontrol_chip(kcontrol);
+ unsigned i;
+
+ for (i = 0; i < AK4113_REG_RXCSB_SIZE; i++)
+ ucontrol->value.iec958.status[i] = reg_read(chip,
+ AK4113_REG_RXCSB0 + i);
+ return 0;
+}
+
+static int snd_ak4113_spdif_mask_info(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_info *uinfo)
+{
+ uinfo->type = SNDRV_CTL_ELEM_TYPE_IEC958;
+ uinfo->count = 1;
+ return 0;
+}
+
+static int snd_ak4113_spdif_mask_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ memset(ucontrol->value.iec958.status, 0xff, AK4113_REG_RXCSB_SIZE);
+ return 0;
+}
+
+static int snd_ak4113_spdif_pinfo(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_info *uinfo)
+{
+ uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
+ uinfo->value.integer.min = 0;
+ uinfo->value.integer.max = 0xffff;
+ uinfo->count = 4;
+ return 0;
+}
+
+static int snd_ak4113_spdif_pget(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct ak4113 *chip = snd_kcontrol_chip(kcontrol);
+ unsigned short tmp;
+
+ ucontrol->value.integer.value[0] = 0xf8f2;
+ ucontrol->value.integer.value[1] = 0x4e1f;
+ tmp = reg_read(chip, AK4113_REG_Pc0) |
+ (reg_read(chip, AK4113_REG_Pc1) << 8);
+ ucontrol->value.integer.value[2] = tmp;
+ tmp = reg_read(chip, AK4113_REG_Pd0) |
+ (reg_read(chip, AK4113_REG_Pd1) << 8);
+ ucontrol->value.integer.value[3] = tmp;
+ return 0;
+}
+
+static int snd_ak4113_spdif_qinfo(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_info *uinfo)
+{
+ uinfo->type = SNDRV_CTL_ELEM_TYPE_BYTES;
+ uinfo->count = AK4113_REG_QSUB_SIZE;
+ return 0;
+}
+
+static int snd_ak4113_spdif_qget(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct ak4113 *chip = snd_kcontrol_chip(kcontrol);
+ unsigned i;
+
+ for (i = 0; i < AK4113_REG_QSUB_SIZE; i++)
+ ucontrol->value.bytes.data[i] = reg_read(chip,
+ AK4113_REG_QSUB_ADDR + i);
+ return 0;
+}
+
+/* Don't forget to change AK4113_CONTROLS define!!! */
+static struct snd_kcontrol_new snd_ak4113_iec958_controls[] = {
+{
+ .iface = SNDRV_CTL_ELEM_IFACE_PCM,
+ .name = "IEC958 Parity Errors",
+ .access = SNDRV_CTL_ELEM_ACCESS_READ |
+ SNDRV_CTL_ELEM_ACCESS_VOLATILE,
+ .info = snd_ak4113_in_error_info,
+ .get = snd_ak4113_in_error_get,
+ .private_value = offsetof(struct ak4113, parity_errors),
+},
+{
+ .iface = SNDRV_CTL_ELEM_IFACE_PCM,
+ .name = "IEC958 V-Bit Errors",
+ .access = SNDRV_CTL_ELEM_ACCESS_READ |
+ SNDRV_CTL_ELEM_ACCESS_VOLATILE,
+ .info = snd_ak4113_in_error_info,
+ .get = snd_ak4113_in_error_get,
+ .private_value = offsetof(struct ak4113, v_bit_errors),
+},
+{
+ .iface = SNDRV_CTL_ELEM_IFACE_PCM,
+ .name = "IEC958 C-CRC Errors",
+ .access = SNDRV_CTL_ELEM_ACCESS_READ |
+ SNDRV_CTL_ELEM_ACCESS_VOLATILE,
+ .info = snd_ak4113_in_error_info,
+ .get = snd_ak4113_in_error_get,
+ .private_value = offsetof(struct ak4113, ccrc_errors),
+},
+{
+ .iface = SNDRV_CTL_ELEM_IFACE_PCM,
+ .name = "IEC958 Q-CRC Errors",
+ .access = SNDRV_CTL_ELEM_ACCESS_READ |
+ SNDRV_CTL_ELEM_ACCESS_VOLATILE,
+ .info = snd_ak4113_in_error_info,
+ .get = snd_ak4113_in_error_get,
+ .private_value = offsetof(struct ak4113, qcrc_errors),
+},
+{
+ .iface = SNDRV_CTL_ELEM_IFACE_PCM,
+ .name = "IEC958 External Rate",
+ .access = SNDRV_CTL_ELEM_ACCESS_READ |
+ SNDRV_CTL_ELEM_ACCESS_VOLATILE,
+ .info = snd_ak4113_rate_info,
+ .get = snd_ak4113_rate_get,
+},
+{
+ .iface = SNDRV_CTL_ELEM_IFACE_PCM,
+ .name = SNDRV_CTL_NAME_IEC958("", CAPTURE, MASK),
+ .access = SNDRV_CTL_ELEM_ACCESS_READ,
+ .info = snd_ak4113_spdif_mask_info,
+ .get = snd_ak4113_spdif_mask_get,
+},
+{
+ .iface = SNDRV_CTL_ELEM_IFACE_PCM,
+ .name = SNDRV_CTL_NAME_IEC958("", CAPTURE, DEFAULT),
+ .access = SNDRV_CTL_ELEM_ACCESS_READ |
+ SNDRV_CTL_ELEM_ACCESS_VOLATILE,
+ .info = snd_ak4113_spdif_info,
+ .get = snd_ak4113_spdif_get,
+},
+{
+ .iface = SNDRV_CTL_ELEM_IFACE_PCM,
+ .name = "IEC958 Preample Capture Default",
+ .access = SNDRV_CTL_ELEM_ACCESS_READ |
+ SNDRV_CTL_ELEM_ACCESS_VOLATILE,
+ .info = snd_ak4113_spdif_pinfo,
+ .get = snd_ak4113_spdif_pget,
+},
+{
+ .iface = SNDRV_CTL_ELEM_IFACE_PCM,
+ .name = "IEC958 Q-subcode Capture Default",
+ .access = SNDRV_CTL_ELEM_ACCESS_READ |
+ SNDRV_CTL_ELEM_ACCESS_VOLATILE,
+ .info = snd_ak4113_spdif_qinfo,
+ .get = snd_ak4113_spdif_qget,
+},
+{
+ .iface = SNDRV_CTL_ELEM_IFACE_PCM,
+ .name = "IEC958 Audio",
+ .access = SNDRV_CTL_ELEM_ACCESS_READ |
+ SNDRV_CTL_ELEM_ACCESS_VOLATILE,
+ .info = snd_ak4113_in_bit_info,
+ .get = snd_ak4113_in_bit_get,
+ .private_value = (1<<31) | (1<<8) | AK4113_REG_RCS0,
+},
+{
+ .iface = SNDRV_CTL_ELEM_IFACE_PCM,
+ .name = "IEC958 Non-PCM Bitstream",
+ .access = SNDRV_CTL_ELEM_ACCESS_READ |
+ SNDRV_CTL_ELEM_ACCESS_VOLATILE,
+ .info = snd_ak4113_in_bit_info,
+ .get = snd_ak4113_in_bit_get,
+ .private_value = (0<<8) | AK4113_REG_RCS1,
+},
+{
+ .iface = SNDRV_CTL_ELEM_IFACE_PCM,
+ .name = "IEC958 DTS Bitstream",
+ .access = SNDRV_CTL_ELEM_ACCESS_READ |
+ SNDRV_CTL_ELEM_ACCESS_VOLATILE,
+ .info = snd_ak4113_in_bit_info,
+ .get = snd_ak4113_in_bit_get,
+ .private_value = (1<<8) | AK4113_REG_RCS1,
+},
+{
+ .iface = SNDRV_CTL_ELEM_IFACE_PCM,
+ .name = "AK4113 Input Select",
+ .access = SNDRV_CTL_ELEM_ACCESS_READ |
+ SNDRV_CTL_ELEM_ACCESS_WRITE,
+ .info = snd_ak4113_rx_info,
+ .get = snd_ak4113_rx_get,
+ .put = snd_ak4113_rx_put,
+}
+};
+
+static void snd_ak4113_proc_regs_read(struct snd_info_entry *entry,
+ struct snd_info_buffer *buffer)
+{
+ struct ak4113 *ak4113 = entry->private_data;
+ int reg, val;
+ /* all ak4113 registers 0x00 - 0x1c */
+ for (reg = 0; reg < 0x1d; reg++) {
+ val = reg_read(ak4113, reg);
+ snd_iprintf(buffer, "0x%02x = 0x%02x\n", reg, val);
+ }
+}
+
+static void snd_ak4113_proc_init(struct ak4113 *ak4113)
+{
+ struct snd_info_entry *entry;
+ if (!snd_card_proc_new(ak4113->card, "ak4113", &entry))
+ snd_info_set_text_ops(entry, ak4113, snd_ak4113_proc_regs_read);
+}
+
+int snd_ak4113_build(struct ak4113 *ak4113,
+ struct snd_pcm_substream *cap_substream)
+{
+ struct snd_kcontrol *kctl;
+ unsigned int idx;
+ int err;
+
+ if (snd_BUG_ON(!cap_substream))
+ return -EINVAL;
+ ak4113->substream = cap_substream;
+ for (idx = 0; idx < AK4113_CONTROLS; idx++) {
+ kctl = snd_ctl_new1(&snd_ak4113_iec958_controls[idx], ak4113);
+ if (kctl == NULL)
+ return -ENOMEM;
+ kctl->id.device = cap_substream->pcm->device;
+ kctl->id.subdevice = cap_substream->number;
+ err = snd_ctl_add(ak4113->card, kctl);
+ if (err < 0)
+ return err;
+ ak4113->kctls[idx] = kctl;
+ }
+ snd_ak4113_proc_init(ak4113);
+ /* trigger workq */
+ schedule_delayed_work(&ak4113->work, HZ / 10);
+ return 0;
+}
+EXPORT_SYMBOL_GPL(snd_ak4113_build);
+
+int snd_ak4113_external_rate(struct ak4113 *ak4113)
+{
+ unsigned char rcs1;
+
+ rcs1 = reg_read(ak4113, AK4113_REG_RCS1);
+ return external_rate(rcs1);
+}
+EXPORT_SYMBOL_GPL(snd_ak4113_external_rate);
+
+int snd_ak4113_check_rate_and_errors(struct ak4113 *ak4113, unsigned int flags)
+{
+ struct snd_pcm_runtime *runtime =
+ ak4113->substream ? ak4113->substream->runtime : NULL;
+ unsigned long _flags;
+ int res = 0;
+ unsigned char rcs0, rcs1, rcs2;
+ unsigned char c0, c1;
+
+ rcs1 = reg_read(ak4113, AK4113_REG_RCS1);
+ if (flags & AK4113_CHECK_NO_STAT)
+ goto __rate;
+ rcs0 = reg_read(ak4113, AK4113_REG_RCS0);
+ rcs2 = reg_read(ak4113, AK4113_REG_RCS2);
+ spin_lock_irqsave(&ak4113->lock, _flags);
+ if (rcs0 & AK4113_PAR)
+ ak4113->parity_errors++;
+ if (rcs0 & AK4113_V)
+ ak4113->v_bit_errors++;
+ if (rcs2 & AK4113_CCRC)
+ ak4113->ccrc_errors++;
+ if (rcs2 & AK4113_QCRC)
+ ak4113->qcrc_errors++;
+ c0 = (ak4113->rcs0 & (AK4113_QINT | AK4113_CINT | AK4113_STC |
+ AK4113_AUDION | AK4113_AUTO | AK4113_UNLCK)) ^
+ (rcs0 & (AK4113_QINT | AK4113_CINT | AK4113_STC |
+ AK4113_AUDION | AK4113_AUTO | AK4113_UNLCK));
+ c1 = (ak4113->rcs1 & (AK4113_DTSCD | AK4113_NPCM | AK4113_PEM |
+ AK4113_DAT | 0xf0)) ^
+ (rcs1 & (AK4113_DTSCD | AK4113_NPCM | AK4113_PEM |
+ AK4113_DAT | 0xf0));
+ ak4113->rcs0 = rcs0 & ~(AK4113_QINT | AK4113_CINT | AK4113_STC);
+ ak4113->rcs1 = rcs1;
+ ak4113->rcs2 = rcs2;
+ spin_unlock_irqrestore(&ak4113->lock, _flags);
+
+ if (rcs0 & AK4113_PAR)
+ snd_ctl_notify(ak4113->card, SNDRV_CTL_EVENT_MASK_VALUE,
+ &ak4113->kctls[0]->id);
+ if (rcs0 & AK4113_V)
+ snd_ctl_notify(ak4113->card, SNDRV_CTL_EVENT_MASK_VALUE,
+ &ak4113->kctls[1]->id);
+ if (rcs2 & AK4113_CCRC)
+ snd_ctl_notify(ak4113->card, SNDRV_CTL_EVENT_MASK_VALUE,
+ &ak4113->kctls[2]->id);
+ if (rcs2 & AK4113_QCRC)
+ snd_ctl_notify(ak4113->card, SNDRV_CTL_EVENT_MASK_VALUE,
+ &ak4113->kctls[3]->id);
+
+ /* rate change */
+ if (c1 & 0xf0)
+ snd_ctl_notify(ak4113->card, SNDRV_CTL_EVENT_MASK_VALUE,
+ &ak4113->kctls[4]->id);
+
+ if ((c1 & AK4113_PEM) | (c0 & AK4113_CINT))
+ snd_ctl_notify(ak4113->card, SNDRV_CTL_EVENT_MASK_VALUE,
+ &ak4113->kctls[6]->id);
+ if (c0 & AK4113_QINT)
+ snd_ctl_notify(ak4113->card, SNDRV_CTL_EVENT_MASK_VALUE,
+ &ak4113->kctls[8]->id);
+
+ if (c0 & AK4113_AUDION)
+ snd_ctl_notify(ak4113->card, SNDRV_CTL_EVENT_MASK_VALUE,
+ &ak4113->kctls[9]->id);
+ if (c1 & AK4113_NPCM)
+ snd_ctl_notify(ak4113->card, SNDRV_CTL_EVENT_MASK_VALUE,
+ &ak4113->kctls[10]->id);
+ if (c1 & AK4113_DTSCD)
+ snd_ctl_notify(ak4113->card, SNDRV_CTL_EVENT_MASK_VALUE,
+ &ak4113->kctls[11]->id);
+
+ if (ak4113->change_callback && (c0 | c1) != 0)
+ ak4113->change_callback(ak4113, c0, c1);
+
+__rate:
+ /* compare rate */
+ res = external_rate(rcs1);
+ if (!(flags & AK4113_CHECK_NO_RATE) && runtime &&
+ (runtime->rate != res)) {
+ snd_pcm_stream_lock_irqsave(ak4113->substream, _flags);
+ if (snd_pcm_running(ak4113->substream)) {
+ /*printk(KERN_DEBUG "rate changed (%i <- %i)\n",
+ * runtime->rate, res); */
+ snd_pcm_stop(ak4113->substream,
+ SNDRV_PCM_STATE_DRAINING);
+ wake_up(&runtime->sleep);
+ res = 1;
+ }
+ snd_pcm_stream_unlock_irqrestore(ak4113->substream, _flags);
+ }
+ return res;
+}
+EXPORT_SYMBOL_GPL(snd_ak4113_check_rate_and_errors);
+
+static void ak4113_stats(struct work_struct *work)
+{
+ struct ak4113 *chip = container_of(work, struct ak4113, work.work);
+
+ if (!chip->init)
+ snd_ak4113_check_rate_and_errors(chip, chip->check_flags);
+
+ schedule_delayed_work(&chip->work, HZ / 10);
+}
diff --git a/sound/i2c/other/ak4xxx-adda.c b/sound/i2c/other/ak4xxx-adda.c
index ee47abab764e..1adb8a3c2b62 100644
--- a/sound/i2c/other/ak4xxx-adda.c
+++ b/sound/i2c/other/ak4xxx-adda.c
@@ -19,7 +19,7 @@
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*
- */
+ */
#include <asm/io.h>
#include <linux/delay.h>
@@ -29,6 +29,7 @@
#include <sound/control.h>
#include <sound/tlv.h>
#include <sound/ak4xxx-adda.h>
+#include <sound/info.h>
MODULE_AUTHOR("Jaroslav Kysela <perex@perex.cz>, Takashi Iwai <tiwai@suse.de>");
MODULE_DESCRIPTION("Routines for control of AK452x / AK43xx AD/DA converters");
@@ -52,26 +53,21 @@ EXPORT_SYMBOL(snd_akm4xxx_write);
static void ak4524_reset(struct snd_akm4xxx *ak, int state)
{
unsigned int chip;
- unsigned char reg, maxreg;
+ unsigned char reg;
- if (ak->type == SND_AK4528)
- maxreg = 0x06;
- else
- maxreg = 0x08;
for (chip = 0; chip < ak->num_dacs/2; chip++) {
snd_akm4xxx_write(ak, chip, 0x01, state ? 0x00 : 0x03);
if (state)
continue;
/* DAC volumes */
- for (reg = 0x04; reg < maxreg; reg++)
+ for (reg = 0x04; reg < ak->total_regs; reg++)
snd_akm4xxx_write(ak, chip, reg,
snd_akm4xxx_get(ak, chip, reg));
}
}
/* reset procedure for AK4355 and AK4358 */
-static void ak435X_reset(struct snd_akm4xxx *ak, int state,
- unsigned char total_regs)
+static void ak435X_reset(struct snd_akm4xxx *ak, int state)
{
unsigned char reg;
@@ -79,7 +75,7 @@ static void ak435X_reset(struct snd_akm4xxx *ak, int state,
snd_akm4xxx_write(ak, 0, 0x01, 0x02); /* reset and soft-mute */
return;
}
- for (reg = 0x00; reg < total_regs; reg++)
+ for (reg = 0x00; reg < ak->total_regs; reg++)
if (reg != 0x01)
snd_akm4xxx_write(ak, 0, reg,
snd_akm4xxx_get(ak, 0, reg));
@@ -91,12 +87,11 @@ static void ak4381_reset(struct snd_akm4xxx *ak, int state)
{
unsigned int chip;
unsigned char reg;
-
for (chip = 0; chip < ak->num_dacs/2; chip++) {
snd_akm4xxx_write(ak, chip, 0x00, state ? 0x0c : 0x0f);
if (state)
continue;
- for (reg = 0x01; reg < 0x05; reg++)
+ for (reg = 0x01; reg < ak->total_regs; reg++)
snd_akm4xxx_write(ak, chip, reg,
snd_akm4xxx_get(ak, chip, reg));
}
@@ -113,16 +108,17 @@ void snd_akm4xxx_reset(struct snd_akm4xxx *ak, int state)
switch (ak->type) {
case SND_AK4524:
case SND_AK4528:
+ case SND_AK4620:
ak4524_reset(ak, state);
break;
case SND_AK4529:
/* FIXME: needed for ak4529? */
break;
case SND_AK4355:
- ak435X_reset(ak, state, 0x0b);
+ ak435X_reset(ak, state);
break;
case SND_AK4358:
- ak435X_reset(ak, state, 0x10);
+ ak435X_reset(ak, state);
break;
case SND_AK4381:
ak4381_reset(ak, state);
@@ -139,7 +135,7 @@ EXPORT_SYMBOL(snd_akm4xxx_reset);
* Volume conversion table for non-linear volumes
* from -63.5dB (mute) to 0dB step 0.5dB
*
- * Used for AK4524 input/ouput attenuation, AK4528, and
+ * Used for AK4524/AK4620 input/ouput attenuation, AK4528, and
* AK5365 input attenuation
*/
static const unsigned char vol_cvt_datt[128] = {
@@ -259,8 +255,22 @@ void snd_akm4xxx_init(struct snd_akm4xxx *ak)
0x00, 0x0f, /* 0: power-up, un-reset */
0xff, 0xff
};
+ static const unsigned char inits_ak4620[] = {
+ 0x00, 0x07, /* 0: normal */
+ 0x01, 0x00, /* 0: reset */
+ 0x01, 0x02, /* 1: RSTAD */
+ 0x01, 0x03, /* 1: RSTDA */
+ 0x01, 0x0f, /* 1: normal */
+ 0x02, 0x60, /* 2: 24bit I2S */
+ 0x03, 0x01, /* 3: deemphasis off */
+ 0x04, 0x00, /* 4: LIN muted */
+ 0x05, 0x00, /* 5: RIN muted */
+ 0x06, 0x00, /* 6: LOUT muted */
+ 0x07, 0x00, /* 7: ROUT muted */
+ 0xff, 0xff
+ };
- int chip, num_chips;
+ int chip;
const unsigned char *ptr, *inits;
unsigned char reg, data;
@@ -270,42 +280,64 @@ void snd_akm4xxx_init(struct snd_akm4xxx *ak)
switch (ak->type) {
case SND_AK4524:
inits = inits_ak4524;
- num_chips = ak->num_dacs / 2;
+ ak->num_chips = ak->num_dacs / 2;
+ ak->name = "ak4524";
+ ak->total_regs = 0x08;
break;
case SND_AK4528:
inits = inits_ak4528;
- num_chips = ak->num_dacs / 2;
+ ak->num_chips = ak->num_dacs / 2;
+ ak->name = "ak4528";
+ ak->total_regs = 0x06;
break;
case SND_AK4529:
inits = inits_ak4529;
- num_chips = 1;
+ ak->num_chips = 1;
+ ak->name = "ak4529";
+ ak->total_regs = 0x0d;
break;
case SND_AK4355:
inits = inits_ak4355;
- num_chips = 1;
+ ak->num_chips = 1;
+ ak->name = "ak4355";
+ ak->total_regs = 0x0b;
break;
case SND_AK4358:
inits = inits_ak4358;
- num_chips = 1;
+ ak->num_chips = 1;
+ ak->name = "ak4358";
+ ak->total_regs = 0x10;
break;
case SND_AK4381:
inits = inits_ak4381;
- num_chips = ak->num_dacs / 2;
+ ak->num_chips = ak->num_dacs / 2;
+ ak->name = "ak4381";
+ ak->total_regs = 0x05;
break;
case SND_AK5365:
/* FIXME: any init sequence? */
+ ak->num_chips = 1;
+ ak->name = "ak5365";
+ ak->total_regs = 0x08;
return;
+ case SND_AK4620:
+ inits = inits_ak4620;
+ ak->num_chips = ak->num_dacs / 2;
+ ak->name = "ak4620";
+ ak->total_regs = 0x08;
+ break;
default:
snd_BUG();
return;
}
- for (chip = 0; chip < num_chips; chip++) {
+ for (chip = 0; chip < ak->num_chips; chip++) {
ptr = inits;
while (*ptr != 0xff) {
reg = *ptr++;
data = *ptr++;
snd_akm4xxx_write(ak, chip, reg, data);
+ udelay(10);
}
}
}
@@ -688,6 +720,12 @@ static int build_dac_controls(struct snd_akm4xxx *ak)
AK_COMPOSE(idx/2, (idx%2) + 3, 0, 255);
knew.tlv.p = db_scale_linear;
break;
+ case SND_AK4620:
+ /* register 6 & 7 */
+ knew.private_value =
+ AK_COMPOSE(idx/2, (idx%2) + 6, 0, 255);
+ knew.tlv.p = db_scale_linear;
+ break;
default:
return -EINVAL;
}
@@ -704,10 +742,12 @@ static int build_dac_controls(struct snd_akm4xxx *ak)
static int build_adc_controls(struct snd_akm4xxx *ak)
{
- int idx, err, mixer_ch, num_stereo;
+ int idx, err, mixer_ch, num_stereo, max_steps;
struct snd_kcontrol_new knew;
mixer_ch = 0;
+ if (ak->type == SND_AK4528)
+ return 0; /* no controls */
for (idx = 0; idx < ak->num_adcs;) {
memset(&knew, 0, sizeof(knew));
if (! ak->adc_info || ! ak->adc_info[mixer_ch].name) {
@@ -733,13 +773,12 @@ static int build_adc_controls(struct snd_akm4xxx *ak)
}
/* register 4 & 5 */
if (ak->type == SND_AK5365)
- knew.private_value =
- AK_COMPOSE(idx/2, (idx%2) + 4, 0, 151) |
- AK_VOL_CVT | AK_IPGA;
+ max_steps = 152;
else
- knew.private_value =
- AK_COMPOSE(idx/2, (idx%2) + 4, 0, 163) |
- AK_VOL_CVT | AK_IPGA;
+ max_steps = 164;
+ knew.private_value =
+ AK_COMPOSE(idx/2, (idx%2) + 4, 0, max_steps) |
+ AK_VOL_CVT | AK_IPGA;
knew.tlv.p = db_scale_vol_datt;
err = snd_ctl_add(ak->card, snd_ctl_new1(&knew, ak));
if (err < 0)
@@ -808,6 +847,7 @@ static int build_deemphasis(struct snd_akm4xxx *ak, int num_emphs)
switch (ak->type) {
case SND_AK4524:
case SND_AK4528:
+ case SND_AK4620:
/* register 3 */
knew.private_value = AK_COMPOSE(idx, 3, 0, 0);
break;
@@ -834,6 +874,35 @@ static int build_deemphasis(struct snd_akm4xxx *ak, int num_emphs)
return 0;
}
+#ifdef CONFIG_PROC_FS
+static void proc_regs_read(struct snd_info_entry *entry,
+ struct snd_info_buffer *buffer)
+{
+ struct snd_akm4xxx *ak = (struct snd_akm4xxx *)entry->private_data;
+ int reg, val, chip;
+ for (chip = 0; chip < ak->num_chips; chip++) {
+ for (reg = 0; reg < ak->total_regs; reg++) {
+ val = snd_akm4xxx_get(ak, chip, reg);
+ snd_iprintf(buffer, "chip %d: 0x%02x = 0x%02x\n", chip,
+ reg, val);
+ }
+ }
+}
+
+static int proc_init(struct snd_akm4xxx *ak)
+{
+ struct snd_info_entry *entry;
+ int err;
+ err = snd_card_proc_new(ak->card, ak->name, &entry);
+ if (err < 0)
+ return err;
+ snd_info_set_text_ops(entry, ak, proc_regs_read);
+ return 0;
+}
+#else /* !CONFIG_PROC_FS */
+static int proc_init(struct snd_akm4xxx *ak) {}
+#endif
+
int snd_akm4xxx_build_controls(struct snd_akm4xxx *ak)
{
int err, num_emphs;
@@ -845,18 +914,21 @@ int snd_akm4xxx_build_controls(struct snd_akm4xxx *ak)
err = build_adc_controls(ak);
if (err < 0)
return err;
-
if (ak->type == SND_AK4355 || ak->type == SND_AK4358)
num_emphs = 1;
+ else if (ak->type == SND_AK4620)
+ num_emphs = 0;
else
num_emphs = ak->num_dacs / 2;
err = build_deemphasis(ak, num_emphs);
if (err < 0)
return err;
+ err = proc_init(ak);
+ if (err < 0)
+ return err;
return 0;
}
-
EXPORT_SYMBOL(snd_akm4xxx_build_controls);
static int __init alsa_akm4xxx_module_init(void)
diff --git a/sound/i2c/other/tea575x-tuner.c b/sound/i2c/other/tea575x-tuner.c
index d31c373e076d..c4c6ef73f9bf 100644
--- a/sound/i2c/other/tea575x-tuner.c
+++ b/sound/i2c/other/tea575x-tuner.c
@@ -225,7 +225,7 @@ static int vidioc_s_ctrl(struct file *file, void *priv,
case V4L2_CID_AUDIO_MUTE:
if (tea->ops->mute) {
tea->ops->mute(tea, ctrl->value);
- tea->mute = 1;
+ tea->mute = ctrl->value;
return 0;
}
}
diff --git a/sound/isa/Kconfig b/sound/isa/Kconfig
index 51a7e3777e17..02fe81ca88fd 100644
--- a/sound/isa/Kconfig
+++ b/sound/isa/Kconfig
@@ -372,15 +372,21 @@ config SND_SGALAXY
config SND_SSCAPE
tristate "Ensoniq SoundScape driver"
- select SND_HWDEP
select SND_MPU401_UART
select SND_WSS_LIB
+ select FW_LOADER
help
Say Y here to include support for Ensoniq SoundScape
- soundcards.
+ and Ensoniq OEM soundcards.
The PCM audio is supported on SoundScape Classic, Elite, PnP
- and VIVO cards. The MIDI support is very experimental.
+ and VIVO cards. The supported OEM cards are SPEA Media FX and
+ Reveal SC-600.
+ The MIDI support is very experimental and requires binary
+ firmware files called "scope.cod" and "sndscape.co?" where the
+ ? is digit 0, 1, 2, 3 or 4. The firmware files can be found
+ in DOS or Windows driver packages. One has to put the firmware
+ files into the /lib/firmware directory.
To compile this driver as a module, choose M here: the module
will be called snd-sscape.
diff --git a/sound/isa/cmi8330.c b/sound/isa/cmi8330.c
index 02f79d252718..8246aae32ab4 100644
--- a/sound/isa/cmi8330.c
+++ b/sound/isa/cmi8330.c
@@ -237,7 +237,7 @@ WSS_DOUBLE("Wavetable Capture Volume", 0,
CMI8330_WAVGAIN, CMI8330_WAVGAIN, 4, 0, 15, 0),
WSS_SINGLE("3D Control - Switch", 0,
CMI8330_RMUX3D, 5, 1, 1),
-WSS_SINGLE("PC Speaker Playback Volume", 0,
+WSS_SINGLE("Beep Playback Volume", 0,
CMI8330_OUTPUTVOL, 3, 3, 0),
WSS_DOUBLE("FM Playback Switch", 0,
CS4231_AUX2_LEFT_INPUT, CS4231_AUX2_RIGHT_INPUT, 7, 7, 1, 1),
@@ -262,7 +262,7 @@ SB_DOUBLE("SB Line Playback Switch", SB_DSP4_OUTPUT_SW, SB_DSP4_OUTPUT_SW, 4, 3,
SB_DOUBLE("SB Line Playback Volume", SB_DSP4_LINE_DEV, (SB_DSP4_LINE_DEV + 1), 3, 3, 31),
SB_SINGLE("SB Mic Playback Switch", SB_DSP4_OUTPUT_SW, 0, 1),
SB_SINGLE("SB Mic Playback Volume", SB_DSP4_MIC_DEV, 3, 31),
-SB_SINGLE("SB PC Speaker Volume", SB_DSP4_SPEAKER_DEV, 6, 3),
+SB_SINGLE("SB Beep Volume", SB_DSP4_SPEAKER_DEV, 6, 3),
SB_DOUBLE("SB Capture Volume", SB_DSP4_IGAIN_DEV, (SB_DSP4_IGAIN_DEV + 1), 6, 6, 3),
SB_DOUBLE("SB Playback Volume", SB_DSP4_OGAIN_DEV, (SB_DSP4_OGAIN_DEV + 1), 6, 6, 3),
SB_SINGLE("SB Mic Auto Gain", SB_DSP4_MIC_AGC, 0, 1),
diff --git a/sound/isa/cs423x/cs4236.c b/sound/isa/cs423x/cs4236.c
index a076a6ce8071..93fa6720d197 100644
--- a/sound/isa/cs423x/cs4236.c
+++ b/sound/isa/cs423x/cs4236.c
@@ -394,21 +394,15 @@ static int __devinit snd_cs423x_probe(struct snd_card *card, int dev)
return -EBUSY;
}
- err = snd_wss_create(card, port[dev], cport[dev],
+ err = snd_cs4236_create(card, port[dev], cport[dev],
irq[dev],
dma1[dev], dma2[dev],
WSS_HW_DETECT3, 0, &chip);
if (err < 0)
return err;
+
+ acard->chip = chip;
if (chip->hardware & WSS_HW_CS4236B_MASK) {
- snd_wss_free(chip);
- err = snd_cs4236_create(card,
- port[dev], cport[dev],
- irq[dev], dma1[dev], dma2[dev],
- WSS_HW_DETECT, 0, &chip);
- if (err < 0)
- return err;
- acard->chip = chip;
err = snd_cs4236_pcm(chip, 0, &pcm);
if (err < 0)
@@ -418,7 +412,6 @@ static int __devinit snd_cs423x_probe(struct snd_card *card, int dev)
if (err < 0)
return err;
} else {
- acard->chip = chip;
err = snd_wss_pcm(chip, 0, &pcm);
if (err < 0)
return err;
diff --git a/sound/isa/cs423x/cs4236_lib.c b/sound/isa/cs423x/cs4236_lib.c
index 38835f31298b..c5adca300632 100644
--- a/sound/isa/cs423x/cs4236_lib.c
+++ b/sound/isa/cs423x/cs4236_lib.c
@@ -87,6 +87,8 @@
#include <sound/core.h>
#include <sound/wss.h>
#include <sound/asoundef.h>
+#include <sound/initval.h>
+#include <sound/tlv.h>
/*
*
@@ -264,7 +266,10 @@ static void snd_cs4236_resume(struct snd_wss *chip)
}
#endif /* CONFIG_PM */
-
+/*
+ * This function does no fail if the chip is not CS4236B or compatible.
+ * It just an equivalent to the snd_wss_create() then.
+ */
int snd_cs4236_create(struct snd_card *card,
unsigned long port,
unsigned long cport,
@@ -281,21 +286,17 @@ int snd_cs4236_create(struct snd_card *card,
*rchip = NULL;
if (hardware == WSS_HW_DETECT)
hardware = WSS_HW_DETECT3;
- if (cport < 0x100) {
- snd_printk(KERN_ERR "please, specify control port "
- "for CS4236+ chips\n");
- return -ENODEV;
- }
+
err = snd_wss_create(card, port, cport,
irq, dma1, dma2, hardware, hwshare, &chip);
if (err < 0)
return err;
- if (!(chip->hardware & WSS_HW_CS4236B_MASK)) {
- snd_printk(KERN_ERR "CS4236+: MODE3 and extended registers "
- "not available, hardware=0x%x\n", chip->hardware);
- snd_device_free(card, chip);
- return -ENODEV;
+ if ((chip->hardware & WSS_HW_CS4236B_MASK) == 0) {
+ snd_printd("chip is not CS4236+, hardware=0x%x\n",
+ chip->hardware);
+ *rchip = chip;
+ return 0;
}
#if 0
{
@@ -308,9 +309,16 @@ int snd_cs4236_create(struct snd_card *card,
idx, snd_cs4236_ctrl_in(chip, idx));
}
#endif
+ if (cport < 0x100 || cport == SNDRV_AUTO_PORT) {
+ snd_printk(KERN_ERR "please, specify control port "
+ "for CS4236+ chips\n");
+ snd_device_free(card, chip);
+ return -ENODEV;
+ }
ver1 = snd_cs4236_ctrl_in(chip, 1);
ver2 = snd_cs4236_ext_in(chip, CS4236_VERSION);
- snd_printdd("CS4236: [0x%lx] C1 (version) = 0x%x, ext = 0x%x\n", cport, ver1, ver2);
+ snd_printdd("CS4236: [0x%lx] C1 (version) = 0x%x, ext = 0x%x\n",
+ cport, ver1, ver2);
if (ver1 != ver2) {
snd_printk(KERN_ERR "CS4236+ chip detected, but "
"control port 0x%lx is not valid\n", cport);
@@ -321,13 +329,17 @@ int snd_cs4236_create(struct snd_card *card,
snd_cs4236_ctrl_out(chip, 2, 0xff);
snd_cs4236_ctrl_out(chip, 3, 0x00);
snd_cs4236_ctrl_out(chip, 4, 0x80);
- snd_cs4236_ctrl_out(chip, 5, ((IEC958_AES1_CON_PCM_CODER & 3) << 6) | IEC958_AES0_CON_EMPHASIS_NONE);
+ reg = ((IEC958_AES1_CON_PCM_CODER & 3) << 6) |
+ IEC958_AES0_CON_EMPHASIS_NONE;
+ snd_cs4236_ctrl_out(chip, 5, reg);
snd_cs4236_ctrl_out(chip, 6, IEC958_AES1_CON_PCM_CODER >> 2);
snd_cs4236_ctrl_out(chip, 7, 0x00);
- /* 0x8c for C8 is valid for Turtle Beach Malibu - the IEC-958 output */
- /* is working with this setup, other hardware should have */
- /* different signal paths and this value should be selectable */
- /* in the future */
+ /*
+ * 0x8c for C8 is valid for Turtle Beach Malibu - the IEC-958
+ * output is working with this setup, other hardware should
+ * have different signal paths and this value should be
+ * selectable in the future
+ */
snd_cs4236_ctrl_out(chip, 8, 0x8c);
chip->rate_constraint = snd_cs4236_xrate;
chip->set_playback_format = snd_cs4236_playback_format;
@@ -339,9 +351,10 @@ int snd_cs4236_create(struct snd_card *card,
/* initialize extended registers */
for (reg = 0; reg < sizeof(snd_cs4236_ext_map); reg++)
- snd_cs4236_ext_out(chip, CS4236_I23VAL(reg), snd_cs4236_ext_map[reg]);
+ snd_cs4236_ext_out(chip, CS4236_I23VAL(reg),
+ snd_cs4236_ext_map[reg]);
- /* initialize compatible but more featured registers */
+ /* initialize compatible but more featured registers */
snd_wss_out(chip, CS4231_LEFT_INPUT, 0x40);
snd_wss_out(chip, CS4231_RIGHT_INPUT, 0x40);
snd_wss_out(chip, CS4231_AUX1_LEFT_INPUT, 0xff);
@@ -387,6 +400,14 @@ int snd_cs4236_pcm(struct snd_wss *chip, int device, struct snd_pcm **rpcm)
.get = snd_cs4236_get_single, .put = snd_cs4236_put_single, \
.private_value = reg | (shift << 8) | (mask << 16) | (invert << 24) }
+#define CS4236_SINGLE_TLV(xname, xindex, reg, shift, mask, invert, xtlv) \
+{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, .index = xindex, \
+ .access = SNDRV_CTL_ELEM_ACCESS_READWRITE | SNDRV_CTL_ELEM_ACCESS_TLV_READ, \
+ .info = snd_cs4236_info_single, \
+ .get = snd_cs4236_get_single, .put = snd_cs4236_put_single, \
+ .private_value = reg | (shift << 8) | (mask << 16) | (invert << 24), \
+ .tlv = { .p = (xtlv) } }
+
static int snd_cs4236_info_single(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *uinfo)
{
int mask = (kcontrol->private_value >> 16) & 0xff;
@@ -490,6 +511,16 @@ static int snd_cs4236_put_singlec(struct snd_kcontrol *kcontrol, struct snd_ctl_
.get = snd_cs4236_get_double, .put = snd_cs4236_put_double, \
.private_value = left_reg | (right_reg << 8) | (shift_left << 16) | (shift_right << 19) | (mask << 24) | (invert << 22) }
+#define CS4236_DOUBLE_TLV(xname, xindex, left_reg, right_reg, shift_left, \
+ shift_right, mask, invert, xtlv) \
+{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, .index = xindex, \
+ .access = SNDRV_CTL_ELEM_ACCESS_READWRITE | SNDRV_CTL_ELEM_ACCESS_TLV_READ, \
+ .info = snd_cs4236_info_double, \
+ .get = snd_cs4236_get_double, .put = snd_cs4236_put_double, \
+ .private_value = left_reg | (right_reg << 8) | (shift_left << 16) | \
+ (shift_right << 19) | (mask << 24) | (invert << 22), \
+ .tlv = { .p = (xtlv) } }
+
static int snd_cs4236_info_double(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *uinfo)
{
int mask = (kcontrol->private_value >> 24) & 0xff;
@@ -560,12 +591,23 @@ static int snd_cs4236_put_double(struct snd_kcontrol *kcontrol, struct snd_ctl_e
return change;
}
-#define CS4236_DOUBLE1(xname, xindex, left_reg, right_reg, shift_left, shift_right, mask, invert) \
+#define CS4236_DOUBLE1(xname, xindex, left_reg, right_reg, shift_left, \
+ shift_right, mask, invert) \
{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, .index = xindex, \
.info = snd_cs4236_info_double, \
.get = snd_cs4236_get_double1, .put = snd_cs4236_put_double1, \
.private_value = left_reg | (right_reg << 8) | (shift_left << 16) | (shift_right << 19) | (mask << 24) | (invert << 22) }
+#define CS4236_DOUBLE1_TLV(xname, xindex, left_reg, right_reg, shift_left, \
+ shift_right, mask, invert, xtlv) \
+{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, .index = xindex, \
+ .access = SNDRV_CTL_ELEM_ACCESS_READWRITE | SNDRV_CTL_ELEM_ACCESS_TLV_READ, \
+ .info = snd_cs4236_info_double, \
+ .get = snd_cs4236_get_double1, .put = snd_cs4236_put_double1, \
+ .private_value = left_reg | (right_reg << 8) | (shift_left << 16) | \
+ (shift_right << 19) | (mask << 24) | (invert << 22), \
+ .tlv = { .p = (xtlv) } }
+
static int snd_cs4236_get_double1(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
{
struct snd_wss *chip = snd_kcontrol_chip(kcontrol);
@@ -619,16 +661,18 @@ static int snd_cs4236_put_double1(struct snd_kcontrol *kcontrol, struct snd_ctl_
return change;
}
-#define CS4236_MASTER_DIGITAL(xname, xindex) \
+#define CS4236_MASTER_DIGITAL(xname, xindex, xtlv) \
{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, .index = xindex, \
+ .access = SNDRV_CTL_ELEM_ACCESS_READWRITE | SNDRV_CTL_ELEM_ACCESS_TLV_READ, \
.info = snd_cs4236_info_double, \
.get = snd_cs4236_get_master_digital, .put = snd_cs4236_put_master_digital, \
- .private_value = 71 << 24 }
+ .private_value = 71 << 24, \
+ .tlv = { .p = (xtlv) } }
static inline int snd_cs4236_mixer_master_digital_invert_volume(int vol)
{
return (vol < 64) ? 63 - vol : 64 + (71 - vol);
-}
+}
static int snd_cs4236_get_master_digital(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
{
@@ -661,11 +705,13 @@ static int snd_cs4236_put_master_digital(struct snd_kcontrol *kcontrol, struct s
return change;
}
-#define CS4235_OUTPUT_ACCU(xname, xindex) \
+#define CS4235_OUTPUT_ACCU(xname, xindex, xtlv) \
{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, .index = xindex, \
+ .access = SNDRV_CTL_ELEM_ACCESS_READWRITE | SNDRV_CTL_ELEM_ACCESS_TLV_READ, \
.info = snd_cs4236_info_double, \
.get = snd_cs4235_get_output_accu, .put = snd_cs4235_put_output_accu, \
- .private_value = 3 << 24 }
+ .private_value = 3 << 24, \
+ .tlv = { .p = (xtlv) } }
static inline int snd_cs4235_mixer_output_accu_get_volume(int vol)
{
@@ -720,41 +766,56 @@ static int snd_cs4235_put_output_accu(struct snd_kcontrol *kcontrol, struct snd_
return change;
}
+static const DECLARE_TLV_DB_SCALE(db_scale_7bit, -9450, 150, 0);
+static const DECLARE_TLV_DB_SCALE(db_scale_6bit, -9450, 150, 0);
+static const DECLARE_TLV_DB_SCALE(db_scale_6bit_12db_max, -8250, 150, 0);
+static const DECLARE_TLV_DB_SCALE(db_scale_5bit_12db_max, -3450, 150, 0);
+static const DECLARE_TLV_DB_SCALE(db_scale_5bit_22db_max, -2400, 150, 0);
+static const DECLARE_TLV_DB_SCALE(db_scale_4bit, -4500, 300, 0);
+static const DECLARE_TLV_DB_SCALE(db_scale_2bit, -1800, 600, 0);
+static const DECLARE_TLV_DB_SCALE(db_scale_rec_gain, 0, 150, 0);
+
static struct snd_kcontrol_new snd_cs4236_controls[] = {
CS4236_DOUBLE("Master Digital Playback Switch", 0,
CS4236_LEFT_MASTER, CS4236_RIGHT_MASTER, 7, 7, 1, 1),
CS4236_DOUBLE("Master Digital Capture Switch", 0,
CS4236_DAC_MUTE, CS4236_DAC_MUTE, 7, 6, 1, 1),
-CS4236_MASTER_DIGITAL("Master Digital Volume", 0),
+CS4236_MASTER_DIGITAL("Master Digital Volume", 0, db_scale_7bit),
-CS4236_DOUBLE("Capture Boost Volume", 0,
- CS4236_LEFT_MIX_CTRL, CS4236_RIGHT_MIX_CTRL, 5, 5, 3, 1),
+CS4236_DOUBLE_TLV("Capture Boost Volume", 0,
+ CS4236_LEFT_MIX_CTRL, CS4236_RIGHT_MIX_CTRL, 5, 5, 3, 1,
+ db_scale_2bit),
WSS_DOUBLE("PCM Playback Switch", 0,
CS4231_LEFT_OUTPUT, CS4231_RIGHT_OUTPUT, 7, 7, 1, 1),
-WSS_DOUBLE("PCM Playback Volume", 0,
- CS4231_LEFT_OUTPUT, CS4231_RIGHT_OUTPUT, 0, 0, 63, 1),
+WSS_DOUBLE_TLV("PCM Playback Volume", 0,
+ CS4231_LEFT_OUTPUT, CS4231_RIGHT_OUTPUT, 0, 0, 63, 1,
+ db_scale_6bit),
CS4236_DOUBLE("DSP Playback Switch", 0,
CS4236_LEFT_DSP, CS4236_RIGHT_DSP, 7, 7, 1, 1),
-CS4236_DOUBLE("DSP Playback Volume", 0,
- CS4236_LEFT_DSP, CS4236_RIGHT_DSP, 0, 0, 63, 1),
+CS4236_DOUBLE_TLV("DSP Playback Volume", 0,
+ CS4236_LEFT_DSP, CS4236_RIGHT_DSP, 0, 0, 63, 1,
+ db_scale_6bit),
CS4236_DOUBLE("FM Playback Switch", 0,
CS4236_LEFT_FM, CS4236_RIGHT_FM, 7, 7, 1, 1),
-CS4236_DOUBLE("FM Playback Volume", 0,
- CS4236_LEFT_FM, CS4236_RIGHT_FM, 0, 0, 63, 1),
+CS4236_DOUBLE_TLV("FM Playback Volume", 0,
+ CS4236_LEFT_FM, CS4236_RIGHT_FM, 0, 0, 63, 1,
+ db_scale_6bit),
CS4236_DOUBLE("Wavetable Playback Switch", 0,
CS4236_LEFT_WAVE, CS4236_RIGHT_WAVE, 7, 7, 1, 1),
-CS4236_DOUBLE("Wavetable Playback Volume", 0,
- CS4236_LEFT_WAVE, CS4236_RIGHT_WAVE, 0, 0, 63, 1),
+CS4236_DOUBLE_TLV("Wavetable Playback Volume", 0,
+ CS4236_LEFT_WAVE, CS4236_RIGHT_WAVE, 0, 0, 63, 1,
+ db_scale_6bit_12db_max),
WSS_DOUBLE("Synth Playback Switch", 0,
CS4231_LEFT_LINE_IN, CS4231_RIGHT_LINE_IN, 7, 7, 1, 1),
-WSS_DOUBLE("Synth Volume", 0,
- CS4231_LEFT_LINE_IN, CS4231_RIGHT_LINE_IN, 0, 0, 31, 1),
+WSS_DOUBLE_TLV("Synth Volume", 0,
+ CS4231_LEFT_LINE_IN, CS4231_RIGHT_LINE_IN, 0, 0, 31, 1,
+ db_scale_5bit_12db_max),
WSS_DOUBLE("Synth Capture Switch", 0,
CS4231_LEFT_LINE_IN, CS4231_RIGHT_LINE_IN, 6, 6, 1, 1),
WSS_DOUBLE("Synth Capture Bypass", 0,
@@ -764,14 +825,16 @@ CS4236_DOUBLE("Mic Playback Switch", 0,
CS4236_LEFT_MIC, CS4236_RIGHT_MIC, 6, 6, 1, 1),
CS4236_DOUBLE("Mic Capture Switch", 0,
CS4236_LEFT_MIC, CS4236_RIGHT_MIC, 7, 7, 1, 1),
-CS4236_DOUBLE("Mic Volume", 0, CS4236_LEFT_MIC, CS4236_RIGHT_MIC, 0, 0, 31, 1),
-CS4236_DOUBLE("Mic Playback Boost", 0,
+CS4236_DOUBLE_TLV("Mic Volume", 0, CS4236_LEFT_MIC, CS4236_RIGHT_MIC,
+ 0, 0, 31, 1, db_scale_5bit_22db_max),
+CS4236_DOUBLE("Mic Playback Boost (+20dB)", 0,
CS4236_LEFT_MIC, CS4236_RIGHT_MIC, 5, 5, 1, 0),
WSS_DOUBLE("Line Playback Switch", 0,
CS4231_AUX1_LEFT_INPUT, CS4231_AUX1_RIGHT_INPUT, 7, 7, 1, 1),
-WSS_DOUBLE("Line Volume", 0,
- CS4231_AUX1_LEFT_INPUT, CS4231_AUX1_RIGHT_INPUT, 0, 0, 31, 1),
+WSS_DOUBLE_TLV("Line Volume", 0,
+ CS4231_AUX1_LEFT_INPUT, CS4231_AUX1_RIGHT_INPUT, 0, 0, 31, 1,
+ db_scale_5bit_12db_max),
WSS_DOUBLE("Line Capture Switch", 0,
CS4231_AUX1_LEFT_INPUT, CS4231_AUX1_RIGHT_INPUT, 6, 6, 1, 1),
WSS_DOUBLE("Line Capture Bypass", 0,
@@ -779,57 +842,63 @@ WSS_DOUBLE("Line Capture Bypass", 0,
WSS_DOUBLE("CD Playback Switch", 0,
CS4231_AUX2_LEFT_INPUT, CS4231_AUX2_RIGHT_INPUT, 7, 7, 1, 1),
-WSS_DOUBLE("CD Volume", 0,
- CS4231_AUX2_LEFT_INPUT, CS4231_AUX2_RIGHT_INPUT, 0, 0, 31, 1),
+WSS_DOUBLE_TLV("CD Volume", 0,
+ CS4231_AUX2_LEFT_INPUT, CS4231_AUX2_RIGHT_INPUT, 0, 0, 31, 1,
+ db_scale_5bit_12db_max),
WSS_DOUBLE("CD Capture Switch", 0,
CS4231_AUX2_LEFT_INPUT, CS4231_AUX2_RIGHT_INPUT, 6, 6, 1, 1),
CS4236_DOUBLE1("Mono Output Playback Switch", 0,
CS4231_MONO_CTRL, CS4236_RIGHT_MIX_CTRL, 6, 7, 1, 1),
-CS4236_DOUBLE1("Mono Playback Switch", 0,
+CS4236_DOUBLE1("Beep Playback Switch", 0,
CS4231_MONO_CTRL, CS4236_LEFT_MIX_CTRL, 7, 7, 1, 1),
-WSS_SINGLE("Mono Playback Volume", 0, CS4231_MONO_CTRL, 0, 15, 1),
-WSS_SINGLE("Mono Playback Bypass", 0, CS4231_MONO_CTRL, 5, 1, 0),
+WSS_SINGLE_TLV("Beep Playback Volume", 0, CS4231_MONO_CTRL, 0, 15, 1,
+ db_scale_4bit),
+WSS_SINGLE("Beep Bypass Playback Switch", 0, CS4231_MONO_CTRL, 5, 1, 0),
-WSS_DOUBLE("Capture Volume", 0,
- CS4231_LEFT_INPUT, CS4231_RIGHT_INPUT, 0, 0, 15, 0),
+WSS_DOUBLE_TLV("Capture Volume", 0, CS4231_LEFT_INPUT, CS4231_RIGHT_INPUT,
+ 0, 0, 15, 0, db_scale_rec_gain),
WSS_DOUBLE("Analog Loopback Capture Switch", 0,
CS4231_LEFT_INPUT, CS4231_RIGHT_INPUT, 7, 7, 1, 0),
-WSS_SINGLE("Digital Loopback Playback Switch", 0, CS4231_LOOPBACK, 0, 1, 0),
-CS4236_DOUBLE1("Digital Loopback Playback Volume", 0,
- CS4231_LOOPBACK, CS4236_RIGHT_LOOPBACK, 2, 0, 63, 1)
+WSS_SINGLE("Loopback Digital Playback Switch", 0, CS4231_LOOPBACK, 0, 1, 0),
+CS4236_DOUBLE1_TLV("Loopback Digital Playback Volume", 0,
+ CS4231_LOOPBACK, CS4236_RIGHT_LOOPBACK, 2, 0, 63, 1,
+ db_scale_6bit),
};
+static const DECLARE_TLV_DB_SCALE(db_scale_5bit_6db_max, -5600, 200, 0);
+static const DECLARE_TLV_DB_SCALE(db_scale_2bit_16db_max, -2400, 800, 0);
+
static struct snd_kcontrol_new snd_cs4235_controls[] = {
-WSS_DOUBLE("Master Switch", 0,
+WSS_DOUBLE("Master Playback Switch", 0,
CS4235_LEFT_MASTER, CS4235_RIGHT_MASTER, 7, 7, 1, 1),
-WSS_DOUBLE("Master Volume", 0,
- CS4235_LEFT_MASTER, CS4235_RIGHT_MASTER, 0, 0, 31, 1),
-
-CS4235_OUTPUT_ACCU("Playback Volume", 0),
+WSS_DOUBLE_TLV("Master Playback Volume", 0,
+ CS4235_LEFT_MASTER, CS4235_RIGHT_MASTER, 0, 0, 31, 1,
+ db_scale_5bit_6db_max),
-CS4236_DOUBLE("Master Digital Playback Switch", 0,
- CS4236_LEFT_MASTER, CS4236_RIGHT_MASTER, 7, 7, 1, 1),
-CS4236_DOUBLE("Master Digital Capture Switch", 0,
- CS4236_DAC_MUTE, CS4236_DAC_MUTE, 7, 6, 1, 1),
-CS4236_MASTER_DIGITAL("Master Digital Volume", 0),
+CS4235_OUTPUT_ACCU("Playback Volume", 0, db_scale_2bit_16db_max),
-WSS_DOUBLE("Master Digital Playback Switch", 1,
+WSS_DOUBLE("Synth Playback Switch", 1,
CS4231_LEFT_LINE_IN, CS4231_RIGHT_LINE_IN, 7, 7, 1, 1),
-WSS_DOUBLE("Master Digital Capture Switch", 1,
+WSS_DOUBLE("Synth Capture Switch", 1,
CS4231_LEFT_LINE_IN, CS4231_RIGHT_LINE_IN, 6, 6, 1, 1),
-WSS_DOUBLE("Master Digital Volume", 1,
- CS4231_LEFT_LINE_IN, CS4231_RIGHT_LINE_IN, 0, 0, 31, 1),
+WSS_DOUBLE_TLV("Synth Volume", 1,
+ CS4231_LEFT_LINE_IN, CS4231_RIGHT_LINE_IN, 0, 0, 31, 1,
+ db_scale_5bit_12db_max),
-CS4236_DOUBLE("Capture Volume", 0,
- CS4236_LEFT_MIX_CTRL, CS4236_RIGHT_MIX_CTRL, 5, 5, 3, 1),
+CS4236_DOUBLE_TLV("Capture Volume", 0,
+ CS4236_LEFT_MIX_CTRL, CS4236_RIGHT_MIX_CTRL, 5, 5, 3, 1,
+ db_scale_2bit),
-WSS_DOUBLE("PCM Switch", 0,
+WSS_DOUBLE("PCM Playback Switch", 0,
CS4231_LEFT_OUTPUT, CS4231_RIGHT_OUTPUT, 7, 7, 1, 1),
-WSS_DOUBLE("PCM Volume", 0,
- CS4231_LEFT_OUTPUT, CS4231_RIGHT_OUTPUT, 0, 0, 63, 1),
+WSS_DOUBLE("PCM Capture Switch", 0,
+ CS4236_DAC_MUTE, CS4236_DAC_MUTE, 7, 6, 1, 1),
+WSS_DOUBLE_TLV("PCM Volume", 0,
+ CS4231_LEFT_OUTPUT, CS4231_RIGHT_OUTPUT, 0, 0, 63, 1,
+ db_scale_6bit),
CS4236_DOUBLE("DSP Switch", 0, CS4236_LEFT_DSP, CS4236_RIGHT_DSP, 7, 7, 1, 1),
@@ -842,29 +911,29 @@ CS4236_DOUBLE("Mic Capture Switch", 0,
CS4236_LEFT_MIC, CS4236_RIGHT_MIC, 7, 7, 1, 1),
CS4236_DOUBLE("Mic Playback Switch", 0,
CS4236_LEFT_MIC, CS4236_RIGHT_MIC, 6, 6, 1, 1),
-CS4236_SINGLE("Mic Volume", 0, CS4236_LEFT_MIC, 0, 31, 1),
-CS4236_SINGLE("Mic Playback Boost", 0, CS4236_LEFT_MIC, 5, 1, 0),
+CS4236_SINGLE_TLV("Mic Volume", 0, CS4236_LEFT_MIC, 0, 31, 1,
+ db_scale_5bit_22db_max),
+CS4236_SINGLE("Mic Boost (+20dB)", 0, CS4236_LEFT_MIC, 5, 1, 0),
-WSS_DOUBLE("Aux Playback Switch", 0,
+WSS_DOUBLE("Line Playback Switch", 0,
CS4231_AUX1_LEFT_INPUT, CS4231_AUX1_RIGHT_INPUT, 7, 7, 1, 1),
-WSS_DOUBLE("Aux Capture Switch", 0,
+WSS_DOUBLE("Line Capture Switch", 0,
CS4231_AUX1_LEFT_INPUT, CS4231_AUX1_RIGHT_INPUT, 6, 6, 1, 1),
-WSS_DOUBLE("Aux Volume", 0,
- CS4231_AUX1_LEFT_INPUT, CS4231_AUX1_RIGHT_INPUT, 0, 0, 31, 1),
+WSS_DOUBLE_TLV("Line Volume", 0,
+ CS4231_AUX1_LEFT_INPUT, CS4231_AUX1_RIGHT_INPUT, 0, 0, 31, 1,
+ db_scale_5bit_12db_max),
-WSS_DOUBLE("Aux Playback Switch", 1,
+WSS_DOUBLE("CD Playback Switch", 1,
CS4231_AUX2_LEFT_INPUT, CS4231_AUX2_RIGHT_INPUT, 7, 7, 1, 1),
-WSS_DOUBLE("Aux Capture Switch", 1,
+WSS_DOUBLE("CD Capture Switch", 1,
CS4231_AUX2_LEFT_INPUT, CS4231_AUX2_RIGHT_INPUT, 6, 6, 1, 1),
-WSS_DOUBLE("Aux Volume", 1,
- CS4231_AUX2_LEFT_INPUT, CS4231_AUX2_RIGHT_INPUT, 0, 0, 31, 1),
-
-CS4236_DOUBLE1("Master Mono Switch", 0,
- CS4231_MONO_CTRL, CS4236_RIGHT_MIX_CTRL, 6, 7, 1, 1),
+WSS_DOUBLE_TLV("CD Volume", 1,
+ CS4231_AUX2_LEFT_INPUT, CS4231_AUX2_RIGHT_INPUT, 0, 0, 31, 1,
+ db_scale_5bit_12db_max),
-CS4236_DOUBLE1("Mono Switch", 0,
+CS4236_DOUBLE1("Beep Playback Switch", 0,
CS4231_MONO_CTRL, CS4236_LEFT_MIX_CTRL, 7, 7, 1, 1),
-WSS_SINGLE("Mono Volume", 0, CS4231_MONO_CTRL, 0, 15, 1),
+WSS_SINGLE("Beep Playback Volume", 0, CS4231_MONO_CTRL, 0, 15, 1),
WSS_DOUBLE("Analog Loopback Switch", 0,
CS4231_LEFT_INPUT, CS4231_RIGHT_INPUT, 7, 7, 1, 0),
diff --git a/sound/isa/es1688/es1688_lib.c b/sound/isa/es1688/es1688_lib.c
index 4c6e14f87f2d..c76bb00c9d15 100644
--- a/sound/isa/es1688/es1688_lib.c
+++ b/sound/isa/es1688/es1688_lib.c
@@ -982,7 +982,7 @@ ES1688_DOUBLE("CD Playback Volume", 0, ES1688_CD_DEV, ES1688_CD_DEV, 4, 0, 15, 0
ES1688_DOUBLE("FM Playback Volume", 0, ES1688_FM_DEV, ES1688_FM_DEV, 4, 0, 15, 0),
ES1688_DOUBLE("Mic Playback Volume", 0, ES1688_MIC_DEV, ES1688_MIC_DEV, 4, 0, 15, 0),
ES1688_DOUBLE("Aux Playback Volume", 0, ES1688_AUX_DEV, ES1688_AUX_DEV, 4, 0, 15, 0),
-ES1688_SINGLE("PC Speaker Playback Volume", 0, ES1688_SPEAKER_DEV, 0, 7, 0),
+ES1688_SINGLE("Beep Playback Volume", 0, ES1688_SPEAKER_DEV, 0, 7, 0),
ES1688_DOUBLE("Capture Volume", 0, ES1688_RECLEV_DEV, ES1688_RECLEV_DEV, 4, 0, 15, 0),
ES1688_SINGLE("Capture Switch", 0, ES1688_REC_DEV, 4, 1, 1),
{
diff --git a/sound/isa/es18xx.c b/sound/isa/es18xx.c
index 8cfbff73a835..9a43baae7250 100644
--- a/sound/isa/es18xx.c
+++ b/sound/isa/es18xx.c
@@ -102,8 +102,6 @@
struct snd_es18xx {
unsigned long port; /* port of ESS chip */
- unsigned long mpu_port; /* MPU-401 port of ESS chip */
- unsigned long fm_port; /* FM port */
unsigned long ctrl_port; /* Control port of ESS chip */
struct resource *res_port;
struct resource *res_mpu_port;
@@ -116,12 +114,9 @@ struct snd_es18xx {
unsigned short audio2_vol; /* volume level of audio2 */
unsigned short active; /* active channel mask */
- unsigned int dma1_size;
- unsigned int dma2_size;
unsigned int dma1_shift;
unsigned int dma2_shift;
- struct snd_card *card;
struct snd_pcm *pcm;
struct snd_pcm_substream *playback_a_substream;
struct snd_pcm_substream *capture_a_substream;
@@ -136,14 +131,9 @@ struct snd_es18xx {
spinlock_t reg_lock;
spinlock_t mixer_lock;
- spinlock_t ctrl_lock;
#ifdef CONFIG_PM
unsigned char pm_reg;
#endif
-};
-
-struct snd_audiodrive {
- struct snd_es18xx *chip;
#ifdef CONFIG_PNP
struct pnp_dev *dev;
struct pnp_dev *devc;
@@ -359,7 +349,7 @@ static inline int snd_es18xx_mixer_writable(struct snd_es18xx *chip, unsigned ch
}
-static int snd_es18xx_reset(struct snd_es18xx *chip)
+static int __devinit snd_es18xx_reset(struct snd_es18xx *chip)
{
int i;
outb(0x03, chip->port + 0x06);
@@ -495,8 +485,6 @@ static int snd_es18xx_playback1_prepare(struct snd_es18xx *chip,
unsigned int size = snd_pcm_lib_buffer_bytes(substream);
unsigned int count = snd_pcm_lib_period_bytes(substream);
- chip->dma2_size = size;
-
snd_es18xx_rate_set(chip, substream, DAC2);
/* Transfer Count Reload */
@@ -596,8 +584,6 @@ static int snd_es18xx_capture_prepare(struct snd_pcm_substream *substream)
unsigned int size = snd_pcm_lib_buffer_bytes(substream);
unsigned int count = snd_pcm_lib_period_bytes(substream);
- chip->dma1_size = size;
-
snd_es18xx_reset_fifo(chip);
/* Set stereo/mono */
@@ -664,8 +650,6 @@ static int snd_es18xx_playback2_prepare(struct snd_es18xx *chip,
unsigned int size = snd_pcm_lib_buffer_bytes(substream);
unsigned int count = snd_pcm_lib_period_bytes(substream);
- chip->dma1_size = size;
-
snd_es18xx_reset_fifo(chip);
/* Set stereo/mono */
@@ -755,7 +739,8 @@ static int snd_es18xx_playback_trigger(struct snd_pcm_substream *substream,
static irqreturn_t snd_es18xx_interrupt(int irq, void *dev_id)
{
- struct snd_es18xx *chip = dev_id;
+ struct snd_card *card = dev_id;
+ struct snd_es18xx *chip = card->private_data;
unsigned char status;
if (chip->caps & ES18XX_CONTROL) {
@@ -805,12 +790,16 @@ static irqreturn_t snd_es18xx_interrupt(int irq, void *dev_id)
int split = 0;
if (chip->caps & ES18XX_HWV) {
split = snd_es18xx_mixer_read(chip, 0x64) & 0x80;
- snd_ctl_notify(chip->card, SNDRV_CTL_EVENT_MASK_VALUE, &chip->hw_switch->id);
- snd_ctl_notify(chip->card, SNDRV_CTL_EVENT_MASK_VALUE, &chip->hw_volume->id);
+ snd_ctl_notify(card, SNDRV_CTL_EVENT_MASK_VALUE,
+ &chip->hw_switch->id);
+ snd_ctl_notify(card, SNDRV_CTL_EVENT_MASK_VALUE,
+ &chip->hw_volume->id);
}
if (!split) {
- snd_ctl_notify(chip->card, SNDRV_CTL_EVENT_MASK_VALUE, &chip->master_switch->id);
- snd_ctl_notify(chip->card, SNDRV_CTL_EVENT_MASK_VALUE, &chip->master_volume->id);
+ snd_ctl_notify(card, SNDRV_CTL_EVENT_MASK_VALUE,
+ &chip->master_switch->id);
+ snd_ctl_notify(card, SNDRV_CTL_EVENT_MASK_VALUE,
+ &chip->master_volume->id);
}
/* ack interrupt */
snd_es18xx_mixer_write(chip, 0x66, 0x00);
@@ -821,17 +810,18 @@ static irqreturn_t snd_es18xx_interrupt(int irq, void *dev_id)
static snd_pcm_uframes_t snd_es18xx_playback_pointer(struct snd_pcm_substream *substream)
{
struct snd_es18xx *chip = snd_pcm_substream_chip(substream);
+ unsigned int size = snd_pcm_lib_buffer_bytes(substream);
int pos;
if (substream->number == 0 && (chip->caps & ES18XX_PCM2)) {
if (!(chip->active & DAC2))
return 0;
- pos = snd_dma_pointer(chip->dma2, chip->dma2_size);
+ pos = snd_dma_pointer(chip->dma2, size);
return pos >> chip->dma2_shift;
} else {
if (!(chip->active & DAC1))
return 0;
- pos = snd_dma_pointer(chip->dma1, chip->dma1_size);
+ pos = snd_dma_pointer(chip->dma1, size);
return pos >> chip->dma1_shift;
}
}
@@ -839,11 +829,12 @@ static snd_pcm_uframes_t snd_es18xx_playback_pointer(struct snd_pcm_substream *s
static snd_pcm_uframes_t snd_es18xx_capture_pointer(struct snd_pcm_substream *substream)
{
struct snd_es18xx *chip = snd_pcm_substream_chip(substream);
+ unsigned int size = snd_pcm_lib_buffer_bytes(substream);
int pos;
if (!(chip->active & ADC1))
return 0;
- pos = snd_dma_pointer(chip->dma1, chip->dma1_size);
+ pos = snd_dma_pointer(chip->dma1, size);
return pos >> chip->dma1_shift;
}
@@ -974,9 +965,6 @@ static int snd_es18xx_capture_close(struct snd_pcm_substream *substream)
static int snd_es18xx_info_mux(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *uinfo)
{
- static char *texts4Source[4] = {
- "Mic", "CD", "Line", "Master"
- };
static char *texts5Source[5] = {
"Mic", "CD", "Line", "Master", "Mix"
};
@@ -994,7 +982,8 @@ static int snd_es18xx_info_mux(struct snd_kcontrol *kcontrol, struct snd_ctl_ele
uinfo->value.enumerated.items = 4;
if (uinfo->value.enumerated.item > 3)
uinfo->value.enumerated.item = 3;
- strcpy(uinfo->value.enumerated.name, texts4Source[uinfo->value.enumerated.item]);
+ strcpy(uinfo->value.enumerated.name,
+ texts5Source[uinfo->value.enumerated.item]);
break;
case 0x1887:
case 0x1888:
@@ -1313,7 +1302,7 @@ ES18XX_DOUBLE("Aux Capture Volume", 0, 0x6c, 0x6c, 4, 0, 15, 0)
* The chipset specific mixer controls
*/
static struct snd_kcontrol_new snd_es18xx_opt_speaker =
- ES18XX_SINGLE("PC Speaker Playback Volume", 0, 0x3c, 0, 7, 0);
+ ES18XX_SINGLE("Beep Playback Volume", 0, 0x3c, 0, 7, 0);
static struct snd_kcontrol_new snd_es18xx_opt_1869[] = {
ES18XX_SINGLE("Capture Switch", 0, 0x1c, 4, 1, 1),
@@ -1378,11 +1367,9 @@ ES18XX_SINGLE("Hardware Master Volume Split", 0, 0x64, 7, 1, 0),
static int __devinit snd_es18xx_config_read(struct snd_es18xx *chip, unsigned char reg)
{
int data;
- unsigned long flags;
- spin_lock_irqsave(&chip->ctrl_lock, flags);
+
outb(reg, chip->ctrl_port);
data = inb(chip->ctrl_port + 1);
- spin_unlock_irqrestore(&chip->ctrl_lock, flags);
return data;
}
@@ -1398,7 +1385,9 @@ static void __devinit snd_es18xx_config_write(struct snd_es18xx *chip,
#endif
}
-static int __devinit snd_es18xx_initialize(struct snd_es18xx *chip)
+static int __devinit snd_es18xx_initialize(struct snd_es18xx *chip,
+ unsigned long mpu_port,
+ unsigned long fm_port)
{
int mask = 0;
@@ -1412,15 +1401,15 @@ static int __devinit snd_es18xx_initialize(struct snd_es18xx *chip)
if (chip->caps & ES18XX_CONTROL) {
/* Hardware volume IRQ */
snd_es18xx_config_write(chip, 0x27, chip->irq);
- if (chip->fm_port > 0 && chip->fm_port != SNDRV_AUTO_PORT) {
+ if (fm_port > 0 && fm_port != SNDRV_AUTO_PORT) {
/* FM I/O */
- snd_es18xx_config_write(chip, 0x62, chip->fm_port >> 8);
- snd_es18xx_config_write(chip, 0x63, chip->fm_port & 0xff);
+ snd_es18xx_config_write(chip, 0x62, fm_port >> 8);
+ snd_es18xx_config_write(chip, 0x63, fm_port & 0xff);
}
- if (chip->mpu_port > 0 && chip->mpu_port != SNDRV_AUTO_PORT) {
+ if (mpu_port > 0 && mpu_port != SNDRV_AUTO_PORT) {
/* MPU-401 I/O */
- snd_es18xx_config_write(chip, 0x64, chip->mpu_port >> 8);
- snd_es18xx_config_write(chip, 0x65, chip->mpu_port & 0xff);
+ snd_es18xx_config_write(chip, 0x64, mpu_port >> 8);
+ snd_es18xx_config_write(chip, 0x65, mpu_port & 0xff);
/* MPU-401 IRQ */
snd_es18xx_config_write(chip, 0x28, chip->irq);
}
@@ -1507,11 +1496,12 @@ static int __devinit snd_es18xx_initialize(struct snd_es18xx *chip)
snd_es18xx_mixer_write(chip, 0x7A, 0x68);
/* Enable and set hardware volume interrupt */
snd_es18xx_mixer_write(chip, 0x64, 0x06);
- if (chip->mpu_port > 0 && chip->mpu_port != SNDRV_AUTO_PORT) {
+ if (mpu_port > 0 && mpu_port != SNDRV_AUTO_PORT) {
/* MPU401 share irq with audio
Joystick enabled
FM enabled */
- snd_es18xx_mixer_write(chip, 0x40, 0x43 | (chip->mpu_port & 0xf0) >> 1);
+ snd_es18xx_mixer_write(chip, 0x40,
+ 0x43 | (mpu_port & 0xf0) >> 1);
}
snd_es18xx_mixer_write(chip, 0x7f, ((irqmask + 1) << 1) | 0x01);
}
@@ -1629,7 +1619,9 @@ static int __devinit snd_es18xx_identify(struct snd_es18xx *chip)
return 0;
}
-static int __devinit snd_es18xx_probe(struct snd_es18xx *chip)
+static int __devinit snd_es18xx_probe(struct snd_es18xx *chip,
+ unsigned long mpu_port,
+ unsigned long fm_port)
{
if (snd_es18xx_identify(chip) < 0) {
snd_printk(KERN_ERR PFX "[0x%lx] ESS chip not found\n", chip->port);
@@ -1650,8 +1642,6 @@ static int __devinit snd_es18xx_probe(struct snd_es18xx *chip)
chip->caps = ES18XX_PCM2 | ES18XX_SPATIALIZER | ES18XX_RECMIX | ES18XX_NEW_RATE | ES18XX_AUXB | ES18XX_I2S | ES18XX_CONTROL | ES18XX_HWV;
break;
case 0x1887:
- chip->caps = ES18XX_PCM2 | ES18XX_RECMIX | ES18XX_AUXB | ES18XX_DUPLEX_SAME;
- break;
case 0x1888:
chip->caps = ES18XX_PCM2 | ES18XX_RECMIX | ES18XX_AUXB | ES18XX_DUPLEX_SAME;
break;
@@ -1666,7 +1656,7 @@ static int __devinit snd_es18xx_probe(struct snd_es18xx *chip)
if (chip->dma1 == chip->dma2)
chip->caps &= ~(ES18XX_PCM2 | ES18XX_DUPLEX_SAME);
- return snd_es18xx_initialize(chip);
+ return snd_es18xx_initialize(chip, mpu_port, fm_port);
}
static struct snd_pcm_ops snd_es18xx_playback_ops = {
@@ -1691,8 +1681,10 @@ static struct snd_pcm_ops snd_es18xx_capture_ops = {
.pointer = snd_es18xx_capture_pointer,
};
-static int __devinit snd_es18xx_pcm(struct snd_es18xx *chip, int device, struct snd_pcm ** rpcm)
+static int __devinit snd_es18xx_pcm(struct snd_card *card, int device,
+ struct snd_pcm **rpcm)
{
+ struct snd_es18xx *chip = card->private_data;
struct snd_pcm *pcm;
char str[16];
int err;
@@ -1701,9 +1693,9 @@ static int __devinit snd_es18xx_pcm(struct snd_es18xx *chip, int device, struct
*rpcm = NULL;
sprintf(str, "ES%x", chip->version);
if (chip->caps & ES18XX_PCM2)
- err = snd_pcm_new(chip->card, str, device, 2, 1, &pcm);
+ err = snd_pcm_new(card, str, device, 2, 1, &pcm);
else
- err = snd_pcm_new(chip->card, str, device, 1, 1, &pcm);
+ err = snd_pcm_new(card, str, device, 1, 1, &pcm);
if (err < 0)
return err;
@@ -1734,10 +1726,9 @@ static int __devinit snd_es18xx_pcm(struct snd_es18xx *chip, int device, struct
#ifdef CONFIG_PM
static int snd_es18xx_suspend(struct snd_card *card, pm_message_t state)
{
- struct snd_audiodrive *acard = card->private_data;
- struct snd_es18xx *chip = acard->chip;
+ struct snd_es18xx *chip = card->private_data;
- snd_power_change_state(chip->card, SNDRV_CTL_POWER_D3hot);
+ snd_power_change_state(card, SNDRV_CTL_POWER_D3hot);
snd_pcm_suspend_all(chip->pcm);
@@ -1752,24 +1743,25 @@ static int snd_es18xx_suspend(struct snd_card *card, pm_message_t state)
static int snd_es18xx_resume(struct snd_card *card)
{
- struct snd_audiodrive *acard = card->private_data;
- struct snd_es18xx *chip = acard->chip;
+ struct snd_es18xx *chip = card->private_data;
/* restore PM register, we won't wake till (not 0x07) i/o activity though */
snd_es18xx_write(chip, ES18XX_PM, chip->pm_reg ^= ES18XX_PM_FM);
- snd_power_change_state(chip->card, SNDRV_CTL_POWER_D0);
+ snd_power_change_state(card, SNDRV_CTL_POWER_D0);
return 0;
}
#endif /* CONFIG_PM */
-static int snd_es18xx_free(struct snd_es18xx *chip)
+static int snd_es18xx_free(struct snd_card *card)
{
+ struct snd_es18xx *chip = card->private_data;
+
release_and_free_resource(chip->res_port);
release_and_free_resource(chip->res_ctrl_port);
release_and_free_resource(chip->res_mpu_port);
if (chip->irq >= 0)
- free_irq(chip->irq, (void *) chip);
+ free_irq(chip->irq, (void *) card);
if (chip->dma1 >= 0) {
disable_dma(chip->dma1);
free_dma(chip->dma1);
@@ -1778,93 +1770,82 @@ static int snd_es18xx_free(struct snd_es18xx *chip)
disable_dma(chip->dma2);
free_dma(chip->dma2);
}
- kfree(chip);
return 0;
}
static int snd_es18xx_dev_free(struct snd_device *device)
{
- struct snd_es18xx *chip = device->device_data;
- return snd_es18xx_free(chip);
+ return snd_es18xx_free(device->card);
}
static int __devinit snd_es18xx_new_device(struct snd_card *card,
unsigned long port,
unsigned long mpu_port,
unsigned long fm_port,
- int irq, int dma1, int dma2,
- struct snd_es18xx ** rchip)
+ int irq, int dma1, int dma2)
{
- struct snd_es18xx *chip;
+ struct snd_es18xx *chip = card->private_data;
static struct snd_device_ops ops = {
.dev_free = snd_es18xx_dev_free,
};
int err;
- *rchip = NULL;
- chip = kzalloc(sizeof(*chip), GFP_KERNEL);
- if (chip == NULL)
- return -ENOMEM;
spin_lock_init(&chip->reg_lock);
spin_lock_init(&chip->mixer_lock);
- spin_lock_init(&chip->ctrl_lock);
- chip->card = card;
chip->port = port;
- chip->mpu_port = mpu_port;
- chip->fm_port = fm_port;
chip->irq = -1;
chip->dma1 = -1;
chip->dma2 = -1;
chip->audio2_vol = 0x00;
chip->active = 0;
- if ((chip->res_port = request_region(port, 16, "ES18xx")) == NULL) {
- snd_es18xx_free(chip);
+ chip->res_port = request_region(port, 16, "ES18xx");
+ if (chip->res_port == NULL) {
+ snd_es18xx_free(card);
snd_printk(KERN_ERR PFX "unable to grap ports 0x%lx-0x%lx\n", port, port + 16 - 1);
return -EBUSY;
}
- if (request_irq(irq, snd_es18xx_interrupt, IRQF_DISABLED, "ES18xx", (void *) chip)) {
- snd_es18xx_free(chip);
+ if (request_irq(irq, snd_es18xx_interrupt, IRQF_DISABLED, "ES18xx",
+ (void *) card)) {
+ snd_es18xx_free(card);
snd_printk(KERN_ERR PFX "unable to grap IRQ %d\n", irq);
return -EBUSY;
}
chip->irq = irq;
if (request_dma(dma1, "ES18xx DMA 1")) {
- snd_es18xx_free(chip);
+ snd_es18xx_free(card);
snd_printk(KERN_ERR PFX "unable to grap DMA1 %d\n", dma1);
return -EBUSY;
}
chip->dma1 = dma1;
if (dma2 != dma1 && request_dma(dma2, "ES18xx DMA 2")) {
- snd_es18xx_free(chip);
+ snd_es18xx_free(card);
snd_printk(KERN_ERR PFX "unable to grap DMA2 %d\n", dma2);
return -EBUSY;
}
chip->dma2 = dma2;
- if (snd_es18xx_probe(chip) < 0) {
- snd_es18xx_free(chip);
- return -ENODEV;
- }
- if ((err = snd_device_new(card, SNDRV_DEV_LOWLEVEL, chip, &ops)) < 0) {
- snd_es18xx_free(chip);
+ if (snd_es18xx_probe(chip, mpu_port, fm_port) < 0) {
+ snd_es18xx_free(card);
+ return -ENODEV;
+ }
+ err = snd_device_new(card, SNDRV_DEV_LOWLEVEL, chip, &ops);
+ if (err < 0) {
+ snd_es18xx_free(card);
return err;
}
- *rchip = chip;
return 0;
}
-static int __devinit snd_es18xx_mixer(struct snd_es18xx *chip)
+static int __devinit snd_es18xx_mixer(struct snd_card *card)
{
- struct snd_card *card;
+ struct snd_es18xx *chip = card->private_data;
int err;
unsigned int idx;
- card = chip->card;
-
strcpy(card->mixername, chip->pcm->name);
for (idx = 0; idx < ARRAY_SIZE(snd_es18xx_base_controls); idx++) {
@@ -1986,7 +1967,7 @@ static int index[SNDRV_CARDS] = SNDRV_DEFAULT_IDX; /* Index 0-MAX */
static char *id[SNDRV_CARDS] = SNDRV_DEFAULT_STR; /* ID for this card */
static int enable[SNDRV_CARDS] = SNDRV_DEFAULT_ENABLE_ISAPNP; /* Enable this card */
#ifdef CONFIG_PNP
-static int isapnp[SNDRV_CARDS] = {[0 ... (SNDRV_CARDS - 1)] = 1};
+static int isapnp[SNDRV_CARDS] = SNDRV_DEFAULT_ENABLE_ISAPNP;
#endif
static long port[SNDRV_CARDS] = SNDRV_DEFAULT_PORT; /* 0x220,0x240,0x260,0x280 */
#ifndef CONFIG_PNP
@@ -2063,11 +2044,11 @@ static int __devinit snd_audiodrive_pnp_init_main(int dev, struct pnp_dev *pdev)
return 0;
}
-static int __devinit snd_audiodrive_pnp(int dev, struct snd_audiodrive *acard,
+static int __devinit snd_audiodrive_pnp(int dev, struct snd_es18xx *chip,
struct pnp_dev *pdev)
{
- acard->dev = pdev;
- if (snd_audiodrive_pnp_init_main(dev, acard->dev) < 0)
+ chip->dev = pdev;
+ if (snd_audiodrive_pnp_init_main(dev, chip->dev) < 0)
return -EBUSY;
return 0;
}
@@ -2093,26 +2074,26 @@ static struct pnp_card_device_id snd_audiodrive_pnpids[] = {
MODULE_DEVICE_TABLE(pnp_card, snd_audiodrive_pnpids);
-static int __devinit snd_audiodrive_pnpc(int dev, struct snd_audiodrive *acard,
+static int __devinit snd_audiodrive_pnpc(int dev, struct snd_es18xx *chip,
struct pnp_card_link *card,
const struct pnp_card_device_id *id)
{
- acard->dev = pnp_request_card_device(card, id->devs[0].id, NULL);
- if (acard->dev == NULL)
+ chip->dev = pnp_request_card_device(card, id->devs[0].id, NULL);
+ if (chip->dev == NULL)
return -EBUSY;
- acard->devc = pnp_request_card_device(card, id->devs[1].id, NULL);
- if (acard->devc == NULL)
+ chip->devc = pnp_request_card_device(card, id->devs[1].id, NULL);
+ if (chip->devc == NULL)
return -EBUSY;
/* Control port initialization */
- if (pnp_activate_dev(acard->devc) < 0) {
+ if (pnp_activate_dev(chip->devc) < 0) {
snd_printk(KERN_ERR PFX "PnP control configure failure (out of resources?)\n");
return -EAGAIN;
}
snd_printdd("pnp: port=0x%llx\n",
- (unsigned long long)pnp_port_start(acard->devc, 0));
- if (snd_audiodrive_pnp_init_main(dev, acard->dev) < 0)
+ (unsigned long long)pnp_port_start(chip->devc, 0));
+ if (snd_audiodrive_pnp_init_main(dev, chip->dev) < 0)
return -EBUSY;
return 0;
@@ -2128,24 +2109,20 @@ static int __devinit snd_audiodrive_pnpc(int dev, struct snd_audiodrive *acard,
static int snd_es18xx_card_new(int dev, struct snd_card **cardp)
{
return snd_card_create(index[dev], id[dev], THIS_MODULE,
- sizeof(struct snd_audiodrive), cardp);
+ sizeof(struct snd_es18xx), cardp);
}
static int __devinit snd_audiodrive_probe(struct snd_card *card, int dev)
{
- struct snd_audiodrive *acard = card->private_data;
- struct snd_es18xx *chip;
+ struct snd_es18xx *chip = card->private_data;
struct snd_opl3 *opl3;
int err;
- if ((err = snd_es18xx_new_device(card,
- port[dev],
- mpu_port[dev],
- fm_port[dev],
- irq[dev], dma1[dev], dma2[dev],
- &chip)) < 0)
+ err = snd_es18xx_new_device(card,
+ port[dev], mpu_port[dev], fm_port[dev],
+ irq[dev], dma1[dev], dma2[dev]);
+ if (err < 0)
return err;
- acard->chip = chip;
sprintf(card->driver, "ES%x", chip->version);
@@ -2161,26 +2138,32 @@ static int __devinit snd_audiodrive_probe(struct snd_card *card, int dev)
chip->port,
irq[dev], dma1[dev]);
- if ((err = snd_es18xx_pcm(chip, 0, NULL)) < 0)
+ err = snd_es18xx_pcm(card, 0, NULL);
+ if (err < 0)
return err;
- if ((err = snd_es18xx_mixer(chip)) < 0)
+ err = snd_es18xx_mixer(card);
+ if (err < 0)
return err;
if (fm_port[dev] > 0 && fm_port[dev] != SNDRV_AUTO_PORT) {
- if (snd_opl3_create(card, chip->fm_port, chip->fm_port + 2, OPL3_HW_OPL3, 0, &opl3) < 0) {
- snd_printk(KERN_WARNING PFX "opl3 not detected at 0x%lx\n", chip->fm_port);
+ if (snd_opl3_create(card, fm_port[dev], fm_port[dev] + 2,
+ OPL3_HW_OPL3, 0, &opl3) < 0) {
+ snd_printk(KERN_WARNING PFX
+ "opl3 not detected at 0x%lx\n",
+ fm_port[dev]);
} else {
- if ((err = snd_opl3_hwdep_new(opl3, 0, 1, NULL)) < 0)
+ err = snd_opl3_hwdep_new(opl3, 0, 1, NULL);
+ if (err < 0)
return err;
}
}
if (mpu_port[dev] > 0 && mpu_port[dev] != SNDRV_AUTO_PORT) {
- if ((err = snd_mpu401_uart_new(card, 0, MPU401_HW_ES18XX,
- chip->mpu_port, 0,
- irq[dev], 0,
- &chip->rmidi)) < 0)
+ err = snd_mpu401_uart_new(card, 0, MPU401_HW_ES18XX,
+ mpu_port[dev], 0,
+ irq[dev], 0, &chip->rmidi);
+ if (err < 0)
return err;
}
diff --git a/sound/isa/opti9xx/miro.c b/sound/isa/opti9xx/miro.c
index 02e30d7c6a93..6123c7531110 100644
--- a/sound/isa/opti9xx/miro.c
+++ b/sound/isa/opti9xx/miro.c
@@ -25,6 +25,7 @@
#include <linux/init.h>
#include <linux/err.h>
#include <linux/isa.h>
+#include <linux/pnp.h>
#include <linux/delay.h>
#include <linux/slab.h>
#include <linux/ioport.h>
@@ -40,7 +41,7 @@
#define SNDRV_LEGACY_FIND_FREE_IRQ
#define SNDRV_LEGACY_FIND_FREE_DMA
#include <sound/initval.h>
-#include "miro.h"
+#include <sound/aci.h>
MODULE_AUTHOR("Martin Langer <martin-langer@gmx.de>");
MODULE_LICENSE("GPL");
@@ -60,6 +61,9 @@ static int dma1 = SNDRV_DEFAULT_DMA1; /* 0,1,3 */
static int dma2 = SNDRV_DEFAULT_DMA1; /* 0,1,3 */
static int wss;
static int ide;
+#ifdef CONFIG_PNP
+static int isapnp = 1; /* Enable ISA PnP detection */
+#endif
module_param(index, int, 0444);
MODULE_PARM_DESC(index, "Index value for miro soundcard.");
@@ -83,6 +87,10 @@ module_param(wss, int, 0444);
MODULE_PARM_DESC(wss, "wss mode");
module_param(ide, int, 0444);
MODULE_PARM_DESC(ide, "enable ide port");
+#ifdef CONFIG_PNP
+module_param(isapnp, bool, 0444);
+MODULE_PARM_DESC(isapnp, "Enable ISA PnP detection for specified soundcard.");
+#endif
#define OPTi9XX_HW_DETECT 0
#define OPTi9XX_HW_82C928 1
@@ -96,7 +104,6 @@ MODULE_PARM_DESC(ide, "enable ide port");
#define OPTi9XX_MC_REG(n) n
-
struct snd_miro {
unsigned short hardware;
unsigned char password;
@@ -110,7 +117,6 @@ struct snd_miro {
unsigned long pwd_reg;
spinlock_t lock;
- struct snd_card *card;
struct snd_pcm *pcm;
long wss_base;
@@ -118,23 +124,13 @@ struct snd_miro {
int dma1;
int dma2;
- long fm_port;
-
long mpu_port;
int mpu_irq;
- unsigned long aci_port;
- int aci_vendor;
- int aci_product;
- int aci_version;
- int aci_amp;
- int aci_preamp;
- int aci_solomode;
-
- struct mutex aci_mutex;
+ struct snd_miro_aci *aci;
};
-static void snd_miro_proc_init(struct snd_miro * miro);
+static struct snd_miro_aci aci_device;
static char * snd_opti9xx_names[] = {
"unkown",
@@ -143,17 +139,33 @@ static char * snd_opti9xx_names[] = {
"82C930", "82C931", "82C933"
};
+static int snd_miro_pnp_is_probed;
+
+#ifdef CONFIG_PNP
+
+static struct pnp_card_device_id snd_miro_pnpids[] = {
+ /* PCM20 and PCM12 in PnP mode */
+ { .id = "MIR0924",
+ .devs = { { "MIR0000" }, { "MIR0002" }, { "MIR0005" } }, },
+ { .id = "" }
+};
+
+MODULE_DEVICE_TABLE(pnp_card, snd_miro_pnpids);
+
+#endif /* CONFIG_PNP */
+
/*
* ACI control
*/
-static int aci_busy_wait(struct snd_miro * miro)
+static int aci_busy_wait(struct snd_miro_aci *aci)
{
long timeout;
unsigned char byte;
- for (timeout = 1; timeout <= ACI_MINTIME+30; timeout++) {
- if (((byte=inb(miro->aci_port + ACI_REG_BUSY)) & 1) == 0) {
+ for (timeout = 1; timeout <= ACI_MINTIME + 30; timeout++) {
+ byte = inb(aci->aci_port + ACI_REG_BUSY);
+ if ((byte & 1) == 0) {
if (timeout >= ACI_MINTIME)
snd_printd("aci ready in round %ld.\n",
timeout-ACI_MINTIME);
@@ -179,10 +191,10 @@ static int aci_busy_wait(struct snd_miro * miro)
return -EBUSY;
}
-static inline int aci_write(struct snd_miro * miro, unsigned char byte)
+static inline int aci_write(struct snd_miro_aci *aci, unsigned char byte)
{
- if (aci_busy_wait(miro) >= 0) {
- outb(byte, miro->aci_port + ACI_REG_COMMAND);
+ if (aci_busy_wait(aci) >= 0) {
+ outb(byte, aci->aci_port + ACI_REG_COMMAND);
return 0;
} else {
snd_printk(KERN_ERR "aci busy, aci_write(0x%x) stopped.\n", byte);
@@ -190,12 +202,12 @@ static inline int aci_write(struct snd_miro * miro, unsigned char byte)
}
}
-static inline int aci_read(struct snd_miro * miro)
+static inline int aci_read(struct snd_miro_aci *aci)
{
unsigned char byte;
- if (aci_busy_wait(miro) >= 0) {
- byte=inb(miro->aci_port + ACI_REG_STATUS);
+ if (aci_busy_wait(aci) >= 0) {
+ byte = inb(aci->aci_port + ACI_REG_STATUS);
return byte;
} else {
snd_printk(KERN_ERR "aci busy, aci_read() stopped.\n");
@@ -203,39 +215,49 @@ static inline int aci_read(struct snd_miro * miro)
}
}
-static int aci_cmd(struct snd_miro * miro, int write1, int write2, int write3)
+int snd_aci_cmd(struct snd_miro_aci *aci, int write1, int write2, int write3)
{
int write[] = {write1, write2, write3};
int value, i;
- if (mutex_lock_interruptible(&miro->aci_mutex))
+ if (mutex_lock_interruptible(&aci->aci_mutex))
return -EINTR;
for (i=0; i<3; i++) {
if (write[i]< 0 || write[i] > 255)
break;
else {
- value = aci_write(miro, write[i]);
+ value = aci_write(aci, write[i]);
if (value < 0)
goto out;
}
}
- value = aci_read(miro);
+ value = aci_read(aci);
-out: mutex_unlock(&miro->aci_mutex);
+out: mutex_unlock(&aci->aci_mutex);
return value;
}
+EXPORT_SYMBOL(snd_aci_cmd);
+
+static int aci_getvalue(struct snd_miro_aci *aci, unsigned char index)
+{
+ return snd_aci_cmd(aci, ACI_STATUS, index, -1);
+}
-static int aci_getvalue(struct snd_miro * miro, unsigned char index)
+static int aci_setvalue(struct snd_miro_aci *aci, unsigned char index,
+ int value)
{
- return aci_cmd(miro, ACI_STATUS, index, -1);
+ return snd_aci_cmd(aci, index, value, -1);
}
-static int aci_setvalue(struct snd_miro * miro, unsigned char index, int value)
+struct snd_miro_aci *snd_aci_get_aci(void)
{
- return aci_cmd(miro, index, value, -1);
+ if (aci_device.aci_port == 0)
+ return NULL;
+ return &aci_device;
}
+EXPORT_SYMBOL(snd_aci_get_aci);
/*
* MIXER part
@@ -249,8 +271,10 @@ static int snd_miro_get_capture(struct snd_kcontrol *kcontrol,
struct snd_miro *miro = snd_kcontrol_chip(kcontrol);
int value;
- if ((value = aci_getvalue(miro, ACI_S_GENERAL)) < 0) {
- snd_printk(KERN_ERR "snd_miro_get_capture() failed: %d\n", value);
+ value = aci_getvalue(miro->aci, ACI_S_GENERAL);
+ if (value < 0) {
+ snd_printk(KERN_ERR "snd_miro_get_capture() failed: %d\n",
+ value);
return value;
}
@@ -267,13 +291,15 @@ static int snd_miro_put_capture(struct snd_kcontrol *kcontrol,
value = !(ucontrol->value.integer.value[0]);
- if ((error = aci_setvalue(miro, ACI_SET_SOLOMODE, value)) < 0) {
- snd_printk(KERN_ERR "snd_miro_put_capture() failed: %d\n", error);
+ error = aci_setvalue(miro->aci, ACI_SET_SOLOMODE, value);
+ if (error < 0) {
+ snd_printk(KERN_ERR "snd_miro_put_capture() failed: %d\n",
+ error);
return error;
}
- change = (value != miro->aci_solomode);
- miro->aci_solomode = value;
+ change = (value != miro->aci->aci_solomode);
+ miro->aci->aci_solomode = value;
return change;
}
@@ -295,7 +321,7 @@ static int snd_miro_get_preamp(struct snd_kcontrol *kcontrol,
struct snd_miro *miro = snd_kcontrol_chip(kcontrol);
int value;
- if (miro->aci_version <= 176) {
+ if (miro->aci->aci_version <= 176) {
/*
OSS says it's not readable with versions < 176.
@@ -303,12 +329,14 @@ static int snd_miro_get_preamp(struct snd_kcontrol *kcontrol,
which is a PCM12 with aci_version = 176.
*/
- ucontrol->value.integer.value[0] = miro->aci_preamp;
+ ucontrol->value.integer.value[0] = miro->aci->aci_preamp;
return 0;
}
- if ((value = aci_getvalue(miro, ACI_GET_PREAMP)) < 0) {
- snd_printk(KERN_ERR "snd_miro_get_preamp() failed: %d\n", value);
+ value = aci_getvalue(miro->aci, ACI_GET_PREAMP);
+ if (value < 0) {
+ snd_printk(KERN_ERR "snd_miro_get_preamp() failed: %d\n",
+ value);
return value;
}
@@ -325,13 +353,15 @@ static int snd_miro_put_preamp(struct snd_kcontrol *kcontrol,
value = ucontrol->value.integer.value[0];
- if ((error = aci_setvalue(miro, ACI_SET_PREAMP, value)) < 0) {
- snd_printk(KERN_ERR "snd_miro_put_preamp() failed: %d\n", error);
+ error = aci_setvalue(miro->aci, ACI_SET_PREAMP, value);
+ if (error < 0) {
+ snd_printk(KERN_ERR "snd_miro_put_preamp() failed: %d\n",
+ error);
return error;
}
- change = (value != miro->aci_preamp);
- miro->aci_preamp = value;
+ change = (value != miro->aci->aci_preamp);
+ miro->aci->aci_preamp = value;
return change;
}
@@ -342,7 +372,7 @@ static int snd_miro_get_amp(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
struct snd_miro *miro = snd_kcontrol_chip(kcontrol);
- ucontrol->value.integer.value[0] = miro->aci_amp;
+ ucontrol->value.integer.value[0] = miro->aci->aci_amp;
return 0;
}
@@ -355,13 +385,14 @@ static int snd_miro_put_amp(struct snd_kcontrol *kcontrol,
value = ucontrol->value.integer.value[0];
- if ((error = aci_setvalue(miro, ACI_SET_POWERAMP, value)) < 0) {
+ error = aci_setvalue(miro->aci, ACI_SET_POWERAMP, value);
+ if (error < 0) {
snd_printk(KERN_ERR "snd_miro_put_amp() to %d failed: %d\n", value, error);
return error;
}
- change = (value != miro->aci_amp);
- miro->aci_amp = value;
+ change = (value != miro->aci->aci_amp);
+ miro->aci->aci_amp = value;
return change;
}
@@ -410,12 +441,14 @@ static int snd_miro_get_double(struct snd_kcontrol *kcontrol,
int right_reg = kcontrol->private_value & 0xff;
int left_reg = right_reg + 1;
- if ((right_val = aci_getvalue(miro, right_reg)) < 0) {
+ right_val = aci_getvalue(miro->aci, right_reg);
+ if (right_val < 0) {
snd_printk(KERN_ERR "aci_getvalue(%d) failed: %d\n", right_reg, right_val);
return right_val;
}
- if ((left_val = aci_getvalue(miro, left_reg)) < 0) {
+ left_val = aci_getvalue(miro->aci, left_reg);
+ if (left_val < 0) {
snd_printk(KERN_ERR "aci_getvalue(%d) failed: %d\n", left_reg, left_val);
return left_val;
}
@@ -451,6 +484,7 @@ static int snd_miro_put_double(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
struct snd_miro *miro = snd_kcontrol_chip(kcontrol);
+ struct snd_miro_aci *aci = miro->aci;
int left, right, left_old, right_old;
int setreg_left, setreg_right, getreg_left, getreg_right;
int change, error;
@@ -459,21 +493,21 @@ static int snd_miro_put_double(struct snd_kcontrol *kcontrol,
right = ucontrol->value.integer.value[1];
setreg_right = (kcontrol->private_value >> 8) & 0xff;
- if (setreg_right == ACI_SET_MASTER) {
- setreg_left = setreg_right + 1;
- } else {
- setreg_left = setreg_right + 8;
- }
+ setreg_left = setreg_right + 8;
+ if (setreg_right == ACI_SET_MASTER)
+ setreg_left -= 7;
getreg_right = kcontrol->private_value & 0xff;
getreg_left = getreg_right + 1;
- if ((left_old = aci_getvalue(miro, getreg_left)) < 0) {
+ left_old = aci_getvalue(aci, getreg_left);
+ if (left_old < 0) {
snd_printk(KERN_ERR "aci_getvalue(%d) failed: %d\n", getreg_left, left_old);
return left_old;
}
- if ((right_old = aci_getvalue(miro, getreg_right)) < 0) {
+ right_old = aci_getvalue(aci, getreg_right);
+ if (right_old < 0) {
snd_printk(KERN_ERR "aci_getvalue(%d) failed: %d\n", getreg_right, right_old);
return right_old;
}
@@ -492,13 +526,15 @@ static int snd_miro_put_double(struct snd_kcontrol *kcontrol,
right_old = 0x80 - right_old;
if (left >= 0) {
- if ((error = aci_setvalue(miro, setreg_left, left)) < 0) {
+ error = aci_setvalue(aci, setreg_left, left);
+ if (error < 0) {
snd_printk(KERN_ERR "aci_setvalue(%d) failed: %d\n",
left, error);
return error;
}
} else {
- if ((error = aci_setvalue(miro, setreg_left, 0x80 - left)) < 0) {
+ error = aci_setvalue(aci, setreg_left, 0x80 - left);
+ if (error < 0) {
snd_printk(KERN_ERR "aci_setvalue(%d) failed: %d\n",
0x80 - left, error);
return error;
@@ -506,13 +542,15 @@ static int snd_miro_put_double(struct snd_kcontrol *kcontrol,
}
if (right >= 0) {
- if ((error = aci_setvalue(miro, setreg_right, right)) < 0) {
+ error = aci_setvalue(aci, setreg_right, right);
+ if (error < 0) {
snd_printk(KERN_ERR "aci_setvalue(%d) failed: %d\n",
right, error);
return error;
}
} else {
- if ((error = aci_setvalue(miro, setreg_right, 0x80 - right)) < 0) {
+ error = aci_setvalue(aci, setreg_right, 0x80 - right);
+ if (error < 0) {
snd_printk(KERN_ERR "aci_setvalue(%d) failed: %d\n",
0x80 - right, error);
return error;
@@ -530,12 +568,14 @@ static int snd_miro_put_double(struct snd_kcontrol *kcontrol,
left_old = 0x20 - left_old;
right_old = 0x20 - right_old;
- if ((error = aci_setvalue(miro, setreg_left, 0x20 - left)) < 0) {
+ error = aci_setvalue(aci, setreg_left, 0x20 - left);
+ if (error < 0) {
snd_printk(KERN_ERR "aci_setvalue(%d) failed: %d\n",
0x20 - left, error);
return error;
}
- if ((error = aci_setvalue(miro, setreg_right, 0x20 - right)) < 0) {
+ error = aci_setvalue(aci, setreg_right, 0x20 - right);
+ if (error < 0) {
snd_printk(KERN_ERR "aci_setvalue(%d) failed: %d\n",
0x20 - right, error);
return error;
@@ -633,11 +673,13 @@ static unsigned char aci_init_values[][2] __devinitdata = {
static int __devinit snd_set_aci_init_values(struct snd_miro *miro)
{
int idx, error;
+ struct snd_miro_aci *aci = miro->aci;
/* enable WSS on PCM1 */
- if ((miro->aci_product == 'A') && wss) {
- if ((error = aci_setvalue(miro, ACI_SET_WSS, wss)) < 0) {
+ if ((aci->aci_product == 'A') && wss) {
+ error = aci_setvalue(aci, ACI_SET_WSS, wss);
+ if (error < 0) {
snd_printk(KERN_ERR "enabling WSS mode failed\n");
return error;
}
@@ -646,7 +688,8 @@ static int __devinit snd_set_aci_init_values(struct snd_miro *miro)
/* enable IDE port */
if (ide) {
- if ((error = aci_setvalue(miro, ACI_SET_IDE, ide)) < 0) {
+ error = aci_setvalue(aci, ACI_SET_IDE, ide);
+ if (error < 0) {
snd_printk(KERN_ERR "enabling IDE port failed\n");
return error;
}
@@ -654,32 +697,31 @@ static int __devinit snd_set_aci_init_values(struct snd_miro *miro)
/* set common aci values */
- for (idx = 0; idx < ARRAY_SIZE(aci_init_values); idx++)
- if ((error = aci_setvalue(miro, aci_init_values[idx][0],
- aci_init_values[idx][1])) < 0) {
+ for (idx = 0; idx < ARRAY_SIZE(aci_init_values); idx++) {
+ error = aci_setvalue(aci, aci_init_values[idx][0],
+ aci_init_values[idx][1]);
+ if (error < 0) {
snd_printk(KERN_ERR "aci_setvalue(%d) failed: %d\n",
aci_init_values[idx][0], error);
return error;
}
-
- miro->aci_amp = 0;
- miro->aci_preamp = 0;
- miro->aci_solomode = 1;
+ }
+ aci->aci_amp = 0;
+ aci->aci_preamp = 0;
+ aci->aci_solomode = 1;
return 0;
}
-static int __devinit snd_miro_mixer(struct snd_miro *miro)
+static int __devinit snd_miro_mixer(struct snd_card *card,
+ struct snd_miro *miro)
{
- struct snd_card *card;
unsigned int idx;
int err;
- if (snd_BUG_ON(!miro || !miro->card))
+ if (snd_BUG_ON(!miro || !card))
return -EINVAL;
- card = miro->card;
-
switch (miro->hardware) {
case OPTi9XX_HW_82C924:
strcpy(card->mixername, "ACI & OPTi924");
@@ -697,7 +739,8 @@ static int __devinit snd_miro_mixer(struct snd_miro *miro)
return err;
}
- if ((miro->aci_product == 'A') || (miro->aci_product == 'B')) {
+ if ((miro->aci->aci_product == 'A') ||
+ (miro->aci->aci_product == 'B')) {
/* PCM1/PCM12 with power-amp and Line 2 */
if ((err = snd_ctl_add(card, snd_ctl_new1(&snd_miro_line_control[0], miro))) < 0)
return err;
@@ -705,16 +748,17 @@ static int __devinit snd_miro_mixer(struct snd_miro *miro)
return err;
}
- if ((miro->aci_product == 'B') || (miro->aci_product == 'C')) {
+ if ((miro->aci->aci_product == 'B') ||
+ (miro->aci->aci_product == 'C')) {
/* PCM12/PCM20 with mic-preamp */
if ((err = snd_ctl_add(card, snd_ctl_new1(&snd_miro_preamp_control[0], miro))) < 0)
return err;
- if (miro->aci_version >= 176)
+ if (miro->aci->aci_version >= 176)
if ((err = snd_ctl_add(card, snd_ctl_new1(&snd_miro_capture_control[0], miro))) < 0)
return err;
}
- if (miro->aci_product == 'C') {
+ if (miro->aci->aci_product == 'C') {
/* PCM20 with radio and 7 band equalizer */
if ((err = snd_ctl_add(card, snd_ctl_new1(&snd_miro_radio_control[0], miro))) < 0)
return err;
@@ -757,21 +801,26 @@ static int __devinit snd_miro_init(struct snd_miro *chip,
chip->irq = -1;
chip->dma1 = -1;
chip->dma2 = -1;
- chip->fm_port = -1;
chip->mpu_port = -1;
chip->mpu_irq = -1;
+ chip->pwd_reg = 3;
+
+#ifdef CONFIG_PNP
+ if (isapnp && chip->mc_base)
+ /* PnP resource gives the least 10 bits */
+ chip->mc_base |= 0xc00;
+ else
+#endif
+ chip->mc_base = 0xf8c;
+
switch (hardware) {
case OPTi9XX_HW_82C929:
- chip->mc_base = 0xf8c;
chip->password = 0xe3;
- chip->pwd_reg = 3;
break;
case OPTi9XX_HW_82C924:
- chip->mc_base = 0xf8c;
chip->password = 0xe5;
- chip->pwd_reg = 3;
break;
default:
@@ -853,14 +902,15 @@ static void snd_miro_proc_read(struct snd_info_entry * entry,
struct snd_info_buffer *buffer)
{
struct snd_miro *miro = (struct snd_miro *) entry->private_data;
+ struct snd_miro_aci *aci = miro->aci;
char* model = "unknown";
/* miroSOUND PCM1 pro, early PCM12 */
if ((miro->hardware == OPTi9XX_HW_82C929) &&
- (miro->aci_vendor == 'm') &&
- (miro->aci_product == 'A')) {
- switch(miro->aci_version) {
+ (aci->aci_vendor == 'm') &&
+ (aci->aci_product == 'A')) {
+ switch (aci->aci_version) {
case 3:
model = "miroSOUND PCM1 pro";
break;
@@ -873,9 +923,9 @@ static void snd_miro_proc_read(struct snd_info_entry * entry,
/* miroSOUND PCM12, PCM12 (Rev. E), PCM12 pnp */
if ((miro->hardware == OPTi9XX_HW_82C924) &&
- (miro->aci_vendor == 'm') &&
- (miro->aci_product == 'B')) {
- switch(miro->aci_version) {
+ (aci->aci_vendor == 'm') &&
+ (aci->aci_product == 'B')) {
+ switch (aci->aci_version) {
case 4:
model = "miroSOUND PCM12";
break;
@@ -891,9 +941,9 @@ static void snd_miro_proc_read(struct snd_info_entry * entry,
/* miroSOUND PCM20 radio */
if ((miro->hardware == OPTi9XX_HW_82C924) &&
- (miro->aci_vendor == 'm') &&
- (miro->aci_product == 'C')) {
- switch(miro->aci_version) {
+ (aci->aci_vendor == 'm') &&
+ (aci->aci_product == 'C')) {
+ switch (aci->aci_version) {
case 7:
model = "miroSOUND PCM20 radio (Rev. E)";
break;
@@ -917,17 +967,17 @@ static void snd_miro_proc_read(struct snd_info_entry * entry,
snd_iprintf(buffer, "ACI information:\n");
snd_iprintf(buffer, " vendor : ");
- switch(miro->aci_vendor) {
+ switch (aci->aci_vendor) {
case 'm':
snd_iprintf(buffer, "Miro\n");
break;
default:
- snd_iprintf(buffer, "unknown (0x%x)\n", miro->aci_vendor);
+ snd_iprintf(buffer, "unknown (0x%x)\n", aci->aci_vendor);
break;
}
snd_iprintf(buffer, " product : ");
- switch(miro->aci_product) {
+ switch (aci->aci_product) {
case 'A':
snd_iprintf(buffer, "miroSOUND PCM1 pro / (early) PCM12\n");
break;
@@ -938,26 +988,27 @@ static void snd_miro_proc_read(struct snd_info_entry * entry,
snd_iprintf(buffer, "miroSOUND PCM20 radio\n");
break;
default:
- snd_iprintf(buffer, "unknown (0x%x)\n", miro->aci_product);
+ snd_iprintf(buffer, "unknown (0x%x)\n", aci->aci_product);
break;
}
snd_iprintf(buffer, " firmware: %d (0x%x)\n",
- miro->aci_version, miro->aci_version);
+ aci->aci_version, aci->aci_version);
snd_iprintf(buffer, " port : 0x%lx-0x%lx\n",
- miro->aci_port, miro->aci_port+2);
+ aci->aci_port, aci->aci_port+2);
snd_iprintf(buffer, " wss : 0x%x\n", wss);
snd_iprintf(buffer, " ide : 0x%x\n", ide);
- snd_iprintf(buffer, " solomode: 0x%x\n", miro->aci_solomode);
- snd_iprintf(buffer, " amp : 0x%x\n", miro->aci_amp);
- snd_iprintf(buffer, " preamp : 0x%x\n", miro->aci_preamp);
+ snd_iprintf(buffer, " solomode: 0x%x\n", aci->aci_solomode);
+ snd_iprintf(buffer, " amp : 0x%x\n", aci->aci_amp);
+ snd_iprintf(buffer, " preamp : 0x%x\n", aci->aci_preamp);
}
-static void __devinit snd_miro_proc_init(struct snd_miro * miro)
+static void __devinit snd_miro_proc_init(struct snd_card *card,
+ struct snd_miro *miro)
{
struct snd_info_entry *entry;
- if (! snd_card_proc_new(miro->card, "miro", &entry))
+ if (!snd_card_proc_new(card, "miro", &entry))
snd_info_set_text_ops(entry, miro, snd_miro_proc_read);
}
@@ -974,37 +1025,40 @@ static int __devinit snd_miro_configure(struct snd_miro *chip)
unsigned char mpu_irq_bits;
unsigned long flags;
+ snd_miro_write_mask(chip, OPTi9XX_MC_REG(1), 0x80, 0x80);
+ snd_miro_write_mask(chip, OPTi9XX_MC_REG(2), 0x20, 0x20); /* OPL4 */
+ snd_miro_write_mask(chip, OPTi9XX_MC_REG(5), 0x02, 0x02);
+
switch (chip->hardware) {
case OPTi9XX_HW_82C924:
snd_miro_write_mask(chip, OPTi9XX_MC_REG(6), 0x02, 0x02);
- snd_miro_write_mask(chip, OPTi9XX_MC_REG(1), 0x80, 0x80);
- snd_miro_write_mask(chip, OPTi9XX_MC_REG(2), 0x20, 0x20); /* OPL4 */
snd_miro_write_mask(chip, OPTi9XX_MC_REG(3), 0xf0, 0xff);
- snd_miro_write_mask(chip, OPTi9XX_MC_REG(5), 0x02, 0x02);
break;
case OPTi9XX_HW_82C929:
/* untested init commands for OPTi929 */
- snd_miro_write_mask(chip, OPTi9XX_MC_REG(1), 0x80, 0x80);
- snd_miro_write_mask(chip, OPTi9XX_MC_REG(2), 0x20, 0x20); /* OPL4 */
snd_miro_write_mask(chip, OPTi9XX_MC_REG(4), 0x00, 0x0c);
- snd_miro_write_mask(chip, OPTi9XX_MC_REG(5), 0x02, 0x02);
break;
default:
snd_printk(KERN_ERR "chip %d not supported\n", chip->hardware);
return -EINVAL;
}
- switch (chip->wss_base) {
- case 0x530:
+ /* PnP resource says it decodes only 10 bits of address */
+ switch (chip->wss_base & 0x3ff) {
+ case 0x130:
+ chip->wss_base = 0x530;
wss_base_bits = 0x00;
break;
- case 0x604:
+ case 0x204:
+ chip->wss_base = 0x604;
wss_base_bits = 0x03;
break;
- case 0xe80:
+ case 0x280:
+ chip->wss_base = 0xe80;
wss_base_bits = 0x01;
break;
- case 0xf40:
+ case 0x340:
+ chip->wss_base = 0xf40;
wss_base_bits = 0x02;
break;
default:
@@ -1122,75 +1176,92 @@ __skip_mpu:
return 0;
}
+static int __devinit snd_miro_opti_check(struct snd_miro *chip)
+{
+ unsigned char value;
+
+ chip->res_mc_base = request_region(chip->mc_base, chip->mc_base_size,
+ "OPTi9xx MC");
+ if (chip->res_mc_base == NULL)
+ return -ENOMEM;
+
+ value = snd_miro_read(chip, OPTi9XX_MC_REG(1));
+ if (value != 0xff && value != inb(chip->mc_base + OPTi9XX_MC_REG(1)))
+ if (value == snd_miro_read(chip, OPTi9XX_MC_REG(1)))
+ return 0;
+
+ release_and_free_resource(chip->res_mc_base);
+ chip->res_mc_base = NULL;
+
+ return -ENODEV;
+}
+
static int __devinit snd_card_miro_detect(struct snd_card *card,
struct snd_miro *chip)
{
int i, err;
- unsigned char value;
for (i = OPTi9XX_HW_82C929; i <= OPTi9XX_HW_82C924; i++) {
if ((err = snd_miro_init(chip, i)) < 0)
return err;
- if ((chip->res_mc_base = request_region(chip->mc_base, chip->mc_base_size, "OPTi9xx MC")) == NULL)
- continue;
-
- value = snd_miro_read(chip, OPTi9XX_MC_REG(1));
- if ((value != 0xff) && (value != inb(chip->mc_base + 1)))
- if (value == snd_miro_read(chip, OPTi9XX_MC_REG(1)))
- return 1;
-
- release_and_free_resource(chip->res_mc_base);
- chip->res_mc_base = NULL;
-
+ err = snd_miro_opti_check(chip);
+ if (err == 0)
+ return 1;
}
return -ENODEV;
}
static int __devinit snd_card_miro_aci_detect(struct snd_card *card,
- struct snd_miro * miro)
+ struct snd_miro *miro)
{
unsigned char regval;
int i;
+ struct snd_miro_aci *aci = &aci_device;
+
+ miro->aci = aci;
- mutex_init(&miro->aci_mutex);
+ mutex_init(&aci->aci_mutex);
/* get ACI port from OPTi9xx MC 4 */
- miro->mc_base = 0xf8c;
regval=inb(miro->mc_base + 4);
- miro->aci_port = (regval & 0x10) ? 0x344: 0x354;
+ aci->aci_port = (regval & 0x10) ? 0x344 : 0x354;
- if ((miro->res_aci_port = request_region(miro->aci_port, 3, "miro aci")) == NULL) {
+ miro->res_aci_port = request_region(aci->aci_port, 3, "miro aci");
+ if (miro->res_aci_port == NULL) {
snd_printk(KERN_ERR "aci i/o area 0x%lx-0x%lx already used.\n",
- miro->aci_port, miro->aci_port+2);
+ aci->aci_port, aci->aci_port+2);
return -ENOMEM;
}
/* force ACI into a known state */
for (i = 0; i < 3; i++)
- if (aci_cmd(miro, ACI_ERROR_OP, -1, -1) < 0) {
+ if (snd_aci_cmd(aci, ACI_ERROR_OP, -1, -1) < 0) {
snd_printk(KERN_ERR "can't force aci into known state.\n");
return -ENXIO;
}
- if ((miro->aci_vendor=aci_cmd(miro, ACI_READ_IDCODE, -1, -1)) < 0 ||
- (miro->aci_product=aci_cmd(miro, ACI_READ_IDCODE, -1, -1)) < 0) {
- snd_printk(KERN_ERR "can't read aci id on 0x%lx.\n", miro->aci_port);
+ aci->aci_vendor = snd_aci_cmd(aci, ACI_READ_IDCODE, -1, -1);
+ aci->aci_product = snd_aci_cmd(aci, ACI_READ_IDCODE, -1, -1);
+ if (aci->aci_vendor < 0 || aci->aci_product < 0) {
+ snd_printk(KERN_ERR "can't read aci id on 0x%lx.\n",
+ aci->aci_port);
return -ENXIO;
}
- if ((miro->aci_version=aci_cmd(miro, ACI_READ_VERSION, -1, -1)) < 0) {
+ aci->aci_version = snd_aci_cmd(aci, ACI_READ_VERSION, -1, -1);
+ if (aci->aci_version < 0) {
snd_printk(KERN_ERR "can't read aci version on 0x%lx.\n",
- miro->aci_port);
+ aci->aci_port);
return -ENXIO;
}
- if (aci_cmd(miro, ACI_INIT, -1, -1) < 0 ||
- aci_cmd(miro, ACI_ERROR_OP, ACI_ERROR_OP, ACI_ERROR_OP) < 0 ||
- aci_cmd(miro, ACI_ERROR_OP, ACI_ERROR_OP, ACI_ERROR_OP) < 0) {
+ if (snd_aci_cmd(aci, ACI_INIT, -1, -1) < 0 ||
+ snd_aci_cmd(aci, ACI_ERROR_OP, ACI_ERROR_OP, ACI_ERROR_OP) < 0 ||
+ snd_aci_cmd(aci, ACI_ERROR_OP, ACI_ERROR_OP, ACI_ERROR_OP) < 0) {
snd_printk(KERN_ERR "can't initialize aci.\n");
return -ENXIO;
}
@@ -1201,157 +1272,80 @@ static int __devinit snd_card_miro_aci_detect(struct snd_card *card,
static void snd_card_miro_free(struct snd_card *card)
{
struct snd_miro *miro = card->private_data;
-
+
release_and_free_resource(miro->res_aci_port);
+ if (miro->aci)
+ miro->aci->aci_port = 0;
release_and_free_resource(miro->res_mc_base);
}
-static int __devinit snd_miro_match(struct device *devptr, unsigned int n)
-{
- return 1;
-}
-
-static int __devinit snd_miro_probe(struct device *devptr, unsigned int n)
+static int __devinit snd_miro_probe(struct snd_card *card)
{
- static long possible_ports[] = {0x530, 0xe80, 0xf40, 0x604, -1};
- static long possible_mpu_ports[] = {0x330, 0x300, 0x310, 0x320, -1};
- static int possible_irqs[] = {11, 9, 10, 7, -1};
- static int possible_mpu_irqs[] = {10, 5, 9, 7, -1};
- static int possible_dma1s[] = {3, 1, 0, -1};
- static int possible_dma2s[][2] = {{1,-1}, {0,-1}, {-1,-1}, {0,-1}};
-
int error;
- struct snd_miro *miro;
+ struct snd_miro *miro = card->private_data;
struct snd_wss *codec;
struct snd_timer *timer;
- struct snd_card *card;
struct snd_pcm *pcm;
struct snd_rawmidi *rmidi;
- error = snd_card_create(index, id, THIS_MODULE,
- sizeof(struct snd_miro), &card);
- if (error < 0)
- return error;
-
- card->private_free = snd_card_miro_free;
- miro = card->private_data;
- miro->card = card;
-
- if ((error = snd_card_miro_aci_detect(card, miro)) < 0) {
- snd_card_free(card);
- snd_printk(KERN_ERR "unable to detect aci chip\n");
- return -ENODEV;
+ if (!miro->res_mc_base) {
+ miro->res_mc_base = request_region(miro->mc_base,
+ miro->mc_base_size,
+ "miro (OPTi9xx MC)");
+ if (miro->res_mc_base == NULL) {
+ snd_printk(KERN_ERR "request for OPTI9xx MC failed\n");
+ return -ENOMEM;
+ }
}
- /* init proc interface */
- snd_miro_proc_init(miro);
-
- if ((error = snd_card_miro_detect(card, miro)) < 0) {
+ error = snd_card_miro_aci_detect(card, miro);
+ if (error < 0) {
snd_card_free(card);
- snd_printk(KERN_ERR "unable to detect OPTi9xx chip\n");
+ snd_printk(KERN_ERR "unable to detect aci chip\n");
return -ENODEV;
}
- if (! miro->res_mc_base &&
- (miro->res_mc_base = request_region(miro->mc_base, miro->mc_base_size,
- "miro (OPTi9xx MC)")) == NULL) {
- snd_card_free(card);
- snd_printk(KERN_ERR "request for OPTI9xx MC failed\n");
- return -ENOMEM;
- }
-
miro->wss_base = port;
- miro->fm_port = fm_port;
miro->mpu_port = mpu_port;
miro->irq = irq;
miro->mpu_irq = mpu_irq;
miro->dma1 = dma1;
miro->dma2 = dma2;
- if (miro->wss_base == SNDRV_AUTO_PORT) {
- if ((miro->wss_base = snd_legacy_find_free_ioport(possible_ports, 4)) < 0) {
- snd_card_free(card);
- snd_printk(KERN_ERR "unable to find a free WSS port\n");
- return -EBUSY;
- }
- }
-
- if (miro->mpu_port == SNDRV_AUTO_PORT) {
- if ((miro->mpu_port = snd_legacy_find_free_ioport(possible_mpu_ports, 2)) < 0) {
- snd_card_free(card);
- snd_printk(KERN_ERR "unable to find a free MPU401 port\n");
- return -EBUSY;
- }
- }
- if (miro->irq == SNDRV_AUTO_IRQ) {
- if ((miro->irq = snd_legacy_find_free_irq(possible_irqs)) < 0) {
- snd_card_free(card);
- snd_printk(KERN_ERR "unable to find a free IRQ\n");
- return -EBUSY;
- }
- }
- if (miro->mpu_irq == SNDRV_AUTO_IRQ) {
- if ((miro->mpu_irq = snd_legacy_find_free_irq(possible_mpu_irqs)) < 0) {
- snd_card_free(card);
- snd_printk(KERN_ERR "unable to find a free MPU401 IRQ\n");
- return -EBUSY;
- }
- }
- if (miro->dma1 == SNDRV_AUTO_DMA) {
- if ((miro->dma1 = snd_legacy_find_free_dma(possible_dma1s)) < 0) {
- snd_card_free(card);
- snd_printk(KERN_ERR "unable to find a free DMA1\n");
- return -EBUSY;
- }
- }
- if (miro->dma2 == SNDRV_AUTO_DMA) {
- if ((miro->dma2 = snd_legacy_find_free_dma(possible_dma2s[miro->dma1 % 4])) < 0) {
- snd_card_free(card);
- snd_printk(KERN_ERR "unable to find a free DMA2\n");
- return -EBUSY;
- }
- }
+ /* init proc interface */
+ snd_miro_proc_init(card, miro);
error = snd_miro_configure(miro);
- if (error) {
- snd_card_free(card);
+ if (error)
return error;
- }
error = snd_wss_create(card, miro->wss_base + 4, -1,
- miro->irq, miro->dma1, miro->dma2,
- WSS_HW_AD1845, 0, &codec);
- if (error < 0) {
- snd_card_free(card);
+ miro->irq, miro->dma1, miro->dma2,
+ WSS_HW_DETECT, 0, &codec);
+ if (error < 0)
return error;
- }
error = snd_wss_pcm(codec, 0, &pcm);
- if (error < 0) {
- snd_card_free(card);
+ if (error < 0)
return error;
- }
+
error = snd_wss_mixer(codec);
- if (error < 0) {
- snd_card_free(card);
+ if (error < 0)
return error;
- }
+
error = snd_wss_timer(codec, 0, &timer);
- if (error < 0) {
- snd_card_free(card);
+ if (error < 0)
return error;
- }
miro->pcm = pcm;
- if ((error = snd_miro_mixer(miro)) < 0) {
- snd_card_free(card);
+ error = snd_miro_mixer(card, miro);
+ if (error < 0)
return error;
- }
- if (miro->aci_vendor == 'm') {
+ if (miro->aci->aci_vendor == 'm') {
/* It looks like a miro sound card. */
- switch (miro->aci_product) {
+ switch (miro->aci->aci_product) {
case 'A':
sprintf(card->shortname,
"miroSOUND PCM1 pro / PCM12");
@@ -1380,30 +1374,131 @@ static int __devinit snd_miro_probe(struct device *devptr, unsigned int n)
card->shortname, miro->name, pcm->name, miro->wss_base + 4,
miro->irq, miro->dma1, miro->dma2);
- if (miro->mpu_port <= 0 || miro->mpu_port == SNDRV_AUTO_PORT)
+ if (mpu_port <= 0 || mpu_port == SNDRV_AUTO_PORT)
rmidi = NULL;
- else
- if ((error = snd_mpu401_uart_new(card, 0, MPU401_HW_MPU401,
- miro->mpu_port, 0, miro->mpu_irq, IRQF_DISABLED,
- &rmidi)))
- snd_printk(KERN_WARNING "no MPU-401 device at 0x%lx?\n", miro->mpu_port);
+ else {
+ error = snd_mpu401_uart_new(card, 0, MPU401_HW_MPU401,
+ mpu_port, 0, miro->mpu_irq, IRQF_DISABLED,
+ &rmidi);
+ if (error < 0)
+ snd_printk(KERN_WARNING "no MPU-401 device at 0x%lx?\n",
+ mpu_port);
+ }
- if (miro->fm_port > 0 && miro->fm_port != SNDRV_AUTO_PORT) {
+ if (fm_port > 0 && fm_port != SNDRV_AUTO_PORT) {
struct snd_opl3 *opl3 = NULL;
struct snd_opl4 *opl4;
- if (snd_opl4_create(card, miro->fm_port, miro->fm_port - 8,
+
+ if (snd_opl4_create(card, fm_port, fm_port - 8,
2, &opl3, &opl4) < 0)
- snd_printk(KERN_WARNING "no OPL4 device at 0x%lx\n", miro->fm_port);
+ snd_printk(KERN_WARNING "no OPL4 device at 0x%lx\n",
+ fm_port);
}
- if ((error = snd_set_aci_init_values(miro)) < 0) {
- snd_card_free(card);
+ error = snd_set_aci_init_values(miro);
+ if (error < 0)
return error;
+
+ return snd_card_register(card);
+}
+
+static int __devinit snd_miro_isa_match(struct device *devptr, unsigned int n)
+{
+#ifdef CONFIG_PNP
+ if (snd_miro_pnp_is_probed)
+ return 0;
+ if (isapnp)
+ return 0;
+#endif
+ return 1;
+}
+
+static int __devinit snd_miro_isa_probe(struct device *devptr, unsigned int n)
+{
+ static long possible_ports[] = {0x530, 0xe80, 0xf40, 0x604, -1};
+ static long possible_mpu_ports[] = {0x330, 0x300, 0x310, 0x320, -1};
+ static int possible_irqs[] = {11, 9, 10, 7, -1};
+ static int possible_mpu_irqs[] = {10, 5, 9, 7, -1};
+ static int possible_dma1s[] = {3, 1, 0, -1};
+ static int possible_dma2s[][2] = { {1, -1}, {0, -1}, {-1, -1},
+ {0, -1} };
+
+ int error;
+ struct snd_miro *miro;
+ struct snd_card *card;
+
+ error = snd_card_create(index, id, THIS_MODULE,
+ sizeof(struct snd_miro), &card);
+ if (error < 0)
+ return error;
+
+ card->private_free = snd_card_miro_free;
+ miro = card->private_data;
+
+ error = snd_card_miro_detect(card, miro);
+ if (error < 0) {
+ snd_card_free(card);
+ snd_printk(KERN_ERR "unable to detect OPTi9xx chip\n");
+ return -ENODEV;
+ }
+
+ if (port == SNDRV_AUTO_PORT) {
+ port = snd_legacy_find_free_ioport(possible_ports, 4);
+ if (port < 0) {
+ snd_card_free(card);
+ snd_printk(KERN_ERR "unable to find a free WSS port\n");
+ return -EBUSY;
+ }
+ }
+
+ if (mpu_port == SNDRV_AUTO_PORT) {
+ mpu_port = snd_legacy_find_free_ioport(possible_mpu_ports, 2);
+ if (mpu_port < 0) {
+ snd_card_free(card);
+ snd_printk(KERN_ERR
+ "unable to find a free MPU401 port\n");
+ return -EBUSY;
+ }
+ }
+
+ if (irq == SNDRV_AUTO_IRQ) {
+ irq = snd_legacy_find_free_irq(possible_irqs);
+ if (irq < 0) {
+ snd_card_free(card);
+ snd_printk(KERN_ERR "unable to find a free IRQ\n");
+ return -EBUSY;
+ }
+ }
+ if (mpu_irq == SNDRV_AUTO_IRQ) {
+ mpu_irq = snd_legacy_find_free_irq(possible_mpu_irqs);
+ if (mpu_irq < 0) {
+ snd_card_free(card);
+ snd_printk(KERN_ERR
+ "unable to find a free MPU401 IRQ\n");
+ return -EBUSY;
+ }
+ }
+ if (dma1 == SNDRV_AUTO_DMA) {
+ dma1 = snd_legacy_find_free_dma(possible_dma1s);
+ if (dma1 < 0) {
+ snd_card_free(card);
+ snd_printk(KERN_ERR "unable to find a free DMA1\n");
+ return -EBUSY;
+ }
+ }
+ if (dma2 == SNDRV_AUTO_DMA) {
+ dma2 = snd_legacy_find_free_dma(possible_dma2s[dma1 % 4]);
+ if (dma2 < 0) {
+ snd_card_free(card);
+ snd_printk(KERN_ERR "unable to find a free DMA2\n");
+ return -EBUSY;
+ }
}
snd_card_set_dev(card, devptr);
- if ((error = snd_card_register(card))) {
+ error = snd_miro_probe(card);
+ if (error < 0) {
snd_card_free(card);
return error;
}
@@ -1412,7 +1507,8 @@ static int __devinit snd_miro_probe(struct device *devptr, unsigned int n)
return 0;
}
-static int __devexit snd_miro_remove(struct device *devptr, unsigned int dev)
+static int __devexit snd_miro_isa_remove(struct device *devptr,
+ unsigned int dev)
{
snd_card_free(dev_get_drvdata(devptr));
dev_set_drvdata(devptr, NULL);
@@ -1422,23 +1518,164 @@ static int __devexit snd_miro_remove(struct device *devptr, unsigned int dev)
#define DEV_NAME "miro"
static struct isa_driver snd_miro_driver = {
- .match = snd_miro_match,
- .probe = snd_miro_probe,
- .remove = __devexit_p(snd_miro_remove),
+ .match = snd_miro_isa_match,
+ .probe = snd_miro_isa_probe,
+ .remove = __devexit_p(snd_miro_isa_remove),
/* FIXME: suspend/resume */
.driver = {
.name = DEV_NAME
},
};
+#ifdef CONFIG_PNP
+
+static int __devinit snd_card_miro_pnp(struct snd_miro *chip,
+ struct pnp_card_link *card,
+ const struct pnp_card_device_id *pid)
+{
+ struct pnp_dev *pdev;
+ int err;
+ struct pnp_dev *devmpu;
+ struct pnp_dev *devmc;
+
+ pdev = pnp_request_card_device(card, pid->devs[0].id, NULL);
+ if (pdev == NULL)
+ return -EBUSY;
+
+ devmpu = pnp_request_card_device(card, pid->devs[1].id, NULL);
+ if (devmpu == NULL)
+ return -EBUSY;
+
+ devmc = pnp_request_card_device(card, pid->devs[2].id, NULL);
+ if (devmc == NULL)
+ return -EBUSY;
+
+ err = pnp_activate_dev(pdev);
+ if (err < 0) {
+ snd_printk(KERN_ERR "AUDIO pnp configure failure: %d\n", err);
+ return err;
+ }
+
+ err = pnp_activate_dev(devmc);
+ if (err < 0) {
+ snd_printk(KERN_ERR "OPL syntg pnp configure failure: %d\n",
+ err);
+ return err;
+ }
+
+ port = pnp_port_start(pdev, 1);
+ fm_port = pnp_port_start(pdev, 2) + 8;
+
+ /*
+ * The MC(0) is never accessed and the miroSOUND PCM20 card does not
+ * include it in the PnP resource range. OPTI93x include it.
+ */
+ chip->mc_base = pnp_port_start(devmc, 0) - 1;
+ chip->mc_base_size = pnp_port_len(devmc, 0) + 1;
+
+ irq = pnp_irq(pdev, 0);
+ dma1 = pnp_dma(pdev, 0);
+ dma2 = pnp_dma(pdev, 1);
+
+ if (mpu_port > 0) {
+ err = pnp_activate_dev(devmpu);
+ if (err < 0) {
+ snd_printk(KERN_ERR "MPU401 pnp configure failure\n");
+ mpu_port = -1;
+ return err;
+ }
+ mpu_port = pnp_port_start(devmpu, 0);
+ mpu_irq = pnp_irq(devmpu, 0);
+ }
+ return 0;
+}
+
+static int __devinit snd_miro_pnp_probe(struct pnp_card_link *pcard,
+ const struct pnp_card_device_id *pid)
+{
+ struct snd_card *card;
+ int err;
+ struct snd_miro *miro;
+
+ if (snd_miro_pnp_is_probed)
+ return -EBUSY;
+ if (!isapnp)
+ return -ENODEV;
+ err = snd_card_create(index, id, THIS_MODULE,
+ sizeof(struct snd_miro), &card);
+ if (err < 0)
+ return err;
+
+ card->private_free = snd_card_miro_free;
+ miro = card->private_data;
+
+ err = snd_card_miro_pnp(miro, pcard, pid);
+ if (err) {
+ snd_card_free(card);
+ return err;
+ }
+
+ /* only miroSOUND PCM20 and PCM12 == OPTi924 */
+ err = snd_miro_init(miro, OPTi9XX_HW_82C924);
+ if (err) {
+ snd_card_free(card);
+ return err;
+ }
+
+ err = snd_miro_opti_check(miro);
+ if (err) {
+ snd_printk(KERN_ERR "OPTI chip not found\n");
+ snd_card_free(card);
+ return err;
+ }
+
+ snd_card_set_dev(card, &pcard->card->dev);
+ err = snd_miro_probe(card);
+ if (err < 0) {
+ snd_card_free(card);
+ return err;
+ }
+ pnp_set_card_drvdata(pcard, card);
+ snd_miro_pnp_is_probed = 1;
+ return 0;
+}
+
+static void __devexit snd_miro_pnp_remove(struct pnp_card_link * pcard)
+{
+ snd_card_free(pnp_get_card_drvdata(pcard));
+ pnp_set_card_drvdata(pcard, NULL);
+ snd_miro_pnp_is_probed = 0;
+}
+
+static struct pnp_card_driver miro_pnpc_driver = {
+ .flags = PNP_DRIVER_RES_DISABLE,
+ .name = "miro",
+ .id_table = snd_miro_pnpids,
+ .probe = snd_miro_pnp_probe,
+ .remove = __devexit_p(snd_miro_pnp_remove),
+};
+#endif
+
static int __init alsa_card_miro_init(void)
{
+#ifdef CONFIG_PNP
+ pnp_register_card_driver(&miro_pnpc_driver);
+ if (snd_miro_pnp_is_probed)
+ return 0;
+ pnp_unregister_card_driver(&miro_pnpc_driver);
+#endif
return isa_register_driver(&snd_miro_driver, 1);
}
static void __exit alsa_card_miro_exit(void)
{
- isa_unregister_driver(&snd_miro_driver);
+ if (!snd_miro_pnp_is_probed) {
+ isa_unregister_driver(&snd_miro_driver);
+ return;
+ }
+#ifdef CONFIG_PNP
+ pnp_unregister_card_driver(&miro_pnpc_driver);
+#endif
}
module_init(alsa_card_miro_init)
diff --git a/sound/isa/opti9xx/opti92x-ad1848.c b/sound/isa/opti9xx/opti92x-ad1848.c
index 5cd555325b9d..d08c38906449 100644
--- a/sound/isa/opti9xx/opti92x-ad1848.c
+++ b/sound/isa/opti9xx/opti92x-ad1848.c
@@ -141,15 +141,7 @@ struct snd_opti9xx {
spinlock_t lock;
- long wss_base;
int irq;
- int dma1;
- int dma2;
-
- long fm_port;
-
- long mpu_port;
- int mpu_irq;
#ifdef CONFIG_PNP
struct pnp_dev *dev;
@@ -216,13 +208,7 @@ static int __devinit snd_opti9xx_init(struct snd_opti9xx *chip,
spin_lock_init(&chip->lock);
- chip->wss_base = -1;
chip->irq = -1;
- chip->dma1 = -1;
- chip->dma2 = -1;
- chip->fm_port = -1;
- chip->mpu_port = -1;
- chip->mpu_irq = -1;
switch (hardware) {
#ifndef OPTi93X
@@ -348,7 +334,10 @@ static void snd_opti9xx_write(struct snd_opti9xx *chip, unsigned char reg,
(snd_opti9xx_read(chip, reg) & ~(mask)) | ((value) & (mask)))
-static int __devinit snd_opti9xx_configure(struct snd_opti9xx *chip)
+static int __devinit snd_opti9xx_configure(struct snd_opti9xx *chip,
+ long wss_base,
+ int irq, int dma1, int dma2,
+ long mpu_port, int mpu_irq)
{
unsigned char wss_base_bits;
unsigned char irq_bits;
@@ -416,7 +405,7 @@ static int __devinit snd_opti9xx_configure(struct snd_opti9xx *chip)
return -EINVAL;
}
- switch (chip->wss_base) {
+ switch (wss_base) {
case 0x530:
wss_base_bits = 0x00;
break;
@@ -430,14 +419,13 @@ static int __devinit snd_opti9xx_configure(struct snd_opti9xx *chip)
wss_base_bits = 0x02;
break;
default:
- snd_printk(KERN_WARNING "WSS port 0x%lx not valid\n",
- chip->wss_base);
+ snd_printk(KERN_WARNING "WSS port 0x%lx not valid\n", wss_base);
goto __skip_base;
}
snd_opti9xx_write_mask(chip, OPTi9XX_MC_REG(1), wss_base_bits << 4, 0x30);
__skip_base:
- switch (chip->irq) {
+ switch (irq) {
//#ifdef OPTi93X
case 5:
irq_bits = 0x05;
@@ -456,11 +444,11 @@ __skip_base:
irq_bits = 0x04;
break;
default:
- snd_printk(KERN_WARNING "WSS irq # %d not valid\n", chip->irq);
+ snd_printk(KERN_WARNING "WSS irq # %d not valid\n", irq);
goto __skip_resources;
}
- switch (chip->dma1) {
+ switch (dma1) {
case 0:
dma_bits = 0x01;
break;
@@ -471,38 +459,36 @@ __skip_base:
dma_bits = 0x03;
break;
default:
- snd_printk(KERN_WARNING "WSS dma1 # %d not valid\n",
- chip->dma1);
+ snd_printk(KERN_WARNING "WSS dma1 # %d not valid\n", dma1);
goto __skip_resources;
}
#if defined(CS4231) || defined(OPTi93X)
- if (chip->dma1 == chip->dma2) {
+ if (dma1 == dma2) {
snd_printk(KERN_ERR "don't want to share dmas\n");
return -EBUSY;
}
- switch (chip->dma2) {
+ switch (dma2) {
case 0:
case 1:
break;
default:
- snd_printk(KERN_WARNING "WSS dma2 # %d not valid\n",
- chip->dma2);
+ snd_printk(KERN_WARNING "WSS dma2 # %d not valid\n", dma2);
goto __skip_resources;
}
dma_bits |= 0x04;
#endif /* CS4231 || OPTi93X */
#ifndef OPTi93X
- outb(irq_bits << 3 | dma_bits, chip->wss_base);
+ outb(irq_bits << 3 | dma_bits, wss_base);
#else /* OPTi93X */
snd_opti9xx_write(chip, OPTi9XX_MC_REG(3), (irq_bits << 3 | dma_bits));
#endif /* OPTi93X */
__skip_resources:
if (chip->hardware > OPTi9XX_HW_82C928) {
- switch (chip->mpu_port) {
+ switch (mpu_port) {
case 0:
case -1:
break;
@@ -520,12 +506,11 @@ __skip_resources:
break;
default:
snd_printk(KERN_WARNING
- "MPU-401 port 0x%lx not valid\n",
- chip->mpu_port);
+ "MPU-401 port 0x%lx not valid\n", mpu_port);
goto __skip_mpu;
}
- switch (chip->mpu_irq) {
+ switch (mpu_irq) {
case 5:
mpu_irq_bits = 0x02;
break;
@@ -540,12 +525,12 @@ __skip_resources:
break;
default:
snd_printk(KERN_WARNING "MPU-401 irq # %d not valid\n",
- chip->mpu_irq);
+ mpu_irq);
goto __skip_mpu;
}
snd_opti9xx_write_mask(chip, OPTi9XX_MC_REG(6),
- (chip->mpu_port <= 0) ? 0x00 :
+ (mpu_port <= 0) ? 0x00 :
0x80 | mpu_port_bits << 5 | mpu_irq_bits << 3,
0xf8);
}
@@ -701,6 +686,7 @@ static int __devinit snd_opti9xx_probe(struct snd_card *card)
{
static long possible_ports[] = {0x530, 0xe80, 0xf40, 0x604, -1};
int error;
+ int xdma2;
struct snd_opti9xx *chip = card->private_data;
struct snd_wss *codec;
#ifdef CS4231
@@ -715,31 +701,25 @@ static int __devinit snd_opti9xx_probe(struct snd_card *card)
"OPTi9xx MC")) == NULL)
return -ENOMEM;
- chip->wss_base = port;
- chip->fm_port = fm_port;
- chip->mpu_port = mpu_port;
- chip->irq = irq;
- chip->mpu_irq = mpu_irq;
- chip->dma1 = dma1;
#if defined(CS4231) || defined(OPTi93X)
- chip->dma2 = dma2;
+ xdma2 = dma2;
#else
- chip->dma2 = -1;
+ xdma2 = -1;
#endif
- if (chip->wss_base == SNDRV_AUTO_PORT) {
- chip->wss_base = snd_legacy_find_free_ioport(possible_ports, 4);
- if (chip->wss_base < 0) {
+ if (port == SNDRV_AUTO_PORT) {
+ port = snd_legacy_find_free_ioport(possible_ports, 4);
+ if (port < 0) {
snd_printk(KERN_ERR "unable to find a free WSS port\n");
return -EBUSY;
}
}
- error = snd_opti9xx_configure(chip);
+ error = snd_opti9xx_configure(chip, port, irq, dma1, xdma2,
+ mpu_port, mpu_irq);
if (error)
return error;
- error = snd_wss_create(card, chip->wss_base + 4, -1,
- chip->irq, chip->dma1, chip->dma2,
+ error = snd_wss_create(card, port + 4, -1, irq, dma1, xdma2,
#ifdef OPTi93X
WSS_HW_OPTI93X, WSS_HWSHARE_IRQ,
#else
@@ -763,35 +743,35 @@ static int __devinit snd_opti9xx_probe(struct snd_card *card)
return error;
#endif
#ifdef OPTi93X
- error = request_irq(chip->irq, snd_opti93x_interrupt,
+ error = request_irq(irq, snd_opti93x_interrupt,
IRQF_DISABLED, DEV_NAME" - WSS", codec);
if (error < 0) {
snd_printk(KERN_ERR "opti9xx: can't grab IRQ %d\n", chip->irq);
return error;
}
#endif
+ chip->irq = irq;
strcpy(card->driver, chip->name);
sprintf(card->shortname, "OPTi %s", card->driver);
#if defined(CS4231) || defined(OPTi93X)
sprintf(card->longname, "%s, %s at 0x%lx, irq %d, dma %d&%d",
- card->shortname, pcm->name, chip->wss_base + 4,
- chip->irq, chip->dma1, chip->dma2);
+ card->shortname, pcm->name, port + 4, irq, dma1, xdma2);
#else
sprintf(card->longname, "%s, %s at 0x%lx, irq %d, dma %d",
- card->shortname, pcm->name, chip->wss_base + 4,
- chip->irq, chip->dma1);
+ card->shortname, pcm->name, port + 4, irq, dma1);
#endif /* CS4231 || OPTi93X */
- if (chip->mpu_port <= 0 || chip->mpu_port == SNDRV_AUTO_PORT)
+ if (mpu_port <= 0 || mpu_port == SNDRV_AUTO_PORT)
rmidi = NULL;
- else
- if ((error = snd_mpu401_uart_new(card, 0, MPU401_HW_MPU401,
- chip->mpu_port, 0, chip->mpu_irq, IRQF_DISABLED,
- &rmidi)))
+ else {
+ error = snd_mpu401_uart_new(card, 0, MPU401_HW_MPU401,
+ mpu_port, 0, mpu_irq, IRQF_DISABLED, &rmidi);
+ if (error)
snd_printk(KERN_WARNING "no MPU-401 device at 0x%lx?\n",
- chip->mpu_port);
+ mpu_port);
+ }
- if (chip->fm_port > 0 && chip->fm_port != SNDRV_AUTO_PORT) {
+ if (fm_port > 0 && fm_port != SNDRV_AUTO_PORT) {
struct snd_opl3 *opl3 = NULL;
#ifndef OPTi93X
if (chip->hardware == OPTi9XX_HW_82C928 ||
@@ -801,9 +781,7 @@ static int __devinit snd_opti9xx_probe(struct snd_card *card)
/* assume we have an OPL4 */
snd_opti9xx_write_mask(chip, OPTi9XX_MC_REG(2),
0x20, 0x20);
- if (snd_opl4_create(card,
- chip->fm_port,
- chip->fm_port - 8,
+ if (snd_opl4_create(card, fm_port, fm_port - 8,
2, &opl3, &opl4) < 0) {
/* no luck, use OPL3 instead */
snd_opti9xx_write_mask(chip, OPTi9XX_MC_REG(2),
@@ -811,12 +789,10 @@ static int __devinit snd_opti9xx_probe(struct snd_card *card)
}
}
#endif /* !OPTi93X */
- if (!opl3 && snd_opl3_create(card,
- chip->fm_port,
- chip->fm_port + 2,
+ if (!opl3 && snd_opl3_create(card, fm_port, fm_port + 2,
OPL3_HW_AUTO, 0, &opl3) < 0) {
snd_printk(KERN_WARNING "no OPL device at 0x%lx-0x%lx\n",
- chip->fm_port, chip->fm_port + 4 - 1);
+ fm_port, fm_port + 4 - 1);
}
if (opl3) {
error = snd_opl3_hwdep_new(opl3, 0, 1, &synth);
diff --git a/sound/isa/sb/sb_mixer.c b/sound/isa/sb/sb_mixer.c
index 475220bbcc96..318ff0c823e7 100644
--- a/sound/isa/sb/sb_mixer.c
+++ b/sound/isa/sb/sb_mixer.c
@@ -631,7 +631,7 @@ static struct sbmix_elem snd_sb16_ctl_mic_play_switch =
static struct sbmix_elem snd_sb16_ctl_mic_play_vol =
SB_SINGLE("Mic Playback Volume", SB_DSP4_MIC_DEV, 3, 31);
static struct sbmix_elem snd_sb16_ctl_pc_speaker_vol =
- SB_SINGLE("PC Speaker Volume", SB_DSP4_SPEAKER_DEV, 6, 3);
+ SB_SINGLE("Beep Volume", SB_DSP4_SPEAKER_DEV, 6, 3);
static struct sbmix_elem snd_sb16_ctl_capture_vol =
SB_DOUBLE("Capture Volume", SB_DSP4_IGAIN_DEV, (SB_DSP4_IGAIN_DEV + 1), 6, 6, 3);
static struct sbmix_elem snd_sb16_ctl_play_vol =
@@ -689,7 +689,7 @@ static struct sbmix_elem snd_dt019x_ctl_cd_play_vol =
static struct sbmix_elem snd_dt019x_ctl_mic_play_vol =
SB_SINGLE("Mic Playback Volume", SB_DT019X_MIC_DEV, 4, 7);
static struct sbmix_elem snd_dt019x_ctl_pc_speaker_vol =
- SB_SINGLE("PC Speaker Volume", SB_DT019X_SPKR_DEV, 0, 7);
+ SB_SINGLE("Beep Volume", SB_DT019X_SPKR_DEV, 0, 7);
static struct sbmix_elem snd_dt019x_ctl_line_play_vol =
SB_DOUBLE("Line Playback Volume", SB_DT019X_LINE_DEV, SB_DT019X_LINE_DEV, 4,0, 15);
static struct sbmix_elem snd_dt019x_ctl_pcm_play_switch =
diff --git a/sound/isa/sscape.c b/sound/isa/sscape.c
index 66187122377c..e2d5d2d3ed96 100644
--- a/sound/isa/sscape.c
+++ b/sound/isa/sscape.c
@@ -1,5 +1,5 @@
/*
- * Low-level ALSA driver for the ENSONIQ SoundScape PnP
+ * Low-level ALSA driver for the ENSONIQ SoundScape
* Copyright (c) by Chris Rankin
*
* This driver was written in part using information obtained from
@@ -25,31 +25,36 @@
#include <linux/err.h>
#include <linux/isa.h>
#include <linux/delay.h>
+#include <linux/firmware.h>
#include <linux/pnp.h>
#include <linux/spinlock.h>
#include <linux/moduleparam.h>
#include <asm/dma.h>
#include <sound/core.h>
-#include <sound/hwdep.h>
#include <sound/wss.h>
#include <sound/mpu401.h>
#include <sound/initval.h>
-#include <sound/sscape_ioctl.h>
-
MODULE_AUTHOR("Chris Rankin");
-MODULE_DESCRIPTION("ENSONIQ SoundScape PnP driver");
+MODULE_DESCRIPTION("ENSONIQ SoundScape driver");
MODULE_LICENSE("GPL");
-
-static int index[SNDRV_CARDS] __devinitdata = SNDRV_DEFAULT_IDX;
-static char* id[SNDRV_CARDS] __devinitdata = SNDRV_DEFAULT_STR;
-static long port[SNDRV_CARDS] __devinitdata = SNDRV_DEFAULT_PORT;
-static long wss_port[SNDRV_CARDS] __devinitdata = SNDRV_DEFAULT_PORT;
-static int irq[SNDRV_CARDS] __devinitdata = SNDRV_DEFAULT_IRQ;
-static int mpu_irq[SNDRV_CARDS] __devinitdata = SNDRV_DEFAULT_IRQ;
-static int dma[SNDRV_CARDS] __devinitdata = SNDRV_DEFAULT_DMA;
-static int dma2[SNDRV_CARDS] __devinitdata = SNDRV_DEFAULT_DMA;
+MODULE_FIRMWARE("sndscape.co0");
+MODULE_FIRMWARE("sndscape.co1");
+MODULE_FIRMWARE("sndscape.co2");
+MODULE_FIRMWARE("sndscape.co3");
+MODULE_FIRMWARE("sndscape.co4");
+MODULE_FIRMWARE("scope.cod");
+
+static int index[SNDRV_CARDS] = SNDRV_DEFAULT_IDX;
+static char* id[SNDRV_CARDS] = SNDRV_DEFAULT_STR;
+static long port[SNDRV_CARDS] = SNDRV_DEFAULT_PORT;
+static long wss_port[SNDRV_CARDS] = SNDRV_DEFAULT_PORT;
+static int irq[SNDRV_CARDS] = SNDRV_DEFAULT_IRQ;
+static int mpu_irq[SNDRV_CARDS] = SNDRV_DEFAULT_IRQ;
+static int dma[SNDRV_CARDS] = SNDRV_DEFAULT_DMA;
+static int dma2[SNDRV_CARDS] = SNDRV_DEFAULT_DMA;
+static bool joystick[SNDRV_CARDS];
module_param_array(index, int, NULL, 0444);
MODULE_PARM_DESC(index, "Index number for SoundScape soundcard");
@@ -75,6 +80,9 @@ MODULE_PARM_DESC(dma, "DMA # for SoundScape driver.");
module_param_array(dma2, int, NULL, 0444);
MODULE_PARM_DESC(dma2, "DMA2 # for SoundScape driver.");
+module_param_array(joystick, bool, NULL, 0444);
+MODULE_PARM_DESC(joystick, "Enable gameport.");
+
#ifdef CONFIG_PNP
static int isa_registered;
static int pnp_registered;
@@ -101,14 +109,14 @@ MODULE_DEVICE_TABLE(pnp_card, sscape_pnpids);
#define RX_READY 0x01
#define TX_READY 0x02
-#define CMD_ACK 0x80
-#define CMD_SET_MIDI_VOL 0x84
-#define CMD_GET_MIDI_VOL 0x85
-#define CMD_XXX_MIDI_VOL 0x86
-#define CMD_SET_EXTMIDI 0x8a
-#define CMD_GET_EXTMIDI 0x8b
-#define CMD_SET_MT32 0x8c
-#define CMD_GET_MT32 0x8d
+#define CMD_ACK 0x80
+#define CMD_SET_MIDI_VOL 0x84
+#define CMD_GET_MIDI_VOL 0x85
+#define CMD_XXX_MIDI_VOL 0x86
+#define CMD_SET_EXTMIDI 0x8a
+#define CMD_GET_EXTMIDI 0x8b
+#define CMD_SET_MT32 0x8c
+#define CMD_GET_MT32 0x8d
enum GA_REG {
GA_INTSTAT_REG = 0,
@@ -127,7 +135,8 @@ enum GA_REG {
enum card_type {
- SSCAPE,
+ MEDIA_FX, /* Sequoia S-1000 */
+ SSCAPE, /* Sequoia S-2000 */
SSCAPE_PNP,
SSCAPE_VIVO,
};
@@ -140,16 +149,7 @@ struct soundscape {
struct resource *io_res;
struct resource *wss_res;
struct snd_wss *chip;
- struct snd_mpu401 *mpu;
- struct snd_hwdep *hw;
- /*
- * The MIDI device won't work until we've loaded
- * its firmware via a hardware-dependent device IOCTL
- */
- spinlock_t fwlock;
- int hw_in_use;
- unsigned long midi_usage;
unsigned char midi_vol;
};
@@ -161,28 +161,21 @@ static inline struct soundscape *get_card_soundscape(struct snd_card *c)
return (struct soundscape *) (c->private_data);
}
-static inline struct soundscape *get_mpu401_soundscape(struct snd_mpu401 * mpu)
-{
- return (struct soundscape *) (mpu->private_data);
-}
-
-static inline struct soundscape *get_hwdep_soundscape(struct snd_hwdep * hw)
-{
- return (struct soundscape *) (hw->private_data);
-}
-
-
/*
* Allocates some kernel memory that we can use for DMA.
* I think this means that the memory has to map to
* contiguous pages of physical memory.
*/
-static struct snd_dma_buffer *get_dmabuf(struct snd_dma_buffer *buf, unsigned long size)
+static struct snd_dma_buffer *get_dmabuf(struct snd_dma_buffer *buf,
+ unsigned long size)
{
if (buf) {
- if (snd_dma_alloc_pages_fallback(SNDRV_DMA_TYPE_DEV, snd_dma_isa_data(),
+ if (snd_dma_alloc_pages_fallback(SNDRV_DMA_TYPE_DEV,
+ snd_dma_isa_data(),
size, buf) < 0) {
- snd_printk(KERN_ERR "sscape: Failed to allocate %lu bytes for DMA\n", size);
+ snd_printk(KERN_ERR "sscape: Failed to allocate "
+ "%lu bytes for DMA\n",
+ size);
return NULL;
}
}
@@ -199,13 +192,13 @@ static void free_dmabuf(struct snd_dma_buffer *buf)
snd_dma_free_pages(buf);
}
-
/*
* This function writes to the SoundScape's control registers,
* but doesn't do any locking. It's up to the caller to do that.
* This is why this function is "unsafe" ...
*/
-static inline void sscape_write_unsafe(unsigned io_base, enum GA_REG reg, unsigned char val)
+static inline void sscape_write_unsafe(unsigned io_base, enum GA_REG reg,
+ unsigned char val)
{
outb(reg, ODIE_ADDR_IO(io_base));
outb(val, ODIE_DATA_IO(io_base));
@@ -215,7 +208,8 @@ static inline void sscape_write_unsafe(unsigned io_base, enum GA_REG reg, unsign
* Write to the SoundScape's control registers, and do the
* necessary locking ...
*/
-static void sscape_write(struct soundscape *s, enum GA_REG reg, unsigned char val)
+static void sscape_write(struct soundscape *s, enum GA_REG reg,
+ unsigned char val)
{
unsigned long flags;
@@ -228,7 +222,8 @@ static void sscape_write(struct soundscape *s, enum GA_REG reg, unsigned char va
* Read from the SoundScape's control registers, but leave any
* locking to the caller. This is why the function is "unsafe" ...
*/
-static inline unsigned char sscape_read_unsafe(unsigned io_base, enum GA_REG reg)
+static inline unsigned char sscape_read_unsafe(unsigned io_base,
+ enum GA_REG reg)
{
outb(reg, ODIE_ADDR_IO(io_base));
return inb(ODIE_DATA_IO(io_base));
@@ -257,9 +252,8 @@ static inline void set_midi_mode_unsafe(unsigned io_base)
static inline int host_read_unsafe(unsigned io_base)
{
int data = -1;
- if ((inb(HOST_CTRL_IO(io_base)) & RX_READY) != 0) {
+ if ((inb(HOST_CTRL_IO(io_base)) & RX_READY) != 0)
data = inb(HOST_DATA_IO(io_base));
- }
return data;
}
@@ -301,7 +295,7 @@ static inline int host_write_unsafe(unsigned io_base, unsigned char data)
* Also leaves all locking-issues to the caller ...
*/
static int host_write_ctrl_unsafe(unsigned io_base, unsigned char data,
- unsigned timeout)
+ unsigned timeout)
{
int err;
@@ -320,7 +314,7 @@ static int host_write_ctrl_unsafe(unsigned io_base, unsigned char data,
*
* NOTE: This check is based upon observation, not documentation.
*/
-static inline int verify_mpu401(const struct snd_mpu401 * mpu)
+static inline int verify_mpu401(const struct snd_mpu401 *mpu)
{
return ((inb(MPU401C(mpu)) & 0xc0) == 0x80);
}
@@ -328,7 +322,7 @@ static inline int verify_mpu401(const struct snd_mpu401 * mpu)
/*
* This is apparently the standard way to initailise an MPU-401
*/
-static inline void initialise_mpu401(const struct snd_mpu401 * mpu)
+static inline void initialise_mpu401(const struct snd_mpu401 *mpu)
{
outb(0, MPU401D(mpu));
}
@@ -338,9 +332,10 @@ static inline void initialise_mpu401(const struct snd_mpu401 * mpu)
* The AD1845 detection fails if we *don't* do this, so I
* think that this is a good idea ...
*/
-static inline void activate_ad1845_unsafe(unsigned io_base)
+static void activate_ad1845_unsafe(unsigned io_base)
{
- sscape_write_unsafe(io_base, GA_HMCTL_REG, (sscape_read_unsafe(io_base, GA_HMCTL_REG) & 0xcf) | 0x10);
+ unsigned char val = sscape_read_unsafe(io_base, GA_HMCTL_REG);
+ sscape_write_unsafe(io_base, GA_HMCTL_REG, (val & 0xcf) | 0x10);
sscape_write_unsafe(io_base, GA_CDCFG_REG, 0x80);
}
@@ -359,24 +354,27 @@ static void soundscape_free(struct snd_card *c)
* Tell the SoundScape to begin a DMA tranfer using the given channel.
* All locking issues are left to the caller.
*/
-static inline void sscape_start_dma_unsafe(unsigned io_base, enum GA_REG reg)
+static void sscape_start_dma_unsafe(unsigned io_base, enum GA_REG reg)
{
- sscape_write_unsafe(io_base, reg, sscape_read_unsafe(io_base, reg) | 0x01);
- sscape_write_unsafe(io_base, reg, sscape_read_unsafe(io_base, reg) & 0xfe);
+ sscape_write_unsafe(io_base, reg,
+ sscape_read_unsafe(io_base, reg) | 0x01);
+ sscape_write_unsafe(io_base, reg,
+ sscape_read_unsafe(io_base, reg) & 0xfe);
}
/*
* Wait for a DMA transfer to complete. This is a "limited busy-wait",
* and all locking issues are left to the caller.
*/
-static int sscape_wait_dma_unsafe(unsigned io_base, enum GA_REG reg, unsigned timeout)
+static int sscape_wait_dma_unsafe(unsigned io_base, enum GA_REG reg,
+ unsigned timeout)
{
while (!(sscape_read_unsafe(io_base, reg) & 0x01) && (timeout != 0)) {
udelay(100);
--timeout;
} /* while */
- return (sscape_read_unsafe(io_base, reg) & 0x01);
+ return sscape_read_unsafe(io_base, reg) & 0x01;
}
/*
@@ -392,12 +390,12 @@ static int obp_startup_ack(struct soundscape *s, unsigned timeout)
do {
unsigned long flags;
- unsigned char x;
+ int x;
spin_lock_irqsave(&s->lock, flags);
- x = inb(HOST_DATA_IO(s->io_base));
+ x = host_read_unsafe(s->io_base);
spin_unlock_irqrestore(&s->lock, flags);
- if ((x & 0xfe) == 0xfe)
+ if (x == 0xfe || x == 0xff)
return 1;
msleep(10);
@@ -419,10 +417,10 @@ static int host_startup_ack(struct soundscape *s, unsigned timeout)
do {
unsigned long flags;
- unsigned char x;
+ int x;
spin_lock_irqsave(&s->lock, flags);
- x = inb(HOST_DATA_IO(s->io_base));
+ x = host_read_unsafe(s->io_base);
spin_unlock_irqrestore(&s->lock, flags);
if (x == 0xfe)
return 1;
@@ -436,15 +434,15 @@ static int host_startup_ack(struct soundscape *s, unsigned timeout)
/*
* Upload a byte-stream into the SoundScape using DMA channel A.
*/
-static int upload_dma_data(struct soundscape *s,
- const unsigned char __user *data,
- size_t size)
+static int upload_dma_data(struct soundscape *s, const unsigned char *data,
+ size_t size)
{
unsigned long flags;
struct snd_dma_buffer dma;
int ret;
+ unsigned char val;
- if (!get_dmabuf(&dma, PAGE_ALIGN(size)))
+ if (!get_dmabuf(&dma, PAGE_ALIGN(32 * 1024)))
return -ENOMEM;
spin_lock_irqsave(&s->lock, flags);
@@ -452,70 +450,57 @@ static int upload_dma_data(struct soundscape *s,
/*
* Reset the board ...
*/
- sscape_write_unsafe(s->io_base, GA_HMCTL_REG, sscape_read_unsafe(s->io_base, GA_HMCTL_REG) & 0x3f);
+ val = sscape_read_unsafe(s->io_base, GA_HMCTL_REG);
+ sscape_write_unsafe(s->io_base, GA_HMCTL_REG, val & 0x3f);
/*
* Enable the DMA channels and configure them ...
*/
- sscape_write_unsafe(s->io_base, GA_DMACFG_REG, 0x50);
- sscape_write_unsafe(s->io_base, GA_DMAA_REG, (s->chip->dma1 << 4) | DMA_8BIT);
+ val = (s->chip->dma1 << 4) | DMA_8BIT;
+ sscape_write_unsafe(s->io_base, GA_DMAA_REG, val);
sscape_write_unsafe(s->io_base, GA_DMAB_REG, 0x20);
/*
* Take the board out of reset ...
*/
- sscape_write_unsafe(s->io_base, GA_HMCTL_REG, sscape_read_unsafe(s->io_base, GA_HMCTL_REG) | 0x80);
+ val = sscape_read_unsafe(s->io_base, GA_HMCTL_REG);
+ sscape_write_unsafe(s->io_base, GA_HMCTL_REG, val | 0x80);
/*
- * Upload the user's data (firmware?) to the SoundScape
+ * Upload the firmware to the SoundScape
* board through the DMA channel ...
*/
while (size != 0) {
unsigned long len;
- /*
- * Apparently, copying to/from userspace can sleep.
- * We are therefore forbidden from holding any
- * spinlocks while we copy ...
- */
- spin_unlock_irqrestore(&s->lock, flags);
-
- /*
- * Remember that the data that we want to DMA
- * comes from USERSPACE. We have already verified
- * the userspace pointer ...
- */
len = min(size, dma.bytes);
- len -= __copy_from_user(dma.area, data, len);
+ memcpy(dma.area, data, len);
data += len;
size -= len;
- /*
- * Grab that spinlock again, now that we've
- * finished copying!
- */
- spin_lock_irqsave(&s->lock, flags);
-
snd_dma_program(s->chip->dma1, dma.addr, len, DMA_MODE_WRITE);
sscape_start_dma_unsafe(s->io_base, GA_DMAA_REG);
if (!sscape_wait_dma_unsafe(s->io_base, GA_DMAA_REG, 5000)) {
/*
- * Don't forget to release this spinlock we're holding ...
+ * Don't forget to release this spinlock we're holding
*/
spin_unlock_irqrestore(&s->lock, flags);
- snd_printk(KERN_ERR "sscape: DMA upload has timed out\n");
+ snd_printk(KERN_ERR
+ "sscape: DMA upload has timed out\n");
ret = -EAGAIN;
goto _release_dma;
}
} /* while */
set_host_mode_unsafe(s->io_base);
+ outb(0x0, s->io_base);
/*
* Boot the board ... (I think)
*/
- sscape_write_unsafe(s->io_base, GA_HMCTL_REG, sscape_read_unsafe(s->io_base, GA_HMCTL_REG) | 0x40);
+ val = sscape_read_unsafe(s->io_base, GA_HMCTL_REG);
+ sscape_write_unsafe(s->io_base, GA_HMCTL_REG, val | 0x40);
spin_unlock_irqrestore(&s->lock, flags);
/*
@@ -525,10 +510,12 @@ static int upload_dma_data(struct soundscape *s,
*/
ret = 0;
if (!obp_startup_ack(s, 5000)) {
- snd_printk(KERN_ERR "sscape: No response from on-board processor after upload\n");
+ snd_printk(KERN_ERR "sscape: No response "
+ "from on-board processor after upload\n");
ret = -EAGAIN;
} else if (!host_startup_ack(s, 5000)) {
- snd_printk(KERN_ERR "sscape: SoundScape failed to initialise\n");
+ snd_printk(KERN_ERR
+ "sscape: SoundScape failed to initialise\n");
ret = -EAGAIN;
}
@@ -536,7 +523,7 @@ _release_dma:
/*
* NOTE!!! We are NOT holding any spinlocks at this point !!!
*/
- sscape_write(s, GA_DMAA_REG, (s->ic_type == IC_ODIE ? 0x70 : 0x40));
+ sscape_write(s, GA_DMAA_REG, (s->ic_type == IC_OPUS ? 0x40 : 0x70));
free_dmabuf(&dma);
return ret;
@@ -546,167 +533,76 @@ _release_dma:
* Upload the bootblock(?) into the SoundScape. The only
* purpose of this block of code seems to be to tell
* us which version of the microcode we should be using.
- *
- * NOTE: The boot-block data resides in USER-SPACE!!!
- * However, we have already verified its memory
- * addresses by the time we get here.
*/
-static int sscape_upload_bootblock(struct soundscape *sscape, struct sscape_bootblock __user *bb)
+static int sscape_upload_bootblock(struct snd_card *card)
{
+ struct soundscape *sscape = get_card_soundscape(card);
unsigned long flags;
+ const struct firmware *init_fw = NULL;
int data = 0;
int ret;
- ret = upload_dma_data(sscape, bb->code, sizeof(bb->code));
-
- spin_lock_irqsave(&sscape->lock, flags);
- if (ret == 0) {
- data = host_read_ctrl_unsafe(sscape->io_base, 100);
- }
- set_midi_mode_unsafe(sscape->io_base);
- spin_unlock_irqrestore(&sscape->lock, flags);
-
- if (ret == 0) {
- if (data < 0) {
- snd_printk(KERN_ERR "sscape: timeout reading firmware version\n");
- ret = -EAGAIN;
- }
- else if (__copy_to_user(&bb->version, &data, sizeof(bb->version))) {
- ret = -EFAULT;
- }
+ ret = request_firmware(&init_fw, "scope.cod", card->dev);
+ if (ret < 0) {
+ snd_printk(KERN_ERR "sscape: Error loading scope.cod");
+ return ret;
}
+ ret = upload_dma_data(sscape, init_fw->data, init_fw->size);
- return ret;
-}
-
-/*
- * Upload the microcode into the SoundScape. The
- * microcode is 64K of data, and if we try to copy
- * it into a local variable then we will SMASH THE
- * KERNEL'S STACK! We therefore leave it in USER
- * SPACE, and save ourselves from copying it at all.
- */
-static int sscape_upload_microcode(struct soundscape *sscape,
- const struct sscape_microcode __user *mc)
-{
- unsigned long flags;
- char __user *code;
- int err;
+ release_firmware(init_fw);
- /*
- * We are going to have to copy this data into a special
- * DMA-able buffer before we can upload it. We shall therefore
- * just check that the data pointer is valid for now.
- *
- * NOTE: This buffer is 64K long! That's WAY too big to
- * copy into a stack-temporary anyway.
- */
- if ( get_user(code, &mc->code) ||
- !access_ok(VERIFY_READ, code, SSCAPE_MICROCODE_SIZE) )
- return -EFAULT;
+ spin_lock_irqsave(&sscape->lock, flags);
+ if (ret == 0)
+ data = host_read_ctrl_unsafe(sscape->io_base, 100);
- if ((err = upload_dma_data(sscape, code, SSCAPE_MICROCODE_SIZE)) == 0) {
- snd_printk(KERN_INFO "sscape: MIDI firmware loaded\n");
- }
+ if (data & 0x10)
+ sscape_write_unsafe(sscape->io_base, GA_SMCFGA_REG, 0x2f);
- spin_lock_irqsave(&sscape->lock, flags);
- set_midi_mode_unsafe(sscape->io_base);
spin_unlock_irqrestore(&sscape->lock, flags);
- initialise_mpu401(sscape->mpu);
+ data &= 0xf;
+ if (ret == 0 && data > 7) {
+ snd_printk(KERN_ERR
+ "sscape: timeout reading firmware version\n");
+ ret = -EAGAIN;
+ }
- return err;
+ return (ret == 0) ? data : ret;
}
/*
- * Hardware-specific device functions, to implement special
- * IOCTLs for the SoundScape card. This is how we upload
- * the microcode into the card, for example, and so we
- * must ensure that no two processes can open this device
- * simultaneously, and that we can't open it at all if
- * someone is using the MIDI device.
+ * Upload the microcode into the SoundScape.
*/
-static int sscape_hw_open(struct snd_hwdep * hw, struct file *file)
+static int sscape_upload_microcode(struct snd_card *card, int version)
{
- register struct soundscape *sscape = get_hwdep_soundscape(hw);
- unsigned long flags;
+ struct soundscape *sscape = get_card_soundscape(card);
+ const struct firmware *init_fw = NULL;
+ char name[14];
int err;
- spin_lock_irqsave(&sscape->fwlock, flags);
+ snprintf(name, sizeof(name), "sndscape.co%d", version);
- if ((sscape->midi_usage != 0) || sscape->hw_in_use) {
- err = -EBUSY;
- } else {
- sscape->hw_in_use = 1;
- err = 0;
+ err = request_firmware(&init_fw, name, card->dev);
+ if (err < 0) {
+ snd_printk(KERN_ERR "sscape: Error loading sndscape.co%d",
+ version);
+ return err;
}
+ err = upload_dma_data(sscape, init_fw->data, init_fw->size);
+ if (err == 0)
+ snd_printk(KERN_INFO "sscape: MIDI firmware loaded %d KBs\n",
+ init_fw->size >> 10);
- spin_unlock_irqrestore(&sscape->fwlock, flags);
- return err;
-}
-
-static int sscape_hw_release(struct snd_hwdep * hw, struct file *file)
-{
- register struct soundscape *sscape = get_hwdep_soundscape(hw);
- unsigned long flags;
-
- spin_lock_irqsave(&sscape->fwlock, flags);
- sscape->hw_in_use = 0;
- spin_unlock_irqrestore(&sscape->fwlock, flags);
- return 0;
-}
-
-static int sscape_hw_ioctl(struct snd_hwdep * hw, struct file *file,
- unsigned int cmd, unsigned long arg)
-{
- struct soundscape *sscape = get_hwdep_soundscape(hw);
- int err = -EBUSY;
-
- switch (cmd) {
- case SND_SSCAPE_LOAD_BOOTB:
- {
- register struct sscape_bootblock __user *bb = (struct sscape_bootblock __user *) arg;
-
- /*
- * We are going to have to copy this data into a special
- * DMA-able buffer before we can upload it. We shall therefore
- * just check that the data pointer is valid for now ...
- */
- if ( !access_ok(VERIFY_READ, bb->code, sizeof(bb->code)) )
- return -EFAULT;
-
- /*
- * Now check that we can write the firmware version number too...
- */
- if ( !access_ok(VERIFY_WRITE, &bb->version, sizeof(bb->version)) )
- return -EFAULT;
-
- err = sscape_upload_bootblock(sscape, bb);
- }
- break;
-
- case SND_SSCAPE_LOAD_MCODE:
- {
- register const struct sscape_microcode __user *mc = (const struct sscape_microcode __user *) arg;
-
- err = sscape_upload_microcode(sscape, mc);
- }
- break;
-
- default:
- err = -EINVAL;
- break;
- } /* switch */
+ release_firmware(init_fw);
return err;
}
-
/*
* Mixer control for the SoundScape's MIDI device.
*/
static int sscape_midi_info(struct snd_kcontrol *ctl,
- struct snd_ctl_elem_info *uinfo)
+ struct snd_ctl_elem_info *uinfo)
{
uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
uinfo->count = 1;
@@ -716,7 +612,7 @@ static int sscape_midi_info(struct snd_kcontrol *ctl,
}
static int sscape_midi_get(struct snd_kcontrol *kctl,
- struct snd_ctl_elem_value *uctl)
+ struct snd_ctl_elem_value *uctl)
{
struct snd_wss *chip = snd_kcontrol_chip(kctl);
struct snd_card *card = chip->card;
@@ -730,16 +626,18 @@ static int sscape_midi_get(struct snd_kcontrol *kctl,
}
static int sscape_midi_put(struct snd_kcontrol *kctl,
- struct snd_ctl_elem_value *uctl)
+ struct snd_ctl_elem_value *uctl)
{
struct snd_wss *chip = snd_kcontrol_chip(kctl);
struct snd_card *card = chip->card;
- register struct soundscape *s = get_card_soundscape(card);
+ struct soundscape *s = get_card_soundscape(card);
unsigned long flags;
int change;
+ unsigned char new_val;
spin_lock_irqsave(&s->lock, flags);
+ new_val = uctl->value.integer.value[0] & 127;
/*
* We need to put the board into HOST mode before we
* can send any volume-changing HOST commands ...
@@ -752,15 +650,16 @@ static int sscape_midi_put(struct snd_kcontrol *kctl,
* and then perform another volume-related command. Perhaps the
* first command is an "open" and the second command is a "close"?
*/
- if (s->midi_vol == ((unsigned char) uctl->value.integer. value[0] & 127)) {
+ if (s->midi_vol == new_val) {
change = 0;
goto __skip_change;
}
- change = (host_write_ctrl_unsafe(s->io_base, CMD_SET_MIDI_VOL, 100)
- && host_write_ctrl_unsafe(s->io_base, ((unsigned char) uctl->value.integer. value[0]) & 127, 100)
- && host_write_ctrl_unsafe(s->io_base, CMD_XXX_MIDI_VOL, 100));
- s->midi_vol = (unsigned char) uctl->value.integer.value[0] & 127;
- __skip_change:
+ change = host_write_ctrl_unsafe(s->io_base, CMD_SET_MIDI_VOL, 100)
+ && host_write_ctrl_unsafe(s->io_base, new_val, 100)
+ && host_write_ctrl_unsafe(s->io_base, CMD_XXX_MIDI_VOL, 100)
+ && host_write_ctrl_unsafe(s->io_base, new_val, 100);
+ s->midi_vol = new_val;
+__skip_change:
/*
* Take the board out of HOST mode and back into MIDI mode ...
@@ -784,20 +683,25 @@ static struct snd_kcontrol_new midi_mixer_ctl = {
* These IRQs are encoded as bit patterns so that they can be
* written to the control registers.
*/
-static unsigned __devinit get_irq_config(int irq)
+static unsigned __devinit get_irq_config(int sscape_type, int irq)
{
static const int valid_irq[] = { 9, 5, 7, 10 };
+ static const int old_irq[] = { 9, 7, 5, 15 };
unsigned cfg;
- for (cfg = 0; cfg < ARRAY_SIZE(valid_irq); ++cfg) {
- if (irq == valid_irq[cfg])
- return cfg;
- } /* for */
+ if (sscape_type == MEDIA_FX) {
+ for (cfg = 0; cfg < ARRAY_SIZE(old_irq); ++cfg)
+ if (irq == old_irq[cfg])
+ return cfg;
+ } else {
+ for (cfg = 0; cfg < ARRAY_SIZE(valid_irq); ++cfg)
+ if (irq == valid_irq[cfg])
+ return cfg;
+ }
return INVALID_IRQ;
}
-
/*
* Perform certain arcane port-checks to see whether there
* is a SoundScape board lurking behind the given ports.
@@ -842,11 +746,38 @@ static int __devinit detect_sscape(struct soundscape *s, long wss_io)
if (s->type != SSCAPE_VIVO && (d & 0x9f) != 0x0e)
goto _done;
- d = sscape_read_unsafe(s->io_base, GA_HMCTL_REG) & 0x3f;
- sscape_write_unsafe(s->io_base, GA_HMCTL_REG, d | 0xc0);
+ if (s->ic_type == IC_OPUS)
+ activate_ad1845_unsafe(s->io_base);
if (s->type == SSCAPE_VIVO)
wss_io += 4;
+
+ d = sscape_read_unsafe(s->io_base, GA_HMCTL_REG);
+ sscape_write_unsafe(s->io_base, GA_HMCTL_REG, d | 0xc0);
+
+ /* wait for WSS codec */
+ for (d = 0; d < 500; d++) {
+ if ((inb(wss_io) & 0x80) == 0)
+ break;
+ spin_unlock_irqrestore(&s->lock, flags);
+ msleep(1);
+ spin_lock_irqsave(&s->lock, flags);
+ }
+
+ if ((inb(wss_io) & 0x80) != 0)
+ goto _done;
+
+ if (inb(wss_io + 2) == 0xff)
+ goto _done;
+
+ d = sscape_read_unsafe(s->io_base, GA_HMCTL_REG) & 0x3f;
+ sscape_write_unsafe(s->io_base, GA_HMCTL_REG, d);
+
+ if ((inb(wss_io) & 0x80) != 0)
+ s->type = MEDIA_FX;
+
+ d = sscape_read_unsafe(s->io_base, GA_HMCTL_REG);
+ sscape_write_unsafe(s->io_base, GA_HMCTL_REG, d | 0xc0);
/* wait for WSS codec */
for (d = 0; d < 500; d++) {
if ((inb(wss_io) & 0x80) == 0)
@@ -855,14 +786,13 @@ static int __devinit detect_sscape(struct soundscape *s, long wss_io)
msleep(1);
spin_lock_irqsave(&s->lock, flags);
}
- snd_printd(KERN_INFO "init delay = %d ms\n", d);
/*
* SoundScape successfully detected!
*/
retval = 1;
- _done:
+_done:
spin_unlock_irqrestore(&s->lock, flags);
return retval;
}
@@ -873,63 +803,35 @@ static int __devinit detect_sscape(struct soundscape *s, long wss_io)
* to crash the machine. Also check that someone isn't using the hardware
* IOCTL device.
*/
-static int mpu401_open(struct snd_mpu401 * mpu)
+static int mpu401_open(struct snd_mpu401 *mpu)
{
- int err;
-
if (!verify_mpu401(mpu)) {
- snd_printk(KERN_ERR "sscape: MIDI disabled, please load firmware\n");
- err = -ENODEV;
- } else {
- register struct soundscape *sscape = get_mpu401_soundscape(mpu);
- unsigned long flags;
-
- spin_lock_irqsave(&sscape->fwlock, flags);
-
- if (sscape->hw_in_use || (sscape->midi_usage == ULONG_MAX)) {
- err = -EBUSY;
- } else {
- ++(sscape->midi_usage);
- err = 0;
- }
-
- spin_unlock_irqrestore(&sscape->fwlock, flags);
+ snd_printk(KERN_ERR "sscape: MIDI disabled, "
+ "please load firmware\n");
+ return -ENODEV;
}
- return err;
-}
-
-static void mpu401_close(struct snd_mpu401 * mpu)
-{
- register struct soundscape *sscape = get_mpu401_soundscape(mpu);
- unsigned long flags;
-
- spin_lock_irqsave(&sscape->fwlock, flags);
- --(sscape->midi_usage);
- spin_unlock_irqrestore(&sscape->fwlock, flags);
+ return 0;
}
/*
* Initialse an MPU-401 subdevice for MIDI support on the SoundScape.
*/
-static int __devinit create_mpu401(struct snd_card *card, int devnum, unsigned long port, int irq)
+static int __devinit create_mpu401(struct snd_card *card, int devnum,
+ unsigned long port, int irq)
{
struct soundscape *sscape = get_card_soundscape(card);
struct snd_rawmidi *rawmidi;
int err;
- if ((err = snd_mpu401_uart_new(card, devnum,
- MPU401_HW_MPU401,
- port, MPU401_INFO_INTEGRATED,
- irq, IRQF_DISABLED,
- &rawmidi)) == 0) {
- struct snd_mpu401 *mpu = (struct snd_mpu401 *) rawmidi->private_data;
+ err = snd_mpu401_uart_new(card, devnum, MPU401_HW_MPU401, port,
+ MPU401_INFO_INTEGRATED, irq, IRQF_DISABLED,
+ &rawmidi);
+ if (err == 0) {
+ struct snd_mpu401 *mpu = rawmidi->private_data;
mpu->open_input = mpu401_open;
mpu->open_output = mpu401_open;
- mpu->close_input = mpu401_close;
- mpu->close_output = mpu401_close;
mpu->private_data = sscape;
- sscape->mpu = mpu;
initialise_mpu401(mpu);
}
@@ -950,32 +852,34 @@ static int __devinit create_ad1845(struct snd_card *card, unsigned port,
register struct soundscape *sscape = get_card_soundscape(card);
struct snd_wss *chip;
int err;
+ int codec_type = WSS_HW_DETECT;
- if (sscape->type == SSCAPE_VIVO)
- port += 4;
+ switch (sscape->type) {
+ case MEDIA_FX:
+ case SSCAPE:
+ /*
+ * There are some freak examples of early Soundscape cards
+ * with CS4231 instead of AD1848/CS4248. Unfortunately, the
+ * CS4231 works only in CS4248 compatibility mode on
+ * these cards so force it.
+ */
+ if (sscape->ic_type != IC_OPUS)
+ codec_type = WSS_HW_AD1848;
+ break;
- if (dma1 == dma2)
- dma2 = -1;
+ case SSCAPE_VIVO:
+ port += 4;
+ break;
+ default:
+ break;
+ }
err = snd_wss_create(card, port, -1, irq, dma1, dma2,
- WSS_HW_DETECT, WSS_HWSHARE_DMA1, &chip);
+ codec_type, WSS_HWSHARE_DMA1, &chip);
if (!err) {
unsigned long flags;
struct snd_pcm *pcm;
-/*
- * It turns out that the PLAYBACK_ENABLE bit is set
- * by the lowlevel driver ...
- *
-#define AD1845_IFACE_CONFIG \
- (CS4231_AUTOCALIB | CS4231_RECORD_ENABLE | CS4231_PLAYBACK_ENABLE)
- snd_wss_mce_up(chip);
- spin_lock_irqsave(&chip->reg_lock, flags);
- snd_wss_out(chip, CS4231_IFACE_CTRL, AD1845_IFACE_CONFIG);
- spin_unlock_irqrestore(&chip->reg_lock, flags);
- snd_wss_mce_down(chip);
- */
-
if (sscape->type != SSCAPE_VIVO) {
/*
* The input clock frequency on the SoundScape must
@@ -1022,17 +926,10 @@ static int __devinit create_ad1845(struct snd_card *card, unsigned port,
}
}
- strcpy(card->driver, "SoundScape");
- strcpy(card->shortname, pcm->name);
- snprintf(card->longname, sizeof(card->longname),
- "%s at 0x%lx, IRQ %d, DMA1 %d, DMA2 %d\n",
- pcm->name, chip->port, chip->irq,
- chip->dma1, chip->dma2);
-
sscape->chip = chip;
}
- _error:
+_error:
return err;
}
@@ -1051,21 +948,8 @@ static int __devinit create_sscape(int dev, struct snd_card *card)
struct resource *wss_res;
unsigned long flags;
int err;
-
- /*
- * Check that the user didn't pass us garbage data ...
- */
- irq_cfg = get_irq_config(irq[dev]);
- if (irq_cfg == INVALID_IRQ) {
- snd_printk(KERN_ERR "sscape: Invalid IRQ %d\n", irq[dev]);
- return -ENXIO;
- }
-
- mpu_irq_cfg = get_irq_config(mpu_irq[dev]);
- if (mpu_irq_cfg == INVALID_IRQ) {
- printk(KERN_ERR "sscape: Invalid IRQ %d\n", mpu_irq[dev]);
- return -ENXIO;
- }
+ int val;
+ const char *name;
/*
* Grab IO ports that we will need to probe so that we
@@ -1098,41 +982,51 @@ static int __devinit create_sscape(int dev, struct snd_card *card)
}
spin_lock_init(&sscape->lock);
- spin_lock_init(&sscape->fwlock);
sscape->io_res = io_res;
sscape->wss_res = wss_res;
sscape->io_base = port[dev];
if (!detect_sscape(sscape, wss_port[dev])) {
- printk(KERN_ERR "sscape: hardware not detected at 0x%x\n", sscape->io_base);
+ printk(KERN_ERR "sscape: hardware not detected at 0x%x\n",
+ sscape->io_base);
err = -ENODEV;
goto _release_dma;
}
- printk(KERN_INFO "sscape: hardware detected at 0x%x, using IRQ %d, DMA %d\n",
- sscape->io_base, irq[dev], dma[dev]);
+ switch (sscape->type) {
+ case MEDIA_FX:
+ name = "MediaFX/SoundFX";
+ break;
+ case SSCAPE:
+ name = "Soundscape";
+ break;
+ case SSCAPE_PNP:
+ name = "Soundscape PnP";
+ break;
+ case SSCAPE_VIVO:
+ name = "Soundscape VIVO";
+ break;
+ default:
+ name = "unknown Soundscape";
+ break;
+ }
- if (sscape->type != SSCAPE_VIVO) {
- /*
- * Now create the hardware-specific device so that we can
- * load the microcode into the on-board processor.
- * We cannot use the MPU-401 MIDI system until this firmware
- * has been loaded into the card.
- */
- err = snd_hwdep_new(card, "MC68EC000", 0, &(sscape->hw));
- if (err < 0) {
- printk(KERN_ERR "sscape: Failed to create "
- "firmware device\n");
- goto _release_dma;
- }
- strlcpy(sscape->hw->name, "SoundScape M68K",
- sizeof(sscape->hw->name));
- sscape->hw->name[sizeof(sscape->hw->name) - 1] = '\0';
- sscape->hw->iface = SNDRV_HWDEP_IFACE_SSCAPE;
- sscape->hw->ops.open = sscape_hw_open;
- sscape->hw->ops.release = sscape_hw_release;
- sscape->hw->ops.ioctl = sscape_hw_ioctl;
- sscape->hw->private_data = sscape;
+ printk(KERN_INFO "sscape: %s card detected at 0x%x, using IRQ %d, DMA %d\n",
+ name, sscape->io_base, irq[dev], dma[dev]);
+
+ /*
+ * Check that the user didn't pass us garbage data ...
+ */
+ irq_cfg = get_irq_config(sscape->type, irq[dev]);
+ if (irq_cfg == INVALID_IRQ) {
+ snd_printk(KERN_ERR "sscape: Invalid IRQ %d\n", irq[dev]);
+ return -ENXIO;
+ }
+
+ mpu_irq_cfg = get_irq_config(sscape->type, mpu_irq[dev]);
+ if (mpu_irq_cfg == INVALID_IRQ) {
+ snd_printk(KERN_ERR "sscape: Invalid IRQ %d\n", mpu_irq[dev]);
+ return -ENXIO;
}
/*
@@ -1141,9 +1035,6 @@ static int __devinit create_sscape(int dev, struct snd_card *card)
*/
spin_lock_irqsave(&sscape->lock, flags);
- activate_ad1845_unsafe(sscape->io_base);
-
- sscape_write_unsafe(sscape->io_base, GA_INTENA_REG, 0x00); /* disable */
sscape_write_unsafe(sscape->io_base, GA_SMCFGA_REG, 0x2e);
sscape_write_unsafe(sscape->io_base, GA_SMCFGB_REG, 0x00);
@@ -1151,15 +1042,23 @@ static int __devinit create_sscape(int dev, struct snd_card *card)
* Enable and configure the DMA channels ...
*/
sscape_write_unsafe(sscape->io_base, GA_DMACFG_REG, 0x50);
- dma_cfg = (sscape->ic_type == IC_ODIE ? 0x70 : 0x40);
+ dma_cfg = (sscape->ic_type == IC_OPUS ? 0x40 : 0x70);
sscape_write_unsafe(sscape->io_base, GA_DMAA_REG, dma_cfg);
sscape_write_unsafe(sscape->io_base, GA_DMAB_REG, 0x20);
- sscape_write_unsafe(sscape->io_base,
- GA_INTCFG_REG, 0xf0 | (mpu_irq_cfg << 2) | mpu_irq_cfg);
+ mpu_irq_cfg |= mpu_irq_cfg << 2;
+ val = sscape_read_unsafe(sscape->io_base, GA_HMCTL_REG) & 0xF7;
+ if (joystick[dev])
+ val |= 8;
+ sscape_write_unsafe(sscape->io_base, GA_HMCTL_REG, val | 0x10);
+ sscape_write_unsafe(sscape->io_base, GA_INTCFG_REG, 0xf0 | mpu_irq_cfg);
sscape_write_unsafe(sscape->io_base,
GA_CDCFG_REG, 0x09 | DMA_8BIT
| (dma[dev] << 4) | (irq_cfg << 1));
+ /*
+ * Enable the master IRQ ...
+ */
+ sscape_write_unsafe(sscape->io_base, GA_INTENA_REG, 0x80);
spin_unlock_irqrestore(&sscape->lock, flags);
@@ -1170,32 +1069,56 @@ static int __devinit create_sscape(int dev, struct snd_card *card)
err = create_ad1845(card, wss_port[dev], irq[dev],
dma[dev], dma2[dev]);
if (err < 0) {
- printk(KERN_ERR "sscape: No AD1845 device at 0x%lx, IRQ %d\n",
- wss_port[dev], irq[dev]);
+ snd_printk(KERN_ERR
+ "sscape: No AD1845 device at 0x%lx, IRQ %d\n",
+ wss_port[dev], irq[dev]);
goto _release_dma;
}
+ strcpy(card->driver, "SoundScape");
+ strcpy(card->shortname, name);
+ snprintf(card->longname, sizeof(card->longname),
+ "%s at 0x%lx, IRQ %d, DMA1 %d, DMA2 %d\n",
+ name, sscape->chip->port, sscape->chip->irq,
+ sscape->chip->dma1, sscape->chip->dma2);
+
#define MIDI_DEVNUM 0
if (sscape->type != SSCAPE_VIVO) {
- err = create_mpu401(card, MIDI_DEVNUM, port[dev], mpu_irq[dev]);
- if (err < 0) {
- printk(KERN_ERR "sscape: Failed to create "
- "MPU-401 device at 0x%lx\n",
- port[dev]);
- goto _release_dma;
- }
+ err = sscape_upload_bootblock(card);
+ if (err >= 0)
+ err = sscape_upload_microcode(card, err);
- /*
- * Enable the master IRQ ...
- */
- sscape_write(sscape, GA_INTENA_REG, 0x80);
+ if (err == 0) {
+ err = create_mpu401(card, MIDI_DEVNUM, port[dev],
+ mpu_irq[dev]);
+ if (err < 0) {
+ snd_printk(KERN_ERR "sscape: Failed to create "
+ "MPU-401 device at 0x%lx\n",
+ port[dev]);
+ goto _release_dma;
+ }
- /*
- * Initialize mixer
- */
- sscape->midi_vol = 0;
- host_write_ctrl_unsafe(sscape->io_base, CMD_SET_MIDI_VOL, 100);
- host_write_ctrl_unsafe(sscape->io_base, 0, 100);
- host_write_ctrl_unsafe(sscape->io_base, CMD_XXX_MIDI_VOL, 100);
+ /*
+ * Initialize mixer
+ */
+ spin_lock_irqsave(&sscape->lock, flags);
+ sscape->midi_vol = 0;
+ host_write_ctrl_unsafe(sscape->io_base,
+ CMD_SET_MIDI_VOL, 100);
+ host_write_ctrl_unsafe(sscape->io_base,
+ sscape->midi_vol, 100);
+ host_write_ctrl_unsafe(sscape->io_base,
+ CMD_XXX_MIDI_VOL, 100);
+ host_write_ctrl_unsafe(sscape->io_base,
+ sscape->midi_vol, 100);
+ host_write_ctrl_unsafe(sscape->io_base,
+ CMD_SET_EXTMIDI, 100);
+ host_write_ctrl_unsafe(sscape->io_base,
+ 0, 100);
+ host_write_ctrl_unsafe(sscape->io_base, CMD_ACK, 100);
+
+ set_midi_mode_unsafe(sscape->io_base);
+ spin_unlock_irqrestore(&sscape->lock, flags);
+ }
}
/*
@@ -1231,7 +1154,8 @@ static int __devinit snd_sscape_match(struct device *pdev, unsigned int i)
mpu_irq[i] == SNDRV_AUTO_IRQ ||
dma[i] == SNDRV_AUTO_DMA) {
printk(KERN_INFO
- "sscape: insufficient parameters, need IO, IRQ, MPU-IRQ and DMA\n");
+ "sscape: insufficient parameters, "
+ "need IO, IRQ, MPU-IRQ and DMA\n");
return 0;
}
@@ -1253,13 +1177,15 @@ static int __devinit snd_sscape_probe(struct device *pdev, unsigned int dev)
sscape->type = SSCAPE;
dma[dev] &= 0x03;
+ snd_card_set_dev(card, pdev);
+
ret = create_sscape(dev, card);
if (ret < 0)
goto _release_card;
- snd_card_set_dev(card, pdev);
- if ((ret = snd_card_register(card)) < 0) {
- printk(KERN_ERR "sscape: Failed to register sound card\n");
+ ret = snd_card_register(card);
+ if (ret < 0) {
+ snd_printk(KERN_ERR "sscape: Failed to register sound card\n");
goto _release_card;
}
dev_set_drvdata(pdev, card);
@@ -1311,36 +1237,20 @@ static int __devinit sscape_pnp_detect(struct pnp_card_link *pcard,
* Allow this function to fail *quietly* if all the ISA PnP
* devices were configured using module parameters instead.
*/
- if ((idx = get_next_autoindex(idx)) >= SNDRV_CARDS)
+ idx = get_next_autoindex(idx);
+ if (idx >= SNDRV_CARDS)
return -ENOSPC;
/*
- * We have found a candidate ISA PnP card. Now we
- * have to check that it has the devices that we
- * expect it to have.
- *
- * We will NOT try and autoconfigure all of the resources
- * needed and then activate the card as we are assuming that
- * has already been done at boot-time using /proc/isapnp.
- * We shall simply try to give each active card the resources
- * that it wants. This is a sensible strategy for a modular
- * system where unused modules are unloaded regularly.
- *
- * This strategy is utterly useless if we compile the driver
- * into the kernel, of course.
- */
- // printk(KERN_INFO "sscape: %s\n", card->name);
-
- /*
* Check that we still have room for another sound card ...
*/
dev = pnp_request_card_device(pcard, pid->devs[0].id, NULL);
- if (! dev)
+ if (!dev)
return -ENODEV;
if (!pnp_is_active(dev)) {
if (pnp_activate_dev(dev) < 0) {
- printk(KERN_INFO "sscape: device is inactive\n");
+ snd_printk(KERN_INFO "sscape: device is inactive\n");
return -EBUSY;
}
}
@@ -1378,14 +1288,15 @@ static int __devinit sscape_pnp_detect(struct pnp_card_link *pcard,
wss_port[idx] = pnp_port_start(dev, 1);
dma2[idx] = pnp_dma(dev, 1);
}
+ snd_card_set_dev(card, &pcard->card->dev);
ret = create_sscape(idx, card);
if (ret < 0)
goto _release_card;
- snd_card_set_dev(card, &pcard->card->dev);
- if ((ret = snd_card_register(card)) < 0) {
- printk(KERN_ERR "sscape: Failed to register sound card\n");
+ ret = snd_card_register(card);
+ if (ret < 0) {
+ snd_printk(KERN_ERR "sscape: Failed to register sound card\n");
goto _release_card;
}
diff --git a/sound/isa/wss/wss_lib.c b/sound/isa/wss/wss_lib.c
index 5d2ba1b749ab..5b9d6c18bc45 100644
--- a/sound/isa/wss/wss_lib.c
+++ b/sound/isa/wss/wss_lib.c
@@ -1682,7 +1682,7 @@ static void snd_wss_resume(struct snd_wss *chip)
}
#endif /* CONFIG_PM */
-int snd_wss_free(struct snd_wss *chip)
+static int snd_wss_free(struct snd_wss *chip)
{
release_and_free_resource(chip->res_port);
release_and_free_resource(chip->res_cport);
@@ -1705,7 +1705,6 @@ int snd_wss_free(struct snd_wss *chip)
kfree(chip);
return 0;
}
-EXPORT_SYMBOL(snd_wss_free);
static int snd_wss_dev_free(struct snd_device *device)
{
@@ -2198,84 +2197,61 @@ EXPORT_SYMBOL(snd_wss_put_double);
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 const DECLARE_TLV_DB_SCALE(db_scale_4bit, -4500, 300, 0);
-static struct snd_kcontrol_new snd_ad1848_controls[] = {
-WSS_DOUBLE("PCM Playback Switch", 0, CS4231_LEFT_OUTPUT, CS4231_RIGHT_OUTPUT,
- 7, 7, 1, 1),
+static struct snd_kcontrol_new snd_wss_controls[] = {
+WSS_DOUBLE("PCM Playback Switch", 0,
+ CS4231_LEFT_OUTPUT, CS4231_RIGHT_OUTPUT, 7, 7, 1, 1),
WSS_DOUBLE_TLV("PCM Playback Volume", 0,
- CS4231_LEFT_OUTPUT, CS4231_RIGHT_OUTPUT, 0, 0, 63, 1,
- db_scale_6bit),
+ CS4231_LEFT_OUTPUT, CS4231_RIGHT_OUTPUT, 0, 0, 63, 1,
+ db_scale_6bit),
WSS_DOUBLE("Aux Playback Switch", 0,
- CS4231_AUX1_LEFT_INPUT, CS4231_AUX1_RIGHT_INPUT, 7, 7, 1, 1),
+ CS4231_AUX1_LEFT_INPUT, CS4231_AUX1_RIGHT_INPUT, 7, 7, 1, 1),
WSS_DOUBLE_TLV("Aux Playback Volume", 0,
- CS4231_AUX1_LEFT_INPUT, CS4231_AUX1_RIGHT_INPUT, 0, 0, 31, 1,
- db_scale_5bit_12db_max),
+ CS4231_AUX1_LEFT_INPUT, CS4231_AUX1_RIGHT_INPUT, 0, 0, 31, 1,
+ db_scale_5bit_12db_max),
WSS_DOUBLE("Aux Playback Switch", 1,
- CS4231_AUX2_LEFT_INPUT, CS4231_AUX2_RIGHT_INPUT, 7, 7, 1, 1),
+ CS4231_AUX2_LEFT_INPUT, CS4231_AUX2_RIGHT_INPUT, 7, 7, 1, 1),
WSS_DOUBLE_TLV("Aux Playback Volume", 1,
- CS4231_AUX2_LEFT_INPUT, CS4231_AUX2_RIGHT_INPUT, 0, 0, 31, 1,
- db_scale_5bit_12db_max),
+ CS4231_AUX2_LEFT_INPUT, CS4231_AUX2_RIGHT_INPUT, 0, 0, 31, 1,
+ db_scale_5bit_12db_max),
WSS_DOUBLE_TLV("Capture Volume", 0, CS4231_LEFT_INPUT, CS4231_RIGHT_INPUT,
0, 0, 15, 0, db_scale_rec_gain),
{
- .name = "Capture Source",
.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+ .name = "Capture Source",
.info = snd_wss_info_mux,
.get = snd_wss_get_mux,
.put = snd_wss_put_mux,
},
-WSS_SINGLE("Loopback Capture Switch", 0, CS4231_LOOPBACK, 0, 1, 0),
-WSS_SINGLE_TLV("Loopback Capture Volume", 0, CS4231_LOOPBACK, 1, 63, 0,
- db_scale_6bit),
-};
-
-static struct snd_kcontrol_new snd_wss_controls[] = {
-WSS_DOUBLE("PCM Playback Switch", 0,
- CS4231_LEFT_OUTPUT, CS4231_RIGHT_OUTPUT, 7, 7, 1, 1),
-WSS_DOUBLE("PCM Playback Volume", 0,
- CS4231_LEFT_OUTPUT, CS4231_RIGHT_OUTPUT, 0, 0, 63, 1),
+WSS_DOUBLE("Mic Boost (+20dB)", 0,
+ CS4231_LEFT_INPUT, CS4231_RIGHT_INPUT, 5, 5, 1, 0),
+WSS_SINGLE("Loopback Capture Switch", 0,
+ CS4231_LOOPBACK, 0, 1, 0),
+WSS_SINGLE_TLV("Loopback Capture Volume", 0, CS4231_LOOPBACK, 2, 63, 1,
+ db_scale_6bit),
WSS_DOUBLE("Line Playback Switch", 0,
CS4231_LEFT_LINE_IN, CS4231_RIGHT_LINE_IN, 7, 7, 1, 1),
-WSS_DOUBLE("Line Playback Volume", 0,
- CS4231_LEFT_LINE_IN, CS4231_RIGHT_LINE_IN, 0, 0, 31, 1),
-WSS_DOUBLE("Aux Playback Switch", 0,
- CS4231_AUX1_LEFT_INPUT, CS4231_AUX1_RIGHT_INPUT, 7, 7, 1, 1),
-WSS_DOUBLE("Aux Playback Volume", 0,
- CS4231_AUX1_LEFT_INPUT, CS4231_AUX1_RIGHT_INPUT, 0, 0, 31, 1),
-WSS_DOUBLE("Aux Playback Switch", 1,
- CS4231_AUX2_LEFT_INPUT, CS4231_AUX2_RIGHT_INPUT, 7, 7, 1, 1),
-WSS_DOUBLE("Aux Playback Volume", 1,
- CS4231_AUX2_LEFT_INPUT, CS4231_AUX2_RIGHT_INPUT, 0, 0, 31, 1),
-WSS_SINGLE("Mono Playback Switch", 0,
+WSS_DOUBLE_TLV("Line Playback Volume", 0,
+ CS4231_LEFT_LINE_IN, CS4231_RIGHT_LINE_IN, 0, 0, 31, 1,
+ db_scale_5bit_12db_max),
+WSS_SINGLE("Beep Playback Switch", 0,
CS4231_MONO_CTRL, 7, 1, 1),
-WSS_SINGLE("Mono Playback Volume", 0,
- CS4231_MONO_CTRL, 0, 15, 1),
+WSS_SINGLE_TLV("Beep Playback Volume", 0,
+ CS4231_MONO_CTRL, 0, 15, 1,
+ db_scale_4bit),
WSS_SINGLE("Mono Output Playback Switch", 0,
CS4231_MONO_CTRL, 6, 1, 1),
-WSS_SINGLE("Mono Output Playback Bypass", 0,
+WSS_SINGLE("Beep Bypass Playback Switch", 0,
CS4231_MONO_CTRL, 5, 1, 0),
-WSS_DOUBLE("Capture Volume", 0,
- CS4231_LEFT_INPUT, CS4231_RIGHT_INPUT, 0, 0, 15, 0),
-{
- .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
- .name = "Capture Source",
- .info = snd_wss_info_mux,
- .get = snd_wss_get_mux,
- .put = snd_wss_put_mux,
-},
-WSS_DOUBLE("Mic Boost", 0,
- CS4231_LEFT_INPUT, CS4231_RIGHT_INPUT, 5, 5, 1, 0),
-WSS_SINGLE("Loopback Capture Switch", 0,
- CS4231_LOOPBACK, 0, 1, 0),
-WSS_SINGLE("Loopback Capture Volume", 0,
- CS4231_LOOPBACK, 2, 63, 1)
};
static struct snd_kcontrol_new snd_opti93x_controls[] = {
WSS_DOUBLE("Master Playback Switch", 0,
OPTi93X_OUT_LEFT, OPTi93X_OUT_RIGHT, 7, 7, 1, 1),
-WSS_DOUBLE("Master Playback Volume", 0,
- OPTi93X_OUT_LEFT, OPTi93X_OUT_RIGHT, 1, 1, 31, 1),
+WSS_DOUBLE_TLV("Master Playback Volume", 0,
+ OPTi93X_OUT_LEFT, OPTi93X_OUT_RIGHT, 1, 1, 31, 1,
+ db_scale_6bit),
WSS_DOUBLE("PCM Playback Switch", 0,
CS4231_LEFT_OUTPUT, CS4231_RIGHT_OUTPUT, 7, 7, 1, 1),
WSS_DOUBLE("PCM Playback Volume", 0,
@@ -2334,22 +2310,21 @@ int snd_wss_mixer(struct snd_wss *chip)
if (err < 0)
return err;
}
- else if (chip->hardware & WSS_HW_AD1848_MASK)
- for (idx = 0; idx < ARRAY_SIZE(snd_ad1848_controls); idx++) {
- err = snd_ctl_add(card,
- snd_ctl_new1(&snd_ad1848_controls[idx],
- chip));
- if (err < 0)
- return err;
- }
- else
- for (idx = 0; idx < ARRAY_SIZE(snd_wss_controls); idx++) {
+ else {
+ int count = ARRAY_SIZE(snd_wss_controls);
+
+ /* Use only the first 11 entries on AD1848 */
+ if (chip->hardware & WSS_HW_AD1848_MASK)
+ count = 11;
+
+ for (idx = 0; idx < count; idx++) {
err = snd_ctl_add(card,
snd_ctl_new1(&snd_wss_controls[idx],
chip));
if (err < 0)
return err;
}
+ }
return 0;
}
EXPORT_SYMBOL(snd_wss_mixer);
diff --git a/sound/oss/Kconfig b/sound/oss/Kconfig
index bcf2a0698d54..135a2b77cc4a 100644
--- a/sound/oss/Kconfig
+++ b/sound/oss/Kconfig
@@ -287,18 +287,6 @@ config SOUND_DMAP
Say Y unless you have 16MB or more RAM or a PCI sound card.
-config SOUND_SSCAPE
- tristate "Ensoniq SoundScape support"
- help
- Answer Y if you have a sound card based on the Ensoniq SoundScape
- chipset. Such cards are being manufactured at least by Ensoniq, Spea
- and Reveal (Reveal makes also other cards).
-
- If you compile the driver into the kernel, you have to add
- "sscape=<io>,<irq>,<dma>,<mpuio>,<mpuirq>" to the kernel command
- line.
-
-
config SOUND_VMIDI
tristate "Loopback MIDI device support"
help
diff --git a/sound/oss/Makefile b/sound/oss/Makefile
index e0ae4d4d6a5c..567b8a74178a 100644
--- a/sound/oss/Makefile
+++ b/sound/oss/Makefile
@@ -13,7 +13,6 @@ obj-$(CONFIG_SOUND_SH_DAC_AUDIO) += sh_dac_audio.o
obj-$(CONFIG_SOUND_AEDSP16) += aedsp16.o
obj-$(CONFIG_SOUND_PSS) += pss.o ad1848.o mpu401.o
obj-$(CONFIG_SOUND_TRIX) += trix.o ad1848.o sb_lib.o uart401.o
-obj-$(CONFIG_SOUND_SSCAPE) += sscape.o ad1848.o mpu401.o
obj-$(CONFIG_SOUND_MSS) += ad1848.o
obj-$(CONFIG_SOUND_PAS) += pas2.o sb.o sb_lib.o uart401.o
obj-$(CONFIG_SOUND_SB) += sb.o sb_lib.o uart401.o
diff --git a/sound/oss/audio.c b/sound/oss/audio.c
index b69c05b7ea7b..7df48a25c4ee 100644
--- a/sound/oss/audio.c
+++ b/sound/oss/audio.c
@@ -838,7 +838,7 @@ static int dma_ioctl(int dev, unsigned int cmd, void __user *arg)
if ((err = audio_devs[dev]->d->prepare_for_input(dev,
dmap_in->fragment_size, dmap_in->nbufs)) < 0) {
spin_unlock_irqrestore(&dmap_in->lock,flags);
- return -err;
+ return err;
}
dmap_in->dma_mode = DMODE_INPUT;
audio_devs[dev]->enable_bits |= PCM_ENABLE_INPUT;
diff --git a/sound/oss/midi_synth.c b/sound/oss/midi_synth.c
index 9e450988ed36..3bc7104c5379 100644
--- a/sound/oss/midi_synth.c
+++ b/sound/oss/midi_synth.c
@@ -426,7 +426,7 @@ midi_synth_open(int dev, int mode)
int err;
struct midi_input_info *inc;
- if (orig_dev < 0 || orig_dev > num_midis || midi_devs[orig_dev] == NULL)
+ if (orig_dev < 0 || orig_dev >= num_midis || midi_devs[orig_dev] == NULL)
return -ENXIO;
midi2synth[orig_dev] = dev;
diff --git a/sound/oss/mpu401.c b/sound/oss/mpu401.c
index 734b8f9e2f78..0af9d24feb8f 100644
--- a/sound/oss/mpu401.c
+++ b/sound/oss/mpu401.c
@@ -770,7 +770,7 @@ static int mpu_synth_ioctl(int dev, unsigned int cmd, void __user *arg)
midi_dev = synth_devs[dev]->midi_dev;
- if (midi_dev < 0 || midi_dev > num_midis || midi_devs[midi_dev] == NULL)
+ if (midi_dev < 0 || midi_dev >= num_midis || midi_devs[midi_dev] == NULL)
return -ENXIO;
devc = &dev_conf[midi_dev];
diff --git a/sound/oss/sh_dac_audio.c b/sound/oss/sh_dac_audio.c
index b2ed8757542a..4153752507e3 100644
--- a/sound/oss/sh_dac_audio.c
+++ b/sound/oss/sh_dac_audio.c
@@ -164,9 +164,6 @@ static ssize_t dac_audio_write(struct file *file, const char *buf, size_t count,
int free;
int nbytes;
- if (count < 0)
- return -EINVAL;
-
if (!count) {
dac_audio_sync();
return 0;
diff --git a/sound/oss/sscape.c b/sound/oss/sscape.c
deleted file mode 100644
index 30c36d1f35d7..000000000000
--- a/sound/oss/sscape.c
+++ /dev/null
@@ -1,1480 +0,0 @@
-/*
- * sound/oss/sscape.c
- *
- * Low level driver for Ensoniq SoundScape
- *
- *
- * Copyright (C) by Hannu Savolainen 1993-1997
- *
- * OSS/Free for Linux is distributed under the GNU GENERAL PUBLIC LICENSE (GPL)
- * Version 2 (June 1991). See the "COPYING" file distributed with this software
- * for more info.
- *
- *
- * Thomas Sailer : ioctl code reworked (vmalloc/vfree removed)
- * Sergey Smitienko : ensoniq p'n'p support
- * Christoph Hellwig : adapted to module_init/module_exit
- * Bartlomiej Zolnierkiewicz : added __init to attach_sscape()
- * Chris Rankin : Specify that this module owns the coprocessor
- * Arnaldo C. de Melo : added missing restore_flags in sscape_pnp_upload_file
- */
-
-#include <linux/init.h>
-#include <linux/module.h>
-
-#include "sound_config.h"
-#include "sound_firmware.h"
-
-#include <linux/types.h>
-#include <linux/errno.h>
-#include <linux/signal.h>
-#include <linux/fcntl.h>
-#include <linux/ctype.h>
-#include <linux/stddef.h>
-#include <linux/kmod.h>
-#include <asm/dma.h>
-#include <asm/io.h>
-#include <linux/wait.h>
-#include <linux/slab.h>
-#include <linux/ioport.h>
-#include <linux/delay.h>
-#include <linux/proc_fs.h>
-#include <linux/mm.h>
-#include <linux/spinlock.h>
-
-#include "coproc.h"
-
-#include "ad1848.h"
-#include "mpu401.h"
-
-/*
- * I/O ports
- */
-#define MIDI_DATA 0
-#define MIDI_CTRL 1
-#define HOST_CTRL 2
-#define TX_READY 0x02
-#define RX_READY 0x01
-#define HOST_DATA 3
-#define ODIE_ADDR 4
-#define ODIE_DATA 5
-
-/*
- * Indirect registers
- */
-
-#define GA_INTSTAT_REG 0
-#define GA_INTENA_REG 1
-#define GA_DMAA_REG 2
-#define GA_DMAB_REG 3
-#define GA_INTCFG_REG 4
-#define GA_DMACFG_REG 5
-#define GA_CDCFG_REG 6
-#define GA_SMCFGA_REG 7
-#define GA_SMCFGB_REG 8
-#define GA_HMCTL_REG 9
-
-/*
- * DMA channel identifiers (A and B)
- */
-
-#define SSCAPE_DMA_A 0
-#define SSCAPE_DMA_B 1
-
-#define PORT(name) (devc->base+name)
-
-/*
- * Host commands recognized by the OBP microcode
- */
-
-#define CMD_GEN_HOST_ACK 0x80
-#define CMD_GEN_MPU_ACK 0x81
-#define CMD_GET_BOARD_TYPE 0x82
-#define CMD_SET_CONTROL 0x88 /* Old firmware only */
-#define CMD_GET_CONTROL 0x89 /* Old firmware only */
-#define CTL_MASTER_VOL 0
-#define CTL_MIC_MODE 2
-#define CTL_SYNTH_VOL 4
-#define CTL_WAVE_VOL 7
-#define CMD_SET_EXTMIDI 0x8a
-#define CMD_GET_EXTMIDI 0x8b
-#define CMD_SET_MT32 0x8c
-#define CMD_GET_MT32 0x8d
-
-#define CMD_ACK 0x80
-
-#define IC_ODIE 1
-#define IC_OPUS 2
-
-typedef struct sscape_info
-{
- int base, irq, dma;
-
- int codec, codec_irq; /* required to setup pnp cards*/
- int codec_type;
- int ic_type;
- char* raw_buf;
- unsigned long raw_buf_phys;
- int buffsize; /* -------------------------- */
- spinlock_t lock;
- int ok; /* Properly detected */
- int failed;
- int dma_allocated;
- int codec_audiodev;
- int opened;
- int *osp;
- int my_audiodev;
-} sscape_info;
-
-static struct sscape_info adev_info = {
- 0
-};
-
-static struct sscape_info *devc = &adev_info;
-static int sscape_mididev = -1;
-
-/* Some older cards have assigned interrupt bits differently than new ones */
-static char valid_interrupts_old[] = {
- 9, 7, 5, 15
-};
-
-static char valid_interrupts_new[] = {
- 9, 5, 7, 10
-};
-
-static char *valid_interrupts = valid_interrupts_new;
-
-/*
- * See the bottom of the driver. This can be set by spea =0/1.
- */
-
-#ifdef REVEAL_SPEA
-static char old_hardware = 1;
-#else
-static char old_hardware;
-#endif
-
-static void sleep(unsigned howlong)
-{
- current->state = TASK_INTERRUPTIBLE;
- schedule_timeout(howlong);
-}
-
-static unsigned char sscape_read(struct sscape_info *devc, int reg)
-{
- unsigned long flags;
- unsigned char val;
-
- spin_lock_irqsave(&devc->lock,flags);
- outb(reg, PORT(ODIE_ADDR));
- val = inb(PORT(ODIE_DATA));
- spin_unlock_irqrestore(&devc->lock,flags);
- return val;
-}
-
-static void __sscape_write(int reg, int data)
-{
- outb(reg, PORT(ODIE_ADDR));
- outb(data, PORT(ODIE_DATA));
-}
-
-static void sscape_write(struct sscape_info *devc, int reg, int data)
-{
- unsigned long flags;
-
- spin_lock_irqsave(&devc->lock,flags);
- __sscape_write(reg, data);
- spin_unlock_irqrestore(&devc->lock,flags);
-}
-
-static unsigned char sscape_pnp_read_codec(sscape_info* devc, unsigned char reg)
-{
- unsigned char res;
- unsigned long flags;
-
- spin_lock_irqsave(&devc->lock,flags);
- outb( reg, devc -> codec);
- res = inb (devc -> codec + 1);
- spin_unlock_irqrestore(&devc->lock,flags);
- return res;
-
-}
-
-static void sscape_pnp_write_codec(sscape_info* devc, unsigned char reg, unsigned char data)
-{
- unsigned long flags;
-
- spin_lock_irqsave(&devc->lock,flags);
- outb( reg, devc -> codec);
- outb( data, devc -> codec + 1);
- spin_unlock_irqrestore(&devc->lock,flags);
-}
-
-static void host_open(struct sscape_info *devc)
-{
- outb((0x00), PORT(HOST_CTRL)); /* Put the board to the host mode */
-}
-
-static void host_close(struct sscape_info *devc)
-{
- outb((0x03), PORT(HOST_CTRL)); /* Put the board to the MIDI mode */
-}
-
-static int host_write(struct sscape_info *devc, unsigned char *data, int count)
-{
- unsigned long flags;
- int i, timeout_val;
-
- spin_lock_irqsave(&devc->lock,flags);
- /*
- * Send the command and data bytes
- */
-
- for (i = 0; i < count; i++)
- {
- for (timeout_val = 10000; timeout_val > 0; timeout_val--)
- if (inb(PORT(HOST_CTRL)) & TX_READY)
- break;
-
- if (timeout_val <= 0)
- {
- spin_unlock_irqrestore(&devc->lock,flags);
- return 0;
- }
- outb(data[i], PORT(HOST_DATA));
- }
- spin_unlock_irqrestore(&devc->lock,flags);
- return 1;
-}
-
-static int host_read(struct sscape_info *devc)
-{
- unsigned long flags;
- int timeout_val;
- unsigned char data;
-
- spin_lock_irqsave(&devc->lock,flags);
- /*
- * Read a byte
- */
-
- for (timeout_val = 10000; timeout_val > 0; timeout_val--)
- if (inb(PORT(HOST_CTRL)) & RX_READY)
- break;
-
- if (timeout_val <= 0)
- {
- spin_unlock_irqrestore(&devc->lock,flags);
- return -1;
- }
- data = inb(PORT(HOST_DATA));
- spin_unlock_irqrestore(&devc->lock,flags);
- return data;
-}
-
-#if 0 /* unused */
-static int host_command1(struct sscape_info *devc, int cmd)
-{
- unsigned char buf[10];
- buf[0] = (unsigned char) (cmd & 0xff);
- return host_write(devc, buf, 1);
-}
-#endif /* unused */
-
-
-static int host_command2(struct sscape_info *devc, int cmd, int parm1)
-{
- unsigned char buf[10];
-
- buf[0] = (unsigned char) (cmd & 0xff);
- buf[1] = (unsigned char) (parm1 & 0xff);
-
- return host_write(devc, buf, 2);
-}
-
-static int host_command3(struct sscape_info *devc, int cmd, int parm1, int parm2)
-{
- unsigned char buf[10];
-
- buf[0] = (unsigned char) (cmd & 0xff);
- buf[1] = (unsigned char) (parm1 & 0xff);
- buf[2] = (unsigned char) (parm2 & 0xff);
- return host_write(devc, buf, 3);
-}
-
-static void set_mt32(struct sscape_info *devc, int value)
-{
- host_open(devc);
- host_command2(devc, CMD_SET_MT32, value ? 1 : 0);
- if (host_read(devc) != CMD_ACK)
- {
- /* printk( "SNDSCAPE: Setting MT32 mode failed\n"); */
- }
- host_close(devc);
-}
-
-static void set_control(struct sscape_info *devc, int ctrl, int value)
-{
- host_open(devc);
- host_command3(devc, CMD_SET_CONTROL, ctrl, value);
- if (host_read(devc) != CMD_ACK)
- {
- /* printk( "SNDSCAPE: Setting control (%d) failed\n", ctrl); */
- }
- host_close(devc);
-}
-
-static void do_dma(struct sscape_info *devc, int dma_chan, unsigned long buf, int blk_size, int mode)
-{
- unsigned char temp;
-
- if (dma_chan != SSCAPE_DMA_A)
- {
- printk(KERN_WARNING "soundscape: Tried to use DMA channel != A. Why?\n");
- return;
- }
- audio_devs[devc->codec_audiodev]->flags &= ~DMA_AUTOMODE;
- DMAbuf_start_dma(devc->codec_audiodev, buf, blk_size, mode);
- audio_devs[devc->codec_audiodev]->flags |= DMA_AUTOMODE;
-
- temp = devc->dma << 4; /* Setup DMA channel select bits */
- if (devc->dma <= 3)
- temp |= 0x80; /* 8 bit DMA channel */
-
- temp |= 1; /* Trigger DMA */
- sscape_write(devc, GA_DMAA_REG, temp);
- temp &= 0xfe; /* Clear DMA trigger */
- sscape_write(devc, GA_DMAA_REG, temp);
-}
-
-static int verify_mpu(struct sscape_info *devc)
-{
- /*
- * The SoundScape board could be in three modes (MPU, 8250 and host).
- * If the card is not in the MPU mode, enabling the MPU driver will
- * cause infinite loop (the driver believes that there is always some
- * received data in the buffer.
- *
- * Detect this by looking if there are more than 10 received MIDI bytes
- * (0x00) in the buffer.
- */
-
- int i;
-
- for (i = 0; i < 10; i++)
- {
- if (inb(devc->base + HOST_CTRL) & 0x80)
- return 1;
-
- if (inb(devc->base) != 0x00)
- return 1;
- }
- printk(KERN_WARNING "SoundScape: The device is not in the MPU-401 mode\n");
- return 0;
-}
-
-static int sscape_coproc_open(void *dev_info, int sub_device)
-{
- if (sub_device == COPR_MIDI)
- {
- set_mt32(devc, 0);
- if (!verify_mpu(devc))
- return -EIO;
- }
- return 0;
-}
-
-static void sscape_coproc_close(void *dev_info, int sub_device)
-{
- struct sscape_info *devc = dev_info;
- unsigned long flags;
-
- spin_lock_irqsave(&devc->lock,flags);
- if (devc->dma_allocated)
- {
- __sscape_write(GA_DMAA_REG, 0x20); /* DMA channel disabled */
- devc->dma_allocated = 0;
- }
- spin_unlock_irqrestore(&devc->lock,flags);
- return;
-}
-
-static void sscape_coproc_reset(void *dev_info)
-{
-}
-
-static int sscape_download_boot(struct sscape_info *devc, unsigned char *block, int size, int flag)
-{
- unsigned long flags;
- unsigned char temp;
- volatile int done, timeout_val;
- static unsigned char codec_dma_bits;
-
- if (flag & CPF_FIRST)
- {
- /*
- * First block. Have to allocate DMA and to reset the board
- * before continuing.
- */
-
- spin_lock_irqsave(&devc->lock,flags);
- codec_dma_bits = sscape_read(devc, GA_CDCFG_REG);
-
- if (devc->dma_allocated == 0)
- devc->dma_allocated = 1;
-
- spin_unlock_irqrestore(&devc->lock,flags);
-
- sscape_write(devc, GA_HMCTL_REG,
- (temp = sscape_read(devc, GA_HMCTL_REG)) & 0x3f); /*Reset */
-
- for (timeout_val = 10000; timeout_val > 0; timeout_val--)
- sscape_read(devc, GA_HMCTL_REG); /* Delay */
-
- /* Take board out of reset */
- sscape_write(devc, GA_HMCTL_REG,
- (temp = sscape_read(devc, GA_HMCTL_REG)) | 0x80);
- }
- /*
- * Transfer one code block using DMA
- */
- if (audio_devs[devc->codec_audiodev]->dmap_out->raw_buf == NULL)
- {
- printk(KERN_WARNING "soundscape: DMA buffer not available\n");
- return 0;
- }
- memcpy(audio_devs[devc->codec_audiodev]->dmap_out->raw_buf, block, size);
-
- spin_lock_irqsave(&devc->lock,flags);
-
- /******** INTERRUPTS DISABLED NOW ********/
-
- do_dma(devc, SSCAPE_DMA_A,
- audio_devs[devc->codec_audiodev]->dmap_out->raw_buf_phys,
- size, DMA_MODE_WRITE);
-
- /*
- * Wait until transfer completes.
- */
-
- done = 0;
- timeout_val = 30;
- while (!done && timeout_val-- > 0)
- {
- int resid;
-
- if (HZ / 50)
- sleep(HZ / 50);
- clear_dma_ff(devc->dma);
- if ((resid = get_dma_residue(devc->dma)) == 0)
- done = 1;
- }
-
- spin_unlock_irqrestore(&devc->lock,flags);
- if (!done)
- return 0;
-
- if (flag & CPF_LAST)
- {
- /*
- * Take the board out of reset
- */
- outb((0x00), PORT(HOST_CTRL));
- outb((0x00), PORT(MIDI_CTRL));
-
- temp = sscape_read(devc, GA_HMCTL_REG);
- temp |= 0x40;
- sscape_write(devc, GA_HMCTL_REG, temp); /* Kickstart the board */
-
- /*
- * Wait until the ODB wakes up
- */
- spin_lock_irqsave(&devc->lock,flags);
- done = 0;
- timeout_val = 5 * HZ;
- while (!done && timeout_val-- > 0)
- {
- unsigned char x;
-
- sleep(1);
- x = inb(PORT(HOST_DATA));
- if (x == 0xff || x == 0xfe) /* OBP startup acknowledge */
- {
- DDB(printk("Soundscape: Acknowledge = %x\n", x));
- done = 1;
- }
- }
- sscape_write(devc, GA_CDCFG_REG, codec_dma_bits);
-
- spin_unlock_irqrestore(&devc->lock,flags);
- if (!done)
- {
- printk(KERN_ERR "soundscape: The OBP didn't respond after code download\n");
- return 0;
- }
- spin_lock_irqsave(&devc->lock,flags);
- done = 0;
- timeout_val = 5 * HZ;
- while (!done && timeout_val-- > 0)
- {
- sleep(1);
- if (inb(PORT(HOST_DATA)) == 0xfe) /* Host startup acknowledge */
- done = 1;
- }
- spin_unlock_irqrestore(&devc->lock,flags);
- if (!done)
- {
- printk(KERN_ERR "soundscape: OBP Initialization failed.\n");
- return 0;
- }
- printk(KERN_INFO "SoundScape board initialized OK\n");
- set_control(devc, CTL_MASTER_VOL, 100);
- set_control(devc, CTL_SYNTH_VOL, 100);
-
-#ifdef SSCAPE_DEBUG3
- /*
- * Temporary debugging aid. Print contents of the registers after
- * downloading the code.
- */
- {
- int i;
-
- for (i = 0; i < 13; i++)
- printk("I%d = %02x (new value)\n", i, sscape_read(devc, i));
- }
-#endif
-
- }
- return 1;
-}
-
-static int download_boot_block(void *dev_info, copr_buffer * buf)
-{
- if (buf->len <= 0 || buf->len > sizeof(buf->data))
- return -EINVAL;
-
- if (!sscape_download_boot(devc, buf->data, buf->len, buf->flags))
- {
- printk(KERN_ERR "soundscape: Unable to load microcode block to the OBP.\n");
- return -EIO;
- }
- return 0;
-}
-
-static int sscape_coproc_ioctl(void *dev_info, unsigned int cmd, void __user *arg, int local)
-{
- copr_buffer *buf;
- int err;
-
- switch (cmd)
- {
- case SNDCTL_COPR_RESET:
- sscape_coproc_reset(dev_info);
- return 0;
-
- case SNDCTL_COPR_LOAD:
- buf = (copr_buffer *) vmalloc(sizeof(copr_buffer));
- if (buf == NULL)
- return -ENOSPC;
- if (copy_from_user(buf, arg, sizeof(copr_buffer)))
- {
- vfree(buf);
- return -EFAULT;
- }
- err = download_boot_block(dev_info, buf);
- vfree(buf);
- return err;
-
- default:
- return -EINVAL;
- }
-}
-
-static coproc_operations sscape_coproc_operations =
-{
- "SoundScape M68K",
- THIS_MODULE,
- sscape_coproc_open,
- sscape_coproc_close,
- sscape_coproc_ioctl,
- sscape_coproc_reset,
- &adev_info
-};
-
-static struct resource *sscape_ports;
-static int sscape_is_pnp;
-
-static void __init attach_sscape(struct address_info *hw_config)
-{
-#ifndef SSCAPE_REGS
- /*
- * Config register values for Spea/V7 Media FX and Ensoniq S-2000.
- * These values are card
- * dependent. If you have another SoundScape based card, you have to
- * find the correct values. Do the following:
- * - Compile this driver with SSCAPE_DEBUG1 defined.
- * - Shut down and power off your machine.
- * - Boot with DOS so that the SSINIT.EXE program is run.
- * - Warm boot to {Linux|SYSV|BSD} and write down the lines displayed
- * when detecting the SoundScape.
- * - Modify the following list to use the values printed during boot.
- * Undefine the SSCAPE_DEBUG1
- */
-#define SSCAPE_REGS { \
-/* I0 */ 0x00, \
-/* I1 */ 0xf0, /* Note! Ignored. Set always to 0xf0 */ \
-/* I2 */ 0x20, /* Note! Ignored. Set always to 0x20 */ \
-/* I3 */ 0x20, /* Note! Ignored. Set always to 0x20 */ \
-/* I4 */ 0xf5, /* Ignored */ \
-/* I5 */ 0x10, \
-/* I6 */ 0x00, \
-/* I7 */ 0x2e, /* I7 MEM config A. Likely to vary between models */ \
-/* I8 */ 0x00, /* I8 MEM config B. Likely to vary between models */ \
-/* I9 */ 0x40 /* Ignored */ \
- }
-#endif
-
- unsigned long flags;
- static unsigned char regs[10] = SSCAPE_REGS;
-
- int i, irq_bits = 0xff;
-
- if (old_hardware)
- {
- valid_interrupts = valid_interrupts_old;
- conf_printf("Ensoniq SoundScape (old)", hw_config);
- }
- else
- conf_printf("Ensoniq SoundScape", hw_config);
-
- for (i = 0; i < 4; i++)
- {
- if (hw_config->irq == valid_interrupts[i])
- {
- irq_bits = i;
- break;
- }
- }
- if (hw_config->irq > 15 || (regs[4] = irq_bits == 0xff))
- {
- printk(KERN_ERR "Invalid IRQ%d\n", hw_config->irq);
- release_region(devc->base, 2);
- release_region(devc->base + 2, 6);
- if (sscape_is_pnp)
- release_region(devc->codec, 2);
- return;
- }
-
- if (!sscape_is_pnp) {
-
- spin_lock_irqsave(&devc->lock,flags);
- /* Host interrupt enable */
- sscape_write(devc, 1, 0xf0); /* All interrupts enabled */
- /* DMA A status/trigger register */
- sscape_write(devc, 2, 0x20); /* DMA channel disabled */
- /* DMA B status/trigger register */
- sscape_write(devc, 3, 0x20); /* DMA channel disabled */
- /* Host interrupt config reg */
- sscape_write(devc, 4, 0xf0 | (irq_bits << 2) | irq_bits);
- /* Don't destroy CD-ROM DMA config bits (0xc0) */
- sscape_write(devc, 5, (regs[5] & 0x3f) | (sscape_read(devc, 5) & 0xc0));
- /* CD-ROM config (WSS codec actually) */
- sscape_write(devc, 6, regs[6]);
- sscape_write(devc, 7, regs[7]);
- sscape_write(devc, 8, regs[8]);
- /* Master control reg. Don't modify CR-ROM bits. Disable SB emul */
- sscape_write(devc, 9, (sscape_read(devc, 9) & 0xf0) | 0x08);
- spin_unlock_irqrestore(&devc->lock,flags);
- }
-#ifdef SSCAPE_DEBUG2
- /*
- * Temporary debugging aid. Print contents of the registers after
- * changing them.
- */
- {
- int i;
-
- for (i = 0; i < 13; i++)
- printk("I%d = %02x (new value)\n", i, sscape_read(devc, i));
- }
-#endif
-
- if (probe_mpu401(hw_config, sscape_ports))
- hw_config->always_detect = 1;
- hw_config->name = "SoundScape";
-
- hw_config->irq *= -1; /* Negative value signals IRQ sharing */
- attach_mpu401(hw_config, THIS_MODULE);
- hw_config->irq *= -1; /* Restore it */
-
- if (hw_config->slots[1] != -1) /* The MPU driver installed itself */
- {
- sscape_mididev = hw_config->slots[1];
- midi_devs[hw_config->slots[1]]->coproc = &sscape_coproc_operations;
- }
- sscape_write(devc, GA_INTENA_REG, 0x80); /* Master IRQ enable */
- devc->ok = 1;
- devc->failed = 0;
-}
-
-static int detect_ga(sscape_info * devc)
-{
- unsigned char save;
-
- DDB(printk("Entered Soundscape detect_ga(%x)\n", devc->base));
-
- /*
- * First check that the address register of "ODIE" is
- * there and that it has exactly 4 writable bits.
- * First 4 bits
- */
-
- if ((save = inb(PORT(ODIE_ADDR))) & 0xf0)
- {
- DDB(printk("soundscape: Detect error A\n"));
- return 0;
- }
- outb((0x00), PORT(ODIE_ADDR));
- if (inb(PORT(ODIE_ADDR)) != 0x00)
- {
- DDB(printk("soundscape: Detect error B\n"));
- return 0;
- }
- outb((0xff), PORT(ODIE_ADDR));
- if (inb(PORT(ODIE_ADDR)) != 0x0f)
- {
- DDB(printk("soundscape: Detect error C\n"));
- return 0;
- }
- outb((save), PORT(ODIE_ADDR));
-
- /*
- * Now verify that some indirect registers return zero on some bits.
- * This may break the driver with some future revisions of "ODIE" but...
- */
-
- if (sscape_read(devc, 0) & 0x0c)
- {
- DDB(printk("soundscape: Detect error D (%x)\n", sscape_read(devc, 0)));
- return 0;
- }
- if (sscape_read(devc, 1) & 0x0f)
- {
- DDB(printk("soundscape: Detect error E\n"));
- return 0;
- }
- if (sscape_read(devc, 5) & 0x0f)
- {
- DDB(printk("soundscape: Detect error F\n"));
- return 0;
- }
- return 1;
-}
-
-static int sscape_read_host_ctrl(sscape_info* devc)
-{
- return host_read(devc);
-}
-
-static void sscape_write_host_ctrl2(sscape_info *devc, int a, int b)
-{
- host_command2(devc, a, b);
-}
-
-static int sscape_alloc_dma(sscape_info *devc)
-{
- char *start_addr, *end_addr;
- int dma_pagesize;
- int sz, size;
- struct page *page;
-
- if (devc->raw_buf != NULL) return 0; /* Already done */
- dma_pagesize = (devc->dma < 4) ? (64 * 1024) : (128 * 1024);
- devc->raw_buf = NULL;
- devc->buffsize = 8192*4;
- if (devc->buffsize > dma_pagesize) devc->buffsize = dma_pagesize;
- start_addr = NULL;
- /*
- * Now loop until we get a free buffer. Try to get smaller buffer if
- * it fails. Don't accept smaller than 8k buffer for performance
- * reasons.
- */
- while (start_addr == NULL && devc->buffsize > PAGE_SIZE) {
- for (sz = 0, size = PAGE_SIZE; size < devc->buffsize; sz++, size <<= 1);
- devc->buffsize = PAGE_SIZE * (1 << sz);
- start_addr = (char *) __get_free_pages(GFP_ATOMIC|GFP_DMA, sz);
- if (start_addr == NULL) devc->buffsize /= 2;
- }
-
- if (start_addr == NULL) {
- printk(KERN_ERR "sscape pnp init error: Couldn't allocate DMA buffer\n");
- return 0;
- } else {
- /* make some checks */
- end_addr = start_addr + devc->buffsize - 1;
- /* now check if it fits into the same dma-pagesize */
-
- if (((long) start_addr & ~(dma_pagesize - 1)) != ((long) end_addr & ~(dma_pagesize - 1))
- || end_addr >= (char *) (MAX_DMA_ADDRESS)) {
- printk(KERN_ERR "sscape pnp: Got invalid address 0x%lx for %db DMA-buffer\n", (long) start_addr, devc->buffsize);
- return 0;
- }
- }
- devc->raw_buf = start_addr;
- devc->raw_buf_phys = virt_to_bus(start_addr);
-
- for (page = virt_to_page(start_addr); page <= virt_to_page(end_addr); page++)
- SetPageReserved(page);
- return 1;
-}
-
-static void sscape_free_dma(sscape_info *devc)
-{
- int sz, size;
- unsigned long start_addr, end_addr;
- struct page *page;
-
- if (devc->raw_buf == NULL) return;
- for (sz = 0, size = PAGE_SIZE; size < devc->buffsize; sz++, size <<= 1);
- start_addr = (unsigned long) devc->raw_buf;
- end_addr = start_addr + devc->buffsize;
-
- for (page = virt_to_page(start_addr); page <= virt_to_page(end_addr); page++)
- ClearPageReserved(page);
-
- free_pages((unsigned long) devc->raw_buf, sz);
- devc->raw_buf = NULL;
-}
-
-/* Intel version !!!!!!!!! */
-
-static int sscape_start_dma(int chan, unsigned long physaddr, int count, int dma_mode)
-{
- unsigned long flags;
-
- flags = claim_dma_lock();
- disable_dma(chan);
- clear_dma_ff(chan);
- set_dma_mode(chan, dma_mode);
- set_dma_addr(chan, physaddr);
- set_dma_count(chan, count);
- enable_dma(chan);
- release_dma_lock(flags);
- return 0;
-}
-
-static void sscape_pnp_start_dma(sscape_info* devc, int arg )
-{
- int reg;
- if (arg == 0) reg = 2;
- else reg = 3;
-
- sscape_write(devc, reg, sscape_read( devc, reg) | 0x01);
- sscape_write(devc, reg, sscape_read( devc, reg) & 0xFE);
-}
-
-static int sscape_pnp_wait_dma (sscape_info* devc, int arg )
-{
- int reg;
- unsigned long i;
- unsigned char d;
-
- if (arg == 0) reg = 2;
- else reg = 3;
-
- sleep ( 1 );
- i = 0;
- do {
- d = sscape_read(devc, reg) & 1;
- if ( d == 1) break;
- i++;
- } while (i < 500000);
- d = sscape_read(devc, reg) & 1;
- return d;
-}
-
-static int sscape_pnp_alloc_dma(sscape_info* devc)
-{
- /* printk(KERN_INFO "sscape: requesting dma\n"); */
- if (request_dma(devc -> dma, "sscape")) return 0;
- /* printk(KERN_INFO "sscape: dma channel allocated\n"); */
- if (!sscape_alloc_dma(devc)) {
- free_dma(devc -> dma);
- return 0;
- };
- return 1;
-}
-
-static void sscape_pnp_free_dma(sscape_info* devc)
-{
- sscape_free_dma( devc);
- free_dma(devc -> dma );
- /* printk(KERN_INFO "sscape: dma released\n"); */
-}
-
-static int sscape_pnp_upload_file(sscape_info* devc, char* fn)
-{
- int done = 0;
- int timeout_val;
- char* data,*dt;
- int len,l;
- unsigned long flags;
-
- sscape_write( devc, 9, sscape_read(devc, 9 ) & 0x3F );
- sscape_write( devc, 2, (devc -> dma << 4) | 0x80 );
- sscape_write( devc, 3, 0x20 );
- sscape_write( devc, 9, sscape_read( devc, 9 ) | 0x80 );
-
- len = mod_firmware_load(fn, &data);
- if (len == 0) {
- printk(KERN_ERR "sscape: file not found: %s\n", fn);
- return 0;
- }
- dt = data;
- spin_lock_irqsave(&devc->lock,flags);
- while ( len > 0 ) {
- if (len > devc -> buffsize) l = devc->buffsize;
- else l = len;
- len -= l;
- memcpy(devc->raw_buf, dt, l); dt += l;
- sscape_start_dma(devc->dma, devc->raw_buf_phys, l, 0x48);
- sscape_pnp_start_dma ( devc, 0 );
- if (sscape_pnp_wait_dma ( devc, 0 ) == 0) {
- spin_unlock_irqrestore(&devc->lock,flags);
- return 0;
- }
- }
-
- spin_unlock_irqrestore(&devc->lock,flags);
- vfree(data);
-
- outb(0, devc -> base + 2);
- outb(0, devc -> base);
-
- sscape_write ( devc, 9, sscape_read( devc, 9 ) | 0x40);
-
- timeout_val = 5 * HZ;
- while (!done && timeout_val-- > 0)
- {
- unsigned char x;
- sleep(1);
- x = inb( devc -> base + 3);
- if (x == 0xff || x == 0xfe) /* OBP startup acknowledge */
- {
- //printk(KERN_ERR "Soundscape: Acknowledge = %x\n", x);
- done = 1;
- }
- }
- timeout_val = 5 * HZ;
- done = 0;
- while (!done && timeout_val-- > 0)
- {
- unsigned char x;
- sleep(1);
- x = inb( devc -> base + 3);
- if (x == 0xfe) /* OBP startup acknowledge */
- {
- //printk(KERN_ERR "Soundscape: Acknowledge = %x\n", x);
- done = 1;
- }
- }
-
- if ( !done ) printk(KERN_ERR "soundscape: OBP Initialization failed.\n");
-
- sscape_write( devc, 2, devc->ic_type == IC_ODIE ? 0x70 : 0x40);
- sscape_write( devc, 3, (devc -> dma << 4) + 0x80);
- return 1;
-}
-
-static void __init sscape_pnp_init_hw(sscape_info* devc)
-{
- unsigned char midi_irq = 0, sb_irq = 0;
- unsigned i;
- static char code_file_name[23] = "/sndscape/sndscape.cox";
-
- int sscape_joystic_enable = 0x7f;
- int sscape_mic_enable = 0;
- int sscape_ext_midi = 0;
-
- if ( !sscape_pnp_alloc_dma(devc) ) {
- printk(KERN_ERR "sscape: faild to allocate dma\n");
- return;
- }
-
- for (i = 0; i < 4; i++) {
- if ( devc -> irq == valid_interrupts[i] )
- midi_irq = i;
- if ( devc -> codec_irq == valid_interrupts[i] )
- sb_irq = i;
- }
-
- sscape_write( devc, 5, 0x50);
- sscape_write( devc, 7, 0x2e);
- sscape_write( devc, 8, 0x00);
-
- sscape_write( devc, 2, devc->ic_type == IC_ODIE ? 0x70 : 0x40);
- sscape_write( devc, 3, ( devc -> dma << 4) | 0x80);
-
- sscape_write (devc, 4, 0xF0 | (midi_irq<<2) | midi_irq);
-
- i = 0x10; //sscape_read(devc, 9) & (devc->ic_type == IC_ODIE ? 0xf0 : 0xc0);
- if (sscape_joystic_enable) i |= 8;
-
- sscape_write (devc, 9, i);
- sscape_write (devc, 6, 0x80);
- sscape_write (devc, 1, 0x80);
-
- if (devc -> codec_type == 2) {
- sscape_pnp_write_codec( devc, 0x0C, 0x50);
- sscape_pnp_write_codec( devc, 0x10, sscape_pnp_read_codec( devc, 0x10) & 0x3F);
- sscape_pnp_write_codec( devc, 0x11, sscape_pnp_read_codec( devc, 0x11) | 0xC0);
- sscape_pnp_write_codec( devc, 29, 0x20);
- }
-
- if (sscape_pnp_upload_file(devc, "/sndscape/scope.cod") == 0 ) {
- printk(KERN_ERR "sscape: faild to upload file /sndscape/scope.cod\n");
- sscape_pnp_free_dma(devc);
- return;
- }
-
- i = sscape_read_host_ctrl( devc );
-
- if ( (i & 0x0F) > 7 ) {
- printk(KERN_ERR "sscape: scope.cod faild\n");
- sscape_pnp_free_dma(devc);
- return;
- }
- if ( i & 0x10 ) sscape_write( devc, 7, 0x2F);
- code_file_name[21] = (char) ( i & 0x0F) + 0x30;
- if (sscape_pnp_upload_file( devc, code_file_name) == 0) {
- printk(KERN_ERR "sscape: faild to upload file %s\n", code_file_name);
- sscape_pnp_free_dma(devc);
- return;
- }
-
- if (devc->ic_type != IC_ODIE) {
- sscape_pnp_write_codec( devc, 10, (sscape_pnp_read_codec(devc, 10) & 0x7f) |
- ( sscape_mic_enable == 0 ? 0x00 : 0x80) );
- }
- sscape_write_host_ctrl2( devc, 0x84, 0x64 ); /* MIDI volume */
- sscape_write_host_ctrl2( devc, 0x86, 0x64 ); /* MIDI volume?? */
- sscape_write_host_ctrl2( devc, 0x8A, sscape_ext_midi);
-
- sscape_pnp_write_codec ( devc, 6, 0x3f ); //WAV_VOL
- sscape_pnp_write_codec ( devc, 7, 0x3f ); //WAV_VOL
- sscape_pnp_write_codec ( devc, 2, 0x1F ); //WD_CDXVOLL
- sscape_pnp_write_codec ( devc, 3, 0x1F ); //WD_CDXVOLR
-
- if (devc -> codec_type == 1) {
- sscape_pnp_write_codec ( devc, 4, 0x1F );
- sscape_pnp_write_codec ( devc, 5, 0x1F );
- sscape_write_host_ctrl2( devc, 0x88, sscape_mic_enable);
- } else {
- int t;
- sscape_pnp_write_codec ( devc, 0x10, 0x1F << 1);
- sscape_pnp_write_codec ( devc, 0x11, 0xC0 | (0x1F << 1));
-
- t = sscape_pnp_read_codec( devc, 0x00) & 0xDF;
- if ( (sscape_mic_enable == 0)) t |= 0;
- else t |= 0x20;
- sscape_pnp_write_codec ( devc, 0x00, t);
- t = sscape_pnp_read_codec( devc, 0x01) & 0xDF;
- if ( (sscape_mic_enable == 0) ) t |= 0;
- else t |= 0x20;
- sscape_pnp_write_codec ( devc, 0x01, t);
- sscape_pnp_write_codec ( devc, 0x40 | 29 , 0x20);
- outb(0, devc -> codec);
- }
- if (devc -> ic_type == IC_OPUS ) {
- int i = sscape_read( devc, 9 );
- sscape_write( devc, 9, i | 3 );
- sscape_write( devc, 3, 0x40);
-
- if (request_region(0x228, 1, "sscape setup junk")) {
- outb(0, 0x228);
- release_region(0x228,1);
- }
- sscape_write( devc, 3, (devc -> dma << 4) | 0x80);
- sscape_write( devc, 9, i );
- }
-
- host_close ( devc );
- sscape_pnp_free_dma(devc);
-}
-
-static int __init detect_sscape_pnp(sscape_info* devc)
-{
- long i, irq_bits = 0xff;
- unsigned int d;
-
- DDB(printk("Entered detect_sscape_pnp(%x)\n", devc->base));
-
- if (!request_region(devc->codec, 2, "sscape codec")) {
- printk(KERN_ERR "detect_sscape_pnp: port %x is not free\n", devc->codec);
- return 0;
- }
-
- if ((inb(devc->base + 2) & 0x78) != 0)
- goto fail;
-
- d = inb ( devc -> base + 4) & 0xF0;
- if (d & 0x80)
- goto fail;
-
- if (d == 0) {
- devc->codec_type = 1;
- devc->ic_type = IC_ODIE;
- } else if ( (d & 0x60) != 0) {
- devc->codec_type = 2;
- devc->ic_type = IC_OPUS;
- } else if ( (d & 0x40) != 0) { /* WTF? */
- devc->codec_type = 2;
- devc->ic_type = IC_ODIE;
- } else
- goto fail;
-
- sscape_is_pnp = 1;
-
- outb(0xFA, devc -> base+4);
- if ((inb( devc -> base+4) & 0x9F) != 0x0A)
- goto fail;
- outb(0xFE, devc -> base+4);
- if ( (inb(devc -> base+4) & 0x9F) != 0x0E)
- goto fail;
- if ( (inb(devc -> base+5) & 0x9F) != 0x0E)
- goto fail;
-
- if (devc->codec_type == 2) {
- if (devc->codec != devc->base + 8) {
- printk("soundscape warning: incorrect codec port specified\n");
- goto fail;
- }
- d = 0x10 | (sscape_read(devc, 9) & 0xCF);
- sscape_write(devc, 9, d);
- sscape_write(devc, 6, 0x80);
- } else {
- //todo: check codec is not base + 8
- }
-
- d = (sscape_read(devc, 9) & 0x3F) | 0xC0;
- sscape_write(devc, 9, d);
-
- for (i = 0; i < 550000; i++)
- if ( !(inb(devc -> codec) & 0x80) ) break;
-
- d = inb(devc -> codec);
- if (d & 0x80)
- goto fail;
- if ( inb(devc -> codec + 2) == 0xFF)
- goto fail;
-
- sscape_write(devc, 9, sscape_read(devc, 9) & 0x3F );
-
- d = inb(devc -> codec) & 0x80;
- if ( d == 0) {
- printk(KERN_INFO "soundscape: hardware detected\n");
- valid_interrupts = valid_interrupts_new;
- } else {
- printk(KERN_INFO "soundscape: board looks like media fx\n");
- valid_interrupts = valid_interrupts_old;
- old_hardware = 1;
- }
-
- sscape_write( devc, 9, 0xC0 | (sscape_read(devc, 9) & 0x3F) );
-
- for (i = 0; i < 550000; i++)
- if ( !(inb(devc -> codec) & 0x80))
- break;
-
- sscape_pnp_init_hw(devc);
-
- for (i = 0; i < 4; i++)
- {
- if (devc->codec_irq == valid_interrupts[i]) {
- irq_bits = i;
- break;
- }
- }
- sscape_write(devc, GA_INTENA_REG, 0x00);
- sscape_write(devc, GA_DMACFG_REG, 0x50);
- sscape_write(devc, GA_DMAA_REG, 0x70);
- sscape_write(devc, GA_DMAB_REG, 0x20);
- sscape_write(devc, GA_INTCFG_REG, 0xf0);
- sscape_write(devc, GA_CDCFG_REG, 0x89 | (devc->dma << 4) | (irq_bits << 1));
-
- sscape_pnp_write_codec( devc, 0, sscape_pnp_read_codec( devc, 0) | 0x20);
- sscape_pnp_write_codec( devc, 0, sscape_pnp_read_codec( devc, 1) | 0x20);
-
- return 1;
-fail:
- release_region(devc->codec, 2);
- return 0;
-}
-
-static int __init probe_sscape(struct address_info *hw_config)
-{
- devc->base = hw_config->io_base;
- devc->irq = hw_config->irq;
- devc->dma = hw_config->dma;
- devc->osp = hw_config->osp;
-
-#ifdef SSCAPE_DEBUG1
- /*
- * Temporary debugging aid. Print contents of the registers before
- * changing them.
- */
- {
- int i;
-
- for (i = 0; i < 13; i++)
- printk("I%d = %02x (old value)\n", i, sscape_read(devc, i));
- }
-#endif
- devc->failed = 1;
-
- sscape_ports = request_region(devc->base, 2, "mpu401");
- if (!sscape_ports)
- return 0;
-
- if (!request_region(devc->base + 2, 6, "SoundScape")) {
- release_region(devc->base, 2);
- return 0;
- }
-
- if (!detect_ga(devc)) {
- if (detect_sscape_pnp(devc))
- return 1;
- release_region(devc->base, 2);
- release_region(devc->base + 2, 6);
- return 0;
- }
-
- if (old_hardware) /* Check that it's really an old Spea/Reveal card. */
- {
- unsigned char tmp;
- int cc;
-
- if (!((tmp = sscape_read(devc, GA_HMCTL_REG)) & 0xc0))
- {
- sscape_write(devc, GA_HMCTL_REG, tmp | 0x80);
- for (cc = 0; cc < 200000; ++cc)
- inb(devc->base + ODIE_ADDR);
- }
- }
- return 1;
-}
-
-static int __init init_ss_ms_sound(struct address_info *hw_config)
-{
- int i, irq_bits = 0xff;
- int ad_flags = 0;
- struct resource *ports;
-
- if (devc->failed)
- {
- printk(KERN_ERR "soundscape: Card not detected\n");
- return 0;
- }
- if (devc->ok == 0)
- {
- printk(KERN_ERR "soundscape: Invalid initialization order.\n");
- return 0;
- }
- for (i = 0; i < 4; i++)
- {
- if (hw_config->irq == valid_interrupts[i])
- {
- irq_bits = i;
- break;
- }
- }
- if (irq_bits == 0xff) {
- printk(KERN_ERR "soundscape: Invalid MSS IRQ%d\n", hw_config->irq);
- return 0;
- }
-
- if (old_hardware)
- ad_flags = 0x12345677; /* Tell that we may have a CS4248 chip (Spea-V7 Media FX) */
- else if (sscape_is_pnp)
- ad_flags = 0x87654321; /* Tell that we have a soundscape pnp with 1845 chip */
-
- ports = request_region(hw_config->io_base, 4, "ad1848");
- if (!ports) {
- printk(KERN_ERR "soundscape: ports busy\n");
- return 0;
- }
-
- if (!ad1848_detect(ports, &ad_flags, hw_config->osp)) {
- release_region(hw_config->io_base, 4);
- return 0;
- }
-
- if (!sscape_is_pnp) /*pnp is already setup*/
- {
- /*
- * Setup the DMA polarity.
- */
- sscape_write(devc, GA_DMACFG_REG, 0x50);
-
- /*
- * Take the gate-array off of the DMA channel.
- */
- sscape_write(devc, GA_DMAB_REG, 0x20);
-
- /*
- * Init the AD1848 (CD-ROM) config reg.
- */
- sscape_write(devc, GA_CDCFG_REG, 0x89 | (hw_config->dma << 4) | (irq_bits << 1));
- }
-
- if (hw_config->irq == devc->irq)
- printk(KERN_WARNING "soundscape: Warning! The WSS mode can't share IRQ with MIDI\n");
-
- hw_config->slots[0] = ad1848_init(
- sscape_is_pnp ? "SoundScape" : "SoundScape PNP",
- ports,
- hw_config->irq,
- hw_config->dma,
- hw_config->dma,
- 0,
- devc->osp,
- THIS_MODULE);
-
-
- if (hw_config->slots[0] != -1) /* The AD1848 driver installed itself */
- {
- audio_devs[hw_config->slots[0]]->coproc = &sscape_coproc_operations;
- devc->codec_audiodev = hw_config->slots[0];
- devc->my_audiodev = hw_config->slots[0];
-
- /* Set proper routings here (what are they) */
- AD1848_REROUTE(SOUND_MIXER_LINE1, SOUND_MIXER_LINE);
- }
-
-#ifdef SSCAPE_DEBUG5
- /*
- * Temporary debugging aid. Print contents of the registers
- * after the AD1848 device has been initialized.
- */
- {
- int i;
-
- for (i = 0; i < 13; i++)
- printk("I%d = %02x\n", i, sscape_read(devc, i));
- }
-#endif
- return 1;
-}
-
-static void __exit unload_sscape(struct address_info *hw_config)
-{
- release_region(devc->base + 2, 6);
- unload_mpu401(hw_config);
- if (sscape_is_pnp)
- release_region(devc->codec, 2);
-}
-
-static void __exit unload_ss_ms_sound(struct address_info *hw_config)
-{
- ad1848_unload(hw_config->io_base,
- hw_config->irq,
- devc->dma,
- devc->dma,
- 0);
- sound_unload_audiodev(hw_config->slots[0]);
-}
-
-static struct address_info cfg;
-static struct address_info cfg_mpu;
-
-static int __initdata spea = -1;
-static int mss = 0;
-static int __initdata dma = -1;
-static int __initdata irq = -1;
-static int __initdata io = -1;
-static int __initdata mpu_irq = -1;
-static int __initdata mpu_io = -1;
-
-module_param(dma, int, 0);
-module_param(irq, int, 0);
-module_param(io, int, 0);
-module_param(spea, int, 0); /* spea=0/1 set the old_hardware */
-module_param(mpu_irq, int, 0);
-module_param(mpu_io, int, 0);
-module_param(mss, int, 0);
-
-static int __init init_sscape(void)
-{
- printk(KERN_INFO "Soundscape driver Copyright (C) by Hannu Savolainen 1993-1996\n");
-
- cfg.irq = irq;
- cfg.dma = dma;
- cfg.io_base = io;
-
- cfg_mpu.irq = mpu_irq;
- cfg_mpu.io_base = mpu_io;
- /* WEH - Try to get right dma channel */
- cfg_mpu.dma = dma;
-
- devc->codec = cfg.io_base;
- devc->codec_irq = cfg.irq;
- devc->codec_type = 0;
- devc->ic_type = 0;
- devc->raw_buf = NULL;
- spin_lock_init(&devc->lock);
-
- if (cfg.dma == -1 || cfg.irq == -1 || cfg.io_base == -1) {
- printk(KERN_ERR "DMA, IRQ, and IO port must be specified.\n");
- return -EINVAL;
- }
-
- if (cfg_mpu.irq == -1 && cfg_mpu.io_base != -1) {
- printk(KERN_ERR "MPU_IRQ must be specified if MPU_IO is set.\n");
- return -EINVAL;
- }
-
- if(spea != -1) {
- old_hardware = spea;
- printk(KERN_INFO "Forcing %s hardware support.\n",
- spea?"new":"old");
- }
- if (probe_sscape(&cfg_mpu) == 0)
- return -ENODEV;
-
- attach_sscape(&cfg_mpu);
-
- mss = init_ss_ms_sound(&cfg);
-
- return 0;
-}
-
-static void __exit cleanup_sscape(void)
-{
- if (mss)
- unload_ss_ms_sound(&cfg);
- unload_sscape(&cfg_mpu);
-}
-
-module_init(init_sscape);
-module_exit(cleanup_sscape);
-
-#ifndef MODULE
-static int __init setup_sscape(char *str)
-{
- /* io, irq, dma, mpu_io, mpu_irq */
- int ints[6];
-
- str = get_options(str, ARRAY_SIZE(ints), ints);
-
- io = ints[1];
- irq = ints[2];
- dma = ints[3];
- mpu_io = ints[4];
- mpu_irq = ints[5];
-
- return 1;
-}
-
-__setup("sscape=", setup_sscape);
-#endif
-MODULE_LICENSE("GPL");
diff --git a/sound/pci/Kconfig b/sound/pci/Kconfig
index 75c602b5b132..351654cf7b09 100644
--- a/sound/pci/Kconfig
+++ b/sound/pci/Kconfig
@@ -570,6 +570,7 @@ config SND_ICE1712
tristate "ICEnsemble ICE1712 (Envy24)"
select SND_MPU401_UART
select SND_AC97_CODEC
+ select BITREVERSE
help
Say Y here to include support for soundcards based on the
ICE1712 (Envy24) chip.
diff --git a/sound/pci/ac97/ac97_codec.c b/sound/pci/ac97/ac97_codec.c
index 78288dbfc17a..20cb60afb200 100644
--- a/sound/pci/ac97/ac97_codec.c
+++ b/sound/pci/ac97/ac97_codec.c
@@ -603,8 +603,8 @@ AC97_SINGLE("Tone Control - Treble", AC97_MASTER_TONE, 0, 15, 1)
};
static const struct snd_kcontrol_new snd_ac97_controls_pc_beep[2] = {
-AC97_SINGLE("PC Speaker Playback Switch", AC97_PC_BEEP, 15, 1, 1),
-AC97_SINGLE("PC Speaker Playback Volume", AC97_PC_BEEP, 1, 15, 1)
+AC97_SINGLE("Beep Playback Switch", AC97_PC_BEEP, 15, 1, 1),
+AC97_SINGLE("Beep Playback Volume", AC97_PC_BEEP, 1, 15, 1)
};
static const struct snd_kcontrol_new snd_ac97_controls_mic_boost =
@@ -1393,7 +1393,7 @@ static int snd_ac97_mixer_build(struct snd_ac97 * ac97)
}
}
- /* build PC Speaker controls */
+ /* build Beep controls */
if (!(ac97->flags & AC97_HAS_NO_PC_BEEP) &&
((ac97->flags & AC97_HAS_PC_BEEP) ||
snd_ac97_try_volume_mix(ac97, AC97_PC_BEEP))) {
diff --git a/sound/pci/ac97/ac97_patch.c b/sound/pci/ac97/ac97_patch.c
index 7337abdbe4e3..139cf3b2b9d7 100644
--- a/sound/pci/ac97/ac97_patch.c
+++ b/sound/pci/ac97/ac97_patch.c
@@ -800,12 +800,12 @@ AC97_SINGLE("Mono Switch", AC97_MASTER_TONE, 7, 1, 1),
AC97_SINGLE("Mono ZC Switch", AC97_MASTER_TONE, 6, 1, 0),
AC97_SINGLE("Mono Volume", AC97_MASTER_TONE, 0, 31, 1),
-AC97_SINGLE("PC Beep to Headphone Switch", AC97_AUX, 15, 1, 1),
-AC97_SINGLE("PC Beep to Headphone Volume", AC97_AUX, 12, 7, 1),
-AC97_SINGLE("PC Beep to Master Switch", AC97_AUX, 11, 1, 1),
-AC97_SINGLE("PC Beep to Master Volume", AC97_AUX, 8, 7, 1),
-AC97_SINGLE("PC Beep to Mono Switch", AC97_AUX, 7, 1, 1),
-AC97_SINGLE("PC Beep to Mono Volume", AC97_AUX, 4, 7, 1),
+AC97_SINGLE("Beep to Headphone Switch", AC97_AUX, 15, 1, 1),
+AC97_SINGLE("Beep to Headphone Volume", AC97_AUX, 12, 7, 1),
+AC97_SINGLE("Beep to Master Switch", AC97_AUX, 11, 1, 1),
+AC97_SINGLE("Beep to Master Volume", AC97_AUX, 8, 7, 1),
+AC97_SINGLE("Beep to Mono Switch", AC97_AUX, 7, 1, 1),
+AC97_SINGLE("Beep to Mono Volume", AC97_AUX, 4, 7, 1),
AC97_SINGLE("Voice to Headphone Switch", AC97_PCM, 15, 1, 1),
AC97_SINGLE("Voice to Headphone Volume", AC97_PCM, 12, 7, 1),
diff --git a/sound/pci/azt3328.c b/sound/pci/azt3328.c
index 8451a0169f32..69867ace7860 100644
--- a/sound/pci/azt3328.c
+++ b/sound/pci/azt3328.c
@@ -830,8 +830,8 @@ static struct snd_kcontrol_new snd_azf3328_mixer_controls[] __devinitdata = {
AZF3328_MIXER_SWITCH("Mic Boost (+20dB)", IDX_MIXER_MIC, 6, 0),
AZF3328_MIXER_SWITCH("Line Playback Switch", IDX_MIXER_LINEIN, 15, 1),
AZF3328_MIXER_VOL_STEREO("Line Playback Volume", IDX_MIXER_LINEIN, 0x1f, 1),
- AZF3328_MIXER_SWITCH("PC Speaker Playback Switch", IDX_MIXER_PCBEEP, 15, 1),
- AZF3328_MIXER_VOL_SPECIAL("PC Speaker Playback Volume", IDX_MIXER_PCBEEP, 0x0f, 1, 1),
+ AZF3328_MIXER_SWITCH("Beep Playback Switch", IDX_MIXER_PCBEEP, 15, 1),
+ AZF3328_MIXER_VOL_SPECIAL("Beep Playback Volume", IDX_MIXER_PCBEEP, 0x0f, 1, 1),
AZF3328_MIXER_SWITCH("Video Playback Switch", IDX_MIXER_VIDEO, 15, 1),
AZF3328_MIXER_VOL_STEREO("Video Playback Volume", IDX_MIXER_VIDEO, 0x1f, 1),
AZF3328_MIXER_SWITCH("Aux Playback Switch", IDX_MIXER_AUX, 15, 1),
diff --git a/sound/pci/ca0106/ca0106_mixer.c b/sound/pci/ca0106/ca0106_mixer.c
index c8c6f437f5b3..8f443a9d61ec 100644
--- a/sound/pci/ca0106/ca0106_mixer.c
+++ b/sound/pci/ca0106/ca0106_mixer.c
@@ -792,8 +792,8 @@ int __devinit snd_ca0106_mixer(struct snd_ca0106 *emu)
"Phone Playback Volume",
"Video Playback Switch",
"Video Playback Volume",
- "PC Speaker Playback Switch",
- "PC Speaker Playback Volume",
+ "Beep Playback Switch",
+ "Beep Playback Volume",
"Mono Output Select",
"Capture Source",
"Capture Switch",
diff --git a/sound/pci/ca0106/ca0106_proc.c b/sound/pci/ca0106/ca0106_proc.c
index c62b7d10ec61..15523e60351c 100644
--- a/sound/pci/ca0106/ca0106_proc.c
+++ b/sound/pci/ca0106/ca0106_proc.c
@@ -304,7 +304,7 @@ static void snd_ca0106_proc_reg_write32(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 && val <= 0xffffffff) {
spin_lock_irqsave(&emu->emu_lock, flags);
outl(val, emu->port + (reg & 0xfffffffc));
spin_unlock_irqrestore(&emu->emu_lock, flags);
@@ -405,7 +405,7 @@ static void snd_ca0106_proc_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 < 0x80) && (reg >=0) && (val <= 0xffffffff) && (channel_id >=0) && (channel_id <= 3) )
+ if (reg < 0x80 && val <= 0xffffffff && channel_id <= 3)
snd_ca0106_ptr_write(emu, reg, channel_id, val);
}
}
diff --git a/sound/pci/cmipci.c b/sound/pci/cmipci.c
index ddcd4a9fd7e6..a312bae08f52 100644
--- a/sound/pci/cmipci.c
+++ b/sound/pci/cmipci.c
@@ -2302,7 +2302,7 @@ static struct snd_kcontrol_new snd_cmipci_mixers[] __devinitdata = {
CMIPCI_SB_VOL_MONO("Mic Playback Volume", SB_DSP4_MIC_DEV, 3, 31),
CMIPCI_SB_SW_MONO("Mic Playback Switch", 0),
CMIPCI_DOUBLE("Mic Capture Switch", SB_DSP4_INPUT_LEFT, SB_DSP4_INPUT_RIGHT, 0, 0, 1, 0, 0),
- CMIPCI_SB_VOL_MONO("PC Speaker Playback Volume", SB_DSP4_SPEAKER_DEV, 6, 3),
+ CMIPCI_SB_VOL_MONO("Beep Playback Volume", SB_DSP4_SPEAKER_DEV, 6, 3),
CMIPCI_MIXER_VOL_STEREO("Aux Playback Volume", CM_REG_AUX_VOL, 4, 0, 15),
CMIPCI_MIXER_SW_STEREO("Aux Playback Switch", CM_REG_MIXER2, CM_VAUXLM_SHIFT, CM_VAUXRM_SHIFT, 0),
CMIPCI_MIXER_SW_STEREO("Aux Capture Switch", CM_REG_MIXER2, CM_RAUXLEN_SHIFT, CM_RAUXREN_SHIFT, 0),
@@ -2310,7 +2310,7 @@ static struct snd_kcontrol_new snd_cmipci_mixers[] __devinitdata = {
CMIPCI_MIXER_VOL_MONO("Mic Capture Volume", CM_REG_MIXER2, CM_VADMIC_SHIFT, 7),
CMIPCI_SB_VOL_MONO("Phone Playback Volume", CM_REG_EXTENT_IND, 5, 7),
CMIPCI_DOUBLE("Phone Playback Switch", CM_REG_EXTENT_IND, CM_REG_EXTENT_IND, 4, 4, 1, 0, 0),
- CMIPCI_DOUBLE("PC Speaker Playback Switch", CM_REG_EXTENT_IND, CM_REG_EXTENT_IND, 3, 3, 1, 0, 0),
+ CMIPCI_DOUBLE("Beep Playback Switch", CM_REG_EXTENT_IND, CM_REG_EXTENT_IND, 3, 3, 1, 0, 0),
CMIPCI_DOUBLE("Mic Boost Capture Switch", CM_REG_EXTENT_IND, CM_REG_EXTENT_IND, 0, 0, 1, 0, 0),
};
diff --git a/sound/pci/ctxfi/ctatc.c b/sound/pci/ctxfi/ctatc.c
index 75454648d50c..cb65bd0dd35b 100644
--- a/sound/pci/ctxfi/ctatc.c
+++ b/sound/pci/ctxfi/ctatc.c
@@ -240,7 +240,7 @@ static int select_rom(unsigned int pitch)
} else if (pitch == 0x02000000) {
/* pitch == 2 */
return 3;
- } else if (pitch >= 0x0 && pitch <= 0x08000000) {
+ } else if (pitch <= 0x08000000) {
/* 0 <= pitch <= 8 */
return 0;
} else {
diff --git a/sound/pci/emu10k1/emu10k1x.c b/sound/pci/emu10k1/emu10k1x.c
index 36e08bd2b3cc..6b8ae7b5cd54 100644
--- a/sound/pci/emu10k1/emu10k1x.c
+++ b/sound/pci/emu10k1/emu10k1x.c
@@ -1040,8 +1040,7 @@ 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 && val <= 0xffffffff && channel_id <= 2)
snd_emu10k1x_ptr_write(emu, reg, channel_id, val);
}
}
diff --git a/sound/pci/emu10k1/emumixer.c b/sound/pci/emu10k1/emumixer.c
index b0fb6c917c38..05afe06e353a 100644
--- a/sound/pci/emu10k1/emumixer.c
+++ b/sound/pci/emu10k1/emumixer.c
@@ -1818,8 +1818,8 @@ int __devinit snd_emu10k1_mixer(struct snd_emu10k1 *emu,
"Master Playback Switch", "Master Capture Switch",
"Master Playback Volume", "Master Capture Volume",
"Wave Master Playback Volume", "Master Playback Volume",
- "PC Speaker Playback Switch", "PC Speaker Capture Switch",
- "PC Speaker Playback Volume", "PC Speaker Capture Volume",
+ "Beep Playback Switch", "Beep Capture Switch",
+ "Beep Playback Volume", "Beep Capture Volume",
"Phone Playback Switch", "Phone Capture Switch",
"Phone Playback Volume", "Phone Capture Volume",
"Mic Playback Switch", "Mic Capture Switch",
diff --git a/sound/pci/emu10k1/emuproc.c b/sound/pci/emu10k1/emuproc.c
index 216f9748aff5..baa7cd508cd8 100644
--- a/sound/pci/emu10k1/emuproc.c
+++ b/sound/pci/emu10k1/emuproc.c
@@ -451,7 +451,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 && val <= 0xffffffff) {
spin_lock_irqsave(&emu->emu_lock, flags);
outl(val, emu->port + (reg & 0xfffffffc));
spin_unlock_irqrestore(&emu->emu_lock, flags);
@@ -527,7 +527,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 && val <= 0xffffffff && channel_id <= 3)
snd_ptr_write(emu, iobase, reg, channel_id, val);
}
}
diff --git a/sound/pci/emu10k1/io.c b/sound/pci/emu10k1/io.c
index c1a5aa15af8f..5ef7080e14d0 100644
--- a/sound/pci/emu10k1/io.c
+++ b/sound/pci/emu10k1/io.c
@@ -256,7 +256,7 @@ int snd_emu1010_fpga_write(struct snd_emu10k1 * emu, u32 reg, u32 value)
if (reg > 0x3f)
return 1;
reg += 0x40; /* 0x40 upwards are registers. */
- if (value < 0 || value > 0x3f) /* 0 to 0x3f are values */
+ if (value > 0x3f) /* 0 to 0x3f are values */
return 1;
spin_lock_irqsave(&emu->emu_lock, flags);
outl(reg, emu->port + A_IOCFG);
diff --git a/sound/pci/es1938.c b/sound/pci/es1938.c
index 820318ee62c1..fb83e1ffa5cb 100644
--- a/sound/pci/es1938.c
+++ b/sound/pci/es1938.c
@@ -1387,7 +1387,7 @@ ES1938_DOUBLE_TLV("Aux Playback Volume", 0, 0x3a, 0x3a, 4, 0, 15, 0,
db_scale_line),
ES1938_DOUBLE_TLV("Capture Volume", 0, 0xb4, 0xb4, 4, 0, 15, 0,
db_scale_capture),
-ES1938_SINGLE("PC Speaker Volume", 0, 0x3c, 0, 7, 0),
+ES1938_SINGLE("Beep Volume", 0, 0x3c, 0, 7, 0),
ES1938_SINGLE("Record Monitor", 0, 0xa8, 3, 1, 0),
ES1938_SINGLE("Capture Switch", 0, 0x1c, 4, 1, 1),
{
diff --git a/sound/pci/fm801.c b/sound/pci/fm801.c
index 60cdb9e0b68d..83508b3964fb 100644
--- a/sound/pci/fm801.c
+++ b/sound/pci/fm801.c
@@ -55,7 +55,7 @@ static int enable[SNDRV_CARDS] = SNDRV_DEFAULT_ENABLE_PNP; /* Enable this card *
* 1 = MediaForte 256-PCS
* 2 = MediaForte 256-PCPR
* 3 = MediaForte 64-PCR
- * 16 = setup tuner only (this is additional bit), i.e. SF-64-PCR FM card
+ * 16 = setup tuner only (this is additional bit), i.e. SF64-PCR FM card
* High 16-bits are video (radio) device number + 1
*/
static int tea575x_tuner[SNDRV_CARDS];
@@ -67,7 +67,10 @@ MODULE_PARM_DESC(id, "ID string for the FM801 soundcard.");
module_param_array(enable, bool, NULL, 0444);
MODULE_PARM_DESC(enable, "Enable FM801 soundcard.");
module_param_array(tea575x_tuner, int, NULL, 0444);
-MODULE_PARM_DESC(tea575x_tuner, "Enable TEA575x tuner.");
+MODULE_PARM_DESC(tea575x_tuner, "TEA575x tuner access method (1 = SF256-PCS, 2=SF256-PCPR, 3=SF64-PCR, +16=tuner-only).");
+
+#define TUNER_ONLY (1<<4)
+#define TUNER_TYPE_MASK (~TUNER_ONLY & 0xFFFF)
/*
* Direct registers
@@ -160,7 +163,7 @@ struct fm801 {
unsigned int multichannel: 1, /* multichannel support */
secondary: 1; /* secondary codec */
unsigned char secondary_addr; /* address of the secondary codec */
- unsigned int tea575x_tuner; /* tuner flags */
+ unsigned int tea575x_tuner; /* tuner access method & flags */
unsigned short ply_ctrl; /* playback control */
unsigned short cap_ctrl; /* capture control */
@@ -1287,7 +1290,7 @@ static int snd_fm801_chip_init(struct fm801 *chip, int resume)
{
unsigned short cmdw;
- if (chip->tea575x_tuner & 0x0010)
+ if (chip->tea575x_tuner & TUNER_ONLY)
goto __ac97_ok;
/* codec cold reset + AC'97 warm reset */
@@ -1296,11 +1299,13 @@ static int snd_fm801_chip_init(struct fm801 *chip, int resume)
udelay(100);
outw(0, FM801_REG(chip, CODEC_CTRL));
- if (wait_for_codec(chip, 0, AC97_RESET, msecs_to_jiffies(750)) < 0) {
- snd_printk(KERN_ERR "Primary AC'97 codec not found\n");
- if (! resume)
- return -EIO;
- }
+ if (wait_for_codec(chip, 0, AC97_RESET, msecs_to_jiffies(750)) < 0)
+ if (!resume) {
+ snd_printk(KERN_INFO "Primary AC'97 codec not found, "
+ "assume SF64-PCR (tuner-only)\n");
+ chip->tea575x_tuner = 3 | TUNER_ONLY;
+ goto __ac97_ok;
+ }
if (chip->multichannel) {
if (chip->secondary_addr) {
@@ -1414,7 +1419,7 @@ static int __devinit snd_fm801_create(struct snd_card *card,
return err;
}
chip->port = pci_resource_start(pci, 0);
- if ((tea575x_tuner & 0x0010) == 0) {
+ if ((tea575x_tuner & TUNER_ONLY) == 0) {
if (request_irq(pci->irq, snd_fm801_interrupt, IRQF_SHARED,
"FM801", chip)) {
snd_printk(KERN_ERR "unable to grab IRQ %d\n", chip->irq);
@@ -1429,6 +1434,14 @@ static int __devinit snd_fm801_create(struct snd_card *card,
chip->multichannel = 1;
snd_fm801_chip_init(chip, 0);
+ /* init might set tuner access method */
+ tea575x_tuner = chip->tea575x_tuner;
+
+ if (chip->irq >= 0 && (tea575x_tuner & TUNER_ONLY)) {
+ pci_clear_master(pci);
+ free_irq(chip->irq, chip);
+ chip->irq = -1;
+ }
if ((err = snd_device_new(card, SNDRV_DEV_LOWLEVEL, chip, &ops)) < 0) {
snd_fm801_free(chip);
@@ -1438,12 +1451,13 @@ static int __devinit snd_fm801_create(struct snd_card *card,
snd_card_set_dev(card, &pci->dev);
#ifdef TEA575X_RADIO
- if (tea575x_tuner > 0 && (tea575x_tuner & 0x000f) < 4) {
+ if ((tea575x_tuner & TUNER_TYPE_MASK) > 0 &&
+ (tea575x_tuner & TUNER_TYPE_MASK) < 4) {
chip->tea.dev_nr = tea575x_tuner >> 16;
chip->tea.card = card;
chip->tea.freq_fixup = 10700;
chip->tea.private_data = chip;
- chip->tea.ops = &snd_fm801_tea_ops[(tea575x_tuner & 0x000f) - 1];
+ chip->tea.ops = &snd_fm801_tea_ops[(tea575x_tuner & TUNER_TYPE_MASK) - 1];
snd_tea575x_init(&chip->tea);
}
#endif
@@ -1483,7 +1497,7 @@ static int __devinit snd_card_fm801_probe(struct pci_dev *pci,
sprintf(card->longname, "%s at 0x%lx, irq %i",
card->shortname, chip->port, chip->irq);
- if (tea575x_tuner[dev] & 0x0010)
+ if (chip->tea575x_tuner & TUNER_ONLY)
goto __fm801_tuner_only;
if ((err = snd_fm801_pcm(chip, 0, NULL)) < 0) {
diff --git a/sound/pci/hda/Kconfig b/sound/pci/hda/Kconfig
index 55545e0818b5..556cff937be7 100644
--- a/sound/pci/hda/Kconfig
+++ b/sound/pci/hda/Kconfig
@@ -38,9 +38,20 @@ config SND_HDA_INPUT_BEEP
Say Y here to build a digital beep interface for HD-audio
driver. This interface is used to generate digital beeps.
+config SND_HDA_INPUT_BEEP_MODE
+ int "Digital beep registration mode (0=off, 1=on, 2=mute sw on/off)"
+ depends on SND_HDA_INPUT_BEEP=y
+ default "1"
+ range 0 2
+ help
+ Set 0 to disable the digital beep interface for HD-audio by default.
+ Set 1 to always enable the digital beep interface for HD-audio by
+ default. Set 2 to control the beep device registration to input
+ layer using a "Beep Switch" in mixer applications.
+
config SND_HDA_INPUT_JACK
bool "Support jack plugging notification via input layer"
- depends on INPUT=y || INPUT=SND_HDA_INTEL
+ depends on INPUT=y || INPUT=SND
select SND_JACK
help
Say Y here to enable the jack plugging notification via
diff --git a/sound/pci/hda/hda_beep.c b/sound/pci/hda/hda_beep.c
index 3f51a981e604..5fe34a8d8c81 100644
--- a/sound/pci/hda/hda_beep.c
+++ b/sound/pci/hda/hda_beep.c
@@ -113,23 +113,25 @@ static int snd_hda_beep_event(struct input_dev *dev, unsigned int type,
return 0;
}
-int snd_hda_attach_beep_device(struct hda_codec *codec, int nid)
+static void snd_hda_do_detach(struct hda_beep *beep)
+{
+ input_unregister_device(beep->dev);
+ beep->dev = NULL;
+ cancel_work_sync(&beep->beep_work);
+ /* turn off beep for sure */
+ snd_hda_codec_write_cache(beep->codec, beep->nid, 0,
+ AC_VERB_SET_BEEP_CONTROL, 0);
+}
+
+static int snd_hda_do_attach(struct hda_beep *beep)
{
struct input_dev *input_dev;
- struct hda_beep *beep;
+ struct hda_codec *codec = beep->codec;
int err;
- if (!snd_hda_get_bool_hint(codec, "beep"))
- return 0; /* disabled explicitly */
-
- beep = kzalloc(sizeof(*beep), GFP_KERNEL);
- if (beep == NULL)
- return -ENOMEM;
- snprintf(beep->phys, sizeof(beep->phys),
- "card%d/codec#%d/beep0", codec->bus->card->number, codec->addr);
input_dev = input_allocate_device();
if (!input_dev) {
- kfree(beep);
+ printk(KERN_INFO "hda_beep: unable to allocate input device\n");
return -ENOMEM;
}
@@ -151,21 +153,96 @@ int snd_hda_attach_beep_device(struct hda_codec *codec, int nid)
err = input_register_device(input_dev);
if (err < 0) {
input_free_device(input_dev);
- kfree(beep);
+ printk(KERN_INFO "hda_beep: unable to register input device\n");
return err;
}
+ beep->dev = input_dev;
+ return 0;
+}
+
+static void snd_hda_do_register(struct work_struct *work)
+{
+ struct hda_beep *beep =
+ container_of(work, struct hda_beep, register_work);
+
+ mutex_lock(&beep->mutex);
+ if (beep->enabled && !beep->dev)
+ snd_hda_do_attach(beep);
+ mutex_unlock(&beep->mutex);
+}
+
+static void snd_hda_do_unregister(struct work_struct *work)
+{
+ struct hda_beep *beep =
+ container_of(work, struct hda_beep, unregister_work.work);
+
+ mutex_lock(&beep->mutex);
+ if (!beep->enabled && beep->dev)
+ snd_hda_do_detach(beep);
+ mutex_unlock(&beep->mutex);
+}
+int snd_hda_enable_beep_device(struct hda_codec *codec, int enable)
+{
+ struct hda_beep *beep = codec->beep;
+ enable = !!enable;
+ if (beep == NULL)
+ return 0;
+ if (beep->enabled != enable) {
+ beep->enabled = enable;
+ if (!enable) {
+ /* turn off beep */
+ snd_hda_codec_write_cache(beep->codec, beep->nid, 0,
+ AC_VERB_SET_BEEP_CONTROL, 0);
+ }
+ if (beep->mode == HDA_BEEP_MODE_SWREG) {
+ if (enable) {
+ cancel_delayed_work(&beep->unregister_work);
+ schedule_work(&beep->register_work);
+ } else {
+ schedule_delayed_work(&beep->unregister_work,
+ HZ);
+ }
+ }
+ return 1;
+ }
+ return 0;
+}
+EXPORT_SYMBOL_HDA(snd_hda_enable_beep_device);
+
+int snd_hda_attach_beep_device(struct hda_codec *codec, int nid)
+{
+ struct hda_beep *beep;
+
+ if (!snd_hda_get_bool_hint(codec, "beep"))
+ return 0; /* disabled explicitly by hints */
+ if (codec->beep_mode == HDA_BEEP_MODE_OFF)
+ return 0; /* disabled by module option */
+
+ beep = kzalloc(sizeof(*beep), GFP_KERNEL);
+ if (beep == NULL)
+ return -ENOMEM;
+ snprintf(beep->phys, sizeof(beep->phys),
+ "card%d/codec#%d/beep0", codec->bus->card->number, codec->addr);
/* enable linear scale */
snd_hda_codec_write(codec, nid, 0,
AC_VERB_SET_DIGI_CONVERT_2, 0x01);
beep->nid = nid;
- beep->dev = input_dev;
beep->codec = codec;
- beep->enabled = 1;
+ beep->mode = codec->beep_mode;
codec->beep = beep;
+ INIT_WORK(&beep->register_work, &snd_hda_do_register);
+ INIT_DELAYED_WORK(&beep->unregister_work, &snd_hda_do_unregister);
INIT_WORK(&beep->beep_work, &snd_hda_generate_beep);
+ mutex_init(&beep->mutex);
+
+ if (beep->mode == HDA_BEEP_MODE_ON) {
+ beep->enabled = 1;
+ snd_hda_do_register(&beep->register_work);
+ }
+
return 0;
}
EXPORT_SYMBOL_HDA(snd_hda_attach_beep_device);
@@ -174,11 +251,12 @@ void snd_hda_detach_beep_device(struct hda_codec *codec)
{
struct hda_beep *beep = codec->beep;
if (beep) {
- cancel_work_sync(&beep->beep_work);
-
- input_unregister_device(beep->dev);
- kfree(beep);
+ cancel_work_sync(&beep->register_work);
+ cancel_delayed_work(&beep->unregister_work);
+ if (beep->enabled)
+ snd_hda_do_detach(beep);
codec->beep = NULL;
+ kfree(beep);
}
}
EXPORT_SYMBOL_HDA(snd_hda_detach_beep_device);
diff --git a/sound/pci/hda/hda_beep.h b/sound/pci/hda/hda_beep.h
index 0c3de787c717..f1de1bac042c 100644
--- a/sound/pci/hda/hda_beep.h
+++ b/sound/pci/hda/hda_beep.h
@@ -24,19 +24,29 @@
#include "hda_codec.h"
+#define HDA_BEEP_MODE_OFF 0
+#define HDA_BEEP_MODE_ON 1
+#define HDA_BEEP_MODE_SWREG 2
+
/* beep information */
struct hda_beep {
struct input_dev *dev;
struct hda_codec *codec;
+ unsigned int mode;
char phys[32];
int tone;
hda_nid_t nid;
unsigned int enabled:1;
+ unsigned int request_enable:1;
unsigned int linear_tone:1; /* linear tone for IDT/STAC codec */
+ struct work_struct register_work; /* registration work */
+ struct delayed_work unregister_work; /* unregistration work */
struct work_struct beep_work; /* scheduled task for beep event */
+ struct mutex mutex;
};
#ifdef CONFIG_SND_HDA_INPUT_BEEP
+int snd_hda_enable_beep_device(struct hda_codec *codec, int enable);
int snd_hda_attach_beep_device(struct hda_codec *codec, int nid);
void snd_hda_detach_beep_device(struct hda_codec *codec);
#else
diff --git a/sound/pci/hda/hda_codec.c b/sound/pci/hda/hda_codec.c
index af989f660cca..9cfdb771928c 100644
--- a/sound/pci/hda/hda_codec.c
+++ b/sound/pci/hda/hda_codec.c
@@ -30,6 +30,7 @@
#include <sound/tlv.h>
#include <sound/initval.h>
#include "hda_local.h"
+#include "hda_beep.h"
#include <sound/hda_hwdep.h>
/*
@@ -93,6 +94,13 @@ static void hda_keep_power_on(struct hda_codec *codec);
static inline void hda_keep_power_on(struct hda_codec *codec) {}
#endif
+/**
+ * snd_hda_get_jack_location - Give a location string of the jack
+ * @cfg: pin default config value
+ *
+ * Parse the pin default config value and returns the string of the
+ * jack location, e.g. "Rear", "Front", etc.
+ */
const char *snd_hda_get_jack_location(u32 cfg)
{
static char *bases[7] = {
@@ -120,6 +128,13 @@ const char *snd_hda_get_jack_location(u32 cfg)
}
EXPORT_SYMBOL_HDA(snd_hda_get_jack_location);
+/**
+ * snd_hda_get_jack_connectivity - Give a connectivity string of the jack
+ * @cfg: pin default config value
+ *
+ * Parse the pin default config value and returns the string of the
+ * jack connectivity, i.e. external or internal connection.
+ */
const char *snd_hda_get_jack_connectivity(u32 cfg)
{
static char *jack_locations[4] = { "Ext", "Int", "Sep", "Oth" };
@@ -128,6 +143,13 @@ const char *snd_hda_get_jack_connectivity(u32 cfg)
}
EXPORT_SYMBOL_HDA(snd_hda_get_jack_connectivity);
+/**
+ * snd_hda_get_jack_type - Give a type string of the jack
+ * @cfg: pin default config value
+ *
+ * Parse the pin default config value and returns the string of the
+ * jack type, i.e. the purpose of the jack, such as Line-Out or CD.
+ */
const char *snd_hda_get_jack_type(u32 cfg)
{
static char *jack_types[16] = {
@@ -515,6 +537,7 @@ static int snd_hda_bus_dev_register(struct snd_device *device)
struct hda_codec *codec;
list_for_each_entry(codec, &bus->codec_list, list) {
snd_hda_hwdep_add_sysfs(codec);
+ snd_hda_hwdep_add_power_sysfs(codec);
}
return 0;
}
@@ -820,6 +843,16 @@ int snd_hda_add_pincfg(struct hda_codec *codec, struct snd_array *list,
return 0;
}
+/**
+ * snd_hda_codec_set_pincfg - Override a pin default configuration
+ * @codec: the HDA codec
+ * @nid: NID to set the pin config
+ * @cfg: the pin default config value
+ *
+ * Override a pin default configuration value in the cache.
+ * This value can be read by snd_hda_codec_get_pincfg() in a higher
+ * priority than the real hardware value.
+ */
int snd_hda_codec_set_pincfg(struct hda_codec *codec,
hda_nid_t nid, unsigned int cfg)
{
@@ -827,7 +860,15 @@ int snd_hda_codec_set_pincfg(struct hda_codec *codec,
}
EXPORT_SYMBOL_HDA(snd_hda_codec_set_pincfg);
-/* get the current pin config value of the given pin NID */
+/**
+ * snd_hda_codec_get_pincfg - Obtain a pin-default configuration
+ * @codec: the HDA codec
+ * @nid: NID to get the pin config
+ *
+ * Get the current pin config value of the given pin NID.
+ * If the pincfg value is cached or overridden via sysfs or driver,
+ * returns the cached value.
+ */
unsigned int snd_hda_codec_get_pincfg(struct hda_codec *codec, hda_nid_t nid)
{
struct hda_pincfg *pin;
@@ -944,7 +985,7 @@ int /*__devinit*/ snd_hda_codec_new(struct hda_bus *bus, unsigned int codec_addr
mutex_init(&codec->control_mutex);
init_hda_cache(&codec->amp_cache, sizeof(struct hda_amp_info));
init_hda_cache(&codec->cmd_cache, sizeof(struct hda_cache_head));
- snd_array_init(&codec->mixers, sizeof(struct snd_kcontrol *), 32);
+ snd_array_init(&codec->mixers, sizeof(struct hda_nid_item), 60);
snd_array_init(&codec->init_pins, sizeof(struct hda_pincfg), 16);
snd_array_init(&codec->driver_pins, sizeof(struct hda_pincfg), 16);
if (codec->bus->modelname) {
@@ -1026,6 +1067,15 @@ int /*__devinit*/ snd_hda_codec_new(struct hda_bus *bus, unsigned int codec_addr
}
EXPORT_SYMBOL_HDA(snd_hda_codec_new);
+/**
+ * snd_hda_codec_configure - (Re-)configure the HD-audio codec
+ * @codec: the HDA codec
+ *
+ * Start parsing of the given codec tree and (re-)initialize the whole
+ * patch instance.
+ *
+ * Returns 0 if successful or a negative error code.
+ */
int snd_hda_codec_configure(struct hda_codec *codec)
{
int err;
@@ -1088,6 +1138,11 @@ void snd_hda_codec_setup_stream(struct hda_codec *codec, hda_nid_t nid,
}
EXPORT_SYMBOL_HDA(snd_hda_codec_setup_stream);
+/**
+ * snd_hda_codec_cleanup_stream - clean up the codec for closing
+ * @codec: the CODEC to clean up
+ * @nid: the NID to clean up
+ */
void snd_hda_codec_cleanup_stream(struct hda_codec *codec, hda_nid_t nid)
{
if (!nid)
@@ -1163,8 +1218,17 @@ get_alloc_amp_hash(struct hda_codec *codec, u32 key)
return (struct hda_amp_info *)get_alloc_hash(&codec->amp_cache, key);
}
-/*
- * query AMP capabilities for the given widget and direction
+/**
+ * query_amp_caps - query AMP capabilities
+ * @codec: the HD-auio codec
+ * @nid: the NID to query
+ * @direction: either #HDA_INPUT or #HDA_OUTPUT
+ *
+ * Query AMP capabilities for the given widget and direction.
+ * Returns the obtained capability bits.
+ *
+ * When cap bits have been already read, this doesn't read again but
+ * returns the cached value.
*/
u32 query_amp_caps(struct hda_codec *codec, hda_nid_t nid, int direction)
{
@@ -1187,6 +1251,19 @@ u32 query_amp_caps(struct hda_codec *codec, hda_nid_t nid, int direction)
}
EXPORT_SYMBOL_HDA(query_amp_caps);
+/**
+ * snd_hda_override_amp_caps - Override the AMP capabilities
+ * @codec: the CODEC to clean up
+ * @nid: the NID to clean up
+ * @direction: either #HDA_INPUT or #HDA_OUTPUT
+ * @caps: the capability bits to set
+ *
+ * Override the cached AMP caps bits value by the given one.
+ * This function is useful if the driver needs to adjust the AMP ranges,
+ * e.g. limit to 0dB, etc.
+ *
+ * Returns zero if successful or a negative error code.
+ */
int snd_hda_override_amp_caps(struct hda_codec *codec, hda_nid_t nid, int dir,
unsigned int caps)
{
@@ -1222,6 +1299,17 @@ static unsigned int read_pin_cap(struct hda_codec *codec, hda_nid_t nid)
return snd_hda_param_read(codec, nid, AC_PAR_PIN_CAP);
}
+/**
+ * snd_hda_query_pin_caps - Query PIN capabilities
+ * @codec: the HD-auio codec
+ * @nid: the NID to query
+ *
+ * Query PIN capabilities for the given widget.
+ * Returns the obtained capability bits.
+ *
+ * When cap bits have been already read, this doesn't read again but
+ * returns the cached value.
+ */
u32 snd_hda_query_pin_caps(struct hda_codec *codec, hda_nid_t nid)
{
return query_caps_hash(codec, nid, HDA_HASH_PINCAP_KEY(nid),
@@ -1229,6 +1317,40 @@ u32 snd_hda_query_pin_caps(struct hda_codec *codec, hda_nid_t nid)
}
EXPORT_SYMBOL_HDA(snd_hda_query_pin_caps);
+/**
+ * snd_hda_pin_sense - execute pin sense measurement
+ * @codec: the CODEC to sense
+ * @nid: the pin NID to sense
+ *
+ * Execute necessary pin sense measurement and return its Presence Detect,
+ * Impedance, ELD Valid etc. status bits.
+ */
+u32 snd_hda_pin_sense(struct hda_codec *codec, hda_nid_t nid)
+{
+ u32 pincap = snd_hda_query_pin_caps(codec, nid);
+
+ if (pincap & AC_PINCAP_TRIG_REQ) /* need trigger? */
+ snd_hda_codec_read(codec, nid, 0, AC_VERB_SET_PIN_SENSE, 0);
+
+ return snd_hda_codec_read(codec, nid, 0,
+ AC_VERB_GET_PIN_SENSE, 0);
+}
+EXPORT_SYMBOL_HDA(snd_hda_pin_sense);
+
+/**
+ * snd_hda_jack_detect - query pin Presence Detect status
+ * @codec: the CODEC to sense
+ * @nid: the pin NID to sense
+ *
+ * Query and return the pin's Presence Detect status.
+ */
+int snd_hda_jack_detect(struct hda_codec *codec, hda_nid_t nid)
+{
+ u32 sense = snd_hda_pin_sense(codec, nid);
+ return !!(sense & AC_PINSENSE_PRESENCE);
+}
+EXPORT_SYMBOL_HDA(snd_hda_jack_detect);
+
/*
* read the current volume to info
* if the cache exists, read the cache value.
@@ -1269,8 +1391,15 @@ static void put_vol_mute(struct hda_codec *codec, struct hda_amp_info *info,
info->vol[ch] = val;
}
-/*
- * read AMP value. The volume is between 0 to 0x7f, 0x80 = mute bit.
+/**
+ * snd_hda_codec_amp_read - Read AMP value
+ * @codec: HD-audio codec
+ * @nid: NID to read the AMP value
+ * @ch: channel (left=0 or right=1)
+ * @direction: #HDA_INPUT or #HDA_OUTPUT
+ * @index: the index value (only for input direction)
+ *
+ * Read AMP value. The volume is between 0 to 0x7f, 0x80 = mute bit.
*/
int snd_hda_codec_amp_read(struct hda_codec *codec, hda_nid_t nid, int ch,
int direction, int index)
@@ -1283,8 +1412,18 @@ int snd_hda_codec_amp_read(struct hda_codec *codec, hda_nid_t nid, int ch,
}
EXPORT_SYMBOL_HDA(snd_hda_codec_amp_read);
-/*
- * update the AMP value, mask = bit mask to set, val = the value
+/**
+ * snd_hda_codec_amp_update - update the AMP value
+ * @codec: HD-audio codec
+ * @nid: NID to read the AMP value
+ * @ch: channel (left=0 or right=1)
+ * @direction: #HDA_INPUT or #HDA_OUTPUT
+ * @idx: the index value (only for input direction)
+ * @mask: bit mask to set
+ * @val: the bits value to set
+ *
+ * Update the AMP value with a bit mask.
+ * Returns 0 if the value is unchanged, 1 if changed.
*/
int snd_hda_codec_amp_update(struct hda_codec *codec, hda_nid_t nid, int ch,
int direction, int idx, int mask, int val)
@@ -1303,8 +1442,17 @@ int snd_hda_codec_amp_update(struct hda_codec *codec, hda_nid_t nid, int ch,
}
EXPORT_SYMBOL_HDA(snd_hda_codec_amp_update);
-/*
- * update the AMP stereo with the same mask and value
+/**
+ * snd_hda_codec_amp_stereo - update the AMP stereo values
+ * @codec: HD-audio codec
+ * @nid: NID to read the AMP value
+ * @direction: #HDA_INPUT or #HDA_OUTPUT
+ * @idx: the index value (only for input direction)
+ * @mask: bit mask to set
+ * @val: the bits value to set
+ *
+ * Update the AMP values like snd_hda_codec_amp_update(), but for a
+ * stereo widget with the same mask and value.
*/
int snd_hda_codec_amp_stereo(struct hda_codec *codec, hda_nid_t nid,
int direction, int idx, int mask, int val)
@@ -1318,7 +1466,12 @@ int snd_hda_codec_amp_stereo(struct hda_codec *codec, hda_nid_t nid,
EXPORT_SYMBOL_HDA(snd_hda_codec_amp_stereo);
#ifdef SND_HDA_NEEDS_RESUME
-/* resume the all amp commands from the cache */
+/**
+ * snd_hda_codec_resume_amp - Resume all AMP commands from the cache
+ * @codec: HD-audio codec
+ *
+ * Resume the all amp commands from the cache.
+ */
void snd_hda_codec_resume_amp(struct hda_codec *codec)
{
struct hda_amp_info *buffer = codec->amp_cache.buf.list;
@@ -1344,7 +1497,12 @@ void snd_hda_codec_resume_amp(struct hda_codec *codec)
EXPORT_SYMBOL_HDA(snd_hda_codec_resume_amp);
#endif /* SND_HDA_NEEDS_RESUME */
-/* volume */
+/**
+ * snd_hda_mixer_amp_volume_info - Info callback for a standard AMP mixer
+ *
+ * The control element is supposed to have the private_value field
+ * set up via HDA_COMPOSE_AMP_VAL*() or related macros.
+ */
int snd_hda_mixer_amp_volume_info(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_info *uinfo)
{
@@ -1400,6 +1558,12 @@ update_amp_value(struct hda_codec *codec, hda_nid_t nid,
HDA_AMP_VOLMASK, val);
}
+/**
+ * snd_hda_mixer_amp_volume_get - Get callback for a standard AMP mixer volume
+ *
+ * The control element is supposed to have the private_value field
+ * set up via HDA_COMPOSE_AMP_VAL*() or related macros.
+ */
int snd_hda_mixer_amp_volume_get(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
@@ -1419,6 +1583,12 @@ int snd_hda_mixer_amp_volume_get(struct snd_kcontrol *kcontrol,
}
EXPORT_SYMBOL_HDA(snd_hda_mixer_amp_volume_get);
+/**
+ * snd_hda_mixer_amp_volume_put - Put callback for a standard AMP mixer volume
+ *
+ * The control element is supposed to have the private_value field
+ * set up via HDA_COMPOSE_AMP_VAL*() or related macros.
+ */
int snd_hda_mixer_amp_volume_put(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
@@ -1443,6 +1613,12 @@ int snd_hda_mixer_amp_volume_put(struct snd_kcontrol *kcontrol,
}
EXPORT_SYMBOL_HDA(snd_hda_mixer_amp_volume_put);
+/**
+ * snd_hda_mixer_amp_volume_put - TLV callback for a standard AMP mixer volume
+ *
+ * The control element is supposed to have the private_value field
+ * set up via HDA_COMPOSE_AMP_VAL*() or related macros.
+ */
int snd_hda_mixer_amp_tlv(struct snd_kcontrol *kcontrol, int op_flag,
unsigned int size, unsigned int __user *_tlv)
{
@@ -1472,8 +1648,16 @@ int snd_hda_mixer_amp_tlv(struct snd_kcontrol *kcontrol, int op_flag,
}
EXPORT_SYMBOL_HDA(snd_hda_mixer_amp_tlv);
-/*
- * set (static) TLV for virtual master volume; recalculated as max 0dB
+/**
+ * snd_hda_set_vmaster_tlv - Set TLV for a virtual master control
+ * @codec: HD-audio codec
+ * @nid: NID of a reference widget
+ * @dir: #HDA_INPUT or #HDA_OUTPUT
+ * @tlv: TLV data to be stored, at least 4 elements
+ *
+ * Set (static) TLV data for a virtual master volume using the AMP caps
+ * obtained from the reference NID.
+ * The volume range is recalculated as if the max volume is 0dB.
*/
void snd_hda_set_vmaster_tlv(struct hda_codec *codec, hda_nid_t nid, int dir,
unsigned int *tlv)
@@ -1507,6 +1691,13 @@ _snd_hda_find_mixer_ctl(struct hda_codec *codec,
return snd_ctl_find_id(codec->bus->card, &id);
}
+/**
+ * snd_hda_find_mixer_ctl - Find a mixer control element with the given name
+ * @codec: HD-audio codec
+ * @name: ctl id name string
+ *
+ * Get the control element with the given id string and IFACE_MIXER.
+ */
struct snd_kcontrol *snd_hda_find_mixer_ctl(struct hda_codec *codec,
const char *name)
{
@@ -1514,30 +1705,57 @@ struct snd_kcontrol *snd_hda_find_mixer_ctl(struct hda_codec *codec,
}
EXPORT_SYMBOL_HDA(snd_hda_find_mixer_ctl);
-/* Add a control element and assign to the codec */
-int snd_hda_ctl_add(struct hda_codec *codec, struct snd_kcontrol *kctl)
+/**
+ * snd_hda_ctl-add - Add a control element and assign to the codec
+ * @codec: HD-audio codec
+ * @nid: corresponding NID (optional)
+ * @kctl: the control element to assign
+ *
+ * Add the given control element to an array inside the codec instance.
+ * All control elements belonging to a codec are supposed to be added
+ * by this function so that a proper clean-up works at the free or
+ * reconfiguration time.
+ *
+ * If non-zero @nid is passed, the NID is assigned to the control element.
+ * The assignment is shown in the codec proc file.
+ *
+ * snd_hda_ctl_add() checks the control subdev id field whether
+ * #HDA_SUBDEV_NID_FLAG bit is set. If set (and @nid is zero), the lower
+ * bits value is taken as the NID to assign.
+ */
+int snd_hda_ctl_add(struct hda_codec *codec, hda_nid_t nid,
+ struct snd_kcontrol *kctl)
{
int err;
- struct snd_kcontrol **knewp;
+ struct hda_nid_item *item;
+ if (kctl->id.subdevice & HDA_SUBDEV_NID_FLAG) {
+ if (nid == 0)
+ nid = kctl->id.subdevice & 0xffff;
+ kctl->id.subdevice = 0;
+ }
err = snd_ctl_add(codec->bus->card, kctl);
if (err < 0)
return err;
- knewp = snd_array_new(&codec->mixers);
- if (!knewp)
+ item = snd_array_new(&codec->mixers);
+ if (!item)
return -ENOMEM;
- *knewp = kctl;
+ item->kctl = kctl;
+ item->nid = nid;
return 0;
}
EXPORT_SYMBOL_HDA(snd_hda_ctl_add);
-/* Clear all controls assigned to the given codec */
+/**
+ * snd_hda_ctls_clear - Clear all controls assigned to the given codec
+ * @codec: HD-audio codec
+ */
void snd_hda_ctls_clear(struct hda_codec *codec)
{
int i;
- struct snd_kcontrol **kctls = codec->mixers.list;
+ struct hda_nid_item *items = codec->mixers.list;
for (i = 0; i < codec->mixers.used; i++)
- snd_ctl_remove(codec->bus->card, kctls[i]);
+ snd_ctl_remove(codec->bus->card, items[i].kctl);
snd_array_free(&codec->mixers);
}
@@ -1563,6 +1781,16 @@ static void hda_unlock_devices(struct snd_card *card)
spin_unlock(&card->files_lock);
}
+/**
+ * snd_hda_codec_reset - Clear all objects assigned to the codec
+ * @codec: HD-audio codec
+ *
+ * This frees the all PCM and control elements assigned to the codec, and
+ * clears the caches and restores the pin default configurations.
+ *
+ * When a device is being used, it returns -EBSY. If successfully freed,
+ * returns zero.
+ */
int snd_hda_codec_reset(struct hda_codec *codec)
{
struct snd_card *card = codec->bus->card;
@@ -1626,7 +1854,22 @@ int snd_hda_codec_reset(struct hda_codec *codec)
return 0;
}
-/* create a virtual master control and add slaves */
+/**
+ * snd_hda_add_vmaster - create a virtual master control and add slaves
+ * @codec: HD-audio codec
+ * @name: vmaster control name
+ * @tlv: TLV data (optional)
+ * @slaves: slave control names (optional)
+ *
+ * Create a virtual master control with the given name. The TLV data
+ * must be either NULL or a valid data.
+ *
+ * @slaves is a NULL-terminated array of strings, each of which is a
+ * slave control name. All controls with these names are assigned to
+ * the new virtual master control.
+ *
+ * This function returns zero if successful or a negative error code.
+ */
int snd_hda_add_vmaster(struct hda_codec *codec, char *name,
unsigned int *tlv, const char **slaves)
{
@@ -1643,7 +1886,7 @@ int snd_hda_add_vmaster(struct hda_codec *codec, char *name,
kctl = snd_ctl_make_virtual_master(name, tlv);
if (!kctl)
return -ENOMEM;
- err = snd_hda_ctl_add(codec, kctl);
+ err = snd_hda_ctl_add(codec, 0, kctl);
if (err < 0)
return err;
@@ -1668,7 +1911,12 @@ int snd_hda_add_vmaster(struct hda_codec *codec, char *name,
}
EXPORT_SYMBOL_HDA(snd_hda_add_vmaster);
-/* switch */
+/**
+ * snd_hda_mixer_amp_switch_info - Info callback for a standard AMP mixer switch
+ *
+ * The control element is supposed to have the private_value field
+ * set up via HDA_COMPOSE_AMP_VAL*() or related macros.
+ */
int snd_hda_mixer_amp_switch_info(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_info *uinfo)
{
@@ -1682,6 +1930,12 @@ int snd_hda_mixer_amp_switch_info(struct snd_kcontrol *kcontrol,
}
EXPORT_SYMBOL_HDA(snd_hda_mixer_amp_switch_info);
+/**
+ * snd_hda_mixer_amp_switch_get - Get callback for a standard AMP mixer switch
+ *
+ * The control element is supposed to have the private_value field
+ * set up via HDA_COMPOSE_AMP_VAL*() or related macros.
+ */
int snd_hda_mixer_amp_switch_get(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
@@ -1702,6 +1956,12 @@ int snd_hda_mixer_amp_switch_get(struct snd_kcontrol *kcontrol,
}
EXPORT_SYMBOL_HDA(snd_hda_mixer_amp_switch_get);
+/**
+ * snd_hda_mixer_amp_switch_put - Put callback for a standard AMP mixer switch
+ *
+ * The control element is supposed to have the private_value field
+ * set up via HDA_COMPOSE_AMP_VAL*() or related macros.
+ */
int snd_hda_mixer_amp_switch_put(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
@@ -1733,6 +1993,25 @@ int snd_hda_mixer_amp_switch_put(struct snd_kcontrol *kcontrol,
}
EXPORT_SYMBOL_HDA(snd_hda_mixer_amp_switch_put);
+#ifdef CONFIG_SND_HDA_INPUT_BEEP
+/**
+ * snd_hda_mixer_amp_switch_put_beep - Put callback for a beep AMP switch
+ *
+ * This function calls snd_hda_enable_beep_device(), which behaves differently
+ * depending on beep_mode option.
+ */
+int snd_hda_mixer_amp_switch_put_beep(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
+ long *valp = ucontrol->value.integer.value;
+
+ snd_hda_enable_beep_device(codec, *valp);
+ return snd_hda_mixer_amp_switch_put(kcontrol, ucontrol);
+}
+EXPORT_SYMBOL_HDA(snd_hda_mixer_amp_switch_put_beep);
+#endif /* CONFIG_SND_HDA_INPUT_BEEP */
+
/*
* bound volume controls
*
@@ -1742,6 +2021,12 @@ EXPORT_SYMBOL_HDA(snd_hda_mixer_amp_switch_put);
#define AMP_VAL_IDX_SHIFT 19
#define AMP_VAL_IDX_MASK (0x0f<<19)
+/**
+ * snd_hda_mixer_bind_switch_get - Get callback for a bound volume control
+ *
+ * The control element is supposed to have the private_value field
+ * set up via HDA_BIND_MUTE*() macros.
+ */
int snd_hda_mixer_bind_switch_get(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
@@ -1759,6 +2044,12 @@ int snd_hda_mixer_bind_switch_get(struct snd_kcontrol *kcontrol,
}
EXPORT_SYMBOL_HDA(snd_hda_mixer_bind_switch_get);
+/**
+ * snd_hda_mixer_bind_switch_put - Put callback for a bound volume control
+ *
+ * The control element is supposed to have the private_value field
+ * set up via HDA_BIND_MUTE*() macros.
+ */
int snd_hda_mixer_bind_switch_put(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
@@ -1783,8 +2074,11 @@ int snd_hda_mixer_bind_switch_put(struct snd_kcontrol *kcontrol,
}
EXPORT_SYMBOL_HDA(snd_hda_mixer_bind_switch_put);
-/*
- * generic bound volume/swtich controls
+/**
+ * snd_hda_mixer_bind_ctls_info - Info callback for a generic bound control
+ *
+ * The control element is supposed to have the private_value field
+ * set up via HDA_BIND_VOL() or HDA_BIND_SW() macros.
*/
int snd_hda_mixer_bind_ctls_info(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_info *uinfo)
@@ -1803,6 +2097,12 @@ int snd_hda_mixer_bind_ctls_info(struct snd_kcontrol *kcontrol,
}
EXPORT_SYMBOL_HDA(snd_hda_mixer_bind_ctls_info);
+/**
+ * snd_hda_mixer_bind_ctls_get - Get callback for a generic bound control
+ *
+ * The control element is supposed to have the private_value field
+ * set up via HDA_BIND_VOL() or HDA_BIND_SW() macros.
+ */
int snd_hda_mixer_bind_ctls_get(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
@@ -1820,6 +2120,12 @@ int snd_hda_mixer_bind_ctls_get(struct snd_kcontrol *kcontrol,
}
EXPORT_SYMBOL_HDA(snd_hda_mixer_bind_ctls_get);
+/**
+ * snd_hda_mixer_bind_ctls_put - Put callback for a generic bound control
+ *
+ * The control element is supposed to have the private_value field
+ * set up via HDA_BIND_VOL() or HDA_BIND_SW() macros.
+ */
int snd_hda_mixer_bind_ctls_put(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
@@ -1843,6 +2149,12 @@ int snd_hda_mixer_bind_ctls_put(struct snd_kcontrol *kcontrol,
}
EXPORT_SYMBOL_HDA(snd_hda_mixer_bind_ctls_put);
+/**
+ * snd_hda_mixer_bind_tlv - TLV callback for a generic bound control
+ *
+ * The control element is supposed to have the private_value field
+ * set up via HDA_BIND_VOL() macro.
+ */
int snd_hda_mixer_bind_tlv(struct snd_kcontrol *kcontrol, int op_flag,
unsigned int size, unsigned int __user *tlv)
{
@@ -2126,7 +2438,7 @@ int snd_hda_create_spdif_out_ctls(struct hda_codec *codec, hda_nid_t nid)
return -ENOMEM;
kctl->id.index = idx;
kctl->private_value = nid;
- err = snd_hda_ctl_add(codec, kctl);
+ err = snd_hda_ctl_add(codec, nid, kctl);
if (err < 0)
return err;
}
@@ -2165,14 +2477,19 @@ static struct snd_kcontrol_new spdif_share_sw = {
.put = spdif_share_sw_put,
};
+/**
+ * snd_hda_create_spdif_share_sw - create Default PCM switch
+ * @codec: the HDA codec
+ * @mout: multi-out instance
+ */
int snd_hda_create_spdif_share_sw(struct hda_codec *codec,
struct hda_multi_out *mout)
{
if (!mout->dig_out_nid)
return 0;
/* ATTENTION: here mout is passed as private_data, instead of codec */
- return snd_hda_ctl_add(codec,
- snd_ctl_new1(&spdif_share_sw, mout));
+ return snd_hda_ctl_add(codec, mout->dig_out_nid,
+ snd_ctl_new1(&spdif_share_sw, mout));
}
EXPORT_SYMBOL_HDA(snd_hda_create_spdif_share_sw);
@@ -2276,7 +2593,7 @@ int snd_hda_create_spdif_in_ctls(struct hda_codec *codec, hda_nid_t nid)
if (!kctl)
return -ENOMEM;
kctl->private_value = nid;
- err = snd_hda_ctl_add(codec, kctl);
+ err = snd_hda_ctl_add(codec, nid, kctl);
if (err < 0)
return err;
}
@@ -2332,7 +2649,12 @@ int snd_hda_codec_write_cache(struct hda_codec *codec, hda_nid_t nid,
}
EXPORT_SYMBOL_HDA(snd_hda_codec_write_cache);
-/* resume the all commands from the cache */
+/**
+ * snd_hda_codec_resume_cache - Resume the all commands from the cache
+ * @codec: HD-audio codec
+ *
+ * Execute all verbs recorded in the command caches to resume.
+ */
void snd_hda_codec_resume_cache(struct hda_codec *codec)
{
struct hda_cache_head *buffer = codec->cmd_cache.buf.list;
@@ -2452,9 +2774,11 @@ static void hda_call_codec_suspend(struct hda_codec *codec)
codec->afg ? codec->afg : codec->mfg,
AC_PWRST_D3);
#ifdef CONFIG_SND_HDA_POWER_SAVE
+ snd_hda_update_power_acct(codec);
cancel_delayed_work(&codec->power_work);
codec->power_on = 0;
codec->power_transition = 0;
+ codec->power_jiffies = jiffies;
#endif
}
@@ -2756,8 +3080,12 @@ static int snd_hda_query_supported_pcm(struct hda_codec *codec, hda_nid_t nid,
}
/**
- * snd_hda_is_supported_format - check whether the given node supports
- * the format val
+ * snd_hda_is_supported_format - Check the validity of the format
+ * @codec: HD-audio codec
+ * @nid: NID to check
+ * @format: the HD-audio format value to check
+ *
+ * Check whether the given node supports the format value.
*
* Returns 1 if supported, 0 if not.
*/
@@ -2877,51 +3205,36 @@ static int set_pcm_default_values(struct hda_codec *codec,
return 0;
}
+/* global */
+const char *snd_hda_pcm_type_name[HDA_PCM_NTYPES] = {
+ "Audio", "SPDIF", "HDMI", "Modem"
+};
+
/*
* get the empty PCM device number to assign
*/
static int get_empty_pcm_device(struct hda_bus *bus, int type)
{
- static const char *dev_name[HDA_PCM_NTYPES] = {
- "Audio", "SPDIF", "HDMI", "Modem"
- };
- /* starting device index for each PCM type */
- static int dev_idx[HDA_PCM_NTYPES] = {
- [HDA_PCM_TYPE_AUDIO] = 0,
- [HDA_PCM_TYPE_SPDIF] = 1,
- [HDA_PCM_TYPE_HDMI] = 3,
- [HDA_PCM_TYPE_MODEM] = 6
+ /* audio device indices; not linear to keep compatibility */
+ static int audio_idx[HDA_PCM_NTYPES][5] = {
+ [HDA_PCM_TYPE_AUDIO] = { 0, 2, 4, 5, -1 },
+ [HDA_PCM_TYPE_SPDIF] = { 1, -1 },
+ [HDA_PCM_TYPE_HDMI] = { 3, 7, 8, 9, -1 },
+ [HDA_PCM_TYPE_MODEM] = { 6, -1 },
};
- /* normal audio device indices; not linear to keep compatibility */
- static int audio_idx[4] = { 0, 2, 4, 5 };
- int i, dev;
-
- switch (type) {
- case HDA_PCM_TYPE_AUDIO:
- for (i = 0; i < ARRAY_SIZE(audio_idx); i++) {
- dev = audio_idx[i];
- if (!test_bit(dev, bus->pcm_dev_bits))
- goto ok;
- }
- snd_printk(KERN_WARNING "Too many audio devices\n");
- return -EAGAIN;
- case HDA_PCM_TYPE_SPDIF:
- case HDA_PCM_TYPE_HDMI:
- case HDA_PCM_TYPE_MODEM:
- dev = dev_idx[type];
- if (test_bit(dev, bus->pcm_dev_bits)) {
- snd_printk(KERN_WARNING "%s already defined\n",
- dev_name[type]);
- return -EAGAIN;
- }
- break;
- default:
+ int i;
+
+ if (type >= HDA_PCM_NTYPES) {
snd_printk(KERN_WARNING "Invalid PCM type %d\n", type);
return -EINVAL;
}
- ok:
- set_bit(dev, bus->pcm_dev_bits);
- return dev;
+
+ for (i = 0; audio_idx[type][i] >= 0 ; i++)
+ if (!test_and_set_bit(audio_idx[type][i], bus->pcm_dev_bits))
+ return audio_idx[type][i];
+
+ snd_printk(KERN_WARNING "Too many %s devices\n", snd_hda_pcm_type_name[type]);
+ return -EAGAIN;
}
/*
@@ -3159,14 +3472,14 @@ EXPORT_SYMBOL_HDA(snd_hda_check_board_codec_sid_config);
*/
int snd_hda_add_new_ctls(struct hda_codec *codec, struct snd_kcontrol_new *knew)
{
- int err;
+ int err;
for (; knew->name; knew++) {
struct snd_kcontrol *kctl;
kctl = snd_ctl_new1(knew, codec);
if (!kctl)
return -ENOMEM;
- err = snd_hda_ctl_add(codec, kctl);
+ err = snd_hda_ctl_add(codec, 0, kctl);
if (err < 0) {
if (!codec->addr)
return err;
@@ -3174,7 +3487,7 @@ int snd_hda_add_new_ctls(struct hda_codec *codec, struct snd_kcontrol_new *knew)
if (!kctl)
return -ENOMEM;
kctl->id.device = codec->addr;
- err = snd_hda_ctl_add(codec, kctl);
+ err = snd_hda_ctl_add(codec, 0, kctl);
if (err < 0)
return err;
}
@@ -3207,8 +3520,27 @@ static void hda_keep_power_on(struct hda_codec *codec)
{
codec->power_count++;
codec->power_on = 1;
+ codec->power_jiffies = jiffies;
}
+/* update the power on/off account with the current jiffies */
+void snd_hda_update_power_acct(struct hda_codec *codec)
+{
+ unsigned long delta = jiffies - codec->power_jiffies;
+ if (codec->power_on)
+ codec->power_on_acct += delta;
+ else
+ codec->power_off_acct += delta;
+ codec->power_jiffies += delta;
+}
+
+/**
+ * snd_hda_power_up - Power-up the codec
+ * @codec: HD-audio codec
+ *
+ * Increment the power-up counter and power up the hardware really when
+ * not turned on yet.
+ */
void snd_hda_power_up(struct hda_codec *codec)
{
struct hda_bus *bus = codec->bus;
@@ -3217,7 +3549,9 @@ void snd_hda_power_up(struct hda_codec *codec)
if (codec->power_on || codec->power_transition)
return;
+ snd_hda_update_power_acct(codec);
codec->power_on = 1;
+ codec->power_jiffies = jiffies;
if (bus->ops.pm_notify)
bus->ops.pm_notify(bus);
hda_call_codec_resume(codec);
@@ -3229,9 +3563,13 @@ EXPORT_SYMBOL_HDA(snd_hda_power_up);
#define power_save(codec) \
((codec)->bus->power_save ? *(codec)->bus->power_save : 0)
-#define power_save(codec) \
- ((codec)->bus->power_save ? *(codec)->bus->power_save : 0)
-
+/**
+ * snd_hda_power_down - Power-down the codec
+ * @codec: HD-audio codec
+ *
+ * Decrement the power-up counter and schedules the power-off work if
+ * the counter rearches to zero.
+ */
void snd_hda_power_down(struct hda_codec *codec)
{
--codec->power_count;
@@ -3245,6 +3583,19 @@ void snd_hda_power_down(struct hda_codec *codec)
}
EXPORT_SYMBOL_HDA(snd_hda_power_down);
+/**
+ * snd_hda_check_amp_list_power - Check the amp list and update the power
+ * @codec: HD-audio codec
+ * @check: the object containing an AMP list and the status
+ * @nid: NID to check / update
+ *
+ * Check whether the given NID is in the amp list. If it's in the list,
+ * check the current AMP status, and update the the power-status according
+ * to the mute status.
+ *
+ * This function is supposed to be set or called from the check_power_status
+ * patch ops.
+ */
int snd_hda_check_amp_list_power(struct hda_codec *codec,
struct hda_loopback_check *check,
hda_nid_t nid)
@@ -3286,6 +3637,10 @@ EXPORT_SYMBOL_HDA(snd_hda_check_amp_list_power);
/*
* Channel mode helper
*/
+
+/**
+ * snd_hda_ch_mode_info - Info callback helper for the channel mode enum
+ */
int snd_hda_ch_mode_info(struct hda_codec *codec,
struct snd_ctl_elem_info *uinfo,
const struct hda_channel_mode *chmode,
@@ -3302,6 +3657,9 @@ int snd_hda_ch_mode_info(struct hda_codec *codec,
}
EXPORT_SYMBOL_HDA(snd_hda_ch_mode_info);
+/**
+ * snd_hda_ch_mode_get - Get callback helper for the channel mode enum
+ */
int snd_hda_ch_mode_get(struct hda_codec *codec,
struct snd_ctl_elem_value *ucontrol,
const struct hda_channel_mode *chmode,
@@ -3320,6 +3678,9 @@ int snd_hda_ch_mode_get(struct hda_codec *codec,
}
EXPORT_SYMBOL_HDA(snd_hda_ch_mode_get);
+/**
+ * snd_hda_ch_mode_put - Put callback helper for the channel mode enum
+ */
int snd_hda_ch_mode_put(struct hda_codec *codec,
struct snd_ctl_elem_value *ucontrol,
const struct hda_channel_mode *chmode,
@@ -3344,6 +3705,10 @@ EXPORT_SYMBOL_HDA(snd_hda_ch_mode_put);
/*
* input MUX helper
*/
+
+/**
+ * snd_hda_input_mux_info_info - Info callback helper for the input-mux enum
+ */
int snd_hda_input_mux_info(const struct hda_input_mux *imux,
struct snd_ctl_elem_info *uinfo)
{
@@ -3362,6 +3727,9 @@ int snd_hda_input_mux_info(const struct hda_input_mux *imux,
}
EXPORT_SYMBOL_HDA(snd_hda_input_mux_info);
+/**
+ * snd_hda_input_mux_info_put - Put callback helper for the input-mux enum
+ */
int snd_hda_input_mux_put(struct hda_codec *codec,
const struct hda_input_mux *imux,
struct snd_ctl_elem_value *ucontrol,
@@ -3421,8 +3789,29 @@ static void cleanup_dig_out_stream(struct hda_codec *codec, hda_nid_t nid)
}
}
-/*
- * open the digital out in the exclusive mode
+/**
+ * snd_hda_bus_reboot_notify - call the reboot notifier of each codec
+ * @bus: HD-audio bus
+ */
+void snd_hda_bus_reboot_notify(struct hda_bus *bus)
+{
+ struct hda_codec *codec;
+
+ if (!bus)
+ return;
+ list_for_each_entry(codec, &bus->codec_list, list) {
+#ifdef CONFIG_SND_HDA_POWER_SAVE
+ if (!codec->power_on)
+ continue;
+#endif
+ if (codec->patch_ops.reboot_notify)
+ codec->patch_ops.reboot_notify(codec);
+ }
+}
+EXPORT_SYMBOL_HDA(snd_hda_bus_reboot_notify);
+
+/**
+ * snd_hda_multi_out_dig_open - open the digital out in the exclusive mode
*/
int snd_hda_multi_out_dig_open(struct hda_codec *codec,
struct hda_multi_out *mout)
@@ -3437,6 +3826,9 @@ int snd_hda_multi_out_dig_open(struct hda_codec *codec,
}
EXPORT_SYMBOL_HDA(snd_hda_multi_out_dig_open);
+/**
+ * snd_hda_multi_out_dig_prepare - prepare the digital out stream
+ */
int snd_hda_multi_out_dig_prepare(struct hda_codec *codec,
struct hda_multi_out *mout,
unsigned int stream_tag,
@@ -3450,6 +3842,9 @@ int snd_hda_multi_out_dig_prepare(struct hda_codec *codec,
}
EXPORT_SYMBOL_HDA(snd_hda_multi_out_dig_prepare);
+/**
+ * snd_hda_multi_out_dig_cleanup - clean-up the digital out stream
+ */
int snd_hda_multi_out_dig_cleanup(struct hda_codec *codec,
struct hda_multi_out *mout)
{
@@ -3460,8 +3855,8 @@ int snd_hda_multi_out_dig_cleanup(struct hda_codec *codec,
}
EXPORT_SYMBOL_HDA(snd_hda_multi_out_dig_cleanup);
-/*
- * release the digital out
+/**
+ * snd_hda_multi_out_dig_close - release the digital out stream
*/
int snd_hda_multi_out_dig_close(struct hda_codec *codec,
struct hda_multi_out *mout)
@@ -3473,8 +3868,12 @@ int snd_hda_multi_out_dig_close(struct hda_codec *codec,
}
EXPORT_SYMBOL_HDA(snd_hda_multi_out_dig_close);
-/*
- * set up more restrictions for analog out
+/**
+ * snd_hda_multi_out_analog_open - open analog outputs
+ *
+ * Open analog outputs and set up the hw-constraints.
+ * If the digital outputs can be opened as slave, open the digital
+ * outputs, too.
*/
int snd_hda_multi_out_analog_open(struct hda_codec *codec,
struct hda_multi_out *mout,
@@ -3519,9 +3918,11 @@ int snd_hda_multi_out_analog_open(struct hda_codec *codec,
}
EXPORT_SYMBOL_HDA(snd_hda_multi_out_analog_open);
-/*
- * set up the i/o for analog out
- * when the digital out is available, copy the front out to digital out, too.
+/**
+ * snd_hda_multi_out_analog_prepare - Preapre the analog outputs.
+ *
+ * Set up the i/o for analog out.
+ * When the digital out is available, copy the front out to digital out, too.
*/
int snd_hda_multi_out_analog_prepare(struct hda_codec *codec,
struct hda_multi_out *mout,
@@ -3578,8 +3979,8 @@ int snd_hda_multi_out_analog_prepare(struct hda_codec *codec,
}
EXPORT_SYMBOL_HDA(snd_hda_multi_out_analog_prepare);
-/*
- * clean up the setting for analog out
+/**
+ * snd_hda_multi_out_analog_cleanup - clean up the setting for analog out
*/
int snd_hda_multi_out_analog_cleanup(struct hda_codec *codec,
struct hda_multi_out *mout)
@@ -3965,8 +4366,14 @@ EXPORT_SYMBOL_HDA(snd_hda_resume);
* generic arrays
*/
-/* get a new element from the given array
- * if it exceeds the pre-allocated array size, re-allocate the array
+/**
+ * snd_array_new - get a new element from the given array
+ * @array: the array object
+ *
+ * Get a new element from the given array. If it exceeds the
+ * pre-allocated array size, re-allocate the array.
+ *
+ * Returns NULL if allocation failed.
*/
void *snd_array_new(struct snd_array *array)
{
@@ -3990,7 +4397,10 @@ void *snd_array_new(struct snd_array *array)
}
EXPORT_SYMBOL_HDA(snd_array_new);
-/* free the given array elements */
+/**
+ * snd_array_free - free the given array elements
+ * @array: the array object
+ */
void snd_array_free(struct snd_array *array)
{
kfree(array->list);
@@ -4000,7 +4410,12 @@ void snd_array_free(struct snd_array *array)
}
EXPORT_SYMBOL_HDA(snd_array_free);
-/*
+/**
+ * snd_print_pcm_rates - Print the supported PCM rates to the string buffer
+ * @pcm: PCM caps bits
+ * @buf: the string buffer to write
+ * @buflen: the max buffer length
+ *
* used by hda_proc.c and hda_eld.c
*/
void snd_print_pcm_rates(int pcm, char *buf, int buflen)
@@ -4019,6 +4434,14 @@ void snd_print_pcm_rates(int pcm, char *buf, int buflen)
}
EXPORT_SYMBOL_HDA(snd_print_pcm_rates);
+/**
+ * snd_print_pcm_bits - Print the supported PCM fmt bits to the string buffer
+ * @pcm: PCM caps bits
+ * @buf: the string buffer to write
+ * @buflen: the max buffer length
+ *
+ * used by hda_proc.c and hda_eld.c
+ */
void snd_print_pcm_bits(int pcm, char *buf, int buflen)
{
static unsigned int bits[] = { 8, 16, 20, 24, 32 };
diff --git a/sound/pci/hda/hda_codec.h b/sound/pci/hda/hda_codec.h
index 99552fb5f756..2d627613aea3 100644
--- a/sound/pci/hda/hda_codec.h
+++ b/sound/pci/hda/hda_codec.h
@@ -286,6 +286,10 @@ enum {
#define AC_PWRST_D1SUP (1<<1)
#define AC_PWRST_D2SUP (1<<2)
#define AC_PWRST_D3SUP (1<<3)
+#define AC_PWRST_D3COLDSUP (1<<4)
+#define AC_PWRST_S3D3COLDSUP (1<<29)
+#define AC_PWRST_CLKSTOP (1<<30)
+#define AC_PWRST_EPSS (1U<<31)
/* Power state values */
#define AC_PWRST_SETTING (0xf<<0)
@@ -674,6 +678,7 @@ struct hda_codec_ops {
#ifdef CONFIG_SND_HDA_POWER_SAVE
int (*check_power_status)(struct hda_codec *codec, hda_nid_t nid);
#endif
+ void (*reboot_notify)(struct hda_codec *codec);
};
/* record for amp information cache */
@@ -771,6 +776,7 @@ struct hda_codec {
/* beep device */
struct hda_beep *beep;
+ unsigned int beep_mode;
/* widget capabilities cache */
unsigned int num_nodes;
@@ -811,6 +817,9 @@ struct hda_codec {
unsigned int power_transition :1; /* power-state in transition */
int power_count; /* current (global) power refcount */
struct delayed_work power_work; /* delayed task for powerdown */
+ unsigned long power_on_acct;
+ unsigned long power_off_acct;
+ unsigned long power_jiffies;
#endif
/* codec-specific additional proc output */
@@ -910,6 +919,7 @@ int snd_hda_is_supported_format(struct hda_codec *codec, hda_nid_t nid,
* Misc
*/
void snd_hda_get_codec_name(struct hda_codec *codec, char *name, int namelen);
+void snd_hda_bus_reboot_notify(struct hda_bus *bus);
/*
* power management
@@ -933,6 +943,7 @@ const char *snd_hda_get_jack_location(u32 cfg);
void snd_hda_power_up(struct hda_codec *codec);
void snd_hda_power_down(struct hda_codec *codec);
#define snd_hda_codec_needs_resume(codec) codec->power_count
+void snd_hda_update_power_acct(struct hda_codec *codec);
#else
static inline void snd_hda_power_up(struct hda_codec *codec) {}
static inline void snd_hda_power_down(struct hda_codec *codec) {}
diff --git a/sound/pci/hda/hda_eld.c b/sound/pci/hda/hda_eld.c
index 9446a5abea13..4228f2fe5956 100644
--- a/sound/pci/hda/hda_eld.c
+++ b/sound/pci/hda/hda_eld.c
@@ -309,17 +309,12 @@ out_fail:
return -EINVAL;
}
-static int hdmi_present_sense(struct hda_codec *codec, hda_nid_t nid)
-{
- return snd_hda_codec_read(codec, nid, 0, AC_VERB_GET_PIN_SENSE, 0);
-}
-
static int hdmi_eld_valid(struct hda_codec *codec, hda_nid_t nid)
{
int eldv;
int present;
- present = hdmi_present_sense(codec, nid);
+ present = snd_hda_pin_sense(codec, nid);
eldv = (present & AC_PINSENSE_ELDV);
present = (present & AC_PINSENSE_PRESENCE);
@@ -477,6 +472,8 @@ static void hdmi_print_eld_info(struct snd_info_entry *entry,
[4 ... 7] = "reserved"
};
+ snd_iprintf(buffer, "monitor_present\t\t%d\n", e->monitor_present);
+ snd_iprintf(buffer, "eld_valid\t\t%d\n", e->eld_valid);
snd_iprintf(buffer, "monitor_name\t\t%s\n", e->monitor_name);
snd_iprintf(buffer, "connection_type\t\t%s\n",
eld_connection_type_names[e->conn_type]);
@@ -518,7 +515,11 @@ static void hdmi_write_eld_info(struct snd_info_entry *entry,
* monitor_name manufacture_id product_id
* eld_version edid_version
*/
- if (!strcmp(name, "connection_type"))
+ if (!strcmp(name, "monitor_present"))
+ e->monitor_present = val;
+ else if (!strcmp(name, "eld_valid"))
+ e->eld_valid = val;
+ else if (!strcmp(name, "connection_type"))
e->conn_type = val;
else if (!strcmp(name, "port_id"))
e->port_id = val;
@@ -560,13 +561,14 @@ static void hdmi_write_eld_info(struct snd_info_entry *entry,
}
-int snd_hda_eld_proc_new(struct hda_codec *codec, struct hdmi_eld *eld)
+int snd_hda_eld_proc_new(struct hda_codec *codec, struct hdmi_eld *eld,
+ int index)
{
char name[32];
struct snd_info_entry *entry;
int err;
- snprintf(name, sizeof(name), "eld#%d", codec->addr);
+ snprintf(name, sizeof(name), "eld#%d.%d", codec->addr, index);
err = snd_card_proc_new(codec->bus->card, name, &entry);
if (err < 0)
return err;
diff --git a/sound/pci/hda/hda_generic.c b/sound/pci/hda/hda_generic.c
index b36f6c5a92df..092c6a7c2ff3 100644
--- a/sound/pci/hda/hda_generic.c
+++ b/sound/pci/hda/hda_generic.c
@@ -727,7 +727,8 @@ static int create_mixer(struct hda_codec *codec, struct hda_gnode *node,
if (is_loopback)
add_input_loopback(codec, node->nid, HDA_INPUT, index);
snd_printdd("[%s] NID=0x%x, DIR=IN, IDX=0x%x\n", name, node->nid, index);
- err = snd_hda_ctl_add(codec, snd_ctl_new1(&knew, codec));
+ err = snd_hda_ctl_add(codec, node->nid,
+ snd_ctl_new1(&knew, codec));
if (err < 0)
return err;
created = 1;
@@ -737,7 +738,8 @@ static int create_mixer(struct hda_codec *codec, struct hda_gnode *node,
if (is_loopback)
add_input_loopback(codec, node->nid, HDA_OUTPUT, 0);
snd_printdd("[%s] NID=0x%x, DIR=OUT\n", name, node->nid);
- err = snd_hda_ctl_add(codec, snd_ctl_new1(&knew, codec));
+ err = snd_hda_ctl_add(codec, node->nid,
+ snd_ctl_new1(&knew, codec));
if (err < 0)
return err;
created = 1;
@@ -751,7 +753,8 @@ static int create_mixer(struct hda_codec *codec, struct hda_gnode *node,
(node->amp_in_caps & AC_AMPCAP_NUM_STEPS)) {
knew = (struct snd_kcontrol_new)HDA_CODEC_VOLUME(name, node->nid, index, HDA_INPUT);
snd_printdd("[%s] NID=0x%x, DIR=IN, IDX=0x%x\n", name, node->nid, index);
- err = snd_hda_ctl_add(codec, snd_ctl_new1(&knew, codec));
+ err = snd_hda_ctl_add(codec, node->nid,
+ snd_ctl_new1(&knew, codec));
if (err < 0)
return err;
created = 1;
@@ -759,7 +762,8 @@ static int create_mixer(struct hda_codec *codec, struct hda_gnode *node,
(node->amp_out_caps & AC_AMPCAP_NUM_STEPS)) {
knew = (struct snd_kcontrol_new)HDA_CODEC_VOLUME(name, node->nid, 0, HDA_OUTPUT);
snd_printdd("[%s] NID=0x%x, DIR=OUT\n", name, node->nid);
- err = snd_hda_ctl_add(codec, snd_ctl_new1(&knew, codec));
+ err = snd_hda_ctl_add(codec, node->nid,
+ snd_ctl_new1(&knew, codec));
if (err < 0)
return err;
created = 1;
@@ -857,7 +861,7 @@ static int build_input_controls(struct hda_codec *codec)
}
/* create input MUX if multiple sources are available */
- err = snd_hda_ctl_add(codec, snd_ctl_new1(&cap_sel, codec));
+ err = snd_hda_ctl_add(codec, 0, snd_ctl_new1(&cap_sel, codec));
if (err < 0)
return err;
@@ -875,7 +879,8 @@ static int build_input_controls(struct hda_codec *codec)
HDA_CODEC_VOLUME(name, adc_node->nid,
spec->input_mux.items[i].index,
HDA_INPUT);
- err = snd_hda_ctl_add(codec, snd_ctl_new1(&knew, codec));
+ err = snd_hda_ctl_add(codec, adc_node->nid,
+ snd_ctl_new1(&knew, codec));
if (err < 0)
return err;
}
diff --git a/sound/pci/hda/hda_hwdep.c b/sound/pci/hda/hda_hwdep.c
index cc24e6721d74..d24328661c6a 100644
--- a/sound/pci/hda/hda_hwdep.c
+++ b/sound/pci/hda/hda_hwdep.c
@@ -154,6 +154,44 @@ int /*__devinit*/ snd_hda_create_hwdep(struct hda_codec *codec)
return 0;
}
+#ifdef CONFIG_SND_HDA_POWER_SAVE
+static ssize_t power_on_acct_show(struct device *dev,
+ struct device_attribute *attr,
+ char *buf)
+{
+ struct snd_hwdep *hwdep = dev_get_drvdata(dev);
+ struct hda_codec *codec = hwdep->private_data;
+ snd_hda_update_power_acct(codec);
+ return sprintf(buf, "%u\n", jiffies_to_msecs(codec->power_on_acct));
+}
+
+static ssize_t power_off_acct_show(struct device *dev,
+ struct device_attribute *attr,
+ char *buf)
+{
+ struct snd_hwdep *hwdep = dev_get_drvdata(dev);
+ struct hda_codec *codec = hwdep->private_data;
+ snd_hda_update_power_acct(codec);
+ return sprintf(buf, "%u\n", jiffies_to_msecs(codec->power_off_acct));
+}
+
+static struct device_attribute power_attrs[] = {
+ __ATTR_RO(power_on_acct),
+ __ATTR_RO(power_off_acct),
+};
+
+int snd_hda_hwdep_add_power_sysfs(struct hda_codec *codec)
+{
+ struct snd_hwdep *hwdep = codec->hwdep;
+ int i;
+
+ for (i = 0; i < ARRAY_SIZE(power_attrs); i++)
+ snd_add_device_sysfs_file(SNDRV_DEVICE_TYPE_HWDEP, hwdep->card,
+ hwdep->device, &power_attrs[i]);
+ return 0;
+}
+#endif /* CONFIG_SND_HDA_POWER_SAVE */
+
#ifdef CONFIG_SND_HDA_RECONFIG
/*
diff --git a/sound/pci/hda/hda_intel.c b/sound/pci/hda/hda_intel.c
index 6517f589d01d..d822bfc6cad6 100644
--- a/sound/pci/hda/hda_intel.c
+++ b/sound/pci/hda/hda_intel.c
@@ -60,10 +60,14 @@ static int bdl_pos_adj[SNDRV_CARDS] = {[0 ... (SNDRV_CARDS-1)] = -1};
static int probe_mask[SNDRV_CARDS] = {[0 ... (SNDRV_CARDS-1)] = -1};
static int probe_only[SNDRV_CARDS];
static int single_cmd;
-static int enable_msi;
+static int enable_msi = -1;
#ifdef CONFIG_SND_HDA_PATCH_LOADER
static char *patch[SNDRV_CARDS];
#endif
+#ifdef CONFIG_SND_HDA_INPUT_BEEP
+static int beep_mode[SNDRV_CARDS] = {[0 ... (SNDRV_CARDS-1)] =
+ CONFIG_SND_HDA_INPUT_BEEP_MODE};
+#endif
module_param_array(index, int, NULL, 0444);
MODULE_PARM_DESC(index, "Index value for Intel HD audio interface.");
@@ -91,6 +95,11 @@ MODULE_PARM_DESC(enable_msi, "Enable Message Signaled Interrupt (MSI)");
module_param_array(patch, charp, NULL, 0444);
MODULE_PARM_DESC(patch, "Patch file for Intel HD audio interface.");
#endif
+#ifdef CONFIG_SND_HDA_INPUT_BEEP
+module_param_array(beep_mode, int, NULL, 0444);
+MODULE_PARM_DESC(beep_mode, "Select HDA Beep registration mode "
+ "(0=off, 1=on, 2=mute switch on/off) (default=1).");
+#endif
#ifdef CONFIG_SND_HDA_POWER_SAVE
static int power_save = CONFIG_SND_HDA_POWER_SAVE_DEFAULT;
@@ -404,6 +413,7 @@ struct azx {
unsigned short codec_mask;
int codec_probe_mask; /* copied from probe_mask option */
struct hda_bus *bus;
+ unsigned int beep_mode;
/* CORB/RIRB */
struct azx_rb corb;
@@ -677,6 +687,14 @@ static unsigned int azx_rirb_get_response(struct hda_bus *bus,
}
}
+ if (!chip->polling_mode) {
+ snd_printk(KERN_WARNING SFX "azx_get_response timeout, "
+ "switching to polling mode: last cmd=0x%08x\n",
+ chip->last_cmd[addr]);
+ chip->polling_mode = 1;
+ goto again;
+ }
+
if (chip->msi) {
snd_printk(KERN_WARNING SFX "No response from codec, "
"disabling MSI: last cmd=0x%08x\n",
@@ -692,14 +710,6 @@ static unsigned int azx_rirb_get_response(struct hda_bus *bus,
goto again;
}
- if (!chip->polling_mode) {
- snd_printk(KERN_WARNING SFX "azx_get_response timeout, "
- "switching to polling mode: last cmd=0x%08x\n",
- chip->last_cmd[addr]);
- chip->polling_mode = 1;
- goto again;
- }
-
if (chip->probing) {
/* If this critical timeout happens during the codec probing
* phase, this is likely an access to a non-existing codec
@@ -1404,6 +1414,7 @@ static int __devinit azx_codec_create(struct azx *chip, const char *model)
err = snd_hda_codec_new(chip->bus, c, &codec);
if (err < 0)
continue;
+ codec->beep_mode = chip->beep_mode;
codecs++;
}
}
@@ -2154,6 +2165,7 @@ static int azx_resume(struct pci_dev *pci)
static int azx_halt(struct notifier_block *nb, unsigned long event, void *buf)
{
struct azx *chip = container_of(nb, struct azx, reboot_notifier);
+ snd_hda_bus_reboot_notify(chip->bus);
azx_stop_chip(chip);
return NOTIFY_OK;
}
@@ -2221,7 +2233,9 @@ static int azx_dev_free(struct snd_device *device)
static struct snd_pci_quirk position_fix_list[] __devinitdata = {
SND_PCI_QUIRK(0x1028, 0x01cc, "Dell D820", POS_FIX_LPIB),
SND_PCI_QUIRK(0x1028, 0x01de, "Dell Precision 390", POS_FIX_LPIB),
+ SND_PCI_QUIRK(0x103c, 0x306d, "HP dv3", POS_FIX_LPIB),
SND_PCI_QUIRK(0x1043, 0x813d, "ASUS P5AD2", POS_FIX_LPIB),
+ SND_PCI_QUIRK(0x1462, 0x1002, "MSI Wind U115", POS_FIX_LPIB),
{}
};
@@ -2304,11 +2318,9 @@ static void __devinit check_probe_mask(struct azx *chip, int dev)
}
/*
- * white-list for enable_msi
+ * white/black-list for enable_msi
*/
-static struct snd_pci_quirk msi_white_list[] __devinitdata = {
- SND_PCI_QUIRK(0x103c, 0x30f7, "HP Pavilion dv4t-1300", 1),
- SND_PCI_QUIRK(0x103c, 0x3607, "HP Compa CQ40", 1),
+static struct snd_pci_quirk msi_black_list[] __devinitdata = {
{}
};
@@ -2316,10 +2328,12 @@ static void __devinit check_msi(struct azx *chip)
{
const struct snd_pci_quirk *q;
- chip->msi = enable_msi;
- if (chip->msi)
+ if (enable_msi >= 0) {
+ chip->msi = !!enable_msi;
return;
- q = snd_pci_quirk_lookup(chip->pci, msi_white_list);
+ }
+ chip->msi = 1; /* enable MSI as default */
+ q = snd_pci_quirk_lookup(chip->pci, msi_black_list);
if (q) {
printk(KERN_INFO
"hda_intel: msi for device %04x:%04x set to %d\n",
@@ -2578,6 +2592,10 @@ static int __devinit azx_probe(struct pci_dev *pci,
goto out_free;
card->private_data = chip;
+#ifdef CONFIG_SND_HDA_INPUT_BEEP
+ chip->beep_mode = beep_mode[dev];
+#endif
+
/* create codec instances */
err = azx_codec_create(chip, model[dev]);
if (err < 0)
diff --git a/sound/pci/hda/hda_local.h b/sound/pci/hda/hda_local.h
index 5f1dcc59002b..5778ae882b83 100644
--- a/sound/pci/hda/hda_local.h
+++ b/sound/pci/hda/hda_local.h
@@ -23,6 +23,15 @@
#ifndef __SOUND_HDA_LOCAL_H
#define __SOUND_HDA_LOCAL_H
+/* We abuse kcontrol_new.subdev field to pass the NID corresponding to
+ * the given new control. If id.subdev has a bit flag HDA_SUBDEV_NID_FLAG,
+ * snd_hda_ctl_add() takes the lower-bit subdev value as a valid NID.
+ *
+ * Note that the subdevice field is cleared again before the real registration
+ * in snd_hda_ctl_add(), so that this value won't appear in the outside.
+ */
+#define HDA_SUBDEV_NID_FLAG (1U << 31)
+
/*
* for mixer controls
*/
@@ -33,6 +42,7 @@
/* mono volume with index (index=0,1,...) (channel=1,2) */
#define HDA_CODEC_VOLUME_MONO_IDX(xname, xcidx, nid, channel, xindex, direction) \
{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, .index = xcidx, \
+ .subdevice = HDA_SUBDEV_NID_FLAG | (nid), \
.access = SNDRV_CTL_ELEM_ACCESS_READWRITE | \
SNDRV_CTL_ELEM_ACCESS_TLV_READ | \
SNDRV_CTL_ELEM_ACCESS_TLV_CALLBACK, \
@@ -53,6 +63,7 @@
/* mono mute switch with index (index=0,1,...) (channel=1,2) */
#define HDA_CODEC_MUTE_MONO_IDX(xname, xcidx, nid, channel, xindex, direction) \
{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, .index = xcidx, \
+ .subdevice = HDA_SUBDEV_NID_FLAG | (nid), \
.info = snd_hda_mixer_amp_switch_info, \
.get = snd_hda_mixer_amp_switch_get, \
.put = snd_hda_mixer_amp_switch_put, \
@@ -66,6 +77,28 @@
/* stereo mute switch */
#define HDA_CODEC_MUTE(xname, nid, xindex, direction) \
HDA_CODEC_MUTE_MONO(xname, nid, 3, xindex, direction)
+#ifdef CONFIG_SND_HDA_INPUT_BEEP
+/* special beep mono mute switch with index (index=0,1,...) (channel=1,2) */
+#define HDA_CODEC_MUTE_BEEP_MONO_IDX(xname, xcidx, nid, channel, xindex, direction) \
+ { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, .index = xcidx, \
+ .subdevice = HDA_SUBDEV_NID_FLAG | (nid), \
+ .info = snd_hda_mixer_amp_switch_info, \
+ .get = snd_hda_mixer_amp_switch_get, \
+ .put = snd_hda_mixer_amp_switch_put_beep, \
+ .private_value = HDA_COMPOSE_AMP_VAL(nid, channel, xindex, direction) }
+#else
+/* no digital beep - just the standard one */
+#define HDA_CODEC_MUTE_BEEP_MONO_IDX(xname, xcidx, nid, ch, xidx, dir) \
+ HDA_CODEC_MUTE_MONO_IDX(xname, xcidx, nid, ch, xidx, dir)
+#endif /* CONFIG_SND_HDA_INPUT_BEEP */
+/* special beep mono mute switch */
+#define HDA_CODEC_MUTE_BEEP_MONO(xname, nid, channel, xindex, direction) \
+ HDA_CODEC_MUTE_BEEP_MONO_IDX(xname, 0, nid, channel, xindex, direction)
+/* special beep stereo mute switch */
+#define HDA_CODEC_MUTE_BEEP(xname, nid, xindex, direction) \
+ HDA_CODEC_MUTE_BEEP_MONO(xname, nid, 3, xindex, direction)
+
+extern const char *snd_hda_pcm_type_name[];
int snd_hda_mixer_amp_volume_info(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_info *uinfo);
@@ -81,6 +114,10 @@ int snd_hda_mixer_amp_switch_get(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol);
int snd_hda_mixer_amp_switch_put(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol);
+#ifdef CONFIG_SND_HDA_INPUT_BEEP
+int snd_hda_mixer_amp_switch_put_beep(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol);
+#endif
/* lowlevel accessor with caching; use carefully */
int snd_hda_codec_amp_read(struct hda_codec *codec, hda_nid_t nid, int ch,
int direction, int index);
@@ -424,8 +461,16 @@ u32 query_amp_caps(struct hda_codec *codec, hda_nid_t nid, int direction);
int snd_hda_override_amp_caps(struct hda_codec *codec, hda_nid_t nid, int dir,
unsigned int caps);
u32 snd_hda_query_pin_caps(struct hda_codec *codec, hda_nid_t nid);
+u32 snd_hda_pin_sense(struct hda_codec *codec, hda_nid_t nid);
+int snd_hda_jack_detect(struct hda_codec *codec, hda_nid_t nid);
-int snd_hda_ctl_add(struct hda_codec *codec, struct snd_kcontrol *kctl);
+struct hda_nid_item {
+ struct snd_kcontrol *kctl;
+ hda_nid_t nid;
+};
+
+int snd_hda_ctl_add(struct hda_codec *codec, hda_nid_t nid,
+ struct snd_kcontrol *kctl);
void snd_hda_ctls_clear(struct hda_codec *codec);
/*
@@ -437,6 +482,15 @@ int snd_hda_create_hwdep(struct hda_codec *codec);
static inline int snd_hda_create_hwdep(struct hda_codec *codec) { return 0; }
#endif
+#if defined(CONFIG_SND_HDA_POWER_SAVE) && defined(CONFIG_SND_HDA_HWDEP)
+int snd_hda_hwdep_add_power_sysfs(struct hda_codec *codec);
+#else
+static inline int snd_hda_hwdep_add_power_sysfs(struct hda_codec *codec)
+{
+ return 0;
+}
+#endif
+
#ifdef CONFIG_SND_HDA_RECONFIG
int snd_hda_hwdep_add_sysfs(struct hda_codec *codec);
#else
@@ -490,7 +544,8 @@ int snd_hda_check_amp_list_power(struct hda_codec *codec,
* AMP control callbacks
*/
/* retrieve parameters from private_value */
-#define get_amp_nid(kc) ((kc)->private_value & 0xffff)
+#define get_amp_nid_(pv) ((pv) & 0xffff)
+#define get_amp_nid(kc) get_amp_nid_((kc)->private_value)
#define get_amp_channels(kc) (((kc)->private_value >> 16) & 0x3)
#define get_amp_direction(kc) (((kc)->private_value >> 18) & 0x1)
#define get_amp_index(kc) (((kc)->private_value >> 19) & 0xf)
@@ -516,9 +571,11 @@ struct cea_sad {
* ELD: EDID Like Data
*/
struct hdmi_eld {
+ bool monitor_present;
+ bool eld_valid;
int eld_size;
int baseline_len;
- int eld_ver; /* (eld_ver == 0) indicates invalid ELD */
+ int eld_ver;
int cea_edid_ver;
char monitor_name[ELD_MAX_MNL + 1];
int manufacture_id;
@@ -541,11 +598,13 @@ int snd_hdmi_get_eld(struct hdmi_eld *, struct hda_codec *, hda_nid_t);
void snd_hdmi_show_eld(struct hdmi_eld *eld);
#ifdef CONFIG_PROC_FS
-int snd_hda_eld_proc_new(struct hda_codec *codec, struct hdmi_eld *eld);
+int snd_hda_eld_proc_new(struct hda_codec *codec, struct hdmi_eld *eld,
+ int index);
void snd_hda_eld_proc_free(struct hda_codec *codec, struct hdmi_eld *eld);
#else
static inline int snd_hda_eld_proc_new(struct hda_codec *codec,
- struct hdmi_eld *eld)
+ struct hdmi_eld *eld,
+ int index)
{
return 0;
}
diff --git a/sound/pci/hda/hda_proc.c b/sound/pci/hda/hda_proc.c
index 95f24e4729f8..09476fc1ab64 100644
--- a/sound/pci/hda/hda_proc.c
+++ b/sound/pci/hda/hda_proc.c
@@ -26,6 +26,21 @@
#include "hda_codec.h"
#include "hda_local.h"
+static char *bits_names(unsigned int bits, char *names[], int size)
+{
+ int i, n;
+ static char buf[128];
+
+ for (i = 0, n = 0; i < size; i++) {
+ if (bits & (1U<<i) && names[i])
+ n += snprintf(buf + n, sizeof(buf) - n, " %s",
+ names[i]);
+ }
+ buf[n] = '\0';
+
+ return buf;
+}
+
static const char *get_wid_type_name(unsigned int wid_value)
{
static char *names[16] = {
@@ -46,6 +61,41 @@ static const char *get_wid_type_name(unsigned int wid_value)
return "UNKNOWN Widget";
}
+static void print_nid_mixers(struct snd_info_buffer *buffer,
+ struct hda_codec *codec, hda_nid_t nid)
+{
+ int i;
+ struct hda_nid_item *items = codec->mixers.list;
+ struct snd_kcontrol *kctl;
+ for (i = 0; i < codec->mixers.used; i++) {
+ if (items[i].nid == nid) {
+ kctl = items[i].kctl;
+ snd_iprintf(buffer,
+ " Control: name=\"%s\", index=%i, device=%i\n",
+ kctl->id.name, kctl->id.index, kctl->id.device);
+ }
+ }
+}
+
+static void print_nid_pcms(struct snd_info_buffer *buffer,
+ struct hda_codec *codec, hda_nid_t nid)
+{
+ int pcm, type;
+ struct hda_pcm *cpcm;
+ for (pcm = 0; pcm < codec->num_pcms; pcm++) {
+ cpcm = &codec->pcm_info[pcm];
+ for (type = 0; type < 2; type++) {
+ if (cpcm->stream[type].nid != nid || cpcm->pcm == NULL)
+ continue;
+ snd_iprintf(buffer, " Device: name=\"%s\", "
+ "type=\"%s\", device=%i\n",
+ cpcm->name,
+ snd_hda_pcm_type_name[cpcm->pcm_type],
+ cpcm->pcm->device);
+ }
+ }
+}
+
static void print_amp_caps(struct snd_info_buffer *buffer,
struct hda_codec *codec, hda_nid_t nid, int dir)
{
@@ -363,8 +413,24 @@ static const char *get_pwr_state(u32 state)
static void print_power_state(struct snd_info_buffer *buffer,
struct hda_codec *codec, hda_nid_t nid)
{
+ static char *names[] = {
+ [ilog2(AC_PWRST_D0SUP)] = "D0",
+ [ilog2(AC_PWRST_D1SUP)] = "D1",
+ [ilog2(AC_PWRST_D2SUP)] = "D2",
+ [ilog2(AC_PWRST_D3SUP)] = "D3",
+ [ilog2(AC_PWRST_D3COLDSUP)] = "D3cold",
+ [ilog2(AC_PWRST_S3D3COLDSUP)] = "S3D3cold",
+ [ilog2(AC_PWRST_CLKSTOP)] = "CLKSTOP",
+ [ilog2(AC_PWRST_EPSS)] = "EPSS",
+ };
+
+ int sup = snd_hda_param_read(codec, nid, AC_PAR_POWER_STATE);
int pwr = snd_hda_codec_read(codec, nid, 0,
AC_VERB_GET_POWER_STATE, 0);
+ if (sup)
+ snd_iprintf(buffer, " Power states: %s\n",
+ bits_names(sup, names, ARRAY_SIZE(names)));
+
snd_iprintf(buffer, " Power: setting=%s, actual=%s\n",
get_pwr_state(pwr & AC_PWRST_SETTING),
get_pwr_state((pwr & AC_PWRST_ACTUAL) >>
@@ -457,6 +523,7 @@ static void print_gpio(struct snd_info_buffer *buffer,
(data & (1<<i)) ? 1 : 0,
(unsol & (1<<i)) ? 1 : 0);
/* FIXME: add GPO and GPI pin information */
+ print_nid_mixers(buffer, codec, nid);
}
static void print_codec_info(struct snd_info_entry *entry,
@@ -536,6 +603,9 @@ static void print_codec_info(struct snd_info_entry *entry,
snd_iprintf(buffer, " CP");
snd_iprintf(buffer, "\n");
+ print_nid_mixers(buffer, codec, nid);
+ print_nid_pcms(buffer, codec, nid);
+
/* volume knob is a special widget that always have connection
* list
*/
diff --git a/sound/pci/hda/patch_analog.c b/sound/pci/hda/patch_analog.c
index 2d603f6aba63..455a0494f907 100644
--- a/sound/pci/hda/patch_analog.c
+++ b/sound/pci/hda/patch_analog.c
@@ -156,15 +156,19 @@ static const char *ad_slave_sws[] = {
static void ad198x_free_kctls(struct hda_codec *codec);
+#ifdef CONFIG_SND_HDA_INPUT_BEEP
/* additional beep mixers; the actual parameters are overwritten at build */
static struct snd_kcontrol_new ad_beep_mixer[] = {
HDA_CODEC_VOLUME("Beep Playback Volume", 0, 0, HDA_OUTPUT),
- HDA_CODEC_MUTE("Beep Playback Switch", 0, 0, HDA_OUTPUT),
+ HDA_CODEC_MUTE_BEEP("Beep Playback Switch", 0, 0, HDA_OUTPUT),
{ } /* end */
};
#define set_beep_amp(spec, nid, idx, dir) \
((spec)->beep_amp = HDA_COMPOSE_AMP_VAL(nid, 1, idx, dir)) /* mono */
+#else
+#define set_beep_amp(spec, nid, idx, dir) /* NOP */
+#endif
static int ad198x_build_controls(struct hda_codec *codec)
{
@@ -194,6 +198,7 @@ static int ad198x_build_controls(struct hda_codec *codec)
}
/* create beep controls if needed */
+#ifdef CONFIG_SND_HDA_INPUT_BEEP
if (spec->beep_amp) {
struct snd_kcontrol_new *knew;
for (knew = ad_beep_mixer; knew->name; knew++) {
@@ -202,11 +207,14 @@ static int ad198x_build_controls(struct hda_codec *codec)
if (!kctl)
return -ENOMEM;
kctl->private_value = spec->beep_amp;
- err = snd_hda_ctl_add(codec, kctl);
+ err = snd_hda_ctl_add(codec,
+ get_amp_nid_(spec->beep_amp),
+ kctl);
if (err < 0)
return err;
}
}
+#endif
/* if we have no master control, let's create it */
if (!snd_hda_find_mixer_ctl(codec, "Master Playback Volume")) {
@@ -712,10 +720,10 @@ static struct snd_kcontrol_new ad1986a_laptop_intmic_mixers[] = {
static void ad1986a_automic(struct hda_codec *codec)
{
unsigned int present;
- present = snd_hda_codec_read(codec, 0x1f, 0, AC_VERB_GET_PIN_SENSE, 0);
+ present = snd_hda_jack_detect(codec, 0x1f);
/* 0 = 0x1f, 2 = 0x1d, 4 = mixed */
snd_hda_codec_write(codec, 0x0f, 0, AC_VERB_SET_CONNECT_SEL,
- (present & AC_PINSENSE_PRESENCE) ? 0 : 2);
+ present ? 0 : 2);
}
#define AD1986A_MIC_EVENT 0x36
@@ -754,10 +762,8 @@ static void ad1986a_update_hp(struct hda_codec *codec)
static void ad1986a_hp_automute(struct hda_codec *codec)
{
struct ad198x_spec *spec = codec->spec;
- unsigned int present;
- present = snd_hda_codec_read(codec, 0x1a, 0, AC_VERB_GET_PIN_SENSE, 0);
- spec->jack_present = !!(present & 0x80000000);
+ spec->jack_present = snd_hda_jack_detect(codec, 0x1a);
if (spec->inv_jack_detect)
spec->jack_present = !spec->jack_present;
ad1986a_update_hp(codec);
@@ -1547,8 +1553,7 @@ static void ad1981_hp_automute(struct hda_codec *codec)
{
unsigned int present;
- present = snd_hda_codec_read(codec, 0x06, 0,
- AC_VERB_GET_PIN_SENSE, 0) & 0x80000000;
+ present = snd_hda_jack_detect(codec, 0x06);
snd_hda_codec_amp_stereo(codec, 0x05, HDA_OUTPUT, 0,
HDA_AMP_MUTE, present ? HDA_AMP_MUTE : 0);
}
@@ -1568,8 +1573,7 @@ static void ad1981_hp_automic(struct hda_codec *codec)
};
unsigned int present;
- present = snd_hda_codec_read(codec, 0x08, 0,
- AC_VERB_GET_PIN_SENSE, 0) & 0x80000000;
+ present = snd_hda_jack_detect(codec, 0x08);
if (present)
snd_hda_sequence_write(codec, mic_jack_on);
else
@@ -2524,7 +2528,7 @@ static void ad1988_laptop_unsol_event(struct hda_codec *codec, unsigned int res)
{
if ((res >> 26) != AD1988_HP_EVENT)
return;
- if (snd_hda_codec_read(codec, 0x11, 0, AC_VERB_GET_PIN_SENSE, 0) & (1 << 31))
+ if (snd_hda_jack_detect(codec, 0x11))
snd_hda_sequence_write(codec, ad1988_laptop_hp_on);
else
snd_hda_sequence_write(codec, ad1988_laptop_hp_off);
@@ -2569,6 +2573,8 @@ static int add_control(struct ad198x_spec *spec, int type, const char *name,
knew->name = kstrdup(name, GFP_KERNEL);
if (! knew->name)
return -ENOMEM;
+ if (get_amp_nid_(val))
+ knew->subdevice = HDA_SUBDEV_NID_FLAG | get_amp_nid_(val);
knew->private_value = val;
return 0;
}
@@ -3768,8 +3774,7 @@ static void ad1884a_hp_automute(struct hda_codec *codec)
{
unsigned int present;
- present = snd_hda_codec_read(codec, 0x11, 0,
- AC_VERB_GET_PIN_SENSE, 0) & 0x80000000;
+ present = snd_hda_jack_detect(codec, 0x11);
snd_hda_codec_amp_stereo(codec, 0x16, HDA_OUTPUT, 0,
HDA_AMP_MUTE, present ? HDA_AMP_MUTE : 0);
snd_hda_codec_write(codec, 0x16, 0, AC_VERB_SET_EAPD_BTLENABLE,
@@ -3781,8 +3786,7 @@ static void ad1884a_hp_automic(struct hda_codec *codec)
{
unsigned int present;
- present = snd_hda_codec_read(codec, 0x14, 0,
- AC_VERB_GET_PIN_SENSE, 0) & 0x80000000;
+ present = snd_hda_jack_detect(codec, 0x14);
snd_hda_codec_write(codec, 0x0c, 0, AC_VERB_SET_CONNECT_SEL,
present ? 0 : 1);
}
@@ -3817,13 +3821,9 @@ static void ad1884a_laptop_automute(struct hda_codec *codec)
{
unsigned int present;
- present = snd_hda_codec_read(codec, 0x11, 0, AC_VERB_GET_PIN_SENSE, 0);
- present &= AC_PINSENSE_PRESENCE;
- if (!present) {
- present = snd_hda_codec_read(codec, 0x12, 0,
- AC_VERB_GET_PIN_SENSE, 0);
- present &= AC_PINSENSE_PRESENCE;
- }
+ present = snd_hda_jack_detect(codec, 0x11);
+ if (!present)
+ present = snd_hda_jack_detect(codec, 0x12);
snd_hda_codec_amp_stereo(codec, 0x16, HDA_OUTPUT, 0,
HDA_AMP_MUTE, present ? HDA_AMP_MUTE : 0);
snd_hda_codec_write(codec, 0x16, 0, AC_VERB_SET_EAPD_BTLENABLE,
@@ -3835,11 +3835,9 @@ static void ad1884a_laptop_automic(struct hda_codec *codec)
{
unsigned int idx;
- if (snd_hda_codec_read(codec, 0x14, 0, AC_VERB_GET_PIN_SENSE, 0) &
- AC_PINSENSE_PRESENCE)
+ if (snd_hda_jack_detect(codec, 0x14))
idx = 0;
- else if (snd_hda_codec_read(codec, 0x1c, 0, AC_VERB_GET_PIN_SENSE, 0) &
- AC_PINSENSE_PRESENCE)
+ else if (snd_hda_jack_detect(codec, 0x1c))
idx = 4;
else
idx = 1;
@@ -4008,8 +4006,7 @@ static void ad1984a_thinkpad_automute(struct hda_codec *codec)
{
unsigned int present;
- present = snd_hda_codec_read(codec, 0x11, 0, AC_VERB_GET_PIN_SENSE, 0)
- & AC_PINSENSE_PRESENCE;
+ present = snd_hda_jack_detect(codec, 0x11);
snd_hda_codec_amp_stereo(codec, 0x12, HDA_OUTPUT, 0,
HDA_AMP_MUTE, present ? HDA_AMP_MUTE : 0);
}
@@ -4117,14 +4114,12 @@ static struct snd_kcontrol_new ad1984a_touchsmart_mixers[] = {
/* switch to external mic if plugged */
static void ad1984a_touchsmart_automic(struct hda_codec *codec)
{
- if (snd_hda_codec_read(codec, 0x1c, 0,
- AC_VERB_GET_PIN_SENSE, 0) & 0x80000000) {
+ if (snd_hda_jack_detect(codec, 0x1c))
snd_hda_codec_write(codec, 0x0c, 0,
AC_VERB_SET_CONNECT_SEL, 0x4);
- } else {
+ else
snd_hda_codec_write(codec, 0x0c, 0,
AC_VERB_SET_CONNECT_SEL, 0x5);
- }
}
diff --git a/sound/pci/hda/patch_ca0110.c b/sound/pci/hda/patch_ca0110.c
index d08353d3bb7f..af478019088e 100644
--- a/sound/pci/hda/patch_ca0110.c
+++ b/sound/pci/hda/patch_ca0110.c
@@ -144,7 +144,7 @@ static int _add_switch(struct hda_codec *codec, hda_nid_t nid, const char *pfx,
struct snd_kcontrol_new knew =
HDA_CODEC_MUTE_MONO(namestr, nid, chan, 0, type);
sprintf(namestr, "%s %s Switch", pfx, dirstr[dir]);
- return snd_hda_ctl_add(codec, snd_ctl_new1(&knew, codec));
+ return snd_hda_ctl_add(codec, nid, snd_ctl_new1(&knew, codec));
}
static int _add_volume(struct hda_codec *codec, hda_nid_t nid, const char *pfx,
@@ -155,7 +155,7 @@ static int _add_volume(struct hda_codec *codec, hda_nid_t nid, const char *pfx,
struct snd_kcontrol_new knew =
HDA_CODEC_VOLUME_MONO(namestr, nid, chan, 0, type);
sprintf(namestr, "%s %s Volume", pfx, dirstr[dir]);
- return snd_hda_ctl_add(codec, snd_ctl_new1(&knew, codec));
+ return snd_hda_ctl_add(codec, nid, snd_ctl_new1(&knew, codec));
}
#define add_out_switch(codec, nid, pfx) _add_switch(codec, nid, pfx, 3, 0)
diff --git a/sound/pci/hda/patch_cirrus.c b/sound/pci/hda/patch_cirrus.c
index 8ba306856d38..2439e84dcb21 100644
--- a/sound/pci/hda/patch_cirrus.c
+++ b/sound/pci/hda/patch_cirrus.c
@@ -500,7 +500,7 @@ static int add_mute(struct hda_codec *codec, const char *name, int index,
knew.private_value = pval;
snprintf(tmp, sizeof(tmp), "%s %s Switch", name, dir_sfx[dir]);
*kctlp = snd_ctl_new1(&knew, codec);
- return snd_hda_ctl_add(codec, *kctlp);
+ return snd_hda_ctl_add(codec, get_amp_nid_(pval), *kctlp);
}
static int add_volume(struct hda_codec *codec, const char *name,
@@ -513,7 +513,7 @@ static int add_volume(struct hda_codec *codec, const char *name,
knew.private_value = pval;
snprintf(tmp, sizeof(tmp), "%s %s Volume", name, dir_sfx[dir]);
*kctlp = snd_ctl_new1(&knew, codec);
- return snd_hda_ctl_add(codec, *kctlp);
+ return snd_hda_ctl_add(codec, get_amp_nid_(pval), *kctlp);
}
static void fix_volume_caps(struct hda_codec *codec, hda_nid_t dac)
@@ -536,14 +536,14 @@ static int add_vmaster(struct hda_codec *codec, hda_nid_t dac)
spec->vmaster_sw =
snd_ctl_make_virtual_master("Master Playback Switch", NULL);
- err = snd_hda_ctl_add(codec, spec->vmaster_sw);
+ err = snd_hda_ctl_add(codec, dac, spec->vmaster_sw);
if (err < 0)
return err;
snd_hda_set_vmaster_tlv(codec, dac, HDA_OUTPUT, tlv);
spec->vmaster_vol =
snd_ctl_make_virtual_master("Master Playback Volume", tlv);
- err = snd_hda_ctl_add(codec, spec->vmaster_vol);
+ err = snd_hda_ctl_add(codec, dac, spec->vmaster_vol);
if (err < 0)
return err;
return 0;
@@ -756,13 +756,13 @@ static int build_input(struct hda_codec *codec)
if (!kctl)
return -ENOMEM;
kctl->private_value = (long)spec->capture_bind[i];
- err = snd_hda_ctl_add(codec, kctl);
+ err = snd_hda_ctl_add(codec, 0, kctl);
if (err < 0)
return err;
}
if (spec->num_inputs > 1 && !spec->mic_detect) {
- err = snd_hda_ctl_add(codec,
+ err = snd_hda_ctl_add(codec, 0,
snd_ctl_new1(&cs_capture_source, codec));
if (err < 0)
return err;
@@ -807,7 +807,7 @@ static void cs_automute(struct hda_codec *codec)
{
struct cs_spec *spec = codec->spec;
struct auto_pin_cfg *cfg = &spec->autocfg;
- unsigned int caps, present, hp_present;
+ unsigned int caps, hp_present;
hda_nid_t nid;
int i;
@@ -817,12 +817,7 @@ static void cs_automute(struct hda_codec *codec)
caps = snd_hda_query_pin_caps(codec, nid);
if (!(caps & AC_PINCAP_PRES_DETECT))
continue;
- if (caps & AC_PINCAP_TRIG_REQ)
- snd_hda_codec_read(codec, nid, 0,
- AC_VERB_SET_PIN_SENSE, 0);
- present = snd_hda_codec_read(codec, nid, 0,
- AC_VERB_GET_PIN_SENSE, 0);
- hp_present |= (present & AC_PINSENSE_PRESENCE) != 0;
+ hp_present = snd_hda_jack_detect(codec, nid);
if (hp_present)
break;
}
@@ -844,15 +839,11 @@ static void cs_automic(struct hda_codec *codec)
struct cs_spec *spec = codec->spec;
struct auto_pin_cfg *cfg = &spec->autocfg;
hda_nid_t nid;
- unsigned int caps, present;
+ unsigned int present;
nid = cfg->input_pins[spec->automic_idx];
- caps = snd_hda_query_pin_caps(codec, nid);
- if (caps & AC_PINCAP_TRIG_REQ)
- snd_hda_codec_read(codec, nid, 0, AC_VERB_SET_PIN_SENSE, 0);
- present = snd_hda_codec_read(codec, nid, 0,
- AC_VERB_GET_PIN_SENSE, 0);
- if (present & AC_PINSENSE_PRESENCE)
+ present = snd_hda_jack_detect(codec, nid);
+ if (present)
change_cur_input(codec, spec->automic_idx, 0);
else {
unsigned int imic = (spec->automic_idx == AUTO_PIN_MIC) ?
diff --git a/sound/pci/hda/patch_cmedia.c b/sound/pci/hda/patch_cmedia.c
index 780e1a72114a..85c81feb10cf 100644
--- a/sound/pci/hda/patch_cmedia.c
+++ b/sound/pci/hda/patch_cmedia.c
@@ -197,8 +197,8 @@ static struct snd_kcontrol_new cmi9880_basic_mixer[] = {
HDA_CODEC_VOLUME_IDX("Capture Volume", 1, 0x09, 0, HDA_INPUT),
HDA_CODEC_MUTE("Capture Switch", 0x08, 0, HDA_INPUT),
HDA_CODEC_MUTE_IDX("Capture Switch", 1, 0x09, 0, HDA_INPUT),
- HDA_CODEC_VOLUME("PC Speaker Playback Volume", 0x23, 0, HDA_OUTPUT),
- HDA_CODEC_MUTE("PC Speaker Playback Switch", 0x23, 0, HDA_OUTPUT),
+ HDA_CODEC_VOLUME("Beep Playback Volume", 0x23, 0, HDA_OUTPUT),
+ HDA_CODEC_MUTE("Beep Playback Switch", 0x23, 0, HDA_OUTPUT),
{ } /* end */
};
diff --git a/sound/pci/hda/patch_conexant.c b/sound/pci/hda/patch_conexant.c
index 905859d4f4df..a09c03c3f62b 100644
--- a/sound/pci/hda/patch_conexant.c
+++ b/sound/pci/hda/patch_conexant.c
@@ -397,9 +397,7 @@ static void conexant_report_jack(struct hda_codec *codec, hda_nid_t nid)
for (i = 0; i < spec->jacks.used; i++) {
if (jacks->nid == nid) {
unsigned int present;
- present = snd_hda_codec_read(codec, nid, 0,
- AC_VERB_GET_PIN_SENSE, 0) &
- AC_PINSENSE_PRESENCE;
+ present = snd_hda_jack_detect(codec, nid);
present = (present) ? jacks->type : 0 ;
@@ -750,8 +748,7 @@ static void cxt5045_hp_automic(struct hda_codec *codec)
};
unsigned int present;
- present = snd_hda_codec_read(codec, 0x12, 0,
- AC_VERB_GET_PIN_SENSE, 0) & 0x80000000;
+ present = snd_hda_jack_detect(codec, 0x12);
if (present)
snd_hda_sequence_write(codec, mic_jack_on);
else
@@ -765,8 +762,7 @@ static void cxt5045_hp_automute(struct hda_codec *codec)
struct conexant_spec *spec = codec->spec;
unsigned int bits;
- spec->hp_present = snd_hda_codec_read(codec, 0x11, 0,
- AC_VERB_GET_PIN_SENSE, 0) & 0x80000000;
+ spec->hp_present = snd_hda_jack_detect(codec, 0x11);
bits = (spec->hp_present || !spec->cur_eapd) ? HDA_AMP_MUTE : 0;
snd_hda_codec_amp_stereo(codec, 0x10, HDA_OUTPUT, 0,
@@ -1175,9 +1171,10 @@ static int patch_cxt5045(struct hda_codec *codec)
switch (codec->subsystem_id >> 16) {
case 0x103c:
- /* HP laptop has a really bad sound over 0dB on NID 0x17.
- * Fix max PCM level to 0 dB
- * (originall it has 0x2b steps with 0dB offset 0x14)
+ case 0x1734:
+ /* HP & Fujitsu-Siemens laptops have really bad sound over 0dB
+ * on NID 0x17. Fix max PCM level to 0 dB
+ * (originally it has 0x2b steps with 0dB offset 0x14)
*/
snd_hda_override_amp_caps(codec, 0x17, HDA_INPUT,
(0x14 << AC_AMPCAP_OFFSET_SHIFT) |
@@ -1243,8 +1240,7 @@ static void cxt5047_hp_automute(struct hda_codec *codec)
struct conexant_spec *spec = codec->spec;
unsigned int bits;
- spec->hp_present = snd_hda_codec_read(codec, 0x13, 0,
- AC_VERB_GET_PIN_SENSE, 0) & 0x80000000;
+ spec->hp_present = snd_hda_jack_detect(codec, 0x13);
bits = (spec->hp_present || !spec->cur_eapd) ? HDA_AMP_MUTE : 0;
/* See the note in cxt5047_hp_master_sw_put */
@@ -1267,8 +1263,7 @@ static void cxt5047_hp_automic(struct hda_codec *codec)
};
unsigned int present;
- present = snd_hda_codec_read(codec, 0x15, 0,
- AC_VERB_GET_PIN_SENSE, 0) & 0x80000000;
+ present = snd_hda_jack_detect(codec, 0x15);
if (present)
snd_hda_sequence_write(codec, mic_jack_on);
else
@@ -1415,16 +1410,7 @@ static struct snd_kcontrol_new cxt5047_test_mixer[] = {
.get = conexant_mux_enum_get,
.put = conexant_mux_enum_put,
},
- HDA_CODEC_VOLUME("Input-1 Volume", 0x1a, 0x0, HDA_INPUT),
- HDA_CODEC_MUTE("Input-1 Switch", 0x1a, 0x0, HDA_INPUT),
- HDA_CODEC_VOLUME("Input-2 Volume", 0x1a, 0x1, HDA_INPUT),
- HDA_CODEC_MUTE("Input-2 Switch", 0x1a, 0x1, HDA_INPUT),
- HDA_CODEC_VOLUME("Input-3 Volume", 0x1a, 0x2, HDA_INPUT),
- HDA_CODEC_MUTE("Input-3 Switch", 0x1a, 0x2, HDA_INPUT),
- HDA_CODEC_VOLUME("Input-4 Volume", 0x1a, 0x3, HDA_INPUT),
- HDA_CODEC_MUTE("Input-4 Switch", 0x1a, 0x3, HDA_INPUT),
- HDA_CODEC_VOLUME("Input-5 Volume", 0x1a, 0x4, HDA_INPUT),
- HDA_CODEC_MUTE("Input-5 Switch", 0x1a, 0x4, HDA_INPUT),
+ HDA_CODEC_VOLUME("Mic Boost Volume", 0x1a, 0x0, HDA_OUTPUT),
{ } /* end */
};
@@ -1621,9 +1607,7 @@ static void cxt5051_portb_automic(struct hda_codec *codec)
if (spec->no_auto_mic)
return;
- present = snd_hda_codec_read(codec, 0x17, 0,
- AC_VERB_GET_PIN_SENSE, 0) &
- AC_PINSENSE_PRESENCE;
+ present = snd_hda_jack_detect(codec, 0x17);
snd_hda_codec_write(codec, 0x14, 0,
AC_VERB_SET_CONNECT_SEL,
present ? 0x01 : 0x00);
@@ -1638,9 +1622,7 @@ static void cxt5051_portc_automic(struct hda_codec *codec)
if (spec->no_auto_mic)
return;
- present = snd_hda_codec_read(codec, 0x18, 0,
- AC_VERB_GET_PIN_SENSE, 0) &
- AC_PINSENSE_PRESENCE;
+ present = snd_hda_jack_detect(codec, 0x18);
if (present)
spec->cur_adc_idx = 1;
else
@@ -1661,9 +1643,7 @@ static void cxt5051_hp_automute(struct hda_codec *codec)
{
struct conexant_spec *spec = codec->spec;
- spec->hp_present = snd_hda_codec_read(codec, 0x16, 0,
- AC_VERB_GET_PIN_SENSE, 0) &
- AC_PINSENSE_PRESENCE;
+ spec->hp_present = snd_hda_jack_detect(codec, 0x16);
cxt5051_update_speaker(codec);
}
@@ -2011,8 +1991,47 @@ static void cxt5066_automic(struct hda_codec *codec)
};
unsigned int present;
- present = snd_hda_codec_read(codec, 0x1a, 0,
- AC_VERB_GET_PIN_SENSE, 0) & 0x80000000;
+ present = snd_hda_jack_detect(codec, 0x1a);
+ if (present) {
+ snd_printdd("CXT5066: external microphone detected\n");
+ snd_hda_sequence_write(codec, ext_mic_present);
+ } else {
+ snd_printdd("CXT5066: external microphone absent\n");
+ snd_hda_sequence_write(codec, ext_mic_absent);
+ }
+}
+
+/* toggle input of built-in digital mic and mic jack appropriately */
+static void cxt5066_vostro_automic(struct hda_codec *codec)
+{
+ struct conexant_spec *spec = codec->spec;
+ unsigned int present;
+
+ struct hda_verb ext_mic_present[] = {
+ /* enable external mic, port B */
+ {0x1a, AC_VERB_SET_PIN_WIDGET_CONTROL, spec->ext_mic_bias},
+
+ /* switch to external mic input */
+ {0x17, AC_VERB_SET_CONNECT_SEL, 0},
+ {0x14, AC_VERB_SET_CONNECT_SEL, 0},
+
+ /* disable internal digital mic */
+ {0x23, AC_VERB_SET_PIN_WIDGET_CONTROL, 0},
+ {}
+ };
+ static struct hda_verb ext_mic_absent[] = {
+ /* enable internal mic, port C */
+ {0x23, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN},
+
+ /* switch to internal mic input */
+ {0x14, AC_VERB_SET_CONNECT_SEL, 2},
+
+ /* disable external mic, port B */
+ {0x1a, AC_VERB_SET_PIN_WIDGET_CONTROL, 0},
+ {}
+ };
+
+ present = snd_hda_jack_detect(codec, 0x1a);
if (present) {
snd_printdd("CXT5066: external microphone detected\n");
snd_hda_sequence_write(codec, ext_mic_present);
@@ -2029,12 +2048,10 @@ static void cxt5066_hp_automute(struct hda_codec *codec)
unsigned int portA, portD;
/* Port A */
- portA = snd_hda_codec_read(codec, 0x19, 0, AC_VERB_GET_PIN_SENSE, 0)
- & AC_PINSENSE_PRESENCE;
+ portA = snd_hda_jack_detect(codec, 0x19);
/* Port D */
- portD = (snd_hda_codec_read(codec, 0x1c, 0, AC_VERB_GET_PIN_SENSE, 0)
- & AC_PINSENSE_PRESENCE) << 1;
+ portD = snd_hda_jack_detect(codec, 0x1c);
spec->hp_present = !!(portA | portD);
snd_printdd("CXT5066: hp automute portA=%x portD=%x present=%d\n",
@@ -2056,6 +2073,20 @@ static void cxt5066_unsol_event(struct hda_codec *codec, unsigned int res)
}
}
+/* unsolicited event for jack sensing */
+static void cxt5066_vostro_event(struct hda_codec *codec, unsigned int res)
+{
+ snd_printdd("CXT5066_vostro: unsol event %x (%x)\n", res, res >> 26);
+ switch (res >> 26) {
+ case CONEXANT_HP_EVENT:
+ cxt5066_hp_automute(codec);
+ break;
+ case CONEXANT_MIC_EVENT:
+ cxt5066_vostro_automic(codec);
+ break;
+ }
+}
+
static const struct hda_input_mux cxt5066_analog_mic_boost = {
.num_items = 5,
.items = {
@@ -2297,6 +2328,67 @@ static struct hda_verb cxt5066_init_verbs_olpc[] = {
{ } /* end */
};
+static struct hda_verb cxt5066_init_verbs_vostro[] = {
+ /* Port A: headphones */
+ {0x19, AC_VERB_SET_PIN_WIDGET_CONTROL, 0},
+ {0x19, AC_VERB_SET_CONNECT_SEL, 0x00}, /* DAC1 */
+
+ /* Port B: external microphone */
+ {0x1a, AC_VERB_SET_PIN_WIDGET_CONTROL, 0},
+
+ /* Port C: unused */
+ {0x1b, AC_VERB_SET_PIN_WIDGET_CONTROL, 0},
+
+ /* Port D: unused */
+ {0x1c, AC_VERB_SET_PIN_WIDGET_CONTROL, 0},
+
+ /* Port E: unused, but has primary EAPD */
+ {0x1d, AC_VERB_SET_PIN_WIDGET_CONTROL, 0},
+ {0x1d, AC_VERB_SET_EAPD_BTLENABLE, 0x2}, /* default on */
+
+ /* Port F: unused */
+ {0x1e, AC_VERB_SET_PIN_WIDGET_CONTROL, 0},
+
+ /* Port G: internal speakers */
+ {0x1f, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
+ {0x1f, AC_VERB_SET_CONNECT_SEL, 0x00}, /* DAC1 */
+
+ /* DAC1 */
+ {0x10, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
+
+ /* DAC2: unused */
+ {0x11, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
+
+ {0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
+ {0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
+ {0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(2)},
+ {0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(3)},
+ {0x15, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
+ {0x15, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
+ {0x15, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(2)},
+ {0x15, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(3)},
+ {0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
+ {0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
+ {0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(2)},
+ {0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(3)},
+
+ /* Digital microphone port */
+ {0x23, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN},
+
+ /* Audio input selectors */
+ {0x17, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE | 0x3},
+ {0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE },
+
+ /* Disable SPDIF */
+ {0x20, AC_VERB_SET_PIN_WIDGET_CONTROL, 0},
+ {0x22, AC_VERB_SET_PIN_WIDGET_CONTROL, 0},
+
+ /* enable unsolicited events for Port A and B */
+ {0x19, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | CONEXANT_HP_EVENT},
+ {0x1a, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | CONEXANT_MIC_EVENT},
+ { } /* end */
+};
+
static struct hda_verb cxt5066_init_verbs_portd_lo[] = {
{0x1c, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
{ } /* end */
@@ -2318,6 +2410,7 @@ enum {
CXT5066_LAPTOP, /* Laptops w/ EAPD support */
CXT5066_DELL_LAPTOP, /* Dell Laptop */
CXT5066_OLPC_XO_1_5, /* OLPC XO 1.5 */
+ CXT5066_DELL_VOSTO, /* Dell Vostro 1015i */
CXT5066_MODELS
};
@@ -2325,6 +2418,7 @@ static const char *cxt5066_models[CXT5066_MODELS] = {
[CXT5066_LAPTOP] = "laptop",
[CXT5066_DELL_LAPTOP] = "dell-laptop",
[CXT5066_OLPC_XO_1_5] = "olpc-xo-1_5",
+ [CXT5066_DELL_VOSTO] = "dell-vostro"
};
static struct snd_pci_quirk cxt5066_cfg_tbl[] = {
@@ -2333,6 +2427,7 @@ static struct snd_pci_quirk cxt5066_cfg_tbl[] = {
SND_PCI_QUIRK(0x1028, 0x02f5, "Dell",
CXT5066_DELL_LAPTOP),
SND_PCI_QUIRK(0x152d, 0x0833, "OLPC XO-1.5", CXT5066_OLPC_XO_1_5),
+ SND_PCI_QUIRK(0x1028, 0x0402, "Dell Vostro", CXT5066_DELL_VOSTO),
{}
};
@@ -2400,6 +2495,19 @@ static int patch_cxt5066(struct hda_codec *codec)
/* input source automatically selected */
spec->input_mux = NULL;
break;
+ case CXT5066_DELL_VOSTO:
+ codec->patch_ops.unsol_event = cxt5066_vostro_event;
+ spec->init_verbs[0] = cxt5066_init_verbs_vostro;
+ spec->mixers[spec->num_mixers++] = cxt5066_mixer_master_olpc;
+ spec->mixers[spec->num_mixers++] = cxt5066_mixers;
+ spec->port_d_mode = 0;
+
+ /* no S/PDIF out */
+ spec->multiout.dig_out_nid = 0;
+
+ /* input source automatically selected */
+ spec->input_mux = NULL;
+ break;
}
return 0;
@@ -2417,6 +2525,8 @@ static struct hda_codec_preset snd_hda_preset_conexant[] = {
.patch = patch_cxt5051 },
{ .id = 0x14f15066, .name = "CX20582 (Pebble)",
.patch = patch_cxt5066 },
+ { .id = 0x14f15067, .name = "CX20583 (Pebble HSF)",
+ .patch = patch_cxt5066 },
{} /* terminator */
};
@@ -2424,6 +2534,7 @@ MODULE_ALIAS("snd-hda-codec-id:14f15045");
MODULE_ALIAS("snd-hda-codec-id:14f15047");
MODULE_ALIAS("snd-hda-codec-id:14f15051");
MODULE_ALIAS("snd-hda-codec-id:14f15066");
+MODULE_ALIAS("snd-hda-codec-id:14f15067");
MODULE_LICENSE("GPL");
MODULE_DESCRIPTION("Conexant HD-audio codec");
diff --git a/sound/pci/hda/patch_intelhdmi.c b/sound/pci/hda/patch_intelhdmi.c
index 01a18ed475ac..928df59be5d8 100644
--- a/sound/pci/hda/patch_intelhdmi.c
+++ b/sound/pci/hda/patch_intelhdmi.c
@@ -33,15 +33,41 @@
#include "hda_codec.h"
#include "hda_local.h"
-static hda_nid_t cvt_nid; /* audio converter */
-static hda_nid_t pin_nid; /* HDMI output pin */
+/*
+ * The HDMI/DisplayPort configuration can be highly dynamic. A graphics device
+ * could support two independent pipes, each of them can be connected to one or
+ * more ports (DVI, HDMI or DisplayPort).
+ *
+ * The HDA correspondence of pipes/ports are converter/pin nodes.
+ */
+#define INTEL_HDMI_CVTS 2
+#define INTEL_HDMI_PINS 3
-#define INTEL_HDMI_EVENT_TAG 0x08
+static char *intel_hdmi_pcm_names[INTEL_HDMI_CVTS] = {
+ "INTEL HDMI 0",
+ "INTEL HDMI 1",
+};
struct intel_hdmi_spec {
- struct hda_multi_out multiout;
- struct hda_pcm pcm_rec;
- struct hdmi_eld sink_eld;
+ int num_cvts;
+ int num_pins;
+ hda_nid_t cvt[INTEL_HDMI_CVTS+1]; /* audio sources */
+ hda_nid_t pin[INTEL_HDMI_PINS+1]; /* audio sinks */
+
+ /*
+ * source connection for each pin
+ */
+ hda_nid_t pin_cvt[INTEL_HDMI_PINS+1];
+
+ /*
+ * HDMI sink attached to each pin
+ */
+ struct hdmi_eld sink_eld[INTEL_HDMI_PINS];
+
+ /*
+ * export one pcm per pipe
+ */
+ struct hda_pcm pcm_rec[INTEL_HDMI_CVTS];
};
struct hdmi_audio_infoframe {
@@ -184,40 +210,186 @@ static struct cea_channel_speaker_allocation channel_allocations[] = {
{ .ca_index = 0x31, .speakers = { FRW, FLW, RR, RL, FC, LFE, FR, FL } },
};
+
+/*
+ * HDA/HDMI auto parsing
+ */
+
+static int hda_node_index(hda_nid_t *nids, hda_nid_t nid)
+{
+ int i;
+
+ for (i = 0; nids[i]; i++)
+ if (nids[i] == nid)
+ return i;
+
+ snd_printk(KERN_WARNING "HDMI: nid %d not registered\n", nid);
+ return -EINVAL;
+}
+
+static int intel_hdmi_read_pin_conn(struct hda_codec *codec, hda_nid_t pin_nid)
+{
+ struct intel_hdmi_spec *spec = codec->spec;
+ hda_nid_t conn_list[HDA_MAX_CONNECTIONS];
+ int conn_len, curr;
+ int index;
+
+ if (!(get_wcaps(codec, pin_nid) & AC_WCAP_CONN_LIST)) {
+ snd_printk(KERN_WARNING
+ "HDMI: pin %d wcaps %#x "
+ "does not support connection list\n",
+ pin_nid, get_wcaps(codec, pin_nid));
+ return -EINVAL;
+ }
+
+ conn_len = snd_hda_get_connections(codec, pin_nid, conn_list,
+ HDA_MAX_CONNECTIONS);
+ if (conn_len > 1)
+ curr = snd_hda_codec_read(codec, pin_nid, 0,
+ AC_VERB_GET_CONNECT_SEL, 0);
+ else
+ curr = 0;
+
+ index = hda_node_index(spec->pin, pin_nid);
+ if (index < 0)
+ return -EINVAL;
+
+ spec->pin_cvt[index] = conn_list[curr];
+
+ return 0;
+}
+
+static void hdmi_get_show_eld(struct hda_codec *codec, hda_nid_t pin_nid,
+ struct hdmi_eld *eld)
+{
+ if (!snd_hdmi_get_eld(eld, codec, pin_nid))
+ snd_hdmi_show_eld(eld);
+}
+
+static void hdmi_present_sense(struct hda_codec *codec, hda_nid_t pin_nid,
+ struct hdmi_eld *eld)
+{
+ int present = snd_hda_pin_sense(codec, pin_nid);
+
+ eld->monitor_present = !!(present & AC_PINSENSE_PRESENCE);
+ eld->eld_valid = !!(present & AC_PINSENSE_ELDV);
+
+ if (present & AC_PINSENSE_ELDV)
+ hdmi_get_show_eld(codec, pin_nid, eld);
+}
+
+static int intel_hdmi_add_pin(struct hda_codec *codec, hda_nid_t pin_nid)
+{
+ struct intel_hdmi_spec *spec = codec->spec;
+
+ if (spec->num_pins >= INTEL_HDMI_PINS) {
+ snd_printk(KERN_WARNING
+ "HDMI: no space for pin %d \n", pin_nid);
+ return -EINVAL;
+ }
+
+ hdmi_present_sense(codec, pin_nid, &spec->sink_eld[spec->num_pins]);
+
+ spec->pin[spec->num_pins] = pin_nid;
+ spec->num_pins++;
+
+ /*
+ * It is assumed that converter nodes come first in the node list and
+ * hence have been registered and usable now.
+ */
+ return intel_hdmi_read_pin_conn(codec, pin_nid);
+}
+
+static int intel_hdmi_add_cvt(struct hda_codec *codec, hda_nid_t nid)
+{
+ struct intel_hdmi_spec *spec = codec->spec;
+
+ if (spec->num_cvts >= INTEL_HDMI_CVTS) {
+ snd_printk(KERN_WARNING
+ "HDMI: no space for converter %d \n", nid);
+ return -EINVAL;
+ }
+
+ spec->cvt[spec->num_cvts] = nid;
+ spec->num_cvts++;
+
+ return 0;
+}
+
+static int intel_hdmi_parse_codec(struct hda_codec *codec)
+{
+ hda_nid_t nid;
+ int i, nodes;
+
+ nodes = snd_hda_get_sub_nodes(codec, codec->afg, &nid);
+ if (!nid || nodes < 0) {
+ snd_printk(KERN_WARNING "HDMI: failed to get afg sub nodes\n");
+ return -EINVAL;
+ }
+
+ for (i = 0; i < nodes; i++, nid++) {
+ unsigned int caps;
+ unsigned int type;
+
+ caps = snd_hda_param_read(codec, nid, AC_PAR_AUDIO_WIDGET_CAP);
+ type = get_wcaps_type(caps);
+
+ if (!(caps & AC_WCAP_DIGITAL))
+ continue;
+
+ switch (type) {
+ case AC_WID_AUD_OUT:
+ if (intel_hdmi_add_cvt(codec, nid) < 0)
+ return -EINVAL;
+ break;
+ case AC_WID_PIN:
+ caps = snd_hda_param_read(codec, nid, AC_PAR_PIN_CAP);
+ if (!(caps & AC_PINCAP_HDMI))
+ continue;
+ if (intel_hdmi_add_pin(codec, nid) < 0)
+ return -EINVAL;
+ break;
+ }
+ }
+
+ return 0;
+}
+
/*
* HDMI routines
*/
#ifdef BE_PARANOID
-static void hdmi_get_dip_index(struct hda_codec *codec, hda_nid_t nid,
+static void hdmi_get_dip_index(struct hda_codec *codec, hda_nid_t pin_nid,
int *packet_index, int *byte_index)
{
int val;
- val = snd_hda_codec_read(codec, nid, 0, AC_VERB_GET_HDMI_DIP_INDEX, 0);
+ val = snd_hda_codec_read(codec, pin_nid, 0,
+ AC_VERB_GET_HDMI_DIP_INDEX, 0);
*packet_index = val >> 5;
*byte_index = val & 0x1f;
}
#endif
-static void hdmi_set_dip_index(struct hda_codec *codec, hda_nid_t nid,
+static void hdmi_set_dip_index(struct hda_codec *codec, hda_nid_t pin_nid,
int packet_index, int byte_index)
{
int val;
val = (packet_index << 5) | (byte_index & 0x1f);
- snd_hda_codec_write(codec, nid, 0, AC_VERB_SET_HDMI_DIP_INDEX, val);
+ snd_hda_codec_write(codec, pin_nid, 0, AC_VERB_SET_HDMI_DIP_INDEX, val);
}
-static void hdmi_write_dip_byte(struct hda_codec *codec, hda_nid_t nid,
+static void hdmi_write_dip_byte(struct hda_codec *codec, hda_nid_t pin_nid,
unsigned char val)
{
- snd_hda_codec_write(codec, nid, 0, AC_VERB_SET_HDMI_DIP_DATA, val);
+ snd_hda_codec_write(codec, pin_nid, 0, AC_VERB_SET_HDMI_DIP_DATA, val);
}
-static void hdmi_enable_output(struct hda_codec *codec)
+static void hdmi_enable_output(struct hda_codec *codec, hda_nid_t pin_nid)
{
/* Unmute */
if (get_wcaps(codec, pin_nid) & AC_WCAP_OUT_AMP)
@@ -231,7 +403,8 @@ static void hdmi_enable_output(struct hda_codec *codec)
/*
* Enable Audio InfoFrame Transmission
*/
-static void hdmi_start_infoframe_trans(struct hda_codec *codec)
+static void hdmi_start_infoframe_trans(struct hda_codec *codec,
+ hda_nid_t pin_nid)
{
hdmi_set_dip_index(codec, pin_nid, 0x0, 0x0);
snd_hda_codec_write(codec, pin_nid, 0, AC_VERB_SET_HDMI_DIP_XMIT,
@@ -241,59 +414,49 @@ static void hdmi_start_infoframe_trans(struct hda_codec *codec)
/*
* Disable Audio InfoFrame Transmission
*/
-static void hdmi_stop_infoframe_trans(struct hda_codec *codec)
+static void hdmi_stop_infoframe_trans(struct hda_codec *codec,
+ hda_nid_t pin_nid)
{
hdmi_set_dip_index(codec, pin_nid, 0x0, 0x0);
snd_hda_codec_write(codec, pin_nid, 0, AC_VERB_SET_HDMI_DIP_XMIT,
AC_DIPXMIT_DISABLE);
}
-static int hdmi_get_channel_count(struct hda_codec *codec)
+static int hdmi_get_channel_count(struct hda_codec *codec, hda_nid_t nid)
{
- return 1 + snd_hda_codec_read(codec, cvt_nid, 0,
+ return 1 + snd_hda_codec_read(codec, nid, 0,
AC_VERB_GET_CVT_CHAN_COUNT, 0);
}
-static void hdmi_set_channel_count(struct hda_codec *codec, int chs)
+static void hdmi_set_channel_count(struct hda_codec *codec,
+ hda_nid_t nid, int chs)
{
- snd_hda_codec_write(codec, cvt_nid, 0,
- AC_VERB_SET_CVT_CHAN_COUNT, chs - 1);
-
- if (chs != hdmi_get_channel_count(codec))
- snd_printd(KERN_INFO "HDMI channel count: expect %d, get %d\n",
- chs, hdmi_get_channel_count(codec));
+ if (chs != hdmi_get_channel_count(codec, nid))
+ snd_hda_codec_write(codec, nid, 0,
+ AC_VERB_SET_CVT_CHAN_COUNT, chs - 1);
}
-static void hdmi_debug_channel_mapping(struct hda_codec *codec)
+static void hdmi_debug_channel_mapping(struct hda_codec *codec, hda_nid_t nid)
{
#ifdef CONFIG_SND_DEBUG_VERBOSE
int i;
int slot;
for (i = 0; i < 8; i++) {
- slot = snd_hda_codec_read(codec, cvt_nid, 0,
+ slot = snd_hda_codec_read(codec, nid, 0,
AC_VERB_GET_HDMI_CHAN_SLOT, i);
printk(KERN_DEBUG "HDMI: ASP channel %d => slot %d\n",
- slot >> 4, slot & 0x7);
+ slot >> 4, slot & 0xf);
}
#endif
}
-static void hdmi_parse_eld(struct hda_codec *codec)
-{
- struct intel_hdmi_spec *spec = codec->spec;
- struct hdmi_eld *eld = &spec->sink_eld;
-
- if (!snd_hdmi_get_eld(eld, codec, pin_nid))
- snd_hdmi_show_eld(eld);
-}
-
/*
* Audio InfoFrame routines
*/
-static void hdmi_debug_dip_size(struct hda_codec *codec)
+static void hdmi_debug_dip_size(struct hda_codec *codec, hda_nid_t pin_nid)
{
#ifdef CONFIG_SND_DEBUG_VERBOSE
int i;
@@ -310,7 +473,7 @@ static void hdmi_debug_dip_size(struct hda_codec *codec)
#endif
}
-static void hdmi_clear_dip_buffers(struct hda_codec *codec)
+static void hdmi_clear_dip_buffers(struct hda_codec *codec, hda_nid_t pin_nid)
{
#ifdef BE_PARANOID
int i, j;
@@ -339,23 +502,35 @@ static void hdmi_clear_dip_buffers(struct hda_codec *codec)
#endif
}
-static void hdmi_fill_audio_infoframe(struct hda_codec *codec,
- struct hdmi_audio_infoframe *ai)
+static void hdmi_checksum_audio_infoframe(struct hdmi_audio_infoframe *ai)
{
- u8 *params = (u8 *)ai;
+ u8 *bytes = (u8 *)ai;
u8 sum = 0;
int i;
- hdmi_debug_dip_size(codec);
- hdmi_clear_dip_buffers(codec); /* be paranoid */
+ ai->checksum = 0;
+
+ for (i = 0; i < sizeof(*ai); i++)
+ sum += bytes[i];
- for (i = 0; i < sizeof(ai); i++)
- sum += params[i];
ai->checksum = - sum;
+}
+
+static void hdmi_fill_audio_infoframe(struct hda_codec *codec,
+ hda_nid_t pin_nid,
+ struct hdmi_audio_infoframe *ai)
+{
+ u8 *bytes = (u8 *)ai;
+ int i;
+
+ hdmi_debug_dip_size(codec, pin_nid);
+ hdmi_clear_dip_buffers(codec, pin_nid); /* be paranoid */
+
+ hdmi_checksum_audio_infoframe(ai);
hdmi_set_dip_index(codec, pin_nid, 0x0, 0x0);
- for (i = 0; i < sizeof(ai); i++)
- hdmi_write_dip_byte(codec, pin_nid, params[i]);
+ for (i = 0; i < sizeof(*ai); i++)
+ hdmi_write_dip_byte(codec, pin_nid, bytes[i]);
}
/*
@@ -386,11 +561,11 @@ static void init_channel_allocations(void)
*
* TODO: it could select the wrong CA from multiple candidates.
*/
-static int hdmi_setup_channel_allocation(struct hda_codec *codec,
+static int hdmi_setup_channel_allocation(struct hda_codec *codec, hda_nid_t nid,
struct hdmi_audio_infoframe *ai)
{
struct intel_hdmi_spec *spec = codec->spec;
- struct hdmi_eld *eld = &spec->sink_eld;
+ struct hdmi_eld *eld;
int i;
int spk_mask = 0;
int channels = 1 + (ai->CC02_CT47 & 0x7);
@@ -402,6 +577,11 @@ static int hdmi_setup_channel_allocation(struct hda_codec *codec,
if (channels <= 2)
return 0;
+ i = hda_node_index(spec->pin_cvt, nid);
+ if (i < 0)
+ return 0;
+ eld = &spec->sink_eld[i];
+
/*
* HDMI sink's ELD info cannot always be retrieved for now, e.g.
* in console or for audio devices. Assume the highest speakers
@@ -439,8 +619,8 @@ static int hdmi_setup_channel_allocation(struct hda_codec *codec,
return ai->CA;
}
-static void hdmi_setup_channel_mapping(struct hda_codec *codec,
- struct hdmi_audio_infoframe *ai)
+static void hdmi_setup_channel_mapping(struct hda_codec *codec, hda_nid_t nid,
+ struct hdmi_audio_infoframe *ai)
{
int i;
@@ -453,17 +633,41 @@ static void hdmi_setup_channel_mapping(struct hda_codec *codec,
*/
for (i = 0; i < 8; i++)
- snd_hda_codec_write(codec, cvt_nid, 0,
+ snd_hda_codec_write(codec, nid, 0,
AC_VERB_SET_HDMI_CHAN_SLOT,
(i << 4) | i);
- hdmi_debug_channel_mapping(codec);
+ hdmi_debug_channel_mapping(codec, nid);
}
+static bool hdmi_infoframe_uptodate(struct hda_codec *codec, hda_nid_t pin_nid,
+ struct hdmi_audio_infoframe *ai)
+{
+ u8 *bytes = (u8 *)ai;
+ u8 val;
+ int i;
+
+ if (snd_hda_codec_read(codec, pin_nid, 0, AC_VERB_GET_HDMI_DIP_XMIT, 0)
+ != AC_DIPXMIT_BEST)
+ return false;
+
+ hdmi_set_dip_index(codec, pin_nid, 0x0, 0x0);
+ for (i = 0; i < sizeof(*ai); i++) {
+ val = snd_hda_codec_read(codec, pin_nid, 0,
+ AC_VERB_GET_HDMI_DIP_DATA, 0);
+ if (val != bytes[i])
+ return false;
+ }
-static void hdmi_setup_audio_infoframe(struct hda_codec *codec,
+ return true;
+}
+
+static void hdmi_setup_audio_infoframe(struct hda_codec *codec, hda_nid_t nid,
struct snd_pcm_substream *substream)
{
+ struct intel_hdmi_spec *spec = codec->spec;
+ hda_nid_t pin_nid;
+ int i;
struct hdmi_audio_infoframe ai = {
.type = 0x84,
.ver = 0x01,
@@ -471,11 +675,22 @@ static void hdmi_setup_audio_infoframe(struct hda_codec *codec,
.CC02_CT47 = substream->runtime->channels - 1,
};
- hdmi_setup_channel_allocation(codec, &ai);
- hdmi_setup_channel_mapping(codec, &ai);
+ hdmi_setup_channel_allocation(codec, nid, &ai);
+ hdmi_setup_channel_mapping(codec, nid, &ai);
- hdmi_fill_audio_infoframe(codec, &ai);
- hdmi_start_infoframe_trans(codec);
+ for (i = 0; i < spec->num_pins; i++) {
+ if (spec->pin_cvt[i] != nid)
+ continue;
+ if (!spec->sink_eld[i].monitor_present)
+ continue;
+
+ pin_nid = spec->pin[i];
+ if (!hdmi_infoframe_uptodate(codec, pin_nid, &ai)) {
+ hdmi_stop_infoframe_trans(codec, pin_nid);
+ hdmi_fill_audio_infoframe(codec, pin_nid, &ai);
+ hdmi_start_infoframe_trans(codec, pin_nid);
+ }
+ }
}
@@ -485,27 +700,39 @@ static void hdmi_setup_audio_infoframe(struct hda_codec *codec,
static void hdmi_intrinsic_event(struct hda_codec *codec, unsigned int res)
{
+ struct intel_hdmi_spec *spec = codec->spec;
+ int tag = res >> AC_UNSOL_RES_TAG_SHIFT;
int pind = !!(res & AC_UNSOL_RES_PD);
int eldv = !!(res & AC_UNSOL_RES_ELDV);
+ int index;
printk(KERN_INFO
- "HDMI hot plug event: Presence_Detect=%d ELD_Valid=%d\n",
- pind, eldv);
+ "HDMI hot plug event: Pin=%d Presence_Detect=%d ELD_Valid=%d\n",
+ tag, pind, eldv);
+
+ index = hda_node_index(spec->pin, tag);
+ if (index < 0)
+ return;
+
+ spec->sink_eld[index].monitor_present = pind;
+ spec->sink_eld[index].eld_valid = eldv;
if (pind && eldv) {
- hdmi_parse_eld(codec);
+ hdmi_get_show_eld(codec, spec->pin[index], &spec->sink_eld[index]);
/* TODO: do real things about ELD */
}
}
static void hdmi_non_intrinsic_event(struct hda_codec *codec, unsigned int res)
{
+ int tag = res >> AC_UNSOL_RES_TAG_SHIFT;
int subtag = (res & AC_UNSOL_RES_SUBTAG) >> AC_UNSOL_RES_SUBTAG_SHIFT;
int cp_state = !!(res & AC_UNSOL_RES_CP_STATE);
int cp_ready = !!(res & AC_UNSOL_RES_CP_READY);
printk(KERN_INFO
- "HDMI content protection event: SUBTAG=0x%x CP_STATE=%d CP_READY=%d\n",
+ "HDMI CP event: PIN=%d SUBTAG=0x%x CP_STATE=%d CP_READY=%d\n",
+ tag,
subtag,
cp_state,
cp_ready);
@@ -520,10 +747,11 @@ static void hdmi_non_intrinsic_event(struct hda_codec *codec, unsigned int res)
static void intel_hdmi_unsol_event(struct hda_codec *codec, unsigned int res)
{
+ struct intel_hdmi_spec *spec = codec->spec;
int tag = res >> AC_UNSOL_RES_TAG_SHIFT;
int subtag = (res & AC_UNSOL_RES_SUBTAG) >> AC_UNSOL_RES_SUBTAG_SHIFT;
- if (tag != INTEL_HDMI_EVENT_TAG) {
+ if (hda_node_index(spec->pin, tag) < 0) {
snd_printd(KERN_INFO "Unexpected HDMI event tag 0x%x\n", tag);
return;
}
@@ -538,24 +766,29 @@ static void intel_hdmi_unsol_event(struct hda_codec *codec, unsigned int res)
* Callbacks
*/
-static int intel_hdmi_playback_pcm_open(struct hda_pcm_stream *hinfo,
- struct hda_codec *codec,
- struct snd_pcm_substream *substream)
+static void hdmi_setup_stream(struct hda_codec *codec, hda_nid_t nid,
+ u32 stream_tag, int format)
{
- struct intel_hdmi_spec *spec = codec->spec;
-
- return snd_hda_multi_out_dig_open(codec, &spec->multiout);
-}
+ int tag;
+ int fmt;
-static int intel_hdmi_playback_pcm_close(struct hda_pcm_stream *hinfo,
- struct hda_codec *codec,
- struct snd_pcm_substream *substream)
-{
- struct intel_hdmi_spec *spec = codec->spec;
+ tag = snd_hda_codec_read(codec, nid, 0, AC_VERB_GET_CONV, 0) >> 4;
+ fmt = snd_hda_codec_read(codec, nid, 0, AC_VERB_GET_STREAM_FORMAT, 0);
- hdmi_stop_infoframe_trans(codec);
+ snd_printdd("hdmi_setup_stream: "
+ "NID=0x%x, %sstream=0x%x, %sformat=0x%x\n",
+ nid,
+ tag == stream_tag ? "" : "new-",
+ stream_tag,
+ fmt == format ? "" : "new-",
+ format);
- return snd_hda_multi_out_dig_close(codec, &spec->multiout);
+ if (tag != stream_tag)
+ snd_hda_codec_write(codec, nid, 0,
+ AC_VERB_SET_CHANNEL_STREAMID, stream_tag << 4);
+ if (fmt != format)
+ snd_hda_codec_write(codec, nid, 0,
+ AC_VERB_SET_STREAM_FORMAT, format);
}
static int intel_hdmi_playback_pcm_prepare(struct hda_pcm_stream *hinfo,
@@ -564,43 +797,53 @@ static int intel_hdmi_playback_pcm_prepare(struct hda_pcm_stream *hinfo,
unsigned int format,
struct snd_pcm_substream *substream)
{
- struct intel_hdmi_spec *spec = codec->spec;
-
- snd_hda_multi_out_dig_prepare(codec, &spec->multiout, stream_tag,
- format, substream);
+ hdmi_set_channel_count(codec, hinfo->nid,
+ substream->runtime->channels);
- hdmi_set_channel_count(codec, substream->runtime->channels);
+ hdmi_setup_audio_infoframe(codec, hinfo->nid, substream);
- hdmi_setup_audio_infoframe(codec, substream);
+ hdmi_setup_stream(codec, hinfo->nid, stream_tag, format);
+ return 0;
+}
+static int intel_hdmi_playback_pcm_cleanup(struct hda_pcm_stream *hinfo,
+ struct hda_codec *codec,
+ struct snd_pcm_substream *substream)
+{
return 0;
}
static struct hda_pcm_stream intel_hdmi_pcm_playback = {
.substreams = 1,
.channels_min = 2,
- .channels_max = 8,
.ops = {
- .open = intel_hdmi_playback_pcm_open,
- .close = intel_hdmi_playback_pcm_close,
- .prepare = intel_hdmi_playback_pcm_prepare
+ .prepare = intel_hdmi_playback_pcm_prepare,
+ .cleanup = intel_hdmi_playback_pcm_cleanup,
},
};
static int intel_hdmi_build_pcms(struct hda_codec *codec)
{
struct intel_hdmi_spec *spec = codec->spec;
- struct hda_pcm *info = &spec->pcm_rec;
+ struct hda_pcm *info = spec->pcm_rec;
+ int i;
- codec->num_pcms = 1;
+ codec->num_pcms = spec->num_cvts;
codec->pcm_info = info;
- /* NID to query formats and rates and setup streams */
- intel_hdmi_pcm_playback.nid = cvt_nid;
+ for (i = 0; i < codec->num_pcms; i++, info++) {
+ unsigned int chans;
- info->name = "INTEL HDMI";
- info->pcm_type = HDA_PCM_TYPE_HDMI;
- info->stream[SNDRV_PCM_STREAM_PLAYBACK] = intel_hdmi_pcm_playback;
+ chans = get_wcaps(codec, spec->cvt[i]);
+ chans = get_wcaps_channels(chans);
+
+ info->name = intel_hdmi_pcm_names[i];
+ info->pcm_type = HDA_PCM_TYPE_HDMI;
+ info->stream[SNDRV_PCM_STREAM_PLAYBACK] =
+ intel_hdmi_pcm_playback;
+ info->stream[SNDRV_PCM_STREAM_PLAYBACK].nid = spec->cvt[i];
+ info->stream[SNDRV_PCM_STREAM_PLAYBACK].channels_max = chans;
+ }
return 0;
}
@@ -609,29 +852,39 @@ static int intel_hdmi_build_controls(struct hda_codec *codec)
{
struct intel_hdmi_spec *spec = codec->spec;
int err;
+ int i;
- err = snd_hda_create_spdif_out_ctls(codec, spec->multiout.dig_out_nid);
- if (err < 0)
- return err;
+ for (i = 0; i < codec->num_pcms; i++) {
+ err = snd_hda_create_spdif_out_ctls(codec, spec->cvt[i]);
+ if (err < 0)
+ return err;
+ }
return 0;
}
static int intel_hdmi_init(struct hda_codec *codec)
{
- hdmi_enable_output(codec);
+ struct intel_hdmi_spec *spec = codec->spec;
+ int i;
- snd_hda_codec_write(codec, pin_nid, 0,
- AC_VERB_SET_UNSOLICITED_ENABLE,
- AC_USRSP_EN | INTEL_HDMI_EVENT_TAG);
+ for (i = 0; spec->pin[i]; i++) {
+ hdmi_enable_output(codec, spec->pin[i]);
+ snd_hda_codec_write(codec, spec->pin[i], 0,
+ AC_VERB_SET_UNSOLICITED_ENABLE,
+ AC_USRSP_EN | spec->pin[i]);
+ }
return 0;
}
static void intel_hdmi_free(struct hda_codec *codec)
{
struct intel_hdmi_spec *spec = codec->spec;
+ int i;
+
+ for (i = 0; i < spec->num_pins; i++)
+ snd_hda_eld_proc_free(codec, &spec->sink_eld[i]);
- snd_hda_eld_proc_free(codec, &spec->sink_eld);
kfree(spec);
}
@@ -643,49 +896,38 @@ static struct hda_codec_ops intel_hdmi_patch_ops = {
.unsol_event = intel_hdmi_unsol_event,
};
-static int do_patch_intel_hdmi(struct hda_codec *codec)
+static int patch_intel_hdmi(struct hda_codec *codec)
{
struct intel_hdmi_spec *spec;
+ int i;
spec = kzalloc(sizeof(*spec), GFP_KERNEL);
if (spec == NULL)
return -ENOMEM;
- spec->multiout.num_dacs = 0; /* no analog */
- spec->multiout.max_channels = 8;
- spec->multiout.dig_out_nid = cvt_nid;
-
codec->spec = spec;
+ if (intel_hdmi_parse_codec(codec) < 0) {
+ codec->spec = NULL;
+ kfree(spec);
+ return -EINVAL;
+ }
codec->patch_ops = intel_hdmi_patch_ops;
- snd_hda_eld_proc_new(codec, &spec->sink_eld);
+ for (i = 0; i < spec->num_pins; i++)
+ snd_hda_eld_proc_new(codec, &spec->sink_eld[i], i);
init_channel_allocations();
return 0;
}
-static int patch_intel_hdmi(struct hda_codec *codec)
-{
- cvt_nid = 0x02;
- pin_nid = 0x03;
- return do_patch_intel_hdmi(codec);
-}
-
-static int patch_intel_hdmi_ibexpeak(struct hda_codec *codec)
-{
- cvt_nid = 0x02;
- pin_nid = 0x04;
- return do_patch_intel_hdmi(codec);
-}
-
static struct hda_codec_preset snd_hda_preset_intelhdmi[] = {
{ .id = 0x808629fb, .name = "G45 DEVCL", .patch = patch_intel_hdmi },
{ .id = 0x80862801, .name = "G45 DEVBLC", .patch = patch_intel_hdmi },
{ .id = 0x80862802, .name = "G45 DEVCTG", .patch = patch_intel_hdmi },
{ .id = 0x80862803, .name = "G45 DEVELK", .patch = patch_intel_hdmi },
{ .id = 0x80862804, .name = "G45 DEVIBX", .patch = patch_intel_hdmi },
- { .id = 0x80860054, .name = "Q57 DEVIBX", .patch = patch_intel_hdmi_ibexpeak },
+ { .id = 0x80860054, .name = "Q57 DEVIBX", .patch = patch_intel_hdmi },
{ .id = 0x10951392, .name = "SiI1392 HDMI", .patch = patch_intel_hdmi },
{} /* terminator */
};
diff --git a/sound/pci/hda/patch_realtek.c b/sound/pci/hda/patch_realtek.c
index 70583719282b..d967836f36bb 100644
--- a/sound/pci/hda/patch_realtek.c
+++ b/sound/pci/hda/patch_realtek.c
@@ -961,18 +961,12 @@ static void alc_fix_pll_init(struct hda_codec *codec, hda_nid_t nid,
static void alc_automute_pin(struct hda_codec *codec)
{
struct alc_spec *spec = codec->spec;
- unsigned int present, pincap;
unsigned int nid = spec->autocfg.hp_pins[0];
int i;
if (!nid)
return;
- pincap = snd_hda_query_pin_caps(codec, nid);
- if (pincap & AC_PINCAP_TRIG_REQ) /* need trigger? */
- snd_hda_codec_read(codec, nid, 0, AC_VERB_SET_PIN_SENSE, 0);
- present = snd_hda_codec_read(codec, nid, 0,
- AC_VERB_GET_PIN_SENSE, 0);
- spec->jack_present = (present & AC_PINSENSE_PRESENCE) != 0;
+ spec->jack_present = snd_hda_jack_detect(codec, nid);
for (i = 0; i < ARRAY_SIZE(spec->autocfg.speaker_pins); i++) {
nid = spec->autocfg.speaker_pins[i];
if (!nid)
@@ -1012,9 +1006,7 @@ static void alc_mic_automute(struct hda_codec *codec)
cap_nid = spec->capsrc_nids ? spec->capsrc_nids[0] : spec->adc_nids[0];
- present = snd_hda_codec_read(codec, spec->ext_mic.pin, 0,
- AC_VERB_GET_PIN_SENSE, 0);
- present &= AC_PINSENSE_PRESENCE;
+ present = snd_hda_jack_detect(codec, spec->ext_mic.pin);
if (present) {
alive = &spec->ext_mic;
dead = &spec->int_mic;
@@ -1402,6 +1394,17 @@ static void alc_pick_fixup(struct hda_codec *codec,
add_verb(codec->spec, fix->verbs);
}
+static int alc_read_coef_idx(struct hda_codec *codec,
+ unsigned int coef_idx)
+{
+ unsigned int val;
+ snd_hda_codec_write(codec, 0x20, 0, AC_VERB_SET_COEF_INDEX,
+ coef_idx);
+ val = snd_hda_codec_read(codec, 0x20, 0,
+ AC_VERB_GET_PROC_COEF, 0);
+ return val;
+}
+
/*
* ALC888
*/
@@ -1513,7 +1516,7 @@ static struct hda_verb alc888_fujitsu_xa3530_verbs[] = {
static void alc_automute_amp(struct hda_codec *codec)
{
struct alc_spec *spec = codec->spec;
- unsigned int val, mute, pincap;
+ unsigned int mute;
hda_nid_t nid;
int i;
@@ -1522,13 +1525,7 @@ static void alc_automute_amp(struct hda_codec *codec)
nid = spec->autocfg.hp_pins[i];
if (!nid)
break;
- pincap = snd_hda_query_pin_caps(codec, nid);
- if (pincap & AC_PINCAP_TRIG_REQ) /* need trigger? */
- snd_hda_codec_read(codec, nid, 0,
- AC_VERB_SET_PIN_SENSE, 0);
- val = snd_hda_codec_read(codec, nid, 0,
- AC_VERB_GET_PIN_SENSE, 0);
- if (val & AC_PINSENSE_PRESENCE) {
+ if (snd_hda_jack_detect(codec, nid)) {
spec->jack_present = 1;
break;
}
@@ -1786,6 +1783,8 @@ static void alc888_acer_aspire_4930g_setup(struct hda_codec *codec)
spec->autocfg.hp_pins[0] = 0x15;
spec->autocfg.speaker_pins[0] = 0x14;
+ spec->autocfg.speaker_pins[1] = 0x16;
+ spec->autocfg.speaker_pins[2] = 0x17;
}
static void alc888_acer_aspire_6530g_setup(struct hda_codec *codec)
@@ -2410,12 +2409,14 @@ static const char *alc_slave_sws[] = {
static void alc_free_kctls(struct hda_codec *codec);
+#ifdef CONFIG_SND_HDA_INPUT_BEEP
/* additional beep mixers; the actual parameters are overwritten at build */
static struct snd_kcontrol_new alc_beep_mixer[] = {
HDA_CODEC_VOLUME("Beep Playback Volume", 0, 0, HDA_INPUT),
- HDA_CODEC_MUTE("Beep Playback Switch", 0, 0, HDA_INPUT),
+ HDA_CODEC_MUTE_BEEP("Beep Playback Switch", 0, 0, HDA_INPUT),
{ } /* end */
};
+#endif
static int alc_build_controls(struct hda_codec *codec)
{
@@ -2452,6 +2453,7 @@ static int alc_build_controls(struct hda_codec *codec)
return err;
}
+#ifdef CONFIG_SND_HDA_INPUT_BEEP
/* create beep controls if needed */
if (spec->beep_amp) {
struct snd_kcontrol_new *knew;
@@ -2461,11 +2463,13 @@ static int alc_build_controls(struct hda_codec *codec)
if (!kctl)
return -ENOMEM;
kctl->private_value = spec->beep_amp;
- err = snd_hda_ctl_add(codec, kctl);
+ err = snd_hda_ctl_add(codec,
+ get_amp_nid_(spec->beep_amp), kctl);
if (err < 0)
return err;
}
}
+#endif
/* if we have no master control, let's create it */
if (!spec->no_analog &&
@@ -2779,8 +2783,7 @@ static void alc880_uniwill_mic_automute(struct hda_codec *codec)
unsigned int present;
unsigned char bits;
- present = snd_hda_codec_read(codec, 0x18, 0,
- AC_VERB_GET_PIN_SENSE, 0) & 0x80000000;
+ present = snd_hda_jack_detect(codec, 0x18);
bits = present ? HDA_AMP_MUTE : 0;
snd_hda_codec_amp_stereo(codec, 0x0b, HDA_INPUT, 1, HDA_AMP_MUTE, bits);
}
@@ -3480,7 +3483,7 @@ static int alc_build_pcms(struct hda_codec *codec)
snprintf(spec->stream_name_analog, sizeof(spec->stream_name_analog),
"%s Analog", codec->chip_name);
info->name = spec->stream_name_analog;
-
+
if (spec->stream_analog_playback) {
if (snd_BUG_ON(!spec->multiout.dac_nids))
return -EINVAL;
@@ -4322,10 +4325,26 @@ static int add_control(struct alc_spec *spec, int type, const char *name,
knew->name = kstrdup(name, GFP_KERNEL);
if (!knew->name)
return -ENOMEM;
+ if (get_amp_nid_(val))
+ knew->subdevice = HDA_SUBDEV_NID_FLAG | get_amp_nid_(val);
knew->private_value = val;
return 0;
}
+static int add_control_with_pfx(struct alc_spec *spec, int type,
+ const char *pfx, const char *dir,
+ const char *sfx, unsigned long val)
+{
+ char name[32];
+ snprintf(name, sizeof(name), "%s %s %s", pfx, dir, sfx);
+ return add_control(spec, type, name, val);
+}
+
+#define add_pb_vol_ctrl(spec, type, pfx, val) \
+ add_control_with_pfx(spec, type, pfx, "Playback", "Volume", val)
+#define add_pb_sw_ctrl(spec, type, pfx, val) \
+ add_control_with_pfx(spec, type, pfx, "Playback", "Switch", val)
+
#define alc880_is_fixed_pin(nid) ((nid) >= 0x14 && (nid) <= 0x17)
#define alc880_fixed_pin_idx(nid) ((nid) - 0x14)
#define alc880_is_multi_pin(nid) ((nid) >= 0x18)
@@ -4379,7 +4398,6 @@ static int alc880_auto_fill_dac_nids(struct alc_spec *spec,
static int alc880_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", NULL /*CLFE*/, "Side"
};
@@ -4392,26 +4410,26 @@ static int alc880_auto_create_multi_out_ctls(struct alc_spec *spec,
nid = alc880_idx_to_mixer(alc880_dac_to_idx(spec->multiout.dac_nids[i]));
if (i == 2) {
/* Center/LFE */
- err = add_control(spec, ALC_CTL_WIDGET_VOL,
- "Center Playback Volume",
+ err = add_pb_vol_ctrl(spec, ALC_CTL_WIDGET_VOL,
+ "Center",
HDA_COMPOSE_AMP_VAL(nid, 1, 0,
HDA_OUTPUT));
if (err < 0)
return err;
- err = add_control(spec, ALC_CTL_WIDGET_VOL,
- "LFE Playback Volume",
+ err = add_pb_vol_ctrl(spec, ALC_CTL_WIDGET_VOL,
+ "LFE",
HDA_COMPOSE_AMP_VAL(nid, 2, 0,
HDA_OUTPUT));
if (err < 0)
return err;
- err = add_control(spec, ALC_CTL_BIND_MUTE,
- "Center Playback Switch",
+ err = add_pb_sw_ctrl(spec, ALC_CTL_BIND_MUTE,
+ "Center",
HDA_COMPOSE_AMP_VAL(nid, 1, 2,
HDA_INPUT));
if (err < 0)
return err;
- err = add_control(spec, ALC_CTL_BIND_MUTE,
- "LFE Playback Switch",
+ err = add_pb_sw_ctrl(spec, ALC_CTL_BIND_MUTE,
+ "LFE",
HDA_COMPOSE_AMP_VAL(nid, 2, 2,
HDA_INPUT));
if (err < 0)
@@ -4423,14 +4441,12 @@ static int alc880_auto_create_multi_out_ctls(struct alc_spec *spec,
pfx = "Speaker";
else
pfx = chname[i];
- sprintf(name, "%s Playback Volume", pfx);
- err = add_control(spec, ALC_CTL_WIDGET_VOL, name,
+ err = add_pb_vol_ctrl(spec, ALC_CTL_WIDGET_VOL, pfx,
HDA_COMPOSE_AMP_VAL(nid, 3, 0,
HDA_OUTPUT));
if (err < 0)
return err;
- sprintf(name, "%s Playback Switch", pfx);
- err = add_control(spec, ALC_CTL_BIND_MUTE, name,
+ err = add_pb_sw_ctrl(spec, ALC_CTL_BIND_MUTE, pfx,
HDA_COMPOSE_AMP_VAL(nid, 3, 2,
HDA_INPUT));
if (err < 0)
@@ -4446,7 +4462,6 @@ static int alc880_auto_create_extra_out(struct alc_spec *spec, hda_nid_t pin,
{
hda_nid_t nid;
int err;
- char name[32];
if (!pin)
return 0;
@@ -4460,21 +4475,18 @@ static int alc880_auto_create_extra_out(struct alc_spec *spec, hda_nid_t pin,
spec->multiout.extra_out_nid[0] = nid;
/* control HP volume/switch on the output mixer amp */
nid = alc880_idx_to_mixer(alc880_fixed_pin_idx(pin));
- sprintf(name, "%s Playback Volume", pfx);
- err = add_control(spec, ALC_CTL_WIDGET_VOL, name,
+ err = add_pb_vol_ctrl(spec, ALC_CTL_WIDGET_VOL, pfx,
HDA_COMPOSE_AMP_VAL(nid, 3, 0, HDA_OUTPUT));
if (err < 0)
return err;
- sprintf(name, "%s Playback Switch", pfx);
- err = add_control(spec, ALC_CTL_BIND_MUTE, name,
+ err = add_pb_sw_ctrl(spec, ALC_CTL_BIND_MUTE, pfx,
HDA_COMPOSE_AMP_VAL(nid, 3, 2, HDA_INPUT));
if (err < 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);
- err = add_control(spec, ALC_CTL_WIDGET_MUTE, name,
+ err = add_pb_sw_ctrl(spec, ALC_CTL_WIDGET_MUTE, pfx,
HDA_COMPOSE_AMP_VAL(pin, 3, 0, HDA_OUTPUT));
if (err < 0)
return err;
@@ -4487,16 +4499,13 @@ static int new_analog_input(struct alc_spec *spec, hda_nid_t pin,
const char *ctlname,
int idx, hda_nid_t mix_nid)
{
- char name[32];
int err;
- sprintf(name, "%s Playback Volume", ctlname);
- err = add_control(spec, ALC_CTL_WIDGET_VOL, name,
+ err = add_pb_vol_ctrl(spec, ALC_CTL_WIDGET_VOL, ctlname,
HDA_COMPOSE_AMP_VAL(mix_nid, 3, idx, HDA_INPUT));
if (err < 0)
return err;
- sprintf(name, "%s Playback Switch", ctlname);
- err = add_control(spec, ALC_CTL_WIDGET_MUTE, name,
+ err = add_pb_sw_ctrl(spec, ALC_CTL_WIDGET_MUTE, ctlname,
HDA_COMPOSE_AMP_VAL(mix_nid, 3, idx, HDA_INPUT));
if (err < 0)
return err;
@@ -4773,8 +4782,12 @@ static void set_capture_mixer(struct hda_codec *codec)
}
}
+#ifdef CONFIG_SND_HDA_INPUT_BEEP
#define set_beep_amp(spec, nid, idx, dir) \
((spec)->beep_amp = HDA_COMPOSE_AMP_VAL(nid, 3, idx, dir))
+#else
+#define set_beep_amp(spec, nid, idx, dir) /* NOP */
+#endif
/*
* OK, here we have finally the patch for ALC880
@@ -5087,11 +5100,8 @@ static struct hda_verb alc260_hp_unsol_verbs[] = {
static void alc260_hp_automute(struct hda_codec *codec)
{
struct alc_spec *spec = codec->spec;
- unsigned int present;
- present = snd_hda_codec_read(codec, 0x10, 0,
- AC_VERB_GET_PIN_SENSE, 0);
- spec->jack_present = (present & AC_PINSENSE_PRESENCE) != 0;
+ spec->jack_present = snd_hda_jack_detect(codec, 0x10);
alc260_hp_master_update(codec, 0x0f, 0x10, 0x11);
}
@@ -5156,11 +5166,8 @@ static struct hda_verb alc260_hp_3013_unsol_verbs[] = {
static void alc260_hp_3013_automute(struct hda_codec *codec)
{
struct alc_spec *spec = codec->spec;
- unsigned int present;
- present = snd_hda_codec_read(codec, 0x15, 0,
- AC_VERB_GET_PIN_SENSE, 0);
- spec->jack_present = (present & AC_PINSENSE_PRESENCE) != 0;
+ spec->jack_present = snd_hda_jack_detect(codec, 0x15);
alc260_hp_master_update(codec, 0x15, 0x10, 0x11);
}
@@ -5173,12 +5180,8 @@ static void alc260_hp_3013_unsol_event(struct hda_codec *codec,
static void alc260_hp_3012_automute(struct hda_codec *codec)
{
- unsigned int present, bits;
-
- present = snd_hda_codec_read(codec, 0x10, 0,
- AC_VERB_GET_PIN_SENSE, 0) & AC_PINSENSE_PRESENCE;
+ unsigned int bits = snd_hda_jack_detect(codec, 0x10) ? 0 : PIN_OUT;
- bits = present ? 0 : PIN_OUT;
snd_hda_codec_write(codec, 0x0f, 0, AC_VERB_SET_PIN_WIDGET_CONTROL,
bits);
snd_hda_codec_write(codec, 0x11, 0, AC_VERB_SET_PIN_WIDGET_CONTROL,
@@ -5748,8 +5751,7 @@ static void alc260_replacer_672v_automute(struct hda_codec *codec)
unsigned int present;
/* speaker --> GPIO Data 0, hp or spdif --> GPIO data 1 */
- present = snd_hda_codec_read(codec, 0x0f, 0,
- AC_VERB_GET_PIN_SENSE, 0) & 0x80000000;
+ present = snd_hda_jack_detect(codec, 0x0f);
if (present) {
snd_hda_codec_write_cache(codec, 0x01, 0,
AC_VERB_SET_GPIO_DATA, 1);
@@ -5989,7 +5991,6 @@ static int alc260_add_playback_controls(struct alc_spec *spec, hda_nid_t nid,
{
hda_nid_t nid_vol;
unsigned long vol_val, sw_val;
- char name[32];
int err;
if (nid >= 0x0f && nid < 0x11) {
@@ -6009,14 +6010,12 @@ static int alc260_add_playback_controls(struct alc_spec *spec, hda_nid_t nid,
if (!(*vol_bits & (1 << nid_vol))) {
/* first control for the volume widget */
- snprintf(name, sizeof(name), "%s Playback Volume", pfx);
- err = add_control(spec, ALC_CTL_WIDGET_VOL, name, vol_val);
+ err = add_pb_vol_ctrl(spec, ALC_CTL_WIDGET_VOL, pfx, vol_val);
if (err < 0)
return err;
*vol_bits |= (1 << nid_vol);
}
- snprintf(name, sizeof(name), "%s Playback Switch", pfx);
- err = add_control(spec, ALC_CTL_WIDGET_MUTE, name, sw_val);
+ err = add_pb_sw_ctrl(spec, ALC_CTL_WIDGET_MUTE, pfx, sw_val);
if (err < 0)
return err;
return 1;
@@ -7336,8 +7335,8 @@ static struct snd_kcontrol_new alc882_macpro_mixer[] = {
HDA_CODEC_VOLUME("Line Playback Volume", 0x0b, 0x01, HDA_INPUT),
HDA_CODEC_MUTE("Line Playback Switch", 0x0b, 0x01, HDA_INPUT),
/* FIXME: this looks suspicious...
- HDA_CODEC_VOLUME("PC Speaker Playback Volume", 0x0b, 0x02, HDA_INPUT),
- HDA_CODEC_MUTE("PC Speaker Playback Switch", 0x0b, 0x02, HDA_INPUT),
+ HDA_CODEC_VOLUME("Beep Playback Volume", 0x0b, 0x02, HDA_INPUT),
+ HDA_CODEC_MUTE("Beep Playback Switch", 0x0b, 0x02, HDA_INPUT),
*/
{ } /* end */
};
@@ -8184,12 +8183,8 @@ static void alc883_mitac_setup(struct hda_codec *codec)
/*
static void alc883_mitac_mic_automute(struct hda_codec *codec)
{
- unsigned int present;
- unsigned char bits;
+ unsigned char bits = snd_hda_jack_detect(codec, 0x18) ? HDA_AMP_MUTE : 0;
- present = snd_hda_codec_read(codec, 0x18, 0,
- AC_VERB_GET_PIN_SENSE, 0) & 0x80000000;
- bits = present ? HDA_AMP_MUTE : 0;
snd_hda_codec_amp_stereo(codec, 0x0b, HDA_INPUT, 1, HDA_AMP_MUTE, bits);
}
*/
@@ -8411,10 +8406,8 @@ static struct hda_channel_mode alc888_3st_hp_modes[3] = {
/* toggle front-jack and RCA according to the hp-jack state */
static void alc888_lenovo_ms7195_front_automute(struct hda_codec *codec)
{
- unsigned int present;
+ unsigned int present = snd_hda_jack_detect(codec, 0x1b);
- present = snd_hda_codec_read(codec, 0x1b, 0,
- AC_VERB_GET_PIN_SENSE, 0) & 0x80000000;
snd_hda_codec_amp_stereo(codec, 0x14, HDA_OUTPUT, 0,
HDA_AMP_MUTE, present ? HDA_AMP_MUTE : 0);
snd_hda_codec_amp_stereo(codec, 0x15, HDA_OUTPUT, 0,
@@ -8424,10 +8417,8 @@ static void alc888_lenovo_ms7195_front_automute(struct hda_codec *codec)
/* toggle RCA according to the front-jack state */
static void alc888_lenovo_ms7195_rca_automute(struct hda_codec *codec)
{
- unsigned int present;
+ unsigned int present = snd_hda_jack_detect(codec, 0x14);
- present = snd_hda_codec_read(codec, 0x14, 0,
- AC_VERB_GET_PIN_SENSE, 0) & 0x80000000;
snd_hda_codec_amp_stereo(codec, 0x15, HDA_OUTPUT, 0,
HDA_AMP_MUTE, present ? HDA_AMP_MUTE : 0);
}
@@ -8468,8 +8459,7 @@ static void alc883_clevo_m720_mic_automute(struct hda_codec *codec)
{
unsigned int present;
- present = snd_hda_codec_read(codec, 0x18, 0,
- AC_VERB_GET_PIN_SENSE, 0) & 0x80000000;
+ present = snd_hda_jack_detect(codec, 0x18);
snd_hda_codec_amp_stereo(codec, 0x0b, HDA_INPUT, 1,
HDA_AMP_MUTE, present ? HDA_AMP_MUTE : 0);
}
@@ -8520,24 +8510,16 @@ static void alc883_haier_w66_setup(struct hda_codec *codec)
static void alc883_lenovo_101e_ispeaker_automute(struct hda_codec *codec)
{
- unsigned int present;
- unsigned char bits;
+ int bits = snd_hda_jack_detect(codec, 0x14) ? HDA_AMP_MUTE : 0;
- present = snd_hda_codec_read(codec, 0x14, 0, AC_VERB_GET_PIN_SENSE, 0)
- & AC_PINSENSE_PRESENCE;
- bits = present ? HDA_AMP_MUTE : 0;
snd_hda_codec_amp_stereo(codec, 0x15, HDA_OUTPUT, 0,
HDA_AMP_MUTE, bits);
}
static void alc883_lenovo_101e_all_automute(struct hda_codec *codec)
{
- unsigned int present;
- unsigned char bits;
+ int bits = snd_hda_jack_detect(codec, 0x1b) ? HDA_AMP_MUTE : 0;
- present = snd_hda_codec_read(codec, 0x1b, 0,
- AC_VERB_GET_PIN_SENSE, 0) & 0x80000000;
- bits = present ? HDA_AMP_MUTE : 0;
snd_hda_codec_amp_stereo(codec, 0x15, HDA_OUTPUT, 0,
HDA_AMP_MUTE, bits);
snd_hda_codec_amp_stereo(codec, 0x14, HDA_OUTPUT, 0,
@@ -8688,8 +8670,7 @@ static void alc889A_mb31_automute(struct hda_codec *codec)
/* Mute only in 2ch or 4ch mode */
if (snd_hda_codec_read(codec, 0x15, 0, AC_VERB_GET_CONNECT_SEL, 0)
== 0x00) {
- present = snd_hda_codec_read(codec, 0x15, 0,
- AC_VERB_GET_PIN_SENSE, 0) & AC_PINSENSE_PRESENCE;
+ present = snd_hda_jack_detect(codec, 0x15);
snd_hda_codec_amp_stereo(codec, 0x14, HDA_OUTPUT, 0,
HDA_AMP_MUTE, present ? HDA_AMP_MUTE : 0);
snd_hda_codec_amp_stereo(codec, 0x16, HDA_OUTPUT, 0,
@@ -10032,10 +10013,8 @@ static void alc262_hp_master_update(struct hda_codec *codec)
static void alc262_hp_bpc_automute(struct hda_codec *codec)
{
struct alc_spec *spec = codec->spec;
- unsigned int presence;
- presence = snd_hda_codec_read(codec, 0x1b, 0,
- AC_VERB_GET_PIN_SENSE, 0);
- spec->jack_present = !!(presence & AC_PINSENSE_PRESENCE);
+
+ spec->jack_present = snd_hda_jack_detect(codec, 0x1b);
alc262_hp_master_update(codec);
}
@@ -10049,10 +10028,8 @@ static void alc262_hp_bpc_unsol_event(struct hda_codec *codec, unsigned int res)
static void alc262_hp_wildwest_automute(struct hda_codec *codec)
{
struct alc_spec *spec = codec->spec;
- unsigned int presence;
- presence = snd_hda_codec_read(codec, 0x15, 0,
- AC_VERB_GET_PIN_SENSE, 0);
- spec->jack_present = !!(presence & AC_PINSENSE_PRESENCE);
+
+ spec->jack_present = snd_hda_jack_detect(codec, 0x15);
alc262_hp_master_update(codec);
}
@@ -10286,13 +10263,8 @@ static void alc262_hippo_automute(struct hda_codec *codec)
{
struct alc_spec *spec = codec->spec;
hda_nid_t hp_nid = spec->autocfg.hp_pins[0];
- unsigned int present;
- /* need to execute and sync at first */
- snd_hda_codec_read(codec, hp_nid, 0, AC_VERB_SET_PIN_SENSE, 0);
- present = snd_hda_codec_read(codec, hp_nid, 0,
- AC_VERB_GET_PIN_SENSE, 0);
- spec->jack_present = (present & 0x80000000) != 0;
+ spec->jack_present = snd_hda_jack_detect(codec, hp_nid);
alc262_hippo_master_update(codec);
}
@@ -10618,21 +10590,8 @@ static void alc262_fujitsu_automute(struct hda_codec *codec, int force)
unsigned int mute;
if (force || !spec->sense_updated) {
- unsigned int present;
- /* need to execute and sync at first */
- snd_hda_codec_read(codec, 0x14, 0, AC_VERB_SET_PIN_SENSE, 0);
- /* check laptop HP jack */
- present = snd_hda_codec_read(codec, 0x14, 0,
- AC_VERB_GET_PIN_SENSE, 0);
- /* need to execute and sync at first */
- snd_hda_codec_read(codec, 0x1b, 0, AC_VERB_SET_PIN_SENSE, 0);
- /* check docking HP jack */
- present |= snd_hda_codec_read(codec, 0x1b, 0,
- AC_VERB_GET_PIN_SENSE, 0);
- if (present & AC_PINSENSE_PRESENCE)
- spec->jack_present = 1;
- else
- spec->jack_present = 0;
+ spec->jack_present = snd_hda_jack_detect(codec, 0x14) ||
+ snd_hda_jack_detect(codec, 0x1b);
spec->sense_updated = 1;
}
/* unmute internal speaker only if both HPs are unplugged and
@@ -10677,12 +10636,7 @@ static void alc262_lenovo_3000_automute(struct hda_codec *codec, int force)
unsigned int mute;
if (force || !spec->sense_updated) {
- unsigned int present_int_hp;
- /* need to execute and sync at first */
- snd_hda_codec_read(codec, 0x1b, 0, AC_VERB_SET_PIN_SENSE, 0);
- present_int_hp = snd_hda_codec_read(codec, 0x1b, 0,
- AC_VERB_GET_PIN_SENSE, 0);
- spec->jack_present = (present_int_hp & 0x80000000) != 0;
+ spec->jack_present = snd_hda_jack_detect(codec, 0x1b);
spec->sense_updated = 1;
}
if (spec->jack_present) {
@@ -10874,12 +10828,7 @@ static void alc262_ultra_automute(struct hda_codec *codec)
mute = 0;
/* auto-mute only when HP is used as HP */
if (!spec->cur_mux[0]) {
- 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 & AC_PINSENSE_PRESENCE) != 0;
+ spec->jack_present = snd_hda_jack_detect(codec, 0x15);
if (spec->jack_present)
mute = HDA_AMP_MUTE;
}
@@ -10956,7 +10905,6 @@ static int alc262_check_volbit(hda_nid_t nid)
static int alc262_add_out_vol_ctl(struct alc_spec *spec, hda_nid_t nid,
const char *pfx, int *vbits)
{
- char name[32];
unsigned long val;
int vbit;
@@ -10966,28 +10914,25 @@ static int alc262_add_out_vol_ctl(struct alc_spec *spec, hda_nid_t nid,
if (*vbits & vbit) /* a volume control for this mixer already there */
return 0;
*vbits |= vbit;
- snprintf(name, sizeof(name), "%s Playback Volume", pfx);
if (vbit == 2)
val = HDA_COMPOSE_AMP_VAL(0x0e, 2, 0, HDA_OUTPUT);
else
val = HDA_COMPOSE_AMP_VAL(0x0c, 3, 0, HDA_OUTPUT);
- return add_control(spec, ALC_CTL_WIDGET_VOL, name, val);
+ return add_pb_vol_ctrl(spec, ALC_CTL_WIDGET_VOL, pfx, val);
}
static int alc262_add_out_sw_ctl(struct alc_spec *spec, hda_nid_t nid,
const char *pfx)
{
- char name[32];
unsigned long val;
if (!nid)
return 0;
- snprintf(name, sizeof(name), "%s Playback Switch", pfx);
if (nid == 0x16)
val = HDA_COMPOSE_AMP_VAL(nid, 2, 0, HDA_OUTPUT);
else
val = HDA_COMPOSE_AMP_VAL(nid, 3, 0, HDA_OUTPUT);
- return add_control(spec, ALC_CTL_WIDGET_MUTE, name, val);
+ return add_pb_sw_ctrl(spec, ALC_CTL_WIDGET_MUTE, pfx, val);
}
/* add playback controls from the parsed DAC table */
@@ -11463,8 +11408,10 @@ static struct snd_pci_quirk alc262_cfg_tbl[] = {
SND_PCI_QUIRK(0x104d, 0x9025, "Sony VAIO Z21MN", ALC262_TOSHIBA_S06),
SND_PCI_QUIRK(0x104d, 0x9035, "Sony VAIO VGN-FW170J", ALC262_AUTO),
SND_PCI_QUIRK(0x104d, 0x9047, "Sony VAIO Type G", ALC262_AUTO),
+#if 0 /* disable the quirk since model=auto works better in recent versions */
SND_PCI_QUIRK_MASK(0x104d, 0xff00, 0x9000, "Sony VAIO",
ALC262_SONY_ASSAMD),
+#endif
SND_PCI_QUIRK(0x1179, 0x0001, "Toshiba dynabook SS RX1",
ALC262_TOSHIBA_RX1),
SND_PCI_QUIRK(0x1179, 0xff7b, "Toshiba S06", ALC262_TOSHIBA_S06),
@@ -11923,10 +11870,7 @@ static void alc268_acer_automute(struct hda_codec *codec, int force)
unsigned int mute;
if (force || !spec->sense_updated) {
- unsigned int present;
- present = snd_hda_codec_read(codec, 0x14, 0,
- AC_VERB_GET_PIN_SENSE, 0);
- spec->jack_present = (present & 0x80000000) != 0;
+ spec->jack_present = snd_hda_jack_detect(codec, 0x14);
spec->sense_updated = 1;
}
if (spec->jack_present)
@@ -12045,8 +11989,7 @@ static void alc268_aspire_one_speaker_automute(struct hda_codec *codec)
unsigned int present;
unsigned char bits;
- present = snd_hda_codec_read(codec, 0x15, 0,
- AC_VERB_GET_PIN_SENSE, 0) & 0x80000000;
+ present = snd_hda_jack_detect(codec, 0x15);
bits = present ? AMP_IN_MUTE(0) : 0;
snd_hda_codec_amp_stereo(codec, 0x0f, HDA_INPUT, 0,
AMP_IN_MUTE(0), bits);
@@ -12327,11 +12270,9 @@ static struct snd_kcontrol_new alc268_test_mixer[] = {
static int alc268_new_analog_output(struct alc_spec *spec, hda_nid_t nid,
const char *ctlname, int idx)
{
- char name[32];
hda_nid_t dac;
int err;
- sprintf(name, "%s Playback Volume", ctlname);
switch (nid) {
case 0x14:
case 0x16:
@@ -12345,7 +12286,7 @@ static int alc268_new_analog_output(struct alc_spec *spec, hda_nid_t nid,
}
if (spec->multiout.dac_nids[0] != dac &&
spec->multiout.dac_nids[1] != dac) {
- err = add_control(spec, ALC_CTL_WIDGET_VOL, name,
+ err = add_pb_vol_ctrl(spec, ALC_CTL_WIDGET_VOL, ctlname,
HDA_COMPOSE_AMP_VAL(dac, 3, idx,
HDA_OUTPUT));
if (err < 0)
@@ -12353,12 +12294,11 @@ static int alc268_new_analog_output(struct alc_spec *spec, hda_nid_t nid,
spec->multiout.dac_nids[spec->multiout.num_dacs++] = dac;
}
- sprintf(name, "%s Playback Switch", ctlname);
if (nid != 0x16)
- err = add_control(spec, ALC_CTL_WIDGET_MUTE, name,
+ err = add_pb_sw_ctrl(spec, ALC_CTL_WIDGET_MUTE, ctlname,
HDA_COMPOSE_AMP_VAL(nid, 3, idx, HDA_OUTPUT));
else /* mono */
- err = add_control(spec, ALC_CTL_WIDGET_MUTE, name,
+ err = add_pb_sw_ctrl(spec, ALC_CTL_WIDGET_MUTE, ctlname,
HDA_COMPOSE_AMP_VAL(nid, 2, idx, HDA_OUTPUT));
if (err < 0)
return err;
@@ -12388,8 +12328,7 @@ static int alc268_auto_create_multi_out_ctls(struct alc_spec *spec,
nid = cfg->speaker_pins[0];
if (nid == 0x1d) {
- err = add_control(spec, ALC_CTL_WIDGET_VOL,
- "Speaker Playback Volume",
+ err = add_pb_vol_ctrl(spec, ALC_CTL_WIDGET_VOL, "Speaker",
HDA_COMPOSE_AMP_VAL(nid, 3, 0, HDA_INPUT));
if (err < 0)
return err;
@@ -12407,8 +12346,7 @@ static int alc268_auto_create_multi_out_ctls(struct alc_spec *spec,
nid = cfg->line_out_pins[1] | cfg->line_out_pins[2];
if (nid == 0x16) {
- err = add_control(spec, ALC_CTL_WIDGET_MUTE,
- "Mono Playback Switch",
+ err = add_pb_sw_ctrl(spec, ALC_CTL_WIDGET_MUTE, "Mono",
HDA_COMPOSE_AMP_VAL(nid, 2, 0, HDA_OUTPUT));
if (err < 0)
return err;
@@ -13034,8 +12972,7 @@ static void alc269_quanta_fl1_speaker_automute(struct hda_codec *codec)
unsigned int present;
unsigned char bits;
- present = snd_hda_codec_read(codec, 0x15, 0,
- AC_VERB_GET_PIN_SENSE, 0) & 0x80000000;
+ present = snd_hda_jack_detect(codec, 0x15);
bits = present ? AMP_IN_MUTE(0) : 0;
snd_hda_codec_amp_stereo(codec, 0x0c, HDA_INPUT, 0,
AMP_IN_MUTE(0), bits);
@@ -13060,12 +12997,10 @@ static void alc269_lifebook_speaker_automute(struct hda_codec *codec)
unsigned char bits;
/* Check laptop headphone socket */
- present = snd_hda_codec_read(codec, 0x15, 0,
- AC_VERB_GET_PIN_SENSE, 0) & 0x80000000;
+ present = snd_hda_jack_detect(codec, 0x15);
/* Check port replicator headphone socket */
- present |= snd_hda_codec_read(codec, 0x1a, 0,
- AC_VERB_GET_PIN_SENSE, 0) & 0x80000000;
+ present |= snd_hda_jack_detect(codec, 0x1a);
bits = present ? AMP_IN_MUTE(0) : 0;
snd_hda_codec_amp_stereo(codec, 0x0c, HDA_INPUT, 0,
@@ -13089,11 +13024,8 @@ static void alc269_lifebook_mic_autoswitch(struct hda_codec *codec)
unsigned int present_laptop;
unsigned int present_dock;
- present_laptop = snd_hda_codec_read(codec, 0x18, 0,
- AC_VERB_GET_PIN_SENSE, 0) & 0x80000000;
-
- present_dock = snd_hda_codec_read(codec, 0x1b, 0,
- AC_VERB_GET_PIN_SENSE, 0) & 0x80000000;
+ present_laptop = snd_hda_jack_detect(codec, 0x18);
+ present_dock = snd_hda_jack_detect(codec, 0x1b);
/* Laptop mic port overrides dock mic port, design decision */
if (present_dock)
@@ -13178,8 +13110,7 @@ static void alc269_speaker_automute(struct hda_codec *codec)
unsigned int present;
unsigned char bits;
- present = snd_hda_codec_read(codec, 0x15, 0,
- AC_VERB_GET_PIN_SENSE, 0) & 0x80000000;
+ present = snd_hda_jack_detect(codec, 0x15);
bits = present ? AMP_IN_MUTE(0) : 0;
snd_hda_codec_amp_stereo(codec, 0x0c, HDA_INPUT, 0,
AMP_IN_MUTE(0), bits);
@@ -13525,6 +13456,15 @@ static int patch_alc269(struct hda_codec *codec)
alc_fix_pll_init(codec, 0x20, 0x04, 15);
+ if ((alc_read_coef_idx(codec, 0) & 0x00f0) == 0x0010){
+ kfree(codec->chip_name);
+ codec->chip_name = kstrdup("ALC259", GFP_KERNEL);
+ if (!codec->chip_name) {
+ alc_free(codec);
+ return -ENOMEM;
+ }
+ }
+
board_config = snd_hda_check_board_config(codec, ALC269_MODEL_LAST,
alc269_models,
alc269_cfg_tbl);
@@ -14157,10 +14097,8 @@ static struct hda_verb alc861_toshiba_init_verbs[] = {
/* toggle speaker-output according to the hp-jack state */
static void alc861_toshiba_automute(struct hda_codec *codec)
{
- unsigned int present;
+ unsigned int present = snd_hda_jack_detect(codec, 0x0f);
- present = snd_hda_codec_read(codec, 0x0f, 0,
- AC_VERB_GET_PIN_SENSE, 0) & 0x80000000;
snd_hda_codec_amp_stereo(codec, 0x16, HDA_INPUT, 0,
HDA_AMP_MUTE, present ? HDA_AMP_MUTE : 0);
snd_hda_codec_amp_stereo(codec, 0x1a, HDA_INPUT, 3,
@@ -14260,9 +14198,7 @@ static int alc861_auto_fill_dac_nids(struct hda_codec *codec,
static int alc861_create_out_sw(struct hda_codec *codec, const char *pfx,
hda_nid_t nid, unsigned int chs)
{
- char name[32];
- snprintf(name, sizeof(name), "%s Playback Switch", pfx);
- return add_control(codec->spec, ALC_CTL_WIDGET_MUTE, name,
+ return add_pb_sw_ctrl(codec->spec, ALC_CTL_WIDGET_MUTE, pfx,
HDA_COMPOSE_AMP_VAL(nid, chs, 0, HDA_OUTPUT));
}
@@ -14627,6 +14563,27 @@ static struct alc_config_preset alc861_presets[] = {
},
};
+/* Pin config fixes */
+enum {
+ PINFIX_FSC_AMILO_PI1505,
+};
+
+static struct alc_pincfg alc861_fsc_amilo_pi1505_pinfix[] = {
+ { 0x0b, 0x0221101f }, /* HP */
+ { 0x0f, 0x90170310 }, /* speaker */
+ { }
+};
+
+static const struct alc_fixup alc861_fixups[] = {
+ [PINFIX_FSC_AMILO_PI1505] = {
+ .pins = alc861_fsc_amilo_pi1505_pinfix
+ },
+};
+
+static struct snd_pci_quirk alc861_fixup_tbl[] = {
+ SND_PCI_QUIRK(0x1734, 0x10c7, "FSC Amilo Pi1505", PINFIX_FSC_AMILO_PI1505),
+ {}
+};
static int patch_alc861(struct hda_codec *codec)
{
@@ -14650,6 +14607,8 @@ static int patch_alc861(struct hda_codec *codec)
board_config = ALC861_AUTO;
}
+ alc_pick_fixup(codec, alc861_fixup_tbl, alc861_fixups);
+
if (board_config == ALC861_AUTO) {
/* automatic parse from the BIOS config */
err = alc861_parse_auto_config(codec);
@@ -15067,9 +15026,9 @@ static void alc861vd_lenovo_mic_automute(struct hda_codec *codec)
unsigned int present;
unsigned char bits;
- present = snd_hda_codec_read(codec, 0x18, 0,
- AC_VERB_GET_PIN_SENSE, 0) & 0x80000000;
+ present = snd_hda_jack_detect(codec, 0x18);
bits = present ? HDA_AMP_MUTE : 0;
+
snd_hda_codec_amp_stereo(codec, 0x0b, HDA_INPUT, 1,
HDA_AMP_MUTE, bits);
}
@@ -15386,7 +15345,6 @@ static void alc861vd_auto_init_analog_input(struct hda_codec *codec)
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;
@@ -15403,26 +15361,26 @@ static int alc861vd_auto_create_multi_out_ctls(struct alc_spec *spec,
if (i == 2) {
/* Center/LFE */
- err = add_control(spec, ALC_CTL_WIDGET_VOL,
- "Center Playback Volume",
+ err = add_pb_vol_ctrl(spec, ALC_CTL_WIDGET_VOL,
+ "Center",
HDA_COMPOSE_AMP_VAL(nid_v, 1, 0,
HDA_OUTPUT));
if (err < 0)
return err;
- err = add_control(spec, ALC_CTL_WIDGET_VOL,
- "LFE Playback Volume",
+ err = add_pb_vol_ctrl(spec, ALC_CTL_WIDGET_VOL,
+ "LFE",
HDA_COMPOSE_AMP_VAL(nid_v, 2, 0,
HDA_OUTPUT));
if (err < 0)
return err;
- err = add_control(spec, ALC_CTL_BIND_MUTE,
- "Center Playback Switch",
+ err = add_pb_sw_ctrl(spec, ALC_CTL_BIND_MUTE,
+ "Center",
HDA_COMPOSE_AMP_VAL(nid_s, 1, 2,
HDA_INPUT));
if (err < 0)
return err;
- err = add_control(spec, ALC_CTL_BIND_MUTE,
- "LFE Playback Switch",
+ err = add_pb_sw_ctrl(spec, ALC_CTL_BIND_MUTE,
+ "LFE",
HDA_COMPOSE_AMP_VAL(nid_s, 2, 2,
HDA_INPUT));
if (err < 0)
@@ -15437,8 +15395,7 @@ static int alc861vd_auto_create_multi_out_ctls(struct alc_spec *spec,
pfx = "PCM";
} else
pfx = chname[i];
- sprintf(name, "%s Playback Volume", pfx);
- err = add_control(spec, ALC_CTL_WIDGET_VOL, name,
+ err = add_pb_vol_ctrl(spec, ALC_CTL_WIDGET_VOL, pfx,
HDA_COMPOSE_AMP_VAL(nid_v, 3, 0,
HDA_OUTPUT));
if (err < 0)
@@ -15446,8 +15403,7 @@ static int alc861vd_auto_create_multi_out_ctls(struct alc_spec *spec,
if (cfg->line_outs == 1 &&
cfg->line_out_type == AUTO_PIN_SPEAKER_OUT)
pfx = "Speaker";
- sprintf(name, "%s Playback Switch", pfx);
- err = add_control(spec, ALC_CTL_BIND_MUTE, name,
+ err = add_pb_sw_ctrl(spec, ALC_CTL_BIND_MUTE, pfx,
HDA_COMPOSE_AMP_VAL(nid_s, 3, 2,
HDA_INPUT));
if (err < 0)
@@ -15465,7 +15421,6 @@ static int alc861vd_auto_create_extra_out(struct alc_spec *spec,
{
hda_nid_t nid_v, nid_s;
int err;
- char name[32];
if (!pin)
return 0;
@@ -15483,21 +15438,18 @@ static int alc861vd_auto_create_extra_out(struct alc_spec *spec,
nid_s = alc861vd_idx_to_mixer_switch(
alc880_fixed_pin_idx(pin));
- sprintf(name, "%s Playback Volume", pfx);
- err = add_control(spec, ALC_CTL_WIDGET_VOL, name,
+ err = add_pb_vol_ctrl(spec, ALC_CTL_WIDGET_VOL, pfx,
HDA_COMPOSE_AMP_VAL(nid_v, 3, 0, HDA_OUTPUT));
if (err < 0)
return err;
- sprintf(name, "%s Playback Switch", pfx);
- err = add_control(spec, ALC_CTL_BIND_MUTE, name,
+ err = add_pb_sw_ctrl(spec, ALC_CTL_BIND_MUTE, pfx,
HDA_COMPOSE_AMP_VAL(nid_s, 3, 2, HDA_INPUT));
if (err < 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);
- err = add_control(spec, ALC_CTL_WIDGET_MUTE, name,
+ err = add_pb_sw_ctrl(spec, ALC_CTL_WIDGET_MUTE, pfx,
HDA_COMPOSE_AMP_VAL(pin, 3, 0, HDA_OUTPUT));
if (err < 0)
return err;
@@ -16387,9 +16339,9 @@ static void alc662_lenovo_101e_ispeaker_automute(struct hda_codec *codec)
unsigned int present;
unsigned char bits;
- present = snd_hda_codec_read(codec, 0x14, 0,
- AC_VERB_GET_PIN_SENSE, 0) & 0x80000000;
+ present = snd_hda_jack_detect(codec, 0x14);
bits = present ? HDA_AMP_MUTE : 0;
+
snd_hda_codec_amp_stereo(codec, 0x15, HDA_OUTPUT, 0,
HDA_AMP_MUTE, bits);
}
@@ -16399,9 +16351,9 @@ static void alc662_lenovo_101e_all_automute(struct hda_codec *codec)
unsigned int present;
unsigned char bits;
- present = snd_hda_codec_read(codec, 0x1b, 0,
- AC_VERB_GET_PIN_SENSE, 0) & 0x80000000;
+ present = snd_hda_jack_detect(codec, 0x1b);
bits = present ? HDA_AMP_MUTE : 0;
+
snd_hda_codec_amp_stereo(codec, 0x15, HDA_OUTPUT, 0,
HDA_AMP_MUTE, bits);
snd_hda_codec_amp_stereo(codec, 0x14, HDA_OUTPUT, 0,
@@ -16460,9 +16412,7 @@ static void alc663_m51va_speaker_automute(struct hda_codec *codec)
unsigned int present;
unsigned char bits;
- present = snd_hda_codec_read(codec, 0x21, 0,
- AC_VERB_GET_PIN_SENSE, 0)
- & AC_PINSENSE_PRESENCE;
+ present = snd_hda_jack_detect(codec, 0x21);
bits = present ? HDA_AMP_MUTE : 0;
snd_hda_codec_amp_stereo(codec, 0x0c, HDA_INPUT, 0,
AMP_IN_MUTE(0), bits);
@@ -16475,9 +16425,7 @@ static void alc663_21jd_two_speaker_automute(struct hda_codec *codec)
unsigned int present;
unsigned char bits;
- present = snd_hda_codec_read(codec, 0x21, 0,
- AC_VERB_GET_PIN_SENSE, 0)
- & AC_PINSENSE_PRESENCE;
+ present = snd_hda_jack_detect(codec, 0x21);
bits = present ? HDA_AMP_MUTE : 0;
snd_hda_codec_amp_stereo(codec, 0x0c, HDA_INPUT, 0,
AMP_IN_MUTE(0), bits);
@@ -16494,9 +16442,7 @@ static void alc663_15jd_two_speaker_automute(struct hda_codec *codec)
unsigned int present;
unsigned char bits;
- present = snd_hda_codec_read(codec, 0x15, 0,
- AC_VERB_GET_PIN_SENSE, 0)
- & AC_PINSENSE_PRESENCE;
+ present = snd_hda_jack_detect(codec, 0x15);
bits = present ? HDA_AMP_MUTE : 0;
snd_hda_codec_amp_stereo(codec, 0x0c, HDA_INPUT, 0,
AMP_IN_MUTE(0), bits);
@@ -16513,9 +16459,7 @@ static void alc662_f5z_speaker_automute(struct hda_codec *codec)
unsigned int present;
unsigned char bits;
- present = snd_hda_codec_read(codec, 0x1b, 0,
- AC_VERB_GET_PIN_SENSE, 0)
- & AC_PINSENSE_PRESENCE;
+ present = snd_hda_jack_detect(codec, 0x1b);
bits = present ? 0 : PIN_OUT;
snd_hda_codec_write(codec, 0x14, 0,
AC_VERB_SET_PIN_WIDGET_CONTROL, bits);
@@ -16525,12 +16469,8 @@ static void alc663_two_hp_m1_speaker_automute(struct hda_codec *codec)
{
unsigned int present1, present2;
- present1 = snd_hda_codec_read(codec, 0x21, 0,
- AC_VERB_GET_PIN_SENSE, 0)
- & AC_PINSENSE_PRESENCE;
- present2 = snd_hda_codec_read(codec, 0x15, 0,
- AC_VERB_GET_PIN_SENSE, 0)
- & AC_PINSENSE_PRESENCE;
+ present1 = snd_hda_jack_detect(codec, 0x21);
+ present2 = snd_hda_jack_detect(codec, 0x15);
if (present1 || present2) {
snd_hda_codec_write_cache(codec, 0x14, 0,
@@ -16545,12 +16485,8 @@ static void alc663_two_hp_m2_speaker_automute(struct hda_codec *codec)
{
unsigned int present1, present2;
- present1 = snd_hda_codec_read(codec, 0x1b, 0,
- AC_VERB_GET_PIN_SENSE, 0)
- & AC_PINSENSE_PRESENCE;
- present2 = snd_hda_codec_read(codec, 0x15, 0,
- AC_VERB_GET_PIN_SENSE, 0)
- & AC_PINSENSE_PRESENCE;
+ present1 = snd_hda_jack_detect(codec, 0x1b);
+ present2 = snd_hda_jack_detect(codec, 0x15);
if (present1 || present2) {
snd_hda_codec_amp_stereo(codec, 0x0c, HDA_INPUT, 0,
@@ -16710,9 +16646,7 @@ static void alc663_g71v_hp_automute(struct hda_codec *codec)
unsigned int present;
unsigned char bits;
- present = snd_hda_codec_read(codec, 0x21, 0,
- AC_VERB_GET_PIN_SENSE, 0)
- & AC_PINSENSE_PRESENCE;
+ present = snd_hda_jack_detect(codec, 0x21);
bits = present ? HDA_AMP_MUTE : 0;
snd_hda_codec_amp_stereo(codec, 0x15, HDA_OUTPUT, 0,
HDA_AMP_MUTE, bits);
@@ -16725,9 +16659,7 @@ static void alc663_g71v_front_automute(struct hda_codec *codec)
unsigned int present;
unsigned char bits;
- present = snd_hda_codec_read(codec, 0x15, 0,
- AC_VERB_GET_PIN_SENSE, 0)
- & AC_PINSENSE_PRESENCE;
+ present = snd_hda_jack_detect(codec, 0x15);
bits = present ? HDA_AMP_MUTE : 0;
snd_hda_codec_amp_stereo(codec, 0x14, HDA_OUTPUT, 0,
HDA_AMP_MUTE, bits);
@@ -17264,21 +17196,17 @@ static int alc662_auto_fill_dac_nids(struct hda_codec *codec,
return 0;
}
-static int alc662_add_vol_ctl(struct alc_spec *spec, const char *pfx,
+static inline int alc662_add_vol_ctl(struct alc_spec *spec, const char *pfx,
hda_nid_t nid, unsigned int chs)
{
- char name[32];
- sprintf(name, "%s Playback Volume", pfx);
- return add_control(spec, ALC_CTL_WIDGET_VOL, name,
+ return add_pb_vol_ctrl(spec, ALC_CTL_WIDGET_VOL, pfx,
HDA_COMPOSE_AMP_VAL(nid, chs, 0, HDA_OUTPUT));
}
-static int alc662_add_sw_ctl(struct alc_spec *spec, const char *pfx,
+static inline int alc662_add_sw_ctl(struct alc_spec *spec, const char *pfx,
hda_nid_t nid, unsigned int chs)
{
- char name[32];
- sprintf(name, "%s Playback Switch", pfx);
- return add_control(spec, ALC_CTL_WIDGET_MUTE, name,
+ return add_pb_sw_ctrl(spec, ALC_CTL_WIDGET_MUTE, pfx,
HDA_COMPOSE_AMP_VAL(nid, chs, 0, HDA_INPUT));
}
@@ -17356,13 +17284,11 @@ static int alc662_auto_create_extra_out(struct hda_codec *codec, hda_nid_t pin,
return 0;
nid = alc662_look_for_dac(codec, pin);
if (!nid) {
- char name[32];
/* the corresponding DAC is already occupied */
if (!(get_wcaps(codec, pin) & AC_WCAP_OUT_AMP))
return 0; /* no way */
/* create a switch only */
- sprintf(name, "%s Playback Switch", pfx);
- return add_control(spec, ALC_CTL_WIDGET_MUTE, name,
+ return add_pb_sw_ctrl(spec, ALC_CTL_WIDGET_MUTE, pfx,
HDA_COMPOSE_AMP_VAL(pin, 3, 0, HDA_OUTPUT));
}
@@ -17538,6 +17464,15 @@ static int patch_alc662(struct hda_codec *codec)
alc_fix_pll_init(codec, 0x20, 0x04, 15);
+ if (alc_read_coef_idx(codec, 0)==0x8020){
+ kfree(codec->chip_name);
+ codec->chip_name = kstrdup("ALC661", GFP_KERNEL);
+ if (!codec->chip_name) {
+ alc_free(codec);
+ return -ENOMEM;
+ }
+ }
+
board_config = snd_hda_check_board_config(codec, ALC662_MODEL_LAST,
alc662_models,
alc662_cfg_tbl);
@@ -17604,6 +17539,20 @@ static int patch_alc662(struct hda_codec *codec)
return 0;
}
+static int patch_alc888(struct hda_codec *codec)
+{
+ if ((alc_read_coef_idx(codec, 0) & 0x00f0)==0x0030){
+ kfree(codec->chip_name);
+ codec->chip_name = kstrdup("ALC888-VD", GFP_KERNEL);
+ if (!codec->chip_name) {
+ alc_free(codec);
+ return -ENOMEM;
+ }
+ return patch_alc662(codec);
+ }
+ return patch_alc882(codec);
+}
+
/*
* patch entries
*/
@@ -17635,8 +17584,9 @@ static struct hda_codec_preset snd_hda_preset_realtek[] = {
{ .id = 0x10ec0887, .name = "ALC887", .patch = patch_alc882 },
{ .id = 0x10ec0888, .rev = 0x100101, .name = "ALC1200",
.patch = patch_alc882 },
- { .id = 0x10ec0888, .name = "ALC888", .patch = patch_alc882 },
+ { .id = 0x10ec0888, .name = "ALC888", .patch = patch_alc888 },
{ .id = 0x10ec0889, .name = "ALC889", .patch = patch_alc882 },
+ { .id = 0x10ec0892, .name = "ALC892", .patch = patch_alc662 },
{} /* terminator */
};
diff --git a/sound/pci/hda/patch_sigmatel.c b/sound/pci/hda/patch_sigmatel.c
index 86de305fc9f2..6b0bc040c3b1 100644
--- a/sound/pci/hda/patch_sigmatel.c
+++ b/sound/pci/hda/patch_sigmatel.c
@@ -93,6 +93,7 @@ enum {
STAC_92HD83XXX_REF,
STAC_92HD83XXX_PWR_REF,
STAC_DELL_S14,
+ STAC_92HD83XXX_HP,
STAC_92HD83XXX_MODELS
};
@@ -1085,7 +1086,7 @@ static int stac92xx_build_controls(struct hda_codec *codec)
if (!spec->auto_mic && spec->num_dmuxes > 0 &&
snd_hda_get_bool_hint(codec, "separate_dmux") == 1) {
stac_dmux_mixer.count = spec->num_dmuxes;
- err = snd_hda_ctl_add(codec,
+ err = snd_hda_ctl_add(codec, 0,
snd_ctl_new1(&stac_dmux_mixer, codec));
if (err < 0)
return err;
@@ -1101,7 +1102,7 @@ static int stac92xx_build_controls(struct hda_codec *codec)
spec->spdif_mute = 1;
}
stac_smux_mixer.count = spec->num_smuxes;
- err = snd_hda_ctl_add(codec,
+ err = snd_hda_ctl_add(codec, 0,
snd_ctl_new1(&stac_smux_mixer, codec));
if (err < 0)
return err;
@@ -1624,6 +1625,7 @@ static const char *stac92hd83xxx_models[STAC_92HD83XXX_MODELS] = {
[STAC_92HD83XXX_REF] = "ref",
[STAC_92HD83XXX_PWR_REF] = "mic-ref",
[STAC_DELL_S14] = "dell-s14",
+ [STAC_92HD83XXX_HP] = "hp",
};
static struct snd_pci_quirk stac92hd83xxx_cfg_tbl[] = {
@@ -1634,6 +1636,8 @@ static struct snd_pci_quirk stac92hd83xxx_cfg_tbl[] = {
"DFI LanParty", STAC_92HD83XXX_REF),
SND_PCI_QUIRK(PCI_VENDOR_ID_DELL, 0x02ba,
"unknown Dell", STAC_DELL_S14),
+ SND_PCI_QUIRK_MASK(PCI_VENDOR_ID_HP, 0xff00, 0x3600,
+ "HP", STAC_92HD83XXX_HP),
{} /* terminator */
};
@@ -2648,6 +2652,7 @@ static int stac92xx_clfe_switch_put(struct snd_kcontrol *kcontrol,
enum {
STAC_CTL_WIDGET_VOL,
STAC_CTL_WIDGET_MUTE,
+ STAC_CTL_WIDGET_MUTE_BEEP,
STAC_CTL_WIDGET_MONO_MUX,
STAC_CTL_WIDGET_HP_SWITCH,
STAC_CTL_WIDGET_IO_SWITCH,
@@ -2658,6 +2663,7 @@ enum {
static struct snd_kcontrol_new stac92xx_control_templates[] = {
HDA_CODEC_VOLUME(NULL, 0, 0, 0),
HDA_CODEC_MUTE(NULL, 0, 0, 0),
+ HDA_CODEC_MUTE_BEEP(NULL, 0, 0, 0),
STAC_MONO_MUX,
STAC_CODEC_HP_SWITCH(NULL),
STAC_CODEC_IO_SWITCH(NULL, 0),
@@ -2669,7 +2675,8 @@ static struct snd_kcontrol_new stac92xx_control_templates[] = {
static struct snd_kcontrol_new *
stac_control_new(struct sigmatel_spec *spec,
struct snd_kcontrol_new *ktemp,
- const char *name)
+ const char *name,
+ hda_nid_t nid)
{
struct snd_kcontrol_new *knew;
@@ -2685,6 +2692,8 @@ stac_control_new(struct sigmatel_spec *spec,
spec->kctls.alloced--;
return NULL;
}
+ if (nid)
+ knew->subdevice = HDA_SUBDEV_NID_FLAG | nid;
return knew;
}
@@ -2693,7 +2702,8 @@ static int stac92xx_add_control_temp(struct sigmatel_spec *spec,
int idx, const char *name,
unsigned long val)
{
- struct snd_kcontrol_new *knew = stac_control_new(spec, ktemp, name);
+ struct snd_kcontrol_new *knew = stac_control_new(spec, ktemp, name,
+ get_amp_nid_(val));
if (!knew)
return -ENOMEM;
knew->index = idx;
@@ -2764,7 +2774,7 @@ static int stac92xx_add_input_source(struct sigmatel_spec *spec)
if (!spec->num_adcs || imux->num_items <= 1)
return 0; /* no need for input source control */
knew = stac_control_new(spec, &stac_input_src_temp,
- stac_input_src_temp.name);
+ stac_input_src_temp.name, 0);
if (!knew)
return -ENOMEM;
knew->count = spec->num_adcs;
@@ -3221,12 +3231,15 @@ static int stac92xx_auto_create_beep_ctls(struct hda_codec *codec,
{
struct sigmatel_spec *spec = codec->spec;
u32 caps = query_amp_caps(codec, nid, HDA_OUTPUT);
- int err;
+ int err, type = STAC_CTL_WIDGET_MUTE_BEEP;
+
+ if (spec->anabeep_nid == nid)
+ type = STAC_CTL_WIDGET_MUTE;
/* check for mute support for the the amp */
if ((caps & AC_AMPCAP_MUTE) >> AC_AMPCAP_MUTE_SHIFT) {
- err = stac92xx_add_control(spec, STAC_CTL_WIDGET_MUTE,
- "PC Beep Playback Switch",
+ err = stac92xx_add_control(spec, type,
+ "Beep Playback Switch",
HDA_COMPOSE_AMP_VAL(nid, 1, 0, HDA_OUTPUT));
if (err < 0)
return err;
@@ -3235,7 +3248,7 @@ static int stac92xx_auto_create_beep_ctls(struct hda_codec *codec,
/* check to see if there is volume support for the amp */
if ((caps & AC_AMPCAP_NUM_STEPS) >> AC_AMPCAP_NUM_STEPS_SHIFT) {
err = stac92xx_add_control(spec, STAC_CTL_WIDGET_VOL,
- "PC Beep Playback Volume",
+ "Beep Playback Volume",
HDA_COMPOSE_AMP_VAL(nid, 1, 0, HDA_OUTPUT));
if (err < 0)
return err;
@@ -3258,12 +3271,7 @@ static int stac92xx_dig_beep_switch_put(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
- int enabled = !!ucontrol->value.integer.value[0];
- if (codec->beep->enabled != enabled) {
- codec->beep->enabled = enabled;
- return 1;
- }
- return 0;
+ return snd_hda_enable_beep_device(codec, ucontrol->value.integer.value[0]);
}
static struct snd_kcontrol_new stac92xx_dig_beep_ctrl = {
@@ -3276,7 +3284,7 @@ static struct snd_kcontrol_new stac92xx_dig_beep_ctrl = {
static int stac92xx_beep_switch_ctl(struct hda_codec *codec)
{
return stac92xx_add_control_temp(codec->spec, &stac92xx_dig_beep_ctrl,
- 0, "PC Beep Playback Switch", 0);
+ 0, "Beep Playback Switch", 0);
}
#endif
@@ -3631,6 +3639,26 @@ static void stac92xx_auto_init_hp_out(struct hda_codec *codec)
}
}
+static int is_dual_headphones(struct hda_codec *codec)
+{
+ struct sigmatel_spec *spec = codec->spec;
+ int i, valid_hps;
+
+ if (spec->autocfg.line_out_type != AUTO_PIN_SPEAKER_OUT ||
+ spec->autocfg.hp_outs <= 1)
+ return 0;
+ valid_hps = 0;
+ for (i = 0; i < spec->autocfg.hp_outs; i++) {
+ hda_nid_t nid = spec->autocfg.hp_pins[i];
+ unsigned int cfg = snd_hda_codec_get_pincfg(codec, nid);
+ if (get_defcfg_location(cfg) & AC_JACK_LOC_SEPARATE)
+ continue;
+ valid_hps++;
+ }
+ return (valid_hps > 1);
+}
+
+
static int stac92xx_parse_auto_config(struct hda_codec *codec, hda_nid_t dig_out, hda_nid_t dig_in)
{
struct sigmatel_spec *spec = codec->spec;
@@ -3647,8 +3675,7 @@ static int stac92xx_parse_auto_config(struct hda_codec *codec, hda_nid_t dig_out
/* If we have no real line-out pin and multiple hp-outs, HPs should
* be set up as multi-channel outputs.
*/
- if (spec->autocfg.line_out_type == AUTO_PIN_SPEAKER_OUT &&
- spec->autocfg.hp_outs > 1) {
+ if (is_dual_headphones(codec)) {
/* Copy hp_outs to line_outs, backup line_outs in
* speaker_outs so that the following routines can handle
* HP pins as primary outputs.
@@ -4329,6 +4356,28 @@ static void stac92xx_free_kctls(struct hda_codec *codec)
snd_array_free(&spec->kctls);
}
+static void stac92xx_shutup(struct hda_codec *codec)
+{
+ struct sigmatel_spec *spec = codec->spec;
+ int i;
+ hda_nid_t nid;
+
+ /* reset each pin before powering down DAC/ADC to avoid click noise */
+ nid = codec->start_nid;
+ for (i = 0; i < codec->num_nodes; i++, nid++) {
+ unsigned int wcaps = get_wcaps(codec, nid);
+ unsigned int wid_type = get_wcaps_type(wcaps);
+ if (wid_type == AC_WID_PIN)
+ snd_hda_codec_read(codec, nid, 0,
+ AC_VERB_SET_PIN_WIDGET_CONTROL, 0);
+ }
+
+ if (spec->eapd_mask)
+ stac_gpio_set(codec, spec->gpio_mask,
+ spec->gpio_dir, spec->gpio_data &
+ ~spec->eapd_mask);
+}
+
static void stac92xx_free(struct hda_codec *codec)
{
struct sigmatel_spec *spec = codec->spec;
@@ -4336,6 +4385,7 @@ static void stac92xx_free(struct hda_codec *codec)
if (! spec)
return;
+ stac92xx_shutup(codec);
stac92xx_free_jacks(codec);
snd_array_free(&spec->events);
@@ -4386,12 +4436,16 @@ static void stac92xx_reset_pinctl(struct hda_codec *codec, hda_nid_t nid,
pin_ctl & ~flag);
}
-static int get_pin_presence(struct hda_codec *codec, hda_nid_t nid)
+static inline int get_pin_presence(struct hda_codec *codec, hda_nid_t nid)
{
if (!nid)
return 0;
- if (snd_hda_codec_read(codec, nid, 0, AC_VERB_GET_PIN_SENSE, 0x00)
- & (1 << 31))
+ /* NOTE: we can't use snd_hda_jack_detect() here because STAC/IDT
+ * codecs behave wrongly when SET_PIN_SENSE is triggered, although
+ * the pincap gives TRIG_REQ bit.
+ */
+ if (snd_hda_codec_read(codec, nid, 0, AC_VERB_GET_PIN_SENSE, 0) &
+ AC_PINSENSE_PRESENCE)
return 1;
return 0;
}
@@ -4791,28 +4845,28 @@ static int stac92xx_hp_check_power_status(struct hda_codec *codec,
return 0;
}
-#endif
-static int stac92xx_suspend(struct hda_codec *codec, pm_message_t state)
+static int idt92hd83xxx_hp_check_power_status(struct hda_codec *codec,
+ hda_nid_t nid)
{
struct sigmatel_spec *spec = codec->spec;
- int i;
- hda_nid_t nid;
- /* reset each pin before powering down DAC/ADC to avoid click noise */
- nid = codec->start_nid;
- for (i = 0; i < codec->num_nodes; i++, nid++) {
- unsigned int wcaps = get_wcaps(codec, nid);
- unsigned int wid_type = get_wcaps_type(wcaps);
- if (wid_type == AC_WID_PIN)
- snd_hda_codec_read(codec, nid, 0,
- AC_VERB_SET_PIN_WIDGET_CONTROL, 0);
- }
+ if (nid != 0x13)
+ return 0;
+ if (snd_hda_codec_amp_read(codec, nid, 0, HDA_OUTPUT, 0) & HDA_AMP_MUTE)
+ spec->gpio_data |= spec->gpio_led; /* mute LED on */
+ else
+ spec->gpio_data &= ~spec->gpio_led; /* mute LED off */
+ stac_gpio_set(codec, spec->gpio_mask, spec->gpio_dir, spec->gpio_data);
- if (spec->eapd_mask)
- stac_gpio_set(codec, spec->gpio_mask,
- spec->gpio_dir, spec->gpio_data &
- ~spec->eapd_mask);
+ return 0;
+}
+
+#endif
+
+static int stac92xx_suspend(struct hda_codec *codec, pm_message_t state)
+{
+ stac92xx_shutup(codec);
return 0;
}
#endif
@@ -4827,6 +4881,7 @@ static struct hda_codec_ops stac92xx_patch_ops = {
.suspend = stac92xx_suspend,
.resume = stac92xx_resume,
#endif
+ .reboot_notify = stac92xx_shutup,
};
static int patch_stac9200(struct hda_codec *codec)
@@ -5172,6 +5227,22 @@ again:
break;
}
+ codec->patch_ops = stac92xx_patch_ops;
+
+ if (spec->board_config == STAC_92HD83XXX_HP)
+ spec->gpio_led = 0x01;
+
+#ifdef CONFIG_SND_HDA_POWER_SAVE
+ if (spec->gpio_led) {
+ spec->gpio_mask |= spec->gpio_led;
+ spec->gpio_dir |= spec->gpio_led;
+ spec->gpio_data |= spec->gpio_led;
+ /* register check_power_status callback. */
+ codec->patch_ops.check_power_status =
+ idt92hd83xxx_hp_check_power_status;
+ }
+#endif
+
err = stac92xx_parse_auto_config(codec, 0x1d, 0);
if (!err) {
if (spec->board_config < 0) {
@@ -5207,8 +5278,6 @@ again:
snd_hda_codec_write_cache(codec, nid, 0,
AC_VERB_SET_CONNECT_SEL, num_dacs);
- codec->patch_ops = stac92xx_patch_ops;
-
codec->proc_widget_hook = stac92hd_proc_hook;
return 0;
diff --git a/sound/pci/hda/patch_via.c b/sound/pci/hda/patch_via.c
index ee89db90c9b6..b70e26ad263f 100644
--- a/sound/pci/hda/patch_via.c
+++ b/sound/pci/hda/patch_via.c
@@ -1,10 +1,10 @@
/*
* Universal Interface for Intel High Definition Audio Codec
*
- * HD audio interface patch for VIA VT1702/VT1708/VT1709 codec
+ * HD audio interface patch for VIA VT17xx/VT18xx/VT20xx codec
*
- * Copyright (c) 2006-2008 Lydia Wang <lydiawang@viatech.com>
- * Takashi Iwai <tiwai@suse.de>
+ * (C) 2006-2009 VIA Technology, Inc.
+ * (C) 2006-2008 Takashi Iwai <tiwai@suse.de>
*
* This driver is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@@ -22,21 +22,27 @@
*/
/* * * * * * * * * * * * * * 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-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 */
-/* 2007-09-12 Lydia Wang Add EAPD enable during driver initialization */
-/* 2007-09-17 Lydia Wang Add VT1708B codec support */
+/* 2007-09-12 Lydia Wang Add EAPD enable during driver initialization */
+/* 2007-09-17 Lydia Wang Add VT1708B codec support */
/* 2007-11-14 Lydia Wang Add VT1708A codec HP and CD pin connect config */
/* 2008-02-03 Lydia Wang Fix Rear channels and Back channels inverse issue */
-/* 2008-03-06 Lydia Wang Add VT1702 codec and VT1708S codec support */
-/* 2008-04-09 Lydia Wang Add mute front speaker when HP plugin */
-/* 2008-04-09 Lydia Wang Add Independent HP feature */
+/* 2008-03-06 Lydia Wang Add VT1702 codec and VT1708S codec support */
+/* 2008-04-09 Lydia Wang Add mute front speaker when HP plugin */
+/* 2008-04-09 Lydia Wang Add Independent HP feature */
/* 2008-05-28 Lydia Wang Add second S/PDIF Out support for VT1702 */
-/* 2008-09-15 Logan Li Add VT1708S Mic Boost workaround/backdoor */
-/* */
+/* 2008-09-15 Logan Li Add VT1708S Mic Boost workaround/backdoor */
+/* 2009-02-16 Logan Li Add support for VT1718S */
+/* 2009-03-13 Logan Li Add support for VT1716S */
+/* 2009-04-14 Lydai Wang Add support for VT1828S and VT2020 */
+/* 2009-07-08 Lydia Wang Add support for VT2002P */
+/* 2009-07-21 Lydia Wang Add support for VT1812 */
+/* 2009-09-19 Lydia Wang Add support for VT1818S */
+/* */
/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
@@ -76,14 +82,6 @@
#define VT1702_HP_NID 0x17
#define VT1702_DIGOUT_NID 0x11
-#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)
-#define IS_VT1708B_8CH_VENDORID(x) ((x) >= 0x1106e720 && (x) <= 0x1106e723)
-#define IS_VT1708B_4CH_VENDORID(x) ((x) >= 0x1106e724 && (x) <= 0x1106e727)
-#define IS_VT1708S_VENDORID(x) ((x) >= 0x11060397 && (x) <= 0x11067397)
-#define IS_VT1702_VENDORID(x) ((x) >= 0x11060398 && (x) <= 0x11067398)
-
enum VIA_HDA_CODEC {
UNKNOWN = -1,
VT1708,
@@ -92,12 +90,76 @@ enum VIA_HDA_CODEC {
VT1708B_8CH,
VT1708B_4CH,
VT1708S,
+ VT1708BCE,
VT1702,
+ VT1718S,
+ VT1716S,
+ VT2002P,
+ VT1812,
CODEC_TYPES,
};
-static enum VIA_HDA_CODEC get_codec_type(u32 vendor_id)
+struct via_spec {
+ /* codec parameterization */
+ struct snd_kcontrol_new *mixers[6];
+ unsigned int num_mixers;
+
+ struct hda_verb *init_verbs[5];
+ unsigned int num_iverbs;
+
+ 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;
+ hda_nid_t slave_dig_outs[2];
+
+ /* capture */
+ unsigned int num_adc_nids;
+ hda_nid_t *adc_nids;
+ hda_nid_t mux_nids[3];
+ hda_nid_t dig_in_nid;
+ hda_nid_t dig_in_pin;
+
+ /* capture source */
+ const struct hda_input_mux *input_mux;
+ unsigned int cur_mux[3];
+
+ /* PCM information */
+ struct hda_pcm pcm_rec[3];
+
+ /* dynamic controls, init_verbs and input_mux */
+ struct auto_pin_cfg autocfg;
+ struct snd_array kctls;
+ struct hda_input_mux private_imux[2];
+ hda_nid_t private_dac_nids[AUTO_CFG_MAX_OUTS];
+
+ /* HP mode source */
+ const struct hda_input_mux *hp_mux;
+ unsigned int hp_independent_mode;
+ unsigned int hp_independent_mode_index;
+ unsigned int smart51_enabled;
+ unsigned int dmic_enabled;
+ enum VIA_HDA_CODEC codec_type;
+
+ /* work to check hp jack state */
+ struct hda_codec *codec;
+ struct delayed_work vt1708_hp_work;
+ int vt1708_jack_detectect;
+ int vt1708_hp_present;
+#ifdef CONFIG_SND_HDA_POWER_SAVE
+ struct hda_loopback_check loopback;
+#endif
+};
+
+static enum VIA_HDA_CODEC get_codec_type(struct hda_codec *codec)
{
+ u32 vendor_id = codec->vendor_id;
u16 ven_id = vendor_id >> 16;
u16 dev_id = vendor_id & 0xffff;
enum VIA_HDA_CODEC codec_type;
@@ -111,9 +173,11 @@ static enum VIA_HDA_CODEC get_codec_type(u32 vendor_id)
codec_type = VT1709_10CH;
else if (dev_id >= 0xe714 && dev_id <= 0xe717)
codec_type = VT1709_6CH;
- else if (dev_id >= 0xe720 && dev_id <= 0xe723)
+ else if (dev_id >= 0xe720 && dev_id <= 0xe723) {
codec_type = VT1708B_8CH;
- else if (dev_id >= 0xe724 && dev_id <= 0xe727)
+ if (snd_hda_param_read(codec, 0x16, AC_PAR_CONNLIST_LEN) == 0x7)
+ codec_type = VT1708BCE;
+ } else if (dev_id >= 0xe724 && dev_id <= 0xe727)
codec_type = VT1708B_4CH;
else if ((dev_id & 0xfff) == 0x397
&& (dev_id >> 12) < 8)
@@ -121,6 +185,19 @@ static enum VIA_HDA_CODEC get_codec_type(u32 vendor_id)
else if ((dev_id & 0xfff) == 0x398
&& (dev_id >> 12) < 8)
codec_type = VT1702;
+ else if ((dev_id & 0xfff) == 0x428
+ && (dev_id >> 12) < 8)
+ codec_type = VT1718S;
+ else if (dev_id == 0x0433 || dev_id == 0xa721)
+ codec_type = VT1716S;
+ else if (dev_id == 0x0441 || dev_id == 0x4441)
+ codec_type = VT1718S;
+ else if (dev_id == 0x0438 || dev_id == 0x4438)
+ codec_type = VT2002P;
+ else if (dev_id == 0x0448)
+ codec_type = VT1812;
+ else if (dev_id == 0x0440)
+ codec_type = VT1708S;
else
codec_type = UNKNOWN;
return codec_type;
@@ -128,10 +205,16 @@ static enum VIA_HDA_CODEC get_codec_type(u32 vendor_id)
#define VIA_HP_EVENT 0x01
#define VIA_GPIO_EVENT 0x02
+#define VIA_JACK_EVENT 0x04
+#define VIA_MONO_EVENT 0x08
+#define VIA_SPEAKER_EVENT 0x10
+#define VIA_BIND_HP_EVENT 0x20
enum {
VIA_CTL_WIDGET_VOL,
VIA_CTL_WIDGET_MUTE,
+ VIA_CTL_WIDGET_ANALOG_MUTE,
+ VIA_CTL_WIDGET_BIND_PIN_MUTE,
};
enum {
@@ -141,99 +224,162 @@ enum {
AUTO_SEQ_SIDE
};
-/* Some VT1708S based boards gets the micboost setting wrong, so we have
- * to apply some brute-force and re-write the TLV's by software. */
-static int mic_boost_tlv(struct snd_kcontrol *kcontrol, int op_flag,
- unsigned int size, unsigned int __user *_tlv)
+static void analog_low_current_mode(struct hda_codec *codec, int stream_idle);
+static void set_jack_power_state(struct hda_codec *codec);
+static int is_aa_path_mute(struct hda_codec *codec);
+
+static void vt1708_start_hp_work(struct via_spec *spec)
{
- struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
- hda_nid_t nid = get_amp_nid(kcontrol);
+ if (spec->codec_type != VT1708 || spec->autocfg.hp_pins[0] == 0)
+ return;
+ snd_hda_codec_write(spec->codec, 0x1, 0, 0xf81,
+ !spec->vt1708_jack_detectect);
+ if (!delayed_work_pending(&spec->vt1708_hp_work))
+ schedule_delayed_work(&spec->vt1708_hp_work,
+ msecs_to_jiffies(100));
+}
- if (get_codec_type(codec->vendor_id) == VT1708S
- && (nid == 0x1a || nid == 0x1e)) {
- if (size < 4 * sizeof(unsigned int))
- return -ENOMEM;
- if (put_user(1, _tlv)) /* SNDRV_CTL_TLVT_DB_SCALE */
- return -EFAULT;
- if (put_user(2 * sizeof(unsigned int), _tlv + 1))
- return -EFAULT;
- if (put_user(0, _tlv + 2)) /* offset = 0 */
- return -EFAULT;
- if (put_user(1000, _tlv + 3)) /* step size = 10 dB */
- return -EFAULT;
- }
- return 0;
+static void vt1708_stop_hp_work(struct via_spec *spec)
+{
+ if (spec->codec_type != VT1708 || spec->autocfg.hp_pins[0] == 0)
+ return;
+ if (snd_hda_get_bool_hint(spec->codec, "analog_loopback_hp_detect") == 1
+ && !is_aa_path_mute(spec->codec))
+ return;
+ snd_hda_codec_write(spec->codec, 0x1, 0, 0xf81,
+ !spec->vt1708_jack_detectect);
+ cancel_delayed_work(&spec->vt1708_hp_work);
+ flush_scheduled_work();
}
-static int mic_boost_volume_info(struct snd_kcontrol *kcontrol,
- struct snd_ctl_elem_info *uinfo)
+
+static int analog_input_switch_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
{
+ int change = snd_hda_mixer_amp_switch_put(kcontrol, ucontrol);
struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
- hda_nid_t nid = get_amp_nid(kcontrol);
- if (get_codec_type(codec->vendor_id) == VT1708S
- && (nid == 0x1a || nid == 0x1e)) {
- uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
- uinfo->count = 2;
- uinfo->value.integer.min = 0;
- uinfo->value.integer.max = 3;
+ set_jack_power_state(codec);
+ analog_low_current_mode(snd_kcontrol_chip(kcontrol), -1);
+ if (snd_hda_get_bool_hint(codec, "analog_loopback_hp_detect") == 1) {
+ if (is_aa_path_mute(codec))
+ vt1708_start_hp_work(codec->spec);
+ else
+ vt1708_stop_hp_work(codec->spec);
}
- return 0;
+ return change;
}
-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;
+/* modify .put = snd_hda_mixer_amp_switch_put */
+#define ANALOG_INPUT_MUTE \
+ { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, \
+ .name = NULL, \
+ .index = 0, \
+ .info = snd_hda_mixer_amp_switch_info, \
+ .get = snd_hda_mixer_amp_switch_get, \
+ .put = analog_input_switch_put, \
+ .private_value = HDA_COMPOSE_AMP_VAL(0, 3, 0, 0) }
- struct hda_verb *init_verbs[5];
- unsigned int num_iverbs;
+static void via_hp_bind_automute(struct hda_codec *codec);
- 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;
- hda_nid_t slave_dig_outs[2];
-
- /* capture */
- unsigned int num_adc_nids;
- hda_nid_t *adc_nids;
- hda_nid_t mux_nids[3];
- hda_nid_t dig_in_nid;
- hda_nid_t dig_in_pin;
+static int bind_pin_switch_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
+ struct via_spec *spec = codec->spec;
+ int i;
+ int change = 0;
- /* capture source */
- const struct hda_input_mux *input_mux;
- unsigned int cur_mux[3];
+ long *valp = ucontrol->value.integer.value;
+ int lmute, rmute;
+ if (strstr(kcontrol->id.name, "Switch") == NULL) {
+ snd_printd("Invalid control!\n");
+ return change;
+ }
+ change = snd_hda_mixer_amp_switch_put(kcontrol,
+ ucontrol);
+ /* Get mute value */
+ lmute = *valp ? 0 : HDA_AMP_MUTE;
+ valp++;
+ rmute = *valp ? 0 : HDA_AMP_MUTE;
+
+ /* Set hp pins */
+ if (!spec->hp_independent_mode) {
+ for (i = 0; i < spec->autocfg.hp_outs; i++) {
+ snd_hda_codec_amp_update(
+ codec, spec->autocfg.hp_pins[i],
+ 0, HDA_OUTPUT, 0, HDA_AMP_MUTE,
+ lmute);
+ snd_hda_codec_amp_update(
+ codec, spec->autocfg.hp_pins[i],
+ 1, HDA_OUTPUT, 0, HDA_AMP_MUTE,
+ rmute);
+ }
+ }
- /* PCM information */
- struct hda_pcm pcm_rec[3];
+ if (!lmute && !rmute) {
+ /* Line Outs */
+ for (i = 0; i < spec->autocfg.line_outs; i++)
+ snd_hda_codec_amp_stereo(
+ codec, spec->autocfg.line_out_pins[i],
+ HDA_OUTPUT, 0, HDA_AMP_MUTE, 0);
+ /* Speakers */
+ for (i = 0; i < spec->autocfg.speaker_outs; i++)
+ snd_hda_codec_amp_stereo(
+ codec, spec->autocfg.speaker_pins[i],
+ HDA_OUTPUT, 0, HDA_AMP_MUTE, 0);
+ /* unmute */
+ via_hp_bind_automute(codec);
- /* dynamic controls, init_verbs and input_mux */
- struct auto_pin_cfg autocfg;
- struct snd_array kctls;
- struct hda_input_mux private_imux[2];
- hda_nid_t private_dac_nids[AUTO_CFG_MAX_OUTS];
+ } else {
+ if (lmute) {
+ /* Mute all left channels */
+ for (i = 1; i < spec->autocfg.line_outs; i++)
+ snd_hda_codec_amp_update(
+ codec,
+ spec->autocfg.line_out_pins[i],
+ 0, HDA_OUTPUT, 0, HDA_AMP_MUTE,
+ lmute);
+ for (i = 0; i < spec->autocfg.speaker_outs; i++)
+ snd_hda_codec_amp_update(
+ codec,
+ spec->autocfg.speaker_pins[i],
+ 0, HDA_OUTPUT, 0, HDA_AMP_MUTE,
+ lmute);
+ }
+ if (rmute) {
+ /* mute all right channels */
+ for (i = 1; i < spec->autocfg.line_outs; i++)
+ snd_hda_codec_amp_update(
+ codec,
+ spec->autocfg.line_out_pins[i],
+ 1, HDA_OUTPUT, 0, HDA_AMP_MUTE,
+ rmute);
+ for (i = 0; i < spec->autocfg.speaker_outs; i++)
+ snd_hda_codec_amp_update(
+ codec,
+ spec->autocfg.speaker_pins[i],
+ 1, HDA_OUTPUT, 0, HDA_AMP_MUTE,
+ rmute);
+ }
+ }
+ return change;
+}
- /* HP mode source */
- const struct hda_input_mux *hp_mux;
- unsigned int hp_independent_mode;
+#define BIND_PIN_MUTE \
+ { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, \
+ .name = NULL, \
+ .index = 0, \
+ .info = snd_hda_mixer_amp_switch_info, \
+ .get = snd_hda_mixer_amp_switch_get, \
+ .put = bind_pin_switch_put, \
+ .private_value = HDA_COMPOSE_AMP_VAL(0, 3, 0, 0) }
-#ifdef CONFIG_SND_HDA_POWER_SAVE
- struct hda_loopback_check loopback;
-#endif
+static struct snd_kcontrol_new via_control_templates[] = {
+ HDA_CODEC_VOLUME(NULL, 0, 0, 0),
+ HDA_CODEC_MUTE(NULL, 0, 0, 0),
+ ANALOG_INPUT_MUTE,
+ BIND_PIN_MUTE,
};
static hda_nid_t vt1708_adc_nids[2] = {
@@ -261,6 +407,27 @@ static hda_nid_t vt1702_adc_nids[3] = {
0x12, 0x20, 0x1F
};
+static hda_nid_t vt1718S_adc_nids[2] = {
+ /* ADC1-2 */
+ 0x10, 0x11
+};
+
+static hda_nid_t vt1716S_adc_nids[2] = {
+ /* ADC1-2 */
+ 0x13, 0x14
+};
+
+static hda_nid_t vt2002P_adc_nids[2] = {
+ /* ADC1-2 */
+ 0x10, 0x11
+};
+
+static hda_nid_t vt1812_adc_nids[2] = {
+ /* ADC1-2 */
+ 0x10, 0x11
+};
+
+
/* add dynamic controls */
static int via_add_control(struct via_spec *spec, int type, const char *name,
unsigned long val)
@@ -271,10 +438,12 @@ static int via_add_control(struct via_spec *spec, int type, const char *name,
knew = snd_array_new(&spec->kctls);
if (!knew)
return -ENOMEM;
- *knew = vt1708_control_templates[type];
+ *knew = via_control_templates[type];
knew->name = kstrdup(name, GFP_KERNEL);
if (!knew->name)
return -ENOMEM;
+ if (get_amp_nid_(val))
+ knew->subdevice = HDA_SUBDEV_NID_FLAG | get_amp_nid_(val);
knew->private_value = val;
return 0;
}
@@ -293,8 +462,8 @@ static void via_free_kctls(struct hda_codec *codec)
}
/* 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)
+static int via_new_analog_input(struct via_spec *spec, const char *ctlname,
+ int idx, int mix_nid)
{
char name[32];
int err;
@@ -305,7 +474,7 @@ static int via_new_analog_input(struct via_spec *spec, hda_nid_t pin,
if (err < 0)
return err;
sprintf(name, "%s Playback Switch", ctlname);
- err = via_add_control(spec, VIA_CTL_WIDGET_MUTE, name,
+ err = via_add_control(spec, VIA_CTL_WIDGET_ANALOG_MUTE, name,
HDA_COMPOSE_AMP_VAL(mix_nid, 3, idx, HDA_INPUT));
if (err < 0)
return err;
@@ -322,7 +491,7 @@ static void via_auto_set_output_and_unmute(struct hda_codec *codec,
snd_hda_codec_write(codec, nid, 0, AC_VERB_SET_AMP_GAIN_MUTE,
AMP_OUT_UNMUTE);
if (snd_hda_query_pin_caps(codec, nid) & AC_PINCAP_EAPD)
- snd_hda_codec_write(codec, nid, 0,
+ snd_hda_codec_write(codec, nid, 0,
AC_VERB_SET_EAPD_BTLENABLE, 0x02);
}
@@ -343,10 +512,13 @@ static void via_auto_init_hp_out(struct hda_codec *codec)
{
struct via_spec *spec = codec->spec;
hda_nid_t pin;
+ int i;
- pin = spec->autocfg.hp_pins[0];
- if (pin) /* connect to front */
- via_auto_set_output_and_unmute(codec, pin, PIN_HP, 0);
+ for (i = 0; i < spec->autocfg.hp_outs; i++) {
+ pin = spec->autocfg.hp_pins[i];
+ 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)
@@ -364,6 +536,502 @@ static void via_auto_init_analog_input(struct hda_codec *codec)
}
}
+
+static int is_smart51_pins(struct via_spec *spec, hda_nid_t pin);
+
+static void set_pin_power_state(struct hda_codec *codec, hda_nid_t nid,
+ unsigned int *affected_parm)
+{
+ unsigned parm;
+ unsigned def_conf = snd_hda_codec_get_pincfg(codec, nid);
+ unsigned no_presence = (def_conf & AC_DEFCFG_MISC)
+ >> AC_DEFCFG_MISC_SHIFT
+ & AC_DEFCFG_MISC_NO_PRESENCE; /* do not support pin sense */
+ unsigned present = snd_hda_jack_detect(codec, nid);
+ struct via_spec *spec = codec->spec;
+ if ((spec->smart51_enabled && is_smart51_pins(spec, nid))
+ || ((no_presence || present)
+ && get_defcfg_connect(def_conf) != AC_JACK_PORT_NONE)) {
+ *affected_parm = AC_PWRST_D0; /* if it's connected */
+ parm = AC_PWRST_D0;
+ } else
+ parm = AC_PWRST_D3;
+
+ snd_hda_codec_write(codec, nid, 0, AC_VERB_SET_POWER_STATE, parm);
+}
+
+static void set_jack_power_state(struct hda_codec *codec)
+{
+ struct via_spec *spec = codec->spec;
+ int imux_is_smixer;
+ unsigned int parm;
+
+ if (spec->codec_type == VT1702) {
+ imux_is_smixer = snd_hda_codec_read(
+ codec, 0x13, 0, AC_VERB_GET_CONNECT_SEL, 0x00) == 3;
+ /* inputs */
+ /* PW 1/2/5 (14h/15h/18h) */
+ parm = AC_PWRST_D3;
+ set_pin_power_state(codec, 0x14, &parm);
+ set_pin_power_state(codec, 0x15, &parm);
+ set_pin_power_state(codec, 0x18, &parm);
+ if (imux_is_smixer)
+ parm = AC_PWRST_D0; /* SW0 = stereo mixer (idx 3) */
+ /* SW0 (13h), AIW 0/1/2 (12h/1fh/20h) */
+ snd_hda_codec_write(codec, 0x13, 0, AC_VERB_SET_POWER_STATE,
+ parm);
+ snd_hda_codec_write(codec, 0x12, 0, AC_VERB_SET_POWER_STATE,
+ parm);
+ snd_hda_codec_write(codec, 0x1f, 0, AC_VERB_SET_POWER_STATE,
+ parm);
+ snd_hda_codec_write(codec, 0x20, 0, AC_VERB_SET_POWER_STATE,
+ parm);
+
+ /* outputs */
+ /* PW 3/4 (16h/17h) */
+ parm = AC_PWRST_D3;
+ set_pin_power_state(codec, 0x16, &parm);
+ set_pin_power_state(codec, 0x17, &parm);
+ /* MW0 (1ah), AOW 0/1 (10h/1dh) */
+ snd_hda_codec_write(codec, 0x1a, 0, AC_VERB_SET_POWER_STATE,
+ imux_is_smixer ? AC_PWRST_D0 : parm);
+ snd_hda_codec_write(codec, 0x10, 0, AC_VERB_SET_POWER_STATE,
+ parm);
+ snd_hda_codec_write(codec, 0x1d, 0, AC_VERB_SET_POWER_STATE,
+ parm);
+ } else if (spec->codec_type == VT1708B_8CH
+ || spec->codec_type == VT1708B_4CH
+ || spec->codec_type == VT1708S) {
+ /* SW0 (17h) = stereo mixer */
+ int is_8ch = spec->codec_type != VT1708B_4CH;
+ imux_is_smixer = snd_hda_codec_read(
+ codec, 0x17, 0, AC_VERB_GET_CONNECT_SEL, 0x00)
+ == ((spec->codec_type == VT1708S) ? 5 : 0);
+ /* inputs */
+ /* PW 1/2/5 (1ah/1bh/1eh) */
+ parm = AC_PWRST_D3;
+ set_pin_power_state(codec, 0x1a, &parm);
+ set_pin_power_state(codec, 0x1b, &parm);
+ set_pin_power_state(codec, 0x1e, &parm);
+ if (imux_is_smixer)
+ parm = AC_PWRST_D0;
+ /* SW0 (17h), AIW 0/1 (13h/14h) */
+ snd_hda_codec_write(codec, 0x17, 0, AC_VERB_SET_POWER_STATE,
+ parm);
+ snd_hda_codec_write(codec, 0x13, 0, AC_VERB_SET_POWER_STATE,
+ parm);
+ snd_hda_codec_write(codec, 0x14, 0, AC_VERB_SET_POWER_STATE,
+ parm);
+
+ /* outputs */
+ /* PW0 (19h), SW1 (18h), AOW1 (11h) */
+ parm = AC_PWRST_D3;
+ set_pin_power_state(codec, 0x19, &parm);
+ snd_hda_codec_write(codec, 0x18, 0, AC_VERB_SET_POWER_STATE,
+ parm);
+ snd_hda_codec_write(codec, 0x11, 0, AC_VERB_SET_POWER_STATE,
+ parm);
+
+ /* PW6 (22h), SW2 (26h), AOW2 (24h) */
+ if (is_8ch) {
+ parm = AC_PWRST_D3;
+ set_pin_power_state(codec, 0x22, &parm);
+ snd_hda_codec_write(codec, 0x26, 0,
+ AC_VERB_SET_POWER_STATE, parm);
+ snd_hda_codec_write(codec, 0x24, 0,
+ AC_VERB_SET_POWER_STATE, parm);
+ }
+
+ /* PW 3/4/7 (1ch/1dh/23h) */
+ parm = AC_PWRST_D3;
+ /* force to D0 for internal Speaker */
+ set_pin_power_state(codec, 0x1c, &parm);
+ set_pin_power_state(codec, 0x1d, &parm);
+ if (is_8ch)
+ set_pin_power_state(codec, 0x23, &parm);
+ /* MW0 (16h), Sw3 (27h), AOW 0/3 (10h/25h) */
+ snd_hda_codec_write(codec, 0x16, 0, AC_VERB_SET_POWER_STATE,
+ imux_is_smixer ? AC_PWRST_D0 : parm);
+ snd_hda_codec_write(codec, 0x10, 0, AC_VERB_SET_POWER_STATE,
+ parm);
+ if (is_8ch) {
+ snd_hda_codec_write(codec, 0x25, 0,
+ AC_VERB_SET_POWER_STATE, parm);
+ snd_hda_codec_write(codec, 0x27, 0,
+ AC_VERB_SET_POWER_STATE, parm);
+ }
+ } else if (spec->codec_type == VT1718S) {
+ /* MUX6 (1eh) = stereo mixer */
+ imux_is_smixer = snd_hda_codec_read(
+ codec, 0x1e, 0, AC_VERB_GET_CONNECT_SEL, 0x00) == 5;
+ /* inputs */
+ /* PW 5/6/7 (29h/2ah/2bh) */
+ parm = AC_PWRST_D3;
+ set_pin_power_state(codec, 0x29, &parm);
+ set_pin_power_state(codec, 0x2a, &parm);
+ set_pin_power_state(codec, 0x2b, &parm);
+ if (imux_is_smixer)
+ parm = AC_PWRST_D0;
+ /* MUX6/7 (1eh/1fh), AIW 0/1 (10h/11h) */
+ snd_hda_codec_write(codec, 0x1e, 0, AC_VERB_SET_POWER_STATE,
+ parm);
+ snd_hda_codec_write(codec, 0x1f, 0, AC_VERB_SET_POWER_STATE,
+ parm);
+ snd_hda_codec_write(codec, 0x10, 0, AC_VERB_SET_POWER_STATE,
+ parm);
+ snd_hda_codec_write(codec, 0x11, 0, AC_VERB_SET_POWER_STATE,
+ parm);
+
+ /* outputs */
+ /* PW3 (27h), MW2 (1ah), AOW3 (bh) */
+ parm = AC_PWRST_D3;
+ set_pin_power_state(codec, 0x27, &parm);
+ snd_hda_codec_write(codec, 0x1a, 0, AC_VERB_SET_POWER_STATE,
+ parm);
+ snd_hda_codec_write(codec, 0xb, 0, AC_VERB_SET_POWER_STATE,
+ parm);
+
+ /* PW2 (26h), AOW2 (ah) */
+ parm = AC_PWRST_D3;
+ set_pin_power_state(codec, 0x26, &parm);
+ snd_hda_codec_write(codec, 0xa, 0, AC_VERB_SET_POWER_STATE,
+ parm);
+
+ /* PW0/1 (24h/25h) */
+ parm = AC_PWRST_D3;
+ set_pin_power_state(codec, 0x24, &parm);
+ set_pin_power_state(codec, 0x25, &parm);
+ if (!spec->hp_independent_mode) /* check for redirected HP */
+ set_pin_power_state(codec, 0x28, &parm);
+ snd_hda_codec_write(codec, 0x8, 0, AC_VERB_SET_POWER_STATE,
+ parm);
+ snd_hda_codec_write(codec, 0x9, 0, AC_VERB_SET_POWER_STATE,
+ parm);
+ /* MW9 (21h), Mw2 (1ah), AOW0 (8h) */
+ snd_hda_codec_write(codec, 0x21, 0, AC_VERB_SET_POWER_STATE,
+ imux_is_smixer ? AC_PWRST_D0 : parm);
+ if (spec->hp_independent_mode) {
+ /* PW4 (28h), MW3 (1bh), MUX1(34h), AOW4 (ch) */
+ parm = AC_PWRST_D3;
+ set_pin_power_state(codec, 0x28, &parm);
+ snd_hda_codec_write(codec, 0x1b, 0,
+ AC_VERB_SET_POWER_STATE, parm);
+ snd_hda_codec_write(codec, 0x34, 0,
+ AC_VERB_SET_POWER_STATE, parm);
+ snd_hda_codec_write(codec, 0xc, 0,
+ AC_VERB_SET_POWER_STATE, parm);
+ }
+ } else if (spec->codec_type == VT1716S) {
+ unsigned int mono_out, present;
+ /* SW0 (17h) = stereo mixer */
+ imux_is_smixer = snd_hda_codec_read(
+ codec, 0x17, 0, AC_VERB_GET_CONNECT_SEL, 0x00) == 5;
+ /* inputs */
+ /* PW 1/2/5 (1ah/1bh/1eh) */
+ parm = AC_PWRST_D3;
+ set_pin_power_state(codec, 0x1a, &parm);
+ set_pin_power_state(codec, 0x1b, &parm);
+ set_pin_power_state(codec, 0x1e, &parm);
+ if (imux_is_smixer)
+ parm = AC_PWRST_D0;
+ /* SW0 (17h), AIW0(13h) */
+ snd_hda_codec_write(codec, 0x17, 0, AC_VERB_SET_POWER_STATE,
+ parm);
+ snd_hda_codec_write(codec, 0x13, 0, AC_VERB_SET_POWER_STATE,
+ parm);
+
+ parm = AC_PWRST_D3;
+ set_pin_power_state(codec, 0x1e, &parm);
+ /* PW11 (22h) */
+ if (spec->dmic_enabled)
+ set_pin_power_state(codec, 0x22, &parm);
+ else
+ snd_hda_codec_write(
+ codec, 0x22, 0,
+ AC_VERB_SET_POWER_STATE, AC_PWRST_D3);
+
+ /* SW2(26h), AIW1(14h) */
+ snd_hda_codec_write(codec, 0x26, 0, AC_VERB_SET_POWER_STATE,
+ parm);
+ snd_hda_codec_write(codec, 0x14, 0, AC_VERB_SET_POWER_STATE,
+ parm);
+
+ /* outputs */
+ /* PW0 (19h), SW1 (18h), AOW1 (11h) */
+ parm = AC_PWRST_D3;
+ set_pin_power_state(codec, 0x19, &parm);
+ /* Smart 5.1 PW2(1bh) */
+ if (spec->smart51_enabled)
+ set_pin_power_state(codec, 0x1b, &parm);
+ snd_hda_codec_write(codec, 0x18, 0, AC_VERB_SET_POWER_STATE,
+ parm);
+ snd_hda_codec_write(codec, 0x11, 0, AC_VERB_SET_POWER_STATE,
+ parm);
+
+ /* PW7 (23h), SW3 (27h), AOW3 (25h) */
+ parm = AC_PWRST_D3;
+ set_pin_power_state(codec, 0x23, &parm);
+ /* Smart 5.1 PW1(1ah) */
+ if (spec->smart51_enabled)
+ set_pin_power_state(codec, 0x1a, &parm);
+ snd_hda_codec_write(codec, 0x27, 0, AC_VERB_SET_POWER_STATE,
+ parm);
+
+ /* Smart 5.1 PW5(1eh) */
+ if (spec->smart51_enabled)
+ set_pin_power_state(codec, 0x1e, &parm);
+ snd_hda_codec_write(codec, 0x25, 0, AC_VERB_SET_POWER_STATE,
+ parm);
+
+ /* Mono out */
+ /* SW4(28h)->MW1(29h)-> PW12 (2ah)*/
+ present = snd_hda_jack_detect(codec, 0x1c);
+ if (present)
+ mono_out = 0;
+ else {
+ present = snd_hda_jack_detect(codec, 0x1d);
+ if (!spec->hp_independent_mode && present)
+ mono_out = 0;
+ else
+ mono_out = 1;
+ }
+ parm = mono_out ? AC_PWRST_D0 : AC_PWRST_D3;
+ snd_hda_codec_write(codec, 0x28, 0, AC_VERB_SET_POWER_STATE,
+ parm);
+ snd_hda_codec_write(codec, 0x29, 0, AC_VERB_SET_POWER_STATE,
+ parm);
+ snd_hda_codec_write(codec, 0x2a, 0, AC_VERB_SET_POWER_STATE,
+ parm);
+
+ /* PW 3/4 (1ch/1dh) */
+ parm = AC_PWRST_D3;
+ set_pin_power_state(codec, 0x1c, &parm);
+ set_pin_power_state(codec, 0x1d, &parm);
+ /* HP Independent Mode, power on AOW3 */
+ if (spec->hp_independent_mode)
+ snd_hda_codec_write(codec, 0x25, 0,
+ AC_VERB_SET_POWER_STATE, parm);
+
+ /* force to D0 for internal Speaker */
+ /* MW0 (16h), AOW0 (10h) */
+ snd_hda_codec_write(codec, 0x16, 0, AC_VERB_SET_POWER_STATE,
+ imux_is_smixer ? AC_PWRST_D0 : parm);
+ snd_hda_codec_write(codec, 0x10, 0, AC_VERB_SET_POWER_STATE,
+ mono_out ? AC_PWRST_D0 : parm);
+ } else if (spec->codec_type == VT2002P) {
+ unsigned int present;
+ /* MUX9 (1eh) = stereo mixer */
+ imux_is_smixer = snd_hda_codec_read(
+ codec, 0x1e, 0, AC_VERB_GET_CONNECT_SEL, 0x00) == 3;
+ /* inputs */
+ /* PW 5/6/7 (29h/2ah/2bh) */
+ parm = AC_PWRST_D3;
+ set_pin_power_state(codec, 0x29, &parm);
+ set_pin_power_state(codec, 0x2a, &parm);
+ set_pin_power_state(codec, 0x2b, &parm);
+ if (imux_is_smixer)
+ parm = AC_PWRST_D0;
+ /* MUX9/10 (1eh/1fh), AIW 0/1 (10h/11h) */
+ snd_hda_codec_write(codec, 0x1e, 0,
+ AC_VERB_SET_POWER_STATE, parm);
+ snd_hda_codec_write(codec, 0x1f, 0,
+ AC_VERB_SET_POWER_STATE, parm);
+ snd_hda_codec_write(codec, 0x10, 0,
+ AC_VERB_SET_POWER_STATE, parm);
+ snd_hda_codec_write(codec, 0x11, 0,
+ AC_VERB_SET_POWER_STATE, parm);
+
+ /* outputs */
+ /* AOW0 (8h)*/
+ snd_hda_codec_write(codec, 0x8, 0,
+ AC_VERB_SET_POWER_STATE, AC_PWRST_D0);
+
+ /* PW4 (26h), MW4 (1ch), MUX4(37h) */
+ parm = AC_PWRST_D3;
+ set_pin_power_state(codec, 0x26, &parm);
+ snd_hda_codec_write(codec, 0x1c, 0,
+ AC_VERB_SET_POWER_STATE, parm);
+ snd_hda_codec_write(codec, 0x37,
+ 0, AC_VERB_SET_POWER_STATE, parm);
+
+ /* PW1 (25h), MW1 (19h), MUX1(35h), AOW1 (9h) */
+ parm = AC_PWRST_D3;
+ set_pin_power_state(codec, 0x25, &parm);
+ snd_hda_codec_write(codec, 0x19, 0,
+ AC_VERB_SET_POWER_STATE, parm);
+ snd_hda_codec_write(codec, 0x35, 0,
+ AC_VERB_SET_POWER_STATE, parm);
+ if (spec->hp_independent_mode) {
+ snd_hda_codec_write(codec, 0x9, 0,
+ AC_VERB_SET_POWER_STATE, parm);
+ }
+
+ /* Class-D */
+ /* PW0 (24h), MW0(18h), MUX0(34h) */
+ present = snd_hda_jack_detect(codec, 0x25);
+ parm = AC_PWRST_D3;
+ set_pin_power_state(codec, 0x24, &parm);
+ if (present) {
+ snd_hda_codec_write(
+ codec, 0x18, 0,
+ AC_VERB_SET_POWER_STATE, AC_PWRST_D3);
+ snd_hda_codec_write(
+ codec, 0x34, 0,
+ AC_VERB_SET_POWER_STATE, AC_PWRST_D3);
+ } else {
+ snd_hda_codec_write(
+ codec, 0x18, 0,
+ AC_VERB_SET_POWER_STATE, AC_PWRST_D0);
+ snd_hda_codec_write(
+ codec, 0x34, 0,
+ AC_VERB_SET_POWER_STATE, AC_PWRST_D0);
+ }
+
+ /* Mono Out */
+ /* PW15 (31h), MW8(17h), MUX8(3bh) */
+ present = snd_hda_jack_detect(codec, 0x26);
+ parm = AC_PWRST_D3;
+ set_pin_power_state(codec, 0x31, &parm);
+ if (present) {
+ snd_hda_codec_write(
+ codec, 0x17, 0,
+ AC_VERB_SET_POWER_STATE, AC_PWRST_D3);
+ snd_hda_codec_write(
+ codec, 0x3b, 0,
+ AC_VERB_SET_POWER_STATE, AC_PWRST_D3);
+ } else {
+ snd_hda_codec_write(
+ codec, 0x17, 0,
+ AC_VERB_SET_POWER_STATE, AC_PWRST_D0);
+ snd_hda_codec_write(
+ codec, 0x3b, 0,
+ AC_VERB_SET_POWER_STATE, AC_PWRST_D0);
+ }
+
+ /* MW9 (21h) */
+ if (imux_is_smixer || !is_aa_path_mute(codec))
+ snd_hda_codec_write(
+ codec, 0x21, 0,
+ AC_VERB_SET_POWER_STATE, AC_PWRST_D0);
+ else
+ snd_hda_codec_write(
+ codec, 0x21, 0,
+ AC_VERB_SET_POWER_STATE, AC_PWRST_D3);
+ } else if (spec->codec_type == VT1812) {
+ unsigned int present;
+ /* MUX10 (1eh) = stereo mixer */
+ imux_is_smixer = snd_hda_codec_read(
+ codec, 0x1e, 0, AC_VERB_GET_CONNECT_SEL, 0x00) == 5;
+ /* inputs */
+ /* PW 5/6/7 (29h/2ah/2bh) */
+ parm = AC_PWRST_D3;
+ set_pin_power_state(codec, 0x29, &parm);
+ set_pin_power_state(codec, 0x2a, &parm);
+ set_pin_power_state(codec, 0x2b, &parm);
+ if (imux_is_smixer)
+ parm = AC_PWRST_D0;
+ /* MUX10/11 (1eh/1fh), AIW 0/1 (10h/11h) */
+ snd_hda_codec_write(codec, 0x1e, 0,
+ AC_VERB_SET_POWER_STATE, parm);
+ snd_hda_codec_write(codec, 0x1f, 0,
+ AC_VERB_SET_POWER_STATE, parm);
+ snd_hda_codec_write(codec, 0x10, 0,
+ AC_VERB_SET_POWER_STATE, parm);
+ snd_hda_codec_write(codec, 0x11, 0,
+ AC_VERB_SET_POWER_STATE, parm);
+
+ /* outputs */
+ /* AOW0 (8h)*/
+ snd_hda_codec_write(codec, 0x8, 0,
+ AC_VERB_SET_POWER_STATE, AC_PWRST_D0);
+
+ /* PW4 (28h), MW4 (18h), MUX4(38h) */
+ parm = AC_PWRST_D3;
+ set_pin_power_state(codec, 0x28, &parm);
+ snd_hda_codec_write(codec, 0x18, 0,
+ AC_VERB_SET_POWER_STATE, parm);
+ snd_hda_codec_write(codec, 0x38, 0,
+ AC_VERB_SET_POWER_STATE, parm);
+
+ /* PW1 (25h), MW1 (15h), MUX1(35h), AOW1 (9h) */
+ parm = AC_PWRST_D3;
+ set_pin_power_state(codec, 0x25, &parm);
+ snd_hda_codec_write(codec, 0x15, 0,
+ AC_VERB_SET_POWER_STATE, parm);
+ snd_hda_codec_write(codec, 0x35, 0,
+ AC_VERB_SET_POWER_STATE, parm);
+ if (spec->hp_independent_mode) {
+ snd_hda_codec_write(codec, 0x9, 0,
+ AC_VERB_SET_POWER_STATE, parm);
+ }
+
+ /* Internal Speaker */
+ /* PW0 (24h), MW0(14h), MUX0(34h) */
+ present = snd_hda_jack_detect(codec, 0x25);
+ parm = AC_PWRST_D3;
+ set_pin_power_state(codec, 0x24, &parm);
+ if (present) {
+ snd_hda_codec_write(codec, 0x14, 0,
+ AC_VERB_SET_POWER_STATE,
+ AC_PWRST_D3);
+ snd_hda_codec_write(codec, 0x34, 0,
+ AC_VERB_SET_POWER_STATE,
+ AC_PWRST_D3);
+ } else {
+ snd_hda_codec_write(codec, 0x14, 0,
+ AC_VERB_SET_POWER_STATE,
+ AC_PWRST_D0);
+ snd_hda_codec_write(codec, 0x34, 0,
+ AC_VERB_SET_POWER_STATE,
+ AC_PWRST_D0);
+ }
+ /* Mono Out */
+ /* PW13 (31h), MW13(1ch), MUX13(3ch), MW14(3eh) */
+ present = snd_hda_jack_detect(codec, 0x28);
+ parm = AC_PWRST_D3;
+ set_pin_power_state(codec, 0x31, &parm);
+ if (present) {
+ snd_hda_codec_write(codec, 0x1c, 0,
+ AC_VERB_SET_POWER_STATE,
+ AC_PWRST_D3);
+ snd_hda_codec_write(codec, 0x3c, 0,
+ AC_VERB_SET_POWER_STATE,
+ AC_PWRST_D3);
+ snd_hda_codec_write(codec, 0x3e, 0,
+ AC_VERB_SET_POWER_STATE,
+ AC_PWRST_D3);
+ } else {
+ snd_hda_codec_write(codec, 0x1c, 0,
+ AC_VERB_SET_POWER_STATE,
+ AC_PWRST_D0);
+ snd_hda_codec_write(codec, 0x3c, 0,
+ AC_VERB_SET_POWER_STATE,
+ AC_PWRST_D0);
+ snd_hda_codec_write(codec, 0x3e, 0,
+ AC_VERB_SET_POWER_STATE,
+ AC_PWRST_D0);
+ }
+
+ /* PW15 (33h), MW15 (1dh), MUX15(3dh) */
+ parm = AC_PWRST_D3;
+ set_pin_power_state(codec, 0x33, &parm);
+ snd_hda_codec_write(codec, 0x1d, 0,
+ AC_VERB_SET_POWER_STATE, parm);
+ snd_hda_codec_write(codec, 0x3d, 0,
+ AC_VERB_SET_POWER_STATE, parm);
+
+ /* MW9 (21h) */
+ if (imux_is_smixer || !is_aa_path_mute(codec))
+ snd_hda_codec_write(
+ codec, 0x21, 0,
+ AC_VERB_SET_POWER_STATE, AC_PWRST_D0);
+ else
+ snd_hda_codec_write(
+ codec, 0x21, 0,
+ AC_VERB_SET_POWER_STATE, AC_PWRST_D3);
+ }
+}
+
/*
* input MUX handling
*/
@@ -395,6 +1063,14 @@ static int via_mux_enum_put(struct snd_kcontrol *kcontrol,
if (!spec->mux_nids[adc_idx])
return -EINVAL;
+ /* switch to D0 beofre change index */
+ if (snd_hda_codec_read(codec, spec->mux_nids[adc_idx], 0,
+ AC_VERB_GET_POWER_STATE, 0x00) != AC_PWRST_D0)
+ snd_hda_codec_write(codec, spec->mux_nids[adc_idx], 0,
+ AC_VERB_SET_POWER_STATE, AC_PWRST_D0);
+ /* update jack power state */
+ set_jack_power_state(codec);
+
return snd_hda_input_mux_put(codec, spec->input_mux, ucontrol,
spec->mux_nids[adc_idx],
&spec->cur_mux[adc_idx]);
@@ -413,16 +1089,74 @@ static int via_independent_hp_get(struct snd_kcontrol *kcontrol,
{
struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
struct via_spec *spec = codec->spec;
- hda_nid_t nid = spec->autocfg.hp_pins[0];
- unsigned int pinsel = snd_hda_codec_read(codec, nid, 0,
- AC_VERB_GET_CONNECT_SEL,
- 0x00);
-
+ hda_nid_t nid;
+ unsigned int pinsel;
+
+ switch (spec->codec_type) {
+ case VT1718S:
+ nid = 0x34;
+ break;
+ case VT2002P:
+ nid = 0x35;
+ break;
+ case VT1812:
+ nid = 0x3d;
+ break;
+ default:
+ nid = spec->autocfg.hp_pins[0];
+ break;
+ }
+ /* use !! to translate conn sel 2 for VT1718S */
+ pinsel = !!snd_hda_codec_read(codec, nid, 0,
+ AC_VERB_GET_CONNECT_SEL,
+ 0x00);
ucontrol->value.enumerated.item[0] = pinsel;
return 0;
}
+static void activate_ctl(struct hda_codec *codec, const char *name, int active)
+{
+ struct snd_kcontrol *ctl = snd_hda_find_mixer_ctl(codec, name);
+ if (ctl) {
+ ctl->vd[0].access &= ~SNDRV_CTL_ELEM_ACCESS_INACTIVE;
+ ctl->vd[0].access |= active
+ ? 0 : SNDRV_CTL_ELEM_ACCESS_INACTIVE;
+ snd_ctl_notify(codec->bus->card,
+ SNDRV_CTL_EVENT_MASK_VALUE, &ctl->id);
+ }
+}
+
+static int update_side_mute_status(struct hda_codec *codec)
+{
+ /* mute side channel */
+ struct via_spec *spec = codec->spec;
+ unsigned int parm = spec->hp_independent_mode
+ ? AMP_OUT_MUTE : AMP_OUT_UNMUTE;
+ hda_nid_t sw3;
+
+ switch (spec->codec_type) {
+ case VT1708:
+ sw3 = 0x1b;
+ break;
+ case VT1709_10CH:
+ sw3 = 0x29;
+ break;
+ case VT1708B_8CH:
+ case VT1708S:
+ sw3 = 0x27;
+ break;
+ default:
+ sw3 = 0;
+ break;
+ }
+
+ if (sw3)
+ snd_hda_codec_write(codec, sw3, 0, AC_VERB_SET_AMP_GAIN_MUTE,
+ parm);
+ return 0;
+}
+
static int via_independent_hp_put(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
@@ -430,47 +1164,46 @@ static int via_independent_hp_put(struct snd_kcontrol *kcontrol,
struct via_spec *spec = codec->spec;
hda_nid_t nid = spec->autocfg.hp_pins[0];
unsigned int pinsel = ucontrol->value.enumerated.item[0];
- unsigned int con_nid = snd_hda_codec_read(codec, nid, 0,
- AC_VERB_GET_CONNECT_LIST, 0) & 0xff;
-
- if (con_nid == spec->multiout.hp_nid) {
- if (pinsel == 0) {
- if (!spec->hp_independent_mode) {
- if (spec->multiout.num_dacs > 1)
- spec->multiout.num_dacs -= 1;
- spec->hp_independent_mode = 1;
- }
- } else if (pinsel == 1) {
- if (spec->hp_independent_mode) {
- if (spec->multiout.num_dacs > 1)
- spec->multiout.num_dacs += 1;
- spec->hp_independent_mode = 0;
- }
- }
- } else {
- if (pinsel == 0) {
- if (spec->hp_independent_mode) {
- if (spec->multiout.num_dacs > 1)
- spec->multiout.num_dacs += 1;
- spec->hp_independent_mode = 0;
- }
- } else if (pinsel == 1) {
- if (!spec->hp_independent_mode) {
- if (spec->multiout.num_dacs > 1)
- spec->multiout.num_dacs -= 1;
- spec->hp_independent_mode = 1;
- }
- }
+ /* Get Independent Mode index of headphone pin widget */
+ spec->hp_independent_mode = spec->hp_independent_mode_index == pinsel
+ ? 1 : 0;
+
+ switch (spec->codec_type) {
+ case VT1718S:
+ nid = 0x34;
+ pinsel = pinsel ? 2 : 0; /* indep HP use AOW4 (index 2) */
+ spec->multiout.num_dacs = 4;
+ break;
+ case VT2002P:
+ nid = 0x35;
+ break;
+ case VT1812:
+ nid = 0x3d;
+ break;
+ default:
+ nid = spec->autocfg.hp_pins[0];
+ break;
+ }
+ snd_hda_codec_write(codec, nid, 0, AC_VERB_SET_CONNECT_SEL, pinsel);
+
+ if (spec->multiout.hp_nid && spec->multiout.hp_nid
+ != spec->multiout.dac_nids[HDA_FRONT])
+ snd_hda_codec_setup_stream(codec, spec->multiout.hp_nid,
+ 0, 0, 0);
+
+ update_side_mute_status(codec);
+ /* update HP volume/swtich active state */
+ if (spec->codec_type == VT1708S
+ || spec->codec_type == VT1702
+ || spec->codec_type == VT1718S
+ || spec->codec_type == VT1716S
+ || spec->codec_type == VT2002P
+ || spec->codec_type == VT1812) {
+ activate_ctl(codec, "Headphone Playback Volume",
+ spec->hp_independent_mode);
+ activate_ctl(codec, "Headphone Playback Switch",
+ spec->hp_independent_mode);
}
- snd_hda_codec_write(codec, nid, 0, AC_VERB_SET_CONNECT_SEL,
- pinsel);
-
- if (spec->multiout.hp_nid &&
- spec->multiout.hp_nid != spec->multiout.dac_nids[HDA_FRONT])
- snd_hda_codec_setup_stream(codec,
- spec->multiout.hp_nid,
- 0, 0, 0);
-
return 0;
}
@@ -486,6 +1219,175 @@ static struct snd_kcontrol_new via_hp_mixer[] = {
{ } /* end */
};
+static void notify_aa_path_ctls(struct hda_codec *codec)
+{
+ int i;
+ struct snd_ctl_elem_id id;
+ const char *labels[] = {"Mic", "Front Mic", "Line"};
+
+ memset(&id, 0, sizeof(id));
+ id.iface = SNDRV_CTL_ELEM_IFACE_MIXER;
+ for (i = 0; i < ARRAY_SIZE(labels); i++) {
+ sprintf(id.name, "%s Playback Volume", labels[i]);
+ snd_ctl_notify(codec->bus->card, SNDRV_CTL_EVENT_MASK_VALUE,
+ &id);
+ }
+}
+
+static void mute_aa_path(struct hda_codec *codec, int mute)
+{
+ struct via_spec *spec = codec->spec;
+ hda_nid_t nid_mixer;
+ int start_idx;
+ int end_idx;
+ int i;
+ /* get nid of MW0 and start & end index */
+ switch (spec->codec_type) {
+ case VT1708:
+ nid_mixer = 0x17;
+ start_idx = 2;
+ end_idx = 4;
+ break;
+ case VT1709_10CH:
+ case VT1709_6CH:
+ nid_mixer = 0x18;
+ start_idx = 2;
+ end_idx = 4;
+ break;
+ case VT1708B_8CH:
+ case VT1708B_4CH:
+ case VT1708S:
+ case VT1716S:
+ nid_mixer = 0x16;
+ start_idx = 2;
+ end_idx = 4;
+ break;
+ default:
+ return;
+ }
+ /* check AA path's mute status */
+ for (i = start_idx; i <= end_idx; i++) {
+ int val = mute ? HDA_AMP_MUTE : HDA_AMP_UNMUTE;
+ snd_hda_codec_amp_stereo(codec, nid_mixer, HDA_INPUT, i,
+ HDA_AMP_MUTE, val);
+ }
+}
+static int is_smart51_pins(struct via_spec *spec, hda_nid_t pin)
+{
+ int res = 0;
+ int index;
+ for (index = AUTO_PIN_MIC; index < AUTO_PIN_FRONT_LINE; index++) {
+ if (pin == spec->autocfg.input_pins[index]) {
+ res = 1;
+ break;
+ }
+ }
+ return res;
+}
+
+static int via_smart51_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 via_smart51_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
+ struct via_spec *spec = codec->spec;
+ int index[] = { AUTO_PIN_MIC, AUTO_PIN_FRONT_MIC, AUTO_PIN_LINE };
+ int on = 1;
+ int i;
+
+ for (i = 0; i < ARRAY_SIZE(index); i++) {
+ hda_nid_t nid = spec->autocfg.input_pins[index[i]];
+ if (nid) {
+ int ctl =
+ snd_hda_codec_read(codec, nid, 0,
+ AC_VERB_GET_PIN_WIDGET_CONTROL,
+ 0);
+ if (i == AUTO_PIN_FRONT_MIC
+ && spec->hp_independent_mode
+ && spec->codec_type != VT1718S)
+ continue; /* ignore FMic for independent HP */
+ if (ctl & AC_PINCTL_IN_EN
+ && !(ctl & AC_PINCTL_OUT_EN))
+ on = 0;
+ }
+ }
+ *ucontrol->value.integer.value = on;
+ return 0;
+}
+
+static int via_smart51_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
+ struct via_spec *spec = codec->spec;
+ int out_in = *ucontrol->value.integer.value
+ ? AC_PINCTL_OUT_EN : AC_PINCTL_IN_EN;
+ int index[] = { AUTO_PIN_MIC, AUTO_PIN_FRONT_MIC, AUTO_PIN_LINE };
+ int i;
+
+ for (i = 0; i < ARRAY_SIZE(index); i++) {
+ hda_nid_t nid = spec->autocfg.input_pins[index[i]];
+ if (i == AUTO_PIN_FRONT_MIC
+ && spec->hp_independent_mode
+ && spec->codec_type != VT1718S)
+ continue; /* don't retask FMic for independent HP */
+ if (nid) {
+ unsigned int parm = snd_hda_codec_read(
+ codec, nid, 0,
+ AC_VERB_GET_PIN_WIDGET_CONTROL, 0);
+ parm &= ~(AC_PINCTL_IN_EN | AC_PINCTL_OUT_EN);
+ parm |= out_in;
+ snd_hda_codec_write(codec, nid, 0,
+ AC_VERB_SET_PIN_WIDGET_CONTROL,
+ parm);
+ if (out_in == AC_PINCTL_OUT_EN) {
+ mute_aa_path(codec, 1);
+ notify_aa_path_ctls(codec);
+ }
+ if (spec->codec_type == VT1718S)
+ snd_hda_codec_amp_stereo(
+ codec, nid, HDA_OUTPUT, 0, HDA_AMP_MUTE,
+ HDA_AMP_UNMUTE);
+ }
+ if (i == AUTO_PIN_FRONT_MIC) {
+ if (spec->codec_type == VT1708S
+ || spec->codec_type == VT1716S) {
+ /* input = index 1 (AOW3) */
+ snd_hda_codec_write(
+ codec, nid, 0,
+ AC_VERB_SET_CONNECT_SEL, 1);
+ snd_hda_codec_amp_stereo(
+ codec, nid, HDA_OUTPUT,
+ 0, HDA_AMP_MUTE, HDA_AMP_UNMUTE);
+ }
+ }
+ }
+ spec->smart51_enabled = *ucontrol->value.integer.value;
+ set_jack_power_state(codec);
+ return 1;
+}
+
+static struct snd_kcontrol_new via_smart51_mixer[] = {
+ {
+ .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+ .name = "Smart 5.1",
+ .count = 1,
+ .info = via_smart51_info,
+ .get = via_smart51_get,
+ .put = via_smart51_put,
+ },
+ {} /* end */
+};
+
/* capture mixer elements */
static struct snd_kcontrol_new vt1708_capture_mixer[] = {
HDA_CODEC_VOLUME("Capture Volume", 0x15, 0x0, HDA_INPUT),
@@ -506,6 +1408,112 @@ static struct snd_kcontrol_new vt1708_capture_mixer[] = {
},
{ } /* end */
};
+
+/* check AA path's mute statue */
+static int is_aa_path_mute(struct hda_codec *codec)
+{
+ int mute = 1;
+ hda_nid_t nid_mixer;
+ int start_idx;
+ int end_idx;
+ int i;
+ struct via_spec *spec = codec->spec;
+ /* get nid of MW0 and start & end index */
+ switch (spec->codec_type) {
+ case VT1708B_8CH:
+ case VT1708B_4CH:
+ case VT1708S:
+ case VT1716S:
+ nid_mixer = 0x16;
+ start_idx = 2;
+ end_idx = 4;
+ break;
+ case VT1702:
+ nid_mixer = 0x1a;
+ start_idx = 1;
+ end_idx = 3;
+ break;
+ case VT1718S:
+ nid_mixer = 0x21;
+ start_idx = 1;
+ end_idx = 3;
+ break;
+ case VT2002P:
+ case VT1812:
+ nid_mixer = 0x21;
+ start_idx = 0;
+ end_idx = 2;
+ break;
+ default:
+ return 0;
+ }
+ /* check AA path's mute status */
+ for (i = start_idx; i <= end_idx; i++) {
+ unsigned int con_list = snd_hda_codec_read(
+ codec, nid_mixer, 0, AC_VERB_GET_CONNECT_LIST, i/4*4);
+ int shift = 8 * (i % 4);
+ hda_nid_t nid_pin = (con_list & (0xff << shift)) >> shift;
+ unsigned int defconf = snd_hda_codec_get_pincfg(codec, nid_pin);
+ if (get_defcfg_connect(defconf) == AC_JACK_PORT_COMPLEX) {
+ /* check mute status while the pin is connected */
+ int mute_l = snd_hda_codec_amp_read(codec, nid_mixer, 0,
+ HDA_INPUT, i) >> 7;
+ int mute_r = snd_hda_codec_amp_read(codec, nid_mixer, 1,
+ HDA_INPUT, i) >> 7;
+ if (!mute_l || !mute_r) {
+ mute = 0;
+ break;
+ }
+ }
+ }
+ return mute;
+}
+
+/* enter/exit analog low-current mode */
+static void analog_low_current_mode(struct hda_codec *codec, int stream_idle)
+{
+ struct via_spec *spec = codec->spec;
+ static int saved_stream_idle = 1; /* saved stream idle status */
+ int enable = is_aa_path_mute(codec);
+ unsigned int verb = 0;
+ unsigned int parm = 0;
+
+ if (stream_idle == -1) /* stream status did not change */
+ enable = enable && saved_stream_idle;
+ else {
+ enable = enable && stream_idle;
+ saved_stream_idle = stream_idle;
+ }
+
+ /* decide low current mode's verb & parameter */
+ switch (spec->codec_type) {
+ case VT1708B_8CH:
+ case VT1708B_4CH:
+ verb = 0xf70;
+ parm = enable ? 0x02 : 0x00; /* 0x02: 2/3x, 0x00: 1x */
+ break;
+ case VT1708S:
+ case VT1718S:
+ case VT1716S:
+ verb = 0xf73;
+ parm = enable ? 0x51 : 0xe1; /* 0x51: 4/28x, 0xe1: 1x */
+ break;
+ case VT1702:
+ verb = 0xf73;
+ parm = enable ? 0x01 : 0x1d; /* 0x01: 4/40x, 0x1d: 1x */
+ break;
+ case VT2002P:
+ case VT1812:
+ verb = 0xf93;
+ parm = enable ? 0x00 : 0xe0; /* 0x00: 4/40x, 0xe0: 1x */
+ break;
+ default:
+ return; /* other codecs are not supported */
+ }
+ /* send verb */
+ snd_hda_codec_write(codec, codec->afg, 0, verb, parm);
+}
+
/*
* generic initialization of ADC, input mixers and output mixers
*/
@@ -534,9 +1542,9 @@ static struct hda_verb vt1708_volume_init_verbs[] = {
{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},
+
+ /* Setup default input MW0 to PW4 */
+ {0x20, AC_VERB_SET_CONNECT_SEL, 0},
/* PW9 Output enable */
{0x25, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x40},
{ }
@@ -547,30 +1555,13 @@ static int via_playback_pcm_open(struct hda_pcm_stream *hinfo,
struct snd_pcm_substream *substream)
{
struct via_spec *spec = codec->spec;
+ int idle = substream->pstr->substream_opened == 1
+ && substream->ref_count == 0;
+ analog_low_current_mode(codec, idle);
return snd_hda_multi_out_analog_open(codec, &spec->multiout, substream,
hinfo);
}
-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);
-}
-
-
static void playback_multi_pcm_prep_0(struct hda_codec *codec,
unsigned int stream_tag,
unsigned int format,
@@ -615,8 +1606,8 @@ static void playback_multi_pcm_prep_0(struct hda_codec *codec,
snd_hda_codec_setup_stream(codec, nids[HDA_FRONT], stream_tag,
0, format);
- if (mout->hp_nid && mout->hp_nid != nids[HDA_FRONT] &&
- !spec->hp_independent_mode)
+ if (mout->hp_nid && mout->hp_nid != nids[HDA_FRONT]
+ && !spec->hp_independent_mode)
/* headphone out will just decode front left/right (stereo) */
snd_hda_codec_setup_stream(codec, mout->hp_nid, stream_tag,
0, format);
@@ -658,7 +1649,7 @@ static int via_playback_multi_pcm_prepare(struct hda_pcm_stream *hinfo,
snd_hda_codec_setup_stream(codec, mout->hp_nid,
stream_tag, 0, format);
}
-
+ vt1708_start_hp_work(spec);
return 0;
}
@@ -698,7 +1689,7 @@ static int via_playback_multi_pcm_cleanup(struct hda_pcm_stream *hinfo,
snd_hda_codec_setup_stream(codec, mout->hp_nid,
0, 0, 0);
}
-
+ vt1708_stop_hp_work(spec);
return 0;
}
@@ -779,7 +1770,7 @@ static struct hda_pcm_stream vt1708_pcm_analog_playback = {
};
static struct hda_pcm_stream vt1708_pcm_analog_s16_playback = {
- .substreams = 1,
+ .substreams = 2,
.channels_min = 2,
.channels_max = 8,
.nid = 0x10, /* NID to query formats and rates */
@@ -790,8 +1781,8 @@ static struct hda_pcm_stream vt1708_pcm_analog_s16_playback = {
.formats = SNDRV_PCM_FMTBIT_S16_LE,
.ops = {
.open = via_playback_pcm_open,
- .prepare = via_playback_pcm_prepare,
- .cleanup = via_playback_pcm_cleanup
+ .prepare = via_playback_multi_pcm_prepare,
+ .cleanup = via_playback_multi_pcm_cleanup
},
};
@@ -853,6 +1844,11 @@ static int via_build_controls(struct hda_codec *codec)
if (err < 0)
return err;
}
+
+ /* init power states */
+ set_jack_power_state(codec);
+ analog_low_current_mode(codec, 1);
+
via_free_kctls(codec); /* no longer needed */
return 0;
}
@@ -866,8 +1862,10 @@ static int via_build_pcms(struct hda_codec *codec)
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_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];
@@ -904,20 +1902,58 @@ static void via_free(struct hda_codec *codec)
return;
via_free_kctls(codec);
+ vt1708_stop_hp_work(spec);
kfree(codec->spec);
}
/* mute internal speaker if HP is plugged */
static void via_hp_automute(struct hda_codec *codec)
{
- unsigned int present;
+ unsigned int present = 0;
struct via_spec *spec = codec->spec;
- present = snd_hda_codec_read(codec, spec->autocfg.hp_pins[0], 0,
- AC_VERB_GET_PIN_SENSE, 0) & 0x80000000;
- snd_hda_codec_amp_stereo(codec, spec->autocfg.line_out_pins[0],
- HDA_OUTPUT, 0, HDA_AMP_MUTE,
- present ? HDA_AMP_MUTE : 0);
+ present = snd_hda_jack_detect(codec, spec->autocfg.hp_pins[0]);
+
+ if (!spec->hp_independent_mode) {
+ struct snd_ctl_elem_id id;
+ /* auto mute */
+ snd_hda_codec_amp_stereo(
+ codec, spec->autocfg.line_out_pins[0], HDA_OUTPUT, 0,
+ HDA_AMP_MUTE, present ? HDA_AMP_MUTE : 0);
+ /* notify change */
+ memset(&id, 0, sizeof(id));
+ id.iface = SNDRV_CTL_ELEM_IFACE_MIXER;
+ strcpy(id.name, "Front Playback Switch");
+ snd_ctl_notify(codec->bus->card, SNDRV_CTL_EVENT_MASK_VALUE,
+ &id);
+ }
+}
+
+/* mute mono out if HP or Line out is plugged */
+static void via_mono_automute(struct hda_codec *codec)
+{
+ unsigned int hp_present, lineout_present;
+ struct via_spec *spec = codec->spec;
+
+ if (spec->codec_type != VT1716S)
+ return;
+
+ lineout_present = snd_hda_jack_detect(codec,
+ spec->autocfg.line_out_pins[0]);
+
+ /* Mute Mono Out if Line Out is plugged */
+ if (lineout_present) {
+ snd_hda_codec_amp_stereo(
+ codec, 0x2A, HDA_OUTPUT, 0, HDA_AMP_MUTE, HDA_AMP_MUTE);
+ return;
+ }
+
+ hp_present = snd_hda_jack_detect(codec, spec->autocfg.hp_pins[0]);
+
+ if (!spec->hp_independent_mode)
+ snd_hda_codec_amp_stereo(
+ codec, 0x2A, HDA_OUTPUT, 0, HDA_AMP_MUTE,
+ hp_present ? HDA_AMP_MUTE : 0);
}
static void via_gpio_control(struct hda_codec *codec)
@@ -968,15 +2004,83 @@ static void via_gpio_control(struct hda_codec *codec)
}
}
+/* mute Internal-Speaker if HP is plugged */
+static void via_speaker_automute(struct hda_codec *codec)
+{
+ unsigned int hp_present;
+ struct via_spec *spec = codec->spec;
+
+ if (spec->codec_type != VT2002P && spec->codec_type != VT1812)
+ return;
+
+ hp_present = snd_hda_jack_detect(codec, spec->autocfg.hp_pins[0]);
+
+ if (!spec->hp_independent_mode) {
+ struct snd_ctl_elem_id id;
+ snd_hda_codec_amp_stereo(
+ codec, spec->autocfg.speaker_pins[0], HDA_OUTPUT, 0,
+ HDA_AMP_MUTE, hp_present ? HDA_AMP_MUTE : 0);
+ /* notify change */
+ memset(&id, 0, sizeof(id));
+ id.iface = SNDRV_CTL_ELEM_IFACE_MIXER;
+ strcpy(id.name, "Speaker Playback Switch");
+ snd_ctl_notify(codec->bus->card, SNDRV_CTL_EVENT_MASK_VALUE,
+ &id);
+ }
+}
+
+/* mute line-out and internal speaker if HP is plugged */
+static void via_hp_bind_automute(struct hda_codec *codec)
+{
+ /* use long instead of int below just to avoid an internal compiler
+ * error with gcc 4.0.x
+ */
+ unsigned long hp_present, present = 0;
+ struct via_spec *spec = codec->spec;
+ int i;
+
+ if (!spec->autocfg.hp_pins[0] || !spec->autocfg.line_out_pins[0])
+ return;
+
+ hp_present = snd_hda_jack_detect(codec, spec->autocfg.hp_pins[0]);
+
+ present = snd_hda_jack_detect(codec, spec->autocfg.line_out_pins[0]);
+
+ if (!spec->hp_independent_mode) {
+ /* Mute Line-Outs */
+ for (i = 0; i < spec->autocfg.line_outs; i++)
+ snd_hda_codec_amp_stereo(
+ codec, spec->autocfg.line_out_pins[i],
+ HDA_OUTPUT, 0,
+ HDA_AMP_MUTE, hp_present ? HDA_AMP_MUTE : 0);
+ if (hp_present)
+ present = hp_present;
+ }
+ /* Speakers */
+ for (i = 0; i < spec->autocfg.speaker_outs; i++)
+ snd_hda_codec_amp_stereo(
+ codec, spec->autocfg.speaker_pins[i], HDA_OUTPUT, 0,
+ HDA_AMP_MUTE, present ? HDA_AMP_MUTE : 0);
+}
+
+
/* unsolicited event for jack sensing */
static void via_unsol_event(struct hda_codec *codec,
unsigned int res)
{
res >>= 26;
- if (res == VIA_HP_EVENT)
+ if (res & VIA_HP_EVENT)
via_hp_automute(codec);
- else if (res == VIA_GPIO_EVENT)
+ if (res & VIA_GPIO_EVENT)
via_gpio_control(codec);
+ if (res & VIA_JACK_EVENT)
+ set_jack_power_state(codec);
+ if (res & VIA_MONO_EVENT)
+ via_mono_automute(codec);
+ if (res & VIA_SPEAKER_EVENT)
+ via_speaker_automute(codec);
+ if (res & VIA_BIND_HP_EVENT)
+ via_hp_bind_automute(codec);
}
static int via_init(struct hda_codec *codec)
@@ -986,6 +2090,10 @@ static int via_init(struct hda_codec *codec)
for (i = 0; i < spec->num_iverbs; i++)
snd_hda_sequence_write(codec, spec->init_verbs[i]);
+ spec->codec_type = get_codec_type(codec);
+ if (spec->codec_type == VT1708BCE)
+ spec->codec_type = VT1708S; /* VT1708BCE & VT1708S are almost
+ same */
/* Lydia Add for EAPD enable */
if (!spec->dig_in_nid) { /* No Digital In connection */
if (spec->dig_in_pin) {
@@ -1003,8 +2111,17 @@ static int via_init(struct hda_codec *codec)
if (spec->slave_dig_outs[0])
codec->slave_dig_outs = spec->slave_dig_outs;
- return 0;
+ return 0;
+}
+
+#ifdef SND_HDA_NEEDS_RESUME
+static int via_suspend(struct hda_codec *codec, pm_message_t state)
+{
+ struct via_spec *spec = codec->spec;
+ vt1708_stop_hp_work(spec);
+ return 0;
}
+#endif
#ifdef CONFIG_SND_HDA_POWER_SAVE
static int via_check_power_status(struct hda_codec *codec, hda_nid_t nid)
@@ -1021,6 +2138,9 @@ static struct hda_codec_ops via_patch_ops = {
.build_pcms = via_build_pcms,
.init = via_init,
.free = via_free,
+#ifdef SND_HDA_NEEDS_RESUME
+ .suspend = via_suspend,
+#endif
#ifdef CONFIG_SND_HDA_POWER_SAVE
.check_power_status = via_check_power_status,
#endif
@@ -1036,8 +2156,8 @@ static int vt1708_auto_fill_dac_nids(struct via_spec *spec,
spec->multiout.num_dacs = cfg->line_outs;
spec->multiout.dac_nids = spec->private_dac_nids;
-
- for(i = 0; i < 4; i++) {
+
+ for (i = 0; i < 4; i++) {
nid = cfg->line_out_pins[i];
if (nid) {
/* config dac list */
@@ -1067,7 +2187,7 @@ static int vt1708_auto_create_multi_out_ctls(struct via_spec *spec,
{
char name[32];
static const char *chname[4] = { "Front", "Surround", "C/LFE", "Side" };
- hda_nid_t nid, nid_vol = 0;
+ hda_nid_t nid, nid_vol, nid_vols[] = {0x17, 0x19, 0x1a, 0x1b};
int i, err;
for (i = 0; i <= AUTO_SEQ_SIDE; i++) {
@@ -1075,9 +2195,8 @@ static int vt1708_auto_create_multi_out_ctls(struct via_spec *spec,
if (!nid)
continue;
-
- if (i != AUTO_SEQ_FRONT)
- nid_vol = 0x18 + i;
+
+ nid_vol = nid_vols[i];
if (i == AUTO_SEQ_CENLFE) {
/* Center/LFE */
@@ -1105,21 +2224,21 @@ static int vt1708_auto_create_multi_out_ctls(struct via_spec *spec,
HDA_OUTPUT));
if (err < 0)
return err;
- } else if (i == AUTO_SEQ_FRONT){
+ } 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_COMPOSE_AMP_VAL(nid_vol, 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_COMPOSE_AMP_VAL(nid_vol, 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,
@@ -1178,6 +2297,7 @@ static int vt1708_auto_create_hp_ctls(struct via_spec *spec, hda_nid_t pin)
return 0;
spec->multiout.hp_nid = VT1708_HP_NID; /* AOW3 */
+ spec->hp_independent_mode_index = 1;
err = via_add_control(spec, VIA_CTL_WIDGET_VOL,
"Headphone Playback Volume",
@@ -1218,7 +2338,7 @@ static int vt1708_auto_create_analog_input_ctls(struct via_spec *spec,
case 0x1d: /* Mic */
idx = 2;
break;
-
+
case 0x1e: /* Line In */
idx = 3;
break;
@@ -1231,8 +2351,7 @@ static int vt1708_auto_create_analog_input_ctls(struct via_spec *spec,
idx = 1;
break;
}
- err = via_new_analog_input(spec, cfg->input_pins[i], labels[i],
- idx, 0x17);
+ err = via_new_analog_input(spec, labels[i], idx, 0x17);
if (err < 0)
return err;
imux->items[imux->num_items].label = labels[i];
@@ -1260,16 +2379,60 @@ static void vt1708_set_pinconfig_connect(struct hda_codec *codec, hda_nid_t nid)
def_conf = snd_hda_codec_get_pincfg(codec, nid);
seqassoc = (unsigned char) get_defcfg_association(def_conf);
seqassoc = (seqassoc << 4) | get_defcfg_sequence(def_conf);
- if (get_defcfg_connect(def_conf) == AC_JACK_PORT_NONE) {
- if (seqassoc == 0xff) {
- def_conf = def_conf & (~(AC_JACK_PORT_BOTH << 30));
- snd_hda_codec_set_pincfg(codec, nid, def_conf);
- }
+ if (get_defcfg_connect(def_conf) == AC_JACK_PORT_NONE
+ && (seqassoc == 0xf0 || seqassoc == 0xff)) {
+ def_conf = def_conf & (~(AC_JACK_PORT_BOTH << 30));
+ snd_hda_codec_set_pincfg(codec, nid, def_conf);
}
return;
}
+static int vt1708_jack_detectect_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;
+
+ if (spec->codec_type != VT1708)
+ return 0;
+ spec->vt1708_jack_detectect =
+ !((snd_hda_codec_read(codec, 0x1, 0, 0xf84, 0) >> 8) & 0x1);
+ ucontrol->value.integer.value[0] = spec->vt1708_jack_detectect;
+ return 0;
+}
+
+static int vt1708_jack_detectect_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
+ struct via_spec *spec = codec->spec;
+ int change;
+
+ if (spec->codec_type != VT1708)
+ return 0;
+ spec->vt1708_jack_detectect = ucontrol->value.integer.value[0];
+ change = (0x1 & (snd_hda_codec_read(codec, 0x1, 0, 0xf84, 0) >> 8))
+ == !spec->vt1708_jack_detectect;
+ if (spec->vt1708_jack_detectect) {
+ mute_aa_path(codec, 1);
+ notify_aa_path_ctls(codec);
+ }
+ return change;
+}
+
+static struct snd_kcontrol_new vt1708_jack_detectect[] = {
+ {
+ .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+ .name = "Jack Detect",
+ .count = 1,
+ .info = snd_ctl_boolean_mono_info,
+ .get = vt1708_jack_detectect_get,
+ .put = vt1708_jack_detectect_put,
+ },
+ {} /* end */
+};
+
static int vt1708_parse_auto_config(struct hda_codec *codec)
{
struct via_spec *spec = codec->spec;
@@ -1297,6 +2460,10 @@ static int vt1708_parse_auto_config(struct hda_codec *codec)
err = vt1708_auto_create_analog_input_ctls(spec, &spec->autocfg);
if (err < 0)
return err;
+ /* add jack detect on/off control */
+ err = snd_hda_add_new_ctls(codec, vt1708_jack_detectect);
+ if (err < 0)
+ return err;
spec->multiout.max_channels = spec->multiout.num_dacs * 2;
@@ -1316,19 +2483,44 @@ static int vt1708_parse_auto_config(struct hda_codec *codec)
if (spec->hp_mux)
spec->mixers[spec->num_mixers++] = via_hp_mixer;
+ spec->mixers[spec->num_mixers++] = via_smart51_mixer;
return 1;
}
/* init callback for auto-configuration model -- overriding the default init */
static int via_auto_init(struct hda_codec *codec)
{
+ struct via_spec *spec = codec->spec;
+
via_init(codec);
via_auto_init_multi_out(codec);
via_auto_init_hp_out(codec);
via_auto_init_analog_input(codec);
+ if (spec->codec_type == VT2002P || spec->codec_type == VT1812) {
+ via_hp_bind_automute(codec);
+ } else {
+ via_hp_automute(codec);
+ via_speaker_automute(codec);
+ }
+
return 0;
}
+static void vt1708_update_hp_jack_state(struct work_struct *work)
+{
+ struct via_spec *spec = container_of(work, struct via_spec,
+ vt1708_hp_work.work);
+ if (spec->codec_type != VT1708)
+ return;
+ /* if jack state toggled */
+ if (spec->vt1708_hp_present
+ != snd_hda_jack_detect(spec->codec, spec->autocfg.hp_pins[0])) {
+ spec->vt1708_hp_present ^= 1;
+ via_hp_automute(spec->codec);
+ }
+ vt1708_start_hp_work(spec);
+}
+
static int get_mux_nids(struct hda_codec *codec)
{
struct via_spec *spec = codec->spec;
@@ -1378,7 +2570,7 @@ static int patch_vt1708(struct hda_codec *codec)
"from BIOS. Using genenic mode...\n");
}
-
+
spec->stream_name_analog = "VT1708 Analog";
spec->stream_analog_playback = &vt1708_pcm_analog_playback;
/* disable 32bit format on VT1708 */
@@ -1390,7 +2582,7 @@ static int patch_vt1708(struct hda_codec *codec)
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);
@@ -1405,7 +2597,8 @@ static int patch_vt1708(struct hda_codec *codec)
#ifdef CONFIG_SND_HDA_POWER_SAVE
spec->loopback.amplist = vt1708_loopbacks;
#endif
-
+ spec->codec = codec;
+ INIT_DELAYED_WORK(&spec->vt1708_hp_work, vt1708_update_hp_jack_state);
return 0;
}
@@ -1433,7 +2626,8 @@ static struct snd_kcontrol_new vt1709_capture_mixer[] = {
};
static struct hda_verb vt1709_uniwill_init_verbs[] = {
- {0x20, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_HP_EVENT},
+ {0x20, AC_VERB_SET_UNSOLICITED_ENABLE,
+ AC_USRSP_EN | VIA_HP_EVENT | VIA_JACK_EVENT},
{ }
};
@@ -1473,8 +2667,8 @@ static struct hda_verb vt1709_10ch_volume_init_verbs[] = {
{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 input of PW4 as MW0 */
+ {0x20, AC_VERB_SET_CONNECT_SEL, 0},
/* PW9 Output enable */
{0x24, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x40},
{ }
@@ -1487,8 +2681,8 @@ static struct hda_pcm_stream vt1709_10ch_pcm_analog_playback = {
.nid = 0x10, /* NID to query formats and rates */
.ops = {
.open = via_playback_pcm_open,
- .prepare = via_playback_pcm_prepare,
- .cleanup = via_playback_pcm_cleanup
+ .prepare = via_playback_multi_pcm_prepare,
+ .cleanup = via_playback_multi_pcm_cleanup,
},
};
@@ -1499,8 +2693,8 @@ static struct hda_pcm_stream vt1709_6ch_pcm_analog_playback = {
.nid = 0x10, /* NID to query formats and rates */
.ops = {
.open = via_playback_pcm_open,
- .prepare = via_playback_pcm_prepare,
- .cleanup = via_playback_pcm_cleanup
+ .prepare = via_playback_multi_pcm_prepare,
+ .cleanup = via_playback_multi_pcm_cleanup,
},
};
@@ -1575,11 +2769,11 @@ static int vt1709_auto_fill_dac_nids(struct via_spec *spec,
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++) {
+ for (i = 0; i < cfg->line_outs; i++) {
nid = cfg->line_out_pins[i];
if (nid) {
/* config dac list */
- switch(i) {
+ switch (i) {
case AUTO_SEQ_FRONT:
/* AOW0 */
spec->multiout.dac_nids[i] = 0x10;
@@ -1608,56 +2802,58 @@ static int vt1709_auto_create_multi_out_ctls(struct via_spec *spec,
{
char name[32];
static const char *chname[4] = { "Front", "Surround", "C/LFE", "Side" };
- hda_nid_t nid = 0;
+ hda_nid_t nid, nid_vol, nid_vols[] = {0x18, 0x1a, 0x1b, 0x29};
int i, err;
for (i = 0; i <= AUTO_SEQ_SIDE; i++) {
nid = cfg->line_out_pins[i];
- if (!nid)
+ if (!nid)
continue;
+ nid_vol = nid_vols[i];
+
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_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(0x1b, 2, 0,
+ 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(0x1b, 1, 0,
+ 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(0x1b, 2, 0,
+ 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 */
+ } 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_COMPOSE_AMP_VAL(nid_vol, 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_COMPOSE_AMP_VAL(nid_vol, 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,
@@ -1674,26 +2870,26 @@ static int vt1709_auto_create_multi_out_ctls(struct via_spec *spec,
} 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(0x1a, 3, 0,
+ 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(0x1a, 3, 0,
+ HDA_COMPOSE_AMP_VAL(nid_vol, 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(0x29, 3, 0,
+ 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(0x29, 3, 0,
+ HDA_COMPOSE_AMP_VAL(nid_vol, 3, 0,
HDA_OUTPUT));
if (err < 0)
return err;
@@ -1714,6 +2910,7 @@ static int vt1709_auto_create_hp_ctls(struct via_spec *spec, hda_nid_t pin)
spec->multiout.hp_nid = VT1709_HP_DAC_NID;
else if (spec->multiout.num_dacs == 3) /* 6 channels */
spec->multiout.hp_nid = 0;
+ spec->hp_independent_mode_index = 1;
err = via_add_control(spec, VIA_CTL_WIDGET_VOL,
"Headphone Playback Volume",
@@ -1752,7 +2949,7 @@ static int vt1709_auto_create_analog_input_ctls(struct via_spec *spec,
case 0x1d: /* Mic */
idx = 2;
break;
-
+
case 0x1e: /* Line In */
idx = 3;
break;
@@ -1765,8 +2962,7 @@ static int vt1709_auto_create_analog_input_ctls(struct via_spec *spec,
idx = 1;
break;
}
- err = via_new_analog_input(spec, cfg->input_pins[i], labels[i],
- idx, 0x18);
+ err = via_new_analog_input(spec, labels[i], idx, 0x18);
if (err < 0)
return err;
imux->items[imux->num_items].label = labels[i];
@@ -1816,6 +3012,7 @@ static int vt1709_parse_auto_config(struct hda_codec *codec)
if (spec->hp_mux)
spec->mixers[spec->num_mixers++] = via_hp_mixer;
+ spec->mixers[spec->num_mixers++] = via_smart51_mixer;
return 1;
}
@@ -1861,7 +3058,7 @@ static int patch_vt1709_10ch(struct hda_codec *codec)
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);
@@ -1955,7 +3152,7 @@ static int patch_vt1709_6ch(struct hda_codec *codec)
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);
@@ -2024,7 +3221,7 @@ static struct hda_verb vt1708B_8ch_volume_init_verbs[] = {
{0x27, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
/* Setup default input to PW4 */
- {0x1d, AC_VERB_SET_CONNECT_SEL, 0x1},
+ {0x1d, AC_VERB_SET_CONNECT_SEL, 0},
/* PW9 Output enable */
{0x20, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x40},
/* PW10 Input enable */
@@ -2068,10 +3265,29 @@ static struct hda_verb vt1708B_4ch_volume_init_verbs[] = {
};
static struct hda_verb vt1708B_uniwill_init_verbs[] = {
- {0x1D, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_HP_EVENT},
+ {0x1d, AC_VERB_SET_UNSOLICITED_ENABLE,
+ AC_USRSP_EN | VIA_HP_EVENT | VIA_JACK_EVENT},
+ {0x19, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT},
+ {0x1a, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT},
+ {0x1b, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT},
+ {0x1c, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT},
+ {0x1e, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT},
+ {0x22, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT},
+ {0x23, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT},
{ }
};
+static int via_pcm_open_close(struct hda_pcm_stream *hinfo,
+ struct hda_codec *codec,
+ struct snd_pcm_substream *substream)
+{
+ int idle = substream->pstr->substream_opened == 1
+ && substream->ref_count == 0;
+
+ analog_low_current_mode(codec, idle);
+ return 0;
+}
+
static struct hda_pcm_stream vt1708B_8ch_pcm_analog_playback = {
.substreams = 2,
.channels_min = 2,
@@ -2080,7 +3296,8 @@ static struct hda_pcm_stream vt1708B_8ch_pcm_analog_playback = {
.ops = {
.open = via_playback_pcm_open,
.prepare = via_playback_multi_pcm_prepare,
- .cleanup = via_playback_multi_pcm_cleanup
+ .cleanup = via_playback_multi_pcm_cleanup,
+ .close = via_pcm_open_close
},
};
@@ -2102,8 +3319,10 @@ static struct hda_pcm_stream vt1708B_pcm_analog_capture = {
.channels_max = 2,
.nid = 0x13, /* NID to query formats and rates */
.ops = {
+ .open = via_pcm_open_close,
.prepare = via_capture_pcm_prepare,
- .cleanup = via_capture_pcm_cleanup
+ .cleanup = via_capture_pcm_cleanup,
+ .close = via_pcm_open_close
},
};
@@ -2260,6 +3479,7 @@ static int vt1708B_auto_create_hp_ctls(struct via_spec *spec, hda_nid_t pin)
return 0;
spec->multiout.hp_nid = VT1708B_HP_NID; /* AOW3 */
+ spec->hp_independent_mode_index = 1;
err = via_add_control(spec, VIA_CTL_WIDGET_VOL,
"Headphone Playback Volume",
@@ -2313,8 +3533,7 @@ static int vt1708B_auto_create_analog_input_ctls(struct via_spec *spec,
idx = 1;
break;
}
- err = via_new_analog_input(spec, cfg->input_pins[i], labels[i],
- idx, 0x16);
+ err = via_new_analog_input(spec, labels[i], idx, 0x16);
if (err < 0)
return err;
imux->items[imux->num_items].label = labels[i];
@@ -2364,6 +3583,7 @@ static int vt1708B_parse_auto_config(struct hda_codec *codec)
if (spec->hp_mux)
spec->mixers[spec->num_mixers++] = via_hp_mixer;
+ spec->mixers[spec->num_mixers++] = via_smart51_mixer;
return 1;
}
@@ -2376,12 +3596,14 @@ static struct hda_amp_list vt1708B_loopbacks[] = {
{ } /* end */
};
#endif
-
+static int patch_vt1708S(struct hda_codec *codec);
static int patch_vt1708B_8ch(struct hda_codec *codec)
{
struct via_spec *spec;
int err;
+ if (get_codec_type(codec) == VT1708BCE)
+ return patch_vt1708S(codec);
/* create a codec specific record */
spec = kzalloc(sizeof(*spec), GFP_KERNEL);
if (spec == NULL)
@@ -2483,29 +3705,15 @@ static int patch_vt1708B_4ch(struct hda_codec *codec)
/* Patch for VT1708S */
-/* VT1708S software backdoor based override for buggy hardware micboost
- * setting */
-#define MIC_BOOST_VOLUME(xname, nid) { \
- .iface = SNDRV_CTL_ELEM_IFACE_MIXER, \
- .name = xname, \
- .index = 0, \
- .access = SNDRV_CTL_ELEM_ACCESS_READWRITE | \
- SNDRV_CTL_ELEM_ACCESS_TLV_READ | \
- SNDRV_CTL_ELEM_ACCESS_TLV_CALLBACK, \
- .info = mic_boost_volume_info, \
- .get = snd_hda_mixer_amp_volume_get, \
- .put = snd_hda_mixer_amp_volume_put, \
- .tlv = { .c = mic_boost_tlv }, \
- .private_value = HDA_COMPOSE_AMP_VAL(nid, 3, 0, HDA_INPUT) }
-
/* capture mixer elements */
static struct snd_kcontrol_new vt1708S_capture_mixer[] = {
HDA_CODEC_VOLUME("Capture Volume", 0x13, 0x0, HDA_INPUT),
HDA_CODEC_MUTE("Capture Switch", 0x13, 0x0, HDA_INPUT),
HDA_CODEC_VOLUME_IDX("Capture Volume", 1, 0x14, 0x0, HDA_INPUT),
HDA_CODEC_MUTE_IDX("Capture Switch", 1, 0x14, 0x0, HDA_INPUT),
- MIC_BOOST_VOLUME("Mic Boost Capture Volume", 0x1A),
- MIC_BOOST_VOLUME("Front Mic Boost Capture Volume", 0x1E),
+ HDA_CODEC_VOLUME("Mic Boost Capture Volume", 0x1A, 0x0, HDA_INPUT),
+ HDA_CODEC_VOLUME("Front Mic Boost Capture Volume", 0x1E, 0x0,
+ HDA_INPUT),
{
.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
/* The multiple "Capture Source" controls confuse alsamixer
@@ -2542,11 +3750,21 @@ static struct hda_verb vt1708S_volume_init_verbs[] = {
{0x21, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x40},
/* Enable Mic Boost Volume backdoor */
{0x1, 0xf98, 0x1},
+ /* don't bybass mixer */
+ {0x1, 0xf88, 0xc0},
{ }
};
static struct hda_verb vt1708S_uniwill_init_verbs[] = {
- {0x1D, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_HP_EVENT},
+ {0x1d, AC_VERB_SET_UNSOLICITED_ENABLE,
+ AC_USRSP_EN | VIA_HP_EVENT | VIA_JACK_EVENT},
+ {0x19, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT},
+ {0x1a, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT},
+ {0x1b, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT},
+ {0x1c, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT},
+ {0x1e, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT},
+ {0x22, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT},
+ {0x23, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT},
{ }
};
@@ -2557,8 +3775,9 @@ static struct hda_pcm_stream vt1708S_pcm_analog_playback = {
.nid = 0x10, /* NID to query formats and rates */
.ops = {
.open = via_playback_pcm_open,
- .prepare = via_playback_pcm_prepare,
- .cleanup = via_playback_pcm_cleanup
+ .prepare = via_playback_multi_pcm_prepare,
+ .cleanup = via_playback_multi_pcm_cleanup,
+ .close = via_pcm_open_close
},
};
@@ -2568,8 +3787,10 @@ static struct hda_pcm_stream vt1708S_pcm_analog_capture = {
.channels_max = 2,
.nid = 0x13, /* NID to query formats and rates */
.ops = {
+ .open = via_pcm_open_close,
.prepare = via_capture_pcm_prepare,
- .cleanup = via_capture_pcm_cleanup
+ .cleanup = via_capture_pcm_cleanup,
+ .close = via_pcm_open_close
},
};
@@ -2726,6 +3947,7 @@ static int vt1708S_auto_create_hp_ctls(struct via_spec *spec, hda_nid_t pin)
return 0;
spec->multiout.hp_nid = VT1708S_HP_NID; /* AOW3 */
+ spec->hp_independent_mode_index = 1;
err = via_add_control(spec, VIA_CTL_WIDGET_VOL,
"Headphone Playback Volume",
@@ -2780,8 +4002,7 @@ static int vt1708S_auto_create_analog_input_ctls(struct via_spec *spec,
idx = 1;
break;
}
- err = via_new_analog_input(spec, cfg->input_pins[i], labels[i],
- idx, 0x16);
+ err = via_new_analog_input(spec, labels[i], idx, 0x16);
if (err < 0)
return err;
imux->items[imux->num_items].label = labels[i];
@@ -2852,6 +4073,7 @@ static int vt1708S_parse_auto_config(struct hda_codec *codec)
if (spec->hp_mux)
spec->mixers[spec->num_mixers++] = via_hp_mixer;
+ spec->mixers[spec->num_mixers++] = via_smart51_mixer;
return 1;
}
@@ -2865,6 +4087,16 @@ static struct hda_amp_list vt1708S_loopbacks[] = {
};
#endif
+static void override_mic_boost(struct hda_codec *codec, hda_nid_t pin,
+ int offset, int num_steps, int step_size)
+{
+ snd_hda_override_amp_caps(codec, pin, HDA_INPUT,
+ (offset << AC_AMPCAP_OFFSET_SHIFT) |
+ (num_steps << AC_AMPCAP_NUM_STEPS_SHIFT) |
+ (step_size << AC_AMPCAP_STEP_SIZE_SHIFT) |
+ (0 << AC_AMPCAP_MUTE_SHIFT));
+}
+
static int patch_vt1708S(struct hda_codec *codec)
{
struct via_spec *spec;
@@ -2890,17 +4122,25 @@ static int patch_vt1708S(struct hda_codec *codec)
spec->init_verbs[spec->num_iverbs++] = vt1708S_volume_init_verbs;
spec->init_verbs[spec->num_iverbs++] = vt1708S_uniwill_init_verbs;
- spec->stream_name_analog = "VT1708S Analog";
+ if (codec->vendor_id == 0x11060440)
+ spec->stream_name_analog = "VT1818S Analog";
+ else
+ spec->stream_name_analog = "VT1708S Analog";
spec->stream_analog_playback = &vt1708S_pcm_analog_playback;
spec->stream_analog_capture = &vt1708S_pcm_analog_capture;
- spec->stream_name_digital = "VT1708S Digital";
+ if (codec->vendor_id == 0x11060440)
+ spec->stream_name_digital = "VT1818S Digital";
+ else
+ spec->stream_name_digital = "VT1708S Digital";
spec->stream_digital_playback = &vt1708S_pcm_digital_playback;
if (!spec->adc_nids && spec->input_mux) {
spec->adc_nids = vt1708S_adc_nids;
spec->num_adc_nids = ARRAY_SIZE(vt1708S_adc_nids);
get_mux_nids(codec);
+ override_mic_boost(codec, 0x1a, 0, 3, 40);
+ override_mic_boost(codec, 0x1e, 0, 3, 40);
spec->mixers[spec->num_mixers] = vt1708S_capture_mixer;
spec->num_mixers++;
}
@@ -2913,6 +4153,16 @@ static int patch_vt1708S(struct hda_codec *codec)
spec->loopback.amplist = vt1708S_loopbacks;
#endif
+ /* correct names for VT1708BCE */
+ if (get_codec_type(codec) == VT1708BCE) {
+ kfree(codec->chip_name);
+ codec->chip_name = kstrdup("VT1708BCE", GFP_KERNEL);
+ snprintf(codec->bus->card->mixername,
+ sizeof(codec->bus->card->mixername),
+ "%s %s", codec->vendor_name, codec->chip_name);
+ spec->stream_name_analog = "VT1708BCE Analog";
+ spec->stream_name_digital = "VT1708BCE Digital";
+ }
return 0;
}
@@ -2967,12 +4217,20 @@ static struct hda_verb vt1702_volume_init_verbs[] = {
/* PW6 PW7 Output enable */
{0x19, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x40},
{0x1C, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x40},
+ /* mixer enable */
+ {0x1, 0xF88, 0x3},
+ /* GPIO 0~2 */
+ {0x1, 0xF82, 0x3F},
{ }
};
static struct hda_verb vt1702_uniwill_init_verbs[] = {
- {0x01, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_GPIO_EVENT},
- {0x17, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_HP_EVENT},
+ {0x17, AC_VERB_SET_UNSOLICITED_ENABLE,
+ AC_USRSP_EN | VIA_HP_EVENT | VIA_JACK_EVENT},
+ {0x14, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT},
+ {0x15, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT},
+ {0x16, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT},
+ {0x18, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT},
{ }
};
@@ -2984,7 +4242,8 @@ static struct hda_pcm_stream vt1702_pcm_analog_playback = {
.ops = {
.open = via_playback_pcm_open,
.prepare = via_playback_multi_pcm_prepare,
- .cleanup = via_playback_multi_pcm_cleanup
+ .cleanup = via_playback_multi_pcm_cleanup,
+ .close = via_pcm_open_close
},
};
@@ -2994,8 +4253,10 @@ static struct hda_pcm_stream vt1702_pcm_analog_capture = {
.channels_max = 2,
.nid = 0x12, /* NID to query formats and rates */
.ops = {
+ .open = via_pcm_open_close,
.prepare = via_capture_pcm_prepare,
- .cleanup = via_capture_pcm_cleanup
+ .cleanup = via_capture_pcm_cleanup,
+ .close = via_pcm_open_close
},
};
@@ -3065,12 +4326,13 @@ static int vt1702_auto_create_line_out_ctls(struct via_spec *spec,
static int vt1702_auto_create_hp_ctls(struct via_spec *spec, hda_nid_t pin)
{
- int err;
-
+ int err, i;
+ struct hda_input_mux *imux;
+ static const char *texts[] = { "ON", "OFF", NULL};
if (!pin)
return 0;
-
spec->multiout.hp_nid = 0x1D;
+ spec->hp_independent_mode_index = 0;
err = via_add_control(spec, VIA_CTL_WIDGET_VOL,
"Headphone Playback Volume",
@@ -3084,8 +4346,18 @@ static int vt1702_auto_create_hp_ctls(struct via_spec *spec, hda_nid_t pin)
if (err < 0)
return err;
- create_hp_imux(spec);
+ imux = &spec->private_imux[1];
+ /* for hp mode select */
+ i = 0;
+ while (texts[i] != NULL) {
+ imux->items[imux->num_items].label = texts[i];
+ imux->items[imux->num_items].index = i;
+ imux->num_items++;
+ i++;
+ }
+
+ spec->hp_mux = &spec->private_imux[1];
return 0;
}
@@ -3121,8 +4393,7 @@ static int vt1702_auto_create_analog_input_ctls(struct via_spec *spec,
idx = 3;
break;
}
- err = via_new_analog_input(spec, cfg->input_pins[i],
- labels[i], idx, 0x1A);
+ err = via_new_analog_input(spec, labels[i], idx, 0x1A);
if (err < 0)
return err;
imux->items[imux->num_items].label = labels[i];
@@ -3152,6 +4423,12 @@ static int vt1702_parse_auto_config(struct hda_codec *codec)
err = vt1702_auto_create_hp_ctls(spec, spec->autocfg.hp_pins[0]);
if (err < 0)
return err;
+ /* limit AA path volume to 0 dB */
+ snd_hda_override_amp_caps(codec, 0x1A, HDA_INPUT,
+ (0x17 << AC_AMPCAP_OFFSET_SHIFT) |
+ (0x17 << AC_AMPCAP_NUM_STEPS_SHIFT) |
+ (0x5 << AC_AMPCAP_STEP_SIZE_SHIFT) |
+ (1 << AC_AMPCAP_MUTE_SHIFT));
err = vt1702_auto_create_analog_input_ctls(spec, &spec->autocfg);
if (err < 0)
return err;
@@ -3185,8 +4462,6 @@ static int patch_vt1702(struct hda_codec *codec)
{
struct via_spec *spec;
int err;
- unsigned int response;
- unsigned char control;
/* create a codec specific record */
spec = kzalloc(sizeof(*spec), GFP_KERNEL);
@@ -3231,17 +4506,1638 @@ static int patch_vt1702(struct hda_codec *codec)
spec->loopback.amplist = vt1702_loopbacks;
#endif
- /* Open backdoor */
- response = snd_hda_codec_read(codec, codec->afg, 0, 0xF8C, 0);
- control = (unsigned char)(response & 0xff);
- control |= 0x3;
- snd_hda_codec_write(codec, codec->afg, 0, 0xF88, control);
+ return 0;
+}
+
+/* Patch for VT1718S */
+
+/* capture mixer elements */
+static struct snd_kcontrol_new vt1718S_capture_mixer[] = {
+ HDA_CODEC_VOLUME("Capture Volume", 0x10, 0x0, HDA_INPUT),
+ HDA_CODEC_MUTE("Capture Switch", 0x10, 0x0, HDA_INPUT),
+ HDA_CODEC_VOLUME_IDX("Capture Volume", 1, 0x11, 0x0, HDA_INPUT),
+ HDA_CODEC_MUTE_IDX("Capture Switch", 1, 0x11, 0x0, HDA_INPUT),
+ HDA_CODEC_VOLUME("Mic Boost Capture Volume", 0x2b, 0x0, HDA_INPUT),
+ HDA_CODEC_VOLUME("Front Mic Boost Capture Volume", 0x29, 0x0,
+ HDA_INPUT),
+ {
+ .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+ /* The multiple "Capture Source" controls confuse alsamixer
+ * So call somewhat different..
+ */
+ .name = "Input Source",
+ .count = 2,
+ .info = via_mux_enum_info,
+ .get = via_mux_enum_get,
+ .put = via_mux_enum_put,
+ },
+ { } /* end */
+};
+
+static struct hda_verb vt1718S_volume_init_verbs[] = {
+ /*
+ * Unmute ADC0-1 and set the default input to mic-in
+ */
+ {0x10, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
+ {0x11, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
+
+
+ /* Mute 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 */
+ {0x21, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
+ {0x21, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
+ {0x21, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(2)},
+ {0x21, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(3)},
+ {0x21, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(5)},
+
+ /* Setup default input of Front HP to MW9 */
+ {0x28, AC_VERB_SET_CONNECT_SEL, 0x1},
+ /* PW9 PW10 Output enable */
+ {0x2d, AC_VERB_SET_PIN_WIDGET_CONTROL, AC_PINCTL_OUT_EN},
+ {0x2e, AC_VERB_SET_PIN_WIDGET_CONTROL, AC_PINCTL_OUT_EN},
+ /* PW11 Input enable */
+ {0x2f, AC_VERB_SET_PIN_WIDGET_CONTROL, AC_PINCTL_IN_EN},
+ /* Enable Boost Volume backdoor */
+ {0x1, 0xf88, 0x8},
+ /* MW0/1/2/3/4: un-mute index 0 (AOWx), mute index 1 (MW9) */
+ {0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
+ {0x19, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
+ {0x1a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
+ {0x1b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
+ {0x1c, 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_MUTE(1)},
+ {0x1a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
+ {0x1b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
+ {0x1c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
+ /* set MUX1 = 2 (AOW4), MUX2 = 1 (AOW3) */
+ {0x34, AC_VERB_SET_CONNECT_SEL, 0x2},
+ {0x35, AC_VERB_SET_CONNECT_SEL, 0x1},
+ /* Unmute MW4's index 0 */
+ {0x1c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
+ { }
+};
+
+
+static struct hda_verb vt1718S_uniwill_init_verbs[] = {
+ {0x28, AC_VERB_SET_UNSOLICITED_ENABLE,
+ AC_USRSP_EN | VIA_HP_EVENT | VIA_JACK_EVENT},
+ {0x24, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT},
+ {0x25, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT},
+ {0x26, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT},
+ {0x27, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT},
+ {0x29, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT},
+ {0x2a, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT},
+ {0x2b, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT},
+ { }
+};
+
+static struct hda_pcm_stream vt1718S_pcm_analog_playback = {
+ .substreams = 2,
+ .channels_min = 2,
+ .channels_max = 10,
+ .nid = 0x8, /* NID to query formats and rates */
+ .ops = {
+ .open = via_playback_pcm_open,
+ .prepare = via_playback_multi_pcm_prepare,
+ .cleanup = via_playback_multi_pcm_cleanup,
+ .close = via_pcm_open_close,
+ },
+};
+
+static struct hda_pcm_stream vt1718S_pcm_analog_capture = {
+ .substreams = 2,
+ .channels_min = 2,
+ .channels_max = 2,
+ .nid = 0x10, /* NID to query formats and rates */
+ .ops = {
+ .open = via_pcm_open_close,
+ .prepare = via_capture_pcm_prepare,
+ .cleanup = via_capture_pcm_cleanup,
+ .close = via_pcm_open_close,
+ },
+};
+
+static struct hda_pcm_stream vt1718S_pcm_digital_playback = {
+ .substreams = 2,
+ .channels_min = 2,
+ .channels_max = 2,
+ /* NID is set in via_build_pcms */
+ .ops = {
+ .open = via_dig_playback_pcm_open,
+ .close = via_dig_playback_pcm_close,
+ .prepare = via_dig_playback_pcm_prepare,
+ .cleanup = via_dig_playback_pcm_cleanup
+ },
+};
+
+static struct hda_pcm_stream vt1718S_pcm_digital_capture = {
+ .substreams = 1,
+ .channels_min = 2,
+ .channels_max = 2,
+};
+
+/* fill in the dac_nids table from the parsed pin configuration */
+static int vt1718S_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] = 0x8;
+ break;
+ case AUTO_SEQ_CENLFE:
+ spec->multiout.dac_nids[i] = 0xa;
+ break;
+ case AUTO_SEQ_SURROUND:
+ spec->multiout.dac_nids[i] = 0x9;
+ break;
+ case AUTO_SEQ_SIDE:
+ spec->multiout.dac_nids[i] = 0xb;
+ break;
+ }
+ }
+ }
+
+ return 0;
+}
+
+/* add playback controls from the parsed DAC table */
+static int vt1718S_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_vols[] = {0x8, 0x9, 0xa, 0xb};
+ hda_nid_t nid_mutes[] = {0x24, 0x25, 0x26, 0x27};
+ hda_nid_t nid, nid_vol, nid_mute = 0;
+ int i, err;
+
+ for (i = 0; i <= AUTO_SEQ_SIDE; i++) {
+ nid = cfg->line_out_pins[i];
+
+ if (!nid)
+ continue;
+ nid_vol = nid_vols[i];
+ nid_mute = nid_mutes[i];
+
+ 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_mute, 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_mute, 2, 0,
+ HDA_OUTPUT));
+ if (err < 0)
+ return err;
+ } else if (i == AUTO_SEQ_FRONT) {
+ /* Front */
+ 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_mute, 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_mute, 3, 0,
+ HDA_OUTPUT));
+ if (err < 0)
+ return err;
+ }
+ }
+ return 0;
+}
+
+static int vt1718S_auto_create_hp_ctls(struct via_spec *spec, hda_nid_t pin)
+{
+ int err;
+
+ if (!pin)
+ return 0;
+
+ spec->multiout.hp_nid = 0xc; /* AOW4 */
+ spec->hp_independent_mode_index = 1;
+
+ err = via_add_control(spec, VIA_CTL_WIDGET_VOL,
+ "Headphone Playback Volume",
+ HDA_COMPOSE_AMP_VAL(0xc, 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;
+
+ create_hp_imux(spec);
+ return 0;
+}
+
+/* create playback/capture controls for input pins */
+static int vt1718S_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[0];
+ int i, err, idx = 0;
+
+ /* for internal loopback recording select */
+ imux->items[imux->num_items].label = "Stereo Mixer";
+ imux->items[imux->num_items].index = 5;
+ imux->num_items++;
+
+ for (i = 0; i < AUTO_PIN_LAST; i++) {
+ if (!cfg->input_pins[i])
+ continue;
+
+ switch (cfg->input_pins[i]) {
+ case 0x2b: /* Mic */
+ idx = 1;
+ break;
+
+ case 0x2a: /* Line In */
+ idx = 2;
+ break;
+
+ case 0x29: /* Front Mic */
+ idx = 3;
+ break;
+
+ case 0x2c: /* CD */
+ idx = 0;
+ break;
+ }
+ err = via_new_analog_input(spec, labels[i], idx, 0x21);
+ 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 vt1718S_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 = vt1718S_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 = vt1718S_auto_create_multi_out_ctls(spec, &spec->autocfg);
+ if (err < 0)
+ return err;
+ err = vt1718S_auto_create_hp_ctls(spec, spec->autocfg.hp_pins[0]);
+ if (err < 0)
+ return err;
+ err = vt1718S_auto_create_analog_input_ctls(spec, &spec->autocfg);
+ if (err < 0)
+ return err;
+
+ spec->multiout.max_channels = spec->multiout.num_dacs * 2;
+
+ fill_dig_outs(codec);
+
+ if (spec->autocfg.dig_in_pin && codec->vendor_id == 0x11060428)
+ spec->dig_in_nid = 0x13;
+
+ if (spec->kctls.list)
+ spec->mixers[spec->num_mixers++] = spec->kctls.list;
+
+ spec->input_mux = &spec->private_imux[0];
+
+ if (spec->hp_mux)
+ spec->mixers[spec->num_mixers++] = via_hp_mixer;
+
+ spec->mixers[spec->num_mixers++] = via_smart51_mixer;
+
+ return 1;
+}
+
+#ifdef CONFIG_SND_HDA_POWER_SAVE
+static struct hda_amp_list vt1718S_loopbacks[] = {
+ { 0x21, HDA_INPUT, 1 },
+ { 0x21, HDA_INPUT, 2 },
+ { 0x21, HDA_INPUT, 3 },
+ { 0x21, HDA_INPUT, 4 },
+ { } /* end */
+};
+#endif
+
+static int patch_vt1718S(struct hda_codec *codec)
+{
+ struct via_spec *spec;
+ int err;
- /* Enable GPIO 0&1 for volume&mute control */
- /* Enable GPIO 2 for DMIC-DATA */
- response = snd_hda_codec_read(codec, codec->afg, 0, 0xF84, 0);
- control = (unsigned char)((response >> 16) & 0x3f);
- snd_hda_codec_write(codec, codec->afg, 0, 0xF82, control);
+ /* create a codec specific record */
+ spec = kzalloc(sizeof(*spec), GFP_KERNEL);
+ if (spec == NULL)
+ return -ENOMEM;
+
+ codec->spec = spec;
+
+ /* automatic parse from the BIOS config */
+ err = vt1718S_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->init_verbs[spec->num_iverbs++] = vt1718S_volume_init_verbs;
+ spec->init_verbs[spec->num_iverbs++] = vt1718S_uniwill_init_verbs;
+
+ if (codec->vendor_id == 0x11060441)
+ spec->stream_name_analog = "VT2020 Analog";
+ else if (codec->vendor_id == 0x11064441)
+ spec->stream_name_analog = "VT1828S Analog";
+ else
+ spec->stream_name_analog = "VT1718S Analog";
+ spec->stream_analog_playback = &vt1718S_pcm_analog_playback;
+ spec->stream_analog_capture = &vt1718S_pcm_analog_capture;
+
+ if (codec->vendor_id == 0x11060441)
+ spec->stream_name_digital = "VT2020 Digital";
+ else if (codec->vendor_id == 0x11064441)
+ spec->stream_name_digital = "VT1828S Digital";
+ else
+ spec->stream_name_digital = "VT1718S Digital";
+ spec->stream_digital_playback = &vt1718S_pcm_digital_playback;
+ if (codec->vendor_id == 0x11060428 || codec->vendor_id == 0x11060441)
+ spec->stream_digital_capture = &vt1718S_pcm_digital_capture;
+
+ if (!spec->adc_nids && spec->input_mux) {
+ spec->adc_nids = vt1718S_adc_nids;
+ spec->num_adc_nids = ARRAY_SIZE(vt1718S_adc_nids);
+ get_mux_nids(codec);
+ override_mic_boost(codec, 0x2b, 0, 3, 40);
+ override_mic_boost(codec, 0x29, 0, 3, 40);
+ spec->mixers[spec->num_mixers] = vt1718S_capture_mixer;
+ spec->num_mixers++;
+ }
+
+ codec->patch_ops = via_patch_ops;
+
+ codec->patch_ops.init = via_auto_init;
+ codec->patch_ops.unsol_event = via_unsol_event;
+
+#ifdef CONFIG_SND_HDA_POWER_SAVE
+ spec->loopback.amplist = vt1718S_loopbacks;
+#endif
+
+ return 0;
+}
+
+/* Patch for VT1716S */
+
+static int vt1716s_dmic_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 vt1716s_dmic_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
+ int index = 0;
+
+ index = snd_hda_codec_read(codec, 0x26, 0,
+ AC_VERB_GET_CONNECT_SEL, 0);
+ if (index != -1)
+ *ucontrol->value.integer.value = index;
+
+ return 0;
+}
+
+static int vt1716s_dmic_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
+ struct via_spec *spec = codec->spec;
+ int index = *ucontrol->value.integer.value;
+
+ snd_hda_codec_write(codec, 0x26, 0,
+ AC_VERB_SET_CONNECT_SEL, index);
+ spec->dmic_enabled = index;
+ set_jack_power_state(codec);
+
+ return 1;
+}
+
+/* capture mixer elements */
+static struct snd_kcontrol_new vt1716S_capture_mixer[] = {
+ HDA_CODEC_VOLUME("Capture Volume", 0x13, 0x0, HDA_INPUT),
+ HDA_CODEC_MUTE("Capture Switch", 0x13, 0x0, HDA_INPUT),
+ HDA_CODEC_VOLUME_IDX("Capture Volume", 1, 0x14, 0x0, HDA_INPUT),
+ HDA_CODEC_MUTE_IDX("Capture Switch", 1, 0x14, 0x0, HDA_INPUT),
+ HDA_CODEC_VOLUME("Mic Boost Capture Volume", 0x1A, 0x0, HDA_INPUT),
+ HDA_CODEC_VOLUME("Front Mic Boost Capture Volume", 0x1E, 0x0,
+ HDA_INPUT),
+ {
+ .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+ .name = "Input Source",
+ .count = 1,
+ .info = via_mux_enum_info,
+ .get = via_mux_enum_get,
+ .put = via_mux_enum_put,
+ },
+ { } /* end */
+};
+
+static struct snd_kcontrol_new vt1716s_dmic_mixer[] = {
+ HDA_CODEC_VOLUME("Digital Mic Capture Volume", 0x22, 0x0, HDA_INPUT),
+ {
+ .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+ .name = "Digital Mic Capture Switch",
+ .count = 1,
+ .info = vt1716s_dmic_info,
+ .get = vt1716s_dmic_get,
+ .put = vt1716s_dmic_put,
+ },
+ {} /* end */
+};
+
+
+/* mono-out mixer elements */
+static struct snd_kcontrol_new vt1716S_mono_out_mixer[] = {
+ HDA_CODEC_MUTE("Mono Playback Switch", 0x2a, 0x0, HDA_OUTPUT),
+ { } /* end */
+};
+
+static struct hda_verb vt1716S_volume_init_verbs[] = {
+ /*
+ * Unmute ADC0-1 and set the default input to mic-in
+ */
+ {0x13, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
+ {0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
+
+
+ /* Mute 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 */
+ {0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
+ {0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
+ {0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(2)},
+ {0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(3)},
+ {0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(4)},
+
+ /* MUX Indices: Stereo Mixer = 5 */
+ {0x17, AC_VERB_SET_CONNECT_SEL, 0x5},
+
+ /* Setup default input of PW4 to MW0 */
+ {0x1d, AC_VERB_SET_CONNECT_SEL, 0x0},
+
+ /* Setup default input of SW1 as MW0 */
+ {0x18, AC_VERB_SET_CONNECT_SEL, 0x1},
+
+ /* Setup default input of SW4 as AOW0 */
+ {0x28, AC_VERB_SET_CONNECT_SEL, 0x1},
+
+ /* PW9 PW10 Output enable */
+ {0x20, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x40},
+ {0x21, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x40},
+
+ /* Unmute SW1, PW12 */
+ {0x29, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
+ {0x2a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
+ /* PW12 Output enable */
+ {0x2a, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x40},
+ /* Enable Boost Volume backdoor */
+ {0x1, 0xf8a, 0x80},
+ /* don't bybass mixer */
+ {0x1, 0xf88, 0xc0},
+ /* Enable mono output */
+ {0x1, 0xf90, 0x08},
+ { }
+};
+
+
+static struct hda_verb vt1716S_uniwill_init_verbs[] = {
+ {0x1d, AC_VERB_SET_UNSOLICITED_ENABLE,
+ AC_USRSP_EN | VIA_HP_EVENT | VIA_JACK_EVENT},
+ {0x19, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT},
+ {0x1a, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT},
+ {0x1b, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT},
+ {0x1c, AC_VERB_SET_UNSOLICITED_ENABLE,
+ AC_USRSP_EN | VIA_MONO_EVENT | VIA_JACK_EVENT},
+ {0x1e, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT},
+ {0x23, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT},
+ { }
+};
+
+static struct hda_pcm_stream vt1716S_pcm_analog_playback = {
+ .substreams = 2,
+ .channels_min = 2,
+ .channels_max = 6,
+ .nid = 0x10, /* NID to query formats and rates */
+ .ops = {
+ .open = via_playback_pcm_open,
+ .prepare = via_playback_multi_pcm_prepare,
+ .cleanup = via_playback_multi_pcm_cleanup,
+ .close = via_pcm_open_close,
+ },
+};
+
+static struct hda_pcm_stream vt1716S_pcm_analog_capture = {
+ .substreams = 2,
+ .channels_min = 2,
+ .channels_max = 2,
+ .nid = 0x13, /* NID to query formats and rates */
+ .ops = {
+ .open = via_pcm_open_close,
+ .prepare = via_capture_pcm_prepare,
+ .cleanup = via_capture_pcm_cleanup,
+ .close = via_pcm_open_close,
+ },
+};
+
+static struct hda_pcm_stream vt1716S_pcm_digital_playback = {
+ .substreams = 2,
+ .channels_min = 2,
+ .channels_max = 2,
+ /* NID is set in via_build_pcms */
+ .ops = {
+ .open = via_dig_playback_pcm_open,
+ .close = via_dig_playback_pcm_close,
+ .prepare = via_dig_playback_pcm_prepare,
+ .cleanup = via_dig_playback_pcm_cleanup
+ },
+};
+
+/* fill in the dac_nids table from the parsed pin configuration */
+static int vt1716S_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 < 3; 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] = 0x25;
+ break;
+ case AUTO_SEQ_SURROUND:
+ spec->multiout.dac_nids[i] = 0x11;
+ break;
+ }
+ }
+ }
+
+ return 0;
+}
+
+/* add playback controls from the parsed DAC table */
+static int vt1716S_auto_create_multi_out_ctls(struct via_spec *spec,
+ const struct auto_pin_cfg *cfg)
+{
+ char name[32];
+ static const char *chname[3] = { "Front", "Surround", "C/LFE" };
+ hda_nid_t nid_vols[] = {0x10, 0x11, 0x25};
+ hda_nid_t nid_mutes[] = {0x1C, 0x18, 0x27};
+ hda_nid_t nid, nid_vol, nid_mute;
+ int i, err;
+
+ for (i = 0; i <= AUTO_SEQ_CENLFE; i++) {
+ nid = cfg->line_out_pins[i];
+
+ if (!nid)
+ continue;
+
+ nid_vol = nid_vols[i];
+ nid_mute = nid_mutes[i];
+
+ if (i == AUTO_SEQ_CENLFE) {
+ 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_mute, 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_mute, 2, 0,
+ HDA_OUTPUT));
+ if (err < 0)
+ return err;
+ } else if (i == AUTO_SEQ_FRONT) {
+
+ err = via_add_control(
+ spec, VIA_CTL_WIDGET_VOL,
+ "Master Front Playback Volume",
+ HDA_COMPOSE_AMP_VAL(0x16, 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(0x16, 3, 0, HDA_INPUT));
+ if (err < 0)
+ return err;
+
+ 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_mute, 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_mute, 3, 0,
+ HDA_OUTPUT));
+ if (err < 0)
+ return err;
+ }
+ }
+ return 0;
+}
+
+static int vt1716S_auto_create_hp_ctls(struct via_spec *spec, hda_nid_t pin)
+{
+ int err;
+
+ if (!pin)
+ return 0;
+
+ spec->multiout.hp_nid = 0x25; /* AOW3 */
+ spec->hp_independent_mode_index = 1;
+
+ err = via_add_control(spec, VIA_CTL_WIDGET_VOL,
+ "Headphone Playback Volume",
+ HDA_COMPOSE_AMP_VAL(0x25, 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;
+
+ create_hp_imux(spec);
+ return 0;
+}
+
+/* create playback/capture controls for input pins */
+static int vt1716S_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[0];
+ int i, err, idx = 0;
+
+ /* for internal loopback recording select */
+ imux->items[imux->num_items].label = "Stereo Mixer";
+ imux->items[imux->num_items].index = 5;
+ imux->num_items++;
+
+ for (i = 0; i < AUTO_PIN_LAST; i++) {
+ if (!cfg->input_pins[i])
+ continue;
+
+ switch (cfg->input_pins[i]) {
+ case 0x1a: /* Mic */
+ idx = 2;
+ break;
+
+ case 0x1b: /* Line In */
+ idx = 3;
+ break;
+
+ case 0x1e: /* Front Mic */
+ idx = 4;
+ break;
+
+ case 0x1f: /* CD */
+ idx = 1;
+ break;
+ }
+ err = via_new_analog_input(spec, labels[i], idx, 0x16);
+ if (err < 0)
+ return err;
+ imux->items[imux->num_items].label = labels[i];
+ imux->items[imux->num_items].index = idx-1;
+ imux->num_items++;
+ }
+ return 0;
+}
+
+static int vt1716S_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 = vt1716S_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 = vt1716S_auto_create_multi_out_ctls(spec, &spec->autocfg);
+ if (err < 0)
+ return err;
+ err = vt1716S_auto_create_hp_ctls(spec, spec->autocfg.hp_pins[0]);
+ if (err < 0)
+ return err;
+ err = vt1716S_auto_create_analog_input_ctls(spec, &spec->autocfg);
+ if (err < 0)
+ return err;
+
+ spec->multiout.max_channels = spec->multiout.num_dacs * 2;
+
+ fill_dig_outs(codec);
+
+ if (spec->kctls.list)
+ spec->mixers[spec->num_mixers++] = spec->kctls.list;
+
+ spec->input_mux = &spec->private_imux[0];
+
+ if (spec->hp_mux)
+ spec->mixers[spec->num_mixers++] = via_hp_mixer;
+
+ spec->mixers[spec->num_mixers++] = via_smart51_mixer;
+
+ return 1;
+}
+
+#ifdef CONFIG_SND_HDA_POWER_SAVE
+static struct hda_amp_list vt1716S_loopbacks[] = {
+ { 0x16, HDA_INPUT, 1 },
+ { 0x16, HDA_INPUT, 2 },
+ { 0x16, HDA_INPUT, 3 },
+ { 0x16, HDA_INPUT, 4 },
+ { } /* end */
+};
+#endif
+
+static int patch_vt1716S(struct hda_codec *codec)
+{
+ struct via_spec *spec;
+ int err;
+
+ /* create a codec specific record */
+ spec = kzalloc(sizeof(*spec), GFP_KERNEL);
+ if (spec == NULL)
+ return -ENOMEM;
+
+ codec->spec = spec;
+
+ /* automatic parse from the BIOS config */
+ err = vt1716S_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->init_verbs[spec->num_iverbs++] = vt1716S_volume_init_verbs;
+ spec->init_verbs[spec->num_iverbs++] = vt1716S_uniwill_init_verbs;
+
+ spec->stream_name_analog = "VT1716S Analog";
+ spec->stream_analog_playback = &vt1716S_pcm_analog_playback;
+ spec->stream_analog_capture = &vt1716S_pcm_analog_capture;
+
+ spec->stream_name_digital = "VT1716S Digital";
+ spec->stream_digital_playback = &vt1716S_pcm_digital_playback;
+
+ if (!spec->adc_nids && spec->input_mux) {
+ spec->adc_nids = vt1716S_adc_nids;
+ spec->num_adc_nids = ARRAY_SIZE(vt1716S_adc_nids);
+ get_mux_nids(codec);
+ override_mic_boost(codec, 0x1a, 0, 3, 40);
+ override_mic_boost(codec, 0x1e, 0, 3, 40);
+ spec->mixers[spec->num_mixers] = vt1716S_capture_mixer;
+ spec->num_mixers++;
+ }
+
+ spec->mixers[spec->num_mixers] = vt1716s_dmic_mixer;
+ spec->num_mixers++;
+
+ spec->mixers[spec->num_mixers++] = vt1716S_mono_out_mixer;
+
+ codec->patch_ops = via_patch_ops;
+
+ codec->patch_ops.init = via_auto_init;
+ codec->patch_ops.unsol_event = via_unsol_event;
+
+#ifdef CONFIG_SND_HDA_POWER_SAVE
+ spec->loopback.amplist = vt1716S_loopbacks;
+#endif
+
+ return 0;
+}
+
+/* for vt2002P */
+
+/* capture mixer elements */
+static struct snd_kcontrol_new vt2002P_capture_mixer[] = {
+ HDA_CODEC_VOLUME("Capture Volume", 0x10, 0x0, HDA_INPUT),
+ HDA_CODEC_MUTE("Capture Switch", 0x10, 0x0, HDA_INPUT),
+ HDA_CODEC_VOLUME_IDX("Capture Volume", 1, 0x11, 0x0, HDA_INPUT),
+ HDA_CODEC_MUTE_IDX("Capture Switch", 1, 0x11, 0x0, HDA_INPUT),
+ HDA_CODEC_VOLUME("Mic Boost Capture Volume", 0x2b, 0x0, HDA_INPUT),
+ HDA_CODEC_VOLUME("Front Mic Boost Capture Volume", 0x29, 0x0,
+ HDA_INPUT),
+ {
+ .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+ /* The multiple "Capture Source" controls confuse alsamixer
+ * So call somewhat different..
+ */
+ /* .name = "Capture Source", */
+ .name = "Input Source",
+ .count = 2,
+ .info = via_mux_enum_info,
+ .get = via_mux_enum_get,
+ .put = via_mux_enum_put,
+ },
+ { } /* end */
+};
+
+static struct hda_verb vt2002P_volume_init_verbs[] = {
+ /*
+ * Unmute ADC0-1 and set the default input to mic-in
+ */
+ {0x8, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
+ {0x9, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
+
+
+ /* Mute 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 */
+ {0x21, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
+ {0x21, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
+ {0x21, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(2)},
+ {0x21, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(3)},
+ {0x21, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(4)},
+
+ /* MUX Indices: Mic = 0 */
+ {0x1e, AC_VERB_SET_CONNECT_SEL, 0},
+ {0x1f, AC_VERB_SET_CONNECT_SEL, 0},
+
+ /* PW9 Output enable */
+ {0x2d, AC_VERB_SET_PIN_WIDGET_CONTROL, AC_PINCTL_OUT_EN},
+
+ /* Enable Boost Volume backdoor */
+ {0x1, 0xfb9, 0x24},
+
+ /* MW0/1/4/8: un-mute index 0 (MUXx), un-mute index 1 (MW9) */
+ {0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
+ {0x19, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
+ {0x1c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
+ {0x17, 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(1)},
+ {0x1c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
+ {0x17, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
+
+ /* set MUX0/1/4/8 = 0 (AOW0) */
+ {0x34, AC_VERB_SET_CONNECT_SEL, 0},
+ {0x35, AC_VERB_SET_CONNECT_SEL, 0},
+ {0x37, AC_VERB_SET_CONNECT_SEL, 0},
+ {0x3b, AC_VERB_SET_CONNECT_SEL, 0},
+
+ /* set PW0 index=0 (MW0) */
+ {0x24, AC_VERB_SET_CONNECT_SEL, 0},
+
+ /* Enable AOW0 to MW9 */
+ {0x1, 0xfb8, 0x88},
+ { }
+};
+
+
+static struct hda_verb vt2002P_uniwill_init_verbs[] = {
+ {0x25, AC_VERB_SET_UNSOLICITED_ENABLE,
+ AC_USRSP_EN | VIA_JACK_EVENT | VIA_BIND_HP_EVENT},
+ {0x26, AC_VERB_SET_UNSOLICITED_ENABLE,
+ AC_USRSP_EN | VIA_JACK_EVENT | VIA_BIND_HP_EVENT},
+ {0x29, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT},
+ {0x2a, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT},
+ {0x2b, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT},
+ { }
+};
+
+static struct hda_pcm_stream vt2002P_pcm_analog_playback = {
+ .substreams = 2,
+ .channels_min = 2,
+ .channels_max = 2,
+ .nid = 0x8, /* NID to query formats and rates */
+ .ops = {
+ .open = via_playback_pcm_open,
+ .prepare = via_playback_multi_pcm_prepare,
+ .cleanup = via_playback_multi_pcm_cleanup,
+ .close = via_pcm_open_close,
+ },
+};
+
+static struct hda_pcm_stream vt2002P_pcm_analog_capture = {
+ .substreams = 2,
+ .channels_min = 2,
+ .channels_max = 2,
+ .nid = 0x10, /* NID to query formats and rates */
+ .ops = {
+ .open = via_pcm_open_close,
+ .prepare = via_capture_pcm_prepare,
+ .cleanup = via_capture_pcm_cleanup,
+ .close = via_pcm_open_close,
+ },
+};
+
+static struct hda_pcm_stream vt2002P_pcm_digital_playback = {
+ .substreams = 1,
+ .channels_min = 2,
+ .channels_max = 2,
+ /* NID is set in via_build_pcms */
+ .ops = {
+ .open = via_dig_playback_pcm_open,
+ .close = via_dig_playback_pcm_close,
+ .prepare = via_dig_playback_pcm_prepare,
+ .cleanup = via_dig_playback_pcm_cleanup
+ },
+};
+
+/* fill in the dac_nids table from the parsed pin configuration */
+static int vt2002P_auto_fill_dac_nids(struct via_spec *spec,
+ const struct auto_pin_cfg *cfg)
+{
+ spec->multiout.num_dacs = 1;
+ spec->multiout.dac_nids = spec->private_dac_nids;
+ if (cfg->line_out_pins[0])
+ spec->multiout.dac_nids[0] = 0x8;
+ return 0;
+}
+
+/* add playback controls from the parsed DAC table */
+static int vt2002P_auto_create_multi_out_ctls(struct via_spec *spec,
+ const struct auto_pin_cfg *cfg)
+{
+ int err;
+
+ if (!cfg->line_out_pins[0])
+ return -1;
+
+
+ /* Line-Out: PortE */
+ err = via_add_control(spec, VIA_CTL_WIDGET_VOL,
+ "Master Front Playback Volume",
+ HDA_COMPOSE_AMP_VAL(0x8, 3, 0, HDA_OUTPUT));
+ if (err < 0)
+ return err;
+ err = via_add_control(spec, VIA_CTL_WIDGET_BIND_PIN_MUTE,
+ "Master Front Playback Switch",
+ HDA_COMPOSE_AMP_VAL(0x26, 3, 0, HDA_OUTPUT));
+ if (err < 0)
+ return err;
+
+ return 0;
+}
+
+static int vt2002P_auto_create_hp_ctls(struct via_spec *spec, hda_nid_t pin)
+{
+ int err;
+
+ if (!pin)
+ return 0;
+
+ spec->multiout.hp_nid = 0x9;
+ spec->hp_independent_mode_index = 1;
+
+ err = via_add_control(spec, VIA_CTL_WIDGET_VOL,
+ "Headphone Playback Volume",
+ HDA_COMPOSE_AMP_VAL(
+ spec->multiout.hp_nid, 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(0x25, 3, 0, HDA_OUTPUT));
+ if (err < 0)
+ return err;
+
+ create_hp_imux(spec);
+ return 0;
+}
+
+/* create playback/capture controls for input pins */
+static int vt2002P_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[0];
+ int i, err, idx = 0;
+
+ for (i = 0; i < AUTO_PIN_LAST; i++) {
+ if (!cfg->input_pins[i])
+ continue;
+
+ switch (cfg->input_pins[i]) {
+ case 0x2b: /* Mic */
+ idx = 0;
+ break;
+
+ case 0x2a: /* Line In */
+ idx = 1;
+ break;
+
+ case 0x29: /* Front Mic */
+ idx = 2;
+ break;
+ }
+ err = via_new_analog_input(spec, labels[i], idx, 0x21);
+ if (err < 0)
+ return err;
+ imux->items[imux->num_items].label = labels[i];
+ imux->items[imux->num_items].index = idx;
+ imux->num_items++;
+ }
+
+ /* build volume/mute control of loopback */
+ err = via_new_analog_input(spec, "Stereo Mixer", 3, 0x21);
+ if (err < 0)
+ return err;
+
+ /* for internal loopback recording select */
+ imux->items[imux->num_items].label = "Stereo Mixer";
+ imux->items[imux->num_items].index = 3;
+ imux->num_items++;
+
+ /* for digital mic select */
+ imux->items[imux->num_items].label = "Digital Mic";
+ imux->items[imux->num_items].index = 4;
+ imux->num_items++;
+
+ return 0;
+}
+
+static int vt2002P_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 = vt2002P_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 = vt2002P_auto_create_multi_out_ctls(spec, &spec->autocfg);
+ if (err < 0)
+ return err;
+ err = vt2002P_auto_create_hp_ctls(spec, spec->autocfg.hp_pins[0]);
+ if (err < 0)
+ return err;
+ err = vt2002P_auto_create_analog_input_ctls(spec, &spec->autocfg);
+ if (err < 0)
+ return err;
+
+ spec->multiout.max_channels = spec->multiout.num_dacs * 2;
+
+ fill_dig_outs(codec);
+
+ if (spec->kctls.list)
+ spec->mixers[spec->num_mixers++] = spec->kctls.list;
+
+ spec->input_mux = &spec->private_imux[0];
+
+ if (spec->hp_mux)
+ spec->mixers[spec->num_mixers++] = via_hp_mixer;
+
+ return 1;
+}
+
+#ifdef CONFIG_SND_HDA_POWER_SAVE
+static struct hda_amp_list vt2002P_loopbacks[] = {
+ { 0x21, HDA_INPUT, 0 },
+ { 0x21, HDA_INPUT, 1 },
+ { 0x21, HDA_INPUT, 2 },
+ { } /* end */
+};
+#endif
+
+
+/* patch for vt2002P */
+static int patch_vt2002P(struct hda_codec *codec)
+{
+ struct via_spec *spec;
+ int err;
+
+ /* create a codec specific record */
+ spec = kzalloc(sizeof(*spec), GFP_KERNEL);
+ if (spec == NULL)
+ return -ENOMEM;
+
+ codec->spec = spec;
+
+ /* automatic parse from the BIOS config */
+ err = vt2002P_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->init_verbs[spec->num_iverbs++] = vt2002P_volume_init_verbs;
+ spec->init_verbs[spec->num_iverbs++] = vt2002P_uniwill_init_verbs;
+
+ spec->stream_name_analog = "VT2002P Analog";
+ spec->stream_analog_playback = &vt2002P_pcm_analog_playback;
+ spec->stream_analog_capture = &vt2002P_pcm_analog_capture;
+
+ spec->stream_name_digital = "VT2002P Digital";
+ spec->stream_digital_playback = &vt2002P_pcm_digital_playback;
+
+ if (!spec->adc_nids && spec->input_mux) {
+ spec->adc_nids = vt2002P_adc_nids;
+ spec->num_adc_nids = ARRAY_SIZE(vt2002P_adc_nids);
+ get_mux_nids(codec);
+ override_mic_boost(codec, 0x2b, 0, 3, 40);
+ override_mic_boost(codec, 0x29, 0, 3, 40);
+ spec->mixers[spec->num_mixers] = vt2002P_capture_mixer;
+ spec->num_mixers++;
+ }
+
+ codec->patch_ops = via_patch_ops;
+
+ codec->patch_ops.init = via_auto_init;
+ codec->patch_ops.unsol_event = via_unsol_event;
+
+#ifdef CONFIG_SND_HDA_POWER_SAVE
+ spec->loopback.amplist = vt2002P_loopbacks;
+#endif
+
+ return 0;
+}
+
+/* for vt1812 */
+
+/* capture mixer elements */
+static struct snd_kcontrol_new vt1812_capture_mixer[] = {
+ HDA_CODEC_VOLUME("Capture Volume", 0x10, 0x0, HDA_INPUT),
+ HDA_CODEC_MUTE("Capture Switch", 0x10, 0x0, HDA_INPUT),
+ HDA_CODEC_VOLUME_IDX("Capture Volume", 1, 0x11, 0x0, HDA_INPUT),
+ HDA_CODEC_MUTE_IDX("Capture Switch", 1, 0x11, 0x0, HDA_INPUT),
+ HDA_CODEC_MUTE("Mic Boost Capture Volume", 0x2b, 0x0, HDA_INPUT),
+ HDA_CODEC_MUTE("Front Mic Boost Capture Volume", 0x29, 0x0,
+ HDA_INPUT),
+ {
+ .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+ /* The multiple "Capture Source" controls confuse alsamixer
+ * So call somewhat different..
+ */
+ .name = "Input Source",
+ .count = 2,
+ .info = via_mux_enum_info,
+ .get = via_mux_enum_get,
+ .put = via_mux_enum_put,
+ },
+ { } /* end */
+};
+
+static struct hda_verb vt1812_volume_init_verbs[] = {
+ /*
+ * Unmute ADC0-1 and set the default input to mic-in
+ */
+ {0x8, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
+ {0x9, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
+
+
+ /* Mute 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 */
+ {0x21, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
+ {0x21, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
+ {0x21, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(2)},
+ {0x21, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(3)},
+ {0x21, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(4)},
+
+ /* MUX Indices: Mic = 0 */
+ {0x1e, AC_VERB_SET_CONNECT_SEL, 0},
+ {0x1f, AC_VERB_SET_CONNECT_SEL, 0},
+
+ /* PW9 Output enable */
+ {0x2d, AC_VERB_SET_PIN_WIDGET_CONTROL, AC_PINCTL_OUT_EN},
+
+ /* Enable Boost Volume backdoor */
+ {0x1, 0xfb9, 0x24},
+
+ /* MW0/1/4/13/15: un-mute index 0 (MUXx), un-mute index 1 (MW9) */
+ {0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
+ {0x15, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
+ {0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
+ {0x1c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
+ {0x1d, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
+ {0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
+ {0x15, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
+ {0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
+ {0x1c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
+ {0x1d, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
+
+ /* set MUX0/1/4/13/15 = 0 (AOW0) */
+ {0x34, AC_VERB_SET_CONNECT_SEL, 0},
+ {0x35, AC_VERB_SET_CONNECT_SEL, 0},
+ {0x38, AC_VERB_SET_CONNECT_SEL, 0},
+ {0x3c, AC_VERB_SET_CONNECT_SEL, 0},
+ {0x3d, AC_VERB_SET_CONNECT_SEL, 0},
+
+ /* Enable AOW0 to MW9 */
+ {0x1, 0xfb8, 0xa8},
+ { }
+};
+
+
+static struct hda_verb vt1812_uniwill_init_verbs[] = {
+ {0x33, AC_VERB_SET_UNSOLICITED_ENABLE,
+ AC_USRSP_EN | VIA_JACK_EVENT | VIA_BIND_HP_EVENT},
+ {0x25, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT },
+ {0x28, AC_VERB_SET_UNSOLICITED_ENABLE,
+ AC_USRSP_EN | VIA_JACK_EVENT | VIA_BIND_HP_EVENT},
+ {0x29, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT},
+ {0x2a, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT},
+ {0x2b, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT},
+ { }
+};
+
+static struct hda_pcm_stream vt1812_pcm_analog_playback = {
+ .substreams = 2,
+ .channels_min = 2,
+ .channels_max = 2,
+ .nid = 0x8, /* NID to query formats and rates */
+ .ops = {
+ .open = via_playback_pcm_open,
+ .prepare = via_playback_multi_pcm_prepare,
+ .cleanup = via_playback_multi_pcm_cleanup,
+ .close = via_pcm_open_close,
+ },
+};
+
+static struct hda_pcm_stream vt1812_pcm_analog_capture = {
+ .substreams = 2,
+ .channels_min = 2,
+ .channels_max = 2,
+ .nid = 0x10, /* NID to query formats and rates */
+ .ops = {
+ .open = via_pcm_open_close,
+ .prepare = via_capture_pcm_prepare,
+ .cleanup = via_capture_pcm_cleanup,
+ .close = via_pcm_open_close,
+ },
+};
+
+static struct hda_pcm_stream vt1812_pcm_digital_playback = {
+ .substreams = 1,
+ .channels_min = 2,
+ .channels_max = 2,
+ /* NID is set in via_build_pcms */
+ .ops = {
+ .open = via_dig_playback_pcm_open,
+ .close = via_dig_playback_pcm_close,
+ .prepare = via_dig_playback_pcm_prepare,
+ .cleanup = via_dig_playback_pcm_cleanup
+ },
+};
+/* fill in the dac_nids table from the parsed pin configuration */
+static int vt1812_auto_fill_dac_nids(struct via_spec *spec,
+ const struct auto_pin_cfg *cfg)
+{
+ spec->multiout.num_dacs = 1;
+ spec->multiout.dac_nids = spec->private_dac_nids;
+ if (cfg->line_out_pins[0])
+ spec->multiout.dac_nids[0] = 0x8;
+ return 0;
+}
+
+
+/* add playback controls from the parsed DAC table */
+static int vt1812_auto_create_multi_out_ctls(struct via_spec *spec,
+ const struct auto_pin_cfg *cfg)
+{
+ int err;
+
+ if (!cfg->line_out_pins[0])
+ return -1;
+
+ /* Line-Out: PortE */
+ err = via_add_control(spec, VIA_CTL_WIDGET_VOL,
+ "Master Front Playback Volume",
+ HDA_COMPOSE_AMP_VAL(0x8, 3, 0, HDA_OUTPUT));
+ if (err < 0)
+ return err;
+ err = via_add_control(spec, VIA_CTL_WIDGET_BIND_PIN_MUTE,
+ "Master Front Playback Switch",
+ HDA_COMPOSE_AMP_VAL(0x28, 3, 0, HDA_OUTPUT));
+ if (err < 0)
+ return err;
+
+ return 0;
+}
+
+static int vt1812_auto_create_hp_ctls(struct via_spec *spec, hda_nid_t pin)
+{
+ int err;
+
+ if (!pin)
+ return 0;
+
+ spec->multiout.hp_nid = 0x9;
+ spec->hp_independent_mode_index = 1;
+
+
+ err = via_add_control(spec, VIA_CTL_WIDGET_VOL,
+ "Headphone Playback Volume",
+ HDA_COMPOSE_AMP_VAL(
+ spec->multiout.hp_nid, 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;
+
+ create_hp_imux(spec);
+ return 0;
+}
+
+/* create playback/capture controls for input pins */
+static int vt1812_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[0];
+ int i, err, idx = 0;
+
+ for (i = 0; i < AUTO_PIN_LAST; i++) {
+ if (!cfg->input_pins[i])
+ continue;
+
+ switch (cfg->input_pins[i]) {
+ case 0x2b: /* Mic */
+ idx = 0;
+ break;
+
+ case 0x2a: /* Line In */
+ idx = 1;
+ break;
+
+ case 0x29: /* Front Mic */
+ idx = 2;
+ break;
+ }
+ err = via_new_analog_input(spec, labels[i], idx, 0x21);
+ if (err < 0)
+ return err;
+ imux->items[imux->num_items].label = labels[i];
+ imux->items[imux->num_items].index = idx;
+ imux->num_items++;
+ }
+ /* build volume/mute control of loopback */
+ err = via_new_analog_input(spec, "Stereo Mixer", 5, 0x21);
+ if (err < 0)
+ return err;
+
+ /* for internal loopback recording select */
+ imux->items[imux->num_items].label = "Stereo Mixer";
+ imux->items[imux->num_items].index = 5;
+ imux->num_items++;
+
+ /* for digital mic select */
+ imux->items[imux->num_items].label = "Digital Mic";
+ imux->items[imux->num_items].index = 6;
+ imux->num_items++;
+
+ return 0;
+}
+
+static int vt1812_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;
+ fill_dig_outs(codec);
+ err = vt1812_auto_fill_dac_nids(spec, &spec->autocfg);
+ if (err < 0)
+ return err;
+
+ if (!spec->autocfg.line_outs && !spec->autocfg.hp_outs)
+ return 0; /* can't find valid BIOS pin config */
+
+ err = vt1812_auto_create_multi_out_ctls(spec, &spec->autocfg);
+ if (err < 0)
+ return err;
+ err = vt1812_auto_create_hp_ctls(spec, spec->autocfg.hp_pins[0]);
+ if (err < 0)
+ return err;
+ err = vt1812_auto_create_analog_input_ctls(spec, &spec->autocfg);
+ if (err < 0)
+ return err;
+
+ spec->multiout.max_channels = spec->multiout.num_dacs * 2;
+
+ fill_dig_outs(codec);
+
+ if (spec->kctls.list)
+ spec->mixers[spec->num_mixers++] = spec->kctls.list;
+
+ spec->input_mux = &spec->private_imux[0];
+
+ if (spec->hp_mux)
+ spec->mixers[spec->num_mixers++] = via_hp_mixer;
+
+ return 1;
+}
+
+#ifdef CONFIG_SND_HDA_POWER_SAVE
+static struct hda_amp_list vt1812_loopbacks[] = {
+ { 0x21, HDA_INPUT, 0 },
+ { 0x21, HDA_INPUT, 1 },
+ { 0x21, HDA_INPUT, 2 },
+ { } /* end */
+};
+#endif
+
+
+/* patch for vt1812 */
+static int patch_vt1812(struct hda_codec *codec)
+{
+ struct via_spec *spec;
+ int err;
+
+ /* create a codec specific record */
+ spec = kzalloc(sizeof(*spec), GFP_KERNEL);
+ if (spec == NULL)
+ return -ENOMEM;
+
+ codec->spec = spec;
+
+ /* automatic parse from the BIOS config */
+ err = vt1812_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->init_verbs[spec->num_iverbs++] = vt1812_volume_init_verbs;
+ spec->init_verbs[spec->num_iverbs++] = vt1812_uniwill_init_verbs;
+
+ spec->stream_name_analog = "VT1812 Analog";
+ spec->stream_analog_playback = &vt1812_pcm_analog_playback;
+ spec->stream_analog_capture = &vt1812_pcm_analog_capture;
+
+ spec->stream_name_digital = "VT1812 Digital";
+ spec->stream_digital_playback = &vt1812_pcm_digital_playback;
+
+
+ if (!spec->adc_nids && spec->input_mux) {
+ spec->adc_nids = vt1812_adc_nids;
+ spec->num_adc_nids = ARRAY_SIZE(vt1812_adc_nids);
+ get_mux_nids(codec);
+ override_mic_boost(codec, 0x2b, 0, 3, 40);
+ override_mic_boost(codec, 0x29, 0, 3, 40);
+ spec->mixers[spec->num_mixers] = vt1812_capture_mixer;
+ spec->num_mixers++;
+ }
+
+ codec->patch_ops = via_patch_ops;
+
+ codec->patch_ops.init = via_auto_init;
+ codec->patch_ops.unsol_event = via_unsol_event;
+
+#ifdef CONFIG_SND_HDA_POWER_SAVE
+ spec->loopback.amplist = vt1812_loopbacks;
+#endif
return 0;
}
@@ -3318,6 +6214,23 @@ static struct hda_codec_preset snd_hda_preset_via[] = {
.patch = patch_vt1702},
{ .id = 0x11067398, .name = "VT1702",
.patch = patch_vt1702},
+ { .id = 0x11060428, .name = "VT1718S",
+ .patch = patch_vt1718S},
+ { .id = 0x11064428, .name = "VT1718S",
+ .patch = patch_vt1718S},
+ { .id = 0x11060441, .name = "VT2020",
+ .patch = patch_vt1718S},
+ { .id = 0x11064441, .name = "VT1828S",
+ .patch = patch_vt1718S},
+ { .id = 0x11060433, .name = "VT1716S",
+ .patch = patch_vt1716S},
+ { .id = 0x1106a721, .name = "VT1716S",
+ .patch = patch_vt1716S},
+ { .id = 0x11060438, .name = "VT2002P", .patch = patch_vt2002P},
+ { .id = 0x11064438, .name = "VT2002P", .patch = patch_vt2002P},
+ { .id = 0x11060448, .name = "VT1812", .patch = patch_vt1812},
+ { .id = 0x11060440, .name = "VT1818S",
+ .patch = patch_vt1708S},
{} /* terminator */
};
diff --git a/sound/pci/ice1712/Makefile b/sound/pci/ice1712/Makefile
index 536eae2ccf94..f7ce33f00ea5 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 prodigy_hifi.o juli.o phase.o wtm.o se.o maya44.o
+snd-ice1724-objs := ice1724.o amp.o revo.o aureon.o vt1720_mobo.o pontis.o prodigy192.o prodigy_hifi.o juli.o phase.o wtm.o se.o maya44.o quartet.o
# Toplevel Module Dependency
obj-$(CONFIG_SND_ICE1712) += snd-ice1712.o snd-ice17xx-ak4xxx.o
diff --git a/sound/pci/ice1712/ice1712.c b/sound/pci/ice1712/ice1712.c
index d74033a2cfbe..c7cff6f8168a 100644
--- a/sound/pci/ice1712/ice1712.c
+++ b/sound/pci/ice1712/ice1712.c
@@ -298,6 +298,16 @@ static void snd_ice1712_set_gpio_dir(struct snd_ice1712 *ice, unsigned int data)
inb(ICEREG(ice, DATA)); /* dummy read for pci-posting */
}
+static unsigned int snd_ice1712_get_gpio_dir(struct snd_ice1712 *ice)
+{
+ return snd_ice1712_read(ice, ICE1712_IREG_GPIO_DIRECTION);
+}
+
+static unsigned int snd_ice1712_get_gpio_mask(struct snd_ice1712 *ice)
+{
+ return snd_ice1712_read(ice, ICE1712_IREG_GPIO_WRITE_MASK);
+}
+
static void snd_ice1712_set_gpio_mask(struct snd_ice1712 *ice, unsigned int data)
{
snd_ice1712_write(ice, ICE1712_IREG_GPIO_WRITE_MASK, data);
@@ -2557,7 +2567,9 @@ static int __devinit snd_ice1712_create(struct snd_card *card,
mutex_init(&ice->i2c_mutex);
mutex_init(&ice->open_mutex);
ice->gpio.set_mask = snd_ice1712_set_gpio_mask;
+ ice->gpio.get_mask = snd_ice1712_get_gpio_mask;
ice->gpio.set_dir = snd_ice1712_set_gpio_dir;
+ ice->gpio.get_dir = snd_ice1712_get_gpio_dir;
ice->gpio.set_data = snd_ice1712_set_gpio_data;
ice->gpio.get_data = snd_ice1712_get_gpio_data;
diff --git a/sound/pci/ice1712/ice1712.h b/sound/pci/ice1712/ice1712.h
index d063149e7047..0da778a69ef8 100644
--- a/sound/pci/ice1712/ice1712.h
+++ b/sound/pci/ice1712/ice1712.h
@@ -359,7 +359,9 @@ struct snd_ice1712 {
unsigned int saved[2]; /* for ewx_i2c */
/* operators */
void (*set_mask)(struct snd_ice1712 *ice, unsigned int data);
+ unsigned int (*get_mask)(struct snd_ice1712 *ice);
void (*set_dir)(struct snd_ice1712 *ice, unsigned int data);
+ unsigned int (*get_dir)(struct snd_ice1712 *ice);
void (*set_data)(struct snd_ice1712 *ice, unsigned int data);
unsigned int (*get_data)(struct snd_ice1712 *ice);
/* misc operators - move to another place? */
@@ -377,8 +379,11 @@ struct snd_ice1712 {
unsigned int (*get_rate)(struct snd_ice1712 *ice);
void (*set_rate)(struct snd_ice1712 *ice, unsigned int rate);
unsigned char (*set_mclk)(struct snd_ice1712 *ice, unsigned int rate);
- void (*set_spdif_clock)(struct snd_ice1712 *ice);
-
+ int (*set_spdif_clock)(struct snd_ice1712 *ice, int type);
+ int (*get_spdif_master_type)(struct snd_ice1712 *ice);
+ char **ext_clock_names;
+ int ext_clock_count;
+ void (*pro_open)(struct snd_ice1712 *, struct snd_pcm_substream *);
#ifdef CONFIG_PM
int (*pm_suspend)(struct snd_ice1712 *);
int (*pm_resume)(struct snd_ice1712 *);
@@ -399,6 +404,11 @@ static inline void snd_ice1712_gpio_set_dir(struct snd_ice1712 *ice, unsigned in
ice->gpio.set_dir(ice, bits);
}
+static inline unsigned int snd_ice1712_gpio_get_dir(struct snd_ice1712 *ice)
+{
+ return ice->gpio.get_dir(ice);
+}
+
static inline void snd_ice1712_gpio_set_mask(struct snd_ice1712 *ice, unsigned int bits)
{
ice->gpio.set_mask(ice, bits);
diff --git a/sound/pci/ice1712/ice1724.c b/sound/pci/ice1712/ice1724.c
index 10fc92c05574..ae29073eea93 100644
--- a/sound/pci/ice1712/ice1724.c
+++ b/sound/pci/ice1712/ice1724.c
@@ -53,6 +53,7 @@
#include "phase.h"
#include "wtm.h"
#include "se.h"
+#include "quartet.h"
MODULE_AUTHOR("Jaroslav Kysela <perex@perex.cz>");
MODULE_DESCRIPTION("VIA ICEnsemble ICE1724/1720 (Envy24HT/PT)");
@@ -70,6 +71,7 @@ MODULE_SUPPORTED_DEVICE("{"
PHASE_DEVICE_DESC
WTM_DEVICE_DESC
SE_DEVICE_DESC
+ QTET_DEVICE_DESC
"{VIA,VT1720},"
"{VIA,VT1724},"
"{ICEnsemble,Generic ICE1724},"
@@ -104,6 +106,8 @@ static int PRO_RATE_LOCKED;
static int PRO_RATE_RESET = 1;
static unsigned int PRO_RATE_DEFAULT = 44100;
+static char *ext_clock_names[1] = { "IEC958 In" };
+
/*
* Basic I/O
*/
@@ -118,9 +122,12 @@ static inline int stdclock_is_spdif_master(struct snd_ice1712 *ice)
return (inb(ICEMT1724(ice, RATE)) & VT1724_SPDIF_MASTER) ? 1 : 0;
}
+/*
+ * locking rate makes sense only for internal clock mode
+ */
static inline int is_pro_rate_locked(struct snd_ice1712 *ice)
{
- return ice->is_spdif_master(ice) || PRO_RATE_LOCKED;
+ return (!ice->is_spdif_master(ice)) && PRO_RATE_LOCKED;
}
/*
@@ -196,6 +203,12 @@ static void snd_vt1724_set_gpio_dir(struct snd_ice1712 *ice, unsigned int data)
inw(ICEREG1724(ice, GPIO_DIRECTION)); /* dummy read for pci-posting */
}
+/* get gpio direction 0 = read, 1 = write */
+static unsigned int snd_vt1724_get_gpio_dir(struct snd_ice1712 *ice)
+{
+ return inl(ICEREG1724(ice, GPIO_DIRECTION));
+}
+
/* set the gpio mask (0 = writable) */
static void snd_vt1724_set_gpio_mask(struct snd_ice1712 *ice, unsigned int data)
{
@@ -205,6 +218,17 @@ static void snd_vt1724_set_gpio_mask(struct snd_ice1712 *ice, unsigned int data)
inw(ICEREG1724(ice, GPIO_WRITE_MASK)); /* dummy read for pci-posting */
}
+static unsigned int snd_vt1724_get_gpio_mask(struct snd_ice1712 *ice)
+{
+ unsigned int mask;
+ if (!ice->vt1720)
+ mask = (unsigned int)inb(ICEREG1724(ice, GPIO_WRITE_MASK_22));
+ else
+ mask = 0;
+ mask = (mask << 16) | inw(ICEREG1724(ice, GPIO_WRITE_MASK));
+ return mask;
+}
+
static void snd_vt1724_set_gpio_data(struct snd_ice1712 *ice, unsigned int data)
{
outw(data, ICEREG1724(ice, GPIO_DATA));
@@ -651,16 +675,22 @@ static int snd_vt1724_set_pro_rate(struct snd_ice1712 *ice, unsigned int rate,
return ((rate == ice->cur_rate) && !force) ? 0 : -EBUSY;
}
if (!force && is_pro_rate_locked(ice)) {
+ /* comparing required and current rate - makes sense for
+ * internal clock only */
spin_unlock_irqrestore(&ice->reg_lock, flags);
return (rate == ice->cur_rate) ? 0 : -EBUSY;
}
- old_rate = ice->get_rate(ice);
- if (force || (old_rate != rate))
- ice->set_rate(ice, rate);
- else if (rate == ice->cur_rate) {
- spin_unlock_irqrestore(&ice->reg_lock, flags);
- return 0;
+ if (force || !ice->is_spdif_master(ice)) {
+ /* force means the rate was switched by ucontrol, otherwise
+ * setting clock rate for internal clock mode */
+ old_rate = ice->get_rate(ice);
+ if (force || (old_rate != rate))
+ ice->set_rate(ice, rate);
+ else if (rate == ice->cur_rate) {
+ spin_unlock_irqrestore(&ice->reg_lock, flags);
+ return 0;
+ }
}
ice->cur_rate = rate;
@@ -1016,6 +1046,8 @@ static int snd_vt1724_playback_pro_open(struct snd_pcm_substream *substream)
VT1724_BUFFER_ALIGN);
snd_pcm_hw_constraint_step(runtime, 0, SNDRV_PCM_HW_PARAM_BUFFER_BYTES,
VT1724_BUFFER_ALIGN);
+ if (ice->pro_open)
+ ice->pro_open(ice, substream);
return 0;
}
@@ -1034,6 +1066,8 @@ static int snd_vt1724_capture_pro_open(struct snd_pcm_substream *substream)
VT1724_BUFFER_ALIGN);
snd_pcm_hw_constraint_step(runtime, 0, SNDRV_PCM_HW_PARAM_BUFFER_BYTES,
VT1724_BUFFER_ALIGN);
+ if (ice->pro_open)
+ ice->pro_open(ice, substream);
return 0;
}
@@ -1787,15 +1821,21 @@ static int snd_vt1724_pro_internal_clock_info(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_info *uinfo)
{
struct snd_ice1712 *ice = snd_kcontrol_chip(kcontrol);
-
+ int hw_rates_count = ice->hw_rates->count;
uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED;
uinfo->count = 1;
- uinfo->value.enumerated.items = ice->hw_rates->count + 1;
+
+ uinfo->value.enumerated.items = hw_rates_count + ice->ext_clock_count;
+ /* upper limit - keep at top */
if (uinfo->value.enumerated.item >= uinfo->value.enumerated.items)
uinfo->value.enumerated.item = uinfo->value.enumerated.items - 1;
- if (uinfo->value.enumerated.item == uinfo->value.enumerated.items - 1)
- strcpy(uinfo->value.enumerated.name, "IEC958 Input");
+ if (uinfo->value.enumerated.item >= hw_rates_count)
+ /* ext_clock items */
+ strcpy(uinfo->value.enumerated.name,
+ ice->ext_clock_names[
+ uinfo->value.enumerated.item - hw_rates_count]);
else
+ /* int clock items */
sprintf(uinfo->value.enumerated.name, "%d",
ice->hw_rates->list[uinfo->value.enumerated.item]);
return 0;
@@ -1809,7 +1849,8 @@ static int snd_vt1724_pro_internal_clock_get(struct snd_kcontrol *kcontrol,
spin_lock_irq(&ice->reg_lock);
if (ice->is_spdif_master(ice)) {
- ucontrol->value.enumerated.item[0] = ice->hw_rates->count;
+ ucontrol->value.enumerated.item[0] = ice->hw_rates->count +
+ ice->get_spdif_master_type(ice);
} else {
rate = ice->get_rate(ice);
ucontrol->value.enumerated.item[0] = 0;
@@ -1824,8 +1865,14 @@ static int snd_vt1724_pro_internal_clock_get(struct snd_kcontrol *kcontrol,
return 0;
}
+static int stdclock_get_spdif_master_type(struct snd_ice1712 *ice)
+{
+ /* standard external clock - only single type - SPDIF IN */
+ return 0;
+}
+
/* setting clock to external - SPDIF */
-static void stdclock_set_spdif_clock(struct snd_ice1712 *ice)
+static int stdclock_set_spdif_clock(struct snd_ice1712 *ice, int type)
{
unsigned char oval;
unsigned char i2s_oval;
@@ -1834,27 +1881,30 @@ static void stdclock_set_spdif_clock(struct snd_ice1712 *ice)
/* setting 256fs */
i2s_oval = inb(ICEMT1724(ice, I2S_FORMAT));
outb(i2s_oval & ~VT1724_MT_I2S_MCLK_128X, ICEMT1724(ice, I2S_FORMAT));
+ return 0;
}
+
static int snd_vt1724_pro_internal_clock_put(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
struct snd_ice1712 *ice = snd_kcontrol_chip(kcontrol);
unsigned int old_rate, new_rate;
unsigned int item = ucontrol->value.enumerated.item[0];
- unsigned int spdif = ice->hw_rates->count;
+ unsigned int first_ext_clock = ice->hw_rates->count;
- if (item > spdif)
+ if (item > first_ext_clock + ice->ext_clock_count - 1)
return -EINVAL;
+ /* if rate = 0 => external clock */
spin_lock_irq(&ice->reg_lock);
if (ice->is_spdif_master(ice))
old_rate = 0;
else
old_rate = ice->get_rate(ice);
- if (item == spdif) {
- /* switching to external clock via SPDIF */
- ice->set_spdif_clock(ice);
+ if (item >= first_ext_clock) {
+ /* switching to external clock */
+ ice->set_spdif_clock(ice, item - first_ext_clock);
new_rate = 0;
} else {
/* internal on-card clock */
@@ -1866,7 +1916,7 @@ static int snd_vt1724_pro_internal_clock_put(struct snd_kcontrol *kcontrol,
}
spin_unlock_irq(&ice->reg_lock);
- /* the first reset to the SPDIF master mode? */
+ /* the first switch to the ext. clock mode? */
if (old_rate != new_rate && !new_rate) {
/* notify akm chips as well */
unsigned int i;
@@ -2136,6 +2186,7 @@ static struct snd_ice1712_card_info *card_tables[] __devinitdata = {
snd_vt1724_phase_cards,
snd_vt1724_wtm_cards,
snd_vt1724_se_cards,
+ snd_vt1724_qtet_cards,
NULL,
};
@@ -2434,7 +2485,9 @@ static int __devinit snd_vt1724_create(struct snd_card *card,
mutex_init(&ice->open_mutex);
mutex_init(&ice->i2c_mutex);
ice->gpio.set_mask = snd_vt1724_set_gpio_mask;
+ ice->gpio.get_mask = snd_vt1724_get_gpio_mask;
ice->gpio.set_dir = snd_vt1724_set_gpio_dir;
+ ice->gpio.get_dir = snd_vt1724_get_gpio_dir;
ice->gpio.set_data = snd_vt1724_set_gpio_data;
ice->gpio.get_data = snd_vt1724_get_gpio_data;
ice->card = card;
@@ -2522,6 +2575,9 @@ static int __devinit snd_vt1724_probe(struct pci_dev *pci,
return err;
}
+ /* field init before calling chip_init */
+ ice->ext_clock_count = 0;
+
for (tbl = card_tables; *tbl; tbl++) {
for (c = *tbl; c->subvendor; c++) {
if (c->subvendor == ice->eeprom.subvendor) {
@@ -2560,6 +2616,13 @@ __found:
ice->set_mclk = stdclock_set_mclk;
if (!ice->set_spdif_clock)
ice->set_spdif_clock = stdclock_set_spdif_clock;
+ if (!ice->get_spdif_master_type)
+ ice->get_spdif_master_type = stdclock_get_spdif_master_type;
+ if (!ice->ext_clock_names)
+ ice->ext_clock_names = ext_clock_names;
+ if (!ice->ext_clock_count)
+ ice->ext_clock_count = ARRAY_SIZE(ext_clock_names);
+
if (!ice->hw_rates)
set_std_hw_rates(ice);
@@ -2719,7 +2782,7 @@ static int snd_vt1724_resume(struct pci_dev *pci)
if (ice->pm_saved_is_spdif_master) {
/* switching to external clock via SPDIF */
- ice->set_spdif_clock(ice);
+ ice->set_spdif_clock(ice, 0);
} else {
/* internal on-card clock */
snd_vt1724_set_pro_rate(ice, ice->pro_rate_default, 1);
diff --git a/sound/pci/ice1712/juli.c b/sound/pci/ice1712/juli.c
index fd948bfd9aef..0c9413d5341b 100644
--- a/sound/pci/ice1712/juli.c
+++ b/sound/pci/ice1712/juli.c
@@ -412,25 +412,6 @@ static struct snd_kcontrol_new juli_mute_controls[] __devinitdata = {
},
};
-
-static void ak4358_proc_regs_read(struct snd_info_entry *entry,
- struct snd_info_buffer *buffer)
-{
- struct snd_ice1712 *ice = (struct snd_ice1712 *)entry->private_data;
- int reg, val;
- for (reg = 0; reg <= 0xf; reg++) {
- val = snd_akm4xxx_get(ice->akm, 0, reg);
- snd_iprintf(buffer, "0x%02x = 0x%02x\n", reg, val);
- }
-}
-
-static void ak4358_proc_init(struct snd_ice1712 *ice)
-{
- struct snd_info_entry *entry;
- if (!snd_card_proc_new(ice->card, "ak4358_codec", &entry))
- snd_info_set_text_ops(entry, ice, ak4358_proc_regs_read);
-}
-
static char *slave_vols[] __devinitdata = {
PCM_VOLUME,
MONITOR_AN_IN_VOLUME,
@@ -496,14 +477,37 @@ static int __devinit juli_add_controls(struct snd_ice1712 *ice)
/* only capture SPDIF over AK4114 */
err = snd_ak4114_build(spec->ak4114, NULL,
ice->pcm->streams[SNDRV_PCM_STREAM_CAPTURE].substream);
-
- ak4358_proc_init(ice);
if (err < 0)
return err;
return 0;
}
/*
+ * suspend/resume
+ * */
+
+#ifdef CONFIG_PM
+static int juli_resume(struct snd_ice1712 *ice)
+{
+ struct snd_akm4xxx *ak = ice->akm;
+ struct juli_spec *spec = ice->spec;
+ /* akm4358 un-reset, un-mute */
+ snd_akm4xxx_reset(ak, 0);
+ /* reinit ak4114 */
+ snd_ak4114_reinit(spec->ak4114);
+ return 0;
+}
+
+static int juli_suspend(struct snd_ice1712 *ice)
+{
+ struct snd_akm4xxx *ak = ice->akm;
+ /* akm4358 reset and soft-mute */
+ snd_akm4xxx_reset(ak, 1);
+ return 0;
+}
+#endif
+
+/*
* initialize the chip
*/
@@ -550,13 +554,14 @@ static inline unsigned char juli_set_mclk(struct snd_ice1712 *ice,
}
/* setting clock to external - SPDIF */
-static void juli_set_spdif_clock(struct snd_ice1712 *ice)
+static int juli_set_spdif_clock(struct snd_ice1712 *ice, int type)
{
unsigned int old;
old = ice->gpio.get_data(ice);
/* external clock (= 0), multiply 1x, 48kHz */
ice->gpio.set_data(ice, (old & ~GPIO_RATE_MASK) | GPIO_MULTI_1X |
GPIO_FREQ_48KHZ);
+ return 0;
}
/* Called when ak4114 detects change in the input SPDIF stream */
@@ -646,6 +651,13 @@ static int __devinit juli_init(struct snd_ice1712 *ice)
ice->set_spdif_clock = juli_set_spdif_clock;
ice->spdif.ops.open = juli_spdif_in_open;
+
+#ifdef CONFIG_PM
+ ice->pm_resume = juli_resume;
+ ice->pm_suspend = juli_suspend;
+ ice->pm_suspend_enabled = 1;
+#endif
+
return 0;
}
diff --git a/sound/pci/ice1712/quartet.c b/sound/pci/ice1712/quartet.c
new file mode 100644
index 000000000000..1948632787e6
--- /dev/null
+++ b/sound/pci/ice1712/quartet.c
@@ -0,0 +1,1130 @@
+/*
+ * ALSA driver for ICEnsemble VT1724 (Envy24HT)
+ *
+ * Lowlevel functions for Infrasonic Quartet
+ *
+ * Copyright (c) 2009 Pavel Hofman <pavel.hofman@ivitera.com>
+ *
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ */
+
+#include <asm/io.h>
+#include <linux/delay.h>
+#include <linux/interrupt.h>
+#include <linux/init.h>
+#include <linux/slab.h>
+#include <sound/core.h>
+#include <sound/tlv.h>
+#include <sound/info.h>
+
+#include "ice1712.h"
+#include "envy24ht.h"
+#include <sound/ak4113.h>
+#include "quartet.h"
+
+struct qtet_spec {
+ struct ak4113 *ak4113;
+ unsigned int scr; /* system control register */
+ unsigned int mcr; /* monitoring control register */
+ unsigned int cpld; /* cpld register */
+};
+
+struct qtet_kcontrol_private {
+ unsigned int bit;
+ void (*set_register)(struct snd_ice1712 *ice, unsigned int val);
+ unsigned int (*get_register)(struct snd_ice1712 *ice);
+ unsigned char *texts[2];
+};
+
+enum {
+ IN12_SEL = 0,
+ IN34_SEL,
+ AIN34_SEL,
+ COAX_OUT,
+ IN12_MON12,
+ IN12_MON34,
+ IN34_MON12,
+ IN34_MON34,
+ OUT12_MON34,
+ OUT34_MON12,
+};
+
+static char *ext_clock_names[3] = {"IEC958 In", "Word Clock 1xFS",
+ "Word Clock 256xFS"};
+
+/* chip address on I2C bus */
+#define AK4113_ADDR 0x26 /* S/PDIF receiver */
+
+/* chip address on SPI bus */
+#define AK4620_ADDR 0x02 /* ADC/DAC */
+
+
+/*
+ * GPIO pins
+ */
+
+/* GPIO0 - O - DATA0, def. 0 */
+#define GPIO_D0 (1<<0)
+/* GPIO1 - I/O - DATA1, Jack Detect Input0 (0:present, 1:missing), def. 1 */
+#define GPIO_D1_JACKDTC0 (1<<1)
+/* GPIO2 - I/O - DATA2, Jack Detect Input1 (0:present, 1:missing), def. 1 */
+#define GPIO_D2_JACKDTC1 (1<<2)
+/* GPIO3 - I/O - DATA3, def. 1 */
+#define GPIO_D3 (1<<3)
+/* GPIO4 - I/O - DATA4, SPI CDTO, def. 1 */
+#define GPIO_D4_SPI_CDTO (1<<4)
+/* GPIO5 - I/O - DATA5, SPI CCLK, def. 1 */
+#define GPIO_D5_SPI_CCLK (1<<5)
+/* GPIO6 - I/O - DATA6, Cable Detect Input (0:detected, 1:not detected */
+#define GPIO_D6_CD (1<<6)
+/* GPIO7 - I/O - DATA7, Device Detect Input (0:detected, 1:not detected */
+#define GPIO_D7_DD (1<<7)
+/* GPIO8 - O - CPLD Chip Select, def. 1 */
+#define GPIO_CPLD_CSN (1<<8)
+/* GPIO9 - O - CPLD register read/write (0:write, 1:read), def. 0 */
+#define GPIO_CPLD_RW (1<<9)
+/* GPIO10 - O - SPI Chip Select for CODEC#0, def. 1 */
+#define GPIO_SPI_CSN0 (1<<10)
+/* GPIO11 - O - SPI Chip Select for CODEC#1, def. 1 */
+#define GPIO_SPI_CSN1 (1<<11)
+/* GPIO12 - O - Ex. Register Output Enable (0:enable, 1:disable), def. 1,
+ * init 0 */
+#define GPIO_EX_GPIOE (1<<12)
+/* GPIO13 - O - Ex. Register0 Chip Select for System Control Register,
+ * def. 1 */
+#define GPIO_SCR (1<<13)
+/* GPIO14 - O - Ex. Register1 Chip Select for Monitor Control Register,
+ * def. 1 */
+#define GPIO_MCR (1<<14)
+
+#define GPIO_SPI_ALL (GPIO_D4_SPI_CDTO | GPIO_D5_SPI_CCLK |\
+ GPIO_SPI_CSN0 | GPIO_SPI_CSN1)
+
+#define GPIO_DATA_MASK (GPIO_D0 | GPIO_D1_JACKDTC0 | \
+ GPIO_D2_JACKDTC1 | GPIO_D3 | \
+ GPIO_D4_SPI_CDTO | GPIO_D5_SPI_CCLK | \
+ GPIO_D6_CD | GPIO_D7_DD)
+
+/* System Control Register GPIO_SCR data bits */
+/* Mic/Line select relay (0:line, 1:mic) */
+#define SCR_RELAY GPIO_D0
+/* Phantom power drive control (0:5V, 1:48V) */
+#define SCR_PHP_V GPIO_D1_JACKDTC0
+/* H/W mute control (0:Normal, 1:Mute) */
+#define SCR_MUTE GPIO_D2_JACKDTC1
+/* Phantom power control (0:Phantom on, 1:off) */
+#define SCR_PHP GPIO_D3
+/* Analog input 1/2 Source Select */
+#define SCR_AIN12_SEL0 GPIO_D4_SPI_CDTO
+#define SCR_AIN12_SEL1 GPIO_D5_SPI_CCLK
+/* Analog input 3/4 Source Select (0:line, 1:hi-z) */
+#define SCR_AIN34_SEL GPIO_D6_CD
+/* Codec Power Down (0:power down, 1:normal) */
+#define SCR_CODEC_PDN GPIO_D7_DD
+
+#define SCR_AIN12_LINE (0)
+#define SCR_AIN12_MIC (SCR_AIN12_SEL0)
+#define SCR_AIN12_LOWCUT (SCR_AIN12_SEL1 | SCR_AIN12_SEL0)
+
+/* Monitor Control Register GPIO_MCR data bits */
+/* Input 1/2 to Monitor 1/2 (0:off, 1:on) */
+#define MCR_IN12_MON12 GPIO_D0
+/* Input 1/2 to Monitor 3/4 (0:off, 1:on) */
+#define MCR_IN12_MON34 GPIO_D1_JACKDTC0
+/* Input 3/4 to Monitor 1/2 (0:off, 1:on) */
+#define MCR_IN34_MON12 GPIO_D2_JACKDTC1
+/* Input 3/4 to Monitor 3/4 (0:off, 1:on) */
+#define MCR_IN34_MON34 GPIO_D3
+/* Output to Monitor 1/2 (0:off, 1:on) */
+#define MCR_OUT34_MON12 GPIO_D4_SPI_CDTO
+/* Output to Monitor 3/4 (0:off, 1:on) */
+#define MCR_OUT12_MON34 GPIO_D5_SPI_CCLK
+
+/* CPLD Register DATA bits */
+/* Clock Rate Select */
+#define CPLD_CKS0 GPIO_D0
+#define CPLD_CKS1 GPIO_D1_JACKDTC0
+#define CPLD_CKS2 GPIO_D2_JACKDTC1
+/* Sync Source Select (0:Internal, 1:External) */
+#define CPLD_SYNC_SEL GPIO_D3
+/* Word Clock FS Select (0:FS, 1:256FS) */
+#define CPLD_WORD_SEL GPIO_D4_SPI_CDTO
+/* Coaxial Output Source (IS-Link) (0:SPDIF, 1:I2S) */
+#define CPLD_COAX_OUT GPIO_D5_SPI_CCLK
+/* Input 1/2 Source Select (0:Analog12, 1:An34) */
+#define CPLD_IN12_SEL GPIO_D6_CD
+/* Input 3/4 Source Select (0:Analog34, 1:Digital In) */
+#define CPLD_IN34_SEL GPIO_D7_DD
+
+/* internal clock (CPLD_SYNC_SEL = 0) options */
+#define CPLD_CKS_44100HZ (0)
+#define CPLD_CKS_48000HZ (CPLD_CKS0)
+#define CPLD_CKS_88200HZ (CPLD_CKS1)
+#define CPLD_CKS_96000HZ (CPLD_CKS1 | CPLD_CKS0)
+#define CPLD_CKS_176400HZ (CPLD_CKS2)
+#define CPLD_CKS_192000HZ (CPLD_CKS2 | CPLD_CKS0)
+
+#define CPLD_CKS_MASK (CPLD_CKS0 | CPLD_CKS1 | CPLD_CKS2)
+
+/* external clock (CPLD_SYNC_SEL = 1) options */
+/* external clock - SPDIF */
+#define CPLD_EXT_SPDIF (0 | CPLD_SYNC_SEL)
+/* external clock - WordClock 1xfs */
+#define CPLD_EXT_WORDCLOCK_1FS (CPLD_CKS1 | CPLD_SYNC_SEL)
+/* external clock - WordClock 256xfs */
+#define CPLD_EXT_WORDCLOCK_256FS (CPLD_CKS1 | CPLD_WORD_SEL |\
+ CPLD_SYNC_SEL)
+
+#define EXT_SPDIF_TYPE 0
+#define EXT_WORDCLOCK_1FS_TYPE 1
+#define EXT_WORDCLOCK_256FS_TYPE 2
+
+#define AK4620_DFS0 (1<<0)
+#define AK4620_DFS1 (1<<1)
+#define AK4620_CKS0 (1<<2)
+#define AK4620_CKS1 (1<<3)
+/* Clock and Format Control register */
+#define AK4620_DFS_REG 0x02
+
+/* Deem and Volume Control register */
+#define AK4620_DEEMVOL_REG 0x03
+#define AK4620_SMUTE (1<<7)
+
+/*
+ * Conversion from int value to its binary form. Used for debugging.
+ * The output buffer must be allocated prior to calling the function.
+ */
+static char *get_binary(char *buffer, int value)
+{
+ int i, j, pos;
+ pos = 0;
+ for (i = 0; i < 4; ++i) {
+ for (j = 0; j < 8; ++j) {
+ if (value & (1 << (31-(i*8 + j))))
+ buffer[pos] = '1';
+ else
+ buffer[pos] = '0';
+ pos++;
+ }
+ if (i < 3) {
+ buffer[pos] = ' ';
+ pos++;
+ }
+ }
+ buffer[pos] = '\0';
+ return buffer;
+}
+
+/*
+ * Initial setup of the conversion array GPIO <-> rate
+ */
+static unsigned int qtet_rates[] = {
+ 44100, 48000, 88200,
+ 96000, 176400, 192000,
+};
+
+static unsigned int cks_vals[] = {
+ CPLD_CKS_44100HZ, CPLD_CKS_48000HZ, CPLD_CKS_88200HZ,
+ CPLD_CKS_96000HZ, CPLD_CKS_176400HZ, CPLD_CKS_192000HZ,
+};
+
+static struct snd_pcm_hw_constraint_list qtet_rates_info = {
+ .count = ARRAY_SIZE(qtet_rates),
+ .list = qtet_rates,
+ .mask = 0,
+};
+
+static void qtet_ak4113_write(void *private_data, unsigned char reg,
+ unsigned char val)
+{
+ snd_vt1724_write_i2c((struct snd_ice1712 *)private_data, AK4113_ADDR,
+ reg, val);
+}
+
+static unsigned char qtet_ak4113_read(void *private_data, unsigned char reg)
+{
+ return snd_vt1724_read_i2c((struct snd_ice1712 *)private_data,
+ AK4113_ADDR, reg);
+}
+
+
+/*
+ * AK4620 section
+ */
+
+/*
+ * Write data to addr register of ak4620
+ */
+static void qtet_akm_write(struct snd_akm4xxx *ak, int chip,
+ unsigned char addr, unsigned char data)
+{
+ unsigned int tmp, orig_dir;
+ int idx;
+ unsigned int addrdata;
+ struct snd_ice1712 *ice = ak->private_data[0];
+
+ if (snd_BUG_ON(chip < 0 || chip >= 4))
+ return;
+ /*printk(KERN_DEBUG "Writing to AK4620: chip=%d, addr=0x%x,
+ data=0x%x\n", chip, addr, data);*/
+ orig_dir = ice->gpio.get_dir(ice);
+ ice->gpio.set_dir(ice, orig_dir | GPIO_SPI_ALL);
+ /* set mask - only SPI bits */
+ ice->gpio.set_mask(ice, ~GPIO_SPI_ALL);
+
+ tmp = ice->gpio.get_data(ice);
+ /* high all */
+ tmp |= GPIO_SPI_ALL;
+ ice->gpio.set_data(ice, tmp);
+ udelay(100);
+ /* drop chip select */
+ if (chip)
+ /* CODEC 1 */
+ tmp &= ~GPIO_SPI_CSN1;
+ else
+ tmp &= ~GPIO_SPI_CSN0;
+ ice->gpio.set_data(ice, tmp);
+ udelay(100);
+
+ /* build I2C address + data byte */
+ addrdata = (AK4620_ADDR << 6) | 0x20 | (addr & 0x1f);
+ addrdata = (addrdata << 8) | data;
+ for (idx = 15; idx >= 0; idx--) {
+ /* drop clock */
+ tmp &= ~GPIO_D5_SPI_CCLK;
+ ice->gpio.set_data(ice, tmp);
+ udelay(100);
+ /* set data */
+ if (addrdata & (1 << idx))
+ tmp |= GPIO_D4_SPI_CDTO;
+ else
+ tmp &= ~GPIO_D4_SPI_CDTO;
+ ice->gpio.set_data(ice, tmp);
+ udelay(100);
+ /* raise clock */
+ tmp |= GPIO_D5_SPI_CCLK;
+ ice->gpio.set_data(ice, tmp);
+ udelay(100);
+ }
+ /* all back to 1 */
+ tmp |= GPIO_SPI_ALL;
+ ice->gpio.set_data(ice, tmp);
+ udelay(100);
+
+ /* return all gpios to non-writable */
+ ice->gpio.set_mask(ice, 0xffffff);
+ /* restore GPIOs direction */
+ ice->gpio.set_dir(ice, orig_dir);
+}
+
+static void qtet_akm_set_regs(struct snd_akm4xxx *ak, unsigned char addr,
+ unsigned char mask, unsigned char value)
+{
+ unsigned char tmp;
+ int chip;
+ for (chip = 0; chip < ak->num_chips; chip++) {
+ tmp = snd_akm4xxx_get(ak, chip, addr);
+ /* clear the bits */
+ tmp &= ~mask;
+ /* set the new bits */
+ tmp |= value;
+ snd_akm4xxx_write(ak, chip, addr, tmp);
+ }
+}
+
+/*
+ * change the rate of AK4620
+ */
+static void qtet_akm_set_rate_val(struct snd_akm4xxx *ak, unsigned int rate)
+{
+ unsigned char ak4620_dfs;
+
+ if (rate == 0) /* no hint - S/PDIF input is master or the new spdif
+ input rate undetected, simply return */
+ return;
+
+ /* adjust DFS on codecs - see datasheet */
+ if (rate > 108000)
+ ak4620_dfs = AK4620_DFS1 | AK4620_CKS1;
+ else if (rate > 54000)
+ ak4620_dfs = AK4620_DFS0 | AK4620_CKS0;
+ else
+ ak4620_dfs = 0;
+
+ /* set new value */
+ qtet_akm_set_regs(ak, AK4620_DFS_REG, AK4620_DFS0 | AK4620_DFS1 |
+ AK4620_CKS0 | AK4620_CKS1, ak4620_dfs);
+}
+
+#define AK_CONTROL(xname, xch) { .name = xname, .num_channels = xch }
+
+#define PCM_12_PLAYBACK_VOLUME "PCM 1/2 Playback Volume"
+#define PCM_34_PLAYBACK_VOLUME "PCM 3/4 Playback Volume"
+#define PCM_12_CAPTURE_VOLUME "PCM 1/2 Capture Volume"
+#define PCM_34_CAPTURE_VOLUME "PCM 3/4 Capture Volume"
+
+static const struct snd_akm4xxx_dac_channel qtet_dac[] = {
+ AK_CONTROL(PCM_12_PLAYBACK_VOLUME, 2),
+ AK_CONTROL(PCM_34_PLAYBACK_VOLUME, 2),
+};
+
+static const struct snd_akm4xxx_adc_channel qtet_adc[] = {
+ AK_CONTROL(PCM_12_CAPTURE_VOLUME, 2),
+ AK_CONTROL(PCM_34_CAPTURE_VOLUME, 2),
+};
+
+static struct snd_akm4xxx akm_qtet_dac __devinitdata = {
+ .type = SND_AK4620,
+ .num_dacs = 4, /* DAC1 - Output 12
+ */
+ .num_adcs = 4, /* ADC1 - Input 12
+ */
+ .ops = {
+ .write = qtet_akm_write,
+ .set_rate_val = qtet_akm_set_rate_val,
+ },
+ .dac_info = qtet_dac,
+ .adc_info = qtet_adc,
+};
+
+/* Communication routines with the CPLD */
+
+
+/* Writes data to external register reg, both reg and data are
+ * GPIO representations */
+static void reg_write(struct snd_ice1712 *ice, unsigned int reg,
+ unsigned int data)
+{
+ unsigned int tmp;
+
+ mutex_lock(&ice->gpio_mutex);
+ /* set direction of used GPIOs*/
+ /* all outputs */
+ tmp = 0x00ffff;
+ ice->gpio.set_dir(ice, tmp);
+ /* mask - writable bits */
+ ice->gpio.set_mask(ice, ~(tmp));
+ /* write the data */
+ tmp = ice->gpio.get_data(ice);
+ tmp &= ~GPIO_DATA_MASK;
+ tmp |= data;
+ ice->gpio.set_data(ice, tmp);
+ udelay(100);
+ /* drop output enable */
+ tmp &= ~GPIO_EX_GPIOE;
+ ice->gpio.set_data(ice, tmp);
+ udelay(100);
+ /* drop the register gpio */
+ tmp &= ~reg;
+ ice->gpio.set_data(ice, tmp);
+ udelay(100);
+ /* raise the register GPIO */
+ tmp |= reg;
+ ice->gpio.set_data(ice, tmp);
+ udelay(100);
+
+ /* raise all data gpios */
+ tmp |= GPIO_DATA_MASK;
+ ice->gpio.set_data(ice, tmp);
+ /* mask - immutable bits */
+ ice->gpio.set_mask(ice, 0xffffff);
+ /* outputs only 8-15 */
+ ice->gpio.set_dir(ice, 0x00ff00);
+ mutex_unlock(&ice->gpio_mutex);
+}
+
+static unsigned int get_scr(struct snd_ice1712 *ice)
+{
+ struct qtet_spec *spec = ice->spec;
+ return spec->scr;
+}
+
+static unsigned int get_mcr(struct snd_ice1712 *ice)
+{
+ struct qtet_spec *spec = ice->spec;
+ return spec->mcr;
+}
+
+static unsigned int get_cpld(struct snd_ice1712 *ice)
+{
+ struct qtet_spec *spec = ice->spec;
+ return spec->cpld;
+}
+
+static void set_scr(struct snd_ice1712 *ice, unsigned int val)
+{
+ struct qtet_spec *spec = ice->spec;
+ reg_write(ice, GPIO_SCR, val);
+ spec->scr = val;
+}
+
+static void set_mcr(struct snd_ice1712 *ice, unsigned int val)
+{
+ struct qtet_spec *spec = ice->spec;
+ reg_write(ice, GPIO_MCR, val);
+ spec->mcr = val;
+}
+
+static void set_cpld(struct snd_ice1712 *ice, unsigned int val)
+{
+ struct qtet_spec *spec = ice->spec;
+ reg_write(ice, GPIO_CPLD_CSN, val);
+ spec->cpld = val;
+}
+#ifdef CONFIG_PROC_FS
+static void proc_regs_read(struct snd_info_entry *entry,
+ struct snd_info_buffer *buffer)
+{
+ struct snd_ice1712 *ice = entry->private_data;
+ char bin_buffer[36];
+
+ snd_iprintf(buffer, "SCR: %s\n", get_binary(bin_buffer,
+ get_scr(ice)));
+ snd_iprintf(buffer, "MCR: %s\n", get_binary(bin_buffer,
+ get_mcr(ice)));
+ snd_iprintf(buffer, "CPLD: %s\n", get_binary(bin_buffer,
+ get_cpld(ice)));
+}
+
+static void proc_init(struct snd_ice1712 *ice)
+{
+ struct snd_info_entry *entry;
+ if (!snd_card_proc_new(ice->card, "quartet", &entry))
+ snd_info_set_text_ops(entry, ice, proc_regs_read);
+}
+#else /* !CONFIG_PROC_FS */
+static void proc_init(struct snd_ice1712 *ice) {}
+#endif
+
+static int qtet_mute_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_ice1712 *ice = snd_kcontrol_chip(kcontrol);
+ unsigned int val;
+ val = get_scr(ice) & SCR_MUTE;
+ ucontrol->value.integer.value[0] = (val) ? 0 : 1;
+ return 0;
+}
+
+static int qtet_mute_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_ice1712 *ice = snd_kcontrol_chip(kcontrol);
+ unsigned int old, new, smute;
+ old = get_scr(ice) & SCR_MUTE;
+ if (ucontrol->value.integer.value[0]) {
+ /* unmute */
+ new = 0;
+ /* un-smuting DAC */
+ smute = 0;
+ } else {
+ /* mute */
+ new = SCR_MUTE;
+ /* smuting DAC */
+ smute = AK4620_SMUTE;
+ }
+ if (old != new) {
+ struct snd_akm4xxx *ak = ice->akm;
+ set_scr(ice, (get_scr(ice) & ~SCR_MUTE) | new);
+ /* set smute */
+ qtet_akm_set_regs(ak, AK4620_DEEMVOL_REG, AK4620_SMUTE, smute);
+ return 1;
+ }
+ /* no change */
+ return 0;
+}
+
+static int qtet_ain12_enum_info(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_info *uinfo)
+{
+ static char *texts[3] = {"Line In 1/2", "Mic", "Mic + Low-cut"};
+ uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED;
+ uinfo->count = 1;
+ uinfo->value.enumerated.items = ARRAY_SIZE(texts);
+
+ 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 qtet_ain12_sw_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_ice1712 *ice = snd_kcontrol_chip(kcontrol);
+ unsigned int val, result;
+ val = get_scr(ice) & (SCR_AIN12_SEL1 | SCR_AIN12_SEL0);
+ switch (val) {
+ case SCR_AIN12_LINE:
+ result = 0;
+ break;
+ case SCR_AIN12_MIC:
+ result = 1;
+ break;
+ case SCR_AIN12_LOWCUT:
+ result = 2;
+ break;
+ default:
+ /* BUG - no other combinations allowed */
+ snd_BUG();
+ result = 0;
+ }
+ ucontrol->value.integer.value[0] = result;
+ return 0;
+}
+
+static int qtet_ain12_sw_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_ice1712 *ice = snd_kcontrol_chip(kcontrol);
+ unsigned int old, new, tmp, masked_old;
+ old = new = get_scr(ice);
+ masked_old = old & (SCR_AIN12_SEL1 | SCR_AIN12_SEL0);
+ tmp = ucontrol->value.integer.value[0];
+ if (tmp == 2)
+ tmp = 3; /* binary 10 is not supported */
+ tmp <<= 4; /* shifting to SCR_AIN12_SEL0 */
+ if (tmp != masked_old) {
+ /* change requested */
+ switch (tmp) {
+ case SCR_AIN12_LINE:
+ new = old & ~(SCR_AIN12_SEL1 | SCR_AIN12_SEL0);
+ set_scr(ice, new);
+ /* turn off relay */
+ new &= ~SCR_RELAY;
+ set_scr(ice, new);
+ break;
+ case SCR_AIN12_MIC:
+ /* turn on relay */
+ new = old | SCR_RELAY;
+ set_scr(ice, new);
+ new = (new & ~SCR_AIN12_SEL1) | SCR_AIN12_SEL0;
+ set_scr(ice, new);
+ break;
+ case SCR_AIN12_LOWCUT:
+ /* turn on relay */
+ new = old | SCR_RELAY;
+ set_scr(ice, new);
+ new |= SCR_AIN12_SEL1 | SCR_AIN12_SEL0;
+ set_scr(ice, new);
+ break;
+ default:
+ snd_BUG();
+ }
+ return 1;
+ }
+ /* no change */
+ return 0;
+}
+
+static int qtet_php_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_ice1712 *ice = snd_kcontrol_chip(kcontrol);
+ unsigned int val;
+ /* if phantom voltage =48V, phantom on */
+ val = get_scr(ice) & SCR_PHP_V;
+ ucontrol->value.integer.value[0] = val ? 1 : 0;
+ return 0;
+}
+
+static int qtet_php_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_ice1712 *ice = snd_kcontrol_chip(kcontrol);
+ unsigned int old, new;
+ old = new = get_scr(ice);
+ if (ucontrol->value.integer.value[0] /* phantom on requested */
+ && (~old & SCR_PHP_V)) /* 0 = voltage 5V */ {
+ /* is off, turn on */
+ /* turn voltage on first, = 1 */
+ new = old | SCR_PHP_V;
+ set_scr(ice, new);
+ /* turn phantom on, = 0 */
+ new &= ~SCR_PHP;
+ set_scr(ice, new);
+ } else if (!ucontrol->value.integer.value[0] && (old & SCR_PHP_V)) {
+ /* phantom off requested and 1 = voltage 48V */
+ /* is on, turn off */
+ /* turn voltage off first, = 0 */
+ new = old & ~SCR_PHP_V;
+ set_scr(ice, new);
+ /* turn phantom off, = 1 */
+ new |= SCR_PHP;
+ set_scr(ice, new);
+ }
+ if (old != new)
+ return 1;
+ /* no change */
+ return 0;
+}
+
+#define PRIV_SW(xid, xbit, xreg) [xid] = {.bit = xbit,\
+ .set_register = set_##xreg,\
+ .get_register = get_##xreg, }
+
+
+#define PRIV_ENUM2(xid, xbit, xreg, xtext1, xtext2) [xid] = {.bit = xbit,\
+ .set_register = set_##xreg,\
+ .get_register = get_##xreg,\
+ .texts = {xtext1, xtext2} }
+
+static struct qtet_kcontrol_private qtet_privates[] = {
+ PRIV_ENUM2(IN12_SEL, CPLD_IN12_SEL, cpld, "An In 1/2", "An In 3/4"),
+ PRIV_ENUM2(IN34_SEL, CPLD_IN34_SEL, cpld, "An In 3/4", "IEC958 In"),
+ PRIV_ENUM2(AIN34_SEL, SCR_AIN34_SEL, scr, "Line In 3/4", "Hi-Z"),
+ PRIV_ENUM2(COAX_OUT, CPLD_COAX_OUT, cpld, "IEC958", "I2S"),
+ PRIV_SW(IN12_MON12, MCR_IN12_MON12, mcr),
+ PRIV_SW(IN12_MON34, MCR_IN12_MON34, mcr),
+ PRIV_SW(IN34_MON12, MCR_IN34_MON12, mcr),
+ PRIV_SW(IN34_MON34, MCR_IN34_MON34, mcr),
+ PRIV_SW(OUT12_MON34, MCR_OUT12_MON34, mcr),
+ PRIV_SW(OUT34_MON12, MCR_OUT34_MON12, mcr),
+};
+
+static int qtet_enum_info(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_info *uinfo)
+{
+ struct qtet_kcontrol_private private =
+ qtet_privates[kcontrol->private_value];
+ uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED;
+ uinfo->count = 1;
+ uinfo->value.enumerated.items = ARRAY_SIZE(private.texts);
+
+ if (uinfo->value.enumerated.item >= uinfo->value.enumerated.items)
+ uinfo->value.enumerated.item =
+ uinfo->value.enumerated.items - 1;
+ strcpy(uinfo->value.enumerated.name,
+ private.texts[uinfo->value.enumerated.item]);
+
+ return 0;
+}
+
+static int qtet_sw_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct qtet_kcontrol_private private =
+ qtet_privates[kcontrol->private_value];
+ struct snd_ice1712 *ice = snd_kcontrol_chip(kcontrol);
+ ucontrol->value.integer.value[0] =
+ (private.get_register(ice) & private.bit) ? 1 : 0;
+ return 0;
+}
+
+static int qtet_sw_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct qtet_kcontrol_private private =
+ qtet_privates[kcontrol->private_value];
+ struct snd_ice1712 *ice = snd_kcontrol_chip(kcontrol);
+ unsigned int old, new;
+ old = private.get_register(ice);
+ if (ucontrol->value.integer.value[0])
+ new = old | private.bit;
+ else
+ new = old & ~private.bit;
+ if (old != new) {
+ private.set_register(ice, new);
+ return 1;
+ }
+ /* no change */
+ return 0;
+}
+
+#define qtet_sw_info snd_ctl_boolean_mono_info
+
+#define QTET_CONTROL(xname, xtype, xpriv) \
+ {.iface = SNDRV_CTL_ELEM_IFACE_MIXER,\
+ .name = xname,\
+ .info = qtet_##xtype##_info,\
+ .get = qtet_sw_get,\
+ .put = qtet_sw_put,\
+ .private_value = xpriv }
+
+static struct snd_kcontrol_new qtet_controls[] __devinitdata = {
+ {
+ .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+ .name = "Master Playback Switch",
+ .info = qtet_sw_info,
+ .get = qtet_mute_get,
+ .put = qtet_mute_put,
+ .private_value = 0
+ },
+ {
+ .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+ .name = "Phantom Power",
+ .info = qtet_sw_info,
+ .get = qtet_php_get,
+ .put = qtet_php_put,
+ .private_value = 0
+ },
+ {
+ .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+ .name = "Analog In 1/2 Capture Switch",
+ .info = qtet_ain12_enum_info,
+ .get = qtet_ain12_sw_get,
+ .put = qtet_ain12_sw_put,
+ .private_value = 0
+ },
+ QTET_CONTROL("Analog In 3/4 Capture Switch", enum, AIN34_SEL),
+ QTET_CONTROL("PCM In 1/2 Capture Switch", enum, IN12_SEL),
+ QTET_CONTROL("PCM In 3/4 Capture Switch", enum, IN34_SEL),
+ QTET_CONTROL("Coax Output Source", enum, COAX_OUT),
+ QTET_CONTROL("Analog In 1/2 to Monitor 1/2", sw, IN12_MON12),
+ QTET_CONTROL("Analog In 1/2 to Monitor 3/4", sw, IN12_MON34),
+ QTET_CONTROL("Analog In 3/4 to Monitor 1/2", sw, IN34_MON12),
+ QTET_CONTROL("Analog In 3/4 to Monitor 3/4", sw, IN34_MON34),
+ QTET_CONTROL("Output 1/2 to Monitor 3/4", sw, OUT12_MON34),
+ QTET_CONTROL("Output 3/4 to Monitor 1/2", sw, OUT34_MON12),
+};
+
+static char *slave_vols[] __devinitdata = {
+ PCM_12_PLAYBACK_VOLUME,
+ PCM_34_PLAYBACK_VOLUME,
+ NULL
+};
+
+static __devinitdata
+DECLARE_TLV_DB_SCALE(qtet_master_db_scale, -6350, 50, 1);
+
+static struct snd_kcontrol __devinit *ctl_find(struct snd_card *card,
+ const char *name)
+{
+ struct snd_ctl_elem_id sid;
+ memset(&sid, 0, sizeof(sid));
+ /* FIXME: strcpy is bad. */
+ strcpy(sid.name, name);
+ sid.iface = SNDRV_CTL_ELEM_IFACE_MIXER;
+ return snd_ctl_find_id(card, &sid);
+}
+
+static void __devinit add_slaves(struct snd_card *card,
+ struct snd_kcontrol *master, char **list)
+{
+ for (; *list; list++) {
+ struct snd_kcontrol *slave = ctl_find(card, *list);
+ if (slave)
+ snd_ctl_add_slave(master, slave);
+ }
+}
+
+static int __devinit qtet_add_controls(struct snd_ice1712 *ice)
+{
+ struct qtet_spec *spec = ice->spec;
+ int err, i;
+ struct snd_kcontrol *vmaster;
+ err = snd_ice1712_akm4xxx_build_controls(ice);
+ if (err < 0)
+ return err;
+ for (i = 0; i < ARRAY_SIZE(qtet_controls); i++) {
+ err = snd_ctl_add(ice->card,
+ snd_ctl_new1(&qtet_controls[i], ice));
+ if (err < 0)
+ return err;
+ }
+
+ /* Create virtual master control */
+ vmaster = snd_ctl_make_virtual_master("Master Playback Volume",
+ qtet_master_db_scale);
+ if (!vmaster)
+ return -ENOMEM;
+ add_slaves(ice->card, vmaster, slave_vols);
+ err = snd_ctl_add(ice->card, vmaster);
+ if (err < 0)
+ return err;
+ /* only capture SPDIF over AK4113 */
+ err = snd_ak4113_build(spec->ak4113,
+ ice->pcm->streams[SNDRV_PCM_STREAM_CAPTURE].substream);
+ if (err < 0)
+ return err;
+ return 0;
+}
+
+static inline int qtet_is_spdif_master(struct snd_ice1712 *ice)
+{
+ /* CPLD_SYNC_SEL: 0 = internal, 1 = external (i.e. spdif master) */
+ return (get_cpld(ice) & CPLD_SYNC_SEL) ? 1 : 0;
+}
+
+static unsigned int qtet_get_rate(struct snd_ice1712 *ice)
+{
+ int i;
+ unsigned char result;
+
+ result = get_cpld(ice) & CPLD_CKS_MASK;
+ for (i = 0; i < ARRAY_SIZE(cks_vals); i++)
+ if (cks_vals[i] == result)
+ return qtet_rates[i];
+ return 0;
+}
+
+static int get_cks_val(int rate)
+{
+ int i;
+ for (i = 0; i < ARRAY_SIZE(qtet_rates); i++)
+ if (qtet_rates[i] == rate)
+ return cks_vals[i];
+ return 0;
+}
+
+/* setting new rate */
+static void qtet_set_rate(struct snd_ice1712 *ice, unsigned int rate)
+{
+ unsigned int new;
+ unsigned char val;
+ /* switching ice1724 to external clock - supplied by ext. circuits */
+ val = inb(ICEMT1724(ice, RATE));
+ outb(val | VT1724_SPDIF_MASTER, ICEMT1724(ice, RATE));
+
+ new = (get_cpld(ice) & ~CPLD_CKS_MASK) | get_cks_val(rate);
+ /* switch to internal clock, drop CPLD_SYNC_SEL */
+ new &= ~CPLD_SYNC_SEL;
+ /* printk(KERN_DEBUG "QT - set_rate: old %x, new %x\n",
+ get_cpld(ice), new); */
+ set_cpld(ice, new);
+}
+
+static inline unsigned char qtet_set_mclk(struct snd_ice1712 *ice,
+ unsigned int rate)
+{
+ /* no change in master clock */
+ return 0;
+}
+
+/* setting clock to external - SPDIF */
+static int qtet_set_spdif_clock(struct snd_ice1712 *ice, int type)
+{
+ unsigned int old, new;
+
+ old = new = get_cpld(ice);
+ new &= ~(CPLD_CKS_MASK | CPLD_WORD_SEL);
+ switch (type) {
+ case EXT_SPDIF_TYPE:
+ new |= CPLD_EXT_SPDIF;
+ break;
+ case EXT_WORDCLOCK_1FS_TYPE:
+ new |= CPLD_EXT_WORDCLOCK_1FS;
+ break;
+ case EXT_WORDCLOCK_256FS_TYPE:
+ new |= CPLD_EXT_WORDCLOCK_256FS;
+ break;
+ default:
+ snd_BUG();
+ }
+ if (old != new) {
+ set_cpld(ice, new);
+ /* changed */
+ return 1;
+ }
+ return 0;
+}
+
+static int qtet_get_spdif_master_type(struct snd_ice1712 *ice)
+{
+ unsigned int val;
+ int result;
+ val = get_cpld(ice);
+ /* checking only rate/clock-related bits */
+ val &= (CPLD_CKS_MASK | CPLD_WORD_SEL | CPLD_SYNC_SEL);
+ if (!(val & CPLD_SYNC_SEL)) {
+ /* switched to internal clock, is not any external type */
+ result = -1;
+ } else {
+ switch (val) {
+ case (CPLD_EXT_SPDIF):
+ result = EXT_SPDIF_TYPE;
+ break;
+ case (CPLD_EXT_WORDCLOCK_1FS):
+ result = EXT_WORDCLOCK_1FS_TYPE;
+ break;
+ case (CPLD_EXT_WORDCLOCK_256FS):
+ result = EXT_WORDCLOCK_256FS_TYPE;
+ break;
+ default:
+ /* undefined combination of external clock setup */
+ snd_BUG();
+ result = 0;
+ }
+ }
+ return result;
+}
+
+/* Called when ak4113 detects change in the input SPDIF stream */
+static void qtet_ak4113_change(struct ak4113 *ak4113, unsigned char c0,
+ unsigned char c1)
+{
+ struct snd_ice1712 *ice = ak4113->change_callback_private;
+ int rate;
+ if ((qtet_get_spdif_master_type(ice) == EXT_SPDIF_TYPE) &&
+ c1) {
+ /* only for SPDIF master mode, rate was changed */
+ rate = snd_ak4113_external_rate(ak4113);
+ /* printk(KERN_DEBUG "ak4113 - input rate changed to %d\n",
+ rate); */
+ qtet_akm_set_rate_val(ice->akm, rate);
+ }
+}
+
+/*
+ * If clock slaved to SPDIF-IN, setting runtime rate
+ * to the detected external rate
+ */
+static void qtet_spdif_in_open(struct snd_ice1712 *ice,
+ struct snd_pcm_substream *substream)
+{
+ struct qtet_spec *spec = ice->spec;
+ struct snd_pcm_runtime *runtime = substream->runtime;
+ int rate;
+
+ if (qtet_get_spdif_master_type(ice) != EXT_SPDIF_TYPE)
+ /* not external SPDIF, no rate limitation */
+ return;
+ /* only external SPDIF can detect incoming sample rate */
+ rate = snd_ak4113_external_rate(spec->ak4113);
+ if (rate >= runtime->hw.rate_min && rate <= runtime->hw.rate_max) {
+ runtime->hw.rate_min = rate;
+ runtime->hw.rate_max = rate;
+ }
+}
+
+/*
+ * initialize the chip
+ */
+static int __devinit qtet_init(struct snd_ice1712 *ice)
+{
+ static const unsigned char ak4113_init_vals[] = {
+ /* AK4113_REG_PWRDN */ AK4113_RST | AK4113_PWN |
+ AK4113_OCKS0 | AK4113_OCKS1,
+ /* AK4113_REQ_FORMAT */ AK4113_DIF_I24I2S | AK4113_VTX |
+ AK4113_DEM_OFF | AK4113_DEAU,
+ /* AK4113_REG_IO0 */ AK4113_OPS2 | AK4113_TXE |
+ AK4113_XTL_24_576M,
+ /* AK4113_REG_IO1 */ AK4113_EFH_1024LRCLK | AK4113_IPS(0),
+ /* AK4113_REG_INT0_MASK */ 0,
+ /* AK4113_REG_INT1_MASK */ 0,
+ /* AK4113_REG_DATDTS */ 0,
+ };
+ int err;
+ struct qtet_spec *spec;
+ struct snd_akm4xxx *ak;
+ unsigned char val;
+
+ /* switching ice1724 to external clock - supplied by ext. circuits */
+ val = inb(ICEMT1724(ice, RATE));
+ outb(val | VT1724_SPDIF_MASTER, ICEMT1724(ice, RATE));
+
+ spec = kzalloc(sizeof(*spec), GFP_KERNEL);
+ if (!spec)
+ return -ENOMEM;
+ /* qtet is clocked by Xilinx array */
+ ice->hw_rates = &qtet_rates_info;
+ ice->is_spdif_master = qtet_is_spdif_master;
+ ice->get_rate = qtet_get_rate;
+ ice->set_rate = qtet_set_rate;
+ ice->set_mclk = qtet_set_mclk;
+ ice->set_spdif_clock = qtet_set_spdif_clock;
+ ice->get_spdif_master_type = qtet_get_spdif_master_type;
+ ice->ext_clock_names = ext_clock_names;
+ ice->ext_clock_count = ARRAY_SIZE(ext_clock_names);
+ /* since Qtet can detect correct SPDIF-in rate, all streams can be
+ * limited to this specific rate */
+ ice->spdif.ops.open = ice->pro_open = qtet_spdif_in_open;
+ ice->spec = spec;
+
+ /* Mute Off */
+ /* SCR Initialize*/
+ /* keep codec power down first */
+ set_scr(ice, SCR_PHP);
+ udelay(1);
+ /* codec power up */
+ set_scr(ice, SCR_PHP | SCR_CODEC_PDN);
+
+ /* MCR Initialize */
+ set_mcr(ice, 0);
+
+ /* CPLD Initialize */
+ set_cpld(ice, 0);
+
+
+ ice->num_total_dacs = 2;
+ ice->num_total_adcs = 2;
+
+ ice->akm = kcalloc(2, sizeof(struct snd_akm4xxx), GFP_KERNEL);
+ ak = ice->akm;
+ if (!ak)
+ return -ENOMEM;
+ /* only one codec with two chips */
+ ice->akm_codecs = 1;
+ err = snd_ice1712_akm4xxx_init(ak, &akm_qtet_dac, NULL, ice);
+ if (err < 0)
+ return err;
+ err = snd_ak4113_create(ice->card,
+ qtet_ak4113_read,
+ qtet_ak4113_write,
+ ak4113_init_vals,
+ ice, &spec->ak4113);
+ if (err < 0)
+ return err;
+ /* callback for codecs rate setting */
+ spec->ak4113->change_callback = qtet_ak4113_change;
+ spec->ak4113->change_callback_private = ice;
+ /* AK41143 in Quartet can detect external rate correctly
+ * (i.e. check_flags = 0) */
+ spec->ak4113->check_flags = 0;
+
+ proc_init(ice);
+
+ qtet_set_rate(ice, 44100);
+ return 0;
+}
+
+static unsigned char qtet_eeprom[] __devinitdata = {
+ [ICE_EEP2_SYSCONF] = 0x28, /* clock 256(24MHz), mpu401, 1xADC,
+ 1xDACs, SPDIF in */
+ [ICE_EEP2_ACLINK] = 0x80, /* I2S */
+ [ICE_EEP2_I2S] = 0x78, /* 96k, 24bit, 192k */
+ [ICE_EEP2_SPDIF] = 0xc3, /* out-en, out-int, in, out-ext */
+ [ICE_EEP2_GPIO_DIR] = 0x00, /* 0-7 inputs, switched to output
+ only during output operations */
+ [ICE_EEP2_GPIO_DIR1] = 0xff, /* 8-15 outputs */
+ [ICE_EEP2_GPIO_DIR2] = 0x00,
+ [ICE_EEP2_GPIO_MASK] = 0xff, /* changed only for OUT operations */
+ [ICE_EEP2_GPIO_MASK1] = 0x00,
+ [ICE_EEP2_GPIO_MASK2] = 0xff,
+
+ [ICE_EEP2_GPIO_STATE] = 0x00, /* inputs */
+ [ICE_EEP2_GPIO_STATE1] = 0x7d, /* all 1, but GPIO_CPLD_RW
+ and GPIO15 always zero */
+ [ICE_EEP2_GPIO_STATE2] = 0x00, /* inputs */
+};
+
+/* entry point */
+struct snd_ice1712_card_info snd_vt1724_qtet_cards[] __devinitdata = {
+ {
+ .subvendor = VT1724_SUBDEVICE_QTET,
+ .name = "Infrasonic Quartet",
+ .model = "quartet",
+ .chip_init = qtet_init,
+ .build_controls = qtet_add_controls,
+ .eeprom_size = sizeof(qtet_eeprom),
+ .eeprom_data = qtet_eeprom,
+ },
+ { } /* terminator */
+};
diff --git a/sound/pci/ice1712/quartet.h b/sound/pci/ice1712/quartet.h
new file mode 100644
index 000000000000..80809b72439a
--- /dev/null
+++ b/sound/pci/ice1712/quartet.h
@@ -0,0 +1,10 @@
+#ifndef __SOUND_QTET_H
+#define __SOUND_QTET_H
+
+#define QTET_DEVICE_DESC "{Infrasonic,Quartet},"
+
+#define VT1724_SUBDEVICE_QTET 0x30305349 /* Infrasonic Quartet */
+
+extern struct snd_ice1712_card_info snd_vt1724_qtet_cards[];
+
+#endif /* __SOUND_QTET_H */
diff --git a/sound/pci/intel8x0.c b/sound/pci/intel8x0.c
index aac20fb4aad2..b990143636f1 100644
--- a/sound/pci/intel8x0.c
+++ b/sound/pci/intel8x0.c
@@ -2063,6 +2063,12 @@ static struct ac97_quirk ac97_quirks[] __devinitdata = {
.type = AC97_TUNE_HP_ONLY
},
{
+ .subvendor = 0x161f,
+ .subdevice = 0x203a,
+ .name = "Gateway 4525GZ", /* AD1981B */
+ .type = AC97_TUNE_INV_EAPD
+ },
+ {
.subvendor = 0x1734,
.subdevice = 0x0088,
.name = "Fujitsu-Siemens D1522", /* AD1981 */
diff --git a/sound/pci/oxygen/Makefile b/sound/pci/oxygen/Makefile
index 4ba07d42fd1d..389941cf6100 100644
--- a/sound/pci/oxygen/Makefile
+++ b/sound/pci/oxygen/Makefile
@@ -1,7 +1,8 @@
snd-oxygen-lib-objs := oxygen_io.o oxygen_lib.o oxygen_mixer.o oxygen_pcm.o
snd-hifier-objs := hifier.o
snd-oxygen-objs := oxygen.o
-snd-virtuoso-objs := virtuoso.o
+snd-virtuoso-objs := virtuoso.o xonar_lib.o \
+ xonar_pcm179x.o xonar_cs43xx.o xonar_hdmi.o
obj-$(CONFIG_SND_OXYGEN_LIB) += snd-oxygen-lib.o
obj-$(CONFIG_SND_HIFIER) += snd-hifier.o
diff --git a/sound/pci/oxygen/cs2000.h b/sound/pci/oxygen/cs2000.h
new file mode 100644
index 000000000000..c3501bdb5edc
--- /dev/null
+++ b/sound/pci/oxygen/cs2000.h
@@ -0,0 +1,83 @@
+#ifndef CS2000_H_INCLUDED
+#define CS2000_H_INCLUDED
+
+#define CS2000_DEV_ID 0x01
+#define CS2000_DEV_CTRL 0x02
+#define CS2000_DEV_CFG_1 0x03
+#define CS2000_DEV_CFG_2 0x04
+#define CS2000_GLOBAL_CFG 0x05
+#define CS2000_RATIO_0 0x06 /* 32 bits, big endian */
+#define CS2000_RATIO_1 0x0a
+#define CS2000_RATIO_2 0x0e
+#define CS2000_RATIO_3 0x12
+#define CS2000_FUN_CFG_1 0x16
+#define CS2000_FUN_CFG_2 0x17
+#define CS2000_FUN_CFG_3 0x1e
+
+/* DEV_ID */
+#define CS2000_DEVICE_MASK 0xf8
+#define CS2000_REVISION_MASK 0x07
+
+/* DEV_CTRL */
+#define CS2000_UNLOCK 0x80
+#define CS2000_AUX_OUT_DIS 0x02
+#define CS2000_CLK_OUT_DIS 0x01
+
+/* DEV_CFG_1 */
+#define CS2000_R_MOD_SEL_MASK 0xe0
+#define CS2000_R_MOD_SEL_1 0x00
+#define CS2000_R_MOD_SEL_2 0x20
+#define CS2000_R_MOD_SEL_4 0x40
+#define CS2000_R_MOD_SEL_8 0x60
+#define CS2000_R_MOD_SEL_1_2 0x80
+#define CS2000_R_MOD_SEL_1_4 0xa0
+#define CS2000_R_MOD_SEL_1_8 0xc0
+#define CS2000_R_MOD_SEL_1_16 0xe0
+#define CS2000_R_SEL_MASK 0x18
+#define CS2000_R_SEL_SHIFT 3
+#define CS2000_AUX_OUT_SRC_MASK 0x06
+#define CS2000_AUX_OUT_SRC_REF_CLK 0x00
+#define CS2000_AUX_OUT_SRC_CLK_IN 0x02
+#define CS2000_AUX_OUT_SRC_CLK_OUT 0x04
+#define CS2000_AUX_OUT_SRC_PLL_LOCK 0x06
+#define CS2000_EN_DEV_CFG_1 0x01
+
+/* DEV_CFG_2 */
+#define CS2000_LOCK_CLK_MASK 0x06
+#define CS2000_LOCK_CLK_SHIFT 1
+#define CS2000_FRAC_N_SRC_MASK 0x01
+#define CS2000_FRAC_N_SRC_STATIC 0x00
+#define CS2000_FRAC_N_SRC_DYNAMIC 0x01
+
+/* GLOBAL_CFG */
+#define CS2000_FREEZE 0x08
+#define CS2000_EN_DEV_CFG_2 0x01
+
+/* FUN_CFG_1 */
+#define CS2000_CLK_SKIP_EN 0x80
+#define CS2000_AUX_LOCK_CFG_MASK 0x40
+#define CS2000_AUX_LOCK_CFG_PP_HIGH 0x00
+#define CS2000_AUX_LOCK_CFG_OD_LOW 0x40
+#define CS2000_REF_CLK_DIV_MASK 0x18
+#define CS2000_REF_CLK_DIV_4 0x00
+#define CS2000_REF_CLK_DIV_2 0x08
+#define CS2000_REF_CLK_DIV_1 0x10
+
+/* FUN_CFG_2 */
+#define CS2000_CLK_OUT_UNL 0x10
+#define CS2000_L_F_RATIO_CFG_MASK 0x08
+#define CS2000_L_F_RATIO_CFG_20_12 0x00
+#define CS2000_L_F_RATIO_CFG_12_20 0x08
+
+/* FUN_CFG_3 */
+#define CS2000_CLK_IN_BW_MASK 0x70
+#define CS2000_CLK_IN_BW_1 0x00
+#define CS2000_CLK_IN_BW_2 0x10
+#define CS2000_CLK_IN_BW_4 0x20
+#define CS2000_CLK_IN_BW_8 0x30
+#define CS2000_CLK_IN_BW_16 0x40
+#define CS2000_CLK_IN_BW_32 0x50
+#define CS2000_CLK_IN_BW_64 0x60
+#define CS2000_CLK_IN_BW_128 0x70
+
+#endif
diff --git a/sound/pci/oxygen/hifier.c b/sound/pci/oxygen/hifier.c
index 84ef13183419..e3c229b63311 100644
--- a/sound/pci/oxygen/hifier.c
+++ b/sound/pci/oxygen/hifier.c
@@ -17,6 +17,12 @@
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
+/*
+ * CMI8788:
+ *
+ * SPI 0 -> AK4396
+ */
+
#include <linux/delay.h>
#include <linux/pci.h>
#include <sound/control.h>
@@ -51,23 +57,28 @@ static struct pci_device_id hifier_ids[] __devinitdata = {
MODULE_DEVICE_TABLE(pci, hifier_ids);
struct hifier_data {
- u8 ak4396_ctl2;
+ u8 ak4396_regs[5];
};
static void ak4396_write(struct oxygen *chip, u8 reg, u8 value)
{
+ struct hifier_data *data = chip->model_data;
+
oxygen_write_spi(chip, OXYGEN_SPI_TRIGGER |
OXYGEN_SPI_DATA_LENGTH_2 |
OXYGEN_SPI_CLOCK_160 |
(0 << OXYGEN_SPI_CODEC_SHIFT) |
OXYGEN_SPI_CEN_LATCH_CLOCK_HI,
AK4396_WRITE | (reg << 8) | value);
+ data->ak4396_regs[reg] = value;
}
-static void update_ak4396_volume(struct oxygen *chip)
+static void ak4396_write_cached(struct oxygen *chip, u8 reg, u8 value)
{
- ak4396_write(chip, AK4396_LCH_ATT, chip->dac_volume[0]);
- ak4396_write(chip, AK4396_RCH_ATT, chip->dac_volume[1]);
+ struct hifier_data *data = chip->model_data;
+
+ if (value != data->ak4396_regs[reg])
+ ak4396_write(chip, reg, value);
}
static void hifier_registers_init(struct oxygen *chip)
@@ -75,16 +86,19 @@ static void hifier_registers_init(struct oxygen *chip)
struct hifier_data *data = chip->model_data;
ak4396_write(chip, AK4396_CONTROL_1, AK4396_DIF_24_MSB | AK4396_RSTN);
- ak4396_write(chip, AK4396_CONTROL_2, data->ak4396_ctl2);
+ ak4396_write(chip, AK4396_CONTROL_2,
+ data->ak4396_regs[AK4396_CONTROL_2]);
ak4396_write(chip, AK4396_CONTROL_3, AK4396_PCM);
- update_ak4396_volume(chip);
+ ak4396_write(chip, AK4396_LCH_ATT, chip->dac_volume[0]);
+ ak4396_write(chip, AK4396_RCH_ATT, chip->dac_volume[1]);
}
static void hifier_init(struct oxygen *chip)
{
struct hifier_data *data = chip->model_data;
- data->ak4396_ctl2 = AK4396_SMUTE | AK4396_DEM_OFF | AK4396_DFS_NORMAL;
+ data->ak4396_regs[AK4396_CONTROL_2] =
+ AK4396_SMUTE | AK4396_DEM_OFF | AK4396_DFS_NORMAL;
hifier_registers_init(chip);
snd_component_add(chip->card, "AK4396");
@@ -106,20 +120,29 @@ static void set_ak4396_params(struct oxygen *chip,
struct hifier_data *data = chip->model_data;
u8 value;
- value = data->ak4396_ctl2 & ~AK4396_DFS_MASK;
+ value = data->ak4396_regs[AK4396_CONTROL_2] & ~AK4396_DFS_MASK;
if (params_rate(params) <= 54000)
value |= AK4396_DFS_NORMAL;
else if (params_rate(params) <= 108000)
value |= AK4396_DFS_DOUBLE;
else
value |= AK4396_DFS_QUAD;
- data->ak4396_ctl2 = value;
msleep(1); /* wait for the new MCLK to become stable */
- ak4396_write(chip, AK4396_CONTROL_1, AK4396_DIF_24_MSB);
- ak4396_write(chip, AK4396_CONTROL_2, value);
- ak4396_write(chip, AK4396_CONTROL_1, AK4396_DIF_24_MSB | AK4396_RSTN);
+ if (value != data->ak4396_regs[AK4396_CONTROL_2]) {
+ ak4396_write(chip, AK4396_CONTROL_1,
+ AK4396_DIF_24_MSB);
+ ak4396_write(chip, AK4396_CONTROL_2, value);
+ ak4396_write(chip, AK4396_CONTROL_1,
+ AK4396_DIF_24_MSB | AK4396_RSTN);
+ }
+}
+
+static void update_ak4396_volume(struct oxygen *chip)
+{
+ ak4396_write_cached(chip, AK4396_LCH_ATT, chip->dac_volume[0]);
+ ak4396_write_cached(chip, AK4396_RCH_ATT, chip->dac_volume[1]);
}
static void update_ak4396_mute(struct oxygen *chip)
@@ -127,11 +150,10 @@ static void update_ak4396_mute(struct oxygen *chip)
struct hifier_data *data = chip->model_data;
u8 value;
- value = data->ak4396_ctl2 & ~AK4396_SMUTE;
+ value = data->ak4396_regs[AK4396_CONTROL_2] & ~AK4396_SMUTE;
if (chip->dac_mute)
value |= AK4396_SMUTE;
- data->ak4396_ctl2 = value;
- ak4396_write(chip, AK4396_CONTROL_2, value);
+ ak4396_write_cached(chip, AK4396_CONTROL_2, value);
}
static void set_cs5340_params(struct oxygen *chip,
@@ -141,21 +163,14 @@ static void set_cs5340_params(struct oxygen *chip,
static const DECLARE_TLV_DB_LINEAR(ak4396_db_scale, TLV_DB_GAIN_MUTE, 0);
-static int hifier_control_filter(struct snd_kcontrol_new *template)
-{
- if (!strcmp(template->name, "Stereo Upmixing"))
- return 1; /* stereo only - we don't need upmixing */
- return 0;
-}
-
static const struct oxygen_model model_hifier = {
.shortname = "C-Media CMI8787",
.longname = "C-Media Oxygen HD Audio",
.chip = "CMI8788",
.init = hifier_init,
- .control_filter = hifier_control_filter,
.cleanup = hifier_cleanup,
.resume = hifier_resume,
+ .get_i2s_mclk = oxygen_default_i2s_mclk,
.set_dac_params = set_ak4396_params,
.set_adc_params = set_cs5340_params,
.update_dac_volume = update_ak4396_volume,
diff --git a/sound/pci/oxygen/oxygen.c b/sound/pci/oxygen/oxygen.c
index 72db4c39007f..acbedebcffd9 100644
--- a/sound/pci/oxygen/oxygen.c
+++ b/sound/pci/oxygen/oxygen.c
@@ -18,6 +18,8 @@
*/
/*
+ * CMI8788:
+ *
* SPI 0 -> 1st AK4396 (front)
* SPI 1 -> 2nd AK4396 (surround)
* SPI 2 -> 3rd AK4396 (center/LFE)
@@ -27,6 +29,10 @@
* GPIO 0 -> DFS0 of AK5385
* GPIO 1 -> DFS1 of AK5385
* GPIO 8 -> enable headphone amplifier on HT-Omega models
+ *
+ * CM9780:
+ *
+ * GPO 0 -> route line-in (0) or AC97 output (1) to ADC input
*/
#include <linux/delay.h>
@@ -91,8 +97,8 @@ MODULE_DEVICE_TABLE(pci, oxygen_ids);
#define GPIO_CLARO_HP 0x0100
struct generic_data {
- u8 ak4396_ctl2;
- u16 saved_wm8785_registers[2];
+ u8 ak4396_regs[4][5];
+ u16 wm8785_regs[3];
};
static void ak4396_write(struct oxygen *chip, unsigned int codec,
@@ -102,12 +108,24 @@ static void ak4396_write(struct oxygen *chip, unsigned int codec,
static const u8 codec_spi_map[4] = {
0, 1, 2, 4
};
+ struct generic_data *data = chip->model_data;
+
oxygen_write_spi(chip, OXYGEN_SPI_TRIGGER |
OXYGEN_SPI_DATA_LENGTH_2 |
OXYGEN_SPI_CLOCK_160 |
(codec_spi_map[codec] << OXYGEN_SPI_CODEC_SHIFT) |
OXYGEN_SPI_CEN_LATCH_CLOCK_HI,
AK4396_WRITE | (reg << 8) | value);
+ data->ak4396_regs[codec][reg] = value;
+}
+
+static void ak4396_write_cached(struct oxygen *chip, unsigned int codec,
+ u8 reg, u8 value)
+{
+ struct generic_data *data = chip->model_data;
+
+ if (value != data->ak4396_regs[codec][reg])
+ ak4396_write(chip, codec, reg, value);
}
static void wm8785_write(struct oxygen *chip, u8 reg, unsigned int value)
@@ -120,20 +138,8 @@ static void wm8785_write(struct oxygen *chip, u8 reg, unsigned int value)
(3 << OXYGEN_SPI_CODEC_SHIFT) |
OXYGEN_SPI_CEN_LATCH_CLOCK_LO,
(reg << 9) | value);
- if (reg < ARRAY_SIZE(data->saved_wm8785_registers))
- data->saved_wm8785_registers[reg] = value;
-}
-
-static void update_ak4396_volume(struct oxygen *chip)
-{
- unsigned int i;
-
- for (i = 0; i < 4; ++i) {
- ak4396_write(chip, i,
- AK4396_LCH_ATT, chip->dac_volume[i * 2]);
- ak4396_write(chip, i,
- AK4396_RCH_ATT, chip->dac_volume[i * 2 + 1]);
- }
+ if (reg < ARRAY_SIZE(data->wm8785_regs))
+ data->wm8785_regs[reg] = value;
}
static void ak4396_registers_init(struct oxygen *chip)
@@ -142,21 +148,25 @@ static void ak4396_registers_init(struct oxygen *chip)
unsigned int i;
for (i = 0; i < 4; ++i) {
- ak4396_write(chip, i,
- AK4396_CONTROL_1, AK4396_DIF_24_MSB | AK4396_RSTN);
- ak4396_write(chip, i,
- AK4396_CONTROL_2, data->ak4396_ctl2);
- ak4396_write(chip, i,
- AK4396_CONTROL_3, AK4396_PCM);
+ ak4396_write(chip, i, AK4396_CONTROL_1,
+ AK4396_DIF_24_MSB | AK4396_RSTN);
+ ak4396_write(chip, i, AK4396_CONTROL_2,
+ data->ak4396_regs[0][AK4396_CONTROL_2]);
+ ak4396_write(chip, i, AK4396_CONTROL_3,
+ AK4396_PCM);
+ ak4396_write(chip, i, AK4396_LCH_ATT,
+ chip->dac_volume[i * 2]);
+ ak4396_write(chip, i, AK4396_RCH_ATT,
+ chip->dac_volume[i * 2 + 1]);
}
- update_ak4396_volume(chip);
}
static void ak4396_init(struct oxygen *chip)
{
struct generic_data *data = chip->model_data;
- data->ak4396_ctl2 = AK4396_SMUTE | AK4396_DEM_OFF | AK4396_DFS_NORMAL;
+ data->ak4396_regs[0][AK4396_CONTROL_2] =
+ AK4396_SMUTE | AK4396_DEM_OFF | AK4396_DFS_NORMAL;
ak4396_registers_init(chip);
snd_component_add(chip->card, "AK4396");
}
@@ -173,17 +183,17 @@ static void wm8785_registers_init(struct oxygen *chip)
struct generic_data *data = chip->model_data;
wm8785_write(chip, WM8785_R7, 0);
- wm8785_write(chip, WM8785_R0, data->saved_wm8785_registers[0]);
- wm8785_write(chip, WM8785_R1, data->saved_wm8785_registers[1]);
+ wm8785_write(chip, WM8785_R0, data->wm8785_regs[0]);
+ wm8785_write(chip, WM8785_R2, data->wm8785_regs[2]);
}
static void wm8785_init(struct oxygen *chip)
{
struct generic_data *data = chip->model_data;
- data->saved_wm8785_registers[0] = WM8785_MCR_SLAVE |
- WM8785_OSR_SINGLE | WM8785_FORMAT_LJUST;
- data->saved_wm8785_registers[1] = WM8785_WL_24;
+ data->wm8785_regs[0] =
+ WM8785_MCR_SLAVE | WM8785_OSR_SINGLE | WM8785_FORMAT_LJUST;
+ data->wm8785_regs[2] = WM8785_HPFR | WM8785_HPFL;
wm8785_registers_init(chip);
snd_component_add(chip->card, "WM8785");
}
@@ -264,24 +274,36 @@ static void set_ak4396_params(struct oxygen *chip,
unsigned int i;
u8 value;
- value = data->ak4396_ctl2 & ~AK4396_DFS_MASK;
+ value = data->ak4396_regs[0][AK4396_CONTROL_2] & ~AK4396_DFS_MASK;
if (params_rate(params) <= 54000)
value |= AK4396_DFS_NORMAL;
else if (params_rate(params) <= 108000)
value |= AK4396_DFS_DOUBLE;
else
value |= AK4396_DFS_QUAD;
- data->ak4396_ctl2 = value;
msleep(1); /* wait for the new MCLK to become stable */
+ if (value != data->ak4396_regs[0][AK4396_CONTROL_2]) {
+ for (i = 0; i < 4; ++i) {
+ ak4396_write(chip, i, AK4396_CONTROL_1,
+ AK4396_DIF_24_MSB);
+ ak4396_write(chip, i, AK4396_CONTROL_2, value);
+ ak4396_write(chip, i, AK4396_CONTROL_1,
+ AK4396_DIF_24_MSB | AK4396_RSTN);
+ }
+ }
+}
+
+static void update_ak4396_volume(struct oxygen *chip)
+{
+ unsigned int i;
+
for (i = 0; i < 4; ++i) {
- ak4396_write(chip, i,
- AK4396_CONTROL_1, AK4396_DIF_24_MSB);
- ak4396_write(chip, i,
- AK4396_CONTROL_2, value);
- ak4396_write(chip, i,
- AK4396_CONTROL_1, AK4396_DIF_24_MSB | AK4396_RSTN);
+ ak4396_write_cached(chip, i, AK4396_LCH_ATT,
+ chip->dac_volume[i * 2]);
+ ak4396_write_cached(chip, i, AK4396_RCH_ATT,
+ chip->dac_volume[i * 2 + 1]);
}
}
@@ -291,21 +313,19 @@ static void update_ak4396_mute(struct oxygen *chip)
unsigned int i;
u8 value;
- value = data->ak4396_ctl2 & ~AK4396_SMUTE;
+ value = data->ak4396_regs[0][AK4396_CONTROL_2] & ~AK4396_SMUTE;
if (chip->dac_mute)
value |= AK4396_SMUTE;
- data->ak4396_ctl2 = value;
for (i = 0; i < 4; ++i)
- ak4396_write(chip, i, AK4396_CONTROL_2, value);
+ ak4396_write_cached(chip, i, AK4396_CONTROL_2, value);
}
static void set_wm8785_params(struct oxygen *chip,
struct snd_pcm_hw_params *params)
{
+ struct generic_data *data = chip->model_data;
unsigned int value;
- wm8785_write(chip, WM8785_R7, 0);
-
value = WM8785_MCR_SLAVE | WM8785_FORMAT_LJUST;
if (params_rate(params) <= 48000)
value |= WM8785_OSR_SINGLE;
@@ -313,13 +333,11 @@ static void set_wm8785_params(struct oxygen *chip,
value |= WM8785_OSR_DOUBLE;
else
value |= WM8785_OSR_QUAD;
- wm8785_write(chip, WM8785_R0, value);
-
- if (snd_pcm_format_width(params_format(params)) <= 16)
- value = WM8785_WL_16;
- else
- value = WM8785_WL_24;
- wm8785_write(chip, WM8785_R1, value);
+ if (value != data->wm8785_regs[0]) {
+ wm8785_write(chip, WM8785_R7, 0);
+ wm8785_write(chip, WM8785_R0, value);
+ wm8785_write(chip, WM8785_R2, data->wm8785_regs[2]);
+ }
}
static void set_ak5385_params(struct oxygen *chip,
@@ -337,6 +355,134 @@ static void set_ak5385_params(struct oxygen *chip,
value, GPIO_AK5385_DFS_MASK);
}
+static int rolloff_info(struct snd_kcontrol *ctl,
+ struct snd_ctl_elem_info *info)
+{
+ static const char *const names[2] = {
+ "Sharp Roll-off", "Slow Roll-off"
+ };
+
+ info->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED;
+ info->count = 1;
+ info->value.enumerated.items = 2;
+ if (info->value.enumerated.item >= 2)
+ info->value.enumerated.item = 1;
+ strcpy(info->value.enumerated.name, names[info->value.enumerated.item]);
+ return 0;
+}
+
+static int rolloff_get(struct snd_kcontrol *ctl,
+ struct snd_ctl_elem_value *value)
+{
+ struct oxygen *chip = ctl->private_data;
+ struct generic_data *data = chip->model_data;
+
+ value->value.enumerated.item[0] =
+ (data->ak4396_regs[0][AK4396_CONTROL_2] & AK4396_SLOW) != 0;
+ return 0;
+}
+
+static int rolloff_put(struct snd_kcontrol *ctl,
+ struct snd_ctl_elem_value *value)
+{
+ struct oxygen *chip = ctl->private_data;
+ struct generic_data *data = chip->model_data;
+ unsigned int i;
+ int changed;
+ u8 reg;
+
+ mutex_lock(&chip->mutex);
+ reg = data->ak4396_regs[0][AK4396_CONTROL_2];
+ if (value->value.enumerated.item[0])
+ reg |= AK4396_SLOW;
+ else
+ reg &= ~AK4396_SLOW;
+ changed = reg != data->ak4396_regs[0][AK4396_CONTROL_2];
+ if (changed) {
+ for (i = 0; i < 4; ++i)
+ ak4396_write(chip, i, AK4396_CONTROL_2, reg);
+ }
+ mutex_unlock(&chip->mutex);
+ return changed;
+}
+
+static const struct snd_kcontrol_new rolloff_control = {
+ .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+ .name = "DAC Filter Playback Enum",
+ .info = rolloff_info,
+ .get = rolloff_get,
+ .put = rolloff_put,
+};
+
+static int hpf_info(struct snd_kcontrol *ctl, struct snd_ctl_elem_info *info)
+{
+ static const char *const names[2] = {
+ "None", "High-pass Filter"
+ };
+
+ info->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED;
+ info->count = 1;
+ info->value.enumerated.items = 2;
+ if (info->value.enumerated.item >= 2)
+ info->value.enumerated.item = 1;
+ strcpy(info->value.enumerated.name, names[info->value.enumerated.item]);
+ return 0;
+}
+
+static int hpf_get(struct snd_kcontrol *ctl, struct snd_ctl_elem_value *value)
+{
+ struct oxygen *chip = ctl->private_data;
+ struct generic_data *data = chip->model_data;
+
+ value->value.enumerated.item[0] =
+ (data->wm8785_regs[WM8785_R2] & WM8785_HPFR) != 0;
+ return 0;
+}
+
+static int hpf_put(struct snd_kcontrol *ctl, struct snd_ctl_elem_value *value)
+{
+ struct oxygen *chip = ctl->private_data;
+ struct generic_data *data = chip->model_data;
+ unsigned int reg;
+ int changed;
+
+ mutex_lock(&chip->mutex);
+ reg = data->wm8785_regs[WM8785_R2] & ~(WM8785_HPFR | WM8785_HPFL);
+ if (value->value.enumerated.item[0])
+ reg |= WM8785_HPFR | WM8785_HPFL;
+ changed = reg != data->wm8785_regs[WM8785_R2];
+ if (changed)
+ wm8785_write(chip, WM8785_R2, reg);
+ mutex_unlock(&chip->mutex);
+ return changed;
+}
+
+static const struct snd_kcontrol_new hpf_control = {
+ .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+ .name = "ADC Filter Capture Enum",
+ .info = hpf_info,
+ .get = hpf_get,
+ .put = hpf_put,
+};
+
+static int generic_mixer_init(struct oxygen *chip)
+{
+ return snd_ctl_add(chip->card, snd_ctl_new1(&rolloff_control, chip));
+}
+
+static int generic_wm8785_mixer_init(struct oxygen *chip)
+{
+ int err;
+
+ err = generic_mixer_init(chip);
+ if (err < 0)
+ return err;
+ err = snd_ctl_add(chip->card, snd_ctl_new1(&hpf_control, chip));
+ if (err < 0)
+ return err;
+ return 0;
+}
+
static const DECLARE_TLV_DB_LINEAR(ak4396_db_scale, TLV_DB_GAIN_MUTE, 0);
static const struct oxygen_model model_generic = {
@@ -344,8 +490,10 @@ static const struct oxygen_model model_generic = {
.longname = "C-Media Oxygen HD Audio",
.chip = "CMI8788",
.init = generic_init,
+ .mixer_init = generic_wm8785_mixer_init,
.cleanup = generic_cleanup,
.resume = generic_resume,
+ .get_i2s_mclk = oxygen_default_i2s_mclk,
.set_dac_params = set_ak4396_params,
.set_adc_params = set_wm8785_params,
.update_dac_volume = update_ak4396_volume,
@@ -374,6 +522,7 @@ static int __devinit get_oxygen_model(struct oxygen *chip,
switch (id->driver_data) {
case MODEL_MERIDIAN:
chip->model.init = meridian_init;
+ chip->model.mixer_init = generic_mixer_init;
chip->model.resume = meridian_resume;
chip->model.set_adc_params = set_ak5385_params;
chip->model.device_config = PLAYBACK_0_TO_I2S |
@@ -389,6 +538,7 @@ static int __devinit get_oxygen_model(struct oxygen *chip,
break;
case MODEL_CLARO_HALO:
chip->model.init = claro_halo_init;
+ chip->model.mixer_init = generic_mixer_init;
chip->model.cleanup = claro_cleanup;
chip->model.suspend = claro_suspend;
chip->model.resume = claro_resume;
diff --git a/sound/pci/oxygen/oxygen.h b/sound/pci/oxygen/oxygen.h
index bd615dbffadb..6147216af744 100644
--- a/sound/pci/oxygen/oxygen.h
+++ b/sound/pci/oxygen/oxygen.h
@@ -78,12 +78,15 @@ struct oxygen_model {
void (*resume)(struct oxygen *chip);
void (*pcm_hardware_filter)(unsigned int channel,
struct snd_pcm_hardware *hardware);
+ unsigned int (*get_i2s_mclk)(struct oxygen *chip, unsigned int channel,
+ struct snd_pcm_hw_params *hw_params);
void (*set_dac_params)(struct oxygen *chip,
struct snd_pcm_hw_params *params);
void (*set_adc_params)(struct oxygen *chip,
struct snd_pcm_hw_params *params);
void (*update_dac_volume)(struct oxygen *chip);
void (*update_dac_mute)(struct oxygen *chip);
+ void (*update_center_lfe_mix)(struct oxygen *chip, bool mixed);
void (*gpio_changed)(struct oxygen *chip);
void (*uart_input)(struct oxygen *chip);
void (*ac97_switch)(struct oxygen *chip,
@@ -162,6 +165,8 @@ void oxygen_update_spdif_source(struct oxygen *chip);
/* oxygen_pcm.c */
int oxygen_pcm_init(struct oxygen *chip);
+unsigned int oxygen_default_i2s_mclk(struct oxygen *chip, unsigned int channel,
+ struct snd_pcm_hw_params *hw_params);
/* oxygen_io.c */
diff --git a/sound/pci/oxygen/oxygen_lib.c b/sound/pci/oxygen/oxygen_lib.c
index 9a8936e20744..9c5e6450eebb 100644
--- a/sound/pci/oxygen/oxygen_lib.c
+++ b/sound/pci/oxygen/oxygen_lib.c
@@ -278,7 +278,11 @@ oxygen_search_pci_id(struct oxygen *chip, const struct pci_device_id ids[])
static void oxygen_restore_eeprom(struct oxygen *chip,
const struct pci_device_id *id)
{
- if (oxygen_read_eeprom(chip, 0) != OXYGEN_EEPROM_ID) {
+ u16 eeprom_id;
+
+ eeprom_id = oxygen_read_eeprom(chip, 0);
+ if (eeprom_id != OXYGEN_EEPROM_ID &&
+ (eeprom_id != 0xffff || id->subdevice != 0x8788)) {
/*
* This function gets called only when a known card model has
* been detected, i.e., we know there is a valid subsystem
@@ -303,6 +307,28 @@ static void oxygen_restore_eeprom(struct oxygen *chip,
}
}
+static void pci_bridge_magic(void)
+{
+ struct pci_dev *pci = NULL;
+ u32 tmp;
+
+ for (;;) {
+ /* If there is any Pericom PI7C9X110 PCI-E/PCI bridge ... */
+ pci = pci_get_device(0x12d8, 0xe110, pci);
+ if (!pci)
+ break;
+ /*
+ * ... configure its secondary internal arbiter to park to
+ * the secondary port, instead of to the last master.
+ */
+ if (!pci_read_config_dword(pci, 0x40, &tmp)) {
+ tmp |= 1;
+ pci_write_config_dword(pci, 0x40, tmp);
+ }
+ /* Why? Try asking C-Media. */
+ }
+}
+
static void oxygen_init(struct oxygen *chip)
{
unsigned int i;
@@ -581,6 +607,7 @@ int oxygen_pci_probe(struct pci_dev *pci, int index, char *id,
snd_card_set_dev(card, &pci->dev);
card->private_free = oxygen_card_free;
+ pci_bridge_magic();
oxygen_init(chip);
chip->model.init(chip);
diff --git a/sound/pci/oxygen/oxygen_mixer.c b/sound/pci/oxygen/oxygen_mixer.c
index 5401c547c4e3..f375b8a27862 100644
--- a/sound/pci/oxygen/oxygen_mixer.c
+++ b/sound/pci/oxygen/oxygen_mixer.c
@@ -99,11 +99,15 @@ static int dac_mute_put(struct snd_kcontrol *ctl,
static int upmix_info(struct snd_kcontrol *ctl, struct snd_ctl_elem_info *info)
{
- static const char *const names[3] = {
- "Front", "Front+Surround", "Front+Surround+Back"
+ static const char *const names[5] = {
+ "Front",
+ "Front+Surround",
+ "Front+Surround+Back",
+ "Front+Surround+Center/LFE",
+ "Front+Surround+Center/LFE+Back",
};
struct oxygen *chip = ctl->private_data;
- unsigned int count = 2 + (chip->model.dac_channels == 8);
+ unsigned int count = chip->model.update_center_lfe_mix ? 5 : 3;
info->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED;
info->count = 1;
@@ -127,7 +131,7 @@ static int upmix_get(struct snd_kcontrol *ctl, struct snd_ctl_elem_value *value)
void oxygen_update_dac_routing(struct oxygen *chip)
{
/* DAC 0: front, DAC 1: surround, DAC 2: center/LFE, DAC 3: back */
- static const unsigned int reg_values[3] = {
+ static const unsigned int reg_values[5] = {
/* stereo -> front */
(0 << OXYGEN_PLAY_DAC0_SOURCE_SHIFT) |
(1 << OXYGEN_PLAY_DAC1_SOURCE_SHIFT) |
@@ -143,6 +147,16 @@ void oxygen_update_dac_routing(struct oxygen *chip)
(0 << OXYGEN_PLAY_DAC1_SOURCE_SHIFT) |
(2 << OXYGEN_PLAY_DAC2_SOURCE_SHIFT) |
(0 << OXYGEN_PLAY_DAC3_SOURCE_SHIFT),
+ /* stereo -> front+surround+center/LFE */
+ (0 << OXYGEN_PLAY_DAC0_SOURCE_SHIFT) |
+ (0 << OXYGEN_PLAY_DAC1_SOURCE_SHIFT) |
+ (0 << OXYGEN_PLAY_DAC2_SOURCE_SHIFT) |
+ (3 << OXYGEN_PLAY_DAC3_SOURCE_SHIFT),
+ /* stereo -> front+surround+center/LFE+back */
+ (0 << OXYGEN_PLAY_DAC0_SOURCE_SHIFT) |
+ (0 << OXYGEN_PLAY_DAC1_SOURCE_SHIFT) |
+ (0 << OXYGEN_PLAY_DAC2_SOURCE_SHIFT) |
+ (0 << OXYGEN_PLAY_DAC3_SOURCE_SHIFT),
};
u8 channels;
unsigned int reg_value;
@@ -167,22 +181,23 @@ void oxygen_update_dac_routing(struct oxygen *chip)
OXYGEN_PLAY_DAC1_SOURCE_MASK |
OXYGEN_PLAY_DAC2_SOURCE_MASK |
OXYGEN_PLAY_DAC3_SOURCE_MASK);
+ if (chip->model.update_center_lfe_mix)
+ chip->model.update_center_lfe_mix(chip, chip->dac_routing > 2);
}
static int upmix_put(struct snd_kcontrol *ctl, struct snd_ctl_elem_value *value)
{
struct oxygen *chip = ctl->private_data;
- unsigned int count = 2 + (chip->model.dac_channels == 8);
+ unsigned int count = chip->model.update_center_lfe_mix ? 5 : 3;
int changed;
+ if (value->value.enumerated.item[0] >= count)
+ return -EINVAL;
mutex_lock(&chip->mutex);
changed = value->value.enumerated.item[0] != chip->dac_routing;
if (changed) {
- chip->dac_routing = min(value->value.enumerated.item[0],
- count - 1);
- spin_lock_irq(&chip->reg_lock);
+ chip->dac_routing = value->value.enumerated.item[0];
oxygen_update_dac_routing(chip);
- spin_unlock_irq(&chip->reg_lock);
}
mutex_unlock(&chip->mutex);
return changed;
@@ -790,7 +805,7 @@ static const struct {
.controls = {
{
.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
- .name = "Analog Input Monitor Switch",
+ .name = "Analog Input Monitor Playback Switch",
.info = snd_ctl_boolean_mono_info,
.get = monitor_get,
.put = monitor_put,
@@ -798,7 +813,7 @@ static const struct {
},
{
.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
- .name = "Analog Input Monitor Volume",
+ .name = "Analog Input Monitor Playback Volume",
.access = SNDRV_CTL_ELEM_ACCESS_READWRITE |
SNDRV_CTL_ELEM_ACCESS_TLV_READ,
.info = monitor_volume_info,
@@ -815,7 +830,7 @@ static const struct {
.controls = {
{
.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
- .name = "Analog Input Monitor Switch",
+ .name = "Analog Input Monitor Playback Switch",
.info = snd_ctl_boolean_mono_info,
.get = monitor_get,
.put = monitor_put,
@@ -823,7 +838,7 @@ static const struct {
},
{
.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
- .name = "Analog Input Monitor Volume",
+ .name = "Analog Input Monitor Playback Volume",
.access = SNDRV_CTL_ELEM_ACCESS_READWRITE |
SNDRV_CTL_ELEM_ACCESS_TLV_READ,
.info = monitor_volume_info,
@@ -840,7 +855,7 @@ static const struct {
.controls = {
{
.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
- .name = "Analog Input Monitor Switch",
+ .name = "Analog Input Monitor Playback Switch",
.index = 1,
.info = snd_ctl_boolean_mono_info,
.get = monitor_get,
@@ -849,7 +864,7 @@ static const struct {
},
{
.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
- .name = "Analog Input Monitor Volume",
+ .name = "Analog Input Monitor Playback Volume",
.index = 1,
.access = SNDRV_CTL_ELEM_ACCESS_READWRITE |
SNDRV_CTL_ELEM_ACCESS_TLV_READ,
@@ -867,7 +882,7 @@ static const struct {
.controls = {
{
.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
- .name = "Digital Input Monitor Switch",
+ .name = "Digital Input Monitor Playback Switch",
.info = snd_ctl_boolean_mono_info,
.get = monitor_get,
.put = monitor_put,
@@ -875,7 +890,7 @@ static const struct {
},
{
.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
- .name = "Digital Input Monitor Volume",
+ .name = "Digital Input Monitor Playback Volume",
.access = SNDRV_CTL_ELEM_ACCESS_READWRITE |
SNDRV_CTL_ELEM_ACCESS_TLV_READ,
.info = monitor_volume_info,
@@ -954,6 +969,9 @@ static int add_controls(struct oxygen *chip,
if (err == 1)
continue;
}
+ if (!strcmp(template.name, "Stereo Upmixing") &&
+ chip->model.dac_channels == 2)
+ continue;
if (!strcmp(template.name, "Master Playback Volume") &&
chip->model.dac_tlv) {
template.tlv.p = chip->model.dac_tlv;
diff --git a/sound/pci/oxygen/oxygen_pcm.c b/sound/pci/oxygen/oxygen_pcm.c
index ef2345d82b86..9dff6954c397 100644
--- a/sound/pci/oxygen/oxygen_pcm.c
+++ b/sound/pci/oxygen/oxygen_pcm.c
@@ -271,13 +271,16 @@ static unsigned int oxygen_rate(struct snd_pcm_hw_params *hw_params)
}
}
-static unsigned int oxygen_i2s_mclk(struct snd_pcm_hw_params *hw_params)
+unsigned int oxygen_default_i2s_mclk(struct oxygen *chip,
+ unsigned int channel,
+ struct snd_pcm_hw_params *hw_params)
{
if (params_rate(hw_params) <= 96000)
return OXYGEN_I2S_MCLK_256;
else
return OXYGEN_I2S_MCLK_128;
}
+EXPORT_SYMBOL(oxygen_default_i2s_mclk);
static unsigned int oxygen_i2s_bits(struct snd_pcm_hw_params *hw_params)
{
@@ -354,7 +357,7 @@ static int oxygen_rec_a_hw_params(struct snd_pcm_substream *substream,
OXYGEN_REC_FORMAT_A_MASK);
oxygen_write16_masked(chip, OXYGEN_I2S_A_FORMAT,
oxygen_rate(hw_params) |
- oxygen_i2s_mclk(hw_params) |
+ chip->model.get_i2s_mclk(chip, PCM_A, hw_params) |
chip->model.adc_i2s_format |
oxygen_i2s_bits(hw_params),
OXYGEN_I2S_RATE_MASK |
@@ -390,7 +393,8 @@ static int oxygen_rec_b_hw_params(struct snd_pcm_substream *substream,
if (!is_ac97)
oxygen_write16_masked(chip, OXYGEN_I2S_B_FORMAT,
oxygen_rate(hw_params) |
- oxygen_i2s_mclk(hw_params) |
+ chip->model.get_i2s_mclk(chip, PCM_B,
+ hw_params) |
chip->model.adc_i2s_format |
oxygen_i2s_bits(hw_params),
OXYGEN_I2S_RATE_MASK |
@@ -435,6 +439,7 @@ static int oxygen_spdif_hw_params(struct snd_pcm_substream *substream,
if (err < 0)
return err;
+ mutex_lock(&chip->mutex);
spin_lock_irq(&chip->reg_lock);
oxygen_clear_bits32(chip, OXYGEN_SPDIF_CONTROL,
OXYGEN_SPDIF_OUT_ENABLE);
@@ -446,6 +451,7 @@ static int oxygen_spdif_hw_params(struct snd_pcm_substream *substream,
OXYGEN_SPDIF_OUT_RATE_MASK);
oxygen_update_spdif_source(chip);
spin_unlock_irq(&chip->reg_lock);
+ mutex_unlock(&chip->mutex);
return 0;
}
@@ -459,6 +465,7 @@ static int oxygen_multich_hw_params(struct snd_pcm_substream *substream,
if (err < 0)
return err;
+ mutex_lock(&chip->mutex);
spin_lock_irq(&chip->reg_lock);
oxygen_write8_masked(chip, OXYGEN_PLAY_CHANNELS,
oxygen_play_channels(hw_params),
@@ -469,18 +476,18 @@ static int oxygen_multich_hw_params(struct snd_pcm_substream *substream,
oxygen_write16_masked(chip, OXYGEN_I2S_MULTICH_FORMAT,
oxygen_rate(hw_params) |
chip->model.dac_i2s_format |
- oxygen_i2s_mclk(hw_params) |
+ chip->model.get_i2s_mclk(chip, PCM_MULTICH,
+ hw_params) |
oxygen_i2s_bits(hw_params),
OXYGEN_I2S_RATE_MASK |
OXYGEN_I2S_FORMAT_MASK |
OXYGEN_I2S_MCLK_MASK |
OXYGEN_I2S_BITS_MASK);
- oxygen_update_dac_routing(chip);
oxygen_update_spdif_source(chip);
spin_unlock_irq(&chip->reg_lock);
- mutex_lock(&chip->mutex);
chip->model.set_dac_params(chip, hw_params);
+ oxygen_update_dac_routing(chip);
mutex_unlock(&chip->mutex);
return 0;
}
diff --git a/sound/pci/oxygen/virtuoso.c b/sound/pci/oxygen/virtuoso.c
index 6ebcb6bdd712..6accaf9580b2 100644
--- a/sound/pci/oxygen/virtuoso.c
+++ b/sound/pci/oxygen/virtuoso.c
@@ -17,145 +17,12 @@
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
-/*
- * Xonar D2/D2X
- * ------------
- *
- * CMI8788:
- *
- * SPI 0 -> 1st PCM1796 (front)
- * SPI 1 -> 2nd PCM1796 (surround)
- * SPI 2 -> 3rd PCM1796 (center/LFE)
- * SPI 4 -> 4th PCM1796 (back)
- *
- * GPIO 2 -> M0 of CS5381
- * GPIO 3 -> M1 of CS5381
- * GPIO 5 <- external power present (D2X only)
- * GPIO 7 -> ALT
- * GPIO 8 -> enable output to speakers
- */
-
-/*
- * Xonar D1/DX
- * -----------
- *
- * CMI8788:
- *
- * I²C <-> CS4398 (front)
- * <-> CS4362A (surround, center/LFE, back)
- *
- * GPI 0 <- external power present (DX only)
- *
- * GPIO 0 -> enable output to speakers
- * GPIO 1 -> enable front panel I/O
- * GPIO 2 -> M0 of CS5361
- * GPIO 3 -> M1 of CS5361
- * GPIO 8 -> route input jack to line-in (0) or mic-in (1)
- *
- * CS4398:
- *
- * AD0 <- 1
- * AD1 <- 1
- *
- * CS4362A:
- *
- * AD0 <- 0
- */
-
-/*
- * Xonar HDAV1.3 (Deluxe)
- * ----------------------
- *
- * CMI8788:
- *
- * I²C <-> PCM1796 (front)
- *
- * GPI 0 <- external power present
- *
- * GPIO 0 -> enable output to speakers
- * GPIO 2 -> M0 of CS5381
- * GPIO 3 -> M1 of CS5381
- * GPIO 8 -> route input jack to line-in (0) or mic-in (1)
- *
- * TXD -> HDMI controller
- * RXD <- HDMI controller
- *
- * PCM1796 front: AD1,0 <- 0,0
- *
- * no daughterboard
- * ----------------
- *
- * GPIO 4 <- 1
- *
- * H6 daughterboard
- * ----------------
- *
- * GPIO 4 <- 0
- * GPIO 5 <- 0
- *
- * I²C <-> PCM1796 (surround)
- * <-> PCM1796 (center/LFE)
- * <-> PCM1796 (back)
- *
- * PCM1796 surround: AD1,0 <- 0,1
- * PCM1796 center/LFE: AD1,0 <- 1,0
- * PCM1796 back: AD1,0 <- 1,1
- *
- * unknown daughterboard
- * ---------------------
- *
- * GPIO 4 <- 0
- * GPIO 5 <- 1
- *
- * I²C <-> CS4362A (surround, center/LFE, back)
- *
- * CS4362A: AD0 <- 0
- */
-
-/*
- * Xonar Essence ST (Deluxe)/STX
- * -----------------------------
- *
- * CMI8788:
- *
- * I²C <-> PCM1792A
- *
- * GPI 0 <- external power present
- *
- * GPIO 0 -> enable output to speakers
- * GPIO 1 -> route HP to front panel (0) or rear jack (1)
- * GPIO 2 -> M0 of CS5381
- * GPIO 3 -> M1 of CS5381
- * GPIO 7 -> route output to speaker jacks (0) or HP (1)
- * GPIO 8 -> route input jack to line-in (0) or mic-in (1)
- *
- * PCM1792A:
- *
- * AD0 <- 0
- *
- * H6 daughterboard
- * ----------------
- *
- * GPIO 4 <- 0
- * GPIO 5 <- 0
- */
-
#include <linux/pci.h>
#include <linux/delay.h>
-#include <linux/mutex.h>
-#include <sound/ac97_codec.h>
-#include <sound/asoundef.h>
-#include <sound/control.h>
#include <sound/core.h>
#include <sound/initval.h>
#include <sound/pcm.h>
-#include <sound/pcm_params.h>
-#include <sound/tlv.h>
-#include "oxygen.h"
-#include "cm9780.h"
-#include "pcm1796.h"
-#include "cs4398.h"
-#include "cs4362a.h"
+#include "xonar.h"
MODULE_AUTHOR("Clemens Ladisch <clemens@ladisch.de>");
MODULE_DESCRIPTION("Asus AVx00 driver");
@@ -173,972 +40,28 @@ MODULE_PARM_DESC(id, "ID string");
module_param_array(enable, bool, NULL, 0444);
MODULE_PARM_DESC(enable, "enable card");
-enum {
- MODEL_D2,
- MODEL_D2X,
- MODEL_D1,
- MODEL_DX,
- MODEL_HDAV, /* without daughterboard */
- MODEL_HDAV_H6, /* with H6 daughterboard */
- MODEL_ST,
- MODEL_ST_H6,
- MODEL_STX,
-};
-
static struct pci_device_id xonar_ids[] __devinitdata = {
- { OXYGEN_PCI_SUBID(0x1043, 0x8269), .driver_data = MODEL_D2 },
- { OXYGEN_PCI_SUBID(0x1043, 0x8275), .driver_data = MODEL_DX },
- { OXYGEN_PCI_SUBID(0x1043, 0x82b7), .driver_data = MODEL_D2X },
- { OXYGEN_PCI_SUBID(0x1043, 0x8314), .driver_data = MODEL_HDAV },
- { OXYGEN_PCI_SUBID(0x1043, 0x8327), .driver_data = MODEL_DX },
- { OXYGEN_PCI_SUBID(0x1043, 0x834f), .driver_data = MODEL_D1 },
- { OXYGEN_PCI_SUBID(0x1043, 0x835c), .driver_data = MODEL_STX },
- { OXYGEN_PCI_SUBID(0x1043, 0x835d), .driver_data = MODEL_ST },
+ { OXYGEN_PCI_SUBID(0x1043, 0x8269) },
+ { OXYGEN_PCI_SUBID(0x1043, 0x8275) },
+ { OXYGEN_PCI_SUBID(0x1043, 0x82b7) },
+ { OXYGEN_PCI_SUBID(0x1043, 0x8314) },
+ { OXYGEN_PCI_SUBID(0x1043, 0x8327) },
+ { OXYGEN_PCI_SUBID(0x1043, 0x834f) },
+ { OXYGEN_PCI_SUBID(0x1043, 0x835c) },
+ { OXYGEN_PCI_SUBID(0x1043, 0x835d) },
{ OXYGEN_PCI_SUBID_BROKEN_EEPROM },
{ }
};
MODULE_DEVICE_TABLE(pci, xonar_ids);
-
-#define GPIO_CS53x1_M_MASK 0x000c
-#define GPIO_CS53x1_M_SINGLE 0x0000
-#define GPIO_CS53x1_M_DOUBLE 0x0004
-#define GPIO_CS53x1_M_QUAD 0x0008
-
-#define GPIO_D2X_EXT_POWER 0x0020
-#define GPIO_D2_ALT 0x0080
-#define GPIO_D2_OUTPUT_ENABLE 0x0100
-
-#define GPI_DX_EXT_POWER 0x01
-#define GPIO_DX_OUTPUT_ENABLE 0x0001
-#define GPIO_DX_FRONT_PANEL 0x0002
-#define GPIO_DX_INPUT_ROUTE 0x0100
-
-#define GPIO_DB_MASK 0x0030
-#define GPIO_DB_H6 0x0000
-#define GPIO_DB_XX 0x0020
-
-#define GPIO_ST_HP_REAR 0x0002
-#define GPIO_ST_HP 0x0080
-
-#define I2C_DEVICE_PCM1796(i) (0x98 + ((i) << 1)) /* 10011, ADx=i, /W=0 */
-#define I2C_DEVICE_CS4398 0x9e /* 10011, AD1=1, AD0=1, /W=0 */
-#define I2C_DEVICE_CS4362A 0x30 /* 001100, AD0=0, /W=0 */
-
-struct xonar_data {
- unsigned int anti_pop_delay;
- unsigned int dacs;
- u16 output_enable_bit;
- u8 ext_power_reg;
- u8 ext_power_int_reg;
- u8 ext_power_bit;
- u8 has_power;
- u8 pcm1796_oversampling;
- u8 cs4398_fm;
- u8 cs4362a_fm;
- u8 hdmi_params[5];
-};
-
-static void xonar_gpio_changed(struct oxygen *chip);
-
-static inline void pcm1796_write_spi(struct oxygen *chip, unsigned int codec,
- u8 reg, u8 value)
-{
- /* maps ALSA channel pair number to SPI output */
- static const u8 codec_map[4] = {
- 0, 1, 2, 4
- };
- oxygen_write_spi(chip, OXYGEN_SPI_TRIGGER |
- OXYGEN_SPI_DATA_LENGTH_2 |
- OXYGEN_SPI_CLOCK_160 |
- (codec_map[codec] << OXYGEN_SPI_CODEC_SHIFT) |
- OXYGEN_SPI_CEN_LATCH_CLOCK_HI,
- (reg << 8) | value);
-}
-
-static inline void pcm1796_write_i2c(struct oxygen *chip, unsigned int codec,
- u8 reg, u8 value)
-{
- oxygen_write_i2c(chip, I2C_DEVICE_PCM1796(codec), reg, value);
-}
-
-static void pcm1796_write(struct oxygen *chip, unsigned int codec,
- u8 reg, u8 value)
-{
- if ((chip->model.function_flags & OXYGEN_FUNCTION_2WIRE_SPI_MASK) ==
- OXYGEN_FUNCTION_SPI)
- pcm1796_write_spi(chip, codec, reg, value);
- else
- pcm1796_write_i2c(chip, codec, reg, value);
-}
-
-static void cs4398_write(struct oxygen *chip, u8 reg, u8 value)
-{
- oxygen_write_i2c(chip, I2C_DEVICE_CS4398, reg, value);
-}
-
-static void cs4362a_write(struct oxygen *chip, u8 reg, u8 value)
-{
- oxygen_write_i2c(chip, I2C_DEVICE_CS4362A, reg, value);
-}
-
-static void hdmi_write_command(struct oxygen *chip, u8 command,
- unsigned int count, const u8 *params)
-{
- unsigned int i;
- u8 checksum;
-
- oxygen_write_uart(chip, 0xfb);
- oxygen_write_uart(chip, 0xef);
- oxygen_write_uart(chip, command);
- oxygen_write_uart(chip, count);
- for (i = 0; i < count; ++i)
- oxygen_write_uart(chip, params[i]);
- checksum = 0xfb + 0xef + command + count;
- for (i = 0; i < count; ++i)
- checksum += params[i];
- oxygen_write_uart(chip, checksum);
-}
-
-static void xonar_enable_output(struct oxygen *chip)
-{
- struct xonar_data *data = chip->model_data;
-
- msleep(data->anti_pop_delay);
- oxygen_set_bits16(chip, OXYGEN_GPIO_DATA, data->output_enable_bit);
-}
-
-static void xonar_common_init(struct oxygen *chip)
-{
- struct xonar_data *data = chip->model_data;
-
- if (data->ext_power_reg) {
- oxygen_set_bits8(chip, data->ext_power_int_reg,
- data->ext_power_bit);
- chip->interrupt_mask |= OXYGEN_INT_GPIO;
- chip->model.gpio_changed = xonar_gpio_changed;
- data->has_power = !!(oxygen_read8(chip, data->ext_power_reg)
- & data->ext_power_bit);
- }
- oxygen_set_bits16(chip, OXYGEN_GPIO_CONTROL,
- GPIO_CS53x1_M_MASK | data->output_enable_bit);
- oxygen_write16_masked(chip, OXYGEN_GPIO_DATA,
- GPIO_CS53x1_M_SINGLE, GPIO_CS53x1_M_MASK);
- oxygen_ac97_set_bits(chip, 0, CM9780_JACK, CM9780_FMIC2MIC);
- xonar_enable_output(chip);
-}
-
-static void update_pcm1796_volume(struct oxygen *chip)
-{
- struct xonar_data *data = chip->model_data;
- unsigned int i;
-
- for (i = 0; i < data->dacs; ++i) {
- pcm1796_write(chip, i, 16, chip->dac_volume[i * 2]);
- pcm1796_write(chip, i, 17, chip->dac_volume[i * 2 + 1]);
- }
-}
-
-static void update_pcm1796_mute(struct oxygen *chip)
-{
- struct xonar_data *data = chip->model_data;
- unsigned int i;
- u8 value;
-
- value = PCM1796_DMF_DISABLED | PCM1796_FMT_24_LJUST | PCM1796_ATLD;
- if (chip->dac_mute)
- value |= PCM1796_MUTE;
- for (i = 0; i < data->dacs; ++i)
- pcm1796_write(chip, i, 18, value);
-}
-
-static void pcm1796_init(struct oxygen *chip)
-{
- struct xonar_data *data = chip->model_data;
- unsigned int i;
-
- for (i = 0; i < data->dacs; ++i) {
- pcm1796_write(chip, i, 19, PCM1796_FLT_SHARP | PCM1796_ATS_1);
- pcm1796_write(chip, i, 20, data->pcm1796_oversampling);
- pcm1796_write(chip, i, 21, 0);
- }
- update_pcm1796_mute(chip); /* set ATLD before ATL/ATR */
- update_pcm1796_volume(chip);
-}
-
-static void xonar_d2_init(struct oxygen *chip)
-{
- struct xonar_data *data = chip->model_data;
-
- data->anti_pop_delay = 300;
- data->dacs = 4;
- data->output_enable_bit = GPIO_D2_OUTPUT_ENABLE;
- data->pcm1796_oversampling = PCM1796_OS_64;
-
- pcm1796_init(chip);
-
- oxygen_set_bits16(chip, OXYGEN_GPIO_CONTROL, GPIO_D2_ALT);
- oxygen_clear_bits16(chip, OXYGEN_GPIO_DATA, GPIO_D2_ALT);
-
- xonar_common_init(chip);
-
- snd_component_add(chip->card, "PCM1796");
- snd_component_add(chip->card, "CS5381");
-}
-
-static void xonar_d2x_init(struct oxygen *chip)
-{
- struct xonar_data *data = chip->model_data;
-
- data->ext_power_reg = OXYGEN_GPIO_DATA;
- data->ext_power_int_reg = OXYGEN_GPIO_INTERRUPT_MASK;
- data->ext_power_bit = GPIO_D2X_EXT_POWER;
- oxygen_clear_bits16(chip, OXYGEN_GPIO_CONTROL, GPIO_D2X_EXT_POWER);
-
- xonar_d2_init(chip);
-}
-
-static void update_cs4362a_volumes(struct oxygen *chip)
-{
- u8 mute;
-
- mute = chip->dac_mute ? CS4362A_MUTE : 0;
- cs4362a_write(chip, 7, (127 - chip->dac_volume[2]) | mute);
- cs4362a_write(chip, 8, (127 - chip->dac_volume[3]) | mute);
- cs4362a_write(chip, 10, (127 - chip->dac_volume[4]) | mute);
- cs4362a_write(chip, 11, (127 - chip->dac_volume[5]) | mute);
- cs4362a_write(chip, 13, (127 - chip->dac_volume[6]) | mute);
- cs4362a_write(chip, 14, (127 - chip->dac_volume[7]) | mute);
-}
-
-static void update_cs43xx_volume(struct oxygen *chip)
-{
- cs4398_write(chip, 5, (127 - chip->dac_volume[0]) * 2);
- cs4398_write(chip, 6, (127 - chip->dac_volume[1]) * 2);
- update_cs4362a_volumes(chip);
-}
-
-static void update_cs43xx_mute(struct oxygen *chip)
-{
- u8 reg;
-
- reg = CS4398_MUTEP_LOW | CS4398_PAMUTE;
- if (chip->dac_mute)
- reg |= CS4398_MUTE_B | CS4398_MUTE_A;
- cs4398_write(chip, 4, reg);
- update_cs4362a_volumes(chip);
-}
-
-static void cs43xx_init(struct oxygen *chip)
-{
- struct xonar_data *data = chip->model_data;
-
- /* set CPEN (control port mode) and power down */
- cs4398_write(chip, 8, CS4398_CPEN | CS4398_PDN);
- cs4362a_write(chip, 0x01, CS4362A_PDN | CS4362A_CPEN);
- /* configure */
- cs4398_write(chip, 2, data->cs4398_fm);
- cs4398_write(chip, 3, CS4398_ATAPI_B_R | CS4398_ATAPI_A_L);
- cs4398_write(chip, 7, CS4398_RMP_DN | CS4398_RMP_UP |
- CS4398_ZERO_CROSS | CS4398_SOFT_RAMP);
- cs4362a_write(chip, 0x02, CS4362A_DIF_LJUST);
- cs4362a_write(chip, 0x03, CS4362A_MUTEC_6 | CS4362A_AMUTE |
- CS4362A_RMP_UP | CS4362A_ZERO_CROSS | CS4362A_SOFT_RAMP);
- cs4362a_write(chip, 0x04, CS4362A_RMP_DN | CS4362A_DEM_NONE);
- cs4362a_write(chip, 0x05, 0);
- cs4362a_write(chip, 0x06, data->cs4362a_fm);
- cs4362a_write(chip, 0x09, data->cs4362a_fm);
- cs4362a_write(chip, 0x0c, data->cs4362a_fm);
- update_cs43xx_volume(chip);
- update_cs43xx_mute(chip);
- /* clear power down */
- cs4398_write(chip, 8, CS4398_CPEN);
- cs4362a_write(chip, 0x01, CS4362A_CPEN);
-}
-
-static void xonar_d1_init(struct oxygen *chip)
-{
- struct xonar_data *data = chip->model_data;
-
- data->anti_pop_delay = 800;
- data->output_enable_bit = GPIO_DX_OUTPUT_ENABLE;
- data->cs4398_fm = CS4398_FM_SINGLE | CS4398_DEM_NONE | CS4398_DIF_LJUST;
- data->cs4362a_fm = CS4362A_FM_SINGLE |
- CS4362A_ATAPI_B_R | CS4362A_ATAPI_A_L;
-
- oxygen_write16(chip, OXYGEN_2WIRE_BUS_STATUS,
- OXYGEN_2WIRE_LENGTH_8 |
- OXYGEN_2WIRE_INTERRUPT_MASK |
- OXYGEN_2WIRE_SPEED_FAST);
-
- cs43xx_init(chip);
-
- oxygen_set_bits16(chip, OXYGEN_GPIO_CONTROL,
- GPIO_DX_FRONT_PANEL | GPIO_DX_INPUT_ROUTE);
- oxygen_clear_bits16(chip, OXYGEN_GPIO_DATA,
- GPIO_DX_FRONT_PANEL | GPIO_DX_INPUT_ROUTE);
-
- xonar_common_init(chip);
-
- snd_component_add(chip->card, "CS4398");
- snd_component_add(chip->card, "CS4362A");
- snd_component_add(chip->card, "CS5361");
-}
-
-static void xonar_dx_init(struct oxygen *chip)
-{
- struct xonar_data *data = chip->model_data;
-
- data->ext_power_reg = OXYGEN_GPI_DATA;
- data->ext_power_int_reg = OXYGEN_GPI_INTERRUPT_MASK;
- data->ext_power_bit = GPI_DX_EXT_POWER;
-
- xonar_d1_init(chip);
-}
-
-static void xonar_hdav_init(struct oxygen *chip)
-{
- struct xonar_data *data = chip->model_data;
- u8 param;
-
- oxygen_write16(chip, OXYGEN_2WIRE_BUS_STATUS,
- OXYGEN_2WIRE_LENGTH_8 |
- OXYGEN_2WIRE_INTERRUPT_MASK |
- OXYGEN_2WIRE_SPEED_FAST);
-
- data->anti_pop_delay = 100;
- data->dacs = chip->model.private_data == MODEL_HDAV_H6 ? 4 : 1;
- data->output_enable_bit = GPIO_DX_OUTPUT_ENABLE;
- data->ext_power_reg = OXYGEN_GPI_DATA;
- data->ext_power_int_reg = OXYGEN_GPI_INTERRUPT_MASK;
- data->ext_power_bit = GPI_DX_EXT_POWER;
- data->pcm1796_oversampling = PCM1796_OS_64;
-
- pcm1796_init(chip);
-
- oxygen_set_bits16(chip, OXYGEN_GPIO_CONTROL, GPIO_DX_INPUT_ROUTE);
- oxygen_clear_bits16(chip, OXYGEN_GPIO_DATA, GPIO_DX_INPUT_ROUTE);
-
- oxygen_reset_uart(chip);
- param = 0;
- hdmi_write_command(chip, 0x61, 1, &param);
- param = 1;
- hdmi_write_command(chip, 0x74, 1, &param);
- data->hdmi_params[1] = IEC958_AES3_CON_FS_48000;
- data->hdmi_params[4] = 1;
- hdmi_write_command(chip, 0x54, 5, data->hdmi_params);
-
- xonar_common_init(chip);
-
- snd_component_add(chip->card, "PCM1796");
- snd_component_add(chip->card, "CS5381");
-}
-
-static void xonar_st_init(struct oxygen *chip)
-{
- struct xonar_data *data = chip->model_data;
-
- oxygen_write16(chip, OXYGEN_2WIRE_BUS_STATUS,
- OXYGEN_2WIRE_LENGTH_8 |
- OXYGEN_2WIRE_INTERRUPT_MASK |
- OXYGEN_2WIRE_SPEED_FAST);
-
- if (chip->model.private_data == MODEL_ST_H6)
- chip->model.dac_channels = 8;
- data->anti_pop_delay = 100;
- data->dacs = chip->model.private_data == MODEL_ST_H6 ? 4 : 1;
- data->output_enable_bit = GPIO_DX_OUTPUT_ENABLE;
- data->pcm1796_oversampling = PCM1796_OS_64;
-
- pcm1796_init(chip);
-
- oxygen_set_bits16(chip, OXYGEN_GPIO_CONTROL,
- GPIO_DX_INPUT_ROUTE | GPIO_ST_HP_REAR | GPIO_ST_HP);
- oxygen_clear_bits16(chip, OXYGEN_GPIO_DATA,
- GPIO_DX_INPUT_ROUTE | GPIO_ST_HP_REAR | GPIO_ST_HP);
-
- xonar_common_init(chip);
-
- snd_component_add(chip->card, "PCM1792A");
- snd_component_add(chip->card, "CS5381");
-}
-
-static void xonar_stx_init(struct oxygen *chip)
-{
- struct xonar_data *data = chip->model_data;
-
- data->ext_power_reg = OXYGEN_GPI_DATA;
- data->ext_power_int_reg = OXYGEN_GPI_INTERRUPT_MASK;
- data->ext_power_bit = GPI_DX_EXT_POWER;
-
- xonar_st_init(chip);
-}
-
-static void xonar_disable_output(struct oxygen *chip)
-{
- struct xonar_data *data = chip->model_data;
-
- oxygen_clear_bits16(chip, OXYGEN_GPIO_DATA, data->output_enable_bit);
-}
-
-static void xonar_d2_cleanup(struct oxygen *chip)
-{
- xonar_disable_output(chip);
-}
-
-static void xonar_d1_cleanup(struct oxygen *chip)
-{
- xonar_disable_output(chip);
- cs4362a_write(chip, 0x01, CS4362A_PDN | CS4362A_CPEN);
- oxygen_clear_bits8(chip, OXYGEN_FUNCTION, OXYGEN_FUNCTION_RESET_CODEC);
-}
-
-static void xonar_hdav_cleanup(struct oxygen *chip)
-{
- u8 param = 0;
-
- hdmi_write_command(chip, 0x74, 1, &param);
- xonar_disable_output(chip);
-}
-
-static void xonar_st_cleanup(struct oxygen *chip)
-{
- xonar_disable_output(chip);
-}
-
-static void xonar_d2_suspend(struct oxygen *chip)
-{
- xonar_d2_cleanup(chip);
-}
-
-static void xonar_d1_suspend(struct oxygen *chip)
-{
- xonar_d1_cleanup(chip);
-}
-
-static void xonar_hdav_suspend(struct oxygen *chip)
-{
- xonar_hdav_cleanup(chip);
- msleep(2);
-}
-
-static void xonar_st_suspend(struct oxygen *chip)
-{
- xonar_st_cleanup(chip);
-}
-
-static void xonar_d2_resume(struct oxygen *chip)
-{
- pcm1796_init(chip);
- xonar_enable_output(chip);
-}
-
-static void xonar_d1_resume(struct oxygen *chip)
-{
- oxygen_set_bits8(chip, OXYGEN_FUNCTION, OXYGEN_FUNCTION_RESET_CODEC);
- msleep(1);
- cs43xx_init(chip);
- xonar_enable_output(chip);
-}
-
-static void xonar_hdav_resume(struct oxygen *chip)
-{
- struct xonar_data *data = chip->model_data;
- u8 param;
-
- oxygen_reset_uart(chip);
- param = 0;
- hdmi_write_command(chip, 0x61, 1, &param);
- param = 1;
- hdmi_write_command(chip, 0x74, 1, &param);
- hdmi_write_command(chip, 0x54, 5, data->hdmi_params);
- pcm1796_init(chip);
- xonar_enable_output(chip);
-}
-
-static void xonar_st_resume(struct oxygen *chip)
-{
- pcm1796_init(chip);
- xonar_enable_output(chip);
-}
-
-static void xonar_hdav_pcm_hardware_filter(unsigned int channel,
- struct snd_pcm_hardware *hardware)
-{
- if (channel == PCM_MULTICH) {
- hardware->rates = SNDRV_PCM_RATE_44100 |
- SNDRV_PCM_RATE_48000 |
- SNDRV_PCM_RATE_96000 |
- SNDRV_PCM_RATE_192000;
- hardware->rate_min = 44100;
- }
-}
-
-static void set_pcm1796_params(struct oxygen *chip,
- struct snd_pcm_hw_params *params)
-{
- struct xonar_data *data = chip->model_data;
- unsigned int i;
-
- data->pcm1796_oversampling =
- params_rate(params) >= 96000 ? PCM1796_OS_32 : PCM1796_OS_64;
- for (i = 0; i < data->dacs; ++i)
- pcm1796_write(chip, i, 20, data->pcm1796_oversampling);
-}
-
-static void set_cs53x1_params(struct oxygen *chip,
- struct snd_pcm_hw_params *params)
-{
- unsigned int value;
-
- if (params_rate(params) <= 54000)
- value = GPIO_CS53x1_M_SINGLE;
- else if (params_rate(params) <= 108000)
- value = GPIO_CS53x1_M_DOUBLE;
- else
- value = GPIO_CS53x1_M_QUAD;
- oxygen_write16_masked(chip, OXYGEN_GPIO_DATA,
- value, GPIO_CS53x1_M_MASK);
-}
-
-static void set_cs43xx_params(struct oxygen *chip,
- struct snd_pcm_hw_params *params)
-{
- struct xonar_data *data = chip->model_data;
-
- data->cs4398_fm = CS4398_DEM_NONE | CS4398_DIF_LJUST;
- data->cs4362a_fm = CS4362A_ATAPI_B_R | CS4362A_ATAPI_A_L;
- if (params_rate(params) <= 50000) {
- data->cs4398_fm |= CS4398_FM_SINGLE;
- data->cs4362a_fm |= CS4362A_FM_SINGLE;
- } else if (params_rate(params) <= 100000) {
- data->cs4398_fm |= CS4398_FM_DOUBLE;
- data->cs4362a_fm |= CS4362A_FM_DOUBLE;
- } else {
- data->cs4398_fm |= CS4398_FM_QUAD;
- data->cs4362a_fm |= CS4362A_FM_QUAD;
- }
- cs4398_write(chip, 2, data->cs4398_fm);
- cs4362a_write(chip, 0x06, data->cs4362a_fm);
- cs4362a_write(chip, 0x09, data->cs4362a_fm);
- cs4362a_write(chip, 0x0c, data->cs4362a_fm);
-}
-
-static void set_hdmi_params(struct oxygen *chip,
- struct snd_pcm_hw_params *params)
-{
- struct xonar_data *data = chip->model_data;
-
- data->hdmi_params[0] = 0; /* 1 = non-audio */
- switch (params_rate(params)) {
- case 44100:
- data->hdmi_params[1] = IEC958_AES3_CON_FS_44100;
- break;
- case 48000:
- data->hdmi_params[1] = IEC958_AES3_CON_FS_48000;
- break;
- default: /* 96000 */
- data->hdmi_params[1] = IEC958_AES3_CON_FS_96000;
- break;
- case 192000:
- data->hdmi_params[1] = IEC958_AES3_CON_FS_192000;
- break;
- }
- data->hdmi_params[2] = params_channels(params) / 2 - 1;
- if (params_format(params) == SNDRV_PCM_FORMAT_S16_LE)
- data->hdmi_params[3] = 0;
- else
- data->hdmi_params[3] = 0xc0;
- data->hdmi_params[4] = 1; /* ? */
- hdmi_write_command(chip, 0x54, 5, data->hdmi_params);
-}
-
-static void set_hdav_params(struct oxygen *chip,
- struct snd_pcm_hw_params *params)
-{
- set_pcm1796_params(chip, params);
- set_hdmi_params(chip, params);
-}
-
-static void xonar_gpio_changed(struct oxygen *chip)
-{
- struct xonar_data *data = chip->model_data;
- u8 has_power;
-
- has_power = !!(oxygen_read8(chip, data->ext_power_reg)
- & data->ext_power_bit);
- if (has_power != data->has_power) {
- data->has_power = has_power;
- if (has_power) {
- snd_printk(KERN_NOTICE "power restored\n");
- } else {
- snd_printk(KERN_CRIT
- "Hey! Don't unplug the power cable!\n");
- /* TODO: stop PCMs */
- }
- }
-}
-
-static void xonar_hdav_uart_input(struct oxygen *chip)
-{
- if (chip->uart_input_count >= 2 &&
- chip->uart_input[chip->uart_input_count - 2] == 'O' &&
- chip->uart_input[chip->uart_input_count - 1] == 'K') {
- printk(KERN_DEBUG "message from Xonar HDAV HDMI chip received:\n");
- print_hex_dump_bytes("", DUMP_PREFIX_OFFSET,
- chip->uart_input, chip->uart_input_count);
- chip->uart_input_count = 0;
- }
-}
-
-static int gpio_bit_switch_get(struct snd_kcontrol *ctl,
- struct snd_ctl_elem_value *value)
-{
- struct oxygen *chip = ctl->private_data;
- u16 bit = ctl->private_value;
-
- value->value.integer.value[0] =
- !!(oxygen_read16(chip, OXYGEN_GPIO_DATA) & bit);
- return 0;
-}
-
-static int gpio_bit_switch_put(struct snd_kcontrol *ctl,
- struct snd_ctl_elem_value *value)
-{
- struct oxygen *chip = ctl->private_data;
- u16 bit = ctl->private_value;
- u16 old_bits, new_bits;
- int changed;
-
- spin_lock_irq(&chip->reg_lock);
- old_bits = oxygen_read16(chip, OXYGEN_GPIO_DATA);
- if (value->value.integer.value[0])
- new_bits = old_bits | bit;
- else
- new_bits = old_bits & ~bit;
- changed = new_bits != old_bits;
- if (changed)
- oxygen_write16(chip, OXYGEN_GPIO_DATA, new_bits);
- spin_unlock_irq(&chip->reg_lock);
- return changed;
-}
-
-static const struct snd_kcontrol_new alt_switch = {
- .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
- .name = "Analog Loopback Switch",
- .info = snd_ctl_boolean_mono_info,
- .get = gpio_bit_switch_get,
- .put = gpio_bit_switch_put,
- .private_value = GPIO_D2_ALT,
-};
-
-static const struct snd_kcontrol_new front_panel_switch = {
- .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
- .name = "Front Panel Switch",
- .info = snd_ctl_boolean_mono_info,
- .get = gpio_bit_switch_get,
- .put = gpio_bit_switch_put,
- .private_value = GPIO_DX_FRONT_PANEL,
-};
-
-static int st_output_switch_info(struct snd_kcontrol *ctl,
- struct snd_ctl_elem_info *info)
-{
- static const char *const names[3] = {
- "Speakers", "Headphones", "FP Headphones"
- };
-
- info->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED;
- info->count = 1;
- info->value.enumerated.items = 3;
- if (info->value.enumerated.item >= 3)
- info->value.enumerated.item = 2;
- strcpy(info->value.enumerated.name, names[info->value.enumerated.item]);
- return 0;
-}
-
-static int st_output_switch_get(struct snd_kcontrol *ctl,
- struct snd_ctl_elem_value *value)
-{
- struct oxygen *chip = ctl->private_data;
- u16 gpio;
-
- gpio = oxygen_read16(chip, OXYGEN_GPIO_DATA);
- if (!(gpio & GPIO_ST_HP))
- value->value.enumerated.item[0] = 0;
- else if (gpio & GPIO_ST_HP_REAR)
- value->value.enumerated.item[0] = 1;
- else
- value->value.enumerated.item[0] = 2;
- return 0;
-}
-
-
-static int st_output_switch_put(struct snd_kcontrol *ctl,
- struct snd_ctl_elem_value *value)
-{
- struct oxygen *chip = ctl->private_data;
- u16 gpio_old, gpio;
-
- mutex_lock(&chip->mutex);
- gpio_old = oxygen_read16(chip, OXYGEN_GPIO_DATA);
- gpio = gpio_old;
- switch (value->value.enumerated.item[0]) {
- case 0:
- gpio &= ~(GPIO_ST_HP | GPIO_ST_HP_REAR);
- break;
- case 1:
- gpio |= GPIO_ST_HP | GPIO_ST_HP_REAR;
- break;
- case 2:
- gpio = (gpio | GPIO_ST_HP) & ~GPIO_ST_HP_REAR;
- break;
- }
- oxygen_write16(chip, OXYGEN_GPIO_DATA, gpio);
- mutex_unlock(&chip->mutex);
- return gpio != gpio_old;
-}
-
-static const struct snd_kcontrol_new st_output_switch = {
- .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
- .name = "Analog Output",
- .info = st_output_switch_info,
- .get = st_output_switch_get,
- .put = st_output_switch_put,
-};
-
-static void xonar_line_mic_ac97_switch(struct oxygen *chip,
- unsigned int reg, unsigned int mute)
-{
- if (reg == AC97_LINE) {
- spin_lock_irq(&chip->reg_lock);
- oxygen_write16_masked(chip, OXYGEN_GPIO_DATA,
- mute ? GPIO_DX_INPUT_ROUTE : 0,
- GPIO_DX_INPUT_ROUTE);
- spin_unlock_irq(&chip->reg_lock);
- }
-}
-
-static const DECLARE_TLV_DB_SCALE(pcm1796_db_scale, -6000, 50, 0);
-static const DECLARE_TLV_DB_SCALE(cs4362a_db_scale, -6000, 100, 0);
-
-static int xonar_d2_control_filter(struct snd_kcontrol_new *template)
-{
- if (!strncmp(template->name, "CD Capture ", 11))
- /* CD in is actually connected to the video in pin */
- template->private_value ^= AC97_CD ^ AC97_VIDEO;
- return 0;
-}
-
-static int xonar_d1_control_filter(struct snd_kcontrol_new *template)
-{
- if (!strncmp(template->name, "CD Capture ", 11))
- return 1; /* no CD input */
- return 0;
-}
-
-static int xonar_st_control_filter(struct snd_kcontrol_new *template)
-{
- if (!strncmp(template->name, "CD Capture ", 11))
- return 1; /* no CD input */
- if (!strcmp(template->name, "Stereo Upmixing"))
- return 1; /* stereo only - we don't need upmixing */
- return 0;
-}
-
-static int xonar_d2_mixer_init(struct oxygen *chip)
-{
- return snd_ctl_add(chip->card, snd_ctl_new1(&alt_switch, chip));
-}
-
-static int xonar_d1_mixer_init(struct oxygen *chip)
-{
- return snd_ctl_add(chip->card, snd_ctl_new1(&front_panel_switch, chip));
-}
-
-static int xonar_st_mixer_init(struct oxygen *chip)
-{
- return snd_ctl_add(chip->card, snd_ctl_new1(&st_output_switch, chip));
-}
-
-static const struct oxygen_model model_xonar_d2 = {
- .longname = "Asus Virtuoso 200",
- .chip = "AV200",
- .init = xonar_d2_init,
- .control_filter = xonar_d2_control_filter,
- .mixer_init = xonar_d2_mixer_init,
- .cleanup = xonar_d2_cleanup,
- .suspend = xonar_d2_suspend,
- .resume = xonar_d2_resume,
- .set_dac_params = set_pcm1796_params,
- .set_adc_params = set_cs53x1_params,
- .update_dac_volume = update_pcm1796_volume,
- .update_dac_mute = update_pcm1796_mute,
- .dac_tlv = pcm1796_db_scale,
- .model_data_size = sizeof(struct xonar_data),
- .device_config = PLAYBACK_0_TO_I2S |
- PLAYBACK_1_TO_SPDIF |
- CAPTURE_0_FROM_I2S_2 |
- CAPTURE_1_FROM_SPDIF |
- MIDI_OUTPUT |
- MIDI_INPUT,
- .dac_channels = 8,
- .dac_volume_min = 255 - 2*60,
- .dac_volume_max = 255,
- .misc_flags = OXYGEN_MISC_MIDI,
- .function_flags = OXYGEN_FUNCTION_SPI |
- OXYGEN_FUNCTION_ENABLE_SPI_4_5,
- .dac_i2s_format = OXYGEN_I2S_FORMAT_LJUST,
- .adc_i2s_format = OXYGEN_I2S_FORMAT_LJUST,
-};
-
-static const struct oxygen_model model_xonar_d1 = {
- .longname = "Asus Virtuoso 100",
- .chip = "AV200",
- .init = xonar_d1_init,
- .control_filter = xonar_d1_control_filter,
- .mixer_init = xonar_d1_mixer_init,
- .cleanup = xonar_d1_cleanup,
- .suspend = xonar_d1_suspend,
- .resume = xonar_d1_resume,
- .set_dac_params = set_cs43xx_params,
- .set_adc_params = set_cs53x1_params,
- .update_dac_volume = update_cs43xx_volume,
- .update_dac_mute = update_cs43xx_mute,
- .ac97_switch = xonar_line_mic_ac97_switch,
- .dac_tlv = cs4362a_db_scale,
- .model_data_size = sizeof(struct xonar_data),
- .device_config = PLAYBACK_0_TO_I2S |
- PLAYBACK_1_TO_SPDIF |
- CAPTURE_0_FROM_I2S_2,
- .dac_channels = 8,
- .dac_volume_min = 127 - 60,
- .dac_volume_max = 127,
- .function_flags = OXYGEN_FUNCTION_2WIRE,
- .dac_i2s_format = OXYGEN_I2S_FORMAT_LJUST,
- .adc_i2s_format = OXYGEN_I2S_FORMAT_LJUST,
-};
-
-static const struct oxygen_model model_xonar_hdav = {
- .longname = "Asus Virtuoso 200",
- .chip = "AV200",
- .init = xonar_hdav_init,
- .cleanup = xonar_hdav_cleanup,
- .suspend = xonar_hdav_suspend,
- .resume = xonar_hdav_resume,
- .pcm_hardware_filter = xonar_hdav_pcm_hardware_filter,
- .set_dac_params = set_hdav_params,
- .set_adc_params = set_cs53x1_params,
- .update_dac_volume = update_pcm1796_volume,
- .update_dac_mute = update_pcm1796_mute,
- .uart_input = xonar_hdav_uart_input,
- .ac97_switch = xonar_line_mic_ac97_switch,
- .dac_tlv = pcm1796_db_scale,
- .model_data_size = sizeof(struct xonar_data),
- .device_config = PLAYBACK_0_TO_I2S |
- PLAYBACK_1_TO_SPDIF |
- CAPTURE_0_FROM_I2S_2 |
- CAPTURE_1_FROM_SPDIF,
- .dac_channels = 8,
- .dac_volume_min = 255 - 2*60,
- .dac_volume_max = 255,
- .misc_flags = OXYGEN_MISC_MIDI,
- .function_flags = OXYGEN_FUNCTION_2WIRE,
- .dac_i2s_format = OXYGEN_I2S_FORMAT_LJUST,
- .adc_i2s_format = OXYGEN_I2S_FORMAT_LJUST,
-};
-
-static const struct oxygen_model model_xonar_st = {
- .longname = "Asus Virtuoso 100",
- .chip = "AV200",
- .init = xonar_st_init,
- .control_filter = xonar_st_control_filter,
- .mixer_init = xonar_st_mixer_init,
- .cleanup = xonar_st_cleanup,
- .suspend = xonar_st_suspend,
- .resume = xonar_st_resume,
- .set_dac_params = set_pcm1796_params,
- .set_adc_params = set_cs53x1_params,
- .update_dac_volume = update_pcm1796_volume,
- .update_dac_mute = update_pcm1796_mute,
- .ac97_switch = xonar_line_mic_ac97_switch,
- .dac_tlv = pcm1796_db_scale,
- .model_data_size = sizeof(struct xonar_data),
- .device_config = PLAYBACK_0_TO_I2S |
- PLAYBACK_1_TO_SPDIF |
- CAPTURE_0_FROM_I2S_2,
- .dac_channels = 2,
- .dac_volume_min = 255 - 2*60,
- .dac_volume_max = 255,
- .function_flags = OXYGEN_FUNCTION_2WIRE,
- .dac_i2s_format = OXYGEN_I2S_FORMAT_LJUST,
- .adc_i2s_format = OXYGEN_I2S_FORMAT_LJUST,
-};
-
static int __devinit get_xonar_model(struct oxygen *chip,
const struct pci_device_id *id)
{
- static const struct oxygen_model *const models[] = {
- [MODEL_D1] = &model_xonar_d1,
- [MODEL_DX] = &model_xonar_d1,
- [MODEL_D2] = &model_xonar_d2,
- [MODEL_D2X] = &model_xonar_d2,
- [MODEL_HDAV] = &model_xonar_hdav,
- [MODEL_ST] = &model_xonar_st,
- [MODEL_STX] = &model_xonar_st,
- };
- static const char *const names[] = {
- [MODEL_D1] = "Xonar D1",
- [MODEL_DX] = "Xonar DX",
- [MODEL_D2] = "Xonar D2",
- [MODEL_D2X] = "Xonar D2X",
- [MODEL_HDAV] = "Xonar HDAV1.3",
- [MODEL_HDAV_H6] = "Xonar HDAV1.3+H6",
- [MODEL_ST] = "Xonar Essence ST",
- [MODEL_ST_H6] = "Xonar Essence ST+H6",
- [MODEL_STX] = "Xonar Essence STX",
- };
- unsigned int model = id->driver_data;
-
- if (model >= ARRAY_SIZE(models) || !models[model])
- return -EINVAL;
- chip->model = *models[model];
-
- switch (model) {
- case MODEL_D2X:
- chip->model.init = xonar_d2x_init;
- break;
- case MODEL_DX:
- chip->model.init = xonar_dx_init;
- break;
- case MODEL_HDAV:
- oxygen_clear_bits16(chip, OXYGEN_GPIO_CONTROL, GPIO_DB_MASK);
- switch (oxygen_read16(chip, OXYGEN_GPIO_DATA) & GPIO_DB_MASK) {
- case GPIO_DB_H6:
- model = MODEL_HDAV_H6;
- break;
- case GPIO_DB_XX:
- snd_printk(KERN_ERR "unknown daughterboard\n");
- return -ENODEV;
- }
- break;
- case MODEL_ST:
- oxygen_clear_bits16(chip, OXYGEN_GPIO_CONTROL, GPIO_DB_MASK);
- switch (oxygen_read16(chip, OXYGEN_GPIO_DATA) & GPIO_DB_MASK) {
- case GPIO_DB_H6:
- model = MODEL_ST_H6;
- break;
- }
- break;
- case MODEL_STX:
- chip->model.init = xonar_stx_init;
- oxygen_clear_bits16(chip, OXYGEN_GPIO_CONTROL, GPIO_DB_MASK);
- break;
- }
-
- chip->model.shortname = names[model];
- chip->model.private_data = model;
- return 0;
+ if (get_xonar_pcm179x_model(chip, id) >= 0)
+ return 0;
+ if (get_xonar_cs43xx_model(chip, id) >= 0)
+ return 0;
+ return -EINVAL;
}
static int __devinit xonar_probe(struct pci_dev *pci,
diff --git a/sound/pci/oxygen/xonar.h b/sound/pci/oxygen/xonar.h
new file mode 100644
index 000000000000..89b3ed814d64
--- /dev/null
+++ b/sound/pci/oxygen/xonar.h
@@ -0,0 +1,50 @@
+#ifndef XONAR_H_INCLUDED
+#define XONAR_H_INCLUDED
+
+#include "oxygen.h"
+
+struct xonar_generic {
+ unsigned int anti_pop_delay;
+ u16 output_enable_bit;
+ u8 ext_power_reg;
+ u8 ext_power_int_reg;
+ u8 ext_power_bit;
+ u8 has_power;
+};
+
+struct xonar_hdmi {
+ u8 params[5];
+};
+
+/* generic helper functions */
+
+void xonar_enable_output(struct oxygen *chip);
+void xonar_disable_output(struct oxygen *chip);
+void xonar_init_ext_power(struct oxygen *chip);
+void xonar_init_cs53x1(struct oxygen *chip);
+void xonar_set_cs53x1_params(struct oxygen *chip,
+ struct snd_pcm_hw_params *params);
+int xonar_gpio_bit_switch_get(struct snd_kcontrol *ctl,
+ struct snd_ctl_elem_value *value);
+int xonar_gpio_bit_switch_put(struct snd_kcontrol *ctl,
+ struct snd_ctl_elem_value *value);
+
+/* model-specific card drivers */
+
+int get_xonar_pcm179x_model(struct oxygen *chip,
+ const struct pci_device_id *id);
+int get_xonar_cs43xx_model(struct oxygen *chip,
+ const struct pci_device_id *id);
+
+/* HDMI helper functions */
+
+void xonar_hdmi_init(struct oxygen *chip, struct xonar_hdmi *data);
+void xonar_hdmi_cleanup(struct oxygen *chip);
+void xonar_hdmi_resume(struct oxygen *chip, struct xonar_hdmi *hdmi);
+void xonar_hdmi_pcm_hardware_filter(unsigned int channel,
+ struct snd_pcm_hardware *hardware);
+void xonar_set_hdmi_params(struct oxygen *chip, struct xonar_hdmi *hdmi,
+ struct snd_pcm_hw_params *params);
+void xonar_hdmi_uart_input(struct oxygen *chip);
+
+#endif
diff --git a/sound/pci/oxygen/xonar_cs43xx.c b/sound/pci/oxygen/xonar_cs43xx.c
new file mode 100644
index 000000000000..16c226bfcd2b
--- /dev/null
+++ b/sound/pci/oxygen/xonar_cs43xx.c
@@ -0,0 +1,434 @@
+/*
+ * card driver for models with CS4398/CS4362A DACs (Xonar D1/DX)
+ *
+ * Copyright (c) Clemens Ladisch <clemens@ladisch.de>
+ *
+ *
+ * This driver is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License, version 2.
+ *
+ * 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 driver; if not, see <http://www.gnu.org/licenses/>.
+ */
+
+/*
+ * Xonar D1/DX
+ * -----------
+ *
+ * CMI8788:
+ *
+ * I²C <-> CS4398 (front)
+ * <-> CS4362A (surround, center/LFE, back)
+ *
+ * GPI 0 <- external power present (DX only)
+ *
+ * GPIO 0 -> enable output to speakers
+ * GPIO 1 -> enable front panel I/O
+ * GPIO 2 -> M0 of CS5361
+ * GPIO 3 -> M1 of CS5361
+ * GPIO 8 -> route input jack to line-in (0) or mic-in (1)
+ *
+ * CS4398:
+ *
+ * AD0 <- 1
+ * AD1 <- 1
+ *
+ * CS4362A:
+ *
+ * AD0 <- 0
+ *
+ * CM9780:
+ *
+ * GPO 0 -> route line-in (0) or AC97 output (1) to CS5361 input
+ */
+
+#include <linux/pci.h>
+#include <linux/delay.h>
+#include <sound/ac97_codec.h>
+#include <sound/control.h>
+#include <sound/core.h>
+#include <sound/pcm.h>
+#include <sound/pcm_params.h>
+#include <sound/tlv.h>
+#include "xonar.h"
+#include "cs4398.h"
+#include "cs4362a.h"
+
+#define GPI_EXT_POWER 0x01
+#define GPIO_D1_OUTPUT_ENABLE 0x0001
+#define GPIO_D1_FRONT_PANEL 0x0002
+#define GPIO_D1_INPUT_ROUTE 0x0100
+
+#define I2C_DEVICE_CS4398 0x9e /* 10011, AD1=1, AD0=1, /W=0 */
+#define I2C_DEVICE_CS4362A 0x30 /* 001100, AD0=0, /W=0 */
+
+struct xonar_cs43xx {
+ struct xonar_generic generic;
+ u8 cs4398_regs[8];
+ u8 cs4362a_regs[15];
+};
+
+static void cs4398_write(struct oxygen *chip, u8 reg, u8 value)
+{
+ struct xonar_cs43xx *data = chip->model_data;
+
+ oxygen_write_i2c(chip, I2C_DEVICE_CS4398, reg, value);
+ if (reg < ARRAY_SIZE(data->cs4398_regs))
+ data->cs4398_regs[reg] = value;
+}
+
+static void cs4398_write_cached(struct oxygen *chip, u8 reg, u8 value)
+{
+ struct xonar_cs43xx *data = chip->model_data;
+
+ if (value != data->cs4398_regs[reg])
+ cs4398_write(chip, reg, value);
+}
+
+static void cs4362a_write(struct oxygen *chip, u8 reg, u8 value)
+{
+ struct xonar_cs43xx *data = chip->model_data;
+
+ oxygen_write_i2c(chip, I2C_DEVICE_CS4362A, reg, value);
+ if (reg < ARRAY_SIZE(data->cs4362a_regs))
+ data->cs4362a_regs[reg] = value;
+}
+
+static void cs4362a_write_cached(struct oxygen *chip, u8 reg, u8 value)
+{
+ struct xonar_cs43xx *data = chip->model_data;
+
+ if (value != data->cs4362a_regs[reg])
+ cs4362a_write(chip, reg, value);
+}
+
+static void cs43xx_registers_init(struct oxygen *chip)
+{
+ struct xonar_cs43xx *data = chip->model_data;
+ unsigned int i;
+
+ /* set CPEN (control port mode) and power down */
+ cs4398_write(chip, 8, CS4398_CPEN | CS4398_PDN);
+ cs4362a_write(chip, 0x01, CS4362A_PDN | CS4362A_CPEN);
+ /* configure */
+ cs4398_write(chip, 2, data->cs4398_regs[2]);
+ cs4398_write(chip, 3, CS4398_ATAPI_B_R | CS4398_ATAPI_A_L);
+ cs4398_write(chip, 4, data->cs4398_regs[4]);
+ cs4398_write(chip, 5, data->cs4398_regs[5]);
+ cs4398_write(chip, 6, data->cs4398_regs[6]);
+ cs4398_write(chip, 7, data->cs4398_regs[7]);
+ cs4362a_write(chip, 0x02, CS4362A_DIF_LJUST);
+ cs4362a_write(chip, 0x03, CS4362A_MUTEC_6 | CS4362A_AMUTE |
+ CS4362A_RMP_UP | CS4362A_ZERO_CROSS | CS4362A_SOFT_RAMP);
+ cs4362a_write(chip, 0x04, data->cs4362a_regs[0x04]);
+ cs4362a_write(chip, 0x05, 0);
+ for (i = 6; i <= 14; ++i)
+ cs4362a_write(chip, i, data->cs4362a_regs[i]);
+ /* clear power down */
+ cs4398_write(chip, 8, CS4398_CPEN);
+ cs4362a_write(chip, 0x01, CS4362A_CPEN);
+}
+
+static void xonar_d1_init(struct oxygen *chip)
+{
+ struct xonar_cs43xx *data = chip->model_data;
+
+ data->generic.anti_pop_delay = 800;
+ data->generic.output_enable_bit = GPIO_D1_OUTPUT_ENABLE;
+ data->cs4398_regs[2] =
+ CS4398_FM_SINGLE | CS4398_DEM_NONE | CS4398_DIF_LJUST;
+ data->cs4398_regs[4] = CS4398_MUTEP_LOW |
+ CS4398_MUTE_B | CS4398_MUTE_A | CS4398_PAMUTE;
+ data->cs4398_regs[5] = 60 * 2;
+ data->cs4398_regs[6] = 60 * 2;
+ data->cs4398_regs[7] = CS4398_RMP_DN | CS4398_RMP_UP |
+ CS4398_ZERO_CROSS | CS4398_SOFT_RAMP;
+ data->cs4362a_regs[4] = CS4362A_RMP_DN | CS4362A_DEM_NONE;
+ data->cs4362a_regs[6] = CS4362A_FM_SINGLE |
+ CS4362A_ATAPI_B_R | CS4362A_ATAPI_A_L;
+ data->cs4362a_regs[7] = 60 | CS4362A_MUTE;
+ data->cs4362a_regs[8] = 60 | CS4362A_MUTE;
+ data->cs4362a_regs[9] = data->cs4362a_regs[6];
+ data->cs4362a_regs[10] = 60 | CS4362A_MUTE;
+ data->cs4362a_regs[11] = 60 | CS4362A_MUTE;
+ data->cs4362a_regs[12] = data->cs4362a_regs[6];
+ data->cs4362a_regs[13] = 60 | CS4362A_MUTE;
+ data->cs4362a_regs[14] = 60 | CS4362A_MUTE;
+
+ oxygen_write16(chip, OXYGEN_2WIRE_BUS_STATUS,
+ OXYGEN_2WIRE_LENGTH_8 |
+ OXYGEN_2WIRE_INTERRUPT_MASK |
+ OXYGEN_2WIRE_SPEED_FAST);
+
+ cs43xx_registers_init(chip);
+
+ oxygen_set_bits16(chip, OXYGEN_GPIO_CONTROL,
+ GPIO_D1_FRONT_PANEL | GPIO_D1_INPUT_ROUTE);
+ oxygen_clear_bits16(chip, OXYGEN_GPIO_DATA,
+ GPIO_D1_FRONT_PANEL | GPIO_D1_INPUT_ROUTE);
+
+ xonar_init_cs53x1(chip);
+ xonar_enable_output(chip);
+
+ snd_component_add(chip->card, "CS4398");
+ snd_component_add(chip->card, "CS4362A");
+ snd_component_add(chip->card, "CS5361");
+}
+
+static void xonar_dx_init(struct oxygen *chip)
+{
+ struct xonar_cs43xx *data = chip->model_data;
+
+ data->generic.ext_power_reg = OXYGEN_GPI_DATA;
+ data->generic.ext_power_int_reg = OXYGEN_GPI_INTERRUPT_MASK;
+ data->generic.ext_power_bit = GPI_EXT_POWER;
+ xonar_init_ext_power(chip);
+ xonar_d1_init(chip);
+}
+
+static void xonar_d1_cleanup(struct oxygen *chip)
+{
+ xonar_disable_output(chip);
+ cs4362a_write(chip, 0x01, CS4362A_PDN | CS4362A_CPEN);
+ oxygen_clear_bits8(chip, OXYGEN_FUNCTION, OXYGEN_FUNCTION_RESET_CODEC);
+}
+
+static void xonar_d1_suspend(struct oxygen *chip)
+{
+ xonar_d1_cleanup(chip);
+}
+
+static void xonar_d1_resume(struct oxygen *chip)
+{
+ oxygen_set_bits8(chip, OXYGEN_FUNCTION, OXYGEN_FUNCTION_RESET_CODEC);
+ msleep(1);
+ cs43xx_registers_init(chip);
+ xonar_enable_output(chip);
+}
+
+static void set_cs43xx_params(struct oxygen *chip,
+ struct snd_pcm_hw_params *params)
+{
+ struct xonar_cs43xx *data = chip->model_data;
+ u8 cs4398_fm, cs4362a_fm;
+
+ if (params_rate(params) <= 50000) {
+ cs4398_fm = CS4398_FM_SINGLE;
+ cs4362a_fm = CS4362A_FM_SINGLE;
+ } else if (params_rate(params) <= 100000) {
+ cs4398_fm = CS4398_FM_DOUBLE;
+ cs4362a_fm = CS4362A_FM_DOUBLE;
+ } else {
+ cs4398_fm = CS4398_FM_QUAD;
+ cs4362a_fm = CS4362A_FM_QUAD;
+ }
+ cs4398_fm |= CS4398_DEM_NONE | CS4398_DIF_LJUST;
+ cs4398_write_cached(chip, 2, cs4398_fm);
+ cs4362a_fm |= data->cs4362a_regs[6] & ~CS4362A_FM_MASK;
+ cs4362a_write_cached(chip, 6, cs4362a_fm);
+ cs4362a_write_cached(chip, 12, cs4362a_fm);
+ cs4362a_fm &= CS4362A_FM_MASK;
+ cs4362a_fm |= data->cs4362a_regs[9] & ~CS4362A_FM_MASK;
+ cs4362a_write_cached(chip, 9, cs4362a_fm);
+}
+
+static void update_cs4362a_volumes(struct oxygen *chip)
+{
+ unsigned int i;
+ u8 mute;
+
+ mute = chip->dac_mute ? CS4362A_MUTE : 0;
+ for (i = 0; i < 6; ++i)
+ cs4362a_write_cached(chip, 7 + i + i / 2,
+ (127 - chip->dac_volume[2 + i]) | mute);
+}
+
+static void update_cs43xx_volume(struct oxygen *chip)
+{
+ cs4398_write_cached(chip, 5, (127 - chip->dac_volume[0]) * 2);
+ cs4398_write_cached(chip, 6, (127 - chip->dac_volume[1]) * 2);
+ update_cs4362a_volumes(chip);
+}
+
+static void update_cs43xx_mute(struct oxygen *chip)
+{
+ u8 reg;
+
+ reg = CS4398_MUTEP_LOW | CS4398_PAMUTE;
+ if (chip->dac_mute)
+ reg |= CS4398_MUTE_B | CS4398_MUTE_A;
+ cs4398_write_cached(chip, 4, reg);
+ update_cs4362a_volumes(chip);
+}
+
+static void update_cs43xx_center_lfe_mix(struct oxygen *chip, bool mixed)
+{
+ struct xonar_cs43xx *data = chip->model_data;
+ u8 reg;
+
+ reg = data->cs4362a_regs[9] & ~CS4362A_ATAPI_MASK;
+ if (mixed)
+ reg |= CS4362A_ATAPI_B_LR | CS4362A_ATAPI_A_LR;
+ else
+ reg |= CS4362A_ATAPI_B_R | CS4362A_ATAPI_A_L;
+ cs4362a_write_cached(chip, 9, reg);
+}
+
+static const struct snd_kcontrol_new front_panel_switch = {
+ .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+ .name = "Front Panel Switch",
+ .info = snd_ctl_boolean_mono_info,
+ .get = xonar_gpio_bit_switch_get,
+ .put = xonar_gpio_bit_switch_put,
+ .private_value = GPIO_D1_FRONT_PANEL,
+};
+
+static int rolloff_info(struct snd_kcontrol *ctl,
+ struct snd_ctl_elem_info *info)
+{
+ static const char *const names[2] = {
+ "Fast Roll-off", "Slow Roll-off"
+ };
+
+ info->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED;
+ info->count = 1;
+ info->value.enumerated.items = 2;
+ if (info->value.enumerated.item >= 2)
+ info->value.enumerated.item = 1;
+ strcpy(info->value.enumerated.name, names[info->value.enumerated.item]);
+ return 0;
+}
+
+static int rolloff_get(struct snd_kcontrol *ctl,
+ struct snd_ctl_elem_value *value)
+{
+ struct oxygen *chip = ctl->private_data;
+ struct xonar_cs43xx *data = chip->model_data;
+
+ value->value.enumerated.item[0] =
+ (data->cs4398_regs[7] & CS4398_FILT_SEL) != 0;
+ return 0;
+}
+
+static int rolloff_put(struct snd_kcontrol *ctl,
+ struct snd_ctl_elem_value *value)
+{
+ struct oxygen *chip = ctl->private_data;
+ struct xonar_cs43xx *data = chip->model_data;
+ int changed;
+ u8 reg;
+
+ mutex_lock(&chip->mutex);
+ reg = data->cs4398_regs[7];
+ if (value->value.enumerated.item[0])
+ reg |= CS4398_FILT_SEL;
+ else
+ reg &= ~CS4398_FILT_SEL;
+ changed = reg != data->cs4398_regs[7];
+ if (changed) {
+ cs4398_write(chip, 7, reg);
+ if (reg & CS4398_FILT_SEL)
+ reg = data->cs4362a_regs[0x04] | CS4362A_FILT_SEL;
+ else
+ reg = data->cs4362a_regs[0x04] & ~CS4362A_FILT_SEL;
+ cs4362a_write(chip, 0x04, reg);
+ }
+ mutex_unlock(&chip->mutex);
+ return changed;
+}
+
+static const struct snd_kcontrol_new rolloff_control = {
+ .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+ .name = "DAC Filter Playback Enum",
+ .info = rolloff_info,
+ .get = rolloff_get,
+ .put = rolloff_put,
+};
+
+static void xonar_d1_line_mic_ac97_switch(struct oxygen *chip,
+ unsigned int reg, unsigned int mute)
+{
+ if (reg == AC97_LINE) {
+ spin_lock_irq(&chip->reg_lock);
+ oxygen_write16_masked(chip, OXYGEN_GPIO_DATA,
+ mute ? GPIO_D1_INPUT_ROUTE : 0,
+ GPIO_D1_INPUT_ROUTE);
+ spin_unlock_irq(&chip->reg_lock);
+ }
+}
+
+static const DECLARE_TLV_DB_SCALE(cs4362a_db_scale, -6000, 100, 0);
+
+static int xonar_d1_control_filter(struct snd_kcontrol_new *template)
+{
+ if (!strncmp(template->name, "CD Capture ", 11))
+ return 1; /* no CD input */
+ return 0;
+}
+
+static int xonar_d1_mixer_init(struct oxygen *chip)
+{
+ int err;
+
+ err = snd_ctl_add(chip->card, snd_ctl_new1(&front_panel_switch, chip));
+ if (err < 0)
+ return err;
+ err = snd_ctl_add(chip->card, snd_ctl_new1(&rolloff_control, chip));
+ if (err < 0)
+ return err;
+ return 0;
+}
+
+static const struct oxygen_model model_xonar_d1 = {
+ .longname = "Asus Virtuoso 100",
+ .chip = "AV200",
+ .init = xonar_d1_init,
+ .control_filter = xonar_d1_control_filter,
+ .mixer_init = xonar_d1_mixer_init,
+ .cleanup = xonar_d1_cleanup,
+ .suspend = xonar_d1_suspend,
+ .resume = xonar_d1_resume,
+ .get_i2s_mclk = oxygen_default_i2s_mclk,
+ .set_dac_params = set_cs43xx_params,
+ .set_adc_params = xonar_set_cs53x1_params,
+ .update_dac_volume = update_cs43xx_volume,
+ .update_dac_mute = update_cs43xx_mute,
+ .update_center_lfe_mix = update_cs43xx_center_lfe_mix,
+ .ac97_switch = xonar_d1_line_mic_ac97_switch,
+ .dac_tlv = cs4362a_db_scale,
+ .model_data_size = sizeof(struct xonar_cs43xx),
+ .device_config = PLAYBACK_0_TO_I2S |
+ PLAYBACK_1_TO_SPDIF |
+ CAPTURE_0_FROM_I2S_2,
+ .dac_channels = 8,
+ .dac_volume_min = 127 - 60,
+ .dac_volume_max = 127,
+ .function_flags = OXYGEN_FUNCTION_2WIRE,
+ .dac_i2s_format = OXYGEN_I2S_FORMAT_LJUST,
+ .adc_i2s_format = OXYGEN_I2S_FORMAT_LJUST,
+};
+
+int __devinit get_xonar_cs43xx_model(struct oxygen *chip,
+ const struct pci_device_id *id)
+{
+ switch (id->subdevice) {
+ case 0x834f:
+ chip->model = model_xonar_d1;
+ chip->model.shortname = "Xonar D1";
+ break;
+ case 0x8275:
+ case 0x8327:
+ chip->model = model_xonar_d1;
+ chip->model.shortname = "Xonar DX";
+ chip->model.init = xonar_dx_init;
+ break;
+ default:
+ return -EINVAL;
+ }
+ return 0;
+}
diff --git a/sound/pci/oxygen/xonar_hdmi.c b/sound/pci/oxygen/xonar_hdmi.c
new file mode 100644
index 000000000000..b12db1f1cea9
--- /dev/null
+++ b/sound/pci/oxygen/xonar_hdmi.c
@@ -0,0 +1,128 @@
+/*
+ * helper functions for HDMI models (Xonar HDAV1.3)
+ *
+ * Copyright (c) Clemens Ladisch <clemens@ladisch.de>
+ *
+ *
+ * This driver is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License, version 2.
+ *
+ * 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 driver; if not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <linux/pci.h>
+#include <linux/delay.h>
+#include <sound/asoundef.h>
+#include <sound/control.h>
+#include <sound/core.h>
+#include <sound/pcm.h>
+#include <sound/pcm_params.h>
+#include <sound/tlv.h>
+#include "xonar.h"
+
+static void hdmi_write_command(struct oxygen *chip, u8 command,
+ unsigned int count, const u8 *params)
+{
+ unsigned int i;
+ u8 checksum;
+
+ oxygen_write_uart(chip, 0xfb);
+ oxygen_write_uart(chip, 0xef);
+ oxygen_write_uart(chip, command);
+ oxygen_write_uart(chip, count);
+ for (i = 0; i < count; ++i)
+ oxygen_write_uart(chip, params[i]);
+ checksum = 0xfb + 0xef + command + count;
+ for (i = 0; i < count; ++i)
+ checksum += params[i];
+ oxygen_write_uart(chip, checksum);
+}
+
+static void xonar_hdmi_init_commands(struct oxygen *chip,
+ struct xonar_hdmi *hdmi)
+{
+ u8 param;
+
+ oxygen_reset_uart(chip);
+ param = 0;
+ hdmi_write_command(chip, 0x61, 1, &param);
+ param = 1;
+ hdmi_write_command(chip, 0x74, 1, &param);
+ hdmi_write_command(chip, 0x54, 5, hdmi->params);
+}
+
+void xonar_hdmi_init(struct oxygen *chip, struct xonar_hdmi *hdmi)
+{
+ hdmi->params[1] = IEC958_AES3_CON_FS_48000;
+ hdmi->params[4] = 1;
+ xonar_hdmi_init_commands(chip, hdmi);
+}
+
+void xonar_hdmi_cleanup(struct oxygen *chip)
+{
+ u8 param = 0;
+
+ hdmi_write_command(chip, 0x74, 1, &param);
+}
+
+void xonar_hdmi_resume(struct oxygen *chip, struct xonar_hdmi *hdmi)
+{
+ xonar_hdmi_init_commands(chip, hdmi);
+}
+
+void xonar_hdmi_pcm_hardware_filter(unsigned int channel,
+ struct snd_pcm_hardware *hardware)
+{
+ if (channel == PCM_MULTICH) {
+ hardware->rates = SNDRV_PCM_RATE_44100 |
+ SNDRV_PCM_RATE_48000 |
+ SNDRV_PCM_RATE_96000 |
+ SNDRV_PCM_RATE_192000;
+ hardware->rate_min = 44100;
+ }
+}
+
+void xonar_set_hdmi_params(struct oxygen *chip, struct xonar_hdmi *hdmi,
+ struct snd_pcm_hw_params *params)
+{
+ hdmi->params[0] = 0; /* 1 = non-audio */
+ switch (params_rate(params)) {
+ case 44100:
+ hdmi->params[1] = IEC958_AES3_CON_FS_44100;
+ break;
+ case 48000:
+ hdmi->params[1] = IEC958_AES3_CON_FS_48000;
+ break;
+ default: /* 96000 */
+ hdmi->params[1] = IEC958_AES3_CON_FS_96000;
+ break;
+ case 192000:
+ hdmi->params[1] = IEC958_AES3_CON_FS_192000;
+ break;
+ }
+ hdmi->params[2] = params_channels(params) / 2 - 1;
+ if (params_format(params) == SNDRV_PCM_FORMAT_S16_LE)
+ hdmi->params[3] = 0;
+ else
+ hdmi->params[3] = 0xc0;
+ hdmi->params[4] = 1; /* ? */
+ hdmi_write_command(chip, 0x54, 5, hdmi->params);
+}
+
+void xonar_hdmi_uart_input(struct oxygen *chip)
+{
+ if (chip->uart_input_count >= 2 &&
+ chip->uart_input[chip->uart_input_count - 2] == 'O' &&
+ chip->uart_input[chip->uart_input_count - 1] == 'K') {
+ printk(KERN_DEBUG "message from HDMI chip received:\n");
+ print_hex_dump_bytes("", DUMP_PREFIX_OFFSET,
+ chip->uart_input, chip->uart_input_count);
+ chip->uart_input_count = 0;
+ }
+}
diff --git a/sound/pci/oxygen/xonar_lib.c b/sound/pci/oxygen/xonar_lib.c
new file mode 100644
index 000000000000..b3ff71316653
--- /dev/null
+++ b/sound/pci/oxygen/xonar_lib.c
@@ -0,0 +1,132 @@
+/*
+ * helper functions for Asus Xonar cards
+ *
+ * Copyright (c) Clemens Ladisch <clemens@ladisch.de>
+ *
+ *
+ * This driver is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License, version 2.
+ *
+ * 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 driver; if not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <linux/delay.h>
+#include <sound/core.h>
+#include <sound/control.h>
+#include <sound/pcm.h>
+#include <sound/pcm_params.h>
+#include "xonar.h"
+
+
+#define GPIO_CS53x1_M_MASK 0x000c
+#define GPIO_CS53x1_M_SINGLE 0x0000
+#define GPIO_CS53x1_M_DOUBLE 0x0004
+#define GPIO_CS53x1_M_QUAD 0x0008
+
+
+void xonar_enable_output(struct oxygen *chip)
+{
+ struct xonar_generic *data = chip->model_data;
+
+ oxygen_set_bits16(chip, OXYGEN_GPIO_CONTROL, data->output_enable_bit);
+ msleep(data->anti_pop_delay);
+ oxygen_set_bits16(chip, OXYGEN_GPIO_DATA, data->output_enable_bit);
+}
+
+void xonar_disable_output(struct oxygen *chip)
+{
+ struct xonar_generic *data = chip->model_data;
+
+ oxygen_clear_bits16(chip, OXYGEN_GPIO_DATA, data->output_enable_bit);
+}
+
+static void xonar_ext_power_gpio_changed(struct oxygen *chip)
+{
+ struct xonar_generic *data = chip->model_data;
+ u8 has_power;
+
+ has_power = !!(oxygen_read8(chip, data->ext_power_reg)
+ & data->ext_power_bit);
+ if (has_power != data->has_power) {
+ data->has_power = has_power;
+ if (has_power) {
+ snd_printk(KERN_NOTICE "power restored\n");
+ } else {
+ snd_printk(KERN_CRIT
+ "Hey! Don't unplug the power cable!\n");
+ /* TODO: stop PCMs */
+ }
+ }
+}
+
+void xonar_init_ext_power(struct oxygen *chip)
+{
+ struct xonar_generic *data = chip->model_data;
+
+ oxygen_set_bits8(chip, data->ext_power_int_reg,
+ data->ext_power_bit);
+ chip->interrupt_mask |= OXYGEN_INT_GPIO;
+ chip->model.gpio_changed = xonar_ext_power_gpio_changed;
+ data->has_power = !!(oxygen_read8(chip, data->ext_power_reg)
+ & data->ext_power_bit);
+}
+
+void xonar_init_cs53x1(struct oxygen *chip)
+{
+ oxygen_set_bits16(chip, OXYGEN_GPIO_CONTROL, GPIO_CS53x1_M_MASK);
+ oxygen_write16_masked(chip, OXYGEN_GPIO_DATA,
+ GPIO_CS53x1_M_SINGLE, GPIO_CS53x1_M_MASK);
+}
+
+void xonar_set_cs53x1_params(struct oxygen *chip,
+ struct snd_pcm_hw_params *params)
+{
+ unsigned int value;
+
+ if (params_rate(params) <= 54000)
+ value = GPIO_CS53x1_M_SINGLE;
+ else if (params_rate(params) <= 108000)
+ value = GPIO_CS53x1_M_DOUBLE;
+ else
+ value = GPIO_CS53x1_M_QUAD;
+ oxygen_write16_masked(chip, OXYGEN_GPIO_DATA,
+ value, GPIO_CS53x1_M_MASK);
+}
+
+int xonar_gpio_bit_switch_get(struct snd_kcontrol *ctl,
+ struct snd_ctl_elem_value *value)
+{
+ struct oxygen *chip = ctl->private_data;
+ u16 bit = ctl->private_value;
+
+ value->value.integer.value[0] =
+ !!(oxygen_read16(chip, OXYGEN_GPIO_DATA) & bit);
+ return 0;
+}
+
+int xonar_gpio_bit_switch_put(struct snd_kcontrol *ctl,
+ struct snd_ctl_elem_value *value)
+{
+ struct oxygen *chip = ctl->private_data;
+ u16 bit = ctl->private_value;
+ u16 old_bits, new_bits;
+ int changed;
+
+ spin_lock_irq(&chip->reg_lock);
+ old_bits = oxygen_read16(chip, OXYGEN_GPIO_DATA);
+ if (value->value.integer.value[0])
+ new_bits = old_bits | bit;
+ else
+ new_bits = old_bits & ~bit;
+ changed = new_bits != old_bits;
+ if (changed)
+ oxygen_write16(chip, OXYGEN_GPIO_DATA, new_bits);
+ spin_unlock_irq(&chip->reg_lock);
+ return changed;
+}
diff --git a/sound/pci/oxygen/xonar_pcm179x.c b/sound/pci/oxygen/xonar_pcm179x.c
new file mode 100644
index 000000000000..ba18fb546b4f
--- /dev/null
+++ b/sound/pci/oxygen/xonar_pcm179x.c
@@ -0,0 +1,1115 @@
+/*
+ * card driver for models with PCM1796 DACs (Xonar D2/D2X/HDAV1.3/ST/STX)
+ *
+ * Copyright (c) Clemens Ladisch <clemens@ladisch.de>
+ *
+ *
+ * This driver is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License, version 2.
+ *
+ * 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 driver; if not, see <http://www.gnu.org/licenses/>.
+ */
+
+/*
+ * Xonar D2/D2X
+ * ------------
+ *
+ * CMI8788:
+ *
+ * SPI 0 -> 1st PCM1796 (front)
+ * SPI 1 -> 2nd PCM1796 (surround)
+ * SPI 2 -> 3rd PCM1796 (center/LFE)
+ * SPI 4 -> 4th PCM1796 (back)
+ *
+ * GPIO 2 -> M0 of CS5381
+ * GPIO 3 -> M1 of CS5381
+ * GPIO 5 <- external power present (D2X only)
+ * GPIO 7 -> ALT
+ * GPIO 8 -> enable output to speakers
+ *
+ * CM9780:
+ *
+ * GPO 0 -> route line-in (0) or AC97 output (1) to CS5381 input
+ */
+
+/*
+ * Xonar HDAV1.3 (Deluxe)
+ * ----------------------
+ *
+ * CMI8788:
+ *
+ * I²C <-> PCM1796 (front)
+ *
+ * GPI 0 <- external power present
+ *
+ * GPIO 0 -> enable output to speakers
+ * GPIO 2 -> M0 of CS5381
+ * GPIO 3 -> M1 of CS5381
+ * GPIO 8 -> route input jack to line-in (0) or mic-in (1)
+ *
+ * TXD -> HDMI controller
+ * RXD <- HDMI controller
+ *
+ * PCM1796 front: AD1,0 <- 0,0
+ *
+ * CM9780:
+ *
+ * GPO 0 -> route line-in (0) or AC97 output (1) to CS5381 input
+ *
+ * no daughterboard
+ * ----------------
+ *
+ * GPIO 4 <- 1
+ *
+ * H6 daughterboard
+ * ----------------
+ *
+ * GPIO 4 <- 0
+ * GPIO 5 <- 0
+ *
+ * I²C <-> PCM1796 (surround)
+ * <-> PCM1796 (center/LFE)
+ * <-> PCM1796 (back)
+ *
+ * PCM1796 surround: AD1,0 <- 0,1
+ * PCM1796 center/LFE: AD1,0 <- 1,0
+ * PCM1796 back: AD1,0 <- 1,1
+ *
+ * unknown daughterboard
+ * ---------------------
+ *
+ * GPIO 4 <- 0
+ * GPIO 5 <- 1
+ *
+ * I²C <-> CS4362A (surround, center/LFE, back)
+ *
+ * CS4362A: AD0 <- 0
+ */
+
+/*
+ * Xonar Essence ST (Deluxe)/STX
+ * -----------------------------
+ *
+ * CMI8788:
+ *
+ * I²C <-> PCM1792A
+ * <-> CS2000 (ST only)
+ *
+ * ADC1 MCLK -> REF_CLK of CS2000 (ST only)
+ *
+ * GPI 0 <- external power present (STX only)
+ *
+ * GPIO 0 -> enable output to speakers
+ * GPIO 1 -> route HP to front panel (0) or rear jack (1)
+ * GPIO 2 -> M0 of CS5381
+ * GPIO 3 -> M1 of CS5381
+ * GPIO 7 -> route output to speaker jacks (0) or HP (1)
+ * GPIO 8 -> route input jack to line-in (0) or mic-in (1)
+ *
+ * PCM1792A:
+ *
+ * AD1,0 <- 0,0
+ * SCK <- CLK_OUT of CS2000 (ST only)
+ *
+ * CS2000:
+ *
+ * AD0 <- 0
+ *
+ * CM9780:
+ *
+ * GPO 0 -> route line-in (0) or AC97 output (1) to CS5381 input
+ *
+ * H6 daughterboard
+ * ----------------
+ *
+ * GPIO 4 <- 0
+ * GPIO 5 <- 0
+ */
+
+#include <linux/pci.h>
+#include <linux/delay.h>
+#include <linux/mutex.h>
+#include <sound/ac97_codec.h>
+#include <sound/control.h>
+#include <sound/core.h>
+#include <sound/pcm.h>
+#include <sound/pcm_params.h>
+#include <sound/tlv.h>
+#include "xonar.h"
+#include "cm9780.h"
+#include "pcm1796.h"
+#include "cs2000.h"
+
+
+#define GPIO_D2X_EXT_POWER 0x0020
+#define GPIO_D2_ALT 0x0080
+#define GPIO_D2_OUTPUT_ENABLE 0x0100
+
+#define GPI_EXT_POWER 0x01
+#define GPIO_INPUT_ROUTE 0x0100
+
+#define GPIO_HDAV_OUTPUT_ENABLE 0x0001
+
+#define GPIO_DB_MASK 0x0030
+#define GPIO_DB_H6 0x0000
+
+#define GPIO_ST_OUTPUT_ENABLE 0x0001
+#define GPIO_ST_HP_REAR 0x0002
+#define GPIO_ST_HP 0x0080
+
+#define I2C_DEVICE_PCM1796(i) (0x98 + ((i) << 1)) /* 10011, ii, /W=0 */
+#define I2C_DEVICE_CS2000 0x9c /* 100111, 0, /W=0 */
+
+#define PCM1796_REG_BASE 16
+
+
+struct xonar_pcm179x {
+ struct xonar_generic generic;
+ unsigned int dacs;
+ u8 pcm1796_regs[4][5];
+ unsigned int current_rate;
+ bool os_128;
+ bool hp_active;
+ s8 hp_gain_offset;
+ bool has_cs2000;
+ u8 cs2000_fun_cfg_1;
+};
+
+struct xonar_hdav {
+ struct xonar_pcm179x pcm179x;
+ struct xonar_hdmi hdmi;
+};
+
+
+static inline void pcm1796_write_spi(struct oxygen *chip, unsigned int codec,
+ u8 reg, u8 value)
+{
+ /* maps ALSA channel pair number to SPI output */
+ static const u8 codec_map[4] = {
+ 0, 1, 2, 4
+ };
+ oxygen_write_spi(chip, OXYGEN_SPI_TRIGGER |
+ OXYGEN_SPI_DATA_LENGTH_2 |
+ OXYGEN_SPI_CLOCK_160 |
+ (codec_map[codec] << OXYGEN_SPI_CODEC_SHIFT) |
+ OXYGEN_SPI_CEN_LATCH_CLOCK_HI,
+ (reg << 8) | value);
+}
+
+static inline void pcm1796_write_i2c(struct oxygen *chip, unsigned int codec,
+ u8 reg, u8 value)
+{
+ oxygen_write_i2c(chip, I2C_DEVICE_PCM1796(codec), reg, value);
+}
+
+static void pcm1796_write(struct oxygen *chip, unsigned int codec,
+ u8 reg, u8 value)
+{
+ struct xonar_pcm179x *data = chip->model_data;
+
+ if ((chip->model.function_flags & OXYGEN_FUNCTION_2WIRE_SPI_MASK) ==
+ OXYGEN_FUNCTION_SPI)
+ pcm1796_write_spi(chip, codec, reg, value);
+ else
+ pcm1796_write_i2c(chip, codec, reg, value);
+ if ((unsigned int)(reg - PCM1796_REG_BASE)
+ < ARRAY_SIZE(data->pcm1796_regs[codec]))
+ data->pcm1796_regs[codec][reg - PCM1796_REG_BASE] = value;
+}
+
+static void pcm1796_write_cached(struct oxygen *chip, unsigned int codec,
+ u8 reg, u8 value)
+{
+ struct xonar_pcm179x *data = chip->model_data;
+
+ if (value != data->pcm1796_regs[codec][reg - PCM1796_REG_BASE])
+ pcm1796_write(chip, codec, reg, value);
+}
+
+static void cs2000_write(struct oxygen *chip, u8 reg, u8 value)
+{
+ struct xonar_pcm179x *data = chip->model_data;
+
+ oxygen_write_i2c(chip, I2C_DEVICE_CS2000, reg, value);
+ if (reg == CS2000_FUN_CFG_1)
+ data->cs2000_fun_cfg_1 = value;
+}
+
+static void cs2000_write_cached(struct oxygen *chip, u8 reg, u8 value)
+{
+ struct xonar_pcm179x *data = chip->model_data;
+
+ if (reg != CS2000_FUN_CFG_1 ||
+ value != data->cs2000_fun_cfg_1)
+ cs2000_write(chip, reg, value);
+}
+
+static void pcm1796_registers_init(struct oxygen *chip)
+{
+ struct xonar_pcm179x *data = chip->model_data;
+ unsigned int i;
+ s8 gain_offset;
+
+ gain_offset = data->hp_active ? data->hp_gain_offset : 0;
+ for (i = 0; i < data->dacs; ++i) {
+ /* set ATLD before ATL/ATR */
+ pcm1796_write(chip, i, 18,
+ data->pcm1796_regs[0][18 - PCM1796_REG_BASE]);
+ pcm1796_write(chip, i, 16, chip->dac_volume[i * 2]
+ + gain_offset);
+ pcm1796_write(chip, i, 17, chip->dac_volume[i * 2 + 1]
+ + gain_offset);
+ pcm1796_write(chip, i, 19,
+ data->pcm1796_regs[0][19 - PCM1796_REG_BASE]);
+ pcm1796_write(chip, i, 20,
+ data->pcm1796_regs[0][20 - PCM1796_REG_BASE]);
+ pcm1796_write(chip, i, 21, 0);
+ }
+}
+
+static void pcm1796_init(struct oxygen *chip)
+{
+ struct xonar_pcm179x *data = chip->model_data;
+
+ data->pcm1796_regs[0][18 - PCM1796_REG_BASE] = PCM1796_MUTE |
+ PCM1796_DMF_DISABLED | PCM1796_FMT_24_LJUST | PCM1796_ATLD;
+ data->pcm1796_regs[0][19 - PCM1796_REG_BASE] =
+ PCM1796_FLT_SHARP | PCM1796_ATS_1;
+ data->pcm1796_regs[0][20 - PCM1796_REG_BASE] = PCM1796_OS_64;
+ pcm1796_registers_init(chip);
+ data->current_rate = 48000;
+}
+
+static void xonar_d2_init(struct oxygen *chip)
+{
+ struct xonar_pcm179x *data = chip->model_data;
+
+ data->generic.anti_pop_delay = 300;
+ data->generic.output_enable_bit = GPIO_D2_OUTPUT_ENABLE;
+ data->dacs = 4;
+
+ pcm1796_init(chip);
+
+ oxygen_set_bits16(chip, OXYGEN_GPIO_CONTROL, GPIO_D2_ALT);
+ oxygen_clear_bits16(chip, OXYGEN_GPIO_DATA, GPIO_D2_ALT);
+
+ oxygen_ac97_set_bits(chip, 0, CM9780_JACK, CM9780_FMIC2MIC);
+
+ xonar_init_cs53x1(chip);
+ xonar_enable_output(chip);
+
+ snd_component_add(chip->card, "PCM1796");
+ snd_component_add(chip->card, "CS5381");
+}
+
+static void xonar_d2x_init(struct oxygen *chip)
+{
+ struct xonar_pcm179x *data = chip->model_data;
+
+ data->generic.ext_power_reg = OXYGEN_GPIO_DATA;
+ data->generic.ext_power_int_reg = OXYGEN_GPIO_INTERRUPT_MASK;
+ data->generic.ext_power_bit = GPIO_D2X_EXT_POWER;
+ oxygen_clear_bits16(chip, OXYGEN_GPIO_CONTROL, GPIO_D2X_EXT_POWER);
+ xonar_init_ext_power(chip);
+ xonar_d2_init(chip);
+}
+
+static void xonar_hdav_init(struct oxygen *chip)
+{
+ struct xonar_hdav *data = chip->model_data;
+
+ oxygen_write16(chip, OXYGEN_2WIRE_BUS_STATUS,
+ OXYGEN_2WIRE_LENGTH_8 |
+ OXYGEN_2WIRE_INTERRUPT_MASK |
+ OXYGEN_2WIRE_SPEED_FAST);
+
+ data->pcm179x.generic.anti_pop_delay = 100;
+ data->pcm179x.generic.output_enable_bit = GPIO_HDAV_OUTPUT_ENABLE;
+ data->pcm179x.generic.ext_power_reg = OXYGEN_GPI_DATA;
+ data->pcm179x.generic.ext_power_int_reg = OXYGEN_GPI_INTERRUPT_MASK;
+ data->pcm179x.generic.ext_power_bit = GPI_EXT_POWER;
+ data->pcm179x.dacs = chip->model.private_data ? 4 : 1;
+
+ pcm1796_init(chip);
+
+ oxygen_set_bits16(chip, OXYGEN_GPIO_CONTROL, GPIO_INPUT_ROUTE);
+ oxygen_clear_bits16(chip, OXYGEN_GPIO_DATA, GPIO_INPUT_ROUTE);
+
+ xonar_init_cs53x1(chip);
+ xonar_init_ext_power(chip);
+ xonar_hdmi_init(chip, &data->hdmi);
+ xonar_enable_output(chip);
+
+ snd_component_add(chip->card, "PCM1796");
+ snd_component_add(chip->card, "CS5381");
+}
+
+static void xonar_st_init_i2c(struct oxygen *chip)
+{
+ oxygen_write16(chip, OXYGEN_2WIRE_BUS_STATUS,
+ OXYGEN_2WIRE_LENGTH_8 |
+ OXYGEN_2WIRE_INTERRUPT_MASK |
+ OXYGEN_2WIRE_SPEED_FAST);
+}
+
+static void xonar_st_init_common(struct oxygen *chip)
+{
+ struct xonar_pcm179x *data = chip->model_data;
+
+ data->generic.anti_pop_delay = 100;
+ data->generic.output_enable_bit = GPIO_ST_OUTPUT_ENABLE;
+ data->dacs = chip->model.private_data ? 4 : 1;
+ data->hp_gain_offset = 2*-18;
+
+ pcm1796_init(chip);
+
+ oxygen_set_bits16(chip, OXYGEN_GPIO_CONTROL,
+ GPIO_INPUT_ROUTE | GPIO_ST_HP_REAR | GPIO_ST_HP);
+ oxygen_clear_bits16(chip, OXYGEN_GPIO_DATA,
+ GPIO_INPUT_ROUTE | GPIO_ST_HP_REAR | GPIO_ST_HP);
+
+ xonar_init_cs53x1(chip);
+ xonar_enable_output(chip);
+
+ snd_component_add(chip->card, "PCM1792A");
+ snd_component_add(chip->card, "CS5381");
+}
+
+static void cs2000_registers_init(struct oxygen *chip)
+{
+ struct xonar_pcm179x *data = chip->model_data;
+
+ cs2000_write(chip, CS2000_GLOBAL_CFG, CS2000_FREEZE);
+ cs2000_write(chip, CS2000_DEV_CTRL, 0);
+ cs2000_write(chip, CS2000_DEV_CFG_1,
+ CS2000_R_MOD_SEL_1 |
+ (0 << CS2000_R_SEL_SHIFT) |
+ CS2000_AUX_OUT_SRC_REF_CLK |
+ CS2000_EN_DEV_CFG_1);
+ cs2000_write(chip, CS2000_DEV_CFG_2,
+ (0 << CS2000_LOCK_CLK_SHIFT) |
+ CS2000_FRAC_N_SRC_STATIC);
+ cs2000_write(chip, CS2000_RATIO_0 + 0, 0x00); /* 1.0 */
+ cs2000_write(chip, CS2000_RATIO_0 + 1, 0x10);
+ cs2000_write(chip, CS2000_RATIO_0 + 2, 0x00);
+ cs2000_write(chip, CS2000_RATIO_0 + 3, 0x00);
+ cs2000_write(chip, CS2000_FUN_CFG_1, data->cs2000_fun_cfg_1);
+ cs2000_write(chip, CS2000_FUN_CFG_2, 0);
+ cs2000_write(chip, CS2000_GLOBAL_CFG, CS2000_EN_DEV_CFG_2);
+}
+
+static void xonar_st_init(struct oxygen *chip)
+{
+ struct xonar_pcm179x *data = chip->model_data;
+
+ data->has_cs2000 = 1;
+ data->cs2000_fun_cfg_1 = CS2000_REF_CLK_DIV_1;
+
+ oxygen_write16(chip, OXYGEN_I2S_A_FORMAT,
+ OXYGEN_RATE_48000 | OXYGEN_I2S_FORMAT_I2S |
+ OXYGEN_I2S_MCLK_128 | OXYGEN_I2S_BITS_16 |
+ OXYGEN_I2S_MASTER | OXYGEN_I2S_BCLK_64);
+
+ xonar_st_init_i2c(chip);
+ cs2000_registers_init(chip);
+ xonar_st_init_common(chip);
+
+ snd_component_add(chip->card, "CS2000");
+}
+
+static void xonar_stx_init(struct oxygen *chip)
+{
+ struct xonar_pcm179x *data = chip->model_data;
+
+ xonar_st_init_i2c(chip);
+ data->generic.ext_power_reg = OXYGEN_GPI_DATA;
+ data->generic.ext_power_int_reg = OXYGEN_GPI_INTERRUPT_MASK;
+ data->generic.ext_power_bit = GPI_EXT_POWER;
+ xonar_init_ext_power(chip);
+ xonar_st_init_common(chip);
+}
+
+static void xonar_d2_cleanup(struct oxygen *chip)
+{
+ xonar_disable_output(chip);
+}
+
+static void xonar_hdav_cleanup(struct oxygen *chip)
+{
+ xonar_hdmi_cleanup(chip);
+ xonar_disable_output(chip);
+ msleep(2);
+}
+
+static void xonar_st_cleanup(struct oxygen *chip)
+{
+ xonar_disable_output(chip);
+}
+
+static void xonar_d2_suspend(struct oxygen *chip)
+{
+ xonar_d2_cleanup(chip);
+}
+
+static void xonar_hdav_suspend(struct oxygen *chip)
+{
+ xonar_hdav_cleanup(chip);
+}
+
+static void xonar_st_suspend(struct oxygen *chip)
+{
+ xonar_st_cleanup(chip);
+}
+
+static void xonar_d2_resume(struct oxygen *chip)
+{
+ pcm1796_registers_init(chip);
+ xonar_enable_output(chip);
+}
+
+static void xonar_hdav_resume(struct oxygen *chip)
+{
+ struct xonar_hdav *data = chip->model_data;
+
+ pcm1796_registers_init(chip);
+ xonar_hdmi_resume(chip, &data->hdmi);
+ xonar_enable_output(chip);
+}
+
+static void xonar_stx_resume(struct oxygen *chip)
+{
+ pcm1796_registers_init(chip);
+ xonar_enable_output(chip);
+}
+
+static void xonar_st_resume(struct oxygen *chip)
+{
+ cs2000_registers_init(chip);
+ xonar_stx_resume(chip);
+}
+
+static unsigned int mclk_from_rate(struct oxygen *chip, unsigned int rate)
+{
+ struct xonar_pcm179x *data = chip->model_data;
+
+ if (rate <= 32000)
+ return OXYGEN_I2S_MCLK_512;
+ else if (rate <= 48000 && data->os_128)
+ return OXYGEN_I2S_MCLK_512;
+ else if (rate <= 96000)
+ return OXYGEN_I2S_MCLK_256;
+ else
+ return OXYGEN_I2S_MCLK_128;
+}
+
+static unsigned int get_pcm1796_i2s_mclk(struct oxygen *chip,
+ unsigned int channel,
+ struct snd_pcm_hw_params *params)
+{
+ if (channel == PCM_MULTICH)
+ return mclk_from_rate(chip, params_rate(params));
+ else
+ return oxygen_default_i2s_mclk(chip, channel, params);
+}
+
+static void update_pcm1796_oversampling(struct oxygen *chip)
+{
+ struct xonar_pcm179x *data = chip->model_data;
+ unsigned int i;
+ u8 reg;
+
+ if (data->current_rate <= 32000)
+ reg = PCM1796_OS_128;
+ else if (data->current_rate <= 48000 && data->os_128)
+ reg = PCM1796_OS_128;
+ else if (data->current_rate <= 96000 || data->os_128)
+ reg = PCM1796_OS_64;
+ else
+ reg = PCM1796_OS_32;
+ for (i = 0; i < data->dacs; ++i)
+ pcm1796_write_cached(chip, i, 20, reg);
+}
+
+static void set_pcm1796_params(struct oxygen *chip,
+ struct snd_pcm_hw_params *params)
+{
+ struct xonar_pcm179x *data = chip->model_data;
+
+ data->current_rate = params_rate(params);
+ update_pcm1796_oversampling(chip);
+}
+
+static void update_pcm1796_volume(struct oxygen *chip)
+{
+ struct xonar_pcm179x *data = chip->model_data;
+ unsigned int i;
+ s8 gain_offset;
+
+ gain_offset = data->hp_active ? data->hp_gain_offset : 0;
+ for (i = 0; i < data->dacs; ++i) {
+ pcm1796_write_cached(chip, i, 16, chip->dac_volume[i * 2]
+ + gain_offset);
+ pcm1796_write_cached(chip, i, 17, chip->dac_volume[i * 2 + 1]
+ + gain_offset);
+ }
+}
+
+static void update_pcm1796_mute(struct oxygen *chip)
+{
+ struct xonar_pcm179x *data = chip->model_data;
+ unsigned int i;
+ u8 value;
+
+ value = PCM1796_DMF_DISABLED | PCM1796_FMT_24_LJUST | PCM1796_ATLD;
+ if (chip->dac_mute)
+ value |= PCM1796_MUTE;
+ for (i = 0; i < data->dacs; ++i)
+ pcm1796_write_cached(chip, i, 18, value);
+}
+
+static void update_cs2000_rate(struct oxygen *chip, unsigned int rate)
+{
+ struct xonar_pcm179x *data = chip->model_data;
+ u8 rate_mclk, reg;
+
+ switch (rate) {
+ /* XXX Why is the I2S A MCLK half the actual I2S MCLK? */
+ case 32000:
+ rate_mclk = OXYGEN_RATE_32000 | OXYGEN_I2S_MCLK_256;
+ break;
+ case 44100:
+ if (data->os_128)
+ rate_mclk = OXYGEN_RATE_44100 | OXYGEN_I2S_MCLK_256;
+ else
+ rate_mclk = OXYGEN_RATE_44100 | OXYGEN_I2S_MCLK_128;
+ break;
+ default: /* 48000 */
+ if (data->os_128)
+ rate_mclk = OXYGEN_RATE_48000 | OXYGEN_I2S_MCLK_256;
+ else
+ rate_mclk = OXYGEN_RATE_48000 | OXYGEN_I2S_MCLK_128;
+ break;
+ case 64000:
+ rate_mclk = OXYGEN_RATE_32000 | OXYGEN_I2S_MCLK_256;
+ break;
+ case 88200:
+ rate_mclk = OXYGEN_RATE_44100 | OXYGEN_I2S_MCLK_256;
+ break;
+ case 96000:
+ rate_mclk = OXYGEN_RATE_48000 | OXYGEN_I2S_MCLK_256;
+ break;
+ case 176400:
+ rate_mclk = OXYGEN_RATE_44100 | OXYGEN_I2S_MCLK_256;
+ break;
+ case 192000:
+ rate_mclk = OXYGEN_RATE_48000 | OXYGEN_I2S_MCLK_256;
+ break;
+ }
+ oxygen_write16_masked(chip, OXYGEN_I2S_A_FORMAT, rate_mclk,
+ OXYGEN_I2S_RATE_MASK | OXYGEN_I2S_MCLK_MASK);
+ if ((rate_mclk & OXYGEN_I2S_MCLK_MASK) <= OXYGEN_I2S_MCLK_128)
+ reg = CS2000_REF_CLK_DIV_1;
+ else
+ reg = CS2000_REF_CLK_DIV_2;
+ cs2000_write_cached(chip, CS2000_FUN_CFG_1, reg);
+}
+
+static void set_st_params(struct oxygen *chip,
+ struct snd_pcm_hw_params *params)
+{
+ update_cs2000_rate(chip, params_rate(params));
+ set_pcm1796_params(chip, params);
+}
+
+static void set_hdav_params(struct oxygen *chip,
+ struct snd_pcm_hw_params *params)
+{
+ struct xonar_hdav *data = chip->model_data;
+
+ set_pcm1796_params(chip, params);
+ xonar_set_hdmi_params(chip, &data->hdmi, params);
+}
+
+static const struct snd_kcontrol_new alt_switch = {
+ .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+ .name = "Analog Loopback Switch",
+ .info = snd_ctl_boolean_mono_info,
+ .get = xonar_gpio_bit_switch_get,
+ .put = xonar_gpio_bit_switch_put,
+ .private_value = GPIO_D2_ALT,
+};
+
+static int rolloff_info(struct snd_kcontrol *ctl,
+ struct snd_ctl_elem_info *info)
+{
+ static const char *const names[2] = {
+ "Sharp Roll-off", "Slow Roll-off"
+ };
+
+ info->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED;
+ info->count = 1;
+ info->value.enumerated.items = 2;
+ if (info->value.enumerated.item >= 2)
+ info->value.enumerated.item = 1;
+ strcpy(info->value.enumerated.name, names[info->value.enumerated.item]);
+ return 0;
+}
+
+static int rolloff_get(struct snd_kcontrol *ctl,
+ struct snd_ctl_elem_value *value)
+{
+ struct oxygen *chip = ctl->private_data;
+ struct xonar_pcm179x *data = chip->model_data;
+
+ value->value.enumerated.item[0] =
+ (data->pcm1796_regs[0][19 - PCM1796_REG_BASE] &
+ PCM1796_FLT_MASK) != PCM1796_FLT_SHARP;
+ return 0;
+}
+
+static int rolloff_put(struct snd_kcontrol *ctl,
+ struct snd_ctl_elem_value *value)
+{
+ struct oxygen *chip = ctl->private_data;
+ struct xonar_pcm179x *data = chip->model_data;
+ unsigned int i;
+ int changed;
+ u8 reg;
+
+ mutex_lock(&chip->mutex);
+ reg = data->pcm1796_regs[0][19 - PCM1796_REG_BASE];
+ reg &= ~PCM1796_FLT_MASK;
+ if (!value->value.enumerated.item[0])
+ reg |= PCM1796_FLT_SHARP;
+ else
+ reg |= PCM1796_FLT_SLOW;
+ changed = reg != data->pcm1796_regs[0][19 - PCM1796_REG_BASE];
+ if (changed) {
+ for (i = 0; i < data->dacs; ++i)
+ pcm1796_write(chip, i, 19, reg);
+ }
+ mutex_unlock(&chip->mutex);
+ return changed;
+}
+
+static const struct snd_kcontrol_new rolloff_control = {
+ .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+ .name = "DAC Filter Playback Enum",
+ .info = rolloff_info,
+ .get = rolloff_get,
+ .put = rolloff_put,
+};
+
+static int os_128_info(struct snd_kcontrol *ctl, struct snd_ctl_elem_info *info)
+{
+ static const char *const names[2] = { "64x", "128x" };
+
+ info->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED;
+ info->count = 1;
+ info->value.enumerated.items = 2;
+ if (info->value.enumerated.item >= 2)
+ info->value.enumerated.item = 1;
+ strcpy(info->value.enumerated.name, names[info->value.enumerated.item]);
+ return 0;
+}
+
+static int os_128_get(struct snd_kcontrol *ctl,
+ struct snd_ctl_elem_value *value)
+{
+ struct oxygen *chip = ctl->private_data;
+ struct xonar_pcm179x *data = chip->model_data;
+
+ value->value.enumerated.item[0] = data->os_128;
+ return 0;
+}
+
+static int os_128_put(struct snd_kcontrol *ctl,
+ struct snd_ctl_elem_value *value)
+{
+ struct oxygen *chip = ctl->private_data;
+ struct xonar_pcm179x *data = chip->model_data;
+ int changed;
+
+ mutex_lock(&chip->mutex);
+ changed = value->value.enumerated.item[0] != data->os_128;
+ if (changed) {
+ data->os_128 = value->value.enumerated.item[0];
+ if (data->has_cs2000)
+ update_cs2000_rate(chip, data->current_rate);
+ oxygen_write16_masked(chip, OXYGEN_I2S_MULTICH_FORMAT,
+ mclk_from_rate(chip, data->current_rate),
+ OXYGEN_I2S_MCLK_MASK);
+ update_pcm1796_oversampling(chip);
+ }
+ mutex_unlock(&chip->mutex);
+ return changed;
+}
+
+static const struct snd_kcontrol_new os_128_control = {
+ .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+ .name = "DAC Oversampling Playback Enum",
+ .info = os_128_info,
+ .get = os_128_get,
+ .put = os_128_put,
+};
+
+static int st_output_switch_info(struct snd_kcontrol *ctl,
+ struct snd_ctl_elem_info *info)
+{
+ static const char *const names[3] = {
+ "Speakers", "Headphones", "FP Headphones"
+ };
+
+ info->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED;
+ info->count = 1;
+ info->value.enumerated.items = 3;
+ if (info->value.enumerated.item >= 3)
+ info->value.enumerated.item = 2;
+ strcpy(info->value.enumerated.name, names[info->value.enumerated.item]);
+ return 0;
+}
+
+static int st_output_switch_get(struct snd_kcontrol *ctl,
+ struct snd_ctl_elem_value *value)
+{
+ struct oxygen *chip = ctl->private_data;
+ u16 gpio;
+
+ gpio = oxygen_read16(chip, OXYGEN_GPIO_DATA);
+ if (!(gpio & GPIO_ST_HP))
+ value->value.enumerated.item[0] = 0;
+ else if (gpio & GPIO_ST_HP_REAR)
+ value->value.enumerated.item[0] = 1;
+ else
+ value->value.enumerated.item[0] = 2;
+ return 0;
+}
+
+
+static int st_output_switch_put(struct snd_kcontrol *ctl,
+ struct snd_ctl_elem_value *value)
+{
+ struct oxygen *chip = ctl->private_data;
+ struct xonar_pcm179x *data = chip->model_data;
+ u16 gpio_old, gpio;
+
+ mutex_lock(&chip->mutex);
+ gpio_old = oxygen_read16(chip, OXYGEN_GPIO_DATA);
+ gpio = gpio_old;
+ switch (value->value.enumerated.item[0]) {
+ case 0:
+ gpio &= ~(GPIO_ST_HP | GPIO_ST_HP_REAR);
+ break;
+ case 1:
+ gpio |= GPIO_ST_HP | GPIO_ST_HP_REAR;
+ break;
+ case 2:
+ gpio = (gpio | GPIO_ST_HP) & ~GPIO_ST_HP_REAR;
+ break;
+ }
+ oxygen_write16(chip, OXYGEN_GPIO_DATA, gpio);
+ data->hp_active = gpio & GPIO_ST_HP;
+ update_pcm1796_volume(chip);
+ mutex_unlock(&chip->mutex);
+ return gpio != gpio_old;
+}
+
+static int st_hp_volume_offset_info(struct snd_kcontrol *ctl,
+ struct snd_ctl_elem_info *info)
+{
+ static const char *const names[3] = {
+ "< 64 ohms", "64-300 ohms", "300-600 ohms"
+ };
+
+ info->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED;
+ info->count = 1;
+ info->value.enumerated.items = 3;
+ if (info->value.enumerated.item > 2)
+ info->value.enumerated.item = 2;
+ strcpy(info->value.enumerated.name, names[info->value.enumerated.item]);
+ return 0;
+}
+
+static int st_hp_volume_offset_get(struct snd_kcontrol *ctl,
+ struct snd_ctl_elem_value *value)
+{
+ struct oxygen *chip = ctl->private_data;
+ struct xonar_pcm179x *data = chip->model_data;
+
+ mutex_lock(&chip->mutex);
+ if (data->hp_gain_offset < 2*-6)
+ value->value.enumerated.item[0] = 0;
+ else if (data->hp_gain_offset < 0)
+ value->value.enumerated.item[0] = 1;
+ else
+ value->value.enumerated.item[0] = 2;
+ mutex_unlock(&chip->mutex);
+ return 0;
+}
+
+
+static int st_hp_volume_offset_put(struct snd_kcontrol *ctl,
+ struct snd_ctl_elem_value *value)
+{
+ static const s8 offsets[] = { 2*-18, 2*-6, 0 };
+ struct oxygen *chip = ctl->private_data;
+ struct xonar_pcm179x *data = chip->model_data;
+ s8 offset;
+ int changed;
+
+ if (value->value.enumerated.item[0] > 2)
+ return -EINVAL;
+ offset = offsets[value->value.enumerated.item[0]];
+ mutex_lock(&chip->mutex);
+ changed = offset != data->hp_gain_offset;
+ if (changed) {
+ data->hp_gain_offset = offset;
+ update_pcm1796_volume(chip);
+ }
+ mutex_unlock(&chip->mutex);
+ return changed;
+}
+
+static const struct snd_kcontrol_new st_controls[] = {
+ {
+ .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+ .name = "Analog Output",
+ .info = st_output_switch_info,
+ .get = st_output_switch_get,
+ .put = st_output_switch_put,
+ },
+ {
+ .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+ .name = "Headphones Impedance Playback Enum",
+ .info = st_hp_volume_offset_info,
+ .get = st_hp_volume_offset_get,
+ .put = st_hp_volume_offset_put,
+ },
+};
+
+static void xonar_line_mic_ac97_switch(struct oxygen *chip,
+ unsigned int reg, unsigned int mute)
+{
+ if (reg == AC97_LINE) {
+ spin_lock_irq(&chip->reg_lock);
+ oxygen_write16_masked(chip, OXYGEN_GPIO_DATA,
+ mute ? GPIO_INPUT_ROUTE : 0,
+ GPIO_INPUT_ROUTE);
+ spin_unlock_irq(&chip->reg_lock);
+ }
+}
+
+static const DECLARE_TLV_DB_SCALE(pcm1796_db_scale, -6000, 50, 0);
+
+static int xonar_d2_control_filter(struct snd_kcontrol_new *template)
+{
+ if (!strncmp(template->name, "CD Capture ", 11))
+ /* CD in is actually connected to the video in pin */
+ template->private_value ^= AC97_CD ^ AC97_VIDEO;
+ return 0;
+}
+
+static int xonar_st_control_filter(struct snd_kcontrol_new *template)
+{
+ if (!strncmp(template->name, "CD Capture ", 11))
+ return 1; /* no CD input */
+ return 0;
+}
+
+static int add_pcm1796_controls(struct oxygen *chip)
+{
+ int err;
+
+ err = snd_ctl_add(chip->card, snd_ctl_new1(&rolloff_control, chip));
+ if (err < 0)
+ return err;
+ err = snd_ctl_add(chip->card, snd_ctl_new1(&os_128_control, chip));
+ if (err < 0)
+ return err;
+ return 0;
+}
+
+static int xonar_d2_mixer_init(struct oxygen *chip)
+{
+ int err;
+
+ err = snd_ctl_add(chip->card, snd_ctl_new1(&alt_switch, chip));
+ if (err < 0)
+ return err;
+ err = add_pcm1796_controls(chip);
+ if (err < 0)
+ return err;
+ return 0;
+}
+
+static int xonar_hdav_mixer_init(struct oxygen *chip)
+{
+ return add_pcm1796_controls(chip);
+}
+
+static int xonar_st_mixer_init(struct oxygen *chip)
+{
+ unsigned int i;
+ int err;
+
+ for (i = 0; i < ARRAY_SIZE(st_controls); ++i) {
+ err = snd_ctl_add(chip->card,
+ snd_ctl_new1(&st_controls[i], chip));
+ if (err < 0)
+ return err;
+ }
+ err = add_pcm1796_controls(chip);
+ if (err < 0)
+ return err;
+ return 0;
+}
+
+static const struct oxygen_model model_xonar_d2 = {
+ .longname = "Asus Virtuoso 200",
+ .chip = "AV200",
+ .init = xonar_d2_init,
+ .control_filter = xonar_d2_control_filter,
+ .mixer_init = xonar_d2_mixer_init,
+ .cleanup = xonar_d2_cleanup,
+ .suspend = xonar_d2_suspend,
+ .resume = xonar_d2_resume,
+ .get_i2s_mclk = get_pcm1796_i2s_mclk,
+ .set_dac_params = set_pcm1796_params,
+ .set_adc_params = xonar_set_cs53x1_params,
+ .update_dac_volume = update_pcm1796_volume,
+ .update_dac_mute = update_pcm1796_mute,
+ .dac_tlv = pcm1796_db_scale,
+ .model_data_size = sizeof(struct xonar_pcm179x),
+ .device_config = PLAYBACK_0_TO_I2S |
+ PLAYBACK_1_TO_SPDIF |
+ CAPTURE_0_FROM_I2S_2 |
+ CAPTURE_1_FROM_SPDIF |
+ MIDI_OUTPUT |
+ MIDI_INPUT,
+ .dac_channels = 8,
+ .dac_volume_min = 255 - 2*60,
+ .dac_volume_max = 255,
+ .misc_flags = OXYGEN_MISC_MIDI,
+ .function_flags = OXYGEN_FUNCTION_SPI |
+ OXYGEN_FUNCTION_ENABLE_SPI_4_5,
+ .dac_i2s_format = OXYGEN_I2S_FORMAT_LJUST,
+ .adc_i2s_format = OXYGEN_I2S_FORMAT_LJUST,
+};
+
+static const struct oxygen_model model_xonar_hdav = {
+ .longname = "Asus Virtuoso 200",
+ .chip = "AV200",
+ .init = xonar_hdav_init,
+ .mixer_init = xonar_hdav_mixer_init,
+ .cleanup = xonar_hdav_cleanup,
+ .suspend = xonar_hdav_suspend,
+ .resume = xonar_hdav_resume,
+ .pcm_hardware_filter = xonar_hdmi_pcm_hardware_filter,
+ .get_i2s_mclk = get_pcm1796_i2s_mclk,
+ .set_dac_params = set_hdav_params,
+ .set_adc_params = xonar_set_cs53x1_params,
+ .update_dac_volume = update_pcm1796_volume,
+ .update_dac_mute = update_pcm1796_mute,
+ .uart_input = xonar_hdmi_uart_input,
+ .ac97_switch = xonar_line_mic_ac97_switch,
+ .dac_tlv = pcm1796_db_scale,
+ .model_data_size = sizeof(struct xonar_hdav),
+ .device_config = PLAYBACK_0_TO_I2S |
+ PLAYBACK_1_TO_SPDIF |
+ CAPTURE_0_FROM_I2S_2 |
+ CAPTURE_1_FROM_SPDIF,
+ .dac_channels = 8,
+ .dac_volume_min = 255 - 2*60,
+ .dac_volume_max = 255,
+ .misc_flags = OXYGEN_MISC_MIDI,
+ .function_flags = OXYGEN_FUNCTION_2WIRE,
+ .dac_i2s_format = OXYGEN_I2S_FORMAT_LJUST,
+ .adc_i2s_format = OXYGEN_I2S_FORMAT_LJUST,
+};
+
+static const struct oxygen_model model_xonar_st = {
+ .longname = "Asus Virtuoso 100",
+ .chip = "AV200",
+ .init = xonar_st_init,
+ .control_filter = xonar_st_control_filter,
+ .mixer_init = xonar_st_mixer_init,
+ .cleanup = xonar_st_cleanup,
+ .suspend = xonar_st_suspend,
+ .resume = xonar_st_resume,
+ .get_i2s_mclk = get_pcm1796_i2s_mclk,
+ .set_dac_params = set_st_params,
+ .set_adc_params = xonar_set_cs53x1_params,
+ .update_dac_volume = update_pcm1796_volume,
+ .update_dac_mute = update_pcm1796_mute,
+ .ac97_switch = xonar_line_mic_ac97_switch,
+ .dac_tlv = pcm1796_db_scale,
+ .model_data_size = sizeof(struct xonar_pcm179x),
+ .device_config = PLAYBACK_0_TO_I2S |
+ PLAYBACK_1_TO_SPDIF |
+ CAPTURE_0_FROM_I2S_2,
+ .dac_channels = 2,
+ .dac_volume_min = 255 - 2*60,
+ .dac_volume_max = 255,
+ .function_flags = OXYGEN_FUNCTION_2WIRE,
+ .dac_i2s_format = OXYGEN_I2S_FORMAT_LJUST,
+ .adc_i2s_format = OXYGEN_I2S_FORMAT_LJUST,
+};
+
+int __devinit get_xonar_pcm179x_model(struct oxygen *chip,
+ const struct pci_device_id *id)
+{
+ switch (id->subdevice) {
+ case 0x8269:
+ chip->model = model_xonar_d2;
+ chip->model.shortname = "Xonar D2";
+ break;
+ case 0x82b7:
+ chip->model = model_xonar_d2;
+ chip->model.shortname = "Xonar D2X";
+ chip->model.init = xonar_d2x_init;
+ break;
+ case 0x8314:
+ chip->model = model_xonar_hdav;
+ oxygen_clear_bits16(chip, OXYGEN_GPIO_CONTROL, GPIO_DB_MASK);
+ switch (oxygen_read16(chip, OXYGEN_GPIO_DATA) & GPIO_DB_MASK) {
+ default:
+ chip->model.shortname = "Xonar HDAV1.3";
+ break;
+ case GPIO_DB_H6:
+ chip->model.shortname = "Xonar HDAV1.3+H6";
+ chip->model.private_data = 1;
+ break;
+ }
+ break;
+ case 0x835d:
+ chip->model = model_xonar_st;
+ oxygen_clear_bits16(chip, OXYGEN_GPIO_CONTROL, GPIO_DB_MASK);
+ switch (oxygen_read16(chip, OXYGEN_GPIO_DATA) & GPIO_DB_MASK) {
+ default:
+ chip->model.shortname = "Xonar ST";
+ break;
+ case GPIO_DB_H6:
+ chip->model.shortname = "Xonar ST+H6";
+ chip->model.dac_channels = 8;
+ chip->model.private_data = 1;
+ break;
+ }
+ break;
+ case 0x835c:
+ chip->model = model_xonar_st;
+ chip->model.shortname = "Xonar STX";
+ chip->model.init = xonar_stx_init;
+ chip->model.resume = xonar_stx_resume;
+ chip->model.set_dac_params = set_pcm1796_params;
+ break;
+ default:
+ return -EINVAL;
+ }
+ return 0;
+}
diff --git a/sound/ppc/awacs.c b/sound/ppc/awacs.c
index 2cc0eda4f20e..2e156467b814 100644
--- a/sound/ppc/awacs.c
+++ b/sound/ppc/awacs.c
@@ -479,7 +479,7 @@ static int snd_pmac_awacs_put_master_amp(struct snd_kcontrol *kcontrol,
static struct snd_kcontrol_new snd_pmac_awacs_amp_vol[] __devinitdata = {
{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
- .name = "PC Speaker Playback Volume",
+ .name = "Speaker Playback Volume",
.info = snd_pmac_awacs_info_volume_amp,
.get = snd_pmac_awacs_get_volume_amp,
.put = snd_pmac_awacs_put_volume_amp,
@@ -525,7 +525,7 @@ static struct snd_kcontrol_new snd_pmac_awacs_amp_hp_sw __devinitdata = {
static struct snd_kcontrol_new snd_pmac_awacs_amp_spk_sw __devinitdata = {
.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
- .name = "PC Speaker Playback Switch",
+ .name = "Speaker Playback Switch",
.info = snd_pmac_boolean_stereo_info,
.get = snd_pmac_awacs_get_switch_amp,
.put = snd_pmac_awacs_put_switch_amp,
@@ -696,17 +696,17 @@ static struct snd_kcontrol_new snd_pmac_screamer_mic_boost_imac[] __devinitdata
};
static struct snd_kcontrol_new snd_pmac_awacs_speaker_vol[] __devinitdata = {
- AWACS_VOLUME("PC Speaker Playback Volume", 4, 6, 1),
+ AWACS_VOLUME("Speaker Playback Volume", 4, 6, 1),
};
static struct snd_kcontrol_new snd_pmac_awacs_speaker_sw __devinitdata =
-AWACS_SWITCH("PC Speaker Playback Switch", 1, SHIFT_SPKMUTE, 1);
+AWACS_SWITCH("Speaker Playback Switch", 1, SHIFT_SPKMUTE, 1);
static struct snd_kcontrol_new snd_pmac_awacs_speaker_sw_imac1 __devinitdata =
-AWACS_SWITCH("PC Speaker Playback Switch", 1, SHIFT_PAROUT1, 1);
+AWACS_SWITCH("Speaker Playback Switch", 1, SHIFT_PAROUT1, 1);
static struct snd_kcontrol_new snd_pmac_awacs_speaker_sw_imac2 __devinitdata =
-AWACS_SWITCH("PC Speaker Playback Switch", 1, SHIFT_PAROUT1, 0);
+AWACS_SWITCH("Speaker Playback Switch", 1, SHIFT_PAROUT1, 0);
/*
diff --git a/sound/ppc/burgundy.c b/sound/ppc/burgundy.c
index 16ed240e423c..0accfe49735b 100644
--- a/sound/ppc/burgundy.c
+++ b/sound/ppc/burgundy.c
@@ -505,7 +505,7 @@ static struct snd_kcontrol_new snd_pmac_burgundy_mixers_imac[] __devinitdata = {
MASK_ADDR_BURGUNDY_GAINLINE, 1, 0),
BURGUNDY_VOLUME_B("Mic Gain Capture Volume", 0,
MASK_ADDR_BURGUNDY_GAINMIC, 1, 0),
- BURGUNDY_VOLUME_B("PC Speaker Playback Volume", 0,
+ BURGUNDY_VOLUME_B("Speaker Playback Volume", 0,
MASK_ADDR_BURGUNDY_ATTENSPEAKER, 1, 1),
BURGUNDY_VOLUME_B("Line out Playback Volume", 0,
MASK_ADDR_BURGUNDY_ATTENLINEOUT, 1, 1),
@@ -527,7 +527,7 @@ static struct snd_kcontrol_new snd_pmac_burgundy_mixers_pmac[] __devinitdata = {
MASK_ADDR_BURGUNDY_VOLMIC, 16),
BURGUNDY_VOLUME_B("Line in Gain Capture Volume", 0,
MASK_ADDR_BURGUNDY_GAINMIC, 1, 0),
- BURGUNDY_VOLUME_B("PC Speaker Playback Volume", 0,
+ BURGUNDY_VOLUME_B("Speaker Playback Volume", 0,
MASK_ADDR_BURGUNDY_ATTENMONO, 0, 1),
BURGUNDY_VOLUME_B("Line out Playback Volume", 0,
MASK_ADDR_BURGUNDY_ATTENSPEAKER, 1, 1),
@@ -549,11 +549,11 @@ BURGUNDY_SWITCH_B("Master Playback Switch", 0,
BURGUNDY_OUTPUT_INTERN
| BURGUNDY_OUTPUT_LEFT, BURGUNDY_OUTPUT_RIGHT, 1);
static struct snd_kcontrol_new snd_pmac_burgundy_speaker_sw_imac __devinitdata =
-BURGUNDY_SWITCH_B("PC Speaker Playback Switch", 0,
+BURGUNDY_SWITCH_B("Speaker Playback Switch", 0,
MASK_ADDR_BURGUNDY_MORE_OUTPUTENABLES,
BURGUNDY_OUTPUT_LEFT, BURGUNDY_OUTPUT_RIGHT, 1);
static struct snd_kcontrol_new snd_pmac_burgundy_speaker_sw_pmac __devinitdata =
-BURGUNDY_SWITCH_B("PC Speaker Playback Switch", 0,
+BURGUNDY_SWITCH_B("Speaker Playback Switch", 0,
MASK_ADDR_BURGUNDY_MORE_OUTPUTENABLES,
BURGUNDY_OUTPUT_INTERN, 0, 0);
static struct snd_kcontrol_new snd_pmac_burgundy_line_sw_imac __devinitdata =
diff --git a/sound/ppc/tumbler.c b/sound/ppc/tumbler.c
index 08e584d1453a..789f44f4ac78 100644
--- a/sound/ppc/tumbler.c
+++ b/sound/ppc/tumbler.c
@@ -905,7 +905,7 @@ static struct snd_kcontrol_new tumbler_hp_sw __devinitdata = {
};
static struct snd_kcontrol_new tumbler_speaker_sw __devinitdata = {
.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
- .name = "PC Speaker Playback Switch",
+ .name = "Speaker Playback Switch",
.info = snd_pmac_boolean_mono_info,
.get = tumbler_get_mute_switch,
.put = tumbler_put_mute_switch,
diff --git a/sound/sh/Kconfig b/sound/sh/Kconfig
index aed0f90c3919..61139f3c1614 100644
--- a/sound/sh/Kconfig
+++ b/sound/sh/Kconfig
@@ -19,5 +19,13 @@ config SND_AICA
help
ALSA Sound driver for the SEGA Dreamcast console.
+config SND_SH_DAC_AUDIO
+ tristate "SuperH DAC audio support"
+ depends on SND
+ depends on CPU_SH3 && HIGH_RES_TIMERS
+ select SND_PCM
+ help
+ Say Y here to include support for the on-chip DAC.
+
endif # SND_SUPERH
diff --git a/sound/sh/Makefile b/sound/sh/Makefile
index 8fdcb6e26f00..7d09b5188cf7 100644
--- a/sound/sh/Makefile
+++ b/sound/sh/Makefile
@@ -3,6 +3,8 @@
#
snd-aica-objs := aica.o
+snd-sh_dac_audio-objs := sh_dac_audio.o
# Toplevel Module Dependency
obj-$(CONFIG_SND_AICA) += snd-aica.o
+obj-$(CONFIG_SND_SH_DAC_AUDIO) += snd-sh_dac_audio.o
diff --git a/sound/sh/sh_dac_audio.c b/sound/sh/sh_dac_audio.c
new file mode 100644
index 000000000000..76d9ad27d91c
--- /dev/null
+++ b/sound/sh/sh_dac_audio.c
@@ -0,0 +1,453 @@
+/*
+ * sh_dac_audio.c - SuperH DAC audio driver for ALSA
+ *
+ * Copyright (c) 2009 by Rafael Ignacio Zurita <rizurita@yahoo.com>
+ *
+ *
+ * Based on sh_dac_audio.c (Copyright (C) 2004, 2005 by Andriy Skulysh)
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ */
+
+#include <linux/hrtimer.h>
+#include <linux/interrupt.h>
+#include <linux/io.h>
+#include <linux/platform_device.h>
+#include <sound/core.h>
+#include <sound/initval.h>
+#include <sound/pcm.h>
+#include <sound/sh_dac_audio.h>
+#include <asm/clock.h>
+#include <asm/hd64461.h>
+#include <mach/hp6xx.h>
+#include <cpu/dac.h>
+
+MODULE_AUTHOR("Rafael Ignacio Zurita <rizurita@yahoo.com>");
+MODULE_DESCRIPTION("SuperH DAC audio driver");
+MODULE_LICENSE("GPL");
+MODULE_SUPPORTED_DEVICE("{{SuperH DAC audio support}}");
+
+/* Module Parameters */
+static int index = SNDRV_DEFAULT_IDX1;
+static char *id = SNDRV_DEFAULT_STR1;
+module_param(index, int, 0444);
+MODULE_PARM_DESC(index, "Index value for SuperH DAC audio.");
+module_param(id, charp, 0444);
+MODULE_PARM_DESC(id, "ID string for SuperH DAC audio.");
+
+/* main struct */
+struct snd_sh_dac {
+ struct snd_card *card;
+ struct snd_pcm_substream *substream;
+ struct hrtimer hrtimer;
+ ktime_t wakeups_per_second;
+
+ int rate;
+ int empty;
+ char *data_buffer, *buffer_begin, *buffer_end;
+ int processed; /* bytes proccesed, to compare with period_size */
+ int buffer_size;
+ struct dac_audio_pdata *pdata;
+};
+
+
+static void dac_audio_start_timer(struct snd_sh_dac *chip)
+{
+ hrtimer_start(&chip->hrtimer, chip->wakeups_per_second,
+ HRTIMER_MODE_REL);
+}
+
+static void dac_audio_stop_timer(struct snd_sh_dac *chip)
+{
+ hrtimer_cancel(&chip->hrtimer);
+}
+
+static void dac_audio_reset(struct snd_sh_dac *chip)
+{
+ dac_audio_stop_timer(chip);
+ chip->buffer_begin = chip->buffer_end = chip->data_buffer;
+ chip->processed = 0;
+ chip->empty = 1;
+}
+
+static void dac_audio_set_rate(struct snd_sh_dac *chip)
+{
+ chip->wakeups_per_second = ktime_set(0, 1000000000 / chip->rate);
+}
+
+
+/* PCM INTERFACE */
+
+static struct snd_pcm_hardware snd_sh_dac_pcm_hw = {
+ .info = (SNDRV_PCM_INFO_MMAP |
+ SNDRV_PCM_INFO_MMAP_VALID |
+ SNDRV_PCM_INFO_INTERLEAVED |
+ SNDRV_PCM_INFO_HALF_DUPLEX),
+ .formats = SNDRV_PCM_FMTBIT_U8,
+ .rates = SNDRV_PCM_RATE_8000,
+ .rate_min = 8000,
+ .rate_max = 8000,
+ .channels_min = 1,
+ .channels_max = 1,
+ .buffer_bytes_max = (48*1024),
+ .period_bytes_min = 1,
+ .period_bytes_max = (48*1024),
+ .periods_min = 1,
+ .periods_max = 1024,
+};
+
+static int snd_sh_dac_pcm_open(struct snd_pcm_substream *substream)
+{
+ struct snd_sh_dac *chip = snd_pcm_substream_chip(substream);
+ struct snd_pcm_runtime *runtime = substream->runtime;
+
+ runtime->hw = snd_sh_dac_pcm_hw;
+
+ chip->substream = substream;
+ chip->buffer_begin = chip->buffer_end = chip->data_buffer;
+ chip->processed = 0;
+ chip->empty = 1;
+
+ chip->pdata->start(chip->pdata);
+
+ return 0;
+}
+
+static int snd_sh_dac_pcm_close(struct snd_pcm_substream *substream)
+{
+ struct snd_sh_dac *chip = snd_pcm_substream_chip(substream);
+
+ chip->substream = NULL;
+
+ dac_audio_stop_timer(chip);
+ chip->pdata->stop(chip->pdata);
+
+ return 0;
+}
+
+static int snd_sh_dac_pcm_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *hw_params)
+{
+ return snd_pcm_lib_malloc_pages(substream,
+ params_buffer_bytes(hw_params));
+}
+
+static int snd_sh_dac_pcm_hw_free(struct snd_pcm_substream *substream)
+{
+ return snd_pcm_lib_free_pages(substream);
+}
+
+static int snd_sh_dac_pcm_prepare(struct snd_pcm_substream *substream)
+{
+ struct snd_sh_dac *chip = snd_pcm_substream_chip(substream);
+ struct snd_pcm_runtime *runtime = chip->substream->runtime;
+
+ chip->buffer_size = runtime->buffer_size;
+ memset(chip->data_buffer, 0, chip->pdata->buffer_size);
+
+ return 0;
+}
+
+static int snd_sh_dac_pcm_trigger(struct snd_pcm_substream *substream, int cmd)
+{
+ struct snd_sh_dac *chip = snd_pcm_substream_chip(substream);
+
+ switch (cmd) {
+ case SNDRV_PCM_TRIGGER_START:
+ dac_audio_start_timer(chip);
+ break;
+ case SNDRV_PCM_TRIGGER_STOP:
+ chip->buffer_begin = chip->buffer_end = chip->data_buffer;
+ chip->processed = 0;
+ chip->empty = 1;
+ dac_audio_stop_timer(chip);
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static int snd_sh_dac_pcm_copy(struct snd_pcm_substream *substream, int channel,
+ snd_pcm_uframes_t pos, void __user *src, snd_pcm_uframes_t count)
+{
+ /* channel is not used (interleaved data) */
+ struct snd_sh_dac *chip = snd_pcm_substream_chip(substream);
+ struct snd_pcm_runtime *runtime = substream->runtime;
+ ssize_t b_count = frames_to_bytes(runtime , count);
+ ssize_t b_pos = frames_to_bytes(runtime , pos);
+
+ if (count < 0)
+ return -EINVAL;
+
+ if (!count)
+ return 0;
+
+ memcpy_toio(chip->data_buffer + b_pos, src, b_count);
+ chip->buffer_end = chip->data_buffer + b_pos + b_count;
+
+ if (chip->empty) {
+ chip->empty = 0;
+ dac_audio_start_timer(chip);
+ }
+
+ return 0;
+}
+
+static int snd_sh_dac_pcm_silence(struct snd_pcm_substream *substream,
+ int channel, snd_pcm_uframes_t pos,
+ snd_pcm_uframes_t count)
+{
+ /* channel is not used (interleaved data) */
+ struct snd_sh_dac *chip = snd_pcm_substream_chip(substream);
+ struct snd_pcm_runtime *runtime = substream->runtime;
+ ssize_t b_count = frames_to_bytes(runtime , count);
+ ssize_t b_pos = frames_to_bytes(runtime , pos);
+
+ if (count < 0)
+ return -EINVAL;
+
+ if (!count)
+ return 0;
+
+ memset_io(chip->data_buffer + b_pos, 0, b_count);
+ chip->buffer_end = chip->data_buffer + b_pos + b_count;
+
+ if (chip->empty) {
+ chip->empty = 0;
+ dac_audio_start_timer(chip);
+ }
+
+ return 0;
+}
+
+static
+snd_pcm_uframes_t snd_sh_dac_pcm_pointer(struct snd_pcm_substream *substream)
+{
+ struct snd_sh_dac *chip = snd_pcm_substream_chip(substream);
+ int pointer = chip->buffer_begin - chip->data_buffer;
+
+ return pointer;
+}
+
+/* pcm ops */
+static struct snd_pcm_ops snd_sh_dac_pcm_ops = {
+ .open = snd_sh_dac_pcm_open,
+ .close = snd_sh_dac_pcm_close,
+ .ioctl = snd_pcm_lib_ioctl,
+ .hw_params = snd_sh_dac_pcm_hw_params,
+ .hw_free = snd_sh_dac_pcm_hw_free,
+ .prepare = snd_sh_dac_pcm_prepare,
+ .trigger = snd_sh_dac_pcm_trigger,
+ .pointer = snd_sh_dac_pcm_pointer,
+ .copy = snd_sh_dac_pcm_copy,
+ .silence = snd_sh_dac_pcm_silence,
+ .mmap = snd_pcm_lib_mmap_iomem,
+};
+
+static int __devinit snd_sh_dac_pcm(struct snd_sh_dac *chip, int device)
+{
+ int err;
+ struct snd_pcm *pcm;
+
+ /* device should be always 0 for us */
+ err = snd_pcm_new(chip->card, "SH_DAC PCM", device, 1, 0, &pcm);
+ if (err < 0)
+ return err;
+
+ pcm->private_data = chip;
+ strcpy(pcm->name, "SH_DAC PCM");
+ snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &snd_sh_dac_pcm_ops);
+
+ /* buffer size=48K */
+ snd_pcm_lib_preallocate_pages_for_all(pcm, SNDRV_DMA_TYPE_CONTINUOUS,
+ snd_dma_continuous_data(GFP_KERNEL),
+ 48 * 1024,
+ 48 * 1024);
+
+ return 0;
+}
+/* END OF PCM INTERFACE */
+
+
+/* driver .remove -- destructor */
+static int snd_sh_dac_remove(struct platform_device *devptr)
+{
+ snd_card_free(platform_get_drvdata(devptr));
+ platform_set_drvdata(devptr, NULL);
+
+ return 0;
+}
+
+/* free -- it has been defined by create */
+static int snd_sh_dac_free(struct snd_sh_dac *chip)
+{
+ /* release the data */
+ kfree(chip->data_buffer);
+ kfree(chip);
+
+ return 0;
+}
+
+static int snd_sh_dac_dev_free(struct snd_device *device)
+{
+ struct snd_sh_dac *chip = device->device_data;
+
+ return snd_sh_dac_free(chip);
+}
+
+static enum hrtimer_restart sh_dac_audio_timer(struct hrtimer *handle)
+{
+ struct snd_sh_dac *chip = container_of(handle, struct snd_sh_dac,
+ hrtimer);
+ struct snd_pcm_runtime *runtime = chip->substream->runtime;
+ ssize_t b_ps = frames_to_bytes(runtime, runtime->period_size);
+
+ if (!chip->empty) {
+ sh_dac_output(*chip->buffer_begin, chip->pdata->channel);
+ chip->buffer_begin++;
+
+ chip->processed++;
+ if (chip->processed >= b_ps) {
+ chip->processed -= b_ps;
+ snd_pcm_period_elapsed(chip->substream);
+ }
+
+ if (chip->buffer_begin == (chip->data_buffer +
+ chip->buffer_size - 1))
+ chip->buffer_begin = chip->data_buffer;
+
+ if (chip->buffer_begin == chip->buffer_end)
+ chip->empty = 1;
+
+ }
+
+ if (!chip->empty)
+ hrtimer_start(&chip->hrtimer, chip->wakeups_per_second,
+ HRTIMER_MODE_REL);
+
+ return HRTIMER_NORESTART;
+}
+
+/* create -- chip-specific constructor for the cards components */
+static int __devinit snd_sh_dac_create(struct snd_card *card,
+ struct platform_device *devptr,
+ struct snd_sh_dac **rchip)
+{
+ struct snd_sh_dac *chip;
+ int err;
+
+ static struct snd_device_ops ops = {
+ .dev_free = snd_sh_dac_dev_free,
+ };
+
+ *rchip = NULL;
+
+ chip = kzalloc(sizeof(*chip), GFP_KERNEL);
+ if (chip == NULL)
+ return -ENOMEM;
+
+ chip->card = card;
+
+ hrtimer_init(&chip->hrtimer, CLOCK_MONOTONIC, HRTIMER_MODE_REL);
+ chip->hrtimer.function = sh_dac_audio_timer;
+
+ dac_audio_reset(chip);
+ chip->rate = 8000;
+ dac_audio_set_rate(chip);
+
+ chip->pdata = devptr->dev.platform_data;
+
+ chip->data_buffer = kmalloc(chip->pdata->buffer_size, GFP_KERNEL);
+ if (chip->data_buffer == NULL) {
+ kfree(chip);
+ return -ENOMEM;
+ }
+
+ err = snd_device_new(card, SNDRV_DEV_LOWLEVEL, chip, &ops);
+ if (err < 0) {
+ snd_sh_dac_free(chip);
+ return err;
+ }
+
+ *rchip = chip;
+
+ return 0;
+}
+
+/* driver .probe -- constructor */
+static int __devinit snd_sh_dac_probe(struct platform_device *devptr)
+{
+ struct snd_sh_dac *chip;
+ struct snd_card *card;
+ int err;
+
+ err = snd_card_create(index, id, THIS_MODULE, 0, &card);
+ if (err < 0) {
+ snd_printk(KERN_ERR "cannot allocate the card\n");
+ return err;
+ }
+
+ err = snd_sh_dac_create(card, devptr, &chip);
+ if (err < 0)
+ goto probe_error;
+
+ err = snd_sh_dac_pcm(chip, 0);
+ if (err < 0)
+ goto probe_error;
+
+ strcpy(card->driver, "snd_sh_dac");
+ strcpy(card->shortname, "SuperH DAC audio driver");
+ printk(KERN_INFO "%s %s", card->longname, card->shortname);
+
+ err = snd_card_register(card);
+ if (err < 0)
+ goto probe_error;
+
+ snd_printk("ALSA driver for SuperH DAC audio");
+
+ platform_set_drvdata(devptr, card);
+ return 0;
+
+probe_error:
+ snd_card_free(card);
+ return err;
+}
+
+/*
+ * "driver" definition
+ */
+static struct platform_driver driver = {
+ .probe = snd_sh_dac_probe,
+ .remove = snd_sh_dac_remove,
+ .driver = {
+ .name = "dac_audio",
+ },
+};
+
+static int __init sh_dac_init(void)
+{
+ return platform_driver_register(&driver);
+}
+
+static void __exit sh_dac_exit(void)
+{
+ platform_driver_unregister(&driver);
+}
+
+module_init(sh_dac_init);
+module_exit(sh_dac_exit);
diff --git a/sound/soc/Makefile b/sound/soc/Makefile
index 0c5eac01bf2e..1470141d4167 100644
--- a/sound/soc/Makefile
+++ b/sound/soc/Makefile
@@ -1,4 +1,4 @@
-snd-soc-core-objs := soc-core.o soc-dapm.o soc-jack.o soc-cache.o
+snd-soc-core-objs := soc-core.o soc-dapm.o soc-jack.o soc-cache.o soc-utils.o
obj-$(CONFIG_SND_SOC) += snd-soc-core.o
obj-$(CONFIG_SND_SOC) += codecs/
diff --git a/sound/soc/atmel/playpaq_wm8510.c b/sound/soc/atmel/playpaq_wm8510.c
index 9eb610c2ba91..9df4c68ef000 100644
--- a/sound/soc/atmel/playpaq_wm8510.c
+++ b/sound/soc/atmel/playpaq_wm8510.c
@@ -268,7 +268,7 @@ static int playpaq_wm8510_hw_params(struct snd_pcm_substream *substream,
#endif /* CONFIG_SND_AT32_SOC_PLAYPAQ_SLAVE */
- ret = snd_soc_dai_set_pll(codec_dai, 0,
+ ret = snd_soc_dai_set_pll(codec_dai, 0, 0,
clk_get_rate(CODEC_CLK), pll_out);
if (ret < 0) {
pr_warning("playpaq_wm8510: Failed to set CODEC DAI PLL (%d)\n",
diff --git a/sound/soc/atmel/sam9g20_wm8731.c b/sound/soc/atmel/sam9g20_wm8731.c
index 885ba012557e..e028744c32ce 100644
--- a/sound/soc/atmel/sam9g20_wm8731.c
+++ b/sound/soc/atmel/sam9g20_wm8731.c
@@ -207,7 +207,7 @@ static int __init at91sam9g20ek_init(void)
struct clk *pllb;
int ret;
- if (!machine_is_at91sam9g20ek())
+ if (!(machine_is_at91sam9g20ek() || machine_is_at91sam9g20ek_2mmc()))
return -ENODEV;
/*
diff --git a/sound/soc/au1x/dbdma2.c b/sound/soc/au1x/dbdma2.c
index 594c6c5b7838..19e4d37eba1c 100644
--- a/sound/soc/au1x/dbdma2.c
+++ b/sound/soc/au1x/dbdma2.c
@@ -2,7 +2,7 @@
* Au12x0/Au1550 PSC ALSA ASoC audio support.
*
* (c) 2007-2008 MSC Vertriebsges.m.b.H.,
- * Manuel Lauss <mano@roarinelk.homelinux.net>
+ * Manuel Lauss <manuel.lauss@gmail.com>
*
* 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
@@ -333,6 +333,30 @@ static int au1xpsc_pcm_new(struct snd_card *card,
static int au1xpsc_pcm_probe(struct platform_device *pdev)
{
+ if (!au1xpsc_audio_pcmdma[PCM_TX] || !au1xpsc_audio_pcmdma[PCM_RX])
+ return -ENODEV;
+
+ return 0;
+}
+
+static int au1xpsc_pcm_remove(struct platform_device *pdev)
+{
+ return 0;
+}
+
+/* au1xpsc audio platform */
+struct snd_soc_platform au1xpsc_soc_platform = {
+ .name = "au1xpsc-pcm-dbdma",
+ .probe = au1xpsc_pcm_probe,
+ .remove = au1xpsc_pcm_remove,
+ .pcm_ops = &au1xpsc_pcm_ops,
+ .pcm_new = au1xpsc_pcm_new,
+ .pcm_free = au1xpsc_pcm_free_dma_buffers,
+};
+EXPORT_SYMBOL_GPL(au1xpsc_soc_platform);
+
+static int __devinit au1xpsc_pcm_drvprobe(struct platform_device *pdev)
+{
struct resource *r;
int ret;
@@ -365,7 +389,9 @@ static int au1xpsc_pcm_probe(struct platform_device *pdev)
}
(au1xpsc_audio_pcmdma[PCM_RX])->ddma_id = r->start;
- return 0;
+ ret = snd_soc_register_platform(&au1xpsc_soc_platform);
+ if (!ret)
+ return ret;
out2:
kfree(au1xpsc_audio_pcmdma[PCM_RX]);
@@ -376,10 +402,12 @@ out1:
return ret;
}
-static int au1xpsc_pcm_remove(struct platform_device *pdev)
+static int __devexit au1xpsc_pcm_drvremove(struct platform_device *pdev)
{
int i;
+ snd_soc_unregister_platform(&au1xpsc_soc_platform);
+
for (i = 0; i < 2; i++) {
if (au1xpsc_audio_pcmdma[i]) {
au1x_pcm_dbdma_free(au1xpsc_audio_pcmdma[i]);
@@ -391,32 +419,81 @@ static int au1xpsc_pcm_remove(struct platform_device *pdev)
return 0;
}
-/* au1xpsc audio platform */
-struct snd_soc_platform au1xpsc_soc_platform = {
- .name = "au1xpsc-pcm-dbdma",
- .probe = au1xpsc_pcm_probe,
- .remove = au1xpsc_pcm_remove,
- .pcm_ops = &au1xpsc_pcm_ops,
- .pcm_new = au1xpsc_pcm_new,
- .pcm_free = au1xpsc_pcm_free_dma_buffers,
+static struct platform_driver au1xpsc_pcm_driver = {
+ .driver = {
+ .name = "au1xpsc-pcm",
+ .owner = THIS_MODULE,
+ },
+ .probe = au1xpsc_pcm_drvprobe,
+ .remove = __devexit_p(au1xpsc_pcm_drvremove),
};
-EXPORT_SYMBOL_GPL(au1xpsc_soc_platform);
-static int __init au1xpsc_audio_dbdma_init(void)
+static int __init au1xpsc_audio_dbdma_load(void)
{
au1xpsc_audio_pcmdma[PCM_TX] = NULL;
au1xpsc_audio_pcmdma[PCM_RX] = NULL;
- return snd_soc_register_platform(&au1xpsc_soc_platform);
+ return platform_driver_register(&au1xpsc_pcm_driver);
}
-static void __exit au1xpsc_audio_dbdma_exit(void)
+static void __exit au1xpsc_audio_dbdma_unload(void)
{
- snd_soc_unregister_platform(&au1xpsc_soc_platform);
+ platform_driver_unregister(&au1xpsc_pcm_driver);
}
-module_init(au1xpsc_audio_dbdma_init);
-module_exit(au1xpsc_audio_dbdma_exit);
+module_init(au1xpsc_audio_dbdma_load);
+module_exit(au1xpsc_audio_dbdma_unload);
+
+
+struct platform_device *au1xpsc_pcm_add(struct platform_device *pdev)
+{
+ struct resource *res, *r;
+ struct platform_device *pd;
+ int id[2];
+ int ret;
+
+ r = platform_get_resource(pdev, IORESOURCE_DMA, 0);
+ if (!r)
+ return NULL;
+ id[0] = r->start;
+
+ r = platform_get_resource(pdev, IORESOURCE_DMA, 1);
+ if (!r)
+ return NULL;
+ id[1] = r->start;
+
+ res = kzalloc(sizeof(struct resource) * 2, GFP_KERNEL);
+ if (!res)
+ return NULL;
+
+ res[0].start = res[0].end = id[0];
+ res[1].start = res[1].end = id[1];
+ res[0].flags = res[1].flags = IORESOURCE_DMA;
+
+ pd = platform_device_alloc("au1xpsc-pcm", -1);
+ if (!pd)
+ goto out;
+
+ pd->resource = res;
+ pd->num_resources = 2;
+
+ ret = platform_device_add(pd);
+ if (!ret)
+ return pd;
+
+ platform_device_put(pd);
+out:
+ kfree(res);
+ return NULL;
+}
+EXPORT_SYMBOL_GPL(au1xpsc_pcm_add);
+
+void au1xpsc_pcm_destroy(struct platform_device *dmapd)
+{
+ if (dmapd)
+ platform_device_unregister(dmapd);
+}
+EXPORT_SYMBOL_GPL(au1xpsc_pcm_destroy);
MODULE_LICENSE("GPL");
MODULE_DESCRIPTION("Au12x0/Au1550 PSC Audio DMA driver");
-MODULE_AUTHOR("Manuel Lauss <mano@roarinelk.homelinux.net>");
+MODULE_AUTHOR("Manuel Lauss");
diff --git a/sound/soc/au1x/psc-ac97.c b/sound/soc/au1x/psc-ac97.c
index a521aa90ddee..340311d7fed5 100644
--- a/sound/soc/au1x/psc-ac97.c
+++ b/sound/soc/au1x/psc-ac97.c
@@ -61,7 +61,8 @@ static unsigned short au1xpsc_ac97_read(struct snd_ac97 *ac97,
{
/* FIXME */
struct au1xpsc_audio_data *pscdata = au1xpsc_ac97_workdata;
- unsigned short data, retry, tmo;
+ unsigned short retry, tmo;
+ unsigned long data;
au_writel(PSC_AC97EVNT_CD, AC97_EVNT(pscdata));
au_sync();
@@ -74,20 +75,26 @@ static unsigned short au1xpsc_ac97_read(struct snd_ac97 *ac97,
AC97_CDC(pscdata));
au_sync();
- tmo = 2000;
- while ((!(au_readl(AC97_EVNT(pscdata)) & PSC_AC97EVNT_CD))
- && --tmo)
- udelay(2);
+ tmo = 20;
+ do {
+ udelay(21);
+ if (au_readl(AC97_EVNT(pscdata)) & PSC_AC97EVNT_CD)
+ break;
+ } while (--tmo);
- data = au_readl(AC97_CDC(pscdata)) & 0xffff;
+ data = au_readl(AC97_CDC(pscdata));
au_writel(PSC_AC97EVNT_CD, AC97_EVNT(pscdata));
au_sync();
mutex_unlock(&pscdata->lock);
+
+ if (reg != ((data >> 16) & 0x7f))
+ tmo = 1; /* wrong register, try again */
+
} while (--retry && !tmo);
- return retry ? data : 0xffff;
+ return retry ? data & 0xffff : 0xffff;
}
/* AC97 controller writes to codec register */
@@ -109,10 +116,12 @@ static void au1xpsc_ac97_write(struct snd_ac97 *ac97, unsigned short reg,
AC97_CDC(pscdata));
au_sync();
- tmo = 2000;
- while ((!(au_readl(AC97_EVNT(pscdata)) & PSC_AC97EVNT_CD))
- && --tmo)
- udelay(2);
+ tmo = 20;
+ do {
+ udelay(21);
+ if (au_readl(AC97_EVNT(pscdata)) & PSC_AC97EVNT_CD)
+ break;
+ } while (--tmo);
au_writel(PSC_AC97EVNT_CD, AC97_EVNT(pscdata));
au_sync();
@@ -195,7 +204,7 @@ static int au1xpsc_ac97_hw_params(struct snd_pcm_substream *substream,
/* FIXME */
struct au1xpsc_audio_data *pscdata = au1xpsc_ac97_workdata;
unsigned long r, ro, stat;
- int chans, stype = SUBSTREAM_TYPE(substream);
+ int chans, t, stype = SUBSTREAM_TYPE(substream);
chans = params_channels(params);
@@ -237,8 +246,12 @@ static int au1xpsc_ac97_hw_params(struct snd_pcm_substream *substream,
au_sync();
/* ...wait for it... */
- while (au_readl(AC97_STAT(pscdata)) & PSC_AC97STAT_DR)
- asm volatile ("nop");
+ t = 100;
+ while ((au_readl(AC97_STAT(pscdata)) & PSC_AC97STAT_DR) && --t)
+ msleep(1);
+
+ if (!t)
+ printk(KERN_ERR "PSC-AC97: can't disable!\n");
/* ...write config... */
au_writel(r, AC97_CFG(pscdata));
@@ -249,8 +262,12 @@ static int au1xpsc_ac97_hw_params(struct snd_pcm_substream *substream,
au_sync();
/* ...and wait for ready bit */
- while (!(au_readl(AC97_STAT(pscdata)) & PSC_AC97STAT_DR))
- asm volatile ("nop");
+ t = 100;
+ while ((!(au_readl(AC97_STAT(pscdata)) & PSC_AC97STAT_DR)) && --t)
+ msleep(1);
+
+ if (!t)
+ printk(KERN_ERR "PSC-AC97: can't enable!\n");
mutex_unlock(&pscdata->lock);
@@ -300,19 +317,55 @@ static int au1xpsc_ac97_trigger(struct snd_pcm_substream *substream,
static int au1xpsc_ac97_probe(struct platform_device *pdev,
struct snd_soc_dai *dai)
{
+ return au1xpsc_ac97_workdata ? 0 : -ENODEV;
+}
+
+static void au1xpsc_ac97_remove(struct platform_device *pdev,
+ struct snd_soc_dai *dai)
+{
+}
+
+static struct snd_soc_dai_ops au1xpsc_ac97_dai_ops = {
+ .trigger = au1xpsc_ac97_trigger,
+ .hw_params = au1xpsc_ac97_hw_params,
+};
+
+struct snd_soc_dai au1xpsc_ac97_dai = {
+ .name = "au1xpsc_ac97",
+ .ac97_control = 1,
+ .probe = au1xpsc_ac97_probe,
+ .remove = au1xpsc_ac97_remove,
+ .playback = {
+ .rates = AC97_RATES,
+ .formats = AC97_FMTS,
+ .channels_min = 2,
+ .channels_max = 2,
+ },
+ .capture = {
+ .rates = AC97_RATES,
+ .formats = AC97_FMTS,
+ .channels_min = 2,
+ .channels_max = 2,
+ },
+ .ops = &au1xpsc_ac97_dai_ops,
+};
+EXPORT_SYMBOL_GPL(au1xpsc_ac97_dai);
+
+static int __devinit au1xpsc_ac97_drvprobe(struct platform_device *pdev)
+{
int ret;
struct resource *r;
unsigned long sel;
+ struct au1xpsc_audio_data *wd;
if (au1xpsc_ac97_workdata)
return -EBUSY;
- au1xpsc_ac97_workdata =
- kzalloc(sizeof(struct au1xpsc_audio_data), GFP_KERNEL);
- if (!au1xpsc_ac97_workdata)
+ wd = kzalloc(sizeof(struct au1xpsc_audio_data), GFP_KERNEL);
+ if (!wd)
return -ENOMEM;
- mutex_init(&au1xpsc_ac97_workdata->lock);
+ mutex_init(&wd->lock);
r = platform_get_resource(pdev, IORESOURCE_MEM, 0);
if (!r) {
@@ -321,81 +374,95 @@ static int au1xpsc_ac97_probe(struct platform_device *pdev,
}
ret = -EBUSY;
- au1xpsc_ac97_workdata->ioarea =
- request_mem_region(r->start, r->end - r->start + 1,
+ wd->ioarea = request_mem_region(r->start, r->end - r->start + 1,
"au1xpsc_ac97");
- if (!au1xpsc_ac97_workdata->ioarea)
+ if (!wd->ioarea)
goto out0;
- au1xpsc_ac97_workdata->mmio = ioremap(r->start, 0xffff);
- if (!au1xpsc_ac97_workdata->mmio)
+ wd->mmio = ioremap(r->start, 0xffff);
+ if (!wd->mmio)
goto out1;
/* configuration: max dma trigger threshold, enable ac97 */
- au1xpsc_ac97_workdata->cfg = PSC_AC97CFG_RT_FIFO8 |
- PSC_AC97CFG_TT_FIFO8 |
- PSC_AC97CFG_DE_ENABLE;
+ wd->cfg = PSC_AC97CFG_RT_FIFO8 | PSC_AC97CFG_TT_FIFO8 |
+ PSC_AC97CFG_DE_ENABLE;
- /* preserve PSC clock source set up by platform (dev.platform_data
- * is already occupied by soc layer)
- */
- sel = au_readl(PSC_SEL(au1xpsc_ac97_workdata)) & PSC_SEL_CLK_MASK;
- au_writel(PSC_CTRL_DISABLE, PSC_CTRL(au1xpsc_ac97_workdata));
+ /* preserve PSC clock source set up by platform */
+ sel = au_readl(PSC_SEL(wd)) & PSC_SEL_CLK_MASK;
+ au_writel(PSC_CTRL_DISABLE, PSC_CTRL(wd));
au_sync();
- au_writel(0, PSC_SEL(au1xpsc_ac97_workdata));
+ au_writel(0, PSC_SEL(wd));
au_sync();
- au_writel(PSC_SEL_PS_AC97MODE | sel, PSC_SEL(au1xpsc_ac97_workdata));
+ au_writel(PSC_SEL_PS_AC97MODE | sel, PSC_SEL(wd));
au_sync();
- /* next up: cold reset. Dont check for PSC-ready now since
- * there may not be any codec clock yet.
- */
- return 0;
+ ret = snd_soc_register_dai(&au1xpsc_ac97_dai);
+ if (ret)
+ goto out1;
+ wd->dmapd = au1xpsc_pcm_add(pdev);
+ if (wd->dmapd) {
+ platform_set_drvdata(pdev, wd);
+ au1xpsc_ac97_workdata = wd; /* MDEV */
+ return 0;
+ }
+
+ snd_soc_unregister_dai(&au1xpsc_ac97_dai);
out1:
- release_resource(au1xpsc_ac97_workdata->ioarea);
- kfree(au1xpsc_ac97_workdata->ioarea);
+ release_resource(wd->ioarea);
+ kfree(wd->ioarea);
out0:
- kfree(au1xpsc_ac97_workdata);
- au1xpsc_ac97_workdata = NULL;
+ kfree(wd);
return ret;
}
-static void au1xpsc_ac97_remove(struct platform_device *pdev,
- struct snd_soc_dai *dai)
+static int __devexit au1xpsc_ac97_drvremove(struct platform_device *pdev)
{
+ struct au1xpsc_audio_data *wd = platform_get_drvdata(pdev);
+
+ if (wd->dmapd)
+ au1xpsc_pcm_destroy(wd->dmapd);
+
+ snd_soc_unregister_dai(&au1xpsc_ac97_dai);
+
/* disable PSC completely */
- au_writel(0, AC97_CFG(au1xpsc_ac97_workdata));
+ au_writel(0, AC97_CFG(wd));
au_sync();
- au_writel(PSC_CTRL_DISABLE, PSC_CTRL(au1xpsc_ac97_workdata));
+ au_writel(PSC_CTRL_DISABLE, PSC_CTRL(wd));
au_sync();
- iounmap(au1xpsc_ac97_workdata->mmio);
- release_resource(au1xpsc_ac97_workdata->ioarea);
- kfree(au1xpsc_ac97_workdata->ioarea);
- kfree(au1xpsc_ac97_workdata);
- au1xpsc_ac97_workdata = NULL;
+ iounmap(wd->mmio);
+ release_resource(wd->ioarea);
+ kfree(wd->ioarea);
+ kfree(wd);
+
+ au1xpsc_ac97_workdata = NULL; /* MDEV */
+
+ return 0;
}
-static int au1xpsc_ac97_suspend(struct snd_soc_dai *dai)
+#ifdef CONFIG_PM
+static int au1xpsc_ac97_drvsuspend(struct device *dev)
{
+ struct au1xpsc_audio_data *wd = dev_get_drvdata(dev);
+
/* save interesting registers and disable PSC */
- au1xpsc_ac97_workdata->pm[0] =
- au_readl(PSC_SEL(au1xpsc_ac97_workdata));
+ wd->pm[0] = au_readl(PSC_SEL(wd));
- au_writel(0, AC97_CFG(au1xpsc_ac97_workdata));
+ au_writel(0, AC97_CFG(wd));
au_sync();
- au_writel(PSC_CTRL_DISABLE, PSC_CTRL(au1xpsc_ac97_workdata));
+ au_writel(PSC_CTRL_DISABLE, PSC_CTRL(wd));
au_sync();
return 0;
}
-static int au1xpsc_ac97_resume(struct snd_soc_dai *dai)
+static int au1xpsc_ac97_drvresume(struct device *dev)
{
+ struct au1xpsc_audio_data *wd = dev_get_drvdata(dev);
+
/* restore PSC clock config */
- au_writel(au1xpsc_ac97_workdata->pm[0] | PSC_SEL_PS_AC97MODE,
- PSC_SEL(au1xpsc_ac97_workdata));
+ au_writel(wd->pm[0] | PSC_SEL_PS_AC97MODE, PSC_SEL(wd));
au_sync();
/* after this point the ac97 core will cold-reset the codec.
@@ -405,48 +472,44 @@ static int au1xpsc_ac97_resume(struct snd_soc_dai *dai)
return 0;
}
-static struct snd_soc_dai_ops au1xpsc_ac97_dai_ops = {
- .trigger = au1xpsc_ac97_trigger,
- .hw_params = au1xpsc_ac97_hw_params,
+static struct dev_pm_ops au1xpscac97_pmops = {
+ .suspend = au1xpsc_ac97_drvsuspend,
+ .resume = au1xpsc_ac97_drvresume,
};
-struct snd_soc_dai au1xpsc_ac97_dai = {
- .name = "au1xpsc_ac97",
- .ac97_control = 1,
- .probe = au1xpsc_ac97_probe,
- .remove = au1xpsc_ac97_remove,
- .suspend = au1xpsc_ac97_suspend,
- .resume = au1xpsc_ac97_resume,
- .playback = {
- .rates = AC97_RATES,
- .formats = AC97_FMTS,
- .channels_min = 2,
- .channels_max = 2,
- },
- .capture = {
- .rates = AC97_RATES,
- .formats = AC97_FMTS,
- .channels_min = 2,
- .channels_max = 2,
+#define AU1XPSCAC97_PMOPS &au1xpscac97_pmops
+
+#else
+
+#define AU1XPSCAC97_PMOPS NULL
+
+#endif
+
+static struct platform_driver au1xpsc_ac97_driver = {
+ .driver = {
+ .name = "au1xpsc_ac97",
+ .owner = THIS_MODULE,
+ .pm = AU1XPSCAC97_PMOPS,
},
- .ops = &au1xpsc_ac97_dai_ops,
+ .probe = au1xpsc_ac97_drvprobe,
+ .remove = __devexit_p(au1xpsc_ac97_drvremove),
};
-EXPORT_SYMBOL_GPL(au1xpsc_ac97_dai);
-static int __init au1xpsc_ac97_init(void)
+static int __init au1xpsc_ac97_load(void)
{
au1xpsc_ac97_workdata = NULL;
- return snd_soc_register_dai(&au1xpsc_ac97_dai);
+ return platform_driver_register(&au1xpsc_ac97_driver);
}
-static void __exit au1xpsc_ac97_exit(void)
+static void __exit au1xpsc_ac97_unload(void)
{
- snd_soc_unregister_dai(&au1xpsc_ac97_dai);
+ platform_driver_unregister(&au1xpsc_ac97_driver);
}
-module_init(au1xpsc_ac97_init);
-module_exit(au1xpsc_ac97_exit);
+module_init(au1xpsc_ac97_load);
+module_exit(au1xpsc_ac97_unload);
MODULE_LICENSE("GPL");
MODULE_DESCRIPTION("Au12x0/Au1550 PSC AC97 ALSA ASoC audio driver");
-MODULE_AUTHOR("Manuel Lauss <manuel.lauss@gmail.com>");
+MODULE_AUTHOR("Manuel Lauss");
+
diff --git a/sound/soc/au1x/psc-i2s.c b/sound/soc/au1x/psc-i2s.c
index bb589327ee32..0cf2ca61c776 100644
--- a/sound/soc/au1x/psc-i2s.c
+++ b/sound/soc/au1x/psc-i2s.c
@@ -2,7 +2,7 @@
* Au12x0/Au1550 PSC ALSA ASoC audio support.
*
* (c) 2007-2008 MSC Vertriebsges.m.b.H.,
- * Manuel Lauss <mano@roarinelk.homelinux.net>
+ * Manuel Lauss <manuel.lauss@gmail.com>
*
* 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
@@ -265,16 +265,52 @@ static int au1xpsc_i2s_trigger(struct snd_pcm_substream *substream, int cmd,
static int au1xpsc_i2s_probe(struct platform_device *pdev,
struct snd_soc_dai *dai)
{
+ return au1xpsc_i2s_workdata ? 0 : -ENODEV;
+}
+
+static void au1xpsc_i2s_remove(struct platform_device *pdev,
+ struct snd_soc_dai *dai)
+{
+}
+
+static struct snd_soc_dai_ops au1xpsc_i2s_dai_ops = {
+ .trigger = au1xpsc_i2s_trigger,
+ .hw_params = au1xpsc_i2s_hw_params,
+ .set_fmt = au1xpsc_i2s_set_fmt,
+};
+
+struct snd_soc_dai au1xpsc_i2s_dai = {
+ .name = "au1xpsc_i2s",
+ .probe = au1xpsc_i2s_probe,
+ .remove = au1xpsc_i2s_remove,
+ .playback = {
+ .rates = AU1XPSC_I2S_RATES,
+ .formats = AU1XPSC_I2S_FMTS,
+ .channels_min = 2,
+ .channels_max = 8, /* 2 without external help */
+ },
+ .capture = {
+ .rates = AU1XPSC_I2S_RATES,
+ .formats = AU1XPSC_I2S_FMTS,
+ .channels_min = 2,
+ .channels_max = 8, /* 2 without external help */
+ },
+ .ops = &au1xpsc_i2s_dai_ops,
+};
+EXPORT_SYMBOL(au1xpsc_i2s_dai);
+
+static int __init au1xpsc_i2s_drvprobe(struct platform_device *pdev)
+{
struct resource *r;
unsigned long sel;
int ret;
+ struct au1xpsc_audio_data *wd;
if (au1xpsc_i2s_workdata)
return -EBUSY;
- au1xpsc_i2s_workdata =
- kzalloc(sizeof(struct au1xpsc_audio_data), GFP_KERNEL);
- if (!au1xpsc_i2s_workdata)
+ wd = kzalloc(sizeof(struct au1xpsc_audio_data), GFP_KERNEL);
+ if (!wd)
return -ENOMEM;
r = platform_get_resource(pdev, IORESOURCE_MEM, 0);
@@ -284,131 +320,146 @@ static int au1xpsc_i2s_probe(struct platform_device *pdev,
}
ret = -EBUSY;
- au1xpsc_i2s_workdata->ioarea =
- request_mem_region(r->start, r->end - r->start + 1,
+ wd->ioarea = request_mem_region(r->start, r->end - r->start + 1,
"au1xpsc_i2s");
- if (!au1xpsc_i2s_workdata->ioarea)
+ if (!wd->ioarea)
goto out0;
- au1xpsc_i2s_workdata->mmio = ioremap(r->start, 0xffff);
- if (!au1xpsc_i2s_workdata->mmio)
+ wd->mmio = ioremap(r->start, 0xffff);
+ if (!wd->mmio)
goto out1;
/* preserve PSC clock source set up by platform (dev.platform_data
* is already occupied by soc layer)
*/
- sel = au_readl(PSC_SEL(au1xpsc_i2s_workdata)) & PSC_SEL_CLK_MASK;
- au_writel(PSC_CTRL_DISABLE, PSC_CTRL(au1xpsc_i2s_workdata));
+ sel = au_readl(PSC_SEL(wd)) & PSC_SEL_CLK_MASK;
+ au_writel(PSC_CTRL_DISABLE, PSC_CTRL(wd));
au_sync();
- au_writel(PSC_SEL_PS_I2SMODE | sel, PSC_SEL(au1xpsc_i2s_workdata));
- au_writel(0, I2S_CFG(au1xpsc_i2s_workdata));
+ au_writel(PSC_SEL_PS_I2SMODE | sel, PSC_SEL(wd));
+ au_writel(0, I2S_CFG(wd));
au_sync();
/* preconfigure: set max rx/tx fifo depths */
- au1xpsc_i2s_workdata->cfg |=
- PSC_I2SCFG_RT_FIFO8 | PSC_I2SCFG_TT_FIFO8;
+ wd->cfg |= PSC_I2SCFG_RT_FIFO8 | PSC_I2SCFG_TT_FIFO8;
/* don't wait for I2S core to become ready now; clocks may not
* be running yet; depending on clock input for PSC a wait might
* time out.
*/
- return 0;
+ ret = snd_soc_register_dai(&au1xpsc_i2s_dai);
+ if (ret)
+ goto out1;
+ /* finally add the DMA device for this PSC */
+ wd->dmapd = au1xpsc_pcm_add(pdev);
+ if (wd->dmapd) {
+ platform_set_drvdata(pdev, wd);
+ au1xpsc_i2s_workdata = wd;
+ return 0;
+ }
+
+ snd_soc_unregister_dai(&au1xpsc_i2s_dai);
out1:
- release_resource(au1xpsc_i2s_workdata->ioarea);
- kfree(au1xpsc_i2s_workdata->ioarea);
+ release_resource(wd->ioarea);
+ kfree(wd->ioarea);
out0:
- kfree(au1xpsc_i2s_workdata);
- au1xpsc_i2s_workdata = NULL;
+ kfree(wd);
return ret;
}
-static void au1xpsc_i2s_remove(struct platform_device *pdev,
- struct snd_soc_dai *dai)
+static int __devexit au1xpsc_i2s_drvremove(struct platform_device *pdev)
{
- au_writel(0, I2S_CFG(au1xpsc_i2s_workdata));
+ struct au1xpsc_audio_data *wd = platform_get_drvdata(pdev);
+
+ if (wd->dmapd)
+ au1xpsc_pcm_destroy(wd->dmapd);
+
+ snd_soc_unregister_dai(&au1xpsc_i2s_dai);
+
+ au_writel(0, I2S_CFG(wd));
au_sync();
- au_writel(PSC_CTRL_DISABLE, PSC_CTRL(au1xpsc_i2s_workdata));
+ au_writel(PSC_CTRL_DISABLE, PSC_CTRL(wd));
au_sync();
- iounmap(au1xpsc_i2s_workdata->mmio);
- release_resource(au1xpsc_i2s_workdata->ioarea);
- kfree(au1xpsc_i2s_workdata->ioarea);
- kfree(au1xpsc_i2s_workdata);
- au1xpsc_i2s_workdata = NULL;
+ iounmap(wd->mmio);
+ release_resource(wd->ioarea);
+ kfree(wd->ioarea);
+ kfree(wd);
+
+ au1xpsc_i2s_workdata = NULL; /* MDEV */
+
+ return 0;
}
-static int au1xpsc_i2s_suspend(struct snd_soc_dai *cpu_dai)
+#ifdef CONFIG_PM
+static int au1xpsc_i2s_drvsuspend(struct device *dev)
{
+ struct au1xpsc_audio_data *wd = dev_get_drvdata(dev);
+
/* save interesting register and disable PSC */
- au1xpsc_i2s_workdata->pm[0] =
- au_readl(PSC_SEL(au1xpsc_i2s_workdata));
+ wd->pm[0] = au_readl(PSC_SEL(wd));
- au_writel(0, I2S_CFG(au1xpsc_i2s_workdata));
+ au_writel(0, I2S_CFG(wd));
au_sync();
- au_writel(PSC_CTRL_DISABLE, PSC_CTRL(au1xpsc_i2s_workdata));
+ au_writel(PSC_CTRL_DISABLE, PSC_CTRL(wd));
au_sync();
return 0;
}
-static int au1xpsc_i2s_resume(struct snd_soc_dai *cpu_dai)
+static int au1xpsc_i2s_drvresume(struct device *dev)
{
+ struct au1xpsc_audio_data *wd = dev_get_drvdata(dev);
+
/* select I2S mode and PSC clock */
- au_writel(PSC_CTRL_DISABLE, PSC_CTRL(au1xpsc_i2s_workdata));
+ au_writel(PSC_CTRL_DISABLE, PSC_CTRL(wd));
au_sync();
- au_writel(0, PSC_SEL(au1xpsc_i2s_workdata));
+ au_writel(0, PSC_SEL(wd));
au_sync();
- au_writel(au1xpsc_i2s_workdata->pm[0],
- PSC_SEL(au1xpsc_i2s_workdata));
+ au_writel(wd->pm[0], PSC_SEL(wd));
au_sync();
return 0;
}
-static struct snd_soc_dai_ops au1xpsc_i2s_dai_ops = {
- .trigger = au1xpsc_i2s_trigger,
- .hw_params = au1xpsc_i2s_hw_params,
- .set_fmt = au1xpsc_i2s_set_fmt,
+static struct dev_pm_ops au1xpsci2s_pmops = {
+ .suspend = au1xpsc_i2s_drvsuspend,
+ .resume = au1xpsc_i2s_drvresume,
};
-struct snd_soc_dai au1xpsc_i2s_dai = {
- .name = "au1xpsc_i2s",
- .probe = au1xpsc_i2s_probe,
- .remove = au1xpsc_i2s_remove,
- .suspend = au1xpsc_i2s_suspend,
- .resume = au1xpsc_i2s_resume,
- .playback = {
- .rates = AU1XPSC_I2S_RATES,
- .formats = AU1XPSC_I2S_FMTS,
- .channels_min = 2,
- .channels_max = 8, /* 2 without external help */
- },
- .capture = {
- .rates = AU1XPSC_I2S_RATES,
- .formats = AU1XPSC_I2S_FMTS,
- .channels_min = 2,
- .channels_max = 8, /* 2 without external help */
+#define AU1XPSCI2S_PMOPS &au1xpsci2s_pmops
+
+#else
+
+#define AU1XPSCI2S_PMOPS NULL
+
+#endif
+
+static struct platform_driver au1xpsc_i2s_driver = {
+ .driver = {
+ .name = "au1xpsc_i2s",
+ .owner = THIS_MODULE,
+ .pm = AU1XPSCI2S_PMOPS,
},
- .ops = &au1xpsc_i2s_dai_ops,
+ .probe = au1xpsc_i2s_drvprobe,
+ .remove = __devexit_p(au1xpsc_i2s_drvremove),
};
-EXPORT_SYMBOL(au1xpsc_i2s_dai);
-static int __init au1xpsc_i2s_init(void)
+static int __init au1xpsc_i2s_load(void)
{
au1xpsc_i2s_workdata = NULL;
- return snd_soc_register_dai(&au1xpsc_i2s_dai);
+ return platform_driver_register(&au1xpsc_i2s_driver);
}
-static void __exit au1xpsc_i2s_exit(void)
+static void __exit au1xpsc_i2s_unload(void)
{
- snd_soc_unregister_dai(&au1xpsc_i2s_dai);
+ platform_driver_unregister(&au1xpsc_i2s_driver);
}
-module_init(au1xpsc_i2s_init);
-module_exit(au1xpsc_i2s_exit);
+module_init(au1xpsc_i2s_load);
+module_exit(au1xpsc_i2s_unload);
MODULE_LICENSE("GPL");
MODULE_DESCRIPTION("Au12x0/Au1550 PSC I2S ALSA ASoC audio driver");
-MODULE_AUTHOR("Manuel Lauss <mano@roarinelk.homelinux.net>");
+MODULE_AUTHOR("Manuel Lauss");
diff --git a/sound/soc/au1x/psc.h b/sound/soc/au1x/psc.h
index 3f474e8ed4f6..32d3807d3f5a 100644
--- a/sound/soc/au1x/psc.h
+++ b/sound/soc/au1x/psc.h
@@ -2,7 +2,7 @@
* Au12x0/Au1550 PSC ALSA ASoC audio support.
*
* (c) 2007-2008 MSC Vertriebsges.m.b.H.,
- * Manuel Lauss <mano@roarinelk.homelinux.net>
+ * Manuel Lauss <manuel.lauss@gmail.com>
*
* 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
@@ -21,6 +21,10 @@ extern struct snd_soc_dai au1xpsc_i2s_dai;
extern struct snd_soc_platform au1xpsc_soc_platform;
extern struct snd_ac97_bus_ops soc_ac97_ops;
+/* DBDMA helpers */
+extern struct platform_device *au1xpsc_pcm_add(struct platform_device *pdev);
+extern void au1xpsc_pcm_destroy(struct platform_device *dmapd);
+
struct au1xpsc_audio_data {
void __iomem *mmio;
@@ -30,6 +34,7 @@ struct au1xpsc_audio_data {
unsigned long pm[2];
struct resource *ioarea;
struct mutex lock;
+ struct platform_device *dmapd;
};
#define PCM_TX 0
diff --git a/sound/soc/blackfin/bf5xx-ad1836.c b/sound/soc/blackfin/bf5xx-ad1836.c
index cd361e304b0f..0f45a3f56be8 100644
--- a/sound/soc/blackfin/bf5xx-ad1836.c
+++ b/sound/soc/blackfin/bf5xx-ad1836.c
@@ -52,6 +52,7 @@ static int bf5xx_ad1836_hw_params(struct snd_pcm_substream *substream,
struct snd_soc_pcm_runtime *rtd = substream->private_data;
struct snd_soc_dai *cpu_dai = rtd->dai->cpu_dai;
struct snd_soc_dai *codec_dai = rtd->dai->codec_dai;
+ unsigned int channel_map[] = {0, 4, 1, 5, 2, 6, 3, 7};
int ret = 0;
/* set cpu DAI configuration */
ret = snd_soc_dai_set_fmt(cpu_dai, SND_SOC_DAIFMT_DSP_A |
@@ -65,6 +66,12 @@ static int bf5xx_ad1836_hw_params(struct snd_pcm_substream *substream,
if (ret < 0)
return ret;
+ /* set cpu DAI channel mapping */
+ ret = snd_soc_dai_set_channel_map(cpu_dai, ARRAY_SIZE(channel_map),
+ channel_map, ARRAY_SIZE(channel_map), channel_map);
+ if (ret < 0)
+ return ret;
+
return 0;
}
diff --git a/sound/soc/blackfin/bf5xx-ad1938.c b/sound/soc/blackfin/bf5xx-ad1938.c
index 08269e91810c..2ef1e5013b8c 100644
--- a/sound/soc/blackfin/bf5xx-ad1938.c
+++ b/sound/soc/blackfin/bf5xx-ad1938.c
@@ -61,6 +61,7 @@ static int bf5xx_ad1938_hw_params(struct snd_pcm_substream *substream,
struct snd_soc_pcm_runtime *rtd = substream->private_data;
struct snd_soc_dai *cpu_dai = rtd->dai->cpu_dai;
struct snd_soc_dai *codec_dai = rtd->dai->codec_dai;
+ unsigned int channel_map[] = {0, 1, 2, 3, 4, 5, 6, 7};
int ret = 0;
/* set cpu DAI configuration */
ret = snd_soc_dai_set_fmt(cpu_dai, SND_SOC_DAIFMT_DSP_A |
@@ -75,7 +76,13 @@ static int bf5xx_ad1938_hw_params(struct snd_pcm_substream *substream,
return ret;
/* set codec DAI slots, 8 channels, all channels are enabled */
- ret = snd_soc_dai_set_tdm_slot(codec_dai, 0xFF, 8);
+ ret = snd_soc_dai_set_tdm_slot(codec_dai, 0xFF, 0xFF, 8, 32);
+ if (ret < 0)
+ return ret;
+
+ /* set cpu DAI channel mapping */
+ ret = snd_soc_dai_set_channel_map(cpu_dai, ARRAY_SIZE(channel_map),
+ channel_map, ARRAY_SIZE(channel_map), channel_map);
if (ret < 0)
return ret;
diff --git a/sound/soc/blackfin/bf5xx-i2s.c b/sound/soc/blackfin/bf5xx-i2s.c
index 084b68884ada..3e6ada0dd1c4 100644
--- a/sound/soc/blackfin/bf5xx-i2s.c
+++ b/sound/soc/blackfin/bf5xx-i2s.c
@@ -49,7 +49,6 @@ struct bf5xx_i2s_port {
u16 rcr1;
u16 tcr2;
u16 rcr2;
- int counter;
int configured;
};
@@ -133,16 +132,6 @@ static int bf5xx_i2s_set_dai_fmt(struct snd_soc_dai *cpu_dai,
return ret;
}
-static int bf5xx_i2s_startup(struct snd_pcm_substream *substream,
- struct snd_soc_dai *dai)
-{
- pr_debug("%s enter\n", __func__);
-
- /*this counter is used for counting how many pcm streams are opened*/
- bf5xx_i2s.counter++;
- return 0;
-}
-
static int bf5xx_i2s_hw_params(struct snd_pcm_substream *substream,
struct snd_pcm_hw_params *params,
struct snd_soc_dai *dai)
@@ -201,9 +190,8 @@ static void bf5xx_i2s_shutdown(struct snd_pcm_substream *substream,
struct snd_soc_dai *dai)
{
pr_debug("%s enter\n", __func__);
- bf5xx_i2s.counter--;
/* No active stream, SPORT is allowed to be configured again. */
- if (!bf5xx_i2s.counter)
+ if (!dai->active)
bf5xx_i2s.configured = 0;
}
@@ -284,7 +272,6 @@ static int bf5xx_i2s_resume(struct snd_soc_dai *dai)
SNDRV_PCM_FMTBIT_S32_LE)
static struct snd_soc_dai_ops bf5xx_i2s_dai_ops = {
- .startup = bf5xx_i2s_startup,
.shutdown = bf5xx_i2s_shutdown,
.hw_params = bf5xx_i2s_hw_params,
.set_fmt = bf5xx_i2s_set_dai_fmt,
diff --git a/sound/soc/blackfin/bf5xx-tdm-pcm.c b/sound/soc/blackfin/bf5xx-tdm-pcm.c
index ccb5e823bd18..a8c73cbbd685 100644
--- a/sound/soc/blackfin/bf5xx-tdm-pcm.c
+++ b/sound/soc/blackfin/bf5xx-tdm-pcm.c
@@ -43,7 +43,7 @@
#include "bf5xx-tdm.h"
#include "bf5xx-sport.h"
-#define PCM_BUFFER_MAX 0x10000
+#define PCM_BUFFER_MAX 0x8000
#define FRAGMENT_SIZE_MIN (4*1024)
#define FRAGMENTS_MIN 2
#define FRAGMENTS_MAX 32
@@ -177,6 +177,9 @@ out:
static int bf5xx_pcm_copy(struct snd_pcm_substream *substream, int channel,
snd_pcm_uframes_t pos, void *buf, snd_pcm_uframes_t count)
{
+ struct snd_pcm_runtime *runtime = substream->runtime;
+ struct sport_device *sport = runtime->private_data;
+ struct bf5xx_tdm_port *tdm_port = sport->private_data;
unsigned int *src;
unsigned int *dst;
int i;
@@ -188,7 +191,7 @@ static int bf5xx_pcm_copy(struct snd_pcm_substream *substream, int channel,
dst += pos * 8;
while (count--) {
for (i = 0; i < substream->runtime->channels; i++)
- *(dst + i) = *src++;
+ *(dst + tdm_port->tx_map[i]) = *src++;
dst += 8;
}
} else {
@@ -198,7 +201,7 @@ static int bf5xx_pcm_copy(struct snd_pcm_substream *substream, int channel,
src += pos * 8;
while (count--) {
for (i = 0; i < substream->runtime->channels; i++)
- *dst++ = *(src+i);
+ *dst++ = *(src + tdm_port->rx_map[i]);
src += 8;
}
}
diff --git a/sound/soc/blackfin/bf5xx-tdm.c b/sound/soc/blackfin/bf5xx-tdm.c
index ff546e91a22e..4b360124083e 100644
--- a/sound/soc/blackfin/bf5xx-tdm.c
+++ b/sound/soc/blackfin/bf5xx-tdm.c
@@ -46,14 +46,6 @@
#include "bf5xx-sport.h"
#include "bf5xx-tdm.h"
-struct bf5xx_tdm_port {
- u16 tcr1;
- u16 rcr1;
- u16 tcr2;
- u16 rcr2;
- int configured;
-};
-
static struct bf5xx_tdm_port bf5xx_tdm;
static int sport_num = CONFIG_SND_BF5XX_SPORT_NUM;
@@ -181,6 +173,40 @@ static void bf5xx_tdm_shutdown(struct snd_pcm_substream *substream,
bf5xx_tdm.configured = 0;
}
+static int bf5xx_tdm_set_channel_map(struct snd_soc_dai *dai,
+ unsigned int tx_num, unsigned int *tx_slot,
+ unsigned int rx_num, unsigned int *rx_slot)
+{
+ int i;
+ unsigned int slot;
+ unsigned int tx_mapped = 0, rx_mapped = 0;
+
+ if ((tx_num > BFIN_TDM_DAI_MAX_SLOTS) ||
+ (rx_num > BFIN_TDM_DAI_MAX_SLOTS))
+ return -EINVAL;
+
+ for (i = 0; i < tx_num; i++) {
+ slot = tx_slot[i];
+ if ((slot < BFIN_TDM_DAI_MAX_SLOTS) &&
+ (!(tx_mapped & (1 << slot)))) {
+ bf5xx_tdm.tx_map[i] = slot;
+ tx_mapped |= 1 << slot;
+ } else
+ return -EINVAL;
+ }
+ for (i = 0; i < rx_num; i++) {
+ slot = rx_slot[i];
+ if ((slot < BFIN_TDM_DAI_MAX_SLOTS) &&
+ (!(rx_mapped & (1 << slot)))) {
+ bf5xx_tdm.rx_map[i] = slot;
+ rx_mapped |= 1 << slot;
+ } else
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
#ifdef CONFIG_PM
static int bf5xx_tdm_suspend(struct snd_soc_dai *dai)
{
@@ -235,6 +261,7 @@ static struct snd_soc_dai_ops bf5xx_tdm_dai_ops = {
.hw_params = bf5xx_tdm_hw_params,
.set_fmt = bf5xx_tdm_set_dai_fmt,
.shutdown = bf5xx_tdm_shutdown,
+ .set_channel_map = bf5xx_tdm_set_channel_map,
};
struct snd_soc_dai bf5xx_tdm_dai = {
@@ -300,6 +327,8 @@ static int __devinit bfin_tdm_probe(struct platform_device *pdev)
pr_err("Failed to register DAI: %d\n", ret);
goto sport_config_err;
}
+
+ sport_handle->private_data = &bf5xx_tdm;
return 0;
sport_config_err:
diff --git a/sound/soc/blackfin/bf5xx-tdm.h b/sound/soc/blackfin/bf5xx-tdm.h
index 618ec3d90cd4..04189a18c1ba 100644
--- a/sound/soc/blackfin/bf5xx-tdm.h
+++ b/sound/soc/blackfin/bf5xx-tdm.h
@@ -9,6 +9,17 @@
#ifndef _BF5XX_TDM_H
#define _BF5XX_TDM_H
+#define BFIN_TDM_DAI_MAX_SLOTS 8
+struct bf5xx_tdm_port {
+ u16 tcr1;
+ u16 rcr1;
+ u16 tcr2;
+ u16 rcr2;
+ unsigned int tx_map[BFIN_TDM_DAI_MAX_SLOTS];
+ unsigned int rx_map[BFIN_TDM_DAI_MAX_SLOTS];
+ int configured;
+};
+
extern struct snd_soc_dai bf5xx_tdm_dai;
#endif
diff --git a/sound/soc/codecs/Kconfig b/sound/soc/codecs/Kconfig
index 0edca93af3b0..52b005f8fed4 100644
--- a/sound/soc/codecs/Kconfig
+++ b/sound/soc/codecs/Kconfig
@@ -15,10 +15,12 @@ config SND_SOC_ALL_CODECS
select SND_SOC_AD1836 if SPI_MASTER
select SND_SOC_AD1938 if SPI_MASTER
select SND_SOC_AD1980 if SND_SOC_AC97_BUS
+ select SND_SOC_ADS117X
select SND_SOC_AD73311 if I2C
select SND_SOC_AK4104 if SPI_MASTER
select SND_SOC_AK4535 if I2C
select SND_SOC_AK4642 if I2C
+ select SND_SOC_AK4671 if I2C
select SND_SOC_CS4270 if I2C
select SND_SOC_MAX9877 if I2C
select SND_SOC_PCM3008
@@ -28,6 +30,8 @@ config SND_SOC_ALL_CODECS
select SND_SOC_TLV320AIC23 if I2C
select SND_SOC_TLV320AIC26 if SPI_MASTER
select SND_SOC_TLV320AIC3X if I2C
+ select SND_SOC_TPA6130A2 if I2C
+ select SND_SOC_TLV320DAC33 if I2C
select SND_SOC_TWL4030 if TWL4030_CORE
select SND_SOC_UDA134X
select SND_SOC_UDA1380 if I2C
@@ -36,6 +40,8 @@ config SND_SOC_ALL_CODECS
select SND_SOC_WM8510 if SND_SOC_I2C_AND_SPI
select SND_SOC_WM8523 if I2C
select SND_SOC_WM8580 if I2C
+ select SND_SOC_WM8711 if SND_SOC_I2C_AND_SPI
+ select SND_SOC_WM8727
select SND_SOC_WM8728 if SND_SOC_I2C_AND_SPI
select SND_SOC_WM8731 if SND_SOC_I2C_AND_SPI
select SND_SOC_WM8750 if SND_SOC_I2C_AND_SPI
@@ -86,6 +92,9 @@ config SND_SOC_AD1980
config SND_SOC_AD73311
tristate
+
+config SND_SOC_ADS117X
+ tristate
config SND_SOC_AK4104
tristate
@@ -96,6 +105,9 @@ config SND_SOC_AK4535
config SND_SOC_AK4642
tristate
+config SND_SOC_AK4671
+ tristate
+
# Cirrus Logic CS4270 Codec
config SND_SOC_CS4270
tristate
@@ -136,7 +148,11 @@ config SND_SOC_TLV320AIC26
config SND_SOC_TLV320AIC3X
tristate
+config SND_SOC_TLV320DAC33
+ tristate
+
config SND_SOC_TWL4030
+ select TWL4030_CODEC
tristate
config SND_SOC_UDA134X
@@ -160,6 +176,12 @@ config SND_SOC_WM8523
config SND_SOC_WM8580
tristate
+config SND_SOC_WM8711
+ tristate
+
+config SND_SOC_WM8727
+ tristate
+
config SND_SOC_WM8728
tristate
@@ -220,3 +242,6 @@ config SND_SOC_WM9713
# Amp
config SND_SOC_MAX9877
tristate
+
+config SND_SOC_TPA6130A2
+ tristate
diff --git a/sound/soc/codecs/Makefile b/sound/soc/codecs/Makefile
index fb4af28486ba..dbaecb133ac7 100644
--- a/sound/soc/codecs/Makefile
+++ b/sound/soc/codecs/Makefile
@@ -3,9 +3,11 @@ snd-soc-ad1836-objs := ad1836.o
snd-soc-ad1938-objs := ad1938.o
snd-soc-ad1980-objs := ad1980.o
snd-soc-ad73311-objs := ad73311.o
+snd-soc-ads117x-objs := ads117x.o
snd-soc-ak4104-objs := ak4104.o
snd-soc-ak4535-objs := ak4535.o
snd-soc-ak4642-objs := ak4642.o
+snd-soc-ak4671-objs := ak4671.o
snd-soc-cs4270-objs := cs4270.o
snd-soc-cx20442-objs := cx20442.o
snd-soc-l3-objs := l3.o
@@ -16,6 +18,7 @@ snd-soc-stac9766-objs := stac9766.o
snd-soc-tlv320aic23-objs := tlv320aic23.o
snd-soc-tlv320aic26-objs := tlv320aic26.o
snd-soc-tlv320aic3x-objs := tlv320aic3x.o
+snd-soc-tlv320dac33-objs := tlv320dac33.o
snd-soc-twl4030-objs := twl4030.o
snd-soc-uda134x-objs := uda134x.o
snd-soc-uda1380-objs := uda1380.o
@@ -24,6 +27,8 @@ snd-soc-wm8400-objs := wm8400.o
snd-soc-wm8510-objs := wm8510.o
snd-soc-wm8523-objs := wm8523.o
snd-soc-wm8580-objs := wm8580.o
+snd-soc-wm8711-objs := wm8711.o
+snd-soc-wm8727-objs := wm8727.o
snd-soc-wm8728-objs := wm8728.o
snd-soc-wm8731-objs := wm8731.o
snd-soc-wm8750-objs := wm8750.o
@@ -47,15 +52,18 @@ snd-soc-wm-hubs-objs := wm_hubs.o
# Amp
snd-soc-max9877-objs := max9877.o
+snd-soc-tpa6130a2-objs := tpa6130a2.o
obj-$(CONFIG_SND_SOC_AC97_CODEC) += snd-soc-ac97.o
obj-$(CONFIG_SND_SOC_AD1836) += snd-soc-ad1836.o
obj-$(CONFIG_SND_SOC_AD1938) += snd-soc-ad1938.o
obj-$(CONFIG_SND_SOC_AD1980) += snd-soc-ad1980.o
obj-$(CONFIG_SND_SOC_AD73311) += snd-soc-ad73311.o
+obj-$(CONFIG_SND_SOC_ADS117X) += snd-soc-ads117x.o
obj-$(CONFIG_SND_SOC_AK4104) += snd-soc-ak4104.o
obj-$(CONFIG_SND_SOC_AK4535) += snd-soc-ak4535.o
obj-$(CONFIG_SND_SOC_AK4642) += snd-soc-ak4642.o
+obj-$(CONFIG_SND_SOC_AK4671) += snd-soc-ak4671.o
obj-$(CONFIG_SND_SOC_CS4270) += snd-soc-cs4270.o
obj-$(CONFIG_SND_SOC_CX20442) += snd-soc-cx20442.o
obj-$(CONFIG_SND_SOC_L3) += snd-soc-l3.o
@@ -66,6 +74,7 @@ obj-$(CONFIG_SND_SOC_STAC9766) += snd-soc-stac9766.o
obj-$(CONFIG_SND_SOC_TLV320AIC23) += snd-soc-tlv320aic23.o
obj-$(CONFIG_SND_SOC_TLV320AIC26) += snd-soc-tlv320aic26.o
obj-$(CONFIG_SND_SOC_TLV320AIC3X) += snd-soc-tlv320aic3x.o
+obj-$(CONFIG_SND_SOC_TLV320DAC33) += snd-soc-tlv320dac33.o
obj-$(CONFIG_SND_SOC_TWL4030) += snd-soc-twl4030.o
obj-$(CONFIG_SND_SOC_UDA134X) += snd-soc-uda134x.o
obj-$(CONFIG_SND_SOC_UDA1380) += snd-soc-uda1380.o
@@ -74,6 +83,8 @@ obj-$(CONFIG_SND_SOC_WM8400) += snd-soc-wm8400.o
obj-$(CONFIG_SND_SOC_WM8510) += snd-soc-wm8510.o
obj-$(CONFIG_SND_SOC_WM8523) += snd-soc-wm8523.o
obj-$(CONFIG_SND_SOC_WM8580) += snd-soc-wm8580.o
+obj-$(CONFIG_SND_SOC_WM8711) += snd-soc-wm8711.o
+obj-$(CONFIG_SND_SOC_WM8727) += snd-soc-wm8727.o
obj-$(CONFIG_SND_SOC_WM8728) += snd-soc-wm8728.o
obj-$(CONFIG_SND_SOC_WM8731) += snd-soc-wm8731.o
obj-$(CONFIG_SND_SOC_WM8750) += snd-soc-wm8750.o
@@ -97,3 +108,4 @@ obj-$(CONFIG_SND_SOC_WM_HUBS) += snd-soc-wm-hubs.o
# Amp
obj-$(CONFIG_SND_SOC_MAX9877) += snd-soc-max9877.o
+obj-$(CONFIG_SND_SOC_TPA6130A2) += snd-soc-tpa6130a2.o
diff --git a/sound/soc/codecs/ac97.c b/sound/soc/codecs/ac97.c
index 932299bb5d1e..69bd0acc81c8 100644
--- a/sound/soc/codecs/ac97.c
+++ b/sound/soc/codecs/ac97.c
@@ -117,9 +117,6 @@ static int ac97_soc_probe(struct platform_device *pdev)
if (ret < 0)
goto bus_err;
- ret = snd_soc_init_card(socdev);
- if (ret < 0)
- goto bus_err;
return 0;
bus_err:
diff --git a/sound/soc/codecs/ad1836.c b/sound/soc/codecs/ad1836.c
index c48485f2c55d..2c18e3d1b71e 100644
--- a/sound/soc/codecs/ad1836.c
+++ b/sound/soc/codecs/ad1836.c
@@ -385,19 +385,7 @@ static int ad1836_probe(struct platform_device *pdev)
snd_soc_dapm_new_controls(codec, ad1836_dapm_widgets,
ARRAY_SIZE(ad1836_dapm_widgets));
snd_soc_dapm_add_routes(codec, audio_paths, ARRAY_SIZE(audio_paths));
- snd_soc_dapm_new_widgets(codec);
- ret = snd_soc_init_card(socdev);
- if (ret < 0) {
- dev_err(codec->dev, "failed to register card: %d\n", ret);
- goto card_err;
- }
-
- return ret;
-
-card_err:
- snd_soc_free_pcms(socdev);
- snd_soc_dapm_free(socdev);
pcm_err:
return ret;
}
diff --git a/sound/soc/codecs/ad1938.c b/sound/soc/codecs/ad1938.c
index 34b30efc3cb0..5d489186c05b 100644
--- a/sound/soc/codecs/ad1938.c
+++ b/sound/soc/codecs/ad1938.c
@@ -592,21 +592,9 @@ static int ad1938_probe(struct platform_device *pdev)
snd_soc_dapm_new_controls(codec, ad1938_dapm_widgets,
ARRAY_SIZE(ad1938_dapm_widgets));
snd_soc_dapm_add_routes(codec, audio_paths, ARRAY_SIZE(audio_paths));
- snd_soc_dapm_new_widgets(codec);
ad1938_set_bias_level(codec, SND_SOC_BIAS_STANDBY);
- ret = snd_soc_init_card(socdev);
- if (ret < 0) {
- dev_err(codec->dev, "failed to register card: %d\n", ret);
- goto card_err;
- }
-
- return ret;
-
-card_err:
- snd_soc_free_pcms(socdev);
- snd_soc_dapm_free(socdev);
pcm_err:
return ret;
}
diff --git a/sound/soc/codecs/ad1980.c b/sound/soc/codecs/ad1980.c
index d7440a982d22..39c0f7584e65 100644
--- a/sound/soc/codecs/ad1980.c
+++ b/sound/soc/codecs/ad1980.c
@@ -257,11 +257,6 @@ static int ad1980_soc_probe(struct platform_device *pdev)
snd_soc_add_controls(codec, ad1980_snd_ac97_controls,
ARRAY_SIZE(ad1980_snd_ac97_controls));
- ret = snd_soc_init_card(socdev);
- if (ret < 0) {
- printk(KERN_ERR "ad1980: failed to register card\n");
- goto reset_err;
- }
return 0;
diff --git a/sound/soc/codecs/ad73311.c b/sound/soc/codecs/ad73311.c
index e61dac5e7b8f..d2fcc601722c 100644
--- a/sound/soc/codecs/ad73311.c
+++ b/sound/soc/codecs/ad73311.c
@@ -64,16 +64,8 @@ static int ad73311_soc_probe(struct platform_device *pdev)
goto pcm_err;
}
- ret = snd_soc_init_card(socdev);
- if (ret < 0) {
- printk(KERN_ERR "ad73311: failed to register card\n");
- goto register_err;
- }
-
return ret;
-register_err:
- snd_soc_free_pcms(socdev);
pcm_err:
kfree(socdev->card->codec);
socdev->card->codec = NULL;
diff --git a/sound/soc/codecs/ads117x.c b/sound/soc/codecs/ads117x.c
new file mode 100644
index 000000000000..cc96411ca3e6
--- /dev/null
+++ b/sound/soc/codecs/ads117x.c
@@ -0,0 +1,123 @@
+/*
+ * ads117x.c -- Driver for ads1174/8 ADC chips
+ *
+ * Copyright 2009 ShotSpotter Inc.
+ * Author: Graeme Gregory <gg@slimlogic.co.uk>
+ *
+ * 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/kernel.h>
+#include <linux/init.h>
+#include <linux/device.h>
+#include <sound/core.h>
+#include <sound/pcm.h>
+#include <sound/initval.h>
+#include <sound/soc.h>
+
+#include "ads117x.h"
+
+#define ADS117X_RATES (SNDRV_PCM_RATE_8000_48000)
+
+#define ADS117X_FORMATS (SNDRV_PCM_FMTBIT_S16_LE)
+
+struct snd_soc_dai ads117x_dai = {
+/* ADC */
+ .name = "ADS117X ADC",
+ .id = 1,
+ .capture = {
+ .stream_name = "Capture",
+ .channels_min = 1,
+ .channels_max = 32,
+ .rates = ADS117X_RATES,
+ .formats = ADS117X_FORMATS,},
+};
+EXPORT_SYMBOL_GPL(ads117x_dai);
+
+static int ads117x_probe(struct platform_device *pdev)
+{
+ struct snd_soc_device *socdev = platform_get_drvdata(pdev);
+ struct snd_soc_codec *codec;
+ int ret;
+
+ codec = kzalloc(sizeof(struct snd_soc_codec), GFP_KERNEL);
+ if (codec == NULL)
+ return -ENOMEM;
+
+ socdev->card->codec = codec;
+ mutex_init(&codec->mutex);
+ INIT_LIST_HEAD(&codec->dapm_widgets);
+ INIT_LIST_HEAD(&codec->dapm_paths);
+ codec->name = "ADS117X";
+ codec->owner = THIS_MODULE;
+ codec->dai = &ads117x_dai;
+ codec->num_dai = 1;
+
+ /* register pcms */
+ ret = snd_soc_new_pcms(socdev, SNDRV_DEFAULT_IDX1, SNDRV_DEFAULT_STR1);
+ if (ret < 0) {
+ printk(KERN_ERR "ads117x: failed to create pcms\n");
+ kfree(codec);
+ return ret;
+ }
+
+ return 0;
+}
+
+static int ads117x_remove(struct platform_device *pdev)
+{
+ struct snd_soc_device *socdev = platform_get_drvdata(pdev);
+ struct snd_soc_codec *codec = socdev->card->codec;
+
+ snd_soc_free_pcms(socdev);
+ kfree(codec);
+
+ return 0;
+}
+
+struct snd_soc_codec_device soc_codec_dev_ads117x = {
+ .probe = ads117x_probe,
+ .remove = ads117x_remove,
+};
+EXPORT_SYMBOL_GPL(soc_codec_dev_ads117x);
+
+static __devinit int ads117x_platform_probe(struct platform_device *pdev)
+{
+ ads117x_dai.dev = &pdev->dev;
+ return snd_soc_register_dai(&ads117x_dai);
+}
+
+static int __devexit ads117x_platform_remove(struct platform_device *pdev)
+{
+ snd_soc_unregister_dai(&ads117x_dai);
+ return 0;
+}
+
+static struct platform_driver ads117x_codec_driver = {
+ .driver = {
+ .name = "ads117x",
+ .owner = THIS_MODULE,
+ },
+
+ .probe = ads117x_platform_probe,
+ .remove = __devexit_p(ads117x_platform_remove),
+};
+
+static int __init ads117x_init(void)
+{
+ return platform_driver_register(&ads117x_codec_driver);
+}
+module_init(ads117x_init);
+
+static void __exit ads117x_exit(void)
+{
+ platform_driver_unregister(&ads117x_codec_driver);
+}
+module_exit(ads117x_exit);
+
+MODULE_DESCRIPTION("ASoC ads117x driver");
+MODULE_AUTHOR("Graeme Gregory");
+MODULE_LICENSE("GPL");
diff --git a/sound/soc/codecs/ads117x.h b/sound/soc/codecs/ads117x.h
new file mode 100644
index 000000000000..dbcf50ec9bd1
--- /dev/null
+++ b/sound/soc/codecs/ads117x.h
@@ -0,0 +1,13 @@
+/*
+ * ads117x.h -- Driver for ads1174/8 ADC chips
+ *
+ * Copyright 2009 ShotSpotter Inc.
+ * Author: Graeme Gregory <gg@slimlogic.co.uk>
+ *
+ * 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.
+ */
+extern struct snd_soc_dai ads117x_dai;
+extern struct snd_soc_codec_device soc_codec_dev_ads117x;
diff --git a/sound/soc/codecs/ak4104.c b/sound/soc/codecs/ak4104.c
index 4d47bc4f7428..3a14c6fc4f5e 100644
--- a/sound/soc/codecs/ak4104.c
+++ b/sound/soc/codecs/ak4104.c
@@ -313,14 +313,6 @@ static int ak4104_probe(struct platform_device *pdev)
return ret;
}
- /* Register the socdev */
- ret = snd_soc_init_card(socdev);
- if (ret < 0) {
- dev_err(codec->dev, "failed to register card\n");
- snd_soc_free_pcms(socdev);
- return ret;
- }
-
return 0;
}
diff --git a/sound/soc/codecs/ak4535.c b/sound/soc/codecs/ak4535.c
index 0abec0d29a96..ff966567e2ba 100644
--- a/sound/soc/codecs/ak4535.c
+++ b/sound/soc/codecs/ak4535.c
@@ -294,7 +294,6 @@ static int ak4535_add_widgets(struct snd_soc_codec *codec)
snd_soc_dapm_add_routes(codec, audio_map, ARRAY_SIZE(audio_map));
- snd_soc_dapm_new_widgets(codec);
return 0;
}
@@ -485,17 +484,9 @@ static int ak4535_init(struct snd_soc_device *socdev)
snd_soc_add_controls(codec, ak4535_snd_controls,
ARRAY_SIZE(ak4535_snd_controls));
ak4535_add_widgets(codec);
- ret = snd_soc_init_card(socdev);
- if (ret < 0) {
- printk(KERN_ERR "ak4535: 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);
diff --git a/sound/soc/codecs/ak4642.c b/sound/soc/codecs/ak4642.c
index e057c7b578df..b69861d52161 100644
--- a/sound/soc/codecs/ak4642.c
+++ b/sound/soc/codecs/ak4642.c
@@ -442,18 +442,9 @@ static int ak4642_probe(struct platform_device *pdev)
goto pcm_err;
}
- ret = snd_soc_init_card(socdev);
- if (ret < 0) {
- printk(KERN_ERR "ak4642: failed to register card\n");
- goto card_err;
- }
-
dev_info(&pdev->dev, "AK4642 Audio Codec %s", AK4642_VERSION);
return ret;
-card_err:
- snd_soc_free_pcms(socdev);
- snd_soc_dapm_free(socdev);
pcm_err:
return ret;
diff --git a/sound/soc/codecs/ak4671.c b/sound/soc/codecs/ak4671.c
new file mode 100644
index 000000000000..82fca284d007
--- /dev/null
+++ b/sound/soc/codecs/ak4671.c
@@ -0,0 +1,815 @@
+/*
+ * ak4671.c -- audio driver for AK4671
+ *
+ * Copyright (C) 2009 Samsung Electronics Co.Ltd
+ * Author: Joonyoung Shim <jy0922.shim@samsung.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/init.h>
+#include <linux/i2c.h>
+#include <linux/delay.h>
+#include <sound/soc.h>
+#include <sound/soc-dapm.h>
+#include <sound/initval.h>
+#include <sound/tlv.h>
+
+#include "ak4671.h"
+
+static struct snd_soc_codec *ak4671_codec;
+
+/* codec private data */
+struct ak4671_priv {
+ struct snd_soc_codec codec;
+ u8 reg_cache[AK4671_CACHEREGNUM];
+};
+
+/* ak4671 register cache & default register settings */
+static const u8 ak4671_reg[AK4671_CACHEREGNUM] = {
+ 0x00, /* AK4671_AD_DA_POWER_MANAGEMENT (0x00) */
+ 0xf6, /* AK4671_PLL_MODE_SELECT0 (0x01) */
+ 0x00, /* AK4671_PLL_MODE_SELECT1 (0x02) */
+ 0x02, /* AK4671_FORMAT_SELECT (0x03) */
+ 0x00, /* AK4671_MIC_SIGNAL_SELECT (0x04) */
+ 0x55, /* AK4671_MIC_AMP_GAIN (0x05) */
+ 0x00, /* AK4671_MIXING_POWER_MANAGEMENT0 (0x06) */
+ 0x00, /* AK4671_MIXING_POWER_MANAGEMENT1 (0x07) */
+ 0xb5, /* AK4671_OUTPUT_VOLUME_CONTROL (0x08) */
+ 0x00, /* AK4671_LOUT1_SIGNAL_SELECT (0x09) */
+ 0x00, /* AK4671_ROUT1_SIGNAL_SELECT (0x0a) */
+ 0x00, /* AK4671_LOUT2_SIGNAL_SELECT (0x0b) */
+ 0x00, /* AK4671_ROUT2_SIGNAL_SELECT (0x0c) */
+ 0x00, /* AK4671_LOUT3_SIGNAL_SELECT (0x0d) */
+ 0x00, /* AK4671_ROUT3_SIGNAL_SELECT (0x0e) */
+ 0x00, /* AK4671_LOUT1_POWER_MANAGERMENT (0x0f) */
+ 0x00, /* AK4671_LOUT2_POWER_MANAGERMENT (0x10) */
+ 0x80, /* AK4671_LOUT3_POWER_MANAGERMENT (0x11) */
+ 0x91, /* AK4671_LCH_INPUT_VOLUME_CONTROL (0x12) */
+ 0x91, /* AK4671_RCH_INPUT_VOLUME_CONTROL (0x13) */
+ 0xe1, /* AK4671_ALC_REFERENCE_SELECT (0x14) */
+ 0x00, /* AK4671_DIGITAL_MIXING_CONTROL (0x15) */
+ 0x00, /* AK4671_ALC_TIMER_SELECT (0x16) */
+ 0x00, /* AK4671_ALC_MODE_CONTROL (0x17) */
+ 0x02, /* AK4671_MODE_CONTROL1 (0x18) */
+ 0x01, /* AK4671_MODE_CONTROL2 (0x19) */
+ 0x18, /* AK4671_LCH_OUTPUT_VOLUME_CONTROL (0x1a) */
+ 0x18, /* AK4671_RCH_OUTPUT_VOLUME_CONTROL (0x1b) */
+ 0x00, /* AK4671_SIDETONE_A_CONTROL (0x1c) */
+ 0x02, /* AK4671_DIGITAL_FILTER_SELECT (0x1d) */
+ 0x00, /* AK4671_FIL3_COEFFICIENT0 (0x1e) */
+ 0x00, /* AK4671_FIL3_COEFFICIENT1 (0x1f) */
+ 0x00, /* AK4671_FIL3_COEFFICIENT2 (0x20) */
+ 0x00, /* AK4671_FIL3_COEFFICIENT3 (0x21) */
+ 0x00, /* AK4671_EQ_COEFFICIENT0 (0x22) */
+ 0x00, /* AK4671_EQ_COEFFICIENT1 (0x23) */
+ 0x00, /* AK4671_EQ_COEFFICIENT2 (0x24) */
+ 0x00, /* AK4671_EQ_COEFFICIENT3 (0x25) */
+ 0x00, /* AK4671_EQ_COEFFICIENT4 (0x26) */
+ 0x00, /* AK4671_EQ_COEFFICIENT5 (0x27) */
+ 0xa9, /* AK4671_FIL1_COEFFICIENT0 (0x28) */
+ 0x1f, /* AK4671_FIL1_COEFFICIENT1 (0x29) */
+ 0xad, /* AK4671_FIL1_COEFFICIENT2 (0x2a) */
+ 0x20, /* AK4671_FIL1_COEFFICIENT3 (0x2b) */
+ 0x00, /* AK4671_FIL2_COEFFICIENT0 (0x2c) */
+ 0x00, /* AK4671_FIL2_COEFFICIENT1 (0x2d) */
+ 0x00, /* AK4671_FIL2_COEFFICIENT2 (0x2e) */
+ 0x00, /* AK4671_FIL2_COEFFICIENT3 (0x2f) */
+ 0x00, /* AK4671_DIGITAL_FILTER_SELECT2 (0x30) */
+ 0x00, /* this register not used */
+ 0x00, /* AK4671_E1_COEFFICIENT0 (0x32) */
+ 0x00, /* AK4671_E1_COEFFICIENT1 (0x33) */
+ 0x00, /* AK4671_E1_COEFFICIENT2 (0x34) */
+ 0x00, /* AK4671_E1_COEFFICIENT3 (0x35) */
+ 0x00, /* AK4671_E1_COEFFICIENT4 (0x36) */
+ 0x00, /* AK4671_E1_COEFFICIENT5 (0x37) */
+ 0x00, /* AK4671_E2_COEFFICIENT0 (0x38) */
+ 0x00, /* AK4671_E2_COEFFICIENT1 (0x39) */
+ 0x00, /* AK4671_E2_COEFFICIENT2 (0x3a) */
+ 0x00, /* AK4671_E2_COEFFICIENT3 (0x3b) */
+ 0x00, /* AK4671_E2_COEFFICIENT4 (0x3c) */
+ 0x00, /* AK4671_E2_COEFFICIENT5 (0x3d) */
+ 0x00, /* AK4671_E3_COEFFICIENT0 (0x3e) */
+ 0x00, /* AK4671_E3_COEFFICIENT1 (0x3f) */
+ 0x00, /* AK4671_E3_COEFFICIENT2 (0x40) */
+ 0x00, /* AK4671_E3_COEFFICIENT3 (0x41) */
+ 0x00, /* AK4671_E3_COEFFICIENT4 (0x42) */
+ 0x00, /* AK4671_E3_COEFFICIENT5 (0x43) */
+ 0x00, /* AK4671_E4_COEFFICIENT0 (0x44) */
+ 0x00, /* AK4671_E4_COEFFICIENT1 (0x45) */
+ 0x00, /* AK4671_E4_COEFFICIENT2 (0x46) */
+ 0x00, /* AK4671_E4_COEFFICIENT3 (0x47) */
+ 0x00, /* AK4671_E4_COEFFICIENT4 (0x48) */
+ 0x00, /* AK4671_E4_COEFFICIENT5 (0x49) */
+ 0x00, /* AK4671_E5_COEFFICIENT0 (0x4a) */
+ 0x00, /* AK4671_E5_COEFFICIENT1 (0x4b) */
+ 0x00, /* AK4671_E5_COEFFICIENT2 (0x4c) */
+ 0x00, /* AK4671_E5_COEFFICIENT3 (0x4d) */
+ 0x00, /* AK4671_E5_COEFFICIENT4 (0x4e) */
+ 0x00, /* AK4671_E5_COEFFICIENT5 (0x4f) */
+ 0x88, /* AK4671_EQ_CONTROL_250HZ_100HZ (0x50) */
+ 0x88, /* AK4671_EQ_CONTROL_3500HZ_1KHZ (0x51) */
+ 0x08, /* AK4671_EQ_CONTRO_10KHZ (0x52) */
+ 0x00, /* AK4671_PCM_IF_CONTROL0 (0x53) */
+ 0x00, /* AK4671_PCM_IF_CONTROL1 (0x54) */
+ 0x00, /* AK4671_PCM_IF_CONTROL2 (0x55) */
+ 0x18, /* AK4671_DIGITAL_VOLUME_B_CONTROL (0x56) */
+ 0x18, /* AK4671_DIGITAL_VOLUME_C_CONTROL (0x57) */
+ 0x00, /* AK4671_SIDETONE_VOLUME_CONTROL (0x58) */
+ 0x00, /* AK4671_DIGITAL_MIXING_CONTROL2 (0x59) */
+ 0x00, /* AK4671_SAR_ADC_CONTROL (0x5a) */
+};
+
+/*
+ * LOUT1/ROUT1 output volume control:
+ * from -24 to 6 dB in 6 dB steps (mute instead of -30 dB)
+ */
+static DECLARE_TLV_DB_SCALE(out1_tlv, -3000, 600, 1);
+
+/*
+ * LOUT2/ROUT2 output volume control:
+ * from -33 to 6 dB in 3 dB steps (mute instead of -33 dB)
+ */
+static DECLARE_TLV_DB_SCALE(out2_tlv, -3300, 300, 1);
+
+/*
+ * LOUT3/ROUT3 output volume control:
+ * from -6 to 3 dB in 3 dB steps
+ */
+static DECLARE_TLV_DB_SCALE(out3_tlv, -600, 300, 0);
+
+/*
+ * Mic amp gain control:
+ * from -15 to 30 dB in 3 dB steps
+ * REVISIT: The actual min value(0x01) is -12 dB and the reg value 0x00 is not
+ * available
+ */
+static DECLARE_TLV_DB_SCALE(mic_amp_tlv, -1500, 300, 0);
+
+static const struct snd_kcontrol_new ak4671_snd_controls[] = {
+ /* Common playback gain controls */
+ SOC_SINGLE_TLV("Line Output1 Playback Volume",
+ AK4671_OUTPUT_VOLUME_CONTROL, 0, 0x6, 0, out1_tlv),
+ SOC_SINGLE_TLV("Headphone Output2 Playback Volume",
+ AK4671_OUTPUT_VOLUME_CONTROL, 4, 0xd, 0, out2_tlv),
+ SOC_SINGLE_TLV("Line Output3 Playback Volume",
+ AK4671_LOUT3_POWER_MANAGERMENT, 6, 0x3, 0, out3_tlv),
+
+ /* Common capture gain controls */
+ SOC_DOUBLE_TLV("Mic Amp Capture Volume",
+ AK4671_MIC_AMP_GAIN, 0, 4, 0xf, 0, mic_amp_tlv),
+};
+
+/* event handlers */
+static int ak4671_out2_event(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol, int event)
+{
+ struct snd_soc_codec *codec = w->codec;
+ u8 reg;
+
+ switch (event) {
+ case SND_SOC_DAPM_POST_PMU:
+ reg = snd_soc_read(codec, AK4671_LOUT2_POWER_MANAGERMENT);
+ reg |= AK4671_MUTEN;
+ snd_soc_write(codec, AK4671_LOUT2_POWER_MANAGERMENT, reg);
+ break;
+ case SND_SOC_DAPM_PRE_PMD:
+ reg = snd_soc_read(codec, AK4671_LOUT2_POWER_MANAGERMENT);
+ reg &= ~AK4671_MUTEN;
+ snd_soc_write(codec, AK4671_LOUT2_POWER_MANAGERMENT, reg);
+ break;
+ }
+
+ return 0;
+}
+
+/* Output Mixers */
+static const struct snd_kcontrol_new ak4671_lout1_mixer_controls[] = {
+ SOC_DAPM_SINGLE("DACL", AK4671_LOUT1_SIGNAL_SELECT, 0, 1, 0),
+ SOC_DAPM_SINGLE("LINL1", AK4671_LOUT1_SIGNAL_SELECT, 1, 1, 0),
+ SOC_DAPM_SINGLE("LINL2", AK4671_LOUT1_SIGNAL_SELECT, 2, 1, 0),
+ SOC_DAPM_SINGLE("LINL3", AK4671_LOUT1_SIGNAL_SELECT, 3, 1, 0),
+ SOC_DAPM_SINGLE("LINL4", AK4671_LOUT1_SIGNAL_SELECT, 4, 1, 0),
+ SOC_DAPM_SINGLE("LOOPL", AK4671_LOUT1_SIGNAL_SELECT, 5, 1, 0),
+};
+
+static const struct snd_kcontrol_new ak4671_rout1_mixer_controls[] = {
+ SOC_DAPM_SINGLE("DACR", AK4671_ROUT1_SIGNAL_SELECT, 0, 1, 0),
+ SOC_DAPM_SINGLE("RINR1", AK4671_ROUT1_SIGNAL_SELECT, 1, 1, 0),
+ SOC_DAPM_SINGLE("RINR2", AK4671_ROUT1_SIGNAL_SELECT, 2, 1, 0),
+ SOC_DAPM_SINGLE("RINR3", AK4671_ROUT1_SIGNAL_SELECT, 3, 1, 0),
+ SOC_DAPM_SINGLE("RINR4", AK4671_ROUT1_SIGNAL_SELECT, 4, 1, 0),
+ SOC_DAPM_SINGLE("LOOPR", AK4671_ROUT1_SIGNAL_SELECT, 5, 1, 0),
+};
+
+static const struct snd_kcontrol_new ak4671_lout2_mixer_controls[] = {
+ SOC_DAPM_SINGLE("DACHL", AK4671_LOUT2_SIGNAL_SELECT, 0, 1, 0),
+ SOC_DAPM_SINGLE("LINH1", AK4671_LOUT2_SIGNAL_SELECT, 1, 1, 0),
+ SOC_DAPM_SINGLE("LINH2", AK4671_LOUT2_SIGNAL_SELECT, 2, 1, 0),
+ SOC_DAPM_SINGLE("LINH3", AK4671_LOUT2_SIGNAL_SELECT, 3, 1, 0),
+ SOC_DAPM_SINGLE("LINH4", AK4671_LOUT2_SIGNAL_SELECT, 4, 1, 0),
+ SOC_DAPM_SINGLE("LOOPHL", AK4671_LOUT2_SIGNAL_SELECT, 5, 1, 0),
+};
+
+static const struct snd_kcontrol_new ak4671_rout2_mixer_controls[] = {
+ SOC_DAPM_SINGLE("DACHR", AK4671_ROUT2_SIGNAL_SELECT, 0, 1, 0),
+ SOC_DAPM_SINGLE("RINH1", AK4671_ROUT2_SIGNAL_SELECT, 1, 1, 0),
+ SOC_DAPM_SINGLE("RINH2", AK4671_ROUT2_SIGNAL_SELECT, 2, 1, 0),
+ SOC_DAPM_SINGLE("RINH3", AK4671_ROUT2_SIGNAL_SELECT, 3, 1, 0),
+ SOC_DAPM_SINGLE("RINH4", AK4671_ROUT2_SIGNAL_SELECT, 4, 1, 0),
+ SOC_DAPM_SINGLE("LOOPHR", AK4671_ROUT2_SIGNAL_SELECT, 5, 1, 0),
+};
+
+static const struct snd_kcontrol_new ak4671_lout3_mixer_controls[] = {
+ SOC_DAPM_SINGLE("DACSL", AK4671_LOUT3_SIGNAL_SELECT, 0, 1, 0),
+ SOC_DAPM_SINGLE("LINS1", AK4671_LOUT3_SIGNAL_SELECT, 1, 1, 0),
+ SOC_DAPM_SINGLE("LINS2", AK4671_LOUT3_SIGNAL_SELECT, 2, 1, 0),
+ SOC_DAPM_SINGLE("LINS3", AK4671_LOUT3_SIGNAL_SELECT, 3, 1, 0),
+ SOC_DAPM_SINGLE("LINS4", AK4671_LOUT3_SIGNAL_SELECT, 4, 1, 0),
+ SOC_DAPM_SINGLE("LOOPSL", AK4671_LOUT3_SIGNAL_SELECT, 5, 1, 0),
+};
+
+static const struct snd_kcontrol_new ak4671_rout3_mixer_controls[] = {
+ SOC_DAPM_SINGLE("DACSR", AK4671_ROUT3_SIGNAL_SELECT, 0, 1, 0),
+ SOC_DAPM_SINGLE("RINS1", AK4671_ROUT3_SIGNAL_SELECT, 1, 1, 0),
+ SOC_DAPM_SINGLE("RINS2", AK4671_ROUT3_SIGNAL_SELECT, 2, 1, 0),
+ SOC_DAPM_SINGLE("RINS3", AK4671_ROUT3_SIGNAL_SELECT, 3, 1, 0),
+ SOC_DAPM_SINGLE("RINS4", AK4671_ROUT3_SIGNAL_SELECT, 4, 1, 0),
+ SOC_DAPM_SINGLE("LOOPSR", AK4671_ROUT3_SIGNAL_SELECT, 5, 1, 0),
+};
+
+/* Input MUXs */
+static const char *ak4671_lin_mux_texts[] =
+ {"LIN1", "LIN2", "LIN3", "LIN4"};
+static const struct soc_enum ak4671_lin_mux_enum =
+ SOC_ENUM_SINGLE(AK4671_MIC_SIGNAL_SELECT, 0,
+ ARRAY_SIZE(ak4671_lin_mux_texts),
+ ak4671_lin_mux_texts);
+static const struct snd_kcontrol_new ak4671_lin_mux_control =
+ SOC_DAPM_ENUM("Route", ak4671_lin_mux_enum);
+
+static const char *ak4671_rin_mux_texts[] =
+ {"RIN1", "RIN2", "RIN3", "RIN4"};
+static const struct soc_enum ak4671_rin_mux_enum =
+ SOC_ENUM_SINGLE(AK4671_MIC_SIGNAL_SELECT, 2,
+ ARRAY_SIZE(ak4671_rin_mux_texts),
+ ak4671_rin_mux_texts);
+static const struct snd_kcontrol_new ak4671_rin_mux_control =
+ SOC_DAPM_ENUM("Route", ak4671_rin_mux_enum);
+
+static const struct snd_soc_dapm_widget ak4671_dapm_widgets[] = {
+ /* Inputs */
+ SND_SOC_DAPM_INPUT("LIN1"),
+ SND_SOC_DAPM_INPUT("RIN1"),
+ SND_SOC_DAPM_INPUT("LIN2"),
+ SND_SOC_DAPM_INPUT("RIN2"),
+ SND_SOC_DAPM_INPUT("LIN3"),
+ SND_SOC_DAPM_INPUT("RIN3"),
+ SND_SOC_DAPM_INPUT("LIN4"),
+ SND_SOC_DAPM_INPUT("RIN4"),
+
+ /* Outputs */
+ SND_SOC_DAPM_OUTPUT("LOUT1"),
+ SND_SOC_DAPM_OUTPUT("ROUT1"),
+ SND_SOC_DAPM_OUTPUT("LOUT2"),
+ SND_SOC_DAPM_OUTPUT("ROUT2"),
+ SND_SOC_DAPM_OUTPUT("LOUT3"),
+ SND_SOC_DAPM_OUTPUT("ROUT3"),
+
+ /* DAC */
+ SND_SOC_DAPM_DAC("DAC Left", "Left HiFi Playback",
+ AK4671_AD_DA_POWER_MANAGEMENT, 6, 0),
+ SND_SOC_DAPM_DAC("DAC Right", "Right HiFi Playback",
+ AK4671_AD_DA_POWER_MANAGEMENT, 7, 0),
+
+ /* ADC */
+ SND_SOC_DAPM_ADC("ADC Left", "Left HiFi Capture",
+ AK4671_AD_DA_POWER_MANAGEMENT, 4, 0),
+ SND_SOC_DAPM_ADC("ADC Right", "Right HiFi Capture",
+ AK4671_AD_DA_POWER_MANAGEMENT, 5, 0),
+
+ /* PGA */
+ SND_SOC_DAPM_PGA("LOUT2 Mix Amp",
+ AK4671_LOUT2_POWER_MANAGERMENT, 5, 0, NULL, 0),
+ SND_SOC_DAPM_PGA("ROUT2 Mix Amp",
+ AK4671_LOUT2_POWER_MANAGERMENT, 6, 0, NULL, 0),
+
+ SND_SOC_DAPM_PGA("LIN1 Mixing Circuit",
+ AK4671_MIXING_POWER_MANAGEMENT1, 0, 0, NULL, 0),
+ SND_SOC_DAPM_PGA("RIN1 Mixing Circuit",
+ AK4671_MIXING_POWER_MANAGEMENT1, 1, 0, NULL, 0),
+ SND_SOC_DAPM_PGA("LIN2 Mixing Circuit",
+ AK4671_MIXING_POWER_MANAGEMENT1, 2, 0, NULL, 0),
+ SND_SOC_DAPM_PGA("RIN2 Mixing Circuit",
+ AK4671_MIXING_POWER_MANAGEMENT1, 3, 0, NULL, 0),
+ SND_SOC_DAPM_PGA("LIN3 Mixing Circuit",
+ AK4671_MIXING_POWER_MANAGEMENT1, 4, 0, NULL, 0),
+ SND_SOC_DAPM_PGA("RIN3 Mixing Circuit",
+ AK4671_MIXING_POWER_MANAGEMENT1, 5, 0, NULL, 0),
+ SND_SOC_DAPM_PGA("LIN4 Mixing Circuit",
+ AK4671_MIXING_POWER_MANAGEMENT1, 6, 0, NULL, 0),
+ SND_SOC_DAPM_PGA("RIN4 Mixing Circuit",
+ AK4671_MIXING_POWER_MANAGEMENT1, 7, 0, NULL, 0),
+
+ /* Output Mixers */
+ SND_SOC_DAPM_MIXER("LOUT1 Mixer", AK4671_LOUT1_POWER_MANAGERMENT, 0, 0,
+ &ak4671_lout1_mixer_controls[0],
+ ARRAY_SIZE(ak4671_lout1_mixer_controls)),
+ SND_SOC_DAPM_MIXER("ROUT1 Mixer", AK4671_LOUT1_POWER_MANAGERMENT, 1, 0,
+ &ak4671_rout1_mixer_controls[0],
+ ARRAY_SIZE(ak4671_rout1_mixer_controls)),
+ SND_SOC_DAPM_MIXER_E("LOUT2 Mixer", AK4671_LOUT2_POWER_MANAGERMENT,
+ 0, 0, &ak4671_lout2_mixer_controls[0],
+ ARRAY_SIZE(ak4671_lout2_mixer_controls),
+ ak4671_out2_event,
+ SND_SOC_DAPM_POST_PMU|SND_SOC_DAPM_PRE_PMD),
+ SND_SOC_DAPM_MIXER_E("ROUT2 Mixer", AK4671_LOUT2_POWER_MANAGERMENT,
+ 1, 0, &ak4671_rout2_mixer_controls[0],
+ ARRAY_SIZE(ak4671_rout2_mixer_controls),
+ ak4671_out2_event,
+ SND_SOC_DAPM_POST_PMU|SND_SOC_DAPM_PRE_PMD),
+ SND_SOC_DAPM_MIXER("LOUT3 Mixer", AK4671_LOUT3_POWER_MANAGERMENT, 0, 0,
+ &ak4671_lout3_mixer_controls[0],
+ ARRAY_SIZE(ak4671_lout3_mixer_controls)),
+ SND_SOC_DAPM_MIXER("ROUT3 Mixer", AK4671_LOUT3_POWER_MANAGERMENT, 1, 0,
+ &ak4671_rout3_mixer_controls[0],
+ ARRAY_SIZE(ak4671_rout3_mixer_controls)),
+
+ /* Input MUXs */
+ SND_SOC_DAPM_MUX("LIN MUX", AK4671_AD_DA_POWER_MANAGEMENT, 2, 0,
+ &ak4671_lin_mux_control),
+ SND_SOC_DAPM_MUX("RIN MUX", AK4671_AD_DA_POWER_MANAGEMENT, 3, 0,
+ &ak4671_rin_mux_control),
+
+ /* Mic Power */
+ SND_SOC_DAPM_MICBIAS("Mic Bias", AK4671_AD_DA_POWER_MANAGEMENT, 1, 0),
+
+ /* Supply */
+ SND_SOC_DAPM_SUPPLY("PMPLL", AK4671_PLL_MODE_SELECT1, 0, 0, NULL, 0),
+};
+
+static const struct snd_soc_dapm_route intercon[] = {
+ {"DAC Left", "NULL", "PMPLL"},
+ {"DAC Right", "NULL", "PMPLL"},
+ {"ADC Left", "NULL", "PMPLL"},
+ {"ADC Right", "NULL", "PMPLL"},
+
+ /* Outputs */
+ {"LOUT1", "NULL", "LOUT1 Mixer"},
+ {"ROUT1", "NULL", "ROUT1 Mixer"},
+ {"LOUT2", "NULL", "LOUT2 Mix Amp"},
+ {"ROUT2", "NULL", "ROUT2 Mix Amp"},
+ {"LOUT3", "NULL", "LOUT3 Mixer"},
+ {"ROUT3", "NULL", "ROUT3 Mixer"},
+
+ {"LOUT1 Mixer", "DACL", "DAC Left"},
+ {"ROUT1 Mixer", "DACR", "DAC Right"},
+ {"LOUT2 Mixer", "DACHL", "DAC Left"},
+ {"ROUT2 Mixer", "DACHR", "DAC Right"},
+ {"LOUT2 Mix Amp", "NULL", "LOUT2 Mixer"},
+ {"ROUT2 Mix Amp", "NULL", "ROUT2 Mixer"},
+ {"LOUT3 Mixer", "DACSL", "DAC Left"},
+ {"ROUT3 Mixer", "DACSR", "DAC Right"},
+
+ /* Inputs */
+ {"LIN MUX", "LIN1", "LIN1"},
+ {"LIN MUX", "LIN2", "LIN2"},
+ {"LIN MUX", "LIN3", "LIN3"},
+ {"LIN MUX", "LIN4", "LIN4"},
+
+ {"RIN MUX", "RIN1", "RIN1"},
+ {"RIN MUX", "RIN2", "RIN2"},
+ {"RIN MUX", "RIN3", "RIN3"},
+ {"RIN MUX", "RIN4", "RIN4"},
+
+ {"LIN1", NULL, "Mic Bias"},
+ {"RIN1", NULL, "Mic Bias"},
+ {"LIN2", NULL, "Mic Bias"},
+ {"RIN2", NULL, "Mic Bias"},
+
+ {"ADC Left", "NULL", "LIN MUX"},
+ {"ADC Right", "NULL", "RIN MUX"},
+
+ /* Analog Loops */
+ {"LIN1 Mixing Circuit", "NULL", "LIN1"},
+ {"RIN1 Mixing Circuit", "NULL", "RIN1"},
+ {"LIN2 Mixing Circuit", "NULL", "LIN2"},
+ {"RIN2 Mixing Circuit", "NULL", "RIN2"},
+ {"LIN3 Mixing Circuit", "NULL", "LIN3"},
+ {"RIN3 Mixing Circuit", "NULL", "RIN3"},
+ {"LIN4 Mixing Circuit", "NULL", "LIN4"},
+ {"RIN4 Mixing Circuit", "NULL", "RIN4"},
+
+ {"LOUT1 Mixer", "LINL1", "LIN1 Mixing Circuit"},
+ {"ROUT1 Mixer", "RINR1", "RIN1 Mixing Circuit"},
+ {"LOUT2 Mixer", "LINH1", "LIN1 Mixing Circuit"},
+ {"ROUT2 Mixer", "RINH1", "RIN1 Mixing Circuit"},
+ {"LOUT3 Mixer", "LINS1", "LIN1 Mixing Circuit"},
+ {"ROUT3 Mixer", "RINS1", "RIN1 Mixing Circuit"},
+
+ {"LOUT1 Mixer", "LINL2", "LIN2 Mixing Circuit"},
+ {"ROUT1 Mixer", "RINR2", "RIN2 Mixing Circuit"},
+ {"LOUT2 Mixer", "LINH2", "LIN2 Mixing Circuit"},
+ {"ROUT2 Mixer", "RINH2", "RIN2 Mixing Circuit"},
+ {"LOUT3 Mixer", "LINS2", "LIN2 Mixing Circuit"},
+ {"ROUT3 Mixer", "RINS2", "RIN2 Mixing Circuit"},
+
+ {"LOUT1 Mixer", "LINL3", "LIN3 Mixing Circuit"},
+ {"ROUT1 Mixer", "RINR3", "RIN3 Mixing Circuit"},
+ {"LOUT2 Mixer", "LINH3", "LIN3 Mixing Circuit"},
+ {"ROUT2 Mixer", "RINH3", "RIN3 Mixing Circuit"},
+ {"LOUT3 Mixer", "LINS3", "LIN3 Mixing Circuit"},
+ {"ROUT3 Mixer", "RINS3", "RIN3 Mixing Circuit"},
+
+ {"LOUT1 Mixer", "LINL4", "LIN4 Mixing Circuit"},
+ {"ROUT1 Mixer", "RINR4", "RIN4 Mixing Circuit"},
+ {"LOUT2 Mixer", "LINH4", "LIN4 Mixing Circuit"},
+ {"ROUT2 Mixer", "RINH4", "RIN4 Mixing Circuit"},
+ {"LOUT3 Mixer", "LINS4", "LIN4 Mixing Circuit"},
+ {"ROUT3 Mixer", "RINS4", "RIN4 Mixing Circuit"},
+};
+
+static int ak4671_add_widgets(struct snd_soc_codec *codec)
+{
+ snd_soc_dapm_new_controls(codec, ak4671_dapm_widgets,
+ ARRAY_SIZE(ak4671_dapm_widgets));
+
+ snd_soc_dapm_add_routes(codec, intercon, ARRAY_SIZE(intercon));
+
+ return 0;
+}
+
+static int ak4671_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params,
+ struct snd_soc_dai *dai)
+{
+ struct snd_soc_codec *codec = dai->codec;
+ u8 fs;
+
+ fs = snd_soc_read(codec, AK4671_PLL_MODE_SELECT0);
+ fs &= ~AK4671_FS;
+
+ switch (params_rate(params)) {
+ case 8000:
+ fs |= AK4671_FS_8KHZ;
+ break;
+ case 12000:
+ fs |= AK4671_FS_12KHZ;
+ break;
+ case 16000:
+ fs |= AK4671_FS_16KHZ;
+ break;
+ case 24000:
+ fs |= AK4671_FS_24KHZ;
+ break;
+ case 11025:
+ fs |= AK4671_FS_11_025KHZ;
+ break;
+ case 22050:
+ fs |= AK4671_FS_22_05KHZ;
+ break;
+ case 32000:
+ fs |= AK4671_FS_32KHZ;
+ break;
+ case 44100:
+ fs |= AK4671_FS_44_1KHZ;
+ break;
+ case 48000:
+ fs |= AK4671_FS_48KHZ;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ snd_soc_write(codec, AK4671_PLL_MODE_SELECT0, fs);
+
+ return 0;
+}
+
+static int ak4671_set_dai_sysclk(struct snd_soc_dai *dai, int clk_id,
+ unsigned int freq, int dir)
+{
+ struct snd_soc_codec *codec = dai->codec;
+ u8 pll;
+
+ pll = snd_soc_read(codec, AK4671_PLL_MODE_SELECT0);
+ pll &= ~AK4671_PLL;
+
+ switch (freq) {
+ case 11289600:
+ pll |= AK4671_PLL_11_2896MHZ;
+ break;
+ case 12000000:
+ pll |= AK4671_PLL_12MHZ;
+ break;
+ case 12288000:
+ pll |= AK4671_PLL_12_288MHZ;
+ break;
+ case 13000000:
+ pll |= AK4671_PLL_13MHZ;
+ break;
+ case 13500000:
+ pll |= AK4671_PLL_13_5MHZ;
+ break;
+ case 19200000:
+ pll |= AK4671_PLL_19_2MHZ;
+ break;
+ case 24000000:
+ pll |= AK4671_PLL_24MHZ;
+ break;
+ case 26000000:
+ pll |= AK4671_PLL_26MHZ;
+ break;
+ case 27000000:
+ pll |= AK4671_PLL_27MHZ;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ snd_soc_write(codec, AK4671_PLL_MODE_SELECT0, pll);
+
+ return 0;
+}
+
+static int ak4671_set_dai_fmt(struct snd_soc_dai *dai, unsigned int fmt)
+{
+ struct snd_soc_codec *codec = dai->codec;
+ u8 mode;
+ u8 format;
+
+ /* set master/slave audio interface */
+ mode = snd_soc_read(codec, AK4671_PLL_MODE_SELECT1);
+
+ switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) {
+ case SND_SOC_DAIFMT_CBM_CFM:
+ mode |= AK4671_M_S;
+ break;
+ case SND_SOC_DAIFMT_CBM_CFS:
+ mode &= ~(AK4671_M_S);
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ /* interface format */
+ format = snd_soc_read(codec, AK4671_FORMAT_SELECT);
+ format &= ~AK4671_DIF;
+
+ switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
+ case SND_SOC_DAIFMT_I2S:
+ format |= AK4671_DIF_I2S_MODE;
+ break;
+ case SND_SOC_DAIFMT_LEFT_J:
+ format |= AK4671_DIF_MSB_MODE;
+ break;
+ case SND_SOC_DAIFMT_DSP_A:
+ format |= AK4671_DIF_DSP_MODE;
+ format |= AK4671_BCKP;
+ format |= AK4671_MSBS;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ /* set mode and format */
+ snd_soc_write(codec, AK4671_PLL_MODE_SELECT1, mode);
+ snd_soc_write(codec, AK4671_FORMAT_SELECT, format);
+
+ return 0;
+}
+
+static int ak4671_set_bias_level(struct snd_soc_codec *codec,
+ enum snd_soc_bias_level level)
+{
+ u8 reg;
+
+ switch (level) {
+ case SND_SOC_BIAS_ON:
+ case SND_SOC_BIAS_PREPARE:
+ case SND_SOC_BIAS_STANDBY:
+ reg = snd_soc_read(codec, AK4671_AD_DA_POWER_MANAGEMENT);
+ snd_soc_write(codec, AK4671_AD_DA_POWER_MANAGEMENT,
+ reg | AK4671_PMVCM);
+ break;
+ case SND_SOC_BIAS_OFF:
+ snd_soc_write(codec, AK4671_AD_DA_POWER_MANAGEMENT, 0x00);
+ break;
+ }
+ codec->bias_level = level;
+ return 0;
+}
+
+#define AK4671_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)
+
+#define AK4671_FORMATS SNDRV_PCM_FMTBIT_S16_LE
+
+static struct snd_soc_dai_ops ak4671_dai_ops = {
+ .hw_params = ak4671_hw_params,
+ .set_sysclk = ak4671_set_dai_sysclk,
+ .set_fmt = ak4671_set_dai_fmt,
+};
+
+struct snd_soc_dai ak4671_dai = {
+ .name = "AK4671",
+ .playback = {
+ .stream_name = "Playback",
+ .channels_min = 1,
+ .channels_max = 2,
+ .rates = AK4671_RATES,
+ .formats = AK4671_FORMATS,},
+ .capture = {
+ .stream_name = "Capture",
+ .channels_min = 1,
+ .channels_max = 2,
+ .rates = AK4671_RATES,
+ .formats = AK4671_FORMATS,},
+ .ops = &ak4671_dai_ops,
+};
+EXPORT_SYMBOL_GPL(ak4671_dai);
+
+static int ak4671_probe(struct platform_device *pdev)
+{
+ struct snd_soc_device *socdev = platform_get_drvdata(pdev);
+ struct snd_soc_codec *codec;
+ int ret = 0;
+
+ if (ak4671_codec == NULL) {
+ dev_err(&pdev->dev, "Codec device not registered\n");
+ return -ENODEV;
+ }
+
+ socdev->card->codec = ak4671_codec;
+ codec = ak4671_codec;
+
+ /* register pcms */
+ ret = snd_soc_new_pcms(socdev, SNDRV_DEFAULT_IDX1, SNDRV_DEFAULT_STR1);
+ if (ret < 0) {
+ dev_err(codec->dev, "failed to create pcms: %d\n", ret);
+ goto pcm_err;
+ }
+
+ snd_soc_add_controls(codec, ak4671_snd_controls,
+ ARRAY_SIZE(ak4671_snd_controls));
+ ak4671_add_widgets(codec);
+
+ ak4671_set_bias_level(codec, SND_SOC_BIAS_STANDBY);
+
+ return ret;
+
+pcm_err:
+ return ret;
+}
+
+static int ak4671_remove(struct platform_device *pdev)
+{
+ struct snd_soc_device *socdev = platform_get_drvdata(pdev);
+
+ snd_soc_free_pcms(socdev);
+ snd_soc_dapm_free(socdev);
+
+ return 0;
+}
+
+struct snd_soc_codec_device soc_codec_dev_ak4671 = {
+ .probe = ak4671_probe,
+ .remove = ak4671_remove,
+};
+EXPORT_SYMBOL_GPL(soc_codec_dev_ak4671);
+
+static int ak4671_register(struct ak4671_priv *ak4671,
+ enum snd_soc_control_type control)
+{
+ int ret;
+ struct snd_soc_codec *codec = &ak4671->codec;
+
+ if (ak4671_codec) {
+ dev_err(codec->dev, "Another AK4671 is registered\n");
+ ret = -EINVAL;
+ goto err;
+ }
+
+ mutex_init(&codec->mutex);
+ INIT_LIST_HEAD(&codec->dapm_widgets);
+ INIT_LIST_HEAD(&codec->dapm_paths);
+
+ codec->private_data = ak4671;
+ codec->name = "AK4671";
+ codec->owner = THIS_MODULE;
+ codec->bias_level = SND_SOC_BIAS_OFF;
+ codec->set_bias_level = ak4671_set_bias_level;
+ codec->dai = &ak4671_dai;
+ codec->num_dai = 1;
+ codec->reg_cache_size = AK4671_CACHEREGNUM;
+ codec->reg_cache = &ak4671->reg_cache;
+
+ memcpy(codec->reg_cache, ak4671_reg, sizeof(ak4671_reg));
+
+ ret = snd_soc_codec_set_cache_io(codec, 8, 8, control);
+ if (ret < 0) {
+ dev_err(codec->dev, "Failed to set cache I/O: %d\n", ret);
+ goto err;
+ }
+
+ ak4671_dai.dev = codec->dev;
+ ak4671_codec = codec;
+
+ ret = snd_soc_register_codec(codec);
+ if (ret != 0) {
+ dev_err(codec->dev, "Failed to register codec: %d\n", ret);
+ goto err;
+ }
+
+ ret = snd_soc_register_dai(&ak4671_dai);
+ if (ret != 0) {
+ dev_err(codec->dev, "Failed to register DAI: %d\n", ret);
+ goto err_codec;
+ }
+
+ return 0;
+
+err_codec:
+ snd_soc_unregister_codec(codec);
+err:
+ kfree(ak4671);
+ return ret;
+}
+
+static void ak4671_unregister(struct ak4671_priv *ak4671)
+{
+ ak4671_set_bias_level(&ak4671->codec, SND_SOC_BIAS_OFF);
+ snd_soc_unregister_dai(&ak4671_dai);
+ snd_soc_unregister_codec(&ak4671->codec);
+ kfree(ak4671);
+ ak4671_codec = NULL;
+}
+
+static int __devinit ak4671_i2c_probe(struct i2c_client *client,
+ const struct i2c_device_id *id)
+{
+ struct ak4671_priv *ak4671;
+ struct snd_soc_codec *codec;
+
+ ak4671 = kzalloc(sizeof(struct ak4671_priv), GFP_KERNEL);
+ if (ak4671 == NULL)
+ return -ENOMEM;
+
+ codec = &ak4671->codec;
+ codec->hw_write = (hw_write_t)i2c_master_send;
+
+ i2c_set_clientdata(client, ak4671);
+ codec->control_data = client;
+
+ codec->dev = &client->dev;
+
+ return ak4671_register(ak4671, SND_SOC_I2C);
+}
+
+static __devexit int ak4671_i2c_remove(struct i2c_client *client)
+{
+ struct ak4671_priv *ak4671 = i2c_get_clientdata(client);
+
+ ak4671_unregister(ak4671);
+
+ return 0;
+}
+
+static const struct i2c_device_id ak4671_i2c_id[] = {
+ { "ak4671", 0 },
+ { }
+};
+MODULE_DEVICE_TABLE(i2c, ak4671_i2c_id);
+
+static struct i2c_driver ak4671_i2c_driver = {
+ .driver = {
+ .name = "ak4671",
+ .owner = THIS_MODULE,
+ },
+ .probe = ak4671_i2c_probe,
+ .remove = __devexit_p(ak4671_i2c_remove),
+ .id_table = ak4671_i2c_id,
+};
+
+static int __init ak4671_modinit(void)
+{
+ return i2c_add_driver(&ak4671_i2c_driver);
+}
+module_init(ak4671_modinit);
+
+static void __exit ak4671_exit(void)
+{
+ i2c_del_driver(&ak4671_i2c_driver);
+}
+module_exit(ak4671_exit);
+
+MODULE_DESCRIPTION("ASoC AK4671 codec driver");
+MODULE_AUTHOR("Joonyoung Shim <jy0922.shim@samsung.com>");
+MODULE_LICENSE("GPL");
diff --git a/sound/soc/codecs/ak4671.h b/sound/soc/codecs/ak4671.h
new file mode 100644
index 000000000000..e2fad964e88b
--- /dev/null
+++ b/sound/soc/codecs/ak4671.h
@@ -0,0 +1,156 @@
+/*
+ * ak4671.h -- audio driver for AK4671
+ *
+ * Copyright (C) 2009 Samsung Electronics Co.Ltd
+ * Author: Joonyoung Shim <jy0922.shim@samsung.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.
+ *
+ */
+
+#ifndef _AK4671_H
+#define _AK4671_H
+
+#define AK4671_AD_DA_POWER_MANAGEMENT 0x00
+#define AK4671_PLL_MODE_SELECT0 0x01
+#define AK4671_PLL_MODE_SELECT1 0x02
+#define AK4671_FORMAT_SELECT 0x03
+#define AK4671_MIC_SIGNAL_SELECT 0x04
+#define AK4671_MIC_AMP_GAIN 0x05
+#define AK4671_MIXING_POWER_MANAGEMENT0 0x06
+#define AK4671_MIXING_POWER_MANAGEMENT1 0x07
+#define AK4671_OUTPUT_VOLUME_CONTROL 0x08
+#define AK4671_LOUT1_SIGNAL_SELECT 0x09
+#define AK4671_ROUT1_SIGNAL_SELECT 0x0a
+#define AK4671_LOUT2_SIGNAL_SELECT 0x0b
+#define AK4671_ROUT2_SIGNAL_SELECT 0x0c
+#define AK4671_LOUT3_SIGNAL_SELECT 0x0d
+#define AK4671_ROUT3_SIGNAL_SELECT 0x0e
+#define AK4671_LOUT1_POWER_MANAGERMENT 0x0f
+#define AK4671_LOUT2_POWER_MANAGERMENT 0x10
+#define AK4671_LOUT3_POWER_MANAGERMENT 0x11
+#define AK4671_LCH_INPUT_VOLUME_CONTROL 0x12
+#define AK4671_RCH_INPUT_VOLUME_CONTROL 0x13
+#define AK4671_ALC_REFERENCE_SELECT 0x14
+#define AK4671_DIGITAL_MIXING_CONTROL 0x15
+#define AK4671_ALC_TIMER_SELECT 0x16
+#define AK4671_ALC_MODE_CONTROL 0x17
+#define AK4671_MODE_CONTROL1 0x18
+#define AK4671_MODE_CONTROL2 0x19
+#define AK4671_LCH_OUTPUT_VOLUME_CONTROL 0x1a
+#define AK4671_RCH_OUTPUT_VOLUME_CONTROL 0x1b
+#define AK4671_SIDETONE_A_CONTROL 0x1c
+#define AK4671_DIGITAL_FILTER_SELECT 0x1d
+#define AK4671_FIL3_COEFFICIENT0 0x1e
+#define AK4671_FIL3_COEFFICIENT1 0x1f
+#define AK4671_FIL3_COEFFICIENT2 0x20
+#define AK4671_FIL3_COEFFICIENT3 0x21
+#define AK4671_EQ_COEFFICIENT0 0x22
+#define AK4671_EQ_COEFFICIENT1 0x23
+#define AK4671_EQ_COEFFICIENT2 0x24
+#define AK4671_EQ_COEFFICIENT3 0x25
+#define AK4671_EQ_COEFFICIENT4 0x26
+#define AK4671_EQ_COEFFICIENT5 0x27
+#define AK4671_FIL1_COEFFICIENT0 0x28
+#define AK4671_FIL1_COEFFICIENT1 0x29
+#define AK4671_FIL1_COEFFICIENT2 0x2a
+#define AK4671_FIL1_COEFFICIENT3 0x2b
+#define AK4671_FIL2_COEFFICIENT0 0x2c
+#define AK4671_FIL2_COEFFICIENT1 0x2d
+#define AK4671_FIL2_COEFFICIENT2 0x2e
+#define AK4671_FIL2_COEFFICIENT3 0x2f
+#define AK4671_DIGITAL_FILTER_SELECT2 0x30
+#define AK4671_E1_COEFFICIENT0 0x32
+#define AK4671_E1_COEFFICIENT1 0x33
+#define AK4671_E1_COEFFICIENT2 0x34
+#define AK4671_E1_COEFFICIENT3 0x35
+#define AK4671_E1_COEFFICIENT4 0x36
+#define AK4671_E1_COEFFICIENT5 0x37
+#define AK4671_E2_COEFFICIENT0 0x38
+#define AK4671_E2_COEFFICIENT1 0x39
+#define AK4671_E2_COEFFICIENT2 0x3a
+#define AK4671_E2_COEFFICIENT3 0x3b
+#define AK4671_E2_COEFFICIENT4 0x3c
+#define AK4671_E2_COEFFICIENT5 0x3d
+#define AK4671_E3_COEFFICIENT0 0x3e
+#define AK4671_E3_COEFFICIENT1 0x3f
+#define AK4671_E3_COEFFICIENT2 0x40
+#define AK4671_E3_COEFFICIENT3 0x41
+#define AK4671_E3_COEFFICIENT4 0x42
+#define AK4671_E3_COEFFICIENT5 0x43
+#define AK4671_E4_COEFFICIENT0 0x44
+#define AK4671_E4_COEFFICIENT1 0x45
+#define AK4671_E4_COEFFICIENT2 0x46
+#define AK4671_E4_COEFFICIENT3 0x47
+#define AK4671_E4_COEFFICIENT4 0x48
+#define AK4671_E4_COEFFICIENT5 0x49
+#define AK4671_E5_COEFFICIENT0 0x4a
+#define AK4671_E5_COEFFICIENT1 0x4b
+#define AK4671_E5_COEFFICIENT2 0x4c
+#define AK4671_E5_COEFFICIENT3 0x4d
+#define AK4671_E5_COEFFICIENT4 0x4e
+#define AK4671_E5_COEFFICIENT5 0x4f
+#define AK4671_EQ_CONTROL_250HZ_100HZ 0x50
+#define AK4671_EQ_CONTROL_3500HZ_1KHZ 0x51
+#define AK4671_EQ_CONTRO_10KHZ 0x52
+#define AK4671_PCM_IF_CONTROL0 0x53
+#define AK4671_PCM_IF_CONTROL1 0x54
+#define AK4671_PCM_IF_CONTROL2 0x55
+#define AK4671_DIGITAL_VOLUME_B_CONTROL 0x56
+#define AK4671_DIGITAL_VOLUME_C_CONTROL 0x57
+#define AK4671_SIDETONE_VOLUME_CONTROL 0x58
+#define AK4671_DIGITAL_MIXING_CONTROL2 0x59
+#define AK4671_SAR_ADC_CONTROL 0x5a
+
+#define AK4671_CACHEREGNUM (AK4671_SAR_ADC_CONTROL + 1)
+
+/* Bitfield Definitions */
+
+/* AK4671_AD_DA_POWER_MANAGEMENT (0x00) Fields */
+#define AK4671_PMVCM 0x01
+
+/* AK4671_PLL_MODE_SELECT0 (0x01) Fields */
+#define AK4671_PLL 0x0f
+#define AK4671_PLL_11_2896MHZ (4 << 0)
+#define AK4671_PLL_12_288MHZ (5 << 0)
+#define AK4671_PLL_12MHZ (6 << 0)
+#define AK4671_PLL_24MHZ (7 << 0)
+#define AK4671_PLL_19_2MHZ (8 << 0)
+#define AK4671_PLL_13_5MHZ (12 << 0)
+#define AK4671_PLL_27MHZ (13 << 0)
+#define AK4671_PLL_13MHZ (14 << 0)
+#define AK4671_PLL_26MHZ (15 << 0)
+#define AK4671_FS 0xf0
+#define AK4671_FS_8KHZ (0 << 4)
+#define AK4671_FS_12KHZ (1 << 4)
+#define AK4671_FS_16KHZ (2 << 4)
+#define AK4671_FS_24KHZ (3 << 4)
+#define AK4671_FS_11_025KHZ (5 << 4)
+#define AK4671_FS_22_05KHZ (7 << 4)
+#define AK4671_FS_32KHZ (10 << 4)
+#define AK4671_FS_48KHZ (11 << 4)
+#define AK4671_FS_44_1KHZ (15 << 4)
+
+/* AK4671_PLL_MODE_SELECT1 (0x02) Fields */
+#define AK4671_PMPLL 0x01
+#define AK4671_M_S 0x02
+
+/* AK4671_FORMAT_SELECT (0x03) Fields */
+#define AK4671_DIF 0x03
+#define AK4671_DIF_DSP_MODE (0 << 0)
+#define AK4671_DIF_MSB_MODE (2 << 0)
+#define AK4671_DIF_I2S_MODE (3 << 0)
+#define AK4671_BCKP 0x04
+#define AK4671_MSBS 0x08
+#define AK4671_SDOD 0x10
+
+/* AK4671_LOUT2_POWER_MANAGEMENT (0x10) Fields */
+#define AK4671_MUTEN 0x04
+
+extern struct snd_soc_dai ak4671_dai;
+extern struct snd_soc_codec_device soc_codec_dev_ak4671;
+
+#endif
diff --git a/sound/soc/codecs/cs4270.c b/sound/soc/codecs/cs4270.c
index ca1e24a8f12a..ffe122d1cd76 100644
--- a/sound/soc/codecs/cs4270.c
+++ b/sound/soc/codecs/cs4270.c
@@ -520,6 +520,7 @@ static const struct snd_kcontrol_new cs4270_snd_controls[] = {
SOC_SINGLE("Digital Sidetone Switch", CS4270_FORMAT, 5, 1, 0),
SOC_SINGLE("Soft Ramp Switch", CS4270_TRANS, 6, 1, 0),
SOC_SINGLE("Zero Cross Switch", CS4270_TRANS, 5, 1, 0),
+ SOC_SINGLE("De-emphasis filter", CS4270_TRANS, 0, 1, 0),
SOC_SINGLE("Popguard Switch", CS4270_MODE, 0, 1, 1),
SOC_SINGLE("Auto-Mute Switch", CS4270_MUTE, 5, 1, 0),
SOC_DOUBLE("Master Capture Switch", CS4270_MUTE, 3, 4, 1, 1),
@@ -598,13 +599,6 @@ static int cs4270_probe(struct platform_device *pdev)
goto error_free_pcms;
}
- /* And finally, register the socdev */
- ret = snd_soc_init_card(socdev);
- if (ret < 0) {
- dev_err(codec->dev, "failed to register card\n");
- goto error_free_pcms;
- }
-
return 0;
error_free_pcms:
@@ -802,22 +796,6 @@ MODULE_DEVICE_TABLE(i2c, cs4270_id);
* and all registers are written back to the hardware when resuming.
*/
-static int cs4270_i2c_suspend(struct i2c_client *client, pm_message_t mesg)
-{
- struct cs4270_private *cs4270 = i2c_get_clientdata(client);
- struct snd_soc_codec *codec = &cs4270->codec;
-
- return snd_soc_suspend_device(codec->dev);
-}
-
-static int cs4270_i2c_resume(struct i2c_client *client)
-{
- struct cs4270_private *cs4270 = i2c_get_clientdata(client);
- struct snd_soc_codec *codec = &cs4270->codec;
-
- return snd_soc_resume_device(codec->dev);
-}
-
static int cs4270_soc_suspend(struct platform_device *pdev, pm_message_t mesg)
{
struct snd_soc_codec *codec = cs4270_codec;
@@ -853,8 +831,6 @@ static int cs4270_soc_resume(struct platform_device *pdev)
return snd_soc_write(codec, CS4270_PWRCTL, reg);
}
#else
-#define cs4270_i2c_suspend NULL
-#define cs4270_i2c_resume NULL
#define cs4270_soc_suspend NULL
#define cs4270_soc_resume NULL
#endif /* CONFIG_PM */
@@ -873,8 +849,6 @@ static struct i2c_driver cs4270_i2c_driver = {
.id_table = cs4270_id,
.probe = cs4270_i2c_probe,
.remove = cs4270_i2c_remove,
- .suspend = cs4270_i2c_suspend,
- .resume = cs4270_i2c_resume,
};
/*
diff --git a/sound/soc/codecs/cx20442.c b/sound/soc/codecs/cx20442.c
index 38eac9c866e1..e000cdfec1ec 100644
--- a/sound/soc/codecs/cx20442.c
+++ b/sound/soc/codecs/cx20442.c
@@ -93,7 +93,6 @@ static int cx20442_add_widgets(struct snd_soc_codec *codec)
snd_soc_dapm_add_routes(codec, cx20442_audio_map,
ARRAY_SIZE(cx20442_audio_map));
- snd_soc_dapm_new_widgets(codec);
return 0;
}
@@ -355,17 +354,6 @@ static int cx20442_codec_probe(struct platform_device *pdev)
cx20442_add_widgets(codec);
- ret = snd_soc_init_card(socdev);
- if (ret < 0) {
- dev_err(&pdev->dev, "failed to register card\n");
- goto card_err;
- }
-
- return ret;
-
-card_err:
- snd_soc_free_pcms(socdev);
- snd_soc_dapm_free(socdev);
pcm_err:
return ret;
}
diff --git a/sound/soc/codecs/pcm3008.c b/sound/soc/codecs/pcm3008.c
index 5cda9e6b5a74..2afcd0a8669d 100644
--- a/sound/soc/codecs/pcm3008.c
+++ b/sound/soc/codecs/pcm3008.c
@@ -90,13 +90,6 @@ static int pcm3008_soc_probe(struct platform_device *pdev)
goto pcm_err;
}
- /* Register Card. */
- ret = snd_soc_init_card(socdev);
- if (ret < 0) {
- printk(KERN_ERR "pcm3008: failed to register card\n");
- goto card_err;
- }
-
/* DEM1 DEM0 DE-EMPHASIS_MODE
* Low Low De-emphasis 44.1 kHz ON
* Low High De-emphasis OFF
@@ -136,8 +129,6 @@ static int pcm3008_soc_probe(struct platform_device *pdev)
gpio_err:
pcm3008_gpio_free(setup);
-card_err:
- snd_soc_free_pcms(socdev);
pcm_err:
kfree(socdev->card->codec);
diff --git a/sound/soc/codecs/ssm2602.c b/sound/soc/codecs/ssm2602.c
index c550750c79c0..d2ff1cde6883 100644
--- a/sound/soc/codecs/ssm2602.c
+++ b/sound/soc/codecs/ssm2602.c
@@ -210,7 +210,6 @@ static int ssm2602_add_widgets(struct snd_soc_codec *codec)
snd_soc_dapm_add_routes(codec, audio_conn, ARRAY_SIZE(audio_conn));
- snd_soc_dapm_new_widgets(codec);
return 0;
}
@@ -613,17 +612,9 @@ static int ssm2602_init(struct snd_soc_device *socdev)
snd_soc_add_controls(codec, ssm2602_snd_controls,
ARRAY_SIZE(ssm2602_snd_controls));
ssm2602_add_widgets(codec);
- ret = snd_soc_init_card(socdev);
- if (ret < 0) {
- pr_err("ssm2602: 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;
diff --git a/sound/soc/codecs/stac9766.c b/sound/soc/codecs/stac9766.c
index befc6488c39a..bbc72c2ddfca 100644
--- a/sound/soc/codecs/stac9766.c
+++ b/sound/soc/codecs/stac9766.c
@@ -418,9 +418,6 @@ static int stac9766_codec_probe(struct platform_device *pdev)
snd_soc_add_controls(codec, stac9766_snd_ac97_controls,
ARRAY_SIZE(stac9766_snd_ac97_controls));
- ret = snd_soc_init_card(socdev);
- if (ret < 0)
- goto reset_err;
return 0;
reset_err:
diff --git a/sound/soc/codecs/tlv320aic23.c b/sound/soc/codecs/tlv320aic23.c
index 90a0264f7538..a9dc5fb54774 100644
--- a/sound/soc/codecs/tlv320aic23.c
+++ b/sound/soc/codecs/tlv320aic23.c
@@ -85,7 +85,7 @@ static int tlv320aic23_write(struct snd_soc_codec *codec, unsigned int reg,
* of data into val
*/
- if ((reg < 0 || reg > 9) && (reg != 15)) {
+ if (reg > 9 && reg != 15) {
printk(KERN_WARNING "%s Invalid register R%u\n", __func__, reg);
return -1;
}
@@ -395,7 +395,6 @@ static int tlv320aic23_add_widgets(struct snd_soc_codec *codec)
/* set up audio path interconnects */
snd_soc_dapm_add_routes(codec, intercon, ARRAY_SIZE(intercon));
- snd_soc_dapm_new_widgets(codec);
return 0;
}
@@ -706,17 +705,9 @@ static int tlv320aic23_init(struct snd_soc_device *socdev)
snd_soc_add_controls(codec, tlv320aic23_snd_controls,
ARRAY_SIZE(tlv320aic23_snd_controls));
tlv320aic23_add_widgets(codec);
- ret = snd_soc_init_card(socdev);
- if (ret < 0) {
- printk(KERN_ERR "tlv320aic23: 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;
diff --git a/sound/soc/codecs/tlv320aic26.c b/sound/soc/codecs/tlv320aic26.c
index 3387d9e736ea..357b609196e3 100644
--- a/sound/soc/codecs/tlv320aic26.c
+++ b/sound/soc/codecs/tlv320aic26.c
@@ -356,18 +356,7 @@ static int aic26_probe(struct platform_device *pdev)
ARRAY_SIZE(aic26_snd_controls));
WARN_ON(err < 0);
- /* CODEC is setup, we can register the card now */
- dev_dbg(&pdev->dev, "Registering card\n");
- ret = snd_soc_init_card(socdev);
- if (ret < 0) {
- dev_err(&pdev->dev, "aic26: failed to register card\n");
- goto card_err;
- }
return 0;
-
- card_err:
- snd_soc_free_pcms(socdev);
- return ret;
}
static int aic26_remove(struct platform_device *pdev)
diff --git a/sound/soc/codecs/tlv320aic3x.c b/sound/soc/codecs/tlv320aic3x.c
index 3395cf945d56..2b4dc2b0b017 100644
--- a/sound/soc/codecs/tlv320aic3x.c
+++ b/sound/soc/codecs/tlv320aic3x.c
@@ -753,7 +753,6 @@ static int aic3x_add_widgets(struct snd_soc_codec *codec)
/* set up audio path interconnects */
snd_soc_dapm_add_routes(codec, intercon, ARRAY_SIZE(intercon));
- snd_soc_dapm_new_widgets(codec);
return 0;
}
@@ -1405,18 +1404,8 @@ static int aic3x_probe(struct platform_device *pdev)
aic3x_add_widgets(codec);
- ret = snd_soc_init_card(socdev);
- if (ret < 0) {
- printk(KERN_ERR "aic3x: 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;
diff --git a/sound/soc/codecs/tlv320dac33.c b/sound/soc/codecs/tlv320dac33.c
new file mode 100644
index 000000000000..9c8903dbe647
--- /dev/null
+++ b/sound/soc/codecs/tlv320dac33.c
@@ -0,0 +1,1229 @@
+/*
+ * ALSA SoC Texas Instruments TLV320DAC33 codec driver
+ *
+ * Author: Peter Ujfalusi <peter.ujfalusi@nokia.com>
+ *
+ * Copyright: (C) 2009 Nokia Corporation
+ *
+ * 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.
+ *
+ * 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., 51 Franklin St, Fifth Floor, Boston, MA
+ * 02110-1301 USA
+ *
+ */
+
+#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 <linux/interrupt.h>
+#include <linux/gpio.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 <sound/tlv.h>
+
+#include <sound/tlv320dac33-plat.h>
+#include "tlv320dac33.h"
+
+#define DAC33_BUFFER_SIZE_BYTES 24576 /* bytes, 12288 16 bit words,
+ * 6144 stereo */
+#define DAC33_BUFFER_SIZE_SAMPLES 6144
+
+#define NSAMPLE_MAX 5700
+
+#define LATENCY_TIME_MS 20
+
+static struct snd_soc_codec *tlv320dac33_codec;
+
+enum dac33_state {
+ DAC33_IDLE = 0,
+ DAC33_PREFILL,
+ DAC33_PLAYBACK,
+ DAC33_FLUSH,
+};
+
+struct tlv320dac33_priv {
+ struct mutex mutex;
+ struct workqueue_struct *dac33_wq;
+ struct work_struct work;
+ struct snd_soc_codec codec;
+ int power_gpio;
+ int chip_power;
+ int irq;
+ unsigned int refclk;
+
+ unsigned int alarm_threshold; /* set to be half of LATENCY_TIME_MS */
+ unsigned int nsample_min; /* nsample should not be lower than
+ * this */
+ unsigned int nsample_max; /* nsample should not be higher than
+ * this */
+ unsigned int nsample_switch; /* Use FIFO or bypass FIFO switch */
+ unsigned int nsample; /* burst read amount from host */
+
+ enum dac33_state state;
+};
+
+static const u8 dac33_reg[DAC33_CACHEREGNUM] = {
+0x00, 0x00, 0x00, 0x00, /* 0x00 - 0x03 */
+0x00, 0x00, 0x00, 0x00, /* 0x04 - 0x07 */
+0x00, 0x00, 0x00, 0x00, /* 0x08 - 0x0b */
+0x00, 0x00, 0x00, 0x00, /* 0x0c - 0x0f */
+0x00, 0x00, 0x00, 0x00, /* 0x10 - 0x13 */
+0x00, 0x00, 0x00, 0x00, /* 0x14 - 0x17 */
+0x00, 0x00, 0x00, 0x00, /* 0x18 - 0x1b */
+0x00, 0x00, 0x00, 0x00, /* 0x1c - 0x1f */
+0x00, 0x00, 0x00, 0x00, /* 0x20 - 0x23 */
+0x00, 0x00, 0x00, 0x00, /* 0x24 - 0x27 */
+0x00, 0x00, 0x00, 0x00, /* 0x28 - 0x2b */
+0x00, 0x00, 0x00, 0x80, /* 0x2c - 0x2f */
+0x80, 0x00, 0x00, 0x00, /* 0x30 - 0x33 */
+0x00, 0x00, 0x00, 0x00, /* 0x34 - 0x37 */
+0x00, 0x00, /* 0x38 - 0x39 */
+/* Registers 0x3a - 0x3f are reserved */
+ 0x00, 0x00, /* 0x3a - 0x3b */
+0x00, 0x00, 0x00, 0x00, /* 0x3c - 0x3f */
+
+0x00, 0x00, 0x00, 0x00, /* 0x40 - 0x43 */
+0x00, 0x80, /* 0x44 - 0x45 */
+/* Registers 0x46 - 0x47 are reserved */
+ 0x80, 0x80, /* 0x46 - 0x47 */
+
+0x80, 0x00, 0x00, /* 0x48 - 0x4a */
+/* Registers 0x4b - 0x7c are reserved */
+ 0x00, /* 0x4b */
+0x00, 0x00, 0x00, 0x00, /* 0x4c - 0x4f */
+0x00, 0x00, 0x00, 0x00, /* 0x50 - 0x53 */
+0x00, 0x00, 0x00, 0x00, /* 0x54 - 0x57 */
+0x00, 0x00, 0x00, 0x00, /* 0x58 - 0x5b */
+0x00, 0x00, 0x00, 0x00, /* 0x5c - 0x5f */
+0x00, 0x00, 0x00, 0x00, /* 0x60 - 0x63 */
+0x00, 0x00, 0x00, 0x00, /* 0x64 - 0x67 */
+0x00, 0x00, 0x00, 0x00, /* 0x68 - 0x6b */
+0x00, 0x00, 0x00, 0x00, /* 0x6c - 0x6f */
+0x00, 0x00, 0x00, 0x00, /* 0x70 - 0x73 */
+0x00, 0x00, 0x00, 0x00, /* 0x74 - 0x77 */
+0x00, 0x00, 0x00, 0x00, /* 0x78 - 0x7b */
+0x00, /* 0x7c */
+
+ 0xda, 0x33, 0x03, /* 0x7d - 0x7f */
+};
+
+/* Register read and write */
+static inline unsigned int dac33_read_reg_cache(struct snd_soc_codec *codec,
+ unsigned reg)
+{
+ u8 *cache = codec->reg_cache;
+ if (reg >= DAC33_CACHEREGNUM)
+ return 0;
+
+ return cache[reg];
+}
+
+static inline void dac33_write_reg_cache(struct snd_soc_codec *codec,
+ u8 reg, u8 value)
+{
+ u8 *cache = codec->reg_cache;
+ if (reg >= DAC33_CACHEREGNUM)
+ return;
+
+ cache[reg] = value;
+}
+
+static int dac33_read(struct snd_soc_codec *codec, unsigned int reg,
+ u8 *value)
+{
+ struct tlv320dac33_priv *dac33 = codec->private_data;
+ int val;
+
+ *value = reg & 0xff;
+
+ /* If powered off, return the cached value */
+ if (dac33->chip_power) {
+ val = i2c_smbus_read_byte_data(codec->control_data, value[0]);
+ if (val < 0) {
+ dev_err(codec->dev, "Read failed (%d)\n", val);
+ value[0] = dac33_read_reg_cache(codec, reg);
+ } else {
+ value[0] = val;
+ dac33_write_reg_cache(codec, reg, val);
+ }
+ } else {
+ value[0] = dac33_read_reg_cache(codec, reg);
+ }
+
+ return 0;
+}
+
+static int dac33_write(struct snd_soc_codec *codec, unsigned int reg,
+ unsigned int value)
+{
+ struct tlv320dac33_priv *dac33 = codec->private_data;
+ u8 data[2];
+ int ret = 0;
+
+ /*
+ * data is
+ * D15..D8 dac33 register offset
+ * D7...D0 register data
+ */
+ data[0] = reg & 0xff;
+ data[1] = value & 0xff;
+
+ dac33_write_reg_cache(codec, data[0], data[1]);
+ if (dac33->chip_power) {
+ ret = codec->hw_write(codec->control_data, data, 2);
+ if (ret != 2)
+ dev_err(codec->dev, "Write failed (%d)\n", ret);
+ else
+ ret = 0;
+ }
+
+ return ret;
+}
+
+static int dac33_write_locked(struct snd_soc_codec *codec, unsigned int reg,
+ unsigned int value)
+{
+ struct tlv320dac33_priv *dac33 = codec->private_data;
+ int ret;
+
+ mutex_lock(&dac33->mutex);
+ ret = dac33_write(codec, reg, value);
+ mutex_unlock(&dac33->mutex);
+
+ return ret;
+}
+
+#define DAC33_I2C_ADDR_AUTOINC 0x80
+static int dac33_write16(struct snd_soc_codec *codec, unsigned int reg,
+ unsigned int value)
+{
+ struct tlv320dac33_priv *dac33 = codec->private_data;
+ u8 data[3];
+ int ret = 0;
+
+ /*
+ * data is
+ * D23..D16 dac33 register offset
+ * D15..D8 register data MSB
+ * D7...D0 register data LSB
+ */
+ data[0] = reg & 0xff;
+ data[1] = (value >> 8) & 0xff;
+ data[2] = value & 0xff;
+
+ dac33_write_reg_cache(codec, data[0], data[1]);
+ dac33_write_reg_cache(codec, data[0] + 1, data[2]);
+
+ if (dac33->chip_power) {
+ /* We need to set autoincrement mode for 16 bit writes */
+ data[0] |= DAC33_I2C_ADDR_AUTOINC;
+ ret = codec->hw_write(codec->control_data, data, 3);
+ if (ret != 3)
+ dev_err(codec->dev, "Write failed (%d)\n", ret);
+ else
+ ret = 0;
+ }
+
+ return ret;
+}
+
+static void dac33_restore_regs(struct snd_soc_codec *codec)
+{
+ struct tlv320dac33_priv *dac33 = codec->private_data;
+ u8 *cache = codec->reg_cache;
+ u8 data[2];
+ int i, ret;
+
+ if (!dac33->chip_power)
+ return;
+
+ for (i = DAC33_PWR_CTRL; i <= DAC33_INTP_CTRL_B; i++) {
+ data[0] = i;
+ data[1] = cache[i];
+ /* Skip the read only registers */
+ if ((i >= DAC33_INT_OSC_STATUS &&
+ i <= DAC33_INT_OSC_FREQ_RAT_READ_B) ||
+ (i >= DAC33_FIFO_WPTR_MSB && i <= DAC33_FIFO_IRQ_FLAG) ||
+ i == DAC33_DAC_STATUS_FLAGS ||
+ i == DAC33_SRC_EST_REF_CLK_RATIO_A ||
+ i == DAC33_SRC_EST_REF_CLK_RATIO_B)
+ continue;
+ ret = codec->hw_write(codec->control_data, data, 2);
+ if (ret != 2)
+ dev_err(codec->dev, "Write failed (%d)\n", ret);
+ }
+ for (i = DAC33_LDAC_PWR_CTRL; i <= DAC33_LINEL_TO_LLO_VOL; i++) {
+ data[0] = i;
+ data[1] = cache[i];
+ ret = codec->hw_write(codec->control_data, data, 2);
+ if (ret != 2)
+ dev_err(codec->dev, "Write failed (%d)\n", ret);
+ }
+ for (i = DAC33_LINER_TO_RLO_VOL; i <= DAC33_OSC_TRIM; i++) {
+ data[0] = i;
+ data[1] = cache[i];
+ ret = codec->hw_write(codec->control_data, data, 2);
+ if (ret != 2)
+ dev_err(codec->dev, "Write failed (%d)\n", ret);
+ }
+}
+
+static inline void dac33_soft_power(struct snd_soc_codec *codec, int power)
+{
+ u8 reg;
+
+ reg = dac33_read_reg_cache(codec, DAC33_PWR_CTRL);
+ if (power)
+ reg |= DAC33_PDNALLB;
+ else
+ reg &= ~DAC33_PDNALLB;
+ dac33_write(codec, DAC33_PWR_CTRL, reg);
+}
+
+static void dac33_hard_power(struct snd_soc_codec *codec, int power)
+{
+ struct tlv320dac33_priv *dac33 = codec->private_data;
+
+ mutex_lock(&dac33->mutex);
+ if (power) {
+ if (dac33->power_gpio >= 0) {
+ gpio_set_value(dac33->power_gpio, 1);
+ dac33->chip_power = 1;
+ /* Restore registers */
+ dac33_restore_regs(codec);
+ }
+ dac33_soft_power(codec, 1);
+ } else {
+ dac33_soft_power(codec, 0);
+ if (dac33->power_gpio >= 0) {
+ gpio_set_value(dac33->power_gpio, 0);
+ dac33->chip_power = 0;
+ }
+ }
+ mutex_unlock(&dac33->mutex);
+
+}
+
+static int dac33_get_nsample(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol);
+ struct tlv320dac33_priv *dac33 = codec->private_data;
+
+ ucontrol->value.integer.value[0] = dac33->nsample;
+
+ return 0;
+}
+
+static int dac33_set_nsample(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol);
+ struct tlv320dac33_priv *dac33 = codec->private_data;
+ int ret = 0;
+
+ if (dac33->nsample == ucontrol->value.integer.value[0])
+ return 0;
+
+ if (ucontrol->value.integer.value[0] < dac33->nsample_min ||
+ ucontrol->value.integer.value[0] > dac33->nsample_max)
+ ret = -EINVAL;
+ else
+ dac33->nsample = ucontrol->value.integer.value[0];
+
+ return ret;
+}
+
+static int dac33_get_nsample_switch(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol);
+ struct tlv320dac33_priv *dac33 = codec->private_data;
+
+ ucontrol->value.integer.value[0] = dac33->nsample_switch;
+
+ return 0;
+}
+
+static int dac33_set_nsample_switch(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol);
+ struct tlv320dac33_priv *dac33 = codec->private_data;
+ int ret = 0;
+
+ if (dac33->nsample_switch == ucontrol->value.integer.value[0])
+ return 0;
+ /* Do not allow changes while stream is running*/
+ if (codec->active)
+ return -EPERM;
+
+ if (ucontrol->value.integer.value[0] < 0 ||
+ ucontrol->value.integer.value[0] > 1)
+ ret = -EINVAL;
+ else
+ dac33->nsample_switch = ucontrol->value.integer.value[0];
+
+ return ret;
+}
+
+/*
+ * DACL/R digital volume control:
+ * from 0 dB to -63.5 in 0.5 dB steps
+ * Need to be inverted later on:
+ * 0x00 == 0 dB
+ * 0x7f == -63.5 dB
+ */
+static DECLARE_TLV_DB_SCALE(dac_digivol_tlv, -6350, 50, 0);
+
+static const struct snd_kcontrol_new dac33_snd_controls[] = {
+ SOC_DOUBLE_R_TLV("DAC Digital Playback Volume",
+ DAC33_LDAC_DIG_VOL_CTRL, DAC33_RDAC_DIG_VOL_CTRL,
+ 0, 0x7f, 1, dac_digivol_tlv),
+ SOC_DOUBLE_R("DAC Digital Playback Switch",
+ DAC33_LDAC_DIG_VOL_CTRL, DAC33_RDAC_DIG_VOL_CTRL, 7, 1, 1),
+ SOC_DOUBLE_R("Line to Line Out Volume",
+ DAC33_LINEL_TO_LLO_VOL, DAC33_LINER_TO_RLO_VOL, 0, 127, 1),
+};
+
+static const struct snd_kcontrol_new dac33_nsample_snd_controls[] = {
+ SOC_SINGLE_EXT("nSample", 0, 0, 5900, 0,
+ dac33_get_nsample, dac33_set_nsample),
+ SOC_SINGLE_EXT("nSample Switch", 0, 0, 1, 0,
+ dac33_get_nsample_switch, dac33_set_nsample_switch),
+};
+
+/* Analog bypass */
+static const struct snd_kcontrol_new dac33_dapm_abypassl_control =
+ SOC_DAPM_SINGLE("Switch", DAC33_LINEL_TO_LLO_VOL, 7, 1, 1);
+
+static const struct snd_kcontrol_new dac33_dapm_abypassr_control =
+ SOC_DAPM_SINGLE("Switch", DAC33_LINER_TO_RLO_VOL, 7, 1, 1);
+
+static const struct snd_soc_dapm_widget dac33_dapm_widgets[] = {
+ SND_SOC_DAPM_OUTPUT("LEFT_LO"),
+ SND_SOC_DAPM_OUTPUT("RIGHT_LO"),
+
+ SND_SOC_DAPM_INPUT("LINEL"),
+ SND_SOC_DAPM_INPUT("LINER"),
+
+ SND_SOC_DAPM_DAC("DACL", "Left Playback", DAC33_LDAC_PWR_CTRL, 2, 0),
+ SND_SOC_DAPM_DAC("DACR", "Right Playback", DAC33_RDAC_PWR_CTRL, 2, 0),
+
+ /* Analog bypass */
+ SND_SOC_DAPM_SWITCH("Analog Left Bypass", SND_SOC_NOPM, 0, 0,
+ &dac33_dapm_abypassl_control),
+ SND_SOC_DAPM_SWITCH("Analog Right Bypass", SND_SOC_NOPM, 0, 0,
+ &dac33_dapm_abypassr_control),
+
+ SND_SOC_DAPM_REG(snd_soc_dapm_mixer, "Output Left Amp Power",
+ DAC33_OUT_AMP_PWR_CTRL, 6, 3, 3, 0),
+ SND_SOC_DAPM_REG(snd_soc_dapm_mixer, "Output Right Amp Power",
+ DAC33_OUT_AMP_PWR_CTRL, 4, 3, 3, 0),
+};
+
+static const struct snd_soc_dapm_route audio_map[] = {
+ /* Analog bypass */
+ {"Analog Left Bypass", "Switch", "LINEL"},
+ {"Analog Right Bypass", "Switch", "LINER"},
+
+ {"Output Left Amp Power", NULL, "DACL"},
+ {"Output Right Amp Power", NULL, "DACR"},
+
+ {"Output Left Amp Power", NULL, "Analog Left Bypass"},
+ {"Output Right Amp Power", NULL, "Analog Right Bypass"},
+
+ /* output */
+ {"LEFT_LO", NULL, "Output Left Amp Power"},
+ {"RIGHT_LO", NULL, "Output Right Amp Power"},
+};
+
+static int dac33_add_widgets(struct snd_soc_codec *codec)
+{
+ snd_soc_dapm_new_controls(codec, dac33_dapm_widgets,
+ ARRAY_SIZE(dac33_dapm_widgets));
+
+ /* set up audio path interconnects */
+ snd_soc_dapm_add_routes(codec, audio_map, ARRAY_SIZE(audio_map));
+
+ return 0;
+}
+
+static int dac33_set_bias_level(struct snd_soc_codec *codec,
+ enum snd_soc_bias_level level)
+{
+ switch (level) {
+ case SND_SOC_BIAS_ON:
+ dac33_soft_power(codec, 1);
+ break;
+ case SND_SOC_BIAS_PREPARE:
+ break;
+ case SND_SOC_BIAS_STANDBY:
+ if (codec->bias_level == SND_SOC_BIAS_OFF)
+ dac33_hard_power(codec, 1);
+ dac33_soft_power(codec, 0);
+ break;
+ case SND_SOC_BIAS_OFF:
+ dac33_hard_power(codec, 0);
+ break;
+ }
+ codec->bias_level = level;
+
+ return 0;
+}
+
+static void dac33_work(struct work_struct *work)
+{
+ struct snd_soc_codec *codec;
+ struct tlv320dac33_priv *dac33;
+ u8 reg;
+
+ dac33 = container_of(work, struct tlv320dac33_priv, work);
+ codec = &dac33->codec;
+
+ mutex_lock(&dac33->mutex);
+ switch (dac33->state) {
+ case DAC33_PREFILL:
+ dac33->state = DAC33_PLAYBACK;
+ dac33_write16(codec, DAC33_NSAMPLE_MSB,
+ DAC33_THRREG(dac33->nsample));
+ dac33_write16(codec, DAC33_PREFILL_MSB,
+ DAC33_THRREG(dac33->alarm_threshold));
+ break;
+ case DAC33_PLAYBACK:
+ dac33_write16(codec, DAC33_NSAMPLE_MSB,
+ DAC33_THRREG(dac33->nsample));
+ break;
+ case DAC33_IDLE:
+ break;
+ case DAC33_FLUSH:
+ dac33->state = DAC33_IDLE;
+ /* Mask all interrupts from dac33 */
+ dac33_write(codec, DAC33_FIFO_IRQ_MASK, 0);
+
+ /* flush fifo */
+ reg = dac33_read_reg_cache(codec, DAC33_FIFO_CTRL_A);
+ reg |= DAC33_FIFOFLUSH;
+ dac33_write(codec, DAC33_FIFO_CTRL_A, reg);
+ break;
+ }
+ mutex_unlock(&dac33->mutex);
+}
+
+static irqreturn_t dac33_interrupt_handler(int irq, void *dev)
+{
+ struct snd_soc_codec *codec = dev;
+ struct tlv320dac33_priv *dac33 = codec->private_data;
+
+ queue_work(dac33->dac33_wq, &dac33->work);
+
+ return IRQ_HANDLED;
+}
+
+static void dac33_shutdown(struct snd_pcm_substream *substream,
+ struct snd_soc_dai *dai)
+{
+ struct snd_soc_pcm_runtime *rtd = substream->private_data;
+ struct snd_soc_device *socdev = rtd->socdev;
+ struct snd_soc_codec *codec = socdev->card->codec;
+ struct tlv320dac33_priv *dac33 = codec->private_data;
+ unsigned int pwr_ctrl;
+
+ /* Stop pending workqueue */
+ if (dac33->nsample_switch)
+ cancel_work_sync(&dac33->work);
+
+ mutex_lock(&dac33->mutex);
+ pwr_ctrl = dac33_read_reg_cache(codec, DAC33_PWR_CTRL);
+ pwr_ctrl &= ~(DAC33_OSCPDNB | DAC33_DACRPDNB | DAC33_DACLPDNB);
+ dac33_write(codec, DAC33_PWR_CTRL, pwr_ctrl);
+ mutex_unlock(&dac33->mutex);
+}
+
+static void dac33_oscwait(struct snd_soc_codec *codec)
+{
+ int timeout = 20;
+ u8 reg;
+
+ do {
+ msleep(1);
+ dac33_read(codec, DAC33_INT_OSC_STATUS, &reg);
+ } while (((reg & 0x03) != DAC33_OSCSTATUS_NORMAL) && timeout--);
+ if ((reg & 0x03) != DAC33_OSCSTATUS_NORMAL)
+ dev_err(codec->dev,
+ "internal oscillator calibration failed\n");
+}
+
+static int dac33_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params,
+ struct snd_soc_dai *dai)
+{
+ struct snd_soc_pcm_runtime *rtd = substream->private_data;
+ struct snd_soc_device *socdev = rtd->socdev;
+ struct snd_soc_codec *codec = socdev->card->codec;
+
+ /* Check parameters for validity */
+ switch (params_rate(params)) {
+ case 44100:
+ case 48000:
+ break;
+ default:
+ dev_err(codec->dev, "unsupported rate %d\n",
+ params_rate(params));
+ return -EINVAL;
+ }
+
+ switch (params_format(params)) {
+ case SNDRV_PCM_FORMAT_S16_LE:
+ break;
+ default:
+ dev_err(codec->dev, "unsupported format %d\n",
+ params_format(params));
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+#define CALC_OSCSET(rate, refclk) ( \
+ ((((rate * 10000) / refclk) * 4096) + 5000) / 10000)
+#define CALC_RATIOSET(rate, refclk) ( \
+ ((((refclk * 100000) / rate) * 16384) + 50000) / 100000)
+
+/*
+ * tlv320dac33 is strict on the sequence of the register writes, if the register
+ * writes happens in different order, than dac33 might end up in unknown state.
+ * Use the known, working sequence of register writes to initialize the dac33.
+ */
+static int dac33_prepare_chip(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->card->codec;
+ struct tlv320dac33_priv *dac33 = codec->private_data;
+ unsigned int oscset, ratioset, pwr_ctrl, reg_tmp;
+ u8 aictrl_a, fifoctrl_a;
+
+ switch (substream->runtime->rate) {
+ case 44100:
+ case 48000:
+ oscset = CALC_OSCSET(substream->runtime->rate, dac33->refclk);
+ ratioset = CALC_RATIOSET(substream->runtime->rate,
+ dac33->refclk);
+ break;
+ default:
+ dev_err(codec->dev, "unsupported rate %d\n",
+ substream->runtime->rate);
+ return -EINVAL;
+ }
+
+
+ aictrl_a = dac33_read_reg_cache(codec, DAC33_SER_AUDIOIF_CTRL_A);
+ aictrl_a &= ~(DAC33_NCYCL_MASK | DAC33_WLEN_MASK);
+ fifoctrl_a = dac33_read_reg_cache(codec, DAC33_FIFO_CTRL_A);
+ fifoctrl_a &= ~DAC33_WIDTH;
+ switch (substream->runtime->format) {
+ case SNDRV_PCM_FORMAT_S16_LE:
+ aictrl_a |= (DAC33_NCYCL_16 | DAC33_WLEN_16);
+ fifoctrl_a |= DAC33_WIDTH;
+ break;
+ default:
+ dev_err(codec->dev, "unsupported format %d\n",
+ substream->runtime->format);
+ return -EINVAL;
+ }
+
+ mutex_lock(&dac33->mutex);
+ dac33_soft_power(codec, 1);
+
+ reg_tmp = dac33_read_reg_cache(codec, DAC33_INT_OSC_CTRL);
+ dac33_write(codec, DAC33_INT_OSC_CTRL, reg_tmp);
+
+ /* Write registers 0x08 and 0x09 (MSB, LSB) */
+ dac33_write16(codec, DAC33_INT_OSC_FREQ_RAT_A, oscset);
+
+ /* calib time: 128 is a nice number ;) */
+ dac33_write(codec, DAC33_CALIB_TIME, 128);
+
+ /* adjustment treshold & step */
+ dac33_write(codec, DAC33_INT_OSC_CTRL_B, DAC33_ADJTHRSHLD(2) |
+ DAC33_ADJSTEP(1));
+
+ /* div=4 / gain=1 / div */
+ dac33_write(codec, DAC33_INT_OSC_CTRL_C, DAC33_REFDIV(4));
+
+ pwr_ctrl = dac33_read_reg_cache(codec, DAC33_PWR_CTRL);
+ pwr_ctrl |= DAC33_OSCPDNB | DAC33_DACRPDNB | DAC33_DACLPDNB;
+ dac33_write(codec, DAC33_PWR_CTRL, pwr_ctrl);
+
+ dac33_oscwait(codec);
+
+ if (dac33->nsample_switch) {
+ /* 50-51 : ASRC Control registers */
+ dac33_write(codec, DAC33_ASRC_CTRL_A, (1 << 4)); /* div=2 */
+ dac33_write(codec, DAC33_ASRC_CTRL_B, 1); /* ??? */
+
+ /* Write registers 0x34 and 0x35 (MSB, LSB) */
+ dac33_write16(codec, DAC33_SRC_REF_CLK_RATIO_A, ratioset);
+
+ /* Set interrupts to high active */
+ dac33_write(codec, DAC33_INTP_CTRL_A, DAC33_INTPM_AHIGH);
+
+ dac33_write(codec, DAC33_FIFO_IRQ_MODE_B,
+ DAC33_ATM(DAC33_FIFO_IRQ_MODE_LEVEL));
+ dac33_write(codec, DAC33_FIFO_IRQ_MASK, DAC33_MAT);
+ } else {
+ /* 50-51 : ASRC Control registers */
+ dac33_write(codec, DAC33_ASRC_CTRL_A, DAC33_SRCBYP);
+ dac33_write(codec, DAC33_ASRC_CTRL_B, 0); /* ??? */
+ }
+
+ if (dac33->nsample_switch)
+ fifoctrl_a &= ~DAC33_FBYPAS;
+ else
+ fifoctrl_a |= DAC33_FBYPAS;
+ dac33_write(codec, DAC33_FIFO_CTRL_A, fifoctrl_a);
+
+ dac33_write(codec, DAC33_SER_AUDIOIF_CTRL_A, aictrl_a);
+ reg_tmp = dac33_read_reg_cache(codec, DAC33_SER_AUDIOIF_CTRL_B);
+ if (dac33->nsample_switch)
+ reg_tmp &= ~DAC33_BCLKON;
+ else
+ reg_tmp |= DAC33_BCLKON;
+ dac33_write(codec, DAC33_SER_AUDIOIF_CTRL_B, reg_tmp);
+
+ if (dac33->nsample_switch) {
+ /* 20: BCLK divide ratio */
+ dac33_write(codec, DAC33_SER_AUDIOIF_CTRL_C, 3);
+
+ dac33_write16(codec, DAC33_ATHR_MSB,
+ DAC33_THRREG(dac33->alarm_threshold));
+ } else {
+ dac33_write(codec, DAC33_SER_AUDIOIF_CTRL_C, 32);
+ }
+
+ mutex_unlock(&dac33->mutex);
+
+ return 0;
+}
+
+static void dac33_calculate_times(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->card->codec;
+ struct tlv320dac33_priv *dac33 = codec->private_data;
+ unsigned int nsample_limit;
+
+ /* Number of samples (16bit, stereo) in one period */
+ dac33->nsample_min = snd_pcm_lib_period_bytes(substream) / 4;
+
+ /* Number of samples (16bit, stereo) in ALSA buffer */
+ dac33->nsample_max = snd_pcm_lib_buffer_bytes(substream) / 4;
+ /* Subtract one period from the total */
+ dac33->nsample_max -= dac33->nsample_min;
+
+ /* Number of samples for LATENCY_TIME_MS / 2 */
+ dac33->alarm_threshold = substream->runtime->rate /
+ (1000 / (LATENCY_TIME_MS / 2));
+
+ /* Find and fix up the lowest nsmaple limit */
+ nsample_limit = substream->runtime->rate / (1000 / LATENCY_TIME_MS);
+
+ if (dac33->nsample_min < nsample_limit)
+ dac33->nsample_min = nsample_limit;
+
+ if (dac33->nsample < dac33->nsample_min)
+ dac33->nsample = dac33->nsample_min;
+
+ /*
+ * Find and fix up the highest nsmaple limit
+ * In order to not overflow the DAC33 buffer substract the
+ * alarm_threshold value from the size of the DAC33 buffer
+ */
+ nsample_limit = DAC33_BUFFER_SIZE_SAMPLES - dac33->alarm_threshold;
+
+ if (dac33->nsample_max > nsample_limit)
+ dac33->nsample_max = nsample_limit;
+
+ if (dac33->nsample > dac33->nsample_max)
+ dac33->nsample = dac33->nsample_max;
+}
+
+static int dac33_pcm_prepare(struct snd_pcm_substream *substream,
+ struct snd_soc_dai *dai)
+{
+ dac33_calculate_times(substream);
+ dac33_prepare_chip(substream);
+
+ return 0;
+}
+
+static int dac33_pcm_trigger(struct snd_pcm_substream *substream, int cmd,
+ struct snd_soc_dai *dai)
+{
+ struct snd_soc_pcm_runtime *rtd = substream->private_data;
+ struct snd_soc_device *socdev = rtd->socdev;
+ struct snd_soc_codec *codec = socdev->card->codec;
+ struct tlv320dac33_priv *dac33 = codec->private_data;
+ int ret = 0;
+
+ switch (cmd) {
+ case SNDRV_PCM_TRIGGER_START:
+ case SNDRV_PCM_TRIGGER_RESUME:
+ case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
+ if (dac33->nsample_switch) {
+ dac33->state = DAC33_PREFILL;
+ queue_work(dac33->dac33_wq, &dac33->work);
+ }
+ break;
+ case SNDRV_PCM_TRIGGER_STOP:
+ case SNDRV_PCM_TRIGGER_SUSPEND:
+ case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
+ if (dac33->nsample_switch) {
+ dac33->state = DAC33_FLUSH;
+ queue_work(dac33->dac33_wq, &dac33->work);
+ }
+ break;
+ default:
+ ret = -EINVAL;
+ }
+
+ return ret;
+}
+
+static int dac33_set_dai_sysclk(struct snd_soc_dai *codec_dai,
+ int clk_id, unsigned int freq, int dir)
+{
+ struct snd_soc_codec *codec = codec_dai->codec;
+ struct tlv320dac33_priv *dac33 = codec->private_data;
+ u8 ioc_reg, asrcb_reg;
+
+ ioc_reg = dac33_read_reg_cache(codec, DAC33_INT_OSC_CTRL);
+ asrcb_reg = dac33_read_reg_cache(codec, DAC33_ASRC_CTRL_B);
+ switch (clk_id) {
+ case TLV320DAC33_MCLK:
+ ioc_reg |= DAC33_REFSEL;
+ asrcb_reg |= DAC33_SRCREFSEL;
+ break;
+ case TLV320DAC33_SLEEPCLK:
+ ioc_reg &= ~DAC33_REFSEL;
+ asrcb_reg &= ~DAC33_SRCREFSEL;
+ break;
+ default:
+ dev_err(codec->dev, "Invalid clock ID (%d)\n", clk_id);
+ break;
+ }
+ dac33->refclk = freq;
+
+ dac33_write_reg_cache(codec, DAC33_INT_OSC_CTRL, ioc_reg);
+ dac33_write_reg_cache(codec, DAC33_ASRC_CTRL_B, asrcb_reg);
+
+ return 0;
+}
+
+static int dac33_set_dai_fmt(struct snd_soc_dai *codec_dai,
+ unsigned int fmt)
+{
+ struct snd_soc_codec *codec = codec_dai->codec;
+ u8 aictrl_a, aictrl_b;
+
+ aictrl_a = dac33_read_reg_cache(codec, DAC33_SER_AUDIOIF_CTRL_A);
+ aictrl_b = dac33_read_reg_cache(codec, DAC33_SER_AUDIOIF_CTRL_B);
+ /* set master/slave audio interface */
+ switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) {
+ case SND_SOC_DAIFMT_CBM_CFM:
+ /* Codec Master */
+ aictrl_a |= (DAC33_MSBCLK | DAC33_MSWCLK);
+ break;
+ case SND_SOC_DAIFMT_CBS_CFS:
+ /* Codec Slave */
+ aictrl_a &= ~(DAC33_MSBCLK | DAC33_MSWCLK);
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ aictrl_a &= ~DAC33_AFMT_MASK;
+ switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
+ case SND_SOC_DAIFMT_I2S:
+ aictrl_a |= DAC33_AFMT_I2S;
+ break;
+ case SND_SOC_DAIFMT_DSP_A:
+ aictrl_a |= DAC33_AFMT_DSP;
+ aictrl_b &= ~DAC33_DATA_DELAY_MASK;
+ aictrl_b |= DAC33_DATA_DELAY(1); /* 1 bit delay */
+ break;
+ case SND_SOC_DAIFMT_DSP_B:
+ aictrl_a |= DAC33_AFMT_DSP;
+ aictrl_b &= ~DAC33_DATA_DELAY_MASK; /* No delay */
+ break;
+ case SND_SOC_DAIFMT_RIGHT_J:
+ aictrl_a |= DAC33_AFMT_RIGHT_J;
+ break;
+ case SND_SOC_DAIFMT_LEFT_J:
+ aictrl_a |= DAC33_AFMT_LEFT_J;
+ break;
+ default:
+ dev_err(codec->dev, "Unsupported format (%u)\n",
+ fmt & SND_SOC_DAIFMT_FORMAT_MASK);
+ return -EINVAL;
+ }
+
+ dac33_write_reg_cache(codec, DAC33_SER_AUDIOIF_CTRL_A, aictrl_a);
+ dac33_write_reg_cache(codec, DAC33_SER_AUDIOIF_CTRL_B, aictrl_b);
+
+ return 0;
+}
+
+static void dac33_init_chip(struct snd_soc_codec *codec)
+{
+ /* 44-46: DAC Control Registers */
+ /* A : DAC sample rate Fsref/1.5 */
+ dac33_write(codec, DAC33_DAC_CTRL_A, DAC33_DACRATE(1));
+ /* B : DAC src=normal, not muted */
+ dac33_write(codec, DAC33_DAC_CTRL_B, DAC33_DACSRCR_RIGHT |
+ DAC33_DACSRCL_LEFT);
+ /* C : (defaults) */
+ dac33_write(codec, DAC33_DAC_CTRL_C, 0x00);
+
+ /* 64-65 : L&R DAC power control
+ Line In -> OUT 1V/V Gain, DAC -> OUT 4V/V Gain*/
+ dac33_write(codec, DAC33_LDAC_PWR_CTRL, DAC33_LROUT_GAIN(2));
+ dac33_write(codec, DAC33_RDAC_PWR_CTRL, DAC33_LROUT_GAIN(2));
+
+ /* 73 : volume soft stepping control,
+ clock source = internal osc (?) */
+ dac33_write(codec, DAC33_ANA_VOL_SOFT_STEP_CTRL, DAC33_VOLCLKEN);
+
+ /* 66 : LOP/LOM Modes */
+ dac33_write(codec, DAC33_OUT_AMP_CM_CTRL, 0xff);
+
+ /* 68 : LOM inverted from LOP */
+ dac33_write(codec, DAC33_OUT_AMP_CTRL, (3<<2));
+
+ dac33_write(codec, DAC33_PWR_CTRL, DAC33_PDNALLB);
+}
+
+static int dac33_soc_probe(struct platform_device *pdev)
+{
+ struct snd_soc_device *socdev = platform_get_drvdata(pdev);
+ struct snd_soc_codec *codec;
+ struct tlv320dac33_priv *dac33;
+ int ret = 0;
+
+ BUG_ON(!tlv320dac33_codec);
+
+ codec = tlv320dac33_codec;
+ socdev->card->codec = codec;
+ dac33 = codec->private_data;
+
+ /* Power up the codec */
+ dac33_hard_power(codec, 1);
+ /* Set default configuration */
+ dac33_init_chip(codec);
+
+ /* register pcms */
+ ret = snd_soc_new_pcms(socdev, SNDRV_DEFAULT_IDX1, SNDRV_DEFAULT_STR1);
+ if (ret < 0) {
+ dev_err(codec->dev, "failed to create pcms\n");
+ goto pcm_err;
+ }
+
+ snd_soc_add_controls(codec, dac33_snd_controls,
+ ARRAY_SIZE(dac33_snd_controls));
+ /* Only add the nSample controls, if we have valid IRQ number */
+ if (dac33->irq >= 0)
+ snd_soc_add_controls(codec, dac33_nsample_snd_controls,
+ ARRAY_SIZE(dac33_nsample_snd_controls));
+
+ dac33_add_widgets(codec);
+
+ /* power on device */
+ dac33_set_bias_level(codec, SND_SOC_BIAS_STANDBY);
+
+ return 0;
+
+pcm_err:
+ dac33_hard_power(codec, 0);
+ return ret;
+}
+
+static int dac33_soc_remove(struct platform_device *pdev)
+{
+ struct snd_soc_device *socdev = platform_get_drvdata(pdev);
+ struct snd_soc_codec *codec = socdev->card->codec;
+
+ dac33_set_bias_level(codec, SND_SOC_BIAS_OFF);
+
+ snd_soc_free_pcms(socdev);
+ snd_soc_dapm_free(socdev);
+
+ return 0;
+}
+
+static int dac33_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->card->codec;
+
+ dac33_set_bias_level(codec, SND_SOC_BIAS_OFF);
+
+ return 0;
+}
+
+static int dac33_soc_resume(struct platform_device *pdev)
+{
+ struct snd_soc_device *socdev = platform_get_drvdata(pdev);
+ struct snd_soc_codec *codec = socdev->card->codec;
+
+ dac33_set_bias_level(codec, SND_SOC_BIAS_STANDBY);
+ dac33_set_bias_level(codec, codec->suspend_bias_level);
+
+ return 0;
+}
+
+struct snd_soc_codec_device soc_codec_dev_tlv320dac33 = {
+ .probe = dac33_soc_probe,
+ .remove = dac33_soc_remove,
+ .suspend = dac33_soc_suspend,
+ .resume = dac33_soc_resume,
+};
+EXPORT_SYMBOL_GPL(soc_codec_dev_tlv320dac33);
+
+#define DAC33_RATES (SNDRV_PCM_RATE_44100 | \
+ SNDRV_PCM_RATE_48000)
+#define DAC33_FORMATS SNDRV_PCM_FMTBIT_S16_LE
+
+static struct snd_soc_dai_ops dac33_dai_ops = {
+ .shutdown = dac33_shutdown,
+ .hw_params = dac33_hw_params,
+ .prepare = dac33_pcm_prepare,
+ .trigger = dac33_pcm_trigger,
+ .set_sysclk = dac33_set_dai_sysclk,
+ .set_fmt = dac33_set_dai_fmt,
+};
+
+struct snd_soc_dai dac33_dai = {
+ .name = "tlv320dac33",
+ .playback = {
+ .stream_name = "Playback",
+ .channels_min = 2,
+ .channels_max = 2,
+ .rates = DAC33_RATES,
+ .formats = DAC33_FORMATS,},
+ .ops = &dac33_dai_ops,
+};
+EXPORT_SYMBOL_GPL(dac33_dai);
+
+static int dac33_i2c_probe(struct i2c_client *client,
+ const struct i2c_device_id *id)
+{
+ struct tlv320dac33_platform_data *pdata;
+ struct tlv320dac33_priv *dac33;
+ struct snd_soc_codec *codec;
+ int ret = 0;
+
+ if (client->dev.platform_data == NULL) {
+ dev_err(&client->dev, "Platform data not set\n");
+ return -ENODEV;
+ }
+ pdata = client->dev.platform_data;
+
+ dac33 = kzalloc(sizeof(struct tlv320dac33_priv), GFP_KERNEL);
+ if (dac33 == NULL)
+ return -ENOMEM;
+
+ codec = &dac33->codec;
+ codec->private_data = dac33;
+ codec->control_data = client;
+
+ mutex_init(&codec->mutex);
+ mutex_init(&dac33->mutex);
+ INIT_LIST_HEAD(&codec->dapm_widgets);
+ INIT_LIST_HEAD(&codec->dapm_paths);
+
+ codec->name = "tlv320dac33";
+ codec->owner = THIS_MODULE;
+ codec->read = dac33_read_reg_cache;
+ codec->write = dac33_write_locked;
+ codec->hw_write = (hw_write_t) i2c_master_send;
+ codec->bias_level = SND_SOC_BIAS_OFF;
+ codec->set_bias_level = dac33_set_bias_level;
+ codec->dai = &dac33_dai;
+ codec->num_dai = 1;
+ codec->reg_cache_size = ARRAY_SIZE(dac33_reg);
+ codec->reg_cache = kmemdup(dac33_reg, ARRAY_SIZE(dac33_reg),
+ GFP_KERNEL);
+ if (codec->reg_cache == NULL) {
+ ret = -ENOMEM;
+ goto error_reg;
+ }
+
+ i2c_set_clientdata(client, dac33);
+
+ dac33->power_gpio = pdata->power_gpio;
+ dac33->irq = client->irq;
+ dac33->nsample = NSAMPLE_MAX;
+ /* Disable FIFO use by default */
+ dac33->nsample_switch = 0;
+
+ tlv320dac33_codec = codec;
+
+ codec->dev = &client->dev;
+ dac33_dai.dev = codec->dev;
+
+ /* Check if the reset GPIO number is valid and request it */
+ if (dac33->power_gpio >= 0) {
+ ret = gpio_request(dac33->power_gpio, "tlv320dac33 reset");
+ if (ret < 0) {
+ dev_err(codec->dev,
+ "Failed to request reset GPIO (%d)\n",
+ dac33->power_gpio);
+ snd_soc_unregister_dai(&dac33_dai);
+ snd_soc_unregister_codec(codec);
+ goto error_gpio;
+ }
+ gpio_direction_output(dac33->power_gpio, 0);
+ } else {
+ dac33->chip_power = 1;
+ }
+
+ /* Check if the IRQ number is valid and request it */
+ if (dac33->irq >= 0) {
+ ret = request_irq(dac33->irq, dac33_interrupt_handler,
+ IRQF_TRIGGER_RISING | IRQF_DISABLED,
+ codec->name, codec);
+ if (ret < 0) {
+ dev_err(codec->dev, "Could not request IRQ%d (%d)\n",
+ dac33->irq, ret);
+ dac33->irq = -1;
+ }
+ if (dac33->irq != -1) {
+ /* Setup work queue */
+ dac33->dac33_wq =
+ create_singlethread_workqueue("tlv320dac33");
+ if (dac33->dac33_wq == NULL) {
+ free_irq(dac33->irq, &dac33->codec);
+ ret = -ENOMEM;
+ goto error_wq;
+ }
+
+ INIT_WORK(&dac33->work, dac33_work);
+ }
+ }
+
+ ret = snd_soc_register_codec(codec);
+ if (ret != 0) {
+ dev_err(codec->dev, "Failed to register codec: %d\n", ret);
+ goto error_codec;
+ }
+
+ ret = snd_soc_register_dai(&dac33_dai);
+ if (ret != 0) {
+ dev_err(codec->dev, "Failed to register DAI: %d\n", ret);
+ snd_soc_unregister_codec(codec);
+ goto error_codec;
+ }
+
+ /* Shut down the codec for now */
+ dac33_hard_power(codec, 0);
+
+ return ret;
+
+error_codec:
+ if (dac33->irq >= 0) {
+ free_irq(dac33->irq, &dac33->codec);
+ destroy_workqueue(dac33->dac33_wq);
+ }
+error_wq:
+ if (dac33->power_gpio >= 0)
+ gpio_free(dac33->power_gpio);
+error_gpio:
+ kfree(codec->reg_cache);
+error_reg:
+ tlv320dac33_codec = NULL;
+ kfree(dac33);
+
+ return ret;
+}
+
+static int dac33_i2c_remove(struct i2c_client *client)
+{
+ struct tlv320dac33_priv *dac33;
+
+ dac33 = i2c_get_clientdata(client);
+ dac33_hard_power(&dac33->codec, 0);
+
+ if (dac33->power_gpio >= 0)
+ gpio_free(dac33->power_gpio);
+ if (dac33->irq >= 0)
+ free_irq(dac33->irq, &dac33->codec);
+
+ destroy_workqueue(dac33->dac33_wq);
+ snd_soc_unregister_dai(&dac33_dai);
+ snd_soc_unregister_codec(&dac33->codec);
+ kfree(dac33->codec.reg_cache);
+ kfree(dac33);
+ tlv320dac33_codec = NULL;
+
+ return 0;
+}
+
+static const struct i2c_device_id tlv320dac33_i2c_id[] = {
+ {
+ .name = "tlv320dac33",
+ .driver_data = 0,
+ },
+ { },
+};
+
+static struct i2c_driver tlv320dac33_i2c_driver = {
+ .driver = {
+ .name = "tlv320dac33",
+ .owner = THIS_MODULE,
+ },
+ .probe = dac33_i2c_probe,
+ .remove = __devexit_p(dac33_i2c_remove),
+ .id_table = tlv320dac33_i2c_id,
+};
+
+static int __init dac33_module_init(void)
+{
+ int r;
+ r = i2c_add_driver(&tlv320dac33_i2c_driver);
+ if (r < 0) {
+ printk(KERN_ERR "DAC33: driver registration failed\n");
+ return r;
+ }
+ return 0;
+}
+module_init(dac33_module_init);
+
+static void __exit dac33_module_exit(void)
+{
+ i2c_del_driver(&tlv320dac33_i2c_driver);
+}
+module_exit(dac33_module_exit);
+
+
+MODULE_DESCRIPTION("ASoC TLV320DAC33 codec driver");
+MODULE_AUTHOR("Peter Ujfalusi <peter.ujfalusi@nokia.com>");
+MODULE_LICENSE("GPL");
diff --git a/sound/soc/codecs/tlv320dac33.h b/sound/soc/codecs/tlv320dac33.h
new file mode 100644
index 000000000000..eb8ae07f0bd2
--- /dev/null
+++ b/sound/soc/codecs/tlv320dac33.h
@@ -0,0 +1,267 @@
+/*
+ * ALSA SoC Texas Instruments TLV320DAC33 codec driver
+ *
+ * Author: Peter Ujfalusi <peter.ujfalusi@nokia.com>
+ *
+ * Copyright: (C) 2009 Nokia Corporation
+ *
+ * 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.
+ *
+ * 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., 51 Franklin St, Fifth Floor, Boston, MA
+ * 02110-1301 USA
+ *
+ */
+
+#ifndef __TLV320DAC33_H
+#define __TLV320DAC33_H
+
+#define DAC33_PAGE_SELECT 0x00
+#define DAC33_PWR_CTRL 0x01
+#define DAC33_PLL_CTRL_A 0x02
+#define DAC33_PLL_CTRL_B 0x03
+#define DAC33_PLL_CTRL_C 0x04
+#define DAC33_PLL_CTRL_D 0x05
+#define DAC33_PLL_CTRL_E 0x06
+#define DAC33_INT_OSC_CTRL 0x07
+#define DAC33_INT_OSC_FREQ_RAT_A 0x08
+#define DAC33_INT_OSC_FREQ_RAT_B 0x09
+#define DAC33_INT_OSC_DAC_RATIO_SET 0x0A
+#define DAC33_CALIB_TIME 0x0B
+#define DAC33_INT_OSC_CTRL_B 0x0C
+#define DAC33_INT_OSC_CTRL_C 0x0D
+#define DAC33_INT_OSC_STATUS 0x0E
+#define DAC33_INT_OSC_DAC_RATIO_READ 0x0F
+#define DAC33_INT_OSC_FREQ_RAT_READ_A 0x10
+#define DAC33_INT_OSC_FREQ_RAT_READ_B 0x11
+#define DAC33_SER_AUDIOIF_CTRL_A 0x12
+#define DAC33_SER_AUDIOIF_CTRL_B 0x13
+#define DAC33_SER_AUDIOIF_CTRL_C 0x14
+#define DAC33_FIFO_CTRL_A 0x15
+#define DAC33_UTHR_MSB 0x16
+#define DAC33_UTHR_LSB 0x17
+#define DAC33_ATHR_MSB 0x18
+#define DAC33_ATHR_LSB 0x19
+#define DAC33_LTHR_MSB 0x1A
+#define DAC33_LTHR_LSB 0x1B
+#define DAC33_PREFILL_MSB 0x1C
+#define DAC33_PREFILL_LSB 0x1D
+#define DAC33_NSAMPLE_MSB 0x1E
+#define DAC33_NSAMPLE_LSB 0x1F
+#define DAC33_FIFO_WPTR_MSB 0x20
+#define DAC33_FIFO_WPTR_LSB 0x21
+#define DAC33_FIFO_RPTR_MSB 0x22
+#define DAC33_FIFO_RPTR_LSB 0x23
+#define DAC33_FIFO_DEPTH_MSB 0x24
+#define DAC33_FIFO_DEPTH_LSB 0x25
+#define DAC33_SAMPLES_REMAINING_MSB 0x26
+#define DAC33_SAMPLES_REMAINING_LSB 0x27
+#define DAC33_FIFO_IRQ_FLAG 0x28
+#define DAC33_FIFO_IRQ_MASK 0x29
+#define DAC33_FIFO_IRQ_MODE_A 0x2A
+#define DAC33_FIFO_IRQ_MODE_B 0x2B
+#define DAC33_DAC_CTRL_A 0x2C
+#define DAC33_DAC_CTRL_B 0x2D
+#define DAC33_DAC_CTRL_C 0x2E
+#define DAC33_LDAC_DIG_VOL_CTRL 0x2F
+#define DAC33_RDAC_DIG_VOL_CTRL 0x30
+#define DAC33_DAC_STATUS_FLAGS 0x31
+#define DAC33_ASRC_CTRL_A 0x32
+#define DAC33_ASRC_CTRL_B 0x33
+#define DAC33_SRC_REF_CLK_RATIO_A 0x34
+#define DAC33_SRC_REF_CLK_RATIO_B 0x35
+#define DAC33_SRC_EST_REF_CLK_RATIO_A 0x36
+#define DAC33_SRC_EST_REF_CLK_RATIO_B 0x37
+#define DAC33_INTP_CTRL_A 0x38
+#define DAC33_INTP_CTRL_B 0x39
+/* Registers 0x3A - 0x3F Reserved */
+#define DAC33_LDAC_PWR_CTRL 0x40
+#define DAC33_RDAC_PWR_CTRL 0x41
+#define DAC33_OUT_AMP_CM_CTRL 0x42
+#define DAC33_OUT_AMP_PWR_CTRL 0x43
+#define DAC33_OUT_AMP_CTRL 0x44
+#define DAC33_LINEL_TO_LLO_VOL 0x45
+/* Registers 0x45 - 0x47 Reserved */
+#define DAC33_LINER_TO_RLO_VOL 0x48
+#define DAC33_ANA_VOL_SOFT_STEP_CTRL 0x49
+#define DAC33_OSC_TRIM 0x4A
+/* Registers 0x4B - 0x7C Reserved */
+#define DAC33_DEVICE_ID_MSB 0x7D
+#define DAC33_DEVICE_ID_LSB 0x7E
+#define DAC33_DEVICE_REV_ID 0x7F
+
+#define DAC33_CACHEREGNUM 128
+
+/* Bit definitions */
+
+/* DAC33_PWR_CTRL (0x01) */
+#define DAC33_DACRPDNB (0x01 << 0)
+#define DAC33_DACLPDNB (0x01 << 1)
+#define DAC33_OSCPDNB (0x01 << 2)
+#define DAC33_PLLPDNB (0x01 << 3)
+#define DAC33_PDNALLB (0x01 << 4)
+#define DAC33_SOFT_RESET (0x01 << 7)
+
+/* DAC33_INT_OSC_CTRL (0x07) */
+#define DAC33_REFSEL (0x01 << 1)
+
+/* DAC33_INT_OSC_CTRL_B (0x0C) */
+#define DAC33_ADJSTEP(x) (x << 0)
+#define DAC33_ADJTHRSHLD(x) (x << 4)
+
+/* DAC33_INT_OSC_CTRL_C (0x0D) */
+#define DAC33_REFDIV(x) (x << 4)
+
+/* DAC33_INT_OSC_STATUS (0x0E) */
+#define DAC33_OSCSTATUS_IDLE_CALIB (0x00)
+#define DAC33_OSCSTATUS_NORMAL (0x01)
+#define DAC33_OSCSTATUS_ADJUSTMENT (0x03)
+#define DAC33_OSCSTATUS_NOT_USED (0x02)
+
+/* DAC33_SER_AUDIOIF_CTRL_A (0x12) */
+#define DAC33_MSWCLK (0x01 << 0)
+#define DAC33_MSBCLK (0x01 << 1)
+#define DAC33_AFMT_MASK (0x03 << 2)
+#define DAC33_AFMT_I2S (0x00 << 2)
+#define DAC33_AFMT_DSP (0x01 << 2)
+#define DAC33_AFMT_RIGHT_J (0x02 << 2)
+#define DAC33_AFMT_LEFT_J (0x03 << 2)
+#define DAC33_WLEN_MASK (0x03 << 4)
+#define DAC33_WLEN_16 (0x00 << 4)
+#define DAC33_WLEN_20 (0x01 << 4)
+#define DAC33_WLEN_24 (0x02 << 4)
+#define DAC33_WLEN_32 (0x03 << 4)
+#define DAC33_NCYCL_MASK (0x03 << 6)
+#define DAC33_NCYCL_16 (0x00 << 6)
+#define DAC33_NCYCL_20 (0x01 << 6)
+#define DAC33_NCYCL_24 (0x02 << 6)
+#define DAC33_NCYCL_32 (0x03 << 6)
+
+/* DAC33_SER_AUDIOIF_CTRL_B (0x13) */
+#define DAC33_DATA_DELAY_MASK (0x03 << 2)
+#define DAC33_DATA_DELAY(x) (x << 2)
+#define DAC33_BCLKON (0x01 << 5)
+
+/* DAC33_FIFO_CTRL_A (0x15) */
+#define DAC33_WIDTH (0x01 << 0)
+#define DAC33_FBYPAS (0x01 << 1)
+#define DAC33_FAUTO (0x01 << 2)
+#define DAC33_FIFOFLUSH (0x01 << 3)
+
+/*
+ * UTHR, ATHR, LTHR, PREFILL, NSAMPLE (0x16 - 0x1F)
+ * 13-bit values
+*/
+#define DAC33_THRREG(x) (((x) & 0x1FFF) << 3)
+
+/* DAC33_FIFO_IRQ_MASK (0x29) */
+#define DAC33_MNS (0x01 << 0)
+#define DAC33_MPS (0x01 << 1)
+#define DAC33_MAT (0x01 << 2)
+#define DAC33_MLT (0x01 << 3)
+#define DAC33_MUT (0x01 << 4)
+#define DAC33_MUF (0x01 << 5)
+#define DAC33_MOF (0x01 << 6)
+
+#define DAC33_FIFO_IRQ_MODE_MASK (0x03)
+#define DAC33_FIFO_IRQ_MODE_RISING (0x00)
+#define DAC33_FIFO_IRQ_MODE_FALLING (0x01)
+#define DAC33_FIFO_IRQ_MODE_LEVEL (0x02)
+#define DAC33_FIFO_IRQ_MODE_EDGE (0x03)
+
+/* DAC33_FIFO_IRQ_MODE_A (0x2A) */
+#define DAC33_UTM(x) (x << 0)
+#define DAC33_UFM(x) (x << 2)
+#define DAC33_OFM(x) (x << 4)
+
+/* DAC33_FIFO_IRQ_MODE_B (0x2B) */
+#define DAC33_NSM(x) (x << 0)
+#define DAC33_PSM(x) (x << 2)
+#define DAC33_ATM(x) (x << 4)
+#define DAC33_LTM(x) (x << 6)
+
+/* DAC33_DAC_CTRL_A (0x2C) */
+#define DAC33_DACRATE(x) (x << 0)
+#define DAC33_DACDUAL (0x01 << 4)
+#define DAC33_DACLKSEL_MASK (0x03 << 5)
+#define DAC33_DACLKSEL_INTSOC (0x00 << 5)
+#define DAC33_DACLKSEL_PLL (0x01 << 5)
+#define DAC33_DACLKSEL_MCLK (0x02 << 5)
+#define DAC33_DACLKSEL_BCLK (0x03 << 5)
+
+/* DAC33_DAC_CTRL_B (0x2D) */
+#define DAC33_DACSRCR_MASK (0x03 << 0)
+#define DAC33_DACSRCR_MUTE (0x00 << 0)
+#define DAC33_DACSRCR_RIGHT (0x01 << 0)
+#define DAC33_DACSRCR_LEFT (0x02 << 0)
+#define DAC33_DACSRCR_MONOMIX (0x03 << 0)
+#define DAC33_DACSRCL_MASK (0x03 << 2)
+#define DAC33_DACSRCL_MUTE (0x00 << 2)
+#define DAC33_DACSRCL_LEFT (0x01 << 2)
+#define DAC33_DACSRCL_RIGHT (0x02 << 2)
+#define DAC33_DACSRCL_MONOMIX (0x03 << 2)
+#define DAC33_DVOLSTEP_MASK (0x03 << 4)
+#define DAC33_DVOLSTEP_SS_PERFS (0x00 << 4)
+#define DAC33_DVOLSTEP_SS_PER2FS (0x01 << 4)
+#define DAC33_DVOLSTEP_SS_DISABLED (0x02 << 4)
+#define DAC33_DVOLCTRL_MASK (0x03 << 6)
+#define DAC33_DVOLCTRL_LR_INDEPENDENT1 (0x00 << 6)
+#define DAC33_DVOLCTRL_LR_RIGHT_CONTROL (0x01 << 6)
+#define DAC33_DVOLCTRL_LR_LEFT_CONTROL (0x02 << 6)
+#define DAC33_DVOLCTRL_LR_INDEPENDENT2 (0x03 << 6)
+
+/* DAC33_DAC_CTRL_C (0x2E) */
+#define DAC33_DEEMENR (0x01 << 0)
+#define DAC33_EFFENR (0x01 << 1)
+#define DAC33_DEEMENL (0x01 << 2)
+#define DAC33_EFFENL (0x01 << 3)
+#define DAC33_EN3D (0x01 << 4)
+#define DAC33_RESYNMUTE (0x01 << 5)
+#define DAC33_RESYNEN (0x01 << 6)
+
+/* DAC33_ASRC_CTRL_A (0x32) */
+#define DAC33_SRCBYP (0x01 << 0)
+#define DAC33_SRCLKSEL_MASK (0x03 << 1)
+#define DAC33_SRCLKSEL_INTSOC (0x00 << 1)
+#define DAC33_SRCLKSEL_PLL (0x01 << 1)
+#define DAC33_SRCLKSEL_MCLK (0x02 << 1)
+#define DAC33_SRCLKSEL_BCLK (0x03 << 1)
+#define DAC33_SRCLKDIV(x) (x << 3)
+
+/* DAC33_ASRC_CTRL_B (0x33) */
+#define DAC33_SRCSETUP(x) (x << 0)
+#define DAC33_SRCREFSEL (0x01 << 4)
+#define DAC33_SRCREFDIV(x) (x << 5)
+
+/* DAC33_INTP_CTRL_A (0x38) */
+#define DAC33_INTPSEL (0x01 << 0)
+#define DAC33_INTPM_MASK (0x03 << 1)
+#define DAC33_INTPM_ALOW_OPENDRAIN (0x00 << 1)
+#define DAC33_INTPM_ALOW (0x01 << 1)
+#define DAC33_INTPM_AHIGH (0x02 << 1)
+
+/* DAC33_LDAC_PWR_CTRL (0x40) */
+/* DAC33_RDAC_PWR_CTRL (0x41) */
+#define DAC33_DACLRNUM (0x01 << 2)
+#define DAC33_LROUT_GAIN(x) (x << 0)
+
+/* DAC33_ANA_VOL_SOFT_STEP_CTRL (0x49) */
+#define DAC33_VOLCLKSEL (0x01 << 0)
+#define DAC33_VOLCLKEN (0x01 << 1)
+#define DAC33_VOLBYPASS (0x01 << 2)
+
+#define TLV320DAC33_MCLK 0
+#define TLV320DAC33_SLEEPCLK 1
+
+extern struct snd_soc_dai dac33_dai;
+extern struct snd_soc_codec_device soc_codec_dev_tlv320dac33;
+
+#endif /* __TLV320DAC33_H */
diff --git a/sound/soc/codecs/tpa6130a2.c b/sound/soc/codecs/tpa6130a2.c
new file mode 100644
index 000000000000..6b650c1aa3d1
--- /dev/null
+++ b/sound/soc/codecs/tpa6130a2.c
@@ -0,0 +1,463 @@
+/*
+ * ALSA SoC Texas Instruments TPA6130A2 headset stereo amplifier driver
+ *
+ * Copyright (C) Nokia Corporation
+ *
+ * Author: Peter Ujfalusi <peter.ujfalusi@nokia.com>
+ *
+ * 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.
+ *
+ * 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., 51 Franklin St, Fifth Floor, Boston, MA
+ * 02110-1301 USA
+ */
+
+#include <linux/module.h>
+#include <linux/errno.h>
+#include <linux/device.h>
+#include <linux/i2c.h>
+#include <linux/gpio.h>
+#include <sound/tpa6130a2-plat.h>
+#include <sound/soc.h>
+#include <sound/soc-dapm.h>
+#include <sound/tlv.h>
+
+#include "tpa6130a2.h"
+
+static struct i2c_client *tpa6130a2_client;
+
+/* This struct is used to save the context */
+struct tpa6130a2_data {
+ struct mutex mutex;
+ unsigned char regs[TPA6130A2_CACHEREGNUM];
+ int power_gpio;
+ unsigned char power_state;
+};
+
+static int tpa6130a2_i2c_read(int reg)
+{
+ struct tpa6130a2_data *data;
+ int val;
+
+ BUG_ON(tpa6130a2_client == NULL);
+ data = i2c_get_clientdata(tpa6130a2_client);
+
+ /* If powered off, return the cached value */
+ if (data->power_state) {
+ val = i2c_smbus_read_byte_data(tpa6130a2_client, reg);
+ if (val < 0)
+ dev_err(&tpa6130a2_client->dev, "Read failed\n");
+ else
+ data->regs[reg] = val;
+ } else {
+ val = data->regs[reg];
+ }
+
+ return val;
+}
+
+static int tpa6130a2_i2c_write(int reg, u8 value)
+{
+ struct tpa6130a2_data *data;
+ int val = 0;
+
+ BUG_ON(tpa6130a2_client == NULL);
+ data = i2c_get_clientdata(tpa6130a2_client);
+
+ if (data->power_state) {
+ val = i2c_smbus_write_byte_data(tpa6130a2_client, reg, value);
+ if (val < 0)
+ dev_err(&tpa6130a2_client->dev, "Write failed\n");
+ }
+
+ /* Either powered on or off, we save the context */
+ data->regs[reg] = value;
+
+ return val;
+}
+
+static u8 tpa6130a2_read(int reg)
+{
+ struct tpa6130a2_data *data;
+
+ BUG_ON(tpa6130a2_client == NULL);
+ data = i2c_get_clientdata(tpa6130a2_client);
+
+ return data->regs[reg];
+}
+
+static void tpa6130a2_initialize(void)
+{
+ struct tpa6130a2_data *data;
+ int i;
+
+ BUG_ON(tpa6130a2_client == NULL);
+ data = i2c_get_clientdata(tpa6130a2_client);
+
+ for (i = 1; i < TPA6130A2_REG_VERSION; i++)
+ tpa6130a2_i2c_write(i, data->regs[i]);
+}
+
+static void tpa6130a2_power(int power)
+{
+ struct tpa6130a2_data *data;
+ u8 val;
+
+ BUG_ON(tpa6130a2_client == NULL);
+ data = i2c_get_clientdata(tpa6130a2_client);
+
+ mutex_lock(&data->mutex);
+ if (power) {
+ /* Power on */
+ if (data->power_gpio >= 0) {
+ gpio_set_value(data->power_gpio, 1);
+ data->power_state = 1;
+ tpa6130a2_initialize();
+ }
+ /* Clear SWS */
+ val = tpa6130a2_read(TPA6130A2_REG_CONTROL);
+ val &= ~TPA6130A2_SWS;
+ tpa6130a2_i2c_write(TPA6130A2_REG_CONTROL, val);
+ } else {
+ /* set SWS */
+ val = tpa6130a2_read(TPA6130A2_REG_CONTROL);
+ val |= TPA6130A2_SWS;
+ tpa6130a2_i2c_write(TPA6130A2_REG_CONTROL, val);
+ /* Power off */
+ if (data->power_gpio >= 0) {
+ gpio_set_value(data->power_gpio, 0);
+ data->power_state = 0;
+ }
+ }
+ mutex_unlock(&data->mutex);
+}
+
+static int tpa6130a2_get_reg(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct soc_mixer_control *mc =
+ (struct soc_mixer_control *)kcontrol->private_value;
+ struct tpa6130a2_data *data;
+ unsigned int reg = mc->reg;
+ unsigned int shift = mc->shift;
+ unsigned int mask = mc->max;
+ unsigned int invert = mc->invert;
+
+ BUG_ON(tpa6130a2_client == NULL);
+ data = i2c_get_clientdata(tpa6130a2_client);
+
+ mutex_lock(&data->mutex);
+
+ ucontrol->value.integer.value[0] =
+ (tpa6130a2_read(reg) >> shift) & mask;
+
+ if (invert)
+ ucontrol->value.integer.value[0] =
+ mask - ucontrol->value.integer.value[0];
+
+ mutex_unlock(&data->mutex);
+ return 0;
+}
+
+static int tpa6130a2_set_reg(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct soc_mixer_control *mc =
+ (struct soc_mixer_control *)kcontrol->private_value;
+ struct tpa6130a2_data *data;
+ unsigned int reg = mc->reg;
+ unsigned int shift = mc->shift;
+ unsigned int mask = mc->max;
+ unsigned int invert = mc->invert;
+ unsigned int val = (ucontrol->value.integer.value[0] & mask);
+ unsigned int val_reg;
+
+ BUG_ON(tpa6130a2_client == NULL);
+ data = i2c_get_clientdata(tpa6130a2_client);
+
+ if (invert)
+ val = mask - val;
+
+ mutex_lock(&data->mutex);
+
+ val_reg = tpa6130a2_read(reg);
+ if (((val_reg >> shift) & mask) == val) {
+ mutex_unlock(&data->mutex);
+ return 0;
+ }
+
+ val_reg &= ~(mask << shift);
+ val_reg |= val << shift;
+ tpa6130a2_i2c_write(reg, val_reg);
+
+ mutex_unlock(&data->mutex);
+
+ return 1;
+}
+
+/*
+ * TPA6130 volume. From -59.5 to 4 dB with increasing step size when going
+ * down in gain.
+ */
+static const unsigned int tpa6130_tlv[] = {
+ TLV_DB_RANGE_HEAD(10),
+ 0, 1, TLV_DB_SCALE_ITEM(-5950, 600, 0),
+ 2, 3, TLV_DB_SCALE_ITEM(-5000, 250, 0),
+ 4, 5, TLV_DB_SCALE_ITEM(-4550, 160, 0),
+ 6, 7, TLV_DB_SCALE_ITEM(-4140, 190, 0),
+ 8, 9, TLV_DB_SCALE_ITEM(-3650, 120, 0),
+ 10, 11, TLV_DB_SCALE_ITEM(-3330, 160, 0),
+ 12, 13, TLV_DB_SCALE_ITEM(-3040, 180, 0),
+ 14, 20, TLV_DB_SCALE_ITEM(-2710, 110, 0),
+ 21, 37, TLV_DB_SCALE_ITEM(-1960, 74, 0),
+ 38, 63, TLV_DB_SCALE_ITEM(-720, 45, 0),
+};
+
+static const struct snd_kcontrol_new tpa6130a2_controls[] = {
+ SOC_SINGLE_EXT_TLV("TPA6130A2 Headphone Playback Volume",
+ TPA6130A2_REG_VOL_MUTE, 0, 0x3f, 0,
+ tpa6130a2_get_reg, tpa6130a2_set_reg,
+ tpa6130_tlv),
+};
+
+/*
+ * Enable or disable channel (left or right)
+ * The bit number for mute and amplifier are the same per channel:
+ * bit 6: Right channel
+ * bit 7: Left channel
+ * in both registers.
+ */
+static void tpa6130a2_channel_enable(u8 channel, int enable)
+{
+ struct tpa6130a2_data *data;
+ u8 val;
+
+ BUG_ON(tpa6130a2_client == NULL);
+ data = i2c_get_clientdata(tpa6130a2_client);
+
+ if (enable) {
+ /* Enable channel */
+ /* Enable amplifier */
+ val = tpa6130a2_read(TPA6130A2_REG_CONTROL);
+ val |= channel;
+ tpa6130a2_i2c_write(TPA6130A2_REG_CONTROL, val);
+
+ /* Unmute channel */
+ val = tpa6130a2_read(TPA6130A2_REG_VOL_MUTE);
+ val &= ~channel;
+ tpa6130a2_i2c_write(TPA6130A2_REG_VOL_MUTE, val);
+ } else {
+ /* Disable channel */
+ /* Mute channel */
+ val = tpa6130a2_read(TPA6130A2_REG_VOL_MUTE);
+ val |= channel;
+ tpa6130a2_i2c_write(TPA6130A2_REG_VOL_MUTE, val);
+
+ /* Disable amplifier */
+ val = tpa6130a2_read(TPA6130A2_REG_CONTROL);
+ val &= ~channel;
+ tpa6130a2_i2c_write(TPA6130A2_REG_CONTROL, val);
+ }
+}
+
+static int tpa6130a2_left_event(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol, int event)
+{
+ switch (event) {
+ case SND_SOC_DAPM_POST_PMU:
+ tpa6130a2_channel_enable(TPA6130A2_HP_EN_L, 1);
+ break;
+ case SND_SOC_DAPM_POST_PMD:
+ tpa6130a2_channel_enable(TPA6130A2_HP_EN_L, 0);
+ break;
+ }
+ return 0;
+}
+
+static int tpa6130a2_right_event(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol, int event)
+{
+ switch (event) {
+ case SND_SOC_DAPM_POST_PMU:
+ tpa6130a2_channel_enable(TPA6130A2_HP_EN_R, 1);
+ break;
+ case SND_SOC_DAPM_POST_PMD:
+ tpa6130a2_channel_enable(TPA6130A2_HP_EN_R, 0);
+ break;
+ }
+ return 0;
+}
+
+static int tpa6130a2_supply_event(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol, int event)
+{
+ switch (event) {
+ case SND_SOC_DAPM_POST_PMU:
+ tpa6130a2_power(1);
+ break;
+ case SND_SOC_DAPM_POST_PMD:
+ tpa6130a2_power(0);
+ break;
+ }
+ return 0;
+}
+
+static const struct snd_soc_dapm_widget tpa6130a2_dapm_widgets[] = {
+ SND_SOC_DAPM_PGA_E("TPA6130A2 Left", SND_SOC_NOPM,
+ 0, 0, NULL, 0, tpa6130a2_left_event,
+ SND_SOC_DAPM_POST_PMU|SND_SOC_DAPM_POST_PMD),
+ SND_SOC_DAPM_PGA_E("TPA6130A2 Right", SND_SOC_NOPM,
+ 0, 0, NULL, 0, tpa6130a2_right_event,
+ SND_SOC_DAPM_POST_PMU|SND_SOC_DAPM_POST_PMD),
+ SND_SOC_DAPM_SUPPLY("TPA6130A2 Enable", SND_SOC_NOPM,
+ 0, 0, tpa6130a2_supply_event,
+ SND_SOC_DAPM_POST_PMU|SND_SOC_DAPM_POST_PMD),
+ /* Outputs */
+ SND_SOC_DAPM_HP("TPA6130A2 Headphone Left", NULL),
+ SND_SOC_DAPM_HP("TPA6130A2 Headphone Right", NULL),
+};
+
+static const struct snd_soc_dapm_route audio_map[] = {
+ {"TPA6130A2 Headphone Left", NULL, "TPA6130A2 Left"},
+ {"TPA6130A2 Headphone Right", NULL, "TPA6130A2 Right"},
+
+ {"TPA6130A2 Headphone Left", NULL, "TPA6130A2 Enable"},
+ {"TPA6130A2 Headphone Right", NULL, "TPA6130A2 Enable"},
+};
+
+int tpa6130a2_add_controls(struct snd_soc_codec *codec)
+{
+ snd_soc_dapm_new_controls(codec, tpa6130a2_dapm_widgets,
+ ARRAY_SIZE(tpa6130a2_dapm_widgets));
+
+ snd_soc_dapm_add_routes(codec, audio_map, ARRAY_SIZE(audio_map));
+
+ return snd_soc_add_controls(codec, tpa6130a2_controls,
+ ARRAY_SIZE(tpa6130a2_controls));
+
+}
+EXPORT_SYMBOL_GPL(tpa6130a2_add_controls);
+
+static int tpa6130a2_probe(struct i2c_client *client,
+ const struct i2c_device_id *id)
+{
+ struct device *dev;
+ struct tpa6130a2_data *data;
+ struct tpa6130a2_platform_data *pdata;
+ int ret;
+
+ dev = &client->dev;
+
+ if (client->dev.platform_data == NULL) {
+ dev_err(dev, "Platform data not set\n");
+ dump_stack();
+ return -ENODEV;
+ }
+
+ data = kzalloc(sizeof(*data), GFP_KERNEL);
+ if (data == NULL) {
+ dev_err(dev, "Can not allocate memory\n");
+ return -ENOMEM;
+ }
+
+ tpa6130a2_client = client;
+
+ i2c_set_clientdata(tpa6130a2_client, data);
+
+ pdata = client->dev.platform_data;
+ data->power_gpio = pdata->power_gpio;
+
+ mutex_init(&data->mutex);
+
+ /* Set default register values */
+ data->regs[TPA6130A2_REG_CONTROL] = TPA6130A2_SWS;
+ data->regs[TPA6130A2_REG_VOL_MUTE] = TPA6130A2_MUTE_R |
+ TPA6130A2_MUTE_L;
+
+ if (data->power_gpio >= 0) {
+ ret = gpio_request(data->power_gpio, "tpa6130a2 enable");
+ if (ret < 0) {
+ dev_err(dev, "Failed to request power GPIO (%d)\n",
+ data->power_gpio);
+ goto fail;
+ }
+ gpio_direction_output(data->power_gpio, 0);
+ } else {
+ data->power_state = 1;
+ tpa6130a2_initialize();
+ }
+
+ tpa6130a2_power(1);
+
+ /* Read version */
+ ret = tpa6130a2_i2c_read(TPA6130A2_REG_VERSION) &
+ TPA6130A2_VERSION_MASK;
+ if ((ret != 1) && (ret != 2))
+ dev_warn(dev, "UNTESTED version detected (%d)\n", ret);
+
+ /* Disable the chip */
+ tpa6130a2_power(0);
+
+ return 0;
+fail:
+ kfree(data);
+ i2c_set_clientdata(tpa6130a2_client, NULL);
+ tpa6130a2_client = NULL;
+
+ return ret;
+}
+
+static int tpa6130a2_remove(struct i2c_client *client)
+{
+ struct tpa6130a2_data *data = i2c_get_clientdata(client);
+
+ tpa6130a2_power(0);
+
+ if (data->power_gpio >= 0)
+ gpio_free(data->power_gpio);
+ kfree(data);
+ tpa6130a2_client = NULL;
+
+ return 0;
+}
+
+static const struct i2c_device_id tpa6130a2_id[] = {
+ { "tpa6130a2", 0 },
+ { }
+};
+MODULE_DEVICE_TABLE(i2c, tpa6130a2_id);
+
+static struct i2c_driver tpa6130a2_i2c_driver = {
+ .driver = {
+ .name = "tpa6130a2",
+ .owner = THIS_MODULE,
+ },
+ .probe = tpa6130a2_probe,
+ .remove = __devexit_p(tpa6130a2_remove),
+ .id_table = tpa6130a2_id,
+};
+
+static int __init tpa6130a2_init(void)
+{
+ return i2c_add_driver(&tpa6130a2_i2c_driver);
+}
+
+static void __exit tpa6130a2_exit(void)
+{
+ i2c_del_driver(&tpa6130a2_i2c_driver);
+}
+
+MODULE_AUTHOR("Peter Ujfalusi");
+MODULE_DESCRIPTION("TPA6130A2 Headphone amplifier driver");
+MODULE_LICENSE("GPL");
+
+module_init(tpa6130a2_init);
+module_exit(tpa6130a2_exit);
diff --git a/sound/soc/codecs/tpa6130a2.h b/sound/soc/codecs/tpa6130a2.h
new file mode 100644
index 000000000000..57e867fd86d1
--- /dev/null
+++ b/sound/soc/codecs/tpa6130a2.h
@@ -0,0 +1,61 @@
+/*
+ * ALSA SoC TPA6130A2 amplifier driver
+ *
+ * Copyright (C) Nokia Corporation
+ *
+ * Author: Peter Ujfalusi <peter.ujfalusi@nokia.com>
+ *
+ * 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.
+ *
+ * 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., 51 Franklin St, Fifth Floor, Boston, MA
+ * 02110-1301 USA
+ *
+ */
+
+#ifndef __TPA6130A2_H__
+#define __TPA6130A2_H__
+
+/* Register addresses */
+#define TPA6130A2_REG_CONTROL 0x01
+#define TPA6130A2_REG_VOL_MUTE 0x02
+#define TPA6130A2_REG_OUT_IMPEDANCE 0x03
+#define TPA6130A2_REG_VERSION 0x04
+
+#define TPA6130A2_CACHEREGNUM (TPA6130A2_REG_VERSION + 1)
+
+/* Register bits */
+/* TPA6130A2_REG_CONTROL (0x01) */
+#define TPA6130A2_SWS (0x01 << 0)
+#define TPA6130A2_TERMAL (0x01 << 1)
+#define TPA6130A2_MODE(x) (x << 4)
+#define TPA6130A2_MODE_STEREO (0x00)
+#define TPA6130A2_MODE_DUAL_MONO (0x01)
+#define TPA6130A2_MODE_BRIDGE (0x02)
+#define TPA6130A2_MODE_MASK (0x03)
+#define TPA6130A2_HP_EN_R (0x01 << 6)
+#define TPA6130A2_HP_EN_L (0x01 << 7)
+
+/* TPA6130A2_REG_VOL_MUTE (0x02) */
+#define TPA6130A2_VOLUME(x) ((x & 0x3f) << 0)
+#define TPA6130A2_MUTE_R (0x01 << 6)
+#define TPA6130A2_MUTE_L (0x01 << 7)
+
+/* TPA6130A2_REG_OUT_IMPEDANCE (0x03) */
+#define TPA6130A2_HIZ_R (0x01 << 0)
+#define TPA6130A2_HIZ_L (0x01 << 1)
+
+/* TPA6130A2_REG_VERSION (0x04) */
+#define TPA6130A2_VERSION_MASK (0x0f)
+
+extern int tpa6130a2_add_controls(struct snd_soc_codec *codec);
+
+#endif /* __TPA6130A2_H__ */
diff --git a/sound/soc/codecs/twl4030.c b/sound/soc/codecs/twl4030.c
index 4df7c6c61c76..5f1681f6ca76 100644
--- a/sound/soc/codecs/twl4030.c
+++ b/sound/soc/codecs/twl4030.c
@@ -120,9 +120,10 @@ static const u8 twl4030_reg[TWL4030_CACHEREGNUM] = {
/* codec private data */
struct twl4030_priv {
- unsigned int bypass_state;
+ struct snd_soc_codec codec;
+
unsigned int codec_powered;
- unsigned int codec_muted;
+ unsigned int apll_enabled;
struct snd_pcm_substream *master_substream;
struct snd_pcm_substream *slave_substream;
@@ -183,19 +184,20 @@ static int twl4030_write(struct snd_soc_codec *codec,
static void twl4030_codec_enable(struct snd_soc_codec *codec, int enable)
{
struct twl4030_priv *twl4030 = codec->private_data;
- u8 mode;
+ int mode;
if (enable == twl4030->codec_powered)
return;
- mode = twl4030_read_reg_cache(codec, TWL4030_REG_CODEC_MODE);
if (enable)
- mode |= TWL4030_CODECPDZ;
+ mode = twl4030_codec_enable_resource(TWL4030_CODEC_RES_POWER);
else
- mode &= ~TWL4030_CODECPDZ;
+ mode = twl4030_codec_disable_resource(TWL4030_CODEC_RES_POWER);
- twl4030_write(codec, TWL4030_REG_CODEC_MODE, mode);
- twl4030->codec_powered = enable;
+ if (mode >= 0) {
+ twl4030_write_reg_cache(codec, TWL4030_REG_CODEC_MODE, mode);
+ twl4030->codec_powered = enable;
+ }
/* REVISIT: this delay is present in TI sample drivers */
/* but there seems to be no TRM requirement for it */
@@ -212,31 +214,30 @@ static void twl4030_init_chip(struct snd_soc_codec *codec)
/* set all audio section registers to reasonable defaults */
for (i = TWL4030_REG_OPTION; i <= TWL4030_REG_MISC_SET_2; i++)
- twl4030_write(codec, i, cache[i]);
+ if (i != TWL4030_REG_APLL_CTL)
+ twl4030_write(codec, i, cache[i]);
}
-static void twl4030_codec_mute(struct snd_soc_codec *codec, int mute)
+static void twl4030_apll_enable(struct snd_soc_codec *codec, int enable)
{
struct twl4030_priv *twl4030 = codec->private_data;
- u8 reg_val;
+ int status;
- if (mute == twl4030->codec_muted)
+ if (enable == twl4030->apll_enabled)
return;
- if (mute) {
- /* Disable PLL */
- reg_val = twl4030_read_reg_cache(codec, TWL4030_REG_APLL_CTL);
- reg_val &= ~TWL4030_APLL_EN;
- twl4030_write(codec, TWL4030_REG_APLL_CTL, reg_val);
- } else {
+ if (enable)
/* Enable PLL */
- reg_val = twl4030_read_reg_cache(codec, TWL4030_REG_APLL_CTL);
- reg_val |= TWL4030_APLL_EN;
- twl4030_write(codec, TWL4030_REG_APLL_CTL, reg_val);
- }
+ status = twl4030_codec_enable_resource(TWL4030_CODEC_RES_APLL);
+ else
+ /* Disable PLL */
+ status = twl4030_codec_disable_resource(TWL4030_CODEC_RES_APLL);
+
+ if (status >= 0)
+ twl4030_write_reg_cache(codec, TWL4030_REG_APLL_CTL, status);
- twl4030->codec_muted = mute;
+ twl4030->apll_enabled = enable;
}
static void twl4030_power_up(struct snd_soc_codec *codec)
@@ -613,6 +614,27 @@ static int handsfreerpga_event(struct snd_soc_dapm_widget *w,
return 0;
}
+static int vibramux_event(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol, int event)
+{
+ twl4030_write(w->codec, TWL4030_REG_VIBRA_SET, 0xff);
+ return 0;
+}
+
+static int apll_event(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol, int event)
+{
+ switch (event) {
+ case SND_SOC_DAPM_PRE_PMU:
+ twl4030_apll_enable(w->codec, 1);
+ break;
+ case SND_SOC_DAPM_POST_PMD:
+ twl4030_apll_enable(w->codec, 0);
+ break;
+ }
+ return 0;
+}
+
static void headset_ramp(struct snd_soc_codec *codec, int ramp)
{
struct snd_soc_device *socdev = codec->socdev;
@@ -724,67 +746,6 @@ static int headsetrpga_event(struct snd_soc_dapm_widget *w,
return 0;
}
-static int bypass_event(struct snd_soc_dapm_widget *w,
- struct snd_kcontrol *kcontrol, int event)
-{
- struct soc_mixer_control *m =
- (struct soc_mixer_control *)w->kcontrols->private_value;
- struct twl4030_priv *twl4030 = w->codec->private_data;
- unsigned char reg, misc;
-
- reg = twl4030_read_reg_cache(w->codec, m->reg);
-
- /*
- * bypass_state[0:3] - analog HiFi bypass
- * bypass_state[4] - analog voice bypass
- * bypass_state[5] - digital voice bypass
- * bypass_state[6:7] - digital HiFi bypass
- */
- if (m->reg == TWL4030_REG_VSTPGA) {
- /* Voice digital bypass */
- if (reg)
- twl4030->bypass_state |= (1 << 5);
- else
- twl4030->bypass_state &= ~(1 << 5);
- } else if (m->reg <= TWL4030_REG_ARXR2_APGA_CTL) {
- /* Analog bypass */
- if (reg & (1 << m->shift))
- twl4030->bypass_state |=
- (1 << (m->reg - TWL4030_REG_ARXL1_APGA_CTL));
- else
- twl4030->bypass_state &=
- ~(1 << (m->reg - TWL4030_REG_ARXL1_APGA_CTL));
- } else if (m->reg == TWL4030_REG_VDL_APGA_CTL) {
- /* Analog voice bypass */
- if (reg & (1 << m->shift))
- twl4030->bypass_state |= (1 << 4);
- else
- twl4030->bypass_state &= ~(1 << 4);
- } else {
- /* Digital bypass */
- if (reg & (0x7 << m->shift))
- twl4030->bypass_state |= (1 << (m->shift ? 7 : 6));
- else
- twl4030->bypass_state &= ~(1 << (m->shift ? 7 : 6));
- }
-
- /* Enable master analog loopback mode if any analog switch is enabled*/
- misc = twl4030_read_reg_cache(w->codec, TWL4030_REG_MISC_SET_1);
- if (twl4030->bypass_state & 0x1F)
- misc |= TWL4030_FMLOOP_EN;
- else
- misc &= ~TWL4030_FMLOOP_EN;
- twl4030_write(w->codec, TWL4030_REG_MISC_SET_1, misc);
-
- if (w->codec->bias_level == SND_SOC_BIAS_STANDBY) {
- if (twl4030->bypass_state)
- twl4030_codec_mute(w->codec, 0);
- else
- twl4030_codec_mute(w->codec, 1);
- }
- return 0;
-}
-
/*
* Some of the gain controls in TWL (mostly those which are associated with
* the outputs) are implemented in an interesting way:
@@ -1192,32 +1153,28 @@ static const struct snd_soc_dapm_widget twl4030_dapm_widgets[] = {
SND_SOC_NOPM, 0, 0),
/* Analog bypasses */
- SND_SOC_DAPM_SWITCH_E("Right1 Analog Loopback", SND_SOC_NOPM, 0, 0,
- &twl4030_dapm_abypassr1_control, bypass_event,
- SND_SOC_DAPM_POST_REG),
- SND_SOC_DAPM_SWITCH_E("Left1 Analog Loopback", SND_SOC_NOPM, 0, 0,
- &twl4030_dapm_abypassl1_control,
- bypass_event, SND_SOC_DAPM_POST_REG),
- SND_SOC_DAPM_SWITCH_E("Right2 Analog Loopback", SND_SOC_NOPM, 0, 0,
- &twl4030_dapm_abypassr2_control,
- bypass_event, SND_SOC_DAPM_POST_REG),
- SND_SOC_DAPM_SWITCH_E("Left2 Analog Loopback", SND_SOC_NOPM, 0, 0,
- &twl4030_dapm_abypassl2_control,
- bypass_event, SND_SOC_DAPM_POST_REG),
- SND_SOC_DAPM_SWITCH_E("Voice Analog Loopback", SND_SOC_NOPM, 0, 0,
- &twl4030_dapm_abypassv_control,
- bypass_event, SND_SOC_DAPM_POST_REG),
+ SND_SOC_DAPM_SWITCH("Right1 Analog Loopback", SND_SOC_NOPM, 0, 0,
+ &twl4030_dapm_abypassr1_control),
+ SND_SOC_DAPM_SWITCH("Left1 Analog Loopback", SND_SOC_NOPM, 0, 0,
+ &twl4030_dapm_abypassl1_control),
+ SND_SOC_DAPM_SWITCH("Right2 Analog Loopback", SND_SOC_NOPM, 0, 0,
+ &twl4030_dapm_abypassr2_control),
+ SND_SOC_DAPM_SWITCH("Left2 Analog Loopback", SND_SOC_NOPM, 0, 0,
+ &twl4030_dapm_abypassl2_control),
+ SND_SOC_DAPM_SWITCH("Voice Analog Loopback", SND_SOC_NOPM, 0, 0,
+ &twl4030_dapm_abypassv_control),
+
+ /* Master analog loopback switch */
+ SND_SOC_DAPM_SUPPLY("FM Loop Enable", TWL4030_REG_MISC_SET_1, 5, 0,
+ NULL, 0),
/* Digital bypasses */
- SND_SOC_DAPM_SWITCH_E("Left Digital Loopback", SND_SOC_NOPM, 0, 0,
- &twl4030_dapm_dbypassl_control, bypass_event,
- SND_SOC_DAPM_POST_REG),
- SND_SOC_DAPM_SWITCH_E("Right Digital Loopback", SND_SOC_NOPM, 0, 0,
- &twl4030_dapm_dbypassr_control, bypass_event,
- SND_SOC_DAPM_POST_REG),
- SND_SOC_DAPM_SWITCH_E("Voice Digital Loopback", SND_SOC_NOPM, 0, 0,
- &twl4030_dapm_dbypassv_control, bypass_event,
- SND_SOC_DAPM_POST_REG),
+ SND_SOC_DAPM_SWITCH("Left Digital Loopback", SND_SOC_NOPM, 0, 0,
+ &twl4030_dapm_dbypassl_control),
+ SND_SOC_DAPM_SWITCH("Right Digital Loopback", SND_SOC_NOPM, 0, 0,
+ &twl4030_dapm_dbypassr_control),
+ SND_SOC_DAPM_SWITCH("Voice Digital Loopback", SND_SOC_NOPM, 0, 0,
+ &twl4030_dapm_dbypassv_control),
/* Digital mixers, power control for the physical DACs */
SND_SOC_DAPM_MIXER("Digital R1 Playback Mixer",
@@ -1243,6 +1200,9 @@ static const struct snd_soc_dapm_widget twl4030_dapm_widgets[] = {
SND_SOC_DAPM_MIXER("Analog Voice Playback Mixer",
TWL4030_REG_VDL_APGA_CTL, 0, 0, NULL, 0),
+ SND_SOC_DAPM_SUPPLY("APLL Enable", SND_SOC_NOPM, 0, 0, apll_event,
+ SND_SOC_DAPM_PRE_PMU|SND_SOC_DAPM_POST_PMD),
+
/* Output MIXER controls */
/* Earpiece */
SND_SOC_DAPM_MIXER("Earpiece Mixer", SND_SOC_NOPM, 0, 0,
@@ -1308,8 +1268,9 @@ static const struct snd_soc_dapm_widget twl4030_dapm_widgets[] = {
0, 0, NULL, 0, handsfreerpga_event,
SND_SOC_DAPM_POST_PMU|SND_SOC_DAPM_POST_PMD),
/* Vibra */
- SND_SOC_DAPM_MUX("Vibra Mux", TWL4030_REG_VIBRA_CTL, 0, 0,
- &twl4030_dapm_vibra_control),
+ SND_SOC_DAPM_MUX_E("Vibra Mux", TWL4030_REG_VIBRA_CTL, 0, 0,
+ &twl4030_dapm_vibra_control, vibramux_event,
+ SND_SOC_DAPM_PRE_PMU),
SND_SOC_DAPM_MUX("Vibra Route", SND_SOC_NOPM, 0, 0,
&twl4030_dapm_vibrapath_control),
@@ -1369,6 +1330,13 @@ static const struct snd_soc_dapm_route intercon[] = {
{"Digital R2 Playback Mixer", NULL, "DAC Right2"},
{"Digital Voice Playback Mixer", NULL, "DAC Voice"},
+ /* Supply for the digital part (APLL) */
+ {"Digital R1 Playback Mixer", NULL, "APLL Enable"},
+ {"Digital L1 Playback Mixer", NULL, "APLL Enable"},
+ {"Digital R2 Playback Mixer", NULL, "APLL Enable"},
+ {"Digital L2 Playback Mixer", NULL, "APLL Enable"},
+ {"Digital Voice Playback Mixer", NULL, "APLL Enable"},
+
{"Analog L1 Playback Mixer", NULL, "Digital L1 Playback Mixer"},
{"Analog R1 Playback Mixer", NULL, "Digital R1 Playback Mixer"},
{"Analog L2 Playback Mixer", NULL, "Digital L2 Playback Mixer"},
@@ -1482,6 +1450,11 @@ static const struct snd_soc_dapm_route intercon[] = {
{"ADC Virtual Left2", NULL, "TX2 Capture Route"},
{"ADC Virtual Right2", NULL, "TX2 Capture Route"},
+ {"ADC Virtual Left1", NULL, "APLL Enable"},
+ {"ADC Virtual Right1", NULL, "APLL Enable"},
+ {"ADC Virtual Left2", NULL, "APLL Enable"},
+ {"ADC Virtual Right2", NULL, "APLL Enable"},
+
/* Analog bypass routes */
{"Right1 Analog Loopback", "Switch", "Analog Right"},
{"Left1 Analog Loopback", "Switch", "Analog Left"},
@@ -1489,6 +1462,13 @@ static const struct snd_soc_dapm_route intercon[] = {
{"Left2 Analog Loopback", "Switch", "Analog Left"},
{"Voice Analog Loopback", "Switch", "Analog Left"},
+ /* Supply for the Analog loopbacks */
+ {"Right1 Analog Loopback", NULL, "FM Loop Enable"},
+ {"Left1 Analog Loopback", NULL, "FM Loop Enable"},
+ {"Right2 Analog Loopback", NULL, "FM Loop Enable"},
+ {"Left2 Analog Loopback", NULL, "FM Loop Enable"},
+ {"Voice Analog Loopback", NULL, "FM Loop Enable"},
+
{"Analog R1 Playback Mixer", NULL, "Right1 Analog Loopback"},
{"Analog L1 Playback Mixer", NULL, "Left1 Analog Loopback"},
{"Analog R2 Playback Mixer", NULL, "Right2 Analog Loopback"},
@@ -1513,32 +1493,20 @@ static int twl4030_add_widgets(struct snd_soc_codec *codec)
snd_soc_dapm_add_routes(codec, intercon, ARRAY_SIZE(intercon));
- snd_soc_dapm_new_widgets(codec);
return 0;
}
static int twl4030_set_bias_level(struct snd_soc_codec *codec,
enum snd_soc_bias_level level)
{
- struct twl4030_priv *twl4030 = codec->private_data;
-
switch (level) {
case SND_SOC_BIAS_ON:
- twl4030_codec_mute(codec, 0);
break;
case SND_SOC_BIAS_PREPARE:
- twl4030_power_up(codec);
- if (twl4030->bypass_state)
- twl4030_codec_mute(codec, 0);
- else
- twl4030_codec_mute(codec, 1);
break;
case SND_SOC_BIAS_STANDBY:
- twl4030_power_up(codec);
- if (twl4030->bypass_state)
- twl4030_codec_mute(codec, 0);
- else
- twl4030_codec_mute(codec, 1);
+ if (codec->bias_level == SND_SOC_BIAS_OFF)
+ twl4030_power_up(codec);
break;
case SND_SOC_BIAS_OFF:
twl4030_power_down(codec);
@@ -1785,29 +1753,23 @@ static int twl4030_set_dai_sysclk(struct snd_soc_dai *codec_dai,
{
struct snd_soc_codec *codec = codec_dai->codec;
struct twl4030_priv *twl4030 = codec->private_data;
- u8 infreq;
switch (freq) {
case 19200000:
- infreq = TWL4030_APLL_INFREQ_19200KHZ;
- twl4030->sysclk = 19200;
- break;
case 26000000:
- infreq = TWL4030_APLL_INFREQ_26000KHZ;
- twl4030->sysclk = 26000;
- break;
case 38400000:
- infreq = TWL4030_APLL_INFREQ_38400KHZ;
- twl4030->sysclk = 38400;
break;
default:
- printk(KERN_ERR "TWL4030 set sysclk: unknown rate %d\n",
- freq);
+ dev_err(codec->dev, "Unsupported APLL mclk: %u\n", freq);
return -EINVAL;
}
- infreq |= TWL4030_APLL_EN;
- twl4030_write(codec, TWL4030_REG_APLL_CTL, infreq);
+ if ((freq / 1000) != twl4030->sysclk) {
+ dev_err(codec->dev,
+ "Mismatch in APLL mclk: %u (configured: %u)\n",
+ freq, twl4030->sysclk * 1000);
+ return -EINVAL;
+ }
return 0;
}
@@ -1905,18 +1867,16 @@ static int twl4030_voice_startup(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->card->codec;
- u8 infreq;
+ struct twl4030_priv *twl4030 = codec->private_data;
u8 mode;
/* If the system master clock is not 26MHz, the voice PCM interface is
* not avilable.
*/
- infreq = twl4030_read_reg_cache(codec, TWL4030_REG_APLL_CTL)
- & TWL4030_APLL_INFREQ;
-
- if (infreq != TWL4030_APLL_INFREQ_26000KHZ) {
- printk(KERN_ERR "TWL4030 voice startup: "
- "MCLK is not 26MHz, call set_sysclk() on init\n");
+ if (twl4030->sysclk != 26000) {
+ dev_err(codec->dev, "The board is configured for %u Hz, while"
+ "the Voice interface needs 26MHz APLL mclk\n",
+ twl4030->sysclk * 1000);
return -EINVAL;
}
@@ -1989,21 +1949,19 @@ static int twl4030_voice_set_dai_sysclk(struct snd_soc_dai *codec_dai,
int clk_id, unsigned int freq, int dir)
{
struct snd_soc_codec *codec = codec_dai->codec;
- u8 infreq;
+ struct twl4030_priv *twl4030 = codec->private_data;
- switch (freq) {
- case 26000000:
- infreq = TWL4030_APLL_INFREQ_26000KHZ;
- break;
- default:
- printk(KERN_ERR "TWL4030 voice set sysclk: unknown rate %d\n",
- freq);
+ if (freq != 26000000) {
+ dev_err(codec->dev, "Unsupported APLL mclk: %u, the Voice"
+ "interface needs 26MHz APLL mclk\n", freq);
+ return -EINVAL;
+ }
+ if ((freq / 1000) != twl4030->sysclk) {
+ dev_err(codec->dev,
+ "Mismatch in APLL mclk: %u (configured: %u)\n",
+ freq, twl4030->sysclk * 1000);
return -EINVAL;
}
-
- infreq |= TWL4030_APLL_EN;
- twl4030_write(codec, TWL4030_REG_APLL_CTL, infreq);
-
return 0;
}
@@ -2121,7 +2079,7 @@ struct snd_soc_dai twl4030_dai[] = {
};
EXPORT_SYMBOL_GPL(twl4030_dai);
-static int twl4030_suspend(struct platform_device *pdev, pm_message_t state)
+static int twl4030_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->card->codec;
@@ -2131,7 +2089,7 @@ static int twl4030_suspend(struct platform_device *pdev, pm_message_t state)
return 0;
}
-static int twl4030_resume(struct platform_device *pdev)
+static int twl4030_soc_resume(struct platform_device *pdev)
{
struct snd_soc_device *socdev = platform_get_drvdata(pdev);
struct snd_soc_codec *codec = socdev->card->codec;
@@ -2141,147 +2099,181 @@ static int twl4030_resume(struct platform_device *pdev)
return 0;
}
-/*
- * initialize the driver
- * register the mixer and dsp interfaces with the kernel
- */
+static struct snd_soc_codec *twl4030_codec;
-static int twl4030_init(struct snd_soc_device *socdev)
+static int twl4030_soc_probe(struct platform_device *pdev)
{
- struct snd_soc_codec *codec = socdev->card->codec;
+ struct snd_soc_device *socdev = platform_get_drvdata(pdev);
struct twl4030_setup_data *setup = socdev->codec_data;
- struct twl4030_priv *twl4030 = codec->private_data;
- int ret = 0;
+ struct snd_soc_codec *codec;
+ struct twl4030_priv *twl4030;
+ int ret;
- printk(KERN_INFO "TWL4030 Audio Codec init \n");
+ BUG_ON(!twl4030_codec);
- codec->name = "twl4030";
- codec->owner = THIS_MODULE;
- codec->read = twl4030_read_reg_cache;
- codec->write = twl4030_write;
- codec->set_bias_level = twl4030_set_bias_level;
- codec->dai = twl4030_dai;
- codec->num_dai = ARRAY_SIZE(twl4030_dai),
- codec->reg_cache_size = sizeof(twl4030_reg);
- codec->reg_cache = kmemdup(twl4030_reg, sizeof(twl4030_reg),
- GFP_KERNEL);
- if (codec->reg_cache == NULL)
- return -ENOMEM;
+ codec = twl4030_codec;
+ twl4030 = codec->private_data;
+ socdev->card->codec = codec;
/* Configuration for headset ramp delay from setup data */
if (setup) {
unsigned char hs_pop;
- if (setup->sysclk)
- twl4030->sysclk = setup->sysclk;
- else
- twl4030->sysclk = 26000;
+ if (setup->sysclk != twl4030->sysclk)
+ dev_warn(&pdev->dev,
+ "Mismatch in APLL mclk: %u (configured: %u)\n",
+ setup->sysclk, twl4030->sysclk);
hs_pop = twl4030_read_reg_cache(codec, TWL4030_REG_HS_POPN_SET);
hs_pop &= ~TWL4030_RAMP_DELAY;
hs_pop |= (setup->ramp_delay_value << 2);
twl4030_write_reg_cache(codec, TWL4030_REG_HS_POPN_SET, hs_pop);
- } else {
- twl4030->sysclk = 26000;
}
/* register pcms */
ret = snd_soc_new_pcms(socdev, SNDRV_DEFAULT_IDX1, SNDRV_DEFAULT_STR1);
if (ret < 0) {
- printk(KERN_ERR "twl4030: failed to create pcms\n");
- goto pcm_err;
+ dev_err(&pdev->dev, "failed to create pcms\n");
+ return ret;
}
- twl4030_init_chip(codec);
-
- /* power on device */
- twl4030_set_bias_level(codec, SND_SOC_BIAS_STANDBY);
-
snd_soc_add_controls(codec, twl4030_snd_controls,
ARRAY_SIZE(twl4030_snd_controls));
twl4030_add_widgets(codec);
- ret = snd_soc_init_card(socdev);
- if (ret < 0) {
- printk(KERN_ERR "twl4030: failed to register card\n");
- goto card_err;
- }
+ return 0;
+}
- return ret;
+static int twl4030_soc_remove(struct platform_device *pdev)
+{
+ struct snd_soc_device *socdev = platform_get_drvdata(pdev);
+ struct snd_soc_codec *codec = socdev->card->codec;
-card_err:
+ twl4030_set_bias_level(codec, SND_SOC_BIAS_OFF);
snd_soc_free_pcms(socdev);
snd_soc_dapm_free(socdev);
-pcm_err:
- kfree(codec->reg_cache);
- return ret;
-}
+ kfree(codec->private_data);
+ kfree(codec);
-static struct snd_soc_device *twl4030_socdev;
+ return 0;
+}
-static int twl4030_probe(struct platform_device *pdev)
+static int __devinit twl4030_codec_probe(struct platform_device *pdev)
{
- struct snd_soc_device *socdev = platform_get_drvdata(pdev);
+ struct twl4030_codec_audio_data *pdata = pdev->dev.platform_data;
struct snd_soc_codec *codec;
struct twl4030_priv *twl4030;
+ int ret;
- codec = kzalloc(sizeof(struct snd_soc_codec), GFP_KERNEL);
- if (codec == NULL)
- return -ENOMEM;
+ if (!pdata) {
+ dev_err(&pdev->dev, "platform_data is missing\n");
+ return -EINVAL;
+ }
twl4030 = kzalloc(sizeof(struct twl4030_priv), GFP_KERNEL);
if (twl4030 == NULL) {
- kfree(codec);
+ dev_err(&pdev->dev, "Can not allocate memroy\n");
return -ENOMEM;
}
+ codec = &twl4030->codec;
codec->private_data = twl4030;
- socdev->card->codec = codec;
+ codec->dev = &pdev->dev;
+ twl4030_dai[0].dev = &pdev->dev;
+ twl4030_dai[1].dev = &pdev->dev;
+
mutex_init(&codec->mutex);
INIT_LIST_HEAD(&codec->dapm_widgets);
INIT_LIST_HEAD(&codec->dapm_paths);
- twl4030_socdev = socdev;
- twl4030_init(socdev);
+ codec->name = "twl4030";
+ codec->owner = THIS_MODULE;
+ codec->read = twl4030_read_reg_cache;
+ codec->write = twl4030_write;
+ codec->set_bias_level = twl4030_set_bias_level;
+ codec->dai = twl4030_dai;
+ codec->num_dai = ARRAY_SIZE(twl4030_dai),
+ codec->reg_cache_size = sizeof(twl4030_reg);
+ codec->reg_cache = kmemdup(twl4030_reg, sizeof(twl4030_reg),
+ GFP_KERNEL);
+ if (codec->reg_cache == NULL) {
+ ret = -ENOMEM;
+ goto error_cache;
+ }
+
+ platform_set_drvdata(pdev, twl4030);
+ twl4030_codec = codec;
+
+ /* Set the defaults, and power up the codec */
+ twl4030->sysclk = twl4030_codec_get_mclk() / 1000;
+ twl4030_init_chip(codec);
+ codec->bias_level = SND_SOC_BIAS_OFF;
+ twl4030_set_bias_level(codec, SND_SOC_BIAS_STANDBY);
+
+ ret = snd_soc_register_codec(codec);
+ if (ret != 0) {
+ dev_err(codec->dev, "Failed to register codec: %d\n", ret);
+ goto error_codec;
+ }
+
+ ret = snd_soc_register_dais(&twl4030_dai[0], ARRAY_SIZE(twl4030_dai));
+ if (ret != 0) {
+ dev_err(codec->dev, "Failed to register DAIs: %d\n", ret);
+ snd_soc_unregister_codec(codec);
+ goto error_codec;
+ }
return 0;
+
+error_codec:
+ twl4030_power_down(codec);
+ kfree(codec->reg_cache);
+error_cache:
+ kfree(twl4030);
+ return ret;
}
-static int twl4030_remove(struct platform_device *pdev)
+static int __devexit twl4030_codec_remove(struct platform_device *pdev)
{
- struct snd_soc_device *socdev = platform_get_drvdata(pdev);
- struct snd_soc_codec *codec = socdev->card->codec;
+ struct twl4030_priv *twl4030 = platform_get_drvdata(pdev);
- printk(KERN_INFO "TWL4030 Audio Codec remove\n");
- twl4030_set_bias_level(codec, SND_SOC_BIAS_OFF);
- snd_soc_free_pcms(socdev);
- snd_soc_dapm_free(socdev);
- kfree(codec->private_data);
- kfree(codec);
+ kfree(twl4030);
+ twl4030_codec = NULL;
return 0;
}
-struct snd_soc_codec_device soc_codec_dev_twl4030 = {
- .probe = twl4030_probe,
- .remove = twl4030_remove,
- .suspend = twl4030_suspend,
- .resume = twl4030_resume,
+MODULE_ALIAS("platform:twl4030_codec_audio");
+
+static struct platform_driver twl4030_codec_driver = {
+ .probe = twl4030_codec_probe,
+ .remove = __devexit_p(twl4030_codec_remove),
+ .driver = {
+ .name = "twl4030_codec_audio",
+ .owner = THIS_MODULE,
+ },
};
-EXPORT_SYMBOL_GPL(soc_codec_dev_twl4030);
static int __init twl4030_modinit(void)
{
- return snd_soc_register_dais(&twl4030_dai[0], ARRAY_SIZE(twl4030_dai));
+ return platform_driver_register(&twl4030_codec_driver);
}
module_init(twl4030_modinit);
static void __exit twl4030_exit(void)
{
- snd_soc_unregister_dais(&twl4030_dai[0], ARRAY_SIZE(twl4030_dai));
+ platform_driver_unregister(&twl4030_codec_driver);
}
module_exit(twl4030_exit);
+struct snd_soc_codec_device soc_codec_dev_twl4030 = {
+ .probe = twl4030_soc_probe,
+ .remove = twl4030_soc_remove,
+ .suspend = twl4030_soc_suspend,
+ .resume = twl4030_soc_resume,
+};
+EXPORT_SYMBOL_GPL(soc_codec_dev_twl4030);
+
MODULE_DESCRIPTION("ASoC TWL4030 codec driver");
MODULE_AUTHOR("Steve Sakoman");
MODULE_LICENSE("GPL");
diff --git a/sound/soc/codecs/twl4030.h b/sound/soc/codecs/twl4030.h
index 2b4bfa23f985..dd6396ec9c79 100644
--- a/sound/soc/codecs/twl4030.h
+++ b/sound/soc/codecs/twl4030.h
@@ -22,245 +22,13 @@
#ifndef __TWL4030_AUDIO_H__
#define __TWL4030_AUDIO_H__
-#define TWL4030_REG_CODEC_MODE 0x1
-#define TWL4030_REG_OPTION 0x2
-#define TWL4030_REG_UNKNOWN 0x3
-#define TWL4030_REG_MICBIAS_CTL 0x4
-#define TWL4030_REG_ANAMICL 0x5
-#define TWL4030_REG_ANAMICR 0x6
-#define TWL4030_REG_AVADC_CTL 0x7
-#define TWL4030_REG_ADCMICSEL 0x8
-#define TWL4030_REG_DIGMIXING 0x9
-#define TWL4030_REG_ATXL1PGA 0xA
-#define TWL4030_REG_ATXR1PGA 0xB
-#define TWL4030_REG_AVTXL2PGA 0xC
-#define TWL4030_REG_AVTXR2PGA 0xD
-#define TWL4030_REG_AUDIO_IF 0xE
-#define TWL4030_REG_VOICE_IF 0xF
-#define TWL4030_REG_ARXR1PGA 0x10
-#define TWL4030_REG_ARXL1PGA 0x11
-#define TWL4030_REG_ARXR2PGA 0x12
-#define TWL4030_REG_ARXL2PGA 0x13
-#define TWL4030_REG_VRXPGA 0x14
-#define TWL4030_REG_VSTPGA 0x15
-#define TWL4030_REG_VRX2ARXPGA 0x16
-#define TWL4030_REG_AVDAC_CTL 0x17
-#define TWL4030_REG_ARX2VTXPGA 0x18
-#define TWL4030_REG_ARXL1_APGA_CTL 0x19
-#define TWL4030_REG_ARXR1_APGA_CTL 0x1A
-#define TWL4030_REG_ARXL2_APGA_CTL 0x1B
-#define TWL4030_REG_ARXR2_APGA_CTL 0x1C
-#define TWL4030_REG_ATX2ARXPGA 0x1D
-#define TWL4030_REG_BT_IF 0x1E
-#define TWL4030_REG_BTPGA 0x1F
-#define TWL4030_REG_BTSTPGA 0x20
-#define TWL4030_REG_EAR_CTL 0x21
-#define TWL4030_REG_HS_SEL 0x22
-#define TWL4030_REG_HS_GAIN_SET 0x23
-#define TWL4030_REG_HS_POPN_SET 0x24
-#define TWL4030_REG_PREDL_CTL 0x25
-#define TWL4030_REG_PREDR_CTL 0x26
-#define TWL4030_REG_PRECKL_CTL 0x27
-#define TWL4030_REG_PRECKR_CTL 0x28
-#define TWL4030_REG_HFL_CTL 0x29
-#define TWL4030_REG_HFR_CTL 0x2A
-#define TWL4030_REG_ALC_CTL 0x2B
-#define TWL4030_REG_ALC_SET1 0x2C
-#define TWL4030_REG_ALC_SET2 0x2D
-#define TWL4030_REG_BOOST_CTL 0x2E
-#define TWL4030_REG_SOFTVOL_CTL 0x2F
-#define TWL4030_REG_DTMF_FREQSEL 0x30
-#define TWL4030_REG_DTMF_TONEXT1H 0x31
-#define TWL4030_REG_DTMF_TONEXT1L 0x32
-#define TWL4030_REG_DTMF_TONEXT2H 0x33
-#define TWL4030_REG_DTMF_TONEXT2L 0x34
-#define TWL4030_REG_DTMF_TONOFF 0x35
-#define TWL4030_REG_DTMF_WANONOFF 0x36
-#define TWL4030_REG_I2S_RX_SCRAMBLE_H 0x37
-#define TWL4030_REG_I2S_RX_SCRAMBLE_M 0x38
-#define TWL4030_REG_I2S_RX_SCRAMBLE_L 0x39
-#define TWL4030_REG_APLL_CTL 0x3A
-#define TWL4030_REG_DTMF_CTL 0x3B
-#define TWL4030_REG_DTMF_PGA_CTL2 0x3C
-#define TWL4030_REG_DTMF_PGA_CTL1 0x3D
-#define TWL4030_REG_MISC_SET_1 0x3E
-#define TWL4030_REG_PCMBTMUX 0x3F
-#define TWL4030_REG_RX_PATH_SEL 0x43
-#define TWL4030_REG_VDL_APGA_CTL 0x44
-#define TWL4030_REG_VIBRA_CTL 0x45
-#define TWL4030_REG_VIBRA_SET 0x46
-#define TWL4030_REG_VIBRA_PWM_SET 0x47
-#define TWL4030_REG_ANAMIC_GAIN 0x48
-#define TWL4030_REG_MISC_SET_2 0x49
-#define TWL4030_REG_SW_SHADOW 0x4A
+/* Register descriptions are here */
+#include <linux/mfd/twl4030-codec.h>
+/* Sgadow register used by the audio driver */
+#define TWL4030_REG_SW_SHADOW 0x4A
#define TWL4030_CACHEREGNUM (TWL4030_REG_SW_SHADOW + 1)
-/* Bitfield Definitions */
-
-/* TWL4030_CODEC_MODE (0x01) Fields */
-
-#define TWL4030_APLL_RATE 0xF0
-#define TWL4030_APLL_RATE_8000 0x00
-#define TWL4030_APLL_RATE_11025 0x10
-#define TWL4030_APLL_RATE_12000 0x20
-#define TWL4030_APLL_RATE_16000 0x40
-#define TWL4030_APLL_RATE_22050 0x50
-#define TWL4030_APLL_RATE_24000 0x60
-#define TWL4030_APLL_RATE_32000 0x80
-#define TWL4030_APLL_RATE_44100 0x90
-#define TWL4030_APLL_RATE_48000 0xA0
-#define TWL4030_APLL_RATE_96000 0xE0
-#define TWL4030_SEL_16K 0x08
-#define TWL4030_CODECPDZ 0x02
-#define TWL4030_OPT_MODE 0x01
-#define TWL4030_OPTION_1 (1 << 0)
-#define TWL4030_OPTION_2 (0 << 0)
-
-/* TWL4030_OPTION (0x02) Fields */
-
-#define TWL4030_ATXL1_EN (1 << 0)
-#define TWL4030_ATXR1_EN (1 << 1)
-#define TWL4030_ATXL2_VTXL_EN (1 << 2)
-#define TWL4030_ATXR2_VTXR_EN (1 << 3)
-#define TWL4030_ARXL1_VRX_EN (1 << 4)
-#define TWL4030_ARXR1_EN (1 << 5)
-#define TWL4030_ARXL2_EN (1 << 6)
-#define TWL4030_ARXR2_EN (1 << 7)
-
-/* TWL4030_REG_MICBIAS_CTL (0x04) Fields */
-
-#define TWL4030_MICBIAS2_CTL 0x40
-#define TWL4030_MICBIAS1_CTL 0x20
-#define TWL4030_HSMICBIAS_EN 0x04
-#define TWL4030_MICBIAS2_EN 0x02
-#define TWL4030_MICBIAS1_EN 0x01
-
-/* ANAMICL (0x05) Fields */
-
-#define TWL4030_CNCL_OFFSET_START 0x80
-#define TWL4030_OFFSET_CNCL_SEL 0x60
-#define TWL4030_OFFSET_CNCL_SEL_ARX1 0x00
-#define TWL4030_OFFSET_CNCL_SEL_ARX2 0x20
-#define TWL4030_OFFSET_CNCL_SEL_VRX 0x40
-#define TWL4030_OFFSET_CNCL_SEL_ALL 0x60
-#define TWL4030_MICAMPL_EN 0x10
-#define TWL4030_CKMIC_EN 0x08
-#define TWL4030_AUXL_EN 0x04
-#define TWL4030_HSMIC_EN 0x02
-#define TWL4030_MAINMIC_EN 0x01
-
-/* ANAMICR (0x06) Fields */
-
-#define TWL4030_MICAMPR_EN 0x10
-#define TWL4030_AUXR_EN 0x04
-#define TWL4030_SUBMIC_EN 0x01
-
-/* AVADC_CTL (0x07) Fields */
-
-#define TWL4030_ADCL_EN 0x08
-#define TWL4030_AVADC_CLK_PRIORITY 0x04
-#define TWL4030_ADCR_EN 0x02
-
-/* TWL4030_REG_ADCMICSEL (0x08) Fields */
-
-#define TWL4030_DIGMIC1_EN 0x08
-#define TWL4030_TX2IN_SEL 0x04
-#define TWL4030_DIGMIC0_EN 0x02
-#define TWL4030_TX1IN_SEL 0x01
-
-/* AUDIO_IF (0x0E) Fields */
-
-#define TWL4030_AIF_SLAVE_EN 0x80
-#define TWL4030_DATA_WIDTH 0x60
-#define TWL4030_DATA_WIDTH_16S_16W 0x00
-#define TWL4030_DATA_WIDTH_32S_16W 0x40
-#define TWL4030_DATA_WIDTH_32S_24W 0x60
-#define TWL4030_AIF_FORMAT 0x18
-#define TWL4030_AIF_FORMAT_CODEC 0x00
-#define TWL4030_AIF_FORMAT_LEFT 0x08
-#define TWL4030_AIF_FORMAT_RIGHT 0x10
-#define TWL4030_AIF_FORMAT_TDM 0x18
-#define TWL4030_AIF_TRI_EN 0x04
-#define TWL4030_CLK256FS_EN 0x02
-#define TWL4030_AIF_EN 0x01
-
-/* VOICE_IF (0x0F) Fields */
-
-#define TWL4030_VIF_SLAVE_EN 0x80
-#define TWL4030_VIF_DIN_EN 0x40
-#define TWL4030_VIF_DOUT_EN 0x20
-#define TWL4030_VIF_SWAP 0x10
-#define TWL4030_VIF_FORMAT 0x08
-#define TWL4030_VIF_TRI_EN 0x04
-#define TWL4030_VIF_SUB_EN 0x02
-#define TWL4030_VIF_EN 0x01
-
-/* EAR_CTL (0x21) */
-#define TWL4030_EAR_GAIN 0x30
-
-/* HS_GAIN_SET (0x23) Fields */
-
-#define TWL4030_HSR_GAIN 0x0C
-#define TWL4030_HSR_GAIN_PWR_DOWN 0x00
-#define TWL4030_HSR_GAIN_PLUS_6DB 0x04
-#define TWL4030_HSR_GAIN_0DB 0x08
-#define TWL4030_HSR_GAIN_MINUS_6DB 0x0C
-#define TWL4030_HSL_GAIN 0x03
-#define TWL4030_HSL_GAIN_PWR_DOWN 0x00
-#define TWL4030_HSL_GAIN_PLUS_6DB 0x01
-#define TWL4030_HSL_GAIN_0DB 0x02
-#define TWL4030_HSL_GAIN_MINUS_6DB 0x03
-
-/* HS_POPN_SET (0x24) Fields */
-
-#define TWL4030_VMID_EN 0x40
-#define TWL4030_EXTMUTE 0x20
-#define TWL4030_RAMP_DELAY 0x1C
-#define TWL4030_RAMP_DELAY_20MS 0x00
-#define TWL4030_RAMP_DELAY_40MS 0x04
-#define TWL4030_RAMP_DELAY_81MS 0x08
-#define TWL4030_RAMP_DELAY_161MS 0x0C
-#define TWL4030_RAMP_DELAY_323MS 0x10
-#define TWL4030_RAMP_DELAY_645MS 0x14
-#define TWL4030_RAMP_DELAY_1291MS 0x18
-#define TWL4030_RAMP_DELAY_2581MS 0x1C
-#define TWL4030_RAMP_EN 0x02
-
-/* PREDL_CTL (0x25) */
-#define TWL4030_PREDL_GAIN 0x30
-
-/* PREDR_CTL (0x26) */
-#define TWL4030_PREDR_GAIN 0x30
-
-/* PRECKL_CTL (0x27) */
-#define TWL4030_PRECKL_GAIN 0x30
-
-/* PRECKR_CTL (0x28) */
-#define TWL4030_PRECKR_GAIN 0x30
-
-/* HFL_CTL (0x29, 0x2A) Fields */
-#define TWL4030_HF_CTL_HB_EN 0x04
-#define TWL4030_HF_CTL_LOOP_EN 0x08
-#define TWL4030_HF_CTL_RAMP_EN 0x10
-#define TWL4030_HF_CTL_REF_EN 0x20
-
-/* APLL_CTL (0x3A) Fields */
-
-#define TWL4030_APLL_EN 0x10
-#define TWL4030_APLL_INFREQ 0x0F
-#define TWL4030_APLL_INFREQ_19200KHZ 0x05
-#define TWL4030_APLL_INFREQ_26000KHZ 0x06
-#define TWL4030_APLL_INFREQ_38400KHZ 0x0F
-
-/* REG_MISC_SET_1 (0x3E) Fields */
-
-#define TWL4030_CLK64_EN 0x80
-#define TWL4030_SCRAMBLE_EN 0x40
-#define TWL4030_FMLOOP_EN 0x20
-#define TWL4030_SMOOTH_ANAVOL_EN 0x02
-#define TWL4030_DIGMIC_LR_SWAP_EN 0x01
-
/* TWL4030_REG_SW_SHADOW (0x4A) Fields */
#define TWL4030_HFL_EN 0x01
#define TWL4030_HFR_EN 0x02
@@ -279,3 +47,5 @@ struct twl4030_setup_data {
};
#endif /* End of __TWL4030_AUDIO_H__ */
+
+
diff --git a/sound/soc/codecs/uda134x.c b/sound/soc/codecs/uda134x.c
index c33b92edbded..aa40d985138f 100644
--- a/sound/soc/codecs/uda134x.c
+++ b/sound/soc/codecs/uda134x.c
@@ -562,17 +562,8 @@ static int uda134x_soc_probe(struct platform_device *pdev)
goto pcm_err;
}
- ret = snd_soc_init_card(socdev);
- if (ret < 0) {
- printk(KERN_ERR "UDA134X: failed to register card\n");
- goto card_err;
- }
-
return 0;
-card_err:
- snd_soc_free_pcms(socdev);
- snd_soc_dapm_free(socdev);
pcm_err:
kfree(codec->reg_cache);
reg_err:
diff --git a/sound/soc/codecs/uda1380.c b/sound/soc/codecs/uda1380.c
index 92ec03442154..a2763c2e7348 100644
--- a/sound/soc/codecs/uda1380.c
+++ b/sound/soc/codecs/uda1380.c
@@ -378,7 +378,6 @@ static int uda1380_add_widgets(struct snd_soc_codec *codec)
snd_soc_dapm_add_routes(codec, audio_map, ARRAY_SIZE(audio_map));
- snd_soc_dapm_new_widgets(codec);
return 0;
}
@@ -713,17 +712,9 @@ static int uda1380_probe(struct platform_device *pdev)
snd_soc_add_controls(codec, uda1380_snd_controls,
ARRAY_SIZE(uda1380_snd_controls));
uda1380_add_widgets(codec);
- ret = snd_soc_init_card(socdev);
- if (ret < 0) {
- dev_err(codec->dev, "failed to register card: %d\n", ret);
- goto card_err;
- }
return ret;
-card_err:
- snd_soc_free_pcms(socdev);
- snd_soc_dapm_free(socdev);
pcm_err:
return ret;
}
diff --git a/sound/soc/codecs/wm8350.c b/sound/soc/codecs/wm8350.c
index 593d5b9c9f03..f82125d9e85a 100644
--- a/sound/soc/codecs/wm8350.c
+++ b/sound/soc/codecs/wm8350.c
@@ -800,7 +800,7 @@ static int wm8350_add_widgets(struct snd_soc_codec *codec)
return ret;
}
- return snd_soc_dapm_new_widgets(codec);
+ return 0;
}
static int wm8350_set_dai_sysclk(struct snd_soc_dai *codec_dai,
@@ -1101,7 +1101,7 @@ static inline int fll_factors(struct _fll_div *fll_div, unsigned int input,
}
static int wm8350_set_fll(struct snd_soc_dai *codec_dai,
- int pll_id, unsigned int freq_in,
+ int pll_id, int source, unsigned int freq_in,
unsigned int freq_out)
{
struct snd_soc_codec *codec = codec_dai->codec;
@@ -1501,18 +1501,7 @@ static int wm8350_probe(struct platform_device *pdev)
wm8350_set_bias_level(codec, SND_SOC_BIAS_STANDBY);
- ret = snd_soc_init_card(socdev);
- if (ret < 0) {
- dev_err(&pdev->dev, "failed to register card\n");
- goto card_err;
- }
-
return 0;
-
-card_err:
- snd_soc_free_pcms(socdev);
- snd_soc_dapm_free(socdev);
- return ret;
}
static int wm8350_remove(struct platform_device *pdev)
@@ -1680,21 +1669,6 @@ static int __devexit wm8350_codec_remove(struct platform_device *pdev)
return 0;
}
-#ifdef CONFIG_PM
-static int wm8350_codec_suspend(struct platform_device *pdev, pm_message_t m)
-{
- return snd_soc_suspend_device(&pdev->dev);
-}
-
-static int wm8350_codec_resume(struct platform_device *pdev)
-{
- return snd_soc_resume_device(&pdev->dev);
-}
-#else
-#define wm8350_codec_suspend NULL
-#define wm8350_codec_resume NULL
-#endif
-
static struct platform_driver wm8350_codec_driver = {
.driver = {
.name = "wm8350-codec",
@@ -1702,8 +1676,6 @@ static struct platform_driver wm8350_codec_driver = {
},
.probe = wm8350_codec_probe,
.remove = __devexit_p(wm8350_codec_remove),
- .suspend = wm8350_codec_suspend,
- .resume = wm8350_codec_resume,
};
static __init int wm8350_init(void)
diff --git a/sound/soc/codecs/wm8400.c b/sound/soc/codecs/wm8400.c
index b9ef4d915221..b432f4d4a324 100644
--- a/sound/soc/codecs/wm8400.c
+++ b/sound/soc/codecs/wm8400.c
@@ -915,7 +915,6 @@ static int wm8400_add_widgets(struct snd_soc_codec *codec)
snd_soc_dapm_add_routes(codec, audio_map, ARRAY_SIZE(audio_map));
- snd_soc_dapm_new_widgets(codec);
return 0;
}
@@ -1011,7 +1010,8 @@ static int fll_factors(struct wm8400_priv *wm8400, struct fll_factors *factors,
}
static int wm8400_set_dai_pll(struct snd_soc_dai *codec_dai, int pll_id,
- unsigned int freq_in, unsigned int freq_out)
+ int source, unsigned int freq_in,
+ unsigned int freq_out)
{
struct snd_soc_codec *codec = codec_dai->codec;
struct wm8400_priv *wm8400 = codec->private_data;
@@ -1399,17 +1399,6 @@ static int wm8400_probe(struct platform_device *pdev)
wm8400_add_controls(codec);
wm8400_add_widgets(codec);
- ret = snd_soc_init_card(socdev);
- if (ret < 0) {
- dev_err(&pdev->dev, "failed to register card\n");
- goto card_err;
- }
-
- return ret;
-
-card_err:
- snd_soc_free_pcms(socdev);
- snd_soc_dapm_free(socdev);
pcm_err:
return ret;
}
@@ -1558,21 +1547,6 @@ static int __exit wm8400_codec_remove(struct platform_device *dev)
return 0;
}
-#ifdef CONFIG_PM
-static int wm8400_pdev_suspend(struct platform_device *pdev, pm_message_t msg)
-{
- return snd_soc_suspend_device(&pdev->dev);
-}
-
-static int wm8400_pdev_resume(struct platform_device *pdev)
-{
- return snd_soc_resume_device(&pdev->dev);
-}
-#else
-#define wm8400_pdev_suspend NULL
-#define wm8400_pdev_resume NULL
-#endif
-
static struct platform_driver wm8400_codec_driver = {
.driver = {
.name = "wm8400-codec",
@@ -1580,8 +1554,6 @@ static struct platform_driver wm8400_codec_driver = {
},
.probe = wm8400_codec_probe,
.remove = __exit_p(wm8400_codec_remove),
- .suspend = wm8400_pdev_suspend,
- .resume = wm8400_pdev_resume,
};
static int __init wm8400_codec_init(void)
diff --git a/sound/soc/codecs/wm8510.c b/sound/soc/codecs/wm8510.c
index 060d5d06ba95..265e68c75df8 100644
--- a/sound/soc/codecs/wm8510.c
+++ b/sound/soc/codecs/wm8510.c
@@ -219,7 +219,6 @@ static int wm8510_add_widgets(struct snd_soc_codec *codec)
snd_soc_dapm_add_routes(codec, audio_map, ARRAY_SIZE(audio_map));
- snd_soc_dapm_new_widgets(codec);
return 0;
}
@@ -271,8 +270,8 @@ static void pll_factors(unsigned int target, unsigned int source)
pll_div.k = K;
}
-static int wm8510_set_dai_pll(struct snd_soc_dai *codec_dai,
- int pll_id, unsigned int freq_in, unsigned int freq_out)
+static int wm8510_set_dai_pll(struct snd_soc_dai *codec_dai, int pll_id,
+ int source, unsigned int freq_in, unsigned int freq_out)
{
struct snd_soc_codec *codec = codec_dai->codec;
u16 reg;
@@ -604,16 +603,9 @@ static int wm8510_init(struct snd_soc_device *socdev,
snd_soc_add_controls(codec, wm8510_snd_controls,
ARRAY_SIZE(wm8510_snd_controls));
wm8510_add_widgets(codec);
- ret = snd_soc_init_card(socdev);
- if (ret < 0) {
- printk(KERN_ERR "wm8510: failed to register card\n");
- goto card_err;
- }
+
return ret;
-card_err:
- snd_soc_free_pcms(socdev);
- snd_soc_dapm_free(socdev);
err:
kfree(codec->reg_cache);
return ret;
diff --git a/sound/soc/codecs/wm8523.c b/sound/soc/codecs/wm8523.c
index 25870a4652fb..d3a61d7ea0c5 100644
--- a/sound/soc/codecs/wm8523.c
+++ b/sound/soc/codecs/wm8523.c
@@ -117,7 +117,6 @@ static int wm8523_add_widgets(struct snd_soc_codec *codec)
snd_soc_dapm_add_routes(codec, intercon, ARRAY_SIZE(intercon));
- snd_soc_dapm_new_widgets(codec);
return 0;
}
@@ -448,17 +447,9 @@ static int wm8523_probe(struct platform_device *pdev)
snd_soc_add_controls(codec, wm8523_snd_controls,
ARRAY_SIZE(wm8523_snd_controls));
wm8523_add_widgets(codec);
- ret = snd_soc_init_card(socdev);
- if (ret < 0) {
- dev_err(codec->dev, "failed to register card: %d\n", ret);
- goto card_err;
- }
return ret;
-card_err:
- snd_soc_free_pcms(socdev);
- snd_soc_dapm_free(socdev);
pcm_err:
return ret;
}
@@ -638,21 +629,6 @@ static __devexit int wm8523_i2c_remove(struct i2c_client *client)
return 0;
}
-#ifdef CONFIG_PM
-static int wm8523_i2c_suspend(struct i2c_client *i2c, pm_message_t msg)
-{
- return snd_soc_suspend_device(&i2c->dev);
-}
-
-static int wm8523_i2c_resume(struct i2c_client *i2c)
-{
- return snd_soc_resume_device(&i2c->dev);
-}
-#else
-#define wm8523_i2c_suspend NULL
-#define wm8523_i2c_resume NULL
-#endif
-
static const struct i2c_device_id wm8523_i2c_id[] = {
{ "wm8523", 0 },
{ }
@@ -666,8 +642,6 @@ static struct i2c_driver wm8523_i2c_driver = {
},
.probe = wm8523_i2c_probe,
.remove = __devexit_p(wm8523_i2c_remove),
- .suspend = wm8523_i2c_suspend,
- .resume = wm8523_i2c_resume,
.id_table = wm8523_i2c_id,
};
#endif
diff --git a/sound/soc/codecs/wm8580.c b/sound/soc/codecs/wm8580.c
index 6bded8c78150..d077df6f5e75 100644
--- a/sound/soc/codecs/wm8580.c
+++ b/sound/soc/codecs/wm8580.c
@@ -315,7 +315,6 @@ static int wm8580_add_widgets(struct snd_soc_codec *codec)
snd_soc_dapm_add_routes(codec, audio_map, ARRAY_SIZE(audio_map));
- snd_soc_dapm_new_widgets(codec);
return 0;
}
@@ -407,8 +406,8 @@ static int pll_factors(struct _pll_div *pll_div, unsigned int target,
return 0;
}
-static int wm8580_set_dai_pll(struct snd_soc_dai *codec_dai,
- int pll_id, unsigned int freq_in, unsigned int freq_out)
+static int wm8580_set_dai_pll(struct snd_soc_dai *codec_dai, int pll_id,
+ int source, unsigned int freq_in, unsigned int freq_out)
{
int offset;
struct snd_soc_codec *codec = codec_dai->codec;
@@ -800,17 +799,9 @@ static int wm8580_probe(struct platform_device *pdev)
snd_soc_add_controls(codec, wm8580_snd_controls,
ARRAY_SIZE(wm8580_snd_controls));
wm8580_add_widgets(codec);
- ret = snd_soc_init_card(socdev);
- if (ret < 0) {
- dev_err(codec->dev, "failed to register card: %d\n", ret);
- goto card_err;
- }
return ret;
-card_err:
- snd_soc_free_pcms(socdev);
- snd_soc_dapm_free(socdev);
pcm_err:
return ret;
}
@@ -961,21 +952,6 @@ static int wm8580_i2c_remove(struct i2c_client *client)
return 0;
}
-#ifdef CONFIG_PM
-static int wm8580_i2c_suspend(struct i2c_client *client, pm_message_t msg)
-{
- return snd_soc_suspend_device(&client->dev);
-}
-
-static int wm8580_i2c_resume(struct i2c_client *client)
-{
- return snd_soc_resume_device(&client->dev);
-}
-#else
-#define wm8580_i2c_suspend NULL
-#define wm8580_i2c_resume NULL
-#endif
-
static const struct i2c_device_id wm8580_i2c_id[] = {
{ "wm8580", 0 },
{ }
@@ -989,8 +965,6 @@ static struct i2c_driver wm8580_i2c_driver = {
},
.probe = wm8580_i2c_probe,
.remove = wm8580_i2c_remove,
- .suspend = wm8580_i2c_suspend,
- .resume = wm8580_i2c_resume,
.id_table = wm8580_i2c_id,
};
#endif
diff --git a/sound/soc/codecs/wm8711.c b/sound/soc/codecs/wm8711.c
new file mode 100644
index 000000000000..24a35603bcf7
--- /dev/null
+++ b/sound/soc/codecs/wm8711.c
@@ -0,0 +1,633 @@
+/*
+ * wm8711.c -- WM8711 ALSA SoC Audio driver
+ *
+ * Copyright 2006 Wolfson Microelectronics
+ *
+ * Author: Mike Arthur <linux@wolfsonmicro.com>
+ *
+ * Based on wm8731.c by Richard Purdie
+ *
+ * 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 <linux/spi/spi.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/tlv.h>
+#include <sound/initval.h>
+
+#include "wm8711.h"
+
+static struct snd_soc_codec *wm8711_codec;
+
+/* codec private data */
+struct wm8711_priv {
+ struct snd_soc_codec codec;
+ u16 reg_cache[WM8711_CACHEREGNUM];
+ unsigned int sysclk;
+};
+
+/*
+ * wm8711 register cache
+ * We can't read the WM8711 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 wm8711_reg[WM8711_CACHEREGNUM] = {
+ 0x0079, 0x0079, 0x000a, 0x0008,
+ 0x009f, 0x000a, 0x0000, 0x0000
+};
+
+#define wm8711_reset(c) snd_soc_write(c, WM8711_RESET, 0)
+
+static const DECLARE_TLV_DB_SCALE(out_tlv, -12100, 100, 1);
+
+static const struct snd_kcontrol_new wm8711_snd_controls[] = {
+
+SOC_DOUBLE_R_TLV("Master Playback Volume", WM8711_LOUT1V, WM8711_ROUT1V,
+ 0, 127, 0, out_tlv),
+SOC_DOUBLE_R("Master Playback ZC Switch", WM8711_LOUT1V, WM8711_ROUT1V,
+ 7, 1, 0),
+
+};
+
+/* Output Mixer */
+static const struct snd_kcontrol_new wm8711_output_mixer_controls[] = {
+SOC_DAPM_SINGLE("Line Bypass Switch", WM8711_APANA, 3, 1, 0),
+SOC_DAPM_SINGLE("HiFi Playback Switch", WM8711_APANA, 4, 1, 0),
+};
+
+static const struct snd_soc_dapm_widget wm8711_dapm_widgets[] = {
+SND_SOC_DAPM_MIXER("Output Mixer", WM8711_PWR, 4, 1,
+ &wm8711_output_mixer_controls[0],
+ ARRAY_SIZE(wm8711_output_mixer_controls)),
+SND_SOC_DAPM_DAC("DAC", "HiFi Playback", WM8711_PWR, 3, 1),
+SND_SOC_DAPM_OUTPUT("LOUT"),
+SND_SOC_DAPM_OUTPUT("LHPOUT"),
+SND_SOC_DAPM_OUTPUT("ROUT"),
+SND_SOC_DAPM_OUTPUT("RHPOUT"),
+};
+
+static const struct snd_soc_dapm_route intercon[] = {
+ /* output mixer */
+ {"Output Mixer", "Line Bypass Switch", "Line Input"},
+ {"Output Mixer", "HiFi Playback Switch", "DAC"},
+
+ /* outputs */
+ {"RHPOUT", NULL, "Output Mixer"},
+ {"ROUT", NULL, "Output Mixer"},
+ {"LHPOUT", NULL, "Output Mixer"},
+ {"LOUT", NULL, "Output Mixer"},
+};
+
+static int wm8711_add_widgets(struct snd_soc_codec *codec)
+{
+ snd_soc_dapm_new_controls(codec, wm8711_dapm_widgets,
+ ARRAY_SIZE(wm8711_dapm_widgets));
+
+ snd_soc_dapm_add_routes(codec, intercon, ARRAY_SIZE(intercon));
+
+ 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 wm8711_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params,
+ struct snd_soc_dai *dai)
+{
+ struct snd_soc_codec *codec = dai->codec;
+ struct wm8711_priv *wm8711 = codec->private_data;
+ u16 iface = snd_soc_read(codec, WM8711_IFACE) & 0xfffc;
+ int i = get_coeff(wm8711->sysclk, params_rate(params));
+ u16 srate = (coeff_div[i].sr << 2) |
+ (coeff_div[i].bosr << 1) | coeff_div[i].usb;
+
+ snd_soc_write(codec, WM8711_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;
+ }
+
+ snd_soc_write(codec, WM8711_IFACE, iface);
+ return 0;
+}
+
+static int wm8711_pcm_prepare(struct snd_pcm_substream *substream,
+ struct snd_soc_dai *dai)
+{
+ struct snd_soc_codec *codec = dai->codec;
+
+ /* set active */
+ snd_soc_write(codec, WM8711_ACTIVE, 0x0001);
+
+ return 0;
+}
+
+static void wm8711_shutdown(struct snd_pcm_substream *substream,
+ struct snd_soc_dai *dai)
+{
+ struct snd_soc_codec *codec = dai->codec;
+
+ /* deactivate */
+ if (!codec->active) {
+ udelay(50);
+ snd_soc_write(codec, WM8711_ACTIVE, 0x0);
+ }
+}
+
+static int wm8711_mute(struct snd_soc_dai *dai, int mute)
+{
+ struct snd_soc_codec *codec = dai->codec;
+ u16 mute_reg = snd_soc_read(codec, WM8711_APDIGI) & 0xfff7;
+
+ if (mute)
+ snd_soc_write(codec, WM8711_APDIGI, mute_reg | 0x8);
+ else
+ snd_soc_write(codec, WM8711_APDIGI, mute_reg);
+
+ return 0;
+}
+
+static int wm8711_set_dai_sysclk(struct snd_soc_dai *codec_dai,
+ int clk_id, unsigned int freq, int dir)
+{
+ struct snd_soc_codec *codec = codec_dai->codec;
+ struct wm8711_priv *wm8711 = codec->private_data;
+
+ switch (freq) {
+ case 11289600:
+ case 12000000:
+ case 12288000:
+ case 16934400:
+ case 18432000:
+ wm8711->sysclk = freq;
+ return 0;
+ }
+ return -EINVAL;
+}
+
+static int wm8711_set_dai_fmt(struct snd_soc_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 */
+ snd_soc_write(codec, WM8711_IFACE, iface);
+ return 0;
+}
+
+
+static int wm8711_set_bias_level(struct snd_soc_codec *codec,
+ enum snd_soc_bias_level level)
+{
+ u16 reg = snd_soc_read(codec, WM8711_PWR) & 0xff7f;
+
+ switch (level) {
+ case SND_SOC_BIAS_ON:
+ snd_soc_write(codec, WM8711_PWR, reg);
+ break;
+ case SND_SOC_BIAS_PREPARE:
+ break;
+ case SND_SOC_BIAS_STANDBY:
+ snd_soc_write(codec, WM8711_PWR, reg | 0x0040);
+ break;
+ case SND_SOC_BIAS_OFF:
+ snd_soc_write(codec, WM8711_ACTIVE, 0x0);
+ snd_soc_write(codec, WM8711_PWR, 0xffff);
+ break;
+ }
+ codec->bias_level = level;
+ return 0;
+}
+
+#define WM8711_RATES SNDRV_PCM_RATE_8000_96000
+
+#define WM8711_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S20_3LE |\
+ SNDRV_PCM_FMTBIT_S24_LE)
+
+static struct snd_soc_dai_ops wm8711_ops = {
+ .prepare = wm8711_pcm_prepare,
+ .hw_params = wm8711_hw_params,
+ .shutdown = wm8711_shutdown,
+ .digital_mute = wm8711_mute,
+ .set_sysclk = wm8711_set_dai_sysclk,
+ .set_fmt = wm8711_set_dai_fmt,
+};
+
+struct snd_soc_dai wm8711_dai = {
+ .name = "WM8711",
+ .playback = {
+ .stream_name = "Playback",
+ .channels_min = 1,
+ .channels_max = 2,
+ .rates = WM8711_RATES,
+ .formats = WM8711_FORMATS,
+ },
+ .ops = &wm8711_ops,
+};
+EXPORT_SYMBOL_GPL(wm8711_dai);
+
+static int wm8711_suspend(struct platform_device *pdev, pm_message_t state)
+{
+ struct snd_soc_device *socdev = platform_get_drvdata(pdev);
+ struct snd_soc_codec *codec = socdev->card->codec;
+
+ snd_soc_write(codec, WM8711_ACTIVE, 0x0);
+ wm8711_set_bias_level(codec, SND_SOC_BIAS_OFF);
+ return 0;
+}
+
+static int wm8711_resume(struct platform_device *pdev)
+{
+ struct snd_soc_device *socdev = platform_get_drvdata(pdev);
+ struct snd_soc_codec *codec = socdev->card->codec;
+ int i;
+ u8 data[2];
+ u16 *cache = codec->reg_cache;
+
+ /* Sync reg_cache with the hardware */
+ for (i = 0; i < ARRAY_SIZE(wm8711_reg); i++) {
+ data[0] = (i << 1) | ((cache[i] >> 8) & 0x0001);
+ data[1] = cache[i] & 0x00ff;
+ codec->hw_write(codec->control_data, data, 2);
+ }
+ wm8711_set_bias_level(codec, SND_SOC_BIAS_STANDBY);
+ wm8711_set_bias_level(codec, codec->suspend_bias_level);
+ return 0;
+}
+
+static int wm8711_probe(struct platform_device *pdev)
+{
+ struct snd_soc_device *socdev = platform_get_drvdata(pdev);
+ struct snd_soc_codec *codec;
+ int ret = 0;
+
+ if (wm8711_codec == NULL) {
+ dev_err(&pdev->dev, "Codec device not registered\n");
+ return -ENODEV;
+ }
+
+ socdev->card->codec = wm8711_codec;
+ codec = wm8711_codec;
+
+ /* register pcms */
+ ret = snd_soc_new_pcms(socdev, SNDRV_DEFAULT_IDX1, SNDRV_DEFAULT_STR1);
+ if (ret < 0) {
+ dev_err(codec->dev, "failed to create pcms: %d\n", ret);
+ goto pcm_err;
+ }
+
+ snd_soc_add_controls(codec, wm8711_snd_controls,
+ ARRAY_SIZE(wm8711_snd_controls));
+ wm8711_add_widgets(codec);
+
+ return ret;
+
+pcm_err:
+ return ret;
+}
+
+/* power down chip */
+static int wm8711_remove(struct platform_device *pdev)
+{
+ struct snd_soc_device *socdev = platform_get_drvdata(pdev);
+
+ snd_soc_free_pcms(socdev);
+ snd_soc_dapm_free(socdev);
+
+ return 0;
+}
+
+struct snd_soc_codec_device soc_codec_dev_wm8711 = {
+ .probe = wm8711_probe,
+ .remove = wm8711_remove,
+ .suspend = wm8711_suspend,
+ .resume = wm8711_resume,
+};
+EXPORT_SYMBOL_GPL(soc_codec_dev_wm8711);
+
+static int wm8711_register(struct wm8711_priv *wm8711,
+ enum snd_soc_control_type control)
+{
+ int ret;
+ struct snd_soc_codec *codec = &wm8711->codec;
+ u16 reg;
+
+ if (wm8711_codec) {
+ dev_err(codec->dev, "Another WM8711 is registered\n");
+ return -EINVAL;
+ }
+
+ mutex_init(&codec->mutex);
+ INIT_LIST_HEAD(&codec->dapm_widgets);
+ INIT_LIST_HEAD(&codec->dapm_paths);
+
+ codec->private_data = wm8711;
+ codec->name = "WM8711";
+ codec->owner = THIS_MODULE;
+ codec->bias_level = SND_SOC_BIAS_OFF;
+ codec->set_bias_level = wm8711_set_bias_level;
+ codec->dai = &wm8711_dai;
+ codec->num_dai = 1;
+ codec->reg_cache_size = WM8711_CACHEREGNUM;
+ codec->reg_cache = &wm8711->reg_cache;
+
+ memcpy(codec->reg_cache, wm8711_reg, sizeof(wm8711_reg));
+
+ ret = snd_soc_codec_set_cache_io(codec, 7, 9, control);
+ if (ret < 0) {
+ dev_err(codec->dev, "Failed to set cache I/O: %d\n", ret);
+ goto err;
+ }
+
+ ret = wm8711_reset(codec);
+ if (ret < 0) {
+ dev_err(codec->dev, "Failed to issue reset\n");
+ goto err;
+ }
+
+ wm8711_dai.dev = codec->dev;
+
+ wm8711_set_bias_level(codec, SND_SOC_BIAS_STANDBY);
+
+ /* Latch the update bits */
+ reg = snd_soc_read(codec, WM8711_LOUT1V);
+ snd_soc_write(codec, WM8711_LOUT1V, reg | 0x0100);
+ reg = snd_soc_read(codec, WM8711_ROUT1V);
+ snd_soc_write(codec, WM8711_ROUT1V, reg | 0x0100);
+
+ wm8711_codec = codec;
+
+ ret = snd_soc_register_codec(codec);
+ if (ret != 0) {
+ dev_err(codec->dev, "Failed to register codec: %d\n", ret);
+ goto err;
+ }
+
+ ret = snd_soc_register_dai(&wm8711_dai);
+ if (ret != 0) {
+ dev_err(codec->dev, "Failed to register DAI: %d\n", ret);
+ goto err_codec;
+ }
+
+ return 0;
+
+err_codec:
+ snd_soc_unregister_codec(codec);
+err:
+ kfree(wm8711);
+ return ret;
+}
+
+static void wm8711_unregister(struct wm8711_priv *wm8711)
+{
+ wm8711_set_bias_level(&wm8711->codec, SND_SOC_BIAS_OFF);
+ snd_soc_unregister_dai(&wm8711_dai);
+ snd_soc_unregister_codec(&wm8711->codec);
+ kfree(wm8711);
+ wm8711_codec = NULL;
+}
+
+#if defined(CONFIG_SPI_MASTER)
+static int __devinit wm8711_spi_probe(struct spi_device *spi)
+{
+ struct snd_soc_codec *codec;
+ struct wm8711_priv *wm8711;
+
+ wm8711 = kzalloc(sizeof(struct wm8711_priv), GFP_KERNEL);
+ if (wm8711 == NULL)
+ return -ENOMEM;
+
+ codec = &wm8711->codec;
+ codec->control_data = spi;
+ codec->dev = &spi->dev;
+
+ dev_set_drvdata(&spi->dev, wm8711);
+
+ return wm8711_register(wm8711, SND_SOC_SPI);
+}
+
+static int __devexit wm8711_spi_remove(struct spi_device *spi)
+{
+ struct wm8711_priv *wm8711 = dev_get_drvdata(&spi->dev);
+
+ wm8711_unregister(wm8711);
+
+ return 0;
+}
+
+static struct spi_driver wm8711_spi_driver = {
+ .driver = {
+ .name = "wm8711",
+ .bus = &spi_bus_type,
+ .owner = THIS_MODULE,
+ },
+ .probe = wm8711_spi_probe,
+ .remove = __devexit_p(wm8711_spi_remove),
+};
+#endif /* CONFIG_SPI_MASTER */
+
+#if defined(CONFIG_I2C) || defined(CONFIG_I2C_MODULE)
+static __devinit int wm8711_i2c_probe(struct i2c_client *i2c,
+ const struct i2c_device_id *id)
+{
+ struct wm8711_priv *wm8711;
+ struct snd_soc_codec *codec;
+
+ wm8711 = kzalloc(sizeof(struct wm8711_priv), GFP_KERNEL);
+ if (wm8711 == NULL)
+ return -ENOMEM;
+
+ codec = &wm8711->codec;
+ codec->hw_write = (hw_write_t)i2c_master_send;
+
+ i2c_set_clientdata(i2c, wm8711);
+ codec->control_data = i2c;
+
+ codec->dev = &i2c->dev;
+
+ return wm8711_register(wm8711, SND_SOC_I2C);
+}
+
+static __devexit int wm8711_i2c_remove(struct i2c_client *client)
+{
+ struct wm8711_priv *wm8711 = i2c_get_clientdata(client);
+ wm8711_unregister(wm8711);
+ return 0;
+}
+
+static const struct i2c_device_id wm8711_i2c_id[] = {
+ { "wm8711", 0 },
+ { }
+};
+MODULE_DEVICE_TABLE(i2c, wm8711_i2c_id);
+
+static struct i2c_driver wm8711_i2c_driver = {
+ .driver = {
+ .name = "WM8711 I2C Codec",
+ .owner = THIS_MODULE,
+ },
+ .probe = wm8711_i2c_probe,
+ .remove = __devexit_p(wm8711_i2c_remove),
+ .id_table = wm8711_i2c_id,
+};
+#endif
+
+static int __init wm8711_modinit(void)
+{
+ int ret;
+#if defined(CONFIG_I2C) || defined(CONFIG_I2C_MODULE)
+ ret = i2c_add_driver(&wm8711_i2c_driver);
+ if (ret != 0) {
+ printk(KERN_ERR "Failed to register WM8711 I2C driver: %d\n",
+ ret);
+ }
+#endif
+#if defined(CONFIG_SPI_MASTER)
+ ret = spi_register_driver(&wm8711_spi_driver);
+ if (ret != 0) {
+ printk(KERN_ERR "Failed to register WM8711 SPI driver: %d\n",
+ ret);
+ }
+#endif
+ return 0;
+}
+module_init(wm8711_modinit);
+
+static void __exit wm8711_exit(void)
+{
+#if defined(CONFIG_I2C) || defined(CONFIG_I2C_MODULE)
+ i2c_del_driver(&wm8711_i2c_driver);
+#endif
+#if defined(CONFIG_SPI_MASTER)
+ spi_unregister_driver(&wm8711_spi_driver);
+#endif
+}
+module_exit(wm8711_exit);
+
+MODULE_DESCRIPTION("ASoC WM8711 driver");
+MODULE_AUTHOR("Mike Arthur");
+MODULE_LICENSE("GPL");
diff --git a/sound/soc/codecs/wm8711.h b/sound/soc/codecs/wm8711.h
new file mode 100644
index 000000000000..381e84a43816
--- /dev/null
+++ b/sound/soc/codecs/wm8711.h
@@ -0,0 +1,42 @@
+/*
+ * wm8711.h -- WM8711 Soc Audio driver
+ *
+ * Copyright 2006 Wolfson Microelectronics
+ *
+ * Author: Mike Arthur <linux@wolfsonmicro.com>
+ *
+ * Based on wm8731.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 _WM8711_H
+#define _WM8711_H
+
+/* WM8711 register space */
+
+#define WM8711_LOUT1V 0x02
+#define WM8711_ROUT1V 0x03
+#define WM8711_APANA 0x04
+#define WM8711_APDIGI 0x05
+#define WM8711_PWR 0x06
+#define WM8711_IFACE 0x07
+#define WM8711_SRATE 0x08
+#define WM8711_ACTIVE 0x09
+#define WM8711_RESET 0x0f
+
+#define WM8711_CACHEREGNUM 8
+
+#define WM8711_SYSCLK 0
+#define WM8711_DAI 0
+
+struct wm8711_setup_data {
+ unsigned short i2c_address;
+};
+
+extern struct snd_soc_dai wm8711_dai;
+extern struct snd_soc_codec_device soc_codec_dev_wm8711;
+
+#endif
diff --git a/sound/soc/codecs/wm8727.c b/sound/soc/codecs/wm8727.c
new file mode 100644
index 000000000000..d8ffbd641d71
--- /dev/null
+++ b/sound/soc/codecs/wm8727.c
@@ -0,0 +1,135 @@
+/*
+ * wm8727.c
+ *
+ * Created on: 15-Oct-2009
+ * Author: neil.jones@imgtec.com
+ *
+ * Copyright (C) 2009 Imagination Technologies Ltd.
+ *
+ * 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/kernel.h>
+#include <linux/device.h>
+#include <sound/core.h>
+#include <sound/pcm.h>
+#include <sound/ac97_codec.h>
+#include <sound/initval.h>
+#include <sound/soc.h>
+
+#include "wm8727.h"
+/*
+ * Note this is a simple chip with no configuration interface, sample rate is
+ * determined automatically by examining the Master clock and Bit clock ratios
+ */
+#define WM8727_RATES (SNDRV_PCM_RATE_32000 | SNDRV_PCM_RATE_44100 |\
+ SNDRV_PCM_RATE_48000 | SNDRV_PCM_RATE_96000 |\
+ SNDRV_PCM_RATE_192000)
+
+
+struct snd_soc_dai wm8727_dai = {
+ .name = "WM8727",
+ .playback = {
+ .stream_name = "Playback",
+ .channels_min = 2,
+ .channels_max = 2,
+ .rates = WM8727_RATES,
+ .formats = SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S24_LE,
+ },
+};
+EXPORT_SYMBOL_GPL(wm8727_dai);
+
+static int wm8727_soc_probe(struct platform_device *pdev)
+{
+ struct snd_soc_device *socdev = platform_get_drvdata(pdev);
+ struct snd_soc_codec *codec;
+ int ret = 0;
+
+ codec = kzalloc(sizeof(struct snd_soc_codec), GFP_KERNEL);
+ if (codec == NULL)
+ return -ENOMEM;
+ mutex_init(&codec->mutex);
+ codec->name = "WM8727";
+ codec->owner = THIS_MODULE;
+ codec->dai = &wm8727_dai;
+ codec->num_dai = 1;
+ socdev->card->codec = codec;
+ 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) {
+ printk(KERN_ERR "wm8727: failed to create pcms\n");
+ goto pcm_err;
+ }
+
+ return ret;
+
+pcm_err:
+ kfree(socdev->card->codec);
+ socdev->card->codec = NULL;
+ return ret;
+}
+
+static int wm8727_soc_remove(struct platform_device *pdev)
+{
+ struct snd_soc_device *socdev = platform_get_drvdata(pdev);
+ struct snd_soc_codec *codec = socdev->card->codec;
+
+ if (codec == NULL)
+ return 0;
+ snd_soc_free_pcms(socdev);
+ kfree(codec);
+ return 0;
+}
+
+struct snd_soc_codec_device soc_codec_dev_wm8727 = {
+ .probe = wm8727_soc_probe,
+ .remove = wm8727_soc_remove,
+};
+EXPORT_SYMBOL_GPL(soc_codec_dev_wm8727);
+
+
+static __devinit int wm8727_platform_probe(struct platform_device *pdev)
+{
+ wm8727_dai.dev = &pdev->dev;
+ return snd_soc_register_dai(&wm8727_dai);
+}
+
+static int __devexit wm8727_platform_remove(struct platform_device *pdev)
+{
+ snd_soc_unregister_dai(&wm8727_dai);
+ return 0;
+}
+
+static struct platform_driver wm8727_codec_driver = {
+ .driver = {
+ .name = "wm8727-codec",
+ .owner = THIS_MODULE,
+ },
+
+ .probe = wm8727_platform_probe,
+ .remove = __devexit_p(wm8727_platform_remove),
+};
+
+static int __init wm8727_init(void)
+{
+ return platform_driver_register(&wm8727_codec_driver);
+}
+module_init(wm8727_init);
+
+static void __exit wm8727_exit(void)
+{
+ platform_driver_unregister(&wm8727_codec_driver);
+}
+module_exit(wm8727_exit);
+
+MODULE_DESCRIPTION("ASoC wm8727 driver");
+MODULE_AUTHOR("Neil Jones");
+MODULE_LICENSE("GPL");
diff --git a/sound/soc/codecs/wm8727.h b/sound/soc/codecs/wm8727.h
new file mode 100644
index 000000000000..ee19aa71bcdc
--- /dev/null
+++ b/sound/soc/codecs/wm8727.h
@@ -0,0 +1,21 @@
+/*
+ * wm8727.h
+ *
+ * Created on: 15-Oct-2009
+ * Author: neil.jones@imgtec.com
+ *
+ * Copyright (C) 2009 Imagination Technologies Ltd.
+ *
+ * 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.
+ */
+
+#ifndef WM8727_H_
+#define WM8727_H_
+
+extern struct snd_soc_dai wm8727_dai;
+extern struct snd_soc_codec_device soc_codec_dev_wm8727;
+
+#endif /* WM8727_H_ */
diff --git a/sound/soc/codecs/wm8728.c b/sound/soc/codecs/wm8728.c
index 16e969a762c3..3fb653ba363a 100644
--- a/sound/soc/codecs/wm8728.c
+++ b/sound/soc/codecs/wm8728.c
@@ -74,8 +74,6 @@ static int wm8728_add_widgets(struct snd_soc_codec *codec)
snd_soc_dapm_add_routes(codec, intercon, ARRAY_SIZE(intercon));
- snd_soc_dapm_new_widgets(codec);
-
return 0;
}
@@ -287,17 +285,9 @@ static int wm8728_init(struct snd_soc_device *socdev,
snd_soc_add_controls(codec, wm8728_snd_controls,
ARRAY_SIZE(wm8728_snd_controls));
wm8728_add_widgets(codec);
- ret = snd_soc_init_card(socdev);
- if (ret < 0) {
- printk(KERN_ERR "wm8728: failed to register card\n");
- goto card_err;
- }
return ret;
-card_err:
- snd_soc_free_pcms(socdev);
- snd_soc_dapm_free(socdev);
err:
kfree(codec->reg_cache);
return ret;
diff --git a/sound/soc/codecs/wm8731.c b/sound/soc/codecs/wm8731.c
index d3fd4f28d96e..3a497810f939 100644
--- a/sound/soc/codecs/wm8731.c
+++ b/sound/soc/codecs/wm8731.c
@@ -19,6 +19,7 @@
#include <linux/pm.h>
#include <linux/i2c.h>
#include <linux/platform_device.h>
+#include <linux/regulator/consumer.h>
#include <linux/spi/spi.h>
#include <sound/core.h>
#include <sound/pcm.h>
@@ -33,9 +34,18 @@
static struct snd_soc_codec *wm8731_codec;
struct snd_soc_codec_device soc_codec_dev_wm8731;
+#define WM8731_NUM_SUPPLIES 4
+static const char *wm8731_supply_names[WM8731_NUM_SUPPLIES] = {
+ "AVDD",
+ "HPVDD",
+ "DCVDD",
+ "DBVDD",
+};
+
/* codec private data */
struct wm8731_priv {
struct snd_soc_codec codec;
+ struct regulator_bulk_data supplies[WM8731_NUM_SUPPLIES];
u16 reg_cache[WM8731_CACHEREGNUM];
unsigned int sysclk;
};
@@ -149,7 +159,6 @@ static int wm8731_add_widgets(struct snd_soc_codec *codec)
snd_soc_dapm_add_routes(codec, intercon, ARRAY_SIZE(intercon));
- snd_soc_dapm_new_widgets(codec);
return 0;
}
@@ -422,9 +431,12 @@ 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->card->codec;
+ struct wm8731_priv *wm8731 = codec->private_data;
snd_soc_write(codec, WM8731_ACTIVE, 0x0);
wm8731_set_bias_level(codec, SND_SOC_BIAS_OFF);
+ regulator_bulk_disable(ARRAY_SIZE(wm8731->supplies),
+ wm8731->supplies);
return 0;
}
@@ -432,10 +444,16 @@ static int wm8731_resume(struct platform_device *pdev)
{
struct snd_soc_device *socdev = platform_get_drvdata(pdev);
struct snd_soc_codec *codec = socdev->card->codec;
- int i;
+ struct wm8731_priv *wm8731 = codec->private_data;
+ int i, ret;
u8 data[2];
u16 *cache = codec->reg_cache;
+ ret = regulator_bulk_enable(ARRAY_SIZE(wm8731->supplies),
+ wm8731->supplies);
+ if (ret != 0)
+ return ret;
+
/* Sync reg_cache with the hardware */
for (i = 0; i < ARRAY_SIZE(wm8731_reg); i++) {
data[0] = (i << 1) | ((cache[i] >> 8) & 0x0001);
@@ -444,6 +462,7 @@ static int wm8731_resume(struct platform_device *pdev)
}
wm8731_set_bias_level(codec, SND_SOC_BIAS_STANDBY);
wm8731_set_bias_level(codec, codec->suspend_bias_level);
+
return 0;
}
#else
@@ -475,17 +494,9 @@ static int wm8731_probe(struct platform_device *pdev)
snd_soc_add_controls(codec, wm8731_snd_controls,
ARRAY_SIZE(wm8731_snd_controls));
wm8731_add_widgets(codec);
- ret = snd_soc_init_card(socdev);
- if (ret < 0) {
- dev_err(codec->dev, "failed to register card: %d\n", ret);
- goto card_err;
- }
return ret;
-card_err:
- snd_soc_free_pcms(socdev);
- snd_soc_dapm_free(socdev);
pcm_err:
return ret;
}
@@ -512,7 +523,7 @@ EXPORT_SYMBOL_GPL(soc_codec_dev_wm8731);
static int wm8731_register(struct wm8731_priv *wm8731,
enum snd_soc_control_type control)
{
- int ret;
+ int ret, i;
struct snd_soc_codec *codec = &wm8731->codec;
if (wm8731_codec) {
@@ -543,10 +554,27 @@ static int wm8731_register(struct wm8731_priv *wm8731,
goto err;
}
+ for (i = 0; i < ARRAY_SIZE(wm8731->supplies); i++)
+ wm8731->supplies[i].supply = wm8731_supply_names[i];
+
+ ret = regulator_bulk_get(codec->dev, ARRAY_SIZE(wm8731->supplies),
+ wm8731->supplies);
+ if (ret != 0) {
+ dev_err(codec->dev, "Failed to request supplies: %d\n", ret);
+ goto err;
+ }
+
+ ret = regulator_bulk_enable(ARRAY_SIZE(wm8731->supplies),
+ wm8731->supplies);
+ if (ret != 0) {
+ dev_err(codec->dev, "Failed to enable supplies: %d\n", ret);
+ goto err_regulator_get;
+ }
+
ret = wm8731_reset(codec);
if (ret < 0) {
dev_err(codec->dev, "Failed to issue reset: %d\n", ret);
- goto err;
+ goto err_regulator_enable;
}
wm8731_dai.dev = codec->dev;
@@ -567,7 +595,7 @@ static int wm8731_register(struct wm8731_priv *wm8731,
ret = snd_soc_register_codec(codec);
if (ret != 0) {
dev_err(codec->dev, "Failed to register codec: %d\n", ret);
- goto err;
+ goto err_regulator_enable;
}
ret = snd_soc_register_dai(&wm8731_dai);
@@ -581,6 +609,10 @@ static int wm8731_register(struct wm8731_priv *wm8731,
err_codec:
snd_soc_unregister_codec(codec);
+err_regulator_enable:
+ regulator_bulk_disable(ARRAY_SIZE(wm8731->supplies), wm8731->supplies);
+err_regulator_get:
+ regulator_bulk_free(ARRAY_SIZE(wm8731->supplies), wm8731->supplies);
err:
kfree(wm8731);
return ret;
@@ -591,6 +623,8 @@ static void wm8731_unregister(struct wm8731_priv *wm8731)
wm8731_set_bias_level(&wm8731->codec, SND_SOC_BIAS_OFF);
snd_soc_unregister_dai(&wm8731_dai);
snd_soc_unregister_codec(&wm8731->codec);
+ regulator_bulk_disable(ARRAY_SIZE(wm8731->supplies), wm8731->supplies);
+ regulator_bulk_free(ARRAY_SIZE(wm8731->supplies), wm8731->supplies);
kfree(wm8731);
wm8731_codec = NULL;
}
@@ -623,21 +657,6 @@ static int __devexit wm8731_spi_remove(struct spi_device *spi)
return 0;
}
-#ifdef CONFIG_PM
-static int wm8731_spi_suspend(struct spi_device *spi, pm_message_t msg)
-{
- return snd_soc_suspend_device(&spi->dev);
-}
-
-static int wm8731_spi_resume(struct spi_device *spi)
-{
- return snd_soc_resume_device(&spi->dev);
-}
-#else
-#define wm8731_spi_suspend NULL
-#define wm8731_spi_resume NULL
-#endif
-
static struct spi_driver wm8731_spi_driver = {
.driver = {
.name = "wm8731",
@@ -645,8 +664,6 @@ static struct spi_driver wm8731_spi_driver = {
.owner = THIS_MODULE,
},
.probe = wm8731_spi_probe,
- .suspend = wm8731_spi_suspend,
- .resume = wm8731_spi_resume,
.remove = __devexit_p(wm8731_spi_remove),
};
#endif /* CONFIG_SPI_MASTER */
@@ -679,21 +696,6 @@ static __devexit int wm8731_i2c_remove(struct i2c_client *client)
return 0;
}
-#ifdef CONFIG_PM
-static int wm8731_i2c_suspend(struct i2c_client *i2c, pm_message_t msg)
-{
- return snd_soc_suspend_device(&i2c->dev);
-}
-
-static int wm8731_i2c_resume(struct i2c_client *i2c)
-{
- return snd_soc_resume_device(&i2c->dev);
-}
-#else
-#define wm8731_i2c_suspend NULL
-#define wm8731_i2c_resume NULL
-#endif
-
static const struct i2c_device_id wm8731_i2c_id[] = {
{ "wm8731", 0 },
{ }
@@ -707,8 +709,6 @@ static struct i2c_driver wm8731_i2c_driver = {
},
.probe = wm8731_i2c_probe,
.remove = __devexit_p(wm8731_i2c_remove),
- .suspend = wm8731_i2c_suspend,
- .resume = wm8731_i2c_resume,
.id_table = wm8731_i2c_id,
};
#endif
diff --git a/sound/soc/codecs/wm8750.c b/sound/soc/codecs/wm8750.c
index 4ba1e7e93fb4..475c67ac7818 100644
--- a/sound/soc/codecs/wm8750.c
+++ b/sound/soc/codecs/wm8750.c
@@ -403,7 +403,6 @@ static int wm8750_add_widgets(struct snd_soc_codec *codec)
snd_soc_dapm_add_routes(codec, audio_map, ARRAY_SIZE(audio_map));
- snd_soc_dapm_new_widgets(codec);
return 0;
}
@@ -772,16 +771,8 @@ static int wm8750_init(struct snd_soc_device *socdev,
snd_soc_add_controls(codec, wm8750_snd_controls,
ARRAY_SIZE(wm8750_snd_controls));
wm8750_add_widgets(codec);
- ret = snd_soc_init_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);
err:
kfree(codec->reg_cache);
return ret;
diff --git a/sound/soc/codecs/wm8753.c b/sound/soc/codecs/wm8753.c
index 5ad677ce80da..d6850dacda29 100644
--- a/sound/soc/codecs/wm8753.c
+++ b/sound/soc/codecs/wm8753.c
@@ -673,7 +673,6 @@ static int wm8753_add_widgets(struct snd_soc_codec *codec)
snd_soc_dapm_add_routes(codec, audio_map, ARRAY_SIZE(audio_map));
- snd_soc_dapm_new_widgets(codec);
return 0;
}
@@ -724,8 +723,8 @@ static void pll_factors(struct _pll_div *pll_div, unsigned int target,
pll_div->k = K;
}
-static int wm8753_set_dai_pll(struct snd_soc_dai *codec_dai,
- int pll_id, unsigned int freq_in, unsigned int freq_out)
+static int wm8753_set_dai_pll(struct snd_soc_dai *codec_dai, int pll_id,
+ int source, unsigned int freq_in, unsigned int freq_out)
{
u16 reg, enable;
int offset;
@@ -1583,18 +1582,9 @@ static int wm8753_probe(struct platform_device *pdev)
snd_soc_add_controls(codec, wm8753_snd_controls,
ARRAY_SIZE(wm8753_snd_controls));
wm8753_add_widgets(codec);
- ret = snd_soc_init_card(socdev);
- if (ret < 0) {
- printk(KERN_ERR "wm8753: failed to register card\n");
- goto card_err;
- }
return 0;
-card_err:
- snd_soc_free_pcms(socdev);
- snd_soc_dapm_free(socdev);
-
pcm_err:
return ret;
}
@@ -1767,21 +1757,6 @@ static int wm8753_i2c_remove(struct i2c_client *client)
return 0;
}
-#ifdef CONFIG_PM
-static int wm8753_i2c_suspend(struct i2c_client *client, pm_message_t msg)
-{
- return snd_soc_suspend_device(&client->dev);
-}
-
-static int wm8753_i2c_resume(struct i2c_client *client)
-{
- return snd_soc_resume_device(&client->dev);
-}
-#else
-#define wm8753_i2c_suspend NULL
-#define wm8753_i2c_resume NULL
-#endif
-
static const struct i2c_device_id wm8753_i2c_id[] = {
{ "wm8753", 0 },
{ }
@@ -1795,8 +1770,6 @@ static struct i2c_driver wm8753_i2c_driver = {
},
.probe = wm8753_i2c_probe,
.remove = wm8753_i2c_remove,
- .suspend = wm8753_i2c_suspend,
- .resume = wm8753_i2c_resume,
.id_table = wm8753_i2c_id,
};
#endif
@@ -1852,22 +1825,6 @@ static int __devexit wm8753_spi_remove(struct spi_device *spi)
return 0;
}
-#ifdef CONFIG_PM
-static int wm8753_spi_suspend(struct spi_device *spi, pm_message_t msg)
-{
- return snd_soc_suspend_device(&spi->dev);
-}
-
-static int wm8753_spi_resume(struct spi_device *spi)
-{
- return snd_soc_resume_device(&spi->dev);
-}
-
-#else
-#define wm8753_spi_suspend NULL
-#define wm8753_spi_resume NULL
-#endif
-
static struct spi_driver wm8753_spi_driver = {
.driver = {
.name = "wm8753",
@@ -1876,8 +1833,6 @@ static struct spi_driver wm8753_spi_driver = {
},
.probe = wm8753_spi_probe,
.remove = __devexit_p(wm8753_spi_remove),
- .suspend = wm8753_spi_suspend,
- .resume = wm8753_spi_resume,
};
#endif
diff --git a/sound/soc/codecs/wm8776.c b/sound/soc/codecs/wm8776.c
index a9829aa26e53..ab2c0da18091 100644
--- a/sound/soc/codecs/wm8776.c
+++ b/sound/soc/codecs/wm8776.c
@@ -447,17 +447,8 @@ static int wm8776_probe(struct platform_device *pdev)
ARRAY_SIZE(wm8776_dapm_widgets));
snd_soc_dapm_add_routes(codec, routes, ARRAY_SIZE(routes));
- ret = snd_soc_init_card(socdev);
- if (ret < 0) {
- dev_err(codec->dev, "failed to register card: %d\n", ret);
- goto card_err;
- }
-
return ret;
-card_err:
- snd_soc_free_pcms(socdev);
- snd_soc_dapm_free(socdev);
pcm_err:
return ret;
}
@@ -616,21 +607,6 @@ static int __devexit wm8776_spi_remove(struct spi_device *spi)
return 0;
}
-#ifdef CONFIG_PM
-static int wm8776_spi_suspend(struct spi_device *spi, pm_message_t msg)
-{
- return snd_soc_suspend_device(&spi->dev);
-}
-
-static int wm8776_spi_resume(struct spi_device *spi)
-{
- return snd_soc_resume_device(&spi->dev);
-}
-#else
-#define wm8776_spi_suspend NULL
-#define wm8776_spi_resume NULL
-#endif
-
static struct spi_driver wm8776_spi_driver = {
.driver = {
.name = "wm8776",
@@ -638,8 +614,6 @@ static struct spi_driver wm8776_spi_driver = {
.owner = THIS_MODULE,
},
.probe = wm8776_spi_probe,
- .suspend = wm8776_spi_suspend,
- .resume = wm8776_spi_resume,
.remove = __devexit_p(wm8776_spi_remove),
};
#endif /* CONFIG_SPI_MASTER */
@@ -673,21 +647,6 @@ static __devexit int wm8776_i2c_remove(struct i2c_client *client)
return 0;
}
-#ifdef CONFIG_PM
-static int wm8776_i2c_suspend(struct i2c_client *i2c, pm_message_t msg)
-{
- return snd_soc_suspend_device(&i2c->dev);
-}
-
-static int wm8776_i2c_resume(struct i2c_client *i2c)
-{
- return snd_soc_resume_device(&i2c->dev);
-}
-#else
-#define wm8776_i2c_suspend NULL
-#define wm8776_i2c_resume NULL
-#endif
-
static const struct i2c_device_id wm8776_i2c_id[] = {
{ "wm8776", 0 },
{ }
@@ -701,8 +660,6 @@ static struct i2c_driver wm8776_i2c_driver = {
},
.probe = wm8776_i2c_probe,
.remove = __devexit_p(wm8776_i2c_remove),
- .suspend = wm8776_i2c_suspend,
- .resume = wm8776_i2c_resume,
.id_table = wm8776_i2c_id,
};
#endif
diff --git a/sound/soc/codecs/wm8900.c b/sound/soc/codecs/wm8900.c
index 5e9c855c0036..c9438dd62df3 100644
--- a/sound/soc/codecs/wm8900.c
+++ b/sound/soc/codecs/wm8900.c
@@ -618,8 +618,6 @@ static int wm8900_add_widgets(struct snd_soc_codec *codec)
snd_soc_dapm_add_routes(codec, audio_map, ARRAY_SIZE(audio_map));
- snd_soc_dapm_new_widgets(codec);
-
return 0;
}
@@ -814,8 +812,8 @@ reenable:
return 0;
}
-static int wm8900_set_dai_pll(struct snd_soc_dai *codec_dai,
- int pll_id, unsigned int freq_in, unsigned int freq_out)
+static int wm8900_set_dai_pll(struct snd_soc_dai *codec_dai, int pll_id,
+ int source, unsigned int freq_in, unsigned int freq_out)
{
return wm8900_set_fll(codec_dai->codec, pll_id, freq_in, freq_out);
}
@@ -1312,21 +1310,6 @@ static __devexit int wm8900_i2c_remove(struct i2c_client *client)
return 0;
}
-#ifdef CONFIG_PM
-static int wm8900_i2c_suspend(struct i2c_client *client, pm_message_t msg)
-{
- return snd_soc_suspend_device(&client->dev);
-}
-
-static int wm8900_i2c_resume(struct i2c_client *client)
-{
- return snd_soc_resume_device(&client->dev);
-}
-#else
-#define wm8900_i2c_suspend NULL
-#define wm8900_i2c_resume NULL
-#endif
-
static const struct i2c_device_id wm8900_i2c_id[] = {
{ "wm8900", 0 },
{ }
@@ -1340,8 +1323,6 @@ static struct i2c_driver wm8900_i2c_driver = {
},
.probe = wm8900_i2c_probe,
.remove = __devexit_p(wm8900_i2c_remove),
- .suspend = wm8900_i2c_suspend,
- .resume = wm8900_i2c_resume,
.id_table = wm8900_i2c_id,
};
@@ -1370,17 +1351,6 @@ static int wm8900_probe(struct platform_device *pdev)
ARRAY_SIZE(wm8900_snd_controls));
wm8900_add_widgets(codec);
- ret = snd_soc_init_card(socdev);
- if (ret < 0) {
- dev_err(&pdev->dev, "Failed to register card\n");
- goto card_err;
- }
-
- return ret;
-
-card_err:
- snd_soc_free_pcms(socdev);
- snd_soc_dapm_free(socdev);
pcm_err:
return ret;
}
diff --git a/sound/soc/codecs/wm8903.c b/sound/soc/codecs/wm8903.c
index fe1307b500cf..b8cae1758642 100644
--- a/sound/soc/codecs/wm8903.c
+++ b/sound/soc/codecs/wm8903.c
@@ -919,8 +919,6 @@ static int wm8903_add_widgets(struct snd_soc_codec *codec)
snd_soc_dapm_add_routes(codec, intercon, ARRAY_SIZE(intercon));
- snd_soc_dapm_new_widgets(codec);
-
return 0;
}
@@ -1655,21 +1653,6 @@ static __devexit int wm8903_i2c_remove(struct i2c_client *client)
return 0;
}
-#ifdef CONFIG_PM
-static int wm8903_i2c_suspend(struct i2c_client *client, pm_message_t msg)
-{
- return snd_soc_suspend_device(&client->dev);
-}
-
-static int wm8903_i2c_resume(struct i2c_client *client)
-{
- return snd_soc_resume_device(&client->dev);
-}
-#else
-#define wm8903_i2c_suspend NULL
-#define wm8903_i2c_resume NULL
-#endif
-
/* i2c codec control layer */
static const struct i2c_device_id wm8903_i2c_id[] = {
{ "wm8903", 0 },
@@ -1684,8 +1667,6 @@ static struct i2c_driver wm8903_i2c_driver = {
},
.probe = wm8903_i2c_probe,
.remove = __devexit_p(wm8903_i2c_remove),
- .suspend = wm8903_i2c_suspend,
- .resume = wm8903_i2c_resume,
.id_table = wm8903_i2c_id,
};
@@ -1712,17 +1693,8 @@ static int wm8903_probe(struct platform_device *pdev)
ARRAY_SIZE(wm8903_snd_controls));
wm8903_add_widgets(socdev->card->codec);
- ret = snd_soc_init_card(socdev);
- if (ret < 0) {
- dev_err(&pdev->dev, "wm8903: failed to register card\n");
- goto card_err;
- }
-
return ret;
-card_err:
- snd_soc_free_pcms(socdev);
- snd_soc_dapm_free(socdev);
err:
return ret;
}
diff --git a/sound/soc/codecs/wm8940.c b/sound/soc/codecs/wm8940.c
index 1ef2454c5205..3d850b97037a 100644
--- a/sound/soc/codecs/wm8940.c
+++ b/sound/soc/codecs/wm8940.c
@@ -298,7 +298,6 @@ static int wm8940_add_widgets(struct snd_soc_codec *codec)
ret = snd_soc_dapm_add_routes(codec, audio_map, ARRAY_SIZE(audio_map));
if (ret)
goto error_ret;
- ret = snd_soc_dapm_new_widgets(codec);
error_ret:
return ret;
@@ -536,8 +535,8 @@ static void pll_factors(unsigned int target, unsigned int source)
}
/* Untested at the moment */
-static int wm8940_set_dai_pll(struct snd_soc_dai *codec_dai,
- int pll_id, unsigned int freq_in, unsigned int freq_out)
+static int wm8940_set_dai_pll(struct snd_soc_dai *codec_dai, int pll_id,
+ int source, unsigned int freq_in, unsigned int freq_out)
{
struct snd_soc_codec *codec = codec_dai->codec;
u16 reg;
@@ -731,12 +730,6 @@ static int wm8940_probe(struct platform_device *pdev)
if (ret)
goto error_free_pcms;
- ret = snd_soc_init_card(socdev);
- if (ret < 0) {
- dev_err(codec->dev, "failed to register card: %d\n", ret);
- goto error_free_pcms;
- }
-
return ret;
error_free_pcms:
@@ -877,21 +870,6 @@ static int __devexit wm8940_i2c_remove(struct i2c_client *client)
return 0;
}
-#ifdef CONFIG_PM
-static int wm8940_i2c_suspend(struct i2c_client *client, pm_message_t msg)
-{
- return snd_soc_suspend_device(&client->dev);
-}
-
-static int wm8940_i2c_resume(struct i2c_client *client)
-{
- return snd_soc_resume_device(&client->dev);
-}
-#else
-#define wm8940_i2c_suspend NULL
-#define wm8940_i2c_resume NULL
-#endif
-
static const struct i2c_device_id wm8940_i2c_id[] = {
{ "wm8940", 0 },
{ }
@@ -905,8 +883,6 @@ static struct i2c_driver wm8940_i2c_driver = {
},
.probe = wm8940_i2c_probe,
.remove = __devexit_p(wm8940_i2c_remove),
- .suspend = wm8940_i2c_suspend,
- .resume = wm8940_i2c_resume,
.id_table = wm8940_i2c_id,
};
diff --git a/sound/soc/codecs/wm8960.c b/sound/soc/codecs/wm8960.c
index f59703be61c8..d07bcc1e1c60 100644
--- a/sound/soc/codecs/wm8960.c
+++ b/sound/soc/codecs/wm8960.c
@@ -307,7 +307,6 @@ static int wm8960_add_widgets(struct snd_soc_codec *codec)
snd_soc_dapm_add_routes(codec, audio_paths, ARRAY_SIZE(audio_paths));
- snd_soc_dapm_new_widgets(codec);
return 0;
}
@@ -540,8 +539,8 @@ static int pll_factors(unsigned int source, unsigned int target,
return 0;
}
-static int wm8960_set_dai_pll(struct snd_soc_dai *codec_dai,
- int pll_id, unsigned int freq_in, unsigned int freq_out)
+static int wm8960_set_dai_pll(struct snd_soc_dai *codec_dai, int pll_id,
+ int source, unsigned int freq_in, unsigned int freq_out)
{
struct snd_soc_codec *codec = codec_dai->codec;
u16 reg;
@@ -713,17 +712,9 @@ static int wm8960_probe(struct platform_device *pdev)
snd_soc_add_controls(codec, wm8960_snd_controls,
ARRAY_SIZE(wm8960_snd_controls));
wm8960_add_widgets(codec);
- ret = snd_soc_init_card(socdev);
- if (ret < 0) {
- dev_err(codec->dev, "failed to register card: %d\n", ret);
- goto card_err;
- }
return ret;
-card_err:
- snd_soc_free_pcms(socdev);
- snd_soc_dapm_free(socdev);
pcm_err:
return ret;
}
@@ -883,21 +874,6 @@ static __devexit int wm8960_i2c_remove(struct i2c_client *client)
return 0;
}
-#ifdef CONFIG_PM
-static int wm8960_i2c_suspend(struct i2c_client *client, pm_message_t msg)
-{
- return snd_soc_suspend_device(&client->dev);
-}
-
-static int wm8960_i2c_resume(struct i2c_client *client)
-{
- return snd_soc_resume_device(&client->dev);
-}
-#else
-#define wm8960_i2c_suspend NULL
-#define wm8960_i2c_resume NULL
-#endif
-
static const struct i2c_device_id wm8960_i2c_id[] = {
{ "wm8960", 0 },
{ }
@@ -911,8 +887,6 @@ static struct i2c_driver wm8960_i2c_driver = {
},
.probe = wm8960_i2c_probe,
.remove = __devexit_p(wm8960_i2c_remove),
- .suspend = wm8960_i2c_suspend,
- .resume = wm8960_i2c_resume,
.id_table = wm8960_i2c_id,
};
diff --git a/sound/soc/codecs/wm8961.c b/sound/soc/codecs/wm8961.c
index 503032085899..a8007d58813f 100644
--- a/sound/soc/codecs/wm8961.c
+++ b/sound/soc/codecs/wm8961.c
@@ -986,19 +986,9 @@ static int wm8961_probe(struct platform_device *pdev)
snd_soc_dapm_new_controls(codec, wm8961_dapm_widgets,
ARRAY_SIZE(wm8961_dapm_widgets));
snd_soc_dapm_add_routes(codec, audio_paths, ARRAY_SIZE(audio_paths));
- snd_soc_dapm_new_widgets(codec);
-
- ret = snd_soc_init_card(socdev);
- if (ret < 0) {
- dev_err(codec->dev, "failed to register card: %d\n", ret);
- goto card_err;
- }
return ret;
-card_err:
- snd_soc_free_pcms(socdev);
- snd_soc_dapm_free(socdev);
pcm_err:
return ret;
}
@@ -1206,21 +1196,6 @@ static __devexit int wm8961_i2c_remove(struct i2c_client *client)
return 0;
}
-#ifdef CONFIG_PM
-static int wm8961_i2c_suspend(struct i2c_client *client, pm_message_t state)
-{
- return snd_soc_suspend_device(&client->dev);
-}
-
-static int wm8961_i2c_resume(struct i2c_client *client)
-{
- return snd_soc_resume_device(&client->dev);
-}
-#else
-#define wm8961_i2c_suspend NULL
-#define wm8961_i2c_resume NULL
-#endif
-
static const struct i2c_device_id wm8961_i2c_id[] = {
{ "wm8961", 0 },
{ }
@@ -1234,8 +1209,6 @@ static struct i2c_driver wm8961_i2c_driver = {
},
.probe = wm8961_i2c_probe,
.remove = __devexit_p(wm8961_i2c_remove),
- .suspend = wm8961_i2c_suspend,
- .resume = wm8961_i2c_resume,
.id_table = wm8961_i2c_id,
};
diff --git a/sound/soc/codecs/wm8971.c b/sound/soc/codecs/wm8971.c
index d66efb0546ea..d9540d55fc89 100644
--- a/sound/soc/codecs/wm8971.c
+++ b/sound/soc/codecs/wm8971.c
@@ -338,8 +338,6 @@ static int wm8971_add_widgets(struct snd_soc_codec *codec)
snd_soc_dapm_add_routes(codec, audio_map, ARRAY_SIZE(audio_map));
- snd_soc_dapm_new_widgets(codec);
-
return 0;
}
@@ -703,16 +701,9 @@ static int wm8971_init(struct snd_soc_device *socdev,
snd_soc_add_controls(codec, wm8971_snd_controls,
ARRAY_SIZE(wm8971_snd_controls));
wm8971_add_widgets(codec);
- ret = snd_soc_init_card(socdev);
- if (ret < 0) {
- printk(KERN_ERR "wm8971: failed to register card\n");
- goto card_err;
- }
+
return ret;
-card_err:
- snd_soc_free_pcms(socdev);
- snd_soc_dapm_free(socdev);
err:
kfree(codec->reg_cache);
return ret;
diff --git a/sound/soc/codecs/wm8974.c b/sound/soc/codecs/wm8974.c
index 98d663afc97d..81c57b5c591c 100644
--- a/sound/soc/codecs/wm8974.c
+++ b/sound/soc/codecs/wm8974.c
@@ -276,41 +276,42 @@ static int wm8974_add_widgets(struct snd_soc_codec *codec)
snd_soc_dapm_add_routes(codec, audio_map, ARRAY_SIZE(audio_map));
- snd_soc_dapm_new_widgets(codec);
return 0;
}
struct pll_ {
- unsigned int pre_div:4; /* prescale - 1 */
+ unsigned int pre_div:1;
unsigned int n:4;
unsigned int k;
};
-static struct pll_ pll_div;
-
/* The size in bits of the pll divide multiplied by 10
* to allow rounding later */
#define FIXED_PLL_SIZE ((1 << 24) * 10)
-static void pll_factors(unsigned int target, unsigned int source)
+static void pll_factors(struct pll_ *pll_div,
+ unsigned int target, unsigned int source)
{
unsigned long long Kpart;
unsigned int K, Ndiv, Nmod;
+ /* There is a fixed divide by 4 in the output path */
+ target *= 4;
+
Ndiv = target / source;
if (Ndiv < 6) {
- source >>= 1;
- pll_div.pre_div = 1;
+ source /= 2;
+ pll_div->pre_div = 1;
Ndiv = target / source;
} else
- pll_div.pre_div = 0;
+ pll_div->pre_div = 0;
if ((Ndiv < 6) || (Ndiv > 12))
printk(KERN_WARNING
"WM8974 N value %u outwith recommended range!\n",
Ndiv);
- pll_div.n = Ndiv;
+ pll_div->n = Ndiv;
Nmod = target % source;
Kpart = FIXED_PLL_SIZE * (long long)Nmod;
@@ -325,13 +326,14 @@ static void pll_factors(unsigned int target, unsigned int source)
/* Move down to proper range now rounding is done */
K /= 10;
- pll_div.k = K;
+ pll_div->k = K;
}
-static int wm8974_set_dai_pll(struct snd_soc_dai *codec_dai,
- int pll_id, unsigned int freq_in, unsigned int freq_out)
+static int wm8974_set_dai_pll(struct snd_soc_dai *codec_dai, int pll_id,
+ int source, unsigned int freq_in, unsigned int freq_out)
{
struct snd_soc_codec *codec = codec_dai->codec;
+ struct pll_ pll_div;
u16 reg;
if (freq_in == 0 || freq_out == 0) {
@@ -345,7 +347,7 @@ static int wm8974_set_dai_pll(struct snd_soc_dai *codec_dai,
return 0;
}
- pll_factors(freq_out*4, freq_in);
+ pll_factors(&pll_div, freq_out, freq_in);
snd_soc_write(codec, WM8974_PLLN, (pll_div.pre_div << 4) | pll_div.n);
snd_soc_write(codec, WM8974_PLLK1, pll_div.k >> 18);
@@ -638,17 +640,9 @@ static int wm8974_probe(struct platform_device *pdev)
snd_soc_add_controls(codec, wm8974_snd_controls,
ARRAY_SIZE(wm8974_snd_controls));
wm8974_add_widgets(codec);
- ret = snd_soc_init_card(socdev);
- if (ret < 0) {
- dev_err(codec->dev, "failed to register card: %d\n", ret);
- goto card_err;
- }
return ret;
-card_err:
- snd_soc_free_pcms(socdev);
- snd_soc_dapm_free(socdev);
pcm_err:
return ret;
}
diff --git a/sound/soc/codecs/wm8988.c b/sound/soc/codecs/wm8988.c
index 3f530f8a972a..2862e4dced27 100644
--- a/sound/soc/codecs/wm8988.c
+++ b/sound/soc/codecs/wm8988.c
@@ -790,19 +790,9 @@ static int wm8988_probe(struct platform_device *pdev)
snd_soc_dapm_new_controls(codec, wm8988_dapm_widgets,
ARRAY_SIZE(wm8988_dapm_widgets));
snd_soc_dapm_add_routes(codec, audio_map, ARRAY_SIZE(audio_map));
- snd_soc_dapm_new_widgets(codec);
-
- ret = snd_soc_init_card(socdev);
- if (ret < 0) {
- dev_err(codec->dev, "failed to register card: %d\n", ret);
- goto card_err;
- }
return ret;
-card_err:
- snd_soc_free_pcms(socdev);
- snd_soc_dapm_free(socdev);
pcm_err:
return ret;
}
@@ -944,21 +934,6 @@ static int wm8988_i2c_remove(struct i2c_client *client)
return 0;
}
-#ifdef CONFIG_PM
-static int wm8988_i2c_suspend(struct i2c_client *client, pm_message_t msg)
-{
- return snd_soc_suspend_device(&client->dev);
-}
-
-static int wm8988_i2c_resume(struct i2c_client *client)
-{
- return snd_soc_resume_device(&client->dev);
-}
-#else
-#define wm8988_i2c_suspend NULL
-#define wm8988_i2c_resume NULL
-#endif
-
static const struct i2c_device_id wm8988_i2c_id[] = {
{ "wm8988", 0 },
{ }
@@ -972,8 +947,6 @@ static struct i2c_driver wm8988_i2c_driver = {
},
.probe = wm8988_i2c_probe,
.remove = wm8988_i2c_remove,
- .suspend = wm8988_i2c_suspend,
- .resume = wm8988_i2c_resume,
.id_table = wm8988_i2c_id,
};
#endif
@@ -1006,21 +979,6 @@ static int __devexit wm8988_spi_remove(struct spi_device *spi)
return 0;
}
-#ifdef CONFIG_PM
-static int wm8988_spi_suspend(struct spi_device *spi, pm_message_t msg)
-{
- return snd_soc_suspend_device(&spi->dev);
-}
-
-static int wm8988_spi_resume(struct spi_device *spi)
-{
- return snd_soc_resume_device(&spi->dev);
-}
-#else
-#define wm8988_spi_suspend NULL
-#define wm8988_spi_resume NULL
-#endif
-
static struct spi_driver wm8988_spi_driver = {
.driver = {
.name = "wm8988",
@@ -1029,8 +987,6 @@ static struct spi_driver wm8988_spi_driver = {
},
.probe = wm8988_spi_probe,
.remove = __devexit_p(wm8988_spi_remove),
- .suspend = wm8988_spi_suspend,
- .resume = wm8988_spi_resume,
};
#endif
diff --git a/sound/soc/codecs/wm8990.c b/sound/soc/codecs/wm8990.c
index 2d702db4131d..341481e0e830 100644
--- a/sound/soc/codecs/wm8990.c
+++ b/sound/soc/codecs/wm8990.c
@@ -920,7 +920,6 @@ static int wm8990_add_widgets(struct snd_soc_codec *codec)
/* set up the WM8990 audio map */
snd_soc_dapm_add_routes(codec, audio_map, ARRAY_SIZE(audio_map));
- snd_soc_dapm_new_widgets(codec);
return 0;
}
@@ -972,8 +971,8 @@ static void pll_factors(struct _pll_div *pll_div, unsigned int target,
pll_div->k = K;
}
-static int wm8990_set_dai_pll(struct snd_soc_dai *codec_dai,
- int pll_id, unsigned int freq_in, unsigned int freq_out)
+static int wm8990_set_dai_pll(struct snd_soc_dai *codec_dai, int pll_id,
+ int source, unsigned int freq_in, unsigned int freq_out)
{
u16 reg;
struct snd_soc_codec *codec = codec_dai->codec;
@@ -1409,16 +1408,9 @@ static int wm8990_init(struct snd_soc_device *socdev)
snd_soc_add_controls(codec, wm8990_snd_controls,
ARRAY_SIZE(wm8990_snd_controls));
wm8990_add_widgets(codec);
- ret = snd_soc_init_card(socdev);
- if (ret < 0) {
- printk(KERN_ERR "wm8990: 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;
diff --git a/sound/soc/codecs/wm8993.c b/sound/soc/codecs/wm8993.c
index d9987999e92c..5e32f2ed5fc2 100644
--- a/sound/soc/codecs/wm8993.c
+++ b/sound/soc/codecs/wm8993.c
@@ -422,7 +422,7 @@ static int fll_factors(struct _fll_div *fll_div, unsigned int Fref,
return 0;
}
-static int wm8993_set_fll(struct snd_soc_dai *dai, int fll_id,
+static int wm8993_set_fll(struct snd_soc_dai *dai, int fll_id, int source,
unsigned int Fref, unsigned int Fout)
{
struct snd_soc_codec *codec = dai->codec;
@@ -1464,19 +1464,8 @@ static int wm8993_probe(struct platform_device *pdev)
wm_hubs_add_analogue_routes(codec, wm8993->pdata.lineout1_diff,
wm8993->pdata.lineout2_diff);
- snd_soc_dapm_new_widgets(codec);
-
- ret = snd_soc_init_card(socdev);
- if (ret < 0) {
- dev_err(codec->dev, "failed to register card\n");
- goto card_err;
- }
-
return ret;
-card_err:
- snd_soc_free_pcms(socdev);
- snd_soc_dapm_free(socdev);
err:
return ret;
}
@@ -1572,33 +1561,15 @@ static int wm8993_i2c_probe(struct i2c_client *i2c,
/* Use automatic clock configuration */
snd_soc_update_bits(codec, WM8993_CLOCKING_4, WM8993_SR_MODE, 0);
- if (!wm8993->pdata.lineout1_diff)
- snd_soc_update_bits(codec, WM8993_LINE_MIXER1,
- WM8993_LINEOUT1_MODE,
- WM8993_LINEOUT1_MODE);
- if (!wm8993->pdata.lineout2_diff)
- snd_soc_update_bits(codec, WM8993_LINE_MIXER2,
- WM8993_LINEOUT2_MODE,
- WM8993_LINEOUT2_MODE);
-
- if (wm8993->pdata.lineout1fb)
- snd_soc_update_bits(codec, WM8993_ADDITIONAL_CONTROL,
- WM8993_LINEOUT1_FB, WM8993_LINEOUT1_FB);
-
- if (wm8993->pdata.lineout2fb)
- snd_soc_update_bits(codec, WM8993_ADDITIONAL_CONTROL,
- WM8993_LINEOUT2_FB, WM8993_LINEOUT2_FB);
-
- /* Apply the microphone bias/detection configuration - the
- * platform data is directly applicable to the register. */
- snd_soc_update_bits(codec, WM8993_MICBIAS,
- WM8993_JD_SCTHR_MASK | WM8993_JD_THR_MASK |
- WM8993_MICB1_LVL | WM8993_MICB2_LVL,
- wm8993->pdata.jd_scthr << WM8993_JD_SCTHR_SHIFT |
- wm8993->pdata.jd_thr << WM8993_JD_THR_SHIFT |
- wm8993->pdata.micbias1_lvl |
- wm8993->pdata.micbias1_lvl << 1);
-
+ wm_hubs_handle_analogue_pdata(codec, wm8993->pdata.lineout1_diff,
+ wm8993->pdata.lineout2_diff,
+ wm8993->pdata.lineout1fb,
+ wm8993->pdata.lineout2fb,
+ wm8993->pdata.jd_scthr,
+ wm8993->pdata.jd_thr,
+ wm8993->pdata.micbias1_lvl,
+ wm8993->pdata.micbias2_lvl);
+
ret = wm8993_set_bias_level(codec, SND_SOC_BIAS_STANDBY);
if (ret != 0)
goto err;
diff --git a/sound/soc/codecs/wm9081.c b/sound/soc/codecs/wm9081.c
index 686e5aa97206..c468497314ba 100644
--- a/sound/soc/codecs/wm9081.c
+++ b/sound/soc/codecs/wm9081.c
@@ -1262,19 +1262,9 @@ static int wm9081_probe(struct platform_device *pdev)
snd_soc_dapm_new_controls(codec, wm9081_dapm_widgets,
ARRAY_SIZE(wm9081_dapm_widgets));
snd_soc_dapm_add_routes(codec, audio_paths, ARRAY_SIZE(audio_paths));
- snd_soc_dapm_new_widgets(codec);
-
- ret = snd_soc_init_card(socdev);
- if (ret < 0) {
- dev_err(codec->dev, "failed to register card: %d\n", ret);
- goto card_err;
- }
return ret;
-card_err:
- snd_soc_free_pcms(socdev);
- snd_soc_dapm_free(socdev);
pcm_err:
return ret;
}
@@ -1452,21 +1442,6 @@ static __devexit int wm9081_i2c_remove(struct i2c_client *client)
return 0;
}
-#ifdef CONFIG_PM
-static int wm9081_i2c_suspend(struct i2c_client *client, pm_message_t msg)
-{
- return snd_soc_suspend_device(&client->dev);
-}
-
-static int wm9081_i2c_resume(struct i2c_client *client)
-{
- return snd_soc_resume_device(&client->dev);
-}
-#else
-#define wm9081_i2c_suspend NULL
-#define wm9081_i2c_resume NULL
-#endif
-
static const struct i2c_device_id wm9081_i2c_id[] = {
{ "wm9081", 0 },
{ }
@@ -1480,8 +1455,6 @@ static struct i2c_driver wm9081_i2c_driver = {
},
.probe = wm9081_i2c_probe,
.remove = __devexit_p(wm9081_i2c_remove),
- .suspend = wm9081_i2c_suspend,
- .resume = wm9081_i2c_resume,
.id_table = wm9081_i2c_id,
};
diff --git a/sound/soc/codecs/wm9705.c b/sound/soc/codecs/wm9705.c
index e7d2840d9e59..ec54c6da9856 100644
--- a/sound/soc/codecs/wm9705.c
+++ b/sound/soc/codecs/wm9705.c
@@ -205,7 +205,6 @@ static int wm9705_add_widgets(struct snd_soc_codec *codec)
snd_soc_dapm_new_controls(codec, wm9705_dapm_widgets,
ARRAY_SIZE(wm9705_dapm_widgets));
snd_soc_dapm_add_routes(codec, audio_map, ARRAY_SIZE(audio_map));
- snd_soc_dapm_new_widgets(codec);
return 0;
}
@@ -403,12 +402,6 @@ static int wm9705_soc_probe(struct platform_device *pdev)
ARRAY_SIZE(wm9705_snd_ac97_controls));
wm9705_add_widgets(codec);
- ret = snd_soc_init_card(socdev);
- if (ret < 0) {
- printk(KERN_ERR "wm9705: failed to register card\n");
- goto reset_err;
- }
-
return 0;
reset_err:
diff --git a/sound/soc/codecs/wm9712.c b/sound/soc/codecs/wm9712.c
index 1fd4e88f50cf..0ac1215dcd9b 100644
--- a/sound/soc/codecs/wm9712.c
+++ b/sound/soc/codecs/wm9712.c
@@ -436,7 +436,6 @@ static int wm9712_add_widgets(struct snd_soc_codec *codec)
snd_soc_dapm_add_routes(codec, audio_map, ARRAY_SIZE(audio_map));
- snd_soc_dapm_new_widgets(codec);
return 0;
}
@@ -695,17 +694,11 @@ static int wm9712_soc_probe(struct platform_device *pdev)
snd_soc_add_controls(codec, wm9712_snd_ac97_controls,
ARRAY_SIZE(wm9712_snd_ac97_controls));
wm9712_add_widgets(codec);
- ret = snd_soc_init_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);
diff --git a/sound/soc/codecs/wm9713.c b/sound/soc/codecs/wm9713.c
index abed37acf787..c58aab375edb 100644
--- a/sound/soc/codecs/wm9713.c
+++ b/sound/soc/codecs/wm9713.c
@@ -165,9 +165,9 @@ SOC_SINGLE("Mono Playback Switch", AC97_MASTER_TONE, 7, 1, 1),
SOC_SINGLE("Mono Playback ZC Switch", AC97_MASTER_TONE, 6, 1, 0),
SOC_SINGLE("Mono Playback Volume", AC97_MASTER_TONE, 0, 31, 1),
-SOC_SINGLE("PC Beep Playback Headphone Volume", AC97_AUX, 12, 7, 1),
-SOC_SINGLE("PC Beep Playback Speaker Volume", AC97_AUX, 8, 7, 1),
-SOC_SINGLE("PC Beep Playback Mono Volume", AC97_AUX, 4, 7, 1),
+SOC_SINGLE("Beep Playback Headphone Volume", AC97_AUX, 12, 7, 1),
+SOC_SINGLE("Beep Playback Speaker Volume", AC97_AUX, 8, 7, 1),
+SOC_SINGLE("Beep Playback Mono Volume", AC97_AUX, 4, 7, 1),
SOC_SINGLE("Voice Playback Headphone Volume", AC97_PCM, 12, 7, 1),
SOC_SINGLE("Voice Playback Master Volume", AC97_PCM, 8, 7, 1),
@@ -266,7 +266,7 @@ static int mixer_event(struct snd_soc_dapm_widget *w,
/* Left Headphone Mixers */
static const struct snd_kcontrol_new wm9713_hpl_mixer_controls[] = {
-SOC_DAPM_SINGLE("PC Beep Playback Switch", HPL_MIXER, 5, 1, 0),
+SOC_DAPM_SINGLE("Beep Playback Switch", HPL_MIXER, 5, 1, 0),
SOC_DAPM_SINGLE("Voice Playback Switch", HPL_MIXER, 4, 1, 0),
SOC_DAPM_SINGLE("Aux Playback Switch", HPL_MIXER, 3, 1, 0),
SOC_DAPM_SINGLE("PCM Playback Switch", HPL_MIXER, 2, 1, 0),
@@ -276,7 +276,7 @@ SOC_DAPM_SINGLE("Bypass Playback Switch", HPL_MIXER, 0, 1, 0),
/* Right Headphone Mixers */
static const struct snd_kcontrol_new wm9713_hpr_mixer_controls[] = {
-SOC_DAPM_SINGLE("PC Beep Playback Switch", HPR_MIXER, 5, 1, 0),
+SOC_DAPM_SINGLE("Beep Playback Switch", HPR_MIXER, 5, 1, 0),
SOC_DAPM_SINGLE("Voice Playback Switch", HPR_MIXER, 4, 1, 0),
SOC_DAPM_SINGLE("Aux Playback Switch", HPR_MIXER, 3, 1, 0),
SOC_DAPM_SINGLE("PCM Playback Switch", HPR_MIXER, 2, 1, 0),
@@ -294,7 +294,7 @@ SOC_DAPM_ENUM("Route", wm9713_enum[0]);
/* Speaker Mixer */
static const struct snd_kcontrol_new wm9713_speaker_mixer_controls[] = {
-SOC_DAPM_SINGLE("PC Beep Playback Switch", AC97_AUX, 11, 1, 1),
+SOC_DAPM_SINGLE("Beep Playback Switch", AC97_AUX, 11, 1, 1),
SOC_DAPM_SINGLE("Voice Playback Switch", AC97_PCM, 11, 1, 1),
SOC_DAPM_SINGLE("Aux Playback Switch", AC97_REC_SEL, 11, 1, 1),
SOC_DAPM_SINGLE("PCM Playback Switch", AC97_PHONE, 14, 1, 1),
@@ -304,7 +304,7 @@ SOC_DAPM_SINGLE("Bypass Playback Switch", AC97_PC_BEEP, 14, 1, 1),
/* Mono Mixer */
static const struct snd_kcontrol_new wm9713_mono_mixer_controls[] = {
-SOC_DAPM_SINGLE("PC Beep Playback Switch", AC97_AUX, 7, 1, 1),
+SOC_DAPM_SINGLE("Beep Playback Switch", AC97_AUX, 7, 1, 1),
SOC_DAPM_SINGLE("Voice Playback Switch", AC97_PCM, 7, 1, 1),
SOC_DAPM_SINGLE("Aux Playback Switch", AC97_REC_SEL, 7, 1, 1),
SOC_DAPM_SINGLE("PCM Playback Switch", AC97_PHONE, 13, 1, 1),
@@ -463,7 +463,7 @@ SND_SOC_DAPM_VMID("VMID"),
static const struct snd_soc_dapm_route audio_map[] = {
/* left HP mixer */
- {"Left HP Mixer", "PC Beep Playback Switch", "PCBEEP"},
+ {"Left HP Mixer", "Beep Playback Switch", "PCBEEP"},
{"Left HP Mixer", "Voice Playback Switch", "Voice DAC"},
{"Left HP Mixer", "Aux Playback Switch", "Aux DAC"},
{"Left HP Mixer", "Bypass Playback Switch", "Left Line In"},
@@ -472,7 +472,7 @@ static const struct snd_soc_dapm_route audio_map[] = {
{"Left HP Mixer", NULL, "Capture Headphone Mux"},
/* right HP mixer */
- {"Right HP Mixer", "PC Beep Playback Switch", "PCBEEP"},
+ {"Right HP Mixer", "Beep Playback Switch", "PCBEEP"},
{"Right HP Mixer", "Voice Playback Switch", "Voice DAC"},
{"Right HP Mixer", "Aux Playback Switch", "Aux DAC"},
{"Right HP Mixer", "Bypass Playback Switch", "Right Line In"},
@@ -491,7 +491,7 @@ static const struct snd_soc_dapm_route audio_map[] = {
{"Capture Mixer", NULL, "Right Capture Source"},
/* speaker mixer */
- {"Speaker Mixer", "PC Beep Playback Switch", "PCBEEP"},
+ {"Speaker Mixer", "Beep Playback Switch", "PCBEEP"},
{"Speaker Mixer", "Voice Playback Switch", "Voice DAC"},
{"Speaker Mixer", "Aux Playback Switch", "Aux DAC"},
{"Speaker Mixer", "Bypass Playback Switch", "Line Mixer"},
@@ -499,7 +499,7 @@ static const struct snd_soc_dapm_route audio_map[] = {
{"Speaker Mixer", "MonoIn Playback Switch", "Mono In"},
/* mono mixer */
- {"Mono Mixer", "PC Beep Playback Switch", "PCBEEP"},
+ {"Mono Mixer", "Beep Playback Switch", "PCBEEP"},
{"Mono Mixer", "Voice Playback Switch", "Voice DAC"},
{"Mono Mixer", "Aux Playback Switch", "Aux DAC"},
{"Mono Mixer", "Bypass Playback Switch", "Line Mixer"},
@@ -625,7 +625,6 @@ static int wm9713_add_widgets(struct snd_soc_codec *codec)
snd_soc_dapm_add_routes(codec, audio_map, ARRAY_SIZE(audio_map));
- snd_soc_dapm_new_widgets(codec);
return 0;
}
@@ -800,8 +799,8 @@ static int wm9713_set_pll(struct snd_soc_codec *codec,
return 0;
}
-static int wm9713_set_dai_pll(struct snd_soc_dai *codec_dai,
- int pll_id, unsigned int freq_in, unsigned int freq_out)
+static int wm9713_set_dai_pll(struct snd_soc_dai *codec_dai, int pll_id,
+ int source, unsigned int freq_in, unsigned int freq_out)
{
struct snd_soc_codec *codec = codec_dai->codec;
return wm9713_set_pll(codec, pll_id, freq_in, freq_out);
@@ -1247,14 +1246,11 @@ static int wm9713_soc_probe(struct platform_device *pdev)
snd_soc_add_controls(codec, wm9713_snd_ac97_controls,
ARRAY_SIZE(wm9713_snd_ac97_controls));
wm9713_add_widgets(codec);
- ret = snd_soc_init_card(socdev);
- if (ret < 0)
- goto reset_err;
+
return 0;
reset_err:
snd_soc_free_pcms(socdev);
-
pcm_err:
snd_soc_free_ac97_codec(codec);
diff --git a/sound/soc/codecs/wm_hubs.c b/sound/soc/codecs/wm_hubs.c
index e542027eea89..d73c30536a2c 100644
--- a/sound/soc/codecs/wm_hubs.c
+++ b/sound/soc/codecs/wm_hubs.c
@@ -438,11 +438,11 @@ static const struct snd_soc_dapm_widget analogue_dapm_widgets[] = {
SND_SOC_DAPM_INPUT("IN1LN"),
SND_SOC_DAPM_INPUT("IN1LP"),
SND_SOC_DAPM_INPUT("IN2LN"),
-SND_SOC_DAPM_INPUT("IN2LP/VXRN"),
+SND_SOC_DAPM_INPUT("IN2LP:VXRN"),
SND_SOC_DAPM_INPUT("IN1RN"),
SND_SOC_DAPM_INPUT("IN1RP"),
SND_SOC_DAPM_INPUT("IN2RN"),
-SND_SOC_DAPM_INPUT("IN2RP/VXRP"),
+SND_SOC_DAPM_INPUT("IN2RP:VXRP"),
SND_SOC_DAPM_MICBIAS("MICBIAS2", WM8993_POWER_MANAGEMENT_1, 5, 0),
SND_SOC_DAPM_MICBIAS("MICBIAS1", WM8993_POWER_MANAGEMENT_1, 4, 0),
@@ -537,14 +537,14 @@ static const struct snd_soc_dapm_route analogue_routes[] = {
{ "IN1R PGA", "IN1RP Switch", "IN1RP" },
{ "IN1R PGA", "IN1RN Switch", "IN1RN" },
- { "IN2L PGA", "IN2LP Switch", "IN2LP/VXRN" },
+ { "IN2L PGA", "IN2LP Switch", "IN2LP:VXRN" },
{ "IN2L PGA", "IN2LN Switch", "IN2LN" },
- { "IN2R PGA", "IN2RP Switch", "IN2RP/VXRP" },
+ { "IN2R PGA", "IN2RP Switch", "IN2RP:VXRP" },
{ "IN2R PGA", "IN2RN Switch", "IN2RN" },
- { "Direct Voice", NULL, "IN2LP/VXRN" },
- { "Direct Voice", NULL, "IN2RP/VXRP" },
+ { "Direct Voice", NULL, "IN2LP:VXRN" },
+ { "Direct Voice", NULL, "IN2RP:VXRP" },
{ "MIXINL", "IN1L Switch", "IN1L PGA" },
{ "MIXINL", "IN2L Switch", "IN2L PGA" },
@@ -565,7 +565,7 @@ static const struct snd_soc_dapm_route analogue_routes[] = {
{ "Left Output Mixer", "Right Input Switch", "MIXINR" },
{ "Left Output Mixer", "IN2RN Switch", "IN2RN" },
{ "Left Output Mixer", "IN2LN Switch", "IN2LN" },
- { "Left Output Mixer", "IN2LP Switch", "IN2LP/VXRN" },
+ { "Left Output Mixer", "IN2LP Switch", "IN2LP:VXRN" },
{ "Left Output Mixer", "IN1L Switch", "IN1L PGA" },
{ "Left Output Mixer", "IN1R Switch", "IN1R PGA" },
@@ -573,7 +573,7 @@ static const struct snd_soc_dapm_route analogue_routes[] = {
{ "Right Output Mixer", "Right Input Switch", "MIXINR" },
{ "Right Output Mixer", "IN2LN Switch", "IN2LN" },
{ "Right Output Mixer", "IN2RN Switch", "IN2RN" },
- { "Right Output Mixer", "IN2RP Switch", "IN2RP/VXRP" },
+ { "Right Output Mixer", "IN2RP Switch", "IN2RP:VXRP" },
{ "Right Output Mixer", "IN1L Switch", "IN1L PGA" },
{ "Right Output Mixer", "IN1R Switch", "IN1R PGA" },
@@ -738,6 +738,41 @@ int wm_hubs_add_analogue_routes(struct snd_soc_codec *codec,
}
EXPORT_SYMBOL_GPL(wm_hubs_add_analogue_routes);
+int wm_hubs_handle_analogue_pdata(struct snd_soc_codec *codec,
+ int lineout1_diff, int lineout2_diff,
+ int lineout1fb, int lineout2fb,
+ int jd_scthr, int jd_thr, int micbias1_lvl,
+ int micbias2_lvl)
+{
+ if (!lineout1_diff)
+ snd_soc_update_bits(codec, WM8993_LINE_MIXER1,
+ WM8993_LINEOUT1_MODE,
+ WM8993_LINEOUT1_MODE);
+ if (!lineout2_diff)
+ snd_soc_update_bits(codec, WM8993_LINE_MIXER2,
+ WM8993_LINEOUT2_MODE,
+ WM8993_LINEOUT2_MODE);
+
+ if (lineout1fb)
+ snd_soc_update_bits(codec, WM8993_ADDITIONAL_CONTROL,
+ WM8993_LINEOUT1_FB, WM8993_LINEOUT1_FB);
+
+ if (lineout2fb)
+ snd_soc_update_bits(codec, WM8993_ADDITIONAL_CONTROL,
+ WM8993_LINEOUT2_FB, WM8993_LINEOUT2_FB);
+
+ snd_soc_update_bits(codec, WM8993_MICBIAS,
+ WM8993_JD_SCTHR_MASK | WM8993_JD_THR_MASK |
+ WM8993_MICB1_LVL | WM8993_MICB2_LVL,
+ jd_scthr << WM8993_JD_SCTHR_SHIFT |
+ jd_thr << WM8993_JD_THR_SHIFT |
+ micbias1_lvl |
+ micbias2_lvl << WM8993_MICB2_LVL_SHIFT);
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(wm_hubs_handle_analogue_pdata);
+
MODULE_DESCRIPTION("Shared support for Wolfson hubs products");
MODULE_AUTHOR("Mark Brown <broonie@opensource.wolfsonmicro.com>");
MODULE_LICENSE("GPL");
diff --git a/sound/soc/codecs/wm_hubs.h b/sound/soc/codecs/wm_hubs.h
index ec09cb6a2939..36d3fba1de8b 100644
--- a/sound/soc/codecs/wm_hubs.h
+++ b/sound/soc/codecs/wm_hubs.h
@@ -20,5 +20,10 @@ extern const unsigned int wm_hubs_spkmix_tlv[];
extern int wm_hubs_add_analogue_controls(struct snd_soc_codec *);
extern int wm_hubs_add_analogue_routes(struct snd_soc_codec *, int, int);
+extern int wm_hubs_handle_analogue_pdata(struct snd_soc_codec *,
+ int lineout1_diff, int lineout2_diff,
+ int lineout1fb, int lineout2fb,
+ int jd_scthr, int jd_thr,
+ int micbias1_lvl, int micbias2_lvl);
#endif
diff --git a/sound/soc/davinci/Kconfig b/sound/soc/davinci/Kconfig
index 4dfd4ad9d90e..047ee39418c0 100644
--- a/sound/soc/davinci/Kconfig
+++ b/sound/soc/davinci/Kconfig
@@ -13,9 +13,9 @@ config SND_DAVINCI_SOC_MCASP
tristate
config SND_DAVINCI_SOC_EVM
- tristate "SoC Audio support for DaVinci DM6446 or DM355 EVM"
+ tristate "SoC Audio support for DaVinci DM6446, DM355 or DM365 EVM"
depends on SND_DAVINCI_SOC
- depends on MACH_DAVINCI_EVM || MACH_DAVINCI_DM355_EVM
+ depends on MACH_DAVINCI_EVM || MACH_DAVINCI_DM355_EVM || MACH_DAVINCI_DM365_EVM
select SND_DAVINCI_SOC_I2S
select SND_SOC_TLV320AIC3X
help
diff --git a/sound/soc/davinci/davinci-evm.c b/sound/soc/davinci/davinci-evm.c
index 67414f659405..7ccbe6684fc2 100644
--- a/sound/soc/davinci/davinci-evm.c
+++ b/sound/soc/davinci/davinci-evm.c
@@ -45,7 +45,8 @@ static int evm_hw_params(struct snd_pcm_substream *substream,
unsigned sysclk;
/* ASP1 on DM355 EVM is clocked by an external oscillator */
- if (machine_is_davinci_dm355_evm() || machine_is_davinci_dm6467_evm())
+ if (machine_is_davinci_dm355_evm() || machine_is_davinci_dm6467_evm() ||
+ machine_is_davinci_dm365_evm())
sysclk = 27000000;
/* ASP0 in DM6446 EVM is clocked by U55, as configured by
@@ -176,7 +177,7 @@ static struct snd_soc_dai_link da8xx_evm_dai = {
.ops = &evm_ops,
};
-/* davinci-evm audio machine driver */
+/* davinci dm6446, dm355 or dm365 evm audio machine driver */
static struct snd_soc_card snd_soc_card_evm = {
.name = "DaVinci EVM",
.platform = &davinci_soc_platform,
@@ -243,7 +244,7 @@ static int __init evm_init(void)
int index;
int ret;
- if (machine_is_davinci_evm()) {
+ if (machine_is_davinci_evm() || machine_is_davinci_dm365_evm()) {
evm_snd_dev_data = &evm_snd_devdata;
index = 0;
} else if (machine_is_davinci_dm355_evm()) {
diff --git a/sound/soc/davinci/davinci-i2s.c b/sound/soc/davinci/davinci-i2s.c
index 4ae707048021..6362ca05506e 100644
--- a/sound/soc/davinci/davinci-i2s.c
+++ b/sound/soc/davinci/davinci-i2s.c
@@ -97,12 +97,24 @@ enum {
DAVINCI_MCBSP_WORD_32,
};
+static const unsigned char data_type[SNDRV_PCM_FORMAT_S32_LE + 1] = {
+ [SNDRV_PCM_FORMAT_S8] = 1,
+ [SNDRV_PCM_FORMAT_S16_LE] = 2,
+ [SNDRV_PCM_FORMAT_S32_LE] = 4,
+};
+
+static const unsigned char asp_word_length[SNDRV_PCM_FORMAT_S32_LE + 1] = {
+ [SNDRV_PCM_FORMAT_S8] = DAVINCI_MCBSP_WORD_8,
+ [SNDRV_PCM_FORMAT_S16_LE] = DAVINCI_MCBSP_WORD_16,
+ [SNDRV_PCM_FORMAT_S32_LE] = DAVINCI_MCBSP_WORD_32,
+};
+
+static const unsigned char double_fmt[SNDRV_PCM_FORMAT_S32_LE + 1] = {
+ [SNDRV_PCM_FORMAT_S8] = SNDRV_PCM_FORMAT_S16_LE,
+ [SNDRV_PCM_FORMAT_S16_LE] = SNDRV_PCM_FORMAT_S32_LE,
+};
+
struct davinci_mcbsp_dev {
- /*
- * dma_params must be first because rtd->dai->cpu_dai->private_data
- * is cast to a pointer of an array of struct davinci_pcm_dma_params in
- * davinci_pcm_open.
- */
struct davinci_pcm_dma_params dma_params[2];
void __iomem *base;
#define MOD_DSP_A 0
@@ -110,6 +122,27 @@ struct davinci_mcbsp_dev {
int mode;
u32 pcr;
struct clk *clk;
+ /*
+ * Combining both channels into 1 element will at least double the
+ * amount of time between servicing the dma channel, increase
+ * effiency, and reduce the chance of overrun/underrun. But,
+ * it will result in the left & right channels being swapped.
+ *
+ * If relabeling the left and right channels is not possible,
+ * you may want to let the codec know to swap them back.
+ *
+ * It may allow x10 the amount of time to service dma requests,
+ * if the codec is master and is using an unnecessarily fast bit clock
+ * (ie. tlvaic23b), independent of the sample rate. So, having an
+ * entire frame at once means it can be serviced at the sample rate
+ * instead of the bit clock rate.
+ *
+ * In the now unlikely case that an underrun still
+ * occurs, both the left and right samples will be repeated
+ * so that no pops are heard, and the left and right channels
+ * won't end up being swapped because of the underrun.
+ */
+ unsigned enable_channel_combine:1;
};
static inline void davinci_mcbsp_write_reg(struct davinci_mcbsp_dev *dev,
@@ -349,6 +382,8 @@ static int davinci_i2s_hw_params(struct snd_pcm_substream *substream,
int mcbsp_word_length;
unsigned int rcr, xcr, srgr;
u32 spcr;
+ snd_pcm_format_t fmt;
+ unsigned element_cnt = 1;
/* general line settings */
spcr = davinci_mcbsp_read_reg(dev, DAVINCI_MCBSP_SPCR_REG);
@@ -378,27 +413,24 @@ static int davinci_i2s_hw_params(struct snd_pcm_substream *substream,
xcr |= DAVINCI_MCBSP_XCR_XDATDLY(1);
}
/* Determine xfer data type */
- switch (params_format(params)) {
- case SNDRV_PCM_FORMAT_S8:
- dma_params->data_type = 1;
- mcbsp_word_length = DAVINCI_MCBSP_WORD_8;
- break;
- case SNDRV_PCM_FORMAT_S16_LE:
- dma_params->data_type = 2;
- mcbsp_word_length = DAVINCI_MCBSP_WORD_16;
- break;
- case SNDRV_PCM_FORMAT_S32_LE:
- dma_params->data_type = 4;
- mcbsp_word_length = DAVINCI_MCBSP_WORD_32;
- break;
- default:
+ fmt = params_format(params);
+ if ((fmt > SNDRV_PCM_FORMAT_S32_LE) || !data_type[fmt]) {
printk(KERN_WARNING "davinci-i2s: unsupported PCM format\n");
return -EINVAL;
}
- dma_params->acnt = dma_params->data_type;
- rcr |= DAVINCI_MCBSP_RCR_RFRLEN1(1);
- xcr |= DAVINCI_MCBSP_XCR_XFRLEN1(1);
+ if (params_channels(params) == 2) {
+ element_cnt = 2;
+ if (double_fmt[fmt] && dev->enable_channel_combine) {
+ element_cnt = 1;
+ fmt = double_fmt[fmt];
+ }
+ }
+ dma_params->acnt = dma_params->data_type = data_type[fmt];
+ dma_params->fifo_level = 0;
+ mcbsp_word_length = asp_word_length[fmt];
+ rcr |= DAVINCI_MCBSP_RCR_RFRLEN1(element_cnt - 1);
+ xcr |= DAVINCI_MCBSP_XCR_XFRLEN1(element_cnt - 1);
rcr |= DAVINCI_MCBSP_RCR_RWDLEN1(mcbsp_word_length) |
DAVINCI_MCBSP_RCR_RWDLEN2(mcbsp_word_length);
@@ -513,7 +545,13 @@ static int davinci_i2s_probe(struct platform_device *pdev)
ret = -ENOMEM;
goto err_release_region;
}
-
+ if (pdata) {
+ dev->enable_channel_combine = pdata->enable_channel_combine;
+ dev->dma_params[SNDRV_PCM_STREAM_PLAYBACK].sram_size =
+ pdata->sram_size_playback;
+ dev->dma_params[SNDRV_PCM_STREAM_CAPTURE].sram_size =
+ pdata->sram_size_capture;
+ }
dev->clk = clk_get(&pdev->dev, NULL);
if (IS_ERR(dev->clk)) {
ret = -ENODEV;
@@ -547,6 +585,7 @@ static int davinci_i2s_probe(struct platform_device *pdev)
dev->dma_params[SNDRV_PCM_STREAM_CAPTURE].channel = res->start;
davinci_i2s_dai.private_data = dev;
+ davinci_i2s_dai.dma_data = dev->dma_params;
ret = snd_soc_register_dai(&davinci_i2s_dai);
if (ret != 0)
goto err_free_mem;
diff --git a/sound/soc/davinci/davinci-mcasp.c b/sound/soc/davinci/davinci-mcasp.c
index 5d1f98a4c978..0a302e1080d9 100644
--- a/sound/soc/davinci/davinci-mcasp.c
+++ b/sound/soc/davinci/davinci-mcasp.c
@@ -714,16 +714,13 @@ static int davinci_mcasp_hw_params(struct snd_pcm_substream *substream,
struct davinci_pcm_dma_params *dma_params =
&dev->dma_params[substream->stream];
int word_length;
- u8 numevt;
+ u8 fifo_level;
davinci_hw_common_param(dev, substream->stream);
if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
- numevt = dev->txnumevt;
+ fifo_level = dev->txnumevt;
else
- numevt = dev->rxnumevt;
-
- if (!numevt)
- numevt = 1;
+ fifo_level = dev->rxnumevt;
if (dev->op_mode == DAVINCI_MCASP_DIT_MODE)
davinci_hw_dit_param(dev);
@@ -751,12 +748,12 @@ static int davinci_mcasp_hw_params(struct snd_pcm_substream *substream,
return -EINVAL;
}
- if (dev->version == MCASP_VERSION_2) {
- dma_params->data_type *= numevt;
- dma_params->acnt = 4 * numevt;
- } else
+ if (dev->version == MCASP_VERSION_2 && !fifo_level)
+ dma_params->acnt = 4;
+ else
dma_params->acnt = dma_params->data_type;
+ dma_params->fifo_level = fifo_level;
davinci_config_channel_size(dev, word_length);
return 0;
@@ -907,6 +904,7 @@ static int davinci_mcasp_probe(struct platform_device *pdev)
dma_data->channel = res->start;
davinci_mcasp_dai[pdata->op_mode].private_data = dev;
+ davinci_mcasp_dai[pdata->op_mode].dma_data = dev->dma_params;
davinci_mcasp_dai[pdata->op_mode].dev = &pdev->dev;
ret = snd_soc_register_dai(&davinci_mcasp_dai[pdata->op_mode]);
diff --git a/sound/soc/davinci/davinci-mcasp.h b/sound/soc/davinci/davinci-mcasp.h
index 9d179cc88f7b..582c9249ef09 100644
--- a/sound/soc/davinci/davinci-mcasp.h
+++ b/sound/soc/davinci/davinci-mcasp.h
@@ -39,11 +39,6 @@ enum {
};
struct davinci_audio_dev {
- /*
- * dma_params must be first because rtd->dai->cpu_dai->private_data
- * is cast to a pointer of an array of struct davinci_pcm_dma_params in
- * davinci_pcm_open.
- */
struct davinci_pcm_dma_params dma_params[2];
void __iomem *base;
int sample_rate;
diff --git a/sound/soc/davinci/davinci-pcm.c b/sound/soc/davinci/davinci-pcm.c
index c73a915f233f..ad4d7f47a86b 100644
--- a/sound/soc/davinci/davinci-pcm.c
+++ b/sound/soc/davinci/davinci-pcm.c
@@ -3,6 +3,7 @@
*
* Author: Vladimir Barinov, <vbarinov@embeddedalley.com>
* Copyright: (C) 2007 MontaVista Software, Inc., <source@mvista.com>
+ * added SRAM ping/pong (C) 2008 Troy Kisky <troy.kisky@boundarydevices.com>
*
* 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
@@ -23,10 +24,29 @@
#include <asm/dma.h>
#include <mach/edma.h>
+#include <mach/sram.h>
#include "davinci-pcm.h"
-static struct snd_pcm_hardware davinci_pcm_hardware = {
+#ifdef DEBUG
+static void print_buf_info(int slot, char *name)
+{
+ struct edmacc_param p;
+ if (slot < 0)
+ return;
+ edma_read_slot(slot, &p);
+ printk(KERN_DEBUG "%s: 0x%x, opt=%x, src=%x, a_b_cnt=%x dst=%x\n",
+ name, slot, p.opt, p.src, p.a_b_cnt, p.dst);
+ printk(KERN_DEBUG " src_dst_bidx=%x link_bcntrld=%x src_dst_cidx=%x ccnt=%x\n",
+ p.src_dst_bidx, p.link_bcntrld, p.src_dst_cidx, p.ccnt);
+}
+#else
+static void print_buf_info(int slot, char *name)
+{
+}
+#endif
+
+static struct snd_pcm_hardware pcm_hardware_playback = {
.info = (SNDRV_PCM_INFO_INTERLEAVED | SNDRV_PCM_INFO_BLOCK_TRANSFER |
SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_MMAP_VALID |
SNDRV_PCM_INFO_PAUSE),
@@ -48,102 +68,432 @@ static struct snd_pcm_hardware davinci_pcm_hardware = {
.fifo_size = 0,
};
+static struct snd_pcm_hardware pcm_hardware_capture = {
+ .info = (SNDRV_PCM_INFO_INTERLEAVED | SNDRV_PCM_INFO_BLOCK_TRANSFER |
+ SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_MMAP_VALID |
+ SNDRV_PCM_INFO_PAUSE),
+ .formats = (SNDRV_PCM_FMTBIT_S16_LE),
+ .rates = (SNDRV_PCM_RATE_8000 | 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 |
+ SNDRV_PCM_RATE_KNOT),
+ .rate_min = 8000,
+ .rate_max = 96000,
+ .channels_min = 2,
+ .channels_max = 2,
+ .buffer_bytes_max = 128 * 1024,
+ .period_bytes_min = 32,
+ .period_bytes_max = 8 * 1024,
+ .periods_min = 16,
+ .periods_max = 255,
+ .fifo_size = 0,
+};
+
+/*
+ * How ping/pong works....
+ *
+ * Playback:
+ * ram_params - copys 2*ping_size from start of SDRAM to iram,
+ * links to ram_link2
+ * ram_link2 - copys rest of SDRAM to iram in ping_size units,
+ * links to ram_link
+ * ram_link - copys entire SDRAM to iram in ping_size uints,
+ * links to self
+ *
+ * asp_params - same as asp_link[0]
+ * asp_link[0] - copys from lower half of iram to asp port
+ * links to asp_link[1], triggers iram copy event on completion
+ * asp_link[1] - copys from upper half of iram to asp port
+ * links to asp_link[0], triggers iram copy event on completion
+ * triggers interrupt only needed to let upper SOC levels update position
+ * in stream on completion
+ *
+ * When playback is started:
+ * ram_params started
+ * asp_params started
+ *
+ * Capture:
+ * ram_params - same as ram_link,
+ * links to ram_link
+ * ram_link - same as playback
+ * links to self
+ *
+ * asp_params - same as playback
+ * asp_link[0] - same as playback
+ * asp_link[1] - same as playback
+ *
+ * When capture is started:
+ * asp_params started
+ */
struct davinci_runtime_data {
spinlock_t lock;
int period; /* current DMA period */
- int master_lch; /* Master DMA channel */
- int slave_lch; /* linked parameter RAM reload slot */
+ int asp_channel; /* Master DMA channel */
+ int asp_link[2]; /* asp parameter link channel, ping/pong */
struct davinci_pcm_dma_params *params; /* DMA params */
+ int ram_channel;
+ int ram_link;
+ int ram_link2;
+ struct edmacc_param asp_params;
+ struct edmacc_param ram_params;
};
+/*
+ * Not used with ping/pong
+ */
static void davinci_pcm_enqueue_dma(struct snd_pcm_substream *substream)
{
struct davinci_runtime_data *prtd = substream->runtime->private_data;
struct snd_pcm_runtime *runtime = substream->runtime;
- int lch = prtd->slave_lch;
+ int link = prtd->asp_link[0];
unsigned int period_size;
unsigned int dma_offset;
dma_addr_t dma_pos;
dma_addr_t src, dst;
unsigned short src_bidx, dst_bidx;
+ unsigned short src_cidx, dst_cidx;
unsigned int data_type;
unsigned short acnt;
unsigned int count;
+ unsigned int fifo_level;
period_size = snd_pcm_lib_period_bytes(substream);
dma_offset = prtd->period * period_size;
dma_pos = runtime->dma_addr + dma_offset;
+ fifo_level = prtd->params->fifo_level;
pr_debug("davinci_pcm: audio_set_dma_params_play channel = %d "
- "dma_ptr = %x period_size=%x\n", lch, dma_pos, period_size);
+ "dma_ptr = %x period_size=%x\n", link, dma_pos, period_size);
data_type = prtd->params->data_type;
count = period_size / data_type;
+ if (fifo_level)
+ count /= fifo_level;
if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
src = dma_pos;
dst = prtd->params->dma_addr;
src_bidx = data_type;
dst_bidx = 0;
+ src_cidx = data_type * fifo_level;
+ dst_cidx = 0;
} else {
src = prtd->params->dma_addr;
dst = dma_pos;
src_bidx = 0;
dst_bidx = data_type;
+ src_cidx = 0;
+ dst_cidx = data_type * fifo_level;
}
acnt = prtd->params->acnt;
- edma_set_src(lch, src, INCR, W8BIT);
- edma_set_dest(lch, dst, INCR, W8BIT);
- edma_set_src_index(lch, src_bidx, 0);
- edma_set_dest_index(lch, dst_bidx, 0);
- edma_set_transfer_params(lch, acnt, count, 1, 0, ASYNC);
+ edma_set_src(link, src, INCR, W8BIT);
+ edma_set_dest(link, dst, INCR, W8BIT);
+
+ edma_set_src_index(link, src_bidx, src_cidx);
+ edma_set_dest_index(link, dst_bidx, dst_cidx);
+
+ if (!fifo_level)
+ edma_set_transfer_params(link, acnt, count, 1, 0, ASYNC);
+ else
+ edma_set_transfer_params(link, acnt, fifo_level, count,
+ fifo_level, ABSYNC);
prtd->period++;
if (unlikely(prtd->period >= runtime->periods))
prtd->period = 0;
}
-static void davinci_pcm_dma_irq(unsigned lch, u16 ch_status, void *data)
+static void davinci_pcm_dma_irq(unsigned link, u16 ch_status, void *data)
{
struct snd_pcm_substream *substream = data;
struct davinci_runtime_data *prtd = substream->runtime->private_data;
- pr_debug("davinci_pcm: lch=%d, status=0x%x\n", lch, ch_status);
+ print_buf_info(prtd->ram_channel, "i ram_channel");
+ pr_debug("davinci_pcm: link=%d, status=0x%x\n", link, ch_status);
if (unlikely(ch_status != DMA_COMPLETE))
return;
if (snd_pcm_running(substream)) {
+ if (prtd->ram_channel < 0) {
+ /* No ping/pong must fix up link dma data*/
+ spin_lock(&prtd->lock);
+ davinci_pcm_enqueue_dma(substream);
+ spin_unlock(&prtd->lock);
+ }
snd_pcm_period_elapsed(substream);
+ }
+}
+
+static int allocate_sram(struct snd_pcm_substream *substream, unsigned size,
+ struct snd_pcm_hardware *ppcm)
+{
+ struct snd_dma_buffer *buf = &substream->dma_buffer;
+ struct snd_dma_buffer *iram_dma = NULL;
+ dma_addr_t iram_phys = 0;
+ void *iram_virt = NULL;
+
+ if (buf->private_data || !size)
+ return 0;
+
+ ppcm->period_bytes_max = size;
+ iram_virt = sram_alloc(size, &iram_phys);
+ if (!iram_virt)
+ goto exit1;
+ iram_dma = kzalloc(sizeof(*iram_dma), GFP_KERNEL);
+ if (!iram_dma)
+ goto exit2;
+ iram_dma->area = iram_virt;
+ iram_dma->addr = iram_phys;
+ memset(iram_dma->area, 0, size);
+ iram_dma->bytes = size;
+ buf->private_data = iram_dma;
+ return 0;
+exit2:
+ if (iram_virt)
+ sram_free(iram_virt, size);
+exit1:
+ return -ENOMEM;
+}
+
+/*
+ * Only used with ping/pong.
+ * This is called after runtime->dma_addr, period_bytes and data_type are valid
+ */
+static int ping_pong_dma_setup(struct snd_pcm_substream *substream)
+{
+ unsigned short ram_src_cidx, ram_dst_cidx;
+ struct snd_pcm_runtime *runtime = substream->runtime;
+ struct davinci_runtime_data *prtd = runtime->private_data;
+ struct snd_dma_buffer *iram_dma =
+ (struct snd_dma_buffer *)substream->dma_buffer.private_data;
+ struct davinci_pcm_dma_params *params = prtd->params;
+ unsigned int data_type = params->data_type;
+ unsigned int acnt = params->acnt;
+ /* divide by 2 for ping/pong */
+ unsigned int ping_size = snd_pcm_lib_period_bytes(substream) >> 1;
+ int link = prtd->asp_link[1];
+ unsigned int fifo_level = prtd->params->fifo_level;
+ unsigned int count;
+ if ((data_type == 0) || (data_type > 4)) {
+ printk(KERN_ERR "%s: data_type=%i\n", __func__, data_type);
+ return -EINVAL;
+ }
+ if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
+ dma_addr_t asp_src_pong = iram_dma->addr + ping_size;
+ ram_src_cidx = ping_size;
+ ram_dst_cidx = -ping_size;
+ edma_set_src(link, asp_src_pong, INCR, W8BIT);
+
+ link = prtd->asp_link[0];
+ edma_set_src_index(link, data_type, data_type * fifo_level);
+ link = prtd->asp_link[1];
+ edma_set_src_index(link, data_type, data_type * fifo_level);
+
+ link = prtd->ram_link;
+ edma_set_src(link, runtime->dma_addr, INCR, W32BIT);
+ } else {
+ dma_addr_t asp_dst_pong = iram_dma->addr + ping_size;
+ ram_src_cidx = -ping_size;
+ ram_dst_cidx = ping_size;
+ edma_set_dest(link, asp_dst_pong, INCR, W8BIT);
+
+ link = prtd->asp_link[0];
+ edma_set_dest_index(link, data_type, data_type * fifo_level);
+ link = prtd->asp_link[1];
+ edma_set_dest_index(link, data_type, data_type * fifo_level);
+
+ link = prtd->ram_link;
+ edma_set_dest(link, runtime->dma_addr, INCR, W32BIT);
+ }
- spin_lock(&prtd->lock);
- davinci_pcm_enqueue_dma(substream);
- spin_unlock(&prtd->lock);
+ if (!fifo_level) {
+ count = ping_size / data_type;
+ edma_set_transfer_params(prtd->asp_link[0], acnt, count,
+ 1, 0, ASYNC);
+ edma_set_transfer_params(prtd->asp_link[1], acnt, count,
+ 1, 0, ASYNC);
+ } else {
+ count = ping_size / (data_type * fifo_level);
+ edma_set_transfer_params(prtd->asp_link[0], acnt, fifo_level,
+ count, fifo_level, ABSYNC);
+ edma_set_transfer_params(prtd->asp_link[1], acnt, fifo_level,
+ count, fifo_level, ABSYNC);
}
+
+ link = prtd->ram_link;
+ edma_set_src_index(link, ping_size, ram_src_cidx);
+ edma_set_dest_index(link, ping_size, ram_dst_cidx);
+ edma_set_transfer_params(link, ping_size, 2,
+ runtime->periods, 2, ASYNC);
+
+ /* init master params */
+ edma_read_slot(prtd->asp_link[0], &prtd->asp_params);
+ edma_read_slot(prtd->ram_link, &prtd->ram_params);
+ if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
+ struct edmacc_param p_ram;
+ /* Copy entire iram buffer before playback started */
+ prtd->ram_params.a_b_cnt = (1 << 16) | (ping_size << 1);
+ /* 0 dst_bidx */
+ prtd->ram_params.src_dst_bidx = (ping_size << 1);
+ /* 0 dst_cidx */
+ prtd->ram_params.src_dst_cidx = (ping_size << 1);
+ prtd->ram_params.ccnt = 1;
+
+ /* Skip 1st period */
+ edma_read_slot(prtd->ram_link, &p_ram);
+ p_ram.src += (ping_size << 1);
+ p_ram.ccnt -= 1;
+ edma_write_slot(prtd->ram_link2, &p_ram);
+ /*
+ * When 1st started, ram -> iram dma channel will fill the
+ * entire iram. Then, whenever a ping/pong asp buffer finishes,
+ * 1/2 iram will be filled.
+ */
+ prtd->ram_params.link_bcntrld =
+ EDMA_CHAN_SLOT(prtd->ram_link2) << 5;
+ }
+ return 0;
+}
+
+/* 1 asp tx or rx channel using 2 parameter channels
+ * 1 ram to/from iram channel using 1 parameter channel
+ *
+ * Playback
+ * ram copy channel kicks off first,
+ * 1st ram copy of entire iram buffer completion kicks off asp channel
+ * asp tcc always kicks off ram copy of 1/2 iram buffer
+ *
+ * Record
+ * asp channel starts, tcc kicks off ram copy
+ */
+static int request_ping_pong(struct snd_pcm_substream *substream,
+ struct davinci_runtime_data *prtd,
+ struct snd_dma_buffer *iram_dma)
+{
+ dma_addr_t asp_src_ping;
+ dma_addr_t asp_dst_ping;
+ int link;
+ struct davinci_pcm_dma_params *params = prtd->params;
+
+ /* Request ram master channel */
+ link = prtd->ram_channel = edma_alloc_channel(EDMA_CHANNEL_ANY,
+ davinci_pcm_dma_irq, substream,
+ EVENTQ_1);
+ if (link < 0)
+ goto exit1;
+
+ /* Request ram link channel */
+ link = prtd->ram_link = edma_alloc_slot(
+ EDMA_CTLR(prtd->ram_channel), EDMA_SLOT_ANY);
+ if (link < 0)
+ goto exit2;
+
+ link = prtd->asp_link[1] = edma_alloc_slot(
+ EDMA_CTLR(prtd->asp_channel), EDMA_SLOT_ANY);
+ if (link < 0)
+ goto exit3;
+
+ prtd->ram_link2 = -1;
+ if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
+ link = prtd->ram_link2 = edma_alloc_slot(
+ EDMA_CTLR(prtd->ram_channel), EDMA_SLOT_ANY);
+ if (link < 0)
+ goto exit4;
+ }
+ /* circle ping-pong buffers */
+ edma_link(prtd->asp_link[0], prtd->asp_link[1]);
+ edma_link(prtd->asp_link[1], prtd->asp_link[0]);
+ /* circle ram buffers */
+ edma_link(prtd->ram_link, prtd->ram_link);
+
+ if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
+ asp_src_ping = iram_dma->addr;
+ asp_dst_ping = params->dma_addr; /* fifo */
+ } else {
+ asp_src_ping = params->dma_addr; /* fifo */
+ asp_dst_ping = iram_dma->addr;
+ }
+ /* ping */
+ link = prtd->asp_link[0];
+ edma_set_src(link, asp_src_ping, INCR, W16BIT);
+ edma_set_dest(link, asp_dst_ping, INCR, W16BIT);
+ edma_set_src_index(link, 0, 0);
+ edma_set_dest_index(link, 0, 0);
+
+ edma_read_slot(link, &prtd->asp_params);
+ prtd->asp_params.opt &= ~(TCCMODE | EDMA_TCC(0x3f) | TCINTEN);
+ prtd->asp_params.opt |= TCCHEN | EDMA_TCC(prtd->ram_channel & 0x3f);
+ edma_write_slot(link, &prtd->asp_params);
+
+ /* pong */
+ link = prtd->asp_link[1];
+ edma_set_src(link, asp_src_ping, INCR, W16BIT);
+ edma_set_dest(link, asp_dst_ping, INCR, W16BIT);
+ edma_set_src_index(link, 0, 0);
+ edma_set_dest_index(link, 0, 0);
+
+ edma_read_slot(link, &prtd->asp_params);
+ prtd->asp_params.opt &= ~(TCCMODE | EDMA_TCC(0x3f));
+ /* interrupt after every pong completion */
+ prtd->asp_params.opt |= TCINTEN | TCCHEN |
+ EDMA_TCC(EDMA_CHAN_SLOT(prtd->ram_channel));
+ edma_write_slot(link, &prtd->asp_params);
+
+ /* ram */
+ link = prtd->ram_link;
+ edma_set_src(link, iram_dma->addr, INCR, W32BIT);
+ edma_set_dest(link, iram_dma->addr, INCR, W32BIT);
+ pr_debug("%s: audio dma channels/slots in use for ram:%u %u %u,"
+ "for asp:%u %u %u\n", __func__,
+ prtd->ram_channel, prtd->ram_link, prtd->ram_link2,
+ prtd->asp_channel, prtd->asp_link[0],
+ prtd->asp_link[1]);
+ return 0;
+exit4:
+ edma_free_channel(prtd->asp_link[1]);
+ prtd->asp_link[1] = -1;
+exit3:
+ edma_free_channel(prtd->ram_link);
+ prtd->ram_link = -1;
+exit2:
+ edma_free_channel(prtd->ram_channel);
+ prtd->ram_channel = -1;
+exit1:
+ return link;
}
static int davinci_pcm_dma_request(struct snd_pcm_substream *substream)
{
+ struct snd_dma_buffer *iram_dma;
struct davinci_runtime_data *prtd = substream->runtime->private_data;
- struct edmacc_param p_ram;
- int ret;
+ struct davinci_pcm_dma_params *params = prtd->params;
+ int link;
- /* Request master DMA channel */
- ret = edma_alloc_channel(prtd->params->channel,
- davinci_pcm_dma_irq, substream,
- EVENTQ_0);
- if (ret < 0)
- return ret;
- prtd->master_lch = ret;
+ if (!params)
+ return -ENODEV;
- /* Request parameter RAM reload slot */
- ret = edma_alloc_slot(EDMA_CTLR(prtd->master_lch), EDMA_SLOT_ANY);
- if (ret < 0) {
- edma_free_channel(prtd->master_lch);
- return ret;
+ /* Request asp master DMA channel */
+ link = prtd->asp_channel = edma_alloc_channel(params->channel,
+ davinci_pcm_dma_irq, substream, EVENTQ_0);
+ if (link < 0)
+ goto exit1;
+
+ /* Request asp link channels */
+ link = prtd->asp_link[0] = edma_alloc_slot(
+ EDMA_CTLR(prtd->asp_channel), EDMA_SLOT_ANY);
+ if (link < 0)
+ goto exit2;
+
+ iram_dma = (struct snd_dma_buffer *)substream->dma_buffer.private_data;
+ if (iram_dma) {
+ if (request_ping_pong(substream, prtd, iram_dma) == 0)
+ return 0;
+ printk(KERN_WARNING "%s: dma channel allocation failed,"
+ "not using sram\n", __func__);
}
- prtd->slave_lch = ret;
/* Issue transfer completion IRQ when the channel completes a
* transfer, then always reload from the same slot (by a kind
@@ -154,12 +504,17 @@ static int davinci_pcm_dma_request(struct snd_pcm_substream *substream)
* the buffer and its length (ccnt) ... use it as a template
* so davinci_pcm_enqueue_dma() takes less time in IRQ.
*/
- edma_read_slot(prtd->slave_lch, &p_ram);
- p_ram.opt |= TCINTEN | EDMA_TCC(EDMA_CHAN_SLOT(prtd->master_lch));
- p_ram.link_bcntrld = EDMA_CHAN_SLOT(prtd->slave_lch) << 5;
- edma_write_slot(prtd->slave_lch, &p_ram);
-
+ edma_read_slot(link, &prtd->asp_params);
+ prtd->asp_params.opt |= TCINTEN |
+ EDMA_TCC(EDMA_CHAN_SLOT(prtd->asp_channel));
+ prtd->asp_params.link_bcntrld = EDMA_CHAN_SLOT(link) << 5;
+ edma_write_slot(link, &prtd->asp_params);
return 0;
+exit2:
+ edma_free_channel(prtd->asp_channel);
+ prtd->asp_channel = -1;
+exit1:
+ return link;
}
static int davinci_pcm_trigger(struct snd_pcm_substream *substream, int cmd)
@@ -173,12 +528,12 @@ static int davinci_pcm_trigger(struct snd_pcm_substream *substream, int cmd)
case SNDRV_PCM_TRIGGER_START:
case SNDRV_PCM_TRIGGER_RESUME:
case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
- edma_start(prtd->master_lch);
+ edma_resume(prtd->asp_channel);
break;
case SNDRV_PCM_TRIGGER_STOP:
case SNDRV_PCM_TRIGGER_SUSPEND:
case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
- edma_stop(prtd->master_lch);
+ edma_pause(prtd->asp_channel);
break;
default:
ret = -EINVAL;
@@ -193,15 +548,37 @@ static int davinci_pcm_trigger(struct snd_pcm_substream *substream, int cmd)
static int davinci_pcm_prepare(struct snd_pcm_substream *substream)
{
struct davinci_runtime_data *prtd = substream->runtime->private_data;
- struct edmacc_param temp;
+ if (prtd->ram_channel >= 0) {
+ int ret = ping_pong_dma_setup(substream);
+ if (ret < 0)
+ return ret;
+
+ edma_write_slot(prtd->ram_channel, &prtd->ram_params);
+ edma_write_slot(prtd->asp_channel, &prtd->asp_params);
+
+ print_buf_info(prtd->ram_channel, "ram_channel");
+ print_buf_info(prtd->ram_link, "ram_link");
+ print_buf_info(prtd->ram_link2, "ram_link2");
+ print_buf_info(prtd->asp_channel, "asp_channel");
+ print_buf_info(prtd->asp_link[0], "asp_link[0]");
+ print_buf_info(prtd->asp_link[1], "asp_link[1]");
+
+ if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
+ /* copy 1st iram buffer */
+ edma_start(prtd->ram_channel);
+ }
+ edma_start(prtd->asp_channel);
+ return 0;
+ }
prtd->period = 0;
davinci_pcm_enqueue_dma(substream);
/* Copy self-linked parameter RAM entry into master channel */
- edma_read_slot(prtd->slave_lch, &temp);
- edma_write_slot(prtd->master_lch, &temp);
+ edma_read_slot(prtd->asp_link[0], &prtd->asp_params);
+ edma_write_slot(prtd->asp_channel, &prtd->asp_params);
davinci_pcm_enqueue_dma(substream);
+ edma_start(prtd->asp_channel);
return 0;
}
@@ -212,20 +589,53 @@ davinci_pcm_pointer(struct snd_pcm_substream *substream)
struct snd_pcm_runtime *runtime = substream->runtime;
struct davinci_runtime_data *prtd = runtime->private_data;
unsigned int offset;
- dma_addr_t count;
- dma_addr_t src, dst;
+ int asp_count;
+ dma_addr_t asp_src, asp_dst;
spin_lock(&prtd->lock);
-
- edma_get_position(prtd->master_lch, &src, &dst);
- if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
- count = src - runtime->dma_addr;
- else
- count = dst - runtime->dma_addr;
-
+ if (prtd->ram_channel >= 0) {
+ int ram_count;
+ int mod_ram;
+ dma_addr_t ram_src, ram_dst;
+ unsigned int period_size = snd_pcm_lib_period_bytes(substream);
+ if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
+ /* reading ram before asp should be safe
+ * as long as the asp transfers less than a ping size
+ * of bytes between the 2 reads
+ */
+ edma_get_position(prtd->ram_channel,
+ &ram_src, &ram_dst);
+ edma_get_position(prtd->asp_channel,
+ &asp_src, &asp_dst);
+ asp_count = asp_src - prtd->asp_params.src;
+ ram_count = ram_src - prtd->ram_params.src;
+ mod_ram = ram_count % period_size;
+ mod_ram -= asp_count;
+ if (mod_ram < 0)
+ mod_ram += period_size;
+ else if (mod_ram == 0) {
+ if (snd_pcm_running(substream))
+ mod_ram += period_size;
+ }
+ ram_count -= mod_ram;
+ if (ram_count < 0)
+ ram_count += period_size * runtime->periods;
+ } else {
+ edma_get_position(prtd->ram_channel,
+ &ram_src, &ram_dst);
+ ram_count = ram_dst - prtd->ram_params.dst;
+ }
+ asp_count = ram_count;
+ } else {
+ edma_get_position(prtd->asp_channel, &asp_src, &asp_dst);
+ if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
+ asp_count = asp_src - runtime->dma_addr;
+ else
+ asp_count = asp_dst - runtime->dma_addr;
+ }
spin_unlock(&prtd->lock);
- offset = bytes_to_frames(runtime, count);
+ offset = bytes_to_frames(runtime, asp_count);
if (offset >= runtime->buffer_size)
offset = 0;
@@ -236,14 +646,19 @@ static int davinci_pcm_open(struct snd_pcm_substream *substream)
{
struct snd_pcm_runtime *runtime = substream->runtime;
struct davinci_runtime_data *prtd;
+ struct snd_pcm_hardware *ppcm;
int ret = 0;
struct snd_soc_pcm_runtime *rtd = substream->private_data;
- struct davinci_pcm_dma_params *pa = rtd->dai->cpu_dai->private_data;
- struct davinci_pcm_dma_params *params = &pa[substream->stream];
- if (!params)
+ struct davinci_pcm_dma_params *pa = rtd->dai->cpu_dai->dma_data;
+ struct davinci_pcm_dma_params *params;
+ if (!pa)
return -ENODEV;
+ params = &pa[substream->stream];
- snd_soc_set_runtime_hwparams(substream, &davinci_pcm_hardware);
+ ppcm = (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) ?
+ &pcm_hardware_playback : &pcm_hardware_capture;
+ allocate_sram(substream, params->sram_size, ppcm);
+ snd_soc_set_runtime_hwparams(substream, ppcm);
/* ensure that buffer size is a multiple of period size */
ret = snd_pcm_hw_constraint_integer(runtime,
SNDRV_PCM_HW_PARAM_PERIODS);
@@ -256,6 +671,11 @@ static int davinci_pcm_open(struct snd_pcm_substream *substream)
spin_lock_init(&prtd->lock);
prtd->params = params;
+ prtd->asp_channel = -1;
+ prtd->asp_link[0] = prtd->asp_link[1] = -1;
+ prtd->ram_channel = -1;
+ prtd->ram_link = -1;
+ prtd->ram_link2 = -1;
runtime->private_data = prtd;
@@ -273,10 +693,29 @@ static int davinci_pcm_close(struct snd_pcm_substream *substream)
struct snd_pcm_runtime *runtime = substream->runtime;
struct davinci_runtime_data *prtd = runtime->private_data;
- edma_unlink(prtd->slave_lch);
-
- edma_free_slot(prtd->slave_lch);
- edma_free_channel(prtd->master_lch);
+ if (prtd->ram_channel >= 0)
+ edma_stop(prtd->ram_channel);
+ if (prtd->asp_channel >= 0)
+ edma_stop(prtd->asp_channel);
+ if (prtd->asp_link[0] >= 0)
+ edma_unlink(prtd->asp_link[0]);
+ if (prtd->asp_link[1] >= 0)
+ edma_unlink(prtd->asp_link[1]);
+ if (prtd->ram_link >= 0)
+ edma_unlink(prtd->ram_link);
+
+ if (prtd->asp_link[0] >= 0)
+ edma_free_slot(prtd->asp_link[0]);
+ if (prtd->asp_link[1] >= 0)
+ edma_free_slot(prtd->asp_link[1]);
+ if (prtd->asp_channel >= 0)
+ edma_free_channel(prtd->asp_channel);
+ if (prtd->ram_link >= 0)
+ edma_free_slot(prtd->ram_link);
+ if (prtd->ram_link2 >= 0)
+ edma_free_slot(prtd->ram_link2);
+ if (prtd->ram_channel >= 0)
+ edma_free_channel(prtd->ram_channel);
kfree(prtd);
@@ -318,11 +757,11 @@ static struct snd_pcm_ops davinci_pcm_ops = {
.mmap = davinci_pcm_mmap,
};
-static int davinci_pcm_preallocate_dma_buffer(struct snd_pcm *pcm, int stream)
+static int davinci_pcm_preallocate_dma_buffer(struct snd_pcm *pcm, int stream,
+ size_t size)
{
struct snd_pcm_substream *substream = pcm->streams[stream].substream;
struct snd_dma_buffer *buf = &substream->dma_buffer;
- size_t size = davinci_pcm_hardware.buffer_bytes_max;
buf->dev.type = SNDRV_DMA_TYPE_DEV;
buf->dev.dev = pcm->card->dev;
@@ -347,6 +786,7 @@ static void davinci_pcm_free(struct snd_pcm *pcm)
int stream;
for (stream = 0; stream < 2; stream++) {
+ struct snd_dma_buffer *iram_dma;
substream = pcm->streams[stream].substream;
if (!substream)
continue;
@@ -358,6 +798,11 @@ static void davinci_pcm_free(struct snd_pcm *pcm)
dma_free_writecombine(pcm->card->dev, buf->bytes,
buf->area, buf->addr);
buf->area = NULL;
+ iram_dma = (struct snd_dma_buffer *)buf->private_data;
+ if (iram_dma) {
+ sram_free(iram_dma->area, iram_dma->bytes);
+ kfree(iram_dma);
+ }
}
}
@@ -375,14 +820,16 @@ static int davinci_pcm_new(struct snd_card *card,
if (dai->playback.channels_min) {
ret = davinci_pcm_preallocate_dma_buffer(pcm,
- SNDRV_PCM_STREAM_PLAYBACK);
+ SNDRV_PCM_STREAM_PLAYBACK,
+ pcm_hardware_playback.buffer_bytes_max);
if (ret)
return ret;
}
if (dai->capture.channels_min) {
ret = davinci_pcm_preallocate_dma_buffer(pcm,
- SNDRV_PCM_STREAM_CAPTURE);
+ SNDRV_PCM_STREAM_CAPTURE,
+ pcm_hardware_capture.buffer_bytes_max);
if (ret)
return ret;
}
diff --git a/sound/soc/davinci/davinci-pcm.h b/sound/soc/davinci/davinci-pcm.h
index 8746606efc89..0764944cf10f 100644
--- a/sound/soc/davinci/davinci-pcm.h
+++ b/sound/soc/davinci/davinci-pcm.h
@@ -20,9 +20,11 @@ struct davinci_pcm_dma_params {
int channel; /* sync dma channel ID */
unsigned short acnt;
dma_addr_t dma_addr; /* device physical address for DMA */
+ unsigned sram_size;
enum dma_event_q eventq_no; /* event queue number */
unsigned char data_type; /* xfer data type */
unsigned char convert_mono_stereo;
+ unsigned int fifo_level;
};
diff --git a/sound/soc/fsl/mpc5200_dma.c b/sound/soc/fsl/mpc5200_dma.c
index 6096d22283e6..30ed568afb2e 100644
--- a/sound/soc/fsl/mpc5200_dma.c
+++ b/sound/soc/fsl/mpc5200_dma.c
@@ -58,47 +58,15 @@ static void psc_dma_bcom_enqueue_next_buffer(struct psc_dma_stream *s)
/* Prepare and enqueue the next buffer descriptor */
bd = bcom_prepare_next_buffer(s->bcom_task);
bd->status = s->period_bytes;
- bd->data[0] = s->period_next_pt;
+ bd->data[0] = s->runtime->dma_addr + (s->period_next * s->period_bytes);
bcom_submit_next_buffer(s->bcom_task, NULL);
/* Update for next period */
- s->period_next_pt += s->period_bytes;
- if (s->period_next_pt >= s->period_end)
- s->period_next_pt = s->period_start;
-}
-
-static void psc_dma_bcom_enqueue_tx(struct psc_dma_stream *s)
-{
- if (s->appl_ptr > s->runtime->control->appl_ptr) {
- /*
- * In this case s->runtime->control->appl_ptr has wrapped around.
- * Play the data to the end of the boundary, then wrap our own
- * appl_ptr back around.
- */
- while (s->appl_ptr < s->runtime->boundary) {
- if (bcom_queue_full(s->bcom_task))
- return;
-
- s->appl_ptr += s->period_size;
-
- psc_dma_bcom_enqueue_next_buffer(s);
- }
- s->appl_ptr -= s->runtime->boundary;
- }
-
- while (s->appl_ptr < s->runtime->control->appl_ptr) {
-
- if (bcom_queue_full(s->bcom_task))
- return;
-
- s->appl_ptr += s->period_size;
-
- psc_dma_bcom_enqueue_next_buffer(s);
- }
+ s->period_next = (s->period_next + 1) % s->runtime->periods;
}
/* Bestcomm DMA irq handler */
-static irqreturn_t psc_dma_bcom_irq_tx(int irq, void *_psc_dma_stream)
+static irqreturn_t psc_dma_bcom_irq(int irq, void *_psc_dma_stream)
{
struct psc_dma_stream *s = _psc_dma_stream;
@@ -108,34 +76,8 @@ static irqreturn_t psc_dma_bcom_irq_tx(int irq, void *_psc_dma_stream)
while (bcom_buffer_done(s->bcom_task)) {
bcom_retrieve_buffer(s->bcom_task, NULL, NULL);
- s->period_current_pt += s->period_bytes;
- if (s->period_current_pt >= s->period_end)
- s->period_current_pt = s->period_start;
- }
- psc_dma_bcom_enqueue_tx(s);
- spin_unlock(&s->psc_dma->lock);
-
- /* If the stream is active, then also inform the PCM middle layer
- * of the period finished event. */
- if (s->active)
- snd_pcm_period_elapsed(s->stream);
-
- return IRQ_HANDLED;
-}
-
-static irqreturn_t psc_dma_bcom_irq_rx(int irq, void *_psc_dma_stream)
-{
- struct psc_dma_stream *s = _psc_dma_stream;
-
- spin_lock(&s->psc_dma->lock);
- /* For each finished period, dequeue the completed period buffer
- * and enqueue a new one in it's place. */
- while (bcom_buffer_done(s->bcom_task)) {
- bcom_retrieve_buffer(s->bcom_task, NULL, NULL);
-
- s->period_current_pt += s->period_bytes;
- if (s->period_current_pt >= s->period_end)
- s->period_current_pt = s->period_start;
+ s->period_current = (s->period_current+1) % s->runtime->periods;
+ s->period_count++;
psc_dma_bcom_enqueue_next_buffer(s);
}
@@ -166,54 +108,38 @@ static int psc_dma_trigger(struct snd_pcm_substream *substream, int cmd)
struct snd_soc_pcm_runtime *rtd = substream->private_data;
struct psc_dma *psc_dma = rtd->dai->cpu_dai->private_data;
struct snd_pcm_runtime *runtime = substream->runtime;
- struct psc_dma_stream *s;
+ struct psc_dma_stream *s = to_psc_dma_stream(substream, psc_dma);
struct mpc52xx_psc __iomem *regs = psc_dma->psc_regs;
u16 imr;
unsigned long flags;
int i;
- if (substream->pstr->stream == SNDRV_PCM_STREAM_CAPTURE)
- s = &psc_dma->capture;
- else
- s = &psc_dma->playback;
-
- dev_dbg(psc_dma->dev, "psc_dma_trigger(substream=%p, cmd=%i)"
- " stream_id=%i\n",
- substream, cmd, substream->pstr->stream);
-
switch (cmd) {
case SNDRV_PCM_TRIGGER_START:
+ dev_dbg(psc_dma->dev, "START: stream=%i fbits=%u ps=%u #p=%u\n",
+ substream->pstr->stream, runtime->frame_bits,
+ (int)runtime->period_size, runtime->periods);
s->period_bytes = frames_to_bytes(runtime,
runtime->period_size);
- s->period_start = virt_to_phys(runtime->dma_area);
- s->period_end = s->period_start +
- (s->period_bytes * runtime->periods);
- s->period_next_pt = s->period_start;
- s->period_current_pt = s->period_start;
- s->period_size = runtime->period_size;
+ s->period_next = 0;
+ s->period_current = 0;
s->active = 1;
-
- /* track appl_ptr so that we have a better chance of detecting
- * end of stream and not over running it.
- */
+ s->period_count = 0;
s->runtime = runtime;
- s->appl_ptr = s->runtime->control->appl_ptr -
- (runtime->period_size * runtime->periods);
/* Fill up the bestcomm bd queue and enable DMA.
* This will begin filling the PSC's fifo.
*/
spin_lock_irqsave(&psc_dma->lock, flags);
- if (substream->pstr->stream == SNDRV_PCM_STREAM_CAPTURE) {
+ if (substream->pstr->stream == SNDRV_PCM_STREAM_CAPTURE)
bcom_gen_bd_rx_reset(s->bcom_task);
- for (i = 0; i < runtime->periods; i++)
- if (!bcom_queue_full(s->bcom_task))
- psc_dma_bcom_enqueue_next_buffer(s);
- } else {
+ else
bcom_gen_bd_tx_reset(s->bcom_task);
- psc_dma_bcom_enqueue_tx(s);
- }
+
+ for (i = 0; i < runtime->periods; i++)
+ if (!bcom_queue_full(s->bcom_task))
+ psc_dma_bcom_enqueue_next_buffer(s);
bcom_enable(s->bcom_task);
spin_unlock_irqrestore(&psc_dma->lock, flags);
@@ -223,6 +149,8 @@ static int psc_dma_trigger(struct snd_pcm_substream *substream, int cmd)
break;
case SNDRV_PCM_TRIGGER_STOP:
+ dev_dbg(psc_dma->dev, "STOP: stream=%i periods_count=%i\n",
+ substream->pstr->stream, s->period_count);
s->active = 0;
spin_lock_irqsave(&psc_dma->lock, flags);
@@ -236,7 +164,8 @@ static int psc_dma_trigger(struct snd_pcm_substream *substream, int cmd)
break;
default:
- dev_dbg(psc_dma->dev, "invalid command\n");
+ dev_dbg(psc_dma->dev, "unhandled trigger: stream=%i cmd=%i\n",
+ substream->pstr->stream, cmd);
return -EINVAL;
}
@@ -343,7 +272,7 @@ psc_dma_pointer(struct snd_pcm_substream *substream)
else
s = &psc_dma->playback;
- count = s->period_current_pt - s->period_start;
+ count = s->period_current * s->period_bytes;
return bytes_to_frames(substream->runtime, count);
}
@@ -532,11 +461,9 @@ int mpc5200_audio_dma_create(struct of_device *op)
rc = request_irq(psc_dma->irq, &psc_dma_status_irq, IRQF_SHARED,
"psc-dma-status", psc_dma);
- rc |= request_irq(psc_dma->capture.irq,
- &psc_dma_bcom_irq_rx, IRQF_SHARED,
+ rc |= request_irq(psc_dma->capture.irq, &psc_dma_bcom_irq, IRQF_SHARED,
"psc-dma-capture", &psc_dma->capture);
- rc |= request_irq(psc_dma->playback.irq,
- &psc_dma_bcom_irq_tx, IRQF_SHARED,
+ rc |= request_irq(psc_dma->playback.irq, &psc_dma_bcom_irq, IRQF_SHARED,
"psc-dma-playback", &psc_dma->playback);
if (rc) {
ret = -ENODEV;
diff --git a/sound/soc/fsl/mpc5200_dma.h b/sound/soc/fsl/mpc5200_dma.h
index 8d396bb9d9fe..22208b373fb9 100644
--- a/sound/soc/fsl/mpc5200_dma.h
+++ b/sound/soc/fsl/mpc5200_dma.h
@@ -13,26 +13,25 @@
* @psc_dma: pointer back to parent psc_dma data structure
* @bcom_task: bestcomm task structure
* @irq: irq number for bestcomm task
- * @period_start: physical address of start of DMA region
* @period_end: physical address of end of DMA region
* @period_next_pt: physical address of next DMA buffer to enqueue
* @period_bytes: size of DMA period in bytes
+ * @ac97_slot_bits: Enable bits for turning on the correct AC97 slot
*/
struct psc_dma_stream {
struct snd_pcm_runtime *runtime;
- snd_pcm_uframes_t appl_ptr;
-
int active;
struct psc_dma *psc_dma;
struct bcom_task *bcom_task;
int irq;
struct snd_pcm_substream *stream;
- dma_addr_t period_start;
- dma_addr_t period_end;
- dma_addr_t period_next_pt;
- dma_addr_t period_current_pt;
+ int period_next;
+ int period_current;
int period_bytes;
- int period_size;
+ int period_count;
+
+ /* AC97 state */
+ u32 ac97_slot_bits;
};
/**
@@ -73,6 +72,15 @@ struct psc_dma {
} stats;
};
+/* Utility for retrieving psc_dma_stream structure from a substream */
+inline struct psc_dma_stream *
+to_psc_dma_stream(struct snd_pcm_substream *substream, struct psc_dma *psc_dma)
+{
+ if (substream->pstr->stream == SNDRV_PCM_STREAM_CAPTURE)
+ return &psc_dma->capture;
+ return &psc_dma->playback;
+}
+
int mpc5200_audio_dma_create(struct of_device *op);
int mpc5200_audio_dma_destroy(struct of_device *op);
diff --git a/sound/soc/fsl/mpc5200_psc_ac97.c b/sound/soc/fsl/mpc5200_psc_ac97.c
index c4ae3e096bb9..3dbc7f7cd7b9 100644
--- a/sound/soc/fsl/mpc5200_psc_ac97.c
+++ b/sound/soc/fsl/mpc5200_psc_ac97.c
@@ -130,6 +130,7 @@ static int psc_ac97_hw_analog_params(struct snd_pcm_substream *substream,
struct snd_soc_dai *cpu_dai)
{
struct psc_dma *psc_dma = cpu_dai->private_data;
+ struct psc_dma_stream *s = to_psc_dma_stream(substream, psc_dma);
dev_dbg(psc_dma->dev, "%s(substream=%p) p_size=%i p_bytes=%i"
" periods=%i buffer_size=%i buffer_bytes=%i channels=%i"
@@ -140,20 +141,10 @@ static int psc_ac97_hw_analog_params(struct snd_pcm_substream *substream,
params_channels(params), params_rate(params),
params_format(params));
-
- if (substream->pstr->stream == SNDRV_PCM_STREAM_CAPTURE) {
- if (params_channels(params) == 1)
- psc_dma->slots |= 0x00000100;
- else
- psc_dma->slots |= 0x00000300;
- } else {
- if (params_channels(params) == 1)
- psc_dma->slots |= 0x01000000;
- else
- psc_dma->slots |= 0x03000000;
- }
- out_be32(&psc_dma->psc_regs->ac97_slots, psc_dma->slots);
-
+ /* Determine the set of enable bits to turn on */
+ s->ac97_slot_bits = (params_channels(params) == 1) ? 0x100 : 0x300;
+ if (substream->pstr->stream != SNDRV_PCM_STREAM_CAPTURE)
+ s->ac97_slot_bits <<= 16;
return 0;
}
@@ -163,6 +154,8 @@ static int psc_ac97_hw_digital_params(struct snd_pcm_substream *substream,
{
struct psc_dma *psc_dma = cpu_dai->private_data;
+ dev_dbg(psc_dma->dev, "%s(substream=%p)\n", __func__, substream);
+
if (params_channels(params) == 1)
out_be32(&psc_dma->psc_regs->ac97_slots, 0x01000000);
else
@@ -176,14 +169,24 @@ static int psc_ac97_trigger(struct snd_pcm_substream *substream, int cmd,
{
struct snd_soc_pcm_runtime *rtd = substream->private_data;
struct psc_dma *psc_dma = rtd->dai->cpu_dai->private_data;
+ struct psc_dma_stream *s = to_psc_dma_stream(substream, psc_dma);
switch (cmd) {
+ case SNDRV_PCM_TRIGGER_START:
+ dev_dbg(psc_dma->dev, "AC97 START: stream=%i\n",
+ substream->pstr->stream);
+
+ /* Set the slot enable bits */
+ psc_dma->slots |= s->ac97_slot_bits;
+ out_be32(&psc_dma->psc_regs->ac97_slots, psc_dma->slots);
+ break;
+
case SNDRV_PCM_TRIGGER_STOP:
- if (substream->pstr->stream == SNDRV_PCM_STREAM_CAPTURE)
- psc_dma->slots &= 0xFFFF0000;
- else
- psc_dma->slots &= 0x0000FFFF;
+ dev_dbg(psc_dma->dev, "AC97 STOP: stream=%i\n",
+ substream->pstr->stream);
+ /* Clear the slot enable bits */
+ psc_dma->slots &= ~(s->ac97_slot_bits);
out_be32(&psc_dma->psc_regs->ac97_slots, psc_dma->slots);
break;
}
diff --git a/sound/soc/imx/mx27vis_wm8974.c b/sound/soc/imx/mx27vis_wm8974.c
index e4dcb539108a..0267d2d91685 100644
--- a/sound/soc/imx/mx27vis_wm8974.c
+++ b/sound/soc/imx/mx27vis_wm8974.c
@@ -157,7 +157,7 @@ static int mx27vis_hifi_hw_params(struct snd_pcm_substream *substream,
/* codec PLL input is 25 MHz */
- ret = codec_dai->ops->set_pll(codec_dai, IGNORED_ARG,
+ ret = codec_dai->ops->set_pll(codec_dai, IGNORED_ARG, IGNORED_ARG,
25000000, pll_out);
if (ret < 0) {
printk(KERN_ERR "Error when setting PLL input\n");
diff --git a/sound/soc/omap/Kconfig b/sound/soc/omap/Kconfig
index 653a362425df..61952aa6cd5a 100644
--- a/sound/soc/omap/Kconfig
+++ b/sound/soc/omap/Kconfig
@@ -43,12 +43,13 @@ config SND_OMAP_SOC_OSK5912
Say Y if you want to add support for SoC audio on osk5912.
config SND_OMAP_SOC_OVERO
- tristate "SoC Audio support for Gumstix Overo"
- depends on TWL4030_CORE && SND_OMAP_SOC && MACH_OVERO
+ tristate "SoC Audio support for Gumstix Overo and CompuLab CM-T35"
+ depends on TWL4030_CORE && SND_OMAP_SOC && (MACH_OVERO || MACH_CM_T35)
select SND_OMAP_SOC_MCBSP
select SND_SOC_TWL4030
help
- Say Y if you want to add support for SoC audio on the Gumstix Overo.
+ Say Y if you want to add support for SoC audio on the
+ Gumstix Overo or CompuLab CM-T35
config SND_OMAP_SOC_OMAP2EVM
tristate "SoC Audio support for OMAP2EVM board"
@@ -66,6 +67,15 @@ config SND_OMAP_SOC_OMAP3EVM
help
Say Y if you want to add support for SoC audio on the omap3evm board.
+config SND_OMAP_SOC_AM3517EVM
+ tristate "SoC Audio support for OMAP3517 / AM3517 EVM"
+ depends on SND_OMAP_SOC && MACH_OMAP3517EVM && I2C
+ select SND_OMAP_SOC_MCBSP
+ select SND_SOC_TLV320AIC23
+ help
+ Say Y if you want to add support for SoC audio on the OMAP3517 / AM3517
+ EVM.
+
config SND_OMAP_SOC_SDP3430
tristate "SoC Audio support for Texas Instruments SDP3430"
depends on TWL4030_CORE && SND_OMAP_SOC && MACH_OMAP_3430SDP
@@ -99,3 +109,10 @@ config SND_OMAP_SOC_ZOOM2
help
Say Y if you want to add support for Soc audio on Zoom2 board.
+config SND_OMAP_SOC_IGEP0020
+ tristate "SoC Audio support for IGEP v2"
+ depends on TWL4030_CORE && SND_OMAP_SOC && MACH_IGEP0020
+ select SND_OMAP_SOC_MCBSP
+ select SND_SOC_TWL4030
+ help
+ Say Y if you want to add support for Soc audio on IGEP v2 board.
diff --git a/sound/soc/omap/Makefile b/sound/soc/omap/Makefile
index 02d69471dcb5..d49458a29bb7 100644
--- a/sound/soc/omap/Makefile
+++ b/sound/soc/omap/Makefile
@@ -12,10 +12,12 @@ snd-soc-osk5912-objs := osk5912.o
snd-soc-overo-objs := overo.o
snd-soc-omap2evm-objs := omap2evm.o
snd-soc-omap3evm-objs := omap3evm.o
+snd-soc-am3517evm-objs := am3517evm.o
snd-soc-sdp3430-objs := sdp3430.o
snd-soc-omap3pandora-objs := omap3pandora.o
snd-soc-omap3beagle-objs := omap3beagle.o
snd-soc-zoom2-objs := zoom2.o
+snd-soc-igep0020-objs := igep0020.o
obj-$(CONFIG_SND_OMAP_SOC_N810) += snd-soc-n810.o
obj-$(CONFIG_SND_OMAP_SOC_AMS_DELTA) += snd-soc-ams-delta.o
@@ -23,7 +25,9 @@ obj-$(CONFIG_SND_OMAP_SOC_OSK5912) += snd-soc-osk5912.o
obj-$(CONFIG_SND_OMAP_SOC_OVERO) += snd-soc-overo.o
obj-$(CONFIG_MACH_OMAP2EVM) += snd-soc-omap2evm.o
obj-$(CONFIG_MACH_OMAP3EVM) += snd-soc-omap3evm.o
+obj-$(CONFIG_MACH_OMAP3517EVM) += snd-soc-am3517evm.o
obj-$(CONFIG_SND_OMAP_SOC_SDP3430) += snd-soc-sdp3430.o
obj-$(CONFIG_SND_OMAP_SOC_OMAP3_PANDORA) += snd-soc-omap3pandora.o
obj-$(CONFIG_SND_OMAP_SOC_OMAP3_BEAGLE) += snd-soc-omap3beagle.o
obj-$(CONFIG_SND_OMAP_SOC_ZOOM2) += snd-soc-zoom2.o
+obj-$(CONFIG_SND_OMAP_SOC_IGEP0020) += snd-soc-igep0020.o
diff --git a/sound/soc/omap/am3517evm.c b/sound/soc/omap/am3517evm.c
new file mode 100644
index 000000000000..135901b2ea11
--- /dev/null
+++ b/sound/soc/omap/am3517evm.c
@@ -0,0 +1,202 @@
+/*
+ * am3517evm.c -- ALSA SoC support for OMAP3517 / AM3517 EVM
+ *
+ * Author: Anuj Aggarwal <anuj.aggarwal@ti.com>
+ *
+ * Based on sound/soc/omap/beagle.c by Steve Sakoman
+ *
+ * Copyright (C) 2009 Texas Instruments Incorporated
+ *
+ * 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 version 2.
+ *
+ * This program is distributed "as is" WITHOUT ANY WARRANTY of any kind,
+ * whether express or implied; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ */
+
+#include <linux/clk.h>
+#include <linux/platform_device.h>
+#include <sound/core.h>
+#include <sound/pcm.h>
+#include <sound/soc.h>
+#include <sound/soc-dapm.h>
+
+#include <asm/mach-types.h>
+#include <mach/hardware.h>
+#include <mach/gpio.h>
+#include <plat/mcbsp.h>
+
+#include "omap-mcbsp.h"
+#include "omap-pcm.h"
+
+#include "../codecs/tlv320aic23.h"
+
+#define CODEC_CLOCK 12000000
+
+static int am3517evm_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_dai *codec_dai = rtd->dai->codec_dai;
+ struct snd_soc_dai *cpu_dai = rtd->dai->cpu_dai;
+ int ret;
+
+ /* Set codec DAI configuration */
+ ret = snd_soc_dai_set_fmt(codec_dai,
+ SND_SOC_DAIFMT_DSP_B |
+ SND_SOC_DAIFMT_NB_NF |
+ SND_SOC_DAIFMT_CBM_CFM);
+ if (ret < 0) {
+ printk(KERN_ERR "can't set codec DAI configuration\n");
+ return ret;
+ }
+
+ /* Set cpu DAI configuration */
+ ret = snd_soc_dai_set_fmt(cpu_dai,
+ SND_SOC_DAIFMT_DSP_B |
+ SND_SOC_DAIFMT_NB_NF |
+ SND_SOC_DAIFMT_CBM_CFM);
+ if (ret < 0) {
+ printk(KERN_ERR "can't set cpu DAI configuration\n");
+ return ret;
+ }
+
+ /* Set the codec system clock for DAC and ADC */
+ ret = snd_soc_dai_set_sysclk(codec_dai, 0,
+ CODEC_CLOCK, SND_SOC_CLOCK_IN);
+ if (ret < 0) {
+ printk(KERN_ERR "can't set codec system clock\n");
+ return ret;
+ }
+
+ ret = snd_soc_dai_set_sysclk(cpu_dai, OMAP_MCBSP_CLKR_SRC_CLKX, 0,
+ SND_SOC_CLOCK_IN);
+ if (ret < 0) {
+ printk(KERN_ERR "can't set CPU system clock OMAP_MCBSP_CLKR_SRC_CLKX\n");
+ return ret;
+ }
+
+ snd_soc_dai_set_sysclk(cpu_dai, OMAP_MCBSP_FSR_SRC_FSX, 0,
+ SND_SOC_CLOCK_IN);
+ if (ret < 0) {
+ printk(KERN_ERR "can't set CPU system clock OMAP_MCBSP_FSR_SRC_FSX\n");
+ return ret;
+ }
+
+ return 0;
+}
+
+static struct snd_soc_ops am3517evm_ops = {
+ .hw_params = am3517evm_hw_params,
+};
+
+/* am3517evm machine dapm widgets */
+static const struct snd_soc_dapm_widget tlv320aic23_dapm_widgets[] = {
+ SND_SOC_DAPM_HP("Line Out", NULL),
+ SND_SOC_DAPM_LINE("Line In", NULL),
+ SND_SOC_DAPM_MIC("Mic In", NULL),
+};
+
+static const struct snd_soc_dapm_route audio_map[] = {
+ /* Line Out connected to LLOUT, RLOUT */
+ {"Line Out", NULL, "LOUT"},
+ {"Line Out", NULL, "ROUT"},
+
+ {"LLINEIN", NULL, "Line In"},
+ {"RLINEIN", NULL, "Line In"},
+
+ {"MICIN", NULL, "Mic In"},
+};
+
+static int am3517evm_aic23_init(struct snd_soc_codec *codec)
+{
+ /* Add am3517-evm specific widgets */
+ snd_soc_dapm_new_controls(codec, tlv320aic23_dapm_widgets,
+ ARRAY_SIZE(tlv320aic23_dapm_widgets));
+
+ /* Set up davinci-evm specific audio path audio_map */
+ snd_soc_dapm_add_routes(codec, audio_map, ARRAY_SIZE(audio_map));
+
+ /* always connected */
+ snd_soc_dapm_enable_pin(codec, "Line Out");
+ snd_soc_dapm_enable_pin(codec, "Line In");
+ snd_soc_dapm_enable_pin(codec, "Mic In");
+
+ snd_soc_dapm_sync(codec);
+
+ return 0;
+}
+
+/* Digital audio interface glue - connects codec <--> CPU */
+static struct snd_soc_dai_link am3517evm_dai = {
+ .name = "TLV320AIC23",
+ .stream_name = "AIC23",
+ .cpu_dai = &omap_mcbsp_dai[0],
+ .codec_dai = &tlv320aic23_dai,
+ .init = am3517evm_aic23_init,
+ .ops = &am3517evm_ops,
+};
+
+/* Audio machine driver */
+static struct snd_soc_card snd_soc_am3517evm = {
+ .name = "am3517evm",
+ .platform = &omap_soc_platform,
+ .dai_link = &am3517evm_dai,
+ .num_links = 1,
+};
+
+/* Audio subsystem */
+static struct snd_soc_device am3517evm_snd_devdata = {
+ .card = &snd_soc_am3517evm,
+ .codec_dev = &soc_codec_dev_tlv320aic23,
+};
+
+static struct platform_device *am3517evm_snd_device;
+
+static int __init am3517evm_soc_init(void)
+{
+ int ret;
+
+ if (!machine_is_omap3517evm()) {
+ pr_err("Not OMAP3517 / AM3517 EVM!\n");
+ return -ENODEV;
+ }
+ pr_info("OMAP3517 / AM3517 EVM SoC init\n");
+
+ am3517evm_snd_device = platform_device_alloc("soc-audio", -1);
+ if (!am3517evm_snd_device) {
+ printk(KERN_ERR "Platform device allocation failed\n");
+ return -ENOMEM;
+ }
+
+ platform_set_drvdata(am3517evm_snd_device, &am3517evm_snd_devdata);
+ am3517evm_snd_devdata.dev = &am3517evm_snd_device->dev;
+ *(unsigned int *)am3517evm_dai.cpu_dai->private_data = 0; /* McBSP1 */
+
+ ret = platform_device_add(am3517evm_snd_device);
+ if (ret)
+ goto err1;
+
+ return 0;
+
+err1:
+ printk(KERN_ERR "Unable to add platform device\n");
+ platform_device_put(am3517evm_snd_device);
+
+ return ret;
+}
+
+static void __exit am3517evm_soc_exit(void)
+{
+ platform_device_unregister(am3517evm_snd_device);
+}
+
+module_init(am3517evm_soc_init);
+module_exit(am3517evm_soc_exit);
+
+MODULE_AUTHOR("Anuj Aggarwal <anuj.aggarwal@ti.com>");
+MODULE_DESCRIPTION("ALSA SoC OMAP3517 / AM3517 EVM");
+MODULE_LICENSE("GPL v2");
diff --git a/sound/soc/omap/ams-delta.c b/sound/soc/omap/ams-delta.c
index 5a5166ac7279..ae0fc9b135d4 100644
--- a/sound/soc/omap/ams-delta.c
+++ b/sound/soc/omap/ams-delta.c
@@ -40,7 +40,7 @@
/* Board specific DAPM widgets */
- const struct snd_soc_dapm_widget ams_delta_dapm_widgets[] = {
+static const struct snd_soc_dapm_widget ams_delta_dapm_widgets[] = {
/* Handset */
SND_SOC_DAPM_MIC("Mouthpiece", NULL),
SND_SOC_DAPM_HP("Earpiece", NULL),
@@ -81,7 +81,7 @@ static const char *ams_delta_audio_mode[] =
(1 << AMS_DELTA_SPEAKER))
#define AMS_DELTA_SPEAKERPHONE (AMS_DELTA_HANDSFREE | (1 << AMS_DELTA_AGC))
-unsigned short ams_delta_audio_mode_pins[] = {
+static const unsigned short ams_delta_audio_mode_pins[] = {
AMS_DELTA_MIXED,
AMS_DELTA_HANDSET,
AMS_DELTA_HANDSFREE,
diff --git a/sound/soc/omap/igep0020.c b/sound/soc/omap/igep0020.c
new file mode 100644
index 000000000000..3583c429f9be
--- /dev/null
+++ b/sound/soc/omap/igep0020.c
@@ -0,0 +1,148 @@
+/*
+ * igep0020.c -- SoC audio for IGEP v2
+ *
+ * Based on sound/soc/omap/overo.c by Steve Sakoman
+ *
+ * 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.
+ *
+ * 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., 51 Franklin St, Fifth Floor, Boston, MA
+ * 02110-1301 USA
+ *
+ */
+
+#include <linux/clk.h>
+#include <linux/platform_device.h>
+#include <sound/core.h>
+#include <sound/pcm.h>
+#include <sound/soc.h>
+#include <sound/soc-dapm.h>
+
+#include <asm/mach-types.h>
+#include <mach/hardware.h>
+#include <mach/gpio.h>
+#include <plat/mcbsp.h>
+
+#include "omap-mcbsp.h"
+#include "omap-pcm.h"
+#include "../codecs/twl4030.h"
+
+static int igep2_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_dai *codec_dai = rtd->dai->codec_dai;
+ struct snd_soc_dai *cpu_dai = rtd->dai->cpu_dai;
+ int ret;
+
+ /* Set codec DAI configuration */
+ ret = snd_soc_dai_set_fmt(codec_dai,
+ SND_SOC_DAIFMT_I2S |
+ SND_SOC_DAIFMT_NB_NF |
+ SND_SOC_DAIFMT_CBM_CFM);
+ if (ret < 0) {
+ printk(KERN_ERR "can't set codec DAI configuration\n");
+ return ret;
+ }
+
+ /* Set cpu DAI configuration */
+ ret = snd_soc_dai_set_fmt(cpu_dai,
+ SND_SOC_DAIFMT_I2S |
+ SND_SOC_DAIFMT_NB_NF |
+ SND_SOC_DAIFMT_CBM_CFM);
+ if (ret < 0) {
+ printk(KERN_ERR "can't set cpu DAI configuration\n");
+ return ret;
+ }
+
+ /* Set the codec system clock for DAC and ADC */
+ ret = snd_soc_dai_set_sysclk(codec_dai, 0, 26000000,
+ SND_SOC_CLOCK_IN);
+ if (ret < 0) {
+ printk(KERN_ERR "can't set codec system clock\n");
+ return ret;
+ }
+
+ return 0;
+}
+
+static struct snd_soc_ops igep2_ops = {
+ .hw_params = igep2_hw_params,
+};
+
+/* Digital audio interface glue - connects codec <--> CPU */
+static struct snd_soc_dai_link igep2_dai = {
+ .name = "TWL4030",
+ .stream_name = "TWL4030",
+ .cpu_dai = &omap_mcbsp_dai[0],
+ .codec_dai = &twl4030_dai[TWL4030_DAI_HIFI],
+ .ops = &igep2_ops,
+};
+
+/* Audio machine driver */
+static struct snd_soc_card snd_soc_card_igep2 = {
+ .name = "igep2",
+ .platform = &omap_soc_platform,
+ .dai_link = &igep2_dai,
+ .num_links = 1,
+};
+
+/* Audio subsystem */
+static struct snd_soc_device igep2_snd_devdata = {
+ .card = &snd_soc_card_igep2,
+ .codec_dev = &soc_codec_dev_twl4030,
+};
+
+static struct platform_device *igep2_snd_device;
+
+static int __init igep2_soc_init(void)
+{
+ int ret;
+
+ if (!machine_is_igep0020()) {
+ pr_debug("Not IGEP v2!\n");
+ return -ENODEV;
+ }
+ printk(KERN_INFO "IGEP v2 SoC init\n");
+
+ igep2_snd_device = platform_device_alloc("soc-audio", -1);
+ if (!igep2_snd_device) {
+ printk(KERN_ERR "Platform device allocation failed\n");
+ return -ENOMEM;
+ }
+
+ platform_set_drvdata(igep2_snd_device, &igep2_snd_devdata);
+ igep2_snd_devdata.dev = &igep2_snd_device->dev;
+ *(unsigned int *)igep2_dai.cpu_dai->private_data = 1; /* McBSP2 */
+
+ ret = platform_device_add(igep2_snd_device);
+ if (ret)
+ goto err1;
+
+ return 0;
+
+err1:
+ printk(KERN_ERR "Unable to add platform device\n");
+ platform_device_put(igep2_snd_device);
+
+ return ret;
+}
+module_init(igep2_soc_init);
+
+static void __exit igep2_soc_exit(void)
+{
+ platform_device_unregister(igep2_snd_device);
+}
+module_exit(igep2_soc_exit);
+
+MODULE_AUTHOR("Enric Balletbo i Serra <eballetbo@iseebcn.com>");
+MODULE_DESCRIPTION("ALSA SoC IGEP v2");
+MODULE_LICENSE("GPL");
diff --git a/sound/soc/omap/omap-mcbsp.c b/sound/soc/omap/omap-mcbsp.c
index 3341f49402ca..45be94201c89 100644
--- a/sound/soc/omap/omap-mcbsp.c
+++ b/sound/soc/omap/omap-mcbsp.c
@@ -49,6 +49,8 @@ struct omap_mcbsp_data {
*/
int active;
int configured;
+ unsigned int in_freq;
+ int clk_div;
};
#define to_mcbsp(priv) container_of((priv), struct omap_mcbsp_data, bus_id)
@@ -257,7 +259,7 @@ static int omap_mcbsp_dai_hw_params(struct snd_pcm_substream *substream,
int dma, bus_id = mcbsp_data->bus_id, id = cpu_dai->id;
int wlen, channels, wpf, sync_mode = OMAP_DMA_SYNC_ELEMENT;
unsigned long port;
- unsigned int format;
+ unsigned int format, div, framesize, master;
if (cpu_class_is_omap1()) {
dma = omap1_dma_reqs[bus_id][substream->stream];
@@ -294,28 +296,19 @@ static int omap_mcbsp_dai_hw_params(struct snd_pcm_substream *substream,
format = mcbsp_data->fmt & SND_SOC_DAIFMT_FORMAT_MASK;
wpf = channels = params_channels(params);
- switch (channels) {
- case 2:
- if (format == SND_SOC_DAIFMT_I2S) {
- /* Use dual-phase frames */
- regs->rcr2 |= RPHASE;
- regs->xcr2 |= XPHASE;
- /* Set 1 word per (McBSP) frame for phase1 and phase2 */
- wpf--;
- regs->rcr2 |= RFRLEN2(wpf - 1);
- regs->xcr2 |= XFRLEN2(wpf - 1);
- }
- case 1:
- case 4:
- /* Set word per (McBSP) frame for phase1 */
- regs->rcr1 |= RFRLEN1(wpf - 1);
- regs->xcr1 |= XFRLEN1(wpf - 1);
- break;
- default:
- /* Unsupported number of channels */
- return -EINVAL;
+ if (channels == 2 && format == SND_SOC_DAIFMT_I2S) {
+ /* Use dual-phase frames */
+ regs->rcr2 |= RPHASE;
+ regs->xcr2 |= XPHASE;
+ /* Set 1 word per (McBSP) frame for phase1 and phase2 */
+ wpf--;
+ regs->rcr2 |= RFRLEN2(wpf - 1);
+ regs->xcr2 |= XFRLEN2(wpf - 1);
}
+ regs->rcr1 |= RFRLEN1(wpf - 1);
+ regs->xcr1 |= XFRLEN1(wpf - 1);
+
switch (params_format(params)) {
case SNDRV_PCM_FORMAT_S16_LE:
/* Set word lengths */
@@ -330,15 +323,30 @@ static int omap_mcbsp_dai_hw_params(struct snd_pcm_substream *substream,
return -EINVAL;
}
+ /* In McBSP master modes, FRAME (i.e. sample rate) is generated
+ * by _counting_ BCLKs. Calculate frame size in BCLKs */
+ master = mcbsp_data->fmt & SND_SOC_DAIFMT_MASTER_MASK;
+ if (master == SND_SOC_DAIFMT_CBS_CFS) {
+ div = mcbsp_data->clk_div ? mcbsp_data->clk_div : 1;
+ framesize = (mcbsp_data->in_freq / div) / params_rate(params);
+
+ if (framesize < wlen * channels) {
+ printk(KERN_ERR "%s: not enough bandwidth for desired rate and "
+ "channels\n", __func__);
+ return -EINVAL;
+ }
+ } else
+ framesize = wlen * channels;
+
/* Set FS period and length in terms of bit clock periods */
switch (format) {
case SND_SOC_DAIFMT_I2S:
- regs->srgr2 |= FPER(wlen * channels - 1);
- regs->srgr1 |= FWID(wlen - 1);
+ regs->srgr2 |= FPER(framesize - 1);
+ regs->srgr1 |= FWID((framesize >> 1) - 1);
break;
case SND_SOC_DAIFMT_DSP_A:
case SND_SOC_DAIFMT_DSP_B:
- regs->srgr2 |= FPER(wlen * channels - 1);
+ regs->srgr2 |= FPER(framesize - 1);
regs->srgr1 |= FWID(0);
break;
}
@@ -454,6 +462,7 @@ static int omap_mcbsp_dai_set_clkdiv(struct snd_soc_dai *cpu_dai,
if (div_id != OMAP_MCBSP_CLKGDV)
return -ENODEV;
+ mcbsp_data->clk_div = div;
regs->srgr1 |= CLKGDV(div - 1);
return 0;
@@ -554,6 +563,8 @@ static int omap_mcbsp_dai_set_dai_sysclk(struct snd_soc_dai *cpu_dai,
struct omap_mcbsp_reg_cfg *regs = &mcbsp_data->regs;
int err = 0;
+ mcbsp_data->in_freq = freq;
+
switch (clk_id) {
case OMAP_MCBSP_SYSCLK_CLK:
regs->srgr2 |= CLKSM;
@@ -598,13 +609,13 @@ static struct snd_soc_dai_ops omap_mcbsp_dai_ops = {
.id = (link_id), \
.playback = { \
.channels_min = 1, \
- .channels_max = 4, \
+ .channels_max = 16, \
.rates = OMAP_MCBSP_RATES, \
.formats = SNDRV_PCM_FMTBIT_S16_LE, \
}, \
.capture = { \
.channels_min = 1, \
- .channels_max = 4, \
+ .channels_max = 16, \
.rates = OMAP_MCBSP_RATES, \
.formats = SNDRV_PCM_FMTBIT_S16_LE, \
}, \
diff --git a/sound/soc/omap/omap3evm.c b/sound/soc/omap/omap3evm.c
index 13aa380de162..f484dcd63408 100644
--- a/sound/soc/omap/omap3evm.c
+++ b/sound/soc/omap/omap3evm.c
@@ -93,10 +93,17 @@ static struct snd_soc_card snd_soc_omap3evm = {
.num_links = 1,
};
+/* twl4030 setup */
+static struct twl4030_setup_data twl4030_setup = {
+ .ramp_delay_value = 4,
+ .sysclk = 26000,
+};
+
/* Audio subsystem */
static struct snd_soc_device omap3evm_snd_devdata = {
.card = &snd_soc_omap3evm,
.codec_dev = &soc_codec_dev_twl4030,
+ .codec_data = &twl4030_setup,
};
static struct platform_device *omap3evm_snd_device;
diff --git a/sound/soc/omap/omap3pandora.c b/sound/soc/omap/omap3pandora.c
index 0cd06f5dd356..71b2c161158d 100644
--- a/sound/soc/omap/omap3pandora.c
+++ b/sound/soc/omap/omap3pandora.c
@@ -40,9 +40,12 @@
#define PREFIX "ASoC omap3pandora: "
-static int omap3pandora_cmn_hw_params(struct snd_soc_dai *codec_dai,
- struct snd_soc_dai *cpu_dai, unsigned int fmt)
+static int omap3pandora_cmn_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params, unsigned int fmt)
{
+ struct snd_soc_pcm_runtime *rtd = substream->private_data;
+ struct snd_soc_dai *codec_dai = rtd->dai->codec_dai;
+ struct snd_soc_dai *cpu_dai = rtd->dai->cpu_dai;
int ret;
/* Set codec DAI configuration */
@@ -68,8 +71,9 @@ static int omap3pandora_cmn_hw_params(struct snd_soc_dai *codec_dai,
}
/* Set McBSP clock to external */
- ret = snd_soc_dai_set_sysclk(cpu_dai, OMAP_MCBSP_SYSCLK_CLKS_EXT, 0,
- SND_SOC_CLOCK_IN);
+ ret = snd_soc_dai_set_sysclk(cpu_dai, OMAP_MCBSP_SYSCLK_CLKS_EXT,
+ 256 * params_rate(params),
+ SND_SOC_CLOCK_IN);
if (ret < 0) {
pr_err(PREFIX "can't set cpu system clock\n");
return ret;
@@ -87,11 +91,7 @@ static int omap3pandora_cmn_hw_params(struct snd_soc_dai *codec_dai,
static int omap3pandora_out_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_dai *codec_dai = rtd->dai->codec_dai;
- struct snd_soc_dai *cpu_dai = rtd->dai->cpu_dai;
-
- return omap3pandora_cmn_hw_params(codec_dai, cpu_dai,
+ return omap3pandora_cmn_hw_params(substream, params,
SND_SOC_DAIFMT_I2S |
SND_SOC_DAIFMT_IB_NF |
SND_SOC_DAIFMT_CBS_CFS);
@@ -100,11 +100,7 @@ static int omap3pandora_out_hw_params(struct snd_pcm_substream *substream,
static int omap3pandora_in_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_dai *codec_dai = rtd->dai->codec_dai;
- struct snd_soc_dai *cpu_dai = rtd->dai->cpu_dai;
-
- return omap3pandora_cmn_hw_params(codec_dai, cpu_dai,
+ return omap3pandora_cmn_hw_params(substream, params,
SND_SOC_DAIFMT_I2S |
SND_SOC_DAIFMT_NB_NF |
SND_SOC_DAIFMT_CBS_CFS);
diff --git a/sound/soc/omap/overo.c b/sound/soc/omap/overo.c
index ec4f8fd8b3a2..97a4d6308bd6 100644
--- a/sound/soc/omap/overo.c
+++ b/sound/soc/omap/overo.c
@@ -107,8 +107,8 @@ static int __init overo_soc_init(void)
{
int ret;
- if (!machine_is_overo()) {
- pr_debug("Not Overo!\n");
+ if (!(machine_is_overo() || machine_is_cm_t35())) {
+ pr_debug("Incomatible machine!\n");
return -ENODEV;
}
printk(KERN_INFO "overo SoC init\n");
diff --git a/sound/soc/pxa/Kconfig b/sound/soc/pxa/Kconfig
index dcb3181bb340..376e14a9c273 100644
--- a/sound/soc/pxa/Kconfig
+++ b/sound/soc/pxa/Kconfig
@@ -90,7 +90,8 @@ config SND_PXA2XX_SOC_E800
config SND_PXA2XX_SOC_EM_X270
tristate "SoC Audio support for CompuLab EM-x270, eXeda and CM-X300"
- depends on SND_PXA2XX_SOC && MACH_EM_X270
+ depends on SND_PXA2XX_SOC && (MACH_EM_X270 || MACH_EXEDA || \
+ MACH_CM_X300)
select SND_PXA2XX_SOC_AC97
select SND_SOC_WM9712
help
@@ -117,6 +118,15 @@ config SND_SOC_ZYLONITE
Say Y if you want to add support for SoC audio on the
Marvell Zylonite reference platform.
+config SND_SOC_RAUMFELD
+ tristate "SoC Audio support Raumfeld audio adapter"
+ depends on SND_PXA2XX_SOC && (MACH_RAUMFELD_SPEAKER || MACH_RAUMFELD_CONNECTOR)
+ select SND_PXA_SOC_SSP
+ select SND_SOC_CS4270
+ select SND_SOC_AK4104
+ help
+ Say Y if you want to add support for SoC audio on Raumfeld devices
+
config SND_PXA2XX_SOC_MAGICIAN
tristate "SoC Audio support for HTC Magician"
depends on SND_PXA2XX_SOC && MACH_MAGICIAN
diff --git a/sound/soc/pxa/Makefile b/sound/soc/pxa/Makefile
index 6e096b480335..f3e08fd40ca2 100644
--- a/sound/soc/pxa/Makefile
+++ b/sound/soc/pxa/Makefile
@@ -23,6 +23,7 @@ snd-soc-zylonite-objs := zylonite.o
snd-soc-magician-objs := magician.o
snd-soc-mioa701-objs := mioa701_wm9713.o
snd-soc-imote2-objs := imote2.o
+snd-soc-raumfeld-objs := raumfeld.o
obj-$(CONFIG_SND_PXA2XX_SOC_CORGI) += snd-soc-corgi.o
obj-$(CONFIG_SND_PXA2XX_SOC_POODLE) += snd-soc-poodle.o
@@ -37,3 +38,4 @@ obj-$(CONFIG_SND_PXA2XX_SOC_MAGICIAN) += snd-soc-magician.o
obj-$(CONFIG_SND_PXA2XX_SOC_MIOA701) += snd-soc-mioa701.o
obj-$(CONFIG_SND_SOC_ZYLONITE) += snd-soc-zylonite.o
obj-$(CONFIG_SND_PXA2XX_SOC_IMOTE2) += snd-soc-imote2.o
+obj-$(CONFIG_SND_SOC_RAUMFELD) += snd-soc-raumfeld.o
diff --git a/sound/soc/pxa/magician.c b/sound/soc/pxa/magician.c
index 9f7c61e23daf..4c8d99a8d386 100644
--- a/sound/soc/pxa/magician.c
+++ b/sound/soc/pxa/magician.c
@@ -213,7 +213,7 @@ static int magician_playback_hw_params(struct snd_pcm_substream *substream,
return ret;
/* set SSP audio pll clock */
- ret = snd_soc_dai_set_pll(cpu_dai, 0, 0, acps);
+ ret = snd_soc_dai_set_pll(cpu_dai, 0, 0, 0, acps);
if (ret < 0)
return ret;
diff --git a/sound/soc/pxa/pxa-ssp.c b/sound/soc/pxa/pxa-ssp.c
index d11a6d7e384a..3bd7712f029b 100644
--- a/sound/soc/pxa/pxa-ssp.c
+++ b/sound/soc/pxa/pxa-ssp.c
@@ -305,8 +305,8 @@ static int pxa_ssp_set_dai_clkdiv(struct snd_soc_dai *cpu_dai,
/*
* Configure the PLL frequency pxa27x and (afaik - pxa320 only)
*/
-static int pxa_ssp_set_dai_pll(struct snd_soc_dai *cpu_dai,
- int pll_id, unsigned int freq_in, unsigned int freq_out)
+static int pxa_ssp_set_dai_pll(struct snd_soc_dai *cpu_dai, int pll_id,
+ int source, unsigned int freq_in, unsigned int freq_out)
{
struct ssp_priv *priv = cpu_dai->private_data;
struct ssp_device *ssp = priv->dev.ssp;
@@ -760,13 +760,13 @@ struct snd_soc_dai pxa_ssp_dai[] = {
.resume = pxa_ssp_resume,
.playback = {
.channels_min = 1,
- .channels_max = 2,
+ .channels_max = 8,
.rates = PXA_SSP_RATES,
.formats = PXA_SSP_FORMATS,
},
.capture = {
.channels_min = 1,
- .channels_max = 2,
+ .channels_max = 8,
.rates = PXA_SSP_RATES,
.formats = PXA_SSP_FORMATS,
},
@@ -780,13 +780,13 @@ struct snd_soc_dai pxa_ssp_dai[] = {
.resume = pxa_ssp_resume,
.playback = {
.channels_min = 1,
- .channels_max = 2,
+ .channels_max = 8,
.rates = PXA_SSP_RATES,
.formats = PXA_SSP_FORMATS,
},
.capture = {
.channels_min = 1,
- .channels_max = 2,
+ .channels_max = 8,
.rates = PXA_SSP_RATES,
.formats = PXA_SSP_FORMATS,
},
@@ -801,13 +801,13 @@ struct snd_soc_dai pxa_ssp_dai[] = {
.resume = pxa_ssp_resume,
.playback = {
.channels_min = 1,
- .channels_max = 2,
+ .channels_max = 8,
.rates = PXA_SSP_RATES,
.formats = PXA_SSP_FORMATS,
},
.capture = {
.channels_min = 1,
- .channels_max = 2,
+ .channels_max = 8,
.rates = PXA_SSP_RATES,
.formats = PXA_SSP_FORMATS,
},
@@ -822,13 +822,13 @@ struct snd_soc_dai pxa_ssp_dai[] = {
.resume = pxa_ssp_resume,
.playback = {
.channels_min = 1,
- .channels_max = 2,
+ .channels_max = 8,
.rates = PXA_SSP_RATES,
.formats = PXA_SSP_FORMATS,
},
.capture = {
.channels_min = 1,
- .channels_max = 2,
+ .channels_max = 8,
.rates = PXA_SSP_RATES,
.formats = PXA_SSP_FORMATS,
},
diff --git a/sound/soc/pxa/raumfeld.c b/sound/soc/pxa/raumfeld.c
new file mode 100644
index 000000000000..acfce1c0f1c9
--- /dev/null
+++ b/sound/soc/pxa/raumfeld.c
@@ -0,0 +1,335 @@
+/*
+ * raumfeld_audio.c -- SoC audio for Raumfeld audio devices
+ *
+ * Copyright (c) 2009 Daniel Mack <daniel@caiaq.de>
+ *
+ * based on code from:
+ *
+ * Wolfson Microelectronics PLC.
+ * Openedhand Ltd.
+ * Liam Girdwood <lrg@slimlogic.co.uk>
+ * 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/i2c.h>
+#include <linux/delay.h>
+#include <linux/gpio.h>
+#include <sound/pcm.h>
+#include <sound/soc.h>
+#include <sound/soc-dapm.h>
+
+#include <asm/mach-types.h>
+
+#include "../codecs/cs4270.h"
+#include "../codecs/ak4104.h"
+#include "pxa2xx-pcm.h"
+#include "pxa-ssp.h"
+
+#define GPIO_SPDIF_RESET (38)
+#define GPIO_MCLK_RESET (111)
+#define GPIO_CODEC_RESET (120)
+
+static struct i2c_client *max9486_client;
+static struct i2c_board_info max9486_hwmon_info = {
+ I2C_BOARD_INFO("max9485", 0x63),
+};
+
+#define MAX9485_MCLK_FREQ_112896 0x22
+#define MAX9485_MCLK_FREQ_122880 0x23
+
+static void set_max9485_clk(char clk)
+{
+ i2c_master_send(max9486_client, &clk, 1);
+}
+
+static void raumfeld_enable_audio(bool en)
+{
+ if (en) {
+ gpio_set_value(GPIO_MCLK_RESET, 1);
+
+ /* wait some time to let the clocks become stable */
+ msleep(100);
+
+ gpio_set_value(GPIO_SPDIF_RESET, 1);
+ gpio_set_value(GPIO_CODEC_RESET, 1);
+ } else {
+ gpio_set_value(GPIO_MCLK_RESET, 0);
+ gpio_set_value(GPIO_SPDIF_RESET, 0);
+ gpio_set_value(GPIO_CODEC_RESET, 0);
+ }
+}
+
+/* CS4270 */
+static int raumfeld_cs4270_startup(struct snd_pcm_substream *substream)
+{
+ struct snd_soc_pcm_runtime *rtd = substream->private_data;
+ struct snd_soc_dai *codec_dai = rtd->dai->codec_dai;
+
+ set_max9485_clk(MAX9485_MCLK_FREQ_112896);
+
+ return snd_soc_dai_set_sysclk(codec_dai, 0, 11289600, 0);
+}
+
+static int raumfeld_cs4270_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_dai *codec_dai = rtd->dai->codec_dai;
+ struct snd_soc_dai *cpu_dai = rtd->dai->cpu_dai;
+ unsigned int fmt, clk = 0;
+ int ret = 0;
+
+ switch (params_rate(params)) {
+ case 8000:
+ case 16000:
+ case 48000:
+ case 96000:
+ set_max9485_clk(MAX9485_MCLK_FREQ_122880);
+ clk = 12288000;
+ break;
+ case 11025:
+ case 22050:
+ case 44100:
+ case 88200:
+ set_max9485_clk(MAX9485_MCLK_FREQ_112896);
+ clk = 11289600;
+ break;
+ }
+
+ fmt = SND_SOC_DAIFMT_I2S |
+ SND_SOC_DAIFMT_NB_NF |
+ SND_SOC_DAIFMT_CBS_CFS;
+
+ /* setup the CODEC DAI */
+ ret = snd_soc_dai_set_fmt(codec_dai, fmt);
+ if (ret < 0)
+ return ret;
+
+ ret = snd_soc_dai_set_sysclk(codec_dai, 0, clk, 0);
+ if (ret < 0)
+ return ret;
+
+ /* setup the CPU DAI */
+ ret = snd_soc_dai_set_pll(cpu_dai, 0, 0, 0, clk);
+ if (ret < 0)
+ return ret;
+
+ ret = snd_soc_dai_set_fmt(cpu_dai, fmt);
+ if (ret < 0)
+ return ret;
+
+ ret = snd_soc_dai_set_clkdiv(cpu_dai, PXA_SSP_DIV_SCR, 4);
+ if (ret < 0)
+ return ret;
+
+ ret = snd_soc_dai_set_sysclk(cpu_dai, PXA_SSP_CLK_EXT, 0, 1);
+ if (ret < 0)
+ return ret;
+
+ return 0;
+}
+
+static struct snd_soc_ops raumfeld_cs4270_ops = {
+ .startup = raumfeld_cs4270_startup,
+ .hw_params = raumfeld_cs4270_hw_params,
+};
+
+static int raumfeld_line_suspend(struct platform_device *pdev, pm_message_t state)
+{
+ raumfeld_enable_audio(false);
+ return 0;
+}
+
+static int raumfeld_line_resume(struct platform_device *pdev)
+{
+ raumfeld_enable_audio(true);
+ return 0;
+}
+
+static struct snd_soc_dai_link raumfeld_line_dai = {
+ .name = "CS4270",
+ .stream_name = "CS4270",
+ .cpu_dai = &pxa_ssp_dai[PXA_DAI_SSP1],
+ .codec_dai = &cs4270_dai,
+ .ops = &raumfeld_cs4270_ops,
+};
+
+static struct snd_soc_card snd_soc_line_raumfeld = {
+ .name = "Raumfeld analog",
+ .platform = &pxa2xx_soc_platform,
+ .dai_link = &raumfeld_line_dai,
+ .suspend_post = raumfeld_line_suspend,
+ .resume_pre = raumfeld_line_resume,
+ .num_links = 1,
+};
+
+
+/* AK4104 */
+
+static int raumfeld_ak4104_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_dai *codec_dai = rtd->dai->codec_dai;
+ struct snd_soc_dai *cpu_dai = rtd->dai->cpu_dai;
+ int fmt, ret = 0, clk = 0;
+
+ switch (params_rate(params)) {
+ case 8000:
+ case 16000:
+ case 48000:
+ case 96000:
+ set_max9485_clk(MAX9485_MCLK_FREQ_122880);
+ clk = 12288000;
+ break;
+ case 11025:
+ case 22050:
+ case 44100:
+ case 88200:
+ set_max9485_clk(MAX9485_MCLK_FREQ_112896);
+ clk = 11289600;
+ break;
+ }
+
+ fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF;
+
+ /* setup the CODEC DAI */
+ ret = snd_soc_dai_set_fmt(codec_dai, fmt | SND_SOC_DAIFMT_CBS_CFS);
+ if (ret < 0)
+ return ret;
+
+ /* setup the CPU DAI */
+ ret = snd_soc_dai_set_pll(cpu_dai, 0, 0, 0, clk);
+ if (ret < 0)
+ return ret;
+
+ ret = snd_soc_dai_set_fmt(cpu_dai, fmt | SND_SOC_DAIFMT_CBS_CFS);
+ if (ret < 0)
+ return ret;
+
+ ret = snd_soc_dai_set_clkdiv(cpu_dai, PXA_SSP_DIV_SCR, 4);
+ if (ret < 0)
+ return ret;
+
+ ret = snd_soc_dai_set_sysclk(cpu_dai, PXA_SSP_CLK_EXT, 0, 1);
+ if (ret < 0)
+ return ret;
+
+ return 0;
+}
+
+static struct snd_soc_ops raumfeld_ak4104_ops = {
+ .hw_params = raumfeld_ak4104_hw_params,
+};
+
+static struct snd_soc_dai_link raumfeld_spdif_dai = {
+ .name = "ak4104",
+ .stream_name = "Playback",
+ .cpu_dai = &pxa_ssp_dai[PXA_DAI_SSP2],
+ .codec_dai = &ak4104_dai,
+ .ops = &raumfeld_ak4104_ops,
+};
+
+static struct snd_soc_card snd_soc_spdif_raumfeld = {
+ .name = "Raumfeld S/PDIF",
+ .platform = &pxa2xx_soc_platform,
+ .dai_link = &raumfeld_spdif_dai,
+ .num_links = 1
+};
+
+/* raumfeld_audio audio subsystem */
+static struct snd_soc_device raumfeld_line_devdata = {
+ .card = &snd_soc_line_raumfeld,
+ .codec_dev = &soc_codec_device_cs4270,
+};
+
+static struct snd_soc_device raumfeld_spdif_devdata = {
+ .card = &snd_soc_spdif_raumfeld,
+ .codec_dev = &soc_codec_device_ak4104,
+};
+
+static struct platform_device *raumfeld_audio_line_device;
+static struct platform_device *raumfeld_audio_spdif_device;
+
+static int __init raumfeld_audio_init(void)
+{
+ int ret;
+
+ if (!machine_is_raumfeld_speaker() &&
+ !machine_is_raumfeld_connector())
+ return 0;
+
+ max9486_client = i2c_new_device(i2c_get_adapter(0),
+ &max9486_hwmon_info);
+
+ if (!max9486_client)
+ return -ENOMEM;
+
+ set_max9485_clk(MAX9485_MCLK_FREQ_122880);
+
+ /* LINE */
+ raumfeld_audio_line_device = platform_device_alloc("soc-audio", 0);
+ if (!raumfeld_audio_line_device)
+ return -ENOMEM;
+
+ platform_set_drvdata(raumfeld_audio_line_device,
+ &raumfeld_line_devdata);
+ raumfeld_line_devdata.dev = &raumfeld_audio_line_device->dev;
+ ret = platform_device_add(raumfeld_audio_line_device);
+ if (ret)
+ platform_device_put(raumfeld_audio_line_device);
+
+ /* no S/PDIF on Speakers */
+ if (machine_is_raumfeld_speaker())
+ return ret;
+
+ /* S/PDIF */
+ raumfeld_audio_spdif_device = platform_device_alloc("soc-audio", 1);
+ if (!raumfeld_audio_spdif_device) {
+ platform_device_put(raumfeld_audio_line_device);
+ return -ENOMEM;
+ }
+
+ platform_set_drvdata(raumfeld_audio_spdif_device,
+ &raumfeld_spdif_devdata);
+ raumfeld_spdif_devdata.dev = &raumfeld_audio_spdif_device->dev;
+ ret = platform_device_add(raumfeld_audio_spdif_device);
+ if (ret) {
+ platform_device_put(raumfeld_audio_line_device);
+ platform_device_put(raumfeld_audio_spdif_device);
+ }
+
+ raumfeld_enable_audio(true);
+
+ return ret;
+}
+
+static void __exit raumfeld_audio_exit(void)
+{
+ raumfeld_enable_audio(false);
+
+ platform_device_unregister(raumfeld_audio_line_device);
+
+ if (machine_is_raumfeld_connector())
+ platform_device_unregister(raumfeld_audio_spdif_device);
+
+ i2c_unregister_device(max9486_client);
+
+ gpio_free(GPIO_MCLK_RESET);
+ gpio_free(GPIO_CODEC_RESET);
+ gpio_free(GPIO_SPDIF_RESET);
+}
+
+module_init(raumfeld_audio_init);
+module_exit(raumfeld_audio_exit);
+
+/* Module information */
+MODULE_AUTHOR("Daniel Mack <daniel@caiaq.de>");
+MODULE_DESCRIPTION("Raumfeld audio SoC");
+MODULE_LICENSE("GPL");
diff --git a/sound/soc/pxa/zylonite.c b/sound/soc/pxa/zylonite.c
index 9a386b4c4ed1..dd678ae24398 100644
--- a/sound/soc/pxa/zylonite.c
+++ b/sound/soc/pxa/zylonite.c
@@ -74,7 +74,8 @@ static const struct snd_soc_dapm_route audio_map[] = {
static int zylonite_wm9713_init(struct snd_soc_codec *codec)
{
if (clk_pout)
- snd_soc_dai_set_pll(&codec->dai[0], 0, clk_get_rate(pout), 0);
+ snd_soc_dai_set_pll(&codec->dai[0], 0, 0,
+ clk_get_rate(pout), 0);
snd_soc_dapm_new_controls(codec, zylonite_dapm_widgets,
ARRAY_SIZE(zylonite_dapm_widgets));
@@ -128,7 +129,7 @@ static int zylonite_voice_hw_params(struct snd_pcm_substream *substream,
if (ret < 0)
return ret;
- ret = snd_soc_dai_set_pll(cpu_dai, 0, 0, pll_out);
+ ret = snd_soc_dai_set_pll(cpu_dai, 0, 0, 0, pll_out);
if (ret < 0)
return ret;
diff --git a/sound/soc/s3c24xx/Kconfig b/sound/soc/s3c24xx/Kconfig
index 923428fc1adb..b489f1ae103d 100644
--- a/sound/soc/s3c24xx/Kconfig
+++ b/sound/soc/s3c24xx/Kconfig
@@ -24,6 +24,9 @@ config SND_S3C64XX_SOC_I2S
select SND_S3C_I2SV2_SOC
select S3C64XX_DMA
+config SND_S3C_SOC_PCM
+ tristate
+
config SND_S3C2443_SOC_AC97
tristate
select S3C2410_DMA
@@ -56,6 +59,15 @@ config SND_S3C24XX_SOC_JIVE_WM8750
help
Sat Y if you want to add support for SoC audio on the Jive.
+config SND_S3C64XX_SOC_WM8580
+ tristate "SoC I2S Audio support for WM8580 on SMDK64XX"
+ depends on SND_S3C24XX_SOC && (MACH_SMDK6400 || MACH_SMDK6410)
+ depends on BROKEN
+ select SND_SOC_WM8580
+ select SND_S3C64XX_SOC_I2S
+ help
+ Sat Y if you want to add support for SoC audio on the SMDK64XX.
+
config SND_S3C24XX_SOC_SMDK2443_WM9710
tristate "SoC AC97 Audio support for SMDK2443 - WM9710"
depends on SND_S3C24XX_SOC && MACH_SMDK2443
diff --git a/sound/soc/s3c24xx/Makefile b/sound/soc/s3c24xx/Makefile
index 99f5a7dd3fc6..b744657733d7 100644
--- a/sound/soc/s3c24xx/Makefile
+++ b/sound/soc/s3c24xx/Makefile
@@ -1,10 +1,11 @@
# S3c24XX Platform Support
-snd-soc-s3c24xx-objs := s3c24xx-pcm.o
+snd-soc-s3c24xx-objs := s3c-dma.o
snd-soc-s3c24xx-i2s-objs := s3c24xx-i2s.o
snd-soc-s3c2412-i2s-objs := s3c2412-i2s.o
snd-soc-s3c64xx-i2s-objs := s3c64xx-i2s.o
snd-soc-s3c2443-ac97-objs := s3c2443-ac97.o
snd-soc-s3c-i2s-v2-objs := s3c-i2s-v2.o
+snd-soc-s3c-pcm-objs := s3c-pcm.o
obj-$(CONFIG_SND_S3C24XX_SOC) += snd-soc-s3c24xx.o
obj-$(CONFIG_SND_S3C24XX_SOC_I2S) += snd-soc-s3c24xx-i2s.o
@@ -12,6 +13,7 @@ obj-$(CONFIG_SND_S3C2443_SOC_AC97) += snd-soc-s3c2443-ac97.o
obj-$(CONFIG_SND_S3C2412_SOC_I2S) += snd-soc-s3c2412-i2s.o
obj-$(CONFIG_SND_S3C64XX_SOC_I2S) += snd-soc-s3c64xx-i2s.o
obj-$(CONFIG_SND_S3C_I2SV2_SOC) += snd-soc-s3c-i2s-v2.o
+obj-$(CONFIG_SND_S3C_SOC_PCM) += snd-soc-s3c-pcm.o
# S3C24XX Machine Support
snd-soc-jive-wm8750-objs := jive_wm8750.o
@@ -23,6 +25,7 @@ snd-soc-s3c24xx-uda134x-objs := s3c24xx_uda134x.o
snd-soc-s3c24xx-simtec-objs := s3c24xx_simtec.o
snd-soc-s3c24xx-simtec-hermes-objs := s3c24xx_simtec_hermes.o
snd-soc-s3c24xx-simtec-tlv320aic23-objs := s3c24xx_simtec_tlv320aic23.o
+snd-soc-smdk64xx-wm8580-objs := smdk64xx_wm8580.o
obj-$(CONFIG_SND_S3C24XX_SOC_JIVE_WM8750) += snd-soc-jive-wm8750.o
obj-$(CONFIG_SND_S3C24XX_SOC_NEO1973_WM8753) += snd-soc-neo1973-wm8753.o
@@ -33,4 +36,5 @@ obj-$(CONFIG_SND_S3C24XX_SOC_S3C24XX_UDA134X) += snd-soc-s3c24xx-uda134x.o
obj-$(CONFIG_SND_S3C24XX_SOC_SIMTEC) += snd-soc-s3c24xx-simtec.o
obj-$(CONFIG_SND_S3C24XX_SOC_SIMTEC_HERMES) += snd-soc-s3c24xx-simtec-hermes.o
obj-$(CONFIG_SND_S3C24XX_SOC_SIMTEC_TLV320AIC23) += snd-soc-s3c24xx-simtec-tlv320aic23.o
+obj-$(CONFIG_SND_S3C64XX_SOC_WM8580) += snd-soc-smdk64xx-wm8580.o
diff --git a/sound/soc/s3c24xx/jive_wm8750.c b/sound/soc/s3c24xx/jive_wm8750.c
index 93e6c87b7399..59dc2c6b56d9 100644
--- a/sound/soc/s3c24xx/jive_wm8750.c
+++ b/sound/soc/s3c24xx/jive_wm8750.c
@@ -25,7 +25,7 @@
#include <asm/mach-types.h>
-#include "s3c24xx-pcm.h"
+#include "s3c-dma.h"
#include "s3c2412-i2s.h"
#include "../codecs/wm8750.h"
diff --git a/sound/soc/s3c24xx/ln2440sbc_alc650.c b/sound/soc/s3c24xx/ln2440sbc_alc650.c
index 12c71482d258..d00d359a03e6 100644
--- a/sound/soc/s3c24xx/ln2440sbc_alc650.c
+++ b/sound/soc/s3c24xx/ln2440sbc_alc650.c
@@ -24,7 +24,7 @@
#include <sound/soc-dapm.h>
#include "../codecs/ac97.h"
-#include "s3c24xx-pcm.h"
+#include "s3c-dma.h"
#include "s3c24xx-ac97.h"
static struct snd_soc_card ln2440sbc;
diff --git a/sound/soc/s3c24xx/neo1973_gta02_wm8753.c b/sound/soc/s3c24xx/neo1973_gta02_wm8753.c
index 0c52e36ddd87..dea83d30a5c9 100644
--- a/sound/soc/s3c24xx/neo1973_gta02_wm8753.c
+++ b/sound/soc/s3c24xx/neo1973_gta02_wm8753.c
@@ -32,7 +32,7 @@
#include <asm/io.h>
#include <mach/gta02.h>
#include "../codecs/wm8753.h"
-#include "s3c24xx-pcm.h"
+#include "s3c-dma.h"
#include "s3c24xx-i2s.h"
static struct snd_soc_card neo1973_gta02;
@@ -119,7 +119,7 @@ static int neo1973_gta02_hifi_hw_params(struct snd_pcm_substream *substream,
return ret;
/* codec PLL input is PCLK/4 */
- ret = snd_soc_dai_set_pll(codec_dai, WM8753_PLL1,
+ ret = snd_soc_dai_set_pll(codec_dai, WM8753_PLL1, 0,
iis_clkrate / 4, pll_out);
if (ret < 0)
return ret;
@@ -133,7 +133,7 @@ static int neo1973_gta02_hifi_hw_free(struct snd_pcm_substream *substream)
struct snd_soc_dai *codec_dai = rtd->dai->codec_dai;
/* disable the PLL */
- return snd_soc_dai_set_pll(codec_dai, WM8753_PLL1, 0, 0);
+ return snd_soc_dai_set_pll(codec_dai, WM8753_PLL1, 0, 0, 0);
}
/*
@@ -183,7 +183,7 @@ static int neo1973_gta02_voice_hw_params(
return ret;
/* configue and enable PLL for 12.288MHz output */
- ret = snd_soc_dai_set_pll(codec_dai, WM8753_PLL2,
+ ret = snd_soc_dai_set_pll(codec_dai, WM8753_PLL2, 0,
iis_clkrate / 4, 12288000);
if (ret < 0)
return ret;
@@ -197,7 +197,7 @@ static int neo1973_gta02_voice_hw_free(struct snd_pcm_substream *substream)
struct snd_soc_dai *codec_dai = rtd->dai->codec_dai;
/* disable the PLL */
- return snd_soc_dai_set_pll(codec_dai, WM8753_PLL2, 0, 0);
+ return snd_soc_dai_set_pll(codec_dai, WM8753_PLL2, 0, 0, 0);
}
static struct snd_soc_ops neo1973_gta02_voice_ops = {
diff --git a/sound/soc/s3c24xx/neo1973_wm8753.c b/sound/soc/s3c24xx/neo1973_wm8753.c
index 906709e6dd5f..0cb4f86f6d1e 100644
--- a/sound/soc/s3c24xx/neo1973_wm8753.c
+++ b/sound/soc/s3c24xx/neo1973_wm8753.c
@@ -29,7 +29,6 @@
#include <mach/regs-clock.h>
#include <mach/regs-gpio.h>
#include <mach/hardware.h>
-#include <plat/audio.h>
#include <linux/io.h>
#include <mach/spi-gpio.h>
@@ -37,7 +36,7 @@
#include "../codecs/wm8753.h"
#include "lm4857.h"
-#include "s3c24xx-pcm.h"
+#include "s3c-dma.h"
#include "s3c24xx-i2s.h"
/* define the scenarios */
@@ -137,7 +136,7 @@ static int neo1973_hifi_hw_params(struct snd_pcm_substream *substream,
return ret;
/* codec PLL input is PCLK/4 */
- ret = snd_soc_dai_set_pll(codec_dai, WM8753_PLL1,
+ ret = snd_soc_dai_set_pll(codec_dai, WM8753_PLL1, 0,
iis_clkrate / 4, pll_out);
if (ret < 0)
return ret;
@@ -153,7 +152,7 @@ static int neo1973_hifi_hw_free(struct snd_pcm_substream *substream)
pr_debug("Entered %s\n", __func__);
/* disable the PLL */
- return snd_soc_dai_set_pll(codec_dai, WM8753_PLL1, 0, 0);
+ return snd_soc_dai_set_pll(codec_dai, WM8753_PLL1, 0, 0, 0);
}
/*
@@ -203,7 +202,7 @@ static int neo1973_voice_hw_params(struct snd_pcm_substream *substream,
return ret;
/* configue and enable PLL for 12.288MHz output */
- ret = snd_soc_dai_set_pll(codec_dai, WM8753_PLL2,
+ ret = snd_soc_dai_set_pll(codec_dai, WM8753_PLL2, 0,
iis_clkrate / 4, 12288000);
if (ret < 0)
return ret;
@@ -219,7 +218,7 @@ static int neo1973_voice_hw_free(struct snd_pcm_substream *substream)
pr_debug("Entered %s\n", __func__);
/* disable the PLL */
- return snd_soc_dai_set_pll(codec_dai, WM8753_PLL2, 0, 0);
+ return snd_soc_dai_set_pll(codec_dai, WM8753_PLL2, 0, 0, 0);
}
static struct snd_soc_ops neo1973_voice_ops = {
diff --git a/sound/soc/s3c24xx/s3c24xx-pcm.c b/sound/soc/s3c24xx/s3c-dma.c
index 1f35c6fcf5fd..7725e26d6c91 100644
--- a/sound/soc/s3c24xx/s3c24xx-pcm.c
+++ b/sound/soc/s3c24xx/s3c-dma.c
@@ -1,5 +1,5 @@
/*
- * s3c24xx-pcm.c -- ALSA Soc Audio Layer
+ * s3c-dma.c -- ALSA Soc Audio Layer
*
* (c) 2006 Wolfson Microelectronics PLC.
* Graeme Gregory graeme.gregory@wolfsonmicro.com or linux@wolfsonmicro.com
@@ -29,11 +29,10 @@
#include <asm/dma.h>
#include <mach/hardware.h>
#include <mach/dma.h>
-#include <plat/audio.h>
-#include "s3c24xx-pcm.h"
+#include "s3c-dma.h"
-static const struct snd_pcm_hardware s3c24xx_pcm_hardware = {
+static const struct snd_pcm_hardware s3c_dma_hardware = {
.info = SNDRV_PCM_INFO_INTERLEAVED |
SNDRV_PCM_INFO_BLOCK_TRANSFER |
SNDRV_PCM_INFO_MMAP |
@@ -63,15 +62,15 @@ struct s3c24xx_runtime_data {
dma_addr_t dma_start;
dma_addr_t dma_pos;
dma_addr_t dma_end;
- struct s3c24xx_pcm_dma_params *params;
+ struct s3c_dma_params *params;
};
-/* s3c24xx_pcm_enqueue
+/* s3c_dma_enqueue
*
* place a dma buffer onto the queue for the dma system
* to handle.
*/
-static void s3c24xx_pcm_enqueue(struct snd_pcm_substream *substream)
+static void s3c_dma_enqueue(struct snd_pcm_substream *substream)
{
struct s3c24xx_runtime_data *prtd = substream->runtime->private_data;
dma_addr_t pos = prtd->dma_pos;
@@ -80,12 +79,13 @@ static void s3c24xx_pcm_enqueue(struct snd_pcm_substream *substream)
pr_debug("Entered %s\n", __func__);
- if (s3c_dma_has_circular()) {
+ if (s3c_dma_has_circular())
limit = (prtd->dma_end - prtd->dma_start) / prtd->dma_period;
- } else
+ else
limit = prtd->dma_limit;
- pr_debug("%s: loaded %d, limit %d\n", __func__, prtd->dma_loaded, limit);
+ pr_debug("%s: loaded %d, limit %d\n",
+ __func__, prtd->dma_loaded, limit);
while (prtd->dma_loaded < limit) {
unsigned long len = prtd->dma_period;
@@ -133,19 +133,19 @@ static void s3c24xx_audio_buffdone(struct s3c2410_dma_chan *channel,
spin_lock(&prtd->lock);
if (prtd->state & ST_RUNNING && !s3c_dma_has_circular()) {
prtd->dma_loaded--;
- s3c24xx_pcm_enqueue(substream);
+ s3c_dma_enqueue(substream);
}
spin_unlock(&prtd->lock);
}
-static int s3c24xx_pcm_hw_params(struct snd_pcm_substream *substream,
+static int s3c_dma_hw_params(struct snd_pcm_substream *substream,
struct snd_pcm_hw_params *params)
{
struct snd_pcm_runtime *runtime = substream->runtime;
struct s3c24xx_runtime_data *prtd = runtime->private_data;
struct snd_soc_pcm_runtime *rtd = substream->private_data;
- struct s3c24xx_pcm_dma_params *dma = rtd->dai->cpu_dai->dma_data;
+ struct s3c_dma_params *dma = rtd->dai->cpu_dai->dma_data;
unsigned long totbytes = params_buffer_bytes(params);
int ret = 0;
@@ -198,7 +198,7 @@ static int s3c24xx_pcm_hw_params(struct snd_pcm_substream *substream,
return 0;
}
-static int s3c24xx_pcm_hw_free(struct snd_pcm_substream *substream)
+static int s3c_dma_hw_free(struct snd_pcm_substream *substream)
{
struct s3c24xx_runtime_data *prtd = substream->runtime->private_data;
@@ -215,7 +215,7 @@ static int s3c24xx_pcm_hw_free(struct snd_pcm_substream *substream)
return 0;
}
-static int s3c24xx_pcm_prepare(struct snd_pcm_substream *substream)
+static int s3c_dma_prepare(struct snd_pcm_substream *substream)
{
struct s3c24xx_runtime_data *prtd = substream->runtime->private_data;
int ret = 0;
@@ -248,12 +248,12 @@ static int s3c24xx_pcm_prepare(struct snd_pcm_substream *substream)
prtd->dma_pos = prtd->dma_start;
/* enqueue dma buffers */
- s3c24xx_pcm_enqueue(substream);
+ s3c_dma_enqueue(substream);
return ret;
}
-static int s3c24xx_pcm_trigger(struct snd_pcm_substream *substream, int cmd)
+static int s3c_dma_trigger(struct snd_pcm_substream *substream, int cmd)
{
struct s3c24xx_runtime_data *prtd = substream->runtime->private_data;
int ret = 0;
@@ -288,7 +288,7 @@ static int s3c24xx_pcm_trigger(struct snd_pcm_substream *substream, int cmd)
}
static snd_pcm_uframes_t
-s3c24xx_pcm_pointer(struct snd_pcm_substream *substream)
+s3c_dma_pointer(struct snd_pcm_substream *substream)
{
struct snd_pcm_runtime *runtime = substream->runtime;
struct s3c24xx_runtime_data *prtd = runtime->private_data;
@@ -323,7 +323,7 @@ s3c24xx_pcm_pointer(struct snd_pcm_substream *substream)
return bytes_to_frames(substream->runtime, res);
}
-static int s3c24xx_pcm_open(struct snd_pcm_substream *substream)
+static int s3c_dma_open(struct snd_pcm_substream *substream)
{
struct snd_pcm_runtime *runtime = substream->runtime;
struct s3c24xx_runtime_data *prtd;
@@ -331,7 +331,7 @@ static int s3c24xx_pcm_open(struct snd_pcm_substream *substream)
pr_debug("Entered %s\n", __func__);
snd_pcm_hw_constraint_integer(runtime, SNDRV_PCM_HW_PARAM_PERIODS);
- snd_soc_set_runtime_hwparams(substream, &s3c24xx_pcm_hardware);
+ snd_soc_set_runtime_hwparams(substream, &s3c_dma_hardware);
prtd = kzalloc(sizeof(struct s3c24xx_runtime_data), GFP_KERNEL);
if (prtd == NULL)
@@ -343,7 +343,7 @@ static int s3c24xx_pcm_open(struct snd_pcm_substream *substream)
return 0;
}
-static int s3c24xx_pcm_close(struct snd_pcm_substream *substream)
+static int s3c_dma_close(struct snd_pcm_substream *substream)
{
struct snd_pcm_runtime *runtime = substream->runtime;
struct s3c24xx_runtime_data *prtd = runtime->private_data;
@@ -351,14 +351,14 @@ static int s3c24xx_pcm_close(struct snd_pcm_substream *substream)
pr_debug("Entered %s\n", __func__);
if (!prtd)
- pr_debug("s3c24xx_pcm_close called with prtd == NULL\n");
+ pr_debug("s3c_dma_close called with prtd == NULL\n");
kfree(prtd);
return 0;
}
-static int s3c24xx_pcm_mmap(struct snd_pcm_substream *substream,
+static int s3c_dma_mmap(struct snd_pcm_substream *substream,
struct vm_area_struct *vma)
{
struct snd_pcm_runtime *runtime = substream->runtime;
@@ -371,23 +371,23 @@ static int s3c24xx_pcm_mmap(struct snd_pcm_substream *substream,
runtime->dma_bytes);
}
-static struct snd_pcm_ops s3c24xx_pcm_ops = {
- .open = s3c24xx_pcm_open,
- .close = s3c24xx_pcm_close,
+static struct snd_pcm_ops s3c_dma_ops = {
+ .open = s3c_dma_open,
+ .close = s3c_dma_close,
.ioctl = snd_pcm_lib_ioctl,
- .hw_params = s3c24xx_pcm_hw_params,
- .hw_free = s3c24xx_pcm_hw_free,
- .prepare = s3c24xx_pcm_prepare,
- .trigger = s3c24xx_pcm_trigger,
- .pointer = s3c24xx_pcm_pointer,
- .mmap = s3c24xx_pcm_mmap,
+ .hw_params = s3c_dma_hw_params,
+ .hw_free = s3c_dma_hw_free,
+ .prepare = s3c_dma_prepare,
+ .trigger = s3c_dma_trigger,
+ .pointer = s3c_dma_pointer,
+ .mmap = s3c_dma_mmap,
};
-static int s3c24xx_pcm_preallocate_dma_buffer(struct snd_pcm *pcm, int stream)
+static int s3c_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 = s3c24xx_pcm_hardware.buffer_bytes_max;
+ size_t size = s3c_dma_hardware.buffer_bytes_max;
pr_debug("Entered %s\n", __func__);
@@ -402,7 +402,7 @@ static int s3c24xx_pcm_preallocate_dma_buffer(struct snd_pcm *pcm, int stream)
return 0;
}
-static void s3c24xx_pcm_free_dma_buffers(struct snd_pcm *pcm)
+static void s3c_dma_free_dma_buffers(struct snd_pcm *pcm)
{
struct snd_pcm_substream *substream;
struct snd_dma_buffer *buf;
@@ -425,9 +425,9 @@ static void s3c24xx_pcm_free_dma_buffers(struct snd_pcm *pcm)
}
}
-static u64 s3c24xx_pcm_dmamask = DMA_BIT_MASK(32);
+static u64 s3c_dma_mask = DMA_BIT_MASK(32);
-static int s3c24xx_pcm_new(struct snd_card *card,
+static int s3c_dma_new(struct snd_card *card,
struct snd_soc_dai *dai, struct snd_pcm *pcm)
{
int ret = 0;
@@ -435,19 +435,19 @@ static int s3c24xx_pcm_new(struct snd_card *card,
pr_debug("Entered %s\n", __func__);
if (!card->dev->dma_mask)
- card->dev->dma_mask = &s3c24xx_pcm_dmamask;
+ card->dev->dma_mask = &s3c_dma_mask;
if (!card->dev->coherent_dma_mask)
card->dev->coherent_dma_mask = 0xffffffff;
if (dai->playback.channels_min) {
- ret = s3c24xx_pcm_preallocate_dma_buffer(pcm,
+ ret = s3c_preallocate_dma_buffer(pcm,
SNDRV_PCM_STREAM_PLAYBACK);
if (ret)
goto out;
}
if (dai->capture.channels_min) {
- ret = s3c24xx_pcm_preallocate_dma_buffer(pcm,
+ ret = s3c_preallocate_dma_buffer(pcm,
SNDRV_PCM_STREAM_CAPTURE);
if (ret)
goto out;
@@ -458,9 +458,9 @@ static int s3c24xx_pcm_new(struct snd_card *card,
struct snd_soc_platform s3c24xx_soc_platform = {
.name = "s3c24xx-audio",
- .pcm_ops = &s3c24xx_pcm_ops,
- .pcm_new = s3c24xx_pcm_new,
- .pcm_free = s3c24xx_pcm_free_dma_buffers,
+ .pcm_ops = &s3c_dma_ops,
+ .pcm_new = s3c_dma_new,
+ .pcm_free = s3c_dma_free_dma_buffers,
};
EXPORT_SYMBOL_GPL(s3c24xx_soc_platform);
@@ -477,5 +477,5 @@ static void __exit s3c24xx_soc_platform_exit(void)
module_exit(s3c24xx_soc_platform_exit);
MODULE_AUTHOR("Ben Dooks, <ben@simtec.co.uk>");
-MODULE_DESCRIPTION("Samsung S3C24XX PCM DMA module");
+MODULE_DESCRIPTION("Samsung S3C Audio DMA module");
MODULE_LICENSE("GPL");
diff --git a/sound/soc/s3c24xx/s3c24xx-pcm.h b/sound/soc/s3c24xx/s3c-dma.h
index 0088c79822ea..69bb6bf6fc1c 100644
--- a/sound/soc/s3c24xx/s3c24xx-pcm.h
+++ b/sound/soc/s3c24xx/s3c-dma.h
@@ -1,5 +1,5 @@
/*
- * s3c24xx-pcm.h --
+ * s3c-dma.h --
*
* 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
@@ -9,13 +9,13 @@
* ALSA PCM interface for the Samsung S3C24xx CPU
*/
-#ifndef _S3C24XX_PCM_H
-#define _S3C24XX_PCM_H
+#ifndef _S3C_AUDIO_H
+#define _S3C_AUDIO_H
#define ST_RUNNING (1<<0)
#define ST_OPENED (1<<1)
-struct s3c24xx_pcm_dma_params {
+struct s3c_dma_params {
struct s3c2410_dma_client *client; /* stream identifier */
int channel; /* Channel ID */
dma_addr_t dma_addr;
diff --git a/sound/soc/s3c24xx/s3c-i2s-v2.c b/sound/soc/s3c24xx/s3c-i2s-v2.c
index 9bc4aa35caab..e994d8374fe6 100644
--- a/sound/soc/s3c24xx/s3c-i2s-v2.c
+++ b/sound/soc/s3c24xx/s3c-i2s-v2.c
@@ -32,11 +32,10 @@
#include <plat/regs-s3c2412-iis.h>
-#include <plat/audio.h>
#include <mach/dma.h>
#include "s3c-i2s-v2.h"
-#include "s3c24xx-pcm.h"
+#include "s3c-dma.h"
#undef S3C_IIS_V2_SUPPORTED
@@ -312,12 +311,15 @@ static int s3c2412_i2s_set_fmt(struct snd_soc_dai *cpu_dai,
switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
case SND_SOC_DAIFMT_RIGHT_J:
+ iismod |= S3C2412_IISMOD_LR_RLOW;
iismod |= S3C2412_IISMOD_SDF_MSB;
break;
case SND_SOC_DAIFMT_LEFT_J:
+ iismod |= S3C2412_IISMOD_LR_RLOW;
iismod |= S3C2412_IISMOD_SDF_LSB;
break;
case SND_SOC_DAIFMT_I2S:
+ iismod &= ~S3C2412_IISMOD_LR_RLOW;
iismod |= S3C2412_IISMOD_SDF_IIS;
break;
default:
@@ -392,7 +394,7 @@ static int s3c2412_i2s_trigger(struct snd_pcm_substream *substream, int cmd,
int capture = (substream->stream == SNDRV_PCM_STREAM_CAPTURE);
unsigned long irqs;
int ret = 0;
- int channel = ((struct s3c24xx_pcm_dma_params *)
+ int channel = ((struct s3c_dma_params *)
rtd->dai->cpu_dai->dma_data)->channel;
pr_debug("Entered %s\n", __func__);
@@ -467,6 +469,31 @@ static int s3c2412_i2s_set_clkdiv(struct snd_soc_dai *cpu_dai,
switch (div_id) {
case S3C_I2SV2_DIV_BCLK:
+ if (div > 3) {
+ /* convert value to bit field */
+
+ switch (div) {
+ case 16:
+ div = S3C2412_IISMOD_BCLK_16FS;
+ break;
+
+ case 32:
+ div = S3C2412_IISMOD_BCLK_32FS;
+ break;
+
+ case 24:
+ div = S3C2412_IISMOD_BCLK_24FS;
+ break;
+
+ case 48:
+ div = S3C2412_IISMOD_BCLK_48FS;
+ break;
+
+ default:
+ return -EINVAL;
+ }
+ }
+
reg = readl(i2s->regs + S3C2412_IISMOD);
reg &= ~S3C2412_IISMOD_BCLK_MASK;
writel(reg | div, i2s->regs + S3C2412_IISMOD);
@@ -626,7 +653,7 @@ int s3c_i2sv2_probe(struct platform_device *pdev,
}
i2s->iis_pclk = clk_get(dev, "iis");
- if (i2s->iis_pclk == NULL) {
+ if (IS_ERR(i2s->iis_pclk)) {
dev_err(dev, "failed to get iis_clock\n");
iounmap(i2s->regs);
return -ENOENT;
diff --git a/sound/soc/s3c24xx/s3c-i2s-v2.h b/sound/soc/s3c24xx/s3c-i2s-v2.h
index f66854a77fb2..ecf8eaaed1db 100644
--- a/sound/soc/s3c24xx/s3c-i2s-v2.h
+++ b/sound/soc/s3c24xx/s3c-i2s-v2.h
@@ -49,8 +49,8 @@ struct s3c_i2sv2_info {
unsigned char master;
- struct s3c24xx_pcm_dma_params *dma_playback;
- struct s3c24xx_pcm_dma_params *dma_capture;
+ struct s3c_dma_params *dma_playback;
+ struct s3c_dma_params *dma_capture;
u32 suspend_iismod;
u32 suspend_iiscon;
diff --git a/sound/soc/s3c24xx/s3c-pcm.c b/sound/soc/s3c24xx/s3c-pcm.c
new file mode 100644
index 000000000000..9e61a7c2d9ac
--- /dev/null
+++ b/sound/soc/s3c24xx/s3c-pcm.c
@@ -0,0 +1,552 @@
+/* sound/soc/s3c24xx/s3c-pcm.c
+ *
+ * ALSA SoC Audio Layer - S3C PCM-Controller driver
+ *
+ * Copyright (c) 2009 Samsung Electronics Co. Ltd
+ * Author: Jaswinder Singh <jassi.brar@samsung.com>
+ * based upon I2S drivers by Ben Dooks.
+ *
+ * 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/device.h>
+#include <linux/delay.h>
+#include <linux/clk.h>
+#include <linux/kernel.h>
+#include <linux/gpio.h>
+#include <linux/io.h>
+
+#include <sound/core.h>
+#include <sound/pcm.h>
+#include <sound/pcm_params.h>
+#include <sound/initval.h>
+#include <sound/soc.h>
+
+#include <plat/audio.h>
+#include <plat/dma.h>
+
+#include "s3c-dma.h"
+#include "s3c-pcm.h"
+
+static struct s3c2410_dma_client s3c_pcm_dma_client_out = {
+ .name = "PCM Stereo out"
+};
+
+static struct s3c2410_dma_client s3c_pcm_dma_client_in = {
+ .name = "PCM Stereo in"
+};
+
+static struct s3c_dma_params s3c_pcm_stereo_out[] = {
+ [0] = {
+ .client = &s3c_pcm_dma_client_out,
+ .dma_size = 4,
+ },
+ [1] = {
+ .client = &s3c_pcm_dma_client_out,
+ .dma_size = 4,
+ },
+};
+
+static struct s3c_dma_params s3c_pcm_stereo_in[] = {
+ [0] = {
+ .client = &s3c_pcm_dma_client_in,
+ .dma_size = 4,
+ },
+ [1] = {
+ .client = &s3c_pcm_dma_client_in,
+ .dma_size = 4,
+ },
+};
+
+static struct s3c_pcm_info s3c_pcm[2];
+
+static inline struct s3c_pcm_info *to_info(struct snd_soc_dai *cpu_dai)
+{
+ return cpu_dai->private_data;
+}
+
+static void s3c_pcm_snd_txctrl(struct s3c_pcm_info *pcm, int on)
+{
+ void __iomem *regs = pcm->regs;
+ u32 ctl, clkctl;
+
+ clkctl = readl(regs + S3C_PCM_CLKCTL);
+ ctl = readl(regs + S3C_PCM_CTL);
+ ctl &= ~(S3C_PCM_CTL_TXDIPSTICK_MASK
+ << S3C_PCM_CTL_TXDIPSTICK_SHIFT);
+
+ if (on) {
+ ctl |= S3C_PCM_CTL_TXDMA_EN;
+ ctl |= S3C_PCM_CTL_TXFIFO_EN;
+ ctl |= S3C_PCM_CTL_ENABLE;
+ ctl |= (0x20<<S3C_PCM_CTL_TXDIPSTICK_SHIFT);
+ clkctl |= S3C_PCM_CLKCTL_SERCLK_EN;
+ } else {
+ ctl &= ~S3C_PCM_CTL_TXDMA_EN;
+ ctl &= ~S3C_PCM_CTL_TXFIFO_EN;
+
+ if (!(ctl & S3C_PCM_CTL_RXFIFO_EN)) {
+ ctl &= ~S3C_PCM_CTL_ENABLE;
+ if (!pcm->idleclk)
+ clkctl |= S3C_PCM_CLKCTL_SERCLK_EN;
+ }
+ }
+
+ writel(clkctl, regs + S3C_PCM_CLKCTL);
+ writel(ctl, regs + S3C_PCM_CTL);
+}
+
+static void s3c_pcm_snd_rxctrl(struct s3c_pcm_info *pcm, int on)
+{
+ void __iomem *regs = pcm->regs;
+ u32 ctl, clkctl;
+
+ ctl = readl(regs + S3C_PCM_CTL);
+ clkctl = readl(regs + S3C_PCM_CLKCTL);
+
+ if (on) {
+ ctl |= S3C_PCM_CTL_RXDMA_EN;
+ ctl |= S3C_PCM_CTL_RXFIFO_EN;
+ ctl |= S3C_PCM_CTL_ENABLE;
+ clkctl |= S3C_PCM_CLKCTL_SERCLK_EN;
+ } else {
+ ctl &= ~S3C_PCM_CTL_RXDMA_EN;
+ ctl &= ~S3C_PCM_CTL_RXFIFO_EN;
+
+ if (!(ctl & S3C_PCM_CTL_TXFIFO_EN)) {
+ ctl &= ~S3C_PCM_CTL_ENABLE;
+ if (!pcm->idleclk)
+ clkctl |= S3C_PCM_CLKCTL_SERCLK_EN;
+ }
+ }
+
+ writel(clkctl, regs + S3C_PCM_CLKCTL);
+ writel(ctl, regs + S3C_PCM_CTL);
+}
+
+static int s3c_pcm_trigger(struct snd_pcm_substream *substream, int cmd,
+ struct snd_soc_dai *dai)
+{
+ struct snd_soc_pcm_runtime *rtd = substream->private_data;
+ struct s3c_pcm_info *pcm = to_info(rtd->dai->cpu_dai);
+ unsigned long flags;
+
+ dev_dbg(pcm->dev, "Entered %s\n", __func__);
+
+ switch (cmd) {
+ case SNDRV_PCM_TRIGGER_START:
+ case SNDRV_PCM_TRIGGER_RESUME:
+ case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
+ spin_lock_irqsave(&pcm->lock, flags);
+
+ if (substream->stream == SNDRV_PCM_STREAM_CAPTURE)
+ s3c_pcm_snd_rxctrl(pcm, 1);
+ else
+ s3c_pcm_snd_txctrl(pcm, 1);
+
+ spin_unlock_irqrestore(&pcm->lock, flags);
+ break;
+
+ case SNDRV_PCM_TRIGGER_STOP:
+ case SNDRV_PCM_TRIGGER_SUSPEND:
+ case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
+ spin_lock_irqsave(&pcm->lock, flags);
+
+ if (substream->stream == SNDRV_PCM_STREAM_CAPTURE)
+ s3c_pcm_snd_rxctrl(pcm, 0);
+ else
+ s3c_pcm_snd_txctrl(pcm, 0);
+
+ spin_unlock_irqrestore(&pcm->lock, flags);
+ break;
+
+ default:
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static int s3c_pcm_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params,
+ struct snd_soc_dai *socdai)
+{
+ struct snd_soc_pcm_runtime *rtd = substream->private_data;
+ struct snd_soc_dai_link *dai = rtd->dai;
+ struct s3c_pcm_info *pcm = to_info(dai->cpu_dai);
+ void __iomem *regs = pcm->regs;
+ struct clk *clk;
+ int sclk_div, sync_div;
+ unsigned long flags;
+ u32 clkctl;
+
+ dev_dbg(pcm->dev, "Entered %s\n", __func__);
+
+ if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
+ dai->cpu_dai->dma_data = pcm->dma_playback;
+ else
+ dai->cpu_dai->dma_data = pcm->dma_capture;
+
+ /* Strictly check for sample size */
+ switch (params_format(params)) {
+ case SNDRV_PCM_FORMAT_S16_LE:
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ spin_lock_irqsave(&pcm->lock, flags);
+
+ /* Get hold of the PCMSOURCE_CLK */
+ clkctl = readl(regs + S3C_PCM_CLKCTL);
+ if (clkctl & S3C_PCM_CLKCTL_SERCLKSEL_PCLK)
+ clk = pcm->pclk;
+ else
+ clk = pcm->cclk;
+
+ /* Set the SCLK divider */
+ sclk_div = clk_get_rate(clk) / pcm->sclk_per_fs /
+ params_rate(params) / 2 - 1;
+
+ clkctl &= ~(S3C_PCM_CLKCTL_SCLKDIV_MASK
+ << S3C_PCM_CLKCTL_SCLKDIV_SHIFT);
+ clkctl |= ((sclk_div & S3C_PCM_CLKCTL_SCLKDIV_MASK)
+ << S3C_PCM_CLKCTL_SCLKDIV_SHIFT);
+
+ /* Set the SYNC divider */
+ sync_div = pcm->sclk_per_fs - 1;
+
+ clkctl &= ~(S3C_PCM_CLKCTL_SYNCDIV_MASK
+ << S3C_PCM_CLKCTL_SYNCDIV_SHIFT);
+ clkctl |= ((sync_div & S3C_PCM_CLKCTL_SYNCDIV_MASK)
+ << S3C_PCM_CLKCTL_SYNCDIV_SHIFT);
+
+ writel(clkctl, regs + S3C_PCM_CLKCTL);
+
+ spin_unlock_irqrestore(&pcm->lock, flags);
+
+ dev_dbg(pcm->dev, "PCMSOURCE_CLK-%lu SCLK=%ufs \
+ SCLK_DIV=%d SYNC_DIV=%d\n",
+ clk_get_rate(clk), pcm->sclk_per_fs,
+ sclk_div, sync_div);
+
+ return 0;
+}
+
+static int s3c_pcm_set_fmt(struct snd_soc_dai *cpu_dai,
+ unsigned int fmt)
+{
+ struct s3c_pcm_info *pcm = to_info(cpu_dai);
+ void __iomem *regs = pcm->regs;
+ unsigned long flags;
+ int ret = 0;
+ u32 ctl;
+
+ dev_dbg(pcm->dev, "Entered %s\n", __func__);
+
+ spin_lock_irqsave(&pcm->lock, flags);
+
+ ctl = readl(regs + S3C_PCM_CTL);
+
+ switch (fmt & SND_SOC_DAIFMT_INV_MASK) {
+ case SND_SOC_DAIFMT_NB_NF:
+ /* Nothing to do, NB_NF by default */
+ break;
+ default:
+ dev_err(pcm->dev, "Unsupported clock inversion!\n");
+ ret = -EINVAL;
+ goto exit;
+ }
+
+ switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) {
+ case SND_SOC_DAIFMT_CBS_CFS:
+ /* Nothing to do, Master by default */
+ break;
+ default:
+ dev_err(pcm->dev, "Unsupported master/slave format!\n");
+ ret = -EINVAL;
+ goto exit;
+ }
+
+ switch (fmt & SND_SOC_DAIFMT_CLOCK_MASK) {
+ case SND_SOC_DAIFMT_CONT:
+ pcm->idleclk = 1;
+ break;
+ case SND_SOC_DAIFMT_GATED:
+ pcm->idleclk = 0;
+ break;
+ default:
+ dev_err(pcm->dev, "Invalid Clock gating request!\n");
+ ret = -EINVAL;
+ goto exit;
+ }
+
+ switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
+ case SND_SOC_DAIFMT_DSP_A:
+ ctl |= S3C_PCM_CTL_TXMSB_AFTER_FSYNC;
+ ctl |= S3C_PCM_CTL_RXMSB_AFTER_FSYNC;
+ break;
+ case SND_SOC_DAIFMT_DSP_B:
+ ctl &= ~S3C_PCM_CTL_TXMSB_AFTER_FSYNC;
+ ctl &= ~S3C_PCM_CTL_RXMSB_AFTER_FSYNC;
+ break;
+ default:
+ dev_err(pcm->dev, "Unsupported data format!\n");
+ ret = -EINVAL;
+ goto exit;
+ }
+
+ writel(ctl, regs + S3C_PCM_CTL);
+
+exit:
+ spin_unlock_irqrestore(&pcm->lock, flags);
+
+ return ret;
+}
+
+static int s3c_pcm_set_clkdiv(struct snd_soc_dai *cpu_dai,
+ int div_id, int div)
+{
+ struct s3c_pcm_info *pcm = to_info(cpu_dai);
+
+ switch (div_id) {
+ case S3C_PCM_SCLK_PER_FS:
+ pcm->sclk_per_fs = div;
+ break;
+
+ default:
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static int s3c_pcm_set_sysclk(struct snd_soc_dai *cpu_dai,
+ int clk_id, unsigned int freq, int dir)
+{
+ struct s3c_pcm_info *pcm = to_info(cpu_dai);
+ void __iomem *regs = pcm->regs;
+ u32 clkctl = readl(regs + S3C_PCM_CLKCTL);
+
+ switch (clk_id) {
+ case S3C_PCM_CLKSRC_PCLK:
+ clkctl |= S3C_PCM_CLKCTL_SERCLKSEL_PCLK;
+ break;
+
+ case S3C_PCM_CLKSRC_MUX:
+ clkctl &= ~S3C_PCM_CLKCTL_SERCLKSEL_PCLK;
+
+ if (clk_get_rate(pcm->cclk) != freq)
+ clk_set_rate(pcm->cclk, freq);
+
+ break;
+
+ default:
+ return -EINVAL;
+ }
+
+ writel(clkctl, regs + S3C_PCM_CLKCTL);
+
+ return 0;
+}
+
+static struct snd_soc_dai_ops s3c_pcm_dai_ops = {
+ .set_sysclk = s3c_pcm_set_sysclk,
+ .set_clkdiv = s3c_pcm_set_clkdiv,
+ .trigger = s3c_pcm_trigger,
+ .hw_params = s3c_pcm_hw_params,
+ .set_fmt = s3c_pcm_set_fmt,
+};
+
+#define S3C_PCM_RATES SNDRV_PCM_RATE_8000_96000
+
+#define S3C_PCM_DECLARE(n) \
+{ \
+ .name = "samsung-pcm", \
+ .id = (n), \
+ .symmetric_rates = 1, \
+ .ops = &s3c_pcm_dai_ops, \
+ .playback = { \
+ .channels_min = 2, \
+ .channels_max = 2, \
+ .rates = S3C_PCM_RATES, \
+ .formats = SNDRV_PCM_FMTBIT_S16_LE, \
+ }, \
+ .capture = { \
+ .channels_min = 2, \
+ .channels_max = 2, \
+ .rates = S3C_PCM_RATES, \
+ .formats = SNDRV_PCM_FMTBIT_S16_LE, \
+ }, \
+}
+
+struct snd_soc_dai s3c_pcm_dai[] = {
+ S3C_PCM_DECLARE(0),
+ S3C_PCM_DECLARE(1),
+};
+EXPORT_SYMBOL_GPL(s3c_pcm_dai);
+
+static __devinit int s3c_pcm_dev_probe(struct platform_device *pdev)
+{
+ struct s3c_pcm_info *pcm;
+ struct snd_soc_dai *dai;
+ struct resource *mem_res, *dmatx_res, *dmarx_res;
+ struct s3c_audio_pdata *pcm_pdata;
+ int ret;
+
+ /* Check for valid device index */
+ if ((pdev->id < 0) || pdev->id >= ARRAY_SIZE(s3c_pcm)) {
+ dev_err(&pdev->dev, "id %d out of range\n", pdev->id);
+ return -EINVAL;
+ }
+
+ pcm_pdata = pdev->dev.platform_data;
+
+ /* Check for availability of necessary resource */
+ dmatx_res = platform_get_resource(pdev, IORESOURCE_DMA, 0);
+ if (!dmatx_res) {
+ dev_err(&pdev->dev, "Unable to get PCM-TX dma resource\n");
+ return -ENXIO;
+ }
+
+ dmarx_res = platform_get_resource(pdev, IORESOURCE_DMA, 1);
+ if (!dmarx_res) {
+ dev_err(&pdev->dev, "Unable to get PCM-RX dma resource\n");
+ return -ENXIO;
+ }
+
+ mem_res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ if (!mem_res) {
+ dev_err(&pdev->dev, "Unable to get register resource\n");
+ return -ENXIO;
+ }
+
+ if (pcm_pdata && pcm_pdata->cfg_gpio && pcm_pdata->cfg_gpio(pdev)) {
+ dev_err(&pdev->dev, "Unable to configure gpio\n");
+ return -EINVAL;
+ }
+
+ pcm = &s3c_pcm[pdev->id];
+ pcm->dev = &pdev->dev;
+
+ spin_lock_init(&pcm->lock);
+
+ dai = &s3c_pcm_dai[pdev->id];
+ dai->dev = &pdev->dev;
+
+ /* Default is 128fs */
+ pcm->sclk_per_fs = 128;
+
+ pcm->cclk = clk_get(&pdev->dev, "audio-bus");
+ if (IS_ERR(pcm->cclk)) {
+ dev_err(&pdev->dev, "failed to get audio-bus\n");
+ ret = PTR_ERR(pcm->cclk);
+ goto err1;
+ }
+ clk_enable(pcm->cclk);
+
+ /* record our pcm structure for later use in the callbacks */
+ dai->private_data = pcm;
+
+ if (!request_mem_region(mem_res->start,
+ resource_size(mem_res), "samsung-pcm")) {
+ dev_err(&pdev->dev, "Unable to request register region\n");
+ ret = -EBUSY;
+ goto err2;
+ }
+
+ pcm->regs = ioremap(mem_res->start, 0x100);
+ if (pcm->regs == NULL) {
+ dev_err(&pdev->dev, "cannot ioremap registers\n");
+ ret = -ENXIO;
+ goto err3;
+ }
+
+ pcm->pclk = clk_get(&pdev->dev, "pcm");
+ if (IS_ERR(pcm->pclk)) {
+ dev_err(&pdev->dev, "failed to get pcm_clock\n");
+ ret = -ENOENT;
+ goto err4;
+ }
+ clk_enable(pcm->pclk);
+
+ ret = snd_soc_register_dai(dai);
+ if (ret != 0) {
+ dev_err(&pdev->dev, "failed to get pcm_clock\n");
+ goto err5;
+ }
+
+ s3c_pcm_stereo_in[pdev->id].dma_addr = mem_res->start
+ + S3C_PCM_RXFIFO;
+ s3c_pcm_stereo_out[pdev->id].dma_addr = mem_res->start
+ + S3C_PCM_TXFIFO;
+
+ s3c_pcm_stereo_in[pdev->id].channel = dmarx_res->start;
+ s3c_pcm_stereo_out[pdev->id].channel = dmatx_res->start;
+
+ pcm->dma_capture = &s3c_pcm_stereo_in[pdev->id];
+ pcm->dma_playback = &s3c_pcm_stereo_out[pdev->id];
+
+ return 0;
+
+err5:
+ clk_disable(pcm->pclk);
+ clk_put(pcm->pclk);
+err4:
+ iounmap(pcm->regs);
+err3:
+ release_mem_region(mem_res->start, resource_size(mem_res));
+err2:
+ clk_disable(pcm->cclk);
+ clk_put(pcm->cclk);
+err1:
+ return ret;
+}
+
+static __devexit int s3c_pcm_dev_remove(struct platform_device *pdev)
+{
+ struct s3c_pcm_info *pcm = &s3c_pcm[pdev->id];
+ struct resource *mem_res;
+
+ iounmap(pcm->regs);
+
+ mem_res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ release_mem_region(mem_res->start, resource_size(mem_res));
+
+ clk_disable(pcm->cclk);
+ clk_disable(pcm->pclk);
+ clk_put(pcm->pclk);
+ clk_put(pcm->cclk);
+
+ return 0;
+}
+
+static struct platform_driver s3c_pcm_driver = {
+ .probe = s3c_pcm_dev_probe,
+ .remove = s3c_pcm_dev_remove,
+ .driver = {
+ .name = "samsung-pcm",
+ .owner = THIS_MODULE,
+ },
+};
+
+static int __init s3c_pcm_init(void)
+{
+ return platform_driver_register(&s3c_pcm_driver);
+}
+module_init(s3c_pcm_init);
+
+static void __exit s3c_pcm_exit(void)
+{
+ platform_driver_unregister(&s3c_pcm_driver);
+}
+module_exit(s3c_pcm_exit);
+
+/* Module information */
+MODULE_AUTHOR("Jaswinder Singh, <jassi.brar@samsung.com>");
+MODULE_DESCRIPTION("S3C PCM Controller Driver");
+MODULE_LICENSE("GPL");
diff --git a/sound/soc/s3c24xx/s3c-pcm.h b/sound/soc/s3c24xx/s3c-pcm.h
new file mode 100644
index 000000000000..69ff9971692f
--- /dev/null
+++ b/sound/soc/s3c24xx/s3c-pcm.h
@@ -0,0 +1,123 @@
+/* sound/soc/s3c24xx/s3c-pcm.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 __S3C_PCM_H
+#define __S3C_PCM_H __FILE__
+
+/*Register Offsets */
+#define S3C_PCM_CTL (0x00)
+#define S3C_PCM_CLKCTL (0x04)
+#define S3C_PCM_TXFIFO (0x08)
+#define S3C_PCM_RXFIFO (0x0C)
+#define S3C_PCM_IRQCTL (0x10)
+#define S3C_PCM_IRQSTAT (0x14)
+#define S3C_PCM_FIFOSTAT (0x18)
+#define S3C_PCM_CLRINT (0x20)
+
+/* PCM_CTL Bit-Fields */
+#define S3C_PCM_CTL_TXDIPSTICK_MASK (0x3f)
+#define S3C_PCM_CTL_TXDIPSTICK_SHIFT (13)
+#define S3C_PCM_CTL_RXDIPSTICK_MSK (0x3f<<7)
+#define S3C_PCM_CTL_TXDMA_EN (0x1<<6)
+#define S3C_PCM_CTL_RXDMA_EN (0x1<<5)
+#define S3C_PCM_CTL_TXMSB_AFTER_FSYNC (0x1<<4)
+#define S3C_PCM_CTL_RXMSB_AFTER_FSYNC (0x1<<3)
+#define S3C_PCM_CTL_TXFIFO_EN (0x1<<2)
+#define S3C_PCM_CTL_RXFIFO_EN (0x1<<1)
+#define S3C_PCM_CTL_ENABLE (0x1<<0)
+
+/* PCM_CLKCTL Bit-Fields */
+#define S3C_PCM_CLKCTL_SERCLK_EN (0x1<<19)
+#define S3C_PCM_CLKCTL_SERCLKSEL_PCLK (0x1<<18)
+#define S3C_PCM_CLKCTL_SCLKDIV_MASK (0x1ff)
+#define S3C_PCM_CLKCTL_SYNCDIV_MASK (0x1ff)
+#define S3C_PCM_CLKCTL_SCLKDIV_SHIFT (9)
+#define S3C_PCM_CLKCTL_SYNCDIV_SHIFT (0)
+
+/* PCM_TXFIFO Bit-Fields */
+#define S3C_PCM_TXFIFO_DVALID (0x1<<16)
+#define S3C_PCM_TXFIFO_DATA_MSK (0xffff<<0)
+
+/* PCM_RXFIFO Bit-Fields */
+#define S3C_PCM_RXFIFO_DVALID (0x1<<16)
+#define S3C_PCM_RXFIFO_DATA_MSK (0xffff<<0)
+
+/* PCM_IRQCTL Bit-Fields */
+#define S3C_PCM_IRQCTL_IRQEN (0x1<<14)
+#define S3C_PCM_IRQCTL_WRDEN (0x1<<12)
+#define S3C_PCM_IRQCTL_TXEMPTYEN (0x1<<11)
+#define S3C_PCM_IRQCTL_TXALMSTEMPTYEN (0x1<<10)
+#define S3C_PCM_IRQCTL_TXFULLEN (0x1<<9)
+#define S3C_PCM_IRQCTL_TXALMSTFULLEN (0x1<<8)
+#define S3C_PCM_IRQCTL_TXSTARVEN (0x1<<7)
+#define S3C_PCM_IRQCTL_TXERROVRFLEN (0x1<<6)
+#define S3C_PCM_IRQCTL_RXEMPTEN (0x1<<5)
+#define S3C_PCM_IRQCTL_RXALMSTEMPTEN (0x1<<4)
+#define S3C_PCM_IRQCTL_RXFULLEN (0x1<<3)
+#define S3C_PCM_IRQCTL_RXALMSTFULLEN (0x1<<2)
+#define S3C_PCM_IRQCTL_RXSTARVEN (0x1<<1)
+#define S3C_PCM_IRQCTL_RXERROVRFLEN (0x1<<0)
+
+/* PCM_IRQSTAT Bit-Fields */
+#define S3C_PCM_IRQSTAT_IRQPND (0x1<<13)
+#define S3C_PCM_IRQSTAT_WRD_XFER (0x1<<12)
+#define S3C_PCM_IRQSTAT_TXEMPTY (0x1<<11)
+#define S3C_PCM_IRQSTAT_TXALMSTEMPTY (0x1<<10)
+#define S3C_PCM_IRQSTAT_TXFULL (0x1<<9)
+#define S3C_PCM_IRQSTAT_TXALMSTFULL (0x1<<8)
+#define S3C_PCM_IRQSTAT_TXSTARV (0x1<<7)
+#define S3C_PCM_IRQSTAT_TXERROVRFL (0x1<<6)
+#define S3C_PCM_IRQSTAT_RXEMPT (0x1<<5)
+#define S3C_PCM_IRQSTAT_RXALMSTEMPT (0x1<<4)
+#define S3C_PCM_IRQSTAT_RXFULL (0x1<<3)
+#define S3C_PCM_IRQSTAT_RXALMSTFULL (0x1<<2)
+#define S3C_PCM_IRQSTAT_RXSTARV (0x1<<1)
+#define S3C_PCM_IRQSTAT_RXERROVRFL (0x1<<0)
+
+/* PCM_FIFOSTAT Bit-Fields */
+#define S3C_PCM_FIFOSTAT_TXCNT_MSK (0x3f<<14)
+#define S3C_PCM_FIFOSTAT_TXFIFOEMPTY (0x1<<13)
+#define S3C_PCM_FIFOSTAT_TXFIFOALMSTEMPTY (0x1<<12)
+#define S3C_PCM_FIFOSTAT_TXFIFOFULL (0x1<<11)
+#define S3C_PCM_FIFOSTAT_TXFIFOALMSTFULL (0x1<<10)
+#define S3C_PCM_FIFOSTAT_RXCNT_MSK (0x3f<<4)
+#define S3C_PCM_FIFOSTAT_RXFIFOEMPTY (0x1<<3)
+#define S3C_PCM_FIFOSTAT_RXFIFOALMSTEMPTY (0x1<<2)
+#define S3C_PCM_FIFOSTAT_RXFIFOFULL (0x1<<1)
+#define S3C_PCM_FIFOSTAT_RXFIFOALMSTFULL (0x1<<0)
+
+#define S3C_PCM_CLKSRC_PCLK 0
+#define S3C_PCM_CLKSRC_MUX 1
+
+#define S3C_PCM_SCLK_PER_FS 0
+
+/**
+ * struct s3c_pcm_info - S3C PCM Controller information
+ * @dev: The parent device passed to use from the probe.
+ * @regs: The pointer to the device register block.
+ * @dma_playback: DMA information for playback channel.
+ * @dma_capture: DMA information for capture channel.
+ */
+struct s3c_pcm_info {
+ spinlock_t lock;
+ struct device *dev;
+ void __iomem *regs;
+
+ unsigned int sclk_per_fs;
+
+ /* Whether to keep PCMSCLK enabled even when idle(no active xfer) */
+ unsigned int idleclk;
+
+ struct clk *pclk;
+ struct clk *cclk;
+
+ struct s3c_dma_params *dma_playback;
+ struct s3c_dma_params *dma_capture;
+};
+
+#endif /* __S3C_PCM_H */
diff --git a/sound/soc/s3c24xx/s3c2412-i2s.c b/sound/soc/s3c24xx/s3c2412-i2s.c
index a587ec40b449..359e59346ba2 100644
--- a/sound/soc/s3c24xx/s3c2412-i2s.c
+++ b/sound/soc/s3c24xx/s3c2412-i2s.c
@@ -34,11 +34,10 @@
#include <plat/regs-s3c2412-iis.h>
-#include <plat/audio.h>
#include <mach/regs-gpio.h>
#include <mach/dma.h>
-#include "s3c24xx-pcm.h"
+#include "s3c-dma.h"
#include "s3c2412-i2s.h"
#define S3C2412_I2S_DEBUG 0
@@ -51,14 +50,14 @@ static struct s3c2410_dma_client s3c2412_dma_client_in = {
.name = "I2S PCM Stereo in"
};
-static struct s3c24xx_pcm_dma_params s3c2412_i2s_pcm_stereo_out = {
+static struct s3c_dma_params s3c2412_i2s_pcm_stereo_out = {
.client = &s3c2412_dma_client_out,
.channel = DMACH_I2S_OUT,
.dma_addr = S3C2410_PA_IIS + S3C2412_IISTXD,
.dma_size = 4,
};
-static struct s3c24xx_pcm_dma_params s3c2412_i2s_pcm_stereo_in = {
+static struct s3c_dma_params s3c2412_i2s_pcm_stereo_in = {
.client = &s3c2412_dma_client_in,
.channel = DMACH_I2S_IN,
.dma_addr = S3C2410_PA_IIS + S3C2412_IISRXD,
diff --git a/sound/soc/s3c24xx/s3c2443-ac97.c b/sound/soc/s3c24xx/s3c2443-ac97.c
index fc1beb0930b9..0191e3acb0b4 100644
--- a/sound/soc/s3c24xx/s3c2443-ac97.c
+++ b/sound/soc/s3c24xx/s3c2443-ac97.c
@@ -32,11 +32,10 @@
#include <plat/regs-ac97.h>
#include <mach/regs-gpio.h>
#include <mach/regs-clock.h>
-#include <plat/audio.h>
#include <asm/dma.h>
#include <mach/dma.h>
-#include "s3c24xx-pcm.h"
+#include "s3c-dma.h"
#include "s3c24xx-ac97.h"
struct s3c24xx_ac97_info {
@@ -189,21 +188,21 @@ static struct s3c2410_dma_client s3c2443_dma_client_micin = {
.name = "AC97 Mic Mono in"
};
-static struct s3c24xx_pcm_dma_params s3c2443_ac97_pcm_stereo_out = {
+static struct s3c_dma_params s3c2443_ac97_pcm_stereo_out = {
.client = &s3c2443_dma_client_out,
.channel = DMACH_PCM_OUT,
.dma_addr = S3C2440_PA_AC97 + S3C_AC97_PCM_DATA,
.dma_size = 4,
};
-static struct s3c24xx_pcm_dma_params s3c2443_ac97_pcm_stereo_in = {
+static struct s3c_dma_params s3c2443_ac97_pcm_stereo_in = {
.client = &s3c2443_dma_client_in,
.channel = DMACH_PCM_IN,
.dma_addr = S3C2440_PA_AC97 + S3C_AC97_PCM_DATA,
.dma_size = 4,
};
-static struct s3c24xx_pcm_dma_params s3c2443_ac97_mic_mono_in = {
+static struct s3c_dma_params s3c2443_ac97_mic_mono_in = {
.client = &s3c2443_dma_client_micin,
.channel = DMACH_MIC_IN,
.dma_addr = S3C2440_PA_AC97 + S3C_AC97_MIC_DATA,
@@ -291,7 +290,7 @@ static int s3c2443_ac97_trigger(struct snd_pcm_substream *substream, int cmd,
{
u32 ac_glbctrl;
struct snd_soc_pcm_runtime *rtd = substream->private_data;
- int channel = ((struct s3c24xx_pcm_dma_params *)
+ int channel = ((struct s3c_dma_params *)
rtd->dai->cpu_dai->dma_data)->channel;
ac_glbctrl = readl(s3c24xx_ac97.regs + S3C_AC97_GLBCTRL);
@@ -340,7 +339,7 @@ static int s3c2443_ac97_mic_trigger(struct snd_pcm_substream *substream,
{
u32 ac_glbctrl;
struct snd_soc_pcm_runtime *rtd = substream->private_data;
- int channel = ((struct s3c24xx_pcm_dma_params *)
+ int channel = ((struct s3c_dma_params *)
rtd->dai->cpu_dai->dma_data)->channel;
ac_glbctrl = readl(s3c24xx_ac97.regs + S3C_AC97_GLBCTRL);
diff --git a/sound/soc/s3c24xx/s3c24xx-i2s.c b/sound/soc/s3c24xx/s3c24xx-i2s.c
index 40e2c4790f0d..0bc5950b9f02 100644
--- a/sound/soc/s3c24xx/s3c24xx-i2s.c
+++ b/sound/soc/s3c24xx/s3c24xx-i2s.c
@@ -32,13 +32,13 @@
#include <mach/hardware.h>
#include <mach/regs-gpio.h>
#include <mach/regs-clock.h>
-#include <plat/audio.h>
+
#include <asm/dma.h>
#include <mach/dma.h>
#include <plat/regs-iis.h>
-#include "s3c24xx-pcm.h"
+#include "s3c-dma.h"
#include "s3c24xx-i2s.h"
static struct s3c2410_dma_client s3c24xx_dma_client_out = {
@@ -49,14 +49,14 @@ static struct s3c2410_dma_client s3c24xx_dma_client_in = {
.name = "I2S PCM Stereo in"
};
-static struct s3c24xx_pcm_dma_params s3c24xx_i2s_pcm_stereo_out = {
+static struct s3c_dma_params s3c24xx_i2s_pcm_stereo_out = {
.client = &s3c24xx_dma_client_out,
.channel = DMACH_I2S_OUT,
.dma_addr = S3C2410_PA_IIS + S3C2410_IISFIFO,
.dma_size = 2,
};
-static struct s3c24xx_pcm_dma_params s3c24xx_i2s_pcm_stereo_in = {
+static struct s3c_dma_params s3c24xx_i2s_pcm_stereo_in = {
.client = &s3c24xx_dma_client_in,
.channel = DMACH_I2S_IN,
.dma_addr = S3C2410_PA_IIS + S3C2410_IISFIFO,
@@ -258,12 +258,12 @@ static int s3c24xx_i2s_hw_params(struct snd_pcm_substream *substream,
switch (params_format(params)) {
case SNDRV_PCM_FORMAT_S8:
iismod &= ~S3C2410_IISMOD_16BIT;
- ((struct s3c24xx_pcm_dma_params *)
+ ((struct s3c_dma_params *)
rtd->dai->cpu_dai->dma_data)->dma_size = 1;
break;
case SNDRV_PCM_FORMAT_S16_LE:
iismod |= S3C2410_IISMOD_16BIT;
- ((struct s3c24xx_pcm_dma_params *)
+ ((struct s3c_dma_params *)
rtd->dai->cpu_dai->dma_data)->dma_size = 2;
break;
default:
@@ -280,7 +280,7 @@ static int s3c24xx_i2s_trigger(struct snd_pcm_substream *substream, int cmd,
{
int ret = 0;
struct snd_soc_pcm_runtime *rtd = substream->private_data;
- int channel = ((struct s3c24xx_pcm_dma_params *)
+ int channel = ((struct s3c_dma_params *)
rtd->dai->cpu_dai->dma_data)->channel;
pr_debug("Entered %s\n", __func__);
diff --git a/sound/soc/s3c24xx/s3c24xx_simtec.c b/sound/soc/s3c24xx/s3c24xx_simtec.c
index 1966e0d5652d..507b2ed5d58b 100644
--- a/sound/soc/s3c24xx/s3c24xx_simtec.c
+++ b/sound/soc/s3c24xx/s3c24xx_simtec.c
@@ -21,7 +21,7 @@
#include <plat/audio-simtec.h>
-#include "s3c24xx-pcm.h"
+#include "s3c-dma.h"
#include "s3c24xx-i2s.h"
#include "s3c24xx_simtec.h"
diff --git a/sound/soc/s3c24xx/s3c24xx_simtec_hermes.c b/sound/soc/s3c24xx/s3c24xx_simtec_hermes.c
index 8346bd96eaf5..bdf8951af8e3 100644
--- a/sound/soc/s3c24xx/s3c24xx_simtec_hermes.c
+++ b/sound/soc/s3c24xx/s3c24xx_simtec_hermes.c
@@ -18,7 +18,7 @@
#include <plat/audio-simtec.h>
-#include "s3c24xx-pcm.h"
+#include "s3c-dma.h"
#include "s3c24xx-i2s.h"
#include "s3c24xx_simtec.h"
diff --git a/sound/soc/s3c24xx/s3c24xx_simtec_tlv320aic23.c b/sound/soc/s3c24xx/s3c24xx_simtec_tlv320aic23.c
index 25797e096175..185c0acb5ce6 100644
--- a/sound/soc/s3c24xx/s3c24xx_simtec_tlv320aic23.c
+++ b/sound/soc/s3c24xx/s3c24xx_simtec_tlv320aic23.c
@@ -18,7 +18,7 @@
#include <plat/audio-simtec.h>
-#include "s3c24xx-pcm.h"
+#include "s3c-dma.h"
#include "s3c24xx-i2s.h"
#include "s3c24xx_simtec.h"
diff --git a/sound/soc/s3c24xx/s3c24xx_uda134x.c b/sound/soc/s3c24xx/s3c24xx_uda134x.c
index c215d32d6322..052d59659c29 100644
--- a/sound/soc/s3c24xx/s3c24xx_uda134x.c
+++ b/sound/soc/s3c24xx/s3c24xx_uda134x.c
@@ -24,7 +24,7 @@
#include <plat/regs-iis.h>
-#include "s3c24xx-pcm.h"
+#include "s3c-dma.h"
#include "s3c24xx-i2s.h"
#include "../codecs/uda134x.h"
diff --git a/sound/soc/s3c24xx/s3c64xx-i2s.c b/sound/soc/s3c24xx/s3c64xx-i2s.c
index 105a77eeded0..cc7edb5f792d 100644
--- a/sound/soc/s3c24xx/s3c64xx-i2s.c
+++ b/sound/soc/s3c24xx/s3c64xx-i2s.c
@@ -31,12 +31,11 @@
#include <plat/gpio-bank-d.h>
#include <plat/gpio-bank-e.h>
#include <plat/gpio-cfg.h>
-#include <plat/audio.h>
#include <mach/map.h>
#include <mach/dma.h>
-#include "s3c24xx-pcm.h"
+#include "s3c-dma.h"
#include "s3c64xx-i2s.h"
static struct s3c2410_dma_client s3c64xx_dma_client_out = {
@@ -47,7 +46,7 @@ static struct s3c2410_dma_client s3c64xx_dma_client_in = {
.name = "I2S PCM Stereo in"
};
-static struct s3c24xx_pcm_dma_params s3c64xx_i2s_pcm_stereo_out[2] = {
+static struct s3c_dma_params s3c64xx_i2s_pcm_stereo_out[2] = {
[0] = {
.channel = DMACH_I2S0_OUT,
.client = &s3c64xx_dma_client_out,
@@ -62,7 +61,7 @@ static struct s3c24xx_pcm_dma_params s3c64xx_i2s_pcm_stereo_out[2] = {
},
};
-static struct s3c24xx_pcm_dma_params s3c64xx_i2s_pcm_stereo_in[2] = {
+static struct s3c_dma_params s3c64xx_i2s_pcm_stereo_in[2] = {
[0] = {
.channel = DMACH_I2S0_IN,
.client = &s3c64xx_dma_client_in,
@@ -99,6 +98,19 @@ static int s3c64xx_i2s_set_sysclk(struct snd_soc_dai *cpu_dai,
iismod |= S3C64XX_IISMOD_IMS_SYSMUX;
break;
+ case S3C64XX_CLKSRC_CDCLK:
+ switch (dir) {
+ case SND_SOC_CLOCK_IN:
+ iismod |= S3C64XX_IISMOD_CDCLKCON;
+ break;
+ case SND_SOC_CLOCK_OUT:
+ iismod &= ~S3C64XX_IISMOD_CDCLKCON;
+ break;
+ default:
+ return -EINVAL;
+ }
+ break;
+
default:
return -EINVAL;
}
@@ -111,8 +123,12 @@ static int s3c64xx_i2s_set_sysclk(struct snd_soc_dai *cpu_dai,
struct clk *s3c64xx_i2s_get_clock(struct snd_soc_dai *dai)
{
struct s3c_i2sv2_info *i2s = to_info(dai);
+ u32 iismod = readl(i2s->regs + S3C2412_IISMOD);
- return i2s->iis_cclk;
+ if (iismod & S3C64XX_IISMOD_IMS_SYSMUX)
+ return i2s->iis_cclk;
+ else
+ return i2s->iis_pclk;
}
EXPORT_SYMBOL_GPL(s3c64xx_i2s_get_clock);
diff --git a/sound/soc/s3c24xx/s3c64xx-i2s.h b/sound/soc/s3c24xx/s3c64xx-i2s.h
index 02148cee2613..abe7253b55fc 100644
--- a/sound/soc/s3c24xx/s3c64xx-i2s.h
+++ b/sound/soc/s3c24xx/s3c64xx-i2s.h
@@ -25,6 +25,7 @@ struct clk;
#define S3C64XX_CLKSRC_PCLK (0)
#define S3C64XX_CLKSRC_MUX (1)
+#define S3C64XX_CLKSRC_CDCLK (2)
extern struct snd_soc_dai s3c64xx_i2s_dai[];
diff --git a/sound/soc/s3c24xx/smdk2443_wm9710.c b/sound/soc/s3c24xx/smdk2443_wm9710.c
index a2a4f5323c17..12b783b12fcb 100644
--- a/sound/soc/s3c24xx/smdk2443_wm9710.c
+++ b/sound/soc/s3c24xx/smdk2443_wm9710.c
@@ -20,7 +20,7 @@
#include <sound/soc-dapm.h>
#include "../codecs/ac97.h"
-#include "s3c24xx-pcm.h"
+#include "s3c-dma.h"
#include "s3c24xx-ac97.h"
static struct snd_soc_card smdk2443;
diff --git a/sound/soc/s3c24xx/smdk64xx_wm8580.c b/sound/soc/s3c24xx/smdk64xx_wm8580.c
new file mode 100644
index 000000000000..efe4901213a3
--- /dev/null
+++ b/sound/soc/s3c24xx/smdk64xx_wm8580.c
@@ -0,0 +1,268 @@
+/*
+ * smdk64xx_wm8580.c
+ *
+ * Copyright (c) 2009 Samsung Electronics Co. Ltd
+ * Author: Jaswinder Singh <jassi.brar@samsung.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/platform_device.h>
+#include <linux/clk.h>
+#include <sound/core.h>
+#include <sound/pcm.h>
+#include <sound/pcm_params.h>
+#include <sound/soc.h>
+#include <sound/soc-dapm.h>
+
+#include "../codecs/wm8580.h"
+#include "s3c-dma.h"
+#include "s3c64xx-i2s.h"
+
+#define S3C64XX_I2S_V4 2
+
+/* SMDK64XX has a 12MHZ crystal attached to WM8580 */
+#define SMDK64XX_WM8580_FREQ 12000000
+
+static int smdk64xx_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_dai *cpu_dai = rtd->dai->cpu_dai;
+ struct snd_soc_dai *codec_dai = rtd->dai->codec_dai;
+ unsigned int pll_out;
+ int bfs, rfs, ret;
+
+ switch (params_format(params)) {
+ case SNDRV_PCM_FORMAT_U8:
+ case SNDRV_PCM_FORMAT_S8:
+ bfs = 16;
+ break;
+ case SNDRV_PCM_FORMAT_U16_LE:
+ case SNDRV_PCM_FORMAT_S16_LE:
+ bfs = 32;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ /* The Fvco for WM8580 PLLs must fall within [90,100]MHz.
+ * This criterion can't be met if we request PLL output
+ * as {8000x256, 64000x256, 11025x256}Hz.
+ * As a wayout, we rather change rfs to a minimum value that
+ * results in (params_rate(params) * rfs), and itself, acceptable
+ * to both - the CODEC and the CPU.
+ */
+ switch (params_rate(params)) {
+ case 16000:
+ case 22050:
+ case 32000:
+ case 44100:
+ case 48000:
+ case 88200:
+ case 96000:
+ rfs = 256;
+ break;
+ case 64000:
+ rfs = 384;
+ break;
+ case 8000:
+ case 11025:
+ rfs = 512;
+ break;
+ default:
+ return -EINVAL;
+ }
+ pll_out = params_rate(params) * rfs;
+
+ /* Set the Codec DAI configuration */
+ ret = snd_soc_dai_set_fmt(codec_dai, SND_SOC_DAIFMT_I2S
+ | SND_SOC_DAIFMT_NB_NF
+ | SND_SOC_DAIFMT_CBM_CFM);
+ if (ret < 0)
+ return ret;
+
+ /* Set the AP DAI configuration */
+ ret = snd_soc_dai_set_fmt(cpu_dai, SND_SOC_DAIFMT_I2S
+ | SND_SOC_DAIFMT_NB_NF
+ | SND_SOC_DAIFMT_CBM_CFM);
+ if (ret < 0)
+ return ret;
+
+ ret = snd_soc_dai_set_sysclk(cpu_dai, S3C64XX_CLKSRC_CDCLK,
+ 0, SND_SOC_CLOCK_IN);
+ if (ret < 0)
+ return ret;
+
+ /* We use PCLK for basic ops in SoC-Slave mode */
+ ret = snd_soc_dai_set_sysclk(cpu_dai, S3C64XX_CLKSRC_PCLK,
+ 0, SND_SOC_CLOCK_IN);
+ if (ret < 0)
+ return ret;
+
+ /* Set WM8580 to drive MCLK from its PLLA */
+ ret = snd_soc_dai_set_clkdiv(codec_dai, WM8580_MCLK,
+ WM8580_CLKSRC_PLLA);
+ if (ret < 0)
+ return ret;
+
+ /* Explicitly set WM8580-DAC to source from MCLK */
+ ret = snd_soc_dai_set_clkdiv(codec_dai, WM8580_DAC_CLKSEL,
+ WM8580_CLKSRC_MCLK);
+ if (ret < 0)
+ return ret;
+
+ ret = snd_soc_dai_set_pll(codec_dai, WM8580_PLLA, 0,
+ SMDK64XX_WM8580_FREQ, pll_out);
+ if (ret < 0)
+ return ret;
+
+ ret = snd_soc_dai_set_clkdiv(cpu_dai, S3C_I2SV2_DIV_BCLK, bfs);
+ if (ret < 0)
+ return ret;
+
+ ret = snd_soc_dai_set_clkdiv(cpu_dai, S3C_I2SV2_DIV_RCLK, rfs);
+ if (ret < 0)
+ return ret;
+
+ return 0;
+}
+
+/*
+ * SMDK64XX WM8580 DAI operations.
+ */
+static struct snd_soc_ops smdk64xx_ops = {
+ .hw_params = smdk64xx_hw_params,
+};
+
+/* SMDK64xx Playback widgets */
+static const struct snd_soc_dapm_widget wm8580_dapm_widgets_pbk[] = {
+ SND_SOC_DAPM_HP("Front-L/R", NULL),
+ SND_SOC_DAPM_HP("Center/Sub", NULL),
+ SND_SOC_DAPM_HP("Rear-L/R", NULL),
+};
+
+/* SMDK64xx Capture widgets */
+static const struct snd_soc_dapm_widget wm8580_dapm_widgets_cpt[] = {
+ SND_SOC_DAPM_MIC("MicIn", NULL),
+ SND_SOC_DAPM_LINE("LineIn", NULL),
+};
+
+/* SMDK-PAIFTX connections */
+static const struct snd_soc_dapm_route audio_map_tx[] = {
+ /* MicIn feeds AINL */
+ {"AINL", NULL, "MicIn"},
+
+ /* LineIn feeds AINL/R */
+ {"AINL", NULL, "LineIn"},
+ {"AINR", NULL, "LineIn"},
+};
+
+/* SMDK-PAIFRX connections */
+static const struct snd_soc_dapm_route audio_map_rx[] = {
+ /* Front Left/Right are fed VOUT1L/R */
+ {"Front-L/R", NULL, "VOUT1L"},
+ {"Front-L/R", NULL, "VOUT1R"},
+
+ /* Center/Sub are fed VOUT2L/R */
+ {"Center/Sub", NULL, "VOUT2L"},
+ {"Center/Sub", NULL, "VOUT2R"},
+
+ /* Rear Left/Right are fed VOUT3L/R */
+ {"Rear-L/R", NULL, "VOUT3L"},
+ {"Rear-L/R", NULL, "VOUT3R"},
+};
+
+static int smdk64xx_wm8580_init_paiftx(struct snd_soc_codec *codec)
+{
+ /* Add smdk64xx specific Capture widgets */
+ snd_soc_dapm_new_controls(codec, wm8580_dapm_widgets_cpt,
+ ARRAY_SIZE(wm8580_dapm_widgets_cpt));
+
+ /* Set up PAIFTX audio path */
+ snd_soc_dapm_add_routes(codec, audio_map_tx, ARRAY_SIZE(audio_map_tx));
+
+ /* Enabling the microphone requires the fitting of a 0R
+ * resistor to connect the line from the microphone jack.
+ */
+ snd_soc_dapm_disable_pin(codec, "MicIn");
+
+ /* signal a DAPM event */
+ snd_soc_dapm_sync(codec);
+
+ return 0;
+}
+
+static int smdk64xx_wm8580_init_paifrx(struct snd_soc_codec *codec)
+{
+ /* Add smdk64xx specific Playback widgets */
+ snd_soc_dapm_new_controls(codec, wm8580_dapm_widgets_pbk,
+ ARRAY_SIZE(wm8580_dapm_widgets_pbk));
+
+ /* Set up PAIFRX audio path */
+ snd_soc_dapm_add_routes(codec, audio_map_rx, ARRAY_SIZE(audio_map_rx));
+
+ /* signal a DAPM event */
+ snd_soc_dapm_sync(codec);
+
+ return 0;
+}
+
+static struct snd_soc_dai_link smdk64xx_dai[] = {
+{ /* Primary Playback i/f */
+ .name = "WM8580 PAIF RX",
+ .stream_name = "Playback",
+ .cpu_dai = &s3c64xx_i2s_dai[S3C64XX_I2S_V4],
+ .codec_dai = &wm8580_dai[WM8580_DAI_PAIFRX],
+ .init = smdk64xx_wm8580_init_paifrx,
+ .ops = &smdk64xx_ops,
+},
+{ /* Primary Capture i/f */
+ .name = "WM8580 PAIF TX",
+ .stream_name = "Capture",
+ .cpu_dai = &s3c64xx_i2s_dai[S3C64XX_I2S_V4],
+ .codec_dai = &wm8580_dai[WM8580_DAI_PAIFTX],
+ .init = smdk64xx_wm8580_init_paiftx,
+ .ops = &smdk64xx_ops,
+},
+};
+
+static struct snd_soc_card smdk64xx = {
+ .name = "smdk64xx",
+ .platform = &s3c24xx_soc_platform,
+ .dai_link = smdk64xx_dai,
+ .num_links = ARRAY_SIZE(smdk64xx_dai),
+};
+
+static struct snd_soc_device smdk64xx_snd_devdata = {
+ .card = &smdk64xx,
+ .codec_dev = &soc_codec_dev_wm8580,
+};
+
+static struct platform_device *smdk64xx_snd_device;
+
+static int __init smdk64xx_audio_init(void)
+{
+ int ret;
+
+ smdk64xx_snd_device = platform_device_alloc("soc-audio", -1);
+ if (!smdk64xx_snd_device)
+ return -ENOMEM;
+
+ platform_set_drvdata(smdk64xx_snd_device, &smdk64xx_snd_devdata);
+ smdk64xx_snd_devdata.dev = &smdk64xx_snd_device->dev;
+ ret = platform_device_add(smdk64xx_snd_device);
+
+ if (ret)
+ platform_device_put(smdk64xx_snd_device);
+
+ return ret;
+}
+module_init(smdk64xx_audio_init);
+
+MODULE_AUTHOR("Jaswinder Singh, jassi.brar@samsung.com");
+MODULE_DESCRIPTION("ALSA SoC SMDK64XX WM8580");
+MODULE_LICENSE("GPL");
diff --git a/sound/soc/s6000/s6000-pcm.c b/sound/soc/s6000/s6000-pcm.c
index 83b8028e209d..0eb1722f6581 100644
--- a/sound/soc/s6000/s6000-pcm.c
+++ b/sound/soc/s6000/s6000-pcm.c
@@ -423,7 +423,7 @@ static void s6000_pcm_free(struct snd_pcm *pcm)
snd_pcm_lib_preallocate_free_for_all(pcm);
}
-static u64 s6000_pcm_dmamask = DMA_32BIT_MASK;
+static u64 s6000_pcm_dmamask = DMA_BIT_MASK(32);
static int s6000_pcm_new(struct snd_card *card,
struct snd_soc_dai *dai, struct snd_pcm *pcm)
@@ -435,7 +435,7 @@ static int s6000_pcm_new(struct snd_card *card,
if (!card->dev->dma_mask)
card->dev->dma_mask = &s6000_pcm_dmamask;
if (!card->dev->coherent_dma_mask)
- card->dev->coherent_dma_mask = DMA_32BIT_MASK;
+ card->dev->coherent_dma_mask = DMA_BIT_MASK(32);
if (params->dma_in) {
s6dmac_disable_chan(DMA_MASK_DMAC(params->dma_in),
diff --git a/sound/soc/sh/Kconfig b/sound/soc/sh/Kconfig
index 9154b4363db3..9e6976586554 100644
--- a/sound/soc/sh/Kconfig
+++ b/sound/soc/sh/Kconfig
@@ -23,7 +23,6 @@ config SND_SOC_SH4_SSI
config SND_SOC_SH4_FSI
tristate "SH4 FSI support"
depends on CPU_SUBTYPE_SH7724
- select SH_DMA
help
This option enables FSI sound support
diff --git a/sound/soc/sh/fsi.c b/sound/soc/sh/fsi.c
index 44123248b630..9c49c11c43ce 100644
--- a/sound/soc/sh/fsi.c
+++ b/sound/soc/sh/fsi.c
@@ -17,7 +17,7 @@
#include <linux/platform_device.h>
#include <linux/delay.h>
#include <linux/list.h>
-#include <linux/clk.h>
+#include <linux/pm_runtime.h>
#include <linux/io.h>
#include <sound/core.h>
#include <sound/pcm.h>
@@ -26,8 +26,6 @@
#include <sound/pcm_params.h>
#include <sound/sh_fsi.h>
#include <asm/atomic.h>
-#include <asm/dma.h>
-#include <asm/dma-sh.h>
#define DO_FMT 0x0000
#define DOFF_CTL 0x0004
@@ -97,7 +95,6 @@ struct fsi_priv {
int fifo_max;
int chan;
- int dma_chan;
int byte_offset;
int period_len;
@@ -108,7 +105,6 @@ struct fsi_priv {
struct fsi_master {
void __iomem *base;
int irq;
- struct clk *clk;
struct fsi_priv fsia;
struct fsi_priv fsib;
struct sh_fsi_platform_info *info;
@@ -308,62 +304,6 @@ static int fsi_get_fifo_residue(struct fsi_priv *fsi, int is_play)
return residue;
}
-static int fsi_get_residue(struct fsi_priv *fsi, int is_play)
-{
- int residue;
- int width;
- struct snd_pcm_runtime *runtime;
-
- runtime = fsi->substream->runtime;
-
- /* get 1 channel data width */
- width = frames_to_bytes(runtime, 1) / fsi->chan;
-
- if (2 == width)
- residue = fsi_get_fifo_residue(fsi, is_play);
- else
- residue = get_dma_residue(fsi->dma_chan);
-
- return residue;
-}
-
-/************************************************************************
-
-
- basic dma function
-
-
-************************************************************************/
-#define PORTA_DMA 0
-#define PORTB_DMA 1
-
-static int fsi_get_dma_chan(void)
-{
- if (0 != request_dma(PORTA_DMA, "fsia"))
- return -EIO;
-
- if (0 != request_dma(PORTB_DMA, "fsib")) {
- free_dma(PORTA_DMA);
- return -EIO;
- }
-
- master->fsia.dma_chan = PORTA_DMA;
- master->fsib.dma_chan = PORTB_DMA;
-
- return 0;
-}
-
-static void fsi_free_dma_chan(void)
-{
- dma_wait_for_completion(PORTA_DMA);
- dma_wait_for_completion(PORTB_DMA);
- free_dma(PORTA_DMA);
- free_dma(PORTB_DMA);
-
- master->fsia.dma_chan = -1;
- master->fsib.dma_chan = -1;
-}
-
/************************************************************************
@@ -435,44 +375,6 @@ static void fsi_soft_all_reset(void)
mdelay(10);
}
-static void fsi_16data_push(struct fsi_priv *fsi,
- struct snd_pcm_runtime *runtime,
- int send)
-{
- u16 *dma_start;
- u32 snd;
- int i;
-
- /* get dma start position for FSI */
- dma_start = (u16 *)runtime->dma_area;
- dma_start += fsi->byte_offset / 2;
-
- /*
- * soft dma
- * FSI can not use DMA when 16bpp
- */
- for (i = 0; i < send; i++) {
- snd = (u32)dma_start[i];
- fsi_reg_write(fsi, DODT, snd << 8);
- }
-}
-
-static void fsi_32data_push(struct fsi_priv *fsi,
- struct snd_pcm_runtime *runtime,
- int send)
-{
- u32 *dma_start;
-
- /* get dma start position for FSI */
- dma_start = (u32 *)runtime->dma_area;
- dma_start += fsi->byte_offset / 4;
-
- dma_wait_for_completion(fsi->dma_chan);
- dma_configure_channel(fsi->dma_chan, (SM_INC|0x400|TS_32|TM_BUR));
- dma_write(fsi->dma_chan, (u32)dma_start,
- (u32)(fsi->base + DODT), send * 4);
-}
-
/* playback interrupt */
static int fsi_data_push(struct fsi_priv *fsi)
{
@@ -481,6 +383,8 @@ static int fsi_data_push(struct fsi_priv *fsi)
int send;
int fifo_free;
int width;
+ u8 *start;
+ int i;
if (!fsi ||
!fsi->substream ||
@@ -515,12 +419,22 @@ static int fsi_data_push(struct fsi_priv *fsi)
if (fifo_free < send)
send = fifo_free;
- if (2 == width)
- fsi_16data_push(fsi, runtime, send);
- else if (4 == width)
- fsi_32data_push(fsi, runtime, send);
- else
+ start = runtime->dma_area;
+ start += fsi->byte_offset;
+
+ switch (width) {
+ case 2:
+ for (i = 0; i < send; i++)
+ fsi_reg_write(fsi, DODT,
+ ((u32)*((u16 *)start + i) << 8));
+ break;
+ case 4:
+ for (i = 0; i < send; i++)
+ fsi_reg_write(fsi, DODT, *((u32 *)start + i));
+ break;
+ default:
return -EINVAL;
+ }
fsi->byte_offset += send * width;
@@ -532,6 +446,75 @@ static int fsi_data_push(struct fsi_priv *fsi)
return 0;
}
+static int fsi_data_pop(struct fsi_priv *fsi)
+{
+ struct snd_pcm_runtime *runtime;
+ struct snd_pcm_substream *substream = NULL;
+ int free;
+ int fifo_fill;
+ int width;
+ u8 *start;
+ int i;
+
+ if (!fsi ||
+ !fsi->substream ||
+ !fsi->substream->runtime)
+ return -EINVAL;
+
+ runtime = fsi->substream->runtime;
+
+ /* FSI FIFO has limit.
+ * So, this driver can not send periods data at a time
+ */
+ if (fsi->byte_offset >=
+ fsi->period_len * (fsi->periods + 1)) {
+
+ substream = fsi->substream;
+ fsi->periods = (fsi->periods + 1) % runtime->periods;
+
+ if (0 == fsi->periods)
+ fsi->byte_offset = 0;
+ }
+
+ /* get 1 channel data width */
+ width = frames_to_bytes(runtime, 1) / fsi->chan;
+
+ /* get free space for alsa */
+ free = (fsi->buffer_len - fsi->byte_offset) / width;
+
+ /* get recv size */
+ fifo_fill = fsi_get_fifo_residue(fsi, 0);
+
+ if (free < fifo_fill)
+ fifo_fill = free;
+
+ start = runtime->dma_area;
+ start += fsi->byte_offset;
+
+ switch (width) {
+ case 2:
+ for (i = 0; i < fifo_fill; i++)
+ *((u16 *)start + i) =
+ (u16)(fsi_reg_read(fsi, DIDT) >> 8);
+ break;
+ case 4:
+ for (i = 0; i < fifo_fill; i++)
+ *((u32 *)start + i) = fsi_reg_read(fsi, DIDT);
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ fsi->byte_offset += fifo_fill * width;
+
+ fsi_irq_enable(fsi, 0);
+
+ if (substream)
+ snd_pcm_period_elapsed(substream);
+
+ return 0;
+}
+
static irqreturn_t fsi_interrupt(int irq, void *data)
{
u32 status = fsi_master_read(SOFT_RST) & ~0x00000010;
@@ -545,6 +528,10 @@ static irqreturn_t fsi_interrupt(int irq, void *data)
fsi_data_push(&master->fsia);
if (int_st & INT_B_OUT)
fsi_data_push(&master->fsib);
+ if (int_st & INT_A_IN)
+ fsi_data_pop(&master->fsia);
+ if (int_st & INT_B_IN)
+ fsi_data_pop(&master->fsib);
fsi_master_write(INT_ST, 0x0000000);
@@ -571,7 +558,7 @@ static int fsi_dai_startup(struct snd_pcm_substream *substream,
int is_master;
int ret = 0;
- clk_enable(master->clk);
+ pm_runtime_get_sync(dai->dev);
/* CKG1 */
data = is_play ? (1 << 0) : (1 << 4);
@@ -664,8 +651,6 @@ static int fsi_dai_startup(struct snd_pcm_substream *substream,
}
fsi_reg_write(fsi, reg, data);
- dev_dbg(dai->dev, "use %s format (%d channel) use %d DMAC\n",
- msg, fsi->chan, fsi->dma_chan);
/*
* clear clk reset if master mode
@@ -688,7 +673,7 @@ static void fsi_dai_shutdown(struct snd_pcm_substream *substream,
fsi_irq_disable(fsi, is_play);
fsi_clk_ctrl(fsi, 0);
- clk_disable(master->clk);
+ pm_runtime_put_sync(dai->dev);
}
static int fsi_dai_trigger(struct snd_pcm_substream *substream, int cmd,
@@ -699,16 +684,12 @@ static int fsi_dai_trigger(struct snd_pcm_substream *substream, int cmd,
int is_play = substream->stream == SNDRV_PCM_STREAM_PLAYBACK;
int ret = 0;
- /* capture not supported */
- if (!is_play)
- return -ENODEV;
-
switch (cmd) {
case SNDRV_PCM_TRIGGER_START:
fsi_stream_push(fsi, substream,
frames_to_bytes(runtime, runtime->buffer_size),
frames_to_bytes(runtime, runtime->period_size));
- ret = fsi_data_push(fsi);
+ ret = is_play ? fsi_data_push(fsi) : fsi_data_pop(fsi);
break;
case SNDRV_PCM_TRIGGER_STOP:
fsi_irq_disable(fsi, is_play);
@@ -780,10 +761,9 @@ static snd_pcm_uframes_t fsi_pointer(struct snd_pcm_substream *substream)
{
struct snd_pcm_runtime *runtime = substream->runtime;
struct fsi_priv *fsi = fsi_get(substream);
- int is_play = substream->stream == SNDRV_PCM_STREAM_PLAYBACK;
long location;
- location = (fsi->byte_offset - 1) - fsi_get_residue(fsi, is_play);
+ location = (fsi->byte_offset - 1);
if (location < 0)
location = 0;
@@ -845,7 +825,12 @@ struct snd_soc_dai fsi_soc_dai[] = {
.channels_min = 1,
.channels_max = 8,
},
- /* capture not supported */
+ .capture = {
+ .rates = FSI_RATES,
+ .formats = FSI_FMTS,
+ .channels_min = 1,
+ .channels_max = 8,
+ },
.ops = &fsi_dai_ops,
},
{
@@ -857,7 +842,12 @@ struct snd_soc_dai fsi_soc_dai[] = {
.channels_min = 1,
.channels_max = 8,
},
- /* capture not supported */
+ .capture = {
+ .rates = FSI_RATES,
+ .formats = FSI_FMTS,
+ .channels_min = 1,
+ .channels_max = 8,
+ },
.ops = &fsi_dai_ops,
},
};
@@ -881,7 +871,6 @@ EXPORT_SYMBOL_GPL(fsi_soc_platform);
static int fsi_probe(struct platform_device *pdev)
{
struct resource *res;
- char clk_name[8];
unsigned int irq;
int ret;
@@ -912,23 +901,8 @@ static int fsi_probe(struct platform_device *pdev)
master->fsia.base = master->base;
master->fsib.base = master->base + 0x40;
- master->fsia.dma_chan = -1;
- master->fsib.dma_chan = -1;
-
- ret = fsi_get_dma_chan();
- if (ret < 0) {
- dev_err(&pdev->dev, "cannot get dma api\n");
- goto exit_iounmap;
- }
-
- /* FSI is based on SPU mstp */
- snprintf(clk_name, sizeof(clk_name), "spu%d", pdev->id);
- master->clk = clk_get(NULL, clk_name);
- if (IS_ERR(master->clk)) {
- dev_err(&pdev->dev, "cannot get %s mstp\n", clk_name);
- ret = -EIO;
- goto exit_free_dma;
- }
+ pm_runtime_enable(&pdev->dev);
+ pm_runtime_resume(&pdev->dev);
fsi_soc_dai[0].dev = &pdev->dev;
fsi_soc_dai[1].dev = &pdev->dev;
@@ -938,7 +912,7 @@ static int fsi_probe(struct platform_device *pdev)
ret = request_irq(irq, &fsi_interrupt, IRQF_DISABLED, "fsi", master);
if (ret) {
dev_err(&pdev->dev, "irq request err\n");
- goto exit_free_dma;
+ goto exit_iounmap;
}
ret = snd_soc_register_platform(&fsi_soc_platform);
@@ -951,10 +925,9 @@ static int fsi_probe(struct platform_device *pdev)
exit_free_irq:
free_irq(irq, master);
-exit_free_dma:
- fsi_free_dma_chan();
exit_iounmap:
iounmap(master->base);
+ pm_runtime_disable(&pdev->dev);
exit_kfree:
kfree(master);
master = NULL;
@@ -967,9 +940,7 @@ static int fsi_remove(struct platform_device *pdev)
snd_soc_unregister_dais(fsi_soc_dai, ARRAY_SIZE(fsi_soc_dai));
snd_soc_unregister_platform(&fsi_soc_platform);
- clk_put(master->clk);
-
- fsi_free_dma_chan();
+ pm_runtime_disable(&pdev->dev);
free_irq(master->irq, master);
@@ -979,9 +950,27 @@ static int fsi_remove(struct platform_device *pdev)
return 0;
}
+static int fsi_runtime_nop(struct device *dev)
+{
+ /* Runtime PM callback shared between ->runtime_suspend()
+ * and ->runtime_resume(). Simply returns success.
+ *
+ * This driver re-initializes all registers after
+ * pm_runtime_get_sync() anyway so there is no need
+ * to save and restore registers here.
+ */
+ return 0;
+}
+
+static struct dev_pm_ops fsi_pm_ops = {
+ .runtime_suspend = fsi_runtime_nop,
+ .runtime_resume = fsi_runtime_nop,
+};
+
static struct platform_driver fsi_driver = {
.driver = {
.name = "sh_fsi",
+ .pm = &fsi_pm_ops,
},
.probe = fsi_probe,
.remove = fsi_remove,
diff --git a/sound/soc/soc-cache.c b/sound/soc/soc-cache.c
index c8ceddc2a26c..d2505e8b06c9 100644
--- a/sound/soc/soc-cache.c
+++ b/sound/soc/soc-cache.c
@@ -77,6 +77,35 @@ static int snd_soc_7_9_spi_write(void *control_data, const char *data,
#define snd_soc_7_9_spi_write NULL
#endif
+static int snd_soc_8_8_write(struct snd_soc_codec *codec, unsigned int reg,
+ unsigned int value)
+{
+ u8 *cache = codec->reg_cache;
+ u8 data[2];
+
+ BUG_ON(codec->volatile_register);
+
+ data[0] = reg & 0xff;
+ data[1] = value & 0xff;
+
+ if (reg < codec->reg_cache_size)
+ cache[reg] = value;
+
+ if (codec->hw_write(codec->control_data, data, 2) == 2)
+ return 0;
+ else
+ return -EIO;
+}
+
+static unsigned int snd_soc_8_8_read(struct snd_soc_codec *codec,
+ unsigned int reg)
+{
+ u8 *cache = codec->reg_cache;
+ if (reg >= codec->reg_cache_size)
+ return -1;
+ return cache[reg];
+}
+
static int snd_soc_8_16_write(struct snd_soc_codec *codec, unsigned int reg,
unsigned int value)
{
@@ -150,9 +179,20 @@ static struct {
unsigned int (*read)(struct snd_soc_codec *, unsigned int);
unsigned int (*i2c_read)(struct snd_soc_codec *, unsigned int);
} io_types[] = {
- { 7, 9, snd_soc_7_9_write, snd_soc_7_9_spi_write, snd_soc_7_9_read },
- { 8, 16, snd_soc_8_16_write, NULL, snd_soc_8_16_read,
- snd_soc_8_16_read_i2c },
+ {
+ .addr_bits = 7, .data_bits = 9,
+ .write = snd_soc_7_9_write, .read = snd_soc_7_9_read,
+ .spi_write = snd_soc_7_9_spi_write
+ },
+ {
+ .addr_bits = 8, .data_bits = 8,
+ .write = snd_soc_8_8_write, .read = snd_soc_8_8_read,
+ },
+ {
+ .addr_bits = 8, .data_bits = 16,
+ .write = snd_soc_8_16_write, .read = snd_soc_8_16_read,
+ .i2c_read = snd_soc_8_16_read_i2c,
+ },
};
/**
diff --git a/sound/soc/soc-core.c b/sound/soc/soc-core.c
index 0a1b2f64bbee..ef8f28284cb9 100644
--- a/sound/soc/soc-core.c
+++ b/sound/soc/soc-core.c
@@ -37,7 +37,6 @@
#include <sound/initval.h>
static DEFINE_MUTEX(pcm_mutex);
-static DEFINE_MUTEX(io_mutex);
static DECLARE_WAIT_QUEUE_HEAD(soc_pm_waitq);
#ifdef CONFIG_DEBUG_FS
@@ -81,6 +80,173 @@ static int run_delayed_work(struct delayed_work *dwork)
return ret;
}
+/* codec register dump */
+static ssize_t soc_codec_reg_show(struct snd_soc_codec *codec, char *buf)
+{
+ 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) {
+ if (codec->readable_register && !codec->readable_register(i))
+ continue;
+
+ count += sprintf(buf + count, "%2x: ", i);
+ if (count >= PAGE_SIZE - 1)
+ break;
+
+ if (codec->display_register)
+ count += codec->display_register(codec, buf + count,
+ PAGE_SIZE - count, i);
+ else
+ count += snprintf(buf + count, PAGE_SIZE - count,
+ "%4x", codec->read(codec, i));
+
+ if (count >= PAGE_SIZE - 1)
+ break;
+
+ count += snprintf(buf + count, PAGE_SIZE - count, "\n");
+ if (count >= PAGE_SIZE - 1)
+ break;
+ }
+
+ /* Truncate count; min() would cause a warning */
+ if (count >= PAGE_SIZE)
+ count = PAGE_SIZE - 1;
+
+ return count;
+}
+static ssize_t codec_reg_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct snd_soc_device *devdata = dev_get_drvdata(dev);
+ return soc_codec_reg_show(devdata->card->codec, buf);
+}
+
+static DEVICE_ATTR(codec_reg, 0444, codec_reg_show, NULL);
+
+#ifdef CONFIG_DEBUG_FS
+static int codec_reg_open_file(struct inode *inode, struct file *file)
+{
+ file->private_data = inode->i_private;
+ return 0;
+}
+
+static ssize_t codec_reg_read_file(struct file *file, char __user *user_buf,
+ size_t count, loff_t *ppos)
+{
+ ssize_t ret;
+ struct snd_soc_codec *codec = file->private_data;
+ char *buf = kmalloc(PAGE_SIZE, GFP_KERNEL);
+ if (!buf)
+ return -ENOMEM;
+ ret = soc_codec_reg_show(codec, buf);
+ if (ret >= 0)
+ ret = simple_read_from_buffer(user_buf, count, ppos, buf, ret);
+ kfree(buf);
+ return ret;
+}
+
+static ssize_t codec_reg_write_file(struct file *file,
+ const char __user *user_buf, size_t count, loff_t *ppos)
+{
+ char buf[32];
+ int buf_size;
+ char *start = buf;
+ unsigned long reg, value;
+ int step = 1;
+ struct snd_soc_codec *codec = file->private_data;
+
+ buf_size = min(count, (sizeof(buf)-1));
+ if (copy_from_user(buf, user_buf, buf_size))
+ return -EFAULT;
+ buf[buf_size] = 0;
+
+ if (codec->reg_cache_step)
+ step = codec->reg_cache_step;
+
+ while (*start == ' ')
+ start++;
+ reg = simple_strtoul(start, &start, 16);
+ if ((reg >= codec->reg_cache_size) || (reg % step))
+ return -EINVAL;
+ while (*start == ' ')
+ start++;
+ if (strict_strtoul(start, 16, &value))
+ return -EINVAL;
+ codec->write(codec, reg, value);
+ return buf_size;
+}
+
+static const struct file_operations codec_reg_fops = {
+ .open = codec_reg_open_file,
+ .read = codec_reg_read_file,
+ .write = codec_reg_write_file,
+};
+
+static void soc_init_codec_debugfs(struct snd_soc_codec *codec)
+{
+ char codec_root[128];
+
+ if (codec->dev)
+ snprintf(codec_root, sizeof(codec_root),
+ "%s.%s", codec->name, dev_name(codec->dev));
+ else
+ snprintf(codec_root, sizeof(codec_root),
+ "%s", codec->name);
+
+ codec->debugfs_codec_root = debugfs_create_dir(codec_root,
+ debugfs_root);
+ if (!codec->debugfs_codec_root) {
+ printk(KERN_WARNING
+ "ASoC: Failed to create codec debugfs directory\n");
+ return;
+ }
+
+ codec->debugfs_reg = debugfs_create_file("codec_reg", 0644,
+ codec->debugfs_codec_root,
+ codec, &codec_reg_fops);
+ if (!codec->debugfs_reg)
+ printk(KERN_WARNING
+ "ASoC: Failed to create codec register debugfs file\n");
+
+ codec->debugfs_pop_time = debugfs_create_u32("dapm_pop_time", 0744,
+ codec->debugfs_codec_root,
+ &codec->pop_time);
+ if (!codec->debugfs_pop_time)
+ printk(KERN_WARNING
+ "Failed to create pop time debugfs file\n");
+
+ codec->debugfs_dapm = debugfs_create_dir("dapm",
+ codec->debugfs_codec_root);
+ if (!codec->debugfs_dapm)
+ printk(KERN_WARNING
+ "Failed to create DAPM debugfs directory\n");
+
+ snd_soc_dapm_debugfs_init(codec);
+}
+
+static void soc_cleanup_codec_debugfs(struct snd_soc_codec *codec)
+{
+ debugfs_remove_recursive(codec->debugfs_codec_root);
+}
+
+#else
+
+static inline void soc_init_codec_debugfs(struct snd_soc_codec *codec)
+{
+}
+
+static inline void soc_cleanup_codec_debugfs(struct snd_soc_codec *codec)
+{
+}
+#endif
+
#ifdef CONFIG_SND_SOC_AC97_BUS
/* unregister ac97 codec */
static int soc_ac97_dev_unregister(struct snd_soc_codec *codec)
@@ -790,45 +956,6 @@ static int soc_resume(struct device *dev)
return 0;
}
-
-/**
- * snd_soc_suspend_device: Notify core of device suspend
- *
- * @dev: Device being suspended.
- *
- * In order to ensure that the entire audio subsystem is suspended in a
- * coordinated fashion ASoC devices should suspend themselves when
- * called by ASoC. When the standard kernel suspend process asks the
- * device to suspend it should call this function to initiate a suspend
- * of the entire ASoC card.
- *
- * \note Currently this function is stubbed out.
- */
-int snd_soc_suspend_device(struct device *dev)
-{
- return 0;
-}
-EXPORT_SYMBOL_GPL(snd_soc_suspend_device);
-
-/**
- * snd_soc_resume_device: Notify core of device resume
- *
- * @dev: Device being resumed.
- *
- * In order to ensure that the entire audio subsystem is resumed in a
- * coordinated fashion ASoC devices should resume themselves when called
- * by ASoC. When the standard kernel resume process asks the device
- * to resume it should call this function. Once all the components of
- * the card have notified that they are ready to be resumed the card
- * will be resumed.
- *
- * \note Currently this function is stubbed out.
- */
-int snd_soc_resume_device(struct device *dev)
-{
- return 0;
-}
-EXPORT_SYMBOL_GPL(snd_soc_resume_device);
#else
#define soc_suspend NULL
#define soc_resume NULL
@@ -843,6 +970,7 @@ static void snd_soc_instantiate_card(struct snd_soc_card *card)
struct platform_device,
dev);
struct snd_soc_codec_device *codec_dev = card->socdev->codec_dev;
+ struct snd_soc_codec *codec;
struct snd_soc_platform *platform;
struct snd_soc_dai *dai;
int i, found, ret, ac97;
@@ -931,6 +1059,7 @@ static void snd_soc_instantiate_card(struct snd_soc_card *card)
if (ret < 0)
goto cpu_dai_err;
}
+ codec = card->codec;
if (platform->probe) {
ret = platform->probe(pdev);
@@ -945,10 +1074,69 @@ static void snd_soc_instantiate_card(struct snd_soc_card *card)
INIT_WORK(&card->deferred_resume_work, soc_resume_deferred);
#endif
+ for (i = 0; i < card->num_links; i++) {
+ if (card->dai_link[i].init) {
+ ret = card->dai_link[i].init(codec);
+ if (ret < 0) {
+ printk(KERN_ERR "asoc: failed to init %s\n",
+ card->dai_link[i].stream_name);
+ continue;
+ }
+ }
+ if (card->dai_link[i].codec_dai->ac97_control)
+ ac97 = 1;
+ }
+
+ snprintf(codec->card->shortname, sizeof(codec->card->shortname),
+ "%s", card->name);
+ snprintf(codec->card->longname, sizeof(codec->card->longname),
+ "%s (%s)", card->name, codec->name);
+
+ /* Make sure all DAPM widgets are instantiated */
+ snd_soc_dapm_new_widgets(codec);
+
+ ret = snd_card_register(codec->card);
+ if (ret < 0) {
+ printk(KERN_ERR "asoc: failed to register soundcard for %s\n",
+ codec->name);
+ goto card_err;
+ }
+
+ mutex_lock(&codec->mutex);
+#ifdef CONFIG_SND_SOC_AC97_BUS
+ /* Only instantiate AC97 if not already done by the adaptor
+ * for the generic AC97 subsystem.
+ */
+ if (ac97 && strcmp(codec->name, "AC97") != 0) {
+ ret = soc_ac97_dev_register(codec);
+ if (ret < 0) {
+ printk(KERN_ERR "asoc: AC97 device register failed\n");
+ snd_card_free(codec->card);
+ mutex_unlock(&codec->mutex);
+ goto card_err;
+ }
+ }
+#endif
+
+ ret = snd_soc_dapm_sys_add(card->socdev->dev);
+ if (ret < 0)
+ printk(KERN_WARNING "asoc: failed to add dapm sysfs entries\n");
+
+ ret = device_create_file(card->socdev->dev, &dev_attr_codec_reg);
+ if (ret < 0)
+ printk(KERN_WARNING "asoc: failed to add codec sysfs files\n");
+
+ soc_init_codec_debugfs(codec);
+ mutex_unlock(&codec->mutex);
+
card->instantiated = 1;
return;
+card_err:
+ if (platform->remove)
+ platform->remove(pdev);
+
platform_err:
if (codec_dev->remove)
codec_dev->remove(pdev);
@@ -1151,157 +1339,6 @@ int snd_soc_codec_volatile_register(struct snd_soc_codec *codec, int reg)
}
EXPORT_SYMBOL_GPL(snd_soc_codec_volatile_register);
-/* codec register dump */
-static ssize_t soc_codec_reg_show(struct snd_soc_codec *codec, char *buf)
-{
- 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) {
- if (codec->readable_register && !codec->readable_register(i))
- continue;
-
- count += sprintf(buf + count, "%2x: ", i);
- if (count >= PAGE_SIZE - 1)
- break;
-
- if (codec->display_register)
- count += codec->display_register(codec, buf + count,
- PAGE_SIZE - count, i);
- else
- count += snprintf(buf + count, PAGE_SIZE - count,
- "%4x", codec->read(codec, i));
-
- if (count >= PAGE_SIZE - 1)
- break;
-
- count += snprintf(buf + count, PAGE_SIZE - count, "\n");
- if (count >= PAGE_SIZE - 1)
- break;
- }
-
- /* Truncate count; min() would cause a warning */
- if (count >= PAGE_SIZE)
- count = PAGE_SIZE - 1;
-
- return count;
-}
-static ssize_t codec_reg_show(struct device *dev,
- struct device_attribute *attr, char *buf)
-{
- struct snd_soc_device *devdata = dev_get_drvdata(dev);
- return soc_codec_reg_show(devdata->card->codec, buf);
-}
-
-static DEVICE_ATTR(codec_reg, 0444, codec_reg_show, NULL);
-
-#ifdef CONFIG_DEBUG_FS
-static int codec_reg_open_file(struct inode *inode, struct file *file)
-{
- file->private_data = inode->i_private;
- return 0;
-}
-
-static ssize_t codec_reg_read_file(struct file *file, char __user *user_buf,
- size_t count, loff_t *ppos)
-{
- ssize_t ret;
- struct snd_soc_codec *codec = file->private_data;
- char *buf = kmalloc(PAGE_SIZE, GFP_KERNEL);
- if (!buf)
- return -ENOMEM;
- ret = soc_codec_reg_show(codec, buf);
- if (ret >= 0)
- ret = simple_read_from_buffer(user_buf, count, ppos, buf, ret);
- kfree(buf);
- return ret;
-}
-
-static ssize_t codec_reg_write_file(struct file *file,
- const char __user *user_buf, size_t count, loff_t *ppos)
-{
- char buf[32];
- int buf_size;
- char *start = buf;
- unsigned long reg, value;
- int step = 1;
- struct snd_soc_codec *codec = file->private_data;
-
- buf_size = min(count, (sizeof(buf)-1));
- if (copy_from_user(buf, user_buf, buf_size))
- return -EFAULT;
- buf[buf_size] = 0;
-
- if (codec->reg_cache_step)
- step = codec->reg_cache_step;
-
- while (*start == ' ')
- start++;
- reg = simple_strtoul(start, &start, 16);
- if ((reg >= codec->reg_cache_size) || (reg % step))
- return -EINVAL;
- while (*start == ' ')
- start++;
- if (strict_strtoul(start, 16, &value))
- return -EINVAL;
- codec->write(codec, reg, value);
- return buf_size;
-}
-
-static const struct file_operations codec_reg_fops = {
- .open = codec_reg_open_file,
- .read = codec_reg_read_file,
- .write = codec_reg_write_file,
-};
-
-static void soc_init_codec_debugfs(struct snd_soc_codec *codec)
-{
- codec->debugfs_reg = debugfs_create_file("codec_reg", 0644,
- debugfs_root, codec,
- &codec_reg_fops);
- if (!codec->debugfs_reg)
- printk(KERN_WARNING
- "ASoC: Failed to create codec register debugfs file\n");
-
- codec->debugfs_pop_time = debugfs_create_u32("dapm_pop_time", 0744,
- debugfs_root,
- &codec->pop_time);
- if (!codec->debugfs_pop_time)
- printk(KERN_WARNING
- "Failed to create pop time debugfs file\n");
-
- codec->debugfs_dapm = debugfs_create_dir("dapm", debugfs_root);
- if (!codec->debugfs_dapm)
- printk(KERN_WARNING
- "Failed to create DAPM debugfs directory\n");
-
- snd_soc_dapm_debugfs_init(codec);
-}
-
-static void soc_cleanup_codec_debugfs(struct snd_soc_codec *codec)
-{
- debugfs_remove_recursive(codec->debugfs_dapm);
- debugfs_remove(codec->debugfs_pop_time);
- debugfs_remove(codec->debugfs_reg);
-}
-
-#else
-
-static inline void soc_init_codec_debugfs(struct snd_soc_codec *codec)
-{
-}
-
-static inline void soc_cleanup_codec_debugfs(struct snd_soc_codec *codec)
-{
-}
-#endif
-
/**
* snd_soc_new_ac97_codec - initailise AC97 device
* @codec: audio codec
@@ -1369,19 +1406,41 @@ int snd_soc_update_bits(struct snd_soc_codec *codec, unsigned short reg,
int change;
unsigned int 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_update_bits_locked - update codec register bits
+ * @codec: audio codec
+ * @reg: codec register
+ * @mask: register mask
+ * @value: new value
+ *
+ * Writes new register value, and takes the codec mutex.
+ *
+ * Returns 1 for change else 0.
+ */
+static int snd_soc_update_bits_locked(struct snd_soc_codec *codec,
+ unsigned short reg, unsigned int mask,
+ unsigned int value)
+{
+ int change;
+
+ mutex_lock(&codec->mutex);
+ change = snd_soc_update_bits(codec, reg, mask, value);
+ mutex_unlock(&codec->mutex);
+
+ return change;
+}
+
+/**
* snd_soc_test_bits - test register for change
* @codec: audio codec
* @reg: codec register
@@ -1399,11 +1458,9 @@ int snd_soc_test_bits(struct snd_soc_codec *codec, unsigned short reg,
int change;
unsigned int 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;
}
@@ -1450,89 +1507,16 @@ int snd_soc_new_pcms(struct snd_soc_device *socdev, int idx, const char *xid)
mutex_unlock(&codec->mutex);
return ret;
}
- }
-
- mutex_unlock(&codec->mutex);
- return ret;
-}
-EXPORT_SYMBOL_GPL(snd_soc_new_pcms);
-
-/**
- * snd_soc_init_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_init_card(struct snd_soc_device *socdev)
-{
- struct snd_soc_card *card = socdev->card;
- struct snd_soc_codec *codec = card->codec;
- int ret = 0, i, ac97 = 0, err = 0;
-
- for (i = 0; i < card->num_links; i++) {
- if (card->dai_link[i].init) {
- err = card->dai_link[i].init(codec);
- if (err < 0) {
- printk(KERN_ERR "asoc: failed to init %s\n",
- card->dai_link[i].stream_name);
- continue;
- }
- }
if (card->dai_link[i].codec_dai->ac97_control) {
- ac97 = 1;
snd_ac97_dev_add_pdata(codec->ac97,
card->dai_link[i].cpu_dai->ac97_pdata);
}
}
- snprintf(codec->card->shortname, sizeof(codec->card->shortname),
- "%s", card->name);
- snprintf(codec->card->longname, sizeof(codec->card->longname),
- "%s (%s)", card->name, codec->name);
-
- /* Make sure all DAPM widgets are instantiated */
- snd_soc_dapm_new_widgets(codec);
-
- ret = snd_card_register(codec->card);
- if (ret < 0) {
- printk(KERN_ERR "asoc: failed to register soundcard for %s\n",
- codec->name);
- goto out;
- }
-
- mutex_lock(&codec->mutex);
-#ifdef CONFIG_SND_SOC_AC97_BUS
- /* Only instantiate AC97 if not already done by the adaptor
- * for the generic AC97 subsystem.
- */
- if (ac97 && strcmp(codec->name, "AC97") != 0) {
- ret = soc_ac97_dev_register(codec);
- if (ret < 0) {
- printk(KERN_ERR "asoc: AC97 device register failed\n");
- snd_card_free(codec->card);
- mutex_unlock(&codec->mutex);
- 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 files\n");
- soc_init_codec_debugfs(codec);
mutex_unlock(&codec->mutex);
-
-out:
return ret;
}
-EXPORT_SYMBOL_GPL(snd_soc_init_card);
+EXPORT_SYMBOL_GPL(snd_soc_new_pcms);
/**
* snd_soc_free_pcms - free sound card and pcms
@@ -1734,7 +1718,7 @@ int snd_soc_put_enum_double(struct snd_kcontrol *kcontrol,
mask |= (bitmask - 1) << e->shift_r;
}
- return snd_soc_update_bits(codec, e->reg, mask, val);
+ return snd_soc_update_bits_locked(codec, e->reg, mask, val);
}
EXPORT_SYMBOL_GPL(snd_soc_put_enum_double);
@@ -1808,7 +1792,7 @@ int snd_soc_put_value_enum_double(struct snd_kcontrol *kcontrol,
mask |= e->mask << e->shift_r;
}
- return snd_soc_update_bits(codec, e->reg, mask, val);
+ return snd_soc_update_bits_locked(codec, e->reg, mask, val);
}
EXPORT_SYMBOL_GPL(snd_soc_put_value_enum_double);
@@ -1969,7 +1953,7 @@ int snd_soc_put_volsw(struct snd_kcontrol *kcontrol,
val_mask |= mask << rshift;
val |= val2 << rshift;
}
- return snd_soc_update_bits(codec, reg, val_mask, val);
+ return snd_soc_update_bits_locked(codec, reg, val_mask, val);
}
EXPORT_SYMBOL_GPL(snd_soc_put_volsw);
@@ -2075,11 +2059,11 @@ int snd_soc_put_volsw_2r(struct snd_kcontrol *kcontrol,
val = val << shift;
val2 = val2 << shift;
- err = snd_soc_update_bits(codec, reg, val_mask, val);
+ err = snd_soc_update_bits_locked(codec, reg, val_mask, val);
if (err < 0)
return err;
- err = snd_soc_update_bits(codec, reg2, val_mask, val2);
+ err = snd_soc_update_bits_locked(codec, reg2, val_mask, val2);
return err;
}
EXPORT_SYMBOL_GPL(snd_soc_put_volsw_2r);
@@ -2158,7 +2142,7 @@ int snd_soc_put_volsw_s8(struct snd_kcontrol *kcontrol,
val = (ucontrol->value.integer.value[0]+min) & 0xff;
val |= ((ucontrol->value.integer.value[1]+min) & 0xff) << 8;
- return snd_soc_update_bits(codec, reg, 0xffff, val);
+ return snd_soc_update_bits_locked(codec, reg, 0xffff, val);
}
EXPORT_SYMBOL_GPL(snd_soc_put_volsw_s8);
@@ -2205,16 +2189,18 @@ EXPORT_SYMBOL_GPL(snd_soc_dai_set_clkdiv);
* snd_soc_dai_set_pll - configure DAI PLL.
* @dai: DAI
* @pll_id: DAI specific PLL ID
+ * @source: DAI specific source for the PLL
* @freq_in: PLL input clock frequency in Hz
* @freq_out: requested PLL output clock frequency in Hz
*
* Configures and enables PLL to generate output clock based on input clock.
*/
-int snd_soc_dai_set_pll(struct snd_soc_dai *dai,
- int pll_id, unsigned int freq_in, unsigned int freq_out)
+int snd_soc_dai_set_pll(struct snd_soc_dai *dai, int pll_id, int source,
+ unsigned int freq_in, unsigned int freq_out)
{
if (dai->ops && dai->ops->set_pll)
- return dai->ops->set_pll(dai, pll_id, freq_in, freq_out);
+ return dai->ops->set_pll(dai, pll_id, source,
+ freq_in, freq_out);
else
return -EINVAL;
}
@@ -2259,6 +2245,30 @@ int snd_soc_dai_set_tdm_slot(struct snd_soc_dai *dai,
EXPORT_SYMBOL_GPL(snd_soc_dai_set_tdm_slot);
/**
+ * snd_soc_dai_set_channel_map - configure DAI audio channel map
+ * @dai: DAI
+ * @tx_num: how many TX channels
+ * @tx_slot: pointer to an array which imply the TX slot number channel
+ * 0~num-1 uses
+ * @rx_num: how many RX channels
+ * @rx_slot: pointer to an array which imply the RX slot number channel
+ * 0~num-1 uses
+ *
+ * configure the relationship between channel number and TDM slot number.
+ */
+int snd_soc_dai_set_channel_map(struct snd_soc_dai *dai,
+ unsigned int tx_num, unsigned int *tx_slot,
+ unsigned int rx_num, unsigned int *rx_slot)
+{
+ if (dai->ops && dai->ops->set_channel_map)
+ return dai->ops->set_channel_map(dai, tx_num, tx_slot,
+ rx_num, rx_slot);
+ else
+ return -EINVAL;
+}
+EXPORT_SYMBOL_GPL(snd_soc_dai_set_channel_map);
+
+/**
* snd_soc_dai_set_tristate - configure DAI system or master clock.
* @dai: DAI
* @tristate: tristate enable
diff --git a/sound/soc/soc-dapm.c b/sound/soc/soc-dapm.c
index 66d4c165f99b..0d294ef72590 100644
--- a/sound/soc/soc-dapm.c
+++ b/sound/soc/soc-dapm.c
@@ -719,6 +719,10 @@ static int dapm_supply_check_power(struct snd_soc_dapm_widget *w)
/* Check if one of our outputs is connected */
list_for_each_entry(path, &w->sinks, list_source) {
+ if (path->connected &&
+ !path->connected(path->source, path->sink))
+ continue;
+
if (path->sink && path->sink->power_check &&
path->sink->power_check(path->sink)) {
power = 1;
@@ -1152,6 +1156,9 @@ static ssize_t dapm_widget_power_read_file(struct file *file,
w->active ? "active" : "inactive");
list_for_each_entry(p, &w->sources, list_sink) {
+ if (p->connected && !p->connected(w, p->sink))
+ continue;
+
if (p->connect)
ret += snprintf(buf + ret, PAGE_SIZE - ret,
" in %s %s\n",
@@ -1159,6 +1166,9 @@ static ssize_t dapm_widget_power_read_file(struct file *file,
p->source->name);
}
list_for_each_entry(p, &w->sinks, list_source) {
+ if (p->connected && !p->connected(w, p->sink))
+ continue;
+
if (p->connect)
ret += snprintf(buf + ret, PAGE_SIZE - ret,
" out %s %s\n",
@@ -1206,8 +1216,8 @@ void snd_soc_dapm_debugfs_init(struct snd_soc_codec *codec)
/* 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 mux, int val, struct soc_enum *e)
+ struct snd_kcontrol *kcontrol, int change,
+ int mux, struct soc_enum *e)
{
struct snd_soc_dapm_path *path;
int found = 0;
@@ -1216,7 +1226,7 @@ static int dapm_mux_update_power(struct snd_soc_dapm_widget *widget,
widget->id != snd_soc_dapm_value_mux)
return -ENODEV;
- if (!snd_soc_test_bits(widget->codec, e->reg, mask, val))
+ if (!change)
return 0;
/* find dapm widget path assoc with kcontrol */
@@ -1401,10 +1411,13 @@ int snd_soc_dapm_sync(struct snd_soc_codec *codec)
EXPORT_SYMBOL_GPL(snd_soc_dapm_sync);
static int snd_soc_dapm_add_route(struct snd_soc_codec *codec,
- const char *sink, const char *control, const char *source)
+ const struct snd_soc_dapm_route *route)
{
struct snd_soc_dapm_path *path;
struct snd_soc_dapm_widget *wsource = NULL, *wsink = NULL, *w;
+ const char *sink = route->sink;
+ const char *control = route->control;
+ const char *source = route->source;
int ret = 0;
/* find src and dest widgets */
@@ -1428,6 +1441,7 @@ static int snd_soc_dapm_add_route(struct snd_soc_codec *codec,
path->source = wsource;
path->sink = wsink;
+ path->connected = route->connected;
INIT_LIST_HEAD(&path->list);
INIT_LIST_HEAD(&path->list_source);
INIT_LIST_HEAD(&path->list_sink);
@@ -1528,8 +1542,7 @@ int snd_soc_dapm_add_routes(struct snd_soc_codec *codec,
int i, ret;
for (i = 0; i < num; i++) {
- ret = snd_soc_dapm_add_route(codec, route->sink,
- route->control, route->source);
+ ret = snd_soc_dapm_add_route(codec, route);
if (ret < 0) {
printk(KERN_ERR "Failed to add route %s->%s\n",
route->source,
@@ -1766,7 +1779,7 @@ int snd_soc_dapm_put_enum_double(struct snd_kcontrol *kcontrol,
{
struct snd_soc_dapm_widget *widget = snd_kcontrol_chip(kcontrol);
struct soc_enum *e = (struct soc_enum *)kcontrol->private_value;
- unsigned int val, mux;
+ unsigned int val, mux, change;
unsigned int mask, bitmask;
int ret = 0;
@@ -1786,20 +1799,21 @@ int snd_soc_dapm_put_enum_double(struct snd_kcontrol *kcontrol,
mutex_lock(&widget->codec->mutex);
widget->value = val;
- dapm_mux_update_power(widget, kcontrol, mask, mux, val, e);
- if (widget->event) {
- if (widget->event_flags & SND_SOC_DAPM_PRE_REG) {
- ret = widget->event(widget,
- kcontrol, 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,
- kcontrol, SND_SOC_DAPM_POST_REG);
- } else
- ret = snd_soc_update_bits(widget->codec, e->reg, mask, val);
+ change = snd_soc_test_bits(widget->codec, e->reg, mask, val);
+ dapm_mux_update_power(widget, kcontrol, change, mux, e);
+
+ if (widget->event_flags & SND_SOC_DAPM_PRE_REG) {
+ ret = widget->event(widget,
+ kcontrol, 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,
+ kcontrol, SND_SOC_DAPM_POST_REG);
out:
mutex_unlock(&widget->codec->mutex);
@@ -1808,6 +1822,54 @@ out:
EXPORT_SYMBOL_GPL(snd_soc_dapm_put_enum_double);
/**
+ * snd_soc_dapm_get_enum_virt - Get virtual DAPM mux
+ * @kcontrol: mixer control
+ * @ucontrol: control element information
+ *
+ * Returns 0 for success.
+ */
+int snd_soc_dapm_get_enum_virt(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_dapm_widget *widget = snd_kcontrol_chip(kcontrol);
+
+ ucontrol->value.enumerated.item[0] = widget->value;
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(snd_soc_dapm_get_enum_virt);
+
+/**
+ * snd_soc_dapm_put_enum_virt - Set virtual DAPM mux
+ * @kcontrol: mixer control
+ * @ucontrol: control element information
+ *
+ * Returns 0 for success.
+ */
+int snd_soc_dapm_put_enum_virt(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;
+ int change;
+ int ret = 0;
+
+ if (ucontrol->value.enumerated.item[0] >= e->max)
+ return -EINVAL;
+
+ mutex_lock(&widget->codec->mutex);
+
+ change = widget->value != ucontrol->value.enumerated.item[0];
+ widget->value = ucontrol->value.enumerated.item[0];
+ dapm_mux_update_power(widget, kcontrol, change, widget->value, e);
+
+ mutex_unlock(&widget->codec->mutex);
+ return ret;
+}
+EXPORT_SYMBOL_GPL(snd_soc_dapm_put_enum_virt);
+
+/**
* snd_soc_dapm_get_value_enum_double - dapm semi enumerated double mixer get
* callback
* @kcontrol: mixer control
@@ -1865,7 +1927,7 @@ int snd_soc_dapm_put_value_enum_double(struct snd_kcontrol *kcontrol,
{
struct snd_soc_dapm_widget *widget = snd_kcontrol_chip(kcontrol);
struct soc_enum *e = (struct soc_enum *)kcontrol->private_value;
- unsigned int val, mux;
+ unsigned int val, mux, change;
unsigned int mask;
int ret = 0;
@@ -1883,20 +1945,21 @@ int snd_soc_dapm_put_value_enum_double(struct snd_kcontrol *kcontrol,
mutex_lock(&widget->codec->mutex);
widget->value = val;
- dapm_mux_update_power(widget, kcontrol, mask, mux, val, e);
- if (widget->event) {
- if (widget->event_flags & SND_SOC_DAPM_PRE_REG) {
- ret = widget->event(widget,
- kcontrol, 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,
- kcontrol, SND_SOC_DAPM_POST_REG);
- } else
- ret = snd_soc_update_bits(widget->codec, e->reg, mask, val);
+ change = snd_soc_test_bits(widget->codec, e->reg, mask, val);
+ dapm_mux_update_power(widget, kcontrol, change, mux, e);
+
+ if (widget->event_flags & SND_SOC_DAPM_PRE_REG) {
+ ret = widget->event(widget,
+ kcontrol, 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,
+ kcontrol, SND_SOC_DAPM_POST_REG);
out:
mutex_unlock(&widget->codec->mutex);
diff --git a/sound/soc/soc-jack.c b/sound/soc/soc-jack.c
index 1d455ab79490..3c07a94c2e30 100644
--- a/sound/soc/soc-jack.c
+++ b/sound/soc/soc-jack.c
@@ -58,7 +58,7 @@ EXPORT_SYMBOL_GPL(snd_soc_jack_new);
*/
void snd_soc_jack_report(struct snd_soc_jack *jack, int status, int mask)
{
- struct snd_soc_codec *codec = jack->card->codec;
+ struct snd_soc_codec *codec;
struct snd_soc_jack_pin *pin;
int enable;
int oldstatus;
@@ -67,6 +67,7 @@ void snd_soc_jack_report(struct snd_soc_jack *jack, int status, int mask)
WARN_ON_ONCE(!jack);
return;
}
+ codec = jack->card->codec;
mutex_lock(&codec->mutex);
@@ -162,6 +163,9 @@ static void snd_soc_jack_gpio_detect(struct snd_soc_jack_gpio *gpio)
else
report = 0;
+ if (gpio->jack_status_check)
+ report = gpio->jack_status_check();
+
snd_soc_jack_report(jack, report, gpio->report);
}
diff --git a/sound/soc/soc-utils.c b/sound/soc/soc-utils.c
new file mode 100644
index 000000000000..1d07b931f3d8
--- /dev/null
+++ b/sound/soc/soc-utils.c
@@ -0,0 +1,74 @@
+/*
+ * soc-util.c -- ALSA SoC Audio Layer utility functions
+ *
+ * Copyright 2009 Wolfson Microelectronics PLC.
+ *
+ * Author: Mark Brown <broonie@opensource.wolfsonmicro.com>
+ * Liam Girdwood <lrg@slimlogic.co.uk>
+ *
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2 of the License, or (at your
+ * option) any later version.
+ */
+
+#include <sound/core.h>
+#include <sound/pcm.h>
+#include <sound/pcm_params.h>
+#include <sound/soc.h>
+
+int snd_soc_calc_frame_size(int sample_size, int channels, int tdm_slots)
+{
+ return sample_size * channels * tdm_slots;
+}
+EXPORT_SYMBOL_GPL(snd_soc_calc_frame_size);
+
+int snd_soc_params_to_frame_size(struct snd_pcm_hw_params *params)
+{
+ int sample_size;
+
+ switch (params_format(params)) {
+ case SNDRV_PCM_FORMAT_S16_LE:
+ case SNDRV_PCM_FORMAT_S16_BE:
+ sample_size = 16;
+ break;
+ case SNDRV_PCM_FORMAT_S20_3LE:
+ case SNDRV_PCM_FORMAT_S20_3BE:
+ sample_size = 20;
+ break;
+ case SNDRV_PCM_FORMAT_S24_LE:
+ case SNDRV_PCM_FORMAT_S24_BE:
+ sample_size = 24;
+ break;
+ case SNDRV_PCM_FORMAT_S32_LE:
+ case SNDRV_PCM_FORMAT_S32_BE:
+ sample_size = 32;
+ break;
+ default:
+ return -ENOTSUPP;
+ }
+
+ return snd_soc_calc_frame_size(sample_size, params_channels(params),
+ 1);
+}
+EXPORT_SYMBOL_GPL(snd_soc_params_to_frame_size);
+
+int snd_soc_calc_bclk(int fs, int sample_size, int channels, int tdm_slots)
+{
+ return fs * snd_soc_calc_frame_size(sample_size, channels, tdm_slots);
+}
+EXPORT_SYMBOL_GPL(snd_soc_calc_bclk);
+
+int snd_soc_params_to_bclk(struct snd_pcm_hw_params *params)
+{
+ int ret;
+
+ ret = snd_soc_params_to_frame_size(params);
+
+ if (ret > 0)
+ return ret * params_rate(params);
+ else
+ return ret;
+}
+EXPORT_SYMBOL_GPL(snd_soc_params_to_bclk);
diff --git a/sound/usb/usbaudio.c b/sound/usb/usbaudio.c
index 8db0374e10d5..b074a594c595 100644
--- a/sound/usb/usbaudio.c
+++ b/sound/usb/usbaudio.c
@@ -2893,7 +2893,9 @@ static int snd_usb_create_streams(struct snd_usb_audio *chip, int ctrlif)
if ((altsd->bInterfaceClass == USB_CLASS_AUDIO ||
altsd->bInterfaceClass == USB_CLASS_VENDOR_SPEC) &&
altsd->bInterfaceSubClass == USB_SUBCLASS_MIDI_STREAMING) {
- if (snd_usb_create_midi_interface(chip, iface, NULL) < 0) {
+ int err = snd_usbmidi_create(chip->card, iface,
+ &chip->midi_list, NULL);
+ if (err < 0) {
snd_printk(KERN_ERR "%d:%u:%d: cannot create sequencer device\n", dev->devnum, ctrlif, j);
continue;
}
@@ -3038,12 +3040,11 @@ static int create_uaxx_quirk(struct snd_usb_audio *chip,
.type = QUIRK_MIDI_FIXED_ENDPOINT,
.data = &uaxx_ep
};
- if (chip->usb_id == USB_ID(0x0582, 0x002b))
- return snd_usb_create_midi_interface(chip, iface,
- &ua700_quirk);
- else
- return snd_usb_create_midi_interface(chip, iface,
- &uaxx_quirk);
+ const struct snd_usb_audio_quirk *quirk =
+ chip->usb_id == USB_ID(0x0582, 0x002b)
+ ? &ua700_quirk : &uaxx_quirk;
+ return snd_usbmidi_create(chip->card, iface,
+ &chip->midi_list, quirk);
}
if (altsd->bNumEndpoints != 1)
@@ -3370,6 +3371,13 @@ static int audiophile_skip_setting_quirk(struct snd_usb_audio *chip,
return 0; /* keep this altsetting */
}
+static int create_any_midi_quirk(struct snd_usb_audio *chip,
+ struct usb_interface *intf,
+ const struct snd_usb_audio_quirk *quirk)
+{
+ return snd_usbmidi_create(chip->card, intf, &chip->midi_list, quirk);
+}
+
/*
* audio-interface quirks
*
@@ -3387,14 +3395,14 @@ static int snd_usb_create_quirk(struct snd_usb_audio *chip,
static const quirk_func_t quirk_funcs[] = {
[QUIRK_IGNORE_INTERFACE] = ignore_interface_quirk,
[QUIRK_COMPOSITE] = create_composite_quirk,
- [QUIRK_MIDI_STANDARD_INTERFACE] = snd_usb_create_midi_interface,
- [QUIRK_MIDI_FIXED_ENDPOINT] = snd_usb_create_midi_interface,
- [QUIRK_MIDI_YAMAHA] = snd_usb_create_midi_interface,
- [QUIRK_MIDI_MIDIMAN] = snd_usb_create_midi_interface,
- [QUIRK_MIDI_NOVATION] = snd_usb_create_midi_interface,
- [QUIRK_MIDI_FASTLANE] = snd_usb_create_midi_interface,
- [QUIRK_MIDI_EMAGIC] = snd_usb_create_midi_interface,
- [QUIRK_MIDI_CME] = snd_usb_create_midi_interface,
+ [QUIRK_MIDI_STANDARD_INTERFACE] = create_any_midi_quirk,
+ [QUIRK_MIDI_FIXED_ENDPOINT] = create_any_midi_quirk,
+ [QUIRK_MIDI_YAMAHA] = create_any_midi_quirk,
+ [QUIRK_MIDI_MIDIMAN] = create_any_midi_quirk,
+ [QUIRK_MIDI_NOVATION] = create_any_midi_quirk,
+ [QUIRK_MIDI_FASTLANE] = create_any_midi_quirk,
+ [QUIRK_MIDI_EMAGIC] = create_any_midi_quirk,
+ [QUIRK_MIDI_CME] = create_any_midi_quirk,
[QUIRK_AUDIO_STANDARD_INTERFACE] = create_standard_audio_quirk,
[QUIRK_AUDIO_FIXED_ENDPOINT] = create_fixed_stream_quirk,
[QUIRK_AUDIO_EDIROL_UA1000] = create_ua1000_quirk,
diff --git a/sound/usb/usbaudio.h b/sound/usb/usbaudio.h
index e9a3a9dca15c..40ba8115fb81 100644
--- a/sound/usb/usbaudio.h
+++ b/sound/usb/usbaudio.h
@@ -132,7 +132,6 @@ struct snd_usb_audio {
int pcm_devs;
struct list_head midi_list; /* list of midi interfaces */
- int next_midi_device;
struct list_head mixer_list; /* list of mixer interfaces */
};
@@ -227,8 +226,10 @@ int snd_usb_create_mixer(struct snd_usb_audio *chip, int ctrlif,
int ignore_error);
void snd_usb_mixer_disconnect(struct list_head *p);
-int snd_usb_create_midi_interface(struct snd_usb_audio *chip, struct usb_interface *iface,
- const struct snd_usb_audio_quirk *quirk);
+int snd_usbmidi_create(struct snd_card *card,
+ struct usb_interface *iface,
+ struct list_head *midi_list,
+ const struct snd_usb_audio_quirk *quirk);
void snd_usbmidi_input_stop(struct list_head* p);
void snd_usbmidi_input_start(struct list_head* p);
void snd_usbmidi_disconnect(struct list_head *p);
diff --git a/sound/usb/usbmidi.c b/sound/usb/usbmidi.c
index 0eff19ceb7e1..6e89b8368d9a 100644
--- a/sound/usb/usbmidi.c
+++ b/sound/usb/usbmidi.c
@@ -1,7 +1,7 @@
/*
* usbmidi.c - ALSA USB MIDI driver
*
- * Copyright (c) 2002-2007 Clemens Ladisch
+ * Copyright (c) 2002-2009 Clemens Ladisch
* All rights reserved.
*
* Based on the OSS usb-midi driver by NAGANO Daisuke,
@@ -47,6 +47,7 @@
#include <linux/usb.h>
#include <linux/wait.h>
#include <sound/core.h>
+#include <sound/control.h>
#include <sound/rawmidi.h>
#include <sound/asequencer.h>
#include "usbaudio.h"
@@ -101,7 +102,8 @@ struct usb_protocol_ops {
};
struct snd_usb_midi {
- struct snd_usb_audio *chip;
+ struct usb_device *dev;
+ struct snd_card *card;
struct usb_interface *iface;
const struct snd_usb_audio_quirk *quirk;
struct snd_rawmidi *rmidi;
@@ -109,13 +111,19 @@ struct snd_usb_midi {
struct list_head list;
struct timer_list error_timer;
spinlock_t disc_lock;
+ struct mutex mutex;
+ u32 usb_id;
+ int next_midi_device;
struct snd_usb_midi_endpoint {
struct snd_usb_midi_out_endpoint *out;
struct snd_usb_midi_in_endpoint *in;
} endpoints[MIDI_MAX_ENDPOINTS];
unsigned long input_triggered;
+ unsigned int opened;
unsigned char disconnected;
+
+ struct snd_kcontrol *roland_load_ctl;
};
struct snd_usb_midi_out_endpoint {
@@ -255,7 +263,7 @@ static void snd_usbmidi_in_urb_complete(struct urb* urb)
}
}
- urb->dev = ep->umidi->chip->dev;
+ urb->dev = ep->umidi->dev;
snd_usbmidi_submit_urb(urb, GFP_ATOMIC);
}
@@ -296,7 +304,7 @@ static void snd_usbmidi_do_output(struct snd_usb_midi_out_endpoint* ep)
unsigned long flags;
spin_lock_irqsave(&ep->buffer_lock, flags);
- if (ep->umidi->chip->shutdown) {
+ if (ep->umidi->disconnected) {
spin_unlock_irqrestore(&ep->buffer_lock, flags);
return;
}
@@ -312,7 +320,7 @@ static void snd_usbmidi_do_output(struct snd_usb_midi_out_endpoint* ep)
dump_urb("sending", urb->transfer_buffer,
urb->transfer_buffer_length);
- urb->dev = ep->umidi->chip->dev;
+ urb->dev = ep->umidi->dev;
if (snd_usbmidi_submit_urb(urb, GFP_ATOMIC) < 0)
break;
ep->active_urbs |= 1 << urb_index;
@@ -349,7 +357,7 @@ static void snd_usbmidi_error_timer(unsigned long data)
if (in && in->error_resubmit) {
in->error_resubmit = 0;
for (j = 0; j < INPUT_URBS; ++j) {
- in->urbs[j]->dev = umidi->chip->dev;
+ in->urbs[j]->dev = umidi->dev;
snd_usbmidi_submit_urb(in->urbs[j], GFP_ATOMIC);
}
}
@@ -369,7 +377,7 @@ static int send_bulk_static_data(struct snd_usb_midi_out_endpoint* ep,
return -ENOMEM;
dump_urb("sending", buf, len);
if (ep->urbs[0].urb)
- err = usb_bulk_msg(ep->umidi->chip->dev, ep->urbs[0].urb->pipe,
+ err = usb_bulk_msg(ep->umidi->dev, ep->urbs[0].urb->pipe,
buf, len, NULL, 250);
kfree(buf);
return err;
@@ -724,8 +732,7 @@ static void snd_usbmidi_us122l_output(struct snd_usb_midi_out_endpoint *ep,
if (!ep->ports[0].active)
return;
- count = snd_usb_get_speed(ep->umidi->chip->dev) == USB_SPEED_HIGH
- ? 1 : 2;
+ count = snd_usb_get_speed(ep->umidi->dev) == USB_SPEED_HIGH ? 1 : 2;
count = snd_rawmidi_transmit(ep->ports[0].substream,
urb->transfer_buffer,
count);
@@ -879,6 +886,50 @@ static struct usb_protocol_ops snd_usbmidi_emagic_ops = {
};
+static void update_roland_altsetting(struct snd_usb_midi* umidi)
+{
+ struct usb_interface *intf;
+ struct usb_host_interface *hostif;
+ struct usb_interface_descriptor *intfd;
+ int is_light_load;
+
+ intf = umidi->iface;
+ is_light_load = intf->cur_altsetting != intf->altsetting;
+ if (umidi->roland_load_ctl->private_value == is_light_load)
+ return;
+ hostif = &intf->altsetting[umidi->roland_load_ctl->private_value];
+ intfd = get_iface_desc(hostif);
+ snd_usbmidi_input_stop(&umidi->list);
+ usb_set_interface(umidi->dev, intfd->bInterfaceNumber,
+ intfd->bAlternateSetting);
+ snd_usbmidi_input_start(&umidi->list);
+}
+
+static void substream_open(struct snd_rawmidi_substream *substream, int open)
+{
+ struct snd_usb_midi* umidi = substream->rmidi->private_data;
+ struct snd_kcontrol *ctl;
+
+ mutex_lock(&umidi->mutex);
+ if (open) {
+ if (umidi->opened++ == 0 && umidi->roland_load_ctl) {
+ ctl = umidi->roland_load_ctl;
+ ctl->vd[0].access |= SNDRV_CTL_ELEM_ACCESS_INACTIVE;
+ snd_ctl_notify(umidi->card,
+ SNDRV_CTL_EVENT_MASK_INFO, &ctl->id);
+ update_roland_altsetting(umidi);
+ }
+ } else {
+ if (--umidi->opened == 0 && umidi->roland_load_ctl) {
+ ctl = umidi->roland_load_ctl;
+ ctl->vd[0].access &= ~SNDRV_CTL_ELEM_ACCESS_INACTIVE;
+ snd_ctl_notify(umidi->card,
+ SNDRV_CTL_EVENT_MASK_INFO, &ctl->id);
+ }
+ }
+ mutex_unlock(&umidi->mutex);
+}
+
static int snd_usbmidi_output_open(struct snd_rawmidi_substream *substream)
{
struct snd_usb_midi* umidi = substream->rmidi->private_data;
@@ -898,11 +949,13 @@ static int snd_usbmidi_output_open(struct snd_rawmidi_substream *substream)
}
substream->runtime->private_data = port;
port->state = STATE_UNKNOWN;
+ substream_open(substream, 1);
return 0;
}
static int snd_usbmidi_output_close(struct snd_rawmidi_substream *substream)
{
+ substream_open(substream, 0);
return 0;
}
@@ -912,7 +965,7 @@ static void snd_usbmidi_output_trigger(struct snd_rawmidi_substream *substream,
port->active = up;
if (up) {
- if (port->ep->umidi->chip->shutdown) {
+ if (port->ep->umidi->disconnected) {
/* gobble up remaining bytes to prevent wait in
* snd_rawmidi_drain_output */
while (!snd_rawmidi_transmit_empty(substream))
@@ -954,11 +1007,13 @@ static void snd_usbmidi_output_drain(struct snd_rawmidi_substream *substream)
static int snd_usbmidi_input_open(struct snd_rawmidi_substream *substream)
{
+ substream_open(substream, 1);
return 0;
}
static int snd_usbmidi_input_close(struct snd_rawmidi_substream *substream)
{
+ substream_open(substream, 0);
return 0;
}
@@ -988,7 +1043,7 @@ static struct snd_rawmidi_ops snd_usbmidi_input_ops = {
static void free_urb_and_buffer(struct snd_usb_midi *umidi, struct urb *urb,
unsigned int buffer_length)
{
- usb_buffer_free(umidi->chip->dev, buffer_length,
+ usb_buffer_free(umidi->dev, buffer_length,
urb->transfer_buffer, urb->transfer_dma);
usb_free_urb(urb);
}
@@ -1035,24 +1090,24 @@ static int snd_usbmidi_in_endpoint_create(struct snd_usb_midi* umidi,
}
}
if (ep_info->in_interval)
- pipe = usb_rcvintpipe(umidi->chip->dev, ep_info->in_ep);
+ pipe = usb_rcvintpipe(umidi->dev, ep_info->in_ep);
else
- pipe = usb_rcvbulkpipe(umidi->chip->dev, ep_info->in_ep);
- length = usb_maxpacket(umidi->chip->dev, pipe, 0);
+ pipe = usb_rcvbulkpipe(umidi->dev, ep_info->in_ep);
+ length = usb_maxpacket(umidi->dev, pipe, 0);
for (i = 0; i < INPUT_URBS; ++i) {
- buffer = usb_buffer_alloc(umidi->chip->dev, length, GFP_KERNEL,
+ buffer = usb_buffer_alloc(umidi->dev, length, GFP_KERNEL,
&ep->urbs[i]->transfer_dma);
if (!buffer) {
snd_usbmidi_in_endpoint_delete(ep);
return -ENOMEM;
}
if (ep_info->in_interval)
- usb_fill_int_urb(ep->urbs[i], umidi->chip->dev,
+ usb_fill_int_urb(ep->urbs[i], umidi->dev,
pipe, buffer, length,
snd_usbmidi_in_urb_complete,
ep, ep_info->in_interval);
else
- usb_fill_bulk_urb(ep->urbs[i], umidi->chip->dev,
+ usb_fill_bulk_urb(ep->urbs[i], umidi->dev,
pipe, buffer, length,
snd_usbmidi_in_urb_complete, ep);
ep->urbs[i]->transfer_flags = URB_NO_TRANSFER_DMA_MAP;
@@ -1062,15 +1117,6 @@ static int snd_usbmidi_in_endpoint_create(struct snd_usb_midi* umidi,
return 0;
}
-static unsigned int snd_usbmidi_count_bits(unsigned int x)
-{
- unsigned int bits;
-
- for (bits = 0; x; ++bits)
- x &= x - 1;
- return bits;
-}
-
/*
* Frees an output endpoint.
* May be called when ep hasn't been initialized completely.
@@ -1113,15 +1159,15 @@ static int snd_usbmidi_out_endpoint_create(struct snd_usb_midi* umidi,
ep->urbs[i].ep = ep;
}
if (ep_info->out_interval)
- pipe = usb_sndintpipe(umidi->chip->dev, ep_info->out_ep);
+ pipe = usb_sndintpipe(umidi->dev, ep_info->out_ep);
else
- pipe = usb_sndbulkpipe(umidi->chip->dev, ep_info->out_ep);
- if (umidi->chip->usb_id == USB_ID(0x0a92, 0x1020)) /* ESI M4U */
+ pipe = usb_sndbulkpipe(umidi->dev, ep_info->out_ep);
+ if (umidi->usb_id == USB_ID(0x0a92, 0x1020)) /* ESI M4U */
ep->max_transfer = 4;
else
- ep->max_transfer = usb_maxpacket(umidi->chip->dev, pipe, 1);
+ ep->max_transfer = usb_maxpacket(umidi->dev, pipe, 1);
for (i = 0; i < OUTPUT_URBS; ++i) {
- buffer = usb_buffer_alloc(umidi->chip->dev,
+ buffer = usb_buffer_alloc(umidi->dev,
ep->max_transfer, GFP_KERNEL,
&ep->urbs[i].urb->transfer_dma);
if (!buffer) {
@@ -1129,12 +1175,12 @@ static int snd_usbmidi_out_endpoint_create(struct snd_usb_midi* umidi,
return -ENOMEM;
}
if (ep_info->out_interval)
- usb_fill_int_urb(ep->urbs[i].urb, umidi->chip->dev,
+ usb_fill_int_urb(ep->urbs[i].urb, umidi->dev,
pipe, buffer, ep->max_transfer,
snd_usbmidi_out_urb_complete,
&ep->urbs[i], ep_info->out_interval);
else
- usb_fill_bulk_urb(ep->urbs[i].urb, umidi->chip->dev,
+ usb_fill_bulk_urb(ep->urbs[i].urb, umidi->dev,
pipe, buffer, ep->max_transfer,
snd_usbmidi_out_urb_complete,
&ep->urbs[i]);
@@ -1172,6 +1218,7 @@ static void snd_usbmidi_free(struct snd_usb_midi* umidi)
if (ep->in)
snd_usbmidi_in_endpoint_delete(ep->in);
}
+ mutex_destroy(&umidi->mutex);
kfree(umidi);
}
@@ -1367,7 +1414,7 @@ static struct port_info *find_port_info(struct snd_usb_midi* umidi, int number)
int i;
for (i = 0; i < ARRAY_SIZE(snd_usbmidi_port_info); ++i) {
- if (snd_usbmidi_port_info[i].id == umidi->chip->usb_id &&
+ if (snd_usbmidi_port_info[i].id == umidi->usb_id &&
snd_usbmidi_port_info[i].port == number)
return &snd_usbmidi_port_info[i];
}
@@ -1405,7 +1452,7 @@ static void snd_usbmidi_init_substream(struct snd_usb_midi* umidi,
port_info = find_port_info(umidi, number);
name_format = port_info ? port_info->name : "%s MIDI %d";
snprintf(substream->name, sizeof(substream->name),
- name_format, umidi->chip->card->shortname, number + 1);
+ name_format, umidi->card->shortname, number + 1);
*rsubstream = substream;
}
@@ -1503,7 +1550,7 @@ static int snd_usbmidi_get_ms_info(struct snd_usb_midi* umidi,
endpoints[epidx].out_ep = usb_endpoint_num(ep);
if (usb_endpoint_xfer_int(ep))
endpoints[epidx].out_interval = ep->bInterval;
- else if (snd_usb_get_speed(umidi->chip->dev) == USB_SPEED_LOW)
+ else if (snd_usb_get_speed(umidi->dev) == USB_SPEED_LOW)
/*
* Low speed bulk transfers don't exist, so
* force interrupt transfers for devices like
@@ -1523,7 +1570,7 @@ static int snd_usbmidi_get_ms_info(struct snd_usb_midi* umidi,
endpoints[epidx].in_ep = usb_endpoint_num(ep);
if (usb_endpoint_xfer_int(ep))
endpoints[epidx].in_interval = ep->bInterval;
- else if (snd_usb_get_speed(umidi->chip->dev) == USB_SPEED_LOW)
+ else if (snd_usb_get_speed(umidi->dev) == USB_SPEED_LOW)
endpoints[epidx].in_interval = 1;
endpoints[epidx].in_cables = (1 << ms_ep->bNumEmbMIDIJack) - 1;
snd_printdd(KERN_INFO "EP %02X: %d jack(s)\n",
@@ -1533,6 +1580,52 @@ static int snd_usbmidi_get_ms_info(struct snd_usb_midi* umidi,
return 0;
}
+static int roland_load_info(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_info *info)
+{
+ static const char *const names[] = { "High Load", "Light Load" };
+
+ info->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED;
+ info->count = 1;
+ info->value.enumerated.items = 2;
+ if (info->value.enumerated.item > 1)
+ info->value.enumerated.item = 1;
+ strcpy(info->value.enumerated.name, names[info->value.enumerated.item]);
+ return 0;
+}
+
+static int roland_load_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *value)
+{
+ value->value.enumerated.item[0] = kcontrol->private_value;
+ return 0;
+}
+
+static int roland_load_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *value)
+{
+ struct snd_usb_midi* umidi = kcontrol->private_data;
+ int changed;
+
+ if (value->value.enumerated.item[0] > 1)
+ return -EINVAL;
+ mutex_lock(&umidi->mutex);
+ changed = value->value.enumerated.item[0] != kcontrol->private_value;
+ if (changed)
+ kcontrol->private_value = value->value.enumerated.item[0];
+ mutex_unlock(&umidi->mutex);
+ return changed;
+}
+
+static struct snd_kcontrol_new roland_load_ctl = {
+ .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+ .name = "MIDI Input Mode",
+ .info = roland_load_info,
+ .get = roland_load_get,
+ .put = roland_load_put,
+ .private_value = 1,
+};
+
/*
* On Roland devices, use the second alternate setting to be able to use
* the interrupt input endpoint.
@@ -1556,8 +1649,12 @@ static void snd_usbmidi_switch_roland_altsetting(struct snd_usb_midi* umidi)
snd_printdd(KERN_INFO "switching to altsetting %d with int ep\n",
intfd->bAlternateSetting);
- usb_set_interface(umidi->chip->dev, intfd->bInterfaceNumber,
+ usb_set_interface(umidi->dev, intfd->bInterfaceNumber,
intfd->bAlternateSetting);
+
+ umidi->roland_load_ctl = snd_ctl_new1(&roland_load_ctl, umidi);
+ if (snd_ctl_add(umidi->card, umidi->roland_load_ctl) < 0)
+ umidi->roland_load_ctl = NULL;
}
/*
@@ -1573,7 +1670,7 @@ static int snd_usbmidi_detect_endpoints(struct snd_usb_midi* umidi,
struct usb_endpoint_descriptor* epd;
int i, out_eps = 0, in_eps = 0;
- if (USB_ID_VENDOR(umidi->chip->usb_id) == 0x0582)
+ if (USB_ID_VENDOR(umidi->usb_id) == 0x0582)
snd_usbmidi_switch_roland_altsetting(umidi);
if (endpoint[0].out_ep || endpoint[0].in_ep)
@@ -1760,12 +1857,12 @@ static int snd_usbmidi_create_rawmidi(struct snd_usb_midi* umidi,
struct snd_rawmidi *rmidi;
int err;
- err = snd_rawmidi_new(umidi->chip->card, "USB MIDI",
- umidi->chip->next_midi_device++,
+ err = snd_rawmidi_new(umidi->card, "USB MIDI",
+ umidi->next_midi_device++,
out_ports, in_ports, &rmidi);
if (err < 0)
return err;
- strcpy(rmidi->name, umidi->chip->card->shortname);
+ strcpy(rmidi->name, umidi->card->shortname);
rmidi->info_flags = SNDRV_RAWMIDI_INFO_OUTPUT |
SNDRV_RAWMIDI_INFO_INPUT |
SNDRV_RAWMIDI_INFO_DUPLEX;
@@ -1804,7 +1901,7 @@ static void snd_usbmidi_input_start_ep(struct snd_usb_midi_in_endpoint* ep)
return;
for (i = 0; i < INPUT_URBS; ++i) {
struct urb* urb = ep->urbs[i];
- urb->dev = ep->umidi->chip->dev;
+ urb->dev = ep->umidi->dev;
snd_usbmidi_submit_urb(urb, GFP_KERNEL);
}
}
@@ -1825,9 +1922,10 @@ void snd_usbmidi_input_start(struct list_head* p)
/*
* Creates and registers everything needed for a MIDI streaming interface.
*/
-int snd_usb_create_midi_interface(struct snd_usb_audio* chip,
- struct usb_interface* iface,
- const struct snd_usb_audio_quirk* quirk)
+int snd_usbmidi_create(struct snd_card *card,
+ struct usb_interface* iface,
+ struct list_head *midi_list,
+ const struct snd_usb_audio_quirk* quirk)
{
struct snd_usb_midi* umidi;
struct snd_usb_midi_endpoint_info endpoints[MIDI_MAX_ENDPOINTS];
@@ -1837,12 +1935,16 @@ int snd_usb_create_midi_interface(struct snd_usb_audio* chip,
umidi = kzalloc(sizeof(*umidi), GFP_KERNEL);
if (!umidi)
return -ENOMEM;
- umidi->chip = chip;
+ umidi->dev = interface_to_usbdev(iface);
+ umidi->card = card;
umidi->iface = iface;
umidi->quirk = quirk;
umidi->usb_protocol_ops = &snd_usbmidi_standard_ops;
init_timer(&umidi->error_timer);
spin_lock_init(&umidi->disc_lock);
+ mutex_init(&umidi->mutex);
+ umidi->usb_id = USB_ID(le16_to_cpu(umidi->dev->descriptor.idVendor),
+ le16_to_cpu(umidi->dev->descriptor.idProduct));
umidi->error_timer.function = snd_usbmidi_error_timer;
umidi->error_timer.data = (unsigned long)umidi;
@@ -1851,7 +1953,7 @@ int snd_usb_create_midi_interface(struct snd_usb_audio* chip,
switch (quirk ? quirk->type : QUIRK_MIDI_STANDARD_INTERFACE) {
case QUIRK_MIDI_STANDARD_INTERFACE:
err = snd_usbmidi_get_ms_info(umidi, endpoints);
- if (chip->usb_id == USB_ID(0x0763, 0x0150)) /* M-Audio Uno */
+ if (umidi->usb_id == USB_ID(0x0763, 0x0150)) /* M-Audio Uno */
umidi->usb_protocol_ops =
&snd_usbmidi_maudio_broken_running_status_ops;
break;
@@ -1887,7 +1989,7 @@ int snd_usb_create_midi_interface(struct snd_usb_audio* chip,
* interface 0, so we have to make sure that the USB core looks
* again at interface 0 by calling usb_set_interface() on it.
*/
- usb_set_interface(umidi->chip->dev, 0, 0);
+ usb_set_interface(umidi->dev, 0, 0);
err = snd_usbmidi_detect_per_port_endpoints(umidi, endpoints);
break;
case QUIRK_MIDI_EMAGIC:
@@ -1914,8 +2016,8 @@ int snd_usb_create_midi_interface(struct snd_usb_audio* chip,
out_ports = 0;
in_ports = 0;
for (i = 0; i < MIDI_MAX_ENDPOINTS; ++i) {
- out_ports += snd_usbmidi_count_bits(endpoints[i].out_cables);
- in_ports += snd_usbmidi_count_bits(endpoints[i].in_cables);
+ out_ports += hweight16(endpoints[i].out_cables);
+ in_ports += hweight16(endpoints[i].in_cables);
}
err = snd_usbmidi_create_rawmidi(umidi, out_ports, in_ports);
if (err < 0) {
@@ -1933,14 +2035,14 @@ int snd_usb_create_midi_interface(struct snd_usb_audio* chip,
return err;
}
- list_add(&umidi->list, &umidi->chip->midi_list);
+ list_add_tail(&umidi->list, midi_list);
for (i = 0; i < MIDI_MAX_ENDPOINTS; ++i)
snd_usbmidi_input_start_ep(umidi->endpoints[i].in);
return 0;
}
-EXPORT_SYMBOL(snd_usb_create_midi_interface);
+EXPORT_SYMBOL(snd_usbmidi_create);
EXPORT_SYMBOL(snd_usbmidi_input_stop);
EXPORT_SYMBOL(snd_usbmidi_input_start);
EXPORT_SYMBOL(snd_usbmidi_disconnect);
diff --git a/sound/usb/usbmixer_maps.c b/sound/usb/usbmixer_maps.c
index 3e5d66cf1f5a..77c35885e21c 100644
--- a/sound/usb/usbmixer_maps.c
+++ b/sound/usb/usbmixer_maps.c
@@ -277,6 +277,22 @@ static struct usbmix_name_map scratch_live_map[] = {
{ 0 } /* terminator */
};
+/* "Gamesurround Muse Pocket LT" looks same like "Sound Blaster MP3+"
+ * most importand difference is SU[8], it should be set to "Capture Source"
+ * to make alsamixer and PA working properly.
+ * FIXME: or mp3plus_map should use "Capture Source" too,
+ * so this maps can be merget
+ */
+static struct usbmix_name_map hercules_usb51_map[] = {
+ { 8, "Capture Source" }, /* SU, default "PCM Capture Source" */
+ { 9, "Master Playback" }, /* FU, default "Speaker Playback" */
+ { 10, "Mic Boost", 7 }, /* FU, default "Auto Gain Input" */
+ { 11, "Line Capture" }, /* FU, default "PCM Capture" */
+ { 13, "Mic Bypass Playback" }, /* FU, default "Mic Playback" */
+ { 14, "Line Bypass Playback" }, /* FU, default "Line Playback" */
+ { 0 } /* terminator */
+};
+
/*
* Control map entries
*/
@@ -316,6 +332,13 @@ static struct usbmix_ctl_map usbmix_ctl_maps[] = {
.ignore_ctl_error = 1,
},
{
+ /* Hercules Gamesurround Muse Pocket LT
+ * (USB 5.1 Channel Audio Adapter)
+ */
+ .id = USB_ID(0x06f8, 0xc000),
+ .map = hercules_usb51_map,
+ },
+ {
.id = USB_ID(0x08bb, 0x2702),
.map = linex_map,
.ignore_ctl_error = 1,
diff --git a/sound/usb/usbquirks.h b/sound/usb/usbquirks.h
index f6f201eb24ce..a892bda03df9 100644
--- a/sound/usb/usbquirks.h
+++ b/sound/usb/usbquirks.h
@@ -1563,6 +1563,29 @@ YAMAHA_DEVICE(0x7010, "UB99"),
}
}
},
+{
+ /* has ID 0x00ea when not in Advanced Driver mode */
+ USB_DEVICE_VENDOR_SPEC(0x0582, 0x00e9),
+ .driver_info = (unsigned long) & (const struct snd_usb_audio_quirk) {
+ /* .vendor_name = "Roland", */
+ /* .product_name = "UA-1G", */
+ .ifnum = QUIRK_ANY_INTERFACE,
+ .type = QUIRK_COMPOSITE,
+ .data = (const struct snd_usb_audio_quirk[]) {
+ {
+ .ifnum = 0,
+ .type = QUIRK_AUDIO_STANDARD_INTERFACE
+ },
+ {
+ .ifnum = 1,
+ .type = QUIRK_AUDIO_STANDARD_INTERFACE
+ },
+ {
+ .ifnum = -1
+ }
+ }
+ }
+},
/* Guillemot devices */
{
diff --git a/sound/usb/usx2y/us122l.c b/sound/usb/usx2y/us122l.c
index 99f33766cd51..f71cd28eca6b 100644
--- a/sound/usb/usx2y/us122l.c
+++ b/sound/usb/usx2y/us122l.c
@@ -59,11 +59,33 @@ static int us122l_create_usbmidi(struct snd_card *card)
.type = QUIRK_MIDI_US122L,
.data = &quirk_data
};
- struct usb_device *dev = US122L(card)->chip.dev;
+ struct usb_device *dev = US122L(card)->dev;
struct usb_interface *iface = usb_ifnum_to_if(dev, 1);
- return snd_usb_create_midi_interface(&US122L(card)->chip,
- iface, &quirk);
+ return snd_usbmidi_create(card, iface,
+ &US122L(card)->midi_list, &quirk);
+}
+
+static int us144_create_usbmidi(struct snd_card *card)
+{
+ static struct snd_usb_midi_endpoint_info quirk_data = {
+ .out_ep = 4,
+ .in_ep = 3,
+ .out_cables = 0x001,
+ .in_cables = 0x001
+ };
+ static struct snd_usb_audio_quirk quirk = {
+ .vendor_name = "US144",
+ .product_name = NAME_ALLCAPS,
+ .ifnum = 0,
+ .type = QUIRK_MIDI_US122L,
+ .data = &quirk_data
+ };
+ struct usb_device *dev = US122L(card)->dev;
+ struct usb_interface *iface = usb_ifnum_to_if(dev, 0);
+
+ return snd_usbmidi_create(card, iface,
+ &US122L(card)->midi_list, &quirk);
}
/*
@@ -171,7 +193,12 @@ static int usb_stream_hwdep_open(struct snd_hwdep *hw, struct file *file)
if (!us122l->first)
us122l->first = file;
- iface = usb_ifnum_to_if(us122l->chip.dev, 1);
+
+ if (us122l->dev->descriptor.idProduct == USB_ID_US144) {
+ iface = usb_ifnum_to_if(us122l->dev, 0);
+ usb_autopm_get_interface(iface);
+ }
+ iface = usb_ifnum_to_if(us122l->dev, 1);
usb_autopm_get_interface(iface);
return 0;
}
@@ -179,8 +206,14 @@ static int usb_stream_hwdep_open(struct snd_hwdep *hw, struct file *file)
static int usb_stream_hwdep_release(struct snd_hwdep *hw, struct file *file)
{
struct us122l *us122l = hw->private_data;
- struct usb_interface *iface = usb_ifnum_to_if(us122l->chip.dev, 1);
+ struct usb_interface *iface;
snd_printdd(KERN_DEBUG "%p %p\n", hw, file);
+
+ if (us122l->dev->descriptor.idProduct == USB_ID_US144) {
+ iface = usb_ifnum_to_if(us122l->dev, 0);
+ usb_autopm_put_interface(iface);
+ }
+ iface = usb_ifnum_to_if(us122l->dev, 1);
usb_autopm_put_interface(iface);
if (us122l->first == file)
us122l->first = NULL;
@@ -264,7 +297,7 @@ static unsigned int usb_stream_hwdep_poll(struct snd_hwdep *hw,
static void us122l_stop(struct us122l *us122l)
{
struct list_head *p;
- list_for_each(p, &us122l->chip.midi_list)
+ list_for_each(p, &us122l->midi_list)
snd_usbmidi_input_stop(p);
usb_stream_stop(&us122l->sk);
@@ -297,7 +330,7 @@ static bool us122l_start(struct us122l *us122l,
unsigned use_packsize = 0;
bool success = false;
- if (us122l->chip.dev->speed == USB_SPEED_HIGH) {
+ if (us122l->dev->speed == USB_SPEED_HIGH) {
/* The us-122l's descriptor defaults to iso max_packsize 78,
which isn't needed for samplerates <= 48000.
Lets save some memory:
@@ -314,11 +347,11 @@ static bool us122l_start(struct us122l *us122l,
break;
}
}
- if (!usb_stream_new(&us122l->sk, us122l->chip.dev, 1, 2,
+ if (!usb_stream_new(&us122l->sk, us122l->dev, 1, 2,
rate, use_packsize, period_frames, 6))
goto out;
- err = us122l_set_sample_rate(us122l->chip.dev, rate);
+ err = us122l_set_sample_rate(us122l->dev, rate);
if (err < 0) {
us122l_stop(us122l);
snd_printk(KERN_ERR "us122l_set_sample_rate error \n");
@@ -330,7 +363,7 @@ static bool us122l_start(struct us122l *us122l,
snd_printk(KERN_ERR "us122l_start error %i \n", err);
goto out;
}
- list_for_each(p, &us122l->chip.midi_list)
+ list_for_each(p, &us122l->midi_list)
snd_usbmidi_input_start(p);
success = true;
out:
@@ -357,7 +390,7 @@ static int usb_stream_hwdep_ioctl(struct snd_hwdep *hw, struct file *file,
err = -ENXIO;
goto free;
}
- high_speed = us122l->chip.dev->speed == USB_SPEED_HIGH;
+ high_speed = us122l->dev->speed == USB_SPEED_HIGH;
if ((cfg->sample_rate != 44100 && cfg->sample_rate != 48000 &&
(!high_speed ||
(cfg->sample_rate != 88200 && cfg->sample_rate != 96000))) ||
@@ -417,7 +450,7 @@ static int usb_stream_hwdep_new(struct snd_card *card)
{
int err;
struct snd_hwdep *hw;
- struct usb_device *dev = US122L(card)->chip.dev;
+ struct usb_device *dev = US122L(card)->dev;
err = snd_hwdep_new(card, SND_USB_STREAM_ID, 0, &hw);
if (err < 0)
@@ -443,19 +476,29 @@ static bool us122l_create_card(struct snd_card *card)
int err;
struct us122l *us122l = US122L(card);
- err = usb_set_interface(us122l->chip.dev, 1, 1);
+ if (us122l->dev->descriptor.idProduct == USB_ID_US144) {
+ err = usb_set_interface(us122l->dev, 0, 1);
+ if (err) {
+ snd_printk(KERN_ERR "usb_set_interface error \n");
+ return false;
+ }
+ }
+ err = usb_set_interface(us122l->dev, 1, 1);
if (err) {
snd_printk(KERN_ERR "usb_set_interface error \n");
return false;
}
- pt_info_set(us122l->chip.dev, 0x11);
- pt_info_set(us122l->chip.dev, 0x10);
+ pt_info_set(us122l->dev, 0x11);
+ pt_info_set(us122l->dev, 0x10);
if (!us122l_start(us122l, 44100, 256))
return false;
- err = us122l_create_usbmidi(card);
+ if (us122l->dev->descriptor.idProduct == USB_ID_US144)
+ err = us144_create_usbmidi(card);
+ else
+ err = us122l_create_usbmidi(card);
if (err < 0) {
snd_printk(KERN_ERR "us122l_create_usbmidi error %i \n", err);
us122l_stop(us122l);
@@ -465,7 +508,7 @@ static bool us122l_create_card(struct snd_card *card)
if (err < 0) {
/* release the midi resources */
struct list_head *p;
- list_for_each(p, &us122l->chip.midi_list)
+ list_for_each(p, &us122l->midi_list)
snd_usbmidi_disconnect(p);
us122l_stop(us122l);
@@ -477,7 +520,7 @@ static bool us122l_create_card(struct snd_card *card)
static void snd_us122l_free(struct snd_card *card)
{
struct us122l *us122l = US122L(card);
- int index = us122l->chip.index;
+ int index = us122l->card_index;
if (index >= 0 && index < SNDRV_CARDS)
snd_us122l_card_used[index] = 0;
}
@@ -497,13 +540,12 @@ static int usx2y_create_card(struct usb_device *device, struct snd_card **cardp)
sizeof(struct us122l), &card);
if (err < 0)
return err;
- snd_us122l_card_used[US122L(card)->chip.index = dev] = 1;
+ snd_us122l_card_used[US122L(card)->card_index = dev] = 1;
card->private_free = snd_us122l_free;
- US122L(card)->chip.dev = device;
- US122L(card)->chip.card = card;
+ US122L(card)->dev = device;
mutex_init(&US122L(card)->mutex);
init_waitqueue_head(&US122L(card)->sk.sleep);
- INIT_LIST_HEAD(&US122L(card)->chip.midi_list);
+ INIT_LIST_HEAD(&US122L(card)->midi_list);
strcpy(card->driver, "USB "NAME_ALLCAPS"");
sprintf(card->shortname, "TASCAM "NAME_ALLCAPS"");
sprintf(card->longname, "%s (%x:%x if %d at %03d/%03d)",
@@ -511,8 +553,8 @@ static int usx2y_create_card(struct usb_device *device, struct snd_card **cardp)
le16_to_cpu(device->descriptor.idVendor),
le16_to_cpu(device->descriptor.idProduct),
0,
- US122L(card)->chip.dev->bus->busnum,
- US122L(card)->chip.dev->devnum
+ US122L(card)->dev->bus->busnum,
+ US122L(card)->dev->devnum
);
*cardp = card;
return 0;
@@ -542,6 +584,7 @@ static int us122l_usb_probe(struct usb_interface *intf,
return err;
}
+ usb_get_intf(usb_ifnum_to_if(device, 0));
usb_get_dev(device);
*cardp = card;
return 0;
@@ -550,9 +593,16 @@ static int us122l_usb_probe(struct usb_interface *intf,
static int snd_us122l_probe(struct usb_interface *intf,
const struct usb_device_id *id)
{
+ struct usb_device *device = interface_to_usbdev(intf);
struct snd_card *card;
int err;
+ if (device->descriptor.idProduct == USB_ID_US144
+ && device->speed == USB_SPEED_HIGH) {
+ snd_printk(KERN_ERR "disable ehci-hcd to run US-144 \n");
+ return -ENODEV;
+ }
+
snd_printdd(KERN_DEBUG"%p:%i\n",
intf, intf->cur_altsetting->desc.bInterfaceNumber);
if (intf->cur_altsetting->desc.bInterfaceNumber != 1)
@@ -584,15 +634,15 @@ static void snd_us122l_disconnect(struct usb_interface *intf)
mutex_lock(&us122l->mutex);
us122l_stop(us122l);
mutex_unlock(&us122l->mutex);
- us122l->chip.shutdown = 1;
/* release the midi resources */
- list_for_each(p, &us122l->chip.midi_list) {
+ list_for_each(p, &us122l->midi_list) {
snd_usbmidi_disconnect(p);
}
- usb_put_intf(intf);
- usb_put_dev(us122l->chip.dev);
+ usb_put_intf(usb_ifnum_to_if(us122l->dev, 0));
+ usb_put_intf(usb_ifnum_to_if(us122l->dev, 1));
+ usb_put_dev(us122l->dev);
while (atomic_read(&us122l->mmap_count))
msleep(500);
@@ -615,7 +665,7 @@ static int snd_us122l_suspend(struct usb_interface *intf, pm_message_t message)
if (!us122l)
return 0;
- list_for_each(p, &us122l->chip.midi_list)
+ list_for_each(p, &us122l->midi_list)
snd_usbmidi_input_stop(p);
mutex_lock(&us122l->mutex);
@@ -642,16 +692,23 @@ static int snd_us122l_resume(struct usb_interface *intf)
mutex_lock(&us122l->mutex);
/* needed, doesn't restart without: */
- err = usb_set_interface(us122l->chip.dev, 1, 1);
+ if (us122l->dev->descriptor.idProduct == USB_ID_US144) {
+ err = usb_set_interface(us122l->dev, 0, 1);
+ if (err) {
+ snd_printk(KERN_ERR "usb_set_interface error \n");
+ goto unlock;
+ }
+ }
+ err = usb_set_interface(us122l->dev, 1, 1);
if (err) {
snd_printk(KERN_ERR "usb_set_interface error \n");
goto unlock;
}
- pt_info_set(us122l->chip.dev, 0x11);
- pt_info_set(us122l->chip.dev, 0x10);
+ pt_info_set(us122l->dev, 0x11);
+ pt_info_set(us122l->dev, 0x10);
- err = us122l_set_sample_rate(us122l->chip.dev,
+ err = us122l_set_sample_rate(us122l->dev,
us122l->sk.s->cfg.sample_rate);
if (err < 0) {
snd_printk(KERN_ERR "us122l_set_sample_rate error \n");
@@ -661,7 +718,7 @@ static int snd_us122l_resume(struct usb_interface *intf)
if (err)
goto unlock;
- list_for_each(p, &us122l->chip.midi_list)
+ list_for_each(p, &us122l->midi_list)
snd_usbmidi_input_start(p);
unlock:
mutex_unlock(&us122l->mutex);
@@ -675,11 +732,11 @@ static struct usb_device_id snd_us122l_usb_id_table[] = {
.idVendor = 0x0644,
.idProduct = USB_ID_US122L
},
-/* { */ /* US-144 maybe works when @USB1.1. Untested. */
-/* .match_flags = USB_DEVICE_ID_MATCH_DEVICE, */
-/* .idVendor = 0x0644, */
-/* .idProduct = USB_ID_US144 */
-/* }, */
+ { /* US-144 only works at USB1.1! Disable module ehci-hcd. */
+ .match_flags = USB_DEVICE_ID_MATCH_DEVICE,
+ .idVendor = 0x0644,
+ .idProduct = USB_ID_US144
+ },
{ /* terminator */ }
};
diff --git a/sound/usb/usx2y/us122l.h b/sound/usb/usx2y/us122l.h
index 3d10c4b2a0f5..4daf1982e821 100644
--- a/sound/usb/usx2y/us122l.h
+++ b/sound/usb/usx2y/us122l.h
@@ -3,7 +3,8 @@
struct us122l {
- struct snd_usb_audio chip;
+ struct usb_device *dev;
+ int card_index;
int stride;
struct usb_stream_kernel sk;
@@ -12,6 +13,7 @@ struct us122l {
unsigned second_periods_polled;
struct file *master;
struct file *slave;
+ struct list_head midi_list;
atomic_t mmap_count;
};
diff --git a/sound/usb/usx2y/usX2Yhwdep.c b/sound/usb/usx2y/usX2Yhwdep.c
index 52e04b2f35d3..1879b72c40f8 100644
--- a/sound/usb/usx2y/usX2Yhwdep.c
+++ b/sound/usb/usx2y/usX2Yhwdep.c
@@ -114,7 +114,7 @@ static int snd_usX2Y_hwdep_dsp_status(struct snd_hwdep *hw,
struct usX2Ydev *us428 = hw->private_data;
int id = -1;
- switch (le16_to_cpu(us428->chip.dev->descriptor.idProduct)) {
+ switch (le16_to_cpu(us428->dev->descriptor.idProduct)) {
case USB_ID_US122:
id = USX2Y_TYPE_122;
break;
@@ -164,14 +164,14 @@ static int usX2Y_create_usbmidi(struct snd_card *card)
.type = QUIRK_MIDI_FIXED_ENDPOINT,
.data = &quirk_data_2
};
- struct usb_device *dev = usX2Y(card)->chip.dev;
+ struct usb_device *dev = usX2Y(card)->dev;
struct usb_interface *iface = usb_ifnum_to_if(dev, 0);
struct snd_usb_audio_quirk *quirk =
le16_to_cpu(dev->descriptor.idProduct) == USB_ID_US428 ?
&quirk_2 : &quirk_1;
snd_printdd("usX2Y_create_usbmidi \n");
- return snd_usb_create_midi_interface(&usX2Y(card)->chip, iface, quirk);
+ return snd_usbmidi_create(card, iface, &usX2Y(card)->midi_list, quirk);
}
static int usX2Y_create_alsa_devices(struct snd_card *card)
@@ -202,7 +202,7 @@ static int snd_usX2Y_hwdep_dsp_load(struct snd_hwdep *hw,
snd_printdd( "dsp_load %s\n", dsp->name);
if (access_ok(VERIFY_READ, dsp->image, dsp->length)) {
- struct usb_device* dev = priv->chip.dev;
+ struct usb_device* dev = priv->dev;
char *buf;
buf = memdup_user(dsp->image, dsp->length);
diff --git a/sound/usb/usx2y/usbusx2y.c b/sound/usb/usx2y/usbusx2y.c
index cb4bb8373ca2..c42350eed2eb 100644
--- a/sound/usb/usx2y/usbusx2y.c
+++ b/sound/usb/usx2y/usbusx2y.c
@@ -239,8 +239,8 @@ static void i_usX2Y_In04Int(struct urb *urb)
for (j = 0; j < URBS_AsyncSeq && !err; ++j)
if (0 == usX2Y->AS04.urb[j]->status) {
struct us428_p4out *p4out = us428ctls->p4out + send; // FIXME if more than 1 p4out is new, 1 gets lost.
- usb_fill_bulk_urb(usX2Y->AS04.urb[j], usX2Y->chip.dev,
- usb_sndbulkpipe(usX2Y->chip.dev, 0x04), &p4out->val.vol,
+ usb_fill_bulk_urb(usX2Y->AS04.urb[j], usX2Y->dev,
+ usb_sndbulkpipe(usX2Y->dev, 0x04), &p4out->val.vol,
p4out->type == eLT_Light ? sizeof(struct us428_lights) : 5,
i_usX2Y_Out04Int, usX2Y);
err = usb_submit_urb(usX2Y->AS04.urb[j], GFP_ATOMIC);
@@ -253,7 +253,7 @@ static void i_usX2Y_In04Int(struct urb *urb)
if (err)
snd_printk(KERN_ERR "In04Int() usb_submit_urb err=%i\n", err);
- urb->dev = usX2Y->chip.dev;
+ urb->dev = usX2Y->dev;
usb_submit_urb(urb, GFP_ATOMIC);
}
@@ -273,8 +273,8 @@ int usX2Y_AsyncSeq04_init(struct usX2Ydev *usX2Y)
err = -ENOMEM;
break;
}
- usb_fill_bulk_urb( usX2Y->AS04.urb[i], usX2Y->chip.dev,
- usb_sndbulkpipe(usX2Y->chip.dev, 0x04),
+ usb_fill_bulk_urb( usX2Y->AS04.urb[i], usX2Y->dev,
+ usb_sndbulkpipe(usX2Y->dev, 0x04),
usX2Y->AS04.buffer + URB_DataLen_AsyncSeq*i, 0,
i_usX2Y_Out04Int, usX2Y
);
@@ -293,7 +293,7 @@ int usX2Y_In04_init(struct usX2Ydev *usX2Y)
}
init_waitqueue_head(&usX2Y->In04WaitQueue);
- usb_fill_int_urb(usX2Y->In04urb, usX2Y->chip.dev, usb_rcvintpipe(usX2Y->chip.dev, 0x4),
+ usb_fill_int_urb(usX2Y->In04urb, usX2Y->dev, usb_rcvintpipe(usX2Y->dev, 0x4),
usX2Y->In04Buf, 21,
i_usX2Y_In04Int, usX2Y,
10);
@@ -348,13 +348,12 @@ static int usX2Y_create_card(struct usb_device *device, struct snd_card **cardp)
sizeof(struct usX2Ydev), &card);
if (err < 0)
return err;
- snd_usX2Y_card_used[usX2Y(card)->chip.index = dev] = 1;
+ snd_usX2Y_card_used[usX2Y(card)->card_index = dev] = 1;
card->private_free = snd_usX2Y_card_private_free;
- usX2Y(card)->chip.dev = device;
- usX2Y(card)->chip.card = card;
+ usX2Y(card)->dev = device;
init_waitqueue_head(&usX2Y(card)->prepare_wait_queue);
mutex_init(&usX2Y(card)->prepare_mutex);
- INIT_LIST_HEAD(&usX2Y(card)->chip.midi_list);
+ INIT_LIST_HEAD(&usX2Y(card)->midi_list);
strcpy(card->driver, "USB "NAME_ALLCAPS"");
sprintf(card->shortname, "TASCAM "NAME_ALLCAPS"");
sprintf(card->longname, "%s (%x:%x if %d at %03d/%03d)",
@@ -362,7 +361,7 @@ static int usX2Y_create_card(struct usb_device *device, struct snd_card **cardp)
le16_to_cpu(device->descriptor.idVendor),
le16_to_cpu(device->descriptor.idProduct),
0,//us428(card)->usbmidi.ifnum,
- usX2Y(card)->chip.dev->bus->busnum, usX2Y(card)->chip.dev->devnum
+ usX2Y(card)->dev->bus->busnum, usX2Y(card)->dev->devnum
);
*cardp = card;
return 0;
@@ -432,8 +431,8 @@ static void snd_usX2Y_card_private_free(struct snd_card *card)
usb_free_urb(usX2Y(card)->In04urb);
if (usX2Y(card)->us428ctls_sharedmem)
snd_free_pages(usX2Y(card)->us428ctls_sharedmem, sizeof(*usX2Y(card)->us428ctls_sharedmem));
- if (usX2Y(card)->chip.index >= 0 && usX2Y(card)->chip.index < SNDRV_CARDS)
- snd_usX2Y_card_used[usX2Y(card)->chip.index] = 0;
+ if (usX2Y(card)->card_index >= 0 && usX2Y(card)->card_index < SNDRV_CARDS)
+ snd_usX2Y_card_used[usX2Y(card)->card_index] = 0;
}
/*
@@ -445,13 +444,12 @@ static void usX2Y_usb_disconnect(struct usb_device *device, void* ptr)
struct snd_card *card = ptr;
struct usX2Ydev *usX2Y = usX2Y(card);
struct list_head *p;
- usX2Y->chip.shutdown = 1;
usX2Y->chip_status = USX2Y_STAT_CHIP_HUP;
usX2Y_unlinkSeq(&usX2Y->AS04);
usb_kill_urb(usX2Y->In04urb);
snd_card_disconnect(card);
/* release the midi resources */
- list_for_each(p, &usX2Y->chip.midi_list) {
+ list_for_each(p, &usX2Y->midi_list) {
snd_usbmidi_disconnect(p);
}
if (usX2Y->us428ctls_sharedmem)
diff --git a/sound/usb/usx2y/usbusx2y.h b/sound/usb/usx2y/usbusx2y.h
index 456b5fdbc339..1d174cea352b 100644
--- a/sound/usb/usx2y/usbusx2y.h
+++ b/sound/usb/usx2y/usbusx2y.h
@@ -22,7 +22,8 @@ struct snd_usX2Y_urbSeq {
#include "usx2yhwdeppcm.h"
struct usX2Ydev {
- struct snd_usb_audio chip;
+ struct usb_device *dev;
+ int card_index;
int stride;
struct urb *In04urb;
void *In04Buf;
@@ -42,6 +43,9 @@ struct usX2Ydev {
struct snd_usX2Y_substream *subs[4];
struct snd_usX2Y_substream * volatile prepare_subs;
wait_queue_head_t prepare_wait_queue;
+ struct list_head midi_list;
+ struct list_head pcm_list;
+ int pcm_devs;
};
diff --git a/sound/usb/usx2y/usbusx2yaudio.c b/sound/usb/usx2y/usbusx2yaudio.c
index 9efd27f6b52f..74a67a85aa81 100644
--- a/sound/usb/usx2y/usbusx2yaudio.c
+++ b/sound/usb/usx2y/usbusx2yaudio.c
@@ -199,7 +199,7 @@ static int usX2Y_urb_submit(struct snd_usX2Y_substream *subs, struct urb *urb, i
return -ENODEV;
urb->start_frame = (frame + NRURBS * nr_of_packs()); // let hcd do rollover sanity checks
urb->hcpriv = NULL;
- urb->dev = subs->usX2Y->chip.dev; /* we need to set this at each time */
+ urb->dev = subs->usX2Y->dev; /* we need to set this at each time */
if ((err = usb_submit_urb(urb, GFP_ATOMIC)) < 0) {
snd_printk(KERN_ERR "usb_submit_urb() returned %i\n", err);
return err;
@@ -300,7 +300,7 @@ static void usX2Y_error_sequence(struct usX2Ydev *usX2Y,
"Sequence Error!(hcd_frame=%i ep=%i%s;wait=%i,frame=%i).\n"
"Most propably some urb of usb-frame %i is still missing.\n"
"Cause could be too long delays in usb-hcd interrupt handling.\n",
- usb_get_current_frame_number(usX2Y->chip.dev),
+ usb_get_current_frame_number(usX2Y->dev),
subs->endpoint, usb_pipein(urb->pipe) ? "in" : "out",
usX2Y->wait_iso_frame, urb->start_frame, usX2Y->wait_iso_frame);
usX2Y_clients_stop(usX2Y);
@@ -313,7 +313,7 @@ static void i_usX2Y_urb_complete(struct urb *urb)
if (unlikely(atomic_read(&subs->state) < state_PREPARED)) {
snd_printdd("hcd_frame=%i ep=%i%s status=%i start_frame=%i\n",
- usb_get_current_frame_number(usX2Y->chip.dev),
+ usb_get_current_frame_number(usX2Y->dev),
subs->endpoint, usb_pipein(urb->pipe) ? "in" : "out",
urb->status, urb->start_frame);
return;
@@ -424,7 +424,7 @@ static int usX2Y_urbs_allocate(struct snd_usX2Y_substream *subs)
int i;
unsigned int pipe;
int is_playback = subs == subs->usX2Y->subs[SNDRV_PCM_STREAM_PLAYBACK];
- struct usb_device *dev = subs->usX2Y->chip.dev;
+ struct usb_device *dev = subs->usX2Y->dev;
pipe = is_playback ? usb_sndisocpipe(dev, subs->endpoint) :
usb_rcvisocpipe(dev, subs->endpoint);
@@ -500,7 +500,7 @@ static int usX2Y_urbs_start(struct snd_usX2Y_substream *subs)
unsigned long pack;
if (0 == i)
atomic_set(&subs->state, state_STARTING3);
- urb->dev = usX2Y->chip.dev;
+ urb->dev = usX2Y->dev;
urb->transfer_flags = URB_ISO_ASAP;
for (pack = 0; pack < nr_of_packs(); pack++) {
urb->iso_frame_desc[pack].offset = subs->maxpacksize * pack;
@@ -692,7 +692,7 @@ static int usX2Y_rate_set(struct usX2Ydev *usX2Y, int rate)
}
((char*)(usbdata + i))[0] = ra[i].c1;
((char*)(usbdata + i))[1] = ra[i].c2;
- usb_fill_bulk_urb(us->urb[i], usX2Y->chip.dev, usb_sndbulkpipe(usX2Y->chip.dev, 4),
+ usb_fill_bulk_urb(us->urb[i], usX2Y->dev, usb_sndbulkpipe(usX2Y->dev, 4),
usbdata + i, 2, i_usX2Y_04Int, usX2Y);
#ifdef OLD_USB
us->urb[i]->transfer_flags = USB_QUEUE_BULK;
@@ -740,17 +740,17 @@ static int usX2Y_format_set(struct usX2Ydev *usX2Y, snd_pcm_format_t format)
alternate = 1;
usX2Y->stride = 4;
}
- list_for_each(p, &usX2Y->chip.midi_list) {
+ list_for_each(p, &usX2Y->midi_list) {
snd_usbmidi_input_stop(p);
}
usb_kill_urb(usX2Y->In04urb);
- if ((err = usb_set_interface(usX2Y->chip.dev, 0, alternate))) {
+ if ((err = usb_set_interface(usX2Y->dev, 0, alternate))) {
snd_printk(KERN_ERR "usb_set_interface error \n");
return err;
}
- usX2Y->In04urb->dev = usX2Y->chip.dev;
+ usX2Y->In04urb->dev = usX2Y->dev;
err = usb_submit_urb(usX2Y->In04urb, GFP_KERNEL);
- list_for_each(p, &usX2Y->chip.midi_list) {
+ list_for_each(p, &usX2Y->midi_list) {
snd_usbmidi_input_start(p);
}
usX2Y->format = format;
@@ -955,7 +955,7 @@ static int usX2Y_audio_stream_new(struct snd_card *card, int playback_endpoint,
struct snd_pcm *pcm;
int err, i;
struct snd_usX2Y_substream **usX2Y_substream =
- usX2Y(card)->subs + 2 * usX2Y(card)->chip.pcm_devs;
+ usX2Y(card)->subs + 2 * usX2Y(card)->pcm_devs;
for (i = playback_endpoint ? SNDRV_PCM_STREAM_PLAYBACK : SNDRV_PCM_STREAM_CAPTURE;
i <= SNDRV_PCM_STREAM_CAPTURE; ++i) {
@@ -971,7 +971,7 @@ static int usX2Y_audio_stream_new(struct snd_card *card, int playback_endpoint,
usX2Y_substream[SNDRV_PCM_STREAM_PLAYBACK]->endpoint = playback_endpoint;
usX2Y_substream[SNDRV_PCM_STREAM_CAPTURE]->endpoint = capture_endpoint;
- err = snd_pcm_new(card, NAME_ALLCAPS" Audio", usX2Y(card)->chip.pcm_devs,
+ err = snd_pcm_new(card, NAME_ALLCAPS" Audio", usX2Y(card)->pcm_devs,
playback_endpoint ? 1 : 0, 1,
&pcm);
if (err < 0) {
@@ -987,7 +987,7 @@ static int usX2Y_audio_stream_new(struct snd_card *card, int playback_endpoint,
pcm->private_free = snd_usX2Y_pcm_private_free;
pcm->info_flags = 0;
- sprintf(pcm->name, NAME_ALLCAPS" Audio #%d", usX2Y(card)->chip.pcm_devs);
+ sprintf(pcm->name, NAME_ALLCAPS" Audio #%d", usX2Y(card)->pcm_devs);
if ((playback_endpoint &&
0 > (err = snd_pcm_lib_preallocate_pages(pcm->streams[SNDRV_PCM_STREAM_PLAYBACK].substream,
@@ -1001,7 +1001,7 @@ static int usX2Y_audio_stream_new(struct snd_card *card, int playback_endpoint,
snd_usX2Y_pcm_private_free(pcm);
return err;
}
- usX2Y(card)->chip.pcm_devs++;
+ usX2Y(card)->pcm_devs++;
return 0;
}
@@ -1013,14 +1013,14 @@ int usX2Y_audio_create(struct snd_card *card)
{
int err = 0;
- INIT_LIST_HEAD(&usX2Y(card)->chip.pcm_list);
+ INIT_LIST_HEAD(&usX2Y(card)->pcm_list);
if (0 > (err = usX2Y_audio_stream_new(card, 0xA, 0x8)))
return err;
- if (le16_to_cpu(usX2Y(card)->chip.dev->descriptor.idProduct) == USB_ID_US428)
+ if (le16_to_cpu(usX2Y(card)->dev->descriptor.idProduct) == USB_ID_US428)
if (0 > (err = usX2Y_audio_stream_new(card, 0, 0xA)))
return err;
- if (le16_to_cpu(usX2Y(card)->chip.dev->descriptor.idProduct) != USB_ID_US122)
+ if (le16_to_cpu(usX2Y(card)->dev->descriptor.idProduct) != USB_ID_US122)
err = usX2Y_rate_set(usX2Y(card), 44100); // Lets us428 recognize output-volume settings, disturbs us122.
return err;
}
diff --git a/sound/usb/usx2y/usx2yhwdeppcm.c b/sound/usb/usx2y/usx2yhwdeppcm.c
index 4b2304c2e02d..9ed6c3956ca7 100644
--- a/sound/usb/usx2y/usx2yhwdeppcm.c
+++ b/sound/usb/usx2y/usx2yhwdeppcm.c
@@ -234,7 +234,7 @@ static void i_usX2Y_usbpcm_urb_complete(struct urb *urb)
if (unlikely(atomic_read(&subs->state) < state_PREPARED)) {
snd_printdd("hcd_frame=%i ep=%i%s status=%i start_frame=%i\n",
- usb_get_current_frame_number(usX2Y->chip.dev),
+ usb_get_current_frame_number(usX2Y->dev),
subs->endpoint, usb_pipein(urb->pipe) ? "in" : "out",
urb->status, urb->start_frame);
return;
@@ -318,7 +318,7 @@ static int usX2Y_usbpcm_urbs_allocate(struct snd_usX2Y_substream *subs)
int i;
unsigned int pipe;
int is_playback = subs == subs->usX2Y->subs[SNDRV_PCM_STREAM_PLAYBACK];
- struct usb_device *dev = subs->usX2Y->chip.dev;
+ struct usb_device *dev = subs->usX2Y->dev;
pipe = is_playback ? usb_sndisocpipe(dev, subs->endpoint) :
usb_rcvisocpipe(dev, subs->endpoint);
@@ -441,7 +441,7 @@ static int usX2Y_usbpcm_urbs_start(struct snd_usX2Y_substream *subs)
unsigned long pack;
if (0 == u)
atomic_set(&subs->state, state_STARTING3);
- urb->dev = usX2Y->chip.dev;
+ urb->dev = usX2Y->dev;
urb->transfer_flags = URB_ISO_ASAP;
for (pack = 0; pack < nr_of_packs(); pack++) {
urb->iso_frame_desc[pack].offset = subs->maxpacksize * (pack + u * nr_of_packs());
@@ -741,7 +741,7 @@ int usX2Y_hwdep_pcm_new(struct snd_card *card)
int err;
struct snd_hwdep *hw;
struct snd_pcm *pcm;
- struct usb_device *dev = usX2Y(card)->chip.dev;
+ struct usb_device *dev = usX2Y(card)->dev;
if (1 != nr_of_packs())
return 0;