summaryrefslogtreecommitdiff
path: root/sound
diff options
context:
space:
mode:
Diffstat (limited to 'sound')
-rw-r--r--sound/aoa/aoa.h2
-rw-r--r--sound/aoa/codecs/snd-aoa-codec-onyx.c12
-rw-r--r--sound/aoa/codecs/snd-aoa-codec-tas.c21
-rw-r--r--sound/aoa/fabrics/snd-aoa-fabric-layout.c2
-rw-r--r--sound/aoa/soundbus/i2sbus/i2sbus-core.c1
-rw-r--r--sound/aoa/soundbus/i2sbus/i2sbus-pcm.c10
-rw-r--r--sound/arm/aaci.c1
-rw-r--r--sound/arm/devdma.c1
-rw-r--r--sound/arm/pxa2xx-ac97.c2
-rw-r--r--sound/arm/pxa2xx-pcm.c1
-rw-r--r--sound/arm/sa11xx-uda1341.c1
-rw-r--r--sound/core/control.c52
-rw-r--r--sound/core/control_compat.c3
-rw-r--r--sound/core/device.c1
-rw-r--r--sound/core/hwdep.c1
-rw-r--r--sound/core/info.c1
-rw-r--r--sound/core/info_oss.c3
-rw-r--r--sound/core/init.c39
-rw-r--r--sound/core/isadma.c1
-rw-r--r--sound/core/memalloc.c1
-rw-r--r--sound/core/memory.c2
-rw-r--r--sound/core/misc.c1
-rw-r--r--sound/core/oss/copy.c1
-rw-r--r--sound/core/oss/io.c1
-rw-r--r--sound/core/oss/linear.c1
-rw-r--r--sound/core/oss/mixer_oss.c1
-rw-r--r--sound/core/oss/mulaw.c1
-rw-r--r--sound/core/oss/pcm_oss.c4
-rw-r--r--sound/core/oss/pcm_plugin.c1
-rw-r--r--sound/core/oss/rate.c1
-rw-r--r--sound/core/oss/route.c1
-rw-r--r--sound/core/pcm.c15
-rw-r--r--sound/core/pcm_compat.c1
-rw-r--r--sound/core/pcm_lib.c339
-rw-r--r--sound/core/pcm_memory.c1
-rw-r--r--sound/core/pcm_misc.c3
-rw-r--r--sound/core/pcm_native.c141
-rw-r--r--sound/core/pcm_timer.c1
-rw-r--r--sound/core/rawmidi.c41
-rw-r--r--sound/core/rtctimer.c1
-rw-r--r--sound/core/seq/Makefile8
-rw-r--r--sound/core/seq/instr/Makefile23
-rw-r--r--sound/core/seq/instr/ainstr_fm.c155
-rw-r--r--sound/core/seq/instr/ainstr_gf1.c359
-rw-r--r--sound/core/seq/instr/ainstr_iw.c623
-rw-r--r--sound/core/seq/instr/ainstr_simple.c215
-rw-r--r--sound/core/seq/oss/seq_oss.c1
-rw-r--r--sound/core/seq/oss/seq_oss_device.h1
-rw-r--r--sound/core/seq/seq.c1
-rw-r--r--sound/core/seq/seq_clientmgr.c6
-rw-r--r--sound/core/seq/seq_clientmgr.h2
-rw-r--r--sound/core/seq/seq_device.c1
-rw-r--r--sound/core/seq/seq_dummy.c1
-rw-r--r--sound/core/seq/seq_fifo.c1
-rw-r--r--sound/core/seq/seq_info.c1
-rw-r--r--sound/core/seq/seq_instr.c655
-rw-r--r--sound/core/seq/seq_lock.c1
-rw-r--r--sound/core/seq/seq_memory.c1
-rw-r--r--sound/core/seq/seq_midi.c1
-rw-r--r--sound/core/seq/seq_midi_emul.c8
-rw-r--r--sound/core/seq/seq_midi_event.c1
-rw-r--r--sound/core/seq/seq_ports.c1
-rw-r--r--sound/core/seq/seq_prioq.c1
-rw-r--r--sound/core/seq/seq_queue.c1
-rw-r--r--sound/core/seq/seq_system.c1
-rw-r--r--sound/core/seq/seq_timer.c8
-rw-r--r--sound/core/seq/seq_timer.h7
-rw-r--r--sound/core/seq/seq_virmidi.c1
-rw-r--r--sound/core/sound.c1
-rw-r--r--sound/core/sound_oss.c2
-rw-r--r--sound/core/timer.c17
-rw-r--r--sound/drivers/Kconfig12
-rw-r--r--sound/drivers/Makefile2
-rw-r--r--sound/drivers/dummy.c1
-rw-r--r--sound/drivers/ml403-ac97cr.c1352
-rw-r--r--sound/drivers/mpu401/mpu401.c1
-rw-r--r--sound/drivers/mpu401/mpu401_uart.c1
-rw-r--r--sound/drivers/mtpav.c1
-rw-r--r--sound/drivers/mts64.c17
-rw-r--r--sound/drivers/opl3/opl3_lib.c5
-rw-r--r--sound/drivers/opl3/opl3_midi.c41
-rw-r--r--sound/drivers/opl3/opl3_oss.c135
-rw-r--r--sound/drivers/opl3/opl3_seq.c42
-rw-r--r--sound/drivers/opl3/opl3_synth.c180
-rw-r--r--sound/drivers/pcm-indirect2.c573
-rw-r--r--sound/drivers/pcm-indirect2.h140
-rw-r--r--sound/drivers/portman2x4.c3
-rw-r--r--sound/drivers/serial-u16550.c10
-rw-r--r--sound/drivers/virmidi.c1
-rw-r--r--sound/drivers/vx/vx_cmd.c1
-rw-r--r--sound/drivers/vx/vx_core.c1
-rw-r--r--sound/drivers/vx/vx_hwdep.c1
-rw-r--r--sound/drivers/vx/vx_mixer.c68
-rw-r--r--sound/drivers/vx/vx_pcm.c1
-rw-r--r--sound/drivers/vx/vx_uer.c1
-rw-r--r--sound/i2c/cs8427.c1
-rw-r--r--sound/i2c/i2c.c1
-rw-r--r--sound/i2c/l3/uda1341.c1
-rw-r--r--sound/i2c/other/ak4114.c1
-rw-r--r--sound/i2c/other/ak4117.c1
-rw-r--r--sound/i2c/other/ak4xxx-adda.c51
-rw-r--r--sound/i2c/other/pt2258.c3
-rw-r--r--sound/i2c/other/tea575x-tuner.c9
-rw-r--r--sound/i2c/tea6330t.c1
-rw-r--r--sound/isa/ad1816a/ad1816a.c54
-rw-r--r--sound/isa/ad1816a/ad1816a_lib.c1
-rw-r--r--sound/isa/ad1848/ad1848.c1
-rw-r--r--sound/isa/ad1848/ad1848_lib.c3
-rw-r--r--sound/isa/adlib.c1
-rw-r--r--sound/isa/als100.c50
-rw-r--r--sound/isa/azt2320.c51
-rw-r--r--sound/isa/cmi8330.c43
-rw-r--r--sound/isa/cs423x/cs4231.c1
-rw-r--r--sound/isa/cs423x/cs4231_lib.c2
-rw-r--r--sound/isa/cs423x/cs4236.c92
-rw-r--r--sound/isa/cs423x/cs4236_lib.c1
-rw-r--r--sound/isa/dt019x.c47
-rw-r--r--sound/isa/es1688/es1688.c1
-rw-r--r--sound/isa/es1688/es1688_lib.c1
-rw-r--r--sound/isa/es18xx.c61
-rw-r--r--sound/isa/gus/Makefile12
-rw-r--r--sound/isa/gus/gus_dma.c1
-rw-r--r--sound/isa/gus/gus_dram.c1
-rw-r--r--sound/isa/gus/gus_instr.c1
-rw-r--r--sound/isa/gus/gus_io.c1
-rw-r--r--sound/isa/gus/gus_irq.c1
-rw-r--r--sound/isa/gus/gus_main.c24
-rw-r--r--sound/isa/gus/gus_mem.c1
-rw-r--r--sound/isa/gus/gus_mem_proc.c1
-rw-r--r--sound/isa/gus/gus_mixer.c1
-rw-r--r--sound/isa/gus/gus_pcm.c1
-rw-r--r--sound/isa/gus/gus_reset.c1
-rw-r--r--sound/isa/gus/gus_sample.c165
-rw-r--r--sound/isa/gus/gus_simple.c634
-rw-r--r--sound/isa/gus/gus_synth.c314
-rw-r--r--sound/isa/gus/gus_timer.c1
-rw-r--r--sound/isa/gus/gus_uart.c1
-rw-r--r--sound/isa/gus/gus_volume.c1
-rw-r--r--sound/isa/gus/gusclassic.c1
-rw-r--r--sound/isa/gus/gusextreme.c1
-rw-r--r--sound/isa/gus/gusmax.c1
-rw-r--r--sound/isa/gus/interwave.c40
-rw-r--r--sound/isa/opl3sa2.c37
-rw-r--r--sound/isa/opti9xx/miro.c11
-rw-r--r--sound/isa/opti9xx/opti92x-ad1848.c51
-rw-r--r--sound/isa/sb/emu8000.c1
-rw-r--r--sound/isa/sb/emu8000_local.h1
-rw-r--r--sound/isa/sb/es968.c28
-rw-r--r--sound/isa/sb/sb16.c40
-rw-r--r--sound/isa/sb/sb16_csp.c4
-rw-r--r--sound/isa/sb/sb16_main.c1
-rw-r--r--sound/isa/sb/sb8.c1
-rw-r--r--sound/isa/sb/sb8_main.c1
-rw-r--r--sound/isa/sb/sb8_midi.c1
-rw-r--r--sound/isa/sb/sb_common.c1
-rw-r--r--sound/isa/sb/sb_mixer.c1
-rw-r--r--sound/isa/sc6000.c3
-rw-r--r--sound/isa/sgalaxy.c1
-rw-r--r--sound/isa/sscape.c1
-rw-r--r--sound/isa/wavefront/wavefront.c56
-rw-r--r--sound/isa/wavefront/wavefront_fx.c1
-rw-r--r--sound/isa/wavefront/wavefront_midi.c1
-rw-r--r--sound/isa/wavefront/wavefront_synth.c1
-rw-r--r--sound/last.c1
-rw-r--r--sound/mips/au1x00.c1
-rw-r--r--sound/parisc/harmony.c1
-rw-r--r--sound/pci/Kconfig56
-rw-r--r--sound/pci/Makefile3
-rw-r--r--sound/pci/ac97/ac97_codec.c1
-rw-r--r--sound/pci/ac97/ac97_patch.c11
-rw-r--r--sound/pci/ac97/ac97_patch.h2
-rw-r--r--sound/pci/ac97/ac97_pcm.c1
-rw-r--r--sound/pci/ac97/ac97_proc.c1
-rw-r--r--sound/pci/ac97/ak4531_codec.c1
-rw-r--r--sound/pci/ad1889.c7
-rw-r--r--sound/pci/ali5451/ali5451.c1
-rw-r--r--sound/pci/als300.c1
-rw-r--r--sound/pci/als4000.c1
-rw-r--r--sound/pci/atiixp.c5
-rw-r--r--sound/pci/atiixp_modem.c1
-rw-r--r--sound/pci/au88x0/au88x0.h1
-rw-r--r--sound/pci/au88x0/au88x0_core.c2
-rw-r--r--sound/pci/au88x0/au88x0_game.c1
-rw-r--r--sound/pci/au88x0/au88x0_mixer.c1
-rw-r--r--sound/pci/au88x0/au88x0_mpu401.c1
-rw-r--r--sound/pci/au88x0/au88x0_pcm.c1
-rw-r--r--sound/pci/azt3328.c1
-rw-r--r--sound/pci/bt87x.c1
-rw-r--r--sound/pci/ca0106/ca0106.h1
-rw-r--r--sound/pci/ca0106/ca0106_main.c1
-rw-r--r--sound/pci/ca0106/ca0106_mixer.c1
-rw-r--r--sound/pci/ca0106/ca0106_proc.c1
-rw-r--r--sound/pci/ca0106/ca_midi.c1
-rw-r--r--sound/pci/cmipci.c116
-rw-r--r--sound/pci/cs4281.c1
-rw-r--r--sound/pci/cs46xx/cs46xx.c1
-rw-r--r--sound/pci/cs46xx/cs46xx_lib.c102
-rw-r--r--sound/pci/cs46xx/dsp_spos.c1
-rw-r--r--sound/pci/cs46xx/dsp_spos_scb_lib.c1
-rw-r--r--sound/pci/cs5530.c1
-rw-r--r--sound/pci/cs5535audio/cs5535audio.c3
-rw-r--r--sound/pci/cs5535audio/cs5535audio_pcm.c5
-rw-r--r--sound/pci/cs5535audio/cs5535audio_pm.c1
-rw-r--r--sound/pci/echoaudio/darla20.c1
-rw-r--r--sound/pci/echoaudio/darla24.c1
-rw-r--r--sound/pci/echoaudio/echo3g.c1
-rw-r--r--sound/pci/echoaudio/echoaudio.c18
-rw-r--r--sound/pci/echoaudio/echoaudio.h2
-rw-r--r--sound/pci/echoaudio/gina20.c1
-rw-r--r--sound/pci/echoaudio/gina24.c1
-rw-r--r--sound/pci/echoaudio/indigo.c1
-rw-r--r--sound/pci/echoaudio/indigodj.c1
-rw-r--r--sound/pci/echoaudio/indigoio.c1
-rw-r--r--sound/pci/echoaudio/layla20.c1
-rw-r--r--sound/pci/echoaudio/layla24.c1
-rw-r--r--sound/pci/echoaudio/mia.c1
-rw-r--r--sound/pci/echoaudio/mona.c1
-rw-r--r--sound/pci/emu10k1/emu10k1.c1
-rw-r--r--sound/pci/emu10k1/emu10k1_callback.c15
-rw-r--r--sound/pci/emu10k1/emu10k1_main.c342
-rw-r--r--sound/pci/emu10k1/emu10k1_synth.c45
-rw-r--r--sound/pci/emu10k1/emu10k1_synth_local.h1
-rw-r--r--sound/pci/emu10k1/emu10k1x.c3
-rw-r--r--sound/pci/emu10k1/emufx.c9
-rw-r--r--sound/pci/emu10k1/emumixer.c276
-rw-r--r--sound/pci/emu10k1/emumpu401.c1
-rw-r--r--sound/pci/emu10k1/emupcm.c9
-rw-r--r--sound/pci/emu10k1/emuproc.c5
-rw-r--r--sound/pci/emu10k1/io.c60
-rw-r--r--sound/pci/emu10k1/irq.c9
-rw-r--r--sound/pci/emu10k1/memory.c1
-rw-r--r--sound/pci/emu10k1/p16v.c1
-rw-r--r--sound/pci/emu10k1/timer.c1
-rw-r--r--sound/pci/emu10k1/voice.c1
-rw-r--r--sound/pci/ens1370.c1
-rw-r--r--sound/pci/es1938.c28
-rw-r--r--sound/pci/es1968.c1
-rw-r--r--sound/pci/fm801.c23
-rw-r--r--sound/pci/hda/Makefile2
-rw-r--r--sound/pci/hda/hda_codec.c117
-rw-r--r--sound/pci/hda/hda_codec.h60
-rw-r--r--sound/pci/hda/hda_generic.c1
-rw-r--r--sound/pci/hda/hda_hwdep.c1
-rw-r--r--sound/pci/hda/hda_intel.c164
-rw-r--r--sound/pci/hda/hda_local.h18
-rw-r--r--sound/pci/hda/hda_proc.c361
-rw-r--r--sound/pci/hda/patch_analog.c190
-rw-r--r--sound/pci/hda/patch_atihdmi.c3
-rw-r--r--sound/pci/hda/patch_cmedia.c2
-rw-r--r--sound/pci/hda/patch_conexant.c445
-rw-r--r--sound/pci/hda/patch_realtek.c1830
-rw-r--r--sound/pci/hda/patch_si3054.c2
-rw-r--r--sound/pci/hda/patch_sigmatel.c1217
-rw-r--r--sound/pci/hda/patch_via.c679
-rw-r--r--sound/pci/hda/vmaster.c364
-rw-r--r--sound/pci/ice1712/Makefile2
-rw-r--r--sound/pci/ice1712/ak4xxx.c1
-rw-r--r--sound/pci/ice1712/amp.c1
-rw-r--r--sound/pci/ice1712/aureon.c133
-rw-r--r--sound/pci/ice1712/delta.c3
-rw-r--r--sound/pci/ice1712/ews.c92
-rw-r--r--sound/pci/ice1712/hoontech.c188
-rw-r--r--sound/pci/ice1712/ice1712.c2
-rw-r--r--sound/pci/ice1712/ice1712.h37
-rw-r--r--sound/pci/ice1712/ice1724.c9
-rw-r--r--sound/pci/ice1712/juli.c23
-rw-r--r--sound/pci/ice1712/phase.c96
-rw-r--r--sound/pci/ice1712/pontis.c1
-rw-r--r--sound/pci/ice1712/prodigy192.c297
-rw-r--r--sound/pci/ice1712/prodigy_hifi.c1210
-rw-r--r--sound/pci/ice1712/prodigy_hifi.h38
-rw-r--r--sound/pci/ice1712/revo.c24
-rw-r--r--sound/pci/ice1712/se.c774
-rw-r--r--sound/pci/ice1712/se.h15
-rw-r--r--sound/pci/ice1712/vt1720_mobo.c1
-rw-r--r--sound/pci/ice1712/wtm.c9
-rw-r--r--sound/pci/intel8x0.c2
-rw-r--r--sound/pci/intel8x0m.c1
-rw-r--r--sound/pci/korg1212/korg1212.c38
-rw-r--r--sound/pci/maestro3.c2
-rw-r--r--sound/pci/mixart/mixart.c1
-rw-r--r--sound/pci/mixart/mixart_core.c1
-rw-r--r--sound/pci/mixart/mixart_hwdep.c1
-rw-r--r--sound/pci/mixart/mixart_mixer.c125
-rw-r--r--sound/pci/nm256/nm256.c1
-rw-r--r--sound/pci/oxygen/Makefile9
-rw-r--r--sound/pci/oxygen/ak4396.h44
-rw-r--r--sound/pci/oxygen/cm9780.h63
-rw-r--r--sound/pci/oxygen/hifier.c207
-rw-r--r--sound/pci/oxygen/oxygen.c385
-rw-r--r--sound/pci/oxygen/oxygen.h190
-rw-r--r--sound/pci/oxygen/oxygen_io.c201
-rw-r--r--sound/pci/oxygen/oxygen_lib.c515
-rw-r--r--sound/pci/oxygen/oxygen_mixer.c794
-rw-r--r--sound/pci/oxygen/oxygen_pcm.c718
-rw-r--r--sound/pci/oxygen/oxygen_regs.h453
-rw-r--r--sound/pci/oxygen/virtuoso.c449
-rw-r--r--sound/pci/pcxhr/pcxhr.c1
-rw-r--r--sound/pci/pcxhr/pcxhr_core.c1
-rw-r--r--sound/pci/pcxhr/pcxhr_hwdep.c1
-rw-r--r--sound/pci/pcxhr/pcxhr_mixer.c72
-rw-r--r--sound/pci/riptide/riptide.c1
-rw-r--r--sound/pci/rme32.c1
-rw-r--r--sound/pci/rme96.c28
-rw-r--r--sound/pci/rme9652/hdsp.c19
-rw-r--r--sound/pci/rme9652/hdspm.c5
-rw-r--r--sound/pci/rme9652/rme9652.c5
-rw-r--r--sound/pci/sis7019.c1460
-rw-r--r--sound/pci/sis7019.h342
-rw-r--r--sound/pci/sonicvibes.c1
-rw-r--r--sound/pci/trident/Makefile10
-rw-r--r--sound/pci/trident/trident.c8
-rw-r--r--sound/pci/trident/trident_main.c71
-rw-r--r--sound/pci/trident/trident_memory.c1
-rw-r--r--sound/pci/trident/trident_synth.c1024
-rw-r--r--sound/pci/via82xx.c13
-rw-r--r--sound/pci/via82xx_modem.c1
-rw-r--r--sound/pci/vx222/vx222.c1
-rw-r--r--sound/pci/vx222/vx222_ops.c10
-rw-r--r--sound/pci/ymfpci/ymfpci.c1
-rw-r--r--sound/pci/ymfpci/ymfpci_main.c5
-rw-r--r--sound/pcmcia/pdaudiocf/pdaudiocf.c3
-rw-r--r--sound/pcmcia/pdaudiocf/pdaudiocf_core.c1
-rw-r--r--sound/pcmcia/pdaudiocf/pdaudiocf_irq.c1
-rw-r--r--sound/pcmcia/pdaudiocf/pdaudiocf_pcm.c1
-rw-r--r--sound/pcmcia/vx/vxp_mixer.c12
-rw-r--r--sound/pcmcia/vx/vxp_ops.c1
-rw-r--r--sound/pcmcia/vx/vxpocket.c1
-rw-r--r--sound/ppc/awacs.c21
-rw-r--r--sound/ppc/beep.c8
-rw-r--r--sound/ppc/burgundy.c12
-rw-r--r--sound/ppc/daca.c18
-rw-r--r--sound/ppc/keywest.c1
-rw-r--r--sound/ppc/pmac.c110
-rw-r--r--sound/ppc/powermac.c1
-rw-r--r--sound/ppc/snd_ps3.c2
-rw-r--r--sound/ppc/tumbler.c56
-rw-r--r--sound/sh/aica.c9
-rw-r--r--sound/soc/Kconfig1
-rw-r--r--sound/soc/Makefile2
-rw-r--r--sound/soc/at91/at91-pcm.c1
-rw-r--r--sound/soc/at91/at91-ssc.c1
-rw-r--r--sound/soc/at91/eti_b1_wm8731.c1
-rw-r--r--sound/soc/codecs/Kconfig3
-rw-r--r--sound/soc/codecs/Makefile2
-rw-r--r--sound/soc/codecs/ac97.c1
-rw-r--r--sound/soc/codecs/cs4270.c262
-rw-r--r--sound/soc/codecs/tlv320aic3x.c1274
-rw-r--r--sound/soc/codecs/tlv320aic3x.h181
-rw-r--r--sound/soc/codecs/wm8731.c9
-rw-r--r--sound/soc/codecs/wm8750.c3
-rw-r--r--sound/soc/codecs/wm8753.c8
-rw-r--r--sound/soc/codecs/wm9712.c10
-rw-r--r--sound/soc/fsl/Kconfig20
-rw-r--r--sound/soc/fsl/Makefile6
-rw-r--r--sound/soc/fsl/fsl_dma.c841
-rw-r--r--sound/soc/fsl/fsl_dma.h149
-rw-r--r--sound/soc/fsl/fsl_ssi.c644
-rw-r--r--sound/soc/fsl/fsl_ssi.h224
-rw-r--r--sound/soc/fsl/mpc8610_hpcd.c631
-rw-r--r--sound/soc/pxa/Kconfig9
-rw-r--r--sound/soc/pxa/Makefile2
-rw-r--r--sound/soc/pxa/corgi.c1
-rw-r--r--sound/soc/pxa/e800_wm9712.c89
-rw-r--r--sound/soc/pxa/poodle.c1
-rw-r--r--sound/soc/pxa/pxa2xx-ac97.c1
-rw-r--r--sound/soc/pxa/pxa2xx-i2s.c1
-rw-r--r--sound/soc/pxa/pxa2xx-pcm.c1
-rw-r--r--sound/soc/pxa/spitz.c1
-rw-r--r--sound/soc/pxa/tosa.c1
-rw-r--r--sound/soc/s3c24xx/Kconfig11
-rw-r--r--sound/soc/s3c24xx/Makefile4
-rw-r--r--sound/soc/s3c24xx/ln2440sbc_alc650.c85
-rw-r--r--sound/soc/s3c24xx/neo1973_wm8753.c5
-rw-r--r--sound/soc/s3c24xx/s3c2412-i2s.c744
-rw-r--r--sound/soc/s3c24xx/s3c2412-i2s.h38
-rw-r--r--sound/soc/s3c24xx/s3c2443-ac97.c5
-rw-r--r--sound/soc/s3c24xx/s3c24xx-ac97.h6
-rw-r--r--sound/soc/s3c24xx/s3c24xx-i2s.c45
-rw-r--r--sound/soc/s3c24xx/s3c24xx-pcm.c49
-rw-r--r--sound/soc/s3c24xx/smdk2443_wm9710.c1
-rw-r--r--sound/soc/sh/dma-sh7760.c1
-rw-r--r--sound/soc/sh/hac.c14
-rw-r--r--sound/soc/sh/sh7760-ac97.c1
-rw-r--r--sound/soc/sh/ssi.c1
-rw-r--r--sound/soc/soc-core.c110
-rw-r--r--sound/soc/soc-dapm.c85
-rw-r--r--sound/sparc/amd7930.c3
-rw-r--r--sound/sparc/cs4231.c1
-rw-r--r--sound/sparc/dbri.c20
-rw-r--r--sound/spi/at73c213.c46
-rw-r--r--sound/synth/emux/emux.c1
-rw-r--r--sound/synth/emux/emux_hwdep.c1
-rw-r--r--sound/synth/emux/emux_oss.c1
-rw-r--r--sound/synth/emux/emux_proc.c1
-rw-r--r--sound/synth/emux/emux_voice.h1
-rw-r--r--sound/synth/emux/soundfont.c1
-rw-r--r--sound/synth/util_mem.c1
-rw-r--r--sound/usb/Kconfig14
-rw-r--r--sound/usb/caiaq/Makefile3
-rw-r--r--sound/usb/caiaq/caiaq-audio.c7
-rw-r--r--sound/usb/caiaq/caiaq-control.c315
-rw-r--r--sound/usb/caiaq/caiaq-control.h6
-rw-r--r--sound/usb/caiaq/caiaq-device.c92
-rw-r--r--sound/usb/caiaq/caiaq-device.h20
-rw-r--r--sound/usb/caiaq/caiaq-input.c238
-rw-r--r--sound/usb/caiaq/caiaq-midi.c3
-rw-r--r--sound/usb/usbaudio.c50
-rw-r--r--sound/usb/usbaudio.h1
-rw-r--r--sound/usb/usbmidi.c1
-rw-r--r--sound/usb/usbmixer.c34
-rw-r--r--sound/usb/usbmixer_maps.c11
-rw-r--r--sound/usb/usbquirks.h28
-rw-r--r--sound/usb/usx2y/usX2Yhwdep.c24
-rw-r--r--sound/usb/usx2y/usbusx2y.c1
-rw-r--r--sound/usb/usx2y/usbusx2yaudio.c1
-rw-r--r--sound/usb/usx2y/usx2yhwdeppcm.c20
417 files changed, 23255 insertions, 7771 deletions
diff --git a/sound/aoa/aoa.h b/sound/aoa/aoa.h
index 541b908..e087894 100644
--- a/sound/aoa/aoa.h
+++ b/sound/aoa/aoa.h
@@ -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>
diff --git a/sound/aoa/codecs/snd-aoa-codec-onyx.c b/sound/aoa/codecs/snd-aoa-codec-onyx.c
index 71e3f93..6a3837d 100644
--- a/sound/aoa/codecs/snd-aoa-codec-onyx.c
+++ b/sound/aoa/codecs/snd-aoa-codec-onyx.c
@@ -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;
diff --git a/sound/aoa/codecs/snd-aoa-codec-tas.c b/sound/aoa/codecs/snd-aoa-codec-tas.c
index 70c3416..7a16a33 100644
--- a/sound/aoa/codecs/snd-aoa-codec-tas.c
+++ b/sound/aoa/codecs/snd-aoa-codec-tas.c
@@ -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);
diff --git a/sound/aoa/fabrics/snd-aoa-fabric-layout.c b/sound/aoa/fabrics/snd-aoa-fabric-layout.c
index 8b2ba99..dea7abb 100644
--- a/sound/aoa/fabrics/snd-aoa-fabric-layout.c
+++ b/sound/aoa/fabrics/snd-aoa-fabric-layout.c
@@ -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 = { \
diff --git a/sound/aoa/soundbus/i2sbus/i2sbus-core.c b/sound/aoa/soundbus/i2sbus/i2sbus-core.c
index efb9441..e6beb92 100644
--- a/sound/aoa/soundbus/i2sbus/i2sbus-core.c
+++ b/sound/aoa/soundbus/i2sbus/i2sbus-core.c
@@ -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>
diff --git a/sound/aoa/soundbus/i2sbus/i2sbus-pcm.c b/sound/aoa/soundbus/i2sbus/i2sbus-pcm.c
index c6b42f9..59bacd3 100644
--- a/sound/aoa/soundbus/i2sbus/i2sbus-pcm.c
+++ b/sound/aoa/soundbus/i2sbus/i2sbus-pcm.c
@@ -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);
diff --git a/sound/arm/aaci.c b/sound/arm/aaci.c
index 3b73ba7..b0a4744 100644
--- a/sound/arm/aaci.c
+++ b/sound/arm/aaci.c
@@ -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>
diff --git a/sound/arm/devdma.c b/sound/arm/devdma.c
index ca3bf4e..9d1e666 100644
--- a/sound/arm/devdma.c
+++ b/sound/arm/devdma.c
@@ -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>
diff --git a/sound/arm/pxa2xx-ac97.c b/sound/arm/pxa2xx-ac97.c
index 55c6c82..5d86e68 100644
--- a/sound/arm/pxa2xx-ac97.c
+++ b/sound/arm/pxa2xx-ac97.c
@@ -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);
diff --git a/sound/arm/pxa2xx-pcm.c b/sound/arm/pxa2xx-pcm.c
index e8cf904..0ede9e4 100644
--- a/sound/arm/pxa2xx-pcm.c
+++ b/sound/arm/pxa2xx-pcm.c
@@ -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>
diff --git a/sound/arm/sa11xx-uda1341.c b/sound/arm/sa11xx-uda1341.c
index 81c64b0..0eff33c 100644
--- a/sound/arm/sa11xx-uda1341.c
+++ b/sound/arm/sa11xx-uda1341.c
@@ -59,7 +59,6 @@
*
***************************************************************************************************/
-#include <sound/driver.h>
#include <linux/module.h>
#include <linux/moduleparam.h>
#include <linux/init.h>
diff --git a/sound/core/control.c b/sound/core/control.c
index df0774c..01a1a5a 100644
--- a/sound/core/control.c
+++ b/sound/core/control.c
@@ -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);
diff --git a/sound/core/control_compat.c b/sound/core/control_compat.c
index 9311ca3..6101259 100644
--- a/sound/core/control_compat.c
+++ b/sound/core/control_compat.c
@@ -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)))
diff --git a/sound/core/device.c b/sound/core/device.c
index ea1a062..202dac0 100644
--- a/sound/core/device.c
+++ b/sound/core/device.c
@@ -19,7 +19,6 @@
*
*/
-#include <sound/driver.h>
#include <linux/slab.h>
#include <linux/time.h>
#include <linux/errno.h>
diff --git a/sound/core/hwdep.c b/sound/core/hwdep.c
index bfd9d18..6d6589f 100644
--- a/sound/core/hwdep.c
+++ b/sound/core/hwdep.c
@@ -19,7 +19,6 @@
*
*/
-#include <sound/driver.h>
#include <linux/major.h>
#include <linux/init.h>
#include <linux/slab.h>
diff --git a/sound/core/info.c b/sound/core/info.c
index 1ffd29b..9977ec2 100644
--- a/sound/core/info.c
+++ b/sound/core/info.c
@@ -19,7 +19,6 @@
*
*/
-#include <sound/driver.h>
#include <linux/init.h>
#include <linux/time.h>
#include <linux/smp_lock.h>
diff --git a/sound/core/info_oss.c b/sound/core/info_oss.c
index 435c939..e35789a 100644
--- a/sound/core/info_oss.c
+++ b/sound/core/info_oss.c
@@ -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;
diff --git a/sound/core/init.c b/sound/core/init.c
index 2cb7099..e3338d6 100644
--- a/sound/core/init.c
+++ b/sound/core/init.c
@@ -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;
}
diff --git a/sound/core/isadma.c b/sound/core/isadma.c
index eb173ce..79f0f16 100644
--- a/sound/core/isadma.c
+++ b/sound/core/isadma.c
@@ -26,7 +26,6 @@
#undef HAVE_REALLY_SLOW_DMA_CONTROLLER
-#include <sound/driver.h>
#include <sound/core.h>
#include <asm/dma.h>
diff --git a/sound/core/memalloc.c b/sound/core/memalloc.c
index 9b4992e..920e578 100644
--- a/sound/core/memalloc.c
+++ b/sound/core/memalloc.c
@@ -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;
}
}
diff --git a/sound/core/memory.c b/sound/core/memory.c
index 25b0f05..1161158 100644
--- a/sound/core/memory.c
+++ b/sound/core/memory.c
@@ -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
diff --git a/sound/core/misc.c b/sound/core/misc.c
index 6cabab8..102d1c3 100644
--- a/sound/core/misc.c
+++ b/sound/core/misc.c
@@ -19,7 +19,6 @@
*
*/
-#include <sound/driver.h>
#include <linux/init.h>
#include <linux/time.h>
#include <linux/ioport.h>
diff --git a/sound/core/oss/copy.c b/sound/core/oss/copy.c
index d6a04c2..9ded30d 100644
--- a/sound/core/oss/copy.c
+++ b/sound/core/oss/copy.c
@@ -19,7 +19,6 @@
*
*/
-#include <sound/driver.h>
#include <linux/time.h>
#include <sound/core.h>
#include <sound/pcm.h>
diff --git a/sound/core/oss/io.c b/sound/core/oss/io.c
index 3ece39f..f874f6c 100644
--- a/sound/core/oss/io.c
+++ b/sound/core/oss/io.c
@@ -19,7 +19,6 @@
*
*/
-#include <sound/driver.h>
#include <linux/time.h>
#include <sound/core.h>
#include <sound/pcm.h>
diff --git a/sound/core/oss/linear.c b/sound/core/oss/linear.c
index 06f96a3..da3dbd4 100644
--- a/sound/core/oss/linear.c
+++ b/sound/core/oss/linear.c
@@ -20,7 +20,6 @@
*
*/
-#include <sound/driver.h>
#include <linux/time.h>
#include <sound/core.h>
#include <sound/pcm.h>
diff --git a/sound/core/oss/mixer_oss.c b/sound/core/oss/mixer_oss.c
index c5a5ab9..75daed2 100644
--- a/sound/core/oss/mixer_oss.c
+++ b/sound/core/oss/mixer_oss.c
@@ -19,7 +19,6 @@
*
*/
-#include <sound/driver.h>
#include <linux/init.h>
#include <linux/slab.h>
#include <linux/time.h>
diff --git a/sound/core/oss/mulaw.c b/sound/core/oss/mulaw.c
index 848db82..77f9619 100644
--- a/sound/core/oss/mulaw.c
+++ b/sound/core/oss/mulaw.c
@@ -21,7 +21,6 @@
*
*/
-#include <sound/driver.h>
#include <linux/time.h>
#include <sound/core.h>
#include <sound/pcm.h>
diff --git a/sound/core/oss/pcm_oss.c b/sound/core/oss/pcm_oss.c
index d0c4ceb..4c601b1 100644
--- a/sound/core/oss/pcm_oss.c
+++ b/sound/core/oss/pcm_oss.c
@@ -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);
diff --git a/sound/core/oss/pcm_plugin.c b/sound/core/oss/pcm_plugin.c
index 14095a9..bec9413 100644
--- a/sound/core/oss/pcm_plugin.c
+++ b/sound/core/oss/pcm_plugin.c
@@ -24,7 +24,6 @@
#define PLUGIN_DEBUG
#endif
-#include <sound/driver.h>
#include <linux/slab.h>
#include <linux/time.h>
#include <linux/vmalloc.h>
diff --git a/sound/core/oss/rate.c b/sound/core/oss/rate.c
index 9eb2679..14dfb31 100644
--- a/sound/core/oss/rate.c
+++ b/sound/core/oss/rate.c
@@ -19,7 +19,6 @@
*
*/
-#include <sound/driver.h>
#include <linux/time.h>
#include <sound/core.h>
#include <sound/pcm.h>
diff --git a/sound/core/oss/route.c b/sound/core/oss/route.c
index de3ffde..da7ab7a 100644
--- a/sound/core/oss/route.c
+++ b/sound/core/oss/route.c
@@ -19,7 +19,6 @@
*
*/
-#include <sound/driver.h>
#include <linux/slab.h>
#include <linux/time.h>
#include <sound/core.h>
diff --git a/sound/core/pcm.c b/sound/core/pcm.c
index cf9b949..9dd9bc7 100644
--- a/sound/core/pcm.c
+++ b/sound/core/pcm.c
@@ -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;
diff --git a/sound/core/pcm_compat.c b/sound/core/pcm_compat.c
index 2b53979..49aa693 100644
--- a/sound/core/pcm_compat.c
+++ b/sound/core/pcm_compat.c
@@ -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:
diff --git a/sound/core/pcm_lib.c b/sound/core/pcm_lib.c
index 806f1fb..1533f03 100644
--- a/sound/core/pcm_lib.c
+++ b/sound/core/pcm_lib.c
@@ -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);
diff --git a/sound/core/pcm_memory.c b/sound/core/pcm_memory.c
index a13e38c..ff07b4a 100644
--- a/sound/core/pcm_memory.c
+++ b/sound/core/pcm_memory.c
@@ -19,7 +19,6 @@
*
*/
-#include <sound/driver.h>
#include <asm/io.h>
#include <linux/time.h>
#include <linux/init.h>
diff --git a/sound/core/pcm_misc.c b/sound/core/pcm_misc.c
index dd9aa51..89b7f54 100644
--- a/sound/core/pcm_misc.c
+++ b/sound/core/pcm_misc.c
@@ -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,
diff --git a/sound/core/pcm_native.c b/sound/core/pcm_native.c
index fb3dde4..6244911 100644
--- a/sound/core/pcm_native.c
+++ b/sound/core/pcm_native.c
@@ -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,
};
/*
diff --git a/sound/core/pcm_timer.c b/sound/core/pcm_timer.c
index 23aa9a2..033a024 100644
--- a/sound/core/pcm_timer.c
+++ b/sound/core/pcm_timer.c
@@ -19,7 +19,6 @@
*
*/
-#include <sound/driver.h>
#include <linux/time.h>
#include <sound/core.h>
#include <sound/pcm.h>
diff --git a/sound/core/rawmidi.c b/sound/core/rawmidi.c
index b8e700b..f7ea728 100644
--- a/sound/core/rawmidi.c
+++ b/sound/core/rawmidi.c
@@ -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;
diff --git a/sound/core/rtctimer.c b/sound/core/rtctimer.c
index 7cd5e8f..97b30fb 100644
--- a/sound/core/rtctimer.c
+++ b/sound/core/rtctimer.c
@@ -20,7 +20,6 @@
*
*/
-#include <sound/driver.h>
#include <linux/init.h>
#include <linux/interrupt.h>
#include <linux/moduleparam.h>
diff --git a/sound/core/seq/Makefile b/sound/core/seq/Makefile
index ceef14a..0695937 100644
--- a/sound/core/seq/Makefile
+++ b/sound/core/seq/Makefile
@@ -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
index 6089603..0000000
--- a/sound/core/seq/instr/Makefile
+++ /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
index f80fab8..0000000
--- a/sound/core/seq/instr/ainstr_fm.c
+++ /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
index 4940026..0000000
--- a/sound/core/seq/instr/ainstr_gf1.c
+++ /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
index 6c40eb7..0000000
--- a/sound/core/seq/instr/ainstr_iw.c
+++ /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
index 78f68be..0000000
--- a/sound/core/seq/instr/ainstr_simple.c
+++ /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);
diff --git a/sound/core/seq/oss/seq_oss.c b/sound/core/seq/oss/seq_oss.c
index bc09923..777796e 100644
--- a/sound/core/seq/oss/seq_oss.c
+++ b/sound/core/seq/oss/seq_oss.c
@@ -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>
diff --git a/sound/core/seq/oss/seq_oss_device.h b/sound/core/seq/oss/seq_oss_device.h
index 9a8567c..bf8d2b4 100644
--- a/sound/core/seq/oss/seq_oss_device.h
+++ b/sound/core/seq/oss/seq_oss_device.h
@@ -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>
diff --git a/sound/core/seq/seq.c b/sound/core/seq/seq.c
index 1878208..ee0f840 100644
--- a/sound/core/seq/seq.c
+++ b/sound/core/seq/seq.c
@@ -19,7 +19,6 @@
*
*/
-#include <sound/driver.h>
#include <linux/init.h>
#include <linux/moduleparam.h>
#include <sound/core.h>
diff --git a/sound/core/seq/seq_clientmgr.c b/sound/core/seq/seq_clientmgr.c
index 2e3fa25..f97c1ba 100644
--- a/sound/core/seq/seq_clientmgr.c
+++ b/sound/core/seq/seq_clientmgr.c
@@ -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;
}
diff --git a/sound/core/seq/seq_clientmgr.h b/sound/core/seq/seq_clientmgr.h
index 5e04e20..20f0a72 100644
--- a/sound/core/seq/seq_clientmgr.h
+++ b/sound/core/seq/seq_clientmgr.h
@@ -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
diff --git a/sound/core/seq/seq_device.c b/sound/core/seq/seq_device.c
index 37852cd..155dc7d 100644
--- a/sound/core/seq/seq_device.c
+++ b/sound/core/seq/seq_device.c
@@ -36,7 +36,6 @@
*
*/
-#include <sound/driver.h>
#include <linux/init.h>
#include <sound/core.h>
#include <sound/info.h>
diff --git a/sound/core/seq/seq_dummy.c b/sound/core/seq/seq_dummy.c
index e55488d..f3bdc54 100644
--- a/sound/core/seq/seq_dummy.c
+++ b/sound/core/seq/seq_dummy.c
@@ -18,7 +18,6 @@
*
*/
-#include <sound/driver.h>
#include <linux/init.h>
#include <linux/slab.h>
#include <linux/moduleparam.h>
diff --git a/sound/core/seq/seq_fifo.c b/sound/core/seq/seq_fifo.c
index 6b055ae..3a94ed0 100644
--- a/sound/core/seq/seq_fifo.c
+++ b/sound/core/seq/seq_fifo.c
@@ -19,7 +19,6 @@
*
*/
-#include <sound/driver.h>
#include <sound/core.h>
#include <linux/slab.h>
#include "seq_fifo.h"
diff --git a/sound/core/seq/seq_info.c b/sound/core/seq/seq_info.c
index 8a7fe5c..201f810 100644
--- a/sound/core/seq/seq_info.c
+++ b/sound/core/seq/seq_info.c
@@ -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
index 9a6fd56..0000000
--- a/sound/core/seq/seq_instr.c
+++ /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);
diff --git a/sound/core/seq/seq_lock.c b/sound/core/seq/seq_lock.c
index 1a34941..54f921e 100644
--- a/sound/core/seq/seq_lock.c
+++ b/sound/core/seq/seq_lock.c
@@ -19,7 +19,6 @@
*
*/
-#include <sound/driver.h>
#include <sound/core.h>
#include "seq_lock.h"
diff --git a/sound/core/seq/seq_memory.c b/sound/core/seq/seq_memory.c
index a72a194..0cf6ac4 100644
--- a/sound/core/seq/seq_memory.c
+++ b/sound/core/seq/seq_memory.c
@@ -20,7 +20,6 @@
*
*/
-#include <sound/driver.h>
#include <linux/init.h>
#include <linux/slab.h>
#include <linux/vmalloc.h>
diff --git a/sound/core/seq/seq_midi.c b/sound/core/seq/seq_midi.c
index 5929aaf..99b3536 100644
--- a/sound/core/seq/seq_midi.c
+++ b/sound/core/seq/seq_midi.c
@@ -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>
diff --git a/sound/core/seq/seq_midi_emul.c b/sound/core/seq/seq_midi_emul.c
index 17b3e6f..07c6631 100644
--- a/sound/core/seq/seq_midi_emul.c
+++ b/sound/core/seq/seq_midi_emul.c
@@ -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:
diff --git a/sound/core/seq/seq_midi_event.c b/sound/core/seq/seq_midi_event.c
index b6820a5..8284f17 100644
--- a/sound/core/seq/seq_midi_event.c
+++ b/sound/core/seq/seq_midi_event.c
@@ -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>
diff --git a/sound/core/seq/seq_ports.c b/sound/core/seq/seq_ports.c
index b6e23ad..1c32a53 100644
--- a/sound/core/seq/seq_ports.c
+++ b/sound/core/seq/seq_ports.c
@@ -20,7 +20,6 @@
*
*/
-#include <sound/driver.h>
#include <sound/core.h>
#include <linux/slab.h>
#include "seq_system.h"
diff --git a/sound/core/seq/seq_prioq.c b/sound/core/seq/seq_prioq.c
index 0744186..85969db 100644
--- a/sound/core/seq/seq_prioq.c
+++ b/sound/core/seq/seq_prioq.c
@@ -19,7 +19,6 @@
*
*/
-#include <sound/driver.h>
#include <linux/time.h>
#include <linux/slab.h>
#include <sound/core.h>
diff --git a/sound/core/seq/seq_queue.c b/sound/core/seq/seq_queue.c
index 9b87bb0..4a48c6e 100644
--- a/sound/core/seq/seq_queue.c
+++ b/sound/core/seq/seq_queue.c
@@ -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>
diff --git a/sound/core/seq/seq_system.c b/sound/core/seq/seq_system.c
index b201b76..77884e6 100644
--- a/sound/core/seq/seq_system.c
+++ b/sound/core/seq/seq_system.c
@@ -19,7 +19,6 @@
*
*/
-#include <sound/driver.h>
#include <linux/init.h>
#include <sound/core.h>
#include "seq_system.h"
diff --git a/sound/core/seq/seq_timer.c b/sound/core/seq/seq_timer.c
index 8716352..d8fcd62 100644
--- a/sound/core/seq/seq_timer.c
+++ b/sound/core/seq/seq_timer.c
@@ -20,20 +20,12 @@
*
*/
-#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
diff --git a/sound/core/seq/seq_timer.h b/sound/core/seq/seq_timer.h
index e9ee154..88dfb71 100644
--- a/sound/core/seq/seq_timer.h
+++ b/sound/core/seq/seq_timer.h
@@ -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
diff --git a/sound/core/seq/seq_virmidi.c b/sound/core/seq/seq_virmidi.c
index e11790f..86e7739 100644
--- a/sound/core/seq/seq_virmidi.c
+++ b/sound/core/seq/seq_virmidi.c
@@ -35,7 +35,6 @@
*
*/
-#include <sound/driver.h>
#include <linux/init.h>
#include <linux/wait.h>
#include <linux/slab.h>
diff --git a/sound/core/sound.c b/sound/core/sound.c
index 7b486c4..00cca4d 100644
--- a/sound/core/sound.c
+++ b/sound/core/sound.c
@@ -19,7 +19,6 @@
*
*/
-#include <sound/driver.h>
#include <linux/init.h>
#include <linux/slab.h>
#include <linux/time.h>
diff --git a/sound/core/sound_oss.c b/sound/core/sound_oss.c
index dc73313..7be5154 100644
--- a/sound/core/sound_oss.c
+++ b/sound/core/sound_oss.c
@@ -19,8 +19,6 @@
*
*/
-#include <sound/driver.h>
-
#ifdef CONFIG_SND_OSSEMUL
#if !defined(CONFIG_SOUND) && !(defined(MODULE) && defined(CONFIG_SOUND_MODULE))
diff --git a/sound/core/timer.c b/sound/core/timer.c
index e7dc56c..aece465 100644
--- a/sound/core/timer.c
+++ b/sound/core/timer.c
@@ -19,7 +19,6 @@
*
*/
-#include <sound/driver.h>
#include <linux/delay.h>
#include <linux/init.h>
#include <linux/slab.h>
@@ -44,11 +43,14 @@
#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;
diff --git a/sound/drivers/Kconfig b/sound/drivers/Kconfig
index 83529b0..75d4fe0 100644
--- a/sound/drivers/Kconfig
+++ b/sound/drivers/Kconfig
@@ -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
diff --git a/sound/drivers/Makefile b/sound/drivers/Makefile
index 80aeff5..8e55300 100644
--- a/sound/drivers/Makefile
+++ b/sound/drivers/Makefile
@@ -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/
diff --git a/sound/drivers/dummy.c b/sound/drivers/dummy.c
index e008f3c..a240eae 100644
--- a/sound/drivers/dummy.c
+++ b/sound/drivers/dummy.c
@@ -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
index 0000000..05a871a
--- /dev/null
+++ b/sound/drivers/ml403-ac97cr.c
@@ -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)
diff --git a/sound/drivers/mpu401/mpu401.c b/sound/drivers/mpu401/mpu401.c
index 1fc95da..5b996f3 100644
--- a/sound/drivers/mpu401/mpu401.c
+++ b/sound/drivers/mpu401/mpu401.c
@@ -20,7 +20,6 @@
*
*/
-#include <sound/driver.h>
#include <linux/init.h>
#include <linux/pnp.h>
#include <linux/err.h>
diff --git a/sound/drivers/mpu401/mpu401_uart.c b/sound/drivers/mpu401/mpu401_uart.c
index b57f2d5..5993864a 100644
--- a/sound/drivers/mpu401/mpu401_uart.c
+++ b/sound/drivers/mpu401/mpu401_uart.c
@@ -28,7 +28,6 @@
*
*/
-#include <sound/driver.h>
#include <asm/io.h>
#include <linux/delay.h>
#include <linux/init.h>
diff --git a/sound/drivers/mtpav.c b/sound/drivers/mtpav.c
index 40eb026..b5e1a71 100644
--- a/sound/drivers/mtpav.c
+++ b/sound/drivers/mtpav.c
@@ -50,7 +50,6 @@
*
*/
-#include <sound/driver.h>
#include <linux/init.h>
#include <linux/interrupt.h>
#include <linux/err.h>
diff --git a/sound/drivers/mts64.c b/sound/drivers/mts64.c
index dcc90f9..87ba1dd 100644
--- a/sound/drivers/mts64.c
+++ b/sound/drivers/mts64.c
@@ -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");
diff --git a/sound/drivers/opl3/opl3_lib.c b/sound/drivers/opl3/opl3_lib.c
index a2b9ce0..ebe4359 100644
--- a/sound/drivers/opl3/opl3_lib.c
+++ b/sound/drivers/opl3/opl3_lib.c
@@ -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,
diff --git a/sound/drivers/opl3/opl3_midi.c b/sound/drivers/opl3/opl3_midi.c
index 3557b6e..cebcb8b 100644
--- a/sound/drivers/opl3/opl3_midi.c
+++ b/sound/drivers/opl3/opl3_midi.c
@@ -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");
diff --git a/sound/drivers/opl3/opl3_oss.c b/sound/drivers/opl3/opl3_oss.c
index 5fd3a4c..239347f 100644
--- a/sound/drivers/opl3/opl3_oss.c
+++ b/sound/drivers/opl3/opl3_oss.c
@@ -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 */
diff --git a/sound/drivers/opl3/opl3_seq.c b/sound/drivers/opl3/opl3_seq.c
index 96762c9..2d33f53 100644
--- a/sound/drivers/opl3/opl3_seq.c
+++ b/sound/drivers/opl3/opl3_seq.c
@@ -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;
}
diff --git a/sound/drivers/opl3/opl3_synth.c b/sound/drivers/opl3/opl3_synth.c
index a4b3543..a7bf7a4 100644
--- a/sound/drivers/opl3/opl3_synth.c
+++ b/sound/drivers/opl3/opl3_synth.c
@@ -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
index 0000000..3c93c23
--- /dev/null
+++ b/sound/drivers/pcm-indirect2.c
@@ -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
index 0000000..2ea6e46
--- /dev/null
+++ b/sound/drivers/pcm-indirect2.h
@@ -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 */
diff --git a/sound/drivers/portman2x4.c b/sound/drivers/portman2x4.c
index 1b83287..b1c047e 100644
--- a/sound/drivers/portman2x4.c
+++ b/sound/drivers/portman2x4.c
@@ -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");
diff --git a/sound/drivers/serial-u16550.c b/sound/drivers/serial-u16550.c
index 65de3a7..d8aab9d 100644
--- a/sound/drivers/serial-u16550.c
+++ b/sound/drivers/serial-u16550.c
@@ -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 */
diff --git a/sound/drivers/virmidi.c b/sound/drivers/virmidi.c
index 915c867..f79e361 100644
--- a/sound/drivers/virmidi.c
+++ b/sound/drivers/virmidi.c
@@ -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>
diff --git a/sound/drivers/vx/vx_cmd.c b/sound/drivers/vx/vx_cmd.c
index 7a22134..9529e3b 100644
--- a/sound/drivers/vx/vx_cmd.c
+++ b/sound/drivers/vx/vx_cmd.c
@@ -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>
diff --git a/sound/drivers/vx/vx_core.c b/sound/drivers/vx/vx_core.c
index ed19bc1..9953886 100644
--- a/sound/drivers/vx/vx_core.c
+++ b/sound/drivers/vx/vx_core.c
@@ -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>
diff --git a/sound/drivers/vx/vx_hwdep.c b/sound/drivers/vx/vx_hwdep.c
index 9a8154c..1dfe694 100644
--- a/sound/drivers/vx/vx_hwdep.c
+++ b/sound/drivers/vx/vx_hwdep.c
@@ -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>
diff --git a/sound/drivers/vx/vx_mixer.c b/sound/drivers/vx/vx_mixer.c
index b8fcd79..5a34732 100644
--- a/sound/drivers/vx/vx_mixer.c
+++ b/sound/drivers/vx/vx_mixer.c
@@ -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;
}
diff --git a/sound/drivers/vx/vx_pcm.c b/sound/drivers/vx/vx_pcm.c
index 7e65a10..fdbf865 100644
--- a/sound/drivers/vx/vx_pcm.c
+++ b/sound/drivers/vx/vx_pcm.c
@@ -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>
diff --git a/sound/drivers/vx/vx_uer.c b/sound/drivers/vx/vx_uer.c
index 7400306..fb8932a 100644
--- a/sound/drivers/vx/vx_uer.c
+++ b/sound/drivers/vx/vx_uer.c
@@ -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>
diff --git a/sound/i2c/cs8427.c b/sound/i2c/cs8427.c
index 744366b..e57e9cb 100644
--- a/sound/i2c/cs8427.c
+++ b/sound/i2c/cs8427.c
@@ -20,7 +20,6 @@
*
*/
-#include <sound/driver.h>
#include <linux/slab.h>
#include <linux/delay.h>
#include <linux/init.h>
diff --git a/sound/i2c/i2c.c b/sound/i2c/i2c.c
index 1e58a96..b1e74e4 100644
--- a/sound/i2c/i2c.c
+++ b/sound/i2c/i2c.c
@@ -20,7 +20,6 @@
*
*/
-#include <sound/driver.h>
#include <linux/init.h>
#include <linux/slab.h>
#include <linux/string.h>
diff --git a/sound/i2c/l3/uda1341.c b/sound/i2c/l3/uda1341.c
index b074fdd..bfa5d2c 100644
--- a/sound/i2c/l3/uda1341.c
+++ b/sound/i2c/l3/uda1341.c
@@ -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>
diff --git a/sound/i2c/other/ak4114.c b/sound/i2c/other/ak4114.c
index facde46..15061bd 100644
--- a/sound/i2c/other/ak4114.c
+++ b/sound/i2c/other/ak4114.c
@@ -20,7 +20,6 @@
*
*/
-#include <sound/driver.h>
#include <linux/slab.h>
#include <linux/delay.h>
#include <sound/core.h>
diff --git a/sound/i2c/other/ak4117.c b/sound/i2c/other/ak4117.c
index ee1585a..f350835 100644
--- a/sound/i2c/other/ak4117.c
+++ b/sound/i2c/other/ak4117.c
@@ -20,7 +20,6 @@
*
*/
-#include <sound/driver.h>
#include <linux/slab.h>
#include <linux/delay.h>
#include <sound/core.h>
diff --git a/sound/i2c/other/ak4xxx-adda.c b/sound/i2c/other/ak4xxx-adda.c
index de03f68..35fbbf2 100644
--- a/sound/i2c/other/ak4xxx-adda.c
+++ b/sound/i2c/other/ak4xxx-adda.c
@@ -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;
diff --git a/sound/i2c/other/pt2258.c b/sound/i2c/other/pt2258.c
index 00c83d8..797d3a6 100644
--- a/sound/i2c/other/pt2258.c
+++ b/sound/i2c/other/pt2258.c
@@ -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;
diff --git a/sound/i2c/other/tea575x-tuner.c b/sound/i2c/other/tea575x-tuner.c
index 37c47fb..87e3aef 100644
--- a/sound/i2c/other/tea575x-tuner.c
+++ b/sound/i2c/other/tea575x-tuner.c
@@ -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)
diff --git a/sound/i2c/tea6330t.c b/sound/i2c/tea6330t.c
index 9bab744..0e3a9f2 100644
--- a/sound/i2c/tea6330t.c
+++ b/sound/i2c/tea6330t.c
@@ -20,7 +20,6 @@
*
*/
-#include <sound/driver.h>
#include <linux/init.h>
#include <linux/slab.h>
#include <sound/core.h>
diff --git a/sound/isa/ad1816a/ad1816a.c b/sound/isa/ad1816a/ad1816a.c
index fc88a31..68f1260 100644
--- a/sound/isa/ad1816a/ad1816a.c
+++ b/sound/isa/ad1816a/ad1816a.c
@@ -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;
}
diff --git a/sound/isa/ad1816a/ad1816a_lib.c b/sound/isa/ad1816a/ad1816a_lib.c
index cf18fe4..4b8dfe2 100644
--- a/sound/isa/ad1816a/ad1816a_lib.c
+++ b/sound/isa/ad1816a/ad1816a_lib.c
@@ -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>
diff --git a/sound/isa/ad1848/ad1848.c b/sound/isa/ad1848/ad1848.c
index a4710b5..5f5271e 100644
--- a/sound/isa/ad1848/ad1848.c
+++ b/sound/isa/ad1848/ad1848.c
@@ -21,7 +21,6 @@
*
*/
-#include <sound/driver.h>
#include <linux/init.h>
#include <linux/err.h>
#include <linux/isa.h>
diff --git a/sound/isa/ad1848/ad1848_lib.c b/sound/isa/ad1848/ad1848_lib.c
index a901cd1..630c90f 100644
--- a/sound/isa/ad1848/ad1848_lib.c
+++ b/sound/isa/ad1848/ad1848_lib.c
@@ -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)
diff --git a/sound/isa/adlib.c b/sound/isa/adlib.c
index d687207..efa8c80 100644
--- a/sound/isa/adlib.c
+++ b/sound/isa/adlib.c
@@ -2,7 +2,6 @@
* AdLib FM card driver.
*/
-#include <sound/driver.h>
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/isa.h>
diff --git a/sound/isa/als100.c b/sound/isa/als100.c
index f2bcfb2..f1ce30f 100644
--- a/sound/isa/als100.c
+++ b/sound/isa/als100.c
@@ -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;
}
diff --git a/sound/isa/azt2320.c b/sound/isa/azt2320.c
index b615538..154e728 100644
--- a/sound/isa/azt2320.c
+++ b/sound/isa/azt2320.c
@@ -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;
}
diff --git a/sound/isa/cmi8330.c b/sound/isa/cmi8330.c
index f471f8a..4d198ec 100644
--- a/sound/isa/cmi8330.c
+++ b/sound/isa/cmi8330.c
@@ -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
diff --git a/sound/isa/cs423x/cs4231.c b/sound/isa/cs423x/cs4231.c
index 13db684..e9462b9 100644
--- a/sound/isa/cs423x/cs4231.c
+++ b/sound/isa/cs423x/cs4231.c
@@ -20,7 +20,6 @@
*
*/
-#include <sound/driver.h>
#include <linux/init.h>
#include <linux/err.h>
#include <linux/isa.h>
diff --git a/sound/isa/cs423x/cs4231_lib.c b/sound/isa/cs423x/cs4231_lib.c
index a5eb965..0aa8649 100644
--- a/sound/isa/cs423x/cs4231_lib.c
+++ b/sound/isa/cs423x/cs4231_lib.c
@@ -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)
diff --git a/sound/isa/cs423x/cs4236.c b/sound/isa/cs423x/cs4236.c
index 5784b43..dbe63db4 100644
--- a/sound/isa/cs423x/cs4236.c
+++ b/sound/isa/cs423x/cs4236.c
@@ -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 */
diff --git a/sound/isa/cs423x/cs4236_lib.c b/sound/isa/cs423x/cs4236_lib.c
index 6bd0644..de71910 100644
--- a/sound/isa/cs423x/cs4236_lib.c
+++ b/sound/isa/cs423x/cs4236_lib.c
@@ -79,7 +79,6 @@
*
*/
-#include <sound/driver.h>
#include <asm/io.h>
#include <linux/delay.h>
#include <linux/init.h>
diff --git a/sound/isa/dt019x.c b/sound/isa/dt019x.c
index ce57d52..a0242c3 100644
--- a/sound/isa/dt019x.c
+++ b/sound/isa/dt019x.c
@@ -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;
}
diff --git a/sound/isa/es1688/es1688.c b/sound/isa/es1688/es1688.c
index 74bbc92..f88639e 100644
--- a/sound/isa/es1688/es1688.c
+++ b/sound/isa/es1688/es1688.c
@@ -19,7 +19,6 @@
*
*/
-#include <sound/driver.h>
#include <linux/init.h>
#include <linux/err.h>
#include <linux/isa.h>
diff --git a/sound/isa/es1688/es1688_lib.c b/sound/isa/es1688/es1688_lib.c
index 5c26d49..1e1e575 100644
--- a/sound/isa/es1688/es1688_lib.c
+++ b/sound/isa/es1688/es1688_lib.c
@@ -19,7 +19,6 @@
*
*/
-#include <sound/driver.h>
#include <linux/init.h>
#include <linux/interrupt.h>
#include <linux/delay.h>
diff --git a/sound/isa/es18xx.c b/sound/isa/es18xx.c
index c1af28f..90498e4 100644
--- a/sound/isa/es18xx.c
+++ b/sound/isa/es18xx.c
@@ -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 */
diff --git a/sound/isa/gus/Makefile b/sound/isa/gus/Makefile
index df3d59f2..6cd4ee0 100644
--- a/sound/isa/gus/Makefile
+++ b/sound/isa/gus/Makefile
@@ -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))
diff --git a/sound/isa/gus/gus_dma.c b/sound/isa/gus/gus_dma.c
index fc90514..f45f611 100644
--- a/sound/isa/gus/gus_dma.c
+++ b/sound/isa/gus/gus_dma.c
@@ -19,7 +19,6 @@
*
*/
-#include <sound/driver.h>
#include <asm/dma.h>
#include <linux/slab.h>
#include <sound/core.h>
diff --git a/sound/isa/gus/gus_dram.c b/sound/isa/gus/gus_dram.c
index 9eaa932..fd2e2e2 100644
--- a/sound/isa/gus/gus_dram.c
+++ b/sound/isa/gus/gus_dram.c
@@ -19,7 +19,6 @@
*
*/
-#include <sound/driver.h>
#include <linux/time.h>
#include <sound/core.h>
#include <sound/gus.h>
diff --git a/sound/isa/gus/gus_instr.c b/sound/isa/gus/gus_instr.c
index bf137ea..4dc9caf 100644
--- a/sound/isa/gus/gus_instr.c
+++ b/sound/isa/gus/gus_instr.c
@@ -19,7 +19,6 @@
*
*/
-#include <sound/driver.h>
#include <linux/time.h>
#include <sound/core.h>
#include <sound/gus.h>
diff --git a/sound/isa/gus/gus_io.c b/sound/isa/gus/gus_io.c
index 3d4f899..ca79878 100644
--- a/sound/isa/gus/gus_io.c
+++ b/sound/isa/gus/gus_io.c
@@ -19,7 +19,6 @@
*
*/
-#include <sound/driver.h>
#include <linux/delay.h>
#include <linux/time.h>
#include <sound/core.h>
diff --git a/sound/isa/gus/gus_irq.c b/sound/isa/gus/gus_irq.c
index cd9a6f1c..041894d 100644
--- a/sound/isa/gus/gus_irq.c
+++ b/sound/isa/gus/gus_irq.c
@@ -19,7 +19,6 @@
*
*/
-#include <sound/driver.h>
#include <sound/core.h>
#include <sound/info.h>
#include <sound/gus.h>
diff --git a/sound/isa/gus/gus_main.c b/sound/isa/gus/gus_main.c
index b14d5d6..cccc16c 100644
--- a/sound/isa/gus/gus_main.c
+++ b/sound/isa/gus/gus_main.c
@@ -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;
diff --git a/sound/isa/gus/gus_mem.c b/sound/isa/gus/gus_mem.c
index bcf4656..661205c 100644
--- a/sound/isa/gus/gus_mem.c
+++ b/sound/isa/gus/gus_mem.c
@@ -19,7 +19,6 @@
*
*/
-#include <sound/driver.h>
#include <linux/slab.h>
#include <linux/string.h>
#include <sound/core.h>
diff --git a/sound/isa/gus/gus_mem_proc.c b/sound/isa/gus/gus_mem_proc.c
index f69a447..2803e22 100644
--- a/sound/isa/gus/gus_mem_proc.c
+++ b/sound/isa/gus/gus_mem_proc.c
@@ -19,7 +19,6 @@
*
*/
-#include <sound/driver.h>
#include <linux/slab.h>
#include <sound/core.h>
#include <sound/gus.h>
diff --git a/sound/isa/gus/gus_mixer.c b/sound/isa/gus/gus_mixer.c
index a96253e..ebdb334 100644
--- a/sound/isa/gus/gus_mixer.c
+++ b/sound/isa/gus/gus_mixer.c
@@ -19,7 +19,6 @@
*
*/
-#include <sound/driver.h>
#include <linux/time.h>
#include <linux/wait.h>
#include <sound/core.h>
diff --git a/sound/isa/gus/gus_pcm.c b/sound/isa/gus/gus_pcm.c
index a7971f5..99731dc 100644
--- a/sound/isa/gus/gus_pcm.c
+++ b/sound/isa/gus/gus_pcm.c
@@ -25,7 +25,6 @@
*
*/
-#include <sound/driver.h>
#include <asm/dma.h>
#include <linux/slab.h>
#include <sound/core.h>
diff --git a/sound/isa/gus/gus_reset.c b/sound/isa/gus/gus_reset.c
index 20cfdb8..3d1fed0c 100644
--- a/sound/isa/gus/gus_reset.c
+++ b/sound/isa/gus/gus_reset.c
@@ -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
index cba0829..0000000
--- a/sound/isa/gus/gus_sample.c
+++ /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
index 39d121e..0000000
--- a/sound/isa/gus/gus_simple.c
+++ /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
index 2c20517..0000000
--- a/sound/isa/gus/gus_synth.c
+++ /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)
diff --git a/sound/isa/gus/gus_timer.c b/sound/isa/gus/gus_timer.c
index 99eac57..c537271 100644
--- a/sound/isa/gus/gus_timer.c
+++ b/sound/isa/gus/gus_timer.c
@@ -21,7 +21,6 @@
*
*/
-#include <sound/driver.h>
#include <linux/time.h>
#include <sound/core.h>
#include <sound/gus.h>
diff --git a/sound/isa/gus/gus_uart.c b/sound/isa/gus/gus_uart.c
index e6fd9b0..f0af3f7 100644
--- a/sound/isa/gus/gus_uart.c
+++ b/sound/isa/gus/gus_uart.c
@@ -19,7 +19,6 @@
*
*/
-#include <sound/driver.h>
#include <linux/delay.h>
#include <linux/interrupt.h>
#include <linux/time.h>
diff --git a/sound/isa/gus/gus_volume.c b/sound/isa/gus/gus_volume.c
index 71a6774..c3c028a 100644
--- a/sound/isa/gus/gus_volume.c
+++ b/sound/isa/gus/gus_volume.c
@@ -18,7 +18,6 @@
*
*/
-#include <sound/driver.h>
#include <linux/time.h>
#include <sound/core.h>
#include <sound/gus.h>
diff --git a/sound/isa/gus/gusclassic.c b/sound/isa/gus/gusclassic.c
index 29e422b..8f914b3 100644
--- a/sound/isa/gus/gusclassic.c
+++ b/sound/isa/gus/gusclassic.c
@@ -19,7 +19,6 @@
*
*/
-#include <sound/driver.h>
#include <linux/init.h>
#include <linux/err.h>
#include <linux/isa.h>
diff --git a/sound/isa/gus/gusextreme.c b/sound/isa/gus/gusextreme.c
index fc59536..da13185 100644
--- a/sound/isa/gus/gusextreme.c
+++ b/sound/isa/gus/gusextreme.c
@@ -19,7 +19,6 @@
*
*/
-#include <sound/driver.h>
#include <linux/init.h>
#include <linux/err.h>
#include <linux/isa.h>
diff --git a/sound/isa/gus/gusmax.c b/sound/isa/gus/gusmax.c
index 4922f5d..f87c623 100644
--- a/sound/isa/gus/gusmax.c
+++ b/sound/isa/gus/gusmax.c
@@ -19,7 +19,6 @@
*
*/
-#include <sound/driver.h>
#include <linux/init.h>
#include <linux/err.h>
#include <linux/isa.h>
diff --git a/sound/isa/gus/interwave.c b/sound/isa/gus/interwave.c
index 2091c50..ca0d7ac 100644
--- a/sound/isa/gus/interwave.c
+++ b/sound/isa/gus/interwave.c
@@ -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 */
diff --git a/sound/isa/opl3sa2.c b/sound/isa/opl3sa2.c
index 59af9ab..854a9f7 100644
--- a/sound/isa/opl3sa2.c
+++ b/sound/isa/opl3sa2.c
@@ -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 */
diff --git a/sound/isa/opti9xx/miro.c b/sound/isa/opti9xx/miro.c
index d295936..2a1e2f5 100644
--- a/sound/isa/opti9xx/miro.c
+++ b/sound/isa/opti9xx/miro.c
@@ -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;
diff --git a/sound/isa/opti9xx/opti92x-ad1848.c b/sound/isa/opti9xx/opti92x-ad1848.c
index ee1a824..fe1afc1 100644
--- a/sound/isa/opti9xx/opti92x-ad1848.c
+++ b/sound/isa/opti9xx/opti92x-ad1848.c
@@ -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 */
diff --git a/sound/isa/sb/emu8000.c b/sound/isa/sb/emu8000.c
index 4eea84c..b35be7d 100644
--- a/sound/isa/sb/emu8000.c
+++ b/sound/isa/sb/emu8000.c
@@ -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>
diff --git a/sound/isa/sb/emu8000_local.h b/sound/isa/sb/emu8000_local.h
index 2ac77f1..7e87c34 100644
--- a/sound/isa/sb/emu8000_local.h
+++ b/sound/isa/sb/emu8000_local.h
@@ -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>
diff --git a/sound/isa/sb/es968.c b/sound/isa/sb/es968.c
index d4b2187..c8c8e21 100644
--- a/sound/isa/sb/es968.c
+++ b/sound/isa/sb/es968.c
@@ -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;
}
diff --git a/sound/isa/sb/sb16.c b/sound/isa/sb/sb16.c
index e7f9edd..2c201f7 100644
--- a/sound/isa/sb/sb16.c
+++ b/sound/isa/sb/sb16.c
@@ -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;
}
diff --git a/sound/isa/sb/sb16_csp.c b/sound/isa/sb/sb16_csp.c
index 3682059..bed29ca 100644
--- a/sound/isa/sb/sb16_csp.c
+++ b/sound/isa/sb/sb16_csp.c
@@ -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)
diff --git a/sound/isa/sb/sb16_main.c b/sound/isa/sb/sb16_main.c
index c06754f..f7e8192 100644
--- a/sound/isa/sb/sb16_main.c
+++ b/sound/isa/sb/sb16_main.c
@@ -33,7 +33,6 @@
*
*/
-#include <sound/driver.h>
#include <asm/io.h>
#include <asm/dma.h>
#include <linux/init.h>
diff --git a/sound/isa/sb/sb8.c b/sound/isa/sb/sb8.c
index f933aef..336a342 100644
--- a/sound/isa/sb/sb8.c
+++ b/sound/isa/sb/sb8.c
@@ -19,7 +19,6 @@
*
*/
-#include <sound/driver.h>
#include <linux/init.h>
#include <linux/err.h>
#include <linux/isa.h>
diff --git a/sound/isa/sb/sb8_main.c b/sound/isa/sb/sb8_main.c
index bee894b..6304c3a 100644
--- a/sound/isa/sb/sb8_main.c
+++ b/sound/isa/sb/sb8_main.c
@@ -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>
diff --git a/sound/isa/sb/sb8_midi.c b/sound/isa/sb/sb8_midi.c
index e56e563..988a8b7 100644
--- a/sound/isa/sb/sb8_midi.c
+++ b/sound/isa/sb/sb8_midi.c
@@ -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>
diff --git a/sound/isa/sb/sb_common.c b/sound/isa/sb/sb_common.c
index 176193c..d63c1af 100644
--- a/sound/isa/sb/sb_common.c
+++ b/sound/isa/sb/sb_common.c
@@ -20,7 +20,6 @@
*
*/
-#include <sound/driver.h>
#include <linux/delay.h>
#include <linux/init.h>
#include <linux/interrupt.h>
diff --git a/sound/isa/sb/sb_mixer.c b/sound/isa/sb/sb_mixer.c
index 03241cd..91d1422 100644
--- a/sound/isa/sb/sb_mixer.c
+++ b/sound/isa/sb/sb_mixer.c
@@ -19,7 +19,6 @@
*
*/
-#include <sound/driver.h>
#include <asm/io.h>
#include <linux/delay.h>
#include <linux/time.h>
diff --git a/sound/isa/sc6000.c b/sound/isa/sc6000.c
index 94daf83..da3d152 100644
--- a/sound/isa/sc6000.c
+++ b/sound/isa/sc6000.c
@@ -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;
}
diff --git a/sound/isa/sgalaxy.c b/sound/isa/sgalaxy.c
index 922519d..a07274e 100644
--- a/sound/isa/sgalaxy.c
+++ b/sound/isa/sgalaxy.c
@@ -21,7 +21,6 @@
*
*/
-#include <sound/driver.h>
#include <linux/init.h>
#include <linux/err.h>
#include <linux/isa.h>
diff --git a/sound/isa/sscape.c b/sound/isa/sscape.c
index 1cb921d..06ad786 100644
--- a/sound/isa/sscape.c
+++ b/sound/isa/sscape.c
@@ -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>
diff --git a/sound/isa/wavefront/wavefront.c b/sound/isa/wavefront/wavefront.c
index 83c2fc4..3a6c6fe 100644
--- a/sound/isa/wavefront/wavefront.c
+++ b/sound/isa/wavefront/wavefront.c
@@ -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;
}
diff --git a/sound/isa/wavefront/wavefront_fx.c b/sound/isa/wavefront/wavefront_fx.c
index fc95a87..2efaa7f 100644
--- a/sound/isa/wavefront/wavefront_fx.c
+++ b/sound/isa/wavefront/wavefront_fx.c
@@ -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>
diff --git a/sound/isa/wavefront/wavefront_midi.c b/sound/isa/wavefront/wavefront_midi.c
index cb34600..a33384a 100644
--- a/sound/isa/wavefront/wavefront_midi.c
+++ b/sound/isa/wavefront/wavefront_midi.c
@@ -47,7 +47,6 @@
*
*/
-#include <sound/driver.h>
#include <asm/io.h>
#include <linux/init.h>
#include <linux/time.h>
diff --git a/sound/isa/wavefront/wavefront_synth.c b/sound/isa/wavefront/wavefront_synth.c
index a1ebb7c..95eeca1 100644
--- a/sound/isa/wavefront/wavefront_synth.c
+++ b/sound/isa/wavefront/wavefront_synth.c
@@ -20,7 +20,6 @@
*
*/
-#include <sound/driver.h>
#include <asm/io.h>
#include <linux/interrupt.h>
#include <linux/init.h>
diff --git a/sound/last.c b/sound/last.c
index 282b0cd..bdd0857 100644
--- a/sound/last.c
+++ b/sound/last.c
@@ -20,7 +20,6 @@
*/
#define SNDRV_MAIN_OBJECT_FILE
-#include <sound/driver.h>
#include <linux/init.h>
#include <sound/core.h>
diff --git a/sound/mips/au1x00.c b/sound/mips/au1x00.c
index 24460a5..ee0741f 100644
--- a/sound/mips/au1x00.c
+++ b/sound/mips/au1x00.c
@@ -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>
diff --git a/sound/parisc/harmony.c b/sound/parisc/harmony.c
index ff705c6..99f5483 100644
--- a/sound/parisc/harmony.c
+++ b/sound/parisc/harmony.c
@@ -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>
diff --git a/sound/pci/Kconfig b/sound/pci/Kconfig
index 356bf21..812085d 100644
--- a/sound/pci/Kconfig
+++ b/sound/pci/Kconfig
@@ -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
diff --git a/sound/pci/Makefile b/sound/pci/Makefile
index 09ddc82..2d42fd2 100644
--- a/sound/pci/Makefile
+++ b/sound/pci/Makefile
@@ -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/ \
diff --git a/sound/pci/ac97/ac97_codec.c b/sound/pci/ac97/ac97_codec.c
index 6a9966d..45fd290 100644
--- a/sound/pci/ac97/ac97_codec.c
+++ b/sound/pci/ac97/ac97_codec.c
@@ -22,7 +22,6 @@
*
*/
-#include <sound/driver.h>
#include <linux/delay.h>
#include <linux/init.h>
#include <linux/slab.h>
diff --git a/sound/pci/ac97/ac97_patch.c b/sound/pci/ac97/ac97_patch.c
index 98c8b72..50c637e 100644
--- a/sound/pci/ac97/ac97_patch.c
+++ b/sound/pci/ac97/ac97_patch.c
@@ -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;
diff --git a/sound/pci/ac97/ac97_patch.h b/sound/pci/ac97/ac97_patch.h
index 9cccc27..47bf8df 100644
--- a/sound/pci/ac97/ac97_patch.h
+++ b/sound/pci/ac97/ac97_patch.h
@@ -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,
diff --git a/sound/pci/ac97/ac97_pcm.c b/sound/pci/ac97/ac97_pcm.c
index 8cbc033..3674f35 100644
--- a/sound/pci/ac97/ac97_pcm.c
+++ b/sound/pci/ac97/ac97_pcm.c
@@ -23,7 +23,6 @@
*
*/
-#include <sound/driver.h>
#include <linux/delay.h>
#include <linux/init.h>
#include <linux/slab.h>
diff --git a/sound/pci/ac97/ac97_proc.c b/sound/pci/ac97/ac97_proc.c
index fed4a2c..060ea59 100644
--- a/sound/pci/ac97/ac97_proc.c
+++ b/sound/pci/ac97/ac97_proc.c
@@ -22,7 +22,6 @@
*
*/
-#include <sound/driver.h>
#include <linux/slab.h>
#include <linux/mutex.h>
diff --git a/sound/pci/ac97/ak4531_codec.c b/sound/pci/ac97/ak4531_codec.c
index 722de45..c0c16339 100644
--- a/sound/pci/ac97/ak4531_codec.c
+++ b/sound/pci/ac97/ak4531_codec.c
@@ -19,7 +19,6 @@
*
*/
-#include <sound/driver.h>
#include <linux/delay.h>
#include <linux/init.h>
#include <linux/slab.h>
diff --git a/sound/pci/ad1889.c b/sound/pci/ad1889.c
index 98970d4..a66d515 100644
--- a/sound/pci/ad1889.c
+++ b/sound/pci/ad1889.c
@@ -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);
diff --git a/sound/pci/ali5451/ali5451.c b/sound/pci/ali5451/ali5451.c
index 4c2bd7a..6a905ed 100644
--- a/sound/pci/ali5451/ali5451.c
+++ b/sound/pci/ali5451/ali5451.c
@@ -25,7 +25,6 @@
*
*/
-#include <sound/driver.h>
#include <asm/io.h>
#include <linux/delay.h>
#include <linux/interrupt.h>
diff --git a/sound/pci/als300.c b/sound/pci/als300.c
index 48cc39b..0e990a7 100644
--- a/sound/pci/als300.c
+++ b/sound/pci/als300.c
@@ -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>
diff --git a/sound/pci/als4000.c b/sound/pci/als4000.c
index 1190ef3..27ce613 100644
--- a/sound/pci/als4000.c
+++ b/sound/pci/als4000.c
@@ -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>
diff --git a/sound/pci/atiixp.c b/sound/pci/atiixp.c
index 89184a4..4594186 100644
--- a/sound/pci/atiixp.c
+++ b/sound/pci/atiixp.c
@@ -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;
diff --git a/sound/pci/atiixp_modem.c b/sound/pci/atiixp_modem.c
index ce752f8..a67a869 100644
--- a/sound/pci/atiixp_modem.c
+++ b/sound/pci/atiixp_modem.c
@@ -19,7 +19,6 @@
*
*/
-#include <sound/driver.h>
#include <asm/io.h>
#include <linux/delay.h>
#include <linux/interrupt.h>
diff --git a/sound/pci/au88x0/au88x0.h b/sound/pci/au88x0/au88x0.h
index 5ccf0b1..4aad35b 100644
--- a/sound/pci/au88x0/au88x0.h
+++ b/sound/pci/au88x0/au88x0.h
@@ -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>
diff --git a/sound/pci/au88x0/au88x0_core.c b/sound/pci/au88x0/au88x0_core.c
index 4a336ea..333c62d 100644
--- a/sound/pci/au88x0/au88x0_core.c
+++ b/sound/pci/au88x0/au88x0_core.c
@@ -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;
diff --git a/sound/pci/au88x0/au88x0_game.c b/sound/pci/au88x0/au88x0_game.c
index a07d1de..bc212f4 100644
--- a/sound/pci/au88x0/au88x0_game.c
+++ b/sound/pci/au88x0/au88x0_game.c
@@ -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>
diff --git a/sound/pci/au88x0/au88x0_mixer.c b/sound/pci/au88x0/au88x0_mixer.c
index c96da1d..c92f493 100644
--- a/sound/pci/au88x0/au88x0_mixer.c
+++ b/sound/pci/au88x0/au88x0_mixer.c
@@ -5,7 +5,6 @@
*
*/
-#include <sound/driver.h>
#include <linux/time.h>
#include <linux/init.h>
#include <sound/core.h>
diff --git a/sound/pci/au88x0/au88x0_mpu401.c b/sound/pci/au88x0/au88x0_mpu401.c
index 8db3d3e..0dc8d25 100644
--- a/sound/pci/au88x0/au88x0_mpu401.c
+++ b/sound/pci/au88x0/au88x0_mpu401.c
@@ -21,7 +21,6 @@
*
*/
-#include <sound/driver.h>
#include <linux/time.h>
#include <linux/init.h>
#include <sound/core.h>
diff --git a/sound/pci/au88x0/au88x0_pcm.c b/sound/pci/au88x0/au88x0_pcm.c
index 7b5baa1..526c6c5 100644
--- a/sound/pci/au88x0/au88x0_pcm.c
+++ b/sound/pci/au88x0/au88x0_pcm.c
@@ -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>
diff --git a/sound/pci/azt3328.c b/sound/pci/azt3328.c
index 36d3666..4e71a55 100644
--- a/sound/pci/azt3328.c
+++ b/sound/pci/azt3328.c
@@ -115,7 +115,6 @@
* 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>
diff --git a/sound/pci/bt87x.c b/sound/pci/bt87x.c
index 2dba752..c9a2421 100644
--- a/sound/pci/bt87x.c
+++ b/sound/pci/bt87x.c
@@ -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>
diff --git a/sound/pci/ca0106/ca0106.h b/sound/pci/ca0106/ca0106.h
index 75da174..74175fc 100644
--- a/sound/pci/ca0106/ca0106.h
+++ b/sound/pci/ca0106/ca0106.h
@@ -272,7 +272,6 @@
#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 */
diff --git a/sound/pci/ca0106/ca0106_main.c b/sound/pci/ca0106/ca0106_main.c
index 31d8db9..176e0f0 100644
--- a/sound/pci/ca0106/ca0106_main.c
+++ b/sound/pci/ca0106/ca0106_main.c
@@ -135,7 +135,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>
diff --git a/sound/pci/ca0106/ca0106_mixer.c b/sound/pci/ca0106/ca0106_mixer.c
index 3f9b5c5..af73686 100644
--- a/sound/pci/ca0106/ca0106_mixer.c
+++ b/sound/pci/ca0106/ca0106_mixer.c
@@ -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>
diff --git a/sound/pci/ca0106/ca0106_proc.c b/sound/pci/ca0106/ca0106_proc.c
index 61f2718..c62b7d1 100644
--- a/sound/pci/ca0106/ca0106_proc.c
+++ b/sound/pci/ca0106/ca0106_proc.c
@@ -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>
diff --git a/sound/pci/ca0106/ca_midi.c b/sound/pci/ca0106/ca_midi.c
index ad32eff..893ee4f 100644
--- a/sound/pci/ca0106/ca_midi.c
+++ b/sound/pci/ca0106/ca_midi.c
@@ -27,7 +27,6 @@
*/
#include <linux/spinlock.h>
-#include <sound/driver.h>
#include <sound/core.h>
#include <sound/rawmidi.h>
diff --git a/sound/pci/cmipci.c b/sound/pci/cmipci.c
index 1fa5f00..135f308 100644
--- a/sound/pci/cmipci.c
+++ b/sound/pci/cmipci.c
@@ -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;
diff --git a/sound/pci/cs4281.c b/sound/pci/cs4281.c
index 9a55f4a..7556fd9 100644
--- a/sound/pci/cs4281.c
+++ b/sound/pci/cs4281.c
@@ -19,7 +19,6 @@
*
*/
-#include <sound/driver.h>
#include <asm/io.h>
#include <linux/delay.h>
#include <linux/interrupt.h>
diff --git a/sound/pci/cs46xx/cs46xx.c b/sound/pci/cs46xx/cs46xx.c
index 2699cb6..e876b32 100644
--- a/sound/pci/cs46xx/cs46xx.c
+++ b/sound/pci/cs46xx/cs46xx.c
@@ -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>
diff --git a/sound/pci/cs46xx/cs46xx_lib.c b/sound/pci/cs46xx/cs46xx_lib.c
index 2c7bfc9..87ddffc 100644
--- a/sound/pci/cs46xx/cs46xx_lib.c
+++ b/sound/pci/cs46xx/cs46xx_lib.c
@@ -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,
@@ -2241,40 +2175,6 @@ static struct snd_kcontrol_new snd_cs46xx_controls[] __devinitdata = {
},
#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
};
#ifdef CONFIG_SND_CS46XX_NEW_DSP
diff --git a/sound/pci/cs46xx/dsp_spos.c b/sound/pci/cs46xx/dsp_spos.c
index 590b35d..ccc8bed 100644
--- a/sound/pci/cs46xx/dsp_spos.c
+++ b/sound/pci/cs46xx/dsp_spos.c
@@ -20,7 +20,6 @@
*/
-#include <sound/driver.h>
#include <asm/io.h>
#include <linux/delay.h>
#include <linux/pm.h>
diff --git a/sound/pci/cs46xx/dsp_spos_scb_lib.c b/sound/pci/cs46xx/dsp_spos_scb_lib.c
index eded4df..2873cfe 100644
--- a/sound/pci/cs46xx/dsp_spos_scb_lib.c
+++ b/sound/pci/cs46xx/dsp_spos_scb_lib.c
@@ -21,7 +21,6 @@
*/
-#include <sound/driver.h>
#include <asm/io.h>
#include <linux/delay.h>
#include <linux/pm.h>
diff --git a/sound/pci/cs5530.c b/sound/pci/cs5530.c
index 240a0a4..7ff8b68 100644
--- a/sound/pci/cs5530.c
+++ b/sound/pci/cs5530.c
@@ -36,7 +36,6 @@
* same manner.
*/
-#include <sound/driver.h>
#include <linux/delay.h>
#include <linux/moduleparam.h>
#include <linux/pci.h>
diff --git a/sound/pci/cs5535audio/cs5535audio.c b/sound/pci/cs5535audio/cs5535audio.c
index 2b35889..1d8b160 100644
--- a/sound/pci/cs5535audio/cs5535audio.c
+++ b/sound/pci/cs5535audio/cs5535audio.c
@@ -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;
diff --git a/sound/pci/cs5535audio/cs5535audio_pcm.c b/sound/pci/cs5535audio/cs5535audio_pcm.c
index 21df063..cdcda87 100644
--- a/sound/pci/cs5535audio/cs5535audio_pcm.c
+++ b/sound/pci/cs5535audio/cs5535audio_pcm.c
@@ -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,
diff --git a/sound/pci/cs5535audio/cs5535audio_pm.c b/sound/pci/cs5535audio/cs5535audio_pm.c
index 838708f..564c33b 100644
--- a/sound/pci/cs5535audio/cs5535audio_pm.c
+++ b/sound/pci/cs5535audio/cs5535audio_pm.c
@@ -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>
diff --git a/sound/pci/echoaudio/darla20.c b/sound/pci/echoaudio/darla20.c
index 87078d3..8c6db3a 100644
--- a/sound/pci/echoaudio/darla20.c
+++ b/sound/pci/echoaudio/darla20.c
@@ -36,7 +36,6 @@
#define BX_NUM 10
-#include <sound/driver.h>
#include <linux/delay.h>
#include <linux/init.h>
#include <linux/interrupt.h>
diff --git a/sound/pci/echoaudio/darla24.c b/sound/pci/echoaudio/darla24.c
index 42b48f9..04cbf3e 100644
--- a/sound/pci/echoaudio/darla24.c
+++ b/sound/pci/echoaudio/darla24.c
@@ -40,7 +40,6 @@
#define BX_NUM 10
-#include <sound/driver.h>
#include <linux/delay.h>
#include <linux/init.h>
#include <linux/interrupt.h>
diff --git a/sound/pci/echoaudio/echo3g.c b/sound/pci/echoaudio/echo3g.c
index 8dbb7ac..4022e43 100644
--- a/sound/pci/echoaudio/echo3g.c
+++ b/sound/pci/echoaudio/echo3g.c
@@ -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>
diff --git a/sound/pci/echoaudio/echoaudio.c b/sound/pci/echoaudio/echoaudio.c
index 499ee1a..90ec090 100644
--- a/sound/pci/echoaudio/echoaudio.c
+++ b/sound/pci/echoaudio/echoaudio.c
@@ -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 ! */
diff --git a/sound/pci/echoaudio/echoaudio.h b/sound/pci/echoaudio/echoaudio.h
index 7e88c96..1c88e05 100644
--- a/sound/pci/echoaudio/echoaudio.h
+++ b/sound/pci/echoaudio/echoaudio.h
@@ -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;
diff --git a/sound/pci/echoaudio/gina20.c b/sound/pci/echoaudio/gina20.c
index fee2d48..c0e64b8 100644
--- a/sound/pci/echoaudio/gina20.c
+++ b/sound/pci/echoaudio/gina20.c
@@ -40,7 +40,6 @@
#define BX_NUM 14
-#include <sound/driver.h>
#include <linux/delay.h>
#include <linux/init.h>
#include <linux/interrupt.h>
diff --git a/sound/pci/echoaudio/gina24.c b/sound/pci/echoaudio/gina24.c
index d5eae47..c36a78d 100644
--- a/sound/pci/echoaudio/gina24.c
+++ b/sound/pci/echoaudio/gina24.c
@@ -46,7 +46,6 @@
#define BX_NUM 26
-#include <sound/driver.h>
#include <linux/delay.h>
#include <linux/init.h>
#include <linux/interrupt.h>
diff --git a/sound/pci/echoaudio/indigo.c b/sound/pci/echoaudio/indigo.c
index 40f601c..0a58a7c 100644
--- a/sound/pci/echoaudio/indigo.c
+++ b/sound/pci/echoaudio/indigo.c
@@ -38,7 +38,6 @@
#define BX_NUM 2
-#include <sound/driver.h>
#include <linux/delay.h>
#include <linux/init.h>
#include <linux/interrupt.h>
diff --git a/sound/pci/echoaudio/indigodj.c b/sound/pci/echoaudio/indigodj.c
index 771c538..2db24d2 100644
--- a/sound/pci/echoaudio/indigodj.c
+++ b/sound/pci/echoaudio/indigodj.c
@@ -38,7 +38,6 @@
#define BX_NUM 4
-#include <sound/driver.h>
#include <linux/delay.h>
#include <linux/init.h>
#include <linux/interrupt.h>
diff --git a/sound/pci/echoaudio/indigoio.c b/sound/pci/echoaudio/indigoio.c
index 49c550d..a60c0a0 100644
--- a/sound/pci/echoaudio/indigoio.c
+++ b/sound/pci/echoaudio/indigoio.c
@@ -39,7 +39,6 @@
#define BX_NUM 4
-#include <sound/driver.h>
#include <linux/delay.h>
#include <linux/init.h>
#include <linux/interrupt.h>
diff --git a/sound/pci/echoaudio/layla20.c b/sound/pci/echoaudio/layla20.c
index 8f5483a..5061946 100644
--- a/sound/pci/echoaudio/layla20.c
+++ b/sound/pci/echoaudio/layla20.c
@@ -45,7 +45,6 @@
#define BX_NUM 22
-#include <sound/driver.h>
#include <linux/delay.h>
#include <linux/init.h>
#include <linux/interrupt.h>
diff --git a/sound/pci/echoaudio/layla24.c b/sound/pci/echoaudio/layla24.c
index 0524667..e09e3ea 100644
--- a/sound/pci/echoaudio/layla24.c
+++ b/sound/pci/echoaudio/layla24.c
@@ -47,7 +47,6 @@
#define BX_NUM 32
-#include <sound/driver.h>
#include <linux/delay.h>
#include <linux/init.h>
#include <linux/interrupt.h>
diff --git a/sound/pci/echoaudio/mia.c b/sound/pci/echoaudio/mia.c
index 893c7c2..f3b9b45 100644
--- a/sound/pci/echoaudio/mia.c
+++ b/sound/pci/echoaudio/mia.c
@@ -45,7 +45,6 @@
#define BX_NUM 8
-#include <sound/driver.h>
#include <linux/delay.h>
#include <linux/init.h>
#include <linux/interrupt.h>
diff --git a/sound/pci/echoaudio/mona.c b/sound/pci/echoaudio/mona.c
index 3a5d5b0..b05bad9 100644
--- a/sound/pci/echoaudio/mona.c
+++ b/sound/pci/echoaudio/mona.c
@@ -44,7 +44,6 @@
#define BX_NUM 26
-#include <sound/driver.h>
#include <linux/delay.h>
#include <linux/init.h>
#include <linux/interrupt.h>
diff --git a/sound/pci/emu10k1/emu10k1.c b/sound/pci/emu10k1/emu10k1.c
index 9680caf..8354c1a 100644
--- a/sound/pci/emu10k1/emu10k1.c
+++ b/sound/pci/emu10k1/emu10k1.c
@@ -23,7 +23,6 @@
*
*/
-#include <sound/driver.h>
#include <linux/init.h>
#include <linux/pci.h>
#include <linux/time.h>
diff --git a/sound/pci/emu10k1/emu10k1_callback.c b/sound/pci/emu10k1/emu10k1_callback.c
index 01965bd..45088eb 100644
--- a/sound/pci/emu10k1/emu10k1_callback.c
+++ b/sound/pci/emu10k1/emu10k1_callback.c
@@ -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);
diff --git a/sound/pci/emu10k1/emu10k1_main.c b/sound/pci/emu10k1/emu10k1_main.c
index 97c41d7..9a9b977 100644
--- a/sound/pci/emu10k1/emu10k1_main.c
+++ b/sound/pci/emu10k1/emu10k1_main.c
@@ -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>
@@ -55,12 +54,14 @@
#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);
diff --git a/sound/pci/emu10k1/emu10k1_synth.c b/sound/pci/emu10k1/emu10k1_synth.c
index 204995a..ad7b714 100644
--- a/sound/pci/emu10k1/emu10k1_synth.c
+++ b/sound/pci/emu10k1/emu10k1_synth.c
@@ -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;
}
diff --git a/sound/pci/emu10k1/emu10k1_synth_local.h b/sound/pci/emu10k1/emu10k1_synth_local.h
index 308ddc8..25f328f 100644
--- a/sound/pci/emu10k1/emu10k1_synth_local.h
+++ b/sound/pci/emu10k1/emu10k1_synth_local.h
@@ -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>
diff --git a/sound/pci/emu10k1/emu10k1x.c b/sound/pci/emu10k1/emu10k1x.c
index 1ec7eba..5512abd 100644
--- a/sound/pci/emu10k1/emu10k1x.c
+++ b/sound/pci/emu10k1/emu10k1x.c
@@ -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;
diff --git a/sound/pci/emu10k1/emufx.c b/sound/pci/emu10k1/emufx.c
index 9bf1cd5..71dc4c8 100644
--- a/sound/pci/emu10k1/emufx.c
+++ b/sound/pci/emu10k1/emufx.c
@@ -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++) {
diff --git a/sound/pci/emu10k1/emumixer.c b/sound/pci/emu10k1/emumixer.c
index ccacd7b..fd22120 100644
--- a/sound/pci/emu10k1/emumixer.c
+++ b/sound/pci/emu10k1/emumixer.c
@@ -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;
}
diff --git a/sound/pci/emu10k1/emumpu401.c b/sound/pci/emu10k1/emumpu401.c
index 04c7cf7..c4d76d1 100644
--- a/sound/pci/emu10k1/emumpu401.c
+++ b/sound/pci/emu10k1/emumpu401.c
@@ -19,7 +19,6 @@
*
*/
-#include <sound/driver.h>
#include <linux/time.h>
#include <linux/init.h>
#include <sound/core.h>
diff --git a/sound/pci/emu10k1/emupcm.c b/sound/pci/emu10k1/emupcm.c
index 5ce5bef..cf9276d 100644
--- a/sound/pci/emu10k1/emupcm.c
+++ b/sound/pci/emu10k1/emupcm.c
@@ -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
diff --git a/sound/pci/emu10k1/emuproc.c b/sound/pci/emu10k1/emuproc.c
index c3fb10e..f3caa3f 100644
--- a/sound/pci/emu10k1/emuproc.c
+++ b/sound/pci/emu10k1/emuproc.c
@@ -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);
}
diff --git a/sound/pci/emu10k1/io.c b/sound/pci/emu10k1/io.c
index 6702c15..b5a802b 100644
--- a/sound/pci/emu10k1/io.c
+++ b/sound/pci/emu10k1/io.c
@@ -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;
}
diff --git a/sound/pci/emu10k1/irq.c b/sound/pci/emu10k1/irq.c
index 3c114b4..30bfed6 100644
--- a/sound/pci/emu10k1/irq.c
+++ b/sound/pci/emu10k1/irq.c
@@ -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);
}
diff --git a/sound/pci/emu10k1/memory.c b/sound/pci/emu10k1/memory.c
index 48097c6..916c1db 100644
--- a/sound/pci/emu10k1/memory.c
+++ b/sound/pci/emu10k1/memory.c
@@ -21,7 +21,6 @@
*
*/
-#include <sound/driver.h>
#include <linux/pci.h>
#include <linux/time.h>
#include <linux/mutex.h>
diff --git a/sound/pci/emu10k1/p16v.c b/sound/pci/emu10k1/p16v.c
index 9fd3135..749a21b 100644
--- a/sound/pci/emu10k1/p16v.c
+++ b/sound/pci/emu10k1/p16v.c
@@ -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>
diff --git a/sound/pci/emu10k1/timer.c b/sound/pci/emu10k1/timer.c
index 6295b2d..72321e9 100644
--- a/sound/pci/emu10k1/timer.c
+++ b/sound/pci/emu10k1/timer.c
@@ -25,7 +25,6 @@
*
*/
-#include <sound/driver.h>
#include <linux/time.h>
#include <sound/core.h>
#include <sound/emu10k1.h>
diff --git a/sound/pci/emu10k1/voice.c b/sound/pci/emu10k1/voice.c
index 04fa849..958cb2a 100644
--- a/sound/pci/emu10k1/voice.c
+++ b/sound/pci/emu10k1/voice.c
@@ -28,7 +28,6 @@
*
*/
-#include <sound/driver.h>
#include <linux/time.h>
#include <sound/core.h>
#include <sound/emu10k1.h>
diff --git a/sound/pci/ens1370.c b/sound/pci/ens1370.c
index b958f86..72d85a5 100644
--- a/sound/pci/ens1370.c
+++ b/sound/pci/ens1370.c
@@ -26,7 +26,6 @@
* by Kurt J. Bosch
*/
-#include <sound/driver.h>
#include <asm/io.h>
#include <linux/delay.h>
#include <linux/interrupt.h>
diff --git a/sound/pci/es1938.c b/sound/pci/es1938.c
index fb25abe..1a314fa 100644
--- a/sound/pci/es1938.c
+++ b/sound/pci/es1938.c
@@ -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;
}
diff --git a/sound/pci/es1968.c b/sound/pci/es1968.c
index d69b11d..25ccfce 100644
--- a/sound/pci/es1968.c
+++ b/sound/pci/es1968.c
@@ -94,7 +94,6 @@
* places.
*/
-#include <sound/driver.h>
#include <asm/io.h>
#include <linux/delay.h>
#include <linux/interrupt.h>
diff --git a/sound/pci/fm801.c b/sound/pci/fm801.c
index 9939109..4c300e6 100644
--- a/sound/pci/fm801.c
+++ b/sound/pci/fm801.c
@@ -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
diff --git a/sound/pci/hda/Makefile b/sound/pci/hda/Makefile
index ab0c726..9e0d8a1 100644
--- a/sound/pci/hda/Makefile
+++ b/sound/pci/hda/Makefile
@@ -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
diff --git a/sound/pci/hda/hda_codec.c b/sound/pci/hda/hda_codec.c
index 8cbe3bf..26812dc 100644
--- a/sound/pci/hda/hda_codec.c
+++ b/sound/pci/hda/hda_codec.c
@@ -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],
diff --git a/sound/pci/hda/hda_codec.h b/sound/pci/hda/hda_codec.h
index 2bce925..f148711 100644
--- a/sound/pci/hda/hda_codec.h
+++ b/sound/pci/hda/hda_codec.h
@@ -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);
};
diff --git a/sound/pci/hda/hda_generic.c b/sound/pci/hda/hda_generic.c
index c957eb5..f9de7c4 100644
--- a/sound/pci/hda/hda_generic.c
+++ b/sound/pci/hda/hda_generic.c
@@ -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>
diff --git a/sound/pci/hda/hda_hwdep.c b/sound/pci/hda/hda_hwdep.c
index bafb7b0..2177d9a 100644
--- a/sound/pci/hda/hda_hwdep.c
+++ b/sound/pci/hda/hda_hwdep.c
@@ -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>
diff --git a/sound/pci/hda/hda_intel.c b/sound/pci/hda/hda_intel.c
index 3fa0f97..56f8a30 100644
--- a/sound/pci/hda/hda_intel.c
+++ b/sound/pci/hda/hda_intel.c
@@ -34,7 +34,6 @@
*
*/
-#include <sound/driver.h>
#include <asm/io.h>
#include <linux/delay.h>
#include <linux/interrupt.h>
@@ -50,29 +49,32 @@
#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 */
diff --git a/sound/pci/hda/hda_local.h b/sound/pci/hda/hda_local.h
index 8c56c9c..ad0014a 100644
--- a/sound/pci/hda/hda_local.h
+++ b/sound/pci/hda/hda_local.h
@@ -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 */
diff --git a/sound/pci/hda/hda_proc.c b/sound/pci/hda/hda_proc.c
index e94944f..35a630d 100644
--- a/sound/pci/hda/hda_proc.c
+++ b/sound/pci/hda/hda_proc.c
@@ -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);
}
diff --git a/sound/pci/hda/patch_analog.c b/sound/pci/hda/patch_analog.c
index 196ad3c..19f0884 100644
--- a/sound/pci/hda/patch_analog.c
+++ b/sound/pci/hda/patch_analog.c
@@ -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;
diff --git a/sound/pci/hda/patch_atihdmi.c b/sound/pci/hda/patch_atihdmi.c
index fbb8969..9a8bb4c 100644
--- a/sound/pci/hda/patch_atihdmi.c
+++ b/sound/pci/hda/patch_atihdmi.c
@@ -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 */
};
diff --git a/sound/pci/hda/patch_cmedia.c b/sound/pci/hda/patch_cmedia.c
index 6c54793..3d6097b 100644
--- a/sound/pci/hda/patch_cmedia.c
+++ b/sound/pci/hda/patch_cmedia.c
@@ -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",
diff --git a/sound/pci/hda/patch_conexant.c b/sound/pci/hda/patch_conexant.c
index 6aa0739..f6dd51c 100644
--- a/sound/pci/hda/patch_conexant.c
+++ b/sound/pci/hda/patch_conexant.c
@@ -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 */
};
diff --git a/sound/pci/hda/patch_realtek.c b/sound/pci/hda/patch_realtek.c
index 1c50278..586d98f 100644
--- a/sound/pci/hda/patch_realtek.c
+++ b/sound/pci/hda/patch_realtek.c
@@ -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,16 +1454,49 @@ 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
*/
static int alc_build_controls(struct hda_codec *codec)
@@ -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,6 +6952,46 @@ static struct snd_kcontrol_new alc888_3st_hp_mixer[] = {
{ } /* end */
};
+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_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),
@@ -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,6 +8271,85 @@ static struct snd_kcontrol_new alc262_HP_BPC_WildWest_option_mixer[] = {
{ } /* end */
};
+/* 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 alc_spec *spec = codec->spec;
+
+ 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)
@@ -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,6 +9830,61 @@ static struct hda_input_mux alc268_capture_source = {
},
};
+#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)
@@ -9194,6 +10032,7 @@ 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_analog_alt_capture alc880_pcm_analog_alt_capture
#define alc268_pcm_digital_playback alc880_pcm_digital_playback
/*
@@ -9259,16 +10098,23 @@ 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(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),
{}
};
@@ -9317,6 +10163,35 @@ static struct alc_config_preset alc268_presets[] = {
.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)
@@ -9361,34 +10236,34 @@ static int patch_alc268(struct hda_codec *codec)
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 (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] =
+ 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->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;
@@ -9397,6 +10272,360 @@ static int patch_alc268(struct hda_codec *codec)
}
/*
+ * 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 alc269_parse_auto_config(struct hda_codec *codec)
+{
+ struct alc_spec *spec = codec->spec;
+ int err;
+ static hda_nid_t alc269_ignore[] = { 0x1d, 0 };
+
+ err = snd_hda_parse_pin_def_config(codec, &spec->autocfg,
+ alc269_ignore);
+ if (err < 0)
+ return err;
+
+ err = alc269_auto_create_multi_out_ctls(spec, &spec->autocfg);
+ if (err < 0)
+ return err;
+ err = alc269_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 = ALC269_DIGOUT_NID;
+
+ if (spec->kctl_alloc)
+ spec->mixers[spec->num_mixers++] = spec->kctl_alloc;
+
+ spec->init_verbs[spec->num_init_verbs++] = alc269_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 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 alc269_auto_init(struct hda_codec *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 *alc269_models[ALC269_MODEL_LAST] = {
+ [ALC269_BASIC] = "basic",
+};
+
+static struct snd_pci_quirk alc269_cfg_tbl[] = {
+ {}
+};
+
+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(alc269_modes),
+ .channel_mode = alc269_modes,
+ .input_mux = &alc269_capture_source,
+ },
+};
+
+static int patch_alc269(struct hda_codec *codec)
+{
+ struct alc_spec *spec;
+ int board_config;
+ int err;
+
+ spec = kzalloc(sizeof(*spec), GFP_KERNEL);
+ if (spec == NULL)
+ return -ENOMEM;
+
+ codec->spec = spec;
+
+ board_config = snd_hda_check_board_config(codec, ALC269_MODEL_LAST,
+ alc269_models,
+ alc269_cfg_tbl);
+
+ if (board_config < 0) {
+ printk(KERN_INFO "hda_codec: Unknown model for ALC269, "
+ "trying auto-probe from BIOS...\n");
+ board_config = ALC269_AUTO;
+ }
+
+ if (board_config == ALC269_AUTO) {
+ /* automatic parse from the BIOS config */
+ err = alc269_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 = ALC269_BASIC;
+ }
+ }
+
+ if (board_config != ALC269_AUTO)
+ setup_preset(spec, &alc269_presets[board_config]);
+
+ 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 = "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++;
+
+ codec->patch_ops = alc_patch_ops;
+ 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;
+}
+
+/*
* ALC861 channel source setting (2/6 channel selection for 3-stack)
*/
@@ -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 */
};
diff --git a/sound/pci/hda/patch_si3054.c b/sound/pci/hda/patch_si3054.c
index 2a4b960..d22f5a6 100644
--- a/sound/pci/hda/patch_si3054.c
+++ b/sound/pci/hda/patch_si3054.c
@@ -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 },
diff --git a/sound/pci/hda/patch_sigmatel.c b/sound/pci/hda/patch_sigmatel.c
index 0401223..caf48ed 100644
--- a/sound/pci/hda/patch_sigmatel.c
+++ b/sound/pci/hda/patch_sigmatel.c
@@ -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,
@@ -62,6 +62,16 @@ enum {
};
enum {
+ STAC_92HD73XX_REF,
+ STAC_92HD73XX_MODELS
+};
+
+enum {
+ STAC_92HD71BXX_REF,
+ STAC_92HD71BXX_MODELS
+};
+
+enum {
STAC_925x_REF,
STAC_M2_2,
STAC_MA6,
@@ -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 */
};
diff --git a/sound/pci/hda/patch_via.c b/sound/pci/hda/patch_via.c
index 4cdf3e6..4e5dd4c 100644
--- a/sound/pci/hda/patch_via.c
+++ b/sound/pci/hda/patch_via.c
@@ -27,11 +27,12 @@
/* 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>
@@ -51,14 +52,23 @@
#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
index 0000000..2da49d2
--- /dev/null
+++ b/sound/pci/hda/vmaster.c
@@ -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;
+}
diff --git a/sound/pci/ice1712/Makefile b/sound/pci/ice1712/Makefile
index 65ce66a..f99fe08 100644
--- a/sound/pci/ice1712/Makefile
+++ b/sound/pci/ice1712/Makefile
@@ -5,7 +5,7 @@
snd-ice17xx-ak4xxx-objs := ak4xxx.o
snd-ice1712-objs := ice1712.o delta.o hoontech.o ews.o
-snd-ice1724-objs := ice1724.o amp.o revo.o aureon.o vt1720_mobo.o pontis.o prodigy192.o juli.o phase.o 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
diff --git a/sound/pci/ice1712/ak4xxx.c b/sound/pci/ice1712/ak4xxx.c
index a1aba0d..dab31b2 100644
--- a/sound/pci/ice1712/ak4xxx.c
+++ b/sound/pci/ice1712/ak4xxx.c
@@ -21,7 +21,6 @@
*
*/
-#include <sound/driver.h>
#include <asm/io.h>
#include <linux/delay.h>
#include <linux/interrupt.h>
diff --git a/sound/pci/ice1712/amp.c b/sound/pci/ice1712/amp.c
index 6e13d75..3756430 100644
--- a/sound/pci/ice1712/amp.c
+++ b/sound/pci/ice1712/amp.c
@@ -21,7 +21,6 @@
*
*/
-#include <sound/driver.h>
#include <asm/io.h>
#include <linux/delay.h>
#include <linux/interrupt.h>
diff --git a/sound/pci/ice1712/aureon.c b/sound/pci/ice1712/aureon.c
index ec0699c..868ae29 100644
--- a/sound/pci/ice1712/aureon.c
+++ b/sound/pci/ice1712/aureon.c
@@ -47,7 +47,6 @@
*
*/
-#include <sound/driver.h>
#include <asm/io.h>
#include <linux/delay.h>
#include <linux/interrupt.h>
@@ -62,6 +61,15 @@
#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;
diff --git a/sound/pci/ice1712/delta.c b/sound/pci/ice1712/delta.c
index 371f784..efd180b 100644
--- a/sound/pci/ice1712/delta.c
+++ b/sound/pci/ice1712/delta.c
@@ -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;
}
diff --git a/sound/pci/ice1712/ews.c b/sound/pci/ice1712/ews.c
index 75e4e5e..064760d 100644
--- a/sound/pci/ice1712/ews.c
+++ b/sound/pci/ice1712/ews.c
@@ -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;
}
diff --git a/sound/pci/ice1712/hoontech.c b/sound/pci/ice1712/hoontech.c
index abcfd1d..cf5c7c0 100644
--- a/sound/pci/ice1712/hoontech.c
+++ b/sound/pci/ice1712/hoontech.c
@@ -21,7 +21,6 @@
*
*/
-#include <sound/driver.h>
#include <asm/io.h>
#include <linux/delay.h>
#include <linux/interrupt.h>
@@ -34,6 +33,12 @@
#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;
diff --git a/sound/pci/ice1712/ice1712.c b/sound/pci/ice1712/ice1712.c
index 052fc3c..df292af 100644
--- a/sound/pci/ice1712/ice1712.c
+++ b/sound/pci/ice1712/ice1712.c
@@ -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;
}
diff --git a/sound/pci/ice1712/ice1712.h b/sound/pci/ice1712/ice1712.h
index 58640af..303cffe 100644
--- a/sound/pci/ice1712/ice1712.h
+++ b/sound/pci/ice1712/ice1712.h
@@ -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;
};
diff --git a/sound/pci/ice1712/ice1724.c b/sound/pci/ice1712/ice1724.c
index 0b0bbb0..f533850 100644
--- a/sound/pci/ice1712/ice1724.c
+++ b/sound/pci/ice1712/ice1724.c
@@ -22,7 +22,6 @@
*
*/
-#include <sound/driver.h>
#include <asm/io.h>
#include <linux/delay.h>
#include <linux/interrupt.h>
@@ -48,9 +47,11 @@
#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;
}
diff --git a/sound/pci/ice1712/juli.c b/sound/pci/ice1712/juli.c
index 1fbe3ef..e8038c0 100644
--- a/sound/pci/ice1712/juli.c
+++ b/sound/pci/ice1712/juli.c
@@ -21,7 +21,6 @@
*
*/
-#include <sound/driver.h>
#include <asm/io.h>
#include <linux/delay.h>
#include <linux/interrupt.h>
@@ -33,6 +32,11 @@
#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;
diff --git a/sound/pci/ice1712/phase.c b/sound/pci/ice1712/phase.c
index 3ac2505..9ab4a9f 100644
--- a/sound/pci/ice1712/phase.c
+++ b/sound/pci/ice1712/phase.c
@@ -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>
@@ -48,6 +47,12 @@
#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) {
diff --git a/sound/pci/ice1712/pontis.c b/sound/pci/ice1712/pontis.c
index faefd52..4945c81 100644
--- a/sound/pci/ice1712/pontis.c
+++ b/sound/pci/ice1712/pontis.c
@@ -21,7 +21,6 @@
*
*/
-#include <sound/driver.h>
#include <asm/io.h>
#include <linux/delay.h>
#include <linux/interrupt.h>
diff --git a/sound/pci/ice1712/prodigy192.c b/sound/pci/ice1712/prodigy192.c
index 4180f97..48cf40a 100644
--- a/sound/pci/ice1712/prodigy192.c
+++ b/sound/pci/ice1712/prodigy192.c
@@ -54,7 +54,6 @@
*
*/
-#include <sound/driver.h>
#include <asm/io.h>
#include <linux/delay.h>
#include <linux/interrupt.h>
@@ -68,6 +67,12 @@
#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
index 0000000..043a938
--- /dev/null
+++ b/sound/pci/ice1712/prodigy_hifi.c
@@ -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
index 0000000..a4415d4
--- /dev/null
+++ b/sound/pci/ice1712/prodigy_hifi.h
@@ -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 */
diff --git a/sound/pci/ice1712/revo.c b/sound/pci/ice1712/revo.c
index d18a31e..ddd5fc8 100644
--- a/sound/pci/ice1712/revo.c
+++ b/sound/pci/ice1712/revo.c
@@ -21,7 +21,6 @@
*
*/
-#include <sound/driver.h>
#include <asm/io.h>
#include <linux/delay.h>
#include <linux/interrupt.h>
@@ -33,6 +32,12 @@
#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
index 0000000..69673b9
--- /dev/null
+++ b/sound/pci/ice1712/se.c
@@ -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
index 0000000..0b0a9da
--- /dev/null
+++ b/sound/pci/ice1712/se.h
@@ -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 */
diff --git a/sound/pci/ice1712/vt1720_mobo.c b/sound/pci/ice1712/vt1720_mobo.c
index 2395241..7f9674b 100644
--- a/sound/pci/ice1712/vt1720_mobo.c
+++ b/sound/pci/ice1712/vt1720_mobo.c
@@ -21,7 +21,6 @@
*
*/
-#include <sound/driver.h>
#include <asm/io.h>
#include <linux/delay.h>
#include <linux/interrupt.h>
diff --git a/sound/pci/ice1712/wtm.c b/sound/pci/ice1712/wtm.c
index 7fcce0a..a08d17c 100644
--- a/sound/pci/ice1712/wtm.c
+++ b/sound/pci/ice1712/wtm.c
@@ -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)
diff --git a/sound/pci/intel8x0.c b/sound/pci/intel8x0.c
index 4bb9764..061072c 100644
--- a/sound/pci/intel8x0.c
+++ b/sound/pci/intel8x0.c
@@ -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 */
diff --git a/sound/pci/intel8x0m.c b/sound/pci/intel8x0m.c
index fad806e..cadda8d 100644
--- a/sound/pci/intel8x0m.c
+++ b/sound/pci/intel8x0m.c
@@ -23,7 +23,6 @@
*
*/
-#include <sound/driver.h>
#include <asm/io.h>
#include <linux/delay.h>
#include <linux/interrupt.h>
diff --git a/sound/pci/korg1212/korg1212.c b/sound/pci/korg1212/korg1212.c
index c4af57f..10c713d 100644
--- a/sound/pci/korg1212/korg1212.c
+++ b/sound/pci/korg1212/korg1212.c
@@ -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;
}
diff --git a/sound/pci/maestro3.c b/sound/pci/maestro3.c
index 3224577..04fa0a6 100644
--- a/sound/pci/maestro3.c
+++ b/sound/pci/maestro3.c
@@ -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
diff --git a/sound/pci/mixart/mixart.c b/sound/pci/mixart/mixart.c
index 880b824..3dd0c79 100644
--- a/sound/pci/mixart/mixart.c
+++ b/sound/pci/mixart/mixart.c
@@ -21,7 +21,6 @@
*/
-#include <sound/driver.h>
#include <linux/init.h>
#include <linux/interrupt.h>
#include <linux/pci.h>
diff --git a/sound/pci/mixart/mixart_core.c b/sound/pci/mixart/mixart_core.c
index d544573..785085e 100644
--- a/sound/pci/mixart/mixart_core.c
+++ b/sound/pci/mixart/mixart_core.c
@@ -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>
diff --git a/sound/pci/mixart/mixart_hwdep.c b/sound/pci/mixart/mixart_hwdep.c
index 170781a..122c28e 100644
--- a/sound/pci/mixart/mixart_hwdep.c
+++ b/sound/pci/mixart/mixart_hwdep.c
@@ -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>
diff --git a/sound/pci/mixart/mixart_mixer.c b/sound/pci/mixart/mixart_mixer.c
index 0e16512..6fdda1f 100644
--- a/sound/pci/mixart/mixart_mixer.c
+++ b/sound/pci/mixart/mixart_mixer.c
@@ -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);
}
}
diff --git a/sound/pci/nm256/nm256.c b/sound/pci/nm256/nm256.c
index 276c576..7ac654e 100644
--- a/sound/pci/nm256/nm256.c
+++ b/sound/pci/nm256/nm256.c
@@ -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
index 0000000..4ba07d4
--- /dev/null
+++ b/sound/pci/oxygen/Makefile
@@ -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
index 0000000..551c1cf
--- /dev/null
+++ b/sound/pci/oxygen/ak4396.h
@@ -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
index 0000000..1445967
--- /dev/null
+++ b/sound/pci/oxygen/cm9780.h
@@ -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
index 0000000..3ea1f05
--- /dev/null
+++ b/sound/pci/oxygen/hifier.c
@@ -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
index 0000000..f31a0eb
--- /dev/null
+++ b/sound/pci/oxygen/oxygen.c
@@ -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
index 0000000..ad50fb8
--- /dev/null
+++ b/sound/pci/oxygen/oxygen.h
@@ -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
index 0000000..74e23ef
--- /dev/null
+++ b/sound/pci/oxygen/oxygen_io.c
@@ -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
index 0000000..6eb36dd
--- /dev/null
+++ b/sound/pci/oxygen/oxygen_lib.c
@@ -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
index 0000000..a8e4623
--- /dev/null
+++ b/sound/pci/oxygen/oxygen_mixer.c
@@ -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
index 0000000..dfad3db
--- /dev/null
+++ b/sound/pci/oxygen/oxygen_pcm.c
@@ -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
index 0000000..72de159
--- /dev/null
+++ b/sound/pci/oxygen/oxygen_regs.h
@@ -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
index 0000000..40e92f5
--- /dev/null
+++ b/sound/pci/oxygen/virtuoso.c
@@ -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)
diff --git a/sound/pci/pcxhr/pcxhr.c b/sound/pci/pcxhr/pcxhr.c
index 2d618bd..9d5bb76 100644
--- a/sound/pci/pcxhr/pcxhr.c
+++ b/sound/pci/pcxhr/pcxhr.c
@@ -21,7 +21,6 @@
*/
-#include <sound/driver.h>
#include <linux/init.h>
#include <linux/interrupt.h>
#include <linux/slab.h>
diff --git a/sound/pci/pcxhr/pcxhr_core.c b/sound/pci/pcxhr/pcxhr_core.c
index 0ff8dc3..c4e415d 100644
--- a/sound/pci/pcxhr/pcxhr_core.c
+++ b/sound/pci/pcxhr/pcxhr_core.c
@@ -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>
diff --git a/sound/pci/pcxhr/pcxhr_hwdep.c b/sound/pci/pcxhr/pcxhr_hwdep.c
index d55d8bc..e6a4bfb 100644
--- a/sound/pci/pcxhr/pcxhr_hwdep.c
+++ b/sound/pci/pcxhr/pcxhr_hwdep.c
@@ -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>
diff --git a/sound/pci/pcxhr/pcxhr_mixer.c b/sound/pci/pcxhr/pcxhr_mixer.c
index 5f8d426..aabc7bc 100644
--- a/sound/pci/pcxhr/pcxhr_mixer.c
+++ b/sound/pci/pcxhr/pcxhr_mixer.c
@@ -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);
diff --git a/sound/pci/riptide/riptide.c b/sound/pci/riptide/riptide.c
index 8e54104..9408b1e 100644
--- a/sound/pci/riptide/riptide.c
+++ b/sound/pci/riptide/riptide.c
@@ -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>
diff --git a/sound/pci/rme32.c b/sound/pci/rme32.c
index 1475912..df184aa 100644
--- a/sound/pci/rme32.c
+++ b/sound/pci/rme32.c
@@ -69,7 +69,6 @@
*/
-#include <sound/driver.h>
#include <linux/delay.h>
#include <linux/init.h>
#include <linux/interrupt.h>
diff --git a/sound/pci/rme96.c b/sound/pci/rme96.c
index 0b3c532..fb0a4ee 100644
--- a/sound/pci/rme96.c
+++ b/sound/pci/rme96.c
@@ -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;
diff --git a/sound/pci/rme9652/hdsp.c b/sound/pci/rme9652/hdsp.c
index ff26a36..c2bd438 100644
--- a/sound/pci/rme9652/hdsp.c
+++ b/sound/pci/rme9652/hdsp.c
@@ -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:
diff --git a/sound/pci/rme9652/hdspm.c b/sound/pci/rme9652/hdspm.c
index f1bdda6..9a19ae6 100644
--- a/sound/pci/rme9652/hdspm.c
+++ b/sound/pci/rme9652/hdspm.c
@@ -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)
diff --git a/sound/pci/rme9652/rme9652.c b/sound/pci/rme9652/rme9652.c
index 34f96f1..a123f0e 100644
--- a/sound/pci/rme9652/rme9652.c
+++ b/sound/pci/rme9652/rme9652.c
@@ -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
index 0000000..dcd7cd0
--- /dev/null
+++ b/sound/pci/sis7019.c
@@ -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
index 0000000..013b673
--- /dev/null
+++ b/sound/pci/sis7019.h
@@ -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__ */
diff --git a/sound/pci/sonicvibes.c b/sound/pci/sonicvibes.c
index 44a7f5f..0d3d305 100644
--- a/sound/pci/sonicvibes.c
+++ b/sound/pci/sonicvibes.c
@@ -22,7 +22,6 @@
*
*/
-#include <sound/driver.h>
#include <linux/delay.h>
#include <linux/init.h>
#include <linux/interrupt.h>
diff --git a/sound/pci/trident/Makefile b/sound/pci/trident/Makefile
index 65f2c21..88676b5 100644
--- a/sound/pci/trident/Makefile
+++ b/sound/pci/trident/Makefile
@@ -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
diff --git a/sound/pci/trident/trident.c b/sound/pci/trident/trident.c
index 8488456..d94b16f 100644
--- a/sound/pci/trident/trident.c
+++ b/sound/pci/trident/trident.c
@@ -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) {
diff --git a/sound/pci/trident/trident_main.c b/sound/pci/trident/trident_main.c
index a235e03..71138ff 100644
--- a/sound/pci/trident/trident_main.c
+++ b/sound/pci/trident/trident_main.c
@@ -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;
diff --git a/sound/pci/trident/trident_memory.c b/sound/pci/trident/trident_memory.c
index 847b8c6..df9b487 100644
--- a/sound/pci/trident/trident_memory.c
+++ b/sound/pci/trident/trident_memory.c
@@ -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
index 9b7dee8..0000000
--- a/sound/pci/trident/trident_synth.c
+++ /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)
diff --git a/sound/pci/via82xx.c b/sound/pci/via82xx.c
index cf62d2a..a756be6 100644
--- a/sound/pci/via82xx.c
+++ b/sound/pci/via82xx.c
@@ -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),
diff --git a/sound/pci/via82xx_modem.c b/sound/pci/via82xx_modem.c
index 57fb9ae..f5df1c7 100644
--- a/sound/pci/via82xx_modem.c
+++ b/sound/pci/via82xx_modem.c
@@ -31,7 +31,6 @@
* modems.
*/
-#include <sound/driver.h>
#include <asm/io.h>
#include <linux/delay.h>
#include <linux/interrupt.h>
diff --git a/sound/pci/vx222/vx222.c b/sound/pci/vx222/vx222.c
index 474eac9..acc352f 100644
--- a/sound/pci/vx222/vx222.c
+++ b/sound/pci/vx222/vx222.c
@@ -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>
diff --git a/sound/pci/vx222/vx222_ops.c b/sound/pci/vx222/vx222_ops.c
index 55558be..b4bfc1a 100644
--- a/sound/pci/vx222/vx222_ops.c
+++ b/sound/pci/vx222/vx222_ops.c
@@ -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];
diff --git a/sound/pci/ymfpci/ymfpci.c b/sound/pci/ymfpci/ymfpci.c
index 5c4256a..2631a55 100644
--- a/sound/pci/ymfpci/ymfpci.c
+++ b/sound/pci/ymfpci/ymfpci.c
@@ -19,7 +19,6 @@
*
*/
-#include <sound/driver.h>
#include <linux/init.h>
#include <linux/pci.h>
#include <linux/time.h>
diff --git a/sound/pci/ymfpci/ymfpci_main.c b/sound/pci/ymfpci/ymfpci_main.c
index 1fe39ed..42c1eb7d 100644
--- a/sound/pci/ymfpci/ymfpci_main.c
+++ b/sound/pci/ymfpci/ymfpci_main.c
@@ -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);
diff --git a/sound/pcmcia/pdaudiocf/pdaudiocf.c b/sound/pcmcia/pdaudiocf/pdaudiocf.c
index de683b0..819aaaa 100644
--- a/sound/pcmcia/pdaudiocf/pdaudiocf.c
+++ b/sound/pcmcia/pdaudiocf/pdaudiocf.c
@@ -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;
diff --git a/sound/pcmcia/pdaudiocf/pdaudiocf_core.c b/sound/pcmcia/pdaudiocf/pdaudiocf_core.c
index 484c8f9..dfa40b0 100644
--- a/sound/pcmcia/pdaudiocf/pdaudiocf_core.c
+++ b/sound/pcmcia/pdaudiocf/pdaudiocf_core.c
@@ -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>
diff --git a/sound/pcmcia/pdaudiocf/pdaudiocf_irq.c b/sound/pcmcia/pdaudiocf/pdaudiocf_irq.c
index 5454336..fa4b113 100644
--- a/sound/pcmcia/pdaudiocf/pdaudiocf_irq.c
+++ b/sound/pcmcia/pdaudiocf/pdaudiocf_irq.c
@@ -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>
diff --git a/sound/pcmcia/pdaudiocf/pdaudiocf_pcm.c b/sound/pcmcia/pdaudiocf/pdaudiocf_pcm.c
index 10afcb2..01066c9 100644
--- a/sound/pcmcia/pdaudiocf/pdaudiocf_pcm.c
+++ b/sound/pcmcia/pdaudiocf/pdaudiocf_pcm.c
@@ -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>
diff --git a/sound/pcmcia/vx/vxp_mixer.c b/sound/pcmcia/vx/vxp_mixer.c
index 1eff158..a4a6642 100644
--- a/sound/pcmcia/vx/vxp_mixer.c
+++ b/sound/pcmcia/vx/vxp_mixer.c
@@ -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;
}
diff --git a/sound/pcmcia/vx/vxp_ops.c b/sound/pcmcia/vx/vxp_ops.c
index 1ee0918..157b0b5 100644
--- a/sound/pcmcia/vx/vxp_ops.c
+++ b/sound/pcmcia/vx/vxp_ops.c
@@ -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>
diff --git a/sound/pcmcia/vx/vxpocket.c b/sound/pcmcia/vx/vxpocket.c
index c57e127..706602a 100644
--- a/sound/pcmcia/vx/vxpocket.c
+++ b/sound/pcmcia/vx/vxpocket.c
@@ -19,7 +19,6 @@
*/
-#include <sound/driver.h>
#include <linux/init.h>
#include <linux/moduleparam.h>
#include <sound/core.h>
diff --git a/sound/ppc/awacs.c b/sound/ppc/awacs.c
index 05dabe4..8441e78 100644
--- a/sound/ppc/awacs.c
+++ b/sound/ppc/awacs.c
@@ -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;
}
diff --git a/sound/ppc/beep.c b/sound/ppc/beep.c
index 566b5ab..baa2a72 100644
--- a/sound/ppc/beep.c
+++ b/sound/ppc/beep.c
@@ -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;
}
diff --git a/sound/ppc/burgundy.c b/sound/ppc/burgundy.c
index e02263f..1a545ac 100644
--- a/sound/ppc/burgundy.c
+++ b/sound/ppc/burgundy.c
@@ -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;
diff --git a/sound/ppc/daca.c b/sound/ppc/daca.c
index c5a1f0b..8432c16 100644
--- a/sound/ppc/daca.c
+++ b/sound/ppc/daca.c
@@ -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);
}
diff --git a/sound/ppc/keywest.c b/sound/ppc/keywest.c
index bb7d744..6ff99ed 100644
--- a/sound/ppc/keywest.c
+++ b/sound/ppc/keywest.c
@@ -19,7 +19,6 @@
*/
-#include <sound/driver.h>
#include <linux/init.h>
#include <linux/i2c.h>
#include <linux/delay.h>
diff --git a/sound/ppc/pmac.c b/sound/ppc/pmac.c
index 4f9b19c..613a565 100644
--- a/sound/ppc/pmac.c
+++ b/sound/ppc/pmac.c
@@ -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
*/
@@ -376,6 +387,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
*/
static void snd_pmac_pcm_update(struct snd_pmac *chip, struct pmac_stream *rec)
@@ -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;
}
diff --git a/sound/ppc/powermac.c b/sound/ppc/powermac.c
index 2264574..c936225 100644
--- a/sound/ppc/powermac.c
+++ b/sound/ppc/powermac.c
@@ -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>
diff --git a/sound/ppc/snd_ps3.c b/sound/ppc/snd_ps3.c
index 27b6189..d8d0b4b 100644
--- a/sound/ppc/snd_ps3.c
+++ b/sound/ppc/snd_ps3.c
@@ -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;
diff --git a/sound/ppc/tumbler.c b/sound/ppc/tumbler.c
index 5821cdd..71a7a97 100644
--- a/sound/ppc/tumbler.c
+++ b/sound/ppc/tumbler.c
@@ -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;
diff --git a/sound/sh/aica.c b/sound/sh/aica.c
index 88dc840..d49417b 100644
--- a/sound/sh/aica.c
+++ b/sound/sh/aica.c
@@ -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];
diff --git a/sound/soc/Kconfig b/sound/soc/Kconfig
index 97b2552..2765852 100644
--- a/sound/soc/Kconfig
+++ b/sound/soc/Kconfig
@@ -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"
diff --git a/sound/soc/Makefile b/sound/soc/Makefile
index 3041403..4869c9a 100644
--- a/sound/soc/Makefile
+++ b/sound/soc/Makefile
@@ -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/
diff --git a/sound/soc/at91/at91-pcm.c b/sound/soc/at91/at91-pcm.c
index b39b95a..67c88e3 100644
--- a/sound/soc/at91/at91-pcm.c
+++ b/sound/soc/at91/at91-pcm.c
@@ -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>
diff --git a/sound/soc/at91/at91-ssc.c b/sound/soc/at91/at91-ssc.c
index 3d4e32c..f642d2d 100644
--- a/sound/soc/at91/at91-ssc.c
+++ b/sound/soc/at91/at91-ssc.c
@@ -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>
diff --git a/sound/soc/at91/eti_b1_wm8731.c b/sound/soc/at91/eti_b1_wm8731.c
index 820a676..ad3ad9d 100644
--- a/sound/soc/at91/eti_b1_wm8731.c
+++ b/sound/soc/at91/eti_b1_wm8731.c
@@ -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>
diff --git a/sound/soc/codecs/Kconfig b/sound/soc/codecs/Kconfig
index 7824880..898a7d3 100644
--- a/sound/soc/codecs/Kconfig
+++ b/sound/soc/codecs/Kconfig
@@ -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
diff --git a/sound/soc/codecs/Makefile b/sound/soc/codecs/Makefile
index 7ad78e3..c6e5338 100644
--- a/sound/soc/codecs/Makefile
+++ b/sound/soc/codecs/Makefile
@@ -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
diff --git a/sound/soc/codecs/ac97.c b/sound/soc/codecs/ac97.c
index 0b8a6f8..242130c 100644
--- a/sound/soc/codecs/ac97.c
+++ b/sound/soc/codecs/ac97.c
@@ -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>
diff --git a/sound/soc/codecs/cs4270.c b/sound/soc/codecs/cs4270.c
index dab22cc..bf2ab72 100644
--- a/sound/soc/codecs/cs4270.c
+++ b/sound/soc/codecs/cs4270.c
@@ -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;
@@ -155,80 +272,6 @@ static int cs4270_set_dai_fmt(struct snd_soc_codec_dai *codec_dai,
}
/*
- * 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
* lower three bits are determined via the AD2, AD1, and AD0 pins
@@ -315,53 +358,6 @@ static int cs4270_i2c_write(struct snd_soc_codec *codec, unsigned int reg,
}
/*
- * 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.
*
* The .dai_ops functions are used to provide board-specific data, like
@@ -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
index 0000000..710e028
--- /dev/null
+++ b/sound/soc/codecs/tlv320aic3x.c
@@ -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
index 0000000..d0cdeeb
--- /dev/null
+++ b/sound/soc/codecs/tlv320aic3x.h
@@ -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 */
diff --git a/sound/soc/codecs/wm8731.c b/sound/soc/codecs/wm8731.c
index 7ca0b52..9c33fe8 100644
--- a/sound/soc/codecs/wm8731.c
+++ b/sound/soc/codecs/wm8731.c
@@ -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);
diff --git a/sound/soc/codecs/wm8750.c b/sound/soc/codecs/wm8750.c
index 28684ee..77a857b 100644
--- a/sound/soc/codecs/wm8750.c
+++ b/sound/soc/codecs/wm8750.c
@@ -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),
diff --git a/sound/soc/codecs/wm8753.c b/sound/soc/codecs/wm8753.c
index efced93..ddd9c71 100644
--- a/sound/soc/codecs/wm8753.c
+++ b/sound/soc/codecs/wm8753.c
@@ -41,13 +41,13 @@
#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),
diff --git a/sound/soc/codecs/wm9712.c b/sound/soc/codecs/wm9712.c
index 986b5d59..590baea 100644
--- a/sound/soc/codecs/wm9712.c
+++ b/sound/soc/codecs/wm9712.c
@@ -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
index 0000000..257101f
--- /dev/null
+++ b/sound/soc/fsl/Kconfig
@@ -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
index 0000000..62f680a
--- /dev/null
+++ b/sound/soc/fsl/Makefile
@@ -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
index 0000000..652514f
--- /dev/null
+++ b/sound/soc/fsl/fsl_dma.c
@@ -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
index 0000000..430a6ce
--- /dev/null
+++ b/sound/soc/fsl/fsl_dma.h
@@ -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
index 0000000..145ad13
--- /dev/null
+++ b/sound/soc/fsl/fsl_ssi.c
@@ -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
index 0000000..c5ce88e
--- /dev/null
+++ b/sound/soc/fsl/fsl_ssi.h
@@ -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
index 0000000..f26c4b2
--- /dev/null
+++ b/sound/soc/fsl/mpc8610_hpcd.c
@@ -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");
diff --git a/sound/soc/pxa/Kconfig b/sound/soc/pxa/Kconfig
index a83e229..484f883 100644
--- a/sound/soc/pxa/Kconfig
+++ b/sound/soc/pxa/Kconfig
@@ -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
diff --git a/sound/soc/pxa/Makefile b/sound/soc/pxa/Makefile
index 78e0d6b..04e5646 100644
--- a/sound/soc/pxa/Makefile
+++ b/sound/soc/pxa/Makefile
@@ -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
diff --git a/sound/soc/pxa/corgi.c b/sound/soc/pxa/corgi.c
index 5ee51a9..3f34e53 100644
--- a/sound/soc/pxa/corgi.c
+++ b/sound/soc/pxa/corgi.c
@@ -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
index 0000000..06e8afb
--- /dev/null
+++ b/sound/soc/pxa/e800_wm9712.c
@@ -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");
diff --git a/sound/soc/pxa/poodle.c b/sound/soc/pxa/poodle.c
index 0915cf7..5ae59bd 100644
--- a/sound/soc/pxa/poodle.c
+++ b/sound/soc/pxa/poodle.c
@@ -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>
diff --git a/sound/soc/pxa/pxa2xx-ac97.c b/sound/soc/pxa/pxa2xx-ac97.c
index 60e6f46..815c153 100644
--- a/sound/soc/pxa/pxa2xx-ac97.c
+++ b/sound/soc/pxa/pxa2xx-ac97.c
@@ -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>
diff --git a/sound/soc/pxa/pxa2xx-i2s.c b/sound/soc/pxa/pxa2xx-i2s.c
index 50c5c83..692b900 100644
--- a/sound/soc/pxa/pxa2xx-i2s.c
+++ b/sound/soc/pxa/pxa2xx-i2s.c
@@ -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>
diff --git a/sound/soc/pxa/pxa2xx-pcm.c b/sound/soc/pxa/pxa2xx-pcm.c
index 35e8fa3..daeaa4c 100644
--- a/sound/soc/pxa/pxa2xx-pcm.c
+++ b/sound/soc/pxa/pxa2xx-pcm.c
@@ -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>
diff --git a/sound/soc/pxa/spitz.c b/sound/soc/pxa/spitz.c
index 4dd8f35..d56709e 100644
--- a/sound/soc/pxa/spitz.c
+++ b/sound/soc/pxa/spitz.c
@@ -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/tosa.c b/sound/soc/pxa/tosa.c
index 5504e30..e4d40b5 100644
--- a/sound/soc/pxa/tosa.c
+++ b/sound/soc/pxa/tosa.c
@@ -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>
diff --git a/sound/soc/s3c24xx/Kconfig b/sound/soc/s3c24xx/Kconfig
index 5632a2e..1f6dbfc 100644
--- a/sound/soc/s3c24xx/Kconfig
+++ b/sound/soc/s3c24xx/Kconfig
@@ -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.
diff --git a/sound/soc/s3c24xx/Makefile b/sound/soc/s3c24xx/Makefile
index 13c92f0..0aa5fb0 100644
--- a/sound/soc/s3c24xx/Makefile
+++ b/sound/soc/s3c24xx/Makefile
@@ -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
index 0000000..9ed8f2e
--- /dev/null
+++ b/sound/soc/s3c24xx/ln2440sbc_alc650.c
@@ -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");
diff --git a/sound/soc/s3c24xx/neo1973_wm8753.c b/sound/soc/s3c24xx/neo1973_wm8753.c
index f1f6b94..6ee115c 100644
--- a/sound/soc/s3c24xx/neo1973_wm8753.c
+++ b/sound/soc/s3c24xx/neo1973_wm8753.c
@@ -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>
@@ -30,13 +29,15 @@
#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
index 0000000..c4a46dd
--- /dev/null
+++ b/sound/soc/s3c24xx/s3c2412-i2s.c
@@ -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
index 0000000..27f48e1
--- /dev/null
+++ b/sound/soc/s3c24xx/s3c2412-i2s.h
@@ -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 */
diff --git a/sound/soc/s3c24xx/s3c2443-ac97.c b/sound/soc/s3c24xx/s3c2443-ac97.c
index 758a263..1c1ddbf 100644
--- a/sound/soc/s3c24xx/s3c2443-ac97.c
+++ b/sound/soc/s3c24xx/s3c2443-ac97.c
@@ -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);
diff --git a/sound/soc/s3c24xx/s3c24xx-ac97.h b/sound/soc/s3c24xx/s3c24xx-ac97.h
index 2b835e8..bf03e8e 100644
--- a/sound/soc/s3c24xx/s3c24xx-ac97.h
+++ b/sound/soc/s3c24xx/s3c24xx-ac97.h
@@ -20,6 +20,12 @@
#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_*/
diff --git a/sound/soc/s3c24xx/s3c24xx-i2s.c b/sound/soc/s3c24xx/s3c24xx-i2s.c
index cd89c41..0a3c630 100644
--- a/sound/soc/s3c24xx/s3c24xx-i2s.c
+++ b/sound/soc/s3c24xx/s3c24xx-i2s.c
@@ -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>
@@ -33,13 +33,14 @@
#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,
diff --git a/sound/soc/s3c24xx/s3c24xx-pcm.c b/sound/soc/s3c24xx/s3c24xx-pcm.c
index 4107a87..29a6c82f 100644
--- a/sound/soc/s3c24xx/s3c24xx-pcm.c
+++ b/sound/soc/s3c24xx/s3c24xx-pcm.c
@@ -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;
diff --git a/sound/soc/s3c24xx/smdk2443_wm9710.c b/sound/soc/s3c24xx/smdk2443_wm9710.c
index d46cd81..b4a5630 100644
--- a/sound/soc/s3c24xx/smdk2443_wm9710.c
+++ b/sound/soc/s3c24xx/smdk2443_wm9710.c
@@ -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>
diff --git a/sound/soc/sh/dma-sh7760.c b/sound/soc/sh/dma-sh7760.c
index cdee374..7a3ce80 100644
--- a/sound/soc/sh/dma-sh7760.c
+++ b/sound/soc/sh/dma-sh7760.c
@@ -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>
diff --git a/sound/soc/sh/hac.c b/sound/soc/sh/hac.c
index 8e3f039..b7b676b 100644
--- a/sound/soc/sh/hac.c
+++ b/sound/soc/sh/hac.c
@@ -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);
diff --git a/sound/soc/sh/sh7760-ac97.c b/sound/soc/sh/sh7760-ac97.c
index 5563f14..2f91de8 100644
--- a/sound/soc/sh/sh7760-ac97.c
+++ b/sound/soc/sh/sh7760-ac97.c
@@ -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>
diff --git a/sound/soc/sh/ssi.c b/sound/soc/sh/ssi.c
index b72bc31..3388bc3 100644
--- a/sound/soc/sh/ssi.c
+++ b/sound/soc/sh/ssi.c
@@ -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>
diff --git a/sound/soc/soc-core.c b/sound/soc/soc-core.c
index e6a67b5..9eb5479 100644
--- a/sound/soc/soc-core.c
+++ b/sound/soc/soc-core.c
@@ -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;
diff --git a/sound/soc/soc-dapm.c b/sound/soc/soc-dapm.c
index 29a546f..620d7ea 100644
--- a/sound/soc/soc-dapm.c
+++ b/sound/soc/soc-dapm.c
@@ -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);
@@ -1280,6 +1294,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
* @endpoint: audio signal endpoint (or start point)
diff --git a/sound/sparc/amd7930.c b/sound/sparc/amd7930.c
index 07962a3..0c63e05 100644
--- a/sound/sparc/amd7930.c
+++ b/sound/sparc/amd7930.c
@@ -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
diff --git a/sound/sparc/cs4231.c b/sound/sparc/cs4231.c
index f8c7a12..1c4797b 100644
--- a/sound/sparc/cs4231.c
+++ b/sound/sparc/cs4231.c
@@ -19,7 +19,6 @@
#include <linux/io.h>
-#include <sound/driver.h>
#include <sound/core.h>
#include <sound/pcm.h>
#include <sound/info.h>
diff --git a/sound/sparc/dbri.c b/sound/sparc/dbri.c
index 376b986..3d00e07 100644
--- a/sound/sparc/dbri.c
+++ b/sound/sparc/dbri.c
@@ -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) {
diff --git a/sound/spi/at73c213.c b/sound/spi/at73c213.c
index fee869b..89d6e9c 100644
--- a/sound/spi/at73c213.c
+++ b/sound/spi/at73c213.c
@@ -18,10 +18,10 @@
#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;
diff --git a/sound/synth/emux/emux.c b/sound/synth/emux/emux.c
index ebcac13..c89d2ea 100644
--- a/sound/synth/emux/emux.c
+++ b/sound/synth/emux/emux.c
@@ -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>
diff --git a/sound/synth/emux/emux_hwdep.c b/sound/synth/emux/emux_hwdep.c
index 9b63814..0a53914 100644
--- a/sound/synth/emux/emux_hwdep.c
+++ b/sound/synth/emux/emux_hwdep.c
@@ -19,7 +19,6 @@
*
*/
-#include <sound/driver.h>
#include <sound/core.h>
#include <sound/hwdep.h>
#include <asm/uaccess.h>
diff --git a/sound/synth/emux/emux_oss.c b/sound/synth/emux/emux_oss.c
index 3436816..f60a98e 100644
--- a/sound/synth/emux/emux_oss.c
+++ b/sound/synth/emux/emux_oss.c
@@ -22,7 +22,6 @@
* midi emulation.
*/
-#include <sound/driver.h>
#ifdef CONFIG_SND_SEQUENCER_OSS
diff --git a/sound/synth/emux/emux_proc.c b/sound/synth/emux/emux_proc.c
index 680f2b7..687e6a1 100644
--- a/sound/synth/emux/emux_proc.c
+++ b/sound/synth/emux/emux_proc.c
@@ -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>
diff --git a/sound/synth/emux/emux_voice.h b/sound/synth/emux/emux_voice.h
index 0a56ca18..09711f8 100644
--- a/sound/synth/emux/emux_voice.h
+++ b/sound/synth/emux/emux_voice.h
@@ -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>
diff --git a/sound/synth/emux/soundfont.c b/sound/synth/emux/soundfont.c
index 455e535..36d53bd 100644
--- a/sound/synth/emux/soundfont.c
+++ b/sound/synth/emux/soundfont.c
@@ -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>
diff --git a/sound/synth/util_mem.c b/sound/synth/util_mem.c
index 6fc3d2b..deabe5f 100644
--- a/sound/synth/util_mem.c
+++ b/sound/synth/util_mem.c
@@ -19,7 +19,6 @@
*/
#include <linux/mutex.h>
-#include <sound/driver.h>
#include <linux/init.h>
#include <linux/slab.h>
#include <sound/core.h>
diff --git a/sound/usb/Kconfig b/sound/usb/Kconfig
index 7061438..9351b8a 100644
--- a/sound/usb/Kconfig
+++ b/sound/usb/Kconfig
@@ -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
diff --git a/sound/usb/caiaq/Makefile b/sound/usb/caiaq/Makefile
index 455c8c5..23dadd5 100644
--- a/sound/usb/caiaq/Makefile
+++ b/sound/usb/caiaq/Makefile
@@ -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
diff --git a/sound/usb/caiaq/caiaq-audio.c b/sound/usb/caiaq/caiaq-audio.c
index 0666908..9cc4cd8 100644
--- a/sound/usb/caiaq/caiaq-audio.c
+++ b/sound/usb/caiaq/caiaq-audio.c
@@ -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
index 0000000..798ca12
--- /dev/null
+++ b/sound/usb/caiaq/caiaq-control.c
@@ -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
index 0000000..2e7ab1a
--- /dev/null
+++ b/sound/usb/caiaq/caiaq-control.h
@@ -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 */
diff --git a/sound/usb/caiaq/caiaq-device.c b/sound/usb/caiaq/caiaq-device.c
index 58af814..58d25e4 100644
--- a/sound/usb/caiaq/caiaq-device.c
+++ b/sound/usb/caiaq/caiaq-device.c
@@ -26,26 +26,28 @@
#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}}");
@@ -94,6 +96,11 @@ static struct usb_device_id snd_usb_id_table[] = {
.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,
.idProduct = USB_PID_AK1
@@ -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;
diff --git a/sound/usb/caiaq/caiaq-device.h b/sound/usb/caiaq/caiaq-device.h
index 79bc5be..96a4913 100644
--- a/sound/usb/caiaq/caiaq-device.h
+++ b/sound/usb/caiaq/caiaq-device.h
@@ -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 */
diff --git a/sound/usb/caiaq/caiaq-input.c b/sound/usb/caiaq/caiaq-input.c
index cd536ca..f743847 100644
--- a/sound/usb/caiaq/caiaq-input.c
+++ b/sound/usb/caiaq/caiaq-input.c
@@ -21,28 +21,61 @@
#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 */
-
diff --git a/sound/usb/caiaq/caiaq-midi.c b/sound/usb/caiaq/caiaq-midi.c
index 793ca20..30b57f9 100644
--- a/sound/usb/caiaq/caiaq-midi.c
+++ b/sound/usb/caiaq/caiaq-midi.c
@@ -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;
diff --git a/sound/usb/usbaudio.c b/sound/usb/usbaudio.c
index 967b823..8fa9356 100644
--- a/sound/usb/usbaudio.c
+++ b/sound/usb/usbaudio.c
@@ -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)
{
diff --git a/sound/usb/usbaudio.h b/sound/usb/usbaudio.h
index 2272f45..7cf18c3 100644
--- a/sound/usb/usbaudio.h
+++ b/sound/usb/usbaudio.h
@@ -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;
diff --git a/sound/usb/usbmidi.c b/sound/usb/usbmidi.c
index 6330788..750e929 100644
--- a/sound/usb/usbmidi.c
+++ b/sound/usb/usbmidi.c
@@ -35,7 +35,6 @@
* SUCH DAMAGE.
*/
-#include <sound/driver.h>
#include <linux/kernel.h>
#include <linux/types.h>
#include <linux/bitops.h>
diff --git a/sound/usb/usbmixer.c b/sound/usb/usbmixer.c
index 5e32969..89c63d0 100644
--- a/sound/usb/usbmixer.c
+++ b/sound/usb/usbmixer.c
@@ -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)
diff --git a/sound/usb/usbmixer_maps.c b/sound/usb/usbmixer_maps.c
index 7c4dcb3..d755be0 100644
--- a/sound/usb/usbmixer_maps.c
+++ b/sound/usb/usbmixer_maps.c
@@ -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),
diff --git a/sound/usb/usbquirks.h b/sound/usb/usbquirks.h
index 59410f4..938dff5 100644
--- a/sound/usb/usbquirks.h
+++ b/sound/usb/usbquirks.h
@@ -1004,11 +1004,35 @@ 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) {
diff --git a/sound/usb/usx2y/usX2Yhwdep.c b/sound/usb/usx2y/usX2Yhwdep.c
index b76b3dd..6495534e 100644
--- a/sound/usb/usx2y/usX2Yhwdep.c
+++ b/sound/usb/usx2y/usX2Yhwdep.c
@@ -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>
@@ -34,34 +33,31 @@
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)
diff --git a/sound/usb/usx2y/usbusx2y.c b/sound/usb/usx2y/usbusx2y.c
index e011fca..e5981a6 100644
--- a/sound/usb/usx2y/usbusx2y.c
+++ b/sound/usb/usx2y/usbusx2y.c
@@ -130,7 +130,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>
diff --git a/sound/usb/usx2y/usbusx2yaudio.c b/sound/usb/usx2y/usbusx2yaudio.c
index 48e9aa3..9a608fa 100644
--- a/sound/usb/usx2y/usbusx2yaudio.c
+++ b/sound/usb/usx2y/usbusx2yaudio.c
@@ -31,7 +31,6 @@
*/
-#include <sound/driver.h>
#include <linux/interrupt.h>
#include <linux/usb.h>
#include <sound/core.h>
diff --git a/sound/usb/usx2y/usx2yhwdeppcm.c b/sound/usb/usx2y/usx2yhwdeppcm.c
index a5e7bcd..800b5ce 100644
--- a/sound/usb/usx2y/usx2yhwdeppcm.c
+++ b/sound/usb/usx2y/usx2yhwdeppcm.c
@@ -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,
};