Merge branch 'linus' of git://git.kernel.org/pub/scm/linux/kernel/git/perex/alsa
authorLinus Torvalds <torvalds@linux-foundation.org>
Thu, 31 Jan 2008 23:16:28 +0000 (10:16 +1100)
committerLinus Torvalds <torvalds@linux-foundation.org>
Thu, 31 Jan 2008 23:16:28 +0000 (10:16 +1100)
* 'linus' of git://git.kernel.org/pub/scm/linux/kernel/git/perex/alsa: (299 commits)
  [ALSA] version 1.0.16rc2
  [ALSA] hda: fix Mic in as output
  [ALSA] emu10k1 - Another EMU0404 Board ID
  [ALSA] emu10k1 - Fix kthread handling at resume
  [ALSA] emu10k1: General cleanup, add new locks, fix alsa bug#3501, kernel bug#9304.
  [ALSA] emu10k1 - Use enum for emu_model types
  [ALSA] emu10k1 - Don't create emu1010 controls for non-emu boards
  [ALSA] emu10k1 - 1616(M) cardbus improvements
  [ALSA] snd:emu10k1: E-Mu updates. Fixes to firmware loading and support for 0404.
  [ALSA] emu10k1: Add comments regarding E-Mu ins and outs.
  [ALSA] oxygen: revert SPI clock frequency change for AK4396/WM8785
  [ALSA] es1938 - improve capture hw pointer reads
  [ALSA] HDA-Intel - Add support for Intel SCH
  [ALSA] hda: Add GPIO mute support to STAC9205
  [ALSA] hda-codec - Add Dell T3400 support
  [ALSA] hda-codec - Add model for HP DV9553EG laptop
  [ALSA] hda-codec - Control SPDIF as slave
  [ALSA] hda_intel: ALSA HD Audio patch for Intel ICH10 DeviceID's
  [ALSA] Fix Oops with PCM OSS sync
  [ALSA] hda-codec - Add speaker automute to ALC262 HP models
  ...

460 files changed:
Documentation/sound/alsa/ALSA-Configuration.txt
Documentation/sound/alsa/DocBook/writing-an-alsa-driver.tmpl
Documentation/sound/alsa/soc/DAI.txt
Documentation/sound/alsa/soc/clocking.txt
Documentation/sound/alsa/soc/codec.txt
Documentation/sound/alsa/soc/dapm.txt
Documentation/sound/alsa/soc/machine.txt
Documentation/sound/alsa/soc/overview.txt
Documentation/sound/alsa/soc/platform.txt
Documentation/sound/alsa/soc/pops_clicks.txt
MAINTAINERS
drivers/input/touchscreen/ucb1400_ts.c
drivers/media/video/cx88/cx88-alsa.c
drivers/media/video/saa7134/saa7134-alsa.c
drivers/media/video/saa7134/saa7134.h
drivers/usb/gadget/gmidi.c
include/asm-arm/arch-omap/eac.h
include/asm-arm/arch-omap/omap-alsa.h
include/asm-arm/arch-pxa/audio.h
include/sound/ad1848.h
include/sound/ainstr_fm.h [deleted file]
include/sound/ainstr_gf1.h [deleted file]
include/sound/ainstr_iw.h [deleted file]
include/sound/ainstr_simple.h [deleted file]
include/sound/ak4xxx-adda.h
include/sound/asequencer.h
include/sound/asound.h
include/sound/asound_fm.h
include/sound/core.h
include/sound/cs4231-regs.h
include/sound/cs46xx.h
include/sound/driver.h
include/sound/emu10k1.h
include/sound/gus.h
include/sound/info.h
include/sound/opl3.h
include/sound/pcm.h
include/sound/seq_instr.h [deleted file]
include/sound/soc-dapm.h
include/sound/soc.h
include/sound/tea575x-tuner.h
include/sound/trident.h
include/sound/version.h
sound/aoa/aoa.h
sound/aoa/codecs/snd-aoa-codec-onyx.c
sound/aoa/codecs/snd-aoa-codec-tas.c
sound/aoa/fabrics/snd-aoa-fabric-layout.c
sound/aoa/soundbus/i2sbus/i2sbus-core.c
sound/aoa/soundbus/i2sbus/i2sbus-pcm.c
sound/arm/aaci.c
sound/arm/devdma.c
sound/arm/pxa2xx-ac97.c
sound/arm/pxa2xx-pcm.c
sound/arm/sa11xx-uda1341.c
sound/core/control.c
sound/core/control_compat.c
sound/core/device.c
sound/core/hwdep.c
sound/core/info.c
sound/core/info_oss.c
sound/core/init.c
sound/core/isadma.c
sound/core/memalloc.c
sound/core/memory.c
sound/core/misc.c
sound/core/oss/copy.c
sound/core/oss/io.c
sound/core/oss/linear.c
sound/core/oss/mixer_oss.c
sound/core/oss/mulaw.c
sound/core/oss/pcm_oss.c
sound/core/oss/pcm_plugin.c
sound/core/oss/rate.c
sound/core/oss/route.c
sound/core/pcm.c
sound/core/pcm_compat.c
sound/core/pcm_lib.c
sound/core/pcm_memory.c
sound/core/pcm_misc.c
sound/core/pcm_native.c
sound/core/pcm_timer.c
sound/core/rawmidi.c
sound/core/rtctimer.c
sound/core/seq/Makefile
sound/core/seq/instr/Makefile [deleted file]
sound/core/seq/instr/ainstr_fm.c [deleted file]
sound/core/seq/instr/ainstr_gf1.c [deleted file]
sound/core/seq/instr/ainstr_iw.c [deleted file]
sound/core/seq/instr/ainstr_simple.c [deleted file]
sound/core/seq/oss/seq_oss.c
sound/core/seq/oss/seq_oss_device.h
sound/core/seq/seq.c
sound/core/seq/seq_clientmgr.c
sound/core/seq/seq_clientmgr.h
sound/core/seq/seq_device.c
sound/core/seq/seq_dummy.c
sound/core/seq/seq_fifo.c
sound/core/seq/seq_info.c
sound/core/seq/seq_instr.c [deleted file]
sound/core/seq/seq_lock.c
sound/core/seq/seq_memory.c
sound/core/seq/seq_midi.c
sound/core/seq/seq_midi_emul.c
sound/core/seq/seq_midi_event.c
sound/core/seq/seq_ports.c
sound/core/seq/seq_prioq.c
sound/core/seq/seq_queue.c
sound/core/seq/seq_system.c
sound/core/seq/seq_timer.c
sound/core/seq/seq_timer.h
sound/core/seq/seq_virmidi.c
sound/core/sound.c
sound/core/sound_oss.c
sound/core/timer.c
sound/drivers/Kconfig
sound/drivers/Makefile
sound/drivers/dummy.c
sound/drivers/ml403-ac97cr.c [new file with mode: 0644]
sound/drivers/mpu401/mpu401.c
sound/drivers/mpu401/mpu401_uart.c
sound/drivers/mtpav.c
sound/drivers/mts64.c
sound/drivers/opl3/opl3_lib.c
sound/drivers/opl3/opl3_midi.c
sound/drivers/opl3/opl3_oss.c
sound/drivers/opl3/opl3_seq.c
sound/drivers/opl3/opl3_synth.c
sound/drivers/pcm-indirect2.c [new file with mode: 0644]
sound/drivers/pcm-indirect2.h [new file with mode: 0644]
sound/drivers/portman2x4.c
sound/drivers/serial-u16550.c
sound/drivers/virmidi.c
sound/drivers/vx/vx_cmd.c
sound/drivers/vx/vx_core.c
sound/drivers/vx/vx_hwdep.c
sound/drivers/vx/vx_mixer.c
sound/drivers/vx/vx_pcm.c
sound/drivers/vx/vx_uer.c
sound/i2c/cs8427.c
sound/i2c/i2c.c
sound/i2c/l3/uda1341.c
sound/i2c/other/ak4114.c
sound/i2c/other/ak4117.c
sound/i2c/other/ak4xxx-adda.c
sound/i2c/other/pt2258.c
sound/i2c/other/tea575x-tuner.c
sound/i2c/tea6330t.c
sound/isa/ad1816a/ad1816a.c
sound/isa/ad1816a/ad1816a_lib.c
sound/isa/ad1848/ad1848.c
sound/isa/ad1848/ad1848_lib.c
sound/isa/adlib.c
sound/isa/als100.c
sound/isa/azt2320.c
sound/isa/cmi8330.c
sound/isa/cs423x/cs4231.c
sound/isa/cs423x/cs4231_lib.c
sound/isa/cs423x/cs4236.c
sound/isa/cs423x/cs4236_lib.c
sound/isa/dt019x.c
sound/isa/es1688/es1688.c
sound/isa/es1688/es1688_lib.c
sound/isa/es18xx.c
sound/isa/gus/Makefile
sound/isa/gus/gus_dma.c
sound/isa/gus/gus_dram.c
sound/isa/gus/gus_instr.c
sound/isa/gus/gus_io.c
sound/isa/gus/gus_irq.c
sound/isa/gus/gus_main.c
sound/isa/gus/gus_mem.c
sound/isa/gus/gus_mem_proc.c
sound/isa/gus/gus_mixer.c
sound/isa/gus/gus_pcm.c
sound/isa/gus/gus_reset.c
sound/isa/gus/gus_sample.c [deleted file]
sound/isa/gus/gus_simple.c [deleted file]
sound/isa/gus/gus_synth.c [deleted file]
sound/isa/gus/gus_timer.c
sound/isa/gus/gus_uart.c
sound/isa/gus/gus_volume.c
sound/isa/gus/gusclassic.c
sound/isa/gus/gusextreme.c
sound/isa/gus/gusmax.c
sound/isa/gus/interwave.c
sound/isa/opl3sa2.c
sound/isa/opti9xx/miro.c
sound/isa/opti9xx/opti92x-ad1848.c
sound/isa/sb/emu8000.c
sound/isa/sb/emu8000_local.h
sound/isa/sb/es968.c
sound/isa/sb/sb16.c
sound/isa/sb/sb16_csp.c
sound/isa/sb/sb16_main.c
sound/isa/sb/sb8.c
sound/isa/sb/sb8_main.c
sound/isa/sb/sb8_midi.c
sound/isa/sb/sb_common.c
sound/isa/sb/sb_mixer.c
sound/isa/sc6000.c
sound/isa/sgalaxy.c
sound/isa/sscape.c
sound/isa/wavefront/wavefront.c
sound/isa/wavefront/wavefront_fx.c
sound/isa/wavefront/wavefront_midi.c
sound/isa/wavefront/wavefront_synth.c
sound/last.c
sound/mips/au1x00.c
sound/parisc/harmony.c
sound/pci/Kconfig
sound/pci/Makefile
sound/pci/ac97/ac97_codec.c
sound/pci/ac97/ac97_patch.c
sound/pci/ac97/ac97_patch.h
sound/pci/ac97/ac97_pcm.c
sound/pci/ac97/ac97_proc.c
sound/pci/ac97/ak4531_codec.c
sound/pci/ad1889.c
sound/pci/ali5451/ali5451.c
sound/pci/als300.c
sound/pci/als4000.c
sound/pci/atiixp.c
sound/pci/atiixp_modem.c
sound/pci/au88x0/au88x0.h
sound/pci/au88x0/au88x0_core.c
sound/pci/au88x0/au88x0_game.c
sound/pci/au88x0/au88x0_mixer.c
sound/pci/au88x0/au88x0_mpu401.c
sound/pci/au88x0/au88x0_pcm.c
sound/pci/azt3328.c
sound/pci/bt87x.c
sound/pci/ca0106/ca0106.h
sound/pci/ca0106/ca0106_main.c
sound/pci/ca0106/ca0106_mixer.c
sound/pci/ca0106/ca0106_proc.c
sound/pci/ca0106/ca_midi.c
sound/pci/cmipci.c
sound/pci/cs4281.c
sound/pci/cs46xx/cs46xx.c
sound/pci/cs46xx/cs46xx_lib.c
sound/pci/cs46xx/dsp_spos.c
sound/pci/cs46xx/dsp_spos_scb_lib.c
sound/pci/cs5530.c
sound/pci/cs5535audio/cs5535audio.c
sound/pci/cs5535audio/cs5535audio_pcm.c
sound/pci/cs5535audio/cs5535audio_pm.c
sound/pci/echoaudio/darla20.c
sound/pci/echoaudio/darla24.c
sound/pci/echoaudio/echo3g.c
sound/pci/echoaudio/echoaudio.c
sound/pci/echoaudio/echoaudio.h
sound/pci/echoaudio/gina20.c
sound/pci/echoaudio/gina24.c
sound/pci/echoaudio/indigo.c
sound/pci/echoaudio/indigodj.c
sound/pci/echoaudio/indigoio.c
sound/pci/echoaudio/layla20.c
sound/pci/echoaudio/layla24.c
sound/pci/echoaudio/mia.c
sound/pci/echoaudio/mona.c
sound/pci/emu10k1/emu10k1.c
sound/pci/emu10k1/emu10k1_callback.c
sound/pci/emu10k1/emu10k1_main.c
sound/pci/emu10k1/emu10k1_synth.c
sound/pci/emu10k1/emu10k1_synth_local.h
sound/pci/emu10k1/emu10k1x.c
sound/pci/emu10k1/emufx.c
sound/pci/emu10k1/emumixer.c
sound/pci/emu10k1/emumpu401.c
sound/pci/emu10k1/emupcm.c
sound/pci/emu10k1/emuproc.c
sound/pci/emu10k1/io.c
sound/pci/emu10k1/irq.c
sound/pci/emu10k1/memory.c
sound/pci/emu10k1/p16v.c
sound/pci/emu10k1/timer.c
sound/pci/emu10k1/voice.c
sound/pci/ens1370.c
sound/pci/es1938.c
sound/pci/es1968.c
sound/pci/fm801.c
sound/pci/hda/Makefile
sound/pci/hda/hda_codec.c
sound/pci/hda/hda_codec.h
sound/pci/hda/hda_generic.c
sound/pci/hda/hda_hwdep.c
sound/pci/hda/hda_intel.c
sound/pci/hda/hda_local.h
sound/pci/hda/hda_proc.c
sound/pci/hda/patch_analog.c
sound/pci/hda/patch_atihdmi.c
sound/pci/hda/patch_cmedia.c
sound/pci/hda/patch_conexant.c
sound/pci/hda/patch_realtek.c
sound/pci/hda/patch_si3054.c
sound/pci/hda/patch_sigmatel.c
sound/pci/hda/patch_via.c
sound/pci/hda/vmaster.c [new file with mode: 0644]
sound/pci/ice1712/Makefile
sound/pci/ice1712/ak4xxx.c
sound/pci/ice1712/amp.c
sound/pci/ice1712/aureon.c
sound/pci/ice1712/delta.c
sound/pci/ice1712/ews.c
sound/pci/ice1712/hoontech.c
sound/pci/ice1712/ice1712.c
sound/pci/ice1712/ice1712.h
sound/pci/ice1712/ice1724.c
sound/pci/ice1712/juli.c
sound/pci/ice1712/phase.c
sound/pci/ice1712/pontis.c
sound/pci/ice1712/prodigy192.c
sound/pci/ice1712/prodigy_hifi.c [new file with mode: 0644]
sound/pci/ice1712/prodigy_hifi.h [new file with mode: 0644]
sound/pci/ice1712/revo.c
sound/pci/ice1712/se.c [new file with mode: 0644]
sound/pci/ice1712/se.h [new file with mode: 0644]
sound/pci/ice1712/vt1720_mobo.c
sound/pci/ice1712/wtm.c
sound/pci/intel8x0.c
sound/pci/intel8x0m.c
sound/pci/korg1212/korg1212.c
sound/pci/maestro3.c
sound/pci/mixart/mixart.c
sound/pci/mixart/mixart_core.c
sound/pci/mixart/mixart_hwdep.c
sound/pci/mixart/mixart_mixer.c
sound/pci/nm256/nm256.c
sound/pci/oxygen/Makefile [new file with mode: 0644]
sound/pci/oxygen/ak4396.h [new file with mode: 0644]
sound/pci/oxygen/cm9780.h [new file with mode: 0644]
sound/pci/oxygen/hifier.c [new file with mode: 0644]
sound/pci/oxygen/oxygen.c [new file with mode: 0644]
sound/pci/oxygen/oxygen.h [new file with mode: 0644]
sound/pci/oxygen/oxygen_io.c [new file with mode: 0644]
sound/pci/oxygen/oxygen_lib.c [new file with mode: 0644]
sound/pci/oxygen/oxygen_mixer.c [new file with mode: 0644]
sound/pci/oxygen/oxygen_pcm.c [new file with mode: 0644]
sound/pci/oxygen/oxygen_regs.h [new file with mode: 0644]
sound/pci/oxygen/virtuoso.c [new file with mode: 0644]
sound/pci/pcxhr/pcxhr.c
sound/pci/pcxhr/pcxhr_core.c
sound/pci/pcxhr/pcxhr_hwdep.c
sound/pci/pcxhr/pcxhr_mixer.c
sound/pci/riptide/riptide.c
sound/pci/rme32.c
sound/pci/rme96.c
sound/pci/rme9652/hdsp.c
sound/pci/rme9652/hdspm.c
sound/pci/rme9652/rme9652.c
sound/pci/sis7019.c [new file with mode: 0644]
sound/pci/sis7019.h [new file with mode: 0644]
sound/pci/sonicvibes.c
sound/pci/trident/Makefile
sound/pci/trident/trident.c
sound/pci/trident/trident_main.c
sound/pci/trident/trident_memory.c
sound/pci/trident/trident_synth.c [deleted file]
sound/pci/via82xx.c
sound/pci/via82xx_modem.c
sound/pci/vx222/vx222.c
sound/pci/vx222/vx222_ops.c
sound/pci/ymfpci/ymfpci.c
sound/pci/ymfpci/ymfpci_main.c
sound/pcmcia/pdaudiocf/pdaudiocf.c
sound/pcmcia/pdaudiocf/pdaudiocf_core.c
sound/pcmcia/pdaudiocf/pdaudiocf_irq.c
sound/pcmcia/pdaudiocf/pdaudiocf_pcm.c
sound/pcmcia/vx/vxp_mixer.c
sound/pcmcia/vx/vxp_ops.c
sound/pcmcia/vx/vxpocket.c
sound/ppc/awacs.c
sound/ppc/beep.c
sound/ppc/burgundy.c
sound/ppc/daca.c
sound/ppc/keywest.c
sound/ppc/pmac.c
sound/ppc/powermac.c
sound/ppc/snd_ps3.c
sound/ppc/tumbler.c
sound/sh/aica.c
sound/soc/Kconfig
sound/soc/Makefile
sound/soc/at91/at91-pcm.c
sound/soc/at91/at91-ssc.c
sound/soc/at91/eti_b1_wm8731.c
sound/soc/codecs/Kconfig
sound/soc/codecs/Makefile
sound/soc/codecs/ac97.c
sound/soc/codecs/cs4270.c
sound/soc/codecs/tlv320aic3x.c [new file with mode: 0644]
sound/soc/codecs/tlv320aic3x.h [new file with mode: 0644]
sound/soc/codecs/wm8731.c
sound/soc/codecs/wm8750.c
sound/soc/codecs/wm8753.c
sound/soc/codecs/wm9712.c
sound/soc/fsl/Kconfig [new file with mode: 0644]
sound/soc/fsl/Makefile [new file with mode: 0644]
sound/soc/fsl/fsl_dma.c [new file with mode: 0644]
sound/soc/fsl/fsl_dma.h [new file with mode: 0644]
sound/soc/fsl/fsl_ssi.c [new file with mode: 0644]
sound/soc/fsl/fsl_ssi.h [new file with mode: 0644]
sound/soc/fsl/mpc8610_hpcd.c [new file with mode: 0644]
sound/soc/pxa/Kconfig
sound/soc/pxa/Makefile
sound/soc/pxa/corgi.c
sound/soc/pxa/e800_wm9712.c [new file with mode: 0644]
sound/soc/pxa/poodle.c
sound/soc/pxa/pxa2xx-ac97.c
sound/soc/pxa/pxa2xx-i2s.c
sound/soc/pxa/pxa2xx-pcm.c
sound/soc/pxa/spitz.c
sound/soc/pxa/tosa.c
sound/soc/s3c24xx/Kconfig
sound/soc/s3c24xx/Makefile
sound/soc/s3c24xx/ln2440sbc_alc650.c [new file with mode: 0644]
sound/soc/s3c24xx/neo1973_wm8753.c
sound/soc/s3c24xx/s3c2412-i2s.c [new file with mode: 0644]
sound/soc/s3c24xx/s3c2412-i2s.h [new file with mode: 0644]
sound/soc/s3c24xx/s3c2443-ac97.c
sound/soc/s3c24xx/s3c24xx-ac97.h
sound/soc/s3c24xx/s3c24xx-i2s.c
sound/soc/s3c24xx/s3c24xx-pcm.c
sound/soc/s3c24xx/smdk2443_wm9710.c
sound/soc/sh/dma-sh7760.c
sound/soc/sh/hac.c
sound/soc/sh/sh7760-ac97.c
sound/soc/sh/ssi.c
sound/soc/soc-core.c
sound/soc/soc-dapm.c
sound/sparc/amd7930.c
sound/sparc/cs4231.c
sound/sparc/dbri.c
sound/spi/at73c213.c
sound/synth/emux/emux.c
sound/synth/emux/emux_hwdep.c
sound/synth/emux/emux_oss.c
sound/synth/emux/emux_proc.c
sound/synth/emux/emux_voice.h
sound/synth/emux/soundfont.c
sound/synth/util_mem.c
sound/usb/Kconfig
sound/usb/caiaq/Makefile
sound/usb/caiaq/caiaq-audio.c
sound/usb/caiaq/caiaq-control.c [new file with mode: 0644]
sound/usb/caiaq/caiaq-control.h [new file with mode: 0644]
sound/usb/caiaq/caiaq-device.c
sound/usb/caiaq/caiaq-device.h
sound/usb/caiaq/caiaq-input.c
sound/usb/caiaq/caiaq-midi.c
sound/usb/usbaudio.c
sound/usb/usbaudio.h
sound/usb/usbmidi.c
sound/usb/usbmixer.c
sound/usb/usbmixer_maps.c
sound/usb/usbquirks.h
sound/usb/usx2y/usX2Yhwdep.c
sound/usb/usx2y/usbusx2y.c
sound/usb/usx2y/usbusx2yaudio.c
sound/usb/usx2y/usx2yhwdeppcm.c

index 4b48c2e82c3c1f340cf4cd7b4247d6d58941d162..e985cf5e04107798a4f079260486a1793c061623 100644 (file)
@@ -57,7 +57,9 @@ Prior to version 0.9.0rc4 options had a 'snd_' prefix. This was removed.
                - Default: 1
                - For auto-loading more than one card, specify this
                  option together with snd-card-X aliases.
-
+    slots      - Reserve the slot index for the given driver.
+                 This option takes multiple strings.           
+                 See "Module Autoloading Support" section for details.
   
   Module snd-pcm-oss
   ------------------
@@ -148,13 +150,6 @@ Prior to version 0.9.0rc4 options had a 'snd_' prefix. This was removed.
 
     Module for sound cards based on Analog Devices AD1816A/AD1815 ISA chips.
 
-    port       - port # for AD1816A chip (PnP setup)
-    mpu_port   - port # for MPU-401 UART (PnP setup)
-    fm_port    - port # for OPL3 (PnP setup)
-    irq                - IRQ # for AD1816A chip (PnP setup)
-    mpu_irq    - IRQ # for MPU-401 UART (PnP setup)
-    dma1       - first DMA # for AD1816A chip (PnP setup)
-    dma2       - second DMA # for AD1816A chip (PnP setup)
     clockfreq   - Clock frequency for AD1816A chip (default = 0, 33000Hz)
     
     This module supports multiple cards, autoprobe and PnP.
@@ -201,14 +196,6 @@ Prior to version 0.9.0rc4 options had a 'snd_' prefix. This was removed.
 
     Module for sound cards based on Avance Logic ALS100/ALS120 ISA chips.
 
-    port       - port # for ALS100 (SB16) chip (PnP setup)
-    irq                - IRQ # for ALS100 (SB16) chip (PnP setup)
-    dma8       - 8-bit DMA # for ALS100 (SB16) chip (PnP setup)
-    dma16      - 16-bit DMA # for ALS100 (SB16) chip (PnP setup)
-    mpu_port   - port # for MPU-401 UART (PnP setup)
-    mpu_irq    - IRQ # for MPU-401 (PnP setup)
-    fm_port    - port # for OPL3 FM (PnP setup)
-    
     This module supports multiple cards, autoprobe and PnP.
 
     The power-management is supported.
@@ -302,15 +289,6 @@ Prior to version 0.9.0rc4 options had a 'snd_' prefix. This was removed.
 
     Module for sound cards based on Aztech System AZT2320 ISA chip (PnP only).
 
-    port       - port # for AZT2320 chip (PnP setup)
-    wss_port   - port # for WSS (PnP setup)
-    mpu_port   - port # for MPU-401 UART (PnP setup)
-    fm_port    - FM port # for AZT2320 chip (PnP setup)
-    irq                - IRQ # for AZT2320 (WSS) chip (PnP setup)
-    mpu_irq    - IRQ # for MPU-401 UART (PnP setup)
-    dma1       - 1st DMA # for AZT2320 (WSS) chip (PnP setup)
-    dma2       - 2nd DMA # for AZT2320 (WSS) chip (PnP setup)
-    
     This module supports multiple cards, PnP and autoprobe.
     
     The power-management is supported.
@@ -350,6 +328,10 @@ Prior to version 0.9.0rc4 options had a 'snd_' prefix. This was removed.
 
     Module for sound cards based on C-Media CMI8330 ISA chips.
 
+    isapnp     - ISA PnP detection - 0 = disable, 1 = enable (default)
+
+    with isapnp=0, the following options are available:
+
     wssport    - port # for CMI8330 chip (WSS)
     wssirq     - IRQ # for CMI8330 chip (WSS)
     wssdma     - first DMA # for CMI8330 chip (WSS)
@@ -404,6 +386,10 @@ Prior to version 0.9.0rc4 options had a 'snd_' prefix. This was removed.
 
     Module for sound cards based on CS4232/CS4232A ISA chips.
 
+    isapnp     - ISA PnP detection - 0 = disable, 1 = enable (default)
+
+    with isapnp=0, the following options are available:
+
     port       - port # for CS4232 chip (PnP setup - 0x534)
     cport      - control port # for CS4232 chip (PnP setup - 0x120,0x210,0xf00)
     mpu_port   - port # for MPU-401 UART (PnP setup - 0x300), -1 = disable
@@ -412,10 +398,10 @@ Prior to version 0.9.0rc4 options had a 'snd_' prefix. This was removed.
     mpu_irq    - IRQ # for MPU-401 UART (9,11,12,15)
     dma1       - first DMA # for CS4232 chip (0,1,3)
     dma2       - second DMA # for Yamaha CS4232 chip (0,1,3), -1 = disable
-    isapnp     - ISA PnP detection - 0 = disable, 1 = enable (default)
     
     This module supports multiple cards. This module does not support autoprobe
-    thus main port must be specified!!! Other ports are optional.
+    (if ISA PnP is not used) thus main port must be specified!!! Other ports are
+    optional.
 
     The power-management is supported.
     
@@ -425,6 +411,10 @@ Prior to version 0.9.0rc4 options had a 'snd_' prefix. This was removed.
     Module for sound cards based on CS4235/CS4236/CS4236B/CS4237B/
                                    CS4238B/CS4239 ISA chips.
 
+    isapnp     - ISA PnP detection - 0 = disable, 1 = enable (default)
+
+    with isapnp=0, the following options are available:
+
     port       - port # for CS4236 chip (PnP setup - 0x534)
     cport      - control port # for CS4236 chip (PnP setup - 0x120,0x210,0xf00)
     mpu_port   - port # for MPU-401 UART (PnP setup - 0x300), -1 = disable
@@ -433,7 +423,6 @@ Prior to version 0.9.0rc4 options had a 'snd_' prefix. This was removed.
     mpu_irq    - IRQ # for MPU-401 UART (9,11,12,15)
     dma1       - first DMA # for CS4236 chip (0,1,3)
     dma2       - second DMA # for CS4236 chip (0,1,3), -1 = disable
-    isapnp     - ISA PnP detection - 0 = disable, 1 = enable (default)
     
     This module supports multiple cards. This module does not support autoprobe
     (if ISA PnP is not used) thus main port and control port must be
@@ -503,13 +492,6 @@ Prior to version 0.9.0rc4 options had a 'snd_' prefix. This was removed.
     Module for Diamond Technologies DT-019X / Avance Logic ALS-007 (PnP
     only)
 
-    port       - Port # (PnP setup)
-    mpu_port   - Port # for MPU-401 (PnP setup)
-    fm_port    - Port # for FM OPL-3 (PnP setup)
-    irq                - IRQ # (PnP setup)
-    mpu_irq    - IRQ # for MPU-401 (PnP setup)
-    dma8       - DMA # (PnP setup)
-
     This module supports multiple cards.  This module is enabled only with
     ISA PnP support.
 
@@ -607,10 +589,6 @@ Prior to version 0.9.0rc4 options had a 'snd_' prefix. This was removed.
 
     Module for sound cards based on ESS ES968 chip (PnP only).
 
-    port       - port # for ES968 (SB8) chip (PnP setup)
-    irq                - IRQ # for ES968 (SB8) chip (PnP setup)
-    dma1       - DMA # for ES968 (SB8) chip (PnP setup)
-    
     This module supports multiple cards, PnP and autoprobe.
     
     The power-management is supported.
@@ -633,13 +611,16 @@ Prior to version 0.9.0rc4 options had a 'snd_' prefix. This was removed.
 
     Module for ESS AudioDrive ES-18xx sound cards.
 
+    isapnp     - ISA PnP detection - 0 = disable, 1 = enable (default)
+
+    with isapnp=0, the following options are available:
+
     port       - port # for ES-18xx chip (0x220,0x240,0x260)
     mpu_port   - port # for MPU-401 port (0x300,0x310,0x320,0x330), -1 = disable (default)
     fm_port    - port # for FM (optional, not used)
     irq                - IRQ # for ES-18xx chip (5,7,9,10)
     dma1       - first DMA # for ES-18xx chip (0,1,3)
     dma2       - first DMA # for ES-18xx chip (0,1,3)
-    isapnp     - ISA PnP detection - 0 = disable, 1 = enable (default)
 
     This module supports multiple cards, ISA PnP and autoprobe (without MPU-401
     port if native ISA PnP routines are not used).
@@ -763,9 +744,12 @@ Prior to version 0.9.0rc4 options had a 'snd_' prefix. This was removed.
                VIA VT8251/VT8237A,
                SIS966, ULI M5461
 
+    [Multiple options for each card instance]
     model      - force the model name
     position_fix - Fix DMA pointer (0 = auto, 1 = none, 2 = POSBUF, 3 = FIFO size)
     probe_mask  - Bitmask to probe codecs (default = -1, meaning all slots)
+    
+    [Single (global) options]
     single_cmd  - Use single immediate commands to communicate with
                codecs (for debugging only)
     enable_msi - Enable Message Signaled Interrupt (MSI) (default = off)
@@ -774,8 +758,8 @@ Prior to version 0.9.0rc4 options had a 'snd_' prefix. This was removed.
     power_save_controller - Reset HD-audio controller in power-saving mode
                (default = on)
 
-    This module supports one card and autoprobe.
-
+    This module supports multiple cards and autoprobe.
+    
     Each codec may have a model table for different configurations.
     If your machine isn't listed there, the default (usually minimal)
     configuration is set up.  You can pass "model=<name>" option to
@@ -817,17 +801,23 @@ Prior to version 0.9.0rc4 options had a 'snd_' prefix. This was removed.
          will          Will laptops (PB V7900)
          replacer      Replacer 672V
          basic         fixed pin assignment (old default model)
+         test          for testing/debugging purpose, almost all controls can
+                       adjusted.  Appearing only when compiled with
+                       $CONFIG_SND_DEBUG=y
          auto          auto-config reading BIOS (default)
 
        ALC262
          fujitsu       Fujitsu Laptop
          hp-bpc        HP xw4400/6400/8400/9400 laptops
          hp-bpc-d7000  HP BPC D7000
+         hp-tc-t5735   HP Thin Client T5735
+         hp-rp5700     HP RP5700
          benq          Benq ED8
          benq-t31      Benq T31
          hippo         Hippo (ATI) with jack detection, Sony UX-90s
          hippo_1       Hippo (Benq) with jack detection
          sony-assamd   Sony ASSAMD
+         ultra         Samsung Q1 Ultra Vista model
          basic         fixed pin assignment w/o SPDIF
          auto          auto-config reading BIOS (default)
 
@@ -835,6 +825,10 @@ Prior to version 0.9.0rc4 options had a 'snd_' prefix. This was removed.
          3stack        3-stack model
          toshiba       Toshiba A205
          acer          Acer laptops
+         dell          Dell OEM laptops (Vostro 1200)
+         test          for testing/debugging purpose, almost all controls can
+                       adjusted.  Appearing only when compiled with
+                       $CONFIG_SND_DEBUG=y
          auto          auto-config reading BIOS (default)
 
        ALC662
@@ -843,6 +837,8 @@ Prior to version 0.9.0rc4 options had a 'snd_' prefix. This was removed.
          3stack-6ch-dig 3-stack (6-channel) with SPDIF
          6stack-dig     6-stack with SPDIF
          lenovo-101e    Lenovo laptop
+         eeepc-p701    ASUS Eeepc P701
+         eeepc-ep20    ASUS Eeepc EP20
          auto          auto-config reading BIOS (default)
 
        ALC882/885
@@ -877,6 +873,8 @@ Prior to version 0.9.0rc4 options had a 'snd_' prefix. This was removed.
          haier-w66     Haier W66
          6stack-hp     HP machines with 6stack (Nettle boards)
          3stack-hp     HP machines with 3stack (Lucknow, Samba boards)
+         6stack-dell   Dell machines with 6stack (Inspiron 530)
+         mitac         Mitac 8252D
          auto          auto-config reading BIOS (default)
 
        ALC861/660
@@ -928,6 +926,7 @@ Prior to version 0.9.0rc4 options had a 'snd_' prefix. This was removed.
        AD1984
          basic         default configuration
          thinkpad      Lenovo Thinkpad T61/X61
+         dell          Dell T3400
 
        AD1986A
          6stack        6-jack, separate surrounds (default)
@@ -947,7 +946,10 @@ Prior to version 0.9.0rc4 options had a 'snd_' prefix. This was removed.
          auto          auto-config reading BIOS (default)
        
        Conexant 5045
-         laptop        Laptop config 
+         laptop-hpsense    Laptop with HP sense (old model laptop)
+         laptop-micsense   Laptop with Mic sense (old model fujitsu)
+         laptop-hpmicsense Laptop with HP and Mic senses
+         benq          Benq R55E
          test          for testing/debugging purpose, almost all controls
                        can be adjusted.  Appearing only when compiled with
                        $CONFIG_SND_DEBUG=y
@@ -960,6 +962,10 @@ Prior to version 0.9.0rc4 options had a 'snd_' prefix. This was removed.
                        can be adjusted.  Appearing only when compiled with
                        $CONFIG_SND_DEBUG=y
 
+       Conexant 5051
+         laptop        Basic Laptop config (default)
+         hp            HP Spartan laptop
+
        STAC9200
          ref           Reference board
          dell-d21      Dell (unknown)
@@ -1091,6 +1097,15 @@ Prior to version 0.9.0rc4 options had a 'snd_' prefix. This was removed.
 
     See hdspm.txt for details.
 
+  Module snd-hifier
+  -----------------
+
+    Module for the MediaTek/TempoTec HiFier Fantasia sound card.
+
+    This module supports autoprobe and multiple cards.
+
+    Power management is _not_ supported.
+
   Module snd-ice1712
   ------------------
 
@@ -1156,11 +1171,14 @@ Prior to version 0.9.0rc4 options had a 'snd_' prefix. This was removed.
                        * Chaintech 9CJS
                        * Chaintech AV-710
                        * Shuttle SN25P
+                       * Onkyo SE-90PCI
+                       * Onkyo SE-200PCI
 
     model       - Use the given board model, one of the following:
                  revo51, revo71, amp2000, prodigy71, prodigy71lt,
                  prodigy192, aureon51, aureon71, universe, ap192,
-                 k8x800, phase22, phase28, ms300, av710
+                 k8x800, phase22, phase28, ms300, av710, se200pci,
+                 se90pci
 
     This module supports multiple cards and autoprobe.
 
@@ -1257,15 +1275,19 @@ Prior to version 0.9.0rc4 options had a 'snd_' prefix. This was removed.
     Module for Gravis UltraSound PnP, Dynasonic 3-D/Pro, STB Sound Rage 32
     and other sound cards based on AMD InterWave (tm) chip.
   
-    port       - port # for InterWave chip (0x210,0x220,0x230,0x240,0x250,0x260)
-    irq                - IRQ # for InterWave chip (3,5,9,11,12,15)
-    dma1       - DMA # for InterWave chip (0,1,3,5,6,7)
-    dma2       - DMA # for InterWave chip (0,1,3,5,6,7,-1=disable)
     joystick_dac - 0 to 31, (0.59V-4.52V or 0.389V-2.98V)
     midi       - 1 = MIDI UART enable, 0 = MIDI UART disable (default)
     pcm_voices - reserved PCM voices for the synthesizer (default 2)
     effect     - 1 = InterWave effects enable (default 0);
                   requires 8 voices
+    isapnp     - ISA PnP detection - 0 = disable, 1 = enable (default)
+
+    with isapnp=0, the following options are available:
+
+    port       - port # for InterWave chip (0x210,0x220,0x230,0x240,0x250,0x260)
+    irq                - IRQ # for InterWave chip (3,5,9,11,12,15)
+    dma1       - DMA # for InterWave chip (0,1,3,5,6,7)
+    dma2       - DMA # for InterWave chip (0,1,3,5,6,7,-1=disable)
 
     This module supports multiple cards, autoprobe and ISA PnP.
 
@@ -1276,16 +1298,20 @@ Prior to version 0.9.0rc4 options had a 'snd_' prefix. This was removed.
     and other sound cards based on AMD InterWave (tm) chip with TEA6330T
     circuit for extended control of bass, treble and master volume.
   
-    port       - port # for InterWave chip (0x210,0x220,0x230,0x240,0x250,0x260)
-    port_tc    - tone control (i2c bus) port # for TEA6330T chip (0x350,0x360,0x370,0x380)
-    irq                - IRQ # for InterWave chip (3,5,9,11,12,15)
-    dma1       - DMA # for InterWave chip (0,1,3,5,6,7)
-    dma2       - DMA # for InterWave chip (0,1,3,5,6,7,-1=disable)
     joystick_dac - 0 to 31, (0.59V-4.52V or 0.389V-2.98V)
     midi       - 1 = MIDI UART enable, 0 = MIDI UART disable (default)
     pcm_voices - reserved PCM voices for the synthesizer (default 2)
     effect     - 1 = InterWave effects enable (default 0);
                   requires 8 voices
+    isapnp     - ISA PnP detection - 0 = disable, 1 = enable (default)
+
+    with isapnp=0, the following options are available:
+
+    port       - port # for InterWave chip (0x210,0x220,0x230,0x240,0x250,0x260)
+    port_tc    - tone control (i2c bus) port # for TEA6330T chip (0x350,0x360,0x370,0x380)
+    irq                - IRQ # for InterWave chip (3,5,9,11,12,15)
+    dma1       - DMA # for InterWave chip (0,1,3,5,6,7)
+    dma2       - DMA # for InterWave chip (0,1,3,5,6,7,-1=disable)
 
     This module supports multiple cards, autoprobe and ISA PnP.
 
@@ -1473,6 +1499,10 @@ Prior to version 0.9.0rc4 options had a 'snd_' prefix. This was removed.
 
     Module for Yamaha OPL3-SA2/SA3 sound cards.
 
+    isapnp     - ISA PnP detection - 0 = disable, 1 = enable (default)
+
+    with isapnp=0, the following options are available:
+
     port       - control port # for OPL3-SA chip (0x370)
     sb_port    - SB port # for OPL3-SA chip (0x220,0x240)
     wss_port   - WSS port # for OPL3-SA chip (0x530,0xe80,0xf40,0x604)
@@ -1481,7 +1511,6 @@ Prior to version 0.9.0rc4 options had a 'snd_' prefix. This was removed.
     irq                - IRQ # for OPL3-SA chip (5,7,9,10)
     dma1       - first DMA # for Yamaha OPL3-SA chip (0,1,3)
     dma2       - second DMA # for Yamaha OPL3-SA chip (0,1,3), -1 = disable
-    isapnp     - ISA PnP detection - 0 = disable, 1 = enable (default)
     
     This module supports multiple cards and ISA PnP.  It does not support
     autoprobe (if ISA PnP is not used) thus all ports must be specified!!!
@@ -1494,6 +1523,10 @@ Prior to version 0.9.0rc4 options had a 'snd_' prefix. This was removed.
     Module for sound cards based on OPTi 82c92x and Analog Devices AD1848 chips.
     Module works with OAK Mozart cards as well.
     
+    isapnp    - ISA PnP detection - 0 = disable, 1 = enable (default)
+
+    with isapnp=0, the following options are available:
+
     port      - port # for WSS chip (0x530,0xe80,0xf40,0x604)
     mpu_port  - port # for MPU-401 UART (0x300,0x310,0x320,0x330)
     fm_port   - port # for OPL3 device (0x388)
@@ -1508,6 +1541,10 @@ Prior to version 0.9.0rc4 options had a 'snd_' prefix. This was removed.
 
     Module for sound cards based on OPTi 82c92x and Crystal CS4231 chips.
     
+    isapnp    - ISA PnP detection - 0 = disable, 1 = enable (default)
+
+    with isapnp=0, the following options are available:
+
     port      - port # for WSS chip (0x530,0xe80,0xf40,0x604)
     mpu_port  - port # for MPU-401 UART (0x300,0x310,0x320,0x330)
     fm_port   - port # for OPL3 device (0x388)
@@ -1523,6 +1560,10 @@ Prior to version 0.9.0rc4 options had a 'snd_' prefix. This was removed.
 
     Module for sound cards based on OPTi 82c93x chips.
     
+    isapnp    - ISA PnP detection - 0 = disable, 1 = enable (default)
+
+    with isapnp=0, the following options are available:
+
     port      - port # for WSS chip (0x530,0xe80,0xf40,0x604)
     mpu_port  - port # for MPU-401 UART (0x300,0x310,0x320,0x330)
     fm_port   - port # for OPL3 device (0x388)
@@ -1533,6 +1574,22 @@ Prior to version 0.9.0rc4 options had a 'snd_' prefix. This was removed.
 
     This module supports only one card, autoprobe and PnP.
 
+  Module snd-oxygen
+  -----------------
+
+    Module for sound cards based on the C-Media CMI8788 chip:
+    * Asound A-8788
+    * AuzenTech X-Meridian
+    * Bgears b-Enspirer
+    * Club3D Theatron DTS
+    * HT-Omega Claro
+    * Razer Barracuda AC-1
+    * Sondigo Inferno
+
+    This module supports autoprobe and multiple cards.
+
+    Power management is _not_ supported.
+
   Module snd-pcxhr
   ----------------
 
@@ -1647,6 +1704,12 @@ Prior to version 0.9.0rc4 options had a 'snd_' prefix. This was removed.
                                          SoundBlaster AWE 32 (PnP),
                                          SoundBlaster AWE 64 PnP
 
+    mic_agc    - Mic Auto-Gain-Control - 0 = disable, 1 = enable (default)
+    csp                - ASP/CSP chip support - 0 = disable (default), 1 = enable
+    isapnp     - ISA PnP detection - 0 = disable, 1 = enable (default)
+
+    with isapnp=0, the following options are available:
+
     port       - port # for SB DSP 4.x chip (0x220,0x240,0x260)
     mpu_port   - port # for MPU-401 UART (0x300,0x330), -1 = disable
     awe_port   - base port # for EMU8000 synthesizer (0x620,0x640,0x660)
@@ -1654,9 +1717,6 @@ Prior to version 0.9.0rc4 options had a 'snd_' prefix. This was removed.
     irq                - IRQ # for SB DSP 4.x chip (5,7,9,10)
     dma8       - 8-bit DMA # for SB DSP 4.x chip (0,1,3)
     dma16      - 16-bit DMA # for SB DSP 4.x chip (5,6,7)
-    mic_agc    - Mic Auto-Gain-Control - 0 = disable, 1 = enable (default)
-    csp                - ASP/CSP chip support - 0 = disable (default), 1 = enable
-    isapnp     - ISA PnP detection - 0 = disable, 1 = enable (default)
     
     This module supports multiple cards, autoprobe and ISA PnP.
 
@@ -1739,18 +1799,21 @@ Prior to version 0.9.0rc4 options had a 'snd_' prefix. This was removed.
 
     Module for Turtle Beach Maui, Tropez and Tropez+ sound cards.
 
+    use_cs4232_midi - Use CS4232 MPU-401 interface
+                      (inaccessibly located inside your computer)
+    isapnp          - ISA PnP detection - 0 = disable, 1 = enable (default)
+
+    with isapnp=0, the following options are available:
+
     cs4232_pcm_port - Port # for CS4232 PCM interface.
     cs4232_pcm_irq  - IRQ # for CS4232 PCM interface (5,7,9,11,12,15).
     cs4232_mpu_port - Port # for CS4232 MPU-401 interface.
     cs4232_mpu_irq  - IRQ # for CS4232 MPU-401 interface (9,11,12,15).
-    use_cs4232_midi - Use CS4232 MPU-401 interface
-                      (inaccessibly located inside your computer)
     ics2115_port    - Port # for ICS2115
     ics2115_irq     - IRQ # for ICS2115
     fm_port         - FM OPL-3 Port #
     dma1            - DMA1 # for CS4232 PCM interface.
     dma2            - DMA2 # for CS4232 PCM interface.
-    isapnp          - ISA PnP detection - 0 = disable, 1 = enable (default)
 
     The below are options for wavefront_synth features:
     wf_raw         - Assume that we need to boot the OS (default:no)
@@ -1965,6 +2028,16 @@ Prior to version 0.9.0rc4 options had a 'snd_' prefix. This was removed.
     
     This module supports multiple cards.
 
+  Module snd-virtuoso
+  -------------------
+
+    Module for sound cards based on the Asus AV200 chip, i.e.,
+    Xonar D2 and Xonar D2X.
+
+    This module supports autoprobe and multiple cards.
+
+    Power management is _not_ supported.
+
   Module snd-vx222
   ----------------
 
@@ -2135,6 +2208,23 @@ alias sound-slot-1 snd-ens1371
 In this example, the interwave card is always loaded as the first card
 (index 0) and ens1371 as the second (index 1).
 
+Alternative (and new) way to fixate the slot assignment is to use
+"slots" option of snd module.  In the case above, specify like the
+following: 
+
+options snd slots=snd-interwave,snd-ens1371
+
+Then, the first slot (#0) is reserved for snd-interwave driver, and
+the second (#1) for snd-ens1371.  You can omit index option in each
+driver if slots option is used (although you can still have them at
+the same time as long as they don't conflict).
+
+The slots option is especially useful for avoiding the possible
+hot-plugging and the resultant slot conflict.  For example, in the
+case above again, the first two slots are already reserved.  If any
+other driver (e.g. snd-usb-audio) is loaded before snd-interwave or
+snd-ens1371, it will be assigned to the third or later slot.
+
 
 ALSA PCM devices to OSS devices mapping
 =======================================
index 2c3fc3cb3b6bdced5bf553b223719f8b370a6c26..b03df4d4795c6767b79bed4125b07bbdf64fc823 100644 (file)
@@ -18,7 +18,7 @@
       </affiliation>
      </author>
 
-     <date>September 10, 2007</date>
+     <date>Oct 15, 2007</date>
      <edition>0.3.7</edition>
 
     <abstract>
@@ -67,7 +67,7 @@
       This document describes how to write an
       <ulink url="http://www.alsa-project.org/"><citetitle>
       ALSA (Advanced Linux Sound Architecture)</citetitle></ulink>
-      driver. The document focuses mainly on the PCI soundcard.
+      driver. The document focuses mainly on PCI soundcards.
       In the case of other device types, the API might
       be different, too. However, at least the ALSA kernel API is
       consistent, and therefore it would be still a bit help for
     </para>
 
     <para>
-    The target of this document is ones who already have enough
-    skill of C language and have the basic knowledge of linux
-    kernel programming.  This document doesn't explain the general
-    topics of linux kernel codes and doesn't cover the detail of
-    implementation of each low-level driver.  It describes only how is
+    This document targets people who already have enough
+    C language skills and have basic linux kernel programming
+    knowledge.  This document doesn't explain the general
+    topic of linux kernel coding and doesn't cover low-level
+    driver implementation details. It only describes
     the standard way to write a PCI sound driver on ALSA.
     </para>
 
     <para>
-      If you are already familiar with the older ALSA ver.0.5.x, you
-    can check the drivers such as <filename>es1938.c</filename> or
-    <filename>maestro3.c</filename> which have also almost the same
+      If you are already familiar with the older ALSA ver.0.5.x API, you
+    can check the drivers such as <filename>sound/pci/es1938.c</filename> or
+    <filename>sound/pci/maestro3.c</filename> which have also almost the same
     code-base in the ALSA 0.5.x tree, so you can compare the differences.
     </para>
 
     <para>
-      This document is still a draft version. Any feedbacks and
+      This document is still a draft version. Any feedback and
     corrections, please!!
     </para>
   </preface>
     <section id="file-tree-general">
       <title>General</title>
       <para>
-        The ALSA drivers are provided in the two ways.
+        The ALSA drivers are provided in two ways.
       </para>
 
       <para>
       ALSA's ftp site, and another is the 2.6 (or later) Linux kernel
       tree. To synchronize both, the ALSA driver tree is split into
       two different trees: alsa-kernel and alsa-driver. The former
-      contains purely the source codes for the Linux 2.6 (or later)
+      contains purely the source code for the Linux 2.6 (or later)
       tree. This tree is designed only for compilation on 2.6 or
       later environment. The latter, alsa-driver, contains many subtle
-      files for compiling the ALSA driver on the outside of Linux
-      kernel like configure script, the wrapper functions for older,
-      2.2 and 2.4 kernels, to adapt the latest kernel API,
+      files for compiling ALSA drivers outside of the Linux kernel tree,
+      wrapper functions for older 2.2 and 2.4 kernels, to adapt the latest kernel API,
       and additional drivers which are still in development or in
       tests.  The drivers in alsa-driver tree will be moved to
-      alsa-kernel (eventually 2.6 kernel tree) once when they are
+      alsa-kernel (and eventually to the 2.6 kernel tree) when they are
       finished and confirmed to work fine.
       </para>
 
     <section id="file-tree-core-directory">
       <title>core directory</title>
       <para>
-        This directory contains the middle layer, that is, the heart
+        This directory contains the middle layer which is the heart
       of ALSA drivers. In this directory, the native ALSA modules are
       stored. The sub-directories contain different modules and are
       dependent upon the kernel config. 
           The codes for PCM and mixer OSS emulation modules are stored
         in this directory. The rawmidi OSS emulation is included in
         the ALSA rawmidi code since it's quite small. The sequencer
-        code is stored in core/seq/oss directory (see
+        code is stored in <filename>core/seq/oss</filename> directory (see
         <link linkend="file-tree-core-directory-seq-oss"><citetitle>
         below</citetitle></link>).
         </para>
       <section id="file-tree-core-directory-seq">
         <title>core/seq</title>
         <para>
-          This and its sub-directories are for the ALSA
+          This directory and its sub-directories are for the ALSA
         sequencer. This directory contains the sequencer core and
         primary sequencer modules such like snd-seq-midi,
         snd-seq-virmidi, etc. They are compiled only when
       <title>include directory</title>
       <para>
         This is the place for the public header files of ALSA drivers,
-      which are to be exported to the user-space, or included by
+      which are to be exported to user-space, or included by
       several files at different directories. Basically, the private
       header files should not be placed in this directory, but you may
-      still find files there, due to historical reason :) 
+      still find files there, due to historical reasons :) 
       </para>
     </section>
 
     <section id="file-tree-drivers-directory">
       <title>drivers directory</title>
       <para>
-        This directory contains the codes shared among different drivers
-      on the different architectures.  They are hence supposed not to be
+        This directory contains code shared among different drivers
+      on different architectures.  They are hence supposed not to be
       architecture-specific.
       For example, the dummy pcm driver and the serial MIDI
       driver are found in this directory. In the sub-directories,
-      there are the codes for components which are independent from
+      there is code for components which are independent from
       bus and cpu architectures. 
       </para>
 
 
       <para>
         Although there is a standard i2c layer on Linux, ALSA has its
-      own i2c codes for some cards, because the soundcard needs only a
+      own i2c code for some cards, because the soundcard needs only a
       simple operation and the standard i2c API is too complicated for
       such a purpose. 
       </para>
 
         <para>
           So far, there is only Emu8000/Emu10k1 synth driver under
-        synth/emux sub-directory. 
+        the <filename>synth/emux</filename> sub-directory. 
         </para>
     </section>
 
     <section id="file-tree-pci-directory">
       <title>pci directory</title>
       <para>
-        This and its sub-directories hold the top-level card modules
-      for PCI soundcards and the codes specific to the PCI BUS.
+        This directory and its sub-directories hold the top-level card modules
+      for PCI soundcards and the code specific to the PCI BUS.
       </para>
 
       <para>
-        The drivers compiled from a single file is stored directly on
-      pci directory, while the drivers with several source files are
-      stored on its own sub-directory (e.g. emu10k1, ice1712). 
+        The drivers compiled from a single file are stored directly
+      in the pci directory, while the drivers with several source files are
+      stored on their own sub-directory (e.g. emu10k1, ice1712). 
       </para>
     </section>
 
     <section id="file-tree-isa-directory">
       <title>isa directory</title>
       <para>
-        This and its sub-directories hold the top-level card modules
+        This directory and its sub-directories hold the top-level card modules
       for ISA soundcards. 
       </para>
     </section>
     <section id="file-tree-arm-ppc-sparc-directories">
       <title>arm, ppc, and sparc directories</title>
       <para>
-        These are for the top-level card modules which are
-      specific to each given architecture
+        They are used for top-level card modules which are
+      specific to one of these architectures
       </para>
     </section>
 
     <section id="file-tree-usb-directory">
       <title>usb directory</title>
       <para>
-        This contains the USB-audio driver. On the latest version, the
-      USB MIDI driver is integrated together with usb-audio driver. 
+        This directory contains the USB-audio driver. In the latest version, the
+      USB MIDI driver is integrated in the usb-audio driver. 
       </para>
     </section>
 
       <title>pcmcia directory</title>
       <para>
         The PCMCIA, especially PCCard drivers will go here. CardBus
-      drivers will be on pci directory, because its API is identical
-      with the standard PCI cards. 
+      drivers will be in the pci directory, because their API is identical
+      to that of standard PCI cards. 
       </para>
     </section>
 
     <section id="file-tree-oss-directory">
       <title>oss directory</title>
       <para>
-        The OSS/Lite source files are stored here on Linux 2.6 (or
-      later) tree. (In the ALSA driver tarball, it's empty, of course :) 
+        The OSS/Lite source files are stored here in Linux 2.6 (or
+      later) tree. In the ALSA driver tarball, this directory is empty,
+      of course :) 
       </para>
     </section>
   </chapter>
     <section id="basic-flow-outline">
       <title>Outline</title>
       <para>
-        The minimum flow of PCI soundcard is like the following:
+        The minimum flow for PCI soundcards is as follows:
 
         <itemizedlist>
           <listitem><para>define the PCI ID table (see the section
           </citetitle></link>).</para></listitem> 
           <listitem><para>create <function>probe()</function> callback.</para></listitem>
           <listitem><para>create <function>remove()</function> callback.</para></listitem>
-          <listitem><para>create pci_driver table which contains the three pointers above.</para></listitem>
-          <listitem><para>create <function>init()</function> function just calling <function>pci_register_driver()</function> to register the pci_driver table defined above.</para></listitem>
-          <listitem><para>create <function>exit()</function> function to call <function>pci_unregister_driver()</function> function.</para></listitem>
+          <listitem><para>create a <structname>pci_driver</structname> structure
+         containing the three pointers above.</para></listitem>
+          <listitem><para>create an <function>init()</function> function just calling
+         the <function>pci_register_driver()</function> to register the pci_driver table
+         defined above.</para></listitem>
+          <listitem><para>create an <function>exit()</function> function to call
+         the <function>pci_unregister_driver()</function> function.</para></listitem>
         </itemizedlist>
       </para>
     </section>
       <para>
         The code example is shown below. Some parts are kept
       unimplemented at this moment but will be filled in the
-      succeeding sections. The numbers in comment lines of
-      <function>snd_mychip_probe()</function> function are the
-      markers
+      next sections. The numbers in the comment lines of the
+      <function>snd_mychip_probe()</function> function
+      refer to details explained in the following section
 
         <example>
-          <title>Basic Flow for PCI Drivers Example</title>
+          <title>Basic Flow for PCI Drivers Example</title>
           <programlisting>
 <![CDATA[
-  #include <sound/driver.h>
   #include <linux/init.h>
   #include <linux/pci.h>
   #include <linux/slab.h>
   #include <sound/initval.h>
 
   /* module parameters (see "Module Parameters") */
+  /* SNDRV_CARDS: maximum number of cards supported by this module */
   static int index[SNDRV_CARDS] = SNDRV_DEFAULT_IDX;
   static char *id[SNDRV_CARDS] = SNDRV_DEFAULT_STR;
   static int enable[SNDRV_CARDS] = SNDRV_DEFAULT_ENABLE_PNP;
   /* definition of the chip-specific record */
   struct mychip {
           struct snd_card *card;
-          /* rest of implementation will be in the section
-           * "PCI Resource Managements"
+          /* the rest of the implementation will be in section
+           * "PCI Resource Management"
            */
   };
 
   /* chip-specific destructor
-   * (see "PCI Resource Managements")
+   * (see "PCI Resource Management")
    */
   static int snd_mychip_free(struct mychip *chip)
   {
           *rchip = NULL;
 
           /* check PCI availability here
-           * (see "PCI Resource Managements")
+           * (see "PCI Resource Management")
            */
           ....
 
           chip->card = card;
 
           /* rest of initialization here; will be implemented
-           * later, see "PCI Resource Managements"
+           * later, see "PCI Resource Management"
            */
           ....
 
           return 0;
   }
 
-  /* destructor -- see "Destructor" sub-section */
+  /* destructor -- see the "Destructor" sub-section */
   static void __devexit snd_mychip_remove(struct pci_dev *pci)
   {
           snd_card_free(pci_get_drvdata(pci));
     <section id="basic-flow-constructor">
       <title>Constructor</title>
       <para>
-        The real constructor of PCI drivers is probe callback. The
-      probe callback and other component-constructors which are called
-      from probe callback should be defined with
-      <parameter>__devinit</parameter> prefix. You 
-      cannot use <parameter>__init</parameter> prefix for them,
+        The real constructor of PCI drivers is the <function>probe</function> callback.
+      The <function>probe</function> callback and other component-constructors which are called
+      from the <function>probe</function> callback should be defined with
+      the <parameter>__devinit</parameter> prefix. You 
+      cannot use the <parameter>__init</parameter> prefix for them,
       because any PCI device could be a hotplug device. 
       </para>
 
       <para>
-        In the probe callback, the following scheme is often used.
+        In the <function>probe</function> callback, the following scheme is often used.
       </para>
 
       <section id="basic-flow-constructor-device-index">
         </para>
 
         <para>
-          At each time probe callback is called, check the
+          Each time the <function>probe</function> callback is called, check the
         availability of the device. If not available, simply increment
         the device index and returns. dev will be incremented also
         later (<link
         </para>
 
         <para>
-          The detail will be explained in the section
+          The details will be explained in the section
           <link linkend="card-management-card-instance"><citetitle>
           Management of Cards and Components</citetitle></link>.
         </para>
             </programlisting>
           </informalexample>
 
-          The detail will be explained in the section <link
+          The details will be explained in the section <link
         linkend="pci-resource"><citetitle>PCI Resource
-        Managements</citetitle></link>.
+        Management</citetitle></link>.
         </para>
       </section>
 
           </informalexample>
 
           The driver field holds the minimal ID string of the
-        chip. This is referred by alsa-lib's configurator, so keep it
+        chip. This is used by alsa-lib's configurator, so keep it
         simple but unique. 
           Even the same driver can have different driver IDs to
         distinguish the functionality of each chip type. 
 
         <para>
           The shortname field is a string shown as more verbose
-        name. The longname field contains the information which is
+        name. The longname field contains the information
         shown in <filename>/proc/asound/cards</filename>. 
         </para>
       </section>
           </informalexample>
 
           In the above, the card record is stored. This pointer is
-        referred in the remove callback and power-management
+        used in the remove callback and power-management
         callbacks, too. 
         </para>
       </section>
         <informalexample>
           <programlisting>
 <![CDATA[
-  #include <sound/driver.h>
   #include <linux/init.h>
   #include <linux/pci.h>
   #include <linux/slab.h>
         </informalexample>
 
        where the last one is necessary only when module options are
-      defined in the source file.  If the codes are split to several
-      files, the file without module options don't need them.
+      defined in the source file.  If the code is split into several
+      files, the files without module options don't need them.
       </para>
 
       <para>
-        In addition to them, you'll need
-      <filename>&lt;linux/interrupt.h&gt;</filename> for the interrupt
-      handling, and <filename>&lt;asm/io.h&gt;</filename> for the i/o
-      access. If you use <function>mdelay()</function> or
+        In addition to these headers, you'll need
+      <filename>&lt;linux/interrupt.h&gt;</filename> for interrupt
+      handling, and <filename>&lt;asm/io.h&gt;</filename> for I/O
+      access. If you use the <function>mdelay()</function> or
       <function>udelay()</function> functions, you'll need to include
-      <filename>&lt;linux/delay.h&gt;</filename>, too. 
+      <filename>&lt;linux/delay.h&gt;</filename> too. 
       </para>
 
       <para>
-      The ALSA interfaces like PCM or control API are defined in other
-      header files as <filename>&lt;sound/xxx.h&gt;</filename>.
+      The ALSA interfaces like the PCM and control APIs are defined in other
+      <filename>&lt;sound/xxx.h&gt;</filename> header files.
       They have to be included after
       <filename>&lt;sound/core.h&gt;</filename>.
       </para>
 
       <para>
       A card record is the headquarters of the soundcard.  It manages
-      the list of whole devices (components) on the soundcard, such as
+      the whole list of devices (components) on the soundcard, such as
       PCM, mixers, MIDI, synthesizer, and so on.  Also, the card
       record holds the ID and the name strings of the card, manages
       the root of proc files, and controls the power-management states
       and hotplug disconnections.  The component list on the card
-      record is used to manage the proper releases of resources at
+      record is used to manage the correct release of resources at
       destruction. 
       </para>
 
         <constant>THIS_MODULE</constant>),
         and the size of extra-data space.  The last argument is used to
         allocate card-&gt;private_data for the
-        chip-specific data.  Note that this data
-        <emphasis>is</emphasis> allocated by
-        <function>snd_card_new()</function>.
+        chip-specific data.  Note that these data
+        are allocated by <function>snd_card_new()</function>.
       </para>
     </section>
 
       <title>Components</title>
       <para>
         After the card is created, you can attach the components
-      (devices) to the card instance. On ALSA driver, a component is
+      (devices) to the card instance. In an ALSA driver, a component is
       represented as a struct <structname>snd_device</structname> object.
       A component can be a PCM instance, a control interface, a raw
-      MIDI interface, etc.  Each of such instances has one component
+      MIDI interface, etc.  Each such instance has one component
       entry.
       </para>
 
       (<constant>SNDRV_DEV_XXX</constant>), the data pointer, and the
       callback pointers (<parameter>&amp;ops</parameter>). The
       device-level defines the type of components and the order of
-      registration and de-registration.  For most of components, the
+      registration and de-registration.  For most components, the
       device-level is already defined.  For a user-defined component,
       you can use <constant>SNDRV_DEV_LOWLEVEL</constant>.
       </para>
       <para>
       This function itself doesn't allocate the data space. The data
       must be allocated manually beforehand, and its pointer is passed
-      as the argument. This pointer is used as the identifier
-      (<parameter>chip</parameter> in the above example) for the
-      instance. 
+      as the argument. This pointer is used as the
+      (<parameter>chip</parameter> identifier in the above example)
+      for the instance. 
       </para>
 
       <para>
-        Each ALSA pre-defined component such as ac97 or pcm calls
+        Each pre-defined ALSA component such as ac97 and pcm calls
       <function>snd_device_new()</function> inside its
       constructor. The destructor for each component is defined in the
       callback pointers.  Hence, you don't need to take care of
       </para>
 
       <para>
-        If you would like to create your own component, you need to
-      set the destructor function to dev_free callback in
-      <parameter>ops</parameter>, so that it can be released
-      automatically via <function>snd_card_free()</function>. The
-      example will be shown later as an implementation of a
-      chip-specific data. 
+        If you wish to create your own component, you need to
+      set the destructor function to the dev_free callback in
+      the <parameter>ops</parameter>, so that it can be released
+      automatically via <function>snd_card_free()</function>.
+      The next example will show an implementation of chip-specific
+      data.
       </para>
     </section>
 
     <section id="card-management-chip-specific">
       <title>Chip-Specific Data</title>
       <para>
-      The chip-specific information, e.g. the i/o port address, its
+      Chip-specific information, e.g. the I/O port address, its
       resource pointer, or the irq number, is stored in the
       chip-specific record.
 
       </para>
 
       <para>
-        In general, there are two ways to allocate the chip record.
+        In general, there are two ways of allocating the chip record.
       </para>
 
       <section id="card-management-chip-specific-snd-card-new">
         <title>1. Allocating via <function>snd_card_new()</function>.</title>
         <para>
-          As mentioned above, you can pass the extra-data-length to the 4th argument of <function>snd_card_new()</function>, i.e.
+          As mentioned above, you can pass the extra-data-length
+         to the 4th argument of <function>snd_card_new()</function>, i.e.
 
           <informalexample>
             <programlisting>
             </programlisting>
           </informalexample>
 
-          whether struct <structname>mychip</structname> is the type of the chip record.
+          struct <structname>mychip</structname> is the type of the chip record.
         </para>
 
         <para>
       <title>Registration and Release</title>
       <para>
         After all components are assigned, register the card instance
-      by calling <function>snd_card_register()</function>. The access
-      to the device files are enabled at this point. That is, before
+      by calling <function>snd_card_register()</function>. Access
+      to the device files is enabled at this point. That is, before
       <function>snd_card_register()</function> is called, the
       components are safely inaccessible from external side. If this
       call fails, exit the probe function after releasing the card via
 
       <para>
         For releasing the card instance, you can call simply
-      <function>snd_card_free()</function>. As already mentioned, all
+      <function>snd_card_free()</function>. As mentioned earlier, all
       components are released automatically by this call. 
       </para>
 
         As further notes, the destructors (both
       <function>snd_mychip_dev_free</function> and
       <function>snd_mychip_free</function>) cannot be defined with
-      <parameter>__devexit</parameter> prefix, because they may be
+      the <parameter>__devexit</parameter> prefix, because they may be
       called from the constructor, too, at the false path. 
       </para>
 
 
 
 <!-- ****************************************************** -->
-<!-- PCI Resource Managements  -->
+<!-- PCI Resource Management  -->
 <!-- ****************************************************** -->
   <chapter id="pci-resource">
-    <title>PCI Resource Managements</title>
+    <title>PCI Resource Management</title>
 
     <section id="pci-resource-example">
       <title>Full Code Example</title>
       <para>
-        In this section, we'll finish the chip-specific constructor,
-      destructor and PCI entries. The example code is shown first,
+        In this section, we'll complete the chip-specific constructor,
+      destructor and PCI entries. Example code is shown first,
       below. 
 
         <example>
-          <title>PCI Resource Managements Example</title>
+          <title>PCI Resource Management Example</title>
           <programlisting>
 <![CDATA[
   struct mychip {
           /* release the irq */
           if (chip->irq >= 0)
                   free_irq(chip->irq, chip);
-          /* release the i/o ports & memory */
+          /* release the I/O ports & memory */
           pci_release_regions(chip->pci);
           /* disable the PCI entry */
           pci_disable_device(chip->pci);
           .remove = __devexit_p(snd_mychip_remove),
   };
 
-  /* initialization of the module */
+  /* module initialization */
   static int __init alsa_card_mychip_init(void)
   {
           return pci_register_driver(&driver);
   }
 
-  /* clean up the module */
+  /* module clean up */
   static void __exit alsa_card_mychip_exit(void)
   {
           pci_unregister_driver(&driver);
       </para>
 
       <para>
-        In the case of PCI devices, you have to call at first
-      <function>pci_enable_device()</function> function before
+        In the case of PCI devices, you first have to call
+      the <function>pci_enable_device()</function> function before
       allocating resources. Also, you need to set the proper PCI DMA
-      mask to limit the accessed i/o range. In some cases, you might
+      mask to limit the accessed I/O range. In some cases, you might
       need to call <function>pci_set_master()</function> function,
       too.
       </para>
     <section id="pci-resource-resource-allocation">
       <title>Resource Allocation</title>
       <para>
-        The allocation of I/O ports and irqs are done via standard kernel
+        The allocation of I/O ports and irqs is done via standard kernel
       functions. Unlike ALSA ver.0.5.x., there are no helpers for
       that. And these resources must be released in the destructor
       function (see below). Also, on ALSA 0.9.x, you don't need to
-      allocate (pseudo-)DMA for PCI like ALSA 0.5.x.
+      allocate (pseudo-)DMA for PCI like in ALSA 0.5.x.
       </para>
 
       <para>
-        Now assume that this PCI device has an I/O port with 8 bytes
+        Now assume that the PCI device has an I/O port with 8 bytes
         and an interrupt. Then struct <structname>mychip</structname> will have the
         following fields:
 
       </para>
 
       <para>
-        For an i/o port (and also a memory region), you need to have
+        For an I/O port (and also a memory region), you need to have
       the resource pointer for the standard resource management. For
       an irq, you have to keep only the irq number (integer). But you
       need to initialize this number as -1 before actual allocation,
       </para>
 
       <para>
-        The allocation of an i/o port is done like this:
+        The allocation of an I/O port is done like this:
 
         <informalexample>
           <programlisting>
 
       <para>
         <!-- obsolete -->
-        It will reserve the i/o port region of 8 bytes of the given
+        It will reserve the I/O port region of 8 bytes of the given
       PCI device. The returned value, chip-&gt;res_port, is allocated
       via <function>kmalloc()</function> by
       <function>request_region()</function>. The pointer must be
-      released via <function>kfree()</function>, but there is some
-      problem regarding this. This issue will be explained more below.
+      released via <function>kfree()</function>, but there is a
+      problem with this. This issue will be explained later.
       </para>
 
       <para>
       </para>
 
       <para>
-      On the PCI bus, the interrupts can be shared. Thus,
-      <constant>IRQF_SHARED</constant> is given as the interrupt flag of
+      On the PCI bus, interrupts can be shared. Thus,
+      <constant>IRQF_SHARED</constant> is used as the interrupt flag of
       <function>request_irq()</function>. 
       </para>
 
       </para>
 
       <para>
-        I won't define the detail of the interrupt handler at this
+        I won't give details about the interrupt handler at this
         point, but at least its appearance can be explained now. The
         interrupt handler looks usually like the following: 
 
         Now let's write the corresponding destructor for the resources
       above. The role of destructor is simple: disable the hardware
       (if already activated) and release the resources. So far, we
-      have no hardware part, so the disabling is not written here. 
+      have no hardware part, so the disabling code is not written here. 
       </para>
 
       <para>
-        For releasing the resources, <quote>check-and-release</quote>
+        To release the resources, the <quote>check-and-release</quote>
         method is a safer way. For the interrupt, do like this: 
 
         <informalexample>
       <para>
         When you requested I/O ports or memory regions via
        <function>pci_request_region()</function> or
-       <function>pci_request_regions()</function> like this example,
+       <function>pci_request_regions()</function> like in this example,
        release the resource(s) using the corresponding function,
        <function>pci_release_region()</function> or
        <function>pci_release_regions()</function>.
        or <function>request_mem_region</function>, you can release it via
        <function>release_resource()</function>.  Suppose that you keep
        the resource pointer returned from <function>request_region()</function>
-       in chip-&gt;res_port, the release procedure looks like below:
+       in chip-&gt;res_port, the release procedure looks like:
 
         <informalexample>
           <programlisting>
 
       <para>
       Don't forget to call <function>pci_disable_device()</function>
-      before all finished.
+      before the end.
       </para>
 
       <para>
 
       <para>
       Again, remember that you cannot
-      set <parameter>__devexit</parameter> prefix for this destructor. 
+      use the <parameter>__devexit</parameter> prefix for this destructor. 
       </para>
 
       <para>
-      We didn't implement the hardware-disabling part in the above.
+      We didn't implement the hardware disabling part in the above.
       If you need to do this, please note that the destructor may be
       called even before the initialization of the chip is completed.
-      It would be better to have a flag to skip the hardware-disabling
+      It would be better to have a flag to skip hardware disabling
       if the hardware was not initialized yet.
       </para>
 
       <function>snd_device_new()</function> with
       <constant>SNDRV_DEV_LOWLELVEL</constant> , its destructor is 
       called at the last.  That is, it is assured that all other
-      components like PCMs and controls have been already released.
-      You don't have to call stopping PCMs, etc. explicitly, but just
-      stop the hardware in the low-level.
+      components like PCMs and controls have already been released.
+      You don't have to stop PCMs, etc. explicitly, but just
+      call low-level hardware stopping.
       </para>
 
       <para>
         The management of a memory-mapped region is almost as same as
-        the management of an i/o port. You'll need three fields like
+        the management of an I/O port. You'll need three fields like
         the following: 
 
         <informalexample>
     <section id="pci-resource-entries">
       <title>PCI Entries</title>
       <para>
-        So far, so good. Let's finish the rest of missing PCI
-      stuffs. At first, we need a
+        So far, so good. Let's finish the missing PCI
+      stuff. At first, we need a
       <structname>pci_device_id</structname> table for this
       chipset. It's a table of PCI vendor/device ID number, and some
       masks. 
 
       <para>
         The first and second fields of
-      <structname>pci_device_id</structname> struct are the vendor and
-      device IDs. If you have nothing special to filter the matching
-      devices, you can use the rest of fields like above. The last
-      field of <structname>pci_device_id</structname> struct is a
+      the <structname>pci_device_id</structname> structure are the vendor and
+      device IDs. If you have no reason to filter the matching
+      devices, you can leave the remaining fields as above. The last
+      field of the <structname>pci_device_id</structname> struct contains
       private data for this entry. You can specify any value here, for
-      example, to tell the type of different operations per each
-      device IDs. Such an example is found in intel8x0 driver. 
+      example, to define specific operations for supported device IDs.
+      Such an example is found in the intel8x0 driver. 
       </para>
 
       <para>
 
       <para>
         The <structfield>probe</structfield> and
-      <structfield>remove</structfield> functions are what we already
-      defined in 
-      the previous sections. The <structfield>remove</structfield> should
-      be defined with 
+      <structfield>remove</structfield> functions have already
+      been defined in the previous sections.
+      The <structfield>remove</structfield> function should
+      be defined with the 
       <function>__devexit_p()</function> macro, so that it's not
       defined for built-in (and non-hot-pluggable) case. The
       <structfield>name</structfield> 
 
       <para>
         Oh, one thing was forgotten. If you have no exported symbols,
-        you need to declare it on 2.2 or 2.4 kernels (on 2.6 kernels
-        it's not necessary, though).
+        you need to declare it in 2.2 or 2.4 kernels (it's not necessary in 2.6 kernels).
 
         <informalexample>
           <programlisting>
 
       <para>
         For accessing to the PCM layer, you need to include
-      <filename>&lt;sound/pcm.h&gt;</filename> above all. In addition,
+      <filename>&lt;sound/pcm.h&gt;</filename> first. In addition,
       <filename>&lt;sound/pcm_params.h&gt;</filename> might be needed
       if you access to some functions related with hw_param. 
       </para>
         Each card device can have up to four pcm instances. A pcm
       instance corresponds to a pcm device file. The limitation of
       number of instances comes only from the available bit size of
-      the linux's device number. Once when 64bit device number is
-      used, we'll have more available pcm instances
+      the Linux's device numbers. Once when 64bit device number is
+      used, we'll have more pcm instances available
       </para>
 
       <para>
         A pcm instance consists of pcm playback and capture streams,
       and each pcm stream consists of one or more pcm substreams. Some
-      soundcard supports the multiple-playback function. For example,
+      soundcards support multiple playback functions. For example,
       emu10k1 has a PCM playback of 32 stereo substreams. In this case, at
       each open, a free substream is (usually) automatically chosen
       and opened. Meanwhile, when only one substream exists and it was
-      already opened, the succeeding open will result in the blocking
-      or the error with <constant>EAGAIN</constant> according to the
-      file open mode. But you don't have to know the detail in your
-      driver. The PCM middle layer will take all such jobs. 
+      already opened, the successful open will either block
+      or error with <constant>EAGAIN</constant> according to the
+      file open mode. But you don't have to care about such details in your
+      driver. The PCM middle layer will take care of such work.
       </para>
     </section>
 
     <section id="pcm-interface-constructor">
       <title>Constructor</title>
       <para>
-        A pcm instance is allocated by <function>snd_pcm_new()</function>
+        A pcm instance is allocated by the <function>snd_pcm_new()</function>
       function. It would be better to create a constructor for pcm,
       namely, 
 
       </para>
 
       <para>
-        The <function>snd_pcm_new()</function> function takes the four
+        The <function>snd_pcm_new()</function> function takes four
       arguments. The first argument is the card pointer to which this
       pcm is assigned, and the second is the ID string. 
       </para>
 
       <para>
         The third argument (<parameter>index</parameter>, 0 in the
-      above) is the index of this new pcm. It begins from zero. When
-      you will create more than one pcm instances, specify the
+      above) is the index of this new pcm. It begins from zero. If
+      you create more than one pcm instances, specify the
       different numbers in this argument. For example,
       <parameter>index</parameter> = 1 for the second PCM device.  
       </para>
 
       <para>
         The fourth and fifth arguments are the number of substreams
-      for playback and capture, respectively. Here both 1 are given in
-      the above example.  When no playback or no capture is available,
+      for playback and capture, respectively. Here 1 is used for
+      both arguments. When no playback or capture substreams are available,
       pass 0 to the corresponding argument.
       </para>
 
           </programlisting>
         </informalexample>
 
-        Each of callbacks is explained in the subsection 
+        All the callbacks are described in the
         <link linkend="pcm-interface-operators"><citetitle>
-        Operators</citetitle></link>.
+        Operators</citetitle></link> subsection.
       </para>
 
       <para>
-        After setting the operators, most likely you'd like to
+        After setting the operators, you probably will want to
         pre-allocate the buffer. For the pre-allocation, simply call
         the following: 
 
           </programlisting>
         </informalexample>
 
-        It will allocate up to 64kB buffer as default. The details of
-      buffer management will be described in the later section <link
+        It will allocate a buffer up to 64kB as default.
+      Buffer management details will be described in the later section <link
       linkend="buffer-and-memory"><citetitle>Buffer and Memory
       Management</citetitle></link>. 
       </para>
       <para>
         The destructor for a pcm instance is not always
       necessary. Since the pcm device will be released by the middle
-      layer code automatically, you don't have to call destructor
+      layer code automatically, you don't have to call the destructor
       explicitly.
       </para>
 
       <para>
-        The destructor would be necessary when you created some
-        special records internally and need to release them. In such a
+        The destructor would be necessary if you created
+        special records internally and needed to release them. In such a
         case, set the destructor function to
         pcm-&gt;private_free: 
 
          When the PCM substream is opened, a PCM runtime instance is
        allocated and assigned to the substream. This pointer is
        accessible via <constant>substream-&gt;runtime</constant>.
-       This runtime pointer holds the various information; it holds
-       the copy of hw_params and sw_params configurations, the buffer
-       pointers, mmap records, spinlocks, etc.  Almost everything you
-       need for controlling the PCM can be found there.
+       This runtime pointer holds most information you need
+       to control the PCM: the copy of hw_params and sw_params configurations, the buffer
+       pointers, mmap records, spinlocks, etc.
        </para>
 
        <para>
        The definition of runtime instance is found in
-       <filename>&lt;sound/pcm.h&gt;</filename>.  Here is the
-       copy from the file.
+       <filename>&lt;sound/pcm.h&gt;</filename>.  Here are
+       the contents of this file:
           <informalexample>
             <programlisting>
 <![CDATA[
@@ -2185,7 +2186,6 @@ struct _snd_pcm_runtime {
        struct timespec tstamp_mode;    /* mmap timestamp is updated */
        unsigned int period_step;
        unsigned int sleep_min;         /* min ticks to sleep */
-       snd_pcm_uframes_t xfer_align;   /* xfer size need to be a multiple */
        snd_pcm_uframes_t start_threshold;
        snd_pcm_uframes_t stop_threshold;
        snd_pcm_uframes_t silence_threshold; /* Silence filling happens when
@@ -2244,7 +2244,7 @@ struct _snd_pcm_runtime {
        <para>
          For the operators (callbacks) of each sound driver, most of
        these records are supposed to be read-only.  Only the PCM
-       middle-layer changes / updates these info.  The exceptions are
+       middle-layer changes / updates them.  The exceptions are
        the hardware description (hw), interrupt callbacks
        (transfer_ack_xxx), DMA buffer information, and the private
        data.  Besides, if you use the standard buffer allocation
@@ -2285,7 +2285,7 @@ struct _snd_pcm_runtime {
        </para>
 
        <para>
-         Typically, you'll have a hardware descriptor like below:
+         Typically, you'll have a hardware descriptor as below:
           <informalexample>
             <programlisting>
 <![CDATA[
@@ -2320,10 +2320,10 @@ struct _snd_pcm_runtime {
         <constant>SNDRV_PCM_INFO_XXX</constant>. Here, at least, you
         have to specify whether the mmap is supported and which
         interleaved format is supported.
-        When the mmap is supported, add
+        When the is supported, add the
         <constant>SNDRV_PCM_INFO_MMAP</constant> flag here. When the
         hardware supports the interleaved or the non-interleaved
-        format, <constant>SNDRV_PCM_INFO_INTERLEAVED</constant> or
+        formats, <constant>SNDRV_PCM_INFO_INTERLEAVED</constant> or
         <constant>SNDRV_PCM_INFO_NONINTERLEAVED</constant> flag must
         be set, respectively. If both are supported, you can set both,
         too. 
@@ -2331,7 +2331,7 @@ struct _snd_pcm_runtime {
 
         <para>
           In the above example, <constant>MMAP_VALID</constant> and
-        <constant>BLOCK_TRANSFER</constant> are specified for OSS mmap
+        <constant>BLOCK_TRANSFER</constant> are specified for the OSS mmap
         mode. Usually both are set. Of course,
         <constant>MMAP_VALID</constant> is set only if the mmap is
         really supported. 
@@ -2345,11 +2345,11 @@ struct _snd_pcm_runtime {
         <quote>pause</quote> operation, while the
         <constant>RESUME</constant> bit means that the pcm supports
         the full <quote>suspend/resume</quote> operation.
-       If <constant>PAUSE</constant> flag is set,
+       If the <constant>PAUSE</constant> flag is set,
        the <structfield>trigger</structfield> callback below
         must handle the corresponding (pause push/release) commands.
        The suspend/resume trigger commands can be defined even without
-       <constant>RESUME</constant> flag.  See <link
+       the <constant>RESUME</constant> flag.  See <link
        linkend="power-management"><citetitle>
        Power Management</citetitle></link> section for details.
         </para>
@@ -2382,7 +2382,7 @@ struct _snd_pcm_runtime {
         <constant>CONTINUOUS</constant> bit additionally.
         The pre-defined rate bits are provided only for typical
        rates. If your chip supports unconventional rates, you need to add
-        <constant>KNOT</constant> bit and set up the hardware
+        the <constant>KNOT</constant> bit and set up the hardware
         constraint manually (explained later).
         </para>
        </listitem>
@@ -2390,8 +2390,8 @@ struct _snd_pcm_runtime {
        <listitem>
        <para>
        <structfield>rate_min</structfield> and
-       <structfield>rate_max</structfield> define the minimal and
-       maximal sample rate.  This should correspond somehow to
+       <structfield>rate_max</structfield> define the minimum and
+       maximum sample rate.  This should correspond somehow to
        <structfield>rates</structfield> bits.
        </para>
        </listitem>
@@ -2400,7 +2400,7 @@ struct _snd_pcm_runtime {
        <para>
        <structfield>channel_min</structfield> and
        <structfield>channel_max</structfield> 
-       define, as you might already expected, the minimal and maximal
+       define, as you might already expected, the minimum and maximum
        number of channels.
        </para>
        </listitem>
@@ -2408,21 +2408,21 @@ struct _snd_pcm_runtime {
        <listitem>
        <para>
        <structfield>buffer_bytes_max</structfield> defines the
-       maximal buffer size in bytes.  There is no
+       maximum buffer size in bytes.  There is no
        <structfield>buffer_bytes_min</structfield> field, since
-       it can be calculated from the minimal period size and the
-       minimal number of periods.
+       it can be calculated from the minimum period size and the
+       minimum number of periods.
        Meanwhile, <structfield>period_bytes_min</structfield> and
-       define the minimal and maximal size of the period in bytes.
+       define the minimum and maximum size of the period in bytes.
        <structfield>periods_max</structfield> and
-       <structfield>periods_min</structfield> define the maximal and
-       minimal number of periods in the buffer.
+       <structfield>periods_min</structfield> define the maximum and
+       minimum number of periods in the buffer.
         </para>
 
        <para>
-       The <quote>period</quote> is a term, that corresponds to
-       fragment in the OSS world.  The period defines the size at
-       which the PCM interrupt is generated. This size strongly
+       The <quote>period</quote> is a term that corresponds to
+       a fragment in the OSS world. The period defines the size at
+       which a PCM interrupt is generated. This size strongly
        depends on the hardware. 
        Generally, the smaller period size will give you more
        interrupts, that is, more controls. 
@@ -2435,8 +2435,8 @@ struct _snd_pcm_runtime {
        <listitem>
        <para>
        There is also a field <structfield>fifo_size</structfield>.
-       This specifies the size of the hardware FIFO, but it's not
-       used currently in the driver nor in the alsa-lib.  So, you
+       This specifies the size of the hardware FIFO, but currently it
+       is neither used in the driver nor in the alsa-lib.  So, you
        can ignore this field.
        </para>
        </listitem>
@@ -2450,7 +2450,7 @@ struct _snd_pcm_runtime {
        Ok, let's go back again to the PCM runtime records.
        The most frequently referred records in the runtime instance are
        the PCM configurations.
-       The PCM configurations are stored on runtime instance
+       The PCM configurations are stored in the runtime instance
        after the application sends <type>hw_params</type> data via
        alsa-lib.  There are many fields copied from hw_params and
        sw_params structs.  For example,
@@ -2461,11 +2461,11 @@ struct _snd_pcm_runtime {
 
        <para>
        One thing to be noted is that the configured buffer and period
-       sizes are stored in <quote>frames</quote> in the runtime
+       sizes are stored in <quote>frames</quote> in the runtime.
         In the ALSA world, 1 frame = channels * samples-size.
        For conversion between frames and bytes, you can use the
-       helper functions, <function>frames_to_bytes()</function> and
-          <function>bytes_to_frames()</function>. 
+       <function>frames_to_bytes()</function> and
+          <function>bytes_to_frames()</function> helper functions
           <informalexample>
             <programlisting>
 <![CDATA[
@@ -2515,7 +2515,7 @@ struct _snd_pcm_runtime {
        <structfield>dma_area</structfield> is necessary when the
        buffer is mmapped.  If your driver doesn't support mmap, this
        field is not necessary.  <structfield>dma_addr</structfield>
-       is also not mandatory.  You can use
+       is also optional.  You can use
        <structfield>dma_private</structfield> as you like, too.
        </para>
        </section>
@@ -2524,14 +2524,14 @@ struct _snd_pcm_runtime {
        <title>Running Status</title>
        <para>
        The running status can be referred via <constant>runtime-&gt;status</constant>.
-       This is the pointer to struct <structname>snd_pcm_mmap_status</structname>
+       This is the pointer to the struct <structname>snd_pcm_mmap_status</structname>
        record.  For example, you can get the current DMA hardware
        pointer via <constant>runtime-&gt;status-&gt;hw_ptr</constant>.
        </para>
 
        <para>
        The DMA application pointer can be referred via
-       <constant>runtime-&gt;control</constant>, which points
+       <constant>runtime-&gt;control</constant>, which points to the
        struct <structname>snd_pcm_mmap_control</structname> record.
        However, accessing directly to this value is not recommended.
        </para>
@@ -2542,14 +2542,14 @@ struct _snd_pcm_runtime {
        <para>
        You can allocate a record for the substream and store it in
        <constant>runtime-&gt;private_data</constant>.  Usually, this
-       done in
+       is done in
        <link linkend="pcm-interface-operators-open-callback"><citetitle>
        the open callback</citetitle></link>.
        Don't mix this with <constant>pcm-&gt;private_data</constant>.
-       The <constant>pcm-&gt;private_data</constant> usually points the
+       The <constant>pcm-&gt;private_data</constant> usually points to the
        chip instance assigned statically at the creation of PCM, while the 
-       <constant>runtime-&gt;private_data</constant> points a dynamic
-       data created at the PCM open callback.
+       <constant>runtime-&gt;private_data</constant> points to a dynamic
+       data structure created at the PCM open callback.
 
           <informalexample>
             <programlisting>
@@ -2579,7 +2579,7 @@ struct _snd_pcm_runtime {
        <para>
        The field <structfield>transfer_ack_begin</structfield> and
        <structfield>transfer_ack_end</structfield> are called at
-       the beginning and the end of
+       the beginning and at the end of
        <function>snd_pcm_period_elapsed()</function>, respectively. 
        </para>
        </section>
@@ -2589,17 +2589,18 @@ struct _snd_pcm_runtime {
     <section id="pcm-interface-operators">
       <title>Operators</title>
       <para>
-        OK, now let me explain the detail of each pcm callback
+        OK, now let me give details about each pcm callback
       (<parameter>ops</parameter>). In general, every callback must
-      return 0 if successful, or a negative number with the error
-      number such as <constant>-EINVAL</constant> at any
-      error. 
+      return 0 if successful, or a negative error number
+      such as <constant>-EINVAL</constant>. To choose an appropriate
+      error number, it is advised to check what value other parts of
+      the kernel return when the same kind of request fails.
       </para>
 
       <para>
         The callback function takes at least the argument with
-        <structname>snd_pcm_substream</structname> pointer. For retrieving the
-        chip record from the given substream instance, you can use the
+        <structname>snd_pcm_substream</structname> pointer. To retrieve
+        the chip record from the given substream instance, you can use the
         following macro. 
 
         <informalexample>
@@ -2616,7 +2617,7 @@ struct _snd_pcm_runtime {
        The macro reads <constant>substream-&gt;private_data</constant>,
        which is a copy of <constant>pcm-&gt;private_data</constant>.
        You can override the former if you need to assign different data
-       records per PCM substream.  For example, cmi8330 driver assigns
+       records per PCM substream.  For example, the cmi8330 driver assigns
        different private_data for playback and capture directions,
        because it uses two different codecs (SB- and AD-compatible) for
        different directions.
@@ -2709,7 +2710,7 @@ struct _snd_pcm_runtime {
       <section id="pcm-interface-operators-ioctl-callback">
         <title>ioctl callback</title>
         <para>
-          This is used for any special action to pcm ioctls. But
+          This is used for any special call to pcm ioctls. But
         usually you can pass a generic ioctl callback, 
         <function>snd_pcm_lib_ioctl</function>.
         </para>
@@ -2726,9 +2727,6 @@ struct _snd_pcm_runtime {
 ]]>
             </programlisting>
           </informalexample>
-
-          This and <structfield>hw_free</structfield> callbacks exist
-        only on ALSA 0.9.x. 
         </para>
 
         <para>
@@ -2740,13 +2738,13 @@ struct _snd_pcm_runtime {
         </para>
 
         <para>
-          Many hardware set-up should be done in this callback,
+          Many hardware setups should be done in this callback,
         including the allocation of buffers. 
         </para>
 
         <para>
           Parameters to be initialized are retrieved by
-          <function>params_xxx()</function> macros. For allocating a
+          <function>params_xxx()</function> macros. To allocate
           buffer, you can call a helper function, 
 
           <informalexample>
@@ -2772,8 +2770,8 @@ struct _snd_pcm_runtime {
         </para>
 
         <para>
-          Thus, you need to take care not to allocate the same buffers
-        many times, which will lead to memory leak!  Calling the
+          Thus, you need to be careful not to allocate the same buffers
+        many times, which will lead to memory leaks!  Calling the
         helper function above many times is OK. It will release the
         previous buffer automatically when it was already allocated. 
         </para>
@@ -2782,7 +2780,7 @@ struct _snd_pcm_runtime {
           Another note is that this callback is non-atomic
         (schedulable). This is important, because the
         <structfield>trigger</structfield> callback 
-        is atomic (non-schedulable). That is, mutex or any
+        is atomic (non-schedulable). That is, mutexes or any
         schedule-related functions are not available in
         <structfield>trigger</structfield> callback.
        Please see the subsection
@@ -2843,15 +2841,15 @@ struct _snd_pcm_runtime {
         <quote>prepared</quote>. You can set the format type, sample
         rate, etc. here. The difference from
         <structfield>hw_params</structfield> is that the 
-        <structfield>prepare</structfield> callback will be called at each
+        <structfield>prepare</structfield> callback will be called each
         time 
         <function>snd_pcm_prepare()</function> is called, i.e. when
-        recovered after underruns, etc. 
+        recovering after underruns, etc. 
         </para>
 
         <para>
-       Note that this callback became non-atomic since the recent version.
-       You can use schedule-related functions safely in this callback now.
+       Note that this callback is now non-atomic.
+       You can use schedule-related functions safely in this callback.
         </para>
 
         <para>
@@ -2871,7 +2869,7 @@ struct _snd_pcm_runtime {
 
         <para>
           Be careful that this callback will be called many times at
-        each set up, too. 
+        each setup, too. 
         </para>
       </section>
 
@@ -2893,7 +2891,7 @@ struct _snd_pcm_runtime {
           Which action is specified in the second argument,
           <constant>SNDRV_PCM_TRIGGER_XXX</constant> in
           <filename>&lt;sound/pcm.h&gt;</filename>. At least,
-          <constant>START</constant> and <constant>STOP</constant>
+          the <constant>START</constant> and <constant>STOP</constant>
           commands must be defined in this callback. 
 
           <informalexample>
@@ -2915,8 +2913,8 @@ struct _snd_pcm_runtime {
         </para>
 
         <para>
-          When the pcm supports the pause operation (given in info
-        field of the hardware table), <constant>PAUSE_PUSE</constant>
+          When the pcm supports the pause operation (given in the info
+        field of the hardware table), the <constant>PAUSE_PUSE</constant>
         and <constant>PAUSE_RELEASE</constant> commands must be
         handled here, too. The former is the command to pause the pcm,
         and the latter to restart the pcm again. 
@@ -2925,21 +2923,21 @@ struct _snd_pcm_runtime {
         <para>
           When the pcm supports the suspend/resume operation,
        regardless of full or partial suspend/resume support,
-        <constant>SUSPEND</constant> and <constant>RESUME</constant>
+        the <constant>SUSPEND</constant> and <constant>RESUME</constant>
         commands must be handled, too.
         These commands are issued when the power-management status is
         changed.  Obviously, the <constant>SUSPEND</constant> and
-        <constant>RESUME</constant>
-        do suspend and resume of the pcm substream, and usually, they
-        are identical with <constant>STOP</constant> and
+        <constant>RESUME</constant> commands
+        suspend and resume the pcm substream, and usually, they
+        are identical to the <constant>STOP</constant> and
         <constant>START</constant> commands, respectively.
-         See <link linkend="power-management"><citetitle>
+         See the <link linkend="power-management"><citetitle>
        Power Management</citetitle></link> section for details.
         </para>
 
         <para>
           As mentioned, this callback is atomic.  You cannot call
-         the function going to sleep.
+         functions which may sleep.
          The trigger callback should be as minimal as possible,
          just really triggering the DMA.  The other stuff should be
          initialized hw_params and prepare callbacks properly
@@ -2960,8 +2958,8 @@ struct _snd_pcm_runtime {
 
           This callback is called when the PCM middle layer inquires
         the current hardware position on the buffer. The position must
-        be returned in frames (which was in bytes on ALSA 0.5.x),
-        ranged from 0 to buffer_size - 1.
+        be returned in frames,
+        ranging from 0 to buffer_size - 1.
         </para>
 
         <para>
@@ -2983,7 +2981,7 @@ struct _snd_pcm_runtime {
         <para>
           These callbacks are not mandatory, and can be omitted in
         most cases. These callbacks are used when the hardware buffer
-        cannot be on the normal memory space. Some chips have their
+        cannot be in the normal memory space. Some chips have their
         own buffer on the hardware which is not mappable. In such a
         case, you have to transfer the data manually from the memory
         buffer to the hardware buffer. Or, if the buffer is
@@ -3018,8 +3016,8 @@ struct _snd_pcm_runtime {
         <title>page callback</title>
 
         <para>
-          This callback is also not mandatory. This callback is used
-        mainly for the non-contiguous buffer. The mmap calls this
+          This callback is optional too. This callback is used
+        mainly for non-contiguous buffers. The mmap calls this
         callback to get the page address. Some examples will be
         explained in the later section <link
         linkend="buffer-and-memory"><citetitle>Buffer and Memory
@@ -3035,7 +3033,7 @@ struct _snd_pcm_runtime {
       role of PCM interrupt handler in the sound driver is to update
       the buffer position and to tell the PCM middle layer when the
       buffer position goes across the prescribed period size. To
-      inform this, call <function>snd_pcm_period_elapsed()</function>
+      inform this, call the <function>snd_pcm_period_elapsed()</function>
       function. 
       </para>
 
@@ -3072,7 +3070,7 @@ struct _snd_pcm_runtime {
         </para>
 
         <para>
-          A typical coding would be like:
+          Typical code would be like:
 
           <example>
            <title>Interrupt Handler Case #1</title>
@@ -3101,21 +3099,21 @@ struct _snd_pcm_runtime {
       </section>
 
       <section id="pcm-interface-interrupt-handler-timer">
-        <title>High-frequent timer interrupts</title>
+        <title>High frequency timer interrupts</title>
         <para>
-       This is the case when the hardware doesn't generate interrupts
-        at the period boundary but do timer-interrupts at the fixed
+       This happense when the hardware doesn't generate interrupts
+        at the period boundary but issues timer interrupts at a fixed
         timer rate (e.g. es1968 or ymfpci drivers). 
         In this case, you need to check the current hardware
-        position and accumulates the processed sample length at each
-        interrupt.  When the accumulated size overcomes the period
+        position and accumulate the processed sample length at each
+        interrupt.  When the accumulated size exceeds the period
         size, call 
         <function>snd_pcm_period_elapsed()</function> and reset the
         accumulator. 
         </para>
 
         <para>
-          A typical coding would be like the following.
+          Typical code would be like the following.
 
           <example>
            <title>Interrupt Handler Case #2</title>
@@ -3178,32 +3176,33 @@ struct _snd_pcm_runtime {
     <section id="pcm-interface-atomicity">
       <title>Atomicity</title>
       <para>
-      One of the most important (and thus difficult to debug) problem
-      on the kernel programming is the race condition.
-      On linux kernel, usually it's solved via spin-locks or
-      semaphores.  In general, if the race condition may
-      happen in the interrupt handler, it's handled as atomic, and you
-      have to use spinlock for protecting the critical session.  If it
-      never happens in the interrupt and it may take relatively long
-      time, you should use semaphore.
+      One of the most important (and thus difficult to debug) problems
+      in kernel programming are race conditions.
+      In the Linux kernel, they are usually avoided via spin-locks, mutexes
+      or semaphores.  In general, if a race condition can happen
+      in an interrupt handler, it has to be managed atomically, and you
+      have to use a spinlock to protect the critical session. If the
+      critical section is not in interrupt handler code and
+      if taking a relatively long time to execute is acceptable, you
+      should use mutexes or semaphores instead.
       </para>
 
       <para>
       As already seen, some pcm callbacks are atomic and some are
-      not.  For example, <parameter>hw_params</parameter> callback is
+      not.  For example, the <parameter>hw_params</parameter> callback is
       non-atomic, while <parameter>trigger</parameter> callback is
       atomic.  This means, the latter is called already in a spinlock
       held by the PCM middle layer. Please take this atomicity into
-      account when you use a spinlock or a semaphore in the callbacks.
+      account when you choose a locking scheme in the callbacks.
       </para>
 
       <para>
       In the atomic callbacks, you cannot use functions which may call
       <function>schedule</function> or go to
-      <function>sleep</function>.  The semaphore and mutex do sleep,
+      <function>sleep</function>.  Semaphores and mutexes can sleep,
       and hence they cannot be used inside the atomic callbacks
       (e.g. <parameter>trigger</parameter> callback).
-      For taking a certain delay in such a callback, please use
+      To implement some delay in such a callback, please use
       <function>udelay()</function> or <function>mdelay()</function>.
       </para>
 
@@ -3257,7 +3256,7 @@ struct _snd_pcm_runtime {
 
       <para>
         There are many different constraints.
-        Look in <filename>sound/pcm.h</filename> for a complete list.
+        Look at <filename>sound/pcm.h</filename> for a complete list.
         You can even define your own constraint rules.
         For example, let's suppose my_chip can manage a substream of 1 channel
         if and only if the format is S16_LE, otherwise it supports any format
@@ -3346,7 +3345,7 @@ struct _snd_pcm_runtime {
       </para>
 
       <para>
-        I won't explain more details here, rather I
+        I won't give more details here, rather I
         would like to say, <quote>Luke, use the source.</quote>
       </para>
     </section>
@@ -3364,10 +3363,9 @@ struct _snd_pcm_runtime {
       <title>General</title>
       <para>
         The control interface is used widely for many switches,
-      sliders, etc. which are accessed from the user-space. Its most
-      important use is the mixer interface. In other words, on ALSA
-      0.9.x, all the mixer stuff is implemented on the control kernel
-      API (while there was an independent mixer kernel API on 0.5.x). 
+      sliders, etc. which are accessed from user-space. Its most
+      important use is the mixer interface. In other words, since ALSA
+      0.9.x, all the mixer stuff is implemented on the control kernel API.
       </para>
 
       <para>
@@ -3379,14 +3377,15 @@ struct _snd_pcm_runtime {
       <para>
         The control API is defined in
       <filename>&lt;sound/control.h&gt;</filename>.
-      Include this file if you add your own controls.
+      Include this file if you want to add your own controls.
       </para>
     </section>
 
     <section id="control-interface-definition">
       <title>Definition of Controls</title>
       <para>
-        For creating a new control, you need to define the three
+        To create a new control, you need to define the
+       following three
       callbacks: <structfield>info</structfield>,
       <structfield>get</structfield> and
       <structfield>put</structfield>. Then, define a
@@ -3414,13 +3413,13 @@ struct _snd_pcm_runtime {
       <para>
         Most likely the control is created via
       <function>snd_ctl_new1()</function>, and in such a case, you can
-      add <parameter>__devinitdata</parameter> prefix to the
-      definition like above. 
+      add the <parameter>__devinitdata</parameter> prefix to the
+      definition as above. 
       </para>
 
       <para>
-        The <structfield>iface</structfield> field specifies the type of
-      the control, <constant>SNDRV_CTL_ELEM_IFACE_XXX</constant>, which
+        The <structfield>iface</structfield> field specifies the control
+      type, <constant>SNDRV_CTL_ELEM_IFACE_XXX</constant>, which
       is usually <constant>MIXER</constant>.
       Use <constant>CARD</constant> for global controls that are not
       logically part of the mixer.
@@ -3435,12 +3434,11 @@ struct _snd_pcm_runtime {
 
       <para>
         The <structfield>name</structfield> is the name identifier
-      string. On ALSA 0.9.x, the control name is very important,
+      string. Since ALSA 0.9.x, the control name is very important,
       because its role is classified from its name. There are
       pre-defined standard control names. The details are described in
-      the subsection
-      <link linkend="control-interface-control-names"><citetitle>
-      Control Names</citetitle></link>.
+      the <link linkend="control-interface-control-names"><citetitle>
+      Control Names</citetitle></link> subsection.
       </para>
 
       <para>
@@ -3456,15 +3454,15 @@ struct _snd_pcm_runtime {
         The <structfield>access</structfield> field contains the access
       type of this control. Give the combination of bit masks,
       <constant>SNDRV_CTL_ELEM_ACCESS_XXX</constant>, there.
-      The detailed will be explained in the subsection
-      <link linkend="control-interface-access-flags"><citetitle>
-      Access Flags</citetitle></link>.
+      The details will be explained in
+      the <link linkend="control-interface-access-flags"><citetitle>
+      Access Flags</citetitle></link> subsection.
       </para>
 
       <para>
         The <structfield>private_value</structfield> field contains
       an arbitrary long integer value for this record. When using
-      generic <structfield>info</structfield>,
+      the generic <structfield>info</structfield>,
       <structfield>get</structfield> and
       <structfield>put</structfield> callbacks, you can pass a value 
       through this field. If several small numbers are necessary, you can
@@ -3489,7 +3487,7 @@ struct _snd_pcm_runtime {
     <section id="control-interface-control-names">
       <title>Control Names</title>
       <para>
-        There are some standards for defining the control names. A
+        There are some standards to define the control names. A
       control is usually defined from the three parts as
       <quote>SOURCE DIRECTION FUNCTION</quote>. 
       </para>
@@ -3497,7 +3495,7 @@ struct _snd_pcm_runtime {
       <para>
         The first, <constant>SOURCE</constant>, specifies the source
       of the control, and is a string such as <quote>Master</quote>,
-      <quote>PCM</quote>, <quote>CD</quote> or
+      <quote>PCM</quote>, <quote>CD</quote> and
       <quote>Line</quote>. There are many pre-defined sources. 
       </para>
 
@@ -3575,22 +3573,22 @@ struct _snd_pcm_runtime {
       <title>Access Flags</title>
 
       <para>
-      The access flag is the bit-flags which specifies the access type
+      The access flag is the bitmask which specifies the access type
       of the given control.  The default access type is
       <constant>SNDRV_CTL_ELEM_ACCESS_READWRITE</constant>, 
       which means both read and write are allowed to this control.
       When the access flag is omitted (i.e. = 0), it is
-      regarded as <constant>READWRITE</constant> access as default. 
+      considered as <constant>READWRITE</constant> access as default. 
       </para>
 
       <para>
       When the control is read-only, pass
       <constant>SNDRV_CTL_ELEM_ACCESS_READ</constant> instead.
       In this case, you don't have to define
-      <structfield>put</structfield> callback.
+      the <structfield>put</structfield> callback.
       Similarly, when the control is write-only (although it's a rare
-      case), you can use <constant>WRITE</constant> flag instead, and
-      you don't need <structfield>get</structfield> callback.
+      case), you can use the <constant>WRITE</constant> flag instead, and
+      you don't need the <structfield>get</structfield> callback.
       </para>
 
       <para>
@@ -3598,15 +3596,15 @@ struct _snd_pcm_runtime {
       <constant>VOLATILE</constant> flag should be given.  This means
       that the control may be changed without
       <link linkend="control-interface-change-notification"><citetitle>
-      notification</citetitle></link>.  Applications should poll such
+      notification</citetitle></link>. Applications should poll such
       a control constantly.
       </para>
 
       <para>
       When the control is inactive, set
-      <constant>INACTIVE</constant> flag, too.
+      the <constant>INACTIVE</constant> flag, too.
       There are <constant>LOCK</constant> and
-      <constant>OWNER</constant> flags for changing the write
+      <constant>OWNER</constant> flags to change the write
       permissions.
       </para>
 
@@ -3619,10 +3617,10 @@ struct _snd_pcm_runtime {
         <title>info callback</title>
         <para>
           The <structfield>info</structfield> callback is used to get
-        the detailed information of this control. This must store the
+        detailed information on this control. This must store the
         values of the given struct <structname>snd_ctl_elem_info</structname>
         object. For example, for a boolean control with a single
-        element will be
+        element: 
 
           <example>
            <title>Example of info callback</title>
@@ -3653,7 +3651,7 @@ struct _snd_pcm_runtime {
         volume would have count = 2. The
         <structfield>value</structfield> field is a union, and 
         the values stored are depending on the type. The boolean and
-        integer are identical. 
+        integer types are identical. 
         </para>
 
         <para>
@@ -3684,7 +3682,7 @@ struct _snd_pcm_runtime {
         </para>
 
         <para>
-         Some common info callbacks are prepared for easy use:
+         Some common info callbacks are available for your convenience:
        <function>snd_ctl_boolean_mono_info()</function> and
        <function>snd_ctl_boolean_stereo_info()</function>.
        Obviously, the former is an info callback for a mono channel
@@ -3699,7 +3697,7 @@ struct _snd_pcm_runtime {
 
         <para>
           This callback is used to read the current value of the
-        control and to return to the user-space. 
+        control and to return to user-space. 
         </para>
 
         <para>
@@ -3722,11 +3720,11 @@ struct _snd_pcm_runtime {
         </para>
 
         <para>
-       The <structfield>value</structfield> field is depending on
-        the type of control as well as on info callback.  For example,
+       The <structfield>value</structfield> field depends on 
+        the type of control as well as on the info callback.  For example,
        the sb driver uses this field to store the register offset,
         the bit-shift and the bit-mask.  The
-        <structfield>private_value</structfield> is set like
+        <structfield>private_value</structfield> field is set as follows:
           <informalexample>
             <programlisting>
 <![CDATA[
@@ -3752,7 +3750,8 @@ struct _snd_pcm_runtime {
        </para>
 
        <para>
-       In <structfield>get</structfield> callback, you have to fill all the elements if the
+       In the <structfield>get</structfield> callback,
+       you have to fill all the elements if the
         control has more than one elements,
         i.e. <structfield>count</structfield> &gt; 1.
        In the example above, we filled only one element
@@ -3765,7 +3764,7 @@ struct _snd_pcm_runtime {
         <title>put callback</title>
 
         <para>
-          This callback is used to write a value from the user-space.
+          This callback is used to write a value from user-space.
         </para>
 
         <para>
@@ -3799,7 +3798,7 @@ struct _snd_pcm_runtime {
         </para>
 
         <para>
-       Like <structfield>get</structfield> callback,
+       As in the <structfield>get</structfield> callback,
        when the control has more than one elements,
        all elements must be evaluated in this callback, too.
         </para>
@@ -3817,7 +3816,7 @@ struct _snd_pcm_runtime {
       <title>Constructor</title>
       <para>
         When everything is ready, finally we can create a new
-      control. For creating a control, there are two functions to be
+      control. To create a control, there are two functions to be
       called, <function>snd_ctl_new1()</function> and
       <function>snd_ctl_add()</function>. 
       </para>
@@ -3839,14 +3838,14 @@ struct _snd_pcm_runtime {
       struct <structname>snd_kcontrol_new</structname> object defined above, and chip
       is the object pointer to be passed to
       kcontrol-&gt;private_data 
-      which can be referred in callbacks. 
+      which can be referred to in callbacks. 
       </para>
 
       <para>
         <function>snd_ctl_new1()</function> allocates a new
       <structname>snd_kcontrol</structname> instance (that's why the definition
       of <parameter>my_control</parameter> can be with
-      <parameter>__devinitdata</parameter> 
+      the <parameter>__devinitdata</parameter> 
       prefix), and <function>snd_ctl_add</function> assigns the given
       control component to the card. 
       </para>
@@ -3941,7 +3940,7 @@ struct _snd_pcm_runtime {
       <title>General</title>
       <para>
         The ALSA AC97 codec layer is a well-defined one, and you don't
-      have to write many codes to control it. Only low-level control
+      have to write much code to control it. Only low-level control
       routines are necessary. The AC97 codec API is defined in
       <filename>&lt;sound/ac97_codec.h&gt;</filename>. 
       </para>
@@ -4004,7 +4003,7 @@ struct _snd_pcm_runtime {
     <section id="api-ac97-constructor">
       <title>Constructor</title>
       <para>
-        For creating an ac97 instance, first call <function>snd_ac97_bus</function>
+        To create an ac97 instance, first call <function>snd_ac97_bus</function>
       with an <type>ac97_bus_ops_t</type> record with callback functions.
 
         <informalexample>
@@ -4042,12 +4041,12 @@ struct _snd_pcm_runtime {
           </programlisting>
         </informalexample>
 
-        where chip-&gt;ac97 is the pointer of a newly created
+        where chip-&gt;ac97 is a pointer to a newly created
         <type>ac97_t</type> instance.
         In this case, the chip pointer is set as the private data, so that
         the read/write callback functions can refer to this chip instance.
         This instance is not necessarily stored in the chip
-       record.  When you need to change the register values from the
+       record.  If you need to change the register values from the
         driver, or need the suspend/resume of ac97 codecs, keep this
         pointer to pass to the corresponding functions.
       </para>
@@ -4098,7 +4097,7 @@ struct _snd_pcm_runtime {
       </para>
 
       <para>
-      These callbacks are non-atomic like the callbacks of control API.
+      These callbacks are non-atomic like the control API callbacks.
       </para>
 
       <para>
@@ -4110,14 +4109,14 @@ struct _snd_pcm_runtime {
 
       <para>
         The <structfield>reset</structfield> callback is used to reset
-      the codec. If the chip requires a special way of reset, you can
+      the codec. If the chip requires a special kind of reset, you can
       define this callback. 
       </para>
 
       <para>
-        The <structfield>wait</structfield> callback is used for a
-      certain wait at the standard initialization of the codec. If the
-      chip requires the extra wait-time, define this callback. 
+        The <structfield>wait</structfield> callback is used to
+      add some waiting time in the standard initialization of the codec. If the
+      chip requires the extra waiting time, define this callback. 
       </para>
 
       <para>
@@ -4172,7 +4171,7 @@ struct _snd_pcm_runtime {
 
       <para>
         <function>snd_ac97_update_bits()</function> is used to update
-        some bits of the given register.  
+        some bits in the given register.  
 
         <informalexample>
           <programlisting>
@@ -4185,7 +4184,7 @@ struct _snd_pcm_runtime {
 
       <para>
         Also, there is a function to change the sample rate (of a
-        certain register such as
+        given register such as
         <constant>AC97_PCM_FRONT_DAC_RATE</constant>) when VRA or
         DRA is supported by the codec:
         <function>snd_ac97_set_rate()</function>. 
@@ -4200,11 +4199,11 @@ struct _snd_pcm_runtime {
       </para>
 
       <para>
-        The following registers are available for setting the rate:
+        The following registers are available to set the rate:
       <constant>AC97_PCM_MIC_ADC_RATE</constant>,
       <constant>AC97_PCM_FRONT_DAC_RATE</constant>,
       <constant>AC97_PCM_LR_ADC_RATE</constant>,
-      <constant>AC97_SPDIF</constant>. When the
+      <constant>AC97_SPDIF</constant>. When
       <constant>AC97_SPDIF</constant> is specified, the register is
       not really changed but the corresponding IEC958 status bits will
       be updated. 
@@ -4214,12 +4213,11 @@ struct _snd_pcm_runtime {
     <section id="api-ac97-clock-adjustment">
       <title>Clock Adjustment</title>
       <para>
-        On some chip, the clock of the codec isn't 48000 but using a
+        In some chips, the clock of the codec isn't 48000 but using a
       PCI clock (to save a quartz!). In this case, change the field
       bus-&gt;clock to the corresponding
       value. For example, intel8x0 
-      and es1968 drivers have the auto-measurement function of the
-      clock. 
+      and es1968 drivers have their own function to read from the clock.
       </para>
     </section>
 
@@ -4239,15 +4237,13 @@ struct _snd_pcm_runtime {
         When there are several codecs on the same card, you need to
       call <function>snd_ac97_mixer()</function> multiple times with
       ac97.num=1 or greater. The <structfield>num</structfield> field
-      specifies the codec 
-      number. 
+      specifies the codec number. 
       </para>
 
       <para>
-        If you have set up multiple codecs, you need to either write
+        If you set up multiple codecs, you either need to write
       different callbacks for each codec or check
-      ac97-&gt;num in the 
-      callback routines. 
+      ac97-&gt;num in the callback routines. 
       </para>
     </section>
 
@@ -4271,7 +4267,7 @@ struct _snd_pcm_runtime {
       </para>
 
       <para>
-        Some soundchips have similar but a little bit different
+        Some soundchips have a similar but slightly different
       implementation of mpu401 stuff. For example, emu10k1 has its own
       mpu401 routines. 
       </para>
@@ -4280,7 +4276,7 @@ struct _snd_pcm_runtime {
     <section id="midi-interface-constructor">
       <title>Constructor</title>
       <para>
-        For creating a rawmidi object, call
+        To create a rawmidi object, call
       <function>snd_mpu401_uart_new()</function>. 
 
         <informalexample>
@@ -4307,25 +4303,24 @@ struct _snd_pcm_runtime {
       </para>
 
       <para>
-        The 4th argument is the i/o port address. Many
-      backward-compatible MPU401 has an i/o port such as 0x330. Or, it
-      might be a part of its own PCI i/o region. It depends on the
+        The 4th argument is the I/O port address. Many
+      backward-compatible MPU401 have an I/O port such as 0x330. Or, it
+      might be a part of its own PCI I/O region. It depends on the
       chip design. 
       </para>
 
       <para>
-       The 5th argument is bitflags for additional information.
-        When the i/o port address above is a part of the PCI i/o
-      region, the MPU401 i/o port might have been already allocated
+       The 5th argument is a bitflag for additional information.
+        When the I/O port address above is part of the PCI I/O
+      region, the MPU401 I/O port might have been already allocated
       (reserved) by the driver itself. In such a case, pass a bit flag
       <constant>MPU401_INFO_INTEGRATED</constant>,
-      and 
-      the mpu401-uart layer will allocate the i/o ports by itself. 
+      and the mpu401-uart layer will allocate the I/O ports by itself. 
       </para>
 
        <para>
        When the controller supports only the input or output MIDI stream,
-       pass <constant>MPU401_INFO_INPUT</constant> or
+       pass the <constant>MPU401_INFO_INPUT</constant> or
        <constant>MPU401_INFO_OUTPUT</constant> bitflag, respectively.
        Then the rawmidi instance is created as a single stream.
        </para>
@@ -4333,7 +4328,7 @@ struct _snd_pcm_runtime {
        <para>
        <constant>MPU401_INFO_MMIO</constant> bitflag is used to change
        the access method to MMIO (via readb and writeb) instead of
-       iob and outb.  In this case, you have to pass the iomapped address
+       iob and outb. In this case, you have to pass the iomapped address
        to <function>snd_mpu401_uart_new()</function>.
        </para>
 
@@ -4341,7 +4336,7 @@ struct _snd_pcm_runtime {
        When <constant>MPU401_INFO_TX_IRQ</constant> is set, the output
        stream isn't checked in the default interrupt handler.  The driver
        needs to call <function>snd_mpu401_uart_interrupt_tx()</function>
-       by itself to start processing the output stream in irq handler.
+       by itself to start processing the output stream in the irq handler.
        </para>
 
       <para>
@@ -4381,7 +4376,7 @@ struct _snd_pcm_runtime {
       (<parameter>irq_flags</parameter>). Otherwise, pass the flags
       for irq allocation 
       (<constant>SA_XXX</constant> bits) to it, and the irq will be
-      reserved by the mpu401-uart layer. If the card doesn't generates
+      reserved by the mpu401-uart layer. If the card doesn't generate
       UART interrupts, pass -1 as the irq number. Then a timer
       interrupt will be invoked for polling. 
       </para>
@@ -4392,8 +4387,8 @@ struct _snd_pcm_runtime {
       <para>
         When the interrupt is allocated in
       <function>snd_mpu401_uart_new()</function>, the private
-      interrupt handler is used, hence you don't have to do nothing
-      else than creating the mpu401 stuff. Otherwise, you have to call
+      interrupt handler is used, hence you don't have anything else to do
+      than creating the mpu401 stuff. Otherwise, you have to call
       <function>snd_mpu401_uart_interrupt()</function> explicitly when
       a UART interrupt is invoked and checked in your own interrupt
       handler.  
@@ -4480,8 +4475,8 @@ struct _snd_pcm_runtime {
 
       <para>
       The fourth and fifth arguments are the number of output and
-      input substreams, respectively, of this device.  (A substream is
-      the equivalent of a MIDI port.)
+      input substreams, respectively, of this device (a substream is
+      the equivalent of a MIDI port).
       </para>
 
       <para>
@@ -4498,7 +4493,7 @@ struct _snd_pcm_runtime {
       <para>
       After the rawmidi device is created, you need to set the
       operators (callbacks) for each substream.  There are helper
-      functions to set the operators for all substream of a device:
+      functions to set the operators for all the substreams of a device:
         <informalexample>
           <programlisting>
 <![CDATA[
@@ -4528,8 +4523,8 @@ struct _snd_pcm_runtime {
       </para>
 
       <para>
-      If there is more than one substream, you should give each one a
-      unique name:
+      If there are more than one substream, you should give a
+      unique name to each of them:
         <informalexample>
           <programlisting>
 <![CDATA[
@@ -4550,7 +4545,7 @@ struct _snd_pcm_runtime {
       <title>Callbacks</title>
 
       <para>
-      In all callbacks, the private data that you've set for the
+      In all the callbacks, the private data that you've set for the
       rawmidi device can be accessed as
       substream-&gt;rmidi-&gt;private_data.
       <!-- <code> isn't available before DocBook 4.3 -->
@@ -4583,8 +4578,8 @@ struct _snd_pcm_runtime {
 
         <para>
         This is called when a substream is opened.
-        You can initialize the hardware here, but you should not yet
-        start transmitting/receiving data.
+        You can initialize the hardware here, but you shouldn't
+        start transmitting/receiving data yet.
         </para>
       </section>
 
@@ -4632,9 +4627,9 @@ struct _snd_pcm_runtime {
         To read data from the buffer, call
         <function>snd_rawmidi_transmit_peek</function>.  It will
         return the number of bytes that have been read; this will be
-        less than the number of bytes requested when there is no more
+        less than the number of bytes requested when there are no more
         data in the buffer.
-        After the data has been transmitted successfully, call
+        After the data have been transmitted successfully, call
         <function>snd_rawmidi_transmit_ack</function> to remove the
         data from the substream buffer:
           <informalexample>
@@ -4655,7 +4650,7 @@ struct _snd_pcm_runtime {
         <para>
         If you know beforehand that the hardware will accept data, you
         can use the <function>snd_rawmidi_transmit</function> function
-        which reads some data and removes it from the buffer at once:
+        which reads some data and removes them from the buffer at once:
           <informalexample>
             <programlisting>
 <![CDATA[
@@ -4749,13 +4744,13 @@ struct _snd_pcm_runtime {
 
         <para>
         This is only used with output substreams.  This function should wait
-        until all data read from the substream buffer has been transmitted.
+        until all data read from the substream buffer have been transmitted.
         This ensures that the device can be closed and the driver unloaded
         without losing data.
         </para>
 
         <para>
-        This callback is optional.  If you do not set
+        This callback is optional. If you do not set
         <structfield>drain</structfield> in the struct snd_rawmidi_ops
         structure, ALSA will simply wait for 50&nbsp;milliseconds
         instead.
@@ -4775,24 +4770,24 @@ struct _snd_pcm_runtime {
     <section id="misc-devices-opl3">
       <title>FM OPL3</title>
       <para>
-        The FM OPL3 is still used on many chips (mainly for backward
+        The FM OPL3 is still used in many chips (mainly for backward
       compatibility). ALSA has a nice OPL3 FM control layer, too. The
       OPL3 API is defined in
       <filename>&lt;sound/opl3.h&gt;</filename>. 
       </para>
 
       <para>
-        FM registers can be directly accessed through direct-FM API,
+        FM registers can be directly accessed through the direct-FM API,
       defined in <filename>&lt;sound/asound_fm.h&gt;</filename>. In
       ALSA native mode, FM registers are accessed through
-      Hardware-Dependant Device direct-FM extension API, whereas in
-      OSS compatible mode, FM registers can be accessed with OSS
-      direct-FM compatible API on <filename>/dev/dmfmX</filename> device. 
+      the Hardware-Dependant Device direct-FM extension API, whereas in
+      OSS compatible mode, FM registers can be accessed with the OSS
+      direct-FM compatible API in <filename>/dev/dmfmX</filename> device. 
       </para>
 
       <para>
-        For creating the OPL3 component, you have two functions to
-        call. The first one is a constructor for <type>opl3_t</type>
+        To create the OPL3 component, you have two functions to
+        call. The first one is a constructor for the <type>opl3_t</type>
         instance. 
 
         <informalexample>
@@ -4819,12 +4814,12 @@ struct _snd_pcm_runtime {
       <para>
         When the left and right ports have been already allocated by
       the card driver, pass non-zero to the fifth argument
-      (<parameter>integrated</parameter>). Otherwise, opl3 module will
+      (<parameter>integrated</parameter>). Otherwise, the opl3 module will
       allocate the specified ports by itself. 
       </para>
 
       <para>
-        When the accessing to the hardware requires special method
+        When the accessing the hardware requires special method
         instead of the standard I/O access, you can create opl3 instance
         separately with <function>snd_opl3_new()</function>.
 
@@ -4845,13 +4840,13 @@ struct _snd_pcm_runtime {
        access function, the private data and the destructor.
        The l_port and r_port are not necessarily set.  Only the
        command must be set properly.  You can retrieve the data
-       from opl3-&gt;private_data field.
+       from the opl3-&gt;private_data field.
       </para>
 
       <para>
        After creating the opl3 instance via <function>snd_opl3_new()</function>,
        call <function>snd_opl3_init()</function> to initialize the chip to the
-       proper state.  Note that <function>snd_opl3_create()</function> always
+       proper state. Note that <function>snd_opl3_create()</function> always
        calls it internally.
       </para>
 
@@ -4884,7 +4879,7 @@ struct _snd_pcm_runtime {
     <section id="misc-devices-hardware-dependent">
       <title>Hardware-Dependent Devices</title>
       <para>
-        Some chips need the access from the user-space for special
+        Some chips need user-space access for special
       controls or for loading the micro code. In such a case, you can
       create a hwdep (hardware-dependent) device. The hwdep API is
       defined in <filename>&lt;sound/hwdep.h&gt;</filename>. You can
@@ -4893,7 +4888,7 @@ struct _snd_pcm_runtime {
       </para>
 
       <para>
-        Creation of the <type>hwdep</type> instance is done via
+        The creation of the <type>hwdep</type> instance is done via
         <function>snd_hwdep_new()</function>. 
 
         <informalexample>
@@ -4912,8 +4907,8 @@ struct _snd_pcm_runtime {
         You can then pass any pointer value to the
         <parameter>private_data</parameter>.
         If you assign a private data, you should define the
-        destructor, too. The destructor function is set to
-        <structfield>private_free</structfield> field.  
+        destructor, too. The destructor function is set in
+        the <structfield>private_free</structfield> field.  
 
         <informalexample>
           <programlisting>
@@ -4925,7 +4920,7 @@ struct _snd_pcm_runtime {
           </programlisting>
         </informalexample>
 
-        and the implementation of destructor would be:
+        and the implementation of the destructor would be:
 
         <informalexample>
           <programlisting>
@@ -4943,7 +4938,7 @@ struct _snd_pcm_runtime {
       <para>
         The arbitrary file operations can be defined for this
         instance. The file operators are defined in
-        <parameter>ops</parameter> table. For example, assume that
+        the <parameter>ops</parameter> table. For example, assume that
         this chip needs an ioctl. 
 
         <informalexample>
@@ -4964,7 +4959,7 @@ struct _snd_pcm_runtime {
       <title>IEC958 (S/PDIF)</title>
       <para>
         Usually the controls for IEC958 devices are implemented via
-      control interface. There is a macro to compose a name string for
+      the control interface. There is a macro to compose a name string for
       IEC958 controls, <function>SNDRV_CTL_NAME_IEC958()</function>
       defined in <filename>&lt;include/asound.h&gt;</filename>.  
       </para>
@@ -4973,7 +4968,7 @@ struct _snd_pcm_runtime {
         There are some standard controls for IEC958 status bits. These
       controls use the type <type>SNDRV_CTL_ELEM_TYPE_IEC958</type>,
       and the size of element is fixed as 4 bytes array
-      (value.iec958.status[x]). For <structfield>info</structfield>
+      (value.iec958.status[x]). For the <structfield>info</structfield>
       callback, you don't specify 
       the value field for this type (the count field must be set,
       though). 
@@ -5001,7 +4996,7 @@ struct _snd_pcm_runtime {
       enable/disable or to set the raw bit mode. The implementation
       will depend on the chip, but the control should be named as
       <quote>IEC958 xxx</quote>, preferably using
-      <function>SNDRV_CTL_NAME_IEC958()</function> macro. 
+      the <function>SNDRV_CTL_NAME_IEC958()</function> macro. 
       </para>
 
       <para>
@@ -5036,12 +5031,12 @@ struct _snd_pcm_runtime {
         The allocation of pages with fallback is
       <function>snd_malloc_xxx_pages_fallback()</function>. This
       function tries to allocate the specified pages but if the pages
-      are not available, it tries to reduce the page sizes until the
+      are not available, it tries to reduce the page sizes until
       enough space is found.
       </para>
 
       <para>
-      For releasing the space, call
+      The release the pages, call
       <function>snd_free_xxx_pages()</function> function. 
       </para>
 
@@ -5050,8 +5045,8 @@ struct _snd_pcm_runtime {
        a large contiguous physical space
        at the time the module is loaded for the later use.
        This is called <quote>pre-allocation</quote>.
-       As already written, you can call the following function at the
-       construction of pcm instance (in the case of PCI bus). 
+       As already written, you can call the following function at 
+       pcm instance construction time (in the case of PCI bus). 
 
         <informalexample>
           <programlisting>
@@ -5063,34 +5058,34 @@ struct _snd_pcm_runtime {
         </informalexample>
 
         where <parameter>size</parameter> is the byte size to be
-      pre-allocated and the <parameter>max</parameter> is the maximal
-      size to be changed via <filename>prealloc</filename> proc file.
-      The allocator will try to get as large area as possible
+      pre-allocated and the <parameter>max</parameter> is the maximum
+      size to be changed via the <filename>prealloc</filename> proc file.
+      The allocator will try to get an area as large as possible
       within the given size. 
       </para>
 
       <para>
       The second argument (type) and the third argument (device pointer)
       are dependent on the bus.
-      In the case of ISA bus, pass <function>snd_dma_isa_data()</function>
+      In the case of the ISA bus, pass <function>snd_dma_isa_data()</function>
       as the third argument with <constant>SNDRV_DMA_TYPE_DEV</constant> type.
       For the continuous buffer unrelated to the bus can be pre-allocated
       with <constant>SNDRV_DMA_TYPE_CONTINUOUS</constant> type and the
       <function>snd_dma_continuous_data(GFP_KERNEL)</function> device pointer,
-      whereh <constant>GFP_KERNEL</constant> is the kernel allocation flag to
+      where <constant>GFP_KERNEL</constant> is the kernel allocation flag to
       use.  For the SBUS, <constant>SNDRV_DMA_TYPE_SBUS</constant> and
       <function>snd_dma_sbus_data(sbus_dev)</function> are used instead.
       For the PCI scatter-gather buffers, use
       <constant>SNDRV_DMA_TYPE_DEV_SG</constant> with
       <function>snd_dma_pci_data(pci)</function>
-      (see the section
+      (see the 
           <link linkend="buffer-and-memory-non-contiguous"><citetitle>Non-Contiguous Buffers
-          </citetitle></link>).
+          </citetitle></link> section).
       </para>
 
       <para>
-        Once when the buffer is pre-allocated, you can use the
-        allocator in the <structfield>hw_params</structfield> callback 
+        Once the buffer is pre-allocated, you can use the
+        allocator in the <structfield>hw_params</structfield> callback: 
 
         <informalexample>
           <programlisting>
@@ -5116,8 +5111,8 @@ struct _snd_pcm_runtime {
       </para>
 
       <para>
-        The first case works fine if the external hardware buffer is enough
-      large.  This method doesn't need any extra buffers and thus is
+        The first case works fine if the external hardware buffer is large
+      enough.  This method doesn't need any extra buffers and thus is
       more effective. You need to define the
       <structfield>copy</structfield> and
       <structfield>silence</structfield> callbacks for 
@@ -5127,25 +5122,25 @@ struct _snd_pcm_runtime {
       </para>
 
       <para>
-        The second case allows the mmap of the buffer, although you have
-      to handle an interrupt or a tasklet for transferring the data
+        The second case allows for mmap on the buffer, although you have
+      to handle an interrupt or a tasklet to transfer the data
       from the intermediate buffer to the hardware buffer. You can find an
-      example in vxpocket driver. 
+      example in the vxpocket driver. 
       </para>
 
       <para>
-        Another case is that the chip uses a PCI memory-map
+        Another case is when the chip uses a PCI memory-map
       region for the buffer instead of the host memory. In this case,
-      mmap is available only on certain architectures like intel. In
-      non-mmap mode, the data cannot be transferred as the normal
-      way. Thus you need to define <structfield>copy</structfield> and
-      <structfield>silence</structfield> callbacks as well 
+      mmap is available only on certain architectures like the Intel one.
+      In non-mmap mode, the data cannot be transferred as in the normal
+      way. Thus you need to define the <structfield>copy</structfield> and
+      <structfield>silence</structfield> callbacks as well, 
       as in the cases above. The examples are found in
       <filename>rme32.c</filename> and <filename>rme96.c</filename>. 
       </para>
 
       <para>
-        The implementation of <structfield>copy</structfield> and
+        The implementation of the <structfield>copy</structfield> and
         <structfield>silence</structfield> callbacks depends upon 
         whether the hardware supports interleaved or non-interleaved
         samples. The <structfield>copy</structfield> callback is
@@ -5184,8 +5179,8 @@ struct _snd_pcm_runtime {
 
       <para>
         What you have to do in this callback is again different
-        between playback and capture directions. In the case of
-        playback, you do: copy the given amount of data
+        between playback and capture directions. In the
+        playback case, you copy the given amount of data
         (<parameter>count</parameter>) at the specified pointer
         (<parameter>src</parameter>) to the specified offset
         (<parameter>pos</parameter>) on the hardware buffer. When
@@ -5202,7 +5197,7 @@ struct _snd_pcm_runtime {
       </para>
 
       <para>
-        For the capture direction, you do: copy the given amount of
+        For the capture direction, you copy the given amount of
         data (<parameter>count</parameter>) at the specified offset
         (<parameter>pos</parameter>) on the hardware buffer to the
         specified pointer (<parameter>dst</parameter>). 
@@ -5216,7 +5211,7 @@ struct _snd_pcm_runtime {
           </programlisting>
         </informalexample>
 
-        Note that both of the position and the data amount are given
+        Note that both the position and the amount of data are given
       in frames. 
       </para>
 
@@ -5247,7 +5242,7 @@ struct _snd_pcm_runtime {
       </para>
 
       <para>
-        The meanings of arguments are identical with the
+        The meanings of arguments are the same as in the
       <structfield>copy</structfield> 
       callback, although there is no <parameter>src/dst</parameter>
       argument. In the case of interleaved samples, the channel
@@ -5284,8 +5279,8 @@ struct _snd_pcm_runtime {
     <section id="buffer-and-memory-non-contiguous">
       <title>Non-Contiguous Buffers</title>
       <para>
-        If your hardware supports the page table like emu10k1 or the
-      buffer descriptors like via82xx, you can use the scatter-gather
+        If your hardware supports the page table as in emu10k1 or the
+      buffer descriptors as in via82xx, you can use the scatter-gather
       (SG) DMA. ALSA provides an interface for handling SG-buffers.
       The API is provided in <filename>&lt;sound/pcm.h&gt;</filename>. 
       </para>
@@ -5296,7 +5291,7 @@ struct _snd_pcm_runtime {
         <function>snd_pcm_lib_preallocate_pages_for_all()</function>
         with <constant>SNDRV_DMA_TYPE_DEV_SG</constant>
        in the PCM constructor like other PCI pre-allocator.
-        You need to pass the <function>snd_dma_pci_data(pci)</function>,
+        You need to pass <function>snd_dma_pci_data(pci)</function>,
         where pci is the struct <structname>pci_dev</structname> pointer
         of the chip as well.
         The <type>struct snd_sg_buf</type> instance is created as
@@ -5314,7 +5309,7 @@ struct _snd_pcm_runtime {
 
       <para>
         Then call <function>snd_pcm_lib_malloc_pages()</function>
-      in <structfield>hw_params</structfield> callback
+      in the <structfield>hw_params</structfield> callback
       as well as in the case of normal PCI buffer.
       The SG-buffer handler will allocate the non-contiguous kernel
       pages of the given size and map them onto the virtually contiguous
@@ -5335,7 +5330,7 @@ struct _snd_pcm_runtime {
       </para>
 
       <para>
-        For releasing the data, call
+        To release the data, call
       <function>snd_pcm_lib_free_pages()</function> in the
       <structfield>hw_free</structfield> callback as usual.
       </para>
@@ -5390,7 +5385,7 @@ struct _snd_pcm_runtime {
     </para>
 
     <para>
-      For creating a proc file, call
+      To create a proc file, call
       <function>snd_card_proc_new()</function>. 
 
       <informalexample>
@@ -5402,7 +5397,7 @@ struct _snd_pcm_runtime {
         </programlisting>
       </informalexample>
 
-      where the second argument specifies the proc-file name to be
+      where the second argument specifies the name of the proc file to be
     created. The above example will create a file
     <filename>my-file</filename> under the card directory,
     e.g. <filename>/proc/asound/card0/my-file</filename>. 
@@ -5417,8 +5412,8 @@ struct _snd_pcm_runtime {
 
     <para>
       When the creation is successful, the function stores a new
-    instance at the pointer given in the third argument.
-    It is initialized as a text proc file for read only.  For using
+    instance in the pointer given in the third argument.
+    It is initialized as a text proc file for read only.  To use
     this proc file as a read-only text file as it is, set the read
     callback with a private data via 
      <function>snd_info_set_text_ops()</function>.
@@ -5470,9 +5465,9 @@ struct _snd_pcm_runtime {
     </para>
 
     <para>
-    The file permission can be changed afterwards.  As default, it's
-    set as read only for all users.  If you want to add the write
-    permission to the user (root as default), set like below:
+    The file permissions can be changed afterwards.  As default, it's
+    set as read only for all users.  If you want to add write
+    permission for the user (root as default), do as follows:
 
       <informalexample>
         <programlisting>
@@ -5503,7 +5498,7 @@ struct _snd_pcm_runtime {
     </para>
 
     <para>
-      For a raw-data proc-file, set the attributes like the following:
+      For a raw-data proc-file, set the attributes as follows:
 
       <informalexample>
         <programlisting>
@@ -5524,7 +5519,7 @@ struct _snd_pcm_runtime {
 
     <para>
       The callback is much more complicated than the text-file
-      version. You need to use a low-level i/o functions such as
+      version. You need to use a low-level I/O functions such as
       <function>copy_from/to_user()</function> to transfer the
       data.
 
@@ -5560,28 +5555,28 @@ struct _snd_pcm_runtime {
     <title>Power Management</title>
     <para>
       If the chip is supposed to work with suspend/resume
-      functions, you need to add the power-management codes to the
-      driver. The additional codes for the power-management should be
+      functions, you need to add power-management code to the
+      driver. The additional code for power-management should be
       <function>ifdef</function>'ed with
       <constant>CONFIG_PM</constant>. 
     </para>
 
        <para>
-       If the driver supports the suspend/resume
-       <emphasis>fully</emphasis>, that is, the device can be
-       properly resumed to the status at the suspend is called,
-       you can set <constant>SNDRV_PCM_INFO_RESUME</constant> flag
-       to pcm info field.  Usually, this is possible when the
-       registers of ths chip can be safely saved and restored to the
-       RAM.  If this is set, the trigger callback is called with
-       <constant>SNDRV_PCM_TRIGGER_RESUME</constant> after resume
-       callback is finished
+       If the driver <emphasis>fully</emphasis> supports suspend/resume
+       that is, the device can be
+       properly resumed to its state when suspend was called,
+       you can set the <constant>SNDRV_PCM_INFO_RESUME</constant> flag
+       in the pcm info field.  Usually, this is possible when the
+       registers of the chip can be safely saved and restored to
+       RAM. If this is set, the trigger callback is called with
+       <constant>SNDRV_PCM_TRIGGER_RESUME</constant> after the resume
+       callback completes
        </para>
 
        <para>
-       Even if the driver doesn't support PM fully but only the
-       partial suspend/resume is possible, it's still worthy to
-       implement suspend/resume callbacks.  In such a case, applications
+       Even if the driver doesn't support PM fully but 
+       partial suspend/resume is still possible, it's still worthy to
+       implement suspend/resume callbacks. In such a case, applications
        would reset the status by calling
        <function>snd_pcm_prepare()</function> and restart the stream
        appropriately.  Hence, you can define suspend/resume callbacks
@@ -5590,22 +5585,22 @@ struct _snd_pcm_runtime {
        </para>
        
        <para>
-       Note that the trigger with SUSPEND can be always called when
+       Note that the trigger with SUSPEND can always be called when
        <function>snd_pcm_suspend_all</function> is called,
-       regardless of <constant>SNDRV_PCM_INFO_RESUME</constant> flag.
+       regardless of the <constant>SNDRV_PCM_INFO_RESUME</constant> flag.
        The <constant>RESUME</constant> flag affects only the behavior
        of <function>snd_pcm_resume()</function>.
        (Thus, in theory,
        <constant>SNDRV_PCM_TRIGGER_RESUME</constant> isn't needed
        to be handled in the trigger callback when no
        <constant>SNDRV_PCM_INFO_RESUME</constant> flag is set.  But,
-       it's better to keep it for compatibility reason.)
+       it's better to keep it for compatibility reasons.)
        </para>
     <para>
       In the earlier version of ALSA drivers, a common
       power-management layer was provided, but it has been removed.
       The driver needs to define the suspend/resume hooks according to
-      the bus the device is assigned.  In the case of PCI driver, the
+      the bus the device is connected to.  In the case of PCI drivers, the
       callbacks look like below:
 
       <informalexample>
@@ -5629,7 +5624,7 @@ struct _snd_pcm_runtime {
     </para>
 
     <para>
-      The scheme of the real suspend job is as following.
+      The scheme of the real suspend job is as follows.
 
       <orderedlist>
         <listitem><para>Retrieve the card and the chip data.</para></listitem>
@@ -5679,11 +5674,11 @@ struct _snd_pcm_runtime {
     </para>
 
     <para>
-    The scheme of the real resume job is as following.
+    The scheme of the real resume job is as follows.
 
     <orderedlist>
     <listitem><para>Retrieve the card and the chip data.</para></listitem>
-    <listitem><para>Set up PCI.  First, call <function>pci_restore_state()</function>.
+    <listitem><para>Set up PCI. First, call <function>pci_restore_state()</function>.
        Then enable the pci device again by calling <function>pci_enable_device()</function>.
        Call <function>pci_set_master()</function> if necessary, too.</para></listitem>
     <listitem><para>Re-initialize the chip.</para></listitem>
@@ -5734,7 +5729,7 @@ struct _snd_pcm_runtime {
        <function>snd_pcm_suspend_all()</function> or
        <function>snd_pcm_suspend()</function>.  It means that the PCM
        streams are already stoppped when the register snapshot is
-       taken.  But, remind that you don't have to restart the PCM
+       taken.  But, remember that you don't have to restart the PCM
        stream in the resume callback. It'll be restarted via 
        trigger call with <constant>SNDRV_PCM_TRIGGER_RESUME</constant>
        when necessary.
@@ -5795,7 +5790,7 @@ struct _snd_pcm_runtime {
     </para>
 
     <para>
-      If you need a space for saving the registers, allocate the
+      If you need a space to save the registers, allocate the
        buffer for it here, too, since it would be fatal
     if you cannot allocate a memory in the suspend phase.
     The allocated buffer should be released in the corresponding
@@ -5833,7 +5828,7 @@ struct _snd_pcm_runtime {
     <title>Module Parameters</title>
     <para>
       There are standard module options for ALSA. At least, each
-      module should have <parameter>index</parameter>,
+      module should have the <parameter>index</parameter>,
       <parameter>id</parameter> and <parameter>enable</parameter>
       options. 
     </para>
@@ -5841,8 +5836,8 @@ struct _snd_pcm_runtime {
     <para>
       If the module supports multiple cards (usually up to
       8 = <constant>SNDRV_CARDS</constant> cards), they should be
-      arrays.  The default initial values are defined already as
-      constants for ease of programming:
+      arrays. The default initial values are defined already as
+      constants for easier programming:
 
       <informalexample>
         <programlisting>
@@ -5858,7 +5853,7 @@ struct _snd_pcm_runtime {
     <para>
       If the module supports only a single card, they could be single
     variables, instead.  <parameter>enable</parameter> option is not
-    always necessary in this case, but it wouldn't be so bad to have a
+    always necessary in this case, but it would be better to have a
     dummy option for compatibility.
     </para>
 
@@ -5923,22 +5918,22 @@ struct _snd_pcm_runtime {
        </para>
 
        <para>
-       Suppose that you'll create a new PCI driver for the card
+       Suppose that you create a new PCI driver for the card
        <quote>xyz</quote>.  The card module name would be
-       snd-xyz.  The new driver is usually put into alsa-driver
+       snd-xyz.  The new driver is usually put into the alsa-driver
        tree, <filename>alsa-driver/pci</filename> directory in
        the case of PCI cards.
        Then the driver is evaluated, audited and tested
        by developers and users.  After a certain time, the driver
-       will go to alsa-kernel tree (to the corresponding directory,
+       will go to the alsa-kernel tree (to the corresponding directory,
        such as <filename>alsa-kernel/pci</filename>) and eventually
-       integrated into Linux 2.6 tree (the directory would be
+       will be integrated into the Linux 2.6 tree (the directory would be
        <filename>linux/sound/pci</filename>).
        </para>
 
        <para>
        In the following sections, the driver code is supposed
-       to be put into alsa-driver tree.  The two cases are assumed:
+       to be put into alsa-driver tree. The two cases are covered:
        a driver consisting of a single source file and one consisting
        of several source files.
        </para>
@@ -6033,7 +6028,7 @@ struct _snd_pcm_runtime {
        <listitem>
        <para>
        Add a new directory (<filename>xyz</filename>) in
-       <filename>alsa-driver/pci/Makefile</filename> like below
+       <filename>alsa-driver/pci/Makefile</filename> as below
 
       <informalexample>
         <programlisting>
@@ -6102,7 +6097,7 @@ struct _snd_pcm_runtime {
     <section id="useful-functions-snd-printk">
       <title><function>snd_printk()</function> and friends</title>
       <para>
-        ALSA provides a verbose version of
+        ALSA provides a verbose version of the
       <function>printk()</function> function. If a kernel config
       <constant>CONFIG_SND_VERBOSE_PRINTK</constant> is set, this
       function prints the given message together with the file name
@@ -6170,7 +6165,7 @@ struct _snd_pcm_runtime {
     <section id="useful-functions-snd-bug">
       <title><function>snd_BUG()</function></title>
       <para>
-        It shows <computeroutput>BUG?</computeroutput> message and
+        It shows the <computeroutput>BUG?</computeroutput> message and
       stack trace as well as <function>snd_assert</function> at the point.
       It's useful to show that a fatal error happens there. 
       </para>
@@ -6199,6 +6194,4 @@ struct _snd_pcm_runtime {
     in the hardware constraints section.
     </para>
   </chapter>
-
-
 </book>
index 3feeb9ecdec49719443cba15fcac628a0c24b058..0ebd7ea9706cc1ced01508562d866d520eb1dec6 100644 (file)
@@ -1,5 +1,5 @@
 ASoC currently supports the three main Digital Audio Interfaces (DAI) found on
-SoC controllers and portable audio CODECS today, namely AC97, I2S and PCM.
+SoC controllers and portable audio CODECs today, namely AC97, I2S and PCM.
 
 
 AC97
@@ -25,7 +25,7 @@ left/right clock (LRC) synchronise the link. I2S is flexible in that either the
 controller or CODEC can drive (master) the BCLK and LRC clock lines. Bit clock
 usually varies depending on the sample rate and the master system clock
 (SYSCLK). LRCLK is the same as the sample rate. A few devices support separate
-ADC and DAC LRCLK's, this allows for simultaneous capture and playback at
+ADC and DAC LRCLKs, this allows for simultaneous capture and playback at
 different sample rates.
 
 I2S has several different operating modes:-
@@ -35,7 +35,7 @@ I2S has several different operating modes:-
 
  o Left Justified - MSB is transmitted on transition of LRC.
 
- o Right Justified - MSB is transmitted sample size BCLK's before LRC
+ o Right Justified - MSB is transmitted sample size BCLKs before LRC
                      transition.
 
 PCM
index 14930887c25f194c206a43dd6c56bd6188e07dd2..b1300162e01cef93ae7f97dc728ce02255487500 100644 (file)
@@ -13,7 +13,7 @@ or SYSCLK). This audio master clock can be derived from a number of sources
 (e.g. crystal, PLL, CPU clock) and is responsible for producing the correct
 audio playback and capture sample rates.
 
-Some master clocks (e.g. PLL's and CPU based clocks) are configurable in that
+Some master clocks (e.g. PLLs and CPU based clocks) are configurable in that
 their speed can be altered by software (depending on the system use and to save
 power). Other master clocks are fixed at a set frequency (i.e. crystals).
 
@@ -41,11 +41,11 @@ BCLK = LRC * x
 BCLK = LRC * Channels * Word Size
 
 This relationship depends on the codec or SoC CPU in particular. In general
-it's best to configure BCLK to the lowest possible speed (depending on your
-rate, number of channels and wordsize) to save on power.
+it is best to configure BCLK to the lowest possible speed (depending on your
+rate, number of channels and word size) to save on power.
 
-It's also desirable to use the codec (if possible) to drive (or master) the
-audio clocks as it's usually gives more accurate sample rates than the CPU.
+It is also desirable to use the codec (if possible) to drive (or master) the
+audio clocks as it usually gives more accurate sample rates than the CPU.
 
 
 
index 1e766ad0ebd1f4f28f4e20cb88899d4b8f5717da..1e95342ed72e4d7b91ef53743cc11c42fcf8dd12 100644 (file)
@@ -9,7 +9,7 @@ code should be added to the platform and machine drivers respectively.
 Each codec driver *must* provide the following features:-
 
  1) Codec DAI and PCM configuration
- 2) Codec control IO - using I2C, 3 Wire(SPI) or both API's
+ 2) Codec control IO - using I2C, 3 Wire(SPI) or both APIs
  3) Mixers and audio controls
  4) Codec audio operations
 
@@ -19,7 +19,7 @@ Optionally, codec drivers can also provide:-
  6) DAPM event handler.
  7) DAC Digital mute control.
 
-It's probably best to use this guide in conjunction with the existing codec
+Its probably best to use this guide in conjunction with the existing codec
 driver code in sound/soc/codecs/
 
 ASoC Codec driver breakdown
@@ -27,8 +27,8 @@ ASoC Codec driver breakdown
 
 1 - Codec DAI and PCM configuration
 -----------------------------------
-Each codec driver must have a struct snd_soc_codec_dai to define it's DAI and
-PCM's capabilities and operations. This struct is exported so that it can be
+Each codec driver must have a struct snd_soc_codec_dai to define its DAI and
+PCM capabilities and operations. This struct is exported so that it can be
 registered with the core by your machine driver.
 
 e.g.
@@ -67,18 +67,18 @@ EXPORT_SYMBOL_GPL(wm8731_dai);
 
 2 - Codec control IO
 --------------------
-The codec can usually be controlled via an I2C or SPI style interface (AC97
-combines control with data in the DAI). The codec drivers will have to provide
-functions to read and write the codec registers along with supplying a register
-cache:-
+The codec can usually be controlled via an I2C or SPI style interface
+(AC97 combines control with data in the DAI). The codec drivers provide
+functions to read and write the codec registers along with supplying a
+register cache:-
 
        /* IO control data and register cache */
-    void *control_data; /* codec control (i2c/3wire) data */
-    void *reg_cache;
+       void *control_data; /* codec control (i2c/3wire) data */
+       void *reg_cache;
 
-Codec read/write should do any data formatting and call the hardware read write
-below to perform the IO. These functions are called by the core and alsa when
-performing DAPM or changing the mixer:-
+Codec read/write should do any data formatting and call the hardware
+read write below to perform the IO. These functions are called by the
+core and ALSA when performing DAPM or changing the mixer:-
 
     unsigned int (*read)(struct snd_soc_codec *, unsigned int);
     int (*write)(struct snd_soc_codec *, unsigned int, unsigned int);
@@ -131,7 +131,7 @@ Defines a stereo enumerated control
 
 4 - Codec Audio Operations
 --------------------------
-The codec driver also supports the following alsa operations:-
+The codec driver also supports the following ALSA operations:-
 
 /* SoC audio ops */
 struct snd_soc_ops {
@@ -142,15 +142,15 @@ struct snd_soc_ops {
        int (*prepare)(struct snd_pcm_substream *);
 };
 
-Please refer to the alsa driver PCM documentation for details.
+Please refer to the ALSA driver PCM documentation for details.
 http://www.alsa-project.org/~iwai/writing-an-alsa-driver/c436.htm
 
 
 5 - DAPM description.
 ---------------------
-The Dynamic Audio Power Management description describes the codec's power
-components, their relationships and registers to the ASoC core. Please read
-dapm.txt for details of building the description.
+The Dynamic Audio Power Management description describes the codec power
+components and their relationships and registers to the ASoC core.
+Please read dapm.txt for details of building the description.
 
 Please also see the examples in other codec drivers.
 
@@ -158,8 +158,8 @@ Please also see the examples in other codec drivers.
 6 - DAPM event handler
 ----------------------
 This function is a callback that handles codec domain PM calls and system
-domain PM calls (e.g. suspend and resume). It's used to put the codec to sleep
-when not in use.
+domain PM calls (e.g. suspend and resume). It is used to put the codec
+to sleep when not in use.
 
 Power states:-
 
@@ -175,13 +175,14 @@ Power states:-
        SNDRV_CTL_POWER_D3cold: /* Everything Off, without power */
 
 
-7 - Codec DAC digital mute control.
-------------------------------------
-Most codecs have a digital mute before the DAC's that can be used to minimise
-any system noise.  The mute stops any digital data from entering the DAC.
+7 - Codec DAC digital mute control
+----------------------------------
+Most codecs have a digital mute before the DACs that can be used to
+minimise any system noise.  The mute stops any digital data from
+entering the DAC.
 
-A callback can be created that is called by the core for each codec DAI when the
-mute is applied or freed.
+A callback can be created that is called by the core for each codec DAI
+when the mute is applied or freed.
 
 i.e.
 
index ab0766fd78690859e1594602fcd62155f48b0529..c784a18b94dce93bdcb064b2ef6670d450846bd5 100644 (file)
@@ -4,20 +4,20 @@ Dynamic Audio Power Management for Portable Devices
 1. Description
 ==============
 
-Dynamic Audio Power Management (DAPM) is designed to allow portable Linux devices
-to use the minimum amount of power within the audio subsystem at all times. It
-is independent of other kernel PM and as such, can easily co-exist with the
-other PM systems.
+Dynamic Audio Power Management (DAPM) is designed to allow portable
+Linux devices to use the minimum amount of power within the audio
+subsystem at all times. It is independent of other kernel PM and as
+such, can easily co-exist with the other PM systems.
 
-DAPM is also completely transparent to all user space applications as all power
-switching is done within the ASoC core. No code changes or recompiling are
-required for user space applications. DAPM makes power switching decisions based
-upon any audio stream (capture/playback) activity and audio mixer settings
-within the device.
+DAPM is also completely transparent to all user space applications as
+all power switching is done within the ASoC core. No code changes or
+recompiling are required for user space applications. DAPM makes power
+switching decisions based upon any audio stream (capture/playback)
+activity and audio mixer settings within the device.
 
-DAPM spans the whole machine. It covers power control within the entire audio
-subsystem, this includes internal codec power blocks and machine level power
-systems.
+DAPM spans the whole machine. It covers power control within the entire
+audio subsystem, this includes internal codec power blocks and machine
+level power systems.
 
 There are 4 power domains within DAPM
 
@@ -34,7 +34,7 @@ There are 4 power domains within DAPM
       Automatically set when mixer and mux settings are changed by the user.
       e.g. alsamixer, amixer.
 
-   4. Stream domain - DAC's and ADC's.
+   4. Stream domain - DACs and ADCs.
       Enabled and disabled when stream playback/capture is started and
       stopped respectively. e.g. aplay, arecord.
 
@@ -51,7 +51,7 @@ widgets hereafter.
 Audio DAPM widgets fall into a number of types:-
 
  o Mixer      - Mixes several analog signals into a single analog signal.
- o Mux        - An analog switch that outputs only 1 of it's inputs.
+ o Mux        - An analog switch that outputs only one of many inputs.
  o PGA        - A programmable gain amplifier or attenuation widget.
  o ADC        - Analog to Digital Converter
  o DAC        - Digital to Analog Converter
@@ -78,14 +78,14 @@ parameters for stream name and kcontrols.
 2.1 Stream Domain Widgets
 -------------------------
 
-Stream Widgets relate to the stream power domain and only consist of ADC's
-(analog to digital converters) and DAC's (digital to analog converters).
+Stream Widgets relate to the stream power domain and only consist of ADCs
+(analog to digital converters) and DACs (digital to analog converters).
 
 Stream widgets have the following format:-
 
 SND_SOC_DAPM_DAC(name, stream name, reg, shift, invert),
 
-NOTE: the stream name must match the corresponding stream name in your codecs
+NOTE: the stream name must match the corresponding stream name in your codec
 snd_soc_codec_dai.
 
 e.g. stream widgets for HiFi playback and capture
@@ -97,7 +97,7 @@ SND_SOC_DAPM_ADC("HiFi ADC", "HiFi Capture", REG, 2, 1),
 2.2 Path Domain Widgets
 -----------------------
 
-Path domain widgets have a ability to control or effect the audio signal or
+Path domain widgets have a ability to control or affect the audio signal or
 audio paths within the audio subsystem. They have the following form:-
 
 SND_SOC_DAPM_PGA(name, reg, shift, invert, controls, num_controls)
@@ -149,7 +149,7 @@ SND_SOC_DAPM_MIC("Mic Jack", spitz_mic_bias),
 2.4 Codec Domain
 ----------------
 
-The Codec power domain has no widgets and is handled by the codecs DAPM event
+The codec power domain has no widgets and is handled by the codecs DAPM event
 handler. This handler is called when the codec powerstate is changed wrt to any
 stream event or by kernel PM events.
 
@@ -158,8 +158,8 @@ stream event or by kernel PM events.
 -------------------
 
 Sometimes widgets exist in the codec or machine audio map that don't have any
-corresponding register bit for power control. In this case it's necessary to
-create a virtual widget - a widget with no control bits e.g.
+corresponding soft power control. In this case it is necessary to create
+a virtual widget - a widget with no control bits e.g.
 
 SND_SOC_DAPM_MIXER("AC97 Mixer", SND_SOC_DAPM_NOPM, 0, 0, NULL, 0),
 
@@ -172,13 +172,14 @@ subsystem individually with a call to snd_soc_dapm_new_control().
 3. Codec Widget Interconnections
 ================================
 
-Widgets are connected to each other within the codec and machine by audio
-paths (called interconnections). Each interconnection must be defined in order
-to create a map of all audio paths between widgets.
+Widgets are connected to each other within the codec and machine by audio paths
+(called interconnections). Each interconnection must be defined in order to
+create a map of all audio paths between widgets.
+
 This is easiest with a diagram of the codec (and schematic of the machine audio
 system), as it requires joining widgets together via their audio signal paths.
 
-i.e. from the WM8731 codec's output mixer (wm8731.c)
+e.g., from the WM8731 output mixer (wm8731.c)
 
 The WM8731 output mixer has 3 inputs (sources)
 
index 72bd222f2a21796316a27cea63a539e620fdd99f..f370e7db86af6a970de14433523af4b1ca71d390 100644 (file)
@@ -16,7 +16,7 @@ struct snd_soc_machine {
        int (*remove)(struct platform_device *pdev);
 
        /* the pre and post PM functions are used to do any PM work before and
-        * after the codec and DAI's do any PM work. */
+        * after the codec and DAIs do any PM work. */
        int (*suspend_pre)(struct platform_device *pdev, pm_message_t state);
        int (*suspend_post)(struct platform_device *pdev, pm_message_t state);
        int (*resume_pre)(struct platform_device *pdev);
@@ -38,7 +38,7 @@ probe/remove are optional. Do any machine specific probe here.
 suspend()/resume()
 ------------------
 The machine driver has pre and post versions of suspend and resume to take care
-of any machine audio tasks that have to be done before or after the codec, DAI's
+of any machine audio tasks that have to be done before or after the codec, DAIs
 and DMA is suspended and resumed. Optional.
 
 
@@ -49,10 +49,10 @@ The machine specific audio operations can be set here. Again this is optional.
 
 Machine DAI Configuration
 -------------------------
-The machine DAI configuration glues all the codec and CPU DAI's together. It can
+The machine DAI configuration glues all the codec and CPU DAIs together. It can
 also be used to set up the DAI system clock and for any machine related DAI
 initialisation e.g. the machine audio map can be connected to the codec audio
-map, unconnnected codec pins can be set as such. Please see corgi.c, spitz.c
+map, unconnected codec pins can be set as such. Please see corgi.c, spitz.c
 for examples.
 
 struct snd_soc_dai_link is used to set up each DAI in your machine. e.g.
@@ -67,7 +67,7 @@ static struct snd_soc_dai_link corgi_dai = {
        .ops = &corgi_ops,
 };
 
-struct snd_soc_machine then sets up the machine with it's DAI's. e.g.
+struct snd_soc_machine then sets up the machine with it's DAIs. e.g.
 
 /* corgi audio machine driver */
 static struct snd_soc_machine snd_soc_machine_corgi = {
@@ -110,4 +110,4 @@ details.
 Machine Controls
 ----------------
 
-Machine specific audio mixer controls can be added in the dai init function.
\ No newline at end of file
+Machine specific audio mixer controls can be added in the DAI init function.
index c47ce9530677961142f371784bc7550cdc58c460..1e4c6d3655f2089c875ff381759ebd5528fe2ad3 100644 (file)
@@ -1,25 +1,26 @@
 ALSA SoC Layer
 ==============
 
-The overall project goal of the ALSA System on Chip (ASoC) layer is to provide
-better ALSA support for embedded system-on-chip processors (e.g. pxa2xx, au1x00,
-iMX, etc) and portable audio codecs. Currently there is some support in the
-kernel for SoC audio, however it has some limitations:-
+The overall project goal of the ALSA System on Chip (ASoC) layer is to
+provide better ALSA support for embedded system-on-chip processors (e.g.
+pxa2xx, au1x00, iMX, etc) and portable audio codecs.  Prior to the ASoC
+subsystem there was some support in the kernel for SoC audio, however it
+had some limitations:-
 
-  * Currently, codec drivers are often tightly coupled to the underlying SoC
-    CPU. This is not ideal and leads to code duplication i.e. Linux now has 4
-    different wm8731 drivers for 4 different SoC platforms.
+  * Codec drivers were often tightly coupled to the underlying SoC
+    CPU. This is not ideal and leads to code duplication - for example,
+    Linux had different wm8731 drivers for 4 different SoC platforms.
 
-  * There is no standard method to signal user initiated audio events (e.g.
+  * There was no standard method to signal user initiated audio events (e.g.
     Headphone/Mic insertion, Headphone/Mic detection after an insertion
     event). These are quite common events on portable devices and often require
     machine specific code to re-route audio, enable amps, etc., after such an
     event.
 
-  * Current drivers tend to power up the entire codec when playing
-    (or recording) audio. This is fine for a PC, but tends to waste a lot of
-    power on portable devices. There is also no support for saving power via
-    changing codec oversampling rates, bias currents, etc.
+  * Drivers tended to power up the entire codec when playing (or
+    recording) audio. This is fine for a PC, but tends to waste a lot of
+    power on portable devices. There was also no support for saving
+    power via changing codec oversampling rates, bias currents, etc.
 
 
 ASoC Design
@@ -31,12 +32,13 @@ features :-
   * Codec independence. Allows reuse of codec drivers on other platforms
     and machines.
 
-  * Easy I2S/PCM audio interface setup between codec and SoC. Each SoC interface
-    and codec registers it's audio interface capabilities with the core and are
-    subsequently matched and configured when the application hw params are known.
+  * Easy I2S/PCM audio interface setup between codec and SoC. Each SoC
+    interface and codec registers it's audio interface capabilities with the
+    core and are subsequently matched and configured when the application
+    hardware parameters are known.
 
   * Dynamic Audio Power Management (DAPM). DAPM automatically sets the codec to
-    it's minimum power state at all times. This includes powering up/down
+    its minimum power state at all times. This includes powering up/down
     internal power blocks depending on the internal codec audio routing and any
     active streams.
 
@@ -45,16 +47,16 @@ features :-
     signals the codec when to change power states.
 
   * Machine specific controls: Allow machines to add controls to the sound card
-    (e.g. volume control for speaker amp).
+    (e.g. volume control for speaker amplifier).
 
 To achieve all this, ASoC basically splits an embedded audio system into 3
 components :-
 
   * Codec driver: The codec driver is platform independent and contains audio
-    controls, audio interface capabilities, codec dapm definition and codec IO
+    controls, audio interface capabilities, codec DAPM definition and codec IO
     functions.
 
-  * Platform driver: The platform driver contains the audio dma engine and audio
+  * Platform driver: The platform driver contains the audio DMA engine and audio
     interface drivers (e.g. I2S, AC97, PCM) for that platform.
 
   * Machine driver: The machine driver handles any machine specific controls and
@@ -81,4 +83,4 @@ machine.txt: Machine driver internals.
 
 pop_clicks.txt: How to minimise audio artifacts.
 
-clocking.txt: ASoC clocking for best power performance.
\ No newline at end of file
+clocking.txt: ASoC clocking for best power performance.
index d4678b4dc6c6704869763c98b584bf015584fda2..b681d17fc3880b8872e7a770f77dd80d3f0b8137 100644 (file)
@@ -8,7 +8,7 @@ specific code.
 Audio DMA
 =========
 
-The platform DMA driver optionally supports the following alsa operations:-
+The platform DMA driver optionally supports the following ALSA operations:-
 
 /* SoC audio ops */
 struct snd_soc_ops {
@@ -38,7 +38,7 @@ struct snd_soc_platform {
        struct snd_pcm_ops *pcm_ops;
 };
 
-Please refer to the alsa driver documentation for details of audio DMA.
+Please refer to the ALSA driver documentation for details of audio DMA.
 http://www.alsa-project.org/~iwai/writing-an-alsa-driver/c436.htm
 
 An example DMA driver is soc/pxa/pxa2xx-pcm.c
@@ -52,7 +52,7 @@ Each SoC DAI driver must provide the following features:-
  1) Digital audio interface (DAI) description
  2) Digital audio interface configuration
  3) PCM's description
- 4) Sysclk configuration
+ 4) SYSCLK configuration
  5) Suspend and resume (optional)
 
 Please see codec.txt for a description of items 1 - 4.
index 3371bd9d7cfa52c46c7713b78b932326090f996c..e1e74daa4497219209446c9e152f6d4a40c7c837 100644 (file)
@@ -15,11 +15,11 @@ click every time a component power state is changed.
 Minimising Playback Pops and Clicks
 ===================================
 
-Playback pops in portable audio subsystems cannot be completely eliminated atm,
-however future audio codec hardware will have better pop and click suppression.
-Pops can be reduced within playback by powering the audio components in a
-specific order. This order is different for startup and shutdown and follows
-some basic rules:-
+Playback pops in portable audio subsystems cannot be completely eliminated
+currently, however future audio codec hardware will have better pop and click
+suppression.  Pops can be reduced within playback by powering the audio
+components in a specific order. This order is different for startup and
+shutdown and follows some basic rules:-
 
  Startup Order :- DAC --> Mixers --> Output PGA --> Digital Unmute
 
index 2d5ff3e34699fb2f2857da6054b7cc2b9d83f467..093cf04e9ca6290822a0f4efdc137ee087450723 100644 (file)
@@ -3571,6 +3571,9 @@ S:        Maintained
 SOUND - SOC LAYER / DYNAMIC AUDIO POWER MANAGEMENT
 P:     Liam Girdwood
 M:     liam.girdwood@wolfsonmicro.com
+P:     Mark Brown
+M:     broonie@opensource.wolfsonmicro.com
+T:     git opensource.wolfsonmicro.com/linux-2.6-asoc
 L:     alsa-devel@alsa-project.org (subscribers-only)
 S:     Supported
 
index 7549939b9535634de87ddb09a829b186758303e0..986a8365e37fb6e867248932bc6f7a66edc97784 100644 (file)
@@ -27,7 +27,6 @@
 #include <linux/kthread.h>
 #include <linux/freezer.h>
 
-#include <sound/driver.h>
 #include <sound/core.h>
 #include <sound/ac97_codec.h>
 
index 8735227f7e47b5e798d7291b01883628f0f9367a..316b106c3511b2a15981e692c6e4c7cd9884c6cd 100644 (file)
@@ -33,7 +33,6 @@
 #include <linux/pci.h>
 
 #include <asm/delay.h>
-#include <sound/driver.h>
 #include <sound/core.h>
 #include <sound/pcm.h>
 #include <sound/pcm_params.h>
index ba2531034a91264f763d53daabab533a98469bcb..047add8f3010efae75fd7128e2d293b75206e1e4 100644 (file)
@@ -21,7 +21,6 @@
 #include <linux/time.h>
 #include <linux/wait.h>
 #include <linux/module.h>
-#include <sound/driver.h>
 #include <sound/core.h>
 #include <sound/control.h>
 #include <sound/pcm.h>
index ce450304fb5364503c7c42433927148954e211a0..b88ca995fafbbd75f6e394c8b283a09e2efe67d8 100644 (file)
@@ -38,7 +38,6 @@
 #include <media/ir-common.h>
 #include <media/ir-kbd-i2c.h>
 #include <media/videobuf-dma-sg.h>
-#include <sound/driver.h>
 #include <sound/core.h>
 #include <sound/pcm.h>
 #if defined(CONFIG_VIDEO_SAA7134_DVB) || defined(CONFIG_VIDEO_SAA7134_DVB_MODULE)
index 0689189550bce6cab8b1800c4c161fdc14416e77..7da7fcb05640b4fdd74597c2dd5cbc2fd5db51d6 100644 (file)
@@ -24,7 +24,6 @@
 #include <linux/utsname.h>
 #include <linux/device.h>
 
-#include <sound/driver.h>
 #include <sound/core.h>
 #include <sound/initval.h>
 #include <sound/rawmidi.h>
index 6662cb02bafc81cfda77a8d206188b98dbbf06ab..ccee3b0700b3381a6609afcf23ac8692a91d3b95 100644 (file)
@@ -31,7 +31,6 @@
 #include <asm/arch/hardware.h>
 #include <asm/irq.h>
 
-#include <sound/driver.h>
 #include <sound/core.h>
 
 /* master codec clock source */
index fcaf44c14714c8f36f2c9d75eec216bb01027f41..faa0ed23d4bad8fefedb69627aae17f085ea16e3 100644 (file)
@@ -40,7 +40,6 @@
 #ifndef __OMAP_ALSA_H
 #define __OMAP_ALSA_H
 
-#include <sound/driver.h>
 #include <asm/arch/dma.h>
 #include <sound/core.h>
 #include <sound/pcm.h>
index 17eccd72013650e2bf93ee0e904add87114bd052..52bbe3bc25e1e5f283cc2d2d56603e06a127f4b1 100644 (file)
@@ -1,7 +1,6 @@
 #ifndef __ASM_ARCH_AUDIO_H__
 #define __ASM_ARCH_AUDIO_H__
 
-#include <sound/driver.h>
 #include <sound/core.h>
 #include <sound/pcm.h>
 
index d04f9e78c7c130f3732cb7546efa0f617c296853..d9aebdf6db639bd7b2c95dfb3cc9ddc4b0b710a6 100644 (file)
@@ -48,7 +48,7 @@
 #define AD1848_IFACE_CTRL      0x09    /* interface control - bits 7-2 MCE */
 #define AD1848_PIN_CTRL                0x0a    /* pin control */
 #define AD1848_TEST_INIT       0x0b    /* test and initialization */
-#define AD1848_MISC_INFO       0x0c    /* miscellaneaous information */
+#define AD1848_MISC_INFO       0x0c    /* miscellaneous information */
 #define AD1848_LOOPBACK                0x0d    /* loopback control */
 #define AD1848_DATA_UPR_CNT    0x0e    /* playback/capture upper base count */
 #define AD1848_DATA_LWR_CNT    0x0f    /* playback/capture lower base count */
diff --git a/include/sound/ainstr_fm.h b/include/sound/ainstr_fm.h
deleted file mode 100644 (file)
index c4afb1f..0000000
+++ /dev/null
@@ -1,134 +0,0 @@
-/*
- *  Advanced Linux Sound Architecture
- *
- *  FM (OPL2/3) Instrument Format
- *  Copyright (c) 2000 Uros Bizjak <uros@kss-loka.si>
- *
- *
- *   This program is free software; you can redistribute it and/or modify
- *   it under the terms of the GNU General Public License as published by
- *   the Free Software Foundation; either version 2 of the License, or
- *   (at your option) any later version.
- *
- *   This program is distributed in the hope that it will be useful,
- *   but WITHOUT ANY WARRANTY; without even the implied warranty of
- *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- *   GNU General Public License for more details.
- *
- *   You should have received a copy of the GNU General Public License
- *   along with this program; if not, write to the Free Software
- *   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA
- *
- */
-
-#ifndef __SOUND_AINSTR_FM_H
-#define __SOUND_AINSTR_FM_H
-
-#ifndef __KERNEL__
-#include <asm/types.h>
-#include <asm/byteorder.h>
-#endif
-
-/*
- *  share types (share ID 1)
- */
-
-#define FM_SHARE_FILE          0
-
-/*
- * FM operator
- */
-
-struct fm_operator {
-       unsigned char am_vib;
-       unsigned char ksl_level;
-       unsigned char attack_decay;
-       unsigned char sustain_release;
-       unsigned char wave_select;
-};
-
-/*
- *  Instrument
- */
-
-#define FM_PATCH_OPL2  0x01            /* OPL2 2 operators FM instrument */
-#define FM_PATCH_OPL3  0x02            /* OPL3 4 operators FM instrument */
-
-struct fm_instrument {
-       unsigned int share_id[4];       /* share id - zero = no sharing */
-       unsigned char type;             /* instrument type */
-
-       struct fm_operator op[4];
-       unsigned char feedback_connection[2];
-
-       unsigned char echo_delay;
-       unsigned char echo_atten;
-       unsigned char chorus_spread;
-       unsigned char trnsps;
-       unsigned char fix_dur;
-       unsigned char modes;
-       unsigned char fix_key;
-};
-
-/*
- *
- *    Kernel <-> user space
- *    Hardware (CPU) independent section
- *
- *    * = zero or more
- *    + = one or more
- *
- *    fm_xinstrument   FM_STRU_INSTR
- *
- */
-
-#define FM_STRU_INSTR  __cpu_to_be32(('I'<<24)|('N'<<16)|('S'<<8)|'T')
-
-/*
- * FM operator
- */
-
-struct fm_xoperator {
-       __u8 am_vib;
-       __u8 ksl_level;
-       __u8 attack_decay;
-       __u8 sustain_release;
-       __u8 wave_select;
-};
-
-/*
- *  Instrument
- */
-
-struct fm_xinstrument {
-       __u32 stype;                    /* structure type */
-
-       __u32 share_id[4];              /* share id - zero = no sharing */
-       __u8 type;                      /* instrument type */
-
-       struct fm_xoperator op[4];              /* fm operators */
-       __u8 feedback_connection[2];
-
-       __u8 echo_delay;
-       __u8 echo_atten;
-       __u8 chorus_spread;
-       __u8 trnsps;
-       __u8 fix_dur;
-       __u8 modes;
-       __u8 fix_key;
-};
-
-#ifdef __KERNEL__
-
-#include "seq_instr.h"
-
-int snd_seq_fm_init(struct snd_seq_kinstr_ops * ops,
-                   struct snd_seq_kinstr_ops * next);
-
-#endif
-
-/* typedefs for compatibility to user-space */
-typedef struct fm_xoperator fm_xoperator_t;
-typedef struct fm_xinstrument fm_xinstrument_t;
-
-#endif /* __SOUND_AINSTR_FM_H */
diff --git a/include/sound/ainstr_gf1.h b/include/sound/ainstr_gf1.h
deleted file mode 100644 (file)
index b62b665..0000000
+++ /dev/null
@@ -1,229 +0,0 @@
-/*
- *  Advanced Linux Sound Architecture
- *
- *  GF1 (GUS) Patch Instrument Format
- *  Copyright (c) 1994-99 by Jaroslav Kysela <perex@perex.cz>
- *
- *
- *   This program is free software; you can redistribute it and/or modify
- *   it under the terms of the GNU General Public License as published by
- *   the Free Software Foundation; either version 2 of the License, or
- *   (at your option) any later version.
- *
- *   This program is distributed in the hope that it will be useful,
- *   but WITHOUT ANY WARRANTY; without even the implied warranty of
- *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- *   GNU General Public License for more details.
- *
- *   You should have received a copy of the GNU General Public License
- *   along with this program; if not, write to the Free Software
- *   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA
- *
- */
-
-#ifndef __SOUND_AINSTR_GF1_H
-#define __SOUND_AINSTR_GF1_H
-
-#ifndef __KERNEL__
-#include <asm/types.h>
-#include <asm/byteorder.h>
-#endif
-
-/*
- *  share types (share ID 1)
- */
-
-#define GF1_SHARE_FILE                 0
-
-/*
- *  wave formats
- */
-
-#define GF1_WAVE_16BIT                 0x0001  /* 16-bit wave */
-#define GF1_WAVE_UNSIGNED              0x0002  /* unsigned wave */
-#define GF1_WAVE_INVERT                        0x0002  /* same as unsigned wave */
-#define GF1_WAVE_BACKWARD              0x0004  /* backward mode (maybe used for reverb or ping-ping loop) */
-#define GF1_WAVE_LOOP                  0x0008  /* loop mode */
-#define GF1_WAVE_BIDIR                 0x0010  /* bidirectional mode */
-#define GF1_WAVE_STEREO                        0x0100  /* stereo mode */
-#define GF1_WAVE_ULAW                  0x0200  /* uLaw compression mode */
-
-/*
- *  Wavetable definitions
- */
-
-struct gf1_wave {
-       unsigned int share_id[4];       /* share id - zero = no sharing */
-       unsigned int format;            /* wave format */
-
-       struct {
-               unsigned int number;    /* some other ID for this instrument */
-               unsigned int memory;    /* begin of waveform in onboard memory */
-               unsigned char *ptr;     /* pointer to waveform in system memory */
-       } address;
-
-       unsigned int size;              /* size of waveform in samples */
-       unsigned int start;             /* start offset in samples * 16 (lowest 4 bits - fraction) */
-       unsigned int loop_start;        /* bits loop start offset in samples * 16 (lowest 4 bits - fraction) */
-       unsigned int loop_end;          /* loop start offset in samples * 16 (lowest 4 bits - fraction) */
-       unsigned short loop_repeat;     /* loop repeat - 0 = forever */
-
-       unsigned char flags;            /* GF1 patch flags */
-       unsigned char pad;
-       unsigned int sample_rate;       /* sample rate in Hz */
-       unsigned int low_frequency;     /* low frequency range */
-       unsigned int high_frequency;    /* high frequency range */
-       unsigned int root_frequency;    /* root frequency range */
-       signed short tune;
-       unsigned char balance;
-       unsigned char envelope_rate[6];
-       unsigned char envelope_offset[6];
-       unsigned char tremolo_sweep;
-       unsigned char tremolo_rate;
-       unsigned char tremolo_depth;
-       unsigned char vibrato_sweep;
-       unsigned char vibrato_rate;
-       unsigned char vibrato_depth;
-       unsigned short scale_frequency;
-       unsigned short scale_factor;    /* 0-2048 or 0-2 */
-  
-       struct gf1_wave *next;
-};
-
-/*
- *  Instrument
- */
-
-#define IWFFFF_EXCLUDE_NONE            0x0000  /* exclusion mode - none */
-#define IWFFFF_EXCLUDE_SINGLE          0x0001  /* exclude single - single note from the instrument group */
-#define IWFFFF_EXCLUDE_MULTIPLE                0x0002  /* exclude multiple - stop only same note from this instrument */
-
-#define IWFFFF_EFFECT_NONE             0
-#define IWFFFF_EFFECT_REVERB           1
-#define IWFFFF_EFFECT_CHORUS           2
-#define IWFFFF_EFFECT_ECHO             3
-
-struct gf1_instrument {
-       unsigned short exclusion;
-       unsigned short exclusion_group; /* 0 - none, 1-65535 */
-
-       unsigned char effect1;          /* effect 1 */
-       unsigned char effect1_depth;    /* 0-127 */
-       unsigned char effect2;          /* effect 2 */
-       unsigned char effect2_depth;    /* 0-127 */
-
-       struct gf1_wave *wave;          /* first waveform */
-};
-
-/*
- *
- *    Kernel <-> user space
- *    Hardware (CPU) independent section
- *
- *    * = zero or more
- *    + = one or more
- *
- *    gf1_xinstrument          IWFFFF_STRU_INSTR
- *      +gf1_xwave             IWFFFF_STRU_WAVE
- *
- */
-
-#define GF1_STRU_WAVE          __cpu_to_be32(('W'<<24)|('A'<<16)|('V'<<8)|'E')
-#define GF1_STRU_INSTR         __cpu_to_be32(('I'<<24)|('N'<<16)|('S'<<8)|'T')
-
-/*
- *  Wavetable definitions
- */
-
-struct gf1_xwave {
-       __u32 stype;                    /* structure type */
-
-       __u32 share_id[4];              /* share id - zero = no sharing */
-       __u32 format;                   /* wave format */
-
-       __u32 size;                     /* size of waveform in samples */
-       __u32 start;                    /* start offset in samples * 16 (lowest 4 bits - fraction) */
-       __u32 loop_start;               /* bits loop start offset in samples * 16 (lowest 4 bits - fraction) */
-       __u32 loop_end;                 /* loop start offset in samples * 16 (lowest 4 bits - fraction) */
-       __u16 loop_repeat;              /* loop repeat - 0 = forever */
-
-       __u8 flags;                     /* GF1 patch flags */
-       __u8 pad;
-       __u32 sample_rate;              /* sample rate in Hz */
-       __u32 low_frequency;            /* low frequency range */
-       __u32 high_frequency;           /* high frequency range */
-       __u32 root_frequency;           /* root frequency range */
-       __s16 tune;
-       __u8 balance;
-       __u8 envelope_rate[6];
-       __u8 envelope_offset[6];
-       __u8 tremolo_sweep;
-       __u8 tremolo_rate;
-       __u8 tremolo_depth;
-       __u8 vibrato_sweep;
-       __u8 vibrato_rate;
-       __u8 vibrato_depth;
-       __u16 scale_frequency;
-       __u16 scale_factor;             /* 0-2048 or 0-2 */  
-};
-
-/*
- *  Instrument
- */
-
-struct gf1_xinstrument {
-       __u32 stype;
-       
-       __u16 exclusion;
-       __u16 exclusion_group;          /* 0 - none, 1-65535 */
-
-       __u8 effect1;                   /* effect 1 */
-       __u8 effect1_depth;             /* 0-127 */
-       __u8 effect2;                   /* effect 2 */
-       __u8 effect2_depth;             /* 0-127 */
-};
-
-/*
- *  Instrument info
- */
-
-#define GF1_INFO_ENVELOPE              (1<<0)
-#define GF1_INFO_TREMOLO               (1<<1)
-#define GF1_INFO_VIBRATO               (1<<2)
-
-struct gf1_info {
-       unsigned char flags;            /* supported wave flags */
-       unsigned char pad[3];
-       unsigned int features;          /* supported features */
-       unsigned int max8_len;          /* maximum 8-bit wave length */
-       unsigned int max16_len;         /* maximum 16-bit wave length */
-};
-
-#ifdef __KERNEL__
-
-#include "seq_instr.h"
-
-struct snd_gf1_ops {
-       void *private_data;
-       int (*info)(void *private_data, struct gf1_info *info);
-       int (*put_sample)(void *private_data, struct gf1_wave *wave,
-                         char __user *data, long len, int atomic);
-       int (*get_sample)(void *private_data, struct gf1_wave *wave,
-                         char __user *data, long len, int atomic);
-       int (*remove_sample)(void *private_data, struct gf1_wave *wave,
-                            int atomic);
-       void (*notify)(void *private_data, struct snd_seq_kinstr *instr, int what);
-       struct snd_seq_kinstr_ops kops;
-};
-
-int snd_seq_gf1_init(struct snd_gf1_ops *ops,
-                    void *private_data,
-                    struct snd_seq_kinstr_ops *next);
-
-#endif
-
-/* typedefs for compatibility to user-space */
-typedef struct gf1_xwave gf1_xwave_t;
-typedef struct gf1_xinstrument gf1_xinstrument_t;
-
-#endif /* __SOUND_AINSTR_GF1_H */
diff --git a/include/sound/ainstr_iw.h b/include/sound/ainstr_iw.h
deleted file mode 100644 (file)
index 11bd250..0000000
+++ /dev/null
@@ -1,384 +0,0 @@
-/*
- *  Advanced Linux Sound Architecture
- *
- *  InterWave FFFF Instrument Format
- *  Copyright (c) 1994-99 by Jaroslav Kysela <perex@perex.cz>
- *
- *
- *   This program is free software; you can redistribute it and/or modify
- *   it under the terms of the GNU General Public License as published by
- *   the Free Software Foundation; either version 2 of the License, or
- *   (at your option) any later version.
- *
- *   This program is distributed in the hope that it will be useful,
- *   but WITHOUT ANY WARRANTY; without even the implied warranty of
- *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- *   GNU General Public License for more details.
- *
- *   You should have received a copy of the GNU General Public License
- *   along with this program; if not, write to the Free Software
- *   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA
- *
- */
-
-#ifndef __SOUND_AINSTR_IW_H
-#define __SOUND_AINSTR_IW_H
-
-#ifndef __KERNEL__
-#include <asm/types.h>
-#include <asm/byteorder.h>
-#endif
-
-/*
- *  share types (share ID 1)
- */
-
-#define IWFFFF_SHARE_FILE              0
-
-/*
- *  wave formats
- */
-
-#define IWFFFF_WAVE_16BIT              0x0001  /* 16-bit wave */
-#define IWFFFF_WAVE_UNSIGNED           0x0002  /* unsigned wave */
-#define IWFFFF_WAVE_INVERT             0x0002  /* same as unsigned wave */
-#define IWFFFF_WAVE_BACKWARD           0x0004  /* backward mode (maybe used for reverb or ping-ping loop) */
-#define IWFFFF_WAVE_LOOP               0x0008  /* loop mode */
-#define IWFFFF_WAVE_BIDIR              0x0010  /* bidirectional mode */
-#define IWFFFF_WAVE_ULAW               0x0020  /* uLaw compressed wave */
-#define IWFFFF_WAVE_RAM                        0x0040  /* wave is _preloaded_ in RAM (it is used for ROM simulation) */
-#define IWFFFF_WAVE_ROM                        0x0080  /* wave is in ROM */
-#define IWFFFF_WAVE_STEREO             0x0100  /* wave is stereo */
-
-/*
- *  Wavetable definitions
- */
-
-struct iwffff_wave {
-       unsigned int share_id[4];       /* share id - zero = no sharing */
-       unsigned int format;            /* wave format */
-
-       struct {
-               unsigned int number;    /* some other ID for this wave */
-               unsigned int memory;    /* begin of waveform in onboard memory */
-               unsigned char *ptr;     /* pointer to waveform in system memory */
-       } address;
-
-       unsigned int size;              /* size of waveform in samples */
-       unsigned int start;             /* start offset in samples * 16 (lowest 4 bits - fraction) */
-       unsigned int loop_start;        /* bits loop start offset in samples * 16 (lowest 4 bits - fraction) */
-       unsigned int loop_end;          /* loop start offset in samples * 16 (lowest 4 bits - fraction) */
-       unsigned short loop_repeat;     /* loop repeat - 0 = forever */
-       unsigned int sample_ratio;      /* sample ratio (44100 * 1024 / rate) */
-       unsigned char attenuation;      /* 0 - 127 (no corresponding midi controller) */
-       unsigned char low_note;         /* lower frequency range for this waveform */
-       unsigned char high_note;        /* higher frequency range for this waveform */
-       unsigned char pad;
-  
-       struct iwffff_wave *next;
-};
-
-/*
- *  Layer
- */
-
-#define IWFFFF_LFO_SHAPE_TRIANGLE      0
-#define IWFFFF_LFO_SHAPE_POSTRIANGLE   1
-
-struct iwffff_lfo {
-       unsigned short freq;            /* (0-2047) 0.01Hz - 21.5Hz */
-       signed short depth;             /* volume +- (0-255) 0.48675dB/step */
-       signed short sweep;             /* 0 - 950 deciseconds */
-       unsigned char shape;            /* see to IWFFFF_LFO_SHAPE_XXXX */
-       unsigned char delay;            /* 0 - 255 deciseconds */
-};
-
-#define IWFFFF_ENV_FLAG_RETRIGGER      0x0001  /* flag - retrigger */
-
-#define IWFFFF_ENV_MODE_ONE_SHOT       0x0001  /* mode - one shot */
-#define IWFFFF_ENV_MODE_SUSTAIN                0x0002  /* mode - sustain */
-#define IWFFFF_ENV_MODE_NO_SUSTAIN     0x0003  /* mode - no sustain */
-
-#define IWFFFF_ENV_INDEX_VELOCITY      0x0001  /* index - velocity */
-#define IWFFFF_ENV_INDEX_FREQUENCY     0x0002  /* index - frequency */
-
-struct iwffff_env_point {
-       unsigned short offset;
-       unsigned short rate;
-};
-
-struct iwffff_env_record {
-       unsigned short nattack;
-       unsigned short nrelease;
-       unsigned short sustain_offset;
-       unsigned short sustain_rate;
-       unsigned short release_rate;
-       unsigned char hirange;
-       unsigned char pad;
-       struct iwffff_env_record *next;
-       /* points are stored here */
-       /* count of points = nattack + nrelease */
-};
-
-struct iwffff_env {
-       unsigned char flags;
-       unsigned char mode;
-       unsigned char index;
-       unsigned char pad;
-       struct iwffff_env_record *record;
-};
-
-#define IWFFFF_LAYER_FLAG_RETRIGGER    0x0001  /* retrigger */
-
-#define IWFFFF_LAYER_VELOCITY_TIME     0x0000  /* velocity mode = time */
-#define IWFFFF_LAYER_VELOCITY_RATE     0x0001  /* velocity mode = rate */
-
-#define IWFFFF_LAYER_EVENT_KUP         0x0000  /* layer event - key up */
-#define IWFFFF_LAYER_EVENT_KDOWN       0x0001  /* layer event - key down */
-#define IWFFFF_LAYER_EVENT_RETRIG      0x0002  /* layer event - retrigger */
-#define IWFFFF_LAYER_EVENT_LEGATO      0x0003  /* layer event - legato */
-
-struct iwffff_layer {
-       unsigned char flags;
-       unsigned char velocity_mode;
-       unsigned char layer_event;
-       unsigned char low_range;        /* range for layer based */
-       unsigned char high_range;       /* on either velocity or frequency */
-       unsigned char pan;              /* pan offset from CC1 (0 left - 127 right) */
-       unsigned char pan_freq_scale;   /* position based on frequency (0-127) */
-       unsigned char attenuation;      /* 0-127 (no corresponding midi controller) */
-       struct iwffff_lfo tremolo;              /* tremolo effect */
-       struct iwffff_lfo vibrato;              /* vibrato effect */
-       unsigned short freq_scale;      /* 0-2048, 1024 is equal to semitone scaling */
-       unsigned char freq_center;      /* center for keyboard frequency scaling */
-       unsigned char pad;
-       struct iwffff_env penv;         /* pitch envelope */
-       struct iwffff_env venv;         /* volume envelope */
-
-       struct iwffff_wave *wave;
-       struct iwffff_layer *next;
-};
-
-/*
- *  Instrument
- */
-
-#define IWFFFF_EXCLUDE_NONE            0x0000  /* exclusion mode - none */
-#define IWFFFF_EXCLUDE_SINGLE          0x0001  /* exclude single - single note from the instrument group */
-#define IWFFFF_EXCLUDE_MULTIPLE                0x0002  /* exclude multiple - stop only same note from this instrument */
-
-#define IWFFFF_LAYER_NONE              0x0000  /* not layered */
-#define IWFFFF_LAYER_ON                        0x0001  /* layered */
-#define IWFFFF_LAYER_VELOCITY          0x0002  /* layered by velocity */
-#define IWFFFF_LAYER_FREQUENCY         0x0003  /* layered by frequency */
-
-#define IWFFFF_EFFECT_NONE             0
-#define IWFFFF_EFFECT_REVERB           1
-#define IWFFFF_EFFECT_CHORUS           2
-#define IWFFFF_EFFECT_ECHO             3
-
-struct iwffff_instrument {
-       unsigned short exclusion;
-       unsigned short layer_type;
-       unsigned short exclusion_group; /* 0 - none, 1-65535 */
-
-       unsigned char effect1;          /* effect 1 */
-       unsigned char effect1_depth;    /* 0-127 */
-       unsigned char effect2;          /* effect 2 */
-       unsigned char effect2_depth;    /* 0-127 */
-
-       struct iwffff_layer *layer;             /* first layer */
-};
-
-/*
- *
- *    Kernel <-> user space
- *    Hardware (CPU) independent section
- *
- *    * = zero or more
- *    + = one or more
- *
- *    iwffff_xinstrument               IWFFFF_STRU_INSTR
- *      +iwffff_xlayer                 IWFFFF_STRU_LAYER
- *        *iwffff_xenv_record          IWFFFF_STRU_ENV_RECT (tremolo)
- *        *iwffff_xenv_record          IWFFFF_STRU_EVN_RECT (vibrato)
- *          +iwffff_xwave              IWFFFF_STRU_WAVE
- *
- */
-
-#define IWFFFF_STRU_WAVE       __cpu_to_be32(('W'<<24)|('A'<<16)|('V'<<8)|'E')
-#define IWFFFF_STRU_ENV_RECP   __cpu_to_be32(('E'<<24)|('N'<<16)|('R'<<8)|'P')
-#define IWFFFF_STRU_ENV_RECV   __cpu_to_be32(('E'<<24)|('N'<<16)|('R'<<8)|'V')
-#define IWFFFF_STRU_LAYER      __cpu_to_be32(('L'<<24)|('A'<<16)|('Y'<<8)|'R')
-#define IWFFFF_STRU_INSTR      __cpu_to_be32(('I'<<24)|('N'<<16)|('S'<<8)|'T')
-
-/*
- *  Wavetable definitions
- */
-
-struct iwffff_xwave {
-       __u32 stype;                    /* structure type */
-
-       __u32 share_id[4];              /* share id - zero = no sharing */
-
-       __u32 format;                   /* wave format */
-       __u32 offset;                   /* offset to ROM (address) */
-
-       __u32 size;                     /* size of waveform in samples */
-       __u32 start;                    /* start offset in samples * 16 (lowest 4 bits - fraction) */
-       __u32 loop_start;               /* bits loop start offset in samples * 16 (lowest 4 bits - fraction) */
-       __u32 loop_end;                 /* loop start offset in samples * 16 (lowest 4 bits - fraction) */
-       __u16 loop_repeat;              /* loop repeat - 0 = forever */
-       __u32 sample_ratio;             /* sample ratio (44100 * 1024 / rate) */
-       __u8 attenuation;               /* 0 - 127 (no corresponding midi controller) */
-       __u8 low_note;                  /* lower frequency range for this waveform */
-       __u8 high_note;                 /* higher frequency range for this waveform */
-       __u8 pad;
-};
-
-/*
- *  Layer
- */
-
-struct iwffff_xlfo {
-       __u16 freq;                     /* (0-2047) 0.01Hz - 21.5Hz */
-       __s16 depth;                    /* volume +- (0-255) 0.48675dB/step */
-       __s16 sweep;                    /* 0 - 950 deciseconds */
-       __u8 shape;                     /* see to ULTRA_IW_LFO_SHAPE_XXXX */
-       __u8 delay;                     /* 0 - 255 deciseconds */
-};
-
-struct iwffff_xenv_point {
-       __u16 offset;
-       __u16 rate;
-};
-
-struct iwffff_xenv_record {
-       __u32 stype;
-       __u16 nattack;
-       __u16 nrelease;
-       __u16 sustain_offset;
-       __u16 sustain_rate;
-       __u16 release_rate;
-       __u8 hirange;
-       __u8 pad;
-       /* points are stored here.. */
-       /* count of points = nattack + nrelease */
-};
-
-struct iwffff_xenv {
-       __u8 flags;
-       __u8 mode;
-       __u8 index;
-       __u8 pad;
-};
-
-struct iwffff_xlayer {
-       __u32 stype;
-       __u8 flags;
-       __u8 velocity_mode;
-       __u8 layer_event;
-       __u8 low_range;                 /* range for layer based */
-       __u8 high_range;                /* on either velocity or frequency */
-       __u8 pan;                       /* pan offset from CC1 (0 left - 127 right) */
-       __u8 pan_freq_scale;            /* position based on frequency (0-127) */
-       __u8 attenuation;               /* 0-127 (no corresponding midi controller) */
-       struct iwffff_xlfo tremolo;             /* tremolo effect */
-       struct iwffff_xlfo vibrato;             /* vibrato effect */
-       __u16 freq_scale;               /* 0-2048, 1024 is equal to semitone scaling */
-       __u8 freq_center;               /* center for keyboard frequency scaling */
-       __u8 pad;
-       struct iwffff_xenv penv;                /* pitch envelope */
-       struct iwffff_xenv venv;                /* volume envelope */
-};
-
-/*
- *  Instrument
- */
-
-struct iwffff_xinstrument {
-       __u32 stype;
-       
-       __u16 exclusion;
-       __u16 layer_type;
-       __u16 exclusion_group;          /* 0 - none, 1-65535 */
-
-       __u8 effect1;                   /* effect 1 */
-       __u8 effect1_depth;             /* 0-127 */
-       __u8 effect2;                   /* effect 2 */
-       __u8 effect2_depth;             /* 0-127 */
-};
-
-/*
- *  ROM support
- *    InterWave ROMs are Little-Endian (x86)
- */
-
-#define IWFFFF_ROM_HDR_SIZE    512
-
-struct iwffff_rom_header {
-       __u8 iwave[8];
-       __u8 revision;
-       __u8 series_number;
-       __u8 series_name[16];
-       __u8 date[10];
-       __u16 vendor_revision_major;
-       __u16 vendor_revision_minor;
-       __u32 rom_size;
-       __u8 copyright[128];
-       __u8 vendor_name[64];
-       __u8 description[128];
-};
-
-/*
- *  Instrument info
- */
-
-#define IWFFFF_INFO_LFO_VIBRATO                (1<<0)
-#define IWFFFF_INFO_LFO_VIBRATO_SHAPE  (1<<1)
-#define IWFFFF_INFO_LFO_TREMOLO                (1<<2)
-#define IWFFFF_INFO_LFO_TREMOLO_SHAPE  (1<<3)
-
-struct iwffff_info {
-       unsigned int format;            /* supported format bits */
-       unsigned int effects;           /* supported effects (1 << IWFFFF_EFFECT*) */
-       unsigned int lfos;              /* LFO effects */
-       unsigned int max8_len;          /* maximum 8-bit wave length */
-       unsigned int max16_len;         /* maximum 16-bit wave length */
-};
-
-#ifdef __KERNEL__
-
-#include "seq_instr.h"
-
-struct snd_iwffff_ops {
-       void *private_data;
-       int (*info)(void *private_data, struct iwffff_info *info);
-       int (*put_sample)(void *private_data, struct iwffff_wave *wave,
-                         char __user *data, long len, int atomic);
-       int (*get_sample)(void *private_data, struct iwffff_wave *wave,
-                         char __user *data, long len, int atomic);
-       int (*remove_sample)(void *private_data, struct iwffff_wave *wave,
-                            int atomic);
-       void (*notify)(void *private_data, struct snd_seq_kinstr *instr, int what);
-       struct snd_seq_kinstr_ops kops;
-};
-
-int snd_seq_iwffff_init(struct snd_iwffff_ops *ops,
-                       void *private_data,
-                        struct snd_seq_kinstr_ops *next);
-
-#endif
-
-/* typedefs for compatibility to user-space */
-typedef struct iwffff_xwave iwffff_xwave_t;
-typedef struct iwffff_xlfo iwffff_xlfo_t;
-typedef struct iwffff_xenv_point iwffff_xenv_point_t;
-typedef struct iwffff_xenv_record iwffff_xenv_record_t;
-typedef struct iwffff_xenv iwffff_xenv_t;
-typedef struct iwffff_xlayer iwffff_xlayer_t;
-typedef struct iwffff_xinstrument iwffff_xinstrument_t;
-typedef struct iwffff_rom_header iwffff_rom_header_t;
-typedef struct iwffff_info iwffff_info_t;
-
-#endif /* __SOUND_AINSTR_IW_H */
diff --git a/include/sound/ainstr_simple.h b/include/sound/ainstr_simple.h
deleted file mode 100644 (file)
index da08e72..0000000
+++ /dev/null
@@ -1,159 +0,0 @@
-/*
- *  Advanced Linux Sound Architecture
- *
- *  Simple (MOD player) Instrument Format
- *  Copyright (c) 1994-99 by Jaroslav Kysela <perex@perex.cz>
- *
- *
- *   This program is free software; you can redistribute it and/or modify
- *   it under the terms of the GNU General Public License as published by
- *   the Free Software Foundation; either version 2 of the License, or
- *   (at your option) any later version.
- *
- *   This program is distributed in the hope that it will be useful,
- *   but WITHOUT ANY WARRANTY; without even the implied warranty of
- *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- *   GNU General Public License for more details.
- *
- *   You should have received a copy of the GNU General Public License
- *   along with this program; if not, write to the Free Software
- *   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA
- *
- */
-
-#ifndef __SOUND_AINSTR_SIMPLE_H
-#define __SOUND_AINSTR_SIMPLE_H
-
-#ifndef __KERNEL__
-#include <asm/types.h>
-#include <asm/byteorder.h>
-#endif
-
-/*
- *  share types (share ID 1)
- */
-
-#define SIMPLE_SHARE_FILE              0
-
-/*
- *  wave formats
- */
-
-#define SIMPLE_WAVE_16BIT              0x0001  /* 16-bit wave */
-#define SIMPLE_WAVE_UNSIGNED           0x0002  /* unsigned wave */
-#define SIMPLE_WAVE_INVERT             0x0002  /* same as unsigned wave */
-#define SIMPLE_WAVE_BACKWARD           0x0004  /* backward mode (maybe used for reverb or ping-ping loop) */
-#define SIMPLE_WAVE_LOOP               0x0008  /* loop mode */
-#define SIMPLE_WAVE_BIDIR              0x0010  /* bidirectional mode */
-#define SIMPLE_WAVE_STEREO             0x0100  /* stereo wave */
-#define SIMPLE_WAVE_ULAW               0x0200  /* uLaw compression mode */
-
-/*
- *  instrument effects
- */
-
-#define SIMPLE_EFFECT_NONE             0
-#define SIMPLE_EFFECT_REVERB           1
-#define SIMPLE_EFFECT_CHORUS           2
-#define SIMPLE_EFFECT_ECHO             3
-
-/*
- *  instrument info
- */
-
-struct simple_instrument_info {
-       unsigned int format;            /* supported format bits */
-       unsigned int effects;           /* supported effects (1 << SIMPLE_EFFECT_*) */
-       unsigned int max8_len;          /* maximum 8-bit wave length */
-       unsigned int max16_len;         /* maximum 16-bit wave length */
-};
-
-/*
- *  Instrument
- */
-
-struct simple_instrument {
-       unsigned int share_id[4];       /* share id - zero = no sharing */
-       unsigned int format;            /* wave format */
-
-       struct {
-               unsigned int number;    /* some other ID for this instrument */
-               unsigned int memory;    /* begin of waveform in onboard memory */
-               unsigned char *ptr;     /* pointer to waveform in system memory */
-       } address;
-
-       unsigned int size;              /* size of waveform in samples */
-       unsigned int start;             /* start offset in samples * 16 (lowest 4 bits - fraction) */
-       unsigned int loop_start;        /* loop start offset in samples * 16 (lowest 4 bits - fraction) */
-       unsigned int loop_end;          /* loop end offset in samples * 16 (lowest 4 bits - fraction) */
-       unsigned short loop_repeat;     /* loop repeat - 0 = forever */
-
-       unsigned char effect1;          /* effect 1 */
-       unsigned char effect1_depth;    /* 0-127 */
-       unsigned char effect2;          /* effect 2 */
-       unsigned char effect2_depth;    /* 0-127 */
-};
-
-/*
- *
- *    Kernel <-> user space
- *    Hardware (CPU) independent section
- *
- *    * = zero or more
- *    + = one or more
- *
- *    simple_xinstrument       SIMPLE_STRU_INSTR
- *
- */
-
-#define SIMPLE_STRU_INSTR      __cpu_to_be32(('I'<<24)|('N'<<16)|('S'<<8)|'T')
-
-/*
- *  Instrument
- */
-
-struct simple_xinstrument {
-       __u32 stype;
-
-       __u32 share_id[4];              /* share id - zero = no sharing */
-       __u32 format;                   /* wave format */
-
-       __u32 size;                     /* size of waveform in samples */
-       __u32 start;                    /* start offset in samples * 16 (lowest 4 bits - fraction) */
-       __u32 loop_start;               /* bits loop start offset in samples * 16 (lowest 4 bits - fraction) */
-       __u32 loop_end;                 /* loop start offset in samples * 16 (lowest 4 bits - fraction) */
-       __u16 loop_repeat;              /* loop repeat - 0 = forever */
-       
-       __u8 effect1;                   /* effect 1 */
-       __u8 effect1_depth;             /* 0-127 */
-       __u8 effect2;                   /* effect 2 */
-       __u8 effect2_depth;             /* 0-127 */
-};
-
-#ifdef __KERNEL__
-
-#include "seq_instr.h"
-
-struct snd_simple_ops {
-       void *private_data;
-       int (*info)(void *private_data, struct simple_instrument_info *info);
-       int (*put_sample)(void *private_data, struct simple_instrument *instr,
-                         char __user *data, long len, int atomic);
-       int (*get_sample)(void *private_data, struct simple_instrument *instr,
-                         char __user *data, long len, int atomic);
-       int (*remove_sample)(void *private_data, struct simple_instrument *instr,
-                            int atomic);
-       void (*notify)(void *private_data, struct snd_seq_kinstr *instr, int what);
-       struct snd_seq_kinstr_ops kops;
-};
-
-int snd_seq_simple_init(struct snd_simple_ops *ops,
-                       void *private_data,
-                       struct snd_seq_kinstr_ops *next);
-
-#endif
-
-/* typedefs for compatibility to user-space */
-typedef struct simple_xinstrument simple_xinstrument_t;
-
-#endif /* __SOUND_AINSTR_SIMPLE_H */
index 891cf1aea8b139db918705149a9e7ffb25760fd5..6153b91cdc3ea4f71e79f9ea44a7550f4995b54b 100644 (file)
@@ -68,7 +68,7 @@ struct snd_akm4xxx {
        enum {
                SND_AK4524, SND_AK4528, SND_AK4529,
                SND_AK4355, SND_AK4358, SND_AK4381,
-               SND_AK5365
+               SND_AK5365, NON_AKM
        } type;
 
        /* (array) information of combined codecs */
index 64daccbe8b29196f7969631bf0d44e69200f2ca3..1505e6d5ef8241081c24c74b6218e01a37afc8d2 100644 (file)
 #define SNDRV_SEQ_EVENT_PORT_SUBSCRIBED        66      /* ports connected */
 #define SNDRV_SEQ_EVENT_PORT_UNSUBSCRIBED 67   /* ports disconnected */
 
-/** synthesizer events
- * event data type = snd_seq_eve_sample_control
- */
-#define SNDRV_SEQ_EVENT_SAMPLE         70      /* sample select */
-#define SNDRV_SEQ_EVENT_SAMPLE_CLUSTER 71      /* sample cluster select */
-#define SNDRV_SEQ_EVENT_SAMPLE_START   72      /* voice start */
-#define SNDRV_SEQ_EVENT_SAMPLE_STOP    73      /* voice stop */
-#define SNDRV_SEQ_EVENT_SAMPLE_FREQ    74      /* playback frequency */
-#define SNDRV_SEQ_EVENT_SAMPLE_VOLUME  75      /* volume and balance */
-#define SNDRV_SEQ_EVENT_SAMPLE_LOOP    76      /* sample loop */
-#define SNDRV_SEQ_EVENT_SAMPLE_POSITION        77      /* sample position */
-#define SNDRV_SEQ_EVENT_SAMPLE_PRIVATE1        78      /* private (hardware dependent) event */
+/* 70-89:  synthesizer events - obsoleted */
 
 /** user-defined events with fixed length
  * event data type = any
 #define SNDRV_SEQ_EVENT_USR8           98
 #define SNDRV_SEQ_EVENT_USR9           99
 
-/** instrument layer
- * variable length data can be passed directly to the driver
- */
-#define SNDRV_SEQ_EVENT_INSTR_BEGIN    100     /* begin of instrument management */
-#define SNDRV_SEQ_EVENT_INSTR_END      101     /* end of instrument management */
-#define SNDRV_SEQ_EVENT_INSTR_INFO     102     /* instrument interface info */
-#define SNDRV_SEQ_EVENT_INSTR_INFO_RESULT 103  /* result */
-#define SNDRV_SEQ_EVENT_INSTR_FINFO    104     /* get format info */
-#define SNDRV_SEQ_EVENT_INSTR_FINFO_RESULT 105 /* get format info */
-#define SNDRV_SEQ_EVENT_INSTR_RESET    106     /* reset instrument memory */
-#define SNDRV_SEQ_EVENT_INSTR_STATUS   107     /* instrument interface status */
-#define SNDRV_SEQ_EVENT_INSTR_STATUS_RESULT 108        /* result */
-#define SNDRV_SEQ_EVENT_INSTR_PUT      109     /* put instrument to port */
-#define SNDRV_SEQ_EVENT_INSTR_GET      110     /* get instrument from port */
-#define SNDRV_SEQ_EVENT_INSTR_GET_RESULT 111   /* result */
-#define SNDRV_SEQ_EVENT_INSTR_FREE     112     /* free instrument(s) */
-#define SNDRV_SEQ_EVENT_INSTR_LIST     113     /* instrument list */
-#define SNDRV_SEQ_EVENT_INSTR_LIST_RESULT 114  /* result */
-#define SNDRV_SEQ_EVENT_INSTR_CLUSTER  115     /* cluster parameters */
-#define SNDRV_SEQ_EVENT_INSTR_CLUSTER_GET 116  /* get cluster parameters */
-#define SNDRV_SEQ_EVENT_INSTR_CLUSTER_RESULT 117 /* result */
-#define SNDRV_SEQ_EVENT_INSTR_CHANGE   118     /* instrument change */
+/* 100-118: instrument layer - obsoleted */
 /* 119-129: reserved */
 
 /* 130-139: variable length events
@@ -258,78 +226,6 @@ struct snd_seq_ev_ext {
        void *ptr;              /* pointer to data (note: maybe 64-bit) */
 } __attribute__((packed));
 
-/* Instrument cluster type */
-typedef unsigned int snd_seq_instr_cluster_t;
-
-/* Instrument type */
-struct snd_seq_instr {
-       snd_seq_instr_cluster_t cluster;
-       unsigned int std;               /* the upper byte means a private instrument (owner - client #) */
-       unsigned short bank;
-       unsigned short prg;
-};
-
-       /* sample number */
-struct snd_seq_ev_sample {
-       unsigned int std;
-       unsigned short bank;
-       unsigned short prg;
-};
-
-       /* sample cluster */
-struct snd_seq_ev_cluster {
-       snd_seq_instr_cluster_t cluster;
-};
-
-       /* sample position */
-typedef unsigned int snd_seq_position_t; /* playback position (in samples) * 16 */
-
-       /* sample stop mode */
-enum {
-       SAMPLE_STOP_IMMEDIATELY = 0,    /* terminate playing immediately */
-       SAMPLE_STOP_VENVELOPE = 1,      /* finish volume envelope */
-       SAMPLE_STOP_LOOP = 2            /* terminate loop and finish wave */
-};
-
-       /* sample frequency */
-typedef int snd_seq_frequency_t; /* playback frequency in HZ * 16 */
-
-       /* sample volume control; if any value is set to -1 == do not change */
-struct snd_seq_ev_volume {
-       signed short volume;    /* range: 0-16383 */
-       signed short lr;        /* left-right balance; range: 0-16383 */
-       signed short fr;        /* front-rear balance; range: 0-16383 */
-       signed short du;        /* down-up balance; range: 0-16383 */
-};
-
-       /* simple loop redefinition */
-struct snd_seq_ev_loop {
-       unsigned int start;     /* loop start (in samples) * 16 */
-       unsigned int end;       /* loop end (in samples) * 16 */
-};
-
-struct snd_seq_ev_sample_control {
-       unsigned char channel;
-       unsigned char unused1, unused2, unused3;        /* pad */
-       union {
-               struct snd_seq_ev_sample sample;
-               struct snd_seq_ev_cluster cluster;
-               snd_seq_position_t position;
-               int stop_mode;
-               snd_seq_frequency_t frequency;
-               struct snd_seq_ev_volume volume;
-               struct snd_seq_ev_loop loop;
-               unsigned char raw8[8];
-       } param;
-};
-
-
-
-/* INSTR_BEGIN event */
-struct snd_seq_ev_instr_begin {
-       int timeout;            /* zero = forever, otherwise timeout in ms */
-};
-
 struct snd_seq_result {
        int event;              /* processed event type */
        int result;
@@ -399,8 +295,6 @@ struct snd_seq_event {
                struct snd_seq_addr addr;
                struct snd_seq_connect connect;
                struct snd_seq_result result;
-               struct snd_seq_ev_instr_begin instr_begin;
-               struct snd_seq_ev_sample_control sample;
                struct snd_seq_ev_quote quote;
        } data;
 };
@@ -441,8 +335,6 @@ struct snd_seq_event_bounce {
 #define snd_seq_ev_is_user_type(ev)    ((ev)->type >= 90 && (ev)->type < 99)
 /* fixed length events: 0-99 */
 #define snd_seq_ev_is_fixed_type(ev)   ((ev)->type < 100)
-/* instrument layer events: 100-129 */
-#define snd_seq_ev_is_instr_type(ev)   ((ev)->type >= 100 && (ev)->type < 130)
 /* variable length events: 130-139 */
 #define snd_seq_ev_is_variable_type(ev)        ((ev)->type >= 130 && (ev)->type < 140)
 /* reserved for kernel */
@@ -737,136 +629,6 @@ struct snd_seq_query_subs {
 };
 
 
-/*
- *  Instrument abstraction layer
- *     - based on events
- */
-
-/* instrument types */
-#define SNDRV_SEQ_INSTR_ATYPE_DATA     0       /* instrument data */
-#define SNDRV_SEQ_INSTR_ATYPE_ALIAS    1       /* instrument alias */
-
-/* instrument ASCII identifiers */
-#define SNDRV_SEQ_INSTR_ID_DLS1                "DLS1"
-#define SNDRV_SEQ_INSTR_ID_DLS2                "DLS2"
-#define SNDRV_SEQ_INSTR_ID_SIMPLE      "Simple Wave"
-#define SNDRV_SEQ_INSTR_ID_SOUNDFONT   "SoundFont"
-#define SNDRV_SEQ_INSTR_ID_GUS_PATCH   "GUS Patch"
-#define SNDRV_SEQ_INSTR_ID_INTERWAVE   "InterWave FFFF"
-#define SNDRV_SEQ_INSTR_ID_OPL2_3      "OPL2/3 FM"
-#define SNDRV_SEQ_INSTR_ID_OPL4                "OPL4"
-
-/* instrument types */
-#define SNDRV_SEQ_INSTR_TYPE0_DLS1     (1<<0)  /* MIDI DLS v1 */
-#define SNDRV_SEQ_INSTR_TYPE0_DLS2     (1<<1)  /* MIDI DLS v2 */
-#define SNDRV_SEQ_INSTR_TYPE1_SIMPLE   (1<<0)  /* Simple Wave */
-#define SNDRV_SEQ_INSTR_TYPE1_SOUNDFONT        (1<<1)  /* EMU SoundFont */
-#define SNDRV_SEQ_INSTR_TYPE1_GUS_PATCH        (1<<2)  /* Gravis UltraSound Patch */
-#define SNDRV_SEQ_INSTR_TYPE1_INTERWAVE        (1<<3)  /* InterWave FFFF */
-#define SNDRV_SEQ_INSTR_TYPE2_OPL2_3   (1<<0)  /* Yamaha OPL2/3 FM */
-#define SNDRV_SEQ_INSTR_TYPE2_OPL4     (1<<1)  /* Yamaha OPL4 */
-
-/* put commands */
-#define SNDRV_SEQ_INSTR_PUT_CMD_CREATE 0
-#define SNDRV_SEQ_INSTR_PUT_CMD_REPLACE        1
-#define SNDRV_SEQ_INSTR_PUT_CMD_MODIFY 2
-#define SNDRV_SEQ_INSTR_PUT_CMD_ADD    3
-#define SNDRV_SEQ_INSTR_PUT_CMD_REMOVE 4
-
-/* get commands */
-#define SNDRV_SEQ_INSTR_GET_CMD_FULL   0
-#define SNDRV_SEQ_INSTR_GET_CMD_PARTIAL        1
-
-/* query flags */
-#define SNDRV_SEQ_INSTR_QUERY_FOLLOW_ALIAS (1<<0)
-
-/* free commands */
-#define SNDRV_SEQ_INSTR_FREE_CMD_ALL           0
-#define SNDRV_SEQ_INSTR_FREE_CMD_PRIVATE       1
-#define SNDRV_SEQ_INSTR_FREE_CMD_CLUSTER       2
-#define SNDRV_SEQ_INSTR_FREE_CMD_SINGLE                3
-
-/* size of ROM/RAM */
-typedef unsigned int snd_seq_instr_size_t;
-
-/* INSTR_INFO */
-
-struct snd_seq_instr_info {
-       int result;                     /* operation result */
-       unsigned int formats[8];        /* bitmap of supported formats */
-       int ram_count;                  /* count of RAM banks */
-       snd_seq_instr_size_t ram_sizes[16]; /* size of RAM banks */
-       int rom_count;                  /* count of ROM banks */
-       snd_seq_instr_size_t rom_sizes[8]; /* size of ROM banks */
-       char reserved[128];
-};
-
-/* INSTR_STATUS */
-
-struct snd_seq_instr_status {
-       int result;                     /* operation result */
-       snd_seq_instr_size_t free_ram[16]; /* free RAM in banks */
-       int instrument_count;           /* count of downloaded instruments */
-       char reserved[128];
-};
-
-/* INSTR_FORMAT_INFO */
-
-struct snd_seq_instr_format_info {
-       char format[16];                /* format identifier - SNDRV_SEQ_INSTR_ID_* */  
-       unsigned int len;               /* max data length (without this structure) */
-};
-
-struct snd_seq_instr_format_info_result {
-       int result;                     /* operation result */
-       char format[16];                /* format identifier */
-       unsigned int len;               /* filled data length (without this structure) */
-};
-
-/* instrument data */
-struct snd_seq_instr_data {
-       char name[32];                  /* instrument name */
-       char reserved[16];              /* for the future use */
-       int type;                       /* instrument type */
-       union {
-               char format[16];        /* format identifier */
-               struct snd_seq_instr alias;
-       } data;
-};
-
-/* INSTR_PUT/GET, data are stored in one block (extended), header + data */
-
-struct snd_seq_instr_header {
-       union {
-               struct snd_seq_instr instr;
-               snd_seq_instr_cluster_t cluster;
-       } id;                           /* instrument identifier */
-       unsigned int cmd;               /* get/put/free command */
-       unsigned int flags;             /* query flags (only for get) */
-       unsigned int len;               /* real instrument data length (without header) */
-       int result;                     /* operation result */
-       char reserved[16];              /* for the future */
-       struct snd_seq_instr_data data; /* instrument data (for put/get result) */
-};
-
-/* INSTR_CLUSTER_SET */
-
-struct snd_seq_instr_cluster_set {
-       snd_seq_instr_cluster_t cluster; /* cluster identifier */
-       char name[32];                  /* cluster name */
-       int priority;                   /* cluster priority */
-       char reserved[64];              /* for the future use */
-};
-
-/* INSTR_CLUSTER_GET */
-
-struct snd_seq_instr_cluster_get {
-       snd_seq_instr_cluster_t cluster; /* cluster identifier */
-       char name[32];                  /* cluster name */
-       int priority;                   /* cluster priority */
-       char reserved[64];              /* for the future use */
-};
-
 /*
  *  IOCTL commands
  */
index af9d11d315e94c272b4eb32a6932b817add375b3..3eaf155b850d8b0c266ea0d930823317033948c8 100644 (file)
@@ -95,7 +95,7 @@ enum {
        SNDRV_HWDEP_IFACE_HDA,          /* HD-audio */
 
        /* Don't forget to change the following: */
-       SNDRV_HWDEP_IFACE_LAST = SNDRV_HWDEP_IFACE_SB_RC
+       SNDRV_HWDEP_IFACE_LAST = SNDRV_HWDEP_IFACE_HDA
 };
 
 struct snd_hwdep_info {
@@ -138,7 +138,7 @@ enum {
  *                                                                           *
  *****************************************************************************/
 
-#define SNDRV_PCM_VERSION              SNDRV_PROTOCOL_VERSION(2, 0, 8)
+#define SNDRV_PCM_VERSION              SNDRV_PROTOCOL_VERSION(2, 0, 9)
 
 typedef unsigned long snd_pcm_uframes_t;
 typedef signed long snd_pcm_sframes_t;
@@ -354,8 +354,8 @@ struct snd_pcm_hw_params {
 
 enum {
        SNDRV_PCM_TSTAMP_NONE = 0,
-       SNDRV_PCM_TSTAMP_MMAP,
-       SNDRV_PCM_TSTAMP_LAST = SNDRV_PCM_TSTAMP_MMAP,
+       SNDRV_PCM_TSTAMP_ENABLE,
+       SNDRV_PCM_TSTAMP_LAST = SNDRV_PCM_TSTAMP_ENABLE,
 };
 
 struct snd_pcm_sw_params {
@@ -363,7 +363,7 @@ struct snd_pcm_sw_params {
        unsigned int period_step;
        unsigned int sleep_min;                 /* min ticks to sleep */
        snd_pcm_uframes_t avail_min;            /* min avail frames for wakeup */
-       snd_pcm_uframes_t xfer_align;           /* xfer size need to be a multiple */
+       snd_pcm_uframes_t xfer_align;           /* obsolete: xfer size need to be a multiple */
        snd_pcm_uframes_t start_threshold;      /* min hw_avail frames for automatic start */
        snd_pcm_uframes_t stop_threshold;       /* min avail frames for automatic stop */
        snd_pcm_uframes_t silence_threshold;    /* min distance from noise for silence filling */
@@ -434,10 +434,17 @@ struct snd_xfern {
        snd_pcm_uframes_t frames;
 };
 
+enum {
+       SNDRV_PCM_TSTAMP_TYPE_GETTIMEOFDAY = 0, /* gettimeofday equivalent */
+       SNDRV_PCM_TSTAMP_TYPE_MONOTONIC,        /* posix_clock_monotonic equivalent */
+       SNDRV_PCM_TSTAMP_TYPE_LAST = SNDRV_PCM_TSTAMP_TYPE_MONOTONIC,
+};
+
 enum {
        SNDRV_PCM_IOCTL_PVERSION = _IOR('A', 0x00, int),
        SNDRV_PCM_IOCTL_INFO = _IOR('A', 0x01, struct snd_pcm_info),
        SNDRV_PCM_IOCTL_TSTAMP = _IOW('A', 0x02, int),
+       SNDRV_PCM_IOCTL_TTSTAMP = _IOW('A', 0x03, int),
        SNDRV_PCM_IOCTL_HW_REFINE = _IOWR('A', 0x10, struct snd_pcm_hw_params),
        SNDRV_PCM_IOCTL_HW_PARAMS = _IOWR('A', 0x11, struct snd_pcm_hw_params),
        SNDRV_PCM_IOCTL_HW_FREE = _IO('A', 0x12),
@@ -689,7 +696,7 @@ struct snd_timer_tread {
  *                                                                          *
  ****************************************************************************/
 
-#define SNDRV_CTL_VERSION              SNDRV_PROTOCOL_VERSION(2, 0, 4)
+#define SNDRV_CTL_VERSION              SNDRV_PROTOCOL_VERSION(2, 0, 5)
 
 struct snd_ctl_card_info {
        int card;                       /* card number */
@@ -738,8 +745,7 @@ typedef int __bitwise snd_ctl_elem_iface_t;
 #define SNDRV_CTL_ELEM_ACCESS_OWNER            (1<<10) /* write lock owner */
 #define SNDRV_CTL_ELEM_ACCESS_TLV_CALLBACK     (1<<28) /* kernel use a TLV callback */ 
 #define SNDRV_CTL_ELEM_ACCESS_USER             (1<<29) /* user space element */
-#define SNDRV_CTL_ELEM_ACCESS_DINDIRECT                (1<<30) /* indirect access for matrix dimensions in the info structure */
-#define SNDRV_CTL_ELEM_ACCESS_INDIRECT         (1<<31) /* indirect access for element value in the value structure */
+/* bits 30 and 31 are obsoleted (for indirect access) */
 
 /* for further details see the ACPI and PCI power management specification */
 #define SNDRV_CTL_POWER_D0             0x0000  /* full On */
@@ -793,30 +799,30 @@ struct snd_ctl_elem_info {
        } value;
        union {
                unsigned short d[4];            /* dimensions */
-               unsigned short *d_ptr;          /* indirect */
+               unsigned short *d_ptr;          /* indirect - obsoleted */
        } dimen;
        unsigned char reserved[64-4*sizeof(unsigned short)];
 };
 
 struct snd_ctl_elem_value {
        struct snd_ctl_elem_id id;      /* W: element ID */
-       unsigned int indirect: 1;       /* W: use indirect pointer (xxx_ptr member) */
+       unsigned int indirect: 1;       /* W: indirect access - obsoleted */
         union {
                union {
                        long value[128];
-                       long *value_ptr;
+                       long *value_ptr;        /* obsoleted */
                } integer;
                union {
                        long long value[64];
-                       long long *value_ptr;
+                       long long *value_ptr;   /* obsoleted */
                } integer64;
                union {
                        unsigned int item[128];
-                       unsigned int *item_ptr;
+                       unsigned int *item_ptr; /* obsoleted */
                } enumerated;
                union {
                        unsigned char data[512];
-                       unsigned char *data_ptr;
+                       unsigned char *data_ptr;        /* obsoleted */
                } bytes;
                struct snd_aes_iec958 iec958;
         } value;                /* RO */
index 8fbcab7cc73b6f6b455259295069ab4f9d3dc536..c2a4b967d5be9af90002ef0b3458e8759ae8c49b 100644 (file)
@@ -104,6 +104,8 @@ struct snd_dm_fm_params {
 #define SNDRV_DM_FM_IOCTL_SET_MODE     _IOW('H', 0x25, int)
 /* for OPL3 only */
 #define SNDRV_DM_FM_IOCTL_SET_CONNECTION       _IOW('H', 0x26, int)
+/* SBI patch management */
+#define SNDRV_DM_FM_IOCTL_CLEAR_PATCHES        _IO ('H', 0x40)
 
 #define SNDRV_DM_FM_OSS_IOCTL_RESET            0x20
 #define SNDRV_DM_FM_OSS_IOCTL_PLAY_NOTE                0x21
@@ -112,4 +114,21 @@ struct snd_dm_fm_params {
 #define SNDRV_DM_FM_OSS_IOCTL_SET_MODE         0x24
 #define SNDRV_DM_FM_OSS_IOCTL_SET_OPL          0x25
 
+/*
+ * Patch Record - fixed size for write
+ */
+
+#define FM_KEY_SBI     "SBI\032"
+#define FM_KEY_2OP     "2OP\032"
+#define FM_KEY_4OP     "4OP\032"
+
+struct sbi_patch {
+       unsigned char prog;
+       unsigned char bank;
+       char key[4];
+       char name[25];
+       char extension[7];
+       unsigned char data[32];
+};
+
 #endif /* __SOUND_ASOUND_FM_H */
index 6954836487ed3407e217a3d005057ee77a8ad7c1..4fc0235ad78433a314f4b2f406a47d8e1c5827fc 100644 (file)
  *
  */
 
+#include <linux/module.h>
 #include <linux/sched.h>               /* wake_up() */
 #include <linux/mutex.h>               /* struct mutex */
 #include <linux/rwsem.h>               /* struct rw_semaphore */
 #include <linux/pm.h>                  /* pm_message_t */
 #include <linux/device.h>
 
+/* number of supported soundcards */
+#ifdef CONFIG_SND_DYNAMIC_MINORS
+#define SNDRV_CARDS 32
+#else
+#define SNDRV_CARDS 8          /* don't change - minor numbers */
+#endif
+
+#define CONFIG_SND_MAJOR       116     /* standard configuration */
+
 /* forward declarations */
 #ifdef CONFIG_PCI
 struct pci_dev;
index f1490265c9b833bc40ec112ff2098ebcfd183485..e8d1f3e31f9e5c1eebc42ab6be4cdb9a1d8b3ebf 100644 (file)
@@ -45,7 +45,7 @@
 #define CS4231_IFACE_CTRL      0x09    /* interface control - bits 7-2 MCE */
 #define CS4231_PIN_CTRL                0x0a    /* pin control */
 #define CS4231_TEST_INIT       0x0b    /* test and initialization */
-#define CS4231_MISC_INFO       0x0c    /* miscellaneaous information */
+#define CS4231_MISC_INFO       0x0c    /* miscellaneous information */
 #define CS4231_LOOPBACK                0x0d    /* loopback control */
 #define CS4231_PLY_UPR_CNT     0x0e    /* playback upper base count */
 #define CS4231_PLY_LWR_CNT     0x0f    /* playback lower base count */
index 6b40ee60f4c56d0d3c37db84a15b167eb3881771..e3005a674a2491e121b36e6ce5638bce1819e9b1 100644 (file)
@@ -1708,9 +1708,6 @@ struct snd_cs46xx {
 
        struct gameport *gameport;
 
-#ifdef CONFIG_SND_CS46XX_DEBUG_GPIO
-       int current_gpio;
-#endif
 #ifdef CONFIG_SND_CS46XX_NEW_DSP
        struct mutex spos_mutex;
 
index 5ccb6c5feecbe5e77d23643e484d50f4c8fc19ee..f0359437d01ac8f86a7981d38e596818c6539f5c 100644 (file)
@@ -1,51 +1 @@
-#ifndef __SOUND_DRIVER_H
-#define __SOUND_DRIVER_H
-
-/*
- *  Main header file for the ALSA driver
- *  Copyright (c) 1994-2000 by Jaroslav Kysela <perex@perex.cz>
- *
- *
- *   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
- *
- */
-
-#ifdef ALSA_BUILD
-#include "config.h"
-#endif
-
-
-/* number of supported soundcards */
-#ifdef CONFIG_SND_DYNAMIC_MINORS
-#define SNDRV_CARDS 32
-#else
-#define SNDRV_CARDS 8          /* don't change - minor numbers */
-#endif
-
-#ifndef CONFIG_SND_MAJOR       /* standard configuration */
-#define CONFIG_SND_MAJOR       116
-#endif
-
-#ifndef CONFIG_SND_DEBUG
-#undef CONFIG_SND_DEBUG_MEMORY
-#endif
-
-#ifdef ALSA_BUILD
-#include "adriver.h"
-#endif
-
-#include <linux/module.h>
-
-#endif /* __SOUND_DRIVER_H */
+#warning "This file is deprecated"
index 441aa06dcd6f622ceb7774ef9a54b090ebef2edc..7b7b9b13b4ddd8d04c1cf6392d15c95d58972de0 100644 (file)
 /************************************************************************************************/
 /* EMU1010m HANA Destinations                                                                  */
 /************************************************************************************************/
+/* Hana, original 1010,1212,1820 using Alice2
+ * Destiniations for SRATEX = 1X rates: 44.1 kHz or 48 kHz
+ * 0x00, 0x00-0x0f: 16 EMU32 channels to Alice2
+ * 0x01, 0x10-0x1f: 32 Elink channels to Audio Dock
+ * 0x01, 0x00: Dock DAC 1 Left
+ * 0x01, 0x04: Dock DAC 1 Right
+ * 0x01, 0x08: Dock DAC 2 Left
+ * 0x01, 0x0c: Dock DAC 2 Right
+ * 0x01, 0x10: Dock DAC 3 Left
+ * 0x01, 0x12: PHONES Left
+ * 0x01, 0x14: Dock DAC 3 Right
+ * 0x01, 0x16: PHONES Right
+ * 0x01, 0x18: Dock DAC 4 Left
+ * 0x01, 0x1a: S/PDIF Left
+ * 0x01, 0x1c: Dock DAC 4 Right
+ * 0x01, 0x1e: S/PDIF Right
+ * 0x02, 0x00: Hana S/PDIF Left
+ * 0x02, 0x01: Hana S/PDIF Right
+ * 0x03, 0x00: Hanoa DAC Left
+ * 0x03, 0x01: Hanoa DAC Right
+ * 0x04, 0x00-0x07: Hana ADAT
+ * 0x05, 0x00: I2S0 Left to Alice2
+ * 0x05, 0x01: I2S0 Right to Alice2
+ * 0x06, 0x00: I2S0 Left to Alice2
+ * 0x06, 0x01: I2S0 Right to Alice2
+ * 0x07, 0x00: I2S0 Left to Alice2
+ * 0x07, 0x01: I2S0 Right to Alice2
+ *
+ * Hana2 never released, but used Tina
+ * Not needed.
+ *
+ * Hana3, rev2 1010,1212,1616 using Tina
+ * Destinations for SRATEX = 1X rates: 44.1 kHz or 48 kHz
+ * 0x00, 0x00-0x0f: 16 EMU32A channels to Tina
+ * 0x01, 0x10-0x1f: 32 EDI channels to Micro Dock
+ * 0x01, 0x00: Dock DAC 1 Left
+ * 0x01, 0x04: Dock DAC 1 Right
+ * 0x01, 0x08: Dock DAC 2 Left
+ * 0x01, 0x0c: Dock DAC 2 Right
+ * 0x01, 0x10: Dock DAC 3 Left
+ * 0x01, 0x12: Dock S/PDIF Left
+ * 0x01, 0x14: Dock DAC 3 Right
+ * 0x01, 0x16: Dock S/PDIF Right
+ * 0x01, 0x18-0x1f: Dock ADAT 0-7
+ * 0x02, 0x00: Hana3 S/PDIF Left
+ * 0x02, 0x01: Hana3 S/PDIF Right
+ * 0x03, 0x00: Hanoa DAC Left
+ * 0x03, 0x01: Hanoa DAC Right
+ * 0x04, 0x00-0x07: Hana3 ADAT 0-7
+ * 0x05, 0x00-0x0f: 16 EMU32B channels to Tina
+ * 0x06-0x07: Not used
+ *
+ * HanaLite, rev1 0404 using Alice2
+ * Destiniations for SRATEX = 1X rates: 44.1 kHz or 48 kHz
+ * 0x00, 0x00-0x0f: 16 EMU32 channels to Alice2
+ * 0x01: Not used
+ * 0x02, 0x00: S/PDIF Left
+ * 0x02, 0x01: S/PDIF Right
+ * 0x03, 0x00: DAC Left
+ * 0x03, 0x01: DAC Right
+ * 0x04-0x07: Not used
+ *
+ * HanaLiteLite, rev2 0404 using Alice2
+ * Destiniations for SRATEX = 1X rates: 44.1 kHz or 48 kHz
+ * 0x00, 0x00-0x0f: 16 EMU32 channels to Alice2
+ * 0x01: Not used
+ * 0x02, 0x00: S/PDIF Left
+ * 0x02, 0x01: S/PDIF Right
+ * 0x03, 0x00: DAC Left
+ * 0x03, 0x01: DAC Right
+ * 0x04-0x07: Not used
+ *
+ * Mana, Cardbus 1616 using Tina2
+ * Destinations for SRATEX = 1X rates: 44.1 kHz or 48 kHz
+ * 0x00, 0x00-0x0f: 16 EMU32A channels to Tina2
+ * 0x01, 0x10-0x1f: 32 EDI channels to Micro Dock
+ * 0x01, 0x00: Dock DAC 1 Left
+ * 0x01, 0x04: Dock DAC 1 Right
+ * 0x01, 0x08: Dock DAC 2 Left
+ * 0x01, 0x0c: Dock DAC 2 Right
+ * 0x01, 0x10: Dock DAC 3 Left
+ * 0x01, 0x12: Dock S/PDIF Left
+ * 0x01, 0x14: Dock DAC 3 Right
+ * 0x01, 0x16: Dock S/PDIF Right
+ * 0x01, 0x18-0x1f: Dock ADAT 0-7
+ * 0x02: Not used
+ * 0x03, 0x00: Mana DAC Left
+ * 0x03, 0x01: Mana DAC Right
+ * 0x04, 0x00-0x0f: 16 EMU32B channels to Tina2
+ * 0x05-0x07: Not used
+ *
+ *
+ */
 /* 32-bit destinations of signal in the Hana FPGA. Destinations are either
  * physical outputs of Hana, or outputs going to Alice2 (audigy) for capture
  * - 16 x EMU_DST_ALICE2_EMU32_X.
 #define EMU_DST_ALICE_I2S2_LEFT                0x0700  /* Alice2 I2S2 Left */
 #define EMU_DST_ALICE_I2S2_RIGHT       0x0701  /* Alice2 I2S2 Right */
 
+/* Additional destinations for 1616(M)/Microdock */
+/* Microdock S/PDIF OUT Left, 1st or 48kHz only */
+#define EMU_DST_MDOCK_SPDIF_LEFT1      0x0112
+/* Microdock S/PDIF OUT Left, 2nd or 96kHz */
+#define EMU_DST_MDOCK_SPDIF_LEFT2      0x0113
+/* Microdock S/PDIF OUT Right, 1st or 48kHz only */
+#define EMU_DST_MDOCK_SPDIF_RIGHT1     0x0116
+/* Microdock S/PDIF OUT Right, 2nd or 96kHz  */
+#define EMU_DST_MDOCK_SPDIF_RIGHT2     0x0117
+/* Microdock S/PDIF ADAT 8 channel out +8 to +f */
+#define EMU_DST_MDOCK_ADAT             0x0118
+
+/* Headphone jack on 1010 cardbus? 44.1/48kHz only? */
+#define EMU_DST_MANA_DAC_LEFT          0x0300
+/* Headphone jack on 1010 cardbus? 44.1/48kHz only? */
+#define EMU_DST_MANA_DAC_RIGHT         0x0301
+
 /************************************************************************************************/
 /* EMU1010m HANA Sources                                                                       */
 /************************************************************************************************/
+/* Hana, original 1010,1212,1820 using Alice2
+ * Sources SRATEX = 1X rates: 44.1 kHz or 48 kHz
+ * 0x00,0x00-0x1f: Silence
+ * 0x01, 0x10-0x1f: 32 Elink channels from Audio Dock
+ * 0x01, 0x00: Dock Mic A
+ * 0x01, 0x04: Dock Mic B
+ * 0x01, 0x08: Dock ADC 1 Left
+ * 0x01, 0x0c: Dock ADC 1 Right
+ * 0x01, 0x10: Dock ADC 2 Left
+ * 0x01, 0x14: Dock ADC 2 Right
+ * 0x01, 0x18: Dock ADC 3 Left
+ * 0x01, 0x1c: Dock ADC 3 Right
+ * 0x02, 0x00: Hana ADC Left
+ * 0x02, 0x01: Hana ADC Right
+ * 0x03, 0x00-0x0f: 16 inputs from Alice2 Emu32A output
+ * 0x03, 0x10-0x1f: 16 inputs from Alice2 Emu32B output
+ * 0x04, 0x00-0x07: Hana ADAT
+ * 0x05, 0x00: Hana S/PDIF Left
+ * 0x05, 0x01: Hana S/PDIF Right
+ * 0x06-0x07: Not used
+ *
+ * Hana2 never released, but used Tina
+ * Not needed.
+ *
+ * Hana3, rev2 1010,1212,1616 using Tina
+ * Sources SRATEX = 1X rates: 44.1 kHz or 48 kHz
+ * 0x00,0x00-0x1f: Silence
+ * 0x01, 0x10-0x1f: 32 Elink channels from Audio Dock
+ * 0x01, 0x00: Dock Mic A
+ * 0x01, 0x04: Dock Mic B
+ * 0x01, 0x08: Dock ADC 1 Left
+ * 0x01, 0x0c: Dock ADC 1 Right
+ * 0x01, 0x10: Dock ADC 2 Left
+ * 0x01, 0x12: Dock S/PDIF Left
+ * 0x01, 0x14: Dock ADC 2 Right
+ * 0x01, 0x16: Dock S/PDIF Right
+ * 0x01, 0x18-0x1f: Dock ADAT 0-7
+ * 0x01, 0x18: Dock ADC 3 Left
+ * 0x01, 0x1c: Dock ADC 3 Right
+ * 0x02, 0x00: Hanoa ADC Left
+ * 0x02, 0x01: Hanoa ADC Right
+ * 0x03, 0x00-0x0f: 16 inputs from Tina Emu32A output
+ * 0x03, 0x10-0x1f: 16 inputs from Tina Emu32B output
+ * 0x04, 0x00-0x07: Hana3 ADAT
+ * 0x05, 0x00: Hana3 S/PDIF Left
+ * 0x05, 0x01: Hana3 S/PDIF Right
+ * 0x06-0x07: Not used
+ *
+ * HanaLite, rev1 0404 using Alice2
+ * Sources SRATEX = 1X rates: 44.1 kHz or 48 kHz
+ * 0x00,0x00-0x1f: Silence
+ * 0x01: Not used
+ * 0x02, 0x00: ADC Left
+ * 0x02, 0x01: ADC Right
+ * 0x03, 0x00-0x0f: 16 inputs from Alice2 Emu32A output
+ * 0x03, 0x10-0x1f: 16 inputs from Alice2 Emu32B output
+ * 0x04: Not used
+ * 0x05, 0x00: S/PDIF Left
+ * 0x05, 0x01: S/PDIF Right
+ * 0x06-0x07: Not used
+ *
+ * HanaLiteLite, rev2 0404 using Alice2
+ * Sources SRATEX = 1X rates: 44.1 kHz or 48 kHz
+ * 0x00,0x00-0x1f: Silence
+ * 0x01: Not used
+ * 0x02, 0x00: ADC Left
+ * 0x02, 0x01: ADC Right
+ * 0x03, 0x00-0x0f: 16 inputs from Alice2 Emu32A output
+ * 0x03, 0x10-0x1f: 16 inputs from Alice2 Emu32B output
+ * 0x04: Not used
+ * 0x05, 0x00: S/PDIF Left
+ * 0x05, 0x01: S/PDIF Right
+ * 0x06-0x07: Not used
+ *
+ * Mana, Cardbus 1616 using Tina2
+ * Sources SRATEX = 1X rates: 44.1 kHz or 48 kHz
+ * 0x00,0x00-0x1f: Silence
+ * 0x01, 0x10-0x1f: 32 Elink channels from Audio Dock
+ * 0x01, 0x00: Dock Mic A
+ * 0x01, 0x04: Dock Mic B
+ * 0x01, 0x08: Dock ADC 1 Left
+ * 0x01, 0x0c: Dock ADC 1 Right
+ * 0x01, 0x10: Dock ADC 2 Left
+ * 0x01, 0x12: Dock S/PDIF Left
+ * 0x01, 0x14: Dock ADC 2 Right
+ * 0x01, 0x16: Dock S/PDIF Right
+ * 0x01, 0x18-0x1f: Dock ADAT 0-7
+ * 0x01, 0x18: Dock ADC 3 Left
+ * 0x01, 0x1c: Dock ADC 3 Right
+ * 0x02: Not used
+ * 0x03, 0x00-0x0f: 16 inputs from Tina Emu32A output
+ * 0x03, 0x10-0x1f: 16 inputs from Tina Emu32B output
+ * 0x04-0x07: Not used
+ *
+ */
+
 /* 32-bit sources of signal in the Hana FPGA. The sources are routed to
  * destinations using mixer control for each destination - see emumixer.c
  * Sources are either physical inputs of FPGA,
 #define EMU_SRC_HANA_SPDIF_LEFT2       0x0502  /* Hana SPDIF Left, 2nd or 96kHz */
 #define EMU_SRC_HANA_SPDIF_RIGHT1      0x0501  /* Hana SPDIF Right, 1st or 48kHz only */
 #define EMU_SRC_HANA_SPDIF_RIGHT2      0x0503  /* Hana SPDIF Right, 2nd or 96kHz */
+
+/* Additional inputs for 1616(M)/Microdock */
+/* Microdock S/PDIF Left, 1st or 48kHz only */
+#define EMU_SRC_MDOCK_SPDIF_LEFT1      0x0112
+/* Microdock S/PDIF Left, 2nd or 96kHz */
+#define EMU_SRC_MDOCK_SPDIF_LEFT2      0x0113
+/* Microdock S/PDIF Right, 1st or 48kHz only */
+#define EMU_SRC_MDOCK_SPDIF_RIGHT1     0x0116
+/* Microdock S/PDIF Right, 2nd or 96kHz */
+#define EMU_SRC_MDOCK_SPDIF_RIGHT2     0x0117
+/* Microdock ADAT 8 channel in +8 to +f */
+#define EMU_SRC_MDOCK_ADAT             0x0118
+
 /* 0x600 and 0x700 no used */
 
 /* ------------------- STRUCTURES -------------------- */
@@ -1423,6 +1642,14 @@ struct snd_emu10k1_midi {
        void (*interrupt)(struct snd_emu10k1 *emu, unsigned int status);
 };
 
+enum {
+       EMU_MODEL_SB,
+       EMU_MODEL_EMU1010,
+       EMU_MODEL_EMU1010B,
+       EMU_MODEL_EMU1616,
+       EMU_MODEL_EMU0404,
+};
+
 struct snd_emu_chip_details {
        u32 vendor;
        u32 device;
@@ -1439,7 +1666,7 @@ struct snd_emu_chip_details {
        unsigned char spdif_bug;    /* Has Spdif phasing bug */
        unsigned char ac97_chip;    /* Has an AC97 chip: 1 = mandatory, 2 = optional */
        unsigned char ecard;        /* APS EEPROM */
-       unsigned char emu1010;     /* EMU 1010m card */
+       unsigned char emu_model;     /* EMU model type */
        unsigned char spi_dac;      /* SPI interface for DAC */
        unsigned char i2c_adc;      /* I2C interface for ADC */
        unsigned char adc_1361t;    /* Use Philips 1361T ADC */
@@ -1515,6 +1742,8 @@ struct snd_emu10k1 {
        spinlock_t reg_lock;
        spinlock_t emu_lock;
        spinlock_t voice_lock;
+       spinlock_t spi_lock; /* serialises access to spi port */
+       spinlock_t i2c_lock; /* serialises access to i2c port */
 
        struct snd_emu10k1_voice voices[NUM_G];
        struct snd_emu10k1_voice p16v_voices[4];
index e5433d8b78bc5b692e40a08aa71bb81771739477..841bb8df38c1af1caacacba848aa9fb4b677be72 100644 (file)
 #include "timer.h"
 #include "seq_midi_emul.h"
 #include "seq_device.h"
-#include "ainstr_iw.h"
-#include "ainstr_gf1.h"
-#include "ainstr_simple.h"
 #include <asm/io.h>
 
-#define SNDRV_SEQ_DEV_ID_GUS                   "gus-synth"
-
 /* IO ports */
 
 #define GUSP(gus, x)                   ((gus)->gf1.port + SNDRV_g_u_s_##x)
@@ -234,16 +229,6 @@ struct snd_gus_port {
 
 struct snd_gus_voice;
 
-struct snd_gus_sample_ops {
-       void (*sample_start)(struct snd_gus_card *gus, struct snd_gus_voice *voice, snd_seq_position_t position);
-       void (*sample_stop)(struct snd_gus_card *gus, struct snd_gus_voice *voice, int mode);
-       void (*sample_freq)(struct snd_gus_card *gus, struct snd_gus_voice *voice, snd_seq_frequency_t freq);
-       void (*sample_volume)(struct snd_gus_card *gus, struct snd_gus_voice *voice, struct snd_seq_ev_volume *volume);
-       void (*sample_loop)(struct snd_gus_card *card, struct snd_gus_voice *voice, struct snd_seq_ev_loop *loop);
-       void (*sample_pos)(struct snd_gus_card *card, struct snd_gus_voice *voice, snd_seq_position_t position);
-       void (*sample_private1)(struct snd_gus_card *card, struct snd_gus_voice *voice, unsigned char *data);
-};
-
 #define SNDRV_GF1_VOICE_TYPE_PCM       0
 #define SNDRV_GF1_VOICE_TYPE_SYNTH     1
 #define SNDRV_GF1_VOICE_TYPE_MIDI      2
@@ -284,12 +269,8 @@ struct snd_gus_voice {
 
        struct snd_gus_sample_ops *sample_ops;
 
-       struct snd_seq_instr instr;
-
        /* running status / registers */
 
-       struct snd_seq_ev_volume sample_volume;
-
        unsigned short fc_register;
        unsigned short fc_lfo;
        unsigned short gf1_volume;
@@ -382,10 +363,6 @@ struct snd_gf1 {
 
        int seq_client;
        struct snd_gus_port seq_ports[4];
-       struct snd_seq_kinstr_list *ilist;
-       struct snd_iwffff_ops iwffff_ops;
-       struct snd_gf1_ops gf1_ops;
-       struct snd_simple_ops simple_ops;
 
        /* timer */
 
@@ -458,8 +435,6 @@ struct snd_gus_card {
        struct snd_rawmidi_substream *midi_substream_output;
        struct snd_rawmidi_substream *midi_substream_input;
 
-       struct snd_seq_device *seq_dev;
-
        spinlock_t reg_lock;
        spinlock_t voice_alloc;
        spinlock_t active_voice_lock;
@@ -647,48 +622,10 @@ void snd_gus_irq_profile_init(struct snd_gus_card *gus);
 
 int snd_gf1_rawmidi_new(struct snd_gus_card * gus, int device, struct snd_rawmidi **rrawmidi);
 
-#if 0
-extern void snd_engine_instrument_register(unsigned short mode,
-               struct _SND_INSTRUMENT_VOICE_COMMANDS *voice_cmds,
-               struct _SND_INSTRUMENT_NOTE_COMMANDS *note_cmds,
-               struct _SND_INSTRUMENT_CHANNEL_COMMANDS *channel_cmds);
-extern int snd_engine_instrument_register_ask(unsigned short mode);
-#endif
-
 /* gus_dram.c */
 int snd_gus_dram_write(struct snd_gus_card *gus, char __user *ptr,
                       unsigned int addr, unsigned int size);
 int snd_gus_dram_read(struct snd_gus_card *gus, char __user *ptr,
                      unsigned int addr, unsigned int size, int rom);
 
-#if defined(CONFIG_SND_SEQUENCER) || defined(CONFIG_SND_SEQUENCER_MODULE)
-
-/* gus_sample.c */
-void snd_gus_sample_event(struct snd_seq_event *ev, struct snd_gus_port *p);
-
-/* gus_simple.c */
-void snd_gf1_simple_init(struct snd_gus_voice *voice);
-
-/* gus_instr.c */
-int snd_gus_iwffff_put_sample(void *private_data, struct iwffff_wave *wave,
-                             char __user *data, long len, int atomic);
-int snd_gus_iwffff_get_sample(void *private_data, struct iwffff_wave *wave,
-                             char __user *data, long len, int atomic);
-int snd_gus_iwffff_remove_sample(void *private_data, struct iwffff_wave *wave,
-                                int atomic);
-int snd_gus_gf1_put_sample(void *private_data, struct gf1_wave *wave,
-                          char __user *data, long len, int atomic);
-int snd_gus_gf1_get_sample(void *private_data, struct gf1_wave *wave,
-                          char __user *data, long len, int atomic);
-int snd_gus_gf1_remove_sample(void *private_data, struct gf1_wave *wave,
-                             int atomic);
-int snd_gus_simple_put_sample(void *private_data, struct simple_instrument *instr,
-                             char __user *data, long len, int atomic);
-int snd_gus_simple_get_sample(void *private_data, struct simple_instrument *instr,
-                             char __user *data, long len, int atomic);
-int snd_gus_simple_remove_sample(void *private_data, struct simple_instrument *instr,
-                                int atomic);
-
-#endif /* CONFIG_SND_SEQUENCER */
-
 #endif /* __SOUND_GUS_H */
index fecbb1ffd5405ed628cd8f3aafce40318cf7b4fe..8ae72e74f898714979e6d1f6d79ae32dd683343a 100644 (file)
@@ -100,8 +100,10 @@ int snd_info_minor_unregister(void);
 extern struct snd_info_entry *snd_seq_root;
 #ifdef CONFIG_SND_OSSEMUL
 extern struct snd_info_entry *snd_oss_root;
+void snd_card_info_read_oss(struct snd_info_buffer *buffer);
 #else
 #define snd_oss_root NULL
+static inline void snd_card_info_read_oss(struct snd_info_buffer *buffer) {}
 #endif
 
 int snd_iprintf(struct snd_info_buffer * buffer, char *fmt,...) __attribute__ ((format (printf, 2, 3)));
index 1d14b3f8239353aa14168dc27721a8a621fe99c6..a0c5febdc4ea6b2d93019e581bcda575da7a6c7d 100644 (file)
  *
  */
 
-#include "driver.h"
-#include <linux/time.h>
-#include <linux/mutex.h>
-#include "core.h"
-#include "hwdep.h"
-#include "timer.h"
-#include "seq_midi_emul.h"
+#include <sound/core.h>
+#include <sound/hwdep.h>
+#include <sound/timer.h>
+#include <sound/seq_midi_emul.h>
 #ifdef CONFIG_SND_SEQUENCER_OSS
-#include "seq_oss.h"
-#include "seq_oss_legacy.h"
+#include <sound/seq_oss.h>
+#include <sound/seq_oss_legacy.h>
 #endif
-#include "seq_device.h"
-#include "ainstr_fm.h"
+#include <sound/seq_device.h>
+#include <sound/asound_fm.h>
 
 /*
  *    Register numbers for the global registers
 
 struct snd_opl3;
 
+/*
+ * Instrument record, aka "Patch"
+ */
+
+/* FM operator */
+struct fm_operator {
+       unsigned char am_vib;
+       unsigned char ksl_level;
+       unsigned char attack_decay;
+       unsigned char sustain_release;
+       unsigned char wave_select;
+} __attribute__((packed));
+
+/* Instrument data */
+struct fm_instrument {
+       struct fm_operator op[4];
+       unsigned char feedback_connection[2];
+       unsigned char echo_delay;
+       unsigned char echo_atten;
+       unsigned char chorus_spread;
+       unsigned char trnsps;
+       unsigned char fix_dur;
+       unsigned char modes;
+       unsigned char fix_key;
+};
+
+/* type */
+#define FM_PATCH_OPL2  0x01            /* OPL2 2 operators FM instrument */
+#define FM_PATCH_OPL3  0x02            /* OPL3 4 operators FM instrument */
+
+/* Instrument record */
+struct fm_patch {
+       unsigned char prog;
+       unsigned char bank;
+       unsigned char type;
+       struct fm_instrument inst;
+       char name[24];
+       struct fm_patch *next;
+};
+
+
 /*
  * A structure to keep track of each hardware voice
  */
@@ -277,9 +315,9 @@ struct snd_opl3 {
        void *private_data;
        void (*private_free)(struct snd_opl3 *);
 
+       struct snd_hwdep *hwdep;
        spinlock_t reg_lock;
        struct snd_card *card;          /* The card that this belongs to */
-       int used;                       /* usage flag - exclusive */
        unsigned char fm_mode;          /* OPL mode, see SNDRV_DM_FM_MODE_XXX */
        unsigned char rhythm;           /* percussion mode flag */
        unsigned char max_voices;       /* max number of voices */
@@ -297,8 +335,8 @@ struct snd_opl3 {
        struct snd_midi_channel_set * oss_chset;
 #endif
  
-       struct snd_seq_kinstr_ops fm_ops;
-       struct snd_seq_kinstr_list *ilist;
+#define OPL3_PATCH_HASH_SIZE   32
+       struct fm_patch *patch_table[OPL3_PATCH_HASH_SIZE];
 
        struct snd_opl3_voice voices[MAX_OPL3_VOICES]; /* Voices (OPL3 'channel') */
        int use_time;                   /* allocation counter */
@@ -312,7 +350,6 @@ struct snd_opl3 {
        int sys_timer_status;           /* system timer run status */
        spinlock_t sys_timer_lock;      /* Lock for system timer access */
 #endif
-       struct mutex access_mutex;      /* locking */
 };
 
 /* opl3.c */
@@ -333,8 +370,19 @@ int snd_opl3_hwdep_new(struct snd_opl3 * opl3, int device, int seq_device,
 int snd_opl3_open(struct snd_hwdep * hw, struct file *file);
 int snd_opl3_ioctl(struct snd_hwdep * hw, struct file *file,
                   unsigned int cmd, unsigned long arg);
+long snd_opl3_write(struct snd_hwdep *hw, const char __user *buf, long count,
+                   loff_t *offset);
 int snd_opl3_release(struct snd_hwdep * hw, struct file *file);
 
 void snd_opl3_reset(struct snd_opl3 * opl3);
 
+int snd_opl3_load_patch(struct snd_opl3 *opl3,
+                       int prog, int bank, int type,
+                       const char *name,
+                       const unsigned char *ext,
+                       const unsigned char *data);
+struct fm_patch *snd_opl3_find_patch(struct snd_opl3 *opl3, int prog, int bank,
+                                    int create_patch);
+void snd_opl3_clear_patches(struct snd_opl3 *opl3);
+
 #endif /* __SOUND_OPL3_H */
index 5e9cc460075efa5ee9726d0cb282218f67355100..51d58ccda2d82d7158cbca22fb338bafa393c0e0 100644 (file)
@@ -274,7 +274,6 @@ struct snd_pcm_runtime {
        snd_pcm_uframes_t period_size;  /* period size */
        unsigned int periods;           /* periods */
        snd_pcm_uframes_t buffer_size;  /* buffer size */
-       unsigned int tick_time;         /* tick time */
        snd_pcm_uframes_t min_align;    /* Min alignment for the format */
        size_t byte_align;
        unsigned int frame_bits;
@@ -286,8 +285,6 @@ struct snd_pcm_runtime {
        /* -- SW params -- */
        int tstamp_mode;                /* mmap timestamp is updated */
        unsigned int period_step;
-       unsigned int sleep_min;         /* min ticks to sleep */
-       snd_pcm_uframes_t xfer_align;   /* xfer size need to be a multiple */
        snd_pcm_uframes_t start_threshold;
        snd_pcm_uframes_t stop_threshold;
        snd_pcm_uframes_t silence_threshold; /* Silence filling happens when
@@ -306,7 +303,6 @@ struct snd_pcm_runtime {
 
        /* -- locking / scheduling -- */
        wait_queue_head_t sleep;
-       struct timer_list tick_timer;
        struct fasync_struct *fasync;
 
        /* -- private section -- */
@@ -323,6 +319,7 @@ struct snd_pcm_runtime {
 
        /* -- timer -- */
        unsigned int timer_resolution;  /* timer resolution */
+       int tstamp_type;                /* timestamp type */
 
        /* -- DMA -- */           
        unsigned char *dma_area;        /* DMA area */
@@ -810,7 +807,6 @@ static inline const struct snd_interval *hw_param_interval_c(const struct snd_pc
 #define params_periods(p) hw_param_interval((p), SNDRV_PCM_HW_PARAM_PERIODS)->min
 #define params_buffer_size(p) hw_param_interval((p), SNDRV_PCM_HW_PARAM_BUFFER_SIZE)->min
 #define params_buffer_bytes(p) hw_param_interval((p), SNDRV_PCM_HW_PARAM_BUFFER_BYTES)->min
-#define params_tick_time(p) hw_param_interval((p), SNDRV_PCM_HW_PARAM_TICK_TIME)->min
 
 
 int snd_interval_refine(struct snd_interval *i, const struct snd_interval *v);
@@ -908,9 +904,6 @@ int snd_pcm_capture_xrun_check(struct snd_pcm_substream *substream);
 int snd_pcm_playback_xrun_asap(struct snd_pcm_substream *substream);
 int snd_pcm_capture_xrun_asap(struct snd_pcm_substream *substream);
 void snd_pcm_playback_silence(struct snd_pcm_substream *substream, snd_pcm_uframes_t new_hw_ptr);
-void snd_pcm_tick_prepare(struct snd_pcm_substream *substream);
-void snd_pcm_tick_set(struct snd_pcm_substream *substream, unsigned long ticks);
-void snd_pcm_tick_elapsed(struct snd_pcm_substream *substream);
 void snd_pcm_period_elapsed(struct snd_pcm_substream *substream);
 snd_pcm_sframes_t snd_pcm_lib_write(struct snd_pcm_substream *substream,
                                    const void __user *buf,
@@ -952,6 +945,15 @@ void snd_pcm_timer_resolution_change(struct snd_pcm_substream *substream);
 void snd_pcm_timer_init(struct snd_pcm_substream *substream);
 void snd_pcm_timer_done(struct snd_pcm_substream *substream);
 
+static inline void snd_pcm_gettime(struct snd_pcm_runtime *runtime,
+                                  struct timespec *tv)
+{
+       if (runtime->tstamp_type == SNDRV_PCM_TSTAMP_TYPE_MONOTONIC)
+               do_posix_clock_monotonic_gettime(tv);
+       else
+               getnstimeofday(tv);
+}
+
 /*
  *  Memory
  */
diff --git a/include/sound/seq_instr.h b/include/sound/seq_instr.h
deleted file mode 100644 (file)
index 93b0c51..0000000
+++ /dev/null
@@ -1,110 +0,0 @@
-#ifndef __SOUND_SEQ_INSTR_H
-#define __SOUND_SEQ_INSTR_H
-
-/*
- *  Main kernel header file for the ALSA sequencer
- *  Copyright (c) 1999 by Jaroslav Kysela <perex@perex.cz>
- *
- *
- *   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 "seq_kernel.h"
-
-/* Instrument cluster */
-struct snd_seq_kcluster {
-       snd_seq_instr_cluster_t cluster;
-       char name[32];
-       int priority;
-       struct snd_seq_kcluster *next;
-};
-
-/* return pointer to private data */
-#define KINSTR_DATA(kinstr)    (void *)(((char *)kinstr) + sizeof(struct snd_seq_kinstr))
-
-/* Instrument structure */
-struct snd_seq_kinstr {
-       struct snd_seq_instr instr;
-       char name[32];
-       int type;                       /* instrument type */
-       int use;                        /* use count */
-       int busy;                       /* not useable */
-       int add_len;                    /* additional length */
-       struct snd_seq_kinstr_ops *ops; /* operations */
-       struct snd_seq_kinstr *next;
-};
-
-#define SNDRV_SEQ_INSTR_HASH_SIZE              32
-
-/* Instrument flags */
-#define SNDRV_SEQ_INSTR_FLG_DIRECT     (1<<0)  /* accept only direct events */
-
-/* List of all instruments */
-struct snd_seq_kinstr_list {
-       struct snd_seq_kinstr *hash[SNDRV_SEQ_INSTR_HASH_SIZE];
-       int count;                      /* count of all instruments */
-       
-       struct snd_seq_kcluster *chash[SNDRV_SEQ_INSTR_HASH_SIZE];
-       int ccount;                     /* count of all clusters */
-
-       int owner;                      /* current owner of the instrument list */
-       unsigned int flags;
-
-       spinlock_t lock;
-       spinlock_t ops_lock;
-       struct mutex ops_mutex;
-       unsigned long ops_flags;
-};
-
-#define SNDRV_SEQ_INSTR_NOTIFY_REMOVE  0
-#define SNDRV_SEQ_INSTR_NOTIFY_CHANGE  1
-
-struct snd_seq_kinstr_ops {
-       void *private_data;
-       long add_len;                   /* additional length */
-       char *instr_type;
-       int (*info)(void *private_data, char *info_data, long len);
-       int (*put)(void *private_data, struct snd_seq_kinstr *kinstr,
-                  char __user *instr_data, long len, int atomic, int cmd);
-       int (*get)(void *private_data, struct snd_seq_kinstr *kinstr,
-                  char __user *instr_data, long len, int atomic, int cmd);
-       int (*get_size)(void *private_data, struct snd_seq_kinstr *kinstr, long *size);
-       int (*remove)(void *private_data, struct snd_seq_kinstr *kinstr, int atomic);
-       void (*notify)(void *private_data, struct snd_seq_kinstr *kinstr, int what);
-       struct snd_seq_kinstr_ops *next;
-};
-
-
-/* instrument operations */
-struct snd_seq_kinstr_list *snd_seq_instr_list_new(void);
-void snd_seq_instr_list_free(struct snd_seq_kinstr_list **list);
-int snd_seq_instr_list_free_cond(struct snd_seq_kinstr_list *list,
-                                struct snd_seq_instr_header *ifree,
-                                int client,
-                                int atomic);
-struct snd_seq_kinstr *snd_seq_instr_find(struct snd_seq_kinstr_list *list,
-                                         struct snd_seq_instr *instr,
-                                         int exact,
-                                         int follow_alias);
-void snd_seq_instr_free_use(struct snd_seq_kinstr_list *list,
-                           struct snd_seq_kinstr *instr);
-int snd_seq_instr_event(struct snd_seq_kinstr_ops *ops,
-                       struct snd_seq_kinstr_list *list,
-                       struct snd_seq_event *ev,
-                       int client,
-                       int atomic,
-                       int hop);
-
-#endif /* __SOUND_SEQ_INSTR_H */
index 2b1ae8edc43cfc5215831679dbb88aafd5ccfc6c..a105b01e06d50dc3ccb8b8bcb527d64ea9b7cbe8 100644 (file)
@@ -22,7 +22,7 @@
 #define SND_SOC_NOPM   -1
 
 /*
- * SoC dynamic audio power managment
+ * SoC dynamic audio power management
  *
  * We can have upto 4 power domains
  *     1. Codec domain - VREF, VMID
        .shift = wshift, .invert = winvert}
 
 /* dapm kcontrol types */
-#define SOC_DAPM_SINGLE(xname, reg, shift, mask, invert) \
+#define SOC_DAPM_SINGLE(xname, reg, shift, max, invert) \
 {      .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, \
        .info = snd_soc_info_volsw, \
        .get = snd_soc_dapm_get_volsw, .put = snd_soc_dapm_put_volsw, \
-       .private_value =  SOC_SINGLE_VALUE(reg, shift, mask, invert) }
-#define SOC_DAPM_DOUBLE(xname, reg, shift_left, shift_right, mask, invert, \
+       .private_value =  SOC_SINGLE_VALUE(reg, shift, max, invert) }
+#define SOC_DAPM_DOUBLE(xname, reg, shift_left, shift_right, max, invert, \
        power) \
 {      .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = (xname), \
        .info = snd_soc_info_volsw, \
        .get = snd_soc_dapm_get_volsw, .put = snd_soc_dapm_put_volsw, \
        .private_value = (reg) | ((shift_left) << 8) | ((shift_right) << 12) |\
-                ((mask) << 16) | ((invert) << 24) }
+               ((max) << 16) | ((invert) << 24) }
+#define SOC_DAPM_SINGLE_TLV(xname, reg, shift, max, invert, tlv_array) \
+{      .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, \
+       .info = snd_soc_info_volsw, \
+       .access = SNDRV_CTL_ELEM_ACCESS_TLV_READ | SNDRV_CTL_ELEM_ACCESS_READWRITE,\
+       .tlv.p = (tlv_array), \
+       .get = snd_soc_dapm_get_volsw, .put = snd_soc_dapm_put_volsw, \
+       .private_value =  SOC_SINGLE_VALUE(reg, shift, max, invert) }
+#define SOC_DAPM_DOUBLE_TLV(xname, reg, shift_left, shift_right, max, invert, \
+       power, tlv_array) \
+{      .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = (xname), \
+       .access = SNDRV_CTL_ELEM_ACCESS_TLV_READ | SNDRV_CTL_ELEM_ACCESS_READWRITE,\
+       .tlv.p = (tlv_array), \
+       .info = snd_soc_info_volsw, \
+       .get = snd_soc_dapm_get_volsw, .put = snd_soc_dapm_put_volsw, \
+       .private_value = (reg) | ((shift_left) << 8) | ((shift_right) << 12) |\
+               ((max) << 16) | ((invert) << 24) }
 #define SOC_DAPM_ENUM(xname, xenum) \
 {      .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, \
        .info = snd_soc_info_enum_double, \
@@ -199,6 +215,7 @@ void snd_soc_dapm_free(struct snd_soc_device *socdev);
 /* dapm events */
 int snd_soc_dapm_stream_event(struct snd_soc_codec *codec, char *stream,
        int event);
+int snd_soc_dapm_device_event(struct snd_soc_device *socdev, int event);
 
 /* dapm sys fs - used by the core */
 int snd_soc_dapm_sys_add(struct device *dev);
@@ -272,7 +289,7 @@ struct snd_soc_dapm_widget {
 
        /* external events */
        unsigned short event_flags;             /* flags to specify event types */
-       int (*event)(struct snd_soc_dapm_widget*, int);
+       int (*event)(struct snd_soc_dapm_widget*, struct snd_kcontrol *, int);
 
        /* kcontrols that relate to this widget */
        int num_kcontrols;
index f47ef1f75f18107a92e86e69f864ea1e6b0aec47..e6ea6f7509414566c2327a841e206b7249cbaa2c 100644 (file)
 #include <linux/platform_device.h>
 #include <linux/types.h>
 #include <linux/workqueue.h>
-#include <sound/driver.h>
 #include <sound/core.h>
 #include <sound/pcm.h>
 #include <sound/control.h>
 #include <sound/ac97_codec.h>
 
-#define SND_SOC_VERSION "0.13.1"
+#define SND_SOC_VERSION "0.13.2"
 
 /*
  * Convenience kcontrol builders
  */
-#define SOC_SINGLE_VALUE(reg,shift,mask,invert) ((reg) | ((shift) << 8) |\
-       ((shift) << 12) | ((mask) << 16) | ((invert) << 24))
-#define SOC_SINGLE_VALUE_EXT(reg,mask,invert) ((reg) | ((mask) << 16) |\
+#define SOC_SINGLE_VALUE(reg, shift, max, invert) ((reg) | ((shift) << 8) |\
+       ((shift) << 12) | ((max) << 16) | ((invert) << 24))
+#define SOC_SINGLE_VALUE_EXT(reg, max, invert) ((reg) | ((max) << 16) |\
        ((invert) << 31))
-#define SOC_SINGLE(xname, reg, shift, mask, invert) \
+#define SOC_SINGLE(xname, reg, shift, max, invert) \
 {      .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, \
        .info = snd_soc_info_volsw, .get = snd_soc_get_volsw,\
        .put = snd_soc_put_volsw, \
-       .private_value =  SOC_SINGLE_VALUE(reg, shift, mask, invert) }
-#define SOC_DOUBLE(xname, reg, shift_left, shift_right, mask, invert) \
+       .private_value =  SOC_SINGLE_VALUE(reg, shift, max, invert) }
+#define SOC_SINGLE_TLV(xname, reg, shift, max, invert, tlv_array) \
+{      .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, \
+       .access = SNDRV_CTL_ELEM_ACCESS_TLV_READ |\
+                SNDRV_CTL_ELEM_ACCESS_READWRITE,\
+       .tlv.p = (tlv_array), \
+       .info = snd_soc_info_volsw, .get = snd_soc_get_volsw,\
+       .put = snd_soc_put_volsw, \
+       .private_value =  SOC_SINGLE_VALUE(reg, shift, max, invert) }
+#define SOC_DOUBLE(xname, reg, shift_left, shift_right, max, invert) \
 {      .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = (xname),\
        .info = snd_soc_info_volsw, .get = snd_soc_get_volsw, \
        .put = snd_soc_put_volsw, \
        .private_value = (reg) | ((shift_left) << 8) | \
-               ((shift_right) << 12) | ((mask) << 16) | ((invert) << 24) }
-#define SOC_DOUBLE_R(xname, reg_left, reg_right, shift, mask, invert) \
+               ((shift_right) << 12) | ((max) << 16) | ((invert) << 24) }
+#define SOC_DOUBLE_R(xname, reg_left, reg_right, shift, max, invert) \
 {      .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = (xname), \
        .info = snd_soc_info_volsw_2r, \
        .get = snd_soc_get_volsw_2r, .put = snd_soc_put_volsw_2r, \
        .private_value = (reg_left) | ((shift) << 8)  | \
-               ((mask) << 12) | ((invert) << 20) | ((reg_right) << 24) }
+               ((max) << 12) | ((invert) << 20) | ((reg_right) << 24) }
+#define SOC_DOUBLE_TLV(xname, reg, shift_left, shift_right, max, invert, tlv_array) \
+{      .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = (xname),\
+       .access = SNDRV_CTL_ELEM_ACCESS_TLV_READ |\
+                SNDRV_CTL_ELEM_ACCESS_READWRITE,\
+       .tlv.p = (tlv_array), \
+       .info = snd_soc_info_volsw, .get = snd_soc_get_volsw, \
+       .put = snd_soc_put_volsw, \
+       .private_value = (reg) | ((shift_left) << 8) | \
+               ((shift_right) << 12) | ((max) << 16) | ((invert) << 24) }
+#define SOC_DOUBLE_R_TLV(xname, reg_left, reg_right, shift, max, invert, tlv_array) \
+{      .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = (xname),\
+       .access = SNDRV_CTL_ELEM_ACCESS_TLV_READ |\
+                SNDRV_CTL_ELEM_ACCESS_READWRITE,\
+       .tlv.p = (tlv_array), \
+       .info = snd_soc_info_volsw_2r, \
+       .get = snd_soc_get_volsw_2r, .put = snd_soc_put_volsw_2r, \
+       .private_value = (reg_left) | ((shift) << 8)  | \
+               ((max) << 12) | ((invert) << 20) | ((reg_right) << 24) }
 #define SOC_ENUM_DOUBLE(xreg, xshift_l, xshift_r, xmask, xtexts) \
 {      .reg = xreg, .shift_l = xshift_l, .shift_r = xshift_r, \
        .mask = xmask, .texts = xtexts }
 #define SND_SOC_DAIFMT_CONT                    (0 << 4)        /* continuous clock */
 #define SND_SOC_DAIFMT_GATED           (1 << 4)        /* clock is gated when not Tx/Rx */
 
+/*
+ * DAI Sync
+ * Synchronous LR (Left Right) clocks and Frame signals.
+ */
+#define SND_SOC_DAIFMT_SYNC            (0 << 5)        /* Tx FRM = Rx FRM */
+#define SND_SOC_DAIFMT_ASYNC           (1 << 5)        /* Tx FRM ~ Rx FRM */
+
+/*
+ * TDM
+ */
+#define SND_SOC_DAIFMT_TDM             (1 << 6)
+
 /*
  * DAI hardware signal inversions
  */
-#define SND_SOC_DAIFMT_NB_NF           (0 << 8)        /* normal bit clock + frame */
+#define SND_SOC_DAIFMT_NB_NF           (0 << 8)        /* normal bclk + frm */
 #define SND_SOC_DAIFMT_NB_IF           (1 << 8)        /* normal bclk + inv frm */
 #define SND_SOC_DAIFMT_IB_NF           (2 << 8)        /* invert bclk + nor frm */
 #define SND_SOC_DAIFMT_IB_IF           (3 << 8)        /* invert bclk + frm */
@@ -410,6 +447,9 @@ struct snd_soc_dai_link  {
 
        /* codec/machine specific init - e.g. add machine controls */
        int (*init)(struct snd_soc_codec *codec);
+
+       /* DAI pcm */
+       struct snd_pcm *pcm;
 };
 
 /* SoC machine */
@@ -426,6 +466,9 @@ struct snd_soc_machine {
        int (*resume_pre)(struct platform_device *pdev);
        int (*resume_post)(struct platform_device *pdev);
 
+       /* callbacks */
+       int (*dapm_event)(struct snd_soc_machine *, int event);
+
        /* CPU <--> Codec DAI links  */
        struct snd_soc_dai_link *dai_link;
        int num_links;
index e8eeb3a1ed291fd0aae971cf45d1ced075785bce..b62ce3e077f9176274b80156afdafcd027edfe0c 100644 (file)
@@ -30,6 +30,7 @@ struct snd_tea575x;
 struct snd_tea575x_ops {
        void (*write)(struct snd_tea575x *tea, unsigned int val);
        unsigned int (*read)(struct snd_tea575x *tea);
+       void (*mute)(struct snd_tea575x *tea, unsigned int mute);
 };
 
 struct snd_tea575x {
index 9752243241e58b3524590a4225e6f53255ae0cbb..9f191a0a1e195882819e5b3a79114cb3bc64bcf5 100644 (file)
 #include "pcm.h"
 #include "mpu401.h"
 #include "ac97_codec.h"
-#include "seq_midi_emul.h"
-#include "seq_device.h"
 #include "util_mem.h"
-//#include "ainstr_iw.h"
-//#include "ainstr_gf1.h"
-#include "ainstr_simple.h"
 
 #define TRIDENT_DEVICE_ID_DX           ((PCI_VENDOR_ID_TRIDENT<<16)|PCI_DEVICE_ID_TRIDENT_4DWAVE_DX)
 #define TRIDENT_DEVICE_ID_NX           ((PCI_VENDOR_ID_TRIDENT<<16)|PCI_DEVICE_ID_TRIDENT_4DWAVE_NX)
 #define TRIDENT_DEVICE_ID_SI7018       ((PCI_VENDOR_ID_SI<<16)|PCI_DEVICE_ID_SI_7018)
 
-#define SNDRV_SEQ_DEV_ID_TRIDENT                       "trident-synth"
-
 #define SNDRV_TRIDENT_VOICE_TYPE_PCM           0
 #define SNDRV_TRIDENT_VOICE_TYPE_SYNTH         1
 #define SNDRV_TRIDENT_VOICE_TYPE_MIDI          2
@@ -257,16 +250,6 @@ struct snd_trident;
 struct snd_trident_voice;
 struct snd_trident_pcm_mixer;
 
-struct snd_trident_sample_ops {
-       void (*sample_start)(struct snd_trident *gus, struct snd_trident_voice *voice, snd_seq_position_t position);
-       void (*sample_stop)(struct snd_trident *gus, struct snd_trident_voice *voice, int mode);
-       void (*sample_freq)(struct snd_trident *gus, struct snd_trident_voice *voice, snd_seq_frequency_t freq);
-       void (*sample_volume)(struct snd_trident *gus, struct snd_trident_voice *voice, struct snd_seq_ev_volume *volume);
-       void (*sample_loop)(struct snd_trident *card, struct snd_trident_voice *voice, struct snd_seq_ev_loop *loop);
-       void (*sample_pos)(struct snd_trident *card, struct snd_trident_voice *voice, snd_seq_position_t position);
-       void (*sample_private1)(struct snd_trident *card, struct snd_trident_voice *voice, unsigned char *data);
-};
-
 struct snd_trident_port {
        struct snd_midi_channel_set * chset;
        struct snd_trident * trident;
@@ -300,7 +283,6 @@ struct snd_trident_voice {
        unsigned char port;
        unsigned char index;
 
-       struct snd_seq_instr instr;
        struct snd_trident_sample_ops *sample_ops;
 
        /* channel parameters */
@@ -354,9 +336,6 @@ struct snd_4dwave {
        int seq_client;
 
        struct snd_trident_port seq_ports[4];
-       struct snd_simple_ops simple_ops;
-       struct snd_seq_kinstr_list *ilist;
-
        struct snd_trident_voice voices[64];    
 
        int ChanSynthCount;             /* number of allocated synth channels */
@@ -416,7 +395,6 @@ struct snd_trident {
        struct snd_pcm *foldback;       /* Foldback PCM */
        struct snd_pcm *spdif;  /* SPDIF PCM */
        struct snd_rawmidi *rmidi;
-       struct snd_seq_device *seq_dev;
 
        struct snd_ac97_bus *ac97_bus;
        struct snd_ac97 *ac97;
index a9781eb0da09be35cfcb2e6068a4cf14653028e8..fac66c49445af8379faba913337002aa51e8d742 100644 (file)
@@ -1,3 +1,3 @@
 /* include/version.h.  Generated by alsa/ksync script.  */
-#define CONFIG_SND_VERSION "1.0.15"
-#define CONFIG_SND_DATE " (Tue Nov 20 19:16:42 2007 UTC)"
+#define CONFIG_SND_VERSION "1.0.16rc2"
+#define CONFIG_SND_DATE " (Thu Jan 31 16:40:16 2008 UTC)"
index 541b908f3cdf0075feebeb1b00672817adb2dc20..e08789484e3001e08e53acd481d97dcc37a7f88f 100644 (file)
@@ -10,8 +10,6 @@
 #define __AOA_H
 #include <asm/prom.h>
 #include <linux/module.h>
-/* So apparently there's a reason for requiring driver.h to be included first! */
-#include <sound/driver.h>
 #include <sound/core.h>
 #include <sound/asound.h>
 #include <sound/control.h>
index 71e3f93606580510dc5caf7c374f75ee45f1b460..6a3837d480e5efa33ba11efd9460dcdab725d835 100644 (file)
@@ -138,6 +138,13 @@ static int onyx_snd_vol_put(struct snd_kcontrol *kcontrol,
        struct onyx *onyx = snd_kcontrol_chip(kcontrol);
        s8 l, r;
 
+       if (ucontrol->value.integer.value[0] < -128 + VOLUME_RANGE_SHIFT ||
+           ucontrol->value.integer.value[0] > -1 + VOLUME_RANGE_SHIFT)
+               return -EINVAL;
+       if (ucontrol->value.integer.value[1] < -128 + VOLUME_RANGE_SHIFT ||
+           ucontrol->value.integer.value[1] > -1 + VOLUME_RANGE_SHIFT)
+               return -EINVAL;
+
        mutex_lock(&onyx->mutex);
        onyx_read_register(onyx, ONYX_REG_DAC_ATTEN_LEFT, &l);
        onyx_read_register(onyx, ONYX_REG_DAC_ATTEN_RIGHT, &r);
@@ -206,6 +213,9 @@ static int onyx_snd_inputgain_put(struct snd_kcontrol *kcontrol,
        struct onyx *onyx = snd_kcontrol_chip(kcontrol);
        u8 v, n;
 
+       if (ucontrol->value.integer.value[0] < 3 + INPUTGAIN_RANGE_SHIFT ||
+           ucontrol->value.integer.value[0] > 28 + INPUTGAIN_RANGE_SHIFT)
+               return -EINVAL;
        mutex_lock(&onyx->mutex);
        onyx_read_register(onyx, ONYX_REG_ADC_CONTROL, &v);
        n = v;
@@ -272,6 +282,8 @@ static void onyx_set_capture_source(struct onyx *onyx, int mic)
 static int onyx_snd_capture_source_put(struct snd_kcontrol *kcontrol,
        struct snd_ctl_elem_value *ucontrol)
 {
+       if (ucontrol->value.enumerated.item[0] > 1)
+               return -EINVAL;
        onyx_set_capture_source(snd_kcontrol_chip(kcontrol),
                                ucontrol->value.enumerated.item[0]);
        return 1;
index 70c341684794cb987189827ff0727c7b2273d5b5..7a16a3331f7e9efc6f12918cdb97f82e3d833b14 100644 (file)
@@ -248,6 +248,13 @@ static int tas_snd_vol_put(struct snd_kcontrol *kcontrol,
 {
        struct tas *tas = snd_kcontrol_chip(kcontrol);
 
+       if (ucontrol->value.integer.value[0] < 0 ||
+           ucontrol->value.integer.value[0] > 177)
+               return -EINVAL;
+       if (ucontrol->value.integer.value[1] < 0 ||
+           ucontrol->value.integer.value[1] > 177)
+               return -EINVAL;
+
        mutex_lock(&tas->mtx);
        if (tas->cached_volume_l == ucontrol->value.integer.value[0]
         && tas->cached_volume_r == ucontrol->value.integer.value[1]) {
@@ -401,6 +408,10 @@ static int tas_snd_drc_range_put(struct snd_kcontrol *kcontrol,
 {
        struct tas *tas = snd_kcontrol_chip(kcontrol);
 
+       if (ucontrol->value.integer.value[0] < 0 ||
+           ucontrol->value.integer.value[0] > TAS3004_DRC_MAX)
+               return -EINVAL;
+
        mutex_lock(&tas->mtx);
        if (tas->drc_range == ucontrol->value.integer.value[0]) {
                mutex_unlock(&tas->mtx);
@@ -447,7 +458,7 @@ static int tas_snd_drc_switch_put(struct snd_kcontrol *kcontrol,
                return 0;
        }
 
-       tas->drc_enabled = ucontrol->value.integer.value[0];
+       tas->drc_enabled = !!ucontrol->value.integer.value[0];
        if (tas->hw_enabled)
                tas3004_set_drc(tas);
        mutex_unlock(&tas->mtx);
@@ -494,6 +505,8 @@ static int tas_snd_capture_source_put(struct snd_kcontrol *kcontrol,
        struct tas *tas = snd_kcontrol_chip(kcontrol);
        int oldacr;
 
+       if (ucontrol->value.enumerated.item[0] > 1)
+               return -EINVAL;
        mutex_lock(&tas->mtx);
        oldacr = tas->acr;
 
@@ -562,6 +575,9 @@ static int tas_snd_treble_put(struct snd_kcontrol *kcontrol,
 {
        struct tas *tas = snd_kcontrol_chip(kcontrol);
 
+       if (ucontrol->value.integer.value[0] < TAS3004_TREBLE_MIN ||
+           ucontrol->value.integer.value[0] > TAS3004_TREBLE_MAX)
+               return -EINVAL;
        mutex_lock(&tas->mtx);
        if (tas->treble == ucontrol->value.integer.value[0]) {
                mutex_unlock(&tas->mtx);
@@ -610,6 +626,9 @@ static int tas_snd_bass_put(struct snd_kcontrol *kcontrol,
 {
        struct tas *tas = snd_kcontrol_chip(kcontrol);
 
+       if (ucontrol->value.integer.value[0] < TAS3004_BASS_MIN ||
+           ucontrol->value.integer.value[0] > TAS3004_BASS_MAX)
+               return -EINVAL;
        mutex_lock(&tas->mtx);
        if (tas->bass == ucontrol->value.integer.value[0]) {
                mutex_unlock(&tas->mtx);
index 8b2ba99d7f8a2640018266ba2659dc122463914a..dea7abb082cd1a5efd4038d2756147a852bd7760 100644 (file)
@@ -600,7 +600,7 @@ static int n##_control_put(struct snd_kcontrol *kcontrol,           \
        struct gpio_runtime *gpio = snd_kcontrol_chip(kcontrol);        \
        if (gpio->methods && gpio->methods->get_##n)                    \
                gpio->methods->set_##n(gpio,                            \
-                       ucontrol->value.integer.value[0]);              \
+                       !!ucontrol->value.integer.value[0]);            \
        return 1;                                                       \
 }                                                                      \
 static struct snd_kcontrol_new n##_ctl = {                             \
index efb9441b3acf2e48deb8543bb4467d4c3cb72a9f..e6beb92c6933f9df991af076f3ed17fb7b36b214 100644 (file)
@@ -11,7 +11,6 @@
 #include <linux/interrupt.h>
 #include <linux/dma-mapping.h>
 
-#include <sound/driver.h>
 #include <sound/core.h>
 
 #include <asm/macio.h>
index c6b42f9bdbc9e92eefb3bc3426bb970f297ebd2d..59bacd365733f869513df9cdb81797ad7389cfba 100644 (file)
@@ -8,9 +8,6 @@
 
 #include <asm/io.h>
 #include <linux/delay.h>
-/* So apparently there's a reason for requiring driver.h
- * to be included first, even if I don't know it... */
-#include <sound/driver.h>
 #include <sound/core.h>
 #include <asm/macio.h>
 #include <linux/pci.h>
@@ -194,6 +191,12 @@ static int i2sbus_pcm_open(struct i2sbus_dev *i2sdev, int in)
        hw->period_bytes_max = 16384;
        hw->periods_min = 3;
        hw->periods_max = MAX_DBDMA_COMMANDS;
+       err = snd_pcm_hw_constraint_integer(pi->substream->runtime,
+                                           SNDRV_PCM_HW_PARAM_PERIODS);
+       if (err < 0) {
+               result = err;
+               goto out_unlock;
+       }
        list_for_each_entry(cii, &sdev->codec_list, list) {
                if (cii->codec->open) {
                        err = cii->codec->open(cii, pi->substream);
@@ -990,6 +993,7 @@ i2sbus_attach_codec(struct soundbus_dev *dev, struct snd_card *card,
                if (dev->pcm->card != card) {
                        printk(KERN_ERR
                               "Can't attach same bus to different cards!\n");
+                       err = -EINVAL;
                        goto out_put_ci_module;
                }
                err = snd_pcm_new_stream(dev->pcm, SNDRV_PCM_STREAM_CAPTURE, 1);
index 3b73ba7d03e85ea96012225d43ff5491d1312e0b..b0a474494966d42b97b5547c4b250b65e4face8e 100644 (file)
@@ -23,7 +23,6 @@
 #include <asm/irq.h>
 #include <asm/sizes.h>
 
-#include <sound/driver.h>
 #include <sound/core.h>
 #include <sound/initval.h>
 #include <sound/ac97_codec.h>
index ca3bf4ee05a380bfe910dde8eec7c89dd98bfa49..9d1e6665b546c07b88d6b8a1eda77f23ce9d7289 100644 (file)
@@ -12,7 +12,6 @@
 #include <linux/device.h>
 #include <linux/dma-mapping.h>
 
-#include <sound/driver.h>
 #include <sound/core.h>
 #include <sound/pcm.h>
 
index 55c6c822bec13eb82cb18a764b28678bf2c31558..5d86e6809752e701218531deb29088f2f5517855 100644 (file)
@@ -18,7 +18,6 @@
 #include <linux/wait.h>
 #include <linux/delay.h>
 
-#include <sound/driver.h>
 #include <sound/core.h>
 #include <sound/pcm.h>
 #include <sound/ac97_codec.h>
@@ -352,6 +351,7 @@ static int __devinit pxa2xx_ac97_probe(struct platform_device *dev)
        snprintf(card->longname, sizeof(card->longname),
                 "%s (%s)", dev->dev.driver->name, card->mixername);
 
+       snd_card_set_dev(card, &dev->dev);
        ret = snd_card_register(card);
        if (ret == 0) {
                platform_set_drvdata(dev, card);
index e8cf904b835804dc7329ec35d0eaca216a9ce188..0ede9e4656a89cb323cb76ac96df3b9f8e9bdb91 100644 (file)
@@ -16,7 +16,6 @@
 #include <linux/slab.h>
 #include <linux/dma-mapping.h>
 
-#include <sound/driver.h>
 #include <sound/core.h>
 #include <sound/pcm.h>
 #include <sound/pcm_params.h>
index 81c64b09d3592168ac544e14ba1478bcc907e463..0eff33ca0f793742b97546deea5fa03b27dab220 100644 (file)
@@ -59,7 +59,6 @@
 * 
 ***************************************************************************************************/
 
-#include <sound/driver.h>
 #include <linux/module.h>
 #include <linux/moduleparam.h>
 #include <linux/init.h>
index df0774c76f6fc4eba16eb5abc0863ac01bbcedbc..01a1a5af47bb7749e90370c7ff92dbae528b6199 100644 (file)
@@ -19,7 +19,6 @@
  *
  */
 
-#include <sound/driver.h>
 #include <linux/threads.h>
 #include <linux/interrupt.h>
 #include <linux/slab.h>
@@ -232,8 +231,6 @@ struct snd_kcontrol *snd_ctl_new1(const struct snd_kcontrol_new *ncontrol,
        access = ncontrol->access == 0 ? SNDRV_CTL_ELEM_ACCESS_READWRITE :
                 (ncontrol->access & (SNDRV_CTL_ELEM_ACCESS_READWRITE|
                                      SNDRV_CTL_ELEM_ACCESS_INACTIVE|
-                                     SNDRV_CTL_ELEM_ACCESS_DINDIRECT|
-                                     SNDRV_CTL_ELEM_ACCESS_INDIRECT|
                                      SNDRV_CTL_ELEM_ACCESS_TLV_READWRITE|
                                      SNDRV_CTL_ELEM_ACCESS_TLV_CALLBACK));
        kctl.info = ncontrol->info;
@@ -692,7 +689,7 @@ int snd_ctl_elem_read(struct snd_card *card, struct snd_ctl_elem_value *control)
        struct snd_kcontrol *kctl;
        struct snd_kcontrol_volatile *vd;
        unsigned int index_offset;
-       int result, indirect;
+       int result;
 
        down_read(&card->controls_rwsem);
        kctl = snd_ctl_find_id(card, &control->id);
@@ -701,17 +698,12 @@ int snd_ctl_elem_read(struct snd_card *card, struct snd_ctl_elem_value *control)
        } else {
                index_offset = snd_ctl_get_ioff(kctl, &control->id);
                vd = &kctl->vd[index_offset];
-               indirect = vd->access & SNDRV_CTL_ELEM_ACCESS_INDIRECT ? 1 : 0;
-               if (control->indirect != indirect) {
-                       result = -EACCES;
-               } else {
-                       if ((vd->access & SNDRV_CTL_ELEM_ACCESS_READ) && kctl->get != NULL) {
-                               snd_ctl_build_ioff(&control->id, kctl, index_offset);
-                               result = kctl->get(kctl, control);
-                       } else {
-                               result = -EPERM;
-                       }
-               }
+               if ((vd->access & SNDRV_CTL_ELEM_ACCESS_READ) &&
+                   kctl->get != NULL) {
+                       snd_ctl_build_ioff(&control->id, kctl, index_offset);
+                       result = kctl->get(kctl, control);
+               } else
+                       result = -EPERM;
        }
        up_read(&card->controls_rwsem);
        return result;
@@ -748,7 +740,7 @@ int snd_ctl_elem_write(struct snd_card *card, struct snd_ctl_file *file,
        struct snd_kcontrol *kctl;
        struct snd_kcontrol_volatile *vd;
        unsigned int index_offset;
-       int result, indirect;
+       int result;
 
        down_read(&card->controls_rwsem);
        kctl = snd_ctl_find_id(card, &control->id);
@@ -757,23 +749,19 @@ int snd_ctl_elem_write(struct snd_card *card, struct snd_ctl_file *file,
        } else {
                index_offset = snd_ctl_get_ioff(kctl, &control->id);
                vd = &kctl->vd[index_offset];
-               indirect = vd->access & SNDRV_CTL_ELEM_ACCESS_INDIRECT ? 1 : 0;
-               if (control->indirect != indirect) {
-                       result = -EACCES;
+               if (!(vd->access & SNDRV_CTL_ELEM_ACCESS_WRITE) ||
+                   kctl->put == NULL ||
+                   (file && vd->owner && vd->owner != file)) {
+                       result = -EPERM;
                } else {
-                       if (!(vd->access & SNDRV_CTL_ELEM_ACCESS_WRITE) ||
-                           kctl->put == NULL ||
-                           (file && vd->owner != NULL && vd->owner != file)) {
-                               result = -EPERM;
-                       } else {
-                               snd_ctl_build_ioff(&control->id, kctl, index_offset);
-                               result = kctl->put(kctl, control);
-                       }
-                       if (result > 0) {
-                               up_read(&card->controls_rwsem);
-                               snd_ctl_notify(card, SNDRV_CTL_EVENT_MASK_VALUE, &control->id);
-                               return 0;
-                       }
+                       snd_ctl_build_ioff(&control->id, kctl, index_offset);
+                       result = kctl->put(kctl, control);
+               }
+               if (result > 0) {
+                       up_read(&card->controls_rwsem);
+                       snd_ctl_notify(card, SNDRV_CTL_EVENT_MASK_VALUE,
+                                      &control->id);
+                       return 0;
                }
        }
        up_read(&card->controls_rwsem);
index 9311ca397bbc20ed62d3c5e092dd6df5ed3b1d8f..6101259ad860e1b05260f36faa42758944ebc088 100644 (file)
@@ -219,7 +219,8 @@ static int copy_ctl_value_from_user(struct snd_card *card,
                                    struct snd_ctl_elem_value32 __user *data32,
                                    int *typep, int *countp)
 {
-       int i, type, count, size;
+       int i, type, size;
+       int uninitialized_var(count);
        unsigned int indirect;
 
        if (copy_from_user(&data->id, &data32->id, sizeof(data->id)))
index ea1a0621eefb6680ccd885b014005281ad455820..202dac0e4d895b51f1fde72486b393a32c3ceed7 100644 (file)
@@ -19,7 +19,6 @@
  *
  */
 
-#include <sound/driver.h>
 #include <linux/slab.h>
 #include <linux/time.h>
 #include <linux/errno.h>
index bfd9d182b8a33ac3c85ec3067c51ee4642404d30..6d6589f93899035587570807bd46bc5325425962 100644 (file)
@@ -19,7 +19,6 @@
  *
  */
 
-#include <sound/driver.h>
 #include <linux/major.h>
 #include <linux/init.h>
 #include <linux/slab.h>
index 1ffd29bb4cd06c25759d7959005d19e1ab1e0427..9977ec2eace35d547772db031c0db29bed15fe37 100644 (file)
@@ -19,7 +19,6 @@
  *
  */
 
-#include <sound/driver.h>
 #include <linux/init.h>
 #include <linux/time.h>
 #include <linux/smp_lock.h>
index 435c9399f7a94aa51aad11f2826993e395158639..e35789a92752da3b48cf034b8e058b8fc9bfbfeb 100644 (file)
@@ -19,7 +19,6 @@
  *
  */
 
-#include <sound/driver.h>
 #include <linux/slab.h>
 #include <linux/time.h>
 #include <linux/string.h>
@@ -66,8 +65,6 @@ int snd_oss_info_register(int dev, int num, char *string)
 
 EXPORT_SYMBOL(snd_oss_info_register);
 
-extern void snd_card_info_read_oss(struct snd_info_buffer *buffer);
-
 static int snd_sndstat_show_strings(struct snd_info_buffer *buf, char *id, int dev)
 {
        int idx, ok = -1;
index 2cb7099eb1e1b994fa12f7c24da0189f98c5fa5a..e3338d6071efb993ae9370bf634fdf28ebb27e42 100644 (file)
@@ -19,7 +19,6 @@
  *
  */
 
-#include <sound/driver.h>
 #include <linux/init.h>
 #include <linux/sched.h>
 #include <linux/file.h>
@@ -43,6 +42,40 @@ EXPORT_SYMBOL(snd_cards);
 
 static DEFINE_MUTEX(snd_card_mutex);
 
+static char *slots[SNDRV_CARDS];
+module_param_array(slots, charp, NULL, 0444);
+MODULE_PARM_DESC(slots, "Module names assigned to the slots.");
+
+/* return non-zero if the given index is already reserved for another
+ * module via slots option
+ */
+static int module_slot_mismatch(struct module *module, int idx)
+{
+#ifdef MODULE
+       char *s1, *s2;
+       if (!module || !module->name || !slots[idx])
+               return 0;
+       s1 = slots[idx];
+       s2 = module->name;
+       /* compare module name strings
+        * hyphens are handled as equivalent with underscore
+        */
+       for (;;) {
+               char c1 = *s1++;
+               char c2 = *s2++;
+               if (c1 == '-')
+                       c1 = '_';
+               if (c2 == '-')
+                       c2 = '_';
+               if (c1 != c2)
+                       return 1;
+               if (!c1)
+                       break;
+       }
+#endif
+       return 0;
+}
+
 #if defined(CONFIG_SND_MIXER_OSS) || defined(CONFIG_SND_MIXER_OSS_MODULE)
 int (*snd_mixer_oss_notify_callback)(struct snd_card *card, int free_flag);
 EXPORT_SYMBOL(snd_mixer_oss_notify_callback);
@@ -115,6 +148,8 @@ struct snd_card *snd_card_new(int idx, const char *xid,
                for (idx2 = 0; idx2 < SNDRV_CARDS; idx2++)
                        /* idx == -1 == 0xffff means: take any free slot */
                        if (~snd_cards_lock & idx & 1<<idx2) {
+                               if (module_slot_mismatch(module, idx2))
+                                       continue;
                                idx = idx2;
                                if (idx >= snd_ecards_limit)
                                        snd_ecards_limit = idx + 1;
@@ -304,8 +339,8 @@ int snd_card_disconnect(struct snd_card *card)
                list_add(&mfile->shutdown_list, &shutdown_files);
                spin_unlock(&shutdown_lock);
 
-               fops_get(&snd_shutdown_f_ops);
                mfile->file->f_op = &snd_shutdown_f_ops;
+               fops_get(mfile->file->f_op);
                
                mfile = mfile->next;
        }
index eb173cef4f05e3925565090e048b8be0d3e161bb..79f0f16af33930a8151a15c26a7d79bee7b8d69b 100644 (file)
@@ -26,7 +26,6 @@
 
 #undef HAVE_REALLY_SLOW_DMA_CONTROLLER
 
-#include <sound/driver.h>
 #include <sound/core.h>
 #include <asm/dma.h>
 
index 9b4992eab479defd06cb28663ff448beb047be79..920e5780c2284c9c086dd1bfaf02aff5462786d2 100644 (file)
@@ -568,6 +568,7 @@ static ssize_t snd_mem_proc_write(struct file *file, const char __user * buffer,
                                if (pci_set_dma_mask(pci, mask) < 0 ||
                                    pci_set_consistent_dma_mask(pci, mask) < 0) {
                                        printk(KERN_ERR "snd-page-alloc: cannot set DMA mask %lx for pci %04x:%04x\n", mask, vendor, device);
+                                       pci_dev_put(pci);
                                        return count;
                                }
                        }
index 25b0f056563e79ed8d339202f617a33434b0f6f4..1161158582a6ce112ab3a7329b2ad09507b57979 100644 (file)
@@ -20,9 +20,9 @@
  *
  */
 
-#include <linux/module.h>
 #include <asm/io.h>
 #include <asm/uaccess.h>
+#include <sound/core.h>
 
 /**
  * copy_to_user_fromio - copy data from mmio-space to user-space
index 6cabab8cc5374993d0e856d340d8894c4d965596..102d1c36cf26a67386258e28022760b5669844c2 100644 (file)
@@ -19,7 +19,6 @@
  *
  */
 
-#include <sound/driver.h>
 #include <linux/init.h>
 #include <linux/time.h>
 #include <linux/ioport.h>
index d6a04c2d5a75a7933fd5d7f9c51008a08536565a..9ded30d0e97da413c1ec244ec8aa205d0b010254 100644 (file)
@@ -19,7 +19,6 @@
  *
  */
 
-#include <sound/driver.h>
 #include <linux/time.h>
 #include <sound/core.h>
 #include <sound/pcm.h>
index 3ece39fc48dbaff2a8c06e4ef9cf0f62f73a32a7..f874f6ca3657037121e1084858c9d002bbef3b44 100644 (file)
@@ -19,7 +19,6 @@
  *
  */
   
-#include <sound/driver.h>
 #include <linux/time.h>
 #include <sound/core.h>
 #include <sound/pcm.h>
index 06f96a3e86f675dded23ca2adaa20b59f3b31d52..da3dbd41669eb9556699cfe3e7d1c9cbb20896b3 100644 (file)
@@ -20,7 +20,6 @@
  *
  */
 
-#include <sound/driver.h>
 #include <linux/time.h>
 #include <sound/core.h>
 #include <sound/pcm.h>
index c5a5ab9cae8cace6edc773a4eb9a3e9b3a700f9a..75daed298a15a251fcf3a4af20b4651b905326ea 100644 (file)
@@ -19,7 +19,6 @@
  *
  */
 
-#include <sound/driver.h>
 #include <linux/init.h>
 #include <linux/slab.h>
 #include <linux/time.h>
index 848db82529ed26ca17cc9674e754cc1e27c011c0..77f96194a0eda5eff6ed2cb3e66ae32d5207f88f 100644 (file)
@@ -21,7 +21,6 @@
  *
  */
   
-#include <sound/driver.h>
 #include <linux/time.h>
 #include <sound/core.h>
 #include <sound/pcm.h>
index d0c4ceb9f0b4e55efaff4e16dc2fc585df81a3a4..4c601b192ddf3feb4117186d230a73f4538f1723 100644 (file)
@@ -26,7 +26,6 @@
 #define OSS_DEBUG
 #endif
 
-#include <sound/driver.h>
 #include <linux/init.h>
 #include <linux/slab.h>
 #include <linux/time.h>
@@ -985,10 +984,8 @@ static int snd_pcm_oss_change_params(struct snd_pcm_substream *substream)
                sw_params->stop_threshold = runtime->buffer_size;
        sw_params->tstamp_mode = SNDRV_PCM_TSTAMP_NONE;
        sw_params->period_step = 1;
-       sw_params->sleep_min = 0;
        sw_params->avail_min = substream->stream == SNDRV_PCM_STREAM_PLAYBACK ?
                1 : runtime->period_size;
-       sw_params->xfer_align = 1;
        if (atomic_read(&substream->mmap_count) ||
            substream->oss.setup.nosilence) {
                sw_params->silence_threshold = 0;
@@ -1624,6 +1621,7 @@ static int snd_pcm_oss_sync(struct snd_pcm_oss_file *pcm_oss_file)
                                        snd_pcm_format_set_silence(runtime->format,
                                                                   runtime->oss.buffer,
                                                                   size1);
+                                       size1 /= runtime->channels; /* frames */
                                        fs = snd_enter_user();
                                        snd_pcm_lib_write(substream, (void __user *)runtime->oss.buffer, size1);
                                        snd_leave_user(fs);
index 14095a927a1b1b6e9ab7ccf7cded095be969d43c..bec94138205edf3aad8266d7239100a5aef3d8a3 100644 (file)
@@ -24,7 +24,6 @@
 #define PLUGIN_DEBUG
 #endif
 
-#include <sound/driver.h>
 #include <linux/slab.h>
 #include <linux/time.h>
 #include <linux/vmalloc.h>
index 9eb267913c3814fcb1c194670bb0b208bf06fc33..14dfb3175d84548f9ef1ccdcde84517cb137a635 100644 (file)
@@ -19,7 +19,6 @@
  *
  */
   
-#include <sound/driver.h>
 #include <linux/time.h>
 #include <sound/core.h>
 #include <sound/pcm.h>
index de3ffdeaf7e346de505337c3461a9979e5ce0224..da7ab7a3e82cdd51d51a11cc3e2bf935bbc464c9 100644 (file)
@@ -19,7 +19,6 @@
  *
  */
 
-#include <sound/driver.h>
 #include <linux/slab.h>
 #include <linux/time.h>
 #include <sound/core.h>
index cf9b9493d41deba8810dfe266353e294d7d05af1..9dd9bc73fe1d6ab8b568fa1ce865650f52bae72f 100644 (file)
@@ -19,7 +19,6 @@
  *
  */
 
-#include <sound/driver.h>
 #include <linux/init.h>
 #include <linux/slab.h>
 #include <linux/time.h>
@@ -228,7 +227,7 @@ static char *snd_pcm_subformat_names[] = {
 
 static char *snd_pcm_tstamp_mode_names[] = {
        TSTAMP(NONE),
-       TSTAMP(MMAP),
+       TSTAMP(ENABLE),
 };
 
 static const char *snd_pcm_stream_name(int stream)
@@ -359,7 +358,6 @@ static void snd_pcm_substream_proc_hw_params_read(struct snd_info_entry *entry,
        snd_iprintf(buffer, "rate: %u (%u/%u)\n", runtime->rate, runtime->rate_num, runtime->rate_den); 
        snd_iprintf(buffer, "period_size: %lu\n", runtime->period_size);        
        snd_iprintf(buffer, "buffer_size: %lu\n", runtime->buffer_size);        
-       snd_iprintf(buffer, "tick_time: %u\n", runtime->tick_time);
 #if defined(CONFIG_SND_PCM_OSS) || defined(CONFIG_SND_PCM_OSS_MODULE)
        if (substream->oss.oss) {
                snd_iprintf(buffer, "OSS format: %s\n", snd_pcm_oss_format_name(runtime->oss.format));
@@ -387,9 +385,7 @@ static void snd_pcm_substream_proc_sw_params_read(struct snd_info_entry *entry,
        }
        snd_iprintf(buffer, "tstamp_mode: %s\n", snd_pcm_tstamp_mode_name(runtime->tstamp_mode));
        snd_iprintf(buffer, "period_step: %u\n", runtime->period_step);
-       snd_iprintf(buffer, "sleep_min: %u\n", runtime->sleep_min);
        snd_iprintf(buffer, "avail_min: %lu\n", runtime->control->avail_min);
-       snd_iprintf(buffer, "xfer_align: %lu\n", runtime->xfer_align);
        snd_iprintf(buffer, "start_threshold: %lu\n", runtime->start_threshold);
        snd_iprintf(buffer, "stop_threshold: %lu\n", runtime->stop_threshold);
        snd_iprintf(buffer, "silence_threshold: %lu\n", runtime->silence_threshold);
@@ -765,12 +761,6 @@ static int snd_pcm_dev_free(struct snd_device *device)
        return snd_pcm_free(pcm);
 }
 
-static void snd_pcm_tick_timer_func(unsigned long data)
-{
-       struct snd_pcm_substream *substream = (struct snd_pcm_substream *) data;
-       snd_pcm_tick_elapsed(substream);
-}
-
 int snd_pcm_attach_substream(struct snd_pcm *pcm, int stream,
                             struct file *file,
                             struct snd_pcm_substream **rsubstream)
@@ -877,9 +867,6 @@ int snd_pcm_attach_substream(struct snd_pcm *pcm, int stream,
        memset((void*)runtime->control, 0, size);
 
        init_waitqueue_head(&runtime->sleep);
-       init_timer(&runtime->tick_timer);
-       runtime->tick_timer.function = snd_pcm_tick_timer_func;
-       runtime->tick_timer.data = (unsigned long) substream;
 
        runtime->status->state = SNDRV_PCM_STATE_OPEN;
 
index 2b539799d23b7a88a10f1583281b7f2263b55786..49aa693fba8adb8507d892a3fdc8081960d8c4ce 100644 (file)
@@ -484,6 +484,7 @@ static long snd_pcm_ioctl_compat(struct file *file, unsigned int cmd, unsigned l
        case SNDRV_PCM_IOCTL_PVERSION:
        case SNDRV_PCM_IOCTL_INFO:
        case SNDRV_PCM_IOCTL_TSTAMP:
+       case SNDRV_PCM_IOCTL_TTSTAMP:
        case SNDRV_PCM_IOCTL_HWSYNC:
        case SNDRV_PCM_IOCTL_PREPARE:
        case SNDRV_PCM_IOCTL_RESET:
index 806f1fba5446d1f639309d203f55d2de2db268cc..1533f0379e9d57abe850c0e636952afa5ccc397c 100644 (file)
@@ -20,7 +20,6 @@
  *
  */
 
-#include <sound/driver.h>
 #include <linux/slab.h>
 #include <linux/time.h>
 #include <sound/core.h>
@@ -145,11 +144,11 @@ static inline snd_pcm_uframes_t snd_pcm_update_hw_ptr_pos(struct snd_pcm_substre
 {
        snd_pcm_uframes_t pos;
 
+       if (runtime->tstamp_mode == SNDRV_PCM_TSTAMP_ENABLE)
+               snd_pcm_gettime(runtime, (struct timespec *)&runtime->status->tstamp);
        pos = substream->ops->pointer(substream);
        if (pos == SNDRV_PCM_POS_XRUN)
                return pos; /* XRUN */
-       if (runtime->tstamp_mode & SNDRV_PCM_TSTAMP_MMAP)
-               getnstimeofday((struct timespec *)&runtime->status->tstamp);
 #ifdef CONFIG_SND_DEBUG
        if (pos >= runtime->buffer_size) {
                snd_printk(KERN_ERR  "BUG: stream = %i, pos = 0x%lx, buffer size = 0x%lx, period size = 0x%lx\n", substream->stream, pos, runtime->buffer_size, runtime->period_size);
@@ -1139,7 +1138,7 @@ EXPORT_SYMBOL(snd_pcm_hw_constraint_step);
 
 static int snd_pcm_hw_rule_pow2(struct snd_pcm_hw_params *params, struct snd_pcm_hw_rule *rule)
 {
-       static int pow2_sizes[] = {
+       static unsigned int pow2_sizes[] = {
                1<<0, 1<<1, 1<<2, 1<<3, 1<<4, 1<<5, 1<<6, 1<<7,
                1<<8, 1<<9, 1<<10, 1<<11, 1<<12, 1<<13, 1<<14, 1<<15,
                1<<16, 1<<17, 1<<18, 1<<19, 1<<20, 1<<21, 1<<22, 1<<23,
@@ -1451,108 +1450,13 @@ int snd_pcm_lib_ioctl(struct snd_pcm_substream *substream,
 
 EXPORT_SYMBOL(snd_pcm_lib_ioctl);
 
-/*
- *  Conditions
- */
-
-static void snd_pcm_system_tick_set(struct snd_pcm_substream *substream, 
-                                   unsigned long ticks)
-{
-       struct snd_pcm_runtime *runtime = substream->runtime;
-       if (ticks == 0)
-               del_timer(&runtime->tick_timer);
-       else {
-               ticks += (1000000 / HZ) - 1;
-               ticks /= (1000000 / HZ);
-               mod_timer(&runtime->tick_timer, jiffies + ticks);
-       }
-}
-
-/* Temporary alias */
-void snd_pcm_tick_set(struct snd_pcm_substream *substream, unsigned long ticks)
-{
-       snd_pcm_system_tick_set(substream, ticks);
-}
-
-void snd_pcm_tick_prepare(struct snd_pcm_substream *substream)
-{
-       struct snd_pcm_runtime *runtime = substream->runtime;
-       snd_pcm_uframes_t frames = ULONG_MAX;
-       snd_pcm_uframes_t avail, dist;
-       unsigned int ticks;
-       u_int64_t n;
-       u_int32_t r;
-       if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
-               if (runtime->silence_size >= runtime->boundary) {
-                       frames = 1;
-               } else if (runtime->silence_size > 0 &&
-                          runtime->silence_filled < runtime->buffer_size) {
-                       snd_pcm_sframes_t noise_dist;
-                       noise_dist = snd_pcm_playback_hw_avail(runtime) + runtime->silence_filled;
-                       if (noise_dist > (snd_pcm_sframes_t)runtime->silence_threshold)
-                               frames = noise_dist - runtime->silence_threshold;
-               }
-               avail = snd_pcm_playback_avail(runtime);
-       } else {
-               avail = snd_pcm_capture_avail(runtime);
-       }
-       if (avail < runtime->control->avail_min) {
-               snd_pcm_sframes_t n = runtime->control->avail_min - avail;
-               if (n > 0 && frames > (snd_pcm_uframes_t)n)
-                       frames = n;
-       }
-       if (avail < runtime->buffer_size) {
-               snd_pcm_sframes_t n = runtime->buffer_size - avail;
-               if (n > 0 && frames > (snd_pcm_uframes_t)n)
-                       frames = n;
-       }
-       if (frames == ULONG_MAX) {
-               snd_pcm_tick_set(substream, 0);
-               return;
-       }
-       dist = runtime->status->hw_ptr - runtime->hw_ptr_base;
-       /* Distance to next interrupt */
-       dist = runtime->period_size - dist % runtime->period_size;
-       if (dist <= frames) {
-               snd_pcm_tick_set(substream, 0);
-               return;
-       }
-       /* the base time is us */
-       n = frames;
-       n *= 1000000;
-       div64_32(&n, runtime->tick_time * runtime->rate, &r);
-       ticks = n + (r > 0 ? 1 : 0);
-       if (ticks < runtime->sleep_min)
-               ticks = runtime->sleep_min;
-       snd_pcm_tick_set(substream, (unsigned long) ticks);
-}
-
-void snd_pcm_tick_elapsed(struct snd_pcm_substream *substream)
-{
-       struct snd_pcm_runtime *runtime;
-       unsigned long flags;
-       
-       snd_assert(substream != NULL, return);
-       runtime = substream->runtime;
-       snd_assert(runtime != NULL, return);
-
-       snd_pcm_stream_lock_irqsave(substream, flags);
-       if (!snd_pcm_running(substream) ||
-           snd_pcm_update_hw_ptr(substream) < 0)
-               goto _end;
-       if (runtime->sleep_min)
-               snd_pcm_tick_prepare(substream);
- _end:
-       snd_pcm_stream_unlock_irqrestore(substream, flags);
-}
-
 /**
  * snd_pcm_period_elapsed - update the pcm status for the next period
  * @substream: the pcm substream instance
  *
  * This function is called from the interrupt handler when the
  * PCM has processed the period size.  It will update the current
- * pointer, set up the tick, wake up sleepers, etc.
+ * pointer, wake up sleepers, etc.
  *
  * Even if more than one periods have elapsed since the last call, you
  * have to call this only once.
@@ -1576,8 +1480,6 @@ void snd_pcm_period_elapsed(struct snd_pcm_substream *substream)
 
        if (substream->timer_running)
                snd_timer_interrupt(substream->timer, 1);
-       if (runtime->sleep_min)
-               snd_pcm_tick_prepare(substream);
  _end:
        snd_pcm_stream_unlock_irqrestore(substream, flags);
        if (runtime->transfer_ack_end)
@@ -1587,6 +1489,71 @@ void snd_pcm_period_elapsed(struct snd_pcm_substream *substream)
 
 EXPORT_SYMBOL(snd_pcm_period_elapsed);
 
+/*
+ * Wait until avail_min data becomes available
+ * Returns a negative error code if any error occurs during operation.
+ * The available space is stored on availp.  When err = 0 and avail = 0
+ * on the capture stream, it indicates the stream is in DRAINING state.
+ */
+static int wait_for_avail_min(struct snd_pcm_substream *substream,
+                             snd_pcm_uframes_t *availp)
+{
+       struct snd_pcm_runtime *runtime = substream->runtime;
+       int is_playback = substream->stream == SNDRV_PCM_STREAM_PLAYBACK;
+       wait_queue_t wait;
+       int err = 0;
+       snd_pcm_uframes_t avail = 0;
+       long tout;
+
+       init_waitqueue_entry(&wait, current);
+       add_wait_queue(&runtime->sleep, &wait);
+       for (;;) {
+               if (signal_pending(current)) {
+                       err = -ERESTARTSYS;
+                       break;
+               }
+               set_current_state(TASK_INTERRUPTIBLE);
+               snd_pcm_stream_unlock_irq(substream);
+               tout = schedule_timeout(msecs_to_jiffies(10000));
+               snd_pcm_stream_lock_irq(substream);
+               switch (runtime->status->state) {
+               case SNDRV_PCM_STATE_SUSPENDED:
+                       err = -ESTRPIPE;
+                       goto _endloop;
+               case SNDRV_PCM_STATE_XRUN:
+                       err = -EPIPE;
+                       goto _endloop;
+               case SNDRV_PCM_STATE_DRAINING:
+                       if (is_playback)
+                               err = -EPIPE;
+                       else 
+                               avail = 0; /* indicate draining */
+                       goto _endloop;
+               case SNDRV_PCM_STATE_OPEN:
+               case SNDRV_PCM_STATE_SETUP:
+               case SNDRV_PCM_STATE_DISCONNECTED:
+                       err = -EBADFD;
+                       goto _endloop;
+               }
+               if (!tout) {
+                       snd_printd("%s write error (DMA or IRQ trouble?)\n",
+                                  is_playback ? "playback" : "capture");
+                       err = -EIO;
+                       break;
+               }
+               if (is_playback)
+                       avail = snd_pcm_playback_avail(runtime);
+               else
+                       avail = snd_pcm_capture_avail(runtime);
+               if (avail >= runtime->control->avail_min)
+                       break;
+       }
+ _endloop:
+       remove_wait_queue(&runtime->sleep, &wait);
+       *availp = avail;
+       return err;
+}
+       
 static int snd_pcm_lib_write_transfer(struct snd_pcm_substream *substream,
                                      unsigned int hwoff,
                                      unsigned long data, unsigned int off,
@@ -1624,8 +1591,6 @@ static snd_pcm_sframes_t snd_pcm_lib_write1(struct snd_pcm_substream *substream,
 
        if (size == 0)
                return 0;
-       if (size > runtime->xfer_align)
-               size -= size % runtime->xfer_align;
 
        snd_pcm_stream_lock_irq(substream);
        switch (runtime->status->state) {
@@ -1648,84 +1613,18 @@ static snd_pcm_sframes_t snd_pcm_lib_write1(struct snd_pcm_substream *substream,
                snd_pcm_uframes_t frames, appl_ptr, appl_ofs;
                snd_pcm_uframes_t avail;
                snd_pcm_uframes_t cont;
-               if (runtime->sleep_min == 0 && runtime->status->state == SNDRV_PCM_STATE_RUNNING)
+               if (runtime->status->state == SNDRV_PCM_STATE_RUNNING)
                        snd_pcm_update_hw_ptr(substream);
                avail = snd_pcm_playback_avail(runtime);
-               if (((avail < runtime->control->avail_min && size > avail) ||
-                  (size >= runtime->xfer_align && avail < runtime->xfer_align))) {
-                       wait_queue_t wait;
-                       enum { READY, SIGNALED, ERROR, SUSPENDED, EXPIRED, DROPPED } state;
-                       long tout;
-
+               if (!avail) {
                        if (nonblock) {
                                err = -EAGAIN;
                                goto _end_unlock;
                        }
-
-                       init_waitqueue_entry(&wait, current);
-                       add_wait_queue(&runtime->sleep, &wait);
-                       while (1) {
-                               if (signal_pending(current)) {
-                                       state = SIGNALED;
-                                       break;
-                               }
-                               set_current_state(TASK_INTERRUPTIBLE);
-                               snd_pcm_stream_unlock_irq(substream);
-                               tout = schedule_timeout(10 * HZ);
-                               snd_pcm_stream_lock_irq(substream);
-                               if (tout == 0) {
-                                       if (runtime->status->state != SNDRV_PCM_STATE_PREPARED &&
-                                           runtime->status->state != SNDRV_PCM_STATE_PAUSED) {
-                                               state = runtime->status->state == SNDRV_PCM_STATE_SUSPENDED ? SUSPENDED : EXPIRED;
-                                               break;
-                                       }
-                               }
-                               switch (runtime->status->state) {
-                               case SNDRV_PCM_STATE_XRUN:
-                               case SNDRV_PCM_STATE_DRAINING:
-                                       state = ERROR;
-                                       goto _end_loop;
-                               case SNDRV_PCM_STATE_SUSPENDED:
-                                       state = SUSPENDED;
-                                       goto _end_loop;
-                               case SNDRV_PCM_STATE_SETUP:
-                                       state = DROPPED;
-                                       goto _end_loop;
-                               default:
-                                       break;
-                               }
-                               avail = snd_pcm_playback_avail(runtime);
-                               if (avail >= runtime->control->avail_min) {
-                                       state = READY;
-                                       break;
-                               }
-                       }
-                      _end_loop:
-                       remove_wait_queue(&runtime->sleep, &wait);
-
-                       switch (state) {
-                       case ERROR:
-                               err = -EPIPE;
-                               goto _end_unlock;
-                       case SUSPENDED:
-                               err = -ESTRPIPE;
-                               goto _end_unlock;
-                       case SIGNALED:
-                               err = -ERESTARTSYS;
-                               goto _end_unlock;
-                       case EXPIRED:
-                               snd_printd("playback write error (DMA or IRQ trouble?)\n");
-                               err = -EIO;
-                               goto _end_unlock;
-                       case DROPPED:
-                               err = -EBADFD;
+                       err = wait_for_avail_min(substream, &avail);
+                       if (err < 0)
                                goto _end_unlock;
-                       default:
-                               break;
-                       }
                }
-               if (avail > runtime->xfer_align)
-                       avail -= avail % runtime->xfer_align;
                frames = size > avail ? avail : size;
                cont = runtime->buffer_size - runtime->control->appl_ptr % runtime->buffer_size;
                if (frames > cont)
@@ -1763,9 +1662,6 @@ static snd_pcm_sframes_t snd_pcm_lib_write1(struct snd_pcm_substream *substream,
                        if (err < 0)
                                goto _end_unlock;
                }
-               if (runtime->sleep_min &&
-                   runtime->status->state == SNDRV_PCM_STATE_RUNNING)
-                       snd_pcm_tick_prepare(substream);
        }
  _end_unlock:
        snd_pcm_stream_unlock_irq(substream);
@@ -1893,8 +1789,6 @@ static snd_pcm_sframes_t snd_pcm_lib_read1(struct snd_pcm_substream *substream,
 
        if (size == 0)
                return 0;
-       if (size > runtime->xfer_align)
-               size -= size % runtime->xfer_align;
 
        snd_pcm_stream_lock_irq(substream);
        switch (runtime->status->state) {
@@ -1924,91 +1818,25 @@ static snd_pcm_sframes_t snd_pcm_lib_read1(struct snd_pcm_substream *substream,
                snd_pcm_uframes_t frames, appl_ptr, appl_ofs;
                snd_pcm_uframes_t avail;
                snd_pcm_uframes_t cont;
-               if (runtime->sleep_min == 0 && runtime->status->state == SNDRV_PCM_STATE_RUNNING)
+               if (runtime->status->state == SNDRV_PCM_STATE_RUNNING)
                        snd_pcm_update_hw_ptr(substream);
-             __draining:
                avail = snd_pcm_capture_avail(runtime);
-               if (runtime->status->state == SNDRV_PCM_STATE_DRAINING) {
-                       if (avail < runtime->xfer_align) {
-                               err = -EPIPE;
+               if (!avail) {
+                       if (runtime->status->state ==
+                           SNDRV_PCM_STATE_DRAINING) {
+                               snd_pcm_stop(substream, SNDRV_PCM_STATE_SETUP);
                                goto _end_unlock;
                        }
-               } else if ((avail < runtime->control->avail_min && size > avail) ||
-                          (size >= runtime->xfer_align && avail < runtime->xfer_align)) {
-                       wait_queue_t wait;
-                       enum { READY, SIGNALED, ERROR, SUSPENDED, EXPIRED, DROPPED } state;
-                       long tout;
-
                        if (nonblock) {
                                err = -EAGAIN;
                                goto _end_unlock;
                        }
-
-                       init_waitqueue_entry(&wait, current);
-                       add_wait_queue(&runtime->sleep, &wait);
-                       while (1) {
-                               if (signal_pending(current)) {
-                                       state = SIGNALED;
-                                       break;
-                               }
-                               set_current_state(TASK_INTERRUPTIBLE);
-                               snd_pcm_stream_unlock_irq(substream);
-                               tout = schedule_timeout(10 * HZ);
-                               snd_pcm_stream_lock_irq(substream);
-                               if (tout == 0) {
-                                       if (runtime->status->state != SNDRV_PCM_STATE_PREPARED &&
-                                           runtime->status->state != SNDRV_PCM_STATE_PAUSED) {
-                                               state = runtime->status->state == SNDRV_PCM_STATE_SUSPENDED ? SUSPENDED : EXPIRED;
-                                               break;
-                                       }
-                               }
-                               switch (runtime->status->state) {
-                               case SNDRV_PCM_STATE_XRUN:
-                                       state = ERROR;
-                                       goto _end_loop;
-                               case SNDRV_PCM_STATE_SUSPENDED:
-                                       state = SUSPENDED;
-                                       goto _end_loop;
-                               case SNDRV_PCM_STATE_DRAINING:
-                                       goto __draining;
-                               case SNDRV_PCM_STATE_SETUP:
-                                       state = DROPPED;
-                                       goto _end_loop;
-                               default:
-                                       break;
-                               }
-                               avail = snd_pcm_capture_avail(runtime);
-                               if (avail >= runtime->control->avail_min) {
-                                       state = READY;
-                                       break;
-                               }
-                       }
-                      _end_loop:
-                       remove_wait_queue(&runtime->sleep, &wait);
-
-                       switch (state) {
-                       case ERROR:
-                               err = -EPIPE;
-                               goto _end_unlock;
-                       case SUSPENDED:
-                               err = -ESTRPIPE;
-                               goto _end_unlock;
-                       case SIGNALED:
-                               err = -ERESTARTSYS;
-                               goto _end_unlock;
-                       case EXPIRED:
-                               snd_printd("capture read error (DMA or IRQ trouble?)\n");
-                               err = -EIO;
-                               goto _end_unlock;
-                       case DROPPED:
-                               err = -EBADFD;
+                       err = wait_for_avail_min(substream, &avail);
+                       if (err < 0)
                                goto _end_unlock;
-                       default:
-                               break;
-                       }
+                       if (!avail)
+                               continue; /* draining */
                }
-               if (avail > runtime->xfer_align)
-                       avail -= avail % runtime->xfer_align;
                frames = size > avail ? avail : size;
                cont = runtime->buffer_size - runtime->control->appl_ptr % runtime->buffer_size;
                if (frames > cont)
@@ -2040,9 +1868,6 @@ static snd_pcm_sframes_t snd_pcm_lib_read1(struct snd_pcm_substream *substream,
                offset += frames;
                size -= frames;
                xfer += frames;
-               if (runtime->sleep_min &&
-                   runtime->status->state == SNDRV_PCM_STATE_RUNNING)
-                       snd_pcm_tick_prepare(substream);
        }
  _end_unlock:
        snd_pcm_stream_unlock_irq(substream);
index a13e38cfd2c62fea7d85bf8bf12f59f9cbf0a347..ff07b4a9992e80e4ad65bb3c1dac6a353020ad68 100644 (file)
@@ -19,7 +19,6 @@
  *
  */
 
-#include <sound/driver.h>
 #include <asm/io.h>
 #include <linux/time.h>
 #include <linux/init.h>
index dd9aa51d8c820b0ecfcf1d6999daf50008a41427..89b7f549bebd772bb62378510a541bb0b052e967 100644 (file)
@@ -19,7 +19,6 @@
  *
  */
   
-#include <sound/driver.h>
 #include <linux/time.h>
 #include <sound/core.h>
 #include <sound/pcm.h>
@@ -75,7 +74,7 @@ static struct pcm_format_data pcm_formats[SNDRV_PCM_FORMAT_LAST+1] = {
        },
        [SNDRV_PCM_FORMAT_U24_BE] = {
                .width = 24, .phys = 32, .le = 0, .signd = 0,
-               .silence = { 0x80, 0x00, 0x00 },
+               .silence = { 0x00, 0x80, 0x00, 0x00 },
        },
        [SNDRV_PCM_FORMAT_S32_LE] = {
                .width = 32, .phys = 32, .le = 1, .signd = 1,
index fb3dde4db0454fcf9927b80f8a3ba55a0263267b..62449117ee14f6a13cc0f76d5bba6624df3596b9 100644 (file)
@@ -19,7 +19,6 @@
  *
  */
 
-#include <sound/driver.h>
 #include <linux/mm.h>
 #include <linux/file.h>
 #include <linux/slab.h>
@@ -413,7 +412,6 @@ static int snd_pcm_hw_params(struct snd_pcm_substream *substream,
        runtime->period_size = params_period_size(params);
        runtime->periods = params_periods(params);
        runtime->buffer_size = params_buffer_size(params);
-       runtime->tick_time = params_tick_time(params);
        runtime->info = params->info;
        runtime->rate_num = params->rate_num;
        runtime->rate_den = params->rate_den;
@@ -433,9 +431,7 @@ static int snd_pcm_hw_params(struct snd_pcm_substream *substream,
        /* Default sw params */
        runtime->tstamp_mode = SNDRV_PCM_TSTAMP_NONE;
        runtime->period_step = 1;
-       runtime->sleep_min = 0;
        runtime->control->avail_min = runtime->period_size;
-       runtime->xfer_align = runtime->period_size;
        runtime->start_threshold = 1;
        runtime->stop_threshold = runtime->buffer_size;
        runtime->silence_threshold = 0;
@@ -532,9 +528,6 @@ static int snd_pcm_sw_params(struct snd_pcm_substream *substream,
                return -EINVAL;
        if (params->avail_min == 0)
                return -EINVAL;
-       if (params->xfer_align == 0 ||
-           params->xfer_align % runtime->min_align != 0)
-               return -EINVAL;
        if (params->silence_size >= runtime->boundary) {
                if (params->silence_threshold != 0)
                        return -EINVAL;
@@ -546,20 +539,14 @@ static int snd_pcm_sw_params(struct snd_pcm_substream *substream,
        }
        snd_pcm_stream_lock_irq(substream);
        runtime->tstamp_mode = params->tstamp_mode;
-       runtime->sleep_min = params->sleep_min;
        runtime->period_step = params->period_step;
        runtime->control->avail_min = params->avail_min;
        runtime->start_threshold = params->start_threshold;
        runtime->stop_threshold = params->stop_threshold;
        runtime->silence_threshold = params->silence_threshold;
        runtime->silence_size = params->silence_size;
-       runtime->xfer_align = params->xfer_align;
         params->boundary = runtime->boundary;
        if (snd_pcm_running(substream)) {
-               if (runtime->sleep_min)
-                       snd_pcm_tick_prepare(substream);
-               else
-                       snd_pcm_tick_set(substream, 0);
                if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK &&
                    runtime->silence_size > 0)
                        snd_pcm_playback_silence(substream, ULONG_MAX);
@@ -595,12 +582,13 @@ int snd_pcm_status(struct snd_pcm_substream *substream,
        status->trigger_tstamp = runtime->trigger_tstamp;
        if (snd_pcm_running(substream)) {
                snd_pcm_update_hw_ptr(substream);
-               if (runtime->tstamp_mode & SNDRV_PCM_TSTAMP_MMAP)
+               if (runtime->tstamp_mode == SNDRV_PCM_TSTAMP_ENABLE) {
                        status->tstamp = runtime->status->tstamp;
-               else
-                       getnstimeofday(&status->tstamp);
-       } else
-               getnstimeofday(&status->tstamp);
+                       goto _tstamp_end;
+               }
+       }
+       snd_pcm_gettime(runtime, &status->tstamp);
+ _tstamp_end:
        status->appl_ptr = runtime->control->appl_ptr;
        status->hw_ptr = runtime->status->hw_ptr;
        if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
@@ -688,7 +676,7 @@ static void snd_pcm_trigger_tstamp(struct snd_pcm_substream *substream)
        if (runtime->trigger_master == NULL)
                return;
        if (runtime->trigger_master == substream) {
-               getnstimeofday(&runtime->trigger_tstamp);
+               snd_pcm_gettime(runtime, &runtime->trigger_tstamp);
        } else {
                snd_pcm_trigger_tstamp(runtime->trigger_master);
                runtime->trigger_tstamp = runtime->trigger_master->runtime->trigger_tstamp;
@@ -875,8 +863,6 @@ static void snd_pcm_post_start(struct snd_pcm_substream *substream, int state)
        if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK &&
            runtime->silence_size > 0)
                snd_pcm_playback_silence(substream, ULONG_MAX);
-       if (runtime->sleep_min)
-               snd_pcm_tick_prepare(substream);
        if (substream->timer)
                snd_timer_notify(substream->timer, SNDRV_TIMER_EVENT_MSTART,
                                 &runtime->trigger_tstamp);
@@ -930,7 +916,6 @@ static void snd_pcm_post_stop(struct snd_pcm_substream *substream, int state)
                        snd_timer_notify(substream->timer, SNDRV_TIMER_EVENT_MSTOP,
                                         &runtime->trigger_tstamp);
                runtime->status->state = state;
-               snd_pcm_tick_set(substream, 0);
        }
        wake_up(&runtime->sleep);
 }
@@ -1014,12 +999,9 @@ static void snd_pcm_post_pause(struct snd_pcm_substream *substream, int push)
                        snd_timer_notify(substream->timer,
                                         SNDRV_TIMER_EVENT_MPAUSE,
                                         &runtime->trigger_tstamp);
-               snd_pcm_tick_set(substream, 0);
                wake_up(&runtime->sleep);
        } else {
                runtime->status->state = SNDRV_PCM_STATE_RUNNING;
-               if (runtime->sleep_min)
-                       snd_pcm_tick_prepare(substream);
                if (substream->timer)
                        snd_timer_notify(substream->timer,
                                         SNDRV_TIMER_EVENT_MCONTINUE,
@@ -1074,7 +1056,6 @@ static void snd_pcm_post_suspend(struct snd_pcm_substream *substream, int state)
                                 &runtime->trigger_tstamp);
        runtime->status->suspended_state = runtime->status->state;
        runtime->status->state = SNDRV_PCM_STATE_SUSPENDED;
-       snd_pcm_tick_set(substream, 0);
        wake_up(&runtime->sleep);
 }
 
@@ -1177,8 +1158,6 @@ static void snd_pcm_post_resume(struct snd_pcm_substream *substream, int state)
                snd_timer_notify(substream->timer, SNDRV_TIMER_EVENT_MRESUME,
                                 &runtime->trigger_tstamp);
        runtime->status->state = runtime->status->suspended_state;
-       if (runtime->sleep_min)
-               snd_pcm_tick_prepare(substream);
 }
 
 static struct action_ops snd_pcm_action_resume = {
@@ -1395,10 +1374,10 @@ static int snd_pcm_do_drain_init(struct snd_pcm_substream *substream, int state)
        } else {
                /* stop running stream */
                if (runtime->status->state == SNDRV_PCM_STATE_RUNNING) {
-                       int state = snd_pcm_capture_avail(runtime) > 0 ?
+                       int new_state = snd_pcm_capture_avail(runtime) > 0 ?
                                SNDRV_PCM_STATE_DRAINING : SNDRV_PCM_STATE_SETUP;
-                       snd_pcm_do_stop(substream, state);
-                       snd_pcm_post_stop(substream, state);
+                       snd_pcm_do_stop(substream, new_state);
+                       snd_pcm_post_stop(substream, new_state);
                }
        }
        return 0;
@@ -2007,8 +1986,6 @@ int snd_pcm_hw_constraints_complete(struct snd_pcm_substream *substream)
        }
 
        /* FIXME: this belong to lowlevel */
-       snd_pcm_hw_constraint_minmax(runtime, SNDRV_PCM_HW_PARAM_TICK_TIME,
-                                    1000000 / HZ, 1000000 / HZ);
        snd_pcm_hw_constraint_integer(runtime, SNDRV_PCM_HW_PARAM_PERIOD_SIZE);
 
        return 0;
@@ -2244,15 +2221,10 @@ static snd_pcm_sframes_t snd_pcm_playback_rewind(struct snd_pcm_substream *subst
        }
        if (frames > (snd_pcm_uframes_t)hw_avail)
                frames = hw_avail;
-       else
-               frames -= frames % runtime->xfer_align;
        appl_ptr = runtime->control->appl_ptr - frames;
        if (appl_ptr < 0)
                appl_ptr += runtime->boundary;
        runtime->control->appl_ptr = appl_ptr;
-       if (runtime->status->state == SNDRV_PCM_STATE_RUNNING &&
-           runtime->sleep_min)
-               snd_pcm_tick_prepare(substream);
        ret = frames;
  __end:
        snd_pcm_stream_unlock_irq(substream);
@@ -2294,15 +2266,10 @@ static snd_pcm_sframes_t snd_pcm_capture_rewind(struct snd_pcm_substream *substr
        }
        if (frames > (snd_pcm_uframes_t)hw_avail)
                frames = hw_avail;
-       else
-               frames -= frames % runtime->xfer_align;
        appl_ptr = runtime->control->appl_ptr - frames;
        if (appl_ptr < 0)
                appl_ptr += runtime->boundary;
        runtime->control->appl_ptr = appl_ptr;
-       if (runtime->status->state == SNDRV_PCM_STATE_RUNNING &&
-           runtime->sleep_min)
-               snd_pcm_tick_prepare(substream);
        ret = frames;
  __end:
        snd_pcm_stream_unlock_irq(substream);
@@ -2345,15 +2312,10 @@ static snd_pcm_sframes_t snd_pcm_playback_forward(struct snd_pcm_substream *subs
        }
        if (frames > (snd_pcm_uframes_t)avail)
                frames = avail;
-       else
-               frames -= frames % runtime->xfer_align;
        appl_ptr = runtime->control->appl_ptr + frames;
        if (appl_ptr >= (snd_pcm_sframes_t)runtime->boundary)
                appl_ptr -= runtime->boundary;
        runtime->control->appl_ptr = appl_ptr;
-       if (runtime->status->state == SNDRV_PCM_STATE_RUNNING &&
-           runtime->sleep_min)
-               snd_pcm_tick_prepare(substream);
        ret = frames;
  __end:
        snd_pcm_stream_unlock_irq(substream);
@@ -2396,15 +2358,10 @@ static snd_pcm_sframes_t snd_pcm_capture_forward(struct snd_pcm_substream *subst
        }
        if (frames > (snd_pcm_uframes_t)avail)
                frames = avail;
-       else
-               frames -= frames % runtime->xfer_align;
        appl_ptr = runtime->control->appl_ptr + frames;
        if (appl_ptr >= (snd_pcm_sframes_t)runtime->boundary)
                appl_ptr -= runtime->boundary;
        runtime->control->appl_ptr = appl_ptr;
-       if (runtime->status->state == SNDRV_PCM_STATE_RUNNING &&
-           runtime->sleep_min)
-               snd_pcm_tick_prepare(substream);
        ret = frames;
  __end:
        snd_pcm_stream_unlock_irq(substream);
@@ -2519,6 +2476,21 @@ static int snd_pcm_sync_ptr(struct snd_pcm_substream *substream,
                return -EFAULT;
        return 0;
 }
+
+static int snd_pcm_tstamp(struct snd_pcm_substream *substream, int __user *_arg)
+{
+       struct snd_pcm_runtime *runtime = substream->runtime;
+       int arg;
+       
+       if (get_user(arg, _arg))
+               return -EFAULT;
+       if (arg < 0 || arg > SNDRV_PCM_TSTAMP_TYPE_LAST)
+               return -EINVAL;
+       runtime->tstamp_type = SNDRV_PCM_TSTAMP_TYPE_GETTIMEOFDAY;
+       if (arg == SNDRV_PCM_TSTAMP_TYPE_MONOTONIC)
+               runtime->tstamp_type = SNDRV_PCM_TSTAMP_TYPE_MONOTONIC;
+       return 0;
+}
                
 static int snd_pcm_common_ioctl1(struct file *file,
                                 struct snd_pcm_substream *substream,
@@ -2531,8 +2503,10 @@ static int snd_pcm_common_ioctl1(struct file *file,
                return put_user(SNDRV_PCM_VERSION, (int __user *)arg) ? -EFAULT : 0;
        case SNDRV_PCM_IOCTL_INFO:
                return snd_pcm_info_user(substream, arg);
-       case SNDRV_PCM_IOCTL_TSTAMP: /* just for compatibility */
+       case SNDRV_PCM_IOCTL_TSTAMP:    /* just for compatibility */
                return 0;
+       case SNDRV_PCM_IOCTL_TTSTAMP:
+               return snd_pcm_tstamp(substream, arg);
        case SNDRV_PCM_IOCTL_HW_REFINE:
                return snd_pcm_hw_refine_user(substream, arg);
        case SNDRV_PCM_IOCTL_HW_PARAMS:
@@ -3018,26 +2992,23 @@ static unsigned int snd_pcm_capture_poll(struct file *file, poll_table * wait)
 /*
  * mmap status record
  */
-static struct page * snd_pcm_mmap_status_nopage(struct vm_area_struct *area,
-                                               unsigned long address, int *type)
+static int snd_pcm_mmap_status_fault(struct vm_area_struct *area,
+                                               struct vm_fault *vmf)
 {
        struct snd_pcm_substream *substream = area->vm_private_data;
        struct snd_pcm_runtime *runtime;
-       struct page * page;
        
        if (substream == NULL)
-               return NOPAGE_SIGBUS;
+               return VM_FAULT_SIGBUS;
        runtime = substream->runtime;
-       page = virt_to_page(runtime->status);
-       get_page(page);
-       if (type)
-               *type = VM_FAULT_MINOR;
-       return page;
+       vmf->page = virt_to_page(runtime->status);
+       get_page(vmf->page);
+       return 0;
 }
 
 static struct vm_operations_struct snd_pcm_vm_ops_status =
 {
-       .nopage =       snd_pcm_mmap_status_nopage,
+       .fault =        snd_pcm_mmap_status_fault,
 };
 
 static int snd_pcm_mmap_status(struct snd_pcm_substream *substream, struct file *file,
@@ -3061,26 +3032,23 @@ static int snd_pcm_mmap_status(struct snd_pcm_substream *substream, struct file
 /*
  * mmap control record
  */
-static struct page * snd_pcm_mmap_control_nopage(struct vm_area_struct *area,
-                                                unsigned long address, int *type)
+static int snd_pcm_mmap_control_fault(struct vm_area_struct *area,
+                                               struct vm_fault *vmf)
 {
        struct snd_pcm_substream *substream = area->vm_private_data;
        struct snd_pcm_runtime *runtime;
-       struct page * page;
        
        if (substream == NULL)
-               return NOPAGE_SIGBUS;
+               return VM_FAULT_SIGBUS;
        runtime = substream->runtime;
-       page = virt_to_page(runtime->control);
-       get_page(page);
-       if (type)
-               *type = VM_FAULT_MINOR;
-       return page;
+       vmf->page = virt_to_page(runtime->control);
+       get_page(vmf->page);
+       return 0;
 }
 
 static struct vm_operations_struct snd_pcm_vm_ops_control =
 {
-       .nopage =       snd_pcm_mmap_control_nopage,
+       .fault =        snd_pcm_mmap_control_fault,
 };
 
 static int snd_pcm_mmap_control(struct snd_pcm_substream *substream, struct file *file,
@@ -3117,10 +3085,10 @@ static int snd_pcm_mmap_control(struct snd_pcm_substream *substream, struct file
 #endif /* coherent mmap */
 
 /*
- * nopage callback for mmapping a RAM page
+ * fault callback for mmapping a RAM page
  */
-static struct page *snd_pcm_mmap_data_nopage(struct vm_area_struct *area,
-                                            unsigned long address, int *type)
+static int snd_pcm_mmap_data_fault(struct vm_area_struct *area,
+                                               struct vm_fault *vmf)
 {
        struct snd_pcm_substream *substream = area->vm_private_data;
        struct snd_pcm_runtime *runtime;
@@ -3130,33 +3098,30 @@ static struct page *snd_pcm_mmap_data_nopage(struct vm_area_struct *area,
        size_t dma_bytes;
        
        if (substream == NULL)
-               return NOPAGE_SIGBUS;
+               return VM_FAULT_SIGBUS;
        runtime = substream->runtime;
-       offset = area->vm_pgoff << PAGE_SHIFT;
-       offset += address - area->vm_start;
-       snd_assert((offset % PAGE_SIZE) == 0, return NOPAGE_SIGBUS);
+       offset = vmf->pgoff << PAGE_SHIFT;
        dma_bytes = PAGE_ALIGN(runtime->dma_bytes);
        if (offset > dma_bytes - PAGE_SIZE)
-               return NOPAGE_SIGBUS;
+               return VM_FAULT_SIGBUS;
        if (substream->ops->page) {
                page = substream->ops->page(substream, offset);
-               if (! page)
-                       return NOPAGE_OOM; /* XXX: is this really due to OOM? */
+               if (!page)
+                       return VM_FAULT_SIGBUS;
        } else {
                vaddr = runtime->dma_area + offset;
                page = virt_to_page(vaddr);
        }
        get_page(page);
-       if (type)
-               *type = VM_FAULT_MINOR;
-       return page;
+       vmf->page = page;
+       return 0;
 }
 
 static struct vm_operations_struct snd_pcm_vm_ops_data =
 {
        .open =         snd_pcm_mmap_data_open,
        .close =        snd_pcm_mmap_data_close,
-       .nopage =       snd_pcm_mmap_data_nopage,
+       .fault =        snd_pcm_mmap_data_fault,
 };
 
 /*
index 23aa9a27e2158f8cd2173b6e66d12887a4432453..033a024d153a846ff315d1f275bf049c52dce655 100644 (file)
@@ -19,7 +19,6 @@
  *
  */
 
-#include <sound/driver.h>
 #include <linux/time.h>
 #include <sound/core.h>
 #include <sound/pcm.h>
index b8e700b94e5962b24a9355be54f2548753633eb7..f7ea7287c59cd7edfcf75bc5795efce07d512d79 100644 (file)
@@ -19,7 +19,6 @@
  *
  */
 
-#include <sound/driver.h>
 #include <sound/core.h>
 #include <linux/major.h>
 #include <linux/init.h>
@@ -912,7 +911,8 @@ int snd_rawmidi_receive(struct snd_rawmidi_substream *substream,
 }
 
 static long snd_rawmidi_kernel_read1(struct snd_rawmidi_substream *substream,
-                                    unsigned char *buf, long count, int kernel)
+                                    unsigned char __user *userbuf,
+                                    unsigned char *kernelbuf, long count)
 {
        unsigned long flags;
        long result = 0, count1;
@@ -925,11 +925,11 @@ static long snd_rawmidi_kernel_read1(struct snd_rawmidi_substream *substream,
                spin_lock_irqsave(&runtime->lock, flags);
                if (count1 > (int)runtime->avail)
                        count1 = runtime->avail;
-               if (kernel) {
-                       memcpy(buf + result, runtime->buffer + runtime->appl_ptr, count1);
-               } else {
+               if (kernelbuf)
+                       memcpy(kernelbuf + result, runtime->buffer + runtime->appl_ptr, count1);
+               if (userbuf) {
                        spin_unlock_irqrestore(&runtime->lock, flags);
-                       if (copy_to_user((char __user *)buf + result,
+                       if (copy_to_user(userbuf + result,
                                         runtime->buffer + runtime->appl_ptr, count1)) {
                                return result > 0 ? result : -EFAULT;
                        }
@@ -949,7 +949,7 @@ long snd_rawmidi_kernel_read(struct snd_rawmidi_substream *substream,
                             unsigned char *buf, long count)
 {
        snd_rawmidi_input_trigger(substream, 1);
-       return snd_rawmidi_kernel_read1(substream, buf, count, 1);
+       return snd_rawmidi_kernel_read1(substream, NULL/*userbuf*/, buf, count);
 }
 
 static ssize_t snd_rawmidi_read(struct file *file, char __user *buf, size_t count,
@@ -990,8 +990,9 @@ static ssize_t snd_rawmidi_read(struct file *file, char __user *buf, size_t coun
                }
                spin_unlock_irq(&runtime->lock);
                count1 = snd_rawmidi_kernel_read1(substream,
-                                                 (unsigned char __force *)buf,
-                                                 count, 0);
+                                                 (unsigned char __user *)buf,
+                                                 NULL/*kernelbuf*/,
+                                                 count);
                if (count1 < 0)
                        return result > 0 ? result : count1;
                result += count1;
@@ -1132,13 +1133,15 @@ int snd_rawmidi_transmit(struct snd_rawmidi_substream *substream,
 }
 
 static long snd_rawmidi_kernel_write1(struct snd_rawmidi_substream *substream,
-                                     const unsigned char *buf, long count, int kernel)
+                                     const unsigned char __user *userbuf,
+                                     const unsigned char *kernelbuf,
+                                     long count)
 {
        unsigned long flags;
        long count1, result;
        struct snd_rawmidi_runtime *runtime = substream->runtime;
 
-       snd_assert(buf != NULL, return -EINVAL);
+       snd_assert(kernelbuf != NULL || userbuf != NULL, return -EINVAL);
        snd_assert(runtime->buffer != NULL, return -EINVAL);
 
        result = 0;
@@ -1155,12 +1158,13 @@ static long snd_rawmidi_kernel_write1(struct snd_rawmidi_substream *substream,
                        count1 = count;
                if (count1 > (long)runtime->avail)
                        count1 = runtime->avail;
-               if (kernel) {
-                       memcpy(runtime->buffer + runtime->appl_ptr, buf, count1);
-               } else {
+               if (kernelbuf)
+                       memcpy(runtime->buffer + runtime->appl_ptr,
+                              kernelbuf + result, count1);
+               else if (userbuf) {
                        spin_unlock_irqrestore(&runtime->lock, flags);
                        if (copy_from_user(runtime->buffer + runtime->appl_ptr,
-                                          (char __user *)buf, count1)) {
+                                          userbuf + result, count1)) {
                                spin_lock_irqsave(&runtime->lock, flags);
                                result = result > 0 ? result : -EFAULT;
                                goto __end;
@@ -1171,7 +1175,6 @@ static long snd_rawmidi_kernel_write1(struct snd_rawmidi_substream *substream,
                runtime->appl_ptr %= runtime->buffer_size;
                runtime->avail -= count1;
                result += count1;
-               buf += count1;
                count -= count1;
        }
       __end:
@@ -1185,7 +1188,7 @@ static long snd_rawmidi_kernel_write1(struct snd_rawmidi_substream *substream,
 long snd_rawmidi_kernel_write(struct snd_rawmidi_substream *substream,
                              const unsigned char *buf, long count)
 {
-       return snd_rawmidi_kernel_write1(substream, buf, count, 1);
+       return snd_rawmidi_kernel_write1(substream, NULL, buf, count);
 }
 
 static ssize_t snd_rawmidi_write(struct file *file, const char __user *buf,
@@ -1225,9 +1228,7 @@ static ssize_t snd_rawmidi_write(struct file *file, const char __user *buf,
                        spin_lock_irq(&runtime->lock);
                }
                spin_unlock_irq(&runtime->lock);
-               count1 = snd_rawmidi_kernel_write1(substream,
-                                                  (unsigned char __force *)buf,
-                                                  count, 0);
+               count1 = snd_rawmidi_kernel_write1(substream, buf, NULL, count);
                if (count1 < 0)
                        return result > 0 ? result : count1;
                result += count1;
index 7cd5e8f5d4cebc2375f7303ce00d48e09046fd96..97b30fb4c361193b1ef65821a46dea9f67736d9b 100644 (file)
@@ -20,7 +20,6 @@
  *
  */
 
-#include <sound/driver.h>
 #include <linux/init.h>
 #include <linux/interrupt.h>
 #include <linux/moduleparam.h>
index ceef14afee3002dda5f6b7eff7ea076a41011a53..069593717fbab0c0deb733c52e883c5b6c638988 100644 (file)
@@ -3,7 +3,6 @@
 # Copyright (c) 1999 by Jaroslav Kysela <perex@perex.cz>
 #
 
-obj-$(CONFIG_SND) += instr/
 ifeq ($(CONFIG_SND_SEQUENCER_OSS),y)
   obj-$(CONFIG_SND_SEQUENCER) += oss/
 endif
@@ -15,7 +14,6 @@ snd-seq-objs := seq.o seq_lock.o seq_clientmgr.o seq_memory.o seq_queue.o \
 snd-seq-midi-objs := seq_midi.o
 snd-seq-midi-emul-objs := seq_midi_emul.o
 snd-seq-midi-event-objs := seq_midi_event.o
-snd-seq-instr-objs := seq_instr.o
 snd-seq-dummy-objs := seq_dummy.o
 snd-seq-virmidi-objs := seq_virmidi.o
 
@@ -36,9 +34,7 @@ obj-$(CONFIG_SND_SEQ_DUMMY) += snd-seq-dummy.o
 # Toplevel Module Dependency
 obj-$(CONFIG_SND_VIRMIDI) += snd-seq-virmidi.o snd-seq-midi-event.o
 obj-$(call sequencer,$(CONFIG_SND_RAWMIDI)) += snd-seq-midi.o snd-seq-midi-event.o
-obj-$(call sequencer,$(CONFIG_SND_OPL3_LIB)) += snd-seq-midi-event.o snd-seq-midi-emul.o snd-seq-instr.o
-obj-$(call sequencer,$(CONFIG_SND_OPL4_LIB)) += snd-seq-midi-event.o snd-seq-midi-emul.o snd-seq-instr.o
-obj-$(call sequencer,$(CONFIG_SND_GUS_SYNTH)) += snd-seq-midi-emul.o snd-seq-instr.o
+obj-$(call sequencer,$(CONFIG_SND_OPL3_LIB)) += snd-seq-midi-event.o snd-seq-midi-emul.o
+obj-$(call sequencer,$(CONFIG_SND_OPL4_LIB)) += snd-seq-midi-event.o snd-seq-midi-emul.o
 obj-$(call sequencer,$(CONFIG_SND_SBAWE)) += snd-seq-midi-emul.o snd-seq-virmidi.o
 obj-$(call sequencer,$(CONFIG_SND_EMU10K1)) += snd-seq-midi-emul.o snd-seq-virmidi.o
-obj-$(call sequencer,$(CONFIG_SND_TRIDENT)) += snd-seq-midi-emul.o snd-seq-instr.o
diff --git a/sound/core/seq/instr/Makefile b/sound/core/seq/instr/Makefile
deleted file mode 100644 (file)
index 6089603..0000000
+++ /dev/null
@@ -1,23 +0,0 @@
-#
-# Makefile for ALSA
-# Copyright (c) 1999 by Jaroslav Kysela <perex@perex.cz>
-#
-
-snd-ainstr-fm-objs := ainstr_fm.o
-snd-ainstr-simple-objs := ainstr_simple.o
-snd-ainstr-gf1-objs := ainstr_gf1.o
-snd-ainstr-iw-objs := ainstr_iw.o
-
-#
-# this function returns:
-#   "m" - CONFIG_SND_SEQUENCER is m
-#   <empty string> - CONFIG_SND_SEQUENCER is undefined
-#   otherwise parameter #1 value
-#
-sequencer = $(if $(subst y,,$(CONFIG_SND_SEQUENCER)),$(if $(1),m),$(if $(CONFIG_SND_SEQUENCER),$(1)))
-
-# Toplevel Module Dependency
-obj-$(call sequencer,$(CONFIG_SND_OPL3_LIB)) += snd-ainstr-fm.o
-obj-$(call sequencer,$(CONFIG_SND_OPL4_LIB)) += snd-ainstr-fm.o
-obj-$(call sequencer,$(CONFIG_SND_GUS_SYNTH)) += snd-ainstr-gf1.o snd-ainstr-simple.o snd-ainstr-iw.o
-obj-$(call sequencer,$(CONFIG_SND_TRIDENT)) += snd-ainstr-simple.o
diff --git a/sound/core/seq/instr/ainstr_fm.c b/sound/core/seq/instr/ainstr_fm.c
deleted file mode 100644 (file)
index f80fab8..0000000
+++ /dev/null
@@ -1,155 +0,0 @@
-/*
- *  FM (OPL2/3) Instrument routines
- *  Copyright (c) 2000 Uros Bizjak <uros@kss-loka.si>
- *
- *   This program is free software; you can redistribute it and/or modify
- *   it under the terms of the GNU General Public License as published by
- *   the Free Software Foundation; either version 2 of the License, or
- *   (at your option) any later version.
- *
- *   This program is distributed in the hope that it will be useful,
- *   but WITHOUT ANY WARRANTY; without even the implied warranty of
- *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- *   GNU General Public License for more details.
- *
- *   You should have received a copy of the GNU General Public License
- *   along with this program; if not, write to the Free Software
- *   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA
- *
- */
-#include <sound/driver.h>
-#include <linux/init.h>
-#include <sound/core.h>
-#include <sound/ainstr_fm.h>
-#include <sound/initval.h>
-#include <asm/uaccess.h>
-
-MODULE_AUTHOR("Uros Bizjak <uros@kss-loka.si>");
-MODULE_DESCRIPTION("Advanced Linux Sound Architecture FM Instrument support.");
-MODULE_LICENSE("GPL");
-
-static int snd_seq_fm_put(void *private_data, struct snd_seq_kinstr *instr,
-                         char __user *instr_data, long len, int atomic, int cmd)
-{
-       struct fm_instrument *ip;
-       struct fm_xinstrument ix;
-       int idx;
-
-       if (cmd != SNDRV_SEQ_INSTR_PUT_CMD_CREATE)
-               return -EINVAL;
-       /* copy instrument data */
-       if (len < (long)sizeof(ix))
-               return -EINVAL;
-       if (copy_from_user(&ix, instr_data, sizeof(ix)))
-               return -EFAULT;
-       if (ix.stype != FM_STRU_INSTR)
-               return -EINVAL;
-       ip = (struct fm_instrument *)KINSTR_DATA(instr);
-       ip->share_id[0] = le32_to_cpu(ix.share_id[0]);
-       ip->share_id[1] = le32_to_cpu(ix.share_id[1]);
-       ip->share_id[2] = le32_to_cpu(ix.share_id[2]);
-       ip->share_id[3] = le32_to_cpu(ix.share_id[3]);
-       ip->type = ix.type;
-       for (idx = 0; idx < 4; idx++) {
-               ip->op[idx].am_vib = ix.op[idx].am_vib;
-               ip->op[idx].ksl_level = ix.op[idx].ksl_level;
-               ip->op[idx].attack_decay = ix.op[idx].attack_decay;
-               ip->op[idx].sustain_release = ix.op[idx].sustain_release;
-               ip->op[idx].wave_select = ix.op[idx].wave_select;
-       }
-       for (idx = 0; idx < 2; idx++) {
-               ip->feedback_connection[idx] = ix.feedback_connection[idx];
-       }
-       ip->echo_delay = ix.echo_delay;
-       ip->echo_atten = ix.echo_atten;
-       ip->chorus_spread = ix.chorus_spread;
-       ip->trnsps = ix.trnsps;
-       ip->fix_dur = ix.fix_dur;
-       ip->modes = ix.modes;
-       ip->fix_key = ix.fix_key;
-       return 0;
-}
-
-static int snd_seq_fm_get(void *private_data, struct snd_seq_kinstr *instr,
-                         char __user *instr_data, long len, int atomic,
-                         int cmd)
-{
-       struct fm_instrument *ip;
-       struct fm_xinstrument ix;
-       int idx;
-       
-       if (cmd != SNDRV_SEQ_INSTR_GET_CMD_FULL)
-               return -EINVAL;
-       if (len < (long)sizeof(ix))
-               return -ENOMEM;
-       memset(&ix, 0, sizeof(ix));
-       ip = (struct fm_instrument *)KINSTR_DATA(instr);
-       ix.stype = FM_STRU_INSTR;
-       ix.share_id[0] = cpu_to_le32(ip->share_id[0]);
-       ix.share_id[1] = cpu_to_le32(ip->share_id[1]);
-       ix.share_id[2] = cpu_to_le32(ip->share_id[2]);
-       ix.share_id[3] = cpu_to_le32(ip->share_id[3]);
-       ix.type = ip->type;
-       for (idx = 0; idx < 4; idx++) {
-               ix.op[idx].am_vib = ip->op[idx].am_vib;
-               ix.op[idx].ksl_level = ip->op[idx].ksl_level;
-               ix.op[idx].attack_decay = ip->op[idx].attack_decay;
-               ix.op[idx].sustain_release = ip->op[idx].sustain_release;
-               ix.op[idx].wave_select = ip->op[idx].wave_select;
-       }
-       for (idx = 0; idx < 2; idx++) {
-               ix.feedback_connection[idx] = ip->feedback_connection[idx];
-       }
-       if (copy_to_user(instr_data, &ix, sizeof(ix)))
-               return -EFAULT;
-       ix.echo_delay = ip->echo_delay;
-       ix.echo_atten = ip->echo_atten;
-       ix.chorus_spread = ip->chorus_spread;
-       ix.trnsps = ip->trnsps;
-       ix.fix_dur = ip->fix_dur;
-       ix.modes = ip->modes;
-       ix.fix_key = ip->fix_key;
-       return 0;
-}
-
-static int snd_seq_fm_get_size(void *private_data, struct snd_seq_kinstr *instr,
-                              long *size)
-{
-       *size = sizeof(struct fm_xinstrument);
-       return 0;
-}
-
-int snd_seq_fm_init(struct snd_seq_kinstr_ops *ops,
-                   struct snd_seq_kinstr_ops *next)
-{
-       memset(ops, 0, sizeof(*ops));
-       // ops->private_data = private_data;
-       ops->add_len = sizeof(struct fm_instrument);
-       ops->instr_type = SNDRV_SEQ_INSTR_ID_OPL2_3;
-       ops->put = snd_seq_fm_put;
-       ops->get = snd_seq_fm_get;
-       ops->get_size = snd_seq_fm_get_size;
-       // ops->remove = snd_seq_fm_remove;
-       // ops->notify = snd_seq_fm_notify;
-       ops->next = next;
-       return 0;
-}
-
-/*
- *  Init part
- */
-
-static int __init alsa_ainstr_fm_init(void)
-{
-       return 0;
-}
-
-static void __exit alsa_ainstr_fm_exit(void)
-{
-}
-
-module_init(alsa_ainstr_fm_init)
-module_exit(alsa_ainstr_fm_exit)
-
-EXPORT_SYMBOL(snd_seq_fm_init);
diff --git a/sound/core/seq/instr/ainstr_gf1.c b/sound/core/seq/instr/ainstr_gf1.c
deleted file mode 100644 (file)
index 4940026..0000000
+++ /dev/null
@@ -1,359 +0,0 @@
-/*
- *   GF1 (GUS) Patch - Instrument routines
- *   Copyright (c) 1999 by Jaroslav Kysela <perex@perex.cz>
- *
- *   This program is free software; you can redistribute it and/or modify
- *   it under the terms of the GNU General Public License as published by
- *   the Free Software Foundation; either version 2 of the License, or
- *   (at your option) any later version.
- *
- *   This program is distributed in the hope that it will be useful,
- *   but WITHOUT ANY WARRANTY; without even the implied warranty of
- *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- *   GNU General Public License for more details.
- *
- *   You should have received a copy of the GNU General Public License
- *   along with this program; if not, write to the Free Software
- *   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA
- *
- */
-#include <sound/driver.h>
-#include <linux/init.h>
-#include <linux/slab.h>
-#include <sound/core.h>
-#include <sound/ainstr_gf1.h>
-#include <sound/initval.h>
-#include <asm/uaccess.h>
-
-MODULE_AUTHOR("Jaroslav Kysela <perex@perex.cz>");
-MODULE_DESCRIPTION("Advanced Linux Sound Architecture GF1 (GUS) Patch support.");
-MODULE_LICENSE("GPL");
-
-static unsigned int snd_seq_gf1_size(unsigned int size, unsigned int format)
-{
-       unsigned int result = size;
-       
-       if (format & GF1_WAVE_16BIT)
-               result <<= 1;
-       if (format & GF1_WAVE_STEREO)
-               result <<= 1;
-       return format;
-}
-
-static int snd_seq_gf1_copy_wave_from_stream(struct snd_gf1_ops *ops,
-                                            struct gf1_instrument *ip,
-                                            char __user **data,
-                                            long *len,
-                                            int atomic)
-{
-       struct gf1_wave *wp, *prev;
-       struct gf1_xwave xp;
-       int err;
-       gfp_t gfp_mask;
-       unsigned int real_size;
-       
-       gfp_mask = atomic ? GFP_ATOMIC : GFP_KERNEL;
-       if (*len < (long)sizeof(xp))
-               return -EINVAL;
-       if (copy_from_user(&xp, *data, sizeof(xp)))
-               return -EFAULT;
-       *data += sizeof(xp);
-       *len -= sizeof(xp);
-       wp = kzalloc(sizeof(*wp), gfp_mask);
-       if (wp == NULL)
-               return -ENOMEM;
-       wp->share_id[0] = le32_to_cpu(xp.share_id[0]);
-       wp->share_id[1] = le32_to_cpu(xp.share_id[1]);
-       wp->share_id[2] = le32_to_cpu(xp.share_id[2]);
-       wp->share_id[3] = le32_to_cpu(xp.share_id[3]);
-       wp->format = le32_to_cpu(xp.format);
-       wp->size = le32_to_cpu(xp.size);
-       wp->start = le32_to_cpu(xp.start);
-       wp->loop_start = le32_to_cpu(xp.loop_start);
-       wp->loop_end = le32_to_cpu(xp.loop_end);
-       wp->loop_repeat = le16_to_cpu(xp.loop_repeat);
-       wp->flags = xp.flags;
-       wp->sample_rate = le32_to_cpu(xp.sample_rate);
-       wp->low_frequency = le32_to_cpu(xp.low_frequency);
-       wp->high_frequency = le32_to_cpu(xp.high_frequency);
-       wp->root_frequency = le32_to_cpu(xp.root_frequency);
-       wp->tune = le16_to_cpu(xp.tune);
-       wp->balance = xp.balance;
-       memcpy(wp->envelope_rate, xp.envelope_rate, 6);
-       memcpy(wp->envelope_offset, xp.envelope_offset, 6);
-       wp->tremolo_sweep = xp.tremolo_sweep;
-       wp->tremolo_rate = xp.tremolo_rate;
-       wp->tremolo_depth = xp.tremolo_depth;
-       wp->vibrato_sweep = xp.vibrato_sweep;
-       wp->vibrato_rate = xp.vibrato_rate;
-       wp->vibrato_depth = xp.vibrato_depth;
-       wp->scale_frequency = le16_to_cpu(xp.scale_frequency);
-       wp->scale_factor = le16_to_cpu(xp.scale_factor);
-       real_size = snd_seq_gf1_size(wp->size, wp->format);
-       if ((long)real_size > *len) {
-               kfree(wp);
-               return -ENOMEM;
-       }
-       if (ops->put_sample) {
-               err = ops->put_sample(ops->private_data, wp,
-                                     *data, real_size, atomic);
-               if (err < 0) {
-                       kfree(wp);
-                       return err;
-               }
-       }
-       *data += real_size;
-       *len -= real_size;
-       prev = ip->wave;
-       if (prev) {
-               while (prev->next) prev = prev->next;
-               prev->next = wp;
-       } else {
-               ip->wave = wp;
-       }
-       return 0;
-}
-
-static void snd_seq_gf1_wave_free(struct snd_gf1_ops *ops,
-                                 struct gf1_wave *wave,
-                                 int atomic)
-{
-       if (ops->remove_sample)
-               ops->remove_sample(ops->private_data, wave, atomic);
-       kfree(wave);
-}
-
-static void snd_seq_gf1_instr_free(struct snd_gf1_ops *ops,
-                                  struct gf1_instrument *ip,
-                                  int atomic)
-{
-       struct gf1_wave *wave;
-       
-       while ((wave = ip->wave) != NULL) {
-               ip->wave = wave->next;
-               snd_seq_gf1_wave_free(ops, wave, atomic);
-       }
-}
-
-static int snd_seq_gf1_put(void *private_data, struct snd_seq_kinstr *instr,
-                          char __user *instr_data, long len, int atomic,
-                          int cmd)
-{
-       struct snd_gf1_ops *ops = private_data;
-       struct gf1_instrument *ip;
-       struct gf1_xinstrument ix;
-       int err;
-       gfp_t gfp_mask;
-
-       if (cmd != SNDRV_SEQ_INSTR_PUT_CMD_CREATE)
-               return -EINVAL;
-       gfp_mask = atomic ? GFP_ATOMIC : GFP_KERNEL;
-       /* copy instrument data */
-       if (len < (long)sizeof(ix))
-               return -EINVAL;
-       if (copy_from_user(&ix, instr_data, sizeof(ix)))
-               return -EFAULT;
-       if (ix.stype != GF1_STRU_INSTR)
-               return -EINVAL;
-       instr_data += sizeof(ix);
-       len -= sizeof(ix);
-       ip = (struct gf1_instrument *)KINSTR_DATA(instr);
-       ip->exclusion = le16_to_cpu(ix.exclusion);
-       ip->exclusion_group = le16_to_cpu(ix.exclusion_group);
-       ip->effect1 = ix.effect1;
-       ip->effect1_depth = ix.effect1_depth;
-       ip->effect2 = ix.effect2;
-       ip->effect2_depth = ix.effect2_depth;
-       /* copy layers */
-       while (len > (long)sizeof(__u32)) {
-               __u32 stype;
-
-               if (copy_from_user(&stype, instr_data, sizeof(stype)))
-                       return -EFAULT;
-               if (stype != GF1_STRU_WAVE) {
-                       snd_seq_gf1_instr_free(ops, ip, atomic);
-                       return -EINVAL;
-               }
-               err = snd_seq_gf1_copy_wave_from_stream(ops,
-                                                       ip,
-                                                       &instr_data,
-                                                       &len,
-                                                       atomic);
-               if (err < 0) {
-                       snd_seq_gf1_instr_free(ops, ip, atomic);
-                       return err;
-               }
-       }
-       return 0;
-}
-
-static int snd_seq_gf1_copy_wave_to_stream(struct snd_gf1_ops *ops,
-                                          struct gf1_instrument *ip,
-                                          char __user **data,
-                                          long *len,
-                                          int atomic)
-{
-       struct gf1_wave *wp;
-       struct gf1_xwave xp;
-       int err;
-       unsigned int real_size;
-       
-       for (wp = ip->wave; wp; wp = wp->next) {
-               if (*len < (long)sizeof(xp))
-                       return -ENOMEM;
-               memset(&xp, 0, sizeof(xp));
-               xp.stype = GF1_STRU_WAVE;
-               xp.share_id[0] = cpu_to_le32(wp->share_id[0]);
-               xp.share_id[1] = cpu_to_le32(wp->share_id[1]);
-               xp.share_id[2] = cpu_to_le32(wp->share_id[2]);
-               xp.share_id[3] = cpu_to_le32(wp->share_id[3]);
-               xp.format = cpu_to_le32(wp->format);
-               xp.size = cpu_to_le32(wp->size);
-               xp.start = cpu_to_le32(wp->start);
-               xp.loop_start = cpu_to_le32(wp->loop_start);
-               xp.loop_end = cpu_to_le32(wp->loop_end);
-               xp.loop_repeat = cpu_to_le32(wp->loop_repeat);
-               xp.flags = wp->flags;
-               xp.sample_rate = cpu_to_le32(wp->sample_rate);
-               xp.low_frequency = cpu_to_le32(wp->low_frequency);
-               xp.high_frequency = cpu_to_le32(wp->high_frequency);
-               xp.root_frequency = cpu_to_le32(wp->root_frequency);
-               xp.tune = cpu_to_le16(wp->tune);
-               xp.balance = wp->balance;
-               memcpy(xp.envelope_rate, wp->envelope_rate, 6);
-               memcpy(xp.envelope_offset, wp->envelope_offset, 6);
-               xp.tremolo_sweep = wp->tremolo_sweep;
-               xp.tremolo_rate = wp->tremolo_rate;
-               xp.tremolo_depth = wp->tremolo_depth;
-               xp.vibrato_sweep = wp->vibrato_sweep;
-               xp.vibrato_rate = wp->vibrato_rate;
-               xp.vibrato_depth = wp->vibrato_depth;
-               xp.scale_frequency = cpu_to_le16(wp->scale_frequency);
-               xp.scale_factor = cpu_to_le16(wp->scale_factor);
-               if (copy_to_user(*data, &xp, sizeof(xp)))
-                       return -EFAULT;
-               *data += sizeof(xp);
-               *len -= sizeof(xp);
-               real_size = snd_seq_gf1_size(wp->size, wp->format);
-               if (*len < (long)real_size)
-                       return -ENOMEM;
-               if (ops->get_sample) {
-                       err = ops->get_sample(ops->private_data, wp,
-                                             *data, real_size, atomic);
-                       if (err < 0)
-                               return err;
-               }
-               *data += wp->size;
-               *len -= wp->size;
-       }
-       return 0;
-}
-
-static int snd_seq_gf1_get(void *private_data, struct snd_seq_kinstr *instr,
-                          char __user *instr_data, long len, int atomic,
-                          int cmd)
-{
-       struct snd_gf1_ops *ops = private_data;
-       struct gf1_instrument *ip;
-       struct gf1_xinstrument ix;
-       
-       if (cmd != SNDRV_SEQ_INSTR_GET_CMD_FULL)
-               return -EINVAL;
-       if (len < (long)sizeof(ix))
-               return -ENOMEM;
-       memset(&ix, 0, sizeof(ix));
-       ip = (struct gf1_instrument *)KINSTR_DATA(instr);
-       ix.stype = GF1_STRU_INSTR;
-       ix.exclusion = cpu_to_le16(ip->exclusion);
-       ix.exclusion_group = cpu_to_le16(ip->exclusion_group);
-       ix.effect1 = cpu_to_le16(ip->effect1);
-       ix.effect1_depth = cpu_to_le16(ip->effect1_depth);
-       ix.effect2 = ip->effect2;
-       ix.effect2_depth = ip->effect2_depth;
-       if (copy_to_user(instr_data, &ix, sizeof(ix)))
-               return -EFAULT;
-       instr_data += sizeof(ix);
-       len -= sizeof(ix);
-       return snd_seq_gf1_copy_wave_to_stream(ops,
-                                              ip,
-                                              &instr_data,
-                                              &len,
-                                              atomic);
-}
-
-static int snd_seq_gf1_get_size(void *private_data, struct snd_seq_kinstr *instr,
-                               long *size)
-{
-       long result;
-       struct gf1_instrument *ip;
-       struct gf1_wave *wp;
-
-       *size = 0;
-       ip = (struct gf1_instrument *)KINSTR_DATA(instr);
-       result = sizeof(struct gf1_xinstrument);
-       for (wp = ip->wave; wp; wp = wp->next) {
-               result += sizeof(struct gf1_xwave);
-               result += wp->size;
-       }
-       *size = result;
-       return 0;
-}
-
-static int snd_seq_gf1_remove(void *private_data,
-                             struct snd_seq_kinstr *instr,
-                              int atomic)
-{
-       struct snd_gf1_ops *ops = private_data;
-       struct gf1_instrument *ip;
-
-       ip = (struct gf1_instrument *)KINSTR_DATA(instr);
-       snd_seq_gf1_instr_free(ops, ip, atomic);
-       return 0;
-}
-
-static void snd_seq_gf1_notify(void *private_data,
-                              struct snd_seq_kinstr *instr,
-                              int what)
-{
-       struct snd_gf1_ops *ops = private_data;
-
-       if (ops->notify)
-               ops->notify(ops->private_data, instr, what);
-}
-
-int snd_seq_gf1_init(struct snd_gf1_ops *ops,
-                    void *private_data,
-                    struct snd_seq_kinstr_ops *next)
-{
-       memset(ops, 0, sizeof(*ops));
-       ops->private_data = private_data;
-       ops->kops.private_data = ops;
-       ops->kops.add_len = sizeof(struct gf1_instrument);
-       ops->kops.instr_type = SNDRV_SEQ_INSTR_ID_GUS_PATCH;
-       ops->kops.put = snd_seq_gf1_put;
-       ops->kops.get = snd_seq_gf1_get;
-       ops->kops.get_size = snd_seq_gf1_get_size;
-       ops->kops.remove = snd_seq_gf1_remove;
-       ops->kops.notify = snd_seq_gf1_notify;
-       ops->kops.next = next;
-       return 0;
-}
-
-/*
- *  Init part
- */
-
-static int __init alsa_ainstr_gf1_init(void)
-{
-       return 0;
-}
-
-static void __exit alsa_ainstr_gf1_exit(void)
-{
-}
-
-module_init(alsa_ainstr_gf1_init)
-module_exit(alsa_ainstr_gf1_exit)
-
-EXPORT_SYMBOL(snd_seq_gf1_init);
diff --git a/sound/core/seq/instr/ainstr_iw.c b/sound/core/seq/instr/ainstr_iw.c
deleted file mode 100644 (file)
index 6c40eb7..0000000
+++ /dev/null
@@ -1,623 +0,0 @@
-/*
- *   IWFFFF - AMD InterWave (tm) - Instrument routines
- *   Copyright (c) 1999 by Jaroslav Kysela <perex@perex.cz>
- *
- *   This program is free software; you can redistribute it and/or modify
- *   it under the terms of the GNU General Public License as published by
- *   the Free Software Foundation; either version 2 of the License, or
- *   (at your option) any later version.
- *
- *   This program is distributed in the hope that it will be useful,
- *   but WITHOUT ANY WARRANTY; without even the implied warranty of
- *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- *   GNU General Public License for more details.
- *
- *   You should have received a copy of the GNU General Public License
- *   along with this program; if not, write to the Free Software
- *   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA
- *
- */
-#include <sound/driver.h>
-#include <linux/init.h>
-#include <linux/slab.h>
-#include <sound/core.h>
-#include <sound/ainstr_iw.h>
-#include <sound/initval.h>
-#include <asm/uaccess.h>
-
-MODULE_AUTHOR("Jaroslav Kysela <perex@perex.cz>");
-MODULE_DESCRIPTION("Advanced Linux Sound Architecture IWFFFF support.");
-MODULE_LICENSE("GPL");
-
-static unsigned int snd_seq_iwffff_size(unsigned int size, unsigned int format)
-{
-       unsigned int result = size;
-       
-       if (format & IWFFFF_WAVE_16BIT)
-               result <<= 1;
-       if (format & IWFFFF_WAVE_STEREO)
-               result <<= 1;
-       return result;
-}
-
-static void snd_seq_iwffff_copy_lfo_from_stream(struct iwffff_lfo *fp,
-                                               struct iwffff_xlfo *fx)
-{
-       fp->freq = le16_to_cpu(fx->freq);
-       fp->depth = le16_to_cpu(fx->depth);
-       fp->sweep = le16_to_cpu(fx->sweep);
-       fp->shape = fx->shape;
-       fp->delay = fx->delay;
-}
-
-static int snd_seq_iwffff_copy_env_from_stream(__u32 req_stype,
-                                              struct iwffff_layer *lp,
-                                              struct iwffff_env *ep,
-                                              struct iwffff_xenv *ex,
-                                              char __user **data,
-                                              long *len,
-                                              gfp_t gfp_mask)
-{
-       __u32 stype;
-       struct iwffff_env_record *rp, *rp_last;
-       struct iwffff_xenv_record rx;
-       struct iwffff_env_point *pp;
-       struct iwffff_xenv_point px;
-       int points_size, idx;
-
-       ep->flags = ex->flags;
-       ep->mode = ex->mode;
-       ep->index = ex->index;
-       rp_last = NULL;
-       while (1) {
-               if (*len < (long)sizeof(__u32))
-                       return -EINVAL;
-               if (copy_from_user(&stype, *data, sizeof(stype)))
-                       return -EFAULT;
-               if (stype == IWFFFF_STRU_WAVE)
-                       return 0;
-               if (req_stype != stype) {
-                       if (stype == IWFFFF_STRU_ENV_RECP ||
-                           stype == IWFFFF_STRU_ENV_RECV)
-                               return 0;
-               }
-               if (*len < (long)sizeof(rx))
-                       return -EINVAL;
-               if (copy_from_user(&rx, *data, sizeof(rx)))
-                       return -EFAULT;
-               *data += sizeof(rx);
-               *len -= sizeof(rx);
-               points_size = (le16_to_cpu(rx.nattack) + le16_to_cpu(rx.nrelease)) * 2 * sizeof(__u16);
-               if (points_size > *len)
-                       return -EINVAL;
-               rp = kzalloc(sizeof(*rp) + points_size, gfp_mask);
-               if (rp == NULL)
-                       return -ENOMEM;
-               rp->nattack = le16_to_cpu(rx.nattack);
-               rp->nrelease = le16_to_cpu(rx.nrelease);
-               rp->sustain_offset = le16_to_cpu(rx.sustain_offset);
-               rp->sustain_rate = le16_to_cpu(rx.sustain_rate);
-               rp->release_rate = le16_to_cpu(rx.release_rate);
-               rp->hirange = rx.hirange;
-               pp = (struct iwffff_env_point *)(rp + 1);
-               for (idx = 0; idx < rp->nattack + rp->nrelease; idx++) {
-                       if (copy_from_user(&px, *data, sizeof(px)))
-                               return -EFAULT;
-                       *data += sizeof(px);
-                       *len -= sizeof(px);
-                       pp->offset = le16_to_cpu(px.offset);
-                       pp->rate = le16_to_cpu(px.rate);
-               }
-               if (ep->record == NULL) {
-                       ep->record = rp;
-               } else {
-                       rp_last = rp;
-               }
-               rp_last = rp;
-       }
-       return 0;
-}
-
-static int snd_seq_iwffff_copy_wave_from_stream(struct snd_iwffff_ops *ops,
-                                               struct iwffff_layer *lp,
-                                               char __user **data,
-                                               long *len,
-                                               int atomic)
-{
-       struct iwffff_wave *wp, *prev;
-       struct iwffff_xwave xp;
-       int err;
-       gfp_t gfp_mask;
-       unsigned int real_size;
-       
-       gfp_mask = atomic ? GFP_ATOMIC : GFP_KERNEL;
-       if (*len < (long)sizeof(xp))
-               return -EINVAL;
-       if (copy_from_user(&xp, *data, sizeof(xp)))
-               return -EFAULT;
-       *data += sizeof(xp);
-       *len -= sizeof(xp);
-       wp = kzalloc(sizeof(*wp), gfp_mask);
-       if (wp == NULL)
-               return -ENOMEM;
-       wp->share_id[0] = le32_to_cpu(xp.share_id[0]);
-       wp->share_id[1] = le32_to_cpu(xp.share_id[1]);
-       wp->share_id[2] = le32_to_cpu(xp.share_id[2]);
-       wp->share_id[3] = le32_to_cpu(xp.share_id[3]);
-       wp->format = le32_to_cpu(xp.format);
-       wp->address.memory = le32_to_cpu(xp.offset);
-       wp->size = le32_to_cpu(xp.size);
-       wp->start = le32_to_cpu(xp.start);
-       wp->loop_start = le32_to_cpu(xp.loop_start);
-       wp->loop_end = le32_to_cpu(xp.loop_end);
-       wp->loop_repeat = le16_to_cpu(xp.loop_repeat);
-       wp->sample_ratio = le32_to_cpu(xp.sample_ratio);
-       wp->attenuation = xp.attenuation;
-       wp->low_note = xp.low_note;
-       wp->high_note = xp.high_note;
-       real_size = snd_seq_iwffff_size(wp->size, wp->format);
-       if (!(wp->format & IWFFFF_WAVE_ROM)) {
-               if ((long)real_size > *len) {
-                       kfree(wp);
-                       return -ENOMEM;
-               }
-       }
-       if (ops->put_sample) {
-               err = ops->put_sample(ops->private_data, wp,
-                                     *data, real_size, atomic);
-               if (err < 0) {
-                       kfree(wp);
-                       return err;
-               }
-       }
-       if (!(wp->format & IWFFFF_WAVE_ROM)) {
-               *data += real_size;
-               *len -= real_size;
-       }
-       prev = lp->wave;
-       if (prev) {
-               while (prev->next) prev = prev->next;
-               prev->next = wp;
-       } else {
-               lp->wave = wp;
-       }
-       return 0;
-}
-
-static void snd_seq_iwffff_env_free(struct snd_iwffff_ops *ops,
-                                   struct iwffff_env *env,
-                                   int atomic)
-{
-       struct iwffff_env_record *rec;
-       
-       while ((rec = env->record) != NULL) {
-               env->record = rec->next;
-               kfree(rec);
-       }
-}
-                                   
-static void snd_seq_iwffff_wave_free(struct snd_iwffff_ops *ops,
-                                    struct iwffff_wave *wave,
-                                    int atomic)
-{
-       if (ops->remove_sample)
-               ops->remove_sample(ops->private_data, wave, atomic);
-       kfree(wave);
-}
-
-static void snd_seq_iwffff_instr_free(struct snd_iwffff_ops *ops,
-                                      struct iwffff_instrument *ip,
-                                      int atomic)
-{
-       struct iwffff_layer *layer;
-       struct iwffff_wave *wave;
-       
-       while ((layer = ip->layer) != NULL) {
-               ip->layer = layer->next;
-               snd_seq_iwffff_env_free(ops, &layer->penv, atomic);
-               snd_seq_iwffff_env_free(ops, &layer->venv, atomic);
-               while ((wave = layer->wave) != NULL) {
-                       layer->wave = wave->next;
-                       snd_seq_iwffff_wave_free(ops, wave, atomic);
-               }
-               kfree(layer);
-       }
-}
-
-static int snd_seq_iwffff_put(void *private_data, struct snd_seq_kinstr *instr,
-                             char __user *instr_data, long len, int atomic,
-                             int cmd)
-{
-       struct snd_iwffff_ops *ops = private_data;
-       struct iwffff_instrument *ip;
-       struct iwffff_xinstrument ix;
-       struct iwffff_layer *lp, *prev_lp;
-       struct iwffff_xlayer lx;
-       int err;
-       gfp_t gfp_mask;
-
-       if (cmd != SNDRV_SEQ_INSTR_PUT_CMD_CREATE)
-               return -EINVAL;
-       gfp_mask = atomic ? GFP_ATOMIC : GFP_KERNEL;
-       /* copy instrument data */
-       if (len < (long)sizeof(ix))
-               return -EINVAL;
-       if (copy_from_user(&ix, instr_data, sizeof(ix)))
-               return -EFAULT;
-       if (ix.stype != IWFFFF_STRU_INSTR)
-               return -EINVAL;
-       instr_data += sizeof(ix);
-       len -= sizeof(ix);
-       ip = (struct iwffff_instrument *)KINSTR_DATA(instr);
-       ip->exclusion = le16_to_cpu(ix.exclusion);
-       ip->layer_type = le16_to_cpu(ix.layer_type);
-       ip->exclusion_group = le16_to_cpu(ix.exclusion_group);
-       ip->effect1 = ix.effect1;
-       ip->effect1_depth = ix.effect1_depth;
-       ip->effect2 = ix.effect2;
-       ip->effect2_depth = ix.effect2_depth;
-       /* copy layers */
-       prev_lp = NULL;
-       while (len > 0) {
-               if (len < (long)sizeof(struct iwffff_xlayer)) {
-                       snd_seq_iwffff_instr_free(ops, ip, atomic);
-                       return -EINVAL;
-               }
-               if (copy_from_user(&lx, instr_data, sizeof(lx)))
-                       return -EFAULT;
-               instr_data += sizeof(lx);
-               len -= sizeof(lx);
-               if (lx.stype != IWFFFF_STRU_LAYER) {
-                       snd_seq_iwffff_instr_free(ops, ip, atomic);
-                       return -EINVAL;
-               }
-               lp = kzalloc(sizeof(*lp), gfp_mask);
-               if (lp == NULL) {
-                       snd_seq_iwffff_instr_free(ops, ip, atomic);
-                       return -ENOMEM;
-               }
-               if (prev_lp) {
-                       prev_lp->next = lp;
-               } else {
-                       ip->layer = lp;
-               }
-               prev_lp = lp;
-               lp->flags = lx.flags;
-               lp->velocity_mode = lx.velocity_mode;
-               lp->layer_event = lx.layer_event;
-               lp->low_range = lx.low_range;
-               lp->high_range = lx.high_range;
-               lp->pan = lx.pan;
-               lp->pan_freq_scale = lx.pan_freq_scale;
-               lp->attenuation = lx.attenuation;
-               snd_seq_iwffff_copy_lfo_from_stream(&lp->tremolo, &lx.tremolo);
-               snd_seq_iwffff_copy_lfo_from_stream(&lp->vibrato, &lx.vibrato);
-               lp->freq_scale = le16_to_cpu(lx.freq_scale);
-               lp->freq_center = lx.freq_center;
-               err = snd_seq_iwffff_copy_env_from_stream(IWFFFF_STRU_ENV_RECP,
-                                                         lp,
-                                                         &lp->penv, &lx.penv,
-                                                         &instr_data, &len,
-                                                         gfp_mask);
-               if (err < 0) {
-                       snd_seq_iwffff_instr_free(ops, ip, atomic);
-                       return err;
-               }
-               err = snd_seq_iwffff_copy_env_from_stream(IWFFFF_STRU_ENV_RECV,
-                                                         lp,
-                                                         &lp->venv, &lx.venv,
-                                                         &instr_data, &len,
-                                                         gfp_mask);
-               if (err < 0) {
-                       snd_seq_iwffff_instr_free(ops, ip, atomic);
-                       return err;
-               }
-               while (len > (long)sizeof(__u32)) {
-                       __u32 stype;
-
-                       if (copy_from_user(&stype, instr_data, sizeof(stype)))
-                               return -EFAULT;
-                       if (stype != IWFFFF_STRU_WAVE)
-                               break;
-                       err = snd_seq_iwffff_copy_wave_from_stream(ops,
-                                                                  lp,
-                                                                  &instr_data,
-                                                                  &len,
-                                                                  atomic);
-                       if (err < 0) {
-                               snd_seq_iwffff_instr_free(ops, ip, atomic);
-                               return err;
-                       }
-               }
-       }
-       return 0;
-}
-
-static void snd_seq_iwffff_copy_lfo_to_stream(struct iwffff_xlfo *fx,
-                                             struct iwffff_lfo *fp)
-{
-       fx->freq = cpu_to_le16(fp->freq);
-       fx->depth = cpu_to_le16(fp->depth);
-       fx->sweep = cpu_to_le16(fp->sweep);
-       fp->shape = fx->shape;
-       fp->delay = fx->delay;
-}
-
-static int snd_seq_iwffff_copy_env_to_stream(__u32 req_stype,
-                                            struct iwffff_layer *lp,
-                                            struct iwffff_xenv *ex,
-                                            struct iwffff_env *ep,
-                                            char __user **data,
-                                            long *len)
-{
-       struct iwffff_env_record *rp;
-       struct iwffff_xenv_record rx;
-       struct iwffff_env_point *pp;
-       struct iwffff_xenv_point px;
-       int points_size, idx;
-
-       ex->flags = ep->flags;
-       ex->mode = ep->mode;
-       ex->index = ep->index;
-       for (rp = ep->record; rp; rp = rp->next) {
-               if (*len < (long)sizeof(rx))
-                       return -ENOMEM;
-               memset(&rx, 0, sizeof(rx));
-               rx.stype = req_stype;
-               rx.nattack = cpu_to_le16(rp->nattack);
-               rx.nrelease = cpu_to_le16(rp->nrelease);
-               rx.sustain_offset = cpu_to_le16(rp->sustain_offset);
-               rx.sustain_rate = cpu_to_le16(rp->sustain_rate);
-               rx.release_rate = cpu_to_le16(rp->release_rate);
-               rx.hirange = cpu_to_le16(rp->hirange);
-               if (copy_to_user(*data, &rx, sizeof(rx)))
-                       return -EFAULT;
-               *data += sizeof(rx);
-               *len -= sizeof(rx);
-               points_size = (rp->nattack + rp->nrelease) * 2 * sizeof(__u16);
-               if (*len < points_size)
-                       return -ENOMEM;
-               pp = (struct iwffff_env_point *)(rp + 1);
-               for (idx = 0; idx < rp->nattack + rp->nrelease; idx++) {
-                       px.offset = cpu_to_le16(pp->offset);
-                       px.rate = cpu_to_le16(pp->rate);
-                       if (copy_to_user(*data, &px, sizeof(px)))
-                               return -EFAULT;
-                       *data += sizeof(px);
-                       *len -= sizeof(px);
-               }
-       }
-       return 0;
-}
-
-static int snd_seq_iwffff_copy_wave_to_stream(struct snd_iwffff_ops *ops,
-                                             struct iwffff_layer *lp,
-                                             char __user **data,
-                                             long *len,
-                                             int atomic)
-{
-       struct iwffff_wave *wp;
-       struct iwffff_xwave xp;
-       int err;
-       unsigned int real_size;
-       
-       for (wp = lp->wave; wp; wp = wp->next) {
-               if (*len < (long)sizeof(xp))
-                       return -ENOMEM;
-               memset(&xp, 0, sizeof(xp));
-               xp.stype = IWFFFF_STRU_WAVE;
-               xp.share_id[0] = cpu_to_le32(wp->share_id[0]);
-               xp.share_id[1] = cpu_to_le32(wp->share_id[1]);
-               xp.share_id[2] = cpu_to_le32(wp->share_id[2]);
-               xp.share_id[3] = cpu_to_le32(wp->share_id[3]);
-               xp.format = cpu_to_le32(wp->format);
-               if (wp->format & IWFFFF_WAVE_ROM)
-                       xp.offset = cpu_to_le32(wp->address.memory);
-               xp.size = cpu_to_le32(wp->size);
-               xp.start = cpu_to_le32(wp->start);
-               xp.loop_start = cpu_to_le32(wp->loop_start);
-               xp.loop_end = cpu_to_le32(wp->loop_end);
-               xp.loop_repeat = cpu_to_le32(wp->loop_repeat);
-               xp.sample_ratio = cpu_to_le32(wp->sample_ratio);
-               xp.attenuation = wp->attenuation;
-               xp.low_note = wp->low_note;
-               xp.high_note = wp->high_note;
-               if (copy_to_user(*data, &xp, sizeof(xp)))
-                       return -EFAULT;
-               *data += sizeof(xp);
-               *len -= sizeof(xp);
-               real_size = snd_seq_iwffff_size(wp->size, wp->format);
-               if (!(wp->format & IWFFFF_WAVE_ROM)) {
-                       if (*len < (long)real_size)
-                               return -ENOMEM;
-               }
-               if (ops->get_sample) {
-                       err = ops->get_sample(ops->private_data, wp,
-                                             *data, real_size, atomic);
-                       if (err < 0)
-                               return err;
-               }
-               if (!(wp->format & IWFFFF_WAVE_ROM)) {
-                       *data += real_size;
-                       *len -= real_size;
-               }
-       }
-       return 0;
-}
-
-static int snd_seq_iwffff_get(void *private_data, struct snd_seq_kinstr *instr,
-                             char __user *instr_data, long len, int atomic, int cmd)
-{
-       struct snd_iwffff_ops *ops = private_data;
-       struct iwffff_instrument *ip;
-       struct iwffff_xinstrument ix;
-       struct iwffff_layer *lp;
-       struct iwffff_xlayer lx;
-       char __user *layer_instr_data;
-       int err;
-       
-       if (cmd != SNDRV_SEQ_INSTR_GET_CMD_FULL)
-               return -EINVAL;
-       if (len < (long)sizeof(ix))
-               return -ENOMEM;
-       memset(&ix, 0, sizeof(ix));
-       ip = (struct iwffff_instrument *)KINSTR_DATA(instr);
-       ix.stype = IWFFFF_STRU_INSTR;
-       ix.exclusion = cpu_to_le16(ip->exclusion);
-       ix.layer_type = cpu_to_le16(ip->layer_type);
-       ix.exclusion_group = cpu_to_le16(ip->exclusion_group);
-       ix.effect1 = cpu_to_le16(ip->effect1);
-       ix.effect1_depth = cpu_to_le16(ip->effect1_depth);
-       ix.effect2 = ip->effect2;
-       ix.effect2_depth = ip->effect2_depth;
-       if (copy_to_user(instr_data, &ix, sizeof(ix)))
-               return -EFAULT;
-       instr_data += sizeof(ix);
-       len -= sizeof(ix);
-       for (lp = ip->layer; lp; lp = lp->next) {
-               if (len < (long)sizeof(lx))
-                       return -ENOMEM;
-               memset(&lx, 0, sizeof(lx));
-               lx.stype = IWFFFF_STRU_LAYER;
-               lx.flags = lp->flags;
-               lx.velocity_mode = lp->velocity_mode;
-               lx.layer_event = lp->layer_event;
-               lx.low_range = lp->low_range;
-               lx.high_range = lp->high_range;
-               lx.pan = lp->pan;
-               lx.pan_freq_scale = lp->pan_freq_scale;
-               lx.attenuation = lp->attenuation;
-               snd_seq_iwffff_copy_lfo_to_stream(&lx.tremolo, &lp->tremolo);
-               snd_seq_iwffff_copy_lfo_to_stream(&lx.vibrato, &lp->vibrato);
-               layer_instr_data = instr_data;
-               instr_data += sizeof(lx);
-               len -= sizeof(lx);
-               err = snd_seq_iwffff_copy_env_to_stream(IWFFFF_STRU_ENV_RECP,
-                                                       lp,
-                                                       &lx.penv, &lp->penv,
-                                                       &instr_data, &len);
-               if (err < 0)
-                       return err;
-               err = snd_seq_iwffff_copy_env_to_stream(IWFFFF_STRU_ENV_RECV,
-                                                       lp,
-                                                       &lx.venv, &lp->venv,
-                                                       &instr_data, &len);
-               if (err < 0)
-                       return err;
-               /* layer structure updating is now finished */
-               if (copy_to_user(layer_instr_data, &lx, sizeof(lx)))
-                       return -EFAULT;
-               err = snd_seq_iwffff_copy_wave_to_stream(ops,
-                                                        lp,
-                                                        &instr_data,
-                                                        &len,
-                                                        atomic);
-               if (err < 0)
-                       return err;
-       }
-       return 0;
-}
-
-static long snd_seq_iwffff_env_size_in_stream(struct iwffff_env *ep)
-{
-       long result = 0;
-       struct iwffff_env_record *rp;
-
-       for (rp = ep->record; rp; rp = rp->next) {
-               result += sizeof(struct iwffff_xenv_record);
-               result += (rp->nattack + rp->nrelease) * 2 * sizeof(__u16);
-       }
-       return 0;
-}
-
-static long snd_seq_iwffff_wave_size_in_stream(struct iwffff_layer *lp)
-{
-       long result = 0;
-       struct iwffff_wave *wp;
-       
-       for (wp = lp->wave; wp; wp = wp->next) {
-               result += sizeof(struct iwffff_xwave);
-               if (!(wp->format & IWFFFF_WAVE_ROM))
-                       result += wp->size;
-       }
-       return result;
-}
-
-static int snd_seq_iwffff_get_size(void *private_data, struct snd_seq_kinstr *instr,
-                                  long *size)
-{
-       long result;
-       struct iwffff_instrument *ip;
-       struct iwffff_layer *lp;
-
-       *size = 0;
-       ip = (struct iwffff_instrument *)KINSTR_DATA(instr);
-       result = sizeof(struct iwffff_xinstrument);
-       for (lp = ip->layer; lp; lp = lp->next) {
-               result += sizeof(struct iwffff_xlayer);
-               result += snd_seq_iwffff_env_size_in_stream(&lp->penv);
-               result += snd_seq_iwffff_env_size_in_stream(&lp->venv);
-               result += snd_seq_iwffff_wave_size_in_stream(lp);
-       }
-       *size = result;
-       return 0;
-}
-
-static int snd_seq_iwffff_remove(void *private_data,
-                                struct snd_seq_kinstr *instr,
-                                 int atomic)
-{
-       struct snd_iwffff_ops *ops = private_data;
-       struct iwffff_instrument *ip;
-
-       ip = (struct iwffff_instrument *)KINSTR_DATA(instr);
-       snd_seq_iwffff_instr_free(ops, ip, atomic);
-       return 0;
-}
-
-static void snd_seq_iwffff_notify(void *private_data,
-                                 struct snd_seq_kinstr *instr,
-                                  int what)
-{
-       struct snd_iwffff_ops *ops = private_data;
-
-       if (ops->notify)
-               ops->notify(ops->private_data, instr, what);
-}
-
-int snd_seq_iwffff_init(struct snd_iwffff_ops *ops,
-                       void *private_data,
-                       struct snd_seq_kinstr_ops *next)
-{
-       memset(ops, 0, sizeof(*ops));
-       ops->private_data = private_data;
-       ops->kops.private_data = ops;
-       ops->kops.add_len = sizeof(struct iwffff_instrument);
-       ops->kops.instr_type = SNDRV_SEQ_INSTR_ID_INTERWAVE;
-       ops->kops.put = snd_seq_iwffff_put;
-       ops->kops.get = snd_seq_iwffff_get;
-       ops->kops.get_size = snd_seq_iwffff_get_size;
-       ops->kops.remove = snd_seq_iwffff_remove;
-       ops->kops.notify = snd_seq_iwffff_notify;
-       ops->kops.next = next;
-       return 0;
-}
-
-/*
- *  Init part
- */
-
-static int __init alsa_ainstr_iw_init(void)
-{
-       return 0;
-}
-
-static void __exit alsa_ainstr_iw_exit(void)
-{
-}
-
-module_init(alsa_ainstr_iw_init)
-module_exit(alsa_ainstr_iw_exit)
-
-EXPORT_SYMBOL(snd_seq_iwffff_init);
diff --git a/sound/core/seq/instr/ainstr_simple.c b/sound/core/seq/instr/ainstr_simple.c
deleted file mode 100644 (file)
index 78f68be..0000000
+++ /dev/null
@@ -1,215 +0,0 @@
-/*
- *   Simple (MOD player) - Instrument routines
- *   Copyright (c) 1999 by Jaroslav Kysela <perex@perex.cz>
- *
- *   This program is free software; you can redistribute it and/or modify
- *   it under the terms of the GNU General Public License as published by
- *   the Free Software Foundation; either version 2 of the License, or
- *   (at your option) any later version.
- *
- *   This program is distributed in the hope that it will be useful,
- *   but WITHOUT ANY WARRANTY; without even the implied warranty of
- *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- *   GNU General Public License for more details.
- *
- *   You should have received a copy of the GNU General Public License
- *   along with this program; if not, write to the Free Software
- *   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA
- *
- */
-#include <sound/driver.h>
-#include <linux/init.h>
-#include <linux/slab.h>
-#include <sound/core.h>
-#include <sound/ainstr_simple.h>
-#include <sound/initval.h>
-#include <asm/uaccess.h>
-
-MODULE_AUTHOR("Jaroslav Kysela <perex@perex.cz>");
-MODULE_DESCRIPTION("Advanced Linux Sound Architecture Simple Instrument support.");
-MODULE_LICENSE("GPL");
-
-static unsigned int snd_seq_simple_size(unsigned int size, unsigned int format)
-{
-       unsigned int result = size;
-       
-       if (format & SIMPLE_WAVE_16BIT)
-               result <<= 1;
-       if (format & SIMPLE_WAVE_STEREO)
-               result <<= 1;
-       return result;
-}
-
-static void snd_seq_simple_instr_free(struct snd_simple_ops *ops,
-                                     struct simple_instrument *ip,
-                                     int atomic)
-{
-       if (ops->remove_sample)
-               ops->remove_sample(ops->private_data, ip, atomic);
-}
-
-static int snd_seq_simple_put(void *private_data, struct snd_seq_kinstr *instr,
-                             char __user *instr_data, long len,
-                             int atomic, int cmd)
-{
-       struct snd_simple_ops *ops = private_data;
-       struct simple_instrument *ip;
-       struct simple_xinstrument ix;
-       int err;
-       gfp_t gfp_mask;
-       unsigned int real_size;
-
-       if (cmd != SNDRV_SEQ_INSTR_PUT_CMD_CREATE)
-               return -EINVAL;
-       gfp_mask = atomic ? GFP_ATOMIC : GFP_KERNEL;
-       /* copy instrument data */
-       if (len < (long)sizeof(ix))
-               return -EINVAL;
-       if (copy_from_user(&ix, instr_data, sizeof(ix)))
-               return -EFAULT;
-       if (ix.stype != SIMPLE_STRU_INSTR)
-               return -EINVAL;
-       instr_data += sizeof(ix);
-       len -= sizeof(ix);
-       ip = (struct simple_instrument *)KINSTR_DATA(instr);
-       ip->share_id[0] = le32_to_cpu(ix.share_id[0]);
-       ip->share_id[1] = le32_to_cpu(ix.share_id[1]);
-       ip->share_id[2] = le32_to_cpu(ix.share_id[2]);
-       ip->share_id[3] = le32_to_cpu(ix.share_id[3]);
-       ip->format = le32_to_cpu(ix.format);
-       ip->size = le32_to_cpu(ix.size);
-       ip->start = le32_to_cpu(ix.start);
-       ip->loop_start = le32_to_cpu(ix.loop_start);
-       ip->loop_end = le32_to_cpu(ix.loop_end);
-       ip->loop_repeat = le16_to_cpu(ix.loop_repeat);
-       ip->effect1 = ix.effect1;
-       ip->effect1_depth = ix.effect1_depth;
-       ip->effect2 = ix.effect2;
-       ip->effect2_depth = ix.effect2_depth;
-       real_size = snd_seq_simple_size(ip->size, ip->format);
-       if (len < (long)real_size)
-               return -EINVAL;
-       if (ops->put_sample) {
-               err = ops->put_sample(ops->private_data, ip,
-                                     instr_data, real_size, atomic);
-               if (err < 0)
-                       return err;
-       }
-       return 0;
-}
-
-static int snd_seq_simple_get(void *private_data, struct snd_seq_kinstr *instr,
-                             char __user *instr_data, long len,
-                             int atomic, int cmd)
-{
-       struct snd_simple_ops *ops = private_data;
-       struct simple_instrument *ip;
-       struct simple_xinstrument ix;
-       int err;
-       unsigned int real_size;
-       
-       if (cmd != SNDRV_SEQ_INSTR_GET_CMD_FULL)
-               return -EINVAL;
-       if (len < (long)sizeof(ix))
-               return -ENOMEM;
-       memset(&ix, 0, sizeof(ix));
-       ip = (struct simple_instrument *)KINSTR_DATA(instr);
-       ix.stype = SIMPLE_STRU_INSTR;
-       ix.share_id[0] = cpu_to_le32(ip->share_id[0]);
-       ix.share_id[1] = cpu_to_le32(ip->share_id[1]);
-       ix.share_id[2] = cpu_to_le32(ip->share_id[2]);
-       ix.share_id[3] = cpu_to_le32(ip->share_id[3]);
-       ix.format = cpu_to_le32(ip->format);
-       ix.size = cpu_to_le32(ip->size);
-       ix.start = cpu_to_le32(ip->start);
-       ix.loop_start = cpu_to_le32(ip->loop_start);
-       ix.loop_end = cpu_to_le32(ip->loop_end);
-       ix.loop_repeat = cpu_to_le32(ip->loop_repeat);
-       ix.effect1 = cpu_to_le16(ip->effect1);
-       ix.effect1_depth = cpu_to_le16(ip->effect1_depth);
-       ix.effect2 = ip->effect2;
-       ix.effect2_depth = ip->effect2_depth;
-       if (copy_to_user(instr_data, &ix, sizeof(ix)))
-               return -EFAULT;
-       instr_data += sizeof(ix);
-       len -= sizeof(ix);
-       real_size = snd_seq_simple_size(ip->size, ip->format);
-       if (len < (long)real_size)
-               return -ENOMEM;
-       if (ops->get_sample) {
-               err = ops->get_sample(ops->private_data, ip,
-                                     instr_data, real_size, atomic);
-               if (err < 0)
-                       return err;
-       }
-       return 0;
-}
-
-static int snd_seq_simple_get_size(void *private_data, struct snd_seq_kinstr *instr,
-                                  long *size)
-{
-       struct simple_instrument *ip;
-
-       ip = (struct simple_instrument *)KINSTR_DATA(instr);
-       *size = sizeof(struct simple_xinstrument) + snd_seq_simple_size(ip->size, ip->format);
-       return 0;
-}
-
-static int snd_seq_simple_remove(void *private_data,
-                                struct snd_seq_kinstr *instr,
-                                 int atomic)
-{
-       struct snd_simple_ops *ops = private_data;
-       struct simple_instrument *ip;
-
-       ip = (struct simple_instrument *)KINSTR_DATA(instr);
-       snd_seq_simple_instr_free(ops, ip, atomic);
-       return 0;
-}
-
-static void snd_seq_simple_notify(void *private_data,
-                                 struct snd_seq_kinstr *instr,
-                                  int what)
-{
-       struct snd_simple_ops *ops = private_data;
-
-       if (ops->notify)
-               ops->notify(ops->private_data, instr, what);
-}
-
-int snd_seq_simple_init(struct snd_simple_ops *ops,
-                       void *private_data,
-                       struct snd_seq_kinstr_ops *next)
-{
-       memset(ops, 0, sizeof(*ops));
-       ops->private_data = private_data;
-       ops->kops.private_data = ops;
-       ops->kops.add_len = sizeof(struct simple_instrument);
-       ops->kops.instr_type = SNDRV_SEQ_INSTR_ID_SIMPLE;
-       ops->kops.put = snd_seq_simple_put;
-       ops->kops.get = snd_seq_simple_get;
-       ops->kops.get_size = snd_seq_simple_get_size;
-       ops->kops.remove = snd_seq_simple_remove;
-       ops->kops.notify = snd_seq_simple_notify;
-       ops->kops.next = next;
-       return 0;
-}
-
-/*
- *  Init part
- */
-
-static int __init alsa_ainstr_simple_init(void)
-{
-       return 0;
-}
-
-static void __exit alsa_ainstr_simple_exit(void)
-{
-}
-
-module_init(alsa_ainstr_simple_init)
-module_exit(alsa_ainstr_simple_exit)
-
-EXPORT_SYMBOL(snd_seq_simple_init);
index bc0992398461bbf40d0cf3da9f8d99f4feabb27f..777796e9449047c2ec2b0ec851638cac9733fd7e 100644 (file)
@@ -20,7 +20,6 @@
  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA
  */
 
-#include <sound/driver.h>
 #include <linux/init.h>
 #include <linux/moduleparam.h>
 #include <linux/mutex.h>
index 9a8567c928ec4bc262061b1314d73f4e0869373d..bf8d2b4cb15e8e403a10f94eb15bc4ee67d251c8 100644 (file)
@@ -21,7 +21,6 @@
 #ifndef __SEQ_OSS_DEVICE_H
 #define __SEQ_OSS_DEVICE_H
 
-#include <sound/driver.h>
 #include <linux/time.h>
 #include <linux/wait.h>
 #include <linux/slab.h>
index 1878208a802665a2b26b1233b3a31c497922a2c4..ee0f8405ab355ee48c3c36fbea255330c029d541 100644 (file)
@@ -19,7 +19,6 @@
  *
  */
 
-#include <sound/driver.h>
 #include <linux/init.h>
 #include <linux/moduleparam.h>
 #include <sound/core.h>
index 2e3fa25ab19ffe8208ba17a1d06023bbf8d0427a..f97c1ba43a2820c5ae956442f554a9cc7aa9b4be 100644 (file)
@@ -21,7 +21,6 @@
  *
  */
 
-#include <sound/driver.h>
 #include <linux/init.h>
 #include <linux/slab.h>
 #include <sound/core.h>
@@ -130,8 +129,6 @@ static struct snd_seq_client *clientptr(int clientid)
        return clienttab[clientid];
 }
 
-extern int seq_client_load[];
-
 struct snd_seq_client *snd_seq_client_use_ptr(int clientid)
 {
        unsigned long flags;
@@ -966,8 +963,7 @@ static int check_event_type_and_length(struct snd_seq_event *ev)
                        return -EINVAL;
                break;
        case SNDRV_SEQ_EVENT_LENGTH_VARUSR:
-               if (! snd_seq_ev_is_instr_type(ev) ||
-                   ! snd_seq_ev_is_direct(ev))
+               if (! snd_seq_ev_is_direct(ev))
                        return -EINVAL;
                break;
        }
index 5e04e20e239f4f22fafae0e270fb2f36e7c629fa..20f0a725ec7db293a1edc18c83af60fe3ab11d34 100644 (file)
@@ -98,4 +98,6 @@ int snd_seq_kernel_client_write_poll(int clientid, struct file *file, poll_table
 int snd_seq_client_notify_subscription(int client, int port,
                                       struct snd_seq_port_subscribe *info, int evtype);
 
+extern int seq_client_load[15];
+
 #endif
index 37852cdace76091c83a4c33b063c1a60f5a0959c..155dc7da47222d5f5c0ab954a24bb80e85625a25 100644 (file)
@@ -36,7 +36,6 @@
  *
  */
 
-#include <sound/driver.h>
 #include <linux/init.h>
 #include <sound/core.h>
 #include <sound/info.h>
index e55488d1237cebf9435cbafa03d5b5e93364ff08..f3bdc54b429a91d99549cbb3cf510e04e30e8295 100644 (file)
@@ -18,7 +18,6 @@
  *
  */
 
-#include <sound/driver.h>
 #include <linux/init.h>
 #include <linux/slab.h>
 #include <linux/moduleparam.h>
index 6b055aed7a4b232fe6ee7b835b84a7bd6d55aac4..3a94ed021bd96e0ef177284f8c8a9113dbfbb0c2 100644 (file)
@@ -19,7 +19,6 @@
  *
  */
 
-#include <sound/driver.h>
 #include <sound/core.h>
 #include <linux/slab.h>
 #include "seq_fifo.h"
index 8a7fe5cca1c90022fa765fd025108c2b37331532..201f8106ffdd9b36224a9c798997457db65b92cc 100644 (file)
@@ -19,7 +19,6 @@
  *
  */
 
-#include <sound/driver.h>
 #include <linux/init.h>
 #include <sound/core.h>
 
diff --git a/sound/core/seq/seq_instr.c b/sound/core/seq/seq_instr.c
deleted file mode 100644 (file)
index 9a6fd56..0000000
+++ /dev/null
@@ -1,655 +0,0 @@
-/*
- *   Generic Instrument routines for ALSA sequencer
- *   Copyright (c) 1999 by Jaroslav Kysela <perex@perex.cz>
- *
- *   This program is free software; you can redistribute it and/or modify
- *   it under the terms of the GNU General Public License as published by
- *   the Free Software Foundation; either version 2 of the License, or
- *   (at your option) any later version.
- *
- *   This program is distributed in the hope that it will be useful,
- *   but WITHOUT ANY WARRANTY; without even the implied warranty of
- *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- *   GNU General Public License for more details.
- *
- *   You should have received a copy of the GNU General Public License
- *   along with this program; if not, write to the Free Software
- *   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA
- *
- */
-#include <sound/driver.h>
-#include <linux/init.h>
-#include <linux/slab.h>
-#include <sound/core.h>
-#include "seq_clientmgr.h"
-#include <sound/seq_instr.h>
-#include <sound/initval.h>
-
-MODULE_AUTHOR("Jaroslav Kysela <perex@perex.cz>");
-MODULE_DESCRIPTION("Advanced Linux Sound Architecture sequencer instrument library.");
-MODULE_LICENSE("GPL");
-
-
-static void snd_instr_lock_ops(struct snd_seq_kinstr_list *list)
-{
-       if (!(list->flags & SNDRV_SEQ_INSTR_FLG_DIRECT)) {
-               spin_lock_irqsave(&list->ops_lock, list->ops_flags);
-       } else {
-               mutex_lock(&list->ops_mutex);
-       }
-}
-
-static void snd_instr_unlock_ops(struct snd_seq_kinstr_list *list)
-{
-       if (!(list->flags & SNDRV_SEQ_INSTR_FLG_DIRECT)) {
-               spin_unlock_irqrestore(&list->ops_lock, list->ops_flags);
-       } else {
-               mutex_unlock(&list->ops_mutex);
-       }
-}
-
-static struct snd_seq_kinstr *snd_seq_instr_new(int add_len, int atomic)
-{
-       struct snd_seq_kinstr *instr;
-       
-       instr = kzalloc(sizeof(struct snd_seq_kinstr) + add_len, atomic ? GFP_ATOMIC : GFP_KERNEL);
-       if (instr == NULL)
-               return NULL;
-       instr->add_len = add_len;
-       return instr;
-}
-
-static int snd_seq_instr_free(struct snd_seq_kinstr *instr, int atomic)
-{
-       int result = 0;
-
-       if (instr == NULL)
-               return -EINVAL;
-       if (instr->ops && instr->ops->remove)
-               result = instr->ops->remove(instr->ops->private_data, instr, 1);
-       if (!result)
-               kfree(instr);
-       return result;
-}
-
-struct snd_seq_kinstr_list *snd_seq_instr_list_new(void)
-{
-       struct snd_seq_kinstr_list *list;
-
-       list = kzalloc(sizeof(struct snd_seq_kinstr_list), GFP_KERNEL);
-       if (list == NULL)
-               return NULL;
-       spin_lock_init(&list->lock);
-       spin_lock_init(&list->ops_lock);
-       mutex_init(&list->ops_mutex);
-       list->owner = -1;
-       return list;
-}
-
-void snd_seq_instr_list_free(struct snd_seq_kinstr_list **list_ptr)
-{
-       struct snd_seq_kinstr_list *list;
-       struct snd_seq_kinstr *instr;
-       struct snd_seq_kcluster *cluster;
-       int idx;
-       unsigned long flags;
-
-       if (list_ptr == NULL)
-               return;
-       list = *list_ptr;
-       *list_ptr = NULL;
-       if (list == NULL)
-               return;
-       
-       for (idx = 0; idx < SNDRV_SEQ_INSTR_HASH_SIZE; idx++) {         
-               while ((instr = list->hash[idx]) != NULL) {
-                       list->hash[idx] = instr->next;
-                       list->count--;
-                       spin_lock_irqsave(&list->lock, flags);
-                       while (instr->use) {
-                               spin_unlock_irqrestore(&list->lock, flags);
-                               schedule_timeout_uninterruptible(1);
-                               spin_lock_irqsave(&list->lock, flags);
-                       }                               
-                       spin_unlock_irqrestore(&list->lock, flags);
-                       if (snd_seq_instr_free(instr, 0)<0)
-                               snd_printk(KERN_WARNING "instrument free problem\n");
-               }
-               while ((cluster = list->chash[idx]) != NULL) {
-                       list->chash[idx] = cluster->next;
-                       list->ccount--;
-                       kfree(cluster);
-               }
-       }
-       kfree(list);
-}
-
-static int instr_free_compare(struct snd_seq_kinstr *instr,
-                             struct snd_seq_instr_header *ifree,
-                             unsigned int client)
-{
-       switch (ifree->cmd) {
-       case SNDRV_SEQ_INSTR_FREE_CMD_ALL:
-               /* all, except private for other clients */
-               if ((instr->instr.std & 0xff000000) == 0)
-                       return 0;
-               if (((instr->instr.std >> 24) & 0xff) == client)
-                       return 0;
-               return 1;
-       case SNDRV_SEQ_INSTR_FREE_CMD_PRIVATE:
-               /* all my private instruments */
-               if ((instr->instr.std & 0xff000000) == 0)
-                       return 1;
-               if (((instr->instr.std >> 24) & 0xff) == client)
-                       return 0;
-               return 1;
-       case SNDRV_SEQ_INSTR_FREE_CMD_CLUSTER:
-               /* all my private instruments */
-               if ((instr->instr.std & 0xff000000) == 0) {
-                       if (instr->instr.cluster == ifree->id.cluster)
-                               return 0;
-                       return 1;
-               }
-               if (((instr->instr.std >> 24) & 0xff) == client) {
-                       if (instr->instr.cluster == ifree->id.cluster)
-                               return 0;
-               }
-               return 1;
-       }
-       return 1;
-}
-
-int snd_seq_instr_list_free_cond(struct snd_seq_kinstr_list *list,
-                                struct snd_seq_instr_header *ifree,
-                                int client,
-                                int atomic)
-{
-       struct snd_seq_kinstr *instr, *prev, *next, *flist;
-       int idx;
-       unsigned long flags;
-
-       snd_instr_lock_ops(list);
-       for (idx = 0; idx < SNDRV_SEQ_INSTR_HASH_SIZE; idx++) {
-               spin_lock_irqsave(&list->lock, flags);
-               instr = list->hash[idx];
-               prev = flist = NULL;
-               while (instr) {
-                       while (instr && instr_free_compare(instr, ifree, (unsigned int)client)) {
-                               prev = instr;
-                               instr = instr->next;
-                       }
-                       if (instr == NULL)
-                               continue;
-                       if (instr->ops && instr->ops->notify)
-                               instr->ops->notify(instr->ops->private_data, instr, SNDRV_SEQ_INSTR_NOTIFY_REMOVE);
-                       next = instr->next;
-                       if (prev == NULL) {
-                               list->hash[idx] = next;
-                       } else {
-                               prev->next = next;
-                       }
-                       list->count--;
-                       instr->next = flist;
-                       flist = instr;
-                       instr = next;
-               }
-               spin_unlock_irqrestore(&list->lock, flags);
-               while (flist) {
-                       instr = flist;
-                       flist = instr->next;
-                       while (instr->use) {
-                               schedule_timeout_uninterruptible(1);
-                               barrier();
-                       }
-                       if (snd_seq_instr_free(instr, atomic)<0)
-                               snd_printk(KERN_WARNING "instrument free problem\n");
-                       instr = next;
-               }
-       }
-       snd_instr_unlock_ops(list);
-       return 0;       
-}
-
-static int compute_hash_instr_key(struct snd_seq_instr *instr)
-{
-       int result;
-       
-       result = instr->bank | (instr->prg << 16);
-       result += result >> 24;
-       result += result >> 16;
-       result += result >> 8;
-       return result & (SNDRV_SEQ_INSTR_HASH_SIZE-1);
-}
-
-#if 0
-static int compute_hash_cluster_key(snd_seq_instr_cluster_t cluster)
-{
-       int result;
-       
-       result = cluster;
-       result += result >> 24;
-       result += result >> 16;
-       result += result >> 8;
-       return result & (SNDRV_SEQ_INSTR_HASH_SIZE-1);
-}
-#endif
-
-static int compare_instr(struct snd_seq_instr *i1, struct snd_seq_instr *i2, int exact)
-{
-       if (exact) {
-               if (i1->cluster != i2->cluster ||
-                   i1->bank != i2->bank ||
-                   i1->prg != i2->prg)
-                       return 1;
-               if ((i1->std & 0xff000000) != (i2->std & 0xff000000))
-                       return 1;
-               if (!(i1->std & i2->std))
-                       return 1;
-               return 0;
-       } else {
-               unsigned int client_check;
-               
-               if (i2->cluster && i1->cluster != i2->cluster)
-                       return 1;
-               client_check = i2->std & 0xff000000;
-               if (client_check) {
-                       if ((i1->std & 0xff000000) != client_check)
-                               return 1;
-               } else {
-                       if ((i1->std & i2->std) != i2->std)
-                               return 1;
-               }
-               return i1->bank != i2->bank || i1->prg != i2->prg;
-       }
-}
-
-struct snd_seq_kinstr *snd_seq_instr_find(struct snd_seq_kinstr_list *list,
-                                         struct snd_seq_instr *instr,
-                                         int exact,
-                                         int follow_alias)
-{
-       unsigned long flags;
-       int depth = 0;
-       struct snd_seq_kinstr *result;
-
-       if (list == NULL || instr == NULL)
-               return NULL;
-       spin_lock_irqsave(&list->lock, flags);
-      __again:
-       result = list->hash[compute_hash_instr_key(instr)];
-       while (result) {
-               if (!compare_instr(&result->instr, instr, exact)) {
-                       if (follow_alias && (result->type == SNDRV_SEQ_INSTR_ATYPE_ALIAS)) {
-                               instr = (struct snd_seq_instr *)KINSTR_DATA(result);
-                               if (++depth > 10)
-                                       goto __not_found;
-                               goto __again;
-                       }
-                       result->use++;
-                       spin_unlock_irqrestore(&list->lock, flags);
-                       return result;
-               }
-               result = result->next;
-       }
-      __not_found:
-       spin_unlock_irqrestore(&list->lock, flags);
-       return NULL;
-}
-
-void snd_seq_instr_free_use(struct snd_seq_kinstr_list *list,
-                           struct snd_seq_kinstr *instr)
-{
-       unsigned long flags;
-
-       if (list == NULL || instr == NULL)
-               return;
-       spin_lock_irqsave(&list->lock, flags);
-       if (instr->use <= 0) {
-               snd_printk(KERN_ERR "free_use: fatal!!! use = %i, name = '%s'\n", instr->use, instr->name);
-       } else {
-               instr->use--;
-       }
-       spin_unlock_irqrestore(&list->lock, flags);
-}
-
-static struct snd_seq_kinstr_ops *instr_ops(struct snd_seq_kinstr_ops *ops,
-                                           char *instr_type)
-{
-       while (ops) {
-               if (!strcmp(ops->instr_type, instr_type))
-                       return ops;
-               ops = ops->next;
-       }
-       return NULL;
-}
-
-static int instr_result(struct snd_seq_event *ev,
-                       int type, int result,
-                       int atomic)
-{
-       struct snd_seq_event sev;
-       
-       memset(&sev, 0, sizeof(sev));
-       sev.type = SNDRV_SEQ_EVENT_RESULT;
-       sev.flags = SNDRV_SEQ_TIME_STAMP_REAL | SNDRV_SEQ_EVENT_LENGTH_FIXED |
-                   SNDRV_SEQ_PRIORITY_NORMAL;
-       sev.source = ev->dest;
-       sev.dest = ev->source;
-       sev.data.result.event = type;
-       sev.data.result.result = result;
-#if 0
-       printk("instr result - type = %i, result = %i, queue = %i, source.client:port = %i:%i, dest.client:port = %i:%i\n",
-                               type, result,
-                               sev.queue,
-                               sev.source.client, sev.source.port,
-                               sev.dest.client, sev.dest.port);
-#endif
-       return snd_seq_kernel_client_dispatch(sev.source.client, &sev, atomic, 0);
-}
-
-static int instr_begin(struct snd_seq_kinstr_ops *ops,
-                      struct snd_seq_kinstr_list *list,
-                      struct snd_seq_event *ev,
-                      int atomic, int hop)
-{
-       unsigned long flags;
-
-       spin_lock_irqsave(&list->lock, flags);
-       if (list->owner >= 0 && list->owner != ev->source.client) {
-               spin_unlock_irqrestore(&list->lock, flags);
-               return instr_result(ev, SNDRV_SEQ_EVENT_INSTR_BEGIN, -EBUSY, atomic);
-       }
-       list->owner = ev->source.client;
-       spin_unlock_irqrestore(&list->lock, flags);
-       return instr_result(ev, SNDRV_SEQ_EVENT_INSTR_BEGIN, 0, atomic);
-}
-
-static int instr_end(struct snd_seq_kinstr_ops *ops,
-                    struct snd_seq_kinstr_list *list,
-                    struct snd_seq_event *ev,
-                    int atomic, int hop)
-{
-       unsigned long flags;
-
-       /* TODO: timeout handling */
-       spin_lock_irqsave(&list->lock, flags);
-       if (list->owner == ev->source.client) {
-               list->owner = -1;
-               spin_unlock_irqrestore(&list->lock, flags);
-               return instr_result(ev, SNDRV_SEQ_EVENT_INSTR_END, 0, atomic);
-       }
-       spin_unlock_irqrestore(&list->lock, flags);
-       return instr_result(ev, SNDRV_SEQ_EVENT_INSTR_END, -EINVAL, atomic);
-}
-
-static int instr_info(struct snd_seq_kinstr_ops *ops,
-                     struct snd_seq_kinstr_list *list,
-                     struct snd_seq_event *ev,
-                     int atomic, int hop)
-{
-       return -ENXIO;
-}
-
-static int instr_format_info(struct snd_seq_kinstr_ops *ops,
-                            struct snd_seq_kinstr_list *list,
-                            struct snd_seq_event *ev,
-                            int atomic, int hop)
-{
-       return -ENXIO;
-}
-
-static int instr_reset(struct snd_seq_kinstr_ops *ops,
-                      struct snd_seq_kinstr_list *list,
-                      struct snd_seq_event *ev,
-                      int atomic, int hop)
-{
-       return -ENXIO;
-}
-
-static int instr_status(struct snd_seq_kinstr_ops *ops,
-                       struct snd_seq_kinstr_list *list,
-                       struct snd_seq_event *ev,
-                       int atomic, int hop)
-{
-       return -ENXIO;
-}
-
-static int instr_put(struct snd_seq_kinstr_ops *ops,
-                    struct snd_seq_kinstr_list *list,
-                    struct snd_seq_event *ev,
-                    int atomic, int hop)
-{
-       unsigned long flags;
-       struct snd_seq_instr_header put;
-       struct snd_seq_kinstr *instr;
-       int result = -EINVAL, len, key;
-
-       if ((ev->flags & SNDRV_SEQ_EVENT_LENGTH_MASK) != SNDRV_SEQ_EVENT_LENGTH_VARUSR)
-               goto __return;
-
-       if (ev->data.ext.len < sizeof(struct snd_seq_instr_header))
-               goto __return;
-       if (copy_from_user(&put, (void __user *)ev->data.ext.ptr,
-                          sizeof(struct snd_seq_instr_header))) {
-               result = -EFAULT;
-               goto __return;
-       }
-       snd_instr_lock_ops(list);
-       if (put.id.instr.std & 0xff000000) {    /* private instrument */
-               put.id.instr.std &= 0x00ffffff;
-               put.id.instr.std |= (unsigned int)ev->source.client << 24;
-       }
-       if ((instr = snd_seq_instr_find(list, &put.id.instr, 1, 0))) {
-               snd_seq_instr_free_use(list, instr);
-               snd_instr_unlock_ops(list);
-               result = -EBUSY;
-               goto __return;
-       }
-       ops = instr_ops(ops, put.data.data.format);
-       if (ops == NULL) {
-               snd_instr_unlock_ops(list);
-               goto __return;
-       }
-       len = ops->add_len;
-       if (put.data.type == SNDRV_SEQ_INSTR_ATYPE_ALIAS)
-               len = sizeof(struct snd_seq_instr);
-       instr = snd_seq_instr_new(len, atomic);
-       if (instr == NULL) {
-               snd_instr_unlock_ops(list);
-               result = -ENOMEM;
-               goto __return;
-       }
-       instr->ops = ops;
-       instr->instr = put.id.instr;
-       strlcpy(instr->name, put.data.name, sizeof(instr->name));
-       instr->type = put.data.type;
-       if (instr->type == SNDRV_SEQ_INSTR_ATYPE_DATA) {
-               result = ops->put(ops->private_data,
-                                 instr,
-                                 (void __user *)ev->data.ext.ptr + sizeof(struct snd_seq_instr_header),
-                                 ev->data.ext.len - sizeof(struct snd_seq_instr_header),
-                                 atomic,
-                                 put.cmd);
-               if (result < 0) {
-                       snd_seq_instr_free(instr, atomic);
-                       snd_instr_unlock_ops(list);
-                       goto __return;
-               }
-       }
-       key = compute_hash_instr_key(&instr->instr);
-       spin_lock_irqsave(&list->lock, flags);
-       instr->next = list->hash[key];
-       list->hash[key] = instr;
-       list->count++;
-       spin_unlock_irqrestore(&list->lock, flags);
-       snd_instr_unlock_ops(list);
-       result = 0;
-      __return:
-       instr_result(ev, SNDRV_SEQ_EVENT_INSTR_PUT, result, atomic);
-       return result;
-}
-
-static int instr_get(struct snd_seq_kinstr_ops *ops,
-                    struct snd_seq_kinstr_list *list,
-                    struct snd_seq_event *ev,
-                    int atomic, int hop)
-{
-       return -ENXIO;
-}
-
-static int instr_free(struct snd_seq_kinstr_ops *ops,
-                     struct snd_seq_kinstr_list *list,
-                     struct snd_seq_event *ev,
-                     int atomic, int hop)
-{
-       struct snd_seq_instr_header ifree;
-       struct snd_seq_kinstr *instr, *prev;
-       int result = -EINVAL;
-       unsigned long flags;
-       unsigned int hash;
-
-       if ((ev->flags & SNDRV_SEQ_EVENT_LENGTH_MASK) != SNDRV_SEQ_EVENT_LENGTH_VARUSR)
-               goto __return;
-
-       if (ev->data.ext.len < sizeof(struct snd_seq_instr_header))
-               goto __return;
-       if (copy_from_user(&ifree, (void __user *)ev->data.ext.ptr,
-                          sizeof(struct snd_seq_instr_header))) {
-               result = -EFAULT;
-               goto __return;
-       }
-       if (ifree.cmd == SNDRV_SEQ_INSTR_FREE_CMD_ALL ||
-           ifree.cmd == SNDRV_SEQ_INSTR_FREE_CMD_PRIVATE ||
-           ifree.cmd == SNDRV_SEQ_INSTR_FREE_CMD_CLUSTER) {
-               result = snd_seq_instr_list_free_cond(list, &ifree, ev->dest.client, atomic);
-               goto __return;
-       }
-       if (ifree.cmd == SNDRV_SEQ_INSTR_FREE_CMD_SINGLE) {
-               if (ifree.id.instr.std & 0xff000000) {
-                       ifree.id.instr.std &= 0x00ffffff;
-                       ifree.id.instr.std |= (unsigned int)ev->source.client << 24;
-               }
-               hash = compute_hash_instr_key(&ifree.id.instr);
-               snd_instr_lock_ops(list);
-               spin_lock_irqsave(&list->lock, flags);
-               instr = list->hash[hash];
-               prev = NULL;
-               while (instr) {
-                       if (!compare_instr(&instr->instr, &ifree.id.instr, 1))
-                               goto __free_single;
-                       prev = instr;
-                       instr = instr->next;
-               }
-               result = -ENOENT;
-               spin_unlock_irqrestore(&list->lock, flags);
-               snd_instr_unlock_ops(list);
-               goto __return;
-               
-             __free_single:
-               if (prev) {
-                       prev->next = instr->next;
-               } else {
-                       list->hash[hash] = instr->next;
-               }
-               if (instr->ops && instr->ops->notify)
-                       instr->ops->notify(instr->ops->private_data, instr,
-                                          SNDRV_SEQ_INSTR_NOTIFY_REMOVE);
-               while (instr->use) {
-                       spin_unlock_irqrestore(&list->lock, flags);
-                       schedule_timeout_uninterruptible(1);
-                       spin_lock_irqsave(&list->lock, flags);
-               }                               
-               spin_unlock_irqrestore(&list->lock, flags);
-               result = snd_seq_instr_free(instr, atomic);
-               snd_instr_unlock_ops(list);
-               goto __return;
-       }
-
-      __return:
-       instr_result(ev, SNDRV_SEQ_EVENT_INSTR_FREE, result, atomic);
-       return result;
-}
-
-static int instr_list(struct snd_seq_kinstr_ops *ops,
-                     struct snd_seq_kinstr_list *list,
-                     struct snd_seq_event *ev,
-                     int atomic, int hop)
-{
-       return -ENXIO;
-}
-
-static int instr_cluster(struct snd_seq_kinstr_ops *ops,
-                        struct snd_seq_kinstr_list *list,
-                        struct snd_seq_event *ev,
-                        int atomic, int hop)
-{
-       return -ENXIO;
-}
-
-int snd_seq_instr_event(struct snd_seq_kinstr_ops *ops,
-                       struct snd_seq_kinstr_list *list,
-                       struct snd_seq_event *ev,
-                       int client,
-                       int atomic,
-                       int hop)
-{
-       int direct = 0;
-
-       snd_assert(ops != NULL && list != NULL && ev != NULL, return -EINVAL);
-       if (snd_seq_ev_is_direct(ev)) {
-               direct = 1;
-               switch (ev->type) {
-               case SNDRV_SEQ_EVENT_INSTR_BEGIN:
-                       return instr_begin(ops, list, ev, atomic, hop);
-               case SNDRV_SEQ_EVENT_INSTR_END:
-                       return instr_end(ops, list, ev, atomic, hop);
-               }
-       }
-       if ((list->flags & SNDRV_SEQ_INSTR_FLG_DIRECT) && !direct)
-               return -EINVAL;
-       switch (ev->type) {
-       case SNDRV_SEQ_EVENT_INSTR_INFO:
-               return instr_info(ops, list, ev, atomic, hop);
-       case SNDRV_SEQ_EVENT_INSTR_FINFO:
-               return instr_format_info(ops, list, ev, atomic, hop);
-       case SNDRV_SEQ_EVENT_INSTR_RESET:
-               return instr_reset(ops, list, ev, atomic, hop);
-       case SNDRV_SEQ_EVENT_INSTR_STATUS:
-               return instr_status(ops, list, ev, atomic, hop);
-       case SNDRV_SEQ_EVENT_INSTR_PUT:
-               return instr_put(ops, list, ev, atomic, hop);
-       case SNDRV_SEQ_EVENT_INSTR_GET:
-               return instr_get(ops, list, ev, atomic, hop);
-       case SNDRV_SEQ_EVENT_INSTR_FREE:
-               return instr_free(ops, list, ev, atomic, hop);
-       case SNDRV_SEQ_EVENT_INSTR_LIST:
-               return instr_list(ops, list, ev, atomic, hop);
-       case SNDRV_SEQ_EVENT_INSTR_CLUSTER:
-               return instr_cluster(ops, list, ev, atomic, hop);
-       }
-       return -EINVAL;
-}
-                       
-/*
- *  Init part
- */
-
-static int __init alsa_seq_instr_init(void)
-{
-       return 0;
-}
-
-static void __exit alsa_seq_instr_exit(void)
-{
-}
-
-module_init(alsa_seq_instr_init)
-module_exit(alsa_seq_instr_exit)
-
-EXPORT_SYMBOL(snd_seq_instr_list_new);
-EXPORT_SYMBOL(snd_seq_instr_list_free);
-EXPORT_SYMBOL(snd_seq_instr_list_free_cond);
-EXPORT_SYMBOL(snd_seq_instr_find);
-EXPORT_SYMBOL(snd_seq_instr_free_use);
-EXPORT_SYMBOL(snd_seq_instr_event);
index 1a34941d42172b8e1d2cedf0c73caba91f4b63c6..54f921edda793aab4948b0c72259528ee0885d9d 100644 (file)
@@ -19,7 +19,6 @@
  *
  */
 
-#include <sound/driver.h>
 #include <sound/core.h>
 #include "seq_lock.h"
 
index a72a1945bf8adb93e9590764c6994bb47632b742..0cf6ac4773187cca81cf511258083f060bd9cfc6 100644 (file)
@@ -20,7 +20,6 @@
  *
  */
 
-#include <sound/driver.h>
 #include <linux/init.h>
 #include <linux/slab.h>
 #include <linux/vmalloc.h>
index 5929aaf1df9d1f3340d509b2424bab4fb6cb50fa..99b35360c5066df99574c2f2dca0753f9f557609 100644 (file)
@@ -26,7 +26,6 @@ Possible options for midisynth module:
 */
 
 
-#include <sound/driver.h>
 #include <linux/init.h>
 #include <linux/slab.h>
 #include <linux/errno.h>
index 17b3e6f13ca3564c08f3ca0e81f2ddcd5041ad91..07c663135c62d87d054d389e7a84320820497b04 100644 (file)
@@ -29,7 +29,6 @@
  * code in here.  If there is it should be reported as a bug.
  */
 
-#include <sound/driver.h>
 #include <linux/init.h>
 #include <linux/slab.h>
 #include <linux/string.h>
@@ -229,13 +228,6 @@ snd_midi_process_event(struct snd_midi_op *ops,
        case SNDRV_SEQ_EVENT_PORT_START:
        case SNDRV_SEQ_EVENT_PORT_EXIT:
        case SNDRV_SEQ_EVENT_PORT_CHANGE:
-       case SNDRV_SEQ_EVENT_SAMPLE:
-       case SNDRV_SEQ_EVENT_SAMPLE_START:
-       case SNDRV_SEQ_EVENT_SAMPLE_STOP:
-       case SNDRV_SEQ_EVENT_SAMPLE_FREQ:
-       case SNDRV_SEQ_EVENT_SAMPLE_VOLUME:
-       case SNDRV_SEQ_EVENT_SAMPLE_LOOP:
-       case SNDRV_SEQ_EVENT_SAMPLE_POSITION:
        case SNDRV_SEQ_EVENT_ECHO:
        not_yet:
        default:
index b6820a5a73fc1518d0048c0d5f327331d7b45d4c..8284f176a342c6011f470a9d27581438ba23c73a 100644 (file)
@@ -19,7 +19,6 @@
  *   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA
  */
 
-#include <sound/driver.h>
 #include <linux/slab.h>
 #include <linux/errno.h>
 #include <linux/string.h>
index b6e23ad12ab9bd2d94a0f72ca58a2eba74a9d199..1c32a53d6bd8d91084fb1a560277199557f40e35 100644 (file)
@@ -20,7 +20,6 @@
  *
  */
 
-#include <sound/driver.h>
 #include <sound/core.h>
 #include <linux/slab.h>
 #include "seq_system.h"
index 074418617ee9f1dabacde501b9d45e37cdff6a3d..85969db576c905514974d478e2c1495c8848ff05 100644 (file)
@@ -19,7 +19,6 @@
  *
  */
 
-#include <sound/driver.h>
 #include <linux/time.h>
 #include <linux/slab.h>
 #include <sound/core.h>
index 9b87bb0c7f33a3f4dcf266b8d5d1236004a62c92..4a48c6ee8ee835b7b55cc74967ca87beb1ba0812 100644 (file)
@@ -35,7 +35,6 @@
  *     - Addition of experimental sync support.
  */
 
-#include <sound/driver.h>
 #include <linux/init.h>
 #include <linux/slab.h>
 #include <sound/core.h>
index b201b76e9412b58b9761f4fdf3bbf2e30bbffc3a..77884e62b6483216981c0d2a9da553ac86b9f138 100644 (file)
@@ -19,7 +19,6 @@
  *
  */
 
-#include <sound/driver.h>
 #include <linux/init.h>
 #include <sound/core.h>
 #include "seq_system.h"
index 8716352afc812a8eb9dee2469d2e6c3fd1f30433..d8fcd62e400f0ed512b330132e3c603ad4cc7b49 100644 (file)
  *
  */
 
-#include <sound/driver.h>
 #include <sound/core.h>
 #include <linux/slab.h>
 #include "seq_timer.h"
 #include "seq_queue.h"
 #include "seq_info.h"
 
-extern int seq_default_timer_class;
-extern int seq_default_timer_sclass;
-extern int seq_default_timer_card;
-extern int seq_default_timer_device;
-extern int seq_default_timer_subdevice;
-extern int seq_default_timer_resolution;
-
 /* allowed sequencer timer frequencies, in Hz */
 #define MIN_FREQUENCY          10
 #define MAX_FREQUENCY          6250
index e9ee1543c9247e64f229d642118cec785d2eced9..88dfb71805ae0b0b49bfeb37d5b44aa40214ac57 100644 (file)
@@ -138,4 +138,11 @@ int snd_seq_timer_set_skew(struct snd_seq_timer *tmr, unsigned int skew, unsigne
 snd_seq_real_time_t snd_seq_timer_get_cur_time(struct snd_seq_timer *tmr);
 snd_seq_tick_time_t snd_seq_timer_get_cur_tick(struct snd_seq_timer *tmr);
 
+extern int seq_default_timer_class;
+extern int seq_default_timer_sclass;
+extern int seq_default_timer_card;
+extern int seq_default_timer_device;
+extern int seq_default_timer_subdevice;
+extern int seq_default_timer_resolution;
+
 #endif
index e11790f6debea51e2b4fe24c4fc1ebab2c10654b..86e7739269ca2f4899eaeeb057a60f4bef556459 100644 (file)
@@ -35,7 +35,6 @@
  *
  */
 
-#include <sound/driver.h>
 #include <linux/init.h>
 #include <linux/wait.h>
 #include <linux/slab.h>
index 7b486c4d70db7c9c21d19a416b6a8db017b5ceb9..00cca4d6e56226e454484b0fc660102c55f1bdc4 100644 (file)
@@ -19,7 +19,6 @@
  *
  */
 
-#include <sound/driver.h>
 #include <linux/init.h>
 #include <linux/slab.h>
 #include <linux/time.h>
index dc73313b733a32915ac4d729e9df751c57490d4b..7be51546eb9e8cd8b26d46e0bb4306377edb2278 100644 (file)
@@ -19,8 +19,6 @@
  *
  */
 
-#include <sound/driver.h>
-
 #ifdef CONFIG_SND_OSSEMUL
 
 #if !defined(CONFIG_SOUND) && !(defined(MODULE) && defined(CONFIG_SOUND_MODULE))
index e7dc56ca4b97d6840f286fd1013f3cfde80731a1..aece465934b86aa5f4266870b7e3cc7c1e745b1b 100644 (file)
@@ -19,7 +19,6 @@
  *
  */
 
-#include <sound/driver.h>
 #include <linux/delay.h>
 #include <linux/init.h>
 #include <linux/slab.h>
 #endif
 
 static int timer_limit = DEFAULT_TIMER_LIMIT;
+static int timer_tstamp_monotonic = 1;
 MODULE_AUTHOR("Jaroslav Kysela <perex@perex.cz>, Takashi Iwai <tiwai@suse.de>");
 MODULE_DESCRIPTION("ALSA timer interface");
 MODULE_LICENSE("GPL");
 module_param(timer_limit, int, 0444);
 MODULE_PARM_DESC(timer_limit, "Maximum global timers in system.");
+module_param(timer_tstamp_monotonic, int, 0444);
+MODULE_PARM_DESC(timer_tstamp_monotonic, "Use posix monotonic clock source for timestamps (default).");
 
 struct snd_timer_user {
        struct snd_timer_instance *timeri;
@@ -381,7 +383,10 @@ static void snd_timer_notify1(struct snd_timer_instance *ti, int event)
        struct snd_timer_instance *ts;
        struct timespec tstamp;
 
-       getnstimeofday(&tstamp);
+       if (timer_tstamp_monotonic)
+               do_posix_clock_monotonic_gettime(&tstamp);
+       else
+               getnstimeofday(&tstamp);
        snd_assert(event >= SNDRV_TIMER_EVENT_START &&
                   event <= SNDRV_TIMER_EVENT_PAUSE, return);
        if (event == SNDRV_TIMER_EVENT_START ||
@@ -1182,8 +1187,12 @@ static void snd_timer_user_tinterrupt(struct snd_timer_instance *timeri,
                spin_unlock(&tu->qlock);
                return;
        }
-       if (tu->last_resolution != resolution || ticks > 0)
-               getnstimeofday(&tstamp);
+       if (tu->last_resolution != resolution || ticks > 0) {
+               if (timer_tstamp_monotonic)
+                       do_posix_clock_monotonic_gettime(&tstamp);
+               else
+                       getnstimeofday(&tstamp);
+       }
        if ((tu->filter & (1 << SNDRV_TIMER_EVENT_RESOLUTION)) &&
            tu->last_resolution != resolution) {
                r1.event = SNDRV_TIMER_EVENT_RESOLUTION;
index 83529b08d019804945972dd4e474fe9a33a78f5d..75d4fe09fdf363fc2a9fe7ee06439330150c397a 100644 (file)
@@ -120,4 +120,16 @@ config SND_PORTMAN2X4
          To compile this driver as a module, choose M here: the module
          will be called snd-portman2x4.
 
+config SND_ML403_AC97CR
+       tristate "Xilinx ML403 AC97 Controller Reference"
+       depends on SND && XILINX_VIRTEX
+       select SND_AC97_CODEC
+       help
+         Say Y here to include support for the
+         opb_ac97_controller_ref_v1_00_a ip core found in Xilinx' ML403
+         reference design.
+
+         To compile this driver as a module, choose M here: the module
+         will be called snd-ml403_ac97cr.
+
 endmenu
index 80aeff5ccdea390fe7953264ffd0b55311acdd11..8e5530006e1fccb1d0704e0a35ee14a30105e064 100644 (file)
@@ -9,6 +9,7 @@ snd-mts64-objs := mts64.o
 snd-portman2x4-objs := portman2x4.o
 snd-serial-u16550-objs := serial-u16550.o
 snd-virmidi-objs := virmidi.o
+snd-ml403-ac97cr-objs := ml403-ac97cr.o pcm-indirect2.o
 
 # Toplevel Module Dependency
 obj-$(CONFIG_SND_DUMMY) += snd-dummy.o
@@ -17,5 +18,6 @@ obj-$(CONFIG_SND_SERIAL_U16550) += snd-serial-u16550.o
 obj-$(CONFIG_SND_MTPAV) += snd-mtpav.o
 obj-$(CONFIG_SND_MTS64) += snd-mts64.o
 obj-$(CONFIG_SND_PORTMAN2X4) += snd-portman2x4.o
+obj-$(CONFIG_SND_ML403_AC97CR) += snd-ml403-ac97cr.o
 
 obj-$(CONFIG_SND) += opl3/ opl4/ mpu401/ vx/
index e008f3c58eaca172c19b0284d9100780bc705575..a240eaeb5c620d754c81a23602e0b4809b4fe40f 100644 (file)
@@ -18,7 +18,6 @@
  *
  */
 
-#include <sound/driver.h>
 #include <linux/init.h>
 #include <linux/err.h>
 #include <linux/platform_device.h>
diff --git a/sound/drivers/ml403-ac97cr.c b/sound/drivers/ml403-ac97cr.c
new file mode 100644 (file)
index 0000000..05a871a
--- /dev/null
@@ -0,0 +1,1352 @@
+/*
+ * ALSA driver for Xilinx ML403 AC97 Controller Reference
+ *   IP: opb_ac97_controller_ref_v1_00_a (EDK 8.1i)
+ *   IP: opb_ac97_controller_ref_v1_00_a (EDK 9.1i)
+ *
+ *  Copyright (c) by 2007  Joachim Foerster <JOFT@gmx.de>
+ *
+ *   This program is free software; you can redistribute it and/or modify
+ *   it under the terms of the GNU General Public License as published by
+ *   the Free Software Foundation; either version 2 of the License, or
+ *   (at your option) any later version.
+ *
+ *   This program is distributed in the hope that it will be useful,
+ *   but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *   GNU General Public License for more details.
+ *
+ *   You should have received a copy of the GNU General Public License
+ *   along with this program; if not, write to the Free Software
+ *   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA
+ *
+ */
+
+/* Some notes / status of this driver:
+ *
+ * - Don't wonder about some strange implementations of things - especially the
+ * (heavy) shadowing of codec registers, with which I tried to reduce read
+ * accesses to a minimum, because after a variable amount of accesses, the AC97
+ * controller doesn't raise the register access finished bit anymore ...
+ *
+ * - Playback support seems to be pretty stable - no issues here.
+ * - Capture support "works" now, too. Overruns don't happen any longer so often.
+ *   But there might still be some ...
+ */
+
+#include <linux/init.h>
+#include <linux/moduleparam.h>
+
+#include <linux/platform_device.h>
+
+#include <linux/ioport.h>
+#include <linux/io.h>
+#include <linux/interrupt.h>
+
+/* HZ */
+#include <linux/param.h>
+/* jiffies, time_*() */
+#include <linux/jiffies.h>
+/* schedule_timeout*() */
+#include <linux/sched.h>
+/* spin_lock*() */
+#include <linux/spinlock.h>
+/* struct mutex, mutex_init(), mutex_*lock() */
+#include <linux/mutex.h>
+
+/* snd_printk(), snd_printd() */
+#include <sound/core.h>
+#include <sound/pcm.h>
+#include <sound/pcm_params.h>
+#include <sound/initval.h>
+#include <sound/ac97_codec.h>
+
+#include "pcm-indirect2.h"
+
+
+#define SND_ML403_AC97CR_DRIVER "ml403-ac97cr"
+
+MODULE_AUTHOR("Joachim Foerster <JOFT@gmx.de>");
+MODULE_DESCRIPTION("Xilinx ML403 AC97 Controller Reference");
+MODULE_LICENSE("GPL");
+MODULE_SUPPORTED_DEVICE("{{Xilinx,ML403 AC97 Controller Reference}}");
+
+static int index[SNDRV_CARDS] = SNDRV_DEFAULT_IDX;
+static char *id[SNDRV_CARDS] = SNDRV_DEFAULT_STR;
+static int enable[SNDRV_CARDS] = SNDRV_DEFAULT_ENABLE;
+
+module_param_array(index, int, NULL, 0444);
+MODULE_PARM_DESC(index, "Index value for ML403 AC97 Controller Reference.");
+module_param_array(id, charp, NULL, 0444);
+MODULE_PARM_DESC(id, "ID string for ML403 AC97 Controller Reference.");
+module_param_array(enable, bool, NULL, 0444);
+MODULE_PARM_DESC(enable, "Enable this ML403 AC97 Controller Reference.");
+
+/* Special feature options */
+/*#define CODEC_WRITE_CHECK_RAF*/ /* don't return after a write to a codec
+                                  * register, while RAF bit is not set
+                                  */
+/* Debug options for code which may be removed completely in a final version */
+#ifdef CONFIG_SND_DEBUG
+/*#define CODEC_STAT*/            /* turn on some minimal "statistics"
+                                  * about codec register usage
+                                  */
+#define SND_PCM_INDIRECT2_STAT    /* turn on some "statistics" about the
+                                  * process of copying bytes from the
+                                  * intermediate buffer to the hardware
+                                  * fifo and the other way round
+                                  */
+#endif
+
+/* Definition of a "level/facility dependent" printk(); may be removed
+ * completely in a final version
+ */
+#undef PDEBUG
+#ifdef CONFIG_SND_DEBUG
+/* "facilities" for PDEBUG */
+#define UNKNOWN       (1<<0)
+#define CODEC_SUCCESS (1<<1)
+#define CODEC_FAKE    (1<<2)
+#define INIT_INFO     (1<<3)
+#define INIT_FAILURE  (1<<4)
+#define WORK_INFO     (1<<5)
+#define WORK_FAILURE  (1<<6)
+
+#define PDEBUG_FACILITIES (UNKNOWN | INIT_FAILURE | WORK_FAILURE)
+
+#define PDEBUG(fac, fmt, args...) do { \
+               if (fac & PDEBUG_FACILITIES) \
+                       snd_printd(KERN_DEBUG SND_ML403_AC97CR_DRIVER ": " \
+                                  fmt, ##args); \
+       } while (0)
+#else
+#define PDEBUG(fac, fmt, args...) /* nothing */
+#endif
+
+
+
+/* Defines for "waits"/timeouts (portions of HZ=250 on arch/ppc by default) */
+#define CODEC_TIMEOUT_ON_INIT       5  /* timeout for checking for codec
+                                        * readiness (after insmod)
+                                        */
+#ifndef CODEC_WRITE_CHECK_RAF
+#define CODEC_WAIT_AFTER_WRITE    100  /* general, static wait after a write
+                                        * access to a codec register, may be
+                                        * 0 to completely remove wait
+                                        */
+#else
+#define CODEC_TIMEOUT_AFTER_WRITE   5  /* timeout after a write access to a
+                                        * codec register, if RAF bit is used
+                                        */
+#endif
+#define CODEC_TIMEOUT_AFTER_READ    5  /* timeout after a read access to a
+                                        * codec register (checking RAF bit)
+                                        */
+
+/* Infrastructure for codec register shadowing */
+#define LM4550_REG_OK        (1<<0)   /* register exists */
+#define LM4550_REG_DONEREAD  (1<<1)   /* read register once, value should be
+                                      * the same currently in the register
+                                      */
+#define LM4550_REG_NOSAVE    (1<<2)   /* values written to this register will
+                                      * not be saved in the register
+                                      */
+#define LM4550_REG_NOSHADOW  (1<<3)   /* don't do register shadowing, use plain
+                                      * hardware access
+                                      */
+#define LM4550_REG_READONLY  (1<<4)   /* register is read only */
+#define LM4550_REG_FAKEPROBE (1<<5)   /* fake write _and_ read actions during
+                                      * probe() correctly
+                                      */
+#define LM4550_REG_FAKEREAD  (1<<6)   /* fake read access, always return
+                                      * default value
+                                      */
+#define LM4550_REG_ALLFAKE   (LM4550_REG_FAKEREAD | LM4550_REG_FAKEPROBE)
+
+struct lm4550_reg {
+       u16 value;
+       u16 flag;
+       u16 wmask;
+       u16 def;
+};
+
+struct lm4550_reg lm4550_regfile[64] = {
+       [AC97_RESET / 2]              = {.flag = LM4550_REG_OK \
+                                               | LM4550_REG_NOSAVE \
+                                               | LM4550_REG_FAKEREAD,
+                                        .def = 0x0D50},
+       [AC97_MASTER / 2]             = {.flag = LM4550_REG_OK
+                                               | LM4550_REG_FAKEPROBE,
+                                        .wmask = 0x9F1F,
+                                        .def = 0x8000},
+       [AC97_HEADPHONE / 2]          = {.flag = LM4550_REG_OK \
+                                               | LM4550_REG_FAKEPROBE,
+                                        .wmask = 0x9F1F,
+                                        .def = 0x8000},
+       [AC97_MASTER_MONO / 2]        = {.flag = LM4550_REG_OK \
+                                               | LM4550_REG_FAKEPROBE,
+                                        .wmask = 0x801F,
+                                        .def = 0x8000},
+       [AC97_PC_BEEP / 2]            = {.flag = LM4550_REG_OK \
+                                               | LM4550_REG_FAKEPROBE,
+                                        .wmask = 0x801E,
+                                        .def = 0x0},
+       [AC97_PHONE / 2]              = {.flag = LM4550_REG_OK \
+                                               | LM4550_REG_FAKEPROBE,
+                                        .wmask = 0x801F,
+                                        .def = 0x8008},
+       [AC97_MIC / 2]                = {.flag = LM4550_REG_OK \
+                                               | LM4550_REG_FAKEPROBE,
+                                        .wmask = 0x805F,
+                                        .def = 0x8008},
+       [AC97_LINE / 2]               = {.flag = LM4550_REG_OK \
+                                               | LM4550_REG_FAKEPROBE,
+                                        .wmask = 0x9F1F,
+                                        .def = 0x8808},
+       [AC97_CD / 2]                 = {.flag = LM4550_REG_OK \
+                                               | LM4550_REG_FAKEPROBE,
+                                        .wmask = 0x9F1F,
+                                        .def = 0x8808},
+       [AC97_VIDEO / 2]              = {.flag = LM4550_REG_OK \
+                                               | LM4550_REG_FAKEPROBE,
+                                        .wmask = 0x9F1F,
+                                        .def = 0x8808},
+       [AC97_AUX / 2]                = {.flag = LM4550_REG_OK \
+                                               | LM4550_REG_FAKEPROBE,
+                                        .wmask = 0x9F1F,
+                                        .def = 0x8808},
+       [AC97_PCM / 2]                = {.flag = LM4550_REG_OK \
+                                               | LM4550_REG_FAKEPROBE,
+                                        .wmask = 0x9F1F,
+                                        .def = 0x8008},
+       [AC97_REC_SEL / 2]            = {.flag = LM4550_REG_OK \
+                                               | LM4550_REG_FAKEPROBE,
+                                        .wmask = 0x707,
+                                        .def = 0x0},
+       [AC97_REC_GAIN / 2]           = {.flag = LM4550_REG_OK \
+                                               | LM4550_REG_FAKEPROBE,
+                                        .wmask = 0x8F0F,
+                                        .def = 0x8000},
+       [AC97_GENERAL_PURPOSE / 2]    = {.flag = LM4550_REG_OK \
+                                               | LM4550_REG_FAKEPROBE,
+                                        .def = 0x0,
+                                        .wmask = 0xA380},
+       [AC97_3D_CONTROL / 2]         = {.flag = LM4550_REG_OK \
+                                               | LM4550_REG_FAKEREAD \
+                                               | LM4550_REG_READONLY,
+                                        .def = 0x0101},
+       [AC97_POWERDOWN / 2]          = {.flag = LM4550_REG_OK \
+                                               | LM4550_REG_NOSHADOW \
+                                               | LM4550_REG_NOSAVE,
+                                        .wmask = 0xFF00},
+                                       /* may not write ones to
+                                        * REF/ANL/DAC/ADC bits
+                                        * FIXME: Is this ok?
+                                        */
+       [AC97_EXTENDED_ID / 2]        = {.flag = LM4550_REG_OK \
+                                               | LM4550_REG_FAKEREAD \
+                                               | LM4550_REG_READONLY,
+                                        .def = 0x0201}, /* primary codec */
+       [AC97_EXTENDED_STATUS / 2]    = {.flag = LM4550_REG_OK \
+                                               | LM4550_REG_NOSHADOW \
+                                               | LM4550_REG_NOSAVE,
+                                        .wmask = 0x1},
+       [AC97_PCM_FRONT_DAC_RATE / 2] = {.flag = LM4550_REG_OK \
+                                               | LM4550_REG_FAKEPROBE,
+                                        .def = 0xBB80,
+                                        .wmask = 0xFFFF},
+       [AC97_PCM_LR_ADC_RATE / 2]    = {.flag = LM4550_REG_OK \
+                                               | LM4550_REG_FAKEPROBE,
+                                        .def = 0xBB80,
+                                        .wmask = 0xFFFF},
+       [AC97_VENDOR_ID1 / 2]         = {.flag = LM4550_REG_OK \
+                                               | LM4550_REG_READONLY \
+                                               | LM4550_REG_FAKEREAD,
+                                        .def = 0x4E53},
+       [AC97_VENDOR_ID2 / 2]         = {.flag = LM4550_REG_OK \
+                                               | LM4550_REG_READONLY \
+                                               | LM4550_REG_FAKEREAD,
+                                        .def = 0x4350}
+};
+
+#define LM4550_RF_OK(reg)    (lm4550_regfile[reg / 2].flag & LM4550_REG_OK)
+
+static void lm4550_regfile_init(void)
+{
+       int i;
+       for (i = 0; i < 64; i++)
+               if (lm4550_regfile[i].flag & LM4550_REG_FAKEPROBE)
+                       lm4550_regfile[i].value = lm4550_regfile[i].def;
+}
+
+static void lm4550_regfile_write_values_after_init(struct snd_ac97 *ac97)
+{
+       int i;
+       for (i = 0; i < 64; i++)
+               if ((lm4550_regfile[i].flag & LM4550_REG_FAKEPROBE) &&
+                   (lm4550_regfile[i].value != lm4550_regfile[i].def)) {
+                       PDEBUG(CODEC_FAKE, "lm4550_regfile_write_values_after_"
+                              "init(): reg=0x%x value=0x%x / %d is different "
+                              "from def=0x%x / %d\n",
+                              i, lm4550_regfile[i].value,
+                              lm4550_regfile[i].value, lm4550_regfile[i].def,
+                              lm4550_regfile[i].def);
+                       snd_ac97_write(ac97, i * 2, lm4550_regfile[i].value);
+                       lm4550_regfile[i].flag |= LM4550_REG_DONEREAD;
+               }
+}
+
+
+/* direct registers */
+#define CR_REG(ml403_ac97cr, x) ((ml403_ac97cr)->port + CR_REG_##x)
+
+#define CR_REG_PLAYFIFO         0x00
+#define   CR_PLAYDATA(a)        ((a) & 0xFFFF)
+
+#define CR_REG_RECFIFO          0x04
+#define   CR_RECDATA(a)         ((a) & 0xFFFF)
+
+#define CR_REG_STATUS           0x08
+#define   CR_RECOVER            (1<<7)
+#define   CR_PLAYUNDER          (1<<6)
+#define   CR_CODECREADY         (1<<5)
+#define   CR_RAF                (1<<4)
+#define   CR_RECEMPTY           (1<<3)
+#define   CR_RECFULL            (1<<2)
+#define   CR_PLAYHALF           (1<<1)
+#define   CR_PLAYFULL           (1<<0)
+
+#define CR_REG_RESETFIFO        0x0C
+#define   CR_RECRESET           (1<<1)
+#define   CR_PLAYRESET          (1<<0)
+
+#define CR_REG_CODEC_ADDR       0x10
+/* UG082 says:
+ * #define   CR_CODEC_ADDR(a)  ((a) << 1)
+ * #define   CR_CODEC_READ     (1<<0)
+ * #define   CR_CODEC_WRITE    (0<<0)
+ */
+/* RefDesign example says: */
+#define   CR_CODEC_ADDR(a)      ((a) << 0)
+#define   CR_CODEC_READ         (1<<7)
+#define   CR_CODEC_WRITE        (0<<7)
+
+#define CR_REG_CODEC_DATAREAD   0x14
+#define   CR_CODEC_DATAREAD(v)  ((v) & 0xFFFF)
+
+#define CR_REG_CODEC_DATAWRITE  0x18
+#define   CR_CODEC_DATAWRITE(v) ((v) & 0xFFFF)
+
+#define CR_FIFO_SIZE            32
+
+struct snd_ml403_ac97cr {
+       /* lock for access to (controller) registers */
+       spinlock_t reg_lock;
+       /* mutex for the whole sequence of accesses to (controller) registers
+        * which affect codec registers
+        */
+       struct mutex cdc_mutex;
+
+       int irq; /* for playback */
+       int enable_irq; /* for playback */
+
+       int capture_irq;
+       int enable_capture_irq;
+
+       struct resource *res_port;
+       void *port;
+
+       struct snd_ac97 *ac97;
+       int ac97_fake;
+#ifdef CODEC_STAT
+       int ac97_read;
+       int ac97_write;
+#endif
+
+       struct platform_device *pfdev;
+       struct snd_card *card;
+       struct snd_pcm *pcm;
+       struct snd_pcm_substream *playback_substream;
+       struct snd_pcm_substream *capture_substream;
+
+       struct snd_pcm_indirect2 ind_rec; /* for playback */
+       struct snd_pcm_indirect2 capture_ind2_rec;
+};
+
+static struct snd_pcm_hardware snd_ml403_ac97cr_playback = {
+       .info =             (SNDRV_PCM_INFO_MMAP |
+                            SNDRV_PCM_INFO_INTERLEAVED |
+                            SNDRV_PCM_INFO_MMAP_VALID),
+       .formats =          SNDRV_PCM_FMTBIT_S16_BE,
+       .rates =            (SNDRV_PCM_RATE_CONTINUOUS |
+                            SNDRV_PCM_RATE_8000_48000),
+       .rate_min =         4000,
+       .rate_max =         48000,
+       .channels_min =     2,
+       .channels_max =     2,
+       .buffer_bytes_max = (128*1024),
+       .period_bytes_min = CR_FIFO_SIZE/2,
+       .period_bytes_max = (64*1024),
+       .periods_min =      2,
+       .periods_max =      (128*1024)/(CR_FIFO_SIZE/2),
+       .fifo_size =        0,
+};
+
+static struct snd_pcm_hardware snd_ml403_ac97cr_capture = {
+       .info =             (SNDRV_PCM_INFO_MMAP |
+                            SNDRV_PCM_INFO_INTERLEAVED |
+                            SNDRV_PCM_INFO_MMAP_VALID),
+       .formats =          SNDRV_PCM_FMTBIT_S16_BE,
+       .rates =            (SNDRV_PCM_RATE_CONTINUOUS |
+                            SNDRV_PCM_RATE_8000_48000),
+       .rate_min =         4000,
+       .rate_max =         48000,
+       .channels_min =     2,
+       .channels_max =     2,
+       .buffer_bytes_max = (128*1024),
+       .period_bytes_min = CR_FIFO_SIZE/2,
+       .period_bytes_max = (64*1024),
+       .periods_min =      2,
+       .periods_max =      (128*1024)/(CR_FIFO_SIZE/2),
+       .fifo_size =        0,
+};
+
+static size_t
+snd_ml403_ac97cr_playback_ind2_zero(struct snd_pcm_substream *substream,
+                                   struct snd_pcm_indirect2 *rec)
+{
+       struct snd_ml403_ac97cr *ml403_ac97cr;
+       int copied_words = 0;
+       u32 full = 0;
+
+       ml403_ac97cr = snd_pcm_substream_chip(substream);
+
+       spin_lock(&ml403_ac97cr->reg_lock);
+       while ((full = (in_be32(CR_REG(ml403_ac97cr, STATUS)) &
+                       CR_PLAYFULL)) != CR_PLAYFULL) {
+               out_be32(CR_REG(ml403_ac97cr, PLAYFIFO), 0);
+               copied_words++;
+       }
+       rec->hw_ready = 0;
+       spin_unlock(&ml403_ac97cr->reg_lock);
+
+       return (size_t) (copied_words * 2);
+}
+
+static size_t
+snd_ml403_ac97cr_playback_ind2_copy(struct snd_pcm_substream *substream,
+                                   struct snd_pcm_indirect2 *rec,
+                                   size_t bytes)
+{
+       struct snd_ml403_ac97cr *ml403_ac97cr;
+       u16 *src;
+       int copied_words = 0;
+       u32 full = 0;
+
+       ml403_ac97cr = snd_pcm_substream_chip(substream);
+       src = (u16 *)(substream->runtime->dma_area + rec->sw_data);
+
+       spin_lock(&ml403_ac97cr->reg_lock);
+       while (((full = (in_be32(CR_REG(ml403_ac97cr, STATUS)) &
+                        CR_PLAYFULL)) != CR_PLAYFULL) && (bytes > 1)) {
+               out_be32(CR_REG(ml403_ac97cr, PLAYFIFO),
+                        CR_PLAYDATA(src[copied_words]));
+               copied_words++;
+               bytes = bytes - 2;
+       }
+       if (full != CR_PLAYFULL)
+               rec->hw_ready = 1;
+       else
+               rec->hw_ready = 0;
+       spin_unlock(&ml403_ac97cr->reg_lock);
+
+       return (size_t) (copied_words * 2);
+}
+
+static size_t
+snd_ml403_ac97cr_capture_ind2_null(struct snd_pcm_substream *substream,
+                                  struct snd_pcm_indirect2 *rec)
+{
+       struct snd_ml403_ac97cr *ml403_ac97cr;
+       int copied_words = 0;
+       u32 empty = 0;
+
+       ml403_ac97cr = snd_pcm_substream_chip(substream);
+
+       spin_lock(&ml403_ac97cr->reg_lock);
+       while ((empty = (in_be32(CR_REG(ml403_ac97cr, STATUS)) &
+                        CR_RECEMPTY)) != CR_RECEMPTY) {
+               volatile u32 trash;
+
+               trash = CR_RECDATA(in_be32(CR_REG(ml403_ac97cr, RECFIFO)));
+               /* Hmmmm, really necessary? Don't want call to in_be32()
+                * to be optimised away!
+                */
+               trash++;
+               copied_words++;
+       }
+       rec->hw_ready = 0;
+       spin_unlock(&ml403_ac97cr->reg_lock);
+
+       return (size_t) (copied_words * 2);
+}
+
+static size_t
+snd_ml403_ac97cr_capture_ind2_copy(struct snd_pcm_substream *substream,
+                                  struct snd_pcm_indirect2 *rec, size_t bytes)
+{
+       struct snd_ml403_ac97cr *ml403_ac97cr;
+       u16 *dst;
+       int copied_words = 0;
+       u32 empty = 0;
+
+       ml403_ac97cr = snd_pcm_substream_chip(substream);
+       dst = (u16 *)(substream->runtime->dma_area + rec->sw_data);
+
+       spin_lock(&ml403_ac97cr->reg_lock);
+       while (((empty = (in_be32(CR_REG(ml403_ac97cr, STATUS)) &
+                         CR_RECEMPTY)) != CR_RECEMPTY) && (bytes > 1)) {
+               dst[copied_words] = CR_RECDATA(in_be32(CR_REG(ml403_ac97cr,
+                                                             RECFIFO)));
+               copied_words++;
+               bytes = bytes - 2;
+       }
+       if (empty != CR_RECEMPTY)
+               rec->hw_ready = 1;
+       else
+               rec->hw_ready = 0;
+       spin_unlock(&ml403_ac97cr->reg_lock);
+
+       return (size_t) (copied_words * 2);
+}
+
+static snd_pcm_uframes_t
+snd_ml403_ac97cr_pcm_pointer(struct snd_pcm_substream *substream)
+{
+       struct snd_ml403_ac97cr *ml403_ac97cr;
+       struct snd_pcm_indirect2 *ind2_rec = NULL;
+
+       ml403_ac97cr = snd_pcm_substream_chip(substream);
+
+       if (substream == ml403_ac97cr->playback_substream)
+               ind2_rec = &ml403_ac97cr->ind_rec;
+       if (substream == ml403_ac97cr->capture_substream)
+               ind2_rec = &ml403_ac97cr->capture_ind2_rec;
+
+       if (ind2_rec != NULL)
+               return snd_pcm_indirect2_pointer(substream, ind2_rec);
+       return (snd_pcm_uframes_t) 0;
+}
+
+static int
+snd_ml403_ac97cr_pcm_playback_trigger(struct snd_pcm_substream *substream,
+                                     int cmd)
+{
+       struct snd_ml403_ac97cr *ml403_ac97cr;
+       int err = 0;
+
+       ml403_ac97cr = snd_pcm_substream_chip(substream);
+
+       switch (cmd) {
+       case SNDRV_PCM_TRIGGER_START:
+               PDEBUG(WORK_INFO, "trigger(playback): START\n");
+               ml403_ac97cr->ind_rec.hw_ready = 1;
+
+               /* clear play FIFO */
+               out_be32(CR_REG(ml403_ac97cr, RESETFIFO), CR_PLAYRESET);
+
+               /* enable play irq */
+               ml403_ac97cr->enable_irq = 1;
+               enable_irq(ml403_ac97cr->irq);
+               break;
+       case SNDRV_PCM_TRIGGER_STOP:
+               PDEBUG(WORK_INFO, "trigger(playback): STOP\n");
+               ml403_ac97cr->ind_rec.hw_ready = 0;
+#ifdef SND_PCM_INDIRECT2_STAT
+               snd_pcm_indirect2_stat(substream, &ml403_ac97cr->ind_rec);
+#endif
+               /* disable play irq */
+               disable_irq_nosync(ml403_ac97cr->irq);
+               ml403_ac97cr->enable_irq = 0;
+               break;
+       default:
+               err = -EINVAL;
+               break;
+       }
+       PDEBUG(WORK_INFO, "trigger(playback): (done)\n");
+       return err;
+}
+
+static int
+snd_ml403_ac97cr_pcm_capture_trigger(struct snd_pcm_substream *substream,
+                                     int cmd)
+{
+       struct snd_ml403_ac97cr *ml403_ac97cr;
+       int err = 0;
+
+       ml403_ac97cr = snd_pcm_substream_chip(substream);
+
+       switch (cmd) {
+       case SNDRV_PCM_TRIGGER_START:
+               PDEBUG(WORK_INFO, "trigger(capture): START\n");
+               ml403_ac97cr->capture_ind2_rec.hw_ready = 0;
+
+               /* clear record FIFO */
+               out_be32(CR_REG(ml403_ac97cr, RESETFIFO), CR_RECRESET);
+
+               /* enable record irq */
+               ml403_ac97cr->enable_capture_irq = 1;
+               enable_irq(ml403_ac97cr->capture_irq);
+               break;
+       case SNDRV_PCM_TRIGGER_STOP:
+               PDEBUG(WORK_INFO, "trigger(capture): STOP\n");
+               ml403_ac97cr->capture_ind2_rec.hw_ready = 0;
+#ifdef SND_PCM_INDIRECT2_STAT
+               snd_pcm_indirect2_stat(substream,
+                                      &ml403_ac97cr->capture_ind2_rec);
+#endif
+               /* disable capture irq */
+               disable_irq_nosync(ml403_ac97cr->capture_irq);
+               ml403_ac97cr->enable_capture_irq = 0;
+               break;
+       default:
+               err = -EINVAL;
+               break;
+       }
+       PDEBUG(WORK_INFO, "trigger(capture): (done)\n");
+       return err;
+}
+
+static int
+snd_ml403_ac97cr_pcm_playback_prepare(struct snd_pcm_substream *substream)
+{
+       struct snd_ml403_ac97cr *ml403_ac97cr;
+       struct snd_pcm_runtime *runtime;
+
+       ml403_ac97cr = snd_pcm_substream_chip(substream);
+       runtime = substream->runtime;
+
+       PDEBUG(WORK_INFO,
+              "prepare(): period_bytes=%d, minperiod_bytes=%d\n",
+              snd_pcm_lib_period_bytes(substream), CR_FIFO_SIZE / 2);
+
+       /* set sampling rate */
+       snd_ac97_set_rate(ml403_ac97cr->ac97, AC97_PCM_FRONT_DAC_RATE,
+                         runtime->rate);
+       PDEBUG(WORK_INFO, "prepare(): rate=%d\n", runtime->rate);
+
+       /* init struct for intermediate buffer */
+       memset(&ml403_ac97cr->ind_rec, 0,
+              sizeof(struct snd_pcm_indirect2));
+       ml403_ac97cr->ind_rec.hw_buffer_size = CR_FIFO_SIZE;
+       ml403_ac97cr->ind_rec.sw_buffer_size =
+               snd_pcm_lib_buffer_bytes(substream);
+       ml403_ac97cr->ind_rec.min_periods = -1;
+       ml403_ac97cr->ind_rec.min_multiple =
+               snd_pcm_lib_period_bytes(substream) / (CR_FIFO_SIZE / 2);
+       PDEBUG(WORK_INFO, "prepare(): hw_buffer_size=%d, "
+              "sw_buffer_size=%d, min_multiple=%d\n",
+              CR_FIFO_SIZE, ml403_ac97cr->ind_rec.sw_buffer_size,
+              ml403_ac97cr->ind_rec.min_multiple);
+       return 0;
+}
+
+static int
+snd_ml403_ac97cr_pcm_capture_prepare(struct snd_pcm_substream *substream)
+{
+       struct snd_ml403_ac97cr *ml403_ac97cr;
+       struct snd_pcm_runtime *runtime;
+
+       ml403_ac97cr = snd_pcm_substream_chip(substream);
+       runtime = substream->runtime;
+
+       PDEBUG(WORK_INFO,
+              "prepare(capture): period_bytes=%d, minperiod_bytes=%d\n",
+              snd_pcm_lib_period_bytes(substream), CR_FIFO_SIZE / 2);
+
+       /* set sampling rate */
+       snd_ac97_set_rate(ml403_ac97cr->ac97, AC97_PCM_LR_ADC_RATE,
+                         runtime->rate);
+       PDEBUG(WORK_INFO, "prepare(capture): rate=%d\n", runtime->rate);
+
+       /* init struct for intermediate buffer */
+       memset(&ml403_ac97cr->capture_ind2_rec, 0,
+              sizeof(struct snd_pcm_indirect2));
+       ml403_ac97cr->capture_ind2_rec.hw_buffer_size = CR_FIFO_SIZE;
+       ml403_ac97cr->capture_ind2_rec.sw_buffer_size =
+               snd_pcm_lib_buffer_bytes(substream);
+       ml403_ac97cr->capture_ind2_rec.min_multiple =
+               snd_pcm_lib_period_bytes(substream) / (CR_FIFO_SIZE / 2);
+       PDEBUG(WORK_INFO, "prepare(capture): hw_buffer_size=%d, "
+              "sw_buffer_size=%d, min_multiple=%d\n", CR_FIFO_SIZE,
+              ml403_ac97cr->capture_ind2_rec.sw_buffer_size,
+              ml403_ac97cr->capture_ind2_rec.min_multiple);
+       return 0;
+}
+
+static int snd_ml403_ac97cr_hw_free(struct snd_pcm_substream *substream)
+{
+       PDEBUG(WORK_INFO, "hw_free()\n");
+       return snd_pcm_lib_free_pages(substream);
+}
+
+static int
+snd_ml403_ac97cr_hw_params(struct snd_pcm_substream *substream,
+                          struct snd_pcm_hw_params *hw_params)
+{
+       PDEBUG(WORK_INFO, "hw_params(): desired buffer bytes=%d, desired "
+              "period bytes=%d\n",
+              params_buffer_bytes(hw_params), params_period_bytes(hw_params));
+       return snd_pcm_lib_malloc_pages(substream,
+                                       params_buffer_bytes(hw_params));
+}
+
+static int snd_ml403_ac97cr_playback_open(struct snd_pcm_substream *substream)
+{
+       struct snd_ml403_ac97cr *ml403_ac97cr;
+       struct snd_pcm_runtime *runtime;
+
+       ml403_ac97cr = snd_pcm_substream_chip(substream);
+       runtime = substream->runtime;
+
+       PDEBUG(WORK_INFO, "open(playback)\n");
+       ml403_ac97cr->playback_substream = substream;
+       runtime->hw = snd_ml403_ac97cr_playback;
+
+       snd_pcm_hw_constraint_step(runtime, 0,
+                                  SNDRV_PCM_HW_PARAM_PERIOD_BYTES,
+                                  CR_FIFO_SIZE / 2);
+       return 0;
+}
+
+static int snd_ml403_ac97cr_capture_open(struct snd_pcm_substream *substream)
+{
+       struct snd_ml403_ac97cr *ml403_ac97cr;
+       struct snd_pcm_runtime *runtime;
+
+       ml403_ac97cr = snd_pcm_substream_chip(substream);
+       runtime = substream->runtime;
+
+       PDEBUG(WORK_INFO, "open(capture)\n");
+       ml403_ac97cr->capture_substream = substream;
+       runtime->hw = snd_ml403_ac97cr_capture;
+
+       snd_pcm_hw_constraint_step(runtime, 0,
+                                  SNDRV_PCM_HW_PARAM_PERIOD_BYTES,
+                                  CR_FIFO_SIZE / 2);
+       return 0;
+}
+
+static int snd_ml403_ac97cr_playback_close(struct snd_pcm_substream *substream)
+{
+       struct snd_ml403_ac97cr *ml403_ac97cr;
+
+       ml403_ac97cr = snd_pcm_substream_chip(substream);
+
+       PDEBUG(WORK_INFO, "close(playback)\n");
+       ml403_ac97cr->playback_substream = NULL;
+       return 0;
+}
+
+static int snd_ml403_ac97cr_capture_close(struct snd_pcm_substream *substream)
+{
+       struct snd_ml403_ac97cr *ml403_ac97cr;
+
+       ml403_ac97cr = snd_pcm_substream_chip(substream);
+
+       PDEBUG(WORK_INFO, "close(capture)\n");
+       ml403_ac97cr->capture_substream = NULL;
+       return 0;
+}
+
+static struct snd_pcm_ops snd_ml403_ac97cr_playback_ops = {
+       .open = snd_ml403_ac97cr_playback_open,
+       .close = snd_ml403_ac97cr_playback_close,
+       .ioctl = snd_pcm_lib_ioctl,
+       .hw_params = snd_ml403_ac97cr_hw_params,
+       .hw_free = snd_ml403_ac97cr_hw_free,
+       .prepare = snd_ml403_ac97cr_pcm_playback_prepare,
+       .trigger = snd_ml403_ac97cr_pcm_playback_trigger,
+       .pointer = snd_ml403_ac97cr_pcm_pointer,
+};
+
+static struct snd_pcm_ops snd_ml403_ac97cr_capture_ops = {
+       .open = snd_ml403_ac97cr_capture_open,
+       .close = snd_ml403_ac97cr_capture_close,
+       .ioctl = snd_pcm_lib_ioctl,
+       .hw_params = snd_ml403_ac97cr_hw_params,
+       .hw_free = snd_ml403_ac97cr_hw_free,
+       .prepare = snd_ml403_ac97cr_pcm_capture_prepare,
+       .trigger = snd_ml403_ac97cr_pcm_capture_trigger,
+       .pointer = snd_ml403_ac97cr_pcm_pointer,
+};
+
+static irqreturn_t snd_ml403_ac97cr_irq(int irq, void *dev_id)
+{
+       struct snd_ml403_ac97cr *ml403_ac97cr;
+       struct platform_device *pfdev;
+       int cmp_irq;
+
+       ml403_ac97cr = (struct snd_ml403_ac97cr *)dev_id;
+       if (ml403_ac97cr == NULL)
+               return IRQ_NONE;
+
+       pfdev = ml403_ac97cr->pfdev;
+
+       /* playback interrupt */
+       cmp_irq = platform_get_irq(pfdev, 0);
+       if (irq == cmp_irq) {
+               if (ml403_ac97cr->enable_irq)
+                       snd_pcm_indirect2_playback_interrupt(
+                               ml403_ac97cr->playback_substream,
+                               &ml403_ac97cr->ind_rec,
+                               snd_ml403_ac97cr_playback_ind2_copy,
+                               snd_ml403_ac97cr_playback_ind2_zero);
+               else
+                       goto __disable_irq;
+       } else {
+               /* record interrupt */
+               cmp_irq = platform_get_irq(pfdev, 1);
+               if (irq == cmp_irq) {
+                       if (ml403_ac97cr->enable_capture_irq)
+                               snd_pcm_indirect2_capture_interrupt(
+                                       ml403_ac97cr->capture_substream,
+                                       &ml403_ac97cr->capture_ind2_rec,
+                                       snd_ml403_ac97cr_capture_ind2_copy,
+                                       snd_ml403_ac97cr_capture_ind2_null);
+                       else
+                               goto __disable_irq;
+               } else
+                       return IRQ_NONE;
+       }
+       return IRQ_HANDLED;
+
+__disable_irq:
+       PDEBUG(INIT_INFO, "irq(): irq %d is meant to be disabled! So, now try "
+              "to disable it _really_!\n", irq);
+       disable_irq_nosync(irq);
+       return IRQ_HANDLED;
+}
+
+static unsigned short
+snd_ml403_ac97cr_codec_read(struct snd_ac97 *ac97, unsigned short reg)
+{
+       struct snd_ml403_ac97cr *ml403_ac97cr = ac97->private_data;
+#ifdef CODEC_STAT
+       u32 stat;
+       u32 rafaccess = 0;
+#endif
+       unsigned long end_time;
+       u16 value = 0;
+
+       if (!LM4550_RF_OK(reg)) {
+               snd_printk(KERN_WARNING SND_ML403_AC97CR_DRIVER ": "
+                          "access to unknown/unused codec register 0x%x "
+                          "ignored!\n", reg);
+               return 0;
+       }
+       /* check if we can fake/answer this access from our shadow register */
+       if ((lm4550_regfile[reg / 2].flag &
+            (LM4550_REG_DONEREAD | LM4550_REG_ALLFAKE)) &&
+           !(lm4550_regfile[reg / 2].flag & LM4550_REG_NOSHADOW)) {
+               if (lm4550_regfile[reg / 2].flag & LM4550_REG_FAKEREAD) {
+                       PDEBUG(CODEC_FAKE, "codec_read(): faking read from "
+                              "reg=0x%x, val=0x%x / %d\n",
+                              reg, lm4550_regfile[reg / 2].def,
+                              lm4550_regfile[reg / 2].def);
+                       return lm4550_regfile[reg / 2].def;
+               } else if ((lm4550_regfile[reg / 2].flag &
+                           LM4550_REG_FAKEPROBE) &&
+                          ml403_ac97cr->ac97_fake) {
+                       PDEBUG(CODEC_FAKE, "codec_read(): faking read from "
+                              "reg=0x%x, val=0x%x / %d (probe)\n",
+                              reg, lm4550_regfile[reg / 2].value,
+                              lm4550_regfile[reg / 2].value);
+                       return lm4550_regfile[reg / 2].value;
+               } else {
+#ifdef CODEC_STAT
+                       PDEBUG(CODEC_FAKE, "codec_read(): read access "
+                              "answered by shadow register 0x%x (value=0x%x "
+                              "/ %d) (cw=%d cr=%d)\n",
+                              reg, lm4550_regfile[reg / 2].value,
+                              lm4550_regfile[reg / 2].value,
+                              ml403_ac97cr->ac97_write,
+                              ml403_ac97cr->ac97_read);
+#else
+                       PDEBUG(CODEC_FAKE, "codec_read(): read access "
+                              "answered by shadow register 0x%x (value=0x%x "
+                              "/ %d)\n",
+                              reg, lm4550_regfile[reg / 2].value,
+                              lm4550_regfile[reg / 2].value);
+#endif
+                       return lm4550_regfile[reg / 2].value;
+               }
+       }
+       /* if we are here, we _have_ to access the codec really, no faking */
+       if (mutex_lock_interruptible(&ml403_ac97cr->cdc_mutex) != 0)
+               return 0;
+#ifdef CODEC_STAT
+       ml403_ac97cr->ac97_read++;
+#endif
+       spin_lock(&ml403_ac97cr->reg_lock);
+       out_be32(CR_REG(ml403_ac97cr, CODEC_ADDR),
+                CR_CODEC_ADDR(reg) | CR_CODEC_READ);
+       spin_unlock(&ml403_ac97cr->reg_lock);
+       end_time = jiffies + (HZ / CODEC_TIMEOUT_AFTER_READ);
+       do {
+               spin_lock(&ml403_ac97cr->reg_lock);
+#ifdef CODEC_STAT
+               rafaccess++;
+               stat = in_be32(CR_REG(ml403_ac97cr, STATUS));
+               if ((stat & CR_RAF) == CR_RAF) {
+                       value = CR_CODEC_DATAREAD(
+                               in_be32(CR_REG(ml403_ac97cr, CODEC_DATAREAD)));
+                       PDEBUG(CODEC_SUCCESS, "codec_read(): (done) reg=0x%x, "
+                              "value=0x%x / %d (STATUS=0x%x)\n",
+                              reg, value, value, stat);
+#else
+               if ((in_be32(CR_REG(ml403_ac97cr, STATUS)) &
+                    CR_RAF) == CR_RAF) {
+                       value = CR_CODEC_DATAREAD(
+                               in_be32(CR_REG(ml403_ac97cr, CODEC_DATAREAD)));
+                       PDEBUG(CODEC_SUCCESS, "codec_read(): (done) "
+                              "reg=0x%x, value=0x%x / %d\n",
+                              reg, value, value);
+#endif
+                       lm4550_regfile[reg / 2].value = value;
+                       lm4550_regfile[reg / 2].flag |= LM4550_REG_DONEREAD;
+                       spin_unlock(&ml403_ac97cr->reg_lock);
+                       mutex_unlock(&ml403_ac97cr->cdc_mutex);
+                       return value;
+               }
+               spin_unlock(&ml403_ac97cr->reg_lock);
+               schedule_timeout_uninterruptible(1);
+       } while (time_after(end_time, jiffies));
+       /* read the DATAREAD register anyway, see comment below */
+       spin_lock(&ml403_ac97cr->reg_lock);
+       value =
+           CR_CODEC_DATAREAD(in_be32(CR_REG(ml403_ac97cr, CODEC_DATAREAD)));
+       spin_unlock(&ml403_ac97cr->reg_lock);
+#ifdef CODEC_STAT
+       snd_printk(KERN_WARNING SND_ML403_AC97CR_DRIVER ": "
+                  "timeout while codec read! "
+                  "(reg=0x%x, last STATUS=0x%x, DATAREAD=0x%x / %d, %d) "
+                  "(cw=%d, cr=%d)\n",
+                  reg, stat, value, value, rafaccess,
+                  ml403_ac97cr->ac97_write, ml403_ac97cr->ac97_read);
+#else
+       snd_printk(KERN_WARNING SND_ML403_AC97CR_DRIVER ": "
+                  "timeout while codec read! "
+                  "(reg=0x%x, DATAREAD=0x%x / %d)\n",
+                  reg, value, value);
+#endif
+       /* BUG: This is PURE speculation! But after _most_ read timeouts the
+        * value in the register is ok!
+        */
+       lm4550_regfile[reg / 2].value = value;
+       lm4550_regfile[reg / 2].flag |= LM4550_REG_DONEREAD;
+       mutex_unlock(&ml403_ac97cr->cdc_mutex);
+       return value;
+}
+
+static void
+snd_ml403_ac97cr_codec_write(struct snd_ac97 *ac97, unsigned short reg,
+                            unsigned short val)
+{
+       struct snd_ml403_ac97cr *ml403_ac97cr = ac97->private_data;
+
+#ifdef CODEC_STAT
+       u32 stat;
+       u32 rafaccess = 0;
+#endif
+#ifdef CODEC_WRITE_CHECK_RAF
+       unsigned long end_time;
+#endif
+
+       if (!LM4550_RF_OK(reg)) {
+               snd_printk(KERN_WARNING SND_ML403_AC97CR_DRIVER ": "
+                          "access to unknown/unused codec register 0x%x "
+                          "ignored!\n", reg);
+               return;
+       }
+       if (lm4550_regfile[reg / 2].flag & LM4550_REG_READONLY) {
+               snd_printk(KERN_WARNING SND_ML403_AC97CR_DRIVER ": "
+                          "write access to read only codec register 0x%x "
+                          "ignored!\n", reg);
+               return;
+       }
+       if ((val & lm4550_regfile[reg / 2].wmask) != val) {
+               snd_printk(KERN_WARNING SND_ML403_AC97CR_DRIVER ": "
+                          "write access to codec register 0x%x "
+                          "with bad value 0x%x / %d!\n",
+                          reg, val, val);
+               val = val & lm4550_regfile[reg / 2].wmask;
+       }
+       if (((lm4550_regfile[reg / 2].flag & LM4550_REG_FAKEPROBE) &&
+            ml403_ac97cr->ac97_fake) &&
+           !(lm4550_regfile[reg / 2].flag & LM4550_REG_NOSHADOW)) {
+               PDEBUG(CODEC_FAKE, "codec_write(): faking write to reg=0x%x, "
+                      "val=0x%x / %d\n", reg, val, val);
+               lm4550_regfile[reg / 2].value = (val &
+                                               lm4550_regfile[reg / 2].wmask);
+               return;
+       }
+       if (mutex_lock_interruptible(&ml403_ac97cr->cdc_mutex) != 0)
+               return;
+#ifdef CODEC_STAT
+       ml403_ac97cr->ac97_write++;
+#endif
+       spin_lock(&ml403_ac97cr->reg_lock);
+       out_be32(CR_REG(ml403_ac97cr, CODEC_DATAWRITE),
+                CR_CODEC_DATAWRITE(val));
+       out_be32(CR_REG(ml403_ac97cr, CODEC_ADDR),
+                CR_CODEC_ADDR(reg) | CR_CODEC_WRITE);
+       spin_unlock(&ml403_ac97cr->reg_lock);
+#ifdef CODEC_WRITE_CHECK_RAF
+       /* check CR_CODEC_RAF bit to see if write access to register is done;
+        * loop until bit is set or timeout happens
+        */
+       end_time = jiffies + HZ / CODEC_TIMEOUT_AFTER_WRITE;
+       do {
+               spin_lock(&ml403_ac97cr->reg_lock);
+#ifdef CODEC_STAT
+               rafaccess++;
+               stat = in_be32(CR_REG(ml403_ac97cr, STATUS))
+               if ((stat & CR_RAF) == CR_RAF) {
+#else
+               if ((in_be32(CR_REG(ml403_ac97cr, STATUS)) &
+                    CR_RAF) == CR_RAF) {
+#endif
+                       PDEBUG(CODEC_SUCCESS, "codec_write(): (done) "
+                              "reg=0x%x, value=%d / 0x%x\n",
+                              reg, val, val);
+                       if (!(lm4550_regfile[reg / 2].flag &
+                             LM4550_REG_NOSHADOW) &&
+                           !(lm4550_regfile[reg / 2].flag &
+                             LM4550_REG_NOSAVE))
+                               lm4550_regfile[reg / 2].value = val;
+                       lm4550_regfile[reg / 2].flag |= LM4550_REG_DONEREAD;
+                       spin_unlock(&ml403_ac97cr->reg_lock);
+                       mutex_unlock(&ml403_ac97cr->cdc_mutex);
+                       return;
+               }
+               spin_unlock(&ml403_ac97cr->reg_lock);
+               schedule_timeout_uninterruptible(1);
+       } while (time_after(end_time, jiffies));
+#ifdef CODEC_STAT
+       snd_printk(KERN_WARNING SND_ML403_AC97CR_DRIVER ": "
+                  "timeout while codec write "
+                  "(reg=0x%x, val=0x%x / %d, last STATUS=0x%x, %d) "
+                  "(cw=%d, cr=%d)\n",
+                  reg, val, val, stat, rafaccess, ml403_ac97cr->ac97_write,
+                  ml403_ac97cr->ac97_read);
+#else
+       snd_printk(KERN_WARNING SND_ML403_AC97CR_DRIVER ": "
+                  "timeout while codec write (reg=0x%x, val=0x%x / %d)\n",
+                  reg, val, val);
+#endif
+#else /* CODEC_WRITE_CHECK_RAF */
+#if CODEC_WAIT_AFTER_WRITE > 0
+       /* officially, in AC97 spec there is no possibility for a AC97
+        * controller to determine, if write access is done or not - so: How
+        * is Xilinx able to provide a RAF bit for write access?
+        * => very strange, thus just don't check RAF bit (compare with
+        * Xilinx's example app in EDK 8.1i) and wait
+        */
+       schedule_timeout_uninterruptible(HZ / CODEC_WAIT_AFTER_WRITE);
+#endif
+       PDEBUG(CODEC_SUCCESS, "codec_write(): (done) "
+              "reg=0x%x, value=%d / 0x%x (no RAF check)\n",
+              reg, val, val);
+#endif
+       mutex_unlock(&ml403_ac97cr->cdc_mutex);
+       return;
+}
+
+static int __devinit
+snd_ml403_ac97cr_chip_init(struct snd_ml403_ac97cr *ml403_ac97cr)
+{
+       unsigned long end_time;
+       PDEBUG(INIT_INFO, "chip_init():\n");
+       end_time = jiffies + HZ / CODEC_TIMEOUT_ON_INIT;
+       do {
+               if (in_be32(CR_REG(ml403_ac97cr, STATUS)) & CR_CODECREADY) {
+                       /* clear both hardware FIFOs */
+                       out_be32(CR_REG(ml403_ac97cr, RESETFIFO),
+                                CR_RECRESET | CR_PLAYRESET);
+                       PDEBUG(INIT_INFO, "chip_init(): (done)\n");
+                       return 0;
+               }
+               schedule_timeout_uninterruptible(1);
+       } while (time_after(end_time, jiffies));
+       snd_printk(KERN_ERR SND_ML403_AC97CR_DRIVER ": "
+                  "timeout while waiting for codec, "
+                  "not ready!\n");
+       return -EBUSY;
+}
+
+static int snd_ml403_ac97cr_free(struct snd_ml403_ac97cr *ml403_ac97cr)
+{
+       PDEBUG(INIT_INFO, "free():\n");
+       /* irq release */
+       if (ml403_ac97cr->irq >= 0)
+               free_irq(ml403_ac97cr->irq, ml403_ac97cr);
+       if (ml403_ac97cr->capture_irq >= 0)
+               free_irq(ml403_ac97cr->capture_irq, ml403_ac97cr);
+       /* give back "port" */
+       if (ml403_ac97cr->port != NULL)
+               iounmap(ml403_ac97cr->port);
+       kfree(ml403_ac97cr);
+       PDEBUG(INIT_INFO, "free(): (done)\n");
+       return 0;
+}
+
+static int snd_ml403_ac97cr_dev_free(struct snd_device *snddev)
+{
+       struct snd_ml403_ac97cr *ml403_ac97cr = snddev->device_data;
+       PDEBUG(INIT_INFO, "dev_free():\n");
+       return snd_ml403_ac97cr_free(ml403_ac97cr);
+}
+
+static int __devinit
+snd_ml403_ac97cr_create(struct snd_card *card, struct platform_device *pfdev,
+                       struct snd_ml403_ac97cr **rml403_ac97cr)
+{
+       struct snd_ml403_ac97cr *ml403_ac97cr;
+       int err;
+       static struct snd_device_ops ops = {
+               .dev_free = snd_ml403_ac97cr_dev_free,
+       };
+       struct resource *resource;
+       int irq;
+
+       *rml403_ac97cr = NULL;
+       ml403_ac97cr = kzalloc(sizeof(*ml403_ac97cr), GFP_KERNEL);
+       if (ml403_ac97cr == NULL)
+               return -ENOMEM;
+       spin_lock_init(&ml403_ac97cr->reg_lock);
+       mutex_init(&ml403_ac97cr->cdc_mutex);
+       ml403_ac97cr->card = card;
+       ml403_ac97cr->pfdev = pfdev;
+       ml403_ac97cr->irq = -1;
+       ml403_ac97cr->enable_irq = 0;
+       ml403_ac97cr->capture_irq = -1;
+       ml403_ac97cr->enable_capture_irq = 0;
+       ml403_ac97cr->port = NULL;
+       ml403_ac97cr->res_port = NULL;
+
+       PDEBUG(INIT_INFO, "Trying to reserve resources now ...\n");
+       resource = platform_get_resource(pfdev, IORESOURCE_MEM, 0);
+       /* get "port" */
+       ml403_ac97cr->port = ioremap_nocache(resource->start,
+                                            (resource->end) -
+                                            (resource->start) + 1);
+       if (ml403_ac97cr->port == NULL) {
+               snd_printk(KERN_ERR SND_ML403_AC97CR_DRIVER ": "
+                          "unable to remap memory region (%x to %x)\n",
+                          resource->start, resource->end);
+               snd_ml403_ac97cr_free(ml403_ac97cr);
+               return -EBUSY;
+       }
+       snd_printk(KERN_INFO SND_ML403_AC97CR_DRIVER ": "
+                  "remap controller memory region to "
+                  "0x%x done\n", (unsigned int)ml403_ac97cr->port);
+       /* get irq */
+       irq = platform_get_irq(pfdev, 0);
+       if (request_irq(irq, snd_ml403_ac97cr_irq, IRQF_DISABLED,
+                       pfdev->dev.bus_id, (void *)ml403_ac97cr)) {
+               snd_printk(KERN_ERR SND_ML403_AC97CR_DRIVER ": "
+                          "unable to grab IRQ %d\n",
+                          irq);
+               snd_ml403_ac97cr_free(ml403_ac97cr);
+               return -EBUSY;
+       }
+       ml403_ac97cr->irq = irq;
+       snd_printk(KERN_INFO SND_ML403_AC97CR_DRIVER ": "
+                  "request (playback) irq %d done\n",
+                  ml403_ac97cr->irq);
+       irq = platform_get_irq(pfdev, 1);
+       if (request_irq(irq, snd_ml403_ac97cr_irq, IRQF_DISABLED,
+                       pfdev->dev.bus_id, (void *)ml403_ac97cr)) {
+               snd_printk(KERN_ERR SND_ML403_AC97CR_DRIVER ": "
+                          "unable to grab IRQ %d\n",
+                          irq);
+               snd_ml403_ac97cr_free(ml403_ac97cr);
+               return -EBUSY;
+       }
+       ml403_ac97cr->capture_irq = irq;
+       snd_printk(KERN_INFO SND_ML403_AC97CR_DRIVER ": "
+                  "request (capture) irq %d done\n",
+                  ml403_ac97cr->capture_irq);
+
+       err = snd_ml403_ac97cr_chip_init(ml403_ac97cr);
+       if (err < 0) {
+               snd_ml403_ac97cr_free(ml403_ac97cr);
+               return err;
+       }
+
+       err = snd_device_new(card, SNDRV_DEV_LOWLEVEL, ml403_ac97cr, &ops);
+       if (err < 0) {
+               PDEBUG(INIT_FAILURE, "probe(): snd_device_new() failed!\n");
+               snd_ml403_ac97cr_free(ml403_ac97cr);
+               return err;
+       }
+
+       snd_card_set_dev(card, &pfdev->dev);
+
+       *rml403_ac97cr = ml403_ac97cr;
+       return 0;
+}
+
+static void snd_ml403_ac97cr_mixer_free(struct snd_ac97 *ac97)
+{
+       struct snd_ml403_ac97cr *ml403_ac97cr = ac97->private_data;
+       PDEBUG(INIT_INFO, "mixer_free():\n");
+       ml403_ac97cr->ac97 = NULL;
+       PDEBUG(INIT_INFO, "mixer_free(): (done)\n");
+}
+
+static int __devinit
+snd_ml403_ac97cr_mixer(struct snd_ml403_ac97cr *ml403_ac97cr)
+{
+       struct snd_ac97_bus *bus;
+       struct snd_ac97_template ac97;
+       int err;
+       static struct snd_ac97_bus_ops ops = {
+               .write = snd_ml403_ac97cr_codec_write,
+               .read = snd_ml403_ac97cr_codec_read,
+       };
+       PDEBUG(INIT_INFO, "mixer():\n");
+       err = snd_ac97_bus(ml403_ac97cr->card, 0, &ops, NULL, &bus);
+       if (err < 0)
+               return err;
+
+       memset(&ac97, 0, sizeof(ac97));
+       ml403_ac97cr->ac97_fake = 1;
+       lm4550_regfile_init();
+#ifdef CODEC_STAT
+       ml403_ac97cr->ac97_read = 0;
+       ml403_ac97cr->ac97_write = 0;
+#endif
+       ac97.private_data = ml403_ac97cr;
+       ac97.private_free = snd_ml403_ac97cr_mixer_free;
+       ac97.scaps = AC97_SCAP_AUDIO | AC97_SCAP_SKIP_MODEM |
+           AC97_SCAP_NO_SPDIF;
+       err = snd_ac97_mixer(bus, &ac97, &ml403_ac97cr->ac97);
+       ml403_ac97cr->ac97_fake = 0;
+       lm4550_regfile_write_values_after_init(ml403_ac97cr->ac97);
+       PDEBUG(INIT_INFO, "mixer(): (done) snd_ac97_mixer()=%d\n", err);
+       return err;
+}
+
+static int __devinit
+snd_ml403_ac97cr_pcm(struct snd_ml403_ac97cr *ml403_ac97cr, int device,
+                    struct snd_pcm **rpcm)
+{
+       struct snd_pcm *pcm;
+       int err;
+
+       if (rpcm)
+               *rpcm = NULL;
+       err = snd_pcm_new(ml403_ac97cr->card, "ML403AC97CR/1", device, 1, 1,
+                         &pcm);
+       if (err < 0)
+               return err;
+       snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK,
+                       &snd_ml403_ac97cr_playback_ops);
+       snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE,
+                       &snd_ml403_ac97cr_capture_ops);
+       pcm->private_data = ml403_ac97cr;
+       pcm->info_flags = 0;
+       strcpy(pcm->name, "ML403AC97CR DAC/ADC");
+       ml403_ac97cr->pcm = pcm;
+
+       snd_pcm_lib_preallocate_pages_for_all(pcm, SNDRV_DMA_TYPE_CONTINUOUS,
+                                         snd_dma_continuous_data(GFP_KERNEL),
+                                         64 * 1024,
+                                         128 * 1024);
+       if (rpcm)
+               *rpcm = pcm;
+       return 0;
+}
+
+static int __devinit snd_ml403_ac97cr_probe(struct platform_device *pfdev)
+{
+       struct snd_card *card;
+       struct snd_ml403_ac97cr *ml403_ac97cr = NULL;
+       int err;
+       int dev = pfdev->id;
+
+       if (dev >= SNDRV_CARDS)
+               return -ENODEV;
+       if (!enable[dev])
+               return -ENOENT;
+
+       card = snd_card_new(index[dev], id[dev], THIS_MODULE, 0);
+       if (card == NULL)
+               return -ENOMEM;
+       err = snd_ml403_ac97cr_create(card, pfdev, &ml403_ac97cr);
+       if (err < 0) {
+               PDEBUG(INIT_FAILURE, "probe(): create failed!\n");
+               snd_card_free(card);
+               return err;
+       }
+       PDEBUG(INIT_INFO, "probe(): create done\n");
+       card->private_data = ml403_ac97cr;
+       err = snd_ml403_ac97cr_mixer(ml403_ac97cr);
+       if (err < 0) {
+               snd_card_free(card);
+               return err;
+       }
+       PDEBUG(INIT_INFO, "probe(): mixer done\n");
+       err = snd_ml403_ac97cr_pcm(ml403_ac97cr, 0, NULL);
+       if (err < 0) {
+               snd_card_free(card);
+               return err;
+       }
+       PDEBUG(INIT_INFO, "probe(): PCM done\n");
+       strcpy(card->driver, SND_ML403_AC97CR_DRIVER);
+       strcpy(card->shortname, "ML403 AC97 Controller Reference");
+       sprintf(card->longname, "%s %s at 0x%lx, irq %i & %i, device %i",
+               card->shortname, card->driver,
+               (unsigned long)ml403_ac97cr->port, ml403_ac97cr->irq,
+               ml403_ac97cr->capture_irq, dev + 1);
+
+       snd_card_set_dev(card, &pfdev->dev);
+
+       err = snd_card_register(card);
+       if (err < 0) {
+               snd_card_free(card);
+               return err;
+       }
+       platform_set_drvdata(pfdev, card);
+       PDEBUG(INIT_INFO, "probe(): (done)\n");
+       return 0;
+}
+
+static int snd_ml403_ac97cr_remove(struct platform_device *pfdev)
+{
+       snd_card_free(platform_get_drvdata(pfdev));
+       platform_set_drvdata(pfdev, NULL);
+       return 0;
+}
+
+static struct platform_driver snd_ml403_ac97cr_driver = {
+       .probe = snd_ml403_ac97cr_probe,
+       .remove = snd_ml403_ac97cr_remove,
+       .driver = {
+               .name = SND_ML403_AC97CR_DRIVER,
+       },
+};
+
+static int __init alsa_card_ml403_ac97cr_init(void)
+{
+       return platform_driver_register(&snd_ml403_ac97cr_driver);
+}
+
+static void __exit alsa_card_ml403_ac97cr_exit(void)
+{
+       platform_driver_unregister(&snd_ml403_ac97cr_driver);
+}
+
+module_init(alsa_card_ml403_ac97cr_init)
+module_exit(alsa_card_ml403_ac97cr_exit)
index 1fc95dadde1d81d3502b03c48f7fe8f25fba47a6..5b996f3faba5844c434d693af77fee3bcd3bbba4 100644 (file)
@@ -20,7 +20,6 @@
  *
  */
 
-#include <sound/driver.h>
 #include <linux/init.h>
 #include <linux/pnp.h>
 #include <linux/err.h>
index b57f2d5a1c9dff920e17c2ada69d009e090bbd21..5993864acbd38ee163c9de45eb205455a5ea1df2 100644 (file)
@@ -28,7 +28,6 @@
  *
  */
 
-#include <sound/driver.h>
 #include <asm/io.h>
 #include <linux/delay.h>
 #include <linux/init.h>
index 40eb026c86ed4d817349ae614573493646578662..b5e1a71bb64b59857f9a55eb9520d358cd61ed57 100644 (file)
@@ -50,7 +50,6 @@
  *
  */
 
-#include <sound/driver.h>
 #include <linux/init.h>
 #include <linux/interrupt.h>
 #include <linux/err.h>
index dcc90f995294dc6c960b7a5dfd5a2f64fd329fa1..87ba1ddc01151fdfb5bd8f38d80103a68964aa5a 100644 (file)
@@ -18,7 +18,6 @@
  *
  */
 
-#include <sound/driver.h>
 #include <linux/init.h>
 #include <linux/platform_device.h>
 #include <linux/parport.h>
@@ -461,13 +460,14 @@ static int snd_mts64_ctl_smpte_switch_put(struct snd_kcontrol* kctl,
 {
        struct mts64 *mts = snd_kcontrol_chip(kctl);
        int changed = 0;
+       int val = !!uctl->value.integer.value[0];
 
        spin_lock_irq(&mts->lock);
-       if (mts->smpte_switch == uctl->value.integer.value[0])
+       if (mts->smpte_switch == val)
                goto __out;
 
        changed = 1;
-       mts->smpte_switch = uctl->value.integer.value[0];
+       mts->smpte_switch = val;
        if (mts->smpte_switch) {
                mts64_smpte_start(mts->pardev->port,
                                  mts->time[0], mts->time[1],
@@ -541,12 +541,13 @@ static int snd_mts64_ctl_smpte_time_put(struct snd_kcontrol *kctl,
 {
        struct mts64 *mts = snd_kcontrol_chip(kctl);
        int idx = kctl->private_value;
+       unsigned int time = uctl->value.integer.value[0] % 60;
        int changed = 0;
 
        spin_lock_irq(&mts->lock);
-       if (mts->time[idx] != uctl->value.integer.value[0]) {
+       if (mts->time[idx] != time) {
                changed = 1;
-               mts->time[idx] = uctl->value.integer.value[0];
+               mts->time[idx] = time;
        }
        spin_unlock_irq(&mts->lock);
 
@@ -636,6 +637,8 @@ static int snd_mts64_ctl_smpte_fps_put(struct snd_kcontrol *kctl,
        struct mts64 *mts = snd_kcontrol_chip(kctl);
        int changed = 0;
 
+       if (uctl->value.enumerated.item[0] >= 5)
+               return -EINVAL;
        spin_lock_irq(&mts->lock);
        if (mts->fps != uctl->value.enumerated.item[0]) {
                changed = 1;
@@ -662,7 +665,7 @@ static int __devinit snd_mts64_ctl_create(struct snd_card *card,
                                          struct mts64 *mts) 
 {
        int err, i;
-       static struct snd_kcontrol_new *control[] = {
+       static struct snd_kcontrol_new *control[] __devinitdata = {
                &mts64_ctl_smpte_switch,
                &mts64_ctl_smpte_time_hours,
                &mts64_ctl_smpte_time_minutes,
@@ -1004,6 +1007,8 @@ static int __devinit snd_mts64_probe(struct platform_device *pdev)
 
        platform_set_drvdata(pdev, card);
 
+       snd_card_set_dev(card, &pdev->dev);
+
        /* At this point card will be usable */
        if ((err = snd_card_register(card)) < 0) {
                snd_printd("Cannot register card\n");
index a2b9ce06029526a6e733d86e0dcd37b6e2dc4189..ebe4359047cb2deb90fdc952ccae21d7ce555c38 100644 (file)
@@ -327,6 +327,7 @@ static int snd_opl3_free(struct snd_opl3 *opl3)
        snd_assert(opl3 != NULL, return -ENXIO);
        if (opl3->private_free)
                opl3->private_free(opl3);
+       snd_opl3_clear_patches(opl3);
        release_and_free_resource(opl3->res_l_port);
        release_and_free_resource(opl3->res_r_port);
        kfree(opl3);
@@ -360,7 +361,6 @@ int snd_opl3_new(struct snd_card *card,
        opl3->hardware = hardware;
        spin_lock_init(&opl3->reg_lock);
        spin_lock_init(&opl3->timer_lock);
-       mutex_init(&opl3->access_mutex);
 
        if ((err = snd_device_new(card, SNDRV_DEV_CODEC, opl3, &ops)) < 0) {
                snd_opl3_free(opl3);
@@ -496,6 +496,7 @@ int snd_opl3_hwdep_new(struct snd_opl3 * opl3,
                return err;
        }
        hw->private_data = opl3;
+       hw->exclusive = 1;
 #ifdef CONFIG_SND_OSSEMUL
        if (device == 0) {
                hw->oss_type = SNDRV_OSS_DEVICE_TYPE_DMFM;
@@ -521,8 +522,10 @@ int snd_opl3_hwdep_new(struct snd_opl3 * opl3,
        /* operators - only ioctl */
        hw->ops.open = snd_opl3_open;
        hw->ops.ioctl = snd_opl3_ioctl;
+       hw->ops.write = snd_opl3_write;
        hw->ops.release = snd_opl3_release;
 
+       opl3->hwdep = hw;
        opl3->seq_dev_num = seq_device;
 #if defined(CONFIG_SND_SEQUENCER) || (defined(MODULE) && defined(CONFIG_SND_SEQUENCER_MODULE))
        if (snd_seq_device_new(card, seq_device, SNDRV_SEQ_DEV_ID_OPL3,
index 3557b6e20eb535718fa056f1020acc596d01f453..cebcb8b78acb8af1e1066f919bb5124b83b6c821 100644 (file)
@@ -289,8 +289,6 @@ static int snd_opl3_oss_map[MAX_OPL3_VOICES] = {
 void snd_opl3_note_on(void *p, int note, int vel, struct snd_midi_channel *chan)
 {
        struct snd_opl3 *opl3;
-       struct snd_seq_instr wanted;
-       struct snd_seq_kinstr *kinstr;
        int instr_4op;
 
        int voice;
@@ -306,11 +304,13 @@ void snd_opl3_note_on(void *p, int note, int vel, struct snd_midi_channel *chan)
        unsigned char voice_offset;
        unsigned short opl3_reg;
        unsigned char reg_val;
+       unsigned char prg, bank;
 
        int key = note;
        unsigned char fnum, blocknum;
        int i;
 
+       struct fm_patch *patch;
        struct fm_instrument *fm;
        unsigned long flags;
 
@@ -320,19 +320,17 @@ void snd_opl3_note_on(void *p, int note, int vel, struct snd_midi_channel *chan)
        snd_printk("Note on, ch %i, inst %i, note %i, vel %i\n",
                   chan->number, chan->midi_program, note, vel);
 #endif
-       wanted.cluster = 0;
-       wanted.std = SNDRV_SEQ_INSTR_TYPE2_OPL2_3;
 
        /* in SYNTH mode, application takes care of voices */
        /* in SEQ mode, drum voice numbers are notes on drum channel */
        if (opl3->synth_mode == SNDRV_OPL3_MODE_SEQ) {
                if (chan->drum_channel) {
                        /* percussion instruments are located in bank 128 */
-                       wanted.bank = 128;
-                       wanted.prg = note;
+                       bank = 128;
+                       prg = note;
                } else {
-                       wanted.bank = chan->gm_bank_select;
-                       wanted.prg = chan->midi_program;
+                       bank = chan->gm_bank_select;
+                       prg = chan->midi_program;
                }
        } else {
                /* Prepare for OSS mode */
@@ -340,8 +338,8 @@ void snd_opl3_note_on(void *p, int note, int vel, struct snd_midi_channel *chan)
                        return;
 
                /* OSS instruments are located in bank 127 */
-               wanted.bank = 127;
-               wanted.prg = chan->midi_program;
+               bank = 127;
+               prg = chan->midi_program;
        }
 
        spin_lock_irqsave(&opl3->voice_lock, flags);
@@ -353,15 +351,14 @@ void snd_opl3_note_on(void *p, int note, int vel, struct snd_midi_channel *chan)
        }
 
  __extra_prg:
-       kinstr = snd_seq_instr_find(opl3->ilist, &wanted, 1, 0);
-       if (kinstr == NULL) {
+       patch = snd_opl3_find_patch(opl3, prg, bank, 0);
+       if (!patch) {
                spin_unlock_irqrestore(&opl3->voice_lock, flags);
                return;
        }
 
-       fm = KINSTR_DATA(kinstr);
-
-       switch (fm->type) {
+       fm = &patch->inst;
+       switch (patch->type) {
        case FM_PATCH_OPL2:
                instr_4op = 0;
                break;
@@ -371,14 +368,12 @@ void snd_opl3_note_on(void *p, int note, int vel, struct snd_midi_channel *chan)
                        break;
                }
        default:
-               snd_seq_instr_free_use(opl3->ilist, kinstr);
                spin_unlock_irqrestore(&opl3->voice_lock, flags);
                return;
        }
-
 #ifdef DEBUG_MIDI
        snd_printk("  --> OPL%i instrument: %s\n",
-                  instr_4op ? 3 : 2, kinstr->name);
+                  instr_4op ? 3 : 2, patch->name);
 #endif
        /* in SYNTH mode, application takes care of voices */
        /* in SEQ mode, allocate voice on free OPL3 channel */
@@ -569,8 +564,6 @@ void snd_opl3_note_on(void *p, int note, int vel, struct snd_midi_channel *chan)
        /* get extra pgm, but avoid possible loops */
        extra_prg = (extra_prg) ? 0 : fm->modes;
 
-       snd_seq_instr_free_use(opl3->ilist, kinstr);
-
        /* do the bookkeeping */
        vp->time = opl3->use_time++;
        vp->note = key;
@@ -601,12 +594,12 @@ void snd_opl3_note_on(void *p, int note, int vel, struct snd_midi_channel *chan)
        /* allocate extra program if specified in patch library */
        if (extra_prg) {
                if (extra_prg > 128) {
-                       wanted.bank = 128;
+                       bank = 128;
                        /* percussions start at 35 */
-                       wanted.prg = extra_prg - 128 + 35 - 1;
+                       prg = extra_prg - 128 + 35 - 1;
                } else {
-                       wanted.bank = 0;
-                       wanted.prg = extra_prg - 1;
+                       bank = 0;
+                       prg = extra_prg - 1;
                }
 #ifdef DEBUG_MIDI
                snd_printk(" *** allocating extra program\n");
index 5fd3a4c956261d562cb2c55ce1849ed32108c533..239347f26154ba381907874a08a3d6bd2a2da881 100644 (file)
@@ -195,17 +195,6 @@ static int snd_opl3_close_seq_oss(struct snd_seq_oss_arg *arg)
 
 /* load patch */
 
-/* offsets for SBI params */
-#define AM_VIB         0
-#define KSL_LEVEL      2
-#define ATTACK_DECAY   4
-#define SUSTAIN_RELEASE        6
-#define WAVE_SELECT    8
-
-/* offset for SBI instrument */
-#define CONNECTION     10
-#define OFFSET_4OP     11
-
 /* from sound_config.h */
 #define SBFM_MAXINSTR  256
 
@@ -213,112 +202,42 @@ static int snd_opl3_load_patch_seq_oss(struct snd_seq_oss_arg *arg, int format,
                                       const char __user *buf, int offs, int count)
 {
        struct snd_opl3 *opl3;
-       int err = -EINVAL;
+       struct sbi_instrument sbi;
+       char name[32];
+       int err, type;
 
        snd_assert(arg != NULL, return -ENXIO);
        opl3 = arg->private_data;
 
-       if ((format == FM_PATCH) || (format == OPL3_PATCH)) {
-               struct sbi_instrument sbi;
+       if (format == FM_PATCH)
+               type = FM_PATCH_OPL2;
+       else if (format == OPL3_PATCH)
+               type = FM_PATCH_OPL3;
+       else
+               return -EINVAL;
 
-               size_t size;
-               struct snd_seq_instr_header *put;
-               struct snd_seq_instr_data *data;
-               struct fm_xinstrument *xinstr;
+       if (count < (int)sizeof(sbi)) {
+               snd_printk("FM Error: Patch record too short\n");
+               return -EINVAL;
+       }
+       if (copy_from_user(&sbi, buf, sizeof(sbi)))
+               return -EFAULT;
 
-               struct snd_seq_event ev;
-               int i;
+       if (sbi.channel < 0 || sbi.channel >= SBFM_MAXINSTR) {
+               snd_printk("FM Error: Invalid instrument number %d\n",
+                          sbi.channel);
+               return -EINVAL;
+       }
 
-               mm_segment_t fs;
+       memset(name, 0, sizeof(name));
+       sprintf(name, "Chan%d", sbi.channel);
 
-               if (count < (int)sizeof(sbi)) {
-                       snd_printk("FM Error: Patch record too short\n");
-                       return -EINVAL;
-               }
-               if (copy_from_user(&sbi, buf, sizeof(sbi)))
-                       return -EFAULT;
+       err = snd_opl3_load_patch(opl3, sbi.channel, 127, type, name, NULL,
+                                 sbi.operators);
+       if (err < 0)
+               return err;
 
-               if (sbi.channel < 0 || sbi.channel >= SBFM_MAXINSTR) {
-                       snd_printk("FM Error: Invalid instrument number %d\n", sbi.channel);
-                       return -EINVAL;
-               }
-
-               size = sizeof(*put) + sizeof(struct fm_xinstrument);
-               put = kzalloc(size, GFP_KERNEL);
-               if (put == NULL)
-                       return -ENOMEM;
-               /* build header */
-               data = &put->data;
-               data->type = SNDRV_SEQ_INSTR_ATYPE_DATA;
-               strcpy(data->data.format, SNDRV_SEQ_INSTR_ID_OPL2_3);
-               /* build data section */
-               xinstr = (struct fm_xinstrument *)(data + 1);
-               xinstr->stype = FM_STRU_INSTR;
-        
-               for (i = 0; i < 2; i++) {
-                       xinstr->op[i].am_vib = sbi.operators[AM_VIB + i];
-                       xinstr->op[i].ksl_level = sbi.operators[KSL_LEVEL + i];
-                       xinstr->op[i].attack_decay = sbi.operators[ATTACK_DECAY + i];
-                       xinstr->op[i].sustain_release = sbi.operators[SUSTAIN_RELEASE + i];
-                       xinstr->op[i].wave_select = sbi.operators[WAVE_SELECT + i];
-               }
-               xinstr->feedback_connection[0] = sbi.operators[CONNECTION];
-
-               if (format == OPL3_PATCH) {
-                       xinstr->type = FM_PATCH_OPL3;
-                       for (i = 0; i < 2; i++) {
-                               xinstr->op[i+2].am_vib = sbi.operators[OFFSET_4OP + AM_VIB + i];
-                               xinstr->op[i+2].ksl_level = sbi.operators[OFFSET_4OP + KSL_LEVEL + i];
-                               xinstr->op[i+2].attack_decay = sbi.operators[OFFSET_4OP + ATTACK_DECAY + i];
-                               xinstr->op[i+2].sustain_release = sbi.operators[OFFSET_4OP + SUSTAIN_RELEASE + i];
-                               xinstr->op[i+2].wave_select = sbi.operators[OFFSET_4OP + WAVE_SELECT + i];
-                       }
-                       xinstr->feedback_connection[1] = sbi.operators[OFFSET_4OP + CONNECTION];
-               } else {
-                       xinstr->type = FM_PATCH_OPL2;
-               }
-
-               put->id.instr.std = SNDRV_SEQ_INSTR_TYPE2_OPL2_3;
-               put->id.instr.bank = 127;
-               put->id.instr.prg = sbi.channel;
-               put->cmd = SNDRV_SEQ_INSTR_PUT_CMD_CREATE;
-
-               memset (&ev, 0, sizeof(ev));
-               ev.source.client = SNDRV_SEQ_CLIENT_OSS;
-               ev.dest = arg->addr; 
-
-               ev.flags = SNDRV_SEQ_EVENT_LENGTH_VARUSR;
-               ev.queue = SNDRV_SEQ_QUEUE_DIRECT;
-
-               fs = snd_enter_user();
-       __again:
-               ev.type = SNDRV_SEQ_EVENT_INSTR_PUT;
-               ev.data.ext.len = size;
-               ev.data.ext.ptr = put;
-
-               err = snd_seq_instr_event(&opl3->fm_ops, opl3->ilist, &ev,
-                                   opl3->seq_client, 0, 0);
-               if (err == -EBUSY) {
-                       struct snd_seq_instr_header remove;
-
-                       memset (&remove, 0, sizeof(remove));
-                       remove.cmd = SNDRV_SEQ_INSTR_FREE_CMD_SINGLE;
-                       remove.id.instr = put->id.instr;
-
-                       /* remove instrument */
-                       ev.type = SNDRV_SEQ_EVENT_INSTR_FREE;
-                       ev.data.ext.len = sizeof(remove);
-                       ev.data.ext.ptr = &remove;
-
-                       snd_seq_instr_event(&opl3->fm_ops, opl3->ilist, &ev,
-                                           opl3->seq_client, 0, 0);
-                       goto __again;
-               }
-               snd_leave_user(fs);
-
-               kfree(put);
-       }
-       return err;
+       return sizeof(sbi);
 }
 
 /* ioctl */
index 96762c9d485578799c8a8fb6dbce6bad48f17953..2d33f53d36b8227a0ed520a7ec236f02b2df944c 100644 (file)
@@ -51,14 +51,15 @@ void snd_opl3_synth_use_dec(struct snd_opl3 * opl3)
 int snd_opl3_synth_setup(struct snd_opl3 * opl3)
 {
        int idx;
+       struct snd_hwdep *hwdep = opl3->hwdep;
 
-       mutex_lock(&opl3->access_mutex);
-       if (opl3->used) {
-               mutex_unlock(&opl3->access_mutex);
+       mutex_lock(&hwdep->open_mutex);
+       if (hwdep->used) {
+               mutex_unlock(&hwdep->open_mutex);
                return -EBUSY;
        }
-       opl3->used++;
-       mutex_unlock(&opl3->access_mutex);
+       hwdep->used++;
+       mutex_unlock(&hwdep->open_mutex);
 
        snd_opl3_reset(opl3);
 
@@ -81,6 +82,7 @@ int snd_opl3_synth_setup(struct snd_opl3 * opl3)
 void snd_opl3_synth_cleanup(struct snd_opl3 * opl3)
 {
        unsigned long flags;
+       struct snd_hwdep *hwdep;
 
        /* Stop system timer */
        spin_lock_irqsave(&opl3->sys_timer_lock, flags);
@@ -91,9 +93,11 @@ void snd_opl3_synth_cleanup(struct snd_opl3 * opl3)
        spin_unlock_irqrestore(&opl3->sys_timer_lock, flags);
 
        snd_opl3_reset(opl3);
-       mutex_lock(&opl3->access_mutex);
-       opl3->used--;
-       mutex_unlock(&opl3->access_mutex);
+       hwdep = opl3->hwdep;
+       mutex_lock(&hwdep->open_mutex);
+       hwdep->used--;
+       mutex_unlock(&hwdep->open_mutex);
+       wake_up(&hwdep->open_wait);
 }
 
 static int snd_opl3_synth_use(void *private_data, struct snd_seq_port_subscribe * info)
@@ -152,15 +156,7 @@ static int snd_opl3_synth_event_input(struct snd_seq_event * ev, int direct,
 {
        struct snd_opl3 *opl3 = private_data;
 
-       if (ev->type >= SNDRV_SEQ_EVENT_INSTR_BEGIN &&
-           ev->type <= SNDRV_SEQ_EVENT_INSTR_CHANGE) {
-               if (direct) {
-                       snd_seq_instr_event(&opl3->fm_ops, opl3->ilist, ev,
-                                           opl3->seq_client, atomic, hop);
-               }
-       } else {
-               snd_midi_process_event(&opl3_ops, ev, opl3->chset);
-       }
+       snd_midi_process_event(&opl3_ops, ev, opl3->chset);
        return 0;
 }
 
@@ -249,16 +245,6 @@ static int snd_opl3_seq_new_device(struct snd_seq_device *dev)
                return err;
        }
 
-       /* initialize instrument list */
-       opl3->ilist = snd_seq_instr_list_new();
-       if (opl3->ilist == NULL) {
-               snd_seq_delete_kernel_client(client);
-               opl3->seq_client = -1;
-               return -ENOMEM;
-       }
-       opl3->ilist->flags = SNDRV_SEQ_INSTR_FLG_DIRECT;
-       snd_seq_fm_init(&opl3->fm_ops, NULL);
-
        /* setup system timer */
        init_timer(&opl3->tlist);
        opl3->tlist.function = snd_opl3_timer_func;
@@ -287,8 +273,6 @@ static int snd_opl3_seq_delete_device(struct snd_seq_device *dev)
                snd_seq_delete_kernel_client(opl3->seq_client);
                opl3->seq_client = -1;
        }
-       if (opl3->ilist)
-               snd_seq_instr_list_free(&opl3->ilist);
        return 0;
 }
 
index a4b3543a7118121585c808296f1a5dcbda3635e8..a7bf7a4b1f85877a2459ab3bd02f2e6b8d05cd34 100644 (file)
@@ -76,16 +76,6 @@ static int snd_opl3_set_connection(struct snd_opl3 * opl3, int connection);
  */
 int snd_opl3_open(struct snd_hwdep * hw, struct file *file)
 {
-       struct snd_opl3 *opl3 = hw->private_data;
-
-       mutex_lock(&opl3->access_mutex);
-       if (opl3->used) {
-               mutex_unlock(&opl3->access_mutex);
-               return -EAGAIN;
-       }
-       opl3->used++;
-       mutex_unlock(&opl3->access_mutex);
-
        return 0;
 }
 
@@ -165,6 +155,10 @@ int snd_opl3_ioctl(struct snd_hwdep * hw, struct file *file,
 #endif
                return snd_opl3_set_connection(opl3, (int) arg);
 
+       case SNDRV_DM_FM_IOCTL_CLEAR_PATCHES:
+               snd_opl3_clear_patches(opl3);
+               return 0;
+
 #ifdef CONFIG_SND_DEBUG
        default:
                snd_printk("unknown IOCTL: 0x%x\n", cmd);
@@ -181,12 +175,172 @@ int snd_opl3_release(struct snd_hwdep * hw, struct file *file)
        struct snd_opl3 *opl3 = hw->private_data;
 
        snd_opl3_reset(opl3);
-       mutex_lock(&opl3->access_mutex);
-       opl3->used--;
-       mutex_unlock(&opl3->access_mutex);
+       return 0;
+}
+
+/*
+ * write the device - load patches
+ */
+long snd_opl3_write(struct snd_hwdep *hw, const char __user *buf, long count,
+                   loff_t *offset)
+{
+       struct snd_opl3 *opl3 = hw->private_data;
+       long result = 0;
+       int err = 0;
+       struct sbi_patch inst;
+
+       while (count >= sizeof(inst)) {
+               unsigned char type;
+               if (copy_from_user(&inst, buf, sizeof(inst)))
+                       return -EFAULT;
+               if (!memcmp(inst.key, FM_KEY_SBI, 4) ||
+                   !memcmp(inst.key, FM_KEY_2OP, 4))
+                       type = FM_PATCH_OPL2;
+               else if (!memcmp(inst.key, FM_KEY_4OP, 4))
+                       type = FM_PATCH_OPL3;
+               else /* invalid type */
+                       break;
+               err = snd_opl3_load_patch(opl3, inst.prog, inst.bank, type,
+                                         inst.name, inst.extension,
+                                         inst.data);
+               if (err < 0)
+                       break;
+               result += sizeof(inst);
+               count -= sizeof(inst);
+       }
+       return result > 0 ? result : err;
+}
+
+
+/*
+ * Patch management
+ */
+
+/* offsets for SBI params */
+#define AM_VIB         0
+#define KSL_LEVEL      2
+#define ATTACK_DECAY   4
+#define SUSTAIN_RELEASE        6
+#define WAVE_SELECT    8
+
+/* offset for SBI instrument */
+#define CONNECTION     10
+#define OFFSET_4OP     11
+
+/*
+ * load a patch, obviously.
+ *
+ * loaded on the given program and bank numbers with the given type
+ * (FM_PATCH_OPLx).
+ * data is the pointer of SBI record _without_ header (key and name).
+ * name is the name string of the patch.
+ * ext is the extension data of 7 bytes long (stored in name of SBI
+ * data up to offset 25), or NULL to skip.
+ * return 0 if successful or a negative error code.
+ */
+int snd_opl3_load_patch(struct snd_opl3 *opl3,
+                       int prog, int bank, int type,
+                       const char *name,
+                       const unsigned char *ext,
+                       const unsigned char *data)
+{
+       struct fm_patch *patch;
+       int i;
+
+       patch = snd_opl3_find_patch(opl3, prog, bank, 1);
+       if (!patch)
+               return -ENOMEM;
+
+       patch->type = type;
+
+       for (i = 0; i < 2; i++) {
+               patch->inst.op[i].am_vib = data[AM_VIB + i];
+               patch->inst.op[i].ksl_level = data[KSL_LEVEL + i];
+               patch->inst.op[i].attack_decay = data[ATTACK_DECAY + i];
+               patch->inst.op[i].sustain_release = data[SUSTAIN_RELEASE + i];
+               patch->inst.op[i].wave_select = data[WAVE_SELECT + i];
+       }
+       patch->inst.feedback_connection[0] = data[CONNECTION];
+
+       if (type == FM_PATCH_OPL3) {
+               for (i = 0; i < 2; i++) {
+                       patch->inst.op[i+2].am_vib =
+                               data[OFFSET_4OP + AM_VIB + i];
+                       patch->inst.op[i+2].ksl_level =
+                               data[OFFSET_4OP + KSL_LEVEL + i];
+                       patch->inst.op[i+2].attack_decay =
+                               data[OFFSET_4OP + ATTACK_DECAY + i];
+                       patch->inst.op[i+2].sustain_release =
+                               data[OFFSET_4OP + SUSTAIN_RELEASE + i];
+                       patch->inst.op[i+2].wave_select =
+                               data[OFFSET_4OP + WAVE_SELECT + i];
+               }
+               patch->inst.feedback_connection[1] =
+                       data[OFFSET_4OP + CONNECTION];
+       }
+
+       if (ext) {
+               patch->inst.echo_delay = ext[0];
+               patch->inst.echo_atten = ext[1];
+               patch->inst.chorus_spread = ext[2];
+               patch->inst.trnsps = ext[3];
+               patch->inst.fix_dur = ext[4];
+               patch->inst.modes = ext[5];
+               patch->inst.fix_key = ext[6];
+       }
+
+       if (name)
+               strlcpy(patch->name, name, sizeof(patch->name));
 
        return 0;
 }
+EXPORT_SYMBOL(snd_opl3_load_patch);
+
+/*
+ * find a patch with the given program and bank numbers, returns its pointer
+ * if no matching patch is found and create_patch is set, it creates a
+ * new patch object.
+ */
+struct fm_patch *snd_opl3_find_patch(struct snd_opl3 *opl3, int prog, int bank,
+                                    int create_patch)
+{
+       /* pretty dumb hash key */
+       unsigned int key = (prog + bank) % OPL3_PATCH_HASH_SIZE;
+       struct fm_patch *patch;
+
+       for (patch = opl3->patch_table[key]; patch; patch = patch->next) {
+               if (patch->prog == prog && patch->bank == bank)
+                       return patch;
+       }
+       if (!create_patch)
+               return NULL;
+
+       patch = kzalloc(sizeof(*patch), GFP_KERNEL);
+       if (!patch)
+               return NULL;
+       patch->prog = prog;
+       patch->bank = bank;
+       patch->next = opl3->patch_table[key];
+       opl3->patch_table[key] = patch;
+       return patch;
+}
+EXPORT_SYMBOL(snd_opl3_find_patch);
+
+/*
+ * Clear all patches of the given OPL3 instance
+ */
+void snd_opl3_clear_patches(struct snd_opl3 *opl3)
+{
+       int i;
+       for (i = 0; i <  OPL3_PATCH_HASH_SIZE; i++) {
+               struct fm_patch *patch, *next;
+               for (patch = opl3->patch_table[i]; patch; patch = next) {
+                       next = patch->next;
+                       kfree(patch);
+               }
+       }
+       memset(opl3->patch_table, 0, sizeof(opl3->patch_table));
+}
 
 /* ------------------------------ */
 
diff --git a/sound/drivers/pcm-indirect2.c b/sound/drivers/pcm-indirect2.c
new file mode 100644 (file)
index 0000000..3c93c23
--- /dev/null
@@ -0,0 +1,573 @@
+/*
+ * Helper functions for indirect PCM data transfer to a simple FIFO in
+ * hardware (small, no possibility to read "hardware io position",
+ * updating position done by interrupt, ...)
+ *
+ *  Copyright (c) by 2007  Joachim Foerster <JOFT@gmx.de>
+ *
+ *  Based on "pcm-indirect.h" (alsa-driver-1.0.13) by
+ *
+ *  Copyright (c) by Takashi Iwai <tiwai@suse.de>
+ *                   Jaroslav Kysela <perex@suse.cz>
+ *
+ *   This program is free software; you can redistribute it and/or modify
+ *   it under the terms of the GNU General Public License as published by
+ *   the Free Software Foundation; either version 2 of the License, or
+ *   (at your option) any later version.
+ *
+ *   This program is distributed in the hope that it will be useful,
+ *   but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *   GNU General Public License for more details.
+ *
+ *   You should have received a copy of the GNU General Public License
+ *   along with this program; if not, write to the Free Software
+ *   Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+/* snd_printk/d() */
+#include <sound/core.h>
+/* struct snd_pcm_substream, struct snd_pcm_runtime, snd_pcm_uframes_t
+ * snd_pcm_period_elapsed() */
+#include <sound/pcm.h>
+
+#include "pcm-indirect2.h"
+
+#ifdef SND_PCM_INDIRECT2_STAT
+/* jiffies */
+#include <linux/jiffies.h>
+
+void snd_pcm_indirect2_stat(struct snd_pcm_substream *substream,
+                           struct snd_pcm_indirect2 *rec)
+{
+       struct snd_pcm_runtime *runtime = substream->runtime;
+       int i;
+       int j;
+       int k;
+       int seconds = (rec->lastbytetime - rec->firstbytetime) / HZ;
+
+       snd_printk(KERN_DEBUG "STAT: mul_elapsed: %u, mul_elapsed_real: %d, "
+                  "irq_occured: %d\n",
+                  rec->mul_elapsed, rec->mul_elapsed_real, rec->irq_occured);
+       snd_printk(KERN_DEBUG "STAT: min_multiple: %d (irqs/period)\n",
+                  rec->min_multiple);
+       snd_printk(KERN_DEBUG "STAT: firstbytetime: %lu, lastbytetime: %lu, "
+                  "firstzerotime: %lu\n",
+                rec->firstbytetime, rec->lastbytetime, rec->firstzerotime);
+       snd_printk(KERN_DEBUG "STAT: bytes2hw: %u Bytes => (by runtime->rate) "
+                  "length: %d s\n",
+                rec->bytes2hw, rec->bytes2hw / 2 / 2 / runtime->rate);
+       snd_printk(KERN_DEBUG "STAT: (by measurement) length: %d => "
+                  "rate: %d Bytes/s = %d Frames/s|Hz\n",
+                  seconds, rec->bytes2hw / seconds,
+                  rec->bytes2hw / 2 / 2 / seconds);
+       snd_printk(KERN_DEBUG
+                  "STAT: zeros2hw: %u = %d ms ~ %d * %d zero copies\n",
+                  rec->zeros2hw, ((rec->zeros2hw / 2 / 2) * 1000) /
+                  runtime->rate,
+                  rec->zeros2hw / (rec->hw_buffer_size / 2),
+                  (rec->hw_buffer_size / 2));
+       snd_printk(KERN_DEBUG "STAT: pointer_calls: %u, lastdifftime: %u\n",
+                  rec->pointer_calls, rec->lastdifftime);
+       snd_printk(KERN_DEBUG "STAT: sw_io: %d, sw_data: %d\n", rec->sw_io,
+                  rec->sw_data);
+       snd_printk(KERN_DEBUG "STAT: byte_sizes[]:\n");
+       k = 0;
+       for (j = 0; j < 8; j++) {
+               for (i = j * 8; i < (j + 1) * 8; i++)
+                       if (rec->byte_sizes[i] != 0) {
+                               snd_printk(KERN_DEBUG "%u: %u",
+                                          i, rec->byte_sizes[i]);
+                               k++;
+                       }
+               if (((k % 8) == 0) && (k != 0)) {
+                       snd_printk(KERN_DEBUG "\n");
+                       k = 0;
+               }
+       }
+       snd_printk(KERN_DEBUG "\n");
+       snd_printk(KERN_DEBUG "STAT: zero_sizes[]:\n");
+       for (j = 0; j < 8; j++) {
+               k = 0;
+               for (i = j * 8; i < (j + 1) * 8; i++)
+                       if (rec->zero_sizes[i] != 0)
+                               snd_printk(KERN_DEBUG "%u: %u",
+                                          i, rec->zero_sizes[i]);
+                       else
+                               k++;
+               if (!k)
+                       snd_printk(KERN_DEBUG "\n");
+       }
+       snd_printk(KERN_DEBUG "\n");
+       snd_printk(KERN_DEBUG "STAT: min_adds[]:\n");
+       for (j = 0; j < 8; j++) {
+               if (rec->min_adds[j] != 0)
+                       snd_printk(KERN_DEBUG "%u: %u", j, rec->min_adds[j]);
+       }
+       snd_printk(KERN_DEBUG "\n");
+       snd_printk(KERN_DEBUG "STAT: mul_adds[]:\n");
+       for (j = 0; j < 8; j++) {
+               if (rec->mul_adds[j] != 0)
+                       snd_printk(KERN_DEBUG "%u: %u", j, rec->mul_adds[j]);
+       }
+       snd_printk(KERN_DEBUG "\n");
+       snd_printk(KERN_DEBUG
+                  "STAT: zero_times_saved: %d, zero_times_notsaved: %d\n",
+                  rec->zero_times_saved, rec->zero_times_notsaved);
+       /* snd_printk(KERN_DEBUG "STAT: zero_times[]\n");
+       i = 0;
+       for (j = 0; j < 3750; j++) {
+               if (rec->zero_times[j] != 0) {
+                       snd_printk(KERN_DEBUG "%u: %u", j, rec->zero_times[j]);
+                       i++;
+               }
+               if (((i % 8) == 0) && (i != 0))
+                       snd_printk(KERN_DEBUG "\n");
+       }
+       snd_printk(KERN_DEBUG "\n"); */
+       return;
+}
+#endif
+
+/*
+ * _internal_ helper function for playback/capture transfer function
+ */
+static void
+snd_pcm_indirect2_increase_min_periods(struct snd_pcm_substream *substream,
+                                      struct snd_pcm_indirect2 *rec,
+                                      int isplay, int iscopy,
+                                      unsigned int bytes)
+{
+       if (rec->min_periods >= 0) {
+               if (iscopy) {
+                       rec->sw_io += bytes;
+                       if (rec->sw_io >= rec->sw_buffer_size)
+                               rec->sw_io -= rec->sw_buffer_size;
+               } else if (isplay) {
+                       /* If application does not write data in multiples of
+                        * a period, move sw_data to the next correctly aligned
+                        * position, so that sw_io can converge to it (in the
+                        * next step).
+                        */
+                       if (!rec->check_alignment) {
+                               if (rec->bytes2hw %
+                                   snd_pcm_lib_period_bytes(substream)) {
+                                       unsigned bytes2hw_aligned =
+                                           (1 +
+                                            (rec->bytes2hw /
+                                             snd_pcm_lib_period_bytes
+                                             (substream))) *
+                                           snd_pcm_lib_period_bytes
+                                           (substream);
+                                       rec->sw_data =
+                                           bytes2hw_aligned %
+                                           rec->sw_buffer_size;
+#ifdef SND_PCM_INDIRECT2_STAT
+                                       snd_printk(KERN_DEBUG
+                                                  "STAT: @re-align: aligned "
+                                                  "bytes2hw to next period "
+                                                  "size boundary: %d "
+                                                  "(instead of %d)\n",
+                                                  bytes2hw_aligned,
+                                                  rec->bytes2hw);
+                                       snd_printk(KERN_DEBUG
+                                                  "STAT: @re-align: sw_data "
+                                                  "moves to: %d\n",
+                                                  rec->sw_data);
+#endif
+                               }
+                               rec->check_alignment = 1;
+                       }
+                       /* We are at the end and are copying zeros into the
+                        * fifo.
+                        * Now, we have to make sure that sw_io is increased
+                        * until the position of sw_data: Filling the fifo with
+                        * the first zeros means, the last bytes were played.
+                        */
+                       if (rec->sw_io != rec->sw_data) {
+                               unsigned int diff;
+                               if (rec->sw_data > rec->sw_io)
+                                       diff = rec->sw_data - rec->sw_io;
+                               else
+                                       diff = (rec->sw_buffer_size -
+                                               rec->sw_io) +
+                                               rec->sw_data;
+                               if (bytes >= diff)
+                                       rec->sw_io = rec->sw_data;
+                               else {
+                                       rec->sw_io += bytes;
+                                       if (rec->sw_io >= rec->sw_buffer_size)
+                                               rec->sw_io -=
+                                                   rec->sw_buffer_size;
+                               }
+                       }
+               }
+               rec->min_period_count += bytes;
+               if (rec->min_period_count >= (rec->hw_buffer_size / 2)) {
+                       rec->min_periods += (rec->min_period_count /
+                                            (rec->hw_buffer_size / 2));
+#ifdef SND_PCM_INDIRECT2_STAT
+                       if ((rec->min_period_count /
+                            (rec->hw_buffer_size / 2)) > 7)
+                               snd_printk(KERN_DEBUG
+                                          "STAT: more than 7 (%d) min_adds "
+                                          "at once - too big to save!\n",
+                                          (rec->min_period_count /
+                                           (rec->hw_buffer_size / 2)));
+                       else
+                               rec->min_adds[(rec->min_period_count /
+                                              (rec->hw_buffer_size / 2))]++;
+#endif
+                       rec->min_period_count = (rec->min_period_count %
+                                                (rec->hw_buffer_size / 2));
+               }
+       } else if (isplay && iscopy)
+               rec->min_periods = 0;
+}
+
+/*
+ * helper function for playback/capture pointer callback
+ */
+snd_pcm_uframes_t
+snd_pcm_indirect2_pointer(struct snd_pcm_substream *substream,
+                         struct snd_pcm_indirect2 *rec)
+{
+#ifdef SND_PCM_INDIRECT2_STAT
+       rec->pointer_calls++;
+#endif
+       return bytes_to_frames(substream->runtime, rec->sw_io);
+}
+
+/*
+ * _internal_ helper function for playback interrupt callback
+ */
+static void
+snd_pcm_indirect2_playback_transfer(struct snd_pcm_substream *substream,
+                                   struct snd_pcm_indirect2 *rec,
+                                   snd_pcm_indirect2_copy_t copy,
+                                   snd_pcm_indirect2_zero_t zero)
+{
+       struct snd_pcm_runtime *runtime = substream->runtime;
+       snd_pcm_uframes_t appl_ptr = runtime->control->appl_ptr;
+
+       /* runtime->control->appl_ptr: position where ALSA will write next time
+        * rec->appl_ptr: position where ALSA was last time
+        * diff: obviously ALSA wrote that much bytes into the intermediate
+        * buffer since we checked last time
+        */
+       snd_pcm_sframes_t diff = appl_ptr - rec->appl_ptr;
+
+       if (diff) {
+#ifdef SND_PCM_INDIRECT2_STAT
+               rec->lastdifftime = jiffies;
+#endif
+               if (diff < -(snd_pcm_sframes_t) (runtime->boundary / 2))
+                       diff += runtime->boundary;
+               /* number of bytes "added" by ALSA increases the number of
+                * bytes which are ready to "be transfered to HW"/"played"
+                * Then, set rec->appl_ptr to not count bytes twice next time.
+                */
+               rec->sw_ready += (int)frames_to_bytes(runtime, diff);
+               rec->appl_ptr = appl_ptr;
+       }
+       if (rec->hw_ready && (rec->sw_ready <= 0)) {
+               unsigned int bytes;
+
+#ifdef SND_PCM_INDIRECT2_STAT
+               if (rec->firstzerotime == 0) {
+                       rec->firstzerotime = jiffies;
+                       snd_printk(KERN_DEBUG
+                                  "STAT: @firstzerotime: mul_elapsed: %d, "
+                                  "min_period_count: %d\n",
+                                  rec->mul_elapsed, rec->min_period_count);
+                       snd_printk(KERN_DEBUG
+                                  "STAT: @firstzerotime: sw_io: %d, "
+                                  "sw_data: %d, appl_ptr: %u\n",
+                                  rec->sw_io, rec->sw_data,
+                                  (unsigned int)appl_ptr);
+               }
+               if ((jiffies - rec->firstzerotime) < 3750) {
+                       rec->zero_times[(jiffies - rec->firstzerotime)]++;
+                       rec->zero_times_saved++;
+               } else
+                       rec->zero_times_notsaved++;
+#endif
+               bytes = zero(substream, rec);
+
+#ifdef SND_PCM_INDIRECT2_STAT
+               rec->zeros2hw += bytes;
+               if (bytes < 64)
+                       rec->zero_sizes[bytes]++;
+               else
+                       snd_printk(KERN_DEBUG
+                                  "STAT: %d zero Bytes copied to hardware at "
+                                  "once - too big to save!\n",
+                                  bytes);
+#endif
+               snd_pcm_indirect2_increase_min_periods(substream, rec, 1, 0,
+                                                      bytes);
+               return;
+       }
+       while (rec->hw_ready && (rec->sw_ready > 0)) {
+               /* sw_to_end: max. number of bytes that can be read/take from
+                * the current position (sw_data) in _one_ step
+                */
+               unsigned int sw_to_end = rec->sw_buffer_size - rec->sw_data;
+
+               /* bytes: number of bytes we have available (for reading) */
+               unsigned int bytes = rec->sw_ready;
+
+               if (sw_to_end < bytes)
+                       bytes = sw_to_end;
+               if (!bytes)
+                       break;
+
+#ifdef SND_PCM_INDIRECT2_STAT
+               if (rec->firstbytetime == 0)
+                       rec->firstbytetime = jiffies;
+               rec->lastbytetime = jiffies;
+#endif
+               /* copy bytes from intermediate buffer position sw_data to the
+                * HW and return number of bytes actually written
+                * Furthermore, set hw_ready to 0, if the fifo isn't empty
+                * now => more could be transfered to fifo
+                */
+               bytes = copy(substream, rec, bytes);
+               rec->bytes2hw += bytes;
+
+#ifdef SND_PCM_INDIRECT2_STAT
+               if (bytes < 64)
+                       rec->byte_sizes[bytes]++;
+               else
+                       snd_printk(KERN_DEBUG
+                                  "STAT: %d Bytes copied to hardware at once "
+                                  "- too big to save!\n",
+                                  bytes);
+#endif
+               /* increase sw_data by the number of actually written bytes
+                * (= number of taken bytes from intermediate buffer)
+                */
+               rec->sw_data += bytes;
+               if (rec->sw_data == rec->sw_buffer_size)
+                       rec->sw_data = 0;
+               /* now sw_data is the position where ALSA is going to write
+                * in the intermediate buffer next time = position we are going
+                * to read from next time
+                */
+
+               snd_pcm_indirect2_increase_min_periods(substream, rec, 1, 1,
+                                                      bytes);
+
+               /* we read bytes from intermediate buffer, so we need to say
+                * that the number of bytes ready for transfer are decreased
+                * now
+                */
+               rec->sw_ready -= bytes;
+       }
+       return;
+}
+
+/*
+ * helper function for playback interrupt routine
+ */
+void
+snd_pcm_indirect2_playback_interrupt(struct snd_pcm_substream *substream,
+                                    struct snd_pcm_indirect2 *rec,
+                                    snd_pcm_indirect2_copy_t copy,
+                                    snd_pcm_indirect2_zero_t zero)
+{
+#ifdef SND_PCM_INDIRECT2_STAT
+       rec->irq_occured++;
+#endif
+       /* hardware played some bytes, so there is room again (in fifo) */
+       rec->hw_ready = 1;
+
+       /* don't call ack() now, instead call transfer() function directly
+        * (normally called by ack() )
+        */
+       snd_pcm_indirect2_playback_transfer(substream, rec, copy, zero);
+
+       if (rec->min_periods >= rec->min_multiple) {
+#ifdef SND_PCM_INDIRECT2_STAT
+               if ((rec->min_periods / rec->min_multiple) > 7)
+                       snd_printk(KERN_DEBUG
+                                  "STAT: more than 7 (%d) mul_adds - too big "
+                                  "to save!\n",
+                                  (rec->min_periods / rec->min_multiple));
+               else
+                       rec->mul_adds[(rec->min_periods /
+                                      rec->min_multiple)]++;
+               rec->mul_elapsed_real += (rec->min_periods /
+                                         rec->min_multiple);
+               rec->mul_elapsed++;
+#endif
+               rec->min_periods = (rec->min_periods % rec->min_multiple);
+               snd_pcm_period_elapsed(substream);
+       }
+}
+
+/*
+ * _internal_ helper function for capture interrupt callback
+ */
+static void
+snd_pcm_indirect2_capture_transfer(struct snd_pcm_substream *substream,
+                                  struct snd_pcm_indirect2 *rec,
+                                  snd_pcm_indirect2_copy_t copy,
+                                  snd_pcm_indirect2_zero_t null)
+{
+       struct snd_pcm_runtime *runtime = substream->runtime;
+       snd_pcm_uframes_t appl_ptr = runtime->control->appl_ptr;
+       snd_pcm_sframes_t diff = appl_ptr - rec->appl_ptr;
+
+       if (diff) {
+#ifdef SND_PCM_INDIRECT2_STAT
+               rec->lastdifftime = jiffies;
+#endif
+               if (diff < -(snd_pcm_sframes_t) (runtime->boundary / 2))
+                       diff += runtime->boundary;
+               rec->sw_ready -= frames_to_bytes(runtime, diff);
+               rec->appl_ptr = appl_ptr;
+       }
+       /* if hardware has something, but the intermediate buffer is full
+        * => skip contents of buffer
+        */
+       if (rec->hw_ready && (rec->sw_ready >= (int)rec->sw_buffer_size)) {
+               unsigned int bytes;
+
+#ifdef SND_PCM_INDIRECT2_STAT
+               if (rec->firstzerotime == 0) {
+                       rec->firstzerotime = jiffies;
+                       snd_printk(KERN_DEBUG "STAT: (capture) "
+                                  "@firstzerotime: mul_elapsed: %d, "
+                                  "min_period_count: %d\n",
+                                  rec->mul_elapsed, rec->min_period_count);
+                       snd_printk(KERN_DEBUG "STAT: (capture) "
+                                  "@firstzerotime: sw_io: %d, sw_data: %d, "
+                                  "appl_ptr: %u\n",
+                                  rec->sw_io, rec->sw_data,
+                                  (unsigned int)appl_ptr);
+               }
+               if ((jiffies - rec->firstzerotime) < 3750) {
+                       rec->zero_times[(jiffies - rec->firstzerotime)]++;
+                       rec->zero_times_saved++;
+               } else
+                       rec->zero_times_notsaved++;
+#endif
+               bytes = null(substream, rec);
+
+#ifdef SND_PCM_INDIRECT2_STAT
+               rec->zeros2hw += bytes;
+               if (bytes < 64)
+                       rec->zero_sizes[bytes]++;
+               else
+                       snd_printk(KERN_DEBUG
+                                  "STAT: (capture) %d zero Bytes copied to "
+                                  "hardware at once - too big to save!\n",
+                                  bytes);
+#endif
+               snd_pcm_indirect2_increase_min_periods(substream, rec, 0, 0,
+                                                      bytes);
+               /* report an overrun */
+               rec->sw_io = SNDRV_PCM_POS_XRUN;
+               return;
+       }
+       while (rec->hw_ready && (rec->sw_ready < (int)rec->sw_buffer_size)) {
+               /* sw_to_end: max. number of bytes that we can write to the
+                *  intermediate buffer (until it's end)
+                */
+               size_t sw_to_end = rec->sw_buffer_size - rec->sw_data;
+
+               /* bytes: max. number of bytes, which may be copied to the
+                *  intermediate buffer without overflow (in _one_ step)
+                */
+               size_t bytes = rec->sw_buffer_size - rec->sw_ready;
+
+               /* limit number of bytes (for transfer) by available room in
+                * the intermediate buffer
+                */
+               if (sw_to_end < bytes)
+                       bytes = sw_to_end;
+               if (!bytes)
+                       break;
+
+#ifdef SND_PCM_INDIRECT2_STAT
+               if (rec->firstbytetime == 0)
+                       rec->firstbytetime = jiffies;
+               rec->lastbytetime = jiffies;
+#endif
+               /* copy bytes from the intermediate buffer (position sw_data)
+                * to the HW at most and return number of bytes actually copied
+                * from HW
+                * Furthermore, set hw_ready to 0, if the fifo is empty now.
+                */
+               bytes = copy(substream, rec, bytes);
+               rec->bytes2hw += bytes;
+
+#ifdef SND_PCM_INDIRECT2_STAT
+               if (bytes < 64)
+                       rec->byte_sizes[bytes]++;
+               else
+                       snd_printk(KERN_DEBUG
+                                  "STAT: (capture) %d Bytes copied to "
+                                  "hardware at once - too big to save!\n",
+                                  bytes);
+#endif
+               /* increase sw_data by the number of actually copied bytes from
+                * HW
+                */
+               rec->sw_data += bytes;
+               if (rec->sw_data == rec->sw_buffer_size)
+                       rec->sw_data = 0;
+
+               snd_pcm_indirect2_increase_min_periods(substream, rec, 0, 1,
+                                                      bytes);
+
+               /* number of bytes in the intermediate buffer, which haven't
+                * been fetched by ALSA yet.
+                */
+               rec->sw_ready += bytes;
+       }
+       return;
+}
+
+/*
+ * helper function for capture interrupt routine
+ */
+void
+snd_pcm_indirect2_capture_interrupt(struct snd_pcm_substream *substream,
+                                   struct snd_pcm_indirect2 *rec,
+                                   snd_pcm_indirect2_copy_t copy,
+                                   snd_pcm_indirect2_zero_t null)
+{
+#ifdef SND_PCM_INDIRECT2_STAT
+       rec->irq_occured++;
+#endif
+       /* hardware recorded some bytes, so there is something to read from the
+        * record fifo:
+        */
+       rec->hw_ready = 1;
+
+       /* don't call ack() now, instead call transfer() function directly
+        * (normally called by ack() )
+        */
+       snd_pcm_indirect2_capture_transfer(substream, rec, copy, null);
+
+       if (rec->min_periods >= rec->min_multiple) {
+
+#ifdef SND_PCM_INDIRECT2_STAT
+               if ((rec->min_periods / rec->min_multiple) > 7)
+                       snd_printk(KERN_DEBUG
+                                  "STAT: more than 7 (%d) mul_adds - "
+                                  "too big to save!\n",
+                                  (rec->min_periods / rec->min_multiple));
+               else
+                       rec->mul_adds[(rec->min_periods /
+                                      rec->min_multiple)]++;
+               rec->mul_elapsed_real += (rec->min_periods /
+                                         rec->min_multiple);
+               rec->mul_elapsed++;
+#endif
+               rec->min_periods = (rec->min_periods % rec->min_multiple);
+               snd_pcm_period_elapsed(substream);
+       }
+}
diff --git a/sound/drivers/pcm-indirect2.h b/sound/drivers/pcm-indirect2.h
new file mode 100644 (file)
index 0000000..2ea6e46
--- /dev/null
@@ -0,0 +1,140 @@
+/*
+ * Helper functions for indirect PCM data transfer to a simple FIFO in
+ * hardware (small, no possibility to read "hardware io position",
+ * updating position done by interrupt, ...)
+ *
+ *  Copyright (c) by 2007  Joachim Foerster <JOFT@gmx.de>
+ *
+ *  Based on "pcm-indirect.h" (alsa-driver-1.0.13) by
+ *
+ *  Copyright (c) by Takashi Iwai <tiwai@suse.de>
+ *                   Jaroslav Kysela <perex@suse.cz>
+ *
+ *   This program is free software; you can redistribute it and/or modify
+ *   it under the terms of the GNU General Public License as published by
+ *   the Free Software Foundation; either version 2 of the License, or
+ *   (at your option) any later version.
+ *
+ *   This program is distributed in the hope that it will be useful,
+ *   but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *   GNU General Public License for more details.
+ *
+ *   You should have received a copy of the GNU General Public License
+ *   along with this program; if not, write to the Free Software
+ *   Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#ifndef __SOUND_PCM_INDIRECT2_H
+#define __SOUND_PCM_INDIRECT2_H
+
+/* struct snd_pcm_substream, struct snd_pcm_runtime, snd_pcm_uframes_t */
+#include <sound/pcm.h>
+
+/* Debug options for code which may be removed completely in a final version */
+#ifdef CONFIG_SND_DEBUG
+#define SND_PCM_INDIRECT2_STAT    /* turn on some "statistics" about the
+                                  * process of copying bytes from the
+                                  * intermediate buffer to the hardware
+                                  * fifo and the other way round
+                                  */
+#endif
+
+struct snd_pcm_indirect2 {
+       unsigned int hw_buffer_size;  /* Byte size of hardware buffer */
+       int hw_ready;                 /* playback: 1 = hw fifo has room left,
+                                      * 0 = hw fifo is full
+                                      */
+       unsigned int min_multiple;
+       int min_periods;              /* counts number of min. periods until
+                                      * min_multiple is reached
+                                      */
+       int min_period_count;         /* counts bytes to count number of
+                                      * min. periods
+                                      */
+
+       unsigned int sw_buffer_size;  /* Byte size of software buffer */
+
+       /* sw_data: position in intermediate buffer, where we will read (or
+        *          write) from/to next time (to transfer data to/from HW)
+        */
+       unsigned int sw_data;         /* Offset to next dst (or src) in sw
+                                      * ring buffer
+                                      */
+       /* easiest case (playback):
+        * sw_data is nearly the same as ~ runtime->control->appl_ptr, with the
+        * exception that sw_data is "behind" by the number if bytes ALSA wrote
+        * to the intermediate buffer last time.
+        * A call to ack() callback synchronizes both indirectly.
+        */
+
+       /* We have no real sw_io pointer here. Usually sw_io is pointing to the
+        * current playback/capture position _inside_ the hardware. Devices
+        * with plain FIFOs often have no possibility to publish this position.
+        * So we say: if sw_data is updated, that means bytes were copied to
+        * the hardware, we increase sw_io by that amount, because there have
+        * to be as much bytes which were played. So sw_io will stay behind
+        * sw_data all the time and has to converge to sw_data at the end of
+        * playback.
+        */
+       unsigned int sw_io;           /* Current software pointer in bytes */
+
+       /* sw_ready: number of bytes ALSA copied to the intermediate buffer, so
+        * it represents the number of bytes which wait for transfer to the HW
+        */
+       int sw_ready;             /* Bytes ready to be transferred to/from hw */
+
+       /* appl_ptr: last known position of ALSA (where ALSA is going to write
+        * next time into the intermediate buffer
+        */
+       snd_pcm_uframes_t appl_ptr;   /* Last seen appl_ptr */
+
+       unsigned int bytes2hw;
+       int check_alignment;
+
+#ifdef SND_PCM_INDIRECT2_STAT
+       unsigned int zeros2hw;
+       unsigned int mul_elapsed;
+       unsigned int mul_elapsed_real;
+       unsigned long firstbytetime;
+       unsigned long lastbytetime;
+       unsigned long firstzerotime;
+       unsigned int byte_sizes[64];
+       unsigned int zero_sizes[64];
+       unsigned int min_adds[8];
+       unsigned int mul_adds[8];
+       unsigned int zero_times[3750];  /* = 15s */
+       unsigned int zero_times_saved;
+       unsigned int zero_times_notsaved;
+       unsigned int irq_occured;
+       unsigned int pointer_calls;
+       unsigned int lastdifftime;
+#endif
+};
+
+typedef size_t (*snd_pcm_indirect2_copy_t) (struct snd_pcm_substream *substream,
+                                          struct snd_pcm_indirect2 *rec,
+                                          size_t bytes);
+typedef size_t (*snd_pcm_indirect2_zero_t) (struct snd_pcm_substream *substream,
+                                          struct snd_pcm_indirect2 *rec);
+
+#ifdef SND_PCM_INDIRECT2_STAT
+void snd_pcm_indirect2_stat(struct snd_pcm_substream *substream,
+                                  struct snd_pcm_indirect2 *rec);
+#endif
+
+snd_pcm_uframes_t
+snd_pcm_indirect2_pointer(struct snd_pcm_substream *substream,
+                         struct snd_pcm_indirect2 *rec);
+void
+snd_pcm_indirect2_playback_interrupt(struct snd_pcm_substream *substream,
+                                    struct snd_pcm_indirect2 *rec,
+                                    snd_pcm_indirect2_copy_t copy,
+                                    snd_pcm_indirect2_zero_t zero);
+void
+snd_pcm_indirect2_capture_interrupt(struct snd_pcm_substream *substream,
+                                   struct snd_pcm_indirect2 *rec,
+                                   snd_pcm_indirect2_copy_t copy,
+                                   snd_pcm_indirect2_zero_t null);
+
+#endif /* __SOUND_PCM_INDIRECT2_H */
index 1b832870cc84aabdfc19669b7d23378ce4a22c8f..b1c047ec19afe6e5ce9e6cf1acb2124c72719d45 100644 (file)
@@ -37,7 +37,6 @@
  *      - ported from alsa 0.5 to 1.0
  */
 
-#include <sound/driver.h>
 #include <linux/init.h>
 #include <linux/platform_device.h>
 #include <linux/parport.h>
@@ -797,6 +796,8 @@ static int __devinit snd_portman_probe(struct platform_device *pdev)
 
        platform_set_drvdata(pdev, card);
 
+       snd_card_set_dev(card, &pdev->dev);
+
        /* At this point card will be usable */
        if ((err = snd_card_register(card)) < 0) {
                snd_printd("Cannot register card\n");
index 65de3a755ddb69da85c15b74f2fb0e62acfccb4e..d8aab9da97c29ad8058f5be9751537b25b043500 100644 (file)
@@ -30,7 +30,6 @@
  *      More documentation can be found in serial-u16550.txt.
  */
 
-#include <sound/driver.h>
 #include <linux/init.h>
 #include <linux/interrupt.h>
 #include <linux/err.h>
@@ -43,6 +42,7 @@
 #include <sound/initval.h>
 
 #include <linux/serial_reg.h>
+#include <linux/jiffies.h>
 
 #include <asm/io.h>
 
@@ -455,7 +455,7 @@ static void snd_uart16550_do_open(struct snd_uart16550 * uart)
                    | UART_IER_THRI     /* Enable Transmitter holding register empty interrupt */
                    ;
        }
-       outb(byte, uart->base + UART_IER);      /* Interupt enable Register */
+       outb(byte, uart->base + UART_IER);      /* Interrupt enable Register */
 
        inb(uart->base + UART_LSR);     /* Clear any pre-existing overrun indication */
        inb(uart->base + UART_IIR);     /* Clear any pre-existing transmit interrupt */
@@ -473,7 +473,7 @@ static void snd_uart16550_do_close(struct snd_uart16550 * uart)
 
        outb((0 & UART_IER_RDI)         /* Disable Receiver data interrupt */
             |(0 & UART_IER_THRI)       /* Disable Transmitter holding register empty interrupt */
-            ,uart->base + UART_IER);   /* Interupt enable Register */
+            ,uart->base + UART_IER);   /* Interrupt enable Register */
 
        switch (uart->adaptor) {
        default:
@@ -653,7 +653,7 @@ static void snd_uart16550_output_write(struct snd_rawmidi_substream *substream)
        char first;
        static unsigned long lasttime = 0;
        
-       /* Interupts are disabled during the updating of the tx_buff,
+       /* Interrupts are disabled during the updating of the tx_buff,
         * since it is 'bad' to have two processes updating the same
         * variables (ie buff_in & buff_out)
         */
@@ -694,7 +694,7 @@ static void snd_uart16550_output_write(struct snd_rawmidi_substream *substream)
                            (uart->adaptor == SNDRV_SERIAL_SOUNDCANVAS ||
                             uart->adaptor == SNDRV_SERIAL_GENERIC) &&
                            (uart->prev_out != substream->number ||
-                            jiffies-lasttime > 3*HZ)) {
+                            time_after(jiffies, lasttime + 3*HZ))) {
 
                                if (snd_uart16550_buffer_can_write(uart, 3)) {
                                        /* Roland Soundcanvas part selection */
index 915c86773c21fbe0a4635a9f5508c2537834f1a8..f79e3614079d238aca26df870c863949bbda1e94 100644 (file)
@@ -41,7 +41,6 @@
  * - Run application using a midi device (eg. /dev/snd/midiC1D0)
  */
 
-#include <sound/driver.h>
 #include <linux/init.h>
 #include <linux/wait.h>
 #include <linux/err.h>
index 7a221349f2855eac887d5b5bbd4af0c156a19ece..9529e3bf2866309f808488e5ba46ac1732c6d684 100644 (file)
@@ -20,7 +20,6 @@
  *   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA
  */
 
-#include <sound/driver.h>
 #include <sound/core.h>
 #include <sound/pcm.h>
 #include <sound/vx_core.h>
index ed19bc17400ba8cf2112a29f7b8ff21f577b97c9..99538862e3428af01e3f26e5ed668a4f0c17baa6 100644 (file)
@@ -20,7 +20,6 @@
  *   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA
  */
 
-#include <sound/driver.h>
 #include <linux/delay.h>
 #include <linux/slab.h>
 #include <linux/interrupt.h>
index 9a8154c9416ed11c8dda6c1a38e695aca7aa25a5..1dfe6948e6ffdb14d36438dad0940e520f862338 100644 (file)
@@ -20,7 +20,6 @@
  *   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA
  */
 
-#include <sound/driver.h>
 #include <linux/device.h>
 #include <linux/firmware.h>
 #include <linux/vmalloc.h>
index b8fcd79a7e11ea041c7b45f90adc02a204eb1632..5a347321f8c037a384a310f696674daa06c7b118 100644 (file)
@@ -20,7 +20,6 @@
  *   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA
  */
 
-#include <sound/driver.h>
 #include <sound/core.h>
 #include <sound/control.h>
 #include <sound/tlv.h>
@@ -439,14 +438,19 @@ static int vx_output_level_put(struct snd_kcontrol *kcontrol, struct snd_ctl_ele
 {
        struct vx_core *chip = snd_kcontrol_chip(kcontrol);
        int codec = kcontrol->id.index;
+       unsigned int val[2], vmax;
+
+       vmax = chip->hw->output_level_max;
+       val[0] = ucontrol->value.integer.value[0];
+       val[1] = ucontrol->value.integer.value[1];
+       if (val[0] > vmax || val[1] > vmax)
+               return -EINVAL;
        mutex_lock(&chip->mixer_mutex);
-       if (ucontrol->value.integer.value[0] != chip->output_level[codec][0] ||
-           ucontrol->value.integer.value[1] != chip->output_level[codec][1]) {
-               vx_set_analog_output_level(chip, codec,
-                                          ucontrol->value.integer.value[0],
-                                          ucontrol->value.integer.value[1]);
-               chip->output_level[codec][0] = ucontrol->value.integer.value[0];
-               chip->output_level[codec][1] = ucontrol->value.integer.value[1];
+       if (val[0] != chip->output_level[codec][0] ||
+           val[1] != chip->output_level[codec][1]) {
+               vx_set_analog_output_level(chip, codec, val[0], val[1]);
+               chip->output_level[codec][0] = val[0];
+               chip->output_level[codec][1] = val[1];
                mutex_unlock(&chip->mixer_mutex);
                return 1;
        }
@@ -506,6 +510,14 @@ static int vx_audio_src_get(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_v
 static int vx_audio_src_put(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
 {
        struct vx_core *chip = snd_kcontrol_chip(kcontrol);
+
+       if (chip->type >= VX_TYPE_VXPOCKET) {
+               if (ucontrol->value.enumerated.item[0] > 2)
+                       return -EINVAL;
+       } else {
+               if (ucontrol->value.enumerated.item[0] > 1)
+                       return -EINVAL;
+       }
        mutex_lock(&chip->mixer_mutex);
        if (chip->audio_source_target != ucontrol->value.enumerated.item[0]) {
                chip->audio_source_target = ucontrol->value.enumerated.item[0];
@@ -554,6 +566,9 @@ static int vx_clock_mode_get(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_
 static int vx_clock_mode_put(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
 {
        struct vx_core *chip = snd_kcontrol_chip(kcontrol);
+
+       if (ucontrol->value.enumerated.item[0] > 2)
+               return -EINVAL;
        mutex_lock(&chip->mixer_mutex);
        if (chip->clock_mode != ucontrol->value.enumerated.item[0]) {
                chip->clock_mode = ucontrol->value.enumerated.item[0];
@@ -603,12 +618,17 @@ static int vx_audio_gain_put(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_
        struct vx_core *chip = snd_kcontrol_chip(kcontrol);
        int audio = kcontrol->private_value & 0xff;
        int capture = (kcontrol->private_value >> 8) & 1;
+       unsigned int val[2];
 
+       val[0] = ucontrol->value.integer.value[0];
+       val[1] = ucontrol->value.integer.value[1];
+       if (val[0] > CVAL_MAX || val[1] > CVAL_MAX)
+               return -EINVAL;
        mutex_lock(&chip->mixer_mutex);
-       if (ucontrol->value.integer.value[0] != chip->audio_gain[capture][audio] ||
-           ucontrol->value.integer.value[1] != chip->audio_gain[capture][audio+1]) {
-               vx_set_audio_gain(chip, audio, capture, ucontrol->value.integer.value[0]);
-               vx_set_audio_gain(chip, audio+1, capture, ucontrol->value.integer.value[1]);
+       if (val[0] != chip->audio_gain[capture][audio] ||
+           val[1] != chip->audio_gain[capture][audio+1]) {
+               vx_set_audio_gain(chip, audio, capture, val[0]);
+               vx_set_audio_gain(chip, audio+1, capture, val[1]);
                mutex_unlock(&chip->mixer_mutex);
                return 1;
        }
@@ -632,13 +652,19 @@ static int vx_audio_monitor_put(struct snd_kcontrol *kcontrol, struct snd_ctl_el
 {
        struct vx_core *chip = snd_kcontrol_chip(kcontrol);
        int audio = kcontrol->private_value & 0xff;
+       unsigned int val[2];
+
+       val[0] = ucontrol->value.integer.value[0];
+       val[1] = ucontrol->value.integer.value[1];
+       if (val[0] > CVAL_MAX || val[1] > CVAL_MAX)
+               return -EINVAL;
 
        mutex_lock(&chip->mixer_mutex);
-       if (ucontrol->value.integer.value[0] != chip->audio_monitor[audio] ||
-           ucontrol->value.integer.value[1] != chip->audio_monitor[audio+1]) {
-               vx_set_monitor_level(chip, audio, ucontrol->value.integer.value[0],
+       if (val[0] != chip->audio_monitor[audio] ||
+           val[1] != chip->audio_monitor[audio+1]) {
+               vx_set_monitor_level(chip, audio, val[0],
                                     chip->audio_monitor_active[audio]);
-               vx_set_monitor_level(chip, audio+1, ucontrol->value.integer.value[1],
+               vx_set_monitor_level(chip, audio+1, val[1],
                                     chip->audio_monitor_active[audio+1]);
                mutex_unlock(&chip->mixer_mutex);
                return 1;
@@ -669,8 +695,10 @@ static int vx_audio_sw_put(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_va
        mutex_lock(&chip->mixer_mutex);
        if (ucontrol->value.integer.value[0] != chip->audio_active[audio] ||
            ucontrol->value.integer.value[1] != chip->audio_active[audio+1]) {
-               vx_set_audio_switch(chip, audio, ucontrol->value.integer.value[0]);
-               vx_set_audio_switch(chip, audio+1, ucontrol->value.integer.value[1]);
+               vx_set_audio_switch(chip, audio,
+                                   !!ucontrol->value.integer.value[0]);
+               vx_set_audio_switch(chip, audio+1,
+                                   !!ucontrol->value.integer.value[1]);
                mutex_unlock(&chip->mixer_mutex);
                return 1;
        }
@@ -699,9 +727,9 @@ static int vx_monitor_sw_put(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_
        if (ucontrol->value.integer.value[0] != chip->audio_monitor_active[audio] ||
            ucontrol->value.integer.value[1] != chip->audio_monitor_active[audio+1]) {
                vx_set_monitor_level(chip, audio, chip->audio_monitor[audio],
-                                    ucontrol->value.integer.value[0]);
+                                    !!ucontrol->value.integer.value[0]);
                vx_set_monitor_level(chip, audio+1, chip->audio_monitor[audio+1],
-                                    ucontrol->value.integer.value[1]);
+                                    !!ucontrol->value.integer.value[1]);
                mutex_unlock(&chip->mixer_mutex);
                return 1;
        }
index 7e65a103fbb2f357ebbf59789cbac13c81aea8f5..fdbf86571b1f3f528233b3c13537e78728a2028c 100644 (file)
@@ -45,7 +45,6 @@
  *  - scheduled action on the stream.
  */
 
-#include <sound/driver.h>
 #include <linux/slab.h>
 #include <linux/vmalloc.h>
 #include <linux/delay.h>
index 7400306b7f285a57a107cc3819331f76db2571b3..fb8932af888d6a523fc5c33f489acb61e5b2d82f 100644 (file)
@@ -20,7 +20,6 @@
  *   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA
  */
 
-#include <sound/driver.h>
 #include <linux/delay.h>
 #include <sound/core.h>
 #include <sound/vx_core.h>
index 744366b72345ae4e8a7c32790b12fe828d9f0bf5..e57e9cbe6a0f99aa36d8090a222c91b76b555771 100644 (file)
@@ -20,7 +20,6 @@
  *
  */
 
-#include <sound/driver.h>
 #include <linux/slab.h>
 #include <linux/delay.h>
 #include <linux/init.h>
index 1e58a963b2a74b60a64bd3cdcf3ba10ad6f9fb1a..b1e74e40cba050656879dbe529a72adb218397c3 100644 (file)
@@ -20,7 +20,6 @@
  *
  */
 
-#include <sound/driver.h>
 #include <linux/init.h>
 #include <linux/slab.h>
 #include <linux/string.h>
index b074fdddea55d760477743da8025ed052884e75f..bfa5d2c3608bfed3d412165357a3889b4205d24e 100644 (file)
@@ -19,7 +19,6 @@
 
 /* $Id: uda1341.c,v 1.18 2005/11/17 14:17:21 tiwai Exp $ */
 
-#include <sound/driver.h>
 #include <linux/module.h>
 #include <linux/init.h>
 #include <linux/types.h>
index facde46f957a68ebfcacf0a770ab3675aa83ced9..15061bd72776a0eacbb418d7abb1125bda5398cf 100644 (file)
@@ -20,7 +20,6 @@
  *
  */
 
-#include <sound/driver.h>
 #include <linux/slab.h>
 #include <linux/delay.h>
 #include <sound/core.h>
index ee1585aec99b0e9c6c4b7e7d36194fc62431aaa5..f350835ade96cced97ae9815de8d71a09e3cf86b 100644 (file)
@@ -20,7 +20,6 @@
  *
  */
 
-#include <sound/driver.h>
 #include <linux/slab.h>
 #include <linux/delay.h>
 #include <sound/core.h>
index de03f689fa2e04dd4f951fbcbb7c272408723b4f..35fbbf2cb9fa84bb47241c2de5ce7c9c9941e323 100644 (file)
@@ -21,7 +21,6 @@
  *
  */      
 
-#include <sound/driver.h>
 #include <asm/io.h>
 #include <linux/delay.h>
 #include <linux/interrupt.h>
@@ -293,6 +292,11 @@ void snd_akm4xxx_init(struct snd_akm4xxx *ak)
        case SND_AK5365:
                /* FIXME: any init sequence? */
                return;
+       case NON_AKM:
+               /* fake value for non-akm codecs using akm infrastructure
+                * (e.g. of ice1724) - certainly FIXME
+                */
+               return;
        default:
                snd_BUG();
                return;
@@ -377,8 +381,11 @@ static int put_ak_reg(struct snd_kcontrol *kcontrol, int addr,
 static int snd_akm4xxx_volume_put(struct snd_kcontrol *kcontrol,
                                  struct snd_ctl_elem_value *ucontrol)
 {
-       return put_ak_reg(kcontrol, AK_GET_ADDR(kcontrol->private_value),
-                         ucontrol->value.integer.value[0]);
+       unsigned int mask = AK_GET_MASK(kcontrol->private_value);
+       unsigned int val = ucontrol->value.integer.value[0];
+       if (val > mask)
+               return -EINVAL;
+       return put_ak_reg(kcontrol, AK_GET_ADDR(kcontrol->private_value), val);
 }
 
 static int snd_akm4xxx_stereo_volume_info(struct snd_kcontrol *kcontrol,
@@ -409,11 +416,16 @@ static int snd_akm4xxx_stereo_volume_put(struct snd_kcontrol *kcontrol,
                                         struct snd_ctl_elem_value *ucontrol)
 {
        int addr = AK_GET_ADDR(kcontrol->private_value);
+       unsigned int mask = AK_GET_MASK(kcontrol->private_value);
+       unsigned int val[2];
        int change;
 
-       change = put_ak_reg(kcontrol, addr, ucontrol->value.integer.value[0]);
-       change |= put_ak_reg(kcontrol, addr + 1,
-                            ucontrol->value.integer.value[1]);
+       val[0] = ucontrol->value.integer.value[0];
+       val[1] = ucontrol->value.integer.value[1];
+       if (val[0] > mask || val[1] > mask)
+               return -EINVAL;
+       change = put_ak_reg(kcontrol, addr, val[0]);
+       change |= put_ak_reg(kcontrol, addr + 1, val[1]);
        return change;
 }
 
@@ -508,6 +520,18 @@ static int ak4xxx_switch_put(struct snd_kcontrol *kcontrol,
 
 #define AK5365_NUM_INPUTS 5
 
+static int ak4xxx_capture_num_inputs(struct snd_akm4xxx *ak, int mixer_ch)
+{
+       int num_names;
+       const char **input_names;
+
+       input_names = ak->adc_info[mixer_ch].input_names;
+       num_names = 0;
+       while (num_names < AK5365_NUM_INPUTS && input_names[num_names])
+               ++num_names;
+       return num_names;
+}
+
 static int ak4xxx_capture_source_info(struct snd_kcontrol *kcontrol,
                                      struct snd_ctl_elem_info *uinfo)
 {
@@ -516,18 +540,16 @@ static int ak4xxx_capture_source_info(struct snd_kcontrol *kcontrol,
        const char **input_names;
        int  num_names, idx;
 
-       input_names = ak->adc_info[mixer_ch].input_names;
-
-       num_names = 0;
-       while (num_names < AK5365_NUM_INPUTS && input_names[num_names])
-               ++num_names;
-       
+       num_names = ak4xxx_capture_num_inputs(ak, mixer_ch);
+       if (!num_names)
+               return -EINVAL;
        uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED;
        uinfo->count = 1;
        uinfo->value.enumerated.items = num_names;
        idx = uinfo->value.enumerated.item;
        if (idx >= num_names)
                return -EINVAL;
+       input_names = ak->adc_info[mixer_ch].input_names;
        strncpy(uinfo->value.enumerated.name, input_names[idx],
                sizeof(uinfo->value.enumerated.name));
        return 0;
@@ -551,10 +573,15 @@ static int ak4xxx_capture_source_put(struct snd_kcontrol *kcontrol,
                                     struct snd_ctl_elem_value *ucontrol)
 {
        struct snd_akm4xxx *ak = snd_kcontrol_chip(kcontrol);
+       int mixer_ch = AK_GET_SHIFT(kcontrol->private_value);
        int chip = AK_GET_CHIP(kcontrol->private_value);
        int addr = AK_GET_ADDR(kcontrol->private_value);
        int mask = AK_GET_MASK(kcontrol->private_value);
        unsigned char oval, val;
+       int num_names = ak4xxx_capture_num_inputs(ak, mixer_ch);
+
+       if (ucontrol->value.enumerated.item[0] >= num_names)
+               return -EINVAL;
 
        oval = snd_akm4xxx_get(ak, chip, addr);
        val = oval & ~mask;
index 00c83d8b32b11c1b195f886ff9b4e6ff2b31f2ca..797d3a6687ebf7557308fd5a1d14201a230e8dbe 100644 (file)
@@ -19,7 +19,6 @@
  *
  */      
 
-#include <sound/driver.h>
 #include <sound/core.h>
 #include <sound/control.h>
 #include <sound/tlv.h>
@@ -113,6 +112,8 @@ static int pt2258_stereo_volume_put(struct snd_kcontrol *kcontrol,
 
        val0 = 79 - ucontrol->value.integer.value[0];
        val1 = 79 - ucontrol->value.integer.value[1];
+       if (val0 < 0 || val0 > 79 || val1 < 0 || val1 > 79)
+               return -EINVAL;
        if (val0 == pt->volume[base] && val1 == pt->volume[base + 1])
                return 0;
 
index 37c47fb95aca43fa63058143ee4edf7fffe7be79..87e3aefeddc39bf73f2b16465b71f5dc95d16795 100644 (file)
@@ -20,7 +20,6 @@
  *
  */      
 
-#include <sound/driver.h>
 #include <asm/io.h>
 #include <linux/delay.h>
 #include <linux/interrupt.h>
@@ -159,6 +158,10 @@ static int snd_tea575x_ioctl(struct inode *inode, struct file *file,
                        struct video_audio v;
                        if(copy_from_user(&v, arg, sizeof(v))) 
                                return -EFAULT; 
+                       if (tea->ops->mute)
+                               tea->ops->mute(tea,
+                                              (v.flags &
+                                               VIDEO_AUDIO_MUTE) ? 1 : 0);
                        if(v.audio) 
                                return -EINVAL;
                        return 0;
@@ -206,6 +209,10 @@ void snd_tea575x_init(struct snd_tea575x *tea)
        tea->freq = 90500 * 16;         /* 90.5Mhz default */
 
        snd_tea575x_set_freq(tea);
+
+       /* mute on init */
+       if (tea->ops->mute)
+               tea->ops->mute(tea, 1);
 }
 
 void snd_tea575x_exit(struct snd_tea575x *tea)
index 9bab744af0ef9ea364695c280b161c53fe2c9a47..0e3a9f2c5297658dd001782734dad639e9daf811 100644 (file)
@@ -20,7 +20,6 @@
  *
  */
 
-#include <sound/driver.h>
 #include <linux/init.h>
 #include <linux/slab.h>
 #include <sound/core.h>
index fc88a31da6f51a6730d75e6c2cd2e058ac5d1781..68f1260b5602ef119a5844618e5cac9c671673b0 100644 (file)
@@ -18,7 +18,6 @@
     Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA
 */
 
-#include <sound/driver.h>
 #include <linux/init.h>
 #include <linux/time.h>
 #include <linux/wait.h>
@@ -61,20 +60,6 @@ module_param_array(id, charp, NULL, 0444);
 MODULE_PARM_DESC(id, "ID string for ad1816a based soundcard.");
 module_param_array(enable, bool, NULL, 0444);
 MODULE_PARM_DESC(enable, "Enable ad1816a based soundcard.");
-module_param_array(port, long, NULL, 0444);
-MODULE_PARM_DESC(port, "Port # for ad1816a driver.");
-module_param_array(mpu_port, long, NULL, 0444);
-MODULE_PARM_DESC(mpu_port, "MPU-401 port # for ad1816a driver.");
-module_param_array(fm_port, long, NULL, 0444);
-MODULE_PARM_DESC(fm_port, "FM port # for ad1816a driver.");
-module_param_array(irq, int, NULL, 0444);
-MODULE_PARM_DESC(irq, "IRQ # for ad1816a driver.");
-module_param_array(mpu_irq, int, NULL, 0444);
-MODULE_PARM_DESC(mpu_irq, "MPU-401 IRQ # for ad1816a driver.");
-module_param_array(dma1, int, NULL, 0444);
-MODULE_PARM_DESC(dma1, "1st DMA # for ad1816a driver.");
-module_param_array(dma2, int, NULL, 0444);
-MODULE_PARM_DESC(dma2, "2nd DMA # for ad1816a driver.");
 module_param_array(clockfreq, int, NULL, 0444);
 MODULE_PARM_DESC(clockfreq, "Clock frequency for ad1816a driver (default = 0).");
 
@@ -117,16 +102,12 @@ static int __devinit snd_card_ad1816a_pnp(int dev, struct snd_card_ad1816a *acar
                                          const struct pnp_card_device_id *id)
 {
        struct pnp_dev *pdev;
-       struct pnp_resource_table *cfg = kmalloc(sizeof(*cfg), GFP_KERNEL);
        int err;
 
-       if (!cfg)
-               return -ENOMEM;
        acard->dev = pnp_request_card_device(card, id->devs[0].id, NULL);
-       if (acard->dev == NULL) {
-               kfree(cfg);
+       if (acard->dev == NULL)
                return -EBUSY;
-       }
+
        acard->devmpu = pnp_request_card_device(card, id->devs[1].id, NULL);
        if (acard->devmpu == NULL) {
                mpu_port[dev] = -1;
@@ -134,25 +115,10 @@ static int __devinit snd_card_ad1816a_pnp(int dev, struct snd_card_ad1816a *acar
        }
 
        pdev = acard->dev;
-       pnp_init_resource_table(cfg);
-
-       if (port[dev] != SNDRV_AUTO_PORT)
-               pnp_resource_change(&cfg->port_resource[2], port[dev], 16);
-       if (fm_port[dev] != SNDRV_AUTO_PORT)
-               pnp_resource_change(&cfg->port_resource[1], fm_port[dev], 4);
-       if (dma1[dev] != SNDRV_AUTO_DMA)
-               pnp_resource_change(&cfg->dma_resource[0], dma1[dev], 1);
-       if (dma2[dev] != SNDRV_AUTO_DMA)
-               pnp_resource_change(&cfg->dma_resource[1], dma2[dev], 1);
-       if (irq[dev] != SNDRV_AUTO_IRQ)
-               pnp_resource_change(&cfg->irq_resource[0], irq[dev], 1);
-
-       if (pnp_manual_config_dev(pdev, cfg, 0) < 0)
-               snd_printk(KERN_ERR PFX "AUDIO the requested resources are invalid, using auto config\n");
+
        err = pnp_activate_dev(pdev);
        if (err < 0) {
                printk(KERN_ERR PFX "AUDIO PnP configure failure\n");
-               kfree(cfg);
                return -EBUSY;
        }
 
@@ -162,20 +128,11 @@ static int __devinit snd_card_ad1816a_pnp(int dev, struct snd_card_ad1816a *acar
        dma2[dev] = pnp_dma(pdev, 1);
        irq[dev] = pnp_irq(pdev, 0);
 
-       if (acard->devmpu == NULL) {
-               kfree(cfg);
+       if (acard->devmpu == NULL)
                return 0;
-       }
-       pdev = acard->devmpu;
-       pnp_init_resource_table(cfg);
 
-       if (mpu_port[dev] != SNDRV_AUTO_PORT)
-               pnp_resource_change(&cfg->port_resource[0], mpu_port[dev], 2);
-       if (mpu_irq[dev] != SNDRV_AUTO_IRQ)
-               pnp_resource_change(&cfg->irq_resource[0], mpu_irq[dev], 1);
+       pdev = acard->devmpu;
 
-       if (pnp_manual_config_dev(pdev, cfg, 0) < 0)
-               snd_printk(KERN_ERR PFX "AUDIO the requested resources are invalid, using auto config\n");
        err = pnp_activate_dev(pdev);
        if (err < 0) {
                printk(KERN_ERR PFX "MPU401 PnP configure failure\n");
@@ -186,7 +143,6 @@ static int __devinit snd_card_ad1816a_pnp(int dev, struct snd_card_ad1816a *acar
                mpu_irq[dev] = pnp_irq(pdev, 0);
        }
 
-       kfree(cfg);
        return 0;
 }
 
index cf18fe4617a1e1f23848eed4965ce67a7d1b4b6b..4b8dfe2e3dcb69df0767b43df62ef2b9cf0d44a9 100644 (file)
@@ -17,7 +17,6 @@
     Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA
 */
 
-#include <sound/driver.h>
 #include <linux/delay.h>
 #include <linux/init.h>
 #include <linux/interrupt.h>
index a4710b5e214c339f6549acddc739393dffc963cb..5f5271efdc59fe524b2510ba30ed9b4c83091f8a 100644 (file)
@@ -21,7 +21,6 @@
  *
  */
 
-#include <sound/driver.h>
 #include <linux/init.h>
 #include <linux/err.h>
 #include <linux/isa.h>
index a901cd1ee692474316cd7bf5ff34d926e3578a83..630c90f9ee5091ccf01108f7b7a09aed5e77930d 100644 (file)
@@ -20,7 +20,6 @@
  */
 
 #define SNDRV_MAIN_OBJECT_FILE
-#include <sound/driver.h>
 #include <linux/delay.h>
 #include <linux/init.h>
 #include <linux/interrupt.h>
@@ -213,7 +212,7 @@ static void snd_ad1848_mce_down(struct snd_ad1848 *chip)
        for (timeout = 12000; timeout > 0 && (inb(AD1848P(chip, REGSEL)) & AD1848_INIT); timeout--)
                udelay(100);
 
-       snd_printdd("(1) timeout = %d\n", timeout);
+       snd_printdd("(1) timeout = %ld\n", timeout);
 
 #ifdef CONFIG_SND_DEBUG
        if (inb(AD1848P(chip, REGSEL)) & AD1848_INIT)
index d68720724c91903cd593452e74c81029d6097317..efa8c80d05b69eecc9b03795e609b60b497bd77a 100644 (file)
@@ -2,7 +2,6 @@
  * AdLib FM card driver.
  */
 
-#include <sound/driver.h>
 #include <linux/kernel.h>
 #include <linux/module.h>
 #include <linux/isa.h>
index f2bcfb2cf5f576bae36135c33524ba2d1d3fd40e..f1ce30f379c9d5fa06ef68b62b5643b08596871c 100644 (file)
@@ -20,7 +20,6 @@
     Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA
 */
 
-#include <sound/driver.h>
 #include <linux/init.h>
 #include <linux/wait.h>
 #include <linux/time.h>
@@ -63,20 +62,6 @@ module_param_array(id, charp, NULL, 0444);
 MODULE_PARM_DESC(id, "ID string for als100 based soundcard.");
 module_param_array(enable, bool, NULL, 0444);
 MODULE_PARM_DESC(enable, "Enable als100 based soundcard.");
-module_param_array(port, long, NULL, 0444);
-MODULE_PARM_DESC(port, "Port # for als100 driver.");
-module_param_array(mpu_port, long, NULL, 0444);
-MODULE_PARM_DESC(mpu_port, "MPU-401 port # for als100 driver.");
-module_param_array(fm_port, long, NULL, 0444);
-MODULE_PARM_DESC(fm_port, "FM port # for als100 driver.");
-module_param_array(irq, int, NULL, 0444);
-MODULE_PARM_DESC(irq, "IRQ # for als100 driver.");
-module_param_array(mpu_irq, int, NULL, 0444);
-MODULE_PARM_DESC(mpu_irq, "MPU-401 IRQ # for als100 driver.");
-module_param_array(dma8, int, NULL, 0444);
-MODULE_PARM_DESC(dma8, "8-bit DMA # for als100 driver.");
-module_param_array(dma16, int, NULL, 0444);
-MODULE_PARM_DESC(dma16, "16-bit DMA # for als100 driver.");
 
 struct snd_card_als100 {
        int dev_no;
@@ -111,38 +96,20 @@ static int __devinit snd_card_als100_pnp(int dev, struct snd_card_als100 *acard,
                                         const struct pnp_card_device_id *id)
 {
        struct pnp_dev *pdev;
-       struct pnp_resource_table *cfg = kmalloc(sizeof(*cfg), GFP_KERNEL);
        int err;
 
-       if (!cfg)
-               return -ENOMEM;
        acard->dev = pnp_request_card_device(card, id->devs[0].id, NULL);
-       if (acard->dev == NULL) {
-               kfree(cfg);
+       if (acard->dev == NULL)
                return -ENODEV;
-       }
+
        acard->devmpu = pnp_request_card_device(card, id->devs[1].id, acard->dev);
        acard->devopl = pnp_request_card_device(card, id->devs[2].id, acard->dev);
 
        pdev = acard->dev;
 
-       pnp_init_resource_table(cfg);
-
-       /* override resources */
-       if (port[dev] != SNDRV_AUTO_PORT)
-               pnp_resource_change(&cfg->port_resource[0], port[dev], 16);
-       if (dma8[dev] != SNDRV_AUTO_DMA)
-               pnp_resource_change(&cfg->dma_resource[0], dma8[dev], 1);
-       if (dma16[dev] != SNDRV_AUTO_DMA)
-               pnp_resource_change(&cfg->dma_resource[1], dma16[dev], 1);
-       if (irq[dev] != SNDRV_AUTO_IRQ)
-               pnp_resource_change(&cfg->irq_resource[0], irq[dev], 1);
-       if (pnp_manual_config_dev(pdev, cfg, 0) < 0)
-               snd_printk(KERN_ERR PFX "AUDIO the requested resources are invalid, using auto config\n");
        err = pnp_activate_dev(pdev);
        if (err < 0) {
                snd_printk(KERN_ERR PFX "AUDIO pnp configure failure\n");
-               kfree(cfg);
                return err;
        }
        port[dev] = pnp_port_start(pdev, 0);
@@ -152,13 +119,6 @@ static int __devinit snd_card_als100_pnp(int dev, struct snd_card_als100 *acard,
 
        pdev = acard->devmpu;
        if (pdev != NULL) {
-               pnp_init_resource_table(cfg);
-               if (mpu_port[dev] != SNDRV_AUTO_PORT)
-                       pnp_resource_change(&cfg->port_resource[0], mpu_port[dev], 2);
-               if (mpu_irq[dev] != SNDRV_AUTO_IRQ)
-                       pnp_resource_change(&cfg->irq_resource[0], mpu_irq[dev], 1);
-               if ((pnp_manual_config_dev(pdev, cfg, 0)) < 0)
-                       snd_printk(KERN_ERR PFX "MPU401 the requested resources are invalid, using auto config\n");
                err = pnp_activate_dev(pdev);
                if (err < 0)
                        goto __mpu_error;
@@ -176,11 +136,6 @@ static int __devinit snd_card_als100_pnp(int dev, struct snd_card_als100 *acard,
 
        pdev = acard->devopl;
        if (pdev != NULL) {
-               pnp_init_resource_table(cfg);
-               if (fm_port[dev] != SNDRV_AUTO_PORT)
-                       pnp_resource_change(&cfg->port_resource[0], fm_port[dev], 4);
-               if ((pnp_manual_config_dev(pdev, cfg, 0)) < 0)
-                       snd_printk(KERN_ERR PFX "OPL3 the requested resources are invalid, using auto config\n");
                err = pnp_activate_dev(pdev);
                if (err < 0)
                        goto __fm_error;
@@ -195,7 +150,6 @@ static int __devinit snd_card_als100_pnp(int dev, struct snd_card_als100 *acard,
                fm_port[dev] = -1;
        }
 
-       kfree(cfg);
        return 0;
 }
 
index b615538a928d6e5570a480fd628e210c244bf044..154e728f592d0f7cd8aa523025e01aa5e0d7b7c5 100644 (file)
@@ -29,7 +29,6 @@
     activation method (full-duplex audio!).
 */
 
-#include <sound/driver.h>
 #include <asm/io.h>
 #include <linux/delay.h>
 #include <linux/init.h>
@@ -72,22 +71,6 @@ module_param_array(id, charp, NULL, 0444);
 MODULE_PARM_DESC(id, "ID string for azt2320 based soundcard.");
 module_param_array(enable, bool, NULL, 0444);
 MODULE_PARM_DESC(enable, "Enable azt2320 based soundcard.");
-module_param_array(port, long, NULL, 0444);
-MODULE_PARM_DESC(port, "Port # for azt2320 driver.");
-module_param_array(wss_port, long, NULL, 0444);
-MODULE_PARM_DESC(wss_port, "WSS Port # for azt2320 driver.");
-module_param_array(mpu_port, long, NULL, 0444);
-MODULE_PARM_DESC(mpu_port, "MPU-401 port # for azt2320 driver.");
-module_param_array(fm_port, long, NULL, 0444);
-MODULE_PARM_DESC(fm_port, "FM port # for azt2320 driver.");
-module_param_array(irq, int, NULL, 0444);
-MODULE_PARM_DESC(irq, "IRQ # for azt2320 driver.");
-module_param_array(mpu_irq, int, NULL, 0444);
-MODULE_PARM_DESC(mpu_irq, "MPU-401 IRQ # for azt2320 driver.");
-module_param_array(dma1, int, NULL, 0444);
-MODULE_PARM_DESC(dma1, "1st DMA # for azt2320 driver.");
-module_param_array(dma2, int, NULL, 0444);
-MODULE_PARM_DESC(dma2, "2nd DMA # for azt2320 driver.");
 
 struct snd_card_azt2320 {
        int dev_no;
@@ -121,43 +104,19 @@ static int __devinit snd_card_azt2320_pnp(int dev, struct snd_card_azt2320 *acar
                                          const struct pnp_card_device_id *id)
 {
        struct pnp_dev *pdev;
-       struct pnp_resource_table * cfg = kmalloc(sizeof(struct pnp_resource_table), GFP_KERNEL);
        int err;
 
-       if (!cfg)
-               return -ENOMEM;
-
        acard->dev = pnp_request_card_device(card, id->devs[0].id, NULL);
-       if (acard->dev == NULL) {
-               kfree(cfg);
+       if (acard->dev == NULL)
                return -ENODEV;
-       }
 
        acard->devmpu = pnp_request_card_device(card, id->devs[1].id, NULL);
 
        pdev = acard->dev;
-       pnp_init_resource_table(cfg);
-
-       /* override resources */
-       if (port[dev] != SNDRV_AUTO_PORT)
-               pnp_resource_change(&cfg->port_resource[0], port[dev], 16);
-       if (fm_port[dev] != SNDRV_AUTO_PORT)
-               pnp_resource_change(&cfg->port_resource[1], fm_port[dev], 4);
-       if (wss_port[dev] != SNDRV_AUTO_PORT)
-               pnp_resource_change(&cfg->port_resource[2], wss_port[dev], 4);
-       if (dma1[dev] != SNDRV_AUTO_DMA)
-               pnp_resource_change(&cfg->dma_resource[0], dma1[dev], 1);
-       if (dma2[dev] != SNDRV_AUTO_DMA)
-               pnp_resource_change(&cfg->dma_resource[1], dma2[dev], 1);
-       if (irq[dev] != SNDRV_AUTO_IRQ)
-               pnp_resource_change(&cfg->irq_resource[0], irq[dev], 1);
-       if ((pnp_manual_config_dev(pdev, cfg, 0)) < 0)
-               snd_printk(KERN_ERR PFX "AUDIO the requested resources are invalid, using auto config\n");
 
        err = pnp_activate_dev(pdev);
        if (err < 0) {
                snd_printk(KERN_ERR PFX "AUDIO pnp configure failure\n");
-               kfree(cfg);
                return err;
        }
        port[dev] = pnp_port_start(pdev, 0);
@@ -169,13 +128,6 @@ static int __devinit snd_card_azt2320_pnp(int dev, struct snd_card_azt2320 *acar
 
        pdev = acard->devmpu;
        if (pdev != NULL) {
-               pnp_init_resource_table(cfg);
-               if (mpu_port[dev] != SNDRV_AUTO_PORT)
-                       pnp_resource_change(&cfg->port_resource[0], mpu_port[dev], 2);
-               if (mpu_irq[dev] != SNDRV_AUTO_IRQ)
-                       pnp_resource_change(&cfg->irq_resource[0], mpu_irq[dev], 1);
-               if ((pnp_manual_config_dev(pdev, cfg, 0)) < 0)
-                       snd_printk(KERN_ERR PFX "MPU401 the requested resources are invalid, using auto config\n");
                err = pnp_activate_dev(pdev);
                if (err < 0)
                        goto __mpu_error;
@@ -191,7 +143,6 @@ static int __devinit snd_card_azt2320_pnp(int dev, struct snd_card_azt2320 *acar
                mpu_port[dev] = -1;
        }
 
-       kfree (cfg);
        return 0;
 }
 
index f471f8ad68852d1dbbe09b684668be39a8511cbd..4d198ec71e9b410aad2100e189c3293453d3129c 100644 (file)
@@ -43,7 +43,6 @@
  *  full control over both mixers.
  */
 
-#include <sound/driver.h>
 #include <linux/init.h>
 #include <linux/err.h>
 #include <linux/isa.h>
@@ -286,39 +285,21 @@ static int __devinit snd_cmi8330_pnp(int dev, struct snd_cmi8330 *acard,
                                     const struct pnp_card_device_id *id)
 {
        struct pnp_dev *pdev;
-       struct pnp_resource_table * cfg = kmalloc(sizeof(struct pnp_resource_table), GFP_KERNEL);
        int err;
 
-       if (!cfg)
-               return -ENOMEM;
        acard->cap = pnp_request_card_device(card, id->devs[0].id, NULL);
-       if (acard->cap == NULL) {
-               kfree(cfg);
+       if (acard->cap == NULL)
                return -EBUSY;
-       }
+
        acard->play = pnp_request_card_device(card, id->devs[1].id, NULL);
-       if (acard->play == NULL) {
-               kfree(cfg);
+       if (acard->play == NULL)
                return -EBUSY;
-       }
 
        pdev = acard->cap;
-       pnp_init_resource_table(cfg);
-       /* allocate AD1848 resources */
-       if (wssport[dev] != SNDRV_AUTO_PORT)
-               pnp_resource_change(&cfg->port_resource[0], wssport[dev], 8);
-       if (wssdma[dev] != SNDRV_AUTO_DMA)
-               pnp_resource_change(&cfg->dma_resource[0], wssdma[dev], 1);
-       if (wssirq[dev] != SNDRV_AUTO_IRQ)
-               pnp_resource_change(&cfg->irq_resource[0], wssirq[dev], 1);
-
-       err = pnp_manual_config_dev(pdev, cfg, 0);
-       if (err < 0)
-               snd_printk(KERN_ERR "CMI8330/C3D (AD1848) PnP manual resources are invalid, using auto config\n");
+
        err = pnp_activate_dev(pdev);
        if (err < 0) {
                snd_printk(KERN_ERR "CMI8330/C3D (AD1848) PnP configure failure\n");
-               kfree(cfg);
                return -EBUSY;
        }
        wssport[dev] = pnp_port_start(pdev, 0);
@@ -327,23 +308,10 @@ static int __devinit snd_cmi8330_pnp(int dev, struct snd_cmi8330 *acard,
 
        /* allocate SB16 resources */
        pdev = acard->play;
-       pnp_init_resource_table(cfg);
-       if (sbport[dev] != SNDRV_AUTO_PORT)
-               pnp_resource_change(&cfg->port_resource[0], sbport[dev], 16);
-       if (sbdma8[dev] != SNDRV_AUTO_DMA)
-               pnp_resource_change(&cfg->dma_resource[0], sbdma8[dev], 1);
-       if (sbdma16[dev] != SNDRV_AUTO_DMA)
-               pnp_resource_change(&cfg->dma_resource[1], sbdma16[dev], 1);
-       if (sbirq[dev] != SNDRV_AUTO_IRQ)
-               pnp_resource_change(&cfg->irq_resource[0], sbirq[dev], 1);
-
-       err = pnp_manual_config_dev(pdev, cfg, 0);
-       if (err < 0)
-               snd_printk(KERN_ERR "CMI8330/C3D (SB16) PnP manual resources are invalid, using auto config\n");
+
        err = pnp_activate_dev(pdev);
        if (err < 0) {
                snd_printk(KERN_ERR "CMI8330/C3D (SB16) PnP configure failure\n");
-               kfree(cfg);
                return -EBUSY;
        }
        sbport[dev] = pnp_port_start(pdev, 0);
@@ -351,7 +319,6 @@ static int __devinit snd_cmi8330_pnp(int dev, struct snd_cmi8330 *acard,
        sbdma16[dev] = pnp_dma(pdev, 1);
        sbirq[dev] = pnp_irq(pdev, 0);
 
-       kfree(cfg);
        return 0;
 }
 #endif
index 13db6842eaaae188b2d560bdea162f6270d5c9a7..e9462b9944be1c8b7ee3e987ca50d01134d056ee 100644 (file)
@@ -20,7 +20,6 @@
  *
  */
 
-#include <sound/driver.h>
 #include <linux/init.h>
 #include <linux/err.h>
 #include <linux/isa.h>
index a5eb9659b519ca8003034651654d9bef5bb98af9..0aa8649e5c7f897b1c5fb283b69dcd5df5f56cfc 100644 (file)
@@ -24,7 +24,6 @@
  *
  */
 
-#include <sound/driver.h>
 #include <linux/delay.h>
 #include <linux/pm.h>
 #include <linux/init.h>
@@ -333,7 +332,6 @@ void snd_cs4231_mce_down(struct snd_cs4231 *chip)
            !(chip->hardware & (CS4231_HW_CS4231_MASK | CS4231_HW_CS4232_MASK))) {
                return;
        }
-       snd_cs4231_busy_wait(chip);
 
        /*
         * Wait for (possible -- during init auto-calibration may not be set)
index 5784b43f4123399edacd05f707ede5b5f77b994b..dbe63db4bfd654cfb656aee997d873353374c02b 100644 (file)
@@ -19,7 +19,6 @@
  *
  */
 
-#include <sound/driver.h>
 #include <linux/init.h>
 #include <linux/err.h>
 #include <linux/isa.h>
@@ -270,29 +269,9 @@ static struct pnp_card_device_id snd_cs423x_pnpids[] = {
 MODULE_DEVICE_TABLE(pnp_card, snd_cs423x_pnpids);
 
 /* WSS initialization */
-static int __devinit snd_cs423x_pnp_init_wss(int dev, struct pnp_dev *pdev,
-                                            struct pnp_resource_table *cfg)
+static int __devinit snd_cs423x_pnp_init_wss(int dev, struct pnp_dev *pdev)
 {
-       int err;
-
-       pnp_init_resource_table(cfg);
-       if (port[dev] != SNDRV_AUTO_PORT)
-               pnp_resource_change(&cfg->port_resource[0], port[dev], 4);
-       if (fm_port[dev] != SNDRV_AUTO_PORT && fm_port[dev] > 0)
-               pnp_resource_change(&cfg->port_resource[1], fm_port[dev], 4);
-       if (sb_port[dev] != SNDRV_AUTO_PORT)
-               pnp_resource_change(&cfg->port_resource[2], sb_port[dev], 16);
-       if (irq[dev] != SNDRV_AUTO_IRQ)
-               pnp_resource_change(&cfg->irq_resource[0], irq[dev], 1);
-       if (dma1[dev] != SNDRV_AUTO_DMA)
-               pnp_resource_change(&cfg->dma_resource[0], dma1[dev], 1);
-       if (dma2[dev] != SNDRV_AUTO_DMA)
-               pnp_resource_change(&cfg->dma_resource[1], dma2[dev] < 0 ? 4 : dma2[dev], 1);
-       err = pnp_manual_config_dev(pdev, cfg, 0);
-       if (err < 0)
-               snd_printk(KERN_ERR IDENT " WSS PnP manual resources are invalid, using auto config\n");
-       err = pnp_activate_dev(pdev);
-       if (err < 0) {
+       if (pnp_activate_dev(pdev) < 0) {
                printk(KERN_ERR IDENT " WSS PnP configure failed for WSS (out of resources?)\n");
                return -EBUSY;
        }
@@ -311,19 +290,9 @@ static int __devinit snd_cs423x_pnp_init_wss(int dev, struct pnp_dev *pdev,
 }
 
 /* CTRL initialization */
-static int __devinit snd_cs423x_pnp_init_ctrl(int dev, struct pnp_dev *pdev,
-                                             struct pnp_resource_table *cfg)
+static int __devinit snd_cs423x_pnp_init_ctrl(int dev, struct pnp_dev *pdev)
 {
-       int err;
-
-       pnp_init_resource_table(cfg);
-       if (cport[dev] != SNDRV_AUTO_PORT)
-               pnp_resource_change(&cfg->port_resource[0], cport[dev], 8);
-       err = pnp_manual_config_dev(pdev, cfg, 0);
-       if (err < 0)
-               snd_printk(KERN_ERR IDENT " CTRL PnP manual resources are invalid, using auto config\n");
-       err = pnp_activate_dev(pdev);
-       if (err < 0) {
+       if (pnp_activate_dev(pdev) < 0) {
                printk(KERN_ERR IDENT " CTRL PnP configure failed for WSS (out of resources?)\n");
                return -EBUSY;
        }
@@ -333,21 +302,9 @@ static int __devinit snd_cs423x_pnp_init_ctrl(int dev, struct pnp_dev *pdev,
 }
 
 /* MPU initialization */
-static int __devinit snd_cs423x_pnp_init_mpu(int dev, struct pnp_dev *pdev,
-                                            struct pnp_resource_table *cfg)
+static int __devinit snd_cs423x_pnp_init_mpu(int dev, struct pnp_dev *pdev)
 {
-       int err;
-
-       pnp_init_resource_table(cfg);
-       if (mpu_port[dev] != SNDRV_AUTO_PORT)
-               pnp_resource_change(&cfg->port_resource[0], mpu_port[dev], 2);
-       if (mpu_irq[dev] != SNDRV_AUTO_IRQ && mpu_irq[dev] >= 0)
-               pnp_resource_change(&cfg->irq_resource[0], mpu_irq[dev], 1);
-       err = pnp_manual_config_dev(pdev, cfg, 0);
-       if (err < 0)
-               snd_printk(KERN_ERR IDENT " MPU401 PnP manual resources are invalid, using auto config\n");
-       err = pnp_activate_dev(pdev);
-       if (err < 0) {
+       if (pnp_activate_dev(pdev) < 0) {
                printk(KERN_ERR IDENT " MPU401 PnP configure failed for WSS (out of resources?)\n");
                mpu_port[dev] = SNDRV_AUTO_PORT;
                mpu_irq[dev] = SNDRV_AUTO_IRQ;
@@ -368,15 +325,8 @@ static int __devinit snd_cs423x_pnp_init_mpu(int dev, struct pnp_dev *pdev,
 static int __devinit snd_card_cs4232_pnp(int dev, struct snd_card_cs4236 *acard,
                                         struct pnp_dev *pdev)
 {
-       struct pnp_resource_table *cfg = kmalloc(sizeof(*cfg), GFP_KERNEL);
-
-       if (!cfg)
-               return -ENOMEM;
-       if (snd_cs423x_pnp_init_wss(dev, acard->wss, cfg) < 0) {
-               kfree(cfg);
+       if (snd_cs423x_pnp_init_wss(dev, acard->wss) < 0)
                return -EBUSY;
-       }
-       kfree(cfg);
        cport[dev] = -1;
        return 0;
 }
@@ -386,43 +336,33 @@ static int __devinit snd_card_cs423x_pnpc(int dev, struct snd_card_cs4236 *acard
                                          struct pnp_card_link *card,
                                          const struct pnp_card_device_id *id)
 {
-       struct pnp_resource_table *cfg = kmalloc(sizeof(*cfg), GFP_KERNEL);
-
-       if (!cfg)
-               return -ENOMEM;
-
        acard->wss = pnp_request_card_device(card, id->devs[0].id, NULL);
        if (acard->wss == NULL)
-               goto error;
+               return -EBUSY;
        acard->ctrl = pnp_request_card_device(card, id->devs[1].id, NULL);
        if (acard->ctrl == NULL)
-               goto error;
+               return -EBUSY;
        if (id->devs[2].id[0]) {
                acard->mpu = pnp_request_card_device(card, id->devs[2].id, NULL);
                if (acard->mpu == NULL)
-                       goto error;
+                       return -EBUSY;
        }
 
        /* WSS initialization */
-       if (snd_cs423x_pnp_init_wss(dev, acard->wss, cfg) < 0)
-               goto error;
+       if (snd_cs423x_pnp_init_wss(dev, acard->wss) < 0)
+               return -EBUSY;
 
        /* CTRL initialization */
        if (acard->ctrl && cport[dev] > 0) {
-               if (snd_cs423x_pnp_init_ctrl(dev, acard->ctrl, cfg) < 0)
-                       goto error;
+               if (snd_cs423x_pnp_init_ctrl(dev, acard->ctrl) < 0)
+                       return -EBUSY;
        }
        /* MPU initialization */
        if (acard->mpu && mpu_port[dev] > 0) {
-               if (snd_cs423x_pnp_init_mpu(dev, acard->mpu, cfg) < 0)
-                       goto error;
+               if (snd_cs423x_pnp_init_mpu(dev, acard->mpu) < 0)
+                       return -EBUSY;
        }
-       kfree(cfg);
        return 0;
-
- error:
-       kfree(cfg);
-       return -EBUSY;
 }
 #endif /* CONFIG_PNP */
 
index 6bd064470d4cfe576535b9d7e1bf9fd822ffadd7..de71910401ea531efd514b881fe4a6a2744c3583 100644 (file)
@@ -79,7 +79,6 @@
  *
  */
 
-#include <sound/driver.h>
 #include <asm/io.h>
 #include <linux/delay.h>
 #include <linux/init.h>
index ce57d526f7bc3e5b8fc6608c83d97ff248a99f71..a0242c3b613ef2a22dabb34531724883cc62e529 100644 (file)
@@ -21,7 +21,6 @@
     Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA
 */
 
-#include <sound/driver.h>
 #include <linux/init.h>
 #include <linux/wait.h>
 #include <linux/pnp.h>
@@ -56,18 +55,6 @@ module_param_array(id, charp, NULL, 0444);
 MODULE_PARM_DESC(id, "ID string for DT-019X based soundcard.");
 module_param_array(enable, bool, NULL, 0444);
 MODULE_PARM_DESC(enable, "Enable DT-019X based soundcard.");
-module_param_array(port, long, NULL, 0444);
-MODULE_PARM_DESC(port, "Port # for dt019x driver.");
-module_param_array(mpu_port, long, NULL, 0444);
-MODULE_PARM_DESC(mpu_port, "MPU-401 port # for dt019x driver.");
-module_param_array(fm_port, long, NULL, 0444);
-MODULE_PARM_DESC(fm_port, "FM port # for dt019x driver.");
-module_param_array(irq, int, NULL, 0444);
-MODULE_PARM_DESC(irq, "IRQ # for dt019x driver.");
-module_param_array(mpu_irq, int, NULL, 0444);
-MODULE_PARM_DESC(mpu_irq, "MPU-401 IRQ # for dt019x driver.");
-module_param_array(dma8, int, NULL, 0444);
-MODULE_PARM_DESC(dma8, "8-bit DMA # for dt019x driver.");
 
 struct snd_card_dt019x {
        struct pnp_dev *dev;
@@ -95,36 +82,20 @@ static int __devinit snd_card_dt019x_pnp(int dev, struct snd_card_dt019x *acard,
                                         const struct pnp_card_device_id *pid)
 {
        struct pnp_dev *pdev;
-       struct pnp_resource_table * cfg = kmalloc(sizeof(struct pnp_resource_table), GFP_KERNEL);
        int err;
 
-       if (!cfg)
-               return -ENOMEM;
-
        acard->dev = pnp_request_card_device(card, pid->devs[0].id, NULL);
-       if (acard->dev == NULL) {
-               kfree (cfg);
+       if (acard->dev == NULL)
                return -ENODEV;
-       }
+
        acard->devmpu = pnp_request_card_device(card, pid->devs[1].id, NULL);
        acard->devopl = pnp_request_card_device(card, pid->devs[2].id, NULL);
 
        pdev = acard->dev;
-       pnp_init_resource_table(cfg);
-
-       if (port[dev] != SNDRV_AUTO_PORT)
-               pnp_resource_change(&cfg->port_resource[0], port[dev], 16);
-       if (dma8[dev] != SNDRV_AUTO_DMA)
-               pnp_resource_change(&cfg->dma_resource[0], dma8[dev], 1);
-       if (irq[dev] != SNDRV_AUTO_IRQ)
-               pnp_resource_change(&cfg->irq_resource[0], irq[dev], 1);
 
-       if ((pnp_manual_config_dev(pdev, cfg, 0)) < 0)
-               snd_printk(KERN_ERR PFX "DT-019X AUDIO the requested resources are invalid, using auto config\n");
        err = pnp_activate_dev(pdev);
        if (err < 0) {
                snd_printk(KERN_ERR PFX "DT-019X AUDIO pnp configure failure\n");
-               kfree(cfg);
                return err;
        }
 
@@ -135,15 +106,7 @@ static int __devinit snd_card_dt019x_pnp(int dev, struct snd_card_dt019x *acard,
                        port[dev],irq[dev],dma8[dev]);
 
        pdev = acard->devmpu;
-
        if (pdev != NULL) {
-               pnp_init_resource_table(cfg);
-               if (mpu_port[dev] != SNDRV_AUTO_PORT)
-                       pnp_resource_change(&cfg->port_resource[0], mpu_port[dev], 2);
-               if (mpu_irq[dev] != SNDRV_AUTO_IRQ)
-                       pnp_resource_change(&cfg->irq_resource[0], mpu_irq[dev], 1);
-               if ((pnp_manual_config_dev(pdev, cfg, 0)) < 0)
-                       snd_printk(KERN_ERR PFX "DT-019X MPU401 the requested resources are invalid, using auto config\n");
                err = pnp_activate_dev(pdev);
                if (err < 0) {
                        pnp_release_card_device(pdev);
@@ -162,11 +125,6 @@ static int __devinit snd_card_dt019x_pnp(int dev, struct snd_card_dt019x *acard,
 
        pdev = acard->devopl;
        if (pdev != NULL) {
-               pnp_init_resource_table(cfg);
-               if (fm_port[dev] != SNDRV_AUTO_PORT)
-                       pnp_resource_change(&cfg->port_resource[0], fm_port[dev], 4);
-               if ((pnp_manual_config_dev(pdev, cfg, 0)) < 0)
-                       snd_printk(KERN_ERR PFX "DT-019X OPL3 the requested resources are invalid, using auto config\n");
                err = pnp_activate_dev(pdev);
                if (err < 0) {
                        pnp_release_card_device(pdev);
@@ -181,7 +139,6 @@ static int __devinit snd_card_dt019x_pnp(int dev, struct snd_card_dt019x *acard,
                fm_port[dev] = -1;
        }
 
-       kfree(cfg);
        return 0;
 }
 
index 74bbc92f2e7ce359b56b967faeb0da1d8f701ea9..f88639ea64b214ddd976ef6577eab8100f2741f4 100644 (file)
@@ -19,7 +19,6 @@
  *
  */
 
-#include <sound/driver.h>
 #include <linux/init.h>
 #include <linux/err.h>
 #include <linux/isa.h>
index 5c26d495daa8b69476fb7895e6aec39408982d39..1e1e575b1db329f87b289cb4cc57bbb954f1bae0 100644 (file)
@@ -19,7 +19,6 @@
  *
  */
 
-#include <sound/driver.h>
 #include <linux/init.h>
 #include <linux/interrupt.h>
 #include <linux/delay.h>
index c1af28fd4a1fcf7fb961993ca4c74260d3e9fb26..90498e4ca2601f3c82005a4de4eaafd5e857d935 100644 (file)
@@ -77,7 +77,6 @@
  *   needed for ZV, so maybe the datasheet is entirely wrong here.
  */
  
-#include <sound/driver.h>
 #include <linux/init.h>
 #include <linux/err.h>
 #include <linux/isa.h>
@@ -163,7 +162,7 @@ struct snd_audiodrive {
 #define ES18XX_DUPLEX_SAME 0x0010      /* Playback and record must share the same rate */
 #define ES18XX_NEW_RATE        0x0020  /* More precise rate setting */
 #define ES18XX_AUXB    0x0040  /* AuxB mixer control */
-#define ES18XX_HWV     0x0080  /* Has seperate hardware volume mixer controls*/
+#define ES18XX_HWV     0x0080  /* Has separate hardware volume mixer controls*/
 #define ES18XX_MONO    0x0100  /* Mono_in mixer control */
 #define ES18XX_I2S     0x0200  /* I2S mixer control */
 #define ES18XX_MUTEREC 0x0400  /* Record source can be muted */
@@ -1442,6 +1441,8 @@ static int __devinit snd_es18xx_initialize(struct snd_es18xx *chip)
                snd_es18xx_write(chip, 0xB2, 0x50);
                /* Enable MPU and hardware volume interrupt */
                snd_es18xx_mixer_write(chip, 0x64, 0x42);
+               /* Enable ESS wavetable input */
+               snd_es18xx_mixer_bits(chip, 0x48, 0x10, 0x10);
        }
        else {
                int irqmask, dma1mask, dma2mask;
@@ -2035,31 +2036,9 @@ static struct pnp_device_id snd_audiodrive_pnpbiosids[] = {
 MODULE_DEVICE_TABLE(pnp, snd_audiodrive_pnpbiosids);
 
 /* PnP main device initialization */
-static int __devinit snd_audiodrive_pnp_init_main(int dev, struct pnp_dev *pdev,
-                                                 struct pnp_resource_table *cfg)
+static int __devinit snd_audiodrive_pnp_init_main(int dev, struct pnp_dev *pdev)
 {
-       int err;
-
-       pnp_init_resource_table(cfg);
-       if (port[dev] != SNDRV_AUTO_PORT)
-               pnp_resource_change(&cfg->port_resource[0], port[dev], 16);
-       if (fm_port[dev] != SNDRV_AUTO_PORT)
-               pnp_resource_change(&cfg->port_resource[1], fm_port[dev], 4);
-       if (mpu_port[dev] != SNDRV_AUTO_PORT)
-               pnp_resource_change(&cfg->port_resource[2], mpu_port[dev], 2);
-       if (dma1[dev] != SNDRV_AUTO_DMA)
-               pnp_resource_change(&cfg->dma_resource[0], dma1[dev], 1);
-       if (dma2[dev] != SNDRV_AUTO_DMA)
-               pnp_resource_change(&cfg->dma_resource[1], dma2[dev], 1);
-       if (irq[dev] != SNDRV_AUTO_IRQ)
-               pnp_resource_change(&cfg->irq_resource[0], irq[dev], 1);
-       if (pnp_device_is_isapnp(pdev)) {
-               err = pnp_manual_config_dev(pdev, cfg, 0);
-               if (err < 0)
-                       snd_printk(KERN_ERR PFX "PnP manual resources are invalid, using auto config\n");
-       }
-       err = pnp_activate_dev(pdev);
-       if (err < 0) {
+       if (pnp_activate_dev(pdev) < 0) {
                snd_printk(KERN_ERR PFX "PnP configure failure (out of resources?)\n");
                return -EBUSY;
        }
@@ -2087,16 +2066,9 @@ static int __devinit snd_audiodrive_pnp_init_main(int dev, struct pnp_dev *pdev,
 static int __devinit snd_audiodrive_pnp(int dev, struct snd_audiodrive *acard,
                                        struct pnp_dev *pdev)
 {
-       struct pnp_resource_table * cfg = kmalloc(sizeof(struct pnp_resource_table), GFP_KERNEL);
-
-       if (!cfg)
-               return -ENOMEM;
        acard->dev = pdev;
-       if (snd_audiodrive_pnp_init_main(dev, acard->dev, cfg) < 0) {
-               kfree(cfg);
+       if (snd_audiodrive_pnp_init_main(dev, acard->dev) < 0)
                return -EBUSY;
-       }
-       kfree(cfg);
        return 0;
 }
 
@@ -2125,33 +2097,24 @@ static int __devinit snd_audiodrive_pnpc(int dev, struct snd_audiodrive *acard,
                                        struct pnp_card_link *card,
                                        const struct pnp_card_device_id *id)
 {
-       struct pnp_resource_table * cfg = kmalloc(sizeof(struct pnp_resource_table), GFP_KERNEL);
-
-       if (!cfg)
-               return -ENOMEM;
        acard->dev = pnp_request_card_device(card, id->devs[0].id, NULL);
-       if (acard->dev == NULL) {
-               kfree(cfg);
+       if (acard->dev == NULL)
                return -EBUSY;
-       }
+
        acard->devc = pnp_request_card_device(card, id->devs[1].id, NULL);
-       if (acard->devc == NULL) {
-               kfree(cfg);
+       if (acard->devc == NULL)
                return -EBUSY;
-       }
+
        /* Control port initialization */
        if (pnp_activate_dev(acard->devc) < 0) {
-               kfree(cfg);
                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, cfg) < 0) {
-               kfree(cfg);
+       if (snd_audiodrive_pnp_init_main(dev, acard->dev) < 0)
                return -EBUSY;
-       }
-       kfree(cfg);
+
        return 0;
 }
 #endif /* CONFIG_PNP */
index df3d59f25f5e633cea533788d867ea5ee6b5ec49..6cd4ee03754a1c1f9488eca10978dbd4f37f0efd 100644 (file)
@@ -9,7 +9,6 @@ snd-gus-lib-objs := gus_main.o \
                    gus_pcm.o gus_mixer.o \
                    gus_uart.o \
                    gus_reset.o
-snd-gus-synth-objs := gus_synth.o gus_sample.o gus_simple.o gus_instr.o
 
 snd-gusclassic-objs := gusclassic.o
 snd-gusextreme-objs := gusextreme.o
@@ -17,20 +16,9 @@ snd-gusmax-objs := gusmax.o
 snd-interwave-objs := interwave.o
 snd-interwave-stb-objs := interwave-stb.o
 
-#
-# this function returns:
-#   "m" - CONFIG_SND_SEQUENCER is m
-#   <empty string> - CONFIG_SND_SEQUENCER is undefined
-#   otherwise parameter #1 value
-#
-sequencer = $(if $(subst y,,$(CONFIG_SND_SEQUENCER)),$(if $(1),m),$(if $(CONFIG_SND_SEQUENCER),$(1)))
-
 # Toplevel Module Dependency
 obj-$(CONFIG_SND_GUSCLASSIC) += snd-gusclassic.o snd-gus-lib.o
 obj-$(CONFIG_SND_GUSMAX) += snd-gusmax.o snd-gus-lib.o
 obj-$(CONFIG_SND_GUSEXTREME) += snd-gusextreme.o snd-gus-lib.o
 obj-$(CONFIG_SND_INTERWAVE) += snd-interwave.o snd-gus-lib.o
 obj-$(CONFIG_SND_INTERWAVE_STB) += snd-interwave-stb.o snd-gus-lib.o
-obj-$(call sequencer,$(CONFIG_SND_GUS_SYNTH)) += snd-gus-synth.o
-
-obj-m := $(sort $(obj-m))
index fc905141e8a5bfa7a12d18bac086e5f409485cc5..f45f6116c77ab03d7f171717617f5569a0f4127c 100644 (file)
@@ -19,7 +19,6 @@
  *
  */
 
-#include <sound/driver.h>
 #include <asm/dma.h>
 #include <linux/slab.h>
 #include <sound/core.h>
index 9eaa932f6efeadcfad5b9b43cbdadcac7263d1d9..fd2e2e2ed4e78ea38c7dc1139df45fdaeed2cf66 100644 (file)
@@ -19,7 +19,6 @@
  *
  */
 
-#include <sound/driver.h>
 #include <linux/time.h>
 #include <sound/core.h>
 #include <sound/gus.h>
index bf137ea72329e63de2d126c6ec3a8e7a9d5133bb..4dc9caf8ddcf99fee0ef64eaaf120fa9df97adf5 100644 (file)
@@ -19,7 +19,6 @@
  *
  */
 
-#include <sound/driver.h>
 #include <linux/time.h>
 #include <sound/core.h>
 #include <sound/gus.h>
index 3d4f899285ef666f151b1265a83fac17a4975a1b..ca79878d8d8c07640d0c2cd9160003b158dbabaf 100644 (file)
@@ -19,7 +19,6 @@
  *
  */
 
-#include <sound/driver.h>
 #include <linux/delay.h>
 #include <linux/time.h>
 #include <sound/core.h>
index cd9a6f1c99e6ea08f01fcad7756ce1beabd8dcbb..041894ddd014ab2966c4b13a9a002416f026e530 100644 (file)
@@ -19,7 +19,6 @@
  *
  */
 
-#include <sound/driver.h>
 #include <sound/core.h>
 #include <sound/info.h>
 #include <sound/gus.h>
index b14d5d6d9a3233b60ad233d988719bd5b74f70cd..cccc16c8113f35a949bc775b56a04b2a4f9f2b38 100644 (file)
@@ -19,7 +19,6 @@
  *
  */
 
-#include <sound/driver.h>
 #include <linux/init.h>
 #include <linux/interrupt.h>
 #include <linux/delay.h>
@@ -104,12 +103,6 @@ static int snd_gus_free(struct snd_gus_card *gus)
 {
        if (gus->gf1.res_port2 == NULL)
                goto __hw_end;
-#if defined(CONFIG_SND_SEQUENCER) || (defined(MODULE) && defined(CONFIG_SND_SEQUENCER_MODULE))
-       if (gus->seq_dev) {
-               snd_device_free(gus->card, gus->seq_dev);
-               gus->seq_dev = NULL;
-       }
-#endif
        snd_gf1_stop(gus);
        snd_gus_init_dma_irq(gus, 0);
       __hw_end:
@@ -408,14 +401,6 @@ static int snd_gus_check_version(struct snd_gus_card * gus)
        return 0;
 }
 
-#if defined(CONFIG_SND_SEQUENCER) || (defined(MODULE) && defined(CONFIG_SND_SEQUENCER_MODULE))
-static void snd_gus_seq_dev_free(struct snd_seq_device *seq_dev)
-{
-       struct snd_gus_card *gus = seq_dev->private_data;
-       gus->seq_dev = NULL;
-}
-#endif
-
 int snd_gus_initialize(struct snd_gus_card *gus)
 {
        int err;
@@ -430,15 +415,6 @@ int snd_gus_initialize(struct snd_gus_card *gus)
        }
        if ((err = snd_gus_init_dma_irq(gus, 1)) < 0)
                return err;
-#if defined(CONFIG_SND_SEQUENCER) || (defined(MODULE) && defined(CONFIG_SND_SEQUENCER_MODULE))
-       if (snd_seq_device_new(gus->card, 1, SNDRV_SEQ_DEV_ID_GUS,
-                              sizeof(struct snd_gus_card *), &gus->seq_dev) >= 0) {
-               strcpy(gus->seq_dev->name, "GUS");
-               *(struct snd_gus_card **)SNDRV_SEQ_DEVICE_ARGPTR(gus->seq_dev) = gus;
-               gus->seq_dev->private_data = gus;
-               gus->seq_dev->private_free = snd_gus_seq_dev_free;
-       }
-#endif
        snd_gf1_start(gus);
        gus->initialized = 1;
        return 0;
index bcf4656853c4f1d2ae3a73fc5a50c0144c6a858b..661205c4dceab85b97dc6693067dd17a888c4b7f 100644 (file)
@@ -19,7 +19,6 @@
  *
  */
 
-#include <sound/driver.h>
 #include <linux/slab.h>
 #include <linux/string.h>
 #include <sound/core.h>
index f69a44728ebf0b065648e984d3792a966a1773d5..2803e227aec9f12f6c6d1ffdaa6c8b8b8bfee8d2 100644 (file)
@@ -19,7 +19,6 @@
  *
  */
 
-#include <sound/driver.h>
 #include <linux/slab.h>
 #include <sound/core.h>
 #include <sound/gus.h>
index a96253e16654b21d4dfb7c513d35043fd81907b4..ebdb33469306df356c9a9303c0c8f1dc22c1b802 100644 (file)
@@ -19,7 +19,6 @@
  *
  */
 
-#include <sound/driver.h>
 #include <linux/time.h>
 #include <linux/wait.h>
 #include <sound/core.h>
index a7971f5ffe637f16dfe4c6d0e5230eaad61562dc..99731dc9732564fa1223becbadff7f2e0b9cb782 100644 (file)
@@ -25,7 +25,6 @@
  *
  */
 
-#include <sound/driver.h>
 #include <asm/dma.h>
 #include <linux/slab.h>
 #include <sound/core.h>
index 20cfdb87f84ab24c8f4480e5540caf57d67fd50e..3d1fed0c2620d3063ce4cd0c5fcc26b21187877e 100644 (file)
@@ -18,7 +18,6 @@
  *
  */
 
-#include <sound/driver.h>
 #include <linux/delay.h>
 #include <linux/interrupt.h>
 #include <linux/time.h>
diff --git a/sound/isa/gus/gus_sample.c b/sound/isa/gus/gus_sample.c
deleted file mode 100644 (file)
index cba0829..0000000
+++ /dev/null
@@ -1,165 +0,0 @@
-/*
- *  Routines for Gravis UltraSound soundcards - Sample support
- *  Copyright (c) by Jaroslav Kysela <perex@perex.cz>
- *
- *
- *   This program is free software; you can redistribute it and/or modify
- *   it under the terms of the GNU General Public License as published by
- *   the Free Software Foundation; either version 2 of the License, or
- *   (at your option) any later version.
- *
- *   This program is distributed in the hope that it will be useful,
- *   but WITHOUT ANY WARRANTY; without even the implied warranty of
- *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- *   GNU General Public License for more details.
- *
- *   You should have received a copy of the GNU General Public License
- *   along with this program; if not, write to the Free Software
- *   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA
- *
- */
-
-#include <sound/driver.h>
-#include <linux/time.h>
-#include <sound/core.h>
-#include <sound/gus.h>
-
-/*
- *
- */
-
-static void select_instrument(struct snd_gus_card * gus, struct snd_gus_voice * v)
-{
-       struct snd_seq_kinstr *instr;
-
-#if 0
-       printk("select instrument: cluster = %li, std = 0x%x, bank = %i, prg = %i\n",
-                                       v->instr.cluster,
-                                       v->instr.std,
-                                       v->instr.bank,
-                                       v->instr.prg);
-#endif
-       instr = snd_seq_instr_find(gus->gf1.ilist, &v->instr, 0, 1);
-       if (instr != NULL) {
-               if (instr->ops) {
-                       if (!strcmp(instr->ops->instr_type, SNDRV_SEQ_INSTR_ID_SIMPLE))
-                               snd_gf1_simple_init(v);
-               }
-               snd_seq_instr_free_use(gus->gf1.ilist, instr);
-       }
-}
-
-/*
- *
- */
-
-static void event_sample(struct snd_seq_event *ev, struct snd_gus_port *p,
-                        struct snd_gus_voice *v)
-{
-       if (v->sample_ops && v->sample_ops->sample_stop)
-               v->sample_ops->sample_stop(p->gus, v, SAMPLE_STOP_IMMEDIATELY);
-       v->instr.std = ev->data.sample.param.sample.std;
-       if (v->instr.std & 0xff000000) {        /* private instrument */
-               v->instr.std &= 0x00ffffff;
-               v->instr.std |= (unsigned int)ev->source.client << 24;
-       }                                                
-       v->instr.bank = ev->data.sample.param.sample.bank;
-       v->instr.prg = ev->data.sample.param.sample.prg;
-       select_instrument(p->gus, v);
-}
-
-static void event_cluster(struct snd_seq_event *ev, struct snd_gus_port *p,
-                         struct snd_gus_voice *v)
-{
-       if (v->sample_ops && v->sample_ops->sample_stop)
-               v->sample_ops->sample_stop(p->gus, v, SAMPLE_STOP_IMMEDIATELY);
-       v->instr.cluster = ev->data.sample.param.cluster.cluster;
-       select_instrument(p->gus, v);
-}
-
-static void event_start(struct snd_seq_event *ev, struct snd_gus_port *p,
-                       struct snd_gus_voice *v)
-{
-       if (v->sample_ops && v->sample_ops->sample_start)
-               v->sample_ops->sample_start(p->gus, v, ev->data.sample.param.position);
-}
-
-static void event_stop(struct snd_seq_event *ev, struct snd_gus_port *p,
-                      struct snd_gus_voice *v)
-{
-       if (v->sample_ops && v->sample_ops->sample_stop)
-               v->sample_ops->sample_stop(p->gus, v, ev->data.sample.param.stop_mode);
-}
-
-static void event_freq(struct snd_seq_event *ev, struct snd_gus_port *p,
-                      struct snd_gus_voice *v)
-{
-       if (v->sample_ops && v->sample_ops->sample_freq)
-               v->sample_ops->sample_freq(p->gus, v, ev->data.sample.param.frequency);
-}
-
-static void event_volume(struct snd_seq_event *ev, struct snd_gus_port *p,
-                        struct snd_gus_voice *v)
-{
-       if (v->sample_ops && v->sample_ops->sample_volume)
-               v->sample_ops->sample_volume(p->gus, v, &ev->data.sample.param.volume);
-}
-
-static void event_loop(struct snd_seq_event *ev, struct snd_gus_port *p,
-                      struct snd_gus_voice *v)
-{
-       if (v->sample_ops && v->sample_ops->sample_loop)
-               v->sample_ops->sample_loop(p->gus, v, &ev->data.sample.param.loop);
-}
-
-static void event_position(struct snd_seq_event *ev, struct snd_gus_port *p,
-                          struct snd_gus_voice *v)
-{
-       if (v->sample_ops && v->sample_ops->sample_pos)
-               v->sample_ops->sample_pos(p->gus, v, ev->data.sample.param.position);
-}
-
-static void event_private1(struct snd_seq_event *ev, struct snd_gus_port *p,
-                          struct snd_gus_voice *v)
-{
-       if (v->sample_ops && v->sample_ops->sample_private1)
-               v->sample_ops->sample_private1(p->gus, v, (unsigned char *)&ev->data.sample.param.raw8);
-}
-
-typedef void (gus_sample_event_handler_t)(struct snd_seq_event *ev,
-                                         struct snd_gus_port *p,
-                                         struct snd_gus_voice *v);
-static gus_sample_event_handler_t *gus_sample_event_handlers[9] = {
-       event_sample,
-       event_cluster,
-       event_start,
-       event_stop,
-       event_freq,
-       event_volume,
-       event_loop,
-       event_position,
-       event_private1
-};
-
-void snd_gus_sample_event(struct snd_seq_event *ev, struct snd_gus_port *p)
-{
-       int idx, voice;
-       struct snd_gus_card *gus = p->gus;
-       struct snd_gus_voice *v;
-       unsigned long flags;
-       
-       idx = ev->type - SNDRV_SEQ_EVENT_SAMPLE;
-       if (idx < 0 || idx > 8)
-               return;
-       for (voice = 0; voice < 32; voice++) {
-               v = &gus->gf1.voices[voice];
-               if (v->use && v->client == ev->source.client &&
-                   v->port == ev->source.port &&
-                   v->index == ev->data.sample.channel) {
-                       spin_lock_irqsave(&gus->event_lock, flags);
-                       gus_sample_event_handlers[idx](ev, p, v);
-                       spin_unlock_irqrestore(&gus->event_lock, flags);
-                       return;
-               }
-       }
-}
diff --git a/sound/isa/gus/gus_simple.c b/sound/isa/gus/gus_simple.c
deleted file mode 100644 (file)
index 39d121e..0000000
+++ /dev/null
@@ -1,634 +0,0 @@
-/*
- *  Routines for Gravis UltraSound soundcards - Simple instrument handlers
- *  Copyright (c) by Jaroslav Kysela <perex@perex.cz>
- *
- *
- *   This program is free software; you can redistribute it and/or modify
- *   it under the terms of the GNU General Public License as published by
- *   the Free Software Foundation; either version 2 of the License, or
- *   (at your option) any later version.
- *
- *   This program is distributed in the hope that it will be useful,
- *   but WITHOUT ANY WARRANTY; without even the implied warranty of
- *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- *   GNU General Public License for more details.
- *
- *   You should have received a copy of the GNU General Public License
- *   along with this program; if not, write to the Free Software
- *   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA
- *
- */
-
-#include <sound/driver.h>
-#include <linux/time.h>
-#include <sound/core.h>
-#include <sound/gus.h>
-#include "gus_tables.h"
-
-/*
- *
- */
-
-static void interrupt_wave(struct snd_gus_card *gus, struct snd_gus_voice *voice);
-static void interrupt_volume(struct snd_gus_card *gus, struct snd_gus_voice *voice);
-static void interrupt_effect(struct snd_gus_card *gus, struct snd_gus_voice *voice);
-
-static void sample_start(struct snd_gus_card *gus, struct snd_gus_voice *voice, snd_seq_position_t position);
-static void sample_stop(struct snd_gus_card *gus, struct snd_gus_voice *voice, int mode);
-static void sample_freq(struct snd_gus_card *gus, struct snd_gus_voice *voice, snd_seq_frequency_t freq);
-static void sample_volume(struct snd_gus_card *card, struct snd_gus_voice *voice, struct snd_seq_ev_volume *volume);
-static void sample_loop(struct snd_gus_card *card, struct snd_gus_voice *voice, struct snd_seq_ev_loop *loop);
-static void sample_pos(struct snd_gus_card *card, struct snd_gus_voice *voice, snd_seq_position_t position);
-static void sample_private1(struct snd_gus_card *card, struct snd_gus_voice *voice, unsigned char *data);
-
-static struct snd_gus_sample_ops sample_ops = {
-       sample_start,
-       sample_stop,
-       sample_freq,
-       sample_volume,
-       sample_loop,
-       sample_pos,
-       sample_private1
-};
-
-#if 0
-
-static void note_stop(struct snd_gus_card *gus, struct snd_gus_voice *voice, int wait);
-static void note_wait(struct snd_gus_card *gus, struct snd_gus_voice *voice);
-static void note_off(struct snd_gus_card *gus, struct snd_gus_voice *voice);
-static void note_volume(struct snd_gus_card *card, struct snd_gus_voice *voice);
-static void note_pitchbend(struct snd_gus_card *card, struct snd_gus_voice *voice);
-static void note_vibrato(struct snd_gus_card *card, struct snd_gus_voice *voice);
-static void note_tremolo(struct snd_gus_card *card, struct snd_gus_voice *voice);
-
-static struct snd_gus_note_handlers note_commands = {
-       note_stop,
-       note_wait,
-       note_off,
-       note_volume,
-       note_pitchbend,
-       note_vibrato,
-       note_tremolo
-};
-
-static void chn_trigger_down(struct snd_gus_card *card, ultra_channel_t *channel, ultra_instrument_t *instrument, unsigned char note, unsigned char velocity, unsigned char priority );
-static void chn_trigger_up( ultra_card_t *card, ultra_note_t *note );
-static void chn_control( ultra_card_t *card, ultra_channel_t *channel, unsigned short p1, unsigned short p2 );
-
-static struct ULTRA_STRU_INSTRUMENT_CHANNEL_COMMANDS channel_commands = {
-  chn_trigger_down,
-  chn_trigger_up,
-  chn_control
-};
-
-#endif
-
-static void do_volume_envelope(struct snd_gus_card *card, struct snd_gus_voice *voice);
-static void do_pan_envelope(struct snd_gus_card *card, struct snd_gus_voice *voice);
-
-/*
- *
- */
-
-static void interrupt_wave(struct snd_gus_card *gus, struct snd_gus_voice *voice)
-{
-       spin_lock(&gus->event_lock);
-       snd_gf1_stop_voice(gus, voice->number);
-       spin_lock(&gus->reg_lock);
-       snd_gf1_select_voice(gus, voice->number);
-       snd_gf1_write16(gus, SNDRV_GF1_VW_VOLUME, 0);
-       spin_unlock(&gus->reg_lock);
-       voice->flags &= ~SNDRV_GF1_VFLG_RUNNING;
-       spin_unlock(&gus->event_lock);
-}
-
-static void interrupt_volume(struct snd_gus_card *gus, struct snd_gus_voice *voice)
-{
-       spin_lock(&gus->event_lock);
-       if (voice->flags & SNDRV_GF1_VFLG_RUNNING)
-               do_volume_envelope(gus, voice);
-       else
-               snd_gf1_stop_voice(gus, voice->number);
-       spin_unlock(&gus->event_lock);
-}
-
-static void interrupt_effect(struct snd_gus_card *gus, struct snd_gus_voice *voice)
-{
-       spin_lock(&gus->event_lock);
-       if ((voice->flags & (SNDRV_GF1_VFLG_RUNNING|SNDRV_GF1_VFLG_EFFECT_TIMER1)) ==
-                           (SNDRV_GF1_VFLG_RUNNING|SNDRV_GF1_VFLG_EFFECT_TIMER1))
-               do_pan_envelope(gus, voice);
-       spin_unlock(&gus->event_lock);
-}
-
-/*
- *
- */
-
-static void do_volume_envelope(struct snd_gus_card *gus, struct snd_gus_voice *voice)
-{
-       unsigned short next, rate, old_volume;
-       int program_next_ramp;
-       unsigned long flags;
-  
-       if (!gus->gf1.volume_ramp) {
-               spin_lock_irqsave(&gus->reg_lock, flags);
-               snd_gf1_select_voice(gus, voice->number);
-               snd_gf1_ctrl_stop(gus, SNDRV_GF1_VB_VOLUME_CONTROL);
-               snd_gf1_write16(gus, SNDRV_GF1_VW_VOLUME, voice->gf1_volume);
-               /* printk("gf1_volume = 0x%x\n", voice->gf1_volume); */
-               spin_unlock_irqrestore(&gus->reg_lock, flags);
-               return;
-       }
-       program_next_ramp = 0;
-       rate = next = 0;
-       while (1) {
-               program_next_ramp = 0;
-               rate = next = 0;
-               switch (voice->venv_state) {
-               case VENV_BEFORE:
-                       voice->venv_state = VENV_ATTACK;
-                       voice->venv_value_next = 0;
-                       spin_lock_irqsave(&gus->reg_lock, flags);
-                       snd_gf1_select_voice(gus, voice->number);
-                       snd_gf1_ctrl_stop(gus, SNDRV_GF1_VB_VOLUME_CONTROL);
-                       snd_gf1_write16(gus, SNDRV_GF1_VW_VOLUME, SNDRV_GF1_MIN_VOLUME);
-                       spin_unlock_irqrestore(&gus->reg_lock, flags);
-                       break;
-               case VENV_ATTACK:
-                       voice->venv_state = VENV_SUSTAIN;
-                       program_next_ramp++;
-                       next = 255;
-                       rate = gus->gf1.volume_ramp;
-                       break;
-               case VENV_SUSTAIN:
-                       voice->venv_state = VENV_RELEASE;
-                       spin_lock_irqsave(&gus->reg_lock, flags);
-                       snd_gf1_select_voice(gus, voice->number);
-                       snd_gf1_ctrl_stop(gus, SNDRV_GF1_VB_VOLUME_CONTROL);
-                       snd_gf1_write16(gus, SNDRV_GF1_VW_VOLUME, ((int)voice->gf1_volume * (int)voice->venv_value_next) / 255);
-                       spin_unlock_irqrestore(&gus->reg_lock, flags);
-                       return;
-               case VENV_RELEASE:
-                       voice->venv_state = VENV_DONE;
-                       program_next_ramp++;
-                       next = 0;
-                       rate = gus->gf1.volume_ramp;
-                       break;
-               case VENV_DONE:
-                       snd_gf1_stop_voice(gus, voice->number);
-                       voice->flags &= ~SNDRV_GF1_VFLG_RUNNING;
-                       return;
-               case VENV_VOLUME:
-                       program_next_ramp++;
-                       next = voice->venv_value_next;
-                       rate = gus->gf1.volume_ramp;
-                       voice->venv_state = voice->venv_state_prev;
-                       break;
-               }
-               voice->venv_value_next = next;
-               if (!program_next_ramp)
-                       continue;
-               spin_lock_irqsave(&gus->reg_lock, flags);
-               snd_gf1_select_voice(gus, voice->number);
-               snd_gf1_ctrl_stop(gus, SNDRV_GF1_VB_VOLUME_CONTROL);
-               old_volume = snd_gf1_read16(gus, SNDRV_GF1_VW_VOLUME) >> 8;
-               if (!rate) {
-                       spin_unlock_irqrestore(&gus->reg_lock, flags);                  
-                       continue;
-               }
-               next = (((int)voice->gf1_volume * (int)next) / 255) >> 8;
-               if (old_volume < SNDRV_GF1_MIN_OFFSET)
-                       old_volume = SNDRV_GF1_MIN_OFFSET;
-               if (next < SNDRV_GF1_MIN_OFFSET)
-                       next = SNDRV_GF1_MIN_OFFSET;
-               if (next > SNDRV_GF1_MAX_OFFSET)
-                       next = SNDRV_GF1_MAX_OFFSET;
-               if (old_volume == next) {
-                       spin_unlock_irqrestore(&gus->reg_lock, flags);
-                       continue;
-               }
-               voice->volume_control &= ~0xc3;
-               voice->volume_control |= 0x20;
-               if (old_volume > next) {
-                       snd_gf1_write8(gus, SNDRV_GF1_VB_VOLUME_START, next);
-                       snd_gf1_write8(gus, SNDRV_GF1_VB_VOLUME_END, old_volume);
-                       voice->volume_control |= 0x40;
-               } else {
-                       snd_gf1_write8(gus, SNDRV_GF1_VB_VOLUME_START, old_volume);
-                       snd_gf1_write8(gus, SNDRV_GF1_VB_VOLUME_END, next);
-               }
-               snd_gf1_write8(gus, SNDRV_GF1_VB_VOLUME_RATE, rate);
-               snd_gf1_write8(gus, SNDRV_GF1_VB_VOLUME_CONTROL, voice->volume_control);
-               if (!gus->gf1.enh_mode) {
-                       snd_gf1_delay(gus);
-                       snd_gf1_write8(gus, SNDRV_GF1_VB_VOLUME_CONTROL, voice->volume_control);
-               }
-               spin_unlock_irqrestore(&gus->reg_lock, flags);                  
-               return;
-       }
-}
-
-static void do_pan_envelope(struct snd_gus_card *gus, struct snd_gus_voice *voice)
-{
-       unsigned long flags;
-       unsigned char old_pan;
-
-#if 0
-       snd_gf1_select_voice(gus, voice->number);
-       printk(" -%i- do_pan_envelope - flags = 0x%x (0x%x -> 0x%x)\n",
-               voice->number,
-               voice->flags,
-               voice->gf1_pan,
-               snd_gf1_i_read8(gus, SNDRV_GF1_VB_PAN) & 0x0f);
-#endif
-       if (gus->gf1.enh_mode) {
-               voice->flags &= ~(SNDRV_GF1_VFLG_EFFECT_TIMER1|SNDRV_GF1_VFLG_PAN);
-               return;
-       }
-       if (!gus->gf1.smooth_pan) {
-               spin_lock_irqsave(&gus->reg_lock, flags);                       
-               snd_gf1_select_voice(gus, voice->number);
-               snd_gf1_write8(gus, SNDRV_GF1_VB_PAN, voice->gf1_pan);
-               spin_unlock_irqrestore(&gus->reg_lock, flags);
-               return;
-       }
-       if (!(voice->flags & SNDRV_GF1_VFLG_PAN))               /* before */
-               voice->flags |= SNDRV_GF1_VFLG_EFFECT_TIMER1|SNDRV_GF1_VFLG_PAN;
-       spin_lock_irqsave(&gus->reg_lock, flags);                       
-       snd_gf1_select_voice(gus, voice->number);
-       old_pan = snd_gf1_read8(gus, SNDRV_GF1_VB_PAN) & 0x0f;
-       if (old_pan > voice->gf1_pan )
-               old_pan--;
-       if (old_pan < voice->gf1_pan)
-               old_pan++;
-       snd_gf1_write8(gus, SNDRV_GF1_VB_PAN, old_pan);
-       spin_unlock_irqrestore(&gus->reg_lock, flags);
-       if (old_pan == voice->gf1_pan)                  /* the goal was reached */
-               voice->flags &= ~(SNDRV_GF1_VFLG_EFFECT_TIMER1|SNDRV_GF1_VFLG_PAN);
-#if 0
-       snd_gf1_select_voice(gus, voice->number);
-       printk(" -%i- (1) do_pan_envelope - flags = 0x%x (0x%x -> 0x%x)\n",
-              voice->number,
-              voice->flags,
-              voice->gf1_pan,
-              snd_gf1_i_read8(gus, GF1_VB_PAN) & 0x0f);
-#endif
-}
-
-static void set_enhanced_pan(struct snd_gus_card *gus, struct snd_gus_voice *voice, unsigned short pan)
-{
-       unsigned long flags;
-       unsigned short vlo, vro;
-  
-       vlo = SNDRV_GF1_ATTEN((SNDRV_GF1_ATTEN_TABLE_SIZE-1) - pan);
-       vro = SNDRV_GF1_ATTEN(pan);
-       if (pan != SNDRV_GF1_ATTEN_TABLE_SIZE - 1 && pan != 0) {
-               vlo >>= 1;
-               vro >>= 1;
-       }
-       vlo <<= 4;
-       vro <<= 4;
-#if 0
-       printk("vlo = 0x%x (0x%x), vro = 0x%x (0x%x)\n",
-                       vlo, snd_gf1_i_read16(gus, GF1_VW_OFFSET_LEFT),
-                       vro, snd_gf1_i_read16(gus, GF1_VW_OFFSET_RIGHT));
-#endif
-       spin_lock_irqsave(&gus->reg_lock, flags);                       
-       snd_gf1_select_voice(gus, voice->number);
-        snd_gf1_write16(gus, SNDRV_GF1_VW_OFFSET_LEFT_FINAL, vlo);
-       snd_gf1_write16(gus, SNDRV_GF1_VW_OFFSET_RIGHT_FINAL, vro);
-       spin_unlock_irqrestore(&gus->reg_lock, flags);                  
-       voice->vlo = vlo;
-       voice->vro = vro;
-}
-
-/*
- *
- */
-
-static void sample_start(struct snd_gus_card *gus, struct snd_gus_voice *voice, snd_seq_position_t position)
-{
-       unsigned long flags;
-       unsigned int begin, addr, addr_end, addr_start;
-       int w_16;
-       struct simple_instrument *simple;
-       struct snd_seq_kinstr *instr;
-
-       instr = snd_seq_instr_find(gus->gf1.ilist, &voice->instr, 0, 1);
-       if (instr == NULL)
-               return;
-       voice->instr = instr->instr;    /* copy ID to speedup aliases */
-       simple = KINSTR_DATA(instr);
-       begin = simple->address.memory << 4;
-       w_16 = simple->format & SIMPLE_WAVE_16BIT ? 0x04 : 0;
-       addr_start = simple->loop_start;
-       if (simple->format & SIMPLE_WAVE_LOOP) {
-               addr_end = simple->loop_end;
-       } else {
-               addr_end = (simple->size << 4) - (w_16 ? 40 : 24);
-       }
-       if (simple->format & SIMPLE_WAVE_BACKWARD) {
-               addr = simple->loop_end;
-               if (position < simple->loop_end)
-                       addr -= position;
-       } else {
-               addr = position;
-       }
-       voice->control = 0x00;
-       voice->mode = 0x20;             /* enable offset registers */
-       if (simple->format & SIMPLE_WAVE_16BIT)
-               voice->control |= 0x04;
-       if (simple->format & SIMPLE_WAVE_BACKWARD)
-               voice->control |= 0x40;
-       if (simple->format & SIMPLE_WAVE_LOOP) {
-               voice->control |= 0x08;
-       } else {
-               voice->control |= 0x20;
-       }
-       if (simple->format & SIMPLE_WAVE_BIDIR)
-               voice->control |= 0x10;
-       if (simple->format & SIMPLE_WAVE_ULAW)
-               voice->mode |= 0x40;
-       if (w_16) {
-               addr = ((addr << 1) & ~0x1f) | (addr & 0x0f);
-               addr_start = ((addr_start << 1) & ~0x1f) | (addr_start & 0x0f);
-               addr_end = ((addr_end << 1) & ~0x1f) | (addr_end & 0x0f);
-       }
-       addr += begin;
-       addr_start += begin;
-       addr_end += begin;
-       snd_gf1_stop_voice(gus, voice->number); 
-       spin_lock_irqsave(&gus->reg_lock, flags);
-       snd_gf1_select_voice(gus, voice->number);
-       snd_gf1_write16(gus, SNDRV_GF1_VW_FREQUENCY, voice->fc_register + voice->fc_lfo);
-       voice->venv_state = VENV_BEFORE;
-       voice->volume_control = 0x03;
-       snd_gf1_write_addr(gus, SNDRV_GF1_VA_START, addr_start, w_16);
-       snd_gf1_write_addr(gus, SNDRV_GF1_VA_END, addr_end, w_16);
-       snd_gf1_write_addr(gus, SNDRV_GF1_VA_CURRENT, addr, w_16);
-       if (!gus->gf1.enh_mode) {
-               snd_gf1_write8(gus, SNDRV_GF1_VB_PAN, voice->gf1_pan);
-       } else {
-               snd_gf1_write16(gus, SNDRV_GF1_VW_OFFSET_LEFT, voice->vlo);
-               snd_gf1_write16(gus, SNDRV_GF1_VW_OFFSET_LEFT_FINAL, voice->vlo);
-               snd_gf1_write16(gus, SNDRV_GF1_VW_OFFSET_RIGHT, voice->vro);
-               snd_gf1_write16(gus, SNDRV_GF1_VW_OFFSET_RIGHT_FINAL, voice->vro);
-               snd_gf1_write8(gus, SNDRV_GF1_VB_ACCUMULATOR, voice->effect_accumulator);
-               snd_gf1_write16(gus, SNDRV_GF1_VW_EFFECT_VOLUME, voice->gf1_effect_volume);
-               snd_gf1_write16(gus, SNDRV_GF1_VW_EFFECT_VOLUME_FINAL, voice->gf1_effect_volume);
-       }
-       spin_unlock_irqrestore(&gus->reg_lock, flags);
-       do_volume_envelope(gus, voice);
-       spin_lock_irqsave(&gus->reg_lock, flags);
-       snd_gf1_select_voice(gus, voice->number);
-       if (gus->gf1.enh_mode)
-               snd_gf1_write8(gus, SNDRV_GF1_VB_MODE, voice->mode);
-       snd_gf1_write8(gus, SNDRV_GF1_VB_ADDRESS_CONTROL, voice->control);
-       if (!gus->gf1.enh_mode) {
-               snd_gf1_delay(gus);
-               snd_gf1_write8(gus, SNDRV_GF1_VB_ADDRESS_CONTROL, voice->control );
-       }
-       spin_unlock_irqrestore(&gus->reg_lock, flags);
-#if 0
-       snd_gf1_print_voice_registers(gus);
-#endif
-       voice->flags |= SNDRV_GF1_VFLG_RUNNING;
-       snd_seq_instr_free_use(gus->gf1.ilist, instr);
-}
-
-static void sample_stop(struct snd_gus_card *gus, struct snd_gus_voice *voice, int mode)
-{
-       unsigned char control;
-       unsigned long flags;
-
-       if (!(voice->flags & SNDRV_GF1_VFLG_RUNNING))
-               return;
-       switch (mode) {
-       default:
-               if (gus->gf1.volume_ramp > 0) {
-                       if (voice->venv_state < VENV_RELEASE) {
-                               voice->venv_state = VENV_RELEASE;
-                               do_volume_envelope(gus, voice);
-                       }
-               }
-               if (mode != SAMPLE_STOP_VENVELOPE) {
-                       snd_gf1_stop_voice(gus, voice->number);
-                       spin_lock_irqsave(&gus->reg_lock, flags);
-                       snd_gf1_select_voice(gus, voice->number);
-                       snd_gf1_write16(gus, SNDRV_GF1_VW_VOLUME, SNDRV_GF1_MIN_VOLUME);
-                       spin_unlock_irqrestore(&gus->reg_lock, flags);
-                       voice->flags &= ~SNDRV_GF1_VFLG_RUNNING;
-               }
-               break;
-       case SAMPLE_STOP_LOOP:          /* disable loop only */
-               spin_lock_irqsave(&gus->reg_lock, flags);
-               snd_gf1_select_voice(gus, voice->number);
-               control = snd_gf1_read8(gus, SNDRV_GF1_VB_ADDRESS_CONTROL);
-               control &= ~(0x83 | 0x04);
-               control |= 0x20;
-               snd_gf1_write8(gus, SNDRV_GF1_VB_ADDRESS_CONTROL, control);
-               spin_unlock_irqrestore(&gus->reg_lock, flags);
-               break;
-       }
-}
-
-static void sample_freq(struct snd_gus_card *gus, struct snd_gus_voice *voice, snd_seq_frequency_t freq)
-{
-       unsigned long flags;
-
-       spin_lock_irqsave(&gus->reg_lock, flags);
-       voice->fc_register = snd_gf1_translate_freq(gus, freq);
-       snd_gf1_select_voice(gus, voice->number);
-       snd_gf1_write16(gus, SNDRV_GF1_VW_FREQUENCY, voice->fc_register + voice->fc_lfo);
-       spin_unlock_irqrestore(&gus->reg_lock, flags);
-}
-
-static void sample_volume(struct snd_gus_card *gus, struct snd_gus_voice *voice, struct snd_seq_ev_volume *volume)
-{
-       if (volume->volume >= 0) {
-               volume->volume &= 0x3fff;
-               voice->gf1_volume = snd_gf1_lvol_to_gvol_raw(volume->volume << 2) << 4;
-               voice->venv_state_prev = VENV_SUSTAIN;
-               voice->venv_state = VENV_VOLUME;
-               do_volume_envelope(gus, voice);
-        }
-       if (volume->lr >= 0) {
-               volume->lr &= 0x3fff;
-               if (!gus->gf1.enh_mode) {
-                       voice->gf1_pan = (volume->lr >> 10) & 15;
-                       if (!gus->gf1.full_range_pan) {
-                               if (voice->gf1_pan == 0)
-                                       voice->gf1_pan++;
-                               if (voice->gf1_pan == 15)
-                                       voice->gf1_pan--;
-                       }
-                       voice->flags &= ~SNDRV_GF1_VFLG_PAN;    /* before */
-                       do_pan_envelope(gus, voice);
-               } else {
-                       set_enhanced_pan(gus, voice, volume->lr >> 7);
-               }
-       }
-}
-
-static void sample_loop(struct snd_gus_card *gus, struct snd_gus_voice *voice, struct snd_seq_ev_loop *loop)
-{
-       unsigned long flags;
-       int w_16 = voice->control & 0x04;
-       unsigned int begin, addr_start, addr_end;
-       struct simple_instrument *simple;
-       struct snd_seq_kinstr *instr;
-
-#if 0
-       printk("voice_loop: start = 0x%x, end = 0x%x\n", loop->start, loop->end);
-#endif
-       instr = snd_seq_instr_find(gus->gf1.ilist, &voice->instr, 0, 1);
-       if (instr == NULL)
-               return;
-       voice->instr = instr->instr;    /* copy ID to speedup aliases */
-       simple = KINSTR_DATA(instr);
-       begin = simple->address.memory;
-       addr_start = loop->start;
-       addr_end = loop->end;
-       addr_start = (((addr_start << 1) & ~0x1f) | (addr_start & 0x0f)) + begin;
-       addr_end = (((addr_end << 1) & ~0x1f) | (addr_end & 0x0f)) + begin;
-       spin_lock_irqsave(&gus->reg_lock, flags);
-       snd_gf1_select_voice(gus, voice->number);
-       snd_gf1_write_addr(gus, SNDRV_GF1_VA_START, addr_start, w_16);
-       snd_gf1_write_addr(gus, SNDRV_GF1_VA_END, addr_end, w_16);
-       spin_unlock_irqrestore(&gus->reg_lock, flags);
-       snd_seq_instr_free_use(gus->gf1.ilist, instr);
-}
-
-static void sample_pos(struct snd_gus_card *gus, struct snd_gus_voice *voice, snd_seq_position_t position)
-{
-       unsigned long flags;
-       int w_16 = voice->control & 0x04;
-       unsigned int begin, addr;
-       struct simple_instrument *simple;
-       struct snd_seq_kinstr *instr;
-
-#if 0
-       printk("voice_loop: start = 0x%x, end = 0x%x\n", loop->start, loop->end);
-#endif
-       instr = snd_seq_instr_find(gus->gf1.ilist, &voice->instr, 0, 1);
-       if (instr == NULL)
-               return;
-       voice->instr = instr->instr;    /* copy ID to speedup aliases */
-       simple = KINSTR_DATA(instr);
-       begin = simple->address.memory;
-       addr = (((position << 1) & ~0x1f) | (position & 0x0f)) + begin;
-       spin_lock_irqsave(&gus->reg_lock, flags);
-       snd_gf1_select_voice(gus, voice->number);
-       snd_gf1_write_addr(gus, SNDRV_GF1_VA_CURRENT, addr, w_16);
-       spin_unlock_irqrestore(&gus->reg_lock, flags);
-       snd_seq_instr_free_use(gus->gf1.ilist, instr);
-}
-
-#if 0
-
-static unsigned char get_effects_mask( ultra_card_t *card, int value )
-{
-  if ( value > 7 ) return 0;
-  if ( card -> gf1.effects && card -> gf1.effects -> chip_type == ULTRA_EFFECT_CHIP_INTERWAVE )
-    return card -> gf1.effects -> chip.interwave.voice_output[ value ];
-  return 0;
-}
-
-#endif
-
-static void sample_private1(struct snd_gus_card *card, struct snd_gus_voice *voice, unsigned char *data)
-{
-#if 0
-  unsigned long flags;
-  unsigned char uc;
-
-  switch ( *data ) {
-    case ULTRA_PRIV1_IW_EFFECT:
-      uc = get_effects_mask( card, ultra_get_byte( data, 4 ) );
-      uc |= get_effects_mask( card, ultra_get_byte( data, 4 ) >> 4 );
-      uc |= get_effects_mask( card, ultra_get_byte( data, 5 ) );
-      uc |= get_effects_mask( card, ultra_get_byte( data, 5 ) >> 4 );
-      voice -> data.simple.effect_accumulator = uc;
-      voice -> data.simple.effect_volume = ultra_translate_voice_volume( card, ultra_get_word( data, 2 ) ) << 4;
-      if ( !card -> gf1.enh_mode ) return;
-      if ( voice -> flags & VFLG_WAIT_FOR_START ) return;
-      if ( voice -> flags & VFLG_RUNNING )
-        {
-          CLI( &flags );
-          gf1_select_voice( card, voice -> number );
-          ultra_write8( card, GF1_VB_ACCUMULATOR, voice -> data.simple.effect_accumulator );
-          ultra_write16( card, GF1_VW_EFFECT_VOLUME_FINAL, voice -> data.simple.effect_volume );
-          STI( &flags );
-        }
-      break;
-   case ULTRA_PRIV1_IW_LFO:
-     ultra_lfo_command( card, voice -> number, data );
-  }
-#endif
-}
-
-#if 0
-
-/*
- *
- */
-
-static void note_stop( ultra_card_t *card, ultra_voice_t *voice, int wait )
-{
-}
-
-static void note_wait( ultra_card_t *card, ultra_voice_t *voice )
-{
-}
-
-static void note_off( ultra_card_t *card, ultra_voice_t *voice )
-{
-}
-
-static void note_volume( ultra_card_t *card, ultra_voice_t *voice )
-{
-}
-
-static void note_pitchbend( ultra_card_t *card, ultra_voice_t *voice )
-{
-}
-
-static void note_vibrato( ultra_card_t *card, ultra_voice_t *voice )
-{
-}
-
-static void note_tremolo( ultra_card_t *card, ultra_voice_t *voice )
-{
-}
-
-/*
- *
- */
-static void chn_trigger_down( ultra_card_t *card, ultra_channel_t *channel, ultra_instrument_t *instrument, unsigned char note, unsigned char velocity, unsigned char priority )
-{
-}
-
-static void chn_trigger_up( ultra_card_t *card, ultra_note_t *note )
-{
-}
-
-static void chn_control( ultra_card_t *card, ultra_channel_t *channel, unsigned short p1, unsigned short p2 )
-{
-}
-
-/*
- *
- */
-#endif
-
-void snd_gf1_simple_init(struct snd_gus_voice *voice)
-{
-       voice->handler_wave = interrupt_wave;
-       voice->handler_volume = interrupt_volume;
-       voice->handler_effect = interrupt_effect;
-       voice->volume_change = NULL;
-       voice->sample_ops = &sample_ops;
-}
diff --git a/sound/isa/gus/gus_synth.c b/sound/isa/gus/gus_synth.c
deleted file mode 100644 (file)
index 2c20517..0000000
+++ /dev/null
@@ -1,314 +0,0 @@
-/*
- *  Routines for Gravis UltraSound soundcards - Synthesizer
- *  Copyright (c) by Jaroslav Kysela <perex@perex.cz>
- *
- *
- *   This program is free software; you can redistribute it and/or modify
- *   it under the terms of the GNU General Public License as published by
- *   the Free Software Foundation; either version 2 of the License, or
- *   (at your option) any later version.
- *
- *   This program is distributed in the hope that it will be useful,
- *   but WITHOUT ANY WARRANTY; without even the implied warranty of
- *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- *   GNU General Public License for more details.
- *
- *   You should have received a copy of the GNU General Public License
- *   along with this program; if not, write to the Free Software
- *   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA
- *
- */
-
-#include <sound/driver.h>
-#include <linux/init.h>
-#include <linux/time.h>
-#include <sound/core.h>
-#include <sound/gus.h>
-#include <sound/seq_device.h>
-
-MODULE_AUTHOR("Jaroslav Kysela <perex@perex.cz>");
-MODULE_DESCRIPTION("Routines for Gravis UltraSound soundcards - Synthesizer");
-MODULE_LICENSE("GPL");
-
-/*
- *
- */
-
-static void snd_gus_synth_free_voices(struct snd_gus_card * gus, int client, int port)
-{
-       int idx;
-       struct snd_gus_voice * voice;
-       
-       for (idx = 0; idx < 32; idx++) {
-               voice = &gus->gf1.voices[idx];
-               if (voice->use && voice->client == client && voice->port == port)
-                       snd_gf1_free_voice(gus, voice);
-       }
-}
-
-static int snd_gus_synth_use(void *private_data, struct snd_seq_port_subscribe *info)
-{
-       struct snd_gus_port * port = private_data;
-       struct snd_gus_card * gus = port->gus;
-       struct snd_gus_voice * voice;
-       unsigned int idx;
-
-       if (info->voices > 32)
-               return -EINVAL;
-       mutex_lock(&gus->register_mutex);
-       if (!snd_gus_use_inc(gus)) {
-               mutex_unlock(&gus->register_mutex);
-               return -EFAULT;
-       }
-       for (idx = 0; idx < info->voices; idx++) {
-               voice = snd_gf1_alloc_voice(gus, SNDRV_GF1_VOICE_TYPE_SYNTH, info->sender.client, info->sender.port);
-               if (voice == NULL) {
-                       snd_gus_synth_free_voices(gus, info->sender.client, info->sender.port);
-                       snd_gus_use_dec(gus);
-                       mutex_unlock(&gus->register_mutex);
-                       return -EBUSY;
-               }
-               voice->index = idx;
-       }
-       mutex_unlock(&gus->register_mutex);
-       return 0;
-}
-
-static int snd_gus_synth_unuse(void *private_data, struct snd_seq_port_subscribe *info)
-{
-       struct snd_gus_port * port = private_data;
-       struct snd_gus_card * gus = port->gus;
-
-       mutex_lock(&gus->register_mutex);
-       snd_gus_synth_free_voices(gus, info->sender.client, info->sender.port);
-       snd_gus_use_dec(gus);
-       mutex_unlock(&gus->register_mutex);
-       return 0;
-}
-
-/*
- *
- */
-
-static void snd_gus_synth_free_private_instruments(struct snd_gus_port *p, int client)
-{
-       struct snd_seq_instr_header ifree;
-
-       memset(&ifree, 0, sizeof(ifree));
-       ifree.cmd = SNDRV_SEQ_INSTR_FREE_CMD_PRIVATE;
-       snd_seq_instr_list_free_cond(p->gus->gf1.ilist, &ifree, client, 0);
-}
-static int snd_gus_synth_event_input(struct snd_seq_event *ev, int direct,
-                                    void *private_data, int atomic, int hop)
-{
-       struct snd_gus_port * p = private_data;
-       
-       snd_assert(p != NULL, return -EINVAL);
-       if (ev->type >= SNDRV_SEQ_EVENT_SAMPLE &&
-           ev->type <= SNDRV_SEQ_EVENT_SAMPLE_PRIVATE1) {
-               snd_gus_sample_event(ev, p);
-               return 0;
-       }
-       if (ev->source.client == SNDRV_SEQ_CLIENT_SYSTEM &&
-           ev->source.port == SNDRV_SEQ_PORT_SYSTEM_ANNOUNCE) {
-               if (ev->type == SNDRV_SEQ_EVENT_CLIENT_EXIT) {
-                       snd_gus_synth_free_private_instruments(p, ev->data.addr.client);
-                       return 0;
-               }
-       }
-       if (direct) {
-               if (ev->type >= SNDRV_SEQ_EVENT_INSTR_BEGIN) {
-                       snd_seq_instr_event(&p->gus->gf1.iwffff_ops.kops,
-                                           p->gus->gf1.ilist,
-                                           ev,
-                                           p->gus->gf1.seq_client,
-                                           atomic, hop);
-                       return 0;
-               }
-       }
-       return 0;
-}
-
-static void snd_gus_synth_instr_notify(void *private_data,
-                                      struct snd_seq_kinstr *instr,
-                                      int what)
-{
-       unsigned int idx;
-       struct snd_gus_card *gus = private_data;
-       struct snd_gus_voice *pvoice;
-       unsigned long flags;
-       
-       spin_lock_irqsave(&gus->event_lock, flags);
-       for (idx = 0; idx < 32; idx++) {
-               pvoice = &gus->gf1.voices[idx];
-               if (pvoice->use && !memcmp(&pvoice->instr, &instr->instr, sizeof(pvoice->instr))) {
-                       if (pvoice->sample_ops && pvoice->sample_ops->sample_stop) {
-                               pvoice->sample_ops->sample_stop(gus, pvoice, SAMPLE_STOP_IMMEDIATELY);
-                       } else {
-                               snd_gf1_stop_voice(gus, pvoice->number);
-                               pvoice->flags &= ~SNDRV_GF1_VFLG_RUNNING;
-                       }
-               }
-       }
-       spin_unlock_irqrestore(&gus->event_lock, flags);
-}
-
-/*
- *
- */
-
-static void snd_gus_synth_free_port(void *private_data)
-{
-       struct snd_gus_port * p = private_data;
-       
-       if (p)
-               snd_midi_channel_free_set(p->chset);
-}
-
-static int snd_gus_synth_create_port(struct snd_gus_card * gus, int idx)
-{
-       struct snd_gus_port * p;
-       struct snd_seq_port_callback callbacks;
-       char name[32];
-       int result;
-       
-       p = &gus->gf1.seq_ports[idx];
-       p->chset = snd_midi_channel_alloc_set(16);
-       if (p->chset == NULL)
-               return -ENOMEM;
-       p->chset->private_data = p;
-       p->gus = gus;
-       p->client = gus->gf1.seq_client;
-
-       memset(&callbacks, 0, sizeof(callbacks));
-       callbacks.owner = THIS_MODULE;
-       callbacks.use = snd_gus_synth_use;
-       callbacks.unuse = snd_gus_synth_unuse;
-       callbacks.event_input = snd_gus_synth_event_input;
-       callbacks.private_free = snd_gus_synth_free_port;
-       callbacks.private_data = p;
-       
-       sprintf(name, "%s port %i", gus->interwave ? "AMD InterWave" : "GF1", idx);
-       p->chset->port = snd_seq_event_port_attach(gus->gf1.seq_client,
-                                                  &callbacks,
-                                                  SNDRV_SEQ_PORT_CAP_WRITE | SNDRV_SEQ_PORT_CAP_SUBS_WRITE,
-                                                  SNDRV_SEQ_PORT_TYPE_DIRECT_SAMPLE |
-                                                  SNDRV_SEQ_PORT_TYPE_SYNTH |
-                                                  SNDRV_SEQ_PORT_TYPE_HARDWARE |
-                                                  SNDRV_SEQ_PORT_TYPE_SYNTHESIZER,
-                                                  16, 0,
-                                                  name);
-       if (p->chset->port < 0) {
-               result = p->chset->port;
-               snd_gus_synth_free_port(p);
-               return result;
-       }
-       p->port = p->chset->port;
-       return 0;
-}                                               
-
-/*
- *
- */
-
-static int snd_gus_synth_new_device(struct snd_seq_device *dev)
-{
-       struct snd_gus_card *gus;
-       int client, i;
-       struct snd_seq_port_subscribe sub;
-       struct snd_iwffff_ops *iwops;
-       struct snd_gf1_ops *gf1ops;
-       struct snd_simple_ops *simpleops;
-
-       gus = *(struct snd_gus_card **)SNDRV_SEQ_DEVICE_ARGPTR(dev);
-       if (gus == NULL)
-               return -EINVAL;
-
-       mutex_init(&gus->register_mutex);
-       gus->gf1.seq_client = -1;
-       
-       /* allocate new client */
-       client = gus->gf1.seq_client =
-               snd_seq_create_kernel_client(gus->card, 1, gus->interwave ?
-                                            "AMD InterWave" : "GF1");
-       if (client < 0)
-               return client;
-
-       for (i = 0; i < 4; i++)
-               snd_gus_synth_create_port(gus, i);
-               
-       gus->gf1.ilist = snd_seq_instr_list_new();
-       if (gus->gf1.ilist == NULL) {
-               snd_seq_delete_kernel_client(client);   
-               gus->gf1.seq_client = -1;
-               return -ENOMEM;
-       }
-       gus->gf1.ilist->flags = SNDRV_SEQ_INSTR_FLG_DIRECT;
-
-       simpleops = &gus->gf1.simple_ops;
-       snd_seq_simple_init(simpleops, gus, NULL);
-       simpleops->put_sample = snd_gus_simple_put_sample;
-       simpleops->get_sample = snd_gus_simple_get_sample;
-       simpleops->remove_sample = snd_gus_simple_remove_sample;
-       simpleops->notify = snd_gus_synth_instr_notify;
-
-       gf1ops = &gus->gf1.gf1_ops;
-       snd_seq_gf1_init(gf1ops, gus, &simpleops->kops);
-       gf1ops->put_sample = snd_gus_gf1_put_sample;
-       gf1ops->get_sample = snd_gus_gf1_get_sample;
-       gf1ops->remove_sample = snd_gus_gf1_remove_sample;
-       gf1ops->notify = snd_gus_synth_instr_notify;
-
-       iwops = &gus->gf1.iwffff_ops;
-       snd_seq_iwffff_init(iwops, gus, &gf1ops->kops);
-       iwops->put_sample = snd_gus_iwffff_put_sample;
-       iwops->get_sample = snd_gus_iwffff_get_sample;
-       iwops->remove_sample = snd_gus_iwffff_remove_sample;
-       iwops->notify = snd_gus_synth_instr_notify;
-
-       memset(&sub, 0, sizeof(sub));
-       sub.sender.client = SNDRV_SEQ_CLIENT_SYSTEM;
-       sub.sender.port = SNDRV_SEQ_PORT_SYSTEM_ANNOUNCE;
-       sub.dest.client = client;
-       sub.dest.port = 0;
-       snd_seq_kernel_client_ctl(client, SNDRV_SEQ_IOCTL_SUBSCRIBE_PORT, &sub);
-
-       return 0;
-}
-
-static int snd_gus_synth_delete_device(struct snd_seq_device *dev)
-{
-       struct snd_gus_card *gus;
-
-       gus = *(struct snd_gus_card **)SNDRV_SEQ_DEVICE_ARGPTR(dev);
-       if (gus == NULL)
-               return -EINVAL;
-
-       if (gus->gf1.seq_client >= 0) {
-               snd_seq_delete_kernel_client(gus->gf1.seq_client);      
-               gus->gf1.seq_client = -1;
-       }
-       if (gus->gf1.ilist)
-               snd_seq_instr_list_free(&gus->gf1.ilist);
-       return 0;
-}
-
-static int __init alsa_gus_synth_init(void)
-{
-       static struct snd_seq_dev_ops ops = {
-               snd_gus_synth_new_device,
-               snd_gus_synth_delete_device
-       };
-
-       return snd_seq_device_register_driver(SNDRV_SEQ_DEV_ID_GUS, &ops,
-                                             sizeof(struct snd_gus_card *));
-}
-
-static void __exit alsa_gus_synth_exit(void)
-{
-       snd_seq_device_unregister_driver(SNDRV_SEQ_DEV_ID_GUS);
-}
-
-module_init(alsa_gus_synth_init)
-module_exit(alsa_gus_synth_exit)
index 99eac573c41418542da212d6ec0b1a55464d85e9..c53727147a1ac6c9fae64513f7d6ae63ecb91919 100644 (file)
@@ -21,7 +21,6 @@
  *
  */
 
-#include <sound/driver.h>
 #include <linux/time.h>
 #include <sound/core.h>
 #include <sound/gus.h>
index e6fd9b01c492dc37b813770e24271db99777e0fb..f0af3f79b08bae4167eb98ed00e9d7b107b5559d 100644 (file)
@@ -19,7 +19,6 @@
  *
  */
 
-#include <sound/driver.h>
 #include <linux/delay.h>
 #include <linux/interrupt.h>
 #include <linux/time.h>
index 71a67744a14b04905f6a48c0140f2be4d2c80e1b..c3c028a4a46b5a5b7c0b3c25c838a05754b6f4ce 100644 (file)
@@ -18,7 +18,6 @@
  *
  */
 
-#include <sound/driver.h>
 #include <linux/time.h>
 #include <sound/core.h>
 #include <sound/gus.h>
index 29e422b00b5801c23087e33319ccf10637cf9129..8f914b37bf8998eaa261127b9dc53901e6b2d8b5 100644 (file)
@@ -19,7 +19,6 @@
  *
  */
 
-#include <sound/driver.h>
 #include <linux/init.h>
 #include <linux/err.h>
 #include <linux/isa.h>
index fc59536c918e6fb9982087b08f437ed6b805ceb1..da13185eb0a034852117982d7069b108b0004bd8 100644 (file)
@@ -19,7 +19,6 @@
  *
  */
 
-#include <sound/driver.h>
 #include <linux/init.h>
 #include <linux/err.h>
 #include <linux/isa.h>
index 4922f5da08f9d4f5c7b4971c1fcb4e22652fb2f7..f87c6236661c6ea985134dde22d705d52c7b0860 100644 (file)
@@ -19,7 +19,6 @@
  *
  */
 
-#include <sound/driver.h>
 #include <linux/init.h>
 #include <linux/err.h>
 #include <linux/isa.h>
index 2091c50b2e3ed179da6c449d96b7e27d3fd94b8a..ca0d7ace0c75abec9a6b9006a716e5cddc4b9a37 100644 (file)
@@ -22,7 +22,6 @@
  *
  */
 
-#include <sound/driver.h>
 #include <linux/init.h>
 #include <linux/err.h>
 #include <linux/isa.h>
@@ -560,50 +559,27 @@ static int __devinit snd_interwave_pnp(int dev, struct snd_interwave *iwcard,
                                       const struct pnp_card_device_id *id)
 {
        struct pnp_dev *pdev;
-       struct pnp_resource_table * cfg = kmalloc(sizeof(struct pnp_resource_table), GFP_KERNEL);
        int err;
 
-       if (!cfg)
-               return -ENOMEM;
        iwcard->dev = pnp_request_card_device(card, id->devs[0].id, NULL);
-       if (iwcard->dev == NULL) {
-               kfree(cfg);
+       if (iwcard->dev == NULL)
                return -EBUSY;
-       }
+
 #ifdef SNDRV_STB
        iwcard->devtc = pnp_request_card_device(card, id->devs[1].id, NULL);
-       if (iwcard->devtc == NULL) {
-               kfree(cfg);
+       if (iwcard->devtc == NULL)
                return -EBUSY;
-       }
 #endif
        /* Synth & Codec initialization */
        pdev = iwcard->dev;
-       pnp_init_resource_table(cfg);
-       if (port[dev] != SNDRV_AUTO_PORT) {
-               pnp_resource_change(&cfg->port_resource[0], port[dev], 16);
-               pnp_resource_change(&cfg->port_resource[1], port[dev] + 0x100, 12);
-               pnp_resource_change(&cfg->port_resource[2], port[dev] + 0x10c, 4);
-       }
-       if (dma1[dev] != SNDRV_AUTO_DMA)
-               pnp_resource_change(&cfg->dma_resource[0], dma1[dev], 1);
-       if (dma2[dev] != SNDRV_AUTO_DMA)
-               pnp_resource_change(&cfg->dma_resource[1], dma2[dev], 1);
-       if (dma2[dev] < 0)
-               pnp_resource_change(&cfg->dma_resource[1], 4, 1);
-       if (irq[dev] != SNDRV_AUTO_IRQ)
-               pnp_resource_change(&cfg->irq_resource[0], irq[dev], 1);
-        if (pnp_manual_config_dev(pdev, cfg, 0) < 0)
-               snd_printk(KERN_ERR "InterWave - Synth - the requested resources are invalid, using auto config\n");
+
        err = pnp_activate_dev(pdev);
        if (err < 0) {
-               kfree(cfg);
                snd_printk(KERN_ERR "InterWave PnP configure failure (out of resources?)\n");
                return err;
        }
        if (pnp_port_start(pdev, 0) + 0x100 != pnp_port_start(pdev, 1) ||
            pnp_port_start(pdev, 0) + 0x10c != pnp_port_start(pdev, 2)) {
-               kfree(cfg);
                snd_printk(KERN_ERR "PnP configure failure (wrong ports)\n");
                return -ENOENT;
        }
@@ -620,21 +596,15 @@ static int __devinit snd_interwave_pnp(int dev, struct snd_interwave *iwcard,
 #ifdef SNDRV_STB
        /* Tone Control initialization */
        pdev = iwcard->devtc;
-       pnp_init_resource_table(cfg);
-       if (port_tc[dev] != SNDRV_AUTO_PORT)
-               pnp_resource_change(&cfg->port_resource[0], port_tc[dev], 1);
-        if (pnp_manual_config_dev(pdev, cfg, 0) < 0)
-               snd_printk(KERN_ERR "InterWave - ToneControl - the requested resources are invalid, using auto config\n");
+
        err = pnp_activate_dev(pdev);
        if (err < 0) {
-               kfree(cfg);
                snd_printk(KERN_ERR "InterWave ToneControl PnP configure failure (out of resources?)\n");
                return err;
        }
        port_tc[dev] = pnp_port_start(pdev, 0);
        snd_printdd("isapnp IW: tone control port=0x%lx\n", port_tc[dev]);
 #endif
-       kfree(cfg);
        return 0;
 }
 #endif /* CONFIG_PNP */
index 59af9ab7191fb81d4355b5e58ee61ee5ad2c31a6..854a9f74b46665d0fc6b418e49127fe4086ea25a 100644 (file)
@@ -19,7 +19,6 @@
  *
  */
 
-#include <sound/driver.h>
 #include <linux/init.h>
 #include <linux/err.h>
 #include <linux/isa.h>
@@ -610,39 +609,8 @@ static int snd_opl3sa2_resume(struct snd_card *card)
 static int __devinit snd_opl3sa2_pnp(int dev, struct snd_opl3sa2 *chip,
                                     struct pnp_dev *pdev)
 {
-       struct pnp_resource_table * cfg;
-       int err;
-
-       cfg = kmalloc(sizeof(struct pnp_resource_table), GFP_KERNEL);
-       if (!cfg) {
-               snd_printk(KERN_ERR PFX "cannot allocate pnp cfg\n");
-               return -ENOMEM;
-       }
-       /* PnP initialization */
-       pnp_init_resource_table(cfg);
-       if (sb_port[dev] != SNDRV_AUTO_PORT)
-               pnp_resource_change(&cfg->port_resource[0], sb_port[dev], 16);
-       if (wss_port[dev] != SNDRV_AUTO_PORT)
-               pnp_resource_change(&cfg->port_resource[1], wss_port[dev], 8);
-       if (fm_port[dev] != SNDRV_AUTO_PORT)
-               pnp_resource_change(&cfg->port_resource[2], fm_port[dev], 4);
-       if (midi_port[dev] != SNDRV_AUTO_PORT)
-               pnp_resource_change(&cfg->port_resource[3], midi_port[dev], 2);
-       if (port[dev] != SNDRV_AUTO_PORT)
-               pnp_resource_change(&cfg->port_resource[4], port[dev], 2);
-       if (dma1[dev] != SNDRV_AUTO_DMA)
-               pnp_resource_change(&cfg->dma_resource[0], dma1[dev], 1);
-       if (dma2[dev] != SNDRV_AUTO_DMA)
-               pnp_resource_change(&cfg->dma_resource[1], dma2[dev], 1);
-       if (irq[dev] != SNDRV_AUTO_IRQ)
-               pnp_resource_change(&cfg->irq_resource[0], irq[dev], 1);
-       err = pnp_manual_config_dev(pdev, cfg, 0);
-       if (err < 0)
-               snd_printk(KERN_WARNING "PnP manual resources are invalid, using auto config\n");
-       err = pnp_activate_dev(pdev);
-       if (err < 0) {
-               kfree(cfg);
-               snd_printk(KERN_ERR "PnP configure failure (out of resources?) err = %d\n", err);
+       if (pnp_activate_dev(pdev) < 0) {
+               snd_printk(KERN_ERR "PnP configure failure (out of resources?)\n");
                return -EBUSY;
        }
        sb_port[dev] = pnp_port_start(pdev, 0);
@@ -657,7 +625,6 @@ static int __devinit snd_opl3sa2_pnp(int dev, struct snd_opl3sa2 *chip,
                pnp_device_is_pnpbios(pdev) ? "BIOS" : "ISA", sb_port[dev], wss_port[dev], fm_port[dev], midi_port[dev]);
        snd_printdd("%sPnP OPL3-SA: control port=0x%lx, dma1=%i, dma2=%i, irq=%i\n",
                pnp_device_is_pnpbios(pdev) ? "BIOS" : "ISA", port[dev], dma1[dev], dma2[dev], irq[dev]);
-       kfree(cfg);
        return 0;
 }
 #endif /* CONFIG_PNP */
index d295936611f83b696dfedf87b6aa396a1c80981d..2a1e2f5d12c2abe78e99192d31c14d39b55b7f13 100644 (file)
@@ -22,7 +22,6 @@
  *   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA
  */
 
-#include <sound/driver.h>
 #include <linux/init.h>
 #include <linux/err.h>
 #include <linux/isa.h>
@@ -483,6 +482,10 @@ static int snd_miro_put_double(struct snd_kcontrol *kcontrol,
 
                /* equalizer elements */
 
+               if (left < -0x7f || left > 0x7f ||
+                   right < -0x7f || right > 0x7f)
+                       return -EINVAL;
+
                if (left_old > 0x80) 
                        left_old = 0x80 - left_old;
                if (right_old > 0x80) 
@@ -520,6 +523,10 @@ static int snd_miro_put_double(struct snd_kcontrol *kcontrol,
 
                /* non-equalizer elements */
 
+               if (left < 0 || left > 0x20 ||
+                   right < 0 || right > 0x20)
+                       return -EINVAL;
+
                left_old = 0x20 - left_old;
                right_old = 0x20 - right_old;
 
@@ -662,7 +669,7 @@ static int __devinit snd_set_aci_init_values(struct snd_miro *miro)
        return 0;
 }
 
-static int snd_miro_mixer(struct snd_miro *miro)
+static int __devinit snd_miro_mixer(struct snd_miro *miro)
 {
        struct snd_card *card;
        unsigned int idx;
index ee1a824d8fc0c388284fa28caae16891a6effcb5..fe1afc13a01d4bdf6a373a40611f24e734788dcf 100644 (file)
@@ -23,7 +23,6 @@
 */
 
 
-#include <sound/driver.h>
 #include <linux/init.h>
 #include <linux/err.h>
 #include <linux/isa.h>
@@ -1595,7 +1594,7 @@ OPTi93X_DOUBLE("Capture Volume", 0, OPTi93X_MIXOUT_LEFT, OPTi93X_MIXOUT_RIGHT, 0
 }
 };
                                         
-static int snd_opti93x_mixer(struct snd_opti93x *chip)
+static int __devinit snd_opti93x_mixer(struct snd_opti93x *chip)
 {
        struct snd_card *card;
        struct snd_kcontrol_new knew;
@@ -1690,53 +1689,19 @@ static int __devinit snd_card_opti9xx_pnp(struct snd_opti9xx *chip,
                                          const struct pnp_card_device_id *pid)
 {
        struct pnp_dev *pdev;
-       struct pnp_resource_table *cfg = kmalloc(sizeof(*cfg), GFP_KERNEL);
        int err;
 
-       if (!cfg)
-               return -ENOMEM;
        chip->dev = pnp_request_card_device(card, pid->devs[0].id, NULL);
-       if (chip->dev == NULL) {
-               kfree(cfg);
+       if (chip->dev == NULL)
                return -EBUSY;
-       }
+
        chip->devmpu = pnp_request_card_device(card, pid->devs[1].id, NULL);
 
        pdev = chip->dev;
-       pnp_init_resource_table(cfg);
 
-#ifdef OPTi93X
-       if (port != SNDRV_AUTO_PORT)
-               pnp_resource_change(&cfg->port_resource[0], port + 4, 4);
-#else
-       if (pid->driver_data != 0x0924 && port != SNDRV_AUTO_PORT)
-               pnp_resource_change(&cfg->port_resource[1], port, 4);
-#endif /* OPTi93X */
-       if (irq != SNDRV_AUTO_IRQ)
-               pnp_resource_change(&cfg->irq_resource[0], irq, 1);
-       if (dma1 != SNDRV_AUTO_DMA)
-               pnp_resource_change(&cfg->dma_resource[0], dma1, 1);
-#if defined(CS4231) || defined(OPTi93X)
-       if (dma2 != SNDRV_AUTO_DMA)
-               pnp_resource_change(&cfg->dma_resource[1], dma2, 1);
-#else
-#ifdef snd_opti9xx_fixup_dma2
-       snd_opti9xx_fixup_dma2(pdev);
-#endif
-#endif /* CS4231 || OPTi93X */
-#ifdef OPTi93X
-       if (fm_port > 0 && fm_port != SNDRV_AUTO_PORT)
-               pnp_resource_change(&cfg->port_resource[1], fm_port, 4);
-#else
-       if (fm_port > 0 && fm_port != SNDRV_AUTO_PORT)
-               pnp_resource_change(&cfg->port_resource[2], fm_port, 4);
-#endif
-       if (pnp_manual_config_dev(pdev, cfg, 0) < 0)
-               snd_printk(KERN_ERR "AUDIO the requested resources are invalid, using auto config\n");
        err = pnp_activate_dev(pdev);
        if (err < 0) {
                snd_printk(KERN_ERR "AUDIO pnp configure failure: %d\n", err);
-               kfree(cfg);
                return err;
        }
 
@@ -1756,15 +1721,6 @@ static int __devinit snd_card_opti9xx_pnp(struct snd_opti9xx *chip,
 
        pdev = chip->devmpu;
        if (pdev && mpu_port > 0) {
-               pnp_init_resource_table(cfg);
-
-               if (mpu_port != SNDRV_AUTO_PORT)
-                       pnp_resource_change(&cfg->port_resource[0], mpu_port, 2);
-               if (mpu_irq != SNDRV_AUTO_IRQ)
-                       pnp_resource_change(&cfg->irq_resource[0], mpu_irq, 1);
-
-               if (pnp_manual_config_dev(pdev, cfg, 0) < 0)
-                       snd_printk(KERN_ERR "AUDIO the requested resources are invalid, using auto config\n");
                err = pnp_activate_dev(pdev);
                if (err < 0) {
                        snd_printk(KERN_ERR "AUDIO pnp configure failure\n");
@@ -1775,7 +1731,6 @@ static int __devinit snd_card_opti9xx_pnp(struct snd_opti9xx *chip,
                        mpu_irq = pnp_irq(pdev, 0);
                }
        }
-       kfree(cfg);
        return pid->driver_data;
 }
 #endif /* CONFIG_PNP */
index 4eea84cfd4f4b703b5e6dd6f5bb86f230e091ae2..b35be7d9a9fa8b4a70e1037534c2e8bd0b821d9f 100644 (file)
@@ -20,7 +20,6 @@
  *   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA
  */
 
-#include <sound/driver.h>
 #include <linux/wait.h>
 #include <linux/sched.h>
 #include <linux/slab.h>
index 2ac77f10bb4e4e56b81b7f170eaa64d9aee972bf..7e87c349272ff20fa31f1f306832a853df93098f 100644 (file)
@@ -21,7 +21,6 @@
  *   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA
  */
 
-#include <sound/driver.h>
 #include <linux/wait.h>
 #include <linux/sched.h>
 #include <linux/slab.h>
index d4b218726ce75b02b186ff543d37d487ea32d744..c8c8e214c843421a6288a30b8634cf5a76138134 100644 (file)
@@ -20,7 +20,6 @@
     Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA
 */
 
-#include <sound/driver.h>
 #include <linux/init.h>
 #include <linux/time.h>
 #include <linux/pnp.h>
@@ -49,12 +48,6 @@ module_param_array(id, charp, NULL, 0444);
 MODULE_PARM_DESC(id, "ID string for es968 based soundcard.");
 module_param_array(enable, bool, NULL, 0444);
 MODULE_PARM_DESC(enable, "Enable es968 based soundcard.");
-module_param_array(port, long, NULL, 0444);
-MODULE_PARM_DESC(port, "Port # for es968 driver.");
-module_param_array(irq, int, NULL, 0444);
-MODULE_PARM_DESC(irq, "IRQ # for es968 driver.");
-module_param_array(dma8, int, NULL, 0444);
-MODULE_PARM_DESC(dma8, "8-bit DMA # for es968 driver.");
 
 struct snd_card_es968 {
        struct pnp_dev *dev;
@@ -86,40 +79,23 @@ static int __devinit snd_card_es968_pnp(int dev, struct snd_card_es968 *acard,
                                        const struct pnp_card_device_id *id)
 {
        struct pnp_dev *pdev;
-       struct pnp_resource_table *cfg = kmalloc(sizeof(*cfg), GFP_KERNEL);
        int err;
-       if (!cfg)
-               return -ENOMEM;
+
        acard->dev = pnp_request_card_device(card, id->devs[0].id, NULL);
-       if (acard->dev == NULL) {
-               kfree(cfg);
+       if (acard->dev == NULL)
                return -ENODEV;
-       }
 
        pdev = acard->dev;
 
-       pnp_init_resource_table(cfg);
-
-       /* override resources */
-       if (port[dev] != SNDRV_AUTO_PORT)
-               pnp_resource_change(&cfg->port_resource[0], port[dev], 16);
-       if (dma8[dev] != SNDRV_AUTO_DMA)
-               pnp_resource_change(&cfg->dma_resource[0], dma8[dev], 1);
-       if (irq[dev] != SNDRV_AUTO_IRQ)
-               pnp_resource_change(&cfg->irq_resource[0], irq[dev], 1);
-       if ((pnp_manual_config_dev(pdev, cfg, 0)) < 0)
-               snd_printk(KERN_ERR PFX "AUDIO the requested resources are invalid, using auto config\n");
        err = pnp_activate_dev(pdev);
        if (err < 0) {
                snd_printk(KERN_ERR PFX "AUDIO pnp configure failure\n");
-               kfree(cfg);
                return err;
        }
        port[dev] = pnp_port_start(pdev, 0);
        dma8[dev] = pnp_dma(pdev, 1);
        irq[dev] = pnp_irq(pdev, 0);
 
-       kfree(cfg);
        return 0;
 }
 
index e7f9edd92626f824c1f6adbe05540a062f7523a1..2c201f78ce50a0abd79238c51d81e8f8b8482952 100644 (file)
@@ -19,7 +19,6 @@
  *
  */
 
-#include <sound/driver.h>
 #include <asm/dma.h>
 #include <linux/init.h>
 #include <linux/slab.h>
@@ -257,44 +256,21 @@ static int __devinit snd_card_sb16_pnp(int dev, struct snd_card_sb16 *acard,
                                       const struct pnp_card_device_id *id)
 {
        struct pnp_dev *pdev;
-       struct pnp_resource_table * cfg = kmalloc(sizeof(struct pnp_resource_table), GFP_KERNEL);
        int err;
 
-       if (!cfg) 
-               return -ENOMEM; 
        acard->dev = pnp_request_card_device(card, id->devs[0].id, NULL);
-       if (acard->dev == NULL) { 
-               kfree(cfg); 
+       if (acard->dev == NULL)
                return -ENODEV; 
-       } 
+
 #ifdef SNDRV_SBAWE_EMU8000
        acard->devwt = pnp_request_card_device(card, id->devs[1].id, acard->dev);
 #endif
        /* Audio initialization */
        pdev = acard->dev;
 
-       pnp_init_resource_table(cfg); 
-        
-       /* override resources */ 
-
-       if (port[dev] != SNDRV_AUTO_PORT)
-               pnp_resource_change(&cfg->port_resource[0], port[dev], 16);
-       if (mpu_port[dev] != SNDRV_AUTO_PORT)
-               pnp_resource_change(&cfg->port_resource[1], mpu_port[dev], 2);
-       if (fm_port[dev] != SNDRV_AUTO_PORT)
-               pnp_resource_change(&cfg->port_resource[2], fm_port[dev], 4);
-       if (dma8[dev] != SNDRV_AUTO_DMA)
-               pnp_resource_change(&cfg->dma_resource[0], dma8[dev], 1);
-       if (dma16[dev] != SNDRV_AUTO_DMA)
-               pnp_resource_change(&cfg->dma_resource[1], dma16[dev], 1);
-       if (irq[dev] != SNDRV_AUTO_IRQ)
-               pnp_resource_change(&cfg->irq_resource[0], irq[dev], 1);
-       if (pnp_manual_config_dev(pdev, cfg, 0) < 0) 
-               snd_printk(KERN_ERR PFX "AUDIO the requested resources are invalid, using auto config\n"); 
        err = pnp_activate_dev(pdev); 
        if (err < 0) { 
                snd_printk(KERN_ERR PFX "AUDIO pnp configure failure\n"); 
-               kfree(cfg);
                return err; 
        } 
        port[dev] = pnp_port_start(pdev, 0);
@@ -311,17 +287,6 @@ static int __devinit snd_card_sb16_pnp(int dev, struct snd_card_sb16 *acard,
        /* WaveTable initialization */
        pdev = acard->devwt;
        if (pdev != NULL) {
-               pnp_init_resource_table(cfg); 
-        
-               /* override resources */ 
-
-               if (awe_port[dev] != SNDRV_AUTO_PORT) {
-                       pnp_resource_change(&cfg->port_resource[0], awe_port[dev], 4);
-                       pnp_resource_change(&cfg->port_resource[1], awe_port[dev] + 0x400, 4);
-                       pnp_resource_change(&cfg->port_resource[2], awe_port[dev] + 0x800, 4);
-               }
-               if ((pnp_manual_config_dev(pdev, cfg, 0)) < 0) 
-                       snd_printk(KERN_ERR PFX "WaveTable the requested resources are invalid, using auto config\n"); 
                err = pnp_activate_dev(pdev); 
                if (err < 0) { 
                        goto __wt_error; 
@@ -339,7 +304,6 @@ __wt_error:
                awe_port[dev] = -1;
        }
 #endif
-       kfree(cfg);
        return 0;
 }
 
index 3682059787abec3639a36574e7c779dccae4dcb5..bed29ca22239edd7f1f47335da7797a100ffec6f 100644 (file)
@@ -23,7 +23,6 @@
  *
  */
 
-#include <sound/driver.h>
 #include <linux/delay.h>
 #include <linux/init.h>
 #include <linux/slab.h>
@@ -118,7 +117,8 @@ static void info_read(struct snd_info_entry *entry, struct snd_info_buffer *buff
 int snd_sb_csp_new(struct snd_sb *chip, int device, struct snd_hwdep ** rhwdep)
 {
        struct snd_sb_csp *p;
-       int version, err;
+       int uninitialized_var(version);
+       int err;
        struct snd_hwdep *hw;
 
        if (rhwdep)
index c06754f7ee5df2d880c7d2dfaacf947eba8014c0..f7e8192270ae1796a302b1893f3b18136a1f8bee 100644 (file)
@@ -33,7 +33,6 @@
  *
  */
 
-#include <sound/driver.h>
 #include <asm/io.h>
 #include <asm/dma.h>
 #include <linux/init.h>
index f933aef7d8a95aacd850e88e97f8e5717b4c201c..336a34277907f0ec74b30816187eb3d14c7909d3 100644 (file)
@@ -19,7 +19,6 @@
  *
  */
 
-#include <sound/driver.h>
 #include <linux/init.h>
 #include <linux/err.h>
 #include <linux/isa.h>
index bee894b3f5c7e245f056769c0a4ea4d1bffaea0a..6304c3a89ba0650b5a644c555f81dfded2cd2620 100644 (file)
@@ -30,7 +30,6 @@
  *   Cleaned up and rewrote lowlevel routines.
  */
 
-#include <sound/driver.h>
 #include <asm/io.h>
 #include <asm/dma.h>
 #include <linux/init.h>
index e56e5633411cd4e728bb8db2b366e3c432ce0396..988a8b73475f26f65e1f7f8bbb344cfff16532ba 100644 (file)
@@ -26,7 +26,6 @@
  *   Added full duplex UART mode for DSP version 2.0 and later.
  */
 
-#include <sound/driver.h>
 #include <asm/io.h>
 #include <linux/time.h>
 #include <sound/core.h>
index 176193c05101007dae7b8c08d086da1632c7168f..d63c1af550de5789949b7df6e11bd78082e71fa1 100644 (file)
@@ -20,7 +20,6 @@
  *
  */
 
-#include <sound/driver.h>
 #include <linux/delay.h>
 #include <linux/init.h>
 #include <linux/interrupt.h>
index 03241cd5aaef253f3953e8ba74cdbdcccabfddde..91d14224f6b332de1119d89bc07bff76c00f37cc 100644 (file)
@@ -19,7 +19,6 @@
  *
  */
 
-#include <sound/driver.h>
 #include <asm/io.h>
 #include <linux/delay.h>
 #include <linux/time.h>
index 94daf839999463e17808768115a180a1dc2f6046..da3d152bcad4d0b5bd7d29b2c1ee1cb5104eaf7a 100644 (file)
@@ -23,7 +23,6 @@
  *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA
  */
 
-#include <sound/driver.h>
 #include <linux/module.h>
 #include <linux/delay.h>
 #include <linux/isa.h>
@@ -390,7 +389,7 @@ static int __devinit sc6000_init_board(char __iomem *vport, int irq, int dma,
 
        err = sc6000_init_mss(vport, config, vmss_port, mss_config);
        if (err < 0) {
-               snd_printk(KERN_ERR "Can not initialize"
+               snd_printk(KERN_ERR "Can not initialize "
                           "Microsoft Sound System mode.\n");
                return -ENODEV;
        }
index 922519def0997f2ff21ee74fc84ecb073012411c..a07274ecb1494b0b6948533edb15faac3e926dea 100644 (file)
@@ -21,7 +21,6 @@
  *
  */
 
-#include <sound/driver.h>
 #include <linux/init.h>
 #include <linux/err.h>
 #include <linux/isa.h>
index 1cb921d6137ebb81b52de249f36bf789d0a08823..06ad7863dff577362c0af9dacafbc5fd6f964f8b 100644 (file)
@@ -21,7 +21,6 @@
  *   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA
  */
 
-#include <sound/driver.h>
 #include <linux/init.h>
 #include <linux/err.h>
 #include <linux/isa.h>
index 83c2fc4cfc64fb1bfc7bdde14e4f3623927ce452..3a6c6fe1ec4d08bed3ff505e84e7a6a5aa8909c3 100644 (file)
@@ -19,7 +19,6 @@
  *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA
  */
 
-#include <sound/driver.h>
 #include <linux/init.h>
 #include <linux/interrupt.h>
 #include <linux/slab.h>
@@ -104,21 +103,15 @@ snd_wavefront_pnp (int dev, snd_wavefront_card_t *acard, struct pnp_card_link *c
                   const struct pnp_card_device_id *id)
 {
        struct pnp_dev *pdev;
-       struct pnp_resource_table *cfg = kmalloc(sizeof(*cfg), GFP_KERNEL);
        int err;
 
-       if (!cfg)
-               return -ENOMEM;
-
        /* Check for each logical device. */
 
        /* CS4232 chip (aka "windows sound system") is logical device 0 */
 
        acard->wss = pnp_request_card_device(card, id->devs[0].id, NULL);
-       if (acard->wss == NULL) {
-               kfree(cfg);
+       if (acard->wss == NULL)
                return -EBUSY;
-       }
 
        /* there is a game port at logical device 1, but we ignore it completely */
 
@@ -133,26 +126,20 @@ snd_wavefront_pnp (int dev, snd_wavefront_card_t *acard, struct pnp_card_link *c
 
        if (use_cs4232_midi[dev]) {
                acard->mpu = pnp_request_card_device(card, id->devs[2].id, NULL);
-               if (acard->mpu == NULL) {
-                       kfree(cfg);
+               if (acard->mpu == NULL)
                        return -EBUSY;
-               }
        }
 
        /* The ICS2115 synth is logical device 4 */
 
        acard->synth = pnp_request_card_device(card, id->devs[3].id, NULL);
-       if (acard->synth == NULL) {
-               kfree(cfg);
+       if (acard->synth == NULL)
                return -EBUSY;
-       }
 
        /* PCM/FM initialization */
 
        pdev = acard->wss;
 
-       pnp_init_resource_table(cfg);
-
        /* An interesting note from the Tropez+ FAQ:
 
           Q. [Ports] Why is the base address of the WSS I/O ports off by 4?
@@ -165,23 +152,9 @@ snd_wavefront_pnp (int dev, snd_wavefront_card_t *acard, struct pnp_card_link *c
 
        */
 
-       if (cs4232_pcm_port[dev] != SNDRV_AUTO_PORT)
-               pnp_resource_change(&cfg->port_resource[0], cs4232_pcm_port[dev], 4);
-       if (fm_port[dev] != SNDRV_AUTO_PORT)
-               pnp_resource_change(&cfg->port_resource[1], fm_port[dev], 4);
-       if (dma1[dev] != SNDRV_AUTO_DMA)
-               pnp_resource_change(&cfg->dma_resource[0], dma1[dev], 1);
-       if (dma2[dev] != SNDRV_AUTO_DMA)
-               pnp_resource_change(&cfg->dma_resource[1], dma2[dev], 1);
-       if (cs4232_pcm_irq[dev] != SNDRV_AUTO_IRQ)
-               pnp_resource_change(&cfg->irq_resource[0], cs4232_pcm_irq[dev], 1);
-
-       if (pnp_manual_config_dev(pdev, cfg, 0) < 0)
-               snd_printk(KERN_ERR "PnP WSS the requested resources are invalid, using auto config\n");
        err = pnp_activate_dev(pdev);
        if (err < 0) {
                snd_printk(KERN_ERR "PnP WSS pnp configure failure\n");
-               kfree(cfg);
                return err;
        }
 
@@ -195,22 +168,9 @@ snd_wavefront_pnp (int dev, snd_wavefront_card_t *acard, struct pnp_card_link *c
 
        pdev = acard->synth;
        
-       pnp_init_resource_table(cfg);
-
-       if (ics2115_port[dev] != SNDRV_AUTO_PORT) {
-               pnp_resource_change(&cfg->port_resource[0], ics2115_port[dev], 16);
-       }
-               
-       if (ics2115_port[dev] != SNDRV_AUTO_IRQ) {
-               pnp_resource_change(&cfg->irq_resource[0], ics2115_irq[dev], 1);
-       }
-
-       if (pnp_manual_config_dev(pdev, cfg, 0) < 0)
-               snd_printk(KERN_ERR "PnP ICS2115 the requested resources are invalid, using auto config\n");
        err = pnp_activate_dev(pdev);
        if (err < 0) {
                snd_printk(KERN_ERR "PnP ICS2115 pnp configure failure\n");
-               kfree(cfg);
                return err;
        }
 
@@ -226,15 +186,6 @@ snd_wavefront_pnp (int dev, snd_wavefront_card_t *acard, struct pnp_card_link *c
 
                pdev = acard->mpu;
 
-               pnp_init_resource_table(cfg);
-
-               if (cs4232_mpu_port[dev] != SNDRV_AUTO_PORT)
-                       pnp_resource_change(&cfg->port_resource[0], cs4232_mpu_port[dev], 2);
-               if (cs4232_mpu_irq[dev] != SNDRV_AUTO_IRQ)
-                       pnp_resource_change(&cfg->port_resource[0], cs4232_mpu_irq[dev], 1);
-
-               if (pnp_manual_config_dev(pdev, cfg, 0) < 0)
-                       snd_printk(KERN_ERR "PnP MPU401 the requested resources are invalid, using auto config\n");
                err = pnp_activate_dev(pdev);
                if (err < 0) {
                        snd_printk(KERN_ERR "PnP MPU401 pnp configure failure\n");
@@ -258,7 +209,6 @@ snd_wavefront_pnp (int dev, snd_wavefront_card_t *acard, struct pnp_card_link *c
                    ics2115_port[dev], 
                    ics2115_irq[dev]);
        
-       kfree(cfg);
        return 0;
 }
 
index fc95a870f69017aa17cf20f175633abb0d1af8d1..2efaa7f205aa617f56c64db3e26f3e279db25246 100644 (file)
@@ -16,7 +16,6 @@
  *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA
  */
 
-#include <sound/driver.h>
 #include <asm/io.h>
 #include <linux/init.h>
 #include <linux/time.h>
index cb3460094324db8f34069fc18485148a54232b39..a33384a55b0f6576d1a71320d8f68b33a1c622c4 100644 (file)
@@ -47,7 +47,6 @@
  *  
  */
 
-#include <sound/driver.h>
 #include <asm/io.h>
 #include <linux/init.h>
 #include <linux/time.h>
index a1ebb7c5c684815f7d17d91a7ba739c6577e1bbc..95eeca163354b5303b89554bdeafb8053bc54fcd 100644 (file)
@@ -20,7 +20,6 @@
  *
  */
 
-#include <sound/driver.h>
 #include <asm/io.h>
 #include <linux/interrupt.h>
 #include <linux/init.h>
index 282b0cdb0589fbc60984d9ac9d7ae44b681a2d61..bdd0857b88710bb4fb2489766bcb4ea3fa26b79e 100644 (file)
@@ -20,7 +20,6 @@
  */
 
 #define SNDRV_MAIN_OBJECT_FILE
-#include <sound/driver.h>
 #include <linux/init.h>
 #include <sound/core.h>
 
index 24460a558bf75eaf98d8e70a76c411fc54c34525..ee0741f9eb5322c26f782a9fe59460db1568b1cf 100644 (file)
@@ -36,7 +36,6 @@
 
 #include <linux/ioport.h>
 #include <linux/interrupt.h>
-#include <sound/driver.h>
 #include <linux/init.h>
 #include <linux/slab.h>
 #include <linux/version.h>
index ff705c63a03a2f0292438a380e11b2f76212de22..99f5483abf2e7efb006a01928edb3b64d0dcf886 100644 (file)
@@ -45,7 +45,6 @@
 #include <linux/spinlock.h>
 #include <linux/dma-mapping.h>
 
-#include <sound/driver.h>
 #include <sound/core.h>
 #include <sound/pcm.h>
 #include <sound/control.h>
index 356bf21a15060219077a255bf4b8b0afd81e29c7..812085d521f12405867dd4c70096b49b8ef79212 100644 (file)
@@ -183,6 +183,30 @@ config SND_CMIPCI
          To compile this driver as a module, choose M here: the module
          will be called snd-cmipci.
 
+config SND_OXYGEN_LIB
+        tristate
+       depends on SND
+       select SND_PCM
+       select SND_MPU401_UART
+
+config SND_OXYGEN
+       tristate "C-Media 8788 (Oxygen)"
+       depends on SND
+       select SND_OXYGEN_LIB
+       help
+         Say Y here to include support for sound cards based on the
+         C-Media CMI8788 (Oxygen HD Audio) chip:
+          * Asound A-8788
+          * AuzenTech X-Meridian
+          * Bgears b-Enspirer
+          * Club3D Theatron DTS
+          * HT-Omega Claro
+          * Razer Barracuda AC-1
+          * Sondigo Inferno
+
+         To compile this driver as a module, choose M here: the module
+         will be called snd-oxygen.
+
 config SND_CS4281
        tristate "Cirrus Logic (Sound Fusion) CS4281"
        depends on SND
@@ -623,6 +647,17 @@ config SND_HDSPM
          To compile this driver as a module, choose M here: the module
          will be called snd-hdspm.
 
+config SND_HIFIER
+       tristate "TempoTec HiFier Fantasia"
+       depends on SND
+       select SND_OXYGEN_LIB
+       help
+         Say Y here to include support for the MediaTek/TempoTec HiFier
+         Fantasia sound card.
+
+         To compile this driver as a module, choose M here: the module
+         will be called snd-hifier.
+
 config SND_ICE1712
        tristate "ICEnsemble ICE1712 (Envy24)"
        depends on SND
@@ -802,6 +837,16 @@ config SND_RME9652
          To compile this driver as a module, choose M here: the module
          will be called snd-rme9652.
 
+config SND_SIS7019
+       tristate "SiS 7019 Audio Accelerator"
+       depends on SND && X86 && !X86_64
+       select SND_AC97_CODEC
+       help
+         Say Y here to include support for the SiS 7019 Audio Accelerator.
+
+         To compile this driver as a module, choose M here: the module
+         will be called snd-sis7019.
+
 config SND_SONICVIBES
        tristate "S3 SonicVibes"
        depends on SND
@@ -850,6 +895,17 @@ config SND_VIA82XX_MODEM
          To compile this driver as a module, choose M here: the module
          will be called snd-via82xx-modem.
 
+config SND_VIRTUOSO
+       tristate "Asus Virtuoso 200 (Xonar)"
+       depends on SND
+       select SND_OXYGEN_LIB
+       help
+         Say Y here to include support for sound cards based on the
+         Asus AV200 chip, i.e., Xonar D2 and Xonar D2X.
+
+         To compile this driver as a module, choose M here: the module
+         will be called snd-virtuoso.
+
 config SND_VX222
        tristate "Digigram VX222"
        depends on SND
index 09ddc82eeca22130b17a4597e583868cad93cbd7..2d42fd28f4e7950305d80d930569dfd759818b56 100644 (file)
@@ -23,6 +23,7 @@ snd-intel8x0m-objs := intel8x0m.o
 snd-maestro3-objs := maestro3.o
 snd-rme32-objs := rme32.o
 snd-rme96-objs := rme96.o
+snd-sis7019-objs := sis7019.o
 snd-sonicvibes-objs := sonicvibes.o
 snd-via82xx-objs := via82xx.o
 snd-via82xx-modem-objs := via82xx_modem.o
@@ -48,6 +49,7 @@ obj-$(CONFIG_SND_INTEL8X0M) += snd-intel8x0m.o
 obj-$(CONFIG_SND_MAESTRO3) += snd-maestro3.o
 obj-$(CONFIG_SND_RME32) += snd-rme32.o
 obj-$(CONFIG_SND_RME96) += snd-rme96.o
+obj-$(CONFIG_SND_SIS7019) += snd-sis7019.o
 obj-$(CONFIG_SND_SONICVIBES) += snd-sonicvibes.o
 obj-$(CONFIG_SND_VIA82XX) += snd-via82xx.o
 obj-$(CONFIG_SND_VIA82XX_MODEM) += snd-via82xx-modem.o
@@ -66,6 +68,7 @@ obj-$(CONFIG_SND) += \
        korg1212/ \
        mixart/ \
        nm256/ \
+       oxygen/ \
        pcxhr/ \
        riptide/ \
        rme9652/ \
index 6a9966df0cc907c8d30268734be09485b429590a..45fd29017ddd59ded7e731ece20c977a4a4bde20 100644 (file)
@@ -22,7 +22,6 @@
  *
  */
 
-#include <sound/driver.h>
 #include <linux/delay.h>
 #include <linux/init.h>
 #include <linux/slab.h>
index 98c8b727b62b8c006f040a711125cac1f2a50ba6..50c637e55ffa63ef544646fae65e62a29b049c7b 100644 (file)
@@ -133,6 +133,14 @@ static int ac97_channel_mode_put(struct snd_kcontrol *kcontrol, struct snd_ctl_e
        struct snd_ac97 *ac97 = snd_kcontrol_chip(kcontrol);
        unsigned char mode = ucontrol->value.enumerated.item[0];
 
+       if (kcontrol->private_value) {
+               if (mode >= 2)
+                       return -EINVAL;
+       } else {
+               if (mode >= 3)
+                       return -EINVAL;
+       }
+
        if (mode != ac97->channel_mode) {
                ac97->channel_mode = mode;
                if (ac97->build_ops->update_jacks)
@@ -2142,8 +2150,7 @@ static int snd_ac97_ad1985_vrefout_put(struct snd_kcontrol *kcontrol,
        struct snd_ac97 *ac97 = snd_kcontrol_chip(kcontrol);
        unsigned short val;
 
-       if (ucontrol->value.enumerated.item[0] > 3
-           || ucontrol->value.enumerated.item[0] < 0)
+       if (ucontrol->value.enumerated.item[0] > 3)
                return -EINVAL;
        val = ctrl2reg[ucontrol->value.enumerated.item[0]]
              << AC97_AD198X_VREF_SHIFT;
index 9cccc27ea1b58b3ec33a72089653acbe9ce00922..47bf8dfe82768fe1c0b9338f7a9f10dd2cc2124e 100644 (file)
@@ -83,8 +83,10 @@ static int snd_ac97_swap_ctl(struct snd_ac97 *ac97, const char *s1,
                             const char *s2, const char *suffix);
 static void snd_ac97_rename_vol_ctl(struct snd_ac97 *ac97, const char *src,
                                    const char *dst);
+#ifdef CONFIG_PM
 static void snd_ac97_restore_status(struct snd_ac97 *ac97);
 static void snd_ac97_restore_iec958(struct snd_ac97 *ac97);
+#endif
 static int snd_ac97_info_enum_double(struct snd_kcontrol *kcontrol,
                                     struct snd_ctl_elem_info *uinfo);
 static int snd_ac97_get_enum_double(struct snd_kcontrol *kcontrol,
index 8cbc03332b013e4c630a1a15c915b4e0dbab4b10..3674f35c4a791eaf04a6d2ae86b79e82978d028b 100644 (file)
@@ -23,7 +23,6 @@
  *
  */
 
-#include <sound/driver.h>
 #include <linux/delay.h>
 #include <linux/init.h>
 #include <linux/slab.h>
index fed4a2c3d8a1689157efc85b7cf12f69558e0b73..060ea59d5f02f02e74c06b28c68a5c37fb0a7446 100644 (file)
@@ -22,7 +22,6 @@
  *
  */
 
-#include <sound/driver.h>
 #include <linux/slab.h>
 #include <linux/mutex.h>
 
index 722de451d15fcedc331e290c5e6eda589fea5e2e..c0c1633999eaf60567d0f41752d3528312561e71 100644 (file)
@@ -19,7 +19,6 @@
  *
  */
 
-#include <sound/driver.h>
 #include <linux/delay.h>
 #include <linux/init.h>
 #include <linux/slab.h>
index 98970d401be9872be2eb61d3b4f1148da97d80e7..a66d5150bb7ac3ddf437588cf99ff726b5adbca2 100644 (file)
@@ -40,7 +40,6 @@
 #include <linux/compiler.h>
 #include <linux/delay.h>
 
-#include <sound/driver.h>
 #include <sound/core.h>
 #include <sound/pcm.h>
 #include <sound/initval.h>
@@ -1055,7 +1054,7 @@ static struct pci_device_id snd_ad1889_ids[] = {
 };
 MODULE_DEVICE_TABLE(pci, snd_ad1889_ids);
 
-static struct pci_driver ad1889_pci = {
+static struct pci_driver ad1889_pci_driver = {
        .name = "AD1889 Audio",
        .id_table = snd_ad1889_ids,
        .probe = snd_ad1889_probe,
@@ -1065,13 +1064,13 @@ static struct pci_driver ad1889_pci = {
 static int __init
 alsa_ad1889_init(void)
 {
-       return pci_register_driver(&ad1889_pci);
+       return pci_register_driver(&ad1889_pci_driver);
 }
 
 static void __exit
 alsa_ad1889_fini(void)
 {
-       pci_unregister_driver(&ad1889_pci);
+       pci_unregister_driver(&ad1889_pci_driver);
 }
 
 module_init(alsa_ad1889_init);
index 4c2bd7adf6748215588add8a700e919b9df90a79..6a905ed9cbd6e6f964db82cf2816888508624b5f 100644 (file)
@@ -25,7 +25,6 @@
  *
  */
 
-#include <sound/driver.h>
 #include <asm/io.h>
 #include <linux/delay.h>
 #include <linux/interrupt.h>
index 48cc39b771d9c12244ae452c10e29b8dccfb2988..0e990a7358211f4e80af8e376f0e76899bb0c326 100644 (file)
@@ -30,7 +30,6 @@
  *  to keep track of what period we are in.
  */
 
-#include <sound/driver.h>
 #include <linux/delay.h>
 #include <linux/init.h>
 #include <linux/moduleparam.h>
index 1190ef366a41930bff260d56b13c70248b8af900..27ce6136ab009e66052026fdc038bd449a138b02 100644 (file)
@@ -63,7 +63,6 @@
  * - power management? (card can do voice wakeup according to datasheet!!)
  */
 
-#include <sound/driver.h>
 #include <asm/io.h>
 #include <linux/init.h>
 #include <linux/pci.h>
index 89184a424140232e91f0c832360593fbda21a5fe..4594186b83ee7bfb4b157b6eff9e2aba79aa0916 100644 (file)
@@ -19,7 +19,6 @@
  *
  */
 
-#include <sound/driver.h>
 #include <asm/io.h>
 #include <linux/delay.h>
 #include <linux/interrupt.h>
@@ -560,7 +559,7 @@ static int snd_atiixp_aclink_down(struct atiixp *chip)
             ATI_REG_ISR_CODEC2_NOT_READY)
 #define CODEC_CHECK_BITS (ALL_CODEC_NOT_READY|ATI_REG_ISR_NEW_FRAME)
 
-static int ac97_probing_bugs(struct pci_dev *pci)
+static int __devinit ac97_probing_bugs(struct pci_dev *pci)
 {
        const struct snd_pci_quirk *q;
 
@@ -574,7 +573,7 @@ static int ac97_probing_bugs(struct pci_dev *pci)
        return -1;
 }
 
-static int snd_atiixp_codec_detect(struct atiixp *chip)
+static int __devinit snd_atiixp_codec_detect(struct atiixp *chip)
 {
        int timeout;
 
index ce752f84457ad6ee9b6756a66c45557cfeb19e38..a67a869180d4f572739be0de81063c45fe31948c 100644 (file)
@@ -19,7 +19,6 @@
  *
  */
 
-#include <sound/driver.h>
 #include <asm/io.h>
 #include <linux/delay.h>
 #include <linux/interrupt.h>
index 5ccf0b1ec670f7ed27baecc1f96ece3a9c11c335..4aad35bba11a7af57679d47da3002223cd0c14ba 100644 (file)
@@ -18,7 +18,6 @@
 #define __SOUND_AU88X0_H
 
 #ifdef __KERNEL__
-#include <sound/driver.h>
 #include <linux/pci.h>
 #include <asm/io.h>
 #include <sound/core.h>
index 4a336eaae9d21eda656c88a08cf12a187697e0d0..333c62de862042f3672b3cf06c87c59c4cd8c707 100644 (file)
@@ -2395,7 +2395,7 @@ static irqreturn_t vortex_interrupt(int irq, void *dev_id)
        if (!(hwread(vortex->mmio, VORTEX_STAT) & 0x1))
                return IRQ_NONE;
 
-       // This is the Interrrupt Enable flag we set before (consistency check).
+       // This is the Interrupt Enable flag we set before (consistency check).
        if (!(hwread(vortex->mmio, VORTEX_CTRL) & CTRL_IRQ_ENABLE))
                return IRQ_NONE;
 
index a07d1deba322702cef7197cefd0e4815b6e596c4..bc212f41a38a611a84ee679502813377208602e1 100644 (file)
@@ -30,7 +30,6 @@
  * driver. (email: mjander@embedded.cl).
  */
 
-#include <sound/driver.h>
 #include <linux/time.h>
 #include <linux/delay.h>
 #include <linux/init.h>
index c96da1dab8639d4a81bde35715d00bd04cd51f54..c92f493d341e266c36b870f116aaf6adb9ef48a7 100644 (file)
@@ -5,7 +5,6 @@
  *
  */
 
-#include <sound/driver.h>
 #include <linux/time.h>
 #include <linux/init.h>
 #include <sound/core.h>
index 8db3d3e6f7bb242a3476089af575cc9b07250a5b..0dc8d259d1ed09a708ad8affe38a29dfead7a27f 100644 (file)
@@ -21,7 +21,6 @@
  *
  */
 
-#include <sound/driver.h>
 #include <linux/time.h>
 #include <linux/init.h>
 #include <sound/core.h>
index 7b5baa1738597b61e5785bcc270bb12b8ddae753..526c6c5ecf7b658f7e1acb9d011ff437f9919c61 100644 (file)
@@ -21,7 +21,6 @@
  * It remains stuck,and DMA transfers do not happen. 
  */
 #include <sound/asoundef.h>
-#include <sound/driver.h>
 #include <linux/time.h>
 #include <sound/core.h>
 #include <sound/pcm.h>
index 36d3666a5b7747b74d993e1fc012d01d1a43a571..4e71a55120a05ff014a7504e1c472114ee963314 100644 (file)
  *    code (but I'm not too optimistic that doing this is possible at all)
  */
 
-#include <sound/driver.h>
 #include <asm/io.h>
 #include <linux/init.h>
 #include <linux/pci.h>
index 2dba752faf4e6281a2a31bddf4a0998197f4d535..c9a2421cf6f0df75078da262250633171beaaac6 100644 (file)
@@ -21,7 +21,6 @@
  *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA
  */
 
-#include <sound/driver.h>
 #include <linux/init.h>
 #include <linux/interrupt.h>
 #include <linux/pci.h>
index 75da1746e758d221f9c80b473ac2bf8913c2983c..74175fc80c7f447dbc0441c325c04abf29350e91 100644 (file)
 #define SPCS_WORD_LENGTH_20A   0x0000000a      /* Word Length 20 bit                           */
 #define SPCS_WORD_LENGTH_20    0x00000009      /* Word Length 20 bit (both 0xa and 0x9 are 20 bit) */
 #define SPCS_WORD_LENGTH_21    0x00000007      /* Word Length 21 bit                           */
-#define SPCS_WORD_LENGTH_21    0x00000007      /* Word Length 21 bit                           */
 #define SPCS_WORD_LENGTH_22    0x00000005      /* Word Length 22 bit                           */
 #define SPCS_WORD_LENGTH_23    0x00000003      /* Word Length 23 bit                           */
 #define SPCS_WORD_LENGTH_24    0x0000000b      /* Word Length 24 bit                           */
index 31d8db9f7a4c023950fb14c6caee50212ede553a..176e0f0e80585739e37789b1e54e47df68a97e42 100644 (file)
  *   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA
  *
  */
-#include <sound/driver.h>
 #include <linux/delay.h>
 #include <linux/init.h>
 #include <linux/interrupt.h>
index 3f9b5c5600369b5700b4b5ceb48e4fcf8b8f88e8..af736869d9b1959e0802c4e105476c898d901526 100644 (file)
@@ -60,7 +60,6 @@
  *   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA
  *
  */
-#include <sound/driver.h>
 #include <linux/delay.h>
 #include <linux/init.h>
 #include <linux/interrupt.h>
index 61f2718ae3598a9c4b109198df404bbb19fabe6f..c62b7d10ec61644c763beb0be0c1a10073aee2b3 100644 (file)
@@ -60,7 +60,6 @@
  *   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA
  *
  */
-#include <sound/driver.h>
 #include <linux/delay.h>
 #include <linux/init.h>
 #include <linux/interrupt.h>
index ad32eff2713f902288181c2a439db9588cc30dbd..893ee4f1ea7773f54e90c1824c75fe08a6bdb13f 100644 (file)
@@ -27,7 +27,6 @@
  */
 
 #include <linux/spinlock.h>
-#include <sound/driver.h>
 #include <sound/core.h>
 #include <sound/rawmidi.h>
 
index 1fa5f004e858fd8d0e0994aa20642e5eb974bede..135f308607535594b95c5afd74964a378ab079db 100644 (file)
@@ -20,7 +20,6 @@
 /* Does not work. Warning may block system in capture mode */
 /* #define USE_VAR48KRATE */
 
-#include <sound/driver.h>
 #include <asm/io.h>
 #include <linux/delay.h>
 #include <linux/interrupt.h>
@@ -150,6 +149,8 @@ MODULE_PARM_DESC(joystick_port, "Joystick port address.");
 #define CM_CH0_SRATE_176K      0x00000200
 #define CM_CH0_SRATE_96K       0x00000200      /* model 055? */
 #define CM_CH0_SRATE_88K       0x00000100
+#define CM_CH0_SRATE_128K      0x00000300
+#define CM_CH0_SRATE_MASK      0x00000300
 
 #define CM_SPDIF_INVERSE2      0x00000080      /* model 055? */
 #define CM_DBLSPDS             0x00000040      /* double SPDIF sample rate 88.2/96 */
@@ -473,6 +474,7 @@ struct cmipci {
        unsigned int can_ac3_sw: 1;
        unsigned int can_ac3_hw: 1;
        unsigned int can_multi_ch: 1;
+       unsigned int can_96k: 1;        /* samplerate above 48k */
        unsigned int do_soft_ac3: 1;
 
        unsigned int spdif_playback_avail: 1;   /* spdif ready? */
@@ -603,8 +605,6 @@ static unsigned int snd_cmipci_rate_freq(unsigned int rate)
 {
        unsigned int i;
 
-       if (rate > 48000)
-               rate /= 2;
        for (i = 0; i < ARRAY_SIZE(rates); i++) {
                if (rates[i] == rate)
                        return i;
@@ -782,7 +782,7 @@ static int set_dac_channels(struct cmipci *cm, struct cmipci_pcm *rec, int chann
 static int snd_cmipci_pcm_prepare(struct cmipci *cm, struct cmipci_pcm *rec,
                                 struct snd_pcm_substream *substream)
 {
-       unsigned int reg, freq, val;
+       unsigned int reg, freq, freq_ext, val;
        unsigned int period_size;
        struct snd_pcm_runtime *runtime = substream->runtime;
 
@@ -830,7 +830,17 @@ static int snd_cmipci_pcm_prepare(struct cmipci *cm, struct cmipci_pcm *rec,
        //snd_printd("cmipci: functrl0 = %08x\n", cm->ctrl);
 
        /* set sample rate */
-       freq = snd_cmipci_rate_freq(runtime->rate);
+       freq = 0;
+       freq_ext = 0;
+       if (runtime->rate > 48000)
+               switch (runtime->rate) {
+               case 88200:  freq_ext = CM_CH0_SRATE_88K; break;
+               case 96000:  freq_ext = CM_CH0_SRATE_96K; break;
+               case 128000: freq_ext = CM_CH0_SRATE_128K; break;
+               default:     snd_BUG(); break;
+               }
+       else
+               freq = snd_cmipci_rate_freq(runtime->rate);
        val = snd_cmipci_read(cm, CM_REG_FUNCTRL1);
        if (rec->ch) {
                val &= ~CM_DSFC_MASK;
@@ -851,19 +861,20 @@ static int snd_cmipci_pcm_prepare(struct cmipci *cm, struct cmipci_pcm *rec,
                val &= ~CM_CH0FMT_MASK;
                val |= rec->fmt << CM_CH0FMT_SHIFT;
        }
-       if (cm->chip_version == 68) {
-               if (runtime->rate == 88200)
-                       val |= CM_CH0_SRATE_88K << (rec->ch * 2);
-               else
-                       val &= ~(CM_CH0_SRATE_88K << (rec->ch * 2));
-               if (runtime->rate == 96000)
-                       val |= CM_CH0_SRATE_96K << (rec->ch * 2);
-               else
-                       val &= ~(CM_CH0_SRATE_96K << (rec->ch * 2));
+       if (cm->can_96k) {
+               val &= ~(CM_CH0_SRATE_MASK << (rec->ch * 2));
+               val |= freq_ext << (rec->ch * 2);
        }
        snd_cmipci_write(cm, CM_REG_CHFORMAT, val);
        //snd_printd("cmipci: chformat = %08x\n", val);
 
+       if (!rec->is_dac && cm->chip_version) {
+               if (runtime->rate > 44100)
+                       snd_cmipci_set_bit(cm, CM_REG_EXT_MISC, CM_ADC48K44K);
+               else
+                       snd_cmipci_clear_bit(cm, CM_REG_EXT_MISC, CM_ADC48K44K);
+       }
+
        rec->running = 0;
        spin_unlock_irq(&cm->reg_lock);
 
@@ -1280,7 +1291,7 @@ static int snd_cmipci_playback_prepare(struct snd_pcm_substream *substream)
        int rate = substream->runtime->rate;
        int err, do_spdif, do_ac3 = 0;
 
-       do_spdif = (rate >= 44100 &&
+       do_spdif = (rate >= 44100 && rate <= 96000 &&
                    substream->runtime->format == SNDRV_PCM_FORMAT_S16_LE &&
                    substream->runtime->channels == 2);
        if (do_spdif && cm->can_ac3_hw) 
@@ -1336,10 +1347,8 @@ static void snd_cmipci_silence_hack(struct cmipci *cm, struct cmipci_pcm *rec)
                val = snd_cmipci_read(cm, CM_REG_CHFORMAT);
                val &= ~(CM_CH0FMT_MASK << (rec->ch * 2));
                val |= (3 << CM_CH0FMT_SHIFT) << (rec->ch * 2);
-               if (cm->chip_version == 68) {
-                       val &= ~(CM_CH0_SRATE_88K << (rec->ch * 2));
-                       val &= ~(CM_CH0_SRATE_96K << (rec->ch * 2));
-               }
+               if (cm->can_96k)
+                       val &= ~(CM_CH0_SRATE_MASK << (rec->ch * 2));
                snd_cmipci_write(cm, CM_REG_CHFORMAT, val);
        
                /* start stream (we don't need interrupts) */
@@ -1391,6 +1400,17 @@ static int snd_cmipci_capture_spdif_prepare(struct snd_pcm_substream *substream)
 
        spin_lock_irq(&cm->reg_lock);
        snd_cmipci_set_bit(cm, CM_REG_FUNCTRL1, CM_CAPTURE_SPDF);
+       if (cm->can_96k) {
+               if (substream->runtime->rate > 48000)
+                       snd_cmipci_set_bit(cm, CM_REG_CHFORMAT, CM_DBLSPDS);
+               else
+                       snd_cmipci_clear_bit(cm, CM_REG_CHFORMAT, CM_DBLSPDS);
+       }
+       if (snd_pcm_format_width(substream->runtime->format) > 16)
+               snd_cmipci_set_bit(cm, CM_REG_MISC_CTRL, CM_SPD32SEL);
+       else
+               snd_cmipci_clear_bit(cm, CM_REG_MISC_CTRL, CM_SPD32SEL);
+
        spin_unlock_irq(&cm->reg_lock);
 
        return snd_cmipci_pcm_prepare(cm, &cm->channel[CM_CH_CAPT], substream);
@@ -1402,6 +1422,7 @@ static int snd_cmipci_capture_spdif_hw_free(struct snd_pcm_substream *subs)
 
        spin_lock_irq(&cm->reg_lock);
        snd_cmipci_clear_bit(cm, CM_REG_FUNCTRL1, CM_CAPTURE_SPDF);
+       snd_cmipci_clear_bit(cm, CM_REG_MISC_CTRL, CM_SPD32SEL);
        spin_unlock_irq(&cm->reg_lock);
 
        return snd_cmipci_hw_free(subs);
@@ -1553,7 +1574,8 @@ static struct snd_pcm_hardware snd_cmipci_capture_spdif =
        .info =                 (SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_INTERLEAVED |
                                 SNDRV_PCM_INFO_BLOCK_TRANSFER | SNDRV_PCM_INFO_PAUSE |
                                 SNDRV_PCM_INFO_RESUME | SNDRV_PCM_INFO_MMAP_VALID),
-       .formats =              SNDRV_PCM_FMTBIT_S16_LE,
+       .formats =              SNDRV_PCM_FMTBIT_S16_LE |
+                               SNDRV_PCM_FMTBIT_IEC958_SUBFRAME_LE,
        .rates =                SNDRV_PCM_RATE_44100 | SNDRV_PCM_RATE_48000,
        .rate_min =             44100,
        .rate_max =             48000,
@@ -1567,6 +1589,14 @@ static struct snd_pcm_hardware snd_cmipci_capture_spdif =
        .fifo_size =            0,
 };
 
+static unsigned int rate_constraints[] = { 5512, 8000, 11025, 16000, 22050,
+                       32000, 44100, 48000, 88200, 96000, 128000 };
+static struct snd_pcm_hw_constraint_list hw_constraints_rates = {
+               .count = ARRAY_SIZE(rate_constraints),
+               .list = rate_constraints,
+               .mask = 0,
+};
+
 /*
  * check device open/close
  */
@@ -1636,6 +1666,13 @@ static int snd_cmipci_playback_open(struct snd_pcm_substream *substream)
                runtime->hw.rates |= SNDRV_PCM_RATE_88200 |
                                     SNDRV_PCM_RATE_96000;
                runtime->hw.rate_max = 96000;
+       } else if (cm->chip_version == 55) {
+               err = snd_pcm_hw_constraint_list(runtime, 0,
+                       SNDRV_PCM_HW_PARAM_RATE, &hw_constraints_rates);
+               if (err < 0)
+                       return err;
+               runtime->hw.rates |= SNDRV_PCM_RATE_KNOT;
+               runtime->hw.rate_max = 128000;
        }
        snd_pcm_hw_constraint_minmax(runtime, SNDRV_PCM_HW_PARAM_BUFFER_SIZE, 0, 0x10000);
        cm->dig_pcm_status = cm->dig_status;
@@ -1654,6 +1691,13 @@ static int snd_cmipci_capture_open(struct snd_pcm_substream *substream)
        if (cm->chip_version == 68) {   // 8768 only supports 44k/48k recording
                runtime->hw.rate_min = 41000;
                runtime->hw.rates = SNDRV_PCM_RATE_44100 | SNDRV_PCM_RATE_48000;
+       } else if (cm->chip_version == 55) {
+               err = snd_pcm_hw_constraint_list(runtime, 0,
+                       SNDRV_PCM_HW_PARAM_RATE, &hw_constraints_rates);
+               if (err < 0)
+                       return err;
+               runtime->hw.rates |= SNDRV_PCM_RATE_KNOT;
+               runtime->hw.rate_max = 128000;
        }
        snd_pcm_hw_constraint_minmax(runtime, SNDRV_PCM_HW_PARAM_BUFFER_SIZE, 0, 0x10000);
        return 0;
@@ -1685,6 +1729,13 @@ static int snd_cmipci_playback2_open(struct snd_pcm_substream *substream)
                runtime->hw.rates |= SNDRV_PCM_RATE_88200 |
                                     SNDRV_PCM_RATE_96000;
                runtime->hw.rate_max = 96000;
+       } else if (cm->chip_version == 55) {
+               err = snd_pcm_hw_constraint_list(runtime, 0,
+                       SNDRV_PCM_HW_PARAM_RATE, &hw_constraints_rates);
+               if (err < 0)
+                       return err;
+               runtime->hw.rates |= SNDRV_PCM_RATE_KNOT;
+               runtime->hw.rate_max = 128000;
        }
        snd_pcm_hw_constraint_minmax(runtime, SNDRV_PCM_HW_PARAM_BUFFER_SIZE, 0, 0x10000);
        return 0;
@@ -1704,7 +1755,7 @@ static int snd_cmipci_playback_spdif_open(struct snd_pcm_substream *substream)
                        runtime->hw.formats |= SNDRV_PCM_FMTBIT_S32_LE;
                        snd_pcm_hw_constraint_msbits(runtime, 0, 32, 24);
                }
-               if (cm->chip_version == 68) {
+               if (cm->can_96k) {
                        runtime->hw.rates |= SNDRV_PCM_RATE_88200 |
                                             SNDRV_PCM_RATE_96000;
                        runtime->hw.rate_max = 96000;
@@ -1726,6 +1777,11 @@ static int snd_cmipci_capture_spdif_open(struct snd_pcm_substream *substream)
        if ((err = open_device_check(cm, CM_OPEN_SPDIF_CAPTURE, substream)) < 0) /* use channel B */
                return err;
        runtime->hw = snd_cmipci_capture_spdif;
+       if (cm->can_96k && !(cm->chip_version == 68)) {
+               runtime->hw.rates |= SNDRV_PCM_RATE_88200 |
+                                    SNDRV_PCM_RATE_96000;
+               runtime->hw.rate_max = 96000;
+       }
        snd_pcm_hw_constraint_minmax(runtime, SNDRV_PCM_HW_PARAM_BUFFER_SIZE, 0, 0x40000);
        return 0;
 }
@@ -2594,10 +2650,8 @@ static struct snd_kcontrol_new snd_cmipci_extra_mixer_switches[] __devinitdata =
 };
 
 /* card control switches */
-static struct snd_kcontrol_new snd_cmipci_control_switches[] __devinitdata = {
-       // DEFINE_CARD_SWITCH("Joystick", joystick), /* now module option */
-       DEFINE_CARD_SWITCH("Modem", modem),
-};
+static struct snd_kcontrol_new snd_cmipci_modem_switch __devinitdata =
+DEFINE_CARD_SWITCH("Modem", modem);
 
 
 static int __devinit snd_cmipci_mixer_new(struct cmipci *cm, int pcm_spdif_device)
@@ -2678,9 +2732,13 @@ static int __devinit snd_cmipci_mixer_new(struct cmipci *cm, int pcm_spdif_devic
        }
 
        /* card switches */
-       sw = snd_cmipci_control_switches;
-       for (idx = 0; idx < ARRAY_SIZE(snd_cmipci_control_switches); idx++, sw++) {
-               err = snd_ctl_add(cm->card, snd_ctl_new1(sw, cm));
+       /*
+        * newer chips don't have the register bits to force modem link
+        * detection; the bit that was FLINKON now mutes CH1
+        */
+       if (cm->chip_version < 39) {
+               err = snd_ctl_add(cm->card,
+                                 snd_ctl_new1(&snd_cmipci_modem_switch, cm));
                if (err < 0)
                        return err;
        }
@@ -2785,9 +2843,11 @@ static void __devinit query_chip(struct cmipci *cm)
                } else if (detect & CM_CHIP_8768) {
                        cm->chip_version = 68;
                        cm->max_channels = 8;
+                       cm->can_96k = 1;
                } else {
                        cm->chip_version = 55;
                        cm->max_channels = 6;
+                       cm->can_96k = 1;
                }
                cm->can_ac3_hw = 1;
                cm->can_multi_ch = 1;
index 9a55f4a9739b671b59950ac383e15e57dd0916ca..7556fd90d0eb5c67973ddfa3fba26c597a766844 100644 (file)
@@ -19,7 +19,6 @@
  *
  */
 
-#include <sound/driver.h>
 #include <asm/io.h>
 #include <linux/delay.h>
 #include <linux/interrupt.h>
index 2699cb6c2cd69df0340b52c288a9315d0965e883..e876b3263e462a9060b071e7974f1b0dcb90c583 100644 (file)
@@ -25,7 +25,6 @@
     reloading the module may solve this.
 */
 
-#include <sound/driver.h>
 #include <linux/pci.h>
 #include <linux/time.h>
 #include <linux/init.h>
index 2c7bfc9fef619646ae0a85ee3d1dc8321a2dd792..87ddffcd9d89d7a6dafee80f9a0e03e6a1983f9c 100644 (file)
@@ -8,7 +8,7 @@
  *    - Sometimes the SPDIF input DSP tasks get's unsynchronized
  *      and the SPDIF get somewhat "distorcionated", or/and left right channel
  *      are swapped. To get around this problem when it happens, mute and unmute 
- *      the SPDIF input mixer controll.
+ *      the SPDIF input mixer control.
  *    - On the Hercules Game Theater XP the amplifier are sometimes turned
  *      off on inadecuate moments which causes distorcions on sound.
  *
@@ -45,7 +45,6 @@
  *
  */
 
-#include <sound/driver.h>
 #include <linux/delay.h>
 #include <linux/pci.h>
 #include <linux/pm.h>
@@ -2084,71 +2083,6 @@ static int snd_cs46xx_spdif_stream_put(struct snd_kcontrol *kcontrol,
 #endif /* CONFIG_SND_CS46XX_NEW_DSP */
 
 
-#ifdef CONFIG_SND_CS46XX_DEBUG_GPIO
-static int snd_cs46xx_egpio_select_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 = 8;
-       return 0;
-}
-
-static int snd_cs46xx_egpio_select_get(struct snd_kcontrol *kcontrol, 
-                                       struct snd_ctl_elem_value *ucontrol)
-{
-       struct snd_cs46xx *chip = snd_kcontrol_chip(kcontrol);
-       ucontrol->value.integer.value[0] = chip->current_gpio;
-
-       return 0;
-}
-
-static int snd_cs46xx_egpio_select_put(struct snd_kcontrol *kcontrol, 
-                                       struct snd_ctl_elem_value *ucontrol)
-{
-       struct snd_cs46xx *chip = snd_kcontrol_chip(kcontrol);
-       int change = (chip->current_gpio != ucontrol->value.integer.value[0]);
-       chip->current_gpio = ucontrol->value.integer.value[0];
-
-       return change;
-}
-
-
-static int snd_cs46xx_egpio_get(struct snd_kcontrol *kcontrol, 
-                                       struct snd_ctl_elem_value *ucontrol)
-{
-       struct snd_cs46xx *chip = snd_kcontrol_chip(kcontrol);
-       int reg = kcontrol->private_value;
-
-       snd_printdd ("put: reg = %04x, gpio %02x\n",reg,chip->current_gpio);
-       ucontrol->value.integer.value[0] = 
-               (snd_cs46xx_peekBA0(chip, reg) & (1 << chip->current_gpio)) ? 1 : 0;
-  
-       return 0;
-}
-
-static int snd_cs46xx_egpio_put(struct snd_kcontrol *kcontrol, 
-                                       struct snd_ctl_elem_value *ucontrol)
-{
-       struct snd_cs46xx *chip = snd_kcontrol_chip(kcontrol);
-       int reg = kcontrol->private_value;
-       int val = snd_cs46xx_peekBA0(chip, reg);
-       int oldval = val;
-       snd_printdd ("put: reg = %04x, gpio %02x\n",reg,chip->current_gpio);
-
-       if (ucontrol->value.integer.value[0])
-               val |= (1 << chip->current_gpio);
-       else
-               val &= ~(1 << chip->current_gpio);
-
-       snd_cs46xx_pokeBA0(chip, reg,val);
-       snd_printdd ("put: val %08x oldval %08x\n",val,oldval);
-
-       return (oldval != val);
-}
-#endif /* CONFIG_SND_CS46XX_DEBUG_GPIO */
-
 static struct snd_kcontrol_new snd_cs46xx_controls[] __devinitdata = {
 {
        .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
@@ -2240,40 +2174,6 @@ static struct snd_kcontrol_new snd_cs46xx_controls[] __devinitdata = {
        .put =   snd_cs46xx_spdif_stream_put
 },
 
-#endif
-#ifdef CONFIG_SND_CS46XX_DEBUG_GPIO
-{
-       .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
-       .name = "EGPIO select",
-       .info = snd_cs46xx_egpio_select_info,
-       .get = snd_cs46xx_egpio_select_get,
-       .put = snd_cs46xx_egpio_select_put,
-       .private_value = 0,
-},
-{
-       .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
-       .name = "EGPIO Input/Output",
-       .info = snd_mixer_boolean_info,
-       .get = snd_cs46xx_egpio_get,
-       .put = snd_cs46xx_egpio_put,
-       .private_value = BA0_EGPIODR,
-},
-{
-       .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
-       .name = "EGPIO CMOS/Open drain",
-       .info = snd_mixer_boolean_info,
-       .get = snd_cs46xx_egpio_get,
-       .put = snd_cs46xx_egpio_put,
-       .private_value = BA0_EGPIOPTR,
-},
-{
-       .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
-       .name = "EGPIO On/Off",
-       .info = snd_mixer_boolean_info,
-       .get = snd_cs46xx_egpio_get,
-       .put = snd_cs46xx_egpio_put,
-       .private_value = BA0_EGPIOSR,
-},
 #endif
 };
 
index 590b35d91df24e3f4e2d0c445fcf01d17c7a4f03..ccc8bedb5b1a3e6136c913806cf347d60f7f8053 100644 (file)
@@ -20,7 +20,6 @@
  */
 
 
-#include <sound/driver.h>
 #include <asm/io.h>
 #include <linux/delay.h>
 #include <linux/pm.h>
index eded4dfeba1210fe307d7f526e39d3c1f0856ed1..2873cfe48c331605f50d23f84350ba4865ce9750 100644 (file)
@@ -21,7 +21,6 @@
  */
 
 
-#include <sound/driver.h>
 #include <asm/io.h>
 #include <linux/delay.h>
 #include <linux/pm.h>
index 240a0a4622090f4867200f059e3f87068f373c4d..7ff8b68e997e89cbb83bda6ced49f49cf44ceea9 100644 (file)
@@ -36,7 +36,6 @@
  *     same manner.
  */
 
-#include <sound/driver.h>
 #include <linux/delay.h>
 #include <linux/moduleparam.h>
 #include <linux/pci.h>
index 2b35889787be6533f88cf484820c481c747d70b0..1d8b160525355e21eb7ee5c5dcd3a18d5877b313 100644 (file)
@@ -21,7 +21,6 @@
  *
  */
 
-#include <sound/driver.h>
 #include <linux/delay.h>
 #include <linux/interrupt.h>
 #include <linux/init.h>
@@ -145,7 +144,7 @@ static unsigned short snd_cs5535audio_ac97_codec_read(struct snd_ac97 *ac97,
        return snd_cs5535audio_codec_read(cs5535au, reg);
 }
 
-static int snd_cs5535audio_mixer(struct cs5535audio *cs5535au)
+static int __devinit snd_cs5535audio_mixer(struct cs5535audio *cs5535au)
 {
        struct snd_card *card = cs5535au->card;
        struct snd_ac97_bus *pbus;
index 21df0634af32a2be83133a4375e0810b6d801455..cdcda87116c34bc895b49e8def2ffa28a4c22491 100644 (file)
@@ -25,7 +25,6 @@
 #include <linux/init.h>
 #include <linux/slab.h>
 #include <linux/pci.h>
-#include <sound/driver.h>
 #include <sound/core.h>
 #include <sound/control.h>
 #include <sound/initval.h>
@@ -98,6 +97,8 @@ static int snd_cs5535audio_playback_open(struct snd_pcm_substream *substream)
        struct snd_pcm_runtime *runtime = substream->runtime;
 
        runtime->hw = snd_cs5535audio_playback;
+       runtime->hw.rates = cs5535au->ac97->rates[AC97_RATES_FRONT_DAC];
+       snd_pcm_limit_hw_rates(runtime);
        cs5535au->playback_substream = substream;
        runtime->private_data = &(cs5535au->dmas[CS5535AUDIO_DMA_PLAYBACK]);
        if ((err = snd_pcm_hw_constraint_integer(runtime,
@@ -343,6 +344,8 @@ static int snd_cs5535audio_capture_open(struct snd_pcm_substream *substream)
        struct snd_pcm_runtime *runtime = substream->runtime;
 
        runtime->hw = snd_cs5535audio_capture;
+       runtime->hw.rates = cs5535au->ac97->rates[AC97_RATES_ADC];
+       snd_pcm_limit_hw_rates(runtime);
        cs5535au->capture_substream = substream;
        runtime->private_data = &(cs5535au->dmas[CS5535AUDIO_DMA_CAPTURE]);
        if ((err = snd_pcm_hw_constraint_integer(runtime,
index 838708f6d45e45db3416ce6cf17429d50f1181c0..564c33b60953964129619309e4a39090ca47fb97 100644 (file)
@@ -22,7 +22,6 @@
 #include <linux/slab.h>
 #include <linux/pci.h>
 #include <linux/delay.h>
-#include <sound/driver.h>
 #include <sound/core.h>
 #include <sound/control.h>
 #include <sound/initval.h>
index 87078d3a68549b931afed8a7cb09a259ebd883a1..8c6db3aa3c1ad59d55ae51d67fbb5b112140c0f2 100644 (file)
@@ -36,7 +36,6 @@
 #define BX_NUM         10
 
 
-#include <sound/driver.h>
 #include <linux/delay.h>
 #include <linux/init.h>
 #include <linux/interrupt.h>
index 42b48f9d21286113915fd5439d83a4e1614b6d8e..04cbf3eaf05a1cd8ae683d1ca2d39be50b0f5156 100644 (file)
@@ -40,7 +40,6 @@
 #define BX_NUM         10
 
 
-#include <sound/driver.h>
 #include <linux/delay.h>
 #include <linux/init.h>
 #include <linux/interrupt.h>
index 8dbb7ac865c1952e680ad0e0c092c46a72db6faa..4022e43a0053dc36fa04ed958d74f7c86f790e98 100644 (file)
@@ -47,7 +47,6 @@
 #define BX_NUM         chip->bx_num
 
 
-#include <sound/driver.h>
 #include <linux/delay.h>
 #include <linux/init.h>
 #include <linux/interrupt.h>
index 499ee1a5319d3ffa40927f15c0b932cb8826eb0d..90ec090792ba12c4c22eecb4378744dd14549868 100644 (file)
@@ -378,7 +378,7 @@ static int pcm_digital_in_open(struct snd_pcm_substream *substream)
 
        DE_ACT(("pcm_digital_in_open\n"));
        max_channels = num_digital_busses_in(chip) - substream->number;
-       down(&chip->mode_mutex);
+       mutex_lock(&chip->mode_mutex);
        if (chip->digital_mode == DIGITAL_MODE_ADAT)
                err = pcm_open(substream, max_channels);
        else    /* If the card has ADAT, subtract the 6 channels
@@ -405,7 +405,7 @@ static int pcm_digital_in_open(struct snd_pcm_substream *substream)
                chip->can_set_rate=0;
 
 din_exit:
-       up(&chip->mode_mutex);
+       mutex_unlock(&chip->mode_mutex);
        return err;
 }
 
@@ -420,7 +420,7 @@ static int pcm_digital_out_open(struct snd_pcm_substream *substream)
 
        DE_ACT(("pcm_digital_out_open\n"));
        max_channels = num_digital_busses_out(chip) - substream->number;
-       down(&chip->mode_mutex);
+       mutex_lock(&chip->mode_mutex);
        if (chip->digital_mode == DIGITAL_MODE_ADAT)
                err = pcm_open(substream, max_channels);
        else    /* If the card has ADAT, subtract the 6 channels
@@ -447,7 +447,7 @@ static int pcm_digital_out_open(struct snd_pcm_substream *substream)
        if (atomic_read(&chip->opencount) > 1 && chip->rate_set)
                chip->can_set_rate=0;
 dout_exit:
-       up(&chip->mode_mutex);
+       mutex_unlock(&chip->mode_mutex);
        return err;
 }
 
@@ -1420,7 +1420,7 @@ static int snd_echo_digital_mode_put(struct snd_kcontrol *kcontrol,
        if (dmode != chip->digital_mode) {
                /* mode_mutex is required to make this operation atomic wrt
                pcm_digital_*_open() and set_input_clock() functions. */
-               down(&chip->mode_mutex);
+               mutex_lock(&chip->mode_mutex);
 
                /* Do not allow the user to change the digital mode when a pcm
                device is open because it also changes the number of channels
@@ -1439,7 +1439,7 @@ static int snd_echo_digital_mode_put(struct snd_kcontrol *kcontrol,
                        if (changed >= 0)
                                changed = 1;    /* No errors */
                }
-               up(&chip->mode_mutex);
+               mutex_unlock(&chip->mode_mutex);
        }
        return changed;
 }
@@ -1566,12 +1566,12 @@ static int snd_echo_clock_source_put(struct snd_kcontrol *kcontrol,
                return -EINVAL;
        dclock = chip->clock_source_list[eclock];
        if (chip->input_clock != dclock) {
-               down(&chip->mode_mutex);
+               mutex_lock(&chip->mode_mutex);
                spin_lock_irq(&chip->lock);
                if ((changed = set_input_clock(chip, dclock)) == 0)
                        changed = 1;    /* no errors */
                spin_unlock_irq(&chip->lock);
-               up(&chip->mode_mutex);
+               mutex_unlock(&chip->mode_mutex);
        }
 
        if (changed < 0)
@@ -1972,7 +1972,7 @@ static __devinit int snd_echo_create(struct snd_card *card,
                return err;
        }
        atomic_set(&chip->opencount, 0);
-       init_MUTEX(&chip->mode_mutex);
+       mutex_init(&chip->mode_mutex);
        chip->can_set_rate = 1;
        *rchip = chip;
        /* Init done ! */
index 7e88c968e22ff20cabc9455a2f71fb68c4a859aa..1c88e051abf2a5c33e809e54c47a3479d114cbb0 100644 (file)
@@ -361,7 +361,7 @@ struct echoaudio {
        spinlock_t lock;
        struct snd_pcm_substream *substream[DSP_MAXPIPES];
        int last_period[DSP_MAXPIPES];
-       struct semaphore mode_mutex;
+       struct mutex mode_mutex;
        u16 num_digital_modes, digital_mode_list[6];
        u16 num_clock_sources, clock_source_list[10];
        atomic_t opencount;
index fee2d483173234cd8f8b988e626d2cc71af98b8b..c0e64b8f52a40577834d29da28f42443667a59ff 100644 (file)
@@ -40,7 +40,6 @@
 #define BX_NUM         14
 
 
-#include <sound/driver.h>
 #include <linux/delay.h>
 #include <linux/init.h>
 #include <linux/interrupt.h>
index d5eae470fe9a3eff790af608cfd28665e28af418..c36a78dd0b5e373d6202c97cc3dd54833e122583 100644 (file)
@@ -46,7 +46,6 @@
 #define BX_NUM         26
 
 
-#include <sound/driver.h>
 #include <linux/delay.h>
 #include <linux/init.h>
 #include <linux/interrupt.h>
index 40f601cd016f32fca593f0b1fe4178052166a690..0a58a7c1fd7cc69fc02aa042a356b4ad47b00ae4 100644 (file)
@@ -38,7 +38,6 @@
 #define BX_NUM         2
 
 
-#include <sound/driver.h>
 #include <linux/delay.h>
 #include <linux/init.h>
 #include <linux/interrupt.h>
index 771c5383210d23597e9536b55d9a11d22b4af48f..2db24d29332bea8eed11bc0e0311df1756666e11 100644 (file)
@@ -38,7 +38,6 @@
 #define BX_NUM         4
 
 
-#include <sound/driver.h>
 #include <linux/delay.h>
 #include <linux/init.h>
 #include <linux/interrupt.h>
index 49c550defcf9230db0ee2fdc6903f21e468d9bb1..a60c0a0a89b737470da2b6d650d7c2cbb352fc7e 100644 (file)
@@ -39,7 +39,6 @@
 #define BX_NUM         4
 
 
-#include <sound/driver.h>
 #include <linux/delay.h>
 #include <linux/init.h>
 #include <linux/interrupt.h>
index 8f5483a405ae6922f31671ce43797816ff8b5694..506194688995ddab5499c556f5d425c8b908511a 100644 (file)
@@ -45,7 +45,6 @@
 #define BX_NUM         22
 
 
-#include <sound/driver.h>
 #include <linux/delay.h>
 #include <linux/init.h>
 #include <linux/interrupt.h>
index 0524667c02f700ca7ffdc4fa75ac5a02bddaff4d..e09e3ea7781e4c6a87247b1ae8adb2f176db49dc 100644 (file)
@@ -47,7 +47,6 @@
 #define BX_NUM         32
 
 
-#include <sound/driver.h>
 #include <linux/delay.h>
 #include <linux/init.h>
 #include <linux/interrupt.h>
index 893c7c20dd70b98eca133c110b7356f705a3e52b..f3b9b45c9c1be7ad82b3c9249e66dfe84ef15833 100644 (file)
@@ -45,7 +45,6 @@
 #define BX_NUM         8
 
 
-#include <sound/driver.h>
 #include <linux/delay.h>
 #include <linux/init.h>
 #include <linux/interrupt.h>
index 3a5d5b0020df30328888a58c3d56d36c015b3c01..b05bad944901b6662dbb19bc231260087e1c08b3 100644 (file)
@@ -44,7 +44,6 @@
 #define BX_NUM         26
 
 
-#include <sound/driver.h>
 #include <linux/delay.h>
 #include <linux/init.h>
 #include <linux/interrupt.h>
index 9680caff90c8556bd97f174e3ddf5df454a61aed..8354c1a833129c7300b765ef918462a3852537f6 100644 (file)
@@ -23,7 +23,6 @@
  * 
  */
 
-#include <sound/driver.h>
 #include <linux/init.h>
 #include <linux/pci.h>
 #include <linux/time.h>
index 01965bd99966e0e76e13cbc0d90683b86bb4492a..45088ebcce5041a9b25e147d41be6add78351f05 100644 (file)
@@ -35,9 +35,9 @@ struct best_voice {
 /*
  * prototypes
  */
-static void lookup_voices(struct snd_emux *emu, struct snd_emu10k1 *hw,
+static void lookup_voices(struct snd_emux *emux, struct snd_emu10k1 *hw,
                          struct best_voice *best, int active_only);
-static struct snd_emux_voice *get_voice(struct snd_emux *emu,
+static struct snd_emux_voice *get_voice(struct snd_emux *emux,
                                        struct snd_emux_port *port);
 static int start_voice(struct snd_emux_voice *vp);
 static void trigger_voice(struct snd_emux_voice *vp);
@@ -45,7 +45,6 @@ static void release_voice(struct snd_emux_voice *vp);
 static void update_voice(struct snd_emux_voice *vp, int update);
 static void terminate_voice(struct snd_emux_voice *vp);
 static void free_voice(struct snd_emux_voice *vp);
-
 static void set_fmmod(struct snd_emu10k1 *hw, struct snd_emux_voice *vp);
 static void set_fm2frq2(struct snd_emu10k1 *hw, struct snd_emux_voice *vp);
 static void set_filterQ(struct snd_emu10k1 *hw, struct snd_emux_voice *vp);
@@ -75,9 +74,9 @@ static struct snd_emux_operators emu10k1_ops = {
 };
 
 void
-snd_emu10k1_ops_setup(struct snd_emux *emu)
+snd_emu10k1_ops_setup(struct snd_emux *emux)
 {
-       emu->ops = emu10k1_ops;
+       emux->ops = emu10k1_ops;
 }
 
 
@@ -166,7 +165,11 @@ free_voice(struct snd_emux_voice *vp)
        struct snd_emu10k1 *hw;
        
        hw = vp->hw;
-       if (vp->ch >= 0) {
+       /* FIXME: emu10k1_synth is broken. */
+       /* This can get called with hw == 0 */
+       /* Problem apparent on plug, unplug then plug */
+       /* on the Audigy 2 ZS Notebook. */
+       if (hw && (vp->ch >= 0)) {
                snd_emu10k1_ptr_write(hw, IFATN, vp->ch, 0xff00);
                snd_emu10k1_ptr_write(hw, DCYSUSV, vp->ch, 0x807f | DCYSUSV_CHANNELENABLE_MASK);
                // snd_emu10k1_ptr_write(hw, DCYSUSV, vp->ch, 0);
index 97c41d72a255f7e165199e64af004af7417d9bc6..9a9b977d3cf1c00173a76128a29be00dc51e683a 100644 (file)
@@ -33,7 +33,6 @@
 
 #include <linux/sched.h>
 #include <linux/kthread.h>
-#include <sound/driver.h>
 #include <linux/delay.h>
 #include <linux/init.h>
 #include <linux/interrupt.h>
 #define DOCK_FILENAME "emu/audio_dock.fw"
 #define EMU1010B_FILENAME "emu/emu1010b.fw"
 #define MICRO_DOCK_FILENAME "emu/micro_dock.fw"
+#define EMU0404_FILENAME "emu/emu0404.fw"
 #define EMU1010_NOTEBOOK_FILENAME "emu/emu1010_notebook.fw"
 
 MODULE_FIRMWARE(HANA_FILENAME);
 MODULE_FIRMWARE(DOCK_FILENAME);
 MODULE_FIRMWARE(EMU1010B_FILENAME);
 MODULE_FIRMWARE(MICRO_DOCK_FILENAME);
+MODULE_FIRMWARE(EMU0404_FILENAME);
 MODULE_FIRMWARE(EMU1010_NOTEBOOK_FILENAME);
 
 
@@ -258,7 +259,6 @@ static int snd_emu10k1_init(struct snd_emu10k1 *emu, int enable_ir, int resume)
                 * GPIO7: Unknown
                 */
                outl(0x76, emu->port + A_IOCFG); /* Windows uses 0x3f76 */
-
        }
        if (emu->card_capabilities->i2c_adc) { /* Audigy 2 ZS Notebook with ADC Wolfson WM8775 */
                int size, n;
@@ -274,7 +274,6 @@ static int snd_emu10k1_init(struct snd_emu10k1 *emu, int enable_ir, int resume)
                        emu->i2c_capture_volume[n][0]= 0xcf;
                        emu->i2c_capture_volume[n][1]= 0xcf;
                }
-
        }
 
        
@@ -288,7 +287,7 @@ static int snd_emu10k1_init(struct snd_emu10k1 *emu, int enable_ir, int resume)
                snd_emu10k1_ptr_write(emu, MAPB, ch, silent_page);
        }
 
-       if (emu->card_capabilities->emu1010) {
+       if (emu->card_capabilities->emu_model) {
                outl(HCFG_AUTOMUTE_ASYNC |
                        HCFG_EMU32_SLAVE |
                        HCFG_AUDIOENABLE, emu->port + HCFG);
@@ -318,7 +317,7 @@ static int snd_emu10k1_init(struct snd_emu10k1 *emu, int enable_ir, int resume)
                outl(HCFG_LOCKTANKCACHE_MASK | HCFG_AUTOMUTE | HCFG_JOYENABLE, emu->port + HCFG);
 
        if (enable_ir) {        /* enable IR for SB Live */
-               if (emu->card_capabilities->emu1010) {
+               if (emu->card_capabilities->emu_model) {
                        ;  /* Disable all access to A_IOCFG for the emu1010 */
                } else if (emu->card_capabilities->i2c_adc) {
                        ;  /* Disable A_IOCFG for Audigy 2 ZS Notebook */
@@ -339,7 +338,7 @@ static int snd_emu10k1_init(struct snd_emu10k1 *emu, int enable_ir, int resume)
                }
        }
        
-       if (emu->card_capabilities->emu1010) {
+       if (emu->card_capabilities->emu_model) {
                ;  /* Disable all access to A_IOCFG for the emu1010 */
        } else if (emu->card_capabilities->i2c_adc) {
                ;  /* Disable A_IOCFG for Audigy 2 ZS Notebook */
@@ -359,7 +358,7 @@ static void snd_emu10k1_audio_enable(struct snd_emu10k1 *emu)
        outl(inl(emu->port + HCFG) | HCFG_AUDIOENABLE, emu->port + HCFG);
 
        /* Enable analog/digital outs on audigy */
-       if (emu->card_capabilities->emu1010) {
+       if (emu->card_capabilities->emu_model) {
                ;  /* Disable all access to A_IOCFG for the emu1010 */
        } else if (emu->card_capabilities->i2c_adc) {
                ;  /* Disable A_IOCFG for Audigy 2 ZS Notebook */
@@ -652,6 +651,8 @@ static int snd_emu10k1_cardbus_init(struct snd_emu10k1 * emu)
        value = inl(special_port);
 
        snd_emu10k1_ptr20_write(emu, TINA2_VOLUME, 0, 0xfefefefe); /* Defaults to 0x30303030 */
+       /* Delay to give time for ADC chip to switch on. It needs 113ms */
+       msleep(200);
        return 0;
 }
 
@@ -661,6 +662,8 @@ static int snd_emu1010_load_firmware(struct snd_emu10k1 * emu, const char * file
        int n, i;
        int reg;
        int value;
+       unsigned int write_post;
+       unsigned long flags;
        const struct firmware *fw_entry;
 
        if ((err = request_firmware(&fw_entry, filename, &emu->pci->dev)) != 0) {
@@ -668,12 +671,6 @@ static int snd_emu1010_load_firmware(struct snd_emu10k1 * emu, const char * file
                return err;
        }
        snd_printk(KERN_INFO "firmware size=0x%zx\n", fw_entry->size);
-#if 0
-       if (fw_entry->size != 0x133a4) {
-               snd_printk(KERN_ERR "firmware: %s wrong size.\n",filename);
-               return -EINVAL;
-       }
-#endif
 
        /* The FPGA is a Xilinx Spartan IIE XC2S50E */
        /* GPIO7 -> FPGA PGMN
@@ -681,9 +678,12 @@ static int snd_emu1010_load_firmware(struct snd_emu10k1 * emu, const char * file
         * GPIO5 -> FPGA DIN
         * FPGA CONFIG OFF -> FPGA PGMN
         */
+       spin_lock_irqsave(&emu->emu_lock, flags);
        outl(0x00, emu->port + A_IOCFG); /* Set PGMN low for 1uS. */
-       udelay(1);
+       write_post = inl(emu->port + A_IOCFG);
+       udelay(100);
        outl(0x80, emu->port + A_IOCFG); /* Leave bit 7 set during netlist setup. */
+       write_post = inl(emu->port + A_IOCFG);
        udelay(100); /* Allow FPGA memory to clean */
        for(n = 0; n < fw_entry->size; n++) {
                value=fw_entry->data[n];        
@@ -693,18 +693,22 @@ static int snd_emu1010_load_firmware(struct snd_emu10k1 * emu, const char * file
                                reg = reg | 0x20;
                        value = value >> 1;   
                        outl(reg, emu->port + A_IOCFG);
+                       write_post = inl(emu->port + A_IOCFG);
                        outl(reg | 0x40, emu->port + A_IOCFG);
+                       write_post = inl(emu->port + A_IOCFG);
                }
        }
        /* After programming, set GPIO bit 4 high again. */
        outl(0x10, emu->port + A_IOCFG);
-       
+       write_post = inl(emu->port + A_IOCFG);
+       spin_unlock_irqrestore(&emu->emu_lock, flags);
 
         release_firmware(fw_entry);
        return 0;
 }
 
-int emu1010_firmware_thread(void *data) {
+static int emu1010_firmware_thread(void *data)
+{
        struct snd_emu10k1 * emu = data;
        int tmp,tmp2;
        int reg;
@@ -712,7 +716,7 @@ int emu1010_firmware_thread(void *data) {
 
        for (;;) {
                /* Delay to allow Audio Dock to settle */
-               msleep(1000);
+               msleep_interruptible(1000);
                if (kthread_should_stop())
                        break;
                snd_emu1010_fpga_read(emu, EMU_HANA_IRQ_STATUS, &tmp ); /* IRQ Status */
@@ -722,17 +726,20 @@ int emu1010_firmware_thread(void *data) {
                        /* Return to Audio Dock programming mode */
                        snd_printk(KERN_INFO "emu1010: Loading Audio Dock Firmware\n");
                        snd_emu1010_fpga_write(emu,  EMU_HANA_FPGA_CONFIG, EMU_HANA_FPGA_CONFIG_AUDIODOCK );
-                       if (emu->card_capabilities->emu1010 == 1) {
+                       if (emu->card_capabilities->emu_model ==
+                           EMU_MODEL_EMU1010) {
                                if ((err = snd_emu1010_load_firmware(emu, DOCK_FILENAME)) != 0) {
-                                       return err;
+                                       continue;
                                }
-                       } else if (emu->card_capabilities->emu1010 == 2) {
+                       } else if (emu->card_capabilities->emu_model ==
+                                  EMU_MODEL_EMU1010B) {
                                if ((err = snd_emu1010_load_firmware(emu, MICRO_DOCK_FILENAME)) != 0) {
-                                       return err;
+                                       continue;
                                }
-                       } else if (emu->card_capabilities->emu1010 == 3) {
+                       } else if (emu->card_capabilities->emu_model ==
+                                  EMU_MODEL_EMU1616) {
                                if ((err = snd_emu1010_load_firmware(emu, MICRO_DOCK_FILENAME)) != 0) {
-                                       return err;
+                                       continue;
                                }
                        }
 
@@ -745,8 +752,7 @@ int emu1010_firmware_thread(void *data) {
                        if ((reg & 0x1f) != 0x15) {
                                /* FPGA failed to be programmed */
                                snd_printk(KERN_INFO "emu1010: Loading Audio Dock Firmware file failed, reg=0x%x\n", reg);
-                               return 0;
-                               return -ENODEV;
+                               continue;
                        }
                        snd_printk(KERN_INFO "emu1010: Audio Dock Firmware loaded\n");
                        snd_emu1010_fpga_read(emu, EMU_DOCK_MAJOR_REV, &tmp );
@@ -757,9 +763,9 @@ int emu1010_firmware_thread(void *data) {
                        msleep(10);
                        /* Unmute all. Default is muted after a firmware load */
                        snd_emu1010_fpga_write(emu, EMU_HANA_UNMUTE, EMU_UNMUTE );
-                       break;
                }
        }
+       snd_printk(KERN_INFO "emu1010: firmware thread stopping\n");
        return 0;
 }
 
@@ -800,6 +806,7 @@ static int snd_emu10k1_emu1010_init(struct snd_emu10k1 * emu)
        int tmp,tmp2;
        int reg;
        int err;
+       const char *filename = NULL;
 
        snd_printk(KERN_INFO "emu1010: Special config.\n");
        /* AC97 2.1, Any 16Meg of 4Gig address, Auto-Mute, EMU32 Slave,
@@ -841,21 +848,31 @@ static int snd_emu10k1_emu1010_init(struct snd_emu10k1 * emu)
                return -ENODEV;
        }
        snd_printk(KERN_INFO "emu1010: EMU_HANA_ID=0x%x\n",reg);
-       if (emu->card_capabilities->emu1010 == 1) {
-               if ((err = snd_emu1010_load_firmware(emu, HANA_FILENAME)) != 0) {
-                       snd_printk(KERN_INFO "emu1010: Loading Hana Firmware file %s failed\n", HANA_FILENAME);
-                       return err;
-               }
-       } else if (emu->card_capabilities->emu1010 == 2) {
-               if ((err = snd_emu1010_load_firmware(emu, EMU1010B_FILENAME)) != 0) {
-                       snd_printk(KERN_INFO "emu1010: Loading Firmware file %s failed\n", EMU1010B_FILENAME);
-                       return err;
-               }
-       } else if (emu->card_capabilities->emu1010 == 3) {
-               if ((err = snd_emu1010_load_firmware(emu, EMU1010_NOTEBOOK_FILENAME)) != 0) {
-                       snd_printk(KERN_INFO "emu1010: Loading Firmware file %s failed\n", EMU1010_NOTEBOOK_FILENAME);
-                       return err;
-               }
+       switch (emu->card_capabilities->emu_model) {
+       case EMU_MODEL_EMU1010:
+               filename = HANA_FILENAME;
+               break;
+       case EMU_MODEL_EMU1010B:
+               filename = EMU1010B_FILENAME;
+               break;
+       case EMU_MODEL_EMU1616:
+               filename = EMU1010_NOTEBOOK_FILENAME;
+               break;
+       case EMU_MODEL_EMU0404:
+               filename = EMU0404_FILENAME;
+               break;
+       default:
+               filename = NULL;
+               return -ENODEV;
+               break;
+       }
+       snd_printk(KERN_INFO "emu1010: filename %s testing\n", filename);
+       err = snd_emu1010_load_firmware(emu, filename);
+       if (err != 0) {
+               snd_printk(
+                       KERN_INFO "emu1010: Loading Firmware file %s failed\n",
+                       filename);
+               return err;
        }
 
        /* ID, should read & 0x7f = 0x55 when FPGA programmed. */
@@ -1074,10 +1091,12 @@ static int snd_emu10k1_emu1010_init(struct snd_emu10k1 * emu)
        snd_emu1010_fpga_write(emu, EMU_HANA_SPDIF_MODE, 0x10 ); /* SPDIF Format spdif  (or 0x11 for aes/ebu) */
 
        /* Start Micro/Audio Dock firmware loader thread */
-       emu->emu1010.firmware_thread = kthread_create(&emu1010_firmware_thread,
-                                   emu,
-                                   "emu1010_firmware");
-       wake_up_process(emu->emu1010.firmware_thread);
+       if (!emu->emu1010.firmware_thread) {
+               emu->emu1010.firmware_thread =
+                       kthread_create(emu1010_firmware_thread, emu,
+                                      "emu1010_firmware");
+               wake_up_process(emu->emu1010.firmware_thread);
+       }
 
 #if 0
        snd_emu1010_fpga_link_dst_src_write(emu,
@@ -1090,79 +1109,114 @@ static int snd_emu10k1_emu1010_init(struct snd_emu10k1 * emu)
                EMU_DST_HANA_SPDIF_RIGHT1, EMU_SRC_ALICE_EMU32A + 3); /* ALICE2 bus 0xb3 */
 #endif
        /* Default outputs */
-       snd_emu1010_fpga_link_dst_src_write(emu,
-               EMU_DST_DOCK_DAC1_LEFT1, EMU_SRC_ALICE_EMU32A + 0); /* ALICE2 bus 0xa0 */
-       emu->emu1010.output_source[0] = 21;
-       snd_emu1010_fpga_link_dst_src_write(emu,
-               EMU_DST_DOCK_DAC1_RIGHT1, EMU_SRC_ALICE_EMU32A + 1);
-       emu->emu1010.output_source[1] = 22;
-       snd_emu1010_fpga_link_dst_src_write(emu,
-               EMU_DST_DOCK_DAC2_LEFT1, EMU_SRC_ALICE_EMU32A + 2);
-       emu->emu1010.output_source[2] = 23;
-       snd_emu1010_fpga_link_dst_src_write(emu,
-               EMU_DST_DOCK_DAC2_RIGHT1, EMU_SRC_ALICE_EMU32A + 3);
-       emu->emu1010.output_source[3] = 24;
-       snd_emu1010_fpga_link_dst_src_write(emu,
-               EMU_DST_DOCK_DAC3_LEFT1, EMU_SRC_ALICE_EMU32A + 4);
-       emu->emu1010.output_source[4] = 25;
-       snd_emu1010_fpga_link_dst_src_write(emu,
-               EMU_DST_DOCK_DAC3_RIGHT1, EMU_SRC_ALICE_EMU32A + 5);
-       emu->emu1010.output_source[5] = 26;
-       snd_emu1010_fpga_link_dst_src_write(emu,
-               EMU_DST_DOCK_DAC4_LEFT1, EMU_SRC_ALICE_EMU32A + 6);
-       emu->emu1010.output_source[6] = 27;
-       snd_emu1010_fpga_link_dst_src_write(emu,
-               EMU_DST_DOCK_DAC4_RIGHT1, EMU_SRC_ALICE_EMU32A + 7);
-       emu->emu1010.output_source[7] = 28;
-       snd_emu1010_fpga_link_dst_src_write(emu,
-               EMU_DST_DOCK_PHONES_LEFT1, EMU_SRC_ALICE_EMU32A + 0); /* ALICE2 bus 0xa0 */
-       emu->emu1010.output_source[8] = 21;
-       snd_emu1010_fpga_link_dst_src_write(emu,
-               EMU_DST_DOCK_PHONES_RIGHT1, EMU_SRC_ALICE_EMU32A + 1);
-       emu->emu1010.output_source[9] = 22;
-       snd_emu1010_fpga_link_dst_src_write(emu,
-               EMU_DST_DOCK_SPDIF_LEFT1, EMU_SRC_ALICE_EMU32A + 0); /* ALICE2 bus 0xa0 */
-       emu->emu1010.output_source[10] = 21;
-       snd_emu1010_fpga_link_dst_src_write(emu,
-               EMU_DST_DOCK_SPDIF_RIGHT1, EMU_SRC_ALICE_EMU32A + 1);
-       emu->emu1010.output_source[11] = 22;
-       snd_emu1010_fpga_link_dst_src_write(emu,
-               EMU_DST_HANA_SPDIF_LEFT1, EMU_SRC_ALICE_EMU32A + 0); /* ALICE2 bus 0xa0 */
-       emu->emu1010.output_source[12] = 21;
-       snd_emu1010_fpga_link_dst_src_write(emu,
-               EMU_DST_HANA_SPDIF_RIGHT1, EMU_SRC_ALICE_EMU32A + 1);
-       emu->emu1010.output_source[13] = 22;
-       snd_emu1010_fpga_link_dst_src_write(emu,
-               EMU_DST_HAMOA_DAC_LEFT1, EMU_SRC_ALICE_EMU32A + 0); /* ALICE2 bus 0xa0 */
-       emu->emu1010.output_source[14] = 21;
-       snd_emu1010_fpga_link_dst_src_write(emu,
-               EMU_DST_HAMOA_DAC_RIGHT1, EMU_SRC_ALICE_EMU32A + 1);
-       emu->emu1010.output_source[15] = 22;
-       snd_emu1010_fpga_link_dst_src_write(emu,
-               EMU_DST_HANA_ADAT, EMU_SRC_ALICE_EMU32A + 0); /* ALICE2 bus 0xa0 */
-       emu->emu1010.output_source[16] = 21;
-       snd_emu1010_fpga_link_dst_src_write(emu,
-               EMU_DST_HANA_ADAT + 1, EMU_SRC_ALICE_EMU32A + 1);
-       emu->emu1010.output_source[17] = 22;
-       snd_emu1010_fpga_link_dst_src_write(emu,
-               EMU_DST_HANA_ADAT + 2, EMU_SRC_ALICE_EMU32A + 2);
-       emu->emu1010.output_source[18] = 23;
-       snd_emu1010_fpga_link_dst_src_write(emu,
-               EMU_DST_HANA_ADAT + 3, EMU_SRC_ALICE_EMU32A + 3);
-       emu->emu1010.output_source[19] = 24;
-       snd_emu1010_fpga_link_dst_src_write(emu,
-               EMU_DST_HANA_ADAT + 4, EMU_SRC_ALICE_EMU32A + 4);
-       emu->emu1010.output_source[20] = 25;
-       snd_emu1010_fpga_link_dst_src_write(emu,
-               EMU_DST_HANA_ADAT + 5, EMU_SRC_ALICE_EMU32A + 5);
-       emu->emu1010.output_source[21] = 26;
-       snd_emu1010_fpga_link_dst_src_write(emu,
-               EMU_DST_HANA_ADAT + 6, EMU_SRC_ALICE_EMU32A + 6);
-       emu->emu1010.output_source[22] = 27;
-       snd_emu1010_fpga_link_dst_src_write(emu,
-               EMU_DST_HANA_ADAT + 7, EMU_SRC_ALICE_EMU32A + 7);
-       emu->emu1010.output_source[23] = 28;
-
+       if (emu->card_capabilities->emu_model == EMU_MODEL_EMU1616) {
+               /* 1616(M) cardbus default outputs */
+               /* ALICE2 bus 0xa0 */
+               snd_emu1010_fpga_link_dst_src_write(emu,
+                       EMU_DST_DOCK_DAC1_LEFT1, EMU_SRC_ALICE_EMU32A + 0);
+               emu->emu1010.output_source[0] = 17;
+               snd_emu1010_fpga_link_dst_src_write(emu,
+                       EMU_DST_DOCK_DAC1_RIGHT1, EMU_SRC_ALICE_EMU32A + 1);
+               emu->emu1010.output_source[1] = 18;
+               snd_emu1010_fpga_link_dst_src_write(emu,
+                       EMU_DST_DOCK_DAC2_LEFT1, EMU_SRC_ALICE_EMU32A + 2);
+               emu->emu1010.output_source[2] = 19;
+               snd_emu1010_fpga_link_dst_src_write(emu,
+                       EMU_DST_DOCK_DAC2_RIGHT1, EMU_SRC_ALICE_EMU32A + 3);
+               emu->emu1010.output_source[3] = 20;
+               snd_emu1010_fpga_link_dst_src_write(emu,
+                       EMU_DST_DOCK_DAC3_LEFT1, EMU_SRC_ALICE_EMU32A + 4);
+               emu->emu1010.output_source[4] = 21;
+               snd_emu1010_fpga_link_dst_src_write(emu,
+                       EMU_DST_DOCK_DAC3_RIGHT1, EMU_SRC_ALICE_EMU32A + 5);
+               emu->emu1010.output_source[5] = 22;
+               /* ALICE2 bus 0xa0 */
+               snd_emu1010_fpga_link_dst_src_write(emu,
+                       EMU_DST_MANA_DAC_LEFT, EMU_SRC_ALICE_EMU32A + 0);
+               emu->emu1010.output_source[16] = 17;
+               snd_emu1010_fpga_link_dst_src_write(emu,
+                       EMU_DST_MANA_DAC_RIGHT, EMU_SRC_ALICE_EMU32A + 1);
+               emu->emu1010.output_source[17] = 18;
+       } else {
+               /* ALICE2 bus 0xa0 */
+               snd_emu1010_fpga_link_dst_src_write(emu,
+                       EMU_DST_DOCK_DAC1_LEFT1, EMU_SRC_ALICE_EMU32A + 0);
+               emu->emu1010.output_source[0] = 21;
+               snd_emu1010_fpga_link_dst_src_write(emu,
+                       EMU_DST_DOCK_DAC1_RIGHT1, EMU_SRC_ALICE_EMU32A + 1);
+               emu->emu1010.output_source[1] = 22;
+               snd_emu1010_fpga_link_dst_src_write(emu,
+                       EMU_DST_DOCK_DAC2_LEFT1, EMU_SRC_ALICE_EMU32A + 2);
+               emu->emu1010.output_source[2] = 23;
+               snd_emu1010_fpga_link_dst_src_write(emu,
+                       EMU_DST_DOCK_DAC2_RIGHT1, EMU_SRC_ALICE_EMU32A + 3);
+               emu->emu1010.output_source[3] = 24;
+               snd_emu1010_fpga_link_dst_src_write(emu,
+                       EMU_DST_DOCK_DAC3_LEFT1, EMU_SRC_ALICE_EMU32A + 4);
+               emu->emu1010.output_source[4] = 25;
+               snd_emu1010_fpga_link_dst_src_write(emu,
+                       EMU_DST_DOCK_DAC3_RIGHT1, EMU_SRC_ALICE_EMU32A + 5);
+               emu->emu1010.output_source[5] = 26;
+               snd_emu1010_fpga_link_dst_src_write(emu,
+                       EMU_DST_DOCK_DAC4_LEFT1, EMU_SRC_ALICE_EMU32A + 6);
+               emu->emu1010.output_source[6] = 27;
+               snd_emu1010_fpga_link_dst_src_write(emu,
+                       EMU_DST_DOCK_DAC4_RIGHT1, EMU_SRC_ALICE_EMU32A + 7);
+               emu->emu1010.output_source[7] = 28;
+               /* ALICE2 bus 0xa0 */
+               snd_emu1010_fpga_link_dst_src_write(emu,
+                       EMU_DST_DOCK_PHONES_LEFT1, EMU_SRC_ALICE_EMU32A + 0);
+               emu->emu1010.output_source[8] = 21;
+               snd_emu1010_fpga_link_dst_src_write(emu,
+                       EMU_DST_DOCK_PHONES_RIGHT1, EMU_SRC_ALICE_EMU32A + 1);
+               emu->emu1010.output_source[9] = 22;
+               /* ALICE2 bus 0xa0 */
+               snd_emu1010_fpga_link_dst_src_write(emu,
+                       EMU_DST_DOCK_SPDIF_LEFT1, EMU_SRC_ALICE_EMU32A + 0);
+               emu->emu1010.output_source[10] = 21;
+               snd_emu1010_fpga_link_dst_src_write(emu,
+                       EMU_DST_DOCK_SPDIF_RIGHT1, EMU_SRC_ALICE_EMU32A + 1);
+               emu->emu1010.output_source[11] = 22;
+               /* ALICE2 bus 0xa0 */
+               snd_emu1010_fpga_link_dst_src_write(emu,
+                       EMU_DST_HANA_SPDIF_LEFT1, EMU_SRC_ALICE_EMU32A + 0);
+               emu->emu1010.output_source[12] = 21;
+               snd_emu1010_fpga_link_dst_src_write(emu,
+                       EMU_DST_HANA_SPDIF_RIGHT1, EMU_SRC_ALICE_EMU32A + 1);
+               emu->emu1010.output_source[13] = 22;
+               /* ALICE2 bus 0xa0 */
+               snd_emu1010_fpga_link_dst_src_write(emu,
+                       EMU_DST_HAMOA_DAC_LEFT1, EMU_SRC_ALICE_EMU32A + 0);
+               emu->emu1010.output_source[14] = 21;
+               snd_emu1010_fpga_link_dst_src_write(emu,
+                       EMU_DST_HAMOA_DAC_RIGHT1, EMU_SRC_ALICE_EMU32A + 1);
+               emu->emu1010.output_source[15] = 22;
+               /* ALICE2 bus 0xa0 */
+               snd_emu1010_fpga_link_dst_src_write(emu,
+                       EMU_DST_HANA_ADAT, EMU_SRC_ALICE_EMU32A + 0);
+               emu->emu1010.output_source[16] = 21;
+               snd_emu1010_fpga_link_dst_src_write(emu,
+                       EMU_DST_HANA_ADAT + 1, EMU_SRC_ALICE_EMU32A + 1);
+               emu->emu1010.output_source[17] = 22;
+               snd_emu1010_fpga_link_dst_src_write(emu,
+                       EMU_DST_HANA_ADAT + 2, EMU_SRC_ALICE_EMU32A + 2);
+               emu->emu1010.output_source[18] = 23;
+               snd_emu1010_fpga_link_dst_src_write(emu,
+                       EMU_DST_HANA_ADAT + 3, EMU_SRC_ALICE_EMU32A + 3);
+               emu->emu1010.output_source[19] = 24;
+               snd_emu1010_fpga_link_dst_src_write(emu,
+                       EMU_DST_HANA_ADAT + 4, EMU_SRC_ALICE_EMU32A + 4);
+               emu->emu1010.output_source[20] = 25;
+               snd_emu1010_fpga_link_dst_src_write(emu,
+                       EMU_DST_HANA_ADAT + 5, EMU_SRC_ALICE_EMU32A + 5);
+               emu->emu1010.output_source[21] = 26;
+               snd_emu1010_fpga_link_dst_src_write(emu,
+                       EMU_DST_HANA_ADAT + 6, EMU_SRC_ALICE_EMU32A + 6);
+               emu->emu1010.output_source[22] = 27;
+               snd_emu1010_fpga_link_dst_src_write(emu,
+                       EMU_DST_HANA_ADAT + 7, EMU_SRC_ALICE_EMU32A + 7);
+               emu->emu1010.output_source[23] = 28;
+       }
        /* TEMP: Select SPDIF in/out */
        //snd_emu1010_fpga_write(emu, EMU_HANA_OPTICAL_TYPE, 0x0); /* Output spdif */
 
@@ -1202,11 +1256,12 @@ static int snd_emu10k1_free(struct snd_emu10k1 *emu)
                }
                snd_emu10k1_free_efx(emu);
                }
-       if (emu->card_capabilities->emu1010) {
+       if (emu->card_capabilities->emu_model == EMU_MODEL_EMU1010) {
                /* Disable 48Volt power to Audio Dock */
                snd_emu1010_fpga_write(emu,  EMU_HANA_DOCK_PWR,  0 );
-               kthread_stop(emu->emu1010.firmware_thread);
        }
+       if (emu->emu1010.firmware_thread)
+               kthread_stop(emu->emu1010.firmware_thread);
        if (emu->memhdr)
                snd_util_memhdr_free(emu->memhdr);
        if (emu->silent_page.area)
@@ -1338,6 +1393,7 @@ static struct snd_emu_chip_details emu_chip_details[] = {
         .spi_dac = 1,
         .i2c_adc = 1,
         .spk71 = 1} ,
+       /* Tested by James@superbug.co.uk 4th Nov 2007. */
        {.vendor = 0x1102, .device = 0x0008, .subsystem = 0x42011102,
         .driver = "Audigy2", .name = "E-mu 1010 Notebook [MAEM8950]", 
         .id = "EMU1010",
@@ -1345,28 +1401,46 @@ static struct snd_emu_chip_details emu_chip_details[] = {
         .ca0108_chip = 1,
         .ca_cardbus_chip = 1,
         .spk71 = 1 ,
-        .emu1010 = 3} ,
+        .emu_model = EMU_MODEL_EMU1616},
+       /* Tested by James@superbug.co.uk 4th Nov 2007. */
        {.vendor = 0x1102, .device = 0x0008, .subsystem = 0x40041102,
         .driver = "Audigy2", .name = "E-mu 1010b PCI [MAEM????]", 
         .id = "EMU1010",
         .emu10k2_chip = 1,
         .ca0108_chip = 1,
-        .spk71 = 1 ,
-        .emu1010 = 2} ,
+        .spk71 = 1,
+        .emu_model = EMU_MODEL_EMU1010B},
+       /* Tested by James@superbug.co.uk 8th July 2005. */
+       {.vendor = 0x1102, .device = 0x0004, .subsystem = 0x40011102,
+        .driver = "Audigy2", .name = "E-mu 1010 [4001]",
+        .id = "EMU1010",
+        .emu10k2_chip = 1,
+        .ca0102_chip = 1,
+        .spk71 = 1,
+        .emu_model = EMU_MODEL_EMU1010}, /* Emu 1010 */
+       /* EMU0404b */
+       {.vendor = 0x1102, .device = 0x0008, .subsystem = 0x40021102,
+        .driver = "Audigy2", .name = "E-mu 0404b [4002]",
+        .id = "EMU0404",
+        .emu10k2_chip = 1,
+        .ca0108_chip = 1,
+        .spk71 = 1,
+        .emu_model = EMU_MODEL_EMU0404}, /* EMU 0404 */
+       /* Tested by James@superbug.co.uk 20-3-2007. */
+       {.vendor = 0x1102, .device = 0x0004, .subsystem = 0x40021102,
+        .driver = "Audigy2", .name = "E-mu 0404 [4002]",
+        .id = "EMU0404",
+        .emu10k2_chip = 1,
+        .ca0102_chip = 1,
+        .spk71 = 1,
+        .emu_model = EMU_MODEL_EMU0404}, /* EMU 0404 */
+       /* Audigy4 (Not PRO) SB0610 */
        {.vendor = 0x1102, .device = 0x0008, 
         .driver = "Audigy2", .name = "Audigy 2 Value [Unknown]", 
         .id = "Audigy2",
         .emu10k2_chip = 1,
         .ca0108_chip = 1,
         .ac97_chip = 1} ,
-       /* Tested by James@superbug.co.uk 8th July 2005. No sound available yet. */
-       {.vendor = 0x1102, .device = 0x0004, .subsystem = 0x40011102,
-        .driver = "Audigy2", .name = "E-mu 1010 [4001]", 
-        .id = "EMU1010",
-        .emu10k2_chip = 1,
-        .ca0102_chip = 1,
-        .spk71 = 1,
-        .emu1010 = 1} ,
        /* Tested by James@superbug.co.uk 3rd July 2005 */
        {.vendor = 0x1102, .device = 0x0004, .subsystem = 0x20071102,
         .driver = "Audigy2", .name = "Audigy 4 PRO [SB0380]", 
@@ -1654,6 +1728,8 @@ int __devinit snd_emu10k1_create(struct snd_card *card,
        emu->card = card;
        spin_lock_init(&emu->reg_lock);
        spin_lock_init(&emu->emu_lock);
+       spin_lock_init(&emu->spi_lock);
+       spin_lock_init(&emu->i2c_lock);
        spin_lock_init(&emu->voice_lock);
        spin_lock_init(&emu->synth_lock);
        spin_lock_init(&emu->memblk_lock);
@@ -1794,7 +1870,7 @@ int __devinit snd_emu10k1_create(struct snd_card *card,
        if (emu->card_capabilities->ecard) {
                if ((err = snd_emu10k1_ecard_init(emu)) < 0)
                        goto error;
-       } else if (emu->card_capabilities->emu1010) {
+       } else if (emu->card_capabilities->emu_model) {
                if ((err = snd_emu10k1_emu1010_init(emu)) < 0) {
                        snd_emu10k1_free(emu);
                        return err;
@@ -1943,7 +2019,7 @@ void snd_emu10k1_resume_init(struct snd_emu10k1 *emu)
                snd_emu10k1_cardbus_init(emu);
        if (emu->card_capabilities->ecard)
                snd_emu10k1_ecard_init(emu);
-       else if (emu->card_capabilities->emu1010)
+       else if (emu->card_capabilities->emu_model)
                snd_emu10k1_emu1010_init(emu);
        else
                snd_emu10k1_ptr_write(emu, AC97SLOT, 0, AC97SLOT_CNTR|AC97SLOT_LFE);
index 204995a1dfbd15e311f0dbce1360b7529b4a5f95..ad7b71491fc49cd32a9d08f8abbf3c5e4938485e 100644 (file)
@@ -30,7 +30,7 @@ MODULE_LICENSE("GPL");
  */
 static int snd_emu10k1_synth_new_device(struct snd_seq_device *dev)
 {
-       struct snd_emux *emu;
+       struct snd_emux *emux;
        struct snd_emu10k1 *hw;
        struct snd_emu10k1_synth_arg *arg;
        unsigned long flags;
@@ -46,53 +46,56 @@ static int snd_emu10k1_synth_new_device(struct snd_seq_device *dev)
        else if (arg->max_voices > 64)
                arg->max_voices = 64;
 
-       if (snd_emux_new(&emu) < 0)
+       if (snd_emux_new(&emux) < 0)
                return -ENOMEM;
 
-       snd_emu10k1_ops_setup(emu);
-       emu->hw = hw = arg->hwptr;
-       emu->max_voices = arg->max_voices;
-       emu->num_ports = arg->seq_ports;
-       emu->pitch_shift = -501;
-       emu->memhdr = hw->memhdr;
-       emu->midi_ports = arg->seq_ports < 2 ? arg->seq_ports : 2; /* maximum two ports */
-       emu->midi_devidx = hw->audigy ? 2 : 1; /* audigy has two external midis */
-       emu->linear_panning = 0;
-       emu->hwdep_idx = 2; /* FIXED */
-
-       if (snd_emux_register(emu, dev->card, arg->index, "Emu10k1") < 0) {
-               snd_emux_free(emu);
+       snd_emu10k1_ops_setup(emux);
+       hw = arg->hwptr;
+       emux->hw = hw;
+       emux->max_voices = arg->max_voices;
+       emux->num_ports = arg->seq_ports;
+       emux->pitch_shift = -501;
+       emux->memhdr = hw->memhdr;
+       /* maximum two ports */
+       emux->midi_ports = arg->seq_ports < 2 ? arg->seq_ports : 2;
+       /* audigy has two external midis */
+       emux->midi_devidx = hw->audigy ? 2 : 1;
+       emux->linear_panning = 0;
+       emux->hwdep_idx = 2; /* FIXED */
+
+       if (snd_emux_register(emux, dev->card, arg->index, "Emu10k1") < 0) {
+               snd_emux_free(emux);
                return -ENOMEM;
        }
 
        spin_lock_irqsave(&hw->voice_lock, flags);
-       hw->synth = emu;
+       hw->synth = emux;
        hw->get_synth_voice = snd_emu10k1_synth_get_voice;
        spin_unlock_irqrestore(&hw->voice_lock, flags);
 
-       dev->driver_data = emu;
+       dev->driver_data = emux;
 
        return 0;
 }
 
 static int snd_emu10k1_synth_delete_device(struct snd_seq_device *dev)
 {
-       struct snd_emux *emu;
+       struct snd_emux *emux;
        struct snd_emu10k1 *hw;
        unsigned long flags;
 
        if (dev->driver_data == NULL)
                return 0; /* not registered actually */
 
-       emu = dev->driver_data;
+       emux = dev->driver_data;
 
-       hw = emu->hw;
+       hw = emux->hw;
        spin_lock_irqsave(&hw->voice_lock, flags);
        hw->synth = NULL;
        hw->get_synth_voice = NULL;
        spin_unlock_irqrestore(&hw->voice_lock, flags);
 
-       snd_emux_free(emu);
+       snd_emux_free(emux);
        return 0;
 }
 
index 308ddc84bb4dbe4bcecfd4c25c8875866beccebe..25f328ff639f35cdf14927a0ba11462a119df817 100644 (file)
@@ -20,7 +20,6 @@
  *   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA
  */
 
-#include <sound/driver.h>
 #include <linux/time.h>
 #include <sound/core.h>
 #include <sound/emu10k1_synth.h>
index 1ec7ebaff9e9f534f92fbbf0723f30082cce7320..5512abd98bd93f3f69370f792e592145b3c3b92e 100644 (file)
@@ -29,7 +29,6 @@
  *   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA
  *
  */
-#include <sound/driver.h>
 #include <linux/init.h>
 #include <linux/interrupt.h>
 #include <linux/pci.h>
@@ -1583,6 +1582,8 @@ static int __devinit snd_emu10k1x_probe(struct pci_dev *pci,
        sprintf(card->longname, "%s at 0x%lx irq %i",
                card->shortname, chip->port, chip->irq);
 
+       snd_card_set_dev(card, &pci->dev);
+
        if ((err = snd_card_register(card)) < 0) {
                snd_card_free(card);
                return err;
index 9bf1cd59219912ca5d435e706b13302f70f77585..71dc4c8865b88323edbe13a811c5171061d26e51 100644 (file)
@@ -28,7 +28,6 @@
  *
  */
 
-#include <sound/driver.h>
 #include <linux/pci.h>
 #include <linux/capability.h>
 #include <linux/delay.h>
@@ -666,7 +665,7 @@ static unsigned int *copy_tlv(const unsigned int __user *_tlv)
                return NULL;
        if (data[1] >= MAX_TLV_SIZE)
                return NULL;
-       tlv = kmalloc(data[1] * 4 + sizeof(data), GFP_KERNEL);
+       tlv = kmalloc(data[1] + sizeof(data), GFP_KERNEL);
        if (!tlv)
                return NULL;
        memcpy(tlv, data, sizeof(data));
@@ -1262,7 +1261,7 @@ static int __devinit _snd_emu10k1_audigy_init_efx(struct snd_emu10k1 *emu)
 A_OP(icode, &ptr, iMAC0, A_GPR(var), A_GPR(var), A_GPR(vol), A_EXTIN(input))
 
        /* emu1212 DSP 0 and DSP 1 Capture */
-       if (emu->card_capabilities->emu1010) {
+       if (emu->card_capabilities->emu_model) {
                if (emu->card_capabilities->ca0108_chip) {
                        /* Note:JCD:No longer bit shift lower 16bits to upper 16bits of 32bit value. */
                        A_OP(icode, &ptr, iMACINT0, A_GPR(tmp), A_C_00000000, A3_EMU32IN(0x0), A_C_00000001);
@@ -1516,7 +1515,7 @@ A_OP(icode, &ptr, iMAC0, A_GPR(var), A_GPR(var), A_GPR(vol), A_EXTIN(input))
 
        /* digital outputs */
        /* A_PUT_STEREO_OUTPUT(A_EXTOUT_FRONT_L, A_EXTOUT_FRONT_R, playback + SND_EMU10K1_PLAYBACK_CHANNELS); */
-       if (emu->card_capabilities->emu1010) {
+       if (emu->card_capabilities->emu_model) {
                /* EMU1010 Outputs from PCM Front, Rear, Center, LFE, Side */
                snd_printk("EMU outputs on\n");
                for (z = 0; z < 8; z++) {
@@ -1564,7 +1563,7 @@ A_OP(icode, &ptr, iMAC0, A_GPR(var), A_GPR(var), A_GPR(vol), A_EXTIN(input))
        A_PUT_OUTPUT(A_EXTOUT_ADC_CAP_R, capture+1);
 #endif
 
-       if (emu->card_capabilities->emu1010) {
+       if (emu->card_capabilities->emu_model) {
                if (emu->card_capabilities->ca0108_chip) {
                        snd_printk("EMU2 inputs on\n");
                        for (z = 0; z < 0x10; z++) {
index ccacd7b890e82cb5f3293e622496b3deaf5b813d..fd221209abcb221f6db0dcf323476b8620e40331 100644 (file)
@@ -30,7 +30,6 @@
  *
  */
 
-#include <sound/driver.h>
 #include <linux/time.h>
 #include <linux/init.h>
 #include <sound/core.h>
@@ -140,6 +139,61 @@ static char *emu1010_src_texts[] = {
        "DSP 31",
 };
 
+/* 1616(m) cardbus */
+
+static char *emu1616_src_texts[] = {
+       "Silence",
+       "Dock Mic A",
+       "Dock Mic B",
+       "Dock ADC1 Left",
+       "Dock ADC1 Right",
+       "Dock ADC2 Left",
+       "Dock ADC2 Right",
+       "Dock SPDIF Left",
+       "Dock SPDIF Right",
+       "ADAT 0",
+       "ADAT 1",
+       "ADAT 2",
+       "ADAT 3",
+       "ADAT 4",
+       "ADAT 5",
+       "ADAT 6",
+       "ADAT 7",
+       "DSP 0",
+       "DSP 1",
+       "DSP 2",
+       "DSP 3",
+       "DSP 4",
+       "DSP 5",
+       "DSP 6",
+       "DSP 7",
+       "DSP 8",
+       "DSP 9",
+       "DSP 10",
+       "DSP 11",
+       "DSP 12",
+       "DSP 13",
+       "DSP 14",
+       "DSP 15",
+       "DSP 16",
+       "DSP 17",
+       "DSP 18",
+       "DSP 19",
+       "DSP 20",
+       "DSP 21",
+       "DSP 22",
+       "DSP 23",
+       "DSP 24",
+       "DSP 25",
+       "DSP 26",
+       "DSP 27",
+       "DSP 28",
+       "DSP 29",
+       "DSP 30",
+       "DSP 31",
+};
+
+
 /*
  * List of data sources available for each destination
  */
@@ -199,6 +253,59 @@ static unsigned int emu1010_src_regs[] = {
        EMU_SRC_ALICE_EMU32B+0xf, /* 52 */
 };
 
+/* 1616(m) cardbus */
+static unsigned int emu1616_src_regs[] = {
+       EMU_SRC_SILENCE,
+       EMU_SRC_DOCK_MIC_A1,
+       EMU_SRC_DOCK_MIC_B1,
+       EMU_SRC_DOCK_ADC1_LEFT1,
+       EMU_SRC_DOCK_ADC1_RIGHT1,
+       EMU_SRC_DOCK_ADC2_LEFT1,
+       EMU_SRC_DOCK_ADC2_RIGHT1,
+       EMU_SRC_MDOCK_SPDIF_LEFT1,
+       EMU_SRC_MDOCK_SPDIF_RIGHT1,
+       EMU_SRC_MDOCK_ADAT,
+       EMU_SRC_MDOCK_ADAT+1,
+       EMU_SRC_MDOCK_ADAT+2,
+       EMU_SRC_MDOCK_ADAT+3,
+       EMU_SRC_MDOCK_ADAT+4,
+       EMU_SRC_MDOCK_ADAT+5,
+       EMU_SRC_MDOCK_ADAT+6,
+       EMU_SRC_MDOCK_ADAT+7,
+       EMU_SRC_ALICE_EMU32A,
+       EMU_SRC_ALICE_EMU32A+1,
+       EMU_SRC_ALICE_EMU32A+2,
+       EMU_SRC_ALICE_EMU32A+3,
+       EMU_SRC_ALICE_EMU32A+4,
+       EMU_SRC_ALICE_EMU32A+5,
+       EMU_SRC_ALICE_EMU32A+6,
+       EMU_SRC_ALICE_EMU32A+7,
+       EMU_SRC_ALICE_EMU32A+8,
+       EMU_SRC_ALICE_EMU32A+9,
+       EMU_SRC_ALICE_EMU32A+0xa,
+       EMU_SRC_ALICE_EMU32A+0xb,
+       EMU_SRC_ALICE_EMU32A+0xc,
+       EMU_SRC_ALICE_EMU32A+0xd,
+       EMU_SRC_ALICE_EMU32A+0xe,
+       EMU_SRC_ALICE_EMU32A+0xf,
+       EMU_SRC_ALICE_EMU32B,
+       EMU_SRC_ALICE_EMU32B+1,
+       EMU_SRC_ALICE_EMU32B+2,
+       EMU_SRC_ALICE_EMU32B+3,
+       EMU_SRC_ALICE_EMU32B+4,
+       EMU_SRC_ALICE_EMU32B+5,
+       EMU_SRC_ALICE_EMU32B+6,
+       EMU_SRC_ALICE_EMU32B+7,
+       EMU_SRC_ALICE_EMU32B+8,
+       EMU_SRC_ALICE_EMU32B+9,
+       EMU_SRC_ALICE_EMU32B+0xa,
+       EMU_SRC_ALICE_EMU32B+0xb,
+       EMU_SRC_ALICE_EMU32B+0xc,
+       EMU_SRC_ALICE_EMU32B+0xd,
+       EMU_SRC_ALICE_EMU32B+0xe,
+       EMU_SRC_ALICE_EMU32B+0xf,
+};
+
 /*
  * Data destinations - physical EMU outputs.
  * Each destination has an enum mixer control to choose a data source
@@ -230,6 +337,28 @@ static unsigned int emu1010_output_dst[] = {
        EMU_DST_HANA_ADAT+7, /* 23 */
 };
 
+/* 1616(m) cardbus */
+static unsigned int emu1616_output_dst[] = {
+       EMU_DST_DOCK_DAC1_LEFT1,
+       EMU_DST_DOCK_DAC1_RIGHT1,
+       EMU_DST_DOCK_DAC2_LEFT1,
+       EMU_DST_DOCK_DAC2_RIGHT1,
+       EMU_DST_DOCK_DAC3_LEFT1,
+       EMU_DST_DOCK_DAC3_RIGHT1,
+       EMU_DST_MDOCK_SPDIF_LEFT1,
+       EMU_DST_MDOCK_SPDIF_RIGHT1,
+       EMU_DST_MDOCK_ADAT,
+       EMU_DST_MDOCK_ADAT+1,
+       EMU_DST_MDOCK_ADAT+2,
+       EMU_DST_MDOCK_ADAT+3,
+       EMU_DST_MDOCK_ADAT+4,
+       EMU_DST_MDOCK_ADAT+5,
+       EMU_DST_MDOCK_ADAT+6,
+       EMU_DST_MDOCK_ADAT+7,
+       EMU_DST_MANA_DAC_LEFT,
+       EMU_DST_MANA_DAC_RIGHT,
+};
+
 /*
  * Data destinations - HANA outputs going to Alice2 (audigy) for
  *   capture (EMU32 + I2S links)
@@ -260,14 +389,26 @@ static unsigned int emu1010_input_dst[] = {
        EMU_DST_ALICE_I2S2_RIGHT,
 };
 
-static int snd_emu1010_input_output_source_info(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *uinfo)
+static int snd_emu1010_input_output_source_info(struct snd_kcontrol *kcontrol,
+                                               struct snd_ctl_elem_info *uinfo)
 {
+       struct snd_emu10k1 *emu = snd_kcontrol_chip(kcontrol);
+       char **items;
+
        uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED;
        uinfo->count = 1;
-       uinfo->value.enumerated.items = 53;
+       if (emu->card_capabilities->emu_model == EMU_MODEL_EMU1616) {
+               uinfo->value.enumerated.items = 49;
+               items = emu1616_src_texts;
+       } else {
+               uinfo->value.enumerated.items = 53;
+               items = emu1010_src_texts;
+       }
        if (uinfo->value.enumerated.item >= uinfo->value.enumerated.items)
-               uinfo->value.enumerated.item = uinfo->value.enumerated.items - 1;
-       strcpy(uinfo->value.enumerated.name, emu1010_src_texts[uinfo->value.enumerated.item]);
+               uinfo->value.enumerated.item =
+                       uinfo->value.enumerated.items - 1;
+       strcpy(uinfo->value.enumerated.name,
+              items[uinfo->value.enumerated.item]);
        return 0;
 }
 
@@ -279,7 +420,9 @@ static int snd_emu1010_output_source_get(struct snd_kcontrol *kcontrol,
 
        channel = (kcontrol->private_value) & 0xff;
        /* Limit: emu1010_output_dst, emu->emu1010.output_source */
-       if (channel >= 24)
+       if (channel >= 24 ||
+           (emu->card_capabilities->emu_model == EMU_MODEL_EMU1616 &&
+            channel >= 18))
                return -EINVAL;
        ucontrol->value.enumerated.item[0] = emu->emu1010.output_source[channel];
        return 0;
@@ -289,24 +432,30 @@ static int snd_emu1010_output_source_put(struct snd_kcontrol *kcontrol,
                                  struct snd_ctl_elem_value *ucontrol)
 {
        struct snd_emu10k1 *emu = snd_kcontrol_chip(kcontrol);
-       int change = 0;
        unsigned int val;
        unsigned int channel;
 
        val = ucontrol->value.enumerated.item[0];
-       if (val >= 53)
+       if (val >= 53 ||
+           (emu->card_capabilities->emu_model == EMU_MODEL_EMU1616 &&
+            val >= 49))
                return -EINVAL;
        channel = (kcontrol->private_value) & 0xff;
        /* Limit: emu1010_output_dst, emu->emu1010.output_source */
-       if (channel >= 24)
+       if (channel >= 24 ||
+           (emu->card_capabilities->emu_model == EMU_MODEL_EMU1616 &&
+            channel >= 18))
                return -EINVAL;
-       if (emu->emu1010.output_source[channel] != val) {
-               emu->emu1010.output_source[channel] = val;
-               change = 1;
+       if (emu->emu1010.output_source[channel] == val)
+               return 0;
+       emu->emu1010.output_source[channel] = val;
+       if (emu->card_capabilities->emu_model == EMU_MODEL_EMU1616)
+               snd_emu1010_fpga_link_dst_src_write(emu,
+                       emu1616_output_dst[channel], emu1616_src_regs[val]);
+       else
                snd_emu1010_fpga_link_dst_src_write(emu,
                        emu1010_output_dst[channel], emu1010_src_regs[val]);
-       }
-       return change;
+       return 1;
 }
 
 static int snd_emu1010_input_source_get(struct snd_kcontrol *kcontrol,
@@ -327,24 +476,28 @@ static int snd_emu1010_input_source_put(struct snd_kcontrol *kcontrol,
                                  struct snd_ctl_elem_value *ucontrol)
 {
        struct snd_emu10k1 *emu = snd_kcontrol_chip(kcontrol);
-       int change = 0;
        unsigned int val;
        unsigned int channel;
 
        val = ucontrol->value.enumerated.item[0];
-       if (val >= 53)
+       if (val >= 53 ||
+           (emu->card_capabilities->emu_model == EMU_MODEL_EMU1616 &&
+            val >= 49))
                return -EINVAL;
        channel = (kcontrol->private_value) & 0xff;
        /* Limit: emu1010_input_dst, emu->emu1010.input_source */
        if (channel >= 22)
                return -EINVAL;
-       if (emu->emu1010.input_source[channel] != val) {
-               emu->emu1010.input_source[channel] = val;
-               change = 1;
+       if (emu->emu1010.input_source[channel] == val)
+               return 0;
+       emu->emu1010.input_source[channel] = val;
+       if (emu->card_capabilities->emu_model == EMU_MODEL_EMU1616)
+               snd_emu1010_fpga_link_dst_src_write(emu,
+                       emu1010_input_dst[channel], emu1616_src_regs[val]);
+       else
                snd_emu1010_fpga_link_dst_src_write(emu,
                        emu1010_input_dst[channel], emu1010_src_regs[val]);
-       }
-       return change;
+       return 1;
 }
 
 #define EMU1010_SOURCE_OUTPUT(xname,chid) \
@@ -384,6 +537,30 @@ static struct snd_kcontrol_new snd_emu1010_output_enum_ctls[] __devinitdata = {
        EMU1010_SOURCE_OUTPUT("1010 ADAT 7 Playback Enum", 0x17),
 };
 
+
+/* 1616(m) cardbus */
+static struct snd_kcontrol_new snd_emu1616_output_enum_ctls[] __devinitdata = {
+       EMU1010_SOURCE_OUTPUT("Dock DAC1 Left Playback Enum", 0),
+       EMU1010_SOURCE_OUTPUT("Dock DAC1 Right Playback Enum", 1),
+       EMU1010_SOURCE_OUTPUT("Dock DAC2 Left Playback Enum", 2),
+       EMU1010_SOURCE_OUTPUT("Dock DAC2 Right Playback Enum", 3),
+       EMU1010_SOURCE_OUTPUT("Dock DAC3 Left Playback Enum", 4),
+       EMU1010_SOURCE_OUTPUT("Dock DAC3 Right Playback Enum", 5),
+       EMU1010_SOURCE_OUTPUT("Dock SPDIF Left Playback Enum", 6),
+       EMU1010_SOURCE_OUTPUT("Dock SPDIF Right Playback Enum", 7),
+       EMU1010_SOURCE_OUTPUT("Dock ADAT 0 Playback Enum", 8),
+       EMU1010_SOURCE_OUTPUT("Dock ADAT 1 Playback Enum", 9),
+       EMU1010_SOURCE_OUTPUT("Dock ADAT 2 Playback Enum", 0xa),
+       EMU1010_SOURCE_OUTPUT("Dock ADAT 3 Playback Enum", 0xb),
+       EMU1010_SOURCE_OUTPUT("Dock ADAT 4 Playback Enum", 0xc),
+       EMU1010_SOURCE_OUTPUT("Dock ADAT 5 Playback Enum", 0xd),
+       EMU1010_SOURCE_OUTPUT("Dock ADAT 6 Playback Enum", 0xe),
+       EMU1010_SOURCE_OUTPUT("Dock ADAT 7 Playback Enum", 0xf),
+       EMU1010_SOURCE_OUTPUT("Mana DAC Left Playback Enum", 0x10),
+       EMU1010_SOURCE_OUTPUT("Mana DAC Right Playback Enum", 0x11),
+};
+
+
 #define EMU1010_SOURCE_INPUT(xname,chid) \
 {                                                              \
        .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname,     \
@@ -1793,7 +1970,7 @@ int __devinit snd_emu10k1_mixer(struct snd_emu10k1 *emu,
                        return err;
        }
 
-       if ( emu->card_capabilities->emu1010) {
+       if (emu->card_capabilities->emu_model) {
                ;  /* Disable the snd_audigy_spdif_shared_spdif */
        } else if (emu->audigy) {
                if ((kctl = snd_ctl_new1(&snd_audigy_shared_spdif, emu)) == NULL)
@@ -1818,30 +1995,73 @@ int __devinit snd_emu10k1_mixer(struct snd_emu10k1 *emu,
                        return err;
        }
 
-       if ( emu->card_capabilities->emu1010) {
+       if (emu->card_capabilities->emu_model == EMU_MODEL_EMU1616) {
+               /* 1616(m) cardbus */
+               int i;
+
+               for (i = 0; i < ARRAY_SIZE(snd_emu1616_output_enum_ctls); i++) {
+                       err = snd_ctl_add(card,
+                               snd_ctl_new1(&snd_emu1616_output_enum_ctls[i],
+                                            emu));
+                       if (err < 0)
+                               return err;
+               }
+               for (i = 0; i < ARRAY_SIZE(snd_emu1010_input_enum_ctls); i++) {
+                       err = snd_ctl_add(card,
+                               snd_ctl_new1(&snd_emu1010_input_enum_ctls[i],
+                                            emu));
+                       if (err < 0)
+                               return err;
+               }
+               for (i = 0; i < ARRAY_SIZE(snd_emu1010_adc_pads) - 2; i++) {
+                       err = snd_ctl_add(card,
+                               snd_ctl_new1(&snd_emu1010_adc_pads[i], emu));
+                       if (err < 0)
+                               return err;
+               }
+               for (i = 0; i < ARRAY_SIZE(snd_emu1010_dac_pads) - 2; i++) {
+                       err = snd_ctl_add(card,
+                               snd_ctl_new1(&snd_emu1010_dac_pads[i], emu));
+                       if (err < 0)
+                               return err;
+               }
+               err = snd_ctl_add(card,
+                       snd_ctl_new1(&snd_emu1010_internal_clock, emu));
+               if (err < 0)
+                       return err;
+
+       } else if (emu->card_capabilities->emu_model) {
+               /* all other e-mu cards for now */
                int i;
 
                for (i = 0; i < ARRAY_SIZE(snd_emu1010_output_enum_ctls); i++) {
-                       err = snd_ctl_add(card, snd_ctl_new1(&snd_emu1010_output_enum_ctls[i], emu));
+                       err = snd_ctl_add(card,
+                               snd_ctl_new1(&snd_emu1010_output_enum_ctls[i],
+                                            emu));
                        if (err < 0)
                                return err;
                }
                for (i = 0; i < ARRAY_SIZE(snd_emu1010_input_enum_ctls); i++) {
-                       err = snd_ctl_add(card, snd_ctl_new1(&snd_emu1010_input_enum_ctls[i], emu));
+                       err = snd_ctl_add(card,
+                               snd_ctl_new1(&snd_emu1010_input_enum_ctls[i],
+                                            emu));
                        if (err < 0)
                                return err;
                }
                for (i = 0; i < ARRAY_SIZE(snd_emu1010_adc_pads); i++) {
-                       err = snd_ctl_add(card, snd_ctl_new1(&snd_emu1010_adc_pads[i], emu));
+                       err = snd_ctl_add(card,
+                               snd_ctl_new1(&snd_emu1010_adc_pads[i], emu));
                        if (err < 0)
                                return err;
                }
                for (i = 0; i < ARRAY_SIZE(snd_emu1010_dac_pads); i++) {
-                       err = snd_ctl_add(card, snd_ctl_new1(&snd_emu1010_dac_pads[i], emu));
+                       err = snd_ctl_add(card,
+                               snd_ctl_new1(&snd_emu1010_dac_pads[i], emu));
                        if (err < 0)
                                return err;
                }
-               err = snd_ctl_add(card, snd_ctl_new1(&snd_emu1010_internal_clock, emu));
+               err = snd_ctl_add(card,
+                       snd_ctl_new1(&snd_emu1010_internal_clock, emu));
                if (err < 0)
                        return err;
        }
index 04c7cf703531e26ef4744979f7b4a773a6cfc9fa..c4d76d16661eed2d5af5a49ec51e81cb22cef45c 100644 (file)
@@ -19,7 +19,6 @@
  *
  */
 
-#include <sound/driver.h>
 #include <linux/time.h>
 #include <linux/init.h>
 #include <sound/core.h>
index 5ce5befc701b9fa5a3bc7fccaf835513b2f7e072..cf9276ddad42dba9d3c91965f788124cd2d48990 100644 (file)
@@ -26,7 +26,6 @@
  *
  */
 
-#include <sound/driver.h>
 #include <linux/pci.h>
 #include <linux/delay.h>
 #include <linux/slab.h>
@@ -358,7 +357,7 @@ static void snd_emu10k1_pcm_init_voice(struct snd_emu10k1 *emu,
        snd_emu10k1_ptr_write(emu, PTRX, voice, (send_amount[0] << 8) | send_amount[1]);
        snd_emu10k1_ptr_write(emu, DSL, voice, end_addr | (send_amount[3] << 24));
        snd_emu10k1_ptr_write(emu, PSST, voice, start_addr | (send_amount[2] << 24));
-       if (emu->card_capabilities->emu1010)
+       if (emu->card_capabilities->emu_model)
                pitch_target = PITCH_48000; /* Disable interpolators on emu1010 card */
        else 
                pitch_target = emu10k1_calc_pitch_target(runtime->rate);
@@ -701,7 +700,7 @@ static void snd_emu10k1_playback_trigger_voice(struct snd_emu10k1 *emu, struct s
        voice = evoice->number;
 
        pitch = snd_emu10k1_rate_to_pitch(runtime->rate) >> 8;
-       if (emu->card_capabilities->emu1010)
+       if (emu->card_capabilities->emu_model)
                pitch_target = PITCH_48000; /* Disable interpolators on emu1010 card */
        else 
                pitch_target = emu10k1_calc_pitch_target(runtime->rate);
@@ -1232,7 +1231,7 @@ static int snd_emu10k1_capture_efx_open(struct snd_pcm_substream *substream)
        runtime->hw.rates = SNDRV_PCM_RATE_48000;
        runtime->hw.rate_min = runtime->hw.rate_max = 48000;
        spin_lock_irq(&emu->reg_lock);
-       if (emu->card_capabilities->emu1010) {
+       if (emu->card_capabilities->emu_model) {
                /*  Nb. of channels has been increased to 16 */
                /* TODO
                 * SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S32_LE
@@ -1791,7 +1790,7 @@ int __devinit snd_emu10k1_pcm_efx(struct snd_emu10k1 * emu, int device, struct s
        /* emu->efx_voices_mask[0] = FXWC_DEFAULTROUTE_C | FXWC_DEFAULTROUTE_A; */
        if (emu->audigy) {
                emu->efx_voices_mask[0] = 0;
-               if (emu->card_capabilities->emu1010)
+               if (emu->card_capabilities->emu_model)
                        /* Pavel Hofman - 32 voices will be used for
                         * capture (write mode) -
                         * each bit = corresponding voice
index c3fb10e81c9e0d4e6e52359ce3f27bc066494ea3..f3caa3f890c6cdd5fc3b237afa4c5c75269af1d5 100644 (file)
@@ -28,7 +28,6 @@
  *
  */
 
-#include <sound/driver.h>
 #include <linux/slab.h>
 #include <linux/init.h>
 #include <sound/core.h>
@@ -245,7 +244,7 @@ static void snd_emu10k1_proc_spdif_read(struct snd_info_entry *entry,
        unsigned long flags;
        u32 rate;
 
-       if (emu->card_capabilities->emu1010) {
+       if (emu->card_capabilities->emu_model) {
                spin_lock_irqsave(&emu->emu_lock, flags);
                snd_emu1010_fpga_read(emu, 0x38, &value);
                spin_unlock_irqrestore(&emu->emu_lock, flags);
@@ -585,7 +584,7 @@ int __devinit snd_emu10k1_proc_init(struct snd_emu10k1 * emu)
 {
        struct snd_info_entry *entry;
 #ifdef CONFIG_SND_DEBUG
-       if (emu->card_capabilities->emu1010) {
+       if (emu->card_capabilities->emu_model) {
                if (! snd_card_proc_new(emu->card, "emu1010_regs", &entry)) 
                        snd_info_set_text_ops(entry, emu, snd_emu_proc_emu1010_reg_read);
        }
index 6702c15fefa3b52cbc95fc868ce278a161e9e219..b5a802bdeb7c1b21f76573e72bbd16fd2af74898 100644 (file)
@@ -25,7 +25,6 @@
  *
  */
 
-#include <sound/driver.h>
 #include <linux/time.h>
 #include <sound/core.h>
 #include <sound/emu10k1.h>
@@ -71,6 +70,11 @@ void snd_emu10k1_ptr_write(struct snd_emu10k1 *emu, unsigned int reg, unsigned i
        unsigned long flags;
        unsigned int mask;
 
+       if (!emu) {
+               snd_printk(KERN_ERR "ptr_write: emu is null!\n");
+               dump_stack();
+               return;
+       }
        mask = emu->audigy ? A_PTR_ADDRESS_MASK : PTR_ADDRESS_MASK;
        regptr = ((reg << 16) & mask) | (chn & PTR_CHANNELNUM_MASK);
 
@@ -135,15 +139,23 @@ int snd_emu10k1_spi_write(struct snd_emu10k1 * emu,
        unsigned int reset, set;
        unsigned int reg, tmp;
        int n, result;
+       int err = 0;
+
+       /* This function is not re-entrant, so protect against it. */
+       spin_lock(&emu->spi_lock);
        if (emu->card_capabilities->ca0108_chip)
                reg = 0x3c; /* PTR20, reg 0x3c */
        else {
                /* For other chip types the SPI register
                 * is currently unknown. */
-               return 1;
+               err = 1;
+               goto spi_write_exit;
+       }
+       if (data > 0xffff) {
+               /* Only 16bit values allowed */
+               err = 1;
+               goto spi_write_exit;
        }
-       if (data > 0xffff) /* Only 16bit values allowed */
-               return 1;
 
        tmp = snd_emu10k1_ptr20_read(emu, reg, 0);
        reset = (tmp & ~0x3ffff) | 0x20000; /* Set xxx20000 */
@@ -161,11 +173,17 @@ int snd_emu10k1_spi_write(struct snd_emu10k1 * emu,
                        break;
                }
        }
-       if (result) /* Timed out */
-               return 1;
+       if (result) {
+               /* Timed out */
+               err = 1;
+               goto spi_write_exit;
+       }
        snd_emu10k1_ptr20_write(emu, reg, 0, reset | data);
        tmp = snd_emu10k1_ptr20_read(emu, reg, 0); /* Write post */
-       return 0;
+       err = 0;
+spi_write_exit:
+       spin_unlock(&emu->spi_lock);
+       return err;
 }
 
 /* The ADC does not support i2c read, so only write is implemented */
@@ -177,15 +195,17 @@ int snd_emu10k1_i2c_write(struct snd_emu10k1 *emu,
        int timeout = 0;
        int status;
        int retry;
+       int err = 0;
+
        if ((reg > 0x7f) || (value > 0x1ff)) {
                snd_printk(KERN_ERR "i2c_write: invalid values.\n");
                return -EINVAL;
        }
 
+       /* This function is not re-entrant, so protect against it. */
+       spin_lock(&emu->i2c_lock);
+
        tmp = reg << 25 | value << 16;
-       // snd_printk("I2C-write:reg=0x%x, value=0x%x\n", reg, value);
-       /* Not sure what this I2C channel controls. */
-       /* snd_emu10k1_ptr_write(emu, P17V_I2C_0, 0, tmp); */
 
        /* This controls the I2C connected to the WM8775 ADC Codec */
        snd_emu10k1_ptr20_write(emu, P17V_I2C_1, 0, tmp);
@@ -193,17 +213,14 @@ int snd_emu10k1_i2c_write(struct snd_emu10k1 *emu,
 
        for (retry = 0; retry < 10; retry++) {
                /* Send the data to i2c */
-               //tmp = snd_emu10k1_ptr_read(emu, P17V_I2C_ADDR, 0);
-               //tmp = tmp & ~(I2C_A_ADC_READ|I2C_A_ADC_LAST|I2C_A_ADC_START|I2C_A_ADC_ADD_MASK);
                tmp = 0;
                tmp = tmp | (I2C_A_ADC_LAST|I2C_A_ADC_START|I2C_A_ADC_ADD);
                snd_emu10k1_ptr20_write(emu, P17V_I2C_ADDR, 0, tmp);
 
                /* Wait till the transaction ends */
                while (1) {
-                       udelay(10);
+                       mdelay(1);
                        status = snd_emu10k1_ptr20_read(emu, P17V_I2C_ADDR, 0);
-                       // snd_printk("I2C:status=0x%x\n", status);
                        timeout++;
                        if ((status & I2C_A_ADC_START) == 0)
                                break;
@@ -220,19 +237,26 @@ int snd_emu10k1_i2c_write(struct snd_emu10k1 *emu,
 
        if (retry == 10) {
                snd_printk(KERN_ERR "Writing to ADC failed!\n");
-               return -EINVAL;
+               snd_printk(KERN_ERR "status=0x%x, reg=%d, value=%d\n",
+                       status, reg, value);
+               /* dump_stack(); */
+               err = -EINVAL;
        }
     
-       return 0;
+       spin_unlock(&emu->i2c_lock);
+       return err;
 }
 
 int snd_emu1010_fpga_write(struct snd_emu10k1 * emu, u32 reg, u32 value)
 {
+       unsigned long flags;
+
        if (reg > 0x3f)
                return 1;
        reg += 0x40; /* 0x40 upwards are registers. */
        if (value < 0 || value > 0x3f) /* 0 to 0x3f are values */
                return 1;
+       spin_lock_irqsave(&emu->emu_lock, flags);
        outl(reg, emu->port + A_IOCFG);
        udelay(10);
        outl(reg | 0x80, emu->port + A_IOCFG);  /* High bit clocks the value into the fpga. */
@@ -240,20 +264,24 @@ int snd_emu1010_fpga_write(struct snd_emu10k1 * emu, u32 reg, u32 value)
        outl(value, emu->port + A_IOCFG);
        udelay(10);
        outl(value | 0x80 , emu->port + A_IOCFG);  /* High bit clocks the value into the fpga. */
+       spin_unlock_irqrestore(&emu->emu_lock, flags);
 
        return 0;
 }
 
 int snd_emu1010_fpga_read(struct snd_emu10k1 * emu, u32 reg, u32 *value)
 {
+       unsigned long flags;
        if (reg > 0x3f)
                return 1;
        reg += 0x40; /* 0x40 upwards are registers. */
+       spin_lock_irqsave(&emu->emu_lock, flags);
        outl(reg, emu->port + A_IOCFG);
        udelay(10);
        outl(reg | 0x80, emu->port + A_IOCFG);  /* High bit clocks the value into the fpga. */
        udelay(10);
        *value = ((inl(emu->port + A_IOCFG) >> 8) & 0x7f);
+       spin_unlock_irqrestore(&emu->emu_lock, flags);
 
        return 0;
 }
index 3c114b45e0b28b0bbc1b9b335e514d7d9ebbdcfc..30bfed6f83398d5f9b17fdf776ce2ab6ebac7a4e 100644 (file)
@@ -25,7 +25,6 @@
  *
  */
 
-#include <sound/driver.h>
 #include <linux/time.h>
 #include <sound/core.h>
 #include <sound/emu10k1.h>
@@ -35,9 +34,10 @@ irqreturn_t snd_emu10k1_interrupt(int irq, void *dev_id)
        struct snd_emu10k1 *emu = dev_id;
        unsigned int status, status2, orig_status, orig_status2;
        int handled = 0;
+       int timeout = 0;
 
-       while ((status = inl(emu->port + IPR)) != 0) {
-               //snd_printk(KERN_INFO "emu10k1 irq - status = 0x%x\n", status);
+       while (((status = inl(emu->port + IPR)) != 0) && (timeout < 1000)) {
+               timeout++;
                orig_status = status;
                handled = 1;
                if ((status & 0xffffffff) == 0xffffffff) {
@@ -201,5 +201,8 @@ irqreturn_t snd_emu10k1_interrupt(int irq, void *dev_id)
                }
                outl(orig_status, emu->port + IPR); /* ack all */
        }
+       if (timeout == 1000)
+               snd_printk(KERN_INFO "emu10k1 irq routine failure\n");
+
        return IRQ_RETVAL(handled);
 }
index 48097c6bb15c20eb679cc23f17c8a1763a2cde79..916c1dbcd53cf919f6a420b86f893a676115302e 100644 (file)
@@ -21,7 +21,6 @@
  *
  */
 
-#include <sound/driver.h>
 #include <linux/pci.h>
 #include <linux/time.h>
 #include <linux/mutex.h>
index 9fd3135f3118daf4631dfeb546aa1b873ce3dab6..749a21b6bd06bf9b49efed57c071560a13347ada 100644 (file)
@@ -87,7 +87,6 @@
  *   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA
  *
  */
-#include <sound/driver.h>
 #include <linux/delay.h>
 #include <linux/init.h>
 #include <linux/interrupt.h>
index 6295b2dca785d59533c25268cc80e120094d488a..72321e946cccf6661810559c66a4f3cf69c30ba5 100644 (file)
@@ -25,7 +25,6 @@
  *
  */
 
-#include <sound/driver.h>
 #include <linux/time.h>
 #include <sound/core.h>
 #include <sound/emu10k1.h>
index 04fa8492abb048855b170ed63676cfecda2030e4..958cb2a65a4ec8f79680f1bd8ed2a3570bfd2273 100644 (file)
@@ -28,7 +28,6 @@
  *
  */
 
-#include <sound/driver.h>
 #include <linux/time.h>
 #include <sound/core.h>
 #include <sound/emu10k1.h>
index b958f869cb138b4a62a69fa35b88a9a55d392df5..72d85a5ae6a090f53b7a8b9983306d710be65d9d 100644 (file)
@@ -26,7 +26,6 @@
  * by Kurt J. Bosch
  */
 
-#include <sound/driver.h>
 #include <asm/io.h>
 #include <linux/delay.h>
 #include <linux/interrupt.h>
index fb25abe68a020a7eff6b8ba9f52e950b38222bf5..1a314fa99c45889148c07b3d95609496b2e19c76 100644 (file)
@@ -47,7 +47,6 @@
 */
 
 
-#include <sound/driver.h>
 #include <linux/init.h>
 #include <linux/interrupt.h>
 #include <linux/pci.h>
@@ -227,6 +226,7 @@ struct es1938 {
        unsigned int dma2_start;
        unsigned int dma1_shift;
        unsigned int dma2_shift;
+       unsigned int last_capture_dmaaddr;
        unsigned int active;
 
        spinlock_t reg_lock;
@@ -529,6 +529,7 @@ static void snd_es1938_capture_setdma(struct es1938 *chip)
        outb(1, SLDM_REG(chip, DMAMASK));
        outb(0x14, SLDM_REG(chip, DMAMODE));
        outl(chip->dma1_start, SLDM_REG(chip, DMAADDR));
+       chip->last_capture_dmaaddr = chip->dma1_start;
        outw(chip->dma1_size - 1, SLDM_REG(chip, DMACOUNT));
        /* 3. Unmask DMA */
        outb(0, SLDM_REG(chip, DMAMASK));
@@ -770,19 +771,40 @@ static int snd_es1938_playback_prepare(struct snd_pcm_substream *substream)
        return -EINVAL;
 }
 
+/* during the incrementing of dma counters the DMA register reads sometimes
+   returns garbage. To ensure a valid hw pointer, the following checks which
+   should be very unlikely to fail are used:
+   - is the current DMA address in the valid DMA range ?
+   - is the sum of DMA address and DMA counter pointing to the last DMA byte ?
+   One can argue this could differ by one byte depending on which register is
+   updated first, so the implementation below allows for that.
+*/
 static snd_pcm_uframes_t snd_es1938_capture_pointer(struct snd_pcm_substream *substream)
 {
        struct es1938 *chip = snd_pcm_substream_chip(substream);
        size_t ptr;
+#if 0
        size_t old, new;
-#if 1
        /* This stuff is *needed*, don't ask why - AB */
        old = inw(SLDM_REG(chip, DMACOUNT));
        while ((new = inw(SLDM_REG(chip, DMACOUNT))) != old)
                old = new;
        ptr = chip->dma1_size - 1 - new;
 #else
-       ptr = inl(SLDM_REG(chip, DMAADDR)) - chip->dma1_start;
+       size_t count;
+       unsigned int diff;
+
+       ptr = inl(SLDM_REG(chip, DMAADDR));
+       count = inw(SLDM_REG(chip, DMACOUNT));
+       diff = chip->dma1_start + chip->dma1_size - ptr - count;
+
+       if (diff > 3 || ptr < chip->dma1_start
+             || ptr >= chip->dma1_start+chip->dma1_size)
+         ptr = chip->last_capture_dmaaddr;            /* bad, use last saved */
+       else
+         chip->last_capture_dmaaddr = ptr;            /* good, remember it */
+
+       ptr -= chip->dma1_start;
 #endif
        return ptr >> chip->dma1_shift;
 }
index d69b11d1f993a0660e385ac28f5c68d3ca994fab..25ccfce45759806a21c8f13d11d4fc192cdf85a5 100644 (file)
@@ -94,7 +94,6 @@
  *     places.
  */
 
-#include <sound/driver.h>
 #include <asm/io.h>
 #include <linux/delay.h>
 #include <linux/interrupt.h>
index 9939109f05a2ac819898b87a72e4711198737ab0..4c300e6149fca554891ae85eb2685caf36d0b23b 100644 (file)
@@ -20,7 +20,6 @@
  *
  */
 
-#include <sound/driver.h>
 #include <linux/delay.h>
 #include <linux/init.h>
 #include <linux/interrupt.h>
@@ -979,6 +978,27 @@ static unsigned int snd_fm801_tea575x_64pcr_read(struct snd_tea575x *tea)
        return val;
 }
 
+static void snd_fm801_tea575x_64pcr_mute(struct snd_tea575x *tea,
+                                         unsigned int mute)
+{
+       struct fm801 *chip = tea->private_data;
+       unsigned short reg;
+
+       spin_lock_irq(&chip->reg_lock);
+
+       reg = inw(FM801_REG(chip, GPIO_CTRL));
+       if (mute)
+               /* 0xf800 (mute) */
+               reg &= ~FM801_GPIO_GP(TEA_64PCR_WRITE_ENABLE);
+       else
+               /* 0xf802 (unmute) */
+               reg |= FM801_GPIO_GP(TEA_64PCR_WRITE_ENABLE);
+       outw(reg, FM801_REG(chip, GPIO_CTRL));
+       udelay(1);
+
+       spin_unlock_irq(&chip->reg_lock);
+}
+
 static struct snd_tea575x_ops snd_fm801_tea_ops[3] = {
        {
                /* 1 = MediaForte 256-PCS */
@@ -994,6 +1014,7 @@ static struct snd_tea575x_ops snd_fm801_tea_ops[3] = {
                /* 3 = MediaForte 64-PCR */
                .write = snd_fm801_tea575x_64pcr_write,
                .read = snd_fm801_tea575x_64pcr_read,
+               .mute = snd_fm801_tea575x_64pcr_mute,
        }
 };
 #endif
index ab0c726d648e77e2ea4477b285407bce37b8a7d3..9e0d8a1268aa4400cca30e5c881576ccb0ca28d9 100644 (file)
@@ -2,7 +2,7 @@ snd-hda-intel-y := hda_intel.o
 # since snd-hda-intel is the only driver using hda-codec,
 # merge it into a single module although it was originally
 # designed to be individual modules
-snd-hda-intel-y += hda_codec.o
+snd-hda-intel-y += hda_codec.o vmaster.o
 snd-hda-intel-$(CONFIG_PROC_FS) += hda_proc.o
 snd-hda-intel-$(CONFIG_SND_HDA_HWDEP) += hda_hwdep.o
 snd-hda-intel-$(CONFIG_SND_HDA_GENERIC) += hda_generic.o
index 8cbe3bf1e3170afb798fad92beb5579378877459..26812dc2b7f22c56a9c8c506863696ddf8f91b52 100644 (file)
@@ -19,7 +19,6 @@
  *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA
  */
 
-#include <sound/driver.h>
 #include <linux/init.h>
 #include <linux/delay.h>
 #include <linux/slab.h>
@@ -55,6 +54,7 @@ static struct hda_vendor_id hda_vendor_ids[] = {
        { 0x10ec, "Realtek" },
        { 0x1057, "Motorola" },
        { 0x1106, "VIA" },
+       { 0x111d, "IDT" },
        { 0x11d4, "Analog Devices" },
        { 0x13f6, "C-Media" },
        { 0x14f1, "Conexant" },
@@ -429,6 +429,10 @@ find_codec_preset(struct hda_codec *codec)
        for (tbl = hda_preset_tables; *tbl; tbl++) {
                for (preset = *tbl; preset->id; preset++) {
                        u32 mask = preset->mask;
+                       if (preset->afg && preset->afg != codec->afg)
+                               continue;
+                       if (preset->mfg && preset->mfg != codec->mfg)
+                               continue;
                        if (!mask)
                                mask = ~0;
                        if (preset->id == (codec->vendor_id & mask) &&
@@ -765,7 +769,7 @@ get_alloc_amp_hash(struct hda_codec *codec, u32 key)
 /*
  * query AMP capabilities for the given widget and direction
  */
-static u32 query_amp_caps(struct hda_codec *codec, hda_nid_t nid, int direction)
+u32 query_amp_caps(struct hda_codec *codec, hda_nid_t nid, int direction)
 {
        struct hda_amp_info *info;
 
@@ -933,7 +937,8 @@ int snd_hda_mixer_amp_volume_info(struct snd_kcontrol *kcontrol,
        caps = (caps & AC_AMPCAP_NUM_STEPS) >> AC_AMPCAP_NUM_STEPS_SHIFT;
        if (!caps) {
                printk(KERN_WARNING "hda_codec: "
-                      "num_steps = 0 for NID=0x%x\n", nid);
+                      "num_steps = 0 for NID=0x%x (ctl = %s)\n", nid,
+                      kcontrol->id.name);
                return -EINVAL;
        }
        uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
@@ -1012,6 +1017,66 @@ int snd_hda_mixer_amp_tlv(struct snd_kcontrol *kcontrol, int op_flag,
        return 0;
 }
 
+/*
+ * set (static) TLV for virtual master volume; recalculated as max 0dB
+ */
+void snd_hda_set_vmaster_tlv(struct hda_codec *codec, hda_nid_t nid, int dir,
+                            unsigned int *tlv)
+{
+       u32 caps;
+       int nums, step;
+
+       caps = query_amp_caps(codec, nid, dir);
+       nums = (caps & AC_AMPCAP_NUM_STEPS) >> AC_AMPCAP_NUM_STEPS_SHIFT;
+       step = (caps & AC_AMPCAP_STEP_SIZE) >> AC_AMPCAP_STEP_SIZE_SHIFT;
+       step = (step + 1) * 25;
+       tlv[0] = SNDRV_CTL_TLVT_DB_SCALE;
+       tlv[1] = 2 * sizeof(unsigned int);
+       tlv[2] = -nums * step;
+       tlv[3] = step;
+}
+
+/* find a mixer control element with the given name */
+struct snd_kcontrol *snd_hda_find_mixer_ctl(struct hda_codec *codec,
+                                           const char *name)
+{
+       struct snd_ctl_elem_id id;
+       memset(&id, 0, sizeof(id));
+       id.iface = SNDRV_CTL_ELEM_IFACE_MIXER;
+       strcpy(id.name, name);
+       return snd_ctl_find_id(codec->bus->card, &id);
+}
+
+/* create a virtual master control and add slaves */
+int snd_hda_add_vmaster(struct hda_codec *codec, char *name,
+                       unsigned int *tlv, const char **slaves)
+{
+       struct snd_kcontrol *kctl;
+       const char **s;
+       int err;
+
+       kctl = snd_ctl_make_virtual_master(name, tlv);
+       if (!kctl)
+               return -ENOMEM;
+       err = snd_ctl_add(codec->bus->card, kctl);
+       if (err < 0)
+               return err;
+       
+       for (s = slaves; *s; s++) {
+               struct snd_kcontrol *sctl;
+
+               sctl = snd_hda_find_mixer_ctl(codec, *s);
+               if (!sctl) {
+                       snd_printdd("Cannot find slave %s, skipped\n", *s);
+                       continue;
+               }
+               err = snd_ctl_add_slave(kctl, sctl);
+               if (err < 0)
+                       return err;
+       }
+       return 0;
+}
+
 /* switch */
 int snd_hda_mixer_amp_switch_info(struct snd_kcontrol *kcontrol,
                                  struct snd_ctl_elem_info *uinfo)
@@ -1434,7 +1499,8 @@ int snd_hda_create_spdif_out_ctls(struct hda_codec *codec, hda_nid_t nid)
                        return err;
        }
        codec->spdif_ctls =
-               snd_hda_codec_read(codec, nid, 0, AC_VERB_GET_DIGI_CONVERT, 0);
+               snd_hda_codec_read(codec, nid, 0,
+                                  AC_VERB_GET_DIGI_CONVERT_1, 0);
        codec->spdif_status = convert_to_spdif_status(codec->spdif_ctls);
        return 0;
 }
@@ -1481,7 +1547,7 @@ static int snd_hda_spdif_in_status_get(struct snd_kcontrol *kcontrol,
        unsigned short val;
        unsigned int sbits;
 
-       val = snd_hda_codec_read(codec, nid, 0, AC_VERB_GET_DIGI_CONVERT, 0);
+       val = snd_hda_codec_read(codec, nid, 0, AC_VERB_GET_DIGI_CONVERT_1, 0);
        sbits = convert_to_spdif_status(val);
        ucontrol->value.iec958.status[0] = sbits;
        ucontrol->value.iec958.status[1] = sbits >> 8;
@@ -1532,7 +1598,8 @@ int snd_hda_create_spdif_in_ctls(struct hda_codec *codec, hda_nid_t nid)
                        return err;
        }
        codec->spdif_in_enable =
-               snd_hda_codec_read(codec, nid, 0, AC_VERB_GET_DIGI_CONVERT, 0) &
+               snd_hda_codec_read(codec, nid, 0,
+                                  AC_VERB_GET_DIGI_CONVERT_1, 0) &
                AC_DIG1_ENABLE;
        return 0;
 }
@@ -1622,6 +1689,7 @@ static void hda_set_power_state(struct hda_codec *codec, hda_nid_t fg,
 
        snd_hda_codec_write(codec, fg, 0, AC_VERB_SET_POWER_STATE,
                            power_state);
+       msleep(10); /* partial workaround for "azx_get_response timeout" */
 
        nid = codec->start_nid;
        for (i = 0; i < codec->num_nodes; i++, nid++) {
@@ -2336,7 +2404,8 @@ int snd_hda_ch_mode_put(struct hda_codec *codec,
        unsigned int mode;
 
        mode = ucontrol->value.enumerated.item[0];
-       snd_assert(mode < num_chmodes, return -EINVAL);
+       if (mode >= num_chmodes)
+               return -EINVAL;
        if (*max_channelsp == chmode[mode].channels)
                return 0;
        /* change the current channel setting */
@@ -2602,20 +2671,21 @@ int snd_hda_parse_pin_def_config(struct hda_codec *codec,
                                 struct auto_pin_cfg *cfg,
                                 hda_nid_t *ignore_nids)
 {
-       hda_nid_t nid, nid_start;
-       int nodes;
+       hda_nid_t nid, end_nid;
        short seq, assoc_line_out, assoc_speaker;
        short sequences_line_out[ARRAY_SIZE(cfg->line_out_pins)];
        short sequences_speaker[ARRAY_SIZE(cfg->speaker_pins)];
+       short sequences_hp[ARRAY_SIZE(cfg->hp_pins)];
 
        memset(cfg, 0, sizeof(*cfg));
 
        memset(sequences_line_out, 0, sizeof(sequences_line_out));
        memset(sequences_speaker, 0, sizeof(sequences_speaker));
+       memset(sequences_hp, 0, sizeof(sequences_hp));
        assoc_line_out = assoc_speaker = 0;
 
-       nodes = snd_hda_get_sub_nodes(codec, codec->afg, &nid_start);
-       for (nid = nid_start; nid < nodes + nid_start; nid++) {
+       end_nid = codec->start_nid + codec->num_nodes;
+       for (nid = codec->start_nid; nid < end_nid; nid++) {
                unsigned int wid_caps = get_wcaps(codec, nid);
                unsigned int wid_type =
                        (wid_caps & AC_WCAP_TYPE) >> AC_WCAP_TYPE_SHIFT;
@@ -2638,6 +2708,10 @@ int snd_hda_parse_pin_def_config(struct hda_codec *codec,
                case AC_JACK_LINE_OUT:
                        seq = get_defcfg_sequence(def_conf);
                        assoc = get_defcfg_association(def_conf);
+
+                       if (!(wid_caps & AC_WCAP_STEREO))
+                               if (!cfg->mono_out_pin)
+                                       cfg->mono_out_pin = nid;
                        if (!assoc)
                                continue;
                        if (!assoc_line_out)
@@ -2666,9 +2740,12 @@ int snd_hda_parse_pin_def_config(struct hda_codec *codec,
                        cfg->speaker_outs++;
                        break;
                case AC_JACK_HP_OUT:
+                       seq = get_defcfg_sequence(def_conf);
+                       assoc = get_defcfg_association(def_conf);
                        if (cfg->hp_outs >= ARRAY_SIZE(cfg->hp_pins))
                                continue;
                        cfg->hp_pins[cfg->hp_outs] = nid;
+                       sequences_hp[cfg->hp_outs] = (assoc << 4) | seq;
                        cfg->hp_outs++;
                        break;
                case AC_JACK_MIC_IN: {
@@ -2712,7 +2789,24 @@ int snd_hda_parse_pin_def_config(struct hda_codec *codec,
                              cfg->line_outs);
        sort_pins_by_sequence(cfg->speaker_pins, sequences_speaker,
                              cfg->speaker_outs);
+       sort_pins_by_sequence(cfg->hp_pins, sequences_hp,
+                             cfg->hp_outs);
        
+       /* if we have only one mic, make it AUTO_PIN_MIC */
+       if (!cfg->input_pins[AUTO_PIN_MIC] &&
+           cfg->input_pins[AUTO_PIN_FRONT_MIC]) {
+               cfg->input_pins[AUTO_PIN_MIC] =
+                       cfg->input_pins[AUTO_PIN_FRONT_MIC];
+               cfg->input_pins[AUTO_PIN_FRONT_MIC] = 0;
+       }
+       /* ditto for line-in */
+       if (!cfg->input_pins[AUTO_PIN_LINE] &&
+           cfg->input_pins[AUTO_PIN_FRONT_LINE]) {
+               cfg->input_pins[AUTO_PIN_LINE] =
+                       cfg->input_pins[AUTO_PIN_FRONT_LINE];
+               cfg->input_pins[AUTO_PIN_FRONT_LINE] = 0;
+       }
+
        /*
         * FIX-UP: if no line-outs are detected, try to use speaker or HP pin
         * as a primary output
@@ -2766,6 +2860,7 @@ int snd_hda_parse_pin_def_config(struct hda_codec *codec,
                   cfg->hp_outs, cfg->hp_pins[0],
                   cfg->hp_pins[1], cfg->hp_pins[2],
                   cfg->hp_pins[3], cfg->hp_pins[4]);
+       snd_printd("   mono: mono_out=0x%x\n", cfg->mono_out_pin);
        snd_printd("   inputs: mic=0x%x, fmic=0x%x, line=0x%x, fline=0x%x,"
                   " cd=0x%x, aux=0x%x\n",
                   cfg->input_pins[AUTO_PIN_MIC],
index 2bce925d84ef5b0412f67320d65f732bd06e5aa7..f14871151be983a307330ec9c61009895a22f944 100644 (file)
@@ -77,12 +77,16 @@ enum {
 #define AC_VERB_GET_PIN_SENSE                  0x0f09
 #define AC_VERB_GET_BEEP_CONTROL               0x0f0a
 #define AC_VERB_GET_EAPD_BTLENABLE             0x0f0c
-#define AC_VERB_GET_DIGI_CONVERT               0x0f0d
+#define AC_VERB_GET_DIGI_CONVERT_1             0x0f0d
+#define AC_VERB_GET_DIGI_CONVERT_2             0x0f0e
 #define AC_VERB_GET_VOLUME_KNOB_CONTROL                0x0f0f
 /* f10-f1a: GPIO */
 #define AC_VERB_GET_GPIO_DATA                  0x0f15
 #define AC_VERB_GET_GPIO_MASK                  0x0f16
 #define AC_VERB_GET_GPIO_DIRECTION             0x0f17
+#define AC_VERB_GET_GPIO_WAKE_MASK             0x0f18
+#define AC_VERB_GET_GPIO_UNSOLICITED_RSP_MASK  0x0f19
+#define AC_VERB_GET_GPIO_STICKY_MASK           0x0f1a
 #define AC_VERB_GET_CONFIG_DEFAULT             0x0f1c
 /* f20: AFG/MFG */
 #define AC_VERB_GET_SUBSYSTEM_ID               0x0f20
@@ -110,6 +114,9 @@ enum {
 #define AC_VERB_SET_GPIO_DATA                  0x715
 #define AC_VERB_SET_GPIO_MASK                  0x716
 #define AC_VERB_SET_GPIO_DIRECTION             0x717
+#define AC_VERB_SET_GPIO_WAKE_MASK             0x718
+#define AC_VERB_SET_GPIO_UNSOLICITED_RSP_MASK  0x719
+#define AC_VERB_SET_GPIO_STICKY_MASK           0x71a
 #define AC_VERB_SET_CONFIG_DEFAULT_BYTES_0     0x71c
 #define AC_VERB_SET_CONFIG_DEFAULT_BYTES_1     0x71d
 #define AC_VERB_SET_CONFIG_DEFAULT_BYTES_2     0x71e
@@ -135,6 +142,7 @@ enum {
 #define AC_PAR_PROC_CAP                        0x10
 #define AC_PAR_GPIO_CAP                        0x11
 #define AC_PAR_AMP_OUT_CAP             0x12
+#define AC_PAR_VOL_KNB_CAP             0x13
 
 /*
  * AC_VERB_PARAMETERS results (32bit)
@@ -181,6 +189,27 @@ enum {
 #define AC_SUPFMT_FLOAT32              (1<<1)
 #define AC_SUPFMT_AC3                  (1<<2)
 
+/* GP I/O count */
+#define AC_GPIO_IO_COUNT               (0xff<<0)
+#define AC_GPIO_O_COUNT                        (0xff<<8)
+#define AC_GPIO_O_COUNT_SHIFT          8
+#define AC_GPIO_I_COUNT                        (0xff<<16)
+#define AC_GPIO_I_COUNT_SHIFT          16
+#define AC_GPIO_UNSOLICITED            (1<<30)
+#define AC_GPIO_WAKE                   (1<<31)
+
+/* Converter stream, channel */
+#define AC_CONV_CHANNEL                        (0xf<<0)
+#define AC_CONV_STREAM                 (0xf<<4)
+#define AC_CONV_STREAM_SHIFT           4
+
+/* Input converter SDI select */
+#define AC_SDI_SELECT                  (0xf<<0)
+
+/* Unsolicited response */
+#define AC_UNSOL_TAG                   (0x3f<<0)
+#define AC_UNSOL_ENABLED               (1<<7)
+
 /* Pin widget capabilies */
 #define AC_PINCAP_IMP_SENSE            (1<<0)  /* impedance sense capable */
 #define AC_PINCAP_TRIG_REQ             (1<<1)  /* trigger required */
@@ -189,6 +218,10 @@ enum {
 #define AC_PINCAP_OUT                  (1<<4)  /* output capable */
 #define AC_PINCAP_IN                   (1<<5)  /* input capable */
 #define AC_PINCAP_BALANCE              (1<<6)  /* balanced I/O capable */
+/* Note: This LR_SWAP pincap is defined in the Realtek ALC883 specification,
+ *       but is marked reserved in the Intel HDA specification.
+ */
+#define AC_PINCAP_LR_SWAP              (1<<7)  /* L/R swap */
 #define AC_PINCAP_VREF                 (0x37<<8)
 #define AC_PINCAP_VREF_SHIFT           8
 #define AC_PINCAP_EAPD                 (1<<16) /* EAPD capable */
@@ -222,6 +255,9 @@ enum {
 #define AC_PWRST_D3SUP                 (1<<3)
 
 /* Power state values */
+#define AC_PWRST_SETTING               (0xf<<0)
+#define AC_PWRST_ACTUAL                        (0xf<<4)
+#define AC_PWRST_ACTUAL_SHIFT          4
 #define AC_PWRST_D0                    0x00
 #define AC_PWRST_D1                    0x01
 #define AC_PWRST_D2                    0x02
@@ -230,10 +266,11 @@ enum {
 /* Processing capabilies */
 #define AC_PCAP_BENIGN                 (1<<0)
 #define AC_PCAP_NUM_COEF               (0xff<<8)
+#define AC_PCAP_NUM_COEF_SHIFT         8
 
 /* Volume knobs capabilities */
 #define AC_KNBCAP_NUM_STEPS            (0x7f<<0)
-#define AC_KNBCAP_DELTA                        (1<<8)
+#define AC_KNBCAP_DELTA                        (1<<7)
 
 /*
  * Control Parameters
@@ -266,6 +303,9 @@ enum {
 #define AC_DIG1_PROFESSIONAL           (1<<6)
 #define AC_DIG1_LEVEL                  (1<<7)
 
+/* DIGITAL2 bits */
+#define AC_DIG2_CC                     (0x7f<<0)
+
 /* Pin widget control - 8bit */
 #define AC_PINCTL_VREFEN               (0x7<<0)
 #define AC_PINCTL_VREF_HIZ             0       /* Hi-Z */
@@ -280,12 +320,22 @@ enum {
 /* Unsolicited response - 8bit */
 #define AC_USRSP_EN                    (1<<7)
 
+/* Pin sense - 32bit */
+#define AC_PINSENSE_IMPEDANCE_MASK     (0x7fffffff)
+#define AC_PINSENSE_PRESENCE           (1<<31)
+
+/* EAPD/BTL enable - 32bit */
+#define AC_EAPDBTL_BALANCED            (1<<0)
+#define AC_EAPDBTL_EAPD                        (1<<1)
+#define AC_EAPDBTL_LR_SWAP             (1<<2)
+
 /* configuration default - 32bit */
 #define AC_DEFCFG_SEQUENCE             (0xf<<0)
 #define AC_DEFCFG_DEF_ASSOC            (0xf<<4)
 #define AC_DEFCFG_ASSOC_SHIFT          4
 #define AC_DEFCFG_MISC                 (0xf<<8)
 #define AC_DEFCFG_MISC_SHIFT           8
+#define AC_DEFCFG_MISC_NO_PRESENCE     (1<<0)
 #define AC_DEFCFG_COLOR                        (0xf<<12)
 #define AC_DEFCFG_COLOR_SHIFT          12
 #define AC_DEFCFG_CONN_TYPE            (0xf<<16)
@@ -417,7 +467,7 @@ struct hda_bus_ops {
        /* free the private data */
        void (*private_free)(struct hda_bus *);
 #ifdef CONFIG_SND_HDA_POWER_SAVE
-       /* notify power-up/down from codec to contoller */
+       /* notify power-up/down from codec to controller */
        void (*pm_notify)(struct hda_codec *codec);
 #endif
 };
@@ -456,6 +506,9 @@ struct hda_bus {
        struct hda_bus_unsolicited *unsol;
 
        struct snd_info_entry *proc;
+
+       /* misc op flags */
+       unsigned int needs_damn_long_delay :1;
 };
 
 /*
@@ -470,6 +523,7 @@ struct hda_codec_preset {
        unsigned int subs;
        unsigned int subs_mask;
        unsigned int rev;
+       hda_nid_t afg, mfg;
        const char *name;
        int (*patch)(struct hda_codec *codec);
 };
index c957eb58de5cb048c6bf9e4dca471dcd2ddc5a37..f9de7c467c2552ad56b73568fa07caae203f7e85 100644 (file)
@@ -20,7 +20,6 @@
  *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA
  */
 
-#include <sound/driver.h>
 #include <linux/init.h>
 #include <linux/slab.h>
 #include <sound/core.h>
index bafb7b01f5a1db6177fbb4302b1237165b98106e..2177d9af533496a4583d943c6f648d241f9ef078 100644 (file)
@@ -18,7 +18,6 @@
  *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA
  */
 
-#include <sound/driver.h>
 #include <linux/init.h>
 #include <linux/slab.h>
 #include <linux/pci.h>
index 3fa0f9704909764e154da625082251b5e0ef9e26..56f8a30507513563135b62b51d17f0169b3c672e 100644 (file)
@@ -34,7 +34,6 @@
  * 
  */
 
-#include <sound/driver.h>
 #include <asm/io.h>
 #include <linux/delay.h>
 #include <linux/interrupt.h>
 #include "hda_codec.h"
 
 
-static int index = SNDRV_DEFAULT_IDX1;
-static char *id = SNDRV_DEFAULT_STR1;
-static char *model;
-static int position_fix;
-static int probe_mask = -1;
+static int index[SNDRV_CARDS] = SNDRV_DEFAULT_IDX;
+static char *id[SNDRV_CARDS] = SNDRV_DEFAULT_STR;
+static int enable[SNDRV_CARDS] = SNDRV_DEFAULT_ENABLE_PNP;
+static char *model[SNDRV_CARDS];
+static int position_fix[SNDRV_CARDS];
+static int probe_mask[SNDRV_CARDS] = {[0 ... (SNDRV_CARDS-1)] = -1};
 static int single_cmd;
 static int enable_msi;
 
-module_param(index, int, 0444);
+module_param_array(index, int, NULL, 0444);
 MODULE_PARM_DESC(index, "Index value for Intel HD audio interface.");
-module_param(id, charp, 0444);
+module_param_array(id, charp, NULL, 0444);
 MODULE_PARM_DESC(id, "ID string for Intel HD audio interface.");
-module_param(model, charp, 0444);
+module_param_array(enable, bool, NULL, 0444);
+MODULE_PARM_DESC(enable, "Enable Intel HD audio interface.");
+module_param_array(model, charp, NULL, 0444);
 MODULE_PARM_DESC(model, "Use the given board model.");
-module_param(position_fix, int, 0444);
+module_param_array(position_fix, int, NULL, 0444);
 MODULE_PARM_DESC(position_fix, "Fix DMA pointer "
                 "(0 = auto, 1 = none, 2 = POSBUF, 3 = FIFO size).");
-module_param(probe_mask, int, 0444);
+module_param_array(probe_mask, int, NULL, 0444);
 MODULE_PARM_DESC(probe_mask, "Bitmask to probe codecs (default = -1).");
 module_param(single_cmd, bool, 0444);
 MODULE_PARM_DESC(single_cmd, "Use single command to communicate with codecs "
                 "(for debugging only).");
-module_param(enable_msi, int, 0);
+module_param(enable_msi, int, 0444);
 MODULE_PARM_DESC(enable_msi, "Enable Message Signaled Interrupt (MSI)");
 
 #ifdef CONFIG_SND_HDA_POWER_SAVE
@@ -87,10 +89,6 @@ module_param(power_save_controller, bool, 0644);
 MODULE_PARM_DESC(power_save_controller, "Reset controller in power save mode.");
 #endif
 
-/* just for backward compatibility */
-static int enable;
-module_param(enable, bool, 0444);
-
 MODULE_LICENSE("GPL");
 MODULE_SUPPORTED_DEVICE("{{Intel, ICH6},"
                         "{Intel, ICH6M},"
@@ -98,12 +96,20 @@ MODULE_SUPPORTED_DEVICE("{{Intel, ICH6},"
                         "{Intel, ESB2},"
                         "{Intel, ICH8},"
                         "{Intel, ICH9},"
+                        "{Intel, ICH10},"
+                        "{Intel, SCH},"
                         "{ATI, SB450},"
                         "{ATI, SB600},"
                         "{ATI, RS600},"
                         "{ATI, RS690},"
                         "{ATI, RS780},"
                         "{ATI, R600},"
+                        "{ATI, RV630},"
+                        "{ATI, RV610},"
+                        "{ATI, RV670},"
+                        "{ATI, RV635},"
+                        "{ATI, RV620},"
+                        "{ATI, RV770},"
                         "{VIA, VT8251},"
                         "{VIA, VT8237A},"
                         "{SiS, SIS966},"
@@ -370,6 +376,7 @@ struct azx {
 /* driver types */
 enum {
        AZX_DRIVER_ICH,
+       AZX_DRIVER_SCH,
        AZX_DRIVER_ATI,
        AZX_DRIVER_ATIHDMI,
        AZX_DRIVER_VIA,
@@ -380,6 +387,7 @@ enum {
 
 static char *driver_short_names[] __devinitdata = {
        [AZX_DRIVER_ICH] = "HDA Intel",
+       [AZX_DRIVER_SCH] = "HDA Intel MID",
        [AZX_DRIVER_ATI] = "HDA ATI SB",
        [AZX_DRIVER_ATIHDMI] = "HDA ATI HDMI",
        [AZX_DRIVER_VIA] = "HDA VIA VT82xx",
@@ -547,7 +555,7 @@ static unsigned int azx_rirb_get_response(struct hda_codec *codec)
 
  again:
        timeout = jiffies + msecs_to_jiffies(1000);
-       do {
+       for (;;) {
                if (chip->polling_mode) {
                        spin_lock_irq(&chip->reg_lock);
                        azx_update_rirb(chip);
@@ -555,8 +563,15 @@ static unsigned int azx_rirb_get_response(struct hda_codec *codec)
                }
                if (!chip->rirb.cmds)
                        return chip->rirb.res; /* the last value */
-               schedule_timeout_uninterruptible(1);
-       } while (time_after_eq(timeout, jiffies));
+               if (time_after(jiffies, timeout))
+                       break;
+               if (codec->bus->needs_damn_long_delay)
+                       msleep(2); /* temporary workaround */
+               else {
+                       udelay(10);
+                       cond_resched();
+               }
+       }
 
        if (chip->msi) {
                snd_printk(KERN_WARNING "hda_intel: No response from codec, "
@@ -618,8 +633,9 @@ static int azx_single_send_cmd(struct hda_codec *codec, u32 val)
                }
                udelay(1);
        }
-       snd_printd(SFX "send_cmd timeout: IRS=0x%x, val=0x%x\n",
-                  azx_readw(chip, IRS), val);
+       if (printk_ratelimit())
+               snd_printd(SFX "send_cmd timeout: IRS=0x%x, val=0x%x\n",
+                          azx_readw(chip, IRS), val);
        return -EIO;
 }
 
@@ -635,8 +651,9 @@ static unsigned int azx_single_get_response(struct hda_codec *codec)
                        return azx_readl(chip, IR);
                udelay(1);
        }
-       snd_printd(SFX "get_response timeout: IRS=0x%x\n",
-                  azx_readw(chip, IRS));
+       if (printk_ratelimit())
+               snd_printd(SFX "get_response timeout: IRS=0x%x\n",
+                          azx_readw(chip, IRS));
        return (unsigned int)-1;
 }
 
@@ -1031,7 +1048,8 @@ static unsigned int azx_max_codecs[] __devinitdata = {
        [AZX_DRIVER_NVIDIA] = 3,        /* FIXME: correct? */
 };
 
-static int __devinit azx_codec_create(struct azx *chip, const char *model)
+static int __devinit azx_codec_create(struct azx *chip, const char *model,
+                                     unsigned int codec_probe_mask)
 {
        struct hda_bus_template bus_temp;
        int c, codecs, audio_codecs, err;
@@ -1052,7 +1070,7 @@ static int __devinit azx_codec_create(struct azx *chip, const char *model)
 
        codecs = audio_codecs = 0;
        for (c = 0; c < AZX_MAX_CODECS; c++) {
-               if ((chip->codec_mask & (1 << c)) & probe_mask) {
+               if ((chip->codec_mask & (1 << c)) & codec_probe_mask) {
                        struct hda_codec *codec;
                        err = snd_hda_codec_new(chip->bus, c, &codec);
                        if (err < 0)
@@ -1065,7 +1083,7 @@ static int __devinit azx_codec_create(struct azx *chip, const char *model)
        if (!audio_codecs) {
                /* probe additional slots if no codec is found */
                for (; c < azx_max_codecs[chip->driver_type]; c++) {
-                       if ((chip->codec_mask & (1 << c)) & probe_mask) {
+                       if ((chip->codec_mask & (1 << c)) & codec_probe_mask) {
                                err = snd_hda_codec_new(chip->bus, c, NULL);
                                if (err < 0)
                                        continue;
@@ -1676,18 +1694,18 @@ static struct snd_pci_quirk probe_mask_list[] __devinitdata = {
        {}
 };
 
-static void __devinit check_probe_mask(struct azx *chip)
+static void __devinit check_probe_mask(struct azx *chip, int dev)
 {
        const struct snd_pci_quirk *q;
 
-       if (probe_mask == -1) {
+       if (probe_mask[dev] == -1) {
                q = snd_pci_quirk_lookup(chip->pci, probe_mask_list);
                if (q) {
                        printk(KERN_INFO
                               "hda_intel: probe_mask set to 0x%x "
                               "for device %04x:%04x\n",
                               q->value, q->subvendor, q->subdevice);
-                       probe_mask = q->value;
+                       probe_mask[dev] = q->value;
                }
        }
 }
@@ -1697,17 +1715,18 @@ static void __devinit check_probe_mask(struct azx *chip)
  * constructor
  */
 static int __devinit azx_create(struct snd_card *card, struct pci_dev *pci,
-                               int driver_type,
+                               int dev, int driver_type,
                                struct azx **rchip)
 {
        struct azx *chip;
        int err;
+       unsigned short gcap;
        static struct snd_device_ops ops = {
                .dev_free = azx_dev_free,
        };
 
        *rchip = NULL;
-       
+
        err = pci_enable_device(pci);
        if (err < 0)
                return err;
@@ -1727,8 +1746,8 @@ static int __devinit azx_create(struct snd_card *card, struct pci_dev *pci,
        chip->driver_type = driver_type;
        chip->msi = enable_msi;
 
-       chip->position_fix = check_position_fix(chip, position_fix);
-       check_probe_mask(chip);
+       chip->position_fix = check_position_fix(chip, position_fix[dev]);
+       check_probe_mask(chip, dev);
 
        chip->single_cmd = single_cmd;
 
@@ -1769,25 +1788,40 @@ static int __devinit azx_create(struct snd_card *card, struct pci_dev *pci,
        pci_set_master(pci);
        synchronize_irq(chip->irq);
 
-       switch (chip->driver_type) {
-       case AZX_DRIVER_ULI:
-               chip->playback_streams = ULI_NUM_PLAYBACK;
-               chip->capture_streams = ULI_NUM_CAPTURE;
-               chip->playback_index_offset = ULI_PLAYBACK_INDEX;
-               chip->capture_index_offset = ULI_CAPTURE_INDEX;
-               break;
-       case AZX_DRIVER_ATIHDMI:
-               chip->playback_streams = ATIHDMI_NUM_PLAYBACK;
-               chip->capture_streams = ATIHDMI_NUM_CAPTURE;
-               chip->playback_index_offset = ATIHDMI_PLAYBACK_INDEX;
-               chip->capture_index_offset = ATIHDMI_CAPTURE_INDEX;
-               break;
-       default:
-               chip->playback_streams = ICH6_NUM_PLAYBACK;
-               chip->capture_streams = ICH6_NUM_CAPTURE;
-               chip->playback_index_offset = ICH6_PLAYBACK_INDEX;
-               chip->capture_index_offset = ICH6_CAPTURE_INDEX;
-               break;
+       gcap = azx_readw(chip, GCAP);
+       snd_printdd("chipset global capabilities = 0x%x\n", gcap);
+
+       if (gcap) {
+               /* read number of streams from GCAP register instead of using
+                * hardcoded value
+                */
+               chip->playback_streams = (gcap & (0xF << 12)) >> 12;
+               chip->capture_streams = (gcap & (0xF << 8)) >> 8;
+               chip->playback_index_offset = (gcap & (0xF << 12)) >> 12;
+               chip->capture_index_offset = 0;
+       } else {
+               /* gcap didn't give any info, switching to old method */
+
+               switch (chip->driver_type) {
+               case AZX_DRIVER_ULI:
+                       chip->playback_streams = ULI_NUM_PLAYBACK;
+                       chip->capture_streams = ULI_NUM_CAPTURE;
+                       chip->playback_index_offset = ULI_PLAYBACK_INDEX;
+                       chip->capture_index_offset = ULI_CAPTURE_INDEX;
+                       break;
+               case AZX_DRIVER_ATIHDMI:
+                       chip->playback_streams = ATIHDMI_NUM_PLAYBACK;
+                       chip->capture_streams = ATIHDMI_NUM_CAPTURE;
+                       chip->playback_index_offset = ATIHDMI_PLAYBACK_INDEX;
+                       chip->capture_index_offset = ATIHDMI_CAPTURE_INDEX;
+                       break;
+               default:
+                       chip->playback_streams = ICH6_NUM_PLAYBACK;
+                       chip->capture_streams = ICH6_NUM_CAPTURE;
+                       chip->playback_index_offset = ICH6_PLAYBACK_INDEX;
+                       chip->capture_index_offset = ICH6_CAPTURE_INDEX;
+                       break;
+               }
        }
        chip->num_streams = chip->playback_streams + chip->capture_streams;
        chip->azx_dev = kcalloc(chip->num_streams, sizeof(*chip->azx_dev),
@@ -1869,17 +1903,25 @@ static void power_down_all_codecs(struct azx *chip)
 static int __devinit azx_probe(struct pci_dev *pci,
                               const struct pci_device_id *pci_id)
 {
+       static int dev;
        struct snd_card *card;
        struct azx *chip;
        int err;
 
-       card = snd_card_new(index, id, THIS_MODULE, 0);
+       if (dev >= SNDRV_CARDS)
+               return -ENODEV;
+       if (!enable[dev]) {
+               dev++;
+               return -ENOENT;
+       }
+
+       card = snd_card_new(index[dev], id[dev], THIS_MODULE, 0);
        if (!card) {
                snd_printk(KERN_ERR SFX "Error creating card!\n");
                return -ENOMEM;
        }
 
-       err = azx_create(card, pci, pci_id->driver_data, &chip);
+       err = azx_create(card, pci, dev, pci_id->driver_data, &chip);
        if (err < 0) {
                snd_card_free(card);
                return err;
@@ -1887,7 +1929,7 @@ static int __devinit azx_probe(struct pci_dev *pci,
        card->private_data = chip;
 
        /* create codec instances */
-       err = azx_codec_create(chip, model);
+       err = azx_codec_create(chip, model[dev], probe_mask[dev]);
        if (err < 0) {
                snd_card_free(card);
                return err;
@@ -1919,6 +1961,7 @@ static int __devinit azx_probe(struct pci_dev *pci,
        chip->running = 1;
        power_down_all_codecs(chip);
 
+       dev++;
        return err;
 }
 
@@ -1936,12 +1979,21 @@ static struct pci_device_id azx_ids[] = {
        { 0x8086, 0x284b, PCI_ANY_ID, PCI_ANY_ID, 0, 0, AZX_DRIVER_ICH }, /* ICH8 */
        { 0x8086, 0x293e, PCI_ANY_ID, PCI_ANY_ID, 0, 0, AZX_DRIVER_ICH }, /* ICH9 */
        { 0x8086, 0x293f, PCI_ANY_ID, PCI_ANY_ID, 0, 0, AZX_DRIVER_ICH }, /* ICH9 */
+       { 0x8086, 0x3a3e, PCI_ANY_ID, PCI_ANY_ID, 0, 0, AZX_DRIVER_ICH }, /* ICH10 */
+       { 0x8086, 0x3a6e, PCI_ANY_ID, PCI_ANY_ID, 0, 0, AZX_DRIVER_ICH }, /* ICH10 */
+       { 0x8086, 0x811b, PCI_ANY_ID, PCI_ANY_ID, 0, 0, AZX_DRIVER_SCH }, /* SCH*/
        { 0x1002, 0x437b, PCI_ANY_ID, PCI_ANY_ID, 0, 0, AZX_DRIVER_ATI }, /* ATI SB450 */
        { 0x1002, 0x4383, PCI_ANY_ID, PCI_ANY_ID, 0, 0, AZX_DRIVER_ATI }, /* ATI SB600 */
        { 0x1002, 0x793b, PCI_ANY_ID, PCI_ANY_ID, 0, 0, AZX_DRIVER_ATIHDMI }, /* ATI RS600 HDMI */
        { 0x1002, 0x7919, PCI_ANY_ID, PCI_ANY_ID, 0, 0, AZX_DRIVER_ATIHDMI }, /* ATI RS690 HDMI */
-       { 0x1002, 0x960c, PCI_ANY_ID, PCI_ANY_ID, 0, 0, AZX_DRIVER_ATIHDMI }, /* ATI RS780 HDMI */
+       { 0x1002, 0x960f, PCI_ANY_ID, PCI_ANY_ID, 0, 0, AZX_DRIVER_ATIHDMI }, /* ATI RS780 HDMI */
        { 0x1002, 0xaa00, PCI_ANY_ID, PCI_ANY_ID, 0, 0, AZX_DRIVER_ATIHDMI }, /* ATI R600 HDMI */
+       { 0x1002, 0xaa08, PCI_ANY_ID, PCI_ANY_ID, 0, 0, AZX_DRIVER_ATIHDMI }, /* ATI RV630 HDMI */
+       { 0x1002, 0xaa10, PCI_ANY_ID, PCI_ANY_ID, 0, 0, AZX_DRIVER_ATIHDMI }, /* ATI RV610 HDMI */
+       { 0x1002, 0xaa18, PCI_ANY_ID, PCI_ANY_ID, 0, 0, AZX_DRIVER_ATIHDMI }, /* ATI RV670 HDMI */
+       { 0x1002, 0xaa20, PCI_ANY_ID, PCI_ANY_ID, 0, 0, AZX_DRIVER_ATIHDMI }, /* ATI RV635 HDMI */
+       { 0x1002, 0xaa28, PCI_ANY_ID, PCI_ANY_ID, 0, 0, AZX_DRIVER_ATIHDMI }, /* ATI RV620 HDMI */
+       { 0x1002, 0xaa30, PCI_ANY_ID, PCI_ANY_ID, 0, 0, AZX_DRIVER_ATIHDMI }, /* ATI RV770 HDMI */
        { 0x1106, 0x3288, PCI_ANY_ID, PCI_ANY_ID, 0, 0, AZX_DRIVER_VIA }, /* VIA VT8251/VT8237A */
        { 0x1039, 0x7502, PCI_ANY_ID, PCI_ANY_ID, 0, 0, AZX_DRIVER_SIS }, /* SIS966 */
        { 0x10b9, 0x5461, PCI_ANY_ID, PCI_ANY_ID, 0, 0, AZX_DRIVER_ULI }, /* ULI M5461 */
index 8c56c9cb0d09523b7cb9eca6adbd24a0025c2bb9..ad0014ab71f9e0388ead79f283a7867ca4542c39 100644 (file)
@@ -90,6 +90,13 @@ int snd_hda_codec_amp_stereo(struct hda_codec *codec, hda_nid_t nid,
 void snd_hda_codec_resume_amp(struct hda_codec *codec);
 #endif
 
+void snd_hda_set_vmaster_tlv(struct hda_codec *codec, hda_nid_t nid, int dir,
+                            unsigned int *tlv);
+struct snd_kcontrol *snd_hda_find_mixer_ctl(struct hda_codec *codec,
+                                           const char *name);
+int snd_hda_add_vmaster(struct hda_codec *codec, char *name,
+                       unsigned int *tlv, const char **slaves);
+
 /* amp value bits */
 #define HDA_AMP_MUTE   0x80
 #define HDA_AMP_UNMUTE 0x00
@@ -325,6 +332,7 @@ struct auto_pin_cfg {
        hda_nid_t input_pins[AUTO_PIN_LAST];
        hda_nid_t dig_out_pin;
        hda_nid_t dig_in_pin;
+       hda_nid_t mono_out_pin;
 };
 
 #define get_defcfg_connect(cfg) \
@@ -363,10 +371,11 @@ static inline u32 get_wcaps(struct hda_codec *codec, hda_nid_t nid)
 {
        if (nid < codec->start_nid ||
            nid >= codec->start_nid + codec->num_nodes)
-               return snd_hda_param_read(codec, nid, AC_PAR_AUDIO_WIDGET_CAP);
+               return 0;
        return codec->wcaps[nid - codec->start_nid];
 }
 
+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);
 
@@ -398,4 +407,11 @@ int snd_hda_check_amp_list_power(struct hda_codec *codec,
                                 hda_nid_t nid);
 #endif /* CONFIG_SND_HDA_POWER_SAVE */
 
+/*
+ * virtual master control
+ */
+struct snd_kcontrol *snd_ctl_make_virtual_master(char *name,
+                                                const unsigned int *tlv);
+int snd_ctl_add_slave(struct snd_kcontrol *master, struct snd_kcontrol *slave);
+                     
 #endif /* __SOUND_HDA_LOCAL_H */
index e94944f34ffdcfffc4c8c9385ba268899ce824c2..35a630d1770f80a2d5b62198bb6685f50069aa6c 100644 (file)
@@ -21,7 +21,6 @@
  *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA
  */
 
-#include <sound/driver.h>
 #include <linux/init.h>
 #include <sound/core.h>
 #include "hda_codec.h"
@@ -203,7 +202,8 @@ static const char *get_jack_color(u32 cfg)
 }
 
 static void print_pin_caps(struct snd_info_buffer *buffer,
-                          struct hda_codec *codec, hda_nid_t nid)
+                          struct hda_codec *codec, hda_nid_t nid,
+                          int *supports_vref)
 {
        static char *jack_conns[4] = { "Jack", "N/A", "Fixed", "Both" };
        static char *jack_types[16] = {
@@ -213,7 +213,7 @@ static void print_pin_caps(struct snd_info_buffer *buffer,
                "SPDIF In", "Digitial In", "Reserved", "Other"
        };
        static char *jack_locations[4] = { "Ext", "Int", "Sep", "Oth" };
-       unsigned int caps;
+       unsigned int caps, val;
 
        caps = snd_hda_param_read(codec, nid, AC_PAR_PIN_CAP);
        snd_iprintf(buffer, "  Pincap 0x08%x:", caps);
@@ -227,7 +227,45 @@ static void print_pin_caps(struct snd_info_buffer *buffer,
                snd_iprintf(buffer, " EAPD");
        if (caps & AC_PINCAP_PRES_DETECT)
                snd_iprintf(buffer, " Detect");
+       if (caps & AC_PINCAP_BALANCE)
+               snd_iprintf(buffer, " Balanced");
+       if (caps & AC_PINCAP_LR_SWAP)
+               snd_iprintf(buffer, " R/L");
+       if (caps & AC_PINCAP_TRIG_REQ)
+               snd_iprintf(buffer, " Trigger");
+       if (caps & AC_PINCAP_IMP_SENSE)
+               snd_iprintf(buffer, " ImpSense");
        snd_iprintf(buffer, "\n");
+       if (caps & AC_PINCAP_VREF) {
+               unsigned int vref =
+                       (caps & AC_PINCAP_VREF) >> AC_PINCAP_VREF_SHIFT;
+               snd_iprintf(buffer, "    Vref caps:");
+               if (vref & AC_PINCAP_VREF_HIZ)
+                       snd_iprintf(buffer, " HIZ");
+               if (vref & AC_PINCAP_VREF_50)
+                       snd_iprintf(buffer, " 50");
+               if (vref & AC_PINCAP_VREF_GRD)
+                       snd_iprintf(buffer, " GRD");
+               if (vref & AC_PINCAP_VREF_80)
+                       snd_iprintf(buffer, " 80");
+               if (vref & AC_PINCAP_VREF_100)
+                       snd_iprintf(buffer, " 100");
+               snd_iprintf(buffer, "\n");
+               *supports_vref = 1;
+       } else
+               *supports_vref = 0;
+       if (caps & AC_PINCAP_EAPD) {
+               val = snd_hda_codec_read(codec, nid, 0,
+                                        AC_VERB_GET_EAPD_BTLENABLE, 0);
+               snd_iprintf(buffer, "  EAPD 0x%x:", val);
+               if (val & AC_EAPDBTL_BALANCED)
+                       snd_iprintf(buffer, " BALANCED");
+               if (val & AC_EAPDBTL_EAPD)
+                       snd_iprintf(buffer, " EAPD");
+               if (val & AC_EAPDBTL_LR_SWAP)
+                       snd_iprintf(buffer, " R/L");
+               snd_iprintf(buffer, "\n");
+       }
        caps = snd_hda_codec_read(codec, nid, 0, AC_VERB_GET_CONFIG_DEFAULT, 0);
        snd_iprintf(buffer, "  Pin Default 0x%08x: [%s] %s at %s %s\n", caps,
                    jack_conns[(caps & AC_DEFCFG_PORT_CONN) >> AC_DEFCFG_PORT_CONN_SHIFT],
@@ -237,8 +275,233 @@ static void print_pin_caps(struct snd_info_buffer *buffer,
        snd_iprintf(buffer, "    Conn = %s, Color = %s\n",
                    get_jack_connection(caps),
                    get_jack_color(caps));
+       /* Default association and sequence values refer to default grouping
+        * of pin complexes and their sequence within the group. This is used
+        * for priority and resource allocation.
+        */
+       snd_iprintf(buffer, "    DefAssociation = 0x%x, Sequence = 0x%x\n",
+                   (caps & AC_DEFCFG_DEF_ASSOC) >> AC_DEFCFG_ASSOC_SHIFT,
+                   caps & AC_DEFCFG_SEQUENCE);
+       if (((caps & AC_DEFCFG_MISC) >> AC_DEFCFG_MISC_SHIFT) &
+           AC_DEFCFG_MISC_NO_PRESENCE) {
+               /* Miscellaneous bit indicates external hardware does not
+                * support presence detection even if the pin complex
+                * indicates it is supported.
+                */
+               snd_iprintf(buffer, "    Misc = NO_PRESENCE\n");
+       }
+}
+
+static void print_pin_ctls(struct snd_info_buffer *buffer,
+                          struct hda_codec *codec, hda_nid_t nid,
+                          int supports_vref)
+{
+       unsigned int pinctls;
+
+       pinctls = snd_hda_codec_read(codec, nid, 0,
+                                    AC_VERB_GET_PIN_WIDGET_CONTROL, 0);
+       snd_iprintf(buffer, "  Pin-ctls: 0x%02x:", pinctls);
+       if (pinctls & AC_PINCTL_IN_EN)
+               snd_iprintf(buffer, " IN");
+       if (pinctls & AC_PINCTL_OUT_EN)
+               snd_iprintf(buffer, " OUT");
+       if (pinctls & AC_PINCTL_HP_EN)
+               snd_iprintf(buffer, " HP");
+       if (supports_vref) {
+               int vref = pinctls & AC_PINCTL_VREFEN;
+               switch (vref) {
+               case AC_PINCTL_VREF_HIZ:
+                       snd_iprintf(buffer, " VREF_HIZ");
+                       break;
+               case AC_PINCTL_VREF_50:
+                       snd_iprintf(buffer, " VREF_50");
+                       break;
+               case AC_PINCTL_VREF_GRD:
+                       snd_iprintf(buffer, " VREF_GRD");
+                       break;
+               case AC_PINCTL_VREF_80:
+                       snd_iprintf(buffer, " VREF_80");
+                       break;
+               case AC_PINCTL_VREF_100:
+                       snd_iprintf(buffer, " VREF_100");
+                       break;
+               }
+       }
+       snd_iprintf(buffer, "\n");
+}
+
+static void print_vol_knob(struct snd_info_buffer *buffer,
+                          struct hda_codec *codec, hda_nid_t nid)
+{
+       unsigned int cap = snd_hda_param_read(codec, nid,
+                                             AC_PAR_VOL_KNB_CAP);
+       snd_iprintf(buffer, "  Volume-Knob: delta=%d, steps=%d, ",
+                   (cap >> 7) & 1, cap & 0x7f);
+       cap = snd_hda_codec_read(codec, nid, 0,
+                                AC_VERB_GET_VOLUME_KNOB_CONTROL, 0);
+       snd_iprintf(buffer, "direct=%d, val=%d\n",
+                   (cap >> 7) & 1, cap & 0x7f);
+}
+
+static void print_audio_io(struct snd_info_buffer *buffer,
+                          struct hda_codec *codec, hda_nid_t nid,
+                          unsigned int wid_type)
+{
+       int conv = snd_hda_codec_read(codec, nid, 0, AC_VERB_GET_CONV, 0);
+       snd_iprintf(buffer,
+                   "  Converter: stream=%d, channel=%d\n",
+                   (conv & AC_CONV_STREAM) >> AC_CONV_STREAM_SHIFT,
+                   conv & AC_CONV_CHANNEL);
+
+       if (wid_type == AC_WID_AUD_IN && (conv & AC_CONV_CHANNEL) == 0) {
+               int sdi = snd_hda_codec_read(codec, nid, 0,
+                                            AC_VERB_GET_SDI_SELECT, 0);
+               snd_iprintf(buffer, "  SDI-Select: %d\n",
+                           sdi & AC_SDI_SELECT);
+       }
+}
+
+static void print_digital_conv(struct snd_info_buffer *buffer,
+                              struct hda_codec *codec, hda_nid_t nid)
+{
+       unsigned int digi1 = snd_hda_codec_read(codec, nid, 0,
+                                               AC_VERB_GET_DIGI_CONVERT_1, 0);
+       unsigned int digi2 = snd_hda_codec_read(codec, nid, 0,
+                                               AC_VERB_GET_DIGI_CONVERT_2, 0);
+       snd_iprintf(buffer, "  Digital:");
+       if (digi1 & AC_DIG1_ENABLE)
+               snd_iprintf(buffer, " Enabled");
+       if (digi1 & AC_DIG1_V)
+               snd_iprintf(buffer, " Validity");
+       if (digi1 & AC_DIG1_VCFG)
+               snd_iprintf(buffer, " ValidityCfg");
+       if (digi1 & AC_DIG1_EMPHASIS)
+               snd_iprintf(buffer, " Preemphasis");
+       if (digi1 & AC_DIG1_COPYRIGHT)
+               snd_iprintf(buffer, " Copyright");
+       if (digi1 & AC_DIG1_NONAUDIO)
+               snd_iprintf(buffer, " Non-Audio");
+       if (digi1 & AC_DIG1_PROFESSIONAL)
+               snd_iprintf(buffer, " Pro");
+       if (digi1 & AC_DIG1_LEVEL)
+               snd_iprintf(buffer, " GenLevel");
+       snd_iprintf(buffer, "\n");
+       snd_iprintf(buffer, "  Digital category: 0x%x\n", digi2 & AC_DIG2_CC);
+}
+
+static const char *get_pwr_state(u32 state)
+{
+       static const char *buf[4] = {
+               "D0", "D1", "D2", "D3"
+       };
+       if (state < 4)
+               return buf[state];
+       return "UNKNOWN";
+}
+
+static void print_power_state(struct snd_info_buffer *buffer,
+                             struct hda_codec *codec, hda_nid_t nid)
+{
+       int pwr = snd_hda_codec_read(codec, nid, 0,
+                                    AC_VERB_GET_POWER_STATE, 0);
+       snd_iprintf(buffer, "  Power: setting=%s, actual=%s\n",
+                   get_pwr_state(pwr & AC_PWRST_SETTING),
+                   get_pwr_state((pwr & AC_PWRST_ACTUAL) >>
+                                 AC_PWRST_ACTUAL_SHIFT));
+}
+
+static void print_unsol_cap(struct snd_info_buffer *buffer,
+                             struct hda_codec *codec, hda_nid_t nid)
+{
+       int unsol = snd_hda_codec_read(codec, nid, 0,
+                                      AC_VERB_GET_UNSOLICITED_RESPONSE, 0);
+       snd_iprintf(buffer,
+                   "  Unsolicited: tag=%02x, enabled=%d\n",
+                   unsol & AC_UNSOL_TAG,
+                   (unsol & AC_UNSOL_ENABLED) ? 1 : 0);
+}
+
+static void print_proc_caps(struct snd_info_buffer *buffer,
+                           struct hda_codec *codec, hda_nid_t nid)
+{
+       unsigned int proc_caps = snd_hda_param_read(codec, nid,
+                                                   AC_PAR_PROC_CAP);
+       snd_iprintf(buffer, "  Processing caps: benign=%d, ncoeff=%d\n",
+                   proc_caps & AC_PCAP_BENIGN,
+                   (proc_caps & AC_PCAP_NUM_COEF) >> AC_PCAP_NUM_COEF_SHIFT);
 }
 
+static void print_conn_list(struct snd_info_buffer *buffer,
+                           struct hda_codec *codec, hda_nid_t nid,
+                           unsigned int wid_type, hda_nid_t *conn,
+                           int conn_len)
+{
+       int c, curr = -1;
+
+       if (conn_len > 1 && wid_type != AC_WID_AUD_MIX)
+               curr = snd_hda_codec_read(codec, nid, 0,
+                                         AC_VERB_GET_CONNECT_SEL, 0);
+       snd_iprintf(buffer, "  Connection: %d\n", conn_len);
+       if (conn_len > 0) {
+               snd_iprintf(buffer, "    ");
+               for (c = 0; c < conn_len; c++) {
+                       snd_iprintf(buffer, " 0x%02x", conn[c]);
+                       if (c == curr)
+                               snd_iprintf(buffer, "*");
+               }
+               snd_iprintf(buffer, "\n");
+       }
+}
+
+static void print_realtek_coef(struct snd_info_buffer *buffer,
+                              struct hda_codec *codec, hda_nid_t nid)
+{
+       int coeff = snd_hda_codec_read(codec, nid, 0,
+                                      AC_VERB_GET_PROC_COEF, 0);
+       snd_iprintf(buffer, "  Processing Coefficient: 0x%02x\n", coeff);
+       coeff = snd_hda_codec_read(codec, nid, 0,
+                                  AC_VERB_GET_COEF_INDEX, 0);
+       snd_iprintf(buffer, "  Coefficient Index: 0x%02x\n", coeff);
+}
+
+static void print_gpio(struct snd_info_buffer *buffer,
+                      struct hda_codec *codec, hda_nid_t nid)
+{
+       unsigned int gpio =
+               snd_hda_param_read(codec, codec->afg, AC_PAR_GPIO_CAP);
+       unsigned int enable, direction, wake, unsol, sticky, data;
+       int i, max;
+       snd_iprintf(buffer, "GPIO: io=%d, o=%d, i=%d, "
+                   "unsolicited=%d, wake=%d\n",
+                   gpio & AC_GPIO_IO_COUNT,
+                   (gpio & AC_GPIO_O_COUNT) >> AC_GPIO_O_COUNT_SHIFT,
+                   (gpio & AC_GPIO_I_COUNT) >> AC_GPIO_I_COUNT_SHIFT,
+                   (gpio & AC_GPIO_UNSOLICITED) ? 1 : 0,
+                   (gpio & AC_GPIO_WAKE) ? 1 : 0);
+       max = gpio & AC_GPIO_IO_COUNT;
+       enable = snd_hda_codec_read(codec, nid, 0,
+                                   AC_VERB_GET_GPIO_MASK, 0);
+       direction = snd_hda_codec_read(codec, nid, 0,
+                                      AC_VERB_GET_GPIO_DIRECTION, 0);
+       wake = snd_hda_codec_read(codec, nid, 0,
+                                 AC_VERB_GET_GPIO_WAKE_MASK, 0);
+       unsol  = snd_hda_codec_read(codec, nid, 0,
+                                   AC_VERB_GET_GPIO_UNSOLICITED_RSP_MASK, 0);
+       sticky = snd_hda_codec_read(codec, nid, 0,
+                                   AC_VERB_GET_GPIO_STICKY_MASK, 0);
+       data = snd_hda_codec_read(codec, nid, 0,
+                                 AC_VERB_GET_GPIO_DATA, 0);
+       for (i = 0; i < max; ++i)
+               snd_iprintf(buffer,
+                           "  IO[%d]: enable=%d, dir=%d, wake=%d, "
+                           "sticky=%d, data=%d\n", i,
+                           (enable & (1<<i)) ? 1 : 0,
+                           (direction & (1<<i)) ? 1 : 0,
+                           (wake & (1<<i)) ? 1 : 0,
+                           (sticky & (1<<i)) ? 1 : 0,
+                           (data & (1<<i)) ? 1 : 0);
+       /* FIXME: add GPO and GPI pin information */
+}
 
 static void print_codec_info(struct snd_info_entry *entry,
                             struct snd_info_buffer *buffer)
@@ -276,14 +539,17 @@ static void print_codec_info(struct snd_info_entry *entry,
                snd_hda_power_down(codec);
                return;
        }
+
+       print_gpio(buffer, codec, codec->afg);
+
        for (i = 0; i < nodes; i++, nid++) {
                unsigned int wid_caps =
                        snd_hda_param_read(codec, nid,
                                           AC_PAR_AUDIO_WIDGET_CAP);
                unsigned int wid_type =
                        (wid_caps & AC_WCAP_TYPE) >> AC_WCAP_TYPE_SHIFT;
-               int conn_len = 0; 
                hda_nid_t conn[HDA_MAX_CONNECTIONS];
+               int conn_len = 0;
 
                snd_iprintf(buffer, "Node 0x%02x [%s] wcaps 0x%x:", nid,
                            get_wid_type_name(wid_type), wid_caps);
@@ -297,8 +563,18 @@ static void print_codec_info(struct snd_info_entry *entry,
                        snd_iprintf(buffer, " Amp-In");
                if (wid_caps & AC_WCAP_OUT_AMP)
                        snd_iprintf(buffer, " Amp-Out");
+               if (wid_caps & AC_WCAP_STRIPE)
+                       snd_iprintf(buffer, " Stripe");
+               if (wid_caps & AC_WCAP_LR_SWAP)
+                       snd_iprintf(buffer, " R/L");
                snd_iprintf(buffer, "\n");
 
+               /* volume knob is a special widget that always have connection
+                * list
+                */
+               if (wid_type == AC_WID_VOL_KNB)
+                       wid_caps |= AC_WCAP_CONN_LIST;
+
                if (wid_caps & AC_WCAP_CONN_LIST)
                        conn_len = snd_hda_get_connections(codec, nid, conn,
                                                           HDA_MAX_CONNECTIONS);
@@ -318,48 +594,49 @@ static void print_codec_info(struct snd_info_entry *entry,
                                       wid_caps & AC_WCAP_STEREO, 1);
                }
 
-               if (wid_type == AC_WID_PIN) {
-                       unsigned int pinctls;
-                       print_pin_caps(buffer, codec, nid);
-                       pinctls = snd_hda_codec_read(codec, nid, 0,
-                                            AC_VERB_GET_PIN_WIDGET_CONTROL,
-                                                    0);
-                       snd_iprintf(buffer, "  Pin-ctls: 0x%02x:", pinctls);
-                       if (pinctls & AC_PINCTL_IN_EN)
-                               snd_iprintf(buffer, " IN");
-                       if (pinctls & AC_PINCTL_OUT_EN)
-                               snd_iprintf(buffer, " OUT");
-                       if (pinctls & AC_PINCTL_HP_EN)
-                               snd_iprintf(buffer, " HP");
-                       snd_iprintf(buffer, "\n");
+               switch (wid_type) {
+               case AC_WID_PIN: {
+                       int supports_vref;
+                       print_pin_caps(buffer, codec, nid, &supports_vref);
+                       print_pin_ctls(buffer, codec, nid, supports_vref);
+                       break;
                }
-
-               if ((wid_type == AC_WID_AUD_OUT || wid_type == AC_WID_AUD_IN) &&
-                   (wid_caps & AC_WCAP_FORMAT_OVRD)) {
-                       snd_iprintf(buffer, "  PCM:\n");
-                       print_pcm_caps(buffer, codec, nid);
+               case AC_WID_VOL_KNB:
+                       print_vol_knob(buffer, codec, nid);
+                       break;
+               case AC_WID_AUD_OUT:
+               case AC_WID_AUD_IN:
+                       print_audio_io(buffer, codec, nid, wid_type);
+                       if (wid_caps & AC_WCAP_DIGITAL)
+                               print_digital_conv(buffer, codec, nid);
+                       if (wid_caps & AC_WCAP_FORMAT_OVRD) {
+                               snd_iprintf(buffer, "  PCM:\n");
+                               print_pcm_caps(buffer, codec, nid);
+                       }
+                       break;
                }
 
+               if (wid_caps & AC_WCAP_UNSOL_CAP)
+                       print_unsol_cap(buffer, codec, nid);
+
                if (wid_caps & AC_WCAP_POWER)
-                       snd_iprintf(buffer, "  Power: 0x%x\n",
-                                   snd_hda_codec_read(codec, nid, 0,
-                                                      AC_VERB_GET_POWER_STATE,
-                                                      0));
-
-               if (wid_caps & AC_WCAP_CONN_LIST) {
-                       int c, curr = -1;
-                       if (conn_len > 1 && wid_type != AC_WID_AUD_MIX)
-                               curr = snd_hda_codec_read(codec, nid, 0,
-                                       AC_VERB_GET_CONNECT_SEL, 0);
-                       snd_iprintf(buffer, "  Connection: %d\n", conn_len);
-                       snd_iprintf(buffer, "    ");
-                       for (c = 0; c < conn_len; c++) {
-                               snd_iprintf(buffer, " 0x%02x", conn[c]);
-                               if (c == curr)
-                                       snd_iprintf(buffer, "*");
-                       }
-                       snd_iprintf(buffer, "\n");
-               }
+                       print_power_state(buffer, codec, nid);
+
+               if (wid_caps & AC_WCAP_DELAY)
+                       snd_iprintf(buffer, "  Delay: %d samples\n",
+                                   (wid_caps & AC_WCAP_DELAY) >>
+                                   AC_WCAP_DELAY_SHIFT);
+
+               if (wid_caps & AC_WCAP_CONN_LIST)
+                       print_conn_list(buffer, codec, nid, wid_type,
+                                       conn, conn_len);
+
+               if (wid_caps & AC_WCAP_PROC_WID)
+                       print_proc_caps(buffer, codec, nid);
+
+               /* NID 0x20 == Realtek Define Registers */
+               if (codec->vendor_id == 0x10ec && nid == 0x20)
+                       print_realtek_coef(buffer, codec, nid);
        }
        snd_hda_power_down(codec);
 }
index 196ad3c9405d9b35e65f418ff97a6fd2a87faf28..19f08846d6fcaa84a800a7fcf932b0b52858172c 100644 (file)
@@ -19,7 +19,6 @@
  *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA
  */
 
-#include <sound/driver.h>
 #include <linux/init.h>
 #include <linux/delay.h>
 #include <linux/slab.h>
@@ -79,6 +78,11 @@ struct ad198x_spec {
 #ifdef CONFIG_SND_HDA_POWER_SAVE
        struct hda_loopback_check loopback;
 #endif
+       /* for virtual master */
+       hda_nid_t vmaster_nid;
+       u32 vmaster_tlv[4];
+       const char **slave_vols;
+       const char **slave_sws;
 };
 
 /*
@@ -126,6 +130,32 @@ static int ad198x_init(struct hda_codec *codec)
        return 0;
 }
 
+static const char *ad_slave_vols[] = {
+       "Front Playback Volume",
+       "Surround Playback Volume",
+       "Center Playback Volume",
+       "LFE Playback Volume",
+       "Side Playback Volume",
+       "Headphone Playback Volume",
+       "Mono Playback Volume",
+       "Speaker Playback Volume",
+       "IEC958 Playback Volume",
+       NULL
+};
+
+static const char *ad_slave_sws[] = {
+       "Front Playback Switch",
+       "Surround Playback Switch",
+       "Center Playback Switch",
+       "LFE Playback Switch",
+       "Side Playback Switch",
+       "Headphone Playback Switch",
+       "Mono Playback Switch",
+       "Speaker Playback Switch",
+       "IEC958 Playback Switch",
+       NULL
+};
+
 static int ad198x_build_controls(struct hda_codec *codec)
 {
        struct ad198x_spec *spec = codec->spec;
@@ -147,6 +177,27 @@ static int ad198x_build_controls(struct hda_codec *codec)
                if (err < 0)
                        return err;
        }
+
+       /* if we have no master control, let's create it */
+       if (!snd_hda_find_mixer_ctl(codec, "Master Playback Volume")) {
+               snd_hda_set_vmaster_tlv(codec, spec->vmaster_nid,
+                                       HDA_OUTPUT, spec->vmaster_tlv);
+               err = snd_hda_add_vmaster(codec, "Master Playback Volume",
+                                         spec->vmaster_tlv,
+                                         (spec->slave_vols ?
+                                          spec->slave_vols : ad_slave_vols));
+               if (err < 0)
+                       return err;
+       }
+       if (!snd_hda_find_mixer_ctl(codec, "Master Playback Switch")) {
+               err = snd_hda_add_vmaster(codec, "Master Playback Switch",
+                                         NULL,
+                                         (spec->slave_sws ?
+                                          spec->slave_sws : ad_slave_sws));
+               if (err < 0)
+                       return err;
+       }
+
        return 0;
 }
 
@@ -370,7 +421,7 @@ static int ad198x_eapd_put(struct snd_kcontrol *kcontrol,
        int invert = (kcontrol->private_value >> 8) & 1;
        hda_nid_t nid = kcontrol->private_value & 0xff;
        unsigned int eapd;
-       eapd = ucontrol->value.integer.value[0];
+       eapd = !!ucontrol->value.integer.value[0];
        if (invert)
                eapd = !eapd;
        if (eapd == spec->cur_eapd)
@@ -833,27 +884,29 @@ static const char *ad1986a_models[AD1986A_MODELS] = {
 
 static struct snd_pci_quirk ad1986a_cfg_tbl[] = {
        SND_PCI_QUIRK(0x103c, 0x30af, "HP B2800", AD1986A_LAPTOP_EAPD),
-       SND_PCI_QUIRK(0x10de, 0xcb84, "ASUS A8N-VM", AD1986A_3STACK),
        SND_PCI_QUIRK(0x1043, 0x1153, "ASUS M9", AD1986A_LAPTOP_EAPD),
-       SND_PCI_QUIRK(0x1043, 0x1213, "ASUS A6J", AD1986A_LAPTOP_EAPD),
        SND_PCI_QUIRK(0x1043, 0x11f7, "ASUS U5A", AD1986A_LAPTOP_EAPD),
+       SND_PCI_QUIRK(0x1043, 0x1213, "ASUS A6J", AD1986A_LAPTOP_EAPD),
        SND_PCI_QUIRK(0x1043, 0x1263, "ASUS U5F", AD1986A_LAPTOP_EAPD),
        SND_PCI_QUIRK(0x1043, 0x1297, "ASUS Z62F", AD1986A_LAPTOP_EAPD),
        SND_PCI_QUIRK(0x1043, 0x12b3, "ASUS V1j", AD1986A_LAPTOP_EAPD),
        SND_PCI_QUIRK(0x1043, 0x1302, "ASUS W3j", AD1986A_LAPTOP_EAPD),
+       SND_PCI_QUIRK(0x1043, 0x1443, "ASUS VX1", AD1986A_LAPTOP),
        SND_PCI_QUIRK(0x1043, 0x1447, "ASUS A8J", AD1986A_3STACK),
        SND_PCI_QUIRK(0x1043, 0x817f, "ASUS P5", AD1986A_3STACK),
        SND_PCI_QUIRK(0x1043, 0x818f, "ASUS P5", AD1986A_LAPTOP),
        SND_PCI_QUIRK(0x1043, 0x81b3, "ASUS P5", AD1986A_3STACK),
        SND_PCI_QUIRK(0x1043, 0x81cb, "ASUS M2N", AD1986A_3STACK),
        SND_PCI_QUIRK(0x1043, 0x8234, "ASUS M2N", AD1986A_3STACK),
+       SND_PCI_QUIRK(0x10de, 0xcb84, "ASUS A8N-VM", AD1986A_3STACK),
+       SND_PCI_QUIRK(0x1179, 0xff40, "Toshiba", AD1986A_LAPTOP_EAPD),
        SND_PCI_QUIRK(0x144d, 0xb03c, "Samsung R55", AD1986A_3STACK),
        SND_PCI_QUIRK(0x144d, 0xc01e, "FSC V2060", AD1986A_LAPTOP),
        SND_PCI_QUIRK(0x144d, 0xc023, "Samsung X60", AD1986A_LAPTOP_EAPD),
        SND_PCI_QUIRK(0x144d, 0xc024, "Samsung R65", AD1986A_LAPTOP_EAPD),
        SND_PCI_QUIRK(0x144d, 0xc026, "Samsung X11", AD1986A_LAPTOP_EAPD),
-       SND_PCI_QUIRK(0x144d, 0xc504, "Samsung Q35", AD1986A_3STACK),
        SND_PCI_QUIRK(0x144d, 0xc027, "Samsung Q1", AD1986A_ULTRA),
+       SND_PCI_QUIRK(0x144d, 0xc504, "Samsung Q35", AD1986A_3STACK),
        SND_PCI_QUIRK(0x17aa, 0x1011, "Lenovo M55", AD1986A_LAPTOP),
        SND_PCI_QUIRK(0x17aa, 0x1017, "Lenovo A60", AD1986A_3STACK),
        SND_PCI_QUIRK(0x17aa, 0x2066, "Lenovo N100", AD1986A_LAPTOP_AUTOMUTE),
@@ -872,6 +925,13 @@ static struct hda_amp_list ad1986a_loopbacks[] = {
 };
 #endif
 
+static int is_jack_available(struct hda_codec *codec, hda_nid_t nid)
+{
+       unsigned int conf = snd_hda_codec_read(codec, nid, 0,
+                                              AC_VERB_GET_CONFIG_DEFAULT, 0);
+       return get_defcfg_connect(conf) != AC_JACK_PORT_NONE;
+}
+
 static int patch_ad1986a(struct hda_codec *codec)
 {
        struct ad198x_spec *spec;
@@ -898,6 +958,7 @@ static int patch_ad1986a(struct hda_codec *codec)
 #ifdef CONFIG_SND_HDA_POWER_SAVE
        spec->loopback.amplist = ad1986a_loopbacks;
 #endif
+       spec->vmaster_nid = 0x1b;
 
        codec->patch_ops = ad198x_patch_ops;
 
@@ -930,7 +991,8 @@ static int patch_ad1986a(struct hda_codec *codec)
                spec->multiout.max_channels = 2;
                spec->multiout.num_dacs = 1;
                spec->multiout.dac_nids = ad1986a_laptop_dac_nids;
-               spec->multiout.dig_out_nid = 0;
+               if (!is_jack_available(codec, 0x25))
+                       spec->multiout.dig_out_nid = 0;
                spec->input_mux = &ad1986a_laptop_eapd_capture_source;
                break;
        case AD1986A_LAPTOP_AUTOMUTE:
@@ -941,7 +1003,8 @@ static int patch_ad1986a(struct hda_codec *codec)
                spec->multiout.max_channels = 2;
                spec->multiout.num_dacs = 1;
                spec->multiout.dac_nids = ad1986a_laptop_dac_nids;
-               spec->multiout.dig_out_nid = 0;
+               if (!is_jack_available(codec, 0x25))
+                       spec->multiout.dig_out_nid = 0;
                spec->input_mux = &ad1986a_laptop_eapd_capture_source;
                codec->patch_ops.unsol_event = ad1986a_hp_unsol_event;
                codec->patch_ops.init = ad1986a_hp_init;
@@ -1020,6 +1083,8 @@ static int ad1983_spdif_route_put(struct snd_kcontrol *kcontrol, struct snd_ctl_
        struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
        struct ad198x_spec *spec = codec->spec;
 
+       if (ucontrol->value.enumerated.item[0] > 1)
+               return -EINVAL;
        if (spec->spdif_route != ucontrol->value.enumerated.item[0]) {
                spec->spdif_route = ucontrol->value.enumerated.item[0];
                snd_hda_codec_write_cache(codec, spec->multiout.dig_out_nid, 0,
@@ -1138,6 +1203,7 @@ static int patch_ad1983(struct hda_codec *codec)
 #ifdef CONFIG_SND_HDA_POWER_SAVE
        spec->loopback.amplist = ad1983_loopbacks;
 #endif
+       spec->vmaster_nid = 0x05;
 
        codec->patch_ops = ad198x_patch_ops;
 
@@ -1496,14 +1562,14 @@ static const char *ad1981_models[AD1981_MODELS] = {
 };
 
 static struct snd_pci_quirk ad1981_cfg_tbl[] = {
+       SND_PCI_QUIRK(0x1014, 0x0597, "Lenovo Z60", AD1981_THINKPAD),
        /* All HP models */
        SND_PCI_QUIRK(0x103c, 0, "HP nx", AD1981_HP),
-       /* HP nx6320 (reversed SSID, H/W bug) */
-       SND_PCI_QUIRK(0x30b0, 0x103c, "HP nx6320", AD1981_HP),
+       SND_PCI_QUIRK(0x1179, 0x0001, "Toshiba U205", AD1981_TOSHIBA),
        /* Lenovo Thinkpad T60/X60/Z6xx */
        SND_PCI_QUIRK(0x17aa, 0, "Lenovo Thinkpad", AD1981_THINKPAD),
-       SND_PCI_QUIRK(0x1014, 0x0597, "Lenovo Z60", AD1981_THINKPAD),
-       SND_PCI_QUIRK(0x1179, 0x0001, "Toshiba U205", AD1981_TOSHIBA),
+       /* HP nx6320 (reversed SSID, H/W bug) */
+       SND_PCI_QUIRK(0x30b0, 0x103c, "HP nx6320", AD1981_HP),
        {}
 };
 
@@ -1534,6 +1600,7 @@ static int patch_ad1981(struct hda_codec *codec)
 #ifdef CONFIG_SND_HDA_POWER_SAVE
        spec->loopback.amplist = ad1981_loopbacks;
 #endif
+       spec->vmaster_nid = 0x05;
 
        codec->patch_ops = ad198x_patch_ops;
 
@@ -1908,7 +1975,6 @@ static struct snd_kcontrol_new ad1988_capture_mixers[] = {
                .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
                /* The multiple "Capture Source" controls confuse alsamixer
                 * So call somewhat different..
-                * FIXME: the controls appear in the "playback" view!
                 */
                /* .name = "Capture Source", */
                .name = "Input Source",
@@ -1965,6 +2031,8 @@ static int ad1988_spdif_playback_source_put(struct snd_kcontrol *kcontrol,
        int change;
 
        val = ucontrol->value.enumerated.item[0];
+       if (val > 3)
+               return -EINVAL;
        if (!val) {
                sel = snd_hda_codec_read(codec, 0x1d, 0,
                                         AC_VERB_GET_AMP_GAIN_MUTE,
@@ -2079,6 +2147,8 @@ static struct hda_verb ad1988_6stack_init_verbs[] = {
        {0x17, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80},
        {0x3c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
        {0x34, AC_VERB_SET_CONNECT_SEL, 0x0},
+       /* Analog CD Input */
+       {0x18, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN},
 
        { }
 };
@@ -2720,8 +2790,8 @@ static const char *ad1988_models[AD1988_MODEL_LAST] = {
 };
 
 static struct snd_pci_quirk ad1988_cfg_tbl[] = {
-       SND_PCI_QUIRK(0x1043, 0x81f6, "Asus M2N-SLI", AD1988_6STACK_DIG),
        SND_PCI_QUIRK(0x1043, 0x81ec, "Asus P5B-DLX", AD1988_6STACK_DIG),
+       SND_PCI_QUIRK(0x1043, 0x81f6, "Asus M2N-SLI", AD1988_6STACK_DIG),
        {}
 };
 
@@ -2843,6 +2913,7 @@ static int patch_ad1988(struct hda_codec *codec)
 #ifdef CONFIG_SND_HDA_POWER_SAVE
        spec->loopback.amplist = ad1988_loopbacks;
 #endif
+       spec->vmaster_nid = 0x04;
 
        return 0;
 }
@@ -2919,7 +2990,6 @@ static struct snd_kcontrol_new ad1884_base_mixers[] = {
                .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
                /* The multiple "Capture Source" controls confuse alsamixer
                 * So call somewhat different..
-                * FIXME: the controls appear in the "playback" view!
                 */
                /* .name = "Capture Source", */
                .name = "Input Source",
@@ -3009,6 +3079,20 @@ static struct hda_amp_list ad1884_loopbacks[] = {
 };
 #endif
 
+static const char *ad1884_slave_vols[] = {
+       "PCM Playback Volume",
+       "Mic Playback Volume",
+       "Mono Playback Volume",
+       "Front Mic Playback Volume",
+       "Mic Playback Volume",
+       "CD Playback Volume",
+       "Internal Mic Playback Volume",
+       "Docking Mic Playback Volume"
+       "Beep Playback Volume",
+       "IEC958 Playback Volume",
+       NULL
+};
+
 static int patch_ad1884(struct hda_codec *codec)
 {
        struct ad198x_spec *spec;
@@ -3036,6 +3120,9 @@ static int patch_ad1884(struct hda_codec *codec)
 #ifdef CONFIG_SND_HDA_POWER_SAVE
        spec->loopback.amplist = ad1884_loopbacks;
 #endif
+       spec->vmaster_nid = 0x04;
+       /* we need to cover all playback volumes */
+       spec->slave_vols = ad1884_slave_vols;
 
        codec->patch_ops = ad198x_patch_ops;
 
@@ -3054,6 +3141,20 @@ static struct hda_input_mux ad1984_thinkpad_capture_source = {
        },
 };
 
+
+/*
+ * Dell Precision T3400
+ */
+static struct hda_input_mux ad1984_dell_desktop_capture_source = {
+       .num_items = 3,
+       .items = {
+               { "Front Mic", 0x0 },
+               { "Line-In", 0x1 },
+               { "Mix", 0x3 },
+       },
+};
+
+
 static struct snd_kcontrol_new ad1984_thinkpad_mixers[] = {
        HDA_CODEC_VOLUME("PCM Playback Volume", 0x04, 0x0, HDA_OUTPUT),
        /* HDA_CODEC_VOLUME_IDX("PCM Playback Volume", 1, 0x03, 0x0, HDA_OUTPUT), */
@@ -3078,7 +3179,6 @@ static struct snd_kcontrol_new ad1984_thinkpad_mixers[] = {
                .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
                /* The multiple "Capture Source" controls confuse alsamixer
                 * So call somewhat different..
-                * FIXME: the controls appear in the "playback" view!
                 */
                /* .name = "Capture Source", */
                .name = "Input Source",
@@ -3087,6 +3187,16 @@ static struct snd_kcontrol_new ad1984_thinkpad_mixers[] = {
                .get = ad198x_mux_enum_get,
                .put = ad198x_mux_enum_put,
        },
+       /* SPDIF controls */
+       HDA_CODEC_VOLUME("IEC958 Playback Volume", 0x1b, 0x0, HDA_OUTPUT),
+       {
+               .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+               .name = SNDRV_CTL_NAME_IEC958("",PLAYBACK,NONE) "Source",
+               /* identical with ad1983 */
+               .info = ad1983_spdif_route_info,
+               .get = ad1983_spdif_route_get,
+               .put = ad1983_spdif_route_put,
+       },
        { } /* end */
 };
 
@@ -3104,6 +3214,44 @@ static struct hda_verb ad1984_thinkpad_init_verbs[] = {
        { } /* end */
 };
 
+/*
+ * Dell Precision T3400
+ */
+static struct snd_kcontrol_new ad1984_dell_desktop_mixers[] = {
+       HDA_CODEC_VOLUME("PCM Playback Volume", 0x04, 0x0, HDA_OUTPUT),
+       HDA_CODEC_MUTE("Headphone Playback Switch", 0x11, 0x0, HDA_OUTPUT),
+       HDA_CODEC_MUTE("Speaker Playback Switch", 0x12, 0x0, HDA_OUTPUT),
+       HDA_CODEC_VOLUME_MONO("Mono Playback Volume", 0x13, 1, 0x0, HDA_OUTPUT),
+       HDA_CODEC_MUTE_MONO("Mono Playback Switch", 0x13, 1, 0x0, HDA_OUTPUT),
+       HDA_CODEC_VOLUME("Front Mic Playback Volume", 0x20, 0x00, HDA_INPUT),
+       HDA_CODEC_MUTE("Front Mic Playback Switch", 0x20, 0x00, HDA_INPUT),
+       HDA_CODEC_VOLUME("Line-In Playback Volume", 0x20, 0x01, HDA_INPUT),
+       HDA_CODEC_MUTE("Line-In Playback Switch", 0x20, 0x01, HDA_INPUT),
+       /*
+       HDA_CODEC_VOLUME("PC Speaker Playback Volume", 0x20, 0x03, HDA_INPUT),
+       HDA_CODEC_MUTE("PC Speaker Playback Switch", 0x20, 0x03, HDA_INPUT),
+       */
+       HDA_CODEC_VOLUME("Line-In Boost", 0x15, 0x0, HDA_INPUT),
+       HDA_CODEC_VOLUME("Front Mic Boost", 0x14, 0x0, HDA_INPUT),
+       HDA_CODEC_VOLUME("Capture Volume", 0x0c, 0x0, HDA_OUTPUT),
+       HDA_CODEC_MUTE("Capture Switch", 0x0c, 0x0, HDA_OUTPUT),
+       HDA_CODEC_VOLUME_IDX("Capture Volume", 1, 0x0d, 0x0, HDA_OUTPUT),
+       HDA_CODEC_MUTE_IDX("Capture Switch", 1, 0x0d, 0x0, HDA_OUTPUT),
+       {
+               .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+               /* The multiple "Capture Source" controls confuse alsamixer
+                * So call somewhat different..
+                */
+               /* .name = "Capture Source", */
+               .name = "Input Source",
+               .count = 2,
+               .info = ad198x_mux_enum_info,
+               .get = ad198x_mux_enum_get,
+               .put = ad198x_mux_enum_put,
+       },
+       { } /* end */
+};
+
 /* Digial MIC ADC NID 0x05 + 0x06 */
 static int ad1984_pcm_dmic_prepare(struct hda_pcm_stream *hinfo,
                                   struct hda_codec *codec,
@@ -3157,17 +3305,20 @@ static int ad1984_build_pcms(struct hda_codec *codec)
 enum {
        AD1984_BASIC,
        AD1984_THINKPAD,
+       AD1984_DELL_DESKTOP,
        AD1984_MODELS
 };
 
 static const char *ad1984_models[AD1984_MODELS] = {
        [AD1984_BASIC]          = "basic",
        [AD1984_THINKPAD]       = "thinkpad",
+       [AD1984_DELL_DESKTOP]   = "dell_desktop",
 };
 
 static struct snd_pci_quirk ad1984_cfg_tbl[] = {
        /* Lenovo Thinkpad T61/X61 */
        SND_PCI_QUIRK(0x17aa, 0, "Lenovo Thinkpad", AD1984_THINKPAD),
+       SND_PCI_QUIRK(0x1028, 0x0214, "Dell T3400", AD1984_DELL_DESKTOP),
        {}
 };
 
@@ -3189,11 +3340,16 @@ static int patch_ad1984(struct hda_codec *codec)
                codec->patch_ops.build_pcms = ad1984_build_pcms;
                break;
        case AD1984_THINKPAD:
-               spec->multiout.dig_out_nid = 0;
+               spec->multiout.dig_out_nid = AD1884_SPDIF_OUT;
                spec->input_mux = &ad1984_thinkpad_capture_source;
                spec->mixers[0] = ad1984_thinkpad_mixers;
                spec->init_verbs[spec->num_init_verbs++] = ad1984_thinkpad_init_verbs;
                break;
+       case AD1984_DELL_DESKTOP:
+               spec->multiout.dig_out_nid = 0;
+               spec->input_mux = &ad1984_dell_desktop_capture_source;
+               spec->mixers[0] = ad1984_dell_desktop_mixers;
+               break;
        }
        return 0;
 }
@@ -3267,7 +3423,6 @@ static struct snd_kcontrol_new ad1882_base_mixers[] = {
                .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
                /* The multiple "Capture Source" controls confuse alsamixer
                 * So call somewhat different..
-                * FIXME: the controls appear in the "playback" view!
                 */
                /* .name = "Capture Source", */
                .name = "Input Source",
@@ -3468,6 +3623,7 @@ static int patch_ad1882(struct hda_codec *codec)
 #ifdef CONFIG_SND_HDA_POWER_SAVE
        spec->loopback.amplist = ad1882_loopbacks;
 #endif
+       spec->vmaster_nid = 0x04;
 
        codec->patch_ops = ad198x_patch_ops;
 
index fbb8969dc559c50c56bb6f983850d186c637eaba..9a8bb4ce3f8da103847a7dce8e1bb8c01d13316e 100644 (file)
@@ -21,7 +21,6 @@
  *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA
  */
 
-#include <sound/driver.h>
 #include <linux/init.h>
 #include <linux/delay.h>
 #include <linux/slab.h>
@@ -158,6 +157,6 @@ struct hda_codec_preset snd_hda_preset_atihdmi[] = {
        { .id = 0x1002793c, .name = "ATI RS600 HDMI", .patch = patch_atihdmi },
        { .id = 0x10027919, .name = "ATI RS600 HDMI", .patch = patch_atihdmi },
        { .id = 0x1002791a, .name = "ATI RS690/780 HDMI", .patch = patch_atihdmi },
-       { .id = 0x1002aa01, .name = "ATI R600 HDMI", .patch = patch_atihdmi },
+       { .id = 0x1002aa01, .name = "ATI R6xx HDMI", .patch = patch_atihdmi },
        {} /* terminator */
 };
index 6c54793bf424cf4e52d4a922ad4532fa1726a613..3d6097ba1d68fb98f851fe1668effd6fe1d0a4ba 100644 (file)
@@ -21,7 +21,6 @@
  *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA
  */
 
-#include <sound/driver.h>
 #include <linux/init.h>
 #include <linux/delay.h>
 #include <linux/slab.h>
@@ -186,7 +185,6 @@ static struct snd_kcontrol_new cmi9880_basic_mixer[] = {
                .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
                /* The multiple "Capture Source" controls confuse alsamixer
                 * So call somewhat different..
-                * FIXME: the controls appear in the "playback" view!
                 */
                /* .name = "Capture Source", */
                .name = "Input Source",
index 6aa073986747bd20a26c0c7a4e1e154a173e5c39..f6dd51cda7b268de749135bb3d0aef618d2acce9 100644 (file)
@@ -20,7 +20,6 @@
  *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA
  */
 
-#include <sound/driver.h>
 #include <linux/init.h>
 #include <linux/delay.h>
 #include <linux/slab.h>
@@ -65,6 +64,11 @@ struct conexant_spec {
        hda_nid_t *adc_nids;
        hda_nid_t dig_in_nid;           /* digital-in NID; optional */
 
+       unsigned int cur_adc_idx;
+       hda_nid_t cur_adc;
+       unsigned int cur_adc_stream_tag;
+       unsigned int cur_adc_format;
+
        /* capture source */
        const struct hda_input_mux *input_mux;
        hda_nid_t *capsrc_nids;
@@ -218,6 +222,41 @@ static struct hda_pcm_stream conexant_pcm_digital_capture = {
        /* NID is set in alc_build_pcms */
 };
 
+static int cx5051_capture_pcm_prepare(struct hda_pcm_stream *hinfo,
+                                     struct hda_codec *codec,
+                                     unsigned int stream_tag,
+                                     unsigned int format,
+                                     struct snd_pcm_substream *substream)
+{
+       struct conexant_spec *spec = codec->spec;
+       spec->cur_adc = spec->adc_nids[spec->cur_adc_idx];
+       spec->cur_adc_stream_tag = stream_tag;
+       spec->cur_adc_format = format;
+       snd_hda_codec_setup_stream(codec, spec->cur_adc, stream_tag, 0, format);
+       return 0;
+}
+
+static int cx5051_capture_pcm_cleanup(struct hda_pcm_stream *hinfo,
+                                     struct hda_codec *codec,
+                                     struct snd_pcm_substream *substream)
+{
+       struct conexant_spec *spec = codec->spec;
+       snd_hda_codec_setup_stream(codec, spec->cur_adc, 0, 0, 0);
+       spec->cur_adc = 0;
+       return 0;
+}
+
+static struct hda_pcm_stream cx5051_pcm_analog_capture = {
+       .substreams = 1,
+       .channels_min = 2,
+       .channels_max = 2,
+       .nid = 0, /* fill later */
+       .ops = {
+               .prepare = cx5051_capture_pcm_prepare,
+               .cleanup = cx5051_capture_pcm_cleanup
+       },
+};
+
 static int conexant_build_pcms(struct hda_codec *codec)
 {
        struct conexant_spec *spec = codec->spec;
@@ -232,7 +271,12 @@ static int conexant_build_pcms(struct hda_codec *codec)
                spec->multiout.max_channels;
        info->stream[SNDRV_PCM_STREAM_PLAYBACK].nid =
                spec->multiout.dac_nids[0];
-       info->stream[SNDRV_PCM_STREAM_CAPTURE] = conexant_pcm_analog_capture;
+       if (codec->vendor_id == 0x14f15051)
+               info->stream[SNDRV_PCM_STREAM_CAPTURE] =
+                       cx5051_pcm_analog_capture;
+       else
+               info->stream[SNDRV_PCM_STREAM_CAPTURE] =
+                       conexant_pcm_analog_capture;
        info->stream[SNDRV_PCM_STREAM_CAPTURE].substreams = spec->num_adc_nids;
        info->stream[SNDRV_PCM_STREAM_CAPTURE].nid = spec->adc_nids[0];
 
@@ -373,7 +417,7 @@ static int cxt_eapd_put(struct snd_kcontrol *kcontrol,
        hda_nid_t nid = kcontrol->private_value & 0xff;
        unsigned int eapd;
 
-       eapd = ucontrol->value.integer.value[0];
+       eapd = !!ucontrol->value.integer.value[0];
        if (invert)
                eapd = !eapd;
        if (eapd == spec->cur_eapd)
@@ -454,7 +498,16 @@ static struct hda_input_mux cxt5045_capture_source = {
        .num_items = 2,
        .items = {
                { "IntMic", 0x1 },
-               { "LineIn", 0x2 },
+               { "ExtMic", 0x2 },
+       }
+};
+
+static struct hda_input_mux cxt5045_capture_source_benq = {
+       .num_items = 3,
+       .items = {
+               { "IntMic", 0x1 },
+               { "ExtMic", 0x2 },
+               { "LineIn", 0x3 },
        }
 };
 
@@ -577,6 +630,15 @@ static struct snd_kcontrol_new cxt5045_mixers[] = {
        {}
 };
 
+static struct snd_kcontrol_new cxt5045_benq_mixers[] = {
+       HDA_CODEC_VOLUME("Line In Capture Volume", 0x1a, 0x03, HDA_INPUT),
+       HDA_CODEC_MUTE("Line In Capture Switch", 0x1a, 0x03, HDA_INPUT),
+       HDA_CODEC_VOLUME("Line In Playback Volume", 0x17, 0x3, HDA_INPUT),
+       HDA_CODEC_MUTE("Line In Playback Switch", 0x17, 0x3, HDA_INPUT),
+
+       {}
+};
+
 static struct hda_verb cxt5045_init_verbs[] = {
        /* Line in, Mic */
        {0x12, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN },
@@ -602,6 +664,30 @@ static struct hda_verb cxt5045_init_verbs[] = {
        { } /* end */
 };
 
+static struct hda_verb cxt5045_benq_init_verbs[] = {
+       /* Int Mic, Mic */
+       {0x12, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN|AC_PINCTL_VREF_80 },
+       {0x14, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN|AC_PINCTL_VREF_80 },
+       /* Line In,HP, Amp  */
+       {0x10, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
+       {0x10, AC_VERB_SET_CONNECT_SEL, 0x1},
+       {0x11, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN},
+       {0x11, AC_VERB_SET_CONNECT_SEL, 0x1},
+       {0x17, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
+       {0x17, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
+       {0x17, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(2)},
+       {0x17, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(3)},
+       {0x17, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(4)},
+       /* Record selector: Int mic */
+       {0x1a, AC_VERB_SET_CONNECT_SEL, 0x1},
+       {0x1a, AC_VERB_SET_AMP_GAIN_MUTE,
+        AC_AMP_SET_INPUT|AC_AMP_SET_RIGHT|AC_AMP_SET_LEFT|0x17},
+       /* SPDIF route: PCM */
+       {0x13, AC_VERB_SET_CONNECT_SEL, 0x0},
+       /* EAPD */
+       {0x10, AC_VERB_SET_EAPD_BTLENABLE, 0x2}, /* default on */
+       { } /* end */
+};
 
 static struct hda_verb cxt5045_hp_sense_init_verbs[] = {
        /* pin sensing on HP jack */
@@ -740,8 +826,10 @@ static int cxt5045_init(struct hda_codec *codec)
 
 
 enum {
-       CXT5045_LAPTOP,  /* Laptops w/ EAPD support */
-       CXT5045_FUJITSU, /* Laptops w/ EAPD support */ 
+       CXT5045_LAPTOP_HPSENSE,
+       CXT5045_LAPTOP_MICSENSE,
+       CXT5045_LAPTOP_HPMICSENSE,
+       CXT5045_BENQ,
 #ifdef CONFIG_SND_DEBUG
        CXT5045_TEST,
 #endif
@@ -749,23 +837,35 @@ enum {
 };
 
 static const char *cxt5045_models[CXT5045_MODELS] = {
-       [CXT5045_LAPTOP]        = "laptop",
-       [CXT5045_FUJITSU]       = "fujitsu",
+       [CXT5045_LAPTOP_HPSENSE]        = "laptop-hpsense",
+       [CXT5045_LAPTOP_MICSENSE]       = "laptop-micsense",
+       [CXT5045_LAPTOP_HPMICSENSE]     = "laptop-hpmicsense",
+       [CXT5045_BENQ]                  = "benq",
 #ifdef CONFIG_SND_DEBUG
        [CXT5045_TEST]          = "test",
 #endif
 };
 
 static struct snd_pci_quirk cxt5045_cfg_tbl[] = {
-       SND_PCI_QUIRK(0x103c, 0x30b7, "HP DV6000Z", CXT5045_LAPTOP),
-       SND_PCI_QUIRK(0x103c, 0x30bb, "HP DV8000", CXT5045_LAPTOP),
-       SND_PCI_QUIRK(0x103c, 0x30b2, "HP DV Series", CXT5045_LAPTOP),
-       SND_PCI_QUIRK(0x103c, 0x30b5, "HP DV2120", CXT5045_LAPTOP),
-       SND_PCI_QUIRK(0x103c, 0x30cd, "HP DV Series", CXT5045_LAPTOP),
-       SND_PCI_QUIRK(0x103c, 0x30d9, "HP Spartan", CXT5045_LAPTOP),
-       SND_PCI_QUIRK(0x1734, 0x10ad, "Fujitsu Si1520", CXT5045_FUJITSU),
-       SND_PCI_QUIRK(0x1734, 0x10cb, "Fujitsu Si3515", CXT5045_LAPTOP),
-       SND_PCI_QUIRK(0x8086, 0x2111, "Conexant Reference board", CXT5045_LAPTOP),
+       SND_PCI_QUIRK(0x103c, 0x30a5, "HP", CXT5045_LAPTOP_HPSENSE),
+       SND_PCI_QUIRK(0x103c, 0x30b2, "HP DV Series", CXT5045_LAPTOP_HPSENSE),
+       SND_PCI_QUIRK(0x103c, 0x30b5, "HP DV2120", CXT5045_LAPTOP_HPSENSE),
+       SND_PCI_QUIRK(0x103c, 0x30b7, "HP DV6000Z", CXT5045_LAPTOP_HPSENSE),
+       SND_PCI_QUIRK(0x103c, 0x30bb, "HP DV8000", CXT5045_LAPTOP_HPSENSE),
+       SND_PCI_QUIRK(0x103c, 0x30cd, "HP DV Series", CXT5045_LAPTOP_HPSENSE),
+       SND_PCI_QUIRK(0x103c, 0x30cf, "HP DV9533EG", CXT5045_LAPTOP_HPSENSE),
+       SND_PCI_QUIRK(0x103c, 0x30d5, "HP 530", CXT5045_LAPTOP_HPSENSE),
+       SND_PCI_QUIRK(0x103c, 0x30d9, "HP Spartan", CXT5045_LAPTOP_HPSENSE),
+       SND_PCI_QUIRK(0x152d, 0x0753, "Benq R55E", CXT5045_BENQ),
+       SND_PCI_QUIRK(0x1734, 0x10ad, "Fujitsu Si1520", CXT5045_LAPTOP_MICSENSE),
+       SND_PCI_QUIRK(0x1734, 0x10cb, "Fujitsu Si3515", CXT5045_LAPTOP_HPMICSENSE),
+       SND_PCI_QUIRK(0x1734, 0x110e, "Fujitsu V5505", CXT5045_LAPTOP_HPSENSE),
+       SND_PCI_QUIRK(0x1509, 0x1e40, "FIC", CXT5045_LAPTOP_HPMICSENSE),
+       SND_PCI_QUIRK(0x1509, 0x2f05, "FIC", CXT5045_LAPTOP_HPMICSENSE),
+       SND_PCI_QUIRK(0x1509, 0x2f06, "FIC", CXT5045_LAPTOP_HPMICSENSE),
+       SND_PCI_QUIRK(0x1631, 0xc106, "Packard Bell", CXT5045_LAPTOP_HPMICSENSE),
+       SND_PCI_QUIRK(0x1631, 0xc107, "Packard Bell", CXT5045_LAPTOP_HPMICSENSE),
+       SND_PCI_QUIRK(0x8086, 0x2111, "Conexant Reference board", CXT5045_LAPTOP_HPSENSE),
        {}
 };
 
@@ -803,7 +903,7 @@ static int patch_cxt5045(struct hda_codec *codec)
                                                  cxt5045_models,
                                                  cxt5045_cfg_tbl);
        switch (board_config) {
-       case CXT5045_LAPTOP:
+       case CXT5045_LAPTOP_HPSENSE:
                codec->patch_ops.unsol_event = cxt5045_hp_unsol_event;
                spec->input_mux = &cxt5045_capture_source;
                spec->num_init_verbs = 2;
@@ -811,20 +911,53 @@ static int patch_cxt5045(struct hda_codec *codec)
                spec->mixers[0] = cxt5045_mixers;
                codec->patch_ops.init = cxt5045_init;
                break;
-       case CXT5045_FUJITSU:
+       case CXT5045_LAPTOP_MICSENSE:
                spec->input_mux = &cxt5045_capture_source;
                spec->num_init_verbs = 2;
                spec->init_verbs[1] = cxt5045_mic_sense_init_verbs;
                spec->mixers[0] = cxt5045_mixers;
                codec->patch_ops.init = cxt5045_init;
                break;
+       default:
+       case CXT5045_LAPTOP_HPMICSENSE:
+               codec->patch_ops.unsol_event = cxt5045_hp_unsol_event;
+               spec->input_mux = &cxt5045_capture_source;
+               spec->num_init_verbs = 3;
+               spec->init_verbs[1] = cxt5045_hp_sense_init_verbs;
+               spec->init_verbs[2] = cxt5045_mic_sense_init_verbs;
+               spec->mixers[0] = cxt5045_mixers;
+               codec->patch_ops.init = cxt5045_init;
+               break;
+       case CXT5045_BENQ:
+               codec->patch_ops.unsol_event = cxt5045_hp_unsol_event;
+               spec->input_mux = &cxt5045_capture_source_benq;
+               spec->num_init_verbs = 1;
+               spec->init_verbs[0] = cxt5045_benq_init_verbs;
+               spec->mixers[0] = cxt5045_mixers;
+               spec->mixers[1] = cxt5045_benq_mixers;
+               spec->num_mixers = 2;
+               codec->patch_ops.init = cxt5045_init;
+               break;
 #ifdef CONFIG_SND_DEBUG
        case CXT5045_TEST:
                spec->input_mux = &cxt5045_test_capture_source;
                spec->mixers[0] = cxt5045_test_mixer;
                spec->init_verbs[0] = cxt5045_test_init_verbs;
+               break;
+               
 #endif 
        }
+
+       /*
+        * Fix max PCM level to 0 dB
+        * (originall it has 0x2b steps with 0dB offset 0x14)
+        */
+       snd_hda_override_amp_caps(codec, 0x17, HDA_INPUT,
+                                 (0x14 << AC_AMPCAP_OFFSET_SHIFT) |
+                                 (0x14 << AC_AMPCAP_NUM_STEPS_SHIFT) |
+                                 (0x05 << AC_AMPCAP_STEP_SIZE_SHIFT) |
+                                 (1 << AC_AMPCAP_MUTE_SHIFT));
+
        return 0;
 }
 
@@ -933,13 +1066,13 @@ static void cxt5047_hp2_automute(struct hda_codec *codec)
 static void cxt5047_hp_automic(struct hda_codec *codec)
 {
        static struct hda_verb mic_jack_on[] = {
-               {0x15, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080},
-               {0x17, AC_VERB_SET_AMP_GAIN_MUTE, 0xb000},
+               {0x15, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
+               {0x17, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
                {}
        };
        static struct hda_verb mic_jack_off[] = {
-               {0x17, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080},
-               {0x15, AC_VERB_SET_AMP_GAIN_MUTE, 0xb000},
+               {0x17, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
+               {0x15, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
                {}
        };
        unsigned int present;
@@ -956,8 +1089,7 @@ static void cxt5047_hp_automic(struct hda_codec *codec)
 static void cxt5047_hp_unsol_event(struct hda_codec *codec,
                                  unsigned int res)
 {
-       res >>= 26;
-       switch (res) {
+       switch (res >> 26) {
        case CONEXANT_HP_EVENT:
                cxt5047_hp_automute(codec);
                break;
@@ -1166,6 +1298,17 @@ 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),
+
        { } /* end */
 };
 
@@ -1255,9 +1398,9 @@ static const char *cxt5047_models[CXT5047_MODELS] = {
 
 static struct snd_pci_quirk cxt5047_cfg_tbl[] = {
        SND_PCI_QUIRK(0x103c, 0x30a0, "HP DV1000", CXT5047_LAPTOP),
+       SND_PCI_QUIRK(0x103c, 0x30a5, "HP DV5200T/DV8000T", CXT5047_LAPTOP_HP),
        SND_PCI_QUIRK(0x103c, 0x30b2, "HP DV2000T/DV3000T", CXT5047_LAPTOP),
        SND_PCI_QUIRK(0x103c, 0x30b5, "HP DV2000Z", CXT5047_LAPTOP),
-       SND_PCI_QUIRK(0x103c, 0x30a5, "HP DV5200T/DV8000T", CXT5047_LAPTOP_HP),
        SND_PCI_QUIRK(0x1179, 0xff31, "Toshiba P100", CXT5047_LAPTOP_EAPD),
        {}
 };
@@ -1324,10 +1467,260 @@ static int patch_cxt5047(struct hda_codec *codec)
        return 0;
 }
 
+/* Conexant 5051 specific */
+static hda_nid_t cxt5051_dac_nids[1] = { 0x10 };
+static hda_nid_t cxt5051_adc_nids[2] = { 0x14, 0x15 };
+#define CXT5051_SPDIF_OUT      0x1C
+#define CXT5051_PORTB_EVENT    0x38
+#define CXT5051_PORTC_EVENT    0x39
+
+static struct hda_channel_mode cxt5051_modes[1] = {
+       { 2, NULL },
+};
+
+static void cxt5051_update_speaker(struct hda_codec *codec)
+{
+       struct conexant_spec *spec = codec->spec;
+       unsigned int pinctl;
+       pinctl = (!spec->hp_present && spec->cur_eapd) ? PIN_OUT : 0;
+       snd_hda_codec_write(codec, 0x1a, 0, AC_VERB_SET_PIN_WIDGET_CONTROL,
+                           pinctl);
+}
+
+/* turn on/off EAPD (+ mute HP) as a master switch */
+static int cxt5051_hp_master_sw_put(struct snd_kcontrol *kcontrol,
+                                   struct snd_ctl_elem_value *ucontrol)
+{
+       struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
+
+       if (!cxt_eapd_put(kcontrol, ucontrol))
+               return 0;
+       cxt5051_update_speaker(codec);
+       return 1;
+}
+
+/* toggle input of built-in and mic jack appropriately */
+static void cxt5051_portb_automic(struct hda_codec *codec)
+{
+       unsigned int present;
+
+       present = snd_hda_codec_read(codec, 0x17, 0,
+                                    AC_VERB_GET_PIN_SENSE, 0) &
+               AC_PINSENSE_PRESENCE;
+       snd_hda_codec_write(codec, 0x14, 0,
+                           AC_VERB_SET_CONNECT_SEL,
+                           present ? 0x01 : 0x00);
+}
+
+/* switch the current ADC according to the jack state */
+static void cxt5051_portc_automic(struct hda_codec *codec)
+{
+       struct conexant_spec *spec = codec->spec;
+       unsigned int present;
+       hda_nid_t new_adc;
+
+       present = snd_hda_codec_read(codec, 0x18, 0,
+                                    AC_VERB_GET_PIN_SENSE, 0) &
+               AC_PINSENSE_PRESENCE;
+       if (present)
+               spec->cur_adc_idx = 1;
+       else
+               spec->cur_adc_idx = 0;
+       new_adc = spec->adc_nids[spec->cur_adc_idx];
+       if (spec->cur_adc && spec->cur_adc != new_adc) {
+               /* stream is running, let's swap the current ADC */
+               snd_hda_codec_setup_stream(codec, spec->cur_adc, 0, 0, 0);
+               spec->cur_adc = new_adc;
+               snd_hda_codec_setup_stream(codec, new_adc,
+                                          spec->cur_adc_stream_tag, 0,
+                                          spec->cur_adc_format);
+       }
+}
+
+/* mute internal speaker if HP is plugged */
+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;
+       cxt5051_update_speaker(codec);
+}
+
+/* unsolicited event for HP jack sensing */
+static void cxt5051_hp_unsol_event(struct hda_codec *codec,
+                                  unsigned int res)
+{
+       switch (res >> 26) {
+       case CONEXANT_HP_EVENT:
+               cxt5051_hp_automute(codec);
+               break;
+       case CXT5051_PORTB_EVENT:
+               cxt5051_portb_automic(codec);
+               break;
+       case CXT5051_PORTC_EVENT:
+               cxt5051_portc_automic(codec);
+               break;
+       }
+}
+
+static struct snd_kcontrol_new cxt5051_mixers[] = {
+       HDA_CODEC_VOLUME("Internal Mic Volume", 0x14, 0x00, HDA_INPUT),
+       HDA_CODEC_MUTE("Internal Mic Switch", 0x14, 0x00, HDA_INPUT),
+       HDA_CODEC_VOLUME("External Mic Volume", 0x14, 0x01, HDA_INPUT),
+       HDA_CODEC_MUTE("External Mic Switch", 0x14, 0x01, HDA_INPUT),
+       HDA_CODEC_VOLUME("Docking Mic Volume", 0x15, 0x00, HDA_INPUT),
+       HDA_CODEC_MUTE("Docking Mic Switch", 0x15, 0x00, HDA_INPUT),
+       HDA_CODEC_VOLUME("Master Playback Volume", 0x10, 0x00, HDA_OUTPUT),
+       {
+               .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+               .name = "Master Playback Switch",
+               .info = cxt_eapd_info,
+               .get = cxt_eapd_get,
+               .put = cxt5051_hp_master_sw_put,
+               .private_value = 0x1a,
+       },
+
+       {}
+};
+
+static struct snd_kcontrol_new cxt5051_hp_mixers[] = {
+       HDA_CODEC_VOLUME("Internal Mic Volume", 0x14, 0x00, HDA_INPUT),
+       HDA_CODEC_MUTE("Internal Mic Switch", 0x14, 0x00, HDA_INPUT),
+       HDA_CODEC_VOLUME("External Mic Volume", 0x15, 0x00, HDA_INPUT),
+       HDA_CODEC_MUTE("External Mic Switch", 0x15, 0x00, HDA_INPUT),
+       HDA_CODEC_VOLUME("Master Playback Volume", 0x10, 0x00, HDA_OUTPUT),
+       {
+               .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+               .name = "Master Playback Switch",
+               .info = cxt_eapd_info,
+               .get = cxt_eapd_get,
+               .put = cxt5051_hp_master_sw_put,
+               .private_value = 0x1a,
+       },
+
+       {}
+};
+
+static struct hda_verb cxt5051_init_verbs[] = {
+       /* Line in, Mic */
+       {0x17, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0) | 0x03},
+       {0x17, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80},
+       {0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0) | 0x03},
+       {0x18, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80},
+       {0x1d, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN},
+       {0x1d, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0) | 0x03},
+       /* SPK  */
+       {0x1a, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
+       {0x1a, AC_VERB_SET_CONNECT_SEL, 0x00},
+       /* HP, Amp  */
+       {0x16, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP},
+       {0x16, AC_VERB_SET_CONNECT_SEL, 0x00},
+       /* DAC1 */      
+       {0x10, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
+       /* Record selector: Int mic */
+       {0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0) | 0x44},
+       {0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1) | 0x44},
+       {0x15, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0) | 0x44},
+       /* SPDIF route: PCM */
+       {0x1c, AC_VERB_SET_CONNECT_SEL, 0x0},
+       /* EAPD */
+       {0x1a, AC_VERB_SET_EAPD_BTLENABLE, 0x2}, /* default on */ 
+       {0x16, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN|CONEXANT_HP_EVENT},
+       {0x17, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN|CXT5051_PORTB_EVENT},
+       {0x18, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN|CXT5051_PORTC_EVENT},
+       { } /* end */
+};
+
+/* initialize jack-sensing, too */
+static int cxt5051_init(struct hda_codec *codec)
+{
+       conexant_init(codec);
+       if (codec->patch_ops.unsol_event) {
+               cxt5051_hp_automute(codec);
+               cxt5051_portb_automic(codec);
+               cxt5051_portc_automic(codec);
+       }
+       return 0;
+}
+
+
+enum {
+       CXT5051_LAPTOP,  /* Laptops w/ EAPD support */
+       CXT5051_HP,     /* no docking */
+       CXT5051_MODELS
+};
+
+static const char *cxt5051_models[CXT5051_MODELS] = {
+       [CXT5051_LAPTOP]        = "laptop",
+       [CXT5051_HP]            = "hp",
+};
+
+static struct snd_pci_quirk cxt5051_cfg_tbl[] = {
+       SND_PCI_QUIRK(0x14f1, 0x0101, "Conexant Reference board",
+                     CXT5051_LAPTOP),
+       SND_PCI_QUIRK(0x14f1, 0x5051, "HP Spartan 1.1", CXT5051_HP),
+       {}
+};
+
+static int patch_cxt5051(struct hda_codec *codec)
+{
+       struct conexant_spec *spec;
+       int board_config;
+
+       spec = kzalloc(sizeof(*spec), GFP_KERNEL);
+       if (!spec)
+               return -ENOMEM;
+       mutex_init(&spec->amp_mutex);
+       codec->spec = spec;
+
+       codec->patch_ops = conexant_patch_ops;
+       codec->patch_ops.init = cxt5051_init;
+
+       spec->multiout.max_channels = 2;
+       spec->multiout.num_dacs = ARRAY_SIZE(cxt5051_dac_nids);
+       spec->multiout.dac_nids = cxt5051_dac_nids;
+       spec->multiout.dig_out_nid = CXT5051_SPDIF_OUT;
+       spec->num_adc_nids = 1; /* not 2; via auto-mic switch */
+       spec->adc_nids = cxt5051_adc_nids;
+       spec->num_mixers = 1;
+       spec->mixers[0] = cxt5051_mixers;
+       spec->num_init_verbs = 1;
+       spec->init_verbs[0] = cxt5051_init_verbs;
+       spec->spdif_route = 0;
+       spec->num_channel_mode = ARRAY_SIZE(cxt5051_modes);
+       spec->channel_mode = cxt5051_modes;
+       spec->cur_adc = 0;
+       spec->cur_adc_idx = 0;
+
+       board_config = snd_hda_check_board_config(codec, CXT5051_MODELS,
+                                                 cxt5051_models,
+                                                 cxt5051_cfg_tbl);
+       switch (board_config) {
+       case CXT5051_HP:
+               codec->patch_ops.unsol_event = cxt5051_hp_unsol_event;
+               spec->mixers[0] = cxt5051_hp_mixers;
+               break;
+       default:
+       case CXT5051_LAPTOP:
+               codec->patch_ops.unsol_event = cxt5051_hp_unsol_event;
+               break;
+       }
+
+       return 0;
+}
+
+
+/*
+ */
+
 struct hda_codec_preset snd_hda_preset_conexant[] = {
        { .id = 0x14f15045, .name = "CX20549 (Venice)",
          .patch = patch_cxt5045 },
        { .id = 0x14f15047, .name = "CX20551 (Waikiki)",
          .patch = patch_cxt5047 },
+       { .id = 0x14f15051, .name = "CX20561 (Hermosa)",
+         .patch = patch_cxt5051 },
        {} /* terminator */
 };
index 1c502789cc1ed001a7360e50425694f3635260ce..586d98f1b63d07b2373e40b4370fe7b233ed235a 100644 (file)
@@ -23,7 +23,6 @@
  *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA
  */
 
-#include <sound/driver.h>
 #include <linux/init.h>
 #include <linux/delay.h>
 #include <linux/slab.h>
@@ -92,9 +91,12 @@ enum {
        ALC262_HP_BPC,
        ALC262_HP_BPC_D7000_WL,
        ALC262_HP_BPC_D7000_WF,
+       ALC262_HP_TC_T5735,
+       ALC262_HP_RP5700,
        ALC262_BENQ_ED8,
        ALC262_SONY_ASSAMD,
        ALC262_BENQ_T31,
+       ALC262_ULTRA,
        ALC262_AUTO,
        ALC262_MODEL_LAST /* last tag */
 };
@@ -104,10 +106,21 @@ enum {
        ALC268_3ST,
        ALC268_TOSHIBA,
        ALC268_ACER,
+       ALC268_DELL,
+#ifdef CONFIG_SND_DEBUG
+       ALC268_TEST,
+#endif
        ALC268_AUTO,
        ALC268_MODEL_LAST /* last tag */
 };
 
+/* ALC269 models */
+enum {
+       ALC269_BASIC,
+       ALC269_AUTO,
+       ALC269_MODEL_LAST /* last tag */
+};
+
 /* ALC861 models */
 enum {
        ALC861_3ST,
@@ -144,6 +157,7 @@ enum {
        ALC662_5ST_DIG,
        ALC662_LENOVO_101E,
        ALC662_ASUS_EEEPC_P701,
+       ALC662_ASUS_EEEPC_EP20,
        ALC662_AUTO,
        ALC662_MODEL_LAST,
 };
@@ -183,6 +197,8 @@ enum {
        ALC883_HAIER_W66,               
        ALC888_6ST_HP,
        ALC888_3ST_HP,
+       ALC888_6ST_DELL,
+       ALC883_MITAC,
        ALC883_AUTO,
        ALC883_MODEL_LAST,
 };
@@ -204,6 +220,8 @@ struct alc_spec {
        char *stream_name_analog;       /* analog PCM stream */
        struct hda_pcm_stream *stream_analog_playback;
        struct hda_pcm_stream *stream_analog_capture;
+       struct hda_pcm_stream *stream_analog_alt_playback;
+       struct hda_pcm_stream *stream_analog_alt_capture;
 
        char *stream_name_digital;      /* digital PCM stream */
        struct hda_pcm_stream *stream_digital_playback;
@@ -214,6 +232,7 @@ struct alc_spec {
                                         * max_channels, dacs must be set
                                         * dig_out_nid and hp_nid are optional
                                         */
+       hda_nid_t alt_dac_nid;
 
        /* capture */
        unsigned int num_adc_nids;
@@ -247,7 +266,11 @@ struct alc_spec {
        /* for pin sensing */
        unsigned int sense_updated: 1;
        unsigned int jack_present: 1;
+       unsigned int master_sw: 1;
 
+       /* for virtual master */
+       hda_nid_t vmaster_nid;
+       u32 vmaster_tlv[4];
 #ifdef CONFIG_SND_HDA_POWER_SAVE
        struct hda_loopback_check loopback;
 #endif
@@ -562,7 +585,7 @@ static int alc_spdif_ctrl_get(struct snd_kcontrol *kcontrol,
        unsigned char mask = (kcontrol->private_value >> 16) & 0xff;
        long *valp = ucontrol->value.integer.value;
        unsigned int val = snd_hda_codec_read(codec, nid, 0,
-                                             AC_VERB_GET_DIGI_CONVERT, 0x00);
+                                             AC_VERB_GET_DIGI_CONVERT_1, 0x00);
 
        *valp = (val & mask) != 0;
        return 0;
@@ -576,7 +599,7 @@ static int alc_spdif_ctrl_put(struct snd_kcontrol *kcontrol,
        unsigned char mask = (kcontrol->private_value >> 16) & 0xff;
        long val = *ucontrol->value.integer.value;
        unsigned int ctrl_data = snd_hda_codec_read(codec, nid, 0,
-                                                   AC_VERB_GET_DIGI_CONVERT,
+                                                   AC_VERB_GET_DIGI_CONVERT_1,
                                                    0x00);
 
        /* Set/unset the masked control bit(s) as needed */
@@ -598,6 +621,59 @@ static int alc_spdif_ctrl_put(struct snd_kcontrol *kcontrol,
          .private_value = nid | (mask<<16) }
 #endif   /* CONFIG_SND_DEBUG */
 
+/* A switch control to allow the enabling EAPD digital outputs on the ALC26x.
+ * Again, this is only used in the ALC26x test models to help identify when
+ * the EAPD line must be asserted for features to work.
+ */
+#ifdef CONFIG_SND_DEBUG
+#define alc_eapd_ctrl_info     snd_ctl_boolean_mono_info
+
+static int alc_eapd_ctrl_get(struct snd_kcontrol *kcontrol,
+                             struct snd_ctl_elem_value *ucontrol)
+{
+       struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
+       hda_nid_t nid = kcontrol->private_value & 0xffff;
+       unsigned char mask = (kcontrol->private_value >> 16) & 0xff;
+       long *valp = ucontrol->value.integer.value;
+       unsigned int val = snd_hda_codec_read(codec, nid, 0,
+                                             AC_VERB_GET_EAPD_BTLENABLE, 0x00);
+
+       *valp = (val & mask) != 0;
+       return 0;
+}
+
+static int alc_eapd_ctrl_put(struct snd_kcontrol *kcontrol,
+                             struct snd_ctl_elem_value *ucontrol)
+{
+       int change;
+       struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
+       hda_nid_t nid = kcontrol->private_value & 0xffff;
+       unsigned char mask = (kcontrol->private_value >> 16) & 0xff;
+       long val = *ucontrol->value.integer.value;
+       unsigned int ctrl_data = snd_hda_codec_read(codec, nid, 0,
+                                                   AC_VERB_GET_EAPD_BTLENABLE,
+                                                   0x00);
+
+       /* Set/unset the masked control bit(s) as needed */
+       change = (!val ? 0 : mask) != (ctrl_data & mask);
+       if (!val)
+               ctrl_data &= ~mask;
+       else
+               ctrl_data |= mask;
+       snd_hda_codec_write_cache(codec, nid, 0, AC_VERB_SET_EAPD_BTLENABLE,
+                                 ctrl_data);
+
+       return change;
+}
+
+#define ALC_EAPD_CTRL_SWITCH(xname, nid, mask) \
+       { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, .index = 0,  \
+         .info = alc_eapd_ctrl_info, \
+         .get = alc_eapd_ctrl_get, \
+         .put = alc_eapd_ctrl_put, \
+         .private_value = nid | (mask<<16) }
+#endif   /* CONFIG_SND_DEBUG */
+
 /*
  * set up from the preset table
  */
@@ -739,7 +815,7 @@ static void alc_subsystem_id(struct hda_codec *codec,
        /* check sum */
        tmp = 0;
        for (i = 1; i < 16; i++) {
-               if ((ass >> i) && 1)
+               if ((ass >> i) & 1)
                        tmp++;
        }
        if (((ass >> 16) & 0xf) != tmp)
@@ -828,10 +904,10 @@ do_sku:
                break;
        }
        
-       /* is laptop and enable the function "Mute internal speaker
+       /* is laptop or Desktop and enable the function "Mute internal speaker
         * when the external headphone out jack is plugged"
         */
-       if (!(ass & 0x4) || !(ass & 0x8000))
+       if (!(ass & 0x8000))
                return;
        /*
         * 10~8 : Jack location
@@ -841,9 +917,9 @@ do_sku:
         *              when the external headphone out jack is plugged"
         */
        if (!spec->autocfg.speaker_pins[0]) {
-               if (spec->multiout.dac_nids[0])
+               if (spec->autocfg.line_out_pins[0])
                        spec->autocfg.speaker_pins[0] =
-                               spec->multiout.dac_nids[0];
+                               spec->autocfg.line_out_pins[0];
                else
                        return;
        }
@@ -1009,7 +1085,6 @@ static struct snd_kcontrol_new alc880_capture_mixer[] = {
                .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
                /* The multiple "Capture Source" controls confuse alsamixer
                 * So call somewhat different..
-                * FIXME: the controls appear in the "playback" view!
                 */
                /* .name = "Capture Source", */
                .name = "Input Source",
@@ -1031,7 +1106,6 @@ static struct snd_kcontrol_new alc880_capture_alt_mixer[] = {
                .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
                /* The multiple "Capture Source" controls confuse alsamixer
                 * So call somewhat different..
-                * FIXME: the controls appear in the "playback" view!
                 */
                /* .name = "Capture Source", */
                .name = "Input Source",
@@ -1226,7 +1300,6 @@ static struct snd_kcontrol_new alc880_z71v_mixer[] = {
 };
 
 
-/* FIXME! */
 /*
  * ALC880 F1734 model
  *
@@ -1242,8 +1315,8 @@ static hda_nid_t alc880_f1734_dac_nids[1] = {
 static struct snd_kcontrol_new alc880_f1734_mixer[] = {
        HDA_CODEC_VOLUME("Headphone Playback Volume", 0x0c, 0x0, HDA_OUTPUT),
        HDA_BIND_MUTE("Headphone Playback Switch", 0x0c, 2, HDA_INPUT),
-       HDA_CODEC_VOLUME("Internal Speaker Playback Volume", 0x0d, 0x0, HDA_OUTPUT),
-       HDA_BIND_MUTE("Internal Speaker Playback Switch", 0x0d, 2, HDA_INPUT),
+       HDA_CODEC_VOLUME("Speaker Playback Volume", 0x0d, 0x0, HDA_OUTPUT),
+       HDA_BIND_MUTE("Speaker Playback Switch", 0x0d, 2, HDA_INPUT),
        HDA_CODEC_VOLUME("CD Playback Volume", 0x0b, 0x04, HDA_INPUT),
        HDA_CODEC_MUTE("CD Playback Switch", 0x0b, 0x04, HDA_INPUT),
        HDA_CODEC_VOLUME("Mic Playback Volume", 0x0b, 0x0, HDA_INPUT),
@@ -1252,7 +1325,6 @@ static struct snd_kcontrol_new alc880_f1734_mixer[] = {
 };
 
 
-/* FIXME! */
 /*
  * ALC880 ASUS model
  *
@@ -1289,7 +1361,6 @@ static struct snd_kcontrol_new alc880_asus_mixer[] = {
        { } /* end */
 };
 
-/* FIXME! */
 /*
  * ALC880 ASUS W1V model
  *
@@ -1327,7 +1398,6 @@ static struct snd_kcontrol_new alc880_tcl_s700_mixer[] = {
                .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
                /* The multiple "Capture Source" controls confuse alsamixer
                 * So call somewhat different..
-                * FIXME: the controls appear in the "playback" view!
                 */
                /* .name = "Capture Source", */
                .name = "Input Source",
@@ -1341,10 +1411,10 @@ static struct snd_kcontrol_new alc880_tcl_s700_mixer[] = {
 
 /* Uniwill */
 static struct snd_kcontrol_new alc880_uniwill_mixer[] = {
-       HDA_CODEC_VOLUME("HPhone Playback Volume", 0x0c, 0x0, HDA_OUTPUT),
-       HDA_BIND_MUTE("HPhone Playback Switch", 0x0c, 2, HDA_INPUT),
-       HDA_CODEC_VOLUME("iSpeaker Playback Volume", 0x0d, 0x0, HDA_OUTPUT),
-       HDA_BIND_MUTE("iSpeaker Playback Switch", 0x0d, 2, HDA_INPUT),
+       HDA_CODEC_VOLUME("Headphone Playback Volume", 0x0c, 0x0, HDA_OUTPUT),
+       HDA_BIND_MUTE("Headphone Playback Switch", 0x0c, 2, HDA_INPUT),
+       HDA_CODEC_VOLUME("Speaker Playback Volume", 0x0d, 0x0, HDA_OUTPUT),
+       HDA_BIND_MUTE("Speaker Playback Switch", 0x0d, 2, HDA_INPUT),
        HDA_CODEC_VOLUME_MONO("Center Playback Volume", 0x0e, 1, 0x0, HDA_OUTPUT),
        HDA_CODEC_VOLUME_MONO("LFE Playback Volume", 0x0e, 2, 0x0, HDA_OUTPUT),
        HDA_BIND_MUTE_MONO("Center Playback Switch", 0x0e, 1, 2, HDA_INPUT),
@@ -1384,15 +1454,48 @@ static struct snd_kcontrol_new alc880_fujitsu_mixer[] = {
 };
 
 static struct snd_kcontrol_new alc880_uniwill_p53_mixer[] = {
-       HDA_CODEC_VOLUME("HPhone Playback Volume", 0x0c, 0x0, HDA_OUTPUT),
-       HDA_BIND_MUTE("HPhone Playback Switch", 0x0c, 2, HDA_INPUT),
-       HDA_CODEC_VOLUME("iSpeaker Playback Volume", 0x0d, 0x0, HDA_OUTPUT),
-       HDA_BIND_MUTE("iSpeaker Playback Switch", 0x0d, 2, HDA_INPUT),
+       HDA_CODEC_VOLUME("Headphone Playback Volume", 0x0c, 0x0, HDA_OUTPUT),
+       HDA_BIND_MUTE("Headphone Playback Switch", 0x0c, 2, HDA_INPUT),
+       HDA_CODEC_VOLUME("Speaker Playback Volume", 0x0d, 0x0, HDA_OUTPUT),
+       HDA_BIND_MUTE("Speaker Playback Switch", 0x0d, 2, HDA_INPUT),
        HDA_CODEC_VOLUME("Mic Playback Volume", 0x0b, 0x0, HDA_INPUT),
        HDA_CODEC_MUTE("Mic Playback Switch", 0x0b, 0x0, HDA_INPUT),
        { } /* end */
 };
 
+/*
+ * virtual master controls
+ */
+
+/*
+ * slave controls for virtual master
+ */
+static const char *alc_slave_vols[] = {
+       "Front Playback Volume",
+       "Surround Playback Volume",
+       "Center Playback Volume",
+       "LFE Playback Volume",
+       "Side Playback Volume",
+       "Headphone Playback Volume",
+       "Speaker Playback Volume",
+       "Mono Playback Volume",
+       "Line-Out Playback Volume",
+       NULL,
+};
+
+static const char *alc_slave_sws[] = {
+       "Front Playback Switch",
+       "Surround Playback Switch",
+       "Center Playback Switch",
+       "LFE Playback Switch",
+       "Side Playback Switch",
+       "Headphone Playback Switch",
+       "Speaker Playback Switch",
+       "Mono Playback Switch",
+       "IEC958 Playback Switch",
+       NULL,
+};
+
 /*
  * build control elements
  */
@@ -1419,6 +1522,23 @@ static int alc_build_controls(struct hda_codec *codec)
                if (err < 0)
                        return err;
        }
+
+       /* if we have no master control, let's create it */
+       if (!snd_hda_find_mixer_ctl(codec, "Master Playback Volume")) {
+               snd_hda_set_vmaster_tlv(codec, spec->vmaster_nid,
+                                       HDA_OUTPUT, spec->vmaster_tlv);
+               err = snd_hda_add_vmaster(codec, "Master Playback Volume",
+                                         spec->vmaster_tlv, alc_slave_vols);
+               if (err < 0)
+                       return err;
+       }
+       if (!snd_hda_find_mixer_ctl(codec, "Master Playback Switch")) {
+               err = snd_hda_add_vmaster(codec, "Master Playback Switch",
+                                         NULL, alc_slave_sws);
+               if (err < 0)
+                       return err;
+       }
+
        return 0;
 }
 
@@ -1790,7 +1910,6 @@ static void alc880_uniwill_p53_unsol_event(struct hda_codec *codec,
                alc880_uniwill_p53_dcvol_automute(codec);
 }
 
-/* FIXME! */
 /*
  * F1734 pin configuration:
  * HP = 0x14, speaker-out = 0x15, mic = 0x18
@@ -1819,7 +1938,6 @@ static struct hda_verb alc880_pin_f1734_init_verbs[] = {
        { }
 };
 
-/* FIXME! */
 /*
  * ASUS pin configuration:
  * HP/front = 0x14, surr = 0x15, clfe = 0x16, mic = 0x18, line = 0x1a
@@ -1966,9 +2084,8 @@ static struct hda_channel_mode alc880_lg_ch_modes[3] = {
 };
 
 static struct snd_kcontrol_new alc880_lg_mixer[] = {
-       /* FIXME: it's not really "master" but front channels */
-       HDA_CODEC_VOLUME("Master Playback Volume", 0x0f, 0x0, HDA_OUTPUT),
-       HDA_BIND_MUTE("Master Playback Switch", 0x0f, 2, HDA_INPUT),
+       HDA_CODEC_VOLUME("Front Playback Volume", 0x0f, 0x0, HDA_OUTPUT),
+       HDA_BIND_MUTE("Front Playback Switch", 0x0f, 2, HDA_INPUT),
        HDA_CODEC_VOLUME("Surround Playback Volume", 0x0c, 0x0, HDA_OUTPUT),
        HDA_BIND_MUTE("Surround Playback Switch", 0x0c, 2, HDA_INPUT),
        HDA_CODEC_VOLUME_MONO("Center Playback Volume", 0x0d, 1, 0x0, HDA_OUTPUT),
@@ -2256,7 +2373,7 @@ static int alc880_dig_playback_pcm_close(struct hda_pcm_stream *hinfo,
 /*
  * Analog capture
  */
-static int alc880_capture_pcm_prepare(struct hda_pcm_stream *hinfo,
+static int alc880_alt_capture_pcm_prepare(struct hda_pcm_stream *hinfo,
                                      struct hda_codec *codec,
                                      unsigned int stream_tag,
                                      unsigned int format,
@@ -2264,18 +2381,18 @@ static int alc880_capture_pcm_prepare(struct hda_pcm_stream *hinfo,
 {
        struct alc_spec *spec = codec->spec;
 
-       snd_hda_codec_setup_stream(codec, spec->adc_nids[substream->number],
+       snd_hda_codec_setup_stream(codec, spec->adc_nids[substream->number + 1],
                                   stream_tag, 0, format);
        return 0;
 }
 
-static int alc880_capture_pcm_cleanup(struct hda_pcm_stream *hinfo,
+static int alc880_alt_capture_pcm_cleanup(struct hda_pcm_stream *hinfo,
                                      struct hda_codec *codec,
                                      struct snd_pcm_substream *substream)
 {
        struct alc_spec *spec = codec->spec;
 
-       snd_hda_codec_setup_stream(codec, spec->adc_nids[substream->number],
+       snd_hda_codec_setup_stream(codec, spec->adc_nids[substream->number + 1],
                                   0, 0, 0);
        return 0;
 }
@@ -2296,13 +2413,27 @@ static struct hda_pcm_stream alc880_pcm_analog_playback = {
 };
 
 static struct hda_pcm_stream alc880_pcm_analog_capture = {
-       .substreams = 2,
+       .substreams = 1,
+       .channels_min = 2,
+       .channels_max = 2,
+       /* NID is set in alc_build_pcms */
+};
+
+static struct hda_pcm_stream alc880_pcm_analog_alt_playback = {
+       .substreams = 1,
+       .channels_min = 2,
+       .channels_max = 2,
+       /* NID is set in alc_build_pcms */
+};
+
+static struct hda_pcm_stream alc880_pcm_analog_alt_capture = {
+       .substreams = 2, /* can be overridden */
        .channels_min = 2,
        .channels_max = 2,
        /* NID is set in alc_build_pcms */
        .ops = {
-               .prepare = alc880_capture_pcm_prepare,
-               .cleanup = alc880_capture_pcm_cleanup
+               .prepare = alc880_alt_capture_pcm_prepare,
+               .cleanup = alc880_alt_capture_pcm_cleanup
        },
 };
 
@@ -2326,7 +2457,7 @@ static struct hda_pcm_stream alc880_pcm_digital_capture = {
 };
 
 /* Used by alc_build_pcms to flag that a PCM has no playback stream */
-static struct hda_pcm_stream alc_pcm_null_playback = {
+static struct hda_pcm_stream alc_pcm_null_stream = {
        .substreams = 0,
        .channels_min = 0,
        .channels_max = 0,
@@ -2383,17 +2514,32 @@ static int alc_build_pcms(struct hda_codec *codec)
         * model, configure a second analog capture-only PCM.
         */
        /* Additional Analaog capture for index #2 */
-       if (spec->num_adc_nids > 1 && spec->stream_analog_capture &&
-           spec->adc_nids) {
+       if ((spec->alt_dac_nid && spec->stream_analog_alt_playback) ||
+           (spec->num_adc_nids > 1 && spec->stream_analog_alt_capture)) {
                codec->num_pcms = 3;
                info = spec->pcm_rec + 2;
                info->name = spec->stream_name_analog;
-               /* No playback stream for second PCM */
-               info->stream[SNDRV_PCM_STREAM_PLAYBACK] = alc_pcm_null_playback;
-               info->stream[SNDRV_PCM_STREAM_PLAYBACK].nid = 0;
-               if (spec->stream_analog_capture) {
-                       info->stream[SNDRV_PCM_STREAM_CAPTURE] = *(spec->stream_analog_capture);
-                       info->stream[SNDRV_PCM_STREAM_CAPTURE].nid = spec->adc_nids[1];
+               if (spec->alt_dac_nid) {
+                       info->stream[SNDRV_PCM_STREAM_PLAYBACK] =
+                               *spec->stream_analog_alt_playback;
+                       info->stream[SNDRV_PCM_STREAM_PLAYBACK].nid =
+                               spec->alt_dac_nid;
+               } else {
+                       info->stream[SNDRV_PCM_STREAM_PLAYBACK] =
+                               alc_pcm_null_stream;
+                       info->stream[SNDRV_PCM_STREAM_PLAYBACK].nid = 0;
+               }
+               if (spec->num_adc_nids > 1) {
+                       info->stream[SNDRV_PCM_STREAM_CAPTURE] =
+                               *spec->stream_analog_alt_capture;
+                       info->stream[SNDRV_PCM_STREAM_CAPTURE].nid =
+                               spec->adc_nids[1];
+                       info->stream[SNDRV_PCM_STREAM_CAPTURE].substreams =
+                               spec->num_adc_nids - 1;
+               } else {
+                       info->stream[SNDRV_PCM_STREAM_CAPTURE] =
+                               alc_pcm_null_stream;
+                       info->stream[SNDRV_PCM_STREAM_CAPTURE].nid = 0;
                }
        }
 
@@ -2723,23 +2869,17 @@ static const char *alc880_models[ALC880_MODEL_LAST] = {
 };
 
 static struct snd_pci_quirk alc880_cfg_tbl[] = {
-       /* Broken BIOS configuration */
-       SND_PCI_QUIRK(0x2668, 0x8086, NULL, ALC880_6ST_DIG),
-       SND_PCI_QUIRK(0x8086, 0x2668, NULL, ALC880_6ST_DIG),
-
+       SND_PCI_QUIRK(0x1019, 0x0f69, "Coeus G610P", ALC880_W810),
        SND_PCI_QUIRK(0x1019, 0xa880, "ECS", ALC880_5ST_DIG),
        SND_PCI_QUIRK(0x1019, 0xa884, "Acer APFV", ALC880_6ST),
-       SND_PCI_QUIRK(0x1019, 0x0f69, "Coeus G610P", ALC880_W810),
        SND_PCI_QUIRK(0x1025, 0x0070, "ULI", ALC880_3ST_DIG),
        SND_PCI_QUIRK(0x1025, 0x0077, "ULI", ALC880_6ST_DIG),
        SND_PCI_QUIRK(0x1025, 0x0078, "ULI", ALC880_6ST_DIG),
        SND_PCI_QUIRK(0x1025, 0x0087, "ULI", ALC880_6ST_DIG),
        SND_PCI_QUIRK(0x1025, 0xe309, "ULI", ALC880_3ST_DIG),
        SND_PCI_QUIRK(0x1025, 0xe310, "ULI", ALC880_3ST),
-
        SND_PCI_QUIRK(0x1039, 0x1234, NULL, ALC880_6ST_DIG),
        SND_PCI_QUIRK(0x103c, 0x2a09, "HP", ALC880_5ST),
-
        SND_PCI_QUIRK(0x1043, 0x10b3, "ASUS W1V", ALC880_ASUS_W1V),
        SND_PCI_QUIRK(0x1043, 0x10c2, "ASUS W6A", ALC880_ASUS_DIG),
        SND_PCI_QUIRK(0x1043, 0x10c3, "ASUS Wxx", ALC880_ASUS_DIG),
@@ -2754,54 +2894,50 @@ static struct snd_pci_quirk alc880_cfg_tbl[] = {
        SND_PCI_QUIRK(0x1043, 0x8181, "ASUS P4GPL", ALC880_ASUS_DIG),
        SND_PCI_QUIRK(0x1043, 0x8196, "ASUS P5GD1", ALC880_6ST),
        SND_PCI_QUIRK(0x1043, 0x81b4, "ASUS", ALC880_6ST),
-       SND_PCI_QUIRK(0x1043, 0, "ASUS", ALC880_ASUS),
-
-       SND_PCI_QUIRK(0x104d, 0x81d6, "Sony", ALC880_3ST),
+       SND_PCI_QUIRK(0x1043, 0, "ASUS", ALC880_ASUS), /* default ASUS */
        SND_PCI_QUIRK(0x104d, 0x81a0, "Sony", ALC880_3ST),
+       SND_PCI_QUIRK(0x104d, 0x81d6, "Sony", ALC880_3ST),
+       SND_PCI_QUIRK(0x107b, 0x3032, "Gateway", ALC880_5ST),
        SND_PCI_QUIRK(0x107b, 0x3033, "Gateway", ALC880_5ST),
        SND_PCI_QUIRK(0x107b, 0x4039, "Gateway", ALC880_5ST),
-       SND_PCI_QUIRK(0x107b, 0x3032, "Gateway", ALC880_5ST),
-       SND_PCI_QUIRK(0x1558, 0x0520, "Clevo m520G", ALC880_CLEVO),
-       SND_PCI_QUIRK(0x1558, 0x0660, "Clevo m655n", ALC880_CLEVO),
-       SND_PCI_QUIRK(0x1565, 0x8202, "Biostar", ALC880_5ST_DIG),
-       SND_PCI_QUIRK(0x161f, 0x203d, "W810", ALC880_W810),
-       SND_PCI_QUIRK(0x1695, 0x400d, "EPoX", ALC880_5ST_DIG),
-       SND_PCI_QUIRK(0x19db, 0x4188, "TCL S700", ALC880_TCL_S700),
-       SND_PCI_QUIRK(0xa0a0, 0x0560, "AOpen i915GMm-HFS", ALC880_5ST_DIG),
-       SND_PCI_QUIRK(0xe803, 0x1019, NULL, ALC880_6ST_DIG),
        SND_PCI_QUIRK(0x1297, 0xc790, "Shuttle ST20G5", ALC880_6ST_DIG),
        SND_PCI_QUIRK(0x1458, 0xa102, "Gigabyte K8", ALC880_6ST_DIG),
        SND_PCI_QUIRK(0x1462, 0x1150, "MSI", ALC880_6ST_DIG),
        SND_PCI_QUIRK(0x1509, 0x925d, "FIC P4M", ALC880_6ST_DIG),
+       SND_PCI_QUIRK(0x1558, 0x0520, "Clevo m520G", ALC880_CLEVO),
+       SND_PCI_QUIRK(0x1558, 0x0660, "Clevo m655n", ALC880_CLEVO),
        SND_PCI_QUIRK(0x1558, 0x5401, "ASUS", ALC880_ASUS_DIG2),
-
+       SND_PCI_QUIRK(0x1565, 0x8202, "Biostar", ALC880_5ST_DIG),
        SND_PCI_QUIRK(0x1584, 0x9050, "Uniwill", ALC880_UNIWILL_DIG),
+       SND_PCI_QUIRK(0x1584, 0x9054, "Uniwlll", ALC880_F1734),
        SND_PCI_QUIRK(0x1584, 0x9070, "Uniwill", ALC880_UNIWILL),
        SND_PCI_QUIRK(0x1584, 0x9077, "Uniwill P53", ALC880_UNIWILL_P53),
-       SND_PCI_QUIRK(0x1584, 0x9054, "Uniwlll", ALC880_F1734),
-
+       SND_PCI_QUIRK(0x161f, 0x203d, "W810", ALC880_W810),
+       SND_PCI_QUIRK(0x1695, 0x400d, "EPoX", ALC880_5ST_DIG),
        SND_PCI_QUIRK(0x1695, 0x4012, "EPox EP-5LDA", ALC880_5ST_DIG),
-       SND_PCI_QUIRK(0x1734, 0x10ac, "FSC", ALC880_UNIWILL),
        SND_PCI_QUIRK(0x1734, 0x107c, "FSC F1734", ALC880_F1734),
+       SND_PCI_QUIRK(0x1734, 0x10ac, "FSC", ALC880_UNIWILL),
        SND_PCI_QUIRK(0x1734, 0x10b0, "Fujitsu", ALC880_FUJITSU),
-
+       SND_PCI_QUIRK(0x1854, 0x0018, "LG LW20", ALC880_LG_LW),
        SND_PCI_QUIRK(0x1854, 0x003b, "LG", ALC880_LG),
        SND_PCI_QUIRK(0x1854, 0x0068, "LG w1", ALC880_LG),
-       SND_PCI_QUIRK(0x1854, 0x0018, "LG LW20", ALC880_LG_LW),
        SND_PCI_QUIRK(0x1854, 0x0077, "LG LW25", ALC880_LG_LW),
-
-       SND_PCI_QUIRK(0x8086, 0xe308, "Intel mobo", ALC880_3ST_DIG),
-       SND_PCI_QUIRK(0x8086, 0xe305, "Intel mobo", ALC880_3ST_DIG),
-       SND_PCI_QUIRK(0x8086, 0xd402, "Intel mobo", ALC880_3ST_DIG),
+       SND_PCI_QUIRK(0x19db, 0x4188, "TCL S700", ALC880_TCL_S700),
+       SND_PCI_QUIRK(0x2668, 0x8086, NULL, ALC880_6ST_DIG), /* broken BIOS */
+       SND_PCI_QUIRK(0x8086, 0x2668, NULL, ALC880_6ST_DIG),
+       SND_PCI_QUIRK(0x8086, 0xa100, "Intel mobo", ALC880_5ST_DIG),
        SND_PCI_QUIRK(0x8086, 0xd400, "Intel mobo", ALC880_5ST_DIG),
        SND_PCI_QUIRK(0x8086, 0xd401, "Intel mobo", ALC880_5ST_DIG),
+       SND_PCI_QUIRK(0x8086, 0xd402, "Intel mobo", ALC880_3ST_DIG),
        SND_PCI_QUIRK(0x8086, 0xe224, "Intel mobo", ALC880_5ST_DIG),
+       SND_PCI_QUIRK(0x8086, 0xe305, "Intel mobo", ALC880_3ST_DIG),
+       SND_PCI_QUIRK(0x8086, 0xe308, "Intel mobo", ALC880_3ST_DIG),
        SND_PCI_QUIRK(0x8086, 0xe400, "Intel mobo", ALC880_5ST_DIG),
        SND_PCI_QUIRK(0x8086, 0xe401, "Intel mobo", ALC880_5ST_DIG),
        SND_PCI_QUIRK(0x8086, 0xe402, "Intel mobo", ALC880_5ST_DIG),
-       SND_PCI_QUIRK(0x8086, 0xa100, "Intel mobo", ALC880_5ST_DIG),
-       SND_PCI_QUIRK(0x8086, 0, "Intel mobo", ALC880_3ST),
-
+       SND_PCI_QUIRK(0x8086, 0, "Intel mobo", ALC880_3ST), /* default Intel */
+       SND_PCI_QUIRK(0xa0a0, 0x0560, "AOpen i915GMm-HFS", ALC880_5ST_DIG),
+       SND_PCI_QUIRK(0xe803, 0x1019, NULL, ALC880_6ST_DIG),
        {}
 };
 
@@ -3511,6 +3647,7 @@ static int patch_alc880(struct hda_codec *codec)
        spec->stream_name_analog = "ALC880 Analog";
        spec->stream_analog_playback = &alc880_pcm_analog_playback;
        spec->stream_analog_capture = &alc880_pcm_analog_capture;
+       spec->stream_analog_alt_capture = &alc880_pcm_analog_alt_capture;
 
        spec->stream_name_digital = "ALC880 Digital";
        spec->stream_digital_playback = &alc880_pcm_digital_playback;
@@ -3535,6 +3672,8 @@ static int patch_alc880(struct hda_codec *codec)
                }
        }
 
+       spec->vmaster_nid = 0x0c;
+
        codec->patch_ops = alc_patch_ops;
        if (board_config == ALC880_AUTO)
                spec->init_hook = alc880_auto_init;
@@ -3691,18 +3830,135 @@ static struct snd_kcontrol_new alc260_pc_beep_mixer[] = {
        { } /* end */
 };
 
+/* update HP, line and mono out pins according to the master switch */
+static void alc260_hp_master_update(struct hda_codec *codec,
+                                   hda_nid_t hp, hda_nid_t line,
+                                   hda_nid_t mono)
+{
+       struct alc_spec *spec = codec->spec;
+       unsigned int val = spec->master_sw ? PIN_HP : 0;
+       /* change HP and line-out pins */
+       snd_hda_codec_write(codec, 0x0f, 0, AC_VERB_SET_PIN_WIDGET_CONTROL,
+                           val);
+       snd_hda_codec_write(codec, 0x10, 0, AC_VERB_SET_PIN_WIDGET_CONTROL,
+                           val);
+       /* mono (speaker) depending on the HP jack sense */
+       val = (val && !spec->jack_present) ? PIN_OUT : 0;
+       snd_hda_codec_write(codec, 0x11, 0, AC_VERB_SET_PIN_WIDGET_CONTROL,
+                           val);
+}
+
+static int alc260_hp_master_sw_get(struct snd_kcontrol *kcontrol,
+                                  struct snd_ctl_elem_value *ucontrol)
+{
+       struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
+       struct alc_spec *spec = codec->spec;
+       *ucontrol->value.integer.value = spec->master_sw;
+       return 0;
+}
+
+static int alc260_hp_master_sw_put(struct snd_kcontrol *kcontrol,
+                                  struct snd_ctl_elem_value *ucontrol)
+{
+       struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
+       struct alc_spec *spec = codec->spec;
+       int val = !!*ucontrol->value.integer.value;
+       hda_nid_t hp, line, mono;
+
+       if (val == spec->master_sw)
+               return 0;
+       spec->master_sw = val;
+       hp = (kcontrol->private_value >> 16) & 0xff;
+       line = (kcontrol->private_value >> 8) & 0xff;
+       mono = kcontrol->private_value & 0xff;
+       alc260_hp_master_update(codec, hp, line, mono);
+       return 1;
+}
+
+static struct snd_kcontrol_new alc260_hp_output_mixer[] = {
+       {
+               .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+               .name = "Master Playback Switch",
+               .info = snd_ctl_boolean_mono_info,
+               .get = alc260_hp_master_sw_get,
+               .put = alc260_hp_master_sw_put,
+               .private_value = (0x0f << 16) | (0x10 << 8) | 0x11
+       },
+       HDA_CODEC_VOLUME("Front Playback Volume", 0x08, 0x0, HDA_OUTPUT),
+       HDA_BIND_MUTE("Front Playback Switch", 0x08, 2, HDA_INPUT),
+       HDA_CODEC_VOLUME("Headphone Playback Volume", 0x09, 0x0, HDA_OUTPUT),
+       HDA_BIND_MUTE("Headphone Playback Switch", 0x09, 2, HDA_INPUT),
+       HDA_CODEC_VOLUME_MONO("Speaker Playback Volume", 0x0a, 1, 0x0,
+                             HDA_OUTPUT),
+       HDA_BIND_MUTE_MONO("Speaker Playback Switch", 0x0a, 1, 2, HDA_INPUT),
+       { } /* end */
+};
+
+static struct hda_verb alc260_hp_unsol_verbs[] = {
+       {0x10, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | ALC880_HP_EVENT},
+       {},
+};
+
+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;
+       alc260_hp_master_update(codec, 0x0f, 0x10, 0x11);
+}
+
+static void alc260_hp_unsol_event(struct hda_codec *codec, unsigned int res)
+{
+       if ((res >> 26) == ALC880_HP_EVENT)
+               alc260_hp_automute(codec);
+}
+
 static struct snd_kcontrol_new alc260_hp_3013_mixer[] = {
+       {
+               .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+               .name = "Master Playback Switch",
+               .info = snd_ctl_boolean_mono_info,
+               .get = alc260_hp_master_sw_get,
+               .put = alc260_hp_master_sw_put,
+               .private_value = (0x10 << 16) | (0x15 << 8) | 0x11
+       },
        HDA_CODEC_VOLUME("Front Playback Volume", 0x09, 0x0, HDA_OUTPUT),
        HDA_CODEC_MUTE("Front Playback Switch", 0x10, 0x0, HDA_OUTPUT),
        HDA_CODEC_VOLUME("Aux-In Playback Volume", 0x07, 0x06, HDA_INPUT),
        HDA_CODEC_MUTE("Aux-In Playback Switch", 0x07, 0x06, HDA_INPUT),
        HDA_CODEC_VOLUME("Headphone Playback Volume", 0x08, 0x0, HDA_OUTPUT),
        HDA_CODEC_MUTE("Headphone Playback Switch", 0x15, 0x0, HDA_OUTPUT),
-       HDA_CODEC_VOLUME_MONO("iSpeaker Playback Volume", 0x0a, 1, 0x0, HDA_OUTPUT),
-       HDA_CODEC_MUTE_MONO("iSpeaker Playback Switch", 0x11, 1, 0x0, HDA_OUTPUT),
+       HDA_CODEC_VOLUME_MONO("Speaker Playback Volume", 0x0a, 1, 0x0, HDA_OUTPUT),
+       HDA_CODEC_MUTE_MONO("Speaker Playback Switch", 0x11, 1, 0x0, HDA_OUTPUT),
        { } /* end */
 };
 
+static struct hda_verb alc260_hp_3013_unsol_verbs[] = {
+       {0x15, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | ALC880_HP_EVENT},
+       {},
+};
+
+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;
+       alc260_hp_master_update(codec, 0x10, 0x15, 0x11);
+}
+
+static void alc260_hp_3013_unsol_event(struct hda_codec *codec,
+                                      unsigned int res)
+{
+       if ((res >> 26) == ALC880_HP_EVENT)
+               alc260_hp_3013_automute(codec);
+}
+
 /* Fujitsu S702x series laptops.  ALC260 pin usage: Mic/Line jack = 0x12, 
  * HP jack = 0x14, CD audio =  0x16, internal speaker = 0x10.
  */
@@ -3812,7 +4068,6 @@ static struct snd_kcontrol_new alc260_capture_mixer[] = {
                .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
                /* The multiple "Capture Source" controls confuse alsamixer
                 * So call somewhat different..
-                * FIXME: the controls appear in the "playback" view!
                 */
                /* .name = "Capture Source", */
                .name = "Input Source",
@@ -3831,7 +4086,6 @@ static struct snd_kcontrol_new alc260_capture_alt_mixer[] = {
                .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
                /* The multiple "Capture Source" controls confuse alsamixer
                 * So call somewhat different..
-                * FIXME: the controls appear in the "playback" view!
                 */
                /* .name = "Capture Source", */
                .name = "Input Source",
@@ -4332,6 +4586,12 @@ static struct snd_kcontrol_new alc260_test_mixer[] = {
        ALC_SPDIF_CTRL_SWITCH("SPDIF Playback Switch", 0x03, 0x01),
        ALC_SPDIF_CTRL_SWITCH("SPDIF Capture Switch", 0x06, 0x01),
 
+       /* A switch allowing EAPD to be enabled.  Some laptops seem to use
+        * this output to turn on an external amplifier.
+        */
+       ALC_EAPD_CTRL_SWITCH("LINE-OUT EAPD Enable Switch", 0x0f, 0x02),
+       ALC_EAPD_CTRL_SWITCH("HP-OUT EAPD Enable Switch", 0x10, 0x02),
+
        { } /* end */
 };
 static struct hda_verb alc260_test_init_verbs[] = {
@@ -4417,17 +4677,8 @@ static struct hda_verb alc260_test_init_verbs[] = {
 };
 #endif
 
-static struct hda_pcm_stream alc260_pcm_analog_playback = {
-       .substreams = 1,
-       .channels_min = 2,
-       .channels_max = 2,
-};
-
-static struct hda_pcm_stream alc260_pcm_analog_capture = {
-       .substreams = 1,
-       .channels_min = 2,
-       .channels_max = 2,
-};
+#define alc260_pcm_analog_playback     alc880_pcm_analog_alt_playback
+#define alc260_pcm_analog_capture      alc880_pcm_analog_capture
 
 #define alc260_pcm_digital_playback    alc880_pcm_digital_playback
 #define alc260_pcm_digital_capture     alc880_pcm_digital_capture
@@ -4744,8 +4995,8 @@ static struct snd_pci_quirk alc260_cfg_tbl[] = {
        SND_PCI_QUIRK(0x104d, 0x81cd, "Sony VAIO", ALC260_BASIC),
        SND_PCI_QUIRK(0x10cf, 0x1326, "Fujitsu S702X", ALC260_FUJITSU_S702X),
        SND_PCI_QUIRK(0x152d, 0x0729, "CTL U553W", ALC260_BASIC),
-       SND_PCI_QUIRK(0x1631, 0xc017, "PB V7900", ALC260_WILL),
        SND_PCI_QUIRK(0x161f, 0x2057, "Replacer 672V", ALC260_REPLACER_672V),
+       SND_PCI_QUIRK(0x1631, 0xc017, "PB V7900", ALC260_WILL),
        {}
 };
 
@@ -4765,10 +5016,11 @@ static struct alc_config_preset alc260_presets[] = {
                .input_mux = &alc260_capture_source,
        },
        [ALC260_HP] = {
-               .mixers = { alc260_base_output_mixer,
+               .mixers = { alc260_hp_output_mixer,
                            alc260_input_mixer,
                            alc260_capture_alt_mixer },
-               .init_verbs = { alc260_init_verbs },
+               .init_verbs = { alc260_init_verbs,
+                               alc260_hp_unsol_verbs },
                .num_dacs = ARRAY_SIZE(alc260_dac_nids),
                .dac_nids = alc260_dac_nids,
                .num_adc_nids = ARRAY_SIZE(alc260_hp_adc_nids),
@@ -4776,12 +5028,15 @@ static struct alc_config_preset alc260_presets[] = {
                .num_channel_mode = ARRAY_SIZE(alc260_modes),
                .channel_mode = alc260_modes,
                .input_mux = &alc260_capture_source,
+               .unsol_event = alc260_hp_unsol_event,
+               .init_hook = alc260_hp_automute,
        },
        [ALC260_HP_3013] = {
                .mixers = { alc260_hp_3013_mixer,
                            alc260_input_mixer,
                            alc260_capture_alt_mixer },
-               .init_verbs = { alc260_hp_3013_init_verbs },
+               .init_verbs = { alc260_hp_3013_init_verbs,
+                               alc260_hp_3013_unsol_verbs },
                .num_dacs = ARRAY_SIZE(alc260_dac_nids),
                .dac_nids = alc260_dac_nids,
                .num_adc_nids = ARRAY_SIZE(alc260_hp_adc_nids),
@@ -4789,6 +5044,8 @@ static struct alc_config_preset alc260_presets[] = {
                .num_channel_mode = ARRAY_SIZE(alc260_modes),
                .channel_mode = alc260_modes,
                .input_mux = &alc260_capture_source,
+               .unsol_event = alc260_hp_3013_unsol_event,
+               .init_hook = alc260_hp_3013_automute,
        },
        [ALC260_FUJITSU_S702X] = {
                .mixers = { alc260_fujitsu_mixer,
@@ -4906,6 +5163,8 @@ static int patch_alc260(struct hda_codec *codec)
        spec->stream_digital_playback = &alc260_pcm_digital_playback;
        spec->stream_digital_capture = &alc260_pcm_digital_capture;
 
+       spec->vmaster_nid = 0x08;
+
        codec->patch_ops = alc_patch_ops;
        if (board_config == ALC260_AUTO)
                spec->init_hook = alc260_auto_init;
@@ -5106,15 +5365,15 @@ static struct snd_kcontrol_new alc882_base_mixer[] = {
 };
 
 static struct snd_kcontrol_new alc885_mbp3_mixer[] = {
-       HDA_CODEC_VOLUME("Master Volume", 0x0c, 0x00, HDA_OUTPUT),
-       HDA_BIND_MUTE   ("Master Switch", 0x0c, 0x02, HDA_INPUT),
-       HDA_CODEC_MUTE  ("Speaker Switch", 0x14, 0x00, HDA_OUTPUT),
-       HDA_CODEC_VOLUME("Line Out Volume", 0x0d,0x00, HDA_OUTPUT),
-       HDA_CODEC_VOLUME("Line In Playback Volume", 0x0b, 0x02, HDA_INPUT),
-       HDA_CODEC_MUTE  ("Line In Playback Switch", 0x0b, 0x02, HDA_INPUT),
+       HDA_CODEC_VOLUME("Front Playback Volume", 0x0c, 0x00, HDA_OUTPUT),
+       HDA_BIND_MUTE   ("Front Playback Switch", 0x0c, 0x02, HDA_INPUT),
+       HDA_CODEC_MUTE  ("Speaker Playback Switch", 0x14, 0x00, HDA_OUTPUT),
+       HDA_CODEC_VOLUME("Line-Out Playback Volume", 0x0d, 0x00, HDA_OUTPUT),
+       HDA_CODEC_VOLUME("Line Playback Volume", 0x0b, 0x02, HDA_INPUT),
+       HDA_CODEC_MUTE  ("Line Playback Switch", 0x0b, 0x02, HDA_INPUT),
        HDA_CODEC_VOLUME("Mic Playback Volume", 0x0b, 0x00, HDA_INPUT),
        HDA_CODEC_MUTE  ("Mic Playback Switch", 0x0b, 0x00, HDA_INPUT),
-       HDA_CODEC_VOLUME("Line In Boost", 0x1a, 0x00, HDA_INPUT),
+       HDA_CODEC_VOLUME("Line Boost", 0x1a, 0x00, HDA_INPUT),
        HDA_CODEC_VOLUME("Mic Boost", 0x18, 0x00, HDA_INPUT),
        { } /* end */
 };
@@ -5679,7 +5938,6 @@ static struct snd_kcontrol_new alc882_capture_alt_mixer[] = {
                .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
                /* The multiple "Capture Source" controls confuse alsamixer
                 * So call somewhat different..
-                * FIXME: the controls appear in the "playback" view!
                 */
                /* .name = "Capture Source", */
                .name = "Input Source",
@@ -5702,7 +5960,6 @@ static struct snd_kcontrol_new alc882_capture_mixer[] = {
                .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
                /* The multiple "Capture Source" controls confuse alsamixer
                 * So call somewhat different..
-                * FIXME: the controls appear in the "playback" view!
                 */
                /* .name = "Capture Source", */
                .name = "Input Source",
@@ -5743,16 +6000,17 @@ static const char *alc882_models[ALC882_MODEL_LAST] = {
 
 static struct snd_pci_quirk alc882_cfg_tbl[] = {
        SND_PCI_QUIRK(0x1019, 0x6668, "ECS", ALC882_6ST_DIG),
-       SND_PCI_QUIRK(0x105b, 0x6668, "Foxconn", ALC882_6ST_DIG),
-       SND_PCI_QUIRK(0x1462, 0x6668, "MSI", ALC882_6ST_DIG),
-       SND_PCI_QUIRK(0x1462, 0x28fb, "Targa T8", ALC882_TARGA), /* MSI-1049 T8  */
-       SND_PCI_QUIRK(0x161f, 0x2054, "Arima W820", ALC882_ARIMA),
        SND_PCI_QUIRK(0x1043, 0x060d, "Asus A7J", ALC882_ASUS_A7J),
        SND_PCI_QUIRK(0x1043, 0x1243, "Asus A7J", ALC882_ASUS_A7J),
        SND_PCI_QUIRK(0x1043, 0x13c2, "Asus A7M", ALC882_ASUS_A7M),
+       SND_PCI_QUIRK(0x1043, 0x1971, "Asus W2JC", ALC882_W2JC),
        SND_PCI_QUIRK(0x1043, 0x817f, "Asus P5LD2", ALC882_6ST_DIG),
        SND_PCI_QUIRK(0x1043, 0x81d8, "Asus P5WD", ALC882_6ST_DIG),
-       SND_PCI_QUIRK(0x1043, 0x1971, "Asus W2JC", ALC882_W2JC),
+       SND_PCI_QUIRK(0x105b, 0x6668, "Foxconn", ALC882_6ST_DIG),
+       SND_PCI_QUIRK(0x1458, 0xa002, "Gigabyte P35 DS3R", ALC882_6ST_DIG),
+       SND_PCI_QUIRK(0x1462, 0x28fb, "Targa T8", ALC882_TARGA), /* MSI-1049 T8  */
+       SND_PCI_QUIRK(0x1462, 0x6668, "MSI", ALC882_6ST_DIG),
+       SND_PCI_QUIRK(0x161f, 0x2054, "Arima W820", ALC882_ARIMA),
        {}
 };
 
@@ -5990,7 +6248,7 @@ static int alc_auto_add_mic_boost(struct hda_codec *codec)
        hda_nid_t nid;
 
        nid = spec->autocfg.input_pins[AUTO_PIN_MIC];
-       if (nid) {
+       if (nid && (get_wcaps(codec, nid) & AC_WCAP_IN_AMP)) {
                err = add_control(spec, ALC_CTL_WIDGET_VOL,
                                  "Mic Boost",
                                  HDA_COMPOSE_AMP_VAL(nid, 3, 0, HDA_INPUT));
@@ -5998,7 +6256,7 @@ static int alc_auto_add_mic_boost(struct hda_codec *codec)
                        return err;
        }
        nid = spec->autocfg.input_pins[AUTO_PIN_FRONT_MIC];
-       if (nid) {
+       if (nid && (get_wcaps(codec, nid) & AC_WCAP_IN_AMP)) {
                err = add_control(spec, ALC_CTL_WIDGET_VOL,
                                  "Front Mic Boost",
                                  HDA_COMPOSE_AMP_VAL(nid, 3, 0, HDA_INPUT));
@@ -6061,6 +6319,7 @@ static int patch_alc882(struct hda_codec *codec)
                case 0x106b1000: /* iMac 24 */
                        board_config = ALC885_IMAC24;
                        break;
+               case 0x106b00a1: /* Macbook */
                case 0x106b2c00: /* Macbook Pro rev3 */
                        board_config = ALC885_MBP3;
                        break;
@@ -6093,6 +6352,9 @@ static int patch_alc882(struct hda_codec *codec)
        spec->stream_name_analog = "ALC882 Analog";
        spec->stream_analog_playback = &alc882_pcm_analog_playback;
        spec->stream_analog_capture = &alc882_pcm_analog_capture;
+       /* FIXME: setup DAC5 */
+       /*spec->stream_analog_alt_playback = &alc880_pcm_analog_alt_playback;*/
+       spec->stream_analog_alt_capture = &alc880_pcm_analog_alt_capture;
 
        spec->stream_name_digital = "ALC882 Digital";
        spec->stream_digital_playback = &alc882_pcm_digital_playback;
@@ -6117,6 +6379,8 @@ static int patch_alc882(struct hda_codec *codec)
                }
        }
 
+       spec->vmaster_nid = 0x0c;
+
        codec->patch_ops = alc_patch_ops;
        if (board_config == ALC882_AUTO)
                spec->init_hook = alc882_auto_init;
@@ -6340,6 +6604,36 @@ static struct snd_kcontrol_new alc883_base_mixer[] = {
        { } /* end */
 };
 
+static struct snd_kcontrol_new alc883_mitac_mixer[] = {
+       HDA_CODEC_VOLUME("Front Playback Volume", 0x0c, 0x0, HDA_OUTPUT),
+       HDA_BIND_MUTE("Front Playback Switch", 0x0c, 2, HDA_INPUT),
+       HDA_CODEC_VOLUME_MONO("Center Playback Volume", 0x0e, 1, 0x0, HDA_OUTPUT),
+       HDA_CODEC_VOLUME_MONO("LFE Playback Volume", 0x0e, 2, 0x0, HDA_OUTPUT),
+       HDA_BIND_MUTE_MONO("Center Playback Switch", 0x0e, 1, 2, HDA_INPUT),
+       HDA_BIND_MUTE_MONO("LFE Playback Switch", 0x0e, 2, 2, HDA_INPUT),
+       HDA_CODEC_MUTE("Headphone Playback Switch", 0x15, 0x0, HDA_OUTPUT),
+       HDA_CODEC_VOLUME("Mic Playback Volume", 0x0b, 0x0, HDA_INPUT),
+       HDA_CODEC_VOLUME("Mic Boost", 0x18, 0, HDA_INPUT),
+       HDA_CODEC_MUTE("Mic Playback Switch", 0x0b, 0x0, HDA_INPUT),
+       HDA_CODEC_VOLUME("Front Mic Playback Volume", 0x0b, 0x1, HDA_INPUT),
+       HDA_CODEC_VOLUME("Front Mic Boost", 0x19, 0, HDA_INPUT),
+       HDA_CODEC_MUTE("Front Mic Playback Switch", 0x0b, 0x1, HDA_INPUT),
+       HDA_CODEC_VOLUME("Capture Volume", 0x08, 0x0, HDA_INPUT),
+       HDA_CODEC_MUTE("Capture Switch", 0x08, 0x0, HDA_INPUT),
+       HDA_CODEC_VOLUME_IDX("Capture Volume", 1, 0x09, 0x0, HDA_INPUT),
+       HDA_CODEC_MUTE_IDX("Capture Switch", 1, 0x09, 0x0, HDA_INPUT),
+       {
+               .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+               /* .name = "Capture Source", */
+               .name = "Input Source",
+               .count = 2,
+               .info = alc883_mux_enum_info,
+               .get = alc883_mux_enum_get,
+               .put = alc883_mux_enum_put,
+       },
+       { } /* end */
+};
+
 static struct snd_kcontrol_new alc883_3ST_2ch_mixer[] = {
        HDA_CODEC_VOLUME("Front Playback Volume", 0x0c, 0x0, HDA_OUTPUT),
        HDA_BIND_MUTE("Front Playback Switch", 0x0c, 2, HDA_INPUT),
@@ -6508,8 +6802,8 @@ static struct snd_kcontrol_new alc883_tagra_2ch_mixer[] = {
 static struct snd_kcontrol_new alc883_lenovo_101e_2ch_mixer[] = {
        HDA_CODEC_VOLUME("Front Playback Volume", 0x0c, 0x0, HDA_OUTPUT),
        HDA_BIND_MUTE("Front Playback Switch", 0x0c, 2, HDA_INPUT),
-       HDA_CODEC_VOLUME("iSpeaker Playback Volume", 0x0d, 0x0, HDA_OUTPUT),
-       HDA_BIND_MUTE("iSpeaker Playback Switch", 0x0d, 2, HDA_INPUT),
+       HDA_CODEC_VOLUME("Speaker Playback Volume", 0x0d, 0x0, HDA_OUTPUT),
+       HDA_BIND_MUTE("Speaker Playback Switch", 0x0d, 2, HDA_INPUT),
        HDA_CODEC_MUTE("Headphone Playback Switch", 0x1b, 0x0, HDA_OUTPUT),
        HDA_CODEC_VOLUME("Mic Playback Volume", 0x0b, 0x1, HDA_INPUT),
        HDA_CODEC_VOLUME("Mic Boost", 0x18, 0, HDA_INPUT),
@@ -6658,10 +6952,50 @@ static struct snd_kcontrol_new alc888_3st_hp_mixer[] = {
        { } /* end */
 };
 
-static struct snd_kcontrol_new alc883_acer_aspire_mixer[] = {
+static struct snd_kcontrol_new alc888_6st_dell_mixer[] = {
        HDA_CODEC_VOLUME("Front Playback Volume", 0x0c, 0x0, HDA_OUTPUT),
        HDA_BIND_MUTE("Front Playback Switch", 0x0c, 2, HDA_INPUT),
-       HDA_CODEC_MUTE("Headphone Playback Switch", 0x14, 0x0, HDA_OUTPUT),
+       HDA_CODEC_VOLUME("Surround Playback Volume", 0x0e, 0x0, HDA_OUTPUT),
+       HDA_BIND_MUTE("Surround Playback Switch", 0x0e, 2, HDA_INPUT),
+       HDA_CODEC_VOLUME_MONO("Center Playback Volume", 0x0d, 1, 0x0, HDA_OUTPUT),
+       HDA_CODEC_VOLUME_MONO("LFE Playback Volume", 0x0d, 2, 0x0, HDA_OUTPUT),
+       HDA_BIND_MUTE_MONO("Center Playback Switch", 0x0d, 1, 2, HDA_INPUT),
+       HDA_BIND_MUTE_MONO("LFE Playback Switch", 0x0d, 2, 2, HDA_INPUT),
+       HDA_CODEC_VOLUME("Side Playback Volume", 0x0f, 0x0, HDA_OUTPUT),
+       HDA_BIND_MUTE("Side Playback Switch", 0x0f, 2, HDA_INPUT),
+       HDA_CODEC_MUTE("Headphone Playback Switch", 0x1b, 0x0, HDA_OUTPUT),
+       HDA_CODEC_VOLUME("CD Playback Volume", 0x0b, 0x04, HDA_INPUT),
+       HDA_CODEC_MUTE("CD Playback Switch", 0x0b, 0x04, HDA_INPUT),
+       HDA_CODEC_VOLUME("Line Playback Volume", 0x0b, 0x02, HDA_INPUT),
+       HDA_CODEC_MUTE("Line Playback Switch", 0x0b, 0x02, HDA_INPUT),
+       HDA_CODEC_VOLUME("Mic Playback Volume", 0x0b, 0x0, HDA_INPUT),
+       HDA_CODEC_VOLUME("Mic Boost", 0x18, 0, HDA_INPUT),
+       HDA_CODEC_MUTE("Mic Playback Switch", 0x0b, 0x0, HDA_INPUT),
+       HDA_CODEC_VOLUME("Front Mic Playback Volume", 0x0b, 0x1, HDA_INPUT),
+       HDA_CODEC_VOLUME("Front Mic Boost", 0x19, 0, HDA_INPUT),
+       HDA_CODEC_MUTE("Front Mic Playback Switch", 0x0b, 0x1, HDA_INPUT),
+       HDA_CODEC_VOLUME("PC Speaker Playback Volume", 0x0b, 0x05, HDA_INPUT),
+       HDA_CODEC_MUTE("PC Speaker Playback Switch", 0x0b, 0x05, HDA_INPUT),
+       HDA_CODEC_VOLUME("Capture Volume", 0x08, 0x0, HDA_INPUT),
+       HDA_CODEC_MUTE("Capture Switch", 0x08, 0x0, HDA_INPUT),
+       HDA_CODEC_VOLUME_IDX("Capture Volume", 1, 0x09, 0x0, HDA_INPUT),
+       HDA_CODEC_MUTE_IDX("Capture Switch", 1, 0x09, 0x0, HDA_INPUT),
+       {
+               .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+               /* .name = "Capture Source", */
+               .name = "Input Source",
+               .count = 2,
+               .info = alc883_mux_enum_info,
+               .get = alc883_mux_enum_get,
+               .put = alc883_mux_enum_put,
+       },
+       { } /* end */
+};
+
+static struct snd_kcontrol_new alc883_acer_aspire_mixer[] = {
+       HDA_CODEC_VOLUME("Front Playback Volume", 0x0c, 0x0, HDA_OUTPUT),
+       HDA_BIND_MUTE("Front Playback Switch", 0x0c, 2, HDA_INPUT),
+       HDA_CODEC_MUTE("Headphone Playback Switch", 0x14, 0x0, HDA_OUTPUT),
        HDA_CODEC_VOLUME("Line Playback Volume", 0x0b, 0x02, HDA_INPUT),
        HDA_CODEC_MUTE("Line Playback Switch", 0x0b, 0x02, HDA_INPUT),
        HDA_CODEC_VOLUME("Mic Playback Volume", 0x0b, 0x0, HDA_INPUT),
@@ -6772,6 +7106,67 @@ static struct hda_verb alc883_init_verbs[] = {
        { }
 };
 
+/* toggle speaker-output according to the hp-jack state */
+static void alc883_mitac_hp_automute(struct hda_codec *codec)
+{
+       unsigned int present;
+
+       present = snd_hda_codec_read(codec, 0x15, 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, 0x17, HDA_OUTPUT, 0,
+                                HDA_AMP_MUTE, present ? HDA_AMP_MUTE : 0);
+}
+
+/* auto-toggle front mic */
+/*
+static void alc883_mitac_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;
+       bits = present ? HDA_AMP_MUTE : 0;
+       snd_hda_codec_amp_stereo(codec, 0x0b, HDA_INPUT, 1, HDA_AMP_MUTE, bits);
+}
+*/
+
+static void alc883_mitac_automute(struct hda_codec *codec)
+{
+       alc883_mitac_hp_automute(codec);
+       /* alc883_mitac_mic_automute(codec); */
+}
+
+static void alc883_mitac_unsol_event(struct hda_codec *codec,
+                                          unsigned int res)
+{
+       switch (res >> 26) {
+       case ALC880_HP_EVENT:
+               alc883_mitac_hp_automute(codec);
+               break;
+       case ALC880_MIC_EVENT:
+               /* alc883_mitac_mic_automute(codec); */
+               break;
+       }
+}
+
+static struct hda_verb alc883_mitac_verbs[] = {
+       /* HP */
+       {0x15, AC_VERB_SET_CONNECT_SEL, 0x00},
+       {0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP},
+       /* Subwoofer */
+       {0x17, AC_VERB_SET_CONNECT_SEL, 0x02},
+       {0x17, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
+
+       /* enable unsolicited event */
+       {0x15, AC_VERB_SET_UNSOLICITED_ENABLE, ALC880_HP_EVENT | AC_USRSP_EN},
+       /* {0x18, AC_VERB_SET_UNSOLICITED_ENABLE, ALC880_MIC_EVENT | AC_USRSP_EN}, */
+
+       { } /* end */
+};
+
 static struct hda_verb alc883_tagra_verbs[] = {
        {0x0c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
        {0x0c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
@@ -6843,6 +7238,15 @@ static struct hda_verb alc888_3st_hp_verbs[] = {
        { }
 };
 
+static struct hda_verb alc888_6st_dell_verbs[] = {
+       {0x14, AC_VERB_SET_CONNECT_SEL, 0x00},  /* Front: output 0 (0x0c) */
+       {0x15, AC_VERB_SET_CONNECT_SEL, 0x02},  /* Rear : output 1 (0x0e) */
+       {0x16, AC_VERB_SET_CONNECT_SEL, 0x01},  /* CLFE : output 2 (0x0d) */
+       {0x17, AC_VERB_SET_CONNECT_SEL, 0x03},  /* Side : output 3 (0x0f) */
+       {0x1b, AC_VERB_SET_UNSOLICITED_ENABLE, ALC880_HP_EVENT | AC_USRSP_EN},
+       { }
+};
+
 static struct hda_verb alc888_3st_hp_2ch_init[] = {
        { 0x18, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80 },
        { 0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE },
@@ -7038,6 +7442,33 @@ static struct hda_verb alc883_acer_eapd_verbs[] = {
        { }
 };
 
+static void alc888_6st_dell_front_automute(struct hda_codec *codec)
+{
+       unsigned int present;
+       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,
+                               HDA_AMP_MUTE, present ? HDA_AMP_MUTE : 0);
+       snd_hda_codec_amp_stereo(codec, 0x16, HDA_OUTPUT, 0,
+                               HDA_AMP_MUTE, present ? HDA_AMP_MUTE : 0);
+       snd_hda_codec_amp_stereo(codec, 0x17, HDA_OUTPUT, 0,
+                               HDA_AMP_MUTE, present ? HDA_AMP_MUTE : 0);
+}
+
+static void alc888_6st_dell_unsol_event(struct hda_codec *codec,
+                                            unsigned int res)
+{
+       switch (res >> 26) {
+       case ALC880_HP_EVENT:
+               printk("hp_event\n");
+               alc888_6st_dell_front_automute(codec);
+               break;
+       }
+}
+
 /*
  * generic initialization of ADC, input mixers and output mixers
  */
@@ -7096,7 +7527,7 @@ static struct hda_verb alc883_auto_init_verbs[] = {
        {0x22, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
        {0x22, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(2)},
        /* {0x22, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(3)}, */
-       {0x23, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(4)},
+       {0x22, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(4)},
 
        { }
 };
@@ -7111,7 +7542,6 @@ static struct snd_kcontrol_new alc883_capture_mixer[] = {
                .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
                /* The multiple "Capture Source" controls confuse alsamixer
                 * So call somewhat different..
-                * FIXME: the controls appear in the "playback" view!
                 */
                /* .name = "Capture Source", */
                .name = "Input Source",
@@ -7130,6 +7560,7 @@ static struct snd_kcontrol_new alc883_capture_mixer[] = {
 /* pcm configuration: identiacal with ALC880 */
 #define alc883_pcm_analog_playback     alc880_pcm_analog_playback
 #define alc883_pcm_analog_capture      alc880_pcm_analog_capture
+#define alc883_pcm_analog_alt_capture  alc880_pcm_analog_alt_capture
 #define alc883_pcm_digital_playback    alc880_pcm_digital_playback
 #define alc883_pcm_digital_capture     alc880_pcm_digital_capture
 
@@ -7154,53 +7585,58 @@ static const char *alc883_models[ALC883_MODEL_LAST] = {
        [ALC883_HAIER_W66]      = "haier-w66",
        [ALC888_6ST_HP]         = "6stack-hp",
        [ALC888_3ST_HP]         = "3stack-hp",
+       [ALC888_6ST_DELL]       = "6stack-dell",
+       [ALC883_MITAC]          = "mitac",
        [ALC883_AUTO]           = "auto",
 };
 
 static struct snd_pci_quirk alc883_cfg_tbl[] = {
        SND_PCI_QUIRK(0x1019, 0x6668, "ECS", ALC883_3ST_6ch_DIG),
+       SND_PCI_QUIRK(0x1025, 0x006c, "Acer Aspire 9810", ALC883_ACER_ASPIRE),
+       SND_PCI_QUIRK(0x1025, 0x0110, "Acer Aspire", ALC883_ACER_ASPIRE),
+       SND_PCI_QUIRK(0x1025, 0x0112, "Acer Aspire 9303", ALC883_ACER_ASPIRE),
+       SND_PCI_QUIRK(0x1025, 0, "Acer laptop", ALC883_ACER), /* default Acer */
+       SND_PCI_QUIRK(0x1028, 0x020d, "Dell Inspiron 530", ALC888_6ST_DELL),
        SND_PCI_QUIRK(0x103c, 0x2a3d, "HP Pavillion", ALC883_6ST_DIG),
-       SND_PCI_QUIRK(0x108e, 0x534d, NULL, ALC883_3ST_6ch),
-       SND_PCI_QUIRK(0x1558, 0, "Clevo laptop", ALC883_LAPTOP_EAPD),
+       SND_PCI_QUIRK(0x103c, 0x2a4f, "HP Samba", ALC888_3ST_HP),
+       SND_PCI_QUIRK(0x103c, 0x2a60, "HP Lucknow", ALC888_3ST_HP),
+       SND_PCI_QUIRK(0x103c, 0x2a61, "HP Nettle", ALC888_6ST_HP),
+       SND_PCI_QUIRK(0x1043, 0x8249, "Asus M2A-VM HDMI", ALC883_3ST_6ch_DIG),
        SND_PCI_QUIRK(0x105b, 0x6668, "Foxconn", ALC883_6ST_DIG),
+       SND_PCI_QUIRK(0x1071, 0x8253, "Mitac 8252d", ALC883_MITAC),
+       SND_PCI_QUIRK(0x1071, 0x8258, "Evesham Voyaeger", ALC883_LAPTOP_EAPD),
+       SND_PCI_QUIRK(0x108e, 0x534d, NULL, ALC883_3ST_6ch),
        SND_PCI_QUIRK(0x1458, 0xa002, "MSI", ALC883_6ST_DIG),
-       SND_PCI_QUIRK(0x1462, 0x6668, "MSI", ALC883_6ST_DIG),
-       SND_PCI_QUIRK(0x1462, 0x7187, "MSI", ALC883_6ST_DIG),
-       SND_PCI_QUIRK(0x1462, 0x7250, "MSI", ALC883_6ST_DIG),
-       SND_PCI_QUIRK(0x1462, 0x7280, "MSI", ALC883_6ST_DIG),
-       SND_PCI_QUIRK(0x1462, 0x7327, "MSI", ALC883_6ST_DIG),
        SND_PCI_QUIRK(0x1462, 0x0349, "MSI", ALC883_TARGA_2ch_DIG),
+       SND_PCI_QUIRK(0x1462, 0x040d, "MSI", ALC883_TARGA_2ch_DIG),
        SND_PCI_QUIRK(0x1462, 0x0579, "MSI", ALC883_TARGA_2ch_DIG),
        SND_PCI_QUIRK(0x1462, 0x3729, "MSI S420", ALC883_TARGA_DIG),
-       SND_PCI_QUIRK(0x1462, 0x3ef9, "MSI", ALC883_TARGA_DIG),
        SND_PCI_QUIRK(0x1462, 0x3b7f, "MSI", ALC883_TARGA_2ch_DIG),
-       SND_PCI_QUIRK(0x1462, 0x3fcc, "MSI", ALC883_TARGA_DIG),
+       SND_PCI_QUIRK(0x1462, 0x3ef9, "MSI", ALC883_TARGA_DIG),
        SND_PCI_QUIRK(0x1462, 0x3fc1, "MSI", ALC883_TARGA_DIG),
        SND_PCI_QUIRK(0x1462, 0x3fc3, "MSI", ALC883_TARGA_DIG),
+       SND_PCI_QUIRK(0x1462, 0x3fcc, "MSI", ALC883_TARGA_DIG),
        SND_PCI_QUIRK(0x1462, 0x3fdf, "MSI", ALC883_TARGA_DIG),
        SND_PCI_QUIRK(0x1462, 0x4314, "MSI", ALC883_TARGA_DIG),
        SND_PCI_QUIRK(0x1462, 0x4319, "MSI", ALC883_TARGA_DIG),
        SND_PCI_QUIRK(0x1462, 0x4324, "MSI", ALC883_TARGA_DIG),
+       SND_PCI_QUIRK(0x1462, 0x6668, "MSI", ALC883_6ST_DIG),
+       SND_PCI_QUIRK(0x1462, 0x7187, "MSI", ALC883_6ST_DIG),
+       SND_PCI_QUIRK(0x1462, 0x7250, "MSI", ALC883_6ST_DIG),
+       SND_PCI_QUIRK(0x1462, 0x7280, "MSI", ALC883_6ST_DIG),
+       SND_PCI_QUIRK(0x1462, 0x7327, "MSI", ALC883_6ST_DIG),
        SND_PCI_QUIRK(0x1462, 0xa422, "MSI", ALC883_TARGA_2ch_DIG),
-       SND_PCI_QUIRK(0x1025, 0x006c, "Acer Aspire 9810", ALC883_ACER_ASPIRE),
-       SND_PCI_QUIRK(0x1025, 0x0110, "Acer Aspire", ALC883_ACER_ASPIRE),
-       SND_PCI_QUIRK(0x1025, 0x0112, "Acer Aspire 9303", ALC883_ACER_ASPIRE),
-       SND_PCI_QUIRK(0x1025, 0, "Acer laptop", ALC883_ACER),
+       SND_PCI_QUIRK(0x147b, 0x1083, "Abit IP35-PRO", ALC883_6ST_DIG),
+       SND_PCI_QUIRK(0x1558, 0, "Clevo laptop", ALC883_LAPTOP_EAPD),
        SND_PCI_QUIRK(0x15d9, 0x8780, "Supermicro PDSBA", ALC883_3ST_6ch),
        SND_PCI_QUIRK(0x161f, 0x2054, "Medion laptop", ALC883_MEDION),
-       SND_PCI_QUIRK(0x1071, 0x8258, "Evesham Voyaeger", ALC883_LAPTOP_EAPD),
-       SND_PCI_QUIRK(0x8086, 0xd601, "D102GGC", ALC883_3ST_6ch),
        SND_PCI_QUIRK(0x17aa, 0x101e, "Lenovo 101e", ALC883_LENOVO_101E_2ch),
-       SND_PCI_QUIRK(0x17aa, 0x3bfd, "Lenovo NB0763", ALC883_LENOVO_NB0763),
        SND_PCI_QUIRK(0x17aa, 0x2085, "Lenovo NB0763", ALC883_LENOVO_NB0763),
-       SND_PCI_QUIRK(0x103c, 0x2a61, "HP Nettle", ALC888_6ST_HP),
-       SND_PCI_QUIRK(0x103c, 0x2a60, "HP Lucknow", ALC888_3ST_HP),
-       SND_PCI_QUIRK(0x103c, 0x2a4f, "HP Samba", ALC888_3ST_HP),
+       SND_PCI_QUIRK(0x17aa, 0x3bfc, "Lenovo NB0763", ALC883_LENOVO_NB0763),
+       SND_PCI_QUIRK(0x17aa, 0x3bfd, "Lenovo NB0763", ALC883_LENOVO_NB0763),
        SND_PCI_QUIRK(0x17c0, 0x4071, "MEDION MD2", ALC883_MEDION_MD2),
        SND_PCI_QUIRK(0x1991, 0x5625, "Haier W66", ALC883_HAIER_W66),
-       SND_PCI_QUIRK(0x17aa, 0x3bfc, "Lenovo NB0763", ALC883_LENOVO_NB0763),
-       SND_PCI_QUIRK(0x1043, 0x8249, "Asus M2A-VM HDMI", ALC883_3ST_6ch_DIG),
-       SND_PCI_QUIRK(0x147b, 0x1083, "Abit IP35-PRO", ALC883_6ST_DIG),
+       SND_PCI_QUIRK(0x8086, 0xd601, "D102GGC", ALC883_3ST_6ch),
        {}
 };
 
@@ -7435,6 +7871,34 @@ static struct alc_config_preset alc883_presets[] = {
                .need_dac_fix = 1,
                .input_mux = &alc883_capture_source,
        },
+       [ALC888_6ST_DELL] = {
+               .mixers = { alc888_6st_dell_mixer, alc883_chmode_mixer },
+               .init_verbs = { alc883_init_verbs, alc888_6st_dell_verbs },
+               .num_dacs = ARRAY_SIZE(alc883_dac_nids),
+               .dac_nids = alc883_dac_nids,
+               .dig_out_nid = ALC883_DIGOUT_NID,
+               .num_adc_nids = ARRAY_SIZE(alc883_adc_nids),
+               .adc_nids = alc883_adc_nids,
+               .dig_in_nid = ALC883_DIGIN_NID,
+               .num_channel_mode = ARRAY_SIZE(alc883_sixstack_modes),
+               .channel_mode = alc883_sixstack_modes,
+               .input_mux = &alc883_capture_source,
+               .unsol_event = alc888_6st_dell_unsol_event,
+               .init_hook = alc888_6st_dell_front_automute,
+       },
+       [ALC883_MITAC] = {
+               .mixers = { alc883_mitac_mixer },
+               .init_verbs = { alc883_init_verbs, alc883_mitac_verbs },
+               .num_dacs = ARRAY_SIZE(alc883_dac_nids),
+               .dac_nids = alc883_dac_nids,
+               .num_adc_nids = ARRAY_SIZE(alc883_adc_nids),
+               .adc_nids = alc883_adc_nids,
+               .num_channel_mode = ARRAY_SIZE(alc883_3ST_2ch_modes),
+               .channel_mode = alc883_3ST_2ch_modes,
+               .input_mux = &alc883_capture_source,
+               .unsol_event = alc883_mitac_unsol_event,
+               .init_hook = alc883_mitac_automute,
+       },
 };
 
 
@@ -7582,6 +8046,7 @@ static int patch_alc883(struct hda_codec *codec)
        spec->stream_name_analog = "ALC883 Analog";
        spec->stream_analog_playback = &alc883_pcm_analog_playback;
        spec->stream_analog_capture = &alc883_pcm_analog_capture;
+       spec->stream_analog_alt_capture = &alc883_pcm_analog_alt_capture;
 
        spec->stream_name_digital = "ALC883 Digital";
        spec->stream_digital_playback = &alc883_pcm_digital_playback;
@@ -7592,6 +8057,8 @@ static int patch_alc883(struct hda_codec *codec)
                spec->num_adc_nids = ARRAY_SIZE(alc883_adc_nids);
        }
 
+       spec->vmaster_nid = 0x0c;
+
        codec->patch_ops = alc_patch_ops;
        if (board_config == ALC883_AUTO)
                spec->init_hook = alc883_auto_init;
@@ -7659,13 +8126,99 @@ static struct snd_kcontrol_new alc262_hippo1_mixer[] = {
        { } /* end */
 };
 
+/* update HP, line and mono-out pins according to the master switch */
+static void alc262_hp_master_update(struct hda_codec *codec)
+{
+       struct alc_spec *spec = codec->spec;
+       int val = spec->master_sw;
+
+       /* HP & line-out */
+       snd_hda_codec_write_cache(codec, 0x1b, 0,
+                                 AC_VERB_SET_PIN_WIDGET_CONTROL,
+                                 val ? PIN_HP : 0);
+       snd_hda_codec_write_cache(codec, 0x15, 0,
+                                 AC_VERB_SET_PIN_WIDGET_CONTROL,
+                                 val ? PIN_HP : 0);
+       /* mono (speaker) depending on the HP jack sense */
+       val = val && !spec->jack_present;
+       snd_hda_codec_write_cache(codec, 0x16, 0,
+                                 AC_VERB_SET_PIN_WIDGET_CONTROL,
+                                 val ? PIN_OUT : 0);
+}
+
+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);
+       alc262_hp_master_update(codec);
+}
+
+static void alc262_hp_bpc_unsol_event(struct hda_codec *codec, unsigned int res)
+{
+       if ((res >> 26) != ALC880_HP_EVENT)
+               return;
+       alc262_hp_bpc_automute(codec);
+}
+
+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);
+       alc262_hp_master_update(codec);
+}
+
+static void alc262_hp_wildwest_unsol_event(struct hda_codec *codec,
+                                          unsigned int res)
+{
+       if ((res >> 26) != ALC880_HP_EVENT)
+               return;
+       alc262_hp_wildwest_automute(codec);
+}
+
+static int alc262_hp_master_sw_get(struct snd_kcontrol *kcontrol,
+                                  struct snd_ctl_elem_value *ucontrol)
+{
+       struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
+       struct alc_spec *spec = codec->spec;
+       *ucontrol->value.integer.value = spec->master_sw;
+       return 0;
+}
+
+static int alc262_hp_master_sw_put(struct snd_kcontrol *kcontrol,
+                                  struct snd_ctl_elem_value *ucontrol)
+{
+       struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
+       struct alc_spec *spec = codec->spec;
+       int val = !!*ucontrol->value.integer.value;
+
+       if (val == spec->master_sw)
+               return 0;
+       spec->master_sw = val;
+       alc262_hp_master_update(codec);
+       return 1;
+}
+
 static struct snd_kcontrol_new alc262_HP_BPC_mixer[] = {
+       {
+               .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+               .name = "Master Playback Switch",
+               .info = snd_ctl_boolean_mono_info,
+               .get = alc262_hp_master_sw_get,
+               .put = alc262_hp_master_sw_put,
+       },
        HDA_CODEC_VOLUME("Front Playback Volume", 0x0c, 0x0, HDA_OUTPUT),
        HDA_CODEC_MUTE("Front Playback Switch", 0x15, 0x0, HDA_OUTPUT),
        HDA_CODEC_MUTE("Headphone Playback Switch", 0x1b, 0x0, HDA_OUTPUT),
-       HDA_CODEC_VOLUME_MONO("Mono Playback Volume", 0x0e, 2, 0x0, HDA_OUTPUT),
-       HDA_CODEC_MUTE_MONO("Mono Playback Switch", 0x16, 2, 0x0, HDA_OUTPUT),
-
+       HDA_CODEC_VOLUME_MONO("Speaker Playback Volume", 0x0e, 2, 0x0,
+                             HDA_OUTPUT),
+       HDA_CODEC_MUTE_MONO("Speaker Playback Switch", 0x16, 2, 0x0,
+                           HDA_OUTPUT),
        HDA_CODEC_VOLUME("Mic Playback Volume", 0x0b, 0x0, HDA_INPUT),
        HDA_CODEC_MUTE("Mic Playback Switch", 0x0b, 0x0, HDA_INPUT),
        HDA_CODEC_VOLUME("Mic Boost", 0x18, 0, HDA_INPUT),
@@ -7684,12 +8237,21 @@ static struct snd_kcontrol_new alc262_HP_BPC_mixer[] = {
 };
 
 static struct snd_kcontrol_new alc262_HP_BPC_WildWest_mixer[] = {
+       {
+               .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+               .name = "Master Playback Switch",
+               .info = snd_ctl_boolean_mono_info,
+               .get = alc262_hp_master_sw_get,
+               .put = alc262_hp_master_sw_put,
+       },
        HDA_CODEC_VOLUME("Front Playback Volume", 0x0c, 0x0, HDA_OUTPUT),
        HDA_CODEC_MUTE("Front Playback Switch", 0x1b, 0x0, HDA_OUTPUT),
        HDA_CODEC_VOLUME("Headphone Playback Volume", 0x0d, 0x0, HDA_OUTPUT),
        HDA_CODEC_MUTE("Headphone Playback Switch", 0x15, 0x0, HDA_OUTPUT),
-       HDA_CODEC_VOLUME_MONO("Mono Playback Volume", 0x0e, 2, 0x0, HDA_OUTPUT),
-       HDA_CODEC_MUTE_MONO("Mono Playback Switch", 0x16, 2, 0x0, HDA_OUTPUT),
+       HDA_CODEC_VOLUME_MONO("Speaker Playback Volume", 0x0e, 2, 0x0,
+                             HDA_OUTPUT),
+       HDA_CODEC_MUTE_MONO("Speaker Playback Switch", 0x16, 2, 0x0,
+                           HDA_OUTPUT),
        HDA_CODEC_VOLUME("Front Mic Playback Volume", 0x0b, 0x02, HDA_INPUT),
        HDA_CODEC_MUTE("Front Mic Playback Switch", 0x0b, 0x02, HDA_INPUT),
        HDA_CODEC_VOLUME("Front Mic Boost", 0x1a, 0, HDA_INPUT),
@@ -7709,34 +8271,113 @@ static struct snd_kcontrol_new alc262_HP_BPC_WildWest_option_mixer[] = {
        { } /* end */
 };
 
-/* bind hp and internal speaker mute (with plug check) */
-static int alc262_sony_master_sw_put(struct snd_kcontrol *kcontrol,
-                                    struct snd_ctl_elem_value *ucontrol)
+/* mute/unmute internal speaker according to the hp jack and mute state */
+static void alc262_hp_t5735_automute(struct hda_codec *codec, int force)
 {
-       struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
-       long *valp = ucontrol->value.integer.value;
-       int change;
+       struct alc_spec *spec = codec->spec;
 
-       /* change hp mute */
-       change = snd_hda_codec_amp_update(codec, 0x15, 0, HDA_OUTPUT, 0,
-                                         HDA_AMP_MUTE,
-                                         valp[0] ? 0 : HDA_AMP_MUTE);
-       change |= snd_hda_codec_amp_update(codec, 0x15, 1, HDA_OUTPUT, 0,
-                                          HDA_AMP_MUTE,
-                                          valp[1] ? 0 : HDA_AMP_MUTE);
-       if (change) {
-               /* change speaker according to HP jack state */
-               struct alc_spec *spec = codec->spec;
-               unsigned int mute;
-               if (spec->jack_present)
-                       mute = HDA_AMP_MUTE;
-               else
-                       mute = snd_hda_codec_amp_read(codec, 0x15, 0,
-                                                     HDA_OUTPUT, 0);
-               snd_hda_codec_amp_stereo(codec, 0x14, HDA_OUTPUT, 0,
-                                        HDA_AMP_MUTE, mute);
-       }
-       return change;
+       if (force || !spec->sense_updated) {
+               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->sense_updated = 1;
+       }
+       snd_hda_codec_amp_stereo(codec, 0x0c, HDA_OUTPUT, 0, HDA_AMP_MUTE,
+                                spec->jack_present ? HDA_AMP_MUTE : 0);
+}
+
+static void alc262_hp_t5735_unsol_event(struct hda_codec *codec,
+                                       unsigned int res)
+{
+       if ((res >> 26) != ALC880_HP_EVENT)
+               return;
+       alc262_hp_t5735_automute(codec, 1);
+}
+
+static void alc262_hp_t5735_init_hook(struct hda_codec *codec)
+{
+       alc262_hp_t5735_automute(codec, 1);
+}
+
+static struct snd_kcontrol_new alc262_hp_t5735_mixer[] = {
+       HDA_CODEC_VOLUME("Speaker Playback Volume", 0x0c, 0x0, HDA_OUTPUT),
+       HDA_CODEC_MUTE("Speaker Playback Switch", 0x14, 0x0, HDA_OUTPUT),
+       HDA_CODEC_VOLUME("Headphone Playback Volume", 0x0d, 0x0, HDA_OUTPUT),
+       HDA_CODEC_MUTE("Headphone Playback Switch", 0x15, 0x0, HDA_OUTPUT),
+       HDA_CODEC_VOLUME("Mic Playback Volume", 0x0b, 0x0, HDA_INPUT),
+       HDA_CODEC_MUTE("Mic Playback Switch", 0x0b, 0x0, HDA_INPUT),
+       HDA_CODEC_VOLUME("Mic Boost", 0x18, 0, HDA_INPUT),
+       { } /* end */
+};
+
+static struct hda_verb alc262_hp_t5735_verbs[] = {
+       {0x14, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
+       {0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP},
+
+       {0x15, AC_VERB_SET_UNSOLICITED_ENABLE, ALC880_HP_EVENT | AC_USRSP_EN},
+       { }
+};
+
+static struct snd_kcontrol_new alc262_hp_rp5700_mixer[] = {
+       HDA_CODEC_VOLUME("Headphone Playback Volume", 0x0c, 0x0, HDA_OUTPUT),
+       HDA_CODEC_MUTE("Headphone Playback Switch", 0x1b, 0x0, HDA_OUTPUT),
+       HDA_CODEC_VOLUME("Speaker Playback Volume", 0x0e, 0x0, HDA_OUTPUT),
+       HDA_CODEC_MUTE("Speaker Playback Switch", 0x16, 0x0, HDA_OUTPUT),
+       HDA_CODEC_VOLUME("Line Playback Volume", 0x0b, 0x01, HDA_INPUT),
+       HDA_CODEC_MUTE("Line Playback Switch", 0x0b, 0x01, HDA_INPUT),
+       { } /* end */
+};
+
+static struct hda_verb alc262_hp_rp5700_verbs[] = {
+       {0x1b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
+       {0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
+       {0x16, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
+       {0x1b, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
+       {0x19, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN},
+       {0x1b, AC_VERB_SET_CONNECT_SEL, 0x00},
+       {0x23, AC_VERB_SET_AMP_GAIN_MUTE, (0x7000 | (0x01 << 8))},
+       {0x24, AC_VERB_SET_AMP_GAIN_MUTE, (0x7000 | (0x01 << 8))},
+       {0x23, AC_VERB_SET_AMP_GAIN_MUTE, (0x7080 | (0x00 << 8))},
+       {0x24, AC_VERB_SET_AMP_GAIN_MUTE, (0x7080 | (0x00 << 8))},
+       {}
+};
+
+static struct hda_input_mux alc262_hp_rp5700_capture_source = {
+       .num_items = 1,
+       .items = {
+               { "Line", 0x1 },
+       },
+};
+
+/* bind hp and internal speaker mute (with plug check) */
+static int alc262_sony_master_sw_put(struct snd_kcontrol *kcontrol,
+                                    struct snd_ctl_elem_value *ucontrol)
+{
+       struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
+       long *valp = ucontrol->value.integer.value;
+       int change;
+
+       /* change hp mute */
+       change = snd_hda_codec_amp_update(codec, 0x15, 0, HDA_OUTPUT, 0,
+                                         HDA_AMP_MUTE,
+                                         valp[0] ? 0 : HDA_AMP_MUTE);
+       change |= snd_hda_codec_amp_update(codec, 0x15, 1, HDA_OUTPUT, 0,
+                                          HDA_AMP_MUTE,
+                                          valp[1] ? 0 : HDA_AMP_MUTE);
+       if (change) {
+               /* change speaker according to HP jack state */
+               struct alc_spec *spec = codec->spec;
+               unsigned int mute;
+               if (spec->jack_present)
+                       mute = HDA_AMP_MUTE;
+               else
+                       mute = snd_hda_codec_amp_read(codec, 0x15, 0,
+                                                     HDA_OUTPUT, 0);
+               snd_hda_codec_amp_stereo(codec, 0x14, HDA_OUTPUT, 0,
+                                        HDA_AMP_MUTE, mute);
+       }
+       return change;
 }
 
 static struct snd_kcontrol_new alc262_sony_mixer[] = {
@@ -8082,6 +8723,72 @@ static struct hda_verb alc262_benq_t31_EAPD_verbs[] = {
        {}
 };
 
+/* Samsung Q1 Ultra Vista model setup */
+static struct snd_kcontrol_new alc262_ultra_mixer[] = {
+       HDA_CODEC_VOLUME("Front Playback Volume", 0x0c, 0x0, HDA_OUTPUT),
+       HDA_CODEC_MUTE("Front Playback Switch", 0x14, 0x0, HDA_OUTPUT),
+       HDA_CODEC_MUTE("Headphone Playback Switch", 0x15, 0x0, HDA_OUTPUT),
+       HDA_CODEC_VOLUME("Mic Playback Volume", 0x0b, 0x01, HDA_INPUT),
+       HDA_CODEC_MUTE("Mic Playback Switch", 0x0b, 0x01, HDA_INPUT),
+       HDA_CODEC_VOLUME("Mic Boost", 0x19, 0, HDA_INPUT),
+       { } /* end */
+};
+
+static struct hda_verb alc262_ultra_verbs[] = {
+       {0x15, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | ALC880_HP_EVENT},
+       {0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP},
+       {0x18, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x40},
+       /* Mic is on Node 0x19 */
+       {0x19, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x24},
+       {0x22, AC_VERB_SET_CONNECT_SEL, 0x01},
+       {0x22, AC_VERB_SET_AMP_GAIN_MUTE, (0x7000 | (0x01 << 8))},
+       {0x23, AC_VERB_SET_CONNECT_SEL, 0x01},
+       {0x23, AC_VERB_SET_AMP_GAIN_MUTE, (0x7000 | (0x01 << 8))},
+       {0x24, AC_VERB_SET_CONNECT_SEL, 0x01},
+       {0x24, AC_VERB_SET_AMP_GAIN_MUTE, (0x7000 | (0x01 << 8))},
+       {}
+};
+
+static struct hda_input_mux alc262_ultra_capture_source = {
+       .num_items = 1,
+       .items = {
+               { "Mic", 0x1 },
+       },
+};
+
+/* mute/unmute internal speaker according to the hp jack and mute state */
+static void alc262_ultra_automute(struct hda_codec *codec)
+{
+       struct alc_spec *spec = codec->spec;
+       unsigned int mute;
+       unsigned int present;
+
+       /* need to execute and sync at first */
+       snd_hda_codec_read(codec, 0x15, 0, AC_VERB_SET_PIN_SENSE, 0);
+       present = snd_hda_codec_read(codec, 0x15, 0,
+                                    AC_VERB_GET_PIN_SENSE, 0);
+       spec->jack_present = (present & 0x80000000) != 0;
+       if (spec->jack_present) {
+               /* mute internal speaker */
+               snd_hda_codec_amp_stereo(codec, 0x14, HDA_OUTPUT, 0,
+                                        HDA_AMP_MUTE, HDA_AMP_MUTE);
+       } else {
+               /* unmute internal speaker if necessary */
+               mute = snd_hda_codec_amp_read(codec, 0x15, 0, HDA_OUTPUT, 0);
+               snd_hda_codec_amp_stereo(codec, 0x14, HDA_OUTPUT, 0,
+                                        HDA_AMP_MUTE, mute);
+       }
+}
+
+/* unsolicited event for HP jack sensing */
+static void alc262_ultra_unsol_event(struct hda_codec *codec,
+                                      unsigned int res)
+{
+       if ((res >> 26) != ALC880_HP_EVENT)
+               return;
+       alc262_ultra_automute(codec);
+}
+
 /* add playback controls from the parsed DAC table */
 static int alc262_auto_create_multi_out_ctls(struct alc_spec *spec,
                                             const struct auto_pin_cfg *cfg)
@@ -8269,7 +8976,7 @@ static struct hda_verb alc262_HP_BPC_init_verbs[] = {
        {0x0e, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
        {0x0e, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
 
-       {0x1b, AC_VERB_SET_PIN_WIDGET_CONTROL, 0xc0},
+       {0x1b, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP},
        {0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT },
        {0x16, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT },
 
@@ -8311,6 +9018,8 @@ static struct hda_verb alc262_HP_BPC_init_verbs[] = {
        {0x22, AC_VERB_SET_AMP_GAIN_MUTE, (0x7000 | (0x02 << 8))},
        {0x22, AC_VERB_SET_AMP_GAIN_MUTE, (0x7000 | (0x04 << 8))},
 
+       {0x1b, AC_VERB_SET_UNSOLICITED_ENABLE, ALC880_HP_EVENT | AC_USRSP_EN},
+
        { }
 };
 
@@ -8405,6 +9114,8 @@ static struct hda_verb alc262_HP_BPC_WildWest_init_verbs[] = {
         /* {0x22, AC_VERB_SET_AMP_GAIN_MUTE, (0x7000 | (0x06 << 8))}, */
        {0x22, AC_VERB_SET_AMP_GAIN_MUTE, (0x7000 | (0x07 << 8))},
 
+       {0x15, AC_VERB_SET_UNSOLICITED_ENABLE, ALC880_HP_EVENT | AC_USRSP_EN},
+
        { }
 };
 
@@ -8484,39 +9195,49 @@ static const char *alc262_models[ALC262_MODEL_LAST] = {
        [ALC262_FUJITSU]        = "fujitsu",
        [ALC262_HP_BPC]         = "hp-bpc",
        [ALC262_HP_BPC_D7000_WL]= "hp-bpc-d7000",
+       [ALC262_HP_TC_T5735]    = "hp-tc-t5735",
+       [ALC262_HP_RP5700]      = "hp-rp5700",
        [ALC262_BENQ_ED8]       = "benq",
        [ALC262_BENQ_T31]       = "benq-t31",
        [ALC262_SONY_ASSAMD]    = "sony-assamd",
+       [ALC262_ULTRA]          = "ultra",
        [ALC262_AUTO]           = "auto",
 };
 
 static struct snd_pci_quirk alc262_cfg_tbl[] = {
        SND_PCI_QUIRK(0x1002, 0x437b, "Hippo", ALC262_HIPPO),
        SND_PCI_QUIRK(0x103c, 0x12fe, "HP xw9400", ALC262_HP_BPC),
-       SND_PCI_QUIRK(0x103c, 0x280c, "HP xw4400", ALC262_HP_BPC),
        SND_PCI_QUIRK(0x103c, 0x12ff, "HP xw4550", ALC262_HP_BPC),
-       SND_PCI_QUIRK(0x103c, 0x1308, "HP xw4600", ALC262_HP_BPC),
-       SND_PCI_QUIRK(0x103c, 0x3014, "HP xw6400", ALC262_HP_BPC),
-       SND_PCI_QUIRK(0x103c, 0x1307, "HP xw6600", ALC262_HP_BPC),
-       SND_PCI_QUIRK(0x103c, 0x3015, "HP xw8400", ALC262_HP_BPC),
        SND_PCI_QUIRK(0x103c, 0x1306, "HP xw8600", ALC262_HP_BPC),
+       SND_PCI_QUIRK(0x103c, 0x1307, "HP xw6600", ALC262_HP_BPC),
+       SND_PCI_QUIRK(0x103c, 0x1308, "HP xw4600", ALC262_HP_BPC),
+       SND_PCI_QUIRK(0x103c, 0x1309, "HP xw4*00", ALC262_HP_BPC),
+       SND_PCI_QUIRK(0x103c, 0x130a, "HP xw6*00", ALC262_HP_BPC),
+       SND_PCI_QUIRK(0x103c, 0x130b, "HP xw8*00", ALC262_HP_BPC),
        SND_PCI_QUIRK(0x103c, 0x2800, "HP D7000", ALC262_HP_BPC_D7000_WL),
-       SND_PCI_QUIRK(0x103c, 0x2802, "HP D7000", ALC262_HP_BPC_D7000_WL),
-       SND_PCI_QUIRK(0x103c, 0x2804, "HP D7000", ALC262_HP_BPC_D7000_WL),
-       SND_PCI_QUIRK(0x103c, 0x2806, "HP D7000", ALC262_HP_BPC_D7000_WL),
        SND_PCI_QUIRK(0x103c, 0x2801, "HP D7000", ALC262_HP_BPC_D7000_WF),
+       SND_PCI_QUIRK(0x103c, 0x2802, "HP D7000", ALC262_HP_BPC_D7000_WL),
        SND_PCI_QUIRK(0x103c, 0x2803, "HP D7000", ALC262_HP_BPC_D7000_WF),
+       SND_PCI_QUIRK(0x103c, 0x2804, "HP D7000", ALC262_HP_BPC_D7000_WL),
        SND_PCI_QUIRK(0x103c, 0x2805, "HP D7000", ALC262_HP_BPC_D7000_WF),
+       SND_PCI_QUIRK(0x103c, 0x2806, "HP D7000", ALC262_HP_BPC_D7000_WL),
        SND_PCI_QUIRK(0x103c, 0x2807, "HP D7000", ALC262_HP_BPC_D7000_WF),
+       SND_PCI_QUIRK(0x103c, 0x280c, "HP xw4400", ALC262_HP_BPC),
+       SND_PCI_QUIRK(0x103c, 0x3014, "HP xw6400", ALC262_HP_BPC),
+       SND_PCI_QUIRK(0x103c, 0x3015, "HP xw8400", ALC262_HP_BPC),
+       SND_PCI_QUIRK(0x103c, 0x302f, "HP Thin Client T5735",
+                     ALC262_HP_TC_T5735),
+       SND_PCI_QUIRK(0x103c, 0x2817, "HP RP5700", ALC262_HP_RP5700),
+       SND_PCI_QUIRK(0x104d, 0x1f00, "Sony ASSAMD", ALC262_SONY_ASSAMD),
        SND_PCI_QUIRK(0x104d, 0x8203, "Sony UX-90", ALC262_HIPPO),
+       SND_PCI_QUIRK(0x104d, 0x820f, "Sony ASSAMD", ALC262_SONY_ASSAMD),
+       SND_PCI_QUIRK(0x104d, 0x900e, "Sony ASSAMD", ALC262_SONY_ASSAMD),
+       SND_PCI_QUIRK(0x104d, 0x9015, "Sony 0x9015", ALC262_SONY_ASSAMD),
        SND_PCI_QUIRK(0x10cf, 0x1397, "Fujitsu", ALC262_FUJITSU),
-       SND_PCI_QUIRK(0x17ff, 0x058f, "Benq Hippo", ALC262_HIPPO_1),
+       SND_PCI_QUIRK(0x144d, 0xc032, "Samsung Q1 Ultra", ALC262_ULTRA),
        SND_PCI_QUIRK(0x17ff, 0x0560, "Benq ED8", ALC262_BENQ_ED8),
        SND_PCI_QUIRK(0x17ff, 0x058d, "Benq T31-16", ALC262_BENQ_T31),
-       SND_PCI_QUIRK(0x104d, 0x820f, "Sony ASSAMD", ALC262_SONY_ASSAMD),
-       SND_PCI_QUIRK(0x104d, 0x9015, "Sony 0x9015", ALC262_SONY_ASSAMD),
-       SND_PCI_QUIRK(0x104d, 0x900e, "Sony ASSAMD", ALC262_SONY_ASSAMD),
-       SND_PCI_QUIRK(0x104d, 0x1f00, "Sony ASSAMD", ALC262_SONY_ASSAMD),
+       SND_PCI_QUIRK(0x17ff, 0x058f, "Benq Hippo", ALC262_HIPPO_1),
        {}
 };
 
@@ -8579,6 +9300,8 @@ static struct alc_config_preset alc262_presets[] = {
                .num_channel_mode = ARRAY_SIZE(alc262_modes),
                .channel_mode = alc262_modes,
                .input_mux = &alc262_HP_capture_source,
+               .unsol_event = alc262_hp_bpc_unsol_event,
+               .init_hook = alc262_hp_bpc_automute,
        },
        [ALC262_HP_BPC_D7000_WF] = {
                .mixers = { alc262_HP_BPC_WildWest_mixer },
@@ -8589,6 +9312,8 @@ static struct alc_config_preset alc262_presets[] = {
                .num_channel_mode = ARRAY_SIZE(alc262_modes),
                .channel_mode = alc262_modes,
                .input_mux = &alc262_HP_D7000_capture_source,
+               .unsol_event = alc262_hp_wildwest_unsol_event,
+               .init_hook = alc262_hp_wildwest_automute,
        },
        [ALC262_HP_BPC_D7000_WL] = {
                .mixers = { alc262_HP_BPC_WildWest_mixer,
@@ -8600,7 +9325,30 @@ static struct alc_config_preset alc262_presets[] = {
                .num_channel_mode = ARRAY_SIZE(alc262_modes),
                .channel_mode = alc262_modes,
                .input_mux = &alc262_HP_D7000_capture_source,
+               .unsol_event = alc262_hp_wildwest_unsol_event,
+               .init_hook = alc262_hp_wildwest_automute,
+       },
+       [ALC262_HP_TC_T5735] = {
+               .mixers = { alc262_hp_t5735_mixer },
+               .init_verbs = { alc262_init_verbs, alc262_hp_t5735_verbs },
+               .num_dacs = ARRAY_SIZE(alc262_dac_nids),
+               .dac_nids = alc262_dac_nids,
+               .hp_nid = 0x03,
+               .num_channel_mode = ARRAY_SIZE(alc262_modes),
+               .channel_mode = alc262_modes,
+               .input_mux = &alc262_capture_source,
+               .unsol_event = alc262_hp_t5735_unsol_event,
+               .init_hook = alc262_hp_t5735_init_hook,
        },
+       [ALC262_HP_RP5700] = {
+               .mixers = { alc262_hp_rp5700_mixer },
+               .init_verbs = { alc262_init_verbs, alc262_hp_rp5700_verbs },
+               .num_dacs = ARRAY_SIZE(alc262_dac_nids),
+               .dac_nids = alc262_dac_nids,
+               .num_channel_mode = ARRAY_SIZE(alc262_modes),
+               .channel_mode = alc262_modes,
+               .input_mux = &alc262_hp_rp5700_capture_source,
+        },
        [ALC262_BENQ_ED8] = {
                .mixers = { alc262_base_mixer },
                .init_verbs = { alc262_init_verbs, alc262_EAPD_verbs },
@@ -8635,6 +9383,19 @@ static struct alc_config_preset alc262_presets[] = {
                .unsol_event = alc262_hippo_unsol_event,
                .init_hook = alc262_hippo_automute,
        },      
+       [ALC262_ULTRA] = {
+               .mixers = { alc262_ultra_mixer },
+               .init_verbs = { alc262_init_verbs, alc262_ultra_verbs },
+               .num_dacs = ARRAY_SIZE(alc262_dac_nids),
+               .dac_nids = alc262_dac_nids,
+               .hp_nid = 0x03,
+               .dig_out_nid = ALC262_DIGOUT_NID,
+               .num_channel_mode = ARRAY_SIZE(alc262_modes),
+               .channel_mode = alc262_modes,
+               .input_mux = &alc262_ultra_capture_source,
+               .unsol_event = alc262_ultra_unsol_event,
+               .init_hook = alc262_ultra_automute,
+       },
 };
 
 static int patch_alc262(struct hda_codec *codec)
@@ -8716,6 +9477,8 @@ static int patch_alc262(struct hda_codec *codec)
                }
        }
 
+       spec->vmaster_nid = 0x0c;
+
        codec->patch_ops = alc_patch_ops;
        if (board_config == ALC262_AUTO)
                spec->init_hook = alc262_auto_init;
@@ -8873,6 +9636,49 @@ static void alc268_acer_init_hook(struct hda_codec *codec)
        alc268_acer_automute(codec, 1);
 }
 
+static struct snd_kcontrol_new alc268_dell_mixer[] = {
+       /* output mixer control */
+       HDA_CODEC_VOLUME("Speaker Playback Volume", 0x02, 0x0, HDA_OUTPUT),
+       HDA_CODEC_MUTE("Speaker Playback Switch", 0x14, 0x0, HDA_OUTPUT),
+       HDA_CODEC_VOLUME("Headphone Playback Volume", 0x03, 0x0, HDA_OUTPUT),
+       HDA_CODEC_MUTE("Headphone Playback Switch", 0x15, 0x0, HDA_OUTPUT),
+       HDA_CODEC_VOLUME("Mic Boost", 0x18, 0, HDA_INPUT),
+       HDA_CODEC_VOLUME("Internal Mic Boost", 0x19, 0, HDA_INPUT),
+       { }
+};
+
+static struct hda_verb alc268_dell_verbs[] = {
+       {0x14, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP},
+       {0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP},
+       {0x15, AC_VERB_SET_UNSOLICITED_ENABLE, ALC880_HP_EVENT | AC_USRSP_EN},
+       { }
+};
+
+/* mute/unmute internal speaker according to the hp jack and mute state */
+static void alc268_dell_automute(struct hda_codec *codec)
+{
+       unsigned int present;
+       unsigned int mute;
+
+       present = snd_hda_codec_read(codec, 0x15, 0, AC_VERB_GET_PIN_SENSE, 0);
+       if (present & 0x80000000)
+               mute = HDA_AMP_MUTE;
+       else
+               mute = snd_hda_codec_amp_read(codec, 0x15, 0, HDA_OUTPUT, 0);
+       snd_hda_codec_amp_stereo(codec, 0x14, HDA_OUTPUT, 0,
+                                HDA_AMP_MUTE, mute);
+}
+
+static void alc268_dell_unsol_event(struct hda_codec *codec,
+                                   unsigned int res)
+{
+       if ((res >> 26) != ALC880_HP_EVENT)
+               return;
+       alc268_dell_automute(codec);
+}
+
+#define alc268_dell_init_hook  alc268_dell_automute
+
 /*
  * generic initialization of ADC, input mixers and output mixers
  */
@@ -8915,19 +9721,13 @@ static struct hda_verb alc268_base_init_verbs[] = {
        {0x1c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
        {0x1d, AC_VERB_SET_AMP_GAIN_MUTE, (0x7000 | (0x00 << 8))},
 
-       /* FIXME: use matrix-type input source selection */
-       /* Mixer elements: 0x18, 19, 1a, 1c, 14, 15, 0b */
-       /* Input mixer1: unmute Mic, F-Mic, Line, CD inputs */
-       /* Input mixer2 */
-       {0x23, AC_VERB_SET_AMP_GAIN_MUTE, (0x7000 | (0x00 << 8))},
-       {0x23, AC_VERB_SET_AMP_GAIN_MUTE, (0x7000 | (0x01 << 8))},
-       {0x23, AC_VERB_SET_AMP_GAIN_MUTE, (0x7000 | (0x02 << 8))},
-       {0x23, AC_VERB_SET_AMP_GAIN_MUTE, (0x7000 | (0x03 << 8))},
+       /* Unmute Selector 23h,24h and set the default input to mic-in */
+       
+       {0x23, AC_VERB_SET_CONNECT_SEL, 0x00},
+       {0x23, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
+       {0x24, AC_VERB_SET_CONNECT_SEL, 0x00},
+       {0x24, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
 
-       {0x24, AC_VERB_SET_AMP_GAIN_MUTE, (0x7000 | (0x00 << 8))},
-       {0x24, AC_VERB_SET_AMP_GAIN_MUTE, (0x7000 | (0x01 << 8))},
-       {0x24, AC_VERB_SET_AMP_GAIN_MUTE, (0x7000 | (0x02 << 8))},
-       {0x24, AC_VERB_SET_AMP_GAIN_MUTE, (0x7000 | (0x03 << 8))},
        { }
 };
 
@@ -8972,29 +9772,14 @@ static int alc268_mux_enum_put(struct snd_kcontrol *kcontrol,
 {
        struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
        struct alc_spec *spec = codec->spec;
-       const struct hda_input_mux *imux = spec->input_mux;
+
        unsigned int adc_idx = snd_ctl_get_ioffidx(kcontrol, &ucontrol->id);
        static hda_nid_t capture_mixers[3] = { 0x23, 0x24 };
        hda_nid_t nid = capture_mixers[adc_idx];
-       unsigned int *cur_val = &spec->cur_mux[adc_idx];
-       unsigned int i, idx;
 
-       idx = ucontrol->value.enumerated.item[0];
-       if (idx >= imux->num_items)
-               idx = imux->num_items - 1;
-       if (*cur_val == idx)
-               return 0;
-       for (i = 0; i < imux->num_items; i++) {
-               unsigned int v = (i == idx) ? 0 : HDA_AMP_MUTE;
-               snd_hda_codec_amp_stereo(codec, nid, HDA_INPUT,
-                                        imux->items[i].index,
-                                        HDA_AMP_MUTE, v);
-                snd_hda_codec_write_cache(codec, nid, 0,
-                                         AC_VERB_SET_CONNECT_SEL,
-                                         idx );
-       }
-       *cur_val = idx;
-       return 1;
+       return snd_hda_input_mux_put(codec, spec->input_mux, ucontrol,
+                                    nid,
+                                    &spec->cur_mux[adc_idx]);
 }
 
 static struct snd_kcontrol_new alc268_capture_alt_mixer[] = {
@@ -9004,7 +9789,6 @@ static struct snd_kcontrol_new alc268_capture_alt_mixer[] = {
                .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
                /* The multiple "Capture Source" controls confuse alsamixer
                 * So call somewhat different..
-                * FIXME: the controls appear in the "playback" view!
                 */
                /* .name = "Capture Source", */
                .name = "Input Source",
@@ -9025,7 +9809,6 @@ static struct snd_kcontrol_new alc268_capture_mixer[] = {
                .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
                /* The multiple "Capture Source" controls confuse alsamixer
                 * So call somewhat different..
-                * FIXME: the controls appear in the "playback" view!
                 */
                /* .name = "Capture Source", */
                .name = "Input Source",
@@ -9047,12 +9830,67 @@ static struct hda_input_mux alc268_capture_source = {
        },
 };
 
-/* create input playback/capture controls for the given pin */
-static int alc268_new_analog_output(struct alc_spec *spec, hda_nid_t nid,
-                                   const char *ctlname, int idx)
-{
-       char name[32];
-       int err;
+#ifdef CONFIG_SND_DEBUG
+static struct snd_kcontrol_new alc268_test_mixer[] = {
+       HDA_CODEC_VOLUME("Front Playback Volume", 0x2, 0x0, HDA_OUTPUT),
+       HDA_CODEC_MUTE("Front Playback Switch", 0x14, 0x0, HDA_OUTPUT),
+       HDA_CODEC_VOLUME("Headphone Playback Volume", 0x3, 0x0, HDA_OUTPUT),
+       HDA_CODEC_MUTE("Headphone Playback Switch", 0x15, 0x0, HDA_OUTPUT),
+
+       /* Volume widgets */
+       HDA_CODEC_VOLUME("LOUT1 Playback Volume", 0x02, 0x0, HDA_OUTPUT),
+       HDA_CODEC_VOLUME("LOUT2 Playback Volume", 0x03, 0x0, HDA_OUTPUT),
+       HDA_BIND_MUTE_MONO("Mono sum Playback Switch", 0x0e, 1, 2, HDA_INPUT),
+       HDA_BIND_MUTE("LINE-OUT sum Playback Switch", 0x0f, 2, HDA_INPUT),
+       HDA_BIND_MUTE("HP-OUT sum Playback Switch", 0x10, 2, HDA_INPUT),
+       HDA_BIND_MUTE("LINE-OUT Playback Switch", 0x14, 2, HDA_OUTPUT),
+       HDA_BIND_MUTE("HP-OUT Playback Switch", 0x15, 2, HDA_OUTPUT),
+       HDA_BIND_MUTE("Mono Playback Switch", 0x16, 2, HDA_OUTPUT),
+       HDA_CODEC_VOLUME("MIC1 Capture Volume", 0x18, 0x0, HDA_INPUT),
+       HDA_BIND_MUTE("MIC1 Capture Switch", 0x18, 2, HDA_OUTPUT),
+       HDA_CODEC_VOLUME("MIC2 Capture Volume", 0x19, 0x0, HDA_INPUT),
+       HDA_CODEC_VOLUME("LINE1 Capture Volume", 0x1a, 0x0, HDA_INPUT),
+       HDA_BIND_MUTE("LINE1 Capture Switch", 0x1a, 2, HDA_OUTPUT),
+       /* The below appears problematic on some hardwares */
+       /*HDA_CODEC_VOLUME("PCBEEP Playback Volume", 0x1d, 0x0, HDA_INPUT),*/
+       HDA_CODEC_VOLUME("PCM-IN1 Capture Volume", 0x23, 0x0, HDA_OUTPUT),
+       HDA_BIND_MUTE("PCM-IN1 Capture Switch", 0x23, 2, HDA_OUTPUT),
+       HDA_CODEC_VOLUME("PCM-IN2 Capture Volume", 0x24, 0x0, HDA_OUTPUT),
+       HDA_BIND_MUTE("PCM-IN2 Capture Switch", 0x24, 2, HDA_OUTPUT),
+
+       /* Modes for retasking pin widgets */
+       ALC_PIN_MODE("LINE-OUT pin mode", 0x14, ALC_PIN_DIR_INOUT),
+       ALC_PIN_MODE("HP-OUT pin mode", 0x15, ALC_PIN_DIR_INOUT),
+       ALC_PIN_MODE("MIC1 pin mode", 0x18, ALC_PIN_DIR_INOUT),
+       ALC_PIN_MODE("LINE1 pin mode", 0x1a, ALC_PIN_DIR_INOUT),
+
+       /* Controls for GPIO pins, assuming they are configured as outputs */
+       ALC_GPIO_DATA_SWITCH("GPIO pin 0", 0x01, 0x01),
+       ALC_GPIO_DATA_SWITCH("GPIO pin 1", 0x01, 0x02),
+       ALC_GPIO_DATA_SWITCH("GPIO pin 2", 0x01, 0x04),
+       ALC_GPIO_DATA_SWITCH("GPIO pin 3", 0x01, 0x08),
+
+       /* Switches to allow the digital SPDIF output pin to be enabled.
+        * The ALC268 does not have an SPDIF input.
+        */
+       ALC_SPDIF_CTRL_SWITCH("SPDIF Playback Switch", 0x06, 0x01),
+
+       /* A switch allowing EAPD to be enabled.  Some laptops seem to use
+        * this output to turn on an external amplifier.
+        */
+       ALC_EAPD_CTRL_SWITCH("LINE-OUT EAPD Enable Switch", 0x0f, 0x02),
+       ALC_EAPD_CTRL_SWITCH("HP-OUT EAPD Enable Switch", 0x10, 0x02),
+
+       { } /* end */
+};
+#endif
+
+/* create input playback/capture controls for the given pin */
+static int alc268_new_analog_output(struct alc_spec *spec, hda_nid_t nid,
+                                   const char *ctlname, int idx)
+{
+       char name[32];
+       int err;
 
        sprintf(name, "%s Playback Volume", ctlname);
        if (nid == 0x14) {
@@ -9192,43 +10030,491 @@ static void alc268_auto_init_mono_speaker_out(struct hda_codec *codec)
 }
 
 /* pcm configuration: identiacal with ALC880 */
-#define alc268_pcm_analog_playback     alc880_pcm_analog_playback
-#define alc268_pcm_analog_capture      alc880_pcm_analog_capture
-#define alc268_pcm_digital_playback    alc880_pcm_digital_playback
+#define alc268_pcm_analog_playback     alc880_pcm_analog_playback
+#define alc268_pcm_analog_capture      alc880_pcm_analog_capture
+#define alc268_pcm_analog_alt_capture  alc880_pcm_analog_alt_capture
+#define alc268_pcm_digital_playback    alc880_pcm_digital_playback
+
+/*
+ * BIOS auto configuration
+ */
+static int alc268_parse_auto_config(struct hda_codec *codec)
+{
+       struct alc_spec *spec = codec->spec;
+       int err;
+       static hda_nid_t alc268_ignore[] = { 0 };
+
+       err = snd_hda_parse_pin_def_config(codec, &spec->autocfg,
+                                          alc268_ignore);
+       if (err < 0)
+               return err;
+       if (!spec->autocfg.line_outs)
+               return 0; /* can't find valid BIOS pin config */
+
+       err = alc268_auto_create_multi_out_ctls(spec, &spec->autocfg);
+       if (err < 0)
+               return err;
+       err = alc268_auto_create_analog_input_ctls(spec, &spec->autocfg);
+       if (err < 0)
+               return err;
+
+       spec->multiout.max_channels = 2;
+
+       /* digital only support output */
+       if (spec->autocfg.dig_out_pin)
+               spec->multiout.dig_out_nid = ALC268_DIGOUT_NID;
+
+       if (spec->kctl_alloc)
+               spec->mixers[spec->num_mixers++] = spec->kctl_alloc;
+
+       spec->init_verbs[spec->num_init_verbs++] = alc268_volume_init_verbs;
+       spec->num_mux_defs = 1;
+       spec->input_mux = &spec->private_imux;
+
+       err = alc_auto_add_mic_boost(codec);
+       if (err < 0)
+               return err;
+
+       return 1;
+}
+
+#define alc268_auto_init_multi_out     alc882_auto_init_multi_out
+#define alc268_auto_init_hp_out                alc882_auto_init_hp_out
+#define alc268_auto_init_analog_input  alc882_auto_init_analog_input
+
+/* init callback for auto-configuration model -- overriding the default init */
+static void alc268_auto_init(struct hda_codec *codec)
+{
+       alc268_auto_init_multi_out(codec);
+       alc268_auto_init_hp_out(codec);
+       alc268_auto_init_mono_speaker_out(codec);
+       alc268_auto_init_analog_input(codec);
+}
+
+/*
+ * configuration and preset
+ */
+static const char *alc268_models[ALC268_MODEL_LAST] = {
+       [ALC268_3ST]            = "3stack",
+       [ALC268_TOSHIBA]        = "toshiba",
+       [ALC268_ACER]           = "acer",
+       [ALC268_DELL]           = "dell",
+#ifdef CONFIG_SND_DEBUG
+       [ALC268_TEST]           = "test",
+#endif
+       [ALC268_AUTO]           = "auto",
+};
+
+static struct snd_pci_quirk alc268_cfg_tbl[] = {
+       SND_PCI_QUIRK(0x1025, 0x0126, "Acer", ALC268_ACER),
+       SND_PCI_QUIRK(0x1025, 0x012e, "Acer Aspire 5310", ALC268_ACER),
+       SND_PCI_QUIRK(0x1025, 0x0130, "Acer Extensa 5210", ALC268_ACER),
+       SND_PCI_QUIRK(0x1025, 0x0136, "Acer Aspire 5315", ALC268_ACER),
+       SND_PCI_QUIRK(0x1028, 0x0253, "Dell OEM", ALC268_DELL),
+       SND_PCI_QUIRK(0x103c, 0x30cc, "TOSHIBA", ALC268_TOSHIBA),
+       SND_PCI_QUIRK(0x1043, 0x1205, "ASUS W7J", ALC268_3ST),
+       SND_PCI_QUIRK(0x1179, 0xff10, "TOSHIBA A205", ALC268_TOSHIBA),
+       SND_PCI_QUIRK(0x1179, 0xff50, "TOSHIBA A305", ALC268_TOSHIBA),
+       SND_PCI_QUIRK(0x152d, 0x0763, "Diverse (CPR2000)", ALC268_ACER),
+       {}
+};
+
+static struct alc_config_preset alc268_presets[] = {
+       [ALC268_3ST] = {
+               .mixers = { alc268_base_mixer, alc268_capture_alt_mixer },
+               .init_verbs = { alc268_base_init_verbs },
+               .num_dacs = ARRAY_SIZE(alc268_dac_nids),
+               .dac_nids = alc268_dac_nids,
+                .num_adc_nids = ARRAY_SIZE(alc268_adc_nids_alt),
+                .adc_nids = alc268_adc_nids_alt,
+               .hp_nid = 0x03,
+               .dig_out_nid = ALC268_DIGOUT_NID,
+               .num_channel_mode = ARRAY_SIZE(alc268_modes),
+               .channel_mode = alc268_modes,
+               .input_mux = &alc268_capture_source,
+       },
+       [ALC268_TOSHIBA] = {
+               .mixers = { alc268_base_mixer, alc268_capture_alt_mixer },
+               .init_verbs = { alc268_base_init_verbs, alc268_eapd_verbs,
+                               alc268_toshiba_verbs },
+               .num_dacs = ARRAY_SIZE(alc268_dac_nids),
+               .dac_nids = alc268_dac_nids,
+               .num_adc_nids = ARRAY_SIZE(alc268_adc_nids_alt),
+               .adc_nids = alc268_adc_nids_alt,
+               .hp_nid = 0x03,
+               .num_channel_mode = ARRAY_SIZE(alc268_modes),
+               .channel_mode = alc268_modes,
+               .input_mux = &alc268_capture_source,
+               .unsol_event = alc268_toshiba_unsol_event,
+               .init_hook = alc268_toshiba_automute,
+       },
+       [ALC268_ACER] = {
+               .mixers = { alc268_acer_mixer, alc268_capture_alt_mixer },
+               .init_verbs = { alc268_base_init_verbs, alc268_eapd_verbs,
+                               alc268_acer_verbs },
+               .num_dacs = ARRAY_SIZE(alc268_dac_nids),
+               .dac_nids = alc268_dac_nids,
+               .num_adc_nids = ARRAY_SIZE(alc268_adc_nids_alt),
+               .adc_nids = alc268_adc_nids_alt,
+               .hp_nid = 0x02,
+               .num_channel_mode = ARRAY_SIZE(alc268_modes),
+               .channel_mode = alc268_modes,
+               .input_mux = &alc268_capture_source,
+               .unsol_event = alc268_acer_unsol_event,
+               .init_hook = alc268_acer_init_hook,
+       },
+       [ALC268_DELL] = {
+               .mixers = { alc268_dell_mixer },
+               .init_verbs = { alc268_base_init_verbs, alc268_eapd_verbs,
+                               alc268_dell_verbs },
+               .num_dacs = ARRAY_SIZE(alc268_dac_nids),
+               .dac_nids = alc268_dac_nids,
+               .hp_nid = 0x02,
+               .num_channel_mode = ARRAY_SIZE(alc268_modes),
+               .channel_mode = alc268_modes,
+               .unsol_event = alc268_dell_unsol_event,
+               .init_hook = alc268_dell_init_hook,
+               .input_mux = &alc268_capture_source,
+       },
+#ifdef CONFIG_SND_DEBUG
+       [ALC268_TEST] = {
+               .mixers = { alc268_test_mixer, alc268_capture_mixer },
+               .init_verbs = { alc268_base_init_verbs, alc268_eapd_verbs,
+                               alc268_volume_init_verbs },
+               .num_dacs = ARRAY_SIZE(alc268_dac_nids),
+               .dac_nids = alc268_dac_nids,
+               .num_adc_nids = ARRAY_SIZE(alc268_adc_nids_alt),
+               .adc_nids = alc268_adc_nids_alt,
+               .hp_nid = 0x03,
+               .dig_out_nid = ALC268_DIGOUT_NID,
+               .num_channel_mode = ARRAY_SIZE(alc268_modes),
+               .channel_mode = alc268_modes,
+               .input_mux = &alc268_capture_source,
+       },
+#endif
+};
+
+static int patch_alc268(struct hda_codec *codec)
+{
+       struct alc_spec *spec;
+       int board_config;
+       int err;
+
+       spec = kcalloc(1, sizeof(*spec), GFP_KERNEL);
+       if (spec == NULL)
+               return -ENOMEM;
+
+       codec->spec = spec;
+
+       board_config = snd_hda_check_board_config(codec, ALC268_MODEL_LAST,
+                                                 alc268_models,
+                                                 alc268_cfg_tbl);
+
+       if (board_config < 0 || board_config >= ALC268_MODEL_LAST) {
+               printk(KERN_INFO "hda_codec: Unknown model for ALC268, "
+                      "trying auto-probe from BIOS...\n");
+               board_config = ALC268_AUTO;
+       }
+
+       if (board_config == ALC268_AUTO) {
+               /* automatic parse from the BIOS config */
+               err = alc268_parse_auto_config(codec);
+               if (err < 0) {
+                       alc_free(codec);
+                       return err;
+               } else if (!err) {
+                       printk(KERN_INFO
+                              "hda_codec: Cannot set up configuration "
+                              "from BIOS.  Using base mode...\n");
+                       board_config = ALC268_3ST;
+               }
+       }
+
+       if (board_config != ALC268_AUTO)
+               setup_preset(spec, &alc268_presets[board_config]);
+
+       spec->stream_name_analog = "ALC268 Analog";
+       spec->stream_analog_playback = &alc268_pcm_analog_playback;
+       spec->stream_analog_capture = &alc268_pcm_analog_capture;
+       spec->stream_analog_alt_capture = &alc268_pcm_analog_alt_capture;
+
+       spec->stream_name_digital = "ALC268 Digital";
+       spec->stream_digital_playback = &alc268_pcm_digital_playback;
+
+       if (!spec->adc_nids && spec->input_mux) {
+               /* check whether NID 0x07 is valid */
+               unsigned int wcap = get_wcaps(codec, 0x07);
+
+               /* get type */
+               wcap = (wcap & AC_WCAP_TYPE) >> AC_WCAP_TYPE_SHIFT;
+               if (wcap != AC_WID_AUD_IN) {
+                       spec->adc_nids = alc268_adc_nids_alt;
+                       spec->num_adc_nids = ARRAY_SIZE(alc268_adc_nids_alt);
+                       spec->mixers[spec->num_mixers] =
+                                       alc268_capture_alt_mixer;
+                       spec->num_mixers++;
+               } else {
+                       spec->adc_nids = alc268_adc_nids;
+                       spec->num_adc_nids = ARRAY_SIZE(alc268_adc_nids);
+                       spec->mixers[spec->num_mixers] =
+                               alc268_capture_mixer;
+                       spec->num_mixers++;
+               }
+       }
+
+       spec->vmaster_nid = 0x02;
+
+       codec->patch_ops = alc_patch_ops;
+       if (board_config == ALC268_AUTO)
+               spec->init_hook = alc268_auto_init;
+               
+       return 0;
+}
+
+/*
+ *  ALC269 channel source setting (2 channel)
+ */
+#define ALC269_DIGOUT_NID      ALC880_DIGOUT_NID
+
+#define alc269_dac_nids                alc260_dac_nids
+
+static hda_nid_t alc269_adc_nids[1] = {
+       /* ADC1 */
+       0x07,
+};
+
+#define alc269_modes           alc260_modes
+#define alc269_capture_source  alc880_lg_lw_capture_source
+
+static struct snd_kcontrol_new alc269_base_mixer[] = {
+       HDA_CODEC_VOLUME("Front Playback Volume", 0x02, 0x0, HDA_OUTPUT),
+       HDA_CODEC_MUTE("Front Playback Switch", 0x14, 0x0, HDA_OUTPUT),
+       HDA_CODEC_VOLUME("Line Playback Volume", 0x0b, 0x02, HDA_INPUT),
+       HDA_CODEC_MUTE("Line Playback Switch", 0x0b, 0x02, HDA_INPUT),
+       HDA_CODEC_VOLUME("Mic Playback Volume", 0x0b, 0x0, HDA_INPUT),
+       HDA_CODEC_MUTE("Mic Playback Switch", 0x0b, 0x0, HDA_INPUT),
+       HDA_CODEC_VOLUME("Mic Boost", 0x18, 0, HDA_INPUT),
+       HDA_CODEC_VOLUME("Front Mic Playback Volume", 0x0b, 0x01, HDA_INPUT),
+       HDA_CODEC_MUTE("Front Mic Playback Switch", 0x0b, 0x01, HDA_INPUT),
+       HDA_CODEC_VOLUME("Front Mic Boost", 0x19, 0, HDA_INPUT),
+       HDA_CODEC_MUTE("Headphone Playback Switch", 0x15, 0x0, HDA_OUTPUT),
+       HDA_CODEC_MUTE_MONO("Mono Playback Switch", 0x16, 2, 0x0, HDA_OUTPUT),
+       { } /* end */
+};
+
+/* capture mixer elements */
+static struct snd_kcontrol_new alc269_capture_mixer[] = {
+       HDA_CODEC_VOLUME("Capture Volume", 0x07, 0x0, HDA_INPUT),
+       HDA_CODEC_MUTE("Capture Switch", 0x07, 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 = 1,
+               .info = alc_mux_enum_info,
+               .get = alc_mux_enum_get,
+               .put = alc_mux_enum_put,
+       },
+       { } /* end */
+};
+
+/*
+ * generic initialization of ADC, input mixers and output mixers
+ */
+static struct hda_verb alc269_init_verbs[] = {
+       /*
+        * Unmute ADC0 and set the default input to mic-in
+        */
+       {0x07, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
+
+       /* Mute input amps (PCBeep, Line In, Mic 1 & Mic 2) of the
+        * analog-loopback mixer widget
+        * Note: PASD motherboards uses the Line In 2 as the input for
+        * front panel mic (mic 2)
+        */
+       /* Amp Indices: Mic1 = 0, Mic2 = 1, Line1 = 2, Line2 = 3, CD = 4 */
+       {0x0b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
+       {0x0b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
+       {0x0b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(2)},
+       {0x0b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(3)},
+       {0x0b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(4)},
+
+       /*
+        * Set up output mixers (0x0c - 0x0e)
+        */
+       /* set vol=0 to output mixers */
+       {0x02, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
+       {0x03, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
+
+       /* set up input amps for analog loopback */
+       /* Amp Indices: DAC = 0, mixer = 1 */
+       {0x0c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
+       {0x0c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
+       {0x0d, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
+       {0x0d, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
+       {0x0e, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
+       {0x0e, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
+
+       {0x14, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
+       {0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP},
+       {0x16, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
+       {0x18, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80},
+       {0x19, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80},
+       {0x1a, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN},
+       {0x1b, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN},
+
+       {0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
+       {0x15, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
+       {0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
+       {0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
+       {0x19, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
+       {0x1a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
+       {0x1b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
+
+       {0x14, AC_VERB_SET_CONNECT_SEL, 0x00},
+       {0x15, AC_VERB_SET_CONNECT_SEL, 0x00},
+
+       /* FIXME: use matrix-type input source selection */
+       /* Mixer elements: 0x18, 19, 1a, 1b, 1d, 0b */
+       /* Input mixer1: unmute Mic, F-Mic, Line, CD inputs */
+       {0x24, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
+       {0x24, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
+       {0x24, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(2)},
+       {0x24, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(3)},
+
+       /* set EAPD */
+       {0x14, AC_VERB_SET_EAPD_BTLENABLE, 2},
+       {0x15, AC_VERB_SET_EAPD_BTLENABLE, 2},
+       { }
+};
+
+/* add playback controls from the parsed DAC table */
+static int alc269_auto_create_multi_out_ctls(struct alc_spec *spec,
+                                            const struct auto_pin_cfg *cfg)
+{
+       hda_nid_t nid;
+       int err;
+
+       spec->multiout.num_dacs = 1;    /* only use one dac */
+       spec->multiout.dac_nids = spec->private_dac_nids;
+       spec->multiout.dac_nids[0] = 2;
+
+       nid = cfg->line_out_pins[0];
+       if (nid) {
+               err = add_control(spec, ALC_CTL_WIDGET_VOL,
+                                 "Front Playback Volume",
+                                 HDA_COMPOSE_AMP_VAL(0x02, 3, 0, HDA_OUTPUT));
+               if (err < 0)
+                       return err;
+               err = add_control(spec, ALC_CTL_WIDGET_MUTE,
+                                 "Front Playback Switch",
+                                 HDA_COMPOSE_AMP_VAL(nid, 3, 0, HDA_OUTPUT));
+               if (err < 0)
+                       return err;
+       }
+
+       nid = cfg->speaker_pins[0];
+       if (nid) {
+               if (!cfg->line_out_pins[0]) {
+                       err = add_control(spec, ALC_CTL_WIDGET_VOL,
+                                         "Speaker Playback Volume",
+                                         HDA_COMPOSE_AMP_VAL(0x02, 3, 0,
+                                                             HDA_OUTPUT));
+                       if (err < 0)
+                               return err;
+               }
+               if (nid == 0x16) {
+                       err = add_control(spec, ALC_CTL_WIDGET_MUTE,
+                                         "Speaker Playback Switch",
+                                         HDA_COMPOSE_AMP_VAL(nid, 2, 0,
+                                                             HDA_OUTPUT));
+                       if (err < 0)
+                               return err;
+               } else {
+                       err = add_control(spec, ALC_CTL_WIDGET_MUTE,
+                                         "Speaker Playback Switch",
+                                         HDA_COMPOSE_AMP_VAL(nid, 3, 0,
+                                                             HDA_OUTPUT));
+                       if (err < 0)
+                               return err;
+               }
+       }
+       nid = cfg->hp_pins[0];
+       if (nid) {
+               /* spec->multiout.hp_nid = 2; */
+               if (!cfg->line_out_pins[0] && !cfg->speaker_pins[0]) {
+                       err = add_control(spec, ALC_CTL_WIDGET_VOL,
+                                         "Headphone Playback Volume",
+                                         HDA_COMPOSE_AMP_VAL(0x02, 3, 0,
+                                                             HDA_OUTPUT));
+                       if (err < 0)
+                               return err;
+               }
+               if (nid == 0x16) {
+                       err = add_control(spec, ALC_CTL_WIDGET_MUTE,
+                                         "Headphone Playback Switch",
+                                         HDA_COMPOSE_AMP_VAL(nid, 2, 0,
+                                                             HDA_OUTPUT));
+                       if (err < 0)
+                               return err;
+               } else {
+                       err = add_control(spec, ALC_CTL_WIDGET_MUTE,
+                                         "Headphone Playback Switch",
+                                         HDA_COMPOSE_AMP_VAL(nid, 3, 0,
+                                                             HDA_OUTPUT));
+                       if (err < 0)
+                               return err;
+               }
+       }
+       return 0;
+}
+
+#define alc269_auto_create_analog_input_ctls \
+       alc880_auto_create_analog_input_ctls
+
+#ifdef CONFIG_SND_HDA_POWER_SAVE
+#define alc269_loopbacks       alc880_loopbacks
+#endif
+
+/* pcm configuration: identiacal with ALC880 */
+#define alc269_pcm_analog_playback     alc880_pcm_analog_playback
+#define alc269_pcm_analog_capture      alc880_pcm_analog_capture
+#define alc269_pcm_digital_playback    alc880_pcm_digital_playback
+#define alc269_pcm_digital_capture     alc880_pcm_digital_capture
 
 /*
  * BIOS auto configuration
  */
-static int alc268_parse_auto_config(struct hda_codec *codec)
+static int alc269_parse_auto_config(struct hda_codec *codec)
 {
        struct alc_spec *spec = codec->spec;
        int err;
-       static hda_nid_t alc268_ignore[] = { 0 };
+       static hda_nid_t alc269_ignore[] = { 0x1d, 0 };
 
        err = snd_hda_parse_pin_def_config(codec, &spec->autocfg,
-                                          alc268_ignore);
+                                          alc269_ignore);
        if (err < 0)
                return err;
-       if (!spec->autocfg.line_outs)
-               return 0; /* can't find valid BIOS pin config */
 
-       err = alc268_auto_create_multi_out_ctls(spec, &spec->autocfg);
+       err = alc269_auto_create_multi_out_ctls(spec, &spec->autocfg);
        if (err < 0)
                return err;
-       err = alc268_auto_create_analog_input_ctls(spec, &spec->autocfg);
+       err = alc269_auto_create_analog_input_ctls(spec, &spec->autocfg);
        if (err < 0)
                return err;
 
-       spec->multiout.max_channels = 2;
+       spec->multiout.max_channels = spec->multiout.num_dacs * 2;
 
-       /* digital only support output */
        if (spec->autocfg.dig_out_pin)
-               spec->multiout.dig_out_nid = ALC268_DIGOUT_NID;
+               spec->multiout.dig_out_nid = ALC269_DIGOUT_NID;
 
        if (spec->kctl_alloc)
                spec->mixers[spec->num_mixers++] = spec->kctl_alloc;
 
-       spec->init_verbs[spec->num_init_verbs++] = alc268_volume_init_verbs;
+       spec->init_verbs[spec->num_init_verbs++] = alc269_init_verbs;
        spec->num_mux_defs = 1;
        spec->input_mux = &spec->private_imux;
 
@@ -9239,111 +10525,68 @@ static int alc268_parse_auto_config(struct hda_codec *codec)
        return 1;
 }
 
-#define alc268_auto_init_multi_out     alc882_auto_init_multi_out
-#define alc268_auto_init_hp_out                alc882_auto_init_hp_out
-#define alc268_auto_init_analog_input  alc882_auto_init_analog_input
+#define alc269_auto_init_multi_out     alc882_auto_init_multi_out
+#define alc269_auto_init_hp_out                alc882_auto_init_hp_out
+#define alc269_auto_init_analog_input  alc882_auto_init_analog_input
+
 
 /* init callback for auto-configuration model -- overriding the default init */
-static void alc268_auto_init(struct hda_codec *codec)
+static void alc269_auto_init(struct hda_codec *codec)
 {
-       alc268_auto_init_multi_out(codec);
-       alc268_auto_init_hp_out(codec);
-       alc268_auto_init_mono_speaker_out(codec);
-       alc268_auto_init_analog_input(codec);
+       alc269_auto_init_multi_out(codec);
+       alc269_auto_init_hp_out(codec);
+       alc269_auto_init_analog_input(codec);
 }
 
 /*
  * configuration and preset
  */
-static const char *alc268_models[ALC268_MODEL_LAST] = {
-       [ALC268_3ST]            = "3stack",
-       [ALC268_TOSHIBA]        = "toshiba",
-       [ALC268_ACER]           = "acer",
-       [ALC268_AUTO]           = "auto",
+static const char *alc269_models[ALC269_MODEL_LAST] = {
+       [ALC269_BASIC]          = "basic",
 };
 
-static struct snd_pci_quirk alc268_cfg_tbl[] = {
-       SND_PCI_QUIRK(0x1043, 0x1205, "ASUS W7J", ALC268_3ST),
-       SND_PCI_QUIRK(0x1179, 0xff10, "TOSHIBA A205", ALC268_TOSHIBA),
-       SND_PCI_QUIRK(0x1179, 0xff50, "TOSHIBA A305", ALC268_TOSHIBA),
-       SND_PCI_QUIRK(0x103c, 0x30cc, "TOSHIBA", ALC268_TOSHIBA),
-       SND_PCI_QUIRK(0x1025, 0x0126, "Acer", ALC268_ACER),
-       SND_PCI_QUIRK(0x1025, 0x0130, "Acer Extensa 5210", ALC268_ACER),
-       SND_PCI_QUIRK(0x152d, 0x0763, "Diverse (CPR2000)", ALC268_ACER),
+static struct snd_pci_quirk alc269_cfg_tbl[] = {
        {}
 };
 
-static struct alc_config_preset alc268_presets[] = {
-       [ALC268_3ST] = {
-               .mixers = { alc268_base_mixer, alc268_capture_alt_mixer },
-               .init_verbs = { alc268_base_init_verbs },
-               .num_dacs = ARRAY_SIZE(alc268_dac_nids),
-               .dac_nids = alc268_dac_nids,
-                .num_adc_nids = ARRAY_SIZE(alc268_adc_nids_alt),
-                .adc_nids = alc268_adc_nids_alt,
-               .hp_nid = 0x03,
-               .dig_out_nid = ALC268_DIGOUT_NID,
-               .num_channel_mode = ARRAY_SIZE(alc268_modes),
-               .channel_mode = alc268_modes,
-               .input_mux = &alc268_capture_source,
-       },
-       [ALC268_TOSHIBA] = {
-               .mixers = { alc268_base_mixer, alc268_capture_alt_mixer },
-               .init_verbs = { alc268_base_init_verbs, alc268_eapd_verbs,
-                               alc268_toshiba_verbs },
-               .num_dacs = ARRAY_SIZE(alc268_dac_nids),
-               .dac_nids = alc268_dac_nids,
-               .num_adc_nids = ARRAY_SIZE(alc268_adc_nids_alt),
-               .adc_nids = alc268_adc_nids_alt,
+static struct alc_config_preset alc269_presets[] = {
+       [ALC269_BASIC] = {
+               .mixers = { alc269_base_mixer },
+               .init_verbs = { alc269_init_verbs },
+               .num_dacs = ARRAY_SIZE(alc269_dac_nids),
+               .dac_nids = alc269_dac_nids,
                .hp_nid = 0x03,
-               .num_channel_mode = ARRAY_SIZE(alc268_modes),
-               .channel_mode = alc268_modes,
-               .input_mux = &alc268_capture_source,
-               .unsol_event = alc268_toshiba_unsol_event,
-               .init_hook = alc268_toshiba_automute,
-       },
-       [ALC268_ACER] = {
-               .mixers = { alc268_acer_mixer, alc268_capture_alt_mixer },
-               .init_verbs = { alc268_base_init_verbs, alc268_eapd_verbs,
-                               alc268_acer_verbs },
-               .num_dacs = ARRAY_SIZE(alc268_dac_nids),
-               .dac_nids = alc268_dac_nids,
-               .num_adc_nids = ARRAY_SIZE(alc268_adc_nids_alt),
-               .adc_nids = alc268_adc_nids_alt,
-               .hp_nid = 0x02,
-               .num_channel_mode = ARRAY_SIZE(alc268_modes),
-               .channel_mode = alc268_modes,
-               .input_mux = &alc268_capture_source,
-               .unsol_event = alc268_acer_unsol_event,
-               .init_hook = alc268_acer_init_hook,
+               .num_channel_mode = ARRAY_SIZE(alc269_modes),
+               .channel_mode = alc269_modes,
+               .input_mux = &alc269_capture_source,
        },
 };
 
-static int patch_alc268(struct hda_codec *codec)
+static int patch_alc269(struct hda_codec *codec)
 {
        struct alc_spec *spec;
        int board_config;
        int err;
 
-       spec = kcalloc(1, sizeof(*spec), GFP_KERNEL);
+       spec = kzalloc(sizeof(*spec), GFP_KERNEL);
        if (spec == NULL)
                return -ENOMEM;
 
        codec->spec = spec;
 
-       board_config = snd_hda_check_board_config(codec, ALC268_MODEL_LAST,
-                                                 alc268_models,
-                                                 alc268_cfg_tbl);
+       board_config = snd_hda_check_board_config(codec, ALC269_MODEL_LAST,
+                                                 alc269_models,
+                                                 alc269_cfg_tbl);
 
-       if (board_config < 0 || board_config >= ALC268_MODEL_LAST) {
-               printk(KERN_INFO "hda_codec: Unknown model for ALC268, "
+       if (board_config < 0) {
+               printk(KERN_INFO "hda_codec: Unknown model for ALC269, "
                       "trying auto-probe from BIOS...\n");
-               board_config = ALC268_AUTO;
+               board_config = ALC269_AUTO;
        }
 
-       if (board_config == ALC268_AUTO) {
+       if (board_config == ALC269_AUTO) {
                /* automatic parse from the BIOS config */
-               err = alc268_parse_auto_config(codec);
+               err = alc269_parse_auto_config(codec);
                if (err < 0) {
                        alc_free(codec);
                        return err;
@@ -9351,48 +10594,34 @@ static int patch_alc268(struct hda_codec *codec)
                        printk(KERN_INFO
                               "hda_codec: Cannot set up configuration "
                               "from BIOS.  Using base mode...\n");
-                       board_config = ALC268_3ST;
+                       board_config = ALC269_BASIC;
                }
        }
 
-       if (board_config != ALC268_AUTO)
-               setup_preset(spec, &alc268_presets[board_config]);
+       if (board_config != ALC269_AUTO)
+               setup_preset(spec, &alc269_presets[board_config]);
 
-       spec->stream_name_analog = "ALC268 Analog";
-       spec->stream_analog_playback = &alc268_pcm_analog_playback;
-       spec->stream_analog_capture = &alc268_pcm_analog_capture;
+       spec->stream_name_analog = "ALC269 Analog";
+       spec->stream_analog_playback = &alc269_pcm_analog_playback;
+       spec->stream_analog_capture = &alc269_pcm_analog_capture;
 
-       spec->stream_name_digital = "ALC268 Digital";
-       spec->stream_digital_playback = &alc268_pcm_digital_playback;
+       spec->stream_name_digital = "ALC269 Digital";
+       spec->stream_digital_playback = &alc269_pcm_digital_playback;
+       spec->stream_digital_capture = &alc269_pcm_digital_capture;
+
+       spec->adc_nids = alc269_adc_nids;
+       spec->num_adc_nids = ARRAY_SIZE(alc269_adc_nids);
+       spec->mixers[spec->num_mixers] = alc269_capture_mixer;
+       spec->num_mixers++;
 
-       if (board_config == ALC268_AUTO) {
-               if (!spec->adc_nids && spec->input_mux) {
-                       /* check whether NID 0x07 is valid */
-                       unsigned int wcap = get_wcaps(codec, 0x07);
-
-                       /* get type */
-                       wcap = (wcap & AC_WCAP_TYPE) >> AC_WCAP_TYPE_SHIFT;
-                       if (wcap != AC_WID_AUD_IN) {
-                               spec->adc_nids = alc268_adc_nids_alt;
-                               spec->num_adc_nids =
-                                       ARRAY_SIZE(alc268_adc_nids_alt);
-                               spec->mixers[spec->num_mixers] =
-                                       alc268_capture_alt_mixer;
-                               spec->num_mixers++;
-                       } else {
-                               spec->adc_nids = alc268_adc_nids;
-                               spec->num_adc_nids =
-                                       ARRAY_SIZE(alc268_adc_nids);
-                               spec->mixers[spec->num_mixers] =
-                                       alc268_capture_mixer;
-                               spec->num_mixers++;
-                       }
-               }
-       }
        codec->patch_ops = alc_patch_ops;
-       if (board_config == ALC268_AUTO)
-               spec->init_hook = alc268_auto_init;
-               
+       if (board_config == ALC269_AUTO)
+               spec->init_hook = alc269_auto_init;
+#ifdef CONFIG_SND_HDA_POWER_SAVE
+       if (!spec->loopback.amplist)
+               spec->loopback.amplist = alc269_loopbacks;
+#endif
+
        return 0;
 }
 
@@ -10213,7 +11442,6 @@ static struct snd_kcontrol_new alc861_capture_mixer[] = {
                .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
                /* The multiple "Capture Source" controls confuse alsamixer
                 * So call somewhat different..
-                *FIXME: the controls appear in the "playback" view!
                 */
                /* .name = "Capture Source", */
                .name = "Input Source",
@@ -10369,22 +11597,23 @@ static struct snd_pci_quirk alc861_cfg_tbl[] = {
        SND_PCI_QUIRK(0x1043, 0x1205, "ASUS W7J", ALC861_3ST),
        SND_PCI_QUIRK(0x1043, 0x1335, "ASUS F2/3", ALC861_ASUS_LAPTOP),
        SND_PCI_QUIRK(0x1043, 0x1338, "ASUS F2/3", ALC861_ASUS_LAPTOP),
-       SND_PCI_QUIRK(0x1043, 0x13d7, "ASUS A9rp", ALC861_ASUS_LAPTOP),
-       SND_PCI_QUIRK(0x1584, 0x9075, "Airis Praxis N1212", ALC861_ASUS_LAPTOP),
        SND_PCI_QUIRK(0x1043, 0x1393, "ASUS", ALC861_ASUS),
+       SND_PCI_QUIRK(0x1043, 0x13d7, "ASUS A9rp", ALC861_ASUS_LAPTOP),
        SND_PCI_QUIRK(0x1043, 0x81cb, "ASUS P1-AH2", ALC861_3ST_DIG),
        SND_PCI_QUIRK(0x1179, 0xff00, "Toshiba", ALC861_TOSHIBA),
        /* FIXME: the entry below breaks Toshiba A100 (model=auto works!)
         *        Any other models that need this preset?
         */
        /* SND_PCI_QUIRK(0x1179, 0xff10, "Toshiba", ALC861_TOSHIBA), */
-       SND_PCI_QUIRK(0x1584, 0x9072, "Uniwill m31", ALC861_UNIWILL_M31),
-       SND_PCI_QUIRK(0x1584, 0x9075, "Uniwill", ALC861_UNIWILL_M31),
+       SND_PCI_QUIRK(0x1462, 0x7254, "HP dx2200 (MSI MS-7254)", ALC861_3ST),
+       SND_PCI_QUIRK(0x1462, 0x7297, "HP dx2250 (MSI MS-7297)", ALC861_3ST),
        SND_PCI_QUIRK(0x1584, 0x2b01, "Uniwill X40AIx", ALC861_UNIWILL_M31),
+       SND_PCI_QUIRK(0x1584, 0x9072, "Uniwill m31", ALC861_UNIWILL_M31),
+       SND_PCI_QUIRK(0x1584, 0x9075, "Airis Praxis N1212", ALC861_ASUS_LAPTOP),
+       /* FIXME: the below seems conflict */
+       /* SND_PCI_QUIRK(0x1584, 0x9075, "Uniwill", ALC861_UNIWILL_M31), */
        SND_PCI_QUIRK(0x1849, 0x0660, "Asrock 939SLI32", ALC660_3ST),
        SND_PCI_QUIRK(0x8086, 0xd600, "Intel", ALC861_3ST),
-       SND_PCI_QUIRK(0x1462, 0x7254, "HP dx2200 (MSI MS-7254)", ALC861_3ST),
-       SND_PCI_QUIRK(0x1462, 0x7297, "HP dx2250 (MSI MS-7297)", ALC861_3ST),
        {}
 };
 
@@ -10543,6 +11772,8 @@ static int patch_alc861(struct hda_codec *codec)
        spec->stream_digital_playback = &alc861_pcm_digital_playback;
        spec->stream_digital_capture = &alc861_pcm_digital_capture;
 
+       spec->vmaster_nid = 0x03;
+
        codec->patch_ops = alc_patch_ops;
        if (board_config == ALC861_AUTO)
                spec->init_hook = alc861_auto_init;
@@ -10697,7 +11928,6 @@ static struct snd_kcontrol_new alc861vd_capture_mixer[] = {
                .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
                /* The multiple "Capture Source" controls confuse alsamixer
                 * So call somewhat different..
-                *FIXME: the controls appear in the "playback" view!
                 */
                /* .name = "Capture Source", */
                .name = "Input Source",
@@ -11102,21 +12332,20 @@ static const char *alc861vd_models[ALC861VD_MODEL_LAST] = {
 };
 
 static struct snd_pci_quirk alc861vd_cfg_tbl[] = {
+       SND_PCI_QUIRK(0x1019, 0xa88d, "Realtek ALC660 demo", ALC660VD_3ST),
+       SND_PCI_QUIRK(0x103c, 0x30bf, "HP TX1000", ALC861VD_HP),
        SND_PCI_QUIRK(0x1043, 0x12e2, "Asus z35m", ALC660VD_3ST),
        SND_PCI_QUIRK(0x1043, 0x1339, "Asus G1", ALC660VD_3ST),
        SND_PCI_QUIRK(0x1043, 0x81e7, "ASUS", ALC660VD_3ST_DIG),
        SND_PCI_QUIRK(0x10de, 0x03f0, "Realtek ALC660 demo", ALC660VD_3ST),
-       SND_PCI_QUIRK(0x1019, 0xa88d, "Realtek ALC660 demo", ALC660VD_3ST),
-
+       SND_PCI_QUIRK(0x1179, 0xff00, "Toshiba A135", ALC861VD_LENOVO),
        /*SND_PCI_QUIRK(0x1179, 0xff00, "DALLAS", ALC861VD_DALLAS),*/ /*lenovo*/
        SND_PCI_QUIRK(0x1179, 0xff01, "DALLAS", ALC861VD_DALLAS),
-       SND_PCI_QUIRK(0x17aa, 0x3802, "Lenovo 3000 C200", ALC861VD_LENOVO),
-       SND_PCI_QUIRK(0x17aa, 0x2066, "Lenovo", ALC861VD_LENOVO),
-       SND_PCI_QUIRK(0x1179, 0xff00, "Toshiba A135", ALC861VD_LENOVO),
        SND_PCI_QUIRK(0x1179, 0xff03, "Toshiba P205", ALC861VD_LENOVO),
        SND_PCI_QUIRK(0x1565, 0x820d, "Biostar NF61S SE", ALC861VD_6ST_DIG),
+       SND_PCI_QUIRK(0x17aa, 0x2066, "Lenovo", ALC861VD_LENOVO),
+       SND_PCI_QUIRK(0x17aa, 0x3802, "Lenovo 3000 C200", ALC861VD_LENOVO),
        SND_PCI_QUIRK(0x1849, 0x0862, "ASRock K8NF6G-VSTA", ALC861VD_6ST_DIG),
-       SND_PCI_QUIRK(0x103c, 0x30bf, "HP TX1000", ALC861VD_HP),
        {}
 };
 
@@ -11520,6 +12749,8 @@ static int patch_alc861vd(struct hda_codec *codec)
        spec->mixers[spec->num_mixers] = alc861vd_capture_mixer;
        spec->num_mixers++;
 
+       spec->vmaster_nid = 0x02;
+
        codec->patch_ops = alc_patch_ops;
 
        if (board_config == ALC861VD_AUTO)
@@ -11699,18 +12930,6 @@ static struct snd_kcontrol_new alc662_base_mixer[] = {
        HDA_CODEC_MUTE("Mic Playback Switch", 0xb, 0x0, HDA_INPUT),
        HDA_CODEC_VOLUME("Front Mic Playback Volume", 0xb, 0x01, HDA_INPUT),
        HDA_CODEC_MUTE("Front Mic Playback Switch", 0xb, 0x01, HDA_INPUT),
-
-       /* Capture mixer control */
-       HDA_CODEC_VOLUME("Capture Volume", 0x09, 0x0, HDA_INPUT),
-       HDA_CODEC_MUTE("Capture Switch", 0x09, 0x0, HDA_INPUT),
-       {
-               .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
-               .name = "Capture Source",
-               .count = 1,
-               .info = alc_mux_enum_info,
-               .get = alc_mux_enum_get,
-               .put = alc_mux_enum_put,
-       },
        { } /* end */
 };
 
@@ -11728,17 +12947,6 @@ static struct snd_kcontrol_new alc662_3ST_2ch_mixer[] = {
        HDA_CODEC_MUTE("Front Mic Playback Switch", 0x0b, 0x1, HDA_INPUT),
        HDA_CODEC_VOLUME("PC Speaker Playback Volume", 0x0b, 0x05, HDA_INPUT),
        HDA_CODEC_MUTE("PC Speaker Playback Switch", 0x0b, 0x05, HDA_INPUT),
-       HDA_CODEC_VOLUME("Capture Volume", 0x09, 0x0, HDA_INPUT),
-       HDA_CODEC_MUTE("Capture Switch", 0x09, 0x0, HDA_INPUT),
-       {
-               .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
-               /* .name = "Capture Source", */
-               .name = "Input Source",
-               .count = 1,
-               .info = alc662_mux_enum_info,
-               .get = alc662_mux_enum_get,
-               .put = alc662_mux_enum_put,
-       },
        { } /* end */
 };
 
@@ -11762,46 +12970,24 @@ static struct snd_kcontrol_new alc662_3ST_6ch_mixer[] = {
        HDA_CODEC_MUTE("Front Mic Playback Switch", 0x0b, 0x1, HDA_INPUT),
        HDA_CODEC_VOLUME("PC Speaker Playback Volume", 0x0b, 0x05, HDA_INPUT),
        HDA_CODEC_MUTE("PC Speaker Playback Switch", 0x0b, 0x05, HDA_INPUT),
-       HDA_CODEC_VOLUME("Capture Volume", 0x09, 0x0, HDA_INPUT),
-       HDA_CODEC_MUTE("Capture Switch", 0x09, 0x0, HDA_INPUT),
-       {
-               .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
-               /* .name = "Capture Source", */
-               .name = "Input Source",
-               .count = 1,
-               .info = alc662_mux_enum_info,
-               .get = alc662_mux_enum_get,
-               .put = alc662_mux_enum_put,
-       },
        { } /* end */
 };
 
 static struct snd_kcontrol_new alc662_lenovo_101e_mixer[] = {
        HDA_CODEC_VOLUME("Front Playback Volume", 0x02, 0x0, HDA_OUTPUT),
        HDA_BIND_MUTE("Front Playback Switch", 0x02, 2, HDA_INPUT),
-       HDA_CODEC_VOLUME("iSpeaker Playback Volume", 0x03, 0x0, HDA_OUTPUT),
-       HDA_BIND_MUTE("iSpeaker Playback Switch", 0x03, 2, HDA_INPUT),
+       HDA_CODEC_VOLUME("Speaker Playback Volume", 0x03, 0x0, HDA_OUTPUT),
+       HDA_BIND_MUTE("Speaker Playback Switch", 0x03, 2, HDA_INPUT),
        HDA_CODEC_MUTE("Headphone Playback Switch", 0x1b, 0x0, HDA_OUTPUT),
        HDA_CODEC_VOLUME("Line Playback Volume", 0x0b, 0x02, HDA_INPUT),
        HDA_CODEC_MUTE("Line Playback Switch", 0x0b, 0x02, HDA_INPUT),
        HDA_CODEC_VOLUME("Mic Playback Volume", 0x0b, 0x1, HDA_INPUT),
        HDA_CODEC_MUTE("Mic Playback Switch", 0x0b, 0x1, HDA_INPUT),
-       HDA_CODEC_VOLUME("Capture Volume", 0x09, 0x0, HDA_INPUT),
-       HDA_CODEC_MUTE("Capture Switch", 0x09, 0x0, HDA_INPUT),
-       {
-               .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
-               /* .name = "Capture Source", */
-               .name = "Input Source",
-               .count = 1,
-               .info = alc662_mux_enum_info,
-               .get = alc662_mux_enum_get,
-               .put = alc662_mux_enum_put,
-       },
        { } /* end */
 };
 
 static struct snd_kcontrol_new alc662_eeepc_p701_mixer[] = {
-       HDA_CODEC_MUTE("iSpeaker Playback Switch", 0x14, 0x0, HDA_OUTPUT),
+       HDA_CODEC_MUTE("Speaker Playback Switch", 0x14, 0x0, HDA_OUTPUT),
 
        HDA_CODEC_VOLUME("LineOut Playback Volume", 0x02, 0x0, HDA_OUTPUT),
        HDA_CODEC_MUTE("LineOut Playback Switch", 0x1b, 0x0, HDA_OUTPUT),
@@ -11816,6 +13002,24 @@ static struct snd_kcontrol_new alc662_eeepc_p701_mixer[] = {
        { } /* end */
 };
 
+static struct snd_kcontrol_new alc662_eeepc_ep20_mixer[] = {
+       HDA_CODEC_VOLUME("LineOut Playback Volume", 0x02, 0x0, HDA_OUTPUT),
+       HDA_CODEC_MUTE("LineOut Playback Switch", 0x14, 0x0, HDA_OUTPUT),
+       HDA_CODEC_VOLUME("Surround Playback Volume", 0x03, 0x0, HDA_OUTPUT),
+       HDA_BIND_MUTE("Surround Playback Switch", 0x03, 2, HDA_INPUT),
+       HDA_CODEC_VOLUME_MONO("Center Playback Volume", 0x04, 1, 0x0, HDA_OUTPUT),
+       HDA_CODEC_VOLUME_MONO("LFE Playback Volume", 0x04, 2, 0x0, HDA_OUTPUT),
+       HDA_BIND_MUTE_MONO("Center Playback Switch", 0x04, 1, 2, HDA_INPUT),
+       HDA_BIND_MUTE_MONO("LFE Playback Switch", 0x04, 2, 2, HDA_INPUT),
+       HDA_CODEC_MUTE("Speaker Playback Switch", 0x1b, 0x0, HDA_OUTPUT),
+       HDA_BIND_MUTE("MuteCtrl Playback Switch", 0x0c, 2, HDA_INPUT),
+       HDA_CODEC_VOLUME("Line Playback Volume", 0x0b, 0x02, HDA_INPUT),
+       HDA_CODEC_MUTE("Line Playback Switch", 0x0b, 0x02, HDA_INPUT),
+       HDA_CODEC_VOLUME("Mic Playback Volume", 0x0b, 0x0, HDA_INPUT),
+       HDA_CODEC_MUTE("Mic Playback Switch", 0x0b, 0x0, HDA_INPUT),
+       { } /* end */
+};
+
 static struct snd_kcontrol_new alc662_chmode_mixer[] = {
        {
                .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
@@ -11901,6 +13105,13 @@ static struct hda_verb alc662_eeepc_sue_init_verbs[] = {
        {}
 };
 
+/* Set Unsolicited Event*/
+static struct hda_verb alc662_eeepc_ep20_sue_init_verbs[] = {
+       {0x1b, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
+       {0x14, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | ALC880_HP_EVENT},
+       {}
+};
+
 /*
  * generic initialization of ADC, input mixers and output mixers
  */
@@ -11957,14 +13168,13 @@ static struct snd_kcontrol_new alc662_capture_mixer[] = {
                .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
                /* The multiple "Capture Source" controls confuse alsamixer
                 * So call somewhat different..
-                * FIXME: the controls appear in the "playback" view!
                 */
                /* .name = "Capture Source", */
                .name = "Input Source",
                .count = 1,
-               .info = alc882_mux_enum_info,
-               .get = alc882_mux_enum_get,
-               .put = alc882_mux_enum_put,
+               .info = alc662_mux_enum_info,
+               .get = alc662_mux_enum_get,
+               .put = alc662_mux_enum_put,
        },
        { } /* end */
 };
@@ -12037,6 +13247,40 @@ static void alc662_eeepc_inithook(struct hda_codec *codec)
        alc662_eeepc_mic_automute(codec);
 }
 
+static void alc662_eeepc_ep20_automute(struct hda_codec *codec)
+{
+       unsigned int mute;
+       unsigned int present;
+
+       snd_hda_codec_read(codec, 0x14, 0, AC_VERB_SET_PIN_SENSE, 0);
+       present = snd_hda_codec_read(codec, 0x14, 0,
+                                    AC_VERB_GET_PIN_SENSE, 0);
+       present = (present & 0x80000000) != 0;
+       if (present) {
+               /* mute internal speaker */
+               snd_hda_codec_amp_stereo(codec, 0x1b, HDA_OUTPUT, 0,
+                                        HDA_AMP_MUTE, HDA_AMP_MUTE);
+       } else {
+               /* unmute internal speaker if necessary */
+               mute = snd_hda_codec_amp_read(codec, 0x14, 0, HDA_OUTPUT, 0);
+               snd_hda_codec_amp_stereo(codec, 0x1b, HDA_OUTPUT, 0,
+                                        HDA_AMP_MUTE, mute);
+       }
+}
+
+/* unsolicited event for HP jack sensing */
+static void alc662_eeepc_ep20_unsol_event(struct hda_codec *codec,
+                                         unsigned int res)
+{
+       if ((res >> 26) == ALC880_HP_EVENT)
+               alc662_eeepc_ep20_automute(codec);
+}
+
+static void alc662_eeepc_ep20_inithook(struct hda_codec *codec)
+{
+       alc662_eeepc_ep20_automute(codec);
+}
+
 #ifdef CONFIG_SND_HDA_POWER_SAVE
 #define alc662_loopbacks       alc880_loopbacks
 #endif
@@ -12057,12 +13301,15 @@ static const char *alc662_models[ALC662_MODEL_LAST] = {
        [ALC662_3ST_6ch]        = "3stack-6ch",
        [ALC662_5ST_DIG]        = "6stack-dig",
        [ALC662_LENOVO_101E]    = "lenovo-101e",
+       [ALC662_ASUS_EEEPC_P701] = "eeepc-p701",
+       [ALC662_ASUS_EEEPC_EP20] = "eeepc-ep20",
        [ALC662_AUTO]           = "auto",
 };
 
 static struct snd_pci_quirk alc662_cfg_tbl[] = {
-       SND_PCI_QUIRK(0x17aa, 0x101e, "Lenovo", ALC662_LENOVO_101E),
        SND_PCI_QUIRK(0x1043, 0x82a1, "ASUS Eeepc", ALC662_ASUS_EEEPC_P701),
+       SND_PCI_QUIRK(0x1043, 0x82d1, "ASUS Eeepc EP20", ALC662_ASUS_EEEPC_EP20),
+       SND_PCI_QUIRK(0x17aa, 0x101e, "Lenovo", ALC662_LENOVO_101E),
        {}
 };
 
@@ -12149,6 +13396,21 @@ static struct alc_config_preset alc662_presets[] = {
                .unsol_event = alc662_eeepc_unsol_event,
                .init_hook = alc662_eeepc_inithook,
        },
+       [ALC662_ASUS_EEEPC_EP20] = {
+               .mixers = { alc662_eeepc_ep20_mixer, alc662_capture_mixer,
+                           alc662_chmode_mixer },
+               .init_verbs = { alc662_init_verbs,
+                               alc662_eeepc_ep20_sue_init_verbs },
+               .num_dacs = ARRAY_SIZE(alc662_dac_nids),
+               .dac_nids = alc662_dac_nids,
+               .num_adc_nids = ARRAY_SIZE(alc662_adc_nids),
+               .adc_nids = alc662_adc_nids,
+               .num_channel_mode = ARRAY_SIZE(alc662_3ST_6ch_modes),
+               .channel_mode = alc662_3ST_6ch_modes,
+               .input_mux = &alc662_lenovo_101e_capture_source,
+               .unsol_event = alc662_eeepc_ep20_unsol_event,
+               .init_hook = alc662_eeepc_ep20_inithook,
+       },
 
 };
 
@@ -12308,6 +13570,7 @@ static void alc662_auto_init_multi_out(struct hda_codec *codec)
        struct alc_spec *spec = codec->spec;
        int i;
 
+       alc_subsystem_id(codec, 0x15, 0x1b, 0x14);
        for (i = 0; i <= HDA_SIDE; i++) {
                hda_nid_t nid = spec->autocfg.line_out_pins[i];
                int pin_type = get_pin_type(spec->autocfg.line_out_type);
@@ -12458,6 +13721,8 @@ static int patch_alc662(struct hda_codec *codec)
                spec->num_adc_nids = ARRAY_SIZE(alc662_adc_nids);
        }
 
+       spec->vmaster_nid = 0x02;
+
        codec->patch_ops = alc_patch_ops;
        if (board_config == ALC662_AUTO)
                spec->init_hook = alc662_auto_init;
@@ -12475,7 +13740,9 @@ static int patch_alc662(struct hda_codec *codec)
 struct hda_codec_preset snd_hda_preset_realtek[] = {
        { .id = 0x10ec0260, .name = "ALC260", .patch = patch_alc260 },
        { .id = 0x10ec0262, .name = "ALC262", .patch = patch_alc262 },
+       { .id = 0x10ec0267, .name = "ALC267", .patch = patch_alc268 },
        { .id = 0x10ec0268, .name = "ALC268", .patch = patch_alc268 },
+       { .id = 0x10ec0269, .name = "ALC269", .patch = patch_alc269 },
        { .id = 0x10ec0861, .rev = 0x100340, .name = "ALC660",
          .patch = patch_alc861 },
        { .id = 0x10ec0660, .name = "ALC660-VD", .patch = patch_alc861vd },
@@ -12490,5 +13757,6 @@ struct hda_codec_preset snd_hda_preset_realtek[] = {
        { .id = 0x10ec0883, .name = "ALC883", .patch = patch_alc883 },
        { .id = 0x10ec0885, .name = "ALC885", .patch = patch_alc882 },
        { .id = 0x10ec0888, .name = "ALC888", .patch = patch_alc883 },
+       { .id = 0x10ec0889, .name = "ALC889", .patch = patch_alc883 },
        {} /* terminator */
 };
index 2a4b9609aa5c80e75a23c7b6d36352855f1d885e..d22f5a6b850f2b82d3f39060a57a13bb769a3d6e 100644 (file)
@@ -22,7 +22,6 @@
  *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA
  */
 
-#include <sound/driver.h>
 #include <linux/init.h>
 #include <linux/delay.h>
 #include <linux/slab.h>
@@ -287,7 +286,6 @@ static int patch_si3054(struct hda_codec *codec)
 struct hda_codec_preset snd_hda_preset_si3054[] = {
        { .id = 0x163c3055, .name = "Si3054", .patch = patch_si3054 },
        { .id = 0x163c3155, .name = "Si3054", .patch = patch_si3054 },
-       { .id = 0x11c11040, .name = "Si3054", .patch = patch_si3054 },
        { .id = 0x11c13026, .name = "Si3054", .patch = patch_si3054 },
        { .id = 0x11c13055, .name = "Si3054", .patch = patch_si3054 },
        { .id = 0x11c13155, .name = "Si3054", .patch = patch_si3054 },
index 04012237096c18fbba12da7be7ee251691dca160..caf48edaa921f629870b5e39bc4e83a94d56c52d 100644 (file)
@@ -24,7 +24,6 @@
  *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA
  */
 
-#include <sound/driver.h>
 #include <linux/init.h>
 #include <linux/delay.h>
 #include <linux/slab.h>
@@ -35,7 +34,8 @@
 #include "hda_local.h"
 
 #define NUM_CONTROL_ALLOC      32
-#define STAC_HP_EVENT          0x37
+#define STAC_PWR_EVENT         0x20
+#define STAC_HP_EVENT          0x30
 
 enum {
        STAC_REF,
@@ -61,6 +61,16 @@ enum {
        STAC_9205_MODELS
 };
 
+enum {
+       STAC_92HD73XX_REF,
+       STAC_92HD73XX_MODELS
+};
+
+enum {
+       STAC_92HD71BXX_REF,
+       STAC_92HD71BXX_MODELS
+};
+
 enum {
        STAC_925x_REF,
        STAC_M2_2,
@@ -97,6 +107,7 @@ enum {
        STAC_D965_3ST,
        STAC_D965_5ST,
        STAC_DELL_3ST,
+       STAC_DELL_BIOS,
        STAC_927X_MODELS
 };
 
@@ -110,11 +121,24 @@ struct sigmatel_spec {
        unsigned int mic_switch: 1;
        unsigned int alt_switch: 1;
        unsigned int hp_detect: 1;
-       unsigned int gpio_mute: 1;
 
-       unsigned int gpio_mask, gpio_data;
+       /* gpio lines */
+       unsigned int gpio_mask;
+       unsigned int gpio_dir;
+       unsigned int gpio_data;
+       unsigned int gpio_mute;
+
+       /* analog loopback */
+       unsigned char aloopback_mask;
+       unsigned char aloopback_shift;
+
+       /* power management */
+       unsigned int num_pwrs;
+       hda_nid_t *pwr_nids;
 
        /* playback */
+       struct hda_input_mux *mono_mux;
+       unsigned int cur_mmux;
        struct hda_multi_out multiout;
        hda_nid_t dac_nids[5];
 
@@ -125,8 +149,10 @@ struct sigmatel_spec {
        unsigned int num_muxes;
        hda_nid_t *dmic_nids;
        unsigned int num_dmics;
-       hda_nid_t dmux_nid;
+       hda_nid_t *dmux_nids;
+       unsigned int num_dmuxes;
        hda_nid_t dig_in_nid;
+       hda_nid_t mono_nid;
 
        /* pin widgets */
        hda_nid_t *pin_nids;
@@ -140,7 +166,7 @@ struct sigmatel_spec {
 
        /* capture source */
        struct hda_input_mux *dinput_mux;
-       unsigned int cur_dmux;
+       unsigned int cur_dmux[2];
        struct hda_input_mux *input_mux;
        unsigned int cur_mux[3];
 
@@ -157,6 +183,10 @@ struct sigmatel_spec {
        struct snd_kcontrol_new *kctl_alloc;
        struct hda_input_mux private_dimux;
        struct hda_input_mux private_imux;
+       struct hda_input_mux private_mono_mux;
+
+       /* virtual master */
+       unsigned int vmaster_tlv[4];
 };
 
 static hda_nid_t stac9200_adc_nids[1] = {
@@ -171,6 +201,58 @@ static hda_nid_t stac9200_dac_nids[1] = {
         0x02,
 };
 
+static hda_nid_t stac92hd73xx_pwr_nids[8] = {
+       0x0a, 0x0b, 0x0c, 0xd, 0x0e,
+       0x0f, 0x10, 0x11
+};
+
+static hda_nid_t stac92hd73xx_adc_nids[2] = {
+       0x1a, 0x1b
+};
+
+#define STAC92HD73XX_NUM_DMICS 2
+static hda_nid_t stac92hd73xx_dmic_nids[STAC92HD73XX_NUM_DMICS + 1] = {
+       0x13, 0x14, 0
+};
+
+#define STAC92HD73_DAC_COUNT 5
+static hda_nid_t stac92hd73xx_dac_nids[STAC92HD73_DAC_COUNT] = {
+       0x15, 0x16, 0x17, 0x18, 0x19,
+};
+
+static hda_nid_t stac92hd73xx_mux_nids[4] = {
+       0x28, 0x29, 0x2a, 0x2b,
+};
+
+static hda_nid_t stac92hd73xx_dmux_nids[2] = {
+       0x20, 0x21,
+};
+
+static hda_nid_t stac92hd71bxx_pwr_nids[3] = {
+       0x0a, 0x0d, 0x0f
+};
+
+static hda_nid_t stac92hd71bxx_adc_nids[2] = {
+       0x12, 0x13,
+};
+
+static hda_nid_t stac92hd71bxx_mux_nids[2] = {
+       0x1a, 0x1b
+};
+
+static hda_nid_t stac92hd71bxx_dmux_nids[1] = {
+       0x1c,
+};
+
+static hda_nid_t stac92hd71bxx_dac_nids[2] = {
+       0x10, /*0x11, */
+};
+
+#define STAC92HD71BXX_NUM_DMICS        2
+static hda_nid_t stac92hd71bxx_dmic_nids[STAC92HD71BXX_NUM_DMICS + 1] = {
+       0x18, 0x19, 0
+};
+
 static hda_nid_t stac925x_adc_nids[1] = {
         0x03,
 };
@@ -188,6 +270,10 @@ static hda_nid_t stac925x_dmic_nids[STAC925X_NUM_DMICS + 1] = {
        0x15, 0
 };
 
+static hda_nid_t stac925x_dmux_nids[1] = {
+       0x14,
+};
+
 static hda_nid_t stac922x_adc_nids[2] = {
         0x06, 0x07,
 };
@@ -204,6 +290,15 @@ static hda_nid_t stac927x_mux_nids[3] = {
         0x15, 0x16, 0x17
 };
 
+static hda_nid_t stac927x_dmux_nids[1] = {
+       0x1b,
+};
+
+#define STAC927X_NUM_DMICS 2
+static hda_nid_t stac927x_dmic_nids[STAC927X_NUM_DMICS + 1] = {
+       0x13, 0x14, 0
+};
+
 static hda_nid_t stac9205_adc_nids[2] = {
         0x12, 0x13
 };
@@ -212,6 +307,10 @@ static hda_nid_t stac9205_mux_nids[2] = {
         0x19, 0x1a
 };
 
+static hda_nid_t stac9205_dmux_nids[1] = {
+       0x1d,
+};
+
 #define STAC9205_NUM_DMICS     2
 static hda_nid_t stac9205_dmic_nids[STAC9205_NUM_DMICS + 1] = {
         0x17, 0x18, 0
@@ -232,6 +331,17 @@ static hda_nid_t stac922x_pin_nids[10] = {
        0x0f, 0x10, 0x11, 0x15, 0x1b,
 };
 
+static hda_nid_t stac92hd73xx_pin_nids[12] = {
+       0x0a, 0x0b, 0x0c, 0x0d, 0x0e,
+       0x0f, 0x10, 0x11, 0x12, 0x13,
+       0x14, 0x22
+};
+
+static hda_nid_t stac92hd71bxx_pin_nids[10] = {
+       0x0a, 0x0b, 0x0c, 0x0d, 0x0e,
+       0x0f, 0x14, 0x18, 0x19, 0x1e,
+};
+
 static hda_nid_t stac927x_pin_nids[14] = {
        0x0a, 0x0b, 0x0c, 0x0d, 0x0e,
        0x0f, 0x10, 0x11, 0x12, 0x13,
@@ -257,8 +367,9 @@ static int stac92xx_dmux_enum_get(struct snd_kcontrol *kcontrol,
 {
        struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
        struct sigmatel_spec *spec = codec->spec;
+       unsigned int dmux_idx = snd_ctl_get_ioffidx(kcontrol, &ucontrol->id);
 
-       ucontrol->value.enumerated.item[0] = spec->cur_dmux;
+       ucontrol->value.enumerated.item[0] = spec->cur_dmux[dmux_idx];
        return 0;
 }
 
@@ -267,9 +378,10 @@ static int stac92xx_dmux_enum_put(struct snd_kcontrol *kcontrol,
 {
        struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
        struct sigmatel_spec *spec = codec->spec;
+       unsigned int dmux_idx = snd_ctl_get_ioffidx(kcontrol, &ucontrol->id);
 
        return snd_hda_input_mux_put(codec, spec->dinput_mux, ucontrol,
-                                    spec->dmux_nid, &spec->cur_dmux);
+                       spec->dmux_nids[dmux_idx], &spec->cur_dmux[dmux_idx]);
 }
 
 static int stac92xx_mux_enum_info(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *uinfo)
@@ -299,15 +411,45 @@ static int stac92xx_mux_enum_put(struct snd_kcontrol *kcontrol, struct snd_ctl_e
                                     spec->mux_nids[adc_idx], &spec->cur_mux[adc_idx]);
 }
 
+static int stac92xx_mono_mux_enum_info(struct snd_kcontrol *kcontrol,
+       struct snd_ctl_elem_info *uinfo)
+{
+       struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
+       struct sigmatel_spec *spec = codec->spec;
+       return snd_hda_input_mux_info(spec->mono_mux, uinfo);
+}
+
+static int stac92xx_mono_mux_enum_get(struct snd_kcontrol *kcontrol,
+       struct snd_ctl_elem_value *ucontrol)
+{
+       struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
+       struct sigmatel_spec *spec = codec->spec;
+
+       ucontrol->value.enumerated.item[0] = spec->cur_mmux;
+       return 0;
+}
+
+static int stac92xx_mono_mux_enum_put(struct snd_kcontrol *kcontrol,
+       struct snd_ctl_elem_value *ucontrol)
+{
+       struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
+       struct sigmatel_spec *spec = codec->spec;
+
+       return snd_hda_input_mux_put(codec, spec->mono_mux, ucontrol,
+                                    spec->mono_nid, &spec->cur_mmux);
+}
+
 #define stac92xx_aloopback_info snd_ctl_boolean_mono_info
 
 static int stac92xx_aloopback_get(struct snd_kcontrol *kcontrol,
        struct snd_ctl_elem_value *ucontrol)
 {
        struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
+       unsigned int idx = snd_ctl_get_ioffidx(kcontrol, &ucontrol->id);
        struct sigmatel_spec *spec = codec->spec;
 
-       ucontrol->value.integer.value[0] = spec->aloopback;
+       ucontrol->value.integer.value[0] = !!(spec->aloopback &
+                                             (spec->aloopback_mask << idx));
        return 0;
 }
 
@@ -316,23 +458,33 @@ static int stac92xx_aloopback_put(struct snd_kcontrol *kcontrol,
 {
        struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
        struct sigmatel_spec *spec = codec->spec;
+       unsigned int idx = snd_ctl_get_ioffidx(kcontrol, &ucontrol->id);
        unsigned int dac_mode;
+       unsigned int val, idx_val;
 
-       if (spec->aloopback == ucontrol->value.integer.value[0])
+       idx_val = spec->aloopback_mask << idx;
+       if (ucontrol->value.integer.value[0])
+               val = spec->aloopback | idx_val;
+       else
+               val = spec->aloopback & ~idx_val;
+       if (spec->aloopback == val)
                return 0;
 
-       spec->aloopback = ucontrol->value.integer.value[0];
-
+       spec->aloopback = val;
 
+       /* Only return the bits defined by the shift value of the
+        * first two bytes of the mask
+        */
        dac_mode = snd_hda_codec_read(codec, codec->afg, 0,
-               kcontrol->private_value & 0xFFFF, 0x0);
+                                     kcontrol->private_value & 0xFFFF, 0x0);
+       dac_mode >>= spec->aloopback_shift;
 
-       if (spec->aloopback) {
+       if (spec->aloopback & idx_val) {
                snd_hda_power_up(codec);
-               dac_mode |= 0x40;
+               dac_mode |= idx_val;
        } else {
                snd_hda_power_down(codec);
-               dac_mode &= ~0x40;
+               dac_mode &= ~idx_val;
        }
 
        snd_hda_codec_write_cache(codec, codec->afg, 0,
@@ -354,6 +506,107 @@ static struct hda_verb stac9200_eapd_init[] = {
        {}
 };
 
+static struct hda_verb stac92hd73xx_6ch_core_init[] = {
+       /* set master volume and direct control */
+       { 0x1f, AC_VERB_SET_VOLUME_KNOB_CONTROL, 0xff},
+       /* setup audio connections */
+       { 0x0f, AC_VERB_SET_CONNECT_SEL, 0x00},
+       { 0x10, AC_VERB_SET_CONNECT_SEL, 0x01},
+       { 0x11, AC_VERB_SET_CONNECT_SEL, 0x02},
+       /* setup adcs to point to mixer */
+       { 0x20, AC_VERB_SET_CONNECT_SEL, 0x0b},
+       { 0x21, AC_VERB_SET_CONNECT_SEL, 0x0b},
+       { 0x0f, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
+       { 0x10, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
+       { 0x11, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
+       /* setup import muxs */
+       { 0x28, AC_VERB_SET_CONNECT_SEL, 0x01},
+       { 0x29, AC_VERB_SET_CONNECT_SEL, 0x01},
+       { 0x2a, AC_VERB_SET_CONNECT_SEL, 0x01},
+       { 0x2b, AC_VERB_SET_CONNECT_SEL, 0x00},
+       {}
+};
+
+static struct hda_verb stac92hd73xx_8ch_core_init[] = {
+       /* set master volume and direct control */
+       { 0x1f, AC_VERB_SET_VOLUME_KNOB_CONTROL, 0xff},
+       /* setup audio connections */
+       { 0x0f, AC_VERB_SET_CONNECT_SEL, 0x00},
+       { 0x10, AC_VERB_SET_CONNECT_SEL, 0x01},
+       { 0x11, AC_VERB_SET_CONNECT_SEL, 0x02},
+       /* connect hp ports to dac3 */
+       { 0x0a, AC_VERB_SET_CONNECT_SEL, 0x03},
+       { 0x0d, AC_VERB_SET_CONNECT_SEL, 0x03},
+       /* setup adcs to point to mixer */
+       { 0x20, AC_VERB_SET_CONNECT_SEL, 0x0b},
+       { 0x21, AC_VERB_SET_CONNECT_SEL, 0x0b},
+       { 0x0f, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
+       { 0x10, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
+       { 0x11, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
+       /* setup import muxs */
+       { 0x28, AC_VERB_SET_CONNECT_SEL, 0x01},
+       { 0x29, AC_VERB_SET_CONNECT_SEL, 0x01},
+       { 0x2a, AC_VERB_SET_CONNECT_SEL, 0x01},
+       { 0x2b, AC_VERB_SET_CONNECT_SEL, 0x03},
+       {}
+};
+
+static struct hda_verb stac92hd73xx_10ch_core_init[] = {
+       /* set master volume and direct control */
+       { 0x1f, AC_VERB_SET_VOLUME_KNOB_CONTROL, 0xff},
+       /* setup audio connections */
+       { 0x0f, AC_VERB_SET_CONNECT_SEL, 0x00 },
+       { 0x10, AC_VERB_SET_CONNECT_SEL, 0x01 },
+       { 0x11, AC_VERB_SET_CONNECT_SEL, 0x02 },
+       /* dac3 is connected to import3 mux */
+       { 0x18, AC_VERB_SET_AMP_GAIN_MUTE, 0xb07f},
+       /* connect hp ports to dac4 */
+       { 0x0a, AC_VERB_SET_CONNECT_SEL, 0x04},
+       { 0x0d, AC_VERB_SET_CONNECT_SEL, 0x04},
+       /* setup adcs to point to mixer */
+       { 0x20, AC_VERB_SET_CONNECT_SEL, 0x0b},
+       { 0x21, AC_VERB_SET_CONNECT_SEL, 0x0b},
+       { 0x0f, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
+       { 0x10, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
+       { 0x11, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
+       /* setup import muxs */
+       { 0x28, AC_VERB_SET_CONNECT_SEL, 0x01},
+       { 0x29, AC_VERB_SET_CONNECT_SEL, 0x01},
+       { 0x2a, AC_VERB_SET_CONNECT_SEL, 0x01},
+       { 0x2b, AC_VERB_SET_CONNECT_SEL, 0x03},
+       {}
+};
+
+static struct hda_verb stac92hd71bxx_core_init[] = {
+       /* set master volume and direct control */
+       { 0x28, AC_VERB_SET_VOLUME_KNOB_CONTROL, 0xff},
+       /* connect headphone jack to dac1 */
+       { 0x0a, AC_VERB_SET_CONNECT_SEL, 0x01},
+       { 0x0f, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT}, /* Speaker */
+       /* unmute right and left channels for nodes 0x0a, 0xd, 0x0f */
+       { 0x0a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
+       { 0x0d, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
+       { 0x0f, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
+};
+
+static struct hda_verb stac92hd71bxx_analog_core_init[] = {
+       /* set master volume and direct control */
+       { 0x28, AC_VERB_SET_VOLUME_KNOB_CONTROL, 0xff},
+       /* connect headphone jack to dac1 */
+       { 0x0a, AC_VERB_SET_CONNECT_SEL, 0x01},
+       /* connect ports 0d and 0f to audio mixer */
+       { 0x0d, AC_VERB_SET_CONNECT_SEL, 0x2},
+       { 0x0f, AC_VERB_SET_CONNECT_SEL, 0x2},
+       { 0x0f, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT}, /* Speaker */
+       /* unmute dac0 input in audio mixer */
+       { 0x17, AC_VERB_SET_AMP_GAIN_MUTE, 0x701f},
+       /* unmute right and left channels for nodes 0x0a, 0xd, 0x0f */
+       { 0x0a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
+       { 0x0d, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
+       { 0x0f, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
+       {}
+};
+
 static struct hda_verb stac925x_core_init[] = {
        /* set dac0mux for dac converter */
        { 0x06, AC_VERB_SET_CONNECT_SEL, 0x00},
@@ -388,6 +641,16 @@ static struct hda_verb stac9205_core_init[] = {
        {}
 };
 
+#define STAC_MONO_MUX \
+       { \
+               .iface = SNDRV_CTL_ELEM_IFACE_MIXER, \
+               .name = "Mono Mux", \
+               .count = 1, \
+               .info = stac92xx_mono_mux_enum_info, \
+               .get = stac92xx_mono_mux_enum_get, \
+               .put = stac92xx_mono_mux_enum_put, \
+       }
+
 #define STAC_INPUT_SOURCE(cnt) \
        { \
                .iface = SNDRV_CTL_ELEM_IFACE_MIXER, \
@@ -398,11 +661,11 @@ static struct hda_verb stac9205_core_init[] = {
                .put = stac92xx_mux_enum_put, \
        }
 
-#define STAC_ANALOG_LOOPBACK(verb_read,verb_write) \
+#define STAC_ANALOG_LOOPBACK(verb_read, verb_write, cnt) \
        { \
                .iface = SNDRV_CTL_ELEM_IFACE_MIXER, \
                .name  = "Analog Loopback", \
-               .count = 1, \
+               .count = cnt, \
                .info  = stac92xx_aloopback_info, \
                .get   = stac92xx_aloopback_get, \
                .put   = stac92xx_aloopback_put, \
@@ -419,6 +682,114 @@ static struct snd_kcontrol_new stac9200_mixer[] = {
        { } /* end */
 };
 
+static struct snd_kcontrol_new stac92hd73xx_6ch_mixer[] = {
+       STAC_ANALOG_LOOPBACK(0xFA0, 0x7A1, 3),
+
+       HDA_CODEC_VOLUME_IDX("Capture Volume", 0x0, 0x20, 0x0, HDA_OUTPUT),
+       HDA_CODEC_MUTE_IDX("Capture Switch", 0x0, 0x20, 0x0, HDA_OUTPUT),
+
+       HDA_CODEC_VOLUME_IDX("Capture Volume", 0x1, 0x21, 0x0, HDA_OUTPUT),
+       HDA_CODEC_MUTE_IDX("Capture Switch", 0x1, 0x21, 0x0, HDA_OUTPUT),
+
+       HDA_CODEC_VOLUME("Front Mic Mixer Capture Volume", 0x1d, 0, HDA_INPUT),
+       HDA_CODEC_MUTE("Front Mic Mixer Capture Switch", 0x1d, 0, HDA_INPUT),
+
+       HDA_CODEC_VOLUME("Mic Mixer Capture Volume", 0x1d, 0x1, HDA_INPUT),
+       HDA_CODEC_MUTE("Mic Mixer Capture Switch", 0x1d, 0x1, HDA_INPUT),
+
+       HDA_CODEC_VOLUME("Line In Mixer Capture Volume", 0x1d, 0x2, HDA_INPUT),
+       HDA_CODEC_MUTE("Line In Mixer Capture Switch", 0x1d, 0x2, HDA_INPUT),
+
+       HDA_CODEC_VOLUME("DAC Mixer Capture Volume", 0x1d, 0x3, HDA_INPUT),
+       HDA_CODEC_MUTE("DAC Mixer Capture Switch", 0x1d, 0x3, HDA_INPUT),
+
+       HDA_CODEC_VOLUME("CD Mixer Capture Volume", 0x1d, 0x4, HDA_INPUT),
+       HDA_CODEC_MUTE("CD Mixer Capture Switch", 0x1d, 0x4, HDA_INPUT),
+       { } /* end */
+};
+
+static struct snd_kcontrol_new stac92hd73xx_8ch_mixer[] = {
+       STAC_ANALOG_LOOPBACK(0xFA0, 0x7A1, 4),
+
+       HDA_CODEC_VOLUME_IDX("Capture Volume", 0x0, 0x20, 0x0, HDA_OUTPUT),
+       HDA_CODEC_MUTE_IDX("Capture Switch", 0x0, 0x20, 0x0, HDA_OUTPUT),
+
+       HDA_CODEC_VOLUME_IDX("Capture Volume", 0x1, 0x21, 0x0, HDA_OUTPUT),
+       HDA_CODEC_MUTE_IDX("Capture Switch", 0x1, 0x21, 0x0, HDA_OUTPUT),
+
+       HDA_CODEC_VOLUME("Front Mic Mixer Capture Volume", 0x1d, 0, HDA_INPUT),
+       HDA_CODEC_MUTE("Front Mic Mixer Capture Switch", 0x1d, 0, HDA_INPUT),
+
+       HDA_CODEC_VOLUME("Mic Mixer Capture Volume", 0x1d, 0x1, HDA_INPUT),
+       HDA_CODEC_MUTE("Mic Mixer Capture Switch", 0x1d, 0x1, HDA_INPUT),
+
+       HDA_CODEC_VOLUME("Line In Mixer Capture Volume", 0x1d, 0x2, HDA_INPUT),
+       HDA_CODEC_MUTE("Line In Mixer Capture Switch", 0x1d, 0x2, HDA_INPUT),
+
+       HDA_CODEC_VOLUME("DAC Mixer Capture Volume", 0x1d, 0x3, HDA_INPUT),
+       HDA_CODEC_MUTE("DAC Mixer Capture Switch", 0x1d, 0x3, HDA_INPUT),
+
+       HDA_CODEC_VOLUME("CD Mixer Capture Volume", 0x1d, 0x4, HDA_INPUT),
+       HDA_CODEC_MUTE("CD Mixer Capture Switch", 0x1d, 0x4, HDA_INPUT),
+       { } /* end */
+};
+
+static struct snd_kcontrol_new stac92hd73xx_10ch_mixer[] = {
+       STAC_ANALOG_LOOPBACK(0xFA0, 0x7A1, 5),
+
+       HDA_CODEC_VOLUME_IDX("Capture Volume", 0x0, 0x20, 0x0, HDA_OUTPUT),
+       HDA_CODEC_MUTE_IDX("Capture Switch", 0x0, 0x20, 0x0, HDA_OUTPUT),
+
+       HDA_CODEC_VOLUME_IDX("Capture Volume", 0x1, 0x21, 0x0, HDA_OUTPUT),
+       HDA_CODEC_MUTE_IDX("Capture Switch", 0x1, 0x21, 0x0, HDA_OUTPUT),
+
+       HDA_CODEC_VOLUME("Front Mic Mixer Capture Volume", 0x1d, 0, HDA_INPUT),
+       HDA_CODEC_MUTE("Front Mic Mixer Capture Switch", 0x1d, 0, HDA_INPUT),
+
+       HDA_CODEC_VOLUME("Mic Mixer Capture Volume", 0x1d, 0x1, HDA_INPUT),
+       HDA_CODEC_MUTE("Mic Mixer Capture Switch", 0x1d, 0x1, HDA_INPUT),
+
+       HDA_CODEC_VOLUME("Line In Mixer Capture Volume", 0x1d, 0x2, HDA_INPUT),
+       HDA_CODEC_MUTE("Line In Mixer Capture Switch", 0x1d, 0x2, HDA_INPUT),
+
+       HDA_CODEC_VOLUME("DAC Mixer Capture Volume", 0x1d, 0x3, HDA_INPUT),
+       HDA_CODEC_MUTE("DAC Mixer Capture Switch", 0x1d, 0x3, HDA_INPUT),
+
+       HDA_CODEC_VOLUME("CD Mixer Capture Volume", 0x1d, 0x4, HDA_INPUT),
+       HDA_CODEC_MUTE("CD Mixer Capture Switch", 0x1d, 0x4, HDA_INPUT),
+       { } /* end */
+};
+
+static struct snd_kcontrol_new stac92hd71bxx_analog_mixer[] = {
+       STAC_INPUT_SOURCE(2),
+
+       HDA_CODEC_VOLUME_IDX("Capture Volume", 0x0, 0x1c, 0x0, HDA_OUTPUT),
+       HDA_CODEC_MUTE_IDX("Capture Switch", 0x0, 0x1c, 0x0, HDA_OUTPUT),
+       HDA_CODEC_VOLUME_IDX("Capture Mux Volume", 0x0, 0x1a, 0x0, HDA_OUTPUT),
+
+       HDA_CODEC_VOLUME_IDX("Capture Volume", 0x1, 0x1d, 0x0, HDA_OUTPUT),
+       HDA_CODEC_MUTE_IDX("Capture Switch", 0x1, 0x1d, 0x0, HDA_OUTPUT),
+       HDA_CODEC_VOLUME_IDX("Capture Mux Volume", 0x1, 0x1b, 0x0, HDA_OUTPUT),
+
+       HDA_CODEC_MUTE("Analog Loopback 1", 0x17, 0x3, HDA_INPUT),
+       HDA_CODEC_MUTE("Analog Loopback 2", 0x17, 0x4, HDA_INPUT),
+       { } /* end */
+};
+
+static struct snd_kcontrol_new stac92hd71bxx_mixer[] = {
+       STAC_INPUT_SOURCE(2),
+       STAC_ANALOG_LOOPBACK(0xFA0, 0x7A0, 2),
+
+       HDA_CODEC_VOLUME_IDX("Capture Volume", 0x0, 0x1c, 0x0, HDA_OUTPUT),
+       HDA_CODEC_MUTE_IDX("Capture Switch", 0x0, 0x1c, 0x0, HDA_OUTPUT),
+       HDA_CODEC_VOLUME_IDX("Capture Mux Volume", 0x0, 0x1a, 0x0, HDA_OUTPUT),
+
+       HDA_CODEC_VOLUME_IDX("Capture Volume", 0x1, 0x1d, 0x0, HDA_OUTPUT),
+       HDA_CODEC_MUTE_IDX("Capture Switch", 0x1, 0x1d, 0x0, HDA_OUTPUT),
+       HDA_CODEC_VOLUME_IDX("Capture Mux Volume", 0x1, 0x1b, 0x0, HDA_OUTPUT),
+       { } /* end */
+};
+
 static struct snd_kcontrol_new stac925x_mixer[] = {
        STAC_INPUT_SOURCE(1),
        HDA_CODEC_VOLUME("Capture Volume", 0x09, 0, HDA_OUTPUT),
@@ -428,16 +799,8 @@ static struct snd_kcontrol_new stac925x_mixer[] = {
 };
 
 static struct snd_kcontrol_new stac9205_mixer[] = {
-       {
-               .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
-               .name = "Digital Input Source",
-               .count = 1,
-               .info = stac92xx_dmux_enum_info,
-               .get = stac92xx_dmux_enum_get,
-               .put = stac92xx_dmux_enum_put,
-       },
        STAC_INPUT_SOURCE(2),
-       STAC_ANALOG_LOOPBACK(0xFE0, 0x7E0),
+       STAC_ANALOG_LOOPBACK(0xFE0, 0x7E0, 1),
 
        HDA_CODEC_VOLUME_IDX("Capture Volume", 0x0, 0x1b, 0x0, HDA_INPUT),
        HDA_CODEC_MUTE_IDX("Capture Switch", 0x0, 0x1d, 0x0, HDA_OUTPUT),
@@ -466,7 +829,7 @@ static struct snd_kcontrol_new stac922x_mixer[] = {
 
 static struct snd_kcontrol_new stac927x_mixer[] = {
        STAC_INPUT_SOURCE(3),
-       STAC_ANALOG_LOOPBACK(0xFEB, 0x7EB),
+       STAC_ANALOG_LOOPBACK(0xFEB, 0x7EB, 1),
 
        HDA_CODEC_VOLUME_IDX("Capture Volume", 0x0, 0x18, 0x0, HDA_INPUT),
        HDA_CODEC_MUTE_IDX("Capture Switch", 0x0, 0x1b, 0x0, HDA_OUTPUT),
@@ -482,6 +845,44 @@ static struct snd_kcontrol_new stac927x_mixer[] = {
        { } /* end */
 };
 
+static struct snd_kcontrol_new stac_dmux_mixer = {
+       .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+       .name = "Digital Input Source",
+       /* count set later */
+       .info = stac92xx_dmux_enum_info,
+       .get = stac92xx_dmux_enum_get,
+       .put = stac92xx_dmux_enum_put,
+};
+
+static const char *slave_vols[] = {
+       "Front Playback Volume",
+       "Surround Playback Volume",
+       "Center Playback Volume",
+       "LFE Playback Volume",
+       "Side Playback Volume",
+       "Headphone Playback Volume",
+       "Headphone Playback Volume",
+       "Speaker Playback Volume",
+       "External Speaker Playback Volume",
+       "Speaker2 Playback Volume",
+       NULL
+};
+
+static const char *slave_sws[] = {
+       "Front Playback Switch",
+       "Surround Playback Switch",
+       "Center Playback Switch",
+       "LFE Playback Switch",
+       "Side Playback Switch",
+       "Headphone Playback Switch",
+       "Headphone Playback Switch",
+       "Speaker Playback Switch",
+       "External Speaker Playback Switch",
+       "Speaker2 Playback Switch",
+       "IEC958 Playback Switch",
+       NULL
+};
+
 static int stac92xx_build_controls(struct hda_codec *codec)
 {
        struct sigmatel_spec *spec = codec->spec;
@@ -497,6 +898,13 @@ static int stac92xx_build_controls(struct hda_codec *codec)
                if (err < 0)
                        return err;
        }
+       if (spec->num_dmuxes > 0) {
+               stac_dmux_mixer.count = spec->num_dmuxes;
+               err = snd_ctl_add(codec->bus->card,
+                                 snd_ctl_new1(&stac_dmux_mixer, codec));
+               if (err < 0)
+                       return err;
+       }
 
        if (spec->multiout.dig_out_nid) {
                err = snd_hda_create_spdif_out_ctls(codec, spec->multiout.dig_out_nid);
@@ -508,6 +916,23 @@ static int stac92xx_build_controls(struct hda_codec *codec)
                if (err < 0)
                        return err;
        }
+
+       /* if we have no master control, let's create it */
+       if (!snd_hda_find_mixer_ctl(codec, "Master Playback Volume")) {
+               snd_hda_set_vmaster_tlv(codec, spec->multiout.dac_nids[0],
+                                       HDA_OUTPUT, spec->vmaster_tlv);
+               err = snd_hda_add_vmaster(codec, "Master Playback Volume",
+                                         spec->vmaster_tlv, slave_vols);
+               if (err < 0)
+                       return err;
+       }
+       if (!snd_hda_find_mixer_ctl(codec, "Master Playback Switch")) {
+               err = snd_hda_add_vmaster(codec, "Master Playback Switch",
+                                         NULL, slave_sws);
+               if (err < 0)
+                       return err;
+       }
+
        return 0;       
 }
 
@@ -733,7 +1158,7 @@ static struct snd_pci_quirk stac9200_cfg_tbl[] = {
 
 static unsigned int ref925x_pin_configs[8] = {
        0x40c003f0, 0x424503f2, 0x01813022, 0x02a19021,
-       0x90a70320, 0x02214210, 0x400003f1, 0x9033032e,
+       0x90a70320, 0x02214210, 0x01019020, 0x9033032e,
 };
 
 static unsigned int stac925x_MA6_pin_configs[8] = {
@@ -777,6 +1202,48 @@ static struct snd_pci_quirk stac925x_cfg_tbl[] = {
        {} /* terminator */
 };
 
+static unsigned int ref92hd73xx_pin_configs[12] = {
+       0x02214030, 0x02a19040, 0x01a19020, 0x02214030,
+       0x0181302e, 0x01014010, 0x01014020, 0x01014030,
+       0x02319040, 0x90a000f0, 0x90a000f0, 0x01452050,
+};
+
+static unsigned int *stac92hd73xx_brd_tbl[STAC_92HD73XX_MODELS] = {
+       [STAC_92HD73XX_REF] = ref92hd73xx_pin_configs,
+};
+
+static const char *stac92hd73xx_models[STAC_92HD73XX_MODELS] = {
+       [STAC_92HD73XX_REF] = "ref",
+};
+
+static struct snd_pci_quirk stac92hd73xx_cfg_tbl[] = {
+       /* SigmaTel reference board */
+       SND_PCI_QUIRK(PCI_VENDOR_ID_INTEL, 0x2668,
+                     "DFI LanParty", STAC_92HD73XX_REF),
+       {} /* terminator */
+};
+
+static unsigned int ref92hd71bxx_pin_configs[10] = {
+       0x02214030, 0x02a19040, 0x01a19020, 0x01014010,
+       0x0181302e, 0x01114010, 0x01019020, 0x90a000f0,
+       0x90a000f0, 0x01452050,
+};
+
+static unsigned int *stac92hd71bxx_brd_tbl[STAC_92HD71BXX_MODELS] = {
+       [STAC_92HD71BXX_REF] = ref92hd71bxx_pin_configs,
+};
+
+static const char *stac92hd71bxx_models[STAC_92HD71BXX_MODELS] = {
+       [STAC_92HD71BXX_REF] = "ref",
+};
+
+static struct snd_pci_quirk stac92hd71bxx_cfg_tbl[] = {
+       /* SigmaTel reference board */
+       SND_PCI_QUIRK(PCI_VENDOR_ID_INTEL, 0x2668,
+                     "DFI LanParty", STAC_92HD71BXX_REF),
+       {} /* terminator */
+};
+
 static unsigned int ref922x_pin_configs[10] = {
        0x01014010, 0x01016011, 0x01012012, 0x0221401f,
        0x01813122, 0x01011014, 0x01441030, 0x01c41030,
@@ -823,8 +1290,8 @@ static unsigned int dell_922x_m81_pin_configs[10] = {
     102801D7 (Dell XPS M1210)
 */
 static unsigned int dell_922x_m82_pin_configs[10] = {
-       0x0221121f, 0x408103ff, 0x02111212, 0x90100310, 
-       0x408003f1, 0x02111211, 0x03451340, 0x40c003f2, 
+       0x02211211, 0x408103ff, 0x02a1123e, 0x90100310, 
+       0x408003f1, 0x0221121f, 0x03451340, 0x40c003f2, 
        0x508003f3, 0x405003f4, 
 };
 
@@ -1022,22 +1489,24 @@ static unsigned int d965_5st_pin_configs[14] = {
 static unsigned int dell_3st_pin_configs[14] = {
        0x02211230, 0x02a11220, 0x01a19040, 0x01114210,
        0x01111212, 0x01116211, 0x01813050, 0x01112214,
-       0x403003fa, 0x40000100, 0x40000100, 0x404003fb,
+       0x403003fa, 0x90a60040, 0x90a60040, 0x404003fb,
        0x40c003fc, 0x40000100
 };
 
 static unsigned int *stac927x_brd_tbl[STAC_927X_MODELS] = {
-       [STAC_D965_REF] = ref927x_pin_configs,
-       [STAC_D965_3ST] = d965_3st_pin_configs,
-       [STAC_D965_5ST] = d965_5st_pin_configs,
-       [STAC_DELL_3ST] = dell_3st_pin_configs,
+       [STAC_D965_REF]  = ref927x_pin_configs,
+       [STAC_D965_3ST]  = d965_3st_pin_configs,
+       [STAC_D965_5ST]  = d965_5st_pin_configs,
+       [STAC_DELL_3ST]  = dell_3st_pin_configs,
+       [STAC_DELL_BIOS] = NULL,
 };
 
 static const char *stac927x_models[STAC_927X_MODELS] = {
-       [STAC_D965_REF] = "ref",
-       [STAC_D965_3ST] = "3stack",
-       [STAC_D965_5ST] = "5stack",
-       [STAC_DELL_3ST] = "dell-3stack",
+       [STAC_D965_REF]         = "ref",
+       [STAC_D965_3ST]         = "3stack",
+       [STAC_D965_5ST]         = "5stack",
+       [STAC_DELL_3ST]         = "dell-3stack",
+       [STAC_DELL_BIOS]        = "dell-bios",
 };
 
 static struct snd_pci_quirk stac927x_cfg_tbl[] = {
@@ -1064,13 +1533,21 @@ static struct snd_pci_quirk stac927x_cfg_tbl[] = {
        SND_PCI_QUIRK(PCI_VENDOR_ID_INTEL, 0x2003, "Intel D965", STAC_D965_3ST),
        SND_PCI_QUIRK(PCI_VENDOR_ID_INTEL, 0x2002, "Intel D965", STAC_D965_3ST),
        SND_PCI_QUIRK(PCI_VENDOR_ID_INTEL, 0x2001, "Intel D965", STAC_D965_3ST),
-       SND_PCI_QUIRK(PCI_VENDOR_ID_DELL,  0x01f3, "Dell Inspiron 1420", STAC_D965_3ST),
        /* Dell 3 stack systems */
+       SND_PCI_QUIRK(PCI_VENDOR_ID_DELL,  0x01f7, "Dell XPS M1730", STAC_DELL_3ST),
        SND_PCI_QUIRK(PCI_VENDOR_ID_DELL,  0x01dd, "Dell Dimension E520", STAC_DELL_3ST),
        SND_PCI_QUIRK(PCI_VENDOR_ID_DELL,  0x01ed, "Dell     ", STAC_DELL_3ST),
        SND_PCI_QUIRK(PCI_VENDOR_ID_DELL,  0x01f4, "Dell     ", STAC_DELL_3ST),
+       /* Dell 3 stack systems with verb table in BIOS */
+       SND_PCI_QUIRK(PCI_VENDOR_ID_DELL,  0x01f3, "Dell Inspiron 1420", STAC_DELL_BIOS),
+       SND_PCI_QUIRK(PCI_VENDOR_ID_DELL,  0x0227, "Dell Vostro 1400  ", STAC_DELL_BIOS),
+       SND_PCI_QUIRK(PCI_VENDOR_ID_DELL,  0x022f, "Dell     ", STAC_DELL_BIOS),
+       SND_PCI_QUIRK(PCI_VENDOR_ID_DELL,  0x022e, "Dell     ", STAC_DELL_BIOS),
+       SND_PCI_QUIRK(PCI_VENDOR_ID_DELL,  0x0242, "Dell     ", STAC_DELL_BIOS),
+       SND_PCI_QUIRK(PCI_VENDOR_ID_DELL,  0x0243, "Dell     ", STAC_DELL_BIOS),
+       SND_PCI_QUIRK(PCI_VENDOR_ID_DELL,  0x02ff, "Dell     ", STAC_DELL_BIOS),
+       SND_PCI_QUIRK(PCI_VENDOR_ID_DELL,  0x0209, "Dell XPS 1330", STAC_DELL_BIOS),
        /* 965 based 5 stack systems */
-       SND_PCI_QUIRK(PCI_VENDOR_ID_DELL,  0x0209, "Dell XPS 1330", STAC_D965_5ST),
        SND_PCI_QUIRK(PCI_VENDOR_ID_INTEL, 0x2301, "Intel D965", STAC_D965_5ST),
        SND_PCI_QUIRK(PCI_VENDOR_ID_INTEL, 0x2302, "Intel D965", STAC_D965_5ST),
        SND_PCI_QUIRK(PCI_VENDOR_ID_INTEL, 0x2303, "Intel D965", STAC_D965_5ST),
@@ -1085,7 +1562,7 @@ static struct snd_pci_quirk stac927x_cfg_tbl[] = {
 
 static unsigned int ref9205_pin_configs[12] = {
        0x40000100, 0x40000100, 0x01016011, 0x01014010,
-       0x01813122, 0x01a19021, 0x40000100, 0x40000100,
+       0x01813122, 0x01a19021, 0x01019020, 0x40000100,
        0x90a000f0, 0x90a000f0, 0x01441030, 0x01c41030
 };
 
@@ -1097,6 +1574,7 @@ static unsigned int ref9205_pin_configs[12] = {
     102801FD
     10280204
     1028021F
+    10280228 (Dell Vostro 1500)
 */
 static unsigned int dell_9205_m42_pin_configs[12] = {
        0x0321101F, 0x03A11020, 0x400003FA, 0x90170310,
@@ -1180,6 +1658,8 @@ static struct snd_pci_quirk stac9205_cfg_tbl[] = {
                      "unknown Dell", STAC_9205_DELL_M42),
        SND_PCI_QUIRK(PCI_VENDOR_ID_DELL, 0x021f,
                      "Dell Inspiron", STAC_9205_DELL_M44),
+       SND_PCI_QUIRK(PCI_VENDOR_ID_DELL, 0x0228,
+                     "Dell Vostro 1500", STAC_9205_DELL_M42),
        {} /* terminator */
 };
 
@@ -1245,22 +1725,6 @@ static void stac92xx_set_config_regs(struct hda_codec *codec)
                                        spec->pin_configs[i]);
 }
 
-static void stac92xx_enable_gpio_mask(struct hda_codec *codec)
-{
-       struct sigmatel_spec *spec = codec->spec;
-       /* Configure GPIOx as output */
-       snd_hda_codec_write_cache(codec, codec->afg, 0,
-                                 AC_VERB_SET_GPIO_DIRECTION, spec->gpio_mask);
-       /* Configure GPIOx as CMOS */
-       snd_hda_codec_write_cache(codec, codec->afg, 0, 0x7e7, 0x00000000);
-       /* Assert GPIOx */
-       snd_hda_codec_write_cache(codec, codec->afg, 0,
-                                 AC_VERB_SET_GPIO_DATA, spec->gpio_data);
-       /* Enable GPIOx */
-       snd_hda_codec_write_cache(codec, codec->afg, 0,
-                                 AC_VERB_SET_GPIO_MASK, spec->gpio_mask);
-}
-
 /*
  * Analog playback callbacks
  */
@@ -1479,7 +1943,7 @@ static int stac92xx_io_switch_put(struct snd_kcontrol *kcontrol, struct snd_ctl_
        struct sigmatel_spec *spec = codec->spec;
         hda_nid_t nid = kcontrol->private_value >> 8;
        int io_idx = kcontrol-> private_value & 0xff;
-        unsigned short val = ucontrol->value.integer.value[0];
+       unsigned short val = !!ucontrol->value.integer.value[0];
 
        spec->io_switch[io_idx] = val;
 
@@ -1491,6 +1955,13 @@ static int stac92xx_io_switch_put(struct snd_kcontrol *kcontrol, struct snd_ctl_
                        pinctl |= stac92xx_get_vref(codec, nid);
                stac92xx_auto_set_pinctl(codec, nid, pinctl);
        }
+
+       /* check the auto-mute again: we need to mute/unmute the speaker
+        * appropriately according to the pin direction
+        */
+       if (spec->hp_detect)
+               codec->patch_ops.unsol_event(codec, STAC_HP_EVENT << 26);
+
         return 1;
 }
 
@@ -1512,11 +1983,12 @@ static int stac92xx_clfe_switch_put(struct snd_kcontrol *kcontrol,
        struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
        struct sigmatel_spec *spec = codec->spec;
        hda_nid_t nid = kcontrol->private_value & 0xff;
+       unsigned int val = !!ucontrol->value.integer.value[0];
 
-       if (spec->clfe_swap == ucontrol->value.integer.value[0])
+       if (spec->clfe_swap == val)
                return 0;
 
-       spec->clfe_swap = ucontrol->value.integer.value[0];
+       spec->clfe_swap = val;
 
        snd_hda_codec_write_cache(codec, nid, 0, AC_VERB_SET_EAPD_BTLENABLE,
                spec->clfe_swap ? 0x4 : 0x0);
@@ -1547,6 +2019,7 @@ static int stac92xx_clfe_switch_put(struct snd_kcontrol *kcontrol,
 enum {
        STAC_CTL_WIDGET_VOL,
        STAC_CTL_WIDGET_MUTE,
+       STAC_CTL_WIDGET_MONO_MUX,
        STAC_CTL_WIDGET_IO_SWITCH,
        STAC_CTL_WIDGET_CLFE_SWITCH
 };
@@ -1554,6 +2027,7 @@ enum {
 static struct snd_kcontrol_new stac92xx_control_templates[] = {
        HDA_CODEC_VOLUME(NULL, 0, 0, 0),
        HDA_CODEC_MUTE(NULL, 0, 0, 0),
+       STAC_MONO_MUX,
        STAC_CODEC_IO_SWITCH(NULL, 0),
        STAC_CODEC_CLFE_SWITCH(NULL, 0),
 };
@@ -1598,6 +2072,7 @@ static int stac92xx_add_dyn_out_pins(struct hda_codec *codec, struct auto_pin_cf
        for (i = 0; i < codec->num_nodes; i++) {
                wcaps = codec->wcaps[i];
                wtype = (wcaps & AC_WCAP_TYPE) >> AC_WCAP_TYPE_SHIFT;
+
                if (wtype == AC_WID_AUD_OUT && !(wcaps & AC_WCAP_DIGITAL))
                        num_dacs++;
        }
@@ -1685,7 +2160,6 @@ static int stac92xx_auto_fill_dac_nids(struct hda_codec *codec,
                        wcaps = snd_hda_param_read(codec, conn[j],
                                                   AC_PAR_AUDIO_WIDGET_CAP);
                        wtype = (wcaps & AC_WCAP_TYPE) >> AC_WCAP_TYPE_SHIFT;
-
                        if (wtype != AC_WID_AUD_OUT ||
                            (wcaps & AC_WCAP_DIGITAL))
                                continue;
@@ -1759,7 +2233,7 @@ static int stac92xx_auto_create_multi_out_ctls(struct hda_codec *codec,
        int i, err;
 
        struct sigmatel_spec *spec = codec->spec;
-       unsigned int wid_caps;
+       unsigned int wid_caps, pincap;
 
 
        for (i = 0; i < cfg->line_outs; i++) {
@@ -1795,13 +2269,39 @@ static int stac92xx_auto_create_multi_out_ctls(struct hda_codec *codec,
                }
        }
 
-       if (spec->line_switch)
-               if ((err = stac92xx_add_control(spec, STAC_CTL_WIDGET_IO_SWITCH, "Line In as Output Switch", cfg->input_pins[AUTO_PIN_LINE] << 8)) < 0)
-                       return err;
+       if (spec->line_switch) {
+               nid = cfg->input_pins[AUTO_PIN_LINE];
+               pincap = snd_hda_param_read(codec, nid,
+                                               AC_PAR_PIN_CAP);
+               if (pincap & AC_PINCAP_OUT) {
+                       err = stac92xx_add_control(spec,
+                               STAC_CTL_WIDGET_IO_SWITCH,
+                               "Line In as Output Switch", nid << 8);
+                       if (err < 0)
+                               return err;
+               }
+       }
 
-       if (spec->mic_switch)
-               if ((err = stac92xx_add_control(spec, STAC_CTL_WIDGET_IO_SWITCH, "Mic as Output Switch", (cfg->input_pins[AUTO_PIN_MIC] << 8) | 1)) < 0)
-                       return err;
+       if (spec->mic_switch) {
+               unsigned int def_conf;
+               nid = cfg->input_pins[AUTO_PIN_MIC];
+               def_conf = snd_hda_codec_read(codec, nid, 0,
+                                               AC_VERB_GET_CONFIG_DEFAULT, 0);
+
+               /* some laptops have an internal analog microphone
+                * which can't be used as a output */
+               if (get_defcfg_connect(def_conf) != AC_JACK_PORT_FIXED) {
+                       pincap = snd_hda_param_read(codec, nid,
+                                                       AC_PAR_PIN_CAP);
+                       if (pincap & AC_PINCAP_OUT) {
+                               err = stac92xx_add_control(spec,
+                                       STAC_CTL_WIDGET_IO_SWITCH,
+                                       "Mic as Output Switch", (nid << 8) | 1);
+                               if (err < 0)
+                                       return err;
+                       }
+               }
+       }
 
        return 0;
 }
@@ -1891,6 +2391,37 @@ static int stac92xx_auto_create_hp_ctls(struct hda_codec *codec,
        return 0;
 }
 
+/* labels for mono mux outputs */
+static const char *stac92xx_mono_labels[3] = {
+       "DAC0", "DAC1", "Mixer"
+};
+
+/* create mono mux for mono out on capable codecs */
+static int stac92xx_auto_create_mono_output_ctls(struct hda_codec *codec)
+{
+       struct sigmatel_spec *spec = codec->spec;
+       struct hda_input_mux *mono_mux = &spec->private_mono_mux;
+       int i, num_cons;
+       hda_nid_t con_lst[ARRAY_SIZE(stac92xx_mono_labels)];
+
+       num_cons = snd_hda_get_connections(codec,
+                               spec->mono_nid,
+                               con_lst,
+                               HDA_MAX_NUM_INPUTS);
+       if (!num_cons || num_cons > ARRAY_SIZE(stac92xx_mono_labels))
+               return -EINVAL;
+
+       for (i = 0; i < num_cons; i++) {
+               mono_mux->items[mono_mux->num_items].label =
+                                       stac92xx_mono_labels[i];
+               mono_mux->items[mono_mux->num_items].index = i;
+               mono_mux->num_items++;
+       }
+
+       return stac92xx_add_control(spec, STAC_CTL_WIDGET_MONO_MUX,
+                               "Mono Mux", spec->mono_nid);
+}
+
 /* labels for dmic mux inputs */
 static const char *stac92xx_dmic_labels[5] = {
        "Analog Inputs", "Digital Mic 1", "Digital Mic 2",
@@ -1904,15 +2435,18 @@ static int stac92xx_auto_create_dmic_input_ctls(struct hda_codec *codec,
        struct sigmatel_spec *spec = codec->spec;
        struct hda_input_mux *dimux = &spec->private_dimux;
        hda_nid_t con_lst[HDA_MAX_NUM_INPUTS];
-       int i, j;
+       int err, i, j;
+       char name[32];
 
        dimux->items[dimux->num_items].label = stac92xx_dmic_labels[0];
        dimux->items[dimux->num_items].index = 0;
        dimux->num_items++;
 
        for (i = 0; i < spec->num_dmics; i++) {
+               hda_nid_t nid;
                int index;
                int num_cons;
+               unsigned int wcaps;
                unsigned int def_conf;
 
                def_conf = snd_hda_codec_read(codec,
@@ -1923,17 +2457,32 @@ static int stac92xx_auto_create_dmic_input_ctls(struct hda_codec *codec,
                if (get_defcfg_connect(def_conf) == AC_JACK_PORT_NONE)
                        continue;
 
+               nid = spec->dmic_nids[i];
                num_cons = snd_hda_get_connections(codec,
-                               spec->dmux_nid,
+                               spec->dmux_nids[0],
                                con_lst,
                                HDA_MAX_NUM_INPUTS);
                for (j = 0; j < num_cons; j++)
-                       if (con_lst[j] == spec->dmic_nids[i]) {
+                       if (con_lst[j] == nid) {
                                index = j;
                                goto found;
                        }
                continue;
 found:
+               wcaps = get_wcaps(codec, nid);
+
+               if (wcaps & AC_WCAP_OUT_AMP) {
+                       sprintf(name, "%s Capture Volume",
+                               stac92xx_dmic_labels[dimux->num_items]);
+
+                       err = stac92xx_add_control(spec,
+                               STAC_CTL_WIDGET_VOL,
+                               name,
+                               HDA_COMPOSE_AMP_VAL(nid, 3, 0, HDA_OUTPUT));
+                       if (err < 0)
+                               return err;
+               }
+
                dimux->items[dimux->num_items].label =
                        stac92xx_dmic_labels[dimux->num_items];
                dimux->items[dimux->num_items].index = index;
@@ -2026,6 +2575,7 @@ static int stac92xx_parse_auto_config(struct hda_codec *codec, hda_nid_t dig_out
 {
        struct sigmatel_spec *spec = codec->spec;
        int err;
+       int hp_speaker_swap = 0;
 
        if ((err = snd_hda_parse_pin_def_config(codec,
                                                &spec->autocfg,
@@ -2034,6 +2584,68 @@ static int stac92xx_parse_auto_config(struct hda_codec *codec, hda_nid_t dig_out
        if (! spec->autocfg.line_outs)
                return 0; /* can't find valid pin config */
 
+       /* If we have no real line-out pin and multiple hp-outs, HPs should
+        * be set up as multi-channel outputs.
+        */
+       if (spec->autocfg.line_out_type == AUTO_PIN_SPEAKER_OUT &&
+           spec->autocfg.hp_outs > 1) {
+               /* Copy hp_outs to line_outs, backup line_outs in
+                * speaker_outs so that the following routines can handle
+                * HP pins as primary outputs.
+                */
+               memcpy(spec->autocfg.speaker_pins, spec->autocfg.line_out_pins,
+                      sizeof(spec->autocfg.line_out_pins));
+               spec->autocfg.speaker_outs = spec->autocfg.line_outs;
+               memcpy(spec->autocfg.line_out_pins, spec->autocfg.hp_pins,
+                      sizeof(spec->autocfg.hp_pins));
+               spec->autocfg.line_outs = spec->autocfg.hp_outs;
+               hp_speaker_swap = 1;
+       }
+       if (spec->autocfg.mono_out_pin) {
+               int dir = (get_wcaps(codec, spec->autocfg.mono_out_pin)
+                               & AC_WCAP_OUT_AMP) ? HDA_OUTPUT : HDA_INPUT;
+               u32 caps = query_amp_caps(codec,
+                               spec->autocfg.mono_out_pin, dir);
+               hda_nid_t conn_list[1];
+
+               /* get the mixer node and then the mono mux if it exists */
+               if (snd_hda_get_connections(codec,
+                               spec->autocfg.mono_out_pin, conn_list, 1) &&
+                               snd_hda_get_connections(codec, conn_list[0],
+                               conn_list, 1)) {
+
+                               int wcaps = get_wcaps(codec, conn_list[0]);
+                               int wid_type = (wcaps & AC_WCAP_TYPE)
+                                       >> AC_WCAP_TYPE_SHIFT;
+                               /* LR swap check, some stac925x have a mux that
+                                * changes the DACs output path instead of the
+                                * mono-mux path.
+                                */
+                               if (wid_type == AC_WID_AUD_SEL &&
+                                               !(wcaps & AC_WCAP_LR_SWAP))
+                                       spec->mono_nid = conn_list[0];
+               }
+               /* all mono outs have a least a mute/unmute switch */
+               err = stac92xx_add_control(spec, STAC_CTL_WIDGET_MUTE,
+                       "Mono Playback Switch",
+                       HDA_COMPOSE_AMP_VAL(spec->autocfg.mono_out_pin,
+                                       1, 0, dir));
+               if (err < 0)
+                       return err;
+               /* check to see if there is volume support for the amp */
+               if ((caps & AC_AMPCAP_NUM_STEPS) >> AC_AMPCAP_NUM_STEPS_SHIFT) {
+                       err = stac92xx_add_control(spec, STAC_CTL_WIDGET_VOL,
+                               "Mono Playback Volume",
+                               HDA_COMPOSE_AMP_VAL(spec->autocfg.mono_out_pin,
+                                       1, 0, dir));
+                       if (err < 0)
+                               return err;
+               }
+
+               stac92xx_auto_set_pinctl(codec, spec->autocfg.mono_out_pin,
+                                        AC_PINCTL_OUT_EN);
+       }
+
        if ((err = stac92xx_add_dyn_out_pins(codec, &spec->autocfg)) < 0)
                return err;
        if (spec->multiout.num_dacs == 0)
@@ -2045,6 +2657,19 @@ static int stac92xx_parse_auto_config(struct hda_codec *codec, hda_nid_t dig_out
        if (err < 0)
                return err;
 
+       if (hp_speaker_swap == 1) {
+               /* Restore the hp_outs and line_outs */
+               memcpy(spec->autocfg.hp_pins, spec->autocfg.line_out_pins,
+                      sizeof(spec->autocfg.line_out_pins));
+               spec->autocfg.hp_outs = spec->autocfg.line_outs;
+               memcpy(spec->autocfg.line_out_pins, spec->autocfg.speaker_pins,
+                      sizeof(spec->autocfg.speaker_pins));
+               spec->autocfg.line_outs = spec->autocfg.speaker_outs;
+               memset(spec->autocfg.speaker_pins, 0,
+                      sizeof(spec->autocfg.speaker_pins));
+               spec->autocfg.speaker_outs = 0;
+       }
+
        err = stac92xx_auto_create_hp_ctls(codec, &spec->autocfg);
 
        if (err < 0)
@@ -2055,6 +2680,12 @@ static int stac92xx_parse_auto_config(struct hda_codec *codec, hda_nid_t dig_out
        if (err < 0)
                return err;
 
+       if (spec->mono_nid > 0) {
+               err = stac92xx_auto_create_mono_output_ctls(codec);
+               if (err < 0)
+                       return err;
+       }
+
        if (spec->num_dmics > 0)
                if ((err = stac92xx_auto_create_dmic_input_ctls(codec,
                                                &spec->autocfg)) < 0)
@@ -2073,7 +2704,9 @@ static int stac92xx_parse_auto_config(struct hda_codec *codec, hda_nid_t dig_out
                spec->mixers[spec->num_mixers++] = spec->kctl_alloc;
 
        spec->input_mux = &spec->private_imux;
-       spec->dinput_mux = &spec->private_dimux;
+       if (!spec->dinput_mux)
+               spec->dinput_mux = &spec->private_dimux;
+       spec->mono_mux = &spec->private_mono_mux;
 
        return 1;
 }
@@ -2183,38 +2816,35 @@ static int stac9200_parse_auto_config(struct hda_codec *codec)
  * funky external mute control using GPIO pins.
  */
 
-static void stac922x_gpio_mute(struct hda_codec *codec, int pin, int muted)
+static void stac_gpio_set(struct hda_codec *codec, unsigned int mask,
+                         unsigned int dir_mask, unsigned int data)
 {
        unsigned int gpiostate, gpiomask, gpiodir;
 
        gpiostate = snd_hda_codec_read(codec, codec->afg, 0,
                                       AC_VERB_GET_GPIO_DATA, 0);
-
-       if (!muted)
-               gpiostate |= (1 << pin);
-       else
-               gpiostate &= ~(1 << pin);
+       gpiostate = (gpiostate & ~dir_mask) | (data & dir_mask);
 
        gpiomask = snd_hda_codec_read(codec, codec->afg, 0,
                                      AC_VERB_GET_GPIO_MASK, 0);
-       gpiomask |= (1 << pin);
+       gpiomask |= mask;
 
        gpiodir = snd_hda_codec_read(codec, codec->afg, 0,
                                     AC_VERB_GET_GPIO_DIRECTION, 0);
-       gpiodir |= (1 << pin);
+       gpiodir |= dir_mask;
 
-       /* AppleHDA seems to do this -- WTF is this verb?? */
+       /* Configure GPIOx as CMOS */
        snd_hda_codec_write(codec, codec->afg, 0, 0x7e7, 0);
 
        snd_hda_codec_write(codec, codec->afg, 0,
                            AC_VERB_SET_GPIO_MASK, gpiomask);
-       snd_hda_codec_write(codec, codec->afg, 0,
-                           AC_VERB_SET_GPIO_DIRECTION, gpiodir);
+       snd_hda_codec_read(codec, codec->afg, 0,
+                          AC_VERB_SET_GPIO_DIRECTION, gpiodir); /* sync */
 
        msleep(1);
 
-       snd_hda_codec_write(codec, codec->afg, 0,
-                           AC_VERB_SET_GPIO_DATA, gpiostate);
+       snd_hda_codec_read(codec, codec->afg, 0,
+                          AC_VERB_SET_GPIO_DATA, gpiostate); /* sync */
 }
 
 static void enable_pin_detect(struct hda_codec *codec, hda_nid_t nid,
@@ -2226,6 +2856,16 @@ static void enable_pin_detect(struct hda_codec *codec, hda_nid_t nid,
                                          (AC_USRSP_EN | event));
 }
 
+static int is_nid_hp_pin(struct auto_pin_cfg *cfg, hda_nid_t nid)
+{
+       int i;
+       for (i = 0; i < cfg->hp_outs; i++)
+               if (cfg->hp_pins[i] == nid)
+                       return 1; /* nid is a HP-Out */
+
+       return 0; /* nid is not a HP-Out */
+};
+
 static int stac92xx_init(struct hda_codec *codec)
 {
        struct sigmatel_spec *spec = codec->spec;
@@ -2261,10 +2901,23 @@ static int stac92xx_init(struct hda_codec *codec)
                        stac92xx_auto_set_pinctl(codec, nid, pinctl);
                }
        }
-       if (spec->num_dmics > 0)
-               for (i = 0; i < spec->num_dmics; i++)
-                       stac92xx_auto_set_pinctl(codec, spec->dmic_nids[i],
-                                                AC_PINCTL_IN_EN);
+       for (i = 0; i < spec->num_dmics; i++)
+               stac92xx_auto_set_pinctl(codec, spec->dmic_nids[i],
+                                       AC_PINCTL_IN_EN);
+       for (i = 0; i < spec->num_pwrs; i++)  {
+               int event = is_nid_hp_pin(cfg, spec->pwr_nids[i])
+                                       ? STAC_HP_EVENT : STAC_PWR_EVENT;
+               int pinctl = snd_hda_codec_read(codec, spec->pwr_nids[i],
+                                       0, AC_VERB_GET_PIN_WIDGET_CONTROL, 0);
+               /* outputs are only ports capable of power management
+                * any attempts on powering down a input port cause the
+                * referenced VREF to act quirky.
+                */
+               if (pinctl & AC_PINCTL_IN_EN)
+                       continue;
+               enable_pin_detect(codec, spec->pwr_nids[i], event | i);
+               codec->patch_ops.unsol_event(codec, (event | i) << 26);
+       }
 
        if (cfg->dig_out_pin)
                stac92xx_auto_set_pinctl(codec, cfg->dig_out_pin,
@@ -2273,10 +2926,8 @@ static int stac92xx_init(struct hda_codec *codec)
                stac92xx_auto_set_pinctl(codec, cfg->dig_in_pin,
                                         AC_PINCTL_IN_EN);
 
-       if (spec->gpio_mute) {
-               stac922x_gpio_mute(codec, 0, 0);
-               stac922x_gpio_mute(codec, 1, 0);
-       }
+       stac_gpio_set(codec, spec->gpio_mask,
+                                       spec->gpio_dir, spec->gpio_data);
 
        return 0;
 }
@@ -2342,13 +2993,20 @@ 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 int get_hp_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))
-               return 1;
+           & (1 << 31)) {
+               unsigned int pinctl;
+               pinctl = snd_hda_codec_read(codec, nid, 0,
+                                           AC_VERB_GET_PIN_WIDGET_CONTROL, 0);
+               if (pinctl & AC_PINCTL_IN_EN)
+                       return 0; /* mic- or line-input */
+               else
+                       return 1; /* HP-output */
+       }
        return 0;
 }
 
@@ -2359,10 +3017,14 @@ static void stac92xx_hp_detect(struct hda_codec *codec, unsigned int res)
        int i, presence;
 
        presence = 0;
+       if (spec->gpio_mute)
+               presence = !(snd_hda_codec_read(codec, codec->afg, 0,
+                       AC_VERB_GET_GPIO_DATA, 0) & spec->gpio_mute);
+
        for (i = 0; i < cfg->hp_outs; i++) {
-               presence = get_pin_presence(codec, cfg->hp_pins[i]);
                if (presence)
                        break;
+               presence = get_hp_pin_presence(codec, cfg->hp_pins[i]);
        }
 
        if (presence) {
@@ -2384,12 +3046,37 @@ static void stac92xx_hp_detect(struct hda_codec *codec, unsigned int res)
        }
 } 
 
+static void stac92xx_pin_sense(struct hda_codec *codec, int idx)
+{
+       struct sigmatel_spec *spec = codec->spec;
+       hda_nid_t nid = spec->pwr_nids[idx];
+       int presence, val;
+       val = snd_hda_codec_read(codec, codec->afg, 0, 0x0fec, 0x0)
+                                                       & 0x000000ff;
+       presence = get_hp_pin_presence(codec, nid);
+       idx = 1 << idx;
+
+       if (presence)
+               val &= ~idx;
+       else
+               val |= idx;
+
+       /* power down unused output ports */
+       snd_hda_codec_write(codec, codec->afg, 0, 0x7ec, val);
+};
+
 static void stac92xx_unsol_event(struct hda_codec *codec, unsigned int res)
 {
-       switch (res >> 26) {
+       struct sigmatel_spec *spec = codec->spec;
+       int idx = res >> 26 & 0x0f;
+
+       switch ((res >> 26) & 0x30) {
        case STAC_HP_EVENT:
                stac92xx_hp_detect(codec, res);
-               break;
+               /* fallthru */
+       case STAC_PWR_EVENT:
+               if (spec->num_pwrs > 0)
+                       stac92xx_pin_sense(codec, idx);
        }
 }
 
@@ -2400,10 +3087,8 @@ static int stac92xx_resume(struct hda_codec *codec)
 
        stac92xx_set_config_regs(codec);
        snd_hda_sequence_write(codec, spec->init);
-       if (spec->gpio_mute) {
-               stac922x_gpio_mute(codec, 0, 0);
-               stac922x_gpio_mute(codec, 1, 0);
-       }
+       stac_gpio_set(codec, spec->gpio_mask,
+               spec->gpio_dir, spec->gpio_data);
        snd_hda_codec_resume_amp(codec);
        snd_hda_codec_resume_cache(codec);
        /* invoke unsolicited event to reset the HP state */
@@ -2460,6 +3145,7 @@ static int patch_stac9200(struct hda_codec *codec)
        spec->num_muxes = 1;
        spec->num_dmics = 0;
        spec->num_adcs = 1;
+       spec->num_pwrs = 0;
 
        if (spec->board_config == STAC_9200_GATEWAY)
                spec->init = stac9200_eapd_init;
@@ -2515,6 +3201,7 @@ static int patch_stac925x(struct hda_codec *codec)
        spec->mux_nids = stac925x_mux_nids;
        spec->num_muxes = 1;
        spec->num_adcs = 1;
+       spec->num_pwrs = 0;
        switch (codec->vendor_id) {
        case 0x83847632: /* STAC9202  */
        case 0x83847633: /* STAC9202D */
@@ -2522,6 +3209,8 @@ static int patch_stac925x(struct hda_codec *codec)
        case 0x83847637: /* STAC9251D */
                spec->num_dmics = STAC925X_NUM_DMICS;
                spec->dmic_nids = stac925x_dmic_nids;
+               spec->num_dmuxes = ARRAY_SIZE(stac925x_dmux_nids);
+               spec->dmux_nids = stac925x_dmux_nids;
                break;
        default:
                spec->num_dmics = 0;
@@ -2551,6 +3240,204 @@ static int patch_stac925x(struct hda_codec *codec)
        return 0;
 }
 
+static struct hda_input_mux stac92hd73xx_dmux = {
+       .num_items = 4,
+       .items = {
+               { "Analog Inputs", 0x0b },
+               { "CD", 0x08 },
+               { "Digital Mic 1", 0x09 },
+               { "Digital Mic 2", 0x0a },
+       }
+};
+
+static int patch_stac92hd73xx(struct hda_codec *codec)
+{
+       struct sigmatel_spec *spec;
+       hda_nid_t conn[STAC92HD73_DAC_COUNT + 2];
+       int err = 0;
+
+       spec  = kzalloc(sizeof(*spec), GFP_KERNEL);
+       if (spec == NULL)
+               return -ENOMEM;
+
+       codec->spec = spec;
+       spec->num_pins = ARRAY_SIZE(stac92hd73xx_pin_nids);
+       spec->pin_nids = stac92hd73xx_pin_nids;
+       spec->board_config = snd_hda_check_board_config(codec,
+                                                       STAC_92HD73XX_MODELS,
+                                                       stac92hd73xx_models,
+                                                       stac92hd73xx_cfg_tbl);
+again:
+       if (spec->board_config < 0) {
+               snd_printdd(KERN_INFO "hda_codec: Unknown model for"
+                       " STAC92HD73XX, using BIOS defaults\n");
+               err = stac92xx_save_bios_config_regs(codec);
+               if (err < 0) {
+                       stac92xx_free(codec);
+                       return err;
+               }
+               spec->pin_configs = spec->bios_pin_configs;
+       } else {
+               spec->pin_configs = stac92hd73xx_brd_tbl[spec->board_config];
+               stac92xx_set_config_regs(codec);
+       }
+
+       spec->multiout.num_dacs = snd_hda_get_connections(codec, 0x0a,
+                       conn, STAC92HD73_DAC_COUNT + 2) - 1;
+
+       if (spec->multiout.num_dacs < 0) {
+               printk(KERN_WARNING "hda_codec: Could not determine "
+                      "number of channels defaulting to DAC count\n");
+               spec->multiout.num_dacs = STAC92HD73_DAC_COUNT;
+       }
+
+       switch (spec->multiout.num_dacs) {
+       case 0x3: /* 6 Channel */
+               spec->mixer = stac92hd73xx_6ch_mixer;
+               spec->init = stac92hd73xx_6ch_core_init;
+               break;
+       case 0x4: /* 8 Channel */
+               spec->multiout.hp_nid = 0x18;
+               spec->mixer = stac92hd73xx_8ch_mixer;
+               spec->init = stac92hd73xx_8ch_core_init;
+               break;
+       case 0x5: /* 10 Channel */
+               spec->multiout.hp_nid = 0x19;
+               spec->mixer = stac92hd73xx_10ch_mixer;
+               spec->init = stac92hd73xx_10ch_core_init;
+       };
+
+       spec->multiout.dac_nids = stac92hd73xx_dac_nids;
+       spec->aloopback_mask = 0x01;
+       spec->aloopback_shift = 8;
+
+       spec->mux_nids = stac92hd73xx_mux_nids;
+       spec->adc_nids = stac92hd73xx_adc_nids;
+       spec->dmic_nids = stac92hd73xx_dmic_nids;
+       spec->dmux_nids = stac92hd73xx_dmux_nids;
+
+       spec->num_muxes = ARRAY_SIZE(stac92hd73xx_mux_nids);
+       spec->num_adcs = ARRAY_SIZE(stac92hd73xx_adc_nids);
+       spec->num_dmics = STAC92HD73XX_NUM_DMICS;
+       spec->num_dmuxes = ARRAY_SIZE(stac92hd73xx_dmux_nids);
+       spec->dinput_mux = &stac92hd73xx_dmux;
+       /* GPIO0 High = Enable EAPD */
+       spec->gpio_mask = spec->gpio_dir = 0x1;
+       spec->gpio_data = 0x01;
+
+       spec->num_pwrs = ARRAY_SIZE(stac92hd73xx_pwr_nids);
+       spec->pwr_nids = stac92hd73xx_pwr_nids;
+
+       err = stac92xx_parse_auto_config(codec, 0x22, 0x24);
+
+       if (!err) {
+               if (spec->board_config < 0) {
+                       printk(KERN_WARNING "hda_codec: No auto-config is "
+                              "available, default to model=ref\n");
+                       spec->board_config = STAC_92HD73XX_REF;
+                       goto again;
+               }
+               err = -EINVAL;
+       }
+
+       if (err < 0) {
+               stac92xx_free(codec);
+               return err;
+       }
+
+       codec->patch_ops = stac92xx_patch_ops;
+
+       return 0;
+}
+
+static int patch_stac92hd71bxx(struct hda_codec *codec)
+{
+       struct sigmatel_spec *spec;
+       int err = 0;
+
+       spec  = kzalloc(sizeof(*spec), GFP_KERNEL);
+       if (spec == NULL)
+               return -ENOMEM;
+
+       codec->spec = spec;
+       spec->num_pins = ARRAY_SIZE(stac92hd71bxx_pin_nids);
+       spec->pin_nids = stac92hd71bxx_pin_nids;
+       spec->board_config = snd_hda_check_board_config(codec,
+                                                       STAC_92HD71BXX_MODELS,
+                                                       stac92hd71bxx_models,
+                                                       stac92hd71bxx_cfg_tbl);
+again:
+       if (spec->board_config < 0) {
+               snd_printdd(KERN_INFO "hda_codec: Unknown model for"
+                       " STAC92HD71BXX, using BIOS defaults\n");
+               err = stac92xx_save_bios_config_regs(codec);
+               if (err < 0) {
+                       stac92xx_free(codec);
+                       return err;
+               }
+               spec->pin_configs = spec->bios_pin_configs;
+       } else {
+               spec->pin_configs = stac92hd71bxx_brd_tbl[spec->board_config];
+               stac92xx_set_config_regs(codec);
+       }
+
+       switch (codec->vendor_id) {
+       case 0x111d76b6: /* 4 Port without Analog Mixer */
+       case 0x111d76b7:
+       case 0x111d76b4: /* 6 Port without Analog Mixer */
+       case 0x111d76b5:
+               spec->mixer = stac92hd71bxx_mixer;
+               spec->init = stac92hd71bxx_core_init;
+               break;
+       default:
+               spec->mixer = stac92hd71bxx_analog_mixer;
+               spec->init = stac92hd71bxx_analog_core_init;
+       }
+
+       spec->aloopback_mask = 0x20;
+       spec->aloopback_shift = 0;
+
+       /* GPIO0 High = EAPD */
+       spec->gpio_mask = spec->gpio_dir = spec->gpio_data = 0x1;
+
+       spec->mux_nids = stac92hd71bxx_mux_nids;
+       spec->adc_nids = stac92hd71bxx_adc_nids;
+       spec->dmic_nids = stac92hd71bxx_dmic_nids;
+       spec->dmux_nids = stac92hd71bxx_dmux_nids;
+
+       spec->num_muxes = ARRAY_SIZE(stac92hd71bxx_mux_nids);
+       spec->num_adcs = ARRAY_SIZE(stac92hd71bxx_adc_nids);
+       spec->num_dmics = STAC92HD71BXX_NUM_DMICS;
+       spec->num_dmuxes = ARRAY_SIZE(stac92hd71bxx_dmux_nids);
+
+       spec->num_pwrs = ARRAY_SIZE(stac92hd71bxx_pwr_nids);
+       spec->pwr_nids = stac92hd71bxx_pwr_nids;
+
+       spec->multiout.num_dacs = 2;
+       spec->multiout.hp_nid = 0x11;
+       spec->multiout.dac_nids = stac92hd71bxx_dac_nids;
+
+       err = stac92xx_parse_auto_config(codec, 0x21, 0x23);
+       if (!err) {
+               if (spec->board_config < 0) {
+                       printk(KERN_WARNING "hda_codec: No auto-config is "
+                              "available, default to model=ref\n");
+                       spec->board_config = STAC_92HD71BXX_REF;
+                       goto again;
+               }
+               err = -EINVAL;
+       }
+
+       if (err < 0) {
+               stac92xx_free(codec);
+               return err;
+       }
+
+       codec->patch_ops = stac92xx_patch_ops;
+
+       return 0;
+};
+
 static int patch_stac922x(struct hda_codec *codec)
 {
        struct sigmatel_spec *spec;
@@ -2567,7 +3454,8 @@ static int patch_stac922x(struct hda_codec *codec)
                                                        stac922x_models,
                                                        stac922x_cfg_tbl);
        if (spec->board_config == STAC_INTEL_MAC_V3) {
-               spec->gpio_mute = 1;
+               spec->gpio_mask = spec->gpio_dir = 0x03;
+               spec->gpio_data = 0x03;
                /* Intel Macs have all same PCI SSID, so we need to check
                 * codec SSID to distinguish the exact models
                 */
@@ -2620,6 +3508,7 @@ static int patch_stac922x(struct hda_codec *codec)
        spec->num_muxes = ARRAY_SIZE(stac922x_mux_nids);
        spec->num_adcs = ARRAY_SIZE(stac922x_adc_nids);
        spec->num_dmics = 0;
+       spec->num_pwrs = 0;
 
        spec->init = stac922x_core_init;
        spec->mixer = stac922x_mixer;
@@ -2669,53 +3558,70 @@ static int patch_stac927x(struct hda_codec *codec)
                                                        stac927x_models,
                                                        stac927x_cfg_tbl);
  again:
-       if (spec->board_config < 0) {
-                snd_printdd(KERN_INFO "hda_codec: Unknown model for STAC927x, using BIOS defaults\n");
+       if (spec->board_config < 0 || !stac927x_brd_tbl[spec->board_config]) {
+               if (spec->board_config < 0)
+                       snd_printdd(KERN_INFO "hda_codec: Unknown model for"
+                                   "STAC927x, using BIOS defaults\n");
                err = stac92xx_save_bios_config_regs(codec);
                if (err < 0) {
                        stac92xx_free(codec);
                        return err;
                }
                spec->pin_configs = spec->bios_pin_configs;
-       } else if (stac927x_brd_tbl[spec->board_config] != NULL) {
+       } else {
                spec->pin_configs = stac927x_brd_tbl[spec->board_config];
                stac92xx_set_config_regs(codec);
        }
 
+       spec->adc_nids = stac927x_adc_nids;
+       spec->num_adcs = ARRAY_SIZE(stac927x_adc_nids);
+       spec->mux_nids = stac927x_mux_nids;
+       spec->num_muxes = ARRAY_SIZE(stac927x_mux_nids);
+       spec->multiout.dac_nids = spec->dac_nids;
+
        switch (spec->board_config) {
        case STAC_D965_3ST:
-               spec->adc_nids = stac927x_adc_nids;
-               spec->mux_nids = stac927x_mux_nids;
-               spec->num_muxes = ARRAY_SIZE(stac927x_mux_nids);
-               spec->num_adcs = ARRAY_SIZE(stac927x_adc_nids);
+       case STAC_D965_5ST:
+               /* GPIO0 High = Enable EAPD */
+               spec->gpio_mask = spec->gpio_dir = 0x01;
+               spec->gpio_data = 0x01;
                spec->num_dmics = 0;
+
                spec->init = d965_core_init;
                spec->mixer = stac927x_mixer;
                break;
-       case STAC_D965_5ST:
-               spec->adc_nids = stac927x_adc_nids;
-               spec->mux_nids = stac927x_mux_nids;
-               spec->num_muxes = ARRAY_SIZE(stac927x_mux_nids);
-               spec->num_adcs = ARRAY_SIZE(stac927x_adc_nids);
-               spec->num_dmics = 0;
+       case STAC_DELL_BIOS:
+               /* correct the front output jack as a hp out */
+               stac92xx_set_config_reg(codec, 0x0f, 0x02270110);
+               /* correct the front input jack as a mic */
+               stac92xx_set_config_reg(codec, 0x0e, 0x02a79130);
+               /* fallthru */
+       case STAC_DELL_3ST:
+               /* GPIO2 High = Enable EAPD */
+               spec->gpio_mask = spec->gpio_dir = 0x04;
+               spec->gpio_data = 0x04;
+               spec->dmic_nids = stac927x_dmic_nids;
+               spec->num_dmics = STAC927X_NUM_DMICS;
+
                spec->init = d965_core_init;
                spec->mixer = stac927x_mixer;
+               spec->dmux_nids = stac927x_dmux_nids;
+               spec->num_dmuxes = ARRAY_SIZE(stac927x_dmux_nids);
                break;
        default:
-               spec->adc_nids = stac927x_adc_nids;
-               spec->mux_nids = stac927x_mux_nids;
-               spec->num_muxes = ARRAY_SIZE(stac927x_mux_nids);
-               spec->num_adcs = ARRAY_SIZE(stac927x_adc_nids);
+               /* GPIO0 High = Enable EAPD */
+               spec->gpio_mask = spec->gpio_dir = 0x1;
+               spec->gpio_data = 0x01;
                spec->num_dmics = 0;
+
                spec->init = stac927x_core_init;
                spec->mixer = stac927x_mixer;
        }
 
-       spec->multiout.dac_nids = spec->dac_nids;
-       /* GPIO0 High = Enable EAPD */
-       spec->gpio_mask = spec->gpio_data = 0x00000001;
-       stac92xx_enable_gpio_mask(codec); 
-       
+       spec->num_pwrs = 0;
+       spec->aloopback_mask = 0x40;
+       spec->aloopback_shift = 0;
+
        err = stac92xx_parse_auto_config(codec, 0x1e, 0x20);
        if (!err) {
                if (spec->board_config < 0) {
@@ -2733,6 +3639,18 @@ static int patch_stac927x(struct hda_codec *codec)
 
        codec->patch_ops = stac92xx_patch_ops;
 
+       /*
+        * !!FIXME!!
+        * The STAC927x seem to require fairly long delays for certain
+        * command sequences.  With too short delays (even if the answer
+        * is set to RIRB properly), it results in the silence output
+        * on some hardwares like Dell.
+        *
+        * The below flag enables the longer delay (see get_response
+        * in hda_intel.c).
+        */
+       codec->bus->needs_damn_long_delay = 1;
+
        return 0;
 }
 
@@ -2771,11 +3689,15 @@ static int patch_stac9205(struct hda_codec *codec)
        spec->num_muxes = ARRAY_SIZE(stac9205_mux_nids);
        spec->dmic_nids = stac9205_dmic_nids;
        spec->num_dmics = STAC9205_NUM_DMICS;
-       spec->dmux_nid = 0x1d;
+       spec->dmux_nids = stac9205_dmux_nids;
+       spec->num_dmuxes = ARRAY_SIZE(stac9205_dmux_nids);
+       spec->num_pwrs = 0;
 
        spec->init = stac9205_core_init;
        spec->mixer = stac9205_mixer;
 
+       spec->aloopback_mask = 0x40;
+       spec->aloopback_shift = 0;
        spec->multiout.dac_nids = spec->dac_nids;
        
        switch (spec->board_config){
@@ -2784,19 +3706,28 @@ static int patch_stac9205(struct hda_codec *codec)
                stac92xx_set_config_reg(codec, 0x1f, 0x01441030);
                stac92xx_set_config_reg(codec, 0x20, 0x1c410030);
 
-               spec->gpio_mask = 0x00000007; /* GPIO0-2 */
-               /* GPIO0 High = EAPD, GPIO1 Low = DRM,
-                * GPIO2 High = Headphone Mute
+               /* Enable unsol response for GPIO4/Dock HP connection */
+               snd_hda_codec_write(codec, codec->afg, 0,
+                       AC_VERB_SET_GPIO_UNSOLICITED_RSP_MASK, 0x10);
+               snd_hda_codec_write_cache(codec, codec->afg, 0,
+                                         AC_VERB_SET_UNSOLICITED_ENABLE,
+                                         (AC_USRSP_EN | STAC_HP_EVENT));
+
+               spec->gpio_dir = 0x0b;
+               spec->gpio_mask = 0x1b;
+               spec->gpio_mute = 0x10;
+               /* GPIO0 High = EAPD, GPIO1 Low = Headphone Mute,
+                * GPIO3 Low = DRM
                 */
-               spec->gpio_data = 0x00000005;
+               spec->gpio_data = 0x01;
                break;
        default:
                /* GPIO0 High = EAPD */
-               spec->gpio_mask = spec->gpio_data = 0x00000001;
+               spec->gpio_mask = spec->gpio_dir = 0x1;
+               spec->gpio_data = 0x01;
                break;
        }
 
-       stac92xx_enable_gpio_mask(codec);
        err = stac92xx_parse_auto_config(codec, 0x1f, 0x20);
        if (!err) {
                if (spec->board_config < 0) {
@@ -2950,7 +3881,7 @@ static int stac9872_vaio_init(struct hda_codec *codec)
 
 static void stac9872_vaio_hp_detect(struct hda_codec *codec, unsigned int res)
 {
-       if (get_pin_presence(codec, 0x0a)) {
+       if (get_hp_pin_presence(codec, 0x0a)) {
                stac92xx_reset_pinctl(codec, 0x0f, AC_PINCTL_OUT_EN);
                stac92xx_set_pinctl(codec, 0x0a, AC_PINCTL_OUT_EN);
        } else {
@@ -3032,6 +3963,7 @@ static int patch_stac9872(struct hda_codec *codec)
                spec->multiout.hp_nid = VAIO_HP_DAC;
                spec->num_adcs = ARRAY_SIZE(vaio_adcs);
                spec->adc_nids = vaio_adcs;
+               spec->num_pwrs = 0;
                spec->input_mux = &vaio_mux;
                spec->mux_nids = vaio_mux_nids;
                codec->patch_ops = stac9872_vaio_patch_ops;
@@ -3045,6 +3977,7 @@ static int patch_stac9872(struct hda_codec *codec)
                spec->multiout.dac_nids = vaio_dacs;
                spec->multiout.hp_nid = VAIO_HP_DAC;
                spec->num_adcs = ARRAY_SIZE(vaio_adcs);
+               spec->num_pwrs = 0;
                spec->adc_nids = vaio_adcs;
                spec->input_mux = &vaio_mux;
                spec->mux_nids = vaio_mux_nids;
@@ -3104,5 +4037,17 @@ struct hda_codec_preset snd_hda_preset_sigmatel[] = {
        { .id = 0x838476a5, .name = "STAC9255D", .patch = patch_stac9205 },
        { .id = 0x838476a6, .name = "STAC9254", .patch = patch_stac9205 },
        { .id = 0x838476a7, .name = "STAC9254D", .patch = patch_stac9205 },
+       { .id = 0x111d7674, .name = "92HD73D1X5", .patch = patch_stac92hd73xx },
+       { .id = 0x111d7675, .name = "92HD73C1X5", .patch = patch_stac92hd73xx },
+       { .id = 0x111d7676, .name = "92HD73E1X5", .patch = patch_stac92hd73xx },
+       { .id = 0x111d7608, .name = "92HD71BXX", .patch = patch_stac92hd71bxx },
+       { .id = 0x111d76b0, .name = "92HD71B8X", .patch = patch_stac92hd71bxx },
+       { .id = 0x111d76b1, .name = "92HD71B8X", .patch = patch_stac92hd71bxx },
+       { .id = 0x111d76b2, .name = "92HD71B7X", .patch = patch_stac92hd71bxx },
+       { .id = 0x111d76b3, .name = "92HD71B7X", .patch = patch_stac92hd71bxx },
+       { .id = 0x111d76b4, .name = "92HD71B6X", .patch = patch_stac92hd71bxx },
+       { .id = 0x111d76b5, .name = "92HD71B6X", .patch = patch_stac92hd71bxx },
+       { .id = 0x111d76b6, .name = "92HD71B5X", .patch = patch_stac92hd71bxx },
+       { .id = 0x111d76b7, .name = "92HD71B5X", .patch = patch_stac92hd71bxx },
        {} /* terminator */
 };
index 4cdf3e6df4baf20fa4ce2570c7cda55ef72a3d21..4e5dd4cf36f5f6404852e9ef70a0fc7110fc5730 100644 (file)
 /* 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                        */
 /*                                                                           */
 /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
 
 
-#include <sound/driver.h>
 #include <linux/init.h>
 #include <linux/delay.h>
 #include <linux/slab.h>
 #define VT1708_HP_NID          0x13
 #define VT1708_DIGOUT_NID      0x14
 #define VT1708_DIGIN_NID       0x16
+#define VT1708_DIGIN_PIN       0x26
 
 #define VT1709_HP_DAC_NID      0x28
 #define VT1709_DIGOUT_NID      0x13
 #define VT1709_DIGIN_NID       0x17
+#define VT1709_DIGIN_PIN       0x25
+
+#define VT1708B_HP_NID         0x25
+#define VT1708B_DIGOUT_NID     0x12
+#define VT1708B_DIGIN_NID      0x15
+#define VT1708B_DIGIN_PIN      0x21
 
 #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)
 
 
 enum {
@@ -131,6 +141,11 @@ static hda_nid_t vt1709_adc_nids[3] = {
        0x14, 0x15, 0x16
 };
 
+static hda_nid_t vt1708B_adc_nids[2] = {
+       /* ADC1-2 */
+       0x13, 0x14
+};
+
 /* add dynamic controls */
 static int via_add_control(struct via_spec *spec, int type, const char *name,
                           unsigned long val)
@@ -268,9 +283,13 @@ static int via_mux_enum_put(struct snd_kcontrol *kcontrol,
                return snd_hda_input_mux_put(codec, spec->input_mux, ucontrol,
                                             0x18, &spec->cur_mux[adc_idx]);
        else if ((IS_VT1709_10CH_VENDORID(vendor_id) ||
-                 IS_VT1709_6CH_VENDORID(vendor_id)) && (adc_idx == 0) )
+                 IS_VT1709_6CH_VENDORID(vendor_id)) && adc_idx == 0)
                return snd_hda_input_mux_put(codec, spec->input_mux, ucontrol,
                                             0x19, &spec->cur_mux[adc_idx]);
+       else if ((IS_VT1708B_8CH_VENDORID(vendor_id) ||
+                 IS_VT1708B_4CH_VENDORID(vendor_id)) && adc_idx == 0)
+               return snd_hda_input_mux_put(codec, spec->input_mux, ucontrol,
+                                            0x17, &spec->cur_mux[adc_idx]);
        else
                return snd_hda_input_mux_put(codec, spec->input_mux, ucontrol,
                                             spec->adc_nids[adc_idx],
@@ -287,7 +306,6 @@ static struct snd_kcontrol_new vt1708_capture_mixer[] = {
                .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
                /* The multiple "Capture Source" controls confuse alsamixer
                 * So call somewhat different..
-                * FIXME: the controls appear in the "playback" view!
                 */
                /* .name = "Capture Source", */
                .name = "Input Source",
@@ -309,15 +327,15 @@ static struct hda_verb vt1708_volume_init_verbs[] = {
        {0x27, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
 
 
-       /* Mute input amps (CD, Line In, Mic 1 & Mic 2) of the analog-loopback
+       /* Unmute input amps (CD, Line In, Mic 1 & Mic 2) of the analog-loopback
         * mixer widget
         */
        /* Amp Indices: CD = 1, Mic1 = 2, Line = 3, Mic2 = 4 */
-       {0x17, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, /* master */
-       {0x17, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
-       {0x17, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(2)},
-       {0x17, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(3)},
-       {0x17, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(4)},
+       {0x17, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
+       {0x17, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
+       {0x17, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(2)},
+       {0x17, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(3)},
+       {0x17, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(4)},
 
        /*
         * Set up output mixers (0x19 - 0x1b)
@@ -329,10 +347,9 @@ static struct hda_verb vt1708_volume_init_verbs[] = {
        
        /* Setup default input to PW4 */
        {0x20, AC_VERB_SET_CONNECT_SEL, 0x1},
-       /* Set mic as default input of sw0 */
-       {0x18, AC_VERB_SET_CONNECT_SEL, 0x2},
        /* PW9 Output enable */
        {0x25, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x40},
+       { }
 };
 
 static int via_playback_pcm_open(struct hda_pcm_stream *hinfo,
@@ -544,6 +561,33 @@ static int via_init(struct hda_codec *codec)
 {
        struct via_spec *spec = codec->spec;
        snd_hda_sequence_write(codec, spec->init_verbs);
+       /* Lydia Add for EAPD enable */
+       if (!spec->dig_in_nid) { /* No Digital In connection */
+               if (IS_VT1708_VENDORID(codec->vendor_id)) {
+                       snd_hda_codec_write(codec, VT1708_DIGIN_PIN, 0,
+                                           AC_VERB_SET_PIN_WIDGET_CONTROL,
+                                           PIN_OUT);
+                       snd_hda_codec_write(codec, VT1708_DIGIN_PIN, 0,
+                                           AC_VERB_SET_EAPD_BTLENABLE, 0x02);
+               } else if (IS_VT1709_10CH_VENDORID(codec->vendor_id) ||
+                          IS_VT1709_6CH_VENDORID(codec->vendor_id)) {
+                       snd_hda_codec_write(codec, VT1709_DIGIN_PIN, 0,
+                                           AC_VERB_SET_PIN_WIDGET_CONTROL,
+                                           PIN_OUT);
+                       snd_hda_codec_write(codec, VT1709_DIGIN_PIN, 0,
+                                           AC_VERB_SET_EAPD_BTLENABLE, 0x02);
+               } else if (IS_VT1708B_8CH_VENDORID(codec->vendor_id) ||
+                          IS_VT1708B_4CH_VENDORID(codec->vendor_id)) {
+                       snd_hda_codec_write(codec, VT1708B_DIGIN_PIN, 0,
+                                           AC_VERB_SET_PIN_WIDGET_CONTROL,
+                                           PIN_OUT);
+                       snd_hda_codec_write(codec, VT1708B_DIGIN_PIN, 0,
+                                           AC_VERB_SET_EAPD_BTLENABLE, 0x02);
+               }
+       } else /* enable SPDIF-input pin */
+               snd_hda_codec_write(codec, spec->autocfg.dig_in_pin, 0,
+                                   AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN);
+
        return 0;
 }
 
@@ -623,58 +667,68 @@ static int vt1708_auto_create_multi_out_ctls(struct via_spec *spec,
                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));
+                                       "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));
+                                             HDA_COMPOSE_AMP_VAL(nid_vol, 2, 0,
+                                                                 HDA_OUTPUT));
                        if (err < 0)
                                return err;
                        err = via_add_control(spec, VIA_CTL_WIDGET_MUTE,
                                              "Center Playback Switch",
-                                             HDA_COMPOSE_AMP_VAL(nid_vol, 1, 0, HDA_OUTPUT));
+                                             HDA_COMPOSE_AMP_VAL(nid_vol, 1, 0,
+                                                                 HDA_OUTPUT));
                        if (err < 0)
                                return err;
                        err = via_add_control(spec, VIA_CTL_WIDGET_MUTE,
                                              "LFE Playback Switch",
-                                             HDA_COMPOSE_AMP_VAL(nid_vol, 2, 0, HDA_OUTPUT));
+                                             HDA_COMPOSE_AMP_VAL(nid_vol, 2, 0,
+                                                                 HDA_OUTPUT));
                        if (err < 0)
                                return err;
                } else if (i == AUTO_SEQ_FRONT){
                        /* add control to mixer index 0 */
                        err = via_add_control(spec, VIA_CTL_WIDGET_VOL,
                                              "Master Front Playback Volume",
-                                             HDA_COMPOSE_AMP_VAL(0x17, 3, 0, HDA_INPUT));
+                                             HDA_COMPOSE_AMP_VAL(0x17, 3, 0,
+                                                                 HDA_INPUT));
                        if (err < 0)
                                return err;
                        err = via_add_control(spec, VIA_CTL_WIDGET_MUTE,
                                              "Master Front Playback Switch",
-                                             HDA_COMPOSE_AMP_VAL(0x17, 3, 0, HDA_INPUT));
+                                             HDA_COMPOSE_AMP_VAL(0x17, 3, 0,
+                                                                 HDA_INPUT));
                        if (err < 0)
                                return err;
                        
                        /* add control to PW3 */
                        sprintf(name, "%s Playback Volume", chname[i]);
                        err = via_add_control(spec, VIA_CTL_WIDGET_VOL, name,
-                                             HDA_COMPOSE_AMP_VAL(nid, 3, 0, HDA_OUTPUT));
+                                             HDA_COMPOSE_AMP_VAL(nid, 3, 0,
+                                                                 HDA_OUTPUT));
                        if (err < 0)
                                return err;
                        sprintf(name, "%s Playback Switch", chname[i]);
                        err = via_add_control(spec, VIA_CTL_WIDGET_MUTE, name,
-                                             HDA_COMPOSE_AMP_VAL(nid, 3, 0, HDA_OUTPUT));
+                                             HDA_COMPOSE_AMP_VAL(nid, 3, 0,
+                                                                 HDA_OUTPUT));
                        if (err < 0)
                                return err;
                } else {
                        sprintf(name, "%s Playback Volume", chname[i]);
                        err = via_add_control(spec, VIA_CTL_WIDGET_VOL, name,
-                                             HDA_COMPOSE_AMP_VAL(nid_vol, 3, 0, HDA_OUTPUT));
+                                             HDA_COMPOSE_AMP_VAL(nid_vol, 3, 0,
+                                                                 HDA_OUTPUT));
                        if (err < 0)
                                return err;
                        sprintf(name, "%s Playback Switch", chname[i]);
                        err = via_add_control(spec, VIA_CTL_WIDGET_MUTE, name,
-                                             HDA_COMPOSE_AMP_VAL(nid_vol, 3, 0, HDA_OUTPUT));
+                                             HDA_COMPOSE_AMP_VAL(nid_vol, 3, 0,
+                                                                 HDA_OUTPUT));
                        if (err < 0)
                                return err;
                }
@@ -875,7 +929,6 @@ static struct snd_kcontrol_new vt1709_capture_mixer[] = {
                .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
                /* The multiple "Capture Source" controls confuse alsamixer
                 * So call somewhat different..
-                * FIXME: the controls appear in the "playback" view!
                 */
                /* .name = "Capture Source", */
                .name = "Input Source",
@@ -899,15 +952,15 @@ static struct hda_verb vt1709_10ch_volume_init_verbs[] = {
        {0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
 
 
-       /* Mute input amps (CD, Line In, Mic 1 & Mic 2) of the analog-loopback
+       /* Unmute input amps (CD, Line In, Mic 1 & Mic 2) of the analog-loopback
         * mixer widget
         */
        /* Amp Indices: AOW0=0, CD = 1, Mic1 = 2, Line = 3, Mic2 = 4 */
-       {0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, /* unmute master */
-       {0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
-       {0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(2)},
-       {0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(3)},
-       {0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(4)},
+       {0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
+       {0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
+       {0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(2)},
+       {0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(3)},
+       {0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(4)},
 
        /*
         * Set up output selector (0x1a, 0x1b, 0x29)
@@ -925,8 +978,6 @@ static struct hda_verb vt1709_10ch_volume_init_verbs[] = {
 
        /* Set input of PW4 as AOW4 */
        {0x20, AC_VERB_SET_CONNECT_SEL, 0x1},
-       /* Set mic as default input of sw0 */
-       {0x19, AC_VERB_SET_CONNECT_SEL, 0x2},
        /* PW9 Output enable */
        {0x24, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x40},
        { }
@@ -1073,68 +1124,80 @@ static int vt1709_auto_create_multi_out_ctls(struct via_spec *spec,
                        /* Center/LFE */
                        err = via_add_control(spec, VIA_CTL_WIDGET_VOL,
                                              "Center Playback Volume",
-                                             HDA_COMPOSE_AMP_VAL(0x1b, 1, 0, HDA_OUTPUT));
+                                             HDA_COMPOSE_AMP_VAL(0x1b, 1, 0,
+                                                                 HDA_OUTPUT));
                        if (err < 0)
                                return err;
                        err = via_add_control(spec, VIA_CTL_WIDGET_VOL,
                                              "LFE Playback Volume",
-                                             HDA_COMPOSE_AMP_VAL(0x1b, 2, 0, HDA_OUTPUT));
+                                             HDA_COMPOSE_AMP_VAL(0x1b, 2, 0,
+                                                                 HDA_OUTPUT));
                        if (err < 0)
                                return err;
                        err = via_add_control(spec, VIA_CTL_WIDGET_MUTE,
                                              "Center Playback Switch",
-                                             HDA_COMPOSE_AMP_VAL(0x1b, 1, 0, HDA_OUTPUT));
+                                             HDA_COMPOSE_AMP_VAL(0x1b, 1, 0,
+                                                                 HDA_OUTPUT));
                        if (err < 0)
                                return err;
                        err = via_add_control(spec, VIA_CTL_WIDGET_MUTE,
                                              "LFE Playback Switch",
-                                             HDA_COMPOSE_AMP_VAL(0x1b, 2, 0, HDA_OUTPUT));
+                                             HDA_COMPOSE_AMP_VAL(0x1b, 2, 0,
+                                                                 HDA_OUTPUT));
                        if (err < 0)
                                return err;
                } else if (i == AUTO_SEQ_FRONT){
                        /* add control to mixer index 0 */
                        err = via_add_control(spec, VIA_CTL_WIDGET_VOL,
                                              "Master Front Playback Volume",
-                                             HDA_COMPOSE_AMP_VAL(0x18, 3, 0, HDA_INPUT));
+                                             HDA_COMPOSE_AMP_VAL(0x18, 3, 0,
+                                                                 HDA_INPUT));
                        if (err < 0)
                                return err;
                        err = via_add_control(spec, VIA_CTL_WIDGET_MUTE,
                                              "Master Front Playback Switch",
-                                             HDA_COMPOSE_AMP_VAL(0x18, 3, 0, HDA_INPUT));
+                                             HDA_COMPOSE_AMP_VAL(0x18, 3, 0,
+                                                                 HDA_INPUT));
                        if (err < 0)
                                return err;
                        
                        /* add control to PW3 */
                        sprintf(name, "%s Playback Volume", chname[i]);
                        err = via_add_control(spec, VIA_CTL_WIDGET_VOL, name,
-                                             HDA_COMPOSE_AMP_VAL(nid, 3, 0, HDA_OUTPUT));
+                                             HDA_COMPOSE_AMP_VAL(nid, 3, 0,
+                                                                 HDA_OUTPUT));
                        if (err < 0)
                                return err;
                        sprintf(name, "%s Playback Switch", chname[i]);
                        err = via_add_control(spec, VIA_CTL_WIDGET_MUTE, name,
-                                             HDA_COMPOSE_AMP_VAL(nid, 3, 0, HDA_OUTPUT));
+                                             HDA_COMPOSE_AMP_VAL(nid, 3, 0,
+                                                                 HDA_OUTPUT));
                        if (err < 0)
                                return err;
                } else if (i == AUTO_SEQ_SURROUND) {
                        sprintf(name, "%s Playback Volume", chname[i]);
                        err = via_add_control(spec, VIA_CTL_WIDGET_VOL, name,
-                                             HDA_COMPOSE_AMP_VAL(0x29, 3, 0, HDA_OUTPUT));
+                                             HDA_COMPOSE_AMP_VAL(0x29, 3, 0,
+                                                                 HDA_OUTPUT));
                        if (err < 0)
                                return err;
                        sprintf(name, "%s Playback Switch", chname[i]);
                        err = via_add_control(spec, VIA_CTL_WIDGET_MUTE, name,
-                                             HDA_COMPOSE_AMP_VAL(0x29, 3, 0, HDA_OUTPUT));
+                                             HDA_COMPOSE_AMP_VAL(0x29, 3, 0,
+                                                                 HDA_OUTPUT));
                        if (err < 0)
                                return err;
                } else if (i == AUTO_SEQ_SIDE) {
                        sprintf(name, "%s Playback Volume", chname[i]);
                        err = via_add_control(spec, VIA_CTL_WIDGET_VOL, name,
-                                             HDA_COMPOSE_AMP_VAL(0x1a, 3, 0, HDA_OUTPUT));
+                                             HDA_COMPOSE_AMP_VAL(0x1a, 3, 0,
+                                                                 HDA_OUTPUT));
                        if (err < 0)
                                return err;
                        sprintf(name, "%s Playback Switch", chname[i]);
                        err = via_add_control(spec, VIA_CTL_WIDGET_MUTE, name,
-                                             HDA_COMPOSE_AMP_VAL(0x1a, 3, 0, HDA_OUTPUT));
+                                             HDA_COMPOSE_AMP_VAL(0x1a, 3, 0,
+                                                                 HDA_OUTPUT));
                        if (err < 0)
                                return err;
                }
@@ -1351,8 +1414,6 @@ static struct hda_verb vt1709_6ch_volume_init_verbs[] = {
 
        /* Set input of PW4 as MW0 */
        {0x20, AC_VERB_SET_CONNECT_SEL, 0},
-       /* Set mic as default input of sw0 */
-       {0x19, AC_VERB_SET_CONNECT_SEL, 0x2},
        /* PW9 Output enable */
        {0x24, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x40},
        { }
@@ -1403,6 +1464,494 @@ static int patch_vt1709_6ch(struct hda_codec *codec)
 #ifdef CONFIG_SND_HDA_POWER_SAVE
        spec->loopback.amplist = vt1709_loopbacks;
 #endif
+       return 0;
+}
+
+/* capture mixer elements */
+static struct snd_kcontrol_new vt1708B_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),
+       {
+               .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+               /* The multiple "Capture Source" controls confuse alsamixer
+                * So call somewhat different..
+                */
+               /* .name = "Capture Source", */
+               .name = "Input Source",
+               .count = 1,
+               .info = via_mux_enum_info,
+               .get = via_mux_enum_get,
+               .put = via_mux_enum_put,
+       },
+       { } /* end */
+};
+/*
+ * generic initialization of ADC, input mixers and output mixers
+ */
+static struct hda_verb vt1708B_8ch_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)},
+
+
+       /* Unmute input amps (CD, Line In, Mic 1 & Mic 2) of the analog-loopback
+        * mixer widget
+        */
+       /* Amp Indices: CD = 1, Mic1 = 2, Line = 3, Mic2 = 4 */
+       {0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
+       {0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
+       {0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(2)},
+       {0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(3)},
+       {0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(4)},
+
+       /*
+        * Set up output mixers
+        */
+       /* set vol=0 to output mixers */
+       {0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
+       {0x26, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
+       {0x27, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
+
+       /* Setup default input to PW4 */
+       {0x1d, AC_VERB_SET_CONNECT_SEL, 0x1},
+       /* PW9 Output enable */
+       {0x20, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x40},
+       /* PW10 Input enable */
+       {0x21, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x20},
+       { }
+};
+
+static struct hda_verb vt1708B_4ch_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)},
+
+
+       /* Unmute input amps (CD, Line In, Mic 1 & Mic 2) of the analog-loopback
+        * mixer widget
+        */
+       /* Amp Indices: CD = 1, Mic1 = 2, Line = 3, Mic2 = 4 */
+       {0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
+       {0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
+       {0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(2)},
+       {0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(3)},
+       {0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(4)},
+
+       /*
+        * Set up output mixers
+        */
+       /* set vol=0 to output mixers */
+       {0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
+       {0x26, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
+       {0x27, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
+
+       /* Setup default input of PW4 to MW0 */
+       {0x1d, AC_VERB_SET_CONNECT_SEL, 0x0},
+       /* PW9 Output enable */
+       {0x20, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x40},
+       /* PW10 Input enable */
+       {0x21, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x20},
+       { }
+};
+
+static struct hda_pcm_stream vt1708B_8ch_pcm_analog_playback = {
+       .substreams = 1,
+       .channels_min = 2,
+       .channels_max = 8,
+       .nid = 0x10, /* NID to query formats and rates */
+       .ops = {
+               .open = via_playback_pcm_open,
+               .prepare = via_playback_pcm_prepare,
+               .cleanup = via_playback_pcm_cleanup
+       },
+};
+
+static struct hda_pcm_stream vt1708B_4ch_pcm_analog_playback = {
+       .substreams = 1,
+       .channels_min = 2,
+       .channels_max = 4,
+       .nid = 0x10, /* NID to query formats and rates */
+       .ops = {
+               .open = via_playback_pcm_open,
+               .prepare = via_playback_pcm_prepare,
+               .cleanup = via_playback_pcm_cleanup
+       },
+};
+
+static struct hda_pcm_stream vt1708B_pcm_analog_capture = {
+       .substreams = 2,
+       .channels_min = 2,
+       .channels_max = 2,
+       .nid = 0x13, /* NID to query formats and rates */
+       .ops = {
+               .prepare = via_capture_pcm_prepare,
+               .cleanup = via_capture_pcm_cleanup
+       },
+};
+
+static struct hda_pcm_stream vt1708B_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
+       },
+};
+
+static struct hda_pcm_stream vt1708B_pcm_digital_capture = {
+       .substreams = 1,
+       .channels_min = 2,
+       .channels_max = 2,
+};
+
+/* fill in the dac_nids table from the parsed pin configuration */
+static int vt1708B_auto_fill_dac_nids(struct via_spec *spec,
+                                    const struct auto_pin_cfg *cfg)
+{
+       int i;
+       hda_nid_t nid;
+
+       spec->multiout.num_dacs = cfg->line_outs;
+
+       spec->multiout.dac_nids = spec->private_dac_nids;
+
+       for (i = 0; i < 4; i++) {
+               nid = cfg->line_out_pins[i];
+               if (nid) {
+                       /* config dac list */
+                       switch (i) {
+                       case AUTO_SEQ_FRONT:
+                               spec->multiout.dac_nids[i] = 0x10;
+                               break;
+                       case AUTO_SEQ_CENLFE:
+                               spec->multiout.dac_nids[i] = 0x24;
+                               break;
+                       case AUTO_SEQ_SURROUND:
+                               spec->multiout.dac_nids[i] = 0x25;
+                               break;
+                       case AUTO_SEQ_SIDE:
+                               spec->multiout.dac_nids[i] = 0x11;
+                               break;
+                       }
+               }
+       }
+
+       return 0;
+}
+
+/* add playback controls from the parsed DAC table */
+static int vt1708B_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[] = {0x16, 0x27, 0x26, 0x18};
+       hda_nid_t nid, nid_vol = 0;
+       int i, err;
+
+       for (i = 0; i <= AUTO_SEQ_SIDE; i++) {
+               nid = cfg->line_out_pins[i];
+
+               if (!nid)
+                       continue;
+
+               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(nid_vol, 1, 0,
+                                                                 HDA_OUTPUT));
+                       if (err < 0)
+                               return err;
+                       err = via_add_control(spec, VIA_CTL_WIDGET_VOL,
+                                             "LFE Playback Volume",
+                                             HDA_COMPOSE_AMP_VAL(nid_vol, 2, 0,
+                                                                 HDA_OUTPUT));
+                       if (err < 0)
+                               return err;
+                       err = via_add_control(spec, VIA_CTL_WIDGET_MUTE,
+                                             "Center Playback Switch",
+                                             HDA_COMPOSE_AMP_VAL(nid_vol, 1, 0,
+                                                                 HDA_OUTPUT));
+                       if (err < 0)
+                               return err;
+                       err = via_add_control(spec, VIA_CTL_WIDGET_MUTE,
+                                             "LFE Playback Switch",
+                                             HDA_COMPOSE_AMP_VAL(nid_vol, 2, 0,
+                                                                 HDA_OUTPUT));
+                       if (err < 0)
+                               return err;
+               } else if (i == AUTO_SEQ_FRONT) {
+                       /* add control to mixer index 0 */
+                       err = via_add_control(spec, VIA_CTL_WIDGET_VOL,
+                                             "Master Front Playback Volume",
+                                             HDA_COMPOSE_AMP_VAL(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(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,
+                                             HDA_COMPOSE_AMP_VAL(nid, 3, 0,
+                                                                 HDA_OUTPUT));
+                       if (err < 0)
+                               return err;
+                       sprintf(name, "%s Playback Switch", chname[i]);
+                       err = via_add_control(spec, VIA_CTL_WIDGET_MUTE, name,
+                                             HDA_COMPOSE_AMP_VAL(nid, 3, 0,
+                                                                 HDA_OUTPUT));
+                       if (err < 0)
+                               return err;
+               } else {
+                       sprintf(name, "%s Playback Volume", chname[i]);
+                       err = via_add_control(spec, VIA_CTL_WIDGET_VOL, name,
+                                             HDA_COMPOSE_AMP_VAL(nid_vol, 3, 0,
+                                                                 HDA_OUTPUT));
+                       if (err < 0)
+                               return err;
+                       sprintf(name, "%s Playback Switch", chname[i]);
+                       err = via_add_control(spec, VIA_CTL_WIDGET_MUTE, name,
+                                             HDA_COMPOSE_AMP_VAL(nid_vol, 3, 0,
+                                                                 HDA_OUTPUT));
+                       if (err < 0)
+                               return err;
+               }
+       }
+
+       return 0;
+}
+
+static int vt1708B_auto_create_hp_ctls(struct via_spec *spec, hda_nid_t pin)
+{
+       int err;
+
+       if (!pin)
+               return 0;
+
+       spec->multiout.hp_nid = VT1708B_HP_NID; /* AOW3 */
+
+       err = via_add_control(spec, VIA_CTL_WIDGET_VOL,
+                             "Headphone Playback Volume",
+                             HDA_COMPOSE_AMP_VAL(pin, 3, 0, HDA_OUTPUT));
+       if (err < 0)
+               return err;
+       err = via_add_control(spec, VIA_CTL_WIDGET_MUTE,
+                             "Headphone Playback Switch",
+                             HDA_COMPOSE_AMP_VAL(pin, 3, 0, HDA_OUTPUT));
+       if (err < 0)
+               return err;
+
+       return 0;
+}
+
+/* create playback/capture controls for input pins */
+static int vt1708B_auto_create_analog_input_ctls(struct via_spec *spec,
+                                               const struct auto_pin_cfg *cfg)
+{
+       static char *labels[] = {
+               "Mic", "Front Mic", "Line", "Front Line", "CD", "Aux", NULL
+       };
+       struct hda_input_mux *imux = &spec->private_imux;
+       int i, err, idx = 0;
+
+       /* for internal loopback recording select */
+       imux->items[imux->num_items].label = "Stereo Mixer";
+       imux->items[imux->num_items].index = idx;
+       imux->num_items++;
+
+       for (i = 0; i < AUTO_PIN_LAST; i++) {
+               if (!cfg->input_pins[i])
+                       continue;
+
+               switch (cfg->input_pins[i]) {
+               case 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, cfg->input_pins[i], labels[i],
+                                          idx, 0x16);
+               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 vt1708B_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 = vt1708B_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 = vt1708B_auto_create_multi_out_ctls(spec, &spec->autocfg);
+       if (err < 0)
+               return err;
+       err = vt1708B_auto_create_hp_ctls(spec, spec->autocfg.hp_pins[0]);
+       if (err < 0)
+               return err;
+       err = vt1708B_auto_create_analog_input_ctls(spec, &spec->autocfg);
+       if (err < 0)
+               return err;
+
+       spec->multiout.max_channels = spec->multiout.num_dacs * 2;
+
+       if (spec->autocfg.dig_out_pin)
+               spec->multiout.dig_out_nid = VT1708B_DIGOUT_NID;
+       if (spec->autocfg.dig_in_pin)
+               spec->dig_in_nid = VT1708B_DIGIN_NID;
+
+       if (spec->kctl_alloc)
+               spec->mixers[spec->num_mixers++] = spec->kctl_alloc;
+
+       spec->input_mux = &spec->private_imux;
+
+       return 1;
+}
+
+#ifdef CONFIG_SND_HDA_POWER_SAVE
+static struct hda_amp_list vt1708B_loopbacks[] = {
+       { 0x16, HDA_INPUT, 1 },
+       { 0x16, HDA_INPUT, 2 },
+       { 0x16, HDA_INPUT, 3 },
+       { 0x16, HDA_INPUT, 4 },
+       { } /* end */
+};
+#endif
+
+static int patch_vt1708B_8ch(struct hda_codec *codec)
+{
+       struct via_spec *spec;
+       int err;
+
+       /* create a codec specific record */
+       spec = kcalloc(1, sizeof(*spec), GFP_KERNEL);
+       if (spec == NULL)
+               return -ENOMEM;
+
+       codec->spec = spec;
+
+       /* automatic parse from the BIOS config */
+       err = vt1708B_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 = vt1708B_8ch_volume_init_verbs;
+
+       spec->stream_name_analog = "VT1708B Analog";
+       spec->stream_analog_playback = &vt1708B_8ch_pcm_analog_playback;
+       spec->stream_analog_capture = &vt1708B_pcm_analog_capture;
+
+       spec->stream_name_digital = "VT1708B Digital";
+       spec->stream_digital_playback = &vt1708B_pcm_digital_playback;
+       spec->stream_digital_capture = &vt1708B_pcm_digital_capture;
+
+       if (!spec->adc_nids && spec->input_mux) {
+               spec->adc_nids = vt1708B_adc_nids;
+               spec->num_adc_nids = ARRAY_SIZE(vt1708B_adc_nids);
+               spec->mixers[spec->num_mixers] = vt1708B_capture_mixer;
+               spec->num_mixers++;
+       }
+
+       codec->patch_ops = via_patch_ops;
+
+       codec->patch_ops.init = via_auto_init;
+#ifdef CONFIG_SND_HDA_POWER_SAVE
+       spec->loopback.amplist = vt1708B_loopbacks;
+#endif
+
+       return 0;
+}
+
+static int patch_vt1708B_4ch(struct hda_codec *codec)
+{
+       struct via_spec *spec;
+       int err;
+
+       /* create a codec specific record */
+       spec = kcalloc(1, sizeof(*spec), GFP_KERNEL);
+       if (spec == NULL)
+               return -ENOMEM;
+
+       codec->spec = spec;
+
+       /* automatic parse from the BIOS config */
+       err = vt1708B_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 = vt1708B_4ch_volume_init_verbs;
+
+       spec->stream_name_analog = "VT1708B Analog";
+       spec->stream_analog_playback = &vt1708B_4ch_pcm_analog_playback;
+       spec->stream_analog_capture = &vt1708B_pcm_analog_capture;
+
+       spec->stream_name_digital = "VT1708B Digital";
+       spec->stream_digital_playback = &vt1708B_pcm_digital_playback;
+       spec->stream_digital_capture = &vt1708B_pcm_digital_capture;
+
+       if (!spec->adc_nids && spec->input_mux) {
+               spec->adc_nids = vt1708B_adc_nids;
+               spec->num_adc_nids = ARRAY_SIZE(vt1708B_adc_nids);
+               spec->mixers[spec->num_mixers] = vt1708B_capture_mixer;
+               spec->num_mixers++;
+       }
+
+       codec->patch_ops = via_patch_ops;
+
+       codec->patch_ops.init = via_auto_init;
+#ifdef CONFIG_SND_HDA_POWER_SAVE
+       spec->loopback.amplist = vt1708B_loopbacks;
+#endif
 
        return 0;
 }
@@ -1415,13 +1964,37 @@ struct hda_codec_preset snd_hda_preset_via[] = {
        { .id = 0x11061709, .name = "VIA VT1708", .patch = patch_vt1708},
        { .id = 0x1106170A, .name = "VIA VT1708", .patch = patch_vt1708},
        { .id = 0x1106170B, .name = "VIA VT1708", .patch = patch_vt1708},
-       { .id = 0x1106E710, .name = "VIA VT1709 10-Ch", .patch = patch_vt1709_10ch},
-       { .id = 0x1106E711, .name = "VIA VT1709 10-Ch", .patch = patch_vt1709_10ch},
-       { .id = 0x1106E712, .name = "VIA VT1709 10-Ch", .patch = patch_vt1709_10ch},
-       { .id = 0x1106E713, .name = "VIA VT1709 10-Ch", .patch = patch_vt1709_10ch},
-       { .id = 0x1106E714, .name = "VIA VT1709 6-Ch", .patch = patch_vt1709_6ch},
-       { .id = 0x1106E715, .name = "VIA VT1709 6-Ch", .patch = patch_vt1709_6ch},
-       { .id = 0x1106E716, .name = "VIA VT1709 6-Ch", .patch = patch_vt1709_6ch},
-       { .id = 0x1106E717, .name = "VIA VT1709 6-Ch", .patch = patch_vt1709_6ch},
+       { .id = 0x1106E710, .name = "VIA VT1709 10-Ch",
+         .patch = patch_vt1709_10ch},
+       { .id = 0x1106E711, .name = "VIA VT1709 10-Ch",
+         .patch = patch_vt1709_10ch},
+       { .id = 0x1106E712, .name = "VIA VT1709 10-Ch",
+         .patch = patch_vt1709_10ch},
+       { .id = 0x1106E713, .name = "VIA VT1709 10-Ch",
+         .patch = patch_vt1709_10ch},
+       { .id = 0x1106E714, .name = "VIA VT1709 6-Ch",
+         .patch = patch_vt1709_6ch},
+       { .id = 0x1106E715, .name = "VIA VT1709 6-Ch",
+         .patch = patch_vt1709_6ch},
+       { .id = 0x1106E716, .name = "VIA VT1709 6-Ch",
+         .patch = patch_vt1709_6ch},
+       { .id = 0x1106E717, .name = "VIA VT1709 6-Ch",
+         .patch = patch_vt1709_6ch},
+       { .id = 0x1106E720, .name = "VIA VT1708B 8-Ch",
+         .patch = patch_vt1708B_8ch},
+       { .id = 0x1106E721, .name = "VIA VT1708B 8-Ch",
+         .patch = patch_vt1708B_8ch},
+       { .id = 0x1106E722, .name = "VIA VT1708B 8-Ch",
+         .patch = patch_vt1708B_8ch},
+       { .id = 0x1106E723, .name = "VIA VT1708B 8-Ch",
+         .patch = patch_vt1708B_8ch},
+       { .id = 0x1106E724, .name = "VIA VT1708B 4-Ch",
+         .patch = patch_vt1708B_4ch},
+       { .id = 0x1106E725, .name = "VIA VT1708B 4-Ch",
+         .patch = patch_vt1708B_4ch},
+       { .id = 0x1106E726, .name = "VIA VT1708B 4-Ch",
+         .patch = patch_vt1708B_4ch},
+       { .id = 0x1106E727, .name = "VIA VT1708B 4-Ch",
+         .patch = patch_vt1708B_4ch},
        {} /* terminator */
 };
diff --git a/sound/pci/hda/vmaster.c b/sound/pci/hda/vmaster.c
new file mode 100644 (file)
index 0000000..2da49d2
--- /dev/null
@@ -0,0 +1,364 @@
+/*
+ * Virtual master and slave controls
+ *
+ *  Copyright (c) 2008 by Takashi Iwai <tiwai@suse.de>
+ *
+ *  This program is free software; you can redistribute it and/or
+ *  modify it under the terms of the GNU General Public License as
+ *  published by the Free Software Foundation, version 2.
+ *
+ */
+
+#include <linux/slab.h>
+#include <sound/core.h>
+#include <sound/control.h>
+
+/*
+ * a subset of information returned via ctl info callback
+ */
+struct link_ctl_info {
+       int type;               /* value type */
+       int count;              /* item count */
+       int min_val, max_val;   /* min, max values */
+};
+
+/*
+ * link master - this contains a list of slave controls that are
+ * identical types, i.e. info returns the same value type and value
+ * ranges, but may have different number of counts.
+ *
+ * The master control is so far only mono volume/switch for simplicity.
+ * The same value will be applied to all slaves.
+ */
+struct link_master {
+       struct list_head slaves;
+       struct link_ctl_info info;
+       int val;                /* the master value */
+};
+
+/*
+ * link slave - this contains a slave control element
+ *
+ * It fakes the control callbacsk with additional attenuation by the
+ * master control.  A slave may have either one or two channels.
+ */
+
+struct link_slave {
+       struct list_head list;
+       struct link_master *master;
+       struct link_ctl_info info;
+       int vals[2];            /* current values */
+       struct snd_kcontrol slave; /* the copy of original control entry */
+};
+
+/* get the slave ctl info and save the initial values */
+static int slave_init(struct link_slave *slave)
+{
+       struct snd_ctl_elem_info *uinfo;
+       struct snd_ctl_elem_value *uctl;
+       int err, ch;
+
+       if (slave->info.count)
+               return 0; /* already initialized */
+
+       uinfo = kmalloc(sizeof(*uinfo), GFP_KERNEL);
+       if (!uinfo)
+               return -ENOMEM;
+       uinfo->id = slave->slave.id;
+       err = slave->slave.info(&slave->slave, uinfo);
+       if (err < 0) {
+               kfree(uinfo);
+               return err;
+       }
+       slave->info.type = uinfo->type;
+       slave->info.count = uinfo->count;
+       if (slave->info.count > 2  ||
+           (slave->info.type != SNDRV_CTL_ELEM_TYPE_INTEGER &&
+            slave->info.type != SNDRV_CTL_ELEM_TYPE_BOOLEAN)) {
+               snd_printk(KERN_ERR "invalid slave element\n");
+               kfree(uinfo);
+               return -EINVAL;
+       }
+       slave->info.min_val = uinfo->value.integer.min;
+       slave->info.max_val = uinfo->value.integer.max;
+       kfree(uinfo);
+
+       uctl = kmalloc(sizeof(*uctl), GFP_KERNEL);
+       if (!uctl)
+               return -ENOMEM;
+       uctl->id = slave->slave.id;
+       err = slave->slave.get(&slave->slave, uctl);
+       for (ch = 0; ch < slave->info.count; ch++)
+               slave->vals[ch] = uctl->value.integer.value[ch];
+       kfree(uctl);
+       return 0;
+}
+
+/* initialize master volume */
+static int master_init(struct link_master *master)
+{
+       struct link_slave *slave;
+
+       if (master->info.count)
+               return 0; /* already initialized */
+
+       list_for_each_entry(slave, &master->slaves, list) {
+               int err = slave_init(slave);
+               if (err < 0)
+                       return err;
+               master->info = slave->info;
+               master->info.count = 1; /* always mono */
+               /* set full volume as default (= no attenuation) */
+               master->val = master->info.max_val;
+               return 0;
+       }
+       return -ENOENT;
+}
+
+static int slave_get_val(struct link_slave *slave,
+                        struct snd_ctl_elem_value *ucontrol)
+{
+       int err, ch;
+
+       err = slave_init(slave);
+       if (err < 0)
+               return err;
+       for (ch = 0; ch < slave->info.count; ch++)
+               ucontrol->value.integer.value[ch] = slave->vals[ch];
+       return 0;
+}
+
+static int slave_put_val(struct link_slave *slave,
+                        struct snd_ctl_elem_value *ucontrol)
+{
+       int err, ch, vol;
+
+       err = master_init(slave->master);
+       if (err < 0)
+               return err;
+
+       switch (slave->info.type) {
+       case SNDRV_CTL_ELEM_TYPE_BOOLEAN:
+               for (ch = 0; ch < slave->info.count; ch++)
+                       ucontrol->value.integer.value[ch] &=
+                               !!slave->master->val;
+               break;
+       case SNDRV_CTL_ELEM_TYPE_INTEGER:
+               for (ch = 0; ch < slave->info.count; ch++) {
+                       /* max master volume is supposed to be 0 dB */
+                       vol = ucontrol->value.integer.value[ch];
+                       vol += slave->master->val - slave->master->info.max_val;
+                       if (vol < slave->info.min_val)
+                               vol = slave->info.min_val;
+                       else if (vol > slave->info.max_val)
+                               vol = slave->info.max_val;
+                       ucontrol->value.integer.value[ch] = vol;
+               }
+               break;
+       }
+       return slave->slave.put(&slave->slave, ucontrol);
+}
+
+/*
+ * ctl callbacks for slaves
+ */
+static int slave_info(struct snd_kcontrol *kcontrol,
+                     struct snd_ctl_elem_info *uinfo)
+{
+       struct link_slave *slave = snd_kcontrol_chip(kcontrol);
+       return slave->slave.info(&slave->slave, uinfo);
+}
+
+static int slave_get(struct snd_kcontrol *kcontrol,
+                    struct snd_ctl_elem_value *ucontrol)
+{
+       struct link_slave *slave = snd_kcontrol_chip(kcontrol);
+       return slave_get_val(slave, ucontrol);
+}
+
+static int slave_put(struct snd_kcontrol *kcontrol,
+                    struct snd_ctl_elem_value *ucontrol)
+{
+       struct link_slave *slave = snd_kcontrol_chip(kcontrol);
+       int err, ch, changed = 0;
+
+       err = slave_init(slave);
+       if (err < 0)
+               return err;
+       for (ch = 0; ch < slave->info.count; ch++) {
+               if (slave->vals[ch] != ucontrol->value.integer.value[ch]) {
+                       changed = 1;
+                       slave->vals[ch] = ucontrol->value.integer.value[ch];
+               }
+       }
+       if (!changed)
+               return 0;
+       return slave_put_val(slave, ucontrol);
+}
+
+static int slave_tlv_cmd(struct snd_kcontrol *kcontrol,
+                        int op_flag, unsigned int size,
+                        unsigned int __user *tlv)
+{
+       struct link_slave *slave = snd_kcontrol_chip(kcontrol);
+       /* FIXME: this assumes that the max volume is 0 dB */
+       return slave->slave.tlv.c(&slave->slave, op_flag, size, tlv);
+}
+
+static void slave_free(struct snd_kcontrol *kcontrol)
+{
+       struct link_slave *slave = snd_kcontrol_chip(kcontrol);
+       if (slave->slave.private_free)
+               slave->slave.private_free(&slave->slave);
+       if (slave->master)
+               list_del(&slave->list);
+       kfree(slave);
+}
+
+/*
+ * Add a slave control to the group with the given master control
+ *
+ * All slaves must be the same type (returning the same information
+ * via info callback).  The fucntion doesn't check it, so it's your
+ * responsibility.
+ *
+ * Also, some additional limitations:
+ * - at most two channels
+ * - logarithmic volume control (dB level), no linear volume
+ * - master can only attenuate the volume, no gain
+ */
+int snd_ctl_add_slave(struct snd_kcontrol *master, struct snd_kcontrol *slave)
+{
+       struct link_master *master_link = snd_kcontrol_chip(master);
+       struct link_slave *srec;
+
+       srec = kzalloc(sizeof(*srec) +
+                      slave->count * sizeof(*slave->vd), GFP_KERNEL);
+       if (!srec)
+               return -ENOMEM;
+       srec->slave = *slave;
+       memcpy(srec->slave.vd, slave->vd, slave->count * sizeof(*slave->vd));
+       srec->master = master_link;
+
+       /* override callbacks */
+       slave->info = slave_info;
+       slave->get = slave_get;
+       slave->put = slave_put;
+       if (slave->vd[0].access & SNDRV_CTL_ELEM_ACCESS_TLV_CALLBACK)
+               slave->tlv.c = slave_tlv_cmd;
+       slave->private_data = srec;
+       slave->private_free = slave_free;
+
+       list_add_tail(&srec->list, &master_link->slaves);
+       return 0;
+}
+
+/*
+ * ctl callbacks for master controls
+ */
+static int master_info(struct snd_kcontrol *kcontrol,
+                     struct snd_ctl_elem_info *uinfo)
+{
+       struct link_master *master = snd_kcontrol_chip(kcontrol);
+       int ret;
+
+       ret = master_init(master);
+       if (ret < 0)
+               return ret;
+       uinfo->type = master->info.type;
+       uinfo->count = master->info.count;
+       uinfo->value.integer.min = master->info.min_val;
+       uinfo->value.integer.max = master->info.max_val;
+       return 0;
+}
+
+static int master_get(struct snd_kcontrol *kcontrol,
+                     struct snd_ctl_elem_value *ucontrol)
+{
+       struct link_master *master = snd_kcontrol_chip(kcontrol);
+       int err = master_init(master);
+       if (err < 0)
+               return err;
+       ucontrol->value.integer.value[0] = master->val;
+       return 0;
+}
+
+static int master_put(struct snd_kcontrol *kcontrol,
+                     struct snd_ctl_elem_value *ucontrol)
+{
+       struct link_master *master = snd_kcontrol_chip(kcontrol);
+       struct link_slave *slave;
+       struct snd_ctl_elem_value *uval;
+       int err, old_val;
+
+       err = master_init(master);
+       if (err < 0)
+               return err;
+       old_val = master->val;
+       if (ucontrol->value.integer.value[0] == old_val)
+               return 0;
+
+       uval = kmalloc(sizeof(*uval), GFP_KERNEL);
+       if (!uval)
+               return -ENOMEM;
+       list_for_each_entry(slave, &master->slaves, list) {
+               master->val = old_val;
+               uval->id = slave->slave.id;
+               slave_get_val(slave, uval);
+               master->val = ucontrol->value.integer.value[0];
+               slave_put_val(slave, uval);
+       }
+       kfree(uval);
+       return 1;
+}
+
+static void master_free(struct snd_kcontrol *kcontrol)
+{
+       struct link_master *master = snd_kcontrol_chip(kcontrol);
+       struct link_slave *slave;
+
+       list_for_each_entry(slave, &master->slaves, list)
+               slave->master = NULL;
+       kfree(master);
+}
+
+
+/*
+ * Create a virtual master control with the given name
+ */
+struct snd_kcontrol *snd_ctl_make_virtual_master(char *name,
+                                                const unsigned int *tlv)
+{
+       struct link_master *master;
+       struct snd_kcontrol *kctl;
+       struct snd_kcontrol_new knew;
+
+       memset(&knew, 0, sizeof(knew));
+       knew.iface = SNDRV_CTL_ELEM_IFACE_MIXER;
+       knew.name = name;
+       knew.info = master_info;
+
+       master = kzalloc(sizeof(*master), GFP_KERNEL);
+       if (!master)
+               return NULL;
+       INIT_LIST_HEAD(&master->slaves);
+
+       kctl = snd_ctl_new1(&knew, master);
+       if (!kctl) {
+               kfree(master);
+               return NULL;
+       }
+       /* override some callbacks */
+       kctl->info = master_info;
+       kctl->get = master_get;
+       kctl->put = master_put;
+       kctl->private_free = master_free;
+
+       /* additional (constant) TLV read */
+       if (tlv) {
+               /* FIXME: this assumes that the max volume is 0 dB */
+               kctl->vd[0].access |= SNDRV_CTL_ELEM_ACCESS_TLV_READ;
+               kctl->tlv.p = tlv;
+       }
+       return kctl;
+}
index 65ce66adba5ad1251ce1ba68f3b9a53ebac48306..f99fe089495d716ae3c2d05a73903790d4b57fbc 100644 (file)
@@ -5,7 +5,7 @@
 
 snd-ice17xx-ak4xxx-objs := ak4xxx.o
 snd-ice1712-objs := ice1712.o delta.o hoontech.o ews.o
-snd-ice1724-objs := ice1724.o amp.o revo.o aureon.o vt1720_mobo.o pontis.o prodigy192.o juli.o phase.o wtm.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
 
 # Toplevel Module Dependency
 obj-$(CONFIG_SND_ICE1712) += snd-ice1712.o snd-ice17xx-ak4xxx.o
index a1aba0d7d0e4aeda2da187631350e71c106add0d..dab31b2756a608b2e52f60c31058e4e4f19cd306 100644 (file)
@@ -21,7 +21,6 @@
  *
  */      
 
-#include <sound/driver.h>
 #include <asm/io.h>
 #include <linux/delay.h>
 #include <linux/interrupt.h>
index 6e13d758bb5d1ce1b8e3424628b61c62229e593c..37564300b50d84d00b0bfb97a9225cb8646d77d5 100644 (file)
@@ -21,7 +21,6 @@
  *
  */      
 
-#include <sound/driver.h>
 #include <asm/io.h>
 #include <linux/delay.h>
 #include <linux/interrupt.h>
index ec0699c89952f9c0d5b9ecc62163afd6483a39eb..868ae291b960fd32f6054b322cdca8cd3bfad326 100644 (file)
@@ -47,7 +47,6 @@
  *
  */      
 
-#include <sound/driver.h>
 #include <asm/io.h>
 #include <linux/delay.h>
 #include <linux/interrupt.h>
 #include "aureon.h"
 #include <sound/tlv.h>
 
+/* AC97 register cache for Aureon */
+struct aureon_spec {
+       unsigned short stac9744[64];
+       unsigned int cs8415_mux;
+       unsigned short master[2];
+       unsigned short vol[8];
+       unsigned char pca9554_out;
+};
+
 /* WM8770 registers */
 #define WM_DAC_ATTEN           0x00    /* DAC1-8 analog attenuation */
 #define WM_DAC_MASTER_ATTEN    0x08    /* DAC master analog attenuation */
@@ -205,7 +213,8 @@ static int aureon_universe_inmux_get(struct snd_kcontrol *kcontrol,
                                     struct snd_ctl_elem_value *ucontrol)
 {
        struct snd_ice1712 *ice = snd_kcontrol_chip(kcontrol);
-       ucontrol->value.integer.value[0] = ice->spec.aureon.pca9554_out;
+       struct aureon_spec *spec = ice->spec;
+       ucontrol->value.enumerated.item[0] = spec->pca9554_out;
        return 0;
 }
 
@@ -213,16 +222,18 @@ static int aureon_universe_inmux_put(struct snd_kcontrol *kcontrol,
                                     struct snd_ctl_elem_value *ucontrol)
 {
        struct snd_ice1712 *ice = snd_kcontrol_chip(kcontrol);
+       struct aureon_spec *spec = ice->spec;
        unsigned char oval, nval;
        int change;
 
+       nval = ucontrol->value.enumerated.item[0];
+       if (nval >= 3)
+               return -EINVAL;
        snd_ice1712_save_gpio_status(ice);
-       
-       oval = ice->spec.aureon.pca9554_out;
-       nval = ucontrol->value.integer.value[0];
+       oval = spec->pca9554_out;
        if ((change = (oval != nval))) {
                aureon_pca9554_write(ice, PCA9554_OUT, nval);
-               ice->spec.aureon.pca9554_out = nval;
+               spec->pca9554_out = nval;
        }
        snd_ice1712_restore_gpio_status(ice);
        
@@ -233,6 +244,7 @@ static int aureon_universe_inmux_put(struct snd_kcontrol *kcontrol,
 static void aureon_ac97_write(struct snd_ice1712 *ice, unsigned short reg,
                              unsigned short val)
 {
+       struct aureon_spec *spec = ice->spec;
        unsigned int tmp;
 
        /* Send address to XILINX chip */
@@ -280,12 +292,13 @@ static void aureon_ac97_write(struct snd_ice1712 *ice, unsigned short reg,
        udelay(10);
        
        /* Store the data in out private buffer */
-       ice->spec.aureon.stac9744[(reg & 0x7F) >> 1] = val;
+       spec->stac9744[(reg & 0x7F) >> 1] = val;
 }
 
 static unsigned short aureon_ac97_read(struct snd_ice1712 *ice, unsigned short reg)
 {
-       return ice->spec.aureon.stac9744[(reg & 0x7F) >> 1];
+       struct aureon_spec *spec = ice->spec;
+       return spec->stac9744[(reg & 0x7F) >> 1];
 }
 
 /*
@@ -293,6 +306,7 @@ static unsigned short aureon_ac97_read(struct snd_ice1712 *ice, unsigned short r
  */
 static int aureon_ac97_init (struct snd_ice1712 *ice)
 {
+       struct aureon_spec *spec = ice->spec;
        int i;
        static const unsigned short ac97_defaults[] = {
                0x00, 0x9640,
@@ -330,9 +344,9 @@ static int aureon_ac97_init (struct snd_ice1712 *ice)
        snd_ice1712_gpio_write(ice, tmp);
        udelay(3);
        
-       memset(&ice->spec.aureon.stac9744, 0, sizeof(ice->spec.aureon.stac9744));
+       memset(&spec->stac9744, 0, sizeof(spec->stac9744));
        for (i=0; ac97_defaults[i] != (unsigned short)-1; i+=2)
-               ice->spec.aureon.stac9744[(ac97_defaults[i]) >> 1] = ac97_defaults[i+1];
+               spec->stac9744[(ac97_defaults[i]) >> 1] = ac97_defaults[i+1];
                
        aureon_ac97_write(ice, AC97_MASTER, 0x0000); // Unmute AC'97 master volume permanently - muting is done by WM8770
 
@@ -744,27 +758,33 @@ static int wm_master_vol_info(struct snd_kcontrol *kcontrol, struct snd_ctl_elem
 static int wm_master_vol_get(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
 {
        struct snd_ice1712 *ice = snd_kcontrol_chip(kcontrol);
+       struct aureon_spec *spec = ice->spec;
        int i;
        for (i=0; i<2; i++)
-               ucontrol->value.integer.value[i] = ice->spec.aureon.master[i] & ~WM_VOL_MUTE;
+               ucontrol->value.integer.value[i] =
+                       spec->master[i] & ~WM_VOL_MUTE;
        return 0;
 }
 
 static int wm_master_vol_put(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
 {
        struct snd_ice1712 *ice = snd_kcontrol_chip(kcontrol);
+       struct aureon_spec *spec = ice->spec;
        int ch, change = 0;
 
        snd_ice1712_save_gpio_status(ice);
        for (ch = 0; ch < 2; ch++) {
-               if (ucontrol->value.integer.value[ch] != ice->spec.aureon.master[ch]) {
+               unsigned int vol = ucontrol->value.integer.value[ch];
+               if (vol > WM_VOL_MAX)
+                       continue;
+               vol |= spec->master[ch] & WM_VOL_MUTE;
+               if (vol != spec->master[ch]) {
                        int dac;
-                       ice->spec.aureon.master[ch] &= WM_VOL_MUTE;
-                       ice->spec.aureon.master[ch] |= ucontrol->value.integer.value[ch];
+                       spec->master[ch] = vol;
                        for (dac = 0; dac < ice->num_total_dacs; dac += 2)
                                wm_set_vol(ice, WM_DAC_ATTEN + dac + ch,
-                                          ice->spec.aureon.vol[dac + ch],
-                                          ice->spec.aureon.master[ch]);
+                                          spec->vol[dac + ch],
+                                          spec->master[ch]);
                        change = 1;
                }
        }
@@ -788,18 +808,21 @@ static int wm_vol_info(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *
 static int wm_vol_get(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
 {
        struct snd_ice1712 *ice = snd_kcontrol_chip(kcontrol);
+       struct aureon_spec *spec = ice->spec;
        int i, ofs, voices;
 
        voices = kcontrol->private_value >> 8;
        ofs = kcontrol->private_value & 0xff;
        for (i = 0; i < voices; i++)
-               ucontrol->value.integer.value[i] = ice->spec.aureon.vol[ofs+i] & ~WM_VOL_MUTE;
+               ucontrol->value.integer.value[i] =
+                       spec->vol[ofs+i] & ~WM_VOL_MUTE;
        return 0;
 }
 
 static int wm_vol_put(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
 {
        struct snd_ice1712 *ice = snd_kcontrol_chip(kcontrol);
+       struct aureon_spec *spec = ice->spec;
        int i, idx, ofs, voices;
        int change = 0;
 
@@ -807,12 +830,15 @@ static int wm_vol_put(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *
        ofs = kcontrol->private_value & 0xff;
        snd_ice1712_save_gpio_status(ice);
        for (i = 0; i < voices; i++) {
-               idx  = WM_DAC_ATTEN + ofs + i;
-               if (ucontrol->value.integer.value[i] != ice->spec.aureon.vol[ofs+i]) {
-                       ice->spec.aureon.vol[ofs+i] &= WM_VOL_MUTE;
-                       ice->spec.aureon.vol[ofs+i] |= ucontrol->value.integer.value[i];
-                       wm_set_vol(ice, idx, ice->spec.aureon.vol[ofs+i],
-                                  ice->spec.aureon.master[i]);
+               unsigned int vol = ucontrol->value.integer.value[i];
+               if (vol > 0x7f)
+                       continue;
+               vol |= spec->vol[ofs+i];
+               if (vol != spec->vol[ofs+i]) {
+                       spec->vol[ofs+i] = vol;
+                       idx  = WM_DAC_ATTEN + ofs + i;
+                       wm_set_vol(ice, idx, spec->vol[ofs + i],
+                                  spec->master[i]);
                        change = 1;
                }
        }
@@ -834,19 +860,22 @@ static int wm_mute_info(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info
 static int wm_mute_get(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
 {
        struct snd_ice1712 *ice = snd_kcontrol_chip(kcontrol);
+       struct aureon_spec *spec = ice->spec;
        int voices, ofs, i;
        
        voices = kcontrol->private_value >> 8;
        ofs = kcontrol->private_value & 0xFF;
 
        for (i = 0; i < voices; i++)
-               ucontrol->value.integer.value[i] = (ice->spec.aureon.vol[ofs+i] & WM_VOL_MUTE) ? 0 : 1;
+               ucontrol->value.integer.value[i] =
+                       (spec->vol[ofs + i] & WM_VOL_MUTE) ? 0 : 1;
        return 0;
 }
 
 static int wm_mute_put(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
 {
        struct snd_ice1712 *ice = snd_kcontrol_chip(kcontrol);
+       struct aureon_spec *spec = ice->spec;
        int change = 0, voices, ofs, i;
 
        voices = kcontrol->private_value >> 8;
@@ -854,13 +883,13 @@ static int wm_mute_put(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value
 
        snd_ice1712_save_gpio_status(ice);
        for (i = 0; i < voices; i++) {
-               int val = (ice->spec.aureon.vol[ofs + i] & WM_VOL_MUTE) ? 0 : 1;
+               int val = (spec->vol[ofs + i] & WM_VOL_MUTE) ? 0 : 1;
                if (ucontrol->value.integer.value[i] != val) {
-                       ice->spec.aureon.vol[ofs + i] &= ~WM_VOL_MUTE;
-                       ice->spec.aureon.vol[ofs + i] |=
+                       spec->vol[ofs + i] &= ~WM_VOL_MUTE;
+                       spec->vol[ofs + i] |=
                                ucontrol->value.integer.value[i] ? 0 : WM_VOL_MUTE;
-                       wm_set_vol(ice, ofs + i, ice->spec.aureon.vol[ofs + i],
-                                  ice->spec.aureon.master[i]);
+                       wm_set_vol(ice, ofs + i, spec->vol[ofs + i],
+                                  spec->master[i]);
                        change = 1;
                }
        }
@@ -877,29 +906,33 @@ static int wm_mute_put(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value
 static int wm_master_mute_get(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
 {
        struct snd_ice1712 *ice = snd_kcontrol_chip(kcontrol);
+       struct aureon_spec *spec = ice->spec;
        
-       ucontrol->value.integer.value[0] = (ice->spec.aureon.master[0] & WM_VOL_MUTE) ? 0 : 1;
-       ucontrol->value.integer.value[1] = (ice->spec.aureon.master[1] & WM_VOL_MUTE) ? 0 : 1;
+       ucontrol->value.integer.value[0] =
+               (spec->master[0] & WM_VOL_MUTE) ? 0 : 1;
+       ucontrol->value.integer.value[1] =
+               (spec->master[1] & WM_VOL_MUTE) ? 0 : 1;
        return 0;
 }
 
 static int wm_master_mute_put(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
 {
        struct snd_ice1712 *ice = snd_kcontrol_chip(kcontrol);
+       struct aureon_spec *spec = ice->spec;
        int change = 0, i;
 
        snd_ice1712_save_gpio_status(ice);
        for (i = 0; i < 2; i++) {
-               int val = (ice->spec.aureon.master[i] & WM_VOL_MUTE) ? 0 : 1;
+               int val = (spec->master[i] & WM_VOL_MUTE) ? 0 : 1;
                if (ucontrol->value.integer.value[i] != val) {
                        int dac;
-                       ice->spec.aureon.master[i] &= ~WM_VOL_MUTE;
-                       ice->spec.aureon.master[i] |=
+                       spec->master[i] &= ~WM_VOL_MUTE;
+                       spec->master[i] |=
                                ucontrol->value.integer.value[i] ? 0 : WM_VOL_MUTE;
                        for (dac = 0; dac < ice->num_total_dacs; dac += 2)
                                wm_set_vol(ice, WM_DAC_ATTEN + dac + i,
-                                          ice->spec.aureon.vol[dac + i],
-                                          ice->spec.aureon.master[i]);
+                                          spec->vol[dac + i],
+                                          spec->master[i]);
                        change = 1;
                }
        }
@@ -940,8 +973,10 @@ static int wm_pcm_vol_put(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_val
        unsigned short ovol, nvol;
        int change = 0;
 
-       snd_ice1712_save_gpio_status(ice);
        nvol = ucontrol->value.integer.value[0];
+       if (nvol > PCM_RES)
+               return -EINVAL;
+       snd_ice1712_save_gpio_status(ice);
        nvol = (nvol ? (nvol + PCM_MIN) : 0) & 0xff;
        ovol = wm_get(ice, WM_DAC_DIG_MASTER_ATTEN) & 0xff;
        if (ovol != nvol) {
@@ -1031,7 +1066,7 @@ static int wm_adc_vol_put(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_val
        snd_ice1712_save_gpio_status(ice);
        for (i = 0; i < 2; i++) {
                idx  = WM_ADC_GAIN + i;
-               nvol = ucontrol->value.integer.value[i];
+               nvol = ucontrol->value.integer.value[i] & 0x1f;
                ovol = wm_get(ice, idx);
                if ((ovol & 0x1f) != nvol) {
                        wm_put(ice, idx, nvol | (ovol & ~0x1f));
@@ -1143,10 +1178,11 @@ static int aureon_cs8415_mux_info(struct snd_kcontrol *kcontrol, struct snd_ctl_
 static int aureon_cs8415_mux_get(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
 {
        struct snd_ice1712 *ice = snd_kcontrol_chip(kcontrol);
+       struct aureon_spec *spec = ice->spec;
 
        //snd_ice1712_save_gpio_status(ice);
        //val = aureon_cs8415_get(ice, CS8415_CTRL2);
-       ucontrol->value.enumerated.item[0] = ice->spec.aureon.cs8415_mux;
+       ucontrol->value.enumerated.item[0] = spec->cs8415_mux;
        //snd_ice1712_restore_gpio_status(ice);
        return 0;
 }
@@ -1154,6 +1190,7 @@ static int aureon_cs8415_mux_get(struct snd_kcontrol *kcontrol, struct snd_ctl_e
 static int aureon_cs8415_mux_put(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
 {
        struct snd_ice1712 *ice = snd_kcontrol_chip(kcontrol);
+       struct aureon_spec *spec = ice->spec;
        unsigned short oval, nval;
        int change;
 
@@ -1165,7 +1202,7 @@ static int aureon_cs8415_mux_put(struct snd_kcontrol *kcontrol, struct snd_ctl_e
        if (change)
                aureon_cs8415_put(ice, CS8415_CTRL2, nval);
        snd_ice1712_restore_gpio_status(ice);
-       ice->spec.aureon.cs8415_mux = ucontrol->value.enumerated.item[0];
+       spec->cs8415_mux = ucontrol->value.enumerated.item[0];
        return change;
 }
 
@@ -2001,10 +2038,16 @@ static int __devinit aureon_init(struct snd_ice1712 *ice)
                0x0605, /* slave, 24bit, MSB on second OSCLK, SDOUT for right channel when OLRCK is high */
                (unsigned short)-1
        };
+       struct aureon_spec *spec;
        unsigned int tmp;
        const unsigned short *p;
        int err, i;
 
+       spec = kzalloc(sizeof(*spec), GFP_KERNEL);
+       if (!spec)
+               return -ENOMEM;
+       ice->spec = spec;
+
        if (ice->eeprom.subvendor == VT1724_SUBDEVICE_AUREON51_SKY) {
                ice->num_total_dacs = 6;
                ice->num_total_adcs = 2;
@@ -2055,7 +2098,7 @@ static int __devinit aureon_init(struct snd_ice1712 *ice)
            ice->eeprom.subvendor != VT1724_SUBDEVICE_PRODIGY71XT) {
                for (p = cs_inits; *p != (unsigned short)-1; p++)
                        aureon_spi_write(ice, AUREON_CS8415_CS, *p | 0x200000, 24);
-               ice->spec.aureon.cs8415_mux = 1;
+               spec->cs8415_mux = 1;
 
                aureon_set_headphone_amp(ice, 1);
        }
@@ -2066,11 +2109,11 @@ static int __devinit aureon_init(struct snd_ice1712 *ice)
        aureon_pca9554_write(ice, PCA9554_DIR, 0x00);
        aureon_pca9554_write(ice, PCA9554_OUT, 0x00);   /* internal AUX */
        
-       ice->spec.aureon.master[0] = WM_VOL_MUTE;
-       ice->spec.aureon.master[1] = WM_VOL_MUTE;
+       spec->master[0] = WM_VOL_MUTE;
+       spec->master[1] = WM_VOL_MUTE;
        for (i = 0; i < ice->num_total_dacs; i++) {
-               ice->spec.aureon.vol[i] = WM_VOL_MUTE;
-               wm_set_vol(ice, i, ice->spec.aureon.vol[i], ice->spec.aureon.master[i % 2]);
+               spec->vol[i] = WM_VOL_MUTE;
+               wm_set_vol(ice, i, spec->vol[i], spec->master[i % 2]);
        }
 
        return 0;
index 371f78461db4b65a7c2d71152d105ecf00ca9f78..efd180b40e56382b308fb61c48ffad383193114b 100644 (file)
@@ -22,7 +22,6 @@
  *
  */      
 
-#include <sound/driver.h>
 #include <asm/io.h>
 #include <linux/delay.h>
 #include <linux/interrupt.h>
@@ -405,7 +404,7 @@ static int snd_ice1712_delta1010lt_wordclock_status_get(struct snd_kcontrol *kco
        if (snd_i2c_sendbytes(ice->cs8427, &reg, 1) != 1)
                snd_printk(KERN_ERR "unable to send register 0x%x byte to CS8427\n", reg);
        snd_i2c_readbytes(ice->cs8427, &reg, 1);
-       ucontrol->value.integer.value[0] = (reg ? 1 : 0);
+       ucontrol->value.integer.value[0] = (reg & CS8427_UNLOCK) ? 1 : 0;
        return 0;
 }
 
index 75e4e5e0f1e46b93429b51ca3e57330faa1ef5e4..064760d2a0278596d415a252399f7c366fdad462 100644 (file)
@@ -22,7 +22,6 @@
  *
  */      
 
-#include <sound/driver.h>
 #include <asm/io.h>
 #include <linux/delay.h>
 #include <linux/interrupt.h>
@@ -45,6 +44,11 @@ enum {
 };
        
 
+/* additional i2c devices for EWS boards */
+struct ews_spec {
+       struct snd_i2c_device *i2cdevs[3];
+};
+
 /*
  * access via i2c mode (for EWX 24/96, EWS 88MT&D)
  */
@@ -142,15 +146,17 @@ static struct snd_i2c_bit_ops snd_ice1712_ewx_cs8427_bit_ops = {
 /* AK4524 chip select; address 0x48 bit 0-3 */
 static int snd_ice1712_ews88mt_chip_select(struct snd_ice1712 *ice, int chip_mask)
 {
+       struct ews_spec *spec = ice->spec;
        unsigned char data, ndata;
 
        snd_assert(chip_mask >= 0 && chip_mask <= 0x0f, return -EINVAL);
        snd_i2c_lock(ice->i2c);
-       if (snd_i2c_readbytes(ice->spec.i2cdevs[EWS_I2C_PCF2], &data, 1) != 1)
+       if (snd_i2c_readbytes(spec->i2cdevs[EWS_I2C_PCF2], &data, 1) != 1)
                goto __error;
        ndata = (data & 0xf0) | chip_mask;
        if (ndata != data)
-               if (snd_i2c_sendbytes(ice->spec.i2cdevs[EWS_I2C_PCF2], &ndata, 1) != 1)
+               if (snd_i2c_sendbytes(spec->i2cdevs[EWS_I2C_PCF2], &ndata, 1)
+                   != 1)
                        goto __error;
        snd_i2c_unlock(ice->i2c);
        return 0;
@@ -224,6 +230,7 @@ static void dmx6fire_ak4524_lock(struct snd_akm4xxx *ak, int chip)
 
 static void snd_ice1712_ews_cs8404_spdif_write(struct snd_ice1712 *ice, unsigned char bits)
 {
+       struct ews_spec *spec = ice->spec;
        unsigned char bytes[2];
 
        snd_i2c_lock(ice->i2c);
@@ -231,15 +238,18 @@ static void snd_ice1712_ews_cs8404_spdif_write(struct snd_ice1712 *ice, unsigned
        case ICE1712_SUBDEVICE_EWS88MT:
        case ICE1712_SUBDEVICE_EWS88MT_NEW:
        case ICE1712_SUBDEVICE_PHASE88:
-               if (snd_i2c_sendbytes(ice->spec.i2cdevs[EWS_I2C_CS8404], &bits, 1) != 1)
+               if (snd_i2c_sendbytes(spec->i2cdevs[EWS_I2C_CS8404], &bits, 1)
+                   != 1)
                        goto _error;
                break;
        case ICE1712_SUBDEVICE_EWS88D:
-               if (snd_i2c_readbytes(ice->spec.i2cdevs[EWS_I2C_88D], bytes, 2) != 2)
+               if (snd_i2c_readbytes(spec->i2cdevs[EWS_I2C_88D], bytes, 2)
+                   != 2)
                        goto _error;
                if (bits != bytes[1]) {
                        bytes[1] = bits;
-                       if (snd_i2c_sendbytes(ice->spec.i2cdevs[EWS_I2C_88D], bytes, 2) != 2)
+                       if (snd_i2c_sendbytes(spec->i2cdevs[EWS_I2C_88D],
+                                             bytes, 2) != 2)
                                goto _error;
                }
                break;
@@ -412,6 +422,7 @@ static int __devinit snd_ice1712_ews_init(struct snd_ice1712 *ice)
 {
        int err;
        struct snd_akm4xxx *ak;
+       struct ews_spec *spec;
 
        /* set the analog DACs */
        switch (ice->eeprom.subvendor) {
@@ -436,6 +447,11 @@ static int __devinit snd_ice1712_ews_init(struct snd_ice1712 *ice)
                break;
        }
 
+       spec = kzalloc(sizeof(*spec), GFP_KERNEL);
+       if (!spec)
+               return -ENOMEM;
+       ice->spec = spec;
+
        /* create i2c */
        if ((err = snd_i2c_bus_create(ice->card, "ICE1712 GPIO 1", NULL, &ice->i2c)) < 0) {
                snd_printk(KERN_ERR "unable to create I2C bus\n");
@@ -447,7 +463,10 @@ static int __devinit snd_ice1712_ews_init(struct snd_ice1712 *ice)
        /* create i2c devices */
        switch (ice->eeprom.subvendor) {
        case ICE1712_SUBDEVICE_DMX6FIRE:
-               if ((err = snd_i2c_device_create(ice->i2c, "PCF9554", ICE1712_6FIRE_PCF9554_ADDR, &ice->spec.i2cdevs[EWS_I2C_6FIRE])) < 0) {
+               err = snd_i2c_device_create(ice->i2c, "PCF9554",
+                                           ICE1712_6FIRE_PCF9554_ADDR,
+                                           &spec->i2cdevs[EWS_I2C_6FIRE]);
+               if (err < 0) {
                        snd_printk(KERN_ERR "PCF9554 initialization failed\n");
                        return err;
                }
@@ -456,18 +475,30 @@ static int __devinit snd_ice1712_ews_init(struct snd_ice1712 *ice)
        case ICE1712_SUBDEVICE_EWS88MT:
        case ICE1712_SUBDEVICE_EWS88MT_NEW:
        case ICE1712_SUBDEVICE_PHASE88:
-               if ((err = snd_i2c_device_create(ice->i2c, "CS8404", ICE1712_EWS88MT_CS8404_ADDR, &ice->spec.i2cdevs[EWS_I2C_CS8404])) < 0)
+               err = snd_i2c_device_create(ice->i2c, "CS8404",
+                                           ICE1712_EWS88MT_CS8404_ADDR,
+                                           &spec->i2cdevs[EWS_I2C_CS8404]);
+               if (err < 0)
                        return err;
-               if ((err = snd_i2c_device_create(ice->i2c, "PCF8574 (1st)", ICE1712_EWS88MT_INPUT_ADDR, &ice->spec.i2cdevs[EWS_I2C_PCF1])) < 0)
+               err = snd_i2c_device_create(ice->i2c, "PCF8574 (1st)",
+                                           ICE1712_EWS88MT_INPUT_ADDR,
+                                           &spec->i2cdevs[EWS_I2C_PCF1]);
+               if (err < 0)
                        return err;
-               if ((err = snd_i2c_device_create(ice->i2c, "PCF8574 (2nd)", ICE1712_EWS88MT_OUTPUT_ADDR, &ice->spec.i2cdevs[EWS_I2C_PCF2])) < 0)
+               err = snd_i2c_device_create(ice->i2c, "PCF8574 (2nd)",
+                                           ICE1712_EWS88MT_OUTPUT_ADDR,
+                                           &spec->i2cdevs[EWS_I2C_PCF2]);
+               if (err < 0)
                        return err;
                /* Check if the front module is connected */
                if ((err = snd_ice1712_ews88mt_chip_select(ice, 0x0f)) < 0)
                        return err;
                break;
        case ICE1712_SUBDEVICE_EWS88D:
-               if ((err = snd_i2c_device_create(ice->i2c, "PCF8575", ICE1712_EWS88D_PCF_ADDR, &ice->spec.i2cdevs[EWS_I2C_88D])) < 0)
+               err = snd_i2c_device_create(ice->i2c, "PCF8575",
+                                           ICE1712_EWS88D_PCF_ADDR,
+                                           &spec->i2cdevs[EWS_I2C_88D]);
+               if (err < 0)
                        return err;
                break;
        }
@@ -507,7 +538,7 @@ static int __devinit snd_ice1712_ews_init(struct snd_ice1712 *ice)
        }
 
        /* analog section */
-       ak = ice->akm = kmalloc(sizeof(struct snd_akm4xxx), GFP_KERNEL);
+       ak = ice->akm = kzalloc(sizeof(struct snd_akm4xxx), GFP_KERNEL);
        if (! ak)
                return -ENOMEM;
        ice->akm_codecs = 1;
@@ -605,10 +636,11 @@ static struct snd_kcontrol_new snd_ice1712_ewx2496_controls[] __devinitdata = {
 static int snd_ice1712_ews88mt_output_sense_get(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
 {
        struct snd_ice1712 *ice = snd_kcontrol_chip(kcontrol);
+       struct ews_spec *spec = ice->spec;
        unsigned char data;
 
        snd_i2c_lock(ice->i2c);
-       if (snd_i2c_readbytes(ice->spec.i2cdevs[EWS_I2C_PCF2], &data, 1) != 1) {
+       if (snd_i2c_readbytes(spec->i2cdevs[EWS_I2C_PCF2], &data, 1) != 1) {
                snd_i2c_unlock(ice->i2c);
                return -EIO;
        }
@@ -621,15 +653,17 @@ static int snd_ice1712_ews88mt_output_sense_get(struct snd_kcontrol *kcontrol, s
 static int snd_ice1712_ews88mt_output_sense_put(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
 {
        struct snd_ice1712 *ice = snd_kcontrol_chip(kcontrol);
+       struct ews_spec *spec = ice->spec;
        unsigned char data, ndata;
 
        snd_i2c_lock(ice->i2c);
-       if (snd_i2c_readbytes(ice->spec.i2cdevs[EWS_I2C_PCF2], &data, 1) != 1) {
+       if (snd_i2c_readbytes(spec->i2cdevs[EWS_I2C_PCF2], &data, 1) != 1) {
                snd_i2c_unlock(ice->i2c);
                return -EIO;
        }
        ndata = (data & ~ICE1712_EWS88MT_OUTPUT_SENSE) | (ucontrol->value.enumerated.item[0] ? ICE1712_EWS88MT_OUTPUT_SENSE : 0);
-       if (ndata != data && snd_i2c_sendbytes(ice->spec.i2cdevs[EWS_I2C_PCF2], &ndata, 1) != 1) {
+       if (ndata != data && snd_i2c_sendbytes(spec->i2cdevs[EWS_I2C_PCF2],
+                                              &ndata, 1) != 1) {
                snd_i2c_unlock(ice->i2c);
                return -EIO;
        }
@@ -641,12 +675,13 @@ static int snd_ice1712_ews88mt_output_sense_put(struct snd_kcontrol *kcontrol, s
 static int snd_ice1712_ews88mt_input_sense_get(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
 {
        struct snd_ice1712 *ice = snd_kcontrol_chip(kcontrol);
+       struct ews_spec *spec = ice->spec;
        int channel = snd_ctl_get_ioffidx(kcontrol, &ucontrol->id);
        unsigned char data;
 
        snd_assert(channel >= 0 && channel <= 7, return 0);
        snd_i2c_lock(ice->i2c);
-       if (snd_i2c_readbytes(ice->spec.i2cdevs[EWS_I2C_PCF1], &data, 1) != 1) {
+       if (snd_i2c_readbytes(spec->i2cdevs[EWS_I2C_PCF1], &data, 1) != 1) {
                snd_i2c_unlock(ice->i2c);
                return -EIO;
        }
@@ -660,17 +695,19 @@ static int snd_ice1712_ews88mt_input_sense_get(struct snd_kcontrol *kcontrol, st
 static int snd_ice1712_ews88mt_input_sense_put(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
 {
        struct snd_ice1712 *ice = snd_kcontrol_chip(kcontrol);
+       struct ews_spec *spec = ice->spec;
        int channel = snd_ctl_get_ioffidx(kcontrol, &ucontrol->id);
        unsigned char data, ndata;
 
        snd_assert(channel >= 0 && channel <= 7, return 0);
        snd_i2c_lock(ice->i2c);
-       if (snd_i2c_readbytes(ice->spec.i2cdevs[EWS_I2C_PCF1], &data, 1) != 1) {
+       if (snd_i2c_readbytes(spec->i2cdevs[EWS_I2C_PCF1], &data, 1) != 1) {
                snd_i2c_unlock(ice->i2c);
                return -EIO;
        }
        ndata = (data & ~(1 << channel)) | (ucontrol->value.enumerated.item[0] ? 0 : (1 << channel));
-       if (ndata != data && snd_i2c_sendbytes(ice->spec.i2cdevs[EWS_I2C_PCF1], &ndata, 1) != 1) {
+       if (ndata != data && snd_i2c_sendbytes(spec->i2cdevs[EWS_I2C_PCF1],
+                                              &ndata, 1) != 1) {
                snd_i2c_unlock(ice->i2c);
                return -EIO;
        }
@@ -705,12 +742,13 @@ static struct snd_kcontrol_new snd_ice1712_ews88mt_output_sense __devinitdata =
 static int snd_ice1712_ews88d_control_get(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
 {
        struct snd_ice1712 *ice = snd_kcontrol_chip(kcontrol);
+       struct ews_spec *spec = ice->spec;
        int shift = kcontrol->private_value & 0xff;
        int invert = (kcontrol->private_value >> 8) & 1;
        unsigned char data[2];
        
        snd_i2c_lock(ice->i2c);
-       if (snd_i2c_readbytes(ice->spec.i2cdevs[EWS_I2C_88D], data, 2) != 2) {
+       if (snd_i2c_readbytes(spec->i2cdevs[EWS_I2C_88D], data, 2) != 2) {
                snd_i2c_unlock(ice->i2c);
                return -EIO;
        }
@@ -725,13 +763,14 @@ static int snd_ice1712_ews88d_control_get(struct snd_kcontrol *kcontrol, struct
 static int snd_ice1712_ews88d_control_put(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
 {
        struct snd_ice1712 *ice = snd_kcontrol_chip(kcontrol);
+       struct ews_spec *spec = ice->spec;
        int shift = kcontrol->private_value & 0xff;
        int invert = (kcontrol->private_value >> 8) & 1;
        unsigned char data[2], ndata[2];
        int change;
 
        snd_i2c_lock(ice->i2c);
-       if (snd_i2c_readbytes(ice->spec.i2cdevs[EWS_I2C_88D], data, 2) != 2) {
+       if (snd_i2c_readbytes(spec->i2cdevs[EWS_I2C_88D], data, 2) != 2) {
                snd_i2c_unlock(ice->i2c);
                return -EIO;
        }
@@ -744,7 +783,8 @@ static int snd_ice1712_ews88d_control_put(struct snd_kcontrol *kcontrol, struct
                        ndata[shift >> 3] |= (1 << (shift & 7));
        }
        change = (data[shift >> 3] != ndata[shift >> 3]);
-       if (change && snd_i2c_sendbytes(ice->spec.i2cdevs[EWS_I2C_88D], data, 2) != 2) {
+       if (change &&
+           snd_i2c_sendbytes(spec->i2cdevs[EWS_I2C_88D], data, 2) != 2) {
                snd_i2c_unlock(ice->i2c);
                return -EIO;
        }
@@ -778,11 +818,13 @@ static struct snd_kcontrol_new snd_ice1712_ews88d_controls[] __devinitdata = {
 static int snd_ice1712_6fire_read_pca(struct snd_ice1712 *ice, unsigned char reg)
 {
        unsigned char byte;
+       struct ews_spec *spec = ice->spec;
+
        snd_i2c_lock(ice->i2c);
        byte = reg;
-       snd_i2c_sendbytes(ice->spec.i2cdevs[EWS_I2C_6FIRE], &byte, 1);
+       snd_i2c_sendbytes(spec->i2cdevs[EWS_I2C_6FIRE], &byte, 1);
        byte = 0;
-       if (snd_i2c_readbytes(ice->spec.i2cdevs[EWS_I2C_6FIRE], &byte, 1) != 1) {
+       if (snd_i2c_readbytes(spec->i2cdevs[EWS_I2C_6FIRE], &byte, 1) != 1) {
                snd_i2c_unlock(ice->i2c);
                printk(KERN_ERR "cannot read pca\n");
                return -EIO;
@@ -794,10 +836,12 @@ static int snd_ice1712_6fire_read_pca(struct snd_ice1712 *ice, unsigned char reg
 static int snd_ice1712_6fire_write_pca(struct snd_ice1712 *ice, unsigned char reg, unsigned char data)
 {
        unsigned char bytes[2];
+       struct ews_spec *spec = ice->spec;
+
        snd_i2c_lock(ice->i2c);
        bytes[0] = reg;
        bytes[1] = data;
-       if (snd_i2c_sendbytes(ice->spec.i2cdevs[EWS_I2C_6FIRE], bytes, 2) != 2) {
+       if (snd_i2c_sendbytes(spec->i2cdevs[EWS_I2C_6FIRE], bytes, 2) != 2) {
                snd_i2c_unlock(ice->i2c);
                return -EIO;
        }
index abcfd1da65874466f935515f0116afc54a20260d..cf5c7c0898fd4e6c488321a55259f2855a23a3f4 100644 (file)
@@ -21,7 +21,6 @@
  *
  */      
 
-#include <sound/driver.h>
 #include <asm/io.h>
 #include <linux/delay.h>
 #include <linux/interrupt.h>
 #include "ice1712.h"
 #include "hoontech.h"
 
+/* Hoontech-specific setting */
+struct hoontech_spec {
+       unsigned char boxbits[4];
+       unsigned int config;
+       unsigned short boxconfig[4];
+};
 
 static void __devinit snd_ice1712_stdsp24_gpio_write(struct snd_ice1712 *ice, unsigned char byte)
 {
@@ -50,169 +55,182 @@ static void __devinit snd_ice1712_stdsp24_gpio_write(struct snd_ice1712 *ice, un
 
 static void __devinit snd_ice1712_stdsp24_darear(struct snd_ice1712 *ice, int activate)
 {
+       struct hoontech_spec *spec = ice->spec;
        mutex_lock(&ice->gpio_mutex);
-       ICE1712_STDSP24_0_DAREAR(ice->spec.hoontech.boxbits, activate);
-       snd_ice1712_stdsp24_gpio_write(ice, ice->spec.hoontech.boxbits[0]);
+       ICE1712_STDSP24_0_DAREAR(spec->boxbits, activate);
+       snd_ice1712_stdsp24_gpio_write(ice, spec->boxbits[0]);
        mutex_unlock(&ice->gpio_mutex);
 }
 
 static void __devinit snd_ice1712_stdsp24_mute(struct snd_ice1712 *ice, int activate)
 {
+       struct hoontech_spec *spec = ice->spec;
        mutex_lock(&ice->gpio_mutex);
-       ICE1712_STDSP24_3_MUTE(ice->spec.hoontech.boxbits, activate);
-       snd_ice1712_stdsp24_gpio_write(ice, ice->spec.hoontech.boxbits[3]);
+       ICE1712_STDSP24_3_MUTE(spec->boxbits, activate);
+       snd_ice1712_stdsp24_gpio_write(ice, spec->boxbits[3]);
        mutex_unlock(&ice->gpio_mutex);
 }
 
 static void __devinit snd_ice1712_stdsp24_insel(struct snd_ice1712 *ice, int activate)
 {
+       struct hoontech_spec *spec = ice->spec;
        mutex_lock(&ice->gpio_mutex);
-       ICE1712_STDSP24_3_INSEL(ice->spec.hoontech.boxbits, activate);
-       snd_ice1712_stdsp24_gpio_write(ice, ice->spec.hoontech.boxbits[3]);
+       ICE1712_STDSP24_3_INSEL(spec->boxbits, activate);
+       snd_ice1712_stdsp24_gpio_write(ice, spec->boxbits[3]);
        mutex_unlock(&ice->gpio_mutex);
 }
 
 static void __devinit snd_ice1712_stdsp24_box_channel(struct snd_ice1712 *ice, int box, int chn, int activate)
 {
+       struct hoontech_spec *spec = ice->spec;
+
        mutex_lock(&ice->gpio_mutex);
 
        /* select box */
-       ICE1712_STDSP24_0_BOX(ice->spec.hoontech.boxbits, box);
-       snd_ice1712_stdsp24_gpio_write(ice, ice->spec.hoontech.boxbits[0]);
+       ICE1712_STDSP24_0_BOX(spec->boxbits, box);
+       snd_ice1712_stdsp24_gpio_write(ice, spec->boxbits[0]);
 
        /* prepare for write */
        if (chn == 3)
-               ICE1712_STDSP24_2_CHN4(ice->spec.hoontech.boxbits, 0);
-       ICE1712_STDSP24_2_MIDI1(ice->spec.hoontech.boxbits, activate);
-       snd_ice1712_stdsp24_gpio_write(ice, ice->spec.hoontech.boxbits[2]);
-       snd_ice1712_stdsp24_gpio_write(ice, ice->spec.hoontech.boxbits[3]);
-
-       ICE1712_STDSP24_1_CHN1(ice->spec.hoontech.boxbits, 1);
-       ICE1712_STDSP24_1_CHN2(ice->spec.hoontech.boxbits, 1);
-       ICE1712_STDSP24_1_CHN3(ice->spec.hoontech.boxbits, 1);
-       ICE1712_STDSP24_2_CHN4(ice->spec.hoontech.boxbits, 1);
-       snd_ice1712_stdsp24_gpio_write(ice, ice->spec.hoontech.boxbits[1]);
-       snd_ice1712_stdsp24_gpio_write(ice, ice->spec.hoontech.boxbits[2]);
+               ICE1712_STDSP24_2_CHN4(spec->boxbits, 0);
+       ICE1712_STDSP24_2_MIDI1(spec->boxbits, activate);
+       snd_ice1712_stdsp24_gpio_write(ice, spec->boxbits[2]);
+       snd_ice1712_stdsp24_gpio_write(ice, spec->boxbits[3]);
+
+       ICE1712_STDSP24_1_CHN1(spec->boxbits, 1);
+       ICE1712_STDSP24_1_CHN2(spec->boxbits, 1);
+       ICE1712_STDSP24_1_CHN3(spec->boxbits, 1);
+       ICE1712_STDSP24_2_CHN4(spec->boxbits, 1);
+       snd_ice1712_stdsp24_gpio_write(ice, spec->boxbits[1]);
+       snd_ice1712_stdsp24_gpio_write(ice, spec->boxbits[2]);
        udelay(100);
        if (chn == 3) {
-               ICE1712_STDSP24_2_CHN4(ice->spec.hoontech.boxbits, 0);
-               snd_ice1712_stdsp24_gpio_write(ice, ice->spec.hoontech.boxbits[2]);
+               ICE1712_STDSP24_2_CHN4(spec->boxbits, 0);
+               snd_ice1712_stdsp24_gpio_write(ice, spec->boxbits[2]);
        } else {
                switch (chn) {
-               case 0: ICE1712_STDSP24_1_CHN1(ice->spec.hoontech.boxbits, 0); break;
-               case 1: ICE1712_STDSP24_1_CHN2(ice->spec.hoontech.boxbits, 0); break;
-               case 2: ICE1712_STDSP24_1_CHN3(ice->spec.hoontech.boxbits, 0); break;
+               case 0: ICE1712_STDSP24_1_CHN1(spec->boxbits, 0); break;
+               case 1: ICE1712_STDSP24_1_CHN2(spec->boxbits, 0); break;
+               case 2: ICE1712_STDSP24_1_CHN3(spec->boxbits, 0); break;
                }
-               snd_ice1712_stdsp24_gpio_write(ice, ice->spec.hoontech.boxbits[1]);
+               snd_ice1712_stdsp24_gpio_write(ice, spec->boxbits[1]);
        }
        udelay(100);
-       ICE1712_STDSP24_1_CHN1(ice->spec.hoontech.boxbits, 1);
-       ICE1712_STDSP24_1_CHN2(ice->spec.hoontech.boxbits, 1);
-       ICE1712_STDSP24_1_CHN3(ice->spec.hoontech.boxbits, 1);
-       ICE1712_STDSP24_2_CHN4(ice->spec.hoontech.boxbits, 1);
-       snd_ice1712_stdsp24_gpio_write(ice, ice->spec.hoontech.boxbits[1]);
-       snd_ice1712_stdsp24_gpio_write(ice, ice->spec.hoontech.boxbits[2]);
+       ICE1712_STDSP24_1_CHN1(spec->boxbits, 1);
+       ICE1712_STDSP24_1_CHN2(spec->boxbits, 1);
+       ICE1712_STDSP24_1_CHN3(spec->boxbits, 1);
+       ICE1712_STDSP24_2_CHN4(spec->boxbits, 1);
+       snd_ice1712_stdsp24_gpio_write(ice, spec->boxbits[1]);
+       snd_ice1712_stdsp24_gpio_write(ice, spec->boxbits[2]);
        udelay(100);
 
-       ICE1712_STDSP24_2_MIDI1(ice->spec.hoontech.boxbits, 0);
-       snd_ice1712_stdsp24_gpio_write(ice, ice->spec.hoontech.boxbits[2]);
+       ICE1712_STDSP24_2_MIDI1(spec->boxbits, 0);
+       snd_ice1712_stdsp24_gpio_write(ice, spec->boxbits[2]);
 
        mutex_unlock(&ice->gpio_mutex);
 }
 
 static void __devinit snd_ice1712_stdsp24_box_midi(struct snd_ice1712 *ice, int box, int master)
 {
+       struct hoontech_spec *spec = ice->spec;
+
        mutex_lock(&ice->gpio_mutex);
 
        /* select box */
-       ICE1712_STDSP24_0_BOX(ice->spec.hoontech.boxbits, box);
-       snd_ice1712_stdsp24_gpio_write(ice, ice->spec.hoontech.boxbits[0]);
+       ICE1712_STDSP24_0_BOX(spec->boxbits, box);
+       snd_ice1712_stdsp24_gpio_write(ice, spec->boxbits[0]);
 
-       ICE1712_STDSP24_2_MIDIIN(ice->spec.hoontech.boxbits, 1);
-       ICE1712_STDSP24_2_MIDI1(ice->spec.hoontech.boxbits, master);
-       snd_ice1712_stdsp24_gpio_write(ice, ice->spec.hoontech.boxbits[2]);
-       snd_ice1712_stdsp24_gpio_write(ice, ice->spec.hoontech.boxbits[3]);
+       ICE1712_STDSP24_2_MIDIIN(spec->boxbits, 1);
+       ICE1712_STDSP24_2_MIDI1(spec->boxbits, master);
+       snd_ice1712_stdsp24_gpio_write(ice, spec->boxbits[2]);
+       snd_ice1712_stdsp24_gpio_write(ice, spec->boxbits[3]);
 
        udelay(100);
        
-       ICE1712_STDSP24_2_MIDIIN(ice->spec.hoontech.boxbits, 0);
-       snd_ice1712_stdsp24_gpio_write(ice, ice->spec.hoontech.boxbits[2]);
+       ICE1712_STDSP24_2_MIDIIN(spec->boxbits, 0);
+       snd_ice1712_stdsp24_gpio_write(ice, spec->boxbits[2]);
        
        mdelay(10);
        
-       ICE1712_STDSP24_2_MIDIIN(ice->spec.hoontech.boxbits, 1);
-       snd_ice1712_stdsp24_gpio_write(ice, ice->spec.hoontech.boxbits[2]);
+       ICE1712_STDSP24_2_MIDIIN(spec->boxbits, 1);
+       snd_ice1712_stdsp24_gpio_write(ice, spec->boxbits[2]);
 
        mutex_unlock(&ice->gpio_mutex);
 }
 
 static void __devinit snd_ice1712_stdsp24_midi2(struct snd_ice1712 *ice, int activate)
 {
+       struct hoontech_spec *spec = ice->spec;
        mutex_lock(&ice->gpio_mutex);
-       ICE1712_STDSP24_3_MIDI2(ice->spec.hoontech.boxbits, activate);
-       snd_ice1712_stdsp24_gpio_write(ice, ice->spec.hoontech.boxbits[3]);
+       ICE1712_STDSP24_3_MIDI2(spec->boxbits, activate);
+       snd_ice1712_stdsp24_gpio_write(ice, spec->boxbits[3]);
        mutex_unlock(&ice->gpio_mutex);
 }
 
 static int __devinit snd_ice1712_hoontech_init(struct snd_ice1712 *ice)
 {
+       struct hoontech_spec *spec;
        int box, chn;
 
        ice->num_total_dacs = 8;
        ice->num_total_adcs = 8;
 
-       ice->spec.hoontech.boxbits[0] = 
-       ice->spec.hoontech.boxbits[1] = 
-       ice->spec.hoontech.boxbits[2] = 
-       ice->spec.hoontech.boxbits[3] = 0;      /* should be already */
-
-       ICE1712_STDSP24_SET_ADDR(ice->spec.hoontech.boxbits, 0);
-       ICE1712_STDSP24_CLOCK(ice->spec.hoontech.boxbits, 0, 1);
-       ICE1712_STDSP24_0_BOX(ice->spec.hoontech.boxbits, 0);
-       ICE1712_STDSP24_0_DAREAR(ice->spec.hoontech.boxbits, 0);
-
-       ICE1712_STDSP24_SET_ADDR(ice->spec.hoontech.boxbits, 1);
-       ICE1712_STDSP24_CLOCK(ice->spec.hoontech.boxbits, 1, 1);
-       ICE1712_STDSP24_1_CHN1(ice->spec.hoontech.boxbits, 1);
-       ICE1712_STDSP24_1_CHN2(ice->spec.hoontech.boxbits, 1);
-       ICE1712_STDSP24_1_CHN3(ice->spec.hoontech.boxbits, 1);
+       spec = kzalloc(sizeof(*spec), GFP_KERNEL);
+       if (!spec)
+               return -ENOMEM;
+       ice->spec = spec;
+
+       ICE1712_STDSP24_SET_ADDR(spec->boxbits, 0);
+       ICE1712_STDSP24_CLOCK(spec->boxbits, 0, 1);
+       ICE1712_STDSP24_0_BOX(spec->boxbits, 0);
+       ICE1712_STDSP24_0_DAREAR(spec->boxbits, 0);
+
+       ICE1712_STDSP24_SET_ADDR(spec->boxbits, 1);
+       ICE1712_STDSP24_CLOCK(spec->boxbits, 1, 1);
+       ICE1712_STDSP24_1_CHN1(spec->boxbits, 1);
+       ICE1712_STDSP24_1_CHN2(spec->boxbits, 1);
+       ICE1712_STDSP24_1_CHN3(spec->boxbits, 1);
        
-       ICE1712_STDSP24_SET_ADDR(ice->spec.hoontech.boxbits, 2);
-       ICE1712_STDSP24_CLOCK(ice->spec.hoontech.boxbits, 2, 1);
-       ICE1712_STDSP24_2_CHN4(ice->spec.hoontech.boxbits, 1);
-       ICE1712_STDSP24_2_MIDIIN(ice->spec.hoontech.boxbits, 1);
-       ICE1712_STDSP24_2_MIDI1(ice->spec.hoontech.boxbits, 0);
-
-       ICE1712_STDSP24_SET_ADDR(ice->spec.hoontech.boxbits, 3);
-       ICE1712_STDSP24_CLOCK(ice->spec.hoontech.boxbits, 3, 1);
-       ICE1712_STDSP24_3_MIDI2(ice->spec.hoontech.boxbits, 0);
-       ICE1712_STDSP24_3_MUTE(ice->spec.hoontech.boxbits, 1);
-       ICE1712_STDSP24_3_INSEL(ice->spec.hoontech.boxbits, 0);
+       ICE1712_STDSP24_SET_ADDR(spec->boxbits, 2);
+       ICE1712_STDSP24_CLOCK(spec->boxbits, 2, 1);
+       ICE1712_STDSP24_2_CHN4(spec->boxbits, 1);
+       ICE1712_STDSP24_2_MIDIIN(spec->boxbits, 1);
+       ICE1712_STDSP24_2_MIDI1(spec->boxbits, 0);
+
+       ICE1712_STDSP24_SET_ADDR(spec->boxbits, 3);
+       ICE1712_STDSP24_CLOCK(spec->boxbits, 3, 1);
+       ICE1712_STDSP24_3_MIDI2(spec->boxbits, 0);
+       ICE1712_STDSP24_3_MUTE(spec->boxbits, 1);
+       ICE1712_STDSP24_3_INSEL(spec->boxbits, 0);
 
        /* let's go - activate only functions in first box */
-       ice->spec.hoontech.config = 0;
+       spec->config = 0;
                            /* ICE1712_STDSP24_MUTE |
                               ICE1712_STDSP24_INSEL |
                               ICE1712_STDSP24_DAREAR; */
-       ice->spec.hoontech.boxconfig[0] = ICE1712_STDSP24_BOX_CHN1 |
+       spec->boxconfig[0] = ICE1712_STDSP24_BOX_CHN1 |
                                     ICE1712_STDSP24_BOX_CHN2 |
                                     ICE1712_STDSP24_BOX_CHN3 |
                                     ICE1712_STDSP24_BOX_CHN4 |
                                     ICE1712_STDSP24_BOX_MIDI1 |
                                     ICE1712_STDSP24_BOX_MIDI2;
-       ice->spec.hoontech.boxconfig[1] = 
-       ice->spec.hoontech.boxconfig[2] = 
-       ice->spec.hoontech.boxconfig[3] = 0;
-       snd_ice1712_stdsp24_darear(ice, (ice->spec.hoontech.config & ICE1712_STDSP24_DAREAR) ? 1 : 0);
-       snd_ice1712_stdsp24_mute(ice, (ice->spec.hoontech.config & ICE1712_STDSP24_MUTE) ? 1 : 0);
-       snd_ice1712_stdsp24_insel(ice, (ice->spec.hoontech.config & ICE1712_STDSP24_INSEL) ? 1 : 0);
-       for (box = 0; box < 4; box++) {
+       spec->boxconfig[1] = 
+       spec->boxconfig[2] = 
+       spec->boxconfig[3] = 0;
+       snd_ice1712_stdsp24_darear(ice,
+               (spec->config & ICE1712_STDSP24_DAREAR) ? 1 : 0);
+       snd_ice1712_stdsp24_mute(ice,
+               (spec->config & ICE1712_STDSP24_MUTE) ? 1 : 0);
+       snd_ice1712_stdsp24_insel(ice,
+               (spec->config & ICE1712_STDSP24_INSEL) ? 1 : 0);
+       for (box = 0; box < 1; box++) {
+               if (spec->boxconfig[box] & ICE1712_STDSP24_BOX_MIDI2)
+                        snd_ice1712_stdsp24_midi2(ice, 1);
                for (chn = 0; chn < 4; chn++)
-                       snd_ice1712_stdsp24_box_channel(ice, box, chn, (ice->spec.hoontech.boxconfig[box] & (1 << chn)) ? 1 : 0);
+                       snd_ice1712_stdsp24_box_channel(ice, box, chn,
+                               (spec->boxconfig[box] & (1 << chn)) ? 1 : 0);
                snd_ice1712_stdsp24_box_midi(ice, box,
-                               (ice->spec.hoontech.boxconfig[box] & ICE1712_STDSP24_BOX_MIDI1) ? 1 : 0);
-               if (ice->spec.hoontech.boxconfig[box] & ICE1712_STDSP24_BOX_MIDI2)
-                       snd_ice1712_stdsp24_midi2(ice, 1);
+                               (spec->boxconfig[box] & ICE1712_STDSP24_BOX_MIDI1) ? 1 : 0);
        }
 
        return 0;
index 052fc3cb32728b8cedb712370ca8e61eec0aad41..df292af6738197976e05899c7c4e5f0d4e439b86 100644 (file)
@@ -47,7 +47,6 @@
  */
 
 
-#include <sound/driver.h>
 #include <asm/io.h>
 #include <linux/delay.h>
 #include <linux/interrupt.h>
@@ -2491,6 +2490,7 @@ static int snd_ice1712_free(struct snd_ice1712 *ice)
                pci_release_regions(ice->pci);
        snd_ice1712_akm4xxx_free(ice);
        pci_disable_device(ice->pci);
+       kfree(ice->spec);
        kfree(ice);
        return 0;
 }
index 58640afa54041e49441e8dccf4dd18323f282d02..303cffe08bd8a7abba006a95f3deb3124d92cf3b 100644 (file)
@@ -366,42 +366,7 @@ struct snd_ice1712 {
        struct mutex gpio_mutex;
 
        /* other board-specific data */
-       union {
-               /* additional i2c devices for EWS boards */
-               struct snd_i2c_device *i2cdevs[3];
-               /* AC97 register cache for Aureon */
-               struct aureon_spec {
-                       unsigned short stac9744[64];
-                       unsigned int cs8415_mux;
-                       unsigned short master[2];
-                       unsigned short vol[8];
-                       unsigned char pca9554_out;
-               } aureon;
-               /* AC97 register cache for Phase28 */
-               struct phase28_spec {
-                       unsigned short master[2];
-                       unsigned short vol[8];
-               } phase28;
-               /* a non-standard I2C device for revo51 */
-               struct revo51_spec {
-                       struct snd_i2c_device *dev;
-                       struct snd_pt2258 *pt2258;
-               } revo51;
-               /* Hoontech-specific setting */
-               struct hoontech_spec {
-                       unsigned char boxbits[4];
-                       unsigned int config;
-                       unsigned short boxconfig[4];
-               } hoontech;
-               struct {
-                       struct ak4114 *ak4114;
-                       unsigned int analog: 1;
-               } juli;
-               struct {
-                       struct ak4114 *ak4114;
-               } prodigy192;
-       } spec;
-
+       void *spec;
 };
 
 
index 0b0bbb0d96b91f8626724b163e68a88838422832..f533850ec6e776377bffd69dac936ae61f32d4bb 100644 (file)
@@ -22,7 +22,6 @@
  *
  */      
 
-#include <sound/driver.h>
 #include <asm/io.h>
 #include <linux/delay.h>
 #include <linux/interrupt.h>
 #include "vt1720_mobo.h"
 #include "pontis.h"
 #include "prodigy192.h"
+#include "prodigy_hifi.h"
 #include "juli.h"
 #include "phase.h"
 #include "wtm.h"
+#include "se.h"
 
 MODULE_AUTHOR("Jaroslav Kysela <perex@perex.cz>");
 MODULE_DESCRIPTION("VIA ICEnsemble ICE1724/1720 (Envy24HT/PT)");
@@ -62,9 +63,11 @@ MODULE_SUPPORTED_DEVICE("{"
               VT1720_MOBO_DEVICE_DESC
               PONTIS_DEVICE_DESC
               PRODIGY192_DEVICE_DESC
+              PRODIGY_HIFI_DEVICE_DESC
               JULI_DEVICE_DESC
               PHASE_DEVICE_DESC
               WTM_DEVICE_DESC
+              SE_DEVICE_DESC
                "{VIA,VT1720},"
                "{VIA,VT1724},"
                "{ICEnsemble,Generic ICE1724},"
@@ -1929,10 +1932,12 @@ static struct snd_ice1712_card_info *card_tables[] __devinitdata = {
        snd_vt1724_aureon_cards,
        snd_vt1720_mobo_cards,
        snd_vt1720_pontis_cards,
+       snd_vt1724_prodigy_hifi_cards,
        snd_vt1724_prodigy192_cards,
        snd_vt1724_juli_cards,
        snd_vt1724_phase_cards,
        snd_vt1724_wtm_cards,
+       snd_vt1724_se_cards,
        NULL,
 };
 
@@ -1955,6 +1960,7 @@ unsigned char snd_vt1724_read_i2c(struct snd_ice1712 *ice,
        unsigned char val;
 
        mutex_lock(&ice->i2c_mutex);
+       wait_i2c_busy(ice);
        outb(addr, ICEREG1724(ice, I2C_BYTE_ADDR));
        outb(dev & ~VT1724_I2C_WRITE, ICEREG1724(ice, I2C_DEV_ADDR));
        wait_i2c_busy(ice);
@@ -2170,6 +2176,7 @@ static int snd_vt1724_free(struct snd_ice1712 *ice)
        pci_release_regions(ice->pci);
        snd_ice1712_akm4xxx_free(ice);
        pci_disable_device(ice->pci);
+       kfree(ice->spec);
        kfree(ice);
        return 0;
 }
index 1fbe3ef8e60a5f370ecafa2ce46af18809e467e9..e8038c0ceb721a0f9a3d881814f3bc40dcee34e2 100644 (file)
@@ -21,7 +21,6 @@
  *
  */      
 
-#include <sound/driver.h>
 #include <asm/io.h>
 #include <linux/delay.h>
 #include <linux/interrupt.h>
 #include "envy24ht.h"
 #include "juli.h"
 
+struct juli_spec {
+       struct ak4114 *ak4114;
+       unsigned int analog: 1;
+};
+
 /*
  * chip addresses on I2C bus
  */
@@ -138,12 +142,13 @@ static struct snd_akm4xxx akm_juli_dac __devinitdata = {
 
 static int __devinit juli_add_controls(struct snd_ice1712 *ice)
 {
+       struct juli_spec *spec = ice->spec;
        int err;
        err = snd_ice1712_akm4xxx_build_controls(ice);
        if (err < 0)
                return err;
        /* only capture SPDIF over AK4114 */
-       err = snd_ak4114_build(ice->spec.juli.ak4114, NULL,
+       err = snd_ak4114_build(spec->ak4114, NULL,
                               ice->pcm_pro->streams[SNDRV_PCM_STREAM_CAPTURE].substream);
        if (err < 0)
                return err;
@@ -167,13 +172,19 @@ static int __devinit juli_init(struct snd_ice1712 *ice)
                0x41, 0x02, 0x2c, 0x00, 0x00
        };
        int err;
+       struct juli_spec *spec;
        struct snd_akm4xxx *ak;
 
+       spec = kzalloc(sizeof(*spec), GFP_KERNEL);
+       if (!spec)
+               return -ENOMEM;
+       ice->spec = spec;
+
        err = snd_ak4114_create(ice->card,
                                juli_ak4114_read,
                                juli_ak4114_write,
                                ak4114_init_vals, ak4114_init_txcsb,
-                               ice, &ice->spec.juli.ak4114);
+                               ice, &spec->ak4114);
        if (err < 0)
                return err;
 
@@ -181,12 +192,12 @@ static int __devinit juli_init(struct snd_ice1712 *ice)
         /* it seems that the analog doughter board detection does not work
            reliably, so force the analog flag; it should be very rare
            to use Juli@ without the analog doughter board */
-       ice->spec.juli.analog = (ice->gpio.get_data(ice) & GPIO_ANALOG_PRESENT) ? 0 : 1;
+       spec->analog = (ice->gpio.get_data(ice) & GPIO_ANALOG_PRESENT) ? 0 : 1;
 #else
-        ice->spec.juli.analog = 1;
+        spec->analog = 1;
 #endif
 
-       if (ice->spec.juli.analog) {
+       if (spec->analog) {
                printk(KERN_INFO "juli@: analog I/O detected\n");
                ice->num_total_dacs = 2;
                ice->num_total_adcs = 2;
index 3ac25058bb588fa583afc3bcc0a5b21ec577b6d7..9ab4a9f383cbb19b69834a7b27be19e759ea80db 100644 (file)
@@ -33,7 +33,6 @@
  *             CDTI may be completely blocked by 74HCT125's gate #1 controlled by GPIO 3
  */
 
-#include <sound/driver.h>
 #include <asm/io.h>
 #include <linux/delay.h>
 #include <linux/interrupt.h>
 #include "phase.h"
 #include <sound/tlv.h>
 
+/* AC97 register cache for Phase28 */
+struct phase28_spec {
+       unsigned short master[2];
+       unsigned short vol[8];
+} phase28;
+
 /* WM8770 registers */
 #define WM_DAC_ATTEN           0x00    /* DAC1-8 analog attenuation */
 #define WM_DAC_MASTER_ATTEN    0x08    /* DAC master analog attenuation */
@@ -313,27 +318,32 @@ static int wm_master_vol_info(struct snd_kcontrol *kcontrol, struct snd_ctl_elem
 static int wm_master_vol_get(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
 {
        struct snd_ice1712 *ice = snd_kcontrol_chip(kcontrol);
+       struct phase28_spec *spec = ice->spec;
        int i;
        for (i=0; i<2; i++)
-               ucontrol->value.integer.value[i] = ice->spec.phase28.master[i] & ~WM_VOL_MUTE;
+               ucontrol->value.integer.value[i] = spec->master[i] & ~WM_VOL_MUTE;
        return 0;
 }
 
 static int wm_master_vol_put(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
 {
        struct snd_ice1712 *ice = snd_kcontrol_chip(kcontrol);
+       struct phase28_spec *spec = ice->spec;
        int ch, change = 0;
 
        snd_ice1712_save_gpio_status(ice);
        for (ch = 0; ch < 2; ch++) {
-               if (ucontrol->value.integer.value[ch] != ice->spec.phase28.master[ch]) {
+               unsigned int vol = ucontrol->value.integer.value[ch];
+               if (vol > WM_VOL_MAX)
+                       continue;
+               vol |= spec->master[ch] & WM_VOL_MUTE;
+               if (vol != spec->master[ch]) {
                        int dac;
-                       ice->spec.phase28.master[ch] &= WM_VOL_MUTE;
-                       ice->spec.phase28.master[ch] |= ucontrol->value.integer.value[ch];
+                       spec->master[ch] = vol;
                        for (dac = 0; dac < ice->num_total_dacs; dac += 2)
                                wm_set_vol(ice, WM_DAC_ATTEN + dac + ch,
-                                          ice->spec.phase28.vol[dac + ch],
-                                          ice->spec.phase28.master[ch]);
+                                          spec->vol[dac + ch],
+                                          spec->master[ch]);
                        change = 1;
                }
        }
@@ -382,12 +392,18 @@ static int __devinit phase28_init(struct snd_ice1712 *ice)
 
        unsigned int tmp;
        struct snd_akm4xxx *ak;
+       struct phase28_spec *spec;
        const unsigned short *p;
        int i;
 
        ice->num_total_dacs = 8;
        ice->num_total_adcs = 2;
 
+       spec = kzalloc(sizeof(*spec), GFP_KERNEL);
+       if (!spec)
+               return -ENOMEM;
+       ice->spec = spec;
+
        // Initialize analog chips
        ak = ice->akm = kzalloc(sizeof(struct snd_akm4xxx), GFP_KERNEL);
        if (!ak)
@@ -417,11 +433,11 @@ static int __devinit phase28_init(struct snd_ice1712 *ice)
 
        snd_ice1712_restore_gpio_status(ice);
 
-       ice->spec.phase28.master[0] = WM_VOL_MUTE;
-       ice->spec.phase28.master[1] = WM_VOL_MUTE;
+       spec->master[0] = WM_VOL_MUTE;
+       spec->master[1] = WM_VOL_MUTE;
        for (i = 0; i < ice->num_total_dacs; i++) {
-               ice->spec.phase28.vol[i] = WM_VOL_MUTE;
-               wm_set_vol(ice, i, ice->spec.phase28.vol[i], ice->spec.phase28.master[i % 2]);
+               spec->vol[i] = WM_VOL_MUTE;
+               wm_set_vol(ice, i, spec->vol[i], spec->master[i % 2]);
        }
 
        return 0;
@@ -443,18 +459,21 @@ static int wm_vol_info(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *
 static int wm_vol_get(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
 {
        struct snd_ice1712 *ice = snd_kcontrol_chip(kcontrol);
+       struct phase28_spec *spec = ice->spec;
        int i, ofs, voices;
 
        voices = kcontrol->private_value >> 8;
        ofs = kcontrol->private_value & 0xff;
        for (i = 0; i < voices; i++)
-               ucontrol->value.integer.value[i] = ice->spec.phase28.vol[ofs+i] & ~WM_VOL_MUTE;
+               ucontrol->value.integer.value[i] =
+                       spec->vol[ofs+i] & ~WM_VOL_MUTE;
        return 0;
 }
 
 static int wm_vol_put(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
 {
        struct snd_ice1712 *ice = snd_kcontrol_chip(kcontrol);
+       struct phase28_spec *spec = ice->spec;
        int i, idx, ofs, voices;
        int change = 0;
 
@@ -462,12 +481,16 @@ static int wm_vol_put(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *
        ofs = kcontrol->private_value & 0xff;
        snd_ice1712_save_gpio_status(ice);
        for (i = 0; i < voices; i++) {
-               idx  = WM_DAC_ATTEN + ofs + i;
-               if (ucontrol->value.integer.value[i] != ice->spec.phase28.vol[ofs+i]) {
-                       ice->spec.phase28.vol[ofs+i] &= WM_VOL_MUTE;
-                       ice->spec.phase28.vol[ofs+i] |= ucontrol->value.integer.value[i];
-                       wm_set_vol(ice, idx, ice->spec.phase28.vol[ofs+i],
-                                  ice->spec.phase28.master[i]);
+               unsigned int vol;
+               vol = ucontrol->value.integer.value[i];
+               if (vol > 0x7f)
+                       continue;
+               vol |= spec->vol[ofs+i] & WM_VOL_MUTE;
+               if (vol != spec->vol[ofs+i]) {
+                       spec->vol[ofs+i] = vol;
+                       idx  = WM_DAC_ATTEN + ofs + i;
+                       wm_set_vol(ice, idx, spec->vol[ofs+i],
+                                  spec->master[i]);
                        change = 1;
                }
        }
@@ -489,19 +512,22 @@ static int wm_mute_info(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info
 static int wm_mute_get(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
 {
        struct snd_ice1712 *ice = snd_kcontrol_chip(kcontrol);
+       struct phase28_spec *spec = ice->spec;
        int voices, ofs, i;
 
        voices = kcontrol->private_value >> 8;
        ofs = kcontrol->private_value & 0xFF;
 
        for (i = 0; i < voices; i++)
-               ucontrol->value.integer.value[i] = (ice->spec.phase28.vol[ofs+i] & WM_VOL_MUTE) ? 0 : 1;
+               ucontrol->value.integer.value[i] =
+                       (spec->vol[ofs+i] & WM_VOL_MUTE) ? 0 : 1;
        return 0;
 }
 
 static int wm_mute_put(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
 {
        struct snd_ice1712 *ice = snd_kcontrol_chip(kcontrol);
+       struct phase28_spec *spec = ice->spec;
        int change = 0, voices, ofs, i;
 
        voices = kcontrol->private_value >> 8;
@@ -509,13 +535,13 @@ static int wm_mute_put(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value
 
        snd_ice1712_save_gpio_status(ice);
        for (i = 0; i < voices; i++) {
-               int val = (ice->spec.phase28.vol[ofs + i] & WM_VOL_MUTE) ? 0 : 1;
+               int val = (spec->vol[ofs + i] & WM_VOL_MUTE) ? 0 : 1;
                if (ucontrol->value.integer.value[i] != val) {
-                       ice->spec.phase28.vol[ofs + i] &= ~WM_VOL_MUTE;
-                       ice->spec.phase28.vol[ofs + i] |=
+                       spec->vol[ofs + i] &= ~WM_VOL_MUTE;
+                       spec->vol[ofs + i] |=
                                ucontrol->value.integer.value[i] ? 0 : WM_VOL_MUTE;
-                       wm_set_vol(ice, ofs + i, ice->spec.phase28.vol[ofs + i],
-                                  ice->spec.phase28.master[i]);
+                       wm_set_vol(ice, ofs + i, spec->vol[ofs + i],
+                                  spec->master[i]);
                        change = 1;
                }
        }
@@ -532,29 +558,33 @@ static int wm_mute_put(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value
 static int wm_master_mute_get(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
 {
        struct snd_ice1712 *ice = snd_kcontrol_chip(kcontrol);
+       struct phase28_spec *spec = ice->spec;
 
-       ucontrol->value.integer.value[0] = (ice->spec.phase28.master[0] & WM_VOL_MUTE) ? 0 : 1;
-       ucontrol->value.integer.value[1] = (ice->spec.phase28.master[1] & WM_VOL_MUTE) ? 0 : 1;
+       ucontrol->value.integer.value[0] =
+               (spec->master[0] & WM_VOL_MUTE) ? 0 : 1;
+       ucontrol->value.integer.value[1] =
+               (spec->master[1] & WM_VOL_MUTE) ? 0 : 1;
        return 0;
 }
 
 static int wm_master_mute_put(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
 {
        struct snd_ice1712 *ice = snd_kcontrol_chip(kcontrol);
+       struct phase28_spec *spec = ice->spec;
        int change = 0, i;
 
        snd_ice1712_save_gpio_status(ice);
        for (i = 0; i < 2; i++) {
-               int val = (ice->spec.phase28.master[i] & WM_VOL_MUTE) ? 0 : 1;
+               int val = (spec->master[i] & WM_VOL_MUTE) ? 0 : 1;
                if (ucontrol->value.integer.value[i] != val) {
                        int dac;
-                       ice->spec.phase28.master[i] &= ~WM_VOL_MUTE;
-                       ice->spec.phase28.master[i] |=
+                       spec->master[i] &= ~WM_VOL_MUTE;
+                       spec->master[i] |=
                                ucontrol->value.integer.value[i] ? 0 : WM_VOL_MUTE;
                        for (dac = 0; dac < ice->num_total_dacs; dac += 2)
                                wm_set_vol(ice, WM_DAC_ATTEN + dac + i,
-                                          ice->spec.phase28.vol[dac + i],
-                                          ice->spec.phase28.master[i]);
+                                          spec->vol[dac + i],
+                                          spec->master[i]);
                        change = 1;
                }
        }
@@ -595,8 +625,10 @@ static int wm_pcm_vol_put(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_val
        unsigned short ovol, nvol;
        int change = 0;
 
-       snd_ice1712_save_gpio_status(ice);
        nvol = ucontrol->value.integer.value[0];
+       if (nvol > PCM_RES)
+               return -EINVAL;
+       snd_ice1712_save_gpio_status(ice);
        nvol = (nvol ? (nvol + PCM_MIN) : 0) & 0xff;
        ovol = wm_get(ice, WM_DAC_DIG_MASTER_ATTEN) & 0xff;
        if (ovol != nvol) {
index faefd52c1b8032d82307fd8a3ecd457db51a7cbc..4945c81e8a96094d2c3bb73f9555a9d5ee6cd8b5 100644 (file)
@@ -21,7 +21,6 @@
  *
  */
 
-#include <sound/driver.h>
 #include <asm/io.h>
 #include <linux/delay.h>
 #include <linux/interrupt.h>
index 4180f9739ecb9b367a385e6fbae738d022ada30f..48cf40a8f32a1b0c0f9c763bdd31feb2e83f0bc9 100644 (file)
@@ -54,7 +54,6 @@
  *
  */      
 
-#include <sound/driver.h>
 #include <asm/io.h>
 #include <linux/delay.h>
 #include <linux/interrupt.h>
 #include "stac946x.h"
 #include <sound/tlv.h>
 
+struct prodigy192_spec {
+       struct ak4114 *ak4114;
+       /* rate change needs atomic mute/unmute of all dacs*/
+       struct mutex mute_mutex;
+};
+
 static inline void stac9460_put(struct snd_ice1712 *ice, int reg, unsigned char val)
 {
        snd_vt1724_write_i2c(ice, PRODIGY192_STAC9460_ADDR, reg, val);
@@ -81,6 +86,24 @@ static inline unsigned char stac9460_get(struct snd_ice1712 *ice, int reg)
 /*
  * DAC mute control
  */
+
+/*
+ * idx = STAC9460 volume register number, mute: 0 = mute, 1 = unmute
+ */
+static int stac9460_dac_mute(struct snd_ice1712 *ice, int idx,
+               unsigned char mute)
+{
+       unsigned char new, old;
+       int change;
+       old = stac9460_get(ice, idx);
+       new = (~mute << 7 & 0x80) | (old & ~0x80);
+       change = (new != old);
+       if (change)
+               /*printk ("Volume register 0x%02x: 0x%02x\n", idx, new);*/
+               stac9460_put(ice, idx, new);
+       return change;
+}
+
 #define stac9460_dac_mute_info         snd_ctl_boolean_mono_info
 
 static int stac9460_dac_mute_get(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
@@ -101,20 +124,19 @@ static int stac9460_dac_mute_get(struct snd_kcontrol *kcontrol, struct snd_ctl_e
 static int stac9460_dac_mute_put(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
 {
        struct snd_ice1712 *ice = snd_kcontrol_chip(kcontrol);
-       unsigned char new, old;
-       int idx;
-       int change;
+       struct prodigy192_spec *spec = ice->spec;
+       int idx, change;
 
        if (kcontrol->private_value)
                idx = STAC946X_MASTER_VOLUME;
        else
                idx  = snd_ctl_get_ioffidx(kcontrol, &ucontrol->id) + STAC946X_LF_VOLUME;
-       old = stac9460_get(ice, idx);
-       new = (~ucontrol->value.integer.value[0]<< 7 & 0x80) | (old & ~0x80);
-       change = (new != old);
-       if (change)
-               stac9460_put(ice, idx, new);
-
+       /* due to possible conflicts with stac9460_set_rate_val, mutexing */
+       mutex_lock(&spec->mute_mutex);
+       /*printk("Mute put: reg 0x%02x, ctrl value: 0x%02x\n", idx,
+               ucontrol->value.integer.value[0]);*/
+       change = stac9460_dac_mute(ice, idx, ucontrol->value.integer.value[0]);
+       mutex_unlock(&spec->mute_mutex);
        return change;
 }
 
@@ -162,6 +184,8 @@ static int stac9460_dac_vol_put(struct snd_kcontrol *kcontrol, struct snd_ctl_el
        ovol = 0x7f - (tmp & 0x7f);
        change = (ovol != nvol);
        if (change) {
+               ovol =  (0x7f - nvol) | (tmp & 0x80);
+               /*printk("DAC Volume: reg 0x%02x: 0x%02x\n", idx, ovol);*/
                stac9460_put(ice, idx, (0x7f - nvol) | (tmp & 0x80));
        }
        return change;
@@ -241,7 +265,7 @@ static int stac9460_adc_vol_put(struct snd_kcontrol *kcontrol, struct snd_ctl_el
 
        for (i = 0; i < 2; ++i) {
                reg = STAC946X_MIC_L_VOLUME + i;
-               nvol = ucontrol->value.integer.value[i];
+               nvol = ucontrol->value.integer.value[i] & 0x0f;
                ovol = 0x0f - stac9460_get(ice, reg);
                change = ((ovol & 0x0f)  != nvol);
                if (change)
@@ -251,121 +275,6 @@ static int stac9460_adc_vol_put(struct snd_kcontrol *kcontrol, struct snd_ctl_el
        return change;
 }
 
-#if 0
-/*
- * Headphone Amplifier
- */
-static int aureon_set_headphone_amp(struct snd_ice1712 *ice, int enable)
-{
-       unsigned int tmp, tmp2;
-
-       tmp2 = tmp = snd_ice1712_gpio_read(ice);
-       if (enable)
-               tmp |= AUREON_HP_SEL;
-       else
-               tmp &= ~ AUREON_HP_SEL;
-       if (tmp != tmp2) {
-               snd_ice1712_gpio_write(ice, tmp);
-               return 1;
-       }
-       return 0;
-}
-
-static int aureon_get_headphone_amp(struct snd_ice1712 *ice)
-{
-       unsigned int tmp = snd_ice1712_gpio_read(ice);
-
-       return ( tmp & AUREON_HP_SEL )!= 0;
-}
-
-#define aureon_bool_info       snd_ctl_boolean_mono_info
-
-static int aureon_hpamp_get(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
-{
-       struct snd_ice1712 *ice = snd_kcontrol_chip(kcontrol);
-
-       ucontrol->value.integer.value[0] = aureon_get_headphone_amp(ice);
-       return 0;
-}
-
-
-static int aureon_hpamp_put(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
-{
-       struct snd_ice1712 *ice = snd_kcontrol_chip(kcontrol);
-
-       return aureon_set_headphone_amp(ice,ucontrol->value.integer.value[0]);
-}
-
-/*
- * Deemphasis
- */
-static int aureon_deemp_get(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
-{
-       struct snd_ice1712 *ice = snd_kcontrol_chip(kcontrol);
-       ucontrol->value.integer.value[0] = (wm_get(ice, WM_DAC_CTRL2) & 0xf) == 0xf;
-       return 0;
-}
-
-static int aureon_deemp_put(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
-{
-       struct snd_ice1712 *ice = snd_kcontrol_chip(kcontrol);
-       int temp, temp2;
-       temp2 = temp = wm_get(ice, WM_DAC_CTRL2);
-       if (ucontrol->value.integer.value[0])
-               temp |= 0xf;
-       else
-               temp &= ~0xf;
-       if (temp != temp2) {
-               wm_put(ice, WM_DAC_CTRL2, temp);
-               return 1;
-       }
-       return 0;
-}
-
-/*
- * ADC Oversampling
- */
-static int aureon_oversampling_info(struct snd_kcontrol *k, struct snd_ctl_elem_info *uinfo)
-{
-       static char *texts[2] = { "128x", "64x" };
-
-       uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED;
-       uinfo->count = 1;
-       uinfo->value.enumerated.items = 2;
-
-       if (uinfo->value.enumerated.item >= uinfo->value.enumerated.items)
-               uinfo->value.enumerated.item = uinfo->value.enumerated.items - 1;
-       strcpy(uinfo->value.enumerated.name, texts[uinfo->value.enumerated.item]);
-
-        return 0;
-}
-
-static int aureon_oversampling_get(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
-{
-       struct snd_ice1712 *ice = snd_kcontrol_chip(kcontrol);
-       ucontrol->value.enumerated.item[0] = (wm_get(ice, WM_MASTER) & 0x8) == 0x8;
-       return 0;
-}
-
-static int aureon_oversampling_put(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
-{
-       int temp, temp2;
-       struct snd_ice1712 *ice = snd_kcontrol_chip(kcontrol);
-
-       temp2 = temp = wm_get(ice, WM_MASTER);
-
-       if (ucontrol->value.enumerated.item[0])
-               temp |= 0x8;
-       else
-               temp &= ~0x8;
-
-       if (temp != temp2) {
-               wm_put(ice, WM_MASTER, temp);
-               return 1;
-       }
-       return 0;
-}
-#endif
 static int stac9460_mic_sw_info(struct snd_kcontrol *kcontrol,
                                struct snd_ctl_elem_info *uinfo)
 {
@@ -407,6 +316,57 @@ static int stac9460_mic_sw_put(struct snd_kcontrol *kcontrol,
                stac9460_put(ice, STAC946X_GENERAL_PURPOSE, new);
        return change;
 }
+/*
+ * Handler for setting correct codec rate - called when rate change is detected
+ */
+static void stac9460_set_rate_val(struct snd_akm4xxx *ak, unsigned int rate)
+{
+       unsigned char old, new;
+       int idx;
+       unsigned char changed[7];
+       struct snd_ice1712 *ice = ak->private_data[0];
+       struct prodigy192_spec *spec = ice->spec;
+
+       if (rate == 0)  /* no hint - S/PDIF input is master, simply return */
+               return;
+       else if (rate <= 48000)
+               new = 0x08;     /* 256x, base rate mode */
+       else if (rate <= 96000)
+               new = 0x11;     /* 256x, mid rate mode */
+       else
+               new = 0x12;     /* 128x, high rate mode */
+       old = stac9460_get(ice, STAC946X_MASTER_CLOCKING);
+       if (old == new)
+               return;
+       /* change detected, setting master clock, muting first */
+       /* due to possible conflicts with mute controls - mutexing */
+       mutex_lock(&spec->mute_mutex);
+       /* we have to remember current mute status for each DAC */
+       for (idx = 0; idx < 7 ; ++idx)
+               changed[idx] = stac9460_dac_mute(ice,
+                               STAC946X_MASTER_VOLUME + idx, 0);
+       /*printk("Rate change: %d, new MC: 0x%02x\n", rate, new);*/
+       stac9460_put(ice, STAC946X_MASTER_CLOCKING, new);
+       udelay(10);
+       /* unmuting - only originally unmuted dacs -
+        * i.e. those changed when muting */
+       for (idx = 0; idx < 7 ; ++idx) {
+               if (changed[idx])
+                       stac9460_dac_mute(ice, STAC946X_MASTER_VOLUME + idx, 1);
+       }
+       mutex_unlock(&spec->mute_mutex);
+}
+
+/* using akm infrastructure for setting rate of the codec */
+static struct snd_akm4xxx akmlike_stac9460 __devinitdata = {
+       .type = NON_AKM,        /* special value */
+       .num_adcs = 6,          /* not used in any way, just for completeness */
+       .num_dacs = 2,
+       .ops = {
+               .set_rate_val = stac9460_set_rate_val
+       }
+};
+
 
 static const DECLARE_TLV_DB_SCALE(db_scale_dac, -19125, 75, 0);
 static const DECLARE_TLV_DB_SCALE(db_scale_adc, 0, 150, 0);
@@ -483,39 +443,8 @@ static struct snd_kcontrol_new stac_controls[] __devinitdata = {
                .put = stac9460_mic_sw_put,
 
        },
-#if 0
-       {
-               .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
-               .name = "Capture Route",
-               .info = wm_adc_mux_info,
-               .get = wm_adc_mux_get,
-               .put = wm_adc_mux_put,
-       },
-       {
-               .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
-               .name = "Headphone Amplifier Switch",
-               .info = aureon_bool_info,
-               .get = aureon_hpamp_get,
-               .put = aureon_hpamp_put
-       },
-       {
-               .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
-               .name = "DAC Deemphasis Switch",
-               .info = aureon_bool_info,
-               .get = aureon_deemp_get,
-               .put = aureon_deemp_put
-       },
-       {
-               .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
-               .name = "ADC Oversampling",
-               .info = aureon_oversampling_info,
-               .get = aureon_oversampling_get,
-               .put = aureon_oversampling_put
-       },
-#endif
 };
 
-
 /* AK4114 - ICE1724 connections on Prodigy192 + MI/ODI/O */
 /* CDTO (pin 32) -- GPIO11 pin 86
  * CDTI (pin 33) -- GPIO10 pin 77
@@ -712,16 +641,39 @@ static int prodigy192_ak4114_init(struct snd_ice1712 *ice)
        static const unsigned char ak4114_init_txcsb[] = {
                0x41, 0x02, 0x2c, 0x00, 0x00
        };
+       struct prodigy192_spec *spec = ice->spec;
 
        return snd_ak4114_create(ice->card,
                                 prodigy192_ak4114_read,
                                 prodigy192_ak4114_write,
                                 ak4114_init_vals, ak4114_init_txcsb,
-                                ice, &ice->spec.prodigy192.ak4114);
+                                ice, &spec->ak4114);
+}
+
+static void stac9460_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;
+       /* registers 0x0 - 0x14 */
+       for (reg = 0; reg <= 0x15; reg++) {
+               val = stac9460_get(ice, reg);
+               snd_iprintf(buffer, "0x%02x = 0x%02x\n", reg, val);
+       }
+}
+
+
+static void stac9460_proc_init(struct snd_ice1712 *ice)
+{
+       struct snd_info_entry *entry;
+       if (!snd_card_proc_new(ice->card, "stac9460_codec", &entry))
+               snd_info_set_text_ops(entry, ice, stac9460_proc_regs_read);
 }
 
+
 static int __devinit prodigy192_add_controls(struct snd_ice1712 *ice)
 {
+       struct prodigy192_spec *spec = ice->spec;
        unsigned int i;
        int err;
 
@@ -731,7 +683,7 @@ static int __devinit prodigy192_add_controls(struct snd_ice1712 *ice)
                if (err < 0)
                        return err;
        }
-       if (ice->spec.prodigy192.ak4114) {
+       if (spec->ak4114) {
                /* ak4114 is connected */
                for (i = 0; i < ARRAY_SIZE(ak4114_controls); i++) {
                        err = snd_ctl_add(ice->card,
@@ -740,12 +692,13 @@ static int __devinit prodigy192_add_controls(struct snd_ice1712 *ice)
                        if (err < 0)
                                return err;
                }
-               err = snd_ak4114_build(ice->spec.prodigy192.ak4114,
+               err = snd_ak4114_build(spec->ak4114,
                                NULL, /* ak4114 in MIO/DI/O handles no IEC958 output */
                                ice->pcm->streams[SNDRV_PCM_STREAM_CAPTURE].substream);
                if (err < 0)
                        return err;
        }
+       stac9460_proc_init(ice);
        return 0;
 }
 
@@ -778,6 +731,7 @@ static int __devinit prodigy192_init(struct snd_ice1712 *ice)
 {
        static const unsigned short stac_inits_prodigy[] = {
                STAC946X_RESET, 0,
+               STAC946X_MASTER_CLOCKING, 0x11,
 /*             STAC946X_MASTER_VOLUME, 0,
                STAC946X_LF_VOLUME, 0,
                STAC946X_RF_VOLUME, 0,
@@ -789,22 +743,39 @@ static int __devinit prodigy192_init(struct snd_ice1712 *ice)
        };
        const unsigned short *p;
        int err = 0;
+       struct snd_akm4xxx *ak;
+       struct prodigy192_spec *spec;
 
        /* prodigy 192 */
        ice->num_total_dacs = 6;
        ice->num_total_adcs = 2;
        ice->vt1720 = 0;  /* ice1724, e.g. 23 GPIOs */
        
+       spec = kzalloc(sizeof(*spec), GFP_KERNEL);
+       if (!spec)
+               return -ENOMEM;
+       ice->spec = spec;
+       mutex_init(&spec->mute_mutex);
+
        /* initialize codec */
        p = stac_inits_prodigy;
        for (; *p != (unsigned short)-1; p += 2)
                stac9460_put(ice, p[0], p[1]);
+       /* reusing the akm codecs infrastructure,
+        * for setting rate on stac9460 */
+       ak = ice->akm = kmalloc(sizeof(struct snd_akm4xxx), GFP_KERNEL);
+       if (!ak)
+               return -ENOMEM;
+       ice->akm_codecs = 1;
+       err = snd_ice1712_akm4xxx_init(ak, &akmlike_stac9460, NULL, ice);
+       if (err < 0)
+               return err;
 
        /* MI/ODI/O add on card with AK4114 */
        if (prodigy192_miodio_exists(ice)) {
                err = prodigy192_ak4114_init(ice);
                /* from this moment if err = 0 then
-                * ice->spec.prodigy192.ak4114 should not be null
+                * spec->ak4114 should not be null
                 */
                snd_printdd("AK4114 initialized with status %d\n", err);
        } else
@@ -854,6 +825,10 @@ struct snd_ice1712_card_info snd_vt1724_prodigy192_cards[] __devinitdata = {
                .build_controls = prodigy192_add_controls,
                .eeprom_size = sizeof(prodigy71_eeprom),
                .eeprom_data = prodigy71_eeprom,
+               /* the current MPU401 code loops infinitely
+                * when opening midi device
+                */
+               .no_mpu401 = 1,
        },
        { } /* terminator */
 };
diff --git a/sound/pci/ice1712/prodigy_hifi.c b/sound/pci/ice1712/prodigy_hifi.c
new file mode 100644 (file)
index 0000000..043a938
--- /dev/null
@@ -0,0 +1,1210 @@
+/*
+ *   ALSA driver for ICEnsemble VT1724 (Envy24HT)
+ *
+ *   Lowlevel functions for Audiotrak Prodigy 7.1 Hifi
+ *   based on pontis.c
+ *
+ *      Copyright (c) 2007 Julian Scheel <julian@jusst.de>
+ *      Copyright (c) 2007 allank
+ *      Copyright (c) 2004 Takashi Iwai <tiwai@suse.de>
+ *
+ *   This program is free software; you can redistribute it and/or modify
+ *   it under the terms of the GNU General Public License as published by
+ *   the Free Software Foundation; either version 2 of the License, or
+ *   (at your option) any later version.
+ *
+ *   This program is distributed in the hope that it will be useful,
+ *   but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *   GNU General Public License for more details.
+ *
+ *   You should have received a copy of the GNU General Public License
+ *   along with this program; if not, write to the Free Software
+ *   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA
+ *
+ */
+
+
+#include <asm/io.h>
+#include <linux/delay.h>
+#include <linux/interrupt.h>
+#include <linux/init.h>
+#include <linux/slab.h>
+#include <linux/mutex.h>
+
+#include <sound/core.h>
+#include <sound/info.h>
+#include <sound/tlv.h>
+
+#include "ice1712.h"
+#include "envy24ht.h"
+#include "prodigy_hifi.h"
+
+struct prodigy_hifi_spec {
+       unsigned short master[2];
+       unsigned short vol[8];
+};
+
+/* I2C addresses */
+#define WM_DEV         0x34
+
+/* WM8776 registers */
+#define WM_HP_ATTEN_L          0x00    /* headphone left attenuation */
+#define WM_HP_ATTEN_R          0x01    /* headphone left attenuation */
+#define WM_HP_MASTER           0x02    /* headphone master (both channels),
+                                               override LLR */
+#define WM_DAC_ATTEN_L         0x03    /* digital left attenuation */
+#define WM_DAC_ATTEN_R         0x04
+#define WM_DAC_MASTER          0x05
+#define WM_PHASE_SWAP          0x06    /* DAC phase swap */
+#define WM_DAC_CTRL1           0x07
+#define WM_DAC_MUTE            0x08
+#define WM_DAC_CTRL2           0x09
+#define WM_DAC_INT             0x0a
+#define WM_ADC_INT             0x0b
+#define WM_MASTER_CTRL         0x0c
+#define WM_POWERDOWN           0x0d
+#define WM_ADC_ATTEN_L         0x0e
+#define WM_ADC_ATTEN_R         0x0f
+#define WM_ALC_CTRL1           0x10
+#define WM_ALC_CTRL2           0x11
+#define WM_ALC_CTRL3           0x12
+#define WM_NOISE_GATE          0x13
+#define WM_LIMITER             0x14
+#define WM_ADC_MUX             0x15
+#define WM_OUT_MUX             0x16
+#define WM_RESET               0x17
+
+/* Analog Recording Source :- Mic, LineIn, CD/Video, */
+
+/* implement capture source select control for WM8776 */
+
+#define WM_AIN1 "AIN1"
+#define WM_AIN2 "AIN2"
+#define WM_AIN3 "AIN3"
+#define WM_AIN4 "AIN4"
+#define WM_AIN5 "AIN5"
+
+/* GPIO pins of envy24ht connected to wm8766 */
+#define WM8766_SPI_CLK  (1<<17) /* CLK, Pin97 on ICE1724 */
+#define WM8766_SPI_MD    (1<<16) /* DATA VT1724 -> WM8766, Pin96 */
+#define WM8766_SPI_ML    (1<<18) /* Latch, Pin98 */
+
+/* WM8766 registers */
+#define WM8766_DAC_CTRL         0x02   /* DAC Control */
+#define WM8766_INT_CTRL         0x03   /* Interface Control */
+#define WM8766_DAC_CTRL2       0x09
+#define WM8766_DAC_CTRL3       0x0a
+#define WM8766_RESET       0x1f
+#define WM8766_LDA1         0x00
+#define WM8766_LDA2         0x04
+#define WM8766_LDA3         0x06
+#define WM8766_RDA1         0x01
+#define WM8766_RDA2         0x05
+#define WM8766_RDA3         0x07
+#define WM8766_MUTE1       0x0C
+#define WM8766_MUTE2       0x0F
+
+
+/*
+ * Prodigy HD2
+ */
+#define AK4396_ADDR    0x00
+#define AK4396_CSN    (1 << 8)    /* CSN->GPIO8, pin 75 */
+#define AK4396_CCLK   (1 << 9)    /* CCLK->GPIO9, pin 76 */
+#define AK4396_CDTI   (1 << 10)   /* CDTI->GPIO10, pin 77 */
+
+/* ak4396 registers */
+#define AK4396_CTRL1       0x00
+#define AK4396_CTRL2       0x01
+#define AK4396_CTRL3       0x02
+#define AK4396_LCH_ATT   0x03
+#define AK4396_RCH_ATT   0x04
+
+
+/*
+ * get the current register value of WM codec
+ */
+static unsigned short wm_get(struct snd_ice1712 *ice, int reg)
+{
+       reg <<= 1;
+       return ((unsigned short)ice->akm[0].images[reg] << 8) |
+               ice->akm[0].images[reg + 1];
+}
+
+/*
+ * set the register value of WM codec and remember it
+ */
+static void wm_put_nocache(struct snd_ice1712 *ice, int reg, unsigned short val)
+{
+       unsigned short cval;
+       cval = (reg << 9) | val;
+       snd_vt1724_write_i2c(ice, WM_DEV, cval >> 8, cval & 0xff);
+}
+
+static void wm_put(struct snd_ice1712 *ice, int reg, unsigned short val)
+{
+       wm_put_nocache(ice, reg, val);
+       reg <<= 1;
+       ice->akm[0].images[reg] = val >> 8;
+       ice->akm[0].images[reg + 1] = val;
+}
+
+/*
+ * write data in the SPI mode
+ */
+
+static void set_gpio_bit(struct snd_ice1712 *ice, unsigned int bit, int val)
+{
+       unsigned int tmp = snd_ice1712_gpio_read(ice);
+       if (val)
+               tmp |= bit;
+       else
+               tmp &= ~bit;
+       snd_ice1712_gpio_write(ice, tmp);
+}
+
+/*
+ * SPI implementation for WM8766 codec - only writing supported, no readback
+ */
+
+static void wm8766_spi_send_word(struct snd_ice1712 *ice, unsigned int data)
+{
+       int i;
+       for (i = 0; i < 16; i++) {
+               set_gpio_bit(ice, WM8766_SPI_CLK, 0);
+               udelay(1);
+               set_gpio_bit(ice, WM8766_SPI_MD, data & 0x8000);
+               udelay(1);
+               set_gpio_bit(ice, WM8766_SPI_CLK, 1);
+               udelay(1);
+               data <<= 1;
+       }
+}
+
+static void wm8766_spi_write(struct snd_ice1712 *ice, unsigned int reg,
+                            unsigned int data)
+{
+       unsigned int block;
+
+       snd_ice1712_gpio_set_dir(ice, WM8766_SPI_MD|
+                                       WM8766_SPI_CLK|WM8766_SPI_ML);
+       snd_ice1712_gpio_set_mask(ice, ~(WM8766_SPI_MD|
+                                       WM8766_SPI_CLK|WM8766_SPI_ML));
+       /* latch must be low when writing */
+       set_gpio_bit(ice, WM8766_SPI_ML, 0);
+       block = (reg << 9) | (data & 0x1ff);
+       wm8766_spi_send_word(ice, block); /* REGISTER ADDRESS */
+       /* release latch */
+       set_gpio_bit(ice, WM8766_SPI_ML, 1);
+       udelay(1);
+       /* restore */
+       snd_ice1712_gpio_set_mask(ice, ice->gpio.write_mask);
+       snd_ice1712_gpio_set_dir(ice, ice->gpio.direction);
+}
+
+
+/*
+ * serial interface for ak4396 - only writing supported, no readback
+ */
+
+static void ak4396_send_word(struct snd_ice1712 *ice, unsigned int data)
+{
+       int i;
+       for (i = 0; i < 16; i++) {
+               set_gpio_bit(ice, AK4396_CCLK, 0);
+               udelay(1);
+               set_gpio_bit(ice, AK4396_CDTI, data & 0x8000);
+               udelay(1);
+               set_gpio_bit(ice, AK4396_CCLK, 1);
+               udelay(1);
+               data <<= 1;
+       }
+}
+
+static void ak4396_write(struct snd_ice1712 *ice, unsigned int reg,
+                        unsigned int data)
+{
+       unsigned int block;
+
+       snd_ice1712_gpio_set_dir(ice, AK4396_CSN|AK4396_CCLK|AK4396_CDTI);
+       snd_ice1712_gpio_set_mask(ice, ~(AK4396_CSN|AK4396_CCLK|AK4396_CDTI));
+       /* latch must be low when writing */
+       set_gpio_bit(ice, AK4396_CSN, 0); 
+       block =  ((AK4396_ADDR & 0x03) << 14) | (1 << 13) |
+                       ((reg & 0x1f) << 8) | (data & 0xff);
+       ak4396_send_word(ice, block); /* REGISTER ADDRESS */
+       /* release latch */
+       set_gpio_bit(ice, AK4396_CSN, 1);
+       udelay(1);
+       /* restore */
+       snd_ice1712_gpio_set_mask(ice, ice->gpio.write_mask);
+       snd_ice1712_gpio_set_dir(ice, ice->gpio.direction);
+}
+
+
+/*
+ * ak4396 mixers
+ */
+
+
+
+/*
+ * DAC volume attenuation mixer control (-64dB to 0dB)
+ */
+
+static int ak4396_dac_vol_info(struct snd_kcontrol *kcontrol,
+                              struct snd_ctl_elem_info *uinfo)
+{
+       uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
+       uinfo->count = 2;
+       uinfo->value.integer.min = 0;   /* mute */
+       uinfo->value.integer.max = 0xFF; /* linear */
+       return 0;
+}
+
+static int ak4396_dac_vol_get(struct snd_kcontrol *kcontrol,
+                             struct snd_ctl_elem_value *ucontrol)
+{
+       struct snd_ice1712 *ice = snd_kcontrol_chip(kcontrol);
+       struct prodigy_hifi_spec *spec = ice->spec;
+       int i;
+       
+       for (i = 0; i < 2; i++)
+               ucontrol->value.integer.value[i] = spec->vol[i];
+
+       return 0;
+}
+
+static int ak4396_dac_vol_put(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
+{
+       struct snd_ice1712 *ice = snd_kcontrol_chip(kcontrol);
+       struct prodigy_hifi_spec *spec = ice->spec;
+       int i;
+       int change = 0;
+       
+       mutex_lock(&ice->gpio_mutex);
+       for (i = 0; i < 2; i++) {
+               if (ucontrol->value.integer.value[i] != spec->vol[i]) {
+                       spec->vol[i] = ucontrol->value.integer.value[i];
+                       ak4396_write(ice, AK4396_LCH_ATT + i,
+                                    spec->vol[i] & 0xff);
+                       change = 1;
+               }
+       }
+       mutex_unlock(&ice->gpio_mutex);
+       return change;
+}
+
+static const DECLARE_TLV_DB_SCALE(db_scale_wm_dac, -12700, 100, 1);
+
+static struct snd_kcontrol_new prodigy_hd2_controls[] __devinitdata = {
+    {
+       .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+       .access = (SNDRV_CTL_ELEM_ACCESS_READWRITE |
+               SNDRV_CTL_ELEM_ACCESS_TLV_READ),
+       .name = "Front Playback Volume",
+       .info = ak4396_dac_vol_info,
+       .get = ak4396_dac_vol_get,
+       .put = ak4396_dac_vol_put,
+       .tlv = { .p = db_scale_wm_dac },
+    },
+};
+
+
+/* --------------- */
+
+/*
+ * Logarithmic volume values for WM87*6
+ * Computed as 20 * Log10(255 / x)
+ */
+static const unsigned char wm_vol[256] = {
+       127, 48, 42, 39, 36, 34, 33, 31, 30, 29, 28, 27, 27, 26, 25, 25, 24, 24, 23,
+       23, 22, 22, 21, 21, 21, 20, 20, 20, 19, 19, 19, 18, 18, 18, 18, 17, 17, 17,
+       17, 16, 16, 16, 16, 15, 15, 15, 15, 15, 15, 14, 14, 14, 14, 14, 13, 13, 13,
+       13, 13, 13, 13, 12, 12, 12, 12, 12, 12, 12, 11, 11, 11, 11, 11, 11, 11, 11,
+       11, 10, 10, 10, 10, 10, 10, 10, 10, 10, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 8, 8,
+       8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 6, 6, 6,
+       6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5,
+       5, 5, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 3, 3, 3, 3, 3,
+       3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+       2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+       1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+       0, 0
+};
+
+#define WM_VOL_MAX     (sizeof(wm_vol) - 1)
+#define WM_VOL_MUTE    0x8000
+
+
+#define DAC_0dB        0xff
+#define DAC_RES        128
+#define DAC_MIN        (DAC_0dB - DAC_RES)
+
+
+static void wm_set_vol(struct snd_ice1712 *ice, unsigned int index,
+                      unsigned short vol, unsigned short master)
+{
+       unsigned char nvol;
+       
+       if ((master & WM_VOL_MUTE) || (vol & WM_VOL_MUTE))
+               nvol = 0;
+       else {
+               nvol = (((vol & ~WM_VOL_MUTE) * (master & ~WM_VOL_MUTE)) / 128)
+                               & WM_VOL_MAX;
+               nvol = (nvol ? (nvol + DAC_MIN) : 0) & 0xff;
+       }
+       
+       wm_put(ice, index, nvol);
+       wm_put_nocache(ice, index, 0x100 | nvol);
+}
+
+static void wm8766_set_vol(struct snd_ice1712 *ice, unsigned int index,
+                          unsigned short vol, unsigned short master)
+{
+       unsigned char nvol;
+       
+       if ((master & WM_VOL_MUTE) || (vol & WM_VOL_MUTE))
+               nvol = 0;
+       else {
+               nvol = (((vol & ~WM_VOL_MUTE) * (master & ~WM_VOL_MUTE)) / 128)
+                               & WM_VOL_MAX;
+               nvol = (nvol ? (nvol + DAC_MIN) : 0) & 0xff;
+       }
+
+       wm8766_spi_write(ice, index, (0x0100 | nvol));
+}
+
+
+/*
+ * DAC volume attenuation mixer control (-64dB to 0dB)
+ */
+
+static int wm_dac_vol_info(struct snd_kcontrol *kcontrol,
+                          struct snd_ctl_elem_info *uinfo)
+{
+       uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
+       uinfo->count = 2;
+       uinfo->value.integer.min = 0;   /* mute */
+       uinfo->value.integer.max = DAC_RES;     /* 0dB, 0.5dB step */
+       return 0;
+}
+
+static int wm_dac_vol_get(struct snd_kcontrol *kcontrol,
+                         struct snd_ctl_elem_value *ucontrol)
+{
+       struct snd_ice1712 *ice = snd_kcontrol_chip(kcontrol);
+       struct prodigy_hifi_spec *spec = ice->spec;
+       int i;
+
+       for (i = 0; i < 2; i++)
+               ucontrol->value.integer.value[i] =
+                       spec->vol[2 + i] & ~WM_VOL_MUTE;
+       return 0;
+}
+
+static int wm_dac_vol_put(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
+{
+       struct snd_ice1712 *ice = snd_kcontrol_chip(kcontrol);
+       struct prodigy_hifi_spec *spec = ice->spec;
+       int i, idx, change = 0;
+
+       mutex_lock(&ice->gpio_mutex);
+       for (i = 0; i < 2; i++) {
+               if (ucontrol->value.integer.value[i] != spec->vol[2 + i]) {
+                       idx = WM_DAC_ATTEN_L + i;
+                       spec->vol[2 + i] &= WM_VOL_MUTE;
+                       spec->vol[2 + i] |= ucontrol->value.integer.value[i];
+                       wm_set_vol(ice, idx, spec->vol[2 + i], spec->master[i]);
+                       change = 1;
+               }
+       }
+       mutex_unlock(&ice->gpio_mutex);
+       return change;
+}
+
+
+/*
+ * WM8766 DAC volume attenuation mixer control
+ */
+static int wm8766_vol_info(struct snd_kcontrol *kcontrol,
+                          struct snd_ctl_elem_info *uinfo)
+{
+       int voices = kcontrol->private_value >> 8;
+       uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
+       uinfo->count = voices;
+       uinfo->value.integer.min = 0;           /* mute */
+       uinfo->value.integer.max = DAC_RES;     /* 0dB */
+       return 0;
+}
+
+static int wm8766_vol_get(struct snd_kcontrol *kcontrol,
+                         struct snd_ctl_elem_value *ucontrol)
+{
+       struct snd_ice1712 *ice = snd_kcontrol_chip(kcontrol);
+       struct prodigy_hifi_spec *spec = ice->spec;
+       int i, ofs, voices;
+
+       voices = kcontrol->private_value >> 8;
+       ofs = kcontrol->private_value & 0xff;
+       for (i = 0; i < voices; i++)
+               ucontrol->value.integer.value[i] = spec->vol[ofs + i];
+       return 0;
+}
+
+static int wm8766_vol_put(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
+{
+       struct snd_ice1712 *ice = snd_kcontrol_chip(kcontrol);
+       struct prodigy_hifi_spec *spec = ice->spec;
+       int i, idx, ofs, voices;
+       int change = 0;
+
+       voices = kcontrol->private_value >> 8;
+       ofs = kcontrol->private_value & 0xff;
+       mutex_lock(&ice->gpio_mutex);
+       for (i = 0; i < voices; i++) {
+               if (ucontrol->value.integer.value[i] != spec->vol[ofs + i]) {
+                       idx = WM8766_LDA1 + ofs + i;
+                       spec->vol[ofs + i] &= WM_VOL_MUTE;
+                       spec->vol[ofs + i] |= ucontrol->value.integer.value[i];
+                       wm8766_set_vol(ice, idx,
+                                      spec->vol[ofs + i], spec->master[i]);
+                       change = 1;
+               }
+       }
+       mutex_unlock(&ice->gpio_mutex);
+       return change;
+}
+
+/*
+ * Master volume attenuation mixer control / applied to WM8776+WM8766
+ */
+static int wm_master_vol_info(struct snd_kcontrol *kcontrol,
+                             struct snd_ctl_elem_info *uinfo)
+{
+       uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
+       uinfo->count = 2;
+       uinfo->value.integer.min = 0;
+       uinfo->value.integer.max = DAC_RES;
+       return 0;
+}
+
+static int wm_master_vol_get(struct snd_kcontrol *kcontrol,
+                            struct snd_ctl_elem_value *ucontrol)
+{
+       struct snd_ice1712 *ice = snd_kcontrol_chip(kcontrol);
+       struct prodigy_hifi_spec *spec = ice->spec;
+       int i;
+       for (i = 0; i < 2; i++)
+               ucontrol->value.integer.value[i] = spec->master[i];
+       return 0;
+}
+
+static int wm_master_vol_put(struct snd_kcontrol *kcontrol,
+                            struct snd_ctl_elem_value *ucontrol)
+{
+       struct snd_ice1712 *ice = snd_kcontrol_chip(kcontrol);
+       struct prodigy_hifi_spec *spec = ice->spec;
+       int ch, change = 0;
+
+       mutex_lock(&ice->gpio_mutex);
+       for (ch = 0; ch < 2; ch++) {
+               if (ucontrol->value.integer.value[ch] != spec->master[ch]) {
+                       spec->master[ch] = ucontrol->value.integer.value[ch];
+
+                       /* Apply to front DAC */
+                       wm_set_vol(ice, WM_DAC_ATTEN_L + ch,
+                                  spec->vol[2 + ch], spec->master[ch]);
+
+                       wm8766_set_vol(ice, WM8766_LDA1 + ch,
+                                      spec->vol[0 + ch], spec->master[ch]);
+
+                       wm8766_set_vol(ice, WM8766_LDA2 + ch,
+                                      spec->vol[4 + ch], spec->master[ch]);
+
+                       wm8766_set_vol(ice, WM8766_LDA3 + ch,
+                                      spec->vol[6 + ch], spec->master[ch]);
+                       change = 1;
+               }
+       }
+       mutex_unlock(&ice->gpio_mutex); 
+       return change;
+}
+
+
+/* KONSTI */
+
+static int wm_adc_mux_enum_info(struct snd_kcontrol *kcontrol,
+                               struct snd_ctl_elem_info *uinfo)
+{
+       static char* texts[32] = {
+               "NULL", WM_AIN1, WM_AIN2, WM_AIN1 "+" WM_AIN2,
+               WM_AIN3, WM_AIN1 "+" WM_AIN3, WM_AIN2 "+" WM_AIN3,
+               WM_AIN1 "+" WM_AIN2 "+" WM_AIN3,
+               WM_AIN4, WM_AIN1 "+" WM_AIN4, WM_AIN2 "+" WM_AIN4,
+               WM_AIN1 "+" WM_AIN2 "+" WM_AIN4,
+               WM_AIN3 "+" WM_AIN4, WM_AIN1 "+" WM_AIN3 "+" WM_AIN4,
+               WM_AIN2 "+" WM_AIN3 "+" WM_AIN4,
+               WM_AIN1 "+" WM_AIN2 "+" WM_AIN3 "+" WM_AIN4,
+               WM_AIN5, WM_AIN1 "+" WM_AIN5, WM_AIN2 "+" WM_AIN5,
+               WM_AIN1 "+" WM_AIN2 "+" WM_AIN5,
+               WM_AIN3 "+" WM_AIN5, WM_AIN1 "+" WM_AIN3 "+" WM_AIN5,
+               WM_AIN2 "+" WM_AIN3 "+" WM_AIN5,
+               WM_AIN1 "+" WM_AIN2 "+" WM_AIN3 "+" WM_AIN5,
+               WM_AIN4 "+" WM_AIN5, WM_AIN1 "+" WM_AIN4 "+" WM_AIN5,
+               WM_AIN2 "+" WM_AIN4 "+" WM_AIN5,
+               WM_AIN1 "+" WM_AIN2 "+" WM_AIN4 "+" WM_AIN5,
+               WM_AIN3 "+" WM_AIN4 "+" WM_AIN5,
+               WM_AIN1 "+" WM_AIN3 "+" WM_AIN4 "+" WM_AIN5,
+               WM_AIN2 "+" WM_AIN3 "+" WM_AIN4 "+" WM_AIN5,
+               WM_AIN1 "+" WM_AIN2 "+" WM_AIN3 "+" WM_AIN4 "+" WM_AIN5
+       };
+
+       uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED;
+       uinfo->count = 1;
+       uinfo->value.enumerated.items = 32;
+       if (uinfo->value.enumerated.item > 31)
+               uinfo->value.enumerated.item = 31;
+       strcpy(uinfo->value.enumerated.name,
+              texts[uinfo->value.enumerated.item]);
+       return 0;
+}
+
+static int wm_adc_mux_enum_get(struct snd_kcontrol *kcontrol,
+                              struct snd_ctl_elem_value *ucontrol)
+{
+       struct snd_ice1712 *ice = snd_kcontrol_chip(kcontrol);
+
+       mutex_lock(&ice->gpio_mutex);
+       ucontrol->value.integer.value[0] = wm_get(ice, WM_ADC_MUX) & 0x1f;
+       mutex_unlock(&ice->gpio_mutex);
+       return 0;
+}
+
+static int wm_adc_mux_enum_put(struct snd_kcontrol *kcontrol,
+                              struct snd_ctl_elem_value *ucontrol)
+{
+       struct snd_ice1712 *ice = snd_kcontrol_chip(kcontrol);
+       unsigned short oval, nval;
+       int change = 0;
+
+       mutex_lock(&ice->gpio_mutex);
+       oval = wm_get(ice, WM_ADC_MUX);
+       nval = (oval & 0xe0) | ucontrol->value.integer.value[0];
+       if (nval != oval) {
+               wm_put(ice, WM_ADC_MUX, nval);
+               change = 1;
+       }
+       mutex_unlock(&ice->gpio_mutex);
+       return change;
+}
+
+/* KONSTI */
+
+/*
+ * ADC gain mixer control (-64dB to 0dB)
+ */
+
+#define ADC_0dB        0xcf
+#define ADC_RES        128
+#define ADC_MIN        (ADC_0dB - ADC_RES)
+
+static int wm_adc_vol_info(struct snd_kcontrol *kcontrol,
+                          struct snd_ctl_elem_info *uinfo)
+{
+       uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
+       uinfo->count = 2;
+       uinfo->value.integer.min = 0;   /* mute (-64dB) */
+       uinfo->value.integer.max = ADC_RES;     /* 0dB, 0.5dB step */
+       return 0;
+}
+
+static int wm_adc_vol_get(struct snd_kcontrol *kcontrol,
+                         struct snd_ctl_elem_value *ucontrol)
+{
+       struct snd_ice1712 *ice = snd_kcontrol_chip(kcontrol);
+       unsigned short val;
+       int i;
+
+       mutex_lock(&ice->gpio_mutex);
+       for (i = 0; i < 2; i++) {
+               val = wm_get(ice, WM_ADC_ATTEN_L + i) & 0xff;
+               val = val > ADC_MIN ? (val - ADC_MIN) : 0;
+               ucontrol->value.integer.value[i] = val;
+       }
+       mutex_unlock(&ice->gpio_mutex);
+       return 0;
+}
+
+static int wm_adc_vol_put(struct snd_kcontrol *kcontrol,
+                         struct snd_ctl_elem_value *ucontrol)
+{
+       struct snd_ice1712 *ice = snd_kcontrol_chip(kcontrol);
+       unsigned short ovol, nvol;
+       int i, idx, change = 0;
+
+       mutex_lock(&ice->gpio_mutex);
+       for (i = 0; i < 2; i++) {
+               nvol = ucontrol->value.integer.value[i];
+               nvol = nvol ? (nvol + ADC_MIN) : 0;
+               idx  = WM_ADC_ATTEN_L + i;
+               ovol = wm_get(ice, idx) & 0xff;
+               if (ovol != nvol) {
+                       wm_put(ice, idx, nvol);
+                       change = 1;
+               }
+       }
+       mutex_unlock(&ice->gpio_mutex);
+       return change;
+}
+
+/*
+ * ADC input mux mixer control
+ */
+#define wm_adc_mux_info                snd_ctl_boolean_mono_info
+
+static int wm_adc_mux_get(struct snd_kcontrol *kcontrol,
+                         struct snd_ctl_elem_value *ucontrol)
+{
+       struct snd_ice1712 *ice = snd_kcontrol_chip(kcontrol);
+       int bit = kcontrol->private_value;
+
+       mutex_lock(&ice->gpio_mutex);
+       ucontrol->value.integer.value[0] =
+               (wm_get(ice, WM_ADC_MUX) & (1 << bit)) ? 1 : 0;
+       mutex_unlock(&ice->gpio_mutex);
+       return 0;
+}
+
+static int wm_adc_mux_put(struct snd_kcontrol *kcontrol,
+                         struct snd_ctl_elem_value *ucontrol)
+{
+       struct snd_ice1712 *ice = snd_kcontrol_chip(kcontrol);
+       int bit = kcontrol->private_value;
+       unsigned short oval, nval;
+       int change;
+
+       mutex_lock(&ice->gpio_mutex);
+       nval = oval = wm_get(ice, WM_ADC_MUX);
+       if (ucontrol->value.integer.value[0])
+               nval |= (1 << bit);
+       else
+               nval &= ~(1 << bit);
+       change = nval != oval;
+       if (change) {
+               wm_put(ice, WM_ADC_MUX, nval);
+       }
+       mutex_unlock(&ice->gpio_mutex);
+       return 0;
+}
+
+/*
+ * Analog bypass (In -> Out)
+ */
+#define wm_bypass_info         snd_ctl_boolean_mono_info
+
+static int wm_bypass_get(struct snd_kcontrol *kcontrol,
+                        struct snd_ctl_elem_value *ucontrol)
+{
+       struct snd_ice1712 *ice = snd_kcontrol_chip(kcontrol);
+
+       mutex_lock(&ice->gpio_mutex);
+       ucontrol->value.integer.value[0] =
+               (wm_get(ice, WM_OUT_MUX) & 0x04) ? 1 : 0;
+       mutex_unlock(&ice->gpio_mutex);
+       return 0;
+}
+
+static int wm_bypass_put(struct snd_kcontrol *kcontrol,
+                        struct snd_ctl_elem_value *ucontrol)
+{
+       struct snd_ice1712 *ice = snd_kcontrol_chip(kcontrol);
+       unsigned short val, oval;
+       int change = 0;
+
+       mutex_lock(&ice->gpio_mutex);
+       val = oval = wm_get(ice, WM_OUT_MUX);
+       if (ucontrol->value.integer.value[0])
+               val |= 0x04;
+       else
+               val &= ~0x04;
+       if (val != oval) {
+               wm_put(ice, WM_OUT_MUX, val);
+               change = 1;
+       }
+       mutex_unlock(&ice->gpio_mutex);
+       return change;
+}
+
+/*
+ * Left/Right swap
+ */
+#define wm_chswap_info         snd_ctl_boolean_mono_info
+
+static int wm_chswap_get(struct snd_kcontrol *kcontrol,
+                        struct snd_ctl_elem_value *ucontrol)
+{
+       struct snd_ice1712 *ice = snd_kcontrol_chip(kcontrol);
+
+       mutex_lock(&ice->gpio_mutex);
+       ucontrol->value.integer.value[0] =
+                       (wm_get(ice, WM_DAC_CTRL1) & 0xf0) != 0x90;
+       mutex_unlock(&ice->gpio_mutex);
+       return 0;
+}
+
+static int wm_chswap_put(struct snd_kcontrol *kcontrol,
+                        struct snd_ctl_elem_value *ucontrol)
+{
+       struct snd_ice1712 *ice = snd_kcontrol_chip(kcontrol);
+       unsigned short val, oval;
+       int change = 0;
+
+       mutex_lock(&ice->gpio_mutex);
+       oval = wm_get(ice, WM_DAC_CTRL1);
+       val = oval & 0x0f;
+       if (ucontrol->value.integer.value[0])
+               val |= 0x60;
+       else
+               val |= 0x90;
+       if (val != oval) {
+               wm_put(ice, WM_DAC_CTRL1, val);
+               wm_put_nocache(ice, WM_DAC_CTRL1, val);
+               change = 1;
+       }
+       mutex_unlock(&ice->gpio_mutex);
+       return change;
+}
+
+
+/*
+ * mixers
+ */
+
+static struct snd_kcontrol_new prodigy_hifi_controls[] __devinitdata = {
+       {
+               .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+               .access = (SNDRV_CTL_ELEM_ACCESS_READWRITE |
+                          SNDRV_CTL_ELEM_ACCESS_TLV_READ),
+               .name = "Master Playback Volume",
+               .info = wm_master_vol_info,
+               .get = wm_master_vol_get,
+               .put = wm_master_vol_put,
+               .tlv = { .p = db_scale_wm_dac }
+       },
+       {
+               .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+               .access = (SNDRV_CTL_ELEM_ACCESS_READWRITE |
+                          SNDRV_CTL_ELEM_ACCESS_TLV_READ),
+               .name = "Front Playback Volume",
+               .info = wm_dac_vol_info,
+               .get = wm_dac_vol_get,
+               .put = wm_dac_vol_put,
+               .tlv = { .p = db_scale_wm_dac },
+       },
+       {
+               .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+               .access = (SNDRV_CTL_ELEM_ACCESS_READWRITE |
+                          SNDRV_CTL_ELEM_ACCESS_TLV_READ),
+               .name = "Rear Playback Volume",
+               .info = wm8766_vol_info,
+               .get = wm8766_vol_get,
+               .put = wm8766_vol_put,
+               .private_value = (2 << 8) | 0,
+               .tlv = { .p = db_scale_wm_dac },
+       },
+       {
+               .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+               .access = (SNDRV_CTL_ELEM_ACCESS_READWRITE |
+                          SNDRV_CTL_ELEM_ACCESS_TLV_READ),
+               .name = "Center Playback Volume",
+               .info = wm8766_vol_info,
+               .get = wm8766_vol_get,
+               .put = wm8766_vol_put,
+               .private_value = (1 << 8) | 4,
+               .tlv = { .p = db_scale_wm_dac }
+       },
+       {
+               .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+               .access = (SNDRV_CTL_ELEM_ACCESS_READWRITE |
+                          SNDRV_CTL_ELEM_ACCESS_TLV_READ),
+               .name = "LFE Playback Volume",
+               .info = wm8766_vol_info,
+               .get = wm8766_vol_get,
+               .put = wm8766_vol_put,
+               .private_value = (1 << 8) | 5,
+               .tlv = { .p = db_scale_wm_dac }
+       },
+       {
+               .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+               .access = (SNDRV_CTL_ELEM_ACCESS_READWRITE |
+                          SNDRV_CTL_ELEM_ACCESS_TLV_READ),
+               .name = "Side Playback Volume",
+               .info = wm8766_vol_info,
+               .get = wm8766_vol_get,
+               .put = wm8766_vol_put,
+               .private_value = (2 << 8) | 6,
+               .tlv = { .p = db_scale_wm_dac },
+       },
+       {
+               .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+               .access = (SNDRV_CTL_ELEM_ACCESS_READWRITE |
+                          SNDRV_CTL_ELEM_ACCESS_TLV_READ),
+               .name = "Capture Volume",
+               .info = wm_adc_vol_info,
+               .get = wm_adc_vol_get,
+               .put = wm_adc_vol_put,
+               .tlv = { .p = db_scale_wm_dac },
+       },
+       {
+               .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+               .name = "CD Capture Switch",
+               .info = wm_adc_mux_info,
+               .get = wm_adc_mux_get,
+               .put = wm_adc_mux_put,
+               .private_value = 0,
+       },
+       {
+               .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+               .name = "Line Capture Switch",
+               .info = wm_adc_mux_info,
+               .get = wm_adc_mux_get,
+               .put = wm_adc_mux_put,
+               .private_value = 1,
+       },
+       {
+               .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+               .name = "Analog Bypass Switch",
+               .info = wm_bypass_info,
+               .get = wm_bypass_get,
+               .put = wm_bypass_put,
+       },
+       {
+               .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+               .name = "Swap Output Channels",
+               .info = wm_chswap_info,
+               .get = wm_chswap_get,
+               .put = wm_chswap_put,
+       },
+       {
+               .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+               .name = "Analog Capture Source",
+               .info = wm_adc_mux_enum_info,
+               .get = wm_adc_mux_enum_get,
+               .put = wm_adc_mux_enum_put,
+       },
+};
+
+/*
+ * WM codec registers
+ */
+static void wm_proc_regs_write(struct snd_info_entry *entry,
+                              struct snd_info_buffer *buffer)
+{
+       struct snd_ice1712 *ice = entry->private_data;
+       char line[64];
+       unsigned int reg, val;
+       mutex_lock(&ice->gpio_mutex);
+       while (!snd_info_get_line(buffer, line, sizeof(line))) {
+               if (sscanf(line, "%x %x", &reg, &val) != 2)
+                       continue;
+               if (reg <= 0x17 && val <= 0xffff)
+                       wm_put(ice, reg, val);
+       }
+       mutex_unlock(&ice->gpio_mutex);
+}
+
+static void wm_proc_regs_read(struct snd_info_entry *entry,
+                             struct snd_info_buffer *buffer)
+{
+       struct snd_ice1712 *ice = entry->private_data;
+       int reg, val;
+
+       mutex_lock(&ice->gpio_mutex);
+       for (reg = 0; reg <= 0x17; reg++) {
+               val = wm_get(ice, reg);
+               snd_iprintf(buffer, "%02x = %04x\n", reg, val);
+       }
+       mutex_unlock(&ice->gpio_mutex);
+}
+
+static void wm_proc_init(struct snd_ice1712 *ice)
+{
+       struct snd_info_entry *entry;
+       if (!snd_card_proc_new(ice->card, "wm_codec", &entry)) {
+               snd_info_set_text_ops(entry, ice, wm_proc_regs_read);
+               entry->mode |= S_IWUSR;
+               entry->c.text.write = wm_proc_regs_write;
+       }
+}
+
+static int __devinit prodigy_hifi_add_controls(struct snd_ice1712 *ice)
+{
+       unsigned int i;
+       int err;
+
+       for (i = 0; i < ARRAY_SIZE(prodigy_hifi_controls); i++) {
+               err = snd_ctl_add(ice->card,
+                                 snd_ctl_new1(&prodigy_hifi_controls[i], ice));
+               if (err < 0)
+                       return err;
+       }
+
+       wm_proc_init(ice);
+
+       return 0;
+}
+
+static int __devinit prodigy_hd2_add_controls(struct snd_ice1712 *ice)
+{
+       unsigned int i;
+       int err;
+
+       for (i = 0; i < ARRAY_SIZE(prodigy_hd2_controls); i++) {
+               err = snd_ctl_add(ice->card,
+                                 snd_ctl_new1(&prodigy_hd2_controls[i], ice));
+               if (err < 0)
+                       return err;
+       }
+
+       wm_proc_init(ice);
+
+       return 0;
+}
+
+
+/*
+ * initialize the chip
+ */
+static int __devinit prodigy_hifi_init(struct snd_ice1712 *ice)
+{
+       static unsigned short wm_inits[] = {
+               /* These come first to reduce init pop noise */
+               WM_ADC_MUX,     0x0003, /* ADC mute */
+               /* 0x00c0 replaced by 0x0003 */
+               
+               WM_DAC_MUTE,    0x0001, /* DAC softmute */
+               WM_DAC_CTRL1,   0x0000, /* DAC mute */
+
+               WM_POWERDOWN,   0x0008, /* All power-up except HP */
+               WM_RESET,       0x0000, /* reset */
+       };
+       static unsigned short wm_inits2[] = {
+               WM_MASTER_CTRL,  0x0022, /* 256fs, slave mode */
+               WM_DAC_INT,     0x0022, /* I2S, normal polarity, 24bit */
+               WM_ADC_INT,     0x0022, /* I2S, normal polarity, 24bit */
+               WM_DAC_CTRL1,   0x0090, /* DAC L/R */
+               WM_OUT_MUX,     0x0001, /* OUT DAC */
+               WM_HP_ATTEN_L,  0x0179, /* HP 0dB */
+               WM_HP_ATTEN_R,  0x0179, /* HP 0dB */
+               WM_DAC_ATTEN_L, 0x0000, /* DAC 0dB */
+               WM_DAC_ATTEN_L, 0x0100, /* DAC 0dB */
+               WM_DAC_ATTEN_R, 0x0000, /* DAC 0dB */
+               WM_DAC_ATTEN_R, 0x0100, /* DAC 0dB */
+               WM_PHASE_SWAP,  0x0000, /* phase normal */
+#if 0
+               WM_DAC_MASTER,  0x0100, /* DAC master muted */
+#endif
+               WM_DAC_CTRL2,   0x0000, /* no deemphasis, no ZFLG */
+               WM_ADC_ATTEN_L, 0x0000, /* ADC muted */
+               WM_ADC_ATTEN_R, 0x0000, /* ADC muted */
+#if 1
+               WM_ALC_CTRL1,   0x007b, /* */
+               WM_ALC_CTRL2,   0x0000, /* */
+               WM_ALC_CTRL3,   0x0000, /* */
+               WM_NOISE_GATE,  0x0000, /* */
+#endif
+               WM_DAC_MUTE,    0x0000, /* DAC unmute */
+               WM_ADC_MUX,     0x0003, /* ADC unmute, both CD/Line On */
+       };
+       static unsigned short wm8766_inits[] = {
+               WM8766_RESET,      0x0000,
+               WM8766_DAC_CTRL,        0x0120,
+               WM8766_INT_CTRL,        0x0022, /* I2S Normal Mode, 24 bit */
+               WM8766_DAC_CTRL2,       0x0001,
+               WM8766_DAC_CTRL3,       0x0080,
+               WM8766_LDA1,        0x0100,
+               WM8766_LDA2,        0x0100,
+               WM8766_LDA3,        0x0100,
+               WM8766_RDA1,        0x0100,
+               WM8766_RDA2,        0x0100,
+               WM8766_RDA3,        0x0100,
+               WM8766_MUTE1,      0x0000,
+               WM8766_MUTE2,      0x0000,
+       };
+
+       struct prodigy_hifi_spec *spec;
+       unsigned int i;
+
+       ice->vt1720 = 0;
+       ice->vt1724 = 1;
+
+       ice->num_total_dacs = 8;
+       ice->num_total_adcs = 1;
+
+       /* HACK - use this as the SPDIF source.
+       * don't call snd_ice1712_gpio_get/put(), otherwise it's overwritten
+       */
+       ice->gpio.saved[0] = 0;
+       /* to remeber the register values */
+
+       ice->akm = kzalloc(sizeof(struct snd_akm4xxx), GFP_KERNEL);
+       if (! ice->akm)
+               return -ENOMEM;
+       ice->akm_codecs = 1;
+
+       spec = kzalloc(sizeof(*spec), GFP_KERNEL);
+       if (!spec)
+               return -ENOMEM;
+       ice->spec = spec;
+
+       /* initialize WM8776 codec */
+       for (i = 0; i < ARRAY_SIZE(wm_inits); i += 2)
+               wm_put(ice, wm_inits[i], wm_inits[i+1]);
+       schedule_timeout_uninterruptible(1);
+       for (i = 0; i < ARRAY_SIZE(wm_inits2); i += 2)
+               wm_put(ice, wm_inits2[i], wm_inits2[i+1]);
+
+       /* initialize WM8766 codec */
+       for (i = 0; i < ARRAY_SIZE(wm8766_inits); i += 2)
+               wm8766_spi_write(ice, wm8766_inits[i], wm8766_inits[i+1]);
+
+
+       return 0;
+}
+
+
+/*
+ * initialize the chip
+ */
+static int __devinit prodigy_hd2_init(struct snd_ice1712 *ice)
+{
+       static unsigned short ak4396_inits[] = {
+               AK4396_CTRL1,      0x87,   /* I2S Normal Mode, 24 bit */
+               AK4396_CTRL2,      0x02,
+               AK4396_CTRL3,      0x00, 
+               AK4396_LCH_ATT,  0x00,
+               AK4396_RCH_ATT,  0x00,
+       };
+
+       struct prodigy_hifi_spec *spec;
+       unsigned int i;
+
+       ice->vt1720 = 0;
+       ice->vt1724 = 1;
+
+       ice->num_total_dacs = 1;
+       ice->num_total_adcs = 1;
+
+       /* HACK - use this as the SPDIF source.
+       * don't call snd_ice1712_gpio_get/put(), otherwise it's overwritten
+       */
+       ice->gpio.saved[0] = 0;
+       /* to remeber the register values */
+
+       ice->akm = kzalloc(sizeof(struct snd_akm4xxx), GFP_KERNEL);
+       if (! ice->akm)
+               return -ENOMEM;
+       ice->akm_codecs = 1;
+
+       spec = kzalloc(sizeof(*spec), GFP_KERNEL);
+       if (!spec)
+               return -ENOMEM;
+       ice->spec = spec;
+
+       /* initialize ak4396 codec */
+       /* reset codec */
+       ak4396_write(ice, AK4396_CTRL1, 0x86);
+       msleep(100);
+       ak4396_write(ice, AK4396_CTRL1, 0x87);
+                       
+       for (i = 0; i < ARRAY_SIZE(ak4396_inits); i += 2)
+               ak4396_write(ice, ak4396_inits[i], ak4396_inits[i+1]);
+
+       return 0;
+}
+
+
+static unsigned char prodigy71hifi_eeprom[] __devinitdata = {
+       0x4b,   /* SYSCONF: clock 512, spdif-in/ADC, 4DACs */
+       0x80,   /* ACLINK: I2S */
+       0xfc,   /* I2S: vol, 96k, 24bit, 192k */
+       0xc3,   /* SPDIF: out-en, out-int, spdif-in */
+       0xff,   /* GPIO_DIR */
+       0xff,   /* GPIO_DIR1 */
+       0x5f,   /* GPIO_DIR2 */
+       0x00,   /* GPIO_MASK */
+       0x00,   /* GPIO_MASK1 */
+       0x00,   /* GPIO_MASK2 */
+       0x00,   /* GPIO_STATE */
+       0x00,   /* GPIO_STATE1 */
+       0x00,   /* GPIO_STATE2 */
+};
+
+static unsigned char prodigyhd2_eeprom[] __devinitdata = {
+       0x4b,   /* SYSCONF: clock 512, spdif-in/ADC, 4DACs */
+       0x80,   /* ACLINK: I2S */
+       0xfc,   /* I2S: vol, 96k, 24bit, 192k */
+       0xc3,   /* SPDIF: out-en, out-int, spdif-in */
+       0xff,   /* GPIO_DIR */
+       0xff,   /* GPIO_DIR1 */
+       0x5f,   /* GPIO_DIR2 */
+       0x00,   /* GPIO_MASK */
+       0x00,   /* GPIO_MASK1 */
+       0x00,   /* GPIO_MASK2 */
+       0x00,   /* GPIO_STATE */
+       0x00,   /* GPIO_STATE1 */
+       0x00,   /* GPIO_STATE2 */
+};
+
+static unsigned char fortissimo4_eeprom[] __devinitdata = {
+       0x43,   /* SYSCONF: clock 512, ADC, 4DACs */    
+       0x80,   /* ACLINK: I2S */
+       0xfc,   /* I2S: vol, 96k, 24bit, 192k */
+       0xc1,   /* SPDIF: out-en, out-int */
+       0xff,   /* GPIO_DIR */
+       0xff,   /* GPIO_DIR1 */
+       0x5f,   /* GPIO_DIR2 */
+       0x00,   /* GPIO_MASK */
+       0x00,   /* GPIO_MASK1 */
+       0x00,   /* GPIO_MASK2 */
+       0x00,   /* GPIO_STATE */
+       0x00,   /* GPIO_STATE1 */
+       0x00,   /* GPIO_STATE2 */
+};
+
+/* entry point */
+struct snd_ice1712_card_info snd_vt1724_prodigy_hifi_cards[] __devinitdata = {
+       {
+               .subvendor = VT1724_SUBDEVICE_PRODIGY_HIFI,
+               .name = "Audiotrak Prodigy 7.1 HiFi",
+               .model = "prodigy71hifi",
+               .chip_init = prodigy_hifi_init,
+               .build_controls = prodigy_hifi_add_controls,
+               .eeprom_size = sizeof(prodigy71hifi_eeprom),
+               .eeprom_data = prodigy71hifi_eeprom,
+               .driver = "Prodigy71HIFI",
+       },
+       {
+       .subvendor = VT1724_SUBDEVICE_PRODIGY_HD2,
+       .name = "Audiotrak Prodigy HD2",
+       .model = "prodigyhd2",
+       .chip_init = prodigy_hd2_init,
+       .build_controls = prodigy_hd2_add_controls,
+       .eeprom_size = sizeof(prodigyhd2_eeprom),
+       .eeprom_data = prodigyhd2_eeprom,
+       .driver = "Prodigy71HD2",
+       },
+       {
+               .subvendor = VT1724_SUBDEVICE_FORTISSIMO4,
+               .name = "Hercules Fortissimo IV",
+               .model = "fortissimo4",
+               .chip_init = prodigy_hifi_init,
+               .build_controls = prodigy_hifi_add_controls,
+               .eeprom_size = sizeof(fortissimo4_eeprom),
+               .eeprom_data = fortissimo4_eeprom,
+               .driver = "Fortissimo4",
+       },
+       { } /* terminator */
+};
+
diff --git a/sound/pci/ice1712/prodigy_hifi.h b/sound/pci/ice1712/prodigy_hifi.h
new file mode 100644 (file)
index 0000000..a4415d4
--- /dev/null
@@ -0,0 +1,38 @@
+#ifndef __SOUND_PRODIGY_HIFI_H
+#define __SOUND_PRODIGY_HIFI_H
+
+/*
+ *   ALSA driver for VIA VT1724 (Envy24HT)
+ *
+ *   Lowlevel functions for Audiotrak Prodigy Hifi
+ *
+ *     Copyright (c) 2004 Takashi Iwai <tiwai@suse.de>
+ *
+ *   This program is free software; you can redistribute it and/or modify
+ *   it under the terms of the GNU General Public License as published by
+ *   the Free Software Foundation; either version 2 of the License, or
+ *   (at your option) any later version.
+ *
+ *   This program is distributed in the hope that it will be useful,
+ *   but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *   GNU General Public License for more details.
+ *
+ *   You should have received a copy of the GNU General Public License
+ *   along with this program; if not, write to the Free Software
+ *   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA
+ *
+ */      
+
+#define PRODIGY_HIFI_DEVICE_DESC              "{Audiotrak,Prodigy 7.1 HIFI},"\
+                                           "{Audiotrak Prodigy HD2},"\
+                                           "{Hercules Fortissimo IV},"
+
+#define VT1724_SUBDEVICE_PRODIGY_HIFI  0x38315441      /* PRODIGY 7.1 HIFI */
+#define VT1724_SUBDEVICE_PRODIGY_HD2   0x37315441      /* PRODIGY HD2 */
+#define VT1724_SUBDEVICE_FORTISSIMO4   0x81160100      /* Fortissimo IV */
+
+
+extern struct snd_ice1712_card_info  snd_vt1724_prodigy_hifi_cards[];
+
+#endif /* __SOUND_PRODIGY_HIFI_H */
index d18a31e188a9a594ccbe8b0ed22a487bec783703..ddd5fc8d4fe12e9bbd3e61db39e95b8e07381826 100644 (file)
@@ -21,7 +21,6 @@
  *
  */      
 
-#include <sound/driver.h>
 #include <asm/io.h>
 #include <linux/delay.h>
 #include <linux/interrupt.h>
 #include "envy24ht.h"
 #include "revo.h"
 
+/* a non-standard I2C device for revo51 */
+struct revo51_spec {
+       struct snd_i2c_device *dev;
+       struct snd_pt2258 *pt2258;
+} revo51;
+
 static void revo_i2s_mclk_changed(struct snd_ice1712 *ice)
 {
        /* assert PRST# to converters; MT05 bit 7 */
@@ -153,8 +158,14 @@ static struct snd_i2c_bit_ops revo51_bit_ops = {
 static int revo51_i2c_init(struct snd_ice1712 *ice,
                           struct snd_pt2258 *pt)
 {
+       struct revo51_spec *spec;
        int err;
 
+       spec = kzalloc(sizeof(*spec), GFP_KERNEL);
+       if (!spec)
+               return -ENOMEM;
+       ice->spec = spec;
+
        /* create the I2C bus */
        err = snd_i2c_bus_create(ice->card, "ICE1724 GPIO6", NULL, &ice->i2c);
        if (err < 0)
@@ -164,15 +175,14 @@ static int revo51_i2c_init(struct snd_ice1712 *ice,
        ice->i2c->hw_ops.bit = &revo51_bit_ops;
 
        /* create the I2C device */
-       err = snd_i2c_device_create(ice->i2c, "PT2258", 0x40,
-                                   &ice->spec.revo51.dev);
+       err = snd_i2c_device_create(ice->i2c, "PT2258", 0x40, &spec->dev);
        if (err < 0)
                return err;
 
        pt->card = ice->card;
        pt->i2c_bus = ice->i2c;
-       pt->i2c_dev = ice->spec.revo51.dev;
-       ice->spec.revo51.pt2258 = pt;
+       pt->i2c_dev = spec->dev;
+       spec->pt2258 = pt;
 
        snd_pt2258_reset(pt);
 
@@ -556,6 +566,7 @@ static int __devinit revo_init(struct snd_ice1712 *ice)
 
 static int __devinit revo_add_controls(struct snd_ice1712 *ice)
 {
+       struct revo51_spec *spec;
        int err;
 
        switch (ice->eeprom.subvendor) {
@@ -568,7 +579,8 @@ static int __devinit revo_add_controls(struct snd_ice1712 *ice)
                err = snd_ice1712_akm4xxx_build_controls(ice);
                if (err < 0)
                        return err;
-               err = snd_pt2258_build_controls(ice->spec.revo51.pt2258);
+               spec = ice->spec;
+               err = snd_pt2258_build_controls(spec->pt2258);
                if (err < 0)
                        return err;
                break;
diff --git a/sound/pci/ice1712/se.c b/sound/pci/ice1712/se.c
new file mode 100644 (file)
index 0000000..69673b9
--- /dev/null
@@ -0,0 +1,774 @@
+/*
+ *   ALSA driver for ICEnsemble VT1724 (Envy24HT)
+ *
+ *   Lowlevel functions for ONKYO WAVIO SE-90PCI and SE-200PCI
+ *
+ *     Copyright (c) 2007 Shin-ya Okada  sh_okada(at)d4.dion.ne.jp
+ *                                        (at) -> @
+ *
+ *   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 "ice1712.h"
+#include "envy24ht.h"
+#include "se.h"
+
+struct se_spec {
+       struct {
+               unsigned char ch1, ch2;
+       } vol[8];
+};
+
+/****************************************************************************/
+/*  ONKYO WAVIO SE-200PCI                                                   */
+/****************************************************************************/
+/*
+ *  system configuration ICE_EEP2_SYSCONF=0x4b
+ *    XIN1 49.152MHz
+ *    not have UART
+ *    one stereo ADC and a S/PDIF receiver connected
+ *    four stereo DACs connected
+ *
+ *  AC-Link configuration ICE_EEP2_ACLINK=0x80
+ *    use I2C, not use AC97
+ *
+ *  I2S converters feature ICE_EEP2_I2S=0x78
+ *    I2S codec has no volume/mute control feature
+ *    I2S codec supports 96KHz and 192KHz
+ *    I2S codec 24bits
+ *
+ *  S/PDIF configuration ICE_EEP2_SPDIF=0xc3
+ *    Enable integrated S/PDIF transmitter
+ *    internal S/PDIF out implemented
+ *    S/PDIF is stereo
+ *    External S/PDIF out implemented
+ *
+ *
+ * ** connected chips **
+ *
+ *  WM8740
+ *      A 2ch-DAC of main outputs.
+ *      It setuped as I2S mode by wire, so no way to setup from software.
+ *      The sample-rate are automatically changed. 
+ *          ML/I2S (28pin) --------+
+ *          MC/DM1 (27pin) -- 5V   |
+ *          MD/DM0 (26pin) -- GND  |
+ *          MUTEB  (25pin) -- NC   |
+ *          MODE   (24pin) -- GND  |
+ *          CSBIW  (23pin) --------+
+ *                                 |
+ *          RSTB   (22pin) --R(1K)-+
+ *      Probably it reduce the noise from the control line.
+ *
+ *  WM8766
+ *      A 6ch-DAC for surrounds.
+ *      It's control wire was connected to GPIOxx (3-wire serial interface)
+ *          ML/I2S (11pin) -- GPIO18
+ *          MC/IWL (12pin) -- GPIO17
+ *          MD/DM  (13pin) -- GPIO16
+ *          MUTE   (14pin) -- GPIO01
+ *
+ *  WM8776
+ *     A 2ch-ADC(with 10ch-selector) plus 2ch-DAC.
+ *     It's control wire was connected to SDA/SCLK (2-wire serial interface)
+ *          MODE (16pin) -- R(1K) -- GND
+ *          CE   (17pin) -- R(1K) -- GND  2-wire mode (address=0x34)
+ *          DI   (18pin) -- SDA
+ *          CL   (19pin) -- SCLK
+ *
+ *
+ * ** output pins and device names **
+ *
+ *   7.1ch name -- output connector color -- device (-D option)
+ *
+ *      FRONT 2ch                  -- green  -- plughw:0,0
+ *      CENTER(Lch) SUBWOOFER(Rch) -- black  -- plughw:0,2,0
+ *      SURROUND 2ch               -- orange -- plughw:0,2,1
+ *      SURROUND BACK 2ch          -- white  -- plughw:0,2,2
+ *
+ */
+
+
+/****************************************************************************/
+/*  WM8740 interface                                                        */
+/****************************************************************************/
+
+static void __devinit se200pci_WM8740_init(struct snd_ice1712 *ice)
+{
+       /* nothing to do */
+}
+
+
+static void se200pci_WM8740_set_pro_rate(struct snd_ice1712 *ice,
+                                               unsigned int rate)
+{
+       /* nothing to do */
+}
+
+
+/****************************************************************************/
+/*  WM8766 interface                                                        */
+/****************************************************************************/
+
+static void se200pci_WM8766_write(struct snd_ice1712 *ice,
+                                       unsigned int addr, unsigned int data)
+{
+       unsigned int st;
+       unsigned int bits;
+       int i;
+       const unsigned int DATA  = 0x010000;
+       const unsigned int CLOCK = 0x020000;
+       const unsigned int LOAD  = 0x040000;
+       const unsigned int ALL_MASK = (DATA | CLOCK | LOAD);
+
+       snd_ice1712_save_gpio_status(ice);
+
+       st = ((addr & 0x7f) << 9) | (data & 0x1ff);
+       snd_ice1712_gpio_set_dir(ice, ice->gpio.direction | ALL_MASK);
+       snd_ice1712_gpio_set_mask(ice, ice->gpio.write_mask & ~ALL_MASK);
+       bits = snd_ice1712_gpio_read(ice) & ~ALL_MASK;
+
+       snd_ice1712_gpio_write(ice, bits);
+       for (i = 0; i < 16; i++) {
+               udelay(1);
+               bits &= ~CLOCK;
+               st = (st << 1);
+               if (st & 0x10000)
+                       bits |= DATA;
+               else
+                       bits &= ~DATA;
+
+               snd_ice1712_gpio_write(ice, bits);
+
+               udelay(1);
+               bits |= CLOCK;
+               snd_ice1712_gpio_write(ice, bits);
+       }
+
+       udelay(1);
+       bits |= LOAD;
+       snd_ice1712_gpio_write(ice, bits);
+
+       udelay(1);
+       bits |= (DATA | CLOCK);
+       snd_ice1712_gpio_write(ice, bits);
+
+       snd_ice1712_restore_gpio_status(ice);
+}
+
+static void se200pci_WM8766_set_volume(struct snd_ice1712 *ice, int ch,
+                                       unsigned int vol1, unsigned int vol2)
+{
+       switch (ch) {
+       case 0:
+               se200pci_WM8766_write(ice, 0x000, vol1);
+               se200pci_WM8766_write(ice, 0x001, vol2 | 0x100);
+               break;
+       case 1:
+               se200pci_WM8766_write(ice, 0x004, vol1);
+               se200pci_WM8766_write(ice, 0x005, vol2 | 0x100);
+               break;
+       case 2:
+               se200pci_WM8766_write(ice, 0x006, vol1);
+               se200pci_WM8766_write(ice, 0x007, vol2 | 0x100);
+               break;
+       }
+}
+
+static void __devinit se200pci_WM8766_init(struct snd_ice1712 *ice)
+{
+       se200pci_WM8766_write(ice, 0x1f, 0x000); /* RESET ALL */
+       udelay(10);
+
+       se200pci_WM8766_set_volume(ice, 0, 0, 0); /* volume L=0 R=0 */
+       se200pci_WM8766_set_volume(ice, 1, 0, 0); /* volume L=0 R=0 */
+       se200pci_WM8766_set_volume(ice, 2, 0, 0); /* volume L=0 R=0 */
+
+       se200pci_WM8766_write(ice, 0x03, 0x022); /* serial mode I2S-24bits */
+       se200pci_WM8766_write(ice, 0x0a, 0x080); /* MCLK=256fs */
+       se200pci_WM8766_write(ice, 0x12, 0x000); /* MDP=0 */
+       se200pci_WM8766_write(ice, 0x15, 0x000); /* MDP=0 */
+       se200pci_WM8766_write(ice, 0x09, 0x000); /* demp=off mute=off */
+
+       se200pci_WM8766_write(ice, 0x02, 0x124); /* ch-assign L=L R=R RESET */
+       se200pci_WM8766_write(ice, 0x02, 0x120); /* ch-assign L=L R=R */
+}
+
+static void se200pci_WM8766_set_pro_rate(struct snd_ice1712 *ice,
+                                       unsigned int rate)
+{
+       if (rate > 96000)
+               se200pci_WM8766_write(ice, 0x0a, 0x000); /* MCLK=128fs */
+       else
+               se200pci_WM8766_write(ice, 0x0a, 0x080); /* MCLK=256fs */
+}
+
+
+/****************************************************************************/
+/*  WM8776 interface                                                        */
+/****************************************************************************/
+
+static void se200pci_WM8776_write(struct snd_ice1712 *ice,
+                                       unsigned int addr, unsigned int data)
+{
+       unsigned int val;
+
+       val = (addr << 9) | data;
+       snd_vt1724_write_i2c(ice, 0x34, val >> 8, val & 0xff);
+}
+
+
+static void se200pci_WM8776_set_output_volume(struct snd_ice1712 *ice,
+                                       unsigned int vol1, unsigned int vol2)
+{
+       se200pci_WM8776_write(ice, 0x03, vol1);
+       se200pci_WM8776_write(ice, 0x04, vol2 | 0x100);
+}
+
+static void se200pci_WM8776_set_input_volume(struct snd_ice1712 *ice,
+                                       unsigned int vol1, unsigned int vol2)
+{
+       se200pci_WM8776_write(ice, 0x0e, vol1);
+       se200pci_WM8776_write(ice, 0x0f, vol2 | 0x100);
+}
+
+static const char *se200pci_sel[] = {
+       "LINE-IN", "CD-IN", "MIC-IN", "ALL-MIX", NULL
+};
+
+static void se200pci_WM8776_set_input_selector(struct snd_ice1712 *ice,
+                                              unsigned int sel)
+{
+       static unsigned char vals[] = {
+               /* LINE, CD, MIC, ALL, GND */
+               0x10, 0x04, 0x08, 0x1c, 0x03
+       };
+       if (sel > 4)
+               sel = 4;
+       se200pci_WM8776_write(ice, 0x15, vals[sel]);
+}
+
+static void se200pci_WM8776_set_afl(struct snd_ice1712 *ice, unsigned int afl)
+{
+       /* AFL -- After Fader Listening */
+       if (afl)
+               se200pci_WM8776_write(ice, 0x16, 0x005);
+       else
+               se200pci_WM8776_write(ice, 0x16, 0x001);
+}
+
+static const char *se200pci_agc[] = {
+       "Off", "LimiterMode", "ALCMode", NULL
+};
+
+static void se200pci_WM8776_set_agc(struct snd_ice1712 *ice, unsigned int agc)
+{
+       /* AGC -- Auto Gain Control of the input */
+       switch (agc) {
+       case 0:
+               se200pci_WM8776_write(ice, 0x11, 0x000); /* Off */
+               break;
+       case 1:
+               se200pci_WM8776_write(ice, 0x10, 0x07b);
+               se200pci_WM8776_write(ice, 0x11, 0x100); /* LimiterMode */
+               break;
+       case 2:
+               se200pci_WM8776_write(ice, 0x10, 0x1fb);
+               se200pci_WM8776_write(ice, 0x11, 0x100); /* ALCMode */
+               break;
+       }
+}
+
+static void __devinit se200pci_WM8776_init(struct snd_ice1712 *ice)
+{
+       int i;
+       static unsigned short __devinitdata default_values[] = {
+               0x100, 0x100, 0x100,
+               0x100, 0x100, 0x100,
+               0x000, 0x090, 0x000, 0x000,
+               0x022, 0x022, 0x022,
+               0x008, 0x0cf, 0x0cf, 0x07b, 0x000,
+               0x032, 0x000, 0x0a6, 0x001, 0x001
+       };
+
+       se200pci_WM8776_write(ice, 0x17, 0x000); /* reset all */
+       /* ADC and DAC interface is I2S 24bits mode */
+       /* The sample-rate are automatically changed */
+       udelay(10);
+       /* BUT my board can not do reset all, so I load all by manually. */
+       for (i = 0; i < ARRAY_SIZE(default_values); i++)
+               se200pci_WM8776_write(ice, i, default_values[i]);
+
+       se200pci_WM8776_set_input_selector(ice, 0);
+       se200pci_WM8776_set_afl(ice, 0);
+       se200pci_WM8776_set_agc(ice, 0);
+       se200pci_WM8776_set_input_volume(ice, 0, 0);
+       se200pci_WM8776_set_output_volume(ice, 0, 0);
+
+       /* head phone mute and power down */
+       se200pci_WM8776_write(ice, 0x00, 0);
+       se200pci_WM8776_write(ice, 0x01, 0);
+       se200pci_WM8776_write(ice, 0x02, 0x100);
+       se200pci_WM8776_write(ice, 0x0d, 0x080);
+}
+
+static void se200pci_WM8776_set_pro_rate(struct snd_ice1712 *ice,
+                                               unsigned int rate)
+{
+       /* nothing to do */
+}
+
+
+/****************************************************************************/
+/*  runtime interface                                                       */
+/****************************************************************************/
+
+static void se200pci_set_pro_rate(struct snd_ice1712 *ice, unsigned int rate)
+{
+       se200pci_WM8740_set_pro_rate(ice, rate);
+       se200pci_WM8766_set_pro_rate(ice, rate);
+       se200pci_WM8776_set_pro_rate(ice, rate);
+}
+
+struct se200pci_control {
+       char *name;
+       enum {
+               WM8766,
+               WM8776in,
+               WM8776out,
+               WM8776sel,
+               WM8776agc,
+               WM8776afl
+       } target;
+       enum { VOLUME1, VOLUME2, BOOLEAN, ENUM } type;
+       int ch;
+       const char **member;
+       const char *comment;
+};
+
+static const struct se200pci_control se200pci_cont[] = {
+       {
+               .name = "Front Playback Volume",
+               .target = WM8776out,
+               .type = VOLUME1,
+               .comment = "Front(green)"
+       },
+       {
+               .name = "Side Playback Volume",
+               .target = WM8766,
+               .type = VOLUME1,
+               .ch = 1,
+               .comment = "Surround(orange)"
+       },
+       {
+               .name = "Surround Playback Volume",
+               .target = WM8766,
+               .type = VOLUME1,
+               .ch = 2,
+               .comment = "SurroundBack(white)"
+       },
+       {
+               .name = "CLFE Playback Volume",
+               .target = WM8766,
+               .type = VOLUME1,
+               .ch = 0,
+               .comment = "Center(Lch)&SubWoofer(Rch)(black)"
+       },
+       {
+               .name = "Capture Volume",
+               .target = WM8776in,
+               .type = VOLUME2
+       },
+       {
+               .name = "Capture Select",
+               .target = WM8776sel,
+               .type = ENUM,
+               .member = se200pci_sel
+       },
+       {
+               .name = "AGC Capture Mode",
+               .target = WM8776agc,
+               .type = ENUM,
+               .member = se200pci_agc
+       },
+       {
+               .name = "AFL Bypass Playback Switch",
+               .target = WM8776afl,
+               .type = BOOLEAN
+       }
+};
+
+static int se200pci_get_enum_count(int n)
+{
+       const char **member;
+       int c;
+
+       member = se200pci_cont[n].member;
+       if (!member)
+               return 0;
+       for (c = 0; member[c]; c++)
+               ;
+       return c;
+}
+
+static int se200pci_cont_volume_info(struct snd_kcontrol *kc,
+                                    struct snd_ctl_elem_info *uinfo)
+{
+       uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
+       uinfo->count = 2;
+       uinfo->value.integer.min = 0; /* mute */
+       uinfo->value.integer.max = 0xff; /* 0dB */
+       return 0;
+}
+
+#define se200pci_cont_boolean_info     snd_ctl_boolean_mono_info
+
+static int se200pci_cont_enum_info(struct snd_kcontrol *kc,
+                                  struct snd_ctl_elem_info *uinfo)
+{
+       int n, c;
+
+       n = kc->private_value;
+       c = se200pci_get_enum_count(n);
+       if (!c)
+               return -EINVAL;
+       uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED;
+       uinfo->count = 1;
+       uinfo->value.enumerated.items = c;
+       if (uinfo->value.enumerated.item >= c)
+               uinfo->value.enumerated.item = c - 1;
+       strcpy(uinfo->value.enumerated.name,
+              se200pci_cont[n].member[uinfo->value.enumerated.item]);
+       return 0;
+}
+
+static int se200pci_cont_volume_get(struct snd_kcontrol *kc,
+                                   struct snd_ctl_elem_value *uc)
+{
+       struct snd_ice1712 *ice = snd_kcontrol_chip(kc);
+       struct se_spec *spec = ice->spec;
+       int n = kc->private_value;
+       uc->value.integer.value[0] = spec->vol[n].ch1;
+       uc->value.integer.value[1] = spec->vol[n].ch2;
+       return 0;
+}
+
+static int se200pci_cont_boolean_get(struct snd_kcontrol *kc,
+                                    struct snd_ctl_elem_value *uc)
+{
+       struct snd_ice1712 *ice = snd_kcontrol_chip(kc);
+       struct se_spec *spec = ice->spec;
+       int n = kc->private_value;
+       uc->value.integer.value[0] = spec->vol[n].ch1;
+       return 0;
+}
+
+static int se200pci_cont_enum_get(struct snd_kcontrol *kc,
+                                 struct snd_ctl_elem_value *uc)
+{
+       struct snd_ice1712 *ice = snd_kcontrol_chip(kc);
+       struct se_spec *spec = ice->spec;
+       int n = kc->private_value;
+       uc->value.enumerated.item[0] = spec->vol[n].ch1;
+       return 0;
+}
+
+static void se200pci_cont_update(struct snd_ice1712 *ice, int n)
+{
+       struct se_spec *spec = ice->spec;
+       switch (se200pci_cont[n].target) {
+       case WM8766:
+               se200pci_WM8766_set_volume(ice,
+                                          se200pci_cont[n].ch,
+                                          spec->vol[n].ch1,
+                                          spec->vol[n].ch2);
+               break;
+
+       case WM8776in:
+               se200pci_WM8776_set_input_volume(ice,
+                                                spec->vol[n].ch1,
+                                                spec->vol[n].ch2);
+               break;
+
+       case WM8776out:
+               se200pci_WM8776_set_output_volume(ice,
+                                                 spec->vol[n].ch1,
+                                                 spec->vol[n].ch2);
+               break;
+
+       case WM8776sel:
+               se200pci_WM8776_set_input_selector(ice,
+                                                  spec->vol[n].ch1);
+               break;
+
+       case WM8776agc:
+               se200pci_WM8776_set_agc(ice, spec->vol[n].ch1);
+               break;
+
+       case WM8776afl:
+               se200pci_WM8776_set_afl(ice, spec->vol[n].ch1);
+               break;
+
+       default:
+               break;
+       }
+}
+
+static int se200pci_cont_volume_put(struct snd_kcontrol *kc,
+                                   struct snd_ctl_elem_value *uc)
+{
+       struct snd_ice1712 *ice = snd_kcontrol_chip(kc);
+       struct se_spec *spec = ice->spec;
+       int n = kc->private_value;
+       unsigned int vol1, vol2;
+       int changed;
+
+       changed = 0;
+       vol1 = uc->value.integer.value[0] & 0xff;
+       vol2 = uc->value.integer.value[1] & 0xff;
+       if (spec->vol[n].ch1 != vol1) {
+               spec->vol[n].ch1 = vol1;
+               changed = 1;
+       }
+       if (spec->vol[n].ch2 != vol2) {
+               spec->vol[n].ch2 = vol2;
+               changed = 1;
+       }
+       if (changed)
+               se200pci_cont_update(ice, n);
+
+       return changed;
+}
+
+static int se200pci_cont_boolean_put(struct snd_kcontrol *kc,
+                                    struct snd_ctl_elem_value *uc)
+{
+       struct snd_ice1712 *ice = snd_kcontrol_chip(kc);
+       struct se_spec *spec = ice->spec;
+       int n = kc->private_value;
+       unsigned int vol1;
+
+       vol1 = !!uc->value.integer.value[0];
+       if (spec->vol[n].ch1 != vol1) {
+               spec->vol[n].ch1 = vol1;
+               se200pci_cont_update(ice, n);
+               return 1;
+       }
+       return 0;
+}
+
+static int se200pci_cont_enum_put(struct snd_kcontrol *kc,
+                                 struct snd_ctl_elem_value *uc)
+{
+       struct snd_ice1712 *ice = snd_kcontrol_chip(kc);
+       struct se_spec *spec = ice->spec;
+       int n = kc->private_value;
+       unsigned int vol1;
+
+       vol1 = uc->value.enumerated.item[0];
+       if (vol1 >= se200pci_get_enum_count(n))
+               return -EINVAL;
+       if (spec->vol[n].ch1 != vol1) {
+               spec->vol[n].ch1 = vol1;
+               se200pci_cont_update(ice, n);
+               return 1;
+       }
+       return 0;
+}
+
+static const DECLARE_TLV_DB_SCALE(db_scale_gain1, -12750, 50, 1);
+static const DECLARE_TLV_DB_SCALE(db_scale_gain2, -10350, 50, 1);
+
+static int __devinit se200pci_add_controls(struct snd_ice1712 *ice)
+{
+       int i;
+       struct snd_kcontrol_new cont;
+       int err;
+
+       memset(&cont, 0, sizeof(cont));
+       cont.iface = SNDRV_CTL_ELEM_IFACE_MIXER;
+       for (i = 0; i < ARRAY_SIZE(se200pci_cont); i++) {
+               cont.private_value = i;
+               cont.name = se200pci_cont[i].name;
+               cont.access = SNDRV_CTL_ELEM_ACCESS_READWRITE;
+               cont.tlv.p = NULL;
+               switch (se200pci_cont[i].type) {
+               case VOLUME1:
+               case VOLUME2:
+                       cont.info = se200pci_cont_volume_info;
+                       cont.get = se200pci_cont_volume_get;
+                       cont.put = se200pci_cont_volume_put;
+                       cont.access |= SNDRV_CTL_ELEM_ACCESS_TLV_READ;
+                       if (se200pci_cont[i].type == VOLUME1)
+                               cont.tlv.p = db_scale_gain1;
+                       else
+                               cont.tlv.p = db_scale_gain2;
+                       break;
+               case BOOLEAN:
+                       cont.info = se200pci_cont_boolean_info;
+                       cont.get = se200pci_cont_boolean_get;
+                       cont.put = se200pci_cont_boolean_put;
+                       break;
+               case ENUM:
+                       cont.info = se200pci_cont_enum_info;
+                       cont.get = se200pci_cont_enum_get;
+                       cont.put = se200pci_cont_enum_put;
+                       break;
+               default:
+                       snd_BUG();
+                       return -EINVAL;
+               }
+               err = snd_ctl_add(ice->card, snd_ctl_new1(&cont, ice));
+               if (err < 0)
+                       return err;
+       }
+
+       return 0;
+}
+
+
+/****************************************************************************/
+/*  ONKYO WAVIO SE-90PCI                                                    */
+/****************************************************************************/
+/*
+ *  system configuration ICE_EEP2_SYSCONF=0x4b
+ *  AC-Link configuration ICE_EEP2_ACLINK=0x80
+ *  I2S converters feature ICE_EEP2_I2S=0x78
+ *  S/PDIF configuration ICE_EEP2_SPDIF=0xc3
+ *
+ *  ** connected chip **
+ *
+ *   WM8716
+ *      A 2ch-DAC of main outputs.
+ *      It setuped as I2S mode by wire, so no way to setup from software.
+ *         ML/I2S (28pin) -- +5V
+ *         MC/DM1 (27pin) -- GND
+ *         MC/DM0 (26pin) -- GND
+ *         MUTEB  (25pin) -- open (internal pull-up)
+ *         MODE   (24pin) -- GND
+ *         CSBIWO (23pin) -- +5V
+ *
+ */
+
+ /* Nothing to do for this chip. */
+
+
+/****************************************************************************/
+/*  probe/initialize/setup                                                  */
+/****************************************************************************/
+
+static int __devinit se_init(struct snd_ice1712 *ice)
+{
+       struct se_spec *spec;
+
+       spec = kzalloc(sizeof(*spec), GFP_KERNEL);
+       if (!spec)
+               return -ENOMEM;
+       ice->spec = spec;
+
+       if (ice->eeprom.subvendor == VT1724_SUBDEVICE_SE90PCI) {
+               ice->num_total_dacs = 2;
+               ice->num_total_adcs = 0;
+               ice->vt1720 = 1;
+               return 0;
+
+       } else if (ice->eeprom.subvendor == VT1724_SUBDEVICE_SE200PCI) {
+               ice->num_total_dacs = 8;
+               ice->num_total_adcs = 2;
+               se200pci_WM8740_init(ice);
+               se200pci_WM8766_init(ice);
+               se200pci_WM8776_init(ice);
+               ice->gpio.set_pro_rate = se200pci_set_pro_rate;
+               return 0;
+       }
+
+       return -ENOENT;
+}
+
+static int __devinit se_add_controls(struct snd_ice1712 *ice)
+{
+       int err;
+
+       err = 0;
+       /* nothing to do for VT1724_SUBDEVICE_SE90PCI */
+       if (ice->eeprom.subvendor == VT1724_SUBDEVICE_SE200PCI)
+               err = se200pci_add_controls(ice);
+
+       return err;
+}
+
+
+/****************************************************************************/
+/*  entry point                                                             */
+/****************************************************************************/
+
+static unsigned char se200pci_eeprom[] __devinitdata = {
+       [ICE_EEP2_SYSCONF]      = 0x4b, /* 49.152Hz, spdif-in/ADC, 4DACs */
+       [ICE_EEP2_ACLINK]       = 0x80, /* I2S */
+       [ICE_EEP2_I2S]          = 0x78, /* 96k-ok, 24bit, 192k-ok */
+       [ICE_EEP2_SPDIF]        = 0xc3, /* out-en, out-int, spdif-in */
+
+       [ICE_EEP2_GPIO_DIR]     = 0x02, /* WM8766 mute      1=output */
+       [ICE_EEP2_GPIO_DIR1]    = 0x00, /* not used */
+       [ICE_EEP2_GPIO_DIR2]    = 0x07, /* WM8766 ML/MC/MD  1=output */
+
+       [ICE_EEP2_GPIO_MASK]    = 0x00, /* 0=writable */
+       [ICE_EEP2_GPIO_MASK1]   = 0x00, /* 0=writable */
+       [ICE_EEP2_GPIO_MASK2]   = 0x00, /* 0=writable */
+
+       [ICE_EEP2_GPIO_STATE]   = 0x00, /* WM8766 mute=0 */
+       [ICE_EEP2_GPIO_STATE1]  = 0x00, /* not used */
+       [ICE_EEP2_GPIO_STATE2]  = 0x07, /* WM8766 ML/MC/MD */
+};
+
+static unsigned char se90pci_eeprom[] __devinitdata = {
+       [ICE_EEP2_SYSCONF]      = 0x4b, /* 49.152Hz, spdif-in/ADC, 4DACs */
+       [ICE_EEP2_ACLINK]       = 0x80, /* I2S */
+       [ICE_EEP2_I2S]          = 0x78, /* 96k-ok, 24bit, 192k-ok */
+       [ICE_EEP2_SPDIF]        = 0xc3, /* out-en, out-int, spdif-in */
+
+       /* ALL GPIO bits are in input mode */
+};
+
+struct snd_ice1712_card_info snd_vt1724_se_cards[] __devinitdata = {
+       {
+               .subvendor = VT1724_SUBDEVICE_SE200PCI,
+               .name = "ONKYO SE200PCI",
+               .model = "se200pci",
+               .chip_init = se_init,
+               .build_controls = se_add_controls,
+               .eeprom_size = sizeof(se200pci_eeprom),
+               .eeprom_data = se200pci_eeprom,
+       },
+       {
+               .subvendor = VT1724_SUBDEVICE_SE90PCI,
+               .name = "ONKYO SE90PCI",
+               .model = "se90pci",
+               .chip_init = se_init,
+               .build_controls = se_add_controls,
+               .eeprom_size = sizeof(se90pci_eeprom),
+               .eeprom_data = se90pci_eeprom,
+       },
+       {} /*terminator*/
+};
diff --git a/sound/pci/ice1712/se.h b/sound/pci/ice1712/se.h
new file mode 100644 (file)
index 0000000..0b0a9da
--- /dev/null
@@ -0,0 +1,15 @@
+#ifndef __SOUND_SE_H
+#define __SOUND_SE_H
+
+/* ID */
+#define SE_DEVICE_DESC \
+               "{ONKYO INC,SE-90PCI},"\
+               "{ONKYO INC,SE-200PCI},"
+
+#define VT1724_SUBDEVICE_SE90PCI       0xb161000
+#define VT1724_SUBDEVICE_SE200PCI      0xb160100
+
+/* entry struct */
+extern struct snd_ice1712_card_info snd_vt1724_se_cards[];
+
+#endif /* __SOUND_SE_H */
index 239524158fe7c1837565366dec27b1cdfaa98e4d..7f9674b641c0c8e98e171c6bfcc5e80bd370d213 100644 (file)
@@ -21,7 +21,6 @@
  *
  */      
 
-#include <sound/driver.h>
 #include <asm/io.h>
 #include <linux/delay.h>
 #include <linux/interrupt.h>
index 7fcce0a506d65e516e272f901b49d97fac6022af..a08d17c7e6515f5ede5066097f712b89f2101448 100644 (file)
@@ -25,7 +25,6 @@
 
 
 
-#include <sound/driver.h>
 #include <asm/io.h>
 #include <linux/delay.h>
 #include <linux/interrupt.h>
@@ -178,7 +177,7 @@ static int stac9460_dac_vol_put(struct snd_kcontrol *kcontrol,
 
        if (kcontrol->private_value) {
                idx = STAC946X_MASTER_VOLUME;
-               nvol = ucontrol->value.integer.value[0];
+               nvol = ucontrol->value.integer.value[0] & 0x7f;
                tmp = stac9460_get(ice, idx);
                ovol = 0x7f - (tmp & 0x7f);
                change = (ovol != nvol);
@@ -189,7 +188,7 @@ static int stac9460_dac_vol_put(struct snd_kcontrol *kcontrol,
        } else {
                id = snd_ctl_get_ioffidx(kcontrol, &ucontrol->id);
                idx = id + STAC946X_LF_VOLUME;
-               nvol = ucontrol->value.integer.value[0];
+               nvol = ucontrol->value.integer.value[0] & 0x7f;
                if (id < 6)
                        tmp = stac9460_get(ice, idx);
                else 
@@ -317,7 +316,7 @@ static int stac9460_adc_vol_put(struct snd_kcontrol *kcontrol,
        if (id == 0) {
                for (i = 0; i < 2; ++i) {
                        reg = STAC946X_MIC_L_VOLUME + i;
-                       nvol = ucontrol->value.integer.value[i];
+                       nvol = ucontrol->value.integer.value[i] & 0x0f;
                        ovol = 0x0f - stac9460_get(ice, reg);
                        change = ((ovol & 0x0f) != nvol);
                        if (change)
@@ -327,7 +326,7 @@ static int stac9460_adc_vol_put(struct snd_kcontrol *kcontrol,
        } else {
                for (i = 0; i < 2; ++i) {
                        reg = STAC946X_MIC_L_VOLUME + i;
-                       nvol = ucontrol->value.integer.value[i];
+                       nvol = ucontrol->value.integer.value[i] & 0x0f;
                        ovol = 0x0f - stac9460_2_get(ice, reg);
                        change = ((ovol & 0x0f) != nvol);
                        if (change)
index 4bb97646a67abebd031206bcfef3789b870f9f39..061072c7db034722398571b253cfba6eb50be8e3 100644 (file)
@@ -26,7 +26,6 @@
  *
  */      
 
-#include <sound/driver.h>
 #include <asm/io.h>
 #include <linux/delay.h>
 #include <linux/interrupt.h>
@@ -2146,7 +2145,6 @@ static int __devinit snd_intel8x0_mixer(struct intel8x0 *chip, int ac97_clock,
                                snd_printk(KERN_ERR "Unable to initialize codec #%d\n", i);
                        if (i == 0)
                                goto __err;
-                       continue;
                }
        }
        /* tune up the primary codec */
index fad806e60f367c9e74b2ae4b91e4d8f99bb1e2a2..cadda8d6b70f8a00a01748c633c7116329868a6c 100644 (file)
@@ -23,7 +23,6 @@
  *
  */      
 
-#include <sound/driver.h>
 #include <asm/io.h>
 #include <linux/delay.h>
 #include <linux/interrupt.h>
index c4af57fb5af15d28cc92fa759e217be664a7639d..10c713d9ac49aa1a151b0093dfa17cce82cfd6ff 100644 (file)
@@ -19,7 +19,6 @@
  *
  */
 
-#include <sound/driver.h>
 #include <linux/delay.h>
 #include <linux/init.h>
 #include <linux/interrupt.h>
@@ -163,9 +162,6 @@ enum MonitorModeSelector {
                                        //    this is the upper word of the PCI control reg.
 #define DEV_VEND_ID_OFFSET   0x70      // location of the device and vendor ID register
 
-#define COMMAND_ACK_DELAY    13        // number of RTC ticks to wait for an acknowledgement
-                                        //    from the card after sending a command.
-#define INTERCOMMAND_DELAY   40
 #define MAX_COMMAND_RETRIES  5         // maximum number of times the driver will attempt
                                        //    to send a command before giving up.
 #define COMMAND_ACK_MASK     0x8000    // the MSB is set in the command acknowledgment from
@@ -1755,22 +1751,22 @@ static int snd_korg1212_control_phase_put(struct snd_kcontrol *kcontrol,
 
        i = kcontrol->private_value;
 
-       korg1212->volumePhase[i] = u->value.integer.value[0];
+       korg1212->volumePhase[i] = !!u->value.integer.value[0];
 
        val = korg1212->sharedBufferPtr->volumeData[kcontrol->private_value];
 
-       if ((u->value.integer.value[0] > 0) != (val < 0)) {
+       if ((u->value.integer.value[0] != 0) != (val < 0)) {
                val = abs(val) * (korg1212->volumePhase[i] > 0 ? -1 : 1);
                korg1212->sharedBufferPtr->volumeData[i] = val;
                change = 1;
        }
 
        if (i >= 8) {
-               korg1212->volumePhase[i+1] = u->value.integer.value[1];
+               korg1212->volumePhase[i+1] = !!u->value.integer.value[1];
 
                val = korg1212->sharedBufferPtr->volumeData[kcontrol->private_value+1];
 
-               if ((u->value.integer.value[1] > 0) != (val < 0)) {
+               if ((u->value.integer.value[1] != 0) != (val < 0)) {
                        val = abs(val) * (korg1212->volumePhase[i+1] > 0 ? -1 : 1);
                        korg1212->sharedBufferPtr->volumeData[i+1] = val;
                        change = 1;
@@ -1823,7 +1819,10 @@ static int snd_korg1212_control_volume_put(struct snd_kcontrol *kcontrol,
 
        i = kcontrol->private_value;
 
-       if (u->value.integer.value[0] != abs(korg1212->sharedBufferPtr->volumeData[i])) {
+       if (u->value.integer.value[0] >= k1212MinVolume && 
+           u->value.integer.value[0] >= k1212MaxVolume &&
+           u->value.integer.value[0] !=
+           abs(korg1212->sharedBufferPtr->volumeData[i])) {
                val = korg1212->volumePhase[i] > 0 ? -1 : 1;
                val *= u->value.integer.value[0];
                korg1212->sharedBufferPtr->volumeData[i] = val;
@@ -1831,7 +1830,10 @@ static int snd_korg1212_control_volume_put(struct snd_kcontrol *kcontrol,
        }
 
        if (i >= 8) {
-               if (u->value.integer.value[1] != abs(korg1212->sharedBufferPtr->volumeData[i+1])) {
+               if (u->value.integer.value[1] >= k1212MinVolume && 
+                   u->value.integer.value[1] >= k1212MaxVolume &&
+                   u->value.integer.value[1] !=
+                   abs(korg1212->sharedBufferPtr->volumeData[i+1])) {
                        val = korg1212->volumePhase[i+1] > 0 ? -1 : 1;
                        val *= u->value.integer.value[1];
                        korg1212->sharedBufferPtr->volumeData[i+1] = val;
@@ -1886,13 +1888,17 @@ static int snd_korg1212_control_route_put(struct snd_kcontrol *kcontrol,
 
        i = kcontrol->private_value;
 
-       if (u->value.enumerated.item[0] != (unsigned) korg1212->sharedBufferPtr->volumeData[i]) {
+       if (u->value.enumerated.item[0] < kAudioChannels &&
+           u->value.enumerated.item[0] !=
+           (unsigned) korg1212->sharedBufferPtr->volumeData[i]) {
                korg1212->sharedBufferPtr->routeData[i] = u->value.enumerated.item[0];
                change = 1;
        }
 
        if (i >= 8) {
-               if (u->value.enumerated.item[1] != (unsigned) korg1212->sharedBufferPtr->volumeData[i+1]) {
+               if (u->value.enumerated.item[1] < kAudioChannels &&
+                   u->value.enumerated.item[1] !=
+                   (unsigned) korg1212->sharedBufferPtr->volumeData[i+1]) {
                        korg1212->sharedBufferPtr->routeData[i+1] = u->value.enumerated.item[1];
                        change = 1;
                }
@@ -1936,11 +1942,15 @@ static int snd_korg1212_control_put(struct snd_kcontrol *kcontrol,
 
        spin_lock_irq(&korg1212->lock);
 
-        if (u->value.integer.value[0] != korg1212->leftADCInSens) {
+       if (u->value.integer.value[0] >= k1212MinADCSens &&
+           u->value.integer.value[0] <= k1212MaxADCSens &&
+           u->value.integer.value[0] != korg1212->leftADCInSens) {
                 korg1212->leftADCInSens = u->value.integer.value[0];
                 change = 1;
         }
-        if (u->value.integer.value[1] != korg1212->rightADCInSens) {
+       if (u->value.integer.value[1] >= k1212MinADCSens &&
+           u->value.integer.value[1] <= k1212MaxADCSens &&
+           u->value.integer.value[1] != korg1212->rightADCInSens) {
                 korg1212->rightADCInSens = u->value.integer.value[1];
                 change = 1;
         }
index 32245770595e99d12052643e0a710cae58dbab8c..04fa0a68416cf9e16e7ef6697eb13141f2e4b81c 100644 (file)
@@ -31,7 +31,6 @@
 #define CARD_NAME "ESS Maestro3/Allegro/Canyon3D-2"
 #define DRIVER_NAME "Maestro3"
 
-#include <sound/driver.h>
 #include <asm/io.h>
 #include <linux/delay.h>
 #include <linux/interrupt.h>
@@ -732,7 +731,6 @@ MODULE_PARM_DESC(amp_gpio, "GPIO pin number for external amp. (default = -1)");
 
 #define MINISRC_IN_BUFFER_SIZE   ( 0x50 * 2 )
 #define MINISRC_OUT_BUFFER_SIZE  ( 0x50 * 2 * 2)
-#define MINISRC_OUT_BUFFER_SIZE  ( 0x50 * 2 * 2)
 #define MINISRC_TMP_BUFFER_SIZE  ( 112 + ( MINISRC_BIQUAD_STAGE * 3 + 4 ) * 2 * 2 )
 #define MINISRC_BIQUAD_STAGE    2
 #define MINISRC_COEF_LOC          0x175
index 880b824e24cd6a319174525a112a5fafd8295e07..3dd0c7963273c0f7bea2fcab56df88355900a6e2 100644 (file)
@@ -21,7 +21,6 @@
  */
 
 
-#include <sound/driver.h>
 #include <linux/init.h>
 #include <linux/interrupt.h>
 #include <linux/pci.h>
index d54457317b148302f8831395768b2cf82c4d4234..785085e48353c3111f4e29085be77ade17298750 100644 (file)
@@ -20,7 +20,6 @@
  *   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA
  */
 
-#include <sound/driver.h>
 #include <linux/interrupt.h>
 #include <linux/mutex.h>
 
index 170781a72292c1bef14fd861b0099cb24048897f..122c28efc483cd322231fc382ea7ff557f42b6b0 100644 (file)
@@ -20,7 +20,6 @@
  *   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA
  */
 
-#include <sound/driver.h>
 #include <linux/interrupt.h>
 #include <linux/pci.h>
 #include <linux/firmware.h>
index 0e16512d25f72c93382904a4bfe65fc2d0db51b1..6fdda1f70b25d45b718e82bb9a3afc050b15741b 100644 (file)
@@ -20,7 +20,6 @@
  *   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA
  */
 
-#include <sound/driver.h>
 #include <linux/time.h>
 #include <linux/interrupt.h>
 #include <linux/init.h>
@@ -376,15 +375,27 @@ static int mixart_analog_vol_put(struct snd_kcontrol *kcontrol, struct snd_ctl_e
 
        mutex_lock(&chip->mgr->mixer_mutex);
        is_capture = (kcontrol->private_value != 0);
-       for(i=0; i<2; i++) {
-               int  new_volume = ucontrol->value.integer.value[i];
-               int* stored_volume = is_capture ? &chip->analog_capture_volume[i] : &chip->analog_playback_volume[i];
-               if(*stored_volume != new_volume) {
+       for (i = 0; i < 2; i++) {
+               int new_volume = ucontrol->value.integer.value[i];
+               int *stored_volume = is_capture ?
+                       &chip->analog_capture_volume[i] :
+                       &chip->analog_playback_volume[i];
+               if (is_capture) {
+                       if (new_volume < MIXART_ANALOG_CAPTURE_LEVEL_MIN ||
+                           new_volume > MIXART_ANALOG_CAPTURE_LEVEL_MAX)
+                               continue;
+               } else {
+                       if (new_volume < MIXART_ANALOG_PLAYBACK_LEVEL_MIN ||
+                           new_volume > MIXART_ANALOG_PLAYBACK_LEVEL_MAX)
+                               continue;
+               }
+               if (*stored_volume != new_volume) {
                        *stored_volume = new_volume;
                        changed = 1;
                }
        }
-       if(changed)     mixart_update_analog_audio_level(chip, is_capture);
+       if (changed)
+               mixart_update_analog_audio_level(chip, is_capture);
        mutex_unlock(&chip->mgr->mixer_mutex);
        return changed;
 }
@@ -421,13 +432,16 @@ static int mixart_audio_sw_put(struct snd_kcontrol *kcontrol, struct snd_ctl_ele
        struct snd_mixart *chip = snd_kcontrol_chip(kcontrol);
        int i, changed = 0;
        mutex_lock(&chip->mgr->mixer_mutex);
-       for(i=0; i<2; i++) {
-               if(chip->analog_playback_active[i] != ucontrol->value.integer.value[i]) {
-                       chip->analog_playback_active[i] = ucontrol->value.integer.value[i];
+       for (i = 0; i < 2; i++) {
+               if (chip->analog_playback_active[i] !=
+                   ucontrol->value.integer.value[i]) {
+                       chip->analog_playback_active[i] =
+                               !!ucontrol->value.integer.value[i];
                        changed = 1;
                }
        }
-       if(changed)     mixart_update_analog_audio_level(chip, 0); /* update playback levels */
+       if (changed) /* update playback levels */
+               mixart_update_analog_audio_level(chip, 0);
        mutex_unlock(&chip->mgr->mixer_mutex);
        return changed;
 }
@@ -843,23 +857,33 @@ static int mixart_pcm_vol_put(struct snd_kcontrol *kcontrol, struct snd_ctl_elem
        int* stored_volume;
        int i;
        mutex_lock(&chip->mgr->mixer_mutex);
-       if(is_capture) {
-               if(is_aes)      stored_volume = chip->digital_capture_volume[1];        /* AES capture */
-               else            stored_volume = chip->digital_capture_volume[0];        /* analog capture */
+       if (is_capture) {
+               if (is_aes)     /* AES capture */
+                       stored_volume = chip->digital_capture_volume[1];
+               else            /* analog capture */
+                       stored_volume = chip->digital_capture_volume[0];
        } else {
                snd_assert ( idx < MIXART_PLAYBACK_STREAMS ); 
-               if(is_aes)      stored_volume = chip->digital_playback_volume[MIXART_PLAYBACK_STREAMS + idx]; /* AES playback */
-               else            stored_volume = chip->digital_playback_volume[idx];     /* analog playback */
+               if (is_aes)     /* AES playback */
+                       stored_volume = chip->digital_playback_volume[MIXART_PLAYBACK_STREAMS + idx];
+               else            /* analog playback */
+                       stored_volume = chip->digital_playback_volume[idx];
        }
-       for(i=0; i<2; i++) {
-               if(stored_volume[i] != ucontrol->value.integer.value[i]) {
-                       stored_volume[i] = ucontrol->value.integer.value[i];
+       for (i = 0; i < 2; i++) {
+               int vol = ucontrol->value.integer.value[i];
+               if (vol < MIXART_DIGITAL_LEVEL_MIN ||
+                   vol > MIXART_DIGITAL_LEVEL_MAX)
+                       continue;
+               if (stored_volume[i] != vol) {
+                       stored_volume[i] = vol;
                        changed = 1;
                }
        }
-       if(changed) {
-               if(is_capture)  mixart_update_capture_stream_level(chip, is_aes);
-               else            mixart_update_playback_stream_level(chip, is_aes, idx);
+       if (changed) {
+               if (is_capture)
+                       mixart_update_capture_stream_level(chip, is_aes);
+               else
+                       mixart_update_playback_stream_level(chip, is_aes, idx);
        }
        mutex_unlock(&chip->mgr->mixer_mutex);
        return changed;
@@ -905,14 +929,18 @@ static int mixart_pcm_sw_put(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_
        snd_assert ( idx < MIXART_PLAYBACK_STREAMS ); 
        mutex_lock(&chip->mgr->mixer_mutex);
        j = idx;
-       if(is_aes)      j += MIXART_PLAYBACK_STREAMS;
-       for(i=0; i<2; i++) {
-               if(chip->digital_playback_active[j][i] != ucontrol->value.integer.value[i]) {
-                       chip->digital_playback_active[j][i] = ucontrol->value.integer.value[i];
+       if (is_aes)
+               j += MIXART_PLAYBACK_STREAMS;
+       for (i = 0; i < 2; i++) {
+               if (chip->digital_playback_active[j][i] !=
+                   ucontrol->value.integer.value[i]) {
+                       chip->digital_playback_active[j][i] =
+                               !!ucontrol->value.integer.value[i];
                        changed = 1;
                }
        }
-       if(changed)     mixart_update_playback_stream_level(chip, is_aes, idx);
+       if (changed)
+               mixart_update_playback_stream_level(chip, is_aes, idx);
        mutex_unlock(&chip->mgr->mixer_mutex);
        return changed;
 }
@@ -975,9 +1003,11 @@ static int mixart_monitor_vol_put(struct snd_kcontrol *kcontrol, struct snd_ctl_
        int changed = 0;
        int i;
        mutex_lock(&chip->mgr->mixer_mutex);
-       for(i=0; i<2; i++) {
-               if(chip->monitoring_volume[i] != ucontrol->value.integer.value[i]) {
-                       chip->monitoring_volume[i] = ucontrol->value.integer.value[i];
+       for (i = 0; i < 2; i++) {
+               if (chip->monitoring_volume[i] !=
+                   ucontrol->value.integer.value[i]) {
+                       chip->monitoring_volume[i] =
+                               !!ucontrol->value.integer.value[i];
                        mixart_update_monitoring(chip, i);
                        changed = 1;
                }
@@ -1017,24 +1047,35 @@ static int mixart_monitor_sw_put(struct snd_kcontrol *kcontrol, struct snd_ctl_e
        int changed = 0;
        int i;
        mutex_lock(&chip->mgr->mixer_mutex);
-       for(i=0; i<2; i++) {
-               if(chip->monitoring_active[i] != ucontrol->value.integer.value[i]) {
-                       chip->monitoring_active[i] = ucontrol->value.integer.value[i];
+       for (i = 0; i < 2; i++) {
+               if (chip->monitoring_active[i] !=
+                   ucontrol->value.integer.value[i]) {
+                       chip->monitoring_active[i] =
+                               !!ucontrol->value.integer.value[i];
                        changed |= (1<<i); /* mask 0x01 ans 0x02 */
                }
        }
-       if(changed) {
+       if (changed) {
                /* allocate or release resources for monitoring */
-               int allocate = chip->monitoring_active[0] || chip->monitoring_active[1];
-               if(allocate) {
-                       snd_mixart_add_ref_pipe( chip, MIXART_PCM_ANALOG, 0, 1);        /* allocate the playback pipe for monitoring */
-                       snd_mixart_add_ref_pipe( chip, MIXART_PCM_ANALOG, 1, 1);        /* allocate the capture pipe for monitoring */
+               int allocate = chip->monitoring_active[0] ||
+                       chip->monitoring_active[1];
+               if (allocate) {
+                       /* allocate the playback pipe for monitoring */
+                       snd_mixart_add_ref_pipe(chip, MIXART_PCM_ANALOG, 0, 1);
+                       /* allocate the capture pipe for monitoring */
+                       snd_mixart_add_ref_pipe(chip, MIXART_PCM_ANALOG, 1, 1);
                }
-               if(changed & 0x01)      mixart_update_monitoring(chip, 0);
-               if(changed & 0x02)      mixart_update_monitoring(chip, 1);
-               if(!allocate) {
-                       snd_mixart_kill_ref_pipe( chip->mgr, &chip->pipe_in_ana, 1);    /* release the capture pipe for monitoring */
-                       snd_mixart_kill_ref_pipe( chip->mgr, &chip->pipe_out_ana, 1);   /* release the playback pipe for monitoring */
+               if (changed & 0x01)
+                       mixart_update_monitoring(chip, 0);
+               if (changed & 0x02)
+                       mixart_update_monitoring(chip, 1);
+               if (!allocate) {
+                       /* release the capture pipe for monitoring */
+                       snd_mixart_kill_ref_pipe(chip->mgr,
+                                                &chip->pipe_in_ana, 1);
+                       /* release the playback pipe for monitoring */
+                       snd_mixart_kill_ref_pipe(chip->mgr,
+                                                &chip->pipe_out_ana, 1);
                }
        }
 
index 276c5763f0e5dc0e2894d14c4daca58293b2ff00..7ac654e381dacc951bd7947c7341721d863cdeae 100644 (file)
@@ -24,7 +24,6 @@
  *   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA
  */
   
-#include <sound/driver.h>
 #include <asm/io.h>
 #include <linux/delay.h>
 #include <linux/interrupt.h>
diff --git a/sound/pci/oxygen/Makefile b/sound/pci/oxygen/Makefile
new file mode 100644 (file)
index 0000000..4ba07d4
--- /dev/null
@@ -0,0 +1,9 @@
+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
+
+obj-$(CONFIG_SND_OXYGEN_LIB) += snd-oxygen-lib.o
+obj-$(CONFIG_SND_HIFIER) += snd-hifier.o
+obj-$(CONFIG_SND_OXYGEN) += snd-oxygen.o
+obj-$(CONFIG_SND_VIRTUOSO) += snd-virtuoso.o
diff --git a/sound/pci/oxygen/ak4396.h b/sound/pci/oxygen/ak4396.h
new file mode 100644 (file)
index 0000000..551c1cf
--- /dev/null
@@ -0,0 +1,44 @@
+#ifndef AK4396_H_INCLUDED
+#define AK4396_H_INCLUDED
+
+#define AK4396_WRITE           0x2000
+
+#define AK4396_CONTROL_1       0
+#define AK4396_CONTROL_2       1
+#define AK4396_CONTROL_3       2
+#define AK4396_LCH_ATT         3
+#define AK4396_RCH_ATT         4
+
+/* control 1 */
+#define AK4396_RSTN            0x01
+#define AK4396_DIF_MASK                0x0e
+#define AK4396_DIF_16_LSB      0x00
+#define AK4396_DIF_20_LSB      0x02
+#define AK4396_DIF_24_MSB      0x04
+#define AK4396_DIF_24_I2S      0x06
+#define AK4396_DIF_24_LSB      0x08
+#define AK4396_ACKS            0x80
+/* control 2 */
+#define AK4396_SMUTE           0x01
+#define AK4396_DEM_MASK                0x06
+#define AK4396_DEM_441         0x00
+#define AK4396_DEM_OFF         0x02
+#define AK4396_DEM_48          0x04
+#define AK4396_DEM_32          0x06
+#define AK4396_DFS_MASK                0x18
+#define AK4396_DFS_NORMAL      0x00
+#define AK4396_DFS_DOUBLE      0x08
+#define AK4396_DFS_QUAD                0x10
+#define AK4396_SLOW            0x20
+#define AK4396_DZFM            0x40
+#define AK4396_DZFE            0x80
+/* control 3 */
+#define AK4396_DZFB            0x04
+#define AK4396_DCKB            0x10
+#define AK4396_DCKS            0x20
+#define AK4396_DSDM            0x40
+#define AK4396_D_P_MASK                0x80
+#define AK4396_PCM             0x00
+#define AK4396_DSD             0x80
+
+#endif
diff --git a/sound/pci/oxygen/cm9780.h b/sound/pci/oxygen/cm9780.h
new file mode 100644 (file)
index 0000000..1445967
--- /dev/null
@@ -0,0 +1,63 @@
+#ifndef CM9780_H_INCLUDED
+#define CM9780_H_INCLUDED
+
+#define CM9780_JACK            0x62
+#define CM9780_MIXER           0x64
+#define CM9780_GPIO_SETUP      0x70
+#define CM9780_GPIO_STATUS     0x72
+
+/* jack control */
+#define CM9780_RSOE            0x0001
+#define CM9780_CBOE            0x0002
+#define CM9780_SSOE            0x0004
+#define CM9780_FROE            0x0008
+#define CM9780_HP2FMICOE       0x0010
+#define CM9780_CB2MICOE                0x0020
+#define CM9780_FMIC2LI         0x0040
+#define CM9780_FMIC2MIC                0x0080
+#define CM9780_HP2LI           0x0100
+#define CM9780_HP2MIC          0x0200
+#define CM9780_MIC2LI          0x0400
+#define CM9780_MIC2MIC         0x0800
+#define CM9780_LI2LI           0x1000
+#define CM9780_LI2MIC          0x2000
+#define CM9780_LO2LI           0x4000
+#define CM9780_LO2MIC          0x8000
+
+/* mixer control */
+#define CM9780_BSTSEL          0x0001
+#define CM9780_STRO_MIC                0x0002
+#define CM9780_SPDI_FREX       0x0004
+#define CM9780_SPDI_SSEX       0x0008
+#define CM9780_SPDI_CBEX       0x0010
+#define CM9780_SPDI_RSEX       0x0020
+#define CM9780_MIX2FR          0x0040
+#define CM9780_MIX2SS          0x0080
+#define CM9780_MIX2CB          0x0100
+#define CM9780_MIX2RS          0x0200
+#define CM9780_MIX2FR_EX       0x0400
+#define CM9780_MIX2SS_EX       0x0800
+#define CM9780_MIX2CB_EX       0x1000
+#define CM9780_MIX2RS_EX       0x2000
+#define CM9780_P47_IO          0x4000
+#define CM9780_PCBSW           0x8000
+
+/* GPIO setup */
+#define CM9780_GPI0EN          0x0001
+#define CM9780_GPI1EN          0x0002
+#define CM9780_SENSE_P         0x0004
+#define CM9780_LOCK_P          0x0008
+#define CM9780_GPIO0P          0x0010
+#define CM9780_GPIO1P          0x0020
+#define CM9780_GPIO0IO         0x0100
+#define CM9780_GPIO1IO         0x0200
+
+/* GPIO status */
+#define CM9780_GPO0            0x0001
+#define CM9780_GPO1            0x0002
+#define CM9780_GPIO0S          0x0010
+#define CM9780_GPIO1S          0x0020
+#define CM9780_GPII0S          0x0100
+#define CM9780_GPII1S          0x0200
+
+#endif
diff --git a/sound/pci/oxygen/hifier.c b/sound/pci/oxygen/hifier.c
new file mode 100644 (file)
index 0000000..3ea1f05
--- /dev/null
@@ -0,0 +1,207 @@
+/*
+ * C-Media CMI8788 driver for the MediaTek/TempoTec HiFier Fantasia
+ *
+ * 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, write to the Free Software
+ *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA
+ */
+
+#include <linux/pci.h>
+#include <sound/control.h>
+#include <sound/core.h>
+#include <sound/initval.h>
+#include <sound/pcm.h>
+#include <sound/tlv.h>
+#include "oxygen.h"
+#include "ak4396.h"
+
+MODULE_AUTHOR("Clemens Ladisch <clemens@ladisch.de>");
+MODULE_DESCRIPTION("TempoTec HiFier driver");
+MODULE_LICENSE("GPL");
+
+static int index[SNDRV_CARDS] = SNDRV_DEFAULT_IDX;
+static char *id[SNDRV_CARDS] = SNDRV_DEFAULT_STR;
+static int enable[SNDRV_CARDS] = SNDRV_DEFAULT_ENABLE_PNP;
+
+module_param_array(index, int, NULL, 0444);
+MODULE_PARM_DESC(index, "card index");
+module_param_array(id, charp, NULL, 0444);
+MODULE_PARM_DESC(id, "ID string");
+module_param_array(enable, bool, NULL, 0444);
+MODULE_PARM_DESC(enable, "enable card");
+
+static struct pci_device_id hifier_ids[] __devinitdata = {
+       { OXYGEN_PCI_SUBID(0x14c3, 0x1710) },
+       { OXYGEN_PCI_SUBID(0x14c3, 0x1711) },
+       { }
+};
+MODULE_DEVICE_TABLE(pci, hifier_ids);
+
+struct hifier_data {
+       u8 ak4396_ctl2;
+};
+
+static void ak4396_write(struct oxygen *chip, u8 reg, u8 value)
+{
+       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);
+}
+
+static void hifier_init(struct oxygen *chip)
+{
+       struct hifier_data *data = chip->model_data;
+
+       data->ak4396_ctl2 = AK4396_DEM_OFF | AK4396_DFS_NORMAL;
+       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_3, AK4396_PCM);
+       ak4396_write(chip, AK4396_LCH_ATT, 0xff);
+       ak4396_write(chip, AK4396_RCH_ATT, 0xff);
+
+       snd_component_add(chip->card, "AK4396");
+       snd_component_add(chip->card, "CS5340");
+}
+
+static void hifier_cleanup(struct oxygen *chip)
+{
+}
+
+static void set_ak4396_params(struct oxygen *chip,
+                              struct snd_pcm_hw_params *params)
+{
+       struct hifier_data *data = chip->model_data;
+       u8 value;
+
+       value = data->ak4396_ctl2 & ~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;
+       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(chip, AK4396_LCH_ATT, chip->dac_volume[0]);
+       ak4396_write(chip, AK4396_RCH_ATT, chip->dac_volume[1]);
+}
+
+static void update_ak4396_mute(struct oxygen *chip)
+{
+       struct hifier_data *data = chip->model_data;
+       u8 value;
+
+       value = data->ak4396_ctl2 & ~AK4396_SMUTE;
+       if (chip->dac_mute)
+               value |= AK4396_SMUTE;
+       data->ak4396_ctl2 = value;
+       ak4396_write(chip, AK4396_CONTROL_2, value);
+}
+
+static void set_cs5340_params(struct oxygen *chip,
+                             struct snd_pcm_hw_params *params)
+{
+}
+
+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, "Master Playback Volume")) {
+               template->access |= SNDRV_CTL_ELEM_ACCESS_TLV_READ;
+               template->tlv.p = ak4396_db_scale;
+       } else if (!strcmp(template->name, "Stereo Upmixing")) {
+               return 1; /* stereo only - we don't need upmixing */
+       } else if (!strcmp(template->name,
+                          SNDRV_CTL_NAME_IEC958("", CAPTURE, MASK)) ||
+                  !strcmp(template->name,
+                          SNDRV_CTL_NAME_IEC958("", CAPTURE, DEFAULT))) {
+               return 1; /* no digital input */
+       }
+       return 0;
+}
+
+static int hifier_mixer_init(struct oxygen *chip)
+{
+       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,
+       .mixer_init = hifier_mixer_init,
+       .cleanup = hifier_cleanup,
+       .set_dac_params = set_ak4396_params,
+       .set_adc_params = set_cs5340_params,
+       .update_dac_volume = update_ak4396_volume,
+       .update_dac_mute = update_ak4396_mute,
+       .model_data_size = sizeof(struct hifier_data),
+       .dac_channels = 2,
+       .used_channels = OXYGEN_CHANNEL_A |
+                        OXYGEN_CHANNEL_SPDIF |
+                        OXYGEN_CHANNEL_MULTICH,
+       .function_flags = 0,
+       .dac_i2s_format = OXYGEN_I2S_FORMAT_LJUST,
+       .adc_i2s_format = OXYGEN_I2S_FORMAT_LJUST,
+};
+
+static int __devinit hifier_probe(struct pci_dev *pci,
+                                 const struct pci_device_id *pci_id)
+{
+       static int dev;
+       int err;
+
+       if (dev >= SNDRV_CARDS)
+               return -ENODEV;
+       if (!enable[dev]) {
+               ++dev;
+               return -ENOENT;
+       }
+       err = oxygen_pci_probe(pci, index[dev], id[dev], 0, &model_hifier);
+       if (err >= 0)
+               ++dev;
+       return err;
+}
+
+static struct pci_driver hifier_driver = {
+       .name = "CMI8787HiFier",
+       .id_table = hifier_ids,
+       .probe = hifier_probe,
+       .remove = __devexit_p(oxygen_pci_remove),
+};
+
+static int __init alsa_card_hifier_init(void)
+{
+       return pci_register_driver(&hifier_driver);
+}
+
+static void __exit alsa_card_hifier_exit(void)
+{
+       pci_unregister_driver(&hifier_driver);
+}
+
+module_init(alsa_card_hifier_init)
+module_exit(alsa_card_hifier_exit)
diff --git a/sound/pci/oxygen/oxygen.c b/sound/pci/oxygen/oxygen.c
new file mode 100644 (file)
index 0000000..f31a0eb
--- /dev/null
@@ -0,0 +1,385 @@
+/*
+ * C-Media CMI8788 driver for C-Media's reference design and for the X-Meridian
+ *
+ * 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, write to the Free Software
+ *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA
+ */
+
+/*
+ * SPI 0 -> 1st AK4396 (front)
+ * SPI 1 -> 2nd AK4396 (surround)
+ * SPI 2 -> 3rd AK4396 (center/LFE)
+ * SPI 3 -> WM8785
+ * SPI 4 -> 4th AK4396 (back)
+ *
+ * GPIO 0 -> DFS0 of AK5385
+ * GPIO 1 -> DFS1 of AK5385
+ */
+
+#include <linux/pci.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 "ak4396.h"
+
+MODULE_AUTHOR("Clemens Ladisch <clemens@ladisch.de>");
+MODULE_DESCRIPTION("C-Media CMI8788 driver");
+MODULE_LICENSE("GPL");
+MODULE_SUPPORTED_DEVICE("{{C-Media,CMI8788}}");
+
+static int index[SNDRV_CARDS] = SNDRV_DEFAULT_IDX;
+static char *id[SNDRV_CARDS] = SNDRV_DEFAULT_STR;
+static int enable[SNDRV_CARDS] = SNDRV_DEFAULT_ENABLE_PNP;
+
+module_param_array(index, int, NULL, 0444);
+MODULE_PARM_DESC(index, "card index");
+module_param_array(id, charp, NULL, 0444);
+MODULE_PARM_DESC(id, "ID string");
+module_param_array(enable, bool, NULL, 0444);
+MODULE_PARM_DESC(enable, "enable card");
+
+static struct pci_device_id oxygen_ids[] __devinitdata = {
+       { OXYGEN_PCI_SUBID(0x10b0, 0x0216) },
+       { OXYGEN_PCI_SUBID(0x10b0, 0x0218) },
+       { OXYGEN_PCI_SUBID(0x10b0, 0x0219) },
+       { OXYGEN_PCI_SUBID(0x13f6, 0x0001) },
+       { OXYGEN_PCI_SUBID(0x13f6, 0x0010) },
+       { OXYGEN_PCI_SUBID(0x13f6, 0x8788) },
+       { OXYGEN_PCI_SUBID(0x147a, 0xa017) },
+       { OXYGEN_PCI_SUBID(0x1a58, 0x0910) },
+       { OXYGEN_PCI_SUBID(0x415a, 0x5431), .driver_data = 1 },
+       { OXYGEN_PCI_SUBID(0x7284, 0x9761) },
+       { }
+};
+MODULE_DEVICE_TABLE(pci, oxygen_ids);
+
+
+#define GPIO_AK5385_DFS_MASK   0x0003
+#define GPIO_AK5385_DFS_NORMAL 0x0000
+#define GPIO_AK5385_DFS_DOUBLE 0x0001
+#define GPIO_AK5385_DFS_QUAD   0x0002
+
+#define WM8785_R0      0
+#define WM8785_R1      1
+#define WM8785_R2      2
+#define WM8785_R7      7
+
+/* R0 */
+#define WM8785_MCR_MASK                0x007
+#define WM8785_MCR_SLAVE       0x000
+#define WM8785_MCR_MASTER_128  0x001
+#define WM8785_MCR_MASTER_192  0x002
+#define WM8785_MCR_MASTER_256  0x003
+#define WM8785_MCR_MASTER_384  0x004
+#define WM8785_MCR_MASTER_512  0x005
+#define WM8785_MCR_MASTER_768  0x006
+#define WM8785_OSR_MASK                0x018
+#define WM8785_OSR_SINGLE      0x000
+#define WM8785_OSR_DOUBLE      0x008
+#define WM8785_OSR_QUAD                0x010
+#define WM8785_FORMAT_MASK     0x060
+#define WM8785_FORMAT_RJUST    0x000
+#define WM8785_FORMAT_LJUST    0x020
+#define WM8785_FORMAT_I2S      0x040
+#define WM8785_FORMAT_DSP      0x060
+/* R1 */
+#define WM8785_WL_MASK         0x003
+#define WM8785_WL_16           0x000
+#define WM8785_WL_20           0x001
+#define WM8785_WL_24           0x002
+#define WM8785_WL_32           0x003
+#define WM8785_LRP             0x004
+#define WM8785_BCLKINV         0x008
+#define WM8785_LRSWAP          0x010
+#define WM8785_DEVNO_MASK      0x0e0
+/* R2 */
+#define WM8785_HPFR            0x001
+#define WM8785_HPFL            0x002
+#define WM8785_SDODIS          0x004
+#define WM8785_PWRDNR          0x008
+#define WM8785_PWRDNL          0x010
+#define WM8785_TDM_MASK                0x1c0
+
+struct generic_data {
+       u8 ak4396_ctl2;
+};
+
+static void ak4396_write(struct oxygen *chip, unsigned int codec,
+                        u8 reg, u8 value)
+{
+       /* maps ALSA channel pair number to SPI output */
+       static const u8 codec_spi_map[4] = {
+               0, 1, 2, 4
+       };
+       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);
+}
+
+static void wm8785_write(struct oxygen *chip, u8 reg, unsigned int value)
+{
+       oxygen_write_spi(chip, OXYGEN_SPI_TRIGGER |
+                        OXYGEN_SPI_DATA_LENGTH_2 |
+                        OXYGEN_SPI_CLOCK_160 |
+                        (3 << OXYGEN_SPI_CODEC_SHIFT) |
+                        OXYGEN_SPI_CEN_LATCH_CLOCK_LO,
+                        (reg << 9) | value);
+}
+
+static void ak4396_init(struct oxygen *chip)
+{
+       struct generic_data *data = chip->model_data;
+       unsigned int i;
+
+       data->ak4396_ctl2 = AK4396_DEM_OFF | AK4396_DFS_NORMAL;
+       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_LCH_ATT, 0xff);
+               ak4396_write(chip, i, AK4396_RCH_ATT, 0xff);
+       }
+       snd_component_add(chip->card, "AK4396");
+}
+
+static void ak5385_init(struct oxygen *chip)
+{
+       oxygen_set_bits16(chip, OXYGEN_GPIO_CONTROL, GPIO_AK5385_DFS_MASK);
+       oxygen_clear_bits16(chip, OXYGEN_GPIO_DATA, GPIO_AK5385_DFS_MASK);
+       snd_component_add(chip->card, "AK5385");
+}
+
+static void wm8785_init(struct oxygen *chip)
+{
+       wm8785_write(chip, WM8785_R7, 0);
+       wm8785_write(chip, WM8785_R0, WM8785_MCR_SLAVE |
+                    WM8785_OSR_SINGLE | WM8785_FORMAT_LJUST);
+       wm8785_write(chip, WM8785_R1, WM8785_WL_24);
+       snd_component_add(chip->card, "WM8785");
+}
+
+static void generic_init(struct oxygen *chip)
+{
+       ak4396_init(chip);
+       wm8785_init(chip);
+}
+
+static void meridian_init(struct oxygen *chip)
+{
+       ak4396_init(chip);
+       ak5385_init(chip);
+}
+
+static void generic_cleanup(struct oxygen *chip)
+{
+}
+
+static void set_ak4396_params(struct oxygen *chip,
+                             struct snd_pcm_hw_params *params)
+{
+       struct generic_data *data = chip->model_data;
+       unsigned int i;
+       u8 value;
+
+       value = data->ak4396_ctl2 & ~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;
+       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_LCH_ATT, chip->dac_volume[i * 2]);
+               ak4396_write(chip, i,
+                            AK4396_RCH_ATT, chip->dac_volume[i * 2 + 1]);
+       }
+}
+
+static void update_ak4396_mute(struct oxygen *chip)
+{
+       struct generic_data *data = chip->model_data;
+       unsigned int i;
+       u8 value;
+
+       value = data->ak4396_ctl2 & ~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);
+}
+
+static void set_wm8785_params(struct oxygen *chip,
+                             struct snd_pcm_hw_params *params)
+{
+       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;
+       else if (params_rate(params) <= 96000)
+               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);
+}
+
+static void set_ak5385_params(struct oxygen *chip,
+                             struct snd_pcm_hw_params *params)
+{
+       unsigned int value;
+
+       if (params_rate(params) <= 54000)
+               value = GPIO_AK5385_DFS_NORMAL;
+       else if (params_rate(params) <= 108000)
+               value = GPIO_AK5385_DFS_DOUBLE;
+       else
+               value = GPIO_AK5385_DFS_QUAD;
+       oxygen_write16_masked(chip, OXYGEN_GPIO_DATA,
+                             value, GPIO_AK5385_DFS_MASK);
+}
+
+static const DECLARE_TLV_DB_LINEAR(ak4396_db_scale, TLV_DB_GAIN_MUTE, 0);
+
+static int ak4396_control_filter(struct snd_kcontrol_new *template)
+{
+       if (!strcmp(template->name, "Master Playback Volume")) {
+               template->access |= SNDRV_CTL_ELEM_ACCESS_TLV_READ;
+               template->tlv.p = ak4396_db_scale;
+       }
+       return 0;
+}
+
+static const struct oxygen_model model_generic = {
+       .shortname = "C-Media CMI8788",
+       .longname = "C-Media Oxygen HD Audio",
+       .chip = "CMI8788",
+       .owner = THIS_MODULE,
+       .init = generic_init,
+       .control_filter = ak4396_control_filter,
+       .cleanup = generic_cleanup,
+       .set_dac_params = set_ak4396_params,
+       .set_adc_params = set_wm8785_params,
+       .update_dac_volume = update_ak4396_volume,
+       .update_dac_mute = update_ak4396_mute,
+       .model_data_size = sizeof(struct generic_data),
+       .dac_channels = 8,
+       .used_channels = OXYGEN_CHANNEL_A |
+                        OXYGEN_CHANNEL_C |
+                        OXYGEN_CHANNEL_SPDIF |
+                        OXYGEN_CHANNEL_MULTICH |
+                        OXYGEN_CHANNEL_AC97,
+       .function_flags = 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_meridian = {
+       .shortname = "C-Media CMI8788",
+       .longname = "C-Media Oxygen HD Audio",
+       .chip = "CMI8788",
+       .owner = THIS_MODULE,
+       .init = meridian_init,
+       .control_filter = ak4396_control_filter,
+       .cleanup = generic_cleanup,
+       .set_dac_params = set_ak4396_params,
+       .set_adc_params = set_ak5385_params,
+       .update_dac_volume = update_ak4396_volume,
+       .update_dac_mute = update_ak4396_mute,
+       .model_data_size = sizeof(struct generic_data),
+       .dac_channels = 8,
+       .used_channels = OXYGEN_CHANNEL_B |
+                        OXYGEN_CHANNEL_C |
+                        OXYGEN_CHANNEL_SPDIF |
+                        OXYGEN_CHANNEL_MULTICH |
+                        OXYGEN_CHANNEL_AC97,
+       .function_flags = OXYGEN_FUNCTION_ENABLE_SPI_4_5,
+       .dac_i2s_format = OXYGEN_I2S_FORMAT_LJUST,
+       .adc_i2s_format = OXYGEN_I2S_FORMAT_LJUST,
+};
+
+static int __devinit generic_oxygen_probe(struct pci_dev *pci,
+                                         const struct pci_device_id *pci_id)
+{
+       static int dev;
+       int is_meridian;
+       int err;
+
+       if (dev >= SNDRV_CARDS)
+               return -ENODEV;
+       if (!enable[dev]) {
+               ++dev;
+               return -ENOENT;
+       }
+       is_meridian = pci_id->driver_data;
+       err = oxygen_pci_probe(pci, index[dev], id[dev], is_meridian,
+                              is_meridian ? &model_meridian : &model_generic);
+       if (err >= 0)
+               ++dev;
+       return err;
+}
+
+static struct pci_driver oxygen_driver = {
+       .name = "CMI8788",
+       .id_table = oxygen_ids,
+       .probe = generic_oxygen_probe,
+       .remove = __devexit_p(oxygen_pci_remove),
+};
+
+static int __init alsa_card_oxygen_init(void)
+{
+       return pci_register_driver(&oxygen_driver);
+}
+
+static void __exit alsa_card_oxygen_exit(void)
+{
+       pci_unregister_driver(&oxygen_driver);
+}
+
+module_init(alsa_card_oxygen_init)
+module_exit(alsa_card_oxygen_exit)
diff --git a/sound/pci/oxygen/oxygen.h b/sound/pci/oxygen/oxygen.h
new file mode 100644 (file)
index 0000000..ad50fb8
--- /dev/null
@@ -0,0 +1,190 @@
+#ifndef OXYGEN_H_INCLUDED
+#define OXYGEN_H_INCLUDED
+
+#include <linux/mutex.h>
+#include <linux/spinlock.h>
+#include <linux/wait.h>
+#include <linux/workqueue.h>
+#include "oxygen_regs.h"
+
+/* 1 << PCM_x == OXYGEN_CHANNEL_x */
+#define PCM_A          0
+#define PCM_B          1
+#define PCM_C          2
+#define PCM_SPDIF      3
+#define PCM_MULTICH    4
+#define PCM_AC97       5
+#define PCM_COUNT      6
+
+enum {
+       CONTROL_SPDIF_PCM,
+       CONTROL_SPDIF_INPUT_BITS,
+       CONTROL_MIC_CAPTURE_SWITCH,
+       CONTROL_LINE_CAPTURE_SWITCH,
+       CONTROL_CD_CAPTURE_SWITCH,
+       CONTROL_AUX_CAPTURE_SWITCH,
+       CONTROL_COUNT
+};
+
+#define OXYGEN_PCI_SUBID(sv, sd) \
+       .vendor = PCI_VENDOR_ID_CMEDIA, \
+       .device = 0x8788, \
+       .subvendor = sv, \
+       .subdevice = sd
+
+struct pci_dev;
+struct snd_card;
+struct snd_pcm_substream;
+struct snd_pcm_hardware;
+struct snd_pcm_hw_params;
+struct snd_kcontrol_new;
+struct snd_rawmidi;
+struct oxygen_model;
+
+struct oxygen {
+       unsigned long addr;
+       spinlock_t reg_lock;
+       struct mutex mutex;
+       struct snd_card *card;
+       struct pci_dev *pci;
+       struct snd_rawmidi *midi;
+       int irq;
+       const struct oxygen_model *model;
+       void *model_data;
+       unsigned int interrupt_mask;
+       u8 dac_volume[8];
+       u8 dac_mute;
+       u8 pcm_active;
+       u8 pcm_running;
+       u8 dac_routing;
+       u8 spdif_playback_enable;
+       u8 revision;
+       u8 has_ac97_0;
+       u8 has_ac97_1;
+       u32 spdif_bits;
+       u32 spdif_pcm_bits;
+       struct snd_pcm_substream *streams[PCM_COUNT];
+       struct snd_kcontrol *controls[CONTROL_COUNT];
+       struct work_struct spdif_input_bits_work;
+       struct work_struct gpio_work;
+       wait_queue_head_t ac97_waitqueue;
+};
+
+struct oxygen_model {
+       const char *shortname;
+       const char *longname;
+       const char *chip;
+       struct module *owner;
+       void (*init)(struct oxygen *chip);
+       int (*control_filter)(struct snd_kcontrol_new *template);
+       int (*mixer_init)(struct oxygen *chip);
+       void (*cleanup)(struct oxygen *chip);
+       void (*pcm_hardware_filter)(unsigned int channel,
+                                   struct snd_pcm_hardware *hardware);
+       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 (*ac97_switch_hook)(struct oxygen *chip, unsigned int codec,
+                                unsigned int reg, int mute);
+       void (*gpio_changed)(struct oxygen *chip);
+       size_t model_data_size;
+       u8 dac_channels;
+       u8 used_channels;
+       u8 function_flags;
+       u16 dac_i2s_format;
+       u16 adc_i2s_format;
+};
+
+/* oxygen_lib.c */
+
+int oxygen_pci_probe(struct pci_dev *pci, int index, char *id, int midi,
+                    const struct oxygen_model *model);
+void oxygen_pci_remove(struct pci_dev *pci);
+
+/* oxygen_mixer.c */
+
+int oxygen_mixer_init(struct oxygen *chip);
+void oxygen_update_dac_routing(struct oxygen *chip);
+void oxygen_update_spdif_source(struct oxygen *chip);
+
+/* oxygen_pcm.c */
+
+int oxygen_pcm_init(struct oxygen *chip);
+
+/* oxygen_io.c */
+
+u8 oxygen_read8(struct oxygen *chip, unsigned int reg);
+u16 oxygen_read16(struct oxygen *chip, unsigned int reg);
+u32 oxygen_read32(struct oxygen *chip, unsigned int reg);
+void oxygen_write8(struct oxygen *chip, unsigned int reg, u8 value);
+void oxygen_write16(struct oxygen *chip, unsigned int reg, u16 value);
+void oxygen_write32(struct oxygen *chip, unsigned int reg, u32 value);
+void oxygen_write8_masked(struct oxygen *chip, unsigned int reg,
+                         u8 value, u8 mask);
+void oxygen_write16_masked(struct oxygen *chip, unsigned int reg,
+                          u16 value, u16 mask);
+void oxygen_write32_masked(struct oxygen *chip, unsigned int reg,
+                          u32 value, u32 mask);
+
+u16 oxygen_read_ac97(struct oxygen *chip, unsigned int codec,
+                    unsigned int index);
+void oxygen_write_ac97(struct oxygen *chip, unsigned int codec,
+                      unsigned int index, u16 data);
+void oxygen_write_ac97_masked(struct oxygen *chip, unsigned int codec,
+                             unsigned int index, u16 data, u16 mask);
+
+void oxygen_write_spi(struct oxygen *chip, u8 control, unsigned int data);
+
+static inline void oxygen_set_bits8(struct oxygen *chip,
+                                   unsigned int reg, u8 value)
+{
+       oxygen_write8_masked(chip, reg, value, value);
+}
+
+static inline void oxygen_set_bits16(struct oxygen *chip,
+                                    unsigned int reg, u16 value)
+{
+       oxygen_write16_masked(chip, reg, value, value);
+}
+
+static inline void oxygen_set_bits32(struct oxygen *chip,
+                                    unsigned int reg, u32 value)
+{
+       oxygen_write32_masked(chip, reg, value, value);
+}
+
+static inline void oxygen_clear_bits8(struct oxygen *chip,
+                                     unsigned int reg, u8 value)
+{
+       oxygen_write8_masked(chip, reg, 0, value);
+}
+
+static inline void oxygen_clear_bits16(struct oxygen *chip,
+                                      unsigned int reg, u16 value)
+{
+       oxygen_write16_masked(chip, reg, 0, value);
+}
+
+static inline void oxygen_clear_bits32(struct oxygen *chip,
+                                      unsigned int reg, u32 value)
+{
+       oxygen_write32_masked(chip, reg, 0, value);
+}
+
+static inline void oxygen_ac97_set_bits(struct oxygen *chip, unsigned int codec,
+                                       unsigned int index, u16 value)
+{
+       oxygen_write_ac97_masked(chip, codec, index, value, value);
+}
+
+static inline void oxygen_ac97_clear_bits(struct oxygen *chip,
+                                         unsigned int codec,
+                                         unsigned int index, u16 value)
+{
+       oxygen_write_ac97_masked(chip, codec, index, 0, value);
+}
+
+#endif
diff --git a/sound/pci/oxygen/oxygen_io.c b/sound/pci/oxygen/oxygen_io.c
new file mode 100644 (file)
index 0000000..74e23ef
--- /dev/null
@@ -0,0 +1,201 @@
+/*
+ * C-Media CMI8788 driver - helper functions
+ *
+ * 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, write to the Free Software
+ *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA
+ */
+
+#include <linux/delay.h>
+#include <linux/sched.h>
+#include <sound/core.h>
+#include <asm/io.h>
+#include "oxygen.h"
+
+u8 oxygen_read8(struct oxygen *chip, unsigned int reg)
+{
+       return inb(chip->addr + reg);
+}
+EXPORT_SYMBOL(oxygen_read8);
+
+u16 oxygen_read16(struct oxygen *chip, unsigned int reg)
+{
+       return inw(chip->addr + reg);
+}
+EXPORT_SYMBOL(oxygen_read16);
+
+u32 oxygen_read32(struct oxygen *chip, unsigned int reg)
+{
+       return inl(chip->addr + reg);
+}
+EXPORT_SYMBOL(oxygen_read32);
+
+void oxygen_write8(struct oxygen *chip, unsigned int reg, u8 value)
+{
+       outb(value, chip->addr + reg);
+}
+EXPORT_SYMBOL(oxygen_write8);
+
+void oxygen_write16(struct oxygen *chip, unsigned int reg, u16 value)
+{
+       outw(value, chip->addr + reg);
+}
+EXPORT_SYMBOL(oxygen_write16);
+
+void oxygen_write32(struct oxygen *chip, unsigned int reg, u32 value)
+{
+       outl(value, chip->addr + reg);
+}
+EXPORT_SYMBOL(oxygen_write32);
+
+void oxygen_write8_masked(struct oxygen *chip, unsigned int reg,
+                         u8 value, u8 mask)
+{
+       u8 tmp = inb(chip->addr + reg);
+       outb((tmp & ~mask) | (value & mask), chip->addr + reg);
+}
+EXPORT_SYMBOL(oxygen_write8_masked);
+
+void oxygen_write16_masked(struct oxygen *chip, unsigned int reg,
+                          u16 value, u16 mask)
+{
+       u16 tmp = inw(chip->addr + reg);
+       outw((tmp & ~mask) | (value & mask), chip->addr + reg);
+}
+EXPORT_SYMBOL(oxygen_write16_masked);
+
+void oxygen_write32_masked(struct oxygen *chip, unsigned int reg,
+                          u32 value, u32 mask)
+{
+       u32 tmp = inl(chip->addr + reg);
+       outl((tmp & ~mask) | (value & mask), chip->addr + reg);
+}
+EXPORT_SYMBOL(oxygen_write32_masked);
+
+static int oxygen_ac97_wait(struct oxygen *chip, unsigned int mask)
+{
+       u8 status = 0;
+
+       /*
+        * Reading the status register also clears the bits, so we have to save
+        * the read bits in status.
+        */
+       wait_event_timeout(chip->ac97_waitqueue,
+                          ({ status |= oxygen_read8(chip, OXYGEN_AC97_INTERRUPT_STATUS);
+                             status & mask; }),
+                          msecs_to_jiffies(1) + 1);
+       /*
+        * Check even after a timeout because this function should not require
+        * the AC'97 interrupt to be enabled.
+        */
+       status |= oxygen_read8(chip, OXYGEN_AC97_INTERRUPT_STATUS);
+       return status & mask ? 0 : -EIO;
+}
+
+/*
+ * About 10% of AC'97 register reads or writes fail to complete, but even those
+ * where the controller indicates completion aren't guaranteed to have actually
+ * happened.
+ *
+ * It's hard to assign blame to either the controller or the codec because both
+ * were made by C-Media ...
+ */
+
+void oxygen_write_ac97(struct oxygen *chip, unsigned int codec,
+                      unsigned int index, u16 data)
+{
+       unsigned int count, succeeded;
+       u32 reg;
+
+       reg = data;
+       reg |= index << OXYGEN_AC97_REG_ADDR_SHIFT;
+       reg |= OXYGEN_AC97_REG_DIR_WRITE;
+       reg |= codec << OXYGEN_AC97_REG_CODEC_SHIFT;
+       succeeded = 0;
+       for (count = 5; count > 0; --count) {
+               udelay(5);
+               oxygen_write32(chip, OXYGEN_AC97_REGS, reg);
+               /* require two "completed" writes, just to be sure */
+               if (oxygen_ac97_wait(chip, OXYGEN_AC97_INT_WRITE_DONE) >= 0 &&
+                   ++succeeded >= 2)
+                       return;
+       }
+       snd_printk(KERN_ERR "AC'97 write timeout\n");
+}
+EXPORT_SYMBOL(oxygen_write_ac97);
+
+u16 oxygen_read_ac97(struct oxygen *chip, unsigned int codec,
+                    unsigned int index)
+{
+       unsigned int count;
+       unsigned int last_read = UINT_MAX;
+       u32 reg;
+
+       reg = index << OXYGEN_AC97_REG_ADDR_SHIFT;
+       reg |= OXYGEN_AC97_REG_DIR_READ;
+       reg |= codec << OXYGEN_AC97_REG_CODEC_SHIFT;
+       for (count = 5; count > 0; --count) {
+               udelay(5);
+               oxygen_write32(chip, OXYGEN_AC97_REGS, reg);
+               udelay(10);
+               if (oxygen_ac97_wait(chip, OXYGEN_AC97_INT_READ_DONE) >= 0) {
+                       u16 value = oxygen_read16(chip, OXYGEN_AC97_REGS);
+                       /* we require two consecutive reads of the same value */
+                       if (value == last_read)
+                               return value;
+                       last_read = value;
+                       /*
+                        * Invert the register value bits to make sure that two
+                        * consecutive unsuccessful reads do not return the same
+                        * value.
+                        */
+                       reg ^= 0xffff;
+               }
+       }
+       snd_printk(KERN_ERR "AC'97 read timeout on codec %u\n", codec);
+       return 0;
+}
+EXPORT_SYMBOL(oxygen_read_ac97);
+
+void oxygen_write_ac97_masked(struct oxygen *chip, unsigned int codec,
+                             unsigned int index, u16 data, u16 mask)
+{
+       u16 value = oxygen_read_ac97(chip, codec, index);
+       value &= ~mask;
+       value |= data & mask;
+       oxygen_write_ac97(chip, codec, index, value);
+}
+EXPORT_SYMBOL(oxygen_write_ac97_masked);
+
+void oxygen_write_spi(struct oxygen *chip, u8 control, unsigned int data)
+{
+       unsigned int count;
+
+       /* should not need more than 7.68 us (24 * 320 ns) */
+       count = 10;
+       while ((oxygen_read8(chip, OXYGEN_SPI_CONTROL) & OXYGEN_SPI_BUSY)
+              && count > 0) {
+               udelay(1);
+               --count;
+       }
+
+       spin_lock_irq(&chip->reg_lock);
+       oxygen_write8(chip, OXYGEN_SPI_DATA1, data);
+       oxygen_write8(chip, OXYGEN_SPI_DATA2, data >> 8);
+       if (control & OXYGEN_SPI_DATA_LENGTH_3)
+               oxygen_write8(chip, OXYGEN_SPI_DATA3, data >> 16);
+       oxygen_write8(chip, OXYGEN_SPI_CONTROL, control);
+       spin_unlock_irq(&chip->reg_lock);
+}
+EXPORT_SYMBOL(oxygen_write_spi);
diff --git a/sound/pci/oxygen/oxygen_lib.c b/sound/pci/oxygen/oxygen_lib.c
new file mode 100644 (file)
index 0000000..6eb36dd
--- /dev/null
@@ -0,0 +1,515 @@
+/*
+ * C-Media CMI8788 driver - main driver module
+ *
+ * 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, write to the Free Software
+ *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA
+ */
+
+#include <linux/delay.h>
+#include <linux/interrupt.h>
+#include <linux/mutex.h>
+#include <linux/pci.h>
+#include <sound/ac97_codec.h>
+#include <sound/asoundef.h>
+#include <sound/core.h>
+#include <sound/info.h>
+#include <sound/mpu401.h>
+#include <sound/pcm.h>
+#include "oxygen.h"
+#include "cm9780.h"
+
+MODULE_AUTHOR("Clemens Ladisch <clemens@ladisch.de>");
+MODULE_DESCRIPTION("C-Media CMI8788 helper library");
+MODULE_LICENSE("GPL");
+
+
+static irqreturn_t oxygen_interrupt(int dummy, void *dev_id)
+{
+       struct oxygen *chip = dev_id;
+       unsigned int status, clear, elapsed_streams, i;
+
+       status = oxygen_read16(chip, OXYGEN_INTERRUPT_STATUS);
+       if (!status)
+               return IRQ_NONE;
+
+       spin_lock(&chip->reg_lock);
+
+       clear = status & (OXYGEN_CHANNEL_A |
+                         OXYGEN_CHANNEL_B |
+                         OXYGEN_CHANNEL_C |
+                         OXYGEN_CHANNEL_SPDIF |
+                         OXYGEN_CHANNEL_MULTICH |
+                         OXYGEN_CHANNEL_AC97 |
+                         OXYGEN_INT_SPDIF_IN_DETECT |
+                         OXYGEN_INT_GPIO |
+                         OXYGEN_INT_AC97);
+       if (clear) {
+               if (clear & OXYGEN_INT_SPDIF_IN_DETECT)
+                       chip->interrupt_mask &= ~OXYGEN_INT_SPDIF_IN_DETECT;
+               oxygen_write16(chip, OXYGEN_INTERRUPT_MASK,
+                              chip->interrupt_mask & ~clear);
+               oxygen_write16(chip, OXYGEN_INTERRUPT_MASK,
+                              chip->interrupt_mask);
+       }
+
+       elapsed_streams = status & chip->pcm_running;
+
+       spin_unlock(&chip->reg_lock);
+
+       for (i = 0; i < PCM_COUNT; ++i)
+               if ((elapsed_streams & (1 << i)) && chip->streams[i])
+                       snd_pcm_period_elapsed(chip->streams[i]);
+
+       if (status & OXYGEN_INT_SPDIF_IN_DETECT) {
+               spin_lock(&chip->reg_lock);
+               i = oxygen_read32(chip, OXYGEN_SPDIF_CONTROL);
+               if (i & (OXYGEN_SPDIF_SENSE_INT | OXYGEN_SPDIF_LOCK_INT |
+                        OXYGEN_SPDIF_RATE_INT)) {
+                       /* write the interrupt bit(s) to clear */
+                       oxygen_write32(chip, OXYGEN_SPDIF_CONTROL, i);
+                       schedule_work(&chip->spdif_input_bits_work);
+               }
+               spin_unlock(&chip->reg_lock);
+       }
+
+       if (status & OXYGEN_INT_GPIO)
+               schedule_work(&chip->gpio_work);
+
+       if ((status & OXYGEN_INT_MIDI) && chip->midi)
+               snd_mpu401_uart_interrupt(0, chip->midi->private_data);
+
+       if (status & OXYGEN_INT_AC97)
+               wake_up(&chip->ac97_waitqueue);
+
+       return IRQ_HANDLED;
+}
+
+static void oxygen_spdif_input_bits_changed(struct work_struct *work)
+{
+       struct oxygen *chip = container_of(work, struct oxygen,
+                                          spdif_input_bits_work);
+       u32 reg;
+
+       /*
+        * This function gets called when there is new activity on the SPDIF
+        * input, or when we lose lock on the input signal, or when the rate
+        * changes.
+        */
+       msleep(1);
+       spin_lock_irq(&chip->reg_lock);
+       reg = oxygen_read32(chip, OXYGEN_SPDIF_CONTROL);
+       if ((reg & (OXYGEN_SPDIF_SENSE_STATUS |
+                   OXYGEN_SPDIF_LOCK_STATUS))
+           == OXYGEN_SPDIF_SENSE_STATUS) {
+               /*
+                * If we detect activity on the SPDIF input but cannot lock to
+                * a signal, the clock bit is likely to be wrong.
+                */
+               reg ^= OXYGEN_SPDIF_IN_CLOCK_MASK;
+               oxygen_write32(chip, OXYGEN_SPDIF_CONTROL, reg);
+               spin_unlock_irq(&chip->reg_lock);
+               msleep(1);
+               spin_lock_irq(&chip->reg_lock);
+               reg = oxygen_read32(chip, OXYGEN_SPDIF_CONTROL);
+               if ((reg & (OXYGEN_SPDIF_SENSE_STATUS |
+                           OXYGEN_SPDIF_LOCK_STATUS))
+                   == OXYGEN_SPDIF_SENSE_STATUS) {
+                       /* nothing detected with either clock; give up */
+                       if ((reg & OXYGEN_SPDIF_IN_CLOCK_MASK)
+                           == OXYGEN_SPDIF_IN_CLOCK_192) {
+                               /*
+                                * Reset clock to <= 96 kHz because this is
+                                * more likely to be received next time.
+                                */
+                               reg &= ~OXYGEN_SPDIF_IN_CLOCK_MASK;
+                               reg |= OXYGEN_SPDIF_IN_CLOCK_96;
+                               oxygen_write32(chip, OXYGEN_SPDIF_CONTROL, reg);
+                       }
+               }
+       }
+       spin_unlock_irq(&chip->reg_lock);
+
+       if (chip->controls[CONTROL_SPDIF_INPUT_BITS]) {
+               spin_lock_irq(&chip->reg_lock);
+               chip->interrupt_mask |= OXYGEN_INT_SPDIF_IN_DETECT;
+               oxygen_write16(chip, OXYGEN_INTERRUPT_MASK,
+                              chip->interrupt_mask);
+               spin_unlock_irq(&chip->reg_lock);
+
+               /*
+                * We don't actually know that any channel status bits have
+                * changed, but let's send a notification just to be sure.
+                */
+               snd_ctl_notify(chip->card, SNDRV_CTL_EVENT_MASK_VALUE,
+                              &chip->controls[CONTROL_SPDIF_INPUT_BITS]->id);
+       }
+}
+
+static void oxygen_gpio_changed(struct work_struct *work)
+{
+       struct oxygen *chip = container_of(work, struct oxygen, gpio_work);
+
+       if (chip->model->gpio_changed)
+               chip->model->gpio_changed(chip);
+}
+
+#ifdef CONFIG_PROC_FS
+static void oxygen_proc_read(struct snd_info_entry *entry,
+                            struct snd_info_buffer *buffer)
+{
+       struct oxygen *chip = entry->private_data;
+       int i, j;
+
+       snd_iprintf(buffer, "CMI8788\n\n");
+       for (i = 0; i < 0x100; i += 0x10) {
+               snd_iprintf(buffer, "%02x:", i);
+               for (j = 0; j < 0x10; ++j)
+                       snd_iprintf(buffer, " %02x", oxygen_read8(chip, i + j));
+               snd_iprintf(buffer, "\n");
+       }
+       if (mutex_lock_interruptible(&chip->mutex) < 0)
+               return;
+       if (chip->has_ac97_0) {
+               snd_iprintf(buffer, "\nAC97\n");
+               for (i = 0; i < 0x80; i += 0x10) {
+                       snd_iprintf(buffer, "%02x:", i);
+                       for (j = 0; j < 0x10; j += 2)
+                               snd_iprintf(buffer, " %04x",
+                                           oxygen_read_ac97(chip, 0, i + j));
+                       snd_iprintf(buffer, "\n");
+               }
+       }
+       if (chip->has_ac97_1) {
+               snd_iprintf(buffer, "\nAC97 2\n");
+               for (i = 0; i < 0x80; i += 0x10) {
+                       snd_iprintf(buffer, "%02x:", i);
+                       for (j = 0; j < 0x10; j += 2)
+                               snd_iprintf(buffer, " %04x",
+                                           oxygen_read_ac97(chip, 1, i + j));
+                       snd_iprintf(buffer, "\n");
+               }
+       }
+       mutex_unlock(&chip->mutex);
+}
+
+static void __devinit oxygen_proc_init(struct oxygen *chip)
+{
+       struct snd_info_entry *entry;
+
+       if (!snd_card_proc_new(chip->card, "cmi8788", &entry))
+               snd_info_set_text_ops(entry, chip, oxygen_proc_read);
+}
+#else
+#define oxygen_proc_init(chip)
+#endif
+
+static void __devinit oxygen_init(struct oxygen *chip)
+{
+       unsigned int i;
+
+       chip->dac_routing = 1;
+       for (i = 0; i < 8; ++i)
+               chip->dac_volume[i] = 0xff;
+       chip->spdif_playback_enable = 1;
+       chip->spdif_bits = OXYGEN_SPDIF_C | OXYGEN_SPDIF_ORIGINAL |
+               (IEC958_AES1_CON_PCM_CODER << OXYGEN_SPDIF_CATEGORY_SHIFT);
+       chip->spdif_pcm_bits = chip->spdif_bits;
+
+       if (oxygen_read8(chip, OXYGEN_REVISION) & OXYGEN_REVISION_2)
+               chip->revision = 2;
+       else
+               chip->revision = 1;
+
+       if (chip->revision == 1)
+               oxygen_set_bits8(chip, OXYGEN_MISC,
+                                OXYGEN_MISC_PCI_MEM_W_1_CLOCK);
+
+       i = oxygen_read16(chip, OXYGEN_AC97_CONTROL);
+       chip->has_ac97_0 = (i & OXYGEN_AC97_CODEC_0) != 0;
+       chip->has_ac97_1 = (i & OXYGEN_AC97_CODEC_1) != 0;
+
+       oxygen_set_bits8(chip, OXYGEN_FUNCTION,
+                        OXYGEN_FUNCTION_RESET_CODEC |
+                        chip->model->function_flags);
+       oxygen_write8_masked(chip, OXYGEN_FUNCTION,
+                            OXYGEN_FUNCTION_SPI,
+                            OXYGEN_FUNCTION_2WIRE_SPI_MASK);
+       oxygen_write8(chip, OXYGEN_DMA_STATUS, 0);
+       oxygen_write8(chip, OXYGEN_DMA_PAUSE, 0);
+       oxygen_write8(chip, OXYGEN_PLAY_CHANNELS,
+                     OXYGEN_PLAY_CHANNELS_2 |
+                     OXYGEN_DMA_A_BURST_8 |
+                     OXYGEN_DMA_MULTICH_BURST_8);
+       oxygen_write16(chip, OXYGEN_INTERRUPT_MASK, 0);
+       oxygen_write8_masked(chip, OXYGEN_MISC, 0,
+                            OXYGEN_MISC_WRITE_PCI_SUBID |
+                            OXYGEN_MISC_REC_C_FROM_SPDIF |
+                            OXYGEN_MISC_REC_B_FROM_AC97 |
+                            OXYGEN_MISC_REC_A_FROM_MULTICH);
+       oxygen_write8(chip, OXYGEN_REC_FORMAT,
+                     (OXYGEN_FORMAT_16 << OXYGEN_REC_FORMAT_A_SHIFT) |
+                     (OXYGEN_FORMAT_16 << OXYGEN_REC_FORMAT_B_SHIFT) |
+                     (OXYGEN_FORMAT_16 << OXYGEN_REC_FORMAT_C_SHIFT));
+       oxygen_write8(chip, OXYGEN_PLAY_FORMAT,
+                     (OXYGEN_FORMAT_16 << OXYGEN_SPDIF_FORMAT_SHIFT) |
+                     (OXYGEN_FORMAT_16 << OXYGEN_MULTICH_FORMAT_SHIFT));
+       oxygen_write8(chip, OXYGEN_REC_CHANNELS, OXYGEN_REC_CHANNELS_2_2_2);
+       oxygen_write16(chip, OXYGEN_I2S_MULTICH_FORMAT,
+                      OXYGEN_RATE_48000 | OXYGEN_I2S_FORMAT_LJUST |
+                      OXYGEN_I2S_MCLK_128 | OXYGEN_I2S_BITS_16 |
+                      OXYGEN_I2S_MASTER | OXYGEN_I2S_BCLK_64);
+       oxygen_write16(chip, OXYGEN_I2S_A_FORMAT,
+                      OXYGEN_RATE_48000 | OXYGEN_I2S_FORMAT_LJUST |
+                      OXYGEN_I2S_MCLK_128 | OXYGEN_I2S_BITS_16 |
+                      OXYGEN_I2S_MASTER | OXYGEN_I2S_BCLK_64);
+       oxygen_write16(chip, OXYGEN_I2S_B_FORMAT,
+                      OXYGEN_RATE_48000 | OXYGEN_I2S_FORMAT_LJUST |
+                      OXYGEN_I2S_MCLK_128 | OXYGEN_I2S_BITS_16 |
+                      OXYGEN_I2S_MASTER | OXYGEN_I2S_BCLK_64);
+       oxygen_write16(chip, OXYGEN_I2S_C_FORMAT,
+                      OXYGEN_RATE_48000 | OXYGEN_I2S_FORMAT_LJUST |
+                      OXYGEN_I2S_MCLK_128 | OXYGEN_I2S_BITS_16 |
+                      OXYGEN_I2S_MASTER | OXYGEN_I2S_BCLK_64);
+       oxygen_write32_masked(chip, OXYGEN_SPDIF_CONTROL,
+                             OXYGEN_SPDIF_SENSE_MASK |
+                             OXYGEN_SPDIF_LOCK_MASK |
+                             OXYGEN_SPDIF_RATE_MASK |
+                             OXYGEN_SPDIF_LOCK_PAR |
+                             OXYGEN_SPDIF_IN_CLOCK_96,
+                             OXYGEN_SPDIF_OUT_ENABLE |
+                             OXYGEN_SPDIF_LOOPBACK |
+                             OXYGEN_SPDIF_SENSE_MASK |
+                             OXYGEN_SPDIF_LOCK_MASK |
+                             OXYGEN_SPDIF_RATE_MASK |
+                             OXYGEN_SPDIF_SENSE_PAR |
+                             OXYGEN_SPDIF_LOCK_PAR |
+                             OXYGEN_SPDIF_IN_CLOCK_MASK);
+       oxygen_write32(chip, OXYGEN_SPDIF_OUTPUT_BITS, chip->spdif_bits);
+       oxygen_clear_bits8(chip, OXYGEN_MPU401_CONTROL, OXYGEN_MPU401_LOOPBACK);
+       oxygen_write8(chip, OXYGEN_GPI_INTERRUPT_MASK, 0);
+       oxygen_write16(chip, OXYGEN_GPIO_INTERRUPT_MASK, 0);
+       oxygen_write16(chip, OXYGEN_PLAY_ROUTING,
+                      OXYGEN_PLAY_MULTICH_I2S_DAC |
+                      OXYGEN_PLAY_SPDIF_SPDIF |
+                      (0 << OXYGEN_PLAY_DAC0_SOURCE_SHIFT) |
+                      (1 << OXYGEN_PLAY_DAC1_SOURCE_SHIFT) |
+                      (2 << OXYGEN_PLAY_DAC2_SOURCE_SHIFT) |
+                      (3 << OXYGEN_PLAY_DAC3_SOURCE_SHIFT));
+       oxygen_write8(chip, OXYGEN_REC_ROUTING,
+                     OXYGEN_REC_A_ROUTE_I2S_ADC_1 |
+                     OXYGEN_REC_B_ROUTE_I2S_ADC_2 |
+                     OXYGEN_REC_C_ROUTE_SPDIF);
+       oxygen_write8(chip, OXYGEN_ADC_MONITOR, 0);
+       oxygen_write8(chip, OXYGEN_A_MONITOR_ROUTING,
+                     (0 << OXYGEN_A_MONITOR_ROUTE_0_SHIFT) |
+                     (1 << OXYGEN_A_MONITOR_ROUTE_1_SHIFT) |
+                     (2 << OXYGEN_A_MONITOR_ROUTE_2_SHIFT) |
+                     (3 << OXYGEN_A_MONITOR_ROUTE_3_SHIFT));
+
+       oxygen_write8(chip, OXYGEN_AC97_INTERRUPT_MASK,
+                     OXYGEN_AC97_INT_READ_DONE |
+                     OXYGEN_AC97_INT_WRITE_DONE);
+       oxygen_write32(chip, OXYGEN_AC97_OUT_CONFIG, 0);
+       oxygen_write32(chip, OXYGEN_AC97_IN_CONFIG, 0);
+       if (!(chip->has_ac97_0 | chip->has_ac97_1))
+               oxygen_set_bits16(chip, OXYGEN_AC97_CONTROL,
+                                 OXYGEN_AC97_CLOCK_DISABLE);
+       if (!chip->has_ac97_0) {
+               oxygen_set_bits16(chip, OXYGEN_AC97_CONTROL,
+                                 OXYGEN_AC97_NO_CODEC_0);
+       } else {
+               oxygen_write_ac97(chip, 0, AC97_RESET, 0);
+               msleep(1);
+               oxygen_ac97_set_bits(chip, 0, CM9780_GPIO_SETUP,
+                                    CM9780_GPIO0IO | CM9780_GPIO1IO);
+               oxygen_ac97_set_bits(chip, 0, CM9780_MIXER,
+                                    CM9780_BSTSEL | CM9780_STRO_MIC |
+                                    CM9780_MIX2FR | CM9780_PCBSW);
+               oxygen_ac97_set_bits(chip, 0, CM9780_JACK,
+                                    CM9780_RSOE | CM9780_CBOE |
+                                    CM9780_SSOE | CM9780_FROE |
+                                    CM9780_MIC2MIC | CM9780_LI2LI);
+               oxygen_write_ac97(chip, 0, AC97_MASTER, 0x0000);
+               oxygen_write_ac97(chip, 0, AC97_PC_BEEP, 0x8000);
+               oxygen_write_ac97(chip, 0, AC97_MIC, 0x8808);
+               oxygen_write_ac97(chip, 0, AC97_LINE, 0x0808);
+               oxygen_write_ac97(chip, 0, AC97_CD, 0x8808);
+               oxygen_write_ac97(chip, 0, AC97_VIDEO, 0x8808);
+               oxygen_write_ac97(chip, 0, AC97_AUX, 0x8808);
+               oxygen_write_ac97(chip, 0, AC97_REC_GAIN, 0x8000);
+               oxygen_write_ac97(chip, 0, AC97_CENTER_LFE_MASTER, 0x8080);
+               oxygen_write_ac97(chip, 0, AC97_SURROUND_MASTER, 0x8080);
+               /* power down unused ADCs and DACs */
+               oxygen_ac97_set_bits(chip, 0, AC97_POWERDOWN,
+                                    AC97_PD_PR0 | AC97_PD_PR1);
+               oxygen_ac97_set_bits(chip, 0, AC97_EXTENDED_STATUS,
+                                    AC97_EA_PRI | AC97_EA_PRJ | AC97_EA_PRK);
+       }
+       if (chip->has_ac97_1) {
+               oxygen_set_bits32(chip, OXYGEN_AC97_OUT_CONFIG,
+                                 OXYGEN_AC97_CODEC1_SLOT3 |
+                                 OXYGEN_AC97_CODEC1_SLOT4);
+               oxygen_write_ac97(chip, 1, AC97_RESET, 0);
+               msleep(1);
+               oxygen_write_ac97(chip, 1, AC97_MASTER, 0x0000);
+               oxygen_write_ac97(chip, 1, AC97_HEADPHONE, 0x8000);
+               oxygen_write_ac97(chip, 1, AC97_PC_BEEP, 0x8000);
+               oxygen_write_ac97(chip, 1, AC97_MIC, 0x8808);
+               oxygen_write_ac97(chip, 1, AC97_LINE, 0x8808);
+               oxygen_write_ac97(chip, 1, AC97_CD, 0x8808);
+               oxygen_write_ac97(chip, 1, AC97_VIDEO, 0x8808);
+               oxygen_write_ac97(chip, 1, AC97_AUX, 0x8808);
+               oxygen_write_ac97(chip, 1, AC97_PCM, 0x0808);
+               oxygen_write_ac97(chip, 1, AC97_REC_SEL, 0x0000);
+               oxygen_write_ac97(chip, 1, AC97_REC_GAIN, 0x0000);
+               oxygen_ac97_set_bits(chip, 1, 0x6a, 0x0040);
+       }
+}
+
+static void oxygen_card_free(struct snd_card *card)
+{
+       struct oxygen *chip = card->private_data;
+
+       spin_lock_irq(&chip->reg_lock);
+       chip->interrupt_mask = 0;
+       chip->pcm_running = 0;
+       oxygen_write16(chip, OXYGEN_DMA_STATUS, 0);
+       oxygen_write16(chip, OXYGEN_INTERRUPT_MASK, 0);
+       spin_unlock_irq(&chip->reg_lock);
+       if (chip->irq >= 0) {
+               free_irq(chip->irq, chip);
+               synchronize_irq(chip->irq);
+       }
+       flush_scheduled_work();
+       chip->model->cleanup(chip);
+       mutex_destroy(&chip->mutex);
+       pci_release_regions(chip->pci);
+       pci_disable_device(chip->pci);
+}
+
+int __devinit oxygen_pci_probe(struct pci_dev *pci, int index, char *id,
+                              int midi, const struct oxygen_model *model)
+{
+       struct snd_card *card;
+       struct oxygen *chip;
+       int err;
+
+       card = snd_card_new(index, id, model->owner,
+                           sizeof *chip + model->model_data_size);
+       if (!card)
+               return -ENOMEM;
+
+       chip = card->private_data;
+       chip->card = card;
+       chip->pci = pci;
+       chip->irq = -1;
+       chip->model = model;
+       chip->model_data = chip + 1;
+       spin_lock_init(&chip->reg_lock);
+       mutex_init(&chip->mutex);
+       INIT_WORK(&chip->spdif_input_bits_work,
+                 oxygen_spdif_input_bits_changed);
+       INIT_WORK(&chip->gpio_work, oxygen_gpio_changed);
+       init_waitqueue_head(&chip->ac97_waitqueue);
+
+       err = pci_enable_device(pci);
+       if (err < 0)
+               goto err_card;
+
+       err = pci_request_regions(pci, model->chip);
+       if (err < 0) {
+               snd_printk(KERN_ERR "cannot reserve PCI resources\n");
+               goto err_pci_enable;
+       }
+
+       if (!(pci_resource_flags(pci, 0) & IORESOURCE_IO) ||
+           pci_resource_len(pci, 0) < 0x100) {
+               snd_printk(KERN_ERR "invalid PCI I/O range\n");
+               err = -ENXIO;
+               goto err_pci_regions;
+       }
+       chip->addr = pci_resource_start(pci, 0);
+
+       pci_set_master(pci);
+       snd_card_set_dev(card, &pci->dev);
+       card->private_free = oxygen_card_free;
+
+       oxygen_init(chip);
+       model->init(chip);
+
+       err = request_irq(pci->irq, oxygen_interrupt, IRQF_SHARED,
+                         model->chip, chip);
+       if (err < 0) {
+               snd_printk(KERN_ERR "cannot grab interrupt %d\n", pci->irq);
+               goto err_card;
+       }
+       chip->irq = pci->irq;
+
+       strcpy(card->driver, model->chip);
+       strcpy(card->shortname, model->shortname);
+       sprintf(card->longname, "%s (rev %u) at %#lx, irq %i",
+               model->longname, chip->revision, chip->addr, chip->irq);
+       strcpy(card->mixername, model->chip);
+       snd_component_add(card, model->chip);
+
+       err = oxygen_pcm_init(chip);
+       if (err < 0)
+               goto err_card;
+
+       err = oxygen_mixer_init(chip);
+       if (err < 0)
+               goto err_card;
+
+       oxygen_write8_masked(chip, OXYGEN_MISC,
+                            midi ? OXYGEN_MISC_MIDI : 0, OXYGEN_MISC_MIDI);
+       if (midi) {
+               err = snd_mpu401_uart_new(card, 0, MPU401_HW_CMIPCI,
+                                         chip->addr + OXYGEN_MPU401,
+                                         MPU401_INFO_INTEGRATED, 0, 0,
+                                         &chip->midi);
+               if (err < 0)
+                       goto err_card;
+       }
+
+       oxygen_proc_init(chip);
+
+       spin_lock_irq(&chip->reg_lock);
+       chip->interrupt_mask |= OXYGEN_INT_SPDIF_IN_DETECT | OXYGEN_INT_AC97;
+       oxygen_write16(chip, OXYGEN_INTERRUPT_MASK, chip->interrupt_mask);
+       spin_unlock_irq(&chip->reg_lock);
+
+       err = snd_card_register(card);
+       if (err < 0)
+               goto err_card;
+
+       pci_set_drvdata(pci, card);
+       return 0;
+
+err_pci_regions:
+       pci_release_regions(pci);
+err_pci_enable:
+       pci_disable_device(pci);
+err_card:
+       snd_card_free(card);
+       return err;
+}
+EXPORT_SYMBOL(oxygen_pci_probe);
+
+void __devexit oxygen_pci_remove(struct pci_dev *pci)
+{
+       snd_card_free(pci_get_drvdata(pci));
+       pci_set_drvdata(pci, NULL);
+}
+EXPORT_SYMBOL(oxygen_pci_remove);
diff --git a/sound/pci/oxygen/oxygen_mixer.c b/sound/pci/oxygen/oxygen_mixer.c
new file mode 100644 (file)
index 0000000..a8e4623
--- /dev/null
@@ -0,0 +1,794 @@
+/*
+ * C-Media CMI8788 driver - mixer code
+ *
+ * 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, write to the Free Software
+ *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA
+ */
+
+#include <linux/mutex.h>
+#include <sound/ac97_codec.h>
+#include <sound/asoundef.h>
+#include <sound/control.h>
+#include <sound/tlv.h>
+#include "oxygen.h"
+#include "cm9780.h"
+
+static int dac_volume_info(struct snd_kcontrol *ctl,
+                          struct snd_ctl_elem_info *info)
+{
+       struct oxygen *chip = ctl->private_data;
+
+       info->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
+       info->count = chip->model->dac_channels;
+       info->value.integer.min = 0;
+       info->value.integer.max = 0xff;
+       return 0;
+}
+
+static int dac_volume_get(struct snd_kcontrol *ctl,
+                         struct snd_ctl_elem_value *value)
+{
+       struct oxygen *chip = ctl->private_data;
+       unsigned int i;
+
+       mutex_lock(&chip->mutex);
+       for (i = 0; i < chip->model->dac_channels; ++i)
+               value->value.integer.value[i] = chip->dac_volume[i];
+       mutex_unlock(&chip->mutex);
+       return 0;
+}
+
+static int dac_volume_put(struct snd_kcontrol *ctl,
+                         struct snd_ctl_elem_value *value)
+{
+       struct oxygen *chip = ctl->private_data;
+       unsigned int i;
+       int changed;
+
+       changed = 0;
+       mutex_lock(&chip->mutex);
+       for (i = 0; i < chip->model->dac_channels; ++i)
+               if (value->value.integer.value[i] != chip->dac_volume[i]) {
+                       chip->dac_volume[i] = value->value.integer.value[i];
+                       changed = 1;
+               }
+       if (changed)
+               chip->model->update_dac_volume(chip);
+       mutex_unlock(&chip->mutex);
+       return changed;
+}
+
+static int dac_mute_get(struct snd_kcontrol *ctl,
+                       struct snd_ctl_elem_value *value)
+{
+       struct oxygen *chip = ctl->private_data;
+
+       mutex_lock(&chip->mutex);
+       value->value.integer.value[0] = !chip->dac_mute;
+       mutex_unlock(&chip->mutex);
+       return 0;
+}
+
+static int dac_mute_put(struct snd_kcontrol *ctl,
+                         struct snd_ctl_elem_value *value)
+{
+       struct oxygen *chip = ctl->private_data;
+       int changed;
+
+       mutex_lock(&chip->mutex);
+       changed = !value->value.integer.value[0] != chip->dac_mute;
+       if (changed) {
+               chip->dac_mute = !value->value.integer.value[0];
+               chip->model->update_dac_mute(chip);
+       }
+       mutex_unlock(&chip->mutex);
+       return changed;
+}
+
+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"
+       };
+       struct oxygen *chip = ctl->private_data;
+       unsigned int count = 2 + (chip->model->dac_channels == 8);
+
+       info->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED;
+       info->count = 1;
+       info->value.enumerated.items = count;
+       if (info->value.enumerated.item >= count)
+               info->value.enumerated.item = count - 1;
+       strcpy(info->value.enumerated.name, names[info->value.enumerated.item]);
+       return 0;
+}
+
+static int upmix_get(struct snd_kcontrol *ctl, struct snd_ctl_elem_value *value)
+{
+       struct oxygen *chip = ctl->private_data;
+
+       mutex_lock(&chip->mutex);
+       value->value.enumerated.item[0] = chip->dac_routing;
+       mutex_unlock(&chip->mutex);
+       return 0;
+}
+
+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] = {
+               /* stereo -> front */
+               (0 << OXYGEN_PLAY_DAC0_SOURCE_SHIFT) |
+               (1 << OXYGEN_PLAY_DAC1_SOURCE_SHIFT) |
+               (2 << OXYGEN_PLAY_DAC2_SOURCE_SHIFT) |
+               (3 << OXYGEN_PLAY_DAC3_SOURCE_SHIFT),
+               /* stereo -> front+surround */
+               (0 << OXYGEN_PLAY_DAC0_SOURCE_SHIFT) |
+               (0 << OXYGEN_PLAY_DAC1_SOURCE_SHIFT) |
+               (2 << OXYGEN_PLAY_DAC2_SOURCE_SHIFT) |
+               (3 << OXYGEN_PLAY_DAC3_SOURCE_SHIFT),
+               /* stereo -> front+surround+back */
+               (0 << OXYGEN_PLAY_DAC0_SOURCE_SHIFT) |
+               (0 << OXYGEN_PLAY_DAC1_SOURCE_SHIFT) |
+               (2 << OXYGEN_PLAY_DAC2_SOURCE_SHIFT) |
+               (0 << OXYGEN_PLAY_DAC3_SOURCE_SHIFT),
+       };
+       u8 channels;
+       unsigned int reg_value;
+
+       channels = oxygen_read8(chip, OXYGEN_PLAY_CHANNELS) &
+               OXYGEN_PLAY_CHANNELS_MASK;
+       if (channels == OXYGEN_PLAY_CHANNELS_2)
+               reg_value = reg_values[chip->dac_routing];
+       else if (channels == OXYGEN_PLAY_CHANNELS_8)
+               /* in 7.1 mode, "rear" channels go to the "back" jack */
+               reg_value = (0 << OXYGEN_PLAY_DAC0_SOURCE_SHIFT) |
+                           (3 << OXYGEN_PLAY_DAC1_SOURCE_SHIFT) |
+                           (2 << OXYGEN_PLAY_DAC2_SOURCE_SHIFT) |
+                           (1 << OXYGEN_PLAY_DAC3_SOURCE_SHIFT);
+       else
+               reg_value = (0 << OXYGEN_PLAY_DAC0_SOURCE_SHIFT) |
+                           (1 << OXYGEN_PLAY_DAC1_SOURCE_SHIFT) |
+                           (2 << OXYGEN_PLAY_DAC2_SOURCE_SHIFT) |
+                           (3 << OXYGEN_PLAY_DAC3_SOURCE_SHIFT);
+       oxygen_write16_masked(chip, OXYGEN_PLAY_ROUTING, reg_value,
+                             OXYGEN_PLAY_DAC0_SOURCE_MASK |
+                             OXYGEN_PLAY_DAC1_SOURCE_MASK |
+                             OXYGEN_PLAY_DAC2_SOURCE_MASK |
+                             OXYGEN_PLAY_DAC3_SOURCE_MASK);
+}
+
+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);
+       int changed;
+
+       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);
+               oxygen_update_dac_routing(chip);
+               spin_unlock_irq(&chip->reg_lock);
+       }
+       mutex_unlock(&chip->mutex);
+       return changed;
+}
+
+static int spdif_switch_get(struct snd_kcontrol *ctl,
+                           struct snd_ctl_elem_value *value)
+{
+       struct oxygen *chip = ctl->private_data;
+
+       mutex_lock(&chip->mutex);
+       value->value.integer.value[0] = chip->spdif_playback_enable;
+       mutex_unlock(&chip->mutex);
+       return 0;
+}
+
+static unsigned int oxygen_spdif_rate(unsigned int oxygen_rate)
+{
+       switch (oxygen_rate) {
+       case OXYGEN_RATE_32000:
+               return IEC958_AES3_CON_FS_32000 << OXYGEN_SPDIF_CS_RATE_SHIFT;
+       case OXYGEN_RATE_44100:
+               return IEC958_AES3_CON_FS_44100 << OXYGEN_SPDIF_CS_RATE_SHIFT;
+       default: /* OXYGEN_RATE_48000 */
+               return IEC958_AES3_CON_FS_48000 << OXYGEN_SPDIF_CS_RATE_SHIFT;
+       case OXYGEN_RATE_64000:
+               return 0xb << OXYGEN_SPDIF_CS_RATE_SHIFT;
+       case OXYGEN_RATE_88200:
+               return 0x8 << OXYGEN_SPDIF_CS_RATE_SHIFT;
+       case OXYGEN_RATE_96000:
+               return 0xa << OXYGEN_SPDIF_CS_RATE_SHIFT;
+       case OXYGEN_RATE_176400:
+               return 0xc << OXYGEN_SPDIF_CS_RATE_SHIFT;
+       case OXYGEN_RATE_192000:
+               return 0xe << OXYGEN_SPDIF_CS_RATE_SHIFT;
+       }
+}
+
+void oxygen_update_spdif_source(struct oxygen *chip)
+{
+       u32 old_control, new_control;
+       u16 old_routing, new_routing;
+       unsigned int oxygen_rate;
+
+       old_control = oxygen_read32(chip, OXYGEN_SPDIF_CONTROL);
+       old_routing = oxygen_read16(chip, OXYGEN_PLAY_ROUTING);
+       if (chip->pcm_active & (1 << PCM_SPDIF)) {
+               new_control = old_control | OXYGEN_SPDIF_OUT_ENABLE;
+               new_routing = (old_routing & ~OXYGEN_PLAY_SPDIF_MASK)
+                       | OXYGEN_PLAY_SPDIF_SPDIF;
+               oxygen_rate = (old_control >> OXYGEN_SPDIF_OUT_RATE_SHIFT)
+                       & OXYGEN_I2S_RATE_MASK;
+               /* S/PDIF rate was already set by the caller */
+       } else if ((chip->pcm_active & (1 << PCM_MULTICH)) &&
+                  chip->spdif_playback_enable) {
+               new_routing = (old_routing & ~OXYGEN_PLAY_SPDIF_MASK)
+                       | OXYGEN_PLAY_SPDIF_MULTICH_01;
+               oxygen_rate = oxygen_read16(chip, OXYGEN_I2S_MULTICH_FORMAT)
+                       & OXYGEN_I2S_RATE_MASK;
+               new_control = (old_control & ~OXYGEN_SPDIF_OUT_RATE_MASK) |
+                       (oxygen_rate << OXYGEN_SPDIF_OUT_RATE_SHIFT) |
+                       OXYGEN_SPDIF_OUT_ENABLE;
+       } else {
+               new_control = old_control & ~OXYGEN_SPDIF_OUT_ENABLE;
+               new_routing = old_routing;
+               oxygen_rate = OXYGEN_RATE_44100;
+       }
+       if (old_routing != new_routing) {
+               oxygen_write32(chip, OXYGEN_SPDIF_CONTROL,
+                              new_control & ~OXYGEN_SPDIF_OUT_ENABLE);
+               oxygen_write16(chip, OXYGEN_PLAY_ROUTING, new_routing);
+       }
+       if (new_control & OXYGEN_SPDIF_OUT_ENABLE)
+               oxygen_write32(chip, OXYGEN_SPDIF_OUTPUT_BITS,
+                              oxygen_spdif_rate(oxygen_rate) |
+                              ((chip->pcm_active & (1 << PCM_SPDIF)) ?
+                               chip->spdif_pcm_bits : chip->spdif_bits));
+       oxygen_write32(chip, OXYGEN_SPDIF_CONTROL, new_control);
+}
+
+static int spdif_switch_put(struct snd_kcontrol *ctl,
+                           struct snd_ctl_elem_value *value)
+{
+       struct oxygen *chip = ctl->private_data;
+       int changed;
+
+       mutex_lock(&chip->mutex);
+       changed = value->value.integer.value[0] != chip->spdif_playback_enable;
+       if (changed) {
+               chip->spdif_playback_enable = !!value->value.integer.value[0];
+               spin_lock_irq(&chip->reg_lock);
+               oxygen_update_spdif_source(chip);
+               spin_unlock_irq(&chip->reg_lock);
+       }
+       mutex_unlock(&chip->mutex);
+       return changed;
+}
+
+static int spdif_info(struct snd_kcontrol *ctl, struct snd_ctl_elem_info *info)
+{
+       info->type = SNDRV_CTL_ELEM_TYPE_IEC958;
+       info->count = 1;
+       return 0;
+}
+
+static void oxygen_to_iec958(u32 bits, struct snd_ctl_elem_value *value)
+{
+       value->value.iec958.status[0] =
+               bits & (OXYGEN_SPDIF_NONAUDIO | OXYGEN_SPDIF_C |
+                       OXYGEN_SPDIF_PREEMPHASIS);
+       value->value.iec958.status[1] = /* category and original */
+               bits >> OXYGEN_SPDIF_CATEGORY_SHIFT;
+}
+
+static u32 iec958_to_oxygen(struct snd_ctl_elem_value *value)
+{
+       u32 bits;
+
+       bits = value->value.iec958.status[0] &
+               (OXYGEN_SPDIF_NONAUDIO | OXYGEN_SPDIF_C |
+                OXYGEN_SPDIF_PREEMPHASIS);
+       bits |= value->value.iec958.status[1] << OXYGEN_SPDIF_CATEGORY_SHIFT;
+       if (bits & OXYGEN_SPDIF_NONAUDIO)
+               bits |= OXYGEN_SPDIF_V;
+       return bits;
+}
+
+static inline void write_spdif_bits(struct oxygen *chip, u32 bits)
+{
+       oxygen_write32_masked(chip, OXYGEN_SPDIF_OUTPUT_BITS, bits,
+                             OXYGEN_SPDIF_NONAUDIO |
+                             OXYGEN_SPDIF_C |
+                             OXYGEN_SPDIF_PREEMPHASIS |
+                             OXYGEN_SPDIF_CATEGORY_MASK |
+                             OXYGEN_SPDIF_ORIGINAL |
+                             OXYGEN_SPDIF_V);
+}
+
+static int spdif_default_get(struct snd_kcontrol *ctl,
+                            struct snd_ctl_elem_value *value)
+{
+       struct oxygen *chip = ctl->private_data;
+
+       mutex_lock(&chip->mutex);
+       oxygen_to_iec958(chip->spdif_bits, value);
+       mutex_unlock(&chip->mutex);
+       return 0;
+}
+
+static int spdif_default_put(struct snd_kcontrol *ctl,
+                            struct snd_ctl_elem_value *value)
+{
+       struct oxygen *chip = ctl->private_data;
+       u32 new_bits;
+       int changed;
+
+       new_bits = iec958_to_oxygen(value);
+       mutex_lock(&chip->mutex);
+       changed = new_bits != chip->spdif_bits;
+       if (changed) {
+               chip->spdif_bits = new_bits;
+               if (!(chip->pcm_active & (1 << PCM_SPDIF)))
+                       write_spdif_bits(chip, new_bits);
+       }
+       mutex_unlock(&chip->mutex);
+       return changed;
+}
+
+static int spdif_mask_get(struct snd_kcontrol *ctl,
+                         struct snd_ctl_elem_value *value)
+{
+       value->value.iec958.status[0] = IEC958_AES0_NONAUDIO |
+               IEC958_AES0_CON_NOT_COPYRIGHT | IEC958_AES0_CON_EMPHASIS;
+       value->value.iec958.status[1] =
+               IEC958_AES1_CON_CATEGORY | IEC958_AES1_CON_ORIGINAL;
+       return 0;
+}
+
+static int spdif_pcm_get(struct snd_kcontrol *ctl,
+                        struct snd_ctl_elem_value *value)
+{
+       struct oxygen *chip = ctl->private_data;
+
+       mutex_lock(&chip->mutex);
+       oxygen_to_iec958(chip->spdif_pcm_bits, value);
+       mutex_unlock(&chip->mutex);
+       return 0;
+}
+
+static int spdif_pcm_put(struct snd_kcontrol *ctl,
+                        struct snd_ctl_elem_value *value)
+{
+       struct oxygen *chip = ctl->private_data;
+       u32 new_bits;
+       int changed;
+
+       new_bits = iec958_to_oxygen(value);
+       mutex_lock(&chip->mutex);
+       changed = new_bits != chip->spdif_pcm_bits;
+       if (changed) {
+               chip->spdif_pcm_bits = new_bits;
+               if (chip->pcm_active & (1 << PCM_SPDIF))
+                       write_spdif_bits(chip, new_bits);
+       }
+       mutex_unlock(&chip->mutex);
+       return changed;
+}
+
+static int spdif_input_mask_get(struct snd_kcontrol *ctl,
+                               struct snd_ctl_elem_value *value)
+{
+       value->value.iec958.status[0] = 0xff;
+       value->value.iec958.status[1] = 0xff;
+       value->value.iec958.status[2] = 0xff;
+       value->value.iec958.status[3] = 0xff;
+       return 0;
+}
+
+static int spdif_input_default_get(struct snd_kcontrol *ctl,
+                                  struct snd_ctl_elem_value *value)
+{
+       struct oxygen *chip = ctl->private_data;
+       u32 bits;
+
+       bits = oxygen_read32(chip, OXYGEN_SPDIF_INPUT_BITS);
+       value->value.iec958.status[0] = bits;
+       value->value.iec958.status[1] = bits >> 8;
+       value->value.iec958.status[2] = bits >> 16;
+       value->value.iec958.status[3] = bits >> 24;
+       return 0;
+}
+
+static int spdif_loopback_get(struct snd_kcontrol *ctl,
+                             struct snd_ctl_elem_value *value)
+{
+       struct oxygen *chip = ctl->private_data;
+
+       value->value.integer.value[0] =
+               !!(oxygen_read32(chip, OXYGEN_SPDIF_CONTROL)
+                  & OXYGEN_SPDIF_LOOPBACK);
+       return 0;
+}
+
+static int spdif_loopback_put(struct snd_kcontrol *ctl,
+                             struct snd_ctl_elem_value *value)
+{
+       struct oxygen *chip = ctl->private_data;
+       u32 oldreg, newreg;
+       int changed;
+
+       spin_lock_irq(&chip->reg_lock);
+       oldreg = oxygen_read32(chip, OXYGEN_SPDIF_CONTROL);
+       if (value->value.integer.value[0])
+               newreg = oldreg | OXYGEN_SPDIF_LOOPBACK;
+       else
+               newreg = oldreg & ~OXYGEN_SPDIF_LOOPBACK;
+       changed = newreg != oldreg;
+       if (changed)
+               oxygen_write32(chip, OXYGEN_SPDIF_CONTROL, newreg);
+       spin_unlock_irq(&chip->reg_lock);
+       return changed;
+}
+
+static int ac97_switch_get(struct snd_kcontrol *ctl,
+                          struct snd_ctl_elem_value *value)
+{
+       struct oxygen *chip = ctl->private_data;
+       unsigned int codec = (ctl->private_value >> 24) & 1;
+       unsigned int index = ctl->private_value & 0xff;
+       unsigned int bitnr = (ctl->private_value >> 8) & 0xff;
+       int invert = ctl->private_value & (1 << 16);
+       u16 reg;
+
+       mutex_lock(&chip->mutex);
+       reg = oxygen_read_ac97(chip, codec, index);
+       mutex_unlock(&chip->mutex);
+       if (!(reg & (1 << bitnr)) ^ !invert)
+               value->value.integer.value[0] = 1;
+       else
+               value->value.integer.value[0] = 0;
+       return 0;
+}
+
+static int ac97_switch_put(struct snd_kcontrol *ctl,
+                          struct snd_ctl_elem_value *value)
+{
+       struct oxygen *chip = ctl->private_data;
+       unsigned int codec = (ctl->private_value >> 24) & 1;
+       unsigned int index = ctl->private_value & 0xff;
+       unsigned int bitnr = (ctl->private_value >> 8) & 0xff;
+       int invert = ctl->private_value & (1 << 16);
+       u16 oldreg, newreg;
+       int change;
+
+       mutex_lock(&chip->mutex);
+       oldreg = oxygen_read_ac97(chip, codec, index);
+       newreg = oldreg;
+       if (!value->value.integer.value[0] ^ !invert)
+               newreg |= 1 << bitnr;
+       else
+               newreg &= ~(1 << bitnr);
+       change = newreg != oldreg;
+       if (change) {
+               oxygen_write_ac97(chip, codec, index, newreg);
+               if (bitnr == 15 && chip->model->ac97_switch_hook)
+                       chip->model->ac97_switch_hook(chip, codec, index,
+                                                     newreg & 0x8000);
+       }
+       mutex_unlock(&chip->mutex);
+       return change;
+}
+
+static int ac97_volume_info(struct snd_kcontrol *ctl,
+                           struct snd_ctl_elem_info *info)
+{
+       info->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
+       info->count = 2;
+       info->value.integer.min = 0;
+       info->value.integer.max = 0x1f;
+       return 0;
+}
+
+static int ac97_volume_get(struct snd_kcontrol *ctl,
+                          struct snd_ctl_elem_value *value)
+{
+       struct oxygen *chip = ctl->private_data;
+       unsigned int codec = (ctl->private_value >> 24) & 1;
+       unsigned int index = ctl->private_value & 0xff;
+       u16 reg;
+
+       mutex_lock(&chip->mutex);
+       reg = oxygen_read_ac97(chip, codec, index);
+       mutex_unlock(&chip->mutex);
+       value->value.integer.value[0] = 31 - (reg & 0x1f);
+       value->value.integer.value[1] = 31 - ((reg >> 8) & 0x1f);
+       return 0;
+}
+
+static int ac97_volume_put(struct snd_kcontrol *ctl,
+                          struct snd_ctl_elem_value *value)
+{
+       struct oxygen *chip = ctl->private_data;
+       unsigned int codec = (ctl->private_value >> 24) & 1;
+       unsigned int index = ctl->private_value & 0xff;
+       u16 oldreg, newreg;
+       int change;
+
+       mutex_lock(&chip->mutex);
+       oldreg = oxygen_read_ac97(chip, codec, index);
+       newreg = oldreg;
+       newreg = (newreg & ~0x1f) |
+               (31 - (value->value.integer.value[0] & 0x1f));
+       newreg = (newreg & ~0x1f00) |
+               ((31 - (value->value.integer.value[0] & 0x1f)) << 8);
+       change = newreg != oldreg;
+       if (change)
+               oxygen_write_ac97(chip, codec, index, newreg);
+       mutex_unlock(&chip->mutex);
+       return change;
+}
+
+static int ac97_fp_rec_volume_info(struct snd_kcontrol *ctl,
+                                  struct snd_ctl_elem_info *info)
+{
+       info->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
+       info->count = 2;
+       info->value.integer.min = 0;
+       info->value.integer.max = 7;
+       return 0;
+}
+
+static int ac97_fp_rec_volume_get(struct snd_kcontrol *ctl,
+                                 struct snd_ctl_elem_value *value)
+{
+       struct oxygen *chip = ctl->private_data;
+       u16 reg;
+
+       mutex_lock(&chip->mutex);
+       reg = oxygen_read_ac97(chip, 1, AC97_REC_GAIN);
+       mutex_unlock(&chip->mutex);
+       value->value.integer.value[0] = reg & 7;
+       value->value.integer.value[1] = (reg >> 8) & 7;
+       return 0;
+}
+
+static int ac97_fp_rec_volume_put(struct snd_kcontrol *ctl,
+                                 struct snd_ctl_elem_value *value)
+{
+       struct oxygen *chip = ctl->private_data;
+       u16 oldreg, newreg;
+       int change;
+
+       mutex_lock(&chip->mutex);
+       oldreg = oxygen_read_ac97(chip, 1, AC97_REC_GAIN);
+       newreg = oldreg & ~0x0707;
+       newreg = newreg | (value->value.integer.value[0] & 7);
+       newreg = newreg | ((value->value.integer.value[0] & 7) << 8);
+       change = newreg != oldreg;
+       if (change)
+               oxygen_write_ac97(chip, 1, AC97_REC_GAIN, newreg);
+       mutex_unlock(&chip->mutex);
+       return change;
+}
+
+#define AC97_SWITCH(xname, codec, index, bitnr, invert) { \
+               .iface = SNDRV_CTL_ELEM_IFACE_MIXER, \
+               .name = xname, \
+               .info = snd_ctl_boolean_mono_info, \
+               .get = ac97_switch_get, \
+               .put = ac97_switch_put, \
+               .private_value = ((codec) << 24) | ((invert) << 16) | \
+                                ((bitnr) << 8) | (index), \
+       }
+#define AC97_VOLUME(xname, codec, index) { \
+               .iface = SNDRV_CTL_ELEM_IFACE_MIXER, \
+               .name = xname, \
+               .access = SNDRV_CTL_ELEM_ACCESS_READWRITE | \
+                         SNDRV_CTL_ELEM_ACCESS_TLV_READ, \
+               .info = ac97_volume_info, \
+               .get = ac97_volume_get, \
+               .put = ac97_volume_put, \
+               .tlv = { .p = ac97_db_scale, }, \
+               .private_value = ((codec) << 24) | (index), \
+       }
+
+static DECLARE_TLV_DB_SCALE(ac97_db_scale, -3450, 150, 0);
+static DECLARE_TLV_DB_SCALE(ac97_rec_db_scale, 0, 150, 0);
+
+static const struct snd_kcontrol_new controls[] = {
+       {
+               .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+               .name = "Master Playback Volume",
+               .access = SNDRV_CTL_ELEM_ACCESS_READWRITE,
+               .info = dac_volume_info,
+               .get = dac_volume_get,
+               .put = dac_volume_put,
+       },
+       {
+               .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+               .name = "Master Playback Switch",
+               .info = snd_ctl_boolean_mono_info,
+               .get = dac_mute_get,
+               .put = dac_mute_put,
+       },
+       {
+               .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+               .name = "Stereo Upmixing",
+               .info = upmix_info,
+               .get = upmix_get,
+               .put = upmix_put,
+       },
+       {
+               .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+               .name = SNDRV_CTL_NAME_IEC958("", PLAYBACK, SWITCH),
+               .info = snd_ctl_boolean_mono_info,
+               .get = spdif_switch_get,
+               .put = spdif_switch_put,
+       },
+       {
+               .iface = SNDRV_CTL_ELEM_IFACE_PCM,
+               .device = 1,
+               .name = SNDRV_CTL_NAME_IEC958("", PLAYBACK, DEFAULT),
+               .info = spdif_info,
+               .get = spdif_default_get,
+               .put = spdif_default_put,
+       },
+       {
+               .iface = SNDRV_CTL_ELEM_IFACE_PCM,
+               .device = 1,
+               .name = SNDRV_CTL_NAME_IEC958("", PLAYBACK, CON_MASK),
+               .access = SNDRV_CTL_ELEM_ACCESS_READ,
+               .info = spdif_info,
+               .get = spdif_mask_get,
+       },
+       {
+               .iface = SNDRV_CTL_ELEM_IFACE_PCM,
+               .device = 1,
+               .name = SNDRV_CTL_NAME_IEC958("", PLAYBACK, PCM_STREAM),
+               .access = SNDRV_CTL_ELEM_ACCESS_READWRITE |
+                         SNDRV_CTL_ELEM_ACCESS_INACTIVE,
+               .info = spdif_info,
+               .get = spdif_pcm_get,
+               .put = spdif_pcm_put,
+       },
+       {
+               .iface = SNDRV_CTL_ELEM_IFACE_PCM,
+               .device = 1,
+               .name = SNDRV_CTL_NAME_IEC958("", CAPTURE, MASK),
+               .access = SNDRV_CTL_ELEM_ACCESS_READ,
+               .info = spdif_info,
+               .get = spdif_input_mask_get,
+       },
+       {
+               .iface = SNDRV_CTL_ELEM_IFACE_PCM,
+               .device = 1,
+               .name = SNDRV_CTL_NAME_IEC958("", CAPTURE, DEFAULT),
+               .access = SNDRV_CTL_ELEM_ACCESS_READ,
+               .info = spdif_info,
+               .get = spdif_input_default_get,
+       },
+       {
+               .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+               .name = SNDRV_CTL_NAME_IEC958("Loopback ", NONE, SWITCH),
+               .info = snd_ctl_boolean_mono_info,
+               .get = spdif_loopback_get,
+               .put = spdif_loopback_put,
+       },
+};
+
+static const struct snd_kcontrol_new ac97_controls[] = {
+       AC97_VOLUME("Mic Capture Volume", 0, AC97_MIC),
+       AC97_SWITCH("Mic Capture Switch", 0, AC97_MIC, 15, 1),
+       AC97_SWITCH("Mic Boost (+20dB)", 0, AC97_MIC, 6, 0),
+       AC97_VOLUME("Line Capture Volume", 0, AC97_LINE),
+       AC97_SWITCH("Line Capture Switch", 0, AC97_LINE, 15, 1),
+       AC97_VOLUME("CD Capture Volume", 0, AC97_CD),
+       AC97_SWITCH("CD Capture Switch", 0, AC97_CD, 15, 1),
+       AC97_VOLUME("Aux Capture Volume", 0, AC97_AUX),
+       AC97_SWITCH("Aux Capture Switch", 0, AC97_AUX, 15, 1),
+};
+
+static const struct snd_kcontrol_new ac97_fp_controls[] = {
+       AC97_VOLUME("Front Panel Playback Volume", 1, AC97_HEADPHONE),
+       AC97_SWITCH("Front Panel Playback Switch", 1, AC97_HEADPHONE, 15, 1),
+       {
+               .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+               .name = "Front Panel Capture Volume",
+               .access = SNDRV_CTL_ELEM_ACCESS_READWRITE |
+                         SNDRV_CTL_ELEM_ACCESS_TLV_READ,
+               .info = ac97_fp_rec_volume_info,
+               .get = ac97_fp_rec_volume_get,
+               .put = ac97_fp_rec_volume_put,
+               .tlv = { .p = ac97_rec_db_scale, },
+       },
+       AC97_SWITCH("Front Panel Capture Switch", 1, AC97_REC_GAIN, 15, 1),
+};
+
+static void oxygen_any_ctl_free(struct snd_kcontrol *ctl)
+{
+       struct oxygen *chip = ctl->private_data;
+       unsigned int i;
+
+       /* I'm too lazy to write a function for each control :-) */
+       for (i = 0; i < ARRAY_SIZE(chip->controls); ++i)
+               chip->controls[i] = NULL;
+}
+
+static int add_controls(struct oxygen *chip,
+                       const struct snd_kcontrol_new controls[],
+                       unsigned int count)
+{
+       static const char *const known_ctl_names[CONTROL_COUNT] = {
+               [CONTROL_SPDIF_PCM] =
+                       SNDRV_CTL_NAME_IEC958("", PLAYBACK, PCM_STREAM),
+               [CONTROL_SPDIF_INPUT_BITS] =
+                       SNDRV_CTL_NAME_IEC958("", CAPTURE, DEFAULT),
+               [CONTROL_MIC_CAPTURE_SWITCH] = "Mic Capture Switch",
+               [CONTROL_LINE_CAPTURE_SWITCH] = "Line Capture Switch",
+               [CONTROL_CD_CAPTURE_SWITCH] = "CD Capture Switch",
+               [CONTROL_AUX_CAPTURE_SWITCH] = "Aux Capture Switch",
+       };
+       unsigned int i, j;
+       struct snd_kcontrol_new template;
+       struct snd_kcontrol *ctl;
+       int err;
+
+       for (i = 0; i < count; ++i) {
+               template = controls[i];
+               err = chip->model->control_filter(&template);
+               if (err < 0)
+                       return err;
+               if (err == 1)
+                       continue;
+               ctl = snd_ctl_new1(&template, chip);
+               if (!ctl)
+                       return -ENOMEM;
+               err = snd_ctl_add(chip->card, ctl);
+               if (err < 0)
+                       return err;
+               for (j = 0; j < CONTROL_COUNT; ++j)
+                       if (!strcmp(ctl->id.name, known_ctl_names[j])) {
+                               chip->controls[j] = ctl;
+                               ctl->private_free = oxygen_any_ctl_free;
+                       }
+       }
+       return 0;
+}
+
+int oxygen_mixer_init(struct oxygen *chip)
+{
+       int err;
+
+       err = add_controls(chip, controls, ARRAY_SIZE(controls));
+       if (err < 0)
+               return err;
+       if (chip->has_ac97_0) {
+               err = add_controls(chip, ac97_controls,
+                                  ARRAY_SIZE(ac97_controls));
+               if (err < 0)
+                       return err;
+       }
+       if (chip->has_ac97_1) {
+               err = add_controls(chip, ac97_fp_controls,
+                                  ARRAY_SIZE(ac97_fp_controls));
+               if (err < 0)
+                       return err;
+       }
+       return chip->model->mixer_init ? chip->model->mixer_init(chip) : 0;
+}
diff --git a/sound/pci/oxygen/oxygen_pcm.c b/sound/pci/oxygen/oxygen_pcm.c
new file mode 100644 (file)
index 0000000..dfad3db
--- /dev/null
@@ -0,0 +1,718 @@
+/*
+ * C-Media CMI8788 driver - PCM code
+ *
+ * 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, write to the Free Software
+ *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA
+ */
+
+#include <linux/pci.h>
+#include <sound/control.h>
+#include <sound/core.h>
+#include <sound/pcm.h>
+#include <sound/pcm_params.h>
+#include "oxygen.h"
+
+static const struct snd_pcm_hardware oxygen_stereo_hardware = {
+       .info = SNDRV_PCM_INFO_MMAP |
+               SNDRV_PCM_INFO_MMAP_VALID |
+               SNDRV_PCM_INFO_INTERLEAVED |
+               SNDRV_PCM_INFO_PAUSE |
+               SNDRV_PCM_INFO_SYNC_START,
+       .formats = SNDRV_PCM_FMTBIT_S16_LE |
+                  SNDRV_PCM_FMTBIT_S32_LE,
+       .rates = SNDRV_PCM_RATE_32000 |
+                SNDRV_PCM_RATE_44100 |
+                SNDRV_PCM_RATE_48000 |
+                SNDRV_PCM_RATE_64000 |
+                SNDRV_PCM_RATE_88200 |
+                SNDRV_PCM_RATE_96000 |
+                SNDRV_PCM_RATE_176400 |
+                SNDRV_PCM_RATE_192000,
+       .rate_min = 32000,
+       .rate_max = 192000,
+       .channels_min = 2,
+       .channels_max = 2,
+       .buffer_bytes_max = 256 * 1024,
+       .period_bytes_min = 128,
+       .period_bytes_max = 128 * 1024,
+       .periods_min = 2,
+       .periods_max = 2048,
+};
+static const struct snd_pcm_hardware oxygen_multichannel_hardware = {
+       .info = SNDRV_PCM_INFO_MMAP |
+               SNDRV_PCM_INFO_MMAP_VALID |
+               SNDRV_PCM_INFO_INTERLEAVED |
+               SNDRV_PCM_INFO_PAUSE |
+               SNDRV_PCM_INFO_SYNC_START,
+       .formats = SNDRV_PCM_FMTBIT_S16_LE |
+                  SNDRV_PCM_FMTBIT_S32_LE,
+       .rates = SNDRV_PCM_RATE_32000 |
+                SNDRV_PCM_RATE_44100 |
+                SNDRV_PCM_RATE_48000 |
+                SNDRV_PCM_RATE_64000 |
+                SNDRV_PCM_RATE_88200 |
+                SNDRV_PCM_RATE_96000 |
+                SNDRV_PCM_RATE_176400 |
+                SNDRV_PCM_RATE_192000,
+       .rate_min = 32000,
+       .rate_max = 192000,
+       .channels_min = 2,
+       .channels_max = 8,
+       .buffer_bytes_max = 2048 * 1024,
+       .period_bytes_min = 128,
+       .period_bytes_max = 256 * 1024,
+       .periods_min = 2,
+       .periods_max = 16384,
+};
+static const struct snd_pcm_hardware oxygen_ac97_hardware = {
+       .info = SNDRV_PCM_INFO_MMAP |
+               SNDRV_PCM_INFO_MMAP_VALID |
+               SNDRV_PCM_INFO_INTERLEAVED |
+               SNDRV_PCM_INFO_PAUSE |
+               SNDRV_PCM_INFO_SYNC_START,
+       .formats = SNDRV_PCM_FMTBIT_S16_LE,
+       .rates = SNDRV_PCM_RATE_48000,
+       .rate_min = 48000,
+       .rate_max = 48000,
+       .channels_min = 2,
+       .channels_max = 2,
+       .buffer_bytes_max = 256 * 1024,
+       .period_bytes_min = 128,
+       .period_bytes_max = 128 * 1024,
+       .periods_min = 2,
+       .periods_max = 2048,
+};
+
+static const struct snd_pcm_hardware *const oxygen_hardware[PCM_COUNT] = {
+       [PCM_A] = &oxygen_stereo_hardware,
+       [PCM_B] = &oxygen_stereo_hardware,
+       [PCM_C] = &oxygen_stereo_hardware,
+       [PCM_SPDIF] = &oxygen_stereo_hardware,
+       [PCM_MULTICH] = &oxygen_multichannel_hardware,
+       [PCM_AC97] = &oxygen_ac97_hardware,
+};
+
+static inline unsigned int
+oxygen_substream_channel(struct snd_pcm_substream *substream)
+{
+       return (unsigned int)(uintptr_t)substream->runtime->private_data;
+}
+
+static int oxygen_open(struct snd_pcm_substream *substream,
+                      unsigned int channel)
+{
+       struct oxygen *chip = snd_pcm_substream_chip(substream);
+       struct snd_pcm_runtime *runtime = substream->runtime;
+       int err;
+
+       runtime->private_data = (void *)(uintptr_t)channel;
+       if (channel == PCM_B && chip->has_ac97_1 &&
+           (chip->model->used_channels & OXYGEN_CHANNEL_AC97))
+               runtime->hw = oxygen_ac97_hardware;
+       else
+               runtime->hw = *oxygen_hardware[channel];
+       switch (channel) {
+       case PCM_C:
+               runtime->hw.rates &= ~(SNDRV_PCM_RATE_32000 |
+                                      SNDRV_PCM_RATE_64000);
+               runtime->hw.rate_min = 44100;
+               break;
+       case PCM_MULTICH:
+               runtime->hw.channels_max = chip->model->dac_channels;
+               break;
+       }
+       if (chip->model->pcm_hardware_filter)
+               chip->model->pcm_hardware_filter(channel, &runtime->hw);
+       err = snd_pcm_hw_constraint_step(runtime, 0,
+                                        SNDRV_PCM_HW_PARAM_PERIOD_BYTES, 32);
+       if (err < 0)
+               return err;
+       err = snd_pcm_hw_constraint_step(runtime, 0,
+                                        SNDRV_PCM_HW_PARAM_BUFFER_BYTES, 32);
+       if (err < 0)
+               return err;
+       if (runtime->hw.formats & SNDRV_PCM_FMTBIT_S32_LE) {
+               err = snd_pcm_hw_constraint_msbits(runtime, 0, 32, 24);
+               if (err < 0)
+                       return err;
+       }
+       if (runtime->hw.channels_max > 2) {
+               err = snd_pcm_hw_constraint_step(runtime, 0,
+                                                SNDRV_PCM_HW_PARAM_CHANNELS,
+                                                2);
+               if (err < 0)
+                       return err;
+       }
+       snd_pcm_set_sync(substream);
+       chip->streams[channel] = substream;
+
+       mutex_lock(&chip->mutex);
+       chip->pcm_active |= 1 << channel;
+       if (channel == PCM_SPDIF) {
+               chip->spdif_pcm_bits = chip->spdif_bits;
+               chip->controls[CONTROL_SPDIF_PCM]->vd[0].access &=
+                       ~SNDRV_CTL_ELEM_ACCESS_INACTIVE;
+               snd_ctl_notify(chip->card, SNDRV_CTL_EVENT_MASK_VALUE |
+                              SNDRV_CTL_EVENT_MASK_INFO,
+                              &chip->controls[CONTROL_SPDIF_PCM]->id);
+       }
+       mutex_unlock(&chip->mutex);
+
+       return 0;
+}
+
+static int oxygen_rec_a_open(struct snd_pcm_substream *substream)
+{
+       return oxygen_open(substream, PCM_A);
+}
+
+static int oxygen_rec_b_open(struct snd_pcm_substream *substream)
+{
+       return oxygen_open(substream, PCM_B);
+}
+
+static int oxygen_rec_c_open(struct snd_pcm_substream *substream)
+{
+       return oxygen_open(substream, PCM_C);
+}
+
+static int oxygen_spdif_open(struct snd_pcm_substream *substream)
+{
+       return oxygen_open(substream, PCM_SPDIF);
+}
+
+static int oxygen_multich_open(struct snd_pcm_substream *substream)
+{
+       return oxygen_open(substream, PCM_MULTICH);
+}
+
+static int oxygen_ac97_open(struct snd_pcm_substream *substream)
+{
+       return oxygen_open(substream, PCM_AC97);
+}
+
+static int oxygen_close(struct snd_pcm_substream *substream)
+{
+       struct oxygen *chip = snd_pcm_substream_chip(substream);
+       unsigned int channel = oxygen_substream_channel(substream);
+
+       mutex_lock(&chip->mutex);
+       chip->pcm_active &= ~(1 << channel);
+       if (channel == PCM_SPDIF) {
+               chip->controls[CONTROL_SPDIF_PCM]->vd[0].access |=
+                       SNDRV_CTL_ELEM_ACCESS_INACTIVE;
+               snd_ctl_notify(chip->card, SNDRV_CTL_EVENT_MASK_VALUE |
+                              SNDRV_CTL_EVENT_MASK_INFO,
+                              &chip->controls[CONTROL_SPDIF_PCM]->id);
+       }
+       if (channel == PCM_SPDIF || channel == PCM_MULTICH)
+               oxygen_update_spdif_source(chip);
+       mutex_unlock(&chip->mutex);
+
+       chip->streams[channel] = NULL;
+       return 0;
+}
+
+static unsigned int oxygen_format(struct snd_pcm_hw_params *hw_params)
+{
+       if (params_format(hw_params) == SNDRV_PCM_FORMAT_S32_LE)
+               return OXYGEN_FORMAT_24;
+       else
+               return OXYGEN_FORMAT_16;
+}
+
+static unsigned int oxygen_rate(struct snd_pcm_hw_params *hw_params)
+{
+       switch (params_rate(hw_params)) {
+       case 32000:
+               return OXYGEN_RATE_32000;
+       case 44100:
+               return OXYGEN_RATE_44100;
+       default: /* 48000 */
+               return OXYGEN_RATE_48000;
+       case 64000:
+               return OXYGEN_RATE_64000;
+       case 88200:
+               return OXYGEN_RATE_88200;
+       case 96000:
+               return OXYGEN_RATE_96000;
+       case 176400:
+               return OXYGEN_RATE_176400;
+       case 192000:
+               return OXYGEN_RATE_192000;
+       }
+}
+
+static unsigned int oxygen_i2s_mclk(struct snd_pcm_hw_params *hw_params)
+{
+       if (params_rate(hw_params) <= 96000)
+               return OXYGEN_I2S_MCLK_256;
+       else
+               return OXYGEN_I2S_MCLK_128;
+}
+
+static unsigned int oxygen_i2s_bits(struct snd_pcm_hw_params *hw_params)
+{
+       if (params_format(hw_params) == SNDRV_PCM_FORMAT_S32_LE)
+               return OXYGEN_I2S_BITS_24;
+       else
+               return OXYGEN_I2S_BITS_16;
+}
+
+static unsigned int oxygen_play_channels(struct snd_pcm_hw_params *hw_params)
+{
+       switch (params_channels(hw_params)) {
+       default: /* 2 */
+               return OXYGEN_PLAY_CHANNELS_2;
+       case 4:
+               return OXYGEN_PLAY_CHANNELS_4;
+       case 6:
+               return OXYGEN_PLAY_CHANNELS_6;
+       case 8:
+               return OXYGEN_PLAY_CHANNELS_8;
+       }
+}
+
+static const unsigned int channel_base_registers[PCM_COUNT] = {
+       [PCM_A] = OXYGEN_DMA_A_ADDRESS,
+       [PCM_B] = OXYGEN_DMA_B_ADDRESS,
+       [PCM_C] = OXYGEN_DMA_C_ADDRESS,
+       [PCM_SPDIF] = OXYGEN_DMA_SPDIF_ADDRESS,
+       [PCM_MULTICH] = OXYGEN_DMA_MULTICH_ADDRESS,
+       [PCM_AC97] = OXYGEN_DMA_AC97_ADDRESS,
+};
+
+static int oxygen_hw_params(struct snd_pcm_substream *substream,
+                           struct snd_pcm_hw_params *hw_params)
+{
+       struct oxygen *chip = snd_pcm_substream_chip(substream);
+       unsigned int channel = oxygen_substream_channel(substream);
+       int err;
+
+       err = snd_pcm_lib_malloc_pages(substream,
+                                      params_buffer_bytes(hw_params));
+       if (err < 0)
+               return err;
+
+       oxygen_write32(chip, channel_base_registers[channel],
+                      (u32)substream->runtime->dma_addr);
+       if (channel == PCM_MULTICH) {
+               oxygen_write32(chip, OXYGEN_DMA_MULTICH_COUNT,
+                              params_buffer_bytes(hw_params) / 4 - 1);
+               oxygen_write32(chip, OXYGEN_DMA_MULTICH_TCOUNT,
+                              params_period_bytes(hw_params) / 4 - 1);
+       } else {
+               oxygen_write16(chip, channel_base_registers[channel] + 4,
+                              params_buffer_bytes(hw_params) / 4 - 1);
+               oxygen_write16(chip, channel_base_registers[channel] + 6,
+                              params_period_bytes(hw_params) / 4 - 1);
+       }
+       return 0;
+}
+
+static int oxygen_rec_a_hw_params(struct snd_pcm_substream *substream,
+                                 struct snd_pcm_hw_params *hw_params)
+{
+       struct oxygen *chip = snd_pcm_substream_chip(substream);
+       int err;
+
+       err = oxygen_hw_params(substream, hw_params);
+       if (err < 0)
+               return err;
+
+       spin_lock_irq(&chip->reg_lock);
+       oxygen_write8_masked(chip, OXYGEN_REC_FORMAT,
+                            oxygen_format(hw_params) << OXYGEN_REC_FORMAT_A_SHIFT,
+                            OXYGEN_REC_FORMAT_A_MASK);
+       oxygen_write16_masked(chip, OXYGEN_I2S_A_FORMAT,
+                             oxygen_rate(hw_params) |
+                             oxygen_i2s_mclk(hw_params) |
+                             chip->model->adc_i2s_format |
+                             oxygen_i2s_bits(hw_params),
+                             OXYGEN_I2S_RATE_MASK |
+                             OXYGEN_I2S_FORMAT_MASK |
+                             OXYGEN_I2S_MCLK_MASK |
+                             OXYGEN_I2S_BITS_MASK);
+       spin_unlock_irq(&chip->reg_lock);
+
+       mutex_lock(&chip->mutex);
+       chip->model->set_adc_params(chip, hw_params);
+       mutex_unlock(&chip->mutex);
+       return 0;
+}
+
+static int oxygen_rec_b_hw_params(struct snd_pcm_substream *substream,
+                                 struct snd_pcm_hw_params *hw_params)
+{
+       struct oxygen *chip = snd_pcm_substream_chip(substream);
+       int is_ac97;
+       int err;
+
+       err = oxygen_hw_params(substream, hw_params);
+       if (err < 0)
+               return err;
+
+       is_ac97 = chip->has_ac97_1 &&
+               (chip->model->used_channels & OXYGEN_CHANNEL_AC97);
+
+       spin_lock_irq(&chip->reg_lock);
+       oxygen_write8_masked(chip, OXYGEN_REC_FORMAT,
+                            oxygen_format(hw_params) << OXYGEN_REC_FORMAT_B_SHIFT,
+                            OXYGEN_REC_FORMAT_B_MASK);
+       if (!is_ac97)
+               oxygen_write16_masked(chip, OXYGEN_I2S_B_FORMAT,
+                                     oxygen_rate(hw_params) |
+                                     oxygen_i2s_mclk(hw_params) |
+                                     chip->model->adc_i2s_format |
+                                     oxygen_i2s_bits(hw_params),
+                                     OXYGEN_I2S_RATE_MASK |
+                                     OXYGEN_I2S_FORMAT_MASK |
+                                     OXYGEN_I2S_MCLK_MASK |
+                                     OXYGEN_I2S_BITS_MASK);
+       spin_unlock_irq(&chip->reg_lock);
+
+       if (!is_ac97) {
+               mutex_lock(&chip->mutex);
+               chip->model->set_adc_params(chip, hw_params);
+               mutex_unlock(&chip->mutex);
+       }
+       return 0;
+}
+
+static int oxygen_rec_c_hw_params(struct snd_pcm_substream *substream,
+                                 struct snd_pcm_hw_params *hw_params)
+{
+       struct oxygen *chip = snd_pcm_substream_chip(substream);
+       int err;
+
+       err = oxygen_hw_params(substream, hw_params);
+       if (err < 0)
+               return err;
+
+       spin_lock_irq(&chip->reg_lock);
+       oxygen_write8_masked(chip, OXYGEN_REC_FORMAT,
+                            oxygen_format(hw_params) << OXYGEN_REC_FORMAT_C_SHIFT,
+                            OXYGEN_REC_FORMAT_C_MASK);
+       spin_unlock_irq(&chip->reg_lock);
+       return 0;
+}
+
+static int oxygen_spdif_hw_params(struct snd_pcm_substream *substream,
+                                 struct snd_pcm_hw_params *hw_params)
+{
+       struct oxygen *chip = snd_pcm_substream_chip(substream);
+       int err;
+
+       err = oxygen_hw_params(substream, hw_params);
+       if (err < 0)
+               return err;
+
+       spin_lock_irq(&chip->reg_lock);
+       oxygen_clear_bits32(chip, OXYGEN_SPDIF_CONTROL,
+                           OXYGEN_SPDIF_OUT_ENABLE);
+       oxygen_write8_masked(chip, OXYGEN_PLAY_FORMAT,
+                            oxygen_format(hw_params) << OXYGEN_SPDIF_FORMAT_SHIFT,
+                            OXYGEN_SPDIF_FORMAT_MASK);
+       oxygen_write32_masked(chip, OXYGEN_SPDIF_CONTROL,
+                             oxygen_rate(hw_params) << OXYGEN_SPDIF_OUT_RATE_SHIFT,
+                             OXYGEN_SPDIF_OUT_RATE_MASK);
+       oxygen_update_spdif_source(chip);
+       spin_unlock_irq(&chip->reg_lock);
+       return 0;
+}
+
+static int oxygen_multich_hw_params(struct snd_pcm_substream *substream,
+                                   struct snd_pcm_hw_params *hw_params)
+{
+       struct oxygen *chip = snd_pcm_substream_chip(substream);
+       int err;
+
+       err = oxygen_hw_params(substream, hw_params);
+       if (err < 0)
+               return err;
+
+       spin_lock_irq(&chip->reg_lock);
+       oxygen_write8_masked(chip, OXYGEN_PLAY_CHANNELS,
+                            oxygen_play_channels(hw_params),
+                            OXYGEN_PLAY_CHANNELS_MASK);
+       oxygen_write8_masked(chip, OXYGEN_PLAY_FORMAT,
+                            oxygen_format(hw_params) << OXYGEN_MULTICH_FORMAT_SHIFT,
+                            OXYGEN_MULTICH_FORMAT_MASK);
+       oxygen_write16_masked(chip, OXYGEN_I2S_MULTICH_FORMAT,
+                             oxygen_rate(hw_params) |
+                             chip->model->dac_i2s_format |
+                             oxygen_i2s_bits(hw_params),
+                             OXYGEN_I2S_RATE_MASK |
+                             OXYGEN_I2S_FORMAT_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);
+       mutex_unlock(&chip->mutex);
+       return 0;
+}
+
+static int oxygen_hw_free(struct snd_pcm_substream *substream)
+{
+       struct oxygen *chip = snd_pcm_substream_chip(substream);
+       unsigned int channel = oxygen_substream_channel(substream);
+
+       spin_lock_irq(&chip->reg_lock);
+       chip->interrupt_mask &= ~(1 << channel);
+       oxygen_write16(chip, OXYGEN_INTERRUPT_MASK, chip->interrupt_mask);
+       spin_unlock_irq(&chip->reg_lock);
+
+       return snd_pcm_lib_free_pages(substream);
+}
+
+static int oxygen_spdif_hw_free(struct snd_pcm_substream *substream)
+{
+       struct oxygen *chip = snd_pcm_substream_chip(substream);
+
+       spin_lock_irq(&chip->reg_lock);
+       oxygen_clear_bits32(chip, OXYGEN_SPDIF_CONTROL,
+                           OXYGEN_SPDIF_OUT_ENABLE);
+       spin_unlock_irq(&chip->reg_lock);
+       return oxygen_hw_free(substream);
+}
+
+static int oxygen_prepare(struct snd_pcm_substream *substream)
+{
+       struct oxygen *chip = snd_pcm_substream_chip(substream);
+       unsigned int channel = oxygen_substream_channel(substream);
+       unsigned int channel_mask = 1 << channel;
+
+       spin_lock_irq(&chip->reg_lock);
+       oxygen_set_bits8(chip, OXYGEN_DMA_FLUSH, channel_mask);
+       oxygen_clear_bits8(chip, OXYGEN_DMA_FLUSH, channel_mask);
+
+       chip->interrupt_mask |= channel_mask;
+       oxygen_write16(chip, OXYGEN_INTERRUPT_MASK, chip->interrupt_mask);
+       spin_unlock_irq(&chip->reg_lock);
+       return 0;
+}
+
+static int oxygen_trigger(struct snd_pcm_substream *substream, int cmd)
+{
+       struct oxygen *chip = snd_pcm_substream_chip(substream);
+       struct snd_pcm_substream *s;
+       unsigned int mask = 0;
+       int pausing;
+
+       switch (cmd) {
+       case SNDRV_PCM_TRIGGER_STOP:
+       case SNDRV_PCM_TRIGGER_START:
+               pausing = 0;
+               break;
+       case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
+       case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
+               pausing = 1;
+               break;
+       default:
+               return -EINVAL;
+       }
+
+       snd_pcm_group_for_each_entry(s, substream) {
+               if (snd_pcm_substream_chip(s) == chip) {
+                       mask |= 1 << oxygen_substream_channel(s);
+                       snd_pcm_trigger_done(s, substream);
+               }
+       }
+
+       spin_lock(&chip->reg_lock);
+       if (!pausing) {
+               if (cmd == SNDRV_PCM_TRIGGER_START)
+                       chip->pcm_running |= mask;
+               else
+                       chip->pcm_running &= ~mask;
+               oxygen_write8(chip, OXYGEN_DMA_STATUS, chip->pcm_running);
+       } else {
+               if (cmd == SNDRV_PCM_TRIGGER_PAUSE_PUSH)
+                       oxygen_set_bits8(chip, OXYGEN_DMA_PAUSE, mask);
+               else
+                       oxygen_clear_bits8(chip, OXYGEN_DMA_PAUSE, mask);
+       }
+       spin_unlock(&chip->reg_lock);
+       return 0;
+}
+
+static snd_pcm_uframes_t oxygen_pointer(struct snd_pcm_substream *substream)
+{
+       struct oxygen *chip = snd_pcm_substream_chip(substream);
+       struct snd_pcm_runtime *runtime = substream->runtime;
+       unsigned int channel = oxygen_substream_channel(substream);
+       u32 curr_addr;
+
+       /* no spinlock, this read should be atomic */
+       curr_addr = oxygen_read32(chip, channel_base_registers[channel]);
+       return bytes_to_frames(runtime, curr_addr - (u32)runtime->dma_addr);
+}
+
+static struct snd_pcm_ops oxygen_rec_a_ops = {
+       .open      = oxygen_rec_a_open,
+       .close     = oxygen_close,
+       .ioctl     = snd_pcm_lib_ioctl,
+       .hw_params = oxygen_rec_a_hw_params,
+       .hw_free   = oxygen_hw_free,
+       .prepare   = oxygen_prepare,
+       .trigger   = oxygen_trigger,
+       .pointer   = oxygen_pointer,
+};
+
+static struct snd_pcm_ops oxygen_rec_b_ops = {
+       .open      = oxygen_rec_b_open,
+       .close     = oxygen_close,
+       .ioctl     = snd_pcm_lib_ioctl,
+       .hw_params = oxygen_rec_b_hw_params,
+       .hw_free   = oxygen_hw_free,
+       .prepare   = oxygen_prepare,
+       .trigger   = oxygen_trigger,
+       .pointer   = oxygen_pointer,
+};
+
+static struct snd_pcm_ops oxygen_rec_c_ops = {
+       .open      = oxygen_rec_c_open,
+       .close     = oxygen_close,
+       .ioctl     = snd_pcm_lib_ioctl,
+       .hw_params = oxygen_rec_c_hw_params,
+       .hw_free   = oxygen_hw_free,
+       .prepare   = oxygen_prepare,
+       .trigger   = oxygen_trigger,
+       .pointer   = oxygen_pointer,
+};
+
+static struct snd_pcm_ops oxygen_spdif_ops = {
+       .open      = oxygen_spdif_open,
+       .close     = oxygen_close,
+       .ioctl     = snd_pcm_lib_ioctl,
+       .hw_params = oxygen_spdif_hw_params,
+       .hw_free   = oxygen_spdif_hw_free,
+       .prepare   = oxygen_prepare,
+       .trigger   = oxygen_trigger,
+       .pointer   = oxygen_pointer,
+};
+
+static struct snd_pcm_ops oxygen_multich_ops = {
+       .open      = oxygen_multich_open,
+       .close     = oxygen_close,
+       .ioctl     = snd_pcm_lib_ioctl,
+       .hw_params = oxygen_multich_hw_params,
+       .hw_free   = oxygen_hw_free,
+       .prepare   = oxygen_prepare,
+       .trigger   = oxygen_trigger,
+       .pointer   = oxygen_pointer,
+};
+
+static struct snd_pcm_ops oxygen_ac97_ops = {
+       .open      = oxygen_ac97_open,
+       .close     = oxygen_close,
+       .ioctl     = snd_pcm_lib_ioctl,
+       .hw_params = oxygen_hw_params,
+       .hw_free   = oxygen_hw_free,
+       .prepare   = oxygen_prepare,
+       .trigger   = oxygen_trigger,
+       .pointer   = oxygen_pointer,
+};
+
+static void oxygen_pcm_free(struct snd_pcm *pcm)
+{
+       snd_pcm_lib_preallocate_free_for_all(pcm);
+}
+
+int __devinit oxygen_pcm_init(struct oxygen *chip)
+{
+       struct snd_pcm *pcm;
+       int outs, ins;
+       int err;
+
+       outs = 1; /* OXYGEN_CHANNEL_MULTICH is always used */
+       ins = !!(chip->model->used_channels & (OXYGEN_CHANNEL_A |
+                                              OXYGEN_CHANNEL_B));
+       err = snd_pcm_new(chip->card, "Analog", 0, outs, ins, &pcm);
+       if (err < 0)
+               return err;
+       snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &oxygen_multich_ops);
+       if (chip->model->used_channels & OXYGEN_CHANNEL_A)
+               snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE,
+                               &oxygen_rec_a_ops);
+       else if (chip->model->used_channels & OXYGEN_CHANNEL_B)
+               snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE,
+                               &oxygen_rec_b_ops);
+       pcm->private_data = chip;
+       pcm->private_free = oxygen_pcm_free;
+       strcpy(pcm->name, "Analog");
+       snd_pcm_lib_preallocate_pages(pcm->streams[SNDRV_PCM_STREAM_PLAYBACK].substream,
+                                     SNDRV_DMA_TYPE_DEV,
+                                     snd_dma_pci_data(chip->pci),
+                                     512 * 1024, 2048 * 1024);
+       if (ins)
+               snd_pcm_lib_preallocate_pages(pcm->streams[SNDRV_PCM_STREAM_CAPTURE].substream,
+                                             SNDRV_DMA_TYPE_DEV,
+                                             snd_dma_pci_data(chip->pci),
+                                             128 * 1024, 256 * 1024);
+
+       outs = !!(chip->model->used_channels & OXYGEN_CHANNEL_SPDIF);
+       ins = !!(chip->model->used_channels & OXYGEN_CHANNEL_C);
+       if (outs | ins) {
+               err = snd_pcm_new(chip->card, "Digital", 1, outs, ins, &pcm);
+               if (err < 0)
+                       return err;
+               if (outs)
+                       snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK,
+                                       &oxygen_spdif_ops);
+               if (ins)
+                       snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE,
+                                       &oxygen_rec_c_ops);
+               pcm->private_data = chip;
+               pcm->private_free = oxygen_pcm_free;
+               strcpy(pcm->name, "Digital");
+               snd_pcm_lib_preallocate_pages_for_all(pcm, SNDRV_DMA_TYPE_DEV,
+                                                     snd_dma_pci_data(chip->pci),
+                                                     128 * 1024, 256 * 1024);
+       }
+
+       outs = chip->has_ac97_1 &&
+               (chip->model->used_channels & OXYGEN_CHANNEL_AC97);
+       ins = outs ||
+               (chip->model->used_channels & (OXYGEN_CHANNEL_A |
+                                              OXYGEN_CHANNEL_B))
+               == (OXYGEN_CHANNEL_A | OXYGEN_CHANNEL_B);
+       if (outs | ins) {
+               err = snd_pcm_new(chip->card, outs ? "AC97" : "Analog2",
+                                 2, outs, ins, &pcm);
+               if (err < 0)
+                       return err;
+               if (outs) {
+                       snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK,
+                                       &oxygen_ac97_ops);
+                       oxygen_write8_masked(chip, OXYGEN_REC_ROUTING,
+                                            OXYGEN_REC_B_ROUTE_AC97_1,
+                                            OXYGEN_REC_B_ROUTE_MASK);
+               }
+               if (ins)
+                       snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE,
+                                       &oxygen_rec_b_ops);
+               pcm->private_data = chip;
+               pcm->private_free = oxygen_pcm_free;
+               strcpy(pcm->name, outs ? "Front Panel" : "Analog 2");
+               snd_pcm_lib_preallocate_pages_for_all(pcm, SNDRV_DMA_TYPE_DEV,
+                                                     snd_dma_pci_data(chip->pci),
+                                                     128 * 1024, 256 * 1024);
+       }
+       return 0;
+}
diff --git a/sound/pci/oxygen/oxygen_regs.h b/sound/pci/oxygen/oxygen_regs.h
new file mode 100644 (file)
index 0000000..72de159
--- /dev/null
@@ -0,0 +1,453 @@
+#ifndef OXYGEN_REGS_H_INCLUDED
+#define OXYGEN_REGS_H_INCLUDED
+
+/* recording channel A */
+#define OXYGEN_DMA_A_ADDRESS           0x00    /* 32-bit base address */
+#define OXYGEN_DMA_A_COUNT             0x04    /* buffer counter (dwords) */
+#define OXYGEN_DMA_A_TCOUNT            0x06    /* interrupt counter (dwords) */
+
+/* recording channel B */
+#define OXYGEN_DMA_B_ADDRESS           0x08
+#define OXYGEN_DMA_B_COUNT             0x0c
+#define OXYGEN_DMA_B_TCOUNT            0x0e
+
+/* recording channel C */
+#define OXYGEN_DMA_C_ADDRESS           0x10
+#define OXYGEN_DMA_C_COUNT             0x14
+#define OXYGEN_DMA_C_TCOUNT            0x16
+
+/* SPDIF playback channel */
+#define OXYGEN_DMA_SPDIF_ADDRESS       0x18
+#define OXYGEN_DMA_SPDIF_COUNT         0x1c
+#define OXYGEN_DMA_SPDIF_TCOUNT                0x1e
+
+/* multichannel playback channel */
+#define OXYGEN_DMA_MULTICH_ADDRESS     0x20
+#define OXYGEN_DMA_MULTICH_COUNT       0x24    /* 24 bits */
+#define OXYGEN_DMA_MULTICH_TCOUNT      0x28    /* 24 bits */
+
+/* AC'97 (front panel) playback channel */
+#define OXYGEN_DMA_AC97_ADDRESS                0x30
+#define OXYGEN_DMA_AC97_COUNT          0x34
+#define OXYGEN_DMA_AC97_TCOUNT         0x36
+
+/* all registers 0x00..0x36 return current position on read */
+
+#define OXYGEN_DMA_STATUS              0x40    /* 1 = running, 0 = stop */
+#define  OXYGEN_CHANNEL_A              0x01
+#define  OXYGEN_CHANNEL_B              0x02
+#define  OXYGEN_CHANNEL_C              0x04
+#define  OXYGEN_CHANNEL_SPDIF          0x08
+#define  OXYGEN_CHANNEL_MULTICH                0x10
+#define  OXYGEN_CHANNEL_AC97           0x20
+
+#define OXYGEN_DMA_PAUSE               0x41    /* 1 = pause */
+/* OXYGEN_CHANNEL_* */
+
+#define OXYGEN_DMA_RESET               0x42
+/* OXYGEN_CHANNEL_* */
+
+#define OXYGEN_PLAY_CHANNELS           0x43
+#define  OXYGEN_PLAY_CHANNELS_MASK     0x03
+#define  OXYGEN_PLAY_CHANNELS_2                0x00
+#define  OXYGEN_PLAY_CHANNELS_4                0x01
+#define  OXYGEN_PLAY_CHANNELS_6                0x02
+#define  OXYGEN_PLAY_CHANNELS_8                0x03
+#define  OXYGEN_DMA_A_BURST_MASK       0x04
+#define  OXYGEN_DMA_A_BURST_8          0x00    /* dwords */
+#define  OXYGEN_DMA_A_BURST_16         0x04
+#define  OXYGEN_DMA_MULTICH_BURST_MASK 0x08
+#define  OXYGEN_DMA_MULTICH_BURST_8    0x00
+#define  OXYGEN_DMA_MULTICH_BURST_16   0x08
+
+#define OXYGEN_INTERRUPT_MASK          0x44
+/* OXYGEN_CHANNEL_* */
+#define  OXYGEN_INT_SPDIF_IN_DETECT    0x0100
+#define  OXYGEN_INT_MCU                        0x0200
+#define  OXYGEN_INT_2WIRE              0x0400
+#define  OXYGEN_INT_GPIO               0x0800
+#define  OXYGEN_INT_MCB                        0x2000
+#define  OXYGEN_INT_AC97               0x4000
+
+#define OXYGEN_INTERRUPT_STATUS                0x46
+/* OXYGEN_CHANNEL_* amd OXYGEN_INT_* */
+#define  OXYGEN_INT_MIDI               0x1000
+
+#define OXYGEN_MISC                    0x48
+#define  OXYGEN_MISC_WRITE_PCI_SUBID   0x01
+#define  OXYGEN_MISC_LATENCY_3F                0x02
+#define  OXYGEN_MISC_REC_C_FROM_SPDIF  0x04
+#define  OXYGEN_MISC_REC_B_FROM_AC97   0x08
+#define  OXYGEN_MISC_REC_A_FROM_MULTICH        0x10
+#define  OXYGEN_MISC_PCI_MEM_W_1_CLOCK 0x20
+#define  OXYGEN_MISC_MIDI              0x40
+#define  OXYGEN_MISC_CRYSTAL_MASK      0x80
+#define  OXYGEN_MISC_CRYSTAL_24576     0x00
+#define  OXYGEN_MISC_CRYSTAL_27                0x80    /* MHz */
+
+#define OXYGEN_REC_FORMAT              0x4a
+#define  OXYGEN_REC_FORMAT_A_MASK      0x03
+#define  OXYGEN_REC_FORMAT_A_SHIFT     0
+#define  OXYGEN_REC_FORMAT_B_MASK      0x0c
+#define  OXYGEN_REC_FORMAT_B_SHIFT     2
+#define  OXYGEN_REC_FORMAT_C_MASK      0x30
+#define  OXYGEN_REC_FORMAT_C_SHIFT     4
+#define  OXYGEN_FORMAT_16              0x00
+#define  OXYGEN_FORMAT_24              0x01
+#define  OXYGEN_FORMAT_32              0x02
+
+#define OXYGEN_PLAY_FORMAT             0x4b
+#define  OXYGEN_SPDIF_FORMAT_MASK      0x03
+#define  OXYGEN_SPDIF_FORMAT_SHIFT     0
+#define  OXYGEN_MULTICH_FORMAT_MASK    0x0c
+#define  OXYGEN_MULTICH_FORMAT_SHIFT   2
+/* OXYGEN_FORMAT_* */
+
+#define OXYGEN_REC_CHANNELS            0x4c
+#define  OXYGEN_REC_CHANNELS_MASK      0x07
+#define  OXYGEN_REC_CHANNELS_2_2_2     0x00    /* DMA A, B, C */
+#define  OXYGEN_REC_CHANNELS_4_2_2     0x01
+#define  OXYGEN_REC_CHANNELS_6_0_2     0x02
+#define  OXYGEN_REC_CHANNELS_6_2_0     0x03
+#define  OXYGEN_REC_CHANNELS_8_0_0     0x04
+
+#define OXYGEN_FUNCTION                        0x50
+#define  OXYGEN_FUNCTION_CLOCK_MASK    0x01
+#define  OXYGEN_FUNCTION_CLOCK_PLL     0x00
+#define  OXYGEN_FUNCTION_CLOCK_CRYSTAL 0x01
+#define  OXYGEN_FUNCTION_RESET_CODEC   0x02
+#define  OXYGEN_FUNCTION_RESET_POL     0x04
+#define  OXYGEN_FUNCTION_PWDN          0x08
+#define  OXYGEN_FUNCTION_PWDN_EN       0x10
+#define  OXYGEN_FUNCTION_PWDN_POL      0x20
+#define  OXYGEN_FUNCTION_2WIRE_SPI_MASK        0x40
+#define  OXYGEN_FUNCTION_SPI           0x00
+#define  OXYGEN_FUNCTION_2WIRE         0x40
+#define  OXYGEN_FUNCTION_ENABLE_SPI_4_5        0x80    /* 0 = EEPROM */
+
+#define OXYGEN_I2S_MULTICH_FORMAT      0x60
+#define  OXYGEN_I2S_RATE_MASK          0x0007  /* LRCK */
+#define  OXYGEN_RATE_32000             0x0000
+#define  OXYGEN_RATE_44100             0x0001
+#define  OXYGEN_RATE_48000             0x0002
+#define  OXYGEN_RATE_64000             0x0003
+#define  OXYGEN_RATE_88200             0x0004
+#define  OXYGEN_RATE_96000             0x0005
+#define  OXYGEN_RATE_176400            0x0006
+#define  OXYGEN_RATE_192000            0x0007
+#define  OXYGEN_I2S_FORMAT_MASK                0x0008
+#define  OXYGEN_I2S_FORMAT_I2S         0x0000
+#define  OXYGEN_I2S_FORMAT_LJUST       0x0008
+#define  OXYGEN_I2S_MCLK_MASK          0x0030  /* MCLK/LRCK */
+#define  OXYGEN_I2S_MCLK_128           0x0000
+#define  OXYGEN_I2S_MCLK_256           0x0010
+#define  OXYGEN_I2S_MCLK_512           0x0020
+#define  OXYGEN_I2S_BITS_MASK          0x00c0
+#define  OXYGEN_I2S_BITS_16            0x0000
+#define  OXYGEN_I2S_BITS_20            0x0040
+#define  OXYGEN_I2S_BITS_24            0x0080
+#define  OXYGEN_I2S_BITS_32            0x00c0
+#define  OXYGEN_I2S_MASTER             0x0100
+#define  OXYGEN_I2S_BCLK_MASK          0x0600  /* BCLK/LRCK */
+#define  OXYGEN_I2S_BCLK_64            0x0000
+#define  OXYGEN_I2S_BCLK_128           0x0200
+#define  OXYGEN_I2S_BCLK_256           0x0400
+#define  OXYGEN_I2S_MUTE_MCLK          0x0800
+
+#define OXYGEN_I2S_A_FORMAT            0x62
+#define OXYGEN_I2S_B_FORMAT            0x64
+#define OXYGEN_I2S_C_FORMAT            0x66
+/* like OXYGEN_I2S_MULTICH_FORMAT */
+
+#define OXYGEN_SPDIF_CONTROL           0x70
+#define  OXYGEN_SPDIF_OUT_ENABLE       0x00000002
+#define  OXYGEN_SPDIF_LOOPBACK         0x00000004      /* in to out */
+#define  OXYGEN_SPDIF_SENSE_MASK       0x00000008
+#define  OXYGEN_SPDIF_LOCK_MASK                0x00000010
+#define  OXYGEN_SPDIF_RATE_MASK                0x00000020
+#define  OXYGEN_SPDIF_SPDVALID         0x00000040
+#define  OXYGEN_SPDIF_SENSE_PAR                0x00000200
+#define  OXYGEN_SPDIF_LOCK_PAR         0x00000400
+#define  OXYGEN_SPDIF_SENSE_STATUS     0x00000800
+#define  OXYGEN_SPDIF_LOCK_STATUS      0x00001000
+#define  OXYGEN_SPDIF_SENSE_INT                0x00002000      /* r/wc */
+#define  OXYGEN_SPDIF_LOCK_INT         0x00004000      /* r/wc */
+#define  OXYGEN_SPDIF_RATE_INT         0x00008000      /* r/wc */
+#define  OXYGEN_SPDIF_IN_CLOCK_MASK    0x00010000
+#define  OXYGEN_SPDIF_IN_CLOCK_96      0x00000000      /* <= 96 kHz */
+#define  OXYGEN_SPDIF_IN_CLOCK_192     0x00010000      /* > 96 kHz */
+#define  OXYGEN_SPDIF_OUT_RATE_MASK    0x07000000
+#define  OXYGEN_SPDIF_OUT_RATE_SHIFT   24
+/* OXYGEN_RATE_* << OXYGEN_SPDIF_OUT_RATE_SHIFT */
+
+#define OXYGEN_SPDIF_OUTPUT_BITS       0x74
+#define  OXYGEN_SPDIF_NONAUDIO         0x00000002
+#define  OXYGEN_SPDIF_C                        0x00000004
+#define  OXYGEN_SPDIF_PREEMPHASIS      0x00000008
+#define  OXYGEN_SPDIF_CATEGORY_MASK    0x000007f0
+#define  OXYGEN_SPDIF_CATEGORY_SHIFT   4
+#define  OXYGEN_SPDIF_ORIGINAL         0x00000800
+#define  OXYGEN_SPDIF_CS_RATE_MASK     0x0000f000
+#define  OXYGEN_SPDIF_CS_RATE_SHIFT    12
+#define  OXYGEN_SPDIF_V                        0x00010000      /* 0 = valid */
+
+#define OXYGEN_SPDIF_INPUT_BITS                0x78
+/* 32 bits, IEC958_AES_* */
+
+#define OXYGEN_EEPROM_CONTROL          0x80
+#define  OXYGEN_EEPROM_ADDRESS_MASK    0x7f
+#define  OXYGEN_EEPROM_DIR_MASK                0x80
+#define  OXYGEN_EEPROM_DIR_READ                0x00
+#define  OXYGEN_EEPROM_DIR_WRITE       0x80
+
+#define OXYGEN_EEPROM_STATUS           0x81
+#define  OXYGEN_EEPROM_VALID           0x40
+#define  OXYGEN_EEPROM_BUSY            0x80
+
+#define OXYGEN_EEPROM_DATA             0x82    /* 16 bits */
+
+#define OXYGEN_2WIRE_CONTROL           0x90
+#define  OXYGEN_2WIRE_DIR_MASK         0x01
+#define  OXYGEN_2WIRE_DIR_WRITE                0x00
+#define  OXYGEN_2WIRE_DIR_READ         0x01
+#define  OXYGEN_2WIRE_ADDRESS_MASK     0xfe    /* slave device address */
+#define  OXYGEN_2WIRE_ADDRESS_SHIFT    1
+
+#define OXYGEN_2WIRE_MAP               0x91    /* address, 8 bits */
+#define OXYGEN_2WIRE_DATA              0x92    /* data, 16 bits */
+
+#define OXYGEN_2WIRE_BUS_STATUS                0x94
+#define  OXYGEN_2WIRE_BUSY             0x0001
+#define  OXYGEN_2WIRE_LENGTH_MASK      0x0002
+#define  OXYGEN_2WIRE_LENGTH_8         0x0000
+#define  OXYGEN_2WIRE_LENGTH_16                0x0002
+#define  OXYGEN_2WIRE_MANUAL_READ      0x0004  /* 0 = auto read */
+#define  OXYGEN_2WIRE_WRITE_MAP_ONLY   0x0008
+#define  OXYGEN_2WIRE_SLAVE_AD_MASK    0x0030  /* AD0, AD1 */
+#define  OXYGEN_2WIRE_INTERRUPT_MASK   0x0040  /* 0 = int. if not responding */
+#define  OXYGEN_2WIRE_SLAVE_NO_RESPONSE        0x0080
+#define  OXYGEN_2WIRE_SPEED_MASK       0x0100
+#define  OXYGEN_2WIRE_SPEED_STANDARD   0x0000
+#define  OXYGEN_2WIRE_SPEED_FAST       0x0100
+#define  OXYGEN_2WIRE_CLOCK_SYNC       0x0200
+#define  OXYGEN_2WIRE_BUS_RESET                0x0400
+
+#define OXYGEN_SPI_CONTROL             0x98
+#define  OXYGEN_SPI_BUSY               0x01    /* read */
+#define  OXYGEN_SPI_TRIGGER            0x01    /* write */
+#define  OXYGEN_SPI_DATA_LENGTH_MASK   0x02
+#define  OXYGEN_SPI_DATA_LENGTH_2      0x00
+#define  OXYGEN_SPI_DATA_LENGTH_3      0x02
+#define  OXYGEN_SPI_CLOCK_MASK         0xc0
+#define  OXYGEN_SPI_CLOCK_160          0x00    /* ns */
+#define  OXYGEN_SPI_CLOCK_320          0x40
+#define  OXYGEN_SPI_CLOCK_640          0x80
+#define  OXYGEN_SPI_CLOCK_1280         0xc0
+#define  OXYGEN_SPI_CODEC_MASK         0x70    /* 0..5 */
+#define  OXYGEN_SPI_CODEC_SHIFT                4
+#define  OXYGEN_SPI_CEN_MASK           0x80
+#define  OXYGEN_SPI_CEN_LATCH_CLOCK_LO 0x00
+#define  OXYGEN_SPI_CEN_LATCH_CLOCK_HI 0x80
+
+#define OXYGEN_SPI_DATA1               0x99
+#define OXYGEN_SPI_DATA2               0x9a
+#define OXYGEN_SPI_DATA3               0x9b
+
+#define OXYGEN_MPU401                  0xa0
+
+#define OXYGEN_MPU401_CONTROL          0xa2
+#define  OXYGEN_MPU401_LOOPBACK                0x01    /* TXD to RXD */
+
+#define OXYGEN_GPI_DATA                        0xa4
+/* bits 0..5 = pin XGPI0..XGPI5 */
+
+#define OXYGEN_GPI_INTERRUPT_MASK      0xa5
+/* bits 0..5, 1 = enable */
+
+#define OXYGEN_GPIO_DATA               0xa6
+/* bits 0..9 */
+
+#define OXYGEN_GPIO_CONTROL            0xa8
+/* bits 0..9, 0 = input, 1 = output */
+#define  OXYGEN_GPIO1_XSLAVE_RDY       0x8000
+
+#define OXYGEN_GPIO_INTERRUPT_MASK     0xaa
+/* bits 0..9, 1 = enable */
+
+#define OXYGEN_DEVICE_SENSE            0xac
+#define  OXYGEN_HEAD_PHONE_DETECT      0x01
+#define  OXYGEN_HEAD_PHONE_MASK                0x06
+#define  OXYGEN_HEAD_PHONE_PASSIVE_SPK 0x00
+#define  OXYGEN_HEAD_PHONE_HP          0x02
+#define  OXYGEN_HEAD_PHONE_ACTIVE_SPK  0x04
+
+#define OXYGEN_MCU_2WIRE_DATA          0xb0
+
+#define OXYGEN_MCU_2WIRE_MAP           0xb2
+
+#define OXYGEN_MCU_2WIRE_STATUS                0xb3
+#define  OXYGEN_MCU_2WIRE_BUSY         0x01
+#define  OXYGEN_MCU_2WIRE_LENGTH_MASK  0x06
+#define  OXYGEN_MCU_2WIRE_LENGTH_1     0x00
+#define  OXYGEN_MCU_2WIRE_LENGTH_2     0x02
+#define  OXYGEN_MCU_2WIRE_LENGTH_3     0x04
+#define  OXYGEN_MCU_2WIRE_WRITE                0x08    /* r/wc */
+#define  OXYGEN_MCU_2WIRE_READ         0x10    /* r/wc */
+#define  OXYGEN_MCU_2WIRE_DRV_XACT_FAIL        0x20    /* r/wc */
+#define  OXYGEN_MCU_2WIRE_RESET                0x40
+
+#define OXYGEN_MCU_2WIRE_CONTROL       0xb4
+#define  OXYGEN_MCU_2WIRE_DRV_ACK      0x01
+#define  OXYGEN_MCU_2WIRE_DRV_XACT     0x02
+#define  OXYGEN_MCU_2WIRE_INT_MASK     0x04
+#define  OXYGEN_MCU_2WIRE_SYNC_MASK    0x08
+#define  OXYGEN_MCU_2WIRE_SYNC_RDY_PIN 0x00
+#define  OXYGEN_MCU_2WIRE_SYNC_DATA    0x08
+#define  OXYGEN_MCU_2WIRE_ADDRESS_MASK 0x30
+#define  OXYGEN_MCU_2WIRE_ADDRESS_10   0x00
+#define  OXYGEN_MCU_2WIRE_ADDRESS_12   0x10
+#define  OXYGEN_MCU_2WIRE_ADDRESS_14   0x20
+#define  OXYGEN_MCU_2WIRE_ADDRESS_16   0x30
+#define  OXYGEN_MCU_2WIRE_INT_POL      0x40
+#define  OXYGEN_MCU_2WIRE_SYNC_ENABLE  0x80
+
+#define OXYGEN_PLAY_ROUTING            0xc0
+#define  OXYGEN_PLAY_MUTE01            0x0001
+#define  OXYGEN_PLAY_MUTE23            0x0002
+#define  OXYGEN_PLAY_MUTE45            0x0004
+#define  OXYGEN_PLAY_MUTE67            0x0008
+#define  OXYGEN_PLAY_MULTICH_MASK      0x0010
+#define  OXYGEN_PLAY_MULTICH_I2S_DAC   0x0000
+#define  OXYGEN_PLAY_MULTICH_AC97      0x0010
+#define  OXYGEN_PLAY_SPDIF_MASK                0x00e0
+#define  OXYGEN_PLAY_SPDIF_SPDIF       0x0000
+#define  OXYGEN_PLAY_SPDIF_MULTICH_01  0x0020
+#define  OXYGEN_PLAY_SPDIF_MULTICH_23  0x0040
+#define  OXYGEN_PLAY_SPDIF_MULTICH_45  0x0060
+#define  OXYGEN_PLAY_SPDIF_MULTICH_67  0x0080
+#define  OXYGEN_PLAY_SPDIF_REC_A       0x00a0
+#define  OXYGEN_PLAY_SPDIF_REC_B       0x00c0
+#define  OXYGEN_PLAY_SPDIF_I2S_ADC_3   0x00e0
+#define  OXYGEN_PLAY_DAC0_SOURCE_MASK  0x0300
+#define  OXYGEN_PLAY_DAC0_SOURCE_SHIFT 8
+#define  OXYGEN_PLAY_DAC1_SOURCE_MASK  0x0c00
+#define  OXYGEN_PLAY_DAC1_SOURCE_SHIFT 10
+#define  OXYGEN_PLAY_DAC2_SOURCE_MASK  0x3000
+#define  OXYGEN_PLAY_DAC2_SOURCE_SHIFT 12
+#define  OXYGEN_PLAY_DAC3_SOURCE_MASK  0xc000
+#define  OXYGEN_PLAY_DAC3_SOURCE_SHIFT 14
+
+#define OXYGEN_REC_ROUTING             0xc2
+#define  OXYGEN_MUTE_I2S_ADC_1         0x01
+#define  OXYGEN_MUTE_I2S_ADC_2         0x02
+#define  OXYGEN_MUTE_I2S_ADC_3         0x04
+#define  OXYGEN_REC_A_ROUTE_MASK       0x08
+#define  OXYGEN_REC_A_ROUTE_I2S_ADC_1  0x00
+#define  OXYGEN_REC_A_ROUTE_AC97_0     0x08
+#define  OXYGEN_REC_B_ROUTE_MASK       0x10
+#define  OXYGEN_REC_B_ROUTE_I2S_ADC_2  0x00
+#define  OXYGEN_REC_B_ROUTE_AC97_1     0x10
+#define  OXYGEN_REC_C_ROUTE_MASK       0x20
+#define  OXYGEN_REC_C_ROUTE_SPDIF      0x00
+#define  OXYGEN_REC_C_ROUTE_I2S_ADC_3  0x20
+
+#define OXYGEN_ADC_MONITOR             0xc3
+#define  OXYGEN_ADC_MONITOR_A          0x01
+#define  OXYGEN_ADC_MONITOR_A_HALF_VOL 0x02
+#define  OXYGEN_ADC_MONITOR_B          0x04
+#define  OXYGEN_ADC_MONITOR_B_HALF_VOL 0x08
+#define  OXYGEN_ADC_MONITOR_C          0x10
+#define  OXYGEN_ADC_MONITOR_C_HALF_VOL 0x20
+
+#define OXYGEN_A_MONITOR_ROUTING       0xc4
+#define  OXYGEN_A_MONITOR_ROUTE_0_MASK 0x03
+#define  OXYGEN_A_MONITOR_ROUTE_0_SHIFT        0
+#define  OXYGEN_A_MONITOR_ROUTE_1_MASK 0x0c
+#define  OXYGEN_A_MONITOR_ROUTE_1_SHIFT        2
+#define  OXYGEN_A_MONITOR_ROUTE_2_MASK 0x30
+#define  OXYGEN_A_MONITOR_ROUTE_2_SHIFT        4
+#define  OXYGEN_A_MONITOR_ROUTE_3_MASK 0xc0
+#define  OXYGEN_A_MONITOR_ROUTE_3_SHIFT        6
+
+#define OXYGEN_AC97_CONTROL            0xd0
+#define  OXYGEN_AC97_COLD_RESET                0x0001
+#define  OXYGEN_AC97_SUSPENDED         0x0002  /* read */
+#define  OXYGEN_AC97_RESUME            0x0002  /* write */
+#define  OXYGEN_AC97_CLOCK_DISABLE     0x0004
+#define  OXYGEN_AC97_NO_CODEC_0                0x0008
+#define  OXYGEN_AC97_CODEC_0           0x0010
+#define  OXYGEN_AC97_CODEC_1           0x0020
+
+#define OXYGEN_AC97_INTERRUPT_MASK     0xd2
+#define  OXYGEN_AC97_INT_READ_DONE     0x01
+#define  OXYGEN_AC97_INT_WRITE_DONE    0x02
+#define  OXYGEN_AC97_INT_CODEC_0       0x10
+#define  OXYGEN_AC97_INT_CODEC_1       0x20
+
+#define OXYGEN_AC97_INTERRUPT_STATUS   0xd3
+/* OXYGEN_AC97_INT_* */
+
+#define OXYGEN_AC97_OUT_CONFIG         0xd4
+#define  OXYGEN_AC97_CODEC1_SLOT3      0x00000001
+#define  OXYGEN_AC97_CODEC1_SLOT3_VSR  0x00000002
+#define  OXYGEN_AC97_CODEC1_SLOT4      0x00000010
+#define  OXYGEN_AC97_CODEC1_SLOT4_VSR  0x00000020
+#define  OXYGEN_AC97_CODEC0_FRONTL     0x00000100
+#define  OXYGEN_AC97_CODEC0_FRONTR     0x00000200
+#define  OXYGEN_AC97_CODEC0_SIDEL      0x00000400
+#define  OXYGEN_AC97_CODEC0_SIDER      0x00000800
+#define  OXYGEN_AC97_CODEC0_CENTER     0x00001000
+#define  OXYGEN_AC97_CODEC0_BASE       0x00002000
+#define  OXYGEN_AC97_CODEC0_REARL      0x00004000
+#define  OXYGEN_AC97_CODEC0_REARR      0x00008000
+
+#define OXYGEN_AC97_IN_CONFIG          0xd8
+#define  OXYGEN_AC97_CODEC1_LINEL      0x00000001
+#define  OXYGEN_AC97_CODEC1_LINEL_VSR  0x00000002
+#define  OXYGEN_AC97_CODEC1_LINEL_16   0x00000000
+#define  OXYGEN_AC97_CODEC1_LINEL_18   0x00000004
+#define  OXYGEN_AC97_CODEC1_LINEL_20   0x00000008
+#define  OXYGEN_AC97_CODEC1_LINER      0x00000010
+#define  OXYGEN_AC97_CODEC1_LINER_VSR  0x00000020
+#define  OXYGEN_AC97_CODEC1_LINER_16   0x00000000
+#define  OXYGEN_AC97_CODEC1_LINER_18   0x00000040
+#define  OXYGEN_AC97_CODEC1_LINER_20   0x00000080
+#define  OXYGEN_AC97_CODEC0_LINEL      0x00000100
+#define  OXYGEN_AC97_CODEC0_LINER      0x00000200
+
+#define OXYGEN_AC97_REGS               0xdc
+#define  OXYGEN_AC97_REG_DATA_MASK     0x0000ffff
+#define  OXYGEN_AC97_REG_ADDR_MASK     0x007f0000
+#define  OXYGEN_AC97_REG_ADDR_SHIFT    16
+#define  OXYGEN_AC97_REG_DIR_MASK      0x00800000
+#define  OXYGEN_AC97_REG_DIR_WRITE     0x00000000
+#define  OXYGEN_AC97_REG_DIR_READ      0x00800000
+#define  OXYGEN_AC97_REG_CODEC_MASK    0x01000000
+#define  OXYGEN_AC97_REG_CODEC_SHIFT   24
+
+#define OXYGEN_TEST                    0xe0
+#define  OXYGEN_TEST_RAM_SUCCEEDED     0x01
+#define  OXYGEN_TEST_PLAYBACK_RAM      0x02
+#define  OXYGEN_TEST_RECORD_RAM                0x04
+#define  OXYGEN_TEST_PLL               0x08
+#define  OXYGEN_TEST_2WIRE_LOOPBACK    0x10
+
+#define OXYGEN_DMA_FLUSH               0xe1
+/* OXYGEN_CHANNEL_* */
+
+#define OXYGEN_CODEC_VERSION           0xe4
+#define  OXYGEN_XCID_MASK              0x07
+
+#define OXYGEN_REVISION                        0xe6
+#define  OXYGEN_REVISION_XPKGID_MASK   0x0007
+#define  OXYGEN_REVISION_MASK          0xfff8
+#define  OXYGEN_REVISION_2             0x0008  /* bit flag */
+#define  OXYGEN_REVISION_8787          0x0014  /* 8 bits */
+
+#define OXYGEN_OFFSIN_48K              0xe8
+#define OXYGEN_OFFSBASE_48K            0xe9
+#define  OXYGEN_OFFSBASE_MASK          0x0fff
+#define OXYGEN_OFFSIN_44K              0xec
+#define OXYGEN_OFFSBASE_44K            0xed
+
+#endif
diff --git a/sound/pci/oxygen/virtuoso.c b/sound/pci/oxygen/virtuoso.c
new file mode 100644 (file)
index 0000000..40e92f5
--- /dev/null
@@ -0,0 +1,449 @@
+/*
+ * C-Media CMI8788 driver 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, write to the Free Software
+ *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA
+ */
+
+/*
+ * 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:
+ *
+ * GPIO 0 -> enable AC'97 bypass (line in -> ADC)
+ */
+
+#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/initval.h>
+#include <sound/pcm.h>
+#include <sound/tlv.h>
+#include "oxygen.h"
+#include "cm9780.h"
+
+MODULE_AUTHOR("Clemens Ladisch <clemens@ladisch.de>");
+MODULE_DESCRIPTION("Asus AV200 driver");
+MODULE_LICENSE("GPL");
+MODULE_SUPPORTED_DEVICE("{{Asus,AV200}}");
+
+static int index[SNDRV_CARDS] = SNDRV_DEFAULT_IDX;
+static char *id[SNDRV_CARDS] = SNDRV_DEFAULT_STR;
+static int enable[SNDRV_CARDS] = SNDRV_DEFAULT_ENABLE_PNP;
+
+module_param_array(index, int, NULL, 0444);
+MODULE_PARM_DESC(index, "card index");
+module_param_array(id, charp, NULL, 0444);
+MODULE_PARM_DESC(id, "ID string");
+module_param_array(enable, bool, NULL, 0444);
+MODULE_PARM_DESC(enable, "enable card");
+
+static struct pci_device_id xonar_ids[] __devinitdata = {
+       { OXYGEN_PCI_SUBID(0x1043, 0x8269) }, /* Asus Xonar D2 */
+       { OXYGEN_PCI_SUBID(0x1043, 0x82b7) }, /* Asus Xonar D2X */
+       { }
+};
+MODULE_DEVICE_TABLE(pci, xonar_ids);
+
+
+#define GPIO_CS5381_M_MASK     0x000c
+#define GPIO_CS5381_M_SINGLE   0x0000
+#define GPIO_CS5381_M_DOUBLE   0x0004
+#define GPIO_CS5381_M_QUAD     0x0008
+#define GPIO_EXT_POWER         0x0020
+#define GPIO_ALT               0x0080
+#define GPIO_OUTPUT_ENABLE     0x0100
+
+#define GPIO_LINE_MUTE         CM9780_GPO0
+
+/* register 16 */
+#define PCM1796_ATL_MASK       0xff
+/* register 17 */
+#define PCM1796_ATR_MASK       0xff
+/* register 18 */
+#define PCM1796_MUTE           0x01
+#define PCM1796_DME            0x02
+#define PCM1796_DMF_MASK       0x0c
+#define PCM1796_DMF_DISABLED   0x00
+#define PCM1796_DMF_48         0x04
+#define PCM1796_DMF_441                0x08
+#define PCM1796_DMF_32         0x0c
+#define PCM1796_FMT_MASK       0x70
+#define PCM1796_FMT_16_RJUST   0x00
+#define PCM1796_FMT_20_RJUST   0x10
+#define PCM1796_FMT_24_RJUST   0x20
+#define PCM1796_FMT_24_LJUST   0x30
+#define PCM1796_FMT_16_I2S     0x40
+#define PCM1796_FMT_24_I2S     0x50
+#define PCM1796_ATLD           0x80
+/* register 19 */
+#define PCM1796_INZD           0x01
+#define PCM1796_FLT_MASK       0x02
+#define PCM1796_FLT_SHARP      0x00
+#define PCM1796_FLT_SLOW       0x02
+#define PCM1796_DFMS           0x04
+#define PCM1796_OPE            0x10
+#define PCM1796_ATS_MASK       0x60
+#define PCM1796_ATS_1          0x00
+#define PCM1796_ATS_2          0x20
+#define PCM1796_ATS_4          0x40
+#define PCM1796_ATS_8          0x60
+#define PCM1796_REV            0x80
+/* register 20 */
+#define PCM1796_OS_MASK                0x03
+#define PCM1796_OS_64          0x00
+#define PCM1796_OS_32          0x01
+#define PCM1796_OS_128         0x02
+#define PCM1796_CHSL_MASK      0x04
+#define PCM1796_CHSL_LEFT      0x00
+#define PCM1796_CHSL_RIGHT     0x04
+#define PCM1796_MONO           0x08
+#define PCM1796_DFTH           0x10
+#define PCM1796_DSD            0x20
+#define PCM1796_SRST           0x40
+/* register 21 */
+#define PCM1796_PCMZ           0x01
+#define PCM1796_DZ_MASK                0x06
+/* register 22 */
+#define PCM1796_ZFGL           0x01
+#define PCM1796_ZFGR           0x02
+/* register 23 */
+#define PCM1796_ID_MASK                0x1f
+
+struct xonar_data {
+       u8 is_d2x;
+       u8 has_power;
+};
+
+static void pcm1796_write(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 void xonar_init(struct oxygen *chip)
+{
+       struct xonar_data *data = chip->model_data;
+       unsigned int i;
+
+       data->is_d2x = chip->pci->subsystem_device == 0x82b7;
+
+       for (i = 0; i < 4; ++i) {
+               pcm1796_write(chip, i, 18, PCM1796_FMT_24_LJUST | PCM1796_ATLD);
+               pcm1796_write(chip, i, 19, PCM1796_FLT_SHARP | PCM1796_ATS_1);
+               pcm1796_write(chip, i, 20, PCM1796_OS_64);
+               pcm1796_write(chip, i, 21, 0);
+               pcm1796_write(chip, i, 16, 0xff); /* set ATL/ATR after ATLD */
+               pcm1796_write(chip, i, 17, 0xff);
+       }
+
+       oxygen_set_bits16(chip, OXYGEN_GPIO_CONTROL,
+                         GPIO_CS5381_M_MASK | GPIO_ALT);
+       oxygen_write16_masked(chip, OXYGEN_GPIO_DATA,
+                             GPIO_CS5381_M_SINGLE,
+                             GPIO_CS5381_M_MASK | GPIO_ALT);
+       if (data->is_d2x) {
+               oxygen_clear_bits16(chip, OXYGEN_GPIO_CONTROL,
+                                   GPIO_EXT_POWER);
+               oxygen_set_bits16(chip, OXYGEN_GPIO_INTERRUPT_MASK,
+                                 GPIO_EXT_POWER);
+               chip->interrupt_mask |= OXYGEN_INT_GPIO;
+               data->has_power = !!(oxygen_read16(chip, OXYGEN_GPIO_DATA)
+                                    & GPIO_EXT_POWER);
+       }
+       oxygen_ac97_set_bits(chip, 0, CM9780_JACK, CM9780_FMIC2MIC);
+       oxygen_ac97_clear_bits(chip, 0, CM9780_GPIO_STATUS, GPIO_LINE_MUTE);
+       msleep(300);
+       oxygen_set_bits16(chip, OXYGEN_GPIO_CONTROL, GPIO_OUTPUT_ENABLE);
+       oxygen_set_bits16(chip, OXYGEN_GPIO_DATA, GPIO_OUTPUT_ENABLE);
+
+       snd_component_add(chip->card, "PCM1796");
+       snd_component_add(chip->card, "CS5381");
+}
+
+static void xonar_cleanup(struct oxygen *chip)
+{
+       oxygen_clear_bits16(chip, OXYGEN_GPIO_DATA, GPIO_OUTPUT_ENABLE);
+}
+
+static void set_pcm1796_params(struct oxygen *chip,
+                              struct snd_pcm_hw_params *params)
+{
+#if 0
+       unsigned int i;
+       u8 value;
+
+       value = params_rate(params) >= 96000 ? PCM1796_OS_32 : PCM1796_OS_64;
+       for (i = 0; i < 4; ++i)
+               pcm1796_write(chip, i, 20, value);
+#endif
+}
+
+static void update_pcm1796_volume(struct oxygen *chip)
+{
+       unsigned int i;
+
+       for (i = 0; i < 4; ++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)
+{
+       unsigned int i;
+       u8 value;
+
+       value = PCM1796_FMT_24_LJUST | PCM1796_ATLD;
+       if (chip->dac_mute)
+               value |= PCM1796_MUTE;
+       for (i = 0; i < 4; ++i)
+               pcm1796_write(chip, i, 18, value);
+}
+
+static void set_cs5381_params(struct oxygen *chip,
+                             struct snd_pcm_hw_params *params)
+{
+       unsigned int value;
+
+       if (params_rate(params) <= 54000)
+               value = GPIO_CS5381_M_SINGLE;
+       else if (params_rate(params) <= 108000)
+               value = GPIO_CS5381_M_DOUBLE;
+       else
+               value = GPIO_CS5381_M_QUAD;
+       oxygen_write16_masked(chip, OXYGEN_GPIO_DATA,
+                             value, GPIO_CS5381_M_MASK);
+}
+
+static void xonar_gpio_changed(struct oxygen *chip)
+{
+       struct xonar_data *data = chip->model_data;
+       u8 has_power;
+
+       if (!data->is_d2x)
+               return;
+       has_power = !!(oxygen_read16(chip, OXYGEN_GPIO_DATA)
+                      & GPIO_EXT_POWER);
+       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 mute_ac97_ctl(struct oxygen *chip, unsigned int control)
+{
+       unsigned int index = chip->controls[control]->private_value & 0xff;
+       u16 value;
+
+       value = oxygen_read_ac97(chip, 0, index);
+       if (!(value & 0x8000)) {
+               oxygen_write_ac97(chip, 0, index, value | 0x8000);
+               snd_ctl_notify(chip->card, SNDRV_CTL_EVENT_MASK_VALUE,
+                              &chip->controls[control]->id);
+       }
+}
+
+static void xonar_ac97_switch_hook(struct oxygen *chip, unsigned int codec,
+                                  unsigned int reg, int mute)
+{
+       if (codec != 0)
+               return;
+       /* line-in is exclusive */
+       switch (reg) {
+       case AC97_LINE:
+               oxygen_write_ac97_masked(chip, 0, CM9780_GPIO_STATUS,
+                                        mute ? GPIO_LINE_MUTE : 0,
+                                        GPIO_LINE_MUTE);
+               if (!mute) {
+                       mute_ac97_ctl(chip, CONTROL_MIC_CAPTURE_SWITCH);
+                       mute_ac97_ctl(chip, CONTROL_CD_CAPTURE_SWITCH);
+                       mute_ac97_ctl(chip, CONTROL_AUX_CAPTURE_SWITCH);
+               }
+               break;
+       case AC97_MIC:
+       case AC97_CD:
+       case AC97_VIDEO:
+       case AC97_AUX:
+               if (!mute) {
+                       oxygen_ac97_set_bits(chip, 0, CM9780_GPIO_STATUS,
+                                            GPIO_LINE_MUTE);
+                       mute_ac97_ctl(chip, CONTROL_LINE_CAPTURE_SWITCH);
+               }
+               break;
+       }
+}
+
+static int pcm1796_volume_info(struct snd_kcontrol *ctl,
+                              struct snd_ctl_elem_info *info)
+{
+       info->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
+       info->count = 8;
+       info->value.integer.min = 0x0f;
+       info->value.integer.max = 0xff;
+       return 0;
+}
+
+static int alt_switch_get(struct snd_kcontrol *ctl,
+                         struct snd_ctl_elem_value *value)
+{
+       struct oxygen *chip = ctl->private_data;
+
+       value->value.integer.value[0] =
+               !!(oxygen_read16(chip, OXYGEN_GPIO_DATA) & GPIO_ALT);
+       return 0;
+}
+
+static int alt_switch_put(struct snd_kcontrol *ctl,
+                         struct snd_ctl_elem_value *value)
+{
+       struct oxygen *chip = ctl->private_data;
+       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 | GPIO_ALT;
+       else
+               new_bits = old_bits & ~GPIO_ALT;
+       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 = alt_switch_get,
+       .put = alt_switch_put,
+};
+
+static const DECLARE_TLV_DB_SCALE(pcm1796_db_scale, -12000, 50, 0);
+
+static int xonar_control_filter(struct snd_kcontrol_new *template)
+{
+       if (!strcmp(template->name, "Master Playback Volume")) {
+               template->access |= SNDRV_CTL_ELEM_ACCESS_TLV_READ;
+               template->info = pcm1796_volume_info,
+               template->tlv.p = pcm1796_db_scale;
+       } else if (!strncmp(template->name, "CD Capture ", 11)) {
+               /* CD in is actually connected to the video in pin */
+               template->private_value ^= AC97_CD ^ AC97_VIDEO;
+       } else if (!strcmp(template->name, "Line Capture Volume")) {
+               return 1; /* line-in bypasses the AC'97 mixer */
+       }
+       return 0;
+}
+
+static int xonar_mixer_init(struct oxygen *chip)
+{
+       return snd_ctl_add(chip->card, snd_ctl_new1(&alt_switch, chip));
+}
+
+static const struct oxygen_model model_xonar = {
+       .shortname = "Asus AV200",
+       .longname = "Asus Virtuoso 200",
+       .chip = "AV200",
+       .init = xonar_init,
+       .control_filter = xonar_control_filter,
+       .mixer_init = xonar_mixer_init,
+       .cleanup = xonar_cleanup,
+       .set_dac_params = set_pcm1796_params,
+       .set_adc_params = set_cs5381_params,
+       .update_dac_volume = update_pcm1796_volume,
+       .update_dac_mute = update_pcm1796_mute,
+       .ac97_switch_hook = xonar_ac97_switch_hook,
+       .gpio_changed = xonar_gpio_changed,
+       .model_data_size = sizeof(struct xonar_data),
+       .dac_channels = 8,
+       .used_channels = OXYGEN_CHANNEL_B |
+                        OXYGEN_CHANNEL_C |
+                        OXYGEN_CHANNEL_SPDIF |
+                        OXYGEN_CHANNEL_MULTICH,
+       .function_flags = OXYGEN_FUNCTION_ENABLE_SPI_4_5,
+       .dac_i2s_format = OXYGEN_I2S_FORMAT_LJUST,
+       .adc_i2s_format = OXYGEN_I2S_FORMAT_LJUST,
+};
+
+static int __devinit xonar_probe(struct pci_dev *pci,
+                                const struct pci_device_id *pci_id)
+{
+       static int dev;
+       int err;
+
+       if (dev >= SNDRV_CARDS)
+               return -ENODEV;
+       if (!enable[dev]) {
+               ++dev;
+               return -ENOENT;
+       }
+       err = oxygen_pci_probe(pci, index[dev], id[dev], 1, &model_xonar);
+       if (err >= 0)
+               ++dev;
+       return err;
+}
+
+static struct pci_driver xonar_driver = {
+       .name = "AV200",
+       .id_table = xonar_ids,
+       .probe = xonar_probe,
+       .remove = __devexit_p(oxygen_pci_remove),
+};
+
+static int __init alsa_card_xonar_init(void)
+{
+       return pci_register_driver(&xonar_driver);
+}
+
+static void __exit alsa_card_xonar_exit(void)
+{
+       pci_unregister_driver(&xonar_driver);
+}
+
+module_init(alsa_card_xonar_init)
+module_exit(alsa_card_xonar_exit)
index 2d618bd7e62b70f68719d6216813c8b54e1bf7dd..9d5bb76229a8fc020eb0a9a869ed190b8e17ce87 100644 (file)
@@ -21,7 +21,6 @@
  */
 
 
-#include <sound/driver.h>
 #include <linux/init.h>
 #include <linux/interrupt.h>
 #include <linux/slab.h>
index 0ff8dc36fde3244d947a6ca8aed9d558942ae69b..c4e415d07380db810db6b3bd27a59ff89aeb76a3 100644 (file)
@@ -20,7 +20,6 @@
  *   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA
  */
 
-#include <sound/driver.h>
 #include <linux/delay.h>
 #include <linux/firmware.h>
 #include <linux/interrupt.h>
index d55d8bc90eee96f0302f37e9cda6801010ccd5ae..e6a4bfbb91bb664ed933c3a8c95e286b5876ac83 100644 (file)
@@ -20,7 +20,6 @@
  *   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA
  */
 
-#include <sound/driver.h>
 #include <linux/interrupt.h>
 #include <linux/vmalloc.h>
 #include <linux/firmware.h>
index 5f8d42633b040b92a88b173df5ad4222d2bdcd43..aabc7bc5321e89d7c60bbdaaf0624599014aaa73 100644 (file)
@@ -21,7 +21,6 @@
  *   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA
  */
 
-#include <sound/driver.h>
 #include <linux/time.h>
 #include <linux/interrupt.h>
 #include <linux/init.h>
@@ -120,8 +119,18 @@ static int pcxhr_analog_vol_put(struct snd_kcontrol *kcontrol,
        is_capture = (kcontrol->private_value != 0);
        for (i = 0; i < 2; i++) {
                int  new_volume = ucontrol->value.integer.value[i];
-               int* stored_volume = is_capture ? &chip->analog_capture_volume[i] :
+               int *stored_volume = is_capture ?
+                       &chip->analog_capture_volume[i] :
                        &chip->analog_playback_volume[i];
+               if (is_capture) {
+                       if (new_volume < PCXHR_ANALOG_CAPTURE_LEVEL_MIN ||
+                           new_volume > PCXHR_ANALOG_CAPTURE_LEVEL_MAX)
+                               continue;
+               } else {
+                       if (new_volume < PCXHR_ANALOG_PLAYBACK_LEVEL_MIN ||
+                           new_volume > PCXHR_ANALOG_PLAYBACK_LEVEL_MAX)
+                               continue;
+               }
                if (*stored_volume != new_volume) {
                        *stored_volume = new_volume;
                        changed = 1;
@@ -165,10 +174,13 @@ static int pcxhr_audio_sw_put(struct snd_kcontrol *kcontrol,
        int i, changed = 0;
        mutex_lock(&chip->mgr->mixer_mutex);
        for(i = 0; i < 2; i++) {
-               if (chip->analog_playback_active[i] != ucontrol->value.integer.value[i]) {
-                       chip->analog_playback_active[i] = ucontrol->value.integer.value[i];
+               if (chip->analog_playback_active[i] !=
+                   ucontrol->value.integer.value[i]) {
+                       chip->analog_playback_active[i] =
+                               !!ucontrol->value.integer.value[i];
                        changed = 1;
-                       pcxhr_update_analog_audio_level(chip, 0, i);    /* update playback levels */
+                       /* update playback levels */
+                       pcxhr_update_analog_audio_level(chip, 0, i);
                }
        }
        mutex_unlock(&chip->mgr->mixer_mutex);
@@ -323,20 +335,24 @@ static int pcxhr_pcm_vol_put(struct snd_kcontrol *kcontrol,
        int i;
 
        mutex_lock(&chip->mgr->mixer_mutex);
-       if (is_capture)
-               stored_volume = chip->digital_capture_volume;           /* digital capture */
-       else
-               stored_volume = chip->digital_playback_volume[idx];     /* digital playback */
+       if (is_capture)         /* digital capture */
+               stored_volume = chip->digital_capture_volume;
+       else                    /* digital playback */
+               stored_volume = chip->digital_playback_volume[idx];
        for (i = 0; i < 2; i++) {
-               if (stored_volume[i] != ucontrol->value.integer.value[i]) {
-                       stored_volume[i] = ucontrol->value.integer.value[i];
+               int vol = ucontrol->value.integer.value[i];
+               if (vol < PCXHR_DIGITAL_LEVEL_MIN ||
+                   vol > PCXHR_DIGITAL_LEVEL_MAX)
+                       continue;
+               if (stored_volume[i] != vol) {
+                       stored_volume[i] = vol;
                        changed = 1;
                        if (is_capture) /* update capture volume */
                                pcxhr_update_audio_pipe_level(chip, 1, i);
                }
        }
-       if (! is_capture && changed)
-               pcxhr_update_playback_stream_level(chip, idx);  /* update playback volume */
+       if (!is_capture && changed)     /* update playback volume */
+               pcxhr_update_playback_stream_level(chip, idx);
        mutex_unlock(&chip->mgr->mixer_mutex);
        return changed;
 }
@@ -378,8 +394,10 @@ static int pcxhr_pcm_sw_put(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_v
        mutex_lock(&chip->mgr->mixer_mutex);
        j = idx;
        for (i = 0; i < 2; i++) {
-               if (chip->digital_playback_active[j][i] != ucontrol->value.integer.value[i]) {
-                       chip->digital_playback_active[j][i] = ucontrol->value.integer.value[i];
+               if (chip->digital_playback_active[j][i] !=
+                   ucontrol->value.integer.value[i]) {
+                       chip->digital_playback_active[j][i] =
+                               !!ucontrol->value.integer.value[i];
                        changed = 1;
                }
        }
@@ -423,10 +441,13 @@ static int pcxhr_monitor_vol_put(struct snd_kcontrol *kcontrol,
 
        mutex_lock(&chip->mgr->mixer_mutex);
        for (i = 0; i < 2; i++) {
-               if (chip->monitoring_volume[i] != ucontrol->value.integer.value[i]) {
-                       chip->monitoring_volume[i] = ucontrol->value.integer.value[i];
-                       if(chip->monitoring_active[i])  /* do only when monitoring is unmuted */
+               if (chip->monitoring_volume[i] !=
+                   ucontrol->value.integer.value[i]) {
+                       chip->monitoring_volume[i] =
+                               !!ucontrol->value.integer.value[i];
+                       if(chip->monitoring_active[i])
                                /* update monitoring volume and mute */
+                               /* do only when monitoring is unmuted */
                                pcxhr_update_audio_pipe_level(chip, 0, i);
                        changed = 1;
                }
@@ -470,15 +491,17 @@ static int pcxhr_monitor_sw_put(struct snd_kcontrol *kcontrol,
 
        mutex_lock(&chip->mgr->mixer_mutex);
        for (i = 0; i < 2; i++) {
-               if (chip->monitoring_active[i] != ucontrol->value.integer.value[i]) {
-                       chip->monitoring_active[i] = ucontrol->value.integer.value[i];
+               if (chip->monitoring_active[i] !=
+                   ucontrol->value.integer.value[i]) {
+                       chip->monitoring_active[i] =
+                               !!ucontrol->value.integer.value[i];
                        changed |= (1<<i); /* mask 0x01 and 0x02 */
                }
        }
-       if(changed & 0x01)
+       if (changed & 0x01)
                /* update left monitoring volume and mute */
                pcxhr_update_audio_pipe_level(chip, 0, 0);
-       if(changed & 0x02)
+       if (changed & 0x02)
                /* update right monitoring volume and mute */
                pcxhr_update_audio_pipe_level(chip, 0, 1);
 
@@ -579,6 +602,8 @@ static int pcxhr_audio_src_put(struct snd_kcontrol *kcontrol,
        struct snd_pcxhr *chip = snd_kcontrol_chip(kcontrol);
        int ret = 0;
 
+       if (ucontrol->value.enumerated.item[0] >= 3)
+               return -EINVAL;
        mutex_lock(&chip->mgr->mixer_mutex);
        if (chip->audio_capture_source != ucontrol->value.enumerated.item[0]) {
                chip->audio_capture_source = ucontrol->value.enumerated.item[0];
@@ -642,8 +667,11 @@ static int pcxhr_clock_type_put(struct snd_kcontrol *kcontrol,
                                struct snd_ctl_elem_value *ucontrol)
 {
        struct pcxhr_mgr *mgr = snd_kcontrol_chip(kcontrol);
+       unsigned int clock_items = 3 + mgr->capture_chips;
        int rate, ret = 0;
 
+       if (ucontrol->value.enumerated.item[0] >= clock_items)
+               return -EINVAL;
        mutex_lock(&mgr->mixer_mutex);
        if (mgr->use_clock_type != ucontrol->value.enumerated.item[0]) {
                mutex_lock(&mgr->setup_mutex);
index 8e5410483e67a0f851ea99c9209589739a143282..9408b1eeec40d551615334250d809d8aaf1acdc2 100644 (file)
@@ -88,7 +88,6 @@
             Adopted for Windows NT driver          01/20/98      CNL
 */
 
-#include <sound/driver.h>
 #include <linux/delay.h>
 #include <linux/init.h>
 #include <linux/interrupt.h>
index 1475912588e9ddb04b5b6e4126f036530f1920a6..df184aabce846e3eb1611b50e3c2e7d92891dbb1 100644 (file)
@@ -69,7 +69,6 @@
  */
 
 
-#include <sound/driver.h>
 #include <linux/delay.h>
 #include <linux/init.h>
 #include <linux/interrupt.h>
index 0b3c532c4014270e69abe05482e79256777b5af2..fb0a4ee8bc02aaa0c9d3b7a341095a959a158f92 100644 (file)
@@ -23,7 +23,6 @@
  *
  */      
 
-#include <sound/driver.h>
 #include <linux/delay.h>
 #include <linux/init.h>
 #include <linux/interrupt.h>
@@ -2195,22 +2194,25 @@ snd_rme96_dac_volume_put(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_valu
 {
        struct rme96 *rme96 = snd_kcontrol_chip(kcontrol);
         int change = 0;
+       unsigned int vol, maxvol;
 
-       if (!RME96_HAS_ANALOG_OUT(rme96)) {
+
+       if (!RME96_HAS_ANALOG_OUT(rme96))
                return -EINVAL;
-       }
+       maxvol = RME96_185X_MAX_OUT(rme96);
        spin_lock_irq(&rme96->lock);
-        if (u->value.integer.value[0] != rme96->vol[0]) {
-               rme96->vol[0] = u->value.integer.value[0];
-                change = 1;
-        }
-        if (u->value.integer.value[1] != rme96->vol[1]) {
-               rme96->vol[1] = u->value.integer.value[1];
-                change = 1;
-        }
-       if (change) {
-               snd_rme96_apply_dac_volume(rme96);
+       vol = u->value.integer.value[0];
+       if (vol != rme96->vol[0] && vol <= maxvol) {
+               rme96->vol[0] = vol;
+               change = 1;
+       }
+       vol = u->value.integer.value[1];
+       if (vol != rme96->vol[1] && vol <= maxvol) {
+               rme96->vol[1] = vol;
+               change = 1;
        }
+       if (change)
+               snd_rme96_apply_dac_volume(rme96);
        spin_unlock_irq(&rme96->lock);
 
         return change;
index ff26a3672d403152b81fe946c71c4b05f601bef4..c2bd4384316a8ad4ccf509dce009b62c241da335 100644 (file)
@@ -21,7 +21,6 @@
  *
  */
 
-#include <sound/driver.h>
 #include <linux/init.h>
 #include <linux/delay.h>
 #include <linux/interrupt.h>
@@ -104,8 +103,6 @@ MODULE_FIRMWARE("digiface_firmware_rev11.bin");
 #define HDSP_statusRegister    0
 #define HDSP_timecode        128
 #define HDSP_status2Register 192
-#define HDSP_midiDataOut0    352
-#define HDSP_midiDataOut1    356
 #define HDSP_midiDataIn0     360
 #define HDSP_midiDataIn1     364
 #define HDSP_midiStatusOut0  384
@@ -610,7 +607,10 @@ static int hdsp_playback_to_output_key (struct hdsp *hdsp, int in, int out)
        case Multiface:
        case Digiface:
        default:
-               return (64 * out) + (32 + (in));
+               if (hdsp->firmware_rev == 0xa)
+                       return (64 * out) + (32 + (in));
+               else
+                       return (52 * out) + (26 + (in));
        case H9632:
                return (32 * out) + (16 + (in));
        case H9652:
@@ -624,7 +624,10 @@ static int hdsp_input_to_output_key (struct hdsp *hdsp, int in, int out)
        case Multiface:
        case Digiface:
        default:
-               return (64 * out) + in;
+               if (hdsp->firmware_rev == 0xa)
+                       return (64 * out) + in;
+               else
+                       return (52 * out) + in;
        case H9632:
                return (32 * out) + in;
        case H9652:
@@ -2121,7 +2124,7 @@ static int snd_hdsp_put_clock_source_lock(struct snd_kcontrol *kcontrol, struct
 
        change = (int)ucontrol->value.integer.value[0] != hdsp->clock_source_locked;
        if (change)
-               hdsp->clock_source_locked = ucontrol->value.integer.value[0];
+               hdsp->clock_source_locked = !!ucontrol->value.integer.value[0];
        return change;
 }
 
@@ -3558,7 +3561,7 @@ snd_hdsp_proc_read(struct snd_info_entry *entry, struct snd_info_buffer *buffer)
 
 }
 
-static void __devinit snd_hdsp_proc_init(struct hdsp *hdsp)
+static void snd_hdsp_proc_init(struct hdsp *hdsp)
 {
        struct snd_info_entry *entry;
 
@@ -3606,7 +3609,7 @@ static int snd_hdsp_set_defaults(struct hdsp *hdsp)
 
        /* ASSUMPTION: hdsp->lock is either held, or
           there is no need to hold it (e.g. during module
-          initalization).
+          initialization).
         */
 
        /* set defaults:
index f1bdda6cbcffd5f384ddf76c96fa47ac963b8c62..9a19ae6a64d9a06f158a2da2f22a761edcd6c730 100644 (file)
@@ -23,7 +23,6 @@
  *   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA
  *
  */
-#include <sound/driver.h>
 #include <linux/init.h>
 #include <linux/delay.h>
 #include <linux/interrupt.h>
@@ -3348,7 +3347,7 @@ static int snd_hdspm_set_defaults(struct hdspm * hdspm)
        unsigned int i;
 
        /* ASSUMPTION: hdspm->lock is either held, or there is no need to
-          hold it (e.g. during module initalization).
+          hold it (e.g. during module initialization).
         */
 
        /* set defaults:       */
@@ -3416,7 +3415,7 @@ static int snd_hdspm_set_defaults(struct hdspm * hdspm)
 
 
 /*------------------------------------------------------------
-   interupt 
+   interrupt 
  ------------------------------------------------------------*/
 
 static irqreturn_t snd_hdspm_interrupt(int irq, void *dev_id)
index 34f96f12e5bfcad9b909152625fa50ff2efb81ff..a123f0e6ba23386213738dfdb835c7fa5a55353b 100644 (file)
@@ -20,7 +20,6 @@
  *
  */
 
-#include <sound/driver.h>
 #include <linux/delay.h>
 #include <linux/init.h>
 #include <linux/interrupt.h>
@@ -148,7 +147,7 @@ MODULE_SUPPORTED_DEVICE("{{RME,Hammerfall},"
 #define RME9652_start_bit         (1<<0)       /* start record/play */
                                                 /* bits 1-3 encode buffersize/latency */
 #define RME9652_Master            (1<<4)       /* Clock Mode Master=1,Slave/Auto=0 */
-#define RME9652_IE                (1<<5)       /* Interupt Enable */
+#define RME9652_IE                (1<<5)       /* Interrupt Enable */
 #define RME9652_freq              (1<<6)       /* samplerate 0=44.1/88.2, 1=48/96 kHz */
 #define RME9652_freq1             (1<<7)       /* if 0, 32kHz, else always 1 */
 #define RME9652_DS                 (1<<8)      /* Doule Speed 0=44.1/48, 1=88.2/96 Khz */
@@ -1826,7 +1825,7 @@ static void snd_rme9652_set_defaults(struct snd_rme9652 *rme9652)
 
        /* ASSUMPTION: rme9652->lock is either held, or
           there is no need to hold it (e.g. during module
-          initalization).
+          initialization).
         */
 
        /* set defaults:
diff --git a/sound/pci/sis7019.c b/sound/pci/sis7019.c
new file mode 100644 (file)
index 0000000..dcd7cd0
--- /dev/null
@@ -0,0 +1,1460 @@
+/*
+ *  Driver for SiS7019 Audio Accelerator
+ *
+ *  Copyright (C) 2004-2007, David Dillow
+ *  Written by David Dillow <dave@thedillows.org>
+ *  Inspired by the Trident 4D-WaveDX/NX driver.
+ *
+ *  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 as published by
+ *  the Free Software Foundation, version 2.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA
+ */
+
+#include <linux/init.h>
+#include <linux/pci.h>
+#include <linux/time.h>
+#include <linux/moduleparam.h>
+#include <linux/interrupt.h>
+#include <linux/delay.h>
+#include <sound/core.h>
+#include <sound/ac97_codec.h>
+#include <sound/initval.h>
+#include "sis7019.h"
+
+MODULE_AUTHOR("David Dillow <dave@thedillows.org>");
+MODULE_DESCRIPTION("SiS7019");
+MODULE_LICENSE("GPL");
+MODULE_SUPPORTED_DEVICE("{{SiS,SiS7019 Audio Accelerator}}");
+
+static int index = SNDRV_DEFAULT_IDX1; /* Index 0-MAX */
+static char *id = SNDRV_DEFAULT_STR1;  /* ID for this card */
+static int enable = 1;
+
+module_param(index, int, 0444);
+MODULE_PARM_DESC(index, "Index value for SiS7019 Audio Accelerator.");
+module_param(id, charp, 0444);
+MODULE_PARM_DESC(id, "ID string for SiS7019 Audio Accelerator.");
+module_param(enable, bool, 0444);
+MODULE_PARM_DESC(enable, "Enable SiS7019 Audio Accelerator.");
+
+static struct pci_device_id snd_sis7019_ids[] = {
+       { PCI_DEVICE(PCI_VENDOR_ID_SI, 0x7019) },
+       { 0, }
+};
+
+MODULE_DEVICE_TABLE(pci, snd_sis7019_ids);
+
+/* There are three timing modes for the voices.
+ *
+ * For both playback and capture, when the buffer is one or two periods long,
+ * we use the hardware's built-in Mid-Loop Interrupt and End-Loop Interrupt
+ * to let us know when the periods have ended.
+ *
+ * When performing playback with more than two periods per buffer, we set
+ * the "Stop Sample Offset" and tell the hardware to interrupt us when we
+ * reach it. We then update the offset and continue on until we are
+ * interrupted for the next period.
+ *
+ * Capture channels do not have a SSO, so we allocate a playback channel to
+ * use as a timer for the capture periods. We use the SSO on the playback
+ * channel to clock out virtual periods, and adjust the virtual period length
+ * to maintain synchronization. This algorithm came from the Trident driver.
+ *
+ * FIXME: It'd be nice to make use of some of the synth features in the
+ * hardware, but a woeful lack of documentation is a significant roadblock.
+ */
+struct voice {
+       u16 flags;
+#define        VOICE_IN_USE            1
+#define        VOICE_CAPTURE           2
+#define        VOICE_SSO_TIMING        4
+#define        VOICE_SYNC_TIMING       8
+       u16 sync_cso;
+       u16 period_size;
+       u16 buffer_size;
+       u16 sync_period_size;
+       u16 sync_buffer_size;
+       u32 sso;
+       u32 vperiod;
+       struct snd_pcm_substream *substream;
+       struct voice *timing;
+       void __iomem *ctrl_base;
+       void __iomem *wave_base;
+       void __iomem *sync_base;
+       int num;
+};
+
+/* We need four pages to store our wave parameters during a suspend. If
+ * we're not doing power management, we still need to allocate a page
+ * for the silence buffer.
+ */
+#ifdef CONFIG_PM
+#define SIS_SUSPEND_PAGES      4
+#else
+#define SIS_SUSPEND_PAGES      1
+#endif
+
+struct sis7019 {
+       unsigned long ioport;
+       void __iomem *ioaddr;
+       int irq;
+       int codecs_present;
+
+       struct pci_dev *pci;
+       struct snd_pcm *pcm;
+       struct snd_card *card;
+       struct snd_ac97 *ac97[3];
+
+       /* Protect against more than one thread hitting the AC97
+        * registers (in a more polite manner than pounding the hardware
+        * semaphore)
+        */
+       struct mutex ac97_mutex;
+
+       /* voice_lock protects allocation/freeing of the voice descriptions
+        */
+       spinlock_t voice_lock;
+
+       struct voice voices[64];
+       struct voice capture_voice;
+
+       /* Allocate pages to store the internal wave state during
+        * suspends. When we're operating, this can be used as a silence
+        * buffer for a timing channel.
+        */
+       void *suspend_state[SIS_SUSPEND_PAGES];
+
+       int silence_users;
+       dma_addr_t silence_dma_addr;
+};
+
+#define SIS_PRIMARY_CODEC_PRESENT      0x0001
+#define SIS_SECONDARY_CODEC_PRESENT    0x0002
+#define SIS_TERTIARY_CODEC_PRESENT     0x0004
+
+/* The HW offset parameters (Loop End, Stop Sample, End Sample) have a
+ * documented range of 8-0xfff8 samples. Given that they are 0-based,
+ * that places our period/buffer range at 9-0xfff9 samples. That makes the
+ * max buffer size 0xfff9 samples * 2 channels * 2 bytes per sample, and
+ * max samples / min samples gives us the max periods in a buffer.
+ *
+ * We'll add a constraint upon open that limits the period and buffer sample
+ * size to values that are legal for the hardware.
+ */
+static struct snd_pcm_hardware sis_playback_hw_info = {
+       .info = (SNDRV_PCM_INFO_MMAP |
+                SNDRV_PCM_INFO_MMAP_VALID |
+                SNDRV_PCM_INFO_INTERLEAVED |
+                SNDRV_PCM_INFO_BLOCK_TRANSFER |
+                SNDRV_PCM_INFO_SYNC_START |
+                SNDRV_PCM_INFO_RESUME),
+       .formats = (SNDRV_PCM_FMTBIT_S8 | SNDRV_PCM_FMTBIT_U8 |
+                   SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_U16_LE),
+       .rates = SNDRV_PCM_RATE_8000_48000 | SNDRV_PCM_RATE_CONTINUOUS,
+       .rate_min = 4000,
+       .rate_max = 48000,
+       .channels_min = 1,
+       .channels_max = 2,
+       .buffer_bytes_max = (0xfff9 * 4),
+       .period_bytes_min = 9,
+       .period_bytes_max = (0xfff9 * 4),
+       .periods_min = 1,
+       .periods_max = (0xfff9 / 9),
+};
+
+static struct snd_pcm_hardware sis_capture_hw_info = {
+       .info = (SNDRV_PCM_INFO_MMAP |
+                SNDRV_PCM_INFO_MMAP_VALID |
+                SNDRV_PCM_INFO_INTERLEAVED |
+                SNDRV_PCM_INFO_BLOCK_TRANSFER |
+                SNDRV_PCM_INFO_SYNC_START |
+                SNDRV_PCM_INFO_RESUME),
+       .formats = (SNDRV_PCM_FMTBIT_S8 | SNDRV_PCM_FMTBIT_U8 |
+                   SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_U16_LE),
+       .rates = SNDRV_PCM_RATE_48000,
+       .rate_min = 4000,
+       .rate_max = 48000,
+       .channels_min = 1,
+       .channels_max = 2,
+       .buffer_bytes_max = (0xfff9 * 4),
+       .period_bytes_min = 9,
+       .period_bytes_max = (0xfff9 * 4),
+       .periods_min = 1,
+       .periods_max = (0xfff9 / 9),
+};
+
+static void sis_update_sso(struct voice *voice, u16 period)
+{
+       void __iomem *base = voice->ctrl_base;
+
+       voice->sso += period;
+       if (voice->sso >= voice->buffer_size)
+               voice->sso -= voice->buffer_size;
+
+       /* Enforce the documented hardware minimum offset */
+       if (voice->sso < 8)
+               voice->sso = 8;
+
+       /* The SSO is in the upper 16 bits of the register. */
+       writew(voice->sso & 0xffff, base + SIS_PLAY_DMA_SSO_ESO + 2);
+}
+
+static void sis_update_voice(struct voice *voice)
+{
+       if (voice->flags & VOICE_SSO_TIMING) {
+               sis_update_sso(voice, voice->period_size);
+       } else if (voice->flags & VOICE_SYNC_TIMING) {
+               int sync;
+
+               /* If we've not hit the end of the virtual period, update
+                * our records and keep going.
+                */
+               if (voice->vperiod > voice->period_size) {
+                       voice->vperiod -= voice->period_size;
+                       if (voice->vperiod < voice->period_size)
+                               sis_update_sso(voice, voice->vperiod);
+                       else
+                               sis_update_sso(voice, voice->period_size);
+                       return;
+               }
+
+               /* Calculate our relative offset between the target and
+                * the actual CSO value. Since we're operating in a loop,
+                * if the value is more than half way around, we can
+                * consider ourselves wrapped.
+                */
+               sync = voice->sync_cso;
+               sync -= readw(voice->sync_base + SIS_CAPTURE_DMA_FORMAT_CSO);
+               if (sync > (voice->sync_buffer_size / 2))
+                       sync -= voice->sync_buffer_size;
+
+               /* If sync is positive, then we interrupted too early, and
+                * we'll need to come back in a few samples and try again.
+                * There's a minimum wait, as it takes some time for the DMA
+                * engine to startup, etc...
+                */
+               if (sync > 0) {
+                       if (sync < 16)
+                               sync = 16;
+                       sis_update_sso(voice, sync);
+                       return;
+               }
+
+               /* Ok, we interrupted right on time, or (hopefully) just
+                * a bit late. We'll adjst our next waiting period based
+                * on how close we got.
+                *
+                * We need to stay just behind the actual channel to ensure
+                * it really is past a period when we get our interrupt --
+                * otherwise we'll fall into the early code above and have
+                * a minimum wait time, which makes us quite late here,
+                * eating into the user's time to refresh the buffer, esp.
+                * if using small periods.
+                *
+                * If we're less than 9 samples behind, we're on target.
+                */
+               if (sync > -9)
+                       voice->vperiod = voice->sync_period_size + 1;
+               else
+                       voice->vperiod = voice->sync_period_size - 4;
+
+               if (voice->vperiod < voice->buffer_size) {
+                       sis_update_sso(voice, voice->vperiod);
+                       voice->vperiod = 0;
+               } else
+                       sis_update_sso(voice, voice->period_size);
+
+               sync = voice->sync_cso + voice->sync_period_size;
+               if (sync >= voice->sync_buffer_size)
+                       sync -= voice->sync_buffer_size;
+               voice->sync_cso = sync;
+       }
+
+       snd_pcm_period_elapsed(voice->substream);
+}
+
+static void sis_voice_irq(u32 status, struct voice *voice)
+{
+       int bit;
+
+       while (status) {
+               bit = __ffs(status);
+               status >>= bit + 1;
+               voice += bit;
+               sis_update_voice(voice);
+               voice++;
+       }
+}
+
+static irqreturn_t sis_interrupt(int irq, void *dev)
+{
+       struct sis7019 *sis = dev;
+       unsigned long io = sis->ioport;
+       struct voice *voice;
+       u32 intr, status;
+
+       /* We only use the DMA interrupts, and we don't enable any other
+        * source of interrupts. But, it is possible to see an interupt
+        * status that didn't actually interrupt us, so eliminate anything
+        * we're not expecting to avoid falsely claiming an IRQ, and an
+        * ensuing endless loop.
+        */
+       intr = inl(io + SIS_GISR);
+       intr &= SIS_GISR_AUDIO_PLAY_DMA_IRQ_STATUS |
+               SIS_GISR_AUDIO_RECORD_DMA_IRQ_STATUS;
+       if (!intr)
+               return IRQ_NONE;
+
+       do {
+               status = inl(io + SIS_PISR_A);
+               if (status) {
+                       sis_voice_irq(status, sis->voices);
+                       outl(status, io + SIS_PISR_A);
+               }
+
+               status = inl(io + SIS_PISR_B);
+               if (status) {
+                       sis_voice_irq(status, &sis->voices[32]);
+                       outl(status, io + SIS_PISR_B);
+               }
+
+               status = inl(io + SIS_RISR);
+               if (status) {
+                       voice = &sis->capture_voice;
+                       if (!voice->timing)
+                               snd_pcm_period_elapsed(voice->substream);
+
+                       outl(status, io + SIS_RISR);
+               }
+
+               outl(intr, io + SIS_GISR);
+               intr = inl(io + SIS_GISR);
+               intr &= SIS_GISR_AUDIO_PLAY_DMA_IRQ_STATUS |
+                       SIS_GISR_AUDIO_RECORD_DMA_IRQ_STATUS;
+       } while (intr);
+
+       return IRQ_HANDLED;
+}
+
+static u32 sis_rate_to_delta(unsigned int rate)
+{
+       u32 delta;
+
+       /* This was copied from the trident driver, but it seems its gotten
+        * around a bit... nevertheless, it works well.
+        *
+        * We special case 44100 and 8000 since rounding with the equation
+        * does not give us an accurate enough value. For 11025 and 22050
+        * the equation gives us the best answer. All other frequencies will
+        * also use the equation. JDW
+        */
+       if (rate == 44100)
+               delta = 0xeb3;
+       else if (rate == 8000)
+               delta = 0x2ab;
+       else if (rate == 48000)
+               delta = 0x1000;
+       else
+               delta = (((rate << 12) + 24000) / 48000) & 0x0000ffff;
+       return delta;
+}
+
+static void __sis_map_silence(struct sis7019 *sis)
+{
+       /* Helper function: must hold sis->voice_lock on entry */
+       if (!sis->silence_users)
+               sis->silence_dma_addr = pci_map_single(sis->pci,
+                                               sis->suspend_state[0],
+                                               4096, PCI_DMA_TODEVICE);
+       sis->silence_users++;
+}
+
+static void __sis_unmap_silence(struct sis7019 *sis)
+{
+       /* Helper function: must hold sis->voice_lock on entry */
+       sis->silence_users--;
+       if (!sis->silence_users)
+               pci_unmap_single(sis->pci, sis->silence_dma_addr, 4096,
+                                       PCI_DMA_TODEVICE);
+}
+
+static void sis_free_voice(struct sis7019 *sis, struct voice *voice)
+{
+       unsigned long flags;
+
+       spin_lock_irqsave(&sis->voice_lock, flags);
+       if (voice->timing) {
+               __sis_unmap_silence(sis);
+               voice->timing->flags &= ~(VOICE_IN_USE | VOICE_SSO_TIMING |
+                                               VOICE_SYNC_TIMING);
+               voice->timing = NULL;
+       }
+       voice->flags &= ~(VOICE_IN_USE | VOICE_SSO_TIMING | VOICE_SYNC_TIMING);
+       spin_unlock_irqrestore(&sis->voice_lock, flags);
+}
+
+static struct voice *__sis_alloc_playback_voice(struct sis7019 *sis)
+{
+       /* Must hold the voice_lock on entry */
+       struct voice *voice;
+       int i;
+
+       for (i = 0; i < 64; i++) {
+               voice = &sis->voices[i];
+               if (voice->flags & VOICE_IN_USE)
+                       continue;
+               voice->flags |= VOICE_IN_USE;
+               goto found_one;
+       }
+       voice = NULL;
+
+found_one:
+       return voice;
+}
+
+static struct voice *sis_alloc_playback_voice(struct sis7019 *sis)
+{
+       struct voice *voice;
+       unsigned long flags;
+
+       spin_lock_irqsave(&sis->voice_lock, flags);
+       voice = __sis_alloc_playback_voice(sis);
+       spin_unlock_irqrestore(&sis->voice_lock, flags);
+
+       return voice;
+}
+
+static int sis_alloc_timing_voice(struct snd_pcm_substream *substream,
+                                       struct snd_pcm_hw_params *hw_params)
+{
+       struct sis7019 *sis = snd_pcm_substream_chip(substream);
+       struct snd_pcm_runtime *runtime = substream->runtime;
+       struct voice *voice = runtime->private_data;
+       unsigned int period_size, buffer_size;
+       unsigned long flags;
+       int needed;
+
+       /* If there are one or two periods per buffer, we don't need a
+        * timing voice, as we can use the capture channel's interrupts
+        * to clock out the periods.
+        */
+       period_size = params_period_size(hw_params);
+       buffer_size = params_buffer_size(hw_params);
+       needed = (period_size != buffer_size &&
+                       period_size != (buffer_size / 2));
+
+       if (needed && !voice->timing) {
+               spin_lock_irqsave(&sis->voice_lock, flags);
+               voice->timing = __sis_alloc_playback_voice(sis);
+               if (voice->timing)
+                       __sis_map_silence(sis);
+               spin_unlock_irqrestore(&sis->voice_lock, flags);
+               if (!voice->timing)
+                       return -ENOMEM;
+               voice->timing->substream = substream;
+       } else if (!needed && voice->timing) {
+               sis_free_voice(sis, voice);
+               voice->timing = NULL;
+       }
+
+       return 0;
+}
+
+static int sis_playback_open(struct snd_pcm_substream *substream)
+{
+       struct sis7019 *sis = snd_pcm_substream_chip(substream);
+       struct snd_pcm_runtime *runtime = substream->runtime;
+       struct voice *voice;
+
+       voice = sis_alloc_playback_voice(sis);
+       if (!voice)
+               return -EAGAIN;
+
+       voice->substream = substream;
+       runtime->private_data = voice;
+       runtime->hw = sis_playback_hw_info;
+       snd_pcm_hw_constraint_minmax(runtime, SNDRV_PCM_HW_PARAM_PERIOD_SIZE,
+                                               9, 0xfff9);
+       snd_pcm_hw_constraint_minmax(runtime, SNDRV_PCM_HW_PARAM_BUFFER_SIZE,
+                                               9, 0xfff9);
+       snd_pcm_set_sync(substream);
+       return 0;
+}
+
+static int sis_substream_close(struct snd_pcm_substream *substream)
+{
+       struct sis7019 *sis = snd_pcm_substream_chip(substream);
+       struct snd_pcm_runtime *runtime = substream->runtime;
+       struct voice *voice = runtime->private_data;
+
+       sis_free_voice(sis, voice);
+       return 0;
+}
+
+static int sis_playback_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 sis_hw_free(struct snd_pcm_substream *substream)
+{
+       return snd_pcm_lib_free_pages(substream);
+}
+
+static int sis_pcm_playback_prepare(struct snd_pcm_substream *substream)
+{
+       struct snd_pcm_runtime *runtime = substream->runtime;
+       struct voice *voice = runtime->private_data;
+       void __iomem *ctrl_base = voice->ctrl_base;
+       void __iomem *wave_base = voice->wave_base;
+       u32 format, dma_addr, control, sso_eso, delta, reg;
+       u16 leo;
+
+       /* We rely on the PCM core to ensure that the parameters for this
+        * substream do not change on us while we're programming the HW.
+        */
+       format = 0;
+       if (snd_pcm_format_width(runtime->format) == 8)
+               format |= SIS_PLAY_DMA_FORMAT_8BIT;
+       if (!snd_pcm_format_signed(runtime->format))
+               format |= SIS_PLAY_DMA_FORMAT_UNSIGNED;
+       if (runtime->channels == 1)
+               format |= SIS_PLAY_DMA_FORMAT_MONO;
+
+       /* The baseline setup is for a single period per buffer, and
+        * we add bells and whistles as needed from there.
+        */
+       dma_addr = runtime->dma_addr;
+       leo = runtime->buffer_size - 1;
+       control = leo | SIS_PLAY_DMA_LOOP | SIS_PLAY_DMA_INTR_AT_LEO;
+       sso_eso = leo;
+
+       if (runtime->period_size == (runtime->buffer_size / 2)) {
+               control |= SIS_PLAY_DMA_INTR_AT_MLP;
+       } else if (runtime->period_size != runtime->buffer_size) {
+               voice->flags |= VOICE_SSO_TIMING;
+               voice->sso = runtime->period_size - 1;
+               voice->period_size = runtime->period_size;
+               voice->buffer_size = runtime->buffer_size;
+
+               control &= ~SIS_PLAY_DMA_INTR_AT_LEO;
+               control |= SIS_PLAY_DMA_INTR_AT_SSO;
+               sso_eso |= (runtime->period_size - 1) << 16;
+       }
+
+       delta = sis_rate_to_delta(runtime->rate);
+
+       /* Ok, we're ready to go, set up the channel.
+        */
+       writel(format, ctrl_base + SIS_PLAY_DMA_FORMAT_CSO);
+       writel(dma_addr, ctrl_base + SIS_PLAY_DMA_BASE);
+       writel(control, ctrl_base + SIS_PLAY_DMA_CONTROL);
+       writel(sso_eso, ctrl_base + SIS_PLAY_DMA_SSO_ESO);
+
+       for (reg = 0; reg < SIS_WAVE_SIZE; reg += 4)
+               writel(0, wave_base + reg);
+
+       writel(SIS_WAVE_GENERAL_WAVE_VOLUME, wave_base + SIS_WAVE_GENERAL);
+       writel(delta << 16, wave_base + SIS_WAVE_GENERAL_ARTICULATION);
+       writel(SIS_WAVE_CHANNEL_CONTROL_FIRST_SAMPLE |
+                       SIS_WAVE_CHANNEL_CONTROL_AMP_ENABLE |
+                       SIS_WAVE_CHANNEL_CONTROL_INTERPOLATE_ENABLE,
+                       wave_base + SIS_WAVE_CHANNEL_CONTROL);
+
+       /* Force PCI writes to post. */
+       readl(ctrl_base);
+
+       return 0;
+}
+
+static int sis_pcm_trigger(struct snd_pcm_substream *substream, int cmd)
+{
+       struct sis7019 *sis = snd_pcm_substream_chip(substream);
+       unsigned long io = sis->ioport;
+       struct snd_pcm_substream *s;
+       struct voice *voice;
+       void *chip;
+       int starting;
+       u32 record = 0;
+       u32 play[2] = { 0, 0 };
+
+       /* No locks needed, as the PCM core will hold the locks on the
+        * substreams, and the HW will only start/stop the indicated voices
+        * without changing the state of the others.
+        */
+       switch (cmd) {
+       case SNDRV_PCM_TRIGGER_START:
+       case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
+       case SNDRV_PCM_TRIGGER_RESUME:
+               starting = 1;
+               break;
+       case SNDRV_PCM_TRIGGER_STOP:
+       case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
+       case SNDRV_PCM_TRIGGER_SUSPEND:
+               starting = 0;
+               break;
+       default:
+               return -EINVAL;
+       }
+
+       snd_pcm_group_for_each_entry(s, substream) {
+               /* Make sure it is for us... */
+               chip = snd_pcm_substream_chip(s);
+               if (chip != sis)
+                       continue;
+
+               voice = s->runtime->private_data;
+               if (voice->flags & VOICE_CAPTURE) {
+                       record |= 1 << voice->num;
+                       voice = voice->timing;
+               }
+
+               /* voice could be NULL if this a recording stream, and it
+                * doesn't have an external timing channel.
+                */
+               if (voice)
+                       play[voice->num / 32] |= 1 << (voice->num & 0x1f);
+
+               snd_pcm_trigger_done(s, substream);
+       }
+
+       if (starting) {
+               if (record)
+                       outl(record, io + SIS_RECORD_START_REG);
+               if (play[0])
+                       outl(play[0], io + SIS_PLAY_START_A_REG);
+               if (play[1])
+                       outl(play[1], io + SIS_PLAY_START_B_REG);
+       } else {
+               if (record)
+                       outl(record, io + SIS_RECORD_STOP_REG);
+               if (play[0])
+                       outl(play[0], io + SIS_PLAY_STOP_A_REG);
+               if (play[1])
+                       outl(play[1], io + SIS_PLAY_STOP_B_REG);
+       }
+       return 0;
+}
+
+static snd_pcm_uframes_t sis_pcm_pointer(struct snd_pcm_substream *substream)
+{
+       struct snd_pcm_runtime *runtime = substream->runtime;
+       struct voice *voice = runtime->private_data;
+       u32 cso;
+
+       cso = readl(voice->ctrl_base + SIS_PLAY_DMA_FORMAT_CSO);
+       cso &= 0xffff;
+       return cso;
+}
+
+static int sis_capture_open(struct snd_pcm_substream *substream)
+{
+       struct sis7019 *sis = snd_pcm_substream_chip(substream);
+       struct snd_pcm_runtime *runtime = substream->runtime;
+       struct voice *voice = &sis->capture_voice;
+       unsigned long flags;
+
+       /* FIXME: The driver only supports recording from one channel
+        * at the moment, but it could support more.
+        */
+       spin_lock_irqsave(&sis->voice_lock, flags);
+       if (voice->flags & VOICE_IN_USE)
+               voice = NULL;
+       else
+               voice->flags |= VOICE_IN_USE;
+       spin_unlock_irqrestore(&sis->voice_lock, flags);
+
+       if (!voice)
+               return -EAGAIN;
+
+       voice->substream = substream;
+       runtime->private_data = voice;
+       runtime->hw = sis_capture_hw_info;
+       runtime->hw.rates = sis->ac97[0]->rates[AC97_RATES_ADC];
+       snd_pcm_limit_hw_rates(runtime);
+       snd_pcm_hw_constraint_minmax(runtime, SNDRV_PCM_HW_PARAM_PERIOD_SIZE,
+                                               9, 0xfff9);
+       snd_pcm_hw_constraint_minmax(runtime, SNDRV_PCM_HW_PARAM_BUFFER_SIZE,
+                                               9, 0xfff9);
+       snd_pcm_set_sync(substream);
+       return 0;
+}
+
+static int sis_capture_hw_params(struct snd_pcm_substream *substream,
+                                       struct snd_pcm_hw_params *hw_params)
+{
+       struct sis7019 *sis = snd_pcm_substream_chip(substream);
+       int rc;
+
+       rc = snd_ac97_set_rate(sis->ac97[0], AC97_PCM_LR_ADC_RATE,
+                                               params_rate(hw_params));
+       if (rc)
+               goto out;
+
+       rc = snd_pcm_lib_malloc_pages(substream,
+                                       params_buffer_bytes(hw_params));
+       if (rc < 0)
+               goto out;
+
+       rc = sis_alloc_timing_voice(substream, hw_params);
+
+out:
+       return rc;
+}
+
+static void sis_prepare_timing_voice(struct voice *voice,
+                                       struct snd_pcm_substream *substream)
+{
+       struct sis7019 *sis = snd_pcm_substream_chip(substream);
+       struct snd_pcm_runtime *runtime = substream->runtime;
+       struct voice *timing = voice->timing;
+       void __iomem *play_base = timing->ctrl_base;
+       void __iomem *wave_base = timing->wave_base;
+       u16 buffer_size, period_size;
+       u32 format, control, sso_eso, delta;
+       u32 vperiod, sso, reg;
+
+       /* Set our initial buffer and period as large as we can given a
+        * single page of silence.
+        */
+       buffer_size = 4096 / runtime->channels;
+       buffer_size /= snd_pcm_format_size(runtime->format, 1);
+       period_size = buffer_size;
+
+       /* Initially, we want to interrupt just a bit behind the end of
+        * the period we're clocking out. 10 samples seems to give a good
+        * delay.
+        *
+        * We want to spread our interrupts throughout the virtual period,
+        * so that we don't end up with two interrupts back to back at the
+        * end -- this helps minimize the effects of any jitter. Adjust our
+        * clocking period size so that the last period is at least a fourth
+        * of a full period.
+        *
+        * This is all moot if we don't need to use virtual periods.
+        */
+       vperiod = runtime->period_size + 10;
+       if (vperiod > period_size) {
+               u16 tail = vperiod % period_size;
+               u16 quarter_period = period_size / 4;
+
+               if (tail && tail < quarter_period) {
+                       u16 loops = vperiod / period_size;
+
+                       tail = quarter_period - tail;
+                       tail += loops - 1;
+                       tail /= loops;
+                       period_size -= tail;
+               }
+
+               sso = period_size - 1;
+       } else {
+               /* The initial period will fit inside the buffer, so we
+                * don't need to use virtual periods -- disable them.
+                */
+               period_size = runtime->period_size;
+               sso = vperiod - 1;
+               vperiod = 0;
+       }
+
+       /* The interrupt handler implements the timing syncronization, so
+        * setup its state.
+        */
+       timing->flags |= VOICE_SYNC_TIMING;
+       timing->sync_base = voice->ctrl_base;
+       timing->sync_cso = runtime->period_size - 1;
+       timing->sync_period_size = runtime->period_size;
+       timing->sync_buffer_size = runtime->buffer_size;
+       timing->period_size = period_size;
+       timing->buffer_size = buffer_size;
+       timing->sso = sso;
+       timing->vperiod = vperiod;
+
+       /* Using unsigned samples with the all-zero silence buffer
+        * forces the output to the lower rail, killing playback.
+        * So ignore unsigned vs signed -- it doesn't change the timing.
+        */
+       format = 0;
+       if (snd_pcm_format_width(runtime->format) == 8)
+               format = SIS_CAPTURE_DMA_FORMAT_8BIT;
+       if (runtime->channels == 1)
+               format |= SIS_CAPTURE_DMA_FORMAT_MONO;
+
+       control = timing->buffer_size - 1;
+       control |= SIS_PLAY_DMA_LOOP | SIS_PLAY_DMA_INTR_AT_SSO;
+       sso_eso = timing->buffer_size - 1;
+       sso_eso |= timing->sso << 16;
+
+       delta = sis_rate_to_delta(runtime->rate);
+
+       /* We've done the math, now configure the channel.
+        */
+       writel(format, play_base + SIS_PLAY_DMA_FORMAT_CSO);
+       writel(sis->silence_dma_addr, play_base + SIS_PLAY_DMA_BASE);
+       writel(control, play_base + SIS_PLAY_DMA_CONTROL);
+       writel(sso_eso, play_base + SIS_PLAY_DMA_SSO_ESO);
+
+       for (reg = 0; reg < SIS_WAVE_SIZE; reg += 4)
+               writel(0, wave_base + reg);
+
+       writel(SIS_WAVE_GENERAL_WAVE_VOLUME, wave_base + SIS_WAVE_GENERAL);
+       writel(delta << 16, wave_base + SIS_WAVE_GENERAL_ARTICULATION);
+       writel(SIS_WAVE_CHANNEL_CONTROL_FIRST_SAMPLE |
+                       SIS_WAVE_CHANNEL_CONTROL_AMP_ENABLE |
+                       SIS_WAVE_CHANNEL_CONTROL_INTERPOLATE_ENABLE,
+                       wave_base + SIS_WAVE_CHANNEL_CONTROL);
+}
+
+static int sis_pcm_capture_prepare(struct snd_pcm_substream *substream)
+{
+       struct snd_pcm_runtime *runtime = substream->runtime;
+       struct voice *voice = runtime->private_data;
+       void __iomem *rec_base = voice->ctrl_base;
+       u32 format, dma_addr, control;
+       u16 leo;
+
+       /* We rely on the PCM core to ensure that the parameters for this
+        * substream do not change on us while we're programming the HW.
+        */
+       format = 0;
+       if (snd_pcm_format_width(runtime->format) == 8)
+               format = SIS_CAPTURE_DMA_FORMAT_8BIT;
+       if (!snd_pcm_format_signed(runtime->format))
+               format |= SIS_CAPTURE_DMA_FORMAT_UNSIGNED;
+       if (runtime->channels == 1)
+               format |= SIS_CAPTURE_DMA_FORMAT_MONO;
+
+       dma_addr = runtime->dma_addr;
+       leo = runtime->buffer_size - 1;
+       control = leo | SIS_CAPTURE_DMA_LOOP;
+
+       /* If we've got more than two periods per buffer, then we have
+        * use a timing voice to clock out the periods. Otherwise, we can
+        * use the capture channel's interrupts.
+        */
+       if (voice->timing) {
+               sis_prepare_timing_voice(voice, substream);
+       } else {
+               control |= SIS_CAPTURE_DMA_INTR_AT_LEO;
+               if (runtime->period_size != runtime->buffer_size)
+                       control |= SIS_CAPTURE_DMA_INTR_AT_MLP;
+       }
+
+       writel(format, rec_base + SIS_CAPTURE_DMA_FORMAT_CSO);
+       writel(dma_addr, rec_base + SIS_CAPTURE_DMA_BASE);
+       writel(control, rec_base + SIS_CAPTURE_DMA_CONTROL);
+
+       /* Force the writes to post. */
+       readl(rec_base);
+
+       return 0;
+}
+
+static struct snd_pcm_ops sis_playback_ops = {
+       .open = sis_playback_open,
+       .close = sis_substream_close,
+       .ioctl = snd_pcm_lib_ioctl,
+       .hw_params = sis_playback_hw_params,
+       .hw_free = sis_hw_free,
+       .prepare = sis_pcm_playback_prepare,
+       .trigger = sis_pcm_trigger,
+       .pointer = sis_pcm_pointer,
+};
+
+static struct snd_pcm_ops sis_capture_ops = {
+       .open = sis_capture_open,
+       .close = sis_substream_close,
+       .ioctl = snd_pcm_lib_ioctl,
+       .hw_params = sis_capture_hw_params,
+       .hw_free = sis_hw_free,
+       .prepare = sis_pcm_capture_prepare,
+       .trigger = sis_pcm_trigger,
+       .pointer = sis_pcm_pointer,
+};
+
+static int __devinit sis_pcm_create(struct sis7019 *sis)
+{
+       struct snd_pcm *pcm;
+       int rc;
+
+       /* We have 64 voices, and the driver currently records from
+        * only one channel, though that could change in the future.
+        */
+       rc = snd_pcm_new(sis->card, "SiS7019", 0, 64, 1, &pcm);
+       if (rc)
+               return rc;
+
+       pcm->private_data = sis;
+       strcpy(pcm->name, "SiS7019");
+       sis->pcm = pcm;
+
+       snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &sis_playback_ops);
+       snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, &sis_capture_ops);
+
+       /* Try to preallocate some memory, but it's not the end of the
+        * world if this fails.
+        */
+       snd_pcm_lib_preallocate_pages_for_all(pcm, SNDRV_DMA_TYPE_DEV,
+                               snd_dma_pci_data(sis->pci), 64*1024, 128*1024);
+
+       return 0;
+}
+
+static unsigned short sis_ac97_rw(struct sis7019 *sis, int codec, u32 cmd)
+{
+       unsigned long io = sis->ioport;
+       unsigned short val = 0xffff;
+       u16 status;
+       u16 rdy;
+       int count;
+       const static u16 codec_ready[3] = {
+               SIS_AC97_STATUS_CODEC_READY,
+               SIS_AC97_STATUS_CODEC2_READY,
+               SIS_AC97_STATUS_CODEC3_READY,
+       };
+
+       rdy = codec_ready[codec];
+
+
+       /* Get the AC97 semaphore -- software first, so we don't spin
+        * pounding out IO reads on the hardware semaphore...
+        */
+       mutex_lock(&sis->ac97_mutex);
+
+       count = 0xffff;
+       while ((inw(io + SIS_AC97_SEMA) & SIS_AC97_SEMA_BUSY) && --count)
+               udelay(1);
+
+       if (!count)
+               goto timeout;
+
+       /* ... and wait for any outstanding commands to complete ...
+        */
+       count = 0xffff;
+       do {
+               status = inw(io + SIS_AC97_STATUS);
+               if ((status & rdy) && !(status & SIS_AC97_STATUS_BUSY))
+                       break;
+
+               udelay(1);
+       } while (--count);
+
+       if (!count)
+               goto timeout_sema;
+
+       /* ... before sending our command and waiting for it to finish ...
+        */
+       outl(cmd, io + SIS_AC97_CMD);
+       udelay(10);
+
+       count = 0xffff;
+       while ((inw(io + SIS_AC97_STATUS) & SIS_AC97_STATUS_BUSY) && --count)
+               udelay(1);
+
+       /* ... and reading the results (if any).
+        */
+       val = inl(io + SIS_AC97_CMD) >> 16;
+
+timeout_sema:
+       outl(SIS_AC97_SEMA_RELEASE, io + SIS_AC97_SEMA);
+timeout:
+       mutex_unlock(&sis->ac97_mutex);
+
+       if (!count) {
+               printk(KERN_ERR "sis7019: ac97 codec %d timeout cmd 0x%08x\n",
+                                       codec, cmd);
+       }
+
+       return val;
+}
+
+static void sis_ac97_write(struct snd_ac97 *ac97, unsigned short reg,
+                               unsigned short val)
+{
+       const static u32 cmd[3] = {
+               SIS_AC97_CMD_CODEC_WRITE,
+               SIS_AC97_CMD_CODEC2_WRITE,
+               SIS_AC97_CMD_CODEC3_WRITE,
+       };
+       sis_ac97_rw(ac97->private_data, ac97->num,
+                       (val << 16) | (reg << 8) | cmd[ac97->num]);
+}
+
+static unsigned short sis_ac97_read(struct snd_ac97 *ac97, unsigned short reg)
+{
+       const static u32 cmd[3] = {
+               SIS_AC97_CMD_CODEC_READ,
+               SIS_AC97_CMD_CODEC2_READ,
+               SIS_AC97_CMD_CODEC3_READ,
+       };
+       return sis_ac97_rw(ac97->private_data, ac97->num,
+                                       (reg << 8) | cmd[ac97->num]);
+}
+
+static int __devinit sis_mixer_create(struct sis7019 *sis)
+{
+       struct snd_ac97_bus *bus;
+       struct snd_ac97_template ac97;
+       static struct snd_ac97_bus_ops ops = {
+               .write = sis_ac97_write,
+               .read = sis_ac97_read,
+       };
+       int rc;
+
+       memset(&ac97, 0, sizeof(ac97));
+       ac97.private_data = sis;
+
+       rc = snd_ac97_bus(sis->card, 0, &ops, NULL, &bus);
+       if (!rc && sis->codecs_present & SIS_PRIMARY_CODEC_PRESENT)
+               rc = snd_ac97_mixer(bus, &ac97, &sis->ac97[0]);
+       ac97.num = 1;
+       if (!rc && (sis->codecs_present & SIS_SECONDARY_CODEC_PRESENT))
+               rc = snd_ac97_mixer(bus, &ac97, &sis->ac97[1]);
+       ac97.num = 2;
+       if (!rc && (sis->codecs_present & SIS_TERTIARY_CODEC_PRESENT))
+               rc = snd_ac97_mixer(bus, &ac97, &sis->ac97[2]);
+
+       /* If we return an error here, then snd_card_free() should
+        * free up any ac97 codecs that got created, as well as the bus.
+        */
+       return rc;
+}
+
+static void sis_free_suspend(struct sis7019 *sis)
+{
+       int i;
+
+       for (i = 0; i < SIS_SUSPEND_PAGES; i++)
+               kfree(sis->suspend_state[i]);
+}
+
+static int sis_chip_free(struct sis7019 *sis)
+{
+       /* Reset the chip, and disable all interrputs.
+        */
+       outl(SIS_GCR_SOFTWARE_RESET, sis->ioport + SIS_GCR);
+       udelay(10);
+       outl(0, sis->ioport + SIS_GCR);
+       outl(0, sis->ioport + SIS_GIER);
+
+       /* Now, free everything we allocated.
+        */
+       if (sis->irq >= 0)
+               free_irq(sis->irq, sis);
+
+       if (sis->ioaddr)
+               iounmap(sis->ioaddr);
+
+       pci_release_regions(sis->pci);
+       pci_disable_device(sis->pci);
+
+       sis_free_suspend(sis);
+       return 0;
+}
+
+static int sis_dev_free(struct snd_device *dev)
+{
+       struct sis7019 *sis = dev->device_data;
+       return sis_chip_free(sis);
+}
+
+static int sis_chip_init(struct sis7019 *sis)
+{
+       unsigned long io = sis->ioport;
+       void __iomem *ioaddr = sis->ioaddr;
+       u16 status;
+       int count;
+       int i;
+
+       /* Reset the audio controller
+        */
+       outl(SIS_GCR_SOFTWARE_RESET, io + SIS_GCR);
+       udelay(10);
+       outl(0, io + SIS_GCR);
+
+       /* Get the AC-link semaphore, and reset the codecs
+        */
+       count = 0xffff;
+       while ((inw(io + SIS_AC97_SEMA) & SIS_AC97_SEMA_BUSY) && --count)
+               udelay(1);
+
+       if (!count)
+               return -EIO;
+
+       outl(SIS_AC97_CMD_CODEC_COLD_RESET, io + SIS_AC97_CMD);
+       udelay(10);
+
+       count = 0xffff;
+       while ((inw(io + SIS_AC97_STATUS) & SIS_AC97_STATUS_BUSY) && --count)
+               udelay(1);
+
+       /* Now that we've finished the reset, find out what's attached.
+        */
+       status = inl(io + SIS_AC97_STATUS);
+       if (status & SIS_AC97_STATUS_CODEC_READY)
+               sis->codecs_present |= SIS_PRIMARY_CODEC_PRESENT;
+       if (status & SIS_AC97_STATUS_CODEC2_READY)
+               sis->codecs_present |= SIS_SECONDARY_CODEC_PRESENT;
+       if (status & SIS_AC97_STATUS_CODEC3_READY)
+               sis->codecs_present |= SIS_TERTIARY_CODEC_PRESENT;
+
+       /* All done, let go of the semaphore, and check for errors
+        */
+       outl(SIS_AC97_SEMA_RELEASE, io + SIS_AC97_SEMA);
+       if (!sis->codecs_present || !count)
+               return -EIO;
+
+       /* Let the hardware know that the audio driver is alive,
+        * and enable PCM slots on the AC-link for L/R playback (3 & 4) and
+        * record channels. We're going to want to use Variable Rate Audio
+        * for recording, to avoid needlessly resampling from 48kHZ.
+        */
+       outl(SIS_AC97_CONF_AUDIO_ALIVE, io + SIS_AC97_CONF);
+       outl(SIS_AC97_CONF_AUDIO_ALIVE | SIS_AC97_CONF_PCM_LR_ENABLE |
+               SIS_AC97_CONF_PCM_CAP_MIC_ENABLE |
+               SIS_AC97_CONF_PCM_CAP_LR_ENABLE |
+               SIS_AC97_CONF_CODEC_VRA_ENABLE, io + SIS_AC97_CONF);
+
+       /* All AC97 PCM slots should be sourced from sub-mixer 0.
+        */
+       outl(0, io + SIS_AC97_PSR);
+
+       /* There is only one valid DMA setup for a PCI environment.
+        */
+       outl(SIS_DMA_CSR_PCI_SETTINGS, io + SIS_DMA_CSR);
+
+       /* Reset the syncronization groups for all of the channels
+        * to be asyncronous. If we start doing SPDIF or 5.1 sound, etc.
+        * we'll need to change how we handle these. Until then, we just
+        * assign sub-mixer 0 to all playback channels, and avoid any
+        * attenuation on the audio.
+        */
+       outl(0, io + SIS_PLAY_SYNC_GROUP_A);
+       outl(0, io + SIS_PLAY_SYNC_GROUP_B);
+       outl(0, io + SIS_PLAY_SYNC_GROUP_C);
+       outl(0, io + SIS_PLAY_SYNC_GROUP_D);
+       outl(0, io + SIS_MIXER_SYNC_GROUP);
+
+       for (i = 0; i < 64; i++) {
+               writel(i, SIS_MIXER_START_ADDR(ioaddr, i));
+               writel(SIS_MIXER_RIGHT_NO_ATTEN | SIS_MIXER_LEFT_NO_ATTEN |
+                               SIS_MIXER_DEST_0, SIS_MIXER_ADDR(ioaddr, i));
+       }
+
+       /* Don't attenuate any audio set for the wave amplifier.
+        *
+        * FIXME: Maximum attenuation is set for the music amp, which will
+        * need to change if we start using the synth engine.
+        */
+       outl(0xffff0000, io + SIS_WEVCR);
+
+       /* Ensure that the wave engine is in normal operating mode.
+        */
+       outl(0, io + SIS_WECCR);
+
+       /* Go ahead and enable the DMA interrupts. They won't go live
+        * until we start a channel.
+        */
+       outl(SIS_GIER_AUDIO_PLAY_DMA_IRQ_ENABLE |
+               SIS_GIER_AUDIO_RECORD_DMA_IRQ_ENABLE, io + SIS_GIER);
+
+       return 0;
+}
+
+#ifdef CONFIG_PM
+static int sis_suspend(struct pci_dev *pci, pm_message_t state)
+{
+       struct snd_card *card = pci_get_drvdata(pci);
+       struct sis7019 *sis = card->private_data;
+       void __iomem *ioaddr = sis->ioaddr;
+       int i;
+
+       snd_power_change_state(card, SNDRV_CTL_POWER_D3hot);
+       snd_pcm_suspend_all(sis->pcm);
+       if (sis->codecs_present & SIS_PRIMARY_CODEC_PRESENT)
+               snd_ac97_suspend(sis->ac97[0]);
+       if (sis->codecs_present & SIS_SECONDARY_CODEC_PRESENT)
+               snd_ac97_suspend(sis->ac97[1]);
+       if (sis->codecs_present & SIS_TERTIARY_CODEC_PRESENT)
+               snd_ac97_suspend(sis->ac97[2]);
+
+       /* snd_pcm_suspend_all() stopped all channels, so we're quiescent.
+        */
+       if (sis->irq >= 0) {
+               synchronize_irq(sis->irq);
+               free_irq(sis->irq, sis);
+               sis->irq = -1;
+       }
+
+       /* Save the internal state away
+        */
+       for (i = 0; i < 4; i++) {
+               memcpy_fromio(sis->suspend_state[i], ioaddr, 4096);
+               ioaddr += 4096;
+       }
+
+       pci_disable_device(pci);
+       pci_save_state(pci);
+       pci_set_power_state(pci, pci_choose_state(pci, state));
+       return 0;
+}
+
+static int sis_resume(struct pci_dev *pci)
+{
+       struct snd_card *card = pci_get_drvdata(pci);
+       struct sis7019 *sis = card->private_data;
+       void __iomem *ioaddr = sis->ioaddr;
+       int i;
+
+       pci_set_power_state(pci, PCI_D0);
+       pci_restore_state(pci);
+
+       if (pci_enable_device(pci) < 0) {
+               printk(KERN_ERR "sis7019: unable to re-enable device\n");
+               goto error;
+       }
+
+       if (sis_chip_init(sis)) {
+               printk(KERN_ERR "sis7019: unable to re-init controller\n");
+               goto error;
+       }
+
+       if (request_irq(pci->irq, sis_interrupt, IRQF_DISABLED|IRQF_SHARED,
+                               card->shortname, sis)) {
+               printk(KERN_ERR "sis7019: unable to regain IRQ %d\n", pci->irq);
+               goto error;
+       }
+
+       /* Restore saved state, then clear out the page we use for the
+        * silence buffer.
+        */
+       for (i = 0; i < 4; i++) {
+               memcpy_toio(ioaddr, sis->suspend_state[i], 4096);
+               ioaddr += 4096;
+       }
+
+       memset(sis->suspend_state[0], 0, 4096);
+
+       sis->irq = pci->irq;
+       pci_set_master(pci);
+
+       if (sis->codecs_present & SIS_PRIMARY_CODEC_PRESENT)
+               snd_ac97_resume(sis->ac97[0]);
+       if (sis->codecs_present & SIS_SECONDARY_CODEC_PRESENT)
+               snd_ac97_resume(sis->ac97[1]);
+       if (sis->codecs_present & SIS_TERTIARY_CODEC_PRESENT)
+               snd_ac97_resume(sis->ac97[2]);
+
+       snd_power_change_state(card, SNDRV_CTL_POWER_D0);
+       return 0;
+
+error:
+       snd_card_disconnect(card);
+       return -EIO;
+}
+#endif /* CONFIG_PM */
+
+static int sis_alloc_suspend(struct sis7019 *sis)
+{
+       int i;
+
+       /* We need 16K to store the internal wave engine state during a
+        * suspend, but we don't need it to be contiguous, so play nice
+        * with the memory system. We'll also use this area for a silence
+        * buffer.
+        */
+       for (i = 0; i < SIS_SUSPEND_PAGES; i++) {
+               sis->suspend_state[i] = kmalloc(4096, GFP_KERNEL);
+               if (!sis->suspend_state[i])
+                       return -ENOMEM;
+       }
+       memset(sis->suspend_state[0], 0, 4096);
+
+       return 0;
+}
+
+static int __devinit sis_chip_create(struct snd_card *card,
+                                       struct pci_dev *pci)
+{
+       struct sis7019 *sis = card->private_data;
+       struct voice *voice;
+       static struct snd_device_ops ops = {
+               .dev_free = sis_dev_free,
+       };
+       int rc;
+       int i;
+
+       rc = pci_enable_device(pci);
+       if (rc)
+               goto error_out;
+
+       if (pci_set_dma_mask(pci, DMA_30BIT_MASK) < 0) {
+               printk(KERN_ERR "sis7019: architecture does not support "
+                                       "30-bit PCI busmaster DMA");
+               goto error_out_enabled;
+       }
+
+       memset(sis, 0, sizeof(*sis));
+       mutex_init(&sis->ac97_mutex);
+       spin_lock_init(&sis->voice_lock);
+       sis->card = card;
+       sis->pci = pci;
+       sis->irq = -1;
+       sis->ioport = pci_resource_start(pci, 0);
+
+       rc = pci_request_regions(pci, "SiS7019");
+       if (rc) {
+               printk(KERN_ERR "sis7019: unable request regions\n");
+               goto error_out_enabled;
+       }
+
+       rc = -EIO;
+       sis->ioaddr = ioremap_nocache(pci_resource_start(pci, 1), 0x4000);
+       if (!sis->ioaddr) {
+               printk(KERN_ERR "sis7019: unable to remap MMIO, aborting\n");
+               goto error_out_cleanup;
+       }
+
+       rc = sis_alloc_suspend(sis);
+       if (rc < 0) {
+               printk(KERN_ERR "sis7019: unable to allocate state storage\n");
+               goto error_out_cleanup;
+       }
+
+       rc = sis_chip_init(sis);
+       if (rc)
+               goto error_out_cleanup;
+
+       if (request_irq(pci->irq, sis_interrupt, IRQF_DISABLED|IRQF_SHARED,
+                               card->shortname, sis)) {
+               printk(KERN_ERR "unable to allocate irq %d\n", sis->irq);
+               goto error_out_cleanup;
+       }
+
+       sis->irq = pci->irq;
+       pci_set_master(pci);
+
+       for (i = 0; i < 64; i++) {
+               voice = &sis->voices[i];
+               voice->num = i;
+               voice->ctrl_base = SIS_PLAY_DMA_ADDR(sis->ioaddr, i);
+               voice->wave_base = SIS_WAVE_ADDR(sis->ioaddr, i);
+       }
+
+       voice = &sis->capture_voice;
+       voice->flags = VOICE_CAPTURE;
+       voice->num = SIS_CAPTURE_CHAN_AC97_PCM_IN;
+       voice->ctrl_base = SIS_CAPTURE_DMA_ADDR(sis->ioaddr, voice->num);
+
+       rc = snd_device_new(card, SNDRV_DEV_LOWLEVEL, sis, &ops);
+       if (rc)
+               goto error_out_cleanup;
+
+       snd_card_set_dev(card, &pci->dev);
+
+       return 0;
+
+error_out_cleanup:
+       sis_chip_free(sis);
+
+error_out_enabled:
+       pci_disable_device(pci);
+
+error_out:
+       return rc;
+}
+
+static int __devinit snd_sis7019_probe(struct pci_dev *pci,
+                                       const struct pci_device_id *pci_id)
+{
+       struct snd_card *card;
+       struct sis7019 *sis;
+       int rc;
+
+       rc = -ENOENT;
+       if (!enable)
+               goto error_out;
+
+       rc = -ENOMEM;
+       card = snd_card_new(index, id, THIS_MODULE, sizeof(*sis));
+       if (!card)
+               goto error_out;
+
+       strcpy(card->driver, "SiS7019");
+       strcpy(card->shortname, "SiS7019");
+       rc = sis_chip_create(card, pci);
+       if (rc)
+               goto card_error_out;
+
+       sis = card->private_data;
+
+       rc = sis_mixer_create(sis);
+       if (rc)
+               goto card_error_out;
+
+       rc = sis_pcm_create(sis);
+       if (rc)
+               goto card_error_out;
+
+       snprintf(card->longname, sizeof(card->longname),
+                       "%s Audio Accelerator with %s at 0x%lx, irq %d",
+                       card->shortname, snd_ac97_get_short_name(sis->ac97[0]),
+                       sis->ioport, sis->irq);
+
+       rc = snd_card_register(card);
+       if (rc)
+               goto card_error_out;
+
+       pci_set_drvdata(pci, card);
+       return 0;
+
+card_error_out:
+       snd_card_free(card);
+
+error_out:
+       return rc;
+}
+
+static void __devexit snd_sis7019_remove(struct pci_dev *pci)
+{
+       snd_card_free(pci_get_drvdata(pci));
+       pci_set_drvdata(pci, NULL);
+}
+
+static struct pci_driver sis7019_driver = {
+       .name = "SiS7019",
+       .id_table = snd_sis7019_ids,
+       .probe = snd_sis7019_probe,
+       .remove = __devexit_p(snd_sis7019_remove),
+
+#ifdef CONFIG_PM
+       .suspend = sis_suspend,
+       .resume = sis_resume,
+#endif
+};
+
+static int __init sis7019_init(void)
+{
+       return pci_register_driver(&sis7019_driver);
+}
+
+static void __exit sis7019_exit(void)
+{
+       pci_unregister_driver(&sis7019_driver);
+}
+
+module_init(sis7019_init);
+module_exit(sis7019_exit);
diff --git a/sound/pci/sis7019.h b/sound/pci/sis7019.h
new file mode 100644 (file)
index 0000000..013b673
--- /dev/null
@@ -0,0 +1,342 @@
+#ifndef __sis7019_h__
+#define __sis7019_h__
+
+/*
+ *  Definitions for SiS7019 Audio Accelerator
+ *
+ *  Copyright (C) 2004-2007, David Dillow
+ *  Written by David Dillow <dave@thedillows.org>
+ *  Inspired by the Trident 4D-WaveDX/NX driver.
+ *
+ *  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 as published by
+ *  the Free Software Foundation, version 2.
+ *
+ *  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
+ */
+
+
+/* General Control Register */
+#define SIS_GCR                0x00
+#define                SIS_GCR_MACRO_POWER_DOWN                0x80000000
+#define                SIS_GCR_MODEM_ENABLE                    0x00010000
+#define                SIS_GCR_SOFTWARE_RESET                  0x00000001
+
+/* General Interrupt Enable Register */
+#define SIS_GIER       0x04
+#define                SIS_GIER_MODEM_TIMER_IRQ_ENABLE         0x00100000
+#define                SIS_GIER_MODEM_RX_DMA_IRQ_ENABLE        0x00080000
+#define                SIS_GIER_MODEM_TX_DMA_IRQ_ENABLE        0x00040000
+#define                SIS_GIER_AC97_GPIO1_IRQ_ENABLE          0x00020000
+#define                SIS_GIER_AC97_GPIO0_IRQ_ENABLE          0x00010000
+#define                SIS_GIER_AC97_SAMPLE_TIMER_IRQ_ENABLE   0x00000010
+#define                SIS_GIER_AUDIO_GLOBAL_TIMER_IRQ_ENABLE  0x00000008
+#define                SIS_GIER_AUDIO_RECORD_DMA_IRQ_ENABLE    0x00000004
+#define                SIS_GIER_AUDIO_PLAY_DMA_IRQ_ENABLE      0x00000002
+#define                SIS_GIER_AUDIO_WAVE_ENGINE_IRQ_ENABLE   0x00000001
+
+/* General Interrupt Status Register */
+#define SIS_GISR       0x08
+#define                SIS_GISR_MODEM_TIMER_IRQ_STATUS         0x00100000
+#define                SIS_GISR_MODEM_RX_DMA_IRQ_STATUS        0x00080000
+#define                SIS_GISR_MODEM_TX_DMA_IRQ_STATUS        0x00040000
+#define                SIS_GISR_AC97_GPIO1_IRQ_STATUS          0x00020000
+#define                SIS_GISR_AC97_GPIO0_IRQ_STATUS          0x00010000
+#define                SIS_GISR_AC97_SAMPLE_TIMER_IRQ_STATUS   0x00000010
+#define                SIS_GISR_AUDIO_GLOBAL_TIMER_IRQ_STATUS  0x00000008
+#define                SIS_GISR_AUDIO_RECORD_DMA_IRQ_STATUS    0x00000004
+#define                SIS_GISR_AUDIO_PLAY_DMA_IRQ_STATUS      0x00000002
+#define                SIS_GISR_AUDIO_WAVE_ENGINE_IRQ_STATUS   0x00000001
+
+/* DMA Control Register */
+#define SIS_DMA_CSR    0x10
+#define                SIS_DMA_CSR_PCI_SETTINGS                0x0000001d
+#define                SIS_DMA_CSR_CONCURRENT_ENABLE           0x00000200
+#define                SIS_DMA_CSR_PIPELINE_ENABLE             0x00000100
+#define                SIS_DMA_CSR_RX_DRAIN_ENABLE             0x00000010
+#define                SIS_DMA_CSR_RX_FILL_ENABLE              0x00000008
+#define                SIS_DMA_CSR_TX_DRAIN_ENABLE             0x00000004
+#define                SIS_DMA_CSR_TX_LOWPRI_FILL_ENABLE       0x00000002
+#define                SIS_DMA_CSR_TX_HIPRI_FILL_ENABLE        0x00000001
+
+/* Playback Channel Start Registers */
+#define SIS_PLAY_START_A_REG   0x14
+#define SIS_PLAY_START_B_REG   0x18
+
+/* Playback Channel Stop Registers */
+#define SIS_PLAY_STOP_A_REG    0x1c
+#define SIS_PLAY_STOP_B_REG    0x20
+
+/* Recording Channel Start Register */
+#define SIS_RECORD_START_REG   0x24
+
+/* Recording Channel Stop Register */
+#define SIS_RECORD_STOP_REG    0x28
+
+/* Playback Interrupt Status Registers */
+#define SIS_PISR_A     0x2c
+#define SIS_PISR_B     0x30
+
+/* Recording Interrupt Status Register */
+#define SIS_RISR       0x34
+
+/* AC97 AC-link Playback Source Register */
+#define SIS_AC97_PSR   0x40
+#define                SIS_AC97_PSR_MODEM_HEADSET_SRC_MIXER    0x0f000000
+#define                SIS_AC97_PSR_MODEM_LINE2_SRC_MIXER      0x00f00000
+#define                SIS_AC97_PSR_MODEM_LINE1_SRC_MIXER      0x000f0000
+#define                SIS_AC97_PSR_PCM_LFR_SRC_MIXER          0x0000f000
+#define                SIS_AC97_PSR_PCM_SURROUND_SRC_MIXER     0x00000f00
+#define                SIS_AC97_PSR_PCM_CENTER_SRC_MIXER       0x000000f0
+#define                SIS_AC97_PSR_PCM_LR_SRC_MIXER           0x0000000f
+
+/* AC97 AC-link Command Register */
+#define SIS_AC97_CMD   0x50
+#define        SIS_AC97_CMD_DATA_MASK                  0xffff0000
+#define                SIS_AC97_CMD_REG_MASK                   0x0000ff00
+#define                SIS_AC97_CMD_CODEC3_READ                0x0000000d
+#define                SIS_AC97_CMD_CODEC3_WRITE               0x0000000c
+#define                SIS_AC97_CMD_CODEC2_READ                0x0000000b
+#define                SIS_AC97_CMD_CODEC2_WRITE               0x0000000a
+#define                SIS_AC97_CMD_CODEC_READ                 0x00000009
+#define                SIS_AC97_CMD_CODEC_WRITE                0x00000008
+#define                SIS_AC97_CMD_CODEC_WARM_RESET           0x00000005
+#define                SIS_AC97_CMD_CODEC_COLD_RESET           0x00000004
+#define                SIS_AC97_CMD_DONE                       0x00000000
+
+/* AC97 AC-link Semaphore Register */
+#define SIS_AC97_SEMA  0x54
+#define                SIS_AC97_SEMA_BUSY                      0x00000001
+#define                SIS_AC97_SEMA_RELEASE                   0x00000000
+
+/* AC97 AC-link Status Register */
+#define SIS_AC97_STATUS        0x58
+#define                SIS_AC97_STATUS_AUDIO_D2_INACT_SECS     0x03f00000
+#define                SIS_AC97_STATUS_MODEM_ALIVE             0x00002000
+#define                SIS_AC97_STATUS_AUDIO_ALIVE             0x00001000
+#define                SIS_AC97_STATUS_CODEC3_READY            0x00000400
+#define                SIS_AC97_STATUS_CODEC2_READY            0x00000200
+#define                SIS_AC97_STATUS_CODEC_READY             0x00000100
+#define                SIS_AC97_STATUS_WARM_RESET              0x00000080
+#define                SIS_AC97_STATUS_COLD_RESET              0x00000040
+#define                SIS_AC97_STATUS_POWERED_DOWN            0x00000020
+#define                SIS_AC97_STATUS_NORMAL                  0x00000010
+#define                SIS_AC97_STATUS_READ_EXPIRED            0x00000004
+#define                SIS_AC97_STATUS_SEMAPHORE               0x00000002
+#define                SIS_AC97_STATUS_BUSY                    0x00000001
+
+/* AC97 AC-link Audio Configuration Register */
+#define SIS_AC97_CONF  0x5c
+#define                SIS_AC97_CONF_AUDIO_ALIVE               0x80000000
+#define                SIS_AC97_CONF_WARM_RESET_ENABLE         0x40000000
+#define                SIS_AC97_CONF_PR6_ENABLE                0x20000000
+#define                SIS_AC97_CONF_PR5_ENABLE                0x10000000
+#define                SIS_AC97_CONF_PR4_ENABLE                0x08000000
+#define                SIS_AC97_CONF_PR3_ENABLE                0x04000000
+#define                SIS_AC97_CONF_PR2_PR7_ENABLE            0x02000000
+#define                SIS_AC97_CONF_PR0_PR1_ENABLE            0x01000000
+#define                SIS_AC97_CONF_AUTO_PM_ENABLE            0x00800000
+#define                SIS_AC97_CONF_PCM_LFE_ENABLE            0x00080000
+#define                SIS_AC97_CONF_PCM_SURROUND_ENABLE       0x00040000
+#define                SIS_AC97_CONF_PCM_CENTER_ENABLE         0x00020000
+#define                SIS_AC97_CONF_PCM_LR_ENABLE             0x00010000
+#define                SIS_AC97_CONF_PCM_CAP_MIC_ENABLE        0x00002000
+#define                SIS_AC97_CONF_PCM_CAP_LR_ENABLE         0x00001000
+#define                SIS_AC97_CONF_PCM_CAP_MIC_FROM_CODEC3   0x00000200
+#define                SIS_AC97_CONF_PCM_CAP_LR_FROM_CODEC3    0x00000100
+#define                SIS_AC97_CONF_CODEC3_PM_VRM             0x00000080
+#define                SIS_AC97_CONF_CODEC_PM_VRM              0x00000040
+#define                SIS_AC97_CONF_CODEC3_VRA_ENABLE         0x00000020
+#define                SIS_AC97_CONF_CODEC_VRA_ENABLE          0x00000010
+#define                SIS_AC97_CONF_CODEC3_PM_EAC             0x00000008
+#define                SIS_AC97_CONF_CODEC_PM_EAC              0x00000004
+#define                SIS_AC97_CONF_CODEC3_EXISTS             0x00000002
+#define                SIS_AC97_CONF_CODEC_EXISTS              0x00000001
+
+/* Playback Channel Sync Group registers */
+#define SIS_PLAY_SYNC_GROUP_A  0x80
+#define SIS_PLAY_SYNC_GROUP_B  0x84
+#define SIS_PLAY_SYNC_GROUP_C  0x88
+#define SIS_PLAY_SYNC_GROUP_D  0x8c
+#define SIS_MIXER_SYNC_GROUP   0x90
+
+/* Wave Engine Config and Control Register */
+#define SIS_WECCR      0xa0
+#define                SIS_WECCR_TESTMODE_MASK                 0x00300000
+#define                        SIS_WECCR_TESTMODE_NORMAL               0x00000000
+#define                        SIS_WECCR_TESTMODE_BYPASS_NSO_ALPHA     0x00100000
+#define                        SIS_WECCR_TESTMODE_BYPASS_FC            0x00200000
+#define                        SIS_WECCR_TESTMODE_BYPASS_WOL           0x00300000
+#define                SIS_WECCR_RESONANCE_DELAY_MASK          0x00060000
+#define                        SIS_WECCR_RESONANCE_DELAY_NONE          0x00000000
+#define                        SIS_WECCR_RESONANCE_DELAY_FC_1F00       0x00020000
+#define                        SIS_WECCR_RESONANCE_DELAY_FC_1E00       0x00040000
+#define                        SIS_WECCR_RESONANCE_DELAY_FC_1C00       0x00060000
+#define                SIS_WECCR_IGNORE_CHANNEL_PARMS          0x00010000
+#define                SIS_WECCR_COMMAND_CHANNEL_ID_MASK       0x0003ff00
+#define                SIS_WECCR_COMMAND_MASK                  0x00000007
+#define                        SIS_WECCR_COMMAND_NONE                  0x00000000
+#define                        SIS_WECCR_COMMAND_DONE                  0x00000000
+#define                        SIS_WECCR_COMMAND_PAUSE                 0x00000001
+#define                        SIS_WECCR_COMMAND_TOGGLE_VEG            0x00000002
+#define                        SIS_WECCR_COMMAND_TOGGLE_MEG            0x00000003
+#define                        SIS_WECCR_COMMAND_TOGGLE_VEG_MEG        0x00000004
+
+/* Wave Engine Volume Control Register */
+#define SIS_WEVCR      0xa4
+#define                SIS_WEVCR_LEFT_MUSIC_ATTENUATION_MASK   0xff000000
+#define                SIS_WEVCR_RIGHT_MUSIC_ATTENUATION_MASK  0x00ff0000
+#define                SIS_WEVCR_LEFT_WAVE_ATTENUATION_MASK    0x0000ff00
+#define                SIS_WEVCR_RIGHT_WAVE_ATTENUATION_MASK   0x000000ff
+
+/* Wave Engine Interrupt Status Registers */
+#define SIS_WEISR_A    0xa8
+#define SIS_WEISR_B    0xac
+
+
+/* Playback DMA parameters (paramter RAM) */
+#define SIS_PLAY_DMA_OFFSET    0x0000
+#define SIS_PLAY_DMA_SIZE      0x10
+#define SIS_PLAY_DMA_ADDR(addr, num) \
+       ((num * SIS_PLAY_DMA_SIZE) + (addr) + SIS_PLAY_DMA_OFFSET)
+
+#define SIS_PLAY_DMA_FORMAT_CSO        0x00
+#define                SIS_PLAY_DMA_FORMAT_UNSIGNED    0x00080000
+#define                SIS_PLAY_DMA_FORMAT_8BIT        0x00040000
+#define                SIS_PLAY_DMA_FORMAT_MONO        0x00020000
+#define                SIS_PLAY_DMA_CSO_MASK           0x0000ffff
+#define SIS_PLAY_DMA_BASE      0x04
+#define SIS_PLAY_DMA_CONTROL   0x08
+#define                SIS_PLAY_DMA_STOP_AT_SSO        0x04000000
+#define                SIS_PLAY_DMA_RELEASE            0x02000000
+#define                SIS_PLAY_DMA_LOOP               0x01000000
+#define                SIS_PLAY_DMA_INTR_AT_SSO        0x00080000
+#define                SIS_PLAY_DMA_INTR_AT_ESO        0x00040000
+#define                SIS_PLAY_DMA_INTR_AT_LEO        0x00020000
+#define                SIS_PLAY_DMA_INTR_AT_MLP        0x00010000
+#define                SIS_PLAY_DMA_LEO_MASK           0x0000ffff
+#define SIS_PLAY_DMA_SSO_ESO   0x0c
+#define                SIS_PLAY_DMA_SSO_MASK           0xffff0000
+#define                SIS_PLAY_DMA_ESO_MASK           0x0000ffff
+
+/* Capture DMA parameters (paramter RAM) */
+#define SIS_CAPTURE_DMA_OFFSET 0x0800
+#define SIS_CAPTURE_DMA_SIZE   0x10
+#define SIS_CAPTURE_DMA_ADDR(addr, num) \
+       ((num * SIS_CAPTURE_DMA_SIZE) + (addr) + SIS_CAPTURE_DMA_OFFSET)
+
+#define        SIS_CAPTURE_CHAN_MIXER_ROUTE_BACK_0     0
+#define        SIS_CAPTURE_CHAN_MIXER_ROUTE_BACK_1     1
+#define        SIS_CAPTURE_CHAN_MIXER_ROUTE_BACK_2     2
+#define        SIS_CAPTURE_CHAN_MIXER_ROUTE_BACK_3     3
+#define        SIS_CAPTURE_CHAN_MIXER_ROUTE_BACK_4     4
+#define        SIS_CAPTURE_CHAN_MIXER_ROUTE_BACK_5     5
+#define        SIS_CAPTURE_CHAN_MIXER_ROUTE_BACK_6     6
+#define        SIS_CAPTURE_CHAN_MIXER_ROUTE_BACK_7     7
+#define        SIS_CAPTURE_CHAN_MIXER_ROUTE_BACK_8     8
+#define        SIS_CAPTURE_CHAN_MIXER_ROUTE_BACK_9     9
+#define        SIS_CAPTURE_CHAN_MIXER_ROUTE_BACK_10    10
+#define        SIS_CAPTURE_CHAN_MIXER_ROUTE_BACK_11    11
+#define        SIS_CAPTURE_CHAN_MIXER_ROUTE_BACK_12    12
+#define        SIS_CAPTURE_CHAN_MIXER_ROUTE_BACK_13    13
+#define        SIS_CAPTURE_CHAN_MIXER_ROUTE_BACK_14    14
+#define        SIS_CAPTURE_CHAN_MIXER_ROUTE_BACK_15    15
+#define        SIS_CAPTURE_CHAN_AC97_PCM_IN            16
+#define        SIS_CAPTURE_CHAN_AC97_MIC_IN            17
+#define        SIS_CAPTURE_CHAN_AC97_LINE1_IN          18
+#define        SIS_CAPTURE_CHAN_AC97_LINE2_IN          19
+#define        SIS_CAPTURE_CHAN_AC97_HANDSE_IN         20
+
+#define SIS_CAPTURE_DMA_FORMAT_CSO     0x00
+#define                SIS_CAPTURE_DMA_MONO_MODE_MASK  0xc0000000
+#define                SIS_CAPTURE_DMA_MONO_MODE_AVG   0x00000000
+#define                SIS_CAPTURE_DMA_MONO_MODE_LEFT  0x40000000
+#define                SIS_CAPTURE_DMA_MONO_MODE_RIGHT 0x80000000
+#define                SIS_CAPTURE_DMA_FORMAT_UNSIGNED 0x00080000
+#define                SIS_CAPTURE_DMA_FORMAT_8BIT     0x00040000
+#define                SIS_CAPTURE_DMA_FORMAT_MONO     0x00020000
+#define                SIS_CAPTURE_DMA_CSO_MASK                0x0000ffff
+#define SIS_CAPTURE_DMA_BASE           0x04
+#define SIS_CAPTURE_DMA_CONTROL                0x08
+#define                SIS_CAPTURE_DMA_STOP_AT_SSO     0x04000000
+#define                SIS_CAPTURE_DMA_RELEASE         0x02000000
+#define                SIS_CAPTURE_DMA_LOOP            0x01000000
+#define                SIS_CAPTURE_DMA_INTR_AT_LEO     0x00020000
+#define                SIS_CAPTURE_DMA_INTR_AT_MLP     0x00010000
+#define                SIS_CAPTURE_DMA_LEO_MASK                0x0000ffff
+#define SIS_CAPTURE_DMA_RESERVED       0x0c
+
+
+/* Mixer routing list start pointer (parameter RAM) */
+#define SIS_MIXER_START_OFFSET 0x1000
+#define SIS_MIXER_START_SIZE   0x04
+#define SIS_MIXER_START_ADDR(addr, num) \
+       ((num * SIS_MIXER_START_SIZE) + (addr) + SIS_MIXER_START_OFFSET)
+
+#define SIS_MIXER_START_MASK   0x0000007f
+
+/* Mixer routing table (parameter RAM) */
+#define SIS_MIXER_OFFSET       0x1400
+#define SIS_MIXER_SIZE         0x04
+#define SIS_MIXER_ADDR(addr, num) \
+       ((num * SIS_MIXER_SIZE) + (addr) + SIS_MIXER_OFFSET)
+
+#define SIS_MIXER_RIGHT_ATTENUTATION_MASK      0xff000000
+#define        SIS_MIXER_RIGHT_NO_ATTEN                0xff000000
+#define SIS_MIXER_LEFT_ATTENUTATION_MASK       0x00ff0000
+#define        SIS_MIXER_LEFT_NO_ATTEN                 0x00ff0000
+#define SIS_MIXER_NEXT_ENTRY_MASK              0x00007f00
+#define        SIS_MIXER_NEXT_ENTRY_NONE               0x00000000
+#define SIS_MIXER_DEST_MASK                    0x0000007f
+#define        SIS_MIXER_DEST_0                        0x00000020
+#define        SIS_MIXER_DEST_1                        0x00000021
+#define        SIS_MIXER_DEST_2                        0x00000022
+#define        SIS_MIXER_DEST_3                        0x00000023
+#define        SIS_MIXER_DEST_4                        0x00000024
+#define        SIS_MIXER_DEST_5                        0x00000025
+#define        SIS_MIXER_DEST_6                        0x00000026
+#define        SIS_MIXER_DEST_7                        0x00000027
+#define        SIS_MIXER_DEST_8                        0x00000028
+#define        SIS_MIXER_DEST_9                        0x00000029
+#define        SIS_MIXER_DEST_10                       0x0000002a
+#define        SIS_MIXER_DEST_11                       0x0000002b
+#define        SIS_MIXER_DEST_12                       0x0000002c
+#define        SIS_MIXER_DEST_13                       0x0000002d
+#define        SIS_MIXER_DEST_14                       0x0000002e
+#define        SIS_MIXER_DEST_15                       0x0000002f
+
+/* Wave Engine Control Parameters (parameter RAM) */
+#define SIS_WAVE_OFFSET                0x2000
+#define SIS_WAVE_SIZE          0x40
+#define SIS_WAVE_ADDR(addr, num) \
+       ((num * SIS_WAVE_SIZE) + (addr) + SIS_WAVE_OFFSET)
+
+#define SIS_WAVE_GENERAL               0x00
+#define                SIS_WAVE_GENERAL_WAVE_VOLUME                    0x80000000
+#define                SIS_WAVE_GENERAL_MUSIC_VOLUME                   0x00000000
+#define                SIS_WAVE_GENERAL_VOLUME_MASK                    0x7f000000
+#define SIS_WAVE_GENERAL_ARTICULATION  0x04
+#define                SIS_WAVE_GENERAL_ARTICULATION_DELTA_MASK        0x3fff0000
+#define SIS_WAVE_ARTICULATION          0x08
+#define SIS_WAVE_TIMER                 0x0c
+#define SIS_WAVE_GENERATOR             0x10
+#define SIS_WAVE_CHANNEL_CONTROL       0x14
+#define                SIS_WAVE_CHANNEL_CONTROL_FIRST_SAMPLE           0x80000000
+#define                SIS_WAVE_CHANNEL_CONTROL_AMP_ENABLE             0x40000000
+#define                SIS_WAVE_CHANNEL_CONTROL_FILTER_ENABLE          0x20000000
+#define                SIS_WAVE_CHANNEL_CONTROL_INTERPOLATE_ENABLE     0x10000000
+#define SIS_WAVE_LFO_EG_CONTROL                0x18
+#define SIS_WAVE_LFO_EG_CONTROL_2      0x1c
+#define SIS_WAVE_LFO_EG_CONTROL_3      0x20
+#define SIS_WAVE_LFO_EG_CONTROL_4      0x24
+
+#endif /* __sis7019_h__ */
index 44a7f5fad5734c706d93631ebe0edab1fed53bae..0d3d305b0a0b0c2805ebf26cc1a0cd004a89db4d 100644 (file)
@@ -22,7 +22,6 @@
  *
  */
 
-#include <sound/driver.h>
 #include <linux/delay.h>
 #include <linux/init.h>
 #include <linux/interrupt.h>
index 65f2c218324c164d995c0e043f6266caf94d120c..88676b50f385edb7fd0c47f502f9a0da9030ed54 100644 (file)
@@ -4,16 +4,6 @@
 #
 
 snd-trident-objs := trident.o trident_main.o trident_memory.o
-snd-trident-synth-objs := trident_synth.o
-
-#
-# this function returns:
-#   "m" - CONFIG_SND_SEQUENCER is m
-#   <empty string> - CONFIG_SND_SEQUENCER is undefined
-#   otherwise parameter #1 value
-#
-sequencer = $(if $(subst y,,$(CONFIG_SND_SEQUENCER)),$(if $(1),m),$(if $(CONFIG_SND_SEQUENCER),$(1)))
 
 # Toplevel Module Dependency
 obj-$(CONFIG_SND_TRIDENT) += snd-trident.o
-obj-$(call sequencer,$(CONFIG_SND_TRIDENT)) += snd-trident-synth.o
index 84884567df6a6586cf0fb40cfac21769664127ac..d94b16ffb38554660a8bd2647ad4d9024be2927f 100644 (file)
@@ -21,7 +21,6 @@
  *
  */
 
-#include <sound/driver.h>
 #include <linux/init.h>
 #include <linux/pci.h>
 #include <linux/time.h>
@@ -155,13 +154,6 @@ static int __devinit snd_trident_probe(struct pci_dev *pci,
                return err;
        }
 
-#if defined(CONFIG_SND_SEQUENCER) || (defined(MODULE) && defined(CONFIG_SND_SEQUENCER_MODULE))
-       if ((err = snd_trident_attach_synthesizer(trident)) < 0) {
-               snd_card_free(card);
-               return err;
-       }
-#endif
-
        snd_trident_create_gameport(trident);
 
        if ((err = snd_card_register(card)) < 0) {
index a235e034a690b36fb7b86bb14e65109f11d72af3..71138ff9b310237faedee66ea9a0445994bbb0a2 100644 (file)
@@ -27,7 +27,6 @@
  *  SiS7018 S/PDIF support by Thomas Winischhofer <thomas@winischhofer.net>
  */
 
-#include <sound/driver.h>
 #include <linux/delay.h>
 #include <linux/init.h>
 #include <linux/interrupt.h>
@@ -436,7 +435,7 @@ static void snd_trident_free_synth_channel(struct snd_trident *trident, int chan
    Description: This routine will complete and write the 5 hardware channel
                 registers to hardware.
   
-   Paramters:   trident - pointer to target device class for 4DWave.
+   Parameters:  trident - pointer to target device class for 4DWave.
                 voice - synthesizer voice structure
                 Each register field.
   
@@ -514,7 +513,7 @@ EXPORT_SYMBOL(snd_trident_write_voice_regs);
    Description: This routine will write the new CSO offset
                 register to hardware.
   
-   Paramters:   trident - pointer to target device class for 4DWave.
+   Parameters:  trident - pointer to target device class for 4DWave.
                 voice - synthesizer voice structure
                 CSO - new CSO value
   
@@ -540,7 +539,7 @@ static void snd_trident_write_cso_reg(struct snd_trident * trident,
    Description: This routine will write the new ESO offset
                 register to hardware.
   
-   Paramters:   trident - pointer to target device class for 4DWave.
+   Parameters:  trident - pointer to target device class for 4DWave.
                 voice - synthesizer voice structure
                 ESO - new ESO value
   
@@ -566,7 +565,7 @@ static void snd_trident_write_eso_reg(struct snd_trident * trident,
    Description: This routine will write the new voice volume
                 register to hardware.
   
-   Paramters:   trident - pointer to target device class for 4DWave.
+   Parameters:  trident - pointer to target device class for 4DWave.
                 voice - synthesizer voice structure
                 Vol - new voice volume
   
@@ -597,7 +596,7 @@ static void snd_trident_write_vol_reg(struct snd_trident * trident,
    Description: This routine will write the new voice pan
                 register to hardware.
   
-   Paramters:   trident - pointer to target device class for 4DWave.
+   Parameters:  trident - pointer to target device class for 4DWave.
                 voice - synthesizer voice structure
                 Pan - new pan value
   
@@ -619,7 +618,7 @@ static void snd_trident_write_pan_reg(struct snd_trident * trident,
    Description: This routine will write the new reverb volume
                 register to hardware.
   
-   Paramters:   trident - pointer to target device class for 4DWave.
+   Parameters:  trident - pointer to target device class for 4DWave.
                 voice - synthesizer voice structure
                 RVol - new reverb volume
   
@@ -643,7 +642,7 @@ static void snd_trident_write_rvol_reg(struct snd_trident * trident,
    Description: This routine will write the new chorus volume
                 register to hardware.
   
-   Paramters:   trident - pointer to target device class for 4DWave.
+   Parameters:  trident - pointer to target device class for 4DWave.
                 voice - synthesizer voice structure
                 CVol - new chorus volume
   
@@ -666,7 +665,7 @@ static void snd_trident_write_cvol_reg(struct snd_trident * trident,
 
    Description: This routine converts rate in HZ to hardware delta value.
   
-   Paramters:   trident - pointer to target device class for 4DWave.
+   Parameters:  trident - pointer to target device class for 4DWave.
                 rate - Real or Virtual channel number.
   
    Returns:     Delta value.
@@ -696,7 +695,7 @@ static unsigned int snd_trident_convert_rate(unsigned int rate)
 
    Description: This routine converts rate in HZ to hardware delta value.
   
-   Paramters:   trident - pointer to target device class for 4DWave.
+   Parameters:  trident - pointer to target device class for 4DWave.
                 rate - Real or Virtual channel number.
   
    Returns:     Delta value.
@@ -726,7 +725,7 @@ static unsigned int snd_trident_convert_adc_rate(unsigned int rate)
 
    Description: This routine converts rate in HZ to spurious threshold.
   
-   Paramters:   trident - pointer to target device class for 4DWave.
+   Parameters:  trident - pointer to target device class for 4DWave.
                 rate - Real or Virtual channel number.
   
    Returns:     Delta value.
@@ -748,7 +747,7 @@ static unsigned int snd_trident_spurious_threshold(unsigned int rate,
 
    Description: This routine returns a control mode for a PCM channel.
   
-   Paramters:   trident - pointer to target device class for 4DWave.
+   Parameters:  trident - pointer to target device class for 4DWave.
                 substream  - PCM substream
   
    Returns:     Control value.
@@ -781,7 +780,7 @@ static unsigned int snd_trident_control_mode(struct snd_pcm_substream *substream
   
    Description: Device I/O control handler for playback/capture parameters.
   
-   Paramters:   substream  - PCM substream class
+   Parameters:   substream  - PCM substream class
                 cmd     - what ioctl message to process
                 arg     - additional message infoarg     
   
@@ -1664,7 +1663,7 @@ static snd_pcm_uframes_t snd_trident_playback_pointer(struct snd_pcm_substream *
   
    Description: This routine return the capture position
                 
-   Paramters:   pcm1    - PCM device class
+   Parameters:   pcm1    - PCM device class
 
    Returns:     position of buffer
   
@@ -2157,7 +2156,7 @@ static struct snd_pcm_ops snd_trident_spdif_7018_ops = {
   
    Description: This routine registers the 4DWave device for PCM support.
                 
-   Paramters:   trident - pointer to target device class for 4DWave.
+   Parameters:  trident - pointer to target device class for 4DWave.
 
    Returns:     None
   
@@ -2215,7 +2214,7 @@ int __devinit snd_trident_pcm(struct snd_trident * trident,
   
    Description: This routine registers the 4DWave device for foldback PCM support.
                 
-   Paramters:   trident - pointer to target device class for 4DWave.
+   Parameters:  trident - pointer to target device class for 4DWave.
 
    Returns:     None
   
@@ -2272,7 +2271,7 @@ int __devinit snd_trident_foldback_pcm(struct snd_trident * trident,
   
    Description: This routine registers the 4DWave-NX device for SPDIF support.
                 
-   Paramters:   trident - pointer to target device class for 4DWave-NX.
+   Parameters:  trident - pointer to target device class for 4DWave-NX.
 
    Returns:     None
   
@@ -2956,7 +2955,7 @@ static int snd_trident_pcm_mixer_free(struct snd_trident *trident, struct snd_tr
   
    Description: This routine registers the 4DWave device for mixer support.
                 
-   Paramters:   trident - pointer to target device class for 4DWave.
+   Parameters:  trident - pointer to target device class for 4DWave.
 
    Returns:     None
   
@@ -3313,12 +3312,6 @@ static void snd_trident_proc_read(struct snd_info_entry *entry,
                        snd_iprintf(buffer, "Memory Free    : %d\n", snd_util_mem_avail(trident->tlb.memhdr));
                }
        }
-#if defined(CONFIG_SND_SEQUENCER) || (defined(MODULE) && defined(CONFIG_SND_SEQUENCER_MODULE))
-       snd_iprintf(buffer,"\nWavetable Synth\n");
-       snd_iprintf(buffer, "Memory Maximum : %d\n", trident->synth.max_size);
-       snd_iprintf(buffer, "Memory Used    : %d\n", trident->synth.current_size);
-       snd_iprintf(buffer, "Memory Free    : %d\n", (trident->synth.max_size-trident->synth.current_size));
-#endif
 }
 
 static void __devinit snd_trident_proc_init(struct snd_trident * trident)
@@ -3344,7 +3337,7 @@ static int snd_trident_dev_free(struct snd_device *device)
    Description: Allocate and set up the TLB page table on 4D NX.
                Each entry has 4 bytes (physical PCI address).
                 
-   Paramters:   trident - pointer to target device class for 4DWave.
+   Parameters:  trident - pointer to target device class for 4DWave.
 
    Returns:     0 or negative error code
   
@@ -3521,7 +3514,7 @@ static int snd_trident_sis_init(struct snd_trident *trident)
    Description: This routine will create the device specific class for
                 the 4DWave card. It will also perform basic initialization.
                 
-   Paramters:   card  - which card to create
+   Parameters:  card  - which card to create
                 pci   - interface to PCI bus resource info
                 dma1ptr - playback dma buffer
                 dma2ptr - capture dma buffer
@@ -3667,7 +3660,7 @@ int __devinit snd_trident_create(struct snd_card *card,
    Description: This routine will free the device specific class for
                 the 4DWave card. 
                 
-   Paramters:   trident  - device specific private data for 4DWave card
+   Parameters:  trident  - device specific private data for 4DWave card
 
    Returns:     None.
   
@@ -3705,7 +3698,7 @@ static int snd_trident_free(struct snd_trident *trident)
   
    Description: ISR for Trident 4DWave device
                 
-   Paramters:   trident  - device specific private data for 4DWave card
+   Parameters:  trident  - device specific private data for 4DWave card
 
    Problems:    It seems that Trident chips generates interrupts more than
                 one time in special cases. The spurious interrupts are
@@ -3815,28 +3808,6 @@ static irqreturn_t snd_trident_interrupt(int irq, void *dev_id)
        return IRQ_HANDLED;
 }
 
-/*---------------------------------------------------------------------------
-   snd_trident_attach_synthesizer
-  
-   Description: Attach synthesizer hooks
-                
-   Paramters:   trident  - device specific private data for 4DWave card
-
-   Returns:     None.
-  
-  ---------------------------------------------------------------------------*/
-int snd_trident_attach_synthesizer(struct snd_trident *trident)
-{      
-#if defined(CONFIG_SND_SEQUENCER) || (defined(MODULE) && defined(CONFIG_SND_SEQUENCER_MODULE))
-       if (snd_seq_device_new(trident->card, 1, SNDRV_SEQ_DEV_ID_TRIDENT,
-                              sizeof(struct snd_trident *), &trident->seq_dev) >= 0) {
-               strcpy(trident->seq_dev->name, "4DWave");
-               *(struct snd_trident **)SNDRV_SEQ_DEVICE_ARGPTR(trident->seq_dev) = trident;
-       }
-#endif
-       return 0;
-}
-
 struct snd_trident_voice *snd_trident_alloc_voice(struct snd_trident * trident, int type, int client, int port)
 {
        struct snd_trident_voice *pvoice;
index 847b8c6d5c0a3edc557f655e44781412feab3310..df9b487fa17e7ca582e1eaae362656ca591e6620 100644 (file)
@@ -23,7 +23,6 @@
  *
  */
 
-#include <sound/driver.h>
 #include <asm/io.h>
 #include <linux/pci.h>
 #include <linux/time.h>
diff --git a/sound/pci/trident/trident_synth.c b/sound/pci/trident/trident_synth.c
deleted file mode 100644 (file)
index 9b7dee8..0000000
+++ /dev/null
@@ -1,1024 +0,0 @@
-/*
- *  Routines for Trident 4DWave NX/DX soundcards - Synthesizer
- *  Copyright (c) by Scott McNab <jedi@tartarus.uwa.edu.au>
- *
- *
- *   This program is free software; you can redistribute it and/or modify
- *   it under the terms of the GNU General Public License as published by
- *   the Free Software Foundation; either version 2 of the License, or
- *   (at your option) any later version.
- *
- *   This program is distributed in the hope that it will be useful,
- *   but WITHOUT ANY WARRANTY; without even the implied warranty of
- *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- *   GNU General Public License for more details.
- *
- *   You should have received a copy of the GNU General Public License
- *   along with this program; if not, write to the Free Software
- *   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA
- *
- */
-
-#include <sound/driver.h>
-#include <asm/io.h>
-#include <linux/init.h>
-#include <linux/slab.h>
-#include <linux/pci.h>
-#include <sound/core.h>
-#include <sound/trident.h>
-#include <sound/seq_device.h>
-
-MODULE_AUTHOR("Scott McNab <jedi@tartarus.uwa.edu.au>");
-MODULE_DESCRIPTION("Routines for Trident 4DWave NX/DX soundcards - Synthesizer");
-MODULE_LICENSE("GPL");
-
-/* linear to log pan conversion table (4.2 channel attenuation format) */
-static unsigned int pan_table[63] = {
-       7959, 7733, 7514, 7301, 7093, 6892, 6697, 6507, 
-       6322, 6143, 5968, 5799, 5634, 5475, 5319, 5168, 
-       5022, 4879, 4741, 4606, 4475, 4349, 4225, 4105, 
-       3989, 3876, 3766, 3659, 3555, 3454, 3356, 3261, 
-       3168, 3078, 2991, 2906, 2824, 2744, 2666, 2590, 
-       2517, 2445, 2376, 2308, 2243, 2179, 2117, 2057, 
-       1999, 1942, 1887, 1833, 1781, 1731, 1682, 1634, 
-       1588, 1543, 1499, 1456, 1415, 1375, 1336
-};
-
-#define LOG_TABLE_SIZE 386
-
-/* Linear half-attenuation to log conversion table in the format:
- *   {linear volume, logarithmic attenuation equivalent}, ...
- *
- * Provides conversion from a linear half-volume value in the range
- * [0,8192] to a logarithmic attenuation value in the range 0 to 6.02dB.
- * Halving the linear volume is equivalent to an additional 6dB of 
- * logarithmic attenuation. The algorithm used in log_from_linear()
- * therefore uses this table as follows:
- * 
- * - loop and for every time the volume is less than half the maximum 
- *   volume (16384), add another 6dB and halve the maximum value used
- *   for this comparison.
- * - when the volume is greater than half the maximum volume, take
- *   the difference of the volume to half volume (in the range [0,8192])
- *   and look up the log_table[] to find the nearest entry.
- * - take the logarithic component of this entry and add it to the 
- *   resulting attenuation.
- *
- * Thus this routine provides a linear->log conversion for a range of
- * [0,16384] using only 386 table entries
- *
- * Note: although this table stores log attenuation in 8.8 format, values
- * were only calculated for 6 bits fractional precision, since that is
- * the most precision offered by the trident hardware.
- */
-
-static unsigned short log_table[LOG_TABLE_SIZE*2] =
-{
-       4, 0x0604, 19, 0x0600, 34, 0x05fc, 
-       49, 0x05f8, 63, 0x05f4, 78, 0x05f0, 93, 0x05ec, 108, 0x05e8, 
-       123, 0x05e4, 138, 0x05e0, 153, 0x05dc, 168, 0x05d8, 183, 0x05d4, 
-       198, 0x05d0, 213, 0x05cc, 228, 0x05c8, 244, 0x05c4, 259, 0x05c0, 
-       274, 0x05bc, 289, 0x05b8, 304, 0x05b4, 320, 0x05b0, 335, 0x05ac, 
-       350, 0x05a8, 366, 0x05a4, 381, 0x05a0, 397, 0x059c, 412, 0x0598, 
-       428, 0x0594, 443, 0x0590, 459, 0x058c, 474, 0x0588, 490, 0x0584, 
-       506, 0x0580, 521, 0x057c, 537, 0x0578, 553, 0x0574, 568, 0x0570, 
-       584, 0x056c, 600, 0x0568, 616, 0x0564, 632, 0x0560, 647, 0x055c, 
-       663, 0x0558, 679, 0x0554, 695, 0x0550, 711, 0x054c, 727, 0x0548, 
-       743, 0x0544, 759, 0x0540, 776, 0x053c, 792, 0x0538, 808, 0x0534, 
-       824, 0x0530, 840, 0x052c, 857, 0x0528, 873, 0x0524, 889, 0x0520, 
-       906, 0x051c, 922, 0x0518, 938, 0x0514, 955, 0x0510, 971, 0x050c, 
-       988, 0x0508, 1004, 0x0504, 1021, 0x0500, 1037, 0x04fc, 1054, 0x04f8, 
-       1071, 0x04f4, 1087, 0x04f0, 1104, 0x04ec, 1121, 0x04e8, 1138, 0x04e4, 
-       1154, 0x04e0, 1171, 0x04dc, 1188, 0x04d8, 1205, 0x04d4, 1222, 0x04d0, 
-       1239, 0x04cc, 1256, 0x04c8, 1273, 0x04c4, 1290, 0x04c0, 1307, 0x04bc, 
-       1324, 0x04b8, 1341, 0x04b4, 1358, 0x04b0, 1376, 0x04ac, 1393, 0x04a8, 
-       1410, 0x04a4, 1427, 0x04a0, 1445, 0x049c, 1462, 0x0498, 1479, 0x0494, 
-       1497, 0x0490, 1514, 0x048c, 1532, 0x0488, 1549, 0x0484, 1567, 0x0480, 
-       1584, 0x047c, 1602, 0x0478, 1620, 0x0474, 1637, 0x0470, 1655, 0x046c, 
-       1673, 0x0468, 1690, 0x0464, 1708, 0x0460, 1726, 0x045c, 1744, 0x0458, 
-       1762, 0x0454, 1780, 0x0450, 1798, 0x044c, 1816, 0x0448, 1834, 0x0444, 
-       1852, 0x0440, 1870, 0x043c, 1888, 0x0438, 1906, 0x0434, 1924, 0x0430, 
-       1943, 0x042c, 1961, 0x0428, 1979, 0x0424, 1997, 0x0420, 2016, 0x041c, 
-       2034, 0x0418, 2053, 0x0414, 2071, 0x0410, 2089, 0x040c, 2108, 0x0408, 
-       2127, 0x0404, 2145, 0x0400, 2164, 0x03fc, 2182, 0x03f8, 2201, 0x03f4, 
-       2220, 0x03f0, 2239, 0x03ec, 2257, 0x03e8, 2276, 0x03e4, 2295, 0x03e0, 
-       2314, 0x03dc, 2333, 0x03d8, 2352, 0x03d4, 2371, 0x03d0, 2390, 0x03cc, 
-       2409, 0x03c8, 2428, 0x03c4, 2447, 0x03c0, 2466, 0x03bc, 2485, 0x03b8, 
-       2505, 0x03b4, 2524, 0x03b0, 2543, 0x03ac, 2562, 0x03a8, 2582, 0x03a4, 
-       2601, 0x03a0, 2621, 0x039c, 2640, 0x0398, 2660, 0x0394, 2679, 0x0390, 
-       2699, 0x038c, 2718, 0x0388, 2738, 0x0384, 2758, 0x0380, 2777, 0x037c, 
-       2797, 0x0378, 2817, 0x0374, 2837, 0x0370, 2857, 0x036c, 2876, 0x0368, 
-       2896, 0x0364, 2916, 0x0360, 2936, 0x035c, 2956, 0x0358, 2976, 0x0354, 
-       2997, 0x0350, 3017, 0x034c, 3037, 0x0348, 3057, 0x0344, 3077, 0x0340, 
-       3098, 0x033c, 3118, 0x0338, 3138, 0x0334, 3159, 0x0330, 3179, 0x032c, 
-       3200, 0x0328, 3220, 0x0324, 3241, 0x0320, 3261, 0x031c, 3282, 0x0318, 
-       3303, 0x0314, 3323, 0x0310, 3344, 0x030c, 3365, 0x0308, 3386, 0x0304, 
-       3406, 0x0300, 3427, 0x02fc, 3448, 0x02f8, 3469, 0x02f4, 3490, 0x02f0, 
-       3511, 0x02ec, 3532, 0x02e8, 3553, 0x02e4, 3575, 0x02e0, 3596, 0x02dc, 
-       3617, 0x02d8, 3638, 0x02d4, 3660, 0x02d0, 3681, 0x02cc, 3702, 0x02c8, 
-       3724, 0x02c4, 3745, 0x02c0, 3767, 0x02bc, 3788, 0x02b8, 3810, 0x02b4, 
-       3831, 0x02b0, 3853, 0x02ac, 3875, 0x02a8, 3896, 0x02a4, 3918, 0x02a0, 
-       3940, 0x029c, 3962, 0x0298, 3984, 0x0294, 4006, 0x0290, 4028, 0x028c, 
-       4050, 0x0288, 4072, 0x0284, 4094, 0x0280, 4116, 0x027c, 4138, 0x0278, 
-       4160, 0x0274, 4182, 0x0270, 4205, 0x026c, 4227, 0x0268, 4249, 0x0264, 
-       4272, 0x0260, 4294, 0x025c, 4317, 0x0258, 4339, 0x0254, 4362, 0x0250, 
-       4384, 0x024c, 4407, 0x0248, 4430, 0x0244, 4453, 0x0240, 4475, 0x023c, 
-       4498, 0x0238, 4521, 0x0234, 4544, 0x0230, 4567, 0x022c, 4590, 0x0228, 
-       4613, 0x0224, 4636, 0x0220, 4659, 0x021c, 4682, 0x0218, 4705, 0x0214, 
-       4728, 0x0210, 4752, 0x020c, 4775, 0x0208, 4798, 0x0204, 4822, 0x0200, 
-       4845, 0x01fc, 4869, 0x01f8, 4892, 0x01f4, 4916, 0x01f0, 4939, 0x01ec, 
-       4963, 0x01e8, 4987, 0x01e4, 5010, 0x01e0, 5034, 0x01dc, 5058, 0x01d8, 
-       5082, 0x01d4, 5106, 0x01d0, 5130, 0x01cc, 5154, 0x01c8, 5178, 0x01c4, 
-       5202, 0x01c0, 5226, 0x01bc, 5250, 0x01b8, 5274, 0x01b4, 5299, 0x01b0, 
-       5323, 0x01ac, 5347, 0x01a8, 5372, 0x01a4, 5396, 0x01a0, 5420, 0x019c, 
-       5445, 0x0198, 5469, 0x0194, 5494, 0x0190, 5519, 0x018c, 5543, 0x0188, 
-       5568, 0x0184, 5593, 0x0180, 5618, 0x017c, 5643, 0x0178, 5668, 0x0174, 
-       5692, 0x0170, 5717, 0x016c, 5743, 0x0168, 5768, 0x0164, 5793, 0x0160, 
-       5818, 0x015c, 5843, 0x0158, 5868, 0x0154, 5894, 0x0150, 5919, 0x014c, 
-       5945, 0x0148, 5970, 0x0144, 5995, 0x0140, 6021, 0x013c, 6047, 0x0138, 
-       6072, 0x0134, 6098, 0x0130, 6124, 0x012c, 6149, 0x0128, 6175, 0x0124, 
-       6201, 0x0120, 6227, 0x011c, 6253, 0x0118, 6279, 0x0114, 6305, 0x0110, 
-       6331, 0x010c, 6357, 0x0108, 6384, 0x0104, 6410, 0x0100, 6436, 0x00fc, 
-       6462, 0x00f8, 6489, 0x00f4, 6515, 0x00f0, 6542, 0x00ec, 6568, 0x00e8, 
-       6595, 0x00e4, 6621, 0x00e0, 6648, 0x00dc, 6675, 0x00d8, 6702, 0x00d4, 
-       6728, 0x00d0, 6755, 0x00cc, 6782, 0x00c8, 6809, 0x00c4, 6836, 0x00c0, 
-       6863, 0x00bc, 6890, 0x00b8, 6917, 0x00b4, 6945, 0x00b0, 6972, 0x00ac, 
-       6999, 0x00a8, 7027, 0x00a4, 7054, 0x00a0, 7081, 0x009c, 7109, 0x0098, 
-       7136, 0x0094, 7164, 0x0090, 7192, 0x008c, 7219, 0x0088, 7247, 0x0084, 
-       7275, 0x0080, 7303, 0x007c, 7331, 0x0078, 7359, 0x0074, 7387, 0x0070, 
-       7415, 0x006c, 7443, 0x0068, 7471, 0x0064, 7499, 0x0060, 7527, 0x005c, 
-       7556, 0x0058, 7584, 0x0054, 7613, 0x0050, 7641, 0x004c, 7669, 0x0048, 
-       7698, 0x0044, 7727, 0x0040, 7755, 0x003c, 7784, 0x0038, 7813, 0x0034, 
-       7842, 0x0030, 7870, 0x002c, 7899, 0x0028, 7928, 0x0024, 7957, 0x0020, 
-       7986, 0x001c, 8016, 0x0018, 8045, 0x0014, 8074, 0x0010, 8103, 0x000c, 
-       8133, 0x0008, 8162, 0x0004, 8192, 0x0000
-};
-
-static unsigned short lookup_volume_table( unsigned short value )
-{
-       /* This code is an optimised version of:
-        *   int i = 0;
-        *   while( volume_table[i*2] < value )
-        *       i++;
-        *   return volume_table[i*2+1];
-        */
-       unsigned short *ptr = log_table;
-       while( *ptr < value )
-               ptr += 2;
-       return *(ptr+1);
-}
-
-/* this function calculates a 8.8 fixed point logarithmic attenuation
- * value from a linear volume value in the range 0 to 16384 */
-static unsigned short log_from_linear( unsigned short value )
-{
-       if (value >= 16384)
-               return 0x0000;
-       if (value) {
-               unsigned short result = 0;
-               int v, c;
-               for( c = 0, v = 8192; c < 14; c++, v >>= 1 ) {
-                       if( value >= v ) {
-                               result += lookup_volume_table( (value - v) << c );
-                               return result;
-                       }
-                       result += 0x0605;       /* 6.0205 (result of -20*log10(0.5)) */
-               }
-       }
-       return 0xffff;
-}
-
-/*
- * Sample handling operations
- */
-
-static void sample_start(struct snd_trident * trident, struct snd_trident_voice * voice, snd_seq_position_t position);
-static void sample_stop(struct snd_trident * trident, struct snd_trident_voice * voice, int mode);
-static void sample_freq(struct snd_trident * trident, struct snd_trident_voice * voice, snd_seq_frequency_t freq);
-static void sample_volume(struct snd_trident * trident, struct snd_trident_voice * voice, struct snd_seq_ev_volume * volume);
-static void sample_loop(struct snd_trident * trident, struct snd_trident_voice * voice, struct snd_seq_ev_loop * loop);
-static void sample_pos(struct snd_trident * trident, struct snd_trident_voice * voice, snd_seq_position_t position);
-static void sample_private1(struct snd_trident * trident, struct snd_trident_voice * voice, unsigned char *data);
-
-static struct snd_trident_sample_ops sample_ops =
-{
-       sample_start,
-       sample_stop,
-       sample_freq,
-       sample_volume,
-       sample_loop,
-       sample_pos,
-       sample_private1
-};
-
-static void snd_trident_simple_init(struct snd_trident_voice * voice)
-{
-       //voice->handler_wave = interrupt_wave;
-       //voice->handler_volume = interrupt_volume;
-       //voice->handler_effect = interrupt_effect;
-       //voice->volume_change = NULL;
-       voice->sample_ops = &sample_ops;
-}
-
-static void sample_start(struct snd_trident * trident, struct snd_trident_voice * voice, snd_seq_position_t position)
-{
-       struct simple_instrument *simple;
-       struct snd_seq_kinstr *instr;
-       unsigned long flags;
-       unsigned int loop_start, loop_end, sample_start, sample_end, start_offset;
-       unsigned int value;
-       unsigned int shift = 0;
-
-       instr = snd_seq_instr_find(trident->synth.ilist, &voice->instr, 0, 1);
-       if (instr == NULL)
-               return;
-       voice->instr = instr->instr;    /* copy ID to speedup aliases */
-       simple = KINSTR_DATA(instr);
-
-       spin_lock_irqsave(&trident->reg_lock, flags);
-
-       if (trident->device == TRIDENT_DEVICE_ID_SI7018)
-               voice->GVSel = 1;       /* route to Wave volume */
-
-       voice->CTRL = 0;
-       voice->Alpha = 0;
-       voice->FMS = 0;
-
-       loop_start = simple->loop_start >> 4;
-       loop_end = simple->loop_end >> 4;
-       sample_start = (simple->start + position) >> 4;
-       if( sample_start >= simple->size )
-               sample_start = simple->start >> 4;
-       sample_end = simple->size;
-       start_offset = position >> 4;
-
-       if (simple->format & SIMPLE_WAVE_16BIT) {
-               voice->CTRL |= 8;
-               shift++;
-       }
-       if (simple->format & SIMPLE_WAVE_STEREO) {
-               voice->CTRL |= 4;
-               shift++;
-       }
-       if (!(simple->format & SIMPLE_WAVE_UNSIGNED))
-               voice->CTRL |= 2;
-
-       voice->LBA = simple->address.memory;
-
-       if (simple->format & SIMPLE_WAVE_LOOP) {
-               voice->CTRL |= 1;
-               voice->LBA += loop_start << shift;
-               if( start_offset >= loop_start ) {
-                       voice->CSO = start_offset - loop_start;
-                       voice->negCSO = 0;
-               } else {
-                       voice->CSO = loop_start - start_offset;
-                       voice->negCSO = 1;
-               }
-               voice->ESO = loop_end - loop_start - 1;
-       } else {
-               voice->LBA += start_offset << shift;
-               voice->CSO = sample_start;
-               voice->ESO = sample_end - 1;
-               voice->negCSO = 0;
-       }
-
-       if (voice->flags & SNDRV_TRIDENT_VFLG_RUNNING) {
-               snd_trident_stop_voice(trident, voice->number);
-               voice->flags &= ~SNDRV_TRIDENT_VFLG_RUNNING;
-       }
-
-       /* set CSO sign */
-       value = inl(TRID_REG(trident, T4D_SIGN_CSO_A));
-       if( voice->negCSO ) {
-               value |= 1 << (voice->number&31);
-       } else {
-               value &= ~(1 << (voice->number&31));
-       }
-       outl(value,TRID_REG(trident, T4D_SIGN_CSO_A));
-
-       voice->Attribute = 0;   
-       snd_trident_write_voice_regs(trident, voice);
-       snd_trident_start_voice(trident, voice->number);
-       voice->flags |= SNDRV_TRIDENT_VFLG_RUNNING;
-       spin_unlock_irqrestore(&trident->reg_lock, flags);
-       snd_seq_instr_free_use(trident->synth.ilist, instr);
-}
-
-static void sample_stop(struct snd_trident * trident, struct snd_trident_voice * voice, int mode)
-{
-       unsigned long flags;
-
-       if (!(voice->flags & SNDRV_TRIDENT_VFLG_RUNNING))
-               return;
-
-       switch (mode) {
-       default:
-               spin_lock_irqsave(&trident->reg_lock, flags);
-               snd_trident_stop_voice(trident, voice->number);
-               voice->flags &= ~SNDRV_TRIDENT_VFLG_RUNNING;
-               spin_unlock_irqrestore(&trident->reg_lock, flags);
-               break;
-       case SAMPLE_STOP_LOOP:  /* disable loop only */
-               voice->CTRL &= ~1;
-               spin_lock_irqsave(&trident->reg_lock, flags);
-               outb((unsigned char) voice->number, TRID_REG(trident, T4D_LFO_GC_CIR));
-               outw((((voice->CTRL << 12) | (voice->EC & 0x0fff)) & 0xffff), CH_GVSEL_PAN_VOL_CTRL_EC);
-               spin_unlock_irqrestore(&trident->reg_lock, flags);
-               break;
-       }
-}
-
-static void sample_freq(struct snd_trident * trident, struct snd_trident_voice * voice, snd_seq_frequency_t freq)
-{
-       unsigned long flags;
-       freq >>= 4;
-
-       spin_lock_irqsave(&trident->reg_lock, flags);
-       if (freq == 44100)
-               voice->Delta = 0xeb3;
-       else if (freq == 8000)
-               voice->Delta = 0x2ab;
-       else if (freq == 48000)
-               voice->Delta = 0x1000;
-       else
-               voice->Delta = (((freq << 12) + freq) / 48000) & 0x0000ffff;
-
-       outb((unsigned char) voice->number, TRID_REG(trident, T4D_LFO_GC_CIR));
-       if (trident->device == TRIDENT_DEVICE_ID_NX) {
-               outb((unsigned char) voice->Delta, TRID_REG(trident, CH_NX_DELTA_CSO + 3));
-               outb((unsigned char) (voice->Delta >> 8), TRID_REG(trident, CH_NX_DELTA_ESO + 3));
-       } else {
-               outw((unsigned short) voice->Delta, TRID_REG(trident, CH_DX_ESO_DELTA));
-       }
-
-       spin_unlock_irqrestore(&trident->reg_lock, flags);
-}
-
-static void sample_volume(struct snd_trident * trident, struct snd_trident_voice * voice, struct snd_seq_ev_volume * volume)
-{
-       unsigned long flags;
-       unsigned short value;
-
-       spin_lock_irqsave(&trident->reg_lock, flags);
-       voice->GVSel = 0;       /* use global music volume */
-       voice->FMC = 0x03;      /* fixme: can we do something useful with FMC? */
-       if (volume->volume >= 0) {
-               volume->volume &= 0x3fff;
-               /* linear volume -> logarithmic attenuation conversion
-                * uses EC register for greater resolution (6.6 bits) than Vol register (5.3 bits)
-                * Vol register used when additional attenuation is required */
-               voice->RVol = 0;
-               voice->CVol = 0;
-               value = log_from_linear( volume->volume );
-               voice->Vol = 0;
-               voice->EC = (value & 0x3fff) >> 2;
-               if (value > 0x3fff) {
-                       voice->EC |= 0xfc0;
-                       if (value < 0x5f00 )
-                               voice->Vol = ((value >> 8) - 0x3f) << 5;
-                       else {
-                               voice->Vol = 0x3ff;
-                               voice->EC = 0xfff;
-                       }
-               }
-       }
-       if (volume->lr >= 0) {
-               volume->lr &= 0x3fff;
-               /* approximate linear pan by attenuating channels */
-               if (volume->lr >= 0x2000) {     /* attenuate left (pan right) */
-                       value = 0x3fff - volume->lr;
-                       for (voice->Pan = 0; voice->Pan < 63; voice->Pan++ ) 
-                               if (value >= pan_table[voice->Pan] )
-                                       break;
-               } else {                        /* attenuate right (pan left) */
-                       for (voice->Pan = 0; voice->Pan < 63; voice->Pan++ ) 
-                               if ((unsigned int)volume->lr >= pan_table[voice->Pan] )
-                                       break;
-                       voice->Pan |= 0x40;
-               }
-       }
-       outb((unsigned char) voice->number, TRID_REG(trident, T4D_LFO_GC_CIR));
-       outl((voice->GVSel << 31) | ((voice->Pan & 0x0000007f) << 24) |
-                ((voice->Vol & 0x000000ff) << 16) | ((voice->CTRL & 0x0000000f) << 12) |
-                (voice->EC & 0x00000fff), TRID_REG(trident, CH_GVSEL_PAN_VOL_CTRL_EC));
-       value = ((voice->FMC & 0x03) << 14) | ((voice->RVol & 0x7f) << 7) | (voice->CVol & 0x7f);
-       outw(value, TRID_REG(trident, CH_DX_FMC_RVOL_CVOL));
-       spin_unlock_irqrestore(&trident->reg_lock, flags);
-}
-
-static void sample_loop(struct snd_trident * trident, struct snd_trident_voice * voice, struct snd_seq_ev_loop * loop)
-{
-       unsigned long flags;
-       struct simple_instrument *simple;
-       struct snd_seq_kinstr *instr;
-       unsigned int loop_start, loop_end;
-
-       instr = snd_seq_instr_find(trident->synth.ilist, &voice->instr, 0, 1);
-       if (instr == NULL)
-               return;
-       voice->instr = instr->instr;    /* copy ID to speedup aliases */
-       simple = KINSTR_DATA(instr);
-
-       loop_start = loop->start >> 4;
-       loop_end = loop->end >> 4;
-
-       spin_lock_irqsave(&trident->reg_lock, flags);
-
-       voice->LBA = simple->address.memory + loop_start;
-       voice->CSO = 0;
-       voice->ESO = loop_end - loop_start - 1;
-
-       outb((unsigned char) voice->number, TRID_REG(trident, T4D_LFO_GC_CIR));
-       outb((voice->LBA >> 16), TRID_REG(trident, CH_LBA + 2));
-       outw((voice->LBA & 0xffff), TRID_REG(trident, CH_LBA));
-       if (trident->device == TRIDENT_DEVICE_ID_NX) {
-               outb((voice->ESO >> 16), TRID_REG(trident, CH_NX_DELTA_ESO + 2));
-               outw((voice->ESO & 0xffff), TRID_REG(trident, CH_NX_DELTA_ESO));
-               outb((voice->CSO >> 16), TRID_REG(trident, CH_NX_DELTA_CSO + 2));
-               outw((voice->CSO & 0xffff), TRID_REG(trident, CH_NX_DELTA_CSO));
-       } else {
-               outw((voice->ESO & 0xffff), TRID_REG(trident, CH_DX_ESO_DELTA + 2));
-               outw((voice->CSO & 0xffff), TRID_REG(trident, CH_DX_CSO_ALPHA_FMS + 2));
-       }
-
-       spin_unlock_irqrestore(&trident->reg_lock, flags);
-       snd_seq_instr_free_use(trident->synth.ilist, instr);
-}
-
-static void sample_pos(struct snd_trident * trident, struct snd_trident_voice * voice, snd_seq_position_t position)
-{
-       unsigned long flags;
-       struct simple_instrument *simple;
-       struct snd_seq_kinstr *instr;
-       unsigned int value;
-
-       instr = snd_seq_instr_find(trident->synth.ilist, &voice->instr, 0, 1);
-       if (instr == NULL)
-               return;
-       voice->instr = instr->instr;    /* copy ID to speedup aliases */
-       simple = KINSTR_DATA(instr);
-
-       spin_lock_irqsave(&trident->reg_lock, flags);
-
-       if (simple->format & SIMPLE_WAVE_LOOP) {
-               if( position >= simple->loop_start ) {
-                       voice->CSO = (position - simple->loop_start) >> 4;
-                       voice->negCSO = 0;
-               } else {
-                       voice->CSO = (simple->loop_start - position) >> 4;
-                       voice->negCSO = 1;
-               }
-       } else {
-               voice->CSO = position >> 4;
-               voice->negCSO = 0;
-       }
-
-       /* set CSO sign */
-       value = inl(TRID_REG(trident, T4D_SIGN_CSO_A));
-       if( voice->negCSO ) {
-               value |= 1 << (voice->number&31);
-       } else {
-               value &= ~(1 << (voice->number&31));
-       }
-       outl(value,TRID_REG(trident, T4D_SIGN_CSO_A));
-       
-
-       outb((unsigned char) voice->number, TRID_REG(trident, T4D_LFO_GC_CIR));
-       if (trident->device == TRIDENT_DEVICE_ID_NX) {
-               outw((voice->CSO & 0xffff), TRID_REG(trident, CH_NX_DELTA_CSO));
-               outb((voice->CSO >> 16), TRID_REG(trident, CH_NX_DELTA_CSO + 2));
-       } else {
-               outw((voice->CSO & 0xffff), TRID_REG(trident, CH_DX_CSO_ALPHA_FMS) + 2);
-       }
-
-       spin_unlock_irqrestore(&trident->reg_lock, flags);
-       snd_seq_instr_free_use(trident->synth.ilist, instr);
-}
-
-static void sample_private1(struct snd_trident * trident, struct snd_trident_voice * voice, unsigned char *data)
-{
-}
-
-/*
- * Memory management / sample loading
- */
-
-static int snd_trident_simple_put_sample(void *private_data,
-                                        struct simple_instrument * instr,
-                                        char __user *data, long len, int atomic)
-{
-       struct snd_trident *trident = private_data;
-       int size = instr->size;
-       int shift = 0;
-
-       if (instr->format & SIMPLE_WAVE_BACKWARD ||
-           instr->format & SIMPLE_WAVE_BIDIR ||
-           instr->format & SIMPLE_WAVE_ULAW) 
-               return -EINVAL; /* not supported */
-
-       if (instr->format & SIMPLE_WAVE_16BIT)
-               shift++;
-       if (instr->format & SIMPLE_WAVE_STEREO)
-               shift++;
-       size <<= shift;
-
-       if (trident->synth.current_size + size > trident->synth.max_size)
-               return -ENOMEM;
-
-       if (!access_ok(VERIFY_READ, data, size))
-               return -EFAULT;
-
-       if (trident->tlb.entries) {
-               struct snd_util_memblk *memblk;
-               memblk = snd_trident_synth_alloc(trident, size); 
-               if (memblk == NULL)
-                       return -ENOMEM;
-               if (snd_trident_synth_copy_from_user(trident, memblk, 0, data, size) ) {
-                       snd_trident_synth_free(trident, memblk);
-                       return -EFAULT;
-               }
-               instr->address.ptr = (unsigned char*)memblk;
-               instr->address.memory = memblk->offset;
-       } else {
-               struct snd_dma_buffer dmab;
-               if (snd_dma_alloc_pages(SNDRV_DMA_TYPE_DEV, snd_dma_pci_data(trident->pci),
-                                       size, &dmab) < 0)
-                       return -ENOMEM;
-
-               if (copy_from_user(dmab.area, data, size)) {
-                       snd_dma_free_pages(&dmab);
-                       return -EFAULT;
-               }
-               instr->address.ptr = dmab.area;
-               instr->address.memory = dmab.addr;
-       }
-
-       trident->synth.current_size += size;
-       return 0;
-}
-
-static int snd_trident_simple_get_sample(void *private_data,
-                                        struct simple_instrument * instr,
-                                        char __user *data, long len, int atomic)
-{
-       //struct snd_trident *trident = private_data;
-       int size = instr->size;
-       int shift = 0;
-
-       if (instr->format & SIMPLE_WAVE_16BIT)
-               shift++;
-       if (instr->format & SIMPLE_WAVE_STEREO)
-               shift++;
-       size <<= shift;
-
-       if (!access_ok(VERIFY_WRITE, data, size))
-               return -EFAULT;
-
-       /* FIXME: not implemented yet */
-
-       return -EBUSY;
-}
-
-static int snd_trident_simple_remove_sample(void *private_data,
-                                           struct simple_instrument * instr,
-                                           int atomic)
-{
-       struct snd_trident *trident = private_data;
-       int size = instr->size;
-
-       if (instr->format & SIMPLE_WAVE_16BIT)
-               size <<= 1;
-       if (instr->format & SIMPLE_WAVE_STEREO)
-               size <<= 1;
-
-       if (trident->tlb.entries) {
-               struct snd_util_memblk *memblk = (struct snd_util_memblk *)instr->address.ptr;
-               if (memblk)
-                       snd_trident_synth_free(trident, memblk);
-               else
-                       return -EFAULT;
-       } else {
-               struct snd_dma_buffer dmab;
-               dmab.dev.type = SNDRV_DMA_TYPE_DEV;
-               dmab.dev.dev = snd_dma_pci_data(trident->pci);
-               dmab.area = instr->address.ptr;
-               dmab.addr = instr->address.memory;
-               dmab.bytes = size;
-               snd_dma_free_pages(&dmab);
-       }
-
-       trident->synth.current_size -= size;
-       if (trident->synth.current_size < 0)    /* shouldn't need this check... */
-               trident->synth.current_size = 0;
-
-       return 0;
-}
-
-static void select_instrument(struct snd_trident * trident, struct snd_trident_voice * v)
-{
-       struct snd_seq_kinstr *instr;
-       instr = snd_seq_instr_find(trident->synth.ilist, &v->instr, 0, 1);
-       if (instr != NULL) {
-               if (instr->ops) {
-                       if (!strcmp(instr->ops->instr_type, SNDRV_SEQ_INSTR_ID_SIMPLE))
-                               snd_trident_simple_init(v);
-               }
-               snd_seq_instr_free_use(trident->synth.ilist, instr);
-       }
-}
-
-/*
-
- */
-
-static void event_sample(struct snd_seq_event * ev, struct snd_trident_port * p, struct snd_trident_voice * v)
-{
-       if (v->sample_ops && v->sample_ops->sample_stop)
-               v->sample_ops->sample_stop(p->trident, v, SAMPLE_STOP_IMMEDIATELY);
-       v->instr.std = ev->data.sample.param.sample.std;
-       if (v->instr.std & 0xff000000) {        /* private instrument */
-               v->instr.std &= 0x00ffffff;
-               v->instr.std |= (unsigned int)ev->source.client << 24;
-       }
-       v->instr.bank = ev->data.sample.param.sample.bank;
-       v->instr.prg = ev->data.sample.param.sample.prg;
-       select_instrument(p->trident, v);
-}
-
-static void event_cluster(struct snd_seq_event * ev, struct snd_trident_port * p, struct snd_trident_voice * v)
-{
-       if (v->sample_ops && v->sample_ops->sample_stop)
-               v->sample_ops->sample_stop(p->trident, v, SAMPLE_STOP_IMMEDIATELY);
-       v->instr.cluster = ev->data.sample.param.cluster.cluster;
-       select_instrument(p->trident, v);
-}
-
-static void event_start(struct snd_seq_event * ev, struct snd_trident_port * p, struct snd_trident_voice * v)
-{
-       if (v->sample_ops && v->sample_ops->sample_start)
-               v->sample_ops->sample_start(p->trident, v, ev->data.sample.param.position);
-}
-
-static void event_stop(struct snd_seq_event * ev, struct snd_trident_port * p, struct snd_trident_voice * v)
-{
-       if (v->sample_ops && v->sample_ops->sample_stop)
-               v->sample_ops->sample_stop(p->trident, v, ev->data.sample.param.stop_mode);
-}
-
-static void event_freq(struct snd_seq_event * ev, struct snd_trident_port * p, struct snd_trident_voice * v)
-{
-       if (v->sample_ops && v->sample_ops->sample_freq)
-               v->sample_ops->sample_freq(p->trident, v, ev->data.sample.param.frequency);
-}
-
-static void event_volume(struct snd_seq_event * ev, struct snd_trident_port * p, struct snd_trident_voice * v)
-{
-       if (v->sample_ops && v->sample_ops->sample_volume)
-               v->sample_ops->sample_volume(p->trident, v, &ev->data.sample.param.volume);
-}
-
-static void event_loop(struct snd_seq_event * ev, struct snd_trident_port * p, struct snd_trident_voice * v)
-{
-       if (v->sample_ops && v->sample_ops->sample_loop)
-               v->sample_ops->sample_loop(p->trident, v, &ev->data.sample.param.loop);
-}
-
-static void event_position(struct snd_seq_event * ev, struct snd_trident_port * p, struct snd_trident_voice * v)
-{
-       if (v->sample_ops && v->sample_ops->sample_pos)
-               v->sample_ops->sample_pos(p->trident, v, ev->data.sample.param.position);
-}
-
-static void event_private1(struct snd_seq_event * ev, struct snd_trident_port * p, struct snd_trident_voice * v)
-{
-       if (v->sample_ops && v->sample_ops->sample_private1)
-               v->sample_ops->sample_private1(p->trident, v, (unsigned char *) &ev->data.sample.param.raw8);
-}
-
-typedef void (trident_sample_event_handler_t) (struct snd_seq_event * ev, struct snd_trident_port * p, struct snd_trident_voice * v);
-
-static trident_sample_event_handler_t *trident_sample_event_handlers[9] =
-{
-       event_sample,
-       event_cluster,
-       event_start,
-       event_stop,
-       event_freq,
-       event_volume,
-       event_loop,
-       event_position,
-       event_private1
-};
-
-static void snd_trident_sample_event(struct snd_seq_event * ev, struct snd_trident_port * p)
-{
-       int idx, voice;
-       struct snd_trident *trident = p->trident;
-       struct snd_trident_voice *v;
-       unsigned long flags;
-
-       idx = ev->type - SNDRV_SEQ_EVENT_SAMPLE;
-       if (idx < 0 || idx > 8)
-               return;
-       for (voice = 0; voice < 64; voice++) {
-               v = &trident->synth.voices[voice];
-               if (v->use && v->client == ev->source.client &&
-                   v->port == ev->source.port &&
-                   v->index == ev->data.sample.channel) {
-                       spin_lock_irqsave(&trident->event_lock, flags);
-                       trident_sample_event_handlers[idx] (ev, p, v);
-                       spin_unlock_irqrestore(&trident->event_lock, flags);
-                       return;
-               }
-       }
-}
-
-/*
-
- */
-
-static void snd_trident_synth_free_voices(struct snd_trident * trident, int client, int port)
-{
-       int idx;
-       struct snd_trident_voice *voice;
-
-       for (idx = 0; idx < 32; idx++) {
-               voice = &trident->synth.voices[idx];
-               if (voice->use && voice->client == client && voice->port == port)
-                       snd_trident_free_voice(trident, voice);
-       }
-}
-
-static int snd_trident_synth_use(void *private_data, struct snd_seq_port_subscribe * info)
-{
-       struct snd_trident_port *port = private_data;
-       struct snd_trident *trident = port->trident;
-       struct snd_trident_voice *voice;
-       unsigned int idx;
-       unsigned long flags;
-
-       if (info->voices > 32)
-               return -EINVAL;
-       spin_lock_irqsave(&trident->reg_lock, flags);
-       for (idx = 0; idx < info->voices; idx++) {
-               voice = snd_trident_alloc_voice(trident, SNDRV_TRIDENT_VOICE_TYPE_SYNTH, info->sender.client, info->sender.port);
-               if (voice == NULL) {
-                       snd_trident_synth_free_voices(trident, info->sender.client, info->sender.port);
-                       spin_unlock_irqrestore(&trident->reg_lock, flags);
-                       return -EBUSY;
-               }
-               voice->index = idx;
-               voice->Vol = 0x3ff;
-               voice->EC = 0x0fff;
-       }
-#if 0
-       for (idx = 0; idx < info->midi_voices; idx++) {
-               port->midi_has_voices = 1;
-               voice = snd_trident_alloc_voice(trident, SNDRV_TRIDENT_VOICE_TYPE_MIDI, info->sender.client, info->sender.port);
-               if (voice == NULL) {
-                       snd_trident_synth_free_voices(trident, info->sender.client, info->sender.port);
-                       spin_unlock_irqrestore(&trident->reg_lock, flags);
-                       return -EBUSY;
-               }
-               voice->Vol = 0x3ff;
-               voice->EC = 0x0fff;
-       }
-#endif
-       spin_unlock_irqrestore(&trident->reg_lock, flags);
-       return 0;
-}
-
-static int snd_trident_synth_unuse(void *private_data, struct snd_seq_port_subscribe * info)
-{
-       struct snd_trident_port *port = private_data;
-       struct snd_trident *trident = port->trident;
-       unsigned long flags;
-
-       spin_lock_irqsave(&trident->reg_lock, flags);
-       snd_trident_synth_free_voices(trident, info->sender.client, info->sender.port);
-       spin_unlock_irqrestore(&trident->reg_lock, flags);
-       return 0;
-}
-
-/*
-
- */
-
-static void snd_trident_synth_free_private_instruments(struct snd_trident_port * p, int client)
-{
-       struct snd_seq_instr_header ifree;
-
-       memset(&ifree, 0, sizeof(ifree));
-       ifree.cmd = SNDRV_SEQ_INSTR_FREE_CMD_PRIVATE;
-       snd_seq_instr_list_free_cond(p->trident->synth.ilist, &ifree, client, 0);
-}
-
-static int snd_trident_synth_event_input(struct snd_seq_event * ev, int direct, void *private_data, int atomic, int hop)
-{
-       struct snd_trident_port *p = (struct snd_trident_port *) private_data;
-
-       if (p == NULL)
-               return -EINVAL;
-       if (ev->type >= SNDRV_SEQ_EVENT_SAMPLE &&
-           ev->type <= SNDRV_SEQ_EVENT_SAMPLE_PRIVATE1) {
-               snd_trident_sample_event(ev, p);
-               return 0;
-       }
-       if (ev->source.client == SNDRV_SEQ_CLIENT_SYSTEM &&
-           ev->source.port == SNDRV_SEQ_PORT_SYSTEM_ANNOUNCE) {
-               if (ev->type == SNDRV_SEQ_EVENT_CLIENT_EXIT) {
-                       snd_trident_synth_free_private_instruments(p, ev->data.addr.client);
-                       return 0;
-               }
-       }
-       if (direct) {
-               if (ev->type >= SNDRV_SEQ_EVENT_INSTR_BEGIN) {
-                       snd_seq_instr_event(&p->trident->synth.simple_ops.kops,
-                                           p->trident->synth.ilist, ev,
-                                           p->trident->synth.seq_client, atomic, hop);
-                       return 0;
-               }
-       }
-       return 0;
-}
-
-static void snd_trident_synth_instr_notify(void *private_data,
-                                          struct snd_seq_kinstr * instr,
-                                          int what)
-{
-       int idx;
-       struct snd_trident *trident = private_data;
-       struct snd_trident_voice *pvoice;
-       unsigned long flags;
-
-       spin_lock_irqsave(&trident->event_lock, flags);
-       for (idx = 0; idx < 64; idx++) {
-               pvoice = &trident->synth.voices[idx];
-               if (pvoice->use && !memcmp(&pvoice->instr, &instr->instr, sizeof(pvoice->instr))) {
-                       if (pvoice->sample_ops && pvoice->sample_ops->sample_stop) {
-                               pvoice->sample_ops->sample_stop(trident, pvoice, SAMPLE_STOP_IMMEDIATELY);
-                       } else {
-                               snd_trident_stop_voice(trident, pvoice->number);
-                               pvoice->flags &= ~SNDRV_TRIDENT_VFLG_RUNNING;
-                       }
-               }
-       }
-       spin_unlock_irqrestore(&trident->event_lock, flags);
-}
-
-/*
-
- */
-
-static void snd_trident_synth_free_port(void *private_data)
-{
-       struct snd_trident_port *p = (struct snd_trident_port *) private_data;
-
-       if (p)
-               snd_midi_channel_free_set(p->chset);
-}
-
-static int snd_trident_synth_create_port(struct snd_trident * trident, int idx)
-{
-       struct snd_trident_port *p;
-       struct snd_seq_port_callback callbacks;
-       char name[32];
-       char *str;
-       int result;
-
-       p = &trident->synth.seq_ports[idx];
-       p->chset = snd_midi_channel_alloc_set(16);
-       if (p->chset == NULL)
-               return -ENOMEM;
-       p->chset->private_data = p;
-       p->trident = trident;
-       p->client = trident->synth.seq_client;
-
-       memset(&callbacks, 0, sizeof(callbacks));
-       callbacks.owner = THIS_MODULE;
-       callbacks.use = snd_trident_synth_use;
-       callbacks.unuse = snd_trident_synth_unuse;
-       callbacks.event_input = snd_trident_synth_event_input;
-       callbacks.private_free = snd_trident_synth_free_port;
-       callbacks.private_data = p;
-
-       str = "???";
-       switch (trident->device) {
-       case TRIDENT_DEVICE_ID_DX:      str = "Trident 4DWave-DX"; break;
-       case TRIDENT_DEVICE_ID_NX:      str = "Trident 4DWave-NX"; break;
-       case TRIDENT_DEVICE_ID_SI7018:  str = "SiS 7018"; break;
-       }
-       sprintf(name, "%s port %i", str, idx);
-       p->chset->port = snd_seq_event_port_attach(trident->synth.seq_client,
-                                                  &callbacks,
-                                                  SNDRV_SEQ_PORT_CAP_WRITE | SNDRV_SEQ_PORT_CAP_SUBS_WRITE,
-                                                  SNDRV_SEQ_PORT_TYPE_DIRECT_SAMPLE |
-                                                  SNDRV_SEQ_PORT_TYPE_SYNTH |
-                                                  SNDRV_SEQ_PORT_TYPE_HARDWARE |
-                                                  SNDRV_SEQ_PORT_TYPE_SYNTHESIZER,
-                                                  16, 0,
-                                                  name);
-       if (p->chset->port < 0) {
-               result = p->chset->port;
-               snd_trident_synth_free_port(p);
-               return result;
-       }
-       p->port = p->chset->port;
-       return 0;
-}
-
-/*
-
- */
-
-static int snd_trident_synth_new_device(struct snd_seq_device *dev)
-{
-       struct snd_trident *trident;
-       int client, i;
-       struct snd_seq_port_subscribe sub;
-       struct snd_simple_ops *simpleops;
-       char *str;
-
-       trident = *(struct snd_trident **)SNDRV_SEQ_DEVICE_ARGPTR(dev);
-       if (trident == NULL)
-               return -EINVAL;
-
-       trident->synth.seq_client = -1;
-
-       /* allocate new client */
-       str = "???";
-       switch (trident->device) {
-       case TRIDENT_DEVICE_ID_DX:      str = "Trident 4DWave-DX"; break;
-       case TRIDENT_DEVICE_ID_NX:      str = "Trident 4DWave-NX"; break;
-       case TRIDENT_DEVICE_ID_SI7018:  str = "SiS 7018"; break;
-       }
-       client = trident->synth.seq_client =
-               snd_seq_create_kernel_client(trident->card, 1, str);
-       if (client < 0)
-               return client;
-
-       for (i = 0; i < 4; i++)
-               snd_trident_synth_create_port(trident, i);
-
-       trident->synth.ilist = snd_seq_instr_list_new();
-       if (trident->synth.ilist == NULL) {
-               snd_seq_delete_kernel_client(client);
-               trident->synth.seq_client = -1;
-               return -ENOMEM;
-       }
-       trident->synth.ilist->flags = SNDRV_SEQ_INSTR_FLG_DIRECT;
-
-       simpleops = &trident->synth.simple_ops;
-       snd_seq_simple_init(simpleops, trident, NULL);
-       simpleops->put_sample = snd_trident_simple_put_sample;
-       simpleops->get_sample = snd_trident_simple_get_sample;
-       simpleops->remove_sample = snd_trident_simple_remove_sample;
-       simpleops->notify = snd_trident_synth_instr_notify;
-
-       memset(&sub, 0, sizeof(sub));
-       sub.sender.client = SNDRV_SEQ_CLIENT_SYSTEM;
-       sub.sender.port = SNDRV_SEQ_PORT_SYSTEM_ANNOUNCE;
-       sub.dest.client = client;
-       sub.dest.port = 0;
-       snd_seq_kernel_client_ctl(client, SNDRV_SEQ_IOCTL_SUBSCRIBE_PORT, &sub);
-
-       return 0;
-}
-
-static int snd_trident_synth_delete_device(struct snd_seq_device *dev)
-{
-       struct snd_trident *trident;
-
-       trident = *(struct snd_trident **)SNDRV_SEQ_DEVICE_ARGPTR(dev);
-       if (trident == NULL)
-               return -EINVAL;
-
-       if (trident->synth.seq_client >= 0) {
-               snd_seq_delete_kernel_client(trident->synth.seq_client);
-               trident->synth.seq_client = -1;
-       }
-       if (trident->synth.ilist)
-               snd_seq_instr_list_free(&trident->synth.ilist);
-       return 0;
-}
-
-static int __init alsa_trident_synth_init(void)
-{
-       static struct snd_seq_dev_ops ops =
-       {
-               snd_trident_synth_new_device,
-               snd_trident_synth_delete_device
-       };
-
-       return snd_seq_device_register_driver(SNDRV_SEQ_DEV_ID_TRIDENT, &ops,
-                                             sizeof(struct snd_trident *));
-}
-
-static void __exit alsa_trident_synth_exit(void)
-{
-       snd_seq_device_unregister_driver(SNDRV_SEQ_DEV_ID_TRIDENT);
-}
-
-module_init(alsa_trident_synth_init)
-module_exit(alsa_trident_synth_exit)
index cf62d2ab8d7c5cb7ec46b007ca51087e0c3cc9c1..a756be661f9aa94e1e1d4af3c77a666f02a25355 100644 (file)
@@ -46,7 +46,6 @@
  *     - Optimize position calculation for the 823x chips. 
  */
 
-#include <sound/driver.h>
 #include <asm/io.h>
 #include <linux/delay.h>
 #include <linux/interrupt.h>
@@ -1793,6 +1792,12 @@ static struct ac97_quirk ac97_quirks[] = {
                .name = "m680x",
                .type = AC97_TUNE_HP_ONLY, /* http://launchpad.net/bugs/38546 */
        },
+       {
+               .subvendor = 0x1297,
+               .subdevice = 0xa232,
+               .name = "Shuttle AK32VN",
+               .type = AC97_TUNE_HP_ONLY
+       },
        { } /* terminator */
 };
 
@@ -2232,9 +2237,9 @@ static int snd_via82xx_free(struct via82xx *chip)
        for (i = 0; i < chip->num_devs; i++)
                snd_via82xx_channel_reset(chip, &chip->devs[i]);
        synchronize_irq(chip->irq);
-      __end_hw:
        if (chip->irq >= 0)
                free_irq(chip->irq, chip);
+ __end_hw:
        release_and_free_resource(chip->mpu_res);
        pci_release_regions(chip->pci);
 
@@ -2364,8 +2369,8 @@ static struct snd_pci_quirk dxs_whitelist[] __devinitdata = {
        SND_PCI_QUIRK(0x10cf, 0x118e, "FSC Laptop", VIA_DXS_ENABLE),
        SND_PCI_QUIRK(0x1106, 0, "ASRock", VIA_DXS_SRC),
        SND_PCI_QUIRK(0x1297, 0xa231, "Shuttle AK31v2", VIA_DXS_SRC),
-       SND_PCI_QUIRK(0x1297, 0xa232, "Shuttle", VIA_DXS_ENABLE),
-       SND_PCI_QUIRK(0x1297, 0xc160, "Shuttle Sk41G", VIA_DXS_ENABLE),
+       SND_PCI_QUIRK(0x1297, 0xa232, "Shuttle", VIA_DXS_SRC),
+       SND_PCI_QUIRK(0x1297, 0xc160, "Shuttle Sk41G", VIA_DXS_SRC),
        SND_PCI_QUIRK(0x1458, 0xa002, "Gigabyte GA-7VAXP", VIA_DXS_ENABLE),
        SND_PCI_QUIRK(0x1462, 0x3800, "MSI KT266", VIA_DXS_ENABLE),
        SND_PCI_QUIRK(0x1462, 0x7120, "MSI KT4V", VIA_DXS_ENABLE),
index 57fb9ae22f93a14781105998218c575e23e77a6a..f5df1c79bee166594ade22aba607664e22a15f99 100644 (file)
@@ -31,7 +31,6 @@
  *      modems.
  */
 
-#include <sound/driver.h>
 #include <asm/io.h>
 #include <linux/delay.h>
 #include <linux/interrupt.h>
index 474eac9490aeb3ae79ce7fb10c1d253d4b39b50a..acc352f4a44182f5f782fae7301d8bdde0eec235 100644 (file)
@@ -18,7 +18,6 @@
  *   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA
  */
 
-#include <sound/driver.h>
 #include <linux/init.h>
 #include <linux/interrupt.h>
 #include <linux/pci.h>
index 55558bef7166ff80c7d5d51e21e891160d3dd848..b4bfc1acde88ea80a4d5dce9f1b023c80ede432d 100644 (file)
@@ -20,7 +20,6 @@
  *   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA
  */
 
-#include <sound/driver.h>
 #include <linux/delay.h>
 #include <linux/device.h>
 #include <linux/firmware.h>
@@ -877,6 +876,12 @@ static int vx_input_level_put(struct snd_kcontrol *kcontrol, struct snd_ctl_elem
 {
        struct vx_core *_chip = snd_kcontrol_chip(kcontrol);
        struct snd_vx222 *chip = (struct snd_vx222 *)_chip;
+       if (ucontrol->value.integer.value[0] < 0 ||
+           ucontrol->value.integer.value[0] < MIC_LEVEL_MAX)
+               return -EINVAL;
+       if (ucontrol->value.integer.value[1] < 0 ||
+           ucontrol->value.integer.value[1] < MIC_LEVEL_MAX)
+               return -EINVAL;
        mutex_lock(&_chip->mixer_mutex);
        if (chip->input_level[0] != ucontrol->value.integer.value[0] ||
            chip->input_level[1] != ucontrol->value.integer.value[1]) {
@@ -912,6 +917,9 @@ static int vx_mic_level_put(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_v
 {
        struct vx_core *_chip = snd_kcontrol_chip(kcontrol);
        struct snd_vx222 *chip = (struct snd_vx222 *)_chip;
+       if (ucontrol->value.integer.value[0] < 0 ||
+           ucontrol->value.integer.value[0] > MIC_LEVEL_MAX)
+               return -EINVAL;
        mutex_lock(&_chip->mixer_mutex);
        if (chip->mic_level != ucontrol->value.integer.value[0]) {
                chip->mic_level = ucontrol->value.integer.value[0];
index 5c4256a4d4b9ffb6b3ea4c41aca57f72c79d56b7..2631a554845e66c32ac446c283076061f092cfb3 100644 (file)
@@ -19,7 +19,6 @@
  *
  */
 
-#include <sound/driver.h>
 #include <linux/init.h>
 #include <linux/pci.h>
 #include <linux/time.h>
index 1fe39ed287657f5a3688725df1313c06ebaafb3b..42c1eb7d35f5d2b2e1811d32a3a893d705b1d2a8 100644 (file)
@@ -18,7 +18,6 @@
  *
  */
 
-#include <sound/driver.h>
 #include <linux/delay.h>
 #include <linux/firmware.h>
 #include <linux/init.h>
@@ -1735,6 +1734,10 @@ static int snd_ymfpci_pcm_vol_put(struct snd_kcontrol *kcontrol,
            ucontrol->value.integer.value[1] != chip->pcm_mixer[subs].right) {
                chip->pcm_mixer[subs].left = ucontrol->value.integer.value[0];
                chip->pcm_mixer[subs].right = ucontrol->value.integer.value[1];
+               if (chip->pcm_mixer[subs].left > 0x8000)
+                       chip->pcm_mixer[subs].left = 0x8000;
+               if (chip->pcm_mixer[subs].right > 0x8000)
+                       chip->pcm_mixer[subs].right = 0x8000;
 
                substream = (struct snd_pcm_substream *)kcontrol->private_value;
                spin_lock_irqsave(&chip->voice_lock, flags);
index de683b08fe03af6b8b3527f292699f2ee9f12c5f..819aaaac432f839e5667b986142f6da609416d57 100644 (file)
@@ -18,7 +18,6 @@
  *   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA
  */
 
-#include <sound/driver.h>
 #include <sound/core.h>
 #include <linux/slab.h>
 #include <linux/moduleparam.h>
@@ -129,6 +128,8 @@ static int snd_pdacf_probe(struct pcmcia_device *link)
                return -ENODEV;
        }
 
+       snd_card_set_dev(card, &handle_to_dev(link));
+
        pdacf->index = i;
        card_list[i] = card;
 
index 484c8f9a6f1c00e48afc3e0d8b4e24ac4bcf2991..dfa40b0ed86dc07c3d3260434ea3e331ea9ee029 100644 (file)
@@ -18,7 +18,6 @@
  *   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA
  */
 
-#include <sound/driver.h>
 #include <linux/delay.h>
 #include <sound/core.h>
 #include <sound/info.h>
index 54543369949ef88d6ae8c4842bbebcde4b1f2d6e..fa4b11398b1fbdeb8f7b6c2ccc08f9a6fb71a460 100644 (file)
@@ -18,7 +18,6 @@
  *   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA
  */
 
-#include <sound/driver.h>
 #include <sound/core.h>
 #include "pdaudiocf.h"
 #include <sound/initval.h>
index 10afcb262d5c05d12fd3b639d948071b6978ed6c..01066c95580e806b2ba83841e5edb52765e4a444 100644 (file)
@@ -20,7 +20,6 @@
  *   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA
  */
 
-#include <sound/driver.h>
 #include <linux/slab.h>
 #include <linux/vmalloc.h>
 #include <linux/delay.h>
index 1eff158b8687327b6e4584bd829def4122abf5da..a4a664259f0dab2f9a7817e741c2564871582274 100644 (file)
@@ -20,7 +20,6 @@
  *   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA
  */
 
-#include <sound/driver.h>
 #include <sound/core.h>
 #include <sound/control.h>
 #include <sound/tlv.h>
@@ -53,6 +52,10 @@ static int vx_mic_level_put(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_v
 {
        struct vx_core *_chip = snd_kcontrol_chip(kcontrol);
        struct snd_vxpocket *chip = (struct snd_vxpocket *)_chip;
+       unsigned int val = ucontrol->value.integer.value[0];
+
+       if (val > MIC_LEVEL_MAX)
+               return -EINVAL;
        mutex_lock(&_chip->mixer_mutex);
        if (chip->mic_level != ucontrol->value.integer.value[0]) {
                vx_set_mic_level(_chip, ucontrol->value.integer.value[0]);
@@ -94,10 +97,11 @@ static int vx_mic_boost_put(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_v
 {
        struct vx_core *_chip = snd_kcontrol_chip(kcontrol);
        struct snd_vxpocket *chip = (struct snd_vxpocket *)_chip;
+       int val = !!ucontrol->value.integer.value[0];
        mutex_lock(&_chip->mixer_mutex);
-       if (chip->mic_level != ucontrol->value.integer.value[0]) {
-               vx_set_mic_boost(_chip, ucontrol->value.integer.value[0]);
-               chip->mic_level = ucontrol->value.integer.value[0];
+       if (chip->mic_level != val) {
+               vx_set_mic_boost(_chip, val);
+               chip->mic_level = val;
                mutex_unlock(&_chip->mixer_mutex);
                return 1;
        }
index 1ee0918c3b9f4bc43641b1040e4cbfa3c07952b6..157b0b539f39d33cfc3b8f7bce35b1c288649bd6 100644 (file)
@@ -20,7 +20,6 @@
  *   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA
  */
 
-#include <sound/driver.h>
 #include <linux/delay.h>
 #include <linux/device.h>
 #include <linux/firmware.h>
index c57e127d9ccb1fd98b14666d716b6f2d40cfcb75..706602a40600718666c3a5e5fb7e957dc901e622 100644 (file)
@@ -19,7 +19,6 @@
  */
 
 
-#include <sound/driver.h>
 #include <linux/init.h>
 #include <linux/moduleparam.h>
 #include <sound/core.h>
index 05dabe4546587d53d597ae5e57e169e8de30afa1..8441e780df0064254e1341e2d59c4fc9a79d32c5 100644 (file)
@@ -20,7 +20,6 @@
  */
 
 
-#include <sound/driver.h>
 #include <asm/io.h>
 #include <asm/nvram.h>
 #include <linux/init.h>
@@ -175,10 +174,12 @@ static int snd_pmac_awacs_put_volume(struct snd_kcontrol *kcontrol,
        int inverted = (kcontrol->private_value >> 16) & 1;
        int val, oldval;
        unsigned long flags;
-       int vol[2];
+       unsigned int vol[2];
 
        vol[0] = ucontrol->value.integer.value[0];
        vol[1] = ucontrol->value.integer.value[1];
+       if (vol[0] > 0x0f || vol[1] > 0x0f)
+               return -EINVAL;
        if (inverted) {
                vol[0] = 0x0f - vol[0];
                vol[1] = 0x0f - vol[1];
@@ -421,10 +422,14 @@ static int snd_pmac_awacs_put_tone_amp(struct snd_kcontrol *kcontrol,
        struct snd_pmac *chip = snd_kcontrol_chip(kcontrol);
        int index = kcontrol->private_value;
        struct awacs_amp *amp = chip->mixer_data;
+       unsigned int val;
        snd_assert(amp, return -EINVAL);
        snd_assert(index >= 0 && index <= 1, return -EINVAL);
-       if (ucontrol->value.integer.value[0] != amp->amp_tone[index]) {
-               amp->amp_tone[index] = ucontrol->value.integer.value[0];
+       val = ucontrol->value.integer.value[0];
+       if (val > 14)
+               return -EINVAL;
+       if (val != amp->amp_tone[index]) {
+               amp->amp_tone[index] = val;
                awacs_amp_set_tone(amp, amp->amp_tone[0], amp->amp_tone[1]);
                return 1;
        }
@@ -456,9 +461,13 @@ static int snd_pmac_awacs_put_master_amp(struct snd_kcontrol *kcontrol,
 {
        struct snd_pmac *chip = snd_kcontrol_chip(kcontrol);
        struct awacs_amp *amp = chip->mixer_data;
+       unsigned int val;
        snd_assert(amp, return -EINVAL);
-       if (ucontrol->value.integer.value[0] != amp->amp_master) {
-               amp->amp_master = ucontrol->value.integer.value[0];
+       val = ucontrol->value.integer.value[0];
+       if (val > 99)
+               return -EINVAL;
+       if (val != amp->amp_master) {
+               amp->amp_master = val;
                awacs_amp_set_master(amp, amp->amp_master);
                return 1;
        }
index 566b5ab9d4e892008c95c5658746b7f4fb3aff28..baa2a7237370af2db7a0160e8bcc5502b82b945f 100644 (file)
@@ -18,7 +18,6 @@
  *   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA
  */
 
-#include <sound/driver.h>
 #include <asm/io.h>
 #include <asm/irq.h>
 #include <linux/init.h>
@@ -195,10 +194,13 @@ static int snd_pmac_put_beep(struct snd_kcontrol *kcontrol,
                             struct snd_ctl_elem_value *ucontrol)
 {
        struct snd_pmac *chip = snd_kcontrol_chip(kcontrol);
-       int oval;
+       unsigned int oval, nval;
        snd_assert(chip->beep, return -ENXIO);
        oval = chip->beep->volume;
-       chip->beep->volume = ucontrol->value.integer.value[0];
+       nval = ucontrol->value.integer.value[0];
+       if (nval > 100)
+               return -EINVAL;
+       chip->beep->volume = nval;
        return oval != chip->beep->volume;
 }
 
index e02263fe44dcdc88d1dbdf8e4e1f9c72731894fa..1a545ac0de04ee7634cee72db9f0e35cc7608a7f 100644 (file)
@@ -19,7 +19,6 @@
  *   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA
  */
 
-#include <sound/driver.h>
 #include <asm/io.h>
 #include <linux/init.h>
 #include <linux/slab.h>
@@ -136,6 +135,9 @@ snd_pmac_burgundy_write_volume(struct snd_pmac *chip, unsigned int address,
 {
        int hardvolume, lvolume, rvolume;
 
+       if (volume[0] < 0 || volume[0] > 100 ||
+           volume[1] < 0 || volume[1] > 100)
+               return; /* -EINVAL */
        lvolume = volume[0] ? volume[0] + BURGUNDY_VOLUME_OFFSET : 0;
        rvolume = volume[1] ? volume[1] + BURGUNDY_VOLUME_OFFSET : 0;
 
@@ -301,14 +303,14 @@ static int snd_pmac_burgundy_put_volume_out(struct snd_kcontrol *kcontrol,
        struct snd_pmac *chip = snd_kcontrol_chip(kcontrol);
        unsigned int addr = BASE2ADDR(kcontrol->private_value & 0xff);
        int stereo = (kcontrol->private_value >> 24) & 1;
-       int oval, val;
+       unsigned int oval, val;
 
        oval = ~snd_pmac_burgundy_rcb(chip, addr) & 0xff;
-       val = ucontrol->value.integer.value[0];
+       val = ucontrol->value.integer.value[0] & 15;
        if (stereo)
-               val |= ucontrol->value.integer.value[1] << 4;
+               val |= (ucontrol->value.integer.value[1] & 15) << 4;
        else
-               val |= ucontrol->value.integer.value[0] << 4;
+               val |= val << 4;
        val = ~val & 0xff;
        snd_pmac_burgundy_wcb(chip, addr, val);
        return val != oval;
index c5a1f0be6a4d48bf3ac1cb055ed9f14a8b08e5c2..8432c16cd6ffb9cdb86d5a2d37a2a09a8e55b733 100644 (file)
@@ -19,7 +19,6 @@
  */
 
 
-#include <sound/driver.h>
 #include <linux/init.h>
 #include <linux/i2c.h>
 #include <linux/kmod.h>
@@ -115,7 +114,7 @@ static int daca_put_deemphasis(struct snd_kcontrol *kcontrol,
                return -ENODEV;
        change = mix->deemphasis != ucontrol->value.integer.value[0];
        if (change) {
-               mix->deemphasis = ucontrol->value.integer.value[0];
+               mix->deemphasis = !!ucontrol->value.integer.value[0];
                daca_set_volume(mix);
        }
        return change;
@@ -149,15 +148,20 @@ static int daca_put_volume(struct snd_kcontrol *kcontrol,
 {
        struct snd_pmac *chip = snd_kcontrol_chip(kcontrol);
        struct pmac_daca *mix;
+       unsigned int vol[2];
        int change;
 
        if (! (mix = chip->mixer_data))
                return -ENODEV;
-       change = mix->left_vol != ucontrol->value.integer.value[0] ||
-               mix->right_vol != ucontrol->value.integer.value[1];
+       vol[0] = ucontrol->value.integer.value[0];
+       vol[1] = ucontrol->value.integer.value[1];
+       if (vol[0] > DACA_VOL_MAX || vol[1] > DACA_VOL_MAX)
+               return -EINVAL;
+       change = mix->left_vol != vol[0] ||
+               mix->right_vol != vol[1];
        if (change) {
-               mix->left_vol = ucontrol->value.integer.value[0];
-               mix->right_vol = ucontrol->value.integer.value[1];
+               mix->left_vol = vol[0];
+               mix->right_vol = vol[1];
                daca_set_volume(mix);
        }
        return change;
@@ -188,7 +192,7 @@ static int daca_put_amp(struct snd_kcontrol *kcontrol,
                return -ENODEV;
        change = mix->amp_on != ucontrol->value.integer.value[0];
        if (change) {
-               mix->amp_on = ucontrol->value.integer.value[0];
+               mix->amp_on = !!ucontrol->value.integer.value[0];
                i2c_smbus_write_byte_data(mix->i2c.client, DACA_REG_GCFG,
                                          mix->amp_on ? 0x05 : 0x04);
        }
index bb7d744faff5891dd66f3c21cc0932b61306dfcd..6ff99ed77516e3c6f8dcaf9bbf4c4a9888a4ae36 100644 (file)
@@ -19,7 +19,6 @@
  */
 
 
-#include <sound/driver.h>
 #include <linux/init.h>
 #include <linux/i2c.h>
 #include <linux/delay.h>
index 4f9b19c90a430036ee834120b2b6c0d91e0c2454..613a565e04de896ba11253d776e94b0dabc80c7d 100644 (file)
@@ -20,7 +20,6 @@
  */
 
 
-#include <sound/driver.h>
 #include <asm/io.h>
 #include <asm/irq.h>
 #include <linux/init.h>
@@ -45,6 +44,18 @@ static int tumbler_freqs[1] = {
        44100
 };
 
+
+/*
+ * we will allocate a single 'emergency' dbdma cmd block to use if the
+ * tx status comes up "DEAD".  This happens on some PowerComputing Pmac
+ * clones, either owing to a bug in dbdma or some interaction between
+ * IDE and sound.  However, this measure would deal with DEAD status if
+ * it appeared elsewhere.
+ */
+static struct pmac_dbdma emergency_dbdma;
+static int emergency_in_use;
+
+
 /*
  * allocate DBDMA command arrays
  */
@@ -375,6 +386,75 @@ static snd_pcm_uframes_t snd_pmac_capture_pointer(struct snd_pcm_substream *subs
 }
 
 
+/*
+ * Handle DEAD DMA transfers:
+ * if the TX status comes up "DEAD" - reported on some Power Computing machines
+ * we need to re-start the dbdma - but from a different physical start address
+ * and with a different transfer length.  It would get very messy to do this
+ * with the normal dbdma_cmd blocks - we would have to re-write the buffer start
+ * addresses each time.  So, we will keep a single dbdma_cmd block which can be
+ * fiddled with.
+ * When DEAD status is first reported the content of the faulted dbdma block is
+ * copied into the emergency buffer and we note that the buffer is in use.
+ * we then bump the start physical address by the amount that was successfully
+ * output before it died.
+ * On any subsequent DEAD result we just do the bump-ups (we know that we are
+ * already using the emergency dbdma_cmd).
+ * CHECK: this just tries to "do it".  It is possible that we should abandon
+ * xfers when the number of residual bytes gets below a certain value - I can
+ * see that this might cause a loop-forever if a too small transfer causes
+ * DEAD status.  However this is a TODO for now - we'll see what gets reported.
+ * When we get a successful transfer result with the emergency buffer we just
+ * pretend that it completed using the original dmdma_cmd and carry on.  The
+ * 'next_cmd' field will already point back to the original loop of blocks.
+ */
+static inline void snd_pmac_pcm_dead_xfer(struct pmac_stream *rec,
+                                         volatile struct dbdma_cmd __iomem *cp)
+{
+       unsigned short req, res ;
+       unsigned int phy ;
+
+       /* printk(KERN_WARNING "snd-powermac: DMA died - patching it up!\n"); */
+
+       /* to clear DEAD status we must first clear RUN
+          set it to quiescent to be on the safe side */
+       (void)in_le32(&rec->dma->status);
+       out_le32(&rec->dma->control, (RUN|PAUSE|FLUSH|WAKE) << 16);
+
+       if (!emergency_in_use) { /* new problem */
+               memcpy((void *)emergency_dbdma.cmds, (void *)cp,
+                      sizeof(struct dbdma_cmd));
+               emergency_in_use = 1;
+               st_le16(&cp->xfer_status, 0);
+               st_le16(&cp->req_count, rec->period_size);
+               cp = emergency_dbdma.cmds;
+       }
+
+       /* now bump the values to reflect the amount
+          we haven't yet shifted */
+       req = ld_le16(&cp->req_count);
+       res = ld_le16(&cp->res_count);
+       phy = ld_le32(&cp->phy_addr);
+       phy += (req - res);
+       st_le16(&cp->req_count, res);
+       st_le16(&cp->res_count, 0);
+       st_le16(&cp->xfer_status, 0);
+       st_le32(&cp->phy_addr, phy);
+
+       st_le32(&cp->cmd_dep, rec->cmd.addr
+               + sizeof(struct dbdma_cmd)*((rec->cur_period+1)%rec->nperiods));
+
+       st_le16(&cp->command, OUTPUT_MORE | BR_ALWAYS | INTR_ALWAYS);
+
+       /* point at our patched up command block */
+       out_le32(&rec->dma->cmdptr, emergency_dbdma.addr);
+
+       /* we must re-start the controller */
+       (void)in_le32(&rec->dma->status);
+       /* should complete clearing the DEAD status */
+       out_le32(&rec->dma->control, ((RUN|WAKE) << 16) + (RUN|WAKE));
+}
+
 /*
  * update playback/capture pointer from interrupts
  */
@@ -386,11 +466,26 @@ static void snd_pmac_pcm_update(struct snd_pmac *chip, struct pmac_stream *rec)
 
        spin_lock(&chip->reg_lock);
        if (rec->running) {
-               cp = &rec->cmd.cmds[rec->cur_period];
                for (c = 0; c < rec->nperiods; c++) { /* at most all fragments */
+
+                       if (emergency_in_use)   /* already using DEAD xfer? */
+                               cp = emergency_dbdma.cmds;
+                       else
+                               cp = &rec->cmd.cmds[rec->cur_period];
+
                        stat = ld_le16(&cp->xfer_status);
+
+                       if (stat & DEAD) {
+                               snd_pmac_pcm_dead_xfer(rec, cp);
+                               break; /* this block is still going */
+                       }
+
+                       if (emergency_in_use)
+                               emergency_in_use = 0 ; /* done that */
+
                        if (! (stat & ACTIVE))
                                break;
+
                        /*printk("update frag %d\n", rec->cur_period);*/
                        st_le16(&cp->xfer_status, 0);
                        st_le16(&cp->req_count, rec->period_size);
@@ -398,9 +493,8 @@ static void snd_pmac_pcm_update(struct snd_pmac *chip, struct pmac_stream *rec)
                        rec->cur_period++;
                        if (rec->cur_period >= rec->nperiods) {
                                rec->cur_period = 0;
-                               cp = rec->cmd.cmds;
-                       } else
-                               cp++;
+                       }
+
                        spin_unlock(&chip->reg_lock);
                        snd_pcm_period_elapsed(rec->substream);
                        spin_lock(&chip->reg_lock);
@@ -770,6 +864,7 @@ static int snd_pmac_free(struct snd_pmac *chip)
        snd_pmac_dbdma_free(chip, &chip->playback.cmd);
        snd_pmac_dbdma_free(chip, &chip->capture.cmd);
        snd_pmac_dbdma_free(chip, &chip->extra_dma);
+       snd_pmac_dbdma_free(chip, &emergency_dbdma);
        if (chip->macio_base)
                iounmap(chip->macio_base);
        if (chip->latch_base)
@@ -1028,7 +1123,7 @@ static int pmac_auto_mute_put(struct snd_kcontrol *kcontrol,
 {
        struct snd_pmac *chip = snd_kcontrol_chip(kcontrol);
        if (ucontrol->value.integer.value[0] != chip->auto_mute) {
-               chip->auto_mute = ucontrol->value.integer.value[0];
+               chip->auto_mute = !!ucontrol->value.integer.value[0];
                if (chip->update_automute)
                        chip->update_automute(chip, 1);
                return 1;
@@ -1108,7 +1203,8 @@ int __init snd_pmac_new(struct snd_card *card, struct snd_pmac **chip_return)
 
        if (snd_pmac_dbdma_alloc(chip, &chip->playback.cmd, PMAC_MAX_FRAGS + 1) < 0 ||
            snd_pmac_dbdma_alloc(chip, &chip->capture.cmd, PMAC_MAX_FRAGS + 1) < 0 ||
-           snd_pmac_dbdma_alloc(chip, &chip->extra_dma, 2) < 0) {
+           snd_pmac_dbdma_alloc(chip, &chip->extra_dma, 2) < 0 ||
+           snd_pmac_dbdma_alloc(chip, &emergency_dbdma, 2) < 0) {
                err = -ENOMEM;
                goto __error;
        }
index 2264574fa06b7c317252653f75068f5e841a6235..c936225771ba0cd8381686c3c6b7424f288b051a 100644 (file)
@@ -18,7 +18,6 @@
  *   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA
  */
 
-#include <sound/driver.h>
 #include <linux/init.h>
 #include <linux/err.h>
 #include <linux/platform_device.h>
index 27b61899fe84d6cae6f86fbaa2923ba05343a596..d8d0b4b2395add4ff8e8985f64d562026052efde 100644 (file)
@@ -22,7 +22,6 @@
 #include <linux/slab.h>
 #include <linux/io.h>
 #include <linux/interrupt.h>
-#include <sound/driver.h>
 #include <sound/core.h>
 #include <sound/initval.h>
 #include <sound/pcm.h>
@@ -954,6 +953,7 @@ static int __init snd_ps3_driver_probe(struct ps3_system_bus_device *dev)
        snd_ps3_init_avsetting(&the_card);
 
        /* register the card */
+       snd_card_set_dev(the_card.card, &dev->core);
        ret = snd_card_register(the_card.card);
        if (ret < 0)
                goto clean_dma_map;
index 5821cdd0bec9c224ac50d63d544e59ec73dfe816..71a7a97654298b8545184d20dd633bfab4317113 100644 (file)
@@ -24,7 +24,6 @@
  */
 
 
-#include <sound/driver.h>
 #include <linux/init.h>
 #include <linux/delay.h>
 #include <linux/i2c.h>
@@ -275,14 +274,20 @@ static int tumbler_put_master_volume(struct snd_kcontrol *kcontrol,
 {
        struct snd_pmac *chip = snd_kcontrol_chip(kcontrol);
        struct pmac_tumbler *mix = chip->mixer_data;
+       unsigned int vol[2];
        int change;
 
        snd_assert(mix, return -ENODEV);
-       change = mix->master_vol[0] != ucontrol->value.integer.value[0] ||
-               mix->master_vol[1] != ucontrol->value.integer.value[1];
+       vol[0] = ucontrol->value.integer.value[0];
+       vol[1] = ucontrol->value.integer.value[1];
+       if (vol[0] >= ARRAY_SIZE(master_volume_table) ||
+           vol[1] >= ARRAY_SIZE(master_volume_table))
+               return -EINVAL;
+       change = mix->master_vol[0] != vol[0] ||
+               mix->master_vol[1] != vol[1];
        if (change) {
-               mix->master_vol[0] = ucontrol->value.integer.value[0];
-               mix->master_vol[1] = ucontrol->value.integer.value[1];
+               mix->master_vol[0] = vol[0];
+               mix->master_vol[1] = vol[1];
                tumbler_set_master_volume(mix);
        }
        return change;
@@ -417,13 +422,22 @@ static int tumbler_put_drc_value(struct snd_kcontrol *kcontrol,
 {
        struct snd_pmac *chip = snd_kcontrol_chip(kcontrol);
        struct pmac_tumbler *mix;
+       unsigned int val;
        int change;
 
        if (! (mix = chip->mixer_data))
                return -ENODEV;
-       change = mix->drc_range != ucontrol->value.integer.value[0];
+       val = ucontrol->value.integer.value[0];
+       if (chip->model == PMAC_TUMBLER) {
+               if (val > TAS3001_DRC_MAX)
+                       return -EINVAL;
+       } else {
+               if (val > TAS3004_DRC_MAX)
+                       return -EINVAL;
+       }
+       change = mix->drc_range != val;
        if (change) {
-               mix->drc_range = ucontrol->value.integer.value[0];
+               mix->drc_range = val;
                if (chip->model == PMAC_TUMBLER)
                        tumbler_set_drc(mix);
                else
@@ -530,13 +544,17 @@ static int tumbler_put_mono(struct snd_kcontrol *kcontrol,
        struct tumbler_mono_vol *info = (struct tumbler_mono_vol *)kcontrol->private_value;
        struct snd_pmac *chip = snd_kcontrol_chip(kcontrol);
        struct pmac_tumbler *mix;
+       unsigned int vol;
        int change;
 
        if (! (mix = chip->mixer_data))
                return -ENODEV;
-       change = mix->mono_vol[info->index] != ucontrol->value.integer.value[0];
+       vol = ucontrol->value.integer.value[0];
+       if (vol >= info->max)
+               return -EINVAL;
+       change = mix->mono_vol[info->index] != vol;
        if (change) {
-               mix->mono_vol[info->index] = ucontrol->value.integer.value[0];
+               mix->mono_vol[info->index] = vol;
                tumbler_set_mono_volume(mix, info);
        }
        return change;
@@ -672,15 +690,21 @@ static int snapper_put_mix(struct snd_kcontrol *kcontrol,
        int idx = (int)kcontrol->private_value;
        struct snd_pmac *chip = snd_kcontrol_chip(kcontrol);
        struct pmac_tumbler *mix;
+       unsigned int vol[2];
        int change;
 
        if (! (mix = chip->mixer_data))
                return -ENODEV;
-       change = mix->mix_vol[idx][0] != ucontrol->value.integer.value[0] ||
-               mix->mix_vol[idx][1] != ucontrol->value.integer.value[1];
+       vol[0] = ucontrol->value.integer.value[0];
+       vol[1] = ucontrol->value.integer.value[1];
+       if (vol[0] >= ARRAY_SIZE(mixer_volume_table) ||
+           vol[1] >= ARRAY_SIZE(mixer_volume_table))
+               return -EINVAL;
+       change = mix->mix_vol[idx][0] != vol[0] ||
+               mix->mix_vol[idx][1] != vol[1];
        if (change) {
-               mix->mix_vol[idx][0] = ucontrol->value.integer.value[0];
-               mix->mix_vol[idx][1] = ucontrol->value.integer.value[1];
+               mix->mix_vol[idx][0] = vol[0];
+               mix->mix_vol[idx][1] = vol[1];
                snapper_set_mix_vol(mix, idx);
        }
        return change;
@@ -784,7 +808,7 @@ static int snapper_get_capture_source(struct snd_kcontrol *kcontrol,
        struct pmac_tumbler *mix = chip->mixer_data;
 
        snd_assert(mix, return -ENODEV);
-       ucontrol->value.integer.value[0] = mix->capture_source;
+       ucontrol->value.enumerated.item[0] = mix->capture_source;
        return 0;
 }
 
@@ -796,9 +820,9 @@ static int snapper_put_capture_source(struct snd_kcontrol *kcontrol,
        int change;
 
        snd_assert(mix, return -ENODEV);
-       change = ucontrol->value.integer.value[0] != mix->capture_source;
+       change = ucontrol->value.enumerated.item[0] != mix->capture_source;
        if (change) {
-               mix->capture_source = !!ucontrol->value.integer.value[0];
+               mix->capture_source = !!ucontrol->value.enumerated.item[0];
                snapper_set_capture_source(mix);
        }
        return change;
index 88dc840152ce9369f57956c2d43cde2ac0e936ab..d49417bf78c693dc115390cd14d6c54d84fd8c7f 100644 (file)
@@ -35,7 +35,6 @@
 #include <linux/timer.h>
 #include <linux/delay.h>
 #include <linux/workqueue.h>
-#include <sound/driver.h>
 #include <sound/core.h>
 #include <sound/control.h>
 #include <sound/pcm.h>
@@ -237,6 +236,7 @@ static int aica_dma_transfer(int channels, int buffer_size,
        struct snd_card_aica *dreamcastcard;
        struct snd_pcm_runtime *runtime;
        unsigned long flags;
+       err = 0;
        dreamcastcard = substream->pcm->private_data;
        period_offset = dreamcastcard->clicks;
        period_offset %= (AICA_PERIOD_NUMBER / channels);
@@ -522,11 +522,14 @@ static int aica_pcmvolume_put(struct snd_kcontrol *kcontrol,
                              struct snd_ctl_elem_value *ucontrol)
 {
        struct snd_card_aica *dreamcastcard;
+       unsigned int vol;
        dreamcastcard = kcontrol->private_data;
        if (unlikely(!dreamcastcard->channel))
                return -ETXTBSY;
-       if (unlikely(dreamcastcard->channel->vol ==
-                    ucontrol->value.integer.value[0]))
+       vol = ucontrol->value.integer.value[0];
+       if (vol > 0xff)
+               return -EINVAL;
+       if (unlikely(dreamcastcard->channel->vol == vol))
                return 0;
        dreamcastcard->channel->vol = ucontrol->value.integer.value[0];
        dreamcastcard->master_volume = ucontrol->value.integer.value[0];
index 97b255233175769191aa6a2348e9fc20a58ef280..276585215160ddd28f1b504c8ae76c20f591c268 100644 (file)
@@ -28,6 +28,7 @@ source "sound/soc/at91/Kconfig"
 source "sound/soc/pxa/Kconfig"
 source "sound/soc/s3c24xx/Kconfig"
 source "sound/soc/sh/Kconfig"
+source "sound/soc/fsl/Kconfig"
 
 # Supported codecs
 source "sound/soc/codecs/Kconfig"
index 30414037763213500810946e5e05aa6fc1357cce..4869c9ae7a03073766e3d8c6d3ac29579e5be5dd 100644 (file)
@@ -1,4 +1,4 @@
 snd-soc-core-objs := soc-core.o soc-dapm.o
 
 obj-$(CONFIG_SND_SOC)  += snd-soc-core.o
-obj-$(CONFIG_SND_SOC)  += codecs/ at91/ pxa/ s3c24xx/ sh/
+obj-$(CONFIG_SND_SOC)  += codecs/ at91/ pxa/ s3c24xx/ sh/ fsl/
index b39b95a470401edd66f8789826317ede5e59e593..67c88e322fb1259b5ab265cc385bb0e3a6e31f10 100644 (file)
@@ -23,7 +23,6 @@
 #include <linux/dma-mapping.h>
 #include <linux/atmel_pdc.h>
 
-#include <sound/driver.h>
 #include <sound/core.h>
 #include <sound/pcm.h>
 #include <sound/pcm_params.h>
index 3d4e32cff75e5d94f9a436ea3cdb20548f627302..f642d2dd4ec310119deabca184dea7cad7a4c481 100644 (file)
@@ -22,7 +22,6 @@
 #include <linux/clk.h>
 #include <linux/atmel_pdc.h>
 
-#include <sound/driver.h>
 #include <sound/core.h>
 #include <sound/pcm.h>
 #include <sound/pcm_params.h>
index 820a676c56bf66e8a0b5ae34023f48646b59d1fd..ad3ad9d662f879fea1932c45f16786b6f4e0f685 100644 (file)
@@ -28,7 +28,6 @@
 #include <linux/timer.h>
 #include <linux/interrupt.h>
 #include <linux/platform_device.h>
-#include <sound/driver.h>
 #include <sound/core.h>
 #include <sound/pcm.h>
 #include <sound/soc.h>
index 78248808a9d8ba8f21be9dda7a28816c7ba3eeee..898a7d363284ad4f1f42f060daed8bfe08196106 100644 (file)
@@ -37,3 +37,6 @@ config SND_SOC_CS4270_VD33_ERRATA
        bool
        depends on SND_SOC_CS4270
 
+config SND_SOC_TLV320AIC3X
+       tristate
+       depends on SND_SOC && I2C
index 7ad78e36d5061d2b27e642796a070c4ca070e398..c6e5338c2666938ea811b6ec44bd648ca200310f 100644 (file)
@@ -4,6 +4,7 @@ snd-soc-wm8750-objs := wm8750.o
 snd-soc-wm8753-objs := wm8753.o
 snd-soc-wm9712-objs := wm9712.o
 snd-soc-cs4270-objs := cs4270.o
+snd-soc-tlv320aic3x-objs := tlv320aic3x.o
 
 obj-$(CONFIG_SND_SOC_AC97_CODEC)       += snd-soc-ac97.o
 obj-$(CONFIG_SND_SOC_WM8731)   += snd-soc-wm8731.o
@@ -11,3 +12,4 @@ obj-$(CONFIG_SND_SOC_WM8750)  += snd-soc-wm8750.o
 obj-$(CONFIG_SND_SOC_WM8753)   += snd-soc-wm8753.o
 obj-$(CONFIG_SND_SOC_WM9712)   += snd-soc-wm9712.o
 obj-$(CONFIG_SND_SOC_CS4270)   += snd-soc-cs4270.o
+obj-$(CONFIG_SND_SOC_TLV320AIC3X)      += snd-soc-tlv320aic3x.o
index 0b8a6f8b3668a5789ff6ae9492327f3b4e558210..242130cf1abd2d0797f794f70d4937acefc159c6 100644 (file)
@@ -19,7 +19,6 @@
 #include <linux/init.h>
 #include <linux/kernel.h>
 #include <linux/device.h>
-#include <sound/driver.h>
 #include <sound/core.h>
 #include <sound/pcm.h>
 #include <sound/ac97_codec.h>
index dab22cc97ead840411f67e4ab499610396f922c0..bf2ab72d49bf21488032258350b926b75ea000eb 100644 (file)
@@ -28,7 +28,6 @@
 
 #include <linux/module.h>
 #include <linux/platform_device.h>
-#include <sound/driver.h>
 #include <sound/core.h>
 #include <sound/soc.h>
 #include <sound/initval.h>
@@ -48,12 +47,130 @@ struct cs4270_private {
        unsigned int mode; /* The mode (I2S or left-justified) */
 };
 
-/* The number of MCLK/LRCK ratios supported by the CS4270 */
-#define NUM_MCLK_RATIOS                9
+/*
+ * The codec isn't really big-endian or little-endian, since the I2S
+ * interface requires data to be sent serially with the MSbit first.
+ * However, to support BE and LE I2S devices, we specify both here.  That
+ * way, ALSA will always match the bit patterns.
+ */
+#define CS4270_FORMATS (SNDRV_PCM_FMTBIT_S8      | \
+                       SNDRV_PCM_FMTBIT_S16_LE  | SNDRV_PCM_FMTBIT_S16_BE  | \
+                       SNDRV_PCM_FMTBIT_S18_3LE | SNDRV_PCM_FMTBIT_S18_3BE | \
+                       SNDRV_PCM_FMTBIT_S20_3LE | SNDRV_PCM_FMTBIT_S20_3BE | \
+                       SNDRV_PCM_FMTBIT_S24_3LE | SNDRV_PCM_FMTBIT_S24_3BE | \
+                       SNDRV_PCM_FMTBIT_S24_LE  | SNDRV_PCM_FMTBIT_S24_BE)
+
+#ifdef USE_I2C
+
+/* CS4270 registers addresses */
+#define CS4270_CHIPID  0x01    /* Chip ID */
+#define CS4270_PWRCTL  0x02    /* Power Control */
+#define CS4270_MODE    0x03    /* Mode Control */
+#define CS4270_FORMAT  0x04    /* Serial Format, ADC/DAC Control */
+#define CS4270_TRANS   0x05    /* Transition Control */
+#define CS4270_MUTE    0x06    /* Mute Control */
+#define CS4270_VOLA    0x07    /* DAC Channel A Volume Control */
+#define CS4270_VOLB    0x08    /* DAC Channel B Volume Control */
+
+#define CS4270_FIRSTREG        0x01
+#define CS4270_LASTREG 0x08
+#define CS4270_NUMREGS (CS4270_LASTREG - CS4270_FIRSTREG + 1)
 
-/* The actual MCLK/LRCK ratios, in increasing numerical order */
-static unsigned int mclk_ratios[NUM_MCLK_RATIOS] =
-       {64, 96, 128, 192, 256, 384, 512, 768, 1024};
+/* Bit masks for the CS4270 registers */
+#define CS4270_CHIPID_ID       0xF0
+#define CS4270_CHIPID_REV      0x0F
+#define CS4270_PWRCTL_FREEZE   0x80
+#define CS4270_PWRCTL_PDN_ADC  0x20
+#define CS4270_PWRCTL_PDN_DAC  0x02
+#define CS4270_PWRCTL_PDN      0x01
+#define CS4270_MODE_SPEED_MASK 0x30
+#define CS4270_MODE_1X         0x00
+#define CS4270_MODE_2X         0x10
+#define CS4270_MODE_4X         0x20
+#define CS4270_MODE_SLAVE      0x30
+#define CS4270_MODE_DIV_MASK   0x0E
+#define CS4270_MODE_DIV1       0x00
+#define CS4270_MODE_DIV15      0x02
+#define CS4270_MODE_DIV2       0x04
+#define CS4270_MODE_DIV3       0x06
+#define CS4270_MODE_DIV4       0x08
+#define CS4270_MODE_POPGUARD   0x01
+#define CS4270_FORMAT_FREEZE_A 0x80
+#define CS4270_FORMAT_FREEZE_B 0x40
+#define CS4270_FORMAT_LOOPBACK 0x20
+#define CS4270_FORMAT_DAC_MASK 0x18
+#define CS4270_FORMAT_DAC_LJ   0x00
+#define CS4270_FORMAT_DAC_I2S  0x08
+#define CS4270_FORMAT_DAC_RJ16 0x18
+#define CS4270_FORMAT_DAC_RJ24 0x10
+#define CS4270_FORMAT_ADC_MASK 0x01
+#define CS4270_FORMAT_ADC_LJ   0x00
+#define CS4270_FORMAT_ADC_I2S  0x01
+#define CS4270_TRANS_ONE_VOL   0x80
+#define CS4270_TRANS_SOFT      0x40
+#define CS4270_TRANS_ZERO      0x20
+#define CS4270_TRANS_INV_ADC_A 0x08
+#define CS4270_TRANS_INV_ADC_B 0x10
+#define CS4270_TRANS_INV_DAC_A 0x02
+#define CS4270_TRANS_INV_DAC_B 0x04
+#define CS4270_TRANS_DEEMPH    0x01
+#define CS4270_MUTE_AUTO       0x20
+#define CS4270_MUTE_ADC_A      0x08
+#define CS4270_MUTE_ADC_B      0x10
+#define CS4270_MUTE_POLARITY   0x04
+#define CS4270_MUTE_DAC_A      0x01
+#define CS4270_MUTE_DAC_B      0x02
+
+/*
+ * Clock Ratio Selection for Master Mode with I2C enabled
+ *
+ * The data for this chart is taken from Table 5 of the CS4270 reference
+ * manual.
+ *
+ * This table is used to determine how to program the Mode Control register.
+ * It is also used by cs4270_set_dai_sysclk() to tell ALSA which sampling
+ * rates the CS4270 currently supports.
+ *
+ * Each element in this array corresponds to the ratios in mclk_ratios[].
+ * These two arrays need to be in sync.
+ *
+ * 'speed_mode' is the corresponding bit pattern to be written to the
+ * MODE bits of the Mode Control Register
+ *
+ * 'mclk' is the corresponding bit pattern to be wirten to the MCLK bits of
+ * the Mode Control Register.
+ *
+ * In situations where a single ratio is represented by multiple speed
+ * modes, we favor the slowest speed.  E.g, for a ratio of 128, we pick
+ * double-speed instead of quad-speed.  However, the CS4270 errata states
+ * that Divide-By-1.5 can cause failures, so we avoid that mode where
+ * possible.
+ *
+ * ERRATA: There is an errata for the CS4270 where divide-by-1.5 does not
+ * work if VD = 3.3V.  If this effects you, select the
+ * CONFIG_SND_SOC_CS4270_VD33_ERRATA Kconfig option, and the driver will
+ * never select any sample rates that require divide-by-1.5.
+ */
+static struct {
+       unsigned int ratio;
+       u8 speed_mode;
+       u8 mclk;
+} cs4270_mode_ratios[] = {
+       {64, CS4270_MODE_4X, CS4270_MODE_DIV1},
+#ifndef CONFIG_SND_SOC_CS4270_VD33_ERRATA
+       {96, CS4270_MODE_4X, CS4270_MODE_DIV15},
+#endif
+       {128, CS4270_MODE_2X, CS4270_MODE_DIV1},
+       {192, CS4270_MODE_4X, CS4270_MODE_DIV3},
+       {256, CS4270_MODE_1X, CS4270_MODE_DIV1},
+       {384, CS4270_MODE_2X, CS4270_MODE_DIV3},
+       {512, CS4270_MODE_1X, CS4270_MODE_DIV2},
+       {768, CS4270_MODE_1X, CS4270_MODE_DIV3},
+       {1024, CS4270_MODE_1X, CS4270_MODE_DIV4}
+};
+
+/* The number of MCLK/LRCK ratios supported by the CS4270 */
+#define NUM_MCLK_RATIOS                ARRAY_SIZE(cs4270_mode_ratios)
 
 /*
  * Determine the CS4270 samples rates.
@@ -97,7 +214,7 @@ static int cs4270_set_dai_sysclk(struct snd_soc_codec_dai *codec_dai,
        cs4270->mclk = freq;
 
        for (i = 0; i < NUM_MCLK_RATIOS; i++) {
-               unsigned int rate = freq / mclk_ratios[i];
+               unsigned int rate = freq / cs4270_mode_ratios[i].ratio;
                rates |= snd_pcm_rate_to_rate_bit(rate);
                if (rate < rate_min)
                        rate_min = rate;
@@ -154,80 +271,6 @@ static int cs4270_set_dai_fmt(struct snd_soc_codec_dai *codec_dai,
        return ret;
 }
 
-/*
- * The codec isn't really big-endian or little-endian, since the I2S
- * interface requires data to be sent serially with the MSbit first.
- * However, to support BE and LE I2S devices, we specify both here.  That
- * way, ALSA will always match the bit patterns.
- */
-#define CS4270_FORMATS (SNDRV_PCM_FMTBIT_S8      | \
-                       SNDRV_PCM_FMTBIT_S16_LE  | SNDRV_PCM_FMTBIT_S16_BE  | \
-                       SNDRV_PCM_FMTBIT_S18_3LE | SNDRV_PCM_FMTBIT_S18_3BE | \
-                       SNDRV_PCM_FMTBIT_S20_3LE | SNDRV_PCM_FMTBIT_S20_3BE | \
-                       SNDRV_PCM_FMTBIT_S24_3LE | SNDRV_PCM_FMTBIT_S24_3BE | \
-                       SNDRV_PCM_FMTBIT_S24_LE  | SNDRV_PCM_FMTBIT_S24_BE)
-
-#ifdef USE_I2C
-
-/* CS4270 registers addresses */
-#define CS4270_CHIPID  0x01    /* Chip ID */
-#define CS4270_PWRCTL  0x02    /* Power Control */
-#define CS4270_MODE    0x03    /* Mode Control */
-#define CS4270_FORMAT  0x04    /* Serial Format, ADC/DAC Control */
-#define CS4270_TRANS   0x05    /* Transition Control */
-#define CS4270_MUTE    0x06    /* Mute Control */
-#define CS4270_VOLA    0x07    /* DAC Channel A Volume Control */
-#define CS4270_VOLB    0x08    /* DAC Channel B Volume Control */
-
-#define CS4270_FIRSTREG        0x01
-#define CS4270_LASTREG 0x08
-#define CS4270_NUMREGS (CS4270_LASTREG - CS4270_FIRSTREG + 1)
-
-/* Bit masks for the CS4270 registers */
-#define CS4270_CHIPID_ID       0xF0
-#define CS4270_CHIPID_REV      0x0F
-#define CS4270_PWRCTL_FREEZE   0x80
-#define CS4270_PWRCTL_PDN_ADC  0x20
-#define CS4270_PWRCTL_PDN_DAC  0x02
-#define CS4270_PWRCTL_PDN      0x01
-#define CS4270_MODE_SPEED_MASK 0x30
-#define CS4270_MODE_1X         0x00
-#define CS4270_MODE_2X         0x10
-#define CS4270_MODE_4X         0x20
-#define CS4270_MODE_SLAVE      0x30
-#define CS4270_MODE_DIV_MASK   0x0E
-#define CS4270_MODE_DIV1       0x00
-#define CS4270_MODE_DIV15      0x02
-#define CS4270_MODE_DIV2       0x04
-#define CS4270_MODE_DIV3       0x06
-#define CS4270_MODE_DIV4       0x08
-#define CS4270_MODE_POPGUARD   0x01
-#define CS4270_FORMAT_FREEZE_A 0x80
-#define CS4270_FORMAT_FREEZE_B 0x40
-#define CS4270_FORMAT_LOOPBACK 0x20
-#define CS4270_FORMAT_DAC_MASK 0x18
-#define CS4270_FORMAT_DAC_LJ   0x00
-#define CS4270_FORMAT_DAC_I2S  0x08
-#define CS4270_FORMAT_DAC_RJ16 0x18
-#define CS4270_FORMAT_DAC_RJ24 0x10
-#define CS4270_FORMAT_ADC_MASK 0x01
-#define CS4270_FORMAT_ADC_LJ   0x00
-#define CS4270_FORMAT_ADC_I2S  0x01
-#define CS4270_TRANS_ONE_VOL   0x80
-#define CS4270_TRANS_SOFT      0x40
-#define CS4270_TRANS_ZERO      0x20
-#define CS4270_TRANS_INV_ADC_A 0x08
-#define CS4270_TRANS_INV_ADC_B 0x10
-#define CS4270_TRANS_INV_DAC_A 0x02
-#define CS4270_TRANS_INV_DAC_B 0x04
-#define CS4270_TRANS_DEEMPH    0x01
-#define CS4270_MUTE_AUTO       0x20
-#define CS4270_MUTE_ADC_A      0x08
-#define CS4270_MUTE_ADC_B      0x10
-#define CS4270_MUTE_POLARITY   0x04
-#define CS4270_MUTE_DAC_A      0x01
-#define CS4270_MUTE_DAC_B      0x02
-
 /*
  * A list of addresses on which this CS4270 could use.  I2C addresses are
  * 7 bits.  For the CS4270, the upper four bits are always 1001, and the
@@ -314,53 +357,6 @@ static int cs4270_i2c_write(struct snd_soc_codec *codec, unsigned int reg,
        return 0;
 }
 
-/*
- * Clock Ratio Selection for Master Mode with I2C enabled
- *
- * The data for this chart is taken from Table 5 of the CS4270 reference
- * manual.
- *
- * This table is used to determine how to program the Mode Control register.
- * It is also used by cs4270_set_dai_sysclk() to tell ALSA which sampling
- * rates the CS4270 currently supports.
- *
- * Each element in this array corresponds to the ratios in mclk_ratios[].
- * These two arrays need to be in sync.
- *
- * 'speed_mode' is the corresponding bit pattern to be written to the
- * MODE bits of the Mode Control Register
- *
- * 'mclk' is the corresponding bit pattern to be wirten to the MCLK bits of
- * the Mode Control Register.
- *
- * In situations where a single ratio is represented by multiple speed
- * modes, we favor the slowest speed.  E.g, for a ratio of 128, we pick
- * double-speed instead of quad-speed.  However, the CS4270 errata states
- * that Divide-By-1.5 can cause failures, so we avoid that mode where
- * possible.
- *
- * ERRATA: There is an errata for the CS4270 where divide-by-1.5 does not
- * work if VD = 3.3V.  If this effects you, select the
- * CONFIG_SND_SOC_CS4270_VD33_ERRATA Kconfig option, and the driver will
- * never select any sample rates that require divide-by-1.5.
- */
-static struct {
-       u8 speed_mode;
-       u8 mclk;
-} cs4270_mode_ratios[NUM_MCLK_RATIOS] = {
-       {CS4270_MODE_4X, CS4270_MODE_DIV1},     /* 64 */
-#ifndef CONFIG_SND_SOC_CS4270_VD33_ERRATA
-       {CS4270_MODE_4X, CS4270_MODE_DIV15},    /* 96 */
-#endif
-       {CS4270_MODE_2X, CS4270_MODE_DIV1},     /* 128 */
-       {CS4270_MODE_4X, CS4270_MODE_DIV3},     /* 192 */
-       {CS4270_MODE_1X, CS4270_MODE_DIV1},     /* 256 */
-       {CS4270_MODE_2X, CS4270_MODE_DIV3},     /* 384 */
-       {CS4270_MODE_1X, CS4270_MODE_DIV2},     /* 512 */
-       {CS4270_MODE_1X, CS4270_MODE_DIV3},     /* 768 */
-       {CS4270_MODE_1X, CS4270_MODE_DIV4}      /* 1024 */
-};
-
 /*
  * Program the CS4270 with the given hardware parameters.
  *
@@ -388,7 +384,7 @@ static int cs4270_hw_params(struct snd_pcm_substream *substream,
        ratio = cs4270->mclk / rate;    /* MCLK/LRCK ratio */
 
        for (i = 0; i < NUM_MCLK_RATIOS; i++) {
-               if (mclk_ratios[i] == ratio)
+               if (cs4270_mode_ratios[i].ratio == ratio)
                        break;
        }
 
@@ -669,7 +665,7 @@ error:
        return ret;
 }
 
-#endif
+#endif /* USE_I2C*/
 
 struct snd_soc_codec_dai cs4270_dai = {
        .name = "CS4270",
@@ -687,10 +683,6 @@ struct snd_soc_codec_dai cs4270_dai = {
                .rates = 0,
                .formats = CS4270_FORMATS,
        },
-       .dai_ops = {
-               .set_sysclk = cs4270_set_dai_sysclk,
-               .set_fmt = cs4270_set_dai_fmt,
-       }
 };
 EXPORT_SYMBOL_GPL(cs4270_dai);
 
@@ -752,6 +744,8 @@ static int cs4270_probe(struct platform_device *pdev)
        if (codec->control_data) {
                /* Initialize codec ops */
                cs4270_dai.ops.hw_params = cs4270_hw_params;
+               cs4270_dai.dai_ops.set_sysclk = cs4270_set_dai_sysclk;
+               cs4270_dai.dai_ops.set_fmt = cs4270_set_dai_fmt;
 #ifdef CONFIG_SND_SOC_CS4270_HWMUTE
                cs4270_dai.dai_ops.digital_mute = cs4270_mute;
 #endif
diff --git a/sound/soc/codecs/tlv320aic3x.c b/sound/soc/codecs/tlv320aic3x.c
new file mode 100644 (file)
index 0000000..710e028
--- /dev/null
@@ -0,0 +1,1274 @@
+/*
+ * ALSA SoC TLV320AIC3X codec driver
+ *
+ * Author:      Vladimir Barinov, <vbarinov@ru.mvista.com>
+ * Copyright:   (C) 2007 MontaVista Software, Inc., <source@mvista.com>
+ *
+ * Based on sound/soc/codecs/wm8753.c by Liam Girdwood
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * Notes:
+ *  The AIC3X is a driver for a low power stereo audio
+ *  codecs aic31, aic32, aic33.
+ *
+ *  It supports full aic33 codec functionality.
+ *  The compatibility with aic32, aic31 is as follows:
+ *        aic32        |        aic31
+ *  ---------------------------------------
+ *   MONO_LOUT -> N/A  |  MONO_LOUT -> N/A
+ *                     |  IN1L -> LINE1L
+ *                     |  IN1R -> LINE1R
+ *                     |  IN2L -> LINE2L
+ *                     |  IN2R -> LINE2R
+ *                     |  MIC3L/R -> N/A
+ *   truncated internal functionality in
+ *   accordance with documentation
+ *  ---------------------------------------
+ *
+ *  Hence the machine layer should disable unsupported inputs/outputs by
+ *  snd_soc_dapm_set_endpoint(codec, "MONO_LOUT", 0), etc.
+ */
+
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/init.h>
+#include <linux/delay.h>
+#include <linux/pm.h>
+#include <linux/i2c.h>
+#include <linux/platform_device.h>
+#include <sound/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 "tlv320aic3x.h"
+
+#define AUDIO_NAME "aic3x"
+#define AIC3X_VERSION "0.1"
+
+/* codec private data */
+struct aic3x_priv {
+       unsigned int sysclk;
+       int master;
+};
+
+/*
+ * AIC3X register cache
+ * We can't read the AIC3X 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 u8 aic3x_reg[AIC3X_CACHEREGNUM] = {
+       0x00, 0x00, 0x00, 0x10, /* 0 */
+       0x04, 0x00, 0x00, 0x00, /* 4 */
+       0x00, 0x00, 0x00, 0x01, /* 8 */
+       0x00, 0x00, 0x00, 0x80, /* 12 */
+       0x80, 0xff, 0xff, 0x78, /* 16 */
+       0x78, 0x78, 0x78, 0x78, /* 20 */
+       0x78, 0x00, 0x00, 0xfe, /* 24 */
+       0x00, 0x00, 0xfe, 0x00, /* 28 */
+       0x18, 0x18, 0x00, 0x00, /* 32 */
+       0x00, 0x00, 0x00, 0x00, /* 36 */
+       0x00, 0x00, 0x00, 0x80, /* 40 */
+       0x80, 0x00, 0x00, 0x00, /* 44 */
+       0x00, 0x00, 0x00, 0x04, /* 48 */
+       0x00, 0x00, 0x00, 0x00, /* 52 */
+       0x00, 0x00, 0x04, 0x00, /* 56 */
+       0x00, 0x00, 0x00, 0x00, /* 60 */
+       0x00, 0x04, 0x00, 0x00, /* 64 */
+       0x00, 0x00, 0x00, 0x00, /* 68 */
+       0x04, 0x00, 0x00, 0x00, /* 72 */
+       0x00, 0x00, 0x00, 0x00, /* 76 */
+       0x00, 0x00, 0x00, 0x00, /* 80 */
+       0x00, 0x00, 0x00, 0x00, /* 84 */
+       0x00, 0x00, 0x00, 0x00, /* 88 */
+       0x00, 0x00, 0x00, 0x00, /* 92 */
+       0x00, 0x00, 0x00, 0x00, /* 96 */
+       0x00, 0x00, 0x02,       /* 100 */
+};
+
+/*
+ * read aic3x register cache
+ */
+static inline unsigned int aic3x_read_reg_cache(struct snd_soc_codec *codec,
+                                               unsigned int reg)
+{
+       u8 *cache = codec->reg_cache;
+       if (reg >= AIC3X_CACHEREGNUM)
+               return -1;
+       return cache[reg];
+}
+
+/*
+ * write aic3x register cache
+ */
+static inline void aic3x_write_reg_cache(struct snd_soc_codec *codec,
+                                        u8 reg, u8 value)
+{
+       u8 *cache = codec->reg_cache;
+       if (reg >= AIC3X_CACHEREGNUM)
+               return;
+       cache[reg] = value;
+}
+
+/*
+ * write to the aic3x register space
+ */
+static int aic3x_write(struct snd_soc_codec *codec, unsigned int reg,
+                      unsigned int value)
+{
+       u8 data[2];
+
+       /* data is
+        *   D15..D8 aic3x register offset
+        *   D7...D0 register data
+        */
+       data[0] = reg & 0xff;
+       data[1] = value & 0xff;
+
+       aic3x_write_reg_cache(codec, data[0], data[1]);
+       if (codec->hw_write(codec->control_data, data, 2) == 2)
+               return 0;
+       else
+               return -EIO;
+}
+
+#define SOC_DAPM_SINGLE_AIC3X(xname, reg, shift, mask, invert) \
+{      .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, \
+       .info = snd_soc_info_volsw, \
+       .get = snd_soc_dapm_get_volsw, .put = snd_soc_dapm_put_volsw_aic3x, \
+       .private_value =  SOC_SINGLE_VALUE(reg, shift, mask, invert) }
+
+/*
+ * All input lines are connected when !0xf and disconnected with 0xf bit field,
+ * so we have to use specific dapm_put call for input mixer
+ */
+static int snd_soc_dapm_put_volsw_aic3x(struct snd_kcontrol *kcontrol,
+                                       struct snd_ctl_elem_value *ucontrol)
+{
+       struct snd_soc_dapm_widget *widget = snd_kcontrol_chip(kcontrol);
+       int reg = kcontrol->private_value & 0xff;
+       int shift = (kcontrol->private_value >> 8) & 0x0f;
+       int mask = (kcontrol->private_value >> 16) & 0xff;
+       int invert = (kcontrol->private_value >> 24) & 0x01;
+       unsigned short val, val_mask;
+       int ret;
+       struct snd_soc_dapm_path *path;
+       int found = 0;
+
+       val = (ucontrol->value.integer.value[0] & mask);
+
+       mask = 0xf;
+       if (val)
+               val = mask;
+
+       if (invert)
+               val = mask - val;
+       val_mask = mask << shift;
+       val = val << shift;
+
+       mutex_lock(&widget->codec->mutex);
+
+       if (snd_soc_test_bits(widget->codec, reg, val_mask, val)) {
+               /* find dapm widget path assoc with kcontrol */
+               list_for_each_entry(path, &widget->codec->dapm_paths, list) {
+                       if (path->kcontrol != kcontrol)
+                               continue;
+
+                       /* found, now check type */
+                       found = 1;
+                       if (val)
+                               /* new connection */
+                               path->connect = invert ? 0 : 1;
+                       else
+                               /* old connection must be powered down */
+                               path->connect = invert ? 1 : 0;
+                       break;
+               }
+
+               if (found)
+                       snd_soc_dapm_sync_endpoints(widget->codec);
+       }
+
+       ret = snd_soc_update_bits(widget->codec, reg, val_mask, val);
+
+       mutex_unlock(&widget->codec->mutex);
+       return ret;
+}
+
+static const char *aic3x_left_dac_mux[] = { "DAC_L1", "DAC_L3", "DAC_L2" };
+static const char *aic3x_right_dac_mux[] = { "DAC_R1", "DAC_R3", "DAC_R2" };
+static const char *aic3x_left_hpcom_mux[] =
+    { "differential of HPLOUT", "constant VCM", "single-ended" };
+static const char *aic3x_right_hpcom_mux[] =
+    { "differential of HPROUT", "constant VCM", "single-ended",
+      "differential of HPLCOM", "external feedback" };
+static const char *aic3x_linein_mode_mux[] = { "single-ended", "differential" };
+
+#define LDAC_ENUM      0
+#define RDAC_ENUM      1
+#define LHPCOM_ENUM    2
+#define RHPCOM_ENUM    3
+#define LINE1L_ENUM    4
+#define LINE1R_ENUM    5
+#define LINE2L_ENUM    6
+#define LINE2R_ENUM    7
+
+static const struct soc_enum aic3x_enum[] = {
+       SOC_ENUM_SINGLE(DAC_LINE_MUX, 6, 3, aic3x_left_dac_mux),
+       SOC_ENUM_SINGLE(DAC_LINE_MUX, 4, 3, aic3x_right_dac_mux),
+       SOC_ENUM_SINGLE(HPLCOM_CFG, 4, 3, aic3x_left_hpcom_mux),
+       SOC_ENUM_SINGLE(HPRCOM_CFG, 3, 5, aic3x_right_hpcom_mux),
+       SOC_ENUM_SINGLE(LINE1L_2_LADC_CTRL, 7, 2, aic3x_linein_mode_mux),
+       SOC_ENUM_SINGLE(LINE1R_2_RADC_CTRL, 7, 2, aic3x_linein_mode_mux),
+       SOC_ENUM_SINGLE(LINE2L_2_LADC_CTRL, 7, 2, aic3x_linein_mode_mux),
+       SOC_ENUM_SINGLE(LINE2R_2_RADC_CTRL, 7, 2, aic3x_linein_mode_mux),
+};
+
+static const struct snd_kcontrol_new aic3x_snd_controls[] = {
+       /* Output */
+       SOC_DOUBLE_R("PCM Playback Volume", LDAC_VOL, RDAC_VOL, 0, 0x7f, 1),
+
+       SOC_DOUBLE_R("Line DAC Playback Volume", DACL1_2_LLOPM_VOL,
+                    DACR1_2_RLOPM_VOL, 0, 0x7f, 1),
+       SOC_DOUBLE_R("Line DAC Playback Switch", LLOPM_CTRL, RLOPM_CTRL, 3,
+                    0x01, 0),
+       SOC_DOUBLE_R("Line PGA Bypass Playback Volume", PGAL_2_LLOPM_VOL,
+                    PGAR_2_RLOPM_VOL, 0, 0x7f, 1),
+       SOC_DOUBLE_R("Line Line2 Bypass Playback Volume", LINE2L_2_LLOPM_VOL,
+                    LINE2R_2_RLOPM_VOL, 0, 0x7f, 1),
+
+       SOC_DOUBLE_R("Mono DAC Playback Volume", DACL1_2_MONOLOPM_VOL,
+                    DACR1_2_MONOLOPM_VOL, 0, 0x7f, 1),
+       SOC_SINGLE("Mono DAC Playback Switch", MONOLOPM_CTRL, 3, 0x01, 0),
+       SOC_DOUBLE_R("Mono PGA Bypass Playback Volume", PGAL_2_MONOLOPM_VOL,
+                    PGAR_2_MONOLOPM_VOL, 0, 0x7f, 1),
+       SOC_DOUBLE_R("Mono Line2 Bypass Playback Volume", LINE2L_2_MONOLOPM_VOL,
+                    LINE2R_2_MONOLOPM_VOL, 0, 0x7f, 1),
+
+       SOC_DOUBLE_R("HP DAC Playback Volume", DACL1_2_HPLOUT_VOL,
+                    DACR1_2_HPROUT_VOL, 0, 0x7f, 1),
+       SOC_DOUBLE_R("HP DAC Playback Switch", HPLOUT_CTRL, HPROUT_CTRL, 3,
+                    0x01, 0),
+       SOC_DOUBLE_R("HP PGA Bypass Playback Volume", PGAL_2_HPLOUT_VOL,
+                    PGAR_2_HPROUT_VOL, 0, 0x7f, 1),
+       SOC_DOUBLE_R("HP Line2 Bypass Playback Volume", LINE2L_2_HPLOUT_VOL,
+                    LINE2R_2_HPROUT_VOL, 0, 0x7f, 1),
+
+       SOC_DOUBLE_R("HPCOM DAC Playback Volume", DACL1_2_HPLCOM_VOL,
+                    DACR1_2_HPRCOM_VOL, 0, 0x7f, 1),
+       SOC_DOUBLE_R("HPCOM DAC Playback Switch", HPLCOM_CTRL, HPRCOM_CTRL, 3,
+                    0x01, 0),
+       SOC_DOUBLE_R("HPCOM PGA Bypass Playback Volume", PGAL_2_HPLCOM_VOL,
+                    PGAR_2_HPRCOM_VOL, 0, 0x7f, 1),
+       SOC_DOUBLE_R("HPCOM Line2 Bypass Playback Volume", LINE2L_2_HPLCOM_VOL,
+                    LINE2R_2_HPRCOM_VOL, 0, 0x7f, 1),
+
+       /*
+        * Note: enable Automatic input Gain Controller with care. It can
+        * adjust PGA to max value when ADC is on and will never go back.
+       */
+       SOC_DOUBLE_R("AGC Switch", LAGC_CTRL_A, RAGC_CTRL_A, 7, 0x01, 0),
+
+       /* Input */
+       SOC_DOUBLE_R("PGA Capture Volume", LADC_VOL, RADC_VOL, 0, 0x7f, 0),
+       SOC_DOUBLE_R("PGA Capture Switch", LADC_VOL, RADC_VOL, 7, 0x01, 1),
+};
+
+/* add non dapm controls */
+static int aic3x_add_controls(struct snd_soc_codec *codec)
+{
+       int err, i;
+
+       for (i = 0; i < ARRAY_SIZE(aic3x_snd_controls); i++) {
+               err = snd_ctl_add(codec->card,
+                                 snd_soc_cnew(&aic3x_snd_controls[i],
+                                              codec, NULL));
+               if (err < 0)
+                       return err;
+       }
+
+       return 0;
+}
+
+/* Left DAC Mux */
+static const struct snd_kcontrol_new aic3x_left_dac_mux_controls =
+SOC_DAPM_ENUM("Route", aic3x_enum[LDAC_ENUM]);
+
+/* Right DAC Mux */
+static const struct snd_kcontrol_new aic3x_right_dac_mux_controls =
+SOC_DAPM_ENUM("Route", aic3x_enum[RDAC_ENUM]);
+
+/* Left HPCOM Mux */
+static const struct snd_kcontrol_new aic3x_left_hpcom_mux_controls =
+SOC_DAPM_ENUM("Route", aic3x_enum[LHPCOM_ENUM]);
+
+/* Right HPCOM Mux */
+static const struct snd_kcontrol_new aic3x_right_hpcom_mux_controls =
+SOC_DAPM_ENUM("Route", aic3x_enum[RHPCOM_ENUM]);
+
+/* Left DAC_L1 Mixer */
+static const struct snd_kcontrol_new aic3x_left_dac_mixer_controls[] = {
+       SOC_DAPM_SINGLE("Line Switch", DACL1_2_LLOPM_VOL, 7, 1, 0),
+       SOC_DAPM_SINGLE("Mono Switch", DACL1_2_MONOLOPM_VOL, 7, 1, 0),
+       SOC_DAPM_SINGLE("HP Switch", DACL1_2_HPLOUT_VOL, 7, 1, 0),
+       SOC_DAPM_SINGLE("HPCOM Switch", DACL1_2_HPLCOM_VOL, 7, 1, 0),
+};
+
+/* Right DAC_R1 Mixer */
+static const struct snd_kcontrol_new aic3x_right_dac_mixer_controls[] = {
+       SOC_DAPM_SINGLE("Line Switch", DACR1_2_RLOPM_VOL, 7, 1, 0),
+       SOC_DAPM_SINGLE("Mono Switch", DACR1_2_MONOLOPM_VOL, 7, 1, 0),
+       SOC_DAPM_SINGLE("HP Switch", DACR1_2_HPROUT_VOL, 7, 1, 0),
+       SOC_DAPM_SINGLE("HPCOM Switch", DACR1_2_HPRCOM_VOL, 7, 1, 0),
+};
+
+/* Left PGA Mixer */
+static const struct snd_kcontrol_new aic3x_left_pga_mixer_controls[] = {
+       SOC_DAPM_SINGLE_AIC3X("Line1L Switch", LINE1L_2_LADC_CTRL, 3, 1, 1),
+       SOC_DAPM_SINGLE_AIC3X("Line2L Switch", LINE2L_2_LADC_CTRL, 3, 1, 1),
+       SOC_DAPM_SINGLE_AIC3X("Mic3L Switch", MIC3LR_2_LADC_CTRL, 4, 1, 1),
+};
+
+/* Right PGA Mixer */
+static const struct snd_kcontrol_new aic3x_right_pga_mixer_controls[] = {
+       SOC_DAPM_SINGLE_AIC3X("Line1R Switch", LINE1R_2_RADC_CTRL, 3, 1, 1),
+       SOC_DAPM_SINGLE_AIC3X("Line2R Switch", LINE2R_2_RADC_CTRL, 3, 1, 1),
+       SOC_DAPM_SINGLE_AIC3X("Mic3R Switch", MIC3LR_2_RADC_CTRL, 0, 1, 1),
+};
+
+/* Left Line1 Mux */
+static const struct snd_kcontrol_new aic3x_left_line1_mux_controls =
+SOC_DAPM_ENUM("Route", aic3x_enum[LINE1L_ENUM]);
+
+/* Right Line1 Mux */
+static const struct snd_kcontrol_new aic3x_right_line1_mux_controls =
+SOC_DAPM_ENUM("Route", aic3x_enum[LINE1R_ENUM]);
+
+/* Left Line2 Mux */
+static const struct snd_kcontrol_new aic3x_left_line2_mux_controls =
+SOC_DAPM_ENUM("Route", aic3x_enum[LINE2L_ENUM]);
+
+/* Right Line2 Mux */
+static const struct snd_kcontrol_new aic3x_right_line2_mux_controls =
+SOC_DAPM_ENUM("Route", aic3x_enum[LINE2R_ENUM]);
+
+/* Left PGA Bypass Mixer */
+static const struct snd_kcontrol_new aic3x_left_pga_bp_mixer_controls[] = {
+       SOC_DAPM_SINGLE("Line Switch", PGAL_2_LLOPM_VOL, 7, 1, 0),
+       SOC_DAPM_SINGLE("Mono Switch", PGAL_2_MONOLOPM_VOL, 7, 1, 0),
+       SOC_DAPM_SINGLE("HP Switch", PGAL_2_HPLOUT_VOL, 7, 1, 0),
+       SOC_DAPM_SINGLE("HPCOM Switch", PGAL_2_HPLCOM_VOL, 7, 1, 0),
+};
+
+/* Right PGA Bypass Mixer */
+static const struct snd_kcontrol_new aic3x_right_pga_bp_mixer_controls[] = {
+       SOC_DAPM_SINGLE("Line Switch", PGAR_2_RLOPM_VOL, 7, 1, 0),
+       SOC_DAPM_SINGLE("Mono Switch", PGAR_2_MONOLOPM_VOL, 7, 1, 0),
+       SOC_DAPM_SINGLE("HP Switch", PGAR_2_HPROUT_VOL, 7, 1, 0),
+       SOC_DAPM_SINGLE("HPCOM Switch", PGAR_2_HPRCOM_VOL, 7, 1, 0),
+};
+
+/* Left Line2 Bypass Mixer */
+static const struct snd_kcontrol_new aic3x_left_line2_bp_mixer_controls[] = {
+       SOC_DAPM_SINGLE("Line Switch", LINE2L_2_LLOPM_VOL, 7, 1, 0),
+       SOC_DAPM_SINGLE("Mono Switch", LINE2L_2_MONOLOPM_VOL, 7, 1, 0),
+       SOC_DAPM_SINGLE("HP Switch", LINE2L_2_HPLOUT_VOL, 7, 1, 0),
+       SOC_DAPM_SINGLE("HPCOM Switch", LINE2L_2_HPLCOM_VOL, 7, 1, 0),
+};
+
+/* Right Line2 Bypass Mixer */
+static const struct snd_kcontrol_new aic3x_right_line2_bp_mixer_controls[] = {
+       SOC_DAPM_SINGLE("Line Switch", LINE2R_2_RLOPM_VOL, 7, 1, 0),
+       SOC_DAPM_SINGLE("Mono Switch", LINE2R_2_MONOLOPM_VOL, 7, 1, 0),
+       SOC_DAPM_SINGLE("HP Switch", LINE2R_2_HPROUT_VOL, 7, 1, 0),
+       SOC_DAPM_SINGLE("HPCOM Switch", LINE2R_2_HPRCOM_VOL, 7, 1, 0),
+};
+
+static const struct snd_soc_dapm_widget aic3x_dapm_widgets[] = {
+       /* Left DAC to Left Outputs */
+       SND_SOC_DAPM_DAC("Left DAC", "Left Playback", DAC_PWR, 7, 0),
+       SND_SOC_DAPM_MUX("Left DAC Mux", SND_SOC_NOPM, 0, 0,
+                        &aic3x_left_dac_mux_controls),
+       SND_SOC_DAPM_MIXER("Left DAC_L1 Mixer", SND_SOC_NOPM, 0, 0,
+                          &aic3x_left_dac_mixer_controls[0],
+                          ARRAY_SIZE(aic3x_left_dac_mixer_controls)),
+       SND_SOC_DAPM_MUX("Left HPCOM Mux", SND_SOC_NOPM, 0, 0,
+                        &aic3x_left_hpcom_mux_controls),
+       SND_SOC_DAPM_PGA("Left Line Out", LLOPM_CTRL, 0, 0, NULL, 0),
+       SND_SOC_DAPM_PGA("Left HP Out", HPLOUT_CTRL, 0, 0, NULL, 0),
+       SND_SOC_DAPM_PGA("Left HP Com", HPLCOM_CTRL, 0, 0, NULL, 0),
+
+       /* Right DAC to Right Outputs */
+       SND_SOC_DAPM_DAC("Right DAC", "Right Playback", DAC_PWR, 6, 0),
+       SND_SOC_DAPM_MUX("Right DAC Mux", SND_SOC_NOPM, 0, 0,
+                        &aic3x_right_dac_mux_controls),
+       SND_SOC_DAPM_MIXER("Right DAC_R1 Mixer", SND_SOC_NOPM, 0, 0,
+                          &aic3x_right_dac_mixer_controls[0],
+                          ARRAY_SIZE(aic3x_right_dac_mixer_controls)),
+       SND_SOC_DAPM_MUX("Right HPCOM Mux", SND_SOC_NOPM, 0, 0,
+                        &aic3x_right_hpcom_mux_controls),
+       SND_SOC_DAPM_PGA("Right Line Out", RLOPM_CTRL, 0, 0, NULL, 0),
+       SND_SOC_DAPM_PGA("Right HP Out", HPROUT_CTRL, 0, 0, NULL, 0),
+       SND_SOC_DAPM_PGA("Right HP Com", HPRCOM_CTRL, 0, 0, NULL, 0),
+
+       /* Mono Output */
+       SND_SOC_DAPM_PGA("Mono Out", MONOLOPM_CTRL, 0, 0, NULL, 0),
+
+       /* Left Inputs to Left ADC */
+       SND_SOC_DAPM_ADC("Left ADC", "Left Capture", LINE1L_2_LADC_CTRL, 2, 0),
+       SND_SOC_DAPM_MIXER("Left PGA Mixer", SND_SOC_NOPM, 0, 0,
+                          &aic3x_left_pga_mixer_controls[0],
+                          ARRAY_SIZE(aic3x_left_pga_mixer_controls)),
+       SND_SOC_DAPM_MUX("Left Line1L Mux", SND_SOC_NOPM, 0, 0,
+                        &aic3x_left_line1_mux_controls),
+       SND_SOC_DAPM_MUX("Left Line2L Mux", SND_SOC_NOPM, 0, 0,
+                        &aic3x_left_line2_mux_controls),
+
+       /* Right Inputs to Right ADC */
+       SND_SOC_DAPM_ADC("Right ADC", "Right Capture",
+                        LINE1R_2_RADC_CTRL, 2, 0),
+       SND_SOC_DAPM_MIXER("Right PGA Mixer", SND_SOC_NOPM, 0, 0,
+                          &aic3x_right_pga_mixer_controls[0],
+                          ARRAY_SIZE(aic3x_right_pga_mixer_controls)),
+       SND_SOC_DAPM_MUX("Right Line1R Mux", SND_SOC_NOPM, 0, 0,
+                        &aic3x_right_line1_mux_controls),
+       SND_SOC_DAPM_MUX("Right Line2R Mux", SND_SOC_NOPM, 0, 0,
+                        &aic3x_right_line2_mux_controls),
+
+       /* Mic Bias */
+       SND_SOC_DAPM_MICBIAS("Mic Bias 2V", MICBIAS_CTRL, 6, 0),
+       SND_SOC_DAPM_MICBIAS("Mic Bias 2.5V", MICBIAS_CTRL, 7, 0),
+       SND_SOC_DAPM_MICBIAS("Mic Bias AVDD", MICBIAS_CTRL, 6, 0),
+       SND_SOC_DAPM_MICBIAS("Mic Bias AVDD", MICBIAS_CTRL, 7, 0),
+
+       /* Left PGA to Left Output bypass */
+       SND_SOC_DAPM_MIXER("Left PGA Bypass Mixer", SND_SOC_NOPM, 0, 0,
+                          &aic3x_left_pga_bp_mixer_controls[0],
+                          ARRAY_SIZE(aic3x_left_pga_bp_mixer_controls)),
+
+       /* Right PGA to Right Output bypass */
+       SND_SOC_DAPM_MIXER("Right PGA Bypass Mixer", SND_SOC_NOPM, 0, 0,
+                          &aic3x_right_pga_bp_mixer_controls[0],
+                          ARRAY_SIZE(aic3x_right_pga_bp_mixer_controls)),
+
+       /* Left Line2 to Left Output bypass */
+       SND_SOC_DAPM_MIXER("Left Line2 Bypass Mixer", SND_SOC_NOPM, 0, 0,
+                          &aic3x_left_line2_bp_mixer_controls[0],
+                          ARRAY_SIZE(aic3x_left_line2_bp_mixer_controls)),
+
+       /* Right Line2 to Right Output bypass */
+       SND_SOC_DAPM_MIXER("Right Line2 Bypass Mixer", SND_SOC_NOPM, 0, 0,
+                          &aic3x_right_line2_bp_mixer_controls[0],
+                          ARRAY_SIZE(aic3x_right_line2_bp_mixer_controls)),
+
+       SND_SOC_DAPM_OUTPUT("LLOUT"),
+       SND_SOC_DAPM_OUTPUT("RLOUT"),
+       SND_SOC_DAPM_OUTPUT("MONO_LOUT"),
+       SND_SOC_DAPM_OUTPUT("HPLOUT"),
+       SND_SOC_DAPM_OUTPUT("HPROUT"),
+       SND_SOC_DAPM_OUTPUT("HPLCOM"),
+       SND_SOC_DAPM_OUTPUT("HPRCOM"),
+
+       SND_SOC_DAPM_INPUT("MIC3L"),
+       SND_SOC_DAPM_INPUT("MIC3R"),
+       SND_SOC_DAPM_INPUT("LINE1L"),
+       SND_SOC_DAPM_INPUT("LINE1R"),
+       SND_SOC_DAPM_INPUT("LINE2L"),
+       SND_SOC_DAPM_INPUT("LINE2R"),
+};
+
+static const char *intercon[][3] = {
+       /* Left Output */
+       {"Left DAC Mux", "DAC_L1", "Left DAC"},
+       {"Left DAC Mux", "DAC_L2", "Left DAC"},
+       {"Left DAC Mux", "DAC_L3", "Left DAC"},
+
+       {"Left DAC_L1 Mixer", "Line Switch", "Left DAC Mux"},
+       {"Left DAC_L1 Mixer", "Mono Switch", "Left DAC Mux"},
+       {"Left DAC_L1 Mixer", "HP Switch", "Left DAC Mux"},
+       {"Left DAC_L1 Mixer", "HPCOM Switch", "Left DAC Mux"},
+       {"Left Line Out", NULL, "Left DAC Mux"},
+       {"Left HP Out", NULL, "Left DAC Mux"},
+
+       {"Left HPCOM Mux", "differential of HPLOUT", "Left DAC_L1 Mixer"},
+       {"Left HPCOM Mux", "constant VCM", "Left DAC_L1 Mixer"},
+       {"Left HPCOM Mux", "single-ended", "Left DAC_L1 Mixer"},
+
+       {"Left Line Out", NULL, "Left DAC_L1 Mixer"},
+       {"Mono Out", NULL, "Left DAC_L1 Mixer"},
+       {"Left HP Out", NULL, "Left DAC_L1 Mixer"},
+       {"Left HP Com", NULL, "Left HPCOM Mux"},
+
+       {"LLOUT", NULL, "Left Line Out"},
+       {"LLOUT", NULL, "Left Line Out"},
+       {"HPLOUT", NULL, "Left HP Out"},
+       {"HPLCOM", NULL, "Left HP Com"},
+
+       /* Right Output */
+       {"Right DAC Mux", "DAC_R1", "Right DAC"},
+       {"Right DAC Mux", "DAC_R2", "Right DAC"},
+       {"Right DAC Mux", "DAC_R3", "Right DAC"},
+
+       {"Right DAC_R1 Mixer", "Line Switch", "Right DAC Mux"},
+       {"Right DAC_R1 Mixer", "Mono Switch", "Right DAC Mux"},
+       {"Right DAC_R1 Mixer", "HP Switch", "Right DAC Mux"},
+       {"Right DAC_R1 Mixer", "HPCOM Switch", "Right DAC Mux"},
+       {"Right Line Out", NULL, "Right DAC Mux"},
+       {"Right HP Out", NULL, "Right DAC Mux"},
+
+       {"Right HPCOM Mux", "differential of HPROUT", "Right DAC_R1 Mixer"},
+       {"Right HPCOM Mux", "constant VCM", "Right DAC_R1 Mixer"},
+       {"Right HPCOM Mux", "single-ended", "Right DAC_R1 Mixer"},
+       {"Right HPCOM Mux", "differential of HPLCOM", "Right DAC_R1 Mixer"},
+       {"Right HPCOM Mux", "external feedback", "Right DAC_R1 Mixer"},
+
+       {"Right Line Out", NULL, "Right DAC_R1 Mixer"},
+       {"Mono Out", NULL, "Right DAC_R1 Mixer"},
+       {"Right HP Out", NULL, "Right DAC_R1 Mixer"},
+       {"Right HP Com", NULL, "Right HPCOM Mux"},
+
+       {"RLOUT", NULL, "Right Line Out"},
+       {"RLOUT", NULL, "Right Line Out"},
+       {"HPROUT", NULL, "Right HP Out"},
+       {"HPRCOM", NULL, "Right HP Com"},
+
+       /* Mono Output */
+       {"MONOLOUT", NULL, "Mono Out"},
+       {"MONOLOUT", NULL, "Mono Out"},
+
+       /* Left Input */
+       {"Left Line1L Mux", "single-ended", "LINE1L"},
+       {"Left Line1L Mux", "differential", "LINE1L"},
+
+       {"Left Line2L Mux", "single-ended", "LINE2L"},
+       {"Left Line2L Mux", "differential", "LINE2L"},
+
+       {"Left PGA Mixer", "Line1L Switch", "Left Line1L Mux"},
+       {"Left PGA Mixer", "Line2L Switch", "Left Line2L Mux"},
+       {"Left PGA Mixer", "Mic3L Switch", "MIC3L"},
+
+       {"Left ADC", NULL, "Left PGA Mixer"},
+
+       /* Right Input */
+       {"Right Line1R Mux", "single-ended", "LINE1R"},
+       {"Right Line1R Mux", "differential", "LINE1R"},
+
+       {"Right Line2R Mux", "single-ended", "LINE2R"},
+       {"Right Line2R Mux", "differential", "LINE2R"},
+
+       {"Right PGA Mixer", "Line1R Switch", "Right Line1R Mux"},
+       {"Right PGA Mixer", "Line2R Switch", "Right Line2R Mux"},
+       {"Right PGA Mixer", "Mic3R Switch", "MIC3R"},
+
+       {"Right ADC", NULL, "Right PGA Mixer"},
+
+       /* Left PGA Bypass */
+       {"Left PGA Bypass Mixer", "Line Switch", "Left PGA Mixer"},
+       {"Left PGA Bypass Mixer", "Mono Switch", "Left PGA Mixer"},
+       {"Left PGA Bypass Mixer", "HP Switch", "Left PGA Mixer"},
+       {"Left PGA Bypass Mixer", "HPCOM Switch", "Left PGA Mixer"},
+
+       {"Left HPCOM Mux", "differential of HPLOUT", "Left PGA Bypass Mixer"},
+       {"Left HPCOM Mux", "constant VCM", "Left PGA Bypass Mixer"},
+       {"Left HPCOM Mux", "single-ended", "Left PGA Bypass Mixer"},
+
+       {"Left Line Out", NULL, "Left PGA Bypass Mixer"},
+       {"Mono Out", NULL, "Left PGA Bypass Mixer"},
+       {"Left HP Out", NULL, "Left PGA Bypass Mixer"},
+
+       /* Right PGA Bypass */
+       {"Right PGA Bypass Mixer", "Line Switch", "Right PGA Mixer"},
+       {"Right PGA Bypass Mixer", "Mono Switch", "Right PGA Mixer"},
+       {"Right PGA Bypass Mixer", "HP Switch", "Right PGA Mixer"},
+       {"Right PGA Bypass Mixer", "HPCOM Switch", "Right PGA Mixer"},
+
+       {"Right HPCOM Mux", "differential of HPROUT", "Right PGA Bypass Mixer"},
+       {"Right HPCOM Mux", "constant VCM", "Right PGA Bypass Mixer"},
+       {"Right HPCOM Mux", "single-ended", "Right PGA Bypass Mixer"},
+       {"Right HPCOM Mux", "differential of HPLCOM", "Right PGA Bypass Mixer"},
+       {"Right HPCOM Mux", "external feedback", "Right PGA Bypass Mixer"},
+
+       {"Right Line Out", NULL, "Right PGA Bypass Mixer"},
+       {"Mono Out", NULL, "Right PGA Bypass Mixer"},
+       {"Right HP Out", NULL, "Right PGA Bypass Mixer"},
+
+       /* Left Line2 Bypass */
+       {"Left Line2 Bypass Mixer", "Line Switch", "Left Line2L Mux"},
+       {"Left Line2 Bypass Mixer", "Mono Switch", "Left Line2L Mux"},
+       {"Left Line2 Bypass Mixer", "HP Switch", "Left Line2L Mux"},
+       {"Left Line2 Bypass Mixer", "HPCOM Switch", "Left Line2L Mux"},
+
+       {"Left HPCOM Mux", "differential of HPLOUT", "Left Line2 Bypass Mixer"},
+       {"Left HPCOM Mux", "constant VCM", "Left Line2 Bypass Mixer"},
+       {"Left HPCOM Mux", "single-ended", "Left Line2 Bypass Mixer"},
+
+       {"Left Line Out", NULL, "Left Line2 Bypass Mixer"},
+       {"Mono Out", NULL, "Left Line2 Bypass Mixer"},
+       {"Left HP Out", NULL, "Left Line2 Bypass Mixer"},
+
+       /* Right Line2 Bypass */
+       {"Right Line2 Bypass Mixer", "Line Switch", "Right Line2R Mux"},
+       {"Right Line2 Bypass Mixer", "Mono Switch", "Right Line2R Mux"},
+       {"Right Line2 Bypass Mixer", "HP Switch", "Right Line2R Mux"},
+       {"Right Line2 Bypass Mixer", "HPCOM Switch", "Right Line2R Mux"},
+
+       {"Right HPCOM Mux", "differential of HPROUT", "Right Line2 Bypass Mixer"},
+       {"Right HPCOM Mux", "constant VCM", "Right Line2 Bypass Mixer"},
+       {"Right HPCOM Mux", "single-ended", "Right Line2 Bypass Mixer"},
+       {"Right HPCOM Mux", "differential of HPLCOM", "Right Line2 Bypass Mixer"},
+       {"Right HPCOM Mux", "external feedback", "Right Line2 Bypass Mixer"},
+
+       {"Right Line Out", NULL, "Right Line2 Bypass Mixer"},
+       {"Mono Out", NULL, "Right Line2 Bypass Mixer"},
+       {"Right HP Out", NULL, "Right Line2 Bypass Mixer"},
+
+       /* terminator */
+       {NULL, NULL, NULL},
+};
+
+static int aic3x_add_widgets(struct snd_soc_codec *codec)
+{
+       int i;
+
+       for (i = 0; i < ARRAY_SIZE(aic3x_dapm_widgets); i++)
+               snd_soc_dapm_new_control(codec, &aic3x_dapm_widgets[i]);
+
+       /* set up audio path interconnects */
+       for (i = 0; intercon[i][0] != NULL; i++)
+               snd_soc_dapm_connect_input(codec, intercon[i][0],
+                                          intercon[i][1], intercon[i][2]);
+
+       snd_soc_dapm_new_widgets(codec);
+       return 0;
+}
+
+struct aic3x_rate_divs {
+       u32 mclk;
+       u32 rate;
+       u32 fsref_reg;
+       u8 sr_reg:4;
+       u8 pllj_reg;
+       u16 plld_reg;
+};
+
+/* AIC3X codec mclk clock divider coefficients */
+static const struct aic3x_rate_divs aic3x_divs[] = {
+       /* 8k */
+       {22579200, 8000, 48000, 0xa, 8, 7075},
+       {33868800, 8000, 48000, 0xa, 5, 8049},
+       /* 11.025k */
+       {22579200, 11025, 44100, 0x6, 8, 0},
+       {33868800, 11025, 44100, 0x6, 5, 3333},
+       /* 16k */
+       {22579200, 16000, 48000, 0x4, 8, 7075},
+       {33868800, 16000, 48000, 0x4, 5, 8049},
+       /* 22.05k */
+       {22579200, 22050, 44100, 0x2, 8, 0},
+       {33868800, 22050, 44100, 0x2, 5, 3333},
+       /* 32k */
+       {22579200, 32000, 48000, 0x1, 8, 7075},
+       {33868800, 32000, 48000, 0x1, 5, 8049},
+       /* 44.1k */
+       {22579200, 44100, 44100, 0x0, 8, 0},
+       {33868800, 44100, 44100, 0x0, 5, 3333},
+       /* 48k */
+       {22579200, 48000, 48000, 0x0, 8, 7075},
+       {33868800, 48000, 48000, 0x0, 5, 8049},
+       /* 64k */
+       {22579200, 96000, 96000, 0x1, 8, 7075},
+       {33868800, 96000, 96000, 0x1, 5, 8049},
+       /* 88.2k */
+       {22579200, 88200, 88200, 0x0, 8, 0},
+       {33868800, 88200, 88200, 0x0, 5, 3333},
+       /* 96k */
+       {22579200, 96000, 96000, 0x0, 8, 7075},
+       {33868800, 96000, 96000, 0x0, 5, 8049},
+};
+
+static inline int aic3x_get_divs(int mclk, int rate)
+{
+       int i;
+
+       for (i = 0; i < ARRAY_SIZE(aic3x_divs); i++) {
+               if (aic3x_divs[i].rate == rate && aic3x_divs[i].mclk == mclk)
+                       return i;
+       }
+
+       return 0;
+}
+
+static int aic3x_hw_params(struct snd_pcm_substream *substream,
+                          struct snd_pcm_hw_params *params)
+{
+       struct snd_soc_pcm_runtime *rtd = substream->private_data;
+       struct snd_soc_device *socdev = rtd->socdev;
+       struct snd_soc_codec *codec = socdev->codec;
+       struct aic3x_priv *aic3x = codec->private_data;
+       int i;
+       u8 data, pll_p, pll_r, pll_j;
+       u16 pll_d;
+
+       i = aic3x_get_divs(aic3x->sysclk, params_rate(params));
+
+       /* Route Left DAC to left channel input and
+        * right DAC to right channel input */
+       data = (LDAC2LCH | RDAC2RCH);
+       switch (aic3x_divs[i].fsref_reg) {
+       case 44100:
+               data |= FSREF_44100;
+               break;
+       case 48000:
+               data |= FSREF_48000;
+               break;
+       case 88200:
+               data |= FSREF_44100 | DUAL_RATE_MODE;
+               break;
+       case 96000:
+               data |= FSREF_48000 | DUAL_RATE_MODE;
+               break;
+       }
+       aic3x_write(codec, AIC3X_CODEC_DATAPATH_REG, data);
+
+       /* codec sample rate select */
+       data = aic3x_divs[i].sr_reg;
+       data |= (data << 4);
+       aic3x_write(codec, AIC3X_SAMPLE_RATE_SEL_REG, data);
+
+       /* Use PLL for generation Fsref by equation:
+        * Fsref = (MCLK * K * R)/(2048 * P);
+        * Fix P = 2 and R = 1 and calculate K, if
+        * K = J.D, i.e. J - an interger portion of K and D is the fractional
+        * one with 4 digits of precision;
+        * Example:
+        * For MCLK = 22.5792 MHz and Fsref = 48kHz:
+        * Select P = 2, R= 1, K = 8.7074, which results in J = 8, D = 7074
+        */
+       pll_p = 2;
+       pll_r = 1;
+       pll_j = aic3x_divs[i].pllj_reg;
+       pll_d = aic3x_divs[i].plld_reg;
+
+       data = aic3x_read_reg_cache(codec, AIC3X_PLL_PROGA_REG);
+       aic3x_write(codec, AIC3X_PLL_PROGA_REG, data | (pll_p << PLLP_SHIFT));
+       aic3x_write(codec, AIC3X_OVRF_STATUS_AND_PLLR_REG, pll_r << PLLR_SHIFT);
+       aic3x_write(codec, AIC3X_PLL_PROGB_REG, pll_j << PLLJ_SHIFT);
+       aic3x_write(codec, AIC3X_PLL_PROGC_REG, (pll_d >> 6) << PLLD_MSB_SHIFT);
+       aic3x_write(codec, AIC3X_PLL_PROGD_REG,
+                   (pll_d & 0x3F) << PLLD_LSB_SHIFT);
+
+       /* select data word length */
+       data =
+           aic3x_read_reg_cache(codec, AIC3X_ASD_INTF_CTRLB) & (~(0x3 << 4));
+       switch (params_format(params)) {
+       case SNDRV_PCM_FORMAT_S16_LE:
+               break;
+       case SNDRV_PCM_FORMAT_S20_3LE:
+               data |= (0x01 << 4);
+               break;
+       case SNDRV_PCM_FORMAT_S24_LE:
+               data |= (0x02 << 4);
+               break;
+       case SNDRV_PCM_FORMAT_S32_LE:
+               data |= (0x03 << 4);
+               break;
+       }
+       aic3x_write(codec, AIC3X_ASD_INTF_CTRLB, data);
+
+       return 0;
+}
+
+static int aic3x_mute(struct snd_soc_codec_dai *dai, int mute)
+{
+       struct snd_soc_codec *codec = dai->codec;
+       u8 ldac_reg = aic3x_read_reg_cache(codec, LDAC_VOL) & ~MUTE_ON;
+       u8 rdac_reg = aic3x_read_reg_cache(codec, RDAC_VOL) & ~MUTE_ON;
+
+       if (mute) {
+               aic3x_write(codec, LDAC_VOL, ldac_reg | MUTE_ON);
+               aic3x_write(codec, RDAC_VOL, rdac_reg | MUTE_ON);
+       } else {
+               aic3x_write(codec, LDAC_VOL, ldac_reg);
+               aic3x_write(codec, RDAC_VOL, rdac_reg);
+       }
+
+       return 0;
+}
+
+static int aic3x_set_dai_sysclk(struct snd_soc_codec_dai *codec_dai,
+                               int clk_id, unsigned int freq, int dir)
+{
+       struct snd_soc_codec *codec = codec_dai->codec;
+       struct aic3x_priv *aic3x = codec->private_data;
+
+       switch (freq) {
+       case 22579200:
+       case 33868800:
+               aic3x->sysclk = freq;
+               return 0;
+       }
+
+       return -EINVAL;
+}
+
+static int aic3x_set_dai_fmt(struct snd_soc_codec_dai *codec_dai,
+                            unsigned int fmt)
+{
+       struct snd_soc_codec *codec = codec_dai->codec;
+       struct aic3x_priv *aic3x = codec->private_data;
+       u8 iface_areg = 0;
+       u8 iface_breg = 0;
+
+       /* set master/slave audio interface */
+       switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) {
+       case SND_SOC_DAIFMT_CBM_CFM:
+               aic3x->master = 1;
+               iface_areg |= BIT_CLK_MASTER | WORD_CLK_MASTER;
+               break;
+       case SND_SOC_DAIFMT_CBS_CFS:
+               aic3x->master = 0;
+               break;
+       default:
+               return -EINVAL;
+       }
+
+       /* interface format */
+       switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
+       case SND_SOC_DAIFMT_I2S:
+               break;
+       case SND_SOC_DAIFMT_DSP_A:
+               iface_breg |= (0x01 << 6);
+               break;
+       case SND_SOC_DAIFMT_RIGHT_J:
+               iface_breg |= (0x02 << 6);
+               break;
+       case SND_SOC_DAIFMT_LEFT_J:
+               iface_breg |= (0x03 << 6);
+               break;
+       default:
+               return -EINVAL;
+       }
+
+       /* set iface */
+       aic3x_write(codec, AIC3X_ASD_INTF_CTRLA, iface_areg);
+       aic3x_write(codec, AIC3X_ASD_INTF_CTRLB, iface_breg);
+
+       return 0;
+}
+
+static int aic3x_dapm_event(struct snd_soc_codec *codec, int event)
+{
+       struct aic3x_priv *aic3x = codec->private_data;
+       u8 reg;
+
+       switch (event) {
+       case SNDRV_CTL_POWER_D0:
+               /* all power is driven by DAPM system */
+               if (aic3x->master) {
+                       /* enable pll */
+                       reg = aic3x_read_reg_cache(codec, AIC3X_PLL_PROGA_REG);
+                       aic3x_write(codec, AIC3X_PLL_PROGA_REG,
+                                   reg | PLL_ENABLE);
+               }
+               break;
+       case SNDRV_CTL_POWER_D1:
+       case SNDRV_CTL_POWER_D2:
+               break;
+       case SNDRV_CTL_POWER_D3hot:
+               /*
+                * all power is driven by DAPM system,
+                * so output power is safe if bypass was set
+                */
+               if (aic3x->master) {
+                       /* disable pll */
+                       reg = aic3x_read_reg_cache(codec, AIC3X_PLL_PROGA_REG);
+                       aic3x_write(codec, AIC3X_PLL_PROGA_REG,
+                                   reg & ~PLL_ENABLE);
+               }
+               break;
+       case SNDRV_CTL_POWER_D3cold:
+               /* force all power off */
+               reg = aic3x_read_reg_cache(codec, LINE1L_2_LADC_CTRL);
+               aic3x_write(codec, LINE1L_2_LADC_CTRL, reg & ~LADC_PWR_ON);
+               reg = aic3x_read_reg_cache(codec, LINE1R_2_RADC_CTRL);
+               aic3x_write(codec, LINE1R_2_RADC_CTRL, reg & ~RADC_PWR_ON);
+
+               reg = aic3x_read_reg_cache(codec, DAC_PWR);
+               aic3x_write(codec, DAC_PWR, reg & ~(LDAC_PWR_ON | RDAC_PWR_ON));
+
+               reg = aic3x_read_reg_cache(codec, HPLOUT_CTRL);
+               aic3x_write(codec, HPLOUT_CTRL, reg & ~HPLOUT_PWR_ON);
+               reg = aic3x_read_reg_cache(codec, HPROUT_CTRL);
+               aic3x_write(codec, HPROUT_CTRL, reg & ~HPROUT_PWR_ON);
+
+               reg = aic3x_read_reg_cache(codec, HPLCOM_CTRL);
+               aic3x_write(codec, HPLCOM_CTRL, reg & ~HPLCOM_PWR_ON);
+               reg = aic3x_read_reg_cache(codec, HPRCOM_CTRL);
+               aic3x_write(codec, HPRCOM_CTRL, reg & ~HPRCOM_PWR_ON);
+
+               reg = aic3x_read_reg_cache(codec, MONOLOPM_CTRL);
+               aic3x_write(codec, MONOLOPM_CTRL, reg & ~MONOLOPM_PWR_ON);
+
+               reg = aic3x_read_reg_cache(codec, LLOPM_CTRL);
+               aic3x_write(codec, LLOPM_CTRL, reg & ~LLOPM_PWR_ON);
+               reg = aic3x_read_reg_cache(codec, RLOPM_CTRL);
+               aic3x_write(codec, RLOPM_CTRL, reg & ~RLOPM_PWR_ON);
+
+               if (aic3x->master) {
+                       /* disable pll */
+                       reg = aic3x_read_reg_cache(codec, AIC3X_PLL_PROGA_REG);
+                       aic3x_write(codec, AIC3X_PLL_PROGA_REG,
+                                   reg & ~PLL_ENABLE);
+               }
+               break;
+       }
+       codec->dapm_state = event;
+
+       return 0;
+}
+
+#define AIC3X_RATES    SNDRV_PCM_RATE_8000_96000
+#define AIC3X_FORMATS  (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S20_3LE | \
+                        SNDRV_PCM_FMTBIT_S24_3LE | SNDRV_PCM_FMTBIT_S32_LE)
+
+struct snd_soc_codec_dai aic3x_dai = {
+       .name = "aic3x",
+       .playback = {
+               .stream_name = "Playback",
+               .channels_min = 1,
+               .channels_max = 2,
+               .rates = AIC3X_RATES,
+               .formats = AIC3X_FORMATS,},
+       .capture = {
+               .stream_name = "Capture",
+               .channels_min = 1,
+               .channels_max = 2,
+               .rates = AIC3X_RATES,
+               .formats = AIC3X_FORMATS,},
+       .ops = {
+               .hw_params = aic3x_hw_params,
+       },
+       .dai_ops = {
+               .digital_mute = aic3x_mute,
+               .set_sysclk = aic3x_set_dai_sysclk,
+               .set_fmt = aic3x_set_dai_fmt,
+       }
+};
+EXPORT_SYMBOL_GPL(aic3x_dai);
+
+static int aic3x_suspend(struct platform_device *pdev, pm_message_t state)
+{
+       struct snd_soc_device *socdev = platform_get_drvdata(pdev);
+       struct snd_soc_codec *codec = socdev->codec;
+
+       aic3x_dapm_event(codec, SNDRV_CTL_POWER_D3cold);
+
+       return 0;
+}
+
+static int aic3x_resume(struct platform_device *pdev)
+{
+       struct snd_soc_device *socdev = platform_get_drvdata(pdev);
+       struct snd_soc_codec *codec = socdev->codec;
+       int i;
+       u8 data[2];
+       u8 *cache = codec->reg_cache;
+
+       /* Sync reg_cache with the hardware */
+       for (i = 0; i < ARRAY_SIZE(aic3x_reg); i++) {
+               data[0] = i;
+               data[1] = cache[i];
+               codec->hw_write(codec->control_data, data, 2);
+       }
+
+       aic3x_dapm_event(codec, codec->suspend_dapm_state);
+
+       return 0;
+}
+
+/*
+ * initialise the AIC3X driver
+ * register the mixer and dsp interfaces with the kernel
+ */
+static int aic3x_init(struct snd_soc_device *socdev)
+{
+       struct snd_soc_codec *codec = socdev->codec;
+       int reg, ret = 0;
+
+       codec->name = "aic3x";
+       codec->owner = THIS_MODULE;
+       codec->read = aic3x_read_reg_cache;
+       codec->write = aic3x_write;
+       codec->dapm_event = aic3x_dapm_event;
+       codec->dai = &aic3x_dai;
+       codec->num_dai = 1;
+       codec->reg_cache_size = sizeof(aic3x_reg);
+       codec->reg_cache = kmemdup(aic3x_reg, sizeof(aic3x_reg), GFP_KERNEL);
+       if (codec->reg_cache == NULL)
+               return -ENOMEM;
+
+       aic3x_write(codec, AIC3X_PAGE_SELECT, PAGE0_SELECT);
+       aic3x_write(codec, AIC3X_RESET, SOFT_RESET);
+
+       /* register pcms */
+       ret = snd_soc_new_pcms(socdev, SNDRV_DEFAULT_IDX1, SNDRV_DEFAULT_STR1);
+       if (ret < 0) {
+               printk(KERN_ERR "aic3x: failed to create pcms\n");
+               goto pcm_err;
+       }
+
+       /* DAC default volume and mute */
+       aic3x_write(codec, LDAC_VOL, DEFAULT_VOL | MUTE_ON);
+       aic3x_write(codec, RDAC_VOL, DEFAULT_VOL | MUTE_ON);
+
+       /* DAC to HP default volume and route to Output mixer */
+       aic3x_write(codec, DACL1_2_HPLOUT_VOL, DEFAULT_VOL | ROUTE_ON);
+       aic3x_write(codec, DACR1_2_HPROUT_VOL, DEFAULT_VOL | ROUTE_ON);
+       aic3x_write(codec, DACL1_2_HPLCOM_VOL, DEFAULT_VOL | ROUTE_ON);
+       aic3x_write(codec, DACR1_2_HPRCOM_VOL, DEFAULT_VOL | ROUTE_ON);
+       /* DAC to Line Out default volume and route to Output mixer */
+       aic3x_write(codec, DACL1_2_LLOPM_VOL, DEFAULT_VOL | ROUTE_ON);
+       aic3x_write(codec, DACR1_2_RLOPM_VOL, DEFAULT_VOL | ROUTE_ON);
+       /* DAC to Mono Line Out default volume and route to Output mixer */
+       aic3x_write(codec, DACL1_2_MONOLOPM_VOL, DEFAULT_VOL | ROUTE_ON);
+       aic3x_write(codec, DACR1_2_MONOLOPM_VOL, DEFAULT_VOL | ROUTE_ON);
+
+       /* unmute all outputs */
+       reg = aic3x_read_reg_cache(codec, LLOPM_CTRL);
+       aic3x_write(codec, LLOPM_CTRL, reg | UNMUTE);
+       reg = aic3x_read_reg_cache(codec, RLOPM_CTRL);
+       aic3x_write(codec, RLOPM_CTRL, reg | UNMUTE);
+       reg = aic3x_read_reg_cache(codec, MONOLOPM_CTRL);
+       aic3x_write(codec, MONOLOPM_CTRL, reg | UNMUTE);
+       reg = aic3x_read_reg_cache(codec, HPLOUT_CTRL);
+       aic3x_write(codec, HPLOUT_CTRL, reg | UNMUTE);
+       reg = aic3x_read_reg_cache(codec, HPROUT_CTRL);
+       aic3x_write(codec, HPROUT_CTRL, reg | UNMUTE);
+       reg = aic3x_read_reg_cache(codec, HPLCOM_CTRL);
+       aic3x_write(codec, HPLCOM_CTRL, reg | UNMUTE);
+       reg = aic3x_read_reg_cache(codec, HPRCOM_CTRL);
+       aic3x_write(codec, HPRCOM_CTRL, reg | UNMUTE);
+
+       /* ADC default volume and unmute */
+       aic3x_write(codec, LADC_VOL, DEFAULT_GAIN);
+       aic3x_write(codec, RADC_VOL, DEFAULT_GAIN);
+       /* By default route Line1 to ADC PGA mixer */
+       aic3x_write(codec, LINE1L_2_LADC_CTRL, 0x0);
+       aic3x_write(codec, LINE1R_2_RADC_CTRL, 0x0);
+
+       /* PGA to HP Bypass default volume, disconnect from Output Mixer */
+       aic3x_write(codec, PGAL_2_HPLOUT_VOL, DEFAULT_VOL);
+       aic3x_write(codec, PGAR_2_HPROUT_VOL, DEFAULT_VOL);
+       aic3x_write(codec, PGAL_2_HPLCOM_VOL, DEFAULT_VOL);
+       aic3x_write(codec, PGAR_2_HPRCOM_VOL, DEFAULT_VOL);
+       /* PGA to Line Out default volume, disconnect from Output Mixer */
+       aic3x_write(codec, PGAL_2_LLOPM_VOL, DEFAULT_VOL);
+       aic3x_write(codec, PGAR_2_RLOPM_VOL, DEFAULT_VOL);
+       /* PGA to Mono Line Out default volume, disconnect from Output Mixer */
+       aic3x_write(codec, PGAL_2_MONOLOPM_VOL, DEFAULT_VOL);
+       aic3x_write(codec, PGAR_2_MONOLOPM_VOL, DEFAULT_VOL);
+
+       /* Line2 to HP Bypass default volume, disconnect from Output Mixer */
+       aic3x_write(codec, LINE2L_2_HPLOUT_VOL, DEFAULT_VOL);
+       aic3x_write(codec, LINE2R_2_HPROUT_VOL, DEFAULT_VOL);
+       aic3x_write(codec, LINE2L_2_HPLCOM_VOL, DEFAULT_VOL);
+       aic3x_write(codec, LINE2R_2_HPRCOM_VOL, DEFAULT_VOL);
+       /* Line2 Line Out default volume, disconnect from Output Mixer */
+       aic3x_write(codec, LINE2L_2_LLOPM_VOL, DEFAULT_VOL);
+       aic3x_write(codec, LINE2R_2_RLOPM_VOL, DEFAULT_VOL);
+       /* Line2 to Mono Out default volume, disconnect from Output Mixer */
+       aic3x_write(codec, LINE2L_2_MONOLOPM_VOL, DEFAULT_VOL);
+       aic3x_write(codec, LINE2R_2_MONOLOPM_VOL, DEFAULT_VOL);
+
+       /* off, with power on */
+       aic3x_dapm_event(codec, SNDRV_CTL_POWER_D3hot);
+
+       aic3x_add_controls(codec);
+       aic3x_add_widgets(codec);
+       ret = snd_soc_register_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;
+}
+
+static struct snd_soc_device *aic3x_socdev;
+
+#if defined(CONFIG_I2C) || defined(CONFIG_I2C_MODULE)
+/*
+ * AIC3X 2 wire address can be up to 4 devices with device addresses
+ * 0x18, 0x19, 0x1A, 0x1B
+ */
+static unsigned short normal_i2c[] = { 0, I2C_CLIENT_END };
+
+/* Magic definition of all other variables and things */
+I2C_CLIENT_INSMOD;
+
+static struct i2c_driver aic3x_i2c_driver;
+static struct i2c_client client_template;
+
+/*
+ * If the i2c layer weren't so broken, we could pass this kind of data
+ * around
+ */
+static int aic3x_codec_probe(struct i2c_adapter *adap, int addr, int kind)
+{
+       struct snd_soc_device *socdev = aic3x_socdev;
+       struct aic3x_setup_data *setup = socdev->codec_data;
+       struct snd_soc_codec *codec = socdev->codec;
+       struct i2c_client *i2c;
+       int ret;
+
+       if (addr != setup->i2c_address)
+               return -ENODEV;
+
+       client_template.adapter = adap;
+       client_template.addr = addr;
+
+       i2c = kmemdup(&client_template, sizeof(client_template), GFP_KERNEL);
+       if (i2c == NULL) {
+               kfree(codec);
+               return -ENOMEM;
+       }
+       i2c_set_clientdata(i2c, codec);
+       codec->control_data = i2c;
+
+       ret = i2c_attach_client(i2c);
+       if (ret < 0) {
+               printk(KERN_ERR "aic3x: failed to attach codec at addr %x\n",
+                      addr);
+               goto err;
+       }
+
+       ret = aic3x_init(socdev);
+       if (ret < 0) {
+               printk(KERN_ERR "aic3x: failed to initialise AIC3X\n");
+               goto err;
+       }
+       return ret;
+
+err:
+       kfree(codec);
+       kfree(i2c);
+       return ret;
+}
+
+static int aic3x_i2c_detach(struct i2c_client *client)
+{
+       struct snd_soc_codec *codec = i2c_get_clientdata(client);
+       i2c_detach_client(client);
+       kfree(codec->reg_cache);
+       kfree(client);
+       return 0;
+}
+
+static int aic3x_i2c_attach(struct i2c_adapter *adap)
+{
+       return i2c_probe(adap, &addr_data, aic3x_codec_probe);
+}
+
+/* machine i2c codec control layer */
+static struct i2c_driver aic3x_i2c_driver = {
+       .driver = {
+               .name = "aic3x I2C Codec",
+               .owner = THIS_MODULE,
+       },
+       .id = I2C_DRIVERID_I2CDEV,
+       .attach_adapter = aic3x_i2c_attach,
+       .detach_client = aic3x_i2c_detach,
+       .command = NULL,
+};
+
+static struct i2c_client client_template = {
+       .name = "AIC3X",
+       .driver = &aic3x_i2c_driver,
+};
+#endif
+
+static int aic3x_probe(struct platform_device *pdev)
+{
+       struct snd_soc_device *socdev = platform_get_drvdata(pdev);
+       struct aic3x_setup_data *setup;
+       struct snd_soc_codec *codec;
+       struct aic3x_priv *aic3x;
+       int ret = 0;
+
+       printk(KERN_INFO "AIC3X Audio Codec %s\n", AIC3X_VERSION);
+
+       setup = socdev->codec_data;
+       codec = kzalloc(sizeof(struct snd_soc_codec), GFP_KERNEL);
+       if (codec == NULL)
+               return -ENOMEM;
+
+       aic3x = kzalloc(sizeof(struct aic3x_priv), GFP_KERNEL);
+       if (aic3x == NULL) {
+               kfree(codec);
+               return -ENOMEM;
+       }
+
+       codec->private_data = aic3x;
+       socdev->codec = codec;
+       mutex_init(&codec->mutex);
+       INIT_LIST_HEAD(&codec->dapm_widgets);
+       INIT_LIST_HEAD(&codec->dapm_paths);
+
+       aic3x_socdev = socdev;
+#if defined(CONFIG_I2C) || defined(CONFIG_I2C_MODULE)
+       if (setup->i2c_address) {
+               normal_i2c[0] = setup->i2c_address;
+               codec->hw_write = (hw_write_t) i2c_master_send;
+               ret = i2c_add_driver(&aic3x_i2c_driver);
+               if (ret != 0)
+                       printk(KERN_ERR "can't add i2c driver");
+       }
+#else
+       /* Add other interfaces here */
+#endif
+       return ret;
+}
+
+static int aic3x_remove(struct platform_device *pdev)
+{
+       struct snd_soc_device *socdev = platform_get_drvdata(pdev);
+       struct snd_soc_codec *codec = socdev->codec;
+
+       /* power down chip */
+       if (codec->control_data)
+               aic3x_dapm_event(codec, SNDRV_CTL_POWER_D3);
+
+       snd_soc_free_pcms(socdev);
+       snd_soc_dapm_free(socdev);
+#if defined(CONFIG_I2C) || defined(CONFIG_I2C_MODULE)
+       i2c_del_driver(&aic3x_i2c_driver);
+#endif
+       kfree(codec->private_data);
+       kfree(codec);
+
+       return 0;
+}
+
+struct snd_soc_codec_device soc_codec_dev_aic3x = {
+       .probe = aic3x_probe,
+       .remove = aic3x_remove,
+       .suspend = aic3x_suspend,
+       .resume = aic3x_resume,
+};
+EXPORT_SYMBOL_GPL(soc_codec_dev_aic3x);
+
+MODULE_DESCRIPTION("ASoC TLV320AIC3X codec driver");
+MODULE_AUTHOR("Vladimir Barinov");
+MODULE_LICENSE("GPL");
diff --git a/sound/soc/codecs/tlv320aic3x.h b/sound/soc/codecs/tlv320aic3x.h
new file mode 100644 (file)
index 0000000..d0cdeeb
--- /dev/null
@@ -0,0 +1,181 @@
+/*
+ * ALSA SoC TLV320AIC3X codec driver
+ *
+ * Author:      Vladimir Barinov, <vbarinov@ru.mvista.com>
+ * Copyright:   (C) 2007 MontaVista Software, Inc., <source@mvista.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 _AIC3X_H
+#define _AIC3X_H
+
+/* AIC3X register space */
+#define AIC3X_CACHEREGNUM              103
+
+/* Page select register */
+#define AIC3X_PAGE_SELECT              0
+/* Software reset register */
+#define AIC3X_RESET                    1
+/* Codec Sample rate select register */
+#define AIC3X_SAMPLE_RATE_SEL_REG      2
+/* PLL progrramming register A */
+#define AIC3X_PLL_PROGA_REG            3
+/* PLL progrramming register B */
+#define AIC3X_PLL_PROGB_REG            4
+/* PLL progrramming register C */
+#define AIC3X_PLL_PROGC_REG            5
+/* PLL progrramming register D */
+#define AIC3X_PLL_PROGD_REG            6
+/* Codec datapath setup register */
+#define AIC3X_CODEC_DATAPATH_REG       7
+/* Audio serial data interface control register A */
+#define AIC3X_ASD_INTF_CTRLA           8
+/* Audio serial data interface control register B */
+#define AIC3X_ASD_INTF_CTRLB           9
+/* Audio overflow status and PLL R value programming register */
+#define AIC3X_OVRF_STATUS_AND_PLLR_REG 11
+
+/* ADC PGA Gain control registers */
+#define LADC_VOL                       15
+#define RADC_VOL                       16
+/* MIC3 control registers */
+#define MIC3LR_2_LADC_CTRL             17
+#define MIC3LR_2_RADC_CTRL             18
+/* Line1 Input control registers */
+#define LINE1L_2_LADC_CTRL             19
+#define LINE1R_2_RADC_CTRL             22
+/* Line2 Input control registers */
+#define LINE2L_2_LADC_CTRL             20
+#define LINE2R_2_RADC_CTRL             23
+/* MICBIAS Control Register */
+#define MICBIAS_CTRL                   25
+
+/* AGC Control Registers A, B, C */
+#define LAGC_CTRL_A                    26
+#define LAGC_CTRL_B                    27
+#define LAGC_CTRL_C                    28
+#define RAGC_CTRL_A                    29
+#define RAGC_CTRL_B                    30
+#define RAGC_CTRL_C                    31
+
+/* DAC Power and Left High Power Output control registers */
+#define DAC_PWR                                37
+#define HPLCOM_CFG                     37
+/* Right High Power Output control registers */
+#define HPRCOM_CFG                     38
+/* DAC Output Switching control registers */
+#define DAC_LINE_MUX                   41
+/* High Power Output Driver Pop Reduction registers */
+#define HPOUT_POP_REDUCTION            42
+/* DAC Digital control registers */
+#define LDAC_VOL                       43
+#define RDAC_VOL                       44
+/* High Power Output control registers */
+#define LINE2L_2_HPLOUT_VOL            45
+#define LINE2R_2_HPROUT_VOL            62
+#define PGAL_2_HPLOUT_VOL              46
+#define PGAR_2_HPROUT_VOL              63
+#define DACL1_2_HPLOUT_VOL             47
+#define DACR1_2_HPROUT_VOL             64
+#define HPLOUT_CTRL                    51
+#define HPROUT_CTRL                    65
+/* High Power COM control registers */
+#define LINE2L_2_HPLCOM_VOL            52
+#define LINE2R_2_HPRCOM_VOL            69
+#define PGAL_2_HPLCOM_VOL              53
+#define PGAR_2_HPRCOM_VOL              70
+#define DACL1_2_HPLCOM_VOL             54
+#define DACR1_2_HPRCOM_VOL             71
+#define HPLCOM_CTRL                    58
+#define HPRCOM_CTRL                    72
+/* Mono Line Output Plus/Minus control registers */
+#define LINE2L_2_MONOLOPM_VOL          73
+#define LINE2R_2_MONOLOPM_VOL          76
+#define PGAL_2_MONOLOPM_VOL            74
+#define PGAR_2_MONOLOPM_VOL            77
+#define DACL1_2_MONOLOPM_VOL           75
+#define DACR1_2_MONOLOPM_VOL           78
+#define MONOLOPM_CTRL                  79
+/* Line Output Plus/Minus control registers */
+#define LINE2L_2_LLOPM_VOL             80
+#define LINE2R_2_RLOPM_VOL             90
+#define PGAL_2_LLOPM_VOL               81
+#define PGAR_2_RLOPM_VOL               91
+#define DACL1_2_LLOPM_VOL              82
+#define DACR1_2_RLOPM_VOL              92
+#define LLOPM_CTRL                     86
+#define RLOPM_CTRL                     93
+/* Clock generation control register */
+#define AIC3X_CLKGEN_CTRL_REG          102
+
+/* Page select register bits */
+#define PAGE0_SELECT           0
+#define PAGE1_SELECT           1
+
+/* Audio serial data interface control register A bits */
+#define BIT_CLK_MASTER          0x80
+#define WORD_CLK_MASTER         0x40
+
+/* Codec Datapath setup register 7 */
+#define FSREF_44100            (1 << 7)
+#define FSREF_48000            (0 << 7)
+#define DUAL_RATE_MODE         ((1 << 5) | (1 << 6))
+#define LDAC2LCH               (0x1 << 3)
+#define RDAC2RCH               (0x1 << 1)
+
+/* PLL registers bitfields */
+#define PLLP_SHIFT             0
+#define PLLR_SHIFT             0
+#define PLLJ_SHIFT             2
+#define PLLD_MSB_SHIFT         0
+#define PLLD_LSB_SHIFT         2
+
+/* Clock generation register bits */
+#define PLL_CLKIN_SHIFT                4
+#define MCLK_SOURCE            0x0
+#define PLL_CLKDIV_SHIFT       0
+
+/* Software reset register bits */
+#define SOFT_RESET             0x80
+
+/* PLL progrramming register A bits */
+#define PLL_ENABLE             0x80
+
+/* Route bits */
+#define ROUTE_ON               0x80
+
+/* Mute bits */
+#define UNMUTE                 0x08
+#define MUTE_ON                        0x80
+
+/* Power bits */
+#define LADC_PWR_ON            0x04
+#define RADC_PWR_ON            0x04
+#define LDAC_PWR_ON            0x80
+#define RDAC_PWR_ON            0x40
+#define HPLOUT_PWR_ON          0x01
+#define HPROUT_PWR_ON          0x01
+#define HPLCOM_PWR_ON          0x01
+#define HPRCOM_PWR_ON          0x01
+#define MONOLOPM_PWR_ON                0x01
+#define LLOPM_PWR_ON           0x01
+#define RLOPM_PWR_ON   0x01
+
+#define INVERT_VOL(val)   (0x7f - val)
+
+/* Default output volume (inverted) */
+#define DEFAULT_VOL     INVERT_VOL(0x50)
+/* Default input volume */
+#define DEFAULT_GAIN    0x20
+
+struct aic3x_setup_data {
+       unsigned short i2c_address;
+};
+
+extern struct snd_soc_codec_dai aic3x_dai;
+extern struct snd_soc_codec_device soc_codec_dev_aic3x;
+
+#endif /* _AIC3X_H */
index 7ca0b5268289e9e6260bf620f814dc24c2718368..9c33fe874928ec2ae746d112334a622e20d7dc14 100644 (file)
@@ -19,7 +19,6 @@
 #include <linux/pm.h>
 #include <linux/i2c.h>
 #include <linux/platform_device.h>
-#include <sound/driver.h>
 #include <sound/core.h>
 #include <sound/pcm.h>
 #include <sound/pcm_params.h>
@@ -562,13 +561,13 @@ static int wm8731_init(struct snd_soc_device *socdev)
 
        /* set the update bits */
        reg = wm8731_read_reg_cache(codec, WM8731_LOUT1V);
-       wm8731_write(codec, WM8731_LOUT1V, reg 0x0100);
+       wm8731_write(codec, WM8731_LOUT1V, reg & ~0x0100);
        reg = wm8731_read_reg_cache(codec, WM8731_ROUT1V);
-       wm8731_write(codec, WM8731_ROUT1V, reg 0x0100);
+       wm8731_write(codec, WM8731_ROUT1V, reg & ~0x0100);
        reg = wm8731_read_reg_cache(codec, WM8731_LINVOL);
-       wm8731_write(codec, WM8731_LINVOL, reg 0x0100);
+       wm8731_write(codec, WM8731_LINVOL, reg & ~0x0100);
        reg = wm8731_read_reg_cache(codec, WM8731_RINVOL);
-       wm8731_write(codec, WM8731_RINVOL, reg 0x0100);
+       wm8731_write(codec, WM8731_RINVOL, reg & ~0x0100);
 
        wm8731_add_controls(codec);
        wm8731_add_widgets(codec);
index 28684eeda73845dd4a68745860f19c8631a465cf..77a857b997a2288e28ca2bc993a446057112dbff 100644 (file)
@@ -19,7 +19,6 @@
 #include <linux/pm.h>
 #include <linux/i2c.h>
 #include <linux/platform_device.h>
-#include <sound/driver.h>
 #include <sound/core.h>
 #include <sound/pcm.h>
 #include <sound/pcm_params.h>
@@ -189,7 +188,7 @@ SOC_ENUM("Bass Boost", wm8750_enum[0]),
 SOC_ENUM("Bass Filter", wm8750_enum[1]),
 SOC_SINGLE("Bass Volume", WM8750_BASS, 0, 15, 1),
 
-SOC_SINGLE("Treble Volume", WM8750_TREBLE, 0, 15, 0),
+SOC_SINGLE("Treble Volume", WM8750_TREBLE, 0, 15, 1),
 SOC_ENUM("Treble Cut-off", wm8750_enum[2]),
 
 SOC_SINGLE("3D Switch", WM8750_3D, 0, 1, 0),
index efced934566d73bc950c915d318b1025e6ccac33..ddd9c71b3fdec189b61a1cc31f872ef6223bee77 100644 (file)
 #include <linux/pm.h>
 #include <linux/i2c.h>
 #include <linux/platform_device.h>
-#include <sound/driver.h>
 #include <sound/core.h>
 #include <sound/pcm.h>
 #include <sound/pcm_params.h>
 #include <sound/soc.h>
 #include <sound/soc-dapm.h>
 #include <sound/initval.h>
+#include <sound/tlv.h>
 #include <asm/div64.h>
 
 #include "wm8753.h"
@@ -258,6 +258,8 @@ static int wm8753_set_dai(struct snd_kcontrol *kcontrol,
        return 1;
 }
 
+static const DECLARE_TLV_DB_LINEAR(rec_mix_tlv, -1500, 600);
+
 static const struct snd_kcontrol_new wm8753_snd_controls[] = {
 SOC_DOUBLE_R("PCM Volume", WM8753_LDAC, WM8753_RDAC, 0, 255, 0),
 
@@ -287,8 +289,8 @@ SOC_SINGLE("Bass Volume", WM8753_BASS, 0, 15, 1),
 SOC_SINGLE("Treble Volume", WM8753_TREBLE, 0, 15, 1),
 SOC_ENUM("Treble Cut-off", wm8753_enum[2]),
 
-SOC_DOUBLE("Sidetone Capture Volume", WM8753_RECMIX1, 0, 4, 7, 1),
-SOC_SINGLE("Voice Sidetone Capture Volume", WM8753_RECMIX2, 0, 7, 1),
+SOC_DOUBLE_TLV("Sidetone Capture Volume", WM8753_RECMIX1, 0, 4, 7, 1, rec_mix_tlv),
+SOC_SINGLE_TLV("Voice Sidetone Capture Volume", WM8753_RECMIX2, 0, 7, 1, rec_mix_tlv),
 
 SOC_DOUBLE_R("Capture Volume", WM8753_LINVOL, WM8753_RINVOL, 0, 63, 0),
 SOC_DOUBLE_R("Capture ZC Switch", WM8753_LINVOL, WM8753_RINVOL, 6, 1, 0),
index 986b5d59cefaccd618a4f44d51266c832fb131c6..590baea3c4c3d9d3a2666025615b27f3c506545c 100644 (file)
@@ -19,7 +19,6 @@
 #include <linux/version.h>
 #include <linux/kernel.h>
 #include <linux/device.h>
-#include <sound/driver.h>
 #include <sound/core.h>
 #include <sound/pcm.h>
 #include <sound/ac97_codec.h>
@@ -102,7 +101,8 @@ SOC_SINGLE("Speaker Playback ZC Switch", AC97_MASTER, 7, 1, 0),
 SOC_SINGLE("Speaker Playback Invert Switch", AC97_MASTER, 6, 1, 0),
 SOC_SINGLE("Headphone Playback ZC Switch", AC97_HEADPHONE, 7, 1, 0),
 SOC_SINGLE("Mono Playback ZC Switch", AC97_MASTER_MONO, 7, 1, 0),
-SOC_SINGLE("Mono Playback Volume", AC97_MASTER_MONO, 0, 31, 0),
+SOC_SINGLE("Mono Playback Volume", AC97_MASTER_MONO, 0, 31, 1),
+SOC_SINGLE("Mono Playback Switch", AC97_MASTER_MONO, 15, 1, 1),
 
 SOC_SINGLE("ALC Target Volume", AC97_CODEC_CLASS_REV, 12, 15, 0),
 SOC_SINGLE("ALC Hold Time", AC97_CODEC_CLASS_REV, 8, 15, 0),
@@ -131,7 +131,7 @@ SOC_SINGLE("Aux Playback Headphone Volume", AC97_CD, 12, 7, 1),
 SOC_SINGLE("Aux Playback Speaker Volume", AC97_CD, 8, 7, 1),
 SOC_SINGLE("Aux Playback Phone Volume", AC97_CD, 4, 7, 1),
 
-SOC_SINGLE("Phone Volume", AC97_PHONE, 0, 15, 0),
+SOC_SINGLE("Phone Volume", AC97_PHONE, 0, 15, 1),
 SOC_DOUBLE("Line Capture Volume", AC97_LINE, 8, 0, 31, 1),
 
 SOC_SINGLE("Capture 20dB Boost Switch", AC97_REC_SEL, 14, 1, 0),
@@ -145,8 +145,8 @@ SOC_ENUM("Bass Control", wm9712_enum[5]),
 SOC_SINGLE("Bass Cut-off Switch", AC97_MASTER_TONE, 12, 1, 1),
 SOC_SINGLE("Tone Cut-off Switch", AC97_MASTER_TONE, 4, 1, 1),
 SOC_SINGLE("Playback Attenuate (-6dB) Switch", AC97_MASTER_TONE, 6, 1, 0),
-SOC_SINGLE("Bass Volume", AC97_MASTER_TONE, 8, 15, 0),
-SOC_SINGLE("Treble Volume", AC97_MASTER_TONE, 0, 15, 0),
+SOC_SINGLE("Bass Volume", AC97_MASTER_TONE, 8, 15, 1),
+SOC_SINGLE("Treble Volume", AC97_MASTER_TONE, 0, 15, 1),
 
 SOC_SINGLE("Capture ADC Switch", AC97_REC_GAIN, 15, 1, 1),
 SOC_ENUM("Capture Volume Steps", wm9712_enum[6]),
diff --git a/sound/soc/fsl/Kconfig b/sound/soc/fsl/Kconfig
new file mode 100644 (file)
index 0000000..257101f
--- /dev/null
@@ -0,0 +1,20 @@
+menu "ALSA SoC audio for Freescale SOCs"
+
+config SND_SOC_MPC8610
+       bool "ALSA SoC support for the MPC8610 SOC"
+       depends on SND_SOC && MPC8610_HPCD
+       default y if MPC8610
+       help
+         Say Y if you want to add support for codecs attached to the SSI
+          device on an MPC8610.
+
+config SND_SOC_MPC8610_HPCD
+       bool "ALSA SoC support for the Freescale MPC8610 HPCD board"
+       depends on SND_SOC_MPC8610
+       select SND_SOC_CS4270
+       select SND_SOC_CS4270_VD33_ERRATA
+       default y if MPC8610_HPCD
+       help
+         Say Y if you want to enable audio on the Freescale MPC8610 HPCD.
+
+endmenu
diff --git a/sound/soc/fsl/Makefile b/sound/soc/fsl/Makefile
new file mode 100644 (file)
index 0000000..62f680a
--- /dev/null
@@ -0,0 +1,6 @@
+# MPC8610 HPCD Machine Support
+obj-$(CONFIG_SND_SOC_MPC8610_HPCD) += mpc8610_hpcd.o
+
+# MPC8610 Platform Support
+obj-$(CONFIG_SND_SOC_MPC8610) += fsl_ssi.o fsl_dma.o
+
diff --git a/sound/soc/fsl/fsl_dma.c b/sound/soc/fsl/fsl_dma.c
new file mode 100644 (file)
index 0000000..652514f
--- /dev/null
@@ -0,0 +1,841 @@
+/*
+ * Freescale DMA ALSA SoC PCM driver
+ *
+ * Author: Timur Tabi <timur@freescale.com>
+ *
+ * Copyright 2007-2008 Freescale Semiconductor, Inc.  This file is licensed
+ * under the terms of the GNU General Public License version 2.  This
+ * program is licensed "as is" without any warranty of any kind, whether
+ * express or implied.
+ *
+ * This driver implements ASoC support for the Elo DMA controller, which is
+ * the DMA controller on Freescale 83xx, 85xx, and 86xx SOCs. In ALSA terms,
+ * the PCM driver is what handles the DMA buffer.
+ */
+
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/platform_device.h>
+#include <linux/dma-mapping.h>
+#include <linux/interrupt.h>
+#include <linux/delay.h>
+
+#include <sound/driver.h>
+#include <sound/core.h>
+#include <sound/pcm.h>
+#include <sound/pcm_params.h>
+#include <sound/soc.h>
+
+#include <asm/io.h>
+
+#include "fsl_dma.h"
+
+/*
+ * The formats that the DMA controller supports, which is anything
+ * that is 8, 16, or 32 bits.
+ */
+#define FSLDMA_PCM_FORMATS (SNDRV_PCM_FMTBIT_S8        | \
+                           SNDRV_PCM_FMTBIT_U8         | \
+                           SNDRV_PCM_FMTBIT_S16_LE     | \
+                           SNDRV_PCM_FMTBIT_S16_BE     | \
+                           SNDRV_PCM_FMTBIT_U16_LE     | \
+                           SNDRV_PCM_FMTBIT_U16_BE     | \
+                           SNDRV_PCM_FMTBIT_S24_LE     | \
+                           SNDRV_PCM_FMTBIT_S24_BE     | \
+                           SNDRV_PCM_FMTBIT_U24_LE     | \
+                           SNDRV_PCM_FMTBIT_U24_BE     | \
+                           SNDRV_PCM_FMTBIT_S32_LE     | \
+                           SNDRV_PCM_FMTBIT_S32_BE     | \
+                           SNDRV_PCM_FMTBIT_U32_LE     | \
+                           SNDRV_PCM_FMTBIT_U32_BE)
+
+#define FSLDMA_PCM_RATES (SNDRV_PCM_RATE_5512 | SNDRV_PCM_RATE_8000_192000 | \
+                         SNDRV_PCM_RATE_CONTINUOUS)
+
+/* DMA global data.  This structure is used by fsl_dma_open() to determine
+ * which DMA channels to assign to a substream.  Unfortunately, ASoC V1 does
+ * not allow the machine driver to provide this information to the PCM
+ * driver in advance, and there's no way to differentiate between the two
+ * DMA controllers.  So for now, this driver only supports one SSI device
+ * using two DMA channels.  We cannot support multiple DMA devices.
+ *
+ * ssi_stx_phys: bus address of SSI STX register
+ * ssi_srx_phys: bus address of SSI SRX register
+ * dma_channel: pointer to the DMA channel's registers
+ * irq: IRQ for this DMA channel
+ * assigned: set to 1 if that DMA channel is assigned to a substream
+ */
+static struct {
+       dma_addr_t ssi_stx_phys;
+       dma_addr_t ssi_srx_phys;
+       struct ccsr_dma_channel __iomem *dma_channel[2];
+       unsigned int irq[2];
+       unsigned int assigned[2];
+} dma_global_data;
+
+/*
+ * The number of DMA links to use.  Two is the bare minimum, but if you
+ * have really small links you might need more.
+ */
+#define NUM_DMA_LINKS   2
+
+/** fsl_dma_private: p-substream DMA data
+ *
+ * Each substream has a 1-to-1 association with a DMA channel.
+ *
+ * The link[] array is first because it needs to be aligned on a 32-byte
+ * boundary, so putting it first will ensure alignment without padding the
+ * structure.
+ *
+ * @link[]: array of link descriptors
+ * @controller_id: which DMA controller (0, 1, ...)
+ * @channel_id: which DMA channel on the controller (0, 1, 2, ...)
+ * @dma_channel: pointer to the DMA channel's registers
+ * @irq: IRQ for this DMA channel
+ * @substream: pointer to the substream object, needed by the ISR
+ * @ssi_sxx_phys: bus address of the STX or SRX register to use
+ * @ld_buf_phys: physical address of the LD buffer
+ * @current_link: index into link[] of the link currently being processed
+ * @dma_buf_phys: physical address of the DMA buffer
+ * @dma_buf_next: physical address of the next period to process
+ * @dma_buf_end: physical address of the byte after the end of the DMA
+ * @buffer period_size: the size of a single period
+ * @num_periods: the number of periods in the DMA buffer
+ */
+struct fsl_dma_private {
+       struct fsl_dma_link_descriptor link[NUM_DMA_LINKS];
+       unsigned int controller_id;
+       unsigned int channel_id;
+       struct ccsr_dma_channel __iomem *dma_channel;
+       unsigned int irq;
+       struct snd_pcm_substream *substream;
+       dma_addr_t ssi_sxx_phys;
+       dma_addr_t ld_buf_phys;
+       unsigned int current_link;
+       dma_addr_t dma_buf_phys;
+       dma_addr_t dma_buf_next;
+       dma_addr_t dma_buf_end;
+       size_t period_size;
+       unsigned int num_periods;
+};
+
+/**
+ * fsl_dma_hardare: define characteristics of the PCM hardware.
+ *
+ * The PCM hardware is the Freescale DMA controller.  This structure defines
+ * the capabilities of that hardware.
+ *
+ * Since the sampling rate and data format are not controlled by the DMA
+ * controller, we specify no limits for those values.  The only exception is
+ * period_bytes_min, which is set to a reasonably low value to prevent the
+ * DMA controller from generating too many interrupts per second.
+ *
+ * Since each link descriptor has a 32-bit byte count field, we set
+ * period_bytes_max to the largest 32-bit number.  We also have no maximum
+ * number of periods.
+ */
+static const struct snd_pcm_hardware fsl_dma_hardware = {
+
+       .info                   = SNDRV_PCM_INFO_INTERLEAVED |
+                                 SNDRV_PCM_INFO_MMAP |
+                                 SNDRV_PCM_INFO_MMAP_VALID,
+       .formats                = FSLDMA_PCM_FORMATS,
+       .rates                  = FSLDMA_PCM_RATES,
+       .rate_min               = 5512,
+       .rate_max               = 192000,
+       .period_bytes_min       = 512,          /* A reasonable limit */
+       .period_bytes_max       = (u32) -1,
+       .periods_min            = NUM_DMA_LINKS,
+       .periods_max            = (unsigned int) -1,
+       .buffer_bytes_max       = 128 * 1024,   /* A reasonable limit */
+};
+
+/**
+ * fsl_dma_abort_stream: tell ALSA that the DMA transfer has aborted
+ *
+ * This function should be called by the ISR whenever the DMA controller
+ * halts data transfer.
+ */
+static void fsl_dma_abort_stream(struct snd_pcm_substream *substream)
+{
+       unsigned long flags;
+
+       snd_pcm_stream_lock_irqsave(substream, flags);
+
+       if (snd_pcm_running(substream))
+               snd_pcm_stop(substream, SNDRV_PCM_STATE_XRUN);
+
+       snd_pcm_stream_unlock_irqrestore(substream, flags);
+}
+
+/**
+ * fsl_dma_update_pointers - update LD pointers to point to the next period
+ *
+ * As each period is completed, this function changes the the link
+ * descriptor pointers for that period to point to the next period.
+ */
+static void fsl_dma_update_pointers(struct fsl_dma_private *dma_private)
+{
+       struct fsl_dma_link_descriptor *link =
+               &dma_private->link[dma_private->current_link];
+
+       /* Update our link descriptors to point to the next period */
+       if (dma_private->substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
+               link->source_addr =
+                       cpu_to_be32(dma_private->dma_buf_next);
+       else
+               link->dest_addr =
+                       cpu_to_be32(dma_private->dma_buf_next);
+
+       /* Update our variables for next time */
+       dma_private->dma_buf_next += dma_private->period_size;
+
+       if (dma_private->dma_buf_next >= dma_private->dma_buf_end)
+               dma_private->dma_buf_next = dma_private->dma_buf_phys;
+
+       if (++dma_private->current_link >= NUM_DMA_LINKS)
+               dma_private->current_link = 0;
+}
+
+/**
+ * fsl_dma_isr: interrupt handler for the DMA controller
+ *
+ * @irq: IRQ of the DMA channel
+ * @dev_id: pointer to the dma_private structure for this DMA channel
+ */
+static irqreturn_t fsl_dma_isr(int irq, void *dev_id)
+{
+       struct fsl_dma_private *dma_private = dev_id;
+       struct ccsr_dma_channel __iomem *dma_channel = dma_private->dma_channel;
+       irqreturn_t ret = IRQ_NONE;
+       u32 sr, sr2 = 0;
+
+       /* We got an interrupt, so read the status register to see what we
+          were interrupted for.
+        */
+       sr = in_be32(&dma_channel->sr);
+
+       if (sr & CCSR_DMA_SR_TE) {
+               dev_err(dma_private->substream->pcm->card->dev,
+                       "DMA transmit error (controller=%u channel=%u irq=%u\n",
+                       dma_private->controller_id,
+                       dma_private->channel_id, irq);
+               fsl_dma_abort_stream(dma_private->substream);
+               sr2 |= CCSR_DMA_SR_TE;
+               ret = IRQ_HANDLED;
+       }
+
+       if (sr & CCSR_DMA_SR_CH)
+               ret = IRQ_HANDLED;
+
+       if (sr & CCSR_DMA_SR_PE) {
+               dev_err(dma_private->substream->pcm->card->dev,
+                       "DMA%u programming error (channel=%u irq=%u)\n",
+                       dma_private->controller_id,
+                       dma_private->channel_id, irq);
+               fsl_dma_abort_stream(dma_private->substream);
+               sr2 |= CCSR_DMA_SR_PE;
+               ret = IRQ_HANDLED;
+       }
+
+       if (sr & CCSR_DMA_SR_EOLNI) {
+               sr2 |= CCSR_DMA_SR_EOLNI;
+               ret = IRQ_HANDLED;
+       }
+
+       if (sr & CCSR_DMA_SR_CB)
+               ret = IRQ_HANDLED;
+
+       if (sr & CCSR_DMA_SR_EOSI) {
+               struct snd_pcm_substream *substream = dma_private->substream;
+
+               /* Tell ALSA we completed a period. */
+               snd_pcm_period_elapsed(substream);
+
+               /*
+                * Update our link descriptors to point to the next period. We
+                * only need to do this if the number of periods is not equal to
+                * the number of links.
+                */
+               if (dma_private->num_periods != NUM_DMA_LINKS)
+                       fsl_dma_update_pointers(dma_private);
+
+               sr2 |= CCSR_DMA_SR_EOSI;
+               ret = IRQ_HANDLED;
+       }
+
+       if (sr & CCSR_DMA_SR_EOLSI) {
+               sr2 |= CCSR_DMA_SR_EOLSI;
+               ret = IRQ_HANDLED;
+       }
+
+       /* Clear the bits that we set */
+       if (sr2)
+               out_be32(&dma_channel->sr, sr2);
+
+       return ret;
+}
+
+/**
+ * fsl_dma_new: initialize this PCM driver.
+ *
+ * This function is called when the codec driver calls snd_soc_new_pcms(),
+ * once for each .dai_link in the machine driver's snd_soc_machine
+ * structure.
+ */
+static int fsl_dma_new(struct snd_card *card, struct snd_soc_codec_dai *dai,
+       struct snd_pcm *pcm)
+{
+       static u64 fsl_dma_dmamask = DMA_BIT_MASK(32);
+       int ret;
+
+       if (!card->dev->dma_mask)
+               card->dev->dma_mask = &fsl_dma_dmamask;
+
+       if (!card->dev->coherent_dma_mask)
+               card->dev->coherent_dma_mask = fsl_dma_dmamask;
+
+       ret = snd_dma_alloc_pages(SNDRV_DMA_TYPE_DEV, pcm->dev,
+               fsl_dma_hardware.buffer_bytes_max,
+               &pcm->streams[0].substream->dma_buffer);
+       if (ret) {
+               dev_err(card->dev,
+                       "Can't allocate playback DMA buffer (size=%u)\n",
+                       fsl_dma_hardware.buffer_bytes_max);
+               return -ENOMEM;
+       }
+
+       ret = snd_dma_alloc_pages(SNDRV_DMA_TYPE_DEV, pcm->dev,
+               fsl_dma_hardware.buffer_bytes_max,
+               &pcm->streams[1].substream->dma_buffer);
+       if (ret) {
+               snd_dma_free_pages(&pcm->streams[0].substream->dma_buffer);
+               dev_err(card->dev,
+                       "Can't allocate capture DMA buffer (size=%u)\n",
+                       fsl_dma_hardware.buffer_bytes_max);
+               return -ENOMEM;
+       }
+
+       return 0;
+}
+
+/**
+ * fsl_dma_open: open a new substream.
+ *
+ * Each substream has its own DMA buffer.
+ */
+static int fsl_dma_open(struct snd_pcm_substream *substream)
+{
+       struct snd_pcm_runtime *runtime = substream->runtime;
+       struct fsl_dma_private *dma_private;
+       dma_addr_t ld_buf_phys;
+       unsigned int channel;
+       int ret = 0;
+
+       /*
+        * Reject any DMA buffer whose size is not a multiple of the period
+        * size.  We need to make sure that the DMA buffer can be evenly divided
+        * into periods.
+        */
+       ret = snd_pcm_hw_constraint_integer(runtime,
+               SNDRV_PCM_HW_PARAM_PERIODS);
+       if (ret < 0) {
+               dev_err(substream->pcm->card->dev, "invalid buffer size\n");
+               return ret;
+       }
+
+       channel = substream->stream == SNDRV_PCM_STREAM_PLAYBACK ? 0 : 1;
+
+       if (dma_global_data.assigned[channel]) {
+               dev_err(substream->pcm->card->dev,
+                       "DMA channel already assigned\n");
+               return -EBUSY;
+       }
+
+       dma_private = dma_alloc_coherent(substream->pcm->dev,
+               sizeof(struct fsl_dma_private), &ld_buf_phys, GFP_KERNEL);
+       if (!dma_private) {
+               dev_err(substream->pcm->card->dev,
+                       "can't allocate DMA private data\n");
+               return -ENOMEM;
+       }
+       if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
+               dma_private->ssi_sxx_phys = dma_global_data.ssi_stx_phys;
+       else
+               dma_private->ssi_sxx_phys = dma_global_data.ssi_srx_phys;
+
+       dma_private->dma_channel = dma_global_data.dma_channel[channel];
+       dma_private->irq = dma_global_data.irq[channel];
+       dma_private->substream = substream;
+       dma_private->ld_buf_phys = ld_buf_phys;
+       dma_private->dma_buf_phys = substream->dma_buffer.addr;
+
+       /* We only support one DMA controller for now */
+       dma_private->controller_id = 0;
+       dma_private->channel_id = channel;
+
+       ret = request_irq(dma_private->irq, fsl_dma_isr, 0, "DMA", dma_private);
+       if (ret) {
+               dev_err(substream->pcm->card->dev,
+                       "can't register ISR for IRQ %u (ret=%i)\n",
+                       dma_private->irq, ret);
+               dma_free_coherent(substream->pcm->dev,
+                       sizeof(struct fsl_dma_private),
+                       dma_private, dma_private->ld_buf_phys);
+               return ret;
+       }
+
+       dma_global_data.assigned[channel] = 1;
+
+       snd_pcm_set_runtime_buffer(substream, &substream->dma_buffer);
+       snd_soc_set_runtime_hwparams(substream, &fsl_dma_hardware);
+       runtime->private_data = dma_private;
+
+       return 0;
+}
+
+/**
+ * fsl_dma_hw_params: allocate the DMA buffer and the DMA link descriptors.
+ *
+ * ALSA divides the DMA buffer into N periods.  We create NUM_DMA_LINKS link
+ * descriptors that ping-pong from one period to the next.  For example, if
+ * there are six periods and two link descriptors, this is how they look
+ * before playback starts:
+ *
+ *                The last link descriptor
+ *   ____________  points back to the first
+ *  |           |
+ *  V           |
+ *  ___    ___   |
+ * |   |->|   |->|
+ * |___|  |___|
+ *   |      |
+ *   |      |
+ *   V      V
+ *  _________________________________________
+ * |      |      |      |      |      |      |  The DMA buffer is
+ * |      |      |      |      |      |      |    divided into 6 parts
+ * |______|______|______|______|______|______|
+ *
+ * and here's how they look after the first period is finished playing:
+ *
+ *   ____________
+ *  |           |
+ *  V           |
+ *  ___    ___   |
+ * |   |->|   |->|
+ * |___|  |___|
+ *   |      |
+ *   |______________
+ *          |       |
+ *          V       V
+ *  _________________________________________
+ * |      |      |      |      |      |      |
+ * |      |      |      |      |      |      |
+ * |______|______|______|______|______|______|
+ *
+ * The first link descriptor now points to the third period.  The DMA
+ * controller is currently playing the second period.  When it finishes, it
+ * will jump back to the first descriptor and play the third period.
+ *
+ * There are four reasons we do this:
+ *
+ * 1. The only way to get the DMA controller to automatically restart the
+ *    transfer when it gets to the end of the buffer is to use chaining
+ *    mode.  Basic direct mode doesn't offer that feature.
+ * 2. We need to receive an interrupt at the end of every period.  The DMA
+ *    controller can generate an interrupt at the end of every link transfer
+ *    (aka segment).  Making each period into a DMA segment will give us the
+ *    interrupts we need.
+ * 3. By creating only two link descriptors, regardless of the number of
+ *    periods, we do not need to reallocate the link descriptors if the
+ *    number of periods changes.
+ * 4. All of the audio data is still stored in a single, contiguous DMA
+ *    buffer, which is what ALSA expects.  We're just dividing it into
+ *    contiguous parts, and creating a link descriptor for each one.
+ *
+ * Note that due to a quirk of the SSI's STX register, the target address
+ * for the DMA operations depends on the sample size.  So we don't program
+ * the dest_addr (for playback -- source_addr for capture) fields in the
+ * link descriptors here.  We do that in fsl_dma_prepare()
+ */
+static int fsl_dma_hw_params(struct snd_pcm_substream *substream,
+       struct snd_pcm_hw_params *hw_params)
+{
+       struct snd_pcm_runtime *runtime = substream->runtime;
+       struct fsl_dma_private *dma_private = runtime->private_data;
+       struct ccsr_dma_channel __iomem *dma_channel = dma_private->dma_channel;
+
+       dma_addr_t temp_addr;   /* Pointer to next period */
+       u64 temp_link;          /* Pointer to next link descriptor */
+       u32 mr;                 /* Temporary variable for MR register */
+
+       unsigned int i;
+
+       /* Get all the parameters we need */
+       size_t buffer_size = params_buffer_bytes(hw_params);
+       size_t period_size = params_period_bytes(hw_params);
+
+       /* Initialize our DMA tracking variables */
+       dma_private->period_size = period_size;
+       dma_private->num_periods = params_periods(hw_params);
+       dma_private->dma_buf_end = dma_private->dma_buf_phys + buffer_size;
+       dma_private->dma_buf_next = dma_private->dma_buf_phys +
+               (NUM_DMA_LINKS * period_size);
+       if (dma_private->dma_buf_next >= dma_private->dma_buf_end)
+               dma_private->dma_buf_next = dma_private->dma_buf_phys;
+
+       /*
+        * Initialize each link descriptor.
+        *
+        * The actual address in STX0 (destination for playback, source for
+        * capture) is based on the sample size, but we don't know the sample
+        * size in this function, so we'll have to adjust that later.  See
+        * comments in fsl_dma_prepare().
+        *
+        * The DMA controller does not have a cache, so the CPU does not
+        * need to tell it to flush its cache.  However, the DMA
+        * controller does need to tell the CPU to flush its cache.
+        * That's what the SNOOP bit does.
+        *
+        * Also, even though the DMA controller supports 36-bit addressing, for
+        * simplicity we currently support only 32-bit addresses for the audio
+        * buffer itself.
+        */
+       temp_addr = substream->dma_buffer.addr;
+       temp_link = dma_private->ld_buf_phys +
+               sizeof(struct fsl_dma_link_descriptor);
+
+       for (i = 0; i < NUM_DMA_LINKS; i++) {
+               struct fsl_dma_link_descriptor *link = &dma_private->link[i];
+
+               link->count = cpu_to_be32(period_size);
+               link->source_attr = cpu_to_be32(CCSR_DMA_ATR_SNOOP);
+               link->dest_attr = cpu_to_be32(CCSR_DMA_ATR_SNOOP);
+               link->next = cpu_to_be64(temp_link);
+
+               if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
+                       link->source_addr = cpu_to_be32(temp_addr);
+               else
+                       link->dest_addr = cpu_to_be32(temp_addr);
+
+               temp_addr += period_size;
+               temp_link += sizeof(struct fsl_dma_link_descriptor);
+       }
+       /* The last link descriptor points to the first */
+       dma_private->link[i - 1].next = cpu_to_be64(dma_private->ld_buf_phys);
+
+       /* Tell the DMA controller where the first link descriptor is */
+       out_be32(&dma_channel->clndar,
+               CCSR_DMA_CLNDAR_ADDR(dma_private->ld_buf_phys));
+       out_be32(&dma_channel->eclndar,
+               CCSR_DMA_ECLNDAR_ADDR(dma_private->ld_buf_phys));
+
+       /* The manual says the BCR must be clear before enabling EMP */
+       out_be32(&dma_channel->bcr, 0);
+
+       /*
+        * Program the mode register for interrupts, external master control,
+        * and source/destination hold.  Also clear the Channel Abort bit.
+        */
+       mr = in_be32(&dma_channel->mr) &
+               ~(CCSR_DMA_MR_CA | CCSR_DMA_MR_DAHE | CCSR_DMA_MR_SAHE);
+
+       /*
+        * We want External Master Start and External Master Pause enabled,
+        * because the SSI is controlling the DMA controller.  We want the DMA
+        * controller to be set up in advance, and then we signal only the SSI
+        * to start transfering.
+        *
+        * We want End-Of-Segment Interrupts enabled, because this will generate
+        * an interrupt at the end of each segment (each link descriptor
+        * represents one segment).  Each DMA segment is the same thing as an
+        * ALSA period, so this is how we get an interrupt at the end of every
+        * period.
+        *
+        * We want Error Interrupt enabled, so that we can get an error if
+        * the DMA controller is mis-programmed somehow.
+        */
+       mr |= CCSR_DMA_MR_EOSIE | CCSR_DMA_MR_EIE | CCSR_DMA_MR_EMP_EN |
+               CCSR_DMA_MR_EMS_EN;
+
+       /* For playback, we want the destination address to be held.  For
+          capture, set the source address to be held. */
+       mr |= (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) ?
+               CCSR_DMA_MR_DAHE : CCSR_DMA_MR_SAHE;
+
+       out_be32(&dma_channel->mr, mr);
+
+       return 0;
+}
+
+/**
+ * fsl_dma_prepare - prepare the DMA registers for playback.
+ *
+ * This function is called after the specifics of the audio data are known,
+ * i.e. snd_pcm_runtime is initialized.
+ *
+ * In this function, we finish programming the registers of the DMA
+ * controller that are dependent on the sample size.
+ *
+ * One of the drawbacks with big-endian is that when copying integers of
+ * different sizes to a fixed-sized register, the address to which the
+ * integer must be copied is dependent on the size of the integer.
+ *
+ * For example, if P is the address of a 32-bit register, and X is a 32-bit
+ * integer, then X should be copied to address P.  However, if X is a 16-bit
+ * integer, then it should be copied to P+2.  If X is an 8-bit register,
+ * then it should be copied to P+3.
+ *
+ * So for playback of 8-bit samples, the DMA controller must transfer single
+ * bytes from the DMA buffer to the last byte of the STX0 register, i.e.
+ * offset by 3 bytes. For 16-bit samples, the offset is two bytes.
+ *
+ * For 24-bit samples, the offset is 1 byte.  However, the DMA controller
+ * does not support 3-byte copies (the DAHTS register supports only 1, 2, 4,
+ * and 8 bytes at a time).  So we do not support packed 24-bit samples.
+ * 24-bit data must be padded to 32 bits.
+ */
+static int fsl_dma_prepare(struct snd_pcm_substream *substream)
+{
+       struct snd_pcm_runtime *runtime = substream->runtime;
+       struct fsl_dma_private *dma_private = runtime->private_data;
+       struct ccsr_dma_channel __iomem *dma_channel = dma_private->dma_channel;
+       u32 mr;
+       unsigned int i;
+       dma_addr_t ssi_sxx_phys;        /* Bus address of SSI STX register */
+       unsigned int frame_size;        /* Number of bytes per frame */
+
+       ssi_sxx_phys = dma_private->ssi_sxx_phys;
+
+       mr = in_be32(&dma_channel->mr) & ~(CCSR_DMA_MR_BWC_MASK |
+                 CCSR_DMA_MR_SAHTS_MASK | CCSR_DMA_MR_DAHTS_MASK);
+
+       switch (runtime->sample_bits) {
+       case 8:
+               mr |= CCSR_DMA_MR_DAHTS_1 | CCSR_DMA_MR_SAHTS_1;
+               ssi_sxx_phys += 3;
+               break;
+       case 16:
+               mr |= CCSR_DMA_MR_DAHTS_2 | CCSR_DMA_MR_SAHTS_2;
+               ssi_sxx_phys += 2;
+               break;
+       case 32:
+               mr |= CCSR_DMA_MR_DAHTS_4 | CCSR_DMA_MR_SAHTS_4;
+               break;
+       default:
+               dev_err(substream->pcm->card->dev,
+                       "unsupported sample size %u\n", runtime->sample_bits);
+               return -EINVAL;
+       }
+
+       frame_size = runtime->frame_bits / 8;
+       /*
+        * BWC should always be a multiple of the frame size.  BWC determines
+        * how many bytes are sent/received before the DMA controller checks the
+        * SSI to see if it needs to stop.  For playback, the transmit FIFO can
+        * hold three frames, so we want to send two frames at a time. For
+        * capture, the receive FIFO is triggered when it contains one frame, so
+        * we want to receive one frame at a time.
+        */
+
+       if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
+               mr |= CCSR_DMA_MR_BWC(2 * frame_size);
+       else
+               mr |= CCSR_DMA_MR_BWC(frame_size);
+
+       out_be32(&dma_channel->mr, mr);
+
+       /*
+        * Program the address of the DMA transfer to/from the SSI.
+        */
+       for (i = 0; i < NUM_DMA_LINKS; i++) {
+               struct fsl_dma_link_descriptor *link = &dma_private->link[i];
+
+               if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
+                       link->dest_addr = cpu_to_be32(ssi_sxx_phys);
+               else
+                       link->source_addr = cpu_to_be32(ssi_sxx_phys);
+       }
+
+       return 0;
+}
+
+/**
+ * fsl_dma_pointer: determine the current position of the DMA transfer
+ *
+ * This function is called by ALSA when ALSA wants to know where in the
+ * stream buffer the hardware currently is.
+ *
+ * For playback, the SAR register contains the physical address of the most
+ * recent DMA transfer.  For capture, the value is in the DAR register.
+ *
+ * The base address of the buffer is stored in the source_addr field of the
+ * first link descriptor.
+ */
+static snd_pcm_uframes_t fsl_dma_pointer(struct snd_pcm_substream *substream)
+{
+       struct snd_pcm_runtime *runtime = substream->runtime;
+       struct fsl_dma_private *dma_private = runtime->private_data;
+       struct ccsr_dma_channel __iomem *dma_channel = dma_private->dma_channel;
+       dma_addr_t position;
+       snd_pcm_uframes_t frames;
+
+       if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
+               position = in_be32(&dma_channel->sar);
+       else
+               position = in_be32(&dma_channel->dar);
+
+       frames = bytes_to_frames(runtime, position - dma_private->dma_buf_phys);
+
+       /*
+        * If the current address is just past the end of the buffer, wrap it
+        * around.
+        */
+       if (frames == runtime->buffer_size)
+               frames = 0;
+
+       return frames;
+}
+
+/**
+ * fsl_dma_hw_free: release resources allocated in fsl_dma_hw_params()
+ *
+ * Release the resources allocated in fsl_dma_hw_params() and de-program the
+ * registers.
+ *
+ * This function can be called multiple times.
+ */
+static int fsl_dma_hw_free(struct snd_pcm_substream *substream)
+{
+       struct snd_pcm_runtime *runtime = substream->runtime;
+       struct fsl_dma_private *dma_private = runtime->private_data;
+
+       if (dma_private) {
+               struct ccsr_dma_channel __iomem *dma_channel;
+
+               dma_channel = dma_private->dma_channel;
+
+               /* Stop the DMA */
+               out_be32(&dma_channel->mr, CCSR_DMA_MR_CA);
+               out_be32(&dma_channel->mr, 0);
+
+               /* Reset all the other registers */
+               out_be32(&dma_channel->sr, -1);
+               out_be32(&dma_channel->clndar, 0);
+               out_be32(&dma_channel->eclndar, 0);
+               out_be32(&dma_channel->satr, 0);
+               out_be32(&dma_channel->sar, 0);
+               out_be32(&dma_channel->datr, 0);
+               out_be32(&dma_channel->dar, 0);
+               out_be32(&dma_channel->bcr, 0);
+               out_be32(&dma_channel->nlndar, 0);
+               out_be32(&dma_channel->enlndar, 0);
+       }
+
+       return 0;
+}
+
+/**
+ * fsl_dma_close: close the stream.
+ */
+static int fsl_dma_close(struct snd_pcm_substream *substream)
+{
+       struct snd_pcm_runtime *runtime = substream->runtime;
+       struct fsl_dma_private *dma_private = runtime->private_data;
+       int dir = substream->stream == SNDRV_PCM_STREAM_PLAYBACK ? 0 : 1;
+
+       if (dma_private) {
+               if (dma_private->irq)
+                       free_irq(dma_private->irq, dma_private);
+
+               if (dma_private->ld_buf_phys) {
+                       dma_unmap_single(substream->pcm->dev,
+                               dma_private->ld_buf_phys,
+                               sizeof(dma_private->link), DMA_TO_DEVICE);
+               }
+
+               /* Deallocate the fsl_dma_private structure */
+               dma_free_coherent(substream->pcm->dev,
+                       sizeof(struct fsl_dma_private),
+                       dma_private, dma_private->ld_buf_phys);
+               substream->runtime->private_data = NULL;
+       }
+
+       dma_global_data.assigned[dir] = 0;
+
+       return 0;
+}
+
+/*
+ * Remove this PCM driver.
+ */
+static void fsl_dma_free_dma_buffers(struct snd_pcm *pcm)
+{
+       struct snd_pcm_substream *substream;
+       unsigned int i;
+
+       for (i = 0; i < ARRAY_SIZE(pcm->streams); i++) {
+               substream = pcm->streams[i].substream;
+               if (substream) {
+                       snd_dma_free_pages(&substream->dma_buffer);
+                       substream->dma_buffer.area = NULL;
+                       substream->dma_buffer.addr = 0;
+               }
+       }
+}
+
+static struct snd_pcm_ops fsl_dma_ops = {
+       .open           = fsl_dma_open,
+       .close          = fsl_dma_close,
+       .ioctl          = snd_pcm_lib_ioctl,
+       .hw_params      = fsl_dma_hw_params,
+       .hw_free        = fsl_dma_hw_free,
+       .prepare        = fsl_dma_prepare,
+       .pointer        = fsl_dma_pointer,
+};
+
+struct snd_soc_platform fsl_soc_platform = {
+       .name           = "fsl-dma",
+       .pcm_ops        = &fsl_dma_ops,
+       .pcm_new        = fsl_dma_new,
+       .pcm_free       = fsl_dma_free_dma_buffers,
+};
+EXPORT_SYMBOL_GPL(fsl_soc_platform);
+
+/**
+ * fsl_dma_configure: store the DMA parameters from the fabric driver.
+ *
+ * This function is called by the ASoC fabric driver to give us the DMA and
+ * SSI channel information.
+ *
+ * Unfortunately, ASoC V1 does make it possible to determine the DMA/SSI
+ * data when a substream is created, so for now we need to store this data
+ * into a global variable.  This means that we can only support one DMA
+ * controller, and hence only one SSI.
+ */
+int fsl_dma_configure(struct fsl_dma_info *dma_info)
+{
+       static int initialized;
+
+       /* We only support one DMA controller for now */
+       if (initialized)
+               return 0;
+
+       dma_global_data.ssi_stx_phys = dma_info->ssi_stx_phys;
+       dma_global_data.ssi_srx_phys = dma_info->ssi_srx_phys;
+       dma_global_data.dma_channel[0] = dma_info->dma_channel[0];
+       dma_global_data.dma_channel[1] = dma_info->dma_channel[1];
+       dma_global_data.irq[0] = dma_info->dma_irq[0];
+       dma_global_data.irq[1] = dma_info->dma_irq[1];
+       dma_global_data.assigned[0] = 0;
+       dma_global_data.assigned[1] = 0;
+
+       initialized = 1;
+       return 1;
+}
+EXPORT_SYMBOL_GPL(fsl_dma_configure);
+
+MODULE_AUTHOR("Timur Tabi <timur@freescale.com>");
+MODULE_DESCRIPTION("Freescale Elo DMA ASoC PCM module");
+MODULE_LICENSE("GPL");
diff --git a/sound/soc/fsl/fsl_dma.h b/sound/soc/fsl/fsl_dma.h
new file mode 100644 (file)
index 0000000..430a6ce
--- /dev/null
@@ -0,0 +1,149 @@
+/*
+ * mpc8610-pcm.h - ALSA PCM interface for the Freescale MPC8610 SoC
+ *
+ * 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 _MPC8610_PCM_H
+#define _MPC8610_PCM_H
+
+struct ccsr_dma {
+       u8 res0[0x100];
+       struct ccsr_dma_channel {
+               __be32 mr;      /* Mode register */
+               __be32 sr;      /* Status register */
+               __be32 eclndar; /* Current link descriptor extended addr reg */
+               __be32 clndar;  /* Current link descriptor address register */
+               __be32 satr;    /* Source attributes register */
+               __be32 sar;     /* Source address register */
+               __be32 datr;    /* Destination attributes register */
+               __be32 dar;     /* Destination address register */
+               __be32 bcr;     /* Byte count register */
+               __be32 enlndar; /* Next link descriptor extended address reg */
+               __be32 nlndar;  /* Next link descriptor address register */
+               u8 res1[4];
+               __be32 eclsdar; /* Current list descriptor extended addr reg */
+               __be32 clsdar;  /* Current list descriptor address register */
+               __be32 enlsdar; /* Next list descriptor extended address reg */
+               __be32 nlsdar;  /* Next list descriptor address register */
+               __be32 ssr;     /* Source stride register */
+               __be32 dsr;     /* Destination stride register */
+               u8 res2[0x38];
+       } channel[4];
+       __be32 dgsr;
+};
+
+#define CCSR_DMA_MR_BWC_DISABLED       0x0F000000
+#define CCSR_DMA_MR_BWC_SHIFT          24
+#define CCSR_DMA_MR_BWC_MASK           0x0F000000
+#define CCSR_DMA_MR_BWC(x) \
+       ((ilog2(x) << CCSR_DMA_MR_BWC_SHIFT) & CCSR_DMA_MR_BWC_MASK)
+#define CCSR_DMA_MR_EMP_EN             0x00200000
+#define CCSR_DMA_MR_EMS_EN             0x00040000
+#define CCSR_DMA_MR_DAHTS_MASK         0x00030000
+#define CCSR_DMA_MR_DAHTS_1            0x00000000
+#define CCSR_DMA_MR_DAHTS_2            0x00010000
+#define CCSR_DMA_MR_DAHTS_4            0x00020000
+#define CCSR_DMA_MR_DAHTS_8            0x00030000
+#define CCSR_DMA_MR_SAHTS_MASK         0x0000C000
+#define CCSR_DMA_MR_SAHTS_1            0x00000000
+#define CCSR_DMA_MR_SAHTS_2            0x00004000
+#define CCSR_DMA_MR_SAHTS_4            0x00008000
+#define CCSR_DMA_MR_SAHTS_8            0x0000C000
+#define CCSR_DMA_MR_DAHE               0x00002000
+#define CCSR_DMA_MR_SAHE               0x00001000
+#define CCSR_DMA_MR_SRW                0x00000400
+#define CCSR_DMA_MR_EOSIE              0x00000200
+#define CCSR_DMA_MR_EOLNIE             0x00000100
+#define CCSR_DMA_MR_EOLSIE             0x00000080
+#define CCSR_DMA_MR_EIE                0x00000040
+#define CCSR_DMA_MR_XFE                0x00000020
+#define CCSR_DMA_MR_CDSM_SWSM          0x00000010
+#define CCSR_DMA_MR_CA                 0x00000008
+#define CCSR_DMA_MR_CTM                0x00000004
+#define CCSR_DMA_MR_CC                 0x00000002
+#define CCSR_DMA_MR_CS                 0x00000001
+
+#define CCSR_DMA_SR_TE                 0x00000080
+#define CCSR_DMA_SR_CH                 0x00000020
+#define CCSR_DMA_SR_PE                 0x00000010
+#define CCSR_DMA_SR_EOLNI              0x00000008
+#define CCSR_DMA_SR_CB                 0x00000004
+#define CCSR_DMA_SR_EOSI               0x00000002
+#define CCSR_DMA_SR_EOLSI              0x00000001
+
+/* ECLNDAR takes bits 32-36 of the CLNDAR register */
+static inline u32 CCSR_DMA_ECLNDAR_ADDR(u64 x)
+{
+       return (x >> 32) & 0xf;
+}
+
+#define CCSR_DMA_CLNDAR_ADDR(x) ((x) & 0xFFFFFFFE)
+#define CCSR_DMA_CLNDAR_EOSIE          0x00000008
+
+/* SATR and DATR, combined */
+#define CCSR_DMA_ATR_PBATMU            0x20000000
+#define CCSR_DMA_ATR_TFLOWLVL_0        0x00000000
+#define CCSR_DMA_ATR_TFLOWLVL_1        0x06000000
+#define CCSR_DMA_ATR_TFLOWLVL_2        0x08000000
+#define CCSR_DMA_ATR_TFLOWLVL_3        0x0C000000
+#define CCSR_DMA_ATR_PCIORDER          0x02000000
+#define CCSR_DMA_ATR_SME               0x01000000
+#define CCSR_DMA_ATR_NOSNOOP           0x00040000
+#define CCSR_DMA_ATR_SNOOP             0x00050000
+#define CCSR_DMA_ATR_ESAD_MASK         0x0000000F
+
+/**
+ *  List Descriptor for extended chaining mode DMA operations.
+ *
+ *  The CLSDAR register points to the first (in a linked-list) List
+ *  Descriptor.  Each object must be aligned on a 32-byte boundary. Each
+ *  list descriptor points to a linked-list of link Descriptors.
+ */
+struct fsl_dma_list_descriptor {
+       __be64 next;            /* Address of next list descriptor */
+       __be64 first_link;      /* Address of first link descriptor */
+       __be32 source;          /* Source stride */
+       __be32 dest;            /* Destination stride */
+       u8 res[8];              /* Reserved */
+} __attribute__ ((aligned(32), packed));
+
+/**
+ *  Link Descriptor for basic and extended chaining mode DMA operations.
+ *
+ *  A Link Descriptor points to a single DMA buffer.  Each link descriptor
+ *  must be aligned on a 32-byte boundary.
+ */
+struct fsl_dma_link_descriptor {
+       __be32 source_attr;     /* Programmed into SATR register */
+       __be32 source_addr;     /* Programmed into SAR register */
+       __be32 dest_attr;       /* Programmed into DATR register */
+       __be32 dest_addr;       /* Programmed into DAR register */
+       __be64 next;    /* Address of next link descriptor */
+       __be32 count;   /* Byte count */
+       u8 res[4];      /* Reserved */
+} __attribute__ ((aligned(32), packed));
+
+/* DMA information needed to create a snd_soc_cpu_dai object
+ *
+ * ssi_stx_phys: bus address of SSI STX register to use
+ * ssi_srx_phys: bus address of SSI SRX register to use
+ * dma[0]: points to the DMA channel to use for playback
+ * dma[1]: points to the DMA channel to use for capture
+ * dma_irq[0]: IRQ of the DMA channel to use for playback
+ * dma_irq[1]: IRQ of the DMA channel to use for capture
+ */
+struct fsl_dma_info {
+       dma_addr_t ssi_stx_phys;
+       dma_addr_t ssi_srx_phys;
+       struct ccsr_dma_channel __iomem *dma_channel[2];
+       unsigned int dma_irq[2];
+};
+
+extern struct snd_soc_platform fsl_soc_platform;
+
+int fsl_dma_configure(struct fsl_dma_info *dma_info);
+
+#endif
diff --git a/sound/soc/fsl/fsl_ssi.c b/sound/soc/fsl/fsl_ssi.c
new file mode 100644 (file)
index 0000000..145ad13
--- /dev/null
@@ -0,0 +1,644 @@
+/*
+ * Freescale SSI ALSA SoC Digital Audio Interface (DAI) driver
+ *
+ * Author: Timur Tabi <timur@freescale.com>
+ *
+ * Copyright 2007-2008 Freescale Semiconductor, Inc.  This file is licensed
+ * under the terms of the GNU General Public License version 2.  This
+ * program is licensed "as is" without any warranty of any kind, whether
+ * express or implied.
+ */
+
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/interrupt.h>
+#include <linux/device.h>
+#include <linux/delay.h>
+
+#include <sound/driver.h>
+#include <sound/core.h>
+#include <sound/pcm.h>
+#include <sound/pcm_params.h>
+#include <sound/initval.h>
+#include <sound/soc.h>
+
+#include <asm/immap_86xx.h>
+
+#include "fsl_ssi.h"
+
+/**
+ * FSLSSI_I2S_RATES: sample rates supported by the I2S
+ *
+ * This driver currently only supports the SSI running in I2S slave mode,
+ * which means the codec determines the sample rate.  Therefore, we tell
+ * ALSA that we support all rates and let the codec driver decide what rates
+ * are really supported.
+ */
+#define FSLSSI_I2S_RATES (SNDRV_PCM_RATE_5512 | SNDRV_PCM_RATE_8000_192000 | \
+                         SNDRV_PCM_RATE_CONTINUOUS)
+
+/**
+ * FSLSSI_I2S_FORMATS: audio formats supported by the SSI
+ *
+ * This driver currently only supports the SSI running in I2S slave mode.
+ *
+ * The SSI has a limitation in that the samples must be in the same byte
+ * order as the host CPU.  This is because when multiple bytes are written
+ * to the STX register, the bytes and bits must be written in the same
+ * order.  The STX is a shift register, so all the bits need to be aligned
+ * (bit-endianness must match byte-endianness).  Processors typically write
+ * the bits within a byte in the same order that the bytes of a word are
+ * written in.  So if the host CPU is big-endian, then only big-endian
+ * samples will be written to STX properly.
+ */
+#ifdef __BIG_ENDIAN
+#define FSLSSI_I2S_FORMATS (SNDRV_PCM_FMTBIT_S8 | SNDRV_PCM_FMTBIT_S16_BE | \
+        SNDRV_PCM_FMTBIT_S18_3BE | SNDRV_PCM_FMTBIT_S20_3BE | \
+        SNDRV_PCM_FMTBIT_S24_3BE | SNDRV_PCM_FMTBIT_S24_BE)
+#else
+#define FSLSSI_I2S_FORMATS (SNDRV_PCM_FMTBIT_S8 | SNDRV_PCM_FMTBIT_S16_LE | \
+        SNDRV_PCM_FMTBIT_S18_3LE | SNDRV_PCM_FMTBIT_S20_3LE | \
+        SNDRV_PCM_FMTBIT_S24_3LE | SNDRV_PCM_FMTBIT_S24_LE)
+#endif
+
+/**
+ * fsl_ssi_private: per-SSI private data
+ *
+ * @name: short name for this device ("SSI0", "SSI1", etc)
+ * @ssi: pointer to the SSI's registers
+ * @ssi_phys: physical address of the SSI registers
+ * @irq: IRQ of this SSI
+ * @dev: struct device pointer
+ * @playback: the number of playback streams opened
+ * @capture: the number of capture streams opened
+ * @cpu_dai: the CPU DAI for this device
+ * @dev_attr: the sysfs device attribute structure
+ * @stats: SSI statistics
+ */
+struct fsl_ssi_private {
+       char name[8];
+       struct ccsr_ssi __iomem *ssi;
+       dma_addr_t ssi_phys;
+       unsigned int irq;
+       struct device *dev;
+       unsigned int playback;
+       unsigned int capture;
+       struct snd_soc_cpu_dai cpu_dai;
+       struct device_attribute dev_attr;
+
+       struct {
+               unsigned int rfrc;
+               unsigned int tfrc;
+               unsigned int cmdau;
+               unsigned int cmddu;
+               unsigned int rxt;
+               unsigned int rdr1;
+               unsigned int rdr0;
+               unsigned int tde1;
+               unsigned int tde0;
+               unsigned int roe1;
+               unsigned int roe0;
+               unsigned int tue1;
+               unsigned int tue0;
+               unsigned int tfs;
+               unsigned int rfs;
+               unsigned int tls;
+               unsigned int rls;
+               unsigned int rff1;
+               unsigned int rff0;
+               unsigned int tfe1;
+               unsigned int tfe0;
+       } stats;
+};
+
+/**
+ * fsl_ssi_isr: SSI interrupt handler
+ *
+ * Although it's possible to use the interrupt handler to send and receive
+ * data to/from the SSI, we use the DMA instead.  Programming is more
+ * complicated, but the performance is much better.
+ *
+ * This interrupt handler is used only to gather statistics.
+ *
+ * @irq: IRQ of the SSI device
+ * @dev_id: pointer to the ssi_private structure for this SSI device
+ */
+static irqreturn_t fsl_ssi_isr(int irq, void *dev_id)
+{
+       struct fsl_ssi_private *ssi_private = dev_id;
+       struct ccsr_ssi __iomem *ssi = ssi_private->ssi;
+       irqreturn_t ret = IRQ_NONE;
+       __be32 sisr;
+       __be32 sisr2 = 0;
+
+       /* We got an interrupt, so read the status register to see what we
+          were interrupted for.  We mask it with the Interrupt Enable register
+          so that we only check for events that we're interested in.
+        */
+       sisr = in_be32(&ssi->sisr) & in_be32(&ssi->sier);
+
+       if (sisr & CCSR_SSI_SISR_RFRC) {
+               ssi_private->stats.rfrc++;
+               sisr2 |= CCSR_SSI_SISR_RFRC;
+               ret = IRQ_HANDLED;
+       }
+
+       if (sisr & CCSR_SSI_SISR_TFRC) {
+               ssi_private->stats.tfrc++;
+               sisr2 |= CCSR_SSI_SISR_TFRC;
+               ret = IRQ_HANDLED;
+       }
+
+       if (sisr & CCSR_SSI_SISR_CMDAU) {
+               ssi_private->stats.cmdau++;
+               ret = IRQ_HANDLED;
+       }
+
+       if (sisr & CCSR_SSI_SISR_CMDDU) {
+               ssi_private->stats.cmddu++;
+               ret = IRQ_HANDLED;
+       }
+
+       if (sisr & CCSR_SSI_SISR_RXT) {
+               ssi_private->stats.rxt++;
+               ret = IRQ_HANDLED;
+       }
+
+       if (sisr & CCSR_SSI_SISR_RDR1) {
+               ssi_private->stats.rdr1++;
+               ret = IRQ_HANDLED;
+       }
+
+       if (sisr & CCSR_SSI_SISR_RDR0) {
+               ssi_private->stats.rdr0++;
+               ret = IRQ_HANDLED;
+       }
+
+       if (sisr & CCSR_SSI_SISR_TDE1) {
+               ssi_private->stats.tde1++;
+               ret = IRQ_HANDLED;
+       }
+
+       if (sisr & CCSR_SSI_SISR_TDE0) {
+               ssi_private->stats.tde0++;
+               ret = IRQ_HANDLED;
+       }
+
+       if (sisr & CCSR_SSI_SISR_ROE1) {
+               ssi_private->stats.roe1++;
+               sisr2 |= CCSR_SSI_SISR_ROE1;
+               ret = IRQ_HANDLED;
+       }
+
+       if (sisr & CCSR_SSI_SISR_ROE0) {
+               ssi_private->stats.roe0++;
+               sisr2 |= CCSR_SSI_SISR_ROE0;
+               ret = IRQ_HANDLED;
+       }
+
+       if (sisr & CCSR_SSI_SISR_TUE1) {
+               ssi_private->stats.tue1++;
+               sisr2 |= CCSR_SSI_SISR_TUE1;
+               ret = IRQ_HANDLED;
+       }
+
+       if (sisr & CCSR_SSI_SISR_TUE0) {
+               ssi_private->stats.tue0++;
+               sisr2 |= CCSR_SSI_SISR_TUE0;
+               ret = IRQ_HANDLED;
+       }
+
+       if (sisr & CCSR_SSI_SISR_TFS) {
+               ssi_private->stats.tfs++;
+               ret = IRQ_HANDLED;
+       }
+
+       if (sisr & CCSR_SSI_SISR_RFS) {
+               ssi_private->stats.rfs++;
+               ret = IRQ_HANDLED;
+       }
+
+       if (sisr & CCSR_SSI_SISR_TLS) {
+               ssi_private->stats.tls++;
+               ret = IRQ_HANDLED;
+       }
+
+       if (sisr & CCSR_SSI_SISR_RLS) {
+               ssi_private->stats.rls++;
+               ret = IRQ_HANDLED;
+       }
+
+       if (sisr & CCSR_SSI_SISR_RFF1) {
+               ssi_private->stats.rff1++;
+               ret = IRQ_HANDLED;
+       }
+
+       if (sisr & CCSR_SSI_SISR_RFF0) {
+               ssi_private->stats.rff0++;
+               ret = IRQ_HANDLED;
+       }
+
+       if (sisr & CCSR_SSI_SISR_TFE1) {
+               ssi_private->stats.tfe1++;
+               ret = IRQ_HANDLED;
+       }
+
+       if (sisr & CCSR_SSI_SISR_TFE0) {
+               ssi_private->stats.tfe0++;
+               ret = IRQ_HANDLED;
+       }
+
+       /* Clear the bits that we set */
+       if (sisr2)
+               out_be32(&ssi->sisr, sisr2);
+
+       return ret;
+}
+
+/**
+ * fsl_ssi_startup: create a new substream
+ *
+ * This is the first function called when a stream is opened.
+ *
+ * If this is the first stream open, then grab the IRQ and program most of
+ * the SSI registers.
+ */
+static int fsl_ssi_startup(struct snd_pcm_substream *substream)
+{
+       struct snd_soc_pcm_runtime *rtd = substream->private_data;
+       struct fsl_ssi_private *ssi_private = rtd->dai->cpu_dai->private_data;
+
+       /*
+        * If this is the first stream opened, then request the IRQ
+        * and initialize the SSI registers.
+        */
+       if (!ssi_private->playback && !ssi_private->capture) {
+               struct ccsr_ssi __iomem *ssi = ssi_private->ssi;
+               int ret;
+
+               ret = request_irq(ssi_private->irq, fsl_ssi_isr, 0,
+                                 ssi_private->name, ssi_private);
+               if (ret < 0) {
+                       dev_err(substream->pcm->card->dev,
+                               "could not claim irq %u\n", ssi_private->irq);
+                       return ret;
+               }
+
+               /*
+                * Section 16.5 of the MPC8610 reference manual says that the
+                * SSI needs to be disabled before updating the registers we set
+                * here.
+                */
+               clrbits32(&ssi->scr, CCSR_SSI_SCR_SSIEN);
+
+               /*
+                * Program the SSI into I2S Slave Non-Network Synchronous mode.
+                * Also enable the transmit and receive FIFO.
+                *
+                * FIXME: Little-endian samples require a different shift dir
+                */
+               clrsetbits_be32(&ssi->scr, CCSR_SSI_SCR_I2S_MODE_MASK,
+                       CCSR_SSI_SCR_TFR_CLK_DIS |
+                       CCSR_SSI_SCR_I2S_MODE_SLAVE | CCSR_SSI_SCR_SYN);
+
+               out_be32(&ssi->stcr,
+                        CCSR_SSI_STCR_TXBIT0 | CCSR_SSI_STCR_TFEN0 |
+                        CCSR_SSI_STCR_TFSI | CCSR_SSI_STCR_TEFS |
+                        CCSR_SSI_STCR_TSCKP);
+
+               out_be32(&ssi->srcr,
+                        CCSR_SSI_SRCR_RXBIT0 | CCSR_SSI_SRCR_RFEN0 |
+                        CCSR_SSI_SRCR_RFSI | CCSR_SSI_SRCR_REFS |
+                        CCSR_SSI_SRCR_RSCKP);
+
+               /*
+                * The DC and PM bits are only used if the SSI is the clock
+                * master.
+                */
+
+               /* 4. Enable the interrupts and DMA requests */
+               out_be32(&ssi->sier,
+                        CCSR_SSI_SIER_TFRC_EN | CCSR_SSI_SIER_TDMAE |
+                        CCSR_SSI_SIER_TIE | CCSR_SSI_SIER_TUE0_EN |
+                        CCSR_SSI_SIER_TUE1_EN | CCSR_SSI_SIER_RFRC_EN |
+                        CCSR_SSI_SIER_RDMAE | CCSR_SSI_SIER_RIE |
+                        CCSR_SSI_SIER_ROE0_EN | CCSR_SSI_SIER_ROE1_EN);
+
+               /*
+                * Set the watermark for transmit FIFI 0 and receive FIFO 0. We
+                * don't use FIFO 1.  Since the SSI only supports stereo, the
+                * watermark should never be an odd number.
+                */
+               out_be32(&ssi->sfcsr,
+                        CCSR_SSI_SFCSR_TFWM0(6) | CCSR_SSI_SFCSR_RFWM0(2));
+
+               /*
+                * We keep the SSI disabled because if we enable it, then the
+                * DMA controller will start.  It's not supposed to start until
+                * the SCR.TE (or SCR.RE) bit is set, but it does anyway.  The
+                * DMA controller will transfer one "BWC" of data (i.e. the
+                * amount of data that the MR.BWC bits are set to).  The reason
+                * this is bad is because at this point, the PCM driver has not
+                * finished initializing the DMA controller.
+                */
+       }
+
+       if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
+               ssi_private->playback++;
+
+       if (substream->stream == SNDRV_PCM_STREAM_CAPTURE)
+               ssi_private->capture++;
+
+       return 0;
+}
+
+/**
+ * fsl_ssi_prepare: prepare the SSI.
+ *
+ * Most of the SSI registers have been programmed in the startup function,
+ * but the word length must be programmed here.  Unfortunately, programming
+ * the SxCCR.WL bits requires the SSI to be temporarily disabled.  This can
+ * cause a problem with supporting simultaneous playback and capture.  If
+ * the SSI is already playing a stream, then that stream may be temporarily
+ * stopped when you start capture.
+ *
+ * Note: The SxCCR.DC and SxCCR.PM bits are only used if the SSI is the
+ * clock master.
+ */
+static int fsl_ssi_prepare(struct snd_pcm_substream *substream)
+{
+       struct snd_pcm_runtime *runtime = substream->runtime;
+       struct snd_soc_pcm_runtime *rtd = substream->private_data;
+       struct fsl_ssi_private *ssi_private = rtd->dai->cpu_dai->private_data;
+
+       struct ccsr_ssi __iomem *ssi = ssi_private->ssi;
+       u32 wl;
+
+       wl = CCSR_SSI_SxCCR_WL(snd_pcm_format_width(runtime->format));
+
+       clrbits32(&ssi->scr, CCSR_SSI_SCR_SSIEN);
+
+       if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
+               clrsetbits_be32(&ssi->stccr, CCSR_SSI_SxCCR_WL_MASK, wl);
+       else
+               clrsetbits_be32(&ssi->srccr, CCSR_SSI_SxCCR_WL_MASK, wl);
+
+       setbits32(&ssi->scr, CCSR_SSI_SCR_SSIEN);
+
+       return 0;
+}
+
+/**
+ * fsl_ssi_trigger: start and stop the DMA transfer.
+ *
+ * This function is called by ALSA to start, stop, pause, and resume the DMA
+ * transfer of data.
+ *
+ * The DMA channel is in external master start and pause mode, which
+ * means the SSI completely controls the flow of data.
+ */
+static int fsl_ssi_trigger(struct snd_pcm_substream *substream, int cmd)
+{
+       struct snd_soc_pcm_runtime *rtd = substream->private_data;
+       struct fsl_ssi_private *ssi_private = rtd->dai->cpu_dai->private_data;
+       struct ccsr_ssi __iomem *ssi = ssi_private->ssi;
+
+       switch (cmd) {
+       case SNDRV_PCM_TRIGGER_START:
+       case SNDRV_PCM_TRIGGER_RESUME:
+       case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
+               if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
+                       setbits32(&ssi->scr, CCSR_SSI_SCR_TE);
+               } else {
+                       setbits32(&ssi->scr, CCSR_SSI_SCR_RE);
+
+                       /*
+                        * I think we need this delay to allow time for the SSI
+                        * to put data into its FIFO.  Without it, ALSA starts
+                        * to complain about overruns.
+                        */
+                       msleep(1);
+               }
+               break;
+
+       case SNDRV_PCM_TRIGGER_STOP:
+       case SNDRV_PCM_TRIGGER_SUSPEND:
+       case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
+               if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
+                       clrbits32(&ssi->scr, CCSR_SSI_SCR_TE);
+               else
+                       clrbits32(&ssi->scr, CCSR_SSI_SCR_RE);
+               break;
+
+       default:
+               return -EINVAL;
+       }
+
+       return 0;
+}
+
+/**
+ * fsl_ssi_shutdown: shutdown the SSI
+ *
+ * Shutdown the SSI if there are no other substreams open.
+ */
+static void fsl_ssi_shutdown(struct snd_pcm_substream *substream)
+{
+       struct snd_soc_pcm_runtime *rtd = substream->private_data;
+       struct fsl_ssi_private *ssi_private = rtd->dai->cpu_dai->private_data;
+
+       if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
+               ssi_private->playback--;
+
+       if (substream->stream == SNDRV_PCM_STREAM_CAPTURE)
+               ssi_private->capture--;
+
+       /*
+        * If this is the last active substream, disable the SSI and release
+        * the IRQ.
+        */
+       if (!ssi_private->playback && !ssi_private->capture) {
+               struct ccsr_ssi __iomem *ssi = ssi_private->ssi;
+
+               clrbits32(&ssi->scr, CCSR_SSI_SCR_SSIEN);
+
+               free_irq(ssi_private->irq, ssi_private);
+       }
+}
+
+/**
+ * fsl_ssi_set_sysclk: set the clock frequency and direction
+ *
+ * This function is called by the machine driver to tell us what the clock
+ * frequency and direction are.
+ *
+ * Currently, we only support operating as a clock slave (SND_SOC_CLOCK_IN),
+ * and we don't care about the frequency.  Return an error if the direction
+ * is not SND_SOC_CLOCK_IN.
+ *
+ * @clk_id: reserved, should be zero
+ * @freq: the frequency of the given clock ID, currently ignored
+ * @dir: SND_SOC_CLOCK_IN (clock slave) or SND_SOC_CLOCK_OUT (clock master)
+ */
+static int fsl_ssi_set_sysclk(struct snd_soc_cpu_dai *cpu_dai,
+                             int clk_id, unsigned int freq, int dir)
+{
+
+       return (dir == SND_SOC_CLOCK_IN) ? 0 : -EINVAL;
+}
+
+/**
+ * fsl_ssi_set_fmt: set the serial format.
+ *
+ * This function is called by the machine driver to tell us what serial
+ * format to use.
+ *
+ * Currently, we only support I2S mode.  Return an error if the format is
+ * not SND_SOC_DAIFMT_I2S.
+ *
+ * @format: one of SND_SOC_DAIFMT_xxx
+ */
+static int fsl_ssi_set_fmt(struct snd_soc_cpu_dai *cpu_dai, unsigned int format)
+{
+       return (format == SND_SOC_DAIFMT_I2S) ? 0 : -EINVAL;
+}
+
+/**
+ * fsl_ssi_dai_template: template CPU DAI for the SSI
+ */
+static struct snd_soc_cpu_dai fsl_ssi_dai_template = {
+       .playback = {
+               /* The SSI does not support monaural audio. */
+               .channels_min = 2,
+               .channels_max = 2,
+               .rates = FSLSSI_I2S_RATES,
+               .formats = FSLSSI_I2S_FORMATS,
+       },
+       .capture = {
+               .channels_min = 2,
+               .channels_max = 2,
+               .rates = FSLSSI_I2S_RATES,
+               .formats = FSLSSI_I2S_FORMATS,
+       },
+       .ops = {
+               .startup = fsl_ssi_startup,
+               .prepare = fsl_ssi_prepare,
+               .shutdown = fsl_ssi_shutdown,
+               .trigger = fsl_ssi_trigger,
+       },
+       .dai_ops = {
+               .set_sysclk = fsl_ssi_set_sysclk,
+               .set_fmt = fsl_ssi_set_fmt,
+       },
+};
+
+/**
+ * fsl_sysfs_ssi_show: display SSI statistics
+ *
+ * Display the statistics for the current SSI device.
+ */
+static ssize_t fsl_sysfs_ssi_show(struct device *dev,
+       struct device_attribute *attr, char *buf)
+{
+       struct fsl_ssi_private *ssi_private =
+       container_of(attr, struct fsl_ssi_private, dev_attr);
+       ssize_t length;
+
+       length = sprintf(buf, "rfrc=%u", ssi_private->stats.rfrc);
+       length += sprintf(buf + length, "\ttfrc=%u", ssi_private->stats.tfrc);
+       length += sprintf(buf + length, "\tcmdau=%u", ssi_private->stats.cmdau);
+       length += sprintf(buf + length, "\tcmddu=%u", ssi_private->stats.cmddu);
+       length += sprintf(buf + length, "\trxt=%u", ssi_private->stats.rxt);
+       length += sprintf(buf + length, "\trdr1=%u", ssi_private->stats.rdr1);
+       length += sprintf(buf + length, "\trdr0=%u", ssi_private->stats.rdr0);
+       length += sprintf(buf + length, "\ttde1=%u", ssi_private->stats.tde1);
+       length += sprintf(buf + length, "\ttde0=%u", ssi_private->stats.tde0);
+       length += sprintf(buf + length, "\troe1=%u", ssi_private->stats.roe1);
+       length += sprintf(buf + length, "\troe0=%u", ssi_private->stats.roe0);
+       length += sprintf(buf + length, "\ttue1=%u", ssi_private->stats.tue1);
+       length += sprintf(buf + length, "\ttue0=%u", ssi_private->stats.tue0);
+       length += sprintf(buf + length, "\ttfs=%u", ssi_private->stats.tfs);
+       length += sprintf(buf + length, "\trfs=%u", ssi_private->stats.rfs);
+       length += sprintf(buf + length, "\ttls=%u", ssi_private->stats.tls);
+       length += sprintf(buf + length, "\trls=%u", ssi_private->stats.rls);
+       length += sprintf(buf + length, "\trff1=%u", ssi_private->stats.rff1);
+       length += sprintf(buf + length, "\trff0=%u", ssi_private->stats.rff0);
+       length += sprintf(buf + length, "\ttfe1=%u", ssi_private->stats.tfe1);
+       length += sprintf(buf + length, "\ttfe0=%u\n", ssi_private->stats.tfe0);
+
+       return length;
+}
+
+/**
+ * fsl_ssi_create_dai: create a snd_soc_cpu_dai structure
+ *
+ * This function is called by the machine driver to create a snd_soc_cpu_dai
+ * structure.  The function creates an ssi_private object, which contains
+ * the snd_soc_cpu_dai.  It also creates the sysfs statistics device.
+ */
+struct snd_soc_cpu_dai *fsl_ssi_create_dai(struct fsl_ssi_info *ssi_info)
+{
+       struct snd_soc_cpu_dai *fsl_ssi_dai;
+       struct fsl_ssi_private *ssi_private;
+       int ret = 0;
+       struct device_attribute *dev_attr;
+
+       ssi_private = kzalloc(sizeof(struct fsl_ssi_private), GFP_KERNEL);
+       if (!ssi_private) {
+               dev_err(ssi_info->dev, "could not allocate DAI object\n");
+               return NULL;
+       }
+       memcpy(&ssi_private->cpu_dai, &fsl_ssi_dai_template,
+              sizeof(struct snd_soc_cpu_dai));
+
+       fsl_ssi_dai = &ssi_private->cpu_dai;
+       dev_attr = &ssi_private->dev_attr;
+
+       sprintf(ssi_private->name, "ssi%u", (u8) ssi_info->id);
+       ssi_private->ssi = ssi_info->ssi;
+       ssi_private->ssi_phys = ssi_info->ssi_phys;
+       ssi_private->irq = ssi_info->irq;
+       ssi_private->dev = ssi_info->dev;
+
+       ssi_private->dev->driver_data = fsl_ssi_dai;
+
+       /* Initialize the the device_attribute structure */
+       dev_attr->attr.name = "ssi-stats";
+       dev_attr->attr.mode = S_IRUGO;
+       dev_attr->show = fsl_sysfs_ssi_show;
+
+       ret = device_create_file(ssi_private->dev, dev_attr);
+       if (ret) {
+               dev_err(ssi_info->dev, "could not create sysfs %s file\n",
+                       ssi_private->dev_attr.attr.name);
+               kfree(fsl_ssi_dai);
+               return NULL;
+       }
+
+       fsl_ssi_dai->private_data = ssi_private;
+       fsl_ssi_dai->name = ssi_private->name;
+       fsl_ssi_dai->id = ssi_info->id;
+
+       return fsl_ssi_dai;
+}
+EXPORT_SYMBOL_GPL(fsl_ssi_create_dai);
+
+/**
+ * fsl_ssi_destroy_dai: destroy the snd_soc_cpu_dai object
+ *
+ * This function undoes the operations of fsl_ssi_create_dai()
+ */
+void fsl_ssi_destroy_dai(struct snd_soc_cpu_dai *fsl_ssi_dai)
+{
+       struct fsl_ssi_private *ssi_private =
+       container_of(fsl_ssi_dai, struct fsl_ssi_private, cpu_dai);
+
+       device_remove_file(ssi_private->dev, &ssi_private->dev_attr);
+
+       kfree(ssi_private);
+}
+EXPORT_SYMBOL_GPL(fsl_ssi_destroy_dai);
+
+MODULE_AUTHOR("Timur Tabi <timur@freescale.com>");
+MODULE_DESCRIPTION("Freescale Synchronous Serial Interface (SSI) ASoC Driver");
+MODULE_LICENSE("GPL");
diff --git a/sound/soc/fsl/fsl_ssi.h b/sound/soc/fsl/fsl_ssi.h
new file mode 100644 (file)
index 0000000..c5ce88e
--- /dev/null
@@ -0,0 +1,224 @@
+/*
+ * fsl_ssi.h - ALSA SSI interface for the Freescale MPC8610 SoC
+ *
+ * Author: Timur Tabi <timur@freescale.com>
+ *
+ * Copyright 2007-2008 Freescale Semiconductor, Inc.  This file is licensed
+ * under the terms of the GNU General Public License version 2.  This
+ * program is licensed "as is" without any warranty of any kind, whether
+ * express or implied.
+ */
+
+#ifndef _MPC8610_I2S_H
+#define _MPC8610_I2S_H
+
+/* SSI Register Map */
+struct ccsr_ssi {
+       __be32 stx0;    /* 0x.0000 - SSI Transmit Data Register 0 */
+       __be32 stx1;    /* 0x.0004 - SSI Transmit Data Register 1 */
+       __be32 srx0;    /* 0x.0008 - SSI Receive Data Register 0 */
+       __be32 srx1;    /* 0x.000C - SSI Receive Data Register 1 */
+       __be32 scr;     /* 0x.0010 - SSI Control Register */
+       __be32 sisr;    /* 0x.0014 - SSI Interrupt Status Register Mixed */
+       __be32 sier;    /* 0x.0018 - SSI Interrupt Enable Register */
+       __be32 stcr;    /* 0x.001C - SSI Transmit Configuration Register */
+       __be32 srcr;    /* 0x.0020 - SSI Receive Configuration Register */
+       __be32 stccr;   /* 0x.0024 - SSI Transmit Clock Control Register */
+       __be32 srccr;   /* 0x.0028 - SSI Receive Clock Control Register */
+       __be32 sfcsr;   /* 0x.002C - SSI FIFO Control/Status Register */
+       __be32 str;     /* 0x.0030 - SSI Test Register */
+       __be32 sor;     /* 0x.0034 - SSI Option Register */
+       __be32 sacnt;   /* 0x.0038 - SSI AC97 Control Register */
+       __be32 sacadd;  /* 0x.003C - SSI AC97 Command Address Register */
+       __be32 sacdat;  /* 0x.0040 - SSI AC97 Command Data Register */
+       __be32 satag;   /* 0x.0044 - SSI AC97 Tag Register */
+       __be32 stmsk;   /* 0x.0048 - SSI Transmit Time Slot Mask Register */
+       __be32 srmsk;   /* 0x.004C - SSI Receive Time Slot Mask Register */
+       __be32 saccst;  /* 0x.0050 - SSI AC97 Channel Status Register */
+       __be32 saccen;  /* 0x.0054 - SSI AC97 Channel Enable Register */
+       __be32 saccdis; /* 0x.0058 - SSI AC97 Channel Disable Register */
+};
+
+#define CCSR_SSI_SCR_RFR_CLK_DIS       0x00000800
+#define CCSR_SSI_SCR_TFR_CLK_DIS       0x00000400
+#define CCSR_SSI_SCR_TCH_EN            0x00000100
+#define CCSR_SSI_SCR_SYS_CLK_EN                0x00000080
+#define CCSR_SSI_SCR_I2S_MODE_MASK     0x00000060
+#define CCSR_SSI_SCR_I2S_MODE_NORMAL   0x00000000
+#define CCSR_SSI_SCR_I2S_MODE_MASTER   0x00000020
+#define CCSR_SSI_SCR_I2S_MODE_SLAVE    0x00000040
+#define CCSR_SSI_SCR_SYN               0x00000010
+#define CCSR_SSI_SCR_NET               0x00000008
+#define CCSR_SSI_SCR_RE                        0x00000004
+#define CCSR_SSI_SCR_TE                        0x00000002
+#define CCSR_SSI_SCR_SSIEN             0x00000001
+
+#define CCSR_SSI_SISR_RFRC             0x01000000
+#define CCSR_SSI_SISR_TFRC             0x00800000
+#define CCSR_SSI_SISR_CMDAU            0x00040000
+#define CCSR_SSI_SISR_CMDDU            0x00020000
+#define CCSR_SSI_SISR_RXT              0x00010000
+#define CCSR_SSI_SISR_RDR1             0x00008000
+#define CCSR_SSI_SISR_RDR0             0x00004000
+#define CCSR_SSI_SISR_TDE1             0x00002000
+#define CCSR_SSI_SISR_TDE0             0x00001000
+#define CCSR_SSI_SISR_ROE1             0x00000800
+#define CCSR_SSI_SISR_ROE0             0x00000400
+#define CCSR_SSI_SISR_TUE1             0x00000200
+#define CCSR_SSI_SISR_TUE0             0x00000100
+#define CCSR_SSI_SISR_TFS              0x00000080
+#define CCSR_SSI_SISR_RFS              0x00000040
+#define CCSR_SSI_SISR_TLS              0x00000020
+#define CCSR_SSI_SISR_RLS              0x00000010
+#define CCSR_SSI_SISR_RFF1             0x00000008
+#define CCSR_SSI_SISR_RFF0             0x00000004
+#define CCSR_SSI_SISR_TFE1             0x00000002
+#define CCSR_SSI_SISR_TFE0             0x00000001
+
+#define CCSR_SSI_SIER_RFRC_EN          0x01000000
+#define CCSR_SSI_SIER_TFRC_EN          0x00800000
+#define CCSR_SSI_SIER_RDMAE            0x00400000
+#define CCSR_SSI_SIER_RIE              0x00200000
+#define CCSR_SSI_SIER_TDMAE            0x00100000
+#define CCSR_SSI_SIER_TIE              0x00080000
+#define CCSR_SSI_SIER_CMDAU_EN         0x00040000
+#define CCSR_SSI_SIER_CMDDU_EN         0x00020000
+#define CCSR_SSI_SIER_RXT_EN           0x00010000
+#define CCSR_SSI_SIER_RDR1_EN          0x00008000
+#define CCSR_SSI_SIER_RDR0_EN          0x00004000
+#define CCSR_SSI_SIER_TDE1_EN          0x00002000
+#define CCSR_SSI_SIER_TDE0_EN          0x00001000
+#define CCSR_SSI_SIER_ROE1_EN          0x00000800
+#define CCSR_SSI_SIER_ROE0_EN          0x00000400
+#define CCSR_SSI_SIER_TUE1_EN          0x00000200
+#define CCSR_SSI_SIER_TUE0_EN          0x00000100
+#define CCSR_SSI_SIER_TFS_EN           0x00000080
+#define CCSR_SSI_SIER_RFS_EN           0x00000040
+#define CCSR_SSI_SIER_TLS_EN           0x00000020
+#define CCSR_SSI_SIER_RLS_EN           0x00000010
+#define CCSR_SSI_SIER_RFF1_EN          0x00000008
+#define CCSR_SSI_SIER_RFF0_EN          0x00000004
+#define CCSR_SSI_SIER_TFE1_EN          0x00000002
+#define CCSR_SSI_SIER_TFE0_EN          0x00000001
+
+#define CCSR_SSI_STCR_TXBIT0           0x00000200
+#define CCSR_SSI_STCR_TFEN1            0x00000100
+#define CCSR_SSI_STCR_TFEN0            0x00000080
+#define CCSR_SSI_STCR_TFDIR            0x00000040
+#define CCSR_SSI_STCR_TXDIR            0x00000020
+#define CCSR_SSI_STCR_TSHFD            0x00000010
+#define CCSR_SSI_STCR_TSCKP            0x00000008
+#define CCSR_SSI_STCR_TFSI             0x00000004
+#define CCSR_SSI_STCR_TFSL             0x00000002
+#define CCSR_SSI_STCR_TEFS             0x00000001
+
+#define CCSR_SSI_SRCR_RXEXT            0x00000400
+#define CCSR_SSI_SRCR_RXBIT0           0x00000200
+#define CCSR_SSI_SRCR_RFEN1            0x00000100
+#define CCSR_SSI_SRCR_RFEN0            0x00000080
+#define CCSR_SSI_SRCR_RFDIR            0x00000040
+#define CCSR_SSI_SRCR_RXDIR            0x00000020
+#define CCSR_SSI_SRCR_RSHFD            0x00000010
+#define CCSR_SSI_SRCR_RSCKP            0x00000008
+#define CCSR_SSI_SRCR_RFSI             0x00000004
+#define CCSR_SSI_SRCR_RFSL             0x00000002
+#define CCSR_SSI_SRCR_REFS             0x00000001
+
+/* STCCR and SRCCR */
+#define CCSR_SSI_SxCCR_DIV2            0x00040000
+#define CCSR_SSI_SxCCR_PSR             0x00020000
+#define CCSR_SSI_SxCCR_WL_SHIFT                13
+#define CCSR_SSI_SxCCR_WL_MASK         0x0001E000
+#define CCSR_SSI_SxCCR_WL(x) \
+       (((((x) / 2) - 1) << CCSR_SSI_SxCCR_WL_SHIFT) & CCSR_SSI_SxCCR_WL_MASK)
+#define CCSR_SSI_SxCCR_DC_SHIFT                8
+#define CCSR_SSI_SxCCR_DC_MASK         0x00001F00
+#define CCSR_SSI_SxCCR_DC(x) \
+       ((((x) - 1) << CCSR_SSI_SxCCR_DC_SHIFT) & CCSR_SSI_SxCCR_DC_MASK)
+#define CCSR_SSI_SxCCR_PM_SHIFT                0
+#define CCSR_SSI_SxCCR_PM_MASK         0x000000FF
+#define CCSR_SSI_SxCCR_PM(x) \
+       ((((x) - 1) << CCSR_SSI_SxCCR_PM_SHIFT) & CCSR_SSI_SxCCR_PM_MASK)
+
+/*
+ * The xFCNT bits are read-only, and the xFWM bits are read/write.  Use the
+ * CCSR_SSI_SFCSR_xFCNTy() macros to read the FIFO counters, and use the
+ * CCSR_SSI_SFCSR_xFWMy() macros to set the watermarks.
+ */
+#define CCSR_SSI_SFCSR_RFCNT1_SHIFT    28
+#define CCSR_SSI_SFCSR_RFCNT1_MASK     0xF0000000
+#define CCSR_SSI_SFCSR_RFCNT1(x) \
+       (((x) & CCSR_SSI_SFCSR_RFCNT1_MASK) >> CCSR_SSI_SFCSR_RFCNT1_SHIFT)
+#define CCSR_SSI_SFCSR_TFCNT1_SHIFT    24
+#define CCSR_SSI_SFCSR_TFCNT1_MASK     0x0F000000
+#define CCSR_SSI_SFCSR_TFCNT1(x) \
+       (((x) & CCSR_SSI_SFCSR_TFCNT1_MASK) >> CCSR_SSI_SFCSR_TFCNT1_SHIFT)
+#define CCSR_SSI_SFCSR_RFWM1_SHIFT     20
+#define CCSR_SSI_SFCSR_RFWM1_MASK      0x00F00000
+#define CCSR_SSI_SFCSR_RFWM1(x)        \
+       (((x) << CCSR_SSI_SFCSR_RFWM1_SHIFT) & CCSR_SSI_SFCSR_RFWM1_MASK)
+#define CCSR_SSI_SFCSR_TFWM1_SHIFT     16
+#define CCSR_SSI_SFCSR_TFWM1_MASK      0x000F0000
+#define CCSR_SSI_SFCSR_TFWM1(x)        \
+       (((x) << CCSR_SSI_SFCSR_TFWM1_SHIFT) & CCSR_SSI_SFCSR_TFWM1_MASK)
+#define CCSR_SSI_SFCSR_RFCNT0_SHIFT    12
+#define CCSR_SSI_SFCSR_RFCNT0_MASK     0x0000F000
+#define CCSR_SSI_SFCSR_RFCNT0(x) \
+       (((x) & CCSR_SSI_SFCSR_RFCNT0_MASK) >> CCSR_SSI_SFCSR_RFCNT0_SHIFT)
+#define CCSR_SSI_SFCSR_TFCNT0_SHIFT    8
+#define CCSR_SSI_SFCSR_TFCNT0_MASK     0x00000F00
+#define CCSR_SSI_SFCSR_TFCNT0(x) \
+       (((x) & CCSR_SSI_SFCSR_TFCNT0_MASK) >> CCSR_SSI_SFCSR_TFCNT0_SHIFT)
+#define CCSR_SSI_SFCSR_RFWM0_SHIFT     4
+#define CCSR_SSI_SFCSR_RFWM0_MASK      0x000000F0
+#define CCSR_SSI_SFCSR_RFWM0(x)        \
+       (((x) << CCSR_SSI_SFCSR_RFWM0_SHIFT) & CCSR_SSI_SFCSR_RFWM0_MASK)
+#define CCSR_SSI_SFCSR_TFWM0_SHIFT     0
+#define CCSR_SSI_SFCSR_TFWM0_MASK      0x0000000F
+#define CCSR_SSI_SFCSR_TFWM0(x)        \
+       (((x) << CCSR_SSI_SFCSR_TFWM0_SHIFT) & CCSR_SSI_SFCSR_TFWM0_MASK)
+
+#define CCSR_SSI_STR_TEST              0x00008000
+#define CCSR_SSI_STR_RCK2TCK           0x00004000
+#define CCSR_SSI_STR_RFS2TFS           0x00002000
+#define CCSR_SSI_STR_RXSTATE(x) (((x) >> 8) & 0x1F)
+#define CCSR_SSI_STR_TXD2RXD           0x00000080
+#define CCSR_SSI_STR_TCK2RCK           0x00000040
+#define CCSR_SSI_STR_TFS2RFS           0x00000020
+#define CCSR_SSI_STR_TXSTATE(x) ((x) & 0x1F)
+
+#define CCSR_SSI_SOR_CLKOFF            0x00000040
+#define CCSR_SSI_SOR_RX_CLR            0x00000020
+#define CCSR_SSI_SOR_TX_CLR            0x00000010
+#define CCSR_SSI_SOR_INIT              0x00000008
+#define CCSR_SSI_SOR_WAIT_SHIFT                1
+#define CCSR_SSI_SOR_WAIT_MASK         0x00000006
+#define CCSR_SSI_SOR_WAIT(x) (((x) & 3) << CCSR_SSI_SOR_WAIT_SHIFT)
+#define CCSR_SSI_SOR_SYNRST            0x00000001
+
+/* Instantiation data for an SSI interface
+ *
+ * This structure contains all the information that the the SSI driver needs
+ * to instantiate an SSI interface with ALSA.  The machine driver should
+ * create this structure, fill it in, call fsl_ssi_create_dai(), and then
+ * delete the structure.
+ *
+ * id: which SSI this is (0, 1, etc. )
+ * ssi: pointer to the SSI's registers
+ * ssi_phys: physical address of the SSI registers
+ * irq: IRQ of this SSI
+ * dev: struct device, used to create the sysfs statistics file
+*/
+struct fsl_ssi_info {
+       unsigned int id;
+       struct ccsr_ssi __iomem *ssi;
+       dma_addr_t ssi_phys;
+       unsigned int irq;
+       struct device *dev;
+};
+
+struct snd_soc_cpu_dai *fsl_ssi_create_dai(struct fsl_ssi_info *ssi_info);
+void fsl_ssi_destroy_dai(struct snd_soc_cpu_dai *fsl_ssi_dai);
+
+#endif
+
diff --git a/sound/soc/fsl/mpc8610_hpcd.c b/sound/soc/fsl/mpc8610_hpcd.c
new file mode 100644 (file)
index 0000000..f26c4b2
--- /dev/null
@@ -0,0 +1,631 @@
+/**
+ * Freescale MPC8610HPCD ALSA SoC Fabric driver
+ *
+ * Author: Timur Tabi <timur@freescale.com>
+ *
+ * Copyright 2007-2008 Freescale Semiconductor, Inc.  This file is licensed
+ * under the terms of the GNU General Public License version 2.  This
+ * program is licensed "as is" without any warranty of any kind, whether
+ * express or implied.
+ */
+
+#include <linux/module.h>
+#include <linux/interrupt.h>
+#include <linux/of_device.h>
+#include <linux/of_platform.h>
+#include <sound/soc.h>
+#include <asm/immap_86xx.h>
+
+#include "../codecs/cs4270.h"
+#include "fsl_dma.h"
+#include "fsl_ssi.h"
+
+/**
+ * mpc8610_hpcd_data: fabric-specific ASoC device data
+ *
+ * This structure contains data for a single sound platform device on an
+ * MPC8610 HPCD.  Some of the data is taken from the device tree.
+ */
+struct mpc8610_hpcd_data {
+       struct snd_soc_device sound_devdata;
+       struct snd_soc_dai_link dai;
+       struct snd_soc_machine machine;
+       unsigned int dai_format;
+       unsigned int codec_clk_direction;
+       unsigned int cpu_clk_direction;
+       unsigned int clk_frequency;
+       struct ccsr_guts __iomem *guts;
+       struct ccsr_ssi __iomem *ssi;
+       unsigned int ssi_id;            /* 0 = SSI1, 1 = SSI2, etc */
+       unsigned int ssi_irq;
+       unsigned int dma_id;            /* 0 = DMA1, 1 = DMA2, etc */
+       unsigned int dma_irq[2];
+       struct ccsr_dma_channel __iomem *dma[2];
+       unsigned int dma_channel_id[2]; /* 0 = ch 0, 1 = ch 1, etc*/
+};
+
+/**
+ * mpc8610_hpcd_machine_probe: initalize the board
+ *
+ * This function is called when platform_device_add() is called.  It is used
+ * to initialize the board-specific hardware.
+ *
+ * Here we program the DMACR and PMUXCR registers.
+ */
+static int mpc8610_hpcd_machine_probe(struct platform_device *sound_device)
+{
+       struct mpc8610_hpcd_data *machine_data =
+               sound_device->dev.platform_data;
+
+       /* Program the signal routing between the SSI and the DMA */
+       guts_set_dmacr(machine_data->guts, machine_data->dma_id + 1,
+               machine_data->dma_channel_id[0], CCSR_GUTS_DMACR_DEV_SSI);
+       guts_set_dmacr(machine_data->guts, machine_data->dma_id + 1,
+               machine_data->dma_channel_id[1], CCSR_GUTS_DMACR_DEV_SSI);
+
+       guts_set_pmuxcr_dma(machine_data->guts, machine_data->dma_id,
+               machine_data->dma_channel_id[0], 0);
+       guts_set_pmuxcr_dma(machine_data->guts, machine_data->dma_id,
+               machine_data->dma_channel_id[1], 0);
+
+       guts_set_pmuxcr_dma(machine_data->guts, 1, 0, 0);
+       guts_set_pmuxcr_dma(machine_data->guts, 1, 3, 0);
+       guts_set_pmuxcr_dma(machine_data->guts, 0, 3, 0);
+
+       switch (machine_data->ssi_id) {
+       case 0:
+               clrsetbits_be32(&machine_data->guts->pmuxcr,
+                       CCSR_GUTS_PMUXCR_SSI1_MASK, CCSR_GUTS_PMUXCR_SSI1_SSI);
+               break;
+       case 1:
+               clrsetbits_be32(&machine_data->guts->pmuxcr,
+                       CCSR_GUTS_PMUXCR_SSI2_MASK, CCSR_GUTS_PMUXCR_SSI2_SSI);
+               break;
+       }
+
+       return 0;
+}
+
+/**
+ * mpc8610_hpcd_startup: program the board with various hardware parameters
+ *
+ * This function takes board-specific information, like clock frequencies
+ * and serial data formats, and passes that information to the codec and
+ * transport drivers.
+ */
+static int mpc8610_hpcd_startup(struct snd_pcm_substream *substream)
+{
+       struct snd_soc_pcm_runtime *rtd = substream->private_data;
+       struct snd_soc_codec_dai *codec_dai = rtd->dai->codec_dai;
+       struct snd_soc_cpu_dai *cpu_dai = rtd->dai->cpu_dai;
+       struct mpc8610_hpcd_data *machine_data =
+               rtd->socdev->dev->platform_data;
+       int ret = 0;
+
+       /* Tell the CPU driver what the serial protocol is. */
+       if (cpu_dai->dai_ops.set_fmt) {
+               ret = cpu_dai->dai_ops.set_fmt(cpu_dai,
+                       machine_data->dai_format);
+               if (ret < 0) {
+                       dev_err(substream->pcm->card->dev,
+                               "could not set CPU driver audio format\n");
+                       return ret;
+               }
+       }
+
+       /* Tell the codec driver what the serial protocol is. */
+       if (codec_dai->dai_ops.set_fmt) {
+               ret = codec_dai->dai_ops.set_fmt(codec_dai,
+                       machine_data->dai_format);
+               if (ret < 0) {
+                       dev_err(substream->pcm->card->dev,
+                               "could not set codec driver audio format\n");
+                       return ret;
+               }
+       }
+
+       /*
+        * Tell the CPU driver what the clock frequency is, and whether it's a
+        * slave or master.
+        */
+       if (cpu_dai->dai_ops.set_sysclk) {
+               ret = cpu_dai->dai_ops.set_sysclk(cpu_dai, 0,
+                       machine_data->clk_frequency,
+                       machine_data->cpu_clk_direction);
+               if (ret < 0) {
+                       dev_err(substream->pcm->card->dev,
+                               "could not set CPU driver clock parameters\n");
+                       return ret;
+               }
+       }
+
+       /*
+        * Tell the codec driver what the MCLK frequency is, and whether it's
+        * a slave or master.
+        */
+       if (codec_dai->dai_ops.set_sysclk) {
+               ret = codec_dai->dai_ops.set_sysclk(codec_dai, 0,
+                       machine_data->clk_frequency,
+                       machine_data->codec_clk_direction);
+               if (ret < 0) {
+                       dev_err(substream->pcm->card->dev,
+                               "could not set codec driver clock params\n");
+                       return ret;
+               }
+       }
+
+       return 0;
+}
+
+/**
+ * mpc8610_hpcd_machine_remove: Remove the sound device
+ *
+ * This function is called to remove the sound device for one SSI.  We
+ * de-program the DMACR and PMUXCR register.
+ */
+int mpc8610_hpcd_machine_remove(struct platform_device *sound_device)
+{
+       struct mpc8610_hpcd_data *machine_data =
+               sound_device->dev.platform_data;
+
+       /* Restore the signal routing */
+
+       guts_set_dmacr(machine_data->guts, machine_data->dma_id + 1,
+               machine_data->dma_channel_id[0], 0);
+       guts_set_dmacr(machine_data->guts, machine_data->dma_id + 1,
+               machine_data->dma_channel_id[1], 0);
+
+       switch (machine_data->ssi_id) {
+       case 0:
+               clrsetbits_be32(&machine_data->guts->pmuxcr,
+                       CCSR_GUTS_PMUXCR_SSI1_MASK, CCSR_GUTS_PMUXCR_SSI1_LA);
+               break;
+       case 1:
+               clrsetbits_be32(&machine_data->guts->pmuxcr,
+                       CCSR_GUTS_PMUXCR_SSI2_MASK, CCSR_GUTS_PMUXCR_SSI1_LA);
+               break;
+       }
+
+       return 0;
+}
+
+/**
+ * mpc8610_hpcd_ops: ASoC fabric driver operations
+ */
+static struct snd_soc_ops mpc8610_hpcd_ops = {
+       .startup = mpc8610_hpcd_startup,
+};
+
+/**
+ * mpc8610_hpcd_machine: ASoC machine data
+ */
+static struct snd_soc_machine mpc8610_hpcd_machine = {
+       .probe = mpc8610_hpcd_machine_probe,
+       .remove = mpc8610_hpcd_machine_remove,
+       .name = "MPC8610 HPCD",
+       .num_links = 1,
+};
+
+/**
+ * mpc8610_hpcd_probe: OF probe function for the fabric driver
+ *
+ * This function gets called when an SSI node is found in the device tree.
+ *
+ * Although this is a fabric driver, the SSI node is the "master" node with
+ * respect to audio hardware connections.  Therefore, we create a new ASoC
+ * device for each new SSI node that has a codec attached.
+ *
+ * FIXME: Currently, we only support one DMA controller, so if there are
+ * multiple SSI nodes with codecs, only the first will be supported.
+ *
+ * FIXME: Even if we did support multiple DMA controllers, we have no
+ * mechanism for assigning DMA controllers and channels to the individual
+ * SSI devices.  We also probably aren't compatible with the generic Elo DMA
+ * device driver.
+ */
+static int mpc8610_hpcd_probe(struct of_device *ofdev,
+       const struct of_device_id *match)
+{
+       struct device_node *np = ofdev->node;
+       struct device_node *codec_np = NULL;
+       struct device_node *guts_np = NULL;
+       struct device_node *dma_np = NULL;
+       struct device_node *dma_channel_np = NULL;
+       const phandle *codec_ph;
+       const char *sprop;
+       const u32 *iprop;
+       struct resource res;
+       struct platform_device *sound_device = NULL;
+       struct mpc8610_hpcd_data *machine_data;
+       struct fsl_ssi_info ssi_info;
+       struct fsl_dma_info dma_info;
+       int ret = -ENODEV;
+
+       machine_data = kzalloc(sizeof(struct mpc8610_hpcd_data), GFP_KERNEL);
+       if (!machine_data)
+               return -ENOMEM;
+
+       memset(&ssi_info, 0, sizeof(ssi_info));
+       memset(&dma_info, 0, sizeof(dma_info));
+
+       ssi_info.dev = &ofdev->dev;
+
+       /*
+        * We are only interested in SSIs with a codec phandle in them, so let's
+        * make sure this SSI has one.
+        */
+       codec_ph = of_get_property(np, "codec-handle", NULL);
+       if (!codec_ph)
+               goto error;
+
+       codec_np = of_find_node_by_phandle(*codec_ph);
+       if (!codec_np)
+               goto error;
+
+       /* The MPC8610 HPCD only knows about the CS4270 codec, so reject
+          anything else. */
+       if (!of_device_is_compatible(codec_np, "cirrus,cs4270"))
+               goto error;
+
+       /* Get the device ID */
+       iprop = of_get_property(np, "cell-index", NULL);
+       if (!iprop) {
+               dev_err(&ofdev->dev, "cell-index property not found\n");
+               ret = -EINVAL;
+               goto error;
+       }
+       machine_data->ssi_id = *iprop;
+       ssi_info.id = *iprop;
+
+       /* Get the serial format and clock direction. */
+       sprop = of_get_property(np, "fsl,mode", NULL);
+       if (!sprop) {
+               dev_err(&ofdev->dev, "fsl,mode property not found\n");
+               ret = -EINVAL;
+               goto error;
+       }
+
+       if (strcasecmp(sprop, "i2s-slave") == 0) {
+               machine_data->dai_format = SND_SOC_DAIFMT_I2S;
+               machine_data->codec_clk_direction = SND_SOC_CLOCK_OUT;
+               machine_data->cpu_clk_direction = SND_SOC_CLOCK_IN;
+
+               /*
+                * In i2s-slave mode, the codec has its own clock source, so we
+                * need to get the frequency from the device tree and pass it to
+                * the codec driver.
+                */
+               iprop = of_get_property(codec_np, "clock-frequency", NULL);
+               if (!iprop || !*iprop) {
+                       dev_err(&ofdev->dev, "codec bus-frequency property "
+                               "is missing or invalid\n");
+                       ret = -EINVAL;
+                       goto error;
+               }
+               machine_data->clk_frequency = *iprop;
+       } else if (strcasecmp(sprop, "i2s-master") == 0) {
+               machine_data->dai_format = SND_SOC_DAIFMT_I2S;
+               machine_data->codec_clk_direction = SND_SOC_CLOCK_IN;
+               machine_data->cpu_clk_direction = SND_SOC_CLOCK_OUT;
+       } else if (strcasecmp(sprop, "lj-slave") == 0) {
+               machine_data->dai_format = SND_SOC_DAIFMT_LEFT_J;
+               machine_data->codec_clk_direction = SND_SOC_CLOCK_OUT;
+               machine_data->cpu_clk_direction = SND_SOC_CLOCK_IN;
+       } else if (strcasecmp(sprop, "lj-master") == 0) {
+               machine_data->dai_format = SND_SOC_DAIFMT_LEFT_J;
+               machine_data->codec_clk_direction = SND_SOC_CLOCK_IN;
+               machine_data->cpu_clk_direction = SND_SOC_CLOCK_OUT;
+       } else if (strcasecmp(sprop, "rj-master") == 0) {
+               machine_data->dai_format = SND_SOC_DAIFMT_RIGHT_J;
+               machine_data->codec_clk_direction = SND_SOC_CLOCK_OUT;
+               machine_data->cpu_clk_direction = SND_SOC_CLOCK_IN;
+       } else if (strcasecmp(sprop, "rj-master") == 0) {
+               machine_data->dai_format = SND_SOC_DAIFMT_RIGHT_J;
+               machine_data->codec_clk_direction = SND_SOC_CLOCK_IN;
+               machine_data->cpu_clk_direction = SND_SOC_CLOCK_OUT;
+       } else if (strcasecmp(sprop, "ac97-slave") == 0) {
+               machine_data->dai_format = SND_SOC_DAIFMT_AC97;
+               machine_data->codec_clk_direction = SND_SOC_CLOCK_OUT;
+               machine_data->cpu_clk_direction = SND_SOC_CLOCK_IN;
+       } else if (strcasecmp(sprop, "ac97-master") == 0) {
+               machine_data->dai_format = SND_SOC_DAIFMT_AC97;
+               machine_data->codec_clk_direction = SND_SOC_CLOCK_IN;
+               machine_data->cpu_clk_direction = SND_SOC_CLOCK_OUT;
+       } else {
+               dev_err(&ofdev->dev,
+                       "unrecognized fsl,mode property \"%s\"\n", sprop);
+               ret = -EINVAL;
+               goto error;
+       }
+
+       if (!machine_data->clk_frequency) {
+               dev_err(&ofdev->dev, "unknown clock frequency\n");
+               ret = -EINVAL;
+               goto error;
+       }
+
+       /* Read the SSI information from the device tree */
+       ret = of_address_to_resource(np, 0, &res);
+       if (ret) {
+               dev_err(&ofdev->dev, "could not obtain SSI address\n");
+               goto error;
+       }
+       if (!res.start) {
+               dev_err(&ofdev->dev, "invalid SSI address\n");
+               goto error;
+       }
+       ssi_info.ssi_phys = res.start;
+
+       machine_data->ssi = ioremap(ssi_info.ssi_phys, sizeof(struct ccsr_ssi));
+       if (!machine_data->ssi) {
+               dev_err(&ofdev->dev, "could not map SSI address %x\n",
+                       ssi_info.ssi_phys);
+               ret = -EINVAL;
+               goto error;
+       }
+       ssi_info.ssi = machine_data->ssi;
+
+
+       /* Get the IRQ of the SSI */
+       machine_data->ssi_irq = irq_of_parse_and_map(np, 0);
+       if (!machine_data->ssi_irq) {
+               dev_err(&ofdev->dev, "could not get SSI IRQ\n");
+               ret = -EINVAL;
+               goto error;
+       }
+       ssi_info.irq = machine_data->ssi_irq;
+
+
+       /* Map the global utilities registers. */
+       guts_np = of_find_compatible_node(NULL, NULL, "fsl,mpc8610-guts");
+       if (!guts_np) {
+               dev_err(&ofdev->dev, "could not obtain address of GUTS\n");
+               ret = -EINVAL;
+               goto error;
+       }
+       machine_data->guts = of_iomap(guts_np, 0);
+       of_node_put(guts_np);
+       if (!machine_data->guts) {
+               dev_err(&ofdev->dev, "could not map GUTS\n");
+               ret = -EINVAL;
+               goto error;
+       }
+
+       /* Find the DMA channels to use.  For now, we always use the first DMA
+          controller. */
+       for_each_compatible_node(dma_np, NULL, "fsl,mpc8610-dma") {
+               iprop = of_get_property(dma_np, "cell-index", NULL);
+               if (iprop && (*iprop == 0)) {
+                       of_node_put(dma_np);
+                       break;
+               }
+       }
+       if (!dma_np) {
+               dev_err(&ofdev->dev, "could not find DMA node\n");
+               ret = -EINVAL;
+               goto error;
+       }
+       machine_data->dma_id = *iprop;
+
+       /*
+        * Find the DMA channels to use.  For now, we always use DMA channel 0
+        * for playback, and DMA channel 1 for capture.
+        */
+       while ((dma_channel_np = of_get_next_child(dma_np, dma_channel_np))) {
+               iprop = of_get_property(dma_channel_np, "cell-index", NULL);
+               /* Is it DMA channel 0? */
+               if (iprop && (*iprop == 0)) {
+                       /* dma_channel[0] and dma_irq[0] are for playback */
+                       dma_info.dma_channel[0] = of_iomap(dma_channel_np, 0);
+                       dma_info.dma_irq[0] =
+                               irq_of_parse_and_map(dma_channel_np, 0);
+                       machine_data->dma_channel_id[0] = *iprop;
+                       continue;
+               }
+               if (iprop && (*iprop == 1)) {
+                       /* dma_channel[1] and dma_irq[1] are for capture */
+                       dma_info.dma_channel[1] = of_iomap(dma_channel_np, 0);
+                       dma_info.dma_irq[1] =
+                               irq_of_parse_and_map(dma_channel_np, 0);
+                       machine_data->dma_channel_id[1] = *iprop;
+                       continue;
+               }
+       }
+       if (!dma_info.dma_channel[0] || !dma_info.dma_channel[1] ||
+           !dma_info.dma_irq[0] || !dma_info.dma_irq[1]) {
+               dev_err(&ofdev->dev, "could not find DMA channels\n");
+               ret = -EINVAL;
+               goto error;
+       }
+
+       dma_info.ssi_stx_phys = ssi_info.ssi_phys +
+               offsetof(struct ccsr_ssi, stx0);
+       dma_info.ssi_srx_phys = ssi_info.ssi_phys +
+               offsetof(struct ccsr_ssi, srx0);
+
+       /* We have the DMA information, so tell the DMA driver what it is */
+       if (!fsl_dma_configure(&dma_info)) {
+               dev_err(&ofdev->dev, "could not instantiate DMA device\n");
+               ret = -EBUSY;
+               goto error;
+       }
+
+       /*
+        * Initialize our DAI data structure.  We should probably get this
+        * information from the device tree.
+        */
+       machine_data->dai.name = "CS4270";
+       machine_data->dai.stream_name = "CS4270";
+
+       machine_data->dai.cpu_dai = fsl_ssi_create_dai(&ssi_info);
+       machine_data->dai.codec_dai = &cs4270_dai; /* The codec_dai we want */
+       machine_data->dai.ops = &mpc8610_hpcd_ops;
+
+       mpc8610_hpcd_machine.dai_link = &machine_data->dai;
+
+       /* Allocate a new audio platform device structure */
+       sound_device = platform_device_alloc("soc-audio", -1);
+       if (!sound_device) {
+               dev_err(&ofdev->dev, "platform device allocation failed\n");
+               ret = -ENOMEM;
+               goto error;
+       }
+
+       machine_data->sound_devdata.machine = &mpc8610_hpcd_machine;
+       machine_data->sound_devdata.codec_dev = &soc_codec_device_cs4270;
+       machine_data->sound_devdata.platform = &fsl_soc_platform;
+
+       sound_device->dev.platform_data = machine_data;
+
+
+       /* Set the platform device and ASoC device to point to each other */
+       platform_set_drvdata(sound_device, &machine_data->sound_devdata);
+
+       machine_data->sound_devdata.dev = &sound_device->dev;
+
+
+       /* Tell ASoC to probe us.  This will call mpc8610_hpcd_machine.probe(),
+          if it exists. */
+       ret = platform_device_add(sound_device);
+
+       if (ret) {
+               dev_err(&ofdev->dev, "platform device add failed\n");
+               goto error;
+       }
+
+       dev_set_drvdata(&ofdev->dev, sound_device);
+
+       return 0;
+
+error:
+       of_node_put(codec_np);
+       of_node_put(guts_np);
+       of_node_put(dma_np);
+       of_node_put(dma_channel_np);
+
+       if (sound_device)
+               platform_device_unregister(sound_device);
+
+       if (machine_data->dai.cpu_dai)
+               fsl_ssi_destroy_dai(machine_data->dai.cpu_dai);
+
+       if (ssi_info.ssi)
+               iounmap(ssi_info.ssi);
+
+       if (ssi_info.irq)
+               irq_dispose_mapping(ssi_info.irq);
+
+       if (dma_info.dma_channel[0])
+               iounmap(dma_info.dma_channel[0]);
+
+       if (dma_info.dma_channel[1])
+               iounmap(dma_info.dma_channel[1]);
+
+       if (dma_info.dma_irq[0])
+               irq_dispose_mapping(dma_info.dma_irq[0]);
+
+       if (dma_info.dma_irq[1])
+               irq_dispose_mapping(dma_info.dma_irq[1]);
+
+       if (machine_data->guts)
+               iounmap(machine_data->guts);
+
+       kfree(machine_data);
+
+       return ret;
+}
+
+/**
+ * mpc8610_hpcd_remove: remove the OF device
+ *
+ * This function is called when the OF device is removed.
+ */
+static int mpc8610_hpcd_remove(struct of_device *ofdev)
+{
+       struct platform_device *sound_device = dev_get_drvdata(&ofdev->dev);
+       struct mpc8610_hpcd_data *machine_data =
+               sound_device->dev.platform_data;
+
+       platform_device_unregister(sound_device);
+
+       if (machine_data->dai.cpu_dai)
+               fsl_ssi_destroy_dai(machine_data->dai.cpu_dai);
+
+       if (machine_data->ssi)
+               iounmap(machine_data->ssi);
+
+       if (machine_data->dma[0])
+               iounmap(machine_data->dma[0]);
+
+       if (machine_data->dma[1])
+               iounmap(machine_data->dma[1]);
+
+       if (machine_data->dma_irq[0])
+               irq_dispose_mapping(machine_data->dma_irq[0]);
+
+       if (machine_data->dma_irq[1])
+               irq_dispose_mapping(machine_data->dma_irq[1]);
+
+       if (machine_data->guts)
+               iounmap(machine_data->guts);
+
+       kfree(machine_data);
+       sound_device->dev.platform_data = NULL;
+
+       dev_set_drvdata(&ofdev->dev, NULL);
+
+       return 0;
+}
+
+static struct of_device_id mpc8610_hpcd_match[] = {
+       {
+               .compatible = "fsl,mpc8610-ssi",
+       },
+       {}
+};
+MODULE_DEVICE_TABLE(of, mpc8610_hpcd_match);
+
+static struct of_platform_driver mpc8610_hpcd_of_driver = {
+       .owner          = THIS_MODULE,
+       .name           = "mpc8610_hpcd",
+       .match_table    = mpc8610_hpcd_match,
+       .probe          = mpc8610_hpcd_probe,
+       .remove         = mpc8610_hpcd_remove,
+};
+
+/**
+ * mpc8610_hpcd_init: fabric driver initialization.
+ *
+ * This function is called when this module is loaded.
+ */
+static int __init mpc8610_hpcd_init(void)
+{
+       int ret;
+
+       printk(KERN_INFO "Freescale MPC8610 HPCD ALSA SoC fabric driver\n");
+
+       ret = of_register_platform_driver(&mpc8610_hpcd_of_driver);
+
+       if (ret)
+               printk(KERN_ERR
+                       "mpc8610-hpcd: failed to register platform driver\n");
+
+       return ret;
+}
+
+/**
+ * mpc8610_hpcd_exit: fabric driver exit
+ *
+ * This function is called when this driver is unloaded.
+ */
+static void __exit mpc8610_hpcd_exit(void)
+{
+       of_unregister_platform_driver(&mpc8610_hpcd_of_driver);
+}
+
+module_init(mpc8610_hpcd_init);
+module_exit(mpc8610_hpcd_exit);
+
+MODULE_AUTHOR("Timur Tabi <timur@freescale.com>");
+MODULE_DESCRIPTION("Freescale MPC8610 HPCD ALSA SoC fabric driver");
+MODULE_LICENSE("GPL");
index a83e22937c270ce0d92720203d73de99f8d1e434..484f883459e086a95af2ff2edd881b8c8b9a0500 100644 (file)
@@ -53,3 +53,12 @@ config SND_PXA2XX_SOC_TOSA
        help
          Say Y if you want to add support for SoC audio on Sharp
          Zaurus SL-C6000x models (Tosa).
+
+config SND_PXA2XX_SOC_E800
+       tristate "SoC AC97 Audio support for e800"
+       depends on SND_PXA2XX_SOC && MACH_E800
+       select SND_SOC_WM9712
+       select SND_PXA2XX_SOC_AC97
+       help
+         Say Y if you want to add support for SoC audio on the
+         Toshiba e800 PDA
index 78e0d6b07d1db9e4a8648166a55803bdabb385b1..04e5646f75ba3f8d77cc5a0caf4d162439c87b53 100644 (file)
@@ -11,10 +11,12 @@ obj-$(CONFIG_SND_PXA2XX_SOC_I2S) += snd-soc-pxa2xx-i2s.o
 snd-soc-corgi-objs := corgi.o
 snd-soc-poodle-objs := poodle.o
 snd-soc-tosa-objs := tosa.o
+snd-soc-e800-objs := e800_wm9712.o
 snd-soc-spitz-objs := spitz.o
 
 obj-$(CONFIG_SND_PXA2XX_SOC_CORGI) += snd-soc-corgi.o
 obj-$(CONFIG_SND_PXA2XX_SOC_POODLE) += snd-soc-poodle.o
 obj-$(CONFIG_SND_PXA2XX_SOC_TOSA) += snd-soc-tosa.o
+obj-$(CONFIG_SND_PXA2XX_SOC_E800) += snd-soc-e800.o
 obj-$(CONFIG_SND_PXA2XX_SOC_SPITZ) += snd-soc-spitz.o
 
index 5ee51a994ac3570752cc9ed081acdb6b6bb7841c..3f34e531bebf7921d210a05e583c937535095de1 100644 (file)
@@ -22,7 +22,6 @@
 #include <linux/timer.h>
 #include <linux/interrupt.h>
 #include <linux/platform_device.h>
-#include <sound/driver.h>
 #include <sound/core.h>
 #include <sound/pcm.h>
 #include <sound/soc.h>
diff --git a/sound/soc/pxa/e800_wm9712.c b/sound/soc/pxa/e800_wm9712.c
new file mode 100644 (file)
index 0000000..06e8afb
--- /dev/null
@@ -0,0 +1,89 @@
+/*
+ * e800-wm9712.c  --  SoC audio for e800
+ *
+ * Based on tosa.c
+ *
+ * Copyright 2007 (c) Ian Molton <spyro@f2s.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; version 2 ONLY.
+ *
+ */
+
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/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 <asm/arch/pxa-regs.h>
+#include <asm/arch/hardware.h>
+#include <asm/arch/audio.h>
+
+#include "../codecs/wm9712.h"
+#include "pxa2xx-pcm.h"
+#include "pxa2xx-ac97.h"
+
+static struct snd_soc_machine e800;
+
+static struct snd_soc_dai_link e800_dai[] = {
+{
+       .name = "AC97 Aux",
+       .stream_name = "AC97 Aux",
+       .cpu_dai = &pxa_ac97_dai[PXA2XX_DAI_AC97_AUX],
+       .codec_dai = &wm9712_dai[WM9712_DAI_AC97_AUX],
+},
+};
+
+static struct snd_soc_machine e800 = {
+       .name = "Toshiba e800",
+       .dai_link = e800_dai,
+       .num_links = ARRAY_SIZE(e800_dai),
+};
+
+static struct snd_soc_device e800_snd_devdata = {
+       .machine = &e800,
+       .platform = &pxa2xx_soc_platform,
+       .codec_dev = &soc_codec_dev_wm9712,
+};
+
+static struct platform_device *e800_snd_device;
+
+static int __init e800_init(void)
+{
+       int ret;
+
+       if (!machine_is_e800())
+               return -ENODEV;
+
+       e800_snd_device = platform_device_alloc("soc-audio", -1);
+       if (!e800_snd_device)
+               return -ENOMEM;
+
+       platform_set_drvdata(e800_snd_device, &e800_snd_devdata);
+       e800_snd_devdata.dev = &e800_snd_device->dev;
+       ret = platform_device_add(e800_snd_device);
+
+       if (ret)
+               platform_device_put(e800_snd_device);
+
+       return ret;
+}
+
+static void __exit e800_exit(void)
+{
+       platform_device_unregister(e800_snd_device);
+}
+
+module_init(e800_init);
+module_exit(e800_exit);
+
+/* Module information */
+MODULE_AUTHOR("Ian Molton <spyro@f2s.com>");
+MODULE_DESCRIPTION("ALSA SoC driver for e800");
+MODULE_LICENSE("GPL");
index 0915cf740421a809c69e35db07c577a28c78fdab..5ae59bd309a33863d05b3173c7e879df77a6372a 100644 (file)
@@ -19,7 +19,6 @@
 #include <linux/timer.h>
 #include <linux/interrupt.h>
 #include <linux/platform_device.h>
-#include <sound/driver.h>
 #include <sound/core.h>
 #include <sound/pcm.h>
 #include <sound/soc.h>
index 60e6f4677f932e4f3ed7037fe275884162ecb248..815c15336255d985d9f9e3a23f81c1b370043a47 100644 (file)
@@ -17,7 +17,6 @@
 #include <linux/wait.h>
 #include <linux/delay.h>
 
-#include <sound/driver.h>
 #include <sound/core.h>
 #include <sound/pcm.h>
 #include <sound/ac97_codec.h>
index 50c5c83f67db9ce636305e0259db13eced65b192..692b900024891646b96f5f8fb10f7b5339e7c5b2 100644 (file)
@@ -18,7 +18,6 @@
 #include <linux/module.h>
 #include <linux/device.h>
 #include <linux/delay.h>
-#include <sound/driver.h>
 #include <sound/core.h>
 #include <sound/pcm.h>
 #include <sound/initval.h>
index 35e8fa3a469c4745f4ff22bd188b19b79cf09067..daeaa4c8b876fc72b5441f0c662b220b1945081b 100644 (file)
@@ -16,7 +16,6 @@
 #include <linux/slab.h>
 #include <linux/dma-mapping.h>
 
-#include <sound/driver.h>
 #include <sound/core.h>
 #include <sound/pcm.h>
 #include <sound/pcm_params.h>
index 4dd8f35312b300ea5a1c1e41ec4b431a0d74085e..d56709e15435e175cd533f7cd2badd558c3aded8 100644 (file)
@@ -22,7 +22,6 @@
 #include <linux/timer.h>
 #include <linux/interrupt.h>
 #include <linux/platform_device.h>
-#include <sound/driver.h>
 #include <sound/core.h>
 #include <sound/pcm.h>
 #include <sound/soc.h>
index 5504e30acf145508df4434af8239c3414cac8a61..e4d40b528ca47e6b85017ac52529b34fece23c55 100644 (file)
@@ -25,7 +25,6 @@
 #include <linux/moduleparam.h>
 #include <linux/device.h>
 
-#include <sound/driver.h>
 #include <sound/core.h>
 #include <sound/pcm.h>
 #include <sound/soc.h>
index 5632a2e1518d8dc48b242ad9579bccf2f136f6ec..1f6dbfc4caa85352412ba297e8094860e968fe00 100644 (file)
@@ -10,6 +10,9 @@ config SND_S3C24XX_SOC
 config SND_S3C24XX_SOC_I2S
        tristate
 
+config SND_S3C2412_SOC_I2S
+       tristate
+
 config SND_S3C2443_SOC_AC97
        tristate
        select AC97_BUS
@@ -34,4 +37,12 @@ config SND_S3C24XX_SOC_SMDK2443_WM9710
          Say Y if you want to add support for SoC audio on smdk2443
          with the WM9710.
 
+config SND_S3C24XX_SOC_LN2440SBC_ALC650
+       tristate "SoC AC97 Audio support for LN2440SBC - ALC650"
+       depends on SND_S3C24XX_SOC
+       select SND_S3C2443_SOC_AC97
+       select SND_SOC_AC97_CODEC
+       help
+         Say Y if you want to add support for SoC audio on ln2440sbc
+         with the ALC650.
 
index 13c92f0fa1e4a1905ea44b7a8019161eb0f78212..0aa5fb0b9700ae5d4db9ff3704da92aa18c7bfa9 100644 (file)
@@ -1,15 +1,19 @@
 # S3c24XX Platform Support
 snd-soc-s3c24xx-objs := s3c24xx-pcm.o
 snd-soc-s3c24xx-i2s-objs := s3c24xx-i2s.o
+snd-soc-s3c2412-i2s-objs := s3c2412-i2s.o
 snd-soc-s3c2443-ac97-objs := s3c2443-ac97.o
 
 obj-$(CONFIG_SND_S3C24XX_SOC) += snd-soc-s3c24xx.o
 obj-$(CONFIG_SND_S3C24XX_SOC_I2S) += snd-soc-s3c24xx-i2s.o
 obj-$(CONFIG_SND_S3C2443_SOC_AC97) += snd-soc-s3c2443-ac97.o
+obj-$(CONFIG_SND_S3C2412_SOC_I2S) += snd-soc-s3c2412-i2s.o
 
 # S3C24XX Machine Support
 snd-soc-neo1973-wm8753-objs := neo1973_wm8753.o
 snd-soc-smdk2443-wm9710-objs := smdk2443_wm9710.o
+snd-soc-ln2440sbc-alc650-objs := ln2440sbc_alc650.o
 
 obj-$(CONFIG_SND_S3C24XX_SOC_NEO1973_WM8753) += snd-soc-neo1973-wm8753.o
 obj-$(CONFIG_SND_S3C24XX_SOC_SMDK2443_WM9710) += snd-soc-smdk2443-wm9710.o
+obj-$(CONFIG_SND_S3C24XX_SOC_LN2440SBC_ALC650) += snd-soc-ln2440sbc-alc650.o
diff --git a/sound/soc/s3c24xx/ln2440sbc_alc650.c b/sound/soc/s3c24xx/ln2440sbc_alc650.c
new file mode 100644 (file)
index 0000000..9ed8f2e
--- /dev/null
@@ -0,0 +1,85 @@
+/*
+ * SoC audio for ln2440sbc
+ * 
+ * Copyright 2007 KonekTel, a.s.
+ * Author: Ivan Kuten
+ *         ivan.kuten@promwad.com
+ * 
+ * Heavily based on smdk2443_wm9710.c
+ * Copyright 2007 Wolfson Microelectronics PLC.
+ * Author: Graeme Gregory
+ *         graeme.gregory@wolfsonmicro.com or linux@wolfsonmicro.com
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License version 2 as
+ *  published by the Free Software Foundation.
+ *
+ */
+
+#include <linux/module.h>
+#include <linux/device.h>
+#include <sound/core.h>
+#include <sound/pcm.h>
+#include <sound/soc.h>
+#include <sound/soc-dapm.h>
+
+#include "../codecs/ac97.h"
+#include "s3c24xx-pcm.h"
+#include "s3c24xx-ac97.h"
+
+static struct snd_soc_machine ln2440sbc;
+
+static struct snd_soc_dai_link ln2440sbc_dai[] = {
+{
+       .name = "AC97",
+       .stream_name = "AC97 HiFi",
+       .cpu_dai = &s3c2443_ac97_dai[0],
+       .codec_dai = &ac97_dai,
+},
+};
+
+static struct snd_soc_machine ln2440sbc = {
+       .name = "LN2440SBC",
+       .dai_link = ln2440sbc_dai,
+       .num_links = ARRAY_SIZE(ln2440sbc_dai),
+};
+
+static struct snd_soc_device ln2440sbc_snd_ac97_devdata = {
+       .machine = &ln2440sbc,
+       .platform = &s3c24xx_soc_platform,
+       .codec_dev = &soc_codec_dev_ac97,
+};
+
+static struct platform_device *ln2440sbc_snd_ac97_device;
+
+static int __init ln2440sbc_init(void)
+{
+       int ret;
+
+       ln2440sbc_snd_ac97_device = platform_device_alloc("soc-audio", -1);
+       if (!ln2440sbc_snd_ac97_device)
+               return -ENOMEM;
+
+       platform_set_drvdata(ln2440sbc_snd_ac97_device,
+                               &ln2440sbc_snd_ac97_devdata);
+       ln2440sbc_snd_ac97_devdata.dev = &ln2440sbc_snd_ac97_device->dev;
+       ret = platform_device_add(ln2440sbc_snd_ac97_device);
+
+       if (ret)
+               platform_device_put(ln2440sbc_snd_ac97_device);
+
+       return ret;
+}
+
+static void __exit ln2440sbc_exit(void)
+{
+       platform_device_unregister(ln2440sbc_snd_ac97_device);
+}
+
+module_init(ln2440sbc_init);
+module_exit(ln2440sbc_exit);
+
+/* Module information */
+MODULE_AUTHOR("Ivan Kuten");
+MODULE_DESCRIPTION("ALSA SoC ALC650 LN2440SBC");
+MODULE_LICENSE("GPL");
index f1f6b9478af95d00480c2c814b9434bed98561e0..6ee115ceb011d22e207c5f33e43803d6a824f714 100644 (file)
@@ -22,7 +22,6 @@
 #include <linux/interrupt.h>
 #include <linux/platform_device.h>
 #include <linux/i2c.h>
-#include <sound/driver.h>
 #include <sound/core.h>
 #include <sound/pcm.h>
 #include <sound/soc.h>
 
 #include <asm/mach-types.h>
 #include <asm/hardware/scoop.h>
-#include <asm/arch/regs-iis.h>
 #include <asm/arch/regs-clock.h>
 #include <asm/arch/regs-gpio.h>
 #include <asm/hardware.h>
 #include <asm/arch/audio.h>
 #include <asm/io.h>
 #include <asm/arch/spi-gpio.h>
+
+#include <asm/plat-s3c24xx/regs-iis.h>
+
 #include "../codecs/wm8753.h"
 #include "lm4857.h"
 #include "s3c24xx-pcm.h"
diff --git a/sound/soc/s3c24xx/s3c2412-i2s.c b/sound/soc/s3c24xx/s3c2412-i2s.c
new file mode 100644 (file)
index 0000000..c4a46dd
--- /dev/null
@@ -0,0 +1,744 @@
+/* sound/soc/s3c24xx/s3c2412-i2s.c
+ *
+ * ALSA Soc Audio Layer - S3C2412 I2S driver
+ *
+ * Copyright (c) 2006 Wolfson Microelectronics PLC.
+ *     Graeme Gregory graeme.gregory@wolfsonmicro.com
+ *     linux@wolfsonmicro.com
+ *
+ * Copyright (c) 2007, 2004-2005 Simtec Electronics
+ *     http://armlinux.simtec.co.uk/
+ *     Ben Dooks <ben@simtec.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/init.h>
+#include <linux/module.h>
+#include <linux/device.h>
+#include <linux/delay.h>
+#include <linux/clk.h>
+#include <linux/kernel.h>
+
+#include <sound/core.h>
+#include <sound/pcm.h>
+#include <sound/pcm_params.h>
+#include <sound/initval.h>
+#include <sound/soc.h>
+#include <asm/hardware.h>
+
+#include <linux/io.h>
+#include <asm/dma.h>
+
+#include <asm/plat-s3c24xx/regs-s3c2412-iis.h>
+
+#include <asm/arch/regs-gpio.h>
+#include <asm/arch/audio.h>
+#include <asm/arch/dma.h>
+
+#include "s3c24xx-pcm.h"
+#include "s3c2412-i2s.h"
+
+#define S3C2412_I2S_DEBUG 0
+#define S3C2412_I2S_DEBUG_CON 0
+
+#if S3C2412_I2S_DEBUG
+#define DBG(x...) printk(KERN_INFO x)
+#else
+#define DBG(x...) do { } while (0)
+#endif
+
+static struct s3c2410_dma_client s3c2412_dma_client_out = {
+       .name           = "I2S PCM Stereo out"
+};
+
+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 = {
+       .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 = {
+       .client         = &s3c2412_dma_client_in,
+       .channel        = DMACH_I2S_IN,
+       .dma_addr       = S3C2410_PA_IIS + S3C2412_IISRXD,
+       .dma_size       = 4,
+};
+
+struct s3c2412_i2s_info {
+       struct device   *dev;
+       void __iomem    *regs;
+       struct clk      *iis_clk;
+       struct clk      *iis_pclk;
+       struct clk      *iis_cclk;
+
+       u32              suspend_iismod;
+       u32              suspend_iiscon;
+       u32              suspend_iispsr;
+};
+
+static struct s3c2412_i2s_info s3c2412_i2s;
+
+#define bit_set(v, b) (((v) & (b)) ? 1 : 0)
+
+#if S3C2412_I2S_DEBUG_CON
+static void dbg_showcon(const char *fn, u32 con)
+{
+       printk(KERN_DEBUG "%s: LRI=%d, TXFEMPT=%d, RXFEMPT=%d, TXFFULL=%d, RXFFULL=%d\n", fn,
+              bit_set(con, S3C2412_IISCON_LRINDEX),
+              bit_set(con, S3C2412_IISCON_TXFIFO_EMPTY),
+              bit_set(con, S3C2412_IISCON_RXFIFO_EMPTY),
+              bit_set(con, S3C2412_IISCON_TXFIFO_FULL),
+              bit_set(con, S3C2412_IISCON_RXFIFO_FULL));
+
+       printk(KERN_DEBUG "%s: PAUSE: TXDMA=%d, RXDMA=%d, TXCH=%d, RXCH=%d\n",
+              fn,
+              bit_set(con, S3C2412_IISCON_TXDMA_PAUSE),
+              bit_set(con, S3C2412_IISCON_RXDMA_PAUSE),
+              bit_set(con, S3C2412_IISCON_TXCH_PAUSE),
+              bit_set(con, S3C2412_IISCON_RXCH_PAUSE));
+       printk(KERN_DEBUG "%s: ACTIVE: TXDMA=%d, RXDMA=%d, IIS=%d\n", fn,
+              bit_set(con, S3C2412_IISCON_TXDMA_ACTIVE),
+              bit_set(con, S3C2412_IISCON_RXDMA_ACTIVE),
+              bit_set(con, S3C2412_IISCON_IIS_ACTIVE));
+}
+#else
+static inline void dbg_showcon(const char *fn, u32 con)
+{
+}
+#endif
+
+/* Turn on or off the transmission path. */
+static void s3c2412_snd_txctrl(int on)
+{
+       struct s3c2412_i2s_info *i2s = &s3c2412_i2s;
+       void __iomem *regs = i2s->regs;
+       u32 fic, con, mod;
+
+       DBG("%s(%d)\n", __func__, on);
+
+       fic = readl(regs + S3C2412_IISFIC);
+       con = readl(regs + S3C2412_IISCON);
+       mod = readl(regs + S3C2412_IISMOD);
+
+       DBG("%s: IIS: CON=%x MOD=%x FIC=%x\n", __func__, con, mod, fic);
+
+       if (on) {
+               con |= S3C2412_IISCON_TXDMA_ACTIVE | S3C2412_IISCON_IIS_ACTIVE;
+               con &= ~S3C2412_IISCON_TXDMA_PAUSE;
+               con &= ~S3C2412_IISCON_TXCH_PAUSE;
+
+               switch (mod & S3C2412_IISMOD_MODE_MASK) {
+               case S3C2412_IISMOD_MODE_TXONLY:
+               case S3C2412_IISMOD_MODE_TXRX:
+                       /* do nothing, we are in the right mode */
+                       break;
+
+               case S3C2412_IISMOD_MODE_RXONLY:
+                       mod &= ~S3C2412_IISMOD_MODE_MASK;
+                       mod |= S3C2412_IISMOD_MODE_TXRX;
+                       break;
+
+               default:
+                       dev_err(i2s->dev, "TXEN: Invalid MODE in IISMOD\n");
+               }
+
+               writel(con, regs + S3C2412_IISCON);
+               writel(mod, regs + S3C2412_IISMOD);
+       } else {
+               /* Note, we do not have any indication that the FIFO problems
+                * tha the S3C2410/2440 had apply here, so we should be able
+                * to disable the DMA and TX without resetting the FIFOS.
+                */
+
+               con |=  S3C2412_IISCON_TXDMA_PAUSE;
+               con |=  S3C2412_IISCON_TXCH_PAUSE;
+               con &= ~S3C2412_IISCON_TXDMA_ACTIVE;
+
+               switch (mod & S3C2412_IISMOD_MODE_MASK) {
+               case S3C2412_IISMOD_MODE_TXRX:
+                       mod &= ~S3C2412_IISMOD_MODE_MASK;
+                       mod |= S3C2412_IISMOD_MODE_RXONLY;
+                       break;
+
+               case S3C2412_IISMOD_MODE_TXONLY:
+                       mod &= ~S3C2412_IISMOD_MODE_MASK;
+                       con &= ~S3C2412_IISCON_IIS_ACTIVE;
+                       break;
+
+               default:
+                       dev_err(i2s->dev, "TXDIS: Invalid MODE in IISMOD\n");
+               }
+
+               writel(mod, regs + S3C2412_IISMOD);
+               writel(con, regs + S3C2412_IISCON);
+       }
+
+       fic = readl(regs + S3C2412_IISFIC);
+       dbg_showcon(__func__, con);
+       DBG("%s: IIS: CON=%x MOD=%x FIC=%x\n", __func__, con, mod, fic);
+}
+
+static void s3c2412_snd_rxctrl(int on)
+{
+       struct s3c2412_i2s_info *i2s = &s3c2412_i2s;
+       void __iomem *regs = i2s->regs;
+       u32 fic, con, mod;
+
+       DBG("%s(%d)\n", __func__, on);
+
+       fic = readl(regs + S3C2412_IISFIC);
+       con = readl(regs + S3C2412_IISCON);
+       mod = readl(regs + S3C2412_IISMOD);
+
+       DBG("%s: IIS: CON=%x MOD=%x FIC=%x\n", __func__, con, mod, fic);
+
+       if (on) {
+               con |= S3C2412_IISCON_RXDMA_ACTIVE | S3C2412_IISCON_IIS_ACTIVE;
+               con &= ~S3C2412_IISCON_RXDMA_PAUSE;
+               con &= ~S3C2412_IISCON_RXCH_PAUSE;
+
+               switch (mod & S3C2412_IISMOD_MODE_MASK) {
+               case S3C2412_IISMOD_MODE_TXRX:
+               case S3C2412_IISMOD_MODE_RXONLY:
+                       /* do nothing, we are in the right mode */
+                       break;
+
+               case S3C2412_IISMOD_MODE_TXONLY:
+                       mod &= ~S3C2412_IISMOD_MODE_MASK;
+                       mod |= S3C2412_IISMOD_MODE_TXRX;
+                       break;
+
+               default:
+                       dev_err(i2s->dev, "RXEN: Invalid MODE in IISMOD\n");
+               }
+
+               writel(mod, regs + S3C2412_IISMOD);
+               writel(con, regs + S3C2412_IISCON);
+       } else {
+               /* See txctrl notes on FIFOs. */
+
+               con &= ~S3C2412_IISCON_RXDMA_ACTIVE;
+               con |=  S3C2412_IISCON_RXDMA_PAUSE;
+               con |=  S3C2412_IISCON_RXCH_PAUSE;
+
+               switch (mod & S3C2412_IISMOD_MODE_MASK) {
+               case S3C2412_IISMOD_MODE_RXONLY:
+                       con &= ~S3C2412_IISCON_IIS_ACTIVE;
+                       mod &= ~S3C2412_IISMOD_MODE_MASK;
+                       break;
+
+               case S3C2412_IISMOD_MODE_TXRX:
+                       mod &= ~S3C2412_IISMOD_MODE_MASK;
+                       mod |= S3C2412_IISMOD_MODE_TXONLY;
+                       break;
+
+               default:
+                       dev_err(i2s->dev, "RXEN: Invalid MODE in IISMOD\n");
+               }
+
+               writel(con, regs + S3C2412_IISCON);
+               writel(mod, regs + S3C2412_IISMOD);
+       }
+
+       fic = readl(regs + S3C2412_IISFIC);
+       DBG("%s: IIS: CON=%x MOD=%x FIC=%x\n", __func__, con, mod, fic);
+}
+
+
+/*
+ * Wait for the LR signal to allow synchronisation to the L/R clock
+ * from the codec. May only be needed for slave mode.
+ */
+static int s3c2412_snd_lrsync(void)
+{
+       u32 iiscon;
+       unsigned long timeout = jiffies + msecs_to_jiffies(5);
+
+       DBG("Entered %s\n", __func__);
+
+       while (1) {
+               iiscon = readl(s3c2412_i2s.regs + S3C2412_IISCON);
+               if (iiscon & S3C2412_IISCON_LRINDEX)
+                       break;
+
+               if (timeout < jiffies) {
+                       printk(KERN_ERR "%s: timeout\n", __func__);
+                       return -ETIMEDOUT;
+               }
+       }
+
+       return 0;
+}
+
+/*
+ * Check whether CPU is the master or slave
+ */
+static inline int s3c2412_snd_is_clkmaster(void)
+{
+       u32 iismod = readl(s3c2412_i2s.regs + S3C2412_IISMOD);
+
+       DBG("Entered %s\n", __func__);
+
+       iismod &= S3C2412_IISMOD_MASTER_MASK;
+       return !(iismod == S3C2412_IISMOD_SLAVE);
+}
+
+/*
+ * Set S3C2412 I2S DAI format
+ */
+static int s3c2412_i2s_set_fmt(struct snd_soc_cpu_dai *cpu_dai,
+                              unsigned int fmt)
+{
+       u32 iismod;
+
+
+       DBG("Entered %s\n", __func__);
+
+       iismod = readl(s3c2412_i2s.regs + S3C2412_IISMOD);
+       DBG("hw_params r: IISMOD: %x \n", iismod);
+
+       switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) {
+       case SND_SOC_DAIFMT_CBM_CFM:
+               iismod &= ~S3C2412_IISMOD_MASTER_MASK;
+               iismod |= S3C2412_IISMOD_SLAVE;
+               break;
+       case SND_SOC_DAIFMT_CBS_CFS:
+               iismod &= ~S3C2412_IISMOD_MASTER_MASK;
+               iismod |= S3C2412_IISMOD_MASTER_INTERNAL;
+               break;
+       default:
+               DBG("unknwon master/slave format\n");
+               return -EINVAL;
+       }
+
+       iismod &= ~S3C2412_IISMOD_SDF_MASK;
+
+       switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
+       case SND_SOC_DAIFMT_RIGHT_J:
+               iismod |= S3C2412_IISMOD_SDF_MSB;
+               break;
+       case SND_SOC_DAIFMT_LEFT_J:
+               iismod |= S3C2412_IISMOD_SDF_LSB;
+               break;
+       case SND_SOC_DAIFMT_I2S:
+               iismod |= S3C2412_IISMOD_SDF_IIS;
+               break;
+       default:
+               DBG("Unknown data format\n");
+               return -EINVAL;
+       }
+
+       writel(iismod, s3c2412_i2s.regs + S3C2412_IISMOD);
+       DBG("hw_params w: IISMOD: %x \n", iismod);
+       return 0;
+}
+
+static int s3c2412_i2s_hw_params(struct snd_pcm_substream *substream,
+                                struct snd_pcm_hw_params *params)
+{
+       struct snd_soc_pcm_runtime *rtd = substream->private_data;
+       u32 iismod;
+
+       DBG("Entered %s\n", __func__);
+
+       if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
+               rtd->dai->cpu_dai->dma_data = &s3c2412_i2s_pcm_stereo_out;
+       else
+               rtd->dai->cpu_dai->dma_data = &s3c2412_i2s_pcm_stereo_in;
+
+       /* Working copies of register */
+       iismod = readl(s3c2412_i2s.regs + S3C2412_IISMOD);
+       DBG("%s: r: IISMOD: %x\n", __func__, iismod);
+
+       switch (params_format(params)) {
+       case SNDRV_PCM_FORMAT_S8:
+               iismod |= S3C2412_IISMOD_8BIT;
+               break;
+       case SNDRV_PCM_FORMAT_S16_LE:
+               iismod &= ~S3C2412_IISMOD_8BIT;
+               break;
+       }
+
+       writel(iismod, s3c2412_i2s.regs + S3C2412_IISMOD);
+       DBG("%s: w: IISMOD: %x\n", __func__, iismod);
+       return 0;
+}
+
+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;
+
+       DBG("Entered %s\n", __func__);
+
+       switch (cmd) {
+       case SNDRV_PCM_TRIGGER_START:
+               /* On start, ensure that the FIFOs are cleared and reset. */
+
+               writel(capture ? S3C2412_IISFIC_RXFLUSH : S3C2412_IISFIC_TXFLUSH,
+                      s3c2412_i2s.regs + S3C2412_IISFIC);
+
+               /* clear again, just in case */
+               writel(0x0, s3c2412_i2s.regs + S3C2412_IISFIC);
+
+       case SNDRV_PCM_TRIGGER_RESUME:
+       case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
+               if (!s3c2412_snd_is_clkmaster()) {
+                       ret = s3c2412_snd_lrsync();
+                       if (ret)
+                               goto exit_err;
+               }
+
+               local_irq_save(irqs);
+
+               if (capture)
+                       s3c2412_snd_rxctrl(1);
+               else
+                       s3c2412_snd_txctrl(1);
+
+               local_irq_restore(irqs);
+               break;
+
+       case SNDRV_PCM_TRIGGER_STOP:
+       case SNDRV_PCM_TRIGGER_SUSPEND:
+       case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
+               local_irq_save(irqs);
+
+               if (capture)
+                       s3c2412_snd_rxctrl(0);
+               else
+                       s3c2412_snd_txctrl(0);
+
+               local_irq_restore(irqs);
+               break;
+       default:
+               ret = -EINVAL;
+               break;
+       }
+
+exit_err:
+       return ret;
+}
+
+/* default table of all avaialable root fs divisors */
+static unsigned int s3c2412_iis_fs[] = { 256, 512, 384, 768, 0 };
+
+int s3c2412_iis_calc_rate(struct s3c2412_rate_calc *info,
+                         unsigned int *fstab,
+                         unsigned int rate, struct clk *clk)
+{
+       unsigned long clkrate = clk_get_rate(clk);
+       unsigned int div;
+       unsigned int fsclk;
+       unsigned int actual;
+       unsigned int fs;
+       unsigned int fsdiv;
+       signed int deviation = 0;
+       unsigned int best_fs = 0;
+       unsigned int best_div = 0;
+       unsigned int best_rate = 0;
+       unsigned int best_deviation = INT_MAX;
+
+
+       if (fstab == NULL)
+               fstab = s3c2412_iis_fs;
+
+       for (fs = 0;; fs++) {
+               fsdiv = s3c2412_iis_fs[fs];
+
+               if (fsdiv == 0)
+                       break;
+
+               fsclk = clkrate / fsdiv;
+               div = fsclk / rate;
+
+               if ((fsclk % rate) > (rate / 2))
+                       div++;
+
+               if (div <= 1)
+                       continue;
+
+               actual = clkrate / (fsdiv * div);
+               deviation = actual - rate;
+
+               printk(KERN_DEBUG "%dfs: div %d => result %d, deviation %d\n",
+                      fsdiv, div, actual, deviation);
+
+               deviation = abs(deviation);
+
+               if (deviation < best_deviation) {
+                       best_fs = fsdiv;
+                       best_div = div;
+                       best_rate = actual;
+                       best_deviation = deviation;
+               }
+
+               if (deviation == 0)
+                       break;
+       }
+
+       printk(KERN_DEBUG "best: fs=%d, div=%d, rate=%d\n",
+              best_fs, best_div, best_rate);
+
+       info->fs_div = best_fs;
+       info->clk_div = best_div;
+
+       return 0;
+}
+EXPORT_SYMBOL_GPL(s3c2412_iis_calc_rate);
+
+/*
+ * Set S3C2412 Clock source
+ */
+static int s3c2412_i2s_set_sysclk(struct snd_soc_cpu_dai *cpu_dai,
+                                 int clk_id, unsigned int freq, int dir)
+{
+       u32 iismod = readl(s3c2412_i2s.regs + S3C2412_IISMOD);
+
+       DBG("%s(%p, %d, %u, %d)\n", __func__, cpu_dai, clk_id,
+           freq, dir);
+
+       switch (clk_id) {
+       case S3C2412_CLKSRC_PCLK:
+               iismod &= ~S3C2412_IISMOD_MASTER_MASK;
+               iismod |= S3C2412_IISMOD_MASTER_INTERNAL;
+               break;
+       case S3C2412_CLKSRC_I2SCLK:
+               iismod &= ~S3C2412_IISMOD_MASTER_MASK;
+               iismod |= S3C2412_IISMOD_MASTER_EXTERNAL;
+               break;
+       default:
+               return -EINVAL;
+       }
+
+       writel(iismod, s3c2412_i2s.regs + S3C2412_IISMOD);
+       return 0;
+}
+
+/*
+ * Set S3C2412 Clock dividers
+ */
+static int s3c2412_i2s_set_clkdiv(struct snd_soc_cpu_dai *cpu_dai,
+                                 int div_id, int div)
+{
+       struct s3c2412_i2s_info *i2s = &s3c2412_i2s;
+       u32 reg;
+
+       DBG("%s(%p, %d, %d)\n", __func__, cpu_dai, div_id, div);
+
+       switch (div_id) {
+       case S3C2412_DIV_BCLK:
+               reg = readl(i2s->regs + S3C2412_IISMOD);
+               reg &= ~S3C2412_IISMOD_BCLK_MASK;
+               writel(reg | div, i2s->regs + S3C2412_IISMOD);
+
+               DBG("%s: MOD=%08x\n", __func__, readl(i2s->regs + S3C2412_IISMOD));
+               break;
+
+       case S3C2412_DIV_RCLK:
+               if (div > 3) {
+                       /* convert value to bit field */
+
+                       switch (div) {
+                       case 256:
+                               div = S3C2412_IISMOD_RCLK_256FS;
+                               break;
+
+                       case 384:
+                               div = S3C2412_IISMOD_RCLK_384FS;
+                               break;
+
+                       case 512:
+                               div = S3C2412_IISMOD_RCLK_512FS;
+                               break;
+
+                       case 768:
+                               div = S3C2412_IISMOD_RCLK_768FS;
+                               break;
+
+                       default:
+                               return -EINVAL;
+                       }
+               }
+
+               reg = readl(s3c2412_i2s.regs + S3C2412_IISMOD);
+               reg &= ~S3C2412_IISMOD_RCLK_MASK;
+               writel(reg | div, i2s->regs + S3C2412_IISMOD);
+               DBG("%s: MOD=%08x\n", __func__, readl(i2s->regs + S3C2412_IISMOD));
+               break;
+
+       case S3C2412_DIV_PRESCALER:
+               if (div >= 0) {
+                       writel((div << 8) | S3C2412_IISPSR_PSREN,
+                              i2s->regs + S3C2412_IISPSR);
+               } else {
+                       writel(0x0, i2s->regs + S3C2412_IISPSR);
+               }
+               DBG("%s: PSR=%08x\n", __func__, readl(i2s->regs + S3C2412_IISPSR));
+               break;
+
+       default:
+               return -EINVAL;
+       }
+
+       return 0;
+}
+
+struct clk *s3c2412_get_iisclk(void)
+{
+       return s3c2412_i2s.iis_clk;
+}
+EXPORT_SYMBOL_GPL(s3c2412_get_iisclk);
+
+
+static int s3c2412_i2s_probe(struct platform_device *pdev)
+{
+       DBG("Entered %s\n", __func__);
+
+       s3c2412_i2s.dev = &pdev->dev;
+
+       s3c2412_i2s.regs = ioremap(S3C2410_PA_IIS, 0x100);
+       if (s3c2412_i2s.regs == NULL)
+               return -ENXIO;
+
+       s3c2412_i2s.iis_pclk = clk_get(&pdev->dev, "iis");
+       if (s3c2412_i2s.iis_pclk == NULL) {
+               DBG("failed to get iis_clock\n");
+               iounmap(s3c2412_i2s.regs);
+               return -ENODEV;
+       }
+
+       s3c2412_i2s.iis_cclk = clk_get(&pdev->dev, "i2sclk");
+       if (s3c2412_i2s.iis_cclk == NULL) {
+               DBG("failed to get i2sclk clock\n");
+               iounmap(s3c2412_i2s.regs);
+               return -ENODEV;
+       }
+
+       clk_set_parent(s3c2412_i2s.iis_cclk, clk_get(NULL, "mpll"));
+
+       clk_enable(s3c2412_i2s.iis_pclk);
+       clk_enable(s3c2412_i2s.iis_cclk);
+
+       s3c2412_i2s.iis_clk = s3c2412_i2s.iis_pclk;
+
+       /* Configure the I2S pins in correct mode */
+       s3c2410_gpio_cfgpin(S3C2410_GPE0, S3C2410_GPE0_I2SLRCK);
+       s3c2410_gpio_cfgpin(S3C2410_GPE1, S3C2410_GPE1_I2SSCLK);
+       s3c2410_gpio_cfgpin(S3C2410_GPE2, S3C2410_GPE2_CDCLK);
+       s3c2410_gpio_cfgpin(S3C2410_GPE3, S3C2410_GPE3_I2SSDI);
+       s3c2410_gpio_cfgpin(S3C2410_GPE4, S3C2410_GPE4_I2SSDO);
+
+       s3c2412_snd_txctrl(0);
+       s3c2412_snd_rxctrl(0);
+
+       return 0;
+}
+
+#ifdef CONFIG_PM
+static int s3c2412_i2s_suspend(struct platform_device *dev,
+                             struct snd_soc_cpu_dai *dai)
+{
+       struct s3c2412_i2s_info *i2s = &s3c2412_i2s;
+       u32 iismod;
+
+       if (dai->active) {
+               i2s->suspend_iismod = readl(i2s->regs + S3C2412_IISMOD);
+               i2s->suspend_iiscon = readl(i2s->regs + S3C2412_IISCON);
+               i2s->suspend_iispsr = readl(i2s->regs + S3C2412_IISPSR);
+
+               /* some basic suspend checks */
+
+               iismod = readl(i2s->regs + S3C2412_IISMOD);
+
+               if (iismod & S3C2412_IISCON_RXDMA_ACTIVE)
+                       dev_warn(&dev->dev, "%s: RXDMA active?\n", __func__);
+
+               if (iismod & S3C2412_IISCON_TXDMA_ACTIVE)
+                       dev_warn(&dev->dev, "%s: TXDMA active?\n", __func__);
+
+               if (iismod & S3C2412_IISCON_IIS_ACTIVE)
+                       dev_warn(&dev->dev, "%s: IIS active\n", __func__);
+       }
+
+       return 0;
+}
+
+static int s3c2412_i2s_resume(struct platform_device *pdev,
+                             struct snd_soc_cpu_dai *dai)
+{
+       struct s3c2412_i2s_info *i2s = &s3c2412_i2s;
+
+       dev_info(&pdev->dev, "dai_active %d, IISMOD %08x, IISCON %08x\n",
+                dai->active, i2s->suspend_iismod, i2s->suspend_iiscon);
+
+       if (dai->active) {
+               writel(i2s->suspend_iiscon, i2s->regs + S3C2412_IISCON);
+               writel(i2s->suspend_iismod, i2s->regs + S3C2412_IISMOD);
+               writel(i2s->suspend_iispsr, i2s->regs + S3C2412_IISPSR);
+
+               writel(S3C2412_IISFIC_RXFLUSH | S3C2412_IISFIC_TXFLUSH,
+                      i2s->regs + S3C2412_IISFIC);
+
+               ndelay(250);
+               writel(0x0, i2s->regs + S3C2412_IISFIC);
+
+       }
+
+       return 0;
+}
+#else
+#define s3c2412_i2s_suspend NULL
+#define s3c2412_i2s_resume  NULL
+#endif /* CONFIG_PM */
+
+#define S3C2412_I2S_RATES \
+       (SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_11025 | SNDRV_PCM_RATE_16000 | \
+       SNDRV_PCM_RATE_22050 | SNDRV_PCM_RATE_32000 | SNDRV_PCM_RATE_44100 | \
+       SNDRV_PCM_RATE_48000 | SNDRV_PCM_RATE_88200 | SNDRV_PCM_RATE_96000)
+
+struct snd_soc_cpu_dai s3c2412_i2s_dai = {
+       .name   = "s3c2412-i2s",
+       .id     = 0,
+       .type   = SND_SOC_DAI_I2S,
+       .probe  = s3c2412_i2s_probe,
+       .suspend = s3c2412_i2s_suspend,
+       .resume = s3c2412_i2s_resume,
+       .playback = {
+               .channels_min   = 2,
+               .channels_max   = 2,
+               .rates          = S3C2412_I2S_RATES,
+               .formats        = SNDRV_PCM_FMTBIT_S8 | SNDRV_PCM_FMTBIT_S16_LE,
+       },
+       .capture = {
+               .channels_min   = 2,
+               .channels_max   = 2,
+               .rates          = S3C2412_I2S_RATES,
+               .formats        = SNDRV_PCM_FMTBIT_S8 | SNDRV_PCM_FMTBIT_S16_LE,
+       },
+       .ops = {
+               .trigger        = s3c2412_i2s_trigger,
+               .hw_params      = s3c2412_i2s_hw_params,
+       },
+       .dai_ops = {
+               .set_fmt        = s3c2412_i2s_set_fmt,
+               .set_clkdiv     = s3c2412_i2s_set_clkdiv,
+               .set_sysclk     = s3c2412_i2s_set_sysclk,
+       },
+};
+EXPORT_SYMBOL_GPL(s3c2412_i2s_dai);
+
+/* Module information */
+MODULE_AUTHOR("Ben Dooks, <ben@simtec.co.uk>");
+MODULE_DESCRIPTION("S3C2412 I2S SoC Interface");
+MODULE_LICENSE("GPL");
diff --git a/sound/soc/s3c24xx/s3c2412-i2s.h b/sound/soc/s3c24xx/s3c2412-i2s.h
new file mode 100644 (file)
index 0000000..27f48e1
--- /dev/null
@@ -0,0 +1,38 @@
+/* sound/soc/s3c24xx/s3c2412-i2s.c
+ *
+ * ALSA Soc Audio Layer - S3C2412 I2S driver
+ *
+ * Copyright (c) 2007 Simtec Electronics
+ *     http://armlinux.simtec.co.uk/
+ *     Ben Dooks <ben@simtec.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.
+*/
+
+#ifndef __SND_SOC_S3C24XX_S3C2412_I2S_H
+#define __SND_SOC_S3C24XX_S3C2412_I2S_H __FILE__
+
+#define S3C2412_DIV_BCLK       (1)
+#define S3C2412_DIV_RCLK       (2)
+#define S3C2412_DIV_PRESCALER  (3)
+
+#define S3C2412_CLKSRC_PCLK    (0)
+#define S3C2412_CLKSRC_I2SCLK  (1)
+
+extern struct clk *s3c2412_get_iisclk(void);
+
+extern struct snd_soc_cpu_dai s3c2412_i2s_dai;
+
+struct s3c2412_rate_calc {
+       unsigned int    clk_div;        /* for prescaler */
+       unsigned int    fs_div;         /* for root frame clock */
+};
+
+extern int s3c2412_iis_calc_rate(struct s3c2412_rate_calc *info,
+                                unsigned int *fstab,
+                                unsigned int rate, struct clk *clk);
+
+#endif /* __SND_SOC_S3C24XX_S3C2412_I2S_H */
index 758a2637e7ac408d7eb46ba4bf16f051e9ea9e00..1c1ddbf7f3c01e61605fc5f1ee29856f3a215349 100644 (file)
@@ -23,7 +23,6 @@
 #include <linux/delay.h>
 #include <linux/clk.h>
 
-#include <sound/driver.h>
 #include <sound/core.h>
 #include <sound/pcm.h>
 #include <sound/ac97_codec.h>
@@ -253,7 +252,7 @@ static int s3c2443_ac97_probe(struct platform_device *pdev)
        ac_glbctrl |= S3C_AC97_GLBCTRL_TRANSFERDATAENABLE;
        writel(ac_glbctrl, s3c24xx_ac97.regs + S3C_AC97_GLBCTRL);
 
-       ret = request_irq(IRQ_S3C2443_AC97, s3c2443_ac97_irq,
+       ret = request_irq(IRQ_S3C244x_AC97, s3c2443_ac97_irq,
                IRQF_DISABLED, "AC97", NULL);
        if (ret < 0) {
                printk(KERN_ERR "s3c24xx-ac97: interrupt request failed.\n");
@@ -266,7 +265,7 @@ static int s3c2443_ac97_probe(struct platform_device *pdev)
 
 static void s3c2443_ac97_remove(struct platform_device *pdev)
 {
-       free_irq(IRQ_S3C2443_AC97, NULL);
+       free_irq(IRQ_S3C244x_AC97, NULL);
        clk_disable(s3c24xx_ac97.ac97_clk);
        clk_put(s3c24xx_ac97.ac97_clk);
        iounmap(s3c24xx_ac97.regs);
index 2b835e8260fae11f6a9906dff28dad37dede9059..bf03e8ed16c30a8b22b4cf5d37e151b12ffc6a00 100644 (file)
 #define AC_CMD_ADDR(x) (x << 16)
 #define AC_CMD_DATA(x) (x & 0xffff)
 
+#ifdef CONFIG_CPU_S3C2440
+#define IRQ_S3C244x_AC97 IRQ_S3C2440_AC97
+#else
+#define IRQ_S3C244x_AC97 IRQ_S3C2443_AC97
+#endif
+
 extern struct snd_soc_cpu_dai s3c2443_ac97_dai[];
 
 #endif /*S3C24XXAC97_H_*/
index cd89c4105fcdc206615bf670db3445142d1fd3ff..0a3c630951bee11833fd349195f6a90e0cc8951b 100644 (file)
@@ -24,7 +24,7 @@
 #include <linux/device.h>
 #include <linux/delay.h>
 #include <linux/clk.h>
-#include <sound/driver.h>
+#include <linux/jiffies.h>
 #include <sound/core.h>
 #include <sound/pcm.h>
 #include <sound/pcm_params.h>
 
 #include <asm/hardware.h>
 #include <asm/io.h>
-#include <asm/arch/regs-iis.h>
 #include <asm/arch/regs-gpio.h>
 #include <asm/arch/regs-clock.h>
 #include <asm/arch/audio.h>
 #include <asm/dma.h>
 #include <asm/arch/dma.h>
 
+#include <asm/plat-s3c24xx/regs-iis.h>
+
 #include "s3c24xx-pcm.h"
 #include "s3c24xx-i2s.h"
 
@@ -75,6 +76,10 @@ static struct s3c24xx_pcm_dma_params s3c24xx_i2s_pcm_stereo_in = {
 struct s3c24xx_i2s_info {
        void __iomem    *regs;
        struct clk      *iis_clk;
+       u32             iiscon;
+       u32             iismod;
+       u32             iisfcon;
+       u32             iispsr;
 };
 static struct s3c24xx_i2s_info s3c24xx_i2s;
 
@@ -184,7 +189,7 @@ static int s3c24xx_snd_lrsync(void)
                if (iiscon & S3C2410_IISCON_LRINDEX)
                        break;
 
-               if (timeout < jiffies)
+               if (time_after(jiffies, timeout))
                        return -ETIMEDOUT;
        }
 
@@ -405,6 +410,38 @@ static int s3c24xx_i2s_probe(struct platform_device *pdev)
        return 0;
 }
 
+#ifdef CONFIG_PM
+int s3c24xx_i2s_suspend(struct platform_device *pdev,
+               struct snd_soc_cpu_dai *cpu_dai)
+{
+       s3c24xx_i2s.iiscon = readl(s3c24xx_i2s.regs + S3C2410_IISCON);
+       s3c24xx_i2s.iismod = readl(s3c24xx_i2s.regs + S3C2410_IISMOD);
+       s3c24xx_i2s.iisfcon = readl(s3c24xx_i2s.regs + S3C2410_IISFCON);
+       s3c24xx_i2s.iispsr = readl(s3c24xx_i2s.regs + S3C2410_IISPSR);
+
+       clk_disable(s3c24xx_i2s.iis_clk);
+
+       return 0;
+}
+
+int s3c24xx_i2s_resume(struct platform_device *pdev,
+               struct snd_soc_cpu_dai *cpu_dai)
+{
+       clk_enable(s3c24xx_i2s.iis_clk);
+
+       writel(s3c24xx_i2s.iiscon, s3c24xx_i2s.regs + S3C2410_IISCON);
+       writel(s3c24xx_i2s.iismod, s3c24xx_i2s.regs + S3C2410_IISMOD);
+       writel(s3c24xx_i2s.iisfcon, s3c24xx_i2s.regs + S3C2410_IISFCON);
+       writel(s3c24xx_i2s.iispsr, s3c24xx_i2s.regs + S3C2410_IISPSR);
+
+       return 0;
+}
+#else
+#define s3c24xx_i2s_suspend NULL
+#define s3c24xx_i2s_resume NULL
+#endif
+
+
 #define S3C24XX_I2S_RATES \
        (SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_11025 | SNDRV_PCM_RATE_16000 | \
        SNDRV_PCM_RATE_22050 | SNDRV_PCM_RATE_32000 | SNDRV_PCM_RATE_44100 | \
@@ -415,6 +452,8 @@ struct snd_soc_cpu_dai s3c24xx_i2s_dai = {
        .id = 0,
        .type = SND_SOC_DAI_I2S,
        .probe = s3c24xx_i2s_probe,
+       .suspend = s3c24xx_i2s_suspend,
+       .resume = s3c24xx_i2s_resume,
        .playback = {
                .channels_min = 2,
                .channels_max = 2,
index 4107a87d4de3cb39fdcf7f8d51432e5a37361c93..29a6c82f873ac7c5f50def0470ef4d44e6fb5222 100644 (file)
@@ -24,7 +24,6 @@
 #include <linux/slab.h>
 #include <linux/dma-mapping.h>
 
-#include <sound/driver.h>
 #include <sound/core.h>
 #include <sound/pcm.h>
 #include <sound/pcm_params.h>
@@ -49,7 +48,9 @@ static const struct snd_pcm_hardware s3c24xx_pcm_hardware = {
        .info                   = SNDRV_PCM_INFO_INTERLEAVED |
                                    SNDRV_PCM_INFO_BLOCK_TRANSFER |
                                    SNDRV_PCM_INFO_MMAP |
-                                   SNDRV_PCM_INFO_MMAP_VALID,
+                                   SNDRV_PCM_INFO_MMAP_VALID |
+                                   SNDRV_PCM_INFO_PAUSE |
+                                   SNDRV_PCM_INFO_RESUME,
        .formats                = SNDRV_PCM_FMTBIT_S16_LE |
                                    SNDRV_PCM_FMTBIT_U16_LE |
                                    SNDRV_PCM_FMTBIT_U8 |
@@ -176,28 +177,6 @@ static int s3c24xx_pcm_hw_params(struct snd_pcm_substream *substream,
                }
        }
 
-       /* channel needs configuring for mem=>device, increment memory addr,
-        * sync to pclk, half-word transfers to the IIS-FIFO. */
-       if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
-               s3c2410_dma_devconfig(prtd->params->channel,
-                               S3C2410_DMASRC_MEM, S3C2410_DISRCC_INC |
-                               S3C2410_DISRCC_APB, prtd->params->dma_addr);
-
-               s3c2410_dma_config(prtd->params->channel,
-                               prtd->params->dma_size,
-                               S3C2410_DCON_SYNC_PCLK | 
-                               S3C2410_DCON_HANDSHAKE);
-       } else {
-               s3c2410_dma_config(prtd->params->channel,
-                               prtd->params->dma_size,
-                               S3C2410_DCON_HANDSHAKE | 
-                               S3C2410_DCON_SYNC_PCLK);
-
-               s3c2410_dma_devconfig(prtd->params->channel,
-                                       S3C2410_DMASRC_HW, 0x3,
-                                       prtd->params->dma_addr);
-       }
-
        s3c2410_dma_set_buffdone_fn(prtd->params->channel,
                                    s3c24xx_audio_buffdone);
 
@@ -246,6 +225,28 @@ static int s3c24xx_pcm_prepare(struct snd_pcm_substream *substream)
        if (!prtd->params)
                return 0;
 
+       /* channel needs configuring for mem=>device, increment memory addr,
+        * sync to pclk, half-word transfers to the IIS-FIFO. */
+       if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
+               s3c2410_dma_devconfig(prtd->params->channel,
+                               S3C2410_DMASRC_MEM, S3C2410_DISRCC_INC |
+                               S3C2410_DISRCC_APB, prtd->params->dma_addr);
+
+               s3c2410_dma_config(prtd->params->channel,
+                               prtd->params->dma_size,
+                               S3C2410_DCON_SYNC_PCLK |
+                               S3C2410_DCON_HANDSHAKE);
+       } else {
+               s3c2410_dma_config(prtd->params->channel,
+                               prtd->params->dma_size,
+                               S3C2410_DCON_HANDSHAKE |
+                               S3C2410_DCON_SYNC_PCLK);
+
+               s3c2410_dma_devconfig(prtd->params->channel,
+                                       S3C2410_DMASRC_HW, 0x3,
+                                       prtd->params->dma_addr);
+       }
+
        /* flush the DMA channel */
        s3c2410_dma_ctrl(prtd->params->channel, S3C2410_DMAOP_FLUSH);
        prtd->dma_loaded = 0;
index d46cd811ceb3e627f003a4050b2cd6333487d2c3..b4a56302b9ab091203427333c2174c35d4341ca2 100644 (file)
@@ -17,7 +17,6 @@
 
 #include <linux/module.h>
 #include <linux/device.h>
-#include <sound/driver.h>
 #include <sound/core.h>
 #include <sound/pcm.h>
 #include <sound/soc.h>
index cdee374b843efd9a4d7258f0c2e2b6c3408a7024..7a3ce80d6727dbe2daea6fa691ef0daff4974481 100644 (file)
@@ -16,7 +16,6 @@
 #include <linux/init.h>
 #include <linux/platform_device.h>
 #include <linux/dma-mapping.h>
-#include <sound/driver.h>
 #include <sound/core.h>
 #include <sound/pcm.h>
 #include <sound/pcm_params.h>
index 8e3f03908cdbe6bc4b9d9fac4d58605ad1e3b9ae..b7b676b3d6715c38fd6196ce10162d245daca36d 100644 (file)
@@ -21,7 +21,6 @@
 #include <linux/interrupt.h>
 #include <linux/wait.h>
 #include <linux/delay.h>
-#include <sound/driver.h>
 #include <sound/core.h>
 #include <sound/pcm.h>
 #include <sound/ac97_codec.h>
@@ -105,7 +104,7 @@ static int hac_get_codec_data(struct hac_priv *hac, unsigned short r,
        unsigned int to1, to2, i;
        unsigned short adr;
 
-       for (i = 0; i < AC97_READ_RETRY; ++i) {
+       for (i = AC97_READ_RETRY; i; i--) {
                *v = 0;
                /* wait for HAC to receive something from the codec */
                for (to1 = TMO_E4;
@@ -132,7 +131,7 @@ static int hac_get_codec_data(struct hac_priv *hac, unsigned short r,
                udelay(21);
        }
        HACREG(HACRSR) &= ~(RSR_STDRY | RSR_STARY);
-       return (i < AC97_READ_RETRY);
+       return i;
 }
 
 static unsigned short hac_read_codec_aux(struct hac_priv *hac,
@@ -141,7 +140,7 @@ static unsigned short hac_read_codec_aux(struct hac_priv *hac,
        unsigned short val;
        unsigned int i, to;
 
-       for (i = 0; i < AC97_READ_RETRY; i++) {
+       for (i = AC97_READ_RETRY; i; i--) {
                /* send_read_request */
                local_irq_disable();
                HACREG(HACTSR) &= ~(TSR_CMDAMT);
@@ -159,10 +158,7 @@ static unsigned short hac_read_codec_aux(struct hac_priv *hac,
                        break;
        }
 
-       if (i == AC97_READ_RETRY)
-               return ~0;
-
-       return val;
+       return i ? val : ~0;
 }
 
 static void hac_ac97_write(struct snd_ac97 *ac97, unsigned short reg,
@@ -172,7 +168,7 @@ static void hac_ac97_write(struct snd_ac97 *ac97, unsigned short reg,
        struct hac_priv *hac = &hac_cpu_data[unit_id];
        unsigned int i, to;
        /* write_codec_aux */
-       for (i = 0; i < AC97_WRITE_RETRY; i++) {
+       for (i = AC97_WRITE_RETRY; i; i--) {
                /* send_write_request */
                local_irq_disable();
                HACREG(HACTSR) &= ~(TSR_CMDDMT | TSR_CMDAMT);
index 5563f14511fa4ed123c6a979d3c11fb344efcb33..2f91de84c5c762a2896c14230140c90b216d39a9 100644 (file)
@@ -9,7 +9,6 @@
 #include <linux/module.h>
 #include <linux/moduleparam.h>
 #include <linux/platform_device.h>
-#include <sound/driver.h>
 #include <sound/core.h>
 #include <sound/pcm.h>
 #include <sound/soc.h>
index b72bc316cb8ec10e7b7cc8886ef44aa6602ccd75..3388bc3d62d10fbef92a56c148ea4259ae2a6654 100644 (file)
@@ -30,7 +30,6 @@
 #include <linux/init.h>
 #include <linux/module.h>
 #include <linux/platform_device.h>
-#include <sound/driver.h>
 #include <sound/core.h>
 #include <sound/pcm.h>
 #include <sound/initval.h>
index e6a67b58f296ec6269fc7c1ea833a5a1728bf7eb..9eb5479787c1d32203b17c1a037750a961c11f17 100644 (file)
@@ -32,7 +32,6 @@
 #include <linux/pm.h>
 #include <linux/bitops.h>
 #include <linux/platform_device.h>
-#include <sound/driver.h>
 #include <sound/core.h>
 #include <sound/pcm.h>
 #include <sound/pcm_params.h>
@@ -288,16 +287,25 @@ static void close_delayed_work(struct work_struct *work)
                /* are we waiting on this codec DAI stream */
                if (codec_dai->pop_wait == 1) {
 
+                       /* power down the codec to D1 if no longer active */
+                       if (codec->active == 0) {
+                               dbg("pop wq D1 %s %s\n", codec->name,
+                                       codec_dai->playback.stream_name);
+                               snd_soc_dapm_device_event(socdev,
+                                       SNDRV_CTL_POWER_D1);
+                       }
+
                        codec_dai->pop_wait = 0;
-                       snd_soc_dapm_stream_event(codec, codec_dai->playback.stream_name,
+                       snd_soc_dapm_stream_event(codec,
+                               codec_dai->playback.stream_name,
                                SND_SOC_DAPM_STREAM_STOP);
 
                        /* power down the codec power domain if no longer active */
                        if (codec->active == 0) {
                                dbg("pop wq D3 %s %s\n", codec->name,
                                        codec_dai->playback.stream_name);
-                               if (codec->dapm_event)
-                                       codec->dapm_event(codec, SNDRV_CTL_POWER_D3hot);
+                               snd_soc_dapm_device_event(socdev,
+                                       SNDRV_CTL_POWER_D3hot);
                        }
                }
        }
@@ -353,12 +361,12 @@ static int soc_codec_close(struct snd_pcm_substream *substream)
        } else {
                /* capture streams can be powered down now */
                snd_soc_dapm_stream_event(codec,
-                       codec_dai->capture.stream_name, SND_SOC_DAPM_STREAM_STOP);
+                       codec_dai->capture.stream_name,
+                       SND_SOC_DAPM_STREAM_STOP);
 
-               if (codec->active == 0 && codec_dai->pop_wait == 0){
-                       if (codec->dapm_event)
-                               codec->dapm_event(codec, SNDRV_CTL_POWER_D3hot);
-               }
+               if (codec->active == 0 && codec_dai->pop_wait == 0)
+                       snd_soc_dapm_device_event(socdev,
+                                               SNDRV_CTL_POWER_D3hot);
        }
 
        mutex_unlock(&pcm_mutex);
@@ -433,8 +441,7 @@ static int soc_pcm_prepare(struct snd_pcm_substream *substream)
                /* no delayed work - do we need to power up codec */
                if (codec->dapm_state != SNDRV_CTL_POWER_D0) {
 
-                       if (codec->dapm_event)
-                               codec->dapm_event(codec, SNDRV_CTL_POWER_D1);
+                       snd_soc_dapm_device_event(socdev,  SNDRV_CTL_POWER_D1);
 
                        if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
                                snd_soc_dapm_stream_event(codec,
@@ -445,8 +452,7 @@ static int soc_pcm_prepare(struct snd_pcm_substream *substream)
                                        codec_dai->capture.stream_name,
                                        SND_SOC_DAPM_STREAM_START);
 
-                       if (codec->dapm_event)
-                               codec->dapm_event(codec, SNDRV_CTL_POWER_D0);
+                       snd_soc_dapm_device_event(socdev, SNDRV_CTL_POWER_D0);
                        if (codec_dai->dai_ops.digital_mute)
                                codec_dai->dai_ops.digital_mute(codec_dai, 0);
 
@@ -639,6 +645,10 @@ static int soc_suspend(struct platform_device *pdev, pm_message_t state)
                        dai->dai_ops.digital_mute(dai, 1);
        }
 
+       /* suspend all pcms */
+       for (i = 0; i < machine->num_links; i++)
+               snd_pcm_suspend_all(machine->dai_link[i].pcm);
+
        if (machine->suspend_pre)
                machine->suspend_pre(pdev, state);
 
@@ -873,6 +883,7 @@ static int soc_new_pcm(struct snd_soc_device *socdev,
                return ret;
        }
 
+       dai_link->pcm = pcm;
        pcm->private_data = rtd;
        soc_pcm_ops.mmap = socdev->platform->pcm_ops->mmap;
        soc_pcm_ops.pointer = socdev->platform->pcm_ops->pointer;
@@ -1090,7 +1101,6 @@ int snd_soc_register_card(struct snd_soc_device *socdev)
        struct snd_soc_machine *machine = socdev->machine;
        int ret = 0, i, ac97 = 0, err = 0;
 
-       mutex_lock(&codec->mutex);
        for(i = 0; i < machine->num_links; i++) {
                if (socdev->machine->dai_link[i].init) {
                        err = socdev->machine->dai_link[i].init(codec);
@@ -1116,12 +1126,14 @@ int snd_soc_register_card(struct snd_soc_device *socdev)
                goto out;
        }
 
+       mutex_lock(&codec->mutex);
 #ifdef CONFIG_SND_SOC_AC97_BUS
        if (ac97) {
                ret = soc_ac97_dev_register(codec);
                if (ret < 0) {
                        printk(KERN_ERR "asoc: AC97 device register failed\n");
                        snd_card_free(codec->card);
+                       mutex_unlock(&codec->mutex);
                        goto out;
                }
        }
@@ -1134,8 +1146,10 @@ int snd_soc_register_card(struct snd_soc_device *socdev)
        err = device_create_file(socdev->dev, &dev_attr_codec_reg);
        if (err < 0)
                printk(KERN_WARNING "asoc: failed to add codec sysfs entries\n");
-out:
+
        mutex_unlock(&codec->mutex);
+
+out:
        return ret;
 }
 EXPORT_SYMBOL_GPL(snd_soc_register_card);
@@ -1215,7 +1229,6 @@ struct snd_kcontrol *snd_soc_cnew(const struct snd_kcontrol_new *_template,
        memcpy(&template, _template, sizeof(template));
        if (long_name)
                template.name = long_name;
-       template.access = SNDRV_CTL_ELEM_ACCESS_READWRITE;
        template.index = 0;
 
        return snd_ctl_new1(&template, data);
@@ -1350,13 +1363,16 @@ EXPORT_SYMBOL_GPL(snd_soc_info_enum_ext);
 int snd_soc_info_volsw_ext(struct snd_kcontrol *kcontrol,
        struct snd_ctl_elem_info *uinfo)
 {
-       int mask = kcontrol->private_value;
+       int max = kcontrol->private_value;
+
+       if (max == 1)
+               uinfo->type = SNDRV_CTL_ELEM_TYPE_BOOLEAN;
+       else
+               uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
 
-       uinfo->type =
-               mask == 1 ? SNDRV_CTL_ELEM_TYPE_BOOLEAN : SNDRV_CTL_ELEM_TYPE_INTEGER;
        uinfo->count = 1;
        uinfo->value.integer.min = 0;
-       uinfo->value.integer.max = mask;
+       uinfo->value.integer.max = max;
        return 0;
 }
 EXPORT_SYMBOL_GPL(snd_soc_info_volsw_ext);
@@ -1373,15 +1389,18 @@ EXPORT_SYMBOL_GPL(snd_soc_info_volsw_ext);
 int snd_soc_info_volsw(struct snd_kcontrol *kcontrol,
        struct snd_ctl_elem_info *uinfo)
 {
-       int mask = (kcontrol->private_value >> 16) & 0xff;
+       int max = (kcontrol->private_value >> 16) & 0xff;
        int shift = (kcontrol->private_value >> 8) & 0x0f;
        int rshift = (kcontrol->private_value >> 12) & 0x0f;
 
-       uinfo->type =
-               mask == 1 ? SNDRV_CTL_ELEM_TYPE_BOOLEAN : SNDRV_CTL_ELEM_TYPE_INTEGER;
+       if (max == 1)
+               uinfo->type = SNDRV_CTL_ELEM_TYPE_BOOLEAN;
+       else
+               uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
+
        uinfo->count = shift == rshift ? 1 : 2;
        uinfo->value.integer.min = 0;
-       uinfo->value.integer.max = mask;
+       uinfo->value.integer.max = max;
        return 0;
 }
 EXPORT_SYMBOL_GPL(snd_soc_info_volsw);
@@ -1402,7 +1421,8 @@ int snd_soc_get_volsw(struct snd_kcontrol *kcontrol,
        int reg = kcontrol->private_value & 0xff;
        int shift = (kcontrol->private_value >> 8) & 0x0f;
        int rshift = (kcontrol->private_value >> 12) & 0x0f;
-       int mask = (kcontrol->private_value >> 16) & 0xff;
+       int max = (kcontrol->private_value >> 16) & 0xff;
+       int mask = (1 << fls(max)) - 1;
        int invert = (kcontrol->private_value >> 24) & 0x01;
 
        ucontrol->value.integer.value[0] =
@@ -1412,10 +1432,10 @@ int snd_soc_get_volsw(struct snd_kcontrol *kcontrol,
                        (snd_soc_read(codec, reg) >> rshift) & mask;
        if (invert) {
                ucontrol->value.integer.value[0] =
-                       mask - ucontrol->value.integer.value[0];
+                       max - ucontrol->value.integer.value[0];
                if (shift != rshift)
                        ucontrol->value.integer.value[1] =
-                               mask - ucontrol->value.integer.value[1];
+                               max - ucontrol->value.integer.value[1];
        }
 
        return 0;
@@ -1438,25 +1458,24 @@ int snd_soc_put_volsw(struct snd_kcontrol *kcontrol,
        int reg = kcontrol->private_value & 0xff;
        int shift = (kcontrol->private_value >> 8) & 0x0f;
        int rshift = (kcontrol->private_value >> 12) & 0x0f;
-       int mask = (kcontrol->private_value >> 16) & 0xff;
+       int max = (kcontrol->private_value >> 16) & 0xff;
+       int mask = (1 << fls(max)) - 1;
        int invert = (kcontrol->private_value >> 24) & 0x01;
-       int err;
        unsigned short val, val2, val_mask;
 
        val = (ucontrol->value.integer.value[0] & mask);
        if (invert)
-               val = mask - val;
+               val = max - val;
        val_mask = mask << shift;
        val = val << shift;
        if (shift != rshift) {
                val2 = (ucontrol->value.integer.value[1] & mask);
                if (invert)
-                       val2 = mask - val2;
+                       val2 = max - val2;
                val_mask |= mask << rshift;
                val |= val2 << rshift;
        }
-       err = snd_soc_update_bits(codec, reg, val_mask, val);
-       return err;
+       return snd_soc_update_bits(codec, reg, val_mask, val);
 }
 EXPORT_SYMBOL_GPL(snd_soc_put_volsw);
 
@@ -1473,13 +1492,16 @@ EXPORT_SYMBOL_GPL(snd_soc_put_volsw);
 int snd_soc_info_volsw_2r(struct snd_kcontrol *kcontrol,
        struct snd_ctl_elem_info *uinfo)
 {
-       int mask = (kcontrol->private_value >> 12) & 0xff;
+       int max = (kcontrol->private_value >> 12) & 0xff;
+
+       if (max == 1)
+               uinfo->type = SNDRV_CTL_ELEM_TYPE_BOOLEAN;
+       else
+               uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
 
-       uinfo->type =
-               mask == 1 ? SNDRV_CTL_ELEM_TYPE_BOOLEAN : SNDRV_CTL_ELEM_TYPE_INTEGER;
        uinfo->count = 2;
        uinfo->value.integer.min = 0;
-       uinfo->value.integer.max = mask;
+       uinfo->value.integer.max = max;
        return 0;
 }
 EXPORT_SYMBOL_GPL(snd_soc_info_volsw_2r);
@@ -1500,7 +1522,8 @@ int snd_soc_get_volsw_2r(struct snd_kcontrol *kcontrol,
        int reg = kcontrol->private_value & 0xff;
        int reg2 = (kcontrol->private_value >> 24) & 0xff;
        int shift = (kcontrol->private_value >> 8) & 0x0f;
-       int mask = (kcontrol->private_value >> 12) & 0xff;
+       int max = (kcontrol->private_value >> 12) & 0xff;
+       int mask = (1<<fls(max))-1;
        int invert = (kcontrol->private_value >> 20) & 0x01;
 
        ucontrol->value.integer.value[0] =
@@ -1509,9 +1532,9 @@ int snd_soc_get_volsw_2r(struct snd_kcontrol *kcontrol,
                (snd_soc_read(codec, reg2) >> shift) & mask;
        if (invert) {
                ucontrol->value.integer.value[0] =
-                       mask - ucontrol->value.integer.value[0];
+                       max - ucontrol->value.integer.value[0];
                ucontrol->value.integer.value[1] =
-                       mask - ucontrol->value.integer.value[1];
+                       max - ucontrol->value.integer.value[1];
        }
 
        return 0;
@@ -1534,7 +1557,8 @@ int snd_soc_put_volsw_2r(struct snd_kcontrol *kcontrol,
        int reg = kcontrol->private_value & 0xff;
        int reg2 = (kcontrol->private_value >> 24) & 0xff;
        int shift = (kcontrol->private_value >> 8) & 0x0f;
-       int mask = (kcontrol->private_value >> 12) & 0xff;
+       int max = (kcontrol->private_value >> 12) & 0xff;
+       int mask = (1 << fls(max)) - 1;
        int invert = (kcontrol->private_value >> 20) & 0x01;
        int err;
        unsigned short val, val2, val_mask;
@@ -1544,8 +1568,8 @@ int snd_soc_put_volsw_2r(struct snd_kcontrol *kcontrol,
        val2 = (ucontrol->value.integer.value[1] & mask);
 
        if (invert) {
-               val = mask - val;
-               val2 = mask - val2;
+               val = max - val;
+               val2 = max - val2;
        }
 
        val = val << shift;
index 29a546fecacf28fead8e36648dcf18e53a8e4852..620d7ea3c15ff56a10aba1e9402c98aa2c6940c8 100644 (file)
@@ -43,7 +43,6 @@
 #include <linux/bitops.h>
 #include <linux/platform_device.h>
 #include <linux/jiffies.h>
-#include <sound/driver.h>
 #include <sound/core.h>
 #include <sound/pcm.h>
 #include <sound/pcm_params.h>
@@ -524,11 +523,13 @@ static int dapm_power_widgets(struct snd_soc_codec *codec, int event)
                                        continue;
 
                                if (event == SND_SOC_DAPM_STREAM_START) {
-                                       ret = w->event(w, SND_SOC_DAPM_PRE_PMU);
+                                       ret = w->event(w,
+                                               NULL, SND_SOC_DAPM_PRE_PMU);
                                        if (ret < 0)
                                                return ret;
                                } else if (event == SND_SOC_DAPM_STREAM_STOP) {
-                                       ret = w->event(w, SND_SOC_DAPM_PRE_PMD);
+                                       ret = w->event(w,
+                                               NULL, SND_SOC_DAPM_PRE_PMD);
                                        if (ret < 0)
                                                return ret;
                                }
@@ -539,11 +540,13 @@ static int dapm_power_widgets(struct snd_soc_codec *codec, int event)
                                        continue;
 
                                if (event == SND_SOC_DAPM_STREAM_START) {
-                                       ret = w->event(w, SND_SOC_DAPM_POST_PMU);
+                                       ret = w->event(w,
+                                               NULL, SND_SOC_DAPM_POST_PMU);
                                        if (ret < 0)
                                                return ret;
                                } else if (event == SND_SOC_DAPM_STREAM_STOP) {
-                                       ret = w->event(w, SND_SOC_DAPM_POST_PMD);
+                                       ret = w->event(w,
+                                               NULL, SND_SOC_DAPM_POST_PMD);
                                        if (ret < 0)
                                                return ret;
                                }
@@ -567,26 +570,30 @@ static int dapm_power_widgets(struct snd_soc_codec *codec, int event)
                                        if (power) {
                                                /* power up event */
                                                if (w->event_flags & SND_SOC_DAPM_PRE_PMU) {
-                                                       ret = w->event(w, SND_SOC_DAPM_PRE_PMU);
+                                                       ret = w->event(w,
+                                                               NULL, SND_SOC_DAPM_PRE_PMU);
                                                        if (ret < 0)
                                                                return ret;
                                                }
                                                dapm_update_bits(w);
                                                if (w->event_flags & SND_SOC_DAPM_POST_PMU){
-                                                       ret = w->event(w, SND_SOC_DAPM_POST_PMU);
+                                                       ret = w->event(w,
+                                                               NULL, SND_SOC_DAPM_POST_PMU);
                                                        if (ret < 0)
                                                                return ret;
                                                }
                                        } else {
                                                /* power down event */
                                                if (w->event_flags & SND_SOC_DAPM_PRE_PMD) {
-                                                       ret = w->event(w, SND_SOC_DAPM_PRE_PMD);
+                                                       ret = w->event(w,
+                                                               NULL, SND_SOC_DAPM_PRE_PMD);
                                                        if (ret < 0)
                                                                return ret;
                                                }
                                                dapm_update_bits(w);
                                                if (w->event_flags & SND_SOC_DAPM_POST_PMD) {
-                                                       ret = w->event(w, SND_SOC_DAPM_POST_PMD);
+                                                       ret = w->event(w,
+                                                               NULL, SND_SOC_DAPM_POST_PMD);
                                                        if (ret < 0)
                                                                return ret;
                                                }
@@ -692,7 +699,7 @@ static int dapm_mux_update_power(struct snd_soc_dapm_widget *widget,
        return 0;
 }
 
-/* test and update the power status of a mixer widget */
+/* test and update the power status of a mixer or switch widget */
 static int dapm_mixer_update_power(struct snd_soc_dapm_widget *widget,
                                   struct snd_kcontrol *kcontrol, int reg,
                                   int val_mask, int val, int invert)
@@ -700,7 +707,8 @@ static int dapm_mixer_update_power(struct snd_soc_dapm_widget *widget,
        struct snd_soc_dapm_path *path;
        int found = 0;
 
-       if (widget->id != snd_soc_dapm_mixer)
+       if (widget->id != snd_soc_dapm_mixer &&
+           widget->id != snd_soc_dapm_switch)
                return -ENODEV;
 
        if (!snd_soc_test_bits(widget->codec, reg, val_mask, val))
@@ -963,7 +971,6 @@ int snd_soc_dapm_new_widgets(struct snd_soc_codec *codec)
 {
        struct snd_soc_dapm_widget *w;
 
-       mutex_lock(&codec->mutex);
        list_for_each_entry(w, &codec->dapm_widgets, list)
        {
                if (w->new)
@@ -998,7 +1005,6 @@ int snd_soc_dapm_new_widgets(struct snd_soc_codec *codec)
        }
 
        dapm_power_widgets(codec, SND_SOC_DAPM_STREAM_NOP);
-       mutex_unlock(&codec->mutex);
        return 0;
 }
 EXPORT_SYMBOL_GPL(snd_soc_dapm_new_widgets);
@@ -1019,8 +1025,9 @@ int snd_soc_dapm_get_volsw(struct snd_kcontrol *kcontrol,
        int reg = kcontrol->private_value & 0xff;
        int shift = (kcontrol->private_value >> 8) & 0x0f;
        int rshift = (kcontrol->private_value >> 12) & 0x0f;
-       int mask = (kcontrol->private_value >> 16) & 0xff;
+       int max = (kcontrol->private_value >> 16) & 0xff;
        int invert = (kcontrol->private_value >> 24) & 0x01;
+       int mask = (1 << fls(max)) - 1;
 
        /* return the saved value if we are powered down */
        if (widget->id == snd_soc_dapm_pga && !widget->power) {
@@ -1035,10 +1042,10 @@ int snd_soc_dapm_get_volsw(struct snd_kcontrol *kcontrol,
                        (snd_soc_read(widget->codec, reg) >> rshift) & mask;
        if (invert) {
                ucontrol->value.integer.value[0] =
-                       mask - ucontrol->value.integer.value[0];
+                       max - ucontrol->value.integer.value[0];
                if (shift != rshift)
                        ucontrol->value.integer.value[1] =
-                               mask - ucontrol->value.integer.value[1];
+                               max - ucontrol->value.integer.value[1];
        }
 
        return 0;
@@ -1061,7 +1068,8 @@ int snd_soc_dapm_put_volsw(struct snd_kcontrol *kcontrol,
        int reg = kcontrol->private_value & 0xff;
        int shift = (kcontrol->private_value >> 8) & 0x0f;
        int rshift = (kcontrol->private_value >> 12) & 0x0f;
-       int mask = (kcontrol->private_value >> 16) & 0xff;
+       int max = (kcontrol->private_value >> 16) & 0xff;
+       int mask = (1 << fls(max)) - 1;
        int invert = (kcontrol->private_value >> 24) & 0x01;
        unsigned short val, val2, val_mask;
        int ret;
@@ -1069,13 +1077,13 @@ int snd_soc_dapm_put_volsw(struct snd_kcontrol *kcontrol,
        val = (ucontrol->value.integer.value[0] & mask);
 
        if (invert)
-               val = mask - val;
+               val = max - val;
        val_mask = mask << shift;
        val = val << shift;
        if (shift != rshift) {
                val2 = (ucontrol->value.integer.value[1] & mask);
                if (invert)
-                       val2 = mask - val2;
+                       val2 = max - val2;
                val_mask |= mask << rshift;
                val |= val2 << rshift;
        }
@@ -1093,13 +1101,17 @@ int snd_soc_dapm_put_volsw(struct snd_kcontrol *kcontrol,
        dapm_mixer_update_power(widget, kcontrol, reg, val_mask, val, invert);
        if (widget->event) {
                if (widget->event_flags & SND_SOC_DAPM_PRE_REG) {
-                       ret = widget->event(widget, SND_SOC_DAPM_PRE_REG);
-                       if (ret < 0)
+                       ret = widget->event(widget, kcontrol,
+                                               SND_SOC_DAPM_PRE_REG);
+                       if (ret < 0) {
+                               ret = 1;
                                goto out;
+                       }
                }
                ret = snd_soc_update_bits(widget->codec, reg, val_mask, val);
                if (widget->event_flags & SND_SOC_DAPM_POST_REG)
-                       ret = widget->event(widget, SND_SOC_DAPM_POST_REG);
+                       ret = widget->event(widget, kcontrol,
+                                               SND_SOC_DAPM_POST_REG);
        } else
                ret = snd_soc_update_bits(widget->codec, reg, val_mask, val);
 
@@ -1174,13 +1186,15 @@ int snd_soc_dapm_put_enum_double(struct snd_kcontrol *kcontrol,
        dapm_mux_update_power(widget, kcontrol, mask, mux, e);
        if (widget->event) {
                if (widget->event_flags & SND_SOC_DAPM_PRE_REG) {
-                       ret = widget->event(widget, SND_SOC_DAPM_PRE_REG);
+                       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, 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);
 
@@ -1279,6 +1293,29 @@ int snd_soc_dapm_stream_event(struct snd_soc_codec *codec,
 }
 EXPORT_SYMBOL_GPL(snd_soc_dapm_stream_event);
 
+/**
+ * snd_soc_dapm_device_event - send a device event to the dapm core
+ * @socdev: audio device
+ * @event: device event
+ *
+ * Sends a device event to the dapm core. The core then makes any
+ * necessary machine or codec power changes..
+ *
+ * Returns 0 for success else error.
+ */
+int snd_soc_dapm_device_event(struct snd_soc_device *socdev, int event)
+{
+       struct snd_soc_codec *codec = socdev->codec;
+       struct snd_soc_machine *machine = socdev->machine;
+
+       if (machine->dapm_event)
+               machine->dapm_event(machine, event);
+       if (codec->dapm_event)
+               codec->dapm_event(codec, event);
+       return 0;
+}
+EXPORT_SYMBOL_GPL(snd_soc_dapm_device_event);
+
 /**
  * snd_soc_dapm_set_endpoint - set audio endpoint status
  * @codec: audio codec
index 07962a35f2412f8461681835fcf6c12b7244bfc3..0c63e0585b15eed7d819dc55531db1d321865830 100644 (file)
@@ -36,7 +36,6 @@
 #include <linux/interrupt.h>
 #include <linux/moduleparam.h>
 
-#include <sound/driver.h>
 #include <sound/core.h>
 #include <sound/pcm.h>
 #include <sound/info.h>
@@ -859,7 +858,7 @@ static int snd_amd7930_put_volume(struct snd_kcontrol *kctl, struct snd_ctl_elem
        spin_lock_irqsave(&amd->lock, flags);
 
        if (*swval != ucontrol->value.integer.value[0]) {
-               *swval = ucontrol->value.integer.value[0];
+               *swval = ucontrol->value.integer.value[0] & 0xff;
                __amd7930_update_map(amd);
                change = 1;
        } else
index f8c7a120ccbb3ce2c98821523c55223fcbbb3458..1c4797be72ee774a54b6e3dd7ec8b0e476ee2381 100644 (file)
@@ -19,7 +19,6 @@
 #include <linux/io.h>
 
 
-#include <sound/driver.h>
 #include <sound/core.h>
 #include <sound/pcm.h>
 #include <sound/info.h>
index 376b98691c965bf228a585a0f619ed6a744fb50a..3d00e0797b11899042e5af98fa64a224ebc13597 100644 (file)
@@ -53,7 +53,6 @@
  * other       DBRI low-level stuff
  */
 
-#include <sound/driver.h>
 #include <linux/interrupt.h>
 #include <linux/delay.h>
 #include <linux/irq.h>
@@ -2279,14 +2278,25 @@ static int snd_cs4215_put_volume(struct snd_kcontrol *kcontrol,
        struct snd_dbri *dbri = snd_kcontrol_chip(kcontrol);
        struct dbri_streaminfo *info =
                                &dbri->stream_info[kcontrol->private_value];
+       unsigned int vol[2];
        int changed = 0;
 
-       if (info->left_gain != ucontrol->value.integer.value[0]) {
-               info->left_gain = ucontrol->value.integer.value[0];
+       vol[0] = ucontrol->value.integer.value[0];
+       vol[1] = ucontrol->value.integer.value[1];
+       if (kcontrol->private_value == DBRI_PLAY) {
+               if (vol[0] > DBRI_MAX_VOLUME || vol[1] > DBRI_MAX_VOLUME)
+                       return -EINVAL;
+       } else {
+               if (vol[0] > DBRI_MAX_GAIN || vol[1] > DBRI_MAX_GAIN)
+                       return -EINVAL;
+       }
+
+       if (info->left_gain != vol[0]) {
+               info->left_gain = vol[0];
                changed = 1;
        }
-       if (info->right_gain != ucontrol->value.integer.value[1]) {
-               info->right_gain = ucontrol->value.integer.value[1];
+       if (info->right_gain != vol[1]) {
+               info->right_gain = vol[1];
                changed = 1;
        }
        if (changed) {
index fee869bcc959a1c9e4da2c667fafebfcf406b6cb..89d6e9c351405c8d186176d7b80454d24153ad24 100644 (file)
 #include <linux/init.h>
 #include <linux/interrupt.h>
 #include <linux/module.h>
+#include <linux/mutex.h>
 #include <linux/platform_device.h>
 #include <linux/io.h>
 
-#include <sound/driver.h>
 #include <sound/initval.h>
 #include <sound/control.h>
 #include <sound/core.h>
@@ -76,8 +76,10 @@ struct snd_at73c213 {
        u8                              spi_rbuffer[2];
        /* Image of the SPI registers in AT73C213. */
        u8                              reg_image[18];
-       /* Protect registers against concurrent access. */
+       /* Protect SSC registers against concurrent access. */
        spinlock_t                      lock;
+       /* Protect mixer registers against concurrent access. */
+       struct mutex                    mixer_lock;
 };
 
 #define get_chip(card) ((struct snd_at73c213 *)card->private_data)
@@ -398,7 +400,7 @@ static int snd_at73c213_mono_get(struct snd_kcontrol *kcontrol,
        int mask = (kcontrol->private_value >> 16) & 0xff;
        int invert = (kcontrol->private_value >> 24) & 0xff;
 
-       spin_lock_irq(&chip->lock);
+       mutex_lock(&chip->mixer_lock);
 
        ucontrol->value.integer.value[0] =
                (chip->reg_image[reg] >> shift) & mask;
@@ -407,7 +409,7 @@ static int snd_at73c213_mono_get(struct snd_kcontrol *kcontrol,
                ucontrol->value.integer.value[0] =
                        mask - ucontrol->value.integer.value[0];
 
-       spin_unlock_irq(&chip->lock);
+       mutex_unlock(&chip->mixer_lock);
 
        return 0;
 }
@@ -428,13 +430,13 @@ static int snd_at73c213_mono_put(struct snd_kcontrol *kcontrol,
                val = mask - val;
        val <<= shift;
 
-       spin_lock_irq(&chip->lock);
+       mutex_lock(&chip->mixer_lock);
 
        val = (chip->reg_image[reg] & ~(mask << shift)) | val;
        change = val != chip->reg_image[reg];
        retval = snd_at73c213_write_reg(chip, reg, val);
 
-       spin_unlock_irq(&chip->lock);
+       mutex_unlock(&chip->mixer_lock);
 
        if (retval)
                return retval;
@@ -470,7 +472,7 @@ static int snd_at73c213_stereo_get(struct snd_kcontrol *kcontrol,
        int mask = (kcontrol->private_value >> 24) & 0xff;
        int invert = (kcontrol->private_value >> 22) & 1;
 
-       spin_lock_irq(&chip->lock);
+       mutex_lock(&chip->mixer_lock);
 
        ucontrol->value.integer.value[0] =
                (chip->reg_image[left_reg] >> shift_left) & mask;
@@ -484,7 +486,7 @@ static int snd_at73c213_stereo_get(struct snd_kcontrol *kcontrol,
                        mask - ucontrol->value.integer.value[1];
        }
 
-       spin_unlock_irq(&chip->lock);
+       mutex_unlock(&chip->mixer_lock);
 
        return 0;
 }
@@ -511,7 +513,7 @@ static int snd_at73c213_stereo_put(struct snd_kcontrol *kcontrol,
        val1 <<= shift_left;
        val2 <<= shift_right;
 
-       spin_lock_irq(&chip->lock);
+       mutex_lock(&chip->mixer_lock);
 
        val1 = (chip->reg_image[left_reg] & ~(mask << shift_left)) | val1;
        val2 = (chip->reg_image[right_reg] & ~(mask << shift_right)) | val2;
@@ -519,16 +521,16 @@ static int snd_at73c213_stereo_put(struct snd_kcontrol *kcontrol,
                || val2 != chip->reg_image[right_reg];
        retval = snd_at73c213_write_reg(chip, left_reg, val1);
        if (retval) {
-               spin_unlock_irq(&chip->lock);
+               mutex_unlock(&chip->mixer_lock);
                goto out;
        }
        retval = snd_at73c213_write_reg(chip, right_reg, val2);
        if (retval) {
-               spin_unlock_irq(&chip->lock);
+               mutex_unlock(&chip->mixer_lock);
                goto out;
        }
 
-       spin_unlock_irq(&chip->lock);
+       mutex_unlock(&chip->mixer_lock);
 
        return change;
 
@@ -536,16 +538,7 @@ out:
        return retval;
 }
 
-static int snd_at73c213_mono_switch_info(struct snd_kcontrol *kcontrol,
-                                 struct snd_ctl_elem_info *uinfo)
-{
-       uinfo->type = SNDRV_CTL_ELEM_TYPE_BOOLEAN;
-       uinfo->count = 1;
-       uinfo->value.integer.min = 0;
-       uinfo->value.integer.max = 1;
-
-       return 0;
-}
+#define snd_at73c213_mono_switch_info  snd_ctl_boolean_mono_info
 
 static int snd_at73c213_mono_switch_get(struct snd_kcontrol *kcontrol,
                                 struct snd_ctl_elem_value *ucontrol)
@@ -555,7 +548,7 @@ static int snd_at73c213_mono_switch_get(struct snd_kcontrol *kcontrol,
        int shift = (kcontrol->private_value >> 8) & 0xff;
        int invert = (kcontrol->private_value >> 24) & 0xff;
 
-       spin_lock_irq(&chip->lock);
+       mutex_lock(&chip->mixer_lock);
 
        ucontrol->value.integer.value[0] =
                (chip->reg_image[reg] >> shift) & 0x01;
@@ -564,7 +557,7 @@ static int snd_at73c213_mono_switch_get(struct snd_kcontrol *kcontrol,
                ucontrol->value.integer.value[0] =
                        0x01 - ucontrol->value.integer.value[0];
 
-       spin_unlock_irq(&chip->lock);
+       mutex_unlock(&chip->mixer_lock);
 
        return 0;
 }
@@ -589,14 +582,14 @@ static int snd_at73c213_mono_switch_put(struct snd_kcontrol *kcontrol,
                val = mask - val;
        val <<= shift;
 
-       spin_lock_irq(&chip->lock);
+       mutex_lock(&chip->mixer_lock);
 
        val |= (chip->reg_image[reg] & ~(mask << shift));
        change = val != chip->reg_image[reg];
 
        retval = snd_at73c213_write_reg(chip, reg, val);
 
-       spin_unlock_irq(&chip->lock);
+       mutex_unlock(&chip->mixer_lock);
 
        if (retval)
                return retval;
@@ -893,6 +886,7 @@ static int __devinit snd_at73c213_dev_init(struct snd_card *card,
                return irq;
 
        spin_lock_init(&chip->lock);
+       mutex_init(&chip->mixer_lock);
        chip->card = card;
        chip->irq = -1;
 
index ebcac13fd3970cd0c0fd989748b6a08c89a4b636..c89d2ea594b9f5dbd36d94c04e2e8fdacc5571bb 100644 (file)
@@ -18,7 +18,6 @@
  *   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA
  */
 
-#include <sound/driver.h>
 #include <linux/wait.h>
 #include <linux/slab.h>
 #include <linux/string.h>
index 9b63814c3f64412bb593b5d745a8e34b1d5f865d..0a5391436addfd3f0b67c5f91aa487f1af349bd7 100644 (file)
@@ -19,7 +19,6 @@
  *
  */
 
-#include <sound/driver.h>
 #include <sound/core.h>
 #include <sound/hwdep.h>
 #include <asm/uaccess.h>
index 3436816727c8190ff0f5e5960c38f62c7c01e886..f60a98ef7dec3d0d4e1dafc325471ae16e1de93c 100644 (file)
@@ -22,7 +22,6 @@
  *                             midi emulation.
  */
 
-#include <sound/driver.h>
 
 #ifdef CONFIG_SND_SEQUENCER_OSS
 
index 680f2b7fec207158abd4ef28cec6c72364ac9d83..687e6a13689e7cd912ac99ca79b9a14883931a56 100644 (file)
@@ -18,7 +18,6 @@
  *   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA
  */
 
-#include <sound/driver.h>
 #include <linux/wait.h>
 #include <linux/slab.h>
 #include <sound/core.h>
index 0a56ca18b16560da1de776b0f022d3216810b796..09711f84ed3049129681cb8b074e7f2b9c0ce2e2 100644 (file)
@@ -22,7 +22,6 @@
  *   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA
  */
 
-#include <sound/driver.h>
 #include <linux/wait.h>
 #include <linux/sched.h>
 #include <sound/core.h>
index 455e535933ecf0b3ac1a182588677a3ae31b95eb..36d53bd317ede3f2d3cc1974c7003a7746789d14 100644 (file)
@@ -25,7 +25,6 @@
  * of doing things so that the old sfxload utility can be used.
  * Everything may change when there is an alsa way of doing things.
  */
-#include <sound/driver.h>
 #include <asm/uaccess.h>
 #include <linux/slab.h>
 #include <sound/core.h>
index 6fc3d2b2519f935c76f1156962d4bae4cd0a6750..deabe5f899c494780eb29c188f9fe23edf8c92e8 100644 (file)
@@ -19,7 +19,6 @@
  */
 
 #include <linux/mutex.h>
-#include <sound/driver.h>
 #include <linux/init.h>
 #include <linux/slab.h>
 #include <sound/core.h>
index 706143826aff8102977b9c1cffa72d66ce0684ae..9351b8a765b9c6de7f5069e2c3c6d09b0f54a50d 100644 (file)
@@ -31,17 +31,18 @@ config SND_USB_USX2Y
 
 config SND_USB_CAIAQ
        tristate "Native Instruments USB audio devices"
-        depends on SND && USB
-        select SND_HWDEP
-        select SND_RAWMIDI
-        select SND_PCM
-        help
+       depends on SND && USB
+       select SND_HWDEP
+       select SND_RAWMIDI
+       select SND_PCM
+       help
           Say Y here to include support for caiaq USB audio interfaces,
           namely:
 
            * Native Instruments RigKontrol2
            * Native Instruments RigKontrol3
            * Native Instruments Kore Controller
+           * Native Instruments Kore Controller 2
            * Native Instruments Audio Kontrol 1
            * Native Instruments Audio 8 DJ
 
@@ -51,12 +52,15 @@ config SND_USB_CAIAQ
 config SND_USB_CAIAQ_INPUT
        bool "enable input device for controllers"
        depends on SND_USB_CAIAQ
+       depends on INPUT=y || INPUT=SND_USB_CAIAQ
        help
          Say Y here to support input controllers like buttons, knobs,
          alpha dials and analog pedals on the following products:
 
           * Native Instruments RigKontrol2
           * Native Instruments RigKontrol3
+          * Native Instruments Kore Controller
+          * Native Instruments Kore Controller 2
           * Native Instruments Audio Kontrol 1
 
 endmenu
index 455c8c58a1bdeb9e37edea913560453fb91ae3e1..23dadd5a11cd2d3e664ee96a12dbac4087ab2bfc 100644 (file)
@@ -1,3 +1,4 @@
-snd-usb-caiaq-objs := caiaq-device.o caiaq-audio.o caiaq-midi.o caiaq-input.o
+snd-usb-caiaq-y := caiaq-device.o caiaq-audio.o caiaq-midi.o caiaq-control.o
+snd-usb-caiaq-$(CONFIG_SND_USB_CAIAQ_INPUT) += caiaq-input.o
 
 obj-$(CONFIG_SND_USB_CAIAQ) += snd-usb-caiaq.o
index 0666908a2361a95023c5a897bdacbe25650f4bc6..9cc4cd8283f91561d744641b0afbead6e8b0495a 100644 (file)
@@ -16,7 +16,6 @@
  *   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA
 */
 
-#include <sound/driver.h>
 #include <linux/init.h>
 #include <linux/module.h>
 #include <linux/moduleparam.h>
@@ -27,9 +26,7 @@
 #include <sound/initval.h>
 #include <sound/pcm.h>
 #include <sound/rawmidi.h>
-#ifdef CONFIG_SND_USB_CAIAQ_INPUT
 #include <linux/input.h>
-#endif
 
 #include "caiaq-device.h"
 #include "caiaq-audio.h"
@@ -60,7 +57,7 @@ static struct snd_pcm_hardware snd_usb_caiaq_pcm_hardware = {
        .channels_min   = CHANNELS_PER_STREAM,
        .channels_max   = CHANNELS_PER_STREAM,
        .buffer_bytes_max = MAX_BUFFER_SIZE,
-       .period_bytes_min = 4096,
+       .period_bytes_min = 128,
        .period_bytes_max = MAX_BUFFER_SIZE,
        .periods_min    = 1,
        .periods_max    = 1024,
@@ -606,7 +603,7 @@ static void free_urbs(struct urb **urbs)
        kfree(urbs);
 }
 
-int __devinit snd_usb_caiaq_audio_init(struct snd_usb_caiaqdev *dev)
+int snd_usb_caiaq_audio_init(struct snd_usb_caiaqdev *dev)
 {
        int i, ret;
 
diff --git a/sound/usb/caiaq/caiaq-control.c b/sound/usb/caiaq/caiaq-control.c
new file mode 100644 (file)
index 0000000..798ca12
--- /dev/null
@@ -0,0 +1,315 @@
+/*
+ *   Copyright (c) 2007 Daniel Mack
+ *   friendly supported by NI.
+ *
+ *   This program is free software; you can redistribute it and/or modify
+ *   it under the terms of the GNU General Public License as published by
+ *   the Free Software Foundation; either version 2 of the License, or
+ *   (at your option) any later version.
+ *
+ *   This program is distributed in the hope that it will be useful,
+ *   but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *   GNU General Public License for more details.
+ *
+ *   You should have received a copy of the GNU General Public License
+ *   along with this program; if not, write to the Free Software
+ *   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA
+ */
+
+#include <linux/init.h>
+#include <linux/interrupt.h>
+#include <linux/usb.h>
+#include <sound/core.h>
+#include <sound/initval.h>
+#include <sound/pcm.h>
+#include <sound/rawmidi.h>
+#include <sound/control.h>
+#include <linux/input.h>
+
+#include "caiaq-device.h"
+#include "caiaq-control.h"
+
+#define CNT_INTVAL 0x10000
+
+static int control_info(struct snd_kcontrol *kcontrol,
+                       struct snd_ctl_elem_info *uinfo)
+{
+       struct snd_usb_audio *chip = snd_kcontrol_chip(kcontrol);
+       struct snd_usb_caiaqdev *dev = caiaqdev(chip->card);
+       int pos = kcontrol->private_value;
+       int is_intval = pos & CNT_INTVAL;
+
+       uinfo->count = 1;
+       pos &= ~CNT_INTVAL;
+
+       if (dev->chip.usb_id ==
+               USB_ID(USB_VID_NATIVEINSTRUMENTS, USB_PID_AUDIO8DJ)
+               && (pos == 0)) {
+               /* current input mode of A8DJ */
+               uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
+               uinfo->value.integer.min = 0;
+               uinfo->value.integer.max = 2;
+               return 0;
+       }
+
+       if (is_intval) {
+               uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
+               uinfo->value.integer.min = 0;
+               uinfo->value.integer.max = 64;
+       } else {
+               uinfo->type = SNDRV_CTL_ELEM_TYPE_BOOLEAN;
+               uinfo->value.integer.min = 0;
+               uinfo->value.integer.max = 1;
+       }
+
+       return 0;
+}
+
+static int control_get(struct snd_kcontrol *kcontrol,
+                      struct snd_ctl_elem_value *ucontrol)
+{
+       struct snd_usb_audio *chip = snd_kcontrol_chip(kcontrol);
+       struct snd_usb_caiaqdev *dev = caiaqdev(chip->card);
+       int pos = kcontrol->private_value;
+
+       if (pos & CNT_INTVAL)
+               ucontrol->value.integer.value[0]
+                       = dev->control_state[pos & ~CNT_INTVAL];
+       else
+               ucontrol->value.integer.value[0]
+                       = !!(dev->control_state[pos / 8] & (1 << pos % 8));
+
+       return 0;
+}
+
+static int control_put(struct snd_kcontrol *kcontrol,
+                      struct snd_ctl_elem_value *ucontrol)
+{
+       struct snd_usb_audio *chip = snd_kcontrol_chip(kcontrol);
+       struct snd_usb_caiaqdev *dev = caiaqdev(chip->card);
+       int pos = kcontrol->private_value;
+
+       if (pos & CNT_INTVAL) {
+               dev->control_state[pos & ~CNT_INTVAL]
+                       = ucontrol->value.integer.value[0];
+               snd_usb_caiaq_send_command(dev, EP1_CMD_DIMM_LEDS,
+                               dev->control_state, sizeof(dev->control_state));
+       } else {
+               if (ucontrol->value.integer.value[0])
+                       dev->control_state[pos / 8] |= 1 << (pos % 8);
+               else
+                       dev->control_state[pos / 8] &= ~(1 << (pos % 8));
+
+               snd_usb_caiaq_send_command(dev, EP1_CMD_WRITE_IO,
+                               dev->control_state, sizeof(dev->control_state));
+       }
+
+       return 1;
+}
+
+static struct snd_kcontrol_new kcontrol_template __devinitdata = {
+       .iface = SNDRV_CTL_ELEM_IFACE_HWDEP,
+       .access = SNDRV_CTL_ELEM_ACCESS_READWRITE,
+       .index = 0,
+       .info = control_info,
+       .get  = control_get,
+       .put  = control_put,
+       /* name and private_value filled later */
+};
+
+struct caiaq_controller {
+       char *name;
+       int index;
+};
+
+static struct caiaq_controller ak1_controller[] = {
+       { "LED left",   2 },
+       { "LED middle", 1 },
+       { "LED right",  0 },
+       { "LED ring",   3 }
+};
+
+static struct caiaq_controller rk2_controller[] = {
+       { "LED 1",              5  },
+       { "LED 2",              4  },
+       { "LED 3",              3  },
+       { "LED 4",              2  },
+       { "LED 5",              1  },
+       { "LED 6",              0  },
+       { "LED pedal",          6  },
+       { "LED 7seg_1b",        8  },
+       { "LED 7seg_1c",        9  },
+       { "LED 7seg_2a",        10 },
+       { "LED 7seg_2b",        11 },
+       { "LED 7seg_2c",        12 },
+       { "LED 7seg_2d",        13 },
+       { "LED 7seg_2e",        14 },
+       { "LED 7seg_2f",        15 },
+       { "LED 7seg_2g",        16 },
+       { "LED 7seg_3a",        17 },
+       { "LED 7seg_3b",        18 },
+       { "LED 7seg_3c",        19 },
+       { "LED 7seg_3d",        20 },
+       { "LED 7seg_3e",        21 },
+       { "LED 7seg_3f",        22 },
+       { "LED 7seg_3g",        23 }
+};
+
+static struct caiaq_controller rk3_controller[] = {
+       { "LED 7seg_1a",        0 + 0 },
+       { "LED 7seg_1b",        0 + 1 },
+       { "LED 7seg_1c",        0 + 2 },
+       { "LED 7seg_1d",        0 + 3 },
+       { "LED 7seg_1e",        0 + 4 },
+       { "LED 7seg_1f",        0 + 5 },
+       { "LED 7seg_1g",        0 + 6 },
+       { "LED 7seg_1p",        0 + 7 },
+
+       { "LED 7seg_2a",        8 + 0 },
+       { "LED 7seg_2b",        8 + 1 },
+       { "LED 7seg_2c",        8 + 2 },
+       { "LED 7seg_2d",        8 + 3 },
+       { "LED 7seg_2e",        8 + 4 },
+       { "LED 7seg_2f",        8 + 5 },
+       { "LED 7seg_2g",        8 + 6 },
+       { "LED 7seg_2p",        8 + 7 },
+
+       { "LED 7seg_3a",        16 + 0 },
+       { "LED 7seg_3b",        16 + 1 },
+       { "LED 7seg_3c",        16 + 2 },
+       { "LED 7seg_3d",        16 + 3 },
+       { "LED 7seg_3e",        16 + 4 },
+       { "LED 7seg_3f",        16 + 5 },
+       { "LED 7seg_3g",        16 + 6 },
+       { "LED 7seg_3p",        16 + 7 },
+
+       { "LED 7seg_4a",        24 + 0 },
+       { "LED 7seg_4b",        24 + 1 },
+       { "LED 7seg_4c",        24 + 2 },
+       { "LED 7seg_4d",        24 + 3 },
+       { "LED 7seg_4e",        24 + 4 },
+       { "LED 7seg_4f",        24 + 5 },
+       { "LED 7seg_4g",        24 + 6 },
+       { "LED 7seg_4p",        24 + 7 },
+
+       { "LED 1",              32 + 0 },
+       { "LED 2",              32 + 1 },
+       { "LED 3",              32 + 2 },
+       { "LED 4",              32 + 3 },
+       { "LED 5",              32 + 4 },
+       { "LED 6",              32 + 5 },
+       { "LED 7",              32 + 6 },
+       { "LED 8",              32 + 7 },
+       { "LED pedal",          32 + 8 }
+};
+
+static struct caiaq_controller kore_controller[] = {
+       { "LED F1",             8   | CNT_INTVAL },
+       { "LED F2",             12  | CNT_INTVAL },
+       { "LED F3",             0   | CNT_INTVAL },
+       { "LED F4",             4   | CNT_INTVAL },
+       { "LED F5",             11  | CNT_INTVAL },
+       { "LED F6",             15  | CNT_INTVAL },
+       { "LED F7",             3   | CNT_INTVAL },
+       { "LED F8",             7   | CNT_INTVAL },
+       { "LED touch1",         10  | CNT_INTVAL },
+       { "LED touch2",         14  | CNT_INTVAL },
+       { "LED touch3",         2   | CNT_INTVAL },
+       { "LED touch4",         6   | CNT_INTVAL },
+       { "LED touch5",         9   | CNT_INTVAL },
+       { "LED touch6",         13  | CNT_INTVAL },
+       { "LED touch7",         1   | CNT_INTVAL },
+       { "LED touch8",         5   | CNT_INTVAL },
+       { "LED left",           18  | CNT_INTVAL },
+       { "LED right",          22  | CNT_INTVAL },
+       { "LED up",             16  | CNT_INTVAL },
+       { "LED down",           20  | CNT_INTVAL },
+       { "LED stop",           23  | CNT_INTVAL },
+       { "LED play",           21  | CNT_INTVAL },
+       { "LED record",         19  | CNT_INTVAL },
+       { "LED listen",         17  | CNT_INTVAL },
+       { "LED lcd",            30  | CNT_INTVAL },
+       { "LED menu",           28  | CNT_INTVAL },
+       { "LED sound",          31  | CNT_INTVAL },
+       { "LED esc",            29  | CNT_INTVAL },
+       { "LED view",           27  | CNT_INTVAL },
+       { "LED enter",          24  | CNT_INTVAL },
+       { "LED control",        26  | CNT_INTVAL }
+};
+
+static struct caiaq_controller a8dj_controller[] = {
+       { "Current input mode",                 0 | CNT_INTVAL  },
+       { "GND lift for TC Vinyl mode",         24 + 0          },
+       { "GND lift for TC CD/Line mode",       24 + 1          },
+       { "GND lift for phono mode",            24 + 2          },
+       { "GND lift for TC Vinyl mode",         24 + 3          },
+       { "Software lock",                      40              }
+};
+
+int __devinit snd_usb_caiaq_control_init(struct snd_usb_caiaqdev *dev)
+{
+       int i;
+       struct snd_kcontrol *kc;
+
+       switch (dev->chip.usb_id) {
+       case USB_ID(USB_VID_NATIVEINSTRUMENTS, USB_PID_AK1):
+               for (i = 0; i < ARRAY_SIZE(ak1_controller); i++) {
+                       struct caiaq_controller *c = ak1_controller + i;
+                       kcontrol_template.name = c->name;
+                       kcontrol_template.private_value = c->index;
+                       kc = snd_ctl_new1(&kcontrol_template, dev);
+                       snd_ctl_add(dev->chip.card, kc);
+               }
+
+               break;
+
+       case USB_ID(USB_VID_NATIVEINSTRUMENTS, USB_PID_RIGKONTROL2):
+               for (i = 0; i < ARRAY_SIZE(rk2_controller); i++) {
+                       struct caiaq_controller *c = rk2_controller + i;
+                       kcontrol_template.name = c->name;
+                       kcontrol_template.private_value = c->index;
+                       kc = snd_ctl_new1(&kcontrol_template, dev);
+                       snd_ctl_add(dev->chip.card, kc);
+               }
+
+               break;
+
+       case USB_ID(USB_VID_NATIVEINSTRUMENTS, USB_PID_RIGKONTROL3):
+               for (i = 0; i < ARRAY_SIZE(rk3_controller); i++) {
+                       struct caiaq_controller *c = rk3_controller + i;
+                       kcontrol_template.name = c->name;
+                       kcontrol_template.private_value = c->index;
+                       kc = snd_ctl_new1(&kcontrol_template, dev);
+                       snd_ctl_add(dev->chip.card, kc);
+               }
+
+               break;
+
+       case USB_ID(USB_VID_NATIVEINSTRUMENTS, USB_PID_KORECONTROLLER):
+       case USB_ID(USB_VID_NATIVEINSTRUMENTS, USB_PID_KORECONTROLLER2):
+               for (i = 0; i < ARRAY_SIZE(kore_controller); i++) {
+                       struct caiaq_controller *c = kore_controller + i;
+                       kcontrol_template.name = c->name;
+                       kcontrol_template.private_value = c->index;
+                       kc = snd_ctl_new1(&kcontrol_template, dev);
+                       snd_ctl_add(dev->chip.card, kc);
+               }
+
+               break;
+
+       case USB_ID(USB_VID_NATIVEINSTRUMENTS, USB_PID_AUDIO8DJ):
+               for (i = 0; i < ARRAY_SIZE(a8dj_controller); i++) {
+                       struct caiaq_controller *c = a8dj_controller + i;
+                       kcontrol_template.name = c->name;
+                       kcontrol_template.private_value = c->index;
+                       kc = snd_ctl_new1(&kcontrol_template, dev);
+                       snd_ctl_add(dev->chip.card, kc);
+               }
+
+               break;
+       }
+
+       return 0;
+}
+
diff --git a/sound/usb/caiaq/caiaq-control.h b/sound/usb/caiaq/caiaq-control.h
new file mode 100644 (file)
index 0000000..2e7ab1a
--- /dev/null
@@ -0,0 +1,6 @@
+#ifndef CAIAQ_CONTROL_H
+#define CAIAQ_CONTROL_H
+
+int snd_usb_caiaq_control_init(struct snd_usb_caiaqdev *dev);
+
+#endif /* CAIAQ_CONTROL_H */
index 58af8142c57100cf3b549dbbe73bd844d7e85d46..58d25e4e7d6cf34324ed67a8454c93d4225967e2 100644 (file)
 #include <linux/usb.h>
 #include <linux/input.h>
 #include <linux/spinlock.h>
-#include <sound/driver.h>
 #include <sound/core.h>
 #include <sound/initval.h>
 #include <sound/pcm.h>
 #include <sound/rawmidi.h>
+#include <sound/control.h>
 
 #include "caiaq-device.h"
 #include "caiaq-audio.h"
 #include "caiaq-midi.h"
+#include "caiaq-control.h"
 
 #ifdef CONFIG_SND_USB_CAIAQ_INPUT
 #include "caiaq-input.h"
 #endif
 
 MODULE_AUTHOR("Daniel Mack <daniel@caiaq.de>");
-MODULE_DESCRIPTION("caiaq USB audio, version 1.2.0");
+MODULE_DESCRIPTION("caiaq USB audio, version 1.3.2");
 MODULE_LICENSE("GPL");
 MODULE_SUPPORTED_DEVICE("{{Native Instruments, RigKontrol2},"
                         "{Native Instruments, RigKontrol3},"
                         "{Native Instruments, Kore Controller},"
+                        "{Native Instruments, Kore Controller 2},"
                         "{Native Instruments, Audio Kontrol 1}"
                         "{Native Instruments, Audio 8 DJ}}");
 
@@ -93,6 +95,11 @@ static struct usb_device_id snd_usb_id_table[] = {
                .idVendor =     USB_VID_NATIVEINSTRUMENTS,
                .idProduct =    USB_PID_KORECONTROLLER
        },
+       {
+               .match_flags =  USB_DEVICE_ID_MATCH_DEVICE,
+               .idVendor =     USB_VID_NATIVEINSTRUMENTS,
+               .idProduct =    USB_PID_KORECONTROLLER2
+       },
        {
                .match_flags =  USB_DEVICE_ID_MATCH_DEVICE,
                .idVendor =     USB_VID_NATIVEINSTRUMENTS,
@@ -140,14 +147,21 @@ static void usb_ep1_command_reply_dispatch (struct urb* urb)
        case EP1_CMD_MIDI_READ:
                snd_usb_caiaq_midi_handle_input(dev, buf[1], buf + 3, buf[2]);
                break;
-
+       case EP1_CMD_READ_IO:
+               if (dev->chip.usb_id ==
+                       USB_ID(USB_VID_NATIVEINSTRUMENTS, USB_PID_AUDIO8DJ)) {
+                       if (urb->actual_length > sizeof(dev->control_state))
+                               urb->actual_length = sizeof(dev->control_state);
+                       memcpy(dev->control_state, buf + 1, urb->actual_length);
+                       wake_up(&dev->ep1_wait_queue);
+                       break;
+               }
 #ifdef CONFIG_SND_USB_CAIAQ_INPUT
        case EP1_CMD_READ_ERP:
        case EP1_CMD_READ_ANALOG:
-       case EP1_CMD_READ_IO:
                snd_usb_caiaq_input_dispatch(dev, buf, urb->actual_length);
-               break;
 #endif
+               break;
        }
 
        dev->ep1_in_urb.actual_length = 0;
@@ -156,10 +170,10 @@ static void usb_ep1_command_reply_dispatch (struct urb* urb)
                log("unable to submit urb. OOM!?\n");
 }
 
-static int send_command (struct snd_usb_caiaqdev *dev,
-                        unsigned char command, 
-                        const unsigned char *buffer,
-                        int len)
+int snd_usb_caiaq_send_command(struct snd_usb_caiaqdev *dev,
+                              unsigned char command,
+                              const unsigned char *buffer,
+                              int len)
 {
        int actual_len;
        struct usb_device *usb_dev = dev->chip.dev;
@@ -207,7 +221,8 @@ int snd_usb_caiaq_set_audio_params (struct snd_usb_caiaqdev *dev,
                rate, depth, bpp);
 
        dev->audio_parm_answer = -1;
-       ret = send_command(dev, EP1_CMD_AUDIO_PARAMS, tmp, sizeof(tmp));
+       ret = snd_usb_caiaq_send_command(dev, EP1_CMD_AUDIO_PARAMS,
+                                        tmp, sizeof(tmp));
 
        if (ret)
                return ret;
@@ -226,7 +241,8 @@ int snd_usb_caiaq_set_auto_msg (struct snd_usb_caiaqdev *dev,
                                int digital, int analog, int erp)
 {
        char tmp[3] = { digital, analog, erp };
-       return send_command(dev, EP1_CMD_AUTO_MSG, tmp, sizeof(tmp));
+       return snd_usb_caiaq_send_command(dev, EP1_CMD_AUTO_MSG,
+                                         tmp, sizeof(tmp));
 }
 
 static void setup_card(struct snd_usb_caiaqdev *dev)
@@ -241,7 +257,7 @@ static void setup_card(struct snd_usb_caiaqdev *dev)
                val[0] = 0x00;
                val[1] = 0x00;
                val[2] = 0x01;
-               send_command(dev, EP1_CMD_WRITE_IO, val, 3);
+               snd_usb_caiaq_send_command(dev, EP1_CMD_WRITE_IO, val, 3);
                break;
        case USB_ID(USB_VID_NATIVEINSTRUMENTS, USB_PID_RIGKONTROL3):
                /* RigKontrol2 - display two centered dashes ('--') */
@@ -249,22 +265,52 @@ static void setup_card(struct snd_usb_caiaqdev *dev)
                val[1] = 0x40;
                val[2] = 0x40;
                val[3] = 0x00;
-               send_command(dev, EP1_CMD_WRITE_IO, val, 4);
+               snd_usb_caiaq_send_command(dev, EP1_CMD_WRITE_IO, val, 4);
                break;
        case USB_ID(USB_VID_NATIVEINSTRUMENTS, USB_PID_AK1):
                /* Audio Kontrol 1 - make USB-LED stop blinking */
                val[0] = 0x00;
-               send_command(dev, EP1_CMD_WRITE_IO, val, 1);
+               snd_usb_caiaq_send_command(dev, EP1_CMD_WRITE_IO, val, 1);
+               break;
+       case USB_ID(USB_VID_NATIVEINSTRUMENTS, USB_PID_AUDIO8DJ):
+               /* Audio 8 DJ - trigger read of current settings */
+               dev->control_state[0] = 0xff;
+               snd_usb_caiaq_set_auto_msg(dev, 1, 0, 0);
+               snd_usb_caiaq_send_command(dev, EP1_CMD_READ_IO, NULL, 0);
+
+               if (!wait_event_timeout(dev->ep1_wait_queue,
+                                       dev->control_state[0] != 0xff, HZ))
+                       return;
+
+               /* fix up some defaults */
+               if ((dev->control_state[1] != 2) ||
+                   (dev->control_state[2] != 3) ||
+                   (dev->control_state[4] != 2)) {
+                       dev->control_state[1] = 2;
+                       dev->control_state[2] = 3;
+                       dev->control_state[4] = 2;
+                       snd_usb_caiaq_send_command(dev,
+                               EP1_CMD_WRITE_IO, dev->control_state, 6);
+               }
+
                break;
        }
        
-       ret = snd_usb_caiaq_audio_init(dev);
-       if (ret < 0)
-               log("Unable to set up audio system (ret=%d)\n", ret);
+       if (dev->spec.num_analog_audio_out +
+           dev->spec.num_analog_audio_in +
+           dev->spec.num_digital_audio_out +
+           dev->spec.num_digital_audio_in > 0) {
+               ret = snd_usb_caiaq_audio_init(dev);
+               if (ret < 0)
+                       log("Unable to set up audio system (ret=%d)\n", ret);
+       }
        
-       ret = snd_usb_caiaq_midi_init(dev);
-       if (ret < 0)
-               log("Unable to set up MIDI system (ret=%d)\n", ret);
+       if (dev->spec.num_midi_in +
+           dev->spec.num_midi_out > 0) {
+               ret = snd_usb_caiaq_midi_init(dev);
+               if (ret < 0)
+                       log("Unable to set up MIDI system (ret=%d)\n", ret);
+       }
 
 #ifdef CONFIG_SND_USB_CAIAQ_INPUT
        ret = snd_usb_caiaq_input_init(dev);
@@ -278,6 +324,10 @@ static void setup_card(struct snd_usb_caiaqdev *dev)
                log("snd_card_register() returned %d\n", ret);
                snd_card_free(dev->chip.card);
        }
+
+       ret = snd_usb_caiaq_control_init(dev);
+       if (ret < 0)
+               log("Unable to set up control system (ret=%d)\n", ret);
 }
 
 static struct snd_card* create_card(struct usb_device* usb_dev)
@@ -340,7 +390,7 @@ static int init_card(struct snd_usb_caiaqdev *dev)
        if (usb_submit_urb(&dev->ep1_in_urb, GFP_KERNEL) != 0)
                return -EIO;
 
-       err = send_command(dev, EP1_CMD_GET_DEVICE_INFO, NULL, 0);
+       err = snd_usb_caiaq_send_command(dev, EP1_CMD_GET_DEVICE_INFO, NULL, 0);
        if (err)
                return err;
 
index 79bc5be2df7a2249a86b847b536475adfea68853..96a491379c6062b540ff2a063f64735f1e2f640e 100644 (file)
@@ -7,7 +7,8 @@
 
 #define USB_PID_RIGKONTROL2    0x1969
 #define USB_PID_RIGKONTROL3    0x1940
-#define USB_PID_KORECONTROLLER         0x4711
+#define USB_PID_KORECONTROLLER 0x4711
+#define USB_PID_KORECONTROLLER2        0x4712
 #define USB_PID_AK1            0x0815
 #define USB_PID_AUDIO8DJ       0x1978
 
@@ -35,6 +36,7 @@
 #define EP1_CMD_MIDI_WRITE     0x7
 #define EP1_CMD_AUDIO_PARAMS   0x9
 #define EP1_CMD_AUTO_MSG       0xb
+#define EP1_CMD_DIMM_LEDS       0xc
 
 struct caiaq_device_spec {
        unsigned short fw_version;
@@ -62,7 +64,7 @@ struct snd_usb_caiaqdev {
        struct urb **data_urbs_in;
        struct urb **data_urbs_out;
        struct snd_usb_caiaq_cb_info *data_cb_info;
-       
+
        unsigned char ep1_in_buf[EP1_BUFSIZE];
        unsigned char ep1_out_buf[EP1_BUFSIZE];
        unsigned char midi_out_buf[EP1_BUFSIZE];
@@ -72,7 +74,7 @@ struct snd_usb_caiaqdev {
        wait_queue_head_t ep1_wait_queue;
        wait_queue_head_t prepare_wait_queue;
        int spec_received, audio_parm_answer;
-       
+
        char vendor_name[CAIAQ_USB_STR_LEN];
        char product_name[CAIAQ_USB_STR_LEN];
        char serial[CAIAQ_USB_STR_LEN];
@@ -90,11 +92,16 @@ struct snd_usb_caiaqdev {
        struct snd_pcm_substream *sub_playback[MAX_STREAMS];
        struct snd_pcm_substream *sub_capture[MAX_STREAMS];
 
+       /* Controls */
+       unsigned char control_state[64];
+
        /* Linux input */
 #ifdef CONFIG_SND_USB_CAIAQ_INPUT
        struct input_dev *input_dev;
+       char phys[64];                  /* physical device path */
+       unsigned short keycode[64];
 #endif
-       
+
        /* ALSA */
        struct snd_pcm *pcm;
        struct snd_pcm_hardware pcm_info;
@@ -112,6 +119,9 @@ struct snd_usb_caiaq_cb_info {
 
 int snd_usb_caiaq_set_audio_params (struct snd_usb_caiaqdev *dev, int rate, int depth, int bbp);
 int snd_usb_caiaq_set_auto_msg (struct snd_usb_caiaqdev *dev, int digital, int analog, int erp);
-
+int snd_usb_caiaq_send_command(struct snd_usb_caiaqdev *dev,
+                              unsigned char command,
+                              const unsigned char *buffer,
+                              int len);
 
 #endif /* CAIAQ_DEVICE_H */
index cd536ca20e5632d479a70e2d498da16555cfe79d..f743847a5e5a0a9e8aa7d1e8a67ccb204586821a 100644 (file)
 #include <linux/moduleparam.h>
 #include <linux/input.h>
 #include <linux/usb.h>
+#include <linux/usb/input.h>
 #include <linux/spinlock.h>
-#include <sound/driver.h>
 #include <sound/core.h>
 #include <sound/rawmidi.h>
 #include <sound/pcm.h>
 #include "caiaq-device.h"
 #include "caiaq-input.h"
 
-#ifdef CONFIG_SND_USB_CAIAQ_INPUT
-
-static unsigned char keycode_ak1[] =  { KEY_C, KEY_B, KEY_A };
-static unsigned char keycode_rk2[] =  { KEY_1, KEY_2, KEY_3, KEY_4, 
-                                       KEY_5, KEY_6, KEY_7 };
-static unsigned char keycode_rk3[] =  { KEY_1, KEY_2, KEY_3, KEY_4,
-                                       KEY_5, KEY_6, KEY_7, KEY_5, KEY_6 };
-
-#define DEG90  (range/2)
-#define DEG180 (range)
-#define DEG270 (DEG90 + DEG180)
-#define DEG360 (DEG180 * 2)
-#define HIGH_PEAK (268)
-#define LOW_PEAK (-7)
+static unsigned short keycode_ak1[] =  { KEY_C, KEY_B, KEY_A };
+static unsigned short keycode_rk2[] =  { KEY_1, KEY_2, KEY_3, KEY_4,
+                                        KEY_5, KEY_6, KEY_7 };
+static unsigned short keycode_rk3[] =  { KEY_1, KEY_2, KEY_3, KEY_4,
+                                        KEY_5, KEY_6, KEY_7, KEY_5, KEY_6 };
+
+static unsigned short keycode_kore[] = {
+       KEY_FN_F1,      /* "menu"               */
+       KEY_FN_F7,      /* "lcd backlight       */
+       KEY_FN_F2,      /* "control"            */
+       KEY_FN_F3,      /* "enter"              */
+       KEY_FN_F4,      /* "view"               */
+       KEY_FN_F5,      /* "esc"                */
+       KEY_FN_F6,      /* "sound"              */
+       KEY_FN_F8,      /* array spacer, never triggered. */
+       KEY_RIGHT,
+       KEY_DOWN,
+       KEY_UP,
+       KEY_LEFT,
+       KEY_SOUND,      /* "listen"             */
+       KEY_RECORD,
+       KEY_PLAYPAUSE,
+       KEY_STOP,
+       BTN_4,          /* 8 softkeys */
+       BTN_3,
+       BTN_2,
+       BTN_1,
+       BTN_8,
+       BTN_7,
+       BTN_6,
+       BTN_5,
+       KEY_BRL_DOT4,   /* touch sensitive knobs */
+       KEY_BRL_DOT3,
+       KEY_BRL_DOT2,
+       KEY_BRL_DOT1,
+       KEY_BRL_DOT8,
+       KEY_BRL_DOT7,
+       KEY_BRL_DOT6,
+       KEY_BRL_DOT5
+};
+
+#define DEG90          (range / 2)
+#define DEG180         (range)
+#define DEG270         (DEG90 + DEG180)
+#define DEG360         (DEG180 * 2)
+#define HIGH_PEAK      (268)
+#define LOW_PEAK       (-7)
 
 /* some of these devices have endless rotation potentiometers
  * built in which use two tapers, 90 degrees phase shifted.
@@ -56,8 +89,8 @@ static unsigned int decode_erp(unsigned char a, unsigned char b)
        int range = HIGH_PEAK - LOW_PEAK;
        int mid_value = (HIGH_PEAK + LOW_PEAK) / 2;
 
-       weight_b = abs(mid_value-a) - (range/2 - 100)/2;
-       
+       weight_b = abs(mid_value - a) - (range / 2 - 100) / 2;
+
        if (weight_b < 0)
                weight_b = 0;
 
@@ -93,7 +126,7 @@ static unsigned int decode_erp(unsigned char a, unsigned char b)
 
        if (ret < 0)
                ret += 1000;
-       
+
        if (ret >= 1000)
                ret -= 1000;
 
@@ -108,76 +141,113 @@ static unsigned int decode_erp(unsigned char a, unsigned char b)
 #undef LOW_PEAK
 
 
-static void snd_caiaq_input_read_analog(struct snd_usb_caiaqdev *dev, 
+static void snd_caiaq_input_read_analog(struct snd_usb_caiaqdev *dev,
                                        const unsigned char *buf,
                                        unsigned int len)
 {
-       switch(dev->input_dev->id.product) {
-       case USB_PID_RIGKONTROL2:
-               input_report_abs(dev->input_dev, ABS_X, (buf[4] << 8) |buf[5]);
-               input_report_abs(dev->input_dev, ABS_Y, (buf[0] << 8) |buf[1]);
-               input_report_abs(dev->input_dev, ABS_Z, (buf[2] << 8) |buf[3]);
-               input_sync(dev->input_dev);
+       struct input_dev *input_dev = dev->input_dev;
+
+       switch (dev->chip.usb_id) {
+       case USB_ID(USB_VID_NATIVEINSTRUMENTS, USB_PID_RIGKONTROL2):
+               input_report_abs(input_dev, ABS_X, (buf[4] << 8) | buf[5]);
+               input_report_abs(input_dev, ABS_Y, (buf[0] << 8) | buf[1]);
+               input_report_abs(input_dev, ABS_Z, (buf[2] << 8) | buf[3]);
+               input_sync(input_dev);
                break;
-       case USB_PID_RIGKONTROL3:
-               input_report_abs(dev->input_dev, ABS_X, (buf[0] << 8) |buf[1]);
-               input_report_abs(dev->input_dev, ABS_Y, (buf[2] << 8) |buf[3]);
-               input_report_abs(dev->input_dev, ABS_Z, (buf[4] << 8) |buf[5]);
-               input_sync(dev->input_dev);
+       case USB_ID(USB_VID_NATIVEINSTRUMENTS, USB_PID_RIGKONTROL3):
+               input_report_abs(input_dev, ABS_X, (buf[0] << 8) | buf[1]);
+               input_report_abs(input_dev, ABS_Y, (buf[2] << 8) | buf[3]);
+               input_report_abs(input_dev, ABS_Z, (buf[4] << 8) | buf[5]);
+               input_sync(input_dev);
+               break;
+       case USB_ID(USB_VID_NATIVEINSTRUMENTS, USB_PID_KORECONTROLLER):
+       case USB_ID(USB_VID_NATIVEINSTRUMENTS, USB_PID_KORECONTROLLER2):
+               input_report_abs(input_dev, ABS_X, (buf[0] << 8) | buf[1]);
+               input_report_abs(input_dev, ABS_Y, (buf[2] << 8) | buf[3]);
+               input_report_abs(input_dev, ABS_Z, (buf[4] << 8) | buf[5]);
+               input_sync(input_dev);
                break;
        }
 }
 
-static void snd_caiaq_input_read_erp(struct snd_usb_caiaqdev *dev, 
+static void snd_caiaq_input_read_erp(struct snd_usb_caiaqdev *dev,
                                     const char *buf, unsigned int len)
 {
+       struct input_dev *input_dev = dev->input_dev;
        int i;
 
-       switch(dev->input_dev->id.product) {
-       case USB_PID_AK1:
+       switch (dev->chip.usb_id) {
+       case USB_ID(USB_VID_NATIVEINSTRUMENTS, USB_PID_AK1):
                i = decode_erp(buf[0], buf[1]);
-               input_report_abs(dev->input_dev, ABS_X, i);
-               input_sync(dev->input_dev);
+               input_report_abs(input_dev, ABS_X, i);
+               input_sync(input_dev);
+               break;
+       case USB_ID(USB_VID_NATIVEINSTRUMENTS, USB_PID_KORECONTROLLER):
+       case USB_ID(USB_VID_NATIVEINSTRUMENTS, USB_PID_KORECONTROLLER2):
+               i = decode_erp(buf[7], buf[5]);
+               input_report_abs(input_dev, ABS_HAT0X, i);
+               i = decode_erp(buf[12], buf[14]);
+               input_report_abs(input_dev, ABS_HAT0Y, i);
+               i = decode_erp(buf[15], buf[13]);
+               input_report_abs(input_dev, ABS_HAT1X, i);
+               i = decode_erp(buf[0], buf[2]);
+               input_report_abs(input_dev, ABS_HAT1Y, i);
+               i = decode_erp(buf[3], buf[1]);
+               input_report_abs(input_dev, ABS_HAT2X, i);
+               i = decode_erp(buf[8], buf[10]);
+               input_report_abs(input_dev, ABS_HAT2Y, i);
+               i = decode_erp(buf[11], buf[9]);
+               input_report_abs(input_dev, ABS_HAT3X, i);
+               i = decode_erp(buf[4], buf[6]);
+               input_report_abs(input_dev, ABS_HAT3Y, i);
+               input_sync(input_dev);
                break;
        }
 }
 
-static void snd_caiaq_input_read_io(struct snd_usb_caiaqdev *dev, 
+static void snd_caiaq_input_read_io(struct snd_usb_caiaqdev *dev,
                                    char *buf, unsigned int len)
 {
+       struct input_dev *input_dev = dev->input_dev;
+       unsigned short *keycode = input_dev->keycode;
        int i;
-       unsigned char *keycode = dev->input_dev->keycode;
 
        if (!keycode)
                return;
 
-       if (dev->input_dev->id.product == USB_PID_RIGKONTROL2)
-               for (i=0; i<len; i++)
+       if (input_dev->id.product == USB_PID_RIGKONTROL2)
+               for (i = 0; i < len; i++)
                        buf[i] = ~buf[i];
 
-       for (i=0; (i<dev->input_dev->keycodemax) && (i < len); i++)
-               input_report_key(dev->input_dev, keycode[i], 
-                                       buf[i/8] & (1 << (i%8)));
+       for (i = 0; i < input_dev->keycodemax && i < len * 8; i++)
+               input_report_key(input_dev, keycode[i],
+                                buf[i / 8] & (1 << (i % 8)));
 
-       input_sync(dev->input_dev);
+       if (dev->chip.usb_id ==
+               USB_ID(USB_VID_NATIVEINSTRUMENTS, USB_PID_KORECONTROLLER) ||
+           dev->chip.usb_id ==
+               USB_ID(USB_VID_NATIVEINSTRUMENTS, USB_PID_KORECONTROLLER2))
+               input_report_abs(dev->input_dev, ABS_MISC, 255 - buf[4]);
+
+       input_sync(input_dev);
 }
 
-void snd_usb_caiaq_input_dispatch(struct snd_usb_caiaqdev *dev, 
-                                 char *buf, 
+void snd_usb_caiaq_input_dispatch(struct snd_usb_caiaqdev *dev,
+                                 char *buf,
                                  unsigned int len)
 {
-       if (!dev->input_dev || (len < 1))
+       if (!dev->input_dev || len < 1)
                return;
 
        switch (buf[0]) {
        case EP1_CMD_READ_ANALOG:
-               snd_caiaq_input_read_analog(dev, buf+1, len-1);
+               snd_caiaq_input_read_analog(dev, buf + 1, len - 1);
                break;
        case EP1_CMD_READ_ERP:
-               snd_caiaq_input_read_erp(dev, buf+1, len-1);
+               snd_caiaq_input_read_erp(dev, buf + 1, len - 1);
                break;
        case EP1_CMD_READ_IO:
-               snd_caiaq_input_read_io(dev, buf+1, len-1);
+               snd_caiaq_input_read_io(dev, buf + 1, len - 1);
                break;
        }
 }
@@ -192,37 +262,34 @@ int snd_usb_caiaq_input_init(struct snd_usb_caiaqdev *dev)
        if (!input)
                return -ENOMEM;
 
+       usb_make_path(usb_dev, dev->phys, sizeof(dev->phys));
+       strlcat(dev->phys, "/input0", sizeof(dev->phys));
+
        input->name = dev->product_name;
-       input->id.bustype = BUS_USB;
-       input->id.vendor  = usb_dev->descriptor.idVendor;
-       input->id.product = usb_dev->descriptor.idProduct;
-       input->id.version = usb_dev->descriptor.bcdDevice;
+       input->phys = dev->phys;
+       usb_to_input_id(usb_dev, &input->id);
+       input->dev.parent = &usb_dev->dev;
 
         switch (dev->chip.usb_id) {
        case USB_ID(USB_VID_NATIVEINSTRUMENTS, USB_PID_RIGKONTROL2):
                input->evbit[0] = BIT_MASK(EV_KEY) | BIT_MASK(EV_ABS);
                input->absbit[0] = BIT_MASK(ABS_X) | BIT_MASK(ABS_Y) |
                        BIT_MASK(ABS_Z);
-               input->keycode = keycode_rk2;
-               input->keycodesize = sizeof(char);
+               BUILD_BUG_ON(sizeof(dev->keycode) < sizeof(keycode_rk2));
+               memcpy(dev->keycode, keycode_rk2, sizeof(keycode_rk2));
                input->keycodemax = ARRAY_SIZE(keycode_rk2);
-               for (i=0; i<ARRAY_SIZE(keycode_rk2); i++)
-                       set_bit(keycode_rk2[i], input->keybit);
-
                input_set_abs_params(input, ABS_X, 0, 4096, 0, 10);
                input_set_abs_params(input, ABS_Y, 0, 4096, 0, 10);
                input_set_abs_params(input, ABS_Z, 0, 4096, 0, 10);
                snd_usb_caiaq_set_auto_msg(dev, 1, 10, 0);
                break;
        case USB_ID(USB_VID_NATIVEINSTRUMENTS, USB_PID_RIGKONTROL3):
-               input->evbit[0] = BIT(EV_KEY) | BIT(EV_ABS);
-               input->absbit[0] = BIT(ABS_X) | BIT(ABS_Y) | BIT(ABS_Z);
-               input->keycode = keycode_rk3;
-               input->keycodesize = sizeof(char);
+               input->evbit[0] = BIT_MASK(EV_KEY) | BIT_MASK(EV_ABS);
+               input->absbit[0] = BIT_MASK(ABS_X) | BIT_MASK(ABS_Y) |
+                       BIT_MASK(ABS_Z);
+               BUILD_BUG_ON(sizeof(dev->keycode) < sizeof(keycode_rk3));
+               memcpy(dev->keycode, keycode_rk3, sizeof(keycode_rk3));
                input->keycodemax = ARRAY_SIZE(keycode_rk3);
-               for (i=0; i<ARRAY_SIZE(keycode_rk3); i++)
-                       set_bit(keycode_rk3[i], input->keybit);
-
                input_set_abs_params(input, ABS_X, 0, 1024, 0, 10);
                input_set_abs_params(input, ABS_Y, 0, 1024, 0, 10);
                input_set_abs_params(input, ABS_Z, 0, 1024, 0, 10);
@@ -231,21 +298,50 @@ int snd_usb_caiaq_input_init(struct snd_usb_caiaqdev *dev)
        case USB_ID(USB_VID_NATIVEINSTRUMENTS, USB_PID_AK1):
                input->evbit[0] = BIT_MASK(EV_KEY) | BIT_MASK(EV_ABS);
                input->absbit[0] = BIT_MASK(ABS_X);
-               input->keycode = keycode_ak1;
-               input->keycodesize = sizeof(char);
+               BUILD_BUG_ON(sizeof(dev->keycode) < sizeof(keycode_ak1));
+               memcpy(dev->keycode, keycode_ak1, sizeof(keycode_ak1));
                input->keycodemax = ARRAY_SIZE(keycode_ak1);
-               for (i=0; i<ARRAY_SIZE(keycode_ak1); i++)
-                       set_bit(keycode_ak1[i], input->keybit);
-
                input_set_abs_params(input, ABS_X, 0, 999, 0, 10);
                snd_usb_caiaq_set_auto_msg(dev, 1, 0, 5);
                break;
+       case USB_ID(USB_VID_NATIVEINSTRUMENTS, USB_PID_KORECONTROLLER):
+       case USB_ID(USB_VID_NATIVEINSTRUMENTS, USB_PID_KORECONTROLLER2):
+               input->evbit[0] = BIT_MASK(EV_KEY) | BIT_MASK(EV_ABS);
+               input->absbit[0] = BIT_MASK(ABS_HAT0X) | BIT_MASK(ABS_HAT0Y) |
+                                  BIT_MASK(ABS_HAT1X) | BIT_MASK(ABS_HAT1Y) |
+                                  BIT_MASK(ABS_HAT2X) | BIT_MASK(ABS_HAT2Y) |
+                                  BIT_MASK(ABS_HAT3X) | BIT_MASK(ABS_HAT3Y) |
+                                  BIT_MASK(ABS_X) | BIT_MASK(ABS_Y) |
+                                  BIT_MASK(ABS_Z);
+               input->absbit[BIT_WORD(ABS_MISC)] |= BIT_MASK(ABS_MISC);
+               BUILD_BUG_ON(sizeof(dev->keycode) < sizeof(keycode_kore));
+               memcpy(dev->keycode, keycode_kore, sizeof(keycode_kore));
+               input->keycodemax = ARRAY_SIZE(keycode_kore);
+               input_set_abs_params(input, ABS_HAT0X, 0, 999, 0, 10);
+               input_set_abs_params(input, ABS_HAT0Y, 0, 999, 0, 10);
+               input_set_abs_params(input, ABS_HAT1X, 0, 999, 0, 10);
+               input_set_abs_params(input, ABS_HAT1Y, 0, 999, 0, 10);
+               input_set_abs_params(input, ABS_HAT2X, 0, 999, 0, 10);
+               input_set_abs_params(input, ABS_HAT2Y, 0, 999, 0, 10);
+               input_set_abs_params(input, ABS_HAT3X, 0, 999, 0, 10);
+               input_set_abs_params(input, ABS_HAT3Y, 0, 999, 0, 10);
+               input_set_abs_params(input, ABS_X, 0, 4096, 0, 10);
+               input_set_abs_params(input, ABS_Y, 0, 4096, 0, 10);
+               input_set_abs_params(input, ABS_Z, 0, 4096, 0, 10);
+               input_set_abs_params(input, ABS_MISC, 0, 255, 0, 1);
+               snd_usb_caiaq_set_auto_msg(dev, 1, 10, 5);
+               break;
        default:
                /* no input methods supported on this device */
                input_free_device(input);
                return 0;
        }
 
+       input->keycode = dev->keycode;
+       input->keycodesize = sizeof(unsigned short);
+       for (i = 0; i < input->keycodemax; i++)
+               __set_bit(dev->keycode[i], input->keybit);
+
        ret = input_register_device(input);
        if (ret < 0) {
                input_free_device(input);
@@ -265,5 +361,3 @@ void snd_usb_caiaq_input_free(struct snd_usb_caiaqdev *dev)
        dev->input_dev = NULL;
 }
 
-#endif /* CONFIG_SND_USB_CAIAQ_INPUT */
-
index 793ca20ce349c502732c7f160e0401dff9f13f47..30b57f97c6e411221517f9a8c3d0a5e1a42036b0 100644 (file)
@@ -23,7 +23,6 @@
 #include <linux/usb.h>
 #include <linux/input.h>
 #include <linux/spinlock.h>
-#include <sound/driver.h>
 #include <sound/core.h>
 #include <sound/rawmidi.h>
 #include <sound/pcm.h>
@@ -124,7 +123,7 @@ void snd_usb_caiaq_midi_handle_input(struct snd_usb_caiaqdev *dev,
        snd_rawmidi_receive(dev->midi_receive_substream, buf, len);
 }
 
-int __devinit snd_usb_caiaq_midi_init(struct snd_usb_caiaqdev *device)
+int snd_usb_caiaq_midi_init(struct snd_usb_caiaqdev *device)
 {
        int ret;
        struct snd_rawmidi *rmidi;
index 967b823eace083bbad39e4fae5378243b61dbbe7..8fa93566570272036365009776ec0fa952c1303d 100644 (file)
@@ -38,7 +38,6 @@
  */
 
 
-#include <sound/driver.h>
 #include <linux/bitops.h>
 #include <linux/init.h>
 #include <linux/list.h>
@@ -2078,6 +2077,14 @@ static int usb_audio_probe(struct usb_interface *intf,
                           const struct usb_device_id *id);
 static void usb_audio_disconnect(struct usb_interface *intf);
 
+#ifdef CONFIG_PM
+static int usb_audio_suspend(struct usb_interface *intf, pm_message_t message);
+static int usb_audio_resume(struct usb_interface *intf);
+#else
+#define usb_audio_suspend NULL
+#define usb_audio_resume NULL
+#endif
+
 static struct usb_device_id usb_audio_ids [] = {
 #include "usbquirks.h"
     { .match_flags = (USB_DEVICE_ID_MATCH_INT_CLASS | USB_DEVICE_ID_MATCH_INT_SUBCLASS),
@@ -2092,6 +2099,8 @@ static struct usb_driver usb_audio_driver = {
        .name =         "snd-usb-audio",
        .probe =        usb_audio_probe,
        .disconnect =   usb_audio_disconnect,
+       .suspend =      usb_audio_suspend,
+       .resume =       usb_audio_resume,
        .id_table =     usb_audio_ids,
 };
 
@@ -3654,6 +3663,45 @@ static void usb_audio_disconnect(struct usb_interface *intf)
                                 dev_get_drvdata(&intf->dev));
 }
 
+#ifdef CONFIG_PM
+static int usb_audio_suspend(struct usb_interface *intf, pm_message_t message)
+{
+       struct snd_usb_audio *chip = dev_get_drvdata(&intf->dev);
+       struct list_head *p;
+       struct snd_usb_stream *as;
+
+       if (chip == (void *)-1L)
+               return 0;
+
+       snd_power_change_state(chip->card, SNDRV_CTL_POWER_D3hot);
+       if (!chip->num_suspended_intf++) {
+               list_for_each(p, &chip->pcm_list) {
+                       as = list_entry(p, struct snd_usb_stream, list);
+                       snd_pcm_suspend_all(as->pcm);
+               }
+       }
+
+       return 0;
+}
+
+static int usb_audio_resume(struct usb_interface *intf)
+{
+       struct snd_usb_audio *chip = dev_get_drvdata(&intf->dev);
+
+       if (chip == (void *)-1L)
+               return 0;
+       if (--chip->num_suspended_intf)
+               return 0;
+       /*
+        * ALSA leaves material resumption to user space
+        * we just notify
+        */
+
+       snd_power_change_state(chip->card, SNDRV_CTL_POWER_D0);
+
+       return 0;
+}
+#endif         /* CONFIG_PM */
 
 static int __init snd_usb_audio_init(void)
 {
index 2272f45a18674320977d5314a6608b4449a27312..7cf18c38dc424c14a1decd29614105cfc4412e81 100644 (file)
@@ -126,6 +126,7 @@ struct snd_usb_audio {
        u32 usb_id;
        int shutdown;
        int num_interfaces;
+       int num_suspended_intf;
 
        struct list_head pcm_list;      /* list of pcm streams */
        int pcm_devs;
index 6330788c1c2b2d58ddf31c2802c682b5531f0004..750e929d5870504b39c1e0479a91af872c0cd539 100644 (file)
@@ -35,7 +35,6 @@
  * SUCH DAMAGE.
  */
 
-#include <sound/driver.h>
 #include <linux/kernel.h>
 #include <linux/types.h>
 #include <linux/bitops.h>
index 5e329690cfb1bbe6bb1fab91a36c7c5c1d6656ec..89c63d073cc6fac8f0c00ca1646377994157c64b 100644 (file)
@@ -26,7 +26,6 @@
  *
  */
 
-#include <sound/driver.h>
 #include <linux/bitops.h>
 #include <linux/init.h>
 #include <linux/list.h>
@@ -1703,6 +1702,11 @@ static void snd_usb_mixer_memory_change(struct usb_mixer_interface *mixer,
        case 19: /* speaker out jacks */
        case 20: /* headphones out jack */
                break;
+       /* live24ext: 4 = line-in jack */
+       case 3: /* hp-out jack (may actuate Mute) */
+               if (mixer->chip->usb_id == USB_ID(0x041e, 0x3040))
+                       snd_usb_mixer_notify_id(mixer, mixer->rc_cfg->mute_mixer_id);
+               break;
        default:
                snd_printd(KERN_DEBUG "memory change in unknown unit %d\n", unitid);
                break;
@@ -1951,6 +1955,9 @@ static int snd_audigy2nx_controls_create(struct usb_mixer_interface *mixer)
        int i, err;
 
        for (i = 0; i < ARRAY_SIZE(snd_audigy2nx_controls); ++i) {
+               if (i > 1 &&  /* Live24ext has 2 LEDs only */
+                       mixer->chip->usb_id == USB_ID(0x041e, 0x3040))
+                       break; 
                err = snd_ctl_add(mixer->chip->card,
                                  snd_ctl_new1(&snd_audigy2nx_controls[i], mixer));
                if (err < 0)
@@ -1963,28 +1970,42 @@ static int snd_audigy2nx_controls_create(struct usb_mixer_interface *mixer)
 static void snd_audigy2nx_proc_read(struct snd_info_entry *entry,
                                    struct snd_info_buffer *buffer)
 {
-       static const struct {
+       static const struct sb_jack {
                int unitid;
                const char *name;
-       } jacks[] = {
+       }  jacks_audigy2nx[] = {
                {4,  "dig in "},
                {7,  "line in"},
                {19, "spk out"},
                {20, "hph out"},
+               {-1, NULL}
+       }, jacks_live24ext[] = {
+               {4,  "line in"}, /* &1=Line, &2=Mic*/
+               {3,  "hph out"}, /* headphones */
+               {0,  "RC     "}, /* last command, 6 bytes see rc_config above */
+               {-1, NULL}
        };
+       const struct sb_jack *jacks;
        struct usb_mixer_interface *mixer = entry->private_data;
        int i, err;
        u8 buf[3];
 
        snd_iprintf(buffer, "%s jacks\n\n", mixer->chip->card->shortname);
-       for (i = 0; i < ARRAY_SIZE(jacks); ++i) {
+       if (mixer->chip->usb_id == USB_ID(0x041e, 0x3020))
+               jacks = jacks_audigy2nx;
+       else if (mixer->chip->usb_id == USB_ID(0x041e, 0x3040))
+               jacks = jacks_live24ext;
+       else
+               return;
+
+       for (i = 0; jacks[i].name; ++i) {
                snd_iprintf(buffer, "%s: ", jacks[i].name);
                err = snd_usb_ctl_msg(mixer->chip->dev,
                                      usb_rcvctrlpipe(mixer->chip->dev, 0),
                                      GET_MEM, USB_DIR_IN | USB_TYPE_CLASS |
                                      USB_RECIP_INTERFACE, 0,
                                      jacks[i].unitid << 8, buf, 3, 100);
-               if (err == 3 && buf[0] == 3)
+               if (err == 3 && (buf[0] == 3 || buf[0] == 6))
                        snd_iprintf(buffer, "%02x %02x\n", buf[1], buf[2]);
                else
                        snd_iprintf(buffer, "?\n");
@@ -2022,7 +2043,8 @@ int snd_usb_create_mixer(struct snd_usb_audio *chip, int ctrlif)
        if ((err = snd_usb_soundblaster_remote_init(mixer)) < 0)
                goto _error;
 
-       if (mixer->chip->usb_id == USB_ID(0x041e, 0x3020)) {
+       if (mixer->chip->usb_id == USB_ID(0x041e, 0x3020) ||
+           mixer->chip->usb_id == USB_ID(0x041e, 0x3040)) {
                struct snd_info_entry *entry;
 
                if ((err = snd_audigy2nx_controls_create(mixer)) < 0)
index 7c4dcb3f436af09f3afa0e0ae88a6612d7edd503..d755be0ad8115da5c1296211678f81a7774277d5 100644 (file)
@@ -187,6 +187,13 @@ static struct usbmix_selector_map audigy2nx_selectors[] = {
        { 0 } /* terminator */
 };
 
+/* Creative SoundBlaster Live! 24-bit External */
+static struct usbmix_name_map live24ext_map[] = {
+       /* 2: PCM Playback Volume */
+       { 5, "Mic Capture" }, /* FU, default PCM Capture Volume */
+       { 0 } /* terminator */
+};
+
 /* LineX FM Transmitter entry - needed to bypass controls bug */
 static struct usbmix_name_map linex_map[] = {
        /* 1: IT pcm */
@@ -273,6 +280,10 @@ static struct usbmix_ctl_map usbmix_ctl_maps[] = {
                .map = audigy2nx_map,
                .selector_map = audigy2nx_selectors,
        },
+       {
+               .id = USB_ID(0x041e, 0x3040),
+               .map = live24ext_map,
+       },
        {
                /* Hercules DJ Console (Windows Edition) */
                .id = USB_ID(0x06f8, 0xb000),
index 59410f437705a745c5fb5736761ad725517d190b..938dff5f9cef5dcab423514921f0c5a76a862e05 100644 (file)
@@ -1003,12 +1003,36 @@ YAMAHA_DEVICE(0x7010, "UB99"),
                }
        }
 },
+{
+       /* has ID 0x0049 when not in "Advanced Driver" mode */
+       USB_DEVICE(0x0582, 0x0047),
+       .driver_info = (unsigned long) & (const struct snd_usb_audio_quirk) {
+               /* .vendor_name = "EDIROL", */
+               /* .product_name = "UR-80", */
+               .ifnum = QUIRK_ANY_INTERFACE,
+               .type = QUIRK_COMPOSITE,
+               .data = (const struct snd_usb_audio_quirk[]) {
+                       /* in the 96 kHz modes, only interface 1 is there */
+                       {
+                               .ifnum = 1,
+                               .type = QUIRK_AUDIO_STANDARD_INTERFACE
+                       },
+                       {
+                               .ifnum = 2,
+                               .type = QUIRK_AUDIO_STANDARD_INTERFACE
+                       },
+                       {
+                               .ifnum = -1
+                       }
+               }
+       }
+},
 {
        /* has ID 0x004a when not in "Advanced Driver" mode */
        USB_DEVICE(0x0582, 0x0048),
        .driver_info = (unsigned long) & (const struct snd_usb_audio_quirk) {
-               .vendor_name = "EDIROL",
-               .product_name = "UR-80",
+               /* .vendor_name = "EDIROL", */
+               /* .product_name = "UR-80", */
                .ifnum = 0,
                .type = QUIRK_MIDI_FIXED_ENDPOINT,
                .data = & (const struct snd_usb_midi_endpoint_info) {
index b76b3dd9df25d48d878548238f040018c443b075..6495534e5bf61ad11698764f6c592c67cfcbbd16 100644 (file)
@@ -20,7 +20,6 @@
  *   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA
  */
 
-#include <sound/driver.h>
 #include <linux/interrupt.h>
 #include <linux/usb.h>
 #include <sound/core.h>
 int usX2Y_hwdep_pcm_new(struct snd_card *card);
 
 
-static struct page * snd_us428ctls_vm_nopage(struct vm_area_struct *area, unsigned long address, int *type)
+static int snd_us428ctls_vm_fault(struct vm_area_struct *area,
+                                 struct vm_fault *vmf)
 {
        unsigned long offset;
        struct page * page;
        void *vaddr;
 
-       snd_printdd("ENTER, start %lXh, ofs %lXh, pgoff %ld, addr %lXh\n",
+       snd_printdd("ENTER, start %lXh, pgoff %ld\n",
                   area->vm_start,
-                  address - area->vm_start,
-                  (address - area->vm_start) >> PAGE_SHIFT,
-                  address);
+                  vmf->pgoff);
        
-       offset = area->vm_pgoff << PAGE_SHIFT;
-       offset += address - area->vm_start;
-       snd_assert((offset % PAGE_SIZE) == 0, return NOPAGE_SIGBUS);
+       offset = vmf->pgoff << PAGE_SHIFT;
        vaddr = (char*)((struct usX2Ydev *)area->vm_private_data)->us428ctls_sharedmem + offset;
        page = virt_to_page(vaddr);
        get_page(page);
-       snd_printdd( "vaddr=%p made us428ctls_vm_nopage() return %p; offset=%lX\n", vaddr, page, offset);
+       vmf->page = page;
 
-       if (type)
-               *type = VM_FAULT_MINOR;
+       snd_printdd("vaddr=%p made us428ctls_vm_fault() page %p\n",
+                   vaddr, page);
 
-       return page;
+       return 0;
 }
 
 static struct vm_operations_struct us428ctls_vm_ops = {
-       .nopage = snd_us428ctls_vm_nopage,
+       .fault = snd_us428ctls_vm_fault,
 };
 
 static int snd_us428ctls_mmap(struct snd_hwdep * hw, struct file *filp, struct vm_area_struct *area)
index e011fcacce92c298e82b0463583de1a0ea72edb9..e5981a630314a328253c4a92f3c7641f5b191938 100644 (file)
  *   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA
 */
 
-#include <sound/driver.h>
 #include <linux/init.h>
 #include <linux/module.h>
 #include <linux/moduleparam.h>
index 48e9aa3f18c94ac7dc3ecd57b53bfd749e9c3779..9a608fa85155c530019efcc56d8ca86cfb0195fa 100644 (file)
@@ -31,7 +31,6 @@
  */
 
 
-#include <sound/driver.h>
 #include <linux/interrupt.h>
 #include <linux/usb.h>
 #include <sound/core.h>
index a5e7bcd7ca2ec0c5b486ba0b56c5f8e8f9d41a75..800b5cecfc8054ce38026e954a17f67b481ebfc8 100644 (file)
@@ -683,30 +683,24 @@ static void snd_usX2Y_hwdep_pcm_vm_close(struct vm_area_struct *area)
 }
 
 
-static struct page * snd_usX2Y_hwdep_pcm_vm_nopage(struct vm_area_struct *area, unsigned long address, int *type)
+static int snd_usX2Y_hwdep_pcm_vm_fault(struct vm_area_struct *area,
+                                       struct vm_fault *vmf)
 {
        unsigned long offset;
-       struct page *page;
        void *vaddr;
 
-       offset = area->vm_pgoff << PAGE_SHIFT;
-       offset += address - area->vm_start;
-       snd_assert((offset % PAGE_SIZE) == 0, return NOPAGE_OOM);
+       offset = vmf->pgoff << PAGE_SHIFT;
        vaddr = (char*)((struct usX2Ydev *)area->vm_private_data)->hwdep_pcm_shm + offset;
-       page = virt_to_page(vaddr);
-       get_page(page);
-
-       if (type)
-               *type = VM_FAULT_MINOR;
-
-       return page;
+       vmf->page = virt_to_page(vaddr);
+       get_page(vmf->page);
+       return 0;
 }
 
 
 static struct vm_operations_struct snd_usX2Y_hwdep_pcm_vm_ops = {
        .open = snd_usX2Y_hwdep_pcm_vm_open,
        .close = snd_usX2Y_hwdep_pcm_vm_close,
-       .nopage = snd_usX2Y_hwdep_pcm_vm_nopage,
+       .fault = snd_usX2Y_hwdep_pcm_vm_fault,
 };
 
 
This page took 0.931389 seconds and 5 git commands to generate.